@rangojs/router 0.25.0 → 0.26.1
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/dist/bin/rango.js +1 -1
- package/dist/types/cache/cf/cf-cache-store.d.ts +0 -2
- package/dist/types/cache/cf/cf-cache-types.d.ts +0 -16
- package/dist/types/cache/handle-snapshot.d.ts +2 -2
- package/dist/types/cache/shell-snapshot.d.ts +6 -4
- package/dist/types/cache/types.d.ts +8 -6
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +1 -15
- package/dist/types/handles/deferred-resolution.d.ts +3 -2
- package/dist/types/handles/meta.d.ts +5 -0
- package/dist/types/prerender/build-shell-capture.d.ts +1 -2
- package/dist/types/prerender/writable-store.d.ts +2 -3
- package/dist/types/router/dev-reload-handoff.d.ts +26 -0
- package/dist/types/router/router-interfaces.d.ts +0 -5
- package/dist/types/router/router-options.d.ts +0 -20
- package/dist/types/router/segment-resolution/mask-nested.d.ts +5 -3
- package/dist/types/router-versions.d.ts +8 -6
- package/dist/types/rsc/shell-capture-constants.d.ts +10 -6
- package/dist/types/rsc/shell-capture.d.ts +25 -8
- package/dist/types/rsc/types.d.ts +0 -8
- package/dist/types/server/build-version-table.d.ts +10 -13
- package/dist/types/server/context.d.ts +26 -2
- package/dist/types/server/handle-store.d.ts +17 -6
- package/dist/types/server/request-context.d.ts +4 -3
- package/dist/types/testing/build-versions.d.ts +0 -3
- package/dist/types/testing/internal/seed-vars.d.ts +4 -4
- package/dist/types/testing/serve-shell-request.d.ts +1 -1
- package/dist/types/types/loader-types.d.ts +6 -1
- package/dist/types/urls/pattern-types.d.ts +7 -6
- package/dist/vite/index.js +3 -8
- package/package.json +1 -1
- package/skills/cache-guide/SKILL.md +4 -8
- package/skills/caching/SKILL.md +3 -5
- package/skills/comparison/references/framework-comparison.md +1 -1
- package/skills/hooks/handle-and-actions.md +6 -6
- package/skills/host-router/SKILL.md +1 -1
- package/skills/loader/SKILL.md +10 -9
- package/skills/ppr/SKILL.md +83 -60
- package/skills/prerender/SKILL.md +3 -3
- package/skills/router-setup/SKILL.md +0 -2
- package/skills/testing/render-handler.md +17 -17
- package/skills/testing/serve-shell-request.md +1 -1
- package/skills/testing/server-actions.md +16 -16
- package/skills/vercel/SKILL.md +1 -1
- package/src/cache/cf/cf-cache-store.ts +4 -7
- package/src/cache/cf/cf-cache-types.ts +0 -17
- package/src/cache/handle-snapshot.ts +2 -2
- package/src/cache/shell-snapshot.ts +7 -3
- package/src/cache/types.ts +8 -6
- package/src/cache/vercel/vercel-cache-store.ts +2 -18
- package/src/handles/deferred-resolution.ts +3 -2
- package/src/handles/meta.ts +5 -0
- package/src/prerender/build-shell-capture.ts +1 -2
- package/src/prerender/writable-store.ts +2 -3
- package/src/route-definition/dsl-helpers.ts +14 -14
- package/src/router/dev-reload-handoff.ts +73 -0
- package/src/router/manifest.ts +9 -2
- package/src/router/match-middleware/intercept-resolution.ts +2 -2
- package/src/router/router-interfaces.ts +0 -6
- package/src/router/router-options.ts +0 -21
- package/src/router/segment-resolution/loader-cache.ts +4 -3
- package/src/router/segment-resolution/mask-nested.ts +5 -3
- package/src/router-versions.ts +7 -7
- package/src/router.ts +19 -8
- package/src/rsc/handler.ts +3 -7
- package/src/rsc/rsc-rendering.ts +44 -12
- package/src/rsc/shell-capture-constants.ts +10 -6
- package/src/rsc/shell-capture.ts +163 -88
- package/src/rsc/types.ts +0 -9
- package/src/server/build-version-table.ts +12 -24
- package/src/server/context.ts +64 -2
- package/src/server/handle-store.ts +77 -10
- package/src/server/request-context.ts +4 -3
- package/src/testing/build-versions.ts +0 -3
- package/src/testing/dispatch.ts +1 -3
- package/src/testing/internal/seed-vars.ts +4 -4
- package/src/testing/serve-shell-request.ts +2 -5
- package/src/types/loader-types.ts +6 -1
- package/src/urls/pattern-types.ts +7 -6
- package/src/vite/discovery/shell-prerender-phase.ts +1 -5
package/src/rsc/types.ts
CHANGED
|
@@ -369,15 +369,6 @@ export interface CreateRSCHandlerOptions<
|
|
|
369
369
|
*/
|
|
370
370
|
cache?: HandlerCacheConfig | ((env: TEnv) => HandlerCacheConfig);
|
|
371
371
|
|
|
372
|
-
/**
|
|
373
|
-
* Version override for this handler. Same meaning as
|
|
374
|
-
* `createRouter({ version })`, which it takes precedence over: this exact
|
|
375
|
-
* value is used for both of the router's versions.
|
|
376
|
-
*
|
|
377
|
-
* @default createRouter({ version }), else the router's build versions
|
|
378
|
-
*/
|
|
379
|
-
version?: string;
|
|
380
|
-
|
|
381
372
|
/**
|
|
382
373
|
* Nonce provider for Content Security Policy (CSP).
|
|
383
374
|
*
|
|
@@ -25,33 +25,24 @@ let table: RouterVersionsTable | undefined = ROUTER_VERSIONS;
|
|
|
25
25
|
/**
|
|
26
26
|
* The versions a router serves with, in order:
|
|
27
27
|
*
|
|
28
|
-
* 1.
|
|
29
|
-
*
|
|
30
|
-
* 2. The router's entry in the build table, else the whole-build entry.
|
|
31
|
-
* 3. `VERSION` for both: the dev stamp, bumped on every RSC module edit.
|
|
28
|
+
* 1. The router's entry in the build table, else the whole-build entry.
|
|
29
|
+
* 2. `VERSION` for both: the dev stamp, bumped on every RSC module edit.
|
|
32
30
|
*/
|
|
33
31
|
export function resolveRouterVersions(
|
|
34
32
|
routerId: string | undefined,
|
|
35
|
-
override?: string,
|
|
36
33
|
): RouterVersions {
|
|
37
|
-
return (
|
|
38
|
-
resolveVersionsFrom(table, routerId, override) ?? uniformVersions(VERSION)
|
|
39
|
-
);
|
|
34
|
+
return resolveVersionsFrom(table, routerId) ?? uniformVersions(VERSION);
|
|
40
35
|
}
|
|
41
36
|
|
|
42
37
|
/**
|
|
43
38
|
* The version an on-demand prerender overlay key carries: the router's data
|
|
44
39
|
* version, because the overlay stores segment data, as the segment cache does
|
|
45
40
|
* (a client-only deploy keeps both). The write side (`router.prerender`,
|
|
46
|
-
* router.ts) and the read side (rsc/handler.ts) both call this
|
|
47
|
-
*
|
|
48
|
-
* cannot disagree; a `createRSCHandler({ version })` does not move it.
|
|
41
|
+
* router.ts) and the read side (rsc/handler.ts) both call this, so they
|
|
42
|
+
* cannot disagree.
|
|
49
43
|
*/
|
|
50
|
-
export function resolvePrerenderVersion(
|
|
51
|
-
routerId
|
|
52
|
-
routerVersion: string | undefined,
|
|
53
|
-
): string {
|
|
54
|
-
return resolveRouterVersions(routerId, routerVersion).data;
|
|
44
|
+
export function resolvePrerenderVersion(routerId: string | undefined): string {
|
|
45
|
+
return resolveRouterVersions(routerId).data;
|
|
55
46
|
}
|
|
56
47
|
|
|
57
48
|
/** The table gives some router a pair of its own. */
|
|
@@ -104,16 +95,13 @@ export type KeyVersion = keyof RouterVersions | null;
|
|
|
104
95
|
|
|
105
96
|
/**
|
|
106
97
|
* The `v/{version}/` prefix of a persistent store key, or "" for an
|
|
107
|
-
* unversioned one.
|
|
108
|
-
*
|
|
109
|
-
*
|
|
98
|
+
* unversioned one. The version is the serving router's, read per operation
|
|
99
|
+
* because the cache factory builds the store before the request context
|
|
100
|
+
* exists.
|
|
110
101
|
*/
|
|
111
|
-
export function versionKeyPrefix(
|
|
112
|
-
storeVersion: string | undefined,
|
|
113
|
-
kind: KeyVersion,
|
|
114
|
-
): string {
|
|
102
|
+
export function versionKeyPrefix(kind: KeyVersion): string {
|
|
115
103
|
if (kind === null) return "";
|
|
116
|
-
const version =
|
|
104
|
+
const version = getCacheVersions()[kind];
|
|
117
105
|
return version ? `v/${version}/` : "";
|
|
118
106
|
}
|
|
119
107
|
|
package/src/server/context.ts
CHANGED
|
@@ -130,6 +130,11 @@ export type EntryPropRender = {
|
|
|
130
130
|
* on both DSL evaluations, whatever `ssr` did to `loading`.
|
|
131
131
|
*/
|
|
132
132
|
loadingPrefetch?: false;
|
|
133
|
+
/**
|
|
134
|
+
* loading(fallback, { ssr: false }), on both DSL evaluations; a document
|
|
135
|
+
* then sets `loading` to false (applyDocumentModeLoading).
|
|
136
|
+
*/
|
|
137
|
+
loadingSsr?: false;
|
|
133
138
|
transition?: TransitionConfig;
|
|
134
139
|
};
|
|
135
140
|
|
|
@@ -151,7 +156,8 @@ export type LoaderEntry = {
|
|
|
151
156
|
* Resolved at DSL-evaluation time from ctx.isSSR — entries are cached
|
|
152
157
|
* per-isSSR (router/manifest.ts cache key), so the flag is already
|
|
153
158
|
* request-mode-correct when resolveLoaders (fresh.ts) reads it and never
|
|
154
|
-
* appears on navigation-lane entries.
|
|
159
|
+
* appears on navigation-lane entries. Set by documentModeLoader, which
|
|
160
|
+
* getDocumentModeEntry also applies to a lazy include's captured ancestors.
|
|
155
161
|
*/
|
|
156
162
|
awaitBeforeFlush?: true;
|
|
157
163
|
/**
|
|
@@ -388,7 +394,8 @@ interface HelperContext extends IncludeScopePosition {
|
|
|
388
394
|
* different routers don't clobber each other (route-map-builder.ts). */
|
|
389
395
|
routerId?: string;
|
|
390
396
|
metrics?: MetricsStore;
|
|
391
|
-
/** True when rendering for SSR (document requests)
|
|
397
|
+
/** True when rendering for SSR (document requests). DSL decisions on it
|
|
398
|
+
* go through the document-mode helpers (documentModeLoader). */
|
|
392
399
|
isSSR?: boolean;
|
|
393
400
|
/** URL patterns map for path() routes (route name -> pattern) */
|
|
394
401
|
patterns?: Map<string, string>;
|
|
@@ -893,6 +900,61 @@ export function getIsolatedLazyParent(
|
|
|
893
900
|
};
|
|
894
901
|
}
|
|
895
902
|
|
|
903
|
+
/*
|
|
904
|
+
* The DSL's document-only decisions. A document (isSSR) evaluation applies
|
|
905
|
+
* them in dsl-helpers.ts; getDocumentModeEntry applies the same ones to a
|
|
906
|
+
* chain built without isSSR. A new ctx.isSSR decision gets a helper here,
|
|
907
|
+
* called from both.
|
|
908
|
+
*/
|
|
909
|
+
|
|
910
|
+
/** loader(Def, { ssr: false }): a document awaits it before the first flush. */
|
|
911
|
+
export function documentModeLoader(entry: LoaderEntry): LoaderEntry {
|
|
912
|
+
return entry.bake ? { ...entry, awaitBeforeFlush: true } : entry;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/** loading(fallback, { ssr: false }): a document renders no fallback. */
|
|
916
|
+
export function applyDocumentModeLoading(entry: EntryPropRender): void {
|
|
917
|
+
if (entry.loadingSsr === false) entry.loading = false;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
const documentModeEntries = new WeakMap<EntryData, EntryData>();
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* `entry`, its ancestors, orphan layouts and parallels as a document (isSSR)
|
|
924
|
+
* DSL evaluation builds them, from an evaluation without isSSR.
|
|
925
|
+
*
|
|
926
|
+
* include() captures its parent chain on router.ts routes() or
|
|
927
|
+
* lazy-includes.ts runExpansion, neither of which has isSSR, and loadManifest
|
|
928
|
+
* reuses it for every request. Handed to a document as is, every ancestor
|
|
929
|
+
* outside the include kept its navigation-mode decisions (#1179). Intercepts
|
|
930
|
+
* stay as they are: a document never resolves one.
|
|
931
|
+
*
|
|
932
|
+
* Memoized per entry, so an ancestor several includes share maps to one copy.
|
|
933
|
+
*/
|
|
934
|
+
export function getDocumentModeEntry(entry: EntryData): EntryData {
|
|
935
|
+
const known = documentModeEntries.get(entry);
|
|
936
|
+
if (known) return known;
|
|
937
|
+
const copy = { ...entry };
|
|
938
|
+
documentModeEntries.set(entry, copy);
|
|
939
|
+
documentModeEntries.set(copy, copy);
|
|
940
|
+
copy.loader = entry.loader.map(documentModeLoader);
|
|
941
|
+
applyDocumentModeLoading(copy);
|
|
942
|
+
copy.layout = entry.layout.map(getDocumentModeEntry);
|
|
943
|
+
const parallel: ParallelEntries = {};
|
|
944
|
+
for (const slot of Object.keys(entry.parallel) as `@${string}`[]) {
|
|
945
|
+
const slotEntry = entry.parallel[slot];
|
|
946
|
+
if (slotEntry) {
|
|
947
|
+
parallel[slot] = getDocumentModeEntry(slotEntry) as ParallelEntryData;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
copy.parallel = parallel;
|
|
951
|
+
if (entry.parent) copy.parent = getDocumentModeEntry(entry.parent);
|
|
952
|
+
if (entry.orphanOwner) {
|
|
953
|
+
copy.orphanOwner = getDocumentModeEntry(entry.orphanOwner);
|
|
954
|
+
}
|
|
955
|
+
return copy;
|
|
956
|
+
}
|
|
957
|
+
|
|
896
958
|
export function getParallelEntries(
|
|
897
959
|
parallels: ParallelEntries | EntryData[] | undefined,
|
|
898
960
|
): ParallelEntryData[] {
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
isInsideLoaderScope,
|
|
5
5
|
} from "./context.js";
|
|
6
6
|
import { holdsThenable } from "../router/segment-resolution/mask-nested.js";
|
|
7
|
+
import { isThenable } from "../handles/is-thenable.js";
|
|
7
8
|
import { pushedByKey } from "../handles/pushed-by.js";
|
|
8
9
|
import { defineSlot } from "./slot-accessors.js";
|
|
9
10
|
|
|
@@ -254,12 +255,14 @@ export interface HandleStore {
|
|
|
254
255
|
): void;
|
|
255
256
|
|
|
256
257
|
/**
|
|
257
|
-
* push() a record's copy of `loaderId`'s
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
258
|
+
* push() a record's copy of `loaderId`'s push that stands (restoreHandles):
|
|
259
|
+
* the record supplies the loader's value too. From then on this request
|
|
260
|
+
* drops every thenable-free push the loader makes (live, or replayed from
|
|
261
|
+
* a cache entry, anywhere inside its body), and keeps the ones holding a
|
|
262
|
+
* thenable (holdsThenable: what the record could not keep), placed among
|
|
263
|
+
* the restored copies in the loader's push order. A top-level promise is
|
|
264
|
+
* judged by its settled value, as the capture judged it (#1182): dropped
|
|
265
|
+
* when it settles thenable-free or rejects, else kept once it settles.
|
|
263
266
|
*/
|
|
264
267
|
pushRestored(
|
|
265
268
|
handleName: string,
|
|
@@ -278,6 +281,16 @@ export interface HandleStore {
|
|
|
278
281
|
*/
|
|
279
282
|
setRecordAuthority(authority: (loaderId: string) => RecordAuthority): void;
|
|
280
283
|
|
|
284
|
+
/**
|
|
285
|
+
* Capture only (shell-capture.ts deriveShellCaptureContext): the slot
|
|
286
|
+
* holding `value` becomes a DSL-loader push without an owner, so the
|
|
287
|
+
* record and the prelude leave it out (getDataForSegment's
|
|
288
|
+
* `excludeLoaderPushes`). The capture owns a bake-lane loader's promise
|
|
289
|
+
* when it is pushed, and calls this once the promise settled to a value
|
|
290
|
+
* that holds one (#1182).
|
|
291
|
+
*/
|
|
292
|
+
unrecordPush(handleName: string, segmentId: string, value: unknown): void;
|
|
293
|
+
|
|
281
294
|
/**
|
|
282
295
|
* `loaderId`'s run in this request settled (resolved or rejected), its
|
|
283
296
|
* pushes made (loader-resolution.ts createLoaderExecutor). Its placeholders
|
|
@@ -666,6 +679,39 @@ export function createHandleStore(): HandleStore {
|
|
|
666
679
|
return after;
|
|
667
680
|
}
|
|
668
681
|
|
|
682
|
+
// A restored loader's top-level promise push, judged by its settled value
|
|
683
|
+
// as push(await value) would be: thenable-free, the record's copy stands
|
|
684
|
+
// (the capture recorded that value, #1182); holding a promise, it is kept
|
|
685
|
+
// where the loader's pushes go, once it settles; rejected, it is dropped.
|
|
686
|
+
// Tracked on the auxiliary lane, so the late channel stays open for it.
|
|
687
|
+
function judgeRestoredPromise(
|
|
688
|
+
store: HandleStore,
|
|
689
|
+
handleName: string,
|
|
690
|
+
segmentId: string,
|
|
691
|
+
value: unknown,
|
|
692
|
+
loaderId: string,
|
|
693
|
+
tag: SlotTag,
|
|
694
|
+
): void {
|
|
695
|
+
const judged = Promise.resolve(value).then(
|
|
696
|
+
(settled) => {
|
|
697
|
+
if (!holdsThenable(settled)) {
|
|
698
|
+
const existing = data[handleName]?.[segmentId];
|
|
699
|
+
if (existing) matchRestoredSlot(existing, loaderId);
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
const values = ((data[handleName] ??= {})[segmentId] ??= []);
|
|
703
|
+
insertSlot(values, restoredInsertIndex(values, loaderId), value, tag);
|
|
704
|
+
if (tag.replayOf !== undefined) {
|
|
705
|
+
addPending(pendingReplays, tag.replayOf, values);
|
|
706
|
+
}
|
|
707
|
+
version++;
|
|
708
|
+
signalEmission();
|
|
709
|
+
},
|
|
710
|
+
() => {},
|
|
711
|
+
);
|
|
712
|
+
store.trackAuxiliary(judged);
|
|
713
|
+
}
|
|
714
|
+
|
|
669
715
|
function replayPush(
|
|
670
716
|
store: HandleStore,
|
|
671
717
|
replay: NonNullable<typeof replaying>,
|
|
@@ -860,10 +906,22 @@ export function createHandleStore(): HandleStore {
|
|
|
860
906
|
restoredBy = restoringLoader(replayOf);
|
|
861
907
|
}
|
|
862
908
|
}
|
|
863
|
-
if (restoredBy !== undefined
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
909
|
+
if (restoredBy !== undefined) {
|
|
910
|
+
if (isThenable(value)) {
|
|
911
|
+
judgeRestoredPromise(this, handleName, segmentId, value, restoredBy, {
|
|
912
|
+
loader: (loaderPush ?? isInsideLoaderScope()) || undefined,
|
|
913
|
+
replayOf,
|
|
914
|
+
liveOf: replayOf === undefined ? restoredBy : undefined,
|
|
915
|
+
owner,
|
|
916
|
+
by: getHandlePushOwner() ?? replayOf ?? owner,
|
|
917
|
+
});
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
if (!holdsThenable(value)) {
|
|
921
|
+
const existing = data[handleName]?.[segmentId];
|
|
922
|
+
if (existing) matchRestoredSlot(existing, restoredBy);
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
867
925
|
}
|
|
868
926
|
|
|
869
927
|
if (!data[handleName]) {
|
|
@@ -977,6 +1035,15 @@ export function createHandleStore(): HandleStore {
|
|
|
977
1035
|
authority = of;
|
|
978
1036
|
},
|
|
979
1037
|
|
|
1038
|
+
unrecordPush(handleName: string, segmentId: string, value: unknown): void {
|
|
1039
|
+
const values = data[handleName]?.[segmentId];
|
|
1040
|
+
if (!values) return;
|
|
1041
|
+
const i = values.indexOf(value);
|
|
1042
|
+
if (i < 0) return;
|
|
1043
|
+
const tags = tagsFor(values);
|
|
1044
|
+
tags[i] = { ...tags[i], loader: true, owner: undefined };
|
|
1045
|
+
},
|
|
1046
|
+
|
|
980
1047
|
settleLoaderRun(loaderId: string): void {
|
|
981
1048
|
if (completed) return;
|
|
982
1049
|
const hole = isHole(loaderId);
|
|
@@ -368,9 +368,10 @@ export interface RequestContext<
|
|
|
368
368
|
|
|
369
369
|
/**
|
|
370
370
|
* @internal Set on a capture's derived context when a loader pushed a
|
|
371
|
-
* value the record cannot keep (
|
|
372
|
-
*
|
|
373
|
-
* `runs: 1`, so a HIT still
|
|
371
|
+
* value the record cannot keep (masked nested promises, a pushed promise
|
|
372
|
+
* that settled to one, a live-lane loader's push, or a push outside a
|
|
373
|
+
* loader body): the loader records then carry `runs: 1`, so a HIT still
|
|
374
|
+
* runs their bodies for those pushes.
|
|
374
375
|
*/
|
|
375
376
|
_shellCaptureUnrecordedLoaderPush?: true;
|
|
376
377
|
|
|
@@ -35,9 +35,6 @@ export interface BuildVersions extends RouterVersions {
|
|
|
35
35
|
* Install the cache versions of a simulated build, or remove them with no
|
|
36
36
|
* argument (routers serve unversioned again, the default in tests).
|
|
37
37
|
*
|
|
38
|
-
* A router that sets `createRouter({ version })` keeps that value for both
|
|
39
|
-
* versions, exactly as in production.
|
|
40
|
-
*
|
|
41
38
|
* @example A deploy that changes the client only keeps cached data
|
|
42
39
|
* ```ts
|
|
43
40
|
* setBuildVersions({ data: "d1", document: "h1" });
|
package/src/testing/dispatch.ts
CHANGED
|
@@ -167,8 +167,6 @@ import { applyStreamIdleTimeout } from "../rsc/stream-idle.js";
|
|
|
167
167
|
interface DispatchableRouter<TEnv> {
|
|
168
168
|
id?: string;
|
|
169
169
|
routerId?: string;
|
|
170
|
-
/** createRouter({ version }), the consumer-set version. */
|
|
171
|
-
version?: string;
|
|
172
170
|
routeMap: Record<string, unknown>;
|
|
173
171
|
/** The routes() patterns the dev trie is rebuilt from (rsc/manifest-init.ts). */
|
|
174
172
|
urlpatterns?: unknown;
|
|
@@ -448,7 +446,7 @@ export async function dispatch<TEnv = any>(
|
|
|
448
446
|
cacheProfiles: router.cacheProfiles,
|
|
449
447
|
// The store keys with the router's versions, resolved as the production
|
|
450
448
|
// handler resolves them (rsc/handler.ts).
|
|
451
|
-
versions: resolveRouterVersions(routerId
|
|
449
|
+
versions: resolveRouterVersions(routerId),
|
|
452
450
|
});
|
|
453
451
|
// Wire background error reporting so cache degradation (reportCacheError ->
|
|
454
452
|
// _reportBackgroundError) reaches the router's onError, mirroring the production
|
|
@@ -23,10 +23,10 @@ export interface StateCookieSeed {
|
|
|
23
23
|
*/
|
|
24
24
|
routerId?: string;
|
|
25
25
|
/**
|
|
26
|
-
* The app's version
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
26
|
+
* The app's version: the rotated value's prefix (`{version}:{timestamp}`)
|
|
27
|
+
* and the request's cache versions, which a store keys with. Defaults to
|
|
28
|
+
* `"0"` for the prefix (resolved inside createRequestContext) and to the
|
|
29
|
+
* build versions for the store.
|
|
30
30
|
*/
|
|
31
31
|
version?: string;
|
|
32
32
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* serveShellRequest — one request through the production PPR serve path.
|
|
3
3
|
*
|
|
4
4
|
* Builds the router's request handler as `router.fetch` does
|
|
5
|
-
* (`createRSCHandler` with the router's `nonce
|
|
5
|
+
* (`createRSCHandler` with the router's `nonce` and `cache`
|
|
6
6
|
* config) and serves one GET through it, so middleware, the shell serve path
|
|
7
7
|
* and a MISS's background capture all run production code, on real Flight
|
|
8
8
|
* (the vendored react-server-dom serializer). The call settles every
|
|
@@ -537,10 +537,7 @@ async function getHandler(
|
|
|
537
537
|
const { resolveRouterVersions } =
|
|
538
538
|
await import("../server/build-version-table.js");
|
|
539
539
|
// What createRSCHandler would resolve for this router now.
|
|
540
|
-
const { document: versions } = resolveRouterVersions(
|
|
541
|
-
internal.id,
|
|
542
|
-
internal.version,
|
|
543
|
-
);
|
|
540
|
+
const { document: versions } = resolveRouterVersions(internal.id);
|
|
544
541
|
const cached = byStore.get(cacheStore ?? router);
|
|
545
542
|
if (cached?.versions === versions) return cached.handler;
|
|
546
543
|
|
|
@@ -189,7 +189,12 @@ export type LoaderFn<
|
|
|
189
189
|
* deterministically precedes Response construction, where the streamed
|
|
190
190
|
* default only wins that race opportunistically). Under ppr it is also the
|
|
191
191
|
* BAKE lane: the loader executes at shell capture and its settled return is
|
|
192
|
-
* shell material (nested promises stay live holes)
|
|
192
|
+
* shell material (nested promises stay live holes), and so are its handle
|
|
193
|
+
* pushes, a pushed promise or `.defer()` included: the capture awaits it and
|
|
194
|
+
* every HIT serves the value it settled to (#1182). A pushed promise that
|
|
195
|
+
* rejects, or misses `ppr.captureTimeout`, keeps the route on MISS. A value
|
|
196
|
+
* that must stay per request belongs to a loader without `ssr: false` — see
|
|
197
|
+
* the ppr docs.
|
|
193
198
|
*
|
|
194
199
|
* Scoped per LOADER, not per segment: an `ssr: false` loader alongside a
|
|
195
200
|
* deliberately streaming sibling awaits only itself, and the sibling keeps
|
|
@@ -66,12 +66,13 @@ export interface PartialPrerenderProps {
|
|
|
66
66
|
/**
|
|
67
67
|
* Upper bound (serialized UTF-8 bytes) on the loader pins in the capture
|
|
68
68
|
* data snapshot: the bake-lane loader records. The doc record (the handler
|
|
69
|
-
* layer every HIT replays) is exempt.
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* Defaults to 8 MiB. The whole
|
|
74
|
-
*
|
|
69
|
+
* layer every HIT replays) is exempt. A document HIT is served only from an
|
|
70
|
+
* entry that holds a pin for every `ssr: false` loader its HTML baked, so
|
|
71
|
+
* over the cap no document shell is stored: documents render in full
|
|
72
|
+
* (MISS), and client navigations still replay the handler layer and run
|
|
73
|
+
* those loaders fresh. Reported once per key. Defaults to 8 MiB. The whole
|
|
74
|
+
* entry is bounded separately by the store's value limit (Cloudflare KV:
|
|
75
|
+
* 25 MiB); an entry over it is refused.
|
|
75
76
|
*/
|
|
76
77
|
maxSnapshotBytes?: number;
|
|
77
78
|
/**
|
|
@@ -231,11 +231,7 @@ export async function runShellPrerenderPhase(
|
|
|
231
231
|
for (const [routerId, routerInstance] of registry) {
|
|
232
232
|
if (typeof routerInstance.match !== "function") continue;
|
|
233
233
|
// The build's table always holds the whole-build pair.
|
|
234
|
-
const versions = resolveVersionsFrom(
|
|
235
|
-
versionsTable,
|
|
236
|
-
routerId,
|
|
237
|
-
routerInstance.version,
|
|
238
|
-
)!;
|
|
234
|
+
const versions = resolveVersionsFrom(versionsTable, routerId)!;
|
|
239
235
|
try {
|
|
240
236
|
const res = await captureMod.captureShellForBuild({
|
|
241
237
|
router: routerInstance,
|