@rangojs/router 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/types/browser/cookie-name.d.ts +6 -0
- package/dist/types/browser/event-controller.d.ts +3 -3
- package/dist/types/browser/history-state.d.ts +18 -0
- package/dist/types/browser/navigation-transaction.d.ts +20 -2
- package/dist/types/browser/partial-update.d.ts +16 -1
- package/dist/types/browser/prefetch/cache.d.ts +6 -6
- package/dist/types/browser/react/Link.d.ts +19 -9
- package/dist/types/browser/react/context.d.ts +2 -2
- package/dist/types/browser/react/location-state-shared.d.ts +95 -56
- package/dist/types/browser/react/location-state.d.ts +1 -1
- package/dist/types/browser/transition-when.d.ts +80 -0
- package/dist/types/browser/types.d.ts +104 -9
- package/dist/types/build/generate-manifest.d.ts +8 -0
- package/dist/types/cache/background-task.d.ts +24 -2
- package/dist/types/cache/cache-exec-scope.d.ts +31 -0
- package/dist/types/cache/cache-key-utils.d.ts +48 -2
- package/dist/types/cache/cache-policy.d.ts +18 -1
- package/dist/types/cache/cache-runtime.d.ts +7 -0
- package/dist/types/cache/cache-scope.d.ts +192 -26
- package/dist/types/cache/cache-tag.d.ts +116 -11
- package/dist/types/cache/cf/cf-base64.d.ts +6 -0
- package/dist/types/cache/cf/cf-cache-constants.d.ts +11 -0
- package/dist/types/cache/cf/cf-cache-store.d.ts +224 -72
- package/dist/types/cache/cf/cf-cache-types.d.ts +31 -4
- package/dist/types/cache/cf/cf-shell-frame.d.ts +95 -0
- package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +19 -0
- package/dist/types/cache/document-cache.d.ts +10 -0
- package/dist/types/cache/handle-snapshot.d.ts +41 -8
- package/dist/types/cache/index.d.ts +1 -0
- package/dist/types/cache/invalidation-order.d.ts +23 -0
- package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
- package/dist/types/cache/request-tag-mask.d.ts +54 -0
- package/dist/types/cache/shell-memo.d.ts +178 -0
- package/dist/types/cache/shell-snapshot.d.ts +141 -105
- package/dist/types/cache/tag-invalidation.d.ts +54 -3
- package/dist/types/cache/types.d.ts +267 -96
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +123 -18
- package/dist/types/client-urls/navigation.d.ts +28 -2
- package/dist/types/client-urls/server-projection.d.ts +5 -2
- package/dist/types/client-urls/types.d.ts +5 -5
- package/dist/types/client.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.rsc.d.ts +1 -1
- package/dist/types/prerender/build-shell-capture.d.ts +11 -4
- package/dist/types/route-definition/helpers-types.d.ts +11 -11
- package/dist/types/route-name.d.ts +7 -0
- package/dist/types/router/error-handling.d.ts +1 -1
- package/dist/types/router/instrument.d.ts +8 -0
- package/dist/types/router/intercept-resolution.d.ts +2 -0
- package/dist/types/router/match-context.d.ts +5 -0
- package/dist/types/router/match-middleware/background-revalidation.d.ts +0 -4
- package/dist/types/router/middleware-types.d.ts +6 -1
- package/dist/types/router/router-options.d.ts +4 -1
- package/dist/types/router/segment-resolution/loader-cache.d.ts +58 -12
- package/dist/types/router/segment-resolution/loader-mask.d.ts +3 -4
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +33 -48
- package/dist/types/router/segment-resolution/mask-nested.d.ts +73 -24
- package/dist/types/router/segment-resolution/view-transition-default.d.ts +7 -5
- package/dist/types/router/transition-when.d.ts +24 -10
- package/dist/types/rsc/attach-transition-when.d.ts +18 -0
- package/dist/types/rsc/full-payload.d.ts +21 -0
- package/dist/types/rsc/handler.d.ts +1 -1
- package/dist/types/rsc/response-cache-serve.d.ts +15 -12
- package/dist/types/rsc/shell-build-manifest.d.ts +12 -3
- package/dist/types/rsc/shell-capture-constants.d.ts +116 -12
- package/dist/types/rsc/shell-capture.d.ts +202 -74
- package/dist/types/rsc/shell-serve.d.ts +144 -50
- package/dist/types/rsc/types.d.ts +9 -0
- package/dist/types/segment-system.d.ts +7 -0
- package/dist/types/server/context.d.ts +155 -33
- package/dist/types/server/cookie-store.d.ts +33 -0
- package/dist/types/server/handle-store.d.ts +52 -1
- package/dist/types/server/request-context.d.ts +210 -63
- package/dist/types/server/request-headers.d.ts +44 -0
- package/dist/types/server.d.ts +1 -0
- package/dist/types/ssr/index.d.ts +9 -0
- package/dist/types/ssr/ssr-root.d.ts +9 -8
- package/dist/types/testing/e2e/index.d.ts +1 -1
- package/dist/types/testing/flight.entry.d.ts +7 -0
- package/dist/types/testing/index.d.ts +3 -2
- package/dist/types/testing/internal/action-id.d.ts +7 -0
- package/dist/types/testing/location-state-key.d.ts +24 -0
- package/dist/types/testing/render-route.d.ts +17 -1
- package/dist/types/testing/run-loader.d.ts +18 -3
- package/dist/types/testing/run-transition-when.d.ts +44 -69
- package/dist/types/testing/serve-shell-request.d.ts +133 -0
- package/dist/types/testing/shell-status.d.ts +44 -19
- package/dist/types/transition-when-ref.d.ts +79 -0
- package/dist/types/types/cache-types.d.ts +15 -3
- package/dist/types/types/handler-context.d.ts +14 -1
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/segments.d.ts +86 -71
- package/dist/types/urls/path-helper-types.d.ts +3 -4
- package/dist/types/urls/pattern-types.d.ts +21 -18
- package/dist/types/vite/plugins/transition-when-hoist.d.ts +45 -0
- package/dist/types/vite/router-discovery.d.ts +29 -2
- package/dist/vite/index.js +1005 -105
- package/package.json +1 -1
- package/skills/breadcrumbs/SKILL.md +17 -7
- package/skills/cache-guide/SKILL.md +175 -76
- package/skills/caching/SKILL.md +361 -33
- package/skills/catalog.json +1 -1
- package/skills/client-urls/SKILL.md +12 -7
- package/skills/cloudflare/SKILL.md +15 -0
- package/skills/comparison/references/framework-comparison.md +52 -37
- package/skills/defer-hydration/SKILL.md +50 -30
- package/skills/deployment-caching/SKILL.md +5 -2
- package/skills/document-cache/SKILL.md +32 -2
- package/skills/handler-use/SKILL.md +2 -0
- package/skills/hooks/SKILL.md +3 -1
- package/skills/hooks/handle-and-actions.md +3 -1
- package/skills/hooks/navigation.md +23 -2
- package/skills/hooks/state.md +101 -7
- package/skills/intercept/SKILL.md +29 -12
- package/skills/loader/SKILL.md +197 -44
- package/skills/middleware/SKILL.md +34 -9
- package/skills/migrate-nextjs/SKILL.md +68 -37
- package/skills/migrate-react-router/SKILL.md +2 -1
- package/skills/migrate-react-router/component-migration.md +3 -1
- package/skills/migrate-react-router/data-and-actions.md +3 -1
- package/skills/migrate-react-router/route-mapping.md +7 -4
- package/skills/observability/SKILL.md +65 -0
- package/skills/parallel/SKILL.md +13 -8
- package/skills/ppr/SKILL.md +715 -371
- package/skills/prerender/SKILL.md +30 -17
- package/skills/rango/SKILL.md +72 -34
- package/skills/route/SKILL.md +14 -7
- package/skills/router-setup/SKILL.md +4 -2
- package/skills/scripts/SKILL.md +8 -1
- package/skills/server-actions/SKILL.md +11 -0
- package/skills/shell-manifest/SKILL.md +21 -12
- package/skills/testing/SKILL.md +49 -43
- package/skills/testing/cache-prerender.md +15 -14
- package/skills/testing/client-components.md +45 -3
- package/skills/testing/loader.md +19 -1
- package/skills/testing/middleware.md +1 -0
- package/skills/testing/predicates.md +49 -49
- package/skills/testing/render-handler.md +1 -0
- package/skills/testing/serve-shell-request.md +202 -0
- package/skills/testing/server-actions.md +9 -3
- package/skills/theme/SKILL.md +56 -4
- package/skills/typesafety/env-and-bindings.md +18 -2
- package/skills/use-cache/SKILL.md +91 -23
- package/skills/vercel/SKILL.md +14 -2
- package/skills/view-transitions/SKILL.md +150 -29
- package/src/__augment-tests__/augmented.check.ts +23 -0
- package/src/browser/cookie-name.ts +23 -0
- package/src/browser/event-controller.ts +4 -4
- package/src/browser/history-state.ts +75 -1
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +77 -18
- package/src/browser/navigation-client.ts +4 -3
- package/src/browser/navigation-store.ts +43 -1
- package/src/browser/navigation-transaction.ts +31 -11
- package/src/browser/partial-update.ts +146 -35
- package/src/browser/prefetch/cache.ts +6 -6
- package/src/browser/prefetch/fetch.ts +2 -2
- package/src/browser/react/Link.tsx +38 -33
- package/src/browser/react/NavigationProvider.tsx +2 -2
- package/src/browser/react/context.ts +2 -2
- package/src/browser/react/location-state-shared.ts +241 -49
- package/src/browser/react/location-state.ts +5 -2
- package/src/browser/rsc-router.tsx +9 -0
- package/src/browser/server-action-bridge.ts +84 -15
- package/src/browser/transition-when.ts +205 -0
- package/src/browser/types.ts +124 -10
- package/src/build/generate-manifest.ts +77 -1
- package/src/cache/background-task.ts +55 -2
- package/src/cache/cache-exec-scope.ts +48 -1
- package/src/cache/cache-key-utils.ts +62 -2
- package/src/cache/cache-policy.ts +31 -4
- package/src/cache/cache-runtime.ts +209 -129
- package/src/cache/cache-scope.ts +573 -69
- package/src/cache/cache-tag.ts +475 -19
- package/src/cache/cf/cf-base64.ts +36 -4
- package/src/cache/cf/cf-cache-constants.ts +12 -0
- package/src/cache/cf/cf-cache-store.ts +1094 -376
- package/src/cache/cf/cf-cache-types.ts +32 -4
- package/src/cache/cf/cf-shell-frame.ts +258 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +55 -40
- package/src/cache/document-cache.ts +53 -7
- package/src/cache/handle-snapshot.ts +53 -9
- package/src/cache/index.ts +2 -0
- package/src/cache/invalidation-order.ts +84 -0
- package/src/cache/isolate-tag-memo.ts +308 -0
- package/src/cache/request-tag-mask.ts +147 -0
- package/src/cache/shell-memo.ts +341 -0
- package/src/cache/shell-snapshot.ts +249 -277
- package/src/cache/tag-invalidation.ts +109 -18
- package/src/cache/types.ts +274 -102
- package/src/cache/vercel/vercel-cache-store.ts +561 -74
- package/src/client-urls/client-root.tsx +8 -4
- package/src/client-urls/client-urls.ts +8 -6
- package/src/client-urls/navigation.ts +55 -4
- package/src/client-urls/server-projection.ts +31 -10
- package/src/client-urls/types.ts +12 -5
- package/src/client.tsx +2 -0
- package/src/host/cookie-handler.ts +2 -1
- package/src/index.rsc.ts +3 -0
- package/src/index.ts +3 -0
- package/src/loader-redirect.tsx +7 -3
- package/src/prerender/build-shell-capture.ts +173 -110
- package/src/route-definition/dsl-helpers.ts +6 -3
- package/src/route-definition/helpers-types.ts +11 -11
- package/src/route-name.ts +12 -0
- package/src/router/content-negotiation.ts +4 -1
- package/src/router/error-handling.ts +5 -2
- package/src/router/handler-context.ts +42 -5
- package/src/router/instrument.ts +54 -2
- package/src/router/intercept-resolution.ts +30 -8
- package/src/router/loader-resolution.ts +89 -33
- package/src/router/logging.ts +5 -3
- package/src/router/match-api.ts +89 -42
- package/src/router/match-context.ts +5 -0
- package/src/router/match-handlers.ts +14 -29
- package/src/router/match-middleware/background-revalidation.ts +3 -5
- package/src/router/match-middleware/cache-lookup.ts +52 -19
- package/src/router/match-middleware/cache-store.ts +36 -10
- package/src/router/match-result.ts +1 -0
- package/src/router/middleware-types.ts +6 -1
- package/src/router/middleware.ts +20 -4
- package/src/router/navigation-snapshot.ts +6 -5
- package/src/router/prerender-match.ts +10 -0
- package/src/router/request-classification.ts +7 -4
- package/src/router/router-options.ts +4 -1
- package/src/router/segment-resolution/fresh.ts +18 -13
- package/src/router/segment-resolution/helpers.ts +4 -2
- package/src/router/segment-resolution/loader-cache.ts +366 -70
- package/src/router/segment-resolution/loader-mask.ts +3 -4
- package/src/router/segment-resolution/loader-snapshot.ts +215 -126
- package/src/router/segment-resolution/mask-nested.ts +161 -42
- package/src/router/segment-resolution/revalidation.ts +27 -13
- package/src/router/segment-resolution/view-transition-default.ts +6 -21
- package/src/router/telemetry.ts +6 -3
- package/src/router/transition-when.ts +34 -52
- package/src/router.ts +2 -1
- package/src/rsc/attach-transition-when.ts +47 -0
- package/src/rsc/full-payload.ts +47 -3
- package/src/rsc/handler.ts +24 -10
- package/src/rsc/loader-fetch.ts +2 -1
- package/src/rsc/manifest-init.ts +4 -0
- package/src/rsc/origin-guard.ts +8 -5
- package/src/rsc/progressive-enhancement.ts +10 -32
- package/src/rsc/response-cache-serve.ts +69 -78
- package/src/rsc/response-route-handler.ts +2 -2
- package/src/rsc/rsc-rendering.ts +712 -338
- package/src/rsc/server-action.ts +8 -20
- package/src/rsc/shell-build-manifest.ts +49 -29
- package/src/rsc/shell-capture-constants.ts +182 -12
- package/src/rsc/shell-capture.ts +1126 -612
- package/src/rsc/shell-serve.ts +331 -85
- package/src/rsc/ssr-setup.ts +3 -2
- package/src/rsc/types.ts +9 -0
- package/src/segment-system.tsx +40 -7
- package/src/server/context.ts +328 -72
- package/src/server/cookie-store.ts +184 -133
- package/src/server/handle-store.ts +245 -27
- package/src/server/request-context.ts +364 -100
- package/src/server/request-headers.ts +134 -0
- package/src/server.ts +4 -0
- package/src/ssr/index.tsx +96 -37
- package/src/ssr/ssr-root.tsx +9 -8
- package/src/testing/dispatch.ts +4 -7
- package/src/testing/e2e/index.ts +1 -0
- package/src/testing/flight.entry.ts +14 -0
- package/src/testing/index.ts +5 -0
- package/src/testing/internal/action-id.ts +25 -0
- package/src/testing/internal/context.ts +1 -1
- package/src/testing/location-state-key.ts +45 -0
- package/src/testing/render-handler.ts +1 -1
- package/src/testing/render-route.tsx +109 -16
- package/src/testing/run-client-revalidate.ts +3 -19
- package/src/testing/run-loader.ts +67 -5
- package/src/testing/run-transition-when.ts +109 -172
- package/src/testing/serve-shell-request.ts +381 -0
- package/src/testing/shell-status.ts +73 -47
- package/src/theme/ThemeProvider.tsx +7 -6
- package/src/transition-when-ref.ts +220 -0
- package/src/types/cache-types.ts +15 -3
- package/src/types/handler-context.ts +14 -1
- package/src/types/index.ts +3 -0
- package/src/types/segments.ts +94 -91
- package/src/urls/path-helper-types.ts +3 -4
- package/src/urls/pattern-types.ts +21 -18
- package/src/vite/discovery/discover-routers.ts +3 -0
- package/src/vite/discovery/shell-prerender-phase.ts +9 -2
- package/src/vite/discovery/virtual-module-codegen.ts +16 -6
- package/src/vite/plugins/transition-when-hoist.ts +1064 -0
- package/src/vite/rango.ts +2 -0
- package/src/vite/router-discovery.ts +89 -9
- package/dist/types/rsc/transition-gate.d.ts +0 -23
- package/src/rsc/transition-gate.ts +0 -86
|
@@ -3,31 +3,17 @@ declare global {
|
|
|
3
3
|
readonly default: Cache;
|
|
4
4
|
}
|
|
5
5
|
}
|
|
6
|
-
|
|
7
|
-
* Cloudflare Edge Cache Store
|
|
8
|
-
*
|
|
9
|
-
* Production cache store using Cloudflare's Cache API (L1) with optional
|
|
10
|
-
* KV persistence (L2).
|
|
11
|
-
*
|
|
12
|
-
* L1 (Cache API): Per-colo, fast, ephemeral. Handles SWR atomically.
|
|
13
|
-
* L2 (KV): Global, persistent, ~50ms reads. Auto-warms cold colos.
|
|
14
|
-
*
|
|
15
|
-
* Read flow: L1 hit → serve | L1 miss → L2 hit → serve + promote to L1 | both miss → render
|
|
16
|
-
* Write flow: L1 write + L2 write (both via waitUntil)
|
|
17
|
-
*
|
|
18
|
-
* Features:
|
|
19
|
-
* - Extended TTL for SWR window (max-age = ttl + swr)
|
|
20
|
-
* - Staleness via x-edge-cache-stale-at header
|
|
21
|
-
* - Atomic REVALIDATING status for thundering herd prevention (L1 only)
|
|
22
|
-
* - Non-blocking writes via waitUntil
|
|
23
|
-
* - KV L2 for cross-colo cache persistence
|
|
24
|
-
*/
|
|
25
|
-
import type { SegmentCacheStore, CachedEntryData, CacheDefaults, CacheGetResult, CacheItemResult, CacheItemOptions, ShellCacheEntry, CacheReadError } from "../types.js";
|
|
6
|
+
import type { SegmentCacheStore, CachedEntryData, CacheDefaults, CacheGetResult, CacheItemResult, CacheItemOptions, ShellCacheEntry, ShellDocumentRead, ShellDocumentReadOptions, CacheReadError } from "../types.js";
|
|
26
7
|
import { type RequestContext } from "../../server/request-context.js";
|
|
27
8
|
import { CACHE_STALE_AT_HEADER, CACHE_STATUS_HEADER, CACHE_TAGS_HEADER, CACHE_TAGGED_AT_HEADER, TAG_MARKER_PREFIX, CACHE_REVALIDATING_AT_HEADER, MAX_REVALIDATION_INTERVAL, EDGE_LOOKUP_TIMEOUT_MS, EDGE_READ_TIMEOUT_MS, KV_READ_TIMEOUT_MS } from "./cf-cache-constants.js";
|
|
28
9
|
export { CACHE_STALE_AT_HEADER, CACHE_STATUS_HEADER, CACHE_TAGS_HEADER, CACHE_TAGGED_AT_HEADER, TAG_MARKER_PREFIX, CACHE_REVALIDATING_AT_HEADER, MAX_REVALIDATION_INTERVAL, EDGE_LOOKUP_TIMEOUT_MS, EDGE_READ_TIMEOUT_MS, KV_READ_TIMEOUT_MS, };
|
|
29
10
|
import type { KVNamespace, CFCacheReadDebugEvent, CFCacheDebug, CFCacheStoreOptions } from "./cf-cache-types.js";
|
|
30
11
|
export type { KVNamespace, CFCacheReadDebugEvent, CFCacheDebug, CFCacheStoreOptions, };
|
|
12
|
+
/**
|
|
13
|
+
* @internal Reset the per-isolate shell and marker memos and the once-per-
|
|
14
|
+
* isolate warnings (tests).
|
|
15
|
+
*/
|
|
16
|
+
export declare function resetCFShellMemoForTests(): void;
|
|
31
17
|
export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
32
18
|
readonly supportsPassiveShellReads: true;
|
|
33
19
|
readonly defaults?: CacheDefaults;
|
|
@@ -39,6 +25,19 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
|
|
|
39
25
|
private readonly edgeLookupTimeoutMs;
|
|
40
26
|
private readonly edgeReadTimeoutMs;
|
|
41
27
|
private readonly kvReadTimeoutMs;
|
|
28
|
+
/**
|
|
29
|
+
* The shell snapshot read's budgets. `read` (getShell: partial replay, the
|
|
30
|
+
* testing helpers) has nothing committed, so a slow snapshot is a quick
|
|
31
|
+
* miss at kvReadTimeoutMs. `document` (readShellDocument, the serve path)
|
|
32
|
+
* has sent the prelude and cannot finish without the snapshot's doc
|
|
33
|
+
* record; a timeout there reloads the page, so it waits at least
|
|
34
|
+
* SHELL_SNAPSHOT_READ_MIN_TIMEOUT_MS. `kvReadTimeoutMs <= 0` keeps both
|
|
35
|
+
* unbounded.
|
|
36
|
+
*/
|
|
37
|
+
private readonly snapshotBudgets;
|
|
38
|
+
private readonly memo;
|
|
39
|
+
/** @internal SegmentCacheStore.freshReadsWindowMs */
|
|
40
|
+
readonly freshReadsWindowMs: number;
|
|
42
41
|
private readonly debug?;
|
|
43
42
|
private readonly kv?;
|
|
44
43
|
/** True when constructed without KV: no durable tag history (see ctor). */
|
|
@@ -72,7 +71,11 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
|
|
|
72
71
|
* @internal
|
|
73
72
|
*/
|
|
74
73
|
private emitDebug;
|
|
75
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Build-time-gated shell tier trace for deployed cross-colo diagnostics.
|
|
76
|
+
* A read's `stats` print as its timings, after the other details and before
|
|
77
|
+
* `expiresAt`.
|
|
78
|
+
*/
|
|
76
79
|
private debugShell;
|
|
77
80
|
/**
|
|
78
81
|
* Resolve the cache-key base URL for the current cache operation.
|
|
@@ -216,7 +219,9 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
|
|
|
216
219
|
get(key: string): Promise<CacheGetResult | null | CacheReadError>;
|
|
217
220
|
/**
|
|
218
221
|
* Store entry data with TTL and optional SWR window.
|
|
219
|
-
*
|
|
222
|
+
* Resolves once the L1 write lands, per the SegmentCacheStore.set contract
|
|
223
|
+
* (also held by waitUntil when available); the KV write stays in the
|
|
224
|
+
* background.
|
|
220
225
|
* When KV is configured, also persists to L2.
|
|
221
226
|
*/
|
|
222
227
|
set(key: string, data: CachedEntryData, ttl: number, swr?: number): Promise<void>;
|
|
@@ -269,7 +274,7 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
|
|
|
269
274
|
/**
|
|
270
275
|
* Generation gate for shell writes and the capture scheduler
|
|
271
276
|
* (isTagsInvalidatedSince). With KV it is the durable marker cascade.
|
|
272
|
-
* Without KV there are no markers: in purge mode the per-request
|
|
277
|
+
* Without KV there are no markers: in purge mode the per-request mask is
|
|
273
278
|
* the only signal — a capture racing THIS request's updateTag() is still
|
|
274
279
|
* rejected (read-your-own-writes), while cross-request races are bounded
|
|
275
280
|
* by ttl+swr exactly like the data families' purge-mode writes. Without
|
|
@@ -278,31 +283,121 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
|
|
|
278
283
|
*/
|
|
279
284
|
private isShellGenerationInvalidated;
|
|
280
285
|
/**
|
|
281
|
-
* Get a cached PPR shell entry
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
+
* Get a cached PPR shell entry: the public custom-store contract, built on
|
|
287
|
+
* readShellDocument. It waits for the snapshot and re-encodes the prelude to
|
|
288
|
+
* the base64 `ShellCacheEntry.prelude` the contract carries (native
|
|
289
|
+
* `toBase64` in workerd). Partial replay and the testing helpers read
|
|
290
|
+
* through here; the document serve path reads readShellDocument directly.
|
|
291
|
+
* A snapshot that failed to read is a miss here: with no committed response
|
|
292
|
+
* to protect, an entry without its pins is worse than none.
|
|
286
293
|
*/
|
|
287
294
|
getShell(key: string): Promise<{
|
|
288
295
|
entry: ShellCacheEntry;
|
|
289
296
|
shouldRevalidate?: boolean;
|
|
290
297
|
} | null>;
|
|
291
298
|
/**
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
* (
|
|
299
|
+
* @internal Prelude-first read of a shell (SegmentCacheStore
|
|
300
|
+
* .readShellDocument). Cache API first, then KV; both tiers hold one
|
|
301
|
+
* cf-shell-frame body, so the prelude, postponed state, snapshot, versions,
|
|
302
|
+
* and generation metadata cannot mix. Before it resolves it reads only the
|
|
303
|
+
* head and the prelude's bytes, and the tag-marker read runs alongside the
|
|
304
|
+
* prelude read (it starts as soon as the head is parsed, and is awaited
|
|
305
|
+
* before this resolves, so an invalidated shell is never returned). The
|
|
306
|
+
* snapshot is the rest of the same body, read on its own promise, bounded
|
|
307
|
+
* by the document read's budget (`snapshotBudgets.document`; getShell uses
|
|
308
|
+
* `snapshotBudgets.read`). SWR is a plain staleness flag; the capture
|
|
309
|
+
* scheduler's in-flight set is the recapture stampede guard.
|
|
310
|
+
*/
|
|
311
|
+
readShellDocument(key: string, options?: ShellDocumentReadOptions): Promise<ShellDocumentRead | null>;
|
|
312
|
+
private readShellDocumentWithin;
|
|
313
|
+
/** @internal SegmentCacheStore.dropShellMemo */
|
|
314
|
+
dropShellMemo(key: string): void;
|
|
315
|
+
/**
|
|
316
|
+
* Start the marker reads of the tags this shell key carried last time (and
|
|
317
|
+
* the route's static tags) alongside the entry read (readShellTagMarker),
|
|
318
|
+
* so the marker check after the head reuses them. With KV only: without it
|
|
319
|
+
* there are no markers to read. A wrong hint costs a wasted read; the check
|
|
320
|
+
* itself uses the head's tags.
|
|
321
|
+
*/
|
|
322
|
+
private prefetchShellMarkers;
|
|
323
|
+
/**
|
|
324
|
+
* A HIT: remember the entry's tags for the next read's hints, and fill the
|
|
325
|
+
* marker row. The shell-read marker values stay in their own record: the
|
|
326
|
+
* request's data reads (the tail's, or a partial navigation's matchPartial
|
|
327
|
+
* on the same context) read their markers as before.
|
|
328
|
+
*/
|
|
329
|
+
private recordShellHit;
|
|
330
|
+
/** The isolate marker memo's key for a tag: markers are per namespace and version. */
|
|
331
|
+
private markerMemoKey;
|
|
332
|
+
/** The memo key: one per namespace, build version, base URL, and shell key. */
|
|
333
|
+
private shellMemoKey;
|
|
334
|
+
/**
|
|
335
|
+
* Serve a memoized shell (shell-memo.ts). The tag-marker check is the only
|
|
336
|
+
* store I/O left, so it is started first and awaited before anything is
|
|
337
|
+
* returned: nothing of an invalidated shell reaches the response. A shell
|
|
338
|
+
* that went stale since it was memoized, or that predates a tag this
|
|
339
|
+
* isolate is invalidating or just invalidated (RecentTagInvalidations), is
|
|
340
|
+
* dropped and read from the store (SWR recapture scheduling and each mode's
|
|
341
|
+
* invalidation semantics stay with the store read). That read keeps the
|
|
342
|
+
* caller's `tagHints`: cfTagHints may have evicted the key while the memo
|
|
343
|
+
* still holds it.
|
|
344
|
+
*/
|
|
345
|
+
private readMemoizedShell;
|
|
346
|
+
/**
|
|
347
|
+
* Stats for one shell read, allocated only when the request collects
|
|
348
|
+
* `debugPerformance` metrics or the build has the internal shell trace on,
|
|
349
|
+
* so a normal HIT takes no timestamps for them.
|
|
350
|
+
*/
|
|
351
|
+
private shellReadStats;
|
|
352
|
+
/**
|
|
353
|
+
* Read a frame's head and prelude under one budget, starting the
|
|
354
|
+
* tag-marker read (`isInvalidated`) the moment the head is parsed so it
|
|
355
|
+
* overlaps the prelude read, then await it. Expired heads are rejected
|
|
356
|
+
* before the marker read starts.
|
|
357
|
+
*/
|
|
358
|
+
private openShellFrame;
|
|
359
|
+
/**
|
|
360
|
+
* The ShellDocumentRead for an opened frame. Its snapshot promise starts
|
|
361
|
+
* reading the rest of the body now (off the commit path, not after it),
|
|
362
|
+
* bounded by `budget` ({@link snapshotReadBudget}), and is registered with
|
|
363
|
+
* waitUntil so the KV tier's L1 promotion and a corrupt entry's eviction
|
|
364
|
+
* still run when nothing awaits it (a read that is not served). A snapshot
|
|
365
|
+
* whose length differs from the head's `sl`, or that fails to parse, evicts
|
|
366
|
+
* the entry from both tiers and reports cache-corrupt. Every failure
|
|
367
|
+
* resolves undefined and names itself on `snapshotFailure` (`unavailable`
|
|
368
|
+
* for a timeout or read error, `corrupt` otherwise): a document HIT then
|
|
369
|
+
* degrades (rsc-rendering.ts serveShellHit), and getShell misses. Only
|
|
370
|
+
* a read whose snapshot parsed goes into the per-isolate memo (when still
|
|
371
|
+
* fresh), and `promote` receives its snapshot bytes (the KV tier's L1
|
|
372
|
+
* promotion). `headers` are the L1 response's, kept for the KV-less
|
|
373
|
+
* memo-hit marker check.
|
|
374
|
+
*/
|
|
375
|
+
private shellDocumentRead;
|
|
376
|
+
/** Delete a shell from both tiers (non-blocking). */
|
|
377
|
+
private evictShell;
|
|
378
|
+
/**
|
|
379
|
+
* Store a PPR shell in Cache API and, when KV is configured, KV — the same
|
|
380
|
+
* cf-shell-frame bytes in both. The shared write is registered with
|
|
381
|
+
* waitUntil and awaited so invalidation rejection can be acknowledged to
|
|
382
|
+
* the capture scheduler. A KV-less store is L1-only by design (edge-only
|
|
383
|
+
* ppr — see the section comment).
|
|
384
|
+
*
|
|
385
|
+
* KV rejects an expirationTtl below KV_MIN_EXPIRATION_TTL, so a shorter
|
|
386
|
+
* retention (a `ppr` ttl + swr under 60, or a shell capped to a short or
|
|
387
|
+
* aging route cache() entry) is written with that floor: the frame head's deadlines (`s`, `e`) are the
|
|
388
|
+
* entry's, and every read checks `e` (openShellFrame), so the extra KV
|
|
389
|
+
* lifetime is never served, and the shell is readable from every colo, not
|
|
390
|
+
* only from the capturing colo's L1.
|
|
298
391
|
*/
|
|
299
392
|
putShell(key: string, entry: ShellCacheEntry, ttlSeconds?: number, swrSeconds?: number, tags?: string[]): Promise<"stored" | "invalidated" | "uncacheable" | void>;
|
|
300
|
-
/**
|
|
301
|
-
private
|
|
302
|
-
/**
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
393
|
+
/** The Cache API representation of a shell frame. */
|
|
394
|
+
private shellFrameResponse;
|
|
395
|
+
/**
|
|
396
|
+
* KV shell fallback: the same prelude-first read, then L1 promotion. After
|
|
397
|
+
* an L1 attempt, `stats` is that read's and gains the L1 attempt's time
|
|
398
|
+
* (`l1MissMs`) before its per-tier fields restart for KV.
|
|
399
|
+
*/
|
|
400
|
+
private kvReadShellDocument;
|
|
306
401
|
/** Promote a valid KV shell into the per-colo Cache API tier. */
|
|
307
402
|
private promoteShellToL1;
|
|
308
403
|
/**
|
|
@@ -442,15 +537,16 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
|
|
|
442
537
|
* configured), invalidateTags() evicts L1 entries via a Cloudflare
|
|
443
538
|
* purge-by-tag call, so a hit that SURVIVED is trusted without a per-read
|
|
444
539
|
* marker lookup — that skipped lookup is the entire point of purge mode.
|
|
445
|
-
* Only the
|
|
446
|
-
* request that ran updateTag() still
|
|
447
|
-
* propagation window (read-your-own-writes).
|
|
540
|
+
* Only the request's mask and its memo are consulted (synchronous, no KV
|
|
541
|
+
* read) so a request that ran updateTag() still misses its own entries
|
|
542
|
+
* during the purge propagation window (read-your-own-writes).
|
|
448
543
|
*
|
|
449
544
|
* The trust is conditional on the entry actually CARRYING this store's
|
|
450
545
|
* entry Cache-Tag tokens (`headers`): an entry a purge cannot reach — one
|
|
451
546
|
* written before the tokens existed, or whose tag set overflowed the
|
|
452
547
|
* Cache-Tag header limit — keeps the full marker check, or purge mode
|
|
453
|
-
* would serve it stale until TTL with no eviction path.
|
|
548
|
+
* would serve it stale until TTL with no eviction path. KV-less there are
|
|
549
|
+
* no markers, so it gets the per-request mask check like any hit (#973).
|
|
454
550
|
*
|
|
455
551
|
* Without tagPurge this is the full marker cascade. KV-tier reads and
|
|
456
552
|
* shells always use isGloballyInvalidated directly: purge cannot reach KV,
|
|
@@ -463,9 +559,8 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
|
|
|
463
559
|
/**
|
|
464
560
|
* Read a tag's latest invalidation timestamp (or null if never invalidated)
|
|
465
561
|
* through the cascade: per-request memo -> per-colo L1 cache (only when
|
|
466
|
-
* tagCacheTtl > 0) -> KV (the global truth).
|
|
467
|
-
*
|
|
468
|
-
* and every KV/L1 result is written back into the memo. A Cache API miss
|
|
562
|
+
* tagCacheTtl > 0) -> KV (the global truth). Every KV/L1 result is written
|
|
563
|
+
* back into the memo, so the request reads one value per tag. A Cache API miss
|
|
469
564
|
* always falls through to KV; absence is represented by a cached sentinel,
|
|
470
565
|
* never by a miss.
|
|
471
566
|
*
|
|
@@ -475,6 +570,18 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
|
|
|
475
570
|
* @internal
|
|
476
571
|
*/
|
|
477
572
|
private readTagMarker;
|
|
573
|
+
/**
|
|
574
|
+
* A PPR shell read's marker for `tag`: this request's own store read if it
|
|
575
|
+
* has one, else through the per-isolate marker memo (isolate-tag-memo.ts,
|
|
576
|
+
* stale-while-revalidate
|
|
577
|
+
* under `markerFreshMs` / `markerMaxStaleMs`; a request carrying the
|
|
578
|
+
* fresh-reads cookie reads L1/KV). The value is kept in the request's
|
|
579
|
+
* shell-read record, never in the per-request memo the data families read,
|
|
580
|
+
* HIT or MISS. A store read still lands in the per-request memo, as any
|
|
581
|
+
* marker read does; it is not stale. A timed-out read fails open for this
|
|
582
|
+
* request and is not memoized.
|
|
583
|
+
*/
|
|
584
|
+
private readShellTagMarker;
|
|
478
585
|
/**
|
|
479
586
|
* Uncached body of readTagMarker: L1 (per-colo Cache API, opt-in via
|
|
480
587
|
* tagCacheTtl) -> KV. Writes the resolved value back into the memo.
|
|
@@ -554,39 +661,84 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
|
|
|
554
661
|
* @internal
|
|
555
662
|
*/
|
|
556
663
|
private putTagMarkerL1;
|
|
664
|
+
/**
|
|
665
|
+
* SegmentCacheStore.isTagsInvalidatedSince: the SAME KV markers used by
|
|
666
|
+
* runtime envelopes also evict immutable build shells, and gate the writes
|
|
667
|
+
* of cache executions that started before an invalidation (#977,
|
|
668
|
+
* tag-invalidation.ts predatesInvalidation). Marker >= since. A marker read
|
|
669
|
+
* that fails or times out answers false (fail open), or true for the write
|
|
670
|
+
* gate (`failClosed`). With KV each marker is read past the per-request
|
|
671
|
+
* memo (isInvalidatedSincePastMemo); KV-less it degrades to the per-request
|
|
672
|
+
* mask in purge mode and to false otherwise — see
|
|
673
|
+
* isShellGenerationInvalidated.
|
|
674
|
+
*/
|
|
675
|
+
isTagsInvalidatedSince(tags: string[], sinceMs: number, options?: {
|
|
676
|
+
failClosed?: boolean;
|
|
677
|
+
}): Promise<boolean>;
|
|
678
|
+
/**
|
|
679
|
+
* isTagsInvalidatedSince with KV, never answered from a marker value this
|
|
680
|
+
* request read before the asking gate. A write gate asks after an
|
|
681
|
+
* execution that can run for seconds, and the per-request memo can hold
|
|
682
|
+
* the marker the request read before it (a stale hit reads its entry's
|
|
683
|
+
* markers, then its refresh runs): answered from it, another isolate's
|
|
684
|
+
* invalidation in between let the refresh write.
|
|
685
|
+
*
|
|
686
|
+
* - This request's own invalidations answer from its mask, checked again
|
|
687
|
+
* once the reads are back: a read in flight when the request invalidated
|
|
688
|
+
* one of the tags resolved to the marker before it.
|
|
689
|
+
* - Each tag is read through gateMarkerRead (request-tag-mask.ts): gates
|
|
690
|
+
* asking while a read is in flight share it; a settled read is not
|
|
691
|
+
* reused.
|
|
692
|
+
* - The read returns its own value from L1 (tagCacheTtl) or KV, not one an
|
|
693
|
+
* older read of the tag memoized while it was in flight, and fills the
|
|
694
|
+
* memo only when it holds nothing, so a build shell's check still serves
|
|
695
|
+
* the request's later reads. A timed-out read fills nothing.
|
|
696
|
+
* - It publishes to L1 unless the request masked the tag
|
|
697
|
+
* (fetchTagMarker), and never without a request context (a detached
|
|
698
|
+
* waitUntil task), where a mask cannot be told apart.
|
|
699
|
+
* - A read that fails or times out answers `failClosed`: the write gate
|
|
700
|
+
* counts it as an invalidation, other callers as none.
|
|
701
|
+
* @internal
|
|
702
|
+
*/
|
|
703
|
+
private isInvalidatedSincePastMemo;
|
|
557
704
|
/**
|
|
558
705
|
* Invalidate every entry tagged with any of `tags`. Receives the whole batch
|
|
559
706
|
* from one updateTag()/revalidateTag() call so the eager-purge hook fires
|
|
560
|
-
* ONCE (one CDN purge request, not one per tag).
|
|
561
|
-
*
|
|
707
|
+
* ONCE (one CDN purge request, not one per tag). Before its first await it
|
|
708
|
+
* masks the tags for the rest of the request (maskTagsForRequest), which is
|
|
709
|
+
* all revalidateTag() waits for. For each tag it then records the KV marker
|
|
710
|
+
* (the durable cross-colo truth that reads compare taggedAt against) and
|
|
562
711
|
* writes the fresh marker straight into this colo's L1 (write-through, NOT
|
|
563
712
|
* delete - a delete would let the next read re-read a not-yet-converged KV
|
|
564
|
-
* value and re-arm the stale window)
|
|
565
|
-
*
|
|
566
|
-
*
|
|
567
|
-
*
|
|
568
|
-
*
|
|
569
|
-
*
|
|
570
|
-
* tagCacheTtl).
|
|
713
|
+
* value and re-arm the stale window). In purge mode (tagPurge) it then
|
|
714
|
+
* AWAITS the consumer's purge-by-tag call with the entry Cache-Tags — the
|
|
715
|
+
* eviction the per-read marker skip on L1 hits relies on. Finally fires
|
|
716
|
+
* onRevalidateTag with the namespaced lookup Cache-Tags so a consumer purge
|
|
717
|
+
* evicts the cached lookups in other colos promptly (otherwise they converge
|
|
718
|
+
* within tagCacheTtl).
|
|
571
719
|
*
|
|
572
|
-
* Durable-write integrity: the
|
|
573
|
-
* runs ONLY after that tag's KV marker
|
|
574
|
-
* fails (transient error; over-limit
|
|
575
|
-
* than rejected), this rejects with
|
|
576
|
-
* failed tags so an awaiting updateTag() surfaces the failure instead of
|
|
577
|
-
* silently reporting success while other requests/colos serve stale data.
|
|
578
|
-
*
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
* Shell tag-generation gate (SegmentCacheStore.isTagsInvalidatedSince): the
|
|
582
|
-
* SAME KV markers used by runtime envelopes also evict immutable build shells
|
|
583
|
-
* and captures whose write races updateTag(). Thin public wrapper over the
|
|
584
|
-
* shell generation check (marker >= since, fail open); KV-less it degrades
|
|
585
|
-
* to the per-request memo in purge mode and to false otherwise — see
|
|
586
|
-
* isShellGenerationInvalidated.
|
|
720
|
+
* Durable-write integrity: the write-through other requests read (the
|
|
721
|
+
* isolate marker memo and L1) for a tag runs ONLY after that tag's KV marker
|
|
722
|
+
* write is confirmed. If any KV write fails (transient error; over-limit
|
|
723
|
+
* keys are normalized by toKVKey rather than rejected), this rejects with
|
|
724
|
+
* the failed tags so an awaiting updateTag() surfaces the failure instead of
|
|
725
|
+
* silently reporting success while other requests/colos serve stale data.
|
|
726
|
+
* The purge and onRevalidateTag still fire for the whole batch once the
|
|
727
|
+
* puts settle, whether or not one failed (they are additive); the rejection
|
|
728
|
+
* follows them.
|
|
587
729
|
*/
|
|
588
|
-
isTagsInvalidatedSince(tags: string[], sinceMs: number): Promise<boolean>;
|
|
589
730
|
invalidateTags(tags: string[]): Promise<void>;
|
|
731
|
+
/**
|
|
732
|
+
* The request-local half of invalidateTags(), run before its first await
|
|
733
|
+
* (#973): the request masks the tags (request-tag-mask.ts), so every later
|
|
734
|
+
* read in the request (data families, purge-mode L1 hits, shell reads)
|
|
735
|
+
* treats them as invalidated at `invalidatedAt` without waiting for the KV
|
|
736
|
+
* put or the purge that revalidateTag() leaves to waitUntil. Skipped where
|
|
737
|
+
* no read consults the mask (neither KV nor tagPurge).
|
|
738
|
+
*/
|
|
739
|
+
private maskTagsForRequest;
|
|
740
|
+
/** invalidateTags' marker writes, tag purge, and hooks. */
|
|
741
|
+
private writeTagInvalidation;
|
|
590
742
|
/**
|
|
591
743
|
* KV fallback for segment cache reads.
|
|
592
744
|
* Returns null if KV is not configured, entry is missing, or expired.
|
|
@@ -2,6 +2,7 @@ import type { ExecutionContext } from "../../types/request-scope.js";
|
|
|
2
2
|
import type { CacheDefaults } from "../types.js";
|
|
3
3
|
import type { RequestContext } from "../../server/request-context.js";
|
|
4
4
|
import type { CloudflareZonePurgeOptions } from "./cf-zone-purge.js";
|
|
5
|
+
import type { StoreMemoOptions } from "../shell-memo.js";
|
|
5
6
|
/**
|
|
6
7
|
* Minimal Cloudflare KV Namespace interface.
|
|
7
8
|
* Avoids hard dependency on @cloudflare/workers-types.
|
|
@@ -294,9 +295,12 @@ export interface CFCacheStoreOptions<TEnv = unknown> {
|
|
|
294
295
|
edgeLookupTimeoutMs?: number;
|
|
295
296
|
/**
|
|
296
297
|
* Latency budget (ms) for reading the BODY of a matched L1 entry
|
|
297
|
-
* (response.json()
|
|
298
|
-
*
|
|
299
|
-
*
|
|
298
|
+
* (response.json(); for a PPR shell, only its head and prelude — the
|
|
299
|
+
* snapshot behind them is read after the HIT commits, under
|
|
300
|
+
* {@link kvReadTimeoutMs}). CF streams the cache body lazily, so the
|
|
301
|
+
* multi-second tail can appear after `match` already resolved; this bounds
|
|
302
|
+
* it. On timeout the read is treated as a miss and falls through to L2/KV or
|
|
303
|
+
* render.
|
|
300
304
|
*
|
|
301
305
|
* Separate from {@link edgeLookupTimeoutMs} because a healthy body read
|
|
302
306
|
* (fetch + JSON parse of a potentially large Flight payload) takes a little
|
|
@@ -309,7 +313,11 @@ export interface CFCacheStoreOptions<TEnv = unknown> {
|
|
|
309
313
|
* Latency budget (ms) for an L2 (KV) read. KV is the last cache tier before a
|
|
310
314
|
* full render and is a global store (~50ms healthy, seconds when degraded);
|
|
311
315
|
* this bounds it so a slow namespace cannot pin the request. On timeout the
|
|
312
|
-
* read is treated as a miss (no L1 promote) and falls through to render.
|
|
316
|
+
* read is treated as a miss (no L1 promote) and falls through to render. For
|
|
317
|
+
* a PPR shell it covers opening the KV value and reading its head and
|
|
318
|
+
* prelude; separately, it bounds the shell's snapshot read (either tier),
|
|
319
|
+
* which runs off the commit path: a timeout there lets the tail run without
|
|
320
|
+
* the snapshot's pins.
|
|
313
321
|
*
|
|
314
322
|
* Defaults to {@link KV_READ_TIMEOUT_MS} (170) -- a few multiples above the
|
|
315
323
|
* ~50ms healthy read, with headroom for legitimate tails (large payloads / far
|
|
@@ -319,6 +327,25 @@ export interface CFCacheStoreOptions<TEnv = unknown> {
|
|
|
319
327
|
* <= 0) to disable and always await KV.
|
|
320
328
|
*/
|
|
321
329
|
kvReadTimeoutMs?: number;
|
|
330
|
+
/**
|
|
331
|
+
* Per-isolate memos ({@link StoreMemoOptions}), one set per isolate and
|
|
332
|
+
* shared by every CFCacheStore in it. `shellMs` (default 2000) and
|
|
333
|
+
* `shellMaxBytes` (default 16 MiB) size the PPR shell memo;
|
|
334
|
+
* `markerFreshMs` (default 1000) and `markerMaxStaleMs` (default 10000)
|
|
335
|
+
* the tag-marker memo PPR shell reads use with KV bound (served
|
|
336
|
+
* stale-while-revalidate). A memo HIT still runs the tag-marker check; the
|
|
337
|
+
* isolate that invalidates drops its own shell copies (and keeps reads in
|
|
338
|
+
* flight from memoizing them again) and writes the new marker into its
|
|
339
|
+
* marker memo, and its own `putShell` replaces its copy. Another isolate
|
|
340
|
+
* rejects a memoized shell once its marker memo sees the invalidation (up
|
|
341
|
+
* to `markerMaxStaleMs`). Without KV, in purge mode, the purge does not
|
|
342
|
+
* reach other isolates' memos: they serve the purged shell until their
|
|
343
|
+
* window passes. The response of a request that ran `updateTag()` /
|
|
344
|
+
* `revalidateTag()` sets the fresh-reads cookie, and the same user's
|
|
345
|
+
* requests carrying it skip both memos. `{ shellMs: 0 }` turns the shell
|
|
346
|
+
* memo off, `{ markerFreshMs: 0 }` the marker memo.
|
|
347
|
+
*/
|
|
348
|
+
memo?: StoreMemoOptions;
|
|
322
349
|
/**
|
|
323
350
|
* Emit a {@link CFCacheReadDebugEvent} per L1 read. `true` logs to console
|
|
324
351
|
* (visible via `wrangler tail`); pass a function to capture events directly.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prelude-first binary layout of a CFCacheStore PPR shell entry, shared by the
|
|
3
|
+
* Cache API body and the KV value (issue #941; docs/design/shell-entry-layout.md):
|
|
4
|
+
*
|
|
5
|
+
* "RSH1" | head length, 8 hex digits | head JSON | prelude bytes | snapshot JSON
|
|
6
|
+
*
|
|
7
|
+
* A document HIT needs the head (versions, freshness, tags, postponed) and the
|
|
8
|
+
* raw prelude before its first byte, and the capture snapshot only in the
|
|
9
|
+
* tail. Laying them out in that order lets the serve path read the head,
|
|
10
|
+
* start the tag-marker read, read exactly `pl` prelude bytes, and commit while
|
|
11
|
+
* the snapshot bytes are still arriving. One record keeps the prelude and the
|
|
12
|
+
* snapshot generation-coupled by construction: a Cache API entry and a KV
|
|
13
|
+
* value are written and read whole, so a split read can never pair one
|
|
14
|
+
* capture's prelude with another's pins.
|
|
15
|
+
*
|
|
16
|
+
* Every byte is UTF-8 text when the prelude is (React's HTML through
|
|
17
|
+
* TextEncoder always is), so the KV value is stored as a string (the
|
|
18
|
+
* KVNamespace contract's `put(key, string)`) and read back as the same bytes
|
|
19
|
+
* with `{ type: "stream" }`.
|
|
20
|
+
*/
|
|
21
|
+
import type { ShellSnapshotRecord } from "../types.js";
|
|
22
|
+
/** The frame head: every entry field except the prelude and the snapshot. */
|
|
23
|
+
export interface ShellFrameHead {
|
|
24
|
+
/** ShellCacheEntry.reactVersion */
|
|
25
|
+
rv: string;
|
|
26
|
+
/** ShellCacheEntry.buildVersion */
|
|
27
|
+
bv: string;
|
|
28
|
+
/** Capture-generation start time (ms epoch), used by tag marker checks. */
|
|
29
|
+
c: number;
|
|
30
|
+
/** When the entry becomes stale (ms epoch). */
|
|
31
|
+
s: number;
|
|
32
|
+
/** When the entry hard-expires (ms epoch). */
|
|
33
|
+
e: number;
|
|
34
|
+
/** Cache tags (for distributed tag invalidation). */
|
|
35
|
+
t?: string[];
|
|
36
|
+
/** Timestamp when tags were attached (ms epoch). */
|
|
37
|
+
ta?: number;
|
|
38
|
+
/** ShellCacheEntry.initialTheme */
|
|
39
|
+
i?: string;
|
|
40
|
+
/** ShellCacheEntry.docKey; navigation replay needs it after any round trip. */
|
|
41
|
+
dk?: string;
|
|
42
|
+
/** ShellCacheEntry.prunedRecords (diagnostic). */
|
|
43
|
+
pr?: string;
|
|
44
|
+
/** ShellCacheEntry.navigationOnly; its partial-context prelude is not document-safe. */
|
|
45
|
+
no?: true;
|
|
46
|
+
/** ShellCacheEntry.postponed. Absent iff `no` (no document half is stored). */
|
|
47
|
+
po?: string | null;
|
|
48
|
+
/** Prelude byte length (0 for a navigationOnly entry). */
|
|
49
|
+
pl: number;
|
|
50
|
+
/**
|
|
51
|
+
* Snapshot byte length (0 when the entry has none). A body whose snapshot
|
|
52
|
+
* part is any other length was truncated or corrupted.
|
|
53
|
+
*/
|
|
54
|
+
sl: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Serialize a shell entry into one prelude-first frame. Throws when the head's
|
|
58
|
+
* byte lengths (`pl`, `sl`) do not describe `prelude` and `snapshot`.
|
|
59
|
+
*/
|
|
60
|
+
export declare function encodeShellFrame(head: ShellFrameHead, prelude: Uint8Array, snapshot?: Uint8Array): Uint8Array<ArrayBuffer>;
|
|
61
|
+
/**
|
|
62
|
+
* The frame as the string KV stores. Throws when the prelude is not valid
|
|
63
|
+
* UTF-8, which would not round-trip through a string.
|
|
64
|
+
*/
|
|
65
|
+
export declare function shellFrameToText(frame: Uint8Array): string;
|
|
66
|
+
/** UTF-8 JSON of a snapshot, or undefined when there is none to store. */
|
|
67
|
+
export declare function encodeShellSnapshot(snapshot: ShellSnapshotRecord[] | undefined): Uint8Array | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Parse the snapshot bytes that follow the prelude. Zero bytes means the entry
|
|
70
|
+
* carries no snapshot. Throws on anything else that is not a JSON array: the
|
|
71
|
+
* caller treats that as corruption.
|
|
72
|
+
*/
|
|
73
|
+
export declare function parseShellSnapshot(bytes: Uint8Array): ShellSnapshotRecord[] | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Incremental reader over a streamed frame (a Cache API response body or a KV
|
|
76
|
+
* `{ type: "stream" }` value). The serve path reads the head and exactly the
|
|
77
|
+
* prelude's bytes before it commits; the snapshot is whatever remains.
|
|
78
|
+
*/
|
|
79
|
+
export declare class ShellFrameReader {
|
|
80
|
+
private readonly reader;
|
|
81
|
+
private chunks;
|
|
82
|
+
private buffered;
|
|
83
|
+
private done;
|
|
84
|
+
/** Bytes of the prefix and head, once readHead has read them. */
|
|
85
|
+
headBytes: number;
|
|
86
|
+
constructor(body: ReadableStream<Uint8Array>);
|
|
87
|
+
/** Exactly `n` bytes, or null when the stream ends first. */
|
|
88
|
+
take(n: number): Promise<Uint8Array | null>;
|
|
89
|
+
/** The head, or null when the magic, length, or JSON is malformed or short. */
|
|
90
|
+
readHead(): Promise<ShellFrameHead | null>;
|
|
91
|
+
/** Everything after what was taken, to the end of the stream. */
|
|
92
|
+
readRest(): Promise<Uint8Array>;
|
|
93
|
+
/** Stop reading and release the body. */
|
|
94
|
+
cancel(): void;
|
|
95
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { MarkerMemoOutcome } from "../isolate-tag-memo.js";
|
|
1
2
|
/**
|
|
2
3
|
* Cache-API path prefix for the optional per-colo L1 cache of tag-invalidation
|
|
3
4
|
* markers (enabled by tagCacheTtl). Distinct from data keys (doc:/fn:/segment)
|
|
@@ -12,4 +13,22 @@ export declare const TAG_MARKER_CACHE_PREFIX = "__tagmarker__/";
|
|
|
12
13
|
*/
|
|
13
14
|
export declare const TAG_MARKER_ABSENT = "none";
|
|
14
15
|
export declare function getTagMarkerMemo(ctx: object, store: object): Map<string, number | null>;
|
|
16
|
+
/**
|
|
17
|
+
* A request's PPR shell-read marker reads (issue #941), kept apart from the
|
|
18
|
+
* per-request memo above, HIT or MISS. A shell read may answer a marker from
|
|
19
|
+
* the isolate marker memo (isolate-tag-memo.ts), up to `markerMaxStaleMs`
|
|
20
|
+
* old; the data families must never see that value. Scar tissue: first the
|
|
21
|
+
* shell read wrote into the per-request memo, so a MISS request rendered and
|
|
22
|
+
* stored its `"use cache"` data under a stale marker; then a HIT copied its
|
|
23
|
+
* entry's tags across, which a partial navigation's matchPartial (on the same
|
|
24
|
+
* context as its replay gate's getShell) read, and a `cache()` segment it
|
|
25
|
+
* wrote kept the stale item past `markerMaxStaleMs` under a fresh taggedAt.
|
|
26
|
+
*/
|
|
27
|
+
export interface ShellMarkerReads {
|
|
28
|
+
/** The read of each tag, settled or in flight (collapses concurrent reads). */
|
|
29
|
+
reads: Map<string, Promise<number | null>>;
|
|
30
|
+
/** How the isolate memo answered each tag (the marker row's `memo=`). */
|
|
31
|
+
outcomes: Map<string, MarkerMemoOutcome>;
|
|
32
|
+
}
|
|
33
|
+
export declare function getShellMarkerReads(ctx: object, store: object): ShellMarkerReads;
|
|
15
34
|
export declare function getTagMarkerInflight(ctx: object, store: object): Map<string, Promise<number | null>>;
|
|
@@ -13,6 +13,16 @@
|
|
|
13
13
|
* 4. If miss → run handler, cache if response has cache headers
|
|
14
14
|
*/
|
|
15
15
|
import type { MiddlewareFn, MiddlewareContext } from "../router/middleware.js";
|
|
16
|
+
import { type RequestContext } from "../server/request-context.js";
|
|
17
|
+
/**
|
|
18
|
+
* Whether this cache will store the response the current render produces, as
|
|
19
|
+
* far as the render can tell: the middleware runs it (a MISS or a stale
|
|
20
|
+
* refresh) and the response stub already opts in. payloadInitialTheme
|
|
21
|
+
* (rsc/full-payload.ts) then renders the no-cookie default theme (#978). A
|
|
22
|
+
* Cache-Control written after next() is not on the stub yet; for that render
|
|
23
|
+
* shouldCacheResponse refuses a payload marked `_payloadVisitorTheme`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function documentCacheStoresRender(reqCtx: Pick<RequestContext, "_documentCacheRender" | "res">): boolean;
|
|
16
26
|
export interface DocumentCacheOptions<TEnv = any> {
|
|
17
27
|
/**
|
|
18
28
|
* Skip caching for specific paths (e.g., API routes)
|