skybridge 0.0.0-dev.fd6e4e8 → 0.0.0-dev.fd767e9
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/README.md +123 -124
- 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/header.js.map +1 -1
- package/dist/cli/resolve-views-dir.d.ts +1 -0
- package/dist/cli/resolve-views-dir.js +17 -0
- package/dist/cli/resolve-views-dir.js.map +1 -0
- package/dist/cli/run-command.js.map +1 -1
- package/dist/cli/telemetry.js.map +1 -1
- package/dist/cli/tunnel-control-server.d.ts +9 -0
- package/dist/cli/tunnel-control-server.js +31 -0
- package/dist/cli/tunnel-control-server.js.map +1 -0
- package/dist/cli/tunnel-control-server.test.js +39 -0
- package/dist/cli/tunnel-control-server.test.js.map +1 -0
- package/dist/cli/tunnel-handler.d.ts +3 -0
- package/dist/cli/tunnel-handler.js +48 -0
- package/dist/cli/tunnel-handler.js.map +1 -0
- package/dist/cli/tunnel-handler.test.js +105 -0
- package/dist/cli/tunnel-handler.test.js.map +1 -0
- package/dist/cli/tunnel.d.ts +57 -0
- package/dist/cli/tunnel.js +154 -0
- package/dist/cli/tunnel.js.map +1 -0
- package/dist/cli/tunnel.test.js +190 -0
- package/dist/cli/tunnel.test.js.map +1 -0
- package/dist/cli/types.js.map +1 -1
- package/dist/cli/use-execute-steps.js.map +1 -1
- package/dist/cli/use-messages.js.map +1 -1
- package/dist/cli/use-nodemon.js +11 -2
- package/dist/cli/use-nodemon.js.map +1 -1
- package/dist/cli/use-open-browser.d.ts +1 -0
- package/dist/cli/use-open-browser.js +44 -0
- package/dist/cli/use-open-browser.js.map +1 -0
- 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/cli/use-tunnel.d.ts +1 -1
- package/dist/cli/use-tunnel.js +102 -68
- package/dist/cli/use-tunnel.js.map +1 -1
- package/dist/cli/use-typescript-check.d.ts +1 -0
- package/dist/cli/use-typescript-check.js +42 -7
- package/dist/cli/use-typescript-check.js.map +1 -1
- package/dist/commands/build.d.ts +0 -1
- package/dist/commands/build.js +51 -8
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/create.d.ts +9 -0
- package/dist/commands/create.js +30 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/dev.d.ts +1 -0
- package/dist/commands/dev.js +51 -2
- 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/commands/telemetry/disable.js.map +1 -1
- package/dist/commands/telemetry/enable.js.map +1 -1
- package/dist/commands/telemetry/status.js.map +1 -1
- package/dist/server/asset-base-url-transform-plugin.d.ts +1 -0
- package/dist/server/asset-base-url-transform-plugin.js +17 -2
- package/dist/server/asset-base-url-transform-plugin.js.map +1 -1
- package/dist/server/asset-base-url-transform-plugin.test.js +80 -1
- package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -1
- package/dist/server/auth.d.ts +20 -0
- package/dist/server/auth.js +28 -0
- package/dist/server/auth.js.map +1 -0
- package/dist/server/build-manifest.test.d.ts +1 -0
- package/dist/server/build-manifest.test.js +27 -0
- package/dist/server/build-manifest.test.js.map +1 -0
- package/dist/server/content-helpers.d.ts +40 -0
- package/dist/server/content-helpers.js +33 -0
- package/dist/server/content-helpers.js.map +1 -1
- package/dist/server/content-helpers.test.js +1 -1
- package/dist/server/content-helpers.test.js.map +1 -1
- package/dist/server/express.d.ts +1 -5
- package/dist/server/express.js +34 -10
- package/dist/server/express.js.map +1 -1
- package/dist/server/express.test.js +279 -71
- package/dist/server/express.test.js.map +1 -1
- package/dist/server/file-ref.d.ts +28 -0
- package/dist/server/file-ref.js +27 -0
- package/dist/server/file-ref.js.map +1 -0
- package/dist/server/index.d.ts +5 -3
- package/dist/server/index.js +4 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/inferUtilityTypes.d.ts +6 -6
- package/dist/server/inferUtilityTypes.js.map +1 -1
- package/dist/server/metric.js.map +1 -1
- package/dist/server/middleware.d.ts +16 -3
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/middleware.test-d.js.map +1 -1
- package/dist/server/middleware.test.js.map +1 -1
- package/dist/server/server.d.ts +248 -41
- package/dist/server/server.js +327 -114
- package/dist/server/server.js.map +1 -1
- package/dist/server/templateHelper.d.ts +5 -7
- package/dist/server/templateHelper.js +3 -22
- package/dist/server/templateHelper.js.map +1 -1
- package/dist/server/templates.generated.d.ts +4 -0
- package/dist/server/templates.generated.js +47 -0
- package/dist/server/templates.generated.js.map +1 -0
- package/dist/server/tunnel-proxy-router.d.ts +7 -0
- package/dist/server/tunnel-proxy-router.js +110 -0
- package/dist/server/tunnel-proxy-router.js.map +1 -0
- package/dist/server/tunnel-proxy-router.test.d.ts +1 -0
- package/dist/server/tunnel-proxy-router.test.js +229 -0
- package/dist/server/tunnel-proxy-router.test.js.map +1 -0
- package/dist/server/viewsDevServer.d.ts +14 -0
- package/dist/server/{widgetsDevServer.js → viewsDevServer.js} +6 -6
- package/dist/server/viewsDevServer.js.map +1 -0
- package/dist/test/utils.d.ts +7 -7
- package/dist/test/utils.js +21 -21
- package/dist/test/utils.js.map +1 -1
- package/dist/test/view.test.d.ts +1 -0
- package/dist/test/{widget.test.js → view.test.js} +173 -59
- package/dist/test/view.test.js.map +1 -0
- package/dist/version.js +1 -3
- package/dist/version.js.map +1 -1
- package/dist/web/bridges/apps-sdk/adaptor.d.ts +8 -3
- package/dist/web/bridges/apps-sdk/adaptor.js +35 -5
- package/dist/web/bridges/apps-sdk/adaptor.js.map +1 -1
- package/dist/web/bridges/apps-sdk/bridge.d.ts +1 -0
- package/dist/web/bridges/apps-sdk/bridge.js +1 -0
- package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
- package/dist/web/bridges/apps-sdk/index.js.map +1 -1
- package/dist/web/bridges/apps-sdk/types.d.ts +8 -1
- package/dist/web/bridges/apps-sdk/types.js.map +1 -1
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +11 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +11 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
- package/dist/web/bridges/get-adaptor.d.ts +7 -0
- package/dist/web/bridges/get-adaptor.js +7 -0
- package/dist/web/bridges/get-adaptor.js.map +1 -1
- package/dist/web/bridges/index.js.map +1 -1
- package/dist/web/bridges/mcp-app/adaptor.d.ts +12 -7
- package/dist/web/bridges/mcp-app/adaptor.js +45 -30
- package/dist/web/bridges/mcp-app/adaptor.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.d.ts +4 -2
- package/dist/web/bridges/mcp-app/bridge.js +23 -1
- package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
- package/dist/web/bridges/mcp-app/index.js.map +1 -1
- package/dist/web/bridges/mcp-app/types.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +12 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js +12 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -1
- package/dist/web/bridges/mcp-app/view-tools.test.d.ts +1 -0
- 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 +105 -10
- package/dist/web/bridges/types.js.map +1 -1
- package/dist/web/bridges/use-host-context.d.ts +5 -0
- package/dist/web/bridges/use-host-context.js +5 -0
- package/dist/web/bridges/use-host-context.js.map +1 -1
- package/dist/web/components/modal-provider.js +1 -1
- package/dist/web/components/modal-provider.js.map +1 -1
- package/dist/web/create-store.d.ts +26 -0
- package/dist/web/create-store.js +35 -9
- package/dist/web/create-store.js.map +1 -1
- package/dist/web/create-store.test.js +14 -16
- package/dist/web/create-store.test.js.map +1 -1
- package/dist/web/data-llm.d.ts +34 -1
- package/dist/web/data-llm.js +31 -3
- package/dist/web/data-llm.js.map +1 -1
- package/dist/web/data-llm.test.js +22 -22
- package/dist/web/data-llm.test.js.map +1 -1
- package/dist/web/generate-helpers.d.ts +16 -14
- package/dist/web/generate-helpers.js +16 -14
- package/dist/web/generate-helpers.js.map +1 -1
- package/dist/web/generate-helpers.test-d.js +30 -28
- package/dist/web/generate-helpers.test-d.js.map +1 -1
- package/dist/web/generate-helpers.test.js.map +1 -1
- package/dist/web/helpers/state.d.ts +2 -2
- package/dist/web/helpers/state.js +11 -11
- package/dist/web/helpers/state.js.map +1 -1
- package/dist/web/helpers/state.test.js +9 -9
- package/dist/web/helpers/state.test.js.map +1 -1
- package/dist/web/hooks/index.d.ts +5 -1
- package/dist/web/hooks/index.js +5 -1
- package/dist/web/hooks/index.js.map +1 -1
- package/dist/web/hooks/test/utils.d.ts +6 -2
- package/dist/web/hooks/test/utils.js +13 -2
- package/dist/web/hooks/test/utils.js.map +1 -1
- package/dist/web/hooks/use-call-tool.d.ts +45 -0
- package/dist/web/hooks/use-call-tool.js +28 -0
- package/dist/web/hooks/use-call-tool.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test-d.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test.js +27 -6
- package/dist/web/hooks/use-call-tool.test.js.map +1 -1
- package/dist/web/hooks/use-display-mode.d.ts +20 -0
- package/dist/web/hooks/use-display-mode.js +20 -0
- package/dist/web/hooks/use-display-mode.js.map +1 -1
- package/dist/web/hooks/use-display-mode.test-d.js.map +1 -1
- package/dist/web/hooks/use-display-mode.test.js.map +1 -1
- package/dist/web/hooks/use-download.d.ts +5 -0
- package/dist/web/hooks/use-download.js +8 -0
- package/dist/web/hooks/use-download.js.map +1 -0
- package/dist/web/hooks/use-download.test.d.ts +1 -0
- package/dist/web/hooks/use-download.test.js +95 -0
- package/dist/web/hooks/use-download.test.js.map +1 -0
- package/dist/web/hooks/use-files.d.ts +32 -0
- package/dist/web/hooks/use-files.js +32 -0
- package/dist/web/hooks/use-files.js.map +1 -1
- package/dist/web/hooks/use-files.test.js.map +1 -1
- package/dist/web/hooks/use-layout.d.ts +2 -0
- package/dist/web/hooks/use-layout.js +2 -0
- package/dist/web/hooks/use-layout.js.map +1 -1
- package/dist/web/hooks/use-layout.test.js.map +1 -1
- package/dist/web/hooks/use-open-external.d.ts +17 -0
- package/dist/web/hooks/use-open-external.js +16 -0
- package/dist/web/hooks/use-open-external.js.map +1 -1
- package/dist/web/hooks/use-open-external.test.js.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.d.ts +16 -0
- package/dist/web/hooks/use-request-close.js +21 -0
- package/dist/web/hooks/use-request-close.js.map +1 -0
- package/dist/web/hooks/use-request-close.test.d.ts +1 -0
- package/dist/web/hooks/use-request-close.test.js +52 -0
- package/dist/web/hooks/use-request-close.test.js.map +1 -0
- package/dist/web/hooks/use-request-modal.d.ts +16 -1
- package/dist/web/hooks/use-request-modal.js +19 -4
- package/dist/web/hooks/use-request-modal.js.map +1 -1
- package/dist/web/hooks/use-request-modal.test.js +1 -1
- package/dist/web/hooks/use-request-modal.test.js.map +1 -1
- package/dist/web/hooks/use-request-size.d.ts +20 -0
- package/dist/web/hooks/use-request-size.js +24 -0
- package/dist/web/hooks/use-request-size.js.map +1 -0
- package/dist/web/hooks/use-request-size.test.d.ts +1 -0
- package/dist/web/hooks/use-request-size.test.js +65 -0
- package/dist/web/hooks/use-request-size.test.js.map +1 -0
- package/dist/web/hooks/use-send-follow-up-message.d.ts +19 -1
- package/dist/web/hooks/use-send-follow-up-message.js +19 -2
- package/dist/web/hooks/use-send-follow-up-message.js.map +1 -1
- package/dist/web/hooks/use-set-open-in-app-url.d.ts +17 -0
- package/dist/web/hooks/use-set-open-in-app-url.js +17 -0
- package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -1
- package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
- package/dist/web/hooks/use-tool-info.d.ts +53 -2
- package/dist/web/hooks/use-tool-info.js +30 -7
- package/dist/web/hooks/use-tool-info.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test-d.js +11 -29
- package/dist/web/hooks/use-tool-info.test-d.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test.js +5 -5
- package/dist/web/hooks/use-tool-info.test.js.map +1 -1
- package/dist/web/hooks/use-user.d.ts +2 -0
- package/dist/web/hooks/use-user.js +2 -0
- package/dist/web/hooks/use-user.js.map +1 -1
- package/dist/web/hooks/use-user.test.js.map +1 -1
- package/dist/web/hooks/use-view-state.d.ts +25 -0
- package/dist/web/hooks/use-view-state.js +32 -0
- package/dist/web/hooks/use-view-state.js.map +1 -0
- package/dist/web/hooks/use-view-state.test.d.ts +1 -0
- package/dist/web/hooks/{use-widget-state.test.js → use-view-state.test.js} +17 -17
- package/dist/web/hooks/use-view-state.test.js.map +1 -0
- package/dist/web/index.d.ts +1 -3
- package/dist/web/index.js +1 -2
- package/dist/web/index.js.map +1 -1
- package/dist/web/mount-view.d.ts +20 -0
- package/dist/web/{mount-widget.js → mount-view.js} +21 -2
- package/dist/web/mount-view.js.map +1 -0
- package/dist/web/plugin/data-llm.test.js.map +1 -1
- package/dist/web/plugin/plugin.d.ts +29 -1
- package/dist/web/plugin/plugin.js +113 -55
- package/dist/web/plugin/plugin.js.map +1 -1
- package/dist/web/plugin/scan-views.d.ts +16 -0
- package/dist/web/plugin/scan-views.js +88 -0
- package/dist/web/plugin/scan-views.js.map +1 -0
- package/dist/web/plugin/scan-views.test.d.ts +1 -0
- package/dist/web/plugin/scan-views.test.js +99 -0
- package/dist/web/plugin/scan-views.test.js.map +1 -0
- package/dist/web/plugin/transform-data-llm.js.map +1 -1
- package/dist/web/plugin/transform-data-llm.test.js.map +1 -1
- package/dist/web/plugin/{validate-widget.js → validate-view.js} +1 -1
- package/dist/web/plugin/validate-view.js.map +1 -0
- package/dist/web/plugin/validate-view.test.d.ts +1 -0
- package/dist/web/plugin/{validate-widget.test.js → validate-view.test.js} +6 -6
- package/dist/web/plugin/validate-view.test.js.map +1 -0
- package/dist/web/proxy.js.map +1 -1
- package/dist/web/types.d.ts +4 -0
- package/dist/web/types.js.map +1 -1
- package/package.json +20 -8
- package/dist/server/templates/development.hbs +0 -12
- package/dist/server/templates/production.hbs +0 -6
- package/dist/server/widgetsDevServer.d.ts +0 -14
- package/dist/server/widgetsDevServer.js.map +0 -1
- package/dist/test/widget.test.js.map +0 -1
- package/dist/web/hooks/use-widget-state.d.ts +0 -4
- package/dist/web/hooks/use-widget-state.js +0 -32
- package/dist/web/hooks/use-widget-state.js.map +0 -1
- package/dist/web/hooks/use-widget-state.test.js.map +0 -1
- package/dist/web/mount-widget.d.ts +0 -1
- package/dist/web/mount-widget.js.map +0 -1
- package/dist/web/plugin/scan-widgets.d.ts +0 -8
- package/dist/web/plugin/scan-widgets.js +0 -68
- package/dist/web/plugin/scan-widgets.js.map +0 -1
- package/dist/web/plugin/scan-widgets.test.js +0 -96
- package/dist/web/plugin/scan-widgets.test.js.map +0 -1
- package/dist/web/plugin/validate-widget.js.map +0 -1
- package/dist/web/plugin/validate-widget.test.js.map +0 -1
- /package/dist/{test/widget.test.d.ts → cli/build-helpers.test.d.ts} +0 -0
- /package/dist/{web/hooks/use-widget-state.test.d.ts → cli/tunnel-control-server.test.d.ts} +0 -0
- /package/dist/{web/plugin/scan-widgets.test.d.ts → cli/tunnel-handler.test.d.ts} +0 -0
- /package/dist/{web/plugin/validate-widget.test.d.ts → cli/tunnel.test.d.ts} +0 -0
- /package/dist/web/plugin/{validate-widget.d.ts → validate-view.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":"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","sourcesContent":["import { EventEmitter } from \"node:events\";\nimport http from \"node:http\";\nimport { Readable } from \"node:stream\";\nimport express from \"express\";\nimport { afterEach, describe, expect, it, vi } from \"vitest\";\nimport { startTunnelControlServer } from \"../cli/tunnel-control-server.js\";\nimport { createTunnelProxyRouter } from \"./tunnel-proxy-router.js\";\n\ntype FakeChild = EventEmitter & {\n stdout: Readable;\n stderr: Readable;\n kill: ReturnType<typeof vi.fn<() => boolean>>;\n};\n\nfunction makeFakeChild(): FakeChild {\n const child = new EventEmitter() as FakeChild;\n child.stdout = new Readable({ read() {} });\n child.stderr = new Readable({ read() {} });\n child.kill = vi.fn<() => boolean>(() => true);\n return child;\n}\n\nasync function listen(handler: http.RequestListener) {\n const server = http.createServer(handler);\n await new Promise<void>((resolve) => server.listen(0, \"127.0.0.1\", resolve));\n const port = (server.address() as { port: number }).port;\n return { port, server };\n}\n\ntype Cleanup = () => Promise<void> | void;\nconst cleanups: Cleanup[] = [];\n\nafterEach(async () => {\n while (cleanups.length > 0) {\n const cleanup = cleanups.pop();\n if (cleanup) {\n await cleanup();\n }\n }\n});\n\nasync function startProxy(controlPort: number) {\n const app = express();\n app.use(createTunnelProxyRouter(controlPort));\n const { port, server } = await listen(app);\n cleanups.push(\n () =>\n new Promise<void>((resolve) => {\n server.closeAllConnections?.();\n server.close(() => resolve());\n }),\n );\n return { port, server };\n}\n\nasync function startControl() {\n const child = makeFakeChild();\n const control = await startTunnelControlServer(() => 3000, {\n spawn: () => child,\n });\n cleanups.push(() => control.close());\n return { control, child };\n}\n\ndescribe(\"createTunnelProxyRouter\", () => {\n describe(\"POST /__skybridge/tunnel\", () => {\n it(\"forwards to upstream and returns the upstream JSON\", async () => {\n const { control } = await startControl();\n const { port } = await startProxy(control.port);\n\n const res = await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel`, {\n method: \"POST\",\n });\n\n expect(res.status).toBe(200);\n expect(res.headers.get(\"content-type\")).toMatch(/application\\/json/);\n expect(await res.json()).toEqual({\n status: \"starting\",\n message: \"Starting tunnel…\",\n });\n expect(control.manager.getState().status).toBe(\"starting\");\n });\n\n it(\"returns 502 when upstream is unavailable\", async () => {\n // Pick a port nothing is listening on by starting+stopping a server.\n const probe = http.createServer();\n await new Promise<void>((resolve) =>\n probe.listen(0, \"127.0.0.1\", resolve),\n );\n const deadPort = (probe.address() as { port: number }).port;\n await new Promise<void>((resolve) => probe.close(() => resolve()));\n\n const { port } = await startProxy(deadPort);\n\n const res = await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel`, {\n method: \"POST\",\n });\n expect(res.status).toBe(502);\n const body = (await res.json()) as { status: string; message: string };\n expect(body.status).toBe(\"error\");\n expect(typeof body.message).toBe(\"string\");\n });\n });\n\n describe(\"DELETE /__skybridge/tunnel\", () => {\n it(\"forwards to upstream and returns the upstream JSON\", async () => {\n const { control, child } = await startControl();\n const { port } = await startProxy(control.port);\n\n // First start the tunnel so DELETE has something to stop.\n await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel`, {\n method: \"POST\",\n });\n\n const res = await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel`, {\n method: \"DELETE\",\n });\n\n expect(res.status).toBe(200);\n expect(await res.json()).toEqual({ status: \"idle\" });\n expect(child.kill).toHaveBeenCalled();\n });\n\n it(\"returns 502 when upstream is unavailable\", async () => {\n const probe = http.createServer();\n await new Promise<void>((resolve) =>\n probe.listen(0, \"127.0.0.1\", resolve),\n );\n const deadPort = (probe.address() as { port: number }).port;\n await new Promise<void>((resolve) => probe.close(() => resolve()));\n\n const { port } = await startProxy(deadPort);\n\n const res = await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel`, {\n method: \"DELETE\",\n });\n expect(res.status).toBe(502);\n });\n });\n\n describe(\"GET /__skybridge/tunnel/events\", () => {\n it(\"pipes the upstream SSE stream through to the client\", async () => {\n const { control, child } = await startControl();\n const { port } = await startProxy(control.port);\n\n // Get the manager into a known state so the initial SSE frame is\n // deterministic.\n await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel`, {\n method: \"POST\",\n });\n child.stdout.emit(\n \"data\",\n Buffer.from(\n \"Forwarding: https://abc.tunnel.example -> http://localhost:3000\\n\",\n ),\n );\n\n const res = await fetch(\n `http://127.0.0.1:${port}/__skybridge/tunnel/events`,\n );\n\n expect(res.status).toBe(200);\n expect(res.headers.get(\"content-type\")).toMatch(/text\\/event-stream/);\n expect(res.headers.get(\"cache-control\")).toMatch(/no-cache/);\n\n const body = res.body;\n if (!body) {\n throw new Error(\"expected response body\");\n }\n const reader = body.getReader();\n const { value } = await reader.read();\n const chunk = new TextDecoder().decode(value);\n\n expect(chunk).toContain(\"event: state\");\n expect(chunk).toContain('\"status\":\"connected\"');\n expect(chunk).toContain('\"url\":\"https://abc.tunnel.example\"');\n\n await reader.cancel();\n });\n\n it(\"forwards subsequent state changes through the SSE stream\", async () => {\n const { control, child } = await startControl();\n const { port } = await startProxy(control.port);\n\n await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel`, {\n method: \"POST\",\n });\n\n const res = await fetch(\n `http://127.0.0.1:${port}/__skybridge/tunnel/events`,\n );\n const body = res.body;\n if (!body) {\n throw new Error(\"expected response body\");\n }\n const reader = body.getReader();\n const decoder = new TextDecoder();\n\n // Drain the initial \"starting\" frame.\n const first = await reader.read();\n expect(decoder.decode(first.value)).toContain('\"status\":\"starting\"');\n\n // Now drive a state change on the manager and read the next frame.\n child.stdout.emit(\n \"data\",\n Buffer.from(\n \"Forwarding: https://abc.tunnel.example -> http://localhost:3000\\n\",\n ),\n );\n\n let combined = \"\";\n // Reads may chunk arbitrarily, so accumulate until we see the connected\n // event or hit a sane cap.\n for (let i = 0; i < 5; i++) {\n const { value, done } = await reader.read();\n if (done) {\n break;\n }\n combined += decoder.decode(value);\n if (combined.includes('\"status\":\"connected\"')) {\n break;\n }\n }\n expect(combined).toContain('\"status\":\"connected\"');\n expect(combined).toContain('\"url\":\"https://abc.tunnel.example\"');\n\n await reader.cancel();\n });\n\n it(\"returns 502 when upstream is unavailable\", async () => {\n const probe = http.createServer();\n await new Promise<void>((resolve) =>\n probe.listen(0, \"127.0.0.1\", resolve),\n );\n const deadPort = (probe.address() as { port: number }).port;\n await new Promise<void>((resolve) => probe.close(() => resolve()));\n\n const { port } = await startProxy(deadPort);\n\n const res = await fetch(\n `http://127.0.0.1:${port}/__skybridge/tunnel/events`,\n );\n expect(res.status).toBe(502);\n const body = (await res.json()) as { status: string };\n expect(body.status).toBe(\"error\");\n });\n\n it(\"aborts the upstream connection when the proxy server is closed mid-stream\", async () => {\n const { control } = await startControl();\n const { port, server } = await startProxy(control.port);\n\n await fetch(`http://127.0.0.1:${port}/__skybridge/tunnel`, {\n method: \"POST\",\n });\n\n // Snapshot the manager's listener counts before the SSE subscription so\n // we can verify the proxy disconnected from upstream after shutdown.\n const baseStateListeners = control.manager.listenerCount(\"state\");\n const baseActivityListeners = control.manager.listenerCount(\"activity\");\n\n const res = await fetch(\n `http://127.0.0.1:${port}/__skybridge/tunnel/events`,\n );\n const body = res.body;\n if (!body) {\n throw new Error(\"expected response body\");\n }\n const reader = body.getReader();\n // Drain the first frame to confirm the stream is live and the upstream\n // SSE handler has subscribed to the manager.\n await reader.read();\n expect(control.manager.listenerCount(\"state\")).toBe(\n baseStateListeners + 1,\n );\n\n // Close the proxy server, destroying in-flight responses. The proxy's\n // req.on(\"close\", ...) should fire and abort the upstream fetch.\n server.closeAllConnections?.();\n await new Promise<void>((resolve) => server.close(() => resolve()));\n\n // The client-side stream is dead — drain or fail, either is acceptable.\n try {\n while (true) {\n const { done } = await reader.read();\n if (done) {\n break;\n }\n }\n } catch {\n // expected: socket terminated when proxy server was destroyed\n }\n\n // Wait briefly for the upstream's req.on(\"close\") to fire, then assert\n // the manager listeners were detached. This is the load-bearing\n // verification: it proves the proxy's AbortController propagated and\n // upstream cleaned up its SSE subscription.\n const start = Date.now();\n while (\n control.manager.listenerCount(\"state\") > baseStateListeners &&\n Date.now() - start < 1000\n ) {\n await new Promise((r) => setTimeout(r, 20));\n }\n expect(control.manager.listenerCount(\"state\")).toBe(baseStateListeners);\n expect(control.manager.listenerCount(\"activity\")).toBe(\n baseActivityListeners,\n );\n });\n });\n});\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type http from "node:http";
|
|
2
|
+
import { type Router } from "express";
|
|
3
|
+
/**
|
|
4
|
+
* Vite dev-server middleware for view assets.
|
|
5
|
+
*
|
|
6
|
+
* MUST be mounted at the Express app root so Vite can intercept
|
|
7
|
+
* `/@vite/client`, `/@react-refresh`, and `/_skybridge/view/...` imports:
|
|
8
|
+
*
|
|
9
|
+
* const app = express();
|
|
10
|
+
* if (env.NODE_ENV !== "production") {
|
|
11
|
+
* app.use(await viewsDevServer(httpServer));
|
|
12
|
+
* }
|
|
13
|
+
*/
|
|
14
|
+
export declare const viewsDevServer: (httpServer: http.Server) => Promise<Router>;
|
|
@@ -3,17 +3,17 @@ import cors from "cors";
|
|
|
3
3
|
import express, {} from "express";
|
|
4
4
|
import { assetBaseUrlTransformPlugin } from "./asset-base-url-transform-plugin.js";
|
|
5
5
|
/**
|
|
6
|
-
* Vite dev-server middleware for
|
|
6
|
+
* Vite dev-server middleware for view assets.
|
|
7
7
|
*
|
|
8
8
|
* MUST be mounted at the Express app root so Vite can intercept
|
|
9
|
-
* `/@vite/client`, `/@react-refresh`, and `/_skybridge/
|
|
9
|
+
* `/@vite/client`, `/@react-refresh`, and `/_skybridge/view/...` imports:
|
|
10
10
|
*
|
|
11
11
|
* const app = express();
|
|
12
12
|
* if (env.NODE_ENV !== "production") {
|
|
13
|
-
* app.use(await
|
|
13
|
+
* app.use(await viewsDevServer(httpServer));
|
|
14
14
|
* }
|
|
15
15
|
*/
|
|
16
|
-
export const
|
|
16
|
+
export const viewsDevServer = async (httpServer) => {
|
|
17
17
|
const router = express.Router();
|
|
18
18
|
const { createServer, loadConfigFromFile } = await import("vite");
|
|
19
19
|
const root = process.cwd();
|
|
@@ -35,11 +35,11 @@ export const widgetsDevServer = async (httpServer) => {
|
|
|
35
35
|
},
|
|
36
36
|
root,
|
|
37
37
|
// optimizeDeps is set by the skybridge Vite plugin (entries + include)
|
|
38
|
-
// so it can derive the
|
|
38
|
+
// so it can derive the view glob from `viewsDir`.
|
|
39
39
|
plugins: [...userPlugins, assetBaseUrlTransformPlugin()],
|
|
40
40
|
});
|
|
41
41
|
router.use(cors());
|
|
42
42
|
router.use("/", vite.middlewares);
|
|
43
43
|
return router;
|
|
44
44
|
};
|
|
45
|
-
//# sourceMappingURL=
|
|
45
|
+
//# sourceMappingURL=viewsDevServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewsDevServer.js","sourceRoot":"","sources":["../../src/server/viewsDevServer.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,OAAO,EAAE,EAAe,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AAEnF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EACjC,UAAuB,EACN,EAAE;IACnB,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAEhC,MAAM,EAAE,YAAY,EAAE,kBAAkB,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;IAElE,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC3B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IAErD,MAAM,YAAY,GAAG,MAAM,kBAAkB,CAC3C,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,EACzC,UAAU,EACV,IAAI,CACL,CAAC;IAEF,MAAM,EACJ,KAAK,EACL,OAAO,EACP,OAAO,EAAE,WAAW,GAAG,EAAE,EACzB,GAAG,SAAS,EACb,GAAG,YAAY,EAAE,MAAM,IAAI,EAAE,CAAC;IAE/B,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC;QAC9B,GAAG,SAAS;QACZ,qEAAqE;QACrE,6CAA6C;QAC7C,UAAU,EAAE,KAAK;QACjB,OAAO,EAAE,QAAQ;QACjB,MAAM,EAAE;YACN,YAAY,EAAE,IAAI;YAClB,cAAc,EAAE,IAAI;YACpB,GAAG,EAAE;gBACH,MAAM,EAAE,UAAU;aACnB;SACF;QACD,IAAI;QACJ,uEAAuE;QACvE,kDAAkD;QAClD,OAAO,EAAE,CAAC,GAAG,WAAW,EAAE,2BAA2B,EAAE,CAAC;KACzD,CAAC,CAAC;IAEH,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IACnB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IAElC,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC","sourcesContent":["import type http from \"node:http\";\nimport path from \"node:path\";\nimport cors from \"cors\";\nimport express, { type Router } from \"express\";\nimport { assetBaseUrlTransformPlugin } from \"./asset-base-url-transform-plugin.js\";\n\n/**\n * Vite dev-server middleware for view assets.\n *\n * MUST be mounted at the Express app root so Vite can intercept\n * `/@vite/client`, `/@react-refresh`, and `/_skybridge/view/...` imports:\n *\n * const app = express();\n * if (env.NODE_ENV !== \"production\") {\n * app.use(await viewsDevServer(httpServer));\n * }\n */\nexport const viewsDevServer = async (\n httpServer: http.Server,\n): Promise<Router> => {\n const router = express.Router();\n\n const { createServer, loadConfigFromFile } = await import(\"vite\");\n\n const root = process.cwd();\n const configFile = path.join(root, \"vite.config.ts\");\n\n const configResult = await loadConfigFromFile(\n { command: \"serve\", mode: \"development\" },\n configFile,\n root,\n );\n\n const {\n build,\n preview,\n plugins: userPlugins = [],\n ...devConfig\n } = configResult?.config || {};\n\n const vite = await createServer({\n ...devConfig,\n // Pass `false` so Vite skips re-resolving a config file — we already\n // loaded and spread the user's config above.\n configFile: false,\n appType: \"custom\",\n server: {\n allowedHosts: true,\n middlewareMode: true,\n hmr: {\n server: httpServer,\n },\n },\n root,\n // optimizeDeps is set by the skybridge Vite plugin (entries + include)\n // so it can derive the view glob from `viewsDir`.\n plugins: [...userPlugins, assetBaseUrlTransformPlugin()],\n });\n\n router.use(cors());\n router.use(\"/\", vite.middlewares);\n\n return router;\n};\n"]}
|
package/dist/test/utils.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export declare function createMockMcpServer(): {
|
|
|
6
6
|
mockRegisterTool: MockInstance;
|
|
7
7
|
};
|
|
8
8
|
export declare function createTestServer(): McpServer<Record<never, import("../server/server.js").ToolDef<unknown, unknown, unknown>> & {
|
|
9
|
-
"search-
|
|
9
|
+
"search-trip": import("../server/server.js").ToolDef<{
|
|
10
10
|
destination: string;
|
|
11
11
|
departureDate?: string | undefined;
|
|
12
12
|
maxPrice?: number | undefined;
|
|
@@ -27,9 +27,9 @@ export declare function createTestServer(): McpServer<Record<never, import("../s
|
|
|
27
27
|
images: string[];
|
|
28
28
|
}, unknown>;
|
|
29
29
|
} & {
|
|
30
|
-
"no-input-
|
|
30
|
+
"no-input-view": import("../server/server.js").ToolDef<{}, {}, unknown>;
|
|
31
31
|
} & {
|
|
32
|
-
"inferred-output-
|
|
32
|
+
"inferred-output-view": import("../server/server.js").ToolDef<{
|
|
33
33
|
query: string;
|
|
34
34
|
}, {
|
|
35
35
|
inferredResults: {
|
|
@@ -57,7 +57,7 @@ export declare function createTestServer(): McpServer<Record<never, import("../s
|
|
|
57
57
|
fetchedAt: string;
|
|
58
58
|
}, unknown>;
|
|
59
59
|
} & {
|
|
60
|
-
"
|
|
60
|
+
"view-with-metadata": import("../server/server.js").ToolDef<{
|
|
61
61
|
resourceId: string;
|
|
62
62
|
}, {
|
|
63
63
|
data: {
|
|
@@ -79,7 +79,7 @@ export declare function createTestServer(): McpServer<Record<never, import("../s
|
|
|
79
79
|
source: string;
|
|
80
80
|
}>;
|
|
81
81
|
} & {
|
|
82
|
-
"
|
|
82
|
+
"view-with-mixed-returns": import("../server/server.js").ToolDef<{
|
|
83
83
|
shouldSucceed: boolean;
|
|
84
84
|
}, {
|
|
85
85
|
error: string;
|
|
@@ -93,7 +93,7 @@ export declare function createTestServer(): McpServer<Record<never, import("../s
|
|
|
93
93
|
}>;
|
|
94
94
|
}>;
|
|
95
95
|
export declare function createMinimalTestServer(): McpServer<Record<never, import("../server/server.js").ToolDef<unknown, unknown, unknown>> & {
|
|
96
|
-
"search-
|
|
96
|
+
"search-trip": import("../server/server.js").ToolDef<{
|
|
97
97
|
destination: string;
|
|
98
98
|
}, {
|
|
99
99
|
results: {
|
|
@@ -102,7 +102,7 @@ export declare function createMinimalTestServer(): McpServer<Record<never, impor
|
|
|
102
102
|
}, unknown>;
|
|
103
103
|
}>;
|
|
104
104
|
export declare function createInterfaceTestServer(): McpServer<Record<never, import("../server/server.js").ToolDef<unknown, unknown, unknown>> & {
|
|
105
|
-
"interface-
|
|
105
|
+
"interface-view": import("../server/server.js").ToolDef<{
|
|
106
106
|
id: string;
|
|
107
107
|
}, {
|
|
108
108
|
itemName: string;
|
package/dist/test/utils.js
CHANGED
|
@@ -18,8 +18,8 @@ export function createMockMcpServer() {
|
|
|
18
18
|
export function createTestServer() {
|
|
19
19
|
return new McpServer({ name: "test-app", version: "1.0.0" }, {})
|
|
20
20
|
.registerTool({
|
|
21
|
-
name: "search-
|
|
22
|
-
description: "Search for
|
|
21
|
+
name: "search-trip",
|
|
22
|
+
description: "Search for trips",
|
|
23
23
|
inputSchema: {
|
|
24
24
|
destination: z.string(),
|
|
25
25
|
departureDate: z.string().optional(),
|
|
@@ -33,7 +33,7 @@ export function createTestServer() {
|
|
|
33
33
|
})),
|
|
34
34
|
totalCount: z.number(),
|
|
35
35
|
},
|
|
36
|
-
view: { component: "search-
|
|
36
|
+
view: { component: "search-trip" },
|
|
37
37
|
}, async ({ destination }) => {
|
|
38
38
|
return {
|
|
39
39
|
content: [{ type: "text", text: `Found trips to ${destination}` }],
|
|
@@ -66,11 +66,11 @@ export function createTestServer() {
|
|
|
66
66
|
};
|
|
67
67
|
})
|
|
68
68
|
.registerTool({
|
|
69
|
-
name: "no-input-
|
|
70
|
-
description: "
|
|
69
|
+
name: "no-input-view",
|
|
70
|
+
description: "View with no input",
|
|
71
71
|
inputSchema: {},
|
|
72
72
|
outputSchema: {},
|
|
73
|
-
view: { component: "no-input-
|
|
73
|
+
view: { component: "no-input-view" },
|
|
74
74
|
}, async () => {
|
|
75
75
|
return {
|
|
76
76
|
content: [{ type: "text", text: "No input needed" }],
|
|
@@ -78,12 +78,12 @@ export function createTestServer() {
|
|
|
78
78
|
};
|
|
79
79
|
})
|
|
80
80
|
.registerTool({
|
|
81
|
-
name: "inferred-output-
|
|
82
|
-
description: "
|
|
81
|
+
name: "inferred-output-view",
|
|
82
|
+
description: "View with output inferred from callback",
|
|
83
83
|
inputSchema: {
|
|
84
84
|
query: z.string(),
|
|
85
85
|
},
|
|
86
|
-
view: { component: "inferred-output-
|
|
86
|
+
view: { component: "inferred-output-view" },
|
|
87
87
|
}, async ({ query }) => {
|
|
88
88
|
return {
|
|
89
89
|
content: [{ type: "text", text: `Query: ${query}` }],
|
|
@@ -129,12 +129,12 @@ export function createTestServer() {
|
|
|
129
129
|
};
|
|
130
130
|
})
|
|
131
131
|
.registerTool({
|
|
132
|
-
name: "
|
|
133
|
-
description: "
|
|
132
|
+
name: "view-with-metadata",
|
|
133
|
+
description: "View that returns response metadata",
|
|
134
134
|
inputSchema: {
|
|
135
135
|
resourceId: z.string(),
|
|
136
136
|
},
|
|
137
|
-
view: { component: "
|
|
137
|
+
view: { component: "view-with-metadata" },
|
|
138
138
|
}, async ({ resourceId }) => {
|
|
139
139
|
return {
|
|
140
140
|
content: [{ type: "text", text: `Resource: ${resourceId}` }],
|
|
@@ -167,12 +167,12 @@ export function createTestServer() {
|
|
|
167
167
|
};
|
|
168
168
|
})
|
|
169
169
|
.registerTool({
|
|
170
|
-
name: "
|
|
171
|
-
description: "
|
|
170
|
+
name: "view-with-mixed-returns",
|
|
171
|
+
description: "View with mixed return paths (some with _meta, some without)",
|
|
172
172
|
inputSchema: {
|
|
173
173
|
shouldSucceed: z.boolean(),
|
|
174
174
|
},
|
|
175
|
-
view: { component: "
|
|
175
|
+
view: { component: "view-with-mixed-returns" },
|
|
176
176
|
}, async ({ shouldSucceed }) => {
|
|
177
177
|
if (!shouldSucceed) {
|
|
178
178
|
return {
|
|
@@ -192,15 +192,15 @@ export function createTestServer() {
|
|
|
192
192
|
}
|
|
193
193
|
export function createMinimalTestServer() {
|
|
194
194
|
return new McpServer({ name: "test-app", version: "1.0.0" }, {}).registerTool({
|
|
195
|
-
name: "search-
|
|
196
|
-
description: "Search for
|
|
195
|
+
name: "search-trip",
|
|
196
|
+
description: "Search for trips",
|
|
197
197
|
inputSchema: {
|
|
198
198
|
destination: z.string(),
|
|
199
199
|
},
|
|
200
200
|
outputSchema: {
|
|
201
201
|
results: z.array(z.object({ id: z.string() })),
|
|
202
202
|
},
|
|
203
|
-
view: { component: "search-
|
|
203
|
+
view: { component: "search-trip" },
|
|
204
204
|
}, async ({ destination }) => {
|
|
205
205
|
return {
|
|
206
206
|
content: [{ type: "text", text: `Found trips to ${destination}` }],
|
|
@@ -210,12 +210,12 @@ export function createMinimalTestServer() {
|
|
|
210
210
|
}
|
|
211
211
|
export function createInterfaceTestServer() {
|
|
212
212
|
return new McpServer({ name: "interface-test-app", version: "1.0.0" }, {}).registerTool({
|
|
213
|
-
name: "interface-
|
|
214
|
-
description: "
|
|
213
|
+
name: "interface-view",
|
|
214
|
+
description: "View with interface-typed output",
|
|
215
215
|
inputSchema: {
|
|
216
216
|
id: z.string(),
|
|
217
217
|
},
|
|
218
|
-
view: { component: "interface-
|
|
218
|
+
view: { component: "interface-view" },
|
|
219
219
|
}, async ({ id }) => {
|
|
220
220
|
return {
|
|
221
221
|
content: [{ type: "text", text: `Item ${id}` }],
|
package/dist/test/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/test/utils.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/test/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { McpServer as McpServerBase } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { type MockInstance, vi } from \"vitest\";\nimport * as z from \"zod\";\nimport { McpServer, type ViewName } from \"../server/server.js\";\n\nexport function createMockMcpServer(): {\n server: McpServer;\n mockRegisterResource: MockInstance<McpServer[\"registerResource\"]>;\n mockRegisterTool: MockInstance;\n} {\n const server = new McpServer(\n {\n name: \"alpic-openai-app\",\n version: \"0.0.1\",\n },\n { capabilities: {} },\n );\n\n const mockRegisterResource = vi.spyOn(server, \"registerResource\");\n const mockRegisterTool = vi.spyOn(McpServerBase.prototype, \"registerTool\");\n\n return {\n server,\n mockRegisterResource,\n mockRegisterTool,\n };\n}\n\nexport function createTestServer() {\n return new McpServer({ name: \"test-app\", version: \"1.0.0\" }, {})\n .registerTool(\n {\n name: \"search-trip\",\n description: \"Search for trips\",\n inputSchema: {\n destination: z.string(),\n departureDate: z.string().optional(),\n maxPrice: z.number().optional(),\n },\n outputSchema: {\n results: z.array(\n z.object({\n id: z.string(),\n name: z.string(),\n price: z.number(),\n }),\n ),\n totalCount: z.number(),\n },\n view: { component: \"search-trip\" as ViewName },\n },\n async ({ destination }) => {\n return {\n content: [{ type: \"text\", text: `Found trips to ${destination}` }],\n structuredContent: {\n results: [{ id: \"1\", name: \"Trip\", price: 1000 }],\n totalCount: 1,\n },\n };\n },\n )\n .registerTool(\n {\n name: \"get-trip-details\",\n description: \"Get trip details\",\n inputSchema: {\n tripId: z.string(),\n },\n outputSchema: {\n name: z.string(),\n description: z.string(),\n images: z.array(z.string()),\n },\n view: { component: \"get-trip-details\" as ViewName },\n },\n async ({ tripId }) => {\n return {\n content: [{ type: \"text\", text: `Details for ${tripId}` }],\n structuredContent: {\n name: \"Trip\",\n description: \"A great trip\",\n images: [\"image1.jpg\"],\n },\n };\n },\n )\n .registerTool(\n {\n name: \"no-input-view\",\n description: \"View with no input\",\n inputSchema: {},\n outputSchema: {},\n view: { component: \"no-input-view\" as ViewName },\n },\n async () => {\n return {\n content: [{ type: \"text\", text: \"No input needed\" }],\n structuredContent: {},\n };\n },\n )\n .registerTool(\n {\n name: \"inferred-output-view\",\n description: \"View with output inferred from callback\",\n inputSchema: {\n query: z.string(),\n },\n view: { component: \"inferred-output-view\" as ViewName },\n },\n async ({ query }) => {\n return {\n content: [{ type: \"text\", text: `Query: ${query}` }],\n structuredContent: {\n inferredResults: [{ id: \"inferred-1\", score: 0.95 }],\n inferredCount: 1,\n },\n };\n },\n )\n .registerTool(\n {\n name: \"calculate-price\",\n description: \"Calculate trip price\",\n inputSchema: {\n tripId: z.string(),\n passengers: z.number(),\n },\n outputSchema: {\n totalPrice: z.number(),\n currency: z.string(),\n },\n },\n async ({ tripId, passengers }) => {\n return {\n content: [{ type: \"text\", text: `Price for ${tripId}` }],\n structuredContent: {\n totalPrice: 1000 * passengers,\n currency: \"USD\",\n },\n };\n },\n )\n .registerTool(\n {\n name: \"inferred-tool\",\n description: \"Tool with output inferred from callback\",\n inputSchema: {\n itemId: z.string(),\n },\n },\n async ({ itemId }) => {\n return {\n content: [{ type: \"text\", text: `Item: ${itemId}` }],\n structuredContent: {\n itemDetails: { name: \"Inferred Item\", available: true },\n fetchedAt: \"2024-01-01\",\n },\n };\n },\n )\n .registerTool(\n {\n name: \"view-with-metadata\",\n description: \"View that returns response metadata\",\n inputSchema: {\n resourceId: z.string(),\n },\n view: { component: \"view-with-metadata\" as ViewName },\n },\n async ({ resourceId }) => {\n return {\n content: [{ type: \"text\", text: `Resource: ${resourceId}` }],\n structuredContent: {\n data: { id: resourceId, loaded: true },\n },\n _meta: {\n requestId: \"req-123\",\n timestamp: 1704067200000,\n cached: false,\n },\n };\n },\n )\n .registerTool(\n {\n name: \"tool-with-metadata\",\n description: \"Tool that returns response metadata\",\n inputSchema: {\n query: z.string(),\n },\n },\n async ({ query }) => {\n return {\n content: [{ type: \"text\", text: `Query: ${query}` }],\n structuredContent: {\n results: [query],\n },\n _meta: {\n executionTime: 150,\n source: \"cache\",\n },\n };\n },\n )\n .registerTool(\n {\n name: \"view-with-mixed-returns\",\n description:\n \"View with mixed return paths (some with _meta, some without)\",\n inputSchema: {\n shouldSucceed: z.boolean(),\n },\n view: { component: \"view-with-mixed-returns\" as ViewName },\n },\n async ({ shouldSucceed }) => {\n if (!shouldSucceed) {\n return {\n content: [{ type: \"text\", text: \"Error occurred\" }],\n structuredContent: { error: \"Something went wrong\" },\n };\n }\n return {\n content: [{ type: \"text\", text: \"Success\" }],\n structuredContent: { data: \"result\" },\n _meta: {\n processedAt: 1704067200000,\n region: \"eu-west-1\",\n },\n };\n },\n );\n}\n\nexport function createMinimalTestServer() {\n return new McpServer({ name: \"test-app\", version: \"1.0.0\" }, {}).registerTool(\n {\n name: \"search-trip\",\n description: \"Search for trips\",\n inputSchema: {\n destination: z.string(),\n },\n outputSchema: {\n results: z.array(z.object({ id: z.string() })),\n },\n view: { component: \"search-trip\" as ViewName },\n },\n async ({ destination }) => {\n return {\n content: [{ type: \"text\", text: `Found trips to ${destination}` }],\n structuredContent: { results: [{ id: \"1\" }] },\n };\n },\n );\n}\n\ninterface InterfaceOutput {\n itemName: string;\n quantity: number;\n}\n\ninterface InterfaceMeta {\n processedBy: string;\n version: number;\n}\n\ninterface InterfaceReturnType {\n content: [{ type: \"text\"; text: string }];\n structuredContent: InterfaceOutput;\n _meta: InterfaceMeta;\n}\n\nexport function createInterfaceTestServer() {\n return new McpServer(\n { name: \"interface-test-app\", version: \"1.0.0\" },\n {},\n ).registerTool(\n {\n name: \"interface-view\" as const,\n description: \"View with interface-typed output\",\n inputSchema: {\n id: z.string(),\n },\n view: { component: \"interface-view\" as ViewName },\n },\n async ({ id }): Promise<InterfaceReturnType> => {\n return {\n content: [{ type: \"text\", text: `Item ${id}` }],\n structuredContent: {\n itemName: \"Test Item\",\n quantity: 42,\n },\n _meta: {\n processedBy: \"test\",\n version: 1,\n },\n };\n },\n );\n}\n\nexport function createMockExtra(\n host: string,\n options?: {\n headers?: Record<string, string | string[]>;\n url?: URL | string;\n },\n) {\n return {\n requestInfo: {\n headers: { host, ...(options?.headers ?? {}) },\n ...(options?.url ? { url: options.url } : {}),\n },\n };\n}\n\nexport function setTestEnv(env: Record<string, string>) {\n Object.assign(process.env, env);\n}\n\nexport function resetTestEnv() {\n delete process.env.NODE_ENV;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|