@rangojs/router 0.26.6 → 0.28.0
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/__internal.d.ts +7 -1
- package/dist/types/browser/action-coordinator.d.ts +0 -4
- package/dist/types/browser/action-fence.d.ts +11 -3
- package/dist/types/browser/event-controller.d.ts +20 -1
- package/dist/types/browser/history-state.d.ts +7 -9
- package/dist/types/browser/intercept-utils.d.ts +23 -0
- package/dist/types/browser/navigation-store.d.ts +8 -6
- package/dist/types/browser/react/filter-segment-order.d.ts +2 -2
- package/dist/types/browser/rsc-router.d.ts +1 -1
- package/dist/types/browser/segment-reconciler.d.ts +6 -4
- package/dist/types/browser/types.d.ts +2 -1
- package/dist/types/browser/wire-protocol.d.ts +25 -0
- package/dist/types/cache/cache-key-utils.d.ts +11 -5
- package/dist/types/cache/cache-scope.d.ts +34 -22
- package/dist/types/cache/cf/cf-cache-constants.d.ts +2 -2
- package/dist/types/cache/cf/cf-cache-store.d.ts +47 -65
- package/dist/types/cache/cf/cf-kv-utils.d.ts +1 -1
- package/dist/types/cache/handle-snapshot.d.ts +9 -4
- package/dist/types/cache/segment-codec.d.ts +14 -0
- package/dist/types/cache/shell-snapshot.d.ts +1 -1
- package/dist/types/cache/tag-invalidation.d.ts +10 -0
- package/dist/types/cache/taint.d.ts +5 -3
- package/dist/types/cache/types.d.ts +7 -3
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +8 -6
- package/dist/types/prefetch-markers.d.ts +4 -6
- package/dist/types/prerender/store.d.ts +31 -0
- package/dist/types/prerender/warm-request.d.ts +3 -3
- package/dist/types/prerender.d.ts +2 -2
- package/dist/types/route-name.d.ts +5 -0
- package/dist/types/router/error-handling.d.ts +4 -4
- package/dist/types/router/instrument.d.ts +4 -3
- package/dist/types/router/intercept-resolution.d.ts +25 -9
- package/dist/types/router/match-context.d.ts +12 -11
- package/dist/types/router/match-handlers.d.ts +2 -6
- package/dist/types/router/match-middleware/background-revalidation.d.ts +10 -20
- package/dist/types/router/match-middleware/cache-lookup.d.ts +35 -87
- package/dist/types/router/match-middleware/cache-store.d.ts +2 -2
- package/dist/types/router/match-middleware/segment-resolution.d.ts +8 -6
- package/dist/types/router/prerender-match.d.ts +21 -6
- package/dist/types/router/preview-match.d.ts +2 -7
- package/dist/types/router/request-classification.d.ts +5 -0
- package/dist/types/router/revalidation.d.ts +6 -1
- package/dist/types/router/router-context.d.ts +9 -63
- package/dist/types/router/router-interfaces.d.ts +8 -14
- package/dist/types/router/segment-resolution/loader-cache.d.ts +3 -3
- package/dist/types/router/segment-resolution/loader-mask.d.ts +4 -4
- package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +20 -18
- package/dist/types/router/segment-resolution/resolution-pass.d.ts +93 -0
- package/dist/types/router/segment-resolution/revalidation.d.ts +82 -31
- package/dist/types/router/segment-resolution.d.ts +1 -2
- package/dist/types/router/segment-wrappers.d.ts +11 -48
- package/dist/types/router/telemetry.d.ts +2 -0
- package/dist/types/router/types.d.ts +10 -0
- package/dist/types/rsc/full-payload.d.ts +2 -2
- package/dist/types/rsc/handler-context.d.ts +6 -6
- package/dist/types/rsc/json-route-result.d.ts +5 -9
- package/dist/types/rsc/request-stages.d.ts +203 -0
- package/dist/types/rsc/response-cache-serve.d.ts +7 -12
- package/dist/types/rsc/response-route-handler.d.ts +2 -2
- package/dist/types/rsc/routine-plan.d.ts +1 -1
- package/dist/types/rsc/server-action.d.ts +2 -2
- package/dist/types/rsc/shell-capture-constants.d.ts +2 -2
- package/dist/types/rsc/shell-capture.d.ts +132 -15
- package/dist/types/rsc/shell-serve.d.ts +37 -12
- package/dist/types/server/context.d.ts +9 -8
- package/dist/types/server/handle-store.d.ts +16 -0
- package/dist/types/server/render-pass.d.ts +72 -0
- package/dist/types/server/request-context.d.ts +24 -104
- package/dist/types/server/rewrite-trail.d.ts +2 -2
- package/dist/types/testing/dispatch.d.ts +35 -18
- package/dist/types/testing/serve-shell-request.d.ts +7 -0
- package/dist/types/types/cache-types.d.ts +6 -3
- package/dist/types/types/error-types.d.ts +9 -1
- package/dist/types/types/handler-context.d.ts +18 -7
- package/dist/types/types/segments.d.ts +1 -1
- package/dist/types/vite/discovery/build-discovery-phase.d.ts +40 -0
- package/dist/types/vite/discovery/build-env.d.ts +22 -0
- package/dist/types/vite/discovery/bundle-scan-phase.d.ts +12 -0
- package/dist/types/vite/discovery/dev-prerender-cache.d.ts +3 -2
- package/dist/types/vite/discovery/dev-server-phase.d.ts +26 -0
- package/dist/types/vite/discovery/discovery-errors.d.ts +1 -1
- package/dist/types/vite/discovery/state.d.ts +12 -0
- package/dist/types/vite/plugins/version-plugin.d.ts +1 -1
- package/dist/types/vite/plugins/virtual-entries.d.ts +1 -1
- package/dist/types/vite/router-discovery.d.ts +8 -47
- package/dist/types/vite/temp-rsc-server.d.ts +39 -0
- package/dist/types/vite/utils/prerender-utils.d.ts +9 -0
- package/dist/types/wire-params.d.ts +34 -0
- package/dist/vite/index.js +3809 -3710
- package/package.json +1 -1
- package/skills/cache-guide/SKILL.md +8 -5
- package/skills/caching/SKILL.md +37 -18
- package/skills/client-urls/SKILL.md +2 -1
- package/skills/document-cache/SKILL.md +18 -1
- package/skills/handler-use/SKILL.md +1 -1
- package/skills/intercept/SKILL.md +53 -13
- package/skills/layout/SKILL.md +11 -7
- package/skills/loader/SKILL.md +30 -13
- package/skills/middleware/SKILL.md +19 -1
- package/skills/migrate-nextjs/SKILL.md +5 -4
- package/skills/parallel/SKILL.md +5 -4
- package/skills/ppr/SKILL.md +19 -3
- package/skills/prerender/SKILL.md +39 -15
- package/skills/rango/SKILL.md +23 -11
- package/skills/server-actions/SKILL.md +51 -6
- package/skills/testing/predicates.md +1 -1
- package/skills/testing/response-routes.md +6 -4
- package/skills/testing/serve-shell-request.md +7 -7
- package/skills/use-cache/SKILL.md +46 -8
- package/skills/ux-architecture/SKILL.md +3 -2
- package/src/__internal.ts +7 -1
- package/src/browser/action-coordinator.ts +2 -13
- package/src/browser/action-fence.ts +11 -3
- package/src/browser/connection-warmup.ts +3 -1
- package/src/browser/event-controller.ts +42 -3
- package/src/browser/history-state.ts +15 -13
- package/src/browser/intercept-utils.ts +51 -0
- package/src/browser/navigation-bridge.ts +34 -30
- package/src/browser/navigation-client.ts +21 -33
- package/src/browser/navigation-store.ts +25 -27
- package/src/browser/navigation-transaction.ts +7 -11
- package/src/browser/partial-update.ts +8 -0
- package/src/browser/prefetch/cache.ts +2 -1
- package/src/browser/prefetch/fetch.ts +8 -16
- package/src/browser/prefetch/queue.ts +4 -4
- package/src/browser/react/NavigationProvider.tsx +2 -1
- package/src/browser/react/filter-segment-order.ts +2 -2
- package/src/browser/rsc-router.tsx +17 -2
- package/src/browser/segment-reconciler.ts +66 -53
- package/src/browser/server-action-bridge.ts +265 -127
- package/src/browser/types.ts +2 -1
- package/src/browser/wire-protocol.ts +41 -0
- package/src/cache/cache-key-utils.ts +11 -5
- package/src/cache/cache-runtime.ts +110 -50
- package/src/cache/cache-scope.ts +110 -64
- package/src/cache/cf/cf-cache-constants.ts +2 -2
- package/src/cache/cf/cf-cache-store.ts +486 -874
- package/src/cache/cf/cf-kv-utils.ts +1 -1
- package/src/cache/document-cache.ts +42 -15
- package/src/cache/handle-snapshot.ts +19 -7
- package/src/cache/segment-codec.ts +55 -0
- package/src/cache/shell-snapshot.ts +2 -2
- package/src/cache/tag-invalidation.ts +21 -2
- package/src/cache/taint.ts +5 -3
- package/src/cache/types.ts +7 -3
- package/src/cache/vercel/vercel-cache-store.ts +243 -310
- package/src/handles/pushed-by.ts +1 -1
- package/src/prefetch-markers.ts +4 -7
- package/src/prerender/store.ts +76 -0
- package/src/prerender/warm-request.ts +8 -4
- package/src/prerender.ts +2 -2
- package/src/route-name.ts +8 -0
- package/src/router/error-handling.ts +4 -4
- package/src/router/instrument.ts +4 -3
- package/src/router/intercept-resolution.ts +77 -75
- package/src/router/loader-resolution.ts +5 -4
- package/src/router/match-api.ts +4 -5
- package/src/router/match-context.ts +33 -12
- package/src/router/match-handlers.ts +8 -20
- package/src/router/match-middleware/background-revalidation.ts +41 -53
- package/src/router/match-middleware/cache-lookup.ts +680 -720
- package/src/router/match-middleware/cache-store.ts +14 -12
- package/src/router/match-middleware/intercept-resolution.ts +3 -23
- package/src/router/match-middleware/segment-resolution.ts +34 -61
- package/src/router/match-result.ts +1 -20
- package/src/router/middleware.ts +69 -11
- package/src/router/navigation-snapshot.ts +4 -3
- package/src/router/prerender-match.ts +100 -44
- package/src/router/preview-match.ts +6 -41
- package/src/router/request-classification.ts +13 -7
- package/src/router/revalidation.ts +33 -7
- package/src/router/route-snapshot.ts +2 -5
- package/src/router/router-context.ts +14 -117
- package/src/router/router-interfaces.ts +8 -14
- package/src/router/segment-resolution/loader-cache.ts +14 -9
- package/src/router/segment-resolution/loader-mask.ts +5 -5
- package/src/router/segment-resolution/prefetch-deferral.ts +30 -27
- package/src/router/segment-resolution/resolution-pass.ts +137 -0
- package/src/router/segment-resolution/revalidation.ts +897 -776
- package/src/router/segment-resolution.ts +4 -13
- package/src/router/segment-wrappers.ts +51 -269
- package/src/router/telemetry.ts +5 -0
- package/src/router/types.ts +10 -0
- package/src/router.ts +12 -13
- package/src/rsc/full-payload.ts +6 -5
- package/src/rsc/handler-context.ts +8 -14
- package/src/rsc/handler.ts +126 -507
- package/src/rsc/json-route-result.ts +5 -9
- package/src/rsc/loader-fetch.ts +6 -5
- package/src/rsc/progressive-enhancement.ts +5 -0
- package/src/rsc/request-stages.ts +757 -0
- package/src/rsc/response-cache-serve.ts +10 -14
- package/src/rsc/response-route-handler.ts +13 -5
- package/src/rsc/rewrite-passes.ts +2 -1
- package/src/rsc/routine-plan.ts +1 -1
- package/src/rsc/rsc-rendering.ts +172 -349
- package/src/rsc/server-action.ts +87 -113
- package/src/rsc/shell-capture-constants.ts +3 -1
- package/src/rsc/shell-capture.ts +212 -169
- package/src/rsc/shell-serve.ts +121 -18
- package/src/rsc/ssr-setup.ts +5 -4
- package/src/segment-system.tsx +3 -3
- package/src/server/context.ts +22 -25
- package/src/server/handle-store.ts +55 -0
- package/src/server/render-pass.ts +291 -0
- package/src/server/request-context.ts +26 -115
- package/src/server/rewrite-trail.ts +6 -4
- package/src/ssr/index.tsx +1 -1
- package/src/ssr/ssr-root.tsx +1 -0
- package/src/testing/dispatch.ts +223 -710
- package/src/testing/e2e/page-helpers.ts +2 -2
- package/src/testing/serve-shell-request.ts +37 -32
- package/src/types/cache-types.ts +6 -3
- package/src/types/error-types.ts +9 -1
- package/src/types/handler-context.ts +21 -7
- package/src/types/segments.ts +1 -1
- package/src/vite/discovery/build-discovery-phase.ts +204 -0
- package/src/vite/discovery/build-env.ts +148 -0
- package/src/vite/discovery/bundle-scan-phase.ts +128 -0
- package/src/vite/discovery/dev-prerender-cache.ts +8 -3
- package/src/vite/discovery/dev-server-phase.ts +1776 -0
- package/src/vite/discovery/discover-routers.ts +1 -1
- package/src/vite/discovery/discovery-errors.ts +1 -1
- package/src/vite/discovery/prerender-collection.ts +24 -39
- package/src/vite/discovery/state.ts +15 -0
- package/src/vite/plugins/version-plugin.ts +1 -1
- package/src/vite/plugins/virtual-entries.ts +1 -1
- package/src/vite/router-discovery.ts +29 -2428
- package/src/vite/temp-rsc-server.ts +223 -0
- package/src/vite/utils/prerender-utils.ts +47 -0
- package/src/wire-params.ts +35 -0
- package/dist/types/router/segment-resolution/fresh.d.ts +0 -72
- package/src/router/segment-resolution/fresh.ts +0 -945
|
@@ -38,6 +38,7 @@ import { SEGMENT_FRAGMENT_CAPABILITY_HEADER } from "../../segment-fragments.js";
|
|
|
38
38
|
import { PREFETCH_HEADER } from "../../prefetch-markers.js";
|
|
39
39
|
import type { RscPayload } from "../types.js";
|
|
40
40
|
import { getRegisteredStore } from "../navigation-store-handle.js";
|
|
41
|
+
import { writePartialParams } from "../wire-protocol.js";
|
|
41
42
|
|
|
42
43
|
/**
|
|
43
44
|
* Decoder injected at app startup (see setPrefetchDecoder). This is
|
|
@@ -153,24 +154,15 @@ function buildPrefetchUrl(
|
|
|
153
154
|
if (targetUrl.origin !== window.location.origin) {
|
|
154
155
|
return null;
|
|
155
156
|
}
|
|
156
|
-
targetUrl.searchParams.set("_rsc_partial", "true");
|
|
157
|
-
if (segmentIds.length > 0) {
|
|
158
|
-
targetUrl.searchParams.set("_rsc_segments", segmentIds.join(","));
|
|
159
|
-
}
|
|
160
|
-
if (version) {
|
|
161
|
-
targetUrl.searchParams.set("_rsc_v", version);
|
|
162
|
-
}
|
|
163
|
-
if (routerId) {
|
|
164
|
-
targetUrl.searchParams.set("_rsc_rid", routerId);
|
|
165
|
-
}
|
|
166
157
|
// The page's rewrite target, so the server diffs against the route that
|
|
167
158
|
// rendered it, as for the navigation.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
159
|
+
return writePartialParams(targetUrl, {
|
|
160
|
+
segmentIds,
|
|
161
|
+
emptySegments: "omit",
|
|
162
|
+
version,
|
|
163
|
+
routerId,
|
|
164
|
+
rewriteTarget: getRegisteredStore()?.getHistoryEntryMemory()?.rewriteTarget,
|
|
165
|
+
});
|
|
174
166
|
}
|
|
175
167
|
|
|
176
168
|
/**
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { wait, waitForIdle, waitForViewportImages } from "./resource-ready.js";
|
|
18
|
+
import { WIRE_PREFIX } from "../../wire-params.js";
|
|
18
19
|
|
|
19
20
|
// Max prefetches executing at once. Mirrors DEFAULT_PREFETCH_CONCURRENCY
|
|
20
21
|
// (router/prefetch-limits.ts); kept as a local literal so the client bundle
|
|
@@ -128,16 +129,15 @@ export function enqueuePrefetch(
|
|
|
128
129
|
|
|
129
130
|
/**
|
|
130
131
|
* Normalize a URL-like string for keep-alive matching: parse against a
|
|
131
|
-
* placeholder origin and strip
|
|
132
|
+
* placeholder origin and strip every `WIRE_PREFIX` query param. Returns
|
|
132
133
|
* `pathname + search` so comparisons ignore hash and the internal params
|
|
133
|
-
* that prefetch appends to targets
|
|
134
|
-
* `_rsc_v`, `_rsc_rid`, `_rsc_stale`).
|
|
134
|
+
* that prefetch appends to targets.
|
|
135
135
|
*/
|
|
136
136
|
function normalizeForMatch(urlish: string): string {
|
|
137
137
|
try {
|
|
138
138
|
const u = new URL(urlish, "http://placeholder");
|
|
139
139
|
for (const k of [...u.searchParams.keys()]) {
|
|
140
|
-
if (k.startsWith(
|
|
140
|
+
if (k.startsWith(WIRE_PREFIX)) u.searchParams.delete(k);
|
|
141
141
|
}
|
|
142
142
|
return u.pathname + u.search;
|
|
143
143
|
} catch {
|
|
@@ -78,7 +78,8 @@ async function processHandles(
|
|
|
78
78
|
|
|
79
79
|
// This nav's instance token, captured before any await — processHandles runs
|
|
80
80
|
// right after its own commit, so this is that commit's token. generateHistoryKey
|
|
81
|
-
// is URL
|
|
81
|
+
// is the URL (plus the source page for an intercept), so an A->B->A revisit
|
|
82
|
+
// reuses the key; the token lets a late
|
|
82
83
|
// resolution tell its own visit apart from a newer same-URL visit, so a stale
|
|
83
84
|
// nav can never clobber a fresher one's live state or cache (P1).
|
|
84
85
|
const myInstance = store.getNavInstance();
|
|
@@ -33,8 +33,8 @@ export function loaderParentId(id: string): string {
|
|
|
33
33
|
* 2. Place each parallel slot id (contains ".@") immediately after its
|
|
34
34
|
* parent layout/route id. Raw segment-resolution emission order does NOT
|
|
35
35
|
* guarantee this: route-mounted parallels are resolved/pushed BEFORE the
|
|
36
|
-
* route handler's segment is appended (see
|
|
37
|
-
*
|
|
36
|
+
* route handler's segment is appended (see resolveSegment in
|
|
37
|
+
* segment-resolution/revalidation.ts), so matched can read
|
|
38
38
|
* `[..., R0.@panel, R0]`. collectHandleData consumes segmentOrder verbatim
|
|
39
39
|
* with later-wins semantics, so without normalization the route handler's
|
|
40
40
|
* Meta would override the slot's more-specific Meta — backwards.
|
|
@@ -42,6 +42,7 @@ import { setAppVersion } from "./app-version.js";
|
|
|
42
42
|
import {
|
|
43
43
|
isInterceptSegment,
|
|
44
44
|
splitInterceptSegments,
|
|
45
|
+
wholeInterceptMatched,
|
|
45
46
|
} from "./intercept-utils.js";
|
|
46
47
|
import { createAppShellRef } from "./app-shell.js";
|
|
47
48
|
import { bootLog, IS_BROWSER_DEBUG } from "./logging.js";
|
|
@@ -75,7 +76,7 @@ export interface InitBrowserAppOptions {
|
|
|
75
76
|
storeOptions?: {
|
|
76
77
|
/**
|
|
77
78
|
* Maximum number of history entries to cache
|
|
78
|
-
* @default
|
|
79
|
+
* @default 20
|
|
79
80
|
*/
|
|
80
81
|
cacheSize?: number;
|
|
81
82
|
};
|
|
@@ -531,6 +532,9 @@ export async function initBrowserApp(
|
|
|
531
532
|
const streamingToken = handle.startStreaming();
|
|
532
533
|
|
|
533
534
|
const interceptSourceUrl = store.getInterceptSourceUrl();
|
|
535
|
+
// Read before a version change below clears the history cache.
|
|
536
|
+
const onScreen =
|
|
537
|
+
store.getCachedSegments(store.getHistoryKey())?.segments ?? [];
|
|
534
538
|
|
|
535
539
|
try {
|
|
536
540
|
const { payload, streamComplete } = await client.fetchPartial({
|
|
@@ -582,7 +586,17 @@ export async function initBrowserApp(
|
|
|
582
586
|
// a paramless document GET would run the SSR path and surface the
|
|
583
587
|
// not-found page during that same transient.
|
|
584
588
|
if (payload.metadata?.isPartial) {
|
|
585
|
-
|
|
589
|
+
// Held nothing: an intercept answer keeps the page under the slot
|
|
590
|
+
// from the entry on screen; the layouts and the slot are fresh.
|
|
591
|
+
let segments = payload.metadata.segments || [];
|
|
592
|
+
const kept = wholeInterceptMatched(onScreen, payload.metadata);
|
|
593
|
+
if (kept) {
|
|
594
|
+
payload.metadata.matched = kept;
|
|
595
|
+
const byId = new Map(
|
|
596
|
+
[...onScreen, ...segments].map((s) => [s.id, s]),
|
|
597
|
+
);
|
|
598
|
+
segments = kept.flatMap((id) => byId.get(id) ?? []);
|
|
599
|
+
}
|
|
586
600
|
const matched = payload.metadata.matched || [];
|
|
587
601
|
|
|
588
602
|
// Derive intercept state from the returned payload, not the
|
|
@@ -600,6 +614,7 @@ export async function initBrowserApp(
|
|
|
600
614
|
|
|
601
615
|
const historyKey = generateHistoryKey(window.location.href, {
|
|
602
616
|
intercept: responseIsIntercept,
|
|
617
|
+
interceptSource: interceptSourceUrl ?? undefined,
|
|
603
618
|
});
|
|
604
619
|
store.setHistoryKey(historyKey);
|
|
605
620
|
const currentHandleData = liveHandleDataForCache(
|
|
@@ -46,6 +46,60 @@ export interface ReconcileResult {
|
|
|
46
46
|
interceptSegments: ResolvedSegment[];
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* A segment the server sent over its cached copy, keeping the cached
|
|
51
|
+
* structural properties so the React tree keeps its shape. Changing them
|
|
52
|
+
* between renders alters the element nesting (with/without
|
|
53
|
+
* RouteContentWrapper, MountContextProvider, etc.), which remounts components
|
|
54
|
+
* and destroys useActionState.
|
|
55
|
+
*/
|
|
56
|
+
function mergeCached(
|
|
57
|
+
fromServer: ResolvedSegment,
|
|
58
|
+
fromCache: ResolvedSegment,
|
|
59
|
+
actor: ReconcileActor,
|
|
60
|
+
): ResolvedSegment {
|
|
61
|
+
let merged = fromServer;
|
|
62
|
+
|
|
63
|
+
// When server returns component: null for a layout segment, it means
|
|
64
|
+
// "this segment doesn't need re-rendering" - preserve the cached component
|
|
65
|
+
// to maintain the outlet chain and prevent React tree changes
|
|
66
|
+
if (
|
|
67
|
+
fromServer.component === null &&
|
|
68
|
+
fromServer.type === "layout" &&
|
|
69
|
+
fromCache.component != null
|
|
70
|
+
) {
|
|
71
|
+
merged = { ...merged, component: fromCache.component };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Loading preservation is actor-aware:
|
|
75
|
+
// - action/stale-revalidation: always preserve cached value to prevent
|
|
76
|
+
// tree remount (even when cached is undefined, to avoid adding a
|
|
77
|
+
// Suspense boundary that wasn't there before)
|
|
78
|
+
// - navigation: only when cached is defined (building a new tree)
|
|
79
|
+
if (
|
|
80
|
+
fromServer.loading !== fromCache.loading &&
|
|
81
|
+
(actor !== "navigation" || fromCache.loading !== undefined)
|
|
82
|
+
) {
|
|
83
|
+
merged = { ...merged, loading: fromCache.loading };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// mountPath: SSR segments may lack mountPath while revalidated segments
|
|
87
|
+
// include it. The conditional MountContextProvider wrapper changes tree depth.
|
|
88
|
+
if (fromServer.mountPath !== fromCache.mountPath) {
|
|
89
|
+
merged = { ...merged, mountPath: fromCache.mountPath };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Dev-mode assertion: warn if the merged result still differs from cache
|
|
93
|
+
// in tree-structural properties. This catches bugs where the merge code
|
|
94
|
+
// above fails to preserve a value it should have.
|
|
95
|
+
assertSegmentStructure(
|
|
96
|
+
fromCache,
|
|
97
|
+
merged,
|
|
98
|
+
actor === "action" ? "action-bridge" : "partial-update",
|
|
99
|
+
);
|
|
100
|
+
return merged;
|
|
101
|
+
}
|
|
102
|
+
|
|
49
103
|
/**
|
|
50
104
|
* Single source of truth for merging server segments with cached segments.
|
|
51
105
|
*
|
|
@@ -70,7 +124,6 @@ export interface ReconcileResult {
|
|
|
70
124
|
*/
|
|
71
125
|
export function reconcileSegments(input: ReconcileInput): ReconcileResult {
|
|
72
126
|
const { actor, matched, diff, insertMissingDiff } = input;
|
|
73
|
-
const context = actor === "action" ? "action-bridge" : "partial-update";
|
|
74
127
|
|
|
75
128
|
// Build lookup maps from arrays
|
|
76
129
|
const serverSegments = new Map<string, ResolvedSegment>();
|
|
@@ -111,59 +164,13 @@ export function reconcileSegments(input: ReconcileInput): ReconcileResult {
|
|
|
111
164
|
|
|
112
165
|
if (fromServer) {
|
|
113
166
|
const inDiff = diffSet.has(segId);
|
|
114
|
-
// Preserve cached structural properties to maintain consistent React tree.
|
|
115
|
-
// Changing these between renders alters the element nesting
|
|
116
|
-
// (with/without RouteContentWrapper, MountContextProvider, etc.),
|
|
117
|
-
// causing React to remount components and destroy useActionState.
|
|
118
167
|
if (fromCache) {
|
|
119
|
-
let merged = fromServer;
|
|
120
|
-
|
|
121
|
-
// When server returns component: null for a layout segment, it means
|
|
122
|
-
// "this segment doesn't need re-rendering" - preserve the cached component
|
|
123
|
-
// to maintain the outlet chain and prevent React tree changes
|
|
124
|
-
if (
|
|
125
|
-
fromServer.component === null &&
|
|
126
|
-
fromServer.type === "layout" &&
|
|
127
|
-
fromCache.component != null
|
|
128
|
-
) {
|
|
129
|
-
merged = { ...merged, component: fromCache.component };
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Loading preservation is actor-aware:
|
|
133
|
-
// - action/stale-revalidation: always preserve cached value to prevent
|
|
134
|
-
// tree remount (even when cached is undefined, to avoid adding a
|
|
135
|
-
// Suspense boundary that wasn't there before)
|
|
136
|
-
// - navigation: only when cached is defined (building a new tree)
|
|
137
|
-
if (actor !== "navigation") {
|
|
138
|
-
if (fromServer.loading !== fromCache.loading) {
|
|
139
|
-
merged = { ...merged, loading: fromCache.loading };
|
|
140
|
-
}
|
|
141
|
-
} else {
|
|
142
|
-
if (
|
|
143
|
-
fromCache.loading !== undefined &&
|
|
144
|
-
fromServer.loading !== fromCache.loading
|
|
145
|
-
) {
|
|
146
|
-
merged = { ...merged, loading: fromCache.loading };
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// mountPath: SSR segments may lack mountPath while revalidated segments
|
|
151
|
-
// include it. The conditional MountContextProvider wrapper changes tree depth.
|
|
152
|
-
if (fromServer.mountPath !== fromCache.mountPath) {
|
|
153
|
-
merged = { ...merged, mountPath: fromCache.mountPath };
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Dev-mode assertion: warn if the merged result still differs from cache
|
|
157
|
-
// in tree-structural properties. This catches bugs where the merge code
|
|
158
|
-
// above fails to preserve a value it should have.
|
|
159
|
-
assertSegmentStructure(fromCache, merged, context);
|
|
160
|
-
|
|
161
168
|
if (IS_BROWSER_DEBUG) {
|
|
162
169
|
debugLog(
|
|
163
170
|
`[reconcile] ${segId}: SERVER+CACHE merge (${inDiff ? "in diff" : "not in diff"}, type=${fromServer.type}, component=${fromServer.component === null ? "null→cached" : "server"})`,
|
|
164
171
|
);
|
|
165
172
|
}
|
|
166
|
-
return
|
|
173
|
+
return mergeCached(fromServer, fromCache, actor);
|
|
167
174
|
}
|
|
168
175
|
if (IS_BROWSER_DEBUG) {
|
|
169
176
|
debugLog(
|
|
@@ -221,11 +228,13 @@ export function reconcileSegments(input: ReconcileInput): ReconcileResult {
|
|
|
221
228
|
}
|
|
222
229
|
|
|
223
230
|
/**
|
|
224
|
-
* Reconcile error
|
|
231
|
+
* Reconcile an error-boundary response with cached segments.
|
|
225
232
|
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
* preserving sibling layouts that aren't in
|
|
233
|
+
* The server returns the error boundary segment and, above it, what the
|
|
234
|
+
* action's revalidation re-ran (rsc/server-action.ts). This function overlays
|
|
235
|
+
* them onto the full cached tree, preserving sibling layouts that aren't in
|
|
236
|
+
* the error parent chain. The error segment replaces its cached copy; every
|
|
237
|
+
* other segment merges as an action's does (mergeCached).
|
|
229
238
|
*/
|
|
230
239
|
export function reconcileErrorSegments(
|
|
231
240
|
cachedSegments: ResolvedSegment[],
|
|
@@ -236,7 +245,11 @@ export function reconcileErrorSegments(
|
|
|
236
245
|
|
|
237
246
|
const segments = cachedSegments.map((cached) => {
|
|
238
247
|
const fromServer = errorMap.get(cached.id);
|
|
239
|
-
return fromServer
|
|
248
|
+
return !fromServer
|
|
249
|
+
? cached
|
|
250
|
+
: fromServer.type === "error"
|
|
251
|
+
? fromServer
|
|
252
|
+
: mergeCached(fromServer, cached, "action");
|
|
240
253
|
});
|
|
241
254
|
|
|
242
255
|
const { main, intercept } = splitInterceptSegments(segments);
|