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.
Files changed (80) hide show
  1. package/esm/deno.js +1 -1
  2. package/esm/src/data/helpers.d.ts +21 -0
  3. package/esm/src/data/helpers.d.ts.map +1 -1
  4. package/esm/src/data/helpers.js +35 -0
  5. package/esm/src/data/server-data-fetcher.d.ts.map +1 -1
  6. package/esm/src/data/server-data-fetcher.js +17 -3
  7. package/esm/src/data/static-data-fetcher.d.ts.map +1 -1
  8. package/esm/src/data/static-data-fetcher.js +15 -2
  9. package/esm/src/html/hydration-script-builder/templates/renderer.d.ts.map +1 -1
  10. package/esm/src/html/hydration-script-builder/templates/renderer.js +36 -2
  11. package/esm/src/internal-agents/run-stream.d.ts.map +1 -1
  12. package/esm/src/internal-agents/run-stream.js +8 -6
  13. package/esm/src/rendering/orchestrator/module-loader/dependency-resolver.d.ts +2 -0
  14. package/esm/src/rendering/orchestrator/module-loader/dependency-resolver.d.ts.map +1 -1
  15. package/esm/src/rendering/orchestrator/module-loader/dependency-resolver.js +17 -16
  16. package/esm/src/routing/api/context-builder.d.ts +23 -1
  17. package/esm/src/routing/api/context-builder.d.ts.map +1 -1
  18. package/esm/src/routing/api/context-builder.js +22 -1
  19. package/esm/src/routing/api/handler.d.ts +11 -1
  20. package/esm/src/routing/api/handler.d.ts.map +1 -1
  21. package/esm/src/routing/api/handler.js +46 -12
  22. package/esm/src/routing/api/module-loader/loader.d.ts +5 -0
  23. package/esm/src/routing/api/module-loader/loader.d.ts.map +1 -1
  24. package/esm/src/routing/api/module-loader/loader.js +37 -2
  25. package/esm/src/server/dev-server/middleware.d.ts.map +1 -1
  26. package/esm/src/server/dev-server/middleware.js +35 -7
  27. package/esm/src/server/handlers/request/ssr/error-page-fallback.js +20 -3
  28. package/esm/src/server/handlers/request/ssr/ssr.handler.d.ts +0 -11
  29. package/esm/src/server/handlers/request/ssr/ssr.handler.d.ts.map +1 -1
  30. package/esm/src/server/handlers/request/ssr/ssr.handler.js +23 -1
  31. package/esm/src/tool/host-tools.d.ts.map +1 -1
  32. package/esm/src/tool/host-tools.js +3 -1
  33. package/esm/src/transforms/esm/degraded-artifact.d.ts +15 -0
  34. package/esm/src/transforms/esm/degraded-artifact.d.ts.map +1 -0
  35. package/esm/src/transforms/esm/degraded-artifact.js +22 -0
  36. package/esm/src/transforms/esm/http-cache.d.ts.map +1 -1
  37. package/esm/src/transforms/esm/http-cache.js +47 -17
  38. package/esm/src/transforms/esm/import-parser.d.ts.map +1 -1
  39. package/esm/src/transforms/esm/import-parser.js +47 -1
  40. package/esm/src/transforms/esm/specifier-resolver.d.ts +21 -4
  41. package/esm/src/transforms/esm/specifier-resolver.d.ts.map +1 -1
  42. package/esm/src/transforms/esm/specifier-resolver.js +43 -23
  43. package/esm/src/transforms/esm/transform-utils.d.ts +12 -0
  44. package/esm/src/transforms/esm/transform-utils.d.ts.map +1 -1
  45. package/esm/src/transforms/esm/transform-utils.js +10 -0
  46. package/esm/src/transforms/import-rewriter/strategies/asset-strategy.d.ts +23 -0
  47. package/esm/src/transforms/import-rewriter/strategies/asset-strategy.d.ts.map +1 -0
  48. package/esm/src/transforms/import-rewriter/strategies/asset-strategy.js +49 -0
  49. package/esm/src/transforms/import-rewriter/strategies/index.d.ts +1 -0
  50. package/esm/src/transforms/import-rewriter/strategies/index.d.ts.map +1 -1
  51. package/esm/src/transforms/import-rewriter/strategies/index.js +1 -0
  52. package/esm/src/transforms/import-rewriter/unified-rewriter.d.ts.map +1 -1
  53. package/esm/src/transforms/import-rewriter/unified-rewriter.js +2 -0
  54. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/index.d.ts.map +1 -1
  55. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/index.js +2 -1
  56. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.d.ts +19 -0
  57. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.d.ts.map +1 -1
  58. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.js +23 -1
  59. package/esm/src/transforms/mdx/esm-module-loader/utils/source-spans.d.ts +11 -0
  60. package/esm/src/transforms/mdx/esm-module-loader/utils/source-spans.d.ts.map +1 -1
  61. package/esm/src/transforms/mdx/esm-module-loader/utils/source-spans.js +74 -0
  62. package/esm/src/transforms/pipeline/index.d.ts.map +1 -1
  63. package/esm/src/transforms/pipeline/index.js +3 -1
  64. package/esm/src/transforms/pipeline/stages/browser-node-builtin-imports.d.ts +33 -0
  65. package/esm/src/transforms/pipeline/stages/browser-node-builtin-imports.d.ts.map +1 -0
  66. package/esm/src/transforms/pipeline/stages/browser-node-builtin-imports.js +116 -0
  67. package/esm/src/transforms/pipeline/stages/browser-server-exports-strip.d.ts +45 -0
  68. package/esm/src/transforms/pipeline/stages/browser-server-exports-strip.d.ts.map +1 -0
  69. package/esm/src/transforms/pipeline/stages/browser-server-exports-strip.js +300 -0
  70. package/esm/src/transforms/pipeline/stages/compile.d.ts.map +1 -1
  71. package/esm/src/transforms/pipeline/stages/compile.js +2 -1
  72. package/esm/src/transforms/pipeline/stages/index.d.ts +2 -0
  73. package/esm/src/transforms/pipeline/stages/index.d.ts.map +1 -1
  74. package/esm/src/transforms/pipeline/stages/index.js +2 -0
  75. package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.d.ts +11 -0
  76. package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.d.ts.map +1 -1
  77. package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.js +17 -0
  78. package/esm/src/utils/version-constant.d.ts +1 -1
  79. package/esm/src/utils/version-constant.js +1 -1
  80. package/package.json +5 -5
