skybridge 0.0.0-dev.84c5e86 → 0.0.0-dev.84e1cce
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/build-helpers.d.ts +7 -0
- package/dist/cli/build-helpers.js +82 -0
- package/dist/cli/build-helpers.js.map +1 -0
- package/dist/cli/build-helpers.test.js +64 -0
- package/dist/cli/build-helpers.test.js.map +1 -0
- package/dist/cli/detect-port.d.ts +2 -2
- package/dist/cli/detect-port.js +9 -20
- package/dist/cli/detect-port.js.map +1 -1
- package/dist/cli/use-open-tunnel-browser.d.ts +6 -0
- package/dist/cli/use-open-tunnel-browser.js +19 -0
- package/dist/cli/use-open-tunnel-browser.js.map +1 -0
- package/dist/commands/build.d.ts +0 -1
- package/dist/commands/build.js +17 -14
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/dev.js +3 -1
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/start.js +7 -1
- package/dist/commands/start.js.map +1 -1
- package/dist/server/build-manifest.test.js +27 -0
- package/dist/server/build-manifest.test.js.map +1 -0
- package/dist/server/express.test.js +30 -0
- package/dist/server/express.test.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/server.d.ts +10 -27
- package/dist/server/server.js +39 -0
- package/dist/server/server.js.map +1 -1
- package/dist/web/bridges/apps-sdk/adaptor.d.ts +29 -0
- package/dist/web/bridges/apps-sdk/adaptor.js +117 -0
- package/dist/web/bridges/apps-sdk/adaptor.js.map +1 -0
- package/dist/web/bridges/apps-sdk/bridge.d.ts +1 -1
- package/dist/web/bridges/apps-sdk/bridge.js +4 -3
- package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
- package/dist/web/bridges/apps-sdk/index.d.ts +1 -0
- package/dist/web/bridges/apps-sdk/index.js +1 -0
- package/dist/web/bridges/apps-sdk/index.js.map +1 -1
- package/dist/web/bridges/get-adaptor.d.ts +4 -6
- package/dist/web/bridges/get-adaptor.js +9 -23
- package/dist/web/bridges/get-adaptor.js.map +1 -1
- package/dist/web/bridges/mcp-app/adaptor.d.ts +53 -0
- package/dist/web/bridges/mcp-app/adaptor.js +283 -0
- package/dist/web/bridges/mcp-app/adaptor.js.map +1 -0
- package/dist/web/bridges/mcp-app/bridge.d.ts +4 -3
- package/dist/web/bridges/mcp-app/bridge.js +26 -2
- package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
- package/dist/web/bridges/mcp-app/index.d.ts +1 -0
- package/dist/web/bridges/mcp-app/index.js +1 -0
- package/dist/web/bridges/mcp-app/index.js.map +1 -1
- package/dist/web/bridges/mcp-app/view-tools.test.js +144 -0
- package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -0
- package/dist/web/bridges/types.d.ts +34 -16
- package/dist/web/bridges/types.js +1 -14
- package/dist/web/bridges/types.js.map +1 -1
- package/dist/web/components/modal-provider.js +3 -4
- package/dist/web/components/modal-provider.js.map +1 -1
- package/dist/web/create-store.test.js +5 -7
- package/dist/web/create-store.test.js.map +1 -1
- package/dist/web/data-llm.test.js +5 -8
- package/dist/web/data-llm.test.js.map +1 -1
- package/dist/web/hooks/index.d.ts +1 -0
- package/dist/web/hooks/index.js +1 -0
- package/dist/web/hooks/index.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test.js +23 -36
- package/dist/web/hooks/use-call-tool.test.js.map +1 -1
- package/dist/web/hooks/use-display-mode.test.js +20 -55
- package/dist/web/hooks/use-display-mode.test.js.map +1 -1
- package/dist/web/hooks/use-download.test.js +3 -11
- package/dist/web/hooks/use-download.test.js.map +1 -1
- package/dist/web/hooks/use-files.test.js +2 -10
- package/dist/web/hooks/use-files.test.js.map +1 -1
- package/dist/web/hooks/use-layout.test.js +26 -59
- package/dist/web/hooks/use-layout.test.js.map +1 -1
- package/dist/web/hooks/use-open-external.test.js +5 -19
- package/dist/web/hooks/use-open-external.test.js.map +1 -1
- package/dist/web/hooks/use-register-view-tool.d.ts +38 -0
- package/dist/web/hooks/use-register-view-tool.js +50 -0
- package/dist/web/hooks/use-register-view-tool.js.map +1 -0
- package/dist/web/hooks/use-request-close.test.js +40 -34
- package/dist/web/hooks/use-request-close.test.js.map +1 -1
- package/dist/web/hooks/use-request-modal.test.js +0 -10
- package/dist/web/hooks/use-request-modal.test.js.map +1 -1
- package/dist/web/hooks/use-request-size.test.js +53 -35
- package/dist/web/hooks/use-request-size.test.js.map +1 -1
- package/dist/web/hooks/use-set-open-in-app-url.test.js +6 -26
- package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test.js +27 -37
- package/dist/web/hooks/use-tool-info.test.js.map +1 -1
- package/dist/web/hooks/use-user.test.js +35 -81
- package/dist/web/hooks/use-user.test.js.map +1 -1
- package/dist/web/hooks/use-view-state.test.js +2 -6
- package/dist/web/hooks/use-view-state.test.js.map +1 -1
- package/package.json +4 -2
- package/dist/web/bridges/adaptor.d.ts +0 -45
- package/dist/web/bridges/adaptor.js +0 -302
- package/dist/web/bridges/adaptor.js.map +0 -1
- package/dist/web/bridges/adaptor.test.js +0 -375
- package/dist/web/bridges/adaptor.test.js.map +0 -1
- package/dist/web/bridges/get-adaptor.test.js +0 -48
- package/dist/web/bridges/get-adaptor.test.js.map +0 -1
- package/dist/web/bridges/host-context-stores.d.ts +0 -27
- package/dist/web/bridges/host-context-stores.js +0 -73
- package/dist/web/bridges/host-context-stores.js.map +0 -1
- package/dist/web/bridges/mcp-app/bridge.test.js +0 -21
- package/dist/web/bridges/mcp-app/bridge.test.js.map +0 -1
- package/dist/web/bridges/types.test.d.ts +0 -1
- package/dist/web/bridges/types.test.js +0 -19
- package/dist/web/bridges/types.test.js.map +0 -1
- /package/dist/{web/bridges/adaptor.test.d.ts → cli/build-helpers.test.d.ts} +0 -0
- /package/dist/{web/bridges/get-adaptor.test.d.ts → server/build-manifest.test.d.ts} +0 -0
- /package/dist/web/bridges/mcp-app/{bridge.test.d.ts → view-tools.test.d.ts} +0 -0
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { HostAdaptor } from "./adaptor.js";
|
|
3
|
-
import { AppsSdkBridge } from "./apps-sdk/bridge.js";
|
|
4
|
-
import { McpAppBridge } from "./mcp-app/bridge.js";
|
|
5
|
-
import { NotSupportedError } from "./types.js";
|
|
6
|
-
describe("HostAdaptor constructor", () => {
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
McpAppBridge.resetInstance();
|
|
9
|
-
AppsSdkBridge.resetInstance();
|
|
10
|
-
vi.stubGlobal("skybridge", { hostType: "mcp-app" });
|
|
11
|
-
vi.stubGlobal("parent", { postMessage: vi.fn() });
|
|
12
|
-
});
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
vi.unstubAllGlobals();
|
|
15
|
-
McpAppBridge.resetInstance();
|
|
16
|
-
AppsSdkBridge.resetInstance();
|
|
17
|
-
});
|
|
18
|
-
it("instantiates without window.openai (oai is null)", () => {
|
|
19
|
-
vi.stubGlobal("openai", undefined);
|
|
20
|
-
const adaptor = new HostAdaptor();
|
|
21
|
-
expect(adaptor.hasAppsSdkOverlay()).toBe(false);
|
|
22
|
-
});
|
|
23
|
-
it("captures window.openai when present (oai is set)", () => {
|
|
24
|
-
vi.stubGlobal("openai", { widgetState: null });
|
|
25
|
-
const adaptor = new HostAdaptor();
|
|
26
|
-
expect(adaptor.hasAppsSdkOverlay()).toBe(true);
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
describe("HostAdaptor MCP-routed methods", () => {
|
|
30
|
-
beforeEach(() => {
|
|
31
|
-
McpAppBridge.resetInstance();
|
|
32
|
-
AppsSdkBridge.resetInstance();
|
|
33
|
-
vi.stubGlobal("skybridge", { hostType: "mcp-app" });
|
|
34
|
-
vi.stubGlobal("parent", { postMessage: vi.fn() });
|
|
35
|
-
vi.stubGlobal("openai", undefined);
|
|
36
|
-
});
|
|
37
|
-
afterEach(() => {
|
|
38
|
-
vi.unstubAllGlobals();
|
|
39
|
-
McpAppBridge.resetInstance();
|
|
40
|
-
AppsSdkBridge.resetInstance();
|
|
41
|
-
});
|
|
42
|
-
it("callTool delegates to mcp.callServerTool", async () => {
|
|
43
|
-
const adaptor = new HostAdaptor();
|
|
44
|
-
const fakeApp = {
|
|
45
|
-
callServerTool: vi.fn().mockResolvedValue({
|
|
46
|
-
content: [],
|
|
47
|
-
structuredContent: { ok: true },
|
|
48
|
-
isError: false,
|
|
49
|
-
_meta: { x: 1 },
|
|
50
|
-
}),
|
|
51
|
-
};
|
|
52
|
-
// biome-ignore lint/suspicious/noExplicitAny: test seam
|
|
53
|
-
adaptor.mcp.getApp = vi.fn().mockResolvedValue(fakeApp);
|
|
54
|
-
const result = await adaptor.callTool("greet", { name: "alice" });
|
|
55
|
-
expect(fakeApp.callServerTool).toHaveBeenCalledWith({
|
|
56
|
-
name: "greet",
|
|
57
|
-
arguments: { name: "alice" },
|
|
58
|
-
});
|
|
59
|
-
expect(result.structuredContent).toEqual({ ok: true });
|
|
60
|
-
expect(result.meta).toEqual({ x: 1 });
|
|
61
|
-
});
|
|
62
|
-
it("requestDisplayMode delegates to mcp.requestDisplayMode", async () => {
|
|
63
|
-
const adaptor = new HostAdaptor();
|
|
64
|
-
const fakeApp = {
|
|
65
|
-
requestDisplayMode: vi.fn().mockResolvedValue({ mode: "fullscreen" }),
|
|
66
|
-
};
|
|
67
|
-
// biome-ignore lint/suspicious/noExplicitAny: test seam
|
|
68
|
-
adaptor.mcp.getApp = vi.fn().mockResolvedValue(fakeApp);
|
|
69
|
-
const r = await adaptor.requestDisplayMode("fullscreen");
|
|
70
|
-
expect(r.mode).toBe("fullscreen");
|
|
71
|
-
expect(fakeApp.requestDisplayMode).toHaveBeenCalledWith({
|
|
72
|
-
mode: "fullscreen",
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
it("requestClose delegates to mcp.requestTeardown", async () => {
|
|
76
|
-
const adaptor = new HostAdaptor();
|
|
77
|
-
const fakeApp = { requestTeardown: vi.fn().mockResolvedValue(undefined) };
|
|
78
|
-
// biome-ignore lint/suspicious/noExplicitAny: test seam
|
|
79
|
-
adaptor.mcp.getApp = vi.fn().mockResolvedValue(fakeApp);
|
|
80
|
-
await adaptor.requestClose();
|
|
81
|
-
expect(fakeApp.requestTeardown).toHaveBeenCalled();
|
|
82
|
-
});
|
|
83
|
-
it("requestSize delegates to mcp.sendSizeChanged", async () => {
|
|
84
|
-
const adaptor = new HostAdaptor();
|
|
85
|
-
const fakeApp = { sendSizeChanged: vi.fn().mockResolvedValue(undefined) };
|
|
86
|
-
// biome-ignore lint/suspicious/noExplicitAny: test seam
|
|
87
|
-
adaptor.mcp.getApp = vi.fn().mockResolvedValue(fakeApp);
|
|
88
|
-
await adaptor.requestSize({ width: 100, height: 200 });
|
|
89
|
-
expect(fakeApp.sendSizeChanged).toHaveBeenCalledWith({
|
|
90
|
-
width: 100,
|
|
91
|
-
height: 200,
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
it("download returns isError when host lacks downloadFile capability", async () => {
|
|
95
|
-
const adaptor = new HostAdaptor();
|
|
96
|
-
const fakeApp = {
|
|
97
|
-
getHostCapabilities: () => ({}),
|
|
98
|
-
downloadFile: vi.fn(),
|
|
99
|
-
};
|
|
100
|
-
// biome-ignore lint/suspicious/noExplicitAny: test seam
|
|
101
|
-
adaptor.mcp.getApp = vi.fn().mockResolvedValue(fakeApp);
|
|
102
|
-
const r = await adaptor.download({ contents: [] });
|
|
103
|
-
expect(r.isError).toBe(true);
|
|
104
|
-
expect(fakeApp.downloadFile).not.toHaveBeenCalled();
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
describe("HostAdaptor conditional routing", () => {
|
|
108
|
-
beforeEach(() => {
|
|
109
|
-
McpAppBridge.resetInstance();
|
|
110
|
-
AppsSdkBridge.resetInstance();
|
|
111
|
-
vi.stubGlobal("skybridge", { hostType: "apps-sdk" });
|
|
112
|
-
vi.stubGlobal("parent", { postMessage: vi.fn() });
|
|
113
|
-
});
|
|
114
|
-
afterEach(() => {
|
|
115
|
-
vi.unstubAllGlobals();
|
|
116
|
-
McpAppBridge.resetInstance();
|
|
117
|
-
AppsSdkBridge.resetInstance();
|
|
118
|
-
});
|
|
119
|
-
it("sendFollowUpMessage uses oai when scrollToBottom set", async () => {
|
|
120
|
-
const sendFollowUpMessage = vi.fn().mockResolvedValue(undefined);
|
|
121
|
-
vi.stubGlobal("openai", { sendFollowUpMessage });
|
|
122
|
-
const adaptor = new HostAdaptor();
|
|
123
|
-
await adaptor.sendFollowUpMessage("hi", { scrollToBottom: false });
|
|
124
|
-
expect(sendFollowUpMessage).toHaveBeenCalledWith({
|
|
125
|
-
prompt: "hi",
|
|
126
|
-
scrollToBottom: false,
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
it("sendFollowUpMessage uses mcp when no options", async () => {
|
|
130
|
-
vi.stubGlobal("openai", undefined);
|
|
131
|
-
const adaptor = new HostAdaptor();
|
|
132
|
-
const fakeApp = { sendMessage: vi.fn().mockResolvedValue(undefined) };
|
|
133
|
-
// biome-ignore lint/suspicious/noExplicitAny: test seam
|
|
134
|
-
adaptor.mcp.getApp = vi.fn().mockResolvedValue(fakeApp);
|
|
135
|
-
await adaptor.sendFollowUpMessage("hi");
|
|
136
|
-
expect(fakeApp.sendMessage).toHaveBeenCalledWith({
|
|
137
|
-
role: "user",
|
|
138
|
-
content: [{ type: "text", text: "hi" }],
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
it("openExternal uses oai when redirectUrl: false", async () => {
|
|
142
|
-
const openExternal = vi.fn();
|
|
143
|
-
vi.stubGlobal("openai", { openExternal });
|
|
144
|
-
const adaptor = new HostAdaptor();
|
|
145
|
-
adaptor.openExternal("https://x", { redirectUrl: false });
|
|
146
|
-
expect(openExternal).toHaveBeenCalledWith({
|
|
147
|
-
href: "https://x",
|
|
148
|
-
redirectUrl: false,
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
it("openExternal uses mcp.openLink for default case", async () => {
|
|
152
|
-
vi.stubGlobal("openai", undefined);
|
|
153
|
-
const adaptor = new HostAdaptor();
|
|
154
|
-
const openLink = vi.fn();
|
|
155
|
-
// biome-ignore lint/suspicious/noExplicitAny: test seam
|
|
156
|
-
adaptor.mcp.getApp = vi.fn().mockResolvedValue({ openLink });
|
|
157
|
-
adaptor.openExternal("https://x");
|
|
158
|
-
// openExternal is sync but mcp.openLink resolves async
|
|
159
|
-
await new Promise((r) => setTimeout(r, 0));
|
|
160
|
-
expect(openLink).toHaveBeenCalledWith({ url: "https://x" });
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
describe("HostAdaptor Apps-SDK-only methods", () => {
|
|
164
|
-
beforeEach(() => {
|
|
165
|
-
McpAppBridge.resetInstance();
|
|
166
|
-
AppsSdkBridge.resetInstance();
|
|
167
|
-
vi.stubGlobal("skybridge", { hostType: "apps-sdk" });
|
|
168
|
-
vi.stubGlobal("parent", { postMessage: vi.fn() });
|
|
169
|
-
});
|
|
170
|
-
afterEach(() => {
|
|
171
|
-
vi.unstubAllGlobals();
|
|
172
|
-
McpAppBridge.resetInstance();
|
|
173
|
-
AppsSdkBridge.resetInstance();
|
|
174
|
-
});
|
|
175
|
-
it("uploadFile throws NotSupportedError when oai is null", async () => {
|
|
176
|
-
vi.stubGlobal("openai", undefined);
|
|
177
|
-
const adaptor = new HostAdaptor();
|
|
178
|
-
await expect(adaptor.uploadFile(new File([], "x"))).rejects.toThrow(NotSupportedError);
|
|
179
|
-
});
|
|
180
|
-
it("uploadFile delegates to oai.uploadFile and tracks fileId", async () => {
|
|
181
|
-
const setWidgetState = vi.fn().mockResolvedValue(undefined);
|
|
182
|
-
const uploadFile = vi
|
|
183
|
-
.fn()
|
|
184
|
-
.mockResolvedValue({ fileId: "abc", fileName: "x" });
|
|
185
|
-
vi.stubGlobal("openai", {
|
|
186
|
-
uploadFile,
|
|
187
|
-
setWidgetState,
|
|
188
|
-
widgetState: null,
|
|
189
|
-
});
|
|
190
|
-
const adaptor = new HostAdaptor();
|
|
191
|
-
const file = new File([], "x");
|
|
192
|
-
const r = await adaptor.uploadFile(file, { library: true });
|
|
193
|
-
expect(uploadFile).toHaveBeenCalledWith(file, { library: true });
|
|
194
|
-
expect(r.fileId).toBe("abc");
|
|
195
|
-
expect(setWidgetState).toHaveBeenCalledWith(expect.objectContaining({ imageIds: ["abc"] }));
|
|
196
|
-
});
|
|
197
|
-
it("selectFiles throws when oai is null", async () => {
|
|
198
|
-
vi.stubGlobal("openai", undefined);
|
|
199
|
-
const adaptor = new HostAdaptor();
|
|
200
|
-
await expect(adaptor.selectFiles()).rejects.toThrow(NotSupportedError);
|
|
201
|
-
});
|
|
202
|
-
it("selectFiles throws when host version lacks support", async () => {
|
|
203
|
-
vi.stubGlobal("openai", { selectFiles: undefined });
|
|
204
|
-
const adaptor = new HostAdaptor();
|
|
205
|
-
await expect(adaptor.selectFiles()).rejects.toThrow(/selectFiles/);
|
|
206
|
-
});
|
|
207
|
-
it("getFileDownloadUrl throws when oai is null", async () => {
|
|
208
|
-
vi.stubGlobal("openai", undefined);
|
|
209
|
-
const adaptor = new HostAdaptor();
|
|
210
|
-
await expect(adaptor.getFileDownloadUrl({ fileId: "x" })).rejects.toThrow(NotSupportedError);
|
|
211
|
-
});
|
|
212
|
-
it("setOpenInAppUrl throws when oai is null", async () => {
|
|
213
|
-
vi.stubGlobal("openai", undefined);
|
|
214
|
-
const adaptor = new HostAdaptor();
|
|
215
|
-
await expect(adaptor.setOpenInAppUrl("https://x")).rejects.toThrow(NotSupportedError);
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
describe("HostAdaptor modal", () => {
|
|
219
|
-
beforeEach(() => {
|
|
220
|
-
McpAppBridge.resetInstance();
|
|
221
|
-
AppsSdkBridge.resetInstance();
|
|
222
|
-
vi.stubGlobal("skybridge", { hostType: "apps-sdk" });
|
|
223
|
-
vi.stubGlobal("parent", { postMessage: vi.fn() });
|
|
224
|
-
});
|
|
225
|
-
afterEach(() => {
|
|
226
|
-
vi.unstubAllGlobals();
|
|
227
|
-
McpAppBridge.resetInstance();
|
|
228
|
-
AppsSdkBridge.resetInstance();
|
|
229
|
-
});
|
|
230
|
-
it("openModal delegates to oai.requestModal when present", () => {
|
|
231
|
-
const requestModal = vi.fn();
|
|
232
|
-
vi.stubGlobal("openai", { requestModal });
|
|
233
|
-
const adaptor = new HostAdaptor();
|
|
234
|
-
adaptor.openModal({ title: "Hi" });
|
|
235
|
-
expect(requestModal).toHaveBeenCalledWith({ title: "Hi" });
|
|
236
|
-
});
|
|
237
|
-
it("closeModal is a no-op when oai is present", () => {
|
|
238
|
-
vi.stubGlobal("openai", { requestModal: vi.fn() });
|
|
239
|
-
const adaptor = new HostAdaptor();
|
|
240
|
-
expect(() => adaptor.closeModal()).not.toThrow();
|
|
241
|
-
});
|
|
242
|
-
it("openModal sets polyfill display state when oai is null", () => {
|
|
243
|
-
vi.stubGlobal("openai", undefined);
|
|
244
|
-
const adaptor = new HostAdaptor();
|
|
245
|
-
const listener = vi.fn();
|
|
246
|
-
// biome-ignore lint/suspicious/noExplicitAny: test seam
|
|
247
|
-
adaptor.polyfillDisplayListeners.add(listener);
|
|
248
|
-
adaptor.openModal({ params: { x: 1 } });
|
|
249
|
-
// biome-ignore lint/suspicious/noExplicitAny: test seam
|
|
250
|
-
expect(adaptor._polyfillDisplay).toEqual({
|
|
251
|
-
mode: "modal",
|
|
252
|
-
params: { x: 1 },
|
|
253
|
-
});
|
|
254
|
-
expect(listener).toHaveBeenCalled();
|
|
255
|
-
});
|
|
256
|
-
it("closeModal resets polyfill display state when oai is null", () => {
|
|
257
|
-
vi.stubGlobal("openai", undefined);
|
|
258
|
-
const adaptor = new HostAdaptor();
|
|
259
|
-
adaptor.openModal({});
|
|
260
|
-
adaptor.closeModal();
|
|
261
|
-
// biome-ignore lint/suspicious/noExplicitAny: test seam
|
|
262
|
-
expect(adaptor._polyfillDisplay).toEqual({ mode: "inline" });
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
describe("HostAdaptor setViewState", () => {
|
|
266
|
-
beforeEach(() => {
|
|
267
|
-
McpAppBridge.resetInstance();
|
|
268
|
-
AppsSdkBridge.resetInstance();
|
|
269
|
-
vi.stubGlobal("skybridge", { hostType: "apps-sdk" });
|
|
270
|
-
vi.stubGlobal("parent", { postMessage: vi.fn() });
|
|
271
|
-
localStorage.clear();
|
|
272
|
-
});
|
|
273
|
-
afterEach(() => {
|
|
274
|
-
vi.unstubAllGlobals();
|
|
275
|
-
McpAppBridge.resetInstance();
|
|
276
|
-
AppsSdkBridge.resetInstance();
|
|
277
|
-
localStorage.clear();
|
|
278
|
-
});
|
|
279
|
-
it("uses window.openai.setWidgetState when oai is present", async () => {
|
|
280
|
-
const setWidgetState = vi.fn().mockResolvedValue(undefined);
|
|
281
|
-
vi.stubGlobal("openai", { setWidgetState, widgetState: null });
|
|
282
|
-
const adaptor = new HostAdaptor();
|
|
283
|
-
await adaptor.setViewState({ count: 1 });
|
|
284
|
-
expect(setWidgetState).toHaveBeenCalledWith(expect.objectContaining({ modelContent: { count: 1 } }));
|
|
285
|
-
});
|
|
286
|
-
it("calls mcp.updateModelContext and writes localStorage when oai is null", async () => {
|
|
287
|
-
vi.stubGlobal("openai", undefined);
|
|
288
|
-
const adaptor = new HostAdaptor();
|
|
289
|
-
const updateModelContext = vi.fn().mockResolvedValue(undefined);
|
|
290
|
-
// biome-ignore lint/suspicious/noExplicitAny: test seam
|
|
291
|
-
adaptor.mcp.getApp = vi
|
|
292
|
-
.fn()
|
|
293
|
-
.mockResolvedValue({ updateModelContext });
|
|
294
|
-
// biome-ignore lint/suspicious/noExplicitAny: test seam
|
|
295
|
-
adaptor._viewUUID = "view-1";
|
|
296
|
-
await adaptor.setViewState({ count: 2 });
|
|
297
|
-
expect(updateModelContext).toHaveBeenCalledWith({
|
|
298
|
-
structuredContent: { count: 2 },
|
|
299
|
-
content: [{ type: "text", text: JSON.stringify({ count: 2 }) }],
|
|
300
|
-
});
|
|
301
|
-
const keys = [];
|
|
302
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
303
|
-
const key = localStorage.key(i);
|
|
304
|
-
if (key !== null) {
|
|
305
|
-
keys.push(key);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
expect(keys.some((k) => k.endsWith(":view-1"))).toBe(true);
|
|
309
|
-
});
|
|
310
|
-
it("accepts an updater function form", async () => {
|
|
311
|
-
vi.stubGlobal("openai", undefined);
|
|
312
|
-
const adaptor = new HostAdaptor();
|
|
313
|
-
// biome-ignore lint/suspicious/noExplicitAny: test seam
|
|
314
|
-
adaptor.mcp.getApp = vi.fn().mockResolvedValue({
|
|
315
|
-
updateModelContext: vi.fn().mockResolvedValue(undefined),
|
|
316
|
-
});
|
|
317
|
-
// biome-ignore lint/suspicious/noExplicitAny: test seam
|
|
318
|
-
adaptor._viewState = { count: 1 };
|
|
319
|
-
await adaptor.setViewState((prev) => ({
|
|
320
|
-
// biome-ignore lint/suspicious/noExplicitAny: test
|
|
321
|
-
count: prev.count + 1,
|
|
322
|
-
}));
|
|
323
|
-
// biome-ignore lint/suspicious/noExplicitAny: test seam
|
|
324
|
-
expect(adaptor._viewState).toEqual({ count: 2 });
|
|
325
|
-
});
|
|
326
|
-
});
|
|
327
|
-
describe("HostAdaptor getHostContextStore", () => {
|
|
328
|
-
beforeEach(() => {
|
|
329
|
-
McpAppBridge.resetInstance();
|
|
330
|
-
AppsSdkBridge.resetInstance();
|
|
331
|
-
vi.stubGlobal("skybridge", { hostType: "apps-sdk" });
|
|
332
|
-
vi.stubGlobal("parent", { postMessage: vi.fn() });
|
|
333
|
-
});
|
|
334
|
-
afterEach(() => {
|
|
335
|
-
vi.unstubAllGlobals();
|
|
336
|
-
McpAppBridge.resetInstance();
|
|
337
|
-
AppsSdkBridge.resetInstance();
|
|
338
|
-
});
|
|
339
|
-
it("routes `display` to oai when overlay is present", () => {
|
|
340
|
-
vi.stubGlobal("openai", { view: { mode: "fullscreen" } });
|
|
341
|
-
const adaptor = new HostAdaptor();
|
|
342
|
-
const store = adaptor.getHostContextStore("display");
|
|
343
|
-
expect(store.getSnapshot()).toEqual({ mode: "fullscreen" });
|
|
344
|
-
});
|
|
345
|
-
it("routes `viewState` to oai.widgetState.modelContent when overlay present", () => {
|
|
346
|
-
vi.stubGlobal("openai", {
|
|
347
|
-
widgetState: { modelContent: { count: 5 }, privateContent: {} },
|
|
348
|
-
});
|
|
349
|
-
const adaptor = new HostAdaptor();
|
|
350
|
-
const store = adaptor.getHostContextStore("viewState");
|
|
351
|
-
expect(store.getSnapshot()).toEqual({ count: 5 });
|
|
352
|
-
});
|
|
353
|
-
it("routes `display` to polyfill when oai is null", () => {
|
|
354
|
-
vi.stubGlobal("openai", undefined);
|
|
355
|
-
const adaptor = new HostAdaptor();
|
|
356
|
-
const store = adaptor.getHostContextStore("display");
|
|
357
|
-
expect(store.getSnapshot()).toEqual({ mode: "inline" });
|
|
358
|
-
adaptor.openModal({});
|
|
359
|
-
expect(store.getSnapshot()).toEqual({ mode: "modal", params: undefined });
|
|
360
|
-
});
|
|
361
|
-
it("routes `viewState` to local LRU state when oai is null", () => {
|
|
362
|
-
vi.stubGlobal("openai", undefined);
|
|
363
|
-
const adaptor = new HostAdaptor();
|
|
364
|
-
const store = adaptor.getHostContextStore("viewState");
|
|
365
|
-
expect(store.getSnapshot()).toBe(null);
|
|
366
|
-
});
|
|
367
|
-
it("routes other keys (theme, locale, etc.) to mcp regardless of overlay", () => {
|
|
368
|
-
vi.stubGlobal("openai", { view: { mode: "inline" } });
|
|
369
|
-
const adaptor = new HostAdaptor();
|
|
370
|
-
const theme = adaptor.getHostContextStore("theme");
|
|
371
|
-
// default fallback when no notification yet
|
|
372
|
-
expect(theme.getSnapshot()).toBe("light");
|
|
373
|
-
});
|
|
374
|
-
});
|
|
375
|
-
//# sourceMappingURL=adaptor.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adaptor.test.js","sourceRoot":"","sources":["../../../src/web/bridges/adaptor.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { HostAdaptor } from \"./adaptor.js\";\nimport { AppsSdkBridge } from \"./apps-sdk/bridge.js\";\nimport { McpAppBridge } from \"./mcp-app/bridge.js\";\nimport { NotSupportedError } from \"./types.js\";\n\ndescribe(\"HostAdaptor constructor\", () => {\n beforeEach(() => {\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n });\n afterEach(() => {\n vi.unstubAllGlobals();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n });\n\n it(\"instantiates without window.openai (oai is null)\", () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n expect(adaptor.hasAppsSdkOverlay()).toBe(false);\n });\n\n it(\"captures window.openai when present (oai is set)\", () => {\n vi.stubGlobal(\"openai\", { widgetState: null });\n const adaptor = new HostAdaptor();\n expect(adaptor.hasAppsSdkOverlay()).toBe(true);\n });\n});\n\ndescribe(\"HostAdaptor MCP-routed methods\", () => {\n beforeEach(() => {\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n vi.stubGlobal(\"openai\", undefined);\n });\n afterEach(() => {\n vi.unstubAllGlobals();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n });\n\n it(\"callTool delegates to mcp.callServerTool\", async () => {\n const adaptor = new HostAdaptor();\n const fakeApp = {\n callServerTool: vi.fn().mockResolvedValue({\n content: [],\n structuredContent: { ok: true },\n isError: false,\n _meta: { x: 1 },\n }),\n };\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue(fakeApp);\n\n const result = await adaptor.callTool(\"greet\", { name: \"alice\" });\n expect(fakeApp.callServerTool).toHaveBeenCalledWith({\n name: \"greet\",\n arguments: { name: \"alice\" },\n });\n expect(result.structuredContent).toEqual({ ok: true });\n expect(result.meta).toEqual({ x: 1 });\n });\n\n it(\"requestDisplayMode delegates to mcp.requestDisplayMode\", async () => {\n const adaptor = new HostAdaptor();\n const fakeApp = {\n requestDisplayMode: vi.fn().mockResolvedValue({ mode: \"fullscreen\" }),\n };\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue(fakeApp);\n const r = await adaptor.requestDisplayMode(\"fullscreen\");\n expect(r.mode).toBe(\"fullscreen\");\n expect(fakeApp.requestDisplayMode).toHaveBeenCalledWith({\n mode: \"fullscreen\",\n });\n });\n\n it(\"requestClose delegates to mcp.requestTeardown\", async () => {\n const adaptor = new HostAdaptor();\n const fakeApp = { requestTeardown: vi.fn().mockResolvedValue(undefined) };\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue(fakeApp);\n await adaptor.requestClose();\n expect(fakeApp.requestTeardown).toHaveBeenCalled();\n });\n\n it(\"requestSize delegates to mcp.sendSizeChanged\", async () => {\n const adaptor = new HostAdaptor();\n const fakeApp = { sendSizeChanged: vi.fn().mockResolvedValue(undefined) };\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue(fakeApp);\n await adaptor.requestSize({ width: 100, height: 200 });\n expect(fakeApp.sendSizeChanged).toHaveBeenCalledWith({\n width: 100,\n height: 200,\n });\n });\n\n it(\"download returns isError when host lacks downloadFile capability\", async () => {\n const adaptor = new HostAdaptor();\n const fakeApp = {\n getHostCapabilities: () => ({}),\n downloadFile: vi.fn(),\n };\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue(fakeApp);\n const r = await adaptor.download({ contents: [] });\n expect(r.isError).toBe(true);\n expect(fakeApp.downloadFile).not.toHaveBeenCalled();\n });\n});\n\ndescribe(\"HostAdaptor conditional routing\", () => {\n beforeEach(() => {\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n });\n afterEach(() => {\n vi.unstubAllGlobals();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n });\n\n it(\"sendFollowUpMessage uses oai when scrollToBottom set\", async () => {\n const sendFollowUpMessage = vi.fn().mockResolvedValue(undefined);\n vi.stubGlobal(\"openai\", { sendFollowUpMessage });\n const adaptor = new HostAdaptor();\n await adaptor.sendFollowUpMessage(\"hi\", { scrollToBottom: false });\n expect(sendFollowUpMessage).toHaveBeenCalledWith({\n prompt: \"hi\",\n scrollToBottom: false,\n });\n });\n\n it(\"sendFollowUpMessage uses mcp when no options\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n const fakeApp = { sendMessage: vi.fn().mockResolvedValue(undefined) };\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue(fakeApp);\n await adaptor.sendFollowUpMessage(\"hi\");\n expect(fakeApp.sendMessage).toHaveBeenCalledWith({\n role: \"user\",\n content: [{ type: \"text\", text: \"hi\" }],\n });\n });\n\n it(\"openExternal uses oai when redirectUrl: false\", async () => {\n const openExternal = vi.fn();\n vi.stubGlobal(\"openai\", { openExternal });\n const adaptor = new HostAdaptor();\n adaptor.openExternal(\"https://x\", { redirectUrl: false });\n expect(openExternal).toHaveBeenCalledWith({\n href: \"https://x\",\n redirectUrl: false,\n });\n });\n\n it(\"openExternal uses mcp.openLink for default case\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n const openLink = vi.fn();\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue({ openLink });\n adaptor.openExternal(\"https://x\");\n // openExternal is sync but mcp.openLink resolves async\n await new Promise((r) => setTimeout(r, 0));\n expect(openLink).toHaveBeenCalledWith({ url: \"https://x\" });\n });\n});\n\ndescribe(\"HostAdaptor Apps-SDK-only methods\", () => {\n beforeEach(() => {\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n });\n afterEach(() => {\n vi.unstubAllGlobals();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n });\n\n it(\"uploadFile throws NotSupportedError when oai is null\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n await expect(adaptor.uploadFile(new File([], \"x\"))).rejects.toThrow(\n NotSupportedError,\n );\n });\n\n it(\"uploadFile delegates to oai.uploadFile and tracks fileId\", async () => {\n const setWidgetState = vi.fn().mockResolvedValue(undefined);\n const uploadFile = vi\n .fn()\n .mockResolvedValue({ fileId: \"abc\", fileName: \"x\" });\n vi.stubGlobal(\"openai\", {\n uploadFile,\n setWidgetState,\n widgetState: null,\n });\n const adaptor = new HostAdaptor();\n const file = new File([], \"x\");\n const r = await adaptor.uploadFile(file, { library: true });\n expect(uploadFile).toHaveBeenCalledWith(file, { library: true });\n expect(r.fileId).toBe(\"abc\");\n expect(setWidgetState).toHaveBeenCalledWith(\n expect.objectContaining({ imageIds: [\"abc\"] }),\n );\n });\n\n it(\"selectFiles throws when oai is null\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n await expect(adaptor.selectFiles()).rejects.toThrow(NotSupportedError);\n });\n\n it(\"selectFiles throws when host version lacks support\", async () => {\n vi.stubGlobal(\"openai\", { selectFiles: undefined });\n const adaptor = new HostAdaptor();\n await expect(adaptor.selectFiles()).rejects.toThrow(/selectFiles/);\n });\n\n it(\"getFileDownloadUrl throws when oai is null\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n await expect(adaptor.getFileDownloadUrl({ fileId: \"x\" })).rejects.toThrow(\n NotSupportedError,\n );\n });\n\n it(\"setOpenInAppUrl throws when oai is null\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n await expect(adaptor.setOpenInAppUrl(\"https://x\")).rejects.toThrow(\n NotSupportedError,\n );\n });\n});\n\ndescribe(\"HostAdaptor modal\", () => {\n beforeEach(() => {\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n });\n afterEach(() => {\n vi.unstubAllGlobals();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n });\n\n it(\"openModal delegates to oai.requestModal when present\", () => {\n const requestModal = vi.fn();\n vi.stubGlobal(\"openai\", { requestModal });\n const adaptor = new HostAdaptor();\n adaptor.openModal({ title: \"Hi\" });\n expect(requestModal).toHaveBeenCalledWith({ title: \"Hi\" });\n });\n\n it(\"closeModal is a no-op when oai is present\", () => {\n vi.stubGlobal(\"openai\", { requestModal: vi.fn() });\n const adaptor = new HostAdaptor();\n expect(() => adaptor.closeModal()).not.toThrow();\n });\n\n it(\"openModal sets polyfill display state when oai is null\", () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n const listener = vi.fn();\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).polyfillDisplayListeners.add(listener);\n adaptor.openModal({ params: { x: 1 } });\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n expect((adaptor as any)._polyfillDisplay).toEqual({\n mode: \"modal\",\n params: { x: 1 },\n });\n expect(listener).toHaveBeenCalled();\n });\n\n it(\"closeModal resets polyfill display state when oai is null\", () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n adaptor.openModal({});\n adaptor.closeModal();\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n expect((adaptor as any)._polyfillDisplay).toEqual({ mode: \"inline\" });\n });\n});\n\ndescribe(\"HostAdaptor setViewState\", () => {\n beforeEach(() => {\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n localStorage.clear();\n });\n afterEach(() => {\n vi.unstubAllGlobals();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n localStorage.clear();\n });\n\n it(\"uses window.openai.setWidgetState when oai is present\", async () => {\n const setWidgetState = vi.fn().mockResolvedValue(undefined);\n vi.stubGlobal(\"openai\", { setWidgetState, widgetState: null });\n const adaptor = new HostAdaptor();\n await adaptor.setViewState({ count: 1 });\n expect(setWidgetState).toHaveBeenCalledWith(\n expect.objectContaining({ modelContent: { count: 1 } }),\n );\n });\n\n it(\"calls mcp.updateModelContext and writes localStorage when oai is null\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n const updateModelContext = vi.fn().mockResolvedValue(undefined);\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi\n .fn()\n .mockResolvedValue({ updateModelContext });\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any)._viewUUID = \"view-1\";\n await adaptor.setViewState({ count: 2 });\n expect(updateModelContext).toHaveBeenCalledWith({\n structuredContent: { count: 2 },\n content: [{ type: \"text\", text: JSON.stringify({ count: 2 }) }],\n });\n const keys: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key !== null) {\n keys.push(key);\n }\n }\n expect(keys.some((k) => k.endsWith(\":view-1\"))).toBe(true);\n });\n\n it(\"accepts an updater function form\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue({\n updateModelContext: vi.fn().mockResolvedValue(undefined),\n });\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any)._viewState = { count: 1 };\n await adaptor.setViewState((prev) => ({\n // biome-ignore lint/suspicious/noExplicitAny: test\n count: (prev as any).count + 1,\n }));\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n expect((adaptor as any)._viewState).toEqual({ count: 2 });\n });\n});\n\ndescribe(\"HostAdaptor getHostContextStore\", () => {\n beforeEach(() => {\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n });\n afterEach(() => {\n vi.unstubAllGlobals();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n });\n\n it(\"routes `display` to oai when overlay is present\", () => {\n vi.stubGlobal(\"openai\", { view: { mode: \"fullscreen\" } });\n const adaptor = new HostAdaptor();\n const store = adaptor.getHostContextStore(\"display\");\n expect(store.getSnapshot()).toEqual({ mode: \"fullscreen\" });\n });\n\n it(\"routes `viewState` to oai.widgetState.modelContent when overlay present\", () => {\n vi.stubGlobal(\"openai\", {\n widgetState: { modelContent: { count: 5 }, privateContent: {} },\n });\n const adaptor = new HostAdaptor();\n const store = adaptor.getHostContextStore(\"viewState\");\n expect(store.getSnapshot()).toEqual({ count: 5 });\n });\n\n it(\"routes `display` to polyfill when oai is null\", () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n const store = adaptor.getHostContextStore(\"display\");\n expect(store.getSnapshot()).toEqual({ mode: \"inline\" });\n adaptor.openModal({});\n expect(store.getSnapshot()).toEqual({ mode: \"modal\", params: undefined });\n });\n\n it(\"routes `viewState` to local LRU state when oai is null\", () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n const store = adaptor.getHostContextStore(\"viewState\");\n expect(store.getSnapshot()).toBe(null);\n });\n\n it(\"routes other keys (theme, locale, etc.) to mcp regardless of overlay\", () => {\n vi.stubGlobal(\"openai\", { view: { mode: \"inline\" } });\n const adaptor = new HostAdaptor();\n const theme = adaptor.getHostContextStore(\"theme\");\n // default fallback when no notification yet\n expect(theme.getSnapshot()).toBe(\"light\");\n });\n});\n"]}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { HostAdaptor } from "./adaptor.js";
|
|
3
|
-
import { AppsSdkBridge } from "./apps-sdk/bridge.js";
|
|
4
|
-
import { _resetAdaptor, getAdaptor } from "./get-adaptor.js";
|
|
5
|
-
import { McpAppBridge } from "./mcp-app/bridge.js";
|
|
6
|
-
describe("getAdaptor", () => {
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
_resetAdaptor();
|
|
9
|
-
McpAppBridge.resetInstance();
|
|
10
|
-
AppsSdkBridge.resetInstance();
|
|
11
|
-
vi.stubGlobal("parent", { postMessage: vi.fn() });
|
|
12
|
-
});
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
vi.unstubAllGlobals();
|
|
15
|
-
_resetAdaptor();
|
|
16
|
-
McpAppBridge.resetInstance();
|
|
17
|
-
AppsSdkBridge.resetInstance();
|
|
18
|
-
});
|
|
19
|
-
it("returns a HostAdaptor instance", () => {
|
|
20
|
-
vi.stubGlobal("skybridge", { hostType: "mcp-app" });
|
|
21
|
-
vi.stubGlobal("openai", undefined);
|
|
22
|
-
expect(getAdaptor()).toBeInstanceOf(HostAdaptor);
|
|
23
|
-
});
|
|
24
|
-
it("memoizes the instance", () => {
|
|
25
|
-
vi.stubGlobal("skybridge", { hostType: "mcp-app" });
|
|
26
|
-
vi.stubGlobal("openai", undefined);
|
|
27
|
-
const a = getAdaptor();
|
|
28
|
-
const b = getAdaptor();
|
|
29
|
-
expect(a).toBe(b);
|
|
30
|
-
});
|
|
31
|
-
it("logs a warning when hostType disagrees with probe", () => {
|
|
32
|
-
const warn = vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
33
|
-
vi.stubGlobal("skybridge", { hostType: "mcp-app" });
|
|
34
|
-
vi.stubGlobal("openai", { view: { mode: "inline" } });
|
|
35
|
-
getAdaptor();
|
|
36
|
-
expect(warn).toHaveBeenCalledWith(expect.stringContaining("hostType"));
|
|
37
|
-
warn.mockRestore();
|
|
38
|
-
});
|
|
39
|
-
it("does not warn when hostType matches probe", () => {
|
|
40
|
-
const warn = vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
41
|
-
vi.stubGlobal("skybridge", { hostType: "apps-sdk" });
|
|
42
|
-
vi.stubGlobal("openai", { view: { mode: "inline" } });
|
|
43
|
-
getAdaptor();
|
|
44
|
-
expect(warn).not.toHaveBeenCalled();
|
|
45
|
-
warn.mockRestore();
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
//# sourceMappingURL=get-adaptor.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"get-adaptor.test.js","sourceRoot":"","sources":["../../../src/web/bridges/get-adaptor.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { HostAdaptor } from \"./adaptor.js\";\nimport { AppsSdkBridge } from \"./apps-sdk/bridge.js\";\nimport { _resetAdaptor, getAdaptor } from \"./get-adaptor.js\";\nimport { McpAppBridge } from \"./mcp-app/bridge.js\";\n\ndescribe(\"getAdaptor\", () => {\n beforeEach(() => {\n _resetAdaptor();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n });\n afterEach(() => {\n vi.unstubAllGlobals();\n _resetAdaptor();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n });\n\n it(\"returns a HostAdaptor instance\", () => {\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"openai\", undefined);\n expect(getAdaptor()).toBeInstanceOf(HostAdaptor);\n });\n\n it(\"memoizes the instance\", () => {\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"openai\", undefined);\n const a = getAdaptor();\n const b = getAdaptor();\n expect(a).toBe(b);\n });\n\n it(\"logs a warning when hostType disagrees with probe\", () => {\n const warn = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"openai\", { view: { mode: \"inline\" } });\n getAdaptor();\n expect(warn).toHaveBeenCalledWith(expect.stringContaining(\"hostType\"));\n warn.mockRestore();\n });\n\n it(\"does not warn when hostType matches probe\", () => {\n const warn = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n vi.stubGlobal(\"openai\", { view: { mode: \"inline\" } });\n getAdaptor();\n expect(warn).not.toHaveBeenCalled();\n warn.mockRestore();\n });\n});\n"]}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { McpAppBridge } from "./mcp-app/bridge.js";
|
|
2
|
-
import type { HostContextStore } from "./types.js";
|
|
3
|
-
/**
|
|
4
|
-
* Build the host-context store map sourced from the MCP App bridge.
|
|
5
|
-
* `display` and `viewState` are *not* included here; those are handled
|
|
6
|
-
* by the adaptor itself because they have transport-specific overlays
|
|
7
|
-
* and additional state (polyfill modal state, localStorage hydration).
|
|
8
|
-
*/
|
|
9
|
-
export declare function buildMcpContextStores(bridge: McpAppBridge): {
|
|
10
|
-
theme: HostContextStore<"theme">;
|
|
11
|
-
locale: HostContextStore<"locale">;
|
|
12
|
-
safeArea: HostContextStore<"safeArea">;
|
|
13
|
-
displayMode: HostContextStore<"displayMode">;
|
|
14
|
-
maxHeight: HostContextStore<"maxHeight">;
|
|
15
|
-
userAgent: HostContextStore<"userAgent">;
|
|
16
|
-
toolInput: HostContextStore<"toolInput">;
|
|
17
|
-
toolOutput: HostContextStore<"toolOutput">;
|
|
18
|
-
toolResponseMetadata: HostContextStore<"toolResponseMetadata">;
|
|
19
|
-
};
|
|
20
|
-
/**
|
|
21
|
-
* Build the Apps-SDK-sourced overlay stores (`display`, `viewState`).
|
|
22
|
-
* Used by `HostAdaptor` only when `window.openai` is present.
|
|
23
|
-
*/
|
|
24
|
-
export declare function buildAppsSdkOverlayStores(bridge: import("./apps-sdk/bridge.js").AppsSdkBridge): {
|
|
25
|
-
display: HostContextStore<"display">;
|
|
26
|
-
viewState: HostContextStore<"viewState">;
|
|
27
|
-
};
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { dequal } from "dequal/lite";
|
|
2
|
-
/**
|
|
3
|
-
* Build a memoized {@link HostContextStore} backed by one or more
|
|
4
|
-
* `McpAppBridge` context keys. Recomputed values are deep-compared via
|
|
5
|
-
* `dequal` so unchanged snapshots return the cached reference.
|
|
6
|
-
*/
|
|
7
|
-
function createMcpStore(bridge, keys, computeSnapshot) {
|
|
8
|
-
let cachedValue;
|
|
9
|
-
return {
|
|
10
|
-
subscribe: bridge.subscribe(keys),
|
|
11
|
-
getSnapshot: () => {
|
|
12
|
-
const context = Object.fromEntries(keys.map((k) => [k, bridge.getSnapshot(k)]));
|
|
13
|
-
const newValue = computeSnapshot(context);
|
|
14
|
-
if (cachedValue !== undefined && dequal(cachedValue, newValue)) {
|
|
15
|
-
return cachedValue;
|
|
16
|
-
}
|
|
17
|
-
cachedValue = newValue;
|
|
18
|
-
return newValue;
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Build the host-context store map sourced from the MCP App bridge.
|
|
24
|
-
* `display` and `viewState` are *not* included here; those are handled
|
|
25
|
-
* by the adaptor itself because they have transport-specific overlays
|
|
26
|
-
* and additional state (polyfill modal state, localStorage hydration).
|
|
27
|
-
*/
|
|
28
|
-
export function buildMcpContextStores(bridge) {
|
|
29
|
-
return {
|
|
30
|
-
theme: createMcpStore(bridge, ["theme"], ({ theme }) => theme ?? "light"),
|
|
31
|
-
locale: createMcpStore(bridge, ["locale"], ({ locale }) => locale ?? "en-US"),
|
|
32
|
-
safeArea: createMcpStore(bridge, ["safeAreaInsets"], ({ safeAreaInsets }) => ({
|
|
33
|
-
insets: safeAreaInsets ?? { top: 0, right: 0, bottom: 0, left: 0 },
|
|
34
|
-
})),
|
|
35
|
-
displayMode: createMcpStore(bridge, ["displayMode"], ({ displayMode }) => displayMode ?? "inline"),
|
|
36
|
-
maxHeight: createMcpStore(bridge, ["containerDimensions"], ({ containerDimensions }) => {
|
|
37
|
-
if (containerDimensions && "maxHeight" in containerDimensions) {
|
|
38
|
-
return containerDimensions.maxHeight;
|
|
39
|
-
}
|
|
40
|
-
return undefined;
|
|
41
|
-
}),
|
|
42
|
-
userAgent: createMcpStore(bridge, ["platform", "deviceCapabilities"], ({ platform, deviceCapabilities }) => ({
|
|
43
|
-
device: {
|
|
44
|
-
type: platform === "web" ? "desktop" : (platform ?? "unknown"),
|
|
45
|
-
},
|
|
46
|
-
capabilities: {
|
|
47
|
-
hover: true,
|
|
48
|
-
touch: true,
|
|
49
|
-
...deviceCapabilities,
|
|
50
|
-
},
|
|
51
|
-
})),
|
|
52
|
-
toolInput: createMcpStore(bridge, ["toolInput"], ({ toolInput }) => toolInput ?? null),
|
|
53
|
-
toolOutput: createMcpStore(bridge, ["toolResult"], ({ toolResult }) => toolResult?.structuredContent ?? null),
|
|
54
|
-
toolResponseMetadata: createMcpStore(bridge, ["toolResult"], ({ toolResult }) => toolResult?._meta ?? null),
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Build the Apps-SDK-sourced overlay stores (`display`, `viewState`).
|
|
59
|
-
* Used by `HostAdaptor` only when `window.openai` is present.
|
|
60
|
-
*/
|
|
61
|
-
export function buildAppsSdkOverlayStores(bridge) {
|
|
62
|
-
return {
|
|
63
|
-
display: {
|
|
64
|
-
subscribe: bridge.subscribe("view"),
|
|
65
|
-
getSnapshot: () => bridge.getSnapshot("view"),
|
|
66
|
-
},
|
|
67
|
-
viewState: {
|
|
68
|
-
subscribe: bridge.subscribe("widgetState"),
|
|
69
|
-
getSnapshot: () => bridge.getSnapshot("widgetState")?.modelContent ?? null,
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
//# sourceMappingURL=host-context-stores.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"host-context-stores.js","sourceRoot":"","sources":["../../../src/web/bridges/host-context-stores.ts"],"names":[],"mappings":"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","sourcesContent":["import { dequal } from \"dequal/lite\";\nimport type { McpAppBridge } from \"./mcp-app/bridge.js\";\nimport type { McpAppContext, McpAppContextKey } from \"./mcp-app/types.js\";\nimport type { HostContextStore } from \"./types.js\";\n\ntype PickContext<K extends readonly McpAppContextKey[]> = {\n [P in K[number]]: McpAppContext[P];\n};\n\n/**\n * Build a memoized {@link HostContextStore} backed by one or more\n * `McpAppBridge` context keys. Recomputed values are deep-compared via\n * `dequal` so unchanged snapshots return the cached reference.\n */\nfunction createMcpStore<const Keys extends readonly McpAppContextKey[], R>(\n bridge: McpAppBridge,\n keys: Keys,\n computeSnapshot: (context: PickContext<Keys>) => R,\n) {\n let cachedValue: R | undefined;\n return {\n subscribe: bridge.subscribe(keys),\n getSnapshot: () => {\n const context = Object.fromEntries(\n keys.map((k) => [k, bridge.getSnapshot(k)]),\n ) as PickContext<Keys>;\n const newValue = computeSnapshot(context);\n if (cachedValue !== undefined && dequal(cachedValue, newValue)) {\n return cachedValue;\n }\n cachedValue = newValue;\n return newValue;\n },\n };\n}\n\n/**\n * Build the host-context store map sourced from the MCP App bridge.\n * `display` and `viewState` are *not* included here; those are handled\n * by the adaptor itself because they have transport-specific overlays\n * and additional state (polyfill modal state, localStorage hydration).\n */\nexport function buildMcpContextStores(bridge: McpAppBridge): {\n theme: HostContextStore<\"theme\">;\n locale: HostContextStore<\"locale\">;\n safeArea: HostContextStore<\"safeArea\">;\n displayMode: HostContextStore<\"displayMode\">;\n maxHeight: HostContextStore<\"maxHeight\">;\n userAgent: HostContextStore<\"userAgent\">;\n toolInput: HostContextStore<\"toolInput\">;\n toolOutput: HostContextStore<\"toolOutput\">;\n toolResponseMetadata: HostContextStore<\"toolResponseMetadata\">;\n} {\n return {\n theme: createMcpStore(bridge, [\"theme\"], ({ theme }) => theme ?? \"light\"),\n locale: createMcpStore(\n bridge,\n [\"locale\"],\n ({ locale }) => locale ?? \"en-US\",\n ),\n safeArea: createMcpStore(\n bridge,\n [\"safeAreaInsets\"],\n ({ safeAreaInsets }) => ({\n insets: safeAreaInsets ?? { top: 0, right: 0, bottom: 0, left: 0 },\n }),\n ),\n displayMode: createMcpStore(\n bridge,\n [\"displayMode\"],\n ({ displayMode }) => displayMode ?? \"inline\",\n ),\n maxHeight: createMcpStore(\n bridge,\n [\"containerDimensions\"],\n ({ containerDimensions }) => {\n if (containerDimensions && \"maxHeight\" in containerDimensions) {\n return containerDimensions.maxHeight;\n }\n return undefined;\n },\n ),\n userAgent: createMcpStore(\n bridge,\n [\"platform\", \"deviceCapabilities\"],\n ({ platform, deviceCapabilities }) => ({\n device: {\n type: platform === \"web\" ? \"desktop\" : (platform ?? \"unknown\"),\n },\n capabilities: {\n hover: true,\n touch: true,\n ...deviceCapabilities,\n },\n }),\n ),\n toolInput: createMcpStore(\n bridge,\n [\"toolInput\"],\n ({ toolInput }) => toolInput ?? null,\n ),\n toolOutput: createMcpStore(\n bridge,\n [\"toolResult\"],\n ({ toolResult }) => toolResult?.structuredContent ?? null,\n ),\n toolResponseMetadata: createMcpStore(\n bridge,\n [\"toolResult\"],\n ({ toolResult }) => toolResult?._meta ?? null,\n ),\n };\n}\n\n/**\n * Build the Apps-SDK-sourced overlay stores (`display`, `viewState`).\n * Used by `HostAdaptor` only when `window.openai` is present.\n */\nexport function buildAppsSdkOverlayStores(\n bridge: import(\"./apps-sdk/bridge.js\").AppsSdkBridge,\n): {\n display: HostContextStore<\"display\">;\n viewState: HostContextStore<\"viewState\">;\n} {\n return {\n display: {\n subscribe: bridge.subscribe(\"view\"),\n getSnapshot: () => bridge.getSnapshot(\"view\"),\n },\n viewState: {\n subscribe: bridge.subscribe(\"widgetState\"),\n getSnapshot: () =>\n bridge.getSnapshot(\"widgetState\")?.modelContent ?? null,\n },\n };\n}\n"]}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { McpAppBridge } from "./bridge.js";
|
|
3
|
-
describe("McpAppBridge.getInstance", () => {
|
|
4
|
-
beforeEach(() => {
|
|
5
|
-
McpAppBridge.resetInstance();
|
|
6
|
-
vi.stubGlobal("parent", { postMessage: vi.fn() });
|
|
7
|
-
});
|
|
8
|
-
afterEach(() => {
|
|
9
|
-
vi.unstubAllGlobals();
|
|
10
|
-
McpAppBridge.resetInstance();
|
|
11
|
-
});
|
|
12
|
-
it("instantiates when hostType is apps-sdk", () => {
|
|
13
|
-
vi.stubGlobal("skybridge", { hostType: "apps-sdk" });
|
|
14
|
-
expect(() => McpAppBridge.getInstance()).not.toThrow();
|
|
15
|
-
});
|
|
16
|
-
it("instantiates when hostType is mcp-app", () => {
|
|
17
|
-
vi.stubGlobal("skybridge", { hostType: "mcp-app" });
|
|
18
|
-
expect(() => McpAppBridge.getInstance()).not.toThrow();
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
//# sourceMappingURL=bridge.test.js.map
|