veryfront 0.1.1127 → 0.1.1128

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 (45) hide show
  1. package/esm/cli/templates/manifest.d.ts +1 -0
  2. package/esm/cli/templates/manifest.js +3 -2
  3. package/esm/deno.js +1 -1
  4. package/esm/src/html/hydration-script-builder/dev-client-renderer.js +1 -1
  5. package/esm/src/html/hydration-script-builder/prod-scripts.js +1 -1
  6. package/esm/src/html/hydration-script-builder/templates/router.d.ts.map +1 -1
  7. package/esm/src/html/hydration-script-builder/templates/router.js +35 -11
  8. package/esm/src/html/utils.d.ts.map +1 -1
  9. package/esm/src/html/utils.js +19 -45
  10. package/esm/src/index.client.d.ts +6 -21
  11. package/esm/src/index.client.d.ts.map +1 -1
  12. package/esm/src/index.client.js +6 -21
  13. package/esm/src/react/runtime/core.d.ts +22 -0
  14. package/esm/src/react/runtime/core.d.ts.map +1 -1
  15. package/esm/src/react/runtime/core.js +1 -1
  16. package/esm/src/rendering/orchestrator/module-collection.d.ts +8 -0
  17. package/esm/src/rendering/orchestrator/module-collection.d.ts.map +1 -1
  18. package/esm/src/rendering/orchestrator/module-collection.js +10 -0
  19. package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
  20. package/esm/src/rendering/orchestrator/pipeline.js +2 -2
  21. package/esm/src/routing/api/context-builder.d.ts +7 -0
  22. package/esm/src/routing/api/context-builder.d.ts.map +1 -1
  23. package/esm/src/routing/api/context-builder.js +20 -2
  24. package/esm/src/server/handlers/dev/files/esbuild-plugins.d.ts.map +1 -1
  25. package/esm/src/server/handlers/dev/files/esbuild-plugins.js +22 -0
  26. package/esm/src/server/handlers/request/module/page-data-endpoint-handler.js +24 -1
  27. package/esm/src/server/handlers/response/not-found.d.ts +0 -1
  28. package/esm/src/server/handlers/response/not-found.d.ts.map +1 -1
  29. package/esm/src/server/handlers/response/not-found.js +9 -110
  30. package/esm/src/server/services/rsc/orchestrators/handler.d.ts +3 -1
  31. package/esm/src/server/services/rsc/orchestrators/handler.d.ts.map +1 -1
  32. package/esm/src/server/services/rsc/orchestrators/handler.js +13 -8
  33. package/esm/src/server/services/rsc/orchestrators/render-handler.d.ts +1 -1
  34. package/esm/src/server/services/rsc/orchestrators/render-handler.d.ts.map +1 -1
  35. package/esm/src/server/services/rsc/orchestrators/render-handler.js +2 -2
  36. package/esm/src/transforms/esm/http-cache.d.ts.map +1 -1
  37. package/esm/src/transforms/esm/http-cache.js +132 -46
  38. package/esm/src/transforms/esm/in-flight-manager.d.ts +3 -2
  39. package/esm/src/transforms/esm/in-flight-manager.d.ts.map +1 -1
  40. package/esm/src/transforms/esm/in-flight-manager.js +6 -5
  41. package/esm/src/transforms/import-rewriter/strategies/bare-strategy.d.ts.map +1 -1
  42. package/esm/src/transforms/import-rewriter/strategies/bare-strategy.js +16 -2
  43. package/esm/src/utils/version-constant.d.ts +1 -1
  44. package/esm/src/utils/version-constant.js +1 -1
  45. package/package.json +5 -5
@@ -236,6 +236,13 @@ function isBareImport(path) {
236
236
  !path.startsWith("http://") &&
237
237
  !path.startsWith("https://"));
238
238
  }
239
+ /**
240
+ * A Node built-in module specifier (`node:crypto`, `node:fs`, …). These are
241
+ * server-only and must never be rewritten to an esm.sh URL for a browser bundle.
242
+ */
243
+ function isNodeBuiltinSpecifier(path) {
244
+ return path === "node" || path.startsWith("node:");
245
+ }
239
246
  function toEsmUrl(path) {
240
247
  return ESM_PACKAGE_MAP[path] ?? `https://esm.sh/${path}`;
241
248
  }
@@ -292,6 +299,21 @@ export function createBareExternalPlugin(options = false) {
292
299
  return undefined;
293
300
  if (args.kind !== "import-statement" && args.kind !== "dynamic-import")
294
301
  return undefined;
302
+ // Fail closed on Node built-ins. This plugin only runs for browser
303
+ // bundles (platform: "browser"), where a server-only `node:*` import can
304
+ // never work: rewriting it to an esm.sh URL silently ships a module that
305
+ // 404s on esm.sh and throws (e.g. `createHash is not a function`) at
306
+ // hydration. Surface a clear build error instead of a broken rewrite.
307
+ if (isNodeBuiltinSpecifier(args.path)) {
308
+ return {
309
+ errors: [{
310
+ text: `Cannot bundle server-only import "${args.path}" for the browser. ` +
311
+ `Node built-in modules are not available on the client. Move it into a ` +
312
+ `server component, an API route, or middleware — or gate the code behind a ` +
313
+ `"use client" boundary that does not import it.`,
314
+ }],
315
+ };
316
+ }
295
317
  // Keep import-map-resolved specifiers as bare externals — the browser's
296
318
  // <script type="importmap"> resolves them to the correct CDN URL.
297
319
  if (importMapOwnsSpecifier(args.path, importMapImports)) {
@@ -239,16 +239,39 @@ function refreshStalePageData(cacheKey, resolve, cachePolicy) {
239
239
  }
240
240
  });
