@rangojs/router 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/rango.js +39 -15
- package/dist/types/browser/event-controller.d.ts +49 -0
- package/dist/types/browser/history-state.d.ts +16 -4
- package/dist/types/browser/navigation-store.d.ts +9 -0
- package/dist/types/browser/navigation-transaction.d.ts +7 -0
- package/dist/types/browser/partial-update.d.ts +2 -1
- package/dist/types/browser/prefetch/default-strategy.d.ts +16 -20
- package/dist/types/browser/rango-state.d.ts +3 -1
- package/dist/types/browser/react/NavigationProvider.d.ts +14 -1
- package/dist/types/browser/react/context.d.ts +25 -0
- package/dist/types/browser/react/location-state-shared.d.ts +84 -10
- package/dist/types/browser/react/location-state.d.ts +7 -2
- package/dist/types/browser/rsc-router.d.ts +13 -2
- package/dist/types/browser/types.d.ts +10 -0
- package/dist/types/build/generate-manifest.d.ts +2 -0
- package/dist/types/cache/background-task.d.ts +22 -0
- package/dist/types/cache/cache-key-utils.d.ts +50 -5
- package/dist/types/cache/cache-policy.d.ts +17 -0
- package/dist/types/cache/cache-scope.d.ts +5 -6
- package/dist/types/cache/cf/cf-cache-store.d.ts +34 -7
- package/dist/types/cache/cf/cf-cache-types.d.ts +12 -4
- package/dist/types/cache/document-cache.d.ts +8 -5
- package/dist/types/cache/handle-snapshot.d.ts +59 -50
- package/dist/types/cache/index.d.ts +3 -1
- package/dist/types/cache/invalidation-order.d.ts +14 -0
- package/dist/types/cache/memory-segment-store.d.ts +33 -1
- package/dist/types/cache/shell-snapshot.d.ts +29 -4
- package/dist/types/cache/store-scope.d.ts +22 -0
- package/dist/types/cache/tag-invalidation.d.ts +7 -6
- package/dist/types/cache/types.d.ts +84 -24
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +35 -17
- package/dist/types/client-urls/navigation.d.ts +7 -1
- package/dist/types/client-urls/optimistic-location.d.ts +10 -4
- package/dist/types/errors.d.ts +6 -0
- package/dist/types/handles/deferred-resolution.d.ts +18 -1
- package/dist/types/internal-suspense-audit.d.ts +1 -0
- package/dist/types/loader-store.d.ts +4 -8
- package/dist/types/outlet-provider.d.ts +2 -1
- package/dist/types/prerender/build-shell-capture.d.ts +8 -6
- package/dist/types/prerender/cloudflare.d.ts +49 -0
- package/dist/types/prerender/create-prerender-trigger.d.ts +121 -0
- package/dist/types/prerender/index.d.ts +12 -0
- package/dist/types/prerender/memory-prerender-store.d.ts +29 -0
- package/dist/types/prerender/on-demand.d.ts +272 -0
- package/dist/types/prerender/producer-guard.d.ts +33 -0
- package/dist/types/prerender/shell-manifest-key.d.ts +1 -18
- package/dist/types/prerender/warm-request.d.ts +65 -0
- package/dist/types/prerender/warm.d.ts +65 -0
- package/dist/types/prerender/writable-store.d.ts +170 -0
- package/dist/types/prerender.d.ts +27 -0
- package/dist/types/reverse.d.ts +1 -1
- package/dist/types/route-content-wrapper.d.ts +3 -1
- package/dist/types/router/error-handling.d.ts +2 -0
- package/dist/types/router/handler-context.d.ts +1 -1
- package/dist/types/router/match-middleware/cache-lookup.d.ts +15 -1
- package/dist/types/router/navigation-snapshot.d.ts +8 -1
- package/dist/types/router/pattern-matching.d.ts +2 -0
- package/dist/types/router/prefetch-default.d.ts +4 -4
- package/dist/types/router/prerender-match.d.ts +23 -1
- package/dist/types/router/route-trie-builder.d.ts +8 -1
- package/dist/types/router/router-context.d.ts +1 -0
- package/dist/types/router/router-interfaces.d.ts +32 -1
- package/dist/types/router/router-options.d.ts +39 -5
- package/dist/types/router/segment-resolution/fresh.d.ts +7 -0
- package/dist/types/router/segment-resolution/loader-cache.d.ts +23 -10
- package/dist/types/router/segment-resolution/revalidation.d.ts +5 -1
- package/dist/types/router/segment-resolution/streamed-handler-boundary.d.ts +43 -0
- package/dist/types/router/trie-matching.d.ts +2 -0
- package/dist/types/router-versions.d.ts +39 -0
- package/dist/types/rsc/handler-context.d.ts +5 -0
- package/dist/types/rsc/shell-build-manifest.d.ts +9 -6
- package/dist/types/rsc/shell-capture-constants.d.ts +20 -5
- package/dist/types/rsc/shell-capture.d.ts +14 -1
- package/dist/types/rsc/shell-serve.d.ts +2 -2
- package/dist/types/rsc/ssr-module-loader.d.ts +15 -0
- package/dist/types/rsc/types.d.ts +12 -6
- package/dist/types/segment-boundary-content.d.ts +17 -0
- package/dist/types/segment-loader-promise.d.ts +2 -6
- package/dist/types/server/build-version-table.d.ts +53 -0
- package/dist/types/server/context.d.ts +12 -4
- package/dist/types/server/handle-store.d.ts +89 -46
- package/dist/types/server/request-context.d.ts +84 -9
- package/dist/types/suspense-audit-react.d.ts +26 -0
- package/dist/types/suspense-audit.d.ts +157 -0
- package/dist/types/testing/action-form.d.ts +34 -0
- package/dist/types/testing/build-versions.d.ts +32 -0
- package/dist/types/testing/dom.entry.d.ts +1 -1
- package/dist/types/testing/flight.entry.d.ts +3 -1
- package/dist/types/testing/index.d.ts +6 -0
- package/dist/types/testing/internal/seed-vars.d.ts +7 -2
- package/dist/types/testing/internal/server-modules.d.ts +8 -0
- package/dist/types/testing/location-state-key.d.ts +5 -1
- package/dist/types/testing/render-route.d.ts +77 -6
- package/dist/types/testing/serve-shell-request.d.ts +55 -4
- package/dist/types/testing/shell-status.d.ts +14 -6
- package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +9 -6
- package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
- package/dist/types/thenable-status.d.ts +6 -0
- package/dist/types/types/handler-context.d.ts +15 -5
- package/dist/types/types/route-entry.d.ts +5 -0
- package/dist/types/urls/path-helper.d.ts +18 -0
- package/dist/types/vite/discovery/build-versions.d.ts +393 -0
- package/dist/types/vite/discovery/installed-packages.d.ts +26 -0
- package/dist/types/vite/discovery/router-versions-phase.d.ts +52 -0
- package/dist/types/vite/discovery/shell-prerender-phase.d.ts +7 -17
- package/dist/types/vite/discovery/state.d.ts +25 -0
- package/dist/types/vite/encryption-key.d.ts +18 -0
- package/dist/types/vite/inject-client-debug.d.ts +6 -25
- package/dist/types/vite/plugin-types.d.ts +27 -0
- package/dist/types/vite/plugins/{version-injector.d.ts → entry-bootstrap-injector.d.ts} +4 -6
- package/dist/types/vite/plugins/expose-ids/export-analysis.d.ts +1 -0
- package/dist/types/vite/plugins/expose-ids/router-transform.d.ts +14 -0
- package/dist/types/vite/plugins/stable-rsc-output.d.ts +51 -0
- package/dist/types/vite/plugins/version-plugin.d.ts +9 -11
- package/dist/types/vite/plugins/virtual-entries.d.ts +18 -1
- package/dist/types/vite/router-discovery.d.ts +6 -0
- package/dist/types/vite/utils/bundle-analysis.d.ts +6 -2
- package/dist/types/vite/utils/client-chunks.d.ts +2 -0
- package/dist/types/vite/utils/compare-strings.d.ts +7 -0
- package/dist/types/vite/utils/package-resolution.d.ts +8 -0
- package/dist/types/vite/utils/prerender-utils.d.ts +2 -0
- package/dist/vite/index.js +1707 -931
- package/package.json +12 -3
- package/skills/cache-guide/SKILL.md +102 -37
- package/skills/caching/SKILL.md +56 -9
- package/skills/catalog.json +1 -1
- package/skills/client-urls/SKILL.md +16 -0
- package/skills/cloudflare/SKILL.md +133 -1
- package/skills/comparison/references/framework-comparison.md +17 -13
- package/skills/deployment-caching/SKILL.md +33 -12
- package/skills/document-cache/SKILL.md +16 -14
- package/skills/hooks/SKILL.md +25 -0
- package/skills/hooks/handle-and-actions.md +5 -0
- package/skills/hooks/navigation.md +38 -0
- package/skills/hooks/state.md +213 -11
- package/skills/hooks/urls.md +40 -1
- package/skills/host-router/SKILL.md +31 -0
- package/skills/intercept/SKILL.md +6 -0
- package/skills/loader/SKILL.md +25 -5
- package/skills/middleware/SKILL.md +2 -0
- package/skills/migrate-nextjs/SKILL.md +82 -9
- package/skills/ppr/SKILL.md +150 -48
- package/skills/prerender/SKILL.md +651 -10
- package/skills/rango/SKILL.md +9 -7
- package/skills/response-routes/SKILL.md +5 -5
- package/skills/route/SKILL.md +29 -0
- package/skills/router-setup/SKILL.md +15 -1
- package/skills/testing/SKILL.md +32 -27
- package/skills/testing/cache-prerender.md +18 -17
- package/skills/testing/client-components.md +193 -8
- package/skills/testing/handles.md +2 -0
- package/skills/testing/loader.md +1 -1
- package/skills/testing/middleware.md +1 -1
- package/skills/testing/render-handler.md +18 -18
- package/skills/testing/serve-shell-request.md +167 -22
- package/skills/testing/server-actions.md +17 -17
- package/skills/testing/server-tree.md +1 -1
- package/skills/typesafety/generated-files-and-cli.md +1 -1
- package/skills/use-cache/SKILL.md +25 -10
- package/skills/vercel/SKILL.md +5 -3
- package/skills/view-transitions/SKILL.md +9 -4
- package/src/bin/rango.ts +1 -1
- package/src/browser/event-controller.ts +119 -2
- package/src/browser/history-state.ts +39 -17
- package/src/browser/link-interceptor.ts +2 -2
- package/src/browser/navigation-bridge.ts +104 -41
- package/src/browser/navigation-store.ts +26 -0
- package/src/browser/navigation-transaction.ts +24 -12
- package/src/browser/network-error-handler.ts +2 -0
- package/src/browser/partial-update.ts +58 -14
- package/src/browser/prefetch/default-strategy.ts +22 -42
- package/src/browser/rango-state.ts +3 -1
- package/src/browser/react/Link.tsx +37 -43
- package/src/browser/react/NavigationProvider.tsx +85 -1
- package/src/browser/react/context.ts +30 -0
- package/src/browser/react/location-state-shared.ts +200 -34
- package/src/browser/react/location-state.ts +28 -77
- package/src/browser/react/use-action.ts +4 -1
- package/src/browser/react/use-handle.ts +3 -0
- package/src/browser/react/use-link-status.ts +4 -3
- package/src/browser/react/use-navigation.ts +4 -1
- package/src/browser/react/use-params.ts +4 -1
- package/src/browser/react/use-pathname.ts +4 -1
- package/src/browser/react/use-search-params.ts +5 -3
- package/src/browser/react/use-segments.ts +36 -54
- package/src/browser/rsc-router.tsx +93 -31
- package/src/browser/segment-reconciler.ts +3 -3
- package/src/browser/server-action-bridge.ts +43 -7
- package/src/browser/types.ts +11 -0
- package/src/build/generate-manifest.ts +17 -0
- package/src/build/merge-full-manifests.ts +5 -0
- package/src/build/route-trie.ts +1 -0
- package/src/cache/background-task.ts +61 -0
- package/src/cache/cache-key-utils.ts +91 -12
- package/src/cache/cache-policy.ts +11 -2
- package/src/cache/cache-runtime.ts +23 -23
- package/src/cache/cache-scope.ts +48 -36
- package/src/cache/cf/cf-cache-store.ts +91 -66
- package/src/cache/cf/cf-cache-types.ts +12 -4
- package/src/cache/document-cache.ts +35 -14
- package/src/cache/handle-snapshot.ts +118 -86
- package/src/cache/index.ts +5 -0
- package/src/cache/invalidation-order.ts +53 -0
- package/src/cache/memory-segment-store.ts +52 -1
- package/src/cache/shell-snapshot.ts +52 -5
- package/src/cache/store-scope.ts +36 -0
- package/src/cache/tag-invalidation.ts +7 -5
- package/src/cache/types.ts +90 -24
- package/src/cache/vercel/vercel-cache-store.ts +68 -26
- package/src/client-urls/client-root.tsx +1 -0
- package/src/client-urls/navigation.ts +10 -0
- package/src/client-urls/optimistic-location.ts +10 -4
- package/src/client.tsx +40 -3
- package/src/errors.ts +13 -6
- package/src/handles/deferred-resolution.ts +36 -0
- package/src/internal-suspense-audit.ts +12 -0
- package/src/loader-store.ts +4 -9
- package/src/outlet-provider.tsx +25 -1
- package/src/prerender/build-shell-capture.ts +11 -9
- package/src/prerender/cloudflare.ts +130 -0
- package/src/prerender/create-prerender-trigger.ts +780 -0
- package/src/prerender/index.ts +39 -0
- package/src/prerender/memory-prerender-store.ts +115 -0
- package/src/prerender/on-demand.ts +339 -0
- package/src/prerender/producer-guard.ts +66 -0
- package/src/prerender/shell-manifest-key.ts +12 -17
- package/src/prerender/warm-request.ts +154 -0
- package/src/prerender/warm.ts +193 -0
- package/src/prerender/writable-store.ts +278 -0
- package/src/prerender.ts +29 -0
- package/src/reverse.ts +1 -1
- package/src/route-content-wrapper.tsx +107 -13
- package/src/router/error-handling.ts +12 -12
- package/src/router/find-match.ts +1 -0
- package/src/router/handler-context.ts +2 -0
- package/src/router/intercept-resolution.ts +15 -8
- package/src/router/loader-resolution.ts +2 -10
- package/src/router/manifest.ts +2 -0
- package/src/router/match-middleware/background-revalidation.ts +2 -0
- package/src/router/match-middleware/cache-lookup.ts +239 -54
- package/src/router/navigation-snapshot.ts +25 -6
- package/src/router/pattern-matching.ts +14 -0
- package/src/router/prefetch-default.ts +4 -4
- package/src/router/prerender-match.ts +460 -352
- package/src/router/request-classification.ts +15 -12
- package/src/router/route-trie-builder.ts +9 -0
- package/src/router/router-context.ts +1 -0
- package/src/router/router-interfaces.ts +35 -1
- package/src/router/router-options.ts +45 -5
- package/src/router/segment-resolution/fresh.ts +20 -19
- package/src/router/segment-resolution/loader-cache.ts +110 -77
- package/src/router/segment-resolution/revalidation.ts +34 -24
- package/src/router/segment-resolution/streamed-handler-boundary.ts +133 -0
- package/src/router/trie-matching.ts +3 -0
- package/src/router-versions.ts +60 -0
- package/src/router.ts +107 -1
- package/src/rsc/full-payload.ts +6 -2
- package/src/rsc/handler-context.ts +5 -0
- package/src/rsc/handler.ts +64 -7
- package/src/rsc/progressive-enhancement.ts +5 -1
- package/src/rsc/response-cache-serve.ts +10 -4
- package/src/rsc/rsc-rendering.ts +65 -12
- package/src/rsc/shell-build-manifest.ts +17 -12
- package/src/rsc/shell-capture-constants.ts +39 -7
- package/src/rsc/shell-capture.ts +144 -45
- package/src/rsc/shell-serve.ts +12 -6
- package/src/rsc/ssr-module-loader.ts +26 -0
- package/src/rsc/types.ts +12 -6
- package/src/segment-boundary-content.ts +25 -0
- package/src/segment-loader-promise.ts +28 -38
- package/src/segment-system.tsx +117 -57
- package/src/server/build-version-table.ts +130 -0
- package/src/server/context.ts +40 -17
- package/src/server/cookie-store.ts +5 -0
- package/src/server/handle-store.ts +357 -127
- package/src/server/request-context.ts +149 -12
- package/src/ssr/ssr-root.tsx +32 -13
- package/src/suspense-audit-react.tsx +98 -0
- package/src/suspense-audit.ts +1030 -0
- package/src/testing/action-form.ts +66 -0
- package/src/testing/build-versions.ts +64 -0
- package/src/testing/dispatch.ts +6 -0
- package/src/testing/dom.entry.ts +1 -0
- package/src/testing/flight-runtime.d.ts +27 -0
- package/src/testing/flight.entry.ts +4 -0
- package/src/testing/index.ts +22 -0
- package/src/testing/internal/context.ts +2 -1
- package/src/testing/internal/seed-vars.ts +14 -2
- package/src/testing/internal/server-modules.ts +32 -0
- package/src/testing/location-state-key.ts +18 -8
- package/src/testing/render-handler.ts +2 -1
- package/src/testing/render-route.tsx +293 -46
- package/src/testing/serve-shell-request.ts +262 -37
- package/src/testing/shell-status.ts +17 -6
- package/src/testing/vitest-stubs/plugin-rsc.ts +37 -11
- package/src/testing/vitest-stubs/version.ts +4 -1
- package/src/thenable-status.ts +10 -0
- package/src/types/handler-context.ts +15 -5
- package/src/types/route-entry.ts +6 -0
- package/src/urls/path-helper.ts +69 -9
- package/src/use-loader.tsx +57 -30
- package/src/vite/discovery/build-versions.ts +1281 -0
- package/src/vite/discovery/bundle-postprocess.ts +4 -0
- package/src/vite/discovery/discover-routers.ts +15 -0
- package/src/vite/discovery/installed-packages.ts +159 -0
- package/src/vite/discovery/prerender-collection.ts +38 -1
- package/src/vite/discovery/router-versions-phase.ts +336 -0
- package/src/vite/discovery/shell-prerender-phase.ts +33 -43
- package/src/vite/discovery/state.ts +36 -1
- package/src/vite/discovery/virtual-module-codegen.ts +7 -2
- package/src/vite/encryption-key.ts +101 -9
- package/src/vite/inject-client-debug.ts +42 -20
- package/src/vite/plugin-types.ts +28 -0
- package/src/vite/plugins/{version-injector.ts → entry-bootstrap-injector.ts} +7 -22
- package/src/vite/plugins/expose-ids/export-analysis.ts +1 -1
- package/src/vite/plugins/expose-ids/router-transform.ts +36 -15
- package/src/vite/plugins/expose-internal-ids.ts +12 -3
- package/src/vite/plugins/stable-rsc-output.ts +147 -0
- package/src/vite/plugins/version-plugin.ts +19 -24
- package/src/vite/plugins/version.d.ts +17 -2
- package/src/vite/plugins/virtual-entries.ts +39 -6
- package/src/vite/rango.ts +28 -3
- package/src/vite/router-discovery.ts +96 -13
- package/src/vite/utils/bundle-analysis.ts +39 -7
- package/src/vite/utils/client-chunks.ts +5 -1
- package/src/vite/utils/compare-strings.ts +9 -0
- package/src/vite/utils/package-resolution.ts +9 -0
- package/src/vite/utils/prerender-utils.ts +4 -1
- package/src/vite/utils/shared-utils.ts +1 -1
- package/dist/types/segment-content-promise.d.ts +0 -13
- package/src/segment-content-promise.ts +0 -67
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/// <reference path="../../vite/plugins/version.d.ts" />
|
|
2
|
-
|
|
3
1
|
// Extend CacheStorage with Cloudflare's default cache property
|
|
4
2
|
declare global {
|
|
5
3
|
interface CacheStorage {
|
|
@@ -29,6 +27,7 @@ declare global {
|
|
|
29
27
|
|
|
30
28
|
import { requestHeaders } from "../../server/request-headers.js";
|
|
31
29
|
import type {
|
|
30
|
+
CacheStoreScope,
|
|
32
31
|
SegmentCacheStore,
|
|
33
32
|
CachedEntryData,
|
|
34
33
|
CacheDefaults,
|
|
@@ -50,7 +49,10 @@ import {
|
|
|
50
49
|
type RequestContext,
|
|
51
50
|
} from "../../server/request-context.js";
|
|
52
51
|
import { INTERNAL_RANGO_DEBUG } from "../../internal-debug.js";
|
|
53
|
-
import {
|
|
52
|
+
import {
|
|
53
|
+
versionKeyPrefix,
|
|
54
|
+
type KeyVersion,
|
|
55
|
+
} from "../../server/build-version-table.js";
|
|
54
56
|
import {
|
|
55
57
|
isPerClientSignalHeader,
|
|
56
58
|
stripPerClientSignals,
|
|
@@ -117,6 +119,7 @@ import {
|
|
|
117
119
|
DEFAULT_CF_MARKER_MAX_STALE_MS,
|
|
118
120
|
} from "../isolate-tag-memo.js";
|
|
119
121
|
import { createCloudflareZonePurge } from "./cf-zone-purge.js";
|
|
122
|
+
import { responseStartedAt, taggedStamp } from "../invalidation-order.js";
|
|
120
123
|
|
|
121
124
|
// ============================================================================
|
|
122
125
|
// Constants
|
|
@@ -314,10 +317,9 @@ interface KVItemEnvelope {
|
|
|
314
317
|
|
|
315
318
|
/**
|
|
316
319
|
* Key namespace of the shell family. `shell2:` holds the prelude-first frame
|
|
317
|
-
* (cf-shell-frame.ts); `shell:` held the JSON envelope.
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
* a frame. Old keys age out by their TTL.
|
|
320
|
+
* (cf-shell-frame.ts); `shell:` held the JSON envelope. The separate
|
|
321
|
+
* namespace keeps new code from reporting an old body as corrupt and a
|
|
322
|
+
* rollback from parsing a frame. Old keys age out by their TTL.
|
|
321
323
|
*/
|
|
322
324
|
const SHELL_KEY_PREFIX = "shell2:";
|
|
323
325
|
|
|
@@ -494,7 +496,8 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
494
496
|
private readonly namespace?: string;
|
|
495
497
|
private readonly explicitBaseUrl?: string;
|
|
496
498
|
private readonly waitUntil?: (fn: () => Promise<void>) => void;
|
|
497
|
-
|
|
499
|
+
/** The `version` option: used for every versioned family when set. */
|
|
500
|
+
private readonly explicitVersion?: string;
|
|
498
501
|
private readonly edgeLookupTimeoutMs: number;
|
|
499
502
|
private readonly edgeReadTimeoutMs: number;
|
|
500
503
|
private readonly kvReadTimeoutMs: number;
|
|
@@ -518,6 +521,12 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
518
521
|
private readonly kv?: KVNamespace;
|
|
519
522
|
/** True when constructed without KV: no durable tag history (see ctor). */
|
|
520
523
|
readonly tagHistoryInert?: boolean;
|
|
524
|
+
/**
|
|
525
|
+
* SegmentCacheStore.scope: with KV every colo's L1 miss reads the same L2
|
|
526
|
+
* entry ("global"); without it entries live in one colo's Cache API
|
|
527
|
+
* ("local"), purge mode included (a purge evicts, it stores nothing).
|
|
528
|
+
*/
|
|
529
|
+
readonly scope: CacheStoreScope;
|
|
521
530
|
private readonly onRevalidateTag?: (tags: string[]) => Promise<void>;
|
|
522
531
|
private readonly tagPurge?: (cacheTags: string[]) => Promise<void>;
|
|
523
532
|
private readonly tagInvalidationTtl?: number;
|
|
@@ -540,7 +549,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
540
549
|
// the internal host. Only the explicit override can be captured eagerly.
|
|
541
550
|
this.explicitBaseUrl = options.baseUrl;
|
|
542
551
|
this.defaults = options.defaults;
|
|
543
|
-
this.
|
|
552
|
+
this.explicitVersion = options.version;
|
|
544
553
|
// Coalesce only finite numbers to the override; a non-finite value (NaN from
|
|
545
554
|
// `Number(env.UNSET)`, or Infinity) would otherwise sail past `?? DEFAULT`
|
|
546
555
|
// (which only replaces null/undefined) into setTimeout, where NaN/Infinity
|
|
@@ -603,6 +612,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
603
612
|
// evict it (purge cannot delete a build asset), so the manifest gate
|
|
604
613
|
// declines on this flag (shell-build-manifest.ts).
|
|
605
614
|
this.tagHistoryInert = options.kv ? undefined : true;
|
|
615
|
+
this.scope = options.kv ? "global" : "local";
|
|
606
616
|
this.onRevalidateTag = options.onRevalidateTag;
|
|
607
617
|
// tagPurge accepts a ready purge function or a credentials object; the
|
|
608
618
|
// object form is normalized through the built-in zone purge client, which
|
|
@@ -1134,7 +1144,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
1134
1144
|
if (this.isReservedSegmentKey(key, "cache-read")) return null;
|
|
1135
1145
|
try {
|
|
1136
1146
|
const cache = await this.getCache();
|
|
1137
|
-
const request = this.keyToRequest(key);
|
|
1147
|
+
const request = this.keyToRequest(key, "data");
|
|
1138
1148
|
const matchStart = Date.now();
|
|
1139
1149
|
const {
|
|
1140
1150
|
response,
|
|
@@ -1381,20 +1391,18 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
1381
1391
|
if (this.skipUncacheableTagSet(data.tags)) return;
|
|
1382
1392
|
try {
|
|
1383
1393
|
const cache = await this.getCache();
|
|
1384
|
-
const request = this.keyToRequest(key);
|
|
1394
|
+
const request = this.keyToRequest(key, "data");
|
|
1385
1395
|
|
|
1386
1396
|
// Extended TTL covers SWR window
|
|
1387
1397
|
const swrWindow = resolveSwrWindow(swr, this.defaults);
|
|
1388
1398
|
const totalTtl = ttl + swrWindow;
|
|
1389
1399
|
const staleAt = Date.now() + ttl * 1000;
|
|
1390
1400
|
|
|
1391
|
-
// Stamp the tag timestamp
|
|
1392
|
-
//
|
|
1393
|
-
//
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
? Date.now()
|
|
1397
|
-
: undefined;
|
|
1401
|
+
// Stamp the tag timestamp and carry it (with the tags) into both the
|
|
1402
|
+
// L1 body and the KV envelope so reads can run the invalidation check.
|
|
1403
|
+
// It is the render's start when the writer passed it (#1068), else the
|
|
1404
|
+
// write time: a marker written while the render ran then rejects it.
|
|
1405
|
+
const taggedAt = taggedStamp(data.tags, data.taggedAt);
|
|
1398
1406
|
const dataToStore: CachedEntryData = taggedAt
|
|
1399
1407
|
? { ...data, taggedAt }
|
|
1400
1408
|
: data;
|
|
@@ -1457,11 +1465,11 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
1457
1465
|
if (this.isReservedSegmentKey(key, "cache-delete")) return false;
|
|
1458
1466
|
try {
|
|
1459
1467
|
const cache = await this.getCache();
|
|
1460
|
-
const result = await cache.delete(this.keyToRequest(key));
|
|
1468
|
+
const result = await cache.delete(this.keyToRequest(key, "data"));
|
|
1461
1469
|
|
|
1462
1470
|
// L2: delete from KV
|
|
1463
1471
|
if (this.kv && this.waitUntil) {
|
|
1464
|
-
const kvKey = await this.toKVKey(key);
|
|
1472
|
+
const kvKey = await this.toKVKey(key, "data");
|
|
1465
1473
|
this.waitUntil(() =>
|
|
1466
1474
|
reportingAsync(
|
|
1467
1475
|
() => this.kv!.delete(kvKey),
|
|
@@ -1492,7 +1500,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
1492
1500
|
): Promise<{ response: Response; shouldRevalidate: boolean } | null> {
|
|
1493
1501
|
try {
|
|
1494
1502
|
const cache = await this.getCache();
|
|
1495
|
-
const request = this.keyToRequest(`doc:${key}
|
|
1503
|
+
const request = this.keyToRequest(`doc:${key}`, "document");
|
|
1496
1504
|
// The document path is outside the debug surface (op is only get/getItem),
|
|
1497
1505
|
// so the match-timeout flag is not surfaced as an event here -- though
|
|
1498
1506
|
// matchWithTimeout still warns on a slow match. A miss or timeout falls
|
|
@@ -1633,14 +1641,13 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
1633
1641
|
if (this.skipUncacheableTagSet(tags)) return;
|
|
1634
1642
|
try {
|
|
1635
1643
|
const cache = await this.getCache();
|
|
1636
|
-
const request = this.keyToRequest(`doc:${key}
|
|
1644
|
+
const request = this.keyToRequest(`doc:${key}`, "document");
|
|
1637
1645
|
|
|
1638
1646
|
// Extended TTL covers SWR window
|
|
1639
1647
|
const swrWindow = resolveSwrWindow(swr, this.defaults);
|
|
1640
1648
|
const totalTtl = ttl + swrWindow;
|
|
1641
1649
|
const staleAt = Date.now() + ttl * 1000;
|
|
1642
|
-
const taggedAt =
|
|
1643
|
-
Array.isArray(tags) && tags.length > 0 ? Date.now() : undefined;
|
|
1650
|
+
const taggedAt = taggedStamp(tags, responseStartedAt(response));
|
|
1644
1651
|
|
|
1645
1652
|
// Clone body for potential KV write before consuming it for L1
|
|
1646
1653
|
const [l1Body, kvBody] = this.kv
|
|
@@ -1747,7 +1754,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
1747
1754
|
async getItem(key: string): Promise<CacheItemResult | null> {
|
|
1748
1755
|
try {
|
|
1749
1756
|
const cache = await this.getCache();
|
|
1750
|
-
const request = this.keyToRequest(`fn:${key}
|
|
1757
|
+
const request = this.keyToRequest(`fn:${key}`, "data");
|
|
1751
1758
|
const matchStart = Date.now();
|
|
1752
1759
|
const {
|
|
1753
1760
|
response,
|
|
@@ -1932,7 +1939,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
1932
1939
|
if (this.skipUncacheableTagSet(options?.tags)) return;
|
|
1933
1940
|
try {
|
|
1934
1941
|
const cache = await this.getCache();
|
|
1935
|
-
const request = this.keyToRequest(`fn:${key}
|
|
1942
|
+
const request = this.keyToRequest(`fn:${key}`, "data");
|
|
1936
1943
|
|
|
1937
1944
|
const ttl = resolveTtl(options?.ttl, this.defaults, DEFAULT_FUNCTION_TTL);
|
|
1938
1945
|
const swrWindow = resolveSwrWindow(options?.swr, this.defaults);
|
|
@@ -1940,8 +1947,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
1940
1947
|
const staleAt = Date.now() + ttl * 1000;
|
|
1941
1948
|
|
|
1942
1949
|
const tags = options?.tags;
|
|
1943
|
-
const taggedAt =
|
|
1944
|
-
Array.isArray(tags) && tags.length > 0 ? Date.now() : undefined;
|
|
1950
|
+
const taggedAt = taggedStamp(tags, options?.startedAt);
|
|
1945
1951
|
|
|
1946
1952
|
// Serialize value/handles once; L1 body and KV envelope.v/h share the
|
|
1947
1953
|
// escaped strings so a large RSC payload is not re-escaped for L2.
|
|
@@ -1984,7 +1990,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
1984
1990
|
// JSON.stringify(KVItemEnvelope); field names differ from L1 so we assemble
|
|
1985
1991
|
// from the pre-escaped value/handles pieces rather than re-stringifying.
|
|
1986
1992
|
if (this.kv && this.waitUntil && totalTtl >= 60) {
|
|
1987
|
-
const kvKey = await this.toKVKey(`fn:${key}
|
|
1993
|
+
const kvKey = await this.toKVKey(`fn:${key}`, "data");
|
|
1988
1994
|
const expiresAt = staleAt + swrWindow * 1000;
|
|
1989
1995
|
let envelopeJson = `{"v":${valueJson}`;
|
|
1990
1996
|
if (handlesJson !== undefined) envelopeJson += `,"h":${handlesJson}`;
|
|
@@ -2154,7 +2160,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
2154
2160
|
}
|
|
2155
2161
|
try {
|
|
2156
2162
|
const cache = await this.getCache();
|
|
2157
|
-
const request = this.
|
|
2163
|
+
const request = this.shellRequest(key);
|
|
2158
2164
|
const matchStartedAt = stats ? performance.now() : 0;
|
|
2159
2165
|
const {
|
|
2160
2166
|
response,
|
|
@@ -2297,14 +2303,17 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
2297
2303
|
}
|
|
2298
2304
|
}
|
|
2299
2305
|
|
|
2300
|
-
/**
|
|
2306
|
+
/**
|
|
2307
|
+
* The isolate marker memo's key for a tag: markers are per namespace, and
|
|
2308
|
+
* shared by every version (tagMarkerKey).
|
|
2309
|
+
*/
|
|
2301
2310
|
private markerMemoKey(tag: string): string {
|
|
2302
|
-
return `${this.namespace ?? ""}\u0000${
|
|
2311
|
+
return `${this.namespace ?? ""}\u0000${tag}`;
|
|
2303
2312
|
}
|
|
2304
2313
|
|
|
2305
|
-
/** The memo key: one per namespace,
|
|
2314
|
+
/** The memo key: one per namespace, document version, base URL, and shell key. */
|
|
2306
2315
|
private shellMemoKey(key: string): string {
|
|
2307
|
-
return `${this.namespace ?? ""}\u0000${this.
|
|
2316
|
+
return `${this.namespace ?? ""}\u0000${this.shellRequest(key).url}`;
|
|
2308
2317
|
}
|
|
2309
2318
|
|
|
2310
2319
|
/**
|
|
@@ -2571,15 +2580,12 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
2571
2580
|
const evict = async (): Promise<void> => {
|
|
2572
2581
|
const cache = await this.getCache();
|
|
2573
2582
|
await reportingAsync(
|
|
2574
|
-
() => cache.delete(this.
|
|
2583
|
+
() => cache.delete(this.shellRequest(key)),
|
|
2575
2584
|
"cache-delete",
|
|
2576
2585
|
"[CFCacheStore] getShell: evict corrupt L1",
|
|
2577
2586
|
);
|
|
2578
2587
|
if (this.kv) {
|
|
2579
|
-
await this.evictKvKey(
|
|
2580
|
-
await this.toKVKey(`${SHELL_KEY_PREFIX}${key}`),
|
|
2581
|
-
"getShell",
|
|
2582
|
-
);
|
|
2588
|
+
await this.evictKvKey(await this.shellKVKey(key), "getShell");
|
|
2583
2589
|
}
|
|
2584
2590
|
};
|
|
2585
2591
|
if (this.waitUntil) this.waitUntil(evict);
|
|
@@ -2641,9 +2647,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
2641
2647
|
Array.isArray(tags) && tags.length > 0 ? entry.createdAt : undefined;
|
|
2642
2648
|
|
|
2643
2649
|
const writeKv = !!this.kv && retentionTtl > 0;
|
|
2644
|
-
const kvKey = writeKv
|
|
2645
|
-
? await this.toKVKey(`${SHELL_KEY_PREFIX}${key}`)
|
|
2646
|
-
: null;
|
|
2650
|
+
const kvKey = writeKv ? await this.shellKVKey(key) : null;
|
|
2647
2651
|
|
|
2648
2652
|
const write = (async (): Promise<"stored" | "invalidated" | void> => {
|
|
2649
2653
|
if (
|
|
@@ -2684,7 +2688,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
2684
2688
|
try {
|
|
2685
2689
|
const cache = await this.getCache();
|
|
2686
2690
|
await cache.put(
|
|
2687
|
-
this.
|
|
2691
|
+
this.shellRequest(key),
|
|
2688
2692
|
this.shellFrameResponse(frame, head),
|
|
2689
2693
|
);
|
|
2690
2694
|
this.debugShell(key, "l1-stored", { expiresAt: head.e });
|
|
@@ -2781,7 +2785,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
2781
2785
|
// One kvReadTimeoutMs budget covers opening the value and reading its
|
|
2782
2786
|
// head and prelude; the remainder after the open bounds the frame read.
|
|
2783
2787
|
const deadline = Date.now() + this.kvReadTimeoutMs;
|
|
2784
|
-
const kvKey = await this.
|
|
2788
|
+
const kvKey = await this.shellKVKey(key);
|
|
2785
2789
|
// A transient read REJECTION propagates to the catch below (reported
|
|
2786
2790
|
// cache-read, the entry left intact); only a malformed frame on a body
|
|
2787
2791
|
// that WAS read is corruption that evicts.
|
|
@@ -2884,7 +2888,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
2884
2888
|
if (Date.now() > head.e) return;
|
|
2885
2889
|
const cache = await this.getCache();
|
|
2886
2890
|
await cache.put(
|
|
2887
|
-
this.
|
|
2891
|
+
this.shellRequest(key),
|
|
2888
2892
|
this.shellFrameResponse(
|
|
2889
2893
|
encodeShellFrame(head, prelude, snapshotBytes),
|
|
2890
2894
|
head,
|
|
@@ -2905,18 +2909,30 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
2905
2909
|
// Key Helpers
|
|
2906
2910
|
// ============================================================================
|
|
2907
2911
|
|
|
2912
|
+
private shellRequest(key: string): Request {
|
|
2913
|
+
return this.keyToRequest(`${SHELL_KEY_PREFIX}${key}`, "document");
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2916
|
+
private shellKVKey(key: string): Promise<string> {
|
|
2917
|
+
return this.toKVKey(`${SHELL_KEY_PREFIX}${key}`, "document");
|
|
2918
|
+
}
|
|
2919
|
+
|
|
2908
2920
|
/**
|
|
2909
2921
|
* Convert string key to Request object for CF Cache API.
|
|
2910
|
-
*
|
|
2922
|
+
*
|
|
2923
|
+
* `family` picks the version in the URL path, so a version change misses:
|
|
2924
|
+
* `"data"` for segment entries and `fn:` items (cached RSC data, valid while
|
|
2925
|
+
* the router's server code is unchanged), `"document"` for `doc:` responses
|
|
2926
|
+
* and `shell2:` shells (their bytes carry asset URLs and the handler
|
|
2927
|
+
* version), `null` for tag markers, which apply to every version.
|
|
2911
2928
|
* @internal
|
|
2912
2929
|
*/
|
|
2913
|
-
private keyToRequest(key: string): Request {
|
|
2930
|
+
private keyToRequest(key: string, family: KeyVersion): Request {
|
|
2914
2931
|
const encodedKey = encodeURIComponent(key);
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
});
|
|
2932
|
+
return new Request(
|
|
2933
|
+
`${this.resolveBaseUrl()}${versionKeyPrefix(this.explicitVersion, family)}${encodedKey}`,
|
|
2934
|
+
{ method: "GET" },
|
|
2935
|
+
);
|
|
2920
2936
|
}
|
|
2921
2937
|
|
|
2922
2938
|
/**
|
|
@@ -2937,9 +2953,8 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
2937
2953
|
* and delete paths agree on the stored key.
|
|
2938
2954
|
* @internal
|
|
2939
2955
|
*/
|
|
2940
|
-
private async toKVKey(key: string): Promise<string> {
|
|
2941
|
-
const
|
|
2942
|
-
const composed = `${versionPath}${key}`;
|
|
2956
|
+
private async toKVKey(key: string, family: KeyVersion): Promise<string> {
|
|
2957
|
+
const composed = `${versionKeyPrefix(this.explicitVersion, family)}${key}`;
|
|
2943
2958
|
if (kvKeyByteLength(composed) <= KV_MAX_KEY_BYTES) return composed;
|
|
2944
2959
|
const prefix = truncateToBytes(composed, KV_KEY_PRESERVED_PREFIX_BYTES);
|
|
2945
2960
|
return `${prefix}~${await kvKeyDigest(composed)}`;
|
|
@@ -2973,7 +2988,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
2973
2988
|
* @internal
|
|
2974
2989
|
*/
|
|
2975
2990
|
private toDocKVKey(key: string): Promise<string> {
|
|
2976
|
-
return this.toKVKey(`h/${this.docKVHost()}/doc:${key}
|
|
2991
|
+
return this.toKVKey(`h/${this.docKVHost()}/doc:${key}`, "document");
|
|
2977
2992
|
}
|
|
2978
2993
|
|
|
2979
2994
|
/**
|
|
@@ -3087,9 +3102,15 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
3087
3102
|
// Tag Invalidation (single-store: markers live in this.kv)
|
|
3088
3103
|
// ============================================================================
|
|
3089
3104
|
|
|
3090
|
-
/**
|
|
3105
|
+
/**
|
|
3106
|
+
* KV key for a tag's invalidation marker. Unversioned: a content-hash
|
|
3107
|
+
* version can come back (deploy A, deploy B, roll back to A), and a marker
|
|
3108
|
+
* written while B was live has to be visible to A's entries. The staleness
|
|
3109
|
+
* check compares the marker time with the entry's taggedAt and nothing else
|
|
3110
|
+
* (isGloballyInvalidated), so one marker per tag serves every version.
|
|
3111
|
+
*/
|
|
3091
3112
|
private tagMarkerKey(tag: string): Promise<string> {
|
|
3092
|
-
return this.toKVKey(`${TAG_MARKER_PREFIX}${tag}
|
|
3113
|
+
return this.toKVKey(`${TAG_MARKER_PREFIX}${tag}`, null);
|
|
3093
3114
|
}
|
|
3094
3115
|
|
|
3095
3116
|
/**
|
|
@@ -3320,9 +3341,13 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
3320
3341
|
return false;
|
|
3321
3342
|
}
|
|
3322
3343
|
|
|
3323
|
-
/**
|
|
3344
|
+
/**
|
|
3345
|
+
* Synthetic Cache API request for a tag's L1-cached invalidation marker.
|
|
3346
|
+
* Unversioned for the same reason as tagMarkerKey; the cache namespace
|
|
3347
|
+
* (getCache) still scopes it.
|
|
3348
|
+
*/
|
|
3324
3349
|
private tagMarkerRequest(tag: string): Request {
|
|
3325
|
-
return this.keyToRequest(`${TAG_MARKER_CACHE_PREFIX}${tag}
|
|
3350
|
+
return this.keyToRequest(`${TAG_MARKER_CACHE_PREFIX}${tag}`, null);
|
|
3326
3351
|
}
|
|
3327
3352
|
|
|
3328
3353
|
/**
|
|
@@ -3974,7 +3999,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
3974
3999
|
if (!this.kv) return null;
|
|
3975
4000
|
|
|
3976
4001
|
try {
|
|
3977
|
-
const kvKey = await this.toKVKey(key);
|
|
4002
|
+
const kvKey = await this.toKVKey(key, "data");
|
|
3978
4003
|
const { value: envelope, timedOut } =
|
|
3979
4004
|
await this.kvGetOrEvict<KVSegmentEnvelope>(
|
|
3980
4005
|
kvKey,
|
|
@@ -4076,7 +4101,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
4076
4101
|
this.waitUntil(() =>
|
|
4077
4102
|
reportingAsync(
|
|
4078
4103
|
async () =>
|
|
4079
|
-
this.kv!.put(await this.toKVKey(key), envelopeJson, {
|
|
4104
|
+
this.kv!.put(await this.toKVKey(key, "data"), envelopeJson, {
|
|
4080
4105
|
expirationTtl: totalTtl,
|
|
4081
4106
|
}),
|
|
4082
4107
|
"cache-write",
|
|
@@ -4101,7 +4126,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
4101
4126
|
Math.floor((envelope.e - now) / 1000),
|
|
4102
4127
|
);
|
|
4103
4128
|
const cache = await this.getCache();
|
|
4104
|
-
const request = this.keyToRequest(key);
|
|
4129
|
+
const request = this.keyToRequest(key, "data");
|
|
4105
4130
|
|
|
4106
4131
|
const response = new Response(JSON.stringify(envelope.d), {
|
|
4107
4132
|
headers: {
|
|
@@ -4137,7 +4162,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
4137
4162
|
if (!this.kv) return null;
|
|
4138
4163
|
|
|
4139
4164
|
try {
|
|
4140
|
-
const kvKey = await this.toKVKey(`fn:${key}
|
|
4165
|
+
const kvKey = await this.toKVKey(`fn:${key}`, "data");
|
|
4141
4166
|
const { value: envelope, timedOut } =
|
|
4142
4167
|
await this.kvGetOrEvict<KVItemEnvelope>(
|
|
4143
4168
|
kvKey,
|
|
@@ -4221,7 +4246,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
4221
4246
|
Math.floor((envelope.e - now) / 1000),
|
|
4222
4247
|
);
|
|
4223
4248
|
const cache = await this.getCache();
|
|
4224
|
-
const request = this.keyToRequest(`fn:${key}
|
|
4249
|
+
const request = this.keyToRequest(`fn:${key}`, "data");
|
|
4225
4250
|
|
|
4226
4251
|
const body = JSON.stringify({
|
|
4227
4252
|
value: envelope.v,
|
|
@@ -4355,7 +4380,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
4355
4380
|
Math.floor((envelope.e - now) / 1000),
|
|
4356
4381
|
);
|
|
4357
4382
|
const cache = await this.getCache();
|
|
4358
|
-
const request = this.keyToRequest(`doc:${key}
|
|
4383
|
+
const request = this.keyToRequest(`doc:${key}`, "document");
|
|
4359
4384
|
|
|
4360
4385
|
const headers = new Headers(envelope.hd);
|
|
4361
4386
|
const originalCacheControl = headers.get("Cache-Control");
|
|
@@ -320,11 +320,19 @@ export interface CFCacheStoreOptions<TEnv = unknown> {
|
|
|
320
320
|
tagCacheTtl?: number;
|
|
321
321
|
|
|
322
322
|
/**
|
|
323
|
-
* Cache version string override. When this
|
|
324
|
-
*
|
|
323
|
+
* Cache version string override. When set, this exact value prefixes every
|
|
324
|
+
* versioned key the store writes (`v/{version}/...`), for cached data and
|
|
325
|
+
* stored HTML alike; changing it makes every earlier entry unreachable.
|
|
325
326
|
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
327
|
+
* Leave it unset for the default: the store keys with the versions of the
|
|
328
|
+
* router serving the request. Segment entries and `"use cache"` items use
|
|
329
|
+
* the router's data version; document responses and PPR shells use its
|
|
330
|
+
* document version (see `createRouter({ version })`). Those are computed
|
|
331
|
+
* from the router's built code, so a deploy that does not change the router
|
|
332
|
+
* keeps its entries.
|
|
333
|
+
*
|
|
334
|
+
* Tag invalidation markers are never versioned: an invalidation applies to
|
|
335
|
+
* the entries of every version, including one that comes back in a rollback.
|
|
328
336
|
*/
|
|
329
337
|
version?: string;
|
|
330
338
|
|
|
@@ -22,10 +22,19 @@ import {
|
|
|
22
22
|
type RequestContext,
|
|
23
23
|
} from "../server/request-context.js";
|
|
24
24
|
import { mayNeedSSR } from "../rsc/ssr-setup.js";
|
|
25
|
-
import {
|
|
25
|
+
import { requestKeyBase } from "./cache-key-utils.js";
|
|
26
26
|
import { runBackground } from "./background-task.js";
|
|
27
|
+
import {
|
|
28
|
+
isWarmReplace,
|
|
29
|
+
noteWarmDocument,
|
|
30
|
+
noteWarmWrite,
|
|
31
|
+
} from "../prerender/warm-request.js";
|
|
27
32
|
import { reportCacheError } from "./cache-error.js";
|
|
28
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
executionStart,
|
|
35
|
+
markResponseStart,
|
|
36
|
+
predatesInvalidation,
|
|
37
|
+
} from "./tag-invalidation.js";
|
|
29
38
|
import { observePhase, PHASES } from "../router/instrument.js";
|
|
30
39
|
import {
|
|
31
40
|
SEGMENT_FRAGMENT_CAPABILITY_HEADER,
|
|
@@ -239,11 +248,14 @@ export interface DocumentCacheOptions<TEnv = any> {
|
|
|
239
248
|
/**
|
|
240
249
|
* Custom cache key generator.
|
|
241
250
|
*
|
|
242
|
-
* Replaces the default `host + pathname + search` key entirely.
|
|
243
|
-
* multi-domain deployment served by one function you MUST include
|
|
244
|
-
* (or an equivalent tenant discriminator) yourself — the default
|
|
245
|
-
* host-namespaced, but a custom generator's output is used verbatim,
|
|
246
|
-
* omitting host bleeds one hostname's cached response to another.
|
|
251
|
+
* Replaces the default `router id + host + pathname + search` key entirely.
|
|
252
|
+
* On a multi-domain deployment served by one function you MUST include
|
|
253
|
+
* `url.host` (or an equivalent tenant discriminator) yourself — the default
|
|
254
|
+
* key is host-namespaced, but a custom generator's output is used verbatim,
|
|
255
|
+
* so omitting host bleeds one hostname's cached response to another. The
|
|
256
|
+
* same goes for the router: two routers that share a cache store and can
|
|
257
|
+
* serve one host and path (a `hostOverride` cookie) need a generator that
|
|
258
|
+
* tells them apart.
|
|
247
259
|
*/
|
|
248
260
|
keyGenerator?: (url: URL) => string;
|
|
249
261
|
|
|
@@ -347,7 +359,8 @@ export function createDocumentCacheMiddleware<TEnv = any>(
|
|
|
347
359
|
return next();
|
|
348
360
|
}
|
|
349
361
|
|
|
350
|
-
// The write gate's start (#977)
|
|
362
|
+
// The write gate's start (#977) and the entry's stamp (#1068): the
|
|
363
|
+
// request's, not this middleware's, also for a stale refresh. A
|
|
351
364
|
// middleware ahead of this one can read tagged data the document bakes,
|
|
352
365
|
// and a stale refresh re-runs the handler over what they set.
|
|
353
366
|
const start = requestCtx._requestStart ?? executionStart();
|
|
@@ -392,7 +405,8 @@ export function createDocumentCacheMiddleware<TEnv = any>(
|
|
|
392
405
|
// existing keys and double any host they already include).
|
|
393
406
|
const cacheKey = keyGenerator
|
|
394
407
|
? keyGenerator(url) + segmentHash + fragmentSuffix + typeSuffix
|
|
395
|
-
:
|
|
408
|
+
: requestKeyBase(
|
|
409
|
+
requestCtx,
|
|
396
410
|
url.host,
|
|
397
411
|
url.pathname,
|
|
398
412
|
url.searchParams,
|
|
@@ -406,9 +420,11 @@ export function createDocumentCacheMiddleware<TEnv = any>(
|
|
|
406
420
|
// Recovery must reach CacheScope's server decoder so it can evict the bad
|
|
407
421
|
// segment. Treat it as a miss, then let the ordinary write path replace
|
|
408
422
|
// the corrupt fragment-capable response with the valid fallback bytes.
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
423
|
+
// A router.prerender() warm is a miss too: it renders and replaces.
|
|
424
|
+
const cached =
|
|
425
|
+
isFragmentRecovery || isWarmReplace(requestCtx)
|
|
426
|
+
? null
|
|
427
|
+
: await store.getResponse(cacheKey);
|
|
412
428
|
// Every path past the lookup either returns a fresh HIT, which renders
|
|
413
429
|
// nothing, or renders a response this cache may store.
|
|
414
430
|
requestCtx._documentCacheRender = true;
|
|
@@ -462,11 +478,13 @@ export function createDocumentCacheMiddleware<TEnv = any>(
|
|
|
462
478
|
if (await predatesInvalidation(store, tags, start)) return;
|
|
463
479
|
await store.putResponse!(
|
|
464
480
|
cacheKey,
|
|
465
|
-
new Response(body, fresh),
|
|
481
|
+
markResponseStart(new Response(body, fresh), start),
|
|
466
482
|
directives.sMaxAge!,
|
|
467
483
|
directives.staleWhileRevalidate,
|
|
468
484
|
tags,
|
|
469
485
|
);
|
|
486
|
+
noteWarmWrite(requestCtx, "response");
|
|
487
|
+
noteWarmDocument(requestCtx, "stored");
|
|
470
488
|
log(
|
|
471
489
|
`[DocumentCache] REVALIDATED ${typeLabel}: ${url.pathname}`,
|
|
472
490
|
);
|
|
@@ -531,11 +549,13 @@ export function createDocumentCacheMiddleware<TEnv = any>(
|
|
|
531
549
|
if (await predatesInvalidation(store, tags, start)) return;
|
|
532
550
|
await store.putResponse!(
|
|
533
551
|
cacheKey,
|
|
534
|
-
new Response(body, originalResponse),
|
|
552
|
+
markResponseStart(new Response(body, originalResponse), start),
|
|
535
553
|
directives.sMaxAge!,
|
|
536
554
|
directives.staleWhileRevalidate,
|
|
537
555
|
tags,
|
|
538
556
|
);
|
|
557
|
+
noteWarmWrite(requestCtx, "response");
|
|
558
|
+
noteWarmDocument(requestCtx, "stored");
|
|
539
559
|
} catch (error) {
|
|
540
560
|
// Detached waitUntil task — pass the captured requestCtx so onError
|
|
541
561
|
// fires even though the ALS context is gone (see the revalidation
|
|
@@ -556,6 +576,7 @@ export function createDocumentCacheMiddleware<TEnv = any>(
|
|
|
556
576
|
}
|
|
557
577
|
|
|
558
578
|
// No cache headers - pass through
|
|
579
|
+
noteWarmDocument(requestCtx, "not-cacheable");
|
|
559
580
|
return originalResponse;
|
|
560
581
|
} catch (error) {
|
|
561
582
|
reportCacheError(error, "cache-read", "[DocumentCache] middleware");
|