@rangojs/router 0.5.1 → 0.6.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 +5 -1
- package/dist/bin/rango.js +343 -125
- package/dist/types/browser/event-controller.d.ts +6 -0
- package/dist/types/browser/react/use-router.d.ts +10 -3
- package/dist/types/browser/react/use-search-params.d.ts +57 -10
- package/dist/types/browser/types.d.ts +22 -0
- package/dist/types/build/merge-full-manifests.d.ts +3 -0
- package/dist/types/build/route-trie.d.ts +4 -73
- package/dist/types/build/route-types/per-module-writer.d.ts +6 -4
- package/dist/types/build/route-types/router-processing.d.ts +2 -3
- package/dist/types/cache/cache-exec-scope.d.ts +31 -0
- package/dist/types/cache/cf/cf-cache-constants.d.ts +8 -1
- package/dist/types/cache/cf/cf-cache-store.d.ts +15 -1
- package/dist/types/cache/cf/cf-cache-types.d.ts +1 -1
- package/dist/types/cache/cf/cf-kv-utils.d.ts +21 -0
- package/dist/types/cache/taint.d.ts +12 -6
- package/dist/types/client-urls/client-root.d.ts +38 -0
- package/dist/types/client-urls/client-urls.d.ts +5 -0
- package/dist/types/client-urls/navigation.d.ts +38 -0
- package/dist/types/client-urls/revalidation-protocol.d.ts +25 -0
- package/dist/types/client-urls/server-projection.d.ts +62 -0
- package/dist/types/client-urls/types.d.ts +144 -0
- package/dist/types/client.d.ts +12 -4
- package/dist/types/client.rsc.d.ts +4 -1
- package/dist/types/decode-loader-results.d.ts +37 -0
- package/dist/types/errors.d.ts +1 -0
- package/dist/types/handles/is-thenable.d.ts +2 -4
- package/dist/types/index.d.ts +1 -1
- package/dist/types/loader-redirect.d.ts +27 -0
- package/dist/types/outlet-context.d.ts +12 -0
- package/dist/types/outlet-provider.d.ts +3 -1
- package/dist/types/redirect-origin.d.ts +4 -0
- package/dist/types/route-content-wrapper.d.ts +42 -1
- package/dist/types/route-definition/helpers-types.d.ts +13 -2
- package/dist/types/router/error-handling.d.ts +35 -1
- package/dist/types/router/intercept-resolution.d.ts +12 -0
- package/dist/types/router/loader-resolution.d.ts +24 -2
- package/dist/types/router/revalidation.d.ts +7 -0
- package/dist/{types.backup/build/route-trie.d.ts → types/router/route-trie-builder.d.ts} +4 -16
- package/dist/types/router/router-interfaces.d.ts +20 -0
- package/dist/types/router/segment-resolution/helpers.d.ts +1 -1
- package/dist/types/router/trie-matching.d.ts +1 -1
- package/dist/types/rsc/helpers.d.ts +3 -0
- package/dist/types/rsc/manifest-init.d.ts +5 -5
- package/dist/types/rsc/render-pipeline.d.ts +9 -0
- package/dist/types/rsc/routine-plan.d.ts +124 -0
- package/dist/types/rsc/shell-capture-constants.d.ts +17 -0
- package/dist/types/rsc/shell-capture.d.ts +9 -0
- package/dist/types/rsc/shell-serve.d.ts +11 -0
- package/dist/types/rsc/types.d.ts +30 -0
- package/dist/types/segment-system.d.ts +2 -0
- package/dist/types/server/context.d.ts +10 -0
- package/dist/types/server/handle-store.d.ts +34 -3
- package/dist/types/server/request-context.d.ts +14 -1
- package/dist/types/server.d.ts +1 -0
- package/dist/types/ssr/index.d.ts +22 -0
- package/dist/types/ssr/ssr-root.d.ts +10 -0
- package/dist/types/testing/dom.entry.d.ts +1 -1
- package/dist/types/testing/render-route.d.ts +16 -6
- package/dist/types/testing/run-loader.d.ts +9 -0
- package/dist/types/types/boundaries.d.ts +22 -0
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/loader-types.d.ts +57 -5
- package/dist/types/types/segments.d.ts +7 -0
- package/dist/types/urls/path-helper-types.d.ts +10 -4
- package/dist/types/vite/discovery/client-urls-projection.d.ts +53 -0
- package/dist/types/vite/discovery/discover-routers.d.ts +1 -1
- package/dist/types/vite/discovery/state.d.ts +8 -1
- package/dist/types/vite/encryption-key.d.ts +2 -0
- package/dist/types/vite/plugins/expose-internal-ids.d.ts +10 -0
- package/dist/types/vite/plugins/server-ref-hashing.d.ts +24 -0
- package/dist/types/vite/plugins/server-reference-pattern.d.ts +1 -0
- package/dist/types/vite/utils/shared-utils.d.ts +12 -0
- package/dist/vite/index.js +5101 -2056
- package/package.json +3 -3
- package/skills/breadcrumbs/SKILL.md +39 -9
- package/skills/caching/SKILL.md +1 -1
- package/skills/catalog.json +7 -1
- package/skills/client-urls/SKILL.md +338 -0
- package/skills/comparison/references/framework-comparison.md +23 -9
- package/skills/hooks/SKILL.md +2 -2
- package/skills/hooks/data.md +11 -2
- package/skills/hooks/handle-and-actions.md +7 -0
- package/skills/hooks/outlets.md +26 -5
- package/skills/hooks/urls.md +40 -3
- package/skills/loader/SKILL.md +132 -20
- package/skills/migrate-nextjs/SKILL.md +70 -10
- package/skills/migrate-react-router/SKILL.md +49 -13
- package/skills/migrate-react-router/component-migration.md +18 -13
- package/skills/migrate-react-router/data-and-actions.md +14 -3
- package/skills/migrate-react-router/route-mapping.md +15 -2
- package/skills/mime-routes/SKILL.md +3 -1
- package/skills/parallel/SKILL.md +32 -1
- package/skills/ppr/SKILL.md +16 -6
- package/skills/prerender/SKILL.md +8 -4
- package/skills/rango/SKILL.md +21 -17
- package/skills/react-compiler/SKILL.md +3 -3
- package/skills/response-routes/SKILL.md +4 -2
- package/skills/route/SKILL.md +5 -2
- package/skills/router-setup/SKILL.md +16 -2
- package/skills/scripts/SKILL.md +16 -6
- package/skills/shell-manifest/SKILL.md +16 -7
- package/skills/testing/SKILL.md +2 -2
- package/skills/testing/client-components.md +6 -0
- package/skills/testing/handles.md +30 -8
- package/skills/testing/loader.md +51 -49
- package/skills/testing/middleware.md +1 -1
- package/skills/theme/SKILL.md +8 -5
- package/skills/typesafety/generated-files-and-cli.md +16 -9
- package/skills/typesafety/route-types.md +5 -1
- package/skills/use-cache/SKILL.md +47 -0
- package/src/bin/rango.ts +7 -3
- package/src/browser/event-controller.ts +40 -15
- package/src/browser/navigation-bridge.ts +6 -0
- package/src/browser/navigation-client.ts +5 -0
- package/src/browser/partial-update.ts +77 -10
- package/src/browser/react/NavigationProvider.tsx +7 -0
- package/src/browser/react/use-router.ts +40 -11
- package/src/browser/react/use-search-params.ts +140 -17
- package/src/browser/rsc-router.tsx +59 -0
- package/src/browser/server-action-bridge.ts +26 -0
- package/src/browser/types.ts +22 -0
- package/src/build/merge-full-manifests.ts +161 -0
- package/src/build/route-trie.ts +9 -332
- package/src/build/route-types/include-resolution.ts +66 -11
- package/src/build/route-types/per-module-writer.ts +11 -6
- package/src/build/route-types/router-processing.ts +184 -153
- package/src/build/runtime-discovery.ts +23 -12
- package/src/cache/cache-exec-scope.ts +47 -0
- package/src/cache/cache-runtime.ts +113 -40
- package/src/cache/cf/cf-cache-constants.ts +8 -1
- package/src/cache/cf/cf-cache-store.ts +41 -64
- package/src/cache/cf/cf-cache-types.ts +1 -1
- package/src/cache/cf/cf-kv-utils.ts +38 -0
- package/src/cache/segment-codec.ts +21 -5
- package/src/cache/taint.ts +28 -9
- package/src/client-urls/client-root.tsx +168 -0
- package/src/client-urls/client-urls.ts +698 -0
- package/src/client-urls/navigation.ts +237 -0
- package/src/client-urls/revalidation-protocol.ts +56 -0
- package/src/client-urls/server-projection.ts +579 -0
- package/src/client-urls/types.ts +195 -0
- package/src/client.rsc.tsx +12 -0
- package/src/client.tsx +49 -6
- package/src/decode-loader-results.ts +113 -0
- package/src/errors.ts +14 -0
- package/src/handles/deferred-resolution.ts +14 -7
- package/src/handles/is-thenable.ts +2 -4
- package/src/index.ts +1 -0
- package/src/loader-redirect.tsx +64 -0
- package/src/outlet-context.ts +12 -0
- package/src/outlet-provider.tsx +15 -1
- package/src/redirect-origin.ts +29 -0
- package/src/route-content-wrapper.tsx +96 -3
- package/src/route-definition/dsl-helpers.ts +28 -3
- package/src/route-definition/helpers-types.ts +13 -0
- package/src/route-definition/redirect.ts +17 -18
- package/src/router/error-handling.ts +65 -11
- package/src/router/intercept-resolution.ts +29 -0
- package/src/router/loader-resolution.ts +261 -28
- package/src/router/match-middleware/cache-lookup.ts +24 -15
- package/src/router/match-result.ts +7 -0
- package/src/router/revalidation.ts +24 -11
- package/src/router/route-trie-builder.ts +334 -0
- package/src/router/router-interfaces.ts +38 -0
- package/src/router/segment-resolution/fresh.ts +47 -0
- package/src/router/segment-resolution/helpers.ts +9 -11
- package/src/router/segment-resolution/loader-cache.ts +14 -24
- package/src/router/segment-resolution/revalidation.ts +20 -1
- package/src/router/trie-matching.ts +3 -3
- package/src/router.ts +46 -1
- package/src/rsc/full-payload.ts +6 -0
- package/src/rsc/handler.ts +36 -12
- package/src/rsc/helpers.ts +13 -0
- package/src/rsc/loader-fetch.ts +2 -2
- package/src/rsc/manifest-init.ts +28 -9
- package/src/rsc/progressive-enhancement.ts +247 -70
- package/src/rsc/render-pipeline.ts +68 -24
- package/src/rsc/routine-plan.ts +359 -0
- package/src/rsc/rsc-rendering.ts +569 -302
- package/src/rsc/server-action.ts +180 -71
- package/src/rsc/shell-capture-constants.ts +18 -0
- package/src/rsc/shell-capture.ts +104 -21
- package/src/rsc/shell-serve.ts +15 -2
- package/src/rsc/ssr-setup.ts +10 -1
- package/src/rsc/types.ts +31 -2
- package/src/segment-system.tsx +83 -26
- package/src/server/context.ts +10 -0
- package/src/server/cookie-store.ts +19 -19
- package/src/server/handle-store.ts +185 -48
- package/src/server/request-context.ts +36 -6
- package/src/server.ts +7 -0
- package/src/ssr/index.tsx +37 -2
- package/src/ssr/ssr-root.tsx +30 -2
- package/src/testing/dom.entry.ts +1 -1
- package/src/testing/render-route.tsx +22 -8
- package/src/testing/run-loader.ts +51 -13
- package/src/types/boundaries.ts +19 -0
- package/src/types/index.ts +1 -0
- package/src/types/loader-types.ts +60 -5
- package/src/types/segments.ts +7 -0
- package/src/urls/include-helper.ts +22 -4
- package/src/urls/path-helper-types.ts +14 -1
- package/src/use-loader.tsx +67 -6
- package/src/vite/discovery/client-urls-projection.ts +322 -0
- package/src/vite/discovery/discover-routers.ts +43 -17
- package/src/vite/discovery/state.ts +11 -1
- package/src/vite/discovery/virtual-module-codegen.ts +20 -0
- package/src/vite/encryption-key.ts +29 -0
- package/src/vite/plugins/expose-action-id.ts +2 -2
- package/src/vite/plugins/expose-internal-ids.ts +46 -0
- package/src/vite/plugins/server-ref-hashing.ts +74 -0
- package/src/vite/plugins/server-reference-pattern.ts +10 -0
- package/src/vite/plugins/virtual-entries.ts +12 -3
- package/src/vite/rango.ts +9 -0
- package/src/vite/router-discovery.ts +184 -14
- package/src/vite/utils/shared-utils.ts +12 -7
- package/dist/types.backup/__internal.d.ts +0 -127
- package/dist/types.backup/bin/rango.d.ts +0 -1
- package/dist/types.backup/browser/action-coordinator.d.ts +0 -57
- package/dist/types.backup/browser/action-fence.d.ts +0 -33
- package/dist/types.backup/browser/app-shell.d.ts +0 -34
- package/dist/types.backup/browser/app-version.d.ts +0 -6
- package/dist/types.backup/browser/connection-warmup.d.ts +0 -31
- package/dist/types.backup/browser/cookie-name.d.ts +0 -66
- package/dist/types.backup/browser/event-controller.d.ts +0 -221
- package/dist/types.backup/browser/history-state.d.ts +0 -26
- package/dist/types.backup/browser/index.d.ts +0 -1
- package/dist/types.backup/browser/intercept-utils.d.ts +0 -30
- package/dist/types.backup/browser/invalidate-client-cache.d.ts +0 -17
- package/dist/types.backup/browser/link-interceptor.d.ts +0 -43
- package/dist/types.backup/browser/logging.d.ts +0 -33
- package/dist/types.backup/browser/merge-segment-loaders.d.ts +0 -38
- package/dist/types.backup/browser/navigation-bridge.d.ts +0 -27
- package/dist/types.backup/browser/navigation-client.d.ts +0 -17
- package/dist/types.backup/browser/navigation-store-handle.d.ts +0 -25
- package/dist/types.backup/browser/navigation-store.d.ts +0 -95
- package/dist/types.backup/browser/navigation-transaction.d.ts +0 -75
- package/dist/types.backup/browser/network-error-handler.d.ts +0 -35
- package/dist/types.backup/browser/partial-update.d.ts +0 -61
- package/dist/types.backup/browser/prefetch/cache.d.ts +0 -183
- package/dist/types.backup/browser/prefetch/fetch.d.ts +0 -52
- package/dist/types.backup/browser/prefetch/observer.d.ts +0 -27
- package/dist/types.backup/browser/prefetch/policy.d.ts +0 -13
- package/dist/types.backup/browser/prefetch/queue.d.ts +0 -48
- package/dist/types.backup/browser/prefetch/resource-ready.d.ts +0 -28
- package/dist/types.backup/browser/rango-state.d.ts +0 -52
- package/dist/types.backup/browser/react/Link.d.ts +0 -140
- package/dist/types.backup/browser/react/NavigationProvider.d.ts +0 -88
- package/dist/types.backup/browser/react/ScrollRestoration.d.ts +0 -78
- package/dist/types.backup/browser/react/context.d.ts +0 -54
- package/dist/types.backup/browser/react/filter-segment-order.d.ts +0 -35
- package/dist/types.backup/browser/react/index.d.ts +0 -1
- package/dist/types.backup/browser/react/location-state-shared.d.ts +0 -162
- package/dist/types.backup/browser/react/location-state.d.ts +0 -29
- package/dist/types.backup/browser/react/mount-context.d.ts +0 -23
- package/dist/types.backup/browser/react/nonce-context.d.ts +0 -14
- package/dist/types.backup/browser/react/shallow-equal.d.ts +0 -5
- package/dist/types.backup/browser/react/use-action.d.ts +0 -61
- package/dist/types.backup/browser/react/use-handle.d.ts +0 -21
- package/dist/types.backup/browser/react/use-href.d.ts +0 -32
- package/dist/types.backup/browser/react/use-link-status.d.ts +0 -36
- package/dist/types.backup/browser/react/use-mount.d.ts +0 -24
- package/dist/types.backup/browser/react/use-navigation.d.ts +0 -15
- package/dist/types.backup/browser/react/use-params.d.ts +0 -21
- package/dist/types.backup/browser/react/use-pathname.d.ts +0 -13
- package/dist/types.backup/browser/react/use-reverse.d.ts +0 -40
- package/dist/types.backup/browser/react/use-router.d.ts +0 -23
- package/dist/types.backup/browser/react/use-search-params.d.ts +0 -19
- package/dist/types.backup/browser/react/use-segments.d.ts +0 -29
- package/dist/types.backup/browser/response-adapter.d.ts +0 -58
- package/dist/types.backup/browser/rsc-router.d.ts +0 -141
- package/dist/types.backup/browser/scroll-restoration.d.ts +0 -103
- package/dist/types.backup/browser/segment-reconciler.d.ts +0 -74
- package/dist/types.backup/browser/segment-structure-assert.d.ts +0 -16
- package/dist/types.backup/browser/server-action-bridge.d.ts +0 -29
- package/dist/types.backup/browser/types.d.ts +0 -530
- package/dist/types.backup/browser/validate-redirect-origin.d.ts +0 -28
- package/dist/types.backup/build/collect-fallback-refs.d.ts +0 -5
- package/dist/types.backup/build/generate-manifest.d.ts +0 -100
- package/dist/types.backup/build/generate-route-types.d.ts +0 -8
- package/dist/types.backup/build/index.d.ts +0 -21
- package/dist/types.backup/build/prefix-tree-utils.d.ts +0 -56
- package/dist/types.backup/build/route-types/ast-helpers.d.ts +0 -3
- package/dist/types.backup/build/route-types/ast-route-extraction.d.ts +0 -13
- package/dist/types.backup/build/route-types/codegen.d.ts +0 -16
- package/dist/types.backup/build/route-types/include-resolution.d.ts +0 -74
- package/dist/types.backup/build/route-types/param-extraction.d.ts +0 -13
- package/dist/types.backup/build/route-types/per-module-writer.d.ts +0 -18
- package/dist/types.backup/build/route-types/router-processing.d.ts +0 -82
- package/dist/types.backup/build/route-types/scan-filter.d.ts +0 -17
- package/dist/types.backup/build/route-types/source-scan.d.ts +0 -13
- package/dist/types.backup/build/runtime-discovery.d.ts +0 -24
- package/dist/types.backup/cache/background-task.d.ts +0 -21
- package/dist/types.backup/cache/cache-error.d.ts +0 -71
- package/dist/types.backup/cache/cache-key-utils.d.ts +0 -35
- package/dist/types.backup/cache/cache-policy.d.ts +0 -59
- package/dist/types.backup/cache/cache-runtime.d.ts +0 -51
- package/dist/types.backup/cache/cache-scope.d.ts +0 -134
- package/dist/types.backup/cache/cache-tag.d.ts +0 -79
- package/dist/types.backup/cache/cf/cf-base64.d.ts +0 -4
- package/dist/types.backup/cache/cf/cf-cache-constants.d.ts +0 -105
- package/dist/types.backup/cache/cf/cf-cache-store.d.ts +0 -481
- package/dist/types.backup/cache/cf/cf-cache-types.d.ts +0 -300
- package/dist/types.backup/cache/cf/cf-kv-utils.d.ts +0 -22
- package/dist/types.backup/cache/cf/cf-tag-marker-memo.d.ts +0 -15
- package/dist/types.backup/cache/cf/index.d.ts +0 -3
- package/dist/types.backup/cache/document-cache.d.ts +0 -69
- package/dist/types.backup/cache/handle-capture.d.ts +0 -23
- package/dist/types.backup/cache/handle-snapshot.d.ts +0 -39
- package/dist/types.backup/cache/index.d.ts +0 -7
- package/dist/types.backup/cache/memory-segment-store.d.ts +0 -163
- package/dist/types.backup/cache/profile-registry.d.ts +0 -40
- package/dist/types.backup/cache/read-through-swr.d.ts +0 -60
- package/dist/types.backup/cache/segment-codec.d.ts +0 -78
- package/dist/types.backup/cache/shell-snapshot.d.ts +0 -162
- package/dist/types.backup/cache/tag-invalidation.d.ts +0 -74
- package/dist/types.backup/cache/taint.d.ts +0 -71
- package/dist/types.backup/cache/types.d.ts +0 -407
- package/dist/types.backup/cache/vercel/index.d.ts +0 -1
- package/dist/types.backup/cache/vercel/vercel-cache-store.d.ts +0 -267
- package/dist/types.backup/client.d.ts +0 -184
- package/dist/types.backup/client.rsc.d.ts +0 -39
- package/dist/types.backup/cloudflare/index.d.ts +0 -7
- package/dist/types.backup/cloudflare/tracing.d.ts +0 -53
- package/dist/types.backup/component-utils.d.ts +0 -46
- package/dist/types.backup/components/DefaultDocument.d.ts +0 -13
- package/dist/types.backup/context-var.d.ts +0 -84
- package/dist/types.backup/debug.d.ts +0 -57
- package/dist/types.backup/decode-loader-results.d.ts +0 -5
- package/dist/types.backup/default-error-boundary.d.ts +0 -10
- package/dist/types.backup/defer.d.ts +0 -89
- package/dist/types.backup/deps/browser.d.ts +0 -1
- package/dist/types.backup/deps/html-stream-client.d.ts +0 -1
- package/dist/types.backup/deps/html-stream-server.d.ts +0 -1
- package/dist/types.backup/deps/rsc.d.ts +0 -1
- package/dist/types.backup/deps/ssr.d.ts +0 -1
- package/dist/types.backup/encode-kv.d.ts +0 -35
- package/dist/types.backup/errors.d.ts +0 -226
- package/dist/types.backup/escape-script.d.ts +0 -44
- package/dist/types.backup/handle.d.ts +0 -93
- package/dist/types.backup/handles/MetaTags.d.ts +0 -17
- package/dist/types.backup/handles/Scripts.d.ts +0 -38
- package/dist/types.backup/handles/breadcrumbs.d.ts +0 -43
- package/dist/types.backup/handles/deferred-resolution.d.ts +0 -53
- package/dist/types.backup/handles/is-thenable.d.ts +0 -12
- package/dist/types.backup/handles/meta.d.ts +0 -43
- package/dist/types.backup/handles/script.d.ts +0 -139
- package/dist/types.backup/host/cookie-handler.d.ts +0 -8
- package/dist/types.backup/host/errors.d.ts +0 -40
- package/dist/types.backup/host/index.d.ts +0 -33
- package/dist/types.backup/host/pattern-matcher.d.ts +0 -30
- package/dist/types.backup/host/router.d.ts +0 -12
- package/dist/types.backup/host/testing.d.ts +0 -41
- package/dist/types.backup/host/types.d.ts +0 -148
- package/dist/types.backup/host/utils.d.ts +0 -20
- package/dist/types.backup/href-client.d.ts +0 -214
- package/dist/types.backup/index.d.ts +0 -112
- package/dist/types.backup/index.rsc.d.ts +0 -51
- package/dist/types.backup/internal-debug.d.ts +0 -1
- package/dist/types.backup/loader-store.d.ts +0 -193
- package/dist/types.backup/loader.d.ts +0 -18
- package/dist/types.backup/loader.rsc.d.ts +0 -18
- package/dist/types.backup/missing-id-error.d.ts +0 -1
- package/dist/types.backup/outlet-context.d.ts +0 -12
- package/dist/types.backup/outlet-provider.d.ts +0 -12
- package/dist/types.backup/prerender/build-shell-capture.d.ts +0 -104
- package/dist/types.backup/prerender/param-hash.d.ts +0 -6
- package/dist/types.backup/prerender/shell-manifest-key.d.ts +0 -18
- package/dist/types.backup/prerender/store.d.ts +0 -62
- package/dist/types.backup/prerender.d.ts +0 -292
- package/dist/types.backup/redirect-origin.d.ts +0 -55
- package/dist/types.backup/regex-escape.d.ts +0 -6
- package/dist/types.backup/render-error-thrower.d.ts +0 -13
- package/dist/types.backup/response-utils.d.ts +0 -35
- package/dist/types.backup/reverse.d.ts +0 -206
- package/dist/types.backup/root-error-boundary.d.ts +0 -32
- package/dist/types.backup/route-content-wrapper.d.ts +0 -40
- package/dist/types.backup/route-definition/dsl-helpers.d.ts +0 -130
- package/dist/types.backup/route-definition/helper-factories.d.ts +0 -22
- package/dist/types.backup/route-definition/helpers-types.d.ts +0 -392
- package/dist/types.backup/route-definition/index.d.ts +0 -7
- package/dist/types.backup/route-definition/redirect.d.ts +0 -48
- package/dist/types.backup/route-definition/resolve-handler-use.d.ts +0 -19
- package/dist/types.backup/route-definition/use-item-types.d.ts +0 -1
- package/dist/types.backup/route-definition.d.ts +0 -1
- package/dist/types.backup/route-map-builder.d.ts +0 -127
- package/dist/types.backup/route-name.d.ts +0 -27
- package/dist/types.backup/route-types.d.ts +0 -172
- package/dist/types.backup/router/basename.d.ts +0 -10
- package/dist/types.backup/router/content-negotiation.d.ts +0 -91
- package/dist/types.backup/router/debug-manifest.d.ts +0 -7
- package/dist/types.backup/router/error-handling.d.ts +0 -76
- package/dist/types.backup/router/find-match.d.ts +0 -19
- package/dist/types.backup/router/handler-context.d.ts +0 -41
- package/dist/types.backup/router/instrument.d.ts +0 -161
- package/dist/types.backup/router/intercept-resolution.d.ts +0 -79
- package/dist/types.backup/router/lazy-includes.d.ts +0 -26
- package/dist/types.backup/router/loader-resolution.d.ts +0 -63
- package/dist/types.backup/router/logging.d.ts +0 -41
- package/dist/types.backup/router/manifest.d.ts +0 -8
- package/dist/types.backup/router/match-api.d.ts +0 -19
- package/dist/types.backup/router/match-context.d.ts +0 -184
- package/dist/types.backup/router/match-handlers.d.ts +0 -49
- package/dist/types.backup/router/match-middleware/background-revalidation.d.ts +0 -113
- package/dist/types.backup/router/match-middleware/cache-lookup.d.ts +0 -113
- package/dist/types.backup/router/match-middleware/cache-store.d.ts +0 -112
- package/dist/types.backup/router/match-middleware/index.d.ts +0 -80
- package/dist/types.backup/router/match-middleware/intercept-resolution.d.ts +0 -116
- package/dist/types.backup/router/match-middleware/segment-resolution.d.ts +0 -94
- package/dist/types.backup/router/match-pipelines.d.ts +0 -103
- package/dist/types.backup/router/match-result.d.ts +0 -114
- package/dist/types.backup/router/metrics.d.ts +0 -6
- package/dist/types.backup/router/middleware-types.d.ts +0 -74
- package/dist/types.backup/router/middleware.d.ts +0 -116
- package/dist/types.backup/router/navigation-snapshot.d.ts +0 -22
- package/dist/types.backup/router/params-util.d.ts +0 -8
- package/dist/types.backup/router/parse-pattern.d.ts +0 -38
- package/dist/types.backup/router/pattern-matching.d.ts +0 -169
- package/dist/types.backup/router/prefetch-cache-ttl.d.ts +0 -27
- package/dist/types.backup/router/prefetch-limits.d.ts +0 -20
- package/dist/types.backup/router/prerender-match.d.ts +0 -50
- package/dist/types.backup/router/preview-match.d.ts +0 -22
- package/dist/types.backup/router/request-classification.d.ts +0 -104
- package/dist/types.backup/router/revalidation.d.ts +0 -57
- package/dist/types.backup/router/route-snapshot.d.ts +0 -112
- package/dist/types.backup/router/router-context.d.ts +0 -137
- package/dist/types.backup/router/router-interfaces.d.ts +0 -432
- package/dist/types.backup/router/router-options.d.ts +0 -738
- package/dist/types.backup/router/router-registry.d.ts +0 -15
- package/dist/types.backup/router/segment-resolution/fresh.d.ts +0 -55
- package/dist/types.backup/router/segment-resolution/helpers.d.ts +0 -93
- package/dist/types.backup/router/segment-resolution/loader-cache.d.ts +0 -33
- package/dist/types.backup/router/segment-resolution/loader-mask.d.ts +0 -44
- package/dist/types.backup/router/segment-resolution/loader-snapshot.d.ts +0 -90
- package/dist/types.backup/router/segment-resolution/mask-nested.d.ts +0 -53
- package/dist/types.backup/router/segment-resolution/revalidation.d.ts +0 -85
- package/dist/types.backup/router/segment-resolution/static-store.d.ts +0 -17
- package/dist/types.backup/router/segment-resolution/streamed-handler-telemetry.d.ts +0 -16
- package/dist/types.backup/router/segment-resolution/view-transition-default.d.ts +0 -28
- package/dist/types.backup/router/segment-resolution.d.ts +0 -3
- package/dist/types.backup/router/segment-wrappers.d.ts +0 -53
- package/dist/types.backup/router/state-cookie-name.d.ts +0 -1
- package/dist/types.backup/router/substitute-pattern-params.d.ts +0 -23
- package/dist/types.backup/router/telemetry-otel.d.ts +0 -113
- package/dist/types.backup/router/telemetry.d.ts +0 -215
- package/dist/types.backup/router/timeout.d.ts +0 -68
- package/dist/types.backup/router/tracing.d.ts +0 -125
- package/dist/types.backup/router/trie-matching.d.ts +0 -32
- package/dist/types.backup/router/types.d.ts +0 -98
- package/dist/types.backup/router/url-params.d.ts +0 -26
- package/dist/types.backup/router.d.ts +0 -7
- package/dist/types.backup/rsc/capture-queue.d.ts +0 -6
- package/dist/types.backup/rsc/full-payload.d.ts +0 -22
- package/dist/types.backup/rsc/handler-context.d.ts +0 -31
- package/dist/types.backup/rsc/handler.d.ts +0 -9
- package/dist/types.backup/rsc/helpers.d.ts +0 -213
- package/dist/types.backup/rsc/index.d.ts +0 -17
- package/dist/types.backup/rsc/json-route-result.d.ts +0 -20
- package/dist/types.backup/rsc/loader-fetch.d.ts +0 -14
- package/dist/types.backup/rsc/manifest-init.d.ts +0 -18
- package/dist/types.backup/rsc/nonce.d.ts +0 -28
- package/dist/types.backup/rsc/origin-guard.d.ts +0 -50
- package/dist/types.backup/rsc/progressive-enhancement.d.ts +0 -19
- package/dist/types.backup/rsc/redirect-guard.d.ts +0 -35
- package/dist/types.backup/rsc/response-cache-serve.d.ts +0 -46
- package/dist/types.backup/rsc/response-error.d.ts +0 -19
- package/dist/types.backup/rsc/response-route-handler.d.ts +0 -29
- package/dist/types.backup/rsc/rsc-rendering.d.ts +0 -23
- package/dist/types.backup/rsc/runtime-warnings.d.ts +0 -22
- package/dist/types.backup/rsc/server-action.d.ts +0 -68
- package/dist/types.backup/rsc/shell-build-manifest.d.ts +0 -84
- package/dist/types.backup/rsc/shell-capture-constants.d.ts +0 -27
- package/dist/types.backup/rsc/shell-capture.d.ts +0 -374
- package/dist/types.backup/rsc/shell-serve.d.ts +0 -136
- package/dist/types.backup/rsc/ssr-setup.d.ts +0 -48
- package/dist/types.backup/rsc/transition-gate.d.ts +0 -27
- package/dist/types.backup/rsc/types.d.ts +0 -290
- package/dist/types.backup/runtime-env.d.ts +0 -1
- package/dist/types.backup/search-params.d.ts +0 -125
- package/dist/types.backup/segment-content-promise.d.ts +0 -13
- package/dist/types.backup/segment-fragments.d.ts +0 -56
- package/dist/types.backup/segment-loader-promise.d.ts +0 -22
- package/dist/types.backup/segment-system.d.ts +0 -84
- package/dist/types.backup/serialize.d.ts +0 -164
- package/dist/types.backup/server/context.d.ts +0 -494
- package/dist/types.backup/server/cookie-parse.d.ts +0 -10
- package/dist/types.backup/server/cookie-store.d.ts +0 -107
- package/dist/types.backup/server/fetchable-loader-store.d.ts +0 -20
- package/dist/types.backup/server/handle-store.d.ts +0 -100
- package/dist/types.backup/server/loader-registry.d.ts +0 -32
- package/dist/types.backup/server/request-context.d.ts +0 -596
- package/dist/types.backup/server/root-layout.d.ts +0 -3
- package/dist/types.backup/server.d.ts +0 -15
- package/dist/types.backup/ssr/index.d.ts +0 -233
- package/dist/types.backup/ssr/inject-rsc-eager.d.ts +0 -3
- package/dist/types.backup/ssr/preinit-client-references.d.ts +0 -71
- package/dist/types.backup/ssr/ssr-root.d.ts +0 -69
- package/dist/types.backup/static-handler.d.ts +0 -57
- package/dist/types.backup/testing/cache-status.d.ts +0 -63
- package/dist/types.backup/testing/collect-handle.d.ts +0 -20
- package/dist/types.backup/testing/dispatch.d.ts +0 -123
- package/dist/types.backup/testing/dom.entry.d.ts +0 -15
- package/dist/types.backup/testing/e2e/fixture.d.ts +0 -37
- package/dist/types.backup/testing/e2e/index.d.ts +0 -30
- package/dist/types.backup/testing/e2e/matchers.d.ts +0 -17
- package/dist/types.backup/testing/e2e/page-helpers.d.ts +0 -62
- package/dist/types.backup/testing/e2e/parity.d.ts +0 -111
- package/dist/types.backup/testing/e2e/server.d.ts +0 -35
- package/dist/types.backup/testing/flight-matchers.d.ts +0 -55
- package/dist/types.backup/testing/flight-normalize.d.ts +0 -1
- package/dist/types.backup/testing/flight-tree.d.ts +0 -192
- package/dist/types.backup/testing/flight.d.ts +0 -115
- package/dist/types.backup/testing/flight.entry.d.ts +0 -27
- package/dist/types.backup/testing/generated-routes.d.ts +0 -66
- package/dist/types.backup/testing/index.d.ts +0 -52
- package/dist/types.backup/testing/internal/context.d.ts +0 -225
- package/dist/types.backup/testing/internal/flight-client-globals.d.ts +0 -1
- package/dist/types.backup/testing/internal/seed-vars.d.ts +0 -30
- package/dist/types.backup/testing/render-handler.d.ts +0 -160
- package/dist/types.backup/testing/render-route.d.ts +0 -246
- package/dist/types.backup/testing/run-loader.d.ts +0 -186
- package/dist/types.backup/testing/run-middleware.d.ts +0 -132
- package/dist/types.backup/testing/run-transition-when.d.ts +0 -77
- package/dist/types.backup/testing/vitest-stubs/cloudflare-email.d.ts +0 -6
- package/dist/types.backup/testing/vitest-stubs/cloudflare-workers.d.ts +0 -13
- package/dist/types.backup/testing/vitest-stubs/plugin-rsc.d.ts +0 -7
- package/dist/types.backup/testing/vitest-stubs/version.d.ts +0 -1
- package/dist/types.backup/testing/vitest.d.ts +0 -205
- package/dist/types.backup/theme/ThemeProvider.d.ts +0 -13
- package/dist/types.backup/theme/ThemeScript.d.ts +0 -45
- package/dist/types.backup/theme/constants.d.ts +0 -39
- package/dist/types.backup/theme/index.d.ts +0 -29
- package/dist/types.backup/theme/theme-context.d.ts +0 -21
- package/dist/types.backup/theme/theme-script.d.ts +0 -26
- package/dist/types.backup/theme/types.d.ts +0 -162
- package/dist/types.backup/theme/use-theme.d.ts +0 -8
- package/dist/types.backup/types/boundaries.d.ts +0 -93
- package/dist/types.backup/types/cache-types.d.ts +0 -191
- package/dist/types.backup/types/error-types.d.ts +0 -114
- package/dist/types.backup/types/global-namespace.d.ts +0 -90
- package/dist/types.backup/types/handler-context.d.ts +0 -658
- package/dist/types.backup/types/index.d.ts +0 -11
- package/dist/types.backup/types/loader-types.d.ts +0 -182
- package/dist/types.backup/types/request-scope.d.ts +0 -93
- package/dist/types.backup/types/route-config.d.ts +0 -105
- package/dist/types.backup/types/route-entry.d.ts +0 -95
- package/dist/types.backup/types/segments.d.ts +0 -234
- package/dist/types.backup/types.d.ts +0 -1
- package/dist/types.backup/urls/include-helper.d.ts +0 -17
- package/dist/types.backup/urls/include-provider.d.ts +0 -27
- package/dist/types.backup/urls/index.d.ts +0 -6
- package/dist/types.backup/urls/path-helper-types.d.ts +0 -197
- package/dist/types.backup/urls/path-helper.d.ts +0 -12
- package/dist/types.backup/urls/pattern-types.d.ts +0 -166
- package/dist/types.backup/urls/response-types.d.ts +0 -67
- package/dist/types.backup/urls/type-extraction.d.ts +0 -157
- package/dist/types.backup/urls/urls-function.d.ts +0 -24
- package/dist/types.backup/urls.d.ts +0 -1
- package/dist/types.backup/use-loader.d.ts +0 -150
- package/dist/types.backup/vercel/index.d.ts +0 -10
- package/dist/types.backup/vercel/tracing.d.ts +0 -70
- package/dist/types.backup/vite/debug.d.ts +0 -80
- package/dist/types.backup/vite/discovery/bundle-postprocess.d.ts +0 -12
- package/dist/types.backup/vite/discovery/dev-prerender-cache.d.ts +0 -65
- package/dist/types.backup/vite/discovery/discover-routers.d.ts +0 -17
- package/dist/types.backup/vite/discovery/discovery-errors.d.ts +0 -113
- package/dist/types.backup/vite/discovery/gate-state.d.ts +0 -79
- package/dist/types.backup/vite/discovery/prerender-collection.d.ts +0 -24
- package/dist/types.backup/vite/discovery/route-types-writer.d.ts +0 -32
- package/dist/types.backup/vite/discovery/self-gen-tracking.d.ts +0 -22
- package/dist/types.backup/vite/discovery/shell-prerender-phase.d.ts +0 -40
- package/dist/types.backup/vite/discovery/state.d.ts +0 -162
- package/dist/types.backup/vite/discovery/virtual-module-codegen.d.ts +0 -15
- package/dist/types.backup/vite/index.d.ts +0 -11
- package/dist/types.backup/vite/inject-client-debug.d.ts +0 -56
- package/dist/types.backup/vite/plugin-types.d.ts +0 -298
- package/dist/types.backup/vite/plugins/cjs-to-esm.d.ts +0 -6
- package/dist/types.backup/vite/plugins/client-ref-dedup.d.ts +0 -40
- package/dist/types.backup/vite/plugins/client-ref-hashing.d.ts +0 -35
- package/dist/types.backup/vite/plugins/cloudflare-protocol-stub.d.ts +0 -64
- package/dist/types.backup/vite/plugins/expose-action-id.d.ts +0 -18
- package/dist/types.backup/vite/plugins/expose-id-utils.d.ts +0 -37
- package/dist/types.backup/vite/plugins/expose-ids/export-analysis.d.ts +0 -19
- package/dist/types.backup/vite/plugins/expose-ids/handler-transform.d.ts +0 -10
- package/dist/types.backup/vite/plugins/expose-ids/loader-transform.d.ts +0 -8
- package/dist/types.backup/vite/plugins/expose-ids/router-transform.d.ts +0 -13
- package/dist/types.backup/vite/plugins/expose-ids/types.d.ts +0 -29
- package/dist/types.backup/vite/plugins/expose-internal-ids.d.ts +0 -6
- package/dist/types.backup/vite/plugins/performance-tracks.d.ts +0 -25
- package/dist/types.backup/vite/plugins/refresh-cmd.d.ts +0 -20
- package/dist/types.backup/vite/plugins/use-cache-transform.d.ts +0 -20
- package/dist/types.backup/vite/plugins/vercel-output.d.ts +0 -85
- package/dist/types.backup/vite/plugins/version-injector.d.ts +0 -21
- package/dist/types.backup/vite/plugins/version-plugin.d.ts +0 -19
- package/dist/types.backup/vite/plugins/virtual-entries.d.ts +0 -36
- package/dist/types.backup/vite/plugins/virtual-stub-plugin.d.ts +0 -7
- package/dist/types.backup/vite/rango.d.ts +0 -29
- package/dist/types.backup/vite/router-discovery.d.ts +0 -23
- package/dist/types.backup/vite/utils/ast-handler-extract.d.ts +0 -64
- package/dist/types.backup/vite/utils/banner.d.ts +0 -2
- package/dist/types.backup/vite/utils/bundle-analysis.d.ts +0 -28
- package/dist/types.backup/vite/utils/client-chunks.d.ts +0 -55
- package/dist/types.backup/vite/utils/directive-prologue.d.ts +0 -16
- package/dist/types.backup/vite/utils/forward-user-plugins.d.ts +0 -37
- package/dist/types.backup/vite/utils/manifest-utils.d.ts +0 -7
- package/dist/types.backup/vite/utils/package-resolution.d.ts +0 -6
- package/dist/types.backup/vite/utils/prerender-utils.d.ts +0 -32
- package/dist/types.backup/vite/utils/shared-utils.d.ts +0 -55
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
recordRequestTags,
|
|
52
52
|
runWithCacheTagScope,
|
|
53
53
|
} from "./cache-tag.js";
|
|
54
|
+
import { runWithCacheExecScope } from "./cache-exec-scope.js";
|
|
54
55
|
import { reportCacheError } from "./cache-error.js";
|
|
55
56
|
import type { CacheItemResult } from "./types.js";
|
|
56
57
|
|
|
@@ -205,8 +206,63 @@ interface CacheEnvelope {
|
|
|
205
206
|
* singleton), and cleared for a key as soon as the leader settles: a rejected
|
|
206
207
|
* leader (function threw, or the result was not serializable) propagates to
|
|
207
208
|
* current waiters, which then retry fresh.
|
|
209
|
+
*
|
|
210
|
+
* A leader that NEVER settles must not hang followers (scar tissue, autobarn
|
|
211
|
+
* pilot outage): a background shell capture's render became leader, awaited a
|
|
212
|
+
* tarpitting upstream fetch, and workerd killed the capture's waitUntil context
|
|
213
|
+
* — orphaning the leader promise as permanently pending, its map entry never
|
|
214
|
+
* cleared. Every later document render calling the same cached function (an
|
|
215
|
+
* isolate-global key for plain-args calls) awaited it forever before first
|
|
216
|
+
* byte: isolate-wide TTFB-0 until redeploy. Followers therefore trust an entry
|
|
217
|
+
* only for {@link IN_FLIGHT_LEADER_MAX_WAIT_MS} from registration; past it
|
|
218
|
+
* they evict the entry and run fresh — bounded duplicate upstream work instead
|
|
219
|
+
* of an unbounded hang. Eviction is age-based off `registeredAt`, so it also
|
|
220
|
+
* heals entries stranded by a killed context (no timer in that context needs
|
|
221
|
+
* to survive).
|
|
222
|
+
*/
|
|
223
|
+
interface InFlightExecution {
|
|
224
|
+
promise: Promise<CacheEnvelope>;
|
|
225
|
+
registeredAt: number;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const inFlightExecutions = new Map<string, InFlightExecution>();
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* How long a follower trusts an in-flight leader before evicting it and
|
|
232
|
+
* running fresh. High enough that a slow-but-healthy upstream never triggers
|
|
233
|
+
* duplicate work (a legitimate cached call taking >15s is already pathological);
|
|
234
|
+
* low enough to bound the blast radius of a wedged leader to seconds, not the
|
|
235
|
+
* isolate lifetime. Aligned with SHELL_CAPTURE_MAX_WAIT_MS.
|
|
208
236
|
*/
|
|
209
|
-
const
|
|
237
|
+
const IN_FLIGHT_LEADER_MAX_WAIT_MS = 15_000;
|
|
238
|
+
|
|
239
|
+
/** Distinguishes a leader timeout from a leader rejection in raceLeader. */
|
|
240
|
+
const LEADER_TIMED_OUT = Symbol("leader-timed-out");
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Await a leader's envelope for at most `remainingMs`. Resolves with the
|
|
244
|
+
* envelope, `undefined` on leader rejection (the leader already cleared its
|
|
245
|
+
* entry — caller falls through to a fresh run), or {@link LEADER_TIMED_OUT}
|
|
246
|
+
* when the window expires first (caller must evict the entry itself).
|
|
247
|
+
*/
|
|
248
|
+
function raceLeader(
|
|
249
|
+
promise: Promise<CacheEnvelope>,
|
|
250
|
+
remainingMs: number,
|
|
251
|
+
): Promise<CacheEnvelope | undefined | typeof LEADER_TIMED_OUT> {
|
|
252
|
+
return new Promise((resolve) => {
|
|
253
|
+
const timer = setTimeout(() => resolve(LEADER_TIMED_OUT), remainingMs);
|
|
254
|
+
promise.then(
|
|
255
|
+
(envelope) => {
|
|
256
|
+
clearTimeout(timer);
|
|
257
|
+
resolve(envelope);
|
|
258
|
+
},
|
|
259
|
+
() => {
|
|
260
|
+
clearTimeout(timer);
|
|
261
|
+
resolve(undefined);
|
|
262
|
+
},
|
|
263
|
+
);
|
|
264
|
+
});
|
|
265
|
+
}
|
|
210
266
|
|
|
211
267
|
// ============================================================================
|
|
212
268
|
// Core: registerCachedFunction
|
|
@@ -234,11 +290,12 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
|
|
|
234
290
|
// cacheTag() call inside the function degrades to a no-op rather than
|
|
235
291
|
// throwing "must be called inside a use cache function" - adopting cacheTag()
|
|
236
292
|
// must not hard-fail in apps/tests without an item-capable cache configured.
|
|
237
|
-
// Note: the
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
//
|
|
241
|
-
// non-serializable-args
|
|
293
|
+
// Note: the cache-exec guards (cookies()/headers()/ctx.set() rejection —
|
|
294
|
+
// runWithCacheExecScope + the INSIDE_CACHE_EXEC arg stamp) are intentionally
|
|
295
|
+
// NOT active here. They are cached-path-only checks; in the bypass the body
|
|
296
|
+
// actually executes, so the guarded side effects take effect and nothing is
|
|
297
|
+
// lost on a (non-existent) hit. Same applies to the non-serializable-args
|
|
298
|
+
// bypass below.
|
|
242
299
|
if (!store?.getItem) {
|
|
243
300
|
// Test-ergonomics guard: under a test runner, a "use cache" function that
|
|
244
301
|
// executes with no item-capable store seeded is exercising the UNCACHED
|
|
@@ -452,9 +509,12 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
|
|
|
452
509
|
// property stamped onto that SHARED object — so for the whole
|
|
453
510
|
// revalidation window a concurrent foreground ctx.set() /
|
|
454
511
|
// ctx.headers.*() would throw (issue #684, plan 010). requestCtx is
|
|
455
|
-
//
|
|
456
|
-
//
|
|
457
|
-
//
|
|
512
|
+
// never stamped anywhere for the same reason. In-body misuse IS
|
|
513
|
+
// still guarded here: runWithCacheExecScope below follows only this
|
|
514
|
+
// body's async chain, so a conditional cookies()/headers() read
|
|
515
|
+
// that fires only during revalidation throws instead of baking one
|
|
516
|
+
// user's value into the shared entry — with zero visibility to the
|
|
517
|
+
// concurrent foreground.
|
|
458
518
|
|
|
459
519
|
try {
|
|
460
520
|
// Re-establish the request-context ALS so a "use cache" body that
|
|
@@ -475,7 +535,7 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
|
|
|
475
535
|
PHASES.background("use-cache-revalidation"),
|
|
476
536
|
async () => {
|
|
477
537
|
const scoped = runWithCacheTagScope(() =>
|
|
478
|
-
fn.apply(this, args),
|
|
538
|
+
runWithCacheExecScope(() => fn.apply(this, args)),
|
|
479
539
|
);
|
|
480
540
|
const freshResult = await scoped.result;
|
|
481
541
|
bgStopCapture?.();
|
|
@@ -541,20 +601,34 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
|
|
|
541
601
|
// requests. The store write stays exactly once (the leader's).
|
|
542
602
|
const existing = inFlightExecutions.get(cacheKey);
|
|
543
603
|
if (existing) {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
604
|
+
const remainingMs =
|
|
605
|
+
IN_FLIGHT_LEADER_MAX_WAIT_MS - (Date.now() - existing.registeredAt);
|
|
606
|
+
const raced =
|
|
607
|
+
remainingMs <= 0
|
|
608
|
+
? LEADER_TIMED_OUT
|
|
609
|
+
: await raceLeader(existing.promise, remainingMs);
|
|
610
|
+
if (raced === LEADER_TIMED_OUT) {
|
|
611
|
+
// Wedged (or context-orphaned) leader: evict so this call and every
|
|
612
|
+
// later one run fresh. Guard the delete so a newer leader's entry is
|
|
613
|
+
// never removed; the stale leader's own clearSelf is identity-guarded
|
|
614
|
+
// the same way, so it can't evict our replacement if it settles late.
|
|
615
|
+
if (inFlightExecutions.get(cacheKey) === existing) {
|
|
616
|
+
inFlightExecutions.delete(cacheKey);
|
|
617
|
+
}
|
|
618
|
+
reportCacheError(
|
|
619
|
+
new Error(
|
|
620
|
+
`in-flight leader did not settle within ${IN_FLIGHT_LEADER_MAX_WAIT_MS}ms; evicted — executing fresh`,
|
|
621
|
+
),
|
|
622
|
+
"cache-read",
|
|
623
|
+
`[use cache] "${id}" inflight-timeout`,
|
|
624
|
+
);
|
|
625
|
+
// Fall through to a fresh execution below (this call becomes leader).
|
|
626
|
+
} else if (raced) {
|
|
553
627
|
try {
|
|
554
628
|
return await serveCached({
|
|
555
|
-
value:
|
|
556
|
-
handles:
|
|
557
|
-
tags:
|
|
629
|
+
value: raced.serialized,
|
|
630
|
+
handles: raced.handles,
|
|
631
|
+
tags: raced.tags,
|
|
558
632
|
shouldRevalidate: false,
|
|
559
633
|
});
|
|
560
634
|
} catch (error) {
|
|
@@ -566,6 +640,8 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
|
|
|
566
640
|
// Fall through to a fresh execution below.
|
|
567
641
|
}
|
|
568
642
|
}
|
|
643
|
+
// raced === undefined: leader rejected; its map entry is already
|
|
644
|
+
// cleared, so fall through to a fresh run.
|
|
569
645
|
}
|
|
570
646
|
|
|
571
647
|
// This call becomes the leader. Register a deferred envelope so concurrent
|
|
@@ -582,9 +658,12 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
|
|
|
582
658
|
// Followers attach their own catch; guard the map's own reference so a
|
|
583
659
|
// rejected envelope with no waiter is not an unhandled rejection.
|
|
584
660
|
envelopePromise.catch(() => {});
|
|
585
|
-
inFlightExecutions.set(cacheKey,
|
|
661
|
+
inFlightExecutions.set(cacheKey, {
|
|
662
|
+
promise: envelopePromise,
|
|
663
|
+
registeredAt: Date.now(),
|
|
664
|
+
});
|
|
586
665
|
const clearSelf = (): void => {
|
|
587
|
-
if (inFlightExecutions.get(cacheKey) === envelopePromise) {
|
|
666
|
+
if (inFlightExecutions.get(cacheKey)?.promise === envelopePromise) {
|
|
588
667
|
inFlightExecutions.delete(cacheKey);
|
|
589
668
|
}
|
|
590
669
|
};
|
|
@@ -604,12 +683,13 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
|
|
|
604
683
|
// Uses ref-counted stamp/unstamp so overlapping executions
|
|
605
684
|
// sharing the same ctx don't clear each other's guards.
|
|
606
685
|
//
|
|
607
|
-
//
|
|
608
|
-
//
|
|
609
|
-
//
|
|
610
|
-
//
|
|
611
|
-
//
|
|
612
|
-
//
|
|
686
|
+
// The ambient guards (cookies()/headers() and getRequestContext()-reached
|
|
687
|
+
// ctx methods) ride runWithCacheExecScope below instead — an ALS scoped to
|
|
688
|
+
// the cached body's own async chain. The RequestContext itself is
|
|
689
|
+
// deliberately NOT stamped: a property on that SHARED object made every
|
|
690
|
+
// parallel read on the request throw for the cached body's whole
|
|
691
|
+
// execution window (a 2s cached fetch poisoned a sibling loader's
|
|
692
|
+
// cookies() — same hazard class as issue #684, plan 010).
|
|
613
693
|
const taintedArgs: unknown[] = [];
|
|
614
694
|
for (const arg of args) {
|
|
615
695
|
if (isTainted(arg)) {
|
|
@@ -617,17 +697,13 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
|
|
|
617
697
|
taintedArgs.push(arg);
|
|
618
698
|
}
|
|
619
699
|
}
|
|
620
|
-
// Always stamp the ALS RequestContext so cookies()/headers() guards fire
|
|
621
|
-
// even when the cached function receives no tainted args. The guard in
|
|
622
|
-
// cookie-store.ts checks RequestContext, not function args.
|
|
623
|
-
if (requestCtx) {
|
|
624
|
-
stampCacheExec(requestCtx as object);
|
|
625
|
-
}
|
|
626
700
|
|
|
627
701
|
let result: any;
|
|
628
702
|
let scoped: ReturnType<typeof runWithCacheTagScope>;
|
|
629
703
|
try {
|
|
630
|
-
scoped = runWithCacheTagScope(() =>
|
|
704
|
+
scoped = runWithCacheTagScope(() =>
|
|
705
|
+
runWithCacheExecScope(() => fn.apply(this, args)),
|
|
706
|
+
);
|
|
631
707
|
result = await scoped.result;
|
|
632
708
|
} catch (execError) {
|
|
633
709
|
// The function threw: drop the in-flight entry and reject any waiters so
|
|
@@ -640,9 +716,6 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
|
|
|
640
716
|
for (const arg of taintedArgs) {
|
|
641
717
|
unstampCacheExec(arg as object);
|
|
642
718
|
}
|
|
643
|
-
if (requestCtx) {
|
|
644
|
-
unstampCacheExec(requestCtx as object);
|
|
645
|
-
}
|
|
646
719
|
// Remove this capture token (order-independent, safe for concurrent use)
|
|
647
720
|
stopCapture?.();
|
|
648
721
|
}
|
|
@@ -85,8 +85,15 @@ export const MAX_REVALIDATION_INTERVAL = 30;
|
|
|
85
85
|
*
|
|
86
86
|
* This is the default; override per store via
|
|
87
87
|
* `CFCacheStoreOptions.edgeLookupTimeoutMs` (<= 0 disables the budget).
|
|
88
|
+
*
|
|
89
|
+
* 25ms, raised from 10: production Workers logs (autobarn pilot) showed the
|
|
90
|
+
* 10ms budget firing frequently on cold colos where the first Cache API touch
|
|
91
|
+
* is slow but healthy — each false positive downgrades a warm L1 HIT to an
|
|
92
|
+
* L2/render round trip that costs far more than the 15ms of extra patience.
|
|
93
|
+
* A genuinely degraded colo still gets cut off; tune per store for
|
|
94
|
+
* shell-critical routes.
|
|
88
95
|
*/
|
|
89
|
-
export const EDGE_LOOKUP_TIMEOUT_MS =
|
|
96
|
+
export const EDGE_LOOKUP_TIMEOUT_MS = 25;
|
|
90
97
|
|
|
91
98
|
/**
|
|
92
99
|
* Maximum time (ms) to wait for the BODY of a matched L1 entry to be read
|
|
@@ -57,10 +57,13 @@ import { reportCacheError, reportingAsync } from "../cache-error.js";
|
|
|
57
57
|
import type { CacheErrorCategory } from "../cache-error.js";
|
|
58
58
|
import { bufferToBase64, base64ToBuffer } from "./cf-base64.js";
|
|
59
59
|
import {
|
|
60
|
+
KV_KEY_PRESERVED_PREFIX_BYTES,
|
|
60
61
|
KV_MAX_KEY_BYTES,
|
|
61
62
|
KV_MIN_EXPIRATION_TTL,
|
|
62
63
|
kvKeyByteLength,
|
|
64
|
+
kvKeyDigest,
|
|
63
65
|
remainingCacheControl,
|
|
66
|
+
truncateToBytes,
|
|
64
67
|
} from "./cf-kv-utils.js";
|
|
65
68
|
import {
|
|
66
69
|
TAG_MARKER_CACHE_PREFIX,
|
|
@@ -1325,7 +1328,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
1325
1328
|
|
|
1326
1329
|
// L2: delete from KV
|
|
1327
1330
|
if (this.kv && this.waitUntil) {
|
|
1328
|
-
const kvKey = this.toKVKey(key);
|
|
1331
|
+
const kvKey = await this.toKVKey(key);
|
|
1329
1332
|
this.waitUntil(() =>
|
|
1330
1333
|
reportingAsync(
|
|
1331
1334
|
() => this.kv!.delete(kvKey),
|
|
@@ -1559,7 +1562,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
1559
1562
|
|
|
1560
1563
|
// L2: persist to KV (KV requires expirationTtl >= 60s)
|
|
1561
1564
|
if (this.kv && this.waitUntil && totalTtl >= 60) {
|
|
1562
|
-
const kvKey = this.toDocKVKey(key);
|
|
1565
|
+
const kvKey = await this.toDocKVKey(key);
|
|
1563
1566
|
// Finding #3: never persist a per-client signal in the KV envelope.
|
|
1564
1567
|
const headersArray: [string, string][] = [];
|
|
1565
1568
|
response.headers.forEach((v, k) => {
|
|
@@ -1848,7 +1851,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
1848
1851
|
// JSON.stringify(KVItemEnvelope); field names differ from L1 so we assemble
|
|
1849
1852
|
// from the pre-escaped value/handles pieces rather than re-stringifying.
|
|
1850
1853
|
if (this.kv && this.waitUntil && totalTtl >= 60) {
|
|
1851
|
-
const kvKey = this.toKVKey(`fn:${key}`);
|
|
1854
|
+
const kvKey = await this.toKVKey(`fn:${key}`);
|
|
1852
1855
|
const expiresAt = staleAt + swrWindow * 1000;
|
|
1853
1856
|
let envelopeJson = `{"v":${valueJson}`;
|
|
1854
1857
|
if (handlesJson !== undefined) envelopeJson += `,"h":${handlesJson}`;
|
|
@@ -2073,20 +2076,8 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
2073
2076
|
const taggedAt =
|
|
2074
2077
|
Array.isArray(tags) && tags.length > 0 ? entry.createdAt : undefined;
|
|
2075
2078
|
|
|
2076
|
-
const kvKey = this.toKVKey(`shell:${key}`);
|
|
2077
|
-
|
|
2078
|
-
const kvKeyBytes = kvKeyByteLength(kvKey);
|
|
2079
|
-
if (kvKeyBytes > KV_MAX_KEY_BYTES) {
|
|
2080
|
-
reportCacheError(
|
|
2081
|
-
new Error(
|
|
2082
|
-
`shell cache key produces a ${kvKeyBytes}-byte KV key, over the ` +
|
|
2083
|
-
`${KV_MAX_KEY_BYTES}-byte limit; the shell was not persisted to KV (L2).`,
|
|
2084
|
-
),
|
|
2085
|
-
"cache-write",
|
|
2086
|
-
"[CFCacheStore] putShell",
|
|
2087
|
-
);
|
|
2088
|
-
writeKv = false;
|
|
2089
|
-
}
|
|
2079
|
+
const kvKey = await this.toKVKey(`shell:${key}`);
|
|
2080
|
+
const writeKv = retentionTtl >= KV_MIN_EXPIRATION_TTL;
|
|
2090
2081
|
|
|
2091
2082
|
const write = (async (): Promise<"stored" | "invalidated" | void> => {
|
|
2092
2083
|
if (
|
|
@@ -2219,7 +2210,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
2219
2210
|
if (!this.kv) return null;
|
|
2220
2211
|
try {
|
|
2221
2212
|
const readStartedAt = INTERNAL_RANGO_DEBUG ? Date.now() : 0;
|
|
2222
|
-
const kvKey = this.toKVKey(`shell:${key}`);
|
|
2213
|
+
const kvKey = await this.toKVKey(`shell:${key}`);
|
|
2223
2214
|
const { value: envelope, timedOut } =
|
|
2224
2215
|
await this.kvGetOrEvict<CFShellEnvelope>(
|
|
2225
2216
|
kvKey,
|
|
@@ -2330,11 +2321,27 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
2330
2321
|
/**
|
|
2331
2322
|
* Convert string key to KV key string.
|
|
2332
2323
|
* Uses same version prefix as Cache API for consistent invalidation.
|
|
2324
|
+
*
|
|
2325
|
+
* Single chokepoint for EVERY KV family (segments, items, shells, documents
|
|
2326
|
+
* via toDocKVKey, tag markers via tagMarkerKey): a composed key over
|
|
2327
|
+
* Cloudflare KV's 512-byte limit is normalized to a preserved readable
|
|
2328
|
+
* prefix plus a SHA-256-derived 128-bit digest of the FULL key. Without
|
|
2329
|
+
* this, kv.put/get reject with `414 ... exceeds key length limit of 512`
|
|
2330
|
+
* and the entry silently never reaches L2 (observed in production for
|
|
2331
|
+
* "use cache" items whose serialized args — e.g. a CMS query object — blow
|
|
2332
|
+
* the cap; autobarn pilot). Keys are opaque storage identifiers, so
|
|
2333
|
+
* normalization is semantics-preserving as long as distinct logical keys
|
|
2334
|
+
* stay distinct: colliding requires an identical 400-byte prefix AND a
|
|
2335
|
+
* 128-bit SHA-256 collision. Deterministic, so every family's read, write,
|
|
2336
|
+
* and delete paths agree on the stored key.
|
|
2333
2337
|
* @internal
|
|
2334
2338
|
*/
|
|
2335
|
-
private toKVKey(key: string): string {
|
|
2339
|
+
private async toKVKey(key: string): Promise<string> {
|
|
2336
2340
|
const versionPath = this.version ? `v/${this.version}/` : "";
|
|
2337
|
-
|
|
2341
|
+
const composed = `${versionPath}${key}`;
|
|
2342
|
+
if (kvKeyByteLength(composed) <= KV_MAX_KEY_BYTES) return composed;
|
|
2343
|
+
const prefix = truncateToBytes(composed, KV_KEY_PRESERVED_PREFIX_BYTES);
|
|
2344
|
+
return `${prefix}~${await kvKeyDigest(composed)}`;
|
|
2338
2345
|
}
|
|
2339
2346
|
|
|
2340
2347
|
/**
|
|
@@ -2364,7 +2371,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
2364
2371
|
* document tier is the one with a request-host context here.
|
|
2365
2372
|
* @internal
|
|
2366
2373
|
*/
|
|
2367
|
-
private toDocKVKey(key: string): string {
|
|
2374
|
+
private toDocKVKey(key: string): Promise<string> {
|
|
2368
2375
|
return this.toKVKey(`h/${this.docKVHost()}/doc:${key}`);
|
|
2369
2376
|
}
|
|
2370
2377
|
|
|
@@ -2480,7 +2487,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
2480
2487
|
// ============================================================================
|
|
2481
2488
|
|
|
2482
2489
|
/** KV key for a tag's invalidation marker. */
|
|
2483
|
-
private tagMarkerKey(tag: string): string {
|
|
2490
|
+
private tagMarkerKey(tag: string): Promise<string> {
|
|
2484
2491
|
return this.toKVKey(`${TAG_MARKER_PREFIX}${tag}`);
|
|
2485
2492
|
}
|
|
2486
2493
|
|
|
@@ -2805,7 +2812,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
2805
2812
|
// (reported cache-read), which also fails open. Either way one slow tag
|
|
2806
2813
|
// never amplifies into a per-segment stall.
|
|
2807
2814
|
const { value: raw, timedOut } = await this.readWithTimeout<string | null>(
|
|
2808
|
-
() => this.kv!.get(this.tagMarkerKey(tag), { type: "text" }),
|
|
2815
|
+
async () => this.kv!.get(await this.tagMarkerKey(tag), { type: "text" }),
|
|
2809
2816
|
this.kvReadTimeoutMs,
|
|
2810
2817
|
"tag marker KV read",
|
|
2811
2818
|
);
|
|
@@ -3012,7 +3019,8 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
3012
3019
|
*
|
|
3013
3020
|
* Durable-write integrity: the in-memory write-through (memo + L1) for a tag
|
|
3014
3021
|
* runs ONLY after that tag's KV marker write is confirmed. If any KV write
|
|
3015
|
-
* fails (transient error
|
|
3022
|
+
* fails (transient error; over-limit keys are normalized by toKVKey rather
|
|
3023
|
+
* than rejected), this rejects with the
|
|
3016
3024
|
* failed tags so an awaiting updateTag() surfaces the failure instead of
|
|
3017
3025
|
* silently reporting success while other requests/colos serve stale data. The
|
|
3018
3026
|
* eager purge still fires for the whole batch first (it is additive).
|
|
@@ -3065,18 +3073,10 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
3065
3073
|
}
|
|
3066
3074
|
await Promise.all(
|
|
3067
3075
|
tags.map(async (tag) => {
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
errors.push(
|
|
3073
|
-
new Error(
|
|
3074
|
-
`tag "${tag}" produces a ${markerKeyBytes}-byte KV ` +
|
|
3075
|
-
`marker key, over the ${KV_MAX_KEY_BYTES}-byte limit`,
|
|
3076
|
-
),
|
|
3077
|
-
);
|
|
3078
|
-
return;
|
|
3079
|
-
}
|
|
3076
|
+
// An over-limit tag no longer rejects: tagMarkerKey normalizes
|
|
3077
|
+
// through toKVKey, and the marker read path derives the key the same
|
|
3078
|
+
// way, so oversized tags invalidate correctly instead of erroring.
|
|
3079
|
+
const markerKey = await this.tagMarkerKey(tag);
|
|
3080
3080
|
try {
|
|
3081
3081
|
await this.kv!.put(markerKey, String(invalidatedAt), {
|
|
3082
3082
|
...(this.tagInvalidationTtl
|
|
@@ -3200,7 +3200,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
3200
3200
|
if (!this.kv) return null;
|
|
3201
3201
|
|
|
3202
3202
|
try {
|
|
3203
|
-
const kvKey = this.toKVKey(key);
|
|
3203
|
+
const kvKey = await this.toKVKey(key);
|
|
3204
3204
|
const { value: envelope, timedOut } =
|
|
3205
3205
|
await this.kvGetOrEvict<KVSegmentEnvelope>(
|
|
3206
3206
|
kvKey,
|
|
@@ -3294,29 +3294,6 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
3294
3294
|
// KV requires expirationTtl >= 60s. Skip write for short-lived entries.
|
|
3295
3295
|
if (!this.kv || !this.waitUntil || totalTtl < 60) return;
|
|
3296
3296
|
|
|
3297
|
-
const kvKey = this.toKVKey(key);
|
|
3298
|
-
|
|
3299
|
-
// Reject an oversized data-segment KV key the same way tag-marker keys are
|
|
3300
|
-
// rejected in invalidateTags(). A key over KV_MAX_KEY_BYTES makes kv.put()
|
|
3301
|
-
// fail, so the segment silently never lands in L2 (KV) and every cold-colo
|
|
3302
|
-
// or TTL-expired read re-renders instead of serving stale. Segment keys can
|
|
3303
|
-
// grow with user-controlled inputs (e.g. a route's search params), so report
|
|
3304
|
-
// a clear, actionable error and skip the doomed write rather than letting it
|
|
3305
|
-
// reject deep inside waitUntil as an opaque cache-write failure.
|
|
3306
|
-
const kvKeyBytes = kvKeyByteLength(kvKey);
|
|
3307
|
-
if (kvKeyBytes > KV_MAX_KEY_BYTES) {
|
|
3308
|
-
reportCacheError(
|
|
3309
|
-
new Error(
|
|
3310
|
-
`cache segment key produces a ${kvKeyBytes}-byte KV key, over the ` +
|
|
3311
|
-
`${KV_MAX_KEY_BYTES}-byte limit; the segment was not persisted to KV (L2). ` +
|
|
3312
|
-
`Reduce the cache-key inputs (e.g. large search params on this route).`,
|
|
3313
|
-
),
|
|
3314
|
-
"cache-write",
|
|
3315
|
-
"[CFCacheStore] kvSetSegment",
|
|
3316
|
-
);
|
|
3317
|
-
return;
|
|
3318
|
-
}
|
|
3319
|
-
|
|
3320
3297
|
const expiresAt = staleAt + swrWindow * 1000;
|
|
3321
3298
|
// Same wire shape as JSON.stringify({ d, s, e }) — dataJson is already
|
|
3322
3299
|
// valid JSON for CachedEntryData, so embedding it avoids re-walking the tree.
|
|
@@ -3324,8 +3301,8 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
3324
3301
|
|
|
3325
3302
|
this.waitUntil(() =>
|
|
3326
3303
|
reportingAsync(
|
|
3327
|
-
() =>
|
|
3328
|
-
this.kv!.put(
|
|
3304
|
+
async () =>
|
|
3305
|
+
this.kv!.put(await this.toKVKey(key), envelopeJson, {
|
|
3329
3306
|
expirationTtl: totalTtl,
|
|
3330
3307
|
}),
|
|
3331
3308
|
"cache-write",
|
|
@@ -3386,7 +3363,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
3386
3363
|
if (!this.kv) return null;
|
|
3387
3364
|
|
|
3388
3365
|
try {
|
|
3389
|
-
const kvKey = this.toKVKey(`fn:${key}`);
|
|
3366
|
+
const kvKey = await this.toKVKey(`fn:${key}`);
|
|
3390
3367
|
const { value: envelope, timedOut } =
|
|
3391
3368
|
await this.kvGetOrEvict<KVItemEnvelope>(
|
|
3392
3369
|
kvKey,
|
|
@@ -3508,7 +3485,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
|
3508
3485
|
if (!this.kv) return null;
|
|
3509
3486
|
|
|
3510
3487
|
try {
|
|
3511
|
-
const kvKey = this.toDocKVKey(key);
|
|
3488
|
+
const kvKey = await this.toDocKVKey(key);
|
|
3512
3489
|
// The document path is debug-silent (op is only get/getItem): a KV-read
|
|
3513
3490
|
// timeout here is bounded for resilience parity (kvGetOrEvict applies the
|
|
3514
3491
|
// budget) but emits no kv-timeout event, so its absence from the debug
|
|
@@ -333,7 +333,7 @@ export interface CFCacheStoreOptions<TEnv = unknown> {
|
|
|
333
333
|
* cannot stall the request; the read then falls through to its normal miss
|
|
334
334
|
* path (L2/KV or render).
|
|
335
335
|
*
|
|
336
|
-
* Defaults to {@link EDGE_LOOKUP_TIMEOUT_MS} (
|
|
336
|
+
* Defaults to {@link EDGE_LOOKUP_TIMEOUT_MS} (25). Set to 0 (or any value
|
|
337
337
|
* <= 0) to disable the budget and always await `match`.
|
|
338
338
|
*/
|
|
339
339
|
edgeLookupTimeoutMs?: number;
|
|
@@ -26,6 +26,44 @@ export function kvKeyByteLength(key: string): number {
|
|
|
26
26
|
return kvKeyEncoder.encode(key).length;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Bytes of a composed key preserved verbatim when an over-limit key is
|
|
31
|
+
* normalized (toKVKey). 400 leaves room for the `~` separator and the 32-hex
|
|
32
|
+
* digest inside KV_MAX_KEY_BYTES while keeping the version prefix and most of
|
|
33
|
+
* the logical key readable (and prefix-listable) in the KV dashboard.
|
|
34
|
+
*/
|
|
35
|
+
export const KV_KEY_PRESERVED_PREFIX_BYTES = 400;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Truncate to at most `maxBytes` of UTF-8. A multibyte sequence split at the
|
|
39
|
+
* boundary decodes to U+FFFD, which is stripped — the result is deterministic
|
|
40
|
+
* for a given input, which is all key normalization needs.
|
|
41
|
+
*/
|
|
42
|
+
export function truncateToBytes(value: string, maxBytes: number): string {
|
|
43
|
+
const bytes = kvKeyEncoder.encode(value);
|
|
44
|
+
if (bytes.length <= maxBytes) return value;
|
|
45
|
+
return new TextDecoder()
|
|
46
|
+
.decode(bytes.subarray(0, maxBytes))
|
|
47
|
+
.replace(/�+$/, "");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 128-bit hex digest (SHA-256 truncated) of a composed KV key. WebCrypto is
|
|
52
|
+
* available on workerd and Node alike; SHA-256 (not a fast non-crypto hash)
|
|
53
|
+
* because cache keys embed user-influenced serialized args — an engineered
|
|
54
|
+
* collision between two normalized keys would serve one entry's payload for
|
|
55
|
+
* the other's.
|
|
56
|
+
*/
|
|
57
|
+
export async function kvKeyDigest(value: string): Promise<string> {
|
|
58
|
+
const digest = await crypto.subtle.digest(
|
|
59
|
+
"SHA-256",
|
|
60
|
+
kvKeyEncoder.encode(value),
|
|
61
|
+
);
|
|
62
|
+
return Array.from(new Uint8Array(digest, 0, 16), (b) =>
|
|
63
|
+
b.toString(16).padStart(2, "0"),
|
|
64
|
+
).join("");
|
|
65
|
+
}
|
|
66
|
+
|
|
29
67
|
/**
|
|
30
68
|
* Compute the Cache-Control directive for a stale-path REVALIDATING re-put from
|
|
31
69
|
* the entry's stored hard-expiry deadline (CACHE_EXPIRES_AT_HEADER). Returns the
|
|
@@ -18,6 +18,12 @@ import {
|
|
|
18
18
|
} from "@vitejs/plugin-rsc/rsc";
|
|
19
19
|
import { createFromReadableStream } from "@vitejs/plugin-rsc/rsc";
|
|
20
20
|
|
|
21
|
+
// Preserve embedded server references on a cache/prerender HIT so they
|
|
22
|
+
// re-serialize to the client instead of resolving to a raw function React
|
|
23
|
+
// refuses to pass to a Client Component. Shared across the deserialize sites so
|
|
24
|
+
// the decode policy has a single owner.
|
|
25
|
+
const PRESERVE_SERVER_REFS = { preserveServerReferences: true } as const;
|
|
26
|
+
|
|
21
27
|
/**
|
|
22
28
|
* Convert a ReadableStream to a string.
|
|
23
29
|
*/
|
|
@@ -81,7 +87,11 @@ export async function rscDeserialize<T>(
|
|
|
81
87
|
|
|
82
88
|
const temporaryReferences = createTemporaryReferenceSet();
|
|
83
89
|
const stream = stringToStream(encoded);
|
|
84
|
-
return createFromReadableStream<T>(
|
|
90
|
+
return createFromReadableStream<T>(
|
|
91
|
+
stream,
|
|
92
|
+
{ temporaryReferences },
|
|
93
|
+
PRESERVE_SERVER_REFS,
|
|
94
|
+
);
|
|
85
95
|
}
|
|
86
96
|
|
|
87
97
|
/**
|
|
@@ -116,7 +126,11 @@ export async function serializeResult(value: unknown): Promise<string | null> {
|
|
|
116
126
|
export async function deserializeResult<T>(encoded: string): Promise<T> {
|
|
117
127
|
const temporaryReferences = createTemporaryReferenceSet();
|
|
118
128
|
const stream = stringToStream(encoded);
|
|
119
|
-
return createFromReadableStream<T>(
|
|
129
|
+
return createFromReadableStream<T>(
|
|
130
|
+
stream,
|
|
131
|
+
{ temporaryReferences },
|
|
132
|
+
PRESERVE_SERVER_REFS,
|
|
133
|
+
);
|
|
120
134
|
}
|
|
121
135
|
|
|
122
136
|
/**
|
|
@@ -271,9 +285,11 @@ export async function deserializeSegments(
|
|
|
271
285
|
|
|
272
286
|
const [component, layout, loaderData, loaderDataPromise, loadingData] =
|
|
273
287
|
await Promise.all([
|
|
274
|
-
createFromReadableStream(
|
|
275
|
-
|
|
276
|
-
|
|
288
|
+
createFromReadableStream(
|
|
289
|
+
stringToStream(item.encoded),
|
|
290
|
+
{ temporaryReferences },
|
|
291
|
+
PRESERVE_SERVER_REFS,
|
|
292
|
+
),
|
|
277
293
|
rscDeserialize(item.encodedLayout),
|
|
278
294
|
rscDeserialize(item.encodedLoaderData),
|
|
279
295
|
rscDeserialize(item.encodedLoaderDataPromise),
|
package/src/cache/taint.ts
CHANGED
|
@@ -21,14 +21,19 @@ export function isTainted(value: unknown): boolean {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
* Symbol stamped on tainted ctx
|
|
25
|
-
*
|
|
26
|
-
* throw if present —
|
|
27
|
-
*
|
|
24
|
+
* Symbol stamped on tainted ctx objects PASSED AS ARGUMENTS to a "use cache"
|
|
25
|
+
* function during its execution. ctx.set(), ctx.header(), etc. check this
|
|
26
|
+
* flag and throw if present — those side effects are lost on cache hit.
|
|
27
|
+
*
|
|
28
|
+
* Argument objects only. Ambient access (cookies()/headers() and ctx methods
|
|
29
|
+
* reached via getRequestContext()) is guarded by the AsyncLocalStorage scope
|
|
30
|
+
* in cache-exec-scope.ts instead: stamping the SHARED RequestContext made
|
|
31
|
+
* every parallel read on the request throw for the cached body's whole
|
|
32
|
+
* execution window (a 2s cached fetch poisoned a sibling loader's cookies()).
|
|
28
33
|
*
|
|
29
34
|
* The value is a numeric reference count, not a boolean. Multiple concurrent
|
|
30
|
-
* cached functions sharing the same ctx
|
|
31
|
-
*
|
|
35
|
+
* cached functions sharing the same ctx each increment on entry and decrement
|
|
36
|
+
* on exit. Guards fire when count > 0.
|
|
32
37
|
*/
|
|
33
38
|
export const INSIDE_CACHE_EXEC: unique symbol = Symbol.for(
|
|
34
39
|
"rango:inside-cache-exec",
|
|
@@ -56,6 +61,20 @@ export function unstampCacheExec(obj: object): void {
|
|
|
56
61
|
}
|
|
57
62
|
}
|
|
58
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Probe for the AsyncLocalStorage-based "use cache" execution scope.
|
|
66
|
+
* Registered by cache-exec-scope.ts at module init; the default ("never
|
|
67
|
+
* inside") keeps this module free of node:async_hooks — taint.ts is reachable
|
|
68
|
+
* from browser-bundled DSL helpers. Covers AMBIENT ctx access inside a cached
|
|
69
|
+
* body (getRequestContext().set(...)) chain-scoped, where the object stamp
|
|
70
|
+
* below only covers ctx objects explicitly passed as arguments.
|
|
71
|
+
*/
|
|
72
|
+
let cacheExecScopeProbe: () => boolean = () => false;
|
|
73
|
+
|
|
74
|
+
export function _setCacheExecScopeProbe(probe: () => boolean): void {
|
|
75
|
+
cacheExecScopeProbe = probe;
|
|
76
|
+
}
|
|
77
|
+
|
|
59
78
|
/**
|
|
60
79
|
* Throw if ctx is inside a "use cache" execution.
|
|
61
80
|
* Call from side-effecting ctx methods (set, header, etc.) and cookie mutations.
|
|
@@ -64,12 +83,12 @@ export function assertNotInsideCacheExec(
|
|
|
64
83
|
ctx: unknown,
|
|
65
84
|
methodName: string,
|
|
66
85
|
): void {
|
|
67
|
-
|
|
86
|
+
const argStamped =
|
|
68
87
|
ctx !== null &&
|
|
69
88
|
ctx !== undefined &&
|
|
70
89
|
typeof ctx === "object" &&
|
|
71
|
-
(INSIDE_CACHE_EXEC as symbol) in (ctx as Record<symbol, unknown>)
|
|
72
|
-
) {
|
|
90
|
+
(INSIDE_CACHE_EXEC as symbol) in (ctx as Record<symbol, unknown>);
|
|
91
|
+
if (argStamped || cacheExecScopeProbe()) {
|
|
73
92
|
throw new Error(
|
|
74
93
|
`ctx.${methodName}() cannot be called inside a "use cache" function. ` +
|
|
75
94
|
`Side effects on the request context are lost on cache hit because ` +
|