241
241
  }
242
+ /**
243
+ * Only http(s) and root-relative destinations may be encoded for the client to
244
+ * follow. The client follows the destination with `window.location.href`, which
245
+ * would EXECUTE a `javascript:`/`data:` URL — unlike the full-page 302 path where
246
+ * the browser ignores such a Location. Protocol-relative `//host` is rejected too
247
+ * (it is easy to smuggle past a naive "starts with /" check). Blocked
248
+ * destinations fall through to normal error handling instead of being followed.
249
+ */
250
+ function isFollowableRedirect(destination) {
251
+ if (destination.startsWith("/")) {
252
+ const baseOrigin = "https://veryfront.local";
253
+ try {
254
+ return new URL(destination, baseOrigin).origin === baseOrigin;
255
+ }
256
+ catch {
257
+ return false;
258
+ }
259
+ }
260
+ return /^https?:\/\//i.test(destination);
261
+ }
242
262
  /**
243
263
  * A redirect() from getServerData is thrown up the pipeline as a VeryfrontError
244
264
  * whose `context.redirect` holds the destination (mirrors extractRedirectLocation
245
- * in the SSR service). Returns null for any other error.
265
+ * in the SSR service). Returns null for any other error, or for a redirect whose
266
+ * destination is not safe to hand to the client to follow.
246
267
  */
247
268
  function extractRedirectFromError(error) {
248
269
  const context = error?.context;
249
270
  const redirect = context?.redirect;
250
271
  if (!redirect || typeof redirect.destination !== "string")
251
272
  return null;
273
+ if (!isFollowableRedirect(redirect.destination))
274
+ return null;
252
275
  return { destination: redirect.destination, permanent: redirect.permanent === true };
253
276
  }
