@rangojs/router 0.16.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 +255 -89
- package/dist/testing/vitest.js +252 -0
- package/dist/types/browser/cookie-name.d.ts +6 -0
- package/dist/types/browser/history-state.d.ts +16 -0
- package/dist/types/browser/navigation-transaction.d.ts +6 -0
- package/dist/types/browser/partial-update.d.ts +11 -0
- package/dist/types/browser/react/ScrollRestoration.d.ts +6 -6
- package/dist/types/browser/rsc-router.d.ts +1 -1
- package/dist/types/browser/scroll-restoration.d.ts +7 -0
- package/dist/types/cache/cache-exec-scope.d.ts +18 -1
- 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 +132 -19
- 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-capture.d.ts +41 -3
- package/dist/types/cache/handle-snapshot.d.ts +54 -13
- package/dist/types/cache/index.d.ts +1 -0
- package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
- package/dist/types/cache/segment-codec.d.ts +10 -3
- 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/taint.d.ts +1 -1
- package/dist/types/cache/types.d.ts +220 -44
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +111 -13
- package/dist/types/client.d.ts +4 -4
- package/dist/types/client.rsc.d.ts +4 -5
- package/dist/types/components/DefaultDocument.d.ts +5 -2
- package/dist/types/context-var.d.ts +6 -5
- package/dist/types/escape-script.d.ts +1 -1
- package/dist/types/handles/MetaTags.d.ts +5 -4
- package/dist/types/handles/Scripts.d.ts +8 -7
- package/dist/types/handles/meta.d.ts +2 -2
- package/dist/types/handles/script.d.ts +17 -16
- package/dist/types/href-client.d.ts +1 -1
- package/dist/types/html.d.ts +12 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.rsc.d.ts +3 -2
- package/dist/types/loader-store.d.ts +7 -4
- package/dist/types/prerender/build-shell-capture.d.ts +11 -4
- package/dist/types/reverse.d.ts +36 -4
- package/dist/types/route-definition/dsl-helpers.d.ts +3 -3
- package/dist/types/route-definition/helpers-types.d.ts +33 -9
- package/dist/types/route-definition/resolve-handler-use.d.ts +23 -1
- package/dist/types/route-types.d.ts +1 -1
- package/dist/types/router/debug-manifest.d.ts +3 -2
- package/dist/types/router/error-handling.d.ts +20 -0
- package/dist/types/router/handler-context.d.ts +6 -0
- package/dist/types/router/instrument.d.ts +8 -0
- package/dist/types/router/match-middleware/background-revalidation.d.ts +46 -5
- package/dist/types/router/middleware-types.d.ts +9 -3
- package/dist/types/router/router-context.d.ts +3 -1
- package/dist/types/router/router-interfaces.d.ts +24 -2
- package/dist/types/router/router-options.d.ts +8 -5
- package/dist/types/router/segment-resolution/fresh.d.ts +10 -0
- package/dist/types/router/segment-resolution/loader-cache.d.ts +66 -1
- package/dist/types/router/segment-resolution/loader-mask.d.ts +9 -19
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +23 -12
- package/dist/types/router/segment-resolution/mask-nested.d.ts +67 -26
- package/dist/types/router/segment-resolution/revalidation.d.ts +6 -2
- package/dist/types/router/segment-wrappers.d.ts +1 -1
- package/dist/types/rsc/full-payload.d.ts +14 -0
- package/dist/types/rsc/handler.d.ts +1 -1
- package/dist/types/rsc/response-route-handler.d.ts +1 -1
- package/dist/types/rsc/rsc-rendering.d.ts +1 -2
- package/dist/types/rsc/shell-build-manifest.d.ts +8 -0
- package/dist/types/rsc/shell-capture-constants.d.ts +61 -6
- package/dist/types/rsc/shell-capture.d.ts +151 -19
- package/dist/types/rsc/shell-serve.d.ts +140 -17
- package/dist/types/rsc/ssr-setup.d.ts +6 -6
- package/dist/types/rsc/types.d.ts +17 -0
- package/dist/types/server/context.d.ts +139 -15
- package/dist/types/server/cookie-store.d.ts +21 -0
- package/dist/types/server/handle-store.d.ts +26 -2
- package/dist/types/server/request-context.d.ts +182 -53
- package/dist/types/ssr/index.d.ts +24 -0
- package/dist/types/ssr/ssr-root.d.ts +9 -8
- package/dist/types/static-handler.d.ts +4 -1
- package/dist/types/testing/flight.entry.d.ts +7 -0
- package/dist/types/testing/generated-routes.d.ts +2 -2
- package/dist/types/testing/internal/context.d.ts +1 -1
- package/dist/types/testing/render-route.d.ts +32 -6
- 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/testing/vitest-stubs/plugin-rsc.d.ts +28 -5
- package/dist/types/testing/vitest.d.ts +35 -1
- package/dist/types/theme/ThemeScript.d.ts +3 -3
- package/dist/types/types/cache-types.d.ts +14 -10
- package/dist/types/types/handler-context.d.ts +28 -1
- package/dist/types/types/loader-types.d.ts +8 -6
- package/dist/types/urls/pattern-types.d.ts +22 -18
- package/dist/types/urls/response-types.d.ts +3 -12
- package/dist/types/use-loader.d.ts +12 -3
- package/dist/types/vite/plugins/use-cache-transform.d.ts +33 -2
- package/dist/types/vite/rango.d.ts +1 -1
- package/dist/types/vite/router-discovery.d.ts +29 -2
- package/dist/vite/index.js +1420 -1348
- package/package.json +2 -2
- package/skills/api-client/SKILL.md +22 -10
- package/skills/breadcrumbs/SKILL.md +75 -22
- package/skills/bundle-analysis/SKILL.md +22 -22
- package/skills/cache-guide/SKILL.md +306 -124
- package/skills/caching/SKILL.md +530 -98
- package/skills/catalog.json +16 -16
- package/skills/client-urls/SKILL.md +65 -38
- package/skills/cloudflare/SKILL.md +87 -20
- package/skills/cloudflare/references/d1-and-local-dev.md +15 -2
- package/skills/cloudflare/references/streaming-and-deploy.md +10 -3
- package/skills/cloudflare/references/webhooks-and-crypto.md +9 -2
- package/skills/comparison/SKILL.md +5 -0
- package/skills/comparison/references/framework-comparison.md +75 -57
- package/skills/composability/SKILL.md +38 -29
- package/skills/css/SKILL.md +10 -13
- package/skills/debug-manifest/SKILL.md +74 -31
- package/skills/defer-hydration/SKILL.md +52 -31
- package/skills/deployment-caching/SKILL.md +11 -3
- package/skills/document-cache/SKILL.md +98 -28
- package/skills/fonts/SKILL.md +16 -8
- package/skills/handler-use/SKILL.md +55 -17
- package/skills/hooks/SKILL.md +28 -20
- package/skills/hooks/data.md +23 -3
- package/skills/hooks/handle-and-actions.md +34 -17
- package/skills/hooks/navigation.md +39 -10
- package/skills/hooks/outlets.md +13 -6
- package/skills/hooks/state.md +39 -15
- package/skills/hooks/urls.md +7 -5
- package/skills/host-router/SKILL.md +64 -12
- package/skills/i18n/SKILL.md +60 -34
- package/skills/intercept/SKILL.md +161 -26
- package/skills/layout/SKILL.md +106 -50
- package/skills/links/SKILL.md +51 -19
- package/skills/loader/SKILL.md +386 -128
- package/skills/middleware/SKILL.md +129 -26
- package/skills/migrate-nextjs/SKILL.md +156 -70
- package/skills/migrate-nextjs/backend-host-swap.md +5 -2
- package/skills/migrate-react-router/SKILL.md +51 -39
- package/skills/migrate-react-router/component-migration.md +28 -7
- package/skills/migrate-react-router/data-and-actions.md +21 -6
- package/skills/migrate-react-router/route-mapping.md +19 -10
- package/skills/mime-routes/SKILL.md +12 -7
- package/skills/observability/SKILL.md +92 -14
- package/skills/parallel/SKILL.md +88 -47
- package/skills/ppr/SKILL.md +866 -379
- package/skills/prerender/SKILL.md +210 -108
- package/skills/rango/SKILL.md +115 -65
- package/skills/react-compiler/SKILL.md +6 -5
- package/skills/response-routes/SKILL.md +133 -25
- package/skills/route/SKILL.md +170 -60
- package/skills/router-setup/SKILL.md +420 -240
- package/skills/scripts/SKILL.md +39 -21
- package/skills/server-actions/SKILL.md +231 -59
- package/skills/shell-manifest/SKILL.md +28 -14
- package/skills/streams-and-websockets/SKILL.md +32 -14
- package/skills/tailwind/SKILL.md +37 -33
- package/skills/testing/SKILL.md +73 -33
- package/skills/testing/cache-prerender.md +60 -33
- package/skills/testing/client-components.md +59 -6
- package/skills/testing/e2e-parity.md +12 -2
- package/skills/testing/flight.md +19 -11
- package/skills/testing/handles.md +8 -5
- package/skills/testing/loader.md +26 -6
- package/skills/testing/middleware.md +8 -3
- package/skills/testing/predicates.md +120 -0
- package/skills/testing/render-handler.md +70 -22
- package/skills/testing/response-routes.md +11 -4
- package/skills/testing/reverse-and-types.md +20 -14
- package/skills/testing/serve-shell-request.md +188 -0
- package/skills/testing/server-actions.md +12 -3
- package/skills/testing/server-tree.md +9 -0
- package/skills/testing/setup.md +9 -5
- package/skills/theme/SKILL.md +155 -30
- package/skills/typesafety/SKILL.md +20 -9
- package/skills/typesafety/env-and-bindings.md +137 -50
- package/skills/typesafety/generated-files-and-cli.md +26 -14
- package/skills/typesafety/params-and-search.md +44 -19
- package/skills/typesafety/route-types.md +50 -14
- package/skills/use-cache/SKILL.md +224 -83
- package/skills/vercel/SKILL.md +35 -13
- package/skills/view-transitions/SKILL.md +42 -21
- package/src/__augment-tests__/augmented.check.ts +26 -1
- package/src/browser/cookie-name.ts +23 -0
- package/src/browser/history-state.ts +23 -0
- package/src/browser/index.ts +2 -2
- package/src/browser/navigation-bridge.ts +3 -4
- package/src/browser/navigation-transaction.ts +22 -2
- package/src/browser/partial-update.ts +35 -25
- package/src/browser/react/ScrollRestoration.tsx +6 -6
- package/src/browser/rsc-router.tsx +6 -1
- package/src/browser/scroll-restoration.ts +53 -6
- package/src/cache/cache-exec-scope.ts +37 -4
- package/src/cache/cache-key-utils.ts +23 -8
- package/src/cache/cache-policy.ts +22 -0
- package/src/cache/cache-runtime.ts +264 -68
- package/src/cache/cache-scope.ts +419 -43
- 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/document-cache.ts +15 -0
- package/src/cache/handle-capture.ts +90 -10
- package/src/cache/handle-snapshot.ts +133 -28
- package/src/cache/index.ts +2 -0
- package/src/cache/isolate-tag-memo.ts +308 -0
- package/src/cache/segment-codec.ts +26 -7
- package/src/cache/shell-memo.ts +341 -0
- package/src/cache/shell-snapshot.ts +217 -27
- package/src/cache/tag-invalidation.ts +108 -22
- package/src/cache/taint.ts +1 -1
- package/src/cache/types.ts +225 -44
- package/src/cache/vercel/vercel-cache-store.ts +545 -61
- package/src/client.rsc.tsx +10 -9
- package/src/client.tsx +5 -4
- package/src/components/DefaultDocument.tsx +7 -2
- package/src/context-var.ts +6 -5
- package/src/escape-script.ts +1 -1
- package/src/handles/MetaTags.tsx +13 -11
- package/src/handles/Scripts.tsx +11 -10
- package/src/handles/meta.ts +2 -2
- package/src/handles/script.ts +17 -16
- package/src/href-client.ts +1 -1
- package/src/html.ts +12 -0
- package/src/index.rsc.ts +5 -0
- package/src/index.ts +1 -0
- package/src/loader-store.ts +7 -4
- package/src/prerender/build-shell-capture.ts +73 -22
- package/src/reverse.ts +44 -4
- package/src/route-definition/dsl-helpers.ts +121 -33
- package/src/route-definition/helpers-types.ts +33 -9
- package/src/route-definition/resolve-handler-use.ts +121 -6
- package/src/route-types.ts +0 -3
- package/src/router/debug-manifest.ts +10 -2
- package/src/router/error-handling.ts +51 -15
- package/src/router/handler-context.ts +71 -28
- package/src/router/instrument.ts +54 -2
- package/src/router/intercept-resolution.ts +141 -43
- package/src/router/loader-resolution.ts +115 -39
- package/src/router/match-api.ts +40 -26
- package/src/router/match-handlers.ts +5 -0
- package/src/router/match-middleware/background-revalidation.ts +161 -96
- package/src/router/match-middleware/cache-lookup.ts +110 -10
- package/src/router/match-middleware/cache-store.ts +48 -101
- package/src/router/middleware-types.ts +9 -6
- package/src/router/middleware.ts +75 -13
- package/src/router/prerender-match.ts +59 -19
- package/src/router/route-snapshot.ts +11 -1
- package/src/router/router-context.ts +2 -0
- package/src/router/router-interfaces.ts +25 -2
- package/src/router/router-options.ts +8 -5
- package/src/router/segment-resolution/fresh.ts +62 -21
- package/src/router/segment-resolution/helpers.ts +4 -2
- package/src/router/segment-resolution/loader-cache.ts +407 -76
- package/src/router/segment-resolution/loader-mask.ts +12 -22
- package/src/router/segment-resolution/loader-snapshot.ts +208 -56
- package/src/router/segment-resolution/mask-nested.ts +138 -44
- package/src/router/segment-resolution/revalidation.ts +61 -16
- package/src/router/segment-wrappers.ts +4 -0
- 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/render-pipeline.ts +3 -0
- package/src/rsc/response-cache-serve.ts +9 -6
- package/src/rsc/response-route-handler.ts +11 -3
- package/src/rsc/rsc-rendering.ts +608 -225
- package/src/rsc/shell-build-manifest.ts +27 -9
- package/src/rsc/shell-capture-constants.ts +73 -6
- package/src/rsc/shell-capture.ts +1068 -296
- package/src/rsc/shell-serve.ts +316 -34
- package/src/rsc/ssr-setup.ts +14 -8
- package/src/rsc/types.ts +18 -0
- package/src/server/context.ts +300 -32
- package/src/server/cookie-store.ts +87 -61
- package/src/server/handle-store.ts +232 -7
- package/src/server/request-context.ts +297 -67
- package/src/ssr/index.tsx +136 -44
- package/src/ssr/ssr-root.tsx +9 -8
- package/src/static-handler.ts +4 -1
- package/src/testing/flight-runtime.d.ts +21 -0
- package/src/testing/flight.entry.ts +14 -0
- package/src/testing/generated-routes.ts +2 -2
- package/src/testing/internal/context.ts +1 -1
- package/src/testing/internal/flight-client-globals.ts +7 -5
- package/src/testing/render-route.tsx +85 -24
- package/src/testing/run-loader.ts +68 -6
- package/src/testing/serve-shell-request.ts +378 -0
- package/src/testing/shell-status.ts +34 -12
- package/src/testing/vitest-stubs/plugin-rsc.ts +123 -20
- package/src/testing/vitest.ts +62 -1
- package/src/theme/ThemeProvider.tsx +7 -6
- package/src/theme/ThemeScript.tsx +3 -3
- package/src/types/cache-types.ts +14 -10
- package/src/types/handler-context.ts +28 -1
- package/src/types/loader-types.ts +8 -6
- package/src/urls/pattern-types.ts +22 -18
- package/src/urls/response-types.ts +3 -23
- package/src/use-loader.tsx +12 -3
- package/src/vite/discovery/shell-prerender-phase.ts +9 -0
- package/src/vite/plugins/use-cache-transform.ts +118 -74
- package/src/vite/rango.ts +7 -3
- package/src/vite/router-discovery.ts +14 -4
|
@@ -21,7 +21,32 @@
|
|
|
21
21
|
* primitive for each tag. A single configured store (the common case) owns its
|
|
22
22
|
* own tag index and distributed invalidation - there is no separate
|
|
23
23
|
* tag-invalidation store.
|
|
24
|
+
*
|
|
25
|
+
* Both call invalidateTags() synchronously, inside the request (#973). A store
|
|
26
|
+
* masks the tags for the rest of the request before its first await (the
|
|
27
|
+
* built-in stores write their per-request marker state there), so the request
|
|
28
|
+
* that ran either verb reads its own writes; revalidateTag() leaves only the
|
|
29
|
+
* durable remainder (KV marker put, tag purge, expireTag) to waitUntil.
|
|
30
|
+
* Invariant: the mask only turns the invalidating request's hits into misses.
|
|
31
|
+
* If the durable write then fails, that request paid extra misses, never a
|
|
32
|
+
* stale read, and other requests read the durable state. Both also record
|
|
33
|
+
* the tags on the request (markInvalidated), so a "use cache" execution or
|
|
34
|
+
* stale refresh this request started before one of its own tags was
|
|
35
|
+
* invalidated is neither joined nor written (cache-runtime.ts). Its own tags
|
|
36
|
+
* only: a nested "use cache" function's tags do not reach the enclosing
|
|
37
|
+
* entry (#980), for this gate as for eviction. Not covered (#977): such an
|
|
38
|
+
* execution in another request, or a loader cache() write, still lands, and
|
|
39
|
+
* the store accepts it (docs/design/caching.md "Read-your-own-writes").
|
|
40
|
+
*/
|
|
41
|
+
import { type RequestContext } from "../server/request-context.js";
|
|
42
|
+
/** The current position in that order; an execution records it at start. */
|
|
43
|
+
export declare function currentCacheSeq(): number;
|
|
44
|
+
/**
|
|
45
|
+
* Whether the request invalidated any of `tags` after `since` (a
|
|
46
|
+
* currentCacheSeq() value an execution recorded when it started): its value
|
|
47
|
+
* may predate the invalidation, so the request neither joins nor writes it.
|
|
24
48
|
*/
|
|
49
|
+
export declare function invalidatedSince(ctx: RequestContext<any, any> | undefined, tags: readonly string[] | undefined, since: number): boolean;
|
|
25
50
|
/**
|
|
26
51
|
* Immediately expire every cache entry tagged with any of `tags`, resolving
|
|
27
52
|
* once in-process invalidation across all configured stores completes.
|
|
@@ -47,10 +72,12 @@ export declare function updateTag(...tags: string[]): Promise<void>;
|
|
|
47
72
|
* This is NOT stale-while-revalidate: like updateTag() it hard-purges, so the
|
|
48
73
|
* next read after the invalidation lands is a miss that re-renders fresh. The
|
|
49
74
|
* only difference from updateTag() is awaitability - revalidateTag() defers the
|
|
50
|
-
*
|
|
75
|
+
* durable write off the response path and is not awaited.
|
|
51
76
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
77
|
+
* Read-your-own-writes (#973): each built-in store masks the tags for the rest
|
|
78
|
+
* of this request before revalidateTag() returns, so a Server Action's own
|
|
79
|
+
* re-render after it is fresh, as after `await updateTag()`. Other requests see
|
|
80
|
+
* the invalidation once the durable write lands.
|
|
54
81
|
*
|
|
55
82
|
* Fire-and-forget: because this returns void and runs in the background, a
|
|
56
83
|
* failed durable marker write (e.g. a transient KV outage) is NOT surfaced to
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Taint symbol for request-scoped objects.
|
|
3
3
|
*
|
|
4
|
-
* Objects branded with NOCACHE_SYMBOL (ctx
|
|
4
|
+
* Objects branded with NOCACHE_SYMBOL (ctx objects) are excluded from
|
|
5
5
|
* "use cache" cache keys and trigger handle capture mode so that side
|
|
6
6
|
* effects (breadcrumbs, metadata) are recorded and replayed on cache hit.
|
|
7
7
|
*/
|
|
@@ -99,7 +99,11 @@ export interface SegmentCacheStore<TEnv = unknown> {
|
|
|
99
99
|
*/
|
|
100
100
|
get(key: string): Promise<CacheGetResult | null | CacheReadError>;
|
|
101
101
|
/**
|
|
102
|
-
* Store entry data with TTL
|
|
102
|
+
* Store entry data with TTL. Resolve once a later `get(key)` from the same
|
|
103
|
+
* location observes the entry: the route cache() write runs in a background
|
|
104
|
+
* task, and a PPR shell capture awaits that task before it reads the entry
|
|
105
|
+
* back (#957). A store that resolves earlier makes the capture re-render
|
|
106
|
+
* the page instead of replaying the entry.
|
|
103
107
|
* @param key - Cache key
|
|
104
108
|
* @param data - Serialized entry data
|
|
105
109
|
* @param ttl - Time-to-live in seconds
|
|
@@ -156,8 +160,14 @@ export interface SegmentCacheStore<TEnv = unknown> {
|
|
|
156
160
|
* upgrade — the reactVersion field on the entry gates that at read time).
|
|
157
161
|
* @param key - Cache key
|
|
158
162
|
* @param entry - The shell prelude/postponed/version/createdAt bundle
|
|
159
|
-
* @param ttlSeconds - Time-to-live in seconds
|
|
160
|
-
*
|
|
163
|
+
* @param ttlSeconds - Time-to-live in seconds: the route's `ppr.ttl`
|
|
164
|
+
* (undefined: the store's default), capped to the route cache() entry
|
|
165
|
+
* the capture replayed or wrote. A capped window arrives in whole
|
|
166
|
+
* seconds, rounded up, with ttl + swr at least 1; its ttl can be 0 (a
|
|
167
|
+
* shell stale from the start, captured from a record inside its swr
|
|
168
|
+
* window).
|
|
169
|
+
* @param swrSeconds - Optional stale-while-revalidate window in seconds,
|
|
170
|
+
* under the same cap
|
|
161
171
|
* @param tags - Optional cache tags for invalidation (participates in
|
|
162
172
|
* invalidateTags via the same tag machinery as the item family)
|
|
163
173
|
* @returns `invalidated` when a generation marker rejected the write,
|
|
@@ -169,6 +179,40 @@ export interface SegmentCacheStore<TEnv = unknown> {
|
|
|
169
179
|
* without acknowledgements.
|
|
170
180
|
*/
|
|
171
181
|
putShell?(key: string, entry: ShellCacheEntry, ttlSeconds?: number, swrSeconds?: number, tags?: string[]): Promise<"stored" | "invalidated" | "uncacheable" | void>;
|
|
182
|
+
/**
|
|
183
|
+
* @internal The document serve path's shell read, for built-in stores. The
|
|
184
|
+
* serve path prefers it over getShell: it resolves with the entry and the
|
|
185
|
+
* raw (decoded) prelude, the snapshot on its own promise, and the read's
|
|
186
|
+
* `debugPerformance` stats. CFCacheStore's layout keeps the snapshot behind
|
|
187
|
+
* the prelude, so its HIT can commit before the snapshot is read;
|
|
188
|
+
* VercelCacheStore reads the whole entry and decodes the prelude once per
|
|
189
|
+
* memoized shell (issue #941, docs/design/shell-entry-layout.md). Not part
|
|
190
|
+
* of the custom-store contract.
|
|
191
|
+
*/
|
|
192
|
+
readShellDocument?(key: string, options?: ShellDocumentReadOptions): Promise<ShellDocumentRead | null>;
|
|
193
|
+
/**
|
|
194
|
+
* @internal How long (ms) this store's isolate memos (shell, tag markers)
|
|
195
|
+
* can serve a read that predates a tag invalidation made elsewhere. After
|
|
196
|
+
* updateTag()/revalidateTag(), the response's fresh-reads cookie lasts the
|
|
197
|
+
* longest of these, and the same user's requests skip the memos meanwhile.
|
|
198
|
+
* Absent or 0: nothing to cover.
|
|
199
|
+
*/
|
|
200
|
+
readonly freshReadsWindowMs?: number;
|
|
201
|
+
/**
|
|
202
|
+
* @internal Drop this isolate's memoized copy of a shell (shell-memo.ts) so
|
|
203
|
+
* the next read goes to the store. A HIT whose doc record failed to decode
|
|
204
|
+
* calls it after replacing the entry with a tombstone: the memoized copy
|
|
205
|
+
* holds the same record. Built-in stores only; not part of the custom-store
|
|
206
|
+
* contract.
|
|
207
|
+
*/
|
|
208
|
+
dropShellMemo?(key: string): void;
|
|
209
|
+
/**
|
|
210
|
+
* @internal The largest shell entry (prelude, postponed state, and snapshot
|
|
211
|
+
* bytes) this store can hold in one value. The capture refuses a bigger
|
|
212
|
+
* entry instead of letting the write fail inside waitUntil. Absent: the
|
|
213
|
+
* capture applies DEFAULT_SHELL_ENTRY_MAX_BYTES (Cloudflare KV's 25 MiB).
|
|
214
|
+
*/
|
|
215
|
+
readonly maxShellEntryBytes?: number;
|
|
172
216
|
/**
|
|
173
217
|
* Declares the shell family present-but-inert: getShell/putShell exist but
|
|
174
218
|
* no-op (a custom store whose backing tier is conditionally unavailable).
|
|
@@ -210,6 +254,20 @@ export interface SegmentCacheStore<TEnv = unknown> {
|
|
|
210
254
|
* APIs delegate to. Receives ALL of one invalidation call's tags at once so
|
|
211
255
|
* stores can batch their work (e.g. a single CDN purge request rather than
|
|
212
256
|
* one per tag). Stores that do not support tags simply omit this method.
|
|
257
|
+
*
|
|
258
|
+
* Both verbs call it synchronously, inside the request that invalidates
|
|
259
|
+
* (#973); revalidateTag() then hands the returned promise to waitUntil
|
|
260
|
+
* without awaiting it. Read-your-own-writes for that request comes from
|
|
261
|
+
* what the store does before its first await: record the tags as
|
|
262
|
+
* invalidated in state its reads consult for the rest of the request (the
|
|
263
|
+
* built-in stores key a map by the request's root context and the store,
|
|
264
|
+
* so contexts derived from the request share it, and compare each hit's
|
|
265
|
+
* tags and write time against it), then start the
|
|
266
|
+
* durable write. Such a mask must only ever turn hits into misses, and only
|
|
267
|
+
* in that request: a durable write that later fails then costs extra
|
|
268
|
+
* misses, never a stale read. A store that records nothing before its
|
|
269
|
+
* first await still works; the request that ran revalidateTag() can read
|
|
270
|
+
* entries the invalidation covers until the durable write lands.
|
|
213
271
|
* @param tags - The cache tags to invalidate
|
|
214
272
|
*/
|
|
215
273
|
invalidateTags?(tags: string[]): Promise<void>;
|
|
@@ -295,12 +353,13 @@ export interface ShellCacheEntry {
|
|
|
295
353
|
*/
|
|
296
354
|
buildVersion?: string;
|
|
297
355
|
/**
|
|
298
|
-
* The initialTheme the CAPTURE render was built with
|
|
299
|
-
*
|
|
356
|
+
* The initialTheme the CAPTURE render was built with: the no-cookie default
|
|
357
|
+
* (payloadInitialTheme, rsc/full-payload.ts), never the capturing visitor's
|
|
358
|
+
* theme (#971). The resume tail must render ThemeProvider with the SAME
|
|
300
359
|
* initialTheme the frozen prelude was rendered with: React resume requires the
|
|
301
360
|
* tree above the holes to match the prerendered tree, and initialTheme is
|
|
302
361
|
* per-request METADATA, not part of the cached segments — a visitor whose
|
|
303
|
-
* theme differs from the
|
|
362
|
+
* theme differs from the capture's would otherwise produce a divergent resume
|
|
304
363
|
* tree (broken stitching/hydration). The visitor's real theme is applied
|
|
305
364
|
* pre-paint by the FOUC script and re-synced from the cookie post-mount by
|
|
306
365
|
* ThemeProvider.
|
|
@@ -321,17 +380,21 @@ export interface ShellCacheEntry {
|
|
|
321
380
|
*/
|
|
322
381
|
snapshot?: ShellSnapshotRecord[];
|
|
323
382
|
/**
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
383
|
+
* Records the capture dropped from `snapshot` because no reader of this
|
|
384
|
+
* entry consumes them, by family (`item:4/response:1`). Diagnostic only: the
|
|
385
|
+
* HIT tail timing reports it next to the kept records. See
|
|
386
|
+
* docs/design/shell-entry-layout.md ("Record only what a HIT reads").
|
|
387
|
+
*/
|
|
388
|
+
prunedRecords?: string;
|
|
389
|
+
/**
|
|
390
|
+
* The key of the document segment record inside `snapshot`: the handler
|
|
391
|
+
* layer every HIT replays (a document HIT tail looks the record up by this
|
|
392
|
+
* key, so a store keyGenerator or a build-time capture host cannot send it
|
|
393
|
+
* elsewhere) and the record navigation replay consumes (resolved under the
|
|
394
|
+
* implicit doc namespace at capture; see CacheScope.cacheRoute). A capture
|
|
395
|
+
* that ran handlers always stores it; only a prerender-served entry (the
|
|
396
|
+
* prerender store supplies the handler layer) has none. A document entry
|
|
397
|
+
* without it is served as a MISS.
|
|
335
398
|
*/
|
|
336
399
|
docKey?: string;
|
|
337
400
|
/**
|
|
@@ -340,26 +403,6 @@ export interface ShellCacheEntry {
|
|
|
340
403
|
* the partial request's headers and middleware state are not document state.
|
|
341
404
|
*/
|
|
342
405
|
navigationOnly?: true;
|
|
343
|
-
/**
|
|
344
|
-
* True when the capture's HANDLER layer declared per-request liveness: a
|
|
345
|
-
* handle pushed OUTSIDE a DSL loader scope carried a nested thenable (the
|
|
346
|
-
* capture mask turns it into a never-filling hole), such a push was still
|
|
347
|
-
* pending when the entry was written, or a handler-invoked loader
|
|
348
|
-
* (ctx.use(loader) from a handler body — the consumption lane, #672)
|
|
349
|
-
* executed during the capture. The serve tail then must NOT take the
|
|
350
|
-
* handler-free fast path (the implicit doc-cache hit): only a handler
|
|
351
|
-
* re-run can mint that hole's live promise or refresh that consumed value.
|
|
352
|
-
* DSL-loader pushes never set this — loaders re-run fresh on every HIT, so
|
|
353
|
-
* their holes always fill.
|
|
354
|
-
*/
|
|
355
|
-
handlerLiveHoles?: boolean;
|
|
356
|
-
/**
|
|
357
|
-
* Legacy/fallback marker for a transition({ when }) predicate that was not
|
|
358
|
-
* evaluated by the PPR pre-handler gate. Such an entry stays conservatively
|
|
359
|
-
* ineligible for handler-free replay. New PPR matches evaluate known segment
|
|
360
|
-
* predicates before the pipeline and do not set this marker.
|
|
361
|
-
*/
|
|
362
|
-
transitionWhen?: true;
|
|
363
406
|
/** Capture-generation start time; tag invalidations at or after it win. */
|
|
364
407
|
createdAt: number;
|
|
365
408
|
}
|
|
@@ -374,12 +417,122 @@ export type DocumentShellCacheEntry = ShellCacheEntry & {
|
|
|
374
417
|
prelude: string;
|
|
375
418
|
postponed: string | null;
|
|
376
419
|
};
|
|
420
|
+
/** @internal Options of {@link SegmentCacheStore.readShellDocument}. */
|
|
421
|
+
export interface ShellDocumentReadOptions {
|
|
422
|
+
/**
|
|
423
|
+
* Tag names the route declares for its shell (`ppr.tags`), known before the
|
|
424
|
+
* entry is read: their marker reads can start alongside the entry read.
|
|
425
|
+
*/
|
|
426
|
+
tagHints?: readonly string[];
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* @internal Why a prelude-first read's snapshot is missing although the entry
|
|
430
|
+
* stored one: `unavailable` (the read timed out or failed; the entry may be
|
|
431
|
+
* sound) or `corrupt` (the bytes arrived and do not parse; the store evicted
|
|
432
|
+
* the entry).
|
|
433
|
+
*/
|
|
434
|
+
export type ShellSnapshotFailure = "unavailable" | "corrupt";
|
|
435
|
+
/**
|
|
436
|
+
* @internal Result of {@link SegmentCacheStore.readShellDocument}.
|
|
437
|
+
*/
|
|
438
|
+
export interface ShellDocumentRead {
|
|
439
|
+
/** The entry without its prelude and snapshot (delivered separately). */
|
|
440
|
+
entry: ShellCacheEntry;
|
|
441
|
+
/** Raw prelude bytes (empty for a navigationOnly entry). */
|
|
442
|
+
prelude: Uint8Array;
|
|
443
|
+
shouldRevalidate?: boolean;
|
|
444
|
+
/**
|
|
445
|
+
* The capture snapshot, read after the prelude. Resolves undefined when the
|
|
446
|
+
* entry has none or it could not be read (`snapshotFailure` says which); a
|
|
447
|
+
* document HIT then cannot replay its doc record and degrades
|
|
448
|
+
* (rsc-rendering.ts serveShellHit).
|
|
449
|
+
* Never rejects.
|
|
450
|
+
*/
|
|
451
|
+
snapshot: Promise<ShellSnapshotRecord[] | undefined>;
|
|
452
|
+
/**
|
|
453
|
+
* Why `snapshot` resolved undefined although the entry stored one, or
|
|
454
|
+
* undefined when it did not fail. A document HIT that cannot replay its
|
|
455
|
+
* doc record replaces the entry only when it is broken, not when its read
|
|
456
|
+
* was merely slow (rsc-rendering.ts serveShellHit). Never rejects.
|
|
457
|
+
*/
|
|
458
|
+
snapshotFailure?: Promise<ShellSnapshotFailure | undefined>;
|
|
459
|
+
/**
|
|
460
|
+
* Where the read's time and bytes went, for the `debugPerformance` metrics
|
|
461
|
+
* (rsc-rendering.ts) and the store's own debug trace. Present only when one
|
|
462
|
+
* of them is on for the request.
|
|
463
|
+
*/
|
|
464
|
+
stats?: ShellReadStats;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* @internal Sub-steps of a prelude-first shell read. Times are ms. workerd
|
|
468
|
+
* advances its clock only on I/O, so a CPU-only field
|
|
469
|
+
* (`snapshot.parseMs`) reads 0 on a deployed worker: the byte counts are the
|
|
470
|
+
* cost signal there.
|
|
471
|
+
*/
|
|
472
|
+
export interface ShellReadStats {
|
|
473
|
+
/**
|
|
474
|
+
* The tier that answered: `memo` (the store's shell memo), `l1`/`kv`
|
|
475
|
+
* (CFCacheStore), or `store` (a single-tier store, VercelCacheStore).
|
|
476
|
+
*/
|
|
477
|
+
tier: "memo" | "l1" | "kv" | "store";
|
|
478
|
+
/** The per-isolate memo's outcome for this read and its size after it. */
|
|
479
|
+
memo?: {
|
|
480
|
+
hit: boolean;
|
|
481
|
+
bytes: number;
|
|
482
|
+
};
|
|
483
|
+
/**
|
|
484
|
+
* A KV read after an L1 attempt: how long the L1 attempt took and why it
|
|
485
|
+
* missed. The KV fields below then restart from the KV read.
|
|
486
|
+
*/
|
|
487
|
+
l1MissMs?: number;
|
|
488
|
+
l1MissReason?: string;
|
|
489
|
+
/** Cache API match (L1) or KV get (KV) until the body was available. */
|
|
490
|
+
matchMs?: number;
|
|
491
|
+
/** Frame head read (I/O) and parse. */
|
|
492
|
+
headMs?: number;
|
|
493
|
+
/** Prelude bytes read. */
|
|
494
|
+
preludeMs?: number;
|
|
495
|
+
/** Tag-marker read, start to resolve (runs alongside the prelude read). */
|
|
496
|
+
markerMs?: number;
|
|
497
|
+
/** How long the read waited for the marker after the prelude was read. */
|
|
498
|
+
markerWaitMs?: number;
|
|
499
|
+
/**
|
|
500
|
+
* The marker check was awaited after the entry read (VercelCacheStore), not
|
|
501
|
+
* alongside the prelude read; hinted reads still start with the entry read.
|
|
502
|
+
*/
|
|
503
|
+
markerSerial?: true;
|
|
504
|
+
/**
|
|
505
|
+
* How the isolate marker memo answered the shell's tags (the most
|
|
506
|
+
* store-bound outcome across them): `fresh`, `stale` (served, refreshing in
|
|
507
|
+
* the background), `read` (from the store), `bypass` (fresh-reads cookie).
|
|
508
|
+
*/
|
|
509
|
+
markerMemo?: "fresh" | "stale" | "read" | "bypass";
|
|
510
|
+
/** Tags whose marker reads were started before the entry's head (hints). */
|
|
511
|
+
markerHinted?: readonly string[];
|
|
512
|
+
/** When the hinted marker reads started (performance.now()). */
|
|
513
|
+
markerHintStartedAt?: number;
|
|
514
|
+
/** How many of the shell's tags were hinted. */
|
|
515
|
+
markerHintHits?: number;
|
|
516
|
+
/** How long the hinted marker reads ran before the entry named its tags. */
|
|
517
|
+
markerLeadMs?: number;
|
|
518
|
+
/** The request carried the fresh-reads cookie: no isolate memo was used. */
|
|
519
|
+
freshReads?: true;
|
|
520
|
+
headBytes?: number;
|
|
521
|
+
preludeBytes?: number;
|
|
522
|
+
/** Number of the shell's tags the marker read covered. */
|
|
523
|
+
tags?: number;
|
|
524
|
+
/** Filled after the commit, once the snapshot has been read. */
|
|
525
|
+
snapshot?: {
|
|
526
|
+
readMs: number;
|
|
527
|
+
parseMs: number;
|
|
528
|
+
bytes: number;
|
|
529
|
+
};
|
|
530
|
+
}
|
|
377
531
|
/**
|
|
378
532
|
* The families a shell snapshot pins. The item/segment/response families are
|
|
379
533
|
* cache-store reads/writes (recorded by RecordingShellStore); the loader family
|
|
380
|
-
* pins the settled CONTAINER of a bake-lane loader (
|
|
381
|
-
*
|
|
382
|
-
* docs/design/loader-container-bake.md). Excludes the shell family itself
|
|
534
|
+
* pins the settled CONTAINER of a bake-lane loader (lane rule: see
|
|
535
|
+
* resolveLoaderData, loader-cache.ts). Excludes the shell family itself
|
|
383
536
|
* (getShell/putShell) — the snapshot rides INSIDE a shell entry, so recording
|
|
384
537
|
* it would be self-referential.
|
|
385
538
|
*/
|
|
@@ -399,12 +552,20 @@ export interface ShellSnapshotLoaderValue {
|
|
|
399
552
|
* container carries hole markers, so a HIT must gate the overlay on the
|
|
400
553
|
* fresh run (only the loader body can mint the live nested promises);
|
|
401
554
|
* 0 = fully pinned, so a HIT resolves the payload promise immediately from
|
|
402
|
-
* the pin
|
|
403
|
-
*
|
|
404
|
-
* recorded paths win wholesale). Absent on pre-bit snapshots, which keep
|
|
405
|
-
* the gated path.
|
|
555
|
+
* the pin and does not run the loader body (unless `runs`). Absent on
|
|
556
|
+
* pre-bit snapshots, which keep the gated path.
|
|
406
557
|
*/
|
|
407
558
|
holes?: 0 | 1;
|
|
559
|
+
/**
|
|
560
|
+
* 1 when the capture saw a loader push it could not record (a deferred
|
|
561
|
+
* push, one with masked nested promises, or one made outside any loader
|
|
562
|
+
* body): a HIT then still runs the loader body in the background
|
|
563
|
+
* (pin-first) so those pushes reach the page. 0: a hole-free record is
|
|
564
|
+
* served from the pin alone and its body does not run on a HIT. Absent (a
|
|
565
|
+
* snapshot written before the bit, which lacks the loader-owned pushes)
|
|
566
|
+
* reads as 1, so the body supplies them.
|
|
567
|
+
*/
|
|
568
|
+
runs?: 0 | 1;
|
|
408
569
|
}
|
|
409
570
|
/** A serialized cached Response for the response family of a shell snapshot. */
|
|
410
571
|
export interface ShellSnapshotResponseValue {
|
|
@@ -481,6 +642,11 @@ export interface CachedEntryData {
|
|
|
481
642
|
* (see handle-snapshot.ts encodeHandles) so Promise/ReactNode handle values
|
|
482
643
|
* round-trip through JSON-serializing stores instead of being flattened. */
|
|
483
644
|
handles: string;
|
|
645
|
+
/**
|
|
646
|
+
* Owning loader ids index-aligned with the decoded `handles` values
|
|
647
|
+
* (HandleOwners). Absent: every value restores as a plain replay.
|
|
648
|
+
*/
|
|
649
|
+
handleOwners?: HandleOwners;
|
|
484
650
|
/** Expiration timestamp (ms since epoch) */
|
|
485
651
|
expiresAt: number;
|
|
486
652
|
/** Cache tags for invalidation */
|
|
@@ -520,3 +686,13 @@ export interface CacheDefaults {
|
|
|
520
686
|
* Structure: { handleName: [values...] }
|
|
521
687
|
*/
|
|
522
688
|
export type SegmentHandleData = Record<string, unknown[]>;
|
|
689
|
+
/**
|
|
690
|
+
* segmentId -> handleName -> the owning loader id of each recorded value, by
|
|
691
|
+
* index (null: not owned). Only a PPR shell capture writes owners: the
|
|
692
|
+
* settled pushes of the loader bodies it ran (an `ssr: false` loader's own,
|
|
693
|
+
* the loaders it awaits, and its own cache() replays), which the record keeps
|
|
694
|
+
* because the prelude rendered them. restoreHandles replays an owned value
|
|
695
|
+
* through HandleStore.pushReplayed, so a loader that does run on the HIT
|
|
696
|
+
* replaces it with its live push.
|
|
697
|
+
*/
|
|
698
|
+
export type HandleOwners = Record<string, Record<string, (string | null)[]>>;
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* `@vercel/functions`). It is the production store analogue to CFCacheStore, but
|
|
6
6
|
* far smaller: Vercel's Runtime Cache already IS a distributed, tag-aware cache
|
|
7
7
|
* (regional storage, global tag-expire within ~300ms), so none of CFCacheStore's
|
|
8
|
-
* L1/L2 tiering
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* L1/L2 tiering or per-tier timeout budgets are needed here - the platform does
|
|
9
|
+
* that work. Tag markers exist only for the shell family (`tm`, see
|
|
10
|
+
* isTagsInvalidatedSince), and only PPR shell reads memoize them. What this
|
|
11
|
+
* store adds on top of the raw primitive is the parts Vercel does NOT give us:
|
|
11
12
|
*
|
|
12
13
|
* 1. Stale-while-revalidate. `getCache` has no stale-but-serve: a TTL'd entry
|
|
13
14
|
* simply becomes a miss. We store our own {staleAt, expiresAt} envelope and
|
|
@@ -30,8 +31,9 @@
|
|
|
30
31
|
* `getCache(...)` result, which satisfies the shape structurally. That keeps the
|
|
31
32
|
* router free of a hard Vercel dependency.
|
|
32
33
|
*/
|
|
33
|
-
import type { SegmentCacheStore, CachedEntryData, CacheDefaults, CacheGetResult, CacheItemResult, CacheItemOptions, ShellCacheEntry, CacheReadError } from "../types.js";
|
|
34
|
-
import type
|
|
34
|
+
import type { SegmentCacheStore, CachedEntryData, CacheDefaults, CacheGetResult, CacheItemResult, CacheItemOptions, ShellCacheEntry, ShellDocumentRead, ShellDocumentReadOptions, CacheReadError } from "../types.js";
|
|
35
|
+
import { type RequestContext } from "../../server/request-context.js";
|
|
36
|
+
import { type StoreMemoOptions } from "../shell-memo.js";
|
|
35
37
|
/**
|
|
36
38
|
* Minimal structural shape of the Vercel Runtime Cache returned by `getCache()`
|
|
37
39
|
* from `@vercel/functions`. Declared locally so @rangojs/router carries no hard
|
|
@@ -69,7 +71,7 @@ export declare const VERCEL_MAX_TAGS_PER_ITEM: number;
|
|
|
69
71
|
/** Max tag length in UTF-8 bytes accepted by Vercel; longer tags are skipped. */
|
|
70
72
|
export declare const VERCEL_MAX_TAG_BYTES: number;
|
|
71
73
|
/** Read-path outcome for the debug sink. */
|
|
72
|
-
export type VercelCacheReadOutcome = "miss" | "fresh" | "stale-revalidate" | "expired" | "corrupt" | "error";
|
|
74
|
+
export type VercelCacheReadOutcome = "miss" | "memo-hit" | "fresh" | "stale-revalidate" | "expired" | "corrupt" | "error";
|
|
73
75
|
/** Diagnostic event emitted on every read when `debug` is set. */
|
|
74
76
|
export interface VercelCacheReadDebugEvent {
|
|
75
77
|
op: "get" | "getItem" | "getResponse" | "getShell";
|
|
@@ -100,10 +102,13 @@ export interface VercelCacheStoreOptions<TEnv = unknown> {
|
|
|
100
102
|
*/
|
|
101
103
|
cache: VercelRuntimeCache;
|
|
102
104
|
/**
|
|
103
|
-
* `waitUntil` from `@vercel/functions`.
|
|
104
|
-
*
|
|
105
|
-
* backgrounds the actual writes
|
|
106
|
-
* (
|
|
105
|
+
* `waitUntil` from `@vercel/functions`. Keeps two background reads/writes
|
|
106
|
+
* alive past the response: the stale-read lock write (herd dampening; the
|
|
107
|
+
* router already backgrounds the actual writes) and the PPR shell reads'
|
|
108
|
+
* tag-marker memo refreshes (`memo.markerMaxStaleMs`). When omitted, both
|
|
109
|
+
* run detached (fire-and-forget), and the platform may end a refresh when
|
|
110
|
+
* the response ends; the next read past the stale window then reads the
|
|
111
|
+
* marker itself.
|
|
107
112
|
*/
|
|
108
113
|
waitUntil?: (promise: Promise<unknown>) => void;
|
|
109
114
|
/**
|
|
@@ -136,6 +141,26 @@ export interface VercelCacheStoreOptions<TEnv = unknown> {
|
|
|
136
141
|
* receives the structured VercelCacheReadDebugEvent.
|
|
137
142
|
*/
|
|
138
143
|
debug?: VercelCacheDebug;
|
|
144
|
+
/**
|
|
145
|
+
* Per-process memos ({@link StoreMemoOptions}), kept per `cache` handle:
|
|
146
|
+
* create the `getCache()` handle once per process, not per request, or the
|
|
147
|
+
* memos never hit. `shellMs` (default 2000) and `shellMaxBytes` (default
|
|
148
|
+
* 16 MiB) size the PPR shell memo; `markerFreshMs` (default 300) and
|
|
149
|
+
* `markerMaxStaleMs` (default 2000) the memo of the tag markers
|
|
150
|
+
* `invalidateTags()` writes, which PPR shell reads use
|
|
151
|
+
* (stale-while-revalidate). The process that invalidates writes the new
|
|
152
|
+
* marker into its memo; another process in the region rejects a memoized
|
|
153
|
+
* shell once its marker memo sees it (up to `markerMaxStaleMs`). The
|
|
154
|
+
* markers are a regional `cache.set` (only `expireTag` is global), so
|
|
155
|
+
* another region serves a shell it memoized before the invalidation until
|
|
156
|
+
* its window passes, where without the memo `expireTag` removes it within
|
|
157
|
+
* about 300 ms; a platform `expireTag` issued outside rango is likewise
|
|
158
|
+
* seen once the window passes. The response of a request that ran
|
|
159
|
+
* `updateTag()` / `revalidateTag()` sets the fresh-reads cookie, and the
|
|
160
|
+
* same user's requests carrying it skip both memos. `{ shellMs: 0 }` turns
|
|
161
|
+
* the shell memo off, `{ markerFreshMs: 0 }` the marker memo.
|
|
162
|
+
*/
|
|
163
|
+
memo?: StoreMemoOptions;
|
|
139
164
|
}
|
|
140
165
|
/**
|
|
141
166
|
* Vercel Runtime Cache-backed segment cache store.
|
|
@@ -170,12 +195,18 @@ export interface VercelCacheStoreOptions<TEnv = unknown> {
|
|
|
170
195
|
* import { getCache, waitUntil } from "@vercel/functions";
|
|
171
196
|
* import { VercelCacheStore } from "@rangojs/router/cache";
|
|
172
197
|
*
|
|
198
|
+
* // One handle per process: getCache() resolves the platform cache on every
|
|
199
|
+
* // call, and the PPR shell and tag-marker memos are kept per handle.
|
|
200
|
+
* // `process.env` (not `import.meta.env`, which needs a VITE_ prefix and
|
|
201
|
+
* // would be undefined here) so cross-deploy busting via `version` works.
|
|
202
|
+
* const runtimeCache = getCache({
|
|
203
|
+
* namespace: process.env.VERCEL_DEPLOYMENT_ID,
|
|
204
|
+
* });
|
|
205
|
+
*
|
|
173
206
|
* export const router = createRouter({
|
|
174
207
|
* cache: () => ({
|
|
175
208
|
* store: new VercelCacheStore({
|
|
176
|
-
*
|
|
177
|
-
* // would be undefined here) so cross-deploy busting via `version` works.
|
|
178
|
-
* cache: getCache({ namespace: process.env.VERCEL_DEPLOYMENT_ID }),
|
|
209
|
+
* cache: runtimeCache,
|
|
179
210
|
* waitUntil,
|
|
180
211
|
* defaults: { ttl: 60, swr: 300 },
|
|
181
212
|
* }),
|
|
@@ -191,6 +222,16 @@ export declare class VercelCacheStore<TEnv = unknown> implements SegmentCacheSto
|
|
|
191
222
|
private readonly waitUntil?;
|
|
192
223
|
private readonly version?;
|
|
193
224
|
private readonly maxItemBytes;
|
|
225
|
+
private readonly memo;
|
|
226
|
+
private readonly handleMemos;
|
|
227
|
+
/** @internal SegmentCacheStore.freshReadsWindowMs */
|
|
228
|
+
readonly freshReadsWindowMs: number;
|
|
229
|
+
/**
|
|
230
|
+
* @internal SegmentCacheStore.maxShellEntryBytes: the item cap, less the
|
|
231
|
+
* base64 expansion of the prelude the shell envelope stores (the capture
|
|
232
|
+
* measures raw prelude bytes).
|
|
233
|
+
*/
|
|
234
|
+
readonly maxShellEntryBytes: number;
|
|
194
235
|
private readonly name?;
|
|
195
236
|
private readonly debug?;
|
|
196
237
|
constructor(options: VercelCacheStoreOptions<TEnv>);
|
|
@@ -210,6 +251,46 @@ export declare class VercelCacheStore<TEnv = unknown> implements SegmentCacheSto
|
|
|
210
251
|
entry: ShellCacheEntry;
|
|
211
252
|
shouldRevalidate?: boolean;
|
|
212
253
|
} | null>;
|
|
254
|
+
/**
|
|
255
|
+
* @internal The document serve path's read (SegmentCacheStore
|
|
256
|
+
* .readShellDocument): the same read as getShell, with the prelude decoded
|
|
257
|
+
* here, once per memoized shell rather than once per HIT, and the read's
|
|
258
|
+
* `debugPerformance` stats. The runtime cache returns a parsed value, so
|
|
259
|
+
* there is no prelude-first read and the tag-marker read follows the entry
|
|
260
|
+
* read (`markerSerial`).
|
|
261
|
+
*/
|
|
262
|
+
readShellDocument(key: string, options?: ShellDocumentReadOptions): Promise<ShellDocumentRead | null>;
|
|
263
|
+
/** @internal SegmentCacheStore.dropShellMemo */
|
|
264
|
+
dropShellMemo(key: string): void;
|
|
265
|
+
/**
|
|
266
|
+
* getShell's read, shared with readShellDocument: the shell memo first
|
|
267
|
+
* (shell-memo.ts), then the runtime cache. A memoized shell still runs the
|
|
268
|
+
* tag-marker check; `memoized` is the memo slot's value when the read came
|
|
269
|
+
* from, or went into, the memo (readShellDocument keeps the decoded prelude
|
|
270
|
+
* there).
|
|
271
|
+
*/
|
|
272
|
+
private readShell;
|
|
273
|
+
/**
|
|
274
|
+
* The shell's tag-marker check, timed into `stats`. Markers a prefetch
|
|
275
|
+
* already started (hinted tags) are awaited, not re-read; the check itself
|
|
276
|
+
* always uses the entry's own tags.
|
|
277
|
+
*/
|
|
278
|
+
private shellTagsInvalidated;
|
|
279
|
+
/**
|
|
280
|
+
* Start the tm-marker reads of the tags this shell key carried last time
|
|
281
|
+
* (and the route's static tags) alongside the entry read.
|
|
282
|
+
*/
|
|
283
|
+
private prefetchShellMarkers;
|
|
284
|
+
/**
|
|
285
|
+
* A tag's latest invalidatedAt (or null). With `isolateMemo` (PPR shell
|
|
286
|
+
* reads only) through the per-process marker memo (isolate-tag-memo.ts):
|
|
287
|
+
* fresh values are used as is, stale ones are used and refreshed in the
|
|
288
|
+
* background, older ones block on the runtime-cache read. A request
|
|
289
|
+
* carrying the fresh-reads cookie skips the memo. A failed read rejects and
|
|
290
|
+
* is never memoized. The shell write gate and build shells
|
|
291
|
+
* (isTagsInvalidatedSince) read the runtime cache every time.
|
|
292
|
+
*/
|
|
293
|
+
private readTagMarker;
|
|
213
294
|
putShell(key: string, entry: ShellCacheEntry, ttlSeconds?: number, swrSeconds?: number, tags?: string[]): Promise<"stored" | "invalidated" | void>;
|
|
214
295
|
/**
|
|
215
296
|
* Shell tag-generation gate (SegmentCacheStore.isTagsInvalidatedSince).
|
|
@@ -218,7 +299,24 @@ export declare class VercelCacheStore<TEnv = unknown> implements SegmentCacheSto
|
|
|
218
299
|
* generation start wins; read errors fail open like the CF marker check.
|
|
219
300
|
*/
|
|
220
301
|
isTagsInvalidatedSince(tags: string[], sinceMs: number): Promise<boolean>;
|
|
302
|
+
private tagsInvalidatedSince;
|
|
221
303
|
invalidateTags(tags: string[]): Promise<void>;
|
|
304
|
+
/**
|
|
305
|
+
* The request-local half of invalidateTags(), run before its first await
|
|
306
|
+
* (#973): see requestInvalidations. revalidateTag() waits for nothing
|
|
307
|
+
* after it.
|
|
308
|
+
*/
|
|
309
|
+
private maskTagsForRequest;
|
|
310
|
+
/**
|
|
311
|
+
* Whether this request invalidated one of `tags` through this store at or
|
|
312
|
+
* after `taggedAt`. `>=`, as the marker check: an entry written in the
|
|
313
|
+
* invalidation's millisecond may hold a value computed before it, so it
|
|
314
|
+
* misses (a false miss, never a stale read). An entry without a stamp was
|
|
315
|
+
* written before the stamp existed and counts as older.
|
|
316
|
+
*/
|
|
317
|
+
private maskedForRequest;
|
|
318
|
+
/** A data read's mask check: reports the miss and returns true when masked. */
|
|
319
|
+
private maskedMiss;
|
|
222
320
|
private toStoreKey;
|
|
223
321
|
private write;
|
|
224
322
|
/**
|
package/dist/types/client.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Component, type ReactNode } from "react";
|
|
2
|
+
import * as Html from "./html.js";
|
|
2
3
|
import { type ClientErrorBoundaryFallbackProps } from "./types";
|
|
3
4
|
/**
|
|
4
5
|
* Outlet component - renders child content in layouts
|
|
@@ -125,7 +126,7 @@ interface ErrorBoundaryState {
|
|
|
125
126
|
* @example
|
|
126
127
|
* ```tsx
|
|
127
128
|
* "use client";
|
|
128
|
-
* import { ErrorBoundary } from "
|
|
129
|
+
* import { ErrorBoundary } from "@rangojs/router/client";
|
|
129
130
|
*
|
|
130
131
|
* function MyComponent() {
|
|
131
132
|
* return (
|
|
@@ -172,14 +173,13 @@ export { useSegments, type SegmentsState, } from "./browser/react/use-segments.j
|
|
|
172
173
|
export { NavigationProvider, type NavigationProviderProps, } from "./browser/react/NavigationProvider.js";
|
|
173
174
|
export { Link, type LinkProps, type PrefetchStrategy, type StateOrGetter, type LinkState, } from "./browser/react/Link.js";
|
|
174
175
|
export { useLinkStatus, type LinkStatus, } from "./browser/react/use-link-status.js";
|
|
175
|
-
export {
|
|
176
|
+
export { useScrollRestoration, type ScrollRestorationProps, } from "./browser/react/ScrollRestoration.js";
|
|
176
177
|
export { type Handle } from "./handle.js";
|
|
177
178
|
export { useHandle } from "./browser/react/use-handle.js";
|
|
178
179
|
export { Meta } from "./handles/meta.js";
|
|
179
|
-
export { MetaTags } from "./handles/MetaTags.js";
|
|
180
180
|
export type { MetaDescriptor, MetaDescriptorBase } from "./router/types.js";
|
|
181
181
|
export { Script, type ScriptConfig, type ScriptAttributes, } from "./handles/script.js";
|
|
182
|
-
export {
|
|
182
|
+
export { Html };
|
|
183
183
|
export { Breadcrumbs, type BreadcrumbItem } from "./handles/breadcrumbs.js";
|
|
184
184
|
export { createLocationState, useLocationState, type LocationStateDefinition, type LocationStateEntry, type LocationStateOptions, } from "./browser/react/location-state.js";
|
|
185
185
|
export { href, type PatternToPath } from "./href-client.js";
|