veryfront 0.1.1102 → 0.1.1104

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 (126) hide show
  1. package/esm/deno.js +1 -1
  2. package/esm/src/agent/ag-ui/tool-shared.d.ts.map +1 -1
  3. package/esm/src/agent/ag-ui/tool-shared.js +0 -4
  4. package/esm/src/agent/factory.d.ts.map +1 -1
  5. package/esm/src/agent/factory.js +29 -31
  6. package/esm/src/agent/hosted/chat-preparation.d.ts +1 -0
  7. package/esm/src/agent/hosted/chat-preparation.d.ts.map +1 -1
  8. package/esm/src/agent/hosted/chat-preparation.js +20 -12
  9. package/esm/src/agent/hosted/default-project-steering-refresh.d.ts +1 -1
  10. package/esm/src/agent/hosted/default-project-steering-refresh.d.ts.map +1 -1
  11. package/esm/src/agent/hosted/default-project-steering-refresh.js +5 -9
  12. package/esm/src/agent/hosted/runtime-essential-tools.d.ts +1 -1
  13. package/esm/src/agent/hosted/runtime-essential-tools.d.ts.map +1 -1
  14. package/esm/src/agent/hosted/runtime-essential-tools.js +14 -5
  15. package/esm/src/agent/hosted/veryfront-cloud-agent-service.d.ts.map +1 -1
  16. package/esm/src/agent/hosted/veryfront-cloud-agent-service.js +4 -0
  17. package/esm/src/agent/index.d.ts +1 -1
  18. package/esm/src/agent/index.js +1 -1
  19. package/esm/src/agent/runtime/agent-definition.d.ts.map +1 -1
  20. package/esm/src/agent/runtime/agent-definition.js +3 -0
  21. package/esm/src/agent/runtime/agent-runtime-step.js +1 -1
  22. package/esm/src/agent/runtime/index.d.ts.map +1 -1
  23. package/esm/src/agent/runtime/index.js +0 -2
  24. package/esm/src/agent/runtime/skill-metadata.d.ts +11 -0
  25. package/esm/src/agent/runtime/skill-metadata.d.ts.map +1 -1
  26. package/esm/src/agent/runtime/skill-metadata.js +24 -0
  27. package/esm/src/agent/runtime/skill-prompt.d.ts.map +1 -1
  28. package/esm/src/agent/runtime/skill-prompt.js +5 -2
  29. package/esm/src/agent/types.d.ts +8 -6
  30. package/esm/src/agent/types.d.ts.map +1 -1
  31. package/esm/src/channels/control-plane.d.ts.map +1 -1
  32. package/esm/src/channels/control-plane.js +1 -4
  33. package/esm/src/data/helpers.d.ts +20 -3
  34. package/esm/src/data/helpers.d.ts.map +1 -1
  35. package/esm/src/data/helpers.js +46 -12
  36. package/esm/src/data/static-data-fetcher.d.ts.map +1 -1
  37. package/esm/src/data/static-data-fetcher.js +15 -0
  38. package/esm/src/extensions/builtin-extensions.d.ts +1 -1
  39. package/esm/src/extensions/builtin-extensions.d.ts.map +1 -1
  40. package/esm/src/extensions/builtin-extensions.js +1 -6
  41. package/esm/src/extensions/builtin-schema-validator.d.ts +3 -0
  42. package/esm/src/extensions/builtin-schema-validator.d.ts.map +1 -0
  43. package/esm/src/extensions/builtin-schema-validator.js +8 -0
  44. package/esm/src/html/hydration-script-builder/templates/renderer.d.ts.map +1 -1
  45. package/esm/src/html/hydration-script-builder/templates/renderer.js +52 -28
  46. package/esm/src/internal-agents/run-stream.d.ts.map +1 -1
  47. package/esm/src/internal-agents/run-stream.js +6 -11
  48. package/esm/src/internal-agents/run-system-prompt.d.ts +1 -1
  49. package/esm/src/internal-agents/run-system-prompt.js +1 -1
  50. package/esm/src/modules/server/module-server.d.ts.map +1 -1
  51. package/esm/src/modules/server/module-server.js +15 -9
  52. package/esm/src/rendering/orchestrator/module-loader/build-failure.d.ts +20 -0
  53. package/esm/src/rendering/orchestrator/module-loader/build-failure.d.ts.map +1 -0
  54. package/esm/src/rendering/orchestrator/module-loader/build-failure.js +26 -0
  55. package/esm/src/rendering/orchestrator/module-loader/index.d.ts +2 -1
  56. package/esm/src/rendering/orchestrator/module-loader/index.d.ts.map +1 -1
  57. package/esm/src/rendering/orchestrator/module-loader/index.js +69 -7
  58. package/esm/src/rendering/orchestrator/module-loader/module-persistence.d.ts +6 -0
  59. package/esm/src/rendering/orchestrator/module-loader/module-persistence.d.ts.map +1 -1
  60. package/esm/src/rendering/orchestrator/module-loader/module-persistence.js +49 -0
  61. package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
  62. package/esm/src/rendering/orchestrator/pipeline.js +10 -1
  63. package/esm/src/routing/api/context-builder.d.ts +26 -15
  64. package/esm/src/routing/api/context-builder.d.ts.map +1 -1
  65. package/esm/src/routing/api/context-builder.js +34 -15
  66. package/esm/src/routing/api/module-loader/loader.js +57 -1
  67. package/esm/src/server/dev-server/middleware.d.ts.map +1 -1
  68. package/esm/src/server/dev-server/middleware.js +29 -6
  69. package/esm/src/server/handlers/request/ssr/ssr.handler.d.ts.map +1 -1
  70. package/esm/src/server/handlers/request/ssr/ssr.handler.js +6 -4
  71. package/esm/src/skill/prompt-augmentation.d.ts +4 -2
  72. package/esm/src/skill/prompt-augmentation.d.ts.map +1 -1
  73. package/esm/src/skill/prompt-augmentation.js +13 -2
  74. package/esm/src/transforms/esm/import-attributes.d.ts +32 -0
  75. package/esm/src/transforms/esm/import-attributes.d.ts.map +1 -0
  76. package/esm/src/transforms/esm/import-attributes.js +116 -0
  77. package/esm/src/transforms/esm/import-parser.d.ts.map +1 -1
  78. package/esm/src/transforms/esm/import-parser.js +68 -18
  79. package/esm/src/transforms/esm/lexer.d.ts +18 -0
  80. package/esm/src/transforms/esm/lexer.d.ts.map +1 -1
  81. package/esm/src/transforms/esm/lexer.js +21 -0
  82. package/esm/src/transforms/esm/specifier-resolver.d.ts.map +1 -1
  83. package/esm/src/transforms/esm/specifier-resolver.js +13 -0
  84. package/esm/src/transforms/esm/transform-utils.d.ts +8 -0
  85. package/esm/src/transforms/esm/transform-utils.d.ts.map +1 -1
  86. package/esm/src/transforms/esm/transform-utils.js +11 -1
  87. package/esm/src/transforms/import-rewriter/project-paths.d.ts +26 -0
  88. package/esm/src/transforms/import-rewriter/project-paths.d.ts.map +1 -0
  89. package/esm/src/transforms/import-rewriter/project-paths.js +49 -0
  90. package/esm/src/transforms/import-rewriter/strategies/alias-strategy.d.ts +0 -1
  91. package/esm/src/transforms/import-rewriter/strategies/alias-strategy.d.ts.map +1 -1
  92. package/esm/src/transforms/import-rewriter/strategies/alias-strategy.js +5 -20
  93. package/esm/src/transforms/import-rewriter/strategies/asset-strategy.d.ts +1 -1
  94. package/esm/src/transforms/import-rewriter/strategies/asset-strategy.d.ts.map +1 -1
  95. package/esm/src/transforms/import-rewriter/strategies/asset-strategy.js +62 -12
  96. package/esm/src/transforms/import-rewriter/strategies/bare-strategy.d.ts.map +1 -1
  97. package/esm/src/transforms/import-rewriter/strategies/bare-strategy.js +19 -3
  98. package/esm/src/transforms/import-rewriter/strategies/node-builtin-strategy.d.ts +11 -0
  99. package/esm/src/transforms/import-rewriter/strategies/node-builtin-strategy.d.ts.map +1 -1
  100. package/esm/src/transforms/import-rewriter/strategies/node-builtin-strategy.js +13 -0
  101. package/esm/src/transforms/import-rewriter/strategies/relative-strategy.d.ts +0 -1
  102. package/esm/src/transforms/import-rewriter/strategies/relative-strategy.d.ts.map +1 -1
  103. package/esm/src/transforms/import-rewriter/strategies/relative-strategy.js +2 -16
  104. package/esm/src/transforms/import-rewriter/strategies/veryfront-strategy.d.ts.map +1 -1
  105. package/esm/src/transforms/import-rewriter/strategies/veryfront-strategy.js +4 -3
  106. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.d.ts +4 -2
  107. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.d.ts.map +1 -1
  108. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.js +23 -7
  109. package/esm/src/transforms/pipeline/stages/browser-node-builtin-imports.d.ts.map +1 -1
  110. package/esm/src/transforms/pipeline/stages/browser-node-builtin-imports.js +5 -0
  111. package/esm/src/transforms/pipeline/stages/browser-server-exports-strip.d.ts +25 -3
  112. package/esm/src/transforms/pipeline/stages/browser-server-exports-strip.d.ts.map +1 -1
  113. package/esm/src/transforms/pipeline/stages/browser-server-exports-strip.js +285 -44
  114. package/esm/src/transforms/pipeline/stages/compile.d.ts.map +1 -1
  115. package/esm/src/transforms/pipeline/stages/compile.js +6 -1
  116. package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.d.ts +9 -5
  117. package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.d.ts.map +1 -1
  118. package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.js +23 -9
  119. package/esm/src/transforms/shared/server-only-packages.d.ts +30 -0
  120. package/esm/src/transforms/shared/server-only-packages.d.ts.map +1 -0
  121. package/esm/src/transforms/shared/server-only-packages.js +47 -0
  122. package/esm/src/types/entities/getEntityInfo.d.ts.map +1 -1
  123. package/esm/src/types/entities/getEntityInfo.js +10 -2
  124. package/esm/src/utils/version-constant.d.ts +1 -1
  125. package/esm/src/utils/version-constant.js +1 -1
  126. package/package.json +5 -5
