skybridge 0.0.0-dev.dc1bd07 → 0.0.0-dev.dc576c4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/resolve-views-dir.d.ts +1 -0
- package/dist/cli/resolve-views-dir.js +17 -0
- package/dist/cli/resolve-views-dir.js.map +1 -0
- package/dist/cli/use-typescript-check.js +1 -1
- package/dist/cli/use-typescript-check.js.map +1 -1
- package/dist/commands/build.js +1 -16
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/dev.js +16 -0
- package/dist/commands/dev.js.map +1 -1
- package/dist/server/content-helpers.d.ts +40 -0
- package/dist/server/content-helpers.js +33 -0
- package/dist/server/content-helpers.js.map +1 -1
- package/dist/server/file-ref.d.ts +20 -0
- package/dist/server/file-ref.js +19 -0
- package/dist/server/file-ref.js.map +1 -1
- package/dist/server/middleware.d.ts +16 -3
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/server.d.ts +152 -0
- package/dist/server/server.js +142 -57
- package/dist/server/server.js.map +1 -1
- package/dist/test/view.test.js +45 -0
- package/dist/test/view.test.js.map +1 -1
- package/dist/web/bridges/adaptor.d.ts +48 -0
- package/dist/web/bridges/adaptor.js +308 -0
- package/dist/web/bridges/adaptor.js.map +1 -0
- package/dist/web/bridges/adaptor.test.d.ts +1 -0
- package/dist/web/bridges/adaptor.test.js +205 -0
- package/dist/web/bridges/adaptor.test.js.map +1 -0
- package/dist/web/bridges/apps-sdk/bridge.d.ts +1 -0
- package/dist/web/bridges/apps-sdk/bridge.js +3 -3
- package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
- package/dist/web/bridges/apps-sdk/index.d.ts +0 -1
- package/dist/web/bridges/apps-sdk/index.js +0 -1
- package/dist/web/bridges/apps-sdk/index.js.map +1 -1
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +11 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +11 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
- package/dist/web/bridges/get-adaptor.d.ts +9 -0
- package/dist/web/bridges/get-adaptor.js +27 -5
- package/dist/web/bridges/get-adaptor.js.map +1 -1
- package/dist/web/bridges/get-adaptor.test.d.ts +1 -0
- package/dist/web/bridges/get-adaptor.test.js +48 -0
- package/dist/web/bridges/get-adaptor.test.js.map +1 -0
- package/dist/web/bridges/host-context-stores.d.ts +27 -0
- package/dist/web/bridges/host-context-stores.js +73 -0
- package/dist/web/bridges/host-context-stores.js.map +1 -0
- package/dist/web/bridges/mcp-app/bridge.d.ts +1 -0
- package/dist/web/bridges/mcp-app/bridge.js +1 -3
- package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.test.d.ts +1 -0
- package/dist/web/bridges/mcp-app/bridge.test.js +21 -0
- package/dist/web/bridges/mcp-app/bridge.test.js.map +1 -0
- package/dist/web/bridges/mcp-app/index.d.ts +0 -1
- package/dist/web/bridges/mcp-app/index.js +0 -1
- package/dist/web/bridges/mcp-app/index.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +12 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js +12 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
- package/dist/web/bridges/types.d.ts +62 -0
- package/dist/web/bridges/types.js +14 -1
- package/dist/web/bridges/types.js.map +1 -1
- package/dist/web/bridges/types.test.d.ts +1 -0
- package/dist/web/bridges/types.test.js +19 -0
- package/dist/web/bridges/types.test.js.map +1 -0
- package/dist/web/bridges/use-host-context.d.ts +5 -0
- package/dist/web/bridges/use-host-context.js +5 -0
- package/dist/web/bridges/use-host-context.js.map +1 -1
- package/dist/web/components/modal-provider.js +4 -3
- package/dist/web/components/modal-provider.js.map +1 -1
- package/dist/web/create-store.d.ts +26 -0
- package/dist/web/create-store.js +26 -0
- package/dist/web/create-store.js.map +1 -1
- package/dist/web/create-store.test.js +7 -5
- package/dist/web/create-store.test.js.map +1 -1
- package/dist/web/data-llm.d.ts +33 -0
- package/dist/web/data-llm.js +28 -0
- package/dist/web/data-llm.js.map +1 -1
- package/dist/web/data-llm.test.js +8 -5
- package/dist/web/data-llm.test.js.map +1 -1
- package/dist/web/generate-helpers.d.ts +2 -0
- package/dist/web/generate-helpers.js +2 -0
- package/dist/web/generate-helpers.js.map +1 -1
- package/dist/web/hooks/use-call-tool.d.ts +45 -0
- package/dist/web/hooks/use-call-tool.js +28 -0
- package/dist/web/hooks/use-call-tool.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test.js +36 -23
- package/dist/web/hooks/use-call-tool.test.js.map +1 -1
- package/dist/web/hooks/use-display-mode.d.ts +20 -0
- package/dist/web/hooks/use-display-mode.js +20 -0
- package/dist/web/hooks/use-display-mode.js.map +1 -1
- package/dist/web/hooks/use-display-mode.test.js +55 -20
- package/dist/web/hooks/use-display-mode.test.js.map +1 -1
- package/dist/web/hooks/use-download.test.js +11 -3
- package/dist/web/hooks/use-download.test.js.map +1 -1
- package/dist/web/hooks/use-files.d.ts +32 -0
- package/dist/web/hooks/use-files.js +32 -0
- package/dist/web/hooks/use-files.js.map +1 -1
- package/dist/web/hooks/use-files.test.js +10 -2
- package/dist/web/hooks/use-files.test.js.map +1 -1
- package/dist/web/hooks/use-layout.d.ts +2 -0
- package/dist/web/hooks/use-layout.js +2 -0
- package/dist/web/hooks/use-layout.js.map +1 -1
- package/dist/web/hooks/use-layout.test.js +59 -26
- package/dist/web/hooks/use-layout.test.js.map +1 -1
- package/dist/web/hooks/use-open-external.d.ts +17 -0
- package/dist/web/hooks/use-open-external.js +16 -0
- package/dist/web/hooks/use-open-external.js.map +1 -1
- package/dist/web/hooks/use-open-external.test.js +19 -5
- package/dist/web/hooks/use-open-external.test.js.map +1 -1
- package/dist/web/hooks/use-request-close.d.ts +14 -0
- package/dist/web/hooks/use-request-close.js +13 -0
- package/dist/web/hooks/use-request-close.js.map +1 -1
- package/dist/web/hooks/use-request-close.test.js +34 -40
- package/dist/web/hooks/use-request-close.test.js.map +1 -1
- package/dist/web/hooks/use-request-modal.d.ts +16 -1
- package/dist/web/hooks/use-request-modal.js +16 -1
- package/dist/web/hooks/use-request-modal.js.map +1 -1
- package/dist/web/hooks/use-request-modal.test.js +10 -0
- package/dist/web/hooks/use-request-modal.test.js.map +1 -1
- package/dist/web/hooks/use-request-size.d.ts +17 -0
- package/dist/web/hooks/use-request-size.js +16 -0
- package/dist/web/hooks/use-request-size.js.map +1 -1
- package/dist/web/hooks/use-request-size.test.js +35 -53
- package/dist/web/hooks/use-request-size.test.js.map +1 -1
- package/dist/web/hooks/use-send-follow-up-message.d.ts +17 -0
- package/dist/web/hooks/use-send-follow-up-message.js +17 -0
- package/dist/web/hooks/use-send-follow-up-message.js.map +1 -1
- package/dist/web/hooks/use-set-open-in-app-url.d.ts +17 -0
- package/dist/web/hooks/use-set-open-in-app-url.js +17 -0
- package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -1
- package/dist/web/hooks/use-set-open-in-app-url.test.js +28 -8
- package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
- package/dist/web/hooks/use-tool-info.d.ts +33 -0
- package/dist/web/hooks/use-tool-info.js +26 -0
- package/dist/web/hooks/use-tool-info.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test.js +37 -27
- package/dist/web/hooks/use-tool-info.test.js.map +1 -1
- package/dist/web/hooks/use-user.d.ts +2 -0
- package/dist/web/hooks/use-user.js +2 -0
- package/dist/web/hooks/use-user.js.map +1 -1
- package/dist/web/hooks/use-user.test.js +81 -35
- package/dist/web/hooks/use-user.test.js.map +1 -1
- package/dist/web/hooks/use-view-state.d.ts +21 -0
- package/dist/web/hooks/use-view-state.js.map +1 -1
- package/dist/web/hooks/use-view-state.test.js +6 -2
- package/dist/web/hooks/use-view-state.test.js.map +1 -1
- package/dist/web/mount-view.d.ts +19 -0
- package/dist/web/mount-view.js +19 -0
- package/dist/web/mount-view.js.map +1 -1
- package/dist/web/plugin/plugin.d.ts +28 -0
- package/dist/web/plugin/plugin.js +26 -0
- package/dist/web/plugin/plugin.js.map +1 -1
- package/dist/web/types.d.ts +4 -0
- package/dist/web/types.js.map +1 -1
- package/package.json +1 -1
- package/dist/web/bridges/apps-sdk/adaptor.d.ts +0 -27
- package/dist/web/bridges/apps-sdk/adaptor.js +0 -112
- package/dist/web/bridges/apps-sdk/adaptor.js.map +0 -1
- package/dist/web/bridges/mcp-app/adaptor.d.ts +0 -51
- package/dist/web/bridges/mcp-app/adaptor.js +0 -279
- package/dist/web/bridges/mcp-app/adaptor.js.map +0 -1
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { cleanup, render } from "@testing-library/react";
|
|
3
3
|
import { afterEach, beforeEach, describe, expect, it, vi, } from "vitest";
|
|
4
|
-
import {
|
|
4
|
+
import { _resetAdaptor, getAdaptor } from "./bridges/get-adaptor.js";
|
|
5
|
+
import { McpAppBridge } from "./bridges/mcp-app/index.js";
|
|
5
6
|
import { DataLLM } from "./data-llm.js";
|
|
6
7
|
import { getMcpAppHostPostMessageMock, MockResizeObserver, } from "./hooks/test/utils.js";
|
|
7
8
|
describe("DataLLM", () => {
|
|
8
9
|
afterEach(() => {
|
|
9
10
|
// Clean up React components BEFORE unstubbing globals
|
|
10
11
|
cleanup();
|
|
12
|
+
_resetAdaptor();
|
|
11
13
|
vi.unstubAllGlobals();
|
|
12
14
|
vi.resetAllMocks();
|
|
13
15
|
});
|
|
@@ -88,6 +90,7 @@ describe("DataLLM", () => {
|
|
|
88
90
|
let postMessageMock;
|
|
89
91
|
beforeEach(() => {
|
|
90
92
|
vi.stubGlobal("skybridge", { hostType: "mcp-app" });
|
|
93
|
+
vi.stubGlobal("openai", undefined);
|
|
91
94
|
vi.stubGlobal("ResizeObserver", MockResizeObserver);
|
|
92
95
|
postMessageMock = getMcpAppHostPostMessageMock();
|
|
93
96
|
vi.stubGlobal("parent", { postMessage: postMessageMock });
|
|
@@ -95,10 +98,10 @@ describe("DataLLM", () => {
|
|
|
95
98
|
afterEach(() => {
|
|
96
99
|
cleanup();
|
|
97
100
|
McpAppBridge.resetInstance();
|
|
98
|
-
|
|
101
|
+
_resetAdaptor();
|
|
99
102
|
});
|
|
100
103
|
it("should register a node and update view state via adaptor", async () => {
|
|
101
|
-
const adaptor =
|
|
104
|
+
const adaptor = getAdaptor();
|
|
102
105
|
render(_jsx(DataLLM, { content: "Test content", children: _jsx("div", { children: "Child" }) }));
|
|
103
106
|
await vi.waitFor(() => {
|
|
104
107
|
const viewState = adaptor
|
|
@@ -108,7 +111,7 @@ describe("DataLLM", () => {
|
|
|
108
111
|
});
|
|
109
112
|
});
|
|
110
113
|
it("should preserve existing view state when updating context", async () => {
|
|
111
|
-
const adaptor =
|
|
114
|
+
const adaptor = getAdaptor();
|
|
112
115
|
await adaptor.setViewState({ existingKey: "existingValue" });
|
|
113
116
|
render(_jsx(DataLLM, { content: "Test content", children: _jsx("div", { children: "Child" }) }));
|
|
114
117
|
await vi.waitFor(() => {
|
|
@@ -120,7 +123,7 @@ describe("DataLLM", () => {
|
|
|
120
123
|
});
|
|
121
124
|
});
|
|
122
125
|
it("should handle multiple DataLLM components sharing state through adaptor", async () => {
|
|
123
|
-
const adaptor =
|
|
126
|
+
const adaptor = getAdaptor();
|
|
124
127
|
render(_jsxs(_Fragment, { children: [_jsx(DataLLM, { content: "First component", children: _jsx("div", { children: "First" }) }), _jsx(DataLLM, { content: "Second component", children: _jsx("div", { children: "Second" }) })] }));
|
|
125
128
|
await vi.waitFor(() => {
|
|
126
129
|
const viewState = adaptor
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-llm.test.js","sourceRoot":"","sources":["../../src/web/data-llm.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import { cleanup, render } from \"@testing-library/react\";\nimport {\n afterEach,\n beforeEach,\n describe,\n expect,\n it,\n type Mock,\n vi,\n} from \"vitest\";\nimport { McpAppAdaptor, McpAppBridge } from \"./bridges/mcp-app/index.js\";\nimport { DataLLM } from \"./data-llm.js\";\nimport {\n getMcpAppHostPostMessageMock,\n MockResizeObserver,\n} from \"./hooks/test/utils.js\";\n\ndescribe(\"DataLLM\", () => {\n afterEach(() => {\n // Clean up React components BEFORE unstubbing globals\n cleanup();\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n });\n\n describe(\"apps-sdk mode\", () => {\n let OpenaiMock: { widgetState: unknown; setWidgetState: Mock };\n\n beforeEach(() => {\n OpenaiMock = {\n widgetState: { modelContent: {} },\n setWidgetState: vi.fn(),\n };\n // Use Object.defineProperty to ensure it persists\n Object.defineProperty(globalThis, \"openai\", {\n value: OpenaiMock,\n writable: true,\n configurable: true,\n });\n // Also set on window for browser-like environment\n if (typeof window !== \"undefined\") {\n Object.defineProperty(window, \"openai\", {\n value: OpenaiMock,\n writable: true,\n configurable: true,\n });\n }\n vi.stubGlobal(\"openai\", OpenaiMock);\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n });\n\n afterEach(() => {\n // Keep the mock available for React cleanup, but reset it\n if (typeof window !== \"undefined\" && window.openai) {\n window.openai.setWidgetState = vi.fn();\n window.openai.widgetState = { modelContent: {}, privateContent: {} };\n }\n });\n\n it(\"should register a node with content and call setViewState\", () => {\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n expect(OpenaiMock.setWidgetState).toHaveBeenCalled();\n const callArgs =\n OpenaiMock.setWidgetState.mock.calls[0]?.[0]?.modelContent;\n expect(callArgs).toHaveProperty(\"__view_context\");\n expect(callArgs?.__view_context).toContain(\"- Test content\");\n });\n it(\"should preserve existing viewState when updating context\", () => {\n OpenaiMock.widgetState = {\n modelContent: { existingKey: \"existingValue\" },\n };\n\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n const callArgs =\n OpenaiMock.setWidgetState.mock.calls[0]?.[0]?.modelContent;\n expect(callArgs).toHaveProperty(\"existingKey\", \"existingValue\");\n expect(callArgs).toHaveProperty(\"__view_context\");\n });\n\n it(\"should handle deeply nested DataLLM components\", () => {\n render(\n <DataLLM content=\"Level 1\">\n <DataLLM content=\"Level 2A\" />\n <DataLLM content=\"Level 2B\">\n <DataLLM content=\"Level 3\">\n <div>Content</div>\n </DataLLM>\n </DataLLM>\n </DataLLM>,\n );\n\n const callArgs =\n OpenaiMock.setWidgetState.mock.calls[\n OpenaiMock.setWidgetState.mock.calls.length - 1\n ]?.[0]?.modelContent;\n const context = callArgs?.__view_context as string;\n expect(context).toContain(\"- Level 1\");\n expect(context).toContain(\" - Level 2A\");\n expect(context).toContain(\" - Level 2B\");\n expect(context).toContain(\" - Level 3\");\n });\n\n it(\"should update context when content changes\", () => {\n const { rerender } = render(\n <DataLLM content=\"Initial content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n const initialCalls = OpenaiMock.setWidgetState.mock.calls.length;\n\n rerender(\n <DataLLM content=\"Updated content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n expect(OpenaiMock.setWidgetState.mock.calls.length).toBeGreaterThan(\n initialCalls,\n );\n const lastCallArgs =\n OpenaiMock.setWidgetState.mock.calls[\n OpenaiMock.setWidgetState.mock.calls.length - 1\n ]?.[0]?.modelContent;\n expect(lastCallArgs?.__view_context).toContain(\"- Updated content\");\n });\n\n it(\"should remove node and update context when component unmounts\", () => {\n const { unmount } = render(\n <DataLLM content=\"Content to remove\">\n <div>Child</div>\n </DataLLM>,\n );\n\n const callsBeforeUnmount = OpenaiMock.setWidgetState.mock.calls.length;\n\n unmount();\n\n expect(OpenaiMock.setWidgetState.mock.calls.length).toBeGreaterThan(\n callsBeforeUnmount,\n );\n const lastCallArgs =\n OpenaiMock.setWidgetState.mock.calls[\n OpenaiMock.setWidgetState.mock.calls.length - 1\n ]?.[0]?.modelContent;\n expect(lastCallArgs?.__view_context).not.toContain(\"Content to remove\");\n });\n });\n\n describe(\"mcp-app mode\", () => {\n let postMessageMock: ReturnType<typeof getMcpAppHostPostMessageMock>;\n\n beforeEach(() => {\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n postMessageMock = getMcpAppHostPostMessageMock();\n vi.stubGlobal(\"parent\", { postMessage: postMessageMock });\n });\n\n afterEach(() => {\n cleanup();\n McpAppBridge.resetInstance();\n McpAppAdaptor.resetInstance();\n });\n\n it(\"should register a node and update view state via adaptor\", async () => {\n const adaptor = McpAppAdaptor.getInstance();\n\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n await vi.waitFor(() => {\n const viewState = adaptor\n .getHostContextStore(\"viewState\")\n .getSnapshot();\n expect(viewState?.__view_context).toContain(\"- Test content\");\n });\n });\n\n it(\"should preserve existing view state when updating context\", async () => {\n const adaptor = McpAppAdaptor.getInstance();\n await adaptor.setViewState({ existingKey: \"existingValue\" });\n\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n await vi.waitFor(() => {\n const viewState = adaptor\n .getHostContextStore(\"viewState\")\n .getSnapshot();\n expect(viewState?.existingKey).toBe(\"existingValue\");\n expect(viewState?.__view_context).toContain(\"- Test content\");\n });\n });\n\n it(\"should handle multiple DataLLM components sharing state through adaptor\", async () => {\n const adaptor = McpAppAdaptor.getInstance();\n\n render(\n <>\n <DataLLM content=\"First component\">\n <div>First</div>\n </DataLLM>\n <DataLLM content=\"Second component\">\n <div>Second</div>\n </DataLLM>\n </>,\n );\n\n await vi.waitFor(() => {\n const viewState = adaptor\n .getHostContextStore(\"viewState\")\n .getSnapshot();\n const context = viewState?.__view_context as string;\n expect(context).toContain(\"- First component\");\n expect(context).toContain(\"- Second component\");\n });\n });\n\n it(\"should call ui/update-model-context when view state changes\", async () => {\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n await vi.waitFor(() => {\n expect(postMessageMock).toHaveBeenCalledWith(\n expect.objectContaining({ method: \"ui/update-model-context\" }),\n \"*\",\n );\n });\n });\n });\n});\n"]}
|
|
1
|
+
{"version":3,"file":"data-llm.test.js","sourceRoot":"","sources":["../../src/web/data-llm.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import { cleanup, render } from \"@testing-library/react\";\nimport {\n afterEach,\n beforeEach,\n describe,\n expect,\n it,\n type Mock,\n vi,\n} from \"vitest\";\nimport { _resetAdaptor, getAdaptor } from \"./bridges/get-adaptor.js\";\nimport { McpAppBridge } from \"./bridges/mcp-app/index.js\";\nimport { DataLLM } from \"./data-llm.js\";\nimport {\n getMcpAppHostPostMessageMock,\n MockResizeObserver,\n} from \"./hooks/test/utils.js\";\n\ndescribe(\"DataLLM\", () => {\n afterEach(() => {\n // Clean up React components BEFORE unstubbing globals\n cleanup();\n _resetAdaptor();\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n });\n\n describe(\"apps-sdk mode\", () => {\n let OpenaiMock: { widgetState: unknown; setWidgetState: Mock };\n\n beforeEach(() => {\n OpenaiMock = {\n widgetState: { modelContent: {} },\n setWidgetState: vi.fn(),\n };\n // Use Object.defineProperty to ensure it persists\n Object.defineProperty(globalThis, \"openai\", {\n value: OpenaiMock,\n writable: true,\n configurable: true,\n });\n // Also set on window for browser-like environment\n if (typeof window !== \"undefined\") {\n Object.defineProperty(window, \"openai\", {\n value: OpenaiMock,\n writable: true,\n configurable: true,\n });\n }\n vi.stubGlobal(\"openai\", OpenaiMock);\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n });\n\n afterEach(() => {\n // Keep the mock available for React cleanup, but reset it\n if (typeof window !== \"undefined\" && window.openai) {\n window.openai.setWidgetState = vi.fn();\n window.openai.widgetState = { modelContent: {}, privateContent: {} };\n }\n });\n\n it(\"should register a node with content and call setViewState\", () => {\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n expect(OpenaiMock.setWidgetState).toHaveBeenCalled();\n const callArgs =\n OpenaiMock.setWidgetState.mock.calls[0]?.[0]?.modelContent;\n expect(callArgs).toHaveProperty(\"__view_context\");\n expect(callArgs?.__view_context).toContain(\"- Test content\");\n });\n it(\"should preserve existing viewState when updating context\", () => {\n OpenaiMock.widgetState = {\n modelContent: { existingKey: \"existingValue\" },\n };\n\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n const callArgs =\n OpenaiMock.setWidgetState.mock.calls[0]?.[0]?.modelContent;\n expect(callArgs).toHaveProperty(\"existingKey\", \"existingValue\");\n expect(callArgs).toHaveProperty(\"__view_context\");\n });\n\n it(\"should handle deeply nested DataLLM components\", () => {\n render(\n <DataLLM content=\"Level 1\">\n <DataLLM content=\"Level 2A\" />\n <DataLLM content=\"Level 2B\">\n <DataLLM content=\"Level 3\">\n <div>Content</div>\n </DataLLM>\n </DataLLM>\n </DataLLM>,\n );\n\n const callArgs =\n OpenaiMock.setWidgetState.mock.calls[\n OpenaiMock.setWidgetState.mock.calls.length - 1\n ]?.[0]?.modelContent;\n const context = callArgs?.__view_context as string;\n expect(context).toContain(\"- Level 1\");\n expect(context).toContain(\" - Level 2A\");\n expect(context).toContain(\" - Level 2B\");\n expect(context).toContain(\" - Level 3\");\n });\n\n it(\"should update context when content changes\", () => {\n const { rerender } = render(\n <DataLLM content=\"Initial content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n const initialCalls = OpenaiMock.setWidgetState.mock.calls.length;\n\n rerender(\n <DataLLM content=\"Updated content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n expect(OpenaiMock.setWidgetState.mock.calls.length).toBeGreaterThan(\n initialCalls,\n );\n const lastCallArgs =\n OpenaiMock.setWidgetState.mock.calls[\n OpenaiMock.setWidgetState.mock.calls.length - 1\n ]?.[0]?.modelContent;\n expect(lastCallArgs?.__view_context).toContain(\"- Updated content\");\n });\n\n it(\"should remove node and update context when component unmounts\", () => {\n const { unmount } = render(\n <DataLLM content=\"Content to remove\">\n <div>Child</div>\n </DataLLM>,\n );\n\n const callsBeforeUnmount = OpenaiMock.setWidgetState.mock.calls.length;\n\n unmount();\n\n expect(OpenaiMock.setWidgetState.mock.calls.length).toBeGreaterThan(\n callsBeforeUnmount,\n );\n const lastCallArgs =\n OpenaiMock.setWidgetState.mock.calls[\n OpenaiMock.setWidgetState.mock.calls.length - 1\n ]?.[0]?.modelContent;\n expect(lastCallArgs?.__view_context).not.toContain(\"Content to remove\");\n });\n });\n\n describe(\"mcp-app mode\", () => {\n let postMessageMock: ReturnType<typeof getMcpAppHostPostMessageMock>;\n\n beforeEach(() => {\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"openai\", undefined);\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n postMessageMock = getMcpAppHostPostMessageMock();\n vi.stubGlobal(\"parent\", { postMessage: postMessageMock });\n });\n\n afterEach(() => {\n cleanup();\n McpAppBridge.resetInstance();\n _resetAdaptor();\n });\n\n it(\"should register a node and update view state via adaptor\", async () => {\n const adaptor = getAdaptor();\n\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n await vi.waitFor(() => {\n const viewState = adaptor\n .getHostContextStore(\"viewState\")\n .getSnapshot();\n expect(viewState?.__view_context).toContain(\"- Test content\");\n });\n });\n\n it(\"should preserve existing view state when updating context\", async () => {\n const adaptor = getAdaptor();\n await adaptor.setViewState({ existingKey: \"existingValue\" });\n\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n await vi.waitFor(() => {\n const viewState = adaptor\n .getHostContextStore(\"viewState\")\n .getSnapshot();\n expect(viewState?.existingKey).toBe(\"existingValue\");\n expect(viewState?.__view_context).toContain(\"- Test content\");\n });\n });\n\n it(\"should handle multiple DataLLM components sharing state through adaptor\", async () => {\n const adaptor = getAdaptor();\n\n render(\n <>\n <DataLLM content=\"First component\">\n <div>First</div>\n </DataLLM>\n <DataLLM content=\"Second component\">\n <div>Second</div>\n </DataLLM>\n </>,\n );\n\n await vi.waitFor(() => {\n const viewState = adaptor\n .getHostContextStore(\"viewState\")\n .getSnapshot();\n const context = viewState?.__view_context as string;\n expect(context).toContain(\"- First component\");\n expect(context).toContain(\"- Second component\");\n });\n });\n\n it(\"should call ui/update-model-context when view state changes\", async () => {\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n await vi.waitFor(() => {\n expect(postMessageMock).toHaveBeenCalledWith(\n expect.objectContaining({ method: \"ui/update-model-context\" }),\n \"*\",\n );\n });\n });\n });\n});\n"]}
|
|
@@ -64,6 +64,8 @@ type TypedToolInfoReturn<TInput, TOutput, TResponseMetadata> = ToolState<Objecti
|
|
|
64
64
|
* // toolInfo.output.structuredContent is typed based on view output schema
|
|
65
65
|
* }
|
|
66
66
|
* ```
|
|
67
|
+
*
|
|
68
|
+
* @see https://docs.skybridge.tech/api-reference/generate-helpers
|
|
67
69
|
*/
|
|
68
70
|
export declare function generateHelpers<ServerType = never>(): {
|
|
69
71
|
/**
|
|
@@ -53,6 +53,8 @@ import { useToolInfo } from "./hooks/use-tool-info.js";
|
|
|
53
53
|
* // toolInfo.output.structuredContent is typed based on view output schema
|
|
54
54
|
* }
|
|
55
55
|
* ```
|
|
56
|
+
*
|
|
57
|
+
* @see https://docs.skybridge.tech/api-reference/generate-helpers
|
|
56
58
|
*/
|
|
57
59
|
export function generateHelpers() {
|
|
58
60
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-helpers.js","sourceRoot":"","sources":["../../src/web/generate-helpers.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,WAAW,GACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAkB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAoBvE
|
|
1
|
+
{"version":3,"file":"generate-helpers.js","sourceRoot":"","sources":["../../src/web/generate-helpers.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,WAAW,GACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAkB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAoBvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,MAAM,UAAU,eAAe;IAI7B,OAAO;QACL;;;;;;;;;;;;;;;;;;WAkBG;QACH,WAAW,EAAE,CACX,IAAc,EAId,EAAE;YACF,OAAO,WAAW,CAAC,IAAI,CAGtB,CAAC;QACJ,CAAC;QAED;;;;;;;;;;;;;;;;;;;;;;;;;;WA0BG;QACH,WAAW,EAAE,GAIX,EAAE;YACF,OAAO,WAAW,EAIjB,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type {\n InferTools,\n ToolInput,\n ToolOutput,\n ToolResponseMetadata,\n} from \"../server/index.js\";\nimport type { CallToolResponse } from \"./bridges/types.js\";\nimport {\n type CallToolAsyncFn,\n type CallToolFn,\n type CallToolState,\n useCallTool,\n} from \"./hooks/use-call-tool.js\";\nimport { type ToolState, useToolInfo } from \"./hooks/use-tool-info.js\";\nimport type { Objectify, Prettify } from \"./types.js\";\n\ntype TypedCallToolResponse<TOutput> = CallToolResponse & {\n structuredContent: TOutput;\n};\n\ntype TypedCallToolReturn<TInput, TOutput> = Prettify<\n CallToolState<TypedCallToolResponse<TOutput>> & {\n callTool: CallToolFn<TInput, TypedCallToolResponse<TOutput>>;\n callToolAsync: CallToolAsyncFn<TInput, TypedCallToolResponse<TOutput>>;\n }\n>;\n\ntype TypedToolInfoReturn<TInput, TOutput, TResponseMetadata> = ToolState<\n Objectify<TInput>,\n Objectify<TOutput>,\n Objectify<TResponseMetadata>\n>;\n\n/**\n * Creates typed versions of skybridge hooks with full type inference\n * for tool names, inputs, and outputs.\n *\n * This is the recommended way to use skybridge hooks in your views.\n * Set this up once in a dedicated file and export the typed hooks for use across your app.\n *\n * @typeParam ServerType - The type of your McpServer instance (use `typeof server`).\n * Must be a server instance created with method chaining.\n * TypeScript will validate that tools can be inferred from this type.\n *\n * @example\n * ```typescript\n * // src/server.ts\n * const server = new McpServer({ name: \"my-app\", version: \"1.0\" }, {})\n * .registerTool({\n * name: \"search-trip\",\n * inputSchema: { destination: z.string() },\n * outputSchema: { results: z.array(z.string()) },\n * view: { component: \"search-trip\", description: \"Search trips\" },\n * }, async ({ destination }) => {\n * return { content: [{ type: \"text\", text: `Found trips to ${destination}` }] };\n * });\n *\n * export type AppType = typeof server;\n * ```\n *\n * @example\n * ```typescript\n * // src/helpers.ts (one-time setup)\n * import type { AppType } from \"./server\";\n * import { generateHelpers } from \"skybridge/web\";\n *\n * export const { useCallTool, useToolInfo } = generateHelpers<AppType>();\n * ```\n *\n * @example\n * ```typescript\n * // src/views/search.tsx (usage)\n * import { useCallTool, useToolInfo } from \"../helpers\";\n *\n * export function SearchView() {\n * const { callTool, data } = useCallTool(\"search-trip\");\n * // ^ autocomplete for tool names\n * callTool({ destination: \"Spain\" });\n * // ^ autocomplete for input fields\n *\n * const toolInfo = useToolInfo<\"search-trip\">();\n * // ^ autocomplete for view names\n * // toolInfo.input is typed based on view input schema\n * // toolInfo.output.structuredContent is typed based on view output schema\n * }\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/generate-helpers\n */\nexport function generateHelpers<ServerType = never>() {\n type Tools = InferTools<ServerType>;\n type ToolNames = keyof Tools & string;\n\n return {\n /**\n * Typed version of `useCallTool` that provides autocomplete for tool names\n * and type inference for inputs and outputs.\n *\n * @param name - The name of the tool to call. Autocompletes based on your server's tool registry.\n * @returns A hook with typed `callTool` function and `data` property.\n *\n * @example\n * ```typescript\n * const { callTool, data, isPending } = useCallTool(\"search-trip\");\n * // TypeScript knows callTool expects { destination: string }\n * callTool({ destination: \"Spain\" });\n *\n * // data.structuredContent is typed based on your outputSchema\n * if (data) {\n * console.log(data.structuredContent.results);\n * }\n * ```\n */\n useCallTool: <ToolName extends ToolNames>(\n name: ToolName,\n ): TypedCallToolReturn<\n ToolInput<ServerType, ToolName>,\n ToolOutput<ServerType, ToolName>\n > => {\n return useCallTool(name) as TypedCallToolReturn<\n ToolInput<ServerType, ToolName>,\n ToolOutput<ServerType, ToolName>\n >;\n },\n\n /**\n * Typed version of `useToolInfo` that provides autocomplete for tool names\n * and type inference for inputs, outputs, and responseMetadata.\n *\n * @typeParam K - The name of the tool. Autocompletes based on your server's tool registry.\n * @returns A discriminated union with `status: \"pending\" | \"success\"` that narrows correctly.\n *\n * @example\n * ```typescript\n * const toolInfo = useToolInfo<\"search-trip\">();\n * // toolInfo.input is typed as { destination: string; ... }\n * // toolInfo.output is typed as { results: Array<...>; ... }\n * // toolInfo.responseMetadata is typed based on _meta in callback return\n * // toolInfo.status narrows correctly: \"pending\" | \"success\"\n *\n * if (toolInfo.isPending) {\n * // TypeScript knows output and responseMetadata are undefined here\n * console.log(toolInfo.input.destination);\n * }\n *\n * if (toolInfo.isSuccess) {\n * // TypeScript knows output and responseMetadata are defined here\n * console.log(toolInfo.output.results);\n * console.log(toolInfo.responseMetadata);\n * }\n * ```\n */\n useToolInfo: <ToolName extends ToolNames>(): TypedToolInfoReturn<\n ToolInput<ServerType, ToolName>,\n ToolOutput<ServerType, ToolName>,\n ToolResponseMetadata<ServerType, ToolName>\n > => {\n return useToolInfo() as TypedToolInfoReturn<\n ToolInput<ServerType, ToolName>,\n ToolOutput<ServerType, ToolName>,\n ToolResponseMetadata<ServerType, ToolName>\n >;\n },\n };\n}\n"]}
|
|
@@ -36,13 +36,26 @@ type CallToolErrorState = {
|
|
|
36
36
|
data: undefined;
|
|
37
37
|
error: unknown;
|
|
38
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* State of a {@link useCallTool} invocation, discriminated by `status`.
|
|
41
|
+
* Use `isIdle` / `isPending` / `isSuccess` / `isError` for ergonomic conditional rendering.
|
|
42
|
+
*/
|
|
39
43
|
export type CallToolState<TData extends CallToolResponse = CallToolResponse> = CallToolIdleState | CallToolPendingState | CallToolSuccessState<TData> | CallToolErrorState;
|
|
44
|
+
/**
|
|
45
|
+
* Optional callbacks fired around a {@link useCallTool} call.
|
|
46
|
+
* `onSettled` runs after success or error.
|
|
47
|
+
*/
|
|
40
48
|
export type SideEffects<ToolArgs, ToolResponse> = {
|
|
41
49
|
onSuccess?: (data: ToolResponse, toolArgs: ToolArgs) => void;
|
|
42
50
|
onError?: (error: unknown, toolArgs: ToolArgs) => void;
|
|
43
51
|
onSettled?: (data: ToolResponse | undefined, error: unknown | undefined, toolArgs: ToolArgs) => void;
|
|
44
52
|
};
|
|
45
53
|
type IsArgsOptional<T> = [T] extends [null] ? true : HasRequiredKeys<T> extends false ? true : false;
|
|
54
|
+
/**
|
|
55
|
+
* Fire-and-forget call function returned by {@link useCallTool}. Tracks state
|
|
56
|
+
* on the hook and supports optional {@link SideEffects} callbacks. Args are
|
|
57
|
+
* optional when the tool accepts none.
|
|
58
|
+
*/
|
|
46
59
|
export type CallToolFn<TArgs, TResponse> = IsArgsOptional<TArgs> extends true ? {
|
|
47
60
|
(): void;
|
|
48
61
|
(sideEffects: SideEffects<TArgs, TResponse>): void;
|
|
@@ -52,11 +65,43 @@ export type CallToolFn<TArgs, TResponse> = IsArgsOptional<TArgs> extends true ?
|
|
|
52
65
|
(args: TArgs): void;
|
|
53
66
|
(args: TArgs, sideEffects: SideEffects<TArgs, TResponse>): void;
|
|
54
67
|
};
|
|
68
|
+
/**
|
|
69
|
+
* Promise-returning call function returned by {@link useCallTool}. Rejects
|
|
70
|
+
* if the tool errors; use `try/catch` for error handling.
|
|
71
|
+
*/
|
|
55
72
|
export type CallToolAsyncFn<TArgs, TResponse> = IsArgsOptional<TArgs> extends true ? {
|
|
56
73
|
(): Promise<TResponse>;
|
|
57
74
|
(args: TArgs): Promise<TResponse>;
|
|
58
75
|
} : (args: TArgs) => Promise<TResponse>;
|
|
59
76
|
type ToolResponseSignature = Pick<CallToolResponse, "structuredContent" | "meta">;
|
|
77
|
+
/**
|
|
78
|
+
* Call a server tool from a view and track its execution state.
|
|
79
|
+
*
|
|
80
|
+
* Returns the current {@link CallToolState} plus two callers: `callTool`
|
|
81
|
+
* (fire-and-forget, with optional {@link SideEffects}) and `callToolAsync`
|
|
82
|
+
* (promise-returning). If the same instance is invoked again while a call is
|
|
83
|
+
* in flight, the older response is dropped from the rendered state (but any
|
|
84
|
+
* `onSuccess` / `onError` / `onSettled` callbacks attached to it still fire).
|
|
85
|
+
*
|
|
86
|
+
* Pair with {@link useToolInfo} to read the result of the tool invocation
|
|
87
|
+
* that produced the current view. For end-to-end type safety across tool
|
|
88
|
+
* inputs and outputs, prefer the typed helpers produced by {@link generateHelpers}
|
|
89
|
+
* over calling this hook directly.
|
|
90
|
+
*
|
|
91
|
+
* @typeParam ToolArgs - Shape of the tool's input args (`null` for no-arg tools).
|
|
92
|
+
* @typeParam ToolResponse - Shape of the tool's `structuredContent` / `meta`.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```tsx
|
|
96
|
+
* const { callTool, isPending, data } = useCallTool<{ query: string }>("search");
|
|
97
|
+
*
|
|
98
|
+
* <button onClick={() => callTool({ query: "skybridge" }, {
|
|
99
|
+
* onSuccess: (res) => console.log(res.structuredContent),
|
|
100
|
+
* })} />
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* @see https://docs.skybridge.tech/api-reference/use-call-tool
|
|
104
|
+
*/
|
|
60
105
|
export declare const useCallTool: <ToolArgs extends CallToolArgs = null, ToolResponse extends Partial<ToolResponseSignature> = Record<string, never>>(name: string) => {
|
|
61
106
|
callTool: CallToolFn<ToolArgs, CallToolResponse & ToolResponse>;
|
|
62
107
|
callToolAsync: CallToolAsyncFn<ToolArgs, CallToolResponse & ToolResponse>;
|
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
import { useRef, useState } from "react";
|
|
2
2
|
import { getAdaptor, } from "../bridges/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Call a server tool from a view and track its execution state.
|
|
5
|
+
*
|
|
6
|
+
* Returns the current {@link CallToolState} plus two callers: `callTool`
|
|
7
|
+
* (fire-and-forget, with optional {@link SideEffects}) and `callToolAsync`
|
|
8
|
+
* (promise-returning). If the same instance is invoked again while a call is
|
|
9
|
+
* in flight, the older response is dropped from the rendered state (but any
|
|
10
|
+
* `onSuccess` / `onError` / `onSettled` callbacks attached to it still fire).
|
|
11
|
+
*
|
|
12
|
+
* Pair with {@link useToolInfo} to read the result of the tool invocation
|
|
13
|
+
* that produced the current view. For end-to-end type safety across tool
|
|
14
|
+
* inputs and outputs, prefer the typed helpers produced by {@link generateHelpers}
|
|
15
|
+
* over calling this hook directly.
|
|
16
|
+
*
|
|
17
|
+
* @typeParam ToolArgs - Shape of the tool's input args (`null` for no-arg tools).
|
|
18
|
+
* @typeParam ToolResponse - Shape of the tool's `structuredContent` / `meta`.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* const { callTool, isPending, data } = useCallTool<{ query: string }>("search");
|
|
23
|
+
*
|
|
24
|
+
* <button onClick={() => callTool({ query: "skybridge" }, {
|
|
25
|
+
* onSuccess: (res) => console.log(res.structuredContent),
|
|
26
|
+
* })} />
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @see https://docs.skybridge.tech/api-reference/use-call-tool
|
|
30
|
+
*/
|
|
3
31
|
export const useCallTool = (name) => {
|
|
4
32
|
const [{ status, data, error }, setCallToolState] = useState({ status: "idle", data: undefined, error: undefined });
|
|
5
33
|
const callIdRef = useRef(0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-call-tool.js","sourceRoot":"","sources":["../../../src/web/hooks/use-call-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEzC,OAAO,EAGL,UAAU,GACX,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"use-call-tool.js","sourceRoot":"","sources":["../../../src/web/hooks/use-call-tool.ts"],"names":[],"mappings":"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","sourcesContent":["import { useRef, useState } from \"react\";\n\nimport {\n type CallToolArgs,\n type CallToolResponse,\n getAdaptor,\n} from \"../bridges/index.js\";\nimport type { HasRequiredKeys } from \"../types.js\";\n\ntype CallToolIdleState = {\n status: \"idle\";\n isIdle: true;\n isPending: false;\n isSuccess: false;\n isError: false;\n data: undefined;\n error: undefined;\n};\n\ntype CallToolPendingState = {\n status: \"pending\";\n isIdle: false;\n isPending: true;\n isSuccess: false;\n isError: false;\n data: undefined;\n error: undefined;\n};\n\ntype CallToolSuccessState<TData extends CallToolResponse = CallToolResponse> = {\n status: \"success\";\n isIdle: false;\n isPending: false;\n isSuccess: true;\n isError: false;\n data: TData;\n error: undefined;\n};\n\ntype CallToolErrorState = {\n status: \"error\";\n isIdle: false;\n isPending: false;\n isSuccess: false;\n isError: true;\n data: undefined;\n error: unknown;\n};\n\n/**\n * State of a {@link useCallTool} invocation, discriminated by `status`.\n * Use `isIdle` / `isPending` / `isSuccess` / `isError` for ergonomic conditional rendering.\n */\nexport type CallToolState<TData extends CallToolResponse = CallToolResponse> =\n | CallToolIdleState\n | CallToolPendingState\n | CallToolSuccessState<TData>\n | CallToolErrorState;\n\n/**\n * Optional callbacks fired around a {@link useCallTool} call.\n * `onSettled` runs after success or error.\n */\nexport type SideEffects<ToolArgs, ToolResponse> = {\n onSuccess?: (data: ToolResponse, toolArgs: ToolArgs) => void;\n onError?: (error: unknown, toolArgs: ToolArgs) => void;\n onSettled?: (\n data: ToolResponse | undefined,\n error: unknown | undefined,\n toolArgs: ToolArgs,\n ) => void;\n};\n\ntype IsArgsOptional<T> = [T] extends [null]\n ? true\n : HasRequiredKeys<T> extends false\n ? true\n : false;\n\n/**\n * Fire-and-forget call function returned by {@link useCallTool}. Tracks state\n * on the hook and supports optional {@link SideEffects} callbacks. Args are\n * optional when the tool accepts none.\n */\nexport type CallToolFn<TArgs, TResponse> =\n IsArgsOptional<TArgs> extends true\n ? {\n (): void;\n (sideEffects: SideEffects<TArgs, TResponse>): void;\n (args: TArgs): void;\n (args: TArgs, sideEffects: SideEffects<TArgs, TResponse>): void;\n }\n : {\n (args: TArgs): void;\n (args: TArgs, sideEffects: SideEffects<TArgs, TResponse>): void;\n };\n\n/**\n * Promise-returning call function returned by {@link useCallTool}. Rejects\n * if the tool errors; use `try/catch` for error handling.\n */\nexport type CallToolAsyncFn<TArgs, TResponse> =\n IsArgsOptional<TArgs> extends true\n ? {\n (): Promise<TResponse>;\n (args: TArgs): Promise<TResponse>;\n }\n : (args: TArgs) => Promise<TResponse>;\n\ntype ToolResponseSignature = Pick<\n CallToolResponse,\n \"structuredContent\" | \"meta\"\n>;\n\n/**\n * Call a server tool from a view and track its execution state.\n *\n * Returns the current {@link CallToolState} plus two callers: `callTool`\n * (fire-and-forget, with optional {@link SideEffects}) and `callToolAsync`\n * (promise-returning). If the same instance is invoked again while a call is\n * in flight, the older response is dropped from the rendered state (but any\n * `onSuccess` / `onError` / `onSettled` callbacks attached to it still fire).\n *\n * Pair with {@link useToolInfo} to read the result of the tool invocation\n * that produced the current view. For end-to-end type safety across tool\n * inputs and outputs, prefer the typed helpers produced by {@link generateHelpers}\n * over calling this hook directly.\n *\n * @typeParam ToolArgs - Shape of the tool's input args (`null` for no-arg tools).\n * @typeParam ToolResponse - Shape of the tool's `structuredContent` / `meta`.\n *\n * @example\n * ```tsx\n * const { callTool, isPending, data } = useCallTool<{ query: string }>(\"search\");\n *\n * <button onClick={() => callTool({ query: \"skybridge\" }, {\n * onSuccess: (res) => console.log(res.structuredContent),\n * })} />\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-call-tool\n */\nexport const useCallTool = <\n ToolArgs extends CallToolArgs = null,\n ToolResponse extends Partial<ToolResponseSignature> = Record<string, never>,\n>(\n name: string,\n) => {\n type CombinedCallToolResponse = CallToolResponse & ToolResponse;\n\n const [{ status, data, error }, setCallToolState] = useState<\n Omit<\n CallToolState<CombinedCallToolResponse>,\n \"isIdle\" | \"isPending\" | \"isSuccess\" | \"isError\"\n >\n >({ status: \"idle\", data: undefined, error: undefined });\n\n const callIdRef = useRef(0);\n const adaptor = getAdaptor();\n\n const execute = async (\n toolArgs: ToolArgs,\n ): Promise<CombinedCallToolResponse> => {\n const callId = ++callIdRef.current;\n setCallToolState({ status: \"pending\", data: undefined, error: undefined });\n\n try {\n const data = await adaptor.callTool<ToolArgs, CombinedCallToolResponse>(\n name,\n toolArgs,\n );\n if (callId === callIdRef.current) {\n setCallToolState({ status: \"success\", data, error: undefined });\n }\n\n return data;\n } catch (error) {\n if (callId === callIdRef.current) {\n setCallToolState({ status: \"error\", data: undefined, error });\n }\n throw error;\n }\n };\n\n const callToolAsync = ((toolArgs?: ToolArgs) => {\n if (toolArgs === undefined) {\n return execute(null as ToolArgs);\n }\n return execute(toolArgs);\n }) as CallToolAsyncFn<ToolArgs, CombinedCallToolResponse>;\n\n const callTool = ((\n firstArg?: ToolArgs | SideEffects<ToolArgs, CombinedCallToolResponse>,\n sideEffects?: SideEffects<ToolArgs, CombinedCallToolResponse>,\n ) => {\n let toolArgs: ToolArgs;\n if (\n firstArg &&\n typeof firstArg === \"object\" &&\n (\"onSuccess\" in firstArg ||\n \"onError\" in firstArg ||\n \"onSettled\" in firstArg)\n ) {\n toolArgs = null as ToolArgs; // no toolArgs provided\n sideEffects = firstArg;\n } else {\n toolArgs = (firstArg === undefined ? null : firstArg) as ToolArgs;\n }\n\n execute(toolArgs)\n .then((data) => {\n sideEffects?.onSuccess?.(data, toolArgs);\n sideEffects?.onSettled?.(data, undefined, toolArgs);\n })\n .catch((error) => {\n sideEffects?.onError?.(error, toolArgs);\n sideEffects?.onSettled?.(undefined, error, toolArgs);\n });\n }) as CallToolFn<ToolArgs, CombinedCallToolResponse>;\n\n const callToolState = {\n status,\n data,\n error,\n isIdle: status === \"idle\",\n isPending: status === \"pending\",\n isSuccess: status === \"success\",\n isError: status === \"error\",\n } as CallToolState<CombinedCallToolResponse>;\n\n return {\n ...callToolState,\n callTool,\n callToolAsync,\n };\n};\n"]}
|
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
import { act, renderHook, waitFor } from "@testing-library/react";
|
|
2
2
|
import { afterEach, beforeEach, describe, expect, expectTypeOf, it, vi, } from "vitest";
|
|
3
|
+
import { AppsSdkBridge } from "../bridges/apps-sdk/bridge.js";
|
|
4
|
+
import { _resetAdaptor, getAdaptor } from "../bridges/get-adaptor.js";
|
|
5
|
+
import { McpAppBridge } from "../bridges/mcp-app/bridge.js";
|
|
3
6
|
import { useCallTool } from "./use-call-tool.js";
|
|
4
7
|
describe("useCallTool - onSuccess callback", () => {
|
|
5
|
-
let
|
|
8
|
+
let callToolMock;
|
|
6
9
|
beforeEach(() => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
vi.stubGlobal("
|
|
10
|
+
_resetAdaptor();
|
|
11
|
+
McpAppBridge.resetInstance();
|
|
12
|
+
AppsSdkBridge.resetInstance();
|
|
13
|
+
vi.stubGlobal("parent", { postMessage: vi.fn() });
|
|
11
14
|
vi.stubGlobal("skybridge", { hostType: "apps-sdk" });
|
|
15
|
+
vi.stubGlobal("openai", { callTool: vi.fn() });
|
|
16
|
+
callToolMock = vi.spyOn(getAdaptor(), "callTool");
|
|
12
17
|
});
|
|
13
18
|
afterEach(() => {
|
|
14
19
|
vi.unstubAllGlobals();
|
|
15
20
|
vi.resetAllMocks();
|
|
21
|
+
_resetAdaptor();
|
|
22
|
+
McpAppBridge.resetInstance();
|
|
23
|
+
AppsSdkBridge.resetInstance();
|
|
16
24
|
});
|
|
17
25
|
const toolName = "test-tool";
|
|
18
26
|
const args = { input: "test input" };
|
|
@@ -28,9 +36,9 @@ describe("useCallTool - onSuccess callback", () => {
|
|
|
28
36
|
content: [{ type: "text", text: "result" }],
|
|
29
37
|
structuredContent: { value: 1 },
|
|
30
38
|
isError: false,
|
|
31
|
-
|
|
39
|
+
meta: { secret: "only visible to widget" },
|
|
32
40
|
};
|
|
33
|
-
|
|
41
|
+
callToolMock.mockResolvedValueOnce(rawSdkResponse);
|
|
34
42
|
const { result } = renderHook(() => useCallTool(toolName));
|
|
35
43
|
await act(async () => {
|
|
36
44
|
result.current.callTool(args);
|
|
@@ -41,17 +49,17 @@ describe("useCallTool - onSuccess callback", () => {
|
|
|
41
49
|
});
|
|
42
50
|
});
|
|
43
51
|
});
|
|
44
|
-
it("should call
|
|
52
|
+
it("should call adaptor.callTool with correct arguments", async () => {
|
|
45
53
|
const { result } = renderHook(() => useCallTool(toolName));
|
|
46
54
|
await act(async () => {
|
|
47
55
|
result.current.callTool(args);
|
|
48
56
|
});
|
|
49
|
-
expect(
|
|
57
|
+
expect(callToolMock).toHaveBeenCalledWith(toolName, args);
|
|
50
58
|
});
|
|
51
59
|
it("should call onSuccess callback with correct data and toolArgs on successful execution", async () => {
|
|
52
60
|
const onSuccess = vi.fn();
|
|
53
61
|
const onError = vi.fn();
|
|
54
|
-
|
|
62
|
+
callToolMock.mockResolvedValueOnce(data);
|
|
55
63
|
const { result } = renderHook(() => useCallTool(toolName));
|
|
56
64
|
act(() => {
|
|
57
65
|
result.current.callTool(args, {
|
|
@@ -67,7 +75,7 @@ describe("useCallTool - onSuccess callback", () => {
|
|
|
67
75
|
it("should call onError callback with error and toolArgs on failed execution", async () => {
|
|
68
76
|
const onSuccess = vi.fn();
|
|
69
77
|
const onError = vi.fn();
|
|
70
|
-
|
|
78
|
+
callToolMock.mockRejectedValueOnce(error);
|
|
71
79
|
const { result } = renderHook(() => useCallTool(toolName));
|
|
72
80
|
act(() => {
|
|
73
81
|
result.current.callTool(args, {
|
|
@@ -84,7 +92,7 @@ describe("useCallTool - onSuccess callback", () => {
|
|
|
84
92
|
const onSuccess = vi.fn();
|
|
85
93
|
const onError = vi.fn();
|
|
86
94
|
const onSettled = vi.fn();
|
|
87
|
-
|
|
95
|
+
callToolMock.mockResolvedValueOnce(data);
|
|
88
96
|
const { result } = renderHook(() => useCallTool(toolName));
|
|
89
97
|
act(() => {
|
|
90
98
|
result.current.callTool(args, {
|
|
@@ -103,7 +111,7 @@ describe("useCallTool - onSuccess callback", () => {
|
|
|
103
111
|
const onSuccess = vi.fn();
|
|
104
112
|
const onError = vi.fn();
|
|
105
113
|
const onSettled = vi.fn();
|
|
106
|
-
|
|
114
|
+
callToolMock.mockRejectedValueOnce(error);
|
|
107
115
|
const { result } = renderHook(() => useCallTool(toolName));
|
|
108
116
|
act(() => {
|
|
109
117
|
result.current.callTool(args, {
|
|
@@ -130,7 +138,7 @@ describe("useCallTool - onSuccess callback", () => {
|
|
|
130
138
|
};
|
|
131
139
|
const { promise: firstCallToolPromise, resolve: resolveFirstCallTool } = Promise.withResolvers();
|
|
132
140
|
const { promise: secondCallToolPromise, resolve: resolveSecondCallTool } = Promise.withResolvers();
|
|
133
|
-
|
|
141
|
+
callToolMock
|
|
134
142
|
.mockImplementationOnce(() => firstCallToolPromise)
|
|
135
143
|
.mockImplementationOnce(() => secondCallToolPromise);
|
|
136
144
|
await act(() => {
|
|
@@ -149,17 +157,22 @@ describe("useCallTool - onSuccess callback", () => {
|
|
|
149
157
|
});
|
|
150
158
|
});
|
|
151
159
|
describe("useCallTool - TypeScript typing", () => {
|
|
152
|
-
let
|
|
160
|
+
let callToolMock;
|
|
153
161
|
beforeEach(() => {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
vi.stubGlobal("
|
|
162
|
+
_resetAdaptor();
|
|
163
|
+
McpAppBridge.resetInstance();
|
|
164
|
+
AppsSdkBridge.resetInstance();
|
|
165
|
+
vi.stubGlobal("parent", { postMessage: vi.fn() });
|
|
158
166
|
vi.stubGlobal("skybridge", { hostType: "apps-sdk" });
|
|
167
|
+
vi.stubGlobal("openai", { callTool: vi.fn() });
|
|
168
|
+
callToolMock = vi.spyOn(getAdaptor(), "callTool");
|
|
159
169
|
});
|
|
160
170
|
afterEach(() => {
|
|
161
171
|
vi.unstubAllGlobals();
|
|
162
172
|
vi.resetAllMocks();
|
|
173
|
+
_resetAdaptor();
|
|
174
|
+
McpAppBridge.resetInstance();
|
|
175
|
+
AppsSdkBridge.resetInstance();
|
|
163
176
|
});
|
|
164
177
|
it("should have correct return types when ToolArgs is null and ToolResponse is specified", async () => {
|
|
165
178
|
const { result } = renderHook(() => useCallTool("test-tool"));
|
|
@@ -169,11 +182,11 @@ describe("useCallTool - TypeScript typing", () => {
|
|
|
169
182
|
isError: false,
|
|
170
183
|
meta: { id: 123 },
|
|
171
184
|
};
|
|
172
|
-
|
|
185
|
+
callToolMock.mockResolvedValueOnce(data);
|
|
173
186
|
await act(async () => {
|
|
174
187
|
result.current.callTool();
|
|
175
188
|
});
|
|
176
|
-
expect(
|
|
189
|
+
expect(callToolMock).toHaveBeenCalledWith("test-tool", null);
|
|
177
190
|
expectTypeOf(result.current.data);
|
|
178
191
|
});
|
|
179
192
|
it("should correctly type callToolAsync return value", async () => {
|
|
@@ -185,7 +198,7 @@ describe("useCallTool - TypeScript typing", () => {
|
|
|
185
198
|
isError: false,
|
|
186
199
|
meta: {},
|
|
187
200
|
};
|
|
188
|
-
|
|
201
|
+
callToolMock.mockResolvedValueOnce(mockResponse);
|
|
189
202
|
const returnedValue = await act(async () => {
|
|
190
203
|
return result.current.callToolAsync(testArgs);
|
|
191
204
|
});
|
|
@@ -200,7 +213,7 @@ describe("useCallTool - TypeScript typing", () => {
|
|
|
200
213
|
isError: false,
|
|
201
214
|
meta: {},
|
|
202
215
|
};
|
|
203
|
-
|
|
216
|
+
callToolMock.mockResolvedValueOnce(mockResponse);
|
|
204
217
|
const returnedValue = await act(async () => {
|
|
205
218
|
return result.current.callToolAsync();
|
|
206
219
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-call-tool.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-call-tool.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { act, renderHook, waitFor } from \"@testing-library/react\";\nimport {\n afterEach,\n beforeEach,\n describe,\n expect,\n expectTypeOf,\n it,\n type Mock,\n vi,\n} from \"vitest\";\n\nimport type { CallToolResponse } from \"../bridges/types.js\";\nimport { useCallTool } from \"./use-call-tool.js\";\n\ndescribe(\"useCallTool - onSuccess callback\", () => {\n let OpenaiMock: { callTool: Mock };\n\n beforeEach(() => {\n OpenaiMock = {\n callTool: vi.fn(),\n };\n vi.stubGlobal(\"openai\", OpenaiMock);\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n });\n\n const toolName = \"test-tool\";\n const args = { input: \"test input\" };\n const data: CallToolResponse = {\n content: [{ type: \"text\" as const, text: \"test result\" }],\n structuredContent: { result: \"test\" },\n isError: false,\n meta: {},\n };\n const error = new Error(\"test error\");\n\n it(\"should normalize _meta to meta when SDK returns _meta instead of meta\", async () => {\n const rawSdkResponse = {\n content: [{ type: \"text\" as const, text: \"result\" }],\n structuredContent: { value: 1 },\n isError: false,\n _meta: { secret: \"only visible to widget\" },\n };\n OpenaiMock.callTool.mockResolvedValueOnce(rawSdkResponse);\n const { result } = renderHook(() =>\n useCallTool<typeof args, typeof data>(toolName),\n );\n\n await act(async () => {\n result.current.callTool(args);\n });\n\n await waitFor(() => {\n expect(result.current.data).toMatchObject({\n meta: { secret: \"only visible to widget\" },\n });\n });\n });\n\n it(\"should call window.openai.callTool with correct arguments\", async () => {\n const { result } = renderHook(() =>\n useCallTool<typeof args, typeof data>(toolName),\n );\n await act(async () => {\n result.current.callTool(args);\n });\n expect(OpenaiMock.callTool).toHaveBeenCalledWith(toolName, args);\n });\n\n it(\"should call onSuccess callback with correct data and toolArgs on successful execution\", async () => {\n const onSuccess = vi.fn();\n const onError = vi.fn();\n OpenaiMock.callTool.mockResolvedValueOnce(data);\n const { result } = renderHook(() =>\n useCallTool<typeof args, typeof data>(toolName),\n );\n\n act(() => {\n result.current.callTool(args, {\n onSuccess,\n onError,\n });\n });\n\n await waitFor(() => {\n expect(onSuccess).toHaveBeenCalledWith(data, args);\n expect(onError).not.toHaveBeenCalled();\n });\n });\n\n it(\"should call onError callback with error and toolArgs on failed execution\", async () => {\n const onSuccess = vi.fn();\n const onError = vi.fn();\n OpenaiMock.callTool.mockRejectedValueOnce(error);\n const { result } = renderHook(() =>\n useCallTool<typeof args, typeof data>(toolName),\n );\n\n act(() => {\n result.current.callTool(args, {\n onSuccess,\n onError,\n });\n });\n\n await waitFor(() => {\n expect(onSuccess).not.toHaveBeenCalled();\n expect(onError).toHaveBeenCalledWith(error, args);\n });\n });\n\n it(\"should call onSettled callback with data and undefined error on successful execution\", async () => {\n const onSuccess = vi.fn();\n const onError = vi.fn();\n const onSettled = vi.fn();\n OpenaiMock.callTool.mockResolvedValueOnce(data);\n const { result } = renderHook(() =>\n useCallTool<typeof args, typeof data>(toolName),\n );\n\n act(() => {\n result.current.callTool(args, {\n onSuccess,\n onError,\n onSettled,\n });\n });\n\n await waitFor(() => {\n expect(onSuccess).toHaveBeenCalledWith(data, args);\n expect(onSettled).toHaveBeenCalledWith(data, undefined, args);\n expect(onError).not.toHaveBeenCalled();\n });\n });\n\n it(\"should call onSettled callback with undefined data and error on failed execution\", async () => {\n const onSuccess = vi.fn();\n const onError = vi.fn();\n const onSettled = vi.fn();\n OpenaiMock.callTool.mockRejectedValueOnce(error);\n const { result } = renderHook(() =>\n useCallTool<typeof args, typeof data>(toolName),\n );\n\n act(() => {\n result.current.callTool(args, {\n onSuccess,\n onError,\n onSettled,\n });\n });\n\n await waitFor(() => {\n expect(onError).toHaveBeenCalledWith(error, args);\n expect(onSettled).toHaveBeenCalledWith(undefined, error, args);\n expect(onSuccess).not.toHaveBeenCalled();\n });\n });\n\n it(\"should always return last call started state\", async () => {\n const { result } = renderHook(() =>\n useCallTool<typeof args, typeof data>(toolName),\n );\n\n const firstCallData = {\n ...data,\n structuredContent: { result: \"first call result\" },\n };\n const secondCallData = {\n ...data,\n structuredContent: { result: \"second call result\" },\n };\n const { promise: firstCallToolPromise, resolve: resolveFirstCallTool } =\n Promise.withResolvers();\n const { promise: secondCallToolPromise, resolve: resolveSecondCallTool } =\n Promise.withResolvers();\n OpenaiMock.callTool\n .mockImplementationOnce(() => firstCallToolPromise)\n .mockImplementationOnce(() => secondCallToolPromise);\n\n await act(() => {\n result.current.callTool(args);\n result.current.callTool(args);\n resolveFirstCallTool(firstCallData);\n return firstCallToolPromise;\n });\n\n expect(result.current.status).toEqual(\"pending\");\n expect(result.current.data).toEqual(undefined);\n resolveSecondCallTool(secondCallData);\n\n await waitFor(() => {\n expect(result.current.status).toEqual(\"success\");\n expect(result.current.data).toEqual(secondCallData);\n });\n });\n});\n\ndescribe(\"useCallTool - TypeScript typing\", () => {\n let OpenaiMock: { callTool: Mock };\n\n beforeEach(() => {\n OpenaiMock = {\n callTool: vi.fn(),\n };\n vi.stubGlobal(\"openai\", OpenaiMock);\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n });\n\n it(\"should have correct return types when ToolArgs is null and ToolResponse is specified\", async () => {\n type TestResponse = CallToolResponse & {\n structuredContent: { result: string };\n meta: { id: number };\n };\n\n const { result } = renderHook(() =>\n useCallTool<null, TestResponse>(\"test-tool\"),\n );\n const data: TestResponse = {\n content: [{ type: \"text\" as const, text: \"test\" }],\n structuredContent: { result: \"test\" },\n isError: false,\n meta: { id: 123 },\n };\n\n OpenaiMock.callTool.mockResolvedValueOnce(data);\n\n await act(async () => {\n result.current.callTool();\n });\n\n expect(OpenaiMock.callTool).toHaveBeenCalledWith(\"test-tool\", null);\n expectTypeOf<typeof data | undefined>(result.current.data);\n });\n\n it(\"should correctly type callToolAsync return value\", async () => {\n type TestArgs = { query: string };\n type TestResponse = CallToolResponse & {\n structuredContent: { answer: string };\n };\n\n const { result } = renderHook(() =>\n useCallTool<TestArgs, TestResponse>(\"test-tool\"),\n );\n\n const testArgs: TestArgs = { query: \"test\" };\n const mockResponse: TestResponse = {\n content: [{ type: \"text\" as const, text: \"answer\" }],\n structuredContent: { answer: \"test answer\" },\n isError: false,\n meta: {},\n };\n\n OpenaiMock.callTool.mockResolvedValueOnce(mockResponse);\n\n const returnedValue = await act(async () => {\n return result.current.callToolAsync(testArgs);\n });\n expectTypeOf<typeof mockResponse>(returnedValue);\n expect(returnedValue).toEqual(mockResponse);\n });\n\n it(\"should correctly type callToolAsync when ToolArgs is null\", async () => {\n type TestResponse = CallToolResponse & {\n structuredContent: { data: string };\n };\n\n const { result } = renderHook(() =>\n useCallTool<null, TestResponse>(\"test-tool\"),\n );\n\n const mockResponse: TestResponse = {\n content: [{ type: \"text\" as const, text: \"data\" }],\n structuredContent: { data: \"test data\" },\n isError: false,\n meta: {},\n };\n\n OpenaiMock.callTool.mockResolvedValueOnce(mockResponse);\n\n const returnedValue = await act(async () => {\n return result.current.callToolAsync();\n });\n expectTypeOf<typeof mockResponse>(returnedValue);\n expect(returnedValue).toEqual(mockResponse);\n });\n});\n"]}
|
|
1
|
+
{"version":3,"file":"use-call-tool.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-call-tool.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { act, renderHook, waitFor } from \"@testing-library/react\";\nimport {\n afterEach,\n beforeEach,\n describe,\n expect,\n expectTypeOf,\n it,\n type Mock,\n vi,\n} from \"vitest\";\n\nimport { AppsSdkBridge } from \"../bridges/apps-sdk/bridge.js\";\nimport { _resetAdaptor, getAdaptor } from \"../bridges/get-adaptor.js\";\nimport { McpAppBridge } from \"../bridges/mcp-app/bridge.js\";\nimport type { CallToolResponse } from \"../bridges/types.js\";\nimport { useCallTool } from \"./use-call-tool.js\";\n\ndescribe(\"useCallTool - onSuccess callback\", () => {\n let callToolMock: Mock;\n\n beforeEach(() => {\n _resetAdaptor();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n vi.stubGlobal(\"openai\", { callTool: vi.fn() });\n callToolMock = vi.spyOn(getAdaptor(), \"callTool\") as unknown as Mock;\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n _resetAdaptor();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n });\n\n const toolName = \"test-tool\";\n const args = { input: \"test input\" };\n const data: CallToolResponse = {\n content: [{ type: \"text\" as const, text: \"test result\" }],\n structuredContent: { result: \"test\" },\n isError: false,\n meta: {},\n };\n const error = new Error(\"test error\");\n\n it(\"should normalize _meta to meta when SDK returns _meta instead of meta\", async () => {\n const rawSdkResponse = {\n content: [{ type: \"text\" as const, text: \"result\" }],\n structuredContent: { value: 1 },\n isError: false,\n meta: { secret: \"only visible to widget\" },\n };\n callToolMock.mockResolvedValueOnce(rawSdkResponse);\n const { result } = renderHook(() =>\n useCallTool<typeof args, typeof data>(toolName),\n );\n\n await act(async () => {\n result.current.callTool(args);\n });\n\n await waitFor(() => {\n expect(result.current.data).toMatchObject({\n meta: { secret: \"only visible to widget\" },\n });\n });\n });\n\n it(\"should call adaptor.callTool with correct arguments\", async () => {\n const { result } = renderHook(() =>\n useCallTool<typeof args, typeof data>(toolName),\n );\n await act(async () => {\n result.current.callTool(args);\n });\n expect(callToolMock).toHaveBeenCalledWith(toolName, args);\n });\n\n it(\"should call onSuccess callback with correct data and toolArgs on successful execution\", async () => {\n const onSuccess = vi.fn();\n const onError = vi.fn();\n callToolMock.mockResolvedValueOnce(data);\n const { result } = renderHook(() =>\n useCallTool<typeof args, typeof data>(toolName),\n );\n\n act(() => {\n result.current.callTool(args, {\n onSuccess,\n onError,\n });\n });\n\n await waitFor(() => {\n expect(onSuccess).toHaveBeenCalledWith(data, args);\n expect(onError).not.toHaveBeenCalled();\n });\n });\n\n it(\"should call onError callback with error and toolArgs on failed execution\", async () => {\n const onSuccess = vi.fn();\n const onError = vi.fn();\n callToolMock.mockRejectedValueOnce(error);\n const { result } = renderHook(() =>\n useCallTool<typeof args, typeof data>(toolName),\n );\n\n act(() => {\n result.current.callTool(args, {\n onSuccess,\n onError,\n });\n });\n\n await waitFor(() => {\n expect(onSuccess).not.toHaveBeenCalled();\n expect(onError).toHaveBeenCalledWith(error, args);\n });\n });\n\n it(\"should call onSettled callback with data and undefined error on successful execution\", async () => {\n const onSuccess = vi.fn();\n const onError = vi.fn();\n const onSettled = vi.fn();\n callToolMock.mockResolvedValueOnce(data);\n const { result } = renderHook(() =>\n useCallTool<typeof args, typeof data>(toolName),\n );\n\n act(() => {\n result.current.callTool(args, {\n onSuccess,\n onError,\n onSettled,\n });\n });\n\n await waitFor(() => {\n expect(onSuccess).toHaveBeenCalledWith(data, args);\n expect(onSettled).toHaveBeenCalledWith(data, undefined, args);\n expect(onError).not.toHaveBeenCalled();\n });\n });\n\n it(\"should call onSettled callback with undefined data and error on failed execution\", async () => {\n const onSuccess = vi.fn();\n const onError = vi.fn();\n const onSettled = vi.fn();\n callToolMock.mockRejectedValueOnce(error);\n const { result } = renderHook(() =>\n useCallTool<typeof args, typeof data>(toolName),\n );\n\n act(() => {\n result.current.callTool(args, {\n onSuccess,\n onError,\n onSettled,\n });\n });\n\n await waitFor(() => {\n expect(onError).toHaveBeenCalledWith(error, args);\n expect(onSettled).toHaveBeenCalledWith(undefined, error, args);\n expect(onSuccess).not.toHaveBeenCalled();\n });\n });\n\n it(\"should always return last call started state\", async () => {\n const { result } = renderHook(() =>\n useCallTool<typeof args, typeof data>(toolName),\n );\n\n const firstCallData = {\n ...data,\n structuredContent: { result: \"first call result\" },\n };\n const secondCallData = {\n ...data,\n structuredContent: { result: \"second call result\" },\n };\n const { promise: firstCallToolPromise, resolve: resolveFirstCallTool } =\n Promise.withResolvers();\n const { promise: secondCallToolPromise, resolve: resolveSecondCallTool } =\n Promise.withResolvers();\n callToolMock\n .mockImplementationOnce(() => firstCallToolPromise)\n .mockImplementationOnce(() => secondCallToolPromise);\n\n await act(() => {\n result.current.callTool(args);\n result.current.callTool(args);\n resolveFirstCallTool(firstCallData);\n return firstCallToolPromise;\n });\n\n expect(result.current.status).toEqual(\"pending\");\n expect(result.current.data).toEqual(undefined);\n resolveSecondCallTool(secondCallData);\n\n await waitFor(() => {\n expect(result.current.status).toEqual(\"success\");\n expect(result.current.data).toEqual(secondCallData);\n });\n });\n});\n\ndescribe(\"useCallTool - TypeScript typing\", () => {\n let callToolMock: Mock;\n\n beforeEach(() => {\n _resetAdaptor();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n vi.stubGlobal(\"openai\", { callTool: vi.fn() });\n callToolMock = vi.spyOn(getAdaptor(), \"callTool\") as unknown as Mock;\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n _resetAdaptor();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n });\n\n it(\"should have correct return types when ToolArgs is null and ToolResponse is specified\", async () => {\n type TestResponse = CallToolResponse & {\n structuredContent: { result: string };\n meta: { id: number };\n };\n\n const { result } = renderHook(() =>\n useCallTool<null, TestResponse>(\"test-tool\"),\n );\n const data: TestResponse = {\n content: [{ type: \"text\" as const, text: \"test\" }],\n structuredContent: { result: \"test\" },\n isError: false,\n meta: { id: 123 },\n };\n\n callToolMock.mockResolvedValueOnce(data);\n\n await act(async () => {\n result.current.callTool();\n });\n\n expect(callToolMock).toHaveBeenCalledWith(\"test-tool\", null);\n expectTypeOf<typeof data | undefined>(result.current.data);\n });\n\n it(\"should correctly type callToolAsync return value\", async () => {\n type TestArgs = { query: string };\n type TestResponse = CallToolResponse & {\n structuredContent: { answer: string };\n };\n\n const { result } = renderHook(() =>\n useCallTool<TestArgs, TestResponse>(\"test-tool\"),\n );\n\n const testArgs: TestArgs = { query: \"test\" };\n const mockResponse: TestResponse = {\n content: [{ type: \"text\" as const, text: \"answer\" }],\n structuredContent: { answer: \"test answer\" },\n isError: false,\n meta: {},\n };\n\n callToolMock.mockResolvedValueOnce(mockResponse);\n\n const returnedValue = await act(async () => {\n return result.current.callToolAsync(testArgs);\n });\n expectTypeOf<typeof mockResponse>(returnedValue);\n expect(returnedValue).toEqual(mockResponse);\n });\n\n it(\"should correctly type callToolAsync when ToolArgs is null\", async () => {\n type TestResponse = CallToolResponse & {\n structuredContent: { data: string };\n };\n\n const { result } = renderHook(() =>\n useCallTool<null, TestResponse>(\"test-tool\"),\n );\n\n const mockResponse: TestResponse = {\n content: [{ type: \"text\" as const, text: \"data\" }],\n structuredContent: { data: \"test data\" },\n isError: false,\n meta: {},\n };\n\n callToolMock.mockResolvedValueOnce(mockResponse);\n\n const returnedValue = await act(async () => {\n return result.current.callToolAsync();\n });\n expectTypeOf<typeof mockResponse>(returnedValue);\n expect(returnedValue).toEqual(mockResponse);\n });\n});\n"]}
|
|
@@ -1,4 +1,24 @@
|
|
|
1
1
|
import type { RequestDisplayMode } from "../bridges/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Read and change the view's display mode (`"inline"`, `"pip"`, `"fullscreen"`).
|
|
4
|
+
*
|
|
5
|
+
* Returns a tuple `[displayMode, setDisplayMode]`. `setDisplayMode` asks the
|
|
6
|
+
* host to switch modes — the host returns the mode it actually applied, which
|
|
7
|
+
* may differ from the request. The reported value also updates when the host
|
|
8
|
+
* changes the mode on its own (e.g. user expands the widget).
|
|
9
|
+
*
|
|
10
|
+
* `"modal"` is reachable via {@link useRequestModal}, not this hook. To react
|
|
11
|
+
* to layout changes that come with display-mode switches (e.g. `maxHeight`),
|
|
12
|
+
* pair with {@link useLayout}.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* const [mode, setMode] = useDisplayMode();
|
|
17
|
+
* <button onClick={() => setMode("fullscreen")}>Expand</button>
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @see https://docs.skybridge.tech/api-reference/use-display-mode
|
|
21
|
+
*/
|
|
2
22
|
export declare function useDisplayMode(): readonly [import("../index.js").DisplayMode, (mode: RequestDisplayMode) => Promise<{
|
|
3
23
|
mode: RequestDisplayMode;
|
|
4
24
|
}>];
|