skybridge 0.0.0-dev.f391982 → 0.0.0-dev.f3dd6f0

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.
Files changed (122) hide show
  1. package/README.md +8 -8
  2. package/dist/cli/tunnel-control-server.d.ts +9 -0
  3. package/dist/cli/tunnel-control-server.js +31 -0
  4. package/dist/cli/tunnel-control-server.js.map +1 -0
  5. package/dist/cli/tunnel-control-server.test.js +39 -0
  6. package/dist/cli/tunnel-control-server.test.js.map +1 -0
  7. package/dist/cli/tunnel-handler.d.ts +3 -0
  8. package/dist/cli/tunnel-handler.js +48 -0
  9. package/dist/cli/tunnel-handler.js.map +1 -0
  10. package/dist/cli/tunnel-handler.test.d.ts +1 -0
  11. package/dist/cli/tunnel-handler.test.js +105 -0
  12. package/dist/cli/tunnel-handler.test.js.map +1 -0
  13. package/dist/cli/tunnel.d.ts +57 -0
  14. package/dist/cli/tunnel.js +154 -0
  15. package/dist/cli/tunnel.js.map +1 -0
  16. package/dist/cli/tunnel.test.d.ts +1 -0
  17. package/dist/cli/tunnel.test.js +190 -0
  18. package/dist/cli/tunnel.test.js.map +1 -0
  19. package/dist/cli/use-open-browser.d.ts +1 -0
  20. package/dist/cli/use-open-browser.js +44 -0
  21. package/dist/cli/use-open-browser.js.map +1 -0
  22. package/dist/cli/use-tunnel.d.ts +1 -1
  23. package/dist/cli/use-tunnel.js +102 -68
  24. package/dist/cli/use-tunnel.js.map +1 -1
  25. package/dist/commands/build.js +36 -1
  26. package/dist/commands/build.js.map +1 -1
  27. package/dist/commands/dev.d.ts +1 -0
  28. package/dist/commands/dev.js +33 -2
  29. package/dist/commands/dev.js.map +1 -1
  30. package/dist/server/asset-base-url-transform-plugin.js +1 -1
  31. package/dist/server/asset-base-url-transform-plugin.js.map +1 -1
  32. package/dist/server/asset-base-url-transform-plugin.test.js +29 -0
  33. package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -1
  34. package/dist/server/express.d.ts +1 -5
  35. package/dist/server/express.js +31 -7
  36. package/dist/server/express.js.map +1 -1
  37. package/dist/server/express.test.js +210 -69
  38. package/dist/server/express.test.js.map +1 -1
  39. package/dist/server/inferUtilityTypes.d.ts +6 -6
  40. package/dist/server/server.d.ts +27 -4
  41. package/dist/server/server.js +64 -20
  42. package/dist/server/server.js.map +1 -1
  43. package/dist/server/templateHelper.d.ts +0 -2
  44. package/dist/server/templateHelper.js +3 -22
  45. package/dist/server/templateHelper.js.map +1 -1
  46. package/dist/server/templates.generated.d.ts +4 -0
  47. package/dist/server/templates.generated.js +47 -0
  48. package/dist/server/templates.generated.js.map +1 -0
  49. package/dist/server/tunnel-proxy-router.d.ts +7 -0
  50. package/dist/server/tunnel-proxy-router.js +110 -0
  51. package/dist/server/tunnel-proxy-router.js.map +1 -0
  52. package/dist/server/tunnel-proxy-router.test.d.ts +1 -0
  53. package/dist/server/tunnel-proxy-router.test.js +229 -0
  54. package/dist/server/tunnel-proxy-router.test.js.map +1 -0
  55. package/dist/test/view.test.js +66 -66
  56. package/dist/test/view.test.js.map +1 -1
  57. package/dist/version.js +1 -3
  58. package/dist/version.js.map +1 -1
  59. package/dist/web/bridges/apps-sdk/adaptor.d.ts +2 -2
  60. package/dist/web/bridges/apps-sdk/adaptor.js +8 -2
  61. package/dist/web/bridges/apps-sdk/adaptor.js.map +1 -1
  62. package/dist/web/bridges/mcp-app/adaptor.d.ts +6 -6
  63. package/dist/web/bridges/mcp-app/adaptor.js +24 -29
  64. package/dist/web/bridges/mcp-app/adaptor.js.map +1 -1
  65. package/dist/web/bridges/types.d.ts +5 -6
  66. package/dist/web/components/modal-provider.js +1 -1
  67. package/dist/web/components/modal-provider.js.map +1 -1
  68. package/dist/web/create-store.js +9 -9
  69. package/dist/web/create-store.js.map +1 -1
  70. package/dist/web/create-store.test.js +14 -16
  71. package/dist/web/create-store.test.js.map +1 -1
  72. package/dist/web/data-llm.d.ts +1 -1
  73. package/dist/web/data-llm.js +3 -3
  74. package/dist/web/data-llm.js.map +1 -1
  75. package/dist/web/data-llm.test.js +22 -22
  76. package/dist/web/data-llm.test.js.map +1 -1
  77. package/dist/web/generate-helpers.test-d.js +7 -7
  78. package/dist/web/generate-helpers.test-d.js.map +1 -1
  79. package/dist/web/helpers/state.d.ts +2 -2
  80. package/dist/web/helpers/state.js +11 -11
  81. package/dist/web/helpers/state.js.map +1 -1
  82. package/dist/web/helpers/state.test.js +9 -9
  83. package/dist/web/helpers/state.test.js.map +1 -1
  84. package/dist/web/hooks/index.d.ts +1 -1
  85. package/dist/web/hooks/index.js +1 -1
  86. package/dist/web/hooks/index.js.map +1 -1
  87. package/dist/web/hooks/use-call-tool.test.js +0 -4
  88. package/dist/web/hooks/use-call-tool.test.js.map +1 -1
  89. package/dist/web/hooks/use-request-modal.d.ts +1 -1
  90. package/dist/web/hooks/use-request-modal.js +4 -4
  91. package/dist/web/hooks/use-request-modal.js.map +1 -1
  92. package/dist/web/hooks/use-request-modal.test.js +1 -1
  93. package/dist/web/hooks/use-request-modal.test.js.map +1 -1
  94. package/dist/web/hooks/use-view-state.d.ts +4 -0
  95. package/dist/web/hooks/use-view-state.js +32 -0
  96. package/dist/web/hooks/use-view-state.js.map +1 -0
  97. package/dist/web/hooks/use-view-state.test.d.ts +1 -0
  98. package/dist/web/hooks/{use-widget-state.test.js → use-view-state.test.js} +17 -17
  99. package/dist/web/hooks/use-view-state.test.js.map +1 -0
  100. package/dist/web/index.d.ts +1 -3
  101. package/dist/web/index.js +1 -2
  102. package/dist/web/index.js.map +1 -1
  103. package/dist/web/mount-view.d.ts +1 -0
  104. package/dist/web/{mount-widget.js → mount-view.js} +2 -2
  105. package/dist/web/mount-view.js.map +1 -0
  106. package/dist/web/plugin/plugin.js +32 -17
  107. package/dist/web/plugin/plugin.js.map +1 -1
  108. package/dist/web/plugin/scan-views.d.ts +8 -0
  109. package/dist/web/plugin/scan-views.js +26 -8
  110. package/dist/web/plugin/scan-views.js.map +1 -1
  111. package/dist/web/plugin/scan-views.test.js +33 -1
  112. package/dist/web/plugin/scan-views.test.js.map +1 -1
  113. package/package.json +12 -6
  114. package/dist/server/templates/development.hbs +0 -12
  115. package/dist/server/templates/production.hbs +0 -6
  116. package/dist/web/hooks/use-widget-state.d.ts +0 -4
  117. package/dist/web/hooks/use-widget-state.js +0 -32
  118. package/dist/web/hooks/use-widget-state.js.map +0 -1
  119. package/dist/web/hooks/use-widget-state.test.js.map +0 -1
  120. package/dist/web/mount-widget.d.ts +0 -1
  121. package/dist/web/mount-widget.js.map +0 -1
  122. /package/dist/{web/hooks/use-widget-state.test.d.ts → cli/tunnel-control-server.test.d.ts} +0 -0
