@rangojs/router 0.17.0 → 0.19.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/README.md +3 -2
- package/dist/types/browser/cookie-name.d.ts +6 -0
- package/dist/types/browser/event-controller.d.ts +3 -3
- package/dist/types/browser/history-state.d.ts +18 -0
- package/dist/types/browser/navigation-transaction.d.ts +20 -2
- package/dist/types/browser/partial-update.d.ts +16 -1
- package/dist/types/browser/prefetch/cache.d.ts +6 -6
- package/dist/types/browser/react/Link.d.ts +19 -9
- package/dist/types/browser/react/context.d.ts +2 -2
- package/dist/types/browser/react/location-state-shared.d.ts +95 -56
- package/dist/types/browser/react/location-state.d.ts +1 -1
- package/dist/types/browser/transition-when.d.ts +80 -0
- package/dist/types/browser/types.d.ts +104 -9
- package/dist/types/build/generate-manifest.d.ts +8 -0
- package/dist/types/cache/background-task.d.ts +24 -2
- package/dist/types/cache/cache-exec-scope.d.ts +31 -0
- package/dist/types/cache/cache-key-utils.d.ts +48 -2
- package/dist/types/cache/cache-policy.d.ts +18 -1
- package/dist/types/cache/cache-runtime.d.ts +7 -0
- package/dist/types/cache/cache-scope.d.ts +192 -26
- package/dist/types/cache/cache-tag.d.ts +116 -11
- package/dist/types/cache/cf/cf-base64.d.ts +6 -0
- package/dist/types/cache/cf/cf-cache-constants.d.ts +11 -0
- package/dist/types/cache/cf/cf-cache-store.d.ts +224 -72
- package/dist/types/cache/cf/cf-cache-types.d.ts +31 -4
- package/dist/types/cache/cf/cf-shell-frame.d.ts +95 -0
- package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +19 -0
- package/dist/types/cache/document-cache.d.ts +10 -0
- package/dist/types/cache/handle-snapshot.d.ts +41 -8
- package/dist/types/cache/index.d.ts +1 -0
- package/dist/types/cache/invalidation-order.d.ts +23 -0
- package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
- package/dist/types/cache/request-tag-mask.d.ts +54 -0
- package/dist/types/cache/shell-memo.d.ts +178 -0
- package/dist/types/cache/shell-snapshot.d.ts +141 -105
- package/dist/types/cache/tag-invalidation.d.ts +54 -3
- package/dist/types/cache/types.d.ts +267 -96
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +123 -18
- package/dist/types/client-urls/navigation.d.ts +28 -2
- package/dist/types/client-urls/server-projection.d.ts +5 -2
- package/dist/types/client-urls/types.d.ts +5 -5
- package/dist/types/client.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.rsc.d.ts +1 -1
- package/dist/types/prerender/build-shell-capture.d.ts +11 -4
- package/dist/types/route-definition/helpers-types.d.ts +11 -11
- package/dist/types/route-name.d.ts +7 -0
- package/dist/types/router/error-handling.d.ts +1 -1
- package/dist/types/router/instrument.d.ts +8 -0
- package/dist/types/router/intercept-resolution.d.ts +2 -0
- package/dist/types/router/match-context.d.ts +5 -0
- package/dist/types/router/match-middleware/background-revalidation.d.ts +0 -4
- package/dist/types/router/middleware-types.d.ts +6 -1
- package/dist/types/router/router-options.d.ts +4 -1
- package/dist/types/router/segment-resolution/loader-cache.d.ts +58 -12
- package/dist/types/router/segment-resolution/loader-mask.d.ts +3 -4
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +33 -48
- package/dist/types/router/segment-resolution/mask-nested.d.ts +73 -24
- package/dist/types/router/segment-resolution/view-transition-default.d.ts +7 -5
- package/dist/types/router/transition-when.d.ts +24 -10
- package/dist/types/rsc/attach-transition-when.d.ts +18 -0
- package/dist/types/rsc/full-payload.d.ts +21 -0
- package/dist/types/rsc/handler.d.ts +1 -1
- package/dist/types/rsc/response-cache-serve.d.ts +15 -12
- package/dist/types/rsc/shell-build-manifest.d.ts +12 -3
- package/dist/types/rsc/shell-capture-constants.d.ts +116 -12
- package/dist/types/rsc/shell-capture.d.ts +202 -74
- package/dist/types/rsc/shell-serve.d.ts +144 -50
- package/dist/types/rsc/types.d.ts +9 -0
- package/dist/types/segment-system.d.ts +7 -0
- package/dist/types/server/context.d.ts +155 -33
- package/dist/types/server/cookie-store.d.ts +33 -0
- package/dist/types/server/handle-store.d.ts +52 -1
- package/dist/types/server/request-context.d.ts +210 -63
- package/dist/types/server/request-headers.d.ts +44 -0
- package/dist/types/server.d.ts +1 -0
- package/dist/types/ssr/index.d.ts +9 -0
- package/dist/types/ssr/ssr-root.d.ts +9 -8
- package/dist/types/testing/e2e/index.d.ts +1 -1
- package/dist/types/testing/flight.entry.d.ts +7 -0
- package/dist/types/testing/index.d.ts +3 -2
- package/dist/types/testing/internal/action-id.d.ts +7 -0
- package/dist/types/testing/location-state-key.d.ts +24 -0
- package/dist/types/testing/render-route.d.ts +17 -1
- package/dist/types/testing/run-loader.d.ts +18 -3
- package/dist/types/testing/run-transition-when.d.ts +44 -69
- package/dist/types/testing/serve-shell-request.d.ts +133 -0
- package/dist/types/testing/shell-status.d.ts +44 -19
- package/dist/types/transition-when-ref.d.ts +79 -0
- package/dist/types/types/cache-types.d.ts +15 -3
- package/dist/types/types/handler-context.d.ts +14 -1
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/segments.d.ts +86 -71
- package/dist/types/urls/path-helper-types.d.ts +3 -4
- package/dist/types/urls/pattern-types.d.ts +21 -18
- package/dist/types/vite/plugins/transition-when-hoist.d.ts +45 -0
- package/dist/types/vite/router-discovery.d.ts +29 -2
- package/dist/vite/index.js +1005 -105
- package/package.json +1 -1
- package/skills/breadcrumbs/SKILL.md +17 -7
- package/skills/cache-guide/SKILL.md +175 -76
- package/skills/caching/SKILL.md +361 -33
- package/skills/catalog.json +1 -1
- package/skills/client-urls/SKILL.md +12 -7
- package/skills/cloudflare/SKILL.md +15 -0
- package/skills/comparison/references/framework-comparison.md +52 -37
- package/skills/defer-hydration/SKILL.md +50 -30
- package/skills/deployment-caching/SKILL.md +5 -2
- package/skills/document-cache/SKILL.md +32 -2
- package/skills/handler-use/SKILL.md +2 -0
- package/skills/hooks/SKILL.md +3 -1
- package/skills/hooks/handle-and-actions.md +3 -1
- package/skills/hooks/navigation.md +23 -2
- package/skills/hooks/state.md +101 -7
- package/skills/intercept/SKILL.md +29 -12
- package/skills/loader/SKILL.md +197 -44
- package/skills/middleware/SKILL.md +34 -9
- package/skills/migrate-nextjs/SKILL.md +68 -37
- package/skills/migrate-react-router/SKILL.md +2 -1
- package/skills/migrate-react-router/component-migration.md +3 -1
- package/skills/migrate-react-router/data-and-actions.md +3 -1
- package/skills/migrate-react-router/route-mapping.md +7 -4
- package/skills/observability/SKILL.md +65 -0
- package/skills/parallel/SKILL.md +13 -8
- package/skills/ppr/SKILL.md +715 -371
- package/skills/prerender/SKILL.md +30 -17
- package/skills/rango/SKILL.md +72 -34
- package/skills/route/SKILL.md +14 -7
- package/skills/router-setup/SKILL.md +4 -2
- package/skills/scripts/SKILL.md +8 -1
- package/skills/server-actions/SKILL.md +11 -0
- package/skills/shell-manifest/SKILL.md +21 -12
- package/skills/testing/SKILL.md +49 -43
- package/skills/testing/cache-prerender.md +15 -14
- package/skills/testing/client-components.md +45 -3
- package/skills/testing/loader.md +19 -1
- package/skills/testing/middleware.md +1 -0
- package/skills/testing/predicates.md +49 -49
- package/skills/testing/render-handler.md +1 -0
- package/skills/testing/serve-shell-request.md +202 -0
- package/skills/testing/server-actions.md +9 -3
- package/skills/theme/SKILL.md +56 -4
- package/skills/typesafety/env-and-bindings.md +18 -2
- package/skills/use-cache/SKILL.md +91 -23
- package/skills/vercel/SKILL.md +14 -2
- package/skills/view-transitions/SKILL.md +150 -29
- package/src/__augment-tests__/augmented.check.ts +23 -0
- package/src/browser/cookie-name.ts +23 -0
- package/src/browser/event-controller.ts +4 -4
- package/src/browser/history-state.ts +75 -1
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +77 -18
- package/src/browser/navigation-client.ts +4 -3
- package/src/browser/navigation-store.ts +43 -1
- package/src/browser/navigation-transaction.ts +31 -11
- package/src/browser/partial-update.ts +146 -35
- package/src/browser/prefetch/cache.ts +6 -6
- package/src/browser/prefetch/fetch.ts +2 -2
- package/src/browser/react/Link.tsx +38 -33
- package/src/browser/react/NavigationProvider.tsx +2 -2
- package/src/browser/react/context.ts +2 -2
- package/src/browser/react/location-state-shared.ts +241 -49
- package/src/browser/react/location-state.ts +5 -2
- package/src/browser/rsc-router.tsx +9 -0
- package/src/browser/server-action-bridge.ts +84 -15
- package/src/browser/transition-when.ts +205 -0
- package/src/browser/types.ts +124 -10
- package/src/build/generate-manifest.ts +77 -1
- package/src/cache/background-task.ts +55 -2
- package/src/cache/cache-exec-scope.ts +48 -1
- package/src/cache/cache-key-utils.ts +62 -2
- package/src/cache/cache-policy.ts +31 -4
- package/src/cache/cache-runtime.ts +209 -129
- package/src/cache/cache-scope.ts +573 -69
- package/src/cache/cache-tag.ts +475 -19
- package/src/cache/cf/cf-base64.ts +36 -4
- package/src/cache/cf/cf-cache-constants.ts +12 -0
- package/src/cache/cf/cf-cache-store.ts +1094 -376
- package/src/cache/cf/cf-cache-types.ts +32 -4
- package/src/cache/cf/cf-shell-frame.ts +258 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +55 -40
- package/src/cache/document-cache.ts +53 -7
- package/src/cache/handle-snapshot.ts +53 -9
- package/src/cache/index.ts +2 -0
- package/src/cache/invalidation-order.ts +84 -0
- package/src/cache/isolate-tag-memo.ts +308 -0
- package/src/cache/request-tag-mask.ts +147 -0
- package/src/cache/shell-memo.ts +341 -0
- package/src/cache/shell-snapshot.ts +249 -277
- package/src/cache/tag-invalidation.ts +109 -18
- package/src/cache/types.ts +274 -102
- package/src/cache/vercel/vercel-cache-store.ts +561 -74
- package/src/client-urls/client-root.tsx +8 -4
- package/src/client-urls/client-urls.ts +8 -6
- package/src/client-urls/navigation.ts +55 -4
- package/src/client-urls/server-projection.ts +31 -10
- package/src/client-urls/types.ts +12 -5
- package/src/client.tsx +2 -0
- package/src/host/cookie-handler.ts +2 -1
- package/src/index.rsc.ts +3 -0
- package/src/index.ts +3 -0
- package/src/loader-redirect.tsx +7 -3
- package/src/prerender/build-shell-capture.ts +173 -110
- package/src/route-definition/dsl-helpers.ts +6 -3
- package/src/route-definition/helpers-types.ts +11 -11
- package/src/route-name.ts +12 -0
- package/src/router/content-negotiation.ts +4 -1
- package/src/router/error-handling.ts +5 -2
- package/src/router/handler-context.ts +42 -5
- package/src/router/instrument.ts +54 -2
- package/src/router/intercept-resolution.ts +30 -8
- package/src/router/loader-resolution.ts +89 -33
- package/src/router/logging.ts +5 -3
- package/src/router/match-api.ts +89 -42
- package/src/router/match-context.ts +5 -0
- package/src/router/match-handlers.ts +14 -29
- package/src/router/match-middleware/background-revalidation.ts +3 -5
- package/src/router/match-middleware/cache-lookup.ts +52 -19
- package/src/router/match-middleware/cache-store.ts +36 -10
- package/src/router/match-result.ts +1 -0
- package/src/router/middleware-types.ts +6 -1
- package/src/router/middleware.ts +20 -4
- package/src/router/navigation-snapshot.ts +6 -5
- package/src/router/prerender-match.ts +10 -0
- package/src/router/request-classification.ts +7 -4
- package/src/router/router-options.ts +4 -1
- package/src/router/segment-resolution/fresh.ts +18 -13
- package/src/router/segment-resolution/helpers.ts +4 -2
- package/src/router/segment-resolution/loader-cache.ts +366 -70
- package/src/router/segment-resolution/loader-mask.ts +3 -4
- package/src/router/segment-resolution/loader-snapshot.ts +215 -126
- package/src/router/segment-resolution/mask-nested.ts +161 -42
- package/src/router/segment-resolution/revalidation.ts +27 -13
- package/src/router/segment-resolution/view-transition-default.ts +6 -21
- package/src/router/telemetry.ts +6 -3
- package/src/router/transition-when.ts +34 -52
- package/src/router.ts +2 -1
- package/src/rsc/attach-transition-when.ts +47 -0
- package/src/rsc/full-payload.ts +47 -3
- package/src/rsc/handler.ts +24 -10
- package/src/rsc/loader-fetch.ts +2 -1
- package/src/rsc/manifest-init.ts +4 -0
- package/src/rsc/origin-guard.ts +8 -5
- package/src/rsc/progressive-enhancement.ts +10 -32
- package/src/rsc/response-cache-serve.ts +69 -78
- package/src/rsc/response-route-handler.ts +2 -2
- package/src/rsc/rsc-rendering.ts +712 -338
- package/src/rsc/server-action.ts +8 -20
- package/src/rsc/shell-build-manifest.ts +49 -29
- package/src/rsc/shell-capture-constants.ts +182 -12
- package/src/rsc/shell-capture.ts +1126 -612
- package/src/rsc/shell-serve.ts +331 -85
- package/src/rsc/ssr-setup.ts +3 -2
- package/src/rsc/types.ts +9 -0
- package/src/segment-system.tsx +40 -7
- package/src/server/context.ts +328 -72
- package/src/server/cookie-store.ts +184 -133
- package/src/server/handle-store.ts +245 -27
- package/src/server/request-context.ts +364 -100
- package/src/server/request-headers.ts +134 -0
- package/src/server.ts +4 -0
- package/src/ssr/index.tsx +96 -37
- package/src/ssr/ssr-root.tsx +9 -8
- package/src/testing/dispatch.ts +4 -7
- package/src/testing/e2e/index.ts +1 -0
- package/src/testing/flight.entry.ts +14 -0
- package/src/testing/index.ts +5 -0
- package/src/testing/internal/action-id.ts +25 -0
- package/src/testing/internal/context.ts +1 -1
- package/src/testing/location-state-key.ts +45 -0
- package/src/testing/render-handler.ts +1 -1
- package/src/testing/render-route.tsx +109 -16
- package/src/testing/run-client-revalidate.ts +3 -19
- package/src/testing/run-loader.ts +67 -5
- package/src/testing/run-transition-when.ts +109 -172
- package/src/testing/serve-shell-request.ts +381 -0
- package/src/testing/shell-status.ts +73 -47
- package/src/theme/ThemeProvider.tsx +7 -6
- package/src/transition-when-ref.ts +220 -0
- package/src/types/cache-types.ts +15 -3
- package/src/types/handler-context.ts +14 -1
- package/src/types/index.ts +3 -0
- package/src/types/segments.ts +94 -91
- package/src/urls/path-helper-types.ts +3 -4
- package/src/urls/pattern-types.ts +21 -18
- package/src/vite/discovery/discover-routers.ts +3 -0
- package/src/vite/discovery/shell-prerender-phase.ts +9 -2
- package/src/vite/discovery/virtual-module-codegen.ts +16 -6
- package/src/vite/plugins/transition-when-hoist.ts +1064 -0
- package/src/vite/rango.ts +2 -0
- package/src/vite/router-discovery.ts +89 -9
- package/dist/types/rsc/transition-gate.d.ts +0 -23
- package/src/rsc/transition-gate.ts +0 -86
|
@@ -14,6 +14,29 @@
|
|
|
14
14
|
/** Default prefix when `stateCookiePrefix` is unset or empty after sanitization. */
|
|
15
15
|
export const DEFAULT_STATE_COOKIE_PREFIX = "rango-state";
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* The fresh-reads cookie: the state cookie PREFIX plus this suffix
|
|
19
|
+
* (`rango-state-fresh` by default). A response whose request ran
|
|
20
|
+
* updateTag()/revalidateTag() sets it (HttpOnly, SameSite=Lax, Path=/,
|
|
21
|
+
* Max-Age = the stores' longest memo staleness), and the same user's requests
|
|
22
|
+
* carrying it skip the stores' isolate memos (src/cache/isolate-tag-memo.ts).
|
|
23
|
+
* Keyed by prefix, not by router, so a mutation through one router also
|
|
24
|
+
* reaches the memos another router on the host reads (they often share a
|
|
25
|
+
* store); a router with its own `stateCookiePrefix` gets its own cookie.
|
|
26
|
+
*/
|
|
27
|
+
const FRESH_READS_COOKIE_SUFFIX = "-fresh";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The fresh-reads cookie name for a router's resolved state cookie name
|
|
31
|
+
* (`{prefix}_{routerId}`; the prefix never contains `_`, see
|
|
32
|
+
* router/state-cookie-name.ts).
|
|
33
|
+
*/
|
|
34
|
+
export function freshReadsCookieName(stateCookieName: string): string {
|
|
35
|
+
const cut = stateCookieName.indexOf("_");
|
|
36
|
+
const prefix = cut < 0 ? stateCookieName : stateCookieName.slice(0, cut);
|
|
37
|
+
return `${prefix}${FRESH_READS_COOKIE_SUFFIX}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
17
40
|
/** Internal response header carrying the keepClientCache() directive. */
|
|
18
41
|
export const KEEP_CACHE_HEADER = "x-rango-keep-cache";
|
|
19
42
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type {
|
|
3
3
|
NavigationLocation,
|
|
4
|
-
|
|
4
|
+
NavigateOptionsInternal,
|
|
5
5
|
TrackedActionState,
|
|
6
6
|
ActionLifecycleState,
|
|
7
7
|
InflightAction,
|
|
@@ -46,7 +46,7 @@ export interface NavigationEntry {
|
|
|
46
46
|
abort: AbortController;
|
|
47
47
|
phase: NavigationPhase;
|
|
48
48
|
startedAt: number;
|
|
49
|
-
options?:
|
|
49
|
+
options?: NavigateOptionsInternal & { skipLoadingState?: boolean };
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
@@ -206,7 +206,7 @@ export interface EventController {
|
|
|
206
206
|
// Navigation operations
|
|
207
207
|
startNavigation(
|
|
208
208
|
url: string,
|
|
209
|
-
options?:
|
|
209
|
+
options?: NavigateOptionsInternal & { skipLoadingState?: boolean },
|
|
210
210
|
): NavigationHandle;
|
|
211
211
|
abortNavigation(): void;
|
|
212
212
|
|
|
@@ -632,7 +632,7 @@ export function createEventController(
|
|
|
632
632
|
|
|
633
633
|
function startNavigation(
|
|
634
634
|
url: string,
|
|
635
|
-
options?:
|
|
635
|
+
options?: NavigateOptionsInternal & { skipLoadingState?: boolean },
|
|
636
636
|
): NavigationHandle {
|
|
637
637
|
// Cancel existing navigation (switchMap semantics)
|
|
638
638
|
if (currentNavigation) {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
+
isLocationStateDefinition,
|
|
2
3
|
isLocationStateEntry,
|
|
4
|
+
peekLocationStateKey,
|
|
5
|
+
replaceCurrentHistoryState,
|
|
3
6
|
resolveLocationStateEntries,
|
|
4
7
|
} from "./react/location-state-shared.js";
|
|
5
8
|
|
|
@@ -13,10 +16,49 @@ function isTypedLocationState(
|
|
|
13
16
|
return Object.keys(state).some((key) => key.startsWith("__rsc_ls_"));
|
|
14
17
|
}
|
|
15
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Dev-only guard for the state mistakes `HistoryState` rejects at compile
|
|
21
|
+
* time, for untyped callers: a typed entry without its array (its `__rsc_ls_*`
|
|
22
|
+
* fields would be spread onto history.state, so useLocationState reads
|
|
23
|
+
* undefined), an uncalled definition (pushState throws DataCloneError), and an
|
|
24
|
+
* array mixing entries with plain values (only `state[0]` picks the format, so
|
|
25
|
+
* the rest land under history.state["undefined"] or are never read).
|
|
26
|
+
*/
|
|
27
|
+
function assertNavigationState(state: unknown): void {
|
|
28
|
+
if (isLocationStateEntry(state)) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`[rango] navigation state is a single location-state entry (key "${state.__rsc_ls_key}"). ` +
|
|
31
|
+
"Wrap it in an array: { state: [MyState(value)] }. " +
|
|
32
|
+
"Without the array, useLocationState(MyState) reads undefined.",
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
const definition = (Array.isArray(state) ? state : [state]).find(
|
|
36
|
+
isLocationStateDefinition,
|
|
37
|
+
);
|
|
38
|
+
if (definition) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`[rango] navigation state contains a location-state definition (key "${peekLocationStateKey(definition) ?? "unset"}") instead of an entry. ` +
|
|
41
|
+
"Call it with the value: { state: [MyState(value)] }, not [MyState].",
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
if (Array.isArray(state) && state.some(isLocationStateEntry)) {
|
|
45
|
+
const index = state.findIndex((item) => !isLocationStateEntry(item));
|
|
46
|
+
if (index !== -1) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`[rango] navigation state mixes location-state entries with other values (index ${index}). ` +
|
|
49
|
+
"Pass only entries ({ state: [MyState(value), Other(value)] }); plain state cannot sit next to typed entries.",
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
16
55
|
/**
|
|
17
56
|
* Resolve navigation state - handles both LocationStateEntry[] and plain formats
|
|
18
57
|
*/
|
|
19
58
|
export function resolveNavigationState(state: unknown): unknown {
|
|
59
|
+
if (process.env.NODE_ENV !== "production") {
|
|
60
|
+
assertNavigationState(state);
|
|
61
|
+
}
|
|
20
62
|
if (
|
|
21
63
|
Array.isArray(state) &&
|
|
22
64
|
state.length > 0 &&
|
|
@@ -61,6 +103,15 @@ export function buildHistoryState(
|
|
|
61
103
|
return Object.keys(result).length > 0 ? result : null;
|
|
62
104
|
}
|
|
63
105
|
|
|
106
|
+
/** Check if a history state object contains location state keys. */
|
|
107
|
+
export function hasLocationState(state: unknown): boolean {
|
|
108
|
+
if (!state || typeof state !== "object") return false;
|
|
109
|
+
return (
|
|
110
|
+
"state" in state ||
|
|
111
|
+
Object.keys(state).some((k) => k.startsWith("__rsc_ls_"))
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
64
115
|
/**
|
|
65
116
|
* Stamp an `idx` on the next history entry's state and call push/replaceState.
|
|
66
117
|
* Push increments the current idx; replace keeps it. Initial entry idx is 0.
|
|
@@ -82,6 +133,29 @@ export function pushHistoryWithIdx(
|
|
|
82
133
|
}
|
|
83
134
|
}
|
|
84
135
|
|
|
136
|
+
/**
|
|
137
|
+
* The forced-MISS reload marker (`SHELL_MISS_PARAM` in rsc/shell-serve.ts,
|
|
138
|
+
* which the browser bundle must not import): a degraded PPR shell HIT
|
|
139
|
+
* reloads the page with it, and the server renders a marked request like a
|
|
140
|
+
* cache miss.
|
|
141
|
+
*/
|
|
142
|
+
export const SHELL_MISS_MARKER: string = "_rsc_shell";
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Drop the forced-MISS marker from the address bar at boot, keeping the
|
|
146
|
+
* history entry's state (initBrowserApp calls it before any location read).
|
|
147
|
+
* Left in place, a refresh or a shared link would stay off the shell and the
|
|
148
|
+
* app's search params would carry it. The server strips the same marker from
|
|
149
|
+
* the request before rendering (shell-serve.ts withoutShellMissMarker), so
|
|
150
|
+
* the page was rendered for the clean URL and the hydrating client agrees.
|
|
151
|
+
*/
|
|
152
|
+
export function stripShellMissMarker(): void {
|
|
153
|
+
const url = new URL(window.location.href);
|
|
154
|
+
if (!url.searchParams.has(SHELL_MISS_MARKER)) return;
|
|
155
|
+
url.searchParams.delete(SHELL_MISS_MARKER);
|
|
156
|
+
window.history.replaceState(window.history.state, "", url.href);
|
|
157
|
+
}
|
|
158
|
+
|
|
85
159
|
/**
|
|
86
160
|
* Merge server-set location state into the current history entry.
|
|
87
161
|
* Replaces the current history state and dispatches notification event
|
|
@@ -94,7 +168,7 @@ export function mergeLocationState(
|
|
|
94
168
|
...window.history.state,
|
|
95
169
|
...locationState,
|
|
96
170
|
};
|
|
97
|
-
|
|
171
|
+
replaceCurrentHistoryState(merged);
|
|
98
172
|
if (Object.keys(locationState).some((k) => k.startsWith("__rsc_ls_"))) {
|
|
99
173
|
window.dispatchEvent(new Event("__rsc_locationstate"));
|
|
100
174
|
}
|
|
@@ -334,6 +334,7 @@ export function setupLinkInterception(
|
|
|
334
334
|
const scrollAttr = link.getAttribute("data-scroll");
|
|
335
335
|
const replaceAttr = link.getAttribute("data-replace");
|
|
336
336
|
const revalidateAttr = link.getAttribute("data-revalidate");
|
|
337
|
+
const transitionAttr = link.getAttribute("data-transition");
|
|
337
338
|
|
|
338
339
|
const navigateOptions: NavigateOptions = {};
|
|
339
340
|
if (scrollAttr === "false") {
|
|
@@ -345,6 +346,9 @@ export function setupLinkInterception(
|
|
|
345
346
|
if (revalidateAttr === "false") {
|
|
346
347
|
navigateOptions.revalidate = false;
|
|
347
348
|
}
|
|
349
|
+
if (transitionAttr === "false") {
|
|
350
|
+
navigateOptions.transition = false;
|
|
351
|
+
}
|
|
348
352
|
|
|
349
353
|
onNavigate(href, navigateOptions);
|
|
350
354
|
};
|
|
@@ -14,7 +14,11 @@ import {
|
|
|
14
14
|
createNavigationTransaction,
|
|
15
15
|
resolveNavigationState,
|
|
16
16
|
} from "./navigation-transaction.js";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
buildHistoryState,
|
|
19
|
+
hasLocationState,
|
|
20
|
+
pushHistoryWithIdx,
|
|
21
|
+
} from "./history-state.js";
|
|
18
22
|
import {
|
|
19
23
|
handleNavigationStart,
|
|
20
24
|
handleNavigationEnd,
|
|
@@ -30,7 +34,11 @@ import {
|
|
|
30
34
|
setupDelegatedLinkPrefetch,
|
|
31
35
|
setupLinkInterception,
|
|
32
36
|
} from "./link-interceptor.js";
|
|
33
|
-
import {
|
|
37
|
+
import {
|
|
38
|
+
createPartialUpdater,
|
|
39
|
+
shouldStartViewTransition,
|
|
40
|
+
} from "./partial-update.js";
|
|
41
|
+
import { decideCommitGatedOff, mergeSegmentParams } from "./transition-when.js";
|
|
34
42
|
import { generateHistoryKey } from "./navigation-store.js";
|
|
35
43
|
import type { EventController } from "./event-controller.js";
|
|
36
44
|
import { isInterceptOnlyCache } from "./intercept-utils.js";
|
|
@@ -214,6 +222,7 @@ export function createNavigationBridge(
|
|
|
214
222
|
|
|
215
223
|
// Snapshot old state before pushState/replaceState overwrites it
|
|
216
224
|
const oldState = window.history.state;
|
|
225
|
+
const shallowRouteName = store.getHistoryEntryMemory()?.routeName;
|
|
217
226
|
|
|
218
227
|
// Update browser URL (carry intercept context into history state)
|
|
219
228
|
const historyState = buildHistoryState(
|
|
@@ -228,18 +237,11 @@ export function createNavigationBridge(
|
|
|
228
237
|
|
|
229
238
|
// Ensure new history entry has a scroll restoration key
|
|
230
239
|
ensureHistoryKey();
|
|
240
|
+
// Same route, new entry (transition({ when }) back/forward source).
|
|
241
|
+
store.rememberDisplayedEntry(shallowRouteName);
|
|
231
242
|
|
|
232
243
|
// Notify useLocationState() hooks when state changes
|
|
233
|
-
|
|
234
|
-
oldState &&
|
|
235
|
-
typeof oldState === "object" &&
|
|
236
|
-
("state" in oldState ||
|
|
237
|
-
Object.keys(oldState).some((k) => k.startsWith("__rsc_ls_")));
|
|
238
|
-
const hasNewState =
|
|
239
|
-
historyState &&
|
|
240
|
-
("state" in historyState ||
|
|
241
|
-
Object.keys(historyState).some((k) => k.startsWith("__rsc_ls_")));
|
|
242
|
-
if (hasOldState || hasNewState) {
|
|
244
|
+
if (hasLocationState(oldState) || hasLocationState(historyState)) {
|
|
243
245
|
window.dispatchEvent(new Event("__rsc_locationstate"));
|
|
244
246
|
}
|
|
245
247
|
|
|
@@ -344,9 +346,44 @@ export function createNavigationBridge(
|
|
|
344
346
|
state: resolvedState,
|
|
345
347
|
skipLoadingState: false,
|
|
346
348
|
});
|
|
349
|
+
// A cross-route clientUrls() navigation presents its destination
|
|
350
|
+
// optimistically at once: transition({ when }) decides THERE (the first
|
|
351
|
+
// presentation), over the destination route's predicate and those of
|
|
352
|
+
// the segments the navigation keeps (everything outside the group's
|
|
353
|
+
// route segment), and the canonical commit reuses the decision.
|
|
354
|
+
// `transition: false` gates the navigation off without calling any
|
|
355
|
+
// predicate, on both the optimistic swap and the canonical commit.
|
|
356
|
+
const transitionOptOut = options?.transition === false;
|
|
347
357
|
const clientUrlPresentation = beginClientUrlNavigation(
|
|
348
358
|
targetUrl,
|
|
349
359
|
tx.handle.signal,
|
|
360
|
+
transitionOptOut
|
|
361
|
+
? () => true
|
|
362
|
+
: (destination) =>
|
|
363
|
+
decideCommitGatedOff(
|
|
364
|
+
store,
|
|
365
|
+
(
|
|
366
|
+
store.getCachedSegments(store.getHistoryKey())?.segments ?? []
|
|
367
|
+
).filter((segment) => segment.clientGroup === undefined),
|
|
368
|
+
options?.replace ? "replace" : "push",
|
|
369
|
+
{
|
|
370
|
+
to: (from) => {
|
|
371
|
+
// Mount (include prefix) params carry over; the origin
|
|
372
|
+
// route's own params do not.
|
|
373
|
+
const params = { ...from.params };
|
|
374
|
+
for (const name of destination.originLocalParamNames) {
|
|
375
|
+
delete params[name];
|
|
376
|
+
}
|
|
377
|
+
return {
|
|
378
|
+
url: targetUrl,
|
|
379
|
+
params: { ...params, ...destination.params },
|
|
380
|
+
routeName: destination.routeName,
|
|
381
|
+
state: buildHistoryState(resolvedState),
|
|
382
|
+
};
|
|
383
|
+
},
|
|
384
|
+
extra: destination.when,
|
|
385
|
+
},
|
|
386
|
+
),
|
|
350
387
|
);
|
|
351
388
|
|
|
352
389
|
// REVALIDATE: Fetch fresh data from server
|
|
@@ -365,6 +402,9 @@ export function createNavigationBridge(
|
|
|
365
402
|
replace: options?.replace,
|
|
366
403
|
scroll: options?.scroll,
|
|
367
404
|
state: resolvedState,
|
|
405
|
+
transitionGatedOff: transitionOptOut
|
|
406
|
+
? true
|
|
407
|
+
: clientUrlPresentation?.transitionGatedOff,
|
|
368
408
|
}),
|
|
369
409
|
isLeavingIntercept
|
|
370
410
|
? { type: "leave-intercept" as const }
|
|
@@ -394,6 +434,7 @@ export function createNavigationBridge(
|
|
|
394
434
|
return this.navigate(redirectUrl, {
|
|
395
435
|
state: error.state,
|
|
396
436
|
replace: options?.replace,
|
|
437
|
+
transition: options?.transition,
|
|
397
438
|
_skipCache: true,
|
|
398
439
|
} as NavigateOptionsInternal);
|
|
399
440
|
}
|
|
@@ -457,6 +498,7 @@ export function createNavigationBridge(
|
|
|
457
498
|
false,
|
|
458
499
|
tx.handle.signal,
|
|
459
500
|
tx.with({ url: window.location.href, replace: true, scroll: false }),
|
|
501
|
+
{ type: "navigate", refresh: true },
|
|
460
502
|
);
|
|
461
503
|
} catch (error) {
|
|
462
504
|
// Aborted or superseded: bail without rendering a boundary (see navigate()).
|
|
@@ -567,6 +609,26 @@ export function createNavigationBridge(
|
|
|
567
609
|
const isStale = (cached?.stale ?? false) || isActionFenceActive();
|
|
568
610
|
|
|
569
611
|
if (cachedSegments && cachedSegments.length > 0) {
|
|
612
|
+
// transition({ when }) decides the restore (kind "pop") against the
|
|
613
|
+
// entry being left, so before the store moves to the restored entry.
|
|
614
|
+
// The decision the entry was committed with is not reused: it was
|
|
615
|
+
// made for a different source. A restore that opens or closes an
|
|
616
|
+
// intercept never holds or animates (hasTransition below), so it is
|
|
617
|
+
// not decided, like every intercept commit.
|
|
618
|
+
const cachedParams = mergeSegmentParams(cachedSegments);
|
|
619
|
+
const gatedOff =
|
|
620
|
+
!isIntercept &&
|
|
621
|
+
!isLeavingIntercept &&
|
|
622
|
+
decideCommitGatedOff(store, cachedSegments, "pop", {
|
|
623
|
+
to: () => ({
|
|
624
|
+
url,
|
|
625
|
+
params: cachedParams,
|
|
626
|
+
routeName: store.getHistoryEntryMemory(historyState?.key)
|
|
627
|
+
?.routeName,
|
|
628
|
+
state: historyState,
|
|
629
|
+
}),
|
|
630
|
+
});
|
|
631
|
+
|
|
570
632
|
// Update store to point to this history entry
|
|
571
633
|
store.setHistoryKey(historyKey);
|
|
572
634
|
store.setSegmentIds(cachedSegments.map((s) => s.id));
|
|
@@ -583,14 +645,11 @@ export function createNavigationBridge(
|
|
|
583
645
|
try {
|
|
584
646
|
const root = await renderSegments(cachedSegments, {
|
|
585
647
|
forceAwait: true,
|
|
648
|
+
transitionGatedOff: gatedOff,
|
|
586
649
|
});
|
|
587
|
-
|
|
650
|
+
store.rememberDisplayedEntry();
|
|
588
651
|
// Set params on event controller before onUpdate so both location
|
|
589
652
|
// and params are current when the debounced notify() fires.
|
|
590
|
-
const cachedParams: Record<string, string> = {};
|
|
591
|
-
for (const s of cachedSegments) {
|
|
592
|
-
if (s.params) Object.assign(cachedParams, s.params);
|
|
593
|
-
}
|
|
594
653
|
eventController.setParams(cachedParams);
|
|
595
654
|
|
|
596
655
|
const popstateUpdate = {
|
|
@@ -613,7 +672,7 @@ export function createNavigationBridge(
|
|
|
613
672
|
const hasTransition =
|
|
614
673
|
!isIntercept &&
|
|
615
674
|
!isLeavingIntercept &&
|
|
616
|
-
cachedSegments
|
|
675
|
+
shouldStartViewTransition(cachedSegments, gatedOff);
|
|
617
676
|
if (hasTransition) {
|
|
618
677
|
startTransition(() => {
|
|
619
678
|
if (addTransitionType) {
|
|
@@ -122,9 +122,10 @@ export function createNavigationClient(
|
|
|
122
122
|
|
|
123
123
|
// Check completed in-memory prefetch cache before making a network
|
|
124
124
|
// request. Try the source-scoped key first (populated when the server
|
|
125
|
-
// tagged the prefetch response as source-sensitive, e.g.
|
|
126
|
-
// or when a Link opted in with
|
|
127
|
-
// back to the wildcard slot shared
|
|
125
|
+
// tagged the prefetch response as source-sensitive, e.g. a route an
|
|
126
|
+
// intercept targets, or when a Link opted in with
|
|
127
|
+
// `prefetchKey=":source"`), then fall back to the wildcard slot shared
|
|
128
|
+
// across source pages.
|
|
128
129
|
// Both keys embed the Rango state, so state rotation (deploy or
|
|
129
130
|
// server-action invalidation) auto-invalidates both scopes.
|
|
130
131
|
// Skip cache for stale revalidation (needs fresh data), HMR (needs
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
HistoryEntryMemory,
|
|
2
3
|
NavigationLocation,
|
|
3
4
|
SegmentState,
|
|
4
5
|
NavigationStore,
|
|
@@ -13,10 +14,18 @@ import {
|
|
|
13
14
|
getRangoState,
|
|
14
15
|
getRangoStateCookieName,
|
|
15
16
|
} from "./rango-state.js";
|
|
17
|
+
import { getHistoryStateKey } from "./scroll-restoration.js";
|
|
18
|
+
import { setCurrentHistoryStateListener } from "./react/location-state-shared.js";
|
|
16
19
|
|
|
17
20
|
// Maximum number of history entries to cache (URLs visited)
|
|
18
21
|
const HISTORY_CACHE_SIZE = 20;
|
|
19
22
|
|
|
23
|
+
// Bound on remembered history entries (transition({ when }) sources), matching
|
|
24
|
+
// scroll-restoration's MAX_SCROLL_ENTRIES: deep enough for any realistic
|
|
25
|
+
// back/forward run, bounded for long sessions. An evicted entry reads as
|
|
26
|
+
// unknown (undefined route name and state), never guessed.
|
|
27
|
+
const HISTORY_ENTRY_MEMORY_SIZE = 200;
|
|
28
|
+
|
|
20
29
|
// Cache entry:
|
|
21
30
|
// [url-key, segments, stale, handleData?, routerId?, navInstance?, handlesPending?]
|
|
22
31
|
// stale=true means the data may be outdated and should be revalidated on access.
|
|
@@ -233,6 +242,11 @@ export function createNavigationStore(
|
|
|
233
242
|
// Current history key (set on navigation, stored in history.state)
|
|
234
243
|
let currentHistoryKey = config?.initialHistoryKey || generateHistoryKey();
|
|
235
244
|
|
|
245
|
+
// Per-history-entry memory (rememberDisplayedEntry), keyed by the entry's
|
|
246
|
+
// history.state.key — unique per entry, unlike the URL-derived history key.
|
|
247
|
+
const entryMemory = new Map<string, HistoryEntryMemory>();
|
|
248
|
+
let displayedEntryKey: string | undefined;
|
|
249
|
+
|
|
236
250
|
// Store initial segments if provided (not stale)
|
|
237
251
|
if (config?.initialHistoryKey && config?.initialSegments) {
|
|
238
252
|
historyCache.push([
|
|
@@ -375,7 +389,7 @@ export function createNavigationStore(
|
|
|
375
389
|
}
|
|
376
390
|
}
|
|
377
391
|
|
|
378
|
-
|
|
392
|
+
const store: NavigationStore = {
|
|
379
393
|
// ========================================================================
|
|
380
394
|
// Internal Segment State (for bridges)
|
|
381
395
|
// ========================================================================
|
|
@@ -426,6 +440,27 @@ export function createNavigationStore(
|
|
|
426
440
|
currentHistoryKey = key;
|
|
427
441
|
},
|
|
428
442
|
|
|
443
|
+
rememberDisplayedEntry(routeName?: string): void {
|
|
444
|
+
if (typeof window === "undefined") return;
|
|
445
|
+
const key = getHistoryStateKey();
|
|
446
|
+
const previous = entryMemory.get(key);
|
|
447
|
+
entryMemory.delete(key);
|
|
448
|
+
entryMemory.set(key, {
|
|
449
|
+
routeName: routeName ?? previous?.routeName,
|
|
450
|
+
state: window.history.state,
|
|
451
|
+
});
|
|
452
|
+
if (entryMemory.size > HISTORY_ENTRY_MEMORY_SIZE) {
|
|
453
|
+
const oldest = entryMemory.keys().next().value;
|
|
454
|
+
if (oldest !== undefined) entryMemory.delete(oldest);
|
|
455
|
+
}
|
|
456
|
+
displayedEntryKey = key;
|
|
457
|
+
},
|
|
458
|
+
|
|
459
|
+
getHistoryEntryMemory(entryKey?: string): HistoryEntryMemory | undefined {
|
|
460
|
+
const key = entryKey ?? displayedEntryKey;
|
|
461
|
+
return key === undefined ? undefined : entryMemory.get(key);
|
|
462
|
+
},
|
|
463
|
+
|
|
429
464
|
/**
|
|
430
465
|
* Current nav-instance token: the instance of the most recently committed
|
|
431
466
|
* navigation (the value last written by cacheSegmentsForHistory). A late
|
|
@@ -682,4 +717,11 @@ export function createNavigationStore(
|
|
|
682
717
|
});
|
|
683
718
|
},
|
|
684
719
|
};
|
|
720
|
+
|
|
721
|
+
// Writes to the displayed entry after its commit (Def.write()/delete(), a
|
|
722
|
+
// flash read, merged server state) refresh its memory.
|
|
723
|
+
if (typeof window !== "undefined") {
|
|
724
|
+
setCurrentHistoryStateListener(() => store.rememberDisplayedEntry());
|
|
725
|
+
}
|
|
726
|
+
return store;
|
|
685
727
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
2
|
+
NavigateOptionsInternal,
|
|
3
3
|
NavigationStore,
|
|
4
4
|
ResolvedSegment,
|
|
5
5
|
StreamingToken,
|
|
@@ -13,21 +13,13 @@ import type { EventController, NavigationHandle } from "./event-controller.js";
|
|
|
13
13
|
import { debugLog } from "./logging.js";
|
|
14
14
|
import {
|
|
15
15
|
buildHistoryState,
|
|
16
|
+
hasLocationState,
|
|
16
17
|
mergeLocationState,
|
|
17
18
|
pushHistoryWithIdx,
|
|
18
19
|
} from "./history-state.js";
|
|
19
20
|
|
|
20
21
|
export { resolveNavigationState } from "./history-state.js";
|
|
21
22
|
|
|
22
|
-
/** Check if a history state object contains location state keys. */
|
|
23
|
-
function hasLocationState(state: unknown): boolean {
|
|
24
|
-
if (!state || typeof state !== "object") return false;
|
|
25
|
-
return (
|
|
26
|
-
"state" in state ||
|
|
27
|
-
Object.keys(state).some((k) => k.startsWith("__rsc_ls_"))
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
23
|
if (typeof Symbol.dispose === "undefined") {
|
|
32
24
|
(Symbol as any).dispose = Symbol("Symbol.dispose");
|
|
33
25
|
}
|
|
@@ -59,6 +51,14 @@ interface CommitOptions {
|
|
|
59
51
|
* merged in. The page left was saved by handleTraversalStart.
|
|
60
52
|
*/
|
|
61
53
|
traversal?: boolean;
|
|
54
|
+
/** The committed route's name (payload metadata), remembered per history entry. */
|
|
55
|
+
routeName?: string;
|
|
56
|
+
/**
|
|
57
|
+
* transition({ when }) decision already made at this navigation's first
|
|
58
|
+
* presentation (the optimistic clientUrls() swap). Not a commit input: the
|
|
59
|
+
* partial updater reuses it instead of deciding again.
|
|
60
|
+
*/
|
|
61
|
+
transitionGatedOff?: boolean;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
@@ -79,6 +79,8 @@ interface BoundCommitOverrides {
|
|
|
79
79
|
cacheOnly?: boolean;
|
|
80
80
|
/** Server-set location state to merge into history.pushState */
|
|
81
81
|
serverState?: Record<string, unknown>;
|
|
82
|
+
/** The committed route's name (payload metadata), remembered per history entry. */
|
|
83
|
+
routeName?: string;
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
/**
|
|
@@ -86,6 +88,14 @@ interface BoundCommitOverrides {
|
|
|
86
88
|
*/
|
|
87
89
|
export interface BoundTransaction {
|
|
88
90
|
readonly currentUrl: string;
|
|
91
|
+
/** Bound `replace` option (history replace instead of push). */
|
|
92
|
+
readonly replace?: boolean;
|
|
93
|
+
/** Bound `traversal` option: a back/forward navigation. */
|
|
94
|
+
readonly traversal?: boolean;
|
|
95
|
+
/** Bound user state (resolved), pushed with the entry at commit. */
|
|
96
|
+
readonly state?: unknown;
|
|
97
|
+
/** Bound `transitionGatedOff`: the decision made at first presentation. */
|
|
98
|
+
readonly transitionGatedOff?: boolean;
|
|
89
99
|
/** Start streaming and get a token to end it when the stream completes */
|
|
90
100
|
startStreaming(): StreamingToken;
|
|
91
101
|
/** Commit the navigation. Returns the effective scroll option for the caller to handle. */
|
|
@@ -117,7 +127,7 @@ export function createNavigationTransaction(
|
|
|
117
127
|
store: NavigationStore,
|
|
118
128
|
eventController: EventController,
|
|
119
129
|
url: string,
|
|
120
|
-
options?:
|
|
130
|
+
options?: NavigateOptionsInternal & { skipLoadingState?: boolean },
|
|
121
131
|
): NavigationTransaction {
|
|
122
132
|
let committed = false;
|
|
123
133
|
const currentUrl = window.location.href;
|
|
@@ -168,6 +178,9 @@ export function createNavigationTransaction(
|
|
|
168
178
|
store.cacheSegmentsForHistory(historyKey, segments, currentHandleData);
|
|
169
179
|
|
|
170
180
|
if (storeOnly) {
|
|
181
|
+
// Same entry (an action refetch): refresh its state, keep its name
|
|
182
|
+
// unless the payload names it.
|
|
183
|
+
store.rememberDisplayedEntry(opts.routeName);
|
|
171
184
|
debugLog("[Browser] Store updated (action)");
|
|
172
185
|
handle.complete(parsedUrl);
|
|
173
186
|
return { scroll: false };
|
|
@@ -177,6 +190,7 @@ export function createNavigationTransaction(
|
|
|
177
190
|
if (serverState && Object.keys(serverState).length > 0) {
|
|
178
191
|
mergeLocationState(serverState);
|
|
179
192
|
}
|
|
193
|
+
store.rememberDisplayedEntry(opts.routeName);
|
|
180
194
|
handle.complete(parsedUrl);
|
|
181
195
|
debugLog("[Browser] Traversal committed, historyKey:", historyKey);
|
|
182
196
|
return { scroll };
|
|
@@ -192,6 +206,7 @@ export function createNavigationTransaction(
|
|
|
192
206
|
|
|
193
207
|
pushHistoryWithIdx(historyState, url, replace ?? false);
|
|
194
208
|
ensureHistoryKey();
|
|
209
|
+
store.rememberDisplayedEntry(opts.routeName);
|
|
195
210
|
|
|
196
211
|
if (hasLocationState(oldState) || hasLocationState(historyState)) {
|
|
197
212
|
window.dispatchEvent(new Event("__rsc_locationstate"));
|
|
@@ -219,6 +234,10 @@ export function createNavigationTransaction(
|
|
|
219
234
|
get currentUrl() {
|
|
220
235
|
return currentUrl;
|
|
221
236
|
},
|
|
237
|
+
replace: opts.replace,
|
|
238
|
+
traversal: opts.traversal,
|
|
239
|
+
state: opts.state,
|
|
240
|
+
transitionGatedOff: opts.transitionGatedOff,
|
|
222
241
|
startStreaming() {
|
|
223
242
|
return handle.startStreaming();
|
|
224
243
|
},
|
|
@@ -249,6 +268,7 @@ export function createNavigationTransaction(
|
|
|
249
268
|
interceptSourceUrl,
|
|
250
269
|
cacheOnly,
|
|
251
270
|
serverState,
|
|
271
|
+
routeName: overrides?.routeName ?? opts.routeName,
|
|
252
272
|
});
|
|
253
273
|
},
|
|
254
274
|
};
|