package/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.1099",
3
+ "version": "0.1.1101",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "minimumDependencyAge": {
@@ -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"}
@@ -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;AAaxE;;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;IA8EtB;;OAEG;YACW,aAAa;IAqE3B;;;OAGG;IACH,OAAO,CAAC,QAAQ;CAGjB"}
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(() => withTimeoutThrow(useIsolation
30
- ? this.fetchIsolated(options.modulePath, options.projectDir, context)
31
- : Promise.resolve(pageModule.getServerData(context)), DATA_FETCH_TIMEOUT_MS, `getServerData for ${pathname}`));
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;AAC7D,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;IAM/B,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,eAAe;YAQT,iBAAiB;YA+BjB,UAAU;YAqEV,sBAAsB;IAoEpC;;;;;OAKG;IACH,OAAO,CAAC,QAAQ;CAIjB"}
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
- return withTimeoutThrow(Promise.resolve(getStaticData(this.createStaticDataContext(context))), timeoutMs, label);
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,cA0P7B,CAAC"}
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
- if (pageSlug === 'index' || pageSlug.endsWith('/index')) throw error;
119
- pageModule = await import(basePath + '/index.js');
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,EAEL,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;AAyVD,wBAAsB,gCAAgC,CACpD,KAAK,EAAE,oBAAoB,EAC3B,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,+BAA+B,GACpC,OAAO,CAAC,QAAQ,CAAC,CA2anB"}
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 bash = sandboxResult.tools[PROJECT_AGENT_SANDBOX_BASH_TOOL_NAME];
214
- if (!bash) {
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;AAczE,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;CACrB,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;AAgGD,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,CAQR"}
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
- return findStaticImportFromSpans(fileContent, (specifier) => specifier.startsWith("@/") ? specifier : null).map(({ original, path, start, end }) => ({
8
- full: original,
9
- path,
10
- start,
11
- end,
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
- return findStaticImportFromSpans(fileContent, (specifier) => specifier.match(/^(\.\.?\/[^?]+)(?:\?.*)?$/)?.[1])
16
- .map(({ original, path, start, end }) => ({
17
- full: original,
18
- path,
19
- fromDir: fileDir,
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
- replacement: `from "file://${dep.depTempPath}"`,
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
- json: (data: unknown, init?: ResponseInit) => Response;
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,KAAK,QAAQ,CAAC;IACvD,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,QAAQ,CAAC;IACtD,EAAE,EAAE,iBAAiB,CAAC;CACvB;AAgBD,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"}
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 = (data, init) => createResponse(JSON.stringify(data), "application/json", init);
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;AAErC;;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;AAE3E,qBAAa,eAAe;IAmBxB,OAAO,CAAC,UAAU;IAlBpB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,UAAU,CAA6E;IAC/F,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,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;IA2FxE,OAAO,CAAC,WAAW;IAuCnB,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"}
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
- logger.error(`handler module failed to load: ${match.route.page}`);
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
- return internalServerError("Handler not found");
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
- if (handler && Object.keys(handler).length > 0) {
172
- this.routeCache.set(modulePath, handler);
173
- }
174
- return handler && Object.keys(handler).length > 0 ? handler : null;
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"}