diff --git a/package.json b/package.json index d85a5c2..449379c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@gui-chat-plugin/set-image-style", - "version": "1.1.1", + "version": "2.0.0", "description": "Set image style modifier plugin for GUIChat", "type": "module", "main": "./dist/index.cjs", @@ -35,7 +35,7 @@ "test": "tsx --test tests/*.test.ts" }, "peerDependencies": { - "gui-chat-protocol": "^1.2.0", + "gui-chat-protocol": "^2.0.0", "vue": "^3.5.0" }, "devDependencies": { @@ -47,7 +47,7 @@ "eslint": "^10.8.0", "eslint-plugin-vue": "^10.10.0", "globals": "^17.8.0", - "gui-chat-protocol": "^1.2.0", + "gui-chat-protocol": "^2.0.0", "tailwindcss": "^4.3.3", "tsx": "^4.23.4", "typescript": "~6.0.3", diff --git a/src/core/hostResponse.ts b/src/core/hostResponse.ts new file mode 100644 index 0000000..76976c5 --- /dev/null +++ b/src/core/hostResponse.ts @@ -0,0 +1,14 @@ +import type { ImageGenerationConfig } from "./types"; + +const isRecord = (value: unknown): value is Record => + typeof value === "object" && value !== null; + +const isOptionalString = (value: unknown): value is string | undefined => + value === undefined || typeof value === "string"; + +export const isImageGenerationConfig = ( + value: unknown, +): value is ImageGenerationConfig => + isRecord(value) && + typeof value["backend"] === "string" && + isOptionalString(value["styleModifier"]); diff --git a/src/core/plugin.ts b/src/core/plugin.ts index f468d59..f0929ff 100644 --- a/src/core/plugin.ts +++ b/src/core/plugin.ts @@ -9,6 +9,7 @@ import type { SetImageStyleJsonData, ImageGenerationConfig, } from "./types"; +import { isImageGenerationConfig } from "./hostResponse"; import { TOOL_DEFINITION } from "./definition"; // Re-export for convenience @@ -27,13 +28,9 @@ export const executeSetImageStyle = async ( ): Promise> => { const { styleModifier } = args; - // Check if app context provides image config functions - const app = context?.app as { - getImageConfig?: () => ImageGenerationConfig; - setConfig?: (key: string, value: ImageGenerationConfig) => void; - }; + const app = context?.app; - if (!app?.getImageConfig) { + if (typeof app?.["getImageConfig"] !== "function") { return { message: "getImageConfig function not available", jsonData: { @@ -44,7 +41,18 @@ export const executeSetImageStyle = async ( } try { - const config = app.getImageConfig(); + const config = app["getImageConfig"](); + + if (!isImageGenerationConfig(config)) { + return { + message: "getImageConfig returned an unrecognized config", + jsonData: { + success: false, + error: "getImageConfig returned an unrecognized config", + }, + }; + } + const previousStyleModifier = config.styleModifier || ""; // Update the config with new style modifier diff --git a/tests/hostResponse.test.ts b/tests/hostResponse.test.ts new file mode 100644 index 0000000..c140c95 --- /dev/null +++ b/tests/hostResponse.test.ts @@ -0,0 +1,44 @@ +/** + * `context.app.getImageConfig` returns `unknown` since gui-chat-protocol + * 2.0.0, so the plugin narrows it here instead of trusting the host's shape. + * + * Run with: yarn test + */ + +import { test, describe } from "node:test"; +import assert from "node:assert"; +import { isImageGenerationConfig } from "../src/core/hostResponse.js"; + +describe("isImageGenerationConfig", () => { + test("accepts a config naming a backend", () => { + assert.equal(isImageGenerationConfig({ backend: "gemini" }), true); + }); + + test("accepts a config that already carries a style modifier", () => { + assert.equal( + isImageGenerationConfig({ backend: "gemini", styleModifier: "watercolor" }), + true, + ); + }); + + test("rejects a config without a backend", () => { + assert.equal(isImageGenerationConfig({ styleModifier: "watercolor" }), false); + }); + + test("rejects a style modifier that is not a string", () => { + assert.equal( + isImageGenerationConfig({ backend: "gemini", styleModifier: 42 }), + false, + ); + }); + + test("rejects values that are not a config object", () => { + [null, undefined, "ok", 7].forEach((value) => { + assert.equal( + isImageGenerationConfig(value), + false, + `should reject ${JSON.stringify(value)}`, + ); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index b1489b4..f4df4a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1051,10 +1051,10 @@ graceful-fs@^4.2.4: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -gui-chat-protocol@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/gui-chat-protocol/-/gui-chat-protocol-1.2.0.tgz#8bc7f8d4e31a625aff94fa2c79951b5b80c30145" - integrity sha512-5qKQCpXOrH5+QlOWujhYpsG469IB6nsSZ9TOoodsBQ+U5aAPGEEUbvmacyOmPsimHvsDLCxP0S/ZNycWEFHOww== +gui-chat-protocol@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/gui-chat-protocol/-/gui-chat-protocol-2.0.0.tgz#96f0f0a115c92f8567b68b9c74a53514e1f3253f" + integrity sha512-E02kPSXKH2bX4WAp6TuMjSx4Ftzr2n8XEQtPhZxRCKCBO9ss3Qkx4G5xWeZ6bVUCZtyhB+N8V8Jlv+YRPQJQ9w== ignore@^5.2.0: version "5.3.2"