@rangojs/router 0.32.0 → 0.33.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/bin/browser-7KIJ5HSV.js +15 -0
- package/dist/bin/chunk-3B7CKBRO.js +463 -0
- package/dist/bin/chunk-524VACCG.js +71 -0
- package/dist/bin/chunk-LD7UBHT4.js +196 -0
- package/dist/bin/chunk-TO6CRWLN.js +1429 -0
- package/dist/bin/chunk-TTXC2U3G.js +2477 -0
- package/dist/bin/client-urls-projection-DQLWPDAT.js +28 -0
- package/dist/bin/forward-user-plugins-URBZKJP4.js +62 -0
- package/dist/bin/rango.js +42 -10173
- package/dist/bin/runtime-EPUDEHO4.js +596 -0
- package/dist/bin/runtime-discovery-3HH6BP3K.js +352 -0
- package/dist/bin/state-ACD2MGKJ.js +16 -0
- package/dist/bin/temp-rsc-server-JALTIWLT.js +3190 -0
- package/dist/types/bin/rango.d.ts +1 -0
- package/dist/types/browser/action-fence.d.ts +20 -0
- package/dist/types/browser/cookie-name.d.ts +51 -10
- package/dist/types/browser/invalidate-client-cache.d.ts +8 -6
- package/dist/types/browser/navigation-bridge.d.ts +36 -1
- package/dist/types/browser/navigation-client.d.ts +19 -0
- package/dist/types/browser/navigation-store-handle.d.ts +37 -4
- package/dist/types/browser/navigation-store.d.ts +2 -2
- package/dist/types/browser/navigation-transaction.d.ts +0 -1
- package/dist/types/browser/partial-update.d.ts +13 -0
- package/dist/types/browser/prefetch/cache.d.ts +96 -8
- package/dist/types/browser/prefetch/fetch.d.ts +26 -2
- package/dist/types/browser/prefetch/queue.d.ts +3 -2
- package/dist/types/browser/rango-state.d.ts +34 -5
- package/dist/types/browser/react/Link.d.ts +15 -6
- package/dist/types/browser/types.d.ts +25 -0
- package/dist/types/build/generate-route-types.d.ts +1 -1
- package/dist/types/build/route-types/include-resolution.d.ts +9 -1
- package/dist/types/build/route-types/router-processing.d.ts +3 -1
- package/dist/types/cache/cache-key-utils.d.ts +12 -8
- package/dist/types/cache/cache-scope.d.ts +4 -2
- package/dist/types/cache/cf/cf-cache-types.d.ts +9 -5
- package/dist/types/cache/document-cache-policy.d.ts +9 -1
- package/dist/types/cache/memory-segment-store.d.ts +3 -2
- package/dist/types/cache/segment-codec.d.ts +2 -2
- package/dist/types/cache/shell-snapshot.d.ts +1 -1
- package/dist/types/cache/types.d.ts +12 -7
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +2 -1
- package/dist/types/client-urls/types.d.ts +7 -5
- package/dist/types/prefetch-markers.d.ts +9 -0
- package/dist/types/prefetch-source-scope.d.ts +66 -0
- package/dist/types/route-definition/helpers-types.d.ts +8 -5
- package/dist/types/router/intercept-resolution.d.ts +25 -11
- package/dist/types/router/match-context.d.ts +1 -0
- package/dist/types/router/match-handlers.d.ts +1 -1
- package/dist/types/router/prefetch-cache-ttl.d.ts +11 -3
- package/dist/types/router/router-context.d.ts +1 -1
- package/dist/types/router/router-options.d.ts +6 -3
- package/dist/types/router/segment-resolution/loader-cache.d.ts +8 -0
- package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +23 -7
- package/dist/types/router/segment-resolution/revalidation.d.ts +19 -0
- package/dist/types/router/source-reads.d.ts +53 -0
- package/dist/types/router/types.d.ts +1 -1
- package/dist/types/rsc/helpers.d.ts +5 -2
- package/dist/types/rsc/prefetch-render-error.d.ts +7 -2
- package/dist/types/rsc/request-stages.d.ts +2 -0
- package/dist/types/rsc/shell-serve.d.ts +6 -1
- package/dist/types/server/context.d.ts +23 -6
- package/dist/types/server/cookie-store.d.ts +15 -1
- package/dist/types/server/document-location-state.d.ts +5 -2
- package/dist/types/server/handle-store.d.ts +2 -0
- package/dist/types/server/request-context.d.ts +43 -7
- package/dist/types/testing/e2e/server.d.ts +16 -1
- package/dist/types/testing/render-route.d.ts +66 -2
- package/dist/types/testing/serve-shell-request.d.ts +6 -3
- package/dist/types/types/handler-context.d.ts +7 -5
- package/dist/types/types/loader-types.d.ts +7 -4
- package/dist/types/types/segments.d.ts +15 -4
- package/dist/types/urls/path-helper-types.d.ts +7 -5
- package/dist/types/vite/temp-rsc-server.d.ts +2 -2
- package/dist/vite/index.js +449 -189
- package/package.json +2 -2
- package/skills/app-structure/SKILL.md +81 -0
- package/skills/app-structure/e-commerce.md +10 -9
- package/skills/app-structure/large-app.md +18 -2
- package/skills/caching/SKILL.md +10 -5
- package/skills/client-urls/SKILL.md +29 -11
- package/skills/comparison/references/framework-comparison.md +13 -8
- package/skills/document-cache/SKILL.md +8 -0
- package/skills/hooks/navigation.md +8 -8
- package/skills/hooks/outlets.md +4 -0
- package/skills/hooks/state.md +24 -8
- package/skills/intercept/SKILL.md +33 -12
- package/skills/layout/SKILL.md +60 -0
- package/skills/links/SKILL.md +101 -4
- package/skills/loader/SKILL.md +49 -22
- package/skills/migrate-nextjs/SKILL.md +8 -2
- package/skills/migrate-react-router/data-and-actions.md +5 -0
- package/skills/ppr/SKILL.md +26 -5
- package/skills/prerender/SKILL.md +4 -1
- package/skills/route/SKILL.md +6 -6
- package/skills/router-setup/SKILL.md +2 -1
- package/skills/server-actions/SKILL.md +41 -8
- package/skills/testing/client-components.md +29 -13
- package/skills/testing/serve-shell-request.md +7 -7
- package/skills/testing/server-actions.md +8 -0
- package/skills/ux-architecture/SKILL.md +70 -19
- package/src/bin/rango.ts +39 -13
- package/src/browser/action-fence.ts +72 -0
- package/src/browser/cookie-name.ts +82 -14
- package/src/browser/invalidate-client-cache.ts +9 -7
- package/src/browser/navigation-bridge.ts +164 -32
- package/src/browser/navigation-client.ts +95 -20
- package/src/browser/navigation-store-handle.ts +67 -5
- package/src/browser/navigation-store.ts +86 -17
- package/src/browser/navigation-transaction.ts +0 -6
- package/src/browser/partial-update.ts +31 -12
- package/src/browser/prefetch/cache.ts +188 -11
- package/src/browser/prefetch/fetch.ts +211 -51
- package/src/browser/prefetch/queue.ts +3 -2
- package/src/browser/rango-state.ts +50 -8
- package/src/browser/react/Link.tsx +20 -9
- package/src/browser/react/use-navigation.ts +1 -1
- package/src/browser/rsc-router.tsx +8 -1
- package/src/browser/server-action-bridge.ts +16 -11
- package/src/browser/types.ts +25 -0
- package/src/build/generate-route-types.ts +1 -0
- package/src/build/route-types/include-resolution.ts +40 -3
- package/src/build/route-types/router-processing.ts +170 -25
- package/src/build/route-types/scan-filter.ts +1 -0
- package/src/cache/cache-key-utils.ts +12 -8
- package/src/cache/cache-runtime.ts +5 -1
- package/src/cache/cache-scope.ts +28 -7
- package/src/cache/cf/cf-cache-types.ts +9 -5
- package/src/cache/document-cache-policy.ts +27 -0
- package/src/cache/document-cache.ts +5 -0
- package/src/cache/memory-segment-store.ts +3 -2
- package/src/cache/segment-codec.ts +2 -2
- package/src/cache/shell-snapshot.ts +1 -1
- package/src/cache/types.ts +12 -7
- package/src/cache/vercel/vercel-cache-store.ts +2 -1
- package/src/client-urls/types.ts +7 -5
- package/src/prefetch-markers.ts +10 -0
- package/src/prefetch-source-scope.ts +137 -0
- package/src/route-definition/helpers-types.ts +8 -5
- package/src/router/intercept-resolution.ts +94 -50
- package/src/router/loader-resolution.ts +8 -6
- package/src/router/match-api.ts +33 -29
- package/src/router/match-context.ts +5 -0
- package/src/router/match-handlers.ts +2 -1
- package/src/router/match-middleware/cache-lookup.ts +21 -13
- package/src/router/match-result.ts +39 -87
- package/src/router/navigation-snapshot.ts +18 -3
- package/src/router/prefetch-cache-ttl.ts +15 -3
- package/src/router/revalidation.ts +53 -23
- package/src/router/router-context.ts +2 -1
- package/src/router/router-options.ts +6 -3
- package/src/router/segment-resolution/loader-cache.ts +21 -3
- package/src/router/segment-resolution/prefetch-deferral.ts +38 -31
- package/src/router/segment-resolution/revalidation.ts +40 -12
- package/src/router/source-reads.ts +122 -0
- package/src/router/types.ts +2 -1
- package/src/router.ts +11 -12
- package/src/rsc/helpers.ts +8 -3
- package/src/rsc/prefetch-render-error.ts +7 -2
- package/src/rsc/progressive-enhancement.ts +3 -2
- package/src/rsc/request-stages.ts +59 -0
- package/src/rsc/rsc-rendering.ts +49 -33
- package/src/rsc/server-action.ts +2 -1
- package/src/rsc/shell-capture.ts +4 -5
- package/src/rsc/shell-serve.ts +12 -1
- package/src/segment-system.tsx +1 -1
- package/src/server/context.ts +37 -6
- package/src/server/cookie-store.ts +22 -1
- package/src/server/document-location-state.ts +23 -2
- package/src/server/handle-store.ts +2 -0
- package/src/server/request-context.ts +113 -28
- package/src/testing/dispatch.ts +3 -0
- package/src/testing/e2e/fixture.ts +4 -12
- package/src/testing/e2e/server.ts +91 -4
- package/src/testing/render-route.tsx +269 -4
- package/src/testing/serve-shell-request.ts +6 -3
- package/src/types/handler-context.ts +7 -5
- package/src/types/loader-types.ts +7 -4
- package/src/types/segments.ts +15 -4
- package/src/urls/path-helper-types.ts +7 -5
- package/src/vite/temp-rsc-server.ts +2 -2
|
@@ -35,8 +35,28 @@
|
|
|
35
35
|
* document. A future multi-router-in-one-document feature must not silently
|
|
36
36
|
* inherit this global, cross-router cache suppression.
|
|
37
37
|
*/
|
|
38
|
+
/**
|
|
39
|
+
* Register what runs when the fence drops holding a write no landing took:
|
|
40
|
+
* the navigation store's markCachesStaleAfterWrite (navigation-store-handle.ts).
|
|
41
|
+
*/
|
|
42
|
+
export declare function setUnlandedWriteHandler(handler: (() => void) | null): void;
|
|
38
43
|
export declare function enterActionFence(): void;
|
|
39
44
|
export declare function exitActionFence(): void;
|
|
45
|
+
/** A cookie read saw an external state rotation (rango-state.ts observer). */
|
|
46
|
+
export declare function noteStateRotation(): void;
|
|
47
|
+
/**
|
|
48
|
+
* A cookie read saw a write-only change (rango-state.ts write observer).
|
|
49
|
+
* Returns whether the fence is up and took it; with the fence down the
|
|
50
|
+
* caller marks the caches stale itself.
|
|
51
|
+
*/
|
|
52
|
+
export declare function noteStateWrite(): boolean;
|
|
53
|
+
/** A landing marked every cache stale: a write seen so far is covered. */
|
|
54
|
+
export declare function takeStateWriteInActionFence(): void;
|
|
55
|
+
/**
|
|
56
|
+
* Whether the state rotated externally since the fence went up or the last
|
|
57
|
+
* landing took it, and clear it. False with the fence down.
|
|
58
|
+
*/
|
|
59
|
+
export declare function takeStateRotationInActionFence(): boolean;
|
|
40
60
|
export declare function isActionFenceActive(): boolean;
|
|
41
61
|
/** Test-only: reset the refcount between cases. */
|
|
42
62
|
export declare function __resetActionFence(): void;
|
|
@@ -36,31 +36,72 @@ export declare function stripPerClientSignals(headers: Headers): void;
|
|
|
36
36
|
*/
|
|
37
37
|
export declare function getRawCookieValue(cookieString: string | null, name: string): string | null;
|
|
38
38
|
/**
|
|
39
|
-
* Encode a state value for the wire: `encodeURIComponent(version):timestamp
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* shape survives and `:` is a legal cookie-value octet.
|
|
39
|
+
* Encode a state value for the wire: `encodeURIComponent(version):timestamp`,
|
|
40
|
+
* plus `:write` when the write part is not 0. Only the build-derived version
|
|
41
|
+
* is encoded (it is arbitrary); the `:` separators and the numeric parts stay
|
|
42
|
+
* raw, so the shape survives and `:` is a legal cookie-value octet. A value no
|
|
43
|
+
* server action has bumped keeps the two-part shape it had before the write
|
|
44
|
+
* part existed.
|
|
45
|
+
*/
|
|
46
|
+
export declare function encodeStateValue(version: string, timestamp: number, write?: number): string;
|
|
47
|
+
/**
|
|
48
|
+
* Parsed state value. `version` is decoded; `timestamp` (the invalidation
|
|
49
|
+
* part) and `write` are the raw integers. `write` is 0 for a two-part value.
|
|
43
50
|
*/
|
|
44
|
-
export declare function encodeStateValue(version: string, timestamp: number): string;
|
|
45
|
-
/** Parsed state value. `version` is decoded; `timestamp` is the raw integer. */
|
|
46
51
|
export interface StateValue {
|
|
47
52
|
version: string;
|
|
48
53
|
timestamp: number;
|
|
54
|
+
write: number;
|
|
49
55
|
}
|
|
50
56
|
/**
|
|
51
|
-
* Decode a wire value back into `{version, timestamp}`.
|
|
52
|
-
*
|
|
53
|
-
*
|
|
57
|
+
* Decode a wire value back into `{version, timestamp, write}`. Accepts the
|
|
58
|
+
* two-part `{version}:{timestamp}` (a value no action bumped, or one an open
|
|
59
|
+
* tab wrote before the write part existed) and the three-part
|
|
60
|
+
* `{version}:{timestamp}:{write}`. Returns null for a malformed value (no `:`,
|
|
61
|
+
* empty version, a non-numeric part, or more parts) so callers mint fresh
|
|
62
|
+
* instead of trusting garbage.
|
|
54
63
|
*/
|
|
55
64
|
export declare function decodeStateValue(raw: string): StateValue | null;
|
|
65
|
+
/**
|
|
66
|
+
* The `{version}:{timestamp}` part of a wire value: what every in-memory
|
|
67
|
+
* comparison on the client reads (the prefetch cache keys, the action
|
|
68
|
+
* fence's rotation check, the external-rotation observer). A change to it is
|
|
69
|
+
* a hard invalidation (a deploy, or invalidateClientCache() on either seat);
|
|
70
|
+
* a change to the write part alone is a server action's write, which marks
|
|
71
|
+
* caches stale and flushes nothing. The full value, write part included, is
|
|
72
|
+
* what requests send as `X-Rango-State`, so the browser HTTP cache (Vary)
|
|
73
|
+
* misses after every write. A raw cut, no decode: the version is
|
|
74
|
+
* percent-encoded, so the second `:` is always the write separator.
|
|
75
|
+
*/
|
|
76
|
+
export declare function stateEpoch(raw: string): string;
|
|
56
77
|
/**
|
|
57
78
|
* Mint a fresh state value whose timestamp is strictly greater than the previous
|
|
58
79
|
* one, so a re-mint inside the same millisecond (or a backward clock step) still
|
|
59
80
|
* differs from the current value. `prevRaw` is the current wire value (the
|
|
60
81
|
* client's in-memory mirror, or the server's inbound header/cookie) or null; its
|
|
61
|
-
* timestamp is the floor.
|
|
82
|
+
* timestamp is the floor. The write part starts over (left out): the new
|
|
83
|
+
* timestamp alone makes the value one no response was cached under. Shared by
|
|
84
|
+
* both seats so the monotonic guard lives once.
|
|
62
85
|
*/
|
|
63
86
|
export declare function mintStateValue(version: string, prevRaw: string | null): string;
|
|
87
|
+
/**
|
|
88
|
+
* Bump the write part of `prevRaw`, keeping its version and timestamp: what
|
|
89
|
+
* the response of a server action without a directive sets. Keeping the
|
|
90
|
+
* version keeps the bump soft for a client still on another build (a form
|
|
91
|
+
* posted from a document served before a deploy); its own boot handles the
|
|
92
|
+
* deploy. Returns null when `prevRaw` is absent or malformed: a client with
|
|
93
|
+
* no state value sends no `X-Rango-State` and has nothing in the HTTP cache
|
|
94
|
+
* keyed by one.
|
|
95
|
+
*
|
|
96
|
+
* The new write part must be one no response was cached under. It is
|
|
97
|
+
* strictly greater than the previous one, and it is the current time in
|
|
98
|
+
* microseconds with random low digits: two actions that start from the same
|
|
99
|
+
* value (two tabs, or two in flight in one tab) can run in the same server
|
|
100
|
+
* millisecond (a Worker's clock does not move between I/O), and the second
|
|
101
|
+
* response must not set the value the first one set, under which a prefetch
|
|
102
|
+
* between them may have been cached with the second write missing.
|
|
103
|
+
*/
|
|
104
|
+
export declare function bumpStateWrite(prevRaw: string | null): string | null;
|
|
64
105
|
/**
|
|
65
106
|
* Attribute string for the state cookie. Session cookie (no Max-Age/Expires),
|
|
66
107
|
* Path=/ (whole app), SameSite=Lax (sent on top-level navigations), and Secure
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Client seat of `invalidateClientCache()` (the `default` export condition).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* the history cache is marked stale (SWR), the prefetch map
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* The full client invalidation, more than a server action without a
|
|
5
|
+
* directive does: the history cache is marked stale (SWR), the prefetch map
|
|
6
|
+
* is flushed (in-flight prefetches aborted, visible links prefetch again), the
|
|
7
|
+
* state rotates, and sibling tabs are broadcast to, which adopt the new state
|
|
8
|
+
* and flush too (`invalidateCacheAndBroadcast()`). A directive-free action
|
|
9
|
+
* only marks history and prefetches stale and rotates nothing
|
|
10
|
+
* (`markCacheAsStaleAndBroadcast()`). History is marked stale, not cleared, so
|
|
11
|
+
* Back renders the cached entry instantly and revalidates.
|
|
10
12
|
*/
|
|
11
13
|
export declare function invalidateClientCache(): void;
|
|
12
14
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { NavigationBridge, NavigationBridgeConfig, NavigationStore } from "./types.js";
|
|
2
2
|
import { createNavigationTransaction } from "./navigation-transaction.js";
|
|
3
|
-
import type { EventController } from "./event-controller.js";
|
|
3
|
+
import type { EventController, NavigationEntry } from "./event-controller.js";
|
|
4
4
|
import type { PrefetchStrategy } from "../router/prefetch-default.js";
|
|
5
5
|
export { createNavigationTransaction };
|
|
6
6
|
/**
|
|
@@ -22,6 +22,41 @@ export interface NavigationBridgeDelegatedPrefetchOptions {
|
|
|
22
22
|
}
|
|
23
23
|
/** Register delegated anchor prefetch with the production bridge wiring. */
|
|
24
24
|
export declare function setupNavigationBridgeDelegatedPrefetch(store: NavigationStore, eventController: EventController, getVersion: () => string | undefined, options?: NavigationBridgeDelegatedPrefetchOptions): () => void;
|
|
25
|
+
/**
|
|
26
|
+
* The request a stale-prefetch revalidation makes (scheduleStalePrefetch-
|
|
27
|
+
* Revalidation): the ids it holds, and the intercept and action error
|
|
28
|
+
* boundary on screen.
|
|
29
|
+
*/
|
|
30
|
+
export interface StalePrefetchRevalidation {
|
|
31
|
+
segmentIds: string[];
|
|
32
|
+
intercept: {
|
|
33
|
+
isIntercept: boolean;
|
|
34
|
+
sourceUrl?: string;
|
|
35
|
+
};
|
|
36
|
+
boundary: string | undefined;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A click that adopted a prefetch a server action marked stale
|
|
40
|
+
* (prefetch/cache.ts isPrefetchStale) committed the pre-write payload, as any
|
|
41
|
+
* prefetched click commits. Once that navigation settled
|
|
42
|
+
* (NavigationEntry.settled: its commit, any `prefetch: false` fill and its
|
|
43
|
+
* stream, so this never supersedes the click nor cuts a response
|
|
44
|
+
* that arrived short), `revalidate` runs the page's request in
|
|
45
|
+
* Back's lane (the bridge's revalidateInBackground), with the ids on screen
|
|
46
|
+
* and the action error boundary it shows held, as Back onto a stale entry
|
|
47
|
+
* does.
|
|
48
|
+
*
|
|
49
|
+
* The entry is marked stale first, so a Back onto it revalidates when this
|
|
50
|
+
* one never ran or was left. Nothing runs when the click did not commit, or
|
|
51
|
+
* when the page moved since (a newer navigation aborts the click's entry, a
|
|
52
|
+
* shallow one moves the history key) or another navigation is open. An
|
|
53
|
+
* action in flight does not hold it back, as it does not hold Back's: its
|
|
54
|
+
* follow-up, when it owes one, starts a navigation that supersedes this one.
|
|
55
|
+
*
|
|
56
|
+
* Exported so renderRoute (testing/render-route.tsx) drives these guards and
|
|
57
|
+
* this plan rather than a copy of them.
|
|
58
|
+
*/
|
|
59
|
+
export declare function scheduleStalePrefetchRevalidation(store: Pick<NavigationStore, "getCachedSegments" | "getHistoryKey" | "updateCacheHandleData">, eventController: Pick<EventController, "getUnsettledNavigation">, entry: Pick<NavigationEntry, "settled">, historyKey: string, revalidate: (request: StalePrefetchRevalidation) => void): void;
|
|
25
60
|
/**
|
|
26
61
|
* Create a navigation bridge for handling client-side navigation
|
|
27
62
|
*
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
import type { NavigationClient, RscBrowserDependencies } from "./types.js";
|
|
2
|
+
import { type DecodedPrefetch } from "./prefetch/cache.js";
|
|
3
|
+
import type { SourcePage } from "../prefetch-source-scope.js";
|
|
4
|
+
/**
|
|
5
|
+
* The completed prefetch a navigation adopts: the one stored under its
|
|
6
|
+
* source key, else one stored for a group of source pages the page is in
|
|
7
|
+
* (`paramsKeys`, paramsCandidateKeys), else the one every source page
|
|
8
|
+
* shares. None when the navigation cannot use a prefetch. A stale one only
|
|
9
|
+
* when `acceptStale` (FetchPartialOptions.acceptStalePrefetch).
|
|
10
|
+
*/
|
|
11
|
+
export declare function consumeStoredPrefetch(canUsePrefetch: boolean, sourceKey: string, paramsKeys: readonly string[], wildcardKey: string, acceptStale?: boolean): {
|
|
12
|
+
entry: DecodedPrefetch | null;
|
|
13
|
+
hitKey: string | null;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Whether a navigation from `page` may serve an in-flight prefetch it
|
|
17
|
+
* adopted through the wildcard key: not one the server scoped to its own
|
|
18
|
+
* source page, nor one scoped to a group of source pages `page` is not in.
|
|
19
|
+
*/
|
|
20
|
+
export declare function wildcardAdoptionFits(entry: DecodedPrefetch, page: SourcePage | null): boolean;
|
|
2
21
|
/**
|
|
3
22
|
* Create a navigation client for fetching RSC payloads
|
|
4
23
|
*
|
|
@@ -7,19 +7,52 @@
|
|
|
7
7
|
* receive it by argument: the jar-divergence observer (below) and the client
|
|
8
8
|
* seat of `invalidateClientCache()` (added later).
|
|
9
9
|
*
|
|
10
|
-
* Dependency-light on purpose: it imports only
|
|
11
|
-
* store type, so pulling it into the default
|
|
12
|
-
* navigation store into bundles that previously
|
|
10
|
+
* Dependency-light on purpose: it imports only the action fence, the
|
|
11
|
+
* rango-state observers and the store type, so pulling it into the default
|
|
12
|
+
* root entry does not drag the navigation store into bundles that previously
|
|
13
|
+
* lacked it.
|
|
13
14
|
*/
|
|
14
15
|
import type { NavigationStore } from "./types.js";
|
|
16
|
+
import type { SourcePage } from "../prefetch-source-scope.js";
|
|
15
17
|
/**
|
|
16
18
|
* Register the active navigation store at boot, and wire the jar-divergence
|
|
17
19
|
* observer: when a per-request cookie read detects an EXTERNAL rotation (a
|
|
18
20
|
* sibling tab, a server `Set-Cookie`, or a cookie clear), mark this tab's
|
|
19
21
|
* history cache stale. The history cache is not state-keyed, so the value
|
|
20
22
|
* rotation alone does not reach it. No broadcast, no prefetch clear, no
|
|
21
|
-
* re-rotation — the value already changed externally.
|
|
23
|
+
* re-rotation — the value already changed externally. A rotation seen while
|
|
24
|
+
* an action is in flight is recorded on the fence: the action's landing then
|
|
25
|
+
* sends the full broadcast (navigation-store.ts markCacheAsStaleAndBroadcast).
|
|
26
|
+
*
|
|
27
|
+
* A read that sees only the write part change (a server action's response,
|
|
28
|
+
* in this tab or another) marks this tab's history entries and prefetches
|
|
29
|
+
* stale, as a soft broadcast's receiver does: a tab the broadcast does not
|
|
30
|
+
* reach would otherwise serve a prefetch made before the write as fresh.
|
|
31
|
+
* While an action is in flight the write is recorded on the fence instead:
|
|
32
|
+
* the action's landing marks every cache stale once (navigation-store.ts
|
|
33
|
+
* markStaleSoftAndBroadcast), and when no landing does, the fence's drop
|
|
34
|
+
* marks them.
|
|
22
35
|
*/
|
|
23
36
|
export declare function registerNavigationStore(store: NavigationStore): void;
|
|
24
37
|
/** The active navigation store, or null before boot has registered it. */
|
|
25
38
|
export declare function getRegisteredStore(): NavigationStore | null;
|
|
39
|
+
/**
|
|
40
|
+
* Register the reader of the committed page's params and route name: the
|
|
41
|
+
* router store's own state, which useParams reads (the event controller's
|
|
42
|
+
* params, the displayed history entry's route name). Read by the prefetch
|
|
43
|
+
* cache outside render, at request time, to look up an answer shared by the
|
|
44
|
+
* pages with equal values of what a revalidate() rule read
|
|
45
|
+
* (prefetch-source-scope.ts). A reader, not a copy: there is no second
|
|
46
|
+
* source of truth for the params.
|
|
47
|
+
*/
|
|
48
|
+
export declare function registerCommittedPageReader(read: () => SourcePage): void;
|
|
49
|
+
/**
|
|
50
|
+
* The boot reader (rsc-router.tsx): the event controller's params, which
|
|
51
|
+
* useParams reads and only a commit sets (an optimistic clientUrls() frame
|
|
52
|
+
* sets React context, not them), and the displayed entry's route name.
|
|
53
|
+
*/
|
|
54
|
+
export declare function committedPageOf(controller: {
|
|
55
|
+
getParams(): Record<string, string>;
|
|
56
|
+
}, store: Pick<NavigationStore, "getHistoryEntryMemory">): () => SourcePage;
|
|
57
|
+
/** The committed page, or null before boot has registered its reader. */
|
|
58
|
+
export declare function getCommittedPage(): SourcePage | null;
|
|
@@ -55,8 +55,8 @@ export interface NavigationStoreConfig {
|
|
|
55
55
|
cacheSize?: number;
|
|
56
56
|
/**
|
|
57
57
|
* Enable cross-tab cache invalidation via BroadcastChannel (default: true)
|
|
58
|
-
*
|
|
59
|
-
*
|
|
58
|
+
* After a server action other tabs that share a segment mark their caches
|
|
59
|
+
* stale; after invalidateClientCache() they adopt its state and flush
|
|
60
60
|
*/
|
|
61
61
|
crossTabSync?: boolean;
|
|
62
62
|
/**
|
|
@@ -87,7 +87,6 @@ export interface CommitResult {
|
|
|
87
87
|
* Bound transaction with pre-configured commit options (without segmentIds/segments)
|
|
88
88
|
*/
|
|
89
89
|
export interface BoundTransaction {
|
|
90
|
-
readonly currentUrl: string;
|
|
91
90
|
/** Bound `replace` option (history replace instead of push). */
|
|
92
91
|
readonly replace?: boolean;
|
|
93
92
|
/** Bound `traversal` option: a back/forward navigation. */
|
|
@@ -114,6 +114,19 @@ export type UpdateMode = {
|
|
|
114
114
|
refresh?: boolean;
|
|
115
115
|
/** The cross-tab refresh's held boundary (see `action`). */
|
|
116
116
|
boundary?: string;
|
|
117
|
+
/**
|
|
118
|
+
* A click's: told of each response this navigation fetches, true when
|
|
119
|
+
* it is a prefetch a server action marked stale
|
|
120
|
+
* (FetchPartialResult.stalePrefetch), false otherwise, so a fresh
|
|
121
|
+
* refetch after it (a fill's start-over, an HMR retry) clears it. Its
|
|
122
|
+
* presence is what lets a stale prefetch answer
|
|
123
|
+
* (FetchPartialOptions.acceptStalePrefetch): only a navigation that
|
|
124
|
+
* revalidates afterwards takes one. The click commits it as it commits
|
|
125
|
+
* any prefetch; the bridge revalidates the page once the navigation
|
|
126
|
+
* settled (navigation-bridge.ts navigate). Nothing here changes how or
|
|
127
|
+
* when this commit lands.
|
|
128
|
+
*/
|
|
129
|
+
onStalePrefetch?: (stale: boolean) => void;
|
|
117
130
|
} | {
|
|
118
131
|
type: "leave-intercept";
|
|
119
132
|
interceptSourceUrl?: string;
|
|
@@ -7,11 +7,14 @@
|
|
|
7
7
|
* stream resolves the route's client references, so the route's JS chunks are
|
|
8
8
|
* imported during prefetch rather than on click. The decoded payload is reused
|
|
9
9
|
* verbatim by navigation, so a prefetched click loads no new code. Two key
|
|
10
|
-
* scopes are in play
|
|
10
|
+
* scopes are in play. Wherever a key below says `rangoState`, callers pass the
|
|
11
|
+
* state's `{version}:{timestamp}` part (cookie-name.ts stateEpoch), not the
|
|
12
|
+
* whole value: a server action's write bump leaves the keys as they were.
|
|
11
13
|
* - Wildcard (default): built by `buildPrefetchKey(rangoState, target)` —
|
|
12
14
|
* shape `rangoState\0/target?...`. Shared across all source pages and
|
|
13
15
|
* source segment trees; `_rsc_segments` is omitted from this key. Invalidated
|
|
14
|
-
* automatically when Rango state bumps (deploy or
|
|
16
|
+
* automatically when Rango state bumps (deploy or invalidateClientCache()).
|
|
17
|
+
* A server action marks entries stale instead (the stale epoch, below).
|
|
15
18
|
* - Source-scoped: built by `buildSourceKey(rangoState, sourceHref, target)`
|
|
16
19
|
* — shape `rangoState\0sourceHref\0/target?...`. Embeds the Rango state
|
|
17
20
|
* (so rotation invalidates source-scoped entries too), the source href, and
|
|
@@ -20,6 +23,14 @@
|
|
|
20
23
|
* targets, matched or not), OR when a Link opts in with
|
|
21
24
|
* `prefetchKey=":source"` — in both cases so source-sensitive responses
|
|
22
25
|
* cannot bleed into navigations from other pages.
|
|
26
|
+
* - Params-scoped: built by `buildParamsKey(rangoState, label, target)` —
|
|
27
|
+
* shape `rangoState\0p:<label>\0/target?...`. Populated when the server
|
|
28
|
+
* tags a response with `X-RSC-Prefetch-Scope: params; <label>`: a
|
|
29
|
+
* revalidate() rule read the source's params or route name and kept a
|
|
30
|
+
* segment, so the answer is right for every page with equal values of
|
|
31
|
+
* what it read (prefetch-source-scope.ts). `paramsShapes` remembers, per
|
|
32
|
+
* target, what the stored labels read, so a lookup rebuilds the label of
|
|
33
|
+
* the page on screen (paramsCandidateKeys).
|
|
23
34
|
*
|
|
24
35
|
* Also tracks in-flight prefetch promises. Each promise resolves to the
|
|
25
36
|
* decoded prefetch entry (or null), letting navigation adopt a
|
|
@@ -39,6 +50,7 @@
|
|
|
39
50
|
* effectively per-document. Unit tests reset them via clearPrefetchCache().
|
|
40
51
|
*/
|
|
41
52
|
import type { RscPayload } from "../types.js";
|
|
53
|
+
import { type SourcePage } from "../../prefetch-source-scope.js";
|
|
42
54
|
/**
|
|
43
55
|
* A prefetch that has been fetched AND eagerly decoded. Storing the decoded
|
|
44
56
|
* payload (not the raw Response) is what makes a prefetched navigation "warm":
|
|
@@ -58,9 +70,23 @@ export interface DecodedPrefetch {
|
|
|
58
70
|
* Prefetch scope as tagged by the server via `X-RSC-Prefetch-Scope`.
|
|
59
71
|
* `"source"` means the response is source-page-sensitive and must not be
|
|
60
72
|
* reused by a navigation from a different page — navigation enforces this
|
|
61
|
-
* when it adopted an inflight entry through the wildcard key.
|
|
73
|
+
* when it adopted an inflight entry through the wildcard key. `"params"`
|
|
74
|
+
* means it serves the pages whose label equals `paramsLabel`.
|
|
62
75
|
*/
|
|
63
|
-
scope:
|
|
76
|
+
scope: PrefetchScope;
|
|
77
|
+
/** The label of a `"params"` entry (prefetch-source-scope.ts). */
|
|
78
|
+
paramsLabel?: string;
|
|
79
|
+
/**
|
|
80
|
+
* The stale epoch (`currentPrefetchEpoch()`) the prefetch's fetch started
|
|
81
|
+
* under. A server action without a directive marks every prefetch stale
|
|
82
|
+
* (`markPrefetchesStale()` bumps the epoch) instead of flushing it: an entry
|
|
83
|
+
* from an older epoch is still served, and the navigation that adopts it
|
|
84
|
+
* revalidates its page once it settled (`isPrefetchStale`). A stale entry
|
|
85
|
+
* is served at most once, and only to a click (`consumePrefetch`'s
|
|
86
|
+
* `acceptStale`). A respawn keeps the original fetch's epoch, as it keeps
|
|
87
|
+
* its timestamp.
|
|
88
|
+
*/
|
|
89
|
+
epoch: number;
|
|
64
90
|
/**
|
|
65
91
|
* Synchronously-readable flag, flipped to true when `streamComplete` resolves
|
|
66
92
|
* (the entire RSC stream has drained). Navigation reads this at click time to
|
|
@@ -101,6 +127,8 @@ export interface DecodedPrefetch {
|
|
|
101
127
|
*/
|
|
102
128
|
dispose?: () => void;
|
|
103
129
|
}
|
|
130
|
+
/** Where a prefetch is stored: see the module comment. */
|
|
131
|
+
export type PrefetchScope = "source" | "params" | "wildcard";
|
|
104
132
|
/**
|
|
105
133
|
* Initialize the prefetch cache with the configured TTL and max size.
|
|
106
134
|
* Called once at app startup with the values from server metadata. Each
|
|
@@ -121,7 +149,7 @@ export declare function isPrefetchCacheDisabled(): boolean;
|
|
|
121
149
|
* one of:
|
|
122
150
|
* - Wildcard (source-agnostic): prefix is the Rango state value from
|
|
123
151
|
* `getRangoState()`. Shared across all source pages. Invalidated
|
|
124
|
-
* automatically when Rango state bumps (deploy or
|
|
152
|
+
* automatically when Rango state bumps (deploy or invalidateClientCache()).
|
|
125
153
|
* `_rsc_segments` is omitted so sibling payloads prefetched from one page
|
|
126
154
|
* remain reusable after another prefetched navigation commits. Key shape:
|
|
127
155
|
* `rangoState\0/target?...`.
|
|
@@ -148,8 +176,38 @@ export declare function buildPrefetchKey(prefix: string, targetUrl: URL): string
|
|
|
148
176
|
* - `sourceHref` makes the key unique per originating page.
|
|
149
177
|
*/
|
|
150
178
|
export declare function buildSourceKey(rangoState: string, sourceHref: string, targetUrl: URL): string;
|
|
179
|
+
/**
|
|
180
|
+
* Build a params-scoped cache key. Key shape:
|
|
181
|
+
* `rangoState\0p:<label>\0/target?...`, the target normalized like the
|
|
182
|
+
* wildcard key's: the answer serves every page with this label, whatever
|
|
183
|
+
* segments it holds, as a wildcard answer serves every page.
|
|
184
|
+
*/
|
|
185
|
+
export declare function buildParamsKey(rangoState: string, label: string, targetUrl: URL): string;
|
|
186
|
+
/**
|
|
187
|
+
* Remember that a params-scoped answer for `targetUrl` is stored under a
|
|
188
|
+
* label of this shape. Called where the response header is read, with the
|
|
189
|
+
* store (fetch.ts executePrefetchFetch).
|
|
190
|
+
*/
|
|
191
|
+
export declare function rememberParamsShape(targetUrl: URL, label: string): void;
|
|
192
|
+
/**
|
|
193
|
+
* The params-scoped keys a request for `targetUrl` from `page` may adopt:
|
|
194
|
+
* the page's label under each shape stored for the target, the shapes that
|
|
195
|
+
* read the most first. Empty without a page (before boot).
|
|
196
|
+
*/
|
|
197
|
+
export declare function paramsCandidateKeys(rangoState: string, targetUrl: URL, page: SourcePage | null): string[];
|
|
198
|
+
/**
|
|
199
|
+
* Whether `page` is in the group a params-scoped entry serves: its label
|
|
200
|
+
* under the entry's shape equals the entry's.
|
|
201
|
+
*/
|
|
202
|
+
export declare function pageMatchesParamsLabel(label: string, page: SourcePage | null): boolean;
|
|
151
203
|
/**
|
|
152
204
|
* Check if a prefetch is already cached, in-flight, or queued for the given key.
|
|
205
|
+
*
|
|
206
|
+
* A stale stored entry (`isPrefetchStale`) counts as absent and is dropped, so
|
|
207
|
+
* a new hover or viewport trigger replaces it with a fresh prefetch, and a
|
|
208
|
+
* click made while that one is in flight adopts it rather than the stale one.
|
|
209
|
+
* An in-flight fetch counts whatever its epoch: it is never aborted, and a
|
|
210
|
+
* duplicate request for its key would race it for the slot.
|
|
153
211
|
*/
|
|
154
212
|
export declare function hasPrefetch(key: string): boolean;
|
|
155
213
|
/**
|
|
@@ -163,10 +221,19 @@ export declare function hasPrefetch(key: string): boolean;
|
|
|
163
221
|
* TTL bounds the age of the data, not of the latest adoption. Without
|
|
164
222
|
* `respawn` the entry is deleted (legacy one-shot behavior).
|
|
165
223
|
*
|
|
224
|
+
* A stale entry (`isPrefetchStale`) is served only to a caller that passes
|
|
225
|
+
* `acceptStale` (a click, which revalidates its page once it settled,
|
|
226
|
+
* navigation-bridge.ts): for any other caller it is a miss and stays in its
|
|
227
|
+
* slot. It is served at most once: its slot is not re-armed, so a second
|
|
228
|
+
* click on the same link fetches fresh rather than showing pre-write data
|
|
229
|
+
* older than what the first click's revalidation showed.
|
|
230
|
+
*
|
|
166
231
|
* Does NOT check in-flight prefetches — use consumeInflightPrefetch()
|
|
167
232
|
* for that (returns a Promise instead of a resolved entry).
|
|
168
233
|
*/
|
|
169
|
-
export declare function consumePrefetch(key: string
|
|
234
|
+
export declare function consumePrefetch(key: string, options?: {
|
|
235
|
+
acceptStale?: boolean;
|
|
236
|
+
}): DecodedPrefetch | null;
|
|
170
237
|
/**
|
|
171
238
|
* Consume an in-flight prefetch promise. Returns null if no prefetch is
|
|
172
239
|
* in-flight for this key. The returned Promise resolves to the decoded
|
|
@@ -188,9 +255,14 @@ export declare function consumeInflightPrefetch(key: string): Promise<DecodedPre
|
|
|
188
255
|
* Store an eagerly-decoded prefetch in the in-memory cache.
|
|
189
256
|
*
|
|
190
257
|
* Skips storage if the generation has changed since the fetch started
|
|
191
|
-
* (
|
|
258
|
+
* (the cache was flushed mid-flight). An entry from an older stale epoch (an
|
|
259
|
+
* action marked prefetches stale mid-flight) is stored, and stays stale.
|
|
260
|
+
*
|
|
261
|
+
* `inflightKey` is the key the prefetch was in flight under, when the
|
|
262
|
+
* storage key is not one of its aliases (a params-scoped answer): a
|
|
263
|
+
* navigation that adopted it marked the aliases.
|
|
192
264
|
*/
|
|
193
|
-
export declare function storePrefetch(key: string, entry: DecodedPrefetch, fetchGeneration: number): void;
|
|
265
|
+
export declare function storePrefetch(key: string, entry: DecodedPrefetch, fetchGeneration: number, inflightKey?: string): void;
|
|
194
266
|
/**
|
|
195
267
|
* Remove a single stored prefetch entry. Used to evict an entry whose body
|
|
196
268
|
* stream stalled after headers arrived (its payload / streamComplete never
|
|
@@ -210,6 +282,22 @@ export declare function removePrefetch(key: string, entry: DecodedPrefetch): voi
|
|
|
210
282
|
* storePrefetch so it can detect stale completions.
|
|
211
283
|
*/
|
|
212
284
|
export declare function currentGeneration(): number;
|
|
285
|
+
/**
|
|
286
|
+
* Capture the current stale epoch. A prefetch fetch stamps its entry with the
|
|
287
|
+
* value read when it started (`DecodedPrefetch.epoch`).
|
|
288
|
+
*/
|
|
289
|
+
export declare function currentPrefetchEpoch(): number;
|
|
290
|
+
/** Whether an action marked prefetches stale after this entry's fetch began. */
|
|
291
|
+
export declare function isPrefetchStale(entry: Pick<DecodedPrefetch, "epoch">): boolean;
|
|
292
|
+
/**
|
|
293
|
+
* Mark every stored and in-flight prefetch stale: what a server action
|
|
294
|
+
* without a directive does (navigation-store.ts markCacheAsStaleAndBroadcast).
|
|
295
|
+
* Only bumps the epoch. Nothing is dropped, aborted or rotated, and visible
|
|
296
|
+
* links are not told (no `notifyPrefetchCacheInvalidated()`), so they do not
|
|
297
|
+
* prefetch again. A click on a stale entry commits it at once; the navigation
|
|
298
|
+
* then revalidates its page (navigation-bridge.ts navigate).
|
|
299
|
+
*/
|
|
300
|
+
export declare function markPrefetchesStale(): void;
|
|
213
301
|
export declare function markPrefetchInflight(key: string): void;
|
|
214
302
|
export declare function setInflightPromise(key: string, promise: Promise<DecodedPrefetch | null>): void;
|
|
215
303
|
/**
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* a prefetch that is still downloading/decoding instead of starting a
|
|
15
15
|
* duplicate request.
|
|
16
16
|
*/
|
|
17
|
+
import { type PrefetchScope } from "./cache.js";
|
|
17
18
|
import type { RscPayload } from "../types.js";
|
|
18
19
|
/**
|
|
19
20
|
* Decoder injected at app startup (see setPrefetchDecoder). This is
|
|
@@ -29,9 +30,31 @@ type PrefetchDecoder = (response: Promise<Response>) => Promise<RscPayload>;
|
|
|
29
30
|
* browser app always sets it before any Link can fire.
|
|
30
31
|
*/
|
|
31
32
|
export declare function setPrefetchDecoder(fn: PrefetchDecoder): void;
|
|
33
|
+
/** Where a completed prefetch is stored, and the label of a params scope. */
|
|
34
|
+
export interface PrefetchSlot {
|
|
35
|
+
scope: PrefetchScope;
|
|
36
|
+
key: string;
|
|
37
|
+
paramsLabel?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Where a completed prefetch is stored: under the source key when the Link
|
|
41
|
+
* forced it or the response says `X-RSC-Prefetch-Scope: source`; under the
|
|
42
|
+
* label's key when it says `params; <label>`, for the pages with equal values
|
|
43
|
+
* of what a revalidate() rule read; else under the key every source page
|
|
44
|
+
* shares (#1331). `stateKey` is the state's epoch the other keys were built
|
|
45
|
+
* with (cookie-name.ts stateEpoch).
|
|
46
|
+
*/
|
|
47
|
+
export declare function prefetchStorageSlot(forceSourceScope: boolean, scopeHeader: string | null, sourceKey: string, wildcardKey: string, stateKey: string, targetUrl: URL): PrefetchSlot;
|
|
32
48
|
/**
|
|
33
49
|
* Prefetch (direct): fetch with low priority and store in in-memory cache.
|
|
34
|
-
* Used by hover
|
|
50
|
+
* Used by hover (on hover and viewport links, and delegated anchors) and
|
|
51
|
+
* useRouter().prefetch() -- fires immediately without queueing.
|
|
52
|
+
*
|
|
53
|
+
* An intent prefetch: the visitor is about to click, so it asks for the full
|
|
54
|
+
* response the navigation would get, `prefetch: false` loaders and flagged
|
|
55
|
+
* `loading()` work included. It runs only on a miss: a cached prefetch or one
|
|
56
|
+
* in flight, even a speculative one that deferred work, is used as it is,
|
|
57
|
+
* and its click sends the fill as before (docs/design/prefetch-false.md, R16).
|
|
35
58
|
*
|
|
36
59
|
* By default the wildcard key (Rango-state-keyed) is used for inflight
|
|
37
60
|
* dedup and for responses that are not source-sensitive; source-scoped
|
|
@@ -44,7 +67,8 @@ export declare function setPrefetchDecoder(fn: PrefetchDecoder): void;
|
|
|
44
67
|
export declare function prefetchDirect(url: string, segmentIds: string[], version?: string, routerId?: string, prefetchKey?: ":source"): void;
|
|
45
68
|
/**
|
|
46
69
|
* Prefetch (queued): goes through the concurrency-limited queue.
|
|
47
|
-
* Used by viewport/render strategies to avoid flooding the server.
|
|
70
|
+
* Used by viewport/render strategies to avoid flooding the server. A
|
|
71
|
+
* speculative prefetch: the server defers `prefetch: false` work in it.
|
|
48
72
|
* Returns the inflight key (wildcard by default, source-scoped when
|
|
49
73
|
* `prefetchKey=":source"` is passed).
|
|
50
74
|
*/
|
|
@@ -42,7 +42,8 @@ export declare function enqueuePrefetch(key: string, execute: (signal: AbortSign
|
|
|
42
42
|
export declare function cancelAllPrefetches(keepUrl?: string | null): void;
|
|
43
43
|
/**
|
|
44
44
|
* Hard-cancel everything including in-flight prefetches.
|
|
45
|
-
* Used by clearPrefetchCache (
|
|
46
|
-
* in-flight responses would be stale.
|
|
45
|
+
* Used by clearPrefetchCache (invalidateClientCache(), a deploy) where
|
|
46
|
+
* in-flight responses would be stale. A server action never calls it: it
|
|
47
|
+
* marks prefetches stale and lets in-flight ones finish (cache.ts).
|
|
47
48
|
*/
|
|
48
49
|
export declare function abortAllPrefetches(): void;
|
|
@@ -6,11 +6,25 @@
|
|
|
6
6
|
* requests; the server responds with `Vary: X-Rango-State`, so the browser HTTP
|
|
7
7
|
* cache keys responses by (URL, X-Rango-State value).
|
|
8
8
|
*
|
|
9
|
-
* Value format: `{buildVersion}:{invalidationTimestamp}`
|
|
9
|
+
* Value format: `{buildVersion}:{invalidationTimestamp}[:{write}]`
|
|
10
10
|
* - Build version: the router's document version. It changes when a deploy
|
|
11
11
|
* changes the router's server code or the client assets, busting all cached
|
|
12
12
|
* prefetches at boot; a deploy that changes neither keeps them.
|
|
13
|
-
* - Timestamp rotates on invalidation (
|
|
13
|
+
* - Timestamp rotates on invalidation (invalidateClientCache, either seat).
|
|
14
|
+
* - Write: bumped by the response of a server action without a directive (its
|
|
15
|
+
* `Set-Cookie`, server side, so a no-JS action bumps it too); left out while
|
|
16
|
+
* 0. It changes the `X-Rango-State` every tab and every reload sends, so
|
|
17
|
+
* responses the browser HTTP cache holds from before the write miss.
|
|
18
|
+
*
|
|
19
|
+
* The client's in-memory comparisons read only `{buildVersion}:{timestamp}`
|
|
20
|
+
* (`stateEpoch`): the prefetch cache keys, the action fence's rotation check,
|
|
21
|
+
* the external-rotation observer below and the cross-tab adopt. A write-only
|
|
22
|
+
* change is soft: prefetches and history entries are marked stale and nothing
|
|
23
|
+
* is flushed. The tab that ran the action marks its own when it lands
|
|
24
|
+
* (navigation-store.ts markStaleSoftAndBroadcast) and tells the tabs that
|
|
25
|
+
* share a segment with it. Any other tab marks its own when a read sees the
|
|
26
|
+
* write part change (the write observer below), so a tab that ignores the
|
|
27
|
+
* broadcast or has crossTabSync off does not serve a pre-write prefetch fresh.
|
|
14
28
|
*
|
|
15
29
|
* Storage is a session cookie named by the server-resolved name passed to
|
|
16
30
|
* initRangoState (`{prefix}_{routerId}`, default prefix `rango-state`). The
|
|
@@ -27,10 +41,20 @@
|
|
|
27
41
|
*/
|
|
28
42
|
/**
|
|
29
43
|
* Register the observer invoked when a read detects an EXTERNAL rotation (a
|
|
30
|
-
* sibling tab, a server `Set-Cookie`, or a cookie clear)
|
|
44
|
+
* sibling tab, a server `Set-Cookie`, or a cookie clear) of the
|
|
45
|
+
* `{version}:{timestamp}` part. A change to the write part alone (a server
|
|
46
|
+
* action's write) goes to the write observer instead. Self-rotations
|
|
31
47
|
* (invalidateRangoState) update the mirror synchronously and never fire it.
|
|
32
48
|
*/
|
|
33
49
|
export declare function setRangoStateObserver(observer: ((value: string) => void) | null): void;
|
|
50
|
+
/**
|
|
51
|
+
* Register the observer invoked when a read sees the write part change while
|
|
52
|
+
* the `{version}:{timestamp}` part stays: a server action's response, in this
|
|
53
|
+
* tab or another, bumped it in the shared jar since the last read. Every read
|
|
54
|
+
* updates the mirror, and a prefetch reads the state before it starts, so a
|
|
55
|
+
* prefetch sent with the new value is never one this observer sees as old.
|
|
56
|
+
*/
|
|
57
|
+
export declare function setRangoStateWriteObserver(observer: (() => void) | null): void;
|
|
34
58
|
/** Return the resolved cookie name used to namespace cross-tab messages. */
|
|
35
59
|
export declare function getRangoStateCookieName(): string;
|
|
36
60
|
/**
|
|
@@ -49,10 +73,15 @@ export declare function initRangoState(version: string, stateCookieName?: string
|
|
|
49
73
|
export declare function getRangoState(): string;
|
|
50
74
|
/**
|
|
51
75
|
* Invalidate the Rango state (self-rotation). Called when the client clears its
|
|
52
|
-
* prefetch caches (e.g.
|
|
76
|
+
* prefetch caches (e.g. invalidateClientCache()). Rotates the timestamp,
|
|
53
77
|
* keeps the version, writes the cookie, and updates the mirror synchronously so
|
|
54
78
|
* the external-rotation observer is NOT triggered by our own write.
|
|
55
79
|
*/
|
|
56
80
|
export declare function invalidateRangoState(): void;
|
|
57
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* Adopt the state carried by a same-origin cache-invalidation broadcast when
|
|
83
|
+
* its timestamp is newer (a rotation). The write part is not compared: a soft
|
|
84
|
+
* broadcast's newer write is already in the shared jar, and a value that
|
|
85
|
+
* differs only there is no rotation to adopt.
|
|
86
|
+
*/
|
|
58
87
|
export declare function adoptRangoState(value: string): boolean;
|