veryfront 0.1.1100 → 0.1.1102

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 (64) hide show
  1. package/esm/deno.js +1 -1
  2. package/esm/src/agent/factory.d.ts.map +1 -1
  3. package/esm/src/agent/factory.js +2 -0
  4. package/esm/src/agent/hosted/cloud-runtime-system-messages.d.ts +5 -0
  5. package/esm/src/agent/hosted/cloud-runtime-system-messages.d.ts.map +1 -1
  6. package/esm/src/agent/hosted/cloud-runtime-system-messages.js +3 -2
  7. package/esm/src/agent/runtime/agent-invocation-contract.d.ts +1 -0
  8. package/esm/src/agent/runtime/agent-invocation-contract.d.ts.map +1 -1
  9. package/esm/src/agent/runtime/agent-invocation-contract.js +1 -0
  10. package/esm/src/agent/runtime/effective-agent-system.d.ts +8 -0
  11. package/esm/src/agent/runtime/effective-agent-system.d.ts.map +1 -0
  12. package/esm/src/agent/runtime/effective-agent-system.js +9 -0
  13. package/esm/src/internal-agents/run-stream.d.ts +1 -0
  14. package/esm/src/internal-agents/run-stream.d.ts.map +1 -1
  15. package/esm/src/internal-agents/run-stream.js +53 -0
  16. package/esm/src/internal-agents/run-system-prompt.d.ts +42 -0
  17. package/esm/src/internal-agents/run-system-prompt.d.ts.map +1 -0
  18. package/esm/src/internal-agents/run-system-prompt.js +109 -0
  19. package/esm/src/internal-agents/schema.d.ts +2 -0
  20. package/esm/src/internal-agents/schema.d.ts.map +1 -1
  21. package/esm/src/internal-agents/schema.js +1 -0
  22. package/esm/src/routing/api/context-builder.d.ts +23 -1
  23. package/esm/src/routing/api/context-builder.d.ts.map +1 -1
  24. package/esm/src/routing/api/context-builder.js +22 -1
  25. package/esm/src/routing/api/handler.d.ts +11 -1
  26. package/esm/src/routing/api/handler.d.ts.map +1 -1
  27. package/esm/src/routing/api/handler.js +46 -12
  28. package/esm/src/routing/api/module-loader/loader.d.ts +5 -0
  29. package/esm/src/routing/api/module-loader/loader.d.ts.map +1 -1
  30. package/esm/src/routing/api/module-loader/loader.js +37 -2
  31. package/esm/src/server/dev-server/middleware.d.ts.map +1 -1
  32. package/esm/src/server/dev-server/middleware.js +35 -7
  33. package/esm/src/server/handlers/request/agent-stream.handler.d.ts.map +1 -1
  34. package/esm/src/server/handlers/request/agent-stream.handler.js +1 -0
  35. package/esm/src/server/handlers/request/ssr/error-page-fallback.js +20 -3
  36. package/esm/src/server/handlers/request/ssr/ssr.handler.d.ts +0 -11
  37. package/esm/src/server/handlers/request/ssr/ssr.handler.d.ts.map +1 -1
  38. package/esm/src/server/handlers/request/ssr/ssr.handler.js +23 -1
  39. package/esm/src/transforms/esm/degraded-artifact.d.ts +15 -0
  40. package/esm/src/transforms/esm/degraded-artifact.d.ts.map +1 -0
  41. package/esm/src/transforms/esm/degraded-artifact.js +22 -0
  42. package/esm/src/transforms/esm/http-cache.d.ts.map +1 -1
  43. package/esm/src/transforms/esm/http-cache.js +47 -17
  44. package/esm/src/transforms/esm/import-parser.d.ts.map +1 -1
  45. package/esm/src/transforms/esm/import-parser.js +47 -1
  46. package/esm/src/transforms/esm/specifier-resolver.d.ts +21 -4
  47. package/esm/src/transforms/esm/specifier-resolver.d.ts.map +1 -1
  48. package/esm/src/transforms/esm/specifier-resolver.js +43 -23
  49. package/esm/src/transforms/import-rewriter/strategies/asset-strategy.d.ts +23 -0
  50. package/esm/src/transforms/import-rewriter/strategies/asset-strategy.d.ts.map +1 -0
  51. package/esm/src/transforms/import-rewriter/strategies/asset-strategy.js +49 -0
  52. package/esm/src/transforms/import-rewriter/strategies/index.d.ts +1 -0
  53. package/esm/src/transforms/import-rewriter/strategies/index.d.ts.map +1 -1
  54. package/esm/src/transforms/import-rewriter/strategies/index.js +1 -0
  55. package/esm/src/transforms/import-rewriter/unified-rewriter.d.ts.map +1 -1
  56. package/esm/src/transforms/import-rewriter/unified-rewriter.js +2 -0
  57. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/index.d.ts.map +1 -1
  58. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/index.js +2 -1
  59. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.d.ts +19 -0
  60. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.d.ts.map +1 -1
  61. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.js +23 -1
  62. package/esm/src/utils/version-constant.d.ts +1 -1
  63. package/esm/src/utils/version-constant.js +1 -1
  64. package/package.json +5 -5