254
277
  function buildPageDataCacheKey(ctx, slug, url) {
@@ -3,6 +3,5 @@ import type { HandlerContext, HandlerMetadata, HandlerResult } from "../types.js
3
3
  export declare class NotFoundHandler extends BaseHandler {
4
4
  metadata: HandlerMetadata;
5
5
  handle(req: Request, ctx: HandlerContext): Promise<HandlerResult>;
6
- private generate404Html;
7
6
  }
8
7
  //# sourceMappingURL=not-found.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"not-found.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/response/not-found.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AASnG,qBAAa,eAAgB,SAAQ,WAAW;IAC9C,QAAQ,EAAE,eAAe,CAIvB;IAEF,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IA8BjE,OAAO,CAAC,eAAe;CA4GxB"}
1
+ {"version":3,"file":"not-found.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/response/not-found.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AAUnG,qBAAa,eAAgB,SAAQ,WAAW;IAC9C,QAAQ,EAAE,eAAe,CAIvB;IAEF,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CAmClE"}
@@ -1,7 +1,8 @@
1
1
  import { BaseHandler } from "./base.js";
2
2
  import { ResponseBuilder } from "../../../security/index.js";
3
3
  import { HTTP_INTERNAL_SERVER_ERROR, HTTP_NOT_FOUND, PRIORITY_FALLBACK, } from "../../../utils/constants/index.js";
4
- import { buildNonceAttribute, escapeHtml } from "../../../html/html-escape.js";
4
+ import { ErrorPages } from "../../utils/error-html.js";
5
+ import { addNonceToHtmlTags } from "../../../html/nonce-injection.js";
5
6
  export class NotFoundHandler extends BaseHandler {
6
7
  metadata = {
7
8
  name: "NotFoundHandler",
@@ -12,7 +13,13 @@ export class NotFoundHandler extends BaseHandler {
12
13
  const pathname = new URL(req.url).pathname;
13
14
  try {
14
15
  const builder = this.createResponseBuilder(ctx);
15
- const html = this.generate404Html(pathname, builder.nonce);
16
+ // Render the SAME 404 page as the SSR miss path (ssr.handler /
17
+ // ssr.service) so every not-found — including a fallthrough like
18
+ // /_veryfront/<missing> that never reaches SSR — looks identical.
19
+ // Nonce the inline <style>/<script> exactly like the SSR response builder
20
+ // (ssr-response-builder addNonceToHtmlTags) so the page still renders
21
+ // styled under a strict nonce-based CSP.
22
+ const html = addNonceToHtmlTags(ErrorPages.notFound(pathname), builder.nonce);
16
23
  const response = builder
17
24
  .withCORS(req, ctx.securityConfig?.cors)
18
25
  .withSecurity(ctx.securityConfig ?? undefined, req)
@@ -29,112 +36,4 @@ export class NotFoundHandler extends BaseHandler {
29
36
  return Promise.resolve(this.respond(response));
30
37
  }
31
38
  }
32
- generate404Html(pathname, nonce) {
33
- const nonceAttr = buildNonceAttribute(nonce);
34
- return `<!DOCTYPE html>
35
- <html lang="en">
36
- <head>
37
- <meta charset="utf-8"/>
38
- <meta name="viewport" content="width=device-width, initial-scale=1"/>
39
- <title>404 Not Found</title>
40
- <style${nonceAttr}>
41
- body {
42
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
43
- margin: 0;
44
- padding: 40px;
45
- background: #f5f5f5;
46
- color: #333;
47
- display: flex;
48
- flex-direction: column;
49
- align-items: center;
50
- justify-content: center;
51
- min-height: 100vh;
52
- box-sizing: border-box;
53
- }
54
- .container {
55
- background: white;
56
- border-radius: 8px;
57
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
58
- padding: 40px;
59
- max-width: 600px;
60
- text-align: center;
61
- }
62
- h1 {
63
- font-size: 48px;
64
- margin: 0 0 16px 0;
65
- color: #000;
66
- }
67
- h2 {
68
- font-size: 24px;
69
- font-weight: normal;
70
- margin: 0 0 24px 0;
71
- color: #666;
72
- }
73
- p {
74
- line-height: 1.6;
75
- margin: 0 0 32px 0;
76
- color: #666;
77
- }
78
- code {
79
- background: #f5f5f5;
80
- padding: 2px 6px;
81
- border-radius: 3px;
82
- font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
83
- font-size: 14px;
84
- }
85
- a {
86
- color: #0066cc;
87
- text-decoration: none;
88
- font-weight: 500;
89
- transition: color 0.2s;
90
- }
91
- a:hover {
92
- color: #0052a3;
93
- text-decoration: underline;
94
- }
95
- .actions {
96
- display: flex;
97
- gap: 16px;
98
- justify-content: center;
99
- flex-wrap: wrap;
100
- }
101
- .button {
102
- display: inline-block;
103
- padding: 12px 24px;
104
- background: #0066cc;
105
- color: white;
106
- border-radius: 6px;
107
- font-weight: 500;
108
- transition: background 0.2s;
109
- }
110
- .button:hover {
111
- background: #0052a3;
112
- text-decoration: none;
113
- }
114
- .secondary {
115
- background: transparent;
116
- color: #0066cc;
117
- border: 2px solid #0066cc;
118
- }
119
- .secondary:hover {
120
- background: #0066cc;
121
- color: white;
122
- }
123
- </style>
124
- </head>
125
- <body>
126
- <div class="container">
127
- <h1>404</h1>
128
- <h2>Page Not Found</h2>
129
- <p>
130
- The path <code>${escapeHtml(pathname)}</code> was not found on this server.
131
- </p>
132
- <div class="actions">
133
- <a href="/" class="button">Go Home</a>
134
- <a href=".." class="button secondary">Go Back</a>
135
- </div>
136
- </div>
137
- </body>
138
- </html>`;
139
- }
140
39
  }
@@ -16,6 +16,7 @@ export declare class RSCDevServerHandler {
16
16
  private renderer;
17
17
  private clientManifest;
18
18
  private rendererPromise;
19
+ private reactVersionPromise;
19
20
  private invalidationGeneration;
20
21
  private readonly manifestHandler;
21
22
  private readonly renderHandler;
@@ -25,8 +26,8 @@ export declare class RSCDevServerHandler {
25
26
  private readonly appDir;
26
27
  private readonly isLocalProject;
27
28
  private readonly mode;
28
- private readonly reactVersionPromise;
29
29
  private readonly fs?;
30
+ private readonly config?;
30
31
  handleManifest(): Promise<Response>;
31
32
  handleRender(pathname: string, searchParams: URLSearchParams, request?: Request): Promise<Response>;
32
33
  handleStream(pathname: string, searchParams: URLSearchParams): Promise<Response>;
@@ -34,5 +35,6 @@ export declare class RSCDevServerHandler {
34
35
  invalidate(): void;
35
36
  private ensureRenderer;
36
37
  private initializeRenderer;
38
+ private getReactVersion;
37
39
  }
38
40
  //# sourceMappingURL=handler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/services/rsc/orchestrators/handler.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAE5E,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,IAAI,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC;IACpC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,4BAA4B,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,GAAG,SAAS,CAKzF;AAED,qBAAa,mBAAmB;IAY5B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAX7B,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,cAAc,CAAiD;IACvE,OAAO,CAAC,eAAe,CAA8B;IACrD,OAAO,CAAC,sBAAsB,CAAK;IAEnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,WAAW,CAA4B;gBAG5B,UAAU,EAAE,MAAM,EACnC,OAAO,GAAE,uBAA4B;IAqCvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA+B;IACpD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAkB;IACtD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAuB;IAE3C,cAAc,IAAI,OAAO,CAAC,QAAQ,CAAC;IAI7B,YAAY,CAChB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,eAAe,EAC7B,OAAO,CAAC,EAAE,OAAO,GAChB,OAAO,CAAC,QAAQ,CAAC;IAKd,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC;IAKhF,UAAU,CACd,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,eAAe,EAC7B,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,QAAQ,CAAC;IAWpB,UAAU,IAAI,IAAI;YAOJ,cAAc;YAed,kBAAkB;CAejC"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/services/rsc/orchestrators/handler.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAE5E,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,IAAI,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC;IACpC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,4BAA4B,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,GAAG,SAAS,CAKzF;AAED,qBAAa,mBAAmB;IAa5B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAZ7B,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,cAAc,CAAiD;IACvE,OAAO,CAAC,eAAe,CAA8B;IACrD,OAAO,CAAC,mBAAmB,CAAgC;IAC3D,OAAO,CAAC,sBAAsB,CAAK;IAEnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,WAAW,CAA4B;gBAG5B,UAAU,EAAE,MAAM,EACnC,OAAO,GAAE,uBAA4B;IAkCvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA+B;IACpD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAuB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAkB;IAE1C,cAAc,IAAI,OAAO,CAAC,QAAQ,CAAC;IAI7B,YAAY,CAChB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,eAAe,EAC7B,OAAO,CAAC,EAAE,OAAO,GAChB,OAAO,CAAC,QAAQ,CAAC;IAKd,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC;IAKhF,UAAU,CACd,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,eAAe,EAC7B,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,QAAQ,CAAC;IAWpB,UAAU,IAAI,IAAI;YAOJ,cAAc;YAed,kBAAkB;IAgBhC,OAAO,CAAC,eAAe;CAOxB"}
@@ -16,6 +16,7 @@ export class RSCDevServerHandler {
16
16
  renderer = null;
17
17
  clientManifest = null;
18
18
  rendererPromise = null;
19
+ reactVersionPromise = null;
19
20
  invalidationGeneration = 0;
20
21
  manifestHandler;
21
22
  renderHandler;
@@ -34,28 +35,25 @@ export class RSCDevServerHandler {
34
35
  fs: options.adapter?.fs,
35
36
  contentSourceId,
36
37
  });
37
- this.reactVersionPromise = resolveProjectReactVersion({
38
- projectDir,
39
- config: options.config,
40
- });
41
38
  this.renderHandler = new RenderHandler(projectDir, () => this.renderer, mode, appDir, {
42
39
  adapter: options.adapter,
43
40
  projectId: options.projectId,
44
41
  projectSlug: options.projectSlug,
45
42
  contentSourceId,
46
- reactVersion: this.reactVersionPromise,
43
+ reactVersion: () => this.getReactVersion(),
47
44
  });
48
45
  this.streamHandler = new StreamHandler(this.renderHandler);
49
46
  this.appDir = appDir;
50
47
  this.isLocalProject = isLocalProject;
51
48
  this.mode = mode;
52
49
  this.fs = options.adapter?.fs;
50
+ this.config = options.config;
53
51
  }
54
52
  appDir;
55
53
  isLocalProject;
56
54
  mode;
57
- reactVersionPromise;
58
55
  fs;
56
+ config;
59
57
  handleManifest() {
60
58
  return this.manifestHandler.handle(this.clientManifest);
61
59
  }
@@ -69,7 +67,7 @@ export class RSCDevServerHandler {
69
67
  }
70
68
  async handlePage(pathname, searchParams, nonce) {
71
69
  if (!this.pageHandler) {
72
- this.pageHandler = new PageHandler(this.mode === "development", await this.reactVersionPromise, this.isLocalProject ? "fs" : "rsc-module");
70
+ this.pageHandler = new PageHandler(this.mode === "development", await this.getReactVersion(), this.isLocalProject ? "fs" : "rsc-module");
73
71
  }
74
72
  return this.pageHandler.handle(pathname, searchParams, nonce);
75
73
  }
@@ -96,7 +94,7 @@ export class RSCDevServerHandler {
96
94
  }
97
95
  async initializeRenderer(generation) {
98
96
  const clientManifest = await buildClientManifest(this.projectDir, this.appDir, this.fs);
99
- const reactVersion = await this.reactVersionPromise;
97
+ const reactVersion = await this.getReactVersion();
100
98
  if (generation !== this.invalidationGeneration)
101
99
  return;
102
100
  this.clientManifest = clientManifest;
@@ -109,4 +107,11 @@ export class RSCDevServerHandler {
109
107
  reactVersion,
110
108
  });
111
109
  }
110
+ getReactVersion() {
111
+ this.reactVersionPromise ??= resolveProjectReactVersion({
112
+ projectDir: this.projectDir,
113
+ config: this.config,
114
+ });
115
+ return this.reactVersionPromise;
116
+ }
112
117
  }
@@ -5,7 +5,7 @@ interface RenderHandlerModuleOptions {
5
5
  projectId?: string;
6
6
  projectSlug?: string;
7
7
  contentSourceId?: string;
8
- reactVersion?: Promise<string>;
8
+ reactVersion?: () => Promise<string>;
9
9
  }
10
10
  export declare class RenderHandler {
11
11
  private projectDir;
@@ -1 +1 @@
1
- {"version":3,"file":"render-handler.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/services/rsc/orchestrators/render-handler.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oDAAoD,CAAC;AActF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAM5E,UAAU,0BAA0B;IAClC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;CAChC;AASD,qBAAa,aAAa;IAEtB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,aAAa;gBAJb,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,WAAW,GAAG,IAAI,EACrC,IAAI,GAAE,aAAa,GAAG,YAA2B,EACjD,MAAM,GAAE,MAAc,EACtB,aAAa,GAAE,0BAA+B;IAGlD,MAAM,CACV,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,eAAe,EAC7B,OAAO,CAAC,EAAE,OAAO,GAChB,OAAO,CAAC,QAAQ,CAAC;YAWN,aAAa;YA+Bb,mBAAmB;IA2CjC,OAAO,CAAC,UAAU;YAOJ,aAAa;IA2B3B,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,mBAAmB;CA6B5B"}
1
+ {"version":3,"file":"render-handler.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/services/rsc/orchestrators/render-handler.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oDAAoD,CAAC;AActF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAM5E,UAAU,0BAA0B;IAClC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACtC;AASD,qBAAa,aAAa;IAEtB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,aAAa;gBAJb,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,WAAW,GAAG,IAAI,EACrC,IAAI,GAAE,aAAa,GAAG,YAA2B,EACjD,MAAM,GAAE,MAAc,EACtB,aAAa,GAAE,0BAA+B;IAGlD,MAAM,CACV,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,eAAe,EAC7B,OAAO,CAAC,EAAE,OAAO,GAChB,OAAO,CAAC,QAAQ,CAAC;YAWN,aAAa;YA+Bb,mBAAmB;IA2CjC,OAAO,CAAC,UAAU;YAOJ,aAAa;IA2B3B,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,mBAAmB;CA6B5B"}
@@ -61,7 +61,7 @@ export class RenderHandler {
61
61
  : await fs.readTextFile(componentPath);
62
62
  const { body, frontmatter } = extractFrontmatter(source);
63
63
  const compiled = await compileContent(this.mode, this.projectDir, body, frontmatter, componentPath, "server");
64
- return await mdxRenderer.loadModuleESM(compiled.compiledCode, adapter, this.moduleOptions.projectId ?? this.projectDir, this.projectDir, this.moduleOptions.projectSlug, this.moduleOptions.contentSourceId, await this.moduleOptions.reactVersion);
64
+ return await mdxRenderer.loadModuleESM(compiled.compiledCode, adapter, this.moduleOptions.projectId ?? this.projectDir, this.projectDir, this.moduleOptions.projectSlug, this.moduleOptions.contentSourceId, await this.moduleOptions.reactVersion?.());
65
65
  }
66
66
  if (!adapter) {
67
67
  return (await import(componentPath));
@@ -73,7 +73,7 @@ export class RenderHandler {
73
73
  contentSourceId: this.moduleOptions.contentSourceId,
74
74
  dev: this.mode === "development",
75
75
  mode: this.mode === "development" ? "preview" : "production",
76
- reactVersion: await this.moduleOptions.reactVersion,
76
+ reactVersion: await this.moduleOptions.reactVersion?.(),
77
77
  });
78
78
  }
79
79
  buildProps(pathname, searchParams) {
@@ -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;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"}
1
+ {"version":3,"file":"http-cache.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/http-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAgBH,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;AAqJ/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;AAmRlC,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"}
@@ -9,9 +9,10 @@
9
9
  import { createFileSystem, exists } from "../../platform/compat/fs.js";
10
10
  import { join } from "../../platform/compat/path/index.js";
11
11
  import { rendererLogger as logger } from "../../utils/index.js";
12
- import { BUILD_FAILED, BUNDLE_ERROR, FILE_NOT_FOUND } from "../../errors/index.js";
12
+ import { BUILD_FAILED, BUNDLE_ERROR, FILE_NOT_FOUND, retryWithBackoff } from "../../errors/index.js";
13
13
  import { SpanNames } from "../../observability/index.js";
14
14
  import { withSpan } from "../../observability/tracing/otlp-setup.js";
15
+ import { sanitizeUrlForSpan } from "../../utils/logger/redact.js";
15
16
  import { replaceSpecifiers } from "./lexer.js";
16
17
  import { createBundleManifest, storeBundleManifest } from "./bundle-manifest.js";
17
18
  import { HTTP_MODULE_DISTRIBUTED_TTL_SEC } from "../../utils/constants/cache.js";
@@ -32,8 +33,119 @@ import { buildReplacements, rewriteModuleImports } from "./specifier-resolver.js
32
33
  import { ensureHttpBundlesExist as ensureHttpBundlesExistImpl, invalidateHttpBundle as invalidateHttpBundleImpl, recoverHttpBundleByHash as recoverHttpBundleByHashImpl, } from "./bundle-recovery.js";
33
34
  /** Threshold in ms above which an HTTP module fetch is considered slow */
34
35
  const SLOW_HTTP_FETCH_THRESHOLD_MS = 500;
36
+ const HTTP_MODULE_FETCH_MAX_ATTEMPTS = 3;
37
+ const HTTP_MODULE_FETCH_RETRY_DELAY_MS = 100;
38
+ const HTTP_MODULE_FETCH_WAIT_GRACE_MS = 5_000;
39
+ const HTTP_MODULE_FETCH_MAX_WAIT_MS = HTTP_FETCH_TIMEOUT_MS * HTTP_MODULE_FETCH_MAX_ATTEMPTS +
40
+ HTTP_MODULE_FETCH_RETRY_DELAY_MS *
41
+ ((HTTP_MODULE_FETCH_MAX_ATTEMPTS - 1) * HTTP_MODULE_FETCH_MAX_ATTEMPTS / 2) +
42
+ HTTP_MODULE_FETCH_WAIT_GRACE_MS;
35
43
  const httpCacheLog = logger.component("http-cache");
36
44
  const contentMetricsLog = logger.component("content-metrics");
45
+ class HttpModuleResponseError extends Error {
46
+ status;
47
+ constructor(status) {
48
+ super(`HTTP module response returned status ${status}`);
49
+ this.status = status;
50
+ this.name = "HttpModuleResponseError";
51
+ }
52
+ }
53
+ class HttpModuleRequestError extends Error {
54
+ requestErrorType;
55
+ constructor(requestErrorType) {
56
+ super(`HTTP module request failed (${requestErrorType})`);
57
+ this.requestErrorType = requestErrorType;
58
+ this.name = "HttpModuleRequestError";
59
+ }
60
+ }
61
+ function shouldRetryHttpModuleFetch(status) {
62
+ return status === 408 || status === 425 || status === 429 || status >= 500;
63
+ }
64
+ async function discardResponseBody(response) {
65
+ try {
66
+ await response.body?.cancel();
67
+ }
68
+ catch (_) {
69
+ // The response is already being discarded.
70
+ }
71
+ }
72
+ async function fetchHttpModuleAttempt(url, safeUrl, urlObj, signal, attempt) {
73
+ let response;
74
+ try {
75
+ const startedAt = performance.now();
76
+ response = await fetch(url, {
77
+ headers: { "user-agent": "Mozilla/5.0 Veryfront/1.0" },
78
+ signal,
79
+ redirect: "follow",
80
+ });
81
+ const duration = Math.round(performance.now() - startedAt);
82
+ contentMetricsLog.debug("HTTP_MODULE_FETCH", {
83
+ url: safeUrl,
84
+ host: urlObj.host,
85
+ duration_ms: duration,
86
+ status: response.status,
87
+ slow: duration > SLOW_HTTP_FETCH_THRESHOLD_MS,
88
+ attempt: attempt + 1,
89
+ });
90
+ if (!response.ok) {
91
+ const status = response.status;
92
+ await discardResponseBody(response);
93
+ throw new HttpModuleResponseError(status);
94
+ }
95
+ return {
96
+ code: await response.text(),
97
+ contentType: response.headers.get("content-type") ?? "",
98
+ };
99
+ }
100
+ catch (error) {
101
+ if (error instanceof HttpModuleResponseError)
102
+ throw error;
103
+ if (response)
104
+ await discardResponseBody(response);
105
+ const requestErrorType = error instanceof Error ? error.name : typeof error;
106
+ throw new HttpModuleRequestError(requestErrorType);
107
+ }
108
+ }
109
+ async function fetchHttpModule(url) {
110
+ const urlObj = new URL(url);
111
+ const safeUrl = sanitizeUrlForSpan(url);
112
+ try {
113
+ return await retryWithBackoff((signal, attempt) => withSpan(SpanNames.HTTP_CLIENT_FETCH, () => fetchHttpModuleAttempt(url, safeUrl, urlObj, signal, attempt), {
114
+ "http.method": "GET",
115
+ "http.url": safeUrl,
116
+ "http.host": urlObj.host,
117
+ "http.scheme": urlObj.protocol.replace(":", ""),
118
+ "esm.package_fetch": true,
119
+ }), {
120
+ maxAttempts: HTTP_MODULE_FETCH_MAX_ATTEMPTS,
121
+ timeoutMs: HTTP_FETCH_TIMEOUT_MS,
122
+ shouldRetry: (error) => !(error instanceof HttpModuleResponseError) ||
123
+ shouldRetryHttpModuleFetch(error.status),
124
+ computeDelay: (attempt) => HTTP_MODULE_FETCH_RETRY_DELAY_MS * (attempt + 1),
125
+ onRetry: ({ error, attempt }) => {
126
+ httpCacheLog.warn("HTTP module fetch failed, retrying", {
127
+ url: safeUrl,
128
+ status: error instanceof HttpModuleResponseError ? error.status : undefined,
129
+ errorType: error instanceof HttpModuleRequestError
130
+ ? error.requestErrorType
131
+ : error.name,
132
+ attempt: attempt + 1,
133
+ });
134
+ },
135
+ });
136
+ }
137
+ catch (error) {
138
+ if (error instanceof HttpModuleResponseError) {
139
+ throw BUILD_FAILED.create({ detail: `Failed to fetch ${safeUrl}: ${error.status}` });
140
+ }
141
+ if (error instanceof HttpModuleRequestError) {
142
+ throw BUILD_FAILED.create({
143
+ detail: `Failed to fetch ${safeUrl}: ${error.requestErrorType}`,
144
+ });
145
+ }
146
+ throw error;
147
+ }
148
+ }
37
149
  // Re-export for backwards compatibility
38
150
  export { CACHE_DIR_TOKEN, detokenizeAllCachePaths, detokenizeCachePaths, tokenizeAllCachePaths, tokenizeCachePaths, };
39
151
  export { extractBundleDeps, hasIncompatibleFilePaths, normalizeHttpUrl };
@@ -42,6 +154,7 @@ export { embedSourceUrl, extractSourceUrl };
42
154
  export { __injectCachesForTests };
43
155
  async function cacheHttpModuleInternal(url, options) {
44
156
  const normalizedUrl = normalizeHttpUrl(url);
157
+ const safeUrl = sanitizeUrlForSpan(normalizedUrl);
45
158
  const cacheDir = ensureAbsoluteDir(options.cacheDir);
46
159
  const cacheIdentity = await buildHttpCacheIdentity(normalizedUrl, options);
47
160
  const identityMetadata = await buildHttpCacheIdentityMetadata(normalizedUrl, options);
@@ -62,7 +175,7 @@ async function cacheHttpModuleInternal(url, options) {
62
175
  // dependency could not be prefetched. Retry the prefetch instead of
63
176
  // handing the degradation on.
64
177
  httpCacheLog.debug("Local cache holds a degraded artifact, will re-fetch", {
65
- url: normalizedUrl,
178
+ url: safeUrl,
66
179
  hash,
67
180
  });
68
181
  }
@@ -72,7 +185,7 @@ async function cacheHttpModuleInternal(url, options) {
72
185
  const depsValid = await validateBundleDepsExist(deps, cacheDir);
73
186
  if (!depsValid) {
74
187
  httpCacheLog.debug("Local cache has missing deps, will re-fetch", {
75
- url: normalizedUrl,
188
+ url: safeUrl,
76
189
  hash,
77
190
  missingDeps: deps.length,
78
191
  });
@@ -96,12 +209,12 @@ async function cacheHttpModuleInternal(url, options) {
96
209
  if (processingStack.has(cacheIdentity)) {
97
210
  if (await exists(cachePath)) {
98
211
  httpCacheLog.debug("Circular dependency detected, file exists", {
99
- url: normalizedUrl,
212
+ url: safeUrl,
100
213
  });
101
214
  }
102
215
  else {
103
216
  httpCacheLog.debug("Circular dependency detected, file pending write", {
104
- url: normalizedUrl,
217
+ url: safeUrl,
105
218
  cachePath,
106
219
  });
107
220
  }
@@ -109,7 +222,7 @@ async function cacheHttpModuleInternal(url, options) {
109
222
  }
110
223
  let inFlight = inFlightHttpFetches.get(cacheKey);
111
224
  while (inFlight) {
112
- const result = await waitForInFlightFetch(inFlight, cacheKey);
225
+ const result = await waitForInFlightFetch(inFlight, HTTP_MODULE_FETCH_MAX_WAIT_MS);
113
226
  if (result !== undefined)
114
227
  return result;
115
228
  if (inFlightHttpFetches.get(cacheKey) === inFlight) {
@@ -127,7 +240,7 @@ async function cacheHttpModuleInternal(url, options) {
127
240
  const depsExist = await validateBundleDepsExist(deps, cacheDir);
128
241
  if (!depsExist) {
129
242
  httpCacheLog.debug("Cached code has missing bundle deps, will re-fetch", {
130
- url: normalizedUrl,
243
+ url: safeUrl,
131
244
  hash,
132
245
  missingDeps: deps.length,
133
246
  });
@@ -135,7 +248,7 @@ async function cacheHttpModuleInternal(url, options) {
135
248
  else {
136
249
  logger.debug(cacheResult.wasGzipped
137
250
  ? "[HTTP-CACHE] Distributed cache hit (gzip decoded)"
138
- : "[HTTP-CACHE] Distributed cache hit", { url: normalizedUrl, hash });
251
+ : "[HTTP-CACHE] Distributed cache hit", { url: safeUrl, hash });
139
252
  await fs.mkdir(cacheDir, { recursive: true });
140
253
  await fs.writeTextFile(cachePath, cachedCode);
141
254
  if (!(await exists(cachePath))) {
@@ -150,7 +263,7 @@ async function cacheHttpModuleInternal(url, options) {
150
263
  else {
151
264
  logger.debug(cacheResult.wasGzipped
152
265
  ? "[HTTP-CACHE] Distributed cache hit (gzip decoded, no deps)"
153
- : "[HTTP-CACHE] Distributed cache hit", { url: normalizedUrl, hash });
266
+ : "[HTTP-CACHE] Distributed cache hit", { url: safeUrl, hash });
154
267
  await fs.mkdir(cacheDir, { recursive: true });
155
268
  await fs.writeTextFile(cachePath, cachedCode);
156
269
  if (!(await exists(cachePath))) {
@@ -164,49 +277,22 @@ async function cacheHttpModuleInternal(url, options) {
164
277
  }
165
278
  else if (cacheResult.failReason && cacheResult.failReason !== "not_found") {
166
279
  httpCacheLog.debug("Distributed cache get failed", {
167
- url: normalizedUrl,
280
+ url: safeUrl,
168
281
  reason: cacheResult.failReason,
169
282
  });
170
283
  }
171
- httpCacheLog.debug("Fetching from network", { url: normalizedUrl });
172
- const urlObj = new URL(normalizedUrl);
173
- const controller = new AbortController();
174
- const timeout = setTimeout(() => controller.abort(), HTTP_FETCH_TIMEOUT_MS);
175
- const httpFetchStartTime = performance.now();
176
- const response = await withSpan(SpanNames.HTTP_CLIENT_FETCH, () => fetch(normalizedUrl, {
177
- headers: { "user-agent": "Mozilla/5.0 Veryfront/1.0" },
178
- signal: controller.signal,
179
- redirect: "follow",
180
- }), {
181
- "http.method": "GET",
182
- "http.url": normalizedUrl,
183
- "http.host": urlObj.host,
184
- "http.scheme": urlObj.protocol.replace(":", ""),
185
- "esm.package_fetch": true,
186
- });
187
- clearTimeout(timeout);
188
- const httpFetchDuration = Math.round(performance.now() - httpFetchStartTime);
189
- contentMetricsLog.debug("HTTP_MODULE_FETCH", {
190
- url: normalizedUrl.substring(0, 120),
191
- host: urlObj.host,
192
- duration_ms: httpFetchDuration,
193
- status: response.status,
194
- slow: httpFetchDuration > SLOW_HTTP_FETCH_THRESHOLD_MS,
195
- });
196
- if (!response.ok) {
197
- throw BUILD_FAILED.create({ detail: `Failed to fetch ${normalizedUrl}: ${response.status}` });
198
- }
199
- let code = await response.text();
200
- const contentType = response.headers.get("content-type") ?? "";
284
+ httpCacheLog.debug("Fetching from network", { url: safeUrl });
285
+ const fetchedModule = await fetchHttpModule(normalizedUrl);
286
+ let code = fetchedModule.code;
287
+ const contentType = fetchedModule.contentType;
201
288
  const isHtmlContent = contentType.includes("text/html") || looksLikeHtmlNotJs(code);
202
289
  if (isHtmlContent) {
203
290
  logger.error("[HTTP-CACHE] Received HTML instead of JavaScript, likely an esm.sh error page", {
204
- url: normalizedUrl,
291
+ url: safeUrl,
205
292
  contentType,
206
- preview: code.slice(0, 200),
207
293
  });
208
294
  throw BUNDLE_ERROR.create({
209
- detail: `Received HTML instead of JavaScript from ${normalizedUrl}. The package may not exist or failed to build on esm.sh.`,
295
+ detail: `Received HTML instead of JavaScript from ${safeUrl}. The package may not exist or failed to build on esm.sh.`,
210
296
  });
211
297
  }
212
298
  processingStack.add(cacheIdentity);
@@ -236,9 +322,9 @@ async function cacheHttpModuleInternal(url, options) {
236
322
  // retries the prefetch instead of inheriting one upstream blip for the
237
323
  // lifetime of the distributed entry.
238
324
  httpCacheLog.warn("Not caching a module with unresolved dynamic imports", {
239
- url: normalizedUrl,
325
+ url: safeUrl,
240
326
  hash,
241
- degraded,
327
+ degraded: degraded.map(sanitizeUrlForSpan),
242
328
  });
243
329
  return cachePath;
244
330
  }
@@ -249,7 +335,7 @@ async function cacheHttpModuleInternal(url, options) {
249
335
  if (error instanceof VeryfrontError && error.slug === "cache-invariant-violation") {
250
336
  throw error;
251
337
  }
252
- httpCacheLog.debug("Distributed cache set failed", { url: normalizedUrl, error });
338
+ httpCacheLog.debug("Distributed cache set failed", { url: safeUrl, error });
253
339
  }
254
340
  getCachedPaths().set(cacheKey, cachePath);
255
341
  const accumulator = bundleAccumulatorStorage.getStore();