@rangojs/router 0.27.0 → 0.29.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-fence.d.ts +2 -1
- package/dist/types/browser/event-controller.d.ts +8 -2
- package/dist/types/browser/history-state.d.ts +3 -9
- package/dist/types/browser/navigation-store.d.ts +8 -6
- package/dist/types/browser/partial-update.d.ts +11 -0
- package/dist/types/browser/react/filter-segment-order.d.ts +2 -2
- package/dist/types/browser/react/use-action.d.ts +7 -2
- package/dist/types/browser/react/use-handle.d.ts +19 -16
- package/dist/types/browser/react/use-link-status.d.ts +5 -2
- package/dist/types/browser/segment-reconciler.d.ts +32 -4
- package/dist/types/browser/types.d.ts +24 -5
- package/dist/types/browser/wire-protocol.d.ts +26 -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/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 +5 -14
- 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 +11 -5
- 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 +6 -11
- package/dist/types/router/match-handlers.d.ts +2 -6
- package/dist/types/router/match-middleware/background-revalidation.d.ts +13 -22
- package/dist/types/router/match-middleware/cache-lookup.d.ts +34 -86
- package/dist/types/router/match-middleware/segment-resolution.d.ts +8 -6
- package/dist/types/router/navigation-snapshot.d.ts +7 -0
- 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/router-context.d.ts +7 -46
- package/dist/types/router/router-interfaces.d.ts +8 -14
- package/dist/types/router/segment-resolution/helpers.d.ts +19 -3
- 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 +95 -0
- package/dist/types/router/segment-resolution/revalidation.d.ts +82 -31
- package/dist/types/router/segment-resolution/streamed-handler-boundary.d.ts +2 -0
- 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 +11 -1
- 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 +1 -1
- 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 +53 -104
- package/dist/types/server/rewrite-trail.d.ts +2 -2
- package/dist/types/testing/dispatch.d.ts +35 -18
- package/dist/types/testing/render-route.d.ts +43 -8
- package/dist/types/testing/serve-shell-request.d.ts +8 -1
- package/dist/types/types/cache-types.d.ts +6 -3
- package/dist/types/types/error-types.d.ts +30 -3
- package/dist/types/types/handler-context.d.ts +9 -1
- 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 +39 -0
- package/dist/vite/index.js +3822 -3727
- 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 +4 -2
- package/skills/document-cache/SKILL.md +13 -1
- package/skills/handler-use/SKILL.md +1 -1
- package/skills/hooks/SKILL.md +1 -1
- package/skills/hooks/data.md +8 -1
- package/skills/hooks/handle-and-actions.md +33 -2
- package/skills/hooks/navigation.md +121 -15
- package/skills/hooks/state.md +6 -4
- package/skills/hooks/urls.md +16 -5
- package/skills/intercept/SKILL.md +23 -7
- package/skills/middleware/SKILL.md +18 -0
- package/skills/observability/SKILL.md +30 -0
- package/skills/ppr/SKILL.md +18 -3
- package/skills/prerender/SKILL.md +39 -15
- package/skills/rango/SKILL.md +5 -1
- package/skills/server-actions/SKILL.md +15 -1
- package/skills/testing/client-components.md +25 -18
- 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/view-transitions/SKILL.md +1 -1
- package/src/__internal.ts +7 -1
- package/src/browser/action-fence.ts +2 -1
- package/src/browser/connection-warmup.ts +3 -1
- package/src/browser/event-controller.ts +37 -13
- package/src/browser/history-state.ts +6 -12
- package/src/browser/navigation-bridge.ts +59 -26
- package/src/browser/navigation-client.ts +20 -25
- package/src/browser/navigation-store.ts +25 -27
- package/src/browser/navigation-transaction.ts +4 -1
- package/src/browser/network-error-handler.ts +4 -0
- package/src/browser/partial-update.ts +33 -6
- 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/react/use-action.ts +24 -48
- package/src/browser/react/use-handle.ts +22 -18
- package/src/browser/react/use-link-status.ts +48 -60
- package/src/browser/react/use-navigation.ts +92 -41
- package/src/browser/rsc-router.tsx +1 -0
- package/src/browser/segment-reconciler.ts +109 -53
- package/src/browser/server-action-bridge.ts +81 -61
- package/src/browser/types.ts +25 -5
- package/src/browser/wire-protocol.ts +43 -0
- package/src/cache/cache-key-utils.ts +11 -5
- package/src/cache/cache-runtime.ts +132 -75
- 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 +64 -46
- package/src/cache/handle-snapshot.ts +19 -7
- package/src/cache/read-through-swr.ts +20 -18
- package/src/cache/segment-codec.ts +55 -0
- package/src/cache/shell-snapshot.ts +2 -2
- package/src/cache/tag-invalidation.ts +6 -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 +5 -16
- 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 +22 -10
- package/src/router/instrument.ts +4 -3
- package/src/router/intercept-resolution.ts +82 -75
- package/src/router/loader-resolution.ts +5 -4
- package/src/router/match-api.ts +5 -5
- package/src/router/match-context.ts +23 -12
- package/src/router/match-handlers.ts +8 -20
- package/src/router/match-middleware/background-revalidation.ts +46 -57
- package/src/router/match-middleware/cache-lookup.ts +677 -726
- package/src/router/match-middleware/cache-store.ts +4 -8
- package/src/router/match-middleware/intercept-resolution.ts +3 -23
- package/src/router/match-middleware/segment-resolution.ts +34 -61
- package/src/router/middleware.ts +69 -11
- package/src/router/navigation-snapshot.ts +14 -4
- 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/route-snapshot.ts +2 -5
- package/src/router/router-context.ts +10 -95
- package/src/router/router-interfaces.ts +8 -14
- package/src/router/segment-resolution/helpers.ts +49 -13
- package/src/router/segment-resolution/loader-cache.ts +15 -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 +139 -0
- package/src/router/segment-resolution/revalidation.ts +939 -789
- package/src/router/segment-resolution/streamed-handler-boundary.ts +3 -0
- 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 +11 -1
- package/src/router.ts +19 -17
- 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 +759 -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 +164 -349
- package/src/rsc/server-action.ts +87 -113
- package/src/rsc/shell-capture-constants.ts +1 -1
- package/src/rsc/shell-capture.ts +275 -227
- 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 +75 -115
- package/src/server/rewrite-trail.ts +6 -4
- package/src/ssr/index.tsx +1 -1
- package/src/testing/dispatch.ts +226 -710
- package/src/testing/e2e/page-helpers.ts +2 -2
- package/src/testing/render-route.tsx +199 -13
- package/src/testing/serve-shell-request.ts +36 -40
- package/src/types/cache-types.ts +6 -3
- package/src/types/error-types.ts +30 -3
- package/src/types/handler-context.ts +12 -1
- 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 +40 -0
- package/dist/types/router/segment-resolution/fresh.d.ts +0 -72
- package/src/router/segment-resolution/fresh.ts +0 -945
|
@@ -92,6 +92,11 @@ export interface ActionEntry {
|
|
|
92
92
|
startedAt: number;
|
|
93
93
|
/** Whether action processing is complete (may still be streaming) */
|
|
94
94
|
completed?: boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Set when a later call started (see startAction): "id" hides this call
|
|
97
|
+
* from every reader, "name" from readers that match by export name.
|
|
98
|
+
*/
|
|
99
|
+
supersededBy?: "id" | "name";
|
|
95
100
|
}
|
|
96
101
|
|
|
97
102
|
/**
|
|
@@ -104,7 +109,7 @@ export interface DerivedNavigationState {
|
|
|
104
109
|
isStreaming: boolean;
|
|
105
110
|
/** Whether a navigation is active (fetching or streaming, before commit) */
|
|
106
111
|
isNavigating: boolean;
|
|
107
|
-
/**
|
|
112
|
+
/** The location of the last page flushed to React (useNavigation keeps the page on screen) */
|
|
108
113
|
location: NavigationLocation;
|
|
109
114
|
/** URL being navigated to (null if idle) */
|
|
110
115
|
pendingUrl: string | null;
|
|
@@ -258,7 +263,8 @@ export interface EventController {
|
|
|
258
263
|
/** Originating history entry key; scopes location-state arbitration. */
|
|
259
264
|
cohort?: string,
|
|
260
265
|
): ActionHandle;
|
|
261
|
-
|
|
266
|
+
/** Abort every action in flight except `except` (an ActionHandle id). */
|
|
267
|
+
abortAllActions(except?: string): void;
|
|
262
268
|
|
|
263
269
|
// State access
|
|
264
270
|
getState(): DerivedNavigationState;
|
|
@@ -711,15 +717,18 @@ export function createEventController(
|
|
|
711
717
|
// idle snapshot (the same reference use-action falls back to).
|
|
712
718
|
if (inflightActions.size === 0) return DEFAULT_ACTION_STATE;
|
|
713
719
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
720
|
+
// The latest call this reader matches, or none once a later call left the
|
|
721
|
+
// map: an earlier call still running is never reported again.
|
|
722
|
+
const byName = !actionId.includes("#");
|
|
723
|
+
let entry: ActionEntry | undefined;
|
|
724
|
+
for (const a of inflightActions.values()) {
|
|
725
|
+
if (
|
|
726
|
+
matchesActionId(actionId, a.actionId) &&
|
|
727
|
+
(byName ? !a.supersededBy : a.supersededBy !== "id")
|
|
728
|
+
) {
|
|
729
|
+
entry = a;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
723
732
|
|
|
724
733
|
if (!entry) {
|
|
725
734
|
return { ...DEFAULT_ACTION_STATE };
|
|
@@ -912,6 +921,20 @@ export function createEventController(
|
|
|
912
921
|
hadAnyConcurrentActions = true;
|
|
913
922
|
}
|
|
914
923
|
|
|
924
|
+
// useAction reports the latest call. Every earlier call that a reader of
|
|
925
|
+
// this one also matches is hidden from that reader for good, so it never
|
|
926
|
+
// shows again after this call settles and leaves the map: a call of the
|
|
927
|
+
// same action from every reader, a call of another action with the same
|
|
928
|
+
// export name from readers that match by name (matchesActionId).
|
|
929
|
+
const hash = actionId.lastIndexOf("#");
|
|
930
|
+
const name = hash === -1 ? null : actionId.slice(hash + 1);
|
|
931
|
+
for (const a of inflightActions.values()) {
|
|
932
|
+
if (a.actionId === actionId) a.supersededBy = "id";
|
|
933
|
+
else if (name !== null && matchesActionId(name, a.actionId)) {
|
|
934
|
+
a.supersededBy ??= "name";
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
|
|
915
938
|
const entry: ActionEntry = {
|
|
916
939
|
id,
|
|
917
940
|
actionId,
|
|
@@ -1108,13 +1131,14 @@ export function createEventController(
|
|
|
1108
1131
|
};
|
|
1109
1132
|
}
|
|
1110
1133
|
|
|
1111
|
-
|
|
1134
|
+
/** Abort every action in flight except `except` (an ActionHandle id). */
|
|
1135
|
+
function abortAllActions(except?: string) {
|
|
1112
1136
|
for (const [id, entry] of inflightActions) {
|
|
1113
1137
|
// Preserve settling entries — they have already been handled by
|
|
1114
1138
|
// fail()/complete() and will self-cleanup via the settlement timeout.
|
|
1115
1139
|
// Clearing them here would prevent debounced notifications from
|
|
1116
1140
|
// delivering the error/result state to subscribers.
|
|
1117
|
-
if (entry.phase === "settling") continue;
|
|
1141
|
+
if (entry.phase === "settling" || id === except) continue;
|
|
1118
1142
|
entry.abort.abort();
|
|
1119
1143
|
inflightActions.delete(id);
|
|
1120
1144
|
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
resolveLocationStateEntries,
|
|
10
10
|
stampLocationState,
|
|
11
11
|
} from "./react/location-state-shared.js";
|
|
12
|
+
import { WIRE } from "../wire-params.js";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Check if state is from typed LocationStateEntry[] (has __rsc_ls_ keys)
|
|
@@ -137,16 +138,9 @@ export function pushHistoryWithIdx(
|
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* cache miss.
|
|
144
|
-
*/
|
|
145
|
-
export const SHELL_MISS_MARKER: string = "_rsc_shell";
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Drop the forced-MISS marker from the address bar at boot, keeping the
|
|
149
|
-
* history entry's state (initBrowserApp calls it before any location read).
|
|
141
|
+
* Drop the forced-MISS marker (`WIRE.shellMiss`, rsc/shell-serve.ts) from the
|
|
142
|
+
* address bar at boot, keeping the history entry's state (initBrowserApp
|
|
143
|
+
* calls it before any location read).
|
|
150
144
|
* Left in place, a refresh or a shared link would stay off the shell and the
|
|
151
145
|
* app's search params would carry it. The server strips the same marker from
|
|
152
146
|
* the request before rendering (shell-serve.ts withoutShellMissMarker), so
|
|
@@ -154,8 +148,8 @@ export const SHELL_MISS_MARKER: string = "_rsc_shell";
|
|
|
154
148
|
*/
|
|
155
149
|
export function stripShellMissMarker(): void {
|
|
156
150
|
const url = new URL(window.location.href);
|
|
157
|
-
if (!url.searchParams.has(
|
|
158
|
-
url.searchParams.delete(
|
|
151
|
+
if (!url.searchParams.has(WIRE.shellMiss)) return;
|
|
152
|
+
url.searchParams.delete(WIRE.shellMiss);
|
|
159
153
|
window.history.replaceState(window.history.state, "", url.href);
|
|
160
154
|
}
|
|
161
155
|
|
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
} from "./navigation-store.js";
|
|
46
46
|
import type { EventController } from "./event-controller.js";
|
|
47
47
|
import { isInterceptOnlyCache } from "./intercept-utils.js";
|
|
48
|
+
import { actionBoundaryAt, idsAboveBoundary } from "./segment-reconciler.js";
|
|
48
49
|
import {
|
|
49
50
|
toNetworkError,
|
|
50
51
|
emitNetworkError,
|
|
@@ -206,7 +207,10 @@ export function createNavigationBridge(
|
|
|
206
207
|
error: ServerRedirect,
|
|
207
208
|
url: string,
|
|
208
209
|
hops: number,
|
|
209
|
-
options:
|
|
210
|
+
options: Pick<
|
|
211
|
+
NavigateOptionsInternal,
|
|
212
|
+
"replace" | "transition" | "_redirectFrom"
|
|
213
|
+
>,
|
|
210
214
|
): Promise<void> {
|
|
211
215
|
const redirectUrl = validateRedirectOrigin(
|
|
212
216
|
error.url,
|
|
@@ -228,6 +232,7 @@ export function createNavigationBridge(
|
|
|
228
232
|
...options,
|
|
229
233
|
_skipCache: true,
|
|
230
234
|
_redirectHops: hops + 1,
|
|
235
|
+
_redirectFrom: options._redirectFrom ?? url,
|
|
231
236
|
});
|
|
232
237
|
}
|
|
233
238
|
|
|
@@ -273,15 +278,18 @@ export function createNavigationBridge(
|
|
|
273
278
|
targetUrl.pathname === new URL(window.location.href).pathname
|
|
274
279
|
) {
|
|
275
280
|
// Preserve intercept context from the current history entry so that
|
|
276
|
-
// popstate uses the
|
|
277
|
-
//
|
|
281
|
+
// popstate uses the same cache key and restores the right full-page
|
|
282
|
+
// vs intercept semantics.
|
|
278
283
|
const currentHistoryState = window.history.state;
|
|
279
284
|
const isIntercept = currentHistoryState?.intercept === true;
|
|
280
285
|
const interceptSourceUrl = isIntercept
|
|
281
286
|
? currentHistoryState?.sourceUrl
|
|
282
287
|
: undefined;
|
|
283
288
|
|
|
284
|
-
const historyKey = generateHistoryKey(url, {
|
|
289
|
+
const historyKey = generateHistoryKey(url, {
|
|
290
|
+
intercept: isIntercept,
|
|
291
|
+
interceptSource: interceptSourceUrl,
|
|
292
|
+
});
|
|
285
293
|
|
|
286
294
|
// Copy current segments to the new history key so back/forward restores instantly.
|
|
287
295
|
// The array itself, not a clone: getPageHistoryKeys reads a shared
|
|
@@ -405,25 +413,24 @@ export function createNavigationBridge(
|
|
|
405
413
|
});
|
|
406
414
|
}
|
|
407
415
|
|
|
408
|
-
// Also check if there's an intercept cache entry for this URL
|
|
409
|
-
// If so, this URL CAN be intercepted, and we shouldn't use the non-intercept cache
|
|
410
|
-
// because the navigation might result in an intercept (depending on source URL)
|
|
411
|
-
const interceptHistoryKey = generateHistoryKey(url, { intercept: true });
|
|
412
|
-
const hasInterceptCache = store.hasHistoryCache(interceptHistoryKey);
|
|
413
|
-
|
|
414
416
|
// Skip cached SWR for:
|
|
415
417
|
// 1. intercept caches - interception depends on source page context
|
|
416
418
|
// 2. routes that CAN be intercepted - we don't know if this navigation will intercept
|
|
417
419
|
// 3. when leaving intercept - we need fresh non-intercept segments from server
|
|
418
420
|
// 4. redirect-with-state - force re-render so hooks read fresh state
|
|
419
421
|
// 5. stale cache - server action invalidated it, need fresh data with loading state
|
|
422
|
+
// 6. an error boundary in the list - a link renders the page again; its
|
|
423
|
+
// ids would tell the server the client holds what the error replaced
|
|
420
424
|
const hasUsableCache =
|
|
421
425
|
isRestorable(cachedSegments) &&
|
|
426
|
+
!cachedSegments.some((s) => s.type === "error") &&
|
|
422
427
|
!isInterceptOnlyCache(cachedSegments) &&
|
|
423
|
-
!hasInterceptCache &&
|
|
424
428
|
!isLeavingIntercept &&
|
|
425
429
|
!cached?.stale &&
|
|
426
|
-
!options?._skipCache
|
|
430
|
+
!options?._skipCache &&
|
|
431
|
+
// A cached intercept entry for this URL (from any source page) means
|
|
432
|
+
// the navigation might intercept, depending on the source URL.
|
|
433
|
+
!store.hasInterceptCache(historyKey);
|
|
427
434
|
|
|
428
435
|
// Forward navigations always await fetchPartialUpdate before rendering,
|
|
429
436
|
// so useNavigation should always report "loading". skipLoadingState is
|
|
@@ -539,7 +546,11 @@ export function createNavigationBridge(
|
|
|
539
546
|
error,
|
|
540
547
|
url,
|
|
541
548
|
options?._redirectHops ?? 0,
|
|
542
|
-
{
|
|
549
|
+
{
|
|
550
|
+
replace: options?.replace,
|
|
551
|
+
transition: options?.transition,
|
|
552
|
+
_redirectFrom: options?._redirectFrom,
|
|
553
|
+
},
|
|
543
554
|
);
|
|
544
555
|
}
|
|
545
556
|
|
|
@@ -570,9 +581,19 @@ export function createNavigationBridge(
|
|
|
570
581
|
},
|
|
571
582
|
|
|
572
583
|
/**
|
|
573
|
-
* Refresh current route
|
|
584
|
+
* Refresh current route. A cross-tab refresh holds the action error
|
|
585
|
+
* boundary the page shows (actionBoundaryAt), as the follow-up does: it
|
|
586
|
+
* is this tab's own outcome, not a read another tab's write changed. A
|
|
587
|
+
* refresh the app asks for renders it again.
|
|
574
588
|
*/
|
|
575
|
-
async refresh(): Promise<void> {
|
|
589
|
+
async refresh(crossTab?: boolean): Promise<void> {
|
|
590
|
+
const shown =
|
|
591
|
+
(crossTab &&
|
|
592
|
+
!store.getInterceptSourceUrl() &&
|
|
593
|
+
store.getCachedSegments(store.getHistoryKey())?.segments) ||
|
|
594
|
+
[];
|
|
595
|
+
const at = actionBoundaryAt(shown);
|
|
596
|
+
if (at === 0) return;
|
|
576
597
|
eventController.abortNavigation();
|
|
577
598
|
|
|
578
599
|
const tx = createNavigationTransaction(
|
|
@@ -590,7 +611,7 @@ export function createNavigationBridge(
|
|
|
590
611
|
false,
|
|
591
612
|
tx.handle.signal,
|
|
592
613
|
tx.with({ url: window.location.href, replace: true, scroll: false }),
|
|
593
|
-
{ type: "navigate", refresh: true },
|
|
614
|
+
{ type: "navigate", refresh: true, boundary: shown[at]?.id },
|
|
594
615
|
);
|
|
595
616
|
} catch (error) {
|
|
596
617
|
// Aborted or superseded: bail without rendering a boundary (see navigate()).
|
|
@@ -664,8 +685,10 @@ export function createNavigationBridge(
|
|
|
664
685
|
const isLeavingIntercept =
|
|
665
686
|
!isIntercept && currentInterceptSource !== null;
|
|
666
687
|
|
|
667
|
-
|
|
668
|
-
|
|
688
|
+
const historyKey = generateHistoryKey(url, {
|
|
689
|
+
intercept: isIntercept,
|
|
690
|
+
interceptSource: interceptSourceUrl,
|
|
691
|
+
});
|
|
669
692
|
|
|
670
693
|
debugLog(
|
|
671
694
|
"[Browser] Popstate -",
|
|
@@ -716,9 +739,20 @@ export function createNavigationBridge(
|
|
|
716
739
|
}),
|
|
717
740
|
});
|
|
718
741
|
|
|
742
|
+
// An action error boundary the entry shows (actionBoundaryAt) is
|
|
743
|
+
// held: the page holds the ids above it, so a link renders it again,
|
|
744
|
+
// and the revalidation below renders above it. Never under an
|
|
745
|
+
// intercept, as the follow-up (server-action-bridge.ts).
|
|
746
|
+
const boundaryAt = isIntercept ? -1 : actionBoundaryAt(cachedSegments);
|
|
747
|
+
const boundary = cachedSegments[boundaryAt]?.id;
|
|
748
|
+
const heldIds = idsAboveBoundary(
|
|
749
|
+
cachedSegments.map((s) => s.id),
|
|
750
|
+
boundary,
|
|
751
|
+
);
|
|
752
|
+
|
|
719
753
|
// Update store to point to this history entry
|
|
720
754
|
store.setHistoryKey(historyKey);
|
|
721
|
-
store.setSegmentIds(
|
|
755
|
+
store.setSegmentIds(heldIds);
|
|
722
756
|
store.setCurrentUrl(url);
|
|
723
757
|
store.setPath(new URL(url).pathname);
|
|
724
758
|
|
|
@@ -797,8 +831,9 @@ export function createNavigationBridge(
|
|
|
797
831
|
onUpdate({ ...popstateUpdate, inTransition: true });
|
|
798
832
|
});
|
|
799
833
|
}
|
|
800
|
-
// SWR: If stale, trigger background revalidation
|
|
801
|
-
|
|
834
|
+
// SWR: If stale, trigger background revalidation. Nothing is
|
|
835
|
+
// above a boundary at the page's first segment.
|
|
836
|
+
if (isStale && boundaryAt !== 0) {
|
|
802
837
|
debugLog("[Browser] Cache is stale, background revalidating...");
|
|
803
838
|
// Background revalidation - don't await, just fire and forget.
|
|
804
839
|
// When the entry's handles are incomplete (a deferred Meta was still
|
|
@@ -810,9 +845,7 @@ export function createNavigationBridge(
|
|
|
810
845
|
// would stay the pre-resolution carry. handlesPending is set only for
|
|
811
846
|
// the deferred-Meta-aborted case, so action/cross-tab SWR keeps the
|
|
812
847
|
// cheap diff path.
|
|
813
|
-
const segmentIds = cached?.handlesPending
|
|
814
|
-
? []
|
|
815
|
-
: cachedSegments.map((s) => s.id);
|
|
848
|
+
const segmentIds = cached?.handlesPending ? [] : heldIds;
|
|
816
849
|
|
|
817
850
|
const tx = createNavigationTransaction(
|
|
818
851
|
store,
|
|
@@ -834,7 +867,7 @@ export function createNavigationBridge(
|
|
|
834
867
|
interceptSourceUrl,
|
|
835
868
|
cacheOnly: true,
|
|
836
869
|
}),
|
|
837
|
-
{ type: "stale-revalidation", interceptSourceUrl },
|
|
870
|
+
{ type: "stale-revalidation", interceptSourceUrl, boundary },
|
|
838
871
|
)
|
|
839
872
|
.catch((error) => {
|
|
840
873
|
if (isBackgroundSuppressible(error)) return;
|
|
@@ -989,7 +1022,7 @@ export function createNavigationBridge(
|
|
|
989
1022
|
|
|
990
1023
|
// Register cross-tab refresh callback with the store
|
|
991
1024
|
store.setCrossTabRefreshCallback(() => {
|
|
992
|
-
this.refresh();
|
|
1025
|
+
this.refresh(true);
|
|
993
1026
|
});
|
|
994
1027
|
|
|
995
1028
|
window.addEventListener("popstate", handlePopstate);
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
type DecodedPrefetch,
|
|
39
39
|
} from "./prefetch/cache.js";
|
|
40
40
|
import { cancelAllPrefetches } from "./prefetch/loader.js";
|
|
41
|
-
import {
|
|
41
|
+
import { writePartialParams } from "./wire-protocol.js";
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Create a navigation client for fetching RSC payloads
|
|
@@ -82,6 +82,7 @@ export function createNavigationClient(
|
|
|
82
82
|
rewriteTarget,
|
|
83
83
|
signal,
|
|
84
84
|
staleRevalidation,
|
|
85
|
+
boundary,
|
|
85
86
|
interceptSourceUrl,
|
|
86
87
|
version,
|
|
87
88
|
routerId,
|
|
@@ -103,30 +104,24 @@ export function createNavigationClient(
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
// Build fetch URL with partial rendering params (used for both
|
|
106
|
-
// cache key lookup and actual fetch if cache misses)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
// A param, not a header: the document cache decides from the raw URL
|
|
125
|
-
// (cache/document-cache.ts), and it keeps the fill out of every
|
|
126
|
-
// prefetch key below.
|
|
127
|
-
if (fill) {
|
|
128
|
-
fetchUrl.searchParams.set(FILL_PARAM, "1");
|
|
129
|
-
}
|
|
107
|
+
// cache key lookup and actual fetch if cache misses). The page left's
|
|
108
|
+
// rewrite target (#1144) is in both prefetch keys, like the params
|
|
109
|
+
// around it. The fill is a param, not a header: the document cache
|
|
110
|
+
// decides from the raw URL (cache/document-cache.ts), and it keeps the
|
|
111
|
+
// fill out of every prefetch key below.
|
|
112
|
+
const fetchUrl = writePartialParams(
|
|
113
|
+
new URL(targetUrl, window.location.origin),
|
|
114
|
+
{
|
|
115
|
+
segmentIds,
|
|
116
|
+
emptySegments: "send",
|
|
117
|
+
stale: staleRevalidation,
|
|
118
|
+
boundary,
|
|
119
|
+
version,
|
|
120
|
+
routerId,
|
|
121
|
+
rewriteTarget,
|
|
122
|
+
fill,
|
|
123
|
+
},
|
|
124
|
+
);
|
|
130
125
|
|
|
131
126
|
// If the lazy prefetch runtime has not loaded yet, prevent a deferred
|
|
132
127
|
// speculative request from starting after this navigation misses the
|
|
@@ -34,7 +34,7 @@ const HISTORY_ENTRY_MEMORY_SIZE = 200;
|
|
|
34
34
|
// stale=true means the data may be outdated and should be revalidated on access.
|
|
35
35
|
// navInstance is the monotonic nav-instance token (see navInstance below): it
|
|
36
36
|
// identifies the per-commit visit that owns this entry. generateHistoryKey is
|
|
37
|
-
// URL
|
|
37
|
+
// the URL (plus the source page for an intercept), so A->B->A reuses the key; the token lets a late async
|
|
38
38
|
// resolution tell its own visit's entry apart from a newer same-URL visit's, so
|
|
39
39
|
// a stale nav can never clobber a fresher one.
|
|
40
40
|
// handlesPending=true means the entry's handle data is INCOMPLETE (a deferred
|
|
@@ -124,36 +124,35 @@ function getCacheInvalidationChannel(): BroadcastChannel | null {
|
|
|
124
124
|
* Options for generating a history key
|
|
125
125
|
*/
|
|
126
126
|
export interface HistoryKeyOptions {
|
|
127
|
-
/**
|
|
127
|
+
/** Key an intercept entry (apart from the plain entry for the URL). */
|
|
128
128
|
intercept?: boolean;
|
|
129
|
+
/** The page the intercept was opened over (history.state.sourceUrl). */
|
|
130
|
+
interceptSource?: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function urlKey(url: string): string {
|
|
134
|
+
const parsed = new URL(url, "http://localhost");
|
|
135
|
+
return parsed.pathname + parsed.search;
|
|
129
136
|
}
|
|
130
137
|
|
|
131
138
|
/**
|
|
132
|
-
* Generate a cache key from a URL.
|
|
133
|
-
*
|
|
134
|
-
* Hash fragments (#) are excluded since they don't affect server data.
|
|
139
|
+
* Generate a cache key from a URL: pathname + search. Hash fragments (#) are
|
|
140
|
+
* excluded since they don't affect server data.
|
|
135
141
|
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
142
|
+
* An intercept entry is keyed `<url>:intercept:<source>`: its cached tree
|
|
143
|
+
* includes the page under the intercept. hasInterceptCache relies on the
|
|
144
|
+
* `:intercept:` separator.
|
|
138
145
|
*/
|
|
139
146
|
export function generateHistoryKey(
|
|
140
147
|
url?: string,
|
|
141
148
|
options?: HistoryKeyOptions,
|
|
142
149
|
): string {
|
|
143
|
-
|
|
144
|
-
url
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
let key = parsed.pathname + parsed.search;
|
|
150
|
-
|
|
151
|
-
// Append intercept suffix for separate caching
|
|
152
|
-
if (options?.intercept) {
|
|
153
|
-
key += ":intercept";
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return key;
|
|
150
|
+
const key = urlKey(
|
|
151
|
+
url || (typeof window !== "undefined" ? window.location.href : "/"),
|
|
152
|
+
);
|
|
153
|
+
if (!options?.intercept) return key;
|
|
154
|
+
const source = options.interceptSource;
|
|
155
|
+
return `${key}:intercept:${source ? urlKey(source) : ""}`;
|
|
157
156
|
}
|
|
158
157
|
|
|
159
158
|
/**
|
|
@@ -258,7 +257,8 @@ export function createNavigationStore(
|
|
|
258
257
|
|
|
259
258
|
// Monotonic nav-instance token. Bumped each time a cache entry is created or
|
|
260
259
|
// replaced in cacheSegmentsForHistory (i.e. once per commit). Because
|
|
261
|
-
// generateHistoryKey is URL
|
|
260
|
+
// generateHistoryKey is the URL (plus the source page for an intercept), two
|
|
261
|
+
// visits to the same URL share a key; this
|
|
262
262
|
// token gives each visit a distinct identity so a late async handle resolution
|
|
263
263
|
// can tell whether it still owns the live page / the target cache entry, and
|
|
264
264
|
// never overwrite a newer same-URL visit's state.
|
|
@@ -580,11 +580,9 @@ export function createNavigationStore(
|
|
|
580
580
|
};
|
|
581
581
|
},
|
|
582
582
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
hasHistoryCache(historyKey: string): boolean {
|
|
587
|
-
return historyCache.some(([key]) => key === historyKey);
|
|
583
|
+
hasInterceptCache(historyKey: string): boolean {
|
|
584
|
+
const prefix = `${historyKey}:intercept:`;
|
|
585
|
+
return historyCache.some(([key]) => key.startsWith(prefix));
|
|
588
586
|
},
|
|
589
587
|
|
|
590
588
|
/**
|
|
@@ -180,7 +180,10 @@ export function createNavigationTransaction(
|
|
|
180
180
|
|
|
181
181
|
const parsedUrl = new URL(url, window.location.origin);
|
|
182
182
|
|
|
183
|
-
const historyKey = generateHistoryKey(url, {
|
|
183
|
+
const historyKey = generateHistoryKey(url, {
|
|
184
|
+
intercept,
|
|
185
|
+
interceptSource: interceptSourceUrl,
|
|
186
|
+
});
|
|
184
187
|
|
|
185
188
|
if (cacheOnly) {
|
|
186
189
|
const currentHandleData = eventController.getHandleState().data;
|
|
@@ -28,6 +28,9 @@ export function toNetworkError(
|
|
|
28
28
|
* Render an error into the segment tree via the onUpdate subscriber so the
|
|
29
29
|
* nearest error boundary catches it. Wrapped in startTransition; RenderErrorThrower
|
|
30
30
|
* throws during render (async rejections do not reach boundaries on their own).
|
|
31
|
+
* Flagged inTransition like every tree handed over in a transition, so route
|
|
32
|
+
* state that arrives after it (the failed navigation's idle) waits for its
|
|
33
|
+
* commit (NavigationProvider's "anchor").
|
|
31
34
|
*/
|
|
32
35
|
function emitErrorToBoundary(
|
|
33
36
|
onUpdate: UpdateSubscriber,
|
|
@@ -43,6 +46,7 @@ function emitErrorToBoundary(
|
|
|
43
46
|
segments: [],
|
|
44
47
|
isError: true,
|
|
45
48
|
},
|
|
49
|
+
inTransition: true,
|
|
46
50
|
});
|
|
47
51
|
});
|
|
48
52
|
}
|
|
@@ -14,7 +14,7 @@ export const addTransitionType: ((type: string) => void) | undefined =
|
|
|
14
14
|
"addTransitionType" in React ? (React as any).addTransitionType : undefined;
|
|
15
15
|
|
|
16
16
|
import type { RenderSegmentsOptions } from "../segment-system.js";
|
|
17
|
-
import { reconcileSegments } from "./segment-reconciler.js";
|
|
17
|
+
import { idsAboveBoundary, reconcileSegments } from "./segment-reconciler.js";
|
|
18
18
|
import type { ReconcileActor } from "./segment-reconciler.js";
|
|
19
19
|
import { Defer } from "./defer.js";
|
|
20
20
|
import {
|
|
@@ -236,15 +236,28 @@ export type UpdateMode =
|
|
|
236
236
|
* transition({ when }) sees kind "revalidate", not "replace".
|
|
237
237
|
*/
|
|
238
238
|
refresh?: boolean;
|
|
239
|
+
/** The cross-tab refresh's held boundary (see `action`). */
|
|
240
|
+
boundary?: string;
|
|
241
|
+
}
|
|
242
|
+
| { type: "leave-intercept"; interceptSourceUrl?: string; boundary?: never }
|
|
243
|
+
| {
|
|
244
|
+
type: "stale-revalidation";
|
|
245
|
+
interceptSourceUrl?: string;
|
|
246
|
+
/** Back/Forward's held boundary (see `action`). */
|
|
247
|
+
boundary?: string;
|
|
239
248
|
}
|
|
240
|
-
| { type: "leave-intercept"; interceptSourceUrl?: string }
|
|
241
|
-
| { type: "stale-revalidation"; interceptSourceUrl?: string }
|
|
242
249
|
| {
|
|
243
250
|
type: "action";
|
|
244
251
|
interceptSourceUrl?: string;
|
|
245
252
|
actionId?: string;
|
|
246
253
|
/** The action's FormData and result, for transition({ when }) `action`. */
|
|
247
254
|
action?: TransitionWhenActionInput;
|
|
255
|
+
/**
|
|
256
|
+
* An action's error boundary the page shows (actionBoundaryAt): the
|
|
257
|
+
* server renders above it, its cached segment stays, and the page
|
|
258
|
+
* holds only the ids above it.
|
|
259
|
+
*/
|
|
260
|
+
boundary?: string;
|
|
248
261
|
};
|
|
249
262
|
|
|
250
263
|
/**
|
|
@@ -569,6 +582,7 @@ export function createPartialUpdater(
|
|
|
569
582
|
const historyKeyAtStart = store.getHistoryKey();
|
|
570
583
|
|
|
571
584
|
const interceptSourceUrl = mode.interceptSourceUrl;
|
|
585
|
+
const boundary = mode.boundary;
|
|
572
586
|
|
|
573
587
|
let segments: string[];
|
|
574
588
|
if (mode.type === "leave-intercept") {
|
|
@@ -690,6 +704,7 @@ export function createPartialUpdater(
|
|
|
690
704
|
: store.getHistoryEntryMemory()?.rewriteTarget,
|
|
691
705
|
staleRevalidation:
|
|
692
706
|
mode.type === "stale-revalidation" || segments.length === 0,
|
|
707
|
+
boundary,
|
|
693
708
|
version: getVersion(),
|
|
694
709
|
routerId: store.getRouterId?.(),
|
|
695
710
|
});
|
|
@@ -874,14 +889,14 @@ export function createPartialUpdater(
|
|
|
874
889
|
// in place of each missing value, and this navigation fills them from
|
|
875
890
|
// one fill request once it has committed the tree.
|
|
876
891
|
let defers: Holes["defers"] | undefined;
|
|
877
|
-
let heldIds = matchedIds;
|
|
892
|
+
let heldIds = idsAboveBoundary(matchedIds, boundary);
|
|
878
893
|
const placeholders = serverSegments.filter(isPlaceholder);
|
|
879
894
|
if (placeholders.length > 0) {
|
|
880
895
|
defers = armHoles(placeholders);
|
|
881
896
|
// A placeholder is not held: every later request from this page
|
|
882
897
|
// leaves its id out, so the server renders it.
|
|
883
898
|
const deferredIds = new Set(placeholders.map((s) => s.id));
|
|
884
|
-
heldIds =
|
|
899
|
+
heldIds = heldIds.filter((id) => !deferredIds.has(id));
|
|
885
900
|
}
|
|
886
901
|
|
|
887
902
|
const reconcile = () =>
|
|
@@ -895,7 +910,10 @@ export function createPartialUpdater(
|
|
|
895
910
|
});
|
|
896
911
|
let reconciled = reconcile();
|
|
897
912
|
|
|
898
|
-
|
|
913
|
+
// Not the ids at or below a held boundary: its loaders may never have
|
|
914
|
+
// reached the client (the route failed on its GET), and nothing renders
|
|
915
|
+
// them under the error.
|
|
916
|
+
const missing = missingIds(heldIds, reconciled.segments);
|
|
899
917
|
|
|
900
918
|
if (missing.length > 0) {
|
|
901
919
|
const missingCount = missing.length;
|
|
@@ -1119,6 +1137,15 @@ export function createPartialUpdater(
|
|
|
1119
1137
|
}),
|
|
1120
1138
|
...(serverLocationState && { serverState: serverLocationState }),
|
|
1121
1139
|
};
|
|
1140
|
+
// A refetch that held an action's boundary commits only while the page
|
|
1141
|
+
// on screen still shows it: a later commit there (an action's success)
|
|
1142
|
+
// replaced it, and this response would put the error back.
|
|
1143
|
+
if (
|
|
1144
|
+
boundary &&
|
|
1145
|
+
!entryOnScreen()?.includes(cachedSegs.find((s) => s.id === boundary)!)
|
|
1146
|
+
) {
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1122
1149
|
const cachedHandleData = readCachedHandleData?.();
|
|
1123
1150
|
const { scroll: navScroll } = tx.commit(
|
|
1124
1151
|
heldIds,
|
|
@@ -43,6 +43,7 @@ import { abortAllPrefetches } from "./loader.js";
|
|
|
43
43
|
import { notifyPrefetchCacheInvalidated } from "./invalidation.js";
|
|
44
44
|
import { invalidateRangoState } from "../rango-state.js";
|
|
45
45
|
import type { RscPayload } from "../types.js";
|
|
46
|
+
import { WIRE } from "../../wire-params.js";
|
|
46
47
|
|
|
47
48
|
/**
|
|
48
49
|
* A prefetch that has been fetched AND eagerly decoded. Storing the decoded
|
|
@@ -190,7 +191,7 @@ let generation = 0;
|
|
|
190
191
|
*/
|
|
191
192
|
export function buildPrefetchKey(prefix: string, targetUrl: URL): string {
|
|
192
193
|
const normalizedTarget = new URL(targetUrl);
|
|
193
|
-
normalizedTarget.searchParams.delete(
|
|
194
|
+
normalizedTarget.searchParams.delete(WIRE.segments);
|
|
194
195
|
return prefix + "\0" + normalizedTarget.pathname + normalizedTarget.search;
|
|
195
196
|
}
|
|
196
197
|
|