@@ -16,6 +16,8 @@ import { resolveModuleDependencies, rewriteResolvedDependencyImports, } from "./
16
16
  import { persistTransformedModule } from "./module-persistence.js";
17
17
  import { transformModuleCodeWithCache } from "./module-transform-cache.js";
18
18
  import { getModuleCacheKey, resolveCachedModulePath } from "./module-cache-lookup.js";
19
+ import { markBuildFailure } from "./build-failure.js";
20
+ export { isBuildFailure } from "./build-failure.js";
19
21
  const logger = rendererLogger.component("module-loader");
20
22
  // Re-export utilities
21
23
  export { createEsmCache, createModuleCache, generateHash } from "./cache.js";
@@ -35,7 +37,12 @@ function decodeFileContent(fileContent) {
35
37
  * @param useLocalAdapter - Whether to use local adapter for reading
36
38
  * @returns Path to the transformed module file
37
39
  */
38
- export async function transformModuleWithDeps(filePath, tmpDir, localAdapter, config, useLocalAdapter = false) {
40
+ export async function transformModuleWithDeps(filePath, tmpDir, localAdapter, config, useLocalAdapter = false, lineage = new Set(),
41
+ // Shared by reference across the whole transform tree (unlike `lineage`, which
42
+ // is copied per level): a descendant records a cycle target here, and the
43
+ // ancestor that eventually persists that target reads it to write a stable
44
+ // alias the left-as-authored cycle edge can resolve to.
45
+ cycleTargets = new Set()) {
39
46
  const { moduleCache, projectDir, projectId, contentSourceId, adapter, mode } = config;
40
47
  const cacheKey = getModuleCacheKey(filePath, projectId, projectDir, contentSourceId, config.reactVersion, mode);
41
48
  const cachedPath = await resolveCachedModulePath({
@@ -58,15 +65,55 @@ export async function transformModuleWithDeps(filePath, tmpDir, localAdapter, co
58
65
  filePath,
59
66
  projectDir,
60
67
  });
61
- const transformedDeps = await Promise.all(resolvedDeps.filter((d) => d.depFilePath).map(async (dep) => {
68
+ // The module cache is only written once a transform completes, so it cannot
69
+ // break a cycle that is still in progress. Carry the chain instead.
70
+ const nextLineage = new Set(lineage).add(filePath);
71
+ const transformedDeps = (await Promise.all(resolvedDeps.filter((d) => d.depFilePath).map(async (dep) => {
72
+ // `await import()` is how a module graph legitimately breaks an import
73
+ // cycle, so following one eagerly can lead straight back to a module
74
+ // further up this chain and recurse until the worker dies. Leave the
75
+ // specifier as authored so the recursion terminates.
76
+ //
77
+ // The cycle target is persisted as a content-hashed artifact whose hash
78
+ // is derived from transformed output we do not produce here (producing it
79
+ // is the recursion we are breaking), so the edge cannot be rewritten to
80
+ // that hashed path. Instead we record the target: when its ancestor
81
+ // persists it, a stable non-hashed alias is written next to the hashed
82
+ // artifact so the relative `.js` specifier esbuild leaves behind resolves.
83
+ // NOTE: this alias path is not yet runtime-verified end to end; if it does
84
+ // not resolve in a real runtime the cycle branch stays broken, which is no
85
+ // worse than before (and still a strict improvement over hanging).
86
+ if (nextLineage.has(dep.depFilePath)) {
87
+ cycleTargets.add(dep.depFilePath);
88
+ logger.debug("Skipping dependency already in the transform chain:", {
89
+ path: dep.path,
90
+ depFilePath: dep.depFilePath,
91
+ });
92
+ return null;
93
+ }
62
94
  logger.debug("Found dependency:", {
63
95
  path: dep.path,
64
96
  depFilePath: dep.depFilePath,
65
97
  isLocalLib: dep.isLocalLib,
66
98
  });
67
- const depTempPath = await transformModuleWithDeps(dep.depFilePath, tmpDir, localAdapter, config, dep.isLocalLib);
68
- return { ...dep, depTempPath };
69
- }));
99
+ try {
100
+ const depTempPath = await transformModuleWithDeps(dep.depFilePath, tmpDir, localAdapter, config, dep.isLocalLib, nextLineage, cycleTargets);
101
+ return { ...dep, depTempPath };
102
+ }
103
+ catch (error) {
104
+ // A static import has to resolve for the importer to run at all. A
105
+ // dynamic one may never be evaluated, so a module behind an untaken
106
+ // branch must not fail the page that merely mentions it.
107
+ if (!dep.isDynamic)
108
+ throw error;
109
+ logger.warn("Leaving an unresolvable dynamic dependency as authored:", {
110
+ path: dep.path,
111
+ depFilePath: dep.depFilePath,
112
+ reason: error instanceof Error ? error.message : String(error),
113
+ });
114
+ return null;
115
+ }
116
+ }))).filter((dep) => dep !== null);
70
117
  fileContent = rewriteResolvedDependencyImports(fileContent, transformedDeps);
71
118
  for (const dep of transformedDeps) {
72
119
  logger.debug("Replaced import:", {
@@ -103,6 +150,7 @@ export async function transformModuleWithDeps(filePath, tmpDir, localAdapter, co
103
150
  cacheKey,
104
151
  contentSourceId,
105
152
  reactVersion: config.reactVersion,
153
+ isCycleTarget: cycleTargets.has(filePath),
106
154
  });
107
155
  }
108
156
  /**
@@ -146,7 +194,15 @@ export function isMissingModuleError(error) {
146
194
  export async function loadModule(filePath, config) {
147
195
  const tmpDir = await getModuleCacheDir(config);
148
196
  const localAdapter = await getLocalAdapter();
149
- const tempFilePath = await transformModuleWithDeps(filePath, tmpDir, localAdapter, config);
197
+ // Everything up to here compiles and resolves source, so a failure is a build
198
+ // failure. Everything after it is the module running.
199
+ let tempFilePath;
200
+ try {
201
+ tempFilePath = await transformModuleWithDeps(filePath, tmpDir, localAdapter, config);
202
+ }
203
+ catch (error) {
204
+ throw markBuildFailure(error);
205
+ }
150
206
  const moduleUrl = `file://${tempFilePath}`;
151
207
  try {
152
208
  return await import(moduleUrl);
@@ -189,7 +245,13 @@ export async function loadModule(filePath, config) {
189
245
  // invalidation stays scoped to this tenant (the path-cache key is not
190
246
  // project-scoped — see invalidateMdxEsmModule).
191
247
  invalidateMdxEsmModule(tmpDir, filePath, config.projectDir, config.reactVersion);
192
- const rebuiltPath = await transformModuleWithDeps(filePath, tmpDir, localAdapter, config);
248
+ let rebuiltPath;
249
+ try {
250
+ rebuiltPath = await transformModuleWithDeps(filePath, tmpDir, localAdapter, config);
251
+ }
252
+ catch (rebuildError) {
253
+ throw markBuildFailure(rebuildError);
254
+ }
193
255
  return await import(`file://${rebuiltPath}?t=${Date.now()}&rebuilt=1`);
194
256
  }
195
257
  logger.error("Failed to import module:", {
@@ -14,6 +14,12 @@ export interface PersistTransformedModuleInput {
14
14
  cacheKey: string;
15
15
  contentSourceId?: string;
16
16
  reactVersion?: string;
17
+ /**
18
+ * True when a dynamic import elsewhere closes a cycle back onto this module.
19
+ * Such an edge is left as authored (`import("../app/page.js")`), so it needs a
20
+ * stable, non-hashed alias next to the content-hashed artifact to resolve to.
21
+ */
22
+ isCycleTarget?: boolean;
17
23
  }
18
24
  /** Write a transformed module artifact and register cache pointers. */
19
25
  export declare function persistTransformedModule(input: PersistTransformedModuleInput): Promise<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"module-persistence.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/module-persistence.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAuDzE,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,cAAc,CAAC;IAC7B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,uEAAuE;AACvE,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,6BAA6B,GACnC,OAAO,CAAC,MAAM,CAAC,CA+DjB"}
1
+ {"version":3,"file":"module-persistence.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/module-persistence.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAuDzE,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,cAAc,CAAC;IAC7B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAqDD,uEAAuE;AACvE,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,6BAA6B,GACnC,OAAO,CAAC,MAAM,CAAC,CAqEjB"}
@@ -44,6 +44,51 @@ async function ensureDir(adapter, dir, force = false) {
44
44
  createdDirs.add(dir);
45
45
  pruneCreatedDirs();
46
46
  }
47
+ /**
48
+ * Whether transformed output exposes a default export, so a cycle alias knows
49
+ * to re-export it. Covers esbuild's `export default …`, `… as default`, and
50
+ * `export { default } from …` forms.
51
+ */
52
+ function hasDefaultExport(code) {
53
+ return /\bexport\s+default\b/.test(code) ||
54
+ /\bas\s+default\b/.test(code) ||
55
+ /\bexport\s*\{[^}]*\bdefault\b[^}]*\}/.test(code);
56
+ }
57
+ /**
58
+ * Write a stable, non-hashed alias next to a cycle target's hashed artifact.
59
+ *
60
+ * A dynamic import that closes an import cycle is left as the author wrote it
61
+ * (see the module loader), so esbuild normalises it to a relative `.js` path
62
+ * (`../app/page.js`) that does not match the content-hashed artifact
63
+ * (`../app/page.<hash>.js`). The alias sits at that relative path and re-exports
64
+ * the real artifact, so the edge resolves if the branch runs. Best-effort: a
65
+ * failed alias just leaves the pre-existing (unresolved) cycle edge in place.
66
+ */
67
+ async function writeCycleTargetAlias(input, relativePath, hashedFileName) {
68
+ const aliasRelativePath = relativePath.replace(/\.(tsx?|jsx|mdx)$/, ".js");
69
+ // Same extension in and out means nothing was renamed (already `.js`): the
70
+ // authored edge already points at the real artifact, so no alias is needed.
71
+ if (aliasRelativePath === relativePath)
72
+ return;
73
+ const aliasPath = join(input.tmpDir, aliasRelativePath);
74
+ const lines = [`export * from "./${hashedFileName}";`];
75
+ if (hasDefaultExport(input.transformedCode)) {
76
+ lines.push(`export { default } from "./${hashedFileName}";`);
77
+ }
78
+ try {
79
+ await input.localAdapter.fs.writeFile(aliasPath, lines.join("\n"));
80
+ logger.debug("Wrote cycle-target alias", {
81
+ alias: aliasRelativePath,
82
+ target: hashedFileName,
83
+ });
84
+ }
85
+ catch (error) {
86
+ logger.warn("Failed to write cycle-target alias", {
87
+ filePath: input.filePath.slice(-40),
88
+ error: error instanceof Error ? error.message : String(error),
89
+ });
90
+ }
91
+ }
47
92
  /** Write a transformed module artifact and register cache pointers. */
48
93
  export async function persistTransformedModule(input) {
49
94
  const transformedHash = hashCodeHex(input.transformedCode).slice(0, 8);
@@ -100,5 +145,9 @@ export async function persistTransformedModule(input) {
100
145
  });
101
146
  }
