diff --git a/frontend/src/adapters/mock/index.ts b/frontend/src/adapters/mock/index.ts index fe6bbc1..0df1c28 100644 --- a/frontend/src/adapters/mock/index.ts +++ b/frontend/src/adapters/mock/index.ts @@ -3523,7 +3523,7 @@ export class MockPluginGateway implements PluginGateway { throw err; } this.plugins.splice(idx, 1); - return { pluginId, restartRequired: true }; + return { pluginId, removalOutcome: "removed", restartRequired: true }; } async listRuntimeContributions(): Promise { diff --git a/frontend/src/adapters/plugin.test.ts b/frontend/src/adapters/plugin.test.ts index c0fc0a4..f215873 100644 --- a/frontend/src/adapters/plugin.test.ts +++ b/frontend/src/adapters/plugin.test.ts @@ -23,4 +23,28 @@ describe("TauriPluginGateway invoke payloads", () => { }, }); }); + + it("normalizes backend review DTOs for the Plugins panel view-model", async () => { + invoke.mockResolvedValueOnce({ + id: "dev.acme.gitgraph", + displayName: "Git Graph", + publisher: "Acme", + version: "1.2.3", + description: "Graph", + sourceKind: "archive", + sourceLabel: "/tmp/gitgraph.ideaplug", + contentHash: "abc", + trustLevel: "full", + contributionSummary: { topLevelMenus: 1, menuItems: 2, layouts: 3, mcpServers: 4 }, + contributes: { menus: [], menuItems: [], layouts: [], mcpServers: [] }, + }); + + const review = await new TauriPluginGateway().reviewPackage({ + sourceKind: "archive", + path: "/tmp/gitgraph.ideaplug", + }); + + expect(review.issues).toEqual([]); + expect(review.installable).toBe(true); + }); }); diff --git a/frontend/src/adapters/plugin.ts b/frontend/src/adapters/plugin.ts index 1e85e13..af577f2 100644 --- a/frontend/src/adapters/plugin.ts +++ b/frontend/src/adapters/plugin.ts @@ -21,15 +21,29 @@ import type { } from "@/domain"; import type { PluginGateway, ReviewPluginPackageInput } from "@/ports"; +type TauriPluginReviewDto = Omit & { + issues?: PluginReview["issues"]; + installable?: boolean; +}; + +function normalizeReview(review: TauriPluginReviewDto): PluginReview { + return { + ...review, + issues: review.issues ?? [], + installable: review.installable ?? true, + }; +} + export class TauriPluginGateway implements PluginGateway { listPlugins(): Promise { return invoke("plugin_list_plugins"); } - reviewPackage(input: ReviewPluginPackageInput): Promise { - return invoke("plugin_review_package", { + async reviewPackage(input: ReviewPluginPackageInput): Promise { + const review = await invoke("plugin_review_package", { input: { sourceKind: input.sourceKind, path: input.path }, }); + return normalizeReview(review); } installFromArchive(path: string): Promise { diff --git a/frontend/src/domain/index.ts b/frontend/src/domain/index.ts index e5a20dc..6c2dc71 100644 --- a/frontend/src/domain/index.ts +++ b/frontend/src/domain/index.ts @@ -1730,6 +1730,7 @@ export interface PluginInstallResult { /** Outcome of `uninstall`. */ export interface PluginUninstallResult { pluginId: string; + removalOutcome?: "removed" | "tombstoned" | "notFound"; restartRequired: boolean; }