@reckona/mreact-router 0.0.202 → 0.0.203
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 +5 -2
- package/dist/actions.d.ts +2 -3
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +61 -41
- package/dist/actions.js.map +1 -1
- package/dist/adapters/cloudflare.d.ts +3 -1
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +26 -10
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/build.d.ts +3 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +98 -12
- package/dist/build.js.map +1 -1
- package/dist/cache.d.ts +1 -1
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +5 -4
- package/dist/cache.js.map +1 -1
- package/dist/cli-options.d.ts +3 -0
- package/dist/cli-options.d.ts.map +1 -1
- package/dist/cli-options.js +10 -2
- package/dist/cli-options.js.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +5 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +11 -2
- package/dist/client.js.map +1 -1
- package/dist/navigation-marker.d.ts +2 -0
- package/dist/navigation-marker.d.ts.map +1 -0
- package/dist/navigation-marker.js +294 -0
- package/dist/navigation-marker.js.map +1 -0
- package/dist/navigation-runtime.d.ts +1 -1
- package/dist/navigation-runtime.d.ts.map +1 -1
- package/dist/navigation-runtime.js +1 -1
- package/dist/navigation-runtime.js.map +1 -1
- package/dist/prerender-entry.d.ts +3 -1
- package/dist/prerender-entry.d.ts.map +1 -1
- package/dist/prerender-entry.js +61 -3
- package/dist/prerender-entry.js.map +1 -1
- package/dist/render.d.ts +7 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +51 -23
- package/dist/render.js.map +1 -1
- package/dist/serve.d.ts +3 -0
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +54 -30
- package/dist/serve.js.map +1 -1
- package/package.json +11 -11
- package/src/actions.ts +72 -56
- package/src/adapters/cloudflare.ts +40 -10
- package/src/build.ts +107 -10
- package/src/cache.ts +10 -4
- package/src/cli-options.ts +15 -2
- package/src/cli.ts +2 -1
- package/src/client.ts +15 -2
- package/src/navigation-marker.ts +353 -0
- package/src/navigation-runtime.ts +1 -0
- package/src/prerender-entry.ts +78 -5
- package/src/render.ts +80 -24
- package/src/serve.ts +71 -34
package/src/render.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
installQueryAsyncStorage,
|
|
17
17
|
runWithQueryClient,
|
|
18
18
|
type DehydratedQueryClient,
|
|
19
|
+
type DehydrateOptions,
|
|
19
20
|
type QueryClient,
|
|
20
21
|
} from "@reckona/mreact-query";
|
|
21
22
|
import {
|
|
@@ -35,9 +36,9 @@ import {
|
|
|
35
36
|
} from "./client-route-inference.js";
|
|
36
37
|
import {
|
|
37
38
|
hydrationMarkerParts,
|
|
39
|
+
routeMarkerParts,
|
|
38
40
|
routeIdForPath,
|
|
39
41
|
withHydrationMarkers,
|
|
40
|
-
withRouteMarkers,
|
|
41
42
|
} from "./navigation-runtime.js";
|
|
42
43
|
import { assetPath } from "./assets.js";
|
|
43
44
|
import { escapeHtmlAttribute } from "@reckona/mreact-shared/html-escape";
|
|
@@ -202,6 +203,7 @@ export interface RenderAppRequestOptions {
|
|
|
202
203
|
clientStylesByFile?: ReadonlyMap<string, readonly string[]>;
|
|
203
204
|
clientStyles?: ReadonlyMap<string, readonly string[]>;
|
|
204
205
|
define?: UserConfig["define"] | undefined;
|
|
206
|
+
dehydrateOptions?: DehydrateOptions | undefined;
|
|
205
207
|
env?: unknown;
|
|
206
208
|
importPolicy?: AppRouterImportPolicy | undefined;
|
|
207
209
|
instrumentation?: RouterInstrumentation | undefined;
|
|
@@ -243,7 +245,12 @@ export interface RenderAppRequestRuntimeOptions extends RenderAppRequestOptions
|
|
|
243
245
|
* dependent. Call it only after draining the response body, because a
|
|
244
246
|
* streamed render can read headers while its deferred parts resolve.
|
|
245
247
|
*/
|
|
246
|
-
renderSignals?:
|
|
248
|
+
renderSignals?:
|
|
249
|
+
| {
|
|
250
|
+
headerDependent: () => boolean;
|
|
251
|
+
strictTransportSecurity: () => string | undefined;
|
|
252
|
+
}
|
|
253
|
+
| undefined;
|
|
247
254
|
serverRenderArtifactLoader?: AppRouterServerRenderArtifactLoader | undefined;
|
|
248
255
|
}
|
|
249
256
|
|
|
@@ -1000,6 +1007,7 @@ async function renderAppRequestInternal(
|
|
|
1000
1007
|
let trackedRequest: TrackedHeaderRequest | undefined;
|
|
1001
1008
|
let sourceUsesRequestInput = true;
|
|
1002
1009
|
let invalidConfiguredHsts = false;
|
|
1010
|
+
let renderedStrictTransportSecurity: string | undefined;
|
|
1003
1011
|
return (await withRouteCacheContext(options.routeCache, async () => {
|
|
1004
1012
|
try {
|
|
1005
1013
|
if (matched.route.kind === "asset") {
|
|
@@ -1076,7 +1084,13 @@ async function renderAppRequestInternal(
|
|
|
1076
1084
|
finishRenderTimingPhase(timing, phaseStartedAt, "sourceAnalysisMs");
|
|
1077
1085
|
const cachePolicy = originalAnalysis.cachePolicy;
|
|
1078
1086
|
const navigationScript = options.navigationScripts?.get(matched.route.path);
|
|
1079
|
-
const
|
|
1087
|
+
const navigationRequest = isNavigationRequest(options.request);
|
|
1088
|
+
const cacheKey = routeCacheKey(
|
|
1089
|
+
options.appDir,
|
|
1090
|
+
matched.route.path,
|
|
1091
|
+
url,
|
|
1092
|
+
navigationRequest ? "navigation" : "document",
|
|
1093
|
+
);
|
|
1080
1094
|
const mayUseRouteCache =
|
|
1081
1095
|
!sourceUsesRequestInput &&
|
|
1082
1096
|
(cachePolicy === undefined
|
|
@@ -1176,7 +1190,7 @@ async function renderAppRequestInternal(
|
|
|
1176
1190
|
clientRoute,
|
|
1177
1191
|
props: {
|
|
1178
1192
|
params: matched.params,
|
|
1179
|
-
request: { url:
|
|
1193
|
+
request: { url: url.pathname },
|
|
1180
1194
|
},
|
|
1181
1195
|
routePath: matched.route.path,
|
|
1182
1196
|
script: clientScript,
|
|
@@ -1265,16 +1279,11 @@ async function renderAppRequestInternal(
|
|
|
1265
1279
|
script: clientScript,
|
|
1266
1280
|
props: {
|
|
1267
1281
|
params: matched.params,
|
|
1268
|
-
request: { url:
|
|
1282
|
+
request: { url: url.pathname },
|
|
1269
1283
|
data,
|
|
1270
1284
|
},
|
|
1271
1285
|
})
|
|
1272
|
-
:
|
|
1273
|
-
? withRouteMarkers({
|
|
1274
|
-
html: pageHtml,
|
|
1275
|
-
routePath: matched.route.path,
|
|
1276
|
-
})
|
|
1277
|
-
: pageHtml;
|
|
1286
|
+
: withServerOnlyRouteMarkers(pageHtml, matched.route.path, options.request);
|
|
1278
1287
|
let html = await runWithQueryClient(queryClient, () =>
|
|
1279
1288
|
applyLayouts({
|
|
1280
1289
|
appDir: options.appDir,
|
|
@@ -1324,7 +1333,7 @@ async function renderAppRequestInternal(
|
|
|
1324
1333
|
html,
|
|
1325
1334
|
originalAnalysis.authIncludesClaims ? currentAuthClaims() : undefined,
|
|
1326
1335
|
);
|
|
1327
|
-
html = injectQueryState(html, dehydrate(queryClient));
|
|
1336
|
+
html = injectQueryState(html, dehydrate(queryClient, options.dehydrateOptions));
|
|
1328
1337
|
const response = withOptionalActionCookie(
|
|
1329
1338
|
htmlResponse(
|
|
1330
1339
|
applyActionHtmlReplacements(
|
|
@@ -1346,6 +1355,7 @@ async function renderAppRequestInternal(
|
|
|
1346
1355
|
preparedActions.csrfToken,
|
|
1347
1356
|
preparedActions.csrfTokenIsNew === true,
|
|
1348
1357
|
);
|
|
1358
|
+
appendNavigationVary(response.headers);
|
|
1349
1359
|
emitRenderTiming(options, timing, response.status);
|
|
1350
1360
|
return response;
|
|
1351
1361
|
}
|
|
@@ -1412,10 +1422,12 @@ async function renderAppRequestInternal(
|
|
|
1412
1422
|
data: streamDataPromise,
|
|
1413
1423
|
define: options.define,
|
|
1414
1424
|
loadingFile,
|
|
1425
|
+
markerRequest: options.request,
|
|
1415
1426
|
pageFile: matched.route.file,
|
|
1416
1427
|
params: matched.params,
|
|
1417
1428
|
queryClient,
|
|
1418
1429
|
request: appRequest,
|
|
1430
|
+
requestUrl: url.pathname,
|
|
1419
1431
|
trackedRequest,
|
|
1420
1432
|
routePath: matched.route.path,
|
|
1421
1433
|
routeScripts: options.clientScripts,
|
|
@@ -1437,6 +1449,7 @@ async function renderAppRequestInternal(
|
|
|
1437
1449
|
preparedActions.csrfToken,
|
|
1438
1450
|
preparedActions.csrfTokenIsNew === true,
|
|
1439
1451
|
);
|
|
1452
|
+
appendNavigationVary(response.headers);
|
|
1440
1453
|
emitRenderTiming(options, timing, response.status);
|
|
1441
1454
|
return response;
|
|
1442
1455
|
}
|
|
@@ -1452,7 +1465,9 @@ async function renderAppRequestInternal(
|
|
|
1452
1465
|
appDir: options.appDir,
|
|
1453
1466
|
assetBaseUrl: options.assetBaseUrl,
|
|
1454
1467
|
pageFile: matched.route.file,
|
|
1468
|
+
markerRequest: options.request,
|
|
1455
1469
|
props,
|
|
1470
|
+
requestUrl: url.pathname,
|
|
1456
1471
|
routePath: matched.route.path,
|
|
1457
1472
|
routeScripts: options.clientScripts,
|
|
1458
1473
|
serverModules: options.serverModules,
|
|
@@ -1475,6 +1490,7 @@ async function renderAppRequestInternal(
|
|
|
1475
1490
|
preparedActions.csrfToken,
|
|
1476
1491
|
preparedActions.csrfTokenIsNew === true,
|
|
1477
1492
|
);
|
|
1493
|
+
appendNavigationVary(response.headers);
|
|
1478
1494
|
emitRenderTiming(options, timing, response.status);
|
|
1479
1495
|
return response;
|
|
1480
1496
|
}
|
|
@@ -1552,16 +1568,11 @@ async function renderAppRequestInternal(
|
|
|
1552
1568
|
script: clientScript,
|
|
1553
1569
|
props: {
|
|
1554
1570
|
params: matched.params,
|
|
1555
|
-
request: { url:
|
|
1571
|
+
request: { url: url.pathname },
|
|
1556
1572
|
data,
|
|
1557
1573
|
},
|
|
1558
1574
|
})
|
|
1559
|
-
:
|
|
1560
|
-
? withRouteMarkers({
|
|
1561
|
-
html: pageHtml,
|
|
1562
|
-
routePath: matched.route.path,
|
|
1563
|
-
})
|
|
1564
|
-
: pageHtml;
|
|
1575
|
+
: withServerOnlyRouteMarkers(pageHtml, matched.route.path, options.request);
|
|
1565
1576
|
phaseStartedAt = renderTimingPhaseStartedAt(timing);
|
|
1566
1577
|
let html = await runWithQueryClient(queryClient, () =>
|
|
1567
1578
|
applyLayouts({
|
|
@@ -1615,7 +1626,7 @@ async function renderAppRequestInternal(
|
|
|
1615
1626
|
html,
|
|
1616
1627
|
originalAnalysis.authIncludesClaims ? currentAuthClaims() : undefined,
|
|
1617
1628
|
);
|
|
1618
|
-
html = injectQueryState(html, dehydrate(queryClient));
|
|
1629
|
+
html = injectQueryState(html, dehydrate(queryClient, options.dehydrateOptions));
|
|
1619
1630
|
|
|
1620
1631
|
const response = withOptionalActionCookie(
|
|
1621
1632
|
htmlResponse(
|
|
@@ -1636,9 +1647,11 @@ async function renderAppRequestInternal(
|
|
|
1636
1647
|
preparedActions.csrfToken,
|
|
1637
1648
|
preparedActions.csrfTokenIsNew === true,
|
|
1638
1649
|
);
|
|
1650
|
+
appendNavigationVary(response.headers);
|
|
1639
1651
|
|
|
1640
1652
|
const effectiveCachePolicy = cachePolicy ?? activeRouteCacheContext()?.cachePolicy;
|
|
1641
1653
|
const configuredHsts = configuredHstsHeader(metadata?.security);
|
|
1654
|
+
renderedStrictTransportSecurity = configuredHsts;
|
|
1642
1655
|
invalidConfiguredHsts = hasInvalidConfiguredHsts(metadata?.security);
|
|
1643
1656
|
|
|
1644
1657
|
const finalResponse = preparedActions.hasFormActions
|
|
@@ -1737,6 +1750,7 @@ async function renderAppRequestInternal(
|
|
|
1737
1750
|
const tracked = trackedRequest;
|
|
1738
1751
|
options.renderSignals.headerDependent = () =>
|
|
1739
1752
|
sourceUsesRequestInput || invalidConfiguredHsts || (tracked?.requestDependent() ?? true);
|
|
1753
|
+
options.renderSignals.strictTransportSecurity = () => renderedStrictTransportSecurity;
|
|
1740
1754
|
}
|
|
1741
1755
|
}
|
|
1742
1756
|
})).value;
|
|
@@ -1966,6 +1980,44 @@ function isNavigationRequest(request: Request): boolean {
|
|
|
1966
1980
|
return request.headers.get("x-mreact-navigation") === "1";
|
|
1967
1981
|
}
|
|
1968
1982
|
|
|
1983
|
+
function withServerOnlyRouteMarkers(html: string, routePath: string, request: Request): string {
|
|
1984
|
+
const marker = serverOnlyRouteMarkerParts(routePath, request);
|
|
1985
|
+
return marker === undefined ? html : `${marker.prefix}${html}${marker.suffix}`;
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
function serverOnlyRouteMarkerParts(
|
|
1989
|
+
routePath: string,
|
|
1990
|
+
request: Request,
|
|
1991
|
+
): { prefix: string; suffix: string } | undefined {
|
|
1992
|
+
if (request.headers.get("x-mreact-prerender-variant-capture") === "1") {
|
|
1993
|
+
return prerenderVariantMarkerParts(routePath);
|
|
1994
|
+
}
|
|
1995
|
+
return isNavigationRequest(request) ? routeMarkerParts(routePath) : undefined;
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
export function prerenderVariantMarkerParts(routePath: string): {
|
|
1999
|
+
prefix: string;
|
|
2000
|
+
suffix: string;
|
|
2001
|
+
} {
|
|
2002
|
+
const routeId = routeIdForPath(routePath);
|
|
2003
|
+
return {
|
|
2004
|
+
prefix: `<!--mreact-route-variant-start:${routeId}-->`,
|
|
2005
|
+
suffix: `<!--mreact-route-variant-end:${routeId}-->`,
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
function appendNavigationVary(headers: Headers): void {
|
|
2010
|
+
const vary = headers.get("vary");
|
|
2011
|
+
if (
|
|
2012
|
+
vary
|
|
2013
|
+
?.split(",")
|
|
2014
|
+
.some((value) => value.trim().toLowerCase() === "x-mreact-navigation") === true
|
|
2015
|
+
) {
|
|
2016
|
+
return;
|
|
2017
|
+
}
|
|
2018
|
+
headers.append("vary", "x-mreact-navigation");
|
|
2019
|
+
}
|
|
2020
|
+
|
|
1969
2021
|
function isNavigationRouteCacheReloadRequest(request: Request): boolean {
|
|
1970
2022
|
return (
|
|
1971
2023
|
isNavigationRequest(request) && request.headers.get("x-mreact-navigation-cache") === "reload"
|
|
@@ -3399,8 +3451,10 @@ function runServerStreamModule(
|
|
|
3399
3451
|
appDir: string;
|
|
3400
3452
|
assetBaseUrl?: string | undefined;
|
|
3401
3453
|
clientRouteInferenceCache: ClientRouteInferenceCache;
|
|
3454
|
+
markerRequest: Request;
|
|
3402
3455
|
pageFile: string;
|
|
3403
3456
|
props: ServerComponentProps;
|
|
3457
|
+
requestUrl: string;
|
|
3404
3458
|
routePath: string;
|
|
3405
3459
|
routeScripts?: ReadonlyMap<string, string> | undefined;
|
|
3406
3460
|
clientRoute: boolean;
|
|
@@ -3445,11 +3499,11 @@ function runServerStreamModule(
|
|
|
3445
3499
|
script: options.script,
|
|
3446
3500
|
props: {
|
|
3447
3501
|
params: options.props.params,
|
|
3448
|
-
request: { url: options.
|
|
3502
|
+
request: { url: options.requestUrl },
|
|
3449
3503
|
data: options.props.data,
|
|
3450
3504
|
},
|
|
3451
3505
|
})
|
|
3452
|
-
:
|
|
3506
|
+
: serverOnlyRouteMarkerParts(options.routePath, options.markerRequest);
|
|
3453
3507
|
|
|
3454
3508
|
sink.append("<!DOCTYPE html>");
|
|
3455
3509
|
sink.append(
|
|
@@ -3667,10 +3721,12 @@ async function runServerStreamModuleWithLoading(
|
|
|
3667
3721
|
data: Promise<unknown>;
|
|
3668
3722
|
define?: UserConfig["define"] | undefined;
|
|
3669
3723
|
loadingFile: string;
|
|
3724
|
+
markerRequest: Request;
|
|
3670
3725
|
pageFile: string;
|
|
3671
3726
|
params: RouteParams;
|
|
3672
3727
|
queryClient: QueryClient;
|
|
3673
3728
|
request: Request;
|
|
3729
|
+
requestUrl: string;
|
|
3674
3730
|
trackedRequest?: TrackedHeaderRequest | undefined;
|
|
3675
3731
|
routePath: string;
|
|
3676
3732
|
routeScripts?: ReadonlyMap<string, string> | undefined;
|
|
@@ -3717,10 +3773,10 @@ async function runServerStreamModuleWithLoading(
|
|
|
3717
3773
|
script: options.script,
|
|
3718
3774
|
props: {
|
|
3719
3775
|
params: options.params,
|
|
3720
|
-
request: { url: options.
|
|
3776
|
+
request: { url: options.requestUrl },
|
|
3721
3777
|
},
|
|
3722
3778
|
})
|
|
3723
|
-
:
|
|
3779
|
+
: serverOnlyRouteMarkerParts(options.routePath, options.markerRequest);
|
|
3724
3780
|
|
|
3725
3781
|
return renderToReadableStream((sink) => {
|
|
3726
3782
|
sink.append("<!DOCTYPE html>");
|
package/src/serve.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Server } from "node:http";
|
|
2
|
+
import type { DehydrateOptions } from "@reckona/mreact-query";
|
|
2
3
|
import { readFile } from "node:fs/promises";
|
|
3
4
|
import { join } from "node:path";
|
|
4
5
|
import type { BuiltPrerenderedRoute } from "./build.js";
|
|
@@ -61,6 +62,8 @@ import {
|
|
|
61
62
|
isCurrentPrerenderedRoute,
|
|
62
63
|
isVisitorDependentResponse,
|
|
63
64
|
PRERENDERED_ROUTE_SCHEMA_VERSION,
|
|
65
|
+
replayedPrerenderedRouteHeaders,
|
|
66
|
+
storedPrerenderedRouteHeaders,
|
|
64
67
|
} from "./prerender-entry.js";
|
|
65
68
|
|
|
66
69
|
interface BuiltRuntimeCacheEntry {
|
|
@@ -99,6 +102,7 @@ let warnedImplicitHostTrust = false;
|
|
|
99
102
|
* Configures rendering a request against a built app-router output directory.
|
|
100
103
|
*/
|
|
101
104
|
export interface RenderBuiltAppRequestOptions {
|
|
105
|
+
dehydrateOptions?: DehydrateOptions | undefined;
|
|
102
106
|
outDir: string;
|
|
103
107
|
importPolicy?: AppRouterImportPolicy | undefined;
|
|
104
108
|
instrumentation?: RouterInstrumentation | undefined;
|
|
@@ -156,6 +160,7 @@ export interface BuiltAppRuntimePreloadStrategy {
|
|
|
156
160
|
* Configures the Node HTTP server used to serve a built app-router output.
|
|
157
161
|
*/
|
|
158
162
|
export interface StartServerOptions {
|
|
163
|
+
dehydrateOptions?: DehydrateOptions | undefined;
|
|
159
164
|
outDir: string;
|
|
160
165
|
port: number;
|
|
161
166
|
hostname?: string;
|
|
@@ -480,7 +485,7 @@ async function renderBuiltAppRequestWithRuntime(
|
|
|
480
485
|
normalizedPath,
|
|
481
486
|
options.prerenderStore,
|
|
482
487
|
);
|
|
483
|
-
const prerenderedResponse = prerenderedRouteResponse(prerendered, request
|
|
488
|
+
const prerenderedResponse = prerenderedRouteResponse(prerendered, request);
|
|
484
489
|
|
|
485
490
|
if (prerenderedResponse !== undefined) {
|
|
486
491
|
return prerenderedResponse;
|
|
@@ -515,7 +520,7 @@ async function renderBuiltAppRequestWithRuntime(
|
|
|
515
520
|
normalizedPath,
|
|
516
521
|
options.prerenderStore,
|
|
517
522
|
);
|
|
518
|
-
const prerenderedResponse = prerenderedRouteResponse(prerendered, request
|
|
523
|
+
const prerenderedResponse = prerenderedRouteResponse(prerendered, request);
|
|
519
524
|
|
|
520
525
|
if (prerenderedResponse !== undefined) {
|
|
521
526
|
emitBuiltRenderTiming(options, request, timing, prerenderedResponse.status);
|
|
@@ -553,21 +558,27 @@ async function renderBuiltAppRequestWithRuntime(
|
|
|
553
558
|
|
|
554
559
|
function prerenderedRouteResponse(
|
|
555
560
|
prerendered: BuiltPrerenderedRoute | undefined,
|
|
556
|
-
|
|
561
|
+
request: Request,
|
|
557
562
|
): Response | undefined {
|
|
558
563
|
if (!isCurrentPrerenderedRoute(prerendered)) {
|
|
559
564
|
return undefined;
|
|
560
565
|
}
|
|
561
566
|
|
|
562
|
-
|
|
567
|
+
const navigation = request.headers.get("x-mreact-navigation") === "1";
|
|
568
|
+
const html = navigation ? prerendered.navigationHtml : prerendered.html;
|
|
569
|
+
if (html === undefined) {
|
|
570
|
+
return undefined;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (request.method === "HEAD") {
|
|
563
574
|
return new Response(null, {
|
|
564
|
-
headers: prerendered
|
|
575
|
+
headers: replayedPrerenderedRouteHeaders(prerendered, request),
|
|
565
576
|
status: prerendered.status,
|
|
566
577
|
});
|
|
567
578
|
}
|
|
568
579
|
|
|
569
|
-
return htmlResponse(
|
|
570
|
-
headers: prerendered
|
|
580
|
+
return htmlResponse(html, {
|
|
581
|
+
headers: replayedPrerenderedRouteHeaders(prerendered, request),
|
|
571
582
|
status: prerendered.status,
|
|
572
583
|
});
|
|
573
584
|
}
|
|
@@ -709,6 +720,7 @@ export async function startServer(
|
|
|
709
720
|
trustForwardedProto: options.trustForwardedProto,
|
|
710
721
|
render: (request) =>
|
|
711
722
|
runtime.render(request, {
|
|
723
|
+
dehydrateOptions: options.dehydrateOptions,
|
|
712
724
|
instrumentation: options.instrumentation,
|
|
713
725
|
logger: options.logger,
|
|
714
726
|
onResponse: options.onResponse,
|
|
@@ -927,6 +939,7 @@ function builtRenderAppRequestOptions(
|
|
|
927
939
|
clientScripts: options.runtime.clientScripts,
|
|
928
940
|
clientStylesByFile: options.runtime.clientStylesByFile,
|
|
929
941
|
clientStyles: options.runtime.clientStyles,
|
|
942
|
+
dehydrateOptions: options.dehydrateOptions,
|
|
930
943
|
importPolicy: {
|
|
931
944
|
...options.importPolicy,
|
|
932
945
|
allowedSourceDirs: options.runtime.allowedSourceDirs,
|
|
@@ -976,7 +989,11 @@ async function renderAndCachePrerenderWithLock(
|
|
|
976
989
|
options: RenderBuiltAppRequestOptions & { runtime: BuiltRuntime },
|
|
977
990
|
path: string,
|
|
978
991
|
): Promise<Response> {
|
|
979
|
-
const
|
|
992
|
+
const lockKey =
|
|
993
|
+
options.request.headers.get("x-mreact-navigation") === "1"
|
|
994
|
+
? `${path}\0navigation`
|
|
995
|
+
: path;
|
|
996
|
+
const existing = options.runtime.prerenderLocks.get(lockKey);
|
|
980
997
|
|
|
981
998
|
if (existing !== undefined) {
|
|
982
999
|
const result = await existing;
|
|
@@ -988,12 +1005,12 @@ async function renderAndCachePrerenderWithLock(
|
|
|
988
1005
|
}
|
|
989
1006
|
|
|
990
1007
|
const task = runPrerenderRegeneration(options, path);
|
|
991
|
-
options.runtime.prerenderLocks.set(
|
|
1008
|
+
options.runtime.prerenderLocks.set(lockKey, task);
|
|
992
1009
|
|
|
993
1010
|
try {
|
|
994
1011
|
return cloneResponse((await task).response);
|
|
995
1012
|
} finally {
|
|
996
|
-
options.runtime.prerenderLocks.delete(
|
|
1013
|
+
options.runtime.prerenderLocks.delete(lockKey);
|
|
997
1014
|
}
|
|
998
1015
|
}
|
|
999
1016
|
|
|
@@ -1001,15 +1018,17 @@ async function runPrerenderRegeneration(
|
|
|
1001
1018
|
options: RenderBuiltAppRequestOptions & { runtime: BuiltRuntime },
|
|
1002
1019
|
path: string,
|
|
1003
1020
|
): Promise<{ response: Response; shareable: boolean }> {
|
|
1021
|
+
const lockKey =
|
|
1022
|
+
options.request.headers.get("x-mreact-navigation") === "1"
|
|
1023
|
+
? `${path}\0navigation`
|
|
1024
|
+
: path;
|
|
1004
1025
|
const regenerate = async () => {
|
|
1005
1026
|
const stored = await readPrerenderedRoute(options.runtime, path, options.prerenderStore);
|
|
1027
|
+
const storedResponse = prerenderedRouteResponse(stored, options.request);
|
|
1006
1028
|
|
|
1007
|
-
if (
|
|
1029
|
+
if (storedResponse !== undefined) {
|
|
1008
1030
|
return {
|
|
1009
|
-
response:
|
|
1010
|
-
headers: stored.headers,
|
|
1011
|
-
status: stored.status,
|
|
1012
|
-
}),
|
|
1031
|
+
response: storedResponse,
|
|
1013
1032
|
shareable: true,
|
|
1014
1033
|
};
|
|
1015
1034
|
}
|
|
@@ -1018,7 +1037,10 @@ async function runPrerenderRegeneration(
|
|
|
1018
1037
|
// render that depended on this visitor's request headers must not become
|
|
1019
1038
|
// one; it is returned to the caller without being stored. The signal starts
|
|
1020
1039
|
// closed so that a render which never reports back is not stored either.
|
|
1021
|
-
const renderSignals = {
|
|
1040
|
+
const renderSignals = {
|
|
1041
|
+
headerDependent: () => true,
|
|
1042
|
+
strictTransportSecurity: () => undefined as string | undefined,
|
|
1043
|
+
};
|
|
1022
1044
|
const response = await renderBuiltDynamicResponse({ ...options, renderSignals });
|
|
1023
1045
|
|
|
1024
1046
|
if (!response.ok) {
|
|
@@ -1039,21 +1061,34 @@ async function runPrerenderRegeneration(
|
|
|
1039
1061
|
};
|
|
1040
1062
|
}
|
|
1041
1063
|
|
|
1064
|
+
const navigation = options.request.headers.get("x-mreact-navigation") === "1";
|
|
1065
|
+
if (navigation && stored === undefined) {
|
|
1066
|
+
return {
|
|
1067
|
+
response: htmlResponse(body, {
|
|
1068
|
+
headers: response.headers,
|
|
1069
|
+
status: response.status,
|
|
1070
|
+
}),
|
|
1071
|
+
shareable: false,
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1042
1075
|
return {
|
|
1043
1076
|
response: await cacheRegeneratedPrerenderedRoute(
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1077
|
+
options.runtime,
|
|
1078
|
+
path,
|
|
1079
|
+
body,
|
|
1080
|
+
response,
|
|
1081
|
+
renderSignals.strictTransportSecurity(),
|
|
1082
|
+
options.prerenderStore,
|
|
1083
|
+
navigation ? stored : undefined,
|
|
1084
|
+
),
|
|
1050
1085
|
shareable: true,
|
|
1051
1086
|
};
|
|
1052
1087
|
};
|
|
1053
1088
|
|
|
1054
1089
|
return options.prerenderStore?.withLock === undefined
|
|
1055
1090
|
? await regenerate()
|
|
1056
|
-
: await options.prerenderStore.withLock(
|
|
1091
|
+
: await options.prerenderStore.withLock(lockKey, regenerate);
|
|
1057
1092
|
}
|
|
1058
1093
|
|
|
1059
1094
|
async function applyBuiltPrerenderInvalidations(
|
|
@@ -1079,19 +1114,21 @@ async function cacheRegeneratedPrerenderedRoute(
|
|
|
1079
1114
|
path: string,
|
|
1080
1115
|
body: string,
|
|
1081
1116
|
response: Response,
|
|
1117
|
+
strictTransportSecurity: string | undefined,
|
|
1082
1118
|
store: AppRouterPrerenderStore | undefined,
|
|
1119
|
+
existing?: BuiltPrerenderedRoute | undefined,
|
|
1083
1120
|
): Promise<Response> {
|
|
1084
|
-
const headers
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1121
|
+
const headers = storedPrerenderedRouteHeaders(response.headers);
|
|
1122
|
+
const entry: BuiltPrerenderedRoute =
|
|
1123
|
+
existing === undefined
|
|
1124
|
+
? {
|
|
1125
|
+
headers,
|
|
1126
|
+
html: body,
|
|
1127
|
+
schemaVersion: PRERENDERED_ROUTE_SCHEMA_VERSION,
|
|
1128
|
+
status: response.status,
|
|
1129
|
+
...(strictTransportSecurity === undefined ? {} : { strictTransportSecurity }),
|
|
1130
|
+
}
|
|
1131
|
+
: { ...existing, navigationHtml: body };
|
|
1095
1132
|
runtime.prerenderedRoutes.set(path, entry);
|
|
1096
1133
|
await store?.set(path, entry);
|
|
1097
1134
|
|