102
147
  input.moduleCache.set(input.cacheKey, tempFilePath);
148
+ if (input.isCycleTarget) {
149
+ const hashedFileName = jsPath.slice(jsPath.lastIndexOf("/") + 1);
150
+ await writeCycleTargetAlias(input, relativePath, hashedFileName);
151
+ }
103
152
  return tempFilePath;
104
153
  }
@@ -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;AAqDhF,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;AA0BD,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;IA6DnC;;;OAGG;YACW,mBAAmB;IA6GjC,OAAO,CAAC,uBAAuB;IAkCzB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAyT9E,+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;IAyFhC,OAAO,CAAC,kBAAkB;YAQZ,uBAAuB;IAmBrC;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;CAStB"}
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;AAsDhF,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;AA0BD,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;IAqEnC;;;OAGG;YACW,mBAAmB;IA6GjC,OAAO,CAAC,uBAAuB;IAkCzB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAyT9E,+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;IAyFhC,OAAO,CAAC,kBAAkB;YAQZ,uBAAuB;IAmBrC;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;CAStB"}
@@ -35,6 +35,7 @@ import { buildReleaseAssetModules } from "../../release-assets/client-module-map
35
35
  import { getCSSByHashAsync, regenerateCSSByHash, } from "../../html/styles-builder/tailwind-compiler.js";