@@ -0,0 +1,229 @@
1
+ import { EventEmitter } from "node:events";
2
+ import http from "node:http";
3
+ import { Readable } from "node:stream";
4
+ import express from "express";
5
+ import { afterEach, describe, expect, it, vi } from "vitest";
6
+ import { startTunnelControlServer } from "../cli/tunnel-control-server.js";
7
+ import { createTunnelProxyRouter } from "./tunnel-proxy-router.js";
8
+ function makeFakeChild() {
9
+ const child = new EventEmitter();
10
+ child.stdout = new Readable({ read() { } });
11
+ child.stderr = new Readable({ read() { } });
12
+ child.kill = vi.fn(() => true);
13
+ return child;
14
+ }
15
+ async function listen(handler) {
16
+ const server = http.createServer(handler);
17
+ await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
18
+ const port = server.address().port;
19
+ return { port, server };
20
+ }
21
+ const cleanups = [];
22
+ afterEach(async () => {
23
+ while (cleanups.length > 0) {
24
+ const cleanup = cleanups.pop();
25
+ if (cleanup) {
26
+ await cleanup();
27
+ }
28
+ }
29
+ });
30
+ async function startProxy(controlPort) {
31
+ const app = express();
32
+ app.use(createTunnelProxyRouter(controlPort));
33
+ const { port, server } = await listen(app);
34
+ cleanups.push(() => new Promise((resolve) => {
35
+ server.closeAllConnections?.();
36
+ server.close(() => resolve());
37
+ }));
38
+ return { port, server };
39
+ }
40
+ async function startControl() {
41
+ const child = makeFakeChild();
42
+ const control = await startTunnelControlServer(() => 3000, {
43
+ spawn: () => child,
44
+ });
45
+ cleanups.push(() => control.close());
46
+ return { control, child };
47
+ }
48
+ describe("createTunnelProxyRouter", () => {
49
+ describe("POST /__skybridge/tunnel", () => {
50
+ it("forwards to upstream and returns the upstream JSON", async () => {
51
+ const { control } = await startControl();
52
+ const { port } = await startProxy(control.port);
53
+ const res = await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel`, {
54
+ method: "POST",
55
+ });
56
+ expect(res.status).toBe(200);
57
+ expect(res.headers.get("content-type")).toMatch(/application\/json/);
58
+ expect(await res.json()).toEqual({
59
+ status: "starting",
60
+ message: "Starting tunnel…",
61
+ });
62
+ expect(control.manager.getState().status).toBe("starting");
63
+ });
64
+ it("returns 502 when upstream is unavailable", async () => {
65
+ // Pick a port nothing is listening on by starting+stopping a server.
66
+ const probe = http.createServer();
67
+ await new Promise((resolve) => probe.listen(0, "127.0.0.1", resolve));
68
+ const deadPort = probe.address().port;
69
+ await new Promise((resolve) => probe.close(() => resolve()));
70
+ const { port } = await startProxy(deadPort);
71
+ const res = await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel`, {
72
+ method: "POST",
73
+ });
74
+ expect(res.status).toBe(502);
75
+ const body = (await res.json());
76
+ expect(body.status).toBe("error");
77
+ expect(typeof body.message).toBe("string");
78
+ });
79
+ });
80
+ describe("DELETE /__skybridge/tunnel", () => {
81
+ it("forwards to upstream and returns the upstream JSON", async () => {
82
+ const { control, child } = await startControl();
83
+ const { port } = await startProxy(control.port);
84
+ // First start the tunnel so DELETE has something to stop.
85
+ await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel`, {
86
+ method: "POST",
87
+ });
88
+ const res = await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel`, {
89
+ method: "DELETE",
90
+ });
91
+ expect(res.status).toBe(200);
92
+ expect(await res.json()).toEqual({ status: "idle" });
93
+ expect(child.kill).toHaveBeenCalled();
94
+ });
95
+ it("returns 502 when upstream is unavailable", async () => {
96
+ const probe = http.createServer();
97
+ await new Promise((resolve) => probe.listen(0, "127.0.0.1", resolve));
98
+ const deadPort = probe.address().port;
99
+ await new Promise((resolve) => probe.close(() => resolve()));
100
+ const { port } = await startProxy(deadPort);
101
+ const res = await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel`, {
102
+ method: "DELETE",
103
+ });
104
+ expect(res.status).toBe(502);
105
+ });
106
+ });
107
+ describe("GET /__skybridge/tunnel/events", () => {
108
+ it("pipes the upstream SSE stream through to the client", async () => {
109
+ const { control, child } = await startControl();
110
+ const { port } = await startProxy(control.port);
111
+ // Get the manager into a known state so the initial SSE frame is
112
+ // deterministic.
113
+ await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel`, {
114
+ method: "POST",
115
+ });
116
+ child.stdout.emit("data", Buffer.from("Forwarding: https://abc.tunnel.example -> http://localhost:3000\n"));
117
+ const res = await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel/events`);
118
+ expect(res.status).toBe(200);
119
+ expect(res.headers.get("content-type")).toMatch(/text\/event-stream/);
120
+ expect(res.headers.get("cache-control")).toMatch(/no-cache/);
121
+ const body = res.body;
122
+ if (!body) {
123
+ throw new Error("expected response body");
124
+ }
125
+ const reader = body.getReader();
126
+ const { value } = await reader.read();
127
+ const chunk = new TextDecoder().decode(value);
128
+ expect(chunk).toContain("event: state");
129
+ expect(chunk).toContain('"status":"connected"');
130
+ expect(chunk).toContain('"url":"https://abc.tunnel.example"');
131
+ await reader.cancel();
132
+ });
133
+ it("forwards subsequent state changes through the SSE stream", async () => {
134
+ const { control, child } = await startControl();
135
+ const { port } = await startProxy(control.port);
136
+ await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel`, {
137
+ method: "POST",
138
+ });
139
+ const res = await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel/events`);
140
+ const body = res.body;
141
+ if (!body) {
142
+ throw new Error("expected response body");
143
+ }
144
+ const reader = body.getReader();
145
+ const decoder = new TextDecoder();
146
+ // Drain the initial "starting" frame.
147
+ const first = await reader.read();
148
+ expect(decoder.decode(first.value)).toContain('"status":"starting"');
149
+ // Now drive a state change on the manager and read the next frame.
150
+ child.stdout.emit("data", Buffer.from("Forwarding: https://abc.tunnel.example -> http://localhost:3000\n"));
151
+ let combined = "";
152
+ // Reads may chunk arbitrarily, so accumulate until we see the connected
153
+ // event or hit a sane cap.
154
+ for (let i = 0; i < 5; i++) {
155
+ const { value, done } = await reader.read();
156
+ if (done) {
157
+ break;
158
+ }
159
+ combined += decoder.decode(value);
160
+ if (combined.includes('"status":"connected"')) {
161
+ break;
162
+ }
163
+ }
164
+ expect(combined).toContain('"status":"connected"');
165
+ expect(combined).toContain('"url":"https://abc.tunnel.example"');
166
+ await reader.cancel();
167
+ });
168
+ it("returns 502 when upstream is unavailable", async () => {
169
+ const probe = http.createServer();
170
+ await new Promise((resolve) => probe.listen(0, "127.0.0.1", resolve));
171
+ const deadPort = probe.address().port;
172
+ await new Promise((resolve) => probe.close(() => resolve()));
173
+ const { port } = await startProxy(deadPort);
174
+ const res = await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel/events`);
175
+ expect(res.status).toBe(502);
176
+ const body = (await res.json());
177
+ expect(body.status).toBe("error");
178
+ });
179
+ it("aborts the upstream connection when the proxy server is closed mid-stream", async () => {
180
+ const { control } = await startControl();
181
+ const { port, server } = await startProxy(control.port);
182
+ await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel`, {
183
+ method: "POST",
184
+ });
185
+ // Snapshot the manager's listener counts before the SSE subscription so
186
+ // we can verify the proxy disconnected from upstream after shutdown.
187
+ const baseStateListeners = control.manager.listenerCount("state");
188
+ const baseActivityListeners = control.manager.listenerCount("activity");
189
+ const res = await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel/events`);
190
+ const body = res.body;
191
+ if (!body) {
192
+ throw new Error("expected response body");
193
+ }
194
+ const reader = body.getReader();
195
+ // Drain the first frame to confirm the stream is live and the upstream
196
+ // SSE handler has subscribed to the manager.
197
+ await reader.read();
198
+ expect(control.manager.listenerCount("state")).toBe(baseStateListeners + 1);
199
+ // Close the proxy server, destroying in-flight responses. The proxy's
200
+ // req.on("close", ...) should fire and abort the upstream fetch.
201
+ server.closeAllConnections?.();
202
+ await new Promise((resolve) => server.close(() => resolve()));
203
+ // The client-side stream is dead — drain or fail, either is acceptable.
204
+ try {
205
+ while (true) {
206
+ const { done } = await reader.read();
207
+ if (done) {
208
+ break;
209
+ }
210
+ }
211
+ }
212
+ catch {
213
+ // expected: socket terminated when proxy server was destroyed
214
+ }
215
+ // Wait briefly for the upstream's req.on("close") to fire, then assert
216
+ // the manager listeners were detached. This is the load-bearing
217
+ // verification: it proves the proxy's AbortController propagated and
218
+ // upstream cleaned up its SSE subscription.
219
+ const start = Date.now();
220
+ while (control.manager.listenerCount("state") > baseStateListeners &&
221
+ Date.now() - start < 1000) {
222
+ await new Promise((r) => setTimeout(r, 20));
223
+ }
224
+ expect(control.manager.listenerCount("state")).toBe(baseStateListeners);
225
+ expect(control.manager.listenerCount("activity")).toBe(baseActivityListeners);
226
+ });
227
+ });
228
+ });
229
+ //# sourceMappingURL=tunnel-proxy-router.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel-proxy-router.test.js","sourceRoot":"","sources":["../../src/server/tunnel-proxy-router.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAQnE,SAAS,aAAa;IACpB,MAAM,KAAK,GAAG,IAAI,YAAY,EAAe,CAAC;IAC9C,KAAK,CAAC,MAAM,GAAG,IAAI,QAAQ,CAAC,EAAE,IAAI,KAAI,CAAC,EAAE,CAAC,CAAC;IAC3C,KAAK,CAAC,MAAM,GAAG,IAAI,QAAQ,CAAC,EAAE,IAAI,KAAI,CAAC,EAAE,CAAC,CAAC;IAC3C,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC,EAAE,CAAgB,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAC9C,OAAO,KAAK,CAAC;AACf,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,OAA6B;IACjD,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7E,MAAM,IAAI,GAAI,MAAM,CAAC,OAAO,EAAuB,CAAC,IAAI,CAAC;IACzD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC1B,CAAC;AAGD,MAAM,QAAQ,GAAc,EAAE,CAAC;AAE/B,SAAS,CAAC,KAAK,IAAI,EAAE;IACnB,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC;QAC/B,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,OAAO,EAAE,CAAC;QAClB,CAAC;IACH,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,KAAK,UAAU,UAAU,CAAC,WAAmB;IAC3C,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,GAAG,CAAC,GAAG,CAAC,uBAAuB,CAAC,WAAW,CAAC,CAAC,CAAC;IAC9C,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3C,QAAQ,CAAC,IAAI,CACX,GAAG,EAAE,CACH,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAC5B,MAAM,CAAC,mBAAmB,EAAE,EAAE,CAAC;QAC/B,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IAChC,CAAC,CAAC,CACL,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC1B,CAAC;AAED,KAAK,UAAU,YAAY;IACzB,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,MAAM,wBAAwB,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE;QACzD,KAAK,EAAE,GAAG,EAAE,CAAC,KAAK;KACnB,CAAC,CAAC;IACH,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACrC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC5B,CAAC;AAED,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACxC,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;YAClE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,YAAY,EAAE,CAAC;YACzC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAEhD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,oBAAoB,IAAI,qBAAqB,EAAE;gBACrE,MAAM,EAAE,MAAM;aACf,CAAC,CAAC;YAEH,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC7B,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;YACrE,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC;gBAC/B,MAAM,EAAE,UAAU;gBAClB,OAAO,EAAE,kBAAkB;aAC5B,CAAC,CAAC;YACH,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;YACxD,qEAAqE;YACrE,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;YAClC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAClC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,CACtC,CAAC;YACF,MAAM,QAAQ,GAAI,KAAK,CAAC,OAAO,EAAuB,CAAC,IAAI,CAAC;YAC5D,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAEnE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;YAE5C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,oBAAoB,IAAI,qBAAqB,EAAE;gBACrE,MAAM,EAAE,MAAM;aACf,CAAC,CAAC;YACH,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC7B,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAwC,CAAC;YACvE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAClC,MAAM,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;QAC1C,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;YAClE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,YAAY,EAAE,CAAC;YAChD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAEhD,0DAA0D;YAC1D,MAAM,KAAK,CAAC,oBAAoB,IAAI,qBAAqB,EAAE;gBACzD,MAAM,EAAE,MAAM;aACf,CAAC,CAAC;YAEH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,oBAAoB,IAAI,qBAAqB,EAAE;gBACrE,MAAM,EAAE,QAAQ;aACjB,CAAC,CAAC;YAEH,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC7B,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;YACrD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;YACxD,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;YAClC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAClC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,CACtC,CAAC;YACF,MAAM,QAAQ,GAAI,KAAK,CAAC,OAAO,EAAuB,CAAC,IAAI,CAAC;YAC5D,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAEnE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;YAE5C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,oBAAoB,IAAI,qBAAqB,EAAE;gBACrE,MAAM,EAAE,QAAQ;aACjB,CAAC,CAAC;YACH,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gCAAgC,EAAE,GAAG,EAAE;QAC9C,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;YACnE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,YAAY,EAAE,CAAC;YAChD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAEhD,iEAAiE;YACjE,iBAAiB;YACjB,MAAM,KAAK,CAAC,oBAAoB,IAAI,qBAAqB,EAAE;gBACzD,MAAM,EAAE,MAAM;aACf,CAAC,CAAC;YACH,KAAK,CAAC,MAAM,CAAC,IAAI,CACf,MAAM,EACN,MAAM,CAAC,IAAI,CACT,mEAAmE,CACpE,CACF,CAAC;YAEF,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,oBAAoB,IAAI,4BAA4B,CACrD,CAAC;YAEF,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC7B,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;YACtE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAE7D,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;YACtB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;YAC5C,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;YAChC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACtC,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAE9C,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;YACxC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;YAChD,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,oCAAoC,CAAC,CAAC;YAE9D,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;QACxB,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;YACxE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,YAAY,EAAE,CAAC;YAChD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAEhD,MAAM,KAAK,CAAC,oBAAoB,IAAI,qBAAqB,EAAE;gBACzD,MAAM,EAAE,MAAM;aACf,CAAC,CAAC;YAEH,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,oBAAoB,IAAI,4BAA4B,CACrD,CAAC;YACF,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;YACtB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;YAC5C,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;YAChC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;YAElC,sCAAsC;YACtC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YAClC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;YAErE,mEAAmE;YACnE,KAAK,CAAC,MAAM,CAAC,IAAI,CACf,MAAM,EACN,MAAM,CAAC,IAAI,CACT,mEAAmE,CACpE,CACF,CAAC;YAEF,IAAI,QAAQ,GAAG,EAAE,CAAC;YAClB,wEAAwE;YACxE,2BAA2B;YAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC3B,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC5C,IAAI,IAAI,EAAE,CAAC;oBACT,MAAM;gBACR,CAAC;gBACD,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAClC,IAAI,QAAQ,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,CAAC;oBAC9C,MAAM;gBACR,CAAC;YACH,CAAC;YACD,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;YACnD,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,oCAAoC,CAAC,CAAC;YAEjE,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;QACxB,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;YACxD,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;YAClC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAClC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,CACtC,CAAC;YACF,MAAM,QAAQ,GAAI,KAAK,CAAC,OAAO,EAAuB,CAAC,IAAI,CAAC;YAC5D,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAEnE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;YAE5C,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,oBAAoB,IAAI,4BAA4B,CACrD,CAAC;YACF,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC7B,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAuB,CAAC;YACtD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2EAA2E,EAAE,KAAK,IAAI,EAAE;YACzF,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,YAAY,EAAE,CAAC;YACzC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAExD,MAAM,KAAK,CAAC,oBAAoB,IAAI,qBAAqB,EAAE;gBACzD,MAAM,EAAE,MAAM;aACf,CAAC,CAAC;YAEH,wEAAwE;YACxE,qEAAqE;YACrE,MAAM,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAClE,MAAM,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YAExE,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,oBAAoB,IAAI,4BAA4B,CACrD,CAAC;YACF,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;YACtB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;YAC5C,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;YAChC,uEAAuE;YACvE,6CAA6C;YAC7C,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CACjD,kBAAkB,GAAG,CAAC,CACvB,CAAC;YAEF,sEAAsE;YACtE,iEAAiE;YACjE,MAAM,CAAC,mBAAmB,EAAE,EAAE,CAAC;YAC/B,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAEpE,wEAAwE;YACxE,IAAI,CAAC;gBACH,OAAO,IAAI,EAAE,CAAC;oBACZ,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;oBACrC,IAAI,IAAI,EAAE,CAAC;wBACT,MAAM;oBACR,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,8DAA8D;YAChE,CAAC;YAED,uEAAuE;YACvE,gEAAgE;YAChE,qEAAqE;YACrE,4CAA4C;YAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACzB,OACE,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,kBAAkB;gBAC3D,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,IAAI,EACzB,CAAC;gBACD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAC9C,CAAC;YACD,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;YACxE,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CACpD,qBAAqB,CACtB,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -2,14 +2,14 @@ import crypto from "node:crypto";
2
2
  import { afterEach, beforeEach, describe, expect, it, vi, } from "vitest";
