@rangojs/router 0.19.1 → 0.21.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 +26 -14
- package/dist/types/browser/event-controller.d.ts +32 -0
- package/dist/types/browser/history-state.d.ts +16 -4
- package/dist/types/browser/navigation-transaction.d.ts +7 -0
- package/dist/types/browser/rango-state.d.ts +3 -1
- package/dist/types/browser/react/context.d.ts +11 -0
- package/dist/types/browser/react/location-state-shared.d.ts +84 -10
- package/dist/types/browser/react/location-state.d.ts +7 -2
- package/dist/types/browser/react/subscribe-to-nothing.d.ts +7 -0
- package/dist/types/cache/cache-scope.d.ts +4 -5
- package/dist/types/cache/cf/cf-cache-store.d.ts +27 -6
- package/dist/types/cache/cf/cf-cache-types.d.ts +12 -4
- package/dist/types/cache/handle-snapshot.d.ts +36 -50
- package/dist/types/cache/index.d.ts +2 -0
- package/dist/types/cache/shell-snapshot.d.ts +19 -3
- package/dist/types/cache/types.d.ts +21 -10
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +28 -16
- package/dist/types/client-urls/navigation.d.ts +7 -1
- package/dist/types/client-urls/optimistic-location.d.ts +10 -4
- package/dist/types/handles/deferred-resolution.d.ts +11 -1
- package/dist/types/prerender/build-shell-capture.d.ts +8 -6
- package/dist/types/router/match-middleware/cache-lookup.d.ts +1 -1
- package/dist/types/router/navigation-snapshot.d.ts +8 -1
- package/dist/types/router/router-interfaces.d.ts +2 -1
- package/dist/types/router/router-options.d.ts +17 -7
- package/dist/types/router/segment-resolution/loader-cache.d.ts +21 -9
- package/dist/types/router-versions.d.ts +39 -0
- package/dist/types/rsc/handler-context.d.ts +5 -0
- package/dist/types/rsc/shell-build-manifest.d.ts +3 -2
- package/dist/types/rsc/types.d.ts +4 -6
- package/dist/types/server/build-version-table.d.ts +44 -0
- package/dist/types/server/handle-store.d.ts +87 -46
- package/dist/types/server/request-context.d.ts +22 -8
- package/dist/types/ssr/entry-preload-priority.d.ts +69 -0
- package/dist/types/ssr/index.d.ts +1 -0
- package/dist/types/ssr/preinit-client-references.d.ts +17 -5
- package/dist/types/testing/build-versions.d.ts +32 -0
- package/dist/types/testing/dom.entry.d.ts +1 -1
- package/dist/types/testing/flight.entry.d.ts +1 -1
- package/dist/types/testing/index.d.ts +2 -0
- package/dist/types/testing/internal/seed-vars.d.ts +7 -2
- package/dist/types/testing/location-state-key.d.ts +5 -1
- package/dist/types/testing/render-route.d.ts +77 -6
- package/dist/types/testing/serve-shell-request.d.ts +21 -0
- package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
- package/dist/types/types/handler-context.d.ts +8 -5
- package/dist/types/vite/discovery/build-versions.d.ts +393 -0
- package/dist/types/vite/discovery/installed-packages.d.ts +26 -0
- package/dist/types/vite/discovery/router-versions-phase.d.ts +52 -0
- package/dist/types/vite/discovery/shell-prerender-phase.d.ts +7 -17
- package/dist/types/vite/discovery/state.d.ts +14 -0
- package/dist/types/vite/encryption-key.d.ts +18 -0
- package/dist/types/vite/plugin-types.d.ts +47 -6
- package/dist/types/vite/plugins/{version-injector.d.ts → entry-bootstrap-injector.d.ts} +4 -6
- package/dist/types/vite/plugins/expose-ids/export-analysis.d.ts +1 -0
- package/dist/types/vite/plugins/expose-ids/router-transform.d.ts +14 -0
- package/dist/types/vite/plugins/stable-rsc-output.d.ts +51 -0
- package/dist/types/vite/plugins/version-plugin.d.ts +9 -11
- package/dist/types/vite/plugins/virtual-entries.d.ts +21 -2
- package/dist/types/vite/router-discovery.d.ts +6 -0
- package/dist/types/vite/utils/client-chunks.d.ts +24 -2
- package/dist/types/vite/utils/compare-strings.d.ts +7 -0
- package/dist/types/vite/utils/package-resolution.d.ts +8 -0
- package/dist/types/vite/utils/prerender-utils.d.ts +2 -0
- package/dist/vite/index.js +1544 -420
- package/package.json +1 -2
- package/skills/bundle-analysis/SKILL.md +1 -1
- package/skills/cache-guide/SKILL.md +84 -20
- package/skills/caching/SKILL.md +16 -5
- package/skills/client-urls/SKILL.md +16 -0
- package/skills/cloudflare/SKILL.md +2 -1
- package/skills/comparison/references/framework-comparison.md +16 -12
- package/skills/hooks/SKILL.md +3 -0
- package/skills/hooks/handle-and-actions.md +6 -0
- package/skills/hooks/navigation.md +28 -0
- package/skills/hooks/state.md +209 -8
- package/skills/hooks/urls.md +40 -1
- package/skills/host-router/SKILL.md +13 -0
- package/skills/intercept/SKILL.md +6 -0
- package/skills/loader/SKILL.md +19 -1
- package/skills/middleware/SKILL.md +2 -0
- package/skills/ppr/SKILL.md +119 -40
- package/skills/prerender/SKILL.md +7 -3
- package/skills/rango/SKILL.md +8 -7
- package/skills/router-setup/SKILL.md +23 -3
- package/skills/testing/SKILL.md +30 -26
- package/skills/testing/client-components.md +193 -8
- package/skills/testing/handles.md +2 -0
- package/skills/testing/loader.md +1 -1
- package/skills/testing/middleware.md +1 -1
- package/skills/testing/render-handler.md +18 -18
- package/skills/testing/serve-shell-request.md +63 -14
- package/skills/testing/server-actions.md +17 -17
- package/skills/testing/server-tree.md +1 -1
- package/skills/typesafety/generated-files-and-cli.md +1 -1
- package/skills/use-cache/SKILL.md +16 -5
- package/skills/vercel/SKILL.md +3 -3
- package/src/bin/rango.ts +1 -1
- package/src/browser/event-controller.ts +75 -2
- package/src/browser/history-state.ts +39 -17
- package/src/browser/navigation-bridge.ts +41 -18
- package/src/browser/navigation-transaction.ts +15 -10
- package/src/browser/partial-update.ts +10 -2
- package/src/browser/rango-state.ts +3 -1
- package/src/browser/react/Link.tsx +2 -3
- package/src/browser/react/NavigationProvider.tsx +25 -1
- package/src/browser/react/context.ts +13 -0
- package/src/browser/react/location-state-shared.ts +200 -34
- package/src/browser/react/location-state.ts +40 -75
- package/src/browser/react/subscribe-to-nothing.ts +7 -0
- package/src/browser/react/use-handle.ts +20 -1
- package/src/browser/rsc-router.tsx +33 -11
- package/src/browser/server-action-bridge.ts +17 -1
- package/src/cache/cache-scope.ts +18 -8
- package/src/cache/cf/cf-cache-store.ts +75 -55
- package/src/cache/cf/cf-cache-types.ts +12 -4
- package/src/cache/handle-snapshot.ts +69 -87
- package/src/cache/index.ts +4 -0
- package/src/cache/shell-snapshot.ts +35 -5
- package/src/cache/types.ts +21 -10
- package/src/cache/vercel/vercel-cache-store.ts +48 -20
- package/src/client-urls/client-root.tsx +1 -0
- package/src/client-urls/navigation.ts +10 -0
- package/src/client-urls/optimistic-location.ts +10 -4
- package/src/handles/deferred-resolution.ts +31 -0
- package/src/prerender/build-shell-capture.ts +11 -9
- package/src/router/manifest.ts +2 -0
- package/src/router/match-middleware/cache-lookup.ts +11 -18
- package/src/router/navigation-snapshot.ts +25 -6
- package/src/router/request-classification.ts +15 -12
- package/src/router/router-interfaces.ts +2 -1
- package/src/router/router-options.ts +17 -7
- package/src/router/segment-resolution/loader-cache.ts +95 -60
- package/src/router-versions.ts +60 -0
- package/src/rsc/full-payload.ts +6 -2
- package/src/rsc/handler-context.ts +5 -0
- package/src/rsc/handler.ts +14 -4
- package/src/rsc/rsc-rendering.ts +12 -1
- package/src/rsc/shell-build-manifest.ts +10 -7
- package/src/rsc/shell-capture.ts +36 -26
- package/src/rsc/types.ts +4 -6
- package/src/server/build-version-table.ts +115 -0
- package/src/server/handle-store.ts +344 -127
- package/src/server/request-context.ts +62 -11
- package/src/ssr/entry-preload-priority.ts +308 -0
- package/src/ssr/index.tsx +46 -9
- package/src/ssr/preinit-client-references.ts +17 -5
- package/src/ssr/ssr-root.tsx +16 -4
- package/src/testing/build-versions.ts +64 -0
- package/src/testing/dispatch.ts +6 -0
- package/src/testing/dom.entry.ts +1 -0
- package/src/testing/flight.entry.ts +1 -0
- package/src/testing/index.ts +3 -0
- package/src/testing/internal/context.ts +2 -1
- package/src/testing/internal/seed-vars.ts +14 -2
- package/src/testing/location-state-key.ts +18 -8
- package/src/testing/render-handler.ts +2 -1
- package/src/testing/render-route.tsx +277 -43
- package/src/testing/serve-shell-request.ts +80 -11
- package/src/testing/vitest-stubs/version.ts +4 -1
- package/src/types/handler-context.ts +8 -5
- package/src/vite/discovery/build-versions.ts +1281 -0
- package/src/vite/discovery/installed-packages.ts +159 -0
- package/src/vite/discovery/prerender-collection.ts +38 -1
- package/src/vite/discovery/router-versions-phase.ts +336 -0
- package/src/vite/discovery/shell-prerender-phase.ts +28 -38
- package/src/vite/discovery/state.ts +19 -0
- package/src/vite/discovery/virtual-module-codegen.ts +7 -2
- package/src/vite/encryption-key.ts +101 -9
- package/src/vite/plugin-types.ts +48 -6
- package/src/vite/plugins/{version-injector.ts → entry-bootstrap-injector.ts} +7 -22
- package/src/vite/plugins/expose-ids/export-analysis.ts +1 -1
- package/src/vite/plugins/expose-ids/router-transform.ts +36 -15
- package/src/vite/plugins/expose-internal-ids.ts +12 -3
- package/src/vite/plugins/stable-rsc-output.ts +147 -0
- package/src/vite/plugins/version-plugin.ts +19 -24
- package/src/vite/plugins/version.d.ts +17 -2
- package/src/vite/plugins/virtual-entries.ts +40 -7
- package/src/vite/rango.ts +30 -3
- package/src/vite/router-discovery.ts +82 -13
- package/src/vite/utils/client-chunks.ts +84 -14
- package/src/vite/utils/compare-strings.ts +9 -0
- package/src/vite/utils/package-resolution.ts +9 -0
- package/src/vite/utils/prerender-utils.ts +4 -1
- package/src/vite/utils/shared-utils.ts +1 -1
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
* the capture's copy of a value: it is dynamic or has its own cache. A
|
|
19
19
|
* bake-lane loader body that runs on a HIT reads the store too; its pinned
|
|
20
20
|
* container paths stay the pin's (overlayLoaderContainer), and its settled
|
|
21
|
-
* pushes stay the record's (HandleStore.pushRestored).
|
|
21
|
+
* pushes stay the record's (HandleStore.pushRestored). The two go together:
|
|
22
|
+
* a loader whose pin the entry lost runs fresh, and the record's copies of
|
|
23
|
+
* its pushes give way to that run's (restoreHandles).
|
|
22
24
|
*/
|
|
23
25
|
import type { SegmentCacheStore, CacheGetResult, CacheItemResult, CacheItemOptions, CachedEntryData, ShellCacheEntry, ShellSnapshotRecord, CacheReadError } from "./types.js";
|
|
24
26
|
/**
|
|
@@ -172,16 +174,30 @@ export interface ShellLoaderSeedEntry {
|
|
|
172
174
|
* hole-free record: a hole-carrying one always runs.
|
|
173
175
|
*/
|
|
174
176
|
runs: boolean;
|
|
177
|
+
/**
|
|
178
|
+
* The capture that wrote the pin recorded every settled push of the
|
|
179
|
+
* loader's run in the doc record (CachedEntryData.handleOwners): the pin
|
|
180
|
+
* carries the `runs` bit, which captures write since they record those
|
|
181
|
+
* pushes (v0.18). A replay then drops any other settled push of the loader
|
|
182
|
+
* (HandleStore RecordAuthority "pin"). False for an older pin, whose record
|
|
183
|
+
* may hold none and leaves them to the run.
|
|
184
|
+
*/
|
|
185
|
+
complete: boolean;
|
|
175
186
|
}
|
|
176
187
|
/**
|
|
177
188
|
* Materialize the loader-family seed from a shell snapshot for a HIT's tail
|
|
178
189
|
* render: Flight-deserialize each recorded (promise-elided) bake-lane
|
|
179
|
-
* container into a
|
|
180
|
-
*
|
|
190
|
+
* container into a loader-id -> pin Map, which serveShellHit assigns to the
|
|
191
|
+
* tail context's `_shellLoaderSeed` (loader-cache.ts servedPins).
|
|
181
192
|
* Lives here so every snapshot family is decoded in this module (the segment
|
|
182
193
|
* family via {@link SeededShellStore}); the loader family is not a store
|
|
183
194
|
* read, so it seeds the context instead of a store.
|
|
184
195
|
*
|
|
196
|
+
* Keyed by loader, not by the segment a pin was stored under: a loader runs
|
|
197
|
+
* once per capture, so every segment that registers it pinned the same
|
|
198
|
+
* container, and the loader's value and its handle pushes are both looked up
|
|
199
|
+
* by loader. A key that does not parse pins nothing.
|
|
200
|
+
*
|
|
185
201
|
* Deserializations run in parallel; a record that fails to decode is skipped
|
|
186
202
|
* (that loader drifts — the pre-snapshot behavior — instead of failing the
|
|
187
203
|
* HIT). Returns undefined when the snapshot carries no loader records, without
|
|
@@ -43,6 +43,13 @@ export interface CacheGetResult {
|
|
|
43
43
|
* The store deals with serialized data - RSC serialization is handled
|
|
44
44
|
* by the cache provider layer.
|
|
45
45
|
*
|
|
46
|
+
* A store that outlives the process has to keep one build from reading what
|
|
47
|
+
* other code wrote. The built-in stores prefix their keys with the serving
|
|
48
|
+
* router's cache versions; a custom store reads the same pair with
|
|
49
|
+
* `getCacheVersions()` (`@rangojs/router/cache`): `data` for segment entries
|
|
50
|
+
* and items, `document` for responses and shells, and no version on its
|
|
51
|
+
* tag-invalidation records, so an invalidation reaches every version.
|
|
52
|
+
*
|
|
46
53
|
* @typeParam TEnv - Platform bindings type (e.g., Cloudflare env)
|
|
47
54
|
*/
|
|
48
55
|
export interface SegmentCacheStore<TEnv = unknown> {
|
|
@@ -361,13 +368,14 @@ export interface ShellCacheEntry {
|
|
|
361
368
|
/** React.version captured at prerender time; the read-time invalidation gate. */
|
|
362
369
|
reactVersion: string;
|
|
363
370
|
/**
|
|
364
|
-
* Build version captured at prerender time
|
|
365
|
-
* the
|
|
366
|
-
*
|
|
371
|
+
* Build version captured at prerender time: the RSC handler's `version`,
|
|
372
|
+
* which is the router's DOCUMENT version (router-versions.ts) — a hash of
|
|
373
|
+
* its server code and the client asset names by default, a stamp bumped on
|
|
374
|
+
* dev RSC-module edits. The second read-time gate: a persistent shared
|
|
367
375
|
* store (KV/runtime-cache) survives deploys, and an app-code change that
|
|
368
376
|
* keeps the same React version would otherwise leave a stale-build
|
|
369
|
-
* prelude+postponed live under the same key. A
|
|
370
|
-
* putShell received it.
|
|
377
|
+
* prelude+postponed live under the same key. A deploy that changes neither
|
|
378
|
+
* keeps the entry. A custom store returns it as putShell received it.
|
|
371
379
|
*/
|
|
372
380
|
buildVersion: string;
|
|
373
381
|
/**
|
|
@@ -696,10 +704,13 @@ export type SegmentHandleData = Record<string, unknown[]>;
|
|
|
696
704
|
* index (null: not owned). Only a PPR shell capture writes owners: the
|
|
697
705
|
* settled pushes of the loader bodies it ran (an `ssr: false` loader's own,
|
|
698
706
|
* the loaders it awaits, and its own cache() replays), which the record keeps
|
|
699
|
-
* because the prelude rendered them. A
|
|
700
|
-
* owner's value
|
|
701
|
-
*
|
|
702
|
-
*
|
|
703
|
-
*
|
|
707
|
+
* because the prelude rendered them. A restore of the record
|
|
708
|
+
* (handle-snapshot.ts restoreHandles) keeps an owner's value where the
|
|
709
|
+
* request serves that loader from the pin stored with the record
|
|
710
|
+
* (HandleStore.pushRestored: it stands against a run of the loader), on a
|
|
711
|
+
* document HIT and on a navigation replay alike. Every other owner's value
|
|
712
|
+
* is a placeholder (pushPlaceholder): a live-lane loader's, and every
|
|
713
|
+
* owner's where the record has no pin for it. The loader's run, or its own
|
|
714
|
+
* cache() entry, replaces it.
|
|
704
715
|
*/
|
|
705
716
|
export type HandleOwners = Record<string, Record<string, (string | null)[]>>;
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
* 3. The platform guardrails: the 2 MB per-item ceiling (oversized writes
|
|
24
24
|
* silently no-op on Vercel, so we skip + report), the per-item tag cap, and
|
|
25
25
|
* cross-deploy non-reconciliation (TTL/tag updates are not reconciled across
|
|
26
|
-
* deployments -
|
|
26
|
+
* deployments - every key carries the serving router's cache version, see
|
|
27
|
+
* toStoreKey).
|
|
27
28
|
*
|
|
28
29
|
* Dependency stance: this module imports NOTHING from `@vercel/functions`. The
|
|
29
30
|
* runtime cache handle (and `waitUntil`) are injected through the constructor and
|
|
@@ -92,14 +93,20 @@ export type VercelCacheDebug = boolean | ((event: VercelCacheReadDebugEvent) =>
|
|
|
92
93
|
export interface VercelCacheStoreOptions<TEnv = unknown> {
|
|
93
94
|
/**
|
|
94
95
|
* The Vercel Runtime Cache handle - `getCache()` from `@vercel/functions`.
|
|
95
|
-
* Required.
|
|
96
|
-
* entries across deployments, since Vercel does not reconcile TTL/tags between
|
|
97
|
-
* deploys:
|
|
96
|
+
* Required.
|
|
98
97
|
*
|
|
99
98
|
* ```ts
|
|
100
99
|
* import { getCache } from "@vercel/functions";
|
|
101
|
-
* new VercelCacheStore({ cache: getCache(
|
|
100
|
+
* new VercelCacheStore({ cache: getCache() });
|
|
102
101
|
* ```
|
|
102
|
+
*
|
|
103
|
+
* Vercel does not reconcile entries between deployments; the store does it
|
|
104
|
+
* through its keys, which carry the serving router's cache versions (see
|
|
105
|
+
* `version`). So a deploy that changes a router's code stops reading that
|
|
106
|
+
* router's old entries, and a deploy that does not keeps them. A namespace
|
|
107
|
+
* that changes per deployment (`getCache({ namespace: VERCEL_DEPLOYMENT_ID })`)
|
|
108
|
+
* still works and clears the cache on every deploy, which is what you want
|
|
109
|
+
* only if you need that.
|
|
103
110
|
*/
|
|
104
111
|
cache: VercelRuntimeCache;
|
|
105
112
|
/**
|
|
@@ -122,9 +129,15 @@ export interface VercelCacheStoreOptions<TEnv = unknown> {
|
|
|
122
129
|
*/
|
|
123
130
|
keyGenerator?: (ctx: RequestContext<TEnv>, defaultKey: string) => string | Promise<string>;
|
|
124
131
|
/**
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
132
|
+
* Version override, folded into every versioned key as `v/{version}/...`.
|
|
133
|
+
* When set, this exact value is used for cached data and stored HTML alike;
|
|
134
|
+
* changing it makes everything this store wrote before unreachable.
|
|
135
|
+
*
|
|
136
|
+
* Leave it unset for the default: the store keys with the versions of the
|
|
137
|
+
* router serving the request, as CFCacheStore does. Segments and `"use
|
|
138
|
+
* cache"` items use the router's data version; responses and PPR shells use
|
|
139
|
+
* its document version (see `createRouter({ version })`). Tag markers are
|
|
140
|
+
* never versioned, so an invalidation applies to every version.
|
|
128
141
|
*/
|
|
129
142
|
version?: string;
|
|
130
143
|
/**
|
|
@@ -181,8 +194,8 @@ export interface VercelCacheStoreOptions<TEnv = unknown> {
|
|
|
181
194
|
* that propagates expireTag rejections does get the strict guarantee, which is
|
|
182
195
|
* what the unit suite exercises.)
|
|
183
196
|
*
|
|
184
|
-
* Key hashing / family isolation. The store namespaces its
|
|
185
|
-
* `rg:{s|i|r}:{key}` so segment, `"use cache"`, and
|
|
197
|
+
* Key hashing / family isolation. The store namespaces its value tiers as
|
|
198
|
+
* `rg:{s|i|r|h}:{key}` so segment, `"use cache"`, response and shell entries occupy
|
|
186
199
|
* disjoint keyspaces. That separation is only as strong as the cache's
|
|
187
200
|
* `keyHashFunction`: `getCache`'s default is djb2, which folds every key to 32
|
|
188
201
|
* bits (8 hex chars), so at a large live-key count a collision can let one
|
|
@@ -198,11 +211,9 @@ export interface VercelCacheStoreOptions<TEnv = unknown> {
|
|
|
198
211
|
*
|
|
199
212
|
* // One handle per process: getCache() resolves the platform cache on every
|
|
200
213
|
* // call, and the PPR shell and tag-marker memos are kept per handle.
|
|
201
|
-
* //
|
|
202
|
-
* //
|
|
203
|
-
* const runtimeCache = getCache(
|
|
204
|
-
* namespace: process.env.VERCEL_DEPLOYMENT_ID,
|
|
205
|
-
* });
|
|
214
|
+
* // No deployment id in the namespace: the store's keys carry the serving
|
|
215
|
+
* // router's cache versions, so a deploy keeps what its code did not change.
|
|
216
|
+
* const runtimeCache = getCache();
|
|
206
217
|
*
|
|
207
218
|
* export const router = createRouter({
|
|
208
219
|
* cache: () => ({
|
|
@@ -221,7 +232,8 @@ export declare class VercelCacheStore<TEnv = unknown> implements SegmentCacheSto
|
|
|
221
232
|
readonly keyGenerator?: (ctx: RequestContext<TEnv>, defaultKey: string) => string | Promise<string>;
|
|
222
233
|
private readonly cache;
|
|
223
234
|
private readonly waitUntil?;
|
|
224
|
-
|
|
235
|
+
/** The `version` option: used for every versioned family when set. */
|
|
236
|
+
private readonly explicitVersion?;
|
|
225
237
|
private readonly maxItemBytes;
|
|
226
238
|
private readonly memo;
|
|
227
239
|
private readonly handleMemos;
|
|
@@ -7,6 +7,8 @@ export interface ClientUrlNavigationIntent {
|
|
|
7
7
|
/** Absolute destination pathname (mount included) and search ("?..." or ""). */
|
|
8
8
|
readonly pathname: string;
|
|
9
9
|
readonly search: string;
|
|
10
|
+
/** `history.state` the navigation will push (OptimisticLocation.state). */
|
|
11
|
+
readonly state: unknown;
|
|
10
12
|
/**
|
|
11
13
|
* transition({ when }) gated this navigation off at the swap: the
|
|
12
14
|
* destination presents urgently instead of in a transition lane.
|
|
@@ -26,6 +28,8 @@ export interface ClientUrlDestination {
|
|
|
26
28
|
* params minus these are the mount's (include prefix) params.
|
|
27
29
|
*/
|
|
28
30
|
readonly originLocalParamNames: readonly string[];
|
|
31
|
+
/** `history.state` the navigation will push (ClientUrlNavigationIntent.state). */
|
|
32
|
+
readonly state: unknown;
|
|
29
33
|
}
|
|
30
34
|
export declare function setActiveInterceptTargets(targets: readonly string[] | undefined): void;
|
|
31
35
|
export interface ClientUrlNavigationPresentation {
|
|
@@ -39,7 +43,9 @@ export interface ClientUrlNavigationPresentation {
|
|
|
39
43
|
clear(): void;
|
|
40
44
|
}
|
|
41
45
|
export declare function registerClientUrlGroup(definition: ClientUrlPatterns, mount: string, namePrefix: string, setIntent: (intent: ClientUrlNavigationIntent | null) => void, routeId?: string): () => void;
|
|
42
|
-
export declare function beginClientUrlNavigation(targetUrl: URL, signal: AbortSignal,
|
|
46
|
+
export declare function beginClientUrlNavigation(targetUrl: URL, signal: AbortSignal,
|
|
47
|
+
/** Builds the entry state; called only when the group presents the target. */
|
|
48
|
+
buildState?: () => unknown, decideTransition?: (destination: ClientUrlDestination) => boolean): ClientUrlNavigationPresentation | null;
|
|
43
49
|
/**
|
|
44
50
|
* Run the held clientUrls route's per-loader revalidate() predicates and
|
|
45
51
|
* encode their decisions for the request header. Predicates are CLIENT code
|
|
@@ -3,15 +3,21 @@ import { type Context } from "react";
|
|
|
3
3
|
* Route identity of an optimistically rendered clientUrls() destination:
|
|
4
4
|
* the values the local trie match produced for the URL the user navigated
|
|
5
5
|
* to. Provided by ClientUrlsRoot around the optimistic branch ONLY, so
|
|
6
|
-
* useParams / usePathname / useSearchParams inside that
|
|
7
|
-
* route being rendered while the same hooks in chrome
|
|
8
|
-
* committed location until the canonical response commits
|
|
9
|
-
* which case the branch — and these values — are discarded).
|
|
6
|
+
* useParams / usePathname / useSearchParams / useLocationState inside that
|
|
7
|
+
* branch describe the route being rendered while the same hooks in chrome
|
|
8
|
+
* outside it keep the committed location until the canonical response commits
|
|
9
|
+
* (or redirects, in which case the branch — and these values — are discarded).
|
|
10
10
|
*/
|
|
11
11
|
export interface OptimisticLocation {
|
|
12
12
|
readonly params: Readonly<Record<string, string>>;
|
|
13
13
|
readonly pathname: string;
|
|
14
14
|
/** Search string including the leading "?" (or ""). */
|
|
15
15
|
readonly search: string;
|
|
16
|
+
/**
|
|
17
|
+
* `history.state` the navigation will push: its `state` option, without
|
|
18
|
+
* what the server adds (that arrives with the canonical commit). History
|
|
19
|
+
* still holds the entry being left, so useLocationState reads this instead.
|
|
20
|
+
*/
|
|
21
|
+
readonly state: unknown;
|
|
16
22
|
}
|
|
17
23
|
export declare const OptimisticLocationContext: Context<OptimisticLocation | null>;
|
|
@@ -39,8 +39,18 @@ export declare function resolveDeferredHandleValues(data: HandleData): Promise<H
|
|
|
39
39
|
* A hung handle promise blocks the full render like any unresolved await (no
|
|
40
40
|
* handle-specific timeout). Partial / action payloads keep streaming via
|
|
41
41
|
* `handleStore.stream()`.
|
|
42
|
+
*
|
|
43
|
+
* `recordedOnly` is a PPR shell capture's render (full-payload.ts): the
|
|
44
|
+
* snapshot is cut down to the pushes the capture's record keeps
|
|
45
|
+
* (getDataForSegment's `excludeLoaderPushes`, the filter captureHandles
|
|
46
|
+
* writes the record with). A HIT restores that record and hydrates with it,
|
|
47
|
+
* so a push the record leaves out must not be in the prelude either: it
|
|
48
|
+
* rendered an element no HIT's hydration data matched (issue #1035). Which
|
|
49
|
+
* pushes a record keeps is the capture funnel's decision (shell-capture.ts
|
|
50
|
+
* deriveShellCaptureContext: today it leaves out a deferred loader push and
|
|
51
|
+
* one holding a masked promise), not this function's.
|
|
42
52
|
*/
|
|
43
|
-
export declare function resolvedHandleStream(handleStore: HandleStore): AsyncGenerator<HandleData, void, unknown>;
|
|
53
|
+
export declare function resolvedHandleStream(handleStore: HandleStore, recordedOnly?: boolean): AsyncGenerator<HandleData, void, unknown>;
|
|
44
54
|
/**
|
|
45
55
|
* Resolve the deferred (Promise) values in ONE segment's handle bucket
|
|
46
56
|
* (`handleName -> entries[]`). Same shallow + drop-rejections rule as
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* identity, the same eligibility rules as at runtime.
|
|
21
21
|
*/
|
|
22
22
|
import type { ShellCacheEntry } from "../cache/types.js";
|
|
23
|
+
import type { RouterVersions } from "../router-versions.js";
|
|
23
24
|
import type { SSRModule } from "../rsc/types.js";
|
|
24
25
|
import { type ResolvedPprConfig } from "../rsc/shell-serve.js";
|
|
25
26
|
/**
|
|
@@ -67,13 +68,14 @@ export interface BuildShellCaptureOptions {
|
|
|
67
68
|
/** Build-time env bindings (rango plugin buildEnv), if configured. */
|
|
68
69
|
buildEnv?: unknown;
|
|
69
70
|
/**
|
|
70
|
-
* The
|
|
71
|
-
*
|
|
72
|
-
* serve-side isValidShellHit gate compares
|
|
73
|
-
* running worker's ctx.version
|
|
74
|
-
*
|
|
71
|
+
* The versions the SHIPPED build serves this router with (the build table,
|
|
72
|
+
* or the router's consumer-set version) — NOT the temp server's own
|
|
73
|
+
* version-plugin stamp. The serve-side isValidShellHit gate compares
|
|
74
|
+
* entry.buildVersion against the running worker's ctx.version, which is the
|
|
75
|
+
* document version; stamping the temp server's would make every build entry
|
|
76
|
+
* an eternal MISS.
|
|
75
77
|
*/
|
|
76
|
-
|
|
78
|
+
versions: RouterVersions;
|
|
77
79
|
/**
|
|
78
80
|
* The SSR half, composed by the plugin from the temp server's SSR
|
|
79
81
|
* environment runner (react-dom/static prerender + Flight client), with the
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
* - Action context (if POST)
|
|
93
93
|
*/
|
|
94
94
|
import type { ResolvedSegment } from "../../types.js";
|
|
95
|
-
import type
|
|
95
|
+
import { type EntryData } from "../../server/context.js";
|
|
96
96
|
import type { MatchContext, MatchPipelineState } from "../match-context.js";
|
|
97
97
|
/**
|
|
98
98
|
* Whether the prerender store holds a baked entry for this route + params.
|
|
@@ -25,8 +25,15 @@ export interface ResolveNavigationDeps {
|
|
|
25
25
|
* never produce a partial match, so the replay path must decide
|
|
26
26
|
* `no-navigation-context` BEFORE spending shell-store reads — the two
|
|
27
27
|
* predicates drifting apart would misattribute the bypass again.
|
|
28
|
+
*
|
|
29
|
+
* `requestOrigin` is the origin pathname matching uses (`url.origin`), which
|
|
30
|
+
* may not be `request.url`'s. `X-RSC-Router-Client-Path` is returned as sent.
|
|
31
|
+
* A Referer counts only when it resolves against that origin and stays there.
|
|
32
|
+
* A cross-origin Referer is dropped: resolveNavigation matches only the
|
|
33
|
+
* pathname, so a foreign URL would be treated as a route on this app. A value
|
|
34
|
+
* that does not parse is no context either.
|
|
28
35
|
*/
|
|
29
|
-
export declare function getNavigationContextHeader(request: Request): string | null;
|
|
36
|
+
export declare function getNavigationContextHeader(request: Request, requestOrigin: string): string | null;
|
|
30
37
|
/**
|
|
31
38
|
* Whether withCacheLookup's prerender short-circuit will serve this partial
|
|
32
39
|
* from the build-time prerender store: a Prerender() match (`matched.pr`)
|
|
@@ -357,7 +357,8 @@ export interface RangoInternal<TEnv = any, TRoutes extends Record<string, unknow
|
|
|
357
357
|
*/
|
|
358
358
|
readonly nonce?: NonceProvider<TEnv>;
|
|
359
359
|
/**
|
|
360
|
-
*
|
|
360
|
+
* The consumer-set version (createRouter({ version })), used for both of the
|
|
361
|
+
* router's versions. Undefined when the router uses its build versions.
|
|
361
362
|
*/
|
|
362
363
|
readonly version?: string;
|
|
363
364
|
/**
|
|
@@ -466,13 +466,23 @@ export interface RangoOptions<TEnv = any> {
|
|
|
466
466
|
*/
|
|
467
467
|
nonce?: NonceProvider<TEnv>;
|
|
468
468
|
/**
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
469
|
+
* Version override. When set, this exact value is used for both of the
|
|
470
|
+
* router's versions: it keys the router's cached data (segments, `"use
|
|
471
|
+
* cache"` values, loader data) and its stored HTML (PPR shells,
|
|
472
|
+
* document-cache responses), is sent in payload metadata, and is what the
|
|
473
|
+
* browser's `_rsc_v` is compared with (a mismatch reloads the tab).
|
|
474
|
+
*
|
|
475
|
+
* Leave it unset for the default: rango computes two versions per router at
|
|
476
|
+
* build time, a data version from the router's server code and a document
|
|
477
|
+
* version that also covers the client assets. The same code builds to the
|
|
478
|
+
* same versions, so a deploy that does not change this router keeps its
|
|
479
|
+
* cache. In dev both are a stamp bumped on every RSC module edit.
|
|
480
|
+
*
|
|
481
|
+
* Set it to clear this router's cache on demand (change the value), or to
|
|
482
|
+
* tie the cache to your own release id (a per-deploy value clears the cache
|
|
483
|
+
* on every deploy).
|
|
484
|
+
*
|
|
485
|
+
* @default the router's build versions
|
|
476
486
|
*/
|
|
477
487
|
version?: string;
|
|
478
488
|
/**
|
|
@@ -42,6 +42,8 @@
|
|
|
42
42
|
*/
|
|
43
43
|
import { type EntryData, type LoaderEntry } from "../../server/context.js";
|
|
44
44
|
import type { HandlerContext } from "../../types.js";
|
|
45
|
+
import { type OwnedPushDelivery } from "../../cache/handle-snapshot.js";
|
|
46
|
+
import { type RequestContext } from "../../server/request-context.js";
|
|
45
47
|
/**
|
|
46
48
|
* Whether a request over the matched chain `entries` resolves a loader bound
|
|
47
49
|
* with its own cache(): on a chain entry, or on its parallel slots, orphan
|
|
@@ -51,15 +53,6 @@ import type { HandlerContext } from "../../types.js";
|
|
|
51
53
|
* Memoized per entry.
|
|
52
54
|
*/
|
|
53
55
|
export declare function bindsLoaderCache(entries: readonly EntryData[]): boolean;
|
|
54
|
-
/**
|
|
55
|
-
* The ids of the loaders a request over the matched chain `entries` runs on
|
|
56
|
-
* the live lane (no `ssr: false`), on a chain entry, its orphan layouts or
|
|
57
|
-
* its parallel slots. A document HIT tail restores the record's pushes of
|
|
58
|
-
* every other owner as authoritative (withCacheLookup, HandleStore
|
|
59
|
-
* pushRestored); a live-lane loader is a hole, so its live run replaces its
|
|
60
|
-
* recorded pushes instead. Memoized per entry.
|
|
61
|
-
*/
|
|
62
|
-
export declare function liveLaneLoaderIds(entries: readonly EntryData[]): ReadonlySet<string>;
|
|
63
56
|
/**
|
|
64
57
|
* The lane of each loader the matched chain `entries` registers: "live" (no
|
|
65
58
|
* `ssr: false`) or "bake". A loader registered on both is "live". A PPR
|
|
@@ -67,6 +60,25 @@ export declare function liveLaneLoaderIds(entries: readonly EntryData[]): Readon
|
|
|
67
60
|
* one is live (shell-capture.ts deriveShellCaptureContext).
|
|
68
61
|
*/
|
|
69
62
|
export declare function routeLoaderLanes(entries: readonly EntryData[]): ReadonlyMap<string, "live" | "bake">;
|
|
63
|
+
/**
|
|
64
|
+
* What a record restored on a request over the matched chain `entries` is to
|
|
65
|
+
* each loader's handle pushes (HandleStore RecordAuthority), from the pins
|
|
66
|
+
* the request serves (servedPins). Call it when the record hits: the pins are
|
|
67
|
+
* read here, once, because a navigation replay arms its seed for the match
|
|
68
|
+
* only and the store asks while loader bodies run.
|
|
69
|
+
*
|
|
70
|
+
* Each side applies its own lane to the pin. The value is per registration
|
|
71
|
+
* (resolveLoaderData: an `ssr: false` registration is served its pin). The
|
|
72
|
+
* pushes are per loader: one the route also registers without `ssr: false`
|
|
73
|
+
* is a hole whatever its other registration's pin says, because the capture
|
|
74
|
+
* credited its pushes to the live registration's run (routeLoaderLanes).
|
|
75
|
+
*
|
|
76
|
+
* A dependency the route does not register has no pin of its own, and the
|
|
77
|
+
* record does not name the loader that ran it at capture: its copies stand
|
|
78
|
+
* only while every `ssr: false` loader is pinned (exact when the pins are
|
|
79
|
+
* all there or all gone).
|
|
80
|
+
*/
|
|
81
|
+
export declare function loaderPins(entries: readonly EntryData[], reqCtx: RequestContext<any> | undefined): OwnedPushDelivery;
|
|
70
82
|
/**
|
|
71
83
|
* Resolve loader data with optional caching.
|
|
72
84
|
*
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-router cache versions: the shapes shared by the build (which computes
|
|
3
|
+
* them, vite/discovery/build-versions.ts) and the runtime (which reads them,
|
|
4
|
+
* server/build-version-table.ts). No imports, so the Vite plugin bundle can
|
|
5
|
+
* use it without resolving the `@rangojs/router:version` virtual module.
|
|
6
|
+
*
|
|
7
|
+
* Design: docs/design/per-app-cache-version.md.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* The two versions one `createRouter()` serves with.
|
|
11
|
+
*
|
|
12
|
+
* - `data` keys cached RSC data: segment entries, `"use cache"` values and
|
|
13
|
+
* loader data. It follows the router's server code only.
|
|
14
|
+
* - `document` keys stored HTML (PPR shells, document-cache responses), is the
|
|
15
|
+
* value the browser echoes as `_rsc_v`, and stamps `ShellCacheEntry.buildVersion`.
|
|
16
|
+
* It follows the server code and the client asset URLs.
|
|
17
|
+
*/
|
|
18
|
+
export interface RouterVersions {
|
|
19
|
+
readonly data: string;
|
|
20
|
+
readonly document: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The build's table as the `@rangojs/router:version` module exports it:
|
|
24
|
+
* router id to `[data, document]`. {@link DEFAULT_ROUTER_VERSIONS_KEY} holds
|
|
25
|
+
* the whole-build pair, used for a router the build could not attribute.
|
|
26
|
+
*/
|
|
27
|
+
export type RouterVersionsTable = Readonly<Record<string, readonly [data: string, document: string]>>;
|
|
28
|
+
/** Table key of the whole-build versions. Not a legal `$$id` (8 hex chars). */
|
|
29
|
+
export declare const DEFAULT_ROUTER_VERSIONS_KEY = "*";
|
|
30
|
+
/** One value for both versions: a consumer-set `version`, or the dev stamp. */
|
|
31
|
+
export declare function uniformVersions(version: string): RouterVersions;
|
|
32
|
+
/**
|
|
33
|
+
* The versions a router serves with under `table`: a consumer-set `version`
|
|
34
|
+
* for both; else the router's own entry; else the whole-build pair, for a
|
|
35
|
+
* router the table does not list. `undefined` when there is no table (dev,
|
|
36
|
+
* tests) or it has neither entry. One function for the request handler and
|
|
37
|
+
* the build's shell capture, which must stamp what the handler will check.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveVersionsFrom(table: RouterVersionsTable | undefined, routerId: string | undefined, override: string | undefined): RouterVersions | undefined;
|
|
@@ -12,6 +12,11 @@ import type { RSCDependencies, LoadSSRModule } from "./types.js";
|
|
|
12
12
|
import type { SSRStreamMode } from "../router/router-options.js";
|
|
13
13
|
export interface HandlerContext<TEnv = unknown> {
|
|
14
14
|
router: RangoInternal<TEnv, any>;
|
|
15
|
+
/**
|
|
16
|
+
* The router's DOCUMENT version (router-versions.ts): payload metadata, the
|
|
17
|
+
* `_rsc_v` reload check, and PPR shell stamping and gating. The data version
|
|
18
|
+
* is not here; cache stores read it from the request context.
|
|
19
|
+
*/
|
|
15
20
|
version: string;
|
|
16
21
|
devDiscoveryEpoch?: number;
|
|
17
22
|
renderToReadableStream: RSCDependencies["renderToReadableStream"];
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
* served through the same serveShellHit as a captured one.
|
|
12
12
|
*
|
|
13
13
|
* Lifecycle:
|
|
14
|
-
* - No expiry until the next deploy
|
|
15
|
-
*
|
|
14
|
+
* - No expiry until the next deploy: every build ships its own manifest, and
|
|
15
|
+
* its entries carry that build's document version for the router, which is
|
|
16
|
+
* what the buildVersion gate compares.
|
|
16
17
|
* - `ppr.ttl` drives STALENESS ONLY: past createdAt + ttl the entry still
|
|
17
18
|
* serves, but a runtime recapture is scheduled — SWR is the UPGRADE path
|
|
18
19
|
* from build entry to fresher runtime entry, not the bootstrap path. The
|
|
@@ -321,13 +321,11 @@ export interface CreateRSCHandlerOptions<TEnv = unknown, TRoutes extends Record<
|
|
|
321
321
|
*/
|
|
322
322
|
cache?: HandlerCacheConfig | ((env: TEnv) => HandlerCacheConfig);
|
|
323
323
|
/**
|
|
324
|
-
*
|
|
325
|
-
*
|
|
324
|
+
* Version override for this handler. Same meaning as
|
|
325
|
+
* `createRouter({ version })`, which it takes precedence over: this exact
|
|
326
|
+
* value is used for both of the router's versions.
|
|
326
327
|
*
|
|
327
|
-
*
|
|
328
|
-
* Only set this if you need a custom versioning strategy.
|
|
329
|
-
*
|
|
330
|
-
* @default VERSION from rsc-router:version
|
|
328
|
+
* @default createRouter({ version }), else the router's build versions
|
|
331
329
|
*/
|
|
332
330
|
version?: string;
|
|
333
331
|
/**
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type RouterVersions, type RouterVersionsTable } from "../router-versions.js";
|
|
2
|
+
/**
|
|
3
|
+
* The versions a router serves with, in order:
|
|
4
|
+
*
|
|
5
|
+
* 1. `override`, a consumer-set `version` (createRouter or createRSCHandler):
|
|
6
|
+
* that exact value for both.
|
|
7
|
+
* 2. The router's entry in the build table, else the whole-build entry.
|
|
8
|
+
* 3. `VERSION` for both: the dev stamp, bumped on every RSC module edit.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveRouterVersions(routerId: string | undefined, override?: string): RouterVersions;
|
|
11
|
+
/**
|
|
12
|
+
* The versions of the router serving the current request, for a cache store
|
|
13
|
+
* building a key. A store operation with no request context (a detached task
|
|
14
|
+
* that lost the ALS) gets the whole-build versions: it misses the request's
|
|
15
|
+
* entries instead of reading another version's. When routers have versions of
|
|
16
|
+
* their own that is a write no router reads back, so it is logged, once per
|
|
17
|
+
* process: `ctx.waitUntil()` re-enters the request context for every task it
|
|
18
|
+
* runs (server/request-context.ts), and this line in a production log means
|
|
19
|
+
* some other path lost it.
|
|
20
|
+
*
|
|
21
|
+
* Public from `@rangojs/router/cache`, for a custom persistent store to key
|
|
22
|
+
* the way the built-in ones do. Prefix cached RSC data (segment entries, items) with
|
|
23
|
+
* `data` and stored HTML or responses (shells, document responses) with
|
|
24
|
+
* `document`, and leave tag-invalidation records unversioned; an empty string
|
|
25
|
+
* means "no version" (tests). Call it per operation, inside the request, not
|
|
26
|
+
* in the store's constructor: the cache factory runs before the request
|
|
27
|
+
* context exists.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getCacheVersions(): RouterVersions;
|
|
30
|
+
/** Which of the serving router's versions a store key carries; `null`: none. */
|
|
31
|
+
export type KeyVersion = keyof RouterVersions | null;
|
|
32
|
+
/**
|
|
33
|
+
* The `v/{version}/` prefix of a persistent store key, or "" for an
|
|
34
|
+
* unversioned one. A store-level `version` is used for every versioned kind;
|
|
35
|
+
* otherwise the version is the serving router's, read per operation because
|
|
36
|
+
* the cache factory builds the store before the request context exists.
|
|
37
|
+
*/
|
|
38
|
+
export declare function versionKeyPrefix(storeVersion: string | undefined, kind: KeyVersion): string;
|
|
39
|
+
/**
|
|
40
|
+
* @internal Install a table, or restore the build's with `undefined`. A
|
|
41
|
+
* request handler resolves its router's versions once, when it is created, so
|
|
42
|
+
* a test that swaps the table (a "deploy") needs new handlers.
|
|
43
|
+
*/
|
|
44
|
+
export declare function installRouterVersionsTable(next: RouterVersionsTable | undefined): void;
|