36
36
  import { getReadyManifestForRender } from "../../release-assets/manifest-cache.js";
37
37
  import { createEsmCache, createModuleCache, loadModule } from "./module-loader/index.js";
38
+ import { isBuildFailure } from "./module-loader/build-failure.js";
38
39
  import { getCSSImports, runWithCSSCollector, } from "../../modules/react-loader/css-import-collector.js";
39
40
  import { assembleRenderResult } from "./render-result-assembly.js";
40
41
  import { isMdxEsmExportMismatchError, recoverStaleMdxEsmPreviewCaches } from "../page-rendering.js";
@@ -160,7 +161,11 @@ export class RenderPipeline {
160
161
  continue;
161
162
  const errorMessage = result.error.message;
162
163
  if (result.type === "page") {
163
- criticalFailures.push({ path: result.path, error: errorMessage });
164
+ criticalFailures.push({
165
+ path: result.path,
166
+ error: errorMessage,
167
+ buildFailure: isBuildFailure(result.error),
168
+ });
164
169
  renderPageLog.error("Critical page module failed to load", {
165
170
  path: result.path,
166
171
  error: errorMessage,
@@ -180,6 +185,10 @@ export class RenderPipeline {
180
185
  detail: `Critical page module(s) failed to load:\n${failedDetails}`,
181
186
  context: {
182
187
  criticalFailures,
188
+ // A module that never compiled is a developer-facing build failure;
189
+ // one that compiled and threw at module scope is an application
190
+ // error the project's own error page should present.
191
+ buildFailure: criticalFailures.some((f) => f.buildFailure),
183
192
  loadedCount: loaded.length,
184
193
  totalModules: modules.length,
185
194
  },
@@ -12,31 +12,42 @@ export interface APIContext {
12
12
  headers: Headers;
13
13
  url: URL;
14
14
  /**
15
- * Read the request body as JSON, or build a JSON response.
15
+ * Build a JSON `Response`. `ctx.json(data, init?)` mirrors `Response.json`.
16
16
  *
17
- * `ctx.json()` with no arguments parses and returns the request body.
18
- * `ctx.json(data, init?)` returns a JSON `Response`.
17
+ * To read the request body, use `ctx.body()` or the raw `ctx.request.json()`.
19
18
  */
20
- json: {
21
- (): Promise<unknown>;
22
- (data: unknown, init?: ResponseInit): Response;
23
- };
19
+ json: (data: unknown, init?: ResponseInit) => Response;
20
+ /**
21
+ * Read and parse the request body as JSON.
22
+ *
23
+ * The result is cached, so calling it more than once (or alongside a manual
24
+ * `ctx.request.json()`) does not throw `Body already consumed`. A body that
25
+ * is not valid JSON becomes a 400, not an unhandled 500.
26
+ */
27
+ body: <T = unknown>() => Promise<T>;
24
28
  text: (data: string, init?: ResponseInit) => Response;
25
29
  fs: FileSystemAdapter;
26
30
  }
27
31
  /**
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.
32
+ * Build the `ctx.json` response helper. Writes only; mirrors `Response.json`.
35
33
  *
36
34
  * Exported because the isolation Worker builds its own context and has to
37
35
  * behave identically. Handlers must not care which one ran them.
38
36
  */
39
- export declare function createJsonHelper(request: Request): APIContext["json"];
37
+ export declare function createJsonHelper(_request: Request): APIContext["json"];
38
+ /**
39
+ * Build the `ctx.body` request-body reader.
40
+ *
41
+ * The parse is memoised on the first call, so a validation helper and a handler
42
+ * that both read the body do not fight over a single-use stream. The clone is
43
+ * taken up front, while the context is built, so `ctx.request` is left intact
44
+ * for a handler that still wants the raw stream — and, crucially, so a handler
45
+ * that reads `ctx.request` raw *before* calling `ctx.body()` cannot make the
46
+ * clone throw `Body already consumed` (`request.clone()` throws synchronously
47
+ * once the original stream is disturbed). A malformed body is turned into a
48
+ * catalogued 400 rather than escaping as a 500.
49
+ */
50
+ export declare function createBodyReader(request: Request): APIContext["body"];
40
51
  export declare function createContext(request: Request, match: RouteMatch, fs: FileSystemAdapter): APIContext;
41
52
  /**
42
53
  * @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;;;;;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"}
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;AAgBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAGtE;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,CAqBZ;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,4 +1,5 @@
1
1
  import { parseCookies } from "../../utils/cookie-utils.js";
2
+ import { INVALID_ARGUMENT } from "../../errors/index.js";
2
3
  import { flattenRouteParams } from "../flatten-route-params.js";
3
4
  export { parseCookies };
4
5
  function createResponse(body, contentType, init) {
@@ -11,29 +12,46 @@ function createResponse(body, contentType, init) {
11
12
  });
12
13
  }
13
14
  /**
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.
15
+ * Build the `ctx.json` response helper. Writes only; mirrors `Response.json`.
21
16
  *
22
17
  * Exported because the isolation Worker builds its own context and has to
23
18
  * behave identically. Handlers must not care which one ran them.
24
19
  */
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;
20
+ export function createJsonHelper(_request) {
21
+ return (data, init) => createResponse(JSON.stringify(data), "application/json", init);
22
+ }
23
+ /**
24
+ * Build the `ctx.body` request-body reader.
25
+ *
26
+ * The parse is memoised on the first call, so a validation helper and a handler
27
+ * that both read the body do not fight over a single-use stream. The clone is
28
+ * taken up front, while the context is built, so `ctx.request` is left intact
29
+ * for a handler that still wants the raw stream — and, crucially, so a handler
30
+ * that reads `ctx.request` raw *before* calling `ctx.body()` cannot make the
31
+ * clone throw `Body already consumed` (`request.clone()` throws synchronously
32
+ * once the original stream is disturbed). A malformed body is turned into a
33
+ * catalogued 400 rather than escaping as a 500.
34
+ */
35
+ export function createBodyReader(request) {
36
+ // Clone eagerly: at construction time the original body is guaranteed
37
+ // untouched, so the read is order-independent with any raw `ctx.request`
38
+ // access a handler performs later.
39
+ const source = request.clone();
40
+ let parsed;
41
+ const read = () => {
42
+ if (!parsed) {
43
+ parsed = source.json().catch(() => {
44
+ throw INVALID_ARGUMENT.create({ detail: "Request body is not valid JSON" });
45
+ });
46
+ }
47
+ return parsed;
48
+ };
49
+ return () => read();
33
50
  }
34
51
  export function createContext(request, match, fs) {
35
52
  const url = new URL(request.url);
36
53
  const json = createJsonHelper(request);
54
+ const body = createBodyReader(request);
37
55
  const text = (data, init) => createResponse(data, "text/plain", init);
38
56
  return {
39
57
  request,
@@ -44,6 +62,7 @@ export function createContext(request, match, fs) {
44
62
  headers: request.headers,
45
63
  url,
46
64
  json,
65
+ body,
47
66
  text,
48
67
  fs,
49
68
  };
@@ -5,7 +5,7 @@ import { validateHTTPImports } from "./http-validator.js";
5
5
  import { loadSecurityConfig } from "./security-config.js";
6
6
  import { createError, toError } from "../../../errors/index.js";
7
7
  import { getEsbuildLoader } from "../../../utils/path-utils.js";
8
- import { createFileSystem } from "../../../platform/compat/fs.js";
8
+ import { createFileSystem, realPath } from "../../../platform/compat/fs.js";
9
9
  import * as pathHelper from "../../../platform/compat/path/index.js";
10
10
  import { FILE_EXTENSIONS, getLoaderForFile, validateModulePath } from "./loader-helpers.js";
11
11
  import { isDeno } from "../../../platform/compat/runtime.js";
@@ -211,6 +211,61 @@ function createAdapterResolvePlugin(adapter, projectDir) {
211
211
  },
212
212
  };
213
213
  }
214
+ /**
215
+ * Refuse to load any file the bundle resolved outside the project.
216
+ *
217
+ * The resolver plugins above validate the specifiers they claim, but esbuild
218
+ * applies the project's own tsconfig `paths` itself, before any `onResolve`
219
+ * callback runs. A templated project maps `@/*` to `./*`, so `@/../secrets.ts`
220
+ * is resolved by esbuild straight to a path above the project root and loaded
221
+ * in the default namespace, where none of those guards ever see it.
222
+ *
223
+ * This runs at load time instead, which is the one point every resolution
224
+ * strategy converges on. Returning `undefined` defers to normal loading, so the
225
+ * plugin only ever subtracts. Package code is exempt: `node_modules` is
226
+ * resolved by esbuild's own node resolution rather than by a project alias, and
227
+ * is legitimately hoisted above the project root in a monorepo.
228
+ *
229
+ * `roots` carries both the project path as configured and its symlink-resolved
230
+ * form, because esbuild reports the real path of a file it loaded. A project
231
+ * reached through a symlink (`/var` -> `/private/var` on macOS, and any deploy
232
+ * layout that symlinks a release directory) would otherwise fail every import.
233
+ */
234
+ /**
235
+ * The project path as configured, plus its symlink-resolved form when they
236
+ * differ. `realPath` throws if the directory is missing, in which case the
237
+ * configured path is all there is to compare against.
238
+ */
239
+ async function resolveProjectRoots(projectDir) {
240
+ const configured = pathHelper.resolve(projectDir);
241
+ try {
242
+ const real = await realPath(configured);
243
+ return real === configured ? [configured] : [configured, real];
244
+ }
245
+ catch {
246
+ return [configured];
247
+ }
248
+ }
249
+ function createProjectBoundaryPlugin(roots) {
250
+ return {
251
+ name: "vf-project-boundary",
252
+ setup(build) {
253
+ build.onLoad({ filter: /.*/ }, (args) => {
254
+ if (roots.some((root) => isWithinDirectory(root, args.path)))
255
+ return undefined;
256
+ if (args.path.split(/[\\/]/).includes("node_modules"))
257
+ return undefined;
258
+ logger.error(`[API] Resolved import escapes project: ${args.path}`);
259
+ return {
260
+ errors: [{
261
+ text: `Import escapes the project directory: ${args.path}. ` +
262
+ `API routes may only import files inside the project.`,
263
+ }],
264
+ };
265
+ });
266
+ },
267
+ };
268
+ }
214
269
  function loadAndTranspileModule(modulePath, projectDir, adapter, fs, config) {
215
270
  return withSpan("api.loadAndTranspileModule", async () => {
216
271
  const { filePath: resolvedPath, contents: source } = await readFileWithExtensions(adapter, modulePath, FILE_EXTENSIONS, projectDir);
@@ -289,6 +344,7 @@ function loadAndTranspileModule(modulePath, projectDir, adapter, fs, config) {
289
344
  createImportMapPlugin(projectDir, adapter, config),
290
345
  createAdapterResolvePlugin(adapter, projectDir),
291
346
  createHTTPPlugin({ allowedHosts, projectDir }),
347
+ createProjectBoundaryPlugin(await resolveProjectRoots(projectDir)),
292
348
  ],
293
349
  });
294
350
  if (result.errors?.length) {
@@ -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,CAkC/B;AAqHD,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"}
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,CAkC/B;AAgJD,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"}
@@ -136,25 +136,48 @@ async function loadMiddlewareFromVirtualFS(middlewarePath, adapter, strictExport
136
136
  await fs.remove(tempDir, { recursive: true });
137
137
  }
138
138
  }
139
+ /** Describe the shape of the default export that was rejected. */
140
+ function describeDefaultExport(exported) {
141
+ if (Array.isArray(exported)) {
142
+ if (exported.length === 0)
143
+ return `Found an empty default export array.`;
144
+ const badIndex = exported.findIndex((value) => typeof value !== "function");
145
+ if (badIndex >= 0) {
146
+ return `Found a default export array with a non-function at index ${badIndex} ` +
147
+ `(${typeof exported[badIndex]}).`;
148
+ }
149
+ }
150
+ if (exported && typeof exported === "object") {
151
+ const keys = Object.keys(exported);
152
+ return keys.length > 0
153
+ ? `Found a default export of type object with keys: ${keys.join(", ")}.`
154
+ : `Found a default export of type object with no keys.`;
155
+ }
156
+ return `Found a default export of type ${typeof exported}.`;
157
+ }
139
158
  /**
140
159
  * Explain what a root middleware file must export. When the module looks like it
141
160
  * was written for Next.js, say so, because that is overwhelmingly why this
142
161
  * fails. A bad root middleware takes down every route, so the message has
143
162
  * to be enough to fix the file without reading the source.
144
163
  */
145
- function invalidMiddlewareExport(middlewareModule, sourceFile) {
164
+ function invalidMiddlewareExport(middlewareModule, sourceFile, exported) {
146
165
  const named = middlewareModule && typeof middlewareModule === "object"
147
166
  ? Object.keys(middlewareModule)
148
167
  : [];
149
168
  const looksLikeNext = named.includes("middleware") &&
150
169
  typeof middlewareModule.middleware === "function";
170
+ const hasDefault = middlewareModule && typeof middlewareModule === "object" &&
171
+ "default" in middlewareModule;
151
172
  const detail = looksLikeNext
152
173
  ? `Found a named "middleware" export, which is the Next.js convention. ` +
153
174
  `Veryfront expects a default export, and its middleware receives ` +
154
175
  `(c, next), where c is a context carrying c.req, not the Request itself.`
155
- : named.length > 0
156
- ? `Found export(s): ${named.join(", ")}.`
157
- : `The module has no usable export.`;
176
+ : hasDefault
177
+ ? describeDefaultExport(exported)
178
+ : named.length > 0
179
+ ? `Found export(s): ${named.join(", ")}.`
180
+ : `The module has no usable export.`;
158
181
  return new TypeError(`Invalid middleware export in ${sourceFile}. ${detail}\n` +
159
182
  `Expected a default export that is a middleware function, or a non-empty ` +
160
183
  `array of them:\n\n` +
@@ -171,7 +194,7 @@ function normalizeMiddlewareExport(middlewareModule, strict = false, sourceFile
171
194
  : middlewareModule;
172
195
  if (Array.isArray(exported)) {
173
196
  if (strict && (exported.length === 0 || exported.some((value) => typeof value !== "function"))) {
174
- throw invalidMiddlewareExport(middlewareModule, sourceFile);
197
+ throw invalidMiddlewareExport(middlewareModule, sourceFile, exported);
175
198
  }
176
199
  return exported.filter((middleware) => typeof middleware === "function");
177
200
  }
@@ -179,7 +202,7 @@ function normalizeMiddlewareExport(middlewareModule, strict = false, sourceFile
179
202
  return [exported];
180
203
  }
181
204
  if (strict) {
182
- throw invalidMiddlewareExport(middlewareModule, sourceFile);
205
+ throw invalidMiddlewareExport(middlewareModule, sourceFile, exported);
183
206
  }
184
207
  return [];
185
208
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ssr.handler.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/handlers/request/ssr/ssr.handler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EAEf,aAAa,EACd,MAAM,gBAAgB,CAAC;AAWxB,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,4CAA4C,CAAC;AAOpD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAKzE;AA+BD,qBAAa,UAAW,SAAQ,WAAW;IACzC,QAAQ,EAAE,eAAe,CAIvB;IAEF,OAAO,CAAC,UAAU,CAAiB;gBAEvB,UAAU,CAAC,EAAE,cAAc;IAKvC,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IA8BjE,OAAO,CAAC,qBAAqB;IAmF7B,OAAO,CAAC,iBAAiB;IAkEzB,OAAO,CAAC,cAAc;YAgBR,cAAc;YA6Bd,sBAAsB;YAgBtB,aAAa;CAU5B"}
1
+ {"version":3,"file":"ssr.handler.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/handlers/request/ssr/ssr.handler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EAEf,aAAa,EACd,MAAM,gBAAgB,CAAC;AAWxB,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,4CAA4C,CAAC;AAOpD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAKzE;AAiCD,qBAAa,UAAW,SAAQ,WAAW;IACzC,QAAQ,EAAE,eAAe,CAIvB;IAEF,OAAO,CAAC,UAAU,CAAiB;gBAEvB,UAAU,CAAC,EAAE,cAAc;IAKvC,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IA8BjE,OAAO,CAAC,qBAAqB;IAmF7B,OAAO,CAAC,iBAAiB;IAkEzB,OAAO,CAAC,cAAc;YAgBR,cAAc;YA6Bd,sBAAsB;YAgBtB,aAAa;CAU5B"}
@@ -49,9 +49,11 @@ export function isProductionMode(ctx, _url) {
49
49
  * opposed to errors thrown by the running application.
50
50
  *
51
51
  * Module-load failures arrive wrapped in a RUNTIME-category `render-error`,
52
- * which loses the original category, so they are identified by their
53
- * `criticalFailures` context instead: a page module that could not be *loaded*
54
- * always failed to compile or resolve.
52
+ * which loses the original category, so they carry a `buildFailure` flag that
53
+ * the module loader sets at the point of failure. Failing to load is not
54
+ * evidence on its own: a module that compiled fine and threw at module scope
55
+ * also fails to load, and that is an application error the project's own error
56
+ * page should present.
55
57
  */
56
58
  function isBuildError(error) {
57
59
  if (!(error instanceof VeryfrontError))
@@ -59,7 +61,7 @@ function isBuildError(error) {
59
61
  if (error.category === "BUILD" || error.category === "MODULE")
60
62
  return true;
61
63
  const context = error.context;
62
- return Array.isArray(context?.criticalFailures) && context.criticalFailures.length > 0;
64
+ return context?.buildFailure === true;
63
65
  }
64
66
  export class SSRHandler extends BaseHandler {
65
67
  metadata = {
@@ -6,11 +6,13 @@
6
6
  * @module
7
7
  */
8
8
  import type { Skill } from "./types.js";
9
+ /** Maximum number of skills rendered in an agent system prompt. */
10
+ export declare const MAX_SKILL_MANIFEST_PROMPT_ENTRIES = 30;
9
11
  /**
10
12
  * Build the skill manifest prompt section for an agent's system prompt.
11
13
  *
12
- * Lists all available skills with their descriptions and instructions
13
- * on how to use the skill tools (load_skill, load_skill_reference, execute_skill_script).
14
+ * Lists up to the prompt entry limit with descriptions and instructions on
15
+ * how to use the skill tools (load_skill, load_skill_reference, execute_skill_script).
14
16
  *
15
17
  * @param skills - Map of resolved skills for the agent
16
18
  * @returns Prompt section string, or empty string if no skills
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-augmentation.d.ts","sourceRoot":"","sources":["../../../src/src/skill/prompt-augmentation.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,CA4B3E"}
1
+ {"version":3,"file":"prompt-augmentation.d.ts","sourceRoot":"","sources":["../../../src/src/skill/prompt-augmentation.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,mEAAmE;AACnE,eAAO,MAAM,iCAAiC,KAAK,CAAC;AAEpD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,CA0C3E"}
@@ -5,11 +5,13 @@
5
5
  *
6
6
  * @module
7
7
  */
8
+ /** Maximum number of skills rendered in an agent system prompt. */
9
+ export const MAX_SKILL_MANIFEST_PROMPT_ENTRIES = 30;
8
10
  /**
9
11
  * Build the skill manifest prompt section for an agent's system prompt.
10
12
  *
11
- * Lists all available skills with their descriptions and instructions
12
- * on how to use the skill tools (load_skill, load_skill_reference, execute_skill_script).
13
+ * Lists up to the prompt entry limit with descriptions and instructions on
14
+ * how to use the skill tools (load_skill, load_skill_reference, execute_skill_script).
13
15
  *
14
16
  * @param skills - Map of resolved skills for the agent
15
17
  * @returns Prompt section string, or empty string if no skills
@@ -23,8 +25,17 @@ export function buildSkillManifestPrompt(skills) {
23
25
  "You have access to skills via tool calling. IMPORTANT: You MUST call the load_skill tool (not write it as text) to activate a skill before performing skill-related tasks.",
24
26
  "",
25
27
  ];
28
+ let displayedSkillCount = 0;
26
29
  for (const [id, skill] of skills) {
27
30
  lines.push(`- **${id}**: ${skill.metadata.description}`);
31
+ displayedSkillCount += 1;
32
+ if (displayedSkillCount === MAX_SKILL_MANIFEST_PROMPT_ENTRIES) {
33
+ break;
34
+ }
35
+ }
36
+ if (skills.size > MAX_SKILL_MANIFEST_PROMPT_ENTRIES) {
37
+ lines.push("");
38
+ lines.push(`${skills.size - MAX_SKILL_MANIFEST_PROMPT_ENTRIES} more skill summaries omitted from this prompt. Call load_skill only with a known skill ID.`);
28
39
  }
29
40
  lines.push("");
30
41
  lines.push("### Skill Tools (call these as tools, never write them as text)");