@@ -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"}
@@ -45,8 +45,25 @@ async function loadModule(args) {
45
45
  return loadAndTranspileModule(modulePath, projectDir, adapter, fs, config);
46
46
  }
47
47
  const fileExistsLocally = await fs.exists(modulePath);
48
- if (fileExistsLocally)
49
- return loadTSModuleDirect(modulePath);
48
+ if (fileExistsLocally) {
49
+ try {
50
+ return await loadTSModuleDirect(modulePath);
51
+ }
52
+ catch (error) {
53
+ // A direct import shares the dev server's runtime context, which is what
54
+ // makes auto-discovery (agentRegistry and friends) work — but it leaves
55
+ // specifier resolution to Deno, which knows nothing about the project's
56
+ // `@/` alias. Bundling can resolve that import map path, so fall back to
57
+ // it rather than reporting a routing-shaped 500.
58
+ if (!isSpecifierResolutionError(error))
59
+ throw error;
60
+ logger.debug("Direct import could not resolve a specifier, bundling instead", {
61
+ modulePath,
62
+ error: error instanceof Error ? error.message : String(error),
63
+ });
64
+ return loadAndTranspileModule(modulePath, projectDir, adapter, fs, config);
65
+ }
66
+ }
50
67
  logger.debug(`File not local, using adapter-based loading: ${modulePath}`);
51
68
  return loadAndTranspileModule(modulePath, projectDir, adapter, fs, config);
52
69
  }
