@rangojs/router 0.5.1 → 0.5.2
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/types/browser/event-controller.d.ts +6 -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/handles/is-thenable.d.ts +2 -4
- package/dist/types/rsc/helpers.d.ts +3 -0
- 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/server/request-context.d.ts +4 -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 +154 -57
- package/package.json +3 -3
- package/skills/caching/SKILL.md +1 -1
- package/skills/mime-routes/SKILL.md +3 -1
- package/skills/response-routes/SKILL.md +4 -2
- 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/browser/event-controller.ts +40 -15
- package/src/browser/partial-update.ts +26 -11
- package/src/browser/react/NavigationProvider.tsx +7 -0
- package/src/cache/cache-runtime.ts +89 -15
- 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/handles/is-thenable.ts +2 -4
- package/src/router/match-middleware/cache-lookup.ts +24 -15
- package/src/rsc/handler.ts +26 -5
- package/src/rsc/helpers.ts +13 -0
- 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 +555 -302
- package/src/rsc/server-action.ts +180 -71
- package/src/rsc/shell-capture-constants.ts +18 -0
- package/src/rsc/shell-capture.ts +92 -21
- package/src/server/request-context.ts +6 -0
- package/src/ssr/ssr-root.tsx +1 -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/rango.ts +9 -0
- package/src/vite/router-discovery.ts +21 -2
- 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-trie.d.ts +0 -89
- 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
|
@@ -307,6 +307,53 @@ intercept("@modal", ".product", async (ctx) => {
|
|
|
307
307
|
}),
|
|
308
308
|
```
|
|
309
309
|
|
|
310
|
+
## Embedding server actions in cached components
|
|
311
|
+
|
|
312
|
+
A cached function may return a component that creates an inline `"use server"`
|
|
313
|
+
action. The canonical case is a cached list whose items each carry an action --
|
|
314
|
+
e.g. a cached article list where every row has a like button:
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
export async function ArticleList() {
|
|
318
|
+
"use cache: articles";
|
|
319
|
+
const articles = await db.query("SELECT id, title FROM articles");
|
|
320
|
+
return (
|
|
321
|
+
<ul>
|
|
322
|
+
{articles.map((a) => {
|
|
323
|
+
const articleId = a.id; // captured -> frozen with the cache entry
|
|
324
|
+
async function like() {
|
|
325
|
+
"use server";
|
|
326
|
+
// runs live on click, in the CURRENT request scope
|
|
327
|
+
const user = cookies().get("session")?.value;
|
|
328
|
+
if (user) await db.like(articleId, user);
|
|
329
|
+
}
|
|
330
|
+
return <LikeButton key={a.id} title={a.title} action={like} />;
|
|
331
|
+
})}
|
|
332
|
+
</ul>
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Three behaviors, locked by `use-cache-inline-action.test.ts` (dev + prod):
|
|
338
|
+
|
|
339
|
+
| What | Behavior | Why |
|
|
340
|
+
| ----------------------------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
341
|
+
| Values the action **closes over** (`articleId`) | **Frozen** at cache-write | The closure compiles to encrypted bound args, snapshotted when the entry is written and replayed verbatim on a hit. Correct for stable identities; wrong for volatile values. |
|
|
342
|
+
| The action **body** | **Runs live** every call | Once invoked it is an ordinary server function: fresh computation and live request context. `cookies()` / `headers()` work here (the body executes in the live request). |
|
|
343
|
+
| Invocability on a **cache hit** | **Works** | The action survives serialize -> cache -> deserialize and stays callable. |
|
|
344
|
+
|
|
345
|
+
Rule of thumb: **capture stable identities, read volatile/request-scoped values
|
|
346
|
+
live in the body.** Do not close over a per-request token, the current user, or
|
|
347
|
+
the current time and expect freshness -- those are frozen at cache-write. The
|
|
348
|
+
`cookies()`/`headers()` read guard applies to the cached function body, NOT to an
|
|
349
|
+
inline action's body.
|
|
350
|
+
|
|
351
|
+
Deploy note: captured values are encrypted with a per-build key by default. If
|
|
352
|
+
cache entries can outlive a deploy (a persistent store such as the Cloudflare
|
|
353
|
+
cache store), set `RANGO_ENCRYPTION_KEY` (base64-encoded 32 bytes) in the build
|
|
354
|
+
environment so actions embedded in entries written by a previous deploy still
|
|
355
|
+
decrypt; without it they fail until the entry expires or revalidates.
|
|
356
|
+
|
|
310
357
|
## Vite Transform
|
|
311
358
|
|
|
312
359
|
The `rango:use-cache` Vite plugin detects the directive and wraps exports with
|
|
@@ -234,6 +234,12 @@ export interface EventController {
|
|
|
234
234
|
listener: ActionStateListener,
|
|
235
235
|
): () => void;
|
|
236
236
|
subscribeToHandles(listener: HandleListener): () => void;
|
|
237
|
+
/**
|
|
238
|
+
* Deliver queued location, params, navigation, and handle notifications now.
|
|
239
|
+
* NavigationProvider calls this inside the payload update so React assigns
|
|
240
|
+
* every route-state hook update to the same normal or transition lane.
|
|
241
|
+
*/
|
|
242
|
+
flushRouteState(): void;
|
|
237
243
|
|
|
238
244
|
// Handle operations
|
|
239
245
|
setHandleData(
|
|
@@ -367,19 +373,32 @@ function matchesActionId(
|
|
|
367
373
|
return entryActionId.endsWith(`#${subscriptionId}`);
|
|
368
374
|
}
|
|
369
375
|
|
|
370
|
-
|
|
371
|
-
|
|
376
|
+
interface DebouncedNotifier {
|
|
377
|
+
schedule(): void;
|
|
378
|
+
flush(): void;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Batch rapid notifications into one task to prevent render storms. The
|
|
382
|
+
// explicit flush lets a React update owner preserve its scheduling lane.
|
|
383
|
+
function makeDebouncedNotifier(listeners: Set<() => void>): DebouncedNotifier {
|
|
372
384
|
let timeout: ReturnType<typeof setTimeout> | null = null;
|
|
373
|
-
|
|
374
|
-
if (timeout
|
|
375
|
-
timeout
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
385
|
+
const flush = () => {
|
|
386
|
+
if (timeout === null) return;
|
|
387
|
+
clearTimeout(timeout);
|
|
388
|
+
timeout = null;
|
|
389
|
+
notifyListeners(
|
|
390
|
+
[...listeners],
|
|
391
|
+
(listener) => listener(),
|
|
392
|
+
(listener) => listeners.has(listener),
|
|
393
|
+
);
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
return {
|
|
397
|
+
schedule() {
|
|
398
|
+
if (timeout !== null) clearTimeout(timeout);
|
|
399
|
+
timeout = setTimeout(flush, 0);
|
|
400
|
+
},
|
|
401
|
+
flush,
|
|
383
402
|
};
|
|
384
403
|
}
|
|
385
404
|
|
|
@@ -459,7 +478,7 @@ export function createEventController(
|
|
|
459
478
|
|
|
460
479
|
function notify(): void {
|
|
461
480
|
cachedDerivedState = null;
|
|
462
|
-
notifyStateListeners();
|
|
481
|
+
notifyStateListeners.schedule();
|
|
463
482
|
}
|
|
464
483
|
|
|
465
484
|
const actionNotifyTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
|
|
@@ -517,6 +536,11 @@ export function createEventController(
|
|
|
517
536
|
|
|
518
537
|
const notifyHandles = makeDebouncedNotifier(handleListeners);
|
|
519
538
|
|
|
539
|
+
function flushRouteState(): void {
|
|
540
|
+
notifyStateListeners.flush();
|
|
541
|
+
notifyHandles.flush();
|
|
542
|
+
}
|
|
543
|
+
|
|
520
544
|
function getState(): DerivedNavigationState {
|
|
521
545
|
if (cachedDerivedState) return cachedDerivedState;
|
|
522
546
|
|
|
@@ -1001,7 +1025,7 @@ export function createEventController(
|
|
|
1001
1025
|
handleSegmentOrder = newSegmentOrder;
|
|
1002
1026
|
routeSegmentIds = newRouteSegmentIds;
|
|
1003
1027
|
|
|
1004
|
-
notifyHandles();
|
|
1028
|
+
notifyHandles.schedule();
|
|
1005
1029
|
}
|
|
1006
1030
|
|
|
1007
1031
|
function getHandleState(): HandleState {
|
|
@@ -1021,7 +1045,7 @@ export function createEventController(
|
|
|
1021
1045
|
return;
|
|
1022
1046
|
}
|
|
1023
1047
|
routeSegmentIds = next;
|
|
1024
|
-
notifyHandles();
|
|
1048
|
+
notifyHandles.schedule();
|
|
1025
1049
|
}
|
|
1026
1050
|
|
|
1027
1051
|
// ========================================================================
|
|
@@ -1102,6 +1126,7 @@ export function createEventController(
|
|
|
1102
1126
|
subscribe,
|
|
1103
1127
|
subscribeToAction,
|
|
1104
1128
|
subscribeToHandles,
|
|
1129
|
+
flushRouteState,
|
|
1105
1130
|
|
|
1106
1131
|
// Direct access
|
|
1107
1132
|
getCurrentNavigation: () => currentNavigation,
|
|
@@ -421,10 +421,8 @@ export function createPartialUpdater(
|
|
|
421
421
|
// forceAwait unwraps the ROUTER loader promises during render so they
|
|
422
422
|
// land without a loading()/fallback frame. A fully-prefetched nav has
|
|
423
423
|
// its router data already resolved (the prefetch stream drained), so
|
|
424
|
-
// awaiting it here is free
|
|
425
|
-
//
|
|
426
|
-
// CLIENT component that suspends on mount, which a transition would
|
|
427
|
-
// wrongly suppress by holding the old UI until that suspense settles.
|
|
424
|
+
// awaiting it here is free; the commit below then runs in a transition
|
|
425
|
+
// (fullyPrefetched branch) so nothing router-owned can flash.
|
|
428
426
|
forceAwait: mode.type === "stale-revalidation" || fullyPrefetched,
|
|
429
427
|
interceptSegments:
|
|
430
428
|
reconciled.interceptSegments.length > 0
|
|
@@ -549,14 +547,31 @@ export function createPartialUpdater(
|
|
|
549
547
|
scroll: scrollPayload,
|
|
550
548
|
});
|
|
551
549
|
});
|
|
550
|
+
} else if (fullyPrefetched) {
|
|
551
|
+
// Fully-prefetched nav: the payload is fully resolved (forceAwait
|
|
552
|
+
// above), so commit inside a transition to hold the current UI across
|
|
553
|
+
// the synchronous resolution — no fallback flash. No addTransitionType:
|
|
554
|
+
// this is the React content-hold, not a view transition. Deliberate
|
|
555
|
+
// trade-off (#622 introduced, #624 reverted, then reinstated): a client
|
|
556
|
+
// component that suspends during its FIRST render (use() of a promise
|
|
557
|
+
// created at mount — see ClientMountSuspense in the e2e test-app) under
|
|
558
|
+
// an ALREADY-REVEALED boundary holds the old content until it resolves
|
|
559
|
+
// instead of revealing that boundary's fallback; its render happens
|
|
560
|
+
// pre-commit inside the transition, so userland effects cannot run
|
|
561
|
+
// first. Boundaries newly mounted by this nav still reveal their
|
|
562
|
+
// fallbacks (React shows new boundaries inside transitions).
|
|
563
|
+
startTransition(() => {
|
|
564
|
+
onUpdate({
|
|
565
|
+
root: newTree,
|
|
566
|
+
metadata: payload.metadata!,
|
|
567
|
+
scroll: scrollPayload,
|
|
568
|
+
});
|
|
569
|
+
});
|
|
552
570
|
} else {
|
|
553
|
-
//
|
|
554
|
-
//
|
|
555
|
-
//
|
|
556
|
-
//
|
|
557
|
-
// component that only starts fetching post-mount, retaining the
|
|
558
|
-
// previous page indefinitely. Explicit transition() routes keep the
|
|
559
|
-
// content-hold via the hasTransition branch above (the opt-in).
|
|
571
|
+
// Cold/partially-prefetched nav: normal commit so fallbacks stream
|
|
572
|
+
// like a first load and the click has visible feedback. Explicit
|
|
573
|
+
// transition() routes keep the content-hold via the hasTransition
|
|
574
|
+
// branch above (the opt-in).
|
|
560
575
|
onUpdate({
|
|
561
576
|
root: newTree,
|
|
562
577
|
metadata: payload.metadata!,
|
|
@@ -456,6 +456,13 @@ export function NavigationProvider({
|
|
|
456
456
|
cached === undefined ? update.metadata.resolvedIds : undefined,
|
|
457
457
|
);
|
|
458
458
|
}
|
|
459
|
+
|
|
460
|
+
// tx.commit() and the metadata updates above mutate the controller
|
|
461
|
+
// synchronously, but its ordinary notifications are task-debounced. Flush
|
|
462
|
+
// them here so hook setState calls inherit this payload update's lane. In
|
|
463
|
+
// a transition that suspends, the source tree therefore keeps its source
|
|
464
|
+
// pathname/params until the destination payload commits with them.
|
|
465
|
+
eventController.flushRouteState();
|
|
459
466
|
});
|
|
460
467
|
|
|
461
468
|
return unsubscribe;
|
|
@@ -205,8 +205,63 @@ interface CacheEnvelope {
|
|
|
205
205
|
* singleton), and cleared for a key as soon as the leader settles: a rejected
|
|
206
206
|
* leader (function threw, or the result was not serializable) propagates to
|
|
207
207
|
* current waiters, which then retry fresh.
|
|
208
|
+
*
|
|
209
|
+
* A leader that NEVER settles must not hang followers (scar tissue, autobarn
|
|
210
|
+
* pilot outage): a background shell capture's render became leader, awaited a
|
|
211
|
+
* tarpitting upstream fetch, and workerd killed the capture's waitUntil context
|
|
212
|
+
* — orphaning the leader promise as permanently pending, its map entry never
|
|
213
|
+
* cleared. Every later document render calling the same cached function (an
|
|
214
|
+
* isolate-global key for plain-args calls) awaited it forever before first
|
|
215
|
+
* byte: isolate-wide TTFB-0 until redeploy. Followers therefore trust an entry
|
|
216
|
+
* only for {@link IN_FLIGHT_LEADER_MAX_WAIT_MS} from registration; past it
|
|
217
|
+
* they evict the entry and run fresh — bounded duplicate upstream work instead
|
|
218
|
+
* of an unbounded hang. Eviction is age-based off `registeredAt`, so it also
|
|
219
|
+
* heals entries stranded by a killed context (no timer in that context needs
|
|
220
|
+
* to survive).
|
|
221
|
+
*/
|
|
222
|
+
interface InFlightExecution {
|
|
223
|
+
promise: Promise<CacheEnvelope>;
|
|
224
|
+
registeredAt: number;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const inFlightExecutions = new Map<string, InFlightExecution>();
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* How long a follower trusts an in-flight leader before evicting it and
|
|
231
|
+
* running fresh. High enough that a slow-but-healthy upstream never triggers
|
|
232
|
+
* duplicate work (a legitimate cached call taking >15s is already pathological);
|
|
233
|
+
* low enough to bound the blast radius of a wedged leader to seconds, not the
|
|
234
|
+
* isolate lifetime. Aligned with SHELL_CAPTURE_MAX_WAIT_MS.
|
|
235
|
+
*/
|
|
236
|
+
const IN_FLIGHT_LEADER_MAX_WAIT_MS = 15_000;
|
|
237
|
+
|
|
238
|
+
/** Distinguishes a leader timeout from a leader rejection in raceLeader. */
|
|
239
|
+
const LEADER_TIMED_OUT = Symbol("leader-timed-out");
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Await a leader's envelope for at most `remainingMs`. Resolves with the
|
|
243
|
+
* envelope, `undefined` on leader rejection (the leader already cleared its
|
|
244
|
+
* entry — caller falls through to a fresh run), or {@link LEADER_TIMED_OUT}
|
|
245
|
+
* when the window expires first (caller must evict the entry itself).
|
|
208
246
|
*/
|
|
209
|
-
|
|
247
|
+
function raceLeader(
|
|
248
|
+
promise: Promise<CacheEnvelope>,
|
|
249
|
+
remainingMs: number,
|
|
250
|
+
): Promise<CacheEnvelope | undefined | typeof LEADER_TIMED_OUT> {
|
|
251
|
+
return new Promise((resolve) => {
|
|
252
|
+
const timer = setTimeout(() => resolve(LEADER_TIMED_OUT), remainingMs);
|
|
253
|
+
promise.then(
|
|
254
|
+
(envelope) => {
|
|
255
|
+
clearTimeout(timer);
|
|
256
|
+
resolve(envelope);
|
|
257
|
+
},
|
|
258
|
+
() => {
|
|
259
|
+
clearTimeout(timer);
|
|
260
|
+
resolve(undefined);
|
|
261
|
+
},
|
|
262
|
+
);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
210
265
|
|
|
211
266
|
// ============================================================================
|
|
212
267
|
// Core: registerCachedFunction
|
|
@@ -541,20 +596,34 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
|
|
|
541
596
|
// requests. The store write stays exactly once (the leader's).
|
|
542
597
|
const existing = inFlightExecutions.get(cacheKey);
|
|
543
598
|
if (existing) {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
599
|
+
const remainingMs =
|
|
600
|
+
IN_FLIGHT_LEADER_MAX_WAIT_MS - (Date.now() - existing.registeredAt);
|
|
601
|
+
const raced =
|
|
602
|
+
remainingMs <= 0
|
|
603
|
+
? LEADER_TIMED_OUT
|
|
604
|
+
: await raceLeader(existing.promise, remainingMs);
|
|
605
|
+
if (raced === LEADER_TIMED_OUT) {
|
|
606
|
+
// Wedged (or context-orphaned) leader: evict so this call and every
|
|
607
|
+
// later one run fresh. Guard the delete so a newer leader's entry is
|
|
608
|
+
// never removed; the stale leader's own clearSelf is identity-guarded
|
|
609
|
+
// the same way, so it can't evict our replacement if it settles late.
|
|
610
|
+
if (inFlightExecutions.get(cacheKey) === existing) {
|
|
611
|
+
inFlightExecutions.delete(cacheKey);
|
|
612
|
+
}
|
|
613
|
+
reportCacheError(
|
|
614
|
+
new Error(
|
|
615
|
+
`in-flight leader did not settle within ${IN_FLIGHT_LEADER_MAX_WAIT_MS}ms; evicted — executing fresh`,
|
|
616
|
+
),
|
|
617
|
+
"cache-read",
|
|
618
|
+
`[use cache] "${id}" inflight-timeout`,
|
|
619
|
+
);
|
|
620
|
+
// Fall through to a fresh execution below (this call becomes leader).
|
|
621
|
+
} else if (raced) {
|
|
553
622
|
try {
|
|
554
623
|
return await serveCached({
|
|
555
|
-
value:
|
|
556
|
-
handles:
|
|
557
|
-
tags:
|
|
624
|
+
value: raced.serialized,
|
|
625
|
+
handles: raced.handles,
|
|
626
|
+
tags: raced.tags,
|
|
558
627
|
shouldRevalidate: false,
|
|
559
628
|
});
|
|
560
629
|
} catch (error) {
|
|
@@ -566,6 +635,8 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
|
|
|
566
635
|
// Fall through to a fresh execution below.
|
|
567
636
|
}
|
|
568
637
|
}
|
|
638
|
+
// raced === undefined: leader rejected; its map entry is already
|
|
639
|
+
// cleared, so fall through to a fresh run.
|
|
569
640
|
}
|
|
570
641
|
|
|
571
642
|
// This call becomes the leader. Register a deferred envelope so concurrent
|
|
@@ -582,9 +653,12 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
|
|
|
582
653
|
// Followers attach their own catch; guard the map's own reference so a
|
|
583
654
|
// rejected envelope with no waiter is not an unhandled rejection.
|
|
584
655
|
envelopePromise.catch(() => {});
|
|
585
|
-
inFlightExecutions.set(cacheKey,
|
|
656
|
+
inFlightExecutions.set(cacheKey, {
|
|
657
|
+
promise: envelopePromise,
|
|
658
|
+
registeredAt: Date.now(),
|
|
659
|
+
});
|
|
586
660
|
const clearSelf = (): void => {
|
|
587
|
-
if (inFlightExecutions.get(cacheKey) === envelopePromise) {
|
|
661
|
+
if (inFlightExecutions.get(cacheKey)?.promise === envelopePromise) {
|
|
588
662
|
inFlightExecutions.delete(cacheKey);
|
|
589
663
|
}
|
|
590
664
|
};
|
|
@@ -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
|