veryfront 0.1.1127 → 0.1.1132
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/esm/cli/commands/eval/command.d.ts +2 -70
- package/esm/cli/commands/eval/command.d.ts.map +1 -1
- package/esm/cli/commands/eval/command.js +207 -550
- package/esm/cli/templates/manifest.d.ts +1 -0
- package/esm/cli/templates/manifest.js +3 -2
- package/esm/deno.js +1 -1
- package/esm/src/agent/hosted/chat-execution-runtime.d.ts.map +1 -1
- package/esm/src/agent/hosted/chat-execution-runtime.js +16 -33
- package/esm/src/agent/hosted/child-fork-tool-sources.d.ts.map +1 -1
- package/esm/src/agent/hosted/child-fork-tool-sources.js +6 -38
- package/esm/src/agent/hosted/hosted-chat-finalization.d.ts +30 -0
- package/esm/src/agent/hosted/hosted-chat-finalization.d.ts.map +1 -0
- package/esm/src/agent/hosted/hosted-chat-finalization.js +249 -0
- package/esm/src/agent/hosted/project-remote-tool-source.d.ts.map +1 -1
- package/esm/src/agent/hosted/project-remote-tool-source.js +4 -23
- package/esm/src/agent/mcp-tool-policy.d.ts +19 -0
- package/esm/src/agent/mcp-tool-policy.d.ts.map +1 -0
- package/esm/src/agent/mcp-tool-policy.js +66 -0
- package/esm/src/agent/runtime/mcp-server-tool-sources.d.ts.map +1 -1
- package/esm/src/agent/runtime/mcp-server-tool-sources.js +8 -44
- package/esm/src/discovery/import-rewriter.d.ts.map +1 -1
- package/esm/src/discovery/import-rewriter.js +7 -90
- package/esm/src/eval/run-report.d.ts +189 -0
- package/esm/src/eval/run-report.d.ts.map +1 -0
- package/esm/src/eval/run-report.js +603 -0
- package/esm/src/html/hydration-script-builder/dev-client-renderer.js +1 -1
- package/esm/src/html/hydration-script-builder/prod-scripts.js +1 -1
- package/esm/src/html/hydration-script-builder/templates/router.d.ts.map +1 -1
- package/esm/src/html/hydration-script-builder/templates/router.js +35 -11
- package/esm/src/html/utils.d.ts.map +1 -1
- package/esm/src/html/utils.js +19 -45
- package/esm/src/index.client.d.ts +6 -21
- package/esm/src/index.client.d.ts.map +1 -1
- package/esm/src/index.client.js +6 -21
- package/esm/src/modules/server/ssr-import-rewriter.d.ts +2 -26
- package/esm/src/modules/server/ssr-import-rewriter.d.ts.map +1 -1
- package/esm/src/modules/server/ssr-import-rewriter.js +1 -206
- package/esm/src/react/runtime/core.d.ts +22 -0
- package/esm/src/react/runtime/core.d.ts.map +1 -1
- package/esm/src/react/runtime/core.js +1 -1
- package/esm/src/rendering/orchestrator/module-collection.d.ts +8 -0
- package/esm/src/rendering/orchestrator/module-collection.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/module-collection.js +10 -0
- package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/pipeline.js +2 -2
- package/esm/src/routing/api/context-builder.d.ts +7 -0
- package/esm/src/routing/api/context-builder.d.ts.map +1 -1
- package/esm/src/routing/api/context-builder.js +20 -2
- package/esm/src/routing/api/module-loader/external-import-rewriter.d.ts +2 -7
- package/esm/src/routing/api/module-loader/external-import-rewriter.d.ts.map +1 -1
- package/esm/src/routing/api/module-loader/external-import-rewriter.js +10 -209
- package/esm/src/server/dev-server/file-watch-setup.d.ts.map +1 -1
- package/esm/src/server/dev-server/file-watch-setup.js +11 -1
- package/esm/src/server/dev-server/middleware.d.ts.map +1 -1
- package/esm/src/server/dev-server/middleware.js +45 -11
- package/esm/src/server/handlers/dev/files/esbuild-plugins.d.ts.map +1 -1
- package/esm/src/server/handlers/dev/files/esbuild-plugins.js +22 -0
- package/esm/src/server/handlers/request/module/page-data-endpoint-handler.js +24 -1
- package/esm/src/server/handlers/response/not-found.d.ts +0 -1
- package/esm/src/server/handlers/response/not-found.d.ts.map +1 -1
- package/esm/src/server/handlers/response/not-found.js +9 -110
- package/esm/src/server/runtime-handler/index.d.ts.map +1 -1
- package/esm/src/server/runtime-handler/index.js +66 -145
- package/esm/src/server/runtime-handler/project-runtime-context.d.ts +118 -0
- package/esm/src/server/runtime-handler/project-runtime-context.d.ts.map +1 -0
- package/esm/src/server/runtime-handler/project-runtime-context.js +181 -0
- package/esm/src/server/services/rsc/orchestrators/handler.d.ts +3 -1
- package/esm/src/server/services/rsc/orchestrators/handler.d.ts.map +1 -1
- package/esm/src/server/services/rsc/orchestrators/handler.js +13 -8
- package/esm/src/server/services/rsc/orchestrators/render-handler.d.ts +1 -1
- package/esm/src/server/services/rsc/orchestrators/render-handler.d.ts.map +1 -1
- package/esm/src/server/services/rsc/orchestrators/render-handler.js +2 -2
- package/esm/src/transforms/esm/http-cache.d.ts.map +1 -1
- package/esm/src/transforms/esm/http-cache.js +132 -46
- package/esm/src/transforms/esm/import-attributes.d.ts.map +1 -1
- package/esm/src/transforms/esm/import-attributes.js +2 -9
- package/esm/src/transforms/esm/in-flight-manager.d.ts +3 -2
- package/esm/src/transforms/esm/in-flight-manager.d.ts.map +1 -1
- package/esm/src/transforms/esm/in-flight-manager.js +6 -5
- package/esm/src/transforms/import-rewriter/core.d.ts +8 -0
- package/esm/src/transforms/import-rewriter/core.d.ts.map +1 -0
- package/esm/src/transforms/import-rewriter/core.js +27 -0
- package/esm/src/transforms/import-rewriter/import-edit.d.ts +57 -0
- package/esm/src/transforms/import-rewriter/import-edit.d.ts.map +1 -0
- package/esm/src/transforms/import-rewriter/import-edit.js +151 -0
- package/esm/src/transforms/import-rewriter/package-resolution.d.ts +12 -0
- package/esm/src/transforms/import-rewriter/package-resolution.d.ts.map +1 -0
- package/esm/src/transforms/import-rewriter/package-resolution.js +79 -0
- package/esm/src/transforms/import-rewriter/parse-cache.d.ts +4 -42
- package/esm/src/transforms/import-rewriter/parse-cache.d.ts.map +1 -1
- package/esm/src/transforms/import-rewriter/parse-cache.js +3 -134
- package/esm/src/transforms/import-rewriter/route-adapter.d.ts +24 -0
- package/esm/src/transforms/import-rewriter/route-adapter.d.ts.map +1 -0
- package/esm/src/transforms/import-rewriter/route-adapter.js +235 -0
- package/esm/src/transforms/import-rewriter/ssr-adapter.d.ts +27 -0
- package/esm/src/transforms/import-rewriter/ssr-adapter.d.ts.map +1 -0
- package/esm/src/transforms/import-rewriter/ssr-adapter.js +206 -0
- package/esm/src/transforms/import-rewriter/strategies/bare-strategy.d.ts.map +1 -1
- package/esm/src/transforms/import-rewriter/strategies/bare-strategy.js +16 -2
- package/esm/src/transforms/import-rewriter/unified-rewriter.d.ts +0 -4
- package/esm/src/transforms/import-rewriter/unified-rewriter.d.ts.map +1 -1
- package/esm/src/transforms/import-rewriter/unified-rewriter.js +2 -39
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/esm/src/workflow/executor/workflow-executor.d.ts +0 -20
- package/esm/src/workflow/executor/workflow-executor.d.ts.map +1 -1
- package/esm/src/workflow/executor/workflow-executor.js +41 -320
- package/esm/src/workflow/runtime/approval-manager.d.ts.map +1 -1
- package/esm/src/workflow/runtime/approval-manager.js +20 -61
- package/esm/src/workflow/runtime/workflow-run-control.d.ts +95 -0
- package/esm/src/workflow/runtime/workflow-run-control.d.ts.map +1 -0
- package/esm/src/workflow/runtime/workflow-run-control.js +529 -0
- package/esm/src/workflow/worker/run-manager.d.ts.map +1 -1
- package/esm/src/workflow/worker/run-manager.js +18 -92
- package/esm/src/workflow/worker/shared.d.ts +1 -1
- package/esm/src/workflow/worker/shared.d.ts.map +1 -1
- package/esm/src/workflow/worker/shared.js +17 -15
- package/package.json +5 -5
|
@@ -17,6 +17,16 @@ export const MODULE_LOAD_HARD_TIMEOUT_MS = 45_000;
|
|
|
17
17
|
export const DATA_FETCH_TIMEOUT_MS = 15_000;
|
|
18
18
|
/** Timeout for SSR rendering stage */
|
|
19
19
|
export const SSR_RENDER_TIMEOUT_MS = 20_000;
|
|
20
|
+
/**
|
|
21
|
+
* Human-readable label for the module-loading timeout.
|
|
22
|
+
*
|
|
23
|
+
* Falls back to the route pathname when the slug is empty — the index route's
|
|
24
|
+
* slug is `""`, which otherwise produced a blank timeout label
|
|
25
|
+
* (`"Module loading for "`) that named no route when a cold load stalled.
|
|
26
|
+
*/
|
|
27
|
+
export function moduleLoadLabel(slug, pathname) {
|
|
28
|
+
return `Module loading for ${slug || pathname || "unknown route"}`;
|
|
29
|
+
}
|
|
20
30
|
/**
|
|
21
31
|
* Collect modules that need to be loaded for data fetching.
|
|
22
32
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAUH,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,iCAAiC,CAAC;AASzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAUH,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,iCAAiC,CAAC;AASzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA6DhF,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAE1D;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACxE,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrF;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,YAAY,CAAC;IAC3B,gBAAgB,EAAE,wBAAwB,CAAC;IAC3C,YAAY,EAAE,YAAY,CAAC;IAC3B,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,eAAe,EAAE,eAAe,CAAC;IACjC,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wEAAwE;IACxE,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,6CAA6C;IAC7C,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,OAAO,qBAAqB,EAAE,sBAAsB,CAAC;CAC1E;AAqCD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,mBAAmB,CAAgC;gBAE/C,MAAM,EAAE,oBAAoB;IAcxC,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,oBAAoB;IA0B5B;;;;OAIG;YACW,yBAAyB;IAWvC;;;OAGG;IACH,gBAAgB,IAAI,IAAI;IAKxB,OAAO,CAAC,UAAU;YAOJ,0BAA0B;IAaxC;;;;;;;;;OASG;YACW,qBAAqB;IAiFnC;;;OAGG;YACW,mBAAmB;IAqHjC,OAAO,CAAC,uBAAuB;IAkCzB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAqU9E,+EAA+E;IACzE,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC;YAsHzE,kBAAkB;YA2ClB,cAAc;IAW5B,OAAO,CAAC,uBAAuB;YAYjB,kBAAkB;IA6FhC,OAAO,CAAC,kBAAkB;YAQZ,uBAAuB;IAmBrC;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;CAStB"}
|
|
@@ -45,7 +45,7 @@ import { determineClientModuleStrategy } from "../rsc/client-module-strategy.js"
|
|
|
45
45
|
// Extracted modules
|
|
46
46
|
import { EMPTY_LAYOUT_RESULT, isDotPath } from "./path-helpers.js";
|
|
47
47
|
import { cachePageCss, CSS_SSR_TIMEOUT_MS, getCachedPageCss, getPageCssCacheKey, } from "./css-cache.js";
|
|
48
|
-
import { collectModulesToLoad, DATA_FETCH_TIMEOUT_MS, hasDataFetchingFunction, MODULE_LOAD_HARD_TIMEOUT_MS, MODULE_LOAD_TIMEOUT_MS, SSR_RENDER_TIMEOUT_MS, } from "./module-collection.js";
|
|
48
|
+
import { collectModulesToLoad, DATA_FETCH_TIMEOUT_MS, hasDataFetchingFunction, MODULE_LOAD_HARD_TIMEOUT_MS, MODULE_LOAD_TIMEOUT_MS, moduleLoadLabel, SSR_RENDER_TIMEOUT_MS, } from "./module-collection.js";
|
|
49
49
|
const renderPageLog = logger.component("render-page");
|
|
50
50
|
const renderPipelineLog = logger.component("render-pipeline");
|
|
51
51
|
const resolvePageDataLog = logger.component("resolve-page-data");
|
|
@@ -251,7 +251,7 @@ export class RenderPipeline {
|
|
|
251
251
|
const loadedModules = await profilePhase("render.load_modules", () => withSpan(SpanNames.RENDER_LOAD_MODULES, () => withProgressTimeoutThrow((control) => this.loadModulesInParallel(modulesToLoad, options, control), {
|
|
252
252
|
idleTimeoutMs: MODULE_LOAD_TIMEOUT_MS,
|
|
253
253
|
hardTimeoutMs: options.abortSignal ? undefined : MODULE_LOAD_HARD_TIMEOUT_MS,
|
|
254
|
-
label:
|
|
254
|
+
label: moduleLoadLabel(slug, options.url?.pathname ?? ""),
|
|
255
255
|
signal: options.abortSignal,
|
|
256
256
|
}), { "render.module_count": modulesToLoad.length }));
|
|
257
257
|
const dataJobs = loadedModules.filter((m) => options?.staticDataOnly
|
|
@@ -35,6 +35,13 @@ export interface APIContext {
|
|
|
35
35
|
* behave identically. Handlers must not care which one ran them.
|
|
36
36
|
*/
|
|
37
37
|
export declare function createJsonHelper(_request: Request): APIContext["json"];
|
|
38
|
+
/**
|
|
39
|
+
* Build the `ctx.text` response helper.
|
|
40
|
+
*
|
|
41
|
+
* Exported for the isolation Worker so both execution modes share Fetch's
|
|
42
|
+
* null-body status handling instead of maintaining separate implementations.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createTextHelper(): APIContext["text"];
|
|
38
45
|
/**
|
|
39
46
|
* Build the `ctx.body` request-body reader.
|
|
40
47
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-builder.d.ts","sourceRoot":"","sources":["../../../../src/src/routing/api/context-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAI3D,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB,mDAAmD;AACnD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC1C,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,GAAG,CAAC;IACT;;;;OAIG;IACH,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,QAAQ,CAAC;IACvD;;;;;;OAMG;IACH,IAAI,EAAE,CAAC,CAAC,GAAG,OAAO,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;IACpC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,QAAQ,CAAC;IACtD,EAAE,EAAE,iBAAiB,CAAC;CACvB;
|
|
1
|
+
{"version":3,"file":"context-builder.d.ts","sourceRoot":"","sources":["../../../../src/src/routing/api/context-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAI3D,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB,mDAAmD;AACnD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC1C,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,GAAG,CAAC;IACT;;;;OAIG;IACH,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,QAAQ,CAAC;IACvD;;;;;;OAMG;IACH,IAAI,EAAE,CAAC,CAAC,GAAG,OAAO,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;IACpC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,QAAQ,CAAC;IACtD,EAAE,EAAE,iBAAiB,CAAC;CACvB;AA0BD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAGtE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CAAC,MAAM,CAAC,CAErD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAiBrE;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,UAAU,EACjB,EAAE,EAAE,iBAAiB,GACpB,UAAU,CAmBZ;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GACxC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAExB"}
|
|
@@ -2,8 +2,17 @@ import { parseCookies } from "../../utils/cookie-utils.js";
|
|
|
2
2
|
import { INVALID_ARGUMENT } from "../../errors/index.js";
|
|
3
3
|
import { flattenRouteParams } from "../flatten-route-params.js";
|
|
4
4
|
export { parseCookies };
|
|
5
|
+
/**
|
|
6
|
+
* Statuses that the Fetch spec forbids from carrying a body. Constructing
|
|
7
|
+
* `new Response(body, { status })` with a non-null `body` (an empty string
|
|
8
|
+
* still counts) throws `Response with null body status cannot have body`, so
|
|
9
|
+
* the helpers below must send `null` for these.
|
|
10
|
+
*/
|
|
11
|
+
const NULL_BODY_STATUSES = new Set([101, 103, 204, 205, 304]);
|
|
5
12
|
function createResponse(body, contentType, init) {
|
|
6
|
-
|
|
13
|
+
const status = init?.status;
|
|
14
|
+
const hasNullBodyStatus = status !== undefined && NULL_BODY_STATUSES.has(status);
|
|
15
|
+
return new Response(hasNullBodyStatus ? null : body, {
|
|
7
16
|
...init,
|
|
8
17
|
headers: {
|
|
9
18
|
"Content-Type": contentType,
|
|
@@ -20,6 +29,15 @@ function createResponse(body, contentType, init) {
|
|
|
20
29
|
export function createJsonHelper(_request) {
|
|
21
30
|
return (data, init) => createResponse(JSON.stringify(data), "application/json", init);
|
|
22
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Build the `ctx.text` response helper.
|
|
34
|
+
*
|
|
35
|
+
* Exported for the isolation Worker so both execution modes share Fetch's
|
|
36
|
+
* null-body status handling instead of maintaining separate implementations.
|
|
37
|
+
*/
|
|
38
|
+
export function createTextHelper() {
|
|
39
|
+
return (data, init) => createResponse(data, "text/plain", init);
|
|
40
|
+
}
|
|
23
41
|
/**
|
|
24
42
|
* Build the `ctx.body` request-body reader.
|
|
25
43
|
*
|
|
@@ -52,7 +70,7 @@ export function createContext(request, match, fs) {
|
|
|
52
70
|
const url = new URL(request.url);
|
|
53
71
|
const json = createJsonHelper(request);
|
|
54
72
|
const body = createBodyReader(request);
|
|
55
|
-
const text = (
|
|
73
|
+
const text = createTextHelper();
|
|
56
74
|
return {
|
|
57
75
|
request,
|
|
58
76
|
req: request,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FileSystem } from "../../../platform/compat/fs.js";
|
|
2
|
+
import type { EsmDependencyLocation as RouteEsmDependencyLocation } from "../../../transforms/import-rewriter/route-adapter.js";
|
|
2
3
|
/** Node.js built-in module names — shared across the CJS shim, esbuild externals, and Deno rewrites. */
|
|
3
4
|
export declare const NODE_BUILTINS: readonly ["assert", "buffer", "child_process", "cluster", "console", "constants", "crypto", "dgram", "dns", "events", "fs", "http", "http2", "https", "module", "net", "os", "path", "perf_hooks", "process", "querystring", "readline", "stream", "string_decoder", "timers", "tls", "tty", "url", "util", "v8", "vm", "worker_threads", "zlib"];
|
|
4
5
|
export declare function readProjectDependencies(projectDir: string, fs: FileSystem): Promise<Map<string, string>>;
|
|
@@ -15,13 +16,7 @@ export declare function readProjectDependencies(projectDir: string, fs: FileSyst
|
|
|
15
16
|
export declare function generateCompiledBinaryRequireShim(projectDir: string): string;
|
|
16
17
|
export declare function getNodeExternalPackagesToResolve(userDeps: Map<string, string>): string[];
|
|
17
18
|
export declare function resolveNodePackageToFileUrl(projectDir: string, packageName: string, fs: FileSystem, pathToFileURL: typeof import("node:url").pathToFileURL): Promise<string | null>;
|
|
18
|
-
|
|
19
|
-
export interface EsmDependencyLocation {
|
|
20
|
-
/** file:// URL of the package's ESM entry point. */
|
|
21
|
-
entryUrl: string;
|
|
22
|
-
/** Absolute path of the package's root directory (used to contain subpath imports). */
|
|
23
|
-
packageDir: string;
|
|
24
|
-
}
|
|
19
|
+
export type EsmDependencyLocation = RouteEsmDependencyLocation;
|
|
25
20
|
/**
|
|
26
21
|
* Identify the subset of user dependencies that are ESM-only and resolve each
|
|
27
22
|
* to file:// URLs so the compiled-binary loader can import them as real ES
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external-import-rewriter.d.ts","sourceRoot":"","sources":["../../../../../src/src/routing/api/module-loader/external-import-rewriter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"external-import-rewriter.d.ts","sourceRoot":"","sources":["../../../../../src/src/routing/api/module-loader/external-import-rewriter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAejE,OAAO,KAAK,EACV,qBAAqB,IAAI,0BAA0B,EACpD,MAAM,sDAAsD,CAAC;AAK9D,wGAAwG;AACxG,eAAO,MAAM,aAAa,mVAAsB,CAAC;AAEjD,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,UAAU,GACb,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAE9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,iCAAiC,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA+E5E;AAED,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,EAAE,CAExF;AAED,wBAAsB,2BAA2B,CAC/C,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,UAAU,EACd,aAAa,EAAE,cAAc,UAAU,EAAE,aAAa,GACrD,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoBxB;AAED,MAAM,MAAM,qBAAqB,GAAG,0BAA0B,CAAC;AAE/D;;;;;GAKG;AACH,wBAAsB,0BAA0B,CAC9C,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,UAAU,EACd,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAE7C;AAED,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,UAAU,GACb,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAW9C;AAED,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,UAAU,EACd,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,OAAO,CAAC,MAAM,CAAC,CA+DjB;AAED,wBAAgB,qCAAqC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE1E;AAED,wBAAgB,0CAA0C,CACxD,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,OAAO,GAAE,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAa,GACtD,MAAM,CAER;AAED,wBAAsB,+BAA+B,CACnD,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,UAAU,EACd,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,OAAO,CAAC,MAAM,CAAC,CAEjB;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,UAAU,EACd,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAa,GACxC,OAAO,CAAC,MAAM,CAAC,CAmCjB"}
|
|
@@ -3,55 +3,13 @@ import * as pathHelper from "../../../platform/compat/path/index.js";
|
|
|
3
3
|
import { isDeno, isNode } from "../../../platform/compat/runtime.js";
|
|
4
4
|
import { rewriteNpmImports } from "../../../transforms/npm-import-rewrites.js";
|
|
5
5
|
import { parseImports, replaceSpecifiers } from "../../../transforms/esm/lexer.js";
|
|
6
|
-
import {
|
|
7
|
-
import { resolveExportEntry
|
|
6
|
+
import { getNodeExternalPackagesToResolveForRoute, NODE_BUILTINS as ROUTE_NODE_BUILTINS, readProjectDependenciesForRoute, resolveEsmUserDependenciesForRoute, rewriteCompiledUserDependencyImportsForRoute, rewriteCompiledVeryfrontImportsForRoute, rewriteDenoNodeBuiltinsForRoute, rewriteDenoNpmDependencyImportsForRoute, } from "../../../transforms/import-rewriter/route-adapter.js";
|
|
7
|
+
import { resolveExportEntry } from "./loader-helpers.js";
|
|
8
8
|
const logger = serverLogger.component("api");
|
|
9
9
|
/** Node.js built-in module names — shared across the CJS shim, esbuild externals, and Deno rewrites. */
|
|
10
|
-
export const NODE_BUILTINS =
|
|
11
|
-
"assert",
|
|
12
|
-
"buffer",
|
|
13
|
-
"child_process",
|
|
14
|
-
"cluster",
|
|
15
|
-
"console",
|
|
16
|
-
"constants",
|
|
17
|
-
"crypto",
|
|
18
|
-
"dgram",
|
|
19
|
-
"dns",
|
|
20
|
-
"events",
|
|
21
|
-
"fs",
|
|
22
|
-
"http",
|
|
23
|
-
"http2",
|
|
24
|
-
"https",
|
|
25
|
-
"module",
|
|
26
|
-
"net",
|
|
27
|
-
"os",
|
|
28
|
-
"path",
|
|
29
|
-
"perf_hooks",
|
|
30
|
-
"process",
|
|
31
|
-
"querystring",
|
|
32
|
-
"readline",
|
|
33
|
-
"stream",
|
|
34
|
-
"string_decoder",
|
|
35
|
-
"timers",
|
|
36
|
-
"tls",
|
|
37
|
-
"tty",
|
|
38
|
-
"url",
|
|
39
|
-
"util",
|
|
40
|
-
"v8",
|
|
41
|
-
"vm",
|
|
42
|
-
"worker_threads",
|
|
43
|
-
"zlib",
|
|
44
|
-
];
|
|
10
|
+
export const NODE_BUILTINS = ROUTE_NODE_BUILTINS;
|
|
45
11
|
export async function readProjectDependencies(projectDir, fs) {
|
|
46
|
-
|
|
47
|
-
const content = await fs.readTextFile(pathHelper.join(projectDir, "package.json"));
|
|
48
|
-
const pkg = JSON.parse(content);
|
|
49
|
-
return new Map(Object.entries(pkg.dependencies ?? {}));
|
|
50
|
-
}
|
|
51
|
-
catch (_) {
|
|
52
|
-
/* expected: package.json may not exist */
|
|
53
|
-
return new Map();
|
|
54
|
-
}
|
|
12
|
+
return await readProjectDependenciesForRoute(projectDir, fs);
|
|
55
13
|
}
|
|
56
14
|
/**
|
|
57
15
|
* Generates a CJS module loader shim for compiled Deno binaries.
|
|
@@ -143,13 +101,7 @@ require.ensure = function(mods, cb) { cb(); };
|
|
|
143
101
|
`.trim();
|
|
144
102
|
}
|
|
145
103
|
export function getNodeExternalPackagesToResolve(userDeps) {
|
|
146
|
-
|
|
147
|
-
for (const name of userDeps.keys()) {
|
|
148
|
-
if (!externalPackagesToResolve.includes(name)) {
|
|
149
|
-
externalPackagesToResolve.push(name);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return externalPackagesToResolve;
|
|
104
|
+
return getNodeExternalPackagesToResolveForRoute(userDeps);
|
|
153
105
|
}
|
|
154
106
|
export async function resolveNodePackageToFileUrl(projectDir, packageName, fs, pathToFileURL) {
|
|
155
107
|
const packagePath = pathHelper.join(projectDir, "node_modules", packageName);
|
|
@@ -170,42 +122,6 @@ export async function resolveNodePackageToFileUrl(projectDir, packageName, fs, p
|
|
|
170
122
|
return null;
|
|
171
123
|
}
|
|
172
124
|
}
|
|
173
|
-
/**
|
|
174
|
-
* Decide whether an installed package must be loaded as a real ES module.
|
|
175
|
-
*
|
|
176
|
-
* ESM-only packages (e.g. `"type": "module"` packages that use `import.meta` or
|
|
177
|
-
* top-level await) cannot be evaluated as CommonJS via the compiled-binary
|
|
178
|
-
* `new Function` shim. We treat a package as ESM when its package.json declares
|
|
179
|
-
* `"type": "module"` or its resolved entry point is a `.mjs` file.
|
|
180
|
-
*/
|
|
181
|
-
function isEsmPackage(pkgJson, entry) {
|
|
182
|
-
if (pkgJson.type === "module")
|
|
183
|
-
return true;
|
|
184
|
-
return entry.endsWith(".mjs");
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Resolve a package's ESM entry point, preferring the conditional `import`
|
|
188
|
-
* export, then the `module` field, then `main`.
|
|
189
|
-
*/
|
|
190
|
-
function resolveEsmEntry(pkgJson) {
|
|
191
|
-
const exportsField = pkgJson.exports;
|
|
192
|
-
if (exportsField && typeof exportsField === "object") {
|
|
193
|
-
const dot = exportsField["."];
|
|
194
|
-
const fromExports = resolveExportEntry(dot ?? exportsField);
|
|
195
|
-
if (fromExports)
|
|
196
|
-
return fromExports;
|
|
197
|
-
}
|
|
198
|
-
else if (typeof exportsField === "string") {
|
|
199
|
-
return exportsField;
|
|
200
|
-
}
|
|
201
|
-
const moduleField = pkgJson.module;
|
|
202
|
-
if (typeof moduleField === "string")
|
|
203
|
-
return moduleField;
|
|
204
|
-
const mainField = pkgJson.main;
|
|
205
|
-
if (typeof mainField === "string")
|
|
206
|
-
return mainField;
|
|
207
|
-
return "index.js";
|
|
208
|
-
}
|
|
209
125
|
/**
|
|
210
126
|
* Identify the subset of user dependencies that are ESM-only and resolve each
|
|
211
127
|
* to file:// URLs so the compiled-binary loader can import them as real ES
|
|
@@ -213,35 +129,7 @@ function resolveEsmEntry(pkgJson) {
|
|
|
213
129
|
* and continue to load through the `createRequire`-based shim.
|
|
214
130
|
*/
|
|
215
131
|
export async function resolveEsmUserDependencies(projectDir, fs, userDeps) {
|
|
216
|
-
|
|
217
|
-
for (const name of userDeps.keys()) {
|
|
218
|
-
const packageDir = pathHelper.resolve(pathHelper.join(projectDir, "node_modules", name));
|
|
219
|
-
try {
|
|
220
|
-
const pkgJson = JSON.parse(await fs.readTextFile(pathHelper.join(packageDir, "package.json")));
|
|
221
|
-
const entry = resolveEsmEntry(pkgJson);
|
|
222
|
-
if (!entry || !isEsmPackage(pkgJson, entry))
|
|
223
|
-
continue;
|
|
224
|
-
// The entry path comes from the dependency's own package.json, which is
|
|
225
|
-
// attacker-influenceable (a malicious/compromised package could set
|
|
226
|
-
// "main": "../../../etc/passwd"). Reject entries that resolve outside the
|
|
227
|
-
// package directory so a crafted package.json cannot turn into a file://
|
|
228
|
-
// import that escapes node_modules. This mirrors the containment guard the
|
|
229
|
-
// CJS loader shim enforces via __vf_assertContained.
|
|
230
|
-
const entryPath = pathHelper.resolve(pathHelper.join(packageDir, entry));
|
|
231
|
-
if (!isWithinDirectory(packageDir, entryPath)) {
|
|
232
|
-
logger.warn(`Skipping ESM dependency ${name}: entry escapes package directory (${entry})`);
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
esmDeps.set(name, {
|
|
236
|
-
entryUrl: pathHelper.toFileUrl(entryPath).href,
|
|
237
|
-
packageDir,
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
catch (_) {
|
|
241
|
-
/* expected: package.json missing/invalid → treat as CJS */
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
return esmDeps;
|
|
132
|
+
return await resolveEsmUserDependenciesForRoute(projectDir, fs, userDeps);
|
|
245
133
|
}
|
|
246
134
|
export async function loadVeryfrontExportsMap(projectDir, fs) {
|
|
247
135
|
const vfPackagePath = pathHelper.join(projectDir, "node_modules", "veryfront");
|
|
@@ -310,103 +198,16 @@ export async function rewriteNodeExternalImports(code, projectDir, fs, userDeps)
|
|
|
310
198
|
return await replaceSpecifiers(code, (specifier) => replacements.get(specifier));
|
|
311
199
|
}
|
|
312
200
|
export function rewriteCompiledBinaryVeryfrontImports(code) {
|
|
313
|
-
|
|
314
|
-
transformed = transformed.replace(/from\s+["']veryfront["']/g, 'from "./_vf_runtime.mjs"');
|
|
315
|
-
transformed = transformed.replace(/import\s*\(\s*["']veryfront["']\s*\)/g, 'import("./_vf_runtime.mjs")');
|
|
316
|
-
transformed = transformed.replace(/from\s+["']veryfront\/([^"']+)["']/g, (_match, subpath) => `from "./_vf_${subpath.replace(/\//g, "_")}.mjs"`);
|
|
317
|
-
transformed = transformed.replace(/import\s*\(\s*["']veryfront\/([^"']+)["']\s*\)/g, (_match, subpath) => `import("./_vf_${subpath.replace(/\//g, "_")}.mjs")`);
|
|
318
|
-
return transformed;
|
|
201
|
+
return rewriteCompiledVeryfrontImportsForRoute(code);
|
|
319
202
|
}
|
|
320
203
|
export function rewriteCompiledBinaryUserDependencyImports(code, userDeps, esmDeps = new Map()) {
|
|
321
|
-
|
|
322
|
-
for (const name of userDeps.keys()) {
|
|
323
|
-
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
324
|
-
// ESM-only dependencies are rewritten to real ES module file:// URLs so that
|
|
325
|
-
// import.meta, top-level await, etc. work. They must NOT be transpiled to
|
|
326
|
-
// CommonJS and evaluated via the `new Function` shim (see __vf_loadCjs).
|
|
327
|
-
const esm = esmDeps.get(name);
|
|
328
|
-
if (esm) {
|
|
329
|
-
// Resolve a subpath import to a contained file:// URL. The subpath comes
|
|
330
|
-
// from the handler source; reject any that escape the package directory
|
|
331
|
-
// (e.g. "pkg/../../secret") by leaving the import untouched so it fails to
|
|
332
|
-
// resolve rather than reading outside node_modules.
|
|
333
|
-
const subpathUrl = (subpath, original) => {
|
|
334
|
-
const target = pathHelper.resolve(pathHelper.join(esm.packageDir, subpath));
|
|
335
|
-
if (!isWithinDirectory(esm.packageDir, target)) {
|
|
336
|
-
logger.warn(`Skipping ESM subpath import that escapes package directory: ${original}`);
|
|
337
|
-
return null;
|
|
338
|
-
}
|
|
339
|
-
return pathHelper.toFileUrl(target).href;
|
|
340
|
-
};
|
|
341
|
-
transformed = transformed.replace(new RegExp(`from\\s+["']${escaped}(/[^"']+)["']`, "g"), (match, subpath) => {
|
|
342
|
-
const url = subpathUrl(subpath, match);
|
|
343
|
-
return url ? `from "${url}"` : match;
|
|
344
|
-
});
|
|
345
|
-
transformed = transformed.replace(new RegExp(`from\\s+["']${escaped}["']`, "g"), () => `from "${esm.entryUrl}"`);
|
|
346
|
-
transformed = transformed.replace(new RegExp(`import\\s*\\(\\s*["']${escaped}(/[^"']+)["']\\s*\\)`, "g"), (match, subpath) => {
|
|
347
|
-
const url = subpathUrl(subpath, match);
|
|
348
|
-
return url ? `import("${url}")` : match;
|
|
349
|
-
});
|
|
350
|
-
transformed = transformed.replace(new RegExp(`import\\s*\\(\\s*["']${escaped}["']\\s*\\)`, "g"), () => `import("${esm.entryUrl}")`);
|
|
351
|
-
continue;
|
|
352
|
-
}
|
|
353
|
-
transformed = transformed.replace(new RegExp(`import\\s+(\\w+)\\s+from\\s+["']${escaped}["']`, "g"), (_, localName) => `const ${localName} = __vf_interopDefault(require("${name}"))`);
|
|
354
|
-
transformed = transformed.replace(new RegExp(`import\\s+(\\{[^}]+\\})\\s+from\\s+["']${escaped}["']`, "g"), (_, bindings) => `const ${toCjsDestructureBindings(bindings)} = require("${name}")`);
|
|
355
|
-
transformed = transformed.replace(new RegExp(`import\\s+\\*\\s+as\\s+(\\w+)\\s+from\\s+["']${escaped}["']`, "g"), (_, localName) => `const ${localName} = require("${name}")`);
|
|
356
|
-
transformed = transformed.replace(new RegExp(`import\\s+(\\w+)\\s*,\\s*(\\{[^}]+\\})\\s+from\\s+["']${escaped}["']`, "g"), (_, defaultName, bindings) => {
|
|
357
|
-
const tmp = `__vf_tmp_${defaultName}`;
|
|
358
|
-
return `const ${tmp} = require("${name}"); const ${defaultName} = __vf_interopDefault(${tmp}); const ${toCjsDestructureBindings(bindings)} = ${tmp}`;
|
|
359
|
-
});
|
|
360
|
-
transformed = transformed.replace(new RegExp(`import\\s+(\\w+|\\*\\s+as\\s+\\w+|\\{[^}]+\\})\\s+from\\s+["']${escaped}(/[^"']+)["']`, "g"), (_, binding, subpath) => {
|
|
361
|
-
const trimmedBinding = String(binding).trim();
|
|
362
|
-
if (trimmedBinding.startsWith("{")) {
|
|
363
|
-
return `const ${toCjsDestructureBindings(trimmedBinding)} = require("${name}${subpath}")`;
|
|
364
|
-
}
|
|
365
|
-
const name_ = trimmedBinding.startsWith("*")
|
|
366
|
-
? trimmedBinding.replace(/\*\s+as\s+/, "")
|
|
367
|
-
: trimmedBinding;
|
|
368
|
-
return `const ${name_} = require("${name}${subpath}")`;
|
|
369
|
-
});
|
|
370
|
-
transformed = transformed.replace(new RegExp(`import\\s*\\(\\s*["']${escaped}(/[^"']*)?["']\\s*\\)`, "g"), (_, subpath) => `Promise.resolve(require("${name}${subpath || ""}"))`);
|
|
371
|
-
}
|
|
372
|
-
return transformed;
|
|
204
|
+
return rewriteCompiledUserDependencyImportsForRoute(code, userDeps, esmDeps);
|
|
373
205
|
}
|
|
374
206
|
export async function rewriteDenoNpmDependencyImports(code, projectDir, fs, userDeps) {
|
|
375
|
-
|
|
376
|
-
.map((imp) => imp.n)
|
|
377
|
-
.filter((specifier) => typeof specifier === "string"));
|
|
378
|
-
const replacements = new Map();
|
|
379
|
-
for (const specifier of importedSpecifiers) {
|
|
380
|
-
const entry = [...userDeps].find(([name]) => specifier === name || specifier.startsWith(`${name}/`));
|
|
381
|
-
if (!entry)
|
|
382
|
-
continue;
|
|
383
|
-
const [name, version] = entry;
|
|
384
|
-
let resolvedVersion = version;
|
|
385
|
-
try {
|
|
386
|
-
const pkgPath = pathHelper.join(projectDir, "node_modules", name, "package.json");
|
|
387
|
-
const pkgContent = await fs.readTextFile(pkgPath);
|
|
388
|
-
const pkg = JSON.parse(pkgContent);
|
|
389
|
-
if (pkg.version)
|
|
390
|
-
resolvedVersion = pkg.version;
|
|
391
|
-
}
|
|
392
|
-
catch (_) {
|
|
393
|
-
/* expected: installed package.json may not exist, fall back to declared range */
|
|
394
|
-
}
|
|
395
|
-
const subpath = specifier.slice(name.length);
|
|
396
|
-
replacements.set(specifier, `npm:${name}@${resolvedVersion}${subpath}`);
|
|
397
|
-
}
|
|
398
|
-
if (replacements.size === 0)
|
|
399
|
-
return code;
|
|
400
|
-
return await replaceSpecifiers(code, (specifier) => replacements.get(specifier));
|
|
207
|
+
return await rewriteDenoNpmDependencyImportsForRoute(code, projectDir, fs, userDeps);
|
|
401
208
|
}
|
|
402
209
|
export function rewriteDenoNodeBuiltinImports(code) {
|
|
403
|
-
|
|
404
|
-
for (const mod of NODE_BUILTINS) {
|
|
405
|
-
const escaped = mod.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
406
|
-
transformed = transformed.replace(new RegExp(`from\\s+["']${escaped}["']`, "g"), `from "node:${mod}"`);
|
|
407
|
-
transformed = transformed.replace(new RegExp(`import\\s*\\(\\s*["']${escaped}["']\\s*\\)`, "g"), `import("node:${mod}")`);
|
|
408
|
-
}
|
|
409
|
-
return transformed;
|
|
210
|
+
return rewriteDenoNodeBuiltinsForRoute(code);
|
|
410
211
|
}
|
|
411
212
|
export async function rewriteExternalImports(code, projectDir, fs, userDeps = new Map()) {
|
|
412
213
|
let transformed = code;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-watch-setup.d.ts","sourceRoot":"","sources":["../../../../src/src/server/dev-server/file-watch-setup.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"file-watch-setup.d.ts","sourceRoot":"","sources":["../../../../src/src/server/dev-server/file-watch-setup.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAuF3D;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAItD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAMhF;AAED,qBAAa,cAAc;IAUvB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,iBAAiB;IACzB,OAAO,CAAC,oBAAoB,CAAC;IAd/B,OAAO,CAAC,WAAW,CAAC,CAAoB;IACxC,OAAO,CAAC,iBAAiB,CAAC,CAAkB;IAC5C,OAAO,CAAC,gBAAgB,CAAC,CAAuB;IAChD,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,aAAa,CAAc;IACnC,uEAAuE;IACvE,OAAO,CAAC,aAAa,CAA6B;gBAGxC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,cAAc,EACvB,cAAc,EAAE,cAAc,EAC9B,UAAU,EAAE,MAAM,EAClB,iBAAiB,GAAE,MAAM,IAAe,EACxC,oBAAoB,CAAC,GAAE,MAAM,OAAO,CAAC,IAAI,CAAC,aAAA,EAClD,iBAAiB,CAAC,EAAE,MAAM,EAAE;IAKxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YA8Bd,aAAa;YA2Bb,kBAAkB;YAiClB,gBAAgB;IAkB9B;;;OAGG;IACH,OAAO,CAAC,eAAe;IAMvB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAMzB,8CAA8C;IAC9C,OAAO,CAAC,WAAW;IASnB,6DAA6D;YAC/C,kBAAkB;YAkBlB,wBAAwB;YA6BxB,yBAAyB;IAMvC,UAAU;IAIV,OAAO,IAAI,IAAI;CAYhB"}
|
|
@@ -43,6 +43,11 @@ const IGNORED_PATH_PATTERNS = [
|
|
|
43
43
|
* would wrongly suppress legitimate updates.
|
|
44
44
|
*/
|
|
45
45
|
const IGNORED_ARTIFACT_EXTENSIONS = new Set([".log", ".tmp"]);
|
|
46
|
+
/**
|
|
47
|
+
* Generated file names written at project root during dev-server request
|
|
48
|
+
* handling. These files are not source and must not trigger HMR.
|
|
49
|
+
*/
|
|
50
|
+
const TRANSIENT_MIDDLEWARE_MODULE_RE = /^\.vf-middleware-.+\.mjs$/;
|
|
46
51
|
/**
|
|
47
52
|
* Project-root directory names that contain runtime data (not source code)
|
|
48
53
|
* and should be excluded from HMR. Matched by first path segment relative
|
|
@@ -69,6 +74,10 @@ function hasIgnoredArtifactExtension(path) {
|
|
|
69
74
|
}
|
|
70
75
|
return false;
|
|
71
76
|
}
|
|
77
|
+
function hasIgnoredArtifactFileName(path) {
|
|
78
|
+
const fileName = path.split(/[\\/]/).at(-1)?.toLowerCase() ?? "";
|
|
79
|
+
return TRANSIENT_MIDDLEWARE_MODULE_RE.test(fileName);
|
|
80
|
+
}
|
|
72
81
|
/**
|
|
73
82
|
* Check if a path should be ignored for HMR purposes — either because it lives
|
|
74
83
|
* in a generated/output directory or because it is a generated-artifact file.
|
|
@@ -77,7 +86,8 @@ function hasIgnoredArtifactExtension(path) {
|
|
|
77
86
|
*/
|
|
78
87
|
export function shouldIgnorePath(path) {
|
|
79
88
|
return IGNORED_PATH_PATTERNS.some((pattern) => path.includes(pattern)) ||
|
|
80
|
-
hasIgnoredArtifactExtension(path)
|
|
89
|
+
hasIgnoredArtifactExtension(path) ||
|
|
90
|
+
hasIgnoredArtifactFileName(path);
|
|
81
91
|
}
|
|
82
92
|
/**
|
|
83
93
|
* Whether a path lives inside a generated build-output directory, evaluated
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../../src/src/server/dev-server/middleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAGxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAGtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAM7D,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAEnD,UAAU,qBAAqB;IAC7B,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAkFD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,kBAAkB,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../../src/src/server/dev-server/middleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAGxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAGtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAM7D,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAEnD,UAAU,qBAAqB;IAC7B,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAkFD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAqC/B;AA2LD,wBAAsB,eAAe,CACnC,QAAQ,EAAE,kBAAkB,EAC5B,MAAM,EAAE,eAAe,EACvB,cAAc,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,GAClD,OAAO,CAAC,IAAI,CAAC,CASf"}
|
|
@@ -85,12 +85,15 @@ export async function loadMiddlewareFile(projectDir, adapter, options = {}) {
|
|
|
85
85
|
continue;
|
|
86
86
|
try {
|
|
87
87
|
logger.debug(`Loading ${middlewareFile}`);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
// Transpile via the embedded esbuild before importing, for both FS kinds.
|
|
89
|
+
// A `deno compile` binary does not transpile an external on-disk `.ts` at
|
|
90
|
+
// import time, so a raw `import(middleware.ts)` throws on TS syntax and
|
|
91
|
+
// takes down every route. The virtual-FS path already transpiled; the
|
|
92
|
+
// real-FS path now does too (writing the output adjacent to the source so
|
|
93
|
+
// its bare/relative imports resolve identically to the original file).
|
|
94
|
+
return isVirtualFilesystem(adapter.fs)
|
|
95
|
+
? await loadMiddlewareFromVirtualFS(middlewarePath, adapter, options.throwOnError === true, middlewareFile)
|
|
96
|
+
: await loadMiddlewareFromRealFS(middlewarePath, adapter, options.throwOnError === true, middlewareFile);
|
|
94
97
|
}
|
|
95
98
|
catch (error) {
|
|
96
99
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -101,10 +104,12 @@ export async function loadMiddlewareFile(projectDir, adapter, options = {}) {
|
|
|
101
104
|
}
|
|
102
105
|
return [];
|
|
103
106
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Transpile a middleware source file to ESM JavaScript via the embedded esbuild.
|
|
109
|
+
* A `deno compile` binary cannot transpile an external `.ts` at import time, so
|
|
110
|
+
* middleware is always transpiled to JS before it is imported.
|
|
111
|
+
*/
|
|
112
|
+
async function transpileMiddlewareSource(source, middlewarePath) {
|
|
108
113
|
const loader = getEsbuildLoader(middlewarePath);
|
|
109
114
|
const { build } = await import("../../extensions/bundler/index.js");
|
|
110
115
|
const result = await build({
|
|
@@ -124,7 +129,13 @@ async function loadMiddlewareFromVirtualFS(middlewarePath, adapter, strictExport
|
|
|
124
129
|
if (firstError) {
|
|
125
130
|
throw COMPILATION_ERROR.create({ detail: `Failed to transpile middleware: ${firstError}` });
|
|
126
131
|
}
|
|
127
|
-
|
|
132
|
+
return result.outputFiles?.[0]?.text ?? "export default []";
|
|
133
|
+
}
|
|
134
|
+
async function loadMiddlewareFromVirtualFS(middlewarePath, adapter, strictExport, sourceFile = "middleware.ts") {
|
|
135
|
+
const fs = createFileSystem();
|
|
136
|
+
const content = await adapter.fs.readFile(middlewarePath);
|
|
137
|
+
const source = typeof content === "string" ? content : new TextDecoder().decode(content);
|
|
138
|
+
const js = await transpileMiddlewareSource(source, middlewarePath);
|
|
128
139
|
const tempDir = await fs.makeTempDir({ prefix: "vf-middleware-" });
|
|
129
140
|
const tempFile = join(tempDir, "middleware.mjs");
|
|
130
141
|
try {
|
|
@@ -136,6 +147,29 @@ async function loadMiddlewareFromVirtualFS(middlewarePath, adapter, strictExport
|
|
|
136
147
|
await fs.remove(tempDir, { recursive: true });
|
|
137
148
|
}
|
|
138
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Load a real-filesystem middleware file. Transpiles to ESM JS and imports the
|
|
152
|
+
* output from a temp file written ADJACENT to the source, so the middleware's
|
|
153
|
+
* bare (`veryfront/middleware`) and relative imports resolve exactly as they
|
|
154
|
+
* would when importing the original file. This is what lets a TypeScript root
|
|
155
|
+
* `middleware.ts` load under the compiled binary, where a raw `import()` of a
|
|
156
|
+
* `.ts` is not transpiled by the `deno compile` runtime.
|
|
157
|
+
*/
|
|
158
|
+
async function loadMiddlewareFromRealFS(middlewarePath, adapter, strictExport, sourceFile = "middleware.ts") {
|
|
159
|
+
const fs = createFileSystem();
|
|
160
|
+
const content = await adapter.fs.readFile(middlewarePath);
|
|
161
|
+
const source = typeof content === "string" ? content : new TextDecoder().decode(content);
|
|
162
|
+
const js = await transpileMiddlewareSource(source, middlewarePath);
|
|
163
|
+
const tempFile = join(dirname(middlewarePath), `.vf-middleware-${dntShim.crypto.randomUUID()}.mjs`);
|
|
164
|
+
try {
|
|
165
|
+
await fs.writeTextFile(tempFile, js);
|
|
166
|
+
const middlewareModule = await import(`file://${tempFile}?v=${Date.now()}`);
|
|
167
|
+
return normalizeMiddlewareExport(middlewareModule, strictExport, sourceFile);
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
await fs.remove(tempFile).catch(() => { });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
139
173
|
/** Describe the shape of the default export that was rejected. */
|
|
140
174
|
function describeDefaultExport(exported) {
|
|
141
175
|
if (Array.isArray(exported)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"esbuild-plugins.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/handlers/dev/files/esbuild-plugins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA6B,MAAM,EAAe,MAAM,yCAAyC,CAAC;AAG9G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAS5E,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,sCAAsC,CAAC;AAgC9C,MAAM,WAAW,uBAAuB;IACtC,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC;AASD,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAC;AAE5E,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,uBAAuB,CAAC,CAmClC;AAoBD,gFAAgF;AAChF,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,uBAA4B,GACpC,MAAM,CA2KR;AAKD,UAAU,yBAAyB;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C;
|
|
1
|
+
{"version":3,"file":"esbuild-plugins.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/handlers/dev/files/esbuild-plugins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA6B,MAAM,EAAe,MAAM,yCAAyC,CAAC;AAG9G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAS5E,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,sCAAsC,CAAC;AAgC9C,MAAM,WAAW,uBAAuB;IACtC,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC;AASD,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAC;AAE5E,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,uBAAuB,CAAC,CAmClC;AAoBD,gFAAgF;AAChF,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,uBAA4B,GACpC,MAAM,CA2KR;AAKD,UAAU,yBAAyB;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C;AAuED,kFAAkF;AAClF,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,yBAAyB,GAAG,OAAe,GACnD,MAAM,CAqFR;AAED,wBAAgB,wBAAwB,IAAI,MAAM,CAUjD"}
|
|
@@ -236,6 +236,13 @@ function isBareImport(path) {
|
|
|
236
236
|
!path.startsWith("http://") &&
|
|
237
237
|
!path.startsWith("https://"));
|
|
238
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* A Node built-in module specifier (`node:crypto`, `node:fs`, …). These are
|
|
241
|
+
* server-only and must never be rewritten to an esm.sh URL for a browser bundle.
|
|
242
|
+
*/
|
|
243
|
+
function isNodeBuiltinSpecifier(path) {
|
|
244
|
+
return path === "node" || path.startsWith("node:");
|
|
245
|
+
}
|
|
239
246
|
function toEsmUrl(path) {
|
|
240
247
|
return ESM_PACKAGE_MAP[path] ?? `https://esm.sh/${path}`;
|
|
241
248
|
}
|
|
@@ -292,6 +299,21 @@ export function createBareExternalPlugin(options = false) {
|
|
|
292
299
|
return undefined;
|
|
293
300
|
if (args.kind !== "import-statement" && args.kind !== "dynamic-import")
|
|
294
301
|
return undefined;
|
|
302
|
+
// Fail closed on Node built-ins. This plugin only runs for browser
|
|
303
|
+
// bundles (platform: "browser"), where a server-only `node:*` import can
|
|
304
|
+
// never work: rewriting it to an esm.sh URL silently ships a module that
|
|
305
|
+
// 404s on esm.sh and throws (e.g. `createHash is not a function`) at
|
|
306
|
+
// hydration. Surface a clear build error instead of a broken rewrite.
|
|
307
|
+
if (isNodeBuiltinSpecifier(args.path)) {
|
|
308
|
+
return {
|
|
309
|
+
errors: [{
|
|
310
|
+
text: `Cannot bundle server-only import "${args.path}" for the browser. ` +
|
|
311
|
+
`Node built-in modules are not available on the client. Move it into a ` +
|
|
312
|
+
`server component, an API route, or middleware — or gate the code behind a ` +
|
|
313
|
+
`"use client" boundary that does not import it.`,
|
|
314
|
+
}],
|
|
315
|
+
};
|
|
316
|
+
}
|
|
295
317
|
// Keep import-map-resolved specifiers as bare externals — the browser's
|
|
296
318
|
// <script type="importmap"> resolves them to the correct CDN URL.
|
|
297
319
|
if (importMapOwnsSpecifier(args.path, importMapImports)) {
|
|
@@ -239,16 +239,39 @@ function refreshStalePageData(cacheKey, resolve, cachePolicy) {
|
|
|
239
239
|
}
|
|
240
240
|
});
|
|
241
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* Only http(s) and root-relative destinations may be encoded for the client to
|
|
244
|
+
* follow. The client follows the destination with `window.location.href`, which
|
|
245
|
+
* would EXECUTE a `javascript:`/`data:` URL — unlike the full-page 302 path where
|
|
246
|
+
* the browser ignores such a Location. Protocol-relative `//host` is rejected too
|
|
247
|
+
* (it is easy to smuggle past a naive "starts with /" check). Blocked
|
|
248
|
+
* destinations fall through to normal error handling instead of being followed.
|
|
249
|
+
*/
|
|
250
|
+
function isFollowableRedirect(destination) {
|
|
251
|
+
if (destination.startsWith("/")) {
|
|
252
|
+
const baseOrigin = "https://veryfront.local";
|
|
253
|
+
try {
|
|
254
|
+
return new URL(destination, baseOrigin).origin === baseOrigin;
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return /^https?:\/\//i.test(destination);
|
|
261
|
+
}
|
|
242
262
|
/**
|
|
243
263
|
* A redirect() from getServerData is thrown up the pipeline as a VeryfrontError
|
|
244
264
|
* whose `context.redirect` holds the destination (mirrors extractRedirectLocation
|
|
245
|
-
* in the SSR service). Returns null for any other error
|
|
265
|
+
* in the SSR service). Returns null for any other error, or for a redirect whose
|
|
266
|
+
* destination is not safe to hand to the client to follow.
|
|
246
267
|
*/
|
|
247
268
|
function extractRedirectFromError(error) {
|
|
248
269
|
const context = error?.context;
|
|
249
270
|
const redirect = context?.redirect;
|
|
250
271
|
if (!redirect || typeof redirect.destination !== "string")
|
|
251
272
|
return null;
|
|
273
|
+
if (!isFollowableRedirect(redirect.destination))
|
|
274
|
+
return null;
|
|
252
275
|
return { destination: redirect.destination, permanent: redirect.permanent === true };
|
|
253
276
|
}
|
|
254
277
|
function buildPageDataCacheKey(ctx, slug, url) {
|