@reckona/mreact-router 0.0.203 → 0.0.205
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/README.md +53 -9
- package/dist/actions.d.ts +6 -0
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +70 -10
- package/dist/actions.js.map +1 -1
- package/dist/adapters/aws-lambda.d.ts +15 -4
- package/dist/adapters/aws-lambda.d.ts.map +1 -1
- package/dist/adapters/aws-lambda.js +301 -63
- package/dist/adapters/aws-lambda.js.map +1 -1
- package/dist/adapters/cloudflare.d.ts +2 -1
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +18 -10
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/adapters/edge.d.ts +1 -1
- package/dist/adapters/edge.d.ts.map +1 -1
- package/dist/adapters/edge.js.map +1 -1
- package/dist/adapters/node.d.ts +5 -3
- package/dist/adapters/node.d.ts.map +1 -1
- package/dist/adapters/node.js +2 -1
- package/dist/adapters/node.js.map +1 -1
- package/dist/adapters/static.d.ts.map +1 -1
- package/dist/adapters/static.js +24 -2
- package/dist/adapters/static.js.map +1 -1
- package/dist/build.d.ts +1 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +91 -5
- package/dist/build.js.map +1 -1
- package/dist/built-runtime.d.ts +2 -0
- package/dist/built-runtime.d.ts.map +1 -1
- package/dist/built-runtime.js +16 -0
- package/dist/built-runtime.js.map +1 -1
- package/dist/cache.d.ts +3 -1
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +211 -13
- package/dist/cache.js.map +1 -1
- package/dist/cli.js +12 -1
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +32 -1
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +21 -2
- package/dist/config.js.map +1 -1
- package/dist/csp.d.ts +1 -0
- package/dist/csp.d.ts.map +1 -1
- package/dist/csp.js +11 -6
- package/dist/csp.js.map +1 -1
- package/dist/dehydrate-policy.d.ts +3 -0
- package/dist/dehydrate-policy.d.ts.map +1 -0
- package/dist/dehydrate-policy.js +16 -0
- package/dist/dehydrate-policy.js.map +1 -0
- package/dist/dev-server.d.ts +7 -2
- package/dist/dev-server.d.ts.map +1 -1
- package/dist/dev-server.js +120 -11
- package/dist/dev-server.js.map +1 -1
- package/dist/http.d.ts +1 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +147 -7
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +22 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +22 -8
- package/dist/logger.js.map +1 -1
- package/dist/metadata.d.ts +8 -0
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +60 -22
- package/dist/metadata.js.map +1 -1
- package/dist/navigation-marker.d.ts.map +1 -1
- package/dist/navigation-marker.js +72 -9
- package/dist/navigation-marker.js.map +1 -1
- package/dist/navigation.d.ts +1 -0
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +1 -1
- package/dist/navigation.js.map +1 -1
- package/dist/node-server.d.ts +5 -2
- package/dist/node-server.d.ts.map +1 -1
- package/dist/node-server.js +75 -6
- package/dist/node-server.js.map +1 -1
- package/dist/prerender-entry.d.ts +14 -0
- package/dist/prerender-entry.d.ts.map +1 -1
- package/dist/prerender-entry.js +40 -3
- package/dist/prerender-entry.js.map +1 -1
- package/dist/render.d.ts +9 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +102 -123
- package/dist/render.js.map +1 -1
- package/dist/serve.d.ts +8 -2
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +22 -20
- package/dist/serve.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +52 -19
- package/dist/session.js.map +1 -1
- package/dist/upgrade.d.ts +59 -2
- package/dist/upgrade.d.ts.map +1 -1
- package/dist/upgrade.js +282 -1
- package/dist/upgrade.js.map +1 -1
- package/dist/vite-config.d.ts +2 -0
- package/dist/vite-config.d.ts.map +1 -1
- package/dist/vite-config.js +2 -0
- package/dist/vite-config.js.map +1 -1
- package/dist/vite.d.ts +4 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +117 -6
- package/dist/vite.js.map +1 -1
- package/package.json +11 -11
- package/src/actions.ts +81 -11
- package/src/adapters/aws-lambda.ts +426 -115
- package/src/adapters/cloudflare.ts +29 -13
- package/src/adapters/edge.ts +2 -0
- package/src/adapters/node.ts +8 -10
- package/src/adapters/static.ts +31 -2
- package/src/build.ts +117 -10
- package/src/built-runtime.ts +28 -0
- package/src/cache.ts +262 -15
- package/src/cli.ts +14 -1
- package/src/client.ts +32 -1
- package/src/config.ts +43 -6
- package/src/csp.ts +14 -11
- package/src/dehydrate-policy.ts +23 -0
- package/src/dev-server.ts +188 -15
- package/src/http.ts +172 -6
- package/src/index.ts +14 -1
- package/src/logger.ts +47 -10
- package/src/metadata.ts +125 -60
- package/src/navigation-marker.ts +79 -9
- package/src/navigation.ts +1 -1
- package/src/node-server.ts +128 -22
- package/src/prerender-entry.ts +52 -3
- package/src/render.ts +807 -753
- package/src/serve.ts +49 -56
- package/src/session.ts +56 -25
- package/src/upgrade.ts +402 -3
- package/src/vite-config.ts +6 -0
- package/src/vite.ts +157 -5
package/dist/prerender-entry.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { responseHeadersContainCspNonce } from "./csp.js";
|
|
1
2
|
import { hasNavigationRouteMarker } from "./navigation-marker.js";
|
|
2
3
|
export const PRERENDERED_ROUTE_SCHEMA_VERSION = 4;
|
|
4
|
+
const validatedNavigationHtmlByEntry = new WeakMap();
|
|
3
5
|
export function isCurrentPrerenderedRoute(value) {
|
|
4
6
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
5
7
|
return false;
|
|
@@ -12,14 +14,35 @@ export function isCurrentPrerenderedRoute(value) {
|
|
|
12
14
|
!hasStoredHstsHeader(entry.headers) &&
|
|
13
15
|
isCanonicalHstsHeader(entry.strictTransportSecurity) &&
|
|
14
16
|
typeof entry.html === "string" &&
|
|
15
|
-
(entry.navigationHtml === undefined ||
|
|
16
|
-
(typeof entry.navigationHtml === "string" &&
|
|
17
|
-
hasNavigationRouteMarker(entry.navigationHtml))) &&
|
|
18
17
|
typeof entry.status === "number" &&
|
|
19
18
|
Number.isInteger(entry.status) &&
|
|
20
19
|
entry.status >= 100 &&
|
|
21
20
|
entry.status <= 599);
|
|
22
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Returns a navigation variant only after validating its route marker.
|
|
24
|
+
*
|
|
25
|
+
* Stored entries are stable objects during a runtime's lifetime, so the
|
|
26
|
+
* marker scan is cached by identity. Document requests never call this
|
|
27
|
+
* function and therefore never inspect navigation HTML.
|
|
28
|
+
*/
|
|
29
|
+
export function validatedPrerenderedNavigationHtml(entry) {
|
|
30
|
+
const cached = validatedNavigationHtmlByEntry.get(entry);
|
|
31
|
+
if (cached !== undefined) {
|
|
32
|
+
return cached === null ? undefined : cached;
|
|
33
|
+
}
|
|
34
|
+
const candidate = entry.navigationHtml;
|
|
35
|
+
const validated = typeof candidate === "string" && hasNavigationRouteMarker(candidate) ? candidate : null;
|
|
36
|
+
validatedNavigationHtmlByEntry.set(entry, validated);
|
|
37
|
+
return validated === null ? undefined : validated;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Adds a regenerated navigation variant only when it satisfies the marker
|
|
41
|
+
* contract. Invalid navigation output must not replace a valid document.
|
|
42
|
+
*/
|
|
43
|
+
export function mergePrerenderedNavigationHtml(entry, navigationHtml) {
|
|
44
|
+
return hasNavigationRouteMarker(navigationHtml) ? { ...entry, navigationHtml } : entry;
|
|
45
|
+
}
|
|
23
46
|
function hasStoredHstsHeader(headers) {
|
|
24
47
|
try {
|
|
25
48
|
return new Headers(headers).has("strict-transport-security");
|
|
@@ -36,6 +59,19 @@ function isCanonicalHstsHeader(value) {
|
|
|
36
59
|
export function isVisitorDependentResponse(response) {
|
|
37
60
|
return isVisitorDependentHeaders(response.headers);
|
|
38
61
|
}
|
|
62
|
+
export function protectNonceBearingResponse(response) {
|
|
63
|
+
if (!responseHeadersContainCspNonce(response.headers)) {
|
|
64
|
+
return response;
|
|
65
|
+
}
|
|
66
|
+
const headers = new Headers(response.headers);
|
|
67
|
+
headers.set("cache-control", "private, no-store");
|
|
68
|
+
headers.set("x-mreact-cache", "DYNAMIC");
|
|
69
|
+
return new Response(response.body, {
|
|
70
|
+
headers,
|
|
71
|
+
status: response.status,
|
|
72
|
+
statusText: response.statusText,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
39
75
|
export function storedPrerenderedRouteHeaders(headers) {
|
|
40
76
|
const stored = {};
|
|
41
77
|
headers.forEach((value, key) => {
|
|
@@ -87,6 +123,7 @@ function isVisitorDependentHeaders(headers) {
|
|
|
87
123
|
.some((directive) => /^(?:private|no-cache|no-store)(?:=|$)/i.test(directive.trim()));
|
|
88
124
|
return (headers.get("x-mreact-cache")?.toUpperCase() === "DYNAMIC" ||
|
|
89
125
|
headers.has("set-cookie") ||
|
|
126
|
+
responseHeadersContainCspNonce(headers) ||
|
|
90
127
|
hasVisitorDependentVary ||
|
|
91
128
|
forbidsSharedStorage);
|
|
92
129
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prerender-entry.js","sourceRoot":"","sources":["../src/prerender-entry.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BuiltPrerenderedRoute } from \"./build.js\";\nimport { hasNavigationRouteMarker } from \"./navigation-marker.js\";\n\nexport const PRERENDERED_ROUTE_SCHEMA_VERSION = 4;\n\nexport function isCurrentPrerenderedRoute(value: unknown): value is BuiltPrerenderedRoute {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return false;\n }\n\n const entry = value as Partial<BuiltPrerenderedRoute>;\n return (\n entry.schemaVersion === PRERENDERED_ROUTE_SCHEMA_VERSION &&\n isStringRecord(entry.headers) &&\n isShareableHeaderRecord(entry.headers) &&\n hasRequiredNavigationVary(entry.headers) &&\n !hasStoredHstsHeader(entry.headers) &&\n isCanonicalHstsHeader(entry.strictTransportSecurity) &&\n typeof entry.html === \"string\" &&\n (entry.navigationHtml === undefined ||\n (typeof entry.navigationHtml === \"string\" &&\n hasNavigationRouteMarker(entry.navigationHtml))) &&\n typeof entry.status === \"number\" &&\n Number.isInteger(entry.status) &&\n entry.status >= 100 &&\n entry.status <= 599\n );\n}\n\nfunction hasStoredHstsHeader(headers: Record<string, string>): boolean {\n try {\n return new Headers(headers).has(\"strict-transport-security\");\n } catch {\n return true;\n }\n}\n\nfunction isCanonicalHstsHeader(value: unknown): value is string | undefined {\n return (\n value === undefined ||\n (typeof value === \"string\" &&\n /^max-age=(?:0|[1-9][0-9]*)(?:; includeSubDomains)?(?:; preload)?$/.test(value))\n );\n}\n\nexport function isVisitorDependentResponse(response: Response): boolean {\n return isVisitorDependentHeaders(response.headers);\n}\n\nexport function storedPrerenderedRouteHeaders(headers: Headers): Record<string, string> {\n const stored: Record<string, string> = {};\n\n headers.forEach((value, key) => {\n if (key !== \"strict-transport-security\") {\n stored[key] = value;\n }\n });\n\n return stored;\n}\n\nexport function replayedPrerenderedRouteHeaders(\n entry: BuiltPrerenderedRoute,\n request: Request,\n): Headers {\n const headers = new Headers(entry.headers);\n\n if (!hasNavigationVary(headers)) {\n headers.append(\"vary\", \"x-mreact-navigation\");\n }\n\n if (entry.strictTransportSecurity !== undefined && request.url.startsWith(\"https://\")) {\n headers.set(\"strict-transport-security\", entry.strictTransportSecurity);\n }\n\n return headers;\n}\n\nfunction hasRequiredNavigationVary(headers: Record<string, string>): boolean {\n try {\n return hasNavigationVary(new Headers(headers));\n } catch {\n return false;\n }\n}\n\nfunction hasNavigationVary(headers: Headers): boolean {\n return (\n headers\n .get(\"vary\")\n ?.split(\",\")\n .some((value) => value.trim().toLowerCase() === \"x-mreact-navigation\") === true\n );\n}\n\nfunction isShareableHeaderRecord(headers: Record<string, string>): boolean {\n try {\n return !isVisitorDependentHeaders(new Headers(headers));\n } catch {\n return false;\n }\n}\n\nfunction isVisitorDependentHeaders(headers: Headers): boolean {\n const cacheControl = headers.get(\"cache-control\") ?? \"\";\n const vary = headers.get(\"vary\");\n const hasVisitorDependentVary =\n vary !== null &&\n vary.split(\",\").some((value) => value.trim().toLowerCase() !== \"x-mreact-navigation\");\n const forbidsSharedStorage = cacheControl\n .split(\",\")\n .some((directive) => /^(?:private|no-cache|no-store)(?:=|$)/i.test(directive.trim()));\n\n return (\n headers.get(\"x-mreact-cache\")?.toUpperCase() === \"DYNAMIC\" ||\n headers.has(\"set-cookie\") ||\n hasVisitorDependentVary ||\n forbidsSharedStorage\n );\n}\n\nfunction isStringRecord(value: unknown): value is Record<string, string> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n Object.values(value).every((entry) => typeof entry === \"string\")\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"prerender-entry.js","sourceRoot":"","sources":["../src/prerender-entry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,8BAA8B,EAAE,MAAM,UAAU,CAAC;AAC1D,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAElE,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,CAAC;AAElD,MAAM,8BAA8B,GAAG,IAAI,OAAO,EAAyB,CAAC;AAE5E,MAAM,UAAU,yBAAyB,CAAC,KAAc;IACtD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,KAAK,GAAG,KAAuC,CAAC;IACtD,OAAO,CACL,KAAK,CAAC,aAAa,KAAK,gCAAgC;QACxD,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC;QAC7B,uBAAuB,CAAC,KAAK,CAAC,OAAO,CAAC;QACtC,yBAAyB,CAAC,KAAK,CAAC,OAAO,CAAC;QACxC,CAAC,mBAAmB,CAAC,KAAK,CAAC,OAAO,CAAC;QACnC,qBAAqB,CAAC,KAAK,CAAC,uBAAuB,CAAC;QACpD,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;QAC9B,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ;QAChC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC;QAC9B,KAAK,CAAC,MAAM,IAAI,GAAG;QACnB,KAAK,CAAC,MAAM,IAAI,GAAG,CACpB,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kCAAkC,CAChD,KAA4B;IAE5B,MAAM,MAAM,GAAG,8BAA8B,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACzD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;IAC9C,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,cAAc,CAAC;IACvC,MAAM,SAAS,GACb,OAAO,SAAS,KAAK,QAAQ,IAAI,wBAAwB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1F,8BAA8B,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACrD,OAAO,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;AACpD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,8BAA8B,CAC5C,KAA4B,EAC5B,cAAsB;IAEtB,OAAO,wBAAwB,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;AACzF,CAAC;AAED,SAAS,mBAAmB,CAAC,OAA+B;IAC1D,IAAI,CAAC;QACH,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAc;IAC3C,OAAO,CACL,KAAK,KAAK,SAAS;QACnB,CAAC,OAAO,KAAK,KAAK,QAAQ;YACxB,mEAAmE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CACnF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,QAAkB;IAC3D,OAAO,yBAAyB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,QAAkB;IAC5D,IAAI,CAAC,8BAA8B,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACtD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,mBAAmB,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC;IACzC,OAAO,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE;QACjC,OAAO;QACP,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;KAChC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,OAAgB;IAC5D,MAAM,MAAM,GAA2B,EAAE,CAAC;IAE1C,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QAC7B,IAAI,GAAG,KAAK,2BAA2B,EAAE,CAAC;YACxC,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACtB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,+BAA+B,CAC7C,KAA4B,EAC5B,OAAgB;IAEhB,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAE3C,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,KAAK,CAAC,uBAAuB,KAAK,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACtF,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC1E,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,yBAAyB,CAAC,OAA+B;IAChE,IAAI,CAAC;QACH,OAAO,iBAAiB,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAgB;IACzC,OAAO,CACL,OAAO;SACJ,GAAG,CAAC,MAAM,CAAC;QACZ,EAAE,KAAK,CAAC,GAAG,CAAC;SACX,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,qBAAqB,CAAC,KAAK,IAAI,CAClF,CAAC;AACJ,CAAC;AAED,SAAS,uBAAuB,CAAC,OAA+B;IAC9D,IAAI,CAAC;QACH,OAAO,CAAC,yBAAyB,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,yBAAyB,CAAC,OAAgB;IACjD,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;IACxD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,uBAAuB,GAC3B,IAAI,KAAK,IAAI;QACb,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,qBAAqB,CAAC,CAAC;IACxF,MAAM,oBAAoB,GAAG,YAAY;SACtC,KAAK,CAAC,GAAG,CAAC;SACV,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,wCAAwC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAExF,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,WAAW,EAAE,KAAK,SAAS;QAC1D,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QACzB,8BAA8B,CAAC,OAAO,CAAC;QACvC,uBAAuB;QACvB,oBAAoB,CACrB,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACrB,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CACjE,CAAC;AACJ,CAAC","sourcesContent":["import type { BuiltPrerenderedRoute } from \"./build.js\";\nimport { responseHeadersContainCspNonce } from \"./csp.js\";\nimport { hasNavigationRouteMarker } from \"./navigation-marker.js\";\n\nexport const PRERENDERED_ROUTE_SCHEMA_VERSION = 4;\n\nconst validatedNavigationHtmlByEntry = new WeakMap<object, string | null>();\n\nexport function isCurrentPrerenderedRoute(value: unknown): value is BuiltPrerenderedRoute {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return false;\n }\n\n const entry = value as Partial<BuiltPrerenderedRoute>;\n return (\n entry.schemaVersion === PRERENDERED_ROUTE_SCHEMA_VERSION &&\n isStringRecord(entry.headers) &&\n isShareableHeaderRecord(entry.headers) &&\n hasRequiredNavigationVary(entry.headers) &&\n !hasStoredHstsHeader(entry.headers) &&\n isCanonicalHstsHeader(entry.strictTransportSecurity) &&\n typeof entry.html === \"string\" &&\n typeof entry.status === \"number\" &&\n Number.isInteger(entry.status) &&\n entry.status >= 100 &&\n entry.status <= 599\n );\n}\n\n/**\n * Returns a navigation variant only after validating its route marker.\n *\n * Stored entries are stable objects during a runtime's lifetime, so the\n * marker scan is cached by identity. Document requests never call this\n * function and therefore never inspect navigation HTML.\n */\nexport function validatedPrerenderedNavigationHtml(\n entry: BuiltPrerenderedRoute,\n): string | undefined {\n const cached = validatedNavigationHtmlByEntry.get(entry);\n if (cached !== undefined) {\n return cached === null ? undefined : cached;\n }\n\n const candidate = entry.navigationHtml;\n const validated =\n typeof candidate === \"string\" && hasNavigationRouteMarker(candidate) ? candidate : null;\n validatedNavigationHtmlByEntry.set(entry, validated);\n return validated === null ? undefined : validated;\n}\n\n/**\n * Adds a regenerated navigation variant only when it satisfies the marker\n * contract. Invalid navigation output must not replace a valid document.\n */\nexport function mergePrerenderedNavigationHtml(\n entry: BuiltPrerenderedRoute,\n navigationHtml: string,\n): BuiltPrerenderedRoute {\n return hasNavigationRouteMarker(navigationHtml) ? { ...entry, navigationHtml } : entry;\n}\n\nfunction hasStoredHstsHeader(headers: Record<string, string>): boolean {\n try {\n return new Headers(headers).has(\"strict-transport-security\");\n } catch {\n return true;\n }\n}\n\nfunction isCanonicalHstsHeader(value: unknown): value is string | undefined {\n return (\n value === undefined ||\n (typeof value === \"string\" &&\n /^max-age=(?:0|[1-9][0-9]*)(?:; includeSubDomains)?(?:; preload)?$/.test(value))\n );\n}\n\nexport function isVisitorDependentResponse(response: Response): boolean {\n return isVisitorDependentHeaders(response.headers);\n}\n\nexport function protectNonceBearingResponse(response: Response): Response {\n if (!responseHeadersContainCspNonce(response.headers)) {\n return response;\n }\n\n const headers = new Headers(response.headers);\n headers.set(\"cache-control\", \"private, no-store\");\n headers.set(\"x-mreact-cache\", \"DYNAMIC\");\n return new Response(response.body, {\n headers,\n status: response.status,\n statusText: response.statusText,\n });\n}\n\nexport function storedPrerenderedRouteHeaders(headers: Headers): Record<string, string> {\n const stored: Record<string, string> = {};\n\n headers.forEach((value, key) => {\n if (key !== \"strict-transport-security\") {\n stored[key] = value;\n }\n });\n\n return stored;\n}\n\nexport function replayedPrerenderedRouteHeaders(\n entry: BuiltPrerenderedRoute,\n request: Request,\n): Headers {\n const headers = new Headers(entry.headers);\n\n if (!hasNavigationVary(headers)) {\n headers.append(\"vary\", \"x-mreact-navigation\");\n }\n\n if (entry.strictTransportSecurity !== undefined && request.url.startsWith(\"https://\")) {\n headers.set(\"strict-transport-security\", entry.strictTransportSecurity);\n }\n\n return headers;\n}\n\nfunction hasRequiredNavigationVary(headers: Record<string, string>): boolean {\n try {\n return hasNavigationVary(new Headers(headers));\n } catch {\n return false;\n }\n}\n\nfunction hasNavigationVary(headers: Headers): boolean {\n return (\n headers\n .get(\"vary\")\n ?.split(\",\")\n .some((value) => value.trim().toLowerCase() === \"x-mreact-navigation\") === true\n );\n}\n\nfunction isShareableHeaderRecord(headers: Record<string, string>): boolean {\n try {\n return !isVisitorDependentHeaders(new Headers(headers));\n } catch {\n return false;\n }\n}\n\nfunction isVisitorDependentHeaders(headers: Headers): boolean {\n const cacheControl = headers.get(\"cache-control\") ?? \"\";\n const vary = headers.get(\"vary\");\n const hasVisitorDependentVary =\n vary !== null &&\n vary.split(\",\").some((value) => value.trim().toLowerCase() !== \"x-mreact-navigation\");\n const forbidsSharedStorage = cacheControl\n .split(\",\")\n .some((directive) => /^(?:private|no-cache|no-store)(?:=|$)/i.test(directive.trim()));\n\n return (\n headers.get(\"x-mreact-cache\")?.toUpperCase() === \"DYNAMIC\" ||\n headers.has(\"set-cookie\") ||\n responseHeadersContainCspNonce(headers) ||\n hasVisitorDependentVary ||\n forbidsSharedStorage\n );\n}\n\nfunction isStringRecord(value: unknown): value is Record<string, string> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n Object.values(value).every((entry) => typeof entry === \"string\")\n );\n}\n"]}
|
package/dist/render.d.ts
CHANGED
|
@@ -47,6 +47,7 @@ export interface RenderAppRequestOptions {
|
|
|
47
47
|
routeMatcher?: RouteMatcher | undefined;
|
|
48
48
|
routes?: readonly AppRoute[] | undefined;
|
|
49
49
|
serverModules?: ReadonlyMap<string, BuiltServerModuleArtifact> | undefined;
|
|
50
|
+
serverRenderArtifactLoader?: AppRouterServerRenderArtifactLoader | undefined;
|
|
50
51
|
serverModuleCacheVersion?: string | undefined;
|
|
51
52
|
devServerModuleCacheVersion?: string | undefined;
|
|
52
53
|
dev?: boolean | undefined;
|
|
@@ -58,6 +59,14 @@ export interface RenderAppRequestOptions {
|
|
|
58
59
|
vitePlugins?: readonly PluginOption[] | undefined;
|
|
59
60
|
}
|
|
60
61
|
export interface RenderAppRequestRuntimeOptions extends RenderAppRequestOptions {
|
|
62
|
+
/**
|
|
63
|
+
* Wraps a server-only page with build-time variant capture comments.
|
|
64
|
+
*
|
|
65
|
+
* This is an in-process render signal. It must not be derived from request
|
|
66
|
+
* headers because network clients are not allowed to change response shape
|
|
67
|
+
* outside the route-cache key.
|
|
68
|
+
*/
|
|
69
|
+
prerenderVariantCapture?: boolean | undefined;
|
|
61
70
|
/**
|
|
62
71
|
* Receives whether the render depended on the incoming request headers.
|
|
63
72
|
*
|
|
@@ -72,7 +81,6 @@ export interface RenderAppRequestRuntimeOptions extends RenderAppRequestOptions
|
|
|
72
81
|
headerDependent: () => boolean;
|
|
73
82
|
strictTransportSecurity: () => string | undefined;
|
|
74
83
|
} | undefined;
|
|
75
|
-
serverRenderArtifactLoader?: AppRouterServerRenderArtifactLoader | undefined;
|
|
76
84
|
}
|
|
77
85
|
export interface AppRouterRenderPreload {
|
|
78
86
|
promise: Promise<void>;
|
package/dist/render.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAEV,gBAAgB,EAChB,eAAe,EAChB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAOL,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,uBAAuB,CAAC;AAS/B,OAAO,EAIL,KAAK,yBAAyB,EAE/B,MAAM,6BAA6B,CAAC;AAUrC,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAExE,OAAO,EACL,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAIjC,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,KAAK,cAAc,EAOpB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
|
package/dist/render.js
CHANGED
|
@@ -5,7 +5,7 @@ import { access, readFile, stat } from "node:fs/promises";
|
|
|
5
5
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
6
6
|
import { formatDiagnostic, transform } from "@reckona/mreact-compiler";
|
|
7
7
|
import { createQueryClient, dehydrate, __MREACT_QUERY_STATE_SCRIPT_ID, installQueryAsyncStorage, runWithQueryClient, } from "@reckona/mreact-query";
|
|
8
|
-
import { createStringSink,
|
|
8
|
+
import { createStringSink, renderOutOfOrderBoundary, renderOutOfOrderReorderScript, renderReactNodeToString, renderToReadableStream, } from "@reckona/mreact-server";
|
|
9
9
|
import { createClientRouteInferenceCache, formatClientRouteInferenceDiagnostic, inferClientRouteModule, } from "./client-route-inference.js";
|
|
10
10
|
import { hydrationMarkerParts, routeMarkerParts, routeIdForPath, withHydrationMarkers, } from "./navigation-runtime.js";
|
|
11
11
|
import { assetPath } from "./assets.js";
|
|
@@ -16,8 +16,9 @@ import { dispatchServerActionRequest, prepareRouteServerActionPlaceholders, prep
|
|
|
16
16
|
import { serverActionCookie } from "./csrf.js";
|
|
17
17
|
import { activeRouteCacheContext, withRouteCacheContext, cachedRouteResponse, cacheRouteResponse, routeCacheKey, routeCachePolicyFromSource, } from "./cache.js";
|
|
18
18
|
import { trackRequestHeaderReads, withTrackedRequest, } from "./request-header-tracking.js";
|
|
19
|
-
import { configuredHstsHeader, hasInvalidConfiguredHsts
|
|
19
|
+
import { configuredHstsHeader, hasInvalidConfiguredHsts } from "./security-headers.js";
|
|
20
20
|
import { resolveRouterCacheLimit } from "./cache-config.js";
|
|
21
|
+
import { protectNonceBearingResponse } from "./prerender-entry.js";
|
|
21
22
|
import { importAppRouterBuiltFileModule, importAppRouterFileModule, importAppRouterSourceModule, fileImportMetaUrlPlugin, rewriteCompatVendorPlaceholderImportsForRunner, } from "./module-runner.js";
|
|
22
23
|
import { bytesResponse, htmlResponse } from "./http.js";
|
|
23
24
|
import { isNotFoundError, isRedirectError, rewriteLocation } from "./navigation.js";
|
|
@@ -30,7 +31,7 @@ import { bundleRouterModule } from "./bundle-pipeline.js";
|
|
|
30
31
|
import { viteDefineCacheKey, vitePluginsCacheKey } from "./vite-plugin-cache-key.js";
|
|
31
32
|
import { invokeRouterInstrumentation, traceContextFromRequest, } from "./trace.js";
|
|
32
33
|
import { mergeRouteMiddlewareControls, middlewareMatches, parseRouteMiddlewareControl, parseStaticMiddlewareConfig, shouldSkipMiddleware, validateRouteMiddlewareControl, } from "./middleware.js";
|
|
33
|
-
import {
|
|
34
|
+
import { applyFileConventionMetadataTemplate, fileConventionMetadataTemplate, injectHeadMetadata, mergeRouteMetadata, responseHeadersForMetadata, serializeRobots, serializeSitemap, validateRouteMetadata, } from "./metadata.js";
|
|
34
35
|
import { createSlotRenderContext, markShellBoundary, shellBoundaryId, splitLayoutSlot, warnUnconsumedRouteSlots, } from "./layout-composer.js";
|
|
35
36
|
import { importPolicyCacheKey, memoizedHashText, prebuiltRequestModuleArtifact, prebuiltRouteLoaderModuleArtifact, prebuiltServerComponentModuleCode, prebuiltServerModuleOutputMatches, prebuiltServerModuleOutputOptionsMatch, } from "./route-module-loader.js";
|
|
36
37
|
import { loadRouteDataFromModule, } from "./route-loader-runtime.js";
|
|
@@ -272,7 +273,7 @@ export async function renderAppRequest(options) {
|
|
|
272
273
|
};
|
|
273
274
|
invokeRouterInstrumentation(options.instrumentation?.onRequestStart, requestEvent);
|
|
274
275
|
const response = await renderAppRequestInternal({ ...options, requestUrl: url });
|
|
275
|
-
const finalResponse = await applyAppRouterResponseHook(response, options);
|
|
276
|
+
const finalResponse = protectNonceBearingResponse(await applyAppRouterResponseHook(response, options));
|
|
276
277
|
invokeRouterInstrumentation(options.instrumentation?.onRequestEnd, {
|
|
277
278
|
...requestEvent,
|
|
278
279
|
status: finalResponse.status,
|
|
@@ -291,31 +292,14 @@ function finishRenderTimingPhase(timing, startedAt, phaseName) {
|
|
|
291
292
|
}
|
|
292
293
|
timing.phases[phaseName] = logDurationMs(startedAt);
|
|
293
294
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
300
|
-
]);
|
|
301
|
-
}
|
|
302
|
-
function createDeferredSignal() {
|
|
303
|
-
let resolve;
|
|
304
|
-
const promise = new Promise((innerResolve) => {
|
|
305
|
-
resolve = innerResolve;
|
|
295
|
+
function observePromiseSettlement(promise) {
|
|
296
|
+
let settlement = { status: "pending" };
|
|
297
|
+
void promise.then((value) => {
|
|
298
|
+
settlement = { status: "fulfilled", value };
|
|
299
|
+
}, (error) => {
|
|
300
|
+
settlement = { error, status: "rejected" };
|
|
306
301
|
});
|
|
307
|
-
return
|
|
308
|
-
promise,
|
|
309
|
-
resolve() {
|
|
310
|
-
resolve?.();
|
|
311
|
-
},
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
async function waitForLoaderReadyOrSettled(dataPromise, loaderReady) {
|
|
315
|
-
if (loaderReady === undefined) {
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
await Promise.race([loaderReady, dataPromise.then(() => undefined, () => undefined)]);
|
|
302
|
+
return () => settlement;
|
|
319
303
|
}
|
|
320
304
|
function addRenderTimingPhaseDuration(timing, startedAt, phaseName) {
|
|
321
305
|
if (timing === undefined || startedAt === undefined) {
|
|
@@ -323,11 +307,6 @@ function addRenderTimingPhaseDuration(timing, startedAt, phaseName) {
|
|
|
323
307
|
}
|
|
324
308
|
timing.phases[phaseName] = (timing.phases[phaseName] ?? 0) + logDurationMs(startedAt);
|
|
325
309
|
}
|
|
326
|
-
function routeLoaderMayReturnControlResponse(code) {
|
|
327
|
-
return (/\b(?:redirect|redirectExternal|rewrite|notFound|throwNotFound)\s*\(/.test(code) ||
|
|
328
|
-
/\bnew\s+Response\s*\(/.test(code) ||
|
|
329
|
-
/\bResponse\.(?:redirect|json)\s*\(/.test(code));
|
|
330
|
-
}
|
|
331
310
|
async function waitForRenderPreload(options, timing) {
|
|
332
311
|
if (options.preload?.wait !== "before-render") {
|
|
333
312
|
return;
|
|
@@ -627,7 +606,6 @@ async function renderAppRequestInternal(options) {
|
|
|
627
606
|
const clientInference = routeAnalysis.clientInference;
|
|
628
607
|
const clientRoute = clientInference.client;
|
|
629
608
|
phaseStartedAt = renderTimingPhaseStartedAt(timing);
|
|
630
|
-
const loaderReady = routeAnalysis.hasLoader ? createDeferredSignal() : undefined;
|
|
631
609
|
const dataPromise = routeAnalysis.hasLoader
|
|
632
610
|
? loadRouteDataWithInstrumentation({
|
|
633
611
|
appDir: options.appDir,
|
|
@@ -649,9 +627,12 @@ async function renderAppRequestInternal(options) {
|
|
|
649
627
|
define: options.define,
|
|
650
628
|
vitePlugins: options.vitePlugins,
|
|
651
629
|
timing,
|
|
652
|
-
onLoaderReady: loaderReady?.resolve,
|
|
653
630
|
})
|
|
654
631
|
: undefined;
|
|
632
|
+
// Route analysis and loading-boundary discovery can continue before the loader is awaited.
|
|
633
|
+
// Observe an early rejection immediately so strict unhandled-rejection runtimes do not
|
|
634
|
+
// terminate before the request-level control/error handler receives the same promise.
|
|
635
|
+
void dataPromise?.catch(() => { });
|
|
655
636
|
finishRenderTimingPhase(timing, phaseStartedAt, "loaderStartMs");
|
|
656
637
|
recoveryRoute = {
|
|
657
638
|
clientRoute,
|
|
@@ -734,7 +715,7 @@ async function renderAppRequestInternal(options) {
|
|
|
734
715
|
data,
|
|
735
716
|
},
|
|
736
717
|
})
|
|
737
|
-
: withServerOnlyRouteMarkers(pageHtml, matched.route.path, options.request);
|
|
718
|
+
: withServerOnlyRouteMarkers(pageHtml, matched.route.path, options.request, options.prerenderVariantCapture === true);
|
|
738
719
|
let html = await runWithQueryClient(queryClient, () => applyLayouts({
|
|
739
720
|
appDir: options.appDir,
|
|
740
721
|
pageFile: matched.route.file,
|
|
@@ -796,39 +777,19 @@ async function renderAppRequestInternal(options) {
|
|
|
796
777
|
return response;
|
|
797
778
|
}
|
|
798
779
|
let streamData;
|
|
799
|
-
let streamDataPromise = dataPromise ?? Promise.resolve(undefined);
|
|
800
780
|
phaseStartedAt = renderTimingPhaseStartedAt(timing);
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
streamData = dataPromise === undefined ? undefined : await dataPromise;
|
|
804
|
-
}
|
|
805
|
-
finally {
|
|
806
|
-
finishRenderTimingPhase(timing, phaseStartedAt, "loaderWaitMs");
|
|
807
|
-
}
|
|
808
|
-
if (streamData instanceof Response) {
|
|
809
|
-
emitRenderTiming(options, timing, streamData.status);
|
|
810
|
-
return streamData;
|
|
811
|
-
}
|
|
812
|
-
streamDataPromise = Promise.resolve(streamData);
|
|
781
|
+
try {
|
|
782
|
+
streamData = dataPromise === undefined ? undefined : await dataPromise;
|
|
813
783
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
const settledData = await readSettledPromiseAtNextTask(streamDataPromise);
|
|
817
|
-
if (settledData.settled) {
|
|
818
|
-
if (settledData.value instanceof Response) {
|
|
819
|
-
emitRenderTiming(options, timing, settledData.value.status);
|
|
820
|
-
return settledData.value;
|
|
821
|
-
}
|
|
822
|
-
streamDataPromise = Promise.resolve(settledData.value);
|
|
823
|
-
}
|
|
824
|
-
else {
|
|
825
|
-
await waitForLoaderReadyOrSettled(streamDataPromise, loaderReady?.promise);
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
finally {
|
|
829
|
-
finishRenderTimingPhase(timing, phaseStartedAt, "loaderWaitMs");
|
|
830
|
-
}
|
|
784
|
+
finally {
|
|
785
|
+
finishRenderTimingPhase(timing, phaseStartedAt, "loaderWaitMs");
|
|
831
786
|
}
|
|
787
|
+
if (streamData instanceof Response) {
|
|
788
|
+
emitRenderTiming(options, timing, streamData.status);
|
|
789
|
+
return streamData;
|
|
790
|
+
}
|
|
791
|
+
const streamDataPromise = Promise.resolve(streamData);
|
|
792
|
+
const streamDataSettlement = loadingFile === undefined ? undefined : observePromiseSettlement(streamDataPromise);
|
|
832
793
|
await waitForRenderPreload(options, timing);
|
|
833
794
|
await loadServerRenderArtifacts(options, matched.route.file, timing);
|
|
834
795
|
phaseStartedAt = renderTimingPhaseStartedAt(timing);
|
|
@@ -849,16 +810,26 @@ async function renderAppRequestInternal(options) {
|
|
|
849
810
|
headers: { "content-type": "text/plain; charset=utf-8" },
|
|
850
811
|
});
|
|
851
812
|
}
|
|
852
|
-
if (loadingFile !== undefined) {
|
|
813
|
+
if (loadingFile !== undefined && streamDataSettlement !== undefined) {
|
|
853
814
|
phaseStartedAt = renderTimingPhaseStartedAt(timing);
|
|
854
|
-
const
|
|
815
|
+
const errorFile = await nearestExistingBoundaryFileForPage({
|
|
816
|
+
appDir: options.appDir,
|
|
817
|
+
filename: "error.mreact.tsx",
|
|
818
|
+
pageFile: matched.route.file,
|
|
819
|
+
serverSourceFiles: options.serverSourceFiles,
|
|
820
|
+
});
|
|
821
|
+
const streamResult = await runServerStreamModuleWithLoading(output.code, {
|
|
855
822
|
appDir: options.appDir,
|
|
856
823
|
assetBaseUrl: options.assetBaseUrl,
|
|
857
824
|
clientRoute,
|
|
858
825
|
data: streamDataPromise,
|
|
826
|
+
dataSettlement: streamDataSettlement,
|
|
859
827
|
define: options.define,
|
|
828
|
+
errorFile,
|
|
860
829
|
loadingFile,
|
|
861
830
|
markerRequest: options.request,
|
|
831
|
+
onRenderError: options.onRenderError,
|
|
832
|
+
prerenderVariantCapture: options.prerenderVariantCapture,
|
|
862
833
|
pageFile: matched.route.file,
|
|
863
834
|
params: matched.params,
|
|
864
835
|
queryClient,
|
|
@@ -877,7 +848,11 @@ async function renderAppRequestInternal(options) {
|
|
|
877
848
|
clientReferenceManifest: output.metadata.clientReferenceManifest,
|
|
878
849
|
});
|
|
879
850
|
finishRenderTimingPhase(timing, phaseStartedAt, "streamConstructionMs");
|
|
880
|
-
|
|
851
|
+
if (streamResult instanceof Response) {
|
|
852
|
+
emitRenderTiming(options, timing, streamResult.status);
|
|
853
|
+
return streamResult;
|
|
854
|
+
}
|
|
855
|
+
const response = withOptionalActionCookie(new Response(streamResult, {
|
|
881
856
|
headers: streamShellResponseHeaders,
|
|
882
857
|
}), preparedActions.csrfToken, preparedActions.csrfTokenIsNew === true);
|
|
883
858
|
appendNavigationVary(response.headers);
|
|
@@ -896,6 +871,7 @@ async function renderAppRequestInternal(options) {
|
|
|
896
871
|
assetBaseUrl: options.assetBaseUrl,
|
|
897
872
|
pageFile: matched.route.file,
|
|
898
873
|
markerRequest: options.request,
|
|
874
|
+
prerenderVariantCapture: options.prerenderVariantCapture,
|
|
899
875
|
props,
|
|
900
876
|
requestUrl: url.pathname,
|
|
901
877
|
routePath: matched.route.path,
|
|
@@ -982,7 +958,7 @@ async function renderAppRequestInternal(options) {
|
|
|
982
958
|
data,
|
|
983
959
|
},
|
|
984
960
|
})
|
|
985
|
-
: withServerOnlyRouteMarkers(pageHtml, matched.route.path, options.request);
|
|
961
|
+
: withServerOnlyRouteMarkers(pageHtml, matched.route.path, options.request, options.prerenderVariantCapture === true);
|
|
986
962
|
phaseStartedAt = renderTimingPhaseStartedAt(timing);
|
|
987
963
|
let html = await runWithQueryClient(queryClient, () => applyLayouts({
|
|
988
964
|
appDir: options.appDir,
|
|
@@ -1137,7 +1113,9 @@ async function renderAppRequestInternal(options) {
|
|
|
1137
1113
|
// after draining the body.
|
|
1138
1114
|
if (options.renderSignals !== undefined) {
|
|
1139
1115
|
const tracked = trackedRequest;
|
|
1140
|
-
options.renderSignals.headerDependent = () => sourceUsesRequestInput ||
|
|
1116
|
+
options.renderSignals.headerDependent = () => sourceUsesRequestInput ||
|
|
1117
|
+
invalidConfiguredHsts ||
|
|
1118
|
+
(tracked?.requestDependent() ?? true);
|
|
1141
1119
|
options.renderSignals.strictTransportSecurity = () => renderedStrictTransportSecurity;
|
|
1142
1120
|
}
|
|
1143
1121
|
}
|
|
@@ -1276,12 +1254,12 @@ function routePrefetchManifestScript(routeScripts, assetBaseUrl) {
|
|
|
1276
1254
|
function isNavigationRequest(request) {
|
|
1277
1255
|
return request.headers.get("x-mreact-navigation") === "1";
|
|
1278
1256
|
}
|
|
1279
|
-
function withServerOnlyRouteMarkers(html, routePath, request) {
|
|
1280
|
-
const marker = serverOnlyRouteMarkerParts(routePath, request);
|
|
1257
|
+
function withServerOnlyRouteMarkers(html, routePath, request, prerenderVariantCapture) {
|
|
1258
|
+
const marker = serverOnlyRouteMarkerParts(routePath, request, prerenderVariantCapture);
|
|
1281
1259
|
return marker === undefined ? html : `${marker.prefix}${html}${marker.suffix}`;
|
|
1282
1260
|
}
|
|
1283
|
-
function serverOnlyRouteMarkerParts(routePath, request) {
|
|
1284
|
-
if (
|
|
1261
|
+
function serverOnlyRouteMarkerParts(routePath, request, prerenderVariantCapture) {
|
|
1262
|
+
if (prerenderVariantCapture) {
|
|
1285
1263
|
return prerenderVariantMarkerParts(routePath);
|
|
1286
1264
|
}
|
|
1287
1265
|
return isNavigationRequest(request) ? routeMarkerParts(routePath) : undefined;
|
|
@@ -1295,9 +1273,7 @@ export function prerenderVariantMarkerParts(routePath) {
|
|
|
1295
1273
|
}
|
|
1296
1274
|
function appendNavigationVary(headers) {
|
|
1297
1275
|
const vary = headers.get("vary");
|
|
1298
|
-
if (vary
|
|
1299
|
-
?.split(",")
|
|
1300
|
-
.some((value) => value.trim().toLowerCase() === "x-mreact-navigation") === true) {
|
|
1276
|
+
if (vary?.split(",").some((value) => value.trim().toLowerCase() === "x-mreact-navigation") === true) {
|
|
1301
1277
|
return;
|
|
1302
1278
|
}
|
|
1303
1279
|
headers.append("vary", "x-mreact-navigation");
|
|
@@ -1415,15 +1391,7 @@ async function renderSpecialRoute(options) {
|
|
|
1415
1391
|
}
|
|
1416
1392
|
}
|
|
1417
1393
|
const props = {
|
|
1418
|
-
|
|
1419
|
-
debug: errorDebugContext(options.error, options.routePath),
|
|
1420
|
-
error: normalizeErrorForProps(options.error),
|
|
1421
|
-
params: {},
|
|
1422
|
-
queryClient: createQueryClient(),
|
|
1423
|
-
request: options.request,
|
|
1424
|
-
requestId: requestIdForErrorContext(options.request),
|
|
1425
|
-
routeId: routeIdForPath(options.routePath ?? new URL(options.request.url).pathname),
|
|
1426
|
-
traceId: traceContextFromRequest(options.request)?.traceId,
|
|
1394
|
+
...errorBoundaryProps(options.error, options.request, options.routePath),
|
|
1427
1395
|
};
|
|
1428
1396
|
const pageHtml = await renderServerFileToHtml(options.routeFile, props, options.serverModules, options.serverModuleCacheVersion, options.serverSourceFiles, options.define, options.vitePlugins);
|
|
1429
1397
|
const pageHtmlForLayout = options.navigation?.clientRoute === true
|
|
@@ -1457,6 +1425,19 @@ async function renderSpecialRoute(options) {
|
|
|
1457
1425
|
status: options.status,
|
|
1458
1426
|
});
|
|
1459
1427
|
}
|
|
1428
|
+
function errorBoundaryProps(error, request, routePath) {
|
|
1429
|
+
return {
|
|
1430
|
+
data: undefined,
|
|
1431
|
+
debug: errorDebugContext(error, routePath),
|
|
1432
|
+
error: normalizeErrorForProps(error),
|
|
1433
|
+
params: {},
|
|
1434
|
+
queryClient: createQueryClient(),
|
|
1435
|
+
request,
|
|
1436
|
+
requestId: requestIdForErrorContext(request),
|
|
1437
|
+
routeId: routeIdForPath(routePath ?? new URL(request.url).pathname),
|
|
1438
|
+
traceId: traceContextFromRequest(request)?.traceId,
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1460
1441
|
async function renderServerFileToHtml(file, props, serverModules, serverModuleCacheVersion, serverSourceFiles, define, vitePlugins) {
|
|
1461
1442
|
const code = await readServerSourceFile(file, serverModuleCacheVersion, serverSourceFiles);
|
|
1462
1443
|
const output = transformServerModule({
|
|
@@ -2216,7 +2197,7 @@ function runServerStreamModule(code, options) {
|
|
|
2216
2197
|
data: options.props.data,
|
|
2217
2198
|
},
|
|
2218
2199
|
})
|
|
2219
|
-
: serverOnlyRouteMarkerParts(options.routePath, options.markerRequest);
|
|
2200
|
+
: serverOnlyRouteMarkerParts(options.routePath, options.markerRequest, options.prerenderVariantCapture === true);
|
|
2220
2201
|
sink.append("<!DOCTYPE html>");
|
|
2221
2202
|
sink.append(clientNavigationHeadTags({
|
|
2222
2203
|
assetBaseUrl: options.assetBaseUrl,
|
|
@@ -2348,7 +2329,14 @@ async function runServerStreamModuleWithLoading(code, options) {
|
|
|
2348
2329
|
request: { url: options.requestUrl },
|
|
2349
2330
|
},
|
|
2350
2331
|
})
|
|
2351
|
-
: serverOnlyRouteMarkerParts(options.routePath, options.markerRequest);
|
|
2332
|
+
: serverOnlyRouteMarkerParts(options.routePath, options.markerRequest, options.prerenderVariantCapture === true);
|
|
2333
|
+
const dataSettlement = options.dataSettlement();
|
|
2334
|
+
if (dataSettlement.status === "rejected") {
|
|
2335
|
+
throw dataSettlement.error;
|
|
2336
|
+
}
|
|
2337
|
+
if (dataSettlement.status === "fulfilled" && dataSettlement.value instanceof Response) {
|
|
2338
|
+
return dataSettlement.value;
|
|
2339
|
+
}
|
|
2352
2340
|
return renderToReadableStream((sink) => {
|
|
2353
2341
|
sink.append("<!DOCTYPE html>");
|
|
2354
2342
|
sink.append(clientNavigationHeadTags({
|
|
@@ -2360,13 +2348,31 @@ async function runServerStreamModuleWithLoading(code, options) {
|
|
|
2360
2348
|
sink.append(shell.prefix);
|
|
2361
2349
|
}
|
|
2362
2350
|
sink.append(marker?.prefix ?? "");
|
|
2363
|
-
|
|
2351
|
+
renderOutOfOrderBoundary(sink, "mreact-route", options.data, async (boundarySink, data) => {
|
|
2364
2352
|
await appendServerStreamModule(code, boundarySink, withTrackedRequest({
|
|
2365
2353
|
data,
|
|
2366
2354
|
params: options.params,
|
|
2367
2355
|
queryClient: options.queryClient,
|
|
2368
2356
|
}, options.request, options.trackedRequest), options.pageFile, options.serverModules, options.serverModuleCacheVersion, options.define, options.vitePlugins, options.importPolicy);
|
|
2369
2357
|
}, {
|
|
2358
|
+
async catch(boundarySink, error) {
|
|
2359
|
+
try {
|
|
2360
|
+
options.onRenderError?.(error);
|
|
2361
|
+
}
|
|
2362
|
+
catch {
|
|
2363
|
+
// Error reporting is best-effort and must not truncate a response that already flushed.
|
|
2364
|
+
}
|
|
2365
|
+
if (options.errorFile === undefined) {
|
|
2366
|
+
boundarySink.append("Internal Server Error");
|
|
2367
|
+
return;
|
|
2368
|
+
}
|
|
2369
|
+
try {
|
|
2370
|
+
boundarySink.append(await renderServerFileToHtml(options.errorFile, errorBoundaryProps(error, options.request, options.routePath), options.serverModules, options.serverModuleCacheVersion, options.serverSourceFiles, options.define, options.vitePlugins));
|
|
2371
|
+
}
|
|
2372
|
+
catch {
|
|
2373
|
+
boundarySink.append("Internal Server Error");
|
|
2374
|
+
}
|
|
2375
|
+
},
|
|
2370
2376
|
placeholder(boundarySink) {
|
|
2371
2377
|
boundarySink.append(loadingHtml);
|
|
2372
2378
|
},
|
|
@@ -2379,35 +2385,6 @@ async function runServerStreamModuleWithLoading(code, options) {
|
|
|
2379
2385
|
renderOutOfOrderReorderScript(sink);
|
|
2380
2386
|
});
|
|
2381
2387
|
}
|
|
2382
|
-
function renderVisibleOutOfOrderBoundary(sink, id, value, render, options = {}) {
|
|
2383
|
-
const placeholderSink = createStringSink();
|
|
2384
|
-
void options.placeholder?.(placeholderSink);
|
|
2385
|
-
const placeholderTag = normalizeVisibleOutOfOrderPlaceholderTag(options.placeholderTag);
|
|
2386
|
-
sink.append(`<${placeholderTag} data-mreact-oob-placeholder="${escapeHtmlAttribute(id)}">${placeholderSink.toString()}</${placeholderTag}>`);
|
|
2387
|
-
const task = renderVisibleOutOfOrderFragment(sink, id, value, render, options);
|
|
2388
|
-
if (sink.defer === undefined) {
|
|
2389
|
-
void task;
|
|
2390
|
-
return;
|
|
2391
|
-
}
|
|
2392
|
-
sink.defer(task);
|
|
2393
|
-
}
|
|
2394
|
-
function normalizeVisibleOutOfOrderPlaceholderTag(tag) {
|
|
2395
|
-
if (typeof tag !== "string") {
|
|
2396
|
-
return "span";
|
|
2397
|
-
}
|
|
2398
|
-
const normalized = tag.trim().toLowerCase();
|
|
2399
|
-
return /^[a-z][a-z0-9-]*$/.test(normalized) ? normalized : "span";
|
|
2400
|
-
}
|
|
2401
|
-
async function renderVisibleOutOfOrderFragment(sink, id, value, render, options) {
|
|
2402
|
-
const fragmentSink = createStringSink();
|
|
2403
|
-
await renderAsyncBoundary(fragmentSink, value, render, options.catch === undefined ? {} : { catch: options.catch });
|
|
2404
|
-
await fragmentSink.drain();
|
|
2405
|
-
sink.append(renderVisibleOutOfOrderFragmentHtml(id, fragmentSink.toString()));
|
|
2406
|
-
}
|
|
2407
|
-
function renderVisibleOutOfOrderFragmentHtml(id, html) {
|
|
2408
|
-
const escapedId = escapeHtmlAttribute(id);
|
|
2409
|
-
return `<template data-mreact-oob-fragment="${escapedId}">${html}</template><mreact-oob-complete hidden data-mreact-oob-complete="${escapedId}"></mreact-oob-complete>`;
|
|
2410
|
-
}
|
|
2411
2388
|
async function appendServerStreamModule(code, sink, props, sourcefile, serverModules, serverModuleCacheVersion, define, vitePlugins, importPolicy) {
|
|
2412
2389
|
const module = await loadServerStreamModule(code, sourcefile, serverModules, serverModuleCacheVersion, define, vitePlugins, importPolicy);
|
|
2413
2390
|
const component = selectStreamComponent(module);
|
|
@@ -3117,15 +3094,16 @@ async function loadComposedRouteMetadata(options) {
|
|
|
3117
3094
|
if (cacheKey !== undefined) {
|
|
3118
3095
|
const cached = readRouterRuntimeCacheEntry(composedRouteMetadataCache, cacheKey, composedRouteMetadataCacheCounters);
|
|
3119
3096
|
if (cached !== undefined) {
|
|
3120
|
-
|
|
3097
|
+
const entry = await cached;
|
|
3098
|
+
return applyFileConventionMetadataTemplate(entry.metadata, entry.template, options.context.params);
|
|
3121
3099
|
}
|
|
3122
3100
|
}
|
|
3123
3101
|
try {
|
|
3124
3102
|
const loaded = await loadComposedRouteMetadataUncached(options);
|
|
3125
3103
|
if (cacheKey !== undefined && !loaded.dynamic) {
|
|
3126
|
-
setBoundedCacheEntry(composedRouteMetadataCache, cacheKey, Promise.resolve(loaded.metadata), maxComposedRouteMetadataCacheEntries, composedRouteMetadataCacheCounters);
|
|
3104
|
+
setBoundedCacheEntry(composedRouteMetadataCache, cacheKey, Promise.resolve({ metadata: loaded.metadata, template: loaded.template }), maxComposedRouteMetadataCacheEntries, composedRouteMetadataCacheCounters);
|
|
3127
3105
|
}
|
|
3128
|
-
return loaded.metadata;
|
|
3106
|
+
return applyFileConventionMetadataTemplate(loaded.metadata, loaded.template, options.context.params);
|
|
3129
3107
|
}
|
|
3130
3108
|
catch (error) {
|
|
3131
3109
|
if (cacheKey !== undefined) {
|
|
@@ -3191,7 +3169,8 @@ async function loadComposedRouteMetadataUncached(options) {
|
|
|
3191
3169
|
}
|
|
3192
3170
|
return {
|
|
3193
3171
|
dynamic,
|
|
3194
|
-
metadata:
|
|
3172
|
+
metadata: mergeRouteMetadata(metadata),
|
|
3173
|
+
template: fileConventionMetadataTemplate(options.routes, options.filename),
|
|
3195
3174
|
};
|
|
3196
3175
|
}
|
|
3197
3176
|
function hasMetadataExport(code) {
|