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.
- package/esm/cli/templates/manifest.d.ts +1 -0
- package/esm/cli/templates/manifest.js +3 -2
- package/esm/deno.js +1 -1
- package/esm/src/html/hydration-script-builder/dev-client-renderer.js +1 -1
- package/esm/src/html/hydration-script-builder/prod-scripts.js +1 -1
- package/esm/src/html/hydration-script-builder/templates/router.d.ts.map +1 -1
- package/esm/src/html/hydration-script-builder/templates/router.js +35 -11
- package/esm/src/html/utils.d.ts.map +1 -1
- package/esm/src/html/utils.js +19 -45
- package/esm/src/index.client.d.ts +6 -21
- package/esm/src/index.client.d.ts.map +1 -1
- package/esm/src/index.client.js +6 -21
- package/esm/src/react/runtime/core.d.ts +22 -0
- package/esm/src/react/runtime/core.d.ts.map +1 -1
- package/esm/src/react/runtime/core.js +1 -1
- package/esm/src/rendering/orchestrator/module-collection.d.ts +8 -0
- package/esm/src/rendering/orchestrator/module-collection.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/module-collection.js +10 -0
- package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/pipeline.js +2 -2
- package/esm/src/routing/api/context-builder.d.ts +7 -0
- package/esm/src/routing/api/context-builder.d.ts.map +1 -1
- package/esm/src/routing/api/context-builder.js +20 -2
- package/esm/src/server/handlers/dev/files/esbuild-plugins.d.ts.map +1 -1
- package/esm/src/server/handlers/dev/files/esbuild-plugins.js +22 -0
- package/esm/src/server/handlers/request/module/page-data-endpoint-handler.js +24 -1
- package/esm/src/server/handlers/response/not-found.d.ts +0 -1
- package/esm/src/server/handlers/response/not-found.d.ts.map +1 -1
- package/esm/src/server/handlers/response/not-found.js +9 -110
- package/esm/src/server/services/rsc/orchestrators/handler.d.ts +3 -1
- package/esm/src/server/services/rsc/orchestrators/handler.d.ts.map +1 -1
- package/esm/src/server/services/rsc/orchestrators/handler.js +13 -8
- package/esm/src/server/services/rsc/orchestrators/render-handler.d.ts +1 -1
- package/esm/src/server/services/rsc/orchestrators/render-handler.d.ts.map +1 -1
- package/esm/src/server/services/rsc/orchestrators/render-handler.js +2 -2
- package/esm/src/transforms/esm/http-cache.d.ts.map +1 -1
- package/esm/src/transforms/esm/http-cache.js +132 -46
- package/esm/src/transforms/esm/in-flight-manager.d.ts +3 -2
- package/esm/src/transforms/esm/in-flight-manager.d.ts.map +1 -1
- package/esm/src/transforms/esm/in-flight-manager.js +6 -5
- package/esm/src/transforms/import-rewriter/strategies/bare-strategy.d.ts.map +1 -1
- package/esm/src/transforms/import-rewriter/strategies/bare-strategy.js +16 -2
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- 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;
|
|
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 {
|
|
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
|
-
|
|
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;
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
}
|
|
@@ -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;
|
|
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;
|
|
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:
|
|
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:
|
|
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:
|
|
212
|
+
url: safeUrl,
|
|
100
213
|
});
|
|
101
214
|
}
|
|
102
215
|
else {
|
|
103
216
|
httpCacheLog.debug("Circular dependency detected, file pending write", {
|
|
104
|
-
url:
|
|
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,
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
280
|
+
url: safeUrl,
|
|
168
281
|
reason: cacheResult.failReason,
|
|
169
282
|
});
|
|
170
283
|
}
|
|
171
|
-
httpCacheLog.debug("Fetching from network", { url:
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
const
|
|
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:
|
|
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 ${
|
|
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:
|
|
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:
|
|
338
|
+
httpCacheLog.debug("Distributed cache set failed", { url: safeUrl, error });
|
|
253
339
|
}
|
|
254
340
|
getCachedPaths().set(cacheKey, cachePath);
|
|
255
341
|
const accumulator = bundleAccumulatorStorage.getStore();
|