veryfront 0.1.1099 → 0.1.1101
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/deno.js +1 -1
- package/esm/src/data/helpers.d.ts +21 -0
- package/esm/src/data/helpers.d.ts.map +1 -1
- package/esm/src/data/helpers.js +35 -0
- package/esm/src/data/server-data-fetcher.d.ts.map +1 -1
- package/esm/src/data/server-data-fetcher.js +17 -3
- package/esm/src/data/static-data-fetcher.d.ts.map +1 -1
- package/esm/src/data/static-data-fetcher.js +15 -2
- package/esm/src/html/hydration-script-builder/templates/renderer.d.ts.map +1 -1
- package/esm/src/html/hydration-script-builder/templates/renderer.js +36 -2
- package/esm/src/internal-agents/run-stream.d.ts.map +1 -1
- package/esm/src/internal-agents/run-stream.js +8 -6
- package/esm/src/rendering/orchestrator/module-loader/dependency-resolver.d.ts +2 -0
- package/esm/src/rendering/orchestrator/module-loader/dependency-resolver.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/module-loader/dependency-resolver.js +17 -16
- package/esm/src/routing/api/context-builder.d.ts +23 -1
- package/esm/src/routing/api/context-builder.d.ts.map +1 -1
- package/esm/src/routing/api/context-builder.js +22 -1
- package/esm/src/routing/api/handler.d.ts +11 -1
- package/esm/src/routing/api/handler.d.ts.map +1 -1
- package/esm/src/routing/api/handler.js +46 -12
- package/esm/src/routing/api/module-loader/loader.d.ts +5 -0
- package/esm/src/routing/api/module-loader/loader.d.ts.map +1 -1
- package/esm/src/routing/api/module-loader/loader.js +37 -2
- package/esm/src/server/dev-server/middleware.d.ts.map +1 -1
- package/esm/src/server/dev-server/middleware.js +35 -7
- package/esm/src/server/handlers/request/ssr/error-page-fallback.js +20 -3
- package/esm/src/server/handlers/request/ssr/ssr.handler.d.ts +0 -11
- package/esm/src/server/handlers/request/ssr/ssr.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/request/ssr/ssr.handler.js +23 -1
- package/esm/src/tool/host-tools.d.ts.map +1 -1
- package/esm/src/tool/host-tools.js +3 -1
- package/esm/src/transforms/esm/degraded-artifact.d.ts +15 -0
- package/esm/src/transforms/esm/degraded-artifact.d.ts.map +1 -0
- package/esm/src/transforms/esm/degraded-artifact.js +22 -0
- package/esm/src/transforms/esm/http-cache.d.ts.map +1 -1
- package/esm/src/transforms/esm/http-cache.js +47 -17
- package/esm/src/transforms/esm/import-parser.d.ts.map +1 -1
- package/esm/src/transforms/esm/import-parser.js +47 -1
- package/esm/src/transforms/esm/specifier-resolver.d.ts +21 -4
- package/esm/src/transforms/esm/specifier-resolver.d.ts.map +1 -1
- package/esm/src/transforms/esm/specifier-resolver.js +43 -23
- package/esm/src/transforms/esm/transform-utils.d.ts +12 -0
- package/esm/src/transforms/esm/transform-utils.d.ts.map +1 -1
- package/esm/src/transforms/esm/transform-utils.js +10 -0
- package/esm/src/transforms/import-rewriter/strategies/asset-strategy.d.ts +23 -0
- package/esm/src/transforms/import-rewriter/strategies/asset-strategy.d.ts.map +1 -0
- package/esm/src/transforms/import-rewriter/strategies/asset-strategy.js +49 -0
- package/esm/src/transforms/import-rewriter/strategies/index.d.ts +1 -0
- package/esm/src/transforms/import-rewriter/strategies/index.d.ts.map +1 -1
- package/esm/src/transforms/import-rewriter/strategies/index.js +1 -0
- package/esm/src/transforms/import-rewriter/unified-rewriter.d.ts.map +1 -1
- package/esm/src/transforms/import-rewriter/unified-rewriter.js +2 -0
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/index.d.ts.map +1 -1
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/index.js +2 -1
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.d.ts +19 -0
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.d.ts.map +1 -1
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.js +23 -1
- package/esm/src/transforms/mdx/esm-module-loader/utils/source-spans.d.ts +11 -0
- package/esm/src/transforms/mdx/esm-module-loader/utils/source-spans.d.ts.map +1 -1
- package/esm/src/transforms/mdx/esm-module-loader/utils/source-spans.js +74 -0
- package/esm/src/transforms/pipeline/index.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/index.js +3 -1
- package/esm/src/transforms/pipeline/stages/browser-node-builtin-imports.d.ts +33 -0
- package/esm/src/transforms/pipeline/stages/browser-node-builtin-imports.d.ts.map +1 -0
- package/esm/src/transforms/pipeline/stages/browser-node-builtin-imports.js +116 -0
- package/esm/src/transforms/pipeline/stages/browser-server-exports-strip.d.ts +45 -0
- package/esm/src/transforms/pipeline/stages/browser-server-exports-strip.d.ts.map +1 -0
- package/esm/src/transforms/pipeline/stages/browser-server-exports-strip.js +300 -0
- package/esm/src/transforms/pipeline/stages/compile.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/stages/compile.js +2 -1
- package/esm/src/transforms/pipeline/stages/index.d.ts +2 -0
- package/esm/src/transforms/pipeline/stages/index.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/stages/index.js +2 -0
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.d.ts +11 -0
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.js +17 -0
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +5 -5
package/esm/deno.js
CHANGED
|
@@ -3,4 +3,25 @@ import type { DataResult } from "./types.js";
|
|
|
3
3
|
export declare function redirect(destination: string, permanent?: boolean): DataResult;
|
|
4
4
|
/** Return a 404 result from a data loader. */
|
|
5
5
|
export declare function notFound(): DataResult;
|
|
6
|
+
/**
|
|
7
|
+
* True when `value` is a control-flow result produced by {@link notFound} or
|
|
8
|
+
* {@link redirect}.
|
|
9
|
+
*
|
|
10
|
+
* These helpers are documented as return values, but `throw notFound()` reads
|
|
11
|
+
* naturally and is what people coming from other frameworks reach for. Thrown,
|
|
12
|
+
* the plain object is not an `Error`, so the SSR error handler stringified it
|
|
13
|
+
* to `[object Object]` and returned a 500 instead of the intended 404 or
|
|
14
|
+
* redirect. Recognising the shape lets a thrown result behave like a returned
|
|
15
|
+
* one.
|
|
16
|
+
*/
|
|
17
|
+
export declare function isDataControlResult(value: unknown): value is DataResult;
|
|
18
|
+
/**
|
|
19
|
+
* Reduce a thrown control result to the shape a returned one produces.
|
|
20
|
+
*
|
|
21
|
+
* Callers apply this inside whatever wraps the data loader, not in an outer
|
|
22
|
+
* `catch`. A 404 is a routing decision, and a circuit breaker that sees it as a
|
|
23
|
+
* failure will open on the fifth legitimate one and fail every later data route
|
|
24
|
+
* for the project.
|
|
25
|
+
*/
|
|
26
|
+
export declare function toDataControlResult(result: DataResult): DataResult;
|
|
6
27
|
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/src/data/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,mDAAmD;AACnD,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,UAAU,CAE3E;AAED,8CAA8C;AAC9C,wBAAgB,QAAQ,IAAI,UAAU,CAErC"}
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/src/data/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,mDAAmD;AACnD,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,UAAU,CAE3E;AAED,8CAA8C;AAC9C,wBAAgB,QAAQ,IAAI,UAAU,CAErC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAUvE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAGlE"}
|
package/esm/src/data/helpers.js
CHANGED
|
@@ -6,3 +6,38 @@ export function redirect(destination, permanent = false) {
|
|
|
6
6
|
export function notFound() {
|
|
7
7
|
return { notFound: true };
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* True when `value` is a control-flow result produced by {@link notFound} or
|
|
11
|
+
* {@link redirect}.
|
|
12
|
+
*
|
|
13
|
+
* These helpers are documented as return values, but `throw notFound()` reads
|
|
14
|
+
* naturally and is what people coming from other frameworks reach for. Thrown,
|
|
15
|
+
* the plain object is not an `Error`, so the SSR error handler stringified it
|
|
16
|
+
* to `[object Object]` and returned a 500 instead of the intended 404 or
|
|
17
|
+
* redirect. Recognising the shape lets a thrown result behave like a returned
|
|
18
|
+
* one.
|
|
19
|
+
*/
|
|
20
|
+
export function isDataControlResult(value) {
|
|
21
|
+
if (value === null || typeof value !== "object")
|
|
22
|
+
return false;
|
|
23
|
+
if (value instanceof Error)
|
|
24
|
+
return false;
|
|
25
|
+
const candidate = value;
|
|
26
|
+
if (candidate.notFound === true)
|
|
27
|
+
return true;
|
|
28
|
+
const destination = candidate.redirect?.destination;
|
|
29
|
+
return typeof destination === "string";
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Reduce a thrown control result to the shape a returned one produces.
|
|
33
|
+
*
|
|
34
|
+
* Callers apply this inside whatever wraps the data loader, not in an outer
|
|
35
|
+
* `catch`. A 404 is a routing decision, and a circuit breaker that sees it as a
|
|
36
|
+
* failure will open on the fifth legitimate one and fail every later data route
|
|
37
|
+
* for the project.
|
|
38
|
+
*/
|
|
39
|
+
export function toDataControlResult(result) {
|
|
40
|
+
if (result.redirect)
|
|
41
|
+
return { redirect: result.redirect };
|
|
42
|
+
return { notFound: true };
|
|
43
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-data-fetcher.d.ts","sourceRoot":"","sources":["../../../src/src/data/server-data-fetcher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"server-data-fetcher.d.ts","sourceRoot":"","sources":["../../../src/src/data/server-data-fetcher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAcxE;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,iBAAiB;IAC5B,KAAK,CACH,UAAU,EAAE,YAAY,EACxB,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,UAAU,CAAC;IAuFtB;;OAEG;YACW,aAAa;IAqE3B;;;OAGG;IACH,OAAO,CAAC,QAAQ;CAGjB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as dntShim from "../../_dnt.shims.js";
|
|
2
|
+
import { isDataControlResult, toDataControlResult } from "./helpers.js";
|
|
2
3
|
import { serverLogger } from "../utils/index.js";
|
|
3
4
|
import { DATA_FETCH_TIMEOUT_MS } from "../config/defaults.js";
|
|
4
5
|
import { TimeoutError, withTimeoutThrow } from "../rendering/utils/stream-utils.js";
|
|
@@ -26,9 +27,22 @@ export class ServerDataFetcher {
|
|
|
26
27
|
return withSpan("data.fetch_server", async () => {
|
|
27
28
|
const start = performance.now();
|
|
28
29
|
try {
|
|
29
|
-
const result = await circuitBreaker.execute(() =>
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
const result = await circuitBreaker.execute(async () => {
|
|
31
|
+
try {
|
|
32
|
+
return await withTimeoutThrow(useIsolation
|
|
33
|
+
? this.fetchIsolated(options.modulePath, options.projectDir, context)
|
|
34
|
+
: Promise.resolve(pageModule.getServerData(context)), DATA_FETCH_TIMEOUT_MS, `getServerData for ${pathname}`);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
// `throw notFound()` / `throw redirect(...)`: treat a thrown
|
|
38
|
+
// control result exactly like a returned one. This has to happen
|
|
39
|
+
// inside the breaker, or five legitimate 404s on the same project
|
|
40
|
+
// open it and every data route after that fails fast for 30s.
|
|
41
|
+
if (isDataControlResult(error))
|
|
42
|
+
return toDataControlResult(error);
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
32
46
|
if (result.redirect)
|
|
33
47
|
return { redirect: result.redirect };
|
|
34
48
|
if (result.notFound)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"static-data-fetcher.d.ts","sourceRoot":"","sources":["../../../src/src/data/static-data-fetcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"static-data-fetcher.d.ts","sourceRoot":"","sources":["../../../src/src/data/static-data-fetcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA2DxE,qBAAa,iBAAiB;IAGhB,OAAO,CAAC,YAAY;IAFhC,OAAO,CAAC,oBAAoB,CAAoC;gBAE5C,YAAY,EAAE,YAAY;IAExC,KAAK,CAAC,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IA4ChF,OAAO,CAAC,uBAAuB;YAMjB,iBAAiB;IAuB/B,OAAO,CAAC,eAAe;YAQT,iBAAiB;YA+BjB,UAAU;YAqEV,sBAAsB;IAoEpC;;;;;OAKG;IACH,OAAO,CAAC,QAAQ;CAIjB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isDataControlResult, toDataControlResult } from "./helpers.js";
|
|
1
2
|
import { serverLogger } from "../utils/index.js";
|
|
2
3
|
import { DATA_FETCH_TIMEOUT_MS } from "../config/defaults.js";
|
|
3
4
|
import { TimeoutError, withTimeoutThrow } from "../rendering/utils/stream-utils.js";
|
|
@@ -78,8 +79,20 @@ export class StaticDataFetcher {
|
|
|
78
79
|
createStaticDataContext(context) {
|
|
79
80
|
return { params: context.params, url: context.url };
|
|
80
81
|
}
|
|
81
|
-
executeStaticData(getStaticData, context, timeoutMs, label) {
|
|
82
|
-
|
|
82
|
+
async executeStaticData(getStaticData, context, timeoutMs, label) {
|
|
83
|
+
try {
|
|
84
|
+
return await withTimeoutThrow(Promise.resolve(getStaticData(this.createStaticDataContext(context))), timeoutMs, label);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
// `throw notFound()` / `throw redirect(...)`: treat a thrown control
|
|
88
|
+
// result exactly like a returned one. Normalising at the one place every
|
|
89
|
+
// path runs the handler covers the cached path as well as the preview
|
|
90
|
+
// one, and keeps a 404 from counting against the caller's circuit
|
|
91
|
+
// breaker.
|
|
92
|
+
if (isDataControlResult(error))
|
|
93
|
+
return toDataControlResult(error);
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
83
96
|
}
|
|
84
97
|
storeCacheEntry(cacheKey, result) {
|
|
85
98
|
this.cacheManager.set(cacheKey, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../../../src/src/html/hydration-script-builder/templates/renderer.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../../../src/src/html/hydration-script-builder/templates/renderer.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,cA4R7B,CAAC"}
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
export const getRendererScript = () => `
|
|
2
2
|
// Note: DEBUG, log, logError are defined in router.ts which loads first
|
|
3
3
|
|
|
4
|
+
// True when a dynamic import failed because the module could not be
|
|
5
|
+
// fetched (404 / network), as opposed to being fetched and then failing to
|
|
6
|
+
// link or evaluate. Browsers report the former as a TypeError with a
|
|
7
|
+
// "dynamically imported module" message; link failures are SyntaxErrors and
|
|
8
|
+
// evaluation failures are whatever the module threw.
|
|
9
|
+
function isModuleNotFoundError(error) {
|
|
10
|
+
if (!error) return false;
|
|
11
|
+
if (error instanceof SyntaxError) return false;
|
|
12
|
+
const message = String((error && error.message) || error);
|
|
13
|
+
return /(?:Failed to fetch|error loading|Importing a module script failed|Failed to load)/i
|
|
14
|
+
.test(message);
|
|
15
|
+
}
|
|
16
|
+
|
|
4
17
|
async function renderPage(pathname) {
|
|
5
18
|
const resolvedPathname = (() => {
|
|
6
19
|
const input = typeof pathname === 'string' ? pathname : window.location.pathname;
|
|
@@ -92,6 +105,8 @@ export const getRendererScript = () => `
|
|
|
92
105
|
};
|
|
93
106
|
}
|
|
94
107
|
|
|
108
|
+
let pageModuleError = null;
|
|
109
|
+
|
|
95
110
|
if (data.pagePath) {
|
|
96
111
|
const moduleUrl = shouldRenderRscClientPage
|
|
97
112
|
? '/_veryfront/rsc/module?rel=' + encodeURIComponent(data.pagePath)
|
|
@@ -101,6 +116,7 @@ export const getRendererScript = () => `
|
|
|
101
116
|
try {
|
|
102
117
|
pageModule = await import(moduleUrl);
|
|
103
118
|
} catch (error) {
|
|
119
|
+
pageModuleError = error;
|
|
104
120
|
logError('Failed to load page from hydration data:', error);
|
|
105
121
|
}
|
|
106
122
|
}
|
|
@@ -115,8 +131,26 @@ export const getRendererScript = () => `
|
|
|
115
131
|
try {
|
|
116
132
|
pageModule = await import(basePath + '.js');
|
|
117
133
|
} catch (error) {
|
|
118
|
-
|
|
119
|
-
|
|
134
|
+
pageModuleError = pageModuleError || error;
|
|
135
|
+
|
|
136
|
+
// Only retry at <route>/index.js when the module genuinely could not
|
|
137
|
+
// be found. If it was found but failed to link or evaluate, retrying
|
|
138
|
+
// a path that does not exist replaces a precise error ("does not
|
|
139
|
+
// provide an export named 'createHash'") with a misleading 404.
|
|
140
|
+
const canRetryAsIndex = isModuleNotFoundError(error) &&
|
|
141
|
+
pageSlug !== 'index' && !pageSlug.endsWith('/index');
|
|
142
|
+
|
|
143
|
+
if (!canRetryAsIndex) throw pageModuleError;
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
pageModule = await import(basePath + '/index.js');
|
|
147
|
+
} catch (indexError) {
|
|
148
|
+
// For a real <route>/index.tsx page, the first 404 was expected
|
|
149
|
+
// and this retry is the path that matters: if it reached a module
|
|
150
|
+
// and failed to link or evaluate, its error is the real one. Only
|
|
151
|
+
// when the retry 404s as well does the original describe more.
|
|
152
|
+
throw isModuleNotFoundError(indexError) ? pageModuleError : indexError;
|
|
153
|
+
}
|
|
120
154
|
}
|
|
121
155
|
}
|
|
122
156
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-stream.d.ts","sourceRoot":"","sources":["../../../src/src/internal-agents/run-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,KAAK,EACV,KAAK,YAAY,IAAI,OAAO,EAC5B,KAAK,aAAa,EAEnB,MAAM,mBAAmB,CAAC;AAY3B,OAAO,KAAK,EACV,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,qBAAqB,CAAC;AAU7B,OAAO,
|
|
1
|
+
{"version":3,"file":"run-stream.d.ts","sourceRoot":"","sources":["../../../src/src/internal-agents/run-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,KAAK,EACV,KAAK,YAAY,IAAI,OAAO,EAC5B,KAAK,aAAa,EAEnB,MAAM,mBAAmB,CAAC;AAY3B,OAAO,KAAK,EACV,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,qBAAqB,CAAC;AAU7B,OAAO,EAGL,KAAK,IAAI,EAGV,MAAM,kBAAkB,CAAC;AAe1B,OAAO,EAA0B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAsCxD,MAAM,WAAW,+BAA+B;IAC9C,cAAc,EAAE,sBAAsB,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC;IAC5C,mBAAmB,CAAC,EAAE;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,cAAc,CAAC,EAAE,+BAA+B,CAAC,gBAAgB,CAAC,CAAC;IACnE,8BAA8B,CAAC,EAAE,CAC/B,KAAK,EAAE,+BAA+B,KACnC,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAC7C,aAAa,CAAC,EAAE,CACd,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAClC;QACH,MAAM,EAAE,CACN,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,SAAS,CAAC,EAAE;YACV,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;SAC9C,EACD,aAAa,CAAC,EAAE,MAAM,EACtB,uBAAuB,CAAC,EAAE,MAAM,EAChC,WAAW,CAAC,EAAE,WAAW,KACtB,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;KAC1C,CAAC;CACH;AAoCD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,oBAAoB,EAC3B,cAAc,EAAE,sBAAsB,EACtC,2BAA2B,CAAC,EAAE,MAAM,EAAE,EACtC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;;cAsFrD;AA6VD,wBAAsB,gCAAgC,CACpD,KAAK,EAAE,oBAAoB,EAC3B,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,+BAA+B,GACpC,OAAO,CAAC,QAAQ,CAAC,CA2anB"}
|
|
@@ -10,7 +10,7 @@ import { importFirstPartyExtensionModule } from "../extensions/first-party-impor
|
|
|
10
10
|
import { SandboxShellToolsProviderName, } from "../extensions/sandbox/index.js";
|
|
11
11
|
import { resolveHostedRuntimeAllowedToolNames } from "../agent/hosted/runtime-essential-tools.js";
|
|
12
12
|
import { SKILL_TOOL_IDS } from "../skill/types.js";
|
|
13
|
-
import { isToolVisibleTo, toolRegistry, } from "../tool/index.js";
|
|
13
|
+
import { createToolsFromHostDefinitions, isToolVisibleTo, toolRegistry, } from "../tool/index.js";
|
|
14
14
|
import { addSpanEvent, setSpanAttributes, withSpan, } from "../observability/tracing/otlp-setup.js";
|
|
15
15
|
import { defineSchema, lazySchema } from "../schemas/index.js";
|
|
16
16
|
import { createStreamTransformState, finalizeRunEvents, formatAgUiEvent, mapRuntimeEventToAgUi, parseSseJsonEvents, } from "./ag-ui-sse.js";
|
|
@@ -210,15 +210,17 @@ async function buildProjectAgentSandboxTools(input) {
|
|
|
210
210
|
: {}),
|
|
211
211
|
getProjectId: () => sandboxConfig.projectId ?? input.deps.projectAgentSandbox?.projectId,
|
|
212
212
|
});
|
|
213
|
-
const
|
|
214
|
-
|
|
213
|
+
const declaredTools = input.agent.config.tools;
|
|
214
|
+
const materializedTools = createToolsFromHostDefinitions(sandboxResult.tools);
|
|
215
|
+
const configuredTools = isRecord(declaredTools)
|
|
216
|
+
? Object.fromEntries(Object.entries(materializedTools).filter(([toolName]) => declaredTools[toolName] === true))
|
|
217
|
+
: {};
|
|
218
|
+
if (!configuredTools[PROJECT_AGENT_SANDBOX_BASH_TOOL_NAME]) {
|
|
215
219
|
await sandboxResult.closeSandbox();
|
|
216
220
|
return {};
|
|
217
221
|
}
|
|
218
222
|
return {
|
|
219
|
-
tools:
|
|
220
|
-
[PROJECT_AGENT_SANDBOX_BASH_TOOL_NAME]: bash,
|
|
221
|
-
},
|
|
223
|
+
tools: configuredTools,
|
|
222
224
|
closeSandbox: sandboxResult.closeSandbox,
|
|
223
225
|
};
|
|
224
226
|
}
|
|
@@ -8,6 +8,8 @@ export type ResolvedModuleDependency = {
|
|
|
8
8
|
relativePath: string;
|
|
9
9
|
depFilePath: string | null;
|
|
10
10
|
isLocalLib: boolean;
|
|
11
|
+
/** True when discovered inside `import("…")` rather than a static import. */
|
|
12
|
+
isDynamic: boolean;
|
|
11
13
|
};
|
|
12
14
|
/** Resolved dependency after its source module has been transformed to a temp file. */
|
|
13
15
|
export type TransformedModuleDependency = ResolvedModuleDependency & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dependency-resolver.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/dependency-resolver.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"dependency-resolver.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/dependency-resolver.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AA4BzE,sEAAsE;AACtE,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,6EAA6E;IAC7E,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,uFAAuF;AACvF,MAAM,MAAM,2BAA2B,GAAG,wBAAwB,GAAG;IACnE,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,sEAAsE;AACtE,MAAM,WAAW,8BAA8B;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,cAAc,CAAC;CACzB;AA4GD,yEAAyE;AACzE,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAuBrC;AAED,2EAA2E;AAC3E,wBAAgB,gCAAgC,CAC9C,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,2BAA2B,EAAE,GAC7C,MAAM,CAUR"}
|
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
import { dirname, join, normalize } from "../../../platform/compat/path/index.js";
|
|
2
2
|
import { parallelMap, rendererLogger } from "../../../utils/index.js";
|
|
3
|
-
import { findStaticImportFromSpans, replaceSourceSpans, } from "../../../transforms/mdx/esm-module-loader/utils/source-spans.js";
|
|
3
|
+
import { findDynamicImportSpans, findStaticImportFromSpans, replaceSourceSpans, } from "../../../transforms/mdx/esm-module-loader/utils/source-spans.js";
|
|
4
4
|
import { findSourceFile } from "../file-resolver/index.js";
|
|
5
5
|
const logger = rendererLogger.component("module-loader");
|
|
6
|
+
const matchAlias = (specifier) => specifier.startsWith("@/") ? specifier : null;
|
|
7
|
+
const matchRelative = (specifier) => specifier.match(/^(\.\.?\/[^?]+)(?:\?.*)?$/)?.[1];
|
|
6
8
|
function collectAliasImports(fileContent) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}));
|
|
9
|
+
const toAlias = (isDynamic) => ({ original, path, start, end }) => ({ full: original, path, start, end, isDynamic });
|
|
10
|
+
return [
|
|
11
|
+
...findStaticImportFromSpans(fileContent, matchAlias).map(toAlias(false)),
|
|
12
|
+
...findDynamicImportSpans(fileContent, matchAlias).map(toAlias(true)),
|
|
13
|
+
];
|
|
13
14
|
}
|
|
14
15
|
function collectRelativeImports(fileContent, fileDir) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
start,
|
|
21
|
-
end,
|
|
22
|
-
}))
|
|
16
|
+
const toRelative = (isDynamic) => ({ original, path, start, end }) => ({ full: original, path, fromDir: fileDir, start, end, isDynamic });
|
|
17
|
+
return [
|
|
18
|
+
...findStaticImportFromSpans(fileContent, matchRelative).map(toRelative(false)),
|
|
19
|
+
...findDynamicImportSpans(fileContent, matchRelative).map(toRelative(true)),
|
|
20
|
+
]
|
|
23
21
|
// Ignore already-transformed file:// imports.
|
|
24
22
|
.filter((imp) => !imp.path.includes("file://"));
|
|
25
23
|
}
|
|
@@ -70,6 +68,7 @@ async function resolveRelativeImport(imp, adapter) {
|
|
|
70
68
|
relativePath: imp.path,
|
|
71
69
|
depFilePath,
|
|
72
70
|
isLocalLib: false,
|
|
71
|
+
isDynamic: imp.isDynamic,
|
|
73
72
|
};
|
|
74
73
|
}
|
|
75
74
|
/** Resolves @/ alias and relative local imports from a source module. */
|
|
@@ -94,7 +93,9 @@ export function rewriteResolvedDependencyImports(fileContent, transformedDeps) {
|
|
|
94
93
|
start: dep.start,
|
|
95
94
|
end: dep.end,
|
|
96
95
|
expected: dep.full,
|
|
97
|
-
|
|
96
|
+
// A dynamic span covers only the quoted specifier; a static one covers the
|
|
97
|
+
// whole `from "…"` clause.
|
|
98
|
+
replacement: dep.isDynamic ? `"file://${dep.depTempPath}"` : `from "file://${dep.depTempPath}"`,
|
|
98
99
|
}));
|
|
99
100
|
return replaceSourceSpans(fileContent, replacements);
|
|
100
101
|
}
|
|
@@ -11,10 +11,32 @@ export interface APIContext {
|
|
|
11
11
|
cookies: Record<string, string>;
|
|
12
12
|
headers: Headers;
|
|
13
13
|
url: URL;
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Read the request body as JSON, or build a JSON response.
|
|
16
|
+
*
|
|
17
|
+
* `ctx.json()` with no arguments parses and returns the request body.
|
|
18
|
+
* `ctx.json(data, init?)` returns a JSON `Response`.
|
|
19
|
+
*/
|
|
20
|
+
json: {
|
|
21
|
+
(): Promise<unknown>;
|
|
22
|
+
(data: unknown, init?: ResponseInit): Response;
|
|
23
|
+
};
|
|
15
24
|
text: (data: string, init?: ResponseInit) => Response;
|
|
16
25
|
fs: FileSystemAdapter;
|
|
17
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Build the `ctx.json` helper for a request.
|
|
29
|
+
*
|
|
30
|
+
* Overloaded on arity. `ctx.json(data)` builds a response; `ctx.json()` reads
|
|
31
|
+
* the request body, which is what handlers reach for, and what the zero-arg
|
|
32
|
+
* form was previously misread as. It used to stringify `undefined` into a
|
|
33
|
+
* Response, so `await ctx.json()` yielded a Response object that serialised
|
|
34
|
+
* back out as `{}` and silently dropped every posted payload.
|
|
35
|
+
*
|
|
36
|
+
* Exported because the isolation Worker builds its own context and has to
|
|
37
|
+
* behave identically. Handlers must not care which one ran them.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createJsonHelper(request: Request): APIContext["json"];
|
|
18
40
|
export declare function createContext(request: Request, match: RouteMatch, fs: FileSystemAdapter): APIContext;
|
|
19
41
|
/**
|
|
20
42
|
* @deprecated Use {@link flattenRouteParams} directly. Kept as a thin alias so
|
|
@@ -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;AAG3D,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,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,YAAY,
|
|
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;AAG3D,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;;;;;OAKG;IACH,IAAI,EAAE;QACJ,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;QACrB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,QAAQ,CAAC;KAChD,CAAC;IACF,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,QAAQ,CAAC;IACtD,EAAE,EAAE,iBAAiB,CAAC;CACvB;AAgBD;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAUrE;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"}
|
|
@@ -10,9 +10,30 @@ function createResponse(body, contentType, init) {
|
|
|
10
10
|
},
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Build the `ctx.json` helper for a request.
|
|
15
|
+
*
|
|
16
|
+
* Overloaded on arity. `ctx.json(data)` builds a response; `ctx.json()` reads
|
|
17
|
+
* the request body, which is what handlers reach for, and what the zero-arg
|
|
18
|
+
* form was previously misread as. It used to stringify `undefined` into a
|
|
19
|
+
* Response, so `await ctx.json()` yielded a Response object that serialised
|
|
20
|
+
* back out as `{}` and silently dropped every posted payload.
|
|
21
|
+
*
|
|
22
|
+
* Exported because the isolation Worker builds its own context and has to
|
|
23
|
+
* behave identically. Handlers must not care which one ran them.
|
|
24
|
+
*/
|
|
25
|
+
export function createJsonHelper(request) {
|
|
26
|
+
function json(...args) {
|
|
27
|
+
if (args.length === 0)
|
|
28
|
+
return request.json();
|
|
29
|
+
const [data, init] = args;
|
|
30
|
+
return createResponse(JSON.stringify(data), "application/json", init);
|
|
31
|
+
}
|
|
32
|
+
return json;
|
|
33
|
+
}
|
|
13
34
|
export function createContext(request, match, fs) {
|
|
14
35
|
const url = new URL(request.url);
|
|
15
|
-
const json = (
|
|
36
|
+
const json = createJsonHelper(request);
|
|
16
37
|
const text = (data, init) => createResponse(data, "text/plain", init);
|
|
17
38
|
return {
|
|
18
39
|
request,
|
|
@@ -6,6 +6,17 @@ import { loadHandlerModule } from "./module-loader/loader.js";
|
|
|
6
6
|
import { discoverAppRoutes, discoverPagesRoutes } from "./route-discovery.js";
|
|
7
7
|
import type { HandlerContext } from "../../types/index.js";
|
|
8
8
|
export type { APIContext, APIRoute };
|
|
9
|
+
/**
|
|
10
|
+
* Reduce a module load error to something safe to put in a response body.
|
|
11
|
+
*
|
|
12
|
+
* The message is worth returning, since it usually names a syntax or import
|
|
13
|
+
* error the developer can act on. The rest of it is not: a raw load error
|
|
14
|
+
* carries the temp directory the bundle was written to, absolute paths into the
|
|
15
|
+
* framework install, and a full stack trace. A path inside the project survives
|
|
16
|
+
* as a project-relative one, because that is the part that identifies the file
|
|
17
|
+
* to fix.
|
|
18
|
+
*/
|
|
19
|
+
export declare function sanitizeLoadErrorForResponse(message: string, projectDir?: string): string;
|
|
9
20
|
/**
|
|
10
21
|
* Injection interface for testing APIRouteHandler dependencies
|
|
11
22
|
*/
|
|
@@ -31,7 +42,6 @@ export declare class APIRouteHandler {
|
|
|
31
42
|
private projectDir;
|
|
32
43
|
private router;
|
|
33
44
|
private routeCache;
|
|
34
|
-
private lastErrorMessage;
|
|
35
45
|
private activeRequests;
|
|
36
46
|
private destroyRequested;
|
|
37
47
|
private destroyed;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../src/src/routing/api/handler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAMlD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAG9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAK3D,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../src/src/routing/api/handler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAMlD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAG9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAK3D,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;AAYrC;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAYzF;AAED;;GAEG;AACH,UAAU,mBAAmB;IAC3B,iBAAiB,CAAC,EAAE,OAAO,iBAAiB,CAAC;IAC7C,mBAAmB,CAAC,EAAE,OAAO,mBAAmB,CAAC;IACjD,iBAAiB,CAAC,EAAE,OAAO,iBAAiB,CAAC;IAC7C,SAAS,CAAC,EAAE,OAAO,SAAS,CAAC;CAC9B;AAID;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,GAAG,IAAI,CAE3E;AAWD,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED,iDAAiD;AACjD,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;AAW3E,qBAAa,eAAe;IAkBxB,OAAO,CAAC,UAAU;IAjBpB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,UAAU,CAA6E;IAC/F,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,SAAS,CAAS;IAE1B,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,UAAU,CAAqC;IACvD,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,iBAAiB,CAA8B;IAEvD,OAAO,CAAC,MAAM,CAAsD;IACpE,OAAO,CAAC,aAAa,CAA8B;gBAGzC,UAAU,EAAE,MAAM,EAC1B,OAAO,CAAC,EAAE,cAAc;IAM1B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAgD3B,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAwGxE,OAAO,CAAC,WAAW;IAsCnB,UAAU,IAAI,IAAI;IAKlB,OAAO,IAAI,IAAI;IAWf,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,UAAU;YAQJ,aAAa;YAsBb,gBAAgB;YAOhB,cAAc;YAcd,YAAY;YAOZ,cAAc;CAW7B;AAED,OAAO,EACL,UAAU,EACV,SAAS,EACT,mBAAmB,IAAI,WAAW,EAClC,YAAY,IAAI,IAAI,EACpB,QAAQ,EACR,gBAAgB,IAAI,QAAQ,EAC5B,YAAY,GACb,MAAM,yCAAyC,CAAC"}
|
|
@@ -12,6 +12,33 @@ import { executeAppRoute, executePagesRoute } from "./route-executor.js";
|
|
|
12
12
|
import { withSpan } from "../../observability/tracing/otlp-setup.js";
|
|
13
13
|
/** Max entries in the loaded-handler LRU cache */
|
|
14
14
|
const HANDLER_CACHE_MAX_ENTRIES = 256;
|
|
15
|
+
/** Longest sanitised load error a response body carries. */
|
|
16
|
+
const MAX_LOAD_ERROR_LENGTH = 300;
|
|
17
|
+
/**
|
|
18
|
+
* Roots that only ever identify the machine running the server: home
|
|
19
|
+
* directories and the temp directories bundling writes to.
|
|
20
|
+
*/
|
|
21
|
+
const MACHINE_PATH_PATTERN = /(?:file:\/\/)?\/(?:private\/)?(?:Users|home|root|var\/folders|var\/tmp|tmp)\/\S*/g;
|
|
22
|
+
/**
|
|
23
|
+
* Reduce a module load error to something safe to put in a response body.
|
|
24
|
+
*
|
|
25
|
+
* The message is worth returning, since it usually names a syntax or import
|
|
26
|
+
* error the developer can act on. The rest of it is not: a raw load error
|
|
27
|
+
* carries the temp directory the bundle was written to, absolute paths into the
|
|
28
|
+
* framework install, and a full stack trace. A path inside the project survives
|
|
29
|
+
* as a project-relative one, because that is the part that identifies the file
|
|
30
|
+
* to fix.
|
|
31
|
+
*/
|
|
32
|
+
export function sanitizeLoadErrorForResponse(message, projectDir) {
|
|
33
|
+
// A stack trace is never the actionable part.
|
|
34
|
+
let text = (message.split("\n")[0] ?? "").trim();
|
|
35
|
+
if (projectDir) {
|
|
36
|
+
const root = projectDir.replace(/\/+$/, "");
|
|
37
|
+
text = text.replaceAll(`file://${root}/`, "").replaceAll(`${root}/`, "");
|
|
38
|
+
}
|
|
39
|
+
text = text.replace(MACHINE_PATH_PATTERN, "<PATH>");
|
|
40
|
+
return text.length > MAX_LOAD_ERROR_LENGTH ? `${text.slice(0, MAX_LOAD_ERROR_LENGTH)}...` : text;
|
|
41
|
+
}
|
|
15
42
|
let injectedDeps = null;
|
|
16
43
|
/**
|
|
17
44
|
* Inject dependencies for testing. Pass null to reset to defaults.
|
|
@@ -31,7 +58,6 @@ export class APIRouteHandler {
|
|
|
31
58
|
projectDir;
|
|
32
59
|
router = new ApiRouteMatcher();
|
|
33
60
|
routeCache = new LRUCache({ maxEntries: HANDLER_CACHE_MAX_ENTRIES });
|
|
34
|
-
lastErrorMessage = null;
|
|
35
61
|
activeRequests = 0;
|
|
36
62
|
destroyRequested = false;
|
|
37
63
|
destroyed = false;
|
|
@@ -116,18 +142,27 @@ export class APIRouteHandler {
|
|
|
116
142
|
page: match.route.page,
|
|
117
143
|
params: match.params,
|
|
118
144
|
});
|
|
119
|
-
const handler = await this.loadHandler(match);
|
|
145
|
+
const { handler, errorMessage } = await this.loadHandler(match);
|
|
120
146
|
if (!handler) {
|
|
147
|
+
const msg = errorMessage ?? "Handler not found";
|
|
121
148
|
try {
|
|
122
|
-
|
|
149
|
+
// The full detail, paths and all, belongs in the log.
|
|
150
|
+
logger.error(`handler module failed to load: ${match.route.page}`, { reason: msg });
|
|
123
151
|
}
|
|
124
152
|
catch (e) {
|
|
125
153
|
logger.warn("API error log failed", e);
|
|
126
154
|
}
|
|
127
|
-
const msg = this.lastErrorMessage ?? "Handler not found";
|
|
128
155
|
if (msg.includes("Remote import blocked by allow-list"))
|
|
129
156
|
return badGateway(msg);
|
|
130
|
-
|
|
157
|
+
// The reason the module failed to load is the only useful thing here.
|
|
158
|
+
// Reporting a flat "Handler not found" for what is usually a syntax or
|
|
159
|
+
// import error sends people hunting for a routing problem that does
|
|
160
|
+
// not exist. Local development only, and sanitised: a raw load error
|
|
161
|
+
// carries temp directories, absolute paths and a stack trace, none of
|
|
162
|
+
// which belong in a response body.
|
|
163
|
+
return internalServerError(ctx?.isLocalProject
|
|
164
|
+
? sanitizeLoadErrorForResponse(msg, this.projectDir)
|
|
165
|
+
: "Handler not found");
|
|
131
166
|
}
|
|
132
167
|
// App Router routes are always named route.ts/js/tsx/jsx
|
|
133
168
|
// Pages Router routes have descriptive names like articles.ts
|
|
@@ -156,7 +191,7 @@ export class APIRouteHandler {
|
|
|
156
191
|
await this.ensureConfig(adapter);
|
|
157
192
|
const cached = this.routeCache.get(modulePath);
|
|
158
193
|
if (cached)
|
|
159
|
-
return cached;
|
|
194
|
+
return { handler: cached, errorMessage: null };
|
|
160
195
|
try {
|
|
161
196
|
const deps = getDeps();
|
|
162
197
|
const handler = await deps.loadHandlerModule({
|
|
@@ -168,16 +203,15 @@ export class APIRouteHandler {
|
|
|
168
203
|
// Only cache handlers that export at least one HTTP method.
|
|
169
204
|
// Empty objects ({}) from failed imports are truthy but useless —
|
|
170
205
|
// caching them would prevent retry after the user fixes the import.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
return
|
|
206
|
+
const usable = handler && Object.keys(handler).length > 0 ? handler : null;
|
|
207
|
+
if (usable)
|
|
208
|
+
this.routeCache.set(modulePath, usable);
|
|
209
|
+
return { handler: usable, errorMessage: null };
|
|
175
210
|
}
|
|
176
211
|
catch (error) {
|
|
177
212
|
const msg = error instanceof Error ? error.message : String(error);
|
|
178
|
-
this.lastErrorMessage = msg;
|
|
179
213
|
logger.error(`[API] Failed to load handler for ${modulePath}: ${msg}`);
|
|
180
|
-
return null;
|
|
214
|
+
return { handler: null, errorMessage: msg };
|
|
181
215
|
}
|
|
182
216
|
}, { "api.modulePath": modulePath });
|
|
183
217
|
}
|
|
@@ -2,4 +2,9 @@ import type { APIRoute, LoadModuleOptions } from "./types.js";
|
|
|
2
2
|
export { generateCompiledBinaryRequireShim, getNodeExternalPackagesToResolve, loadVeryfrontExportsMap, resolveEsmUserDependencies, resolveNodePackageToFileUrl, rewriteCompiledBinaryUserDependencyImports, rewriteCompiledBinaryVeryfrontImports, rewriteDenoNodeBuiltinImports, rewriteDenoNpmDependencyImports, rewriteNodeExternalImports, } from "./external-import-rewriter.js";
|
|
3
3
|
export { toCjsDestructureBindings } from "./loader-helpers.js";
|
|
4
4
|
export declare function loadHandlerModule(options: LoadModuleOptions): Promise<APIRoute | null>;
|
|
5
|
+
/**
|
|
6
|
+
* True when a module failed to load because Deno could not resolve one of its
|
|
7
|
+
* import specifiers, rather than because the module itself is broken.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isSpecifierResolutionError(error: unknown): boolean;
|
|
5
10
|
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../../../src/src/routing/api/module-loader/loader.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAkB9D,OAAO,EACL,iCAAiC,EACjC,gCAAgC,EAChC,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,EAC3B,0CAA0C,EAC1C,qCAAqC,EACrC,6BAA6B,EAC7B,+BAA+B,EAC/B,0BAA0B,GAC3B,MAAM,+BAA+B,CAAC;AAIvC,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAyBtF"}
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../../../src/src/routing/api/module-loader/loader.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAkB9D,OAAO,EACL,iCAAiC,EACjC,gCAAgC,EAChC,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,EAC3B,0CAA0C,EAC1C,qCAAqC,EACrC,6BAA6B,EAC7B,+BAA+B,EAC/B,0BAA0B,GAC3B,MAAM,+BAA+B,CAAC;AAIvC,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAyBtF;AA0DD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAIlE"}
|