@@ -55,6 +72,24 @@ async function loadModule(args) {
55
72
  * This allows the module to share the same runtime context as the dev server,
56
73
  * enabling auto-discovery features like agentRegistry to work.
57
74
  */
75
+ /**
76
+ * Deno's resolver reports an unresolvable import as a TypeError whose message
77
+ * opens by naming the specifier it could not resolve. Anchoring on that opening
78
+ * is what keeps a module's own error out: a route that throws
79
+ * `Error("Cannot find module x")` at evaluation time is broken, and re-running
80
+ * it under bundling would evaluate broken code a second time.
81
+ */
82
+ const SPECIFIER_RESOLUTION_MESSAGE = /^(?:Import "[^"]+" not a dependency|Module not found "[^"]+"|Relative import path "[^"]+" not prefixed)/;
83
+ /**
84
+ * True when a module failed to load because Deno could not resolve one of its
85
+ * import specifiers, rather than because the module itself is broken.
86
+ */
87
+ export function isSpecifierResolutionError(error) {
88
+ // Every other error shape the direct import can produce belongs to the module.
89
+ if (!(error instanceof TypeError))
90
+ return false;
91
+ return SPECIFIER_RESOLUTION_MESSAGE.test(error.message.trimStart());
92
+ }
58
93
  function loadTSModuleDirect(modulePath) {
59
94
  const cacheBuster = `?v=${Date.now()}`;
60
95
  const url = modulePath.startsWith("file://")
@@ -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,CA6B/B;AAkFD,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;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"}
@@ -86,11 +86,11 @@ export async function loadMiddlewareFile(projectDir, adapter, options = {}) {
86
86
  try {
87
87
  logger.debug(`Loading ${middlewareFile}`);
88
88
  if (isVirtualFilesystem(adapter.fs)) {
89
- return await loadMiddlewareFromVirtualFS(middlewarePath, adapter, options.throwOnError === true);
89
+ return await loadMiddlewareFromVirtualFS(middlewarePath, adapter, options.throwOnError === true, middlewareFile);
90
90
  }
91
91
  const middlewareUrl = `file://${middlewarePath}?t=${Date.now()}-${dntShim.crypto.randomUUID()}`;
92
92
  const middlewareModule = await import(middlewareUrl);
93
- return normalizeMiddlewareExport(middlewareModule, options.throwOnError === true);
93
+ return normalizeMiddlewareExport(middlewareModule, options.throwOnError === true, middlewareFile);
94
94
  }
95
95
  catch (error) {
96
96
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -101,7 +101,7 @@ export async function loadMiddlewareFile(projectDir, adapter, options = {}) {
101
101
  }
102
102
  return [];
103
103
  }
104
- async function loadMiddlewareFromVirtualFS(middlewarePath, adapter, strictExport) {
104
+ async function loadMiddlewareFromVirtualFS(middlewarePath, adapter, strictExport, sourceFile = "middleware.ts") {
105
105
  const fs = createFileSystem();
106
106
  const content = await adapter.fs.readFile(middlewarePath);
107
107
  const source = typeof content === "string" ? content : new TextDecoder().decode(content);
@@ -130,20 +130,48 @@ async function loadMiddlewareFromVirtualFS(middlewarePath, adapter, strictExport
130
130
  try {
131
131
  await fs.writeTextFile(tempFile, js);
132
132
  const middlewareModule = await import(`file://${tempFile}?v=${Date.now()}`);
133
- return normalizeMiddlewareExport(middlewareModule, strictExport);
133
+ return normalizeMiddlewareExport(middlewareModule, strictExport, sourceFile);
134
134
  }
135
135
  finally {
136
136
  await fs.remove(tempDir, { recursive: true });
137
137
  }
138
138
  }
139
- function normalizeMiddlewareExport(middlewareModule, strict = false) {
139
+ /**
140
+ * Explain what a root middleware file must export. When the module looks like it
141
+ * was written for Next.js, say so, because that is overwhelmingly why this
142
+ * fails. A bad root middleware takes down every route, so the message has
143
+ * to be enough to fix the file without reading the source.
144
+ */
145
+ function invalidMiddlewareExport(middlewareModule, sourceFile) {
146
+ const named = middlewareModule && typeof middlewareModule === "object"
147
+ ? Object.keys(middlewareModule)
148
+ : [];
149
+ const looksLikeNext = named.includes("middleware") &&
150
+ typeof middlewareModule.middleware === "function";
151
+ const detail = looksLikeNext
152
+ ? `Found a named "middleware" export, which is the Next.js convention. ` +
153
+ `Veryfront expects a default export, and its middleware receives ` +
154
+ `(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.`;
158
+ return new TypeError(`Invalid middleware export in ${sourceFile}. ${detail}\n` +
159
+ `Expected a default export that is a middleware function, or a non-empty ` +
160
+ `array of them:\n\n` +
161
+ ` export default async function middleware(c, next) {\n` +
162
+ ` const response = await next();\n` +
163
+ ` return response;\n` +
164
+ ` }\n\n` +
165
+ `See docs/guides/middleware.md.`);
166
+ }
167
+ function normalizeMiddlewareExport(middlewareModule, strict = false, sourceFile = "middleware.ts") {
140
168
  const exported = middlewareModule && typeof middlewareModule === "object" &&
141
169
  "default" in middlewareModule
142
170
  ? middlewareModule.default
143
171
  : middlewareModule;
144
172
  if (Array.isArray(exported)) {
145
173
  if (strict && (exported.length === 0 || exported.some((value) => typeof value !== "function"))) {
146
- throw new TypeError("Invalid middleware export: expected a function or non-empty array of functions");
174
+ throw invalidMiddlewareExport(middlewareModule, sourceFile);
147
175
  }
148
176
  return exported.filter((middleware) => typeof middleware === "function");
149
177
  }
@@ -151,7 +179,7 @@ function normalizeMiddlewareExport(middlewareModule, strict = false) {
151
179
  return [exported];
152
180
  }
153
181
  if (strict) {
154
- throw new TypeError("Invalid middleware export: expected a function or non-empty array of functions");
182
+ throw invalidMiddlewareExport(middlewareModule, sourceFile);
155
183
  }
156
184
  return [];
157
185
  }
@@ -1 +1 @@
1
- {"version":3,"file":"agent-stream.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/request/agent-stream.handler.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAGpF,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,wCAAwC,CAAC;AAQhD,OAAO,EACL,4BAA4B,EAE7B,MAAM,2CAA2C,CAAC;AA0BnD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AAkBnG,MAAM,WAAW,sBACf,SAAQ,yBAAyB,EAAE,+BAA+B;IAClE,4BAA4B,CAAC,EAAE,OAAO,4BAA4B,CAAC;IACnE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,+BAA+B,CAAC,YAAY,CAAC,CAAC;CACpF;AAkhBD,qBAAa,kBAAmB,SAAQ,WAAW;IASrC,OAAO,CAAC,QAAQ,CAAC,IAAI;IARjC,QAAQ,EAAE,eAAe,CAMvB;gBAE2B,IAAI,GAAE,sBAAoC;IAIvE,OAAO,CAAC,sBAAsB;IAsBxB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CA4NxE"}
1
+ {"version":3,"file":"agent-stream.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/request/agent-stream.handler.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAGpF,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,wCAAwC,CAAC;AAQhD,OAAO,EACL,4BAA4B,EAE7B,MAAM,2CAA2C,CAAC;AA0BnD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AAkBnG,MAAM,WAAW,sBACf,SAAQ,yBAAyB,EAAE,+BAA+B;IAClE,4BAA4B,CAAC,EAAE,OAAO,4BAA4B,CAAC;IACnE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,+BAA+B,CAAC,YAAY,CAAC,CAAC;CACpF;AAkhBD,qBAAa,kBAAmB,SAAQ,WAAW;IASrC,OAAO,CAAC,QAAQ,CAAC,IAAI;IARjC,QAAQ,EAAE,eAAe,CAMvB;gBAE2B,IAAI,GAAE,sBAAoC;IAIvE,OAAO,CAAC,sBAAsB;IAsBxB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CA6NxE"}
@@ -533,6 +533,7 @@ export class AgentStreamHandler extends BaseHandler {
533
533
  projectAgentSandbox: {
534
534
  apiUrl: resolveVeryfrontApiBaseUrlFromHostEnv(),
535
535
  authToken: apiAuthToken || undefined,
536
+ branchId: payload.runtimeTargetBranchId,
536
537
  projectId: sourceScopedContext.projectId ?? null,
537
538
  },
538
539
  });
@@ -78,6 +78,23 @@ async function setCachedPath(cacheKey, path) {
78
78
  }
79
79
  errorPagePathCache.set(cacheKey, path);
80
80
  }
81
+ /**
82
+ * Whether a "this project has no error page" answer may be cached.
83
+ *
84
+ * In dev it may not. Nothing invalidates this cache when the filesystem
85
+ * changes, so caching the miss means a project that adds `pages/500.tsx` while
86
+ * the server is running keeps getting the dev overlay until restart, with no
87
+ * indication why. A miss costs one file probe on an error render, which is not
88
+ * a path worth optimising in dev.
89
+ */
90
+ function canCacheMiss(ctx) {
91
+ return !ctx.isLocalProject;
92
+ }
93
+ async function setCachedMiss(cacheKey, ctx) {
94
+ if (!canCacheMiss(ctx))
95
+ return;
96
+ await setCachedPath(cacheKey, null);
97
+ }
81
98
  async function deleteCachedPath(cacheKey) {
82
99
  if (injectedCacheRepo) {
83
100
  await injectedCacheRepo.delete(cacheKey);
@@ -104,7 +121,7 @@ async function tryLoadErrorPage(pagesDir, pageType, ctx, reactVersion) {
104
121
  try {
105
122
  const resolvedPath = await ctx.adapter.fs.resolveFile(basePath);
106
123
  if (!resolvedPath) {
107
- await setCachedPath(cacheKey, null);
124
+ await setCachedMiss(cacheKey, ctx);
108
125
  return null;
109
126
  }
110
127
  const fullPath = joinPath(ctx.projectDir, resolvedPath);
@@ -117,7 +134,7 @@ async function tryLoadErrorPage(pagesDir, pageType, ctx, reactVersion) {
117
134
  catch (_) {
118
135
  // expected: resolveFile may fail, fall through to extension probing
119
136
  }
120
- await setCachedPath(cacheKey, null);
137
+ await setCachedMiss(cacheKey, ctx);
121
138
  return null;
122
139
  }
123
140
  for (const ext of ERROR_PAGE_EXTENSIONS) {
@@ -136,7 +153,7 @@ async function tryLoadErrorPage(pagesDir, pageType, ctx, reactVersion) {
136
153
  // expected: file with this extension doesn't exist
137
154
  }
138
155
  }
139
- await setCachedPath(cacheKey, null);
156
+ await setCachedMiss(cacheKey, ctx);
140
157
  return null;
141
158
  }
142
159
  async function loadErrorComponent(filePath, ctx, reactVersion) {
@@ -15,17 +15,6 @@ import { type SSRServiceLike } from "../../../services/rendering/ssr.service.js"
15
15
  * Config override (PRODUCTION_MODE) takes precedence.
16
16
  */
17
17
  export declare function isProductionMode(ctx: HandlerContext, _url?: URL): boolean;
18
- /**
19
- * SSR Handler - Thin orchestration layer
20
- *
21
- * Responsibilities:
22
- * - Route matching and quick rejections
23
- * - Multi-project context setup
24
- * - Response building with headers
25
- * - Custom fallback handling (not-found, error pages)
26
- *
27
- * Business logic is delegated to SSRService.
28
- */
29
18
  export declare class SSRHandler extends BaseHandler {
30
19
  metadata: HandlerMetadata;
31
20
  private ssrService;
@@ -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;AAMpD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAKzE;AAED;;;;;;;;;;GAUG;AACH,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;IA8DzB,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;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"}
@@ -19,6 +19,7 @@ import { tryNotFoundFallback } from "./not-found-fallback.js";
19
19
  import { tryErrorPageFallback } from "./error-page-fallback.js";
20
20
  import { SSRService, } from "../../../services/rendering/ssr.service.js";
21
21
  import { ErrorPages } from "../../../utils/error-html.js";
22
+ import { VeryfrontError } from "../../../../errors/index.js";
22
23
  import { buildSSRResponse } from "./ssr-response-builder.js";
23
24
  const logger = serverLogger.component("ssr");
24
25
  /**
@@ -43,6 +44,23 @@ export function isProductionMode(ctx, _url) {
43
44
  *
44
45
  * Business logic is delegated to SSRService.
45
46
  */
47
+ /**
48
+ * True for errors raised while compiling or resolving project source, as
49
+ * opposed to errors thrown by the running application.
50
+ *
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.
55
+ */
56
+ function isBuildError(error) {
57
+ if (!(error instanceof VeryfrontError))
58
+ return false;
59
+ if (error.category === "BUILD" || error.category === "MODULE")
60
+ return true;
61
+ const context = error.context;
62
+ return Array.isArray(context?.criticalFailures) && context.criticalFailures.length > 0;
63
+ }
46
64
  export class SSRHandler extends BaseHandler {
47
65
  metadata = {
48
66
  name: "SSRHandler",
@@ -182,7 +200,11 @@ export class SSRHandler extends BaseHandler {
182
200
  if (result.errorType === "not-found") {
183
201
  return this.handleNotFound(req, ctx, slug, nonce);
184
202
  }
185
- if (result.errorType === "server-error" && !result.showDevOverlay) {
203
+ const isServerError = result.errorType === "server-error" ||
204
+ result.errorType === "runtime";
205
+ // Project error pages should beat the dev overlay for runtime errors.
206
+ // Build/import errors stay visible because their overlay is actionable.
207
+ if (isServerError && !(result.showDevOverlay && isBuildError(result.error))) {
186
208
  const customResponse = await this.tryCustomErrorFallback(req, ctx, result, nonce);
187
209
  if (customResponse)
188
210
  return customResponse;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Degradation marking for cached HTTP module artifacts.
3
+ *
4
+ * A module whose lazy dependency could not be prefetched is still usable for
5
+ * the render that produced it, but it must never be mistaken for a complete
6
+ * artifact on a later render. The marker travels with the code, so any reader
7
+ * of the on-disk cache can tell the two apart.
8
+ *
9
+ * @module transforms/esm/degraded-artifact
10
+ */
11
+ /** Mark bundle code as produced from an incomplete dependency prefetch. */
12
+ export declare function markDegradedArtifact(code: string): string;
13
+ /** Return whether bundle code was produced from an incomplete prefetch. */
14
+ export declare function isDegradedArtifact(code: string): boolean;
15
+ //# sourceMappingURL=degraded-artifact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"degraded-artifact.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/degraded-artifact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Degradation marking for cached HTTP module artifacts.
3
+ *
4
+ * A module whose lazy dependency could not be prefetched is still usable for
5
+ * the render that produced it, but it must never be mistaken for a complete
6
+ * artifact on a later render. The marker travels with the code, so any reader
7
+ * of the on-disk cache can tell the two apart.
8
+ *
9
+ * @module transforms/esm/degraded-artifact
10
+ */
11
+ /** Preserved comment format that survives minification */
12
+ const VF_DEGRADED_MARKER = "\n/*! @vf-degraded */\n";
13
+ /** Mark bundle code as produced from an incomplete dependency prefetch. */
14
+ export function markDegradedArtifact(code) {
15
+ if (isDegradedArtifact(code))
16
+ return code;
17
+ return `${code}${VF_DEGRADED_MARKER}`;
18
+ }
19
+ /** Return whether bundle code was produced from an incomplete prefetch. */
20
+ export function isDegradedArtifact(code) {
21
+ return code.endsWith(VF_DEGRADED_MARKER);
22
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"http-cache.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/http-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAGL,KAAK,YAAY,EAKjB,wBAAwB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,aAAa,EAElB,gBAAgB,EAEhB,KAAK,OAAO,EACb,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAA2B,MAAM,4BAA4B,CAAC;AACxF,OAAO,EACL,0BAA0B,EAO3B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,sBAAsB,EAKvB,MAAM,uBAAuB,CAAC;AAe/B,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,GACnB,CAAC;AAGF,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAsRlC,wEAAwE;AACxE,UAAU,sBAAsB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,sBAAsB,CAAC,CAkCjC;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,CAOjB;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,wBAAwB,GAClC,OAAO,CAAC,OAAO,CAAC,CAElB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EAClD,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,wBAAwB,GAClC,OAAO,CAAC,MAAM,EAAE,CAAC,CAEnB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAErF;AAGD,eAAO,MAAM,wBAAwB,0BAAoB,CAAC"}
1
+ {"version":3,"file":"http-cache.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/http-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzE,OAAO,EAGL,KAAK,YAAY,EAKjB,wBAAwB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,aAAa,EAElB,gBAAgB,EAEhB,KAAK,OAAO,EACb,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAA2B,MAAM,4BAA4B,CAAC;AACxF,OAAO,EACL,0BAA0B,EAO3B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,sBAAsB,EAKvB,MAAM,uBAAuB,CAAC;AAe/B,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,GACnB,CAAC;AAGF,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAwTlC,wEAAwE;AACxE,UAAU,sBAAsB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,sBAAsB,CAAC,CAkCjC;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,CAOjB;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,wBAAwB,GAClC,OAAO,CAAC,OAAO,CAAC,CAElB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EAClD,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,wBAAwB,GAClC,OAAO,CAAC,MAAM,EAAE,CAAC,CAEnB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAErF;AAGD,eAAO,MAAM,wBAAwB,0BAAoB,CAAC"}
@@ -23,6 +23,7 @@ import { CACHE_DIR_TOKEN, detokenizeAllCachePaths, detokenizeCachePaths, tokeniz
23
23
  import { looksLikeHtmlContent as looksLikeHtmlNotJs } from "./html-content.js";
24
24
  // Extracted modules
25
25
  import { embedSourceUrl, extractSourceUrl } from "./source-url-embed.js";
26
+ import { isDegradedArtifact, markDegradedArtifact } from "./degraded-artifact.js";
26
27
  import { buildHttpCacheIdentity, buildHttpCacheIdentityMetadata, ensureAbsoluteDir, ensurePreparedHttpCacheRequestOptions, getEffectiveHttpCacheRequest, hashHttpCacheIdentity, hasIncompatibleFilePaths, isHttpUrl, normalizeHttpUrl, prepareHttpCacheRequestOptions, } from "./http-cache-helpers.js";
27
28
  import { extractBundleDeps, validateBundleDepsExist } from "./bundle-deps-validator.js";
28
29
  import { __clearInFlightHttpFetches, bundleAccumulatorStorage, inFlightHttpFetches, processingStackStorage, refreshDistributedCacheAsync, trackBundleAccumulator, waitForInFlightFetch, } from "./in-flight-manager.js";
@@ -56,15 +57,32 @@ async function cacheHttpModuleInternal(url, options) {
56
57
  const fs = createFileSystem();
57
58
  if (await exists(cachePath)) {
58
59
  const code = await fs.readTextFile(cachePath);
59
- const deps = extractBundleDeps(code);
60
- if (deps.length > 0) {
61
- const depsValid = await validateBundleDepsExist(deps, cacheDir);
62
- if (!depsValid) {
63
- httpCacheLog.debug("Local cache has missing deps, will re-fetch", {
64
- url: normalizedUrl,
65
- hash,
66
- missingDeps: deps.length,
67
- });
60
+ if (isDegradedArtifact(code)) {
61
+ // The artifact on disk is the fallback a previous render wrote when a
62
+ // dependency could not be prefetched. Retry the prefetch instead of
63
+ // handing the degradation on.
64
+ httpCacheLog.debug("Local cache holds a degraded artifact, will re-fetch", {
65
+ url: normalizedUrl,
66
+ hash,
67
+ });
68
+ }
69
+ else {
70
+ const deps = extractBundleDeps(code);
71
+ if (deps.length > 0) {
72
+ const depsValid = await validateBundleDepsExist(deps, cacheDir);
73
+ if (!depsValid) {
74
+ httpCacheLog.debug("Local cache has missing deps, will re-fetch", {
75
+ url: normalizedUrl,
76
+ hash,
77
+ missingDeps: deps.length,
78
+ });
79
+ }
80
+ else {
81
+ getCachedPaths().set(cacheKey, cachePath);
82
+ refreshDistributedCacheAsync(hash, code, cacheDir, normalizedUrl, identityMetadata, getLastDistributedRefresh);
83
+ trackBundleAccumulator(hash, normalizedUrl, cachePath);
84
+ return cachePath;
85
+ }
68
86
  }
69
87
  else {
70
88
  getCachedPaths().set(cacheKey, cachePath);
@@ -73,12 +91,6 @@ async function cacheHttpModuleInternal(url, options) {
73
91
  return cachePath;
74
92
  }
75
93
  }
76
- else {
77
- getCachedPaths().set(cacheKey, cachePath);
78
- refreshDistributedCacheAsync(hash, code, cacheDir, normalizedUrl, identityMetadata, getLastDistributedRefresh);
79
- trackBundleAccumulator(hash, normalizedUrl, cachePath);
80
- return cachePath;
81
- }
82
94
  }
83
95
  const processingStack = getProcessingStack();
84
96
  if (processingStack.has(cacheIdentity)) {
@@ -198,13 +210,18 @@ async function cacheHttpModuleInternal(url, options) {
198
210
  });
199
211
  }
200
212
  processingStack.add(cacheIdentity);
213
+ let degraded = [];
201
214
  try {
202
- code = await rewriteModuleImports(code, normalizedUrl, options, cacheHttpModule);
215
+ const rewritten = await rewriteModuleImports(code, normalizedUrl, options, cacheHttpModule);
216
+ code = rewritten.code;
217
+ degraded = rewritten.degraded;
203
218
  }
204
219
  finally {
205
220
  processingStack.delete(cacheIdentity);
206
221
  }
207
222
  code = embedSourceUrl(code, normalizedUrl);
223
+ if (degraded.length > 0)
224
+ code = markDegradedArtifact(code);
208
225
  await fs.mkdir(cacheDir, { recursive: true });
209
226
  await fs.writeTextFile(cachePath, code);
210
227
  if (!(await exists(cachePath))) {
@@ -212,6 +229,19 @@ async function cacheHttpModuleInternal(url, options) {
212
229
  detail: `[HTTP-CACHE] INVARIANT VIOLATION: File write succeeded but file does not exist: ${cachePath}`,
213
230
  });
214
231
  }
232
+ if (degraded.length > 0) {
233
+ // The file on disk carries this render through, but the artifact is not
234
+ // the one this URL is supposed to produce. Keeping it out of the
235
+ // distributed cache and the in-memory path map means the next render
236
+ // retries the prefetch instead of inheriting one upstream blip for the
237
+ // lifetime of the distributed entry.
238
+ httpCacheLog.warn("Not caching a module with unresolved dynamic imports", {
239
+ url: normalizedUrl,
240
+ hash,
241
+ degraded,
242
+ });
243
+ return cachePath;
244
+ }
215
245
  try {
216
246
  await httpBundleCache.setCode(String(hash), asLocalModuleCode(code), normalizedUrl, HTTP_MODULE_DISTRIBUTED_TTL_SEC, identityMetadata);
217
247
  }
@@ -256,7 +286,7 @@ async function cacheHttpModule(url, options) {
256
286
  export function cacheHttpImportsToLocal(code, options) {
257
287
  const requestOptions = prepareHttpCacheRequestOptions(options);
258
288
  return bundleAccumulatorStorage.run([], async () => {
259
- const replacements = await buildReplacements(code, undefined, requestOptions, cacheHttpModule);
289
+ const { replacements } = await buildReplacements(code, undefined, requestOptions, cacheHttpModule);
260
290
  if (replacements.size === 0)
261
291
  return { code };
262
292
  httpCacheLog.debug("Cached HTTP imports", { count: replacements.size });
@@ -1 +1 @@
1
- {"version":3,"file":"import-parser.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/import-parser.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAKtE,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,uBAAuB;IAC/B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,UAAU,EAAE,WAAW,EAAE,CAAC;IAC1B,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;IAC1C,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAKD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,uBAAuB,CAAC,CAiFlC"}
1
+ {"version":3,"file":"import-parser.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/import-parser.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAKtE,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,uBAAuB;IAC/B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,UAAU,EAAE,WAAW,EAAE,CAAC;IAC1B,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;IAC1C,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAKD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,uBAAuB,CAAC,CA0HlC"}