skybridge 0.0.0-dev.fe23f20 → 0.0.0-dev.fe35f75
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 +153 -0
- package/bin/run.js +0 -4
- package/dist/cli/detect-port.d.ts +18 -0
- package/dist/cli/detect-port.js +61 -0
- package/dist/cli/detect-port.js.map +1 -0
- package/dist/cli/header.js +1 -1
- package/dist/cli/header.js.map +1 -1
- package/dist/cli/run-command.js.map +1 -1
- package/dist/cli/telemetry.d.ts +7 -0
- package/dist/cli/telemetry.js +123 -0
- package/dist/cli/telemetry.js.map +1 -0
- 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.d.ts +1 -0
- package/dist/cli/tunnel.test.js +190 -0
- package/dist/cli/tunnel.test.js.map +1 -0
- package/dist/cli/types.d.ts +5 -0
- package/dist/cli/types.js +2 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli/use-execute-steps.d.ts +3 -2
- package/dist/cli/use-execute-steps.js +6 -1
- package/dist/cli/use-execute-steps.js.map +1 -1
- package/dist/cli/use-messages.d.ts +3 -0
- package/dist/cli/use-messages.js +11 -0
- package/dist/cli/use-messages.js.map +1 -0
- package/dist/cli/use-nodemon.d.ts +2 -0
- package/dist/cli/use-nodemon.js +73 -0
- package/dist/cli/use-nodemon.js.map +1 -0
- 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-tunnel.d.ts +14 -0
- package/dist/cli/use-tunnel.js +131 -0
- package/dist/cli/use-tunnel.js.map +1 -0
- package/dist/cli/use-typescript-check.d.ts +9 -0
- package/dist/cli/use-typescript-check.js +94 -0
- package/dist/cli/use-typescript-check.js.map +1 -0
- package/dist/commands/build.js +64 -6
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/dev.d.ts +6 -1
- package/dist/commands/dev.js +69 -9
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/start.d.ts +3 -1
- package/dist/commands/start.js +31 -15
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/telemetry/disable.d.ts +5 -0
- package/dist/commands/telemetry/disable.js +14 -0
- package/dist/commands/telemetry/disable.js.map +1 -0
- package/dist/commands/telemetry/enable.d.ts +5 -0
- package/dist/commands/telemetry/enable.js +14 -0
- package/dist/commands/telemetry/enable.js.map +1 -0
- package/dist/commands/telemetry/status.d.ts +5 -0
- package/dist/commands/telemetry/status.js +14 -0
- package/dist/commands/telemetry/status.js.map +1 -0
- package/dist/server/asset-base-url-transform-plugin.d.ts +10 -0
- package/dist/server/asset-base-url-transform-plugin.js +33 -0
- package/dist/server/asset-base-url-transform-plugin.js.map +1 -0
- package/dist/server/asset-base-url-transform-plugin.test.d.ts +1 -0
- package/dist/server/asset-base-url-transform-plugin.test.js +84 -0
- package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -0
- package/dist/server/content-helpers.d.ts +27 -0
- package/dist/server/content-helpers.js +46 -0
- package/dist/server/content-helpers.js.map +1 -0
- package/dist/server/content-helpers.test.d.ts +1 -0
- package/dist/server/content-helpers.test.js +70 -0
- package/dist/server/content-helpers.test.js.map +1 -0
- package/dist/server/express.d.ts +11 -0
- package/dist/server/express.js +101 -0
- package/dist/server/express.js.map +1 -0
- package/dist/server/express.test.d.ts +1 -0
- package/dist/server/express.test.js +430 -0
- package/dist/server/express.test.js.map +1 -0
- package/dist/server/index.d.ts +5 -3
- package/dist/server/index.js +3 -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.d.ts +14 -0
- package/dist/server/metric.js +62 -0
- package/dist/server/metric.js.map +1 -0
- package/dist/server/middleware.d.ts +124 -0
- package/dist/server/middleware.js +93 -0
- package/dist/server/middleware.js.map +1 -0
- package/dist/server/middleware.test-d.d.ts +1 -0
- package/dist/server/middleware.test-d.js +75 -0
- package/dist/server/middleware.test-d.js.map +1 -0
- package/dist/server/middleware.test.d.ts +1 -0
- package/dist/server/middleware.test.js +493 -0
- package/dist/server/middleware.test.js.map +1 -0
- package/dist/server/server.d.ts +160 -63
- package/dist/server/server.js +393 -63
- 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/viewsDevServer.js +45 -0
- package/dist/server/viewsDevServer.js.map +1 -0
- package/dist/test/utils.d.ts +13 -21
- package/dist/test/utils.js +42 -37
- package/dist/test/utils.js.map +1 -1
- package/dist/test/view.test.d.ts +1 -0
- package/dist/test/view.test.js +523 -0
- package/dist/test/view.test.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/web/bridges/apps-sdk/adaptor.d.ts +18 -6
- package/dist/web/bridges/apps-sdk/adaptor.js +71 -8
- package/dist/web/bridges/apps-sdk/adaptor.js.map +1 -1
- package/dist/web/bridges/apps-sdk/bridge.d.ts +1 -1
- package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
- package/dist/web/bridges/apps-sdk/index.d.ts +1 -1
- package/dist/web/bridges/apps-sdk/index.js.map +1 -1
- package/dist/web/bridges/apps-sdk/types.d.ts +39 -27
- package/dist/web/bridges/apps-sdk/types.js.map +1 -1
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
- 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 +39 -8
- package/dist/web/bridges/mcp-app/adaptor.js +182 -56
- package/dist/web/bridges/mcp-app/adaptor.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.d.ts +13 -30
- package/dist/web/bridges/mcp-app/bridge.js +43 -196
- 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 +5 -3
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js +2 -2
- 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 +1 -41
- package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -1
- package/dist/web/bridges/types.d.ts +55 -12
- package/dist/web/bridges/types.js.map +1 -1
- package/dist/web/bridges/use-host-context.js.map +1 -1
- package/dist/web/components/modal-provider.d.ts +4 -0
- package/dist/web/components/modal-provider.js +45 -0
- package/dist/web/components/modal-provider.js.map +1 -0
- package/dist/web/create-store.js +17 -3
- package/dist/web/create-store.js.map +1 -1
- package/dist/web/create-store.test.js +23 -20
- package/dist/web/create-store.test.js.map +1 -1
- package/dist/web/data-llm.d.ts +1 -1
- package/dist/web/data-llm.js +3 -3
- package/dist/web/data-llm.js.map +1 -1
- package/dist/web/data-llm.test.js +33 -30
- package/dist/web/data-llm.test.js.map +1 -1
- package/dist/web/generate-helpers.d.ts +20 -18
- package/dist/web/generate-helpers.js +20 -18
- package/dist/web/generate-helpers.js.map +1 -1
- package/dist/web/generate-helpers.test-d.js +26 -26
- 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 -2
- package/dist/web/hooks/index.js +4 -1
- package/dist/web/hooks/index.js.map +1 -1
- package/dist/web/hooks/test/utils.js +4 -0
- package/dist/web/hooks/test/utils.js.map +1 -1
- 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 +0 -4
- package/dist/web/hooks/use-call-tool.test.js.map +1 -1
- package/dist/web/hooks/use-display-mode.d.ts +3 -3
- package/dist/web/hooks/use-display-mode.js.map +1 -1
- package/dist/web/hooks/use-display-mode.test-d.d.ts +1 -0
- package/dist/web/hooks/use-display-mode.test-d.js +8 -0
- package/dist/web/hooks/use-display-mode.test-d.js.map +1 -0
- package/dist/web/hooks/use-display-mode.test.js.map +1 -1
- package/dist/web/hooks/use-files.d.ts +3 -6
- package/dist/web/hooks/use-files.js +5 -2
- package/dist/web/hooks/use-files.js.map +1 -1
- package/dist/web/hooks/use-files.test.js +28 -3
- package/dist/web/hooks/use-files.test.js.map +1 -1
- package/dist/web/hooks/use-layout.d.ts +1 -1
- package/dist/web/hooks/use-layout.js.map +1 -1
- package/dist/web/hooks/use-layout.test.js +3 -3
- package/dist/web/hooks/use-layout.test.js.map +1 -1
- package/dist/web/hooks/use-open-external.d.ts +3 -1
- package/dist/web/hooks/use-open-external.js +1 -1
- package/dist/web/hooks/use-open-external.js.map +1 -1
- package/dist/web/hooks/use-open-external.test.js +26 -11
- package/dist/web/hooks/use-open-external.test.js.map +1 -1
- package/dist/web/hooks/use-request-close.d.ts +2 -0
- package/dist/web/hooks/use-request-close.js +8 -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 +3 -3
- package/dist/web/hooks/use-request-modal.js +10 -8
- package/dist/web/hooks/use-request-modal.js.map +1 -1
- package/dist/web/hooks/use-request-modal.test.js +5 -1
- package/dist/web/hooks/use-request-modal.test.js.map +1 -1
- package/dist/web/hooks/use-request-size.d.ts +3 -0
- package/dist/web/hooks/use-request-size.js +8 -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 +2 -1
- package/dist/web/hooks/use-send-follow-up-message.js +2 -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 +1 -0
- package/dist/web/hooks/use-set-open-in-app-url.js +8 -0
- package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -0
- package/dist/web/hooks/use-set-open-in-app-url.test.d.ts +1 -0
- package/dist/web/hooks/use-set-open-in-app-url.test.js +43 -0
- package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -0
- package/dist/web/hooks/use-tool-info.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test-d.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test.js +1 -1
- package/dist/web/hooks/use-tool-info.test.js.map +1 -1
- package/dist/web/hooks/use-user.js +18 -2
- package/dist/web/hooks/use-user.js.map +1 -1
- package/dist/web/hooks/use-user.test.js +29 -1
- package/dist/web/hooks/use-user.test.js.map +1 -1
- package/dist/web/hooks/use-view-state.d.ts +4 -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-view-state.test.js +177 -0
- package/dist/web/hooks/use-view-state.test.js.map +1 -0
- package/dist/web/index.d.ts +1 -2
- package/dist/web/index.js +1 -2
- package/dist/web/index.js.map +1 -1
- package/dist/web/mount-view.d.ts +1 -0
- package/dist/web/{mount-widget.js → mount-view.js} +11 -3
- 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 +4 -1
- package/dist/web/plugin/plugin.js +135 -18
- 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 +1 -1
- 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-view.d.ts +1 -0
- package/dist/web/plugin/validate-view.js +9 -0
- 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-view.test.js +24 -0
- package/dist/web/plugin/validate-view.test.js.map +1 -0
- package/dist/web/proxy.js +0 -1
- package/dist/web/proxy.js.map +1 -1
- package/dist/web/types.js.map +1 -1
- package/package.json +51 -30
- package/tsconfig.base.json +33 -0
- package/dist/server/templates/development.hbs +0 -66
- package/dist/server/templates/production.hbs +0 -7
- package/dist/server/widgetsDevServer.d.ts +0 -12
- package/dist/server/widgetsDevServer.js +0 -47
- package/dist/server/widgetsDevServer.js.map +0 -1
- package/dist/test/widget.test.js +0 -255
- 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 +0 -61
- 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/{test/widget.test.d.ts → cli/tunnel-control-server.test.d.ts} +0 -0
- /package/dist/{web/hooks/use-widget-state.test.d.ts → cli/tunnel-handler.test.d.ts} +0 -0
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ViewHostType } from "./server.js";
|
|
2
2
|
declare class TemplateHelper {
|
|
3
|
-
private templateCache;
|
|
4
|
-
private loadTemplate;
|
|
5
3
|
renderProduction(data: {
|
|
6
|
-
hostType:
|
|
4
|
+
hostType: ViewHostType;
|
|
7
5
|
serverUrl: string;
|
|
8
|
-
|
|
6
|
+
viewFile: string;
|
|
9
7
|
styleFile: string;
|
|
10
8
|
}): string;
|
|
11
9
|
renderDevelopment(data: {
|
|
12
|
-
hostType:
|
|
10
|
+
hostType: ViewHostType;
|
|
13
11
|
serverUrl: string;
|
|
14
|
-
|
|
12
|
+
viewName: string;
|
|
15
13
|
}): string;
|
|
16
14
|
}
|
|
17
15
|
export declare const templateHelper: TemplateHelper;
|
|
@@ -1,29 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
import Handlebars from "handlebars";
|
|
5
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
-
const __dirname = dirname(__filename);
|
|
1
|
+
import { developmentTemplate, productionTemplate, } from "./templates.generated.js";
|
|
7
2
|
class TemplateHelper {
|
|
8
|
-
templateCache = new Map();
|
|
9
|
-
loadTemplate(templateName) {
|
|
10
|
-
const cached = this.templateCache.get(templateName);
|
|
11
|
-
if (cached) {
|
|
12
|
-
return cached;
|
|
13
|
-
}
|
|
14
|
-
const templatePath = join(__dirname, "templates", `${templateName}.hbs`);
|
|
15
|
-
const templateSource = readFileSync(templatePath, "utf-8");
|
|
16
|
-
const template = Handlebars.compile(templateSource);
|
|
17
|
-
this.templateCache.set(templateName, template);
|
|
18
|
-
return template;
|
|
19
|
-
}
|
|
20
3
|
renderProduction(data) {
|
|
21
|
-
|
|
22
|
-
return template(data);
|
|
4
|
+
return productionTemplate(data);
|
|
23
5
|
}
|
|
24
6
|
renderDevelopment(data) {
|
|
25
|
-
|
|
26
|
-
return template(data);
|
|
7
|
+
return developmentTemplate(data);
|
|
27
8
|
}
|
|
28
9
|
}
|
|
29
10
|
export const templateHelper = new TemplateHelper();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templateHelper.js","sourceRoot":"","sources":["../../src/server/templateHelper.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"templateHelper.js","sourceRoot":"","sources":["../../src/server/templateHelper.ts"],"names":[],"mappings":"AACA,OAAO,EACL,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,0BAA0B,CAAC;AAElC,MAAM,cAAc;IAClB,gBAAgB,CAAC,IAKhB;QACC,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,iBAAiB,CAAC,IAIjB;QACC,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;CACF;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC","sourcesContent":["import type { ViewHostType } from \"./server.js\";\nimport {\n developmentTemplate,\n productionTemplate,\n} from \"./templates.generated.js\";\n\nclass TemplateHelper {\n renderProduction(data: {\n hostType: ViewHostType;\n serverUrl: string;\n viewFile: string;\n styleFile: string;\n }): string {\n return productionTemplate(data);\n }\n\n renderDevelopment(data: {\n hostType: ViewHostType;\n serverUrl: string;\n viewName: string;\n }): string {\n return developmentTemplate(data);\n }\n}\n\nexport const templateHelper = new TemplateHelper();\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// AUTO-GENERATED by scripts/precompile-templates.mjs. Do not edit.
|
|
2
|
+
import Handlebars from "handlebars/runtime.js";
|
|
3
|
+
/** @type {HandlebarsTemplateDelegate} */
|
|
4
|
+
export const developmentTemplate = Handlebars.template({ "compiler": [8, ">= 4.3.0"], "main": function (container, depth0, helpers, partials, data) {
|
|
5
|
+
var helper, alias1 = depth0 != null ? depth0 : (container.nullContext || {}), alias2 = container.hooks.helperMissing, alias3 = "function", alias4 = container.escapeExpression, lookupProperty = container.lookupProperty || function (parent, propertyName) {
|
|
6
|
+
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
|
7
|
+
return parent[propertyName];
|
|
8
|
+
}
|
|
9
|
+
return undefined;
|
|
10
|
+
};
|
|
11
|
+
return "<script type=\"module\">window.skybridge = { hostType: \""
|
|
12
|
+
+ alias4(((helper = (helper = lookupProperty(helpers, "hostType") || (depth0 != null ? lookupProperty(depth0, "hostType") : depth0)) != null ? helper : alias2), (typeof helper === alias3 ? helper.call(alias1, { "name": "hostType", "hash": {}, "data": data, "loc": { "start": { "line": 1, "column": 54 }, "end": { "line": 1, "column": 66 } } }) : helper)))
|
|
13
|
+
+ "\", serverUrl: \""
|
|
14
|
+
+ alias4(((helper = (helper = lookupProperty(helpers, "serverUrl") || (depth0 != null ? lookupProperty(depth0, "serverUrl") : depth0)) != null ? helper : alias2), (typeof helper === alias3 ? helper.call(alias1, { "name": "serverUrl", "hash": {}, "data": data, "loc": { "start": { "line": 1, "column": 81 }, "end": { "line": 1, "column": 94 } } }) : helper)))
|
|
15
|
+
+ "\" };</script>\n<script type=\"module\">\n import { injectIntoGlobalHook } from \""
|
|
16
|
+
+ alias4(((helper = (helper = lookupProperty(helpers, "serverUrl") || (depth0 != null ? lookupProperty(depth0, "serverUrl") : depth0)) != null ? helper : alias2), (typeof helper === alias3 ? helper.call(alias1, { "name": "serverUrl", "hash": {}, "data": data, "loc": { "start": { "line": 3, "column": 40 }, "end": { "line": 3, "column": 53 } } }) : helper)))
|
|
17
|
+
+ "/assets/@react-refresh\";\n injectIntoGlobalHook(window); window.$RefreshReg$ = () => {};\n window.$RefreshSig$ = () => (type) => type;\n window.__vite_plugin_react_preamble_installed__ = true;\n</script>\n<script type=\"module\" src=\""
|
|
18
|
+
+ alias4(((helper = (helper = lookupProperty(helpers, "serverUrl") || (depth0 != null ? lookupProperty(depth0, "serverUrl") : depth0)) != null ? helper : alias2), (typeof helper === alias3 ? helper.call(alias1, { "name": "serverUrl", "hash": {}, "data": data, "loc": { "start": { "line": 8, "column": 27 }, "end": { "line": 8, "column": 40 } } }) : helper)))
|
|
19
|
+
+ "/@vite/client\"></script>\n<div id=\"root\"></div>\n<script type=\"module\" id=\"dev-view-entry\">\n import('"
|
|
20
|
+
+ alias4(((helper = (helper = lookupProperty(helpers, "serverUrl") || (depth0 != null ? lookupProperty(depth0, "serverUrl") : depth0)) != null ? helper : alias2), (typeof helper === alias3 ? helper.call(alias1, { "name": "serverUrl", "hash": {}, "data": data, "loc": { "start": { "line": 11, "column": 10 }, "end": { "line": 11, "column": 23 } } }) : helper)))
|
|
21
|
+
+ "/_skybridge/view/"
|
|
22
|
+
+ alias4(((helper = (helper = lookupProperty(helpers, "viewName") || (depth0 != null ? lookupProperty(depth0, "viewName") : depth0)) != null ? helper : alias2), (typeof helper === alias3 ? helper.call(alias1, { "name": "viewName", "hash": {}, "data": data, "loc": { "start": { "line": 11, "column": 40 }, "end": { "line": 11, "column": 52 } } }) : helper)))
|
|
23
|
+
+ "');\n</script>";
|
|
24
|
+
}, "useData": true });
|
|
25
|
+
/** @type {HandlebarsTemplateDelegate} */
|
|
26
|
+
export const productionTemplate = Handlebars.template({ "compiler": [8, ">= 4.3.0"], "main": function (container, depth0, helpers, partials, data) {
|
|
27
|
+
var helper, alias1 = depth0 != null ? depth0 : (container.nullContext || {}), alias2 = container.hooks.helperMissing, alias3 = "function", alias4 = container.escapeExpression, lookupProperty = container.lookupProperty || function (parent, propertyName) {
|
|
28
|
+
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
|
29
|
+
return parent[propertyName];
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
};
|
|
33
|
+
return "<script type=\"module\">window.skybridge = { hostType: \""
|
|
34
|
+
+ alias4(((helper = (helper = lookupProperty(helpers, "hostType") || (depth0 != null ? lookupProperty(depth0, "hostType") : depth0)) != null ? helper : alias2), (typeof helper === alias3 ? helper.call(alias1, { "name": "hostType", "hash": {}, "data": data, "loc": { "start": { "line": 1, "column": 54 }, "end": { "line": 1, "column": 66 } } }) : helper)))
|
|
35
|
+
+ "\", serverUrl: \""
|
|
36
|
+
+ alias4(((helper = (helper = lookupProperty(helpers, "serverUrl") || (depth0 != null ? lookupProperty(depth0, "serverUrl") : depth0)) != null ? helper : alias2), (typeof helper === alias3 ? helper.call(alias1, { "name": "serverUrl", "hash": {}, "data": data, "loc": { "start": { "line": 1, "column": 81 }, "end": { "line": 1, "column": 94 } } }) : helper)))
|
|
37
|
+
+ "\" };</script>\n<div id=\"root\"></div>\n<script type=\"module\">\n import('"
|
|
38
|
+
+ alias4(((helper = (helper = lookupProperty(helpers, "serverUrl") || (depth0 != null ? lookupProperty(depth0, "serverUrl") : depth0)) != null ? helper : alias2), (typeof helper === alias3 ? helper.call(alias1, { "name": "serverUrl", "hash": {}, "data": data, "loc": { "start": { "line": 4, "column": 10 }, "end": { "line": 4, "column": 23 } } }) : helper)))
|
|
39
|
+
+ "/assets/"
|
|
40
|
+
+ alias4(((helper = (helper = lookupProperty(helpers, "viewFile") || (depth0 != null ? lookupProperty(depth0, "viewFile") : depth0)) != null ? helper : alias2), (typeof helper === alias3 ? helper.call(alias1, { "name": "viewFile", "hash": {}, "data": data, "loc": { "start": { "line": 4, "column": 31 }, "end": { "line": 4, "column": 43 } } }) : helper)))
|
|
41
|
+
+ "');\n</script>\n<link rel=\"stylesheet\" crossorigin href=\""
|
|
42
|
+
+ alias4(((helper = (helper = lookupProperty(helpers, "serverUrl") || (depth0 != null ? lookupProperty(depth0, "serverUrl") : depth0)) != null ? helper : alias2), (typeof helper === alias3 ? helper.call(alias1, { "name": "serverUrl", "hash": {}, "data": data, "loc": { "start": { "line": 6, "column": 41 }, "end": { "line": 6, "column": 54 } } }) : helper)))
|
|
43
|
+
+ "/assets/"
|
|
44
|
+
+ alias4(((helper = (helper = lookupProperty(helpers, "styleFile") || (depth0 != null ? lookupProperty(depth0, "styleFile") : depth0)) != null ? helper : alias2), (typeof helper === alias3 ? helper.call(alias1, { "name": "styleFile", "hash": {}, "data": data, "loc": { "start": { "line": 6, "column": 62 }, "end": { "line": 6, "column": 75 } } }) : helper)))
|
|
45
|
+
+ "\" />";
|
|
46
|
+
}, "useData": true });
|
|
47
|
+
//# sourceMappingURL=templates.generated.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.generated.js","sourceRoot":"","sources":["../../src/server/templates.generated.js"],"names":[],"mappings":"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","sourcesContent":["// AUTO-GENERATED by scripts/precompile-templates.mjs. Do not edit.\nimport Handlebars from \"handlebars/runtime.js\";\n\n/** @type {HandlebarsTemplateDelegate} */\nexport const developmentTemplate = Handlebars.template({\"compiler\":[8,\">= 4.3.0\"],\"main\":function(container,depth0,helpers,partials,data) {\n var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3=\"function\", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {\n if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {\n return parent[propertyName];\n }\n return undefined\n };\n\n return \"<script type=\\\"module\\\">window.skybridge = { hostType: \\\"\"\n + alias4(((helper = (helper = lookupProperty(helpers,\"hostType\") || (depth0 != null ? lookupProperty(depth0,\"hostType\") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{\"name\":\"hostType\",\"hash\":{},\"data\":data,\"loc\":{\"start\":{\"line\":1,\"column\":54},\"end\":{\"line\":1,\"column\":66}}}) : helper)))\n + \"\\\", serverUrl: \\\"\"\n + alias4(((helper = (helper = lookupProperty(helpers,\"serverUrl\") || (depth0 != null ? lookupProperty(depth0,\"serverUrl\") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{\"name\":\"serverUrl\",\"hash\":{},\"data\":data,\"loc\":{\"start\":{\"line\":1,\"column\":81},\"end\":{\"line\":1,\"column\":94}}}) : helper)))\n + \"\\\" };</script>\\n<script type=\\\"module\\\">\\n import { injectIntoGlobalHook } from \\\"\"\n + alias4(((helper = (helper = lookupProperty(helpers,\"serverUrl\") || (depth0 != null ? lookupProperty(depth0,\"serverUrl\") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{\"name\":\"serverUrl\",\"hash\":{},\"data\":data,\"loc\":{\"start\":{\"line\":3,\"column\":40},\"end\":{\"line\":3,\"column\":53}}}) : helper)))\n + \"/assets/@react-refresh\\\";\\n injectIntoGlobalHook(window); window.$RefreshReg$ = () => {};\\n window.$RefreshSig$ = () => (type) => type;\\n window.__vite_plugin_react_preamble_installed__ = true;\\n</script>\\n<script type=\\\"module\\\" src=\\\"\"\n + alias4(((helper = (helper = lookupProperty(helpers,\"serverUrl\") || (depth0 != null ? lookupProperty(depth0,\"serverUrl\") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{\"name\":\"serverUrl\",\"hash\":{},\"data\":data,\"loc\":{\"start\":{\"line\":8,\"column\":27},\"end\":{\"line\":8,\"column\":40}}}) : helper)))\n + \"/@vite/client\\\"></script>\\n<div id=\\\"root\\\"></div>\\n<script type=\\\"module\\\" id=\\\"dev-view-entry\\\">\\n import('\"\n + alias4(((helper = (helper = lookupProperty(helpers,\"serverUrl\") || (depth0 != null ? lookupProperty(depth0,\"serverUrl\") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{\"name\":\"serverUrl\",\"hash\":{},\"data\":data,\"loc\":{\"start\":{\"line\":11,\"column\":10},\"end\":{\"line\":11,\"column\":23}}}) : helper)))\n + \"/_skybridge/view/\"\n + alias4(((helper = (helper = lookupProperty(helpers,\"viewName\") || (depth0 != null ? lookupProperty(depth0,\"viewName\") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{\"name\":\"viewName\",\"hash\":{},\"data\":data,\"loc\":{\"start\":{\"line\":11,\"column\":40},\"end\":{\"line\":11,\"column\":52}}}) : helper)))\n + \"');\\n</script>\";\n},\"useData\":true});\n/** @type {HandlebarsTemplateDelegate} */\nexport const productionTemplate = Handlebars.template({\"compiler\":[8,\">= 4.3.0\"],\"main\":function(container,depth0,helpers,partials,data) {\n var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3=\"function\", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {\n if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {\n return parent[propertyName];\n }\n return undefined\n };\n\n return \"<script type=\\\"module\\\">window.skybridge = { hostType: \\\"\"\n + alias4(((helper = (helper = lookupProperty(helpers,\"hostType\") || (depth0 != null ? lookupProperty(depth0,\"hostType\") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{\"name\":\"hostType\",\"hash\":{},\"data\":data,\"loc\":{\"start\":{\"line\":1,\"column\":54},\"end\":{\"line\":1,\"column\":66}}}) : helper)))\n + \"\\\", serverUrl: \\\"\"\n + alias4(((helper = (helper = lookupProperty(helpers,\"serverUrl\") || (depth0 != null ? lookupProperty(depth0,\"serverUrl\") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{\"name\":\"serverUrl\",\"hash\":{},\"data\":data,\"loc\":{\"start\":{\"line\":1,\"column\":81},\"end\":{\"line\":1,\"column\":94}}}) : helper)))\n + \"\\\" };</script>\\n<div id=\\\"root\\\"></div>\\n<script type=\\\"module\\\">\\n import('\"\n + alias4(((helper = (helper = lookupProperty(helpers,\"serverUrl\") || (depth0 != null ? lookupProperty(depth0,\"serverUrl\") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{\"name\":\"serverUrl\",\"hash\":{},\"data\":data,\"loc\":{\"start\":{\"line\":4,\"column\":10},\"end\":{\"line\":4,\"column\":23}}}) : helper)))\n + \"/assets/\"\n + alias4(((helper = (helper = lookupProperty(helpers,\"viewFile\") || (depth0 != null ? lookupProperty(depth0,\"viewFile\") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{\"name\":\"viewFile\",\"hash\":{},\"data\":data,\"loc\":{\"start\":{\"line\":4,\"column\":31},\"end\":{\"line\":4,\"column\":43}}}) : helper)))\n + \"');\\n</script>\\n<link rel=\\\"stylesheet\\\" crossorigin href=\\\"\"\n + alias4(((helper = (helper = lookupProperty(helpers,\"serverUrl\") || (depth0 != null ? lookupProperty(depth0,\"serverUrl\") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{\"name\":\"serverUrl\",\"hash\":{},\"data\":data,\"loc\":{\"start\":{\"line\":6,\"column\":41},\"end\":{\"line\":6,\"column\":54}}}) : helper)))\n + \"/assets/\"\n + alias4(((helper = (helper = lookupProperty(helpers,\"styleFile\") || (depth0 != null ? lookupProperty(depth0,\"styleFile\") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{\"name\":\"styleFile\",\"hash\":{},\"data\":data,\"loc\":{\"start\":{\"line\":6,\"column\":62},\"end\":{\"line\":6,\"column\":75}}}) : helper)))\n + \"\\\" />\";\n},\"useData\":true});\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Router } from "express";
|
|
2
|
+
/**
|
|
3
|
+
* Builds an Express router that forwards `/__skybridge/tunnel` (POST/DELETE)
|
|
4
|
+
* and `/__skybridge/tunnel/events` (GET, SSE) to the cli's loopback control
|
|
5
|
+
* server. The `/__skybridge/` prefix avoids colliding with user-defined routes.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createTunnelProxyRouter(controlPort: number): Router;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import express, {} from "express";
|
|
2
|
+
/**
|
|
3
|
+
* Builds an Express router that forwards `/__skybridge/tunnel` (POST/DELETE)
|
|
4
|
+
* and `/__skybridge/tunnel/events` (GET, SSE) to the cli's loopback control
|
|
5
|
+
* server. The `/__skybridge/` prefix avoids colliding with user-defined routes.
|
|
6
|
+
*/
|
|
7
|
+
export function createTunnelProxyRouter(controlPort) {
|
|
8
|
+
const router = express.Router();
|
|
9
|
+
const upstream = `http://127.0.0.1:${controlPort}/__skybridge/tunnel`;
|
|
10
|
+
const forwardJson = async (method, res) => {
|
|
11
|
+
try {
|
|
12
|
+
const upstreamRes = await fetch(upstream, { method });
|
|
13
|
+
const body = await upstreamRes.text();
|
|
14
|
+
res
|
|
15
|
+
.status(upstreamRes.status)
|
|
16
|
+
.type(upstreamRes.headers.get("content-type") ?? "application/json")
|
|
17
|
+
.send(body);
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
res.status(502).json({
|
|
21
|
+
status: "error",
|
|
22
|
+
message: err instanceof Error ? err.message : "Tunnel control unavailable",
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
router.post("/__skybridge/tunnel", (_req, res) => {
|
|
27
|
+
void forwardJson("POST", res);
|
|
28
|
+
});
|
|
29
|
+
router.delete("/__skybridge/tunnel", (_req, res) => {
|
|
30
|
+
void forwardJson("DELETE", res);
|
|
31
|
+
});
|
|
32
|
+
router.get("/__skybridge/tunnel/events", async (req, res) => {
|
|
33
|
+
// Abort the upstream fetch when the client disconnects (or when the dev
|
|
34
|
+
// server shuts down and destroys the response).
|
|
35
|
+
const controller = new AbortController();
|
|
36
|
+
const onClose = () => controller.abort();
|
|
37
|
+
req.on("close", onClose);
|
|
38
|
+
let upstreamRes;
|
|
39
|
+
try {
|
|
40
|
+
upstreamRes = await fetch(`${upstream}/events`, {
|
|
41
|
+
signal: controller.signal,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
req.off("close", onClose);
|
|
46
|
+
if (!res.headersSent) {
|
|
47
|
+
res.status(502).json({
|
|
48
|
+
status: "error",
|
|
49
|
+
message: err instanceof Error ? err.message : "Tunnel control unavailable",
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const upstreamBody = upstreamRes.body;
|
|
55
|
+
if (!upstreamRes.ok || !upstreamBody) {
|
|
56
|
+
req.off("close", onClose);
|
|
57
|
+
res.status(upstreamRes.status).end();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
res.setHeader("Content-Type", upstreamRes.headers.get("content-type") ?? "text/event-stream");
|
|
61
|
+
res.setHeader("Cache-Control", "no-cache, no-transform");
|
|
62
|
+
res.setHeader("Connection", "keep-alive");
|
|
63
|
+
res.flushHeaders?.();
|
|
64
|
+
const reader = upstreamBody.getReader();
|
|
65
|
+
try {
|
|
66
|
+
while (true) {
|
|
67
|
+
const { value, done } = await reader.read();
|
|
68
|
+
if (done) {
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
if (!res.write(value)) {
|
|
72
|
+
// Race "drain" against "close": Node does not emit "drain" on a
|
|
73
|
+
// destroyed socket, so without the close listener this hangs
|
|
74
|
+
// forever when the client disconnects under backpressure, leaking
|
|
75
|
+
// the upstream fetch and TunnelManager listeners.
|
|
76
|
+
await new Promise((resolve) => {
|
|
77
|
+
const onDrain = () => {
|
|
78
|
+
res.off("close", onClose);
|
|
79
|
+
resolve();
|
|
80
|
+
};
|
|
81
|
+
const onClose = () => {
|
|
82
|
+
res.off("drain", onDrain);
|
|
83
|
+
resolve();
|
|
84
|
+
};
|
|
85
|
+
res.once("drain", onDrain);
|
|
86
|
+
res.once("close", onClose);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// upstream aborted (typically because the client disconnected) —
|
|
93
|
+
// fall through to end()
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
req.off("close", onClose);
|
|
97
|
+
// Release the reader so the upstream stream's lifecycle isn't held by
|
|
98
|
+
// a lingering lock on the clean-done path.
|
|
99
|
+
try {
|
|
100
|
+
await reader.cancel();
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// ignore — the reader may already be closed
|
|
104
|
+
}
|
|
105
|
+
res.end();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
return router;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=tunnel-proxy-router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-proxy-router.js","sourceRoot":"","sources":["../../src/server/tunnel-proxy-router.ts"],"names":[],"mappings":"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","sourcesContent":["import express, { type Router } from \"express\";\n\n/**\n * Builds an Express router that forwards `/__skybridge/tunnel` (POST/DELETE)\n * and `/__skybridge/tunnel/events` (GET, SSE) to the cli's loopback control\n * server. The `/__skybridge/` prefix avoids colliding with user-defined routes.\n */\nexport function createTunnelProxyRouter(controlPort: number): Router {\n const router = express.Router();\n const upstream = `http://127.0.0.1:${controlPort}/__skybridge/tunnel`;\n\n const forwardJson = async (\n method: \"POST\" | \"DELETE\",\n res: express.Response,\n ): Promise<void> => {\n try {\n const upstreamRes = await fetch(upstream, { method });\n const body = await upstreamRes.text();\n res\n .status(upstreamRes.status)\n .type(upstreamRes.headers.get(\"content-type\") ?? \"application/json\")\n .send(body);\n } catch (err) {\n res.status(502).json({\n status: \"error\",\n message:\n err instanceof Error ? err.message : \"Tunnel control unavailable\",\n });\n }\n };\n\n router.post(\"/__skybridge/tunnel\", (_req, res) => {\n void forwardJson(\"POST\", res);\n });\n\n router.delete(\"/__skybridge/tunnel\", (_req, res) => {\n void forwardJson(\"DELETE\", res);\n });\n\n router.get(\"/__skybridge/tunnel/events\", async (req, res) => {\n // Abort the upstream fetch when the client disconnects (or when the dev\n // server shuts down and destroys the response).\n const controller = new AbortController();\n const onClose = () => controller.abort();\n req.on(\"close\", onClose);\n\n let upstreamRes: Response;\n try {\n upstreamRes = await fetch(`${upstream}/events`, {\n signal: controller.signal,\n });\n } catch (err) {\n req.off(\"close\", onClose);\n if (!res.headersSent) {\n res.status(502).json({\n status: \"error\",\n message:\n err instanceof Error ? err.message : \"Tunnel control unavailable\",\n });\n }\n return;\n }\n\n const upstreamBody = upstreamRes.body;\n if (!upstreamRes.ok || !upstreamBody) {\n req.off(\"close\", onClose);\n res.status(upstreamRes.status).end();\n return;\n }\n\n res.setHeader(\n \"Content-Type\",\n upstreamRes.headers.get(\"content-type\") ?? \"text/event-stream\",\n );\n res.setHeader(\"Cache-Control\", \"no-cache, no-transform\");\n res.setHeader(\"Connection\", \"keep-alive\");\n res.flushHeaders?.();\n\n const reader = upstreamBody.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n break;\n }\n if (!res.write(value)) {\n // Race \"drain\" against \"close\": Node does not emit \"drain\" on a\n // destroyed socket, so without the close listener this hangs\n // forever when the client disconnects under backpressure, leaking\n // the upstream fetch and TunnelManager listeners.\n await new Promise<void>((resolve) => {\n const onDrain = () => {\n res.off(\"close\", onClose);\n resolve();\n };\n const onClose = () => {\n res.off(\"drain\", onDrain);\n resolve();\n };\n res.once(\"drain\", onDrain);\n res.once(\"close\", onClose);\n });\n }\n }\n } catch {\n // upstream aborted (typically because the client disconnected) —\n // fall through to end()\n } finally {\n req.off(\"close\", onClose);\n // Release the reader so the upstream stream's lifecycle isn't held by\n // a lingering lock on the clean-done path.\n try {\n await reader.cancel();\n } catch {\n // ignore — the reader may already be closed\n }\n res.end();\n }\n });\n\n return router;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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>;
|