3
3
  import { createMockExtra, createMockMcpServer, resetTestEnv, setTestEnv, } from "./utils.js";
4
4
  const mockManifest = {
5
- "skybridge:view:my-widget": {
6
- file: "assets/my-widget-abc123.js",
7
- name: "my-widget",
5
+ "skybridge:view:my-view": {
6
+ file: "assets/my-view-abc123.js",
7
+ name: "my-view",
8
8
  isEntry: true,
9
9
  },
10
- "skybridge:view:folder-widget": {
11
- file: "assets/folder-widget-def456.js",
12
- name: "folder-widget",
10
+ "skybridge:view:folder-view": {
11
+ file: "assets/folder-view-def456.js",
12
+ name: "folder-view",
13
13
  isEntry: true,
14
14
  },
15
15
  "style.css": { file: "style.css" },
@@ -57,10 +57,10 @@ describe("McpServer.registerTool (unified API)", () => {
57
57
  it("should generate correct HTML for development mode", async () => {
58
58
  setTestEnv({ NODE_ENV: "development" });
59
59
  server.registerTool({
60
- name: "my-widget",
60
+ name: "my-view",
61
61
  description: "Test tool",
62
62
  view: {
63
- component: "my-widget",
63
+ component: "my-view",
64
64
  description: "Test view",
65
65
  },
66
66
  }, vi.fn());
@@ -71,12 +71,12 @@ describe("McpServer.registerTool (unified API)", () => {
71
71
  const serverUrl = `http://${host}`;
72
72
  const hmrUrl = `ws://${host}`;
73
73
  const mockExtra = createMockExtra(host);
74
- const result = await appsSdkResourceCallback(new URL("ui://widgets/apps-sdk/my-widget.html"), mockExtra);
74
+ const result = await appsSdkResourceCallback(new URL("ui://views/apps-sdk/my-view.html"), mockExtra);
75
75
  expect(mockRegisterTool).toHaveBeenCalled();
76
76
  expect(result).toEqual({
77
77
  contents: [
78
78
  {
79
- uri: "ui://widgets/apps-sdk/my-widget.html",
79
+ uri: "ui://views/apps-sdk/my-view.html",
80
80
  mimeType: "text/html+skybridge",
81
81
  text: expect.stringContaining('<div id="root"></div>'),
82
82
  _meta: {
@@ -92,15 +92,15 @@ describe("McpServer.registerTool (unified API)", () => {
92
92
  });
93
93
  expect(result.contents[0]?.text).toContain(`${serverUrl}/assets/@react-refresh`);
94
94
  expect(result.contents[0]?.text).toContain(`${serverUrl}/@vite/client`);
95
- expect(result.contents[0]?.text).toContain(`${serverUrl}/_skybridge/view/my-widget`);
95
+ expect(result.contents[0]?.text).toContain(`${serverUrl}/_skybridge/view/my-view`);
96
96
  });
97
97
  it("should generate correct HTML for production mode", async () => {
98
98
  setTestEnv({ NODE_ENV: "production" });
99
99
  server.registerTool({
100
- name: "my-widget",
100
+ name: "my-view",
101
101
  description: "Test tool",
102
102
  view: {
103
- component: "my-widget",
103
+ component: "my-view",
104
104
  description: "Test view",
105
105
  },
106
106
  }, vi.fn());
@@ -110,7 +110,7 @@ describe("McpServer.registerTool (unified API)", () => {
110
110
  const host = "myapp.com";
111
111
  const serverUrl = `https://${host}`;
112
112
  const mockExtra = createMockExtra(host);
113
- const versionedUri = `ui://widgets/apps-sdk/my-widget.html${expectedVersionParam("assets/my-widget-abc123.js", "style.css")}`;
113
+ const versionedUri = `ui://views/apps-sdk/my-view.html${expectedVersionParam("assets/my-view-abc123.js", "style.css")}`;
114
114
  const result = await appsSdkResourceCallback(new URL(versionedUri), mockExtra);
115
115
  expect(result).toEqual({
116
116
  contents: [
@@ -131,15 +131,15 @@ describe("McpServer.registerTool (unified API)", () => {
131
131
  });
132
132
  expect(result.contents[0]?.text).not.toContain(`${serverUrl}/assets/@react-refresh`);
133
133
  expect(result.contents[0]?.text).not.toContain(`${serverUrl}@vite/client`);
134
- expect(result.contents[0]?.text).toContain(`${serverUrl}/assets/assets/my-widget-abc123.js`);
134
+ expect(result.contents[0]?.text).toContain(`${serverUrl}/assets/assets/my-view-abc123.js`);
135
135
  expect(result.contents[0]?.text).toContain(`${serverUrl}/assets/style.css`);
136
136
  });
137
137
  it("should prefer x-alpic-forwarded-url when hashing Claude view domains", async () => {
138
138
  setTestEnv({ NODE_ENV: "production" });
139
139
  server.registerTool({
140
- name: "my-widget",
140
+ name: "my-view",
141
141
  description: "Test tool",
142
- view: { component: "my-widget", description: "Test view" },
142
+ view: { component: "my-view", description: "Test view" },
143
143
  }, vi.fn());
144
144
  const extAppsResourceCallback = mockRegisterResource.mock
145
145
  .calls[1]?.[3];
@@ -150,7 +150,7 @@ describe("McpServer.registerTool (unified API)", () => {
150
150
  .update(forwardedUrl)
151
151
  .digest("hex")
152
152
  .slice(0, 32)}.claudemcpcontent.com`;
153
- const result = await extAppsResourceCallback(new URL(`ui://widgets/ext-apps/my-widget.html${expectedVersionParam("assets/my-widget-abc123.js", "style.css")}`), createMockExtra("localhost:3000", {
153
+ const result = await extAppsResourceCallback(new URL(`ui://views/ext-apps/my-view.html${expectedVersionParam("assets/my-view-abc123.js", "style.css")}`), createMockExtra("localhost:3000", {
154
154
  headers: {
155
155
  "user-agent": "Claude-User",
156
156
  "x-alpic-forwarded-url": forwardedUrl,
@@ -171,10 +171,10 @@ describe("McpServer.registerTool (unified API)", () => {
171
171
  });
172
172
  it("should register resources with correct hostType for both apps-sdk and ext-apps", async () => {
173
173
  server.registerTool({
174
- name: "my-widget",
174
+ name: "my-view",
175
175
  description: "Test tool",
176
176
  view: {
177
- component: "my-widget",
177
+ component: "my-view",
178
178
  description: "Test view",
179
179
  prefersBorder: true,
180
180
  },
@@ -185,11 +185,11 @@ describe("McpServer.registerTool (unified API)", () => {
185
185
  const host = "localhost:3000";
186
186
  const serverUrl = `http://${host}`;
187
187
  const hmrUrl = `ws://${host}`;
188
- const appsSdkResult = await appsSdkCallback(new URL("ui://widgets/apps-sdk/my-widget.html"), createMockExtra(host));
188
+ const appsSdkResult = await appsSdkCallback(new URL("ui://views/apps-sdk/my-view.html"), createMockExtra(host));
189
189
  expect(appsSdkResult).toEqual({
190
190
  contents: [
191
191
  {
192
- uri: "ui://widgets/apps-sdk/my-widget.html",
192
+ uri: "ui://views/apps-sdk/my-view.html",
193
193
  mimeType: "text/html+skybridge",
194
194
  text: expect.stringContaining('<div id="root"></div>'),
195
195
  _meta: {
@@ -208,11 +208,11 @@ describe("McpServer.registerTool (unified API)", () => {
208
208
  const extAppsResourceCallback = mockRegisterResource.mock
209
209
  .calls[1]?.[3];
210
210
  expect(extAppsResourceCallback).toBeDefined();
211
- const extAppsResult = await extAppsResourceCallback(new URL("ui://widgets/ext-apps/my-widget.html"), createMockExtra(host));
211
+ const extAppsResult = await extAppsResourceCallback(new URL("ui://views/ext-apps/my-view.html"), createMockExtra(host));
212
212
  expect(extAppsResult).toEqual({
213
213
  contents: [
214
214
  {
215
- uri: "ui://widgets/ext-apps/my-widget.html",
215
+ uri: "ui://views/ext-apps/my-view.html",
216
216
  mimeType: "text/html;profile=mcp-app",
217
217
  text: expect.stringContaining('<div id="root"></div>'),
218
218
  _meta: {
@@ -234,24 +234,24 @@ describe("McpServer.registerTool (unified API)", () => {
234
234
  });
235
235
  it("should register tool with ui.resourceUri metadata", async () => {
236
236
  server.registerTool({
237
- name: "my-widget",
237
+ name: "my-view",
238
238
  description: "Test tool",
239
- view: { component: "my-widget", description: "Test view" },
239
+ view: { component: "my-view", description: "Test view" },
240
240
  }, vi.fn());
241
241
  expect(mockRegisterTool).toHaveBeenCalledTimes(1);
242
242
  const toolCallArgs = mockRegisterTool.mock.calls[0];
243
243
  const toolConfig = toolCallArgs?.[1];
244
244
  expect(toolConfig._meta).toHaveProperty("ui");
245
245
  expect(toolConfig._meta?.ui).toEqual({
246
- resourceUri: "ui://widgets/ext-apps/my-widget.html",
246
+ resourceUri: "ui://views/ext-apps/my-view.html",
247
247
  });
248
248
  });
249
249
  it("should register tool with openai/outputTemplate when apps-sdk only", async () => {
250
250
  server.registerTool({
251
- name: "my-widget",
251
+ name: "my-view",
252
252
  description: "Test tool",
253
253
  view: {
254
- component: "my-widget",
254
+ component: "my-view",
255
255
  description: "Test view",
256
256
  hosts: ["apps-sdk"],
257
257
  },
@@ -260,70 +260,70 @@ describe("McpServer.registerTool (unified API)", () => {
260
260
  const toolCallArgs = mockRegisterTool.mock.calls[0];
261
261
  const toolConfig = toolCallArgs?.[1];
262
262
  expect(toolConfig._meta).not.toHaveProperty("ui");
263
- expect(toolConfig._meta?.["openai/outputTemplate"]).toBe("ui://widgets/apps-sdk/my-widget.html");
263
+ expect(toolConfig._meta?.["openai/outputTemplate"]).toBe("ui://views/apps-sdk/my-view.html");
264
264
  });
265
265
  it("should not version view URIs in development", () => {
266
266
  server.registerTool({
267
- name: "my-widget",
267
+ name: "my-view",
268
268
  description: "Test tool",
269
- view: { component: "my-widget", description: "Test view" },
269
+ view: { component: "my-view", description: "Test view" },
270
270
  }, vi.fn());
271
271
  const toolConfig = mockRegisterTool.mock.calls[0]?.[1];
272
- expect(toolConfig._meta?.["openai/outputTemplate"]).toBe("ui://widgets/apps-sdk/my-widget.html");
273
- expect(toolConfig._meta?.ui?.resourceUri).toBe("ui://widgets/ext-apps/my-widget.html");
272
+ expect(toolConfig._meta?.["openai/outputTemplate"]).toBe("ui://views/apps-sdk/my-view.html");
273
+ expect(toolConfig._meta?.ui?.resourceUri).toBe("ui://views/ext-apps/my-view.html");
274
274
  // The URI registered with the resource handler must match the URI in
275
275
  // outputTemplate exactly so the SDK can resolve `resources/read` requests.
276
- expect(mockRegisterResource.mock.calls[0]?.[1]).toBe("ui://widgets/apps-sdk/my-widget.html");
277
- expect(mockRegisterResource.mock.calls[1]?.[1]).toBe("ui://widgets/ext-apps/my-widget.html");
276
+ expect(mockRegisterResource.mock.calls[0]?.[1]).toBe("ui://views/apps-sdk/my-view.html");
277
+ expect(mockRegisterResource.mock.calls[1]?.[1]).toBe("ui://views/ext-apps/my-view.html");
278
278
  });
279
279
  it("should append a stable content hash to view URIs in production", () => {
280
280
  setTestEnv({ NODE_ENV: "production" });
281
281
  server.registerTool({
282
- name: "my-widget",
282
+ name: "my-view",
283
283
  description: "Test tool",
284
- view: { component: "my-widget", description: "Test view" },
284
+ view: { component: "my-view", description: "Test view" },
285
285
  }, vi.fn());
286
- const expected = expectedVersionParam("assets/my-widget-abc123.js", "style.css");
286
+ const expected = expectedVersionParam("assets/my-view-abc123.js", "style.css");
287
287
  const toolConfig = mockRegisterTool.mock.calls[0]?.[1];
288
- expect(toolConfig._meta?.["openai/outputTemplate"]).toBe(`ui://widgets/apps-sdk/my-widget.html${expected}`);
289
- expect(toolConfig._meta?.ui?.resourceUri).toBe(`ui://widgets/ext-apps/my-widget.html${expected}`);
290
- expect(mockRegisterResource.mock.calls[0]?.[1]).toBe(`ui://widgets/apps-sdk/my-widget.html${expected}`);
291
- expect(mockRegisterResource.mock.calls[1]?.[1]).toBe(`ui://widgets/ext-apps/my-widget.html${expected}`);
288
+ expect(toolConfig._meta?.["openai/outputTemplate"]).toBe(`ui://views/apps-sdk/my-view.html${expected}`);
289
+ expect(toolConfig._meta?.ui?.resourceUri).toBe(`ui://views/ext-apps/my-view.html${expected}`);
290
+ expect(mockRegisterResource.mock.calls[0]?.[1]).toBe(`ui://views/apps-sdk/my-view.html${expected}`);
291
+ expect(mockRegisterResource.mock.calls[1]?.[1]).toBe(`ui://views/ext-apps/my-view.html${expected}`);
292
292
  });
293
293
  it("should produce different version params for views with different bundles", () => {
294
294
  setTestEnv({ NODE_ENV: "production" });
295
295
  server.registerTool({
296
- name: "my-widget",
296
+ name: "my-view",
297
297
  description: "First tool",
298
- view: { component: "my-widget" },
298
+ view: { component: "my-view" },
299
299
  }, vi.fn());
300
300
  server.registerTool({
301
- name: "folder-widget",
301
+ name: "folder-view",
302
302
  description: "Second tool",
303
- view: { component: "folder-widget" },
303
+ view: { component: "folder-view" },
304
304
  }, vi.fn());
305
- const myWidgetTemplate = (mockRegisterTool.mock.calls[0]?.[1])._meta?.["openai/outputTemplate"];
306
- const folderWidgetTemplate = (mockRegisterTool.mock.calls[1]?.[1])._meta?.["openai/outputTemplate"];
307
- expect(myWidgetTemplate).not.toEqual(folderWidgetTemplate);
308
- expect(myWidgetTemplate).toMatch(/\?v=[0-9a-f]{8}$/);
309
- expect(folderWidgetTemplate).toMatch(/\?v=[0-9a-f]{8}$/);
305
+ const myviewTemplate = (mockRegisterTool.mock.calls[0]?.[1])._meta?.["openai/outputTemplate"];
306
+ const folderviewTemplate = (mockRegisterTool.mock.calls[1]?.[1])._meta?.["openai/outputTemplate"];
307
+ expect(myviewTemplate).not.toEqual(folderviewTemplate);
308
+ expect(myviewTemplate).toMatch(/\?v=[0-9a-f]{8}$/);
309
+ expect(folderviewTemplate).toMatch(/\?v=[0-9a-f]{8}$/);
310
310
  });
311
311
  it("should fall back to bare URI in production when manifest is missing", () => {
312
312
  setTestEnv({ NODE_ENV: "production" });
313
313
  server.registerTool({
314
- name: "unknown-widget",
314
+ name: "unknown-view",
315
315
  description: "Test tool",
316
- view: { component: "unknown-widget" },
316
+ view: { component: "unknown-view" },
317
317
  }, vi.fn());
318
318
  const toolConfig = mockRegisterTool.mock.calls[0]?.[1];
319
- expect(toolConfig._meta?.["openai/outputTemplate"]).toBe("ui://widgets/apps-sdk/unknown-widget.html");
319
+ expect(toolConfig._meta?.["openai/outputTemplate"]).toBe("ui://views/apps-sdk/unknown-view.html");
320
320
  });
321
321
  it("should register tool with ui.resourceUri only when mcp-app only", async () => {
322
322
  server.registerTool({
323
- name: "my-widget",
323
+ name: "my-view",
324
324
  description: "Test tool",
325
325
  view: {
326
- component: "my-widget",
326
+ component: "my-view",
327
327
  description: "Test view",
328
328
  hosts: ["mcp-app"],
329
329
  },
@@ -333,7 +333,7 @@ describe("McpServer.registerTool (unified API)", () => {
333
333
  const toolConfig = toolCallArgs?.[1];
334
334
  expect(toolConfig._meta).toHaveProperty("ui");
335
335
  expect(toolConfig._meta?.ui).toEqual({
336
- resourceUri: "ui://widgets/ext-apps/my-widget.html",
336
+ resourceUri: "ui://views/ext-apps/my-view.html",
337
337
  });
338
338
  expect(toolConfig._meta?.["openai/outputTemplate"]).toBeUndefined();
339
339
  });
@@ -343,9 +343,9 @@ describe("McpServer.registerTool (unified API)", () => {
343
343
  structuredContent: { data: "test" },
344
344
  });
345
345
  server.registerTool({
346
- name: "my-widget",
346
+ name: "my-view",
347
347
  description: "Test tool",
348
- view: { component: "my-widget", description: "Test view" },
348
+ view: { component: "my-view", description: "Test view" },
349
349
  }, mockToolCallback);
350
350
  const wrappedCallback = mockRegisterTool.mock.calls[0]?.[2];
351
351
  expect(wrappedCallback).toBeDefined();
@@ -362,9 +362,9 @@ describe("McpServer.registerTool (unified API)", () => {
362
362
  _meta: { requestId: "req-123", cached: true },
363
363
  });
364
364
  server.registerTool({
365
- name: "my-widget",
365
+ name: "my-view",
366
366
  description: "Test tool",
367
- view: { component: "my-widget", description: "Test view" },
367
+ view: { component: "my-view", description: "Test view" },
368
368
  }, mockToolCallback);
369
369
  const wrappedCallback = mockRegisterTool.mock.calls[0]?.[2];
370
370
  const result = await wrappedCallback({}, {});
@@ -378,9 +378,9 @@ describe("McpServer.registerTool (unified API)", () => {
378
378
  structuredContent: {},
379
379
  });
380
380
  server.registerTool({
381
- name: "my-widget",
381
+ name: "my-view",
382
382
  description: "Test tool",
383
- view: { component: "my-widget", description: "Test view" },
383
+ view: { component: "my-view", description: "Test view" },
384
384
  }, mockToolCallback);
385
385
  const wrappedCallback = mockRegisterTool.mock.calls[0]?.[2];
386
386
  const result1 = await wrappedCallback({}, {});
@@ -473,7 +473,7 @@ describe("McpServer.registerTool (unified API)", () => {
473
473
  const host = "localhost:3000";
474
474
  const serverUrl = `http://${host}`;
475
475
  const hmrUrl = `ws://${host}`;
476
- const result = await appsSdkCallback(new URL("ui://widgets/apps-sdk/csp-tool.html"), createMockExtra(host));
476
+ const result = await appsSdkCallback(new URL("ui://views/apps-sdk/csp-tool.html"), createMockExtra(host));
477
477
  const meta = result.contents[0]?._meta;
478
478
  expect(meta["openai/widgetCSP"]).toEqual({
479
479
  resource_domains: [serverUrl, "https://cdn.example.com"],
@@ -497,7 +497,7 @@ describe("McpServer.registerTool (unified API)", () => {
497
497
  }, vi.fn());
498
498
  const appsSdkCallback = mockRegisterResource.mock
499
499
  .calls[0]?.[3];
500
- const result = await appsSdkCallback(new URL("ui://widgets/apps-sdk/override-tool.html"), createMockExtra("localhost:3000"));
500
+ const result = await appsSdkCallback(new URL("ui://views/apps-sdk/override-tool.html"), createMockExtra("localhost:3000"));
501
501
  const meta = result.contents[0]?._meta;
502
502
  expect(meta["openai/widgetCSP"]).toEqual({
503
503
  connect_domains: ["https://api.y.com"],