@rangojs/router 0.17.0 → 0.18.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/history-state.d.ts +16 -0
- package/dist/types/cache/cache-key-utils.d.ts +17 -2
- package/dist/types/cache/cache-policy.d.ts +17 -0
- package/dist/types/cache/cache-scope.d.ts +98 -14
- package/dist/types/cache/cache-tag.d.ts +92 -7
- 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 +185 -44
- 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 +34 -0
- package/dist/types/cache/handle-snapshot.d.ts +4 -3
- package/dist/types/cache/index.d.ts +1 -0
- package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
- package/dist/types/cache/shell-memo.d.ts +178 -0
- package/dist/types/cache/shell-snapshot.d.ts +110 -21
- package/dist/types/cache/tag-invalidation.d.ts +30 -3
- package/dist/types/cache/types.d.ts +207 -43
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +111 -13
- package/dist/types/prerender/build-shell-capture.d.ts +11 -4
- package/dist/types/router/instrument.d.ts +8 -0
- package/dist/types/router/middleware-types.d.ts +6 -1
- package/dist/types/router/segment-resolution/loader-cache.d.ts +40 -11
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +21 -10
- package/dist/types/router/segment-resolution/mask-nested.d.ts +65 -24
- package/dist/types/rsc/full-payload.d.ts +14 -0
- package/dist/types/rsc/handler.d.ts +1 -1
- package/dist/types/rsc/shell-build-manifest.d.ts +8 -0
- package/dist/types/rsc/shell-capture-constants.d.ts +53 -6
- package/dist/types/rsc/shell-capture.d.ts +146 -15
- package/dist/types/rsc/shell-serve.d.ts +140 -17
- package/dist/types/rsc/types.d.ts +7 -0
- package/dist/types/server/context.d.ts +109 -15
- package/dist/types/server/cookie-store.d.ts +21 -0
- package/dist/types/server/request-context.d.ts +159 -33
- package/dist/types/ssr/index.d.ts +9 -0
- package/dist/types/ssr/ssr-root.d.ts +9 -8
- package/dist/types/testing/flight.entry.d.ts +7 -0
- package/dist/types/testing/run-loader.d.ts +18 -3
- package/dist/types/testing/serve-shell-request.d.ts +132 -0
- package/dist/types/testing/shell-status.d.ts +23 -10
- package/dist/types/types/handler-context.d.ts +14 -1
- package/dist/types/urls/pattern-types.d.ts +22 -18
- package/dist/types/vite/router-discovery.d.ts +29 -2
- package/dist/vite/index.js +46 -9
- package/package.json +1 -1
- package/skills/breadcrumbs/SKILL.md +17 -7
- package/skills/cache-guide/SKILL.md +123 -51
- package/skills/caching/SKILL.md +253 -23
- package/skills/catalog.json +1 -1
- package/skills/client-urls/SKILL.md +11 -6
- package/skills/cloudflare/SKILL.md +15 -0
- package/skills/comparison/references/framework-comparison.md +44 -32
- package/skills/defer-hydration/SKILL.md +50 -30
- package/skills/deployment-caching/SKILL.md +5 -2
- package/skills/document-cache/SKILL.md +8 -2
- package/skills/handler-use/SKILL.md +2 -0
- package/skills/hooks/handle-and-actions.md +3 -1
- package/skills/hooks/state.md +3 -1
- package/skills/loader/SKILL.md +161 -39
- package/skills/middleware/SKILL.md +16 -9
- package/skills/migrate-nextjs/SKILL.md +67 -36
- 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 +64 -0
- package/skills/parallel/SKILL.md +13 -8
- package/skills/ppr/SKILL.md +655 -363
- package/skills/prerender/SKILL.md +27 -14
- package/skills/rango/SKILL.md +52 -33
- package/skills/route/SKILL.md +12 -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 +25 -21
- package/skills/testing/cache-prerender.md +15 -14
- package/skills/testing/loader.md +18 -1
- package/skills/testing/serve-shell-request.md +188 -0
- package/skills/theme/SKILL.md +52 -4
- package/skills/typesafety/env-and-bindings.md +10 -2
- package/skills/use-cache/SKILL.md +39 -16
- package/skills/vercel/SKILL.md +14 -2
- package/skills/view-transitions/SKILL.md +1 -1
- package/src/browser/cookie-name.ts +23 -0
- package/src/browser/history-state.ts +23 -0
- package/src/browser/rsc-router.tsx +5 -0
- package/src/cache/cache-key-utils.ts +22 -2
- package/src/cache/cache-policy.ts +22 -0
- package/src/cache/cache-runtime.ts +34 -7
- package/src/cache/cache-scope.ts +345 -33
- package/src/cache/cache-tag.ts +375 -11
- 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 +990 -348
- 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 +143 -39
- package/src/cache/handle-snapshot.ts +4 -3
- package/src/cache/index.ts +2 -0
- package/src/cache/isolate-tag-memo.ts +308 -0
- package/src/cache/shell-memo.ts +341 -0
- package/src/cache/shell-snapshot.ts +217 -27
- package/src/cache/tag-invalidation.ts +104 -19
- package/src/cache/types.ts +211 -43
- package/src/cache/vercel/vercel-cache-store.ts +545 -61
- package/src/prerender/build-shell-capture.ts +65 -11
- package/src/route-definition/dsl-helpers.ts +4 -3
- package/src/router/handler-context.ts +38 -4
- package/src/router/instrument.ts +54 -2
- package/src/router/intercept-resolution.ts +19 -7
- package/src/router/loader-resolution.ts +63 -32
- package/src/router/match-api.ts +28 -9
- package/src/router/match-handlers.ts +5 -0
- package/src/router/match-middleware/cache-lookup.ts +17 -0
- package/src/router/match-middleware/cache-store.ts +31 -11
- package/src/router/middleware-types.ts +6 -1
- package/src/router/middleware.ts +23 -4
- package/src/router/prerender-match.ts +6 -0
- package/src/router/segment-resolution/fresh.ts +18 -9
- package/src/router/segment-resolution/helpers.ts +4 -2
- package/src/router/segment-resolution/loader-cache.ts +227 -56
- package/src/router/segment-resolution/loader-snapshot.ts +203 -51
- package/src/router/segment-resolution/mask-nested.ts +136 -42
- package/src/router/segment-resolution/revalidation.ts +27 -10
- package/src/rsc/full-payload.ts +25 -1
- package/src/rsc/handler.ts +14 -2
- package/src/rsc/progressive-enhancement.ts +3 -2
- package/src/rsc/response-cache-serve.ts +9 -6
- package/src/rsc/rsc-rendering.ts +601 -181
- package/src/rsc/shell-build-manifest.ts +27 -9
- package/src/rsc/shell-capture-constants.ts +60 -6
- package/src/rsc/shell-capture.ts +1001 -250
- package/src/rsc/shell-serve.ts +316 -34
- package/src/rsc/types.ts +7 -0
- package/src/server/context.ts +219 -28
- package/src/server/cookie-store.ts +87 -62
- package/src/server/request-context.ts +263 -38
- package/src/ssr/index.tsx +96 -37
- package/src/ssr/ssr-root.tsx +9 -8
- package/src/testing/flight.entry.ts +14 -0
- package/src/testing/run-loader.ts +63 -5
- package/src/testing/serve-shell-request.ts +378 -0
- package/src/testing/shell-status.ts +34 -12
- package/src/theme/ThemeProvider.tsx +7 -6
- package/src/types/handler-context.ts +14 -1
- package/src/urls/pattern-types.ts +22 -18
- package/src/vite/discovery/shell-prerender-phase.ts +9 -0
- package/src/vite/router-discovery.ts +14 -4
package/README.md
CHANGED
|
@@ -524,8 +524,9 @@ parityDescribe("add to cart", (f) => {
|
|
|
524
524
|
```
|
|
525
525
|
|
|
526
526
|
`runMiddleware`, `renderHandler` (Server Component handlers rendered to a
|
|
527
|
-
real Flight tree), `
|
|
528
|
-
the
|
|
527
|
+
real Flight tree), `serveShellRequest` (a `ppr` route's real shell capture and
|
|
528
|
+
HIT), `renderRoute` (DOM tests) and the cache assertions follow the same
|
|
529
|
+
pattern. See the [`/testing` skill](./skills/testing/SKILL.md).
|
|
529
530
|
|
|
530
531
|
## Everything else, when you need it
|
|
531
532
|
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
*/
|
|
13
13
|
/** Default prefix when `stateCookiePrefix` is unset or empty after sanitization. */
|
|
14
14
|
export declare const DEFAULT_STATE_COOKIE_PREFIX = "rango-state";
|
|
15
|
+
/**
|
|
16
|
+
* The fresh-reads cookie name for a router's resolved state cookie name
|
|
17
|
+
* (`{prefix}_{routerId}`; the prefix never contains `_`, see
|
|
18
|
+
* router/state-cookie-name.ts).
|
|
19
|
+
*/
|
|
20
|
+
export declare function freshReadsCookieName(stateCookieName: string): string;
|
|
15
21
|
/** Internal response header carrying the keepClientCache() directive. */
|
|
16
22
|
export declare const KEEP_CACHE_HEADER = "x-rango-keep-cache";
|
|
17
23
|
/** True for a per-client signal header. */
|
|
@@ -18,6 +18,22 @@ export declare function buildHistoryState(userState: unknown, routerState?: {
|
|
|
18
18
|
* relying on the Navigation API.
|
|
19
19
|
*/
|
|
20
20
|
export declare function pushHistoryWithIdx(state: Record<string, unknown> | null, url: string, replace: boolean): void;
|
|
21
|
+
/**
|
|
22
|
+
* The forced-MISS reload marker (`SHELL_MISS_PARAM` in rsc/shell-serve.ts,
|
|
23
|
+
* which the browser bundle must not import): a degraded PPR shell HIT
|
|
24
|
+
* reloads the page with it, and the server renders a marked request like a
|
|
25
|
+
* cache miss.
|
|
26
|
+
*/
|
|
27
|
+
export declare const SHELL_MISS_MARKER: string;
|
|
28
|
+
/**
|
|
29
|
+
* Drop the forced-MISS marker from the address bar at boot, keeping the
|
|
30
|
+
* history entry's state (initBrowserApp calls it before any location read).
|
|
31
|
+
* Left in place, a refresh or a shared link would stay off the shell and the
|
|
32
|
+
* app's search params would carry it. The server strips the same marker from
|
|
33
|
+
* the request before rendering (shell-serve.ts withoutShellMissMarker), so
|
|
34
|
+
* the page was rendered for the clean URL and the hydrating client agrees.
|
|
35
|
+
*/
|
|
36
|
+
export declare function stripShellMissMarker(): void;
|
|
21
37
|
/**
|
|
22
38
|
* Merge server-set location state into the current history entry.
|
|
23
39
|
* Replaces the current history state and dispatches notification event
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Shared Cache Key Utilities
|
|
3
3
|
*
|
|
4
4
|
* Deterministic normalization of search params and route params
|
|
5
|
-
* for cache key generation
|
|
6
|
-
* document-cache, and loader-cache.
|
|
5
|
+
* for cache key generation, and the composition of nested cache() keys.
|
|
6
|
+
* Used by cache-runtime, cache-scope, document-cache, and loader-cache.
|
|
7
7
|
*/
|
|
8
8
|
import type { SearchParamsFilter } from "./search-params-filter.js";
|
|
9
9
|
/**
|
|
@@ -41,3 +41,18 @@ export declare function sortedRouteParams(params: Record<string, string> | undef
|
|
|
41
41
|
* tier-specific suffixes (`:rsc`/`:html`, segment hash) after this base.
|
|
42
42
|
*/
|
|
43
43
|
export declare function cacheKeyBase(host: string, pathname: string, searchParams?: URLSearchParams, params?: Record<string, string>, filter?: SearchParamsFilter): string;
|
|
44
|
+
/**
|
|
45
|
+
* The record key, or shell partition, of nested keyed cache() scopes (#970)
|
|
46
|
+
* from its parts, outermost first: the chain's `key()` results and, for an
|
|
47
|
+
* inner scope without its own `key()`, that scope's default key
|
|
48
|
+
* (CacheScope.resolveKeyFrom). One part is returned raw, so a single keyed
|
|
49
|
+
* scope keeps its key byte for byte. Two or more are joined by `|`, each
|
|
50
|
+
* URI-encoded (like partitionShellKey, rsc/shell-capture-constants.ts): an
|
|
51
|
+
* encoded part holds no `|`, so every tuple maps to its own key, of any
|
|
52
|
+
* depth. Raw, `("a|b", "c")` and `("a", "b|c")` named one record. A composed
|
|
53
|
+
* key holds no `:` either, so a single raw result with one (a prefixed
|
|
54
|
+
* `tier:...`, a `doc:` default key) never equals it; an unprefixed raw result
|
|
55
|
+
* can. Shared with the testing helper `shellCacheKey`, which composes a
|
|
56
|
+
* nested partition the same way.
|
|
57
|
+
*/
|
|
58
|
+
export declare function composeCacheKeys(parts: readonly string[]): string;
|
|
@@ -42,6 +42,23 @@ export declare function resolveTtl(explicit: number | undefined, defaults: Cache
|
|
|
42
42
|
* non-finite/negative (degrade rather than feed bad math into expiry).
|
|
43
43
|
*/
|
|
44
44
|
export declare function resolveSwrWindow(explicit: number | undefined, defaults: CacheDefaults | undefined): number;
|
|
45
|
+
/**
|
|
46
|
+
* Minimum age (ms) of a stale PPR shell before a HIT schedules its recapture.
|
|
47
|
+
* A shell capped to a route cache() record inside its swr window is stale
|
|
48
|
+
* from the start (rsc/shell-capture.ts capShellWindow), so without a floor
|
|
49
|
+
* every HIT of it recaptured, and a capture's store write is one KV write per
|
|
50
|
+
* key: Cloudflare KV accepts one per second per key. Inside the floor the
|
|
51
|
+
* stale shell is still served. Per isolate: each isolate's stale HIT past the
|
|
52
|
+
* floor can schedule one (stampede-guarded per isolate).
|
|
53
|
+
*/
|
|
54
|
+
export declare const SHELL_MIN_RECAPTURE_INTERVAL_MS = 1000;
|
|
55
|
+
/**
|
|
56
|
+
* Whether a stale shell HIT schedules its recapture (see the floor above).
|
|
57
|
+
* Fails open: an entry without a numeric `createdAt` recaptures.
|
|
58
|
+
*/
|
|
59
|
+
export declare function staleShellRecaptureDue(entry: {
|
|
60
|
+
readonly createdAt?: number;
|
|
61
|
+
}, now: number): boolean;
|
|
45
62
|
/**
|
|
46
63
|
* Compute staleAt and expiresAt timestamps from TTL and SWR window.
|
|
47
64
|
*
|
|
@@ -12,6 +12,7 @@ import type { ResolvedSegment } from "../types.js";
|
|
|
12
12
|
import type { SegmentCacheStore } from "./types.js";
|
|
13
13
|
import type { RequestContext } from "../server/request-context.js";
|
|
14
14
|
export declare function resolveCacheTags(config: PartialCacheOptions | false, ctx: RequestContext | undefined): string[] | undefined;
|
|
15
|
+
type CacheKeyFn = NonNullable<PartialCacheOptions["key"]>;
|
|
15
16
|
/**
|
|
16
17
|
* Discriminated outcome of a route cache lookup — see
|
|
17
18
|
* {@link CacheScope.lookupRouteDetailed} for what each status licenses.
|
|
@@ -56,6 +57,18 @@ export declare class CacheScope {
|
|
|
56
57
|
readonly parent: CacheScope | null;
|
|
57
58
|
/** Explicit store from cache() options, if specified */
|
|
58
59
|
private readonly explicitStore;
|
|
60
|
+
/**
|
|
61
|
+
* @internal The `key()` functions that partition this scope's records,
|
|
62
|
+
* outermost first: every cache() on the parent chain that sets one, this
|
|
63
|
+
* scope's last (#970). `cache(false)` adds none and does not cut the chain,
|
|
64
|
+
* so a cache() re-enabled under it stays in the partition. Before, the
|
|
65
|
+
* innermost config alone decided the key: an inner cache() without `key()`
|
|
66
|
+
* wrote under the default key and an inner `key()` dropped the outer one,
|
|
67
|
+
* so a request in one partition HIT another partition's inner records.
|
|
68
|
+
*/
|
|
69
|
+
readonly keyFns: readonly CacheKeyFn[];
|
|
70
|
+
/** Whether this scope's own config sets `key()` (the last of keyFns). */
|
|
71
|
+
private readonly hasOwnKey;
|
|
59
72
|
constructor(config: PartialCacheOptions | false, parent?: CacheScope | null, defaultKeyPrefix?: "doc" | undefined,
|
|
60
73
|
/**
|
|
61
74
|
* @internal shortCode of the outermost cache() entry of this scope's
|
|
@@ -106,6 +119,57 @@ export declare class CacheScope {
|
|
|
106
119
|
* @internal
|
|
107
120
|
*/
|
|
108
121
|
private resolveKey;
|
|
122
|
+
/**
|
|
123
|
+
* A shell captured from a route record this request read or wrote must not
|
|
124
|
+
* outlive it (noteRouteRecordWindow). The implicit doc scope's own record
|
|
125
|
+
* is the shell's, so it notes nothing.
|
|
126
|
+
*/
|
|
127
|
+
private noteRecordWindow;
|
|
128
|
+
/**
|
|
129
|
+
* @internal This scope's record key for `defaultKey`, resolved once per
|
|
130
|
+
* request: a ppr route's shell partition (resolvePartition) and the record
|
|
131
|
+
* lookup share the `key()` runs, and a rejection is shared too.
|
|
132
|
+
*
|
|
133
|
+
* With `key()` functions on the chain (keyFns) the parts are their results,
|
|
134
|
+
* outermost first, composed by composeCacheKeys (a single part raw), so a
|
|
135
|
+
* nested cache() keys its records within the enclosing partition (#970).
|
|
136
|
+
* A scope whose own config sets no `key()` appends its own default key
|
|
137
|
+
* (resolveDefaultKey). Reusing the outer `key()` result alone would give
|
|
138
|
+
* every route under the inner cache() one record: a `key()` replaces the
|
|
139
|
+
* whole default key, so an outer key that names no route (`tier:gold`)
|
|
140
|
+
* would make `/a` HIT `/b`'s record. A scope with its own `key()` adds no
|
|
141
|
+
* default key: a `key()` is a full override, and no keyGenerator runs.
|
|
142
|
+
* Without `key()` on the chain it is resolveDefaultKey alone. With one but
|
|
143
|
+
* no request context it rejects (keyWithoutContextError).
|
|
144
|
+
*
|
|
145
|
+
* Each `key()` result lives on the request context (`_resolvedCacheKeys`),
|
|
146
|
+
* by function: it runs once per request whatever the default key
|
|
147
|
+
* (document, partial or `doc` shell prefix) and however many scopes share
|
|
148
|
+
* it (sibling routes under one keyed cache()). A shell capture, whose
|
|
149
|
+
* context is `Object.create` of the request's (shell-capture.ts), reads the
|
|
150
|
+
* foreground's results through the prototype chain and never runs `key()`
|
|
151
|
+
* itself: a `key()` calling `cookies()` would trip the capture guard there,
|
|
152
|
+
* and a capture must store the partition its request resolved.
|
|
153
|
+
*/
|
|
154
|
+
resolveKeyFrom(defaultKey: string): Promise<string>;
|
|
155
|
+
/**
|
|
156
|
+
* @internal The request partition of a ppr route's shell under this scope
|
|
157
|
+
* (resolveShellPartition): the chain's `key()` results, composed like the
|
|
158
|
+
* record key but without the default key, which the URL-based shell key
|
|
159
|
+
* already carries. A scope without its own `key()` whose store has a
|
|
160
|
+
* keyGenerator adds that result when it differs from the `doc` default
|
|
161
|
+
* key, as an unkeyed scope's partition always did: the record is split by
|
|
162
|
+
* it, so the shell must be too. Undefined (synchronously, with no work)
|
|
163
|
+
* when nothing can partition; null when nothing does.
|
|
164
|
+
*/
|
|
165
|
+
resolvePartition(pathname: string, params: Record<string, string> | undefined): Promise<string | null> | undefined;
|
|
166
|
+
/** The chain's `key()` results, each once per request (memoizedKey). */
|
|
167
|
+
private resolveKeyResults;
|
|
168
|
+
/**
|
|
169
|
+
* This scope's store keyGenerator result for `defaultKey` (once per request
|
|
170
|
+
* and default key), else `defaultKey` (resolveCacheKey).
|
|
171
|
+
*/
|
|
172
|
+
private resolveDefaultKey;
|
|
109
173
|
/**
|
|
110
174
|
* @internal Whether a cache read/write is allowed for the current request:
|
|
111
175
|
* the scope is enabled AND its `condition` (if any) returns true. "read"
|
|
@@ -205,6 +269,21 @@ export declare class CacheScope {
|
|
|
205
269
|
*/
|
|
206
270
|
cacheRoute(pathname: string, params: Record<string, string>, segments: ResolvedSegment[], isIntercept?: boolean): Promise<void>;
|
|
207
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* @internal The request partition of a ppr route's shell: with a route
|
|
274
|
+
* cache() scope, its CacheScope.resolvePartition (the chain's `key()`
|
|
275
|
+
* results, composed, plus a keyGenerator's contribution for a scope without
|
|
276
|
+
* its own `key()` (#970)); with none, the app store's keyGenerator result
|
|
277
|
+
* given the document default key. Undefined (synchronously, with no work)
|
|
278
|
+
* when neither applies: the shell key stays host + path + filtered search.
|
|
279
|
+
* Resolves to null when the partition is the default key itself (a
|
|
280
|
+
* keyGenerator that returns it unchanged): that partitions nothing, so the
|
|
281
|
+
* shell stays unpartitioned and keeps its build shell. The shell's key, its
|
|
282
|
+
* read and capture, and partial replay all use it, so a visitor is only
|
|
283
|
+
* served its own partition. A rejected promise is a failed key resolution:
|
|
284
|
+
* the caller serves no shell.
|
|
285
|
+
*/
|
|
286
|
+
export declare function resolveShellPartition(routeScope: CacheScope | null | undefined, appStore: SegmentCacheStore | null | undefined, pathname: string, params: Record<string, string> | undefined): Promise<string | null> | undefined;
|
|
208
287
|
/**
|
|
209
288
|
* Create a cache scope from entry's cache config. `shortCode` is the cache()
|
|
210
289
|
* entry's: a scope nested in an enabled parent keeps the parent's boundary
|
|
@@ -221,24 +300,29 @@ type ShellImplicitCacheMarker = NonNullable<RequestContext["_shellImplicitCache"
|
|
|
221
300
|
* {@link resolveShellImplicitCacheScope} (routes that derived no scope) and
|
|
222
301
|
* the explicit-scope composition sites (capture doc record in the cache-store
|
|
223
302
|
* middleware, seeded replay fallback in withCacheLookup) so both ends of the
|
|
224
|
-
* shell contract resolve the SAME canonical document key.
|
|
303
|
+
* shell contract resolve the SAME canonical document key. A document HIT
|
|
304
|
+
* tail's marker carries the entry's own key (`fixedDocKey`), which wins over
|
|
305
|
+
* key resolution.
|
|
225
306
|
*/
|
|
226
307
|
export declare function createShellImplicitDocScope(marker: ShellImplicitCacheMarker): CacheScope;
|
|
227
308
|
/**
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
* (resolveFreshLoadersAndYield).
|
|
309
|
+
* The doc record scope: when the current request context carries the
|
|
310
|
+
* `_shellImplicitCache` marker (a shell capture, a document HIT tail, or a
|
|
311
|
+
* normal partial navigation replay), substitute an implicit doc-level scope
|
|
312
|
+
* so withCacheLookup/withCacheStore treat the WHOLE matched route as a
|
|
313
|
+
* cache() boundary — the shell entry IS a cache() of the handler layer, with
|
|
314
|
+
* loaders as the live carve-outs (resolveFreshLoadersAndYield).
|
|
235
315
|
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
316
|
+
* A document HIT tail (`docTail`) always gets the implicit scope: it replays
|
|
317
|
+
* the shell's own record and never consults a route-derived scope, whose
|
|
318
|
+
* opt-outs the serve gate evaluated before the commit (shellServePlan). Every
|
|
319
|
+
* other marker leaves an existing scope — including an explicit cache(false)
|
|
320
|
+
* — in place: the consumer's cache() semantics (their ttl/swr/store/condition)
|
|
321
|
+
* are never overridden. On the navigation-replay serve path the marker still
|
|
322
|
+
* composes with an explicit scope downstream (withCacheLookup's seeded
|
|
323
|
+
* fallback after an explicit-tier miss) — see `onExplicitHit` on
|
|
324
|
+
* `_shellImplicitCache`; during a capture, recordShellCaptureDocRecord
|
|
325
|
+
* (cache-store.ts) writes the record for such a scope.
|
|
242
326
|
*/
|
|
243
327
|
export declare function resolveShellImplicitCacheScope(scope: CacheScope | null): CacheScope | null;
|
|
244
328
|
export {};
|
|
@@ -10,6 +10,54 @@
|
|
|
10
10
|
* profile/DSL tags before storing.
|
|
11
11
|
*/
|
|
12
12
|
import { type RequestContext } from "../server/request-context.js";
|
|
13
|
+
import { type LoaderIdentityRead } from "../server/context.js";
|
|
14
|
+
/**
|
|
15
|
+
* Arm the record tag owners for `ctx`: its match resolved a cache scope, so
|
|
16
|
+
* it can write a route cache() record (match-api.ts). A capture's or
|
|
17
|
+
* background re-render's derived context inherits the flag.
|
|
18
|
+
*/
|
|
19
|
+
export declare function armRecordTagOwners(ctx?: RequestContext | undefined): void;
|
|
20
|
+
/** Whether `ctx` records tag owners (see armRecordTagOwners). */
|
|
21
|
+
export declare function recordTagOwnersArmed(ctx?: RequestContext | undefined): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Call `fn(arg)` inside segment `segmentId`'s tag scope: tags recorded while
|
|
24
|
+
* it or its async continuations run (outside a loader body) are owned by that
|
|
25
|
+
* segment. Unarmed, it is a plain call.
|
|
26
|
+
*/
|
|
27
|
+
export declare function runInSegmentTagScope<R, A = undefined>(segmentId: string, fn: (arg: A) => R, arg?: A): R;
|
|
28
|
+
/**
|
|
29
|
+
* Record that the current owner reads loader `loaderId`'s value (ctx.use),
|
|
30
|
+
* so the loader's tags follow the reader onto a record. The DSL funnel
|
|
31
|
+
* starting a loader (resolveLoaderData, inside the DSL loader scope but no
|
|
32
|
+
* loader body) is not a reader.
|
|
33
|
+
*/
|
|
34
|
+
export declare function linkLoaderTags(loaderId: string, ctx?: RequestContext | undefined): void;
|
|
35
|
+
/**
|
|
36
|
+
* Record `tags` as loader `loaderId`'s own, read outside its body (its
|
|
37
|
+
* cache() config tags, or a loader-cache HIT's stored tags):
|
|
38
|
+
* recordRequestTags with an explicit owner.
|
|
39
|
+
*/
|
|
40
|
+
export declare function recordLoaderTags(loaderId: string, tags: Iterable<string> | undefined, ctx?: RequestContext | undefined): void;
|
|
41
|
+
/**
|
|
42
|
+
* The tags segment `segmentId`'s content recorded in `ctx`: its own, plus
|
|
43
|
+
* those of every loader it consumed, transitively.
|
|
44
|
+
*/
|
|
45
|
+
export declare function getSegmentTags(ctx: RequestContext, segmentId: string): ReadonlySet<string>;
|
|
46
|
+
/**
|
|
47
|
+
* Record `tags` onto each of `segmentIds` (a route cache() HIT replaying
|
|
48
|
+
* those segments): a record written from the replay, such as a shell
|
|
49
|
+
* capture's doc record (match-middleware/cache-store.ts), then carries them
|
|
50
|
+
* as a fresh render's record would. Unarmed, a no-op.
|
|
51
|
+
*/
|
|
52
|
+
export declare function recordSegmentTags(segmentIds: readonly string[], tags: Iterable<string> | undefined, ctx?: RequestContext | undefined): void;
|
|
53
|
+
/**
|
|
54
|
+
* The owner of the loaders a shell capture bakes (loader-cache.ts bake
|
|
55
|
+
* lane): their data is shell material that no handler reads, so their tags
|
|
56
|
+
* reach the shell through this owner (shell-capture.ts), not a record.
|
|
57
|
+
*/
|
|
58
|
+
export declare const SHELL_BAKE_TAG_OWNER: string;
|
|
59
|
+
/** Link loader `loaderId`'s tags to `ownerId` (see SHELL_BAKE_TAG_OWNER). */
|
|
60
|
+
export declare function linkLoaderTagsTo(ownerId: string, loaderId: string, ctx?: RequestContext | undefined): void;
|
|
13
61
|
export declare function normalizeTag(tag: string): string | null;
|
|
14
62
|
export declare function normalizeTags(tags: Iterable<string>): string[];
|
|
15
63
|
/**
|
|
@@ -32,14 +80,15 @@ export declare function normalizeTags(tags: Iterable<string>): string[];
|
|
|
32
80
|
* cache" in its tree. This is PPR's DERIVATIVE invalidation: PPR is
|
|
33
81
|
* execution-PRESERVING (everything still runs underneath; only document bytes
|
|
34
82
|
* are shortcut), so its tags ride this existing instrument rather than a
|
|
35
|
-
* first-class ppr key/tag API.
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
83
|
+
* first-class ppr key/tag API. Whatever renders into the shell records here,
|
|
84
|
+
* at capture or on the route's cache() record the capture replays. A masked
|
|
85
|
+
* live-lane loader never executes at capture, so its own tags reach a shell
|
|
86
|
+
* only through a handler that reads it (ctx.use).
|
|
39
87
|
*
|
|
40
|
-
* Inside a cache()
|
|
41
|
-
*
|
|
42
|
-
*
|
|
88
|
+
* Inside a route cache() boundary the render-callable form also tags that
|
|
89
|
+
* route's cache() record, which re-records it on every HIT (#957). A loader's
|
|
90
|
+
* tags reach the record only when a handler consumes its value (ctx.use);
|
|
91
|
+
* see runInSegmentTagScope / linkLoaderTags. An empty/whitespace-only tag is
|
|
43
92
|
* dropped in both forms (the render-callable form silently, via normalizeTags in
|
|
44
93
|
* recordRequestTags; the scope form with a dev warning).
|
|
45
94
|
*
|
|
@@ -63,6 +112,42 @@ export declare function normalizeTags(tags: Iterable<string>): string[];
|
|
|
63
112
|
*/
|
|
64
113
|
export declare function cacheTag(...tags: string[]): void;
|
|
65
114
|
export declare function recordRequestTags(tags: Iterable<string> | undefined, ctx?: RequestContext | undefined): void;
|
|
115
|
+
/**
|
|
116
|
+
* Arm the per-execution loader tag sets for `ctx`: its match resolved a
|
|
117
|
+
* loader bound with its own cache() (match-api.ts, bindsLoaderCache), or such
|
|
118
|
+
* a binding started (loader-cache.ts). A loader-cache MISS captures its own
|
|
119
|
+
* execution and encodes either way.
|
|
120
|
+
*/
|
|
121
|
+
export declare function armLoaderTagSets(ctx?: RequestContext | undefined): void;
|
|
122
|
+
/** Whether `ctx` records per-execution loader tag sets (armLoaderTagSets). */
|
|
123
|
+
export declare function loaderTagSetsArmed(ctx: RequestContext | undefined): boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Call `fn`, adding every tag recorded in its async chain
|
|
126
|
+
* (recordRequestTags, recordLoaderTags) to `into`, until a loader body
|
|
127
|
+
* started inside it takes over with its own set.
|
|
128
|
+
*/
|
|
129
|
+
export declare function captureRecordedTags<T>(into: Set<string>, fn: () => T): T;
|
|
130
|
+
/** `into` also answers for every tag `from` records. */
|
|
131
|
+
export declare function linkRecordedTags(into: Set<string>, from: Set<string>): void;
|
|
132
|
+
/** Mark a loader's value promise as carrying `tags` (see readValueTags). */
|
|
133
|
+
export declare function tagLoaderValue(value: object, tags: Set<string>): void;
|
|
134
|
+
/** A loader value's tags also answer for `tags` (tagLoaderValue). */
|
|
135
|
+
export declare function linkValueTags(value: object, tags: Set<string>): void;
|
|
136
|
+
/** The current execution reads a loader's value: it takes on its tags. */
|
|
137
|
+
export declare function readValueTags(value: object): void;
|
|
138
|
+
/**
|
|
139
|
+
* A loader-cache HIT restores the read its entry's MISS recorded onto the
|
|
140
|
+
* value's set, so an unkeyed reader of the value refuses it as it would on
|
|
141
|
+
* the MISS (loader-cache.ts identity mark).
|
|
142
|
+
*/
|
|
143
|
+
export declare function markIdentityRead(set: Set<string>, read: LoaderIdentityRead): void;
|
|
144
|
+
/**
|
|
145
|
+
* The identity read `root`'s execution recorded, or one a set it links to
|
|
146
|
+
* recorded, nearest first (breadth-first, so the execution's own read wins).
|
|
147
|
+
*/
|
|
148
|
+
export declare function recordedIdentityRead(root: Set<string>): LoaderIdentityRead | undefined;
|
|
149
|
+
/** `root`'s tags plus those of every set it links to, transitively. */
|
|
150
|
+
export declare function flattenRecordedTags(root: Set<string>): Set<string>;
|
|
66
151
|
/**
|
|
67
152
|
* Run a function within a cache tag scope. Any cacheTag() calls inside `fn`
|
|
68
153
|
* accumulate into the returned Set.
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
/** Encode ArrayBuffer to base64 string. */
|
|
2
2
|
export declare function bufferToBase64(buffer: ArrayBuffer): string;
|
|
3
|
+
/** Encode bytes (a view's own region only) to a base64 string. */
|
|
4
|
+
export declare function bytesToBase64(bytes: Uint8Array): string;
|
|
5
|
+
/** Decode a base64 string to bytes. Throws on a character outside the alphabet. */
|
|
6
|
+
export declare function base64ToBytes(base64: string): Uint8Array;
|
|
7
|
+
/** The byte length `base64` decodes to (padded or not), without decoding. */
|
|
8
|
+
export declare function base64ByteLength(base64: string): number;
|
|
3
9
|
/** Decode base64 string to ArrayBuffer. */
|
|
4
10
|
export declare function base64ToBuffer(base64: string): ArrayBuffer;
|
|
@@ -110,3 +110,14 @@ export declare const EDGE_READ_TIMEOUT_MS = 20;
|
|
|
110
110
|
* Override per store via `CFCacheStoreOptions.kvReadTimeoutMs` (<= 0 disables).
|
|
111
111
|
*/
|
|
112
112
|
export declare const KV_READ_TIMEOUT_MS = 170;
|
|
113
|
+
/**
|
|
114
|
+
* Floor (ms) on the budget for reading a PPR shell's snapshot, the rest of a
|
|
115
|
+
* frame whose head and prelude were already read. A document HIT has sent
|
|
116
|
+
* its prelude by then and cannot render the rest without the snapshot's doc
|
|
117
|
+
* record, so a timed-out read degrades the HIT (the entry is replaced, the
|
|
118
|
+
* page reloads once into a MISS; rsc-rendering.ts degradeUnreplayableShell).
|
|
119
|
+
* That costs far more than waiting, so a slow body read gets at least this
|
|
120
|
+
* long; `kvReadTimeoutMs` above it wins, and `kvReadTimeoutMs <= 0` still
|
|
121
|
+
* disables the bound.
|
|
122
|
+
*/
|
|
123
|
+
export declare const SHELL_SNAPSHOT_READ_MIN_TIMEOUT_MS = 1000;
|