@rangojs/router 0.0.0-experimental.14 → 0.0.0-experimental.140
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/AGENTS.md +17 -0
- package/README.md +432 -7
- package/dist/bin/rango.js +2073 -213
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +7258 -2714
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +140 -67
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +329 -0
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +487 -0
- package/skills/caching/SKILL.md +357 -25
- package/skills/comparison/SKILL.md +50 -0
- package/skills/comparison/agents/openai.yaml +4 -0
- package/skills/comparison/references/framework-comparison.md +837 -0
- package/skills/composability/SKILL.md +246 -0
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +16 -10
- package/skills/document-cache/SKILL.md +87 -62
- package/skills/fonts/SKILL.md +6 -4
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +557 -79
- package/skills/host-router/SKILL.md +320 -0
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +207 -15
- package/skills/layout/SKILL.md +146 -6
- package/skills/links/SKILL.md +304 -25
- package/skills/loader/SKILL.md +616 -54
- package/skills/middleware/SKILL.md +217 -37
- package/skills/migrate-nextjs/SKILL.md +611 -0
- package/skills/migrate-react-router/SKILL.md +927 -0
- package/skills/mime-routes/SKILL.md +42 -11
- package/skills/observability/SKILL.md +194 -0
- package/skills/parallel/SKILL.md +284 -3
- package/skills/ppr/SKILL.md +426 -0
- package/skills/prerender/SKILL.md +437 -52
- package/skills/rango/SKILL.md +369 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +263 -121
- package/skills/route/SKILL.md +350 -21
- package/skills/router-setup/SKILL.md +246 -33
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +126 -222
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +131 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +122 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +85 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +123 -0
- package/skills/theme/SKILL.md +9 -8
- package/skills/typesafety/SKILL.md +532 -103
- package/skills/use-cache/SKILL.md +367 -0
- package/skills/vercel/SKILL.md +128 -0
- package/skills/view-transitions/SKILL.md +337 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +77 -44
- package/src/bin/rango.ts +312 -15
- package/src/browser/action-coordinator.ts +114 -0
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +293 -202
- package/src/browser/history-state.ts +101 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/link-interceptor.ts +24 -4
- package/src/browser/logging.ts +11 -0
- package/src/browser/merge-segment-loaders.ts +20 -12
- package/src/browser/navigation-bridge.ts +385 -576
- package/src/browser/navigation-client.ts +245 -75
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +184 -118
- package/src/browser/navigation-transaction.ts +247 -0
- package/src/browser/network-error-handler.ts +88 -0
- package/src/browser/partial-update.ts +412 -364
- package/src/browser/prefetch/cache.ts +359 -0
- package/src/browser/prefetch/fetch.ts +452 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +209 -0
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +194 -0
- package/src/browser/react/Link.tsx +275 -68
- package/src/browser/react/NavigationProvider.tsx +265 -109
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +11 -0
- package/src/browser/react/filter-segment-order.ts +70 -0
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +272 -60
- package/src/browser/react/location-state.ts +90 -20
- package/src/browser/react/mount-context.ts +6 -1
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +35 -66
- package/src/browser/react/use-handle.ts +39 -126
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +39 -13
- package/src/browser/react/use-navigation.ts +53 -69
- package/src/browser/react/use-params.ts +75 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +98 -0
- package/src/browser/react/use-search-params.ts +51 -0
- package/src/browser/react/use-segments.ts +72 -99
- package/src/browser/response-adapter.ts +164 -0
- package/src/browser/rsc-router.tsx +300 -72
- package/src/browser/scroll-restoration.ts +138 -50
- package/src/browser/segment-reconciler.ts +243 -0
- package/src/browser/segment-structure-assert.ts +17 -1
- package/src/browser/server-action-bridge.ts +668 -613
- package/src/browser/types.ts +223 -51
- package/src/browser/validate-redirect-origin.ts +56 -0
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +252 -161
- package/src/build/generate-route-types.ts +41 -1038
- package/src/build/index.ts +12 -7
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +225 -42
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +105 -0
- package/src/build/route-types/codegen.ts +113 -0
- package/src/build/route-types/include-resolution.ts +812 -0
- package/src/build/route-types/param-extraction.ts +51 -0
- package/src/build/route-types/per-module-writer.ts +144 -0
- package/src/build/route-types/router-processing.ts +695 -0
- package/src/build/route-types/scan-filter.ts +85 -0
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +223 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +60 -0
- package/src/cache/cache-policy.ts +199 -0
- package/src/cache/cache-runtime.ts +525 -0
- package/src/cache/cache-scope.ts +298 -332
- package/src/cache/cache-tag.ts +103 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +2500 -158
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +17 -17
- package/src/cache/document-cache.ts +199 -92
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +111 -0
- package/src/cache/index.ts +29 -35
- package/src/cache/memory-segment-store.ts +363 -30
- package/src/cache/profile-registry.ts +88 -0
- package/src/cache/read-through-swr.ts +178 -0
- package/src/cache/segment-codec.ts +248 -0
- package/src/cache/shell-cache.ts +386 -0
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/taint.ts +153 -0
- package/src/cache/types.ts +156 -211
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1102 -0
- package/src/client.rsc.tsx +43 -21
- package/src/client.tsx +131 -347
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +109 -0
- package/src/component-utils.ts +23 -4
- package/src/components/DefaultDocument.tsx +13 -3
- package/src/context-var.ts +168 -0
- package/src/debug.ts +19 -9
- package/src/decode-loader-results.ts +52 -0
- package/src/defer.ts +185 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +106 -10
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +110 -35
- package/src/handles/MetaTags.tsx +83 -59
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +93 -0
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +18 -0
- package/src/handles/meta.ts +44 -53
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +20 -65
- package/src/host/errors.ts +21 -30
- package/src/host/index.ts +13 -9
- package/src/host/pattern-matcher.ts +50 -79
- package/src/host/router.ts +151 -121
- package/src/host/testing.ts +45 -32
- package/src/host/types.ts +52 -11
- package/src/host/utils.ts +2 -2
- package/src/href-client.ts +192 -57
- package/src/index.rsc.ts +177 -35
- package/src/index.ts +255 -71
- package/src/internal-debug.ts +9 -2
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +31 -99
- package/src/loader.ts +30 -12
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +41 -0
- package/src/prerender/param-hash.ts +16 -14
- package/src/prerender/store.ts +121 -21
- package/src/prerender.ts +460 -26
- package/src/redirect-origin.ts +100 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +62 -0
- package/src/reverse.ts +198 -128
- package/src/root-error-boundary.tsx +42 -48
- package/src/route-content-wrapper.tsx +22 -77
- package/src/route-definition/dsl-helpers.ts +1116 -0
- package/src/route-definition/helper-factories.ts +88 -0
- package/src/route-definition/helpers-types.ts +505 -0
- package/src/route-definition/index.ts +54 -0
- package/src/route-definition/redirect.ts +134 -0
- package/src/route-definition/resolve-handler-use.ts +160 -0
- package/src/route-definition/use-item-types.ts +29 -0
- package/src/route-definition.ts +1 -1481
- package/src/route-map-builder.ts +82 -144
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +71 -45
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +263 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +54 -27
- package/src/router/find-match.ts +245 -0
- package/src/router/handler-context.ts +377 -125
- package/src/router/instrument.ts +350 -0
- package/src/router/intercept-resolution.ts +59 -28
- package/src/router/lazy-includes.ts +254 -0
- package/src/router/loader-resolution.ts +421 -157
- package/src/router/logging.ts +106 -6
- package/src/router/manifest.ts +131 -57
- package/src/router/match-api.ts +167 -246
- package/src/router/match-context.ts +4 -24
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +117 -93
- package/src/router/match-middleware/cache-lookup.ts +297 -150
- package/src/router/match-middleware/cache-store.ts +123 -51
- package/src/router/match-middleware/intercept-resolution.ts +44 -43
- package/src/router/match-middleware/segment-resolution.ts +64 -22
- package/src/router/match-pipelines.ts +11 -87
- package/src/router/match-result.ts +121 -50
- package/src/router/metrics.ts +219 -28
- package/src/router/middleware-types.ts +93 -0
- package/src/router/middleware.ts +505 -441
- package/src/router/navigation-snapshot.ts +133 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +311 -142
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +547 -0
- package/src/router/preview-match.ts +102 -0
- package/src/router/request-classification.ts +278 -0
- package/src/router/revalidation.ts +203 -62
- package/src/router/route-snapshot.ts +246 -0
- package/src/router/router-context.ts +45 -48
- package/src/router/router-interfaces.ts +554 -0
- package/src/router/router-options.ts +779 -0
- package/src/router/router-registry.ts +21 -0
- package/src/router/segment-resolution/fresh.ts +772 -0
- package/src/router/segment-resolution/helpers.ts +348 -0
- package/src/router/segment-resolution/loader-cache.ts +250 -0
- package/src/router/segment-resolution/loader-mask.ts +44 -0
- package/src/router/segment-resolution/revalidation.ts +1331 -0
- package/src/router/segment-resolution/static-store.ts +81 -0
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +56 -0
- package/src/router/segment-resolution.ts +25 -1354
- package/src/router/segment-wrappers.ts +292 -0
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +75 -0
- package/src/router/telemetry-otel.ts +261 -0
- package/src/router/telemetry.ts +377 -0
- package/src/router/timeout.ts +128 -0
- package/src/router/tracing.ts +206 -0
- package/src/router/trie-matching.ts +240 -61
- package/src/router/types.ts +23 -70
- package/src/router/url-params.ts +57 -0
- package/src/router.ts +781 -2378
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +46 -0
- package/src/rsc/handler.ts +905 -1142
- package/src/rsc/helpers.ts +275 -19
- package/src/rsc/index.ts +2 -25
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +305 -0
- package/src/rsc/manifest-init.ts +77 -0
- package/src/rsc/nonce.ts +14 -0
- package/src/rsc/origin-guard.ts +155 -0
- package/src/rsc/progressive-enhancement.ts +502 -0
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +104 -0
- package/src/rsc/response-route-handler.ts +257 -0
- package/src/rsc/rsc-rendering.ts +337 -0
- package/src/rsc/runtime-warnings.ts +55 -0
- package/src/rsc/server-action.ts +522 -0
- package/src/rsc/shell-capture.ts +439 -0
- package/src/rsc/ssr-setup.ts +144 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +95 -12
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +99 -82
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +149 -0
- package/src/segment-system.tsx +349 -134
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +452 -85
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +310 -0
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +123 -42
- package/src/server/live.ts +130 -0
- package/src/server/loader-registry.ts +51 -100
- package/src/server/request-context.ts +842 -157
- package/src/server.ts +15 -8
- package/src/ssr/index.tsx +412 -136
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/static-handler.ts +45 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +46 -0
- package/src/testing/dispatch.ts +701 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +128 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +257 -0
- package/src/testing/generated-routes.ts +199 -0
- package/src/testing/index.ts +105 -0
- package/src/testing/internal/context.ts +371 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +357 -0
- package/src/testing/render-route.tsx +584 -0
- package/src/testing/run-loader.ts +385 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/run-transition-when.ts +164 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +305 -0
- package/src/theme/ThemeProvider.tsx +40 -72
- package/src/theme/ThemeScript.tsx +12 -14
- package/src/theme/constants.ts +57 -15
- package/src/theme/index.ts +3 -20
- package/src/theme/theme-context.ts +5 -35
- package/src/theme/theme-script.ts +43 -39
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +123 -0
- package/src/types/cache-types.ts +207 -0
- package/src/types/error-types.ts +132 -0
- package/src/types/global-namespace.ts +113 -0
- package/src/types/handler-context.ts +839 -0
- package/src/types/index.ts +81 -0
- package/src/types/loader-types.ts +212 -0
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +138 -0
- package/src/types/route-entry.ts +114 -0
- package/src/types/segments.ts +271 -0
- package/src/types.ts +1 -1795
- package/src/urls/include-helper.ts +162 -0
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +43 -0
- package/src/urls/path-helper-types.ts +413 -0
- package/src/urls/path-helper.ts +275 -0
- package/src/urls/pattern-types.ts +124 -0
- package/src/urls/response-types.ts +109 -0
- package/src/urls/type-extraction.ts +316 -0
- package/src/urls/urls-function.ts +80 -0
- package/src/urls.ts +1 -1341
- package/src/use-loader.tsx +406 -141
- package/src/vercel/index.ts +11 -0
- package/src/vercel/tracing.ts +88 -0
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +182 -0
- package/src/vite/discovery/discover-routers.ts +389 -0
- package/src/vite/discovery/discovery-errors.ts +255 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +467 -0
- package/src/vite/discovery/route-types-writer.ts +214 -0
- package/src/vite/discovery/self-gen-tracking.ts +73 -0
- package/src/vite/discovery/state.ts +161 -0
- package/src/vite/discovery/virtual-module-codegen.ts +183 -0
- package/src/vite/index.ts +23 -2255
- package/src/vite/inject-client-debug.ts +36 -0
- package/src/vite/plugin-types.ts +303 -0
- package/src/vite/plugins/cjs-to-esm.ts +90 -0
- package/src/vite/plugins/client-ref-dedup.ts +120 -0
- package/src/vite/plugins/client-ref-hashing.ts +118 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
- package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +88 -110
- package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +89 -79
- package/src/vite/plugins/expose-ids/export-analysis.ts +363 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +130 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +64 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +199 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +805 -0
- package/src/vite/plugins/performance-tracks.ts +89 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -0
- package/src/vite/plugins/use-cache-transform.ts +313 -0
- package/src/vite/plugins/vercel-output.ts +384 -0
- package/src/vite/plugins/version-injector.ts +94 -0
- package/src/vite/plugins/version-plugin.ts +263 -0
- package/src/vite/plugins/virtual-entries.ts +234 -0
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +560 -0
- package/src/vite/router-discovery.ts +1638 -0
- package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +200 -37
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +132 -0
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +15 -0
- package/src/vite/utils/package-resolution.ts +89 -0
- package/src/vite/utils/prerender-utils.ts +249 -0
- package/src/vite/utils/shared-utils.ts +269 -0
- package/CLAUDE.md +0 -43
- package/dist/vite/index.named-routes.gen.ts +0 -103
- package/src/browser/lru-cache.ts +0 -69
- package/src/browser/react/use-client-cache.ts +0 -56
- package/src/browser/request-controller.ts +0 -164
- package/src/browser/shallow.ts +0 -35
- package/src/cache/memory-store.ts +0 -253
- package/src/handles/index.ts +0 -6
- package/src/href-context.ts +0 -33
- package/src/network-error-thrower.tsx +0 -21
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- package/src/vite/expose-internal-ids.ts +0 -1167
- package/src/vite/package-resolution.ts +0 -125
- package/src/vite/virtual-entries.ts +0 -114
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
|
@@ -5,15 +5,101 @@
|
|
|
5
5
|
* Uses globalThis to survive HMR in development.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type {
|
|
8
|
+
import type {
|
|
9
|
+
SegmentCacheStore,
|
|
10
|
+
CachedEntryData,
|
|
11
|
+
CacheDefaults,
|
|
12
|
+
CacheGetResult,
|
|
13
|
+
CacheItemResult,
|
|
14
|
+
CacheItemOptions,
|
|
15
|
+
ShellCacheEntry,
|
|
16
|
+
} from "./types.js";
|
|
9
17
|
import type { RequestContext } from "../server/request-context.js";
|
|
18
|
+
import { isPerClientSignalHeader } from "../browser/cookie-name.js";
|
|
19
|
+
import {
|
|
20
|
+
resolveTtl,
|
|
21
|
+
resolveSwrWindow,
|
|
22
|
+
computeExpiration,
|
|
23
|
+
DEFAULT_FUNCTION_TTL,
|
|
24
|
+
} from "./cache-policy.js";
|
|
25
|
+
import { reportCacheError } from "./cache-error.js";
|
|
10
26
|
|
|
11
|
-
const
|
|
27
|
+
const CACHE_REGISTRY_KEY = "__rsc_router_segment_cache_registry__";
|
|
28
|
+
const RESPONSE_CACHE_REGISTRY_KEY = "__rsc_router_response_cache_registry__";
|
|
29
|
+
const ITEM_CACHE_REGISTRY_KEY = "__rsc_router_item_cache_registry__";
|
|
30
|
+
const SHELL_CACHE_REGISTRY_KEY = "__rsc_router_shell_cache_registry__";
|
|
31
|
+
const TAG_INDEX_REGISTRY_KEY = "__rsc_router_tag_index_registry__";
|
|
32
|
+
const KEY_TAGS_REGISTRY_KEY = "__rsc_router_key_tags_registry__";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get or create a named Map from a globalThis-backed registry.
|
|
36
|
+
* The registry survives HMR; individual stores are keyed by name.
|
|
37
|
+
*/
|
|
38
|
+
function getNamedMap<V>(registryKey: string, name: string): Map<string, V> {
|
|
39
|
+
let registry = (globalThis as any)[registryKey] as
|
|
40
|
+
| Map<string, Map<string, V>>
|
|
41
|
+
| undefined;
|
|
42
|
+
if (!registry) {
|
|
43
|
+
registry = new Map();
|
|
44
|
+
(globalThis as any)[registryKey] = registry;
|
|
45
|
+
}
|
|
46
|
+
let map = registry.get(name);
|
|
47
|
+
if (!map) {
|
|
48
|
+
map = new Map<string, V>();
|
|
49
|
+
registry.set(name, map);
|
|
50
|
+
}
|
|
51
|
+
return map;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface CachedResponseEntry {
|
|
55
|
+
body: ArrayBuffer;
|
|
56
|
+
status: number;
|
|
57
|
+
headers: [string, string][];
|
|
58
|
+
expiresAt: number;
|
|
59
|
+
staleAt: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface CachedItemEntry {
|
|
63
|
+
value: string;
|
|
64
|
+
handles?: string;
|
|
65
|
+
expiresAt: number;
|
|
66
|
+
staleAt: number;
|
|
67
|
+
tags?: string[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface CachedShellEntry {
|
|
71
|
+
entry: ShellCacheEntry;
|
|
72
|
+
expiresAt: number;
|
|
73
|
+
staleAt: number;
|
|
74
|
+
tags?: string[];
|
|
75
|
+
}
|
|
12
76
|
|
|
13
77
|
/**
|
|
14
78
|
* Options for MemorySegmentCacheStore
|
|
15
79
|
*/
|
|
16
80
|
export interface MemorySegmentCacheStoreOptions<TEnv = unknown> {
|
|
81
|
+
/**
|
|
82
|
+
* Optional name for this store instance. Named stores persist their
|
|
83
|
+
* backing Map on globalThis so data survives Vite HMR module reloads.
|
|
84
|
+
* Stores with different names get separate Maps.
|
|
85
|
+
*
|
|
86
|
+
* When omitted, the store uses a plain instance-level Map with no
|
|
87
|
+
* globalThis sharing, which is the safest default for isolation.
|
|
88
|
+
*
|
|
89
|
+
* Caveat: two instances constructed with the SAME name share all backing maps
|
|
90
|
+
* (data + tag index), but each keeps its OWN `defaults` and `keyGenerator` from
|
|
91
|
+
* its options - those are not shared. Use one instance per name, or keep the
|
|
92
|
+
* options identical, to avoid surprising divergence.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* // Two named stores are isolated from each other
|
|
97
|
+
* const fast = new MemorySegmentCacheStore({ name: "fast", defaults: { ttl: 10 } });
|
|
98
|
+
* const slow = new MemorySegmentCacheStore({ name: "slow", defaults: { ttl: 300 } });
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
name?: string;
|
|
102
|
+
|
|
17
103
|
/**
|
|
18
104
|
* Default cache options for cache() boundaries.
|
|
19
105
|
* When cache() is called without explicit ttl/swr,
|
|
@@ -35,14 +121,14 @@ export interface MemorySegmentCacheStoreOptions<TEnv = unknown> {
|
|
|
35
121
|
* @example
|
|
36
122
|
* ```typescript
|
|
37
123
|
* keyGenerator: (ctx, defaultKey) => {
|
|
38
|
-
* const locale =
|
|
124
|
+
* const locale = cookies().get('locale')?.value || 'en';
|
|
39
125
|
* return `${locale}:${defaultKey}`;
|
|
40
126
|
* }
|
|
41
127
|
* ```
|
|
42
128
|
*/
|
|
43
129
|
keyGenerator?: (
|
|
44
130
|
ctx: RequestContext<TEnv>,
|
|
45
|
-
defaultKey: string
|
|
131
|
+
defaultKey: string,
|
|
46
132
|
) => string | Promise<string>;
|
|
47
133
|
}
|
|
48
134
|
|
|
@@ -53,6 +139,11 @@ export interface MemorySegmentCacheStoreOptions<TEnv = unknown> {
|
|
|
53
139
|
* For production with multiple instances, use a distributed store
|
|
54
140
|
* like Cloudflare KV or Redis.
|
|
55
141
|
*
|
|
142
|
+
* Tag-index cleanup is lazy, mirroring the data maps: a tagged entry that
|
|
143
|
+
* expires but is never re-read or invalidated leaves its forward+reverse index
|
|
144
|
+
* entries resident until the key is reused or invalidated. This is bounded by
|
|
145
|
+
* the distinct-tag count and acceptable for a dev/single-instance store.
|
|
146
|
+
*
|
|
56
147
|
* @example
|
|
57
148
|
* ```typescript
|
|
58
149
|
* // Basic usage
|
|
@@ -69,19 +160,57 @@ export interface MemorySegmentCacheStoreOptions<TEnv = unknown> {
|
|
|
69
160
|
* })
|
|
70
161
|
* ```
|
|
71
162
|
*/
|
|
72
|
-
export class MemorySegmentCacheStore<
|
|
163
|
+
export class MemorySegmentCacheStore<
|
|
164
|
+
TEnv = unknown,
|
|
165
|
+
> implements SegmentCacheStore<TEnv> {
|
|
73
166
|
private cache: Map<string, CachedEntryData>;
|
|
167
|
+
private responseCache: Map<string, CachedResponseEntry>;
|
|
168
|
+
private itemCache: Map<string, CachedItemEntry>;
|
|
169
|
+
private shellCache: Map<string, CachedShellEntry>;
|
|
170
|
+
/** tag -> set of prefixed cache keys (seg:key, res:key, item:key, shell:key) */
|
|
171
|
+
private tagIndex: Map<string, Set<string>>;
|
|
172
|
+
/** prefixed cache key -> set of tags (reverse index for O(tags) unregister) */
|
|
173
|
+
private keyTags: Map<string, Set<string>>;
|
|
74
174
|
readonly defaults?: CacheDefaults;
|
|
75
175
|
readonly keyGenerator?: (
|
|
76
176
|
ctx: RequestContext<TEnv>,
|
|
77
|
-
defaultKey: string
|
|
177
|
+
defaultKey: string,
|
|
78
178
|
) => string | Promise<string>;
|
|
79
179
|
|
|
80
180
|
constructor(options?: MemorySegmentCacheStoreOptions<TEnv>) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
181
|
+
if (options?.name != null) {
|
|
182
|
+
this.cache = getNamedMap<CachedEntryData>(
|
|
183
|
+
CACHE_REGISTRY_KEY,
|
|
184
|
+
options.name,
|
|
185
|
+
);
|
|
186
|
+
this.responseCache = getNamedMap<CachedResponseEntry>(
|
|
187
|
+
RESPONSE_CACHE_REGISTRY_KEY,
|
|
188
|
+
options.name,
|
|
189
|
+
);
|
|
190
|
+
this.itemCache = getNamedMap<CachedItemEntry>(
|
|
191
|
+
ITEM_CACHE_REGISTRY_KEY,
|
|
192
|
+
options.name,
|
|
193
|
+
);
|
|
194
|
+
this.shellCache = getNamedMap<CachedShellEntry>(
|
|
195
|
+
SHELL_CACHE_REGISTRY_KEY,
|
|
196
|
+
options.name,
|
|
197
|
+
);
|
|
198
|
+
this.tagIndex = getNamedMap<Set<string>>(
|
|
199
|
+
TAG_INDEX_REGISTRY_KEY,
|
|
200
|
+
options.name,
|
|
201
|
+
);
|
|
202
|
+
this.keyTags = getNamedMap<Set<string>>(
|
|
203
|
+
KEY_TAGS_REGISTRY_KEY,
|
|
204
|
+
options.name,
|
|
205
|
+
);
|
|
206
|
+
} else {
|
|
207
|
+
this.cache = new Map<string, CachedEntryData>();
|
|
208
|
+
this.responseCache = new Map<string, CachedResponseEntry>();
|
|
209
|
+
this.itemCache = new Map<string, CachedItemEntry>();
|
|
210
|
+
this.shellCache = new Map<string, CachedShellEntry>();
|
|
211
|
+
this.tagIndex = new Map<string, Set<string>>();
|
|
212
|
+
this.keyTags = new Map<string, Set<string>>();
|
|
213
|
+
}
|
|
85
214
|
this.defaults = options?.defaults;
|
|
86
215
|
this.keyGenerator = options?.keyGenerator;
|
|
87
216
|
}
|
|
@@ -95,6 +224,7 @@ export class MemorySegmentCacheStore<TEnv = unknown> implements SegmentCacheStor
|
|
|
95
224
|
|
|
96
225
|
// Check expiration
|
|
97
226
|
if (Date.now() > cached.expiresAt) {
|
|
227
|
+
this.unregisterTags(`seg:${key}`);
|
|
98
228
|
this.cache.delete(key);
|
|
99
229
|
return null;
|
|
100
230
|
}
|
|
@@ -103,28 +233,238 @@ export class MemorySegmentCacheStore<TEnv = unknown> implements SegmentCacheStor
|
|
|
103
233
|
return { data: cached, shouldRevalidate: false };
|
|
104
234
|
}
|
|
105
235
|
|
|
106
|
-
async set(
|
|
107
|
-
|
|
108
|
-
|
|
236
|
+
async set(
|
|
237
|
+
key: string,
|
|
238
|
+
data: CachedEntryData,
|
|
239
|
+
ttl: number,
|
|
240
|
+
_swr?: number,
|
|
241
|
+
): Promise<void> {
|
|
109
242
|
const entry: CachedEntryData = {
|
|
110
243
|
...data,
|
|
111
244
|
expiresAt: Date.now() + ttl * 1000,
|
|
112
245
|
};
|
|
246
|
+
const prefixedKey = `seg:${key}`;
|
|
247
|
+
this.unregisterTags(prefixedKey);
|
|
113
248
|
this.cache.set(key, entry);
|
|
249
|
+
if (data.tags && data.tags.length > 0) {
|
|
250
|
+
this.registerTags(data.tags, prefixedKey);
|
|
251
|
+
}
|
|
114
252
|
}
|
|
115
253
|
|
|
116
254
|
async delete(key: string): Promise<boolean> {
|
|
255
|
+
this.unregisterTags(`seg:${key}`);
|
|
117
256
|
return this.cache.delete(key);
|
|
118
257
|
}
|
|
119
258
|
|
|
120
259
|
async clear(): Promise<void> {
|
|
121
260
|
this.cache.clear();
|
|
261
|
+
this.responseCache.clear();
|
|
262
|
+
this.itemCache.clear();
|
|
263
|
+
this.shellCache.clear();
|
|
264
|
+
this.tagIndex.clear();
|
|
265
|
+
this.keyTags.clear();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async getResponse(
|
|
269
|
+
key: string,
|
|
270
|
+
): Promise<{ response: Response; shouldRevalidate: boolean } | null> {
|
|
271
|
+
const cached = this.responseCache.get(key);
|
|
272
|
+
if (!cached) return null;
|
|
273
|
+
|
|
274
|
+
if (Date.now() > cached.expiresAt) {
|
|
275
|
+
this.unregisterTags(`res:${key}`);
|
|
276
|
+
this.responseCache.delete(key);
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const isStale = cached.staleAt > 0 && Date.now() > cached.staleAt;
|
|
281
|
+
const headers = new Headers(cached.headers);
|
|
282
|
+
return {
|
|
283
|
+
response: new Response(cached.body, {
|
|
284
|
+
status: cached.status,
|
|
285
|
+
headers,
|
|
286
|
+
}),
|
|
287
|
+
shouldRevalidate: isStale,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async putResponse(
|
|
292
|
+
key: string,
|
|
293
|
+
response: Response,
|
|
294
|
+
ttl: number,
|
|
295
|
+
swr?: number,
|
|
296
|
+
tags?: string[],
|
|
297
|
+
): Promise<void> {
|
|
298
|
+
try {
|
|
299
|
+
const body = await response.clone().arrayBuffer();
|
|
300
|
+
const headers: [string, string][] = [];
|
|
301
|
+
response.headers.forEach((value, name) => {
|
|
302
|
+
if (isPerClientSignalHeader(name)) return;
|
|
303
|
+
headers.push([name, value]);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const swrWindow = resolveSwrWindow(swr, this.defaults);
|
|
307
|
+
const { staleAt, expiresAt } = computeExpiration(ttl, swrWindow);
|
|
308
|
+
|
|
309
|
+
const prefixedKey = `res:${key}`;
|
|
310
|
+
this.unregisterTags(prefixedKey);
|
|
311
|
+
this.responseCache.set(key, {
|
|
312
|
+
body,
|
|
313
|
+
status: response.status,
|
|
314
|
+
headers,
|
|
315
|
+
expiresAt,
|
|
316
|
+
staleAt,
|
|
317
|
+
});
|
|
318
|
+
if (tags && tags.length > 0) {
|
|
319
|
+
this.registerTags(tags, prefixedKey);
|
|
320
|
+
}
|
|
321
|
+
} catch (error) {
|
|
322
|
+
reportCacheError(
|
|
323
|
+
error,
|
|
324
|
+
"cache-write",
|
|
325
|
+
"[MemorySegmentCacheStore] putResponse",
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async getItem(key: string): Promise<CacheItemResult | null> {
|
|
331
|
+
const cached = this.itemCache.get(key);
|
|
332
|
+
if (!cached) return null;
|
|
333
|
+
|
|
334
|
+
const now = Date.now();
|
|
335
|
+
if (now > cached.expiresAt) {
|
|
336
|
+
this.unregisterTags(`item:${key}`);
|
|
337
|
+
this.itemCache.delete(key);
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const isStale = now > cached.staleAt;
|
|
342
|
+
return {
|
|
343
|
+
value: cached.value,
|
|
344
|
+
handles: cached.handles,
|
|
345
|
+
shouldRevalidate: isStale,
|
|
346
|
+
tags: cached.tags,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
async setItem(
|
|
351
|
+
key: string,
|
|
352
|
+
value: string,
|
|
353
|
+
options?: CacheItemOptions,
|
|
354
|
+
): Promise<void> {
|
|
355
|
+
const ttl = resolveTtl(options?.ttl, this.defaults, DEFAULT_FUNCTION_TTL);
|
|
356
|
+
const swrWindow = resolveSwrWindow(options?.swr, this.defaults);
|
|
357
|
+
const { staleAt, expiresAt } = computeExpiration(ttl, swrWindow);
|
|
358
|
+
const prefixedKey = `item:${key}`;
|
|
359
|
+
this.unregisterTags(prefixedKey);
|
|
360
|
+
this.itemCache.set(key, {
|
|
361
|
+
value,
|
|
362
|
+
handles: options?.handles,
|
|
363
|
+
expiresAt,
|
|
364
|
+
staleAt,
|
|
365
|
+
tags: options?.tags,
|
|
366
|
+
});
|
|
367
|
+
if (options?.tags && options.tags.length > 0) {
|
|
368
|
+
this.registerTags(options.tags, prefixedKey);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
async getShell(
|
|
373
|
+
key: string,
|
|
374
|
+
): Promise<{ entry: ShellCacheEntry; shouldRevalidate?: boolean } | null> {
|
|
375
|
+
const cached = this.shellCache.get(key);
|
|
376
|
+
if (!cached) return null;
|
|
377
|
+
|
|
378
|
+
const now = Date.now();
|
|
379
|
+
if (now > cached.expiresAt) {
|
|
380
|
+
this.unregisterTags(`shell:${key}`);
|
|
381
|
+
this.shellCache.delete(key);
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// SWR mirrors the item family: stale within the swr window still serves, and
|
|
386
|
+
// signals shouldRevalidate so the middleware schedules a background recapture.
|
|
387
|
+
const shouldRevalidate = cached.staleAt > 0 && now > cached.staleAt;
|
|
388
|
+
return { entry: cached.entry, shouldRevalidate };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
async putShell(
|
|
392
|
+
key: string,
|
|
393
|
+
entry: ShellCacheEntry,
|
|
394
|
+
ttlSeconds?: number,
|
|
395
|
+
swrSeconds?: number,
|
|
396
|
+
tags?: string[],
|
|
397
|
+
): Promise<void> {
|
|
398
|
+
const ttl = resolveTtl(ttlSeconds, this.defaults, DEFAULT_FUNCTION_TTL);
|
|
399
|
+
const swrWindow = resolveSwrWindow(swrSeconds, this.defaults);
|
|
400
|
+
const { staleAt, expiresAt } = computeExpiration(ttl, swrWindow);
|
|
401
|
+
const prefixedKey = `shell:${key}`;
|
|
402
|
+
this.unregisterTags(prefixedKey);
|
|
403
|
+
this.shellCache.set(key, { entry, expiresAt, staleAt, tags });
|
|
404
|
+
if (tags && tags.length > 0) {
|
|
405
|
+
this.registerTags(tags, prefixedKey);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async invalidateTags(tags: string[]): Promise<void> {
|
|
410
|
+
for (const tag of tags) {
|
|
411
|
+
const keys = this.tagIndex.get(tag);
|
|
412
|
+
if (!keys || keys.size === 0) continue;
|
|
413
|
+
|
|
414
|
+
const prefixedKeys = [...keys];
|
|
415
|
+
|
|
416
|
+
for (const prefixedKey of prefixedKeys) {
|
|
417
|
+
const colonIdx = prefixedKey.indexOf(":");
|
|
418
|
+
const prefix = prefixedKey.slice(0, colonIdx);
|
|
419
|
+
const rawKey = prefixedKey.slice(colonIdx + 1);
|
|
420
|
+
|
|
421
|
+
if (prefix === "seg") {
|
|
422
|
+
this.cache.delete(rawKey);
|
|
423
|
+
} else if (prefix === "res") {
|
|
424
|
+
this.responseCache.delete(rawKey);
|
|
425
|
+
} else if (prefix === "item") {
|
|
426
|
+
this.itemCache.delete(rawKey);
|
|
427
|
+
} else if (prefix === "shell") {
|
|
428
|
+
this.shellCache.delete(rawKey);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
this.unregisterTags(prefixedKey);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
private registerTags(tags: string[], prefixedKey: string): void {
|
|
437
|
+
let tagSet = this.keyTags.get(prefixedKey);
|
|
438
|
+
if (!tagSet) {
|
|
439
|
+
tagSet = new Set();
|
|
440
|
+
this.keyTags.set(prefixedKey, tagSet);
|
|
441
|
+
}
|
|
442
|
+
for (const tag of tags) {
|
|
443
|
+
tagSet.add(tag);
|
|
444
|
+
let keys = this.tagIndex.get(tag);
|
|
445
|
+
if (!keys) {
|
|
446
|
+
keys = new Set();
|
|
447
|
+
this.tagIndex.set(tag, keys);
|
|
448
|
+
}
|
|
449
|
+
keys.add(prefixedKey);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
private unregisterTags(prefixedKey: string): void {
|
|
454
|
+
const tagSet = this.keyTags.get(prefixedKey);
|
|
455
|
+
if (!tagSet) return;
|
|
456
|
+
for (const tag of tagSet) {
|
|
457
|
+
const keys = this.tagIndex.get(tag);
|
|
458
|
+
if (keys) {
|
|
459
|
+
keys.delete(prefixedKey);
|
|
460
|
+
if (keys.size === 0) {
|
|
461
|
+
this.tagIndex.delete(tag);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
this.keyTags.delete(prefixedKey);
|
|
122
466
|
}
|
|
123
467
|
|
|
124
|
-
/**
|
|
125
|
-
* Get cache statistics for debugging purposes.
|
|
126
|
-
* @internal
|
|
127
|
-
*/
|
|
128
468
|
getStats(): { size: number; keys: string[] } {
|
|
129
469
|
return {
|
|
130
470
|
size: this.cache.size,
|
|
@@ -132,19 +472,12 @@ export class MemorySegmentCacheStore<TEnv = unknown> implements SegmentCacheStor
|
|
|
132
472
|
};
|
|
133
473
|
}
|
|
134
474
|
|
|
135
|
-
/**
|
|
136
|
-
* Reset the global cache state.
|
|
137
|
-
* Useful for test isolation - call this in beforeEach to ensure
|
|
138
|
-
* tests don't share cache state via globalThis.
|
|
139
|
-
*
|
|
140
|
-
* @example
|
|
141
|
-
* ```typescript
|
|
142
|
-
* beforeEach(() => {
|
|
143
|
-
* MemorySegmentCacheStore.resetGlobalCache();
|
|
144
|
-
* });
|
|
145
|
-
* ```
|
|
146
|
-
*/
|
|
147
475
|
static resetGlobalCache(): void {
|
|
148
|
-
delete (globalThis as any)[
|
|
476
|
+
delete (globalThis as any)[CACHE_REGISTRY_KEY];
|
|
477
|
+
delete (globalThis as any)[RESPONSE_CACHE_REGISTRY_KEY];
|
|
478
|
+
delete (globalThis as any)[ITEM_CACHE_REGISTRY_KEY];
|
|
479
|
+
delete (globalThis as any)[SHELL_CACHE_REGISTRY_KEY];
|
|
480
|
+
delete (globalThis as any)[TAG_INDEX_REGISTRY_KEY];
|
|
481
|
+
delete (globalThis as any)[KEY_TAGS_REGISTRY_KEY];
|
|
149
482
|
}
|
|
150
483
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache profile resolution.
|
|
3
|
+
*
|
|
4
|
+
* Named cache profiles for the "use cache" directive define TTL, SWR, and
|
|
5
|
+
* optional default tags. createRouter() resolves the user's profiles once via
|
|
6
|
+
* resolveCacheProfiles() and threads the resulting map onto each request
|
|
7
|
+
* context; the "use cache: <profile>" runtime path reads it from there
|
|
8
|
+
* (request-scoped) — there is no global registry.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export interface CacheProfile {
|
|
12
|
+
/** Time-to-live in seconds */
|
|
13
|
+
ttl: number;
|
|
14
|
+
/** Stale-while-revalidate window in seconds */
|
|
15
|
+
swr?: number;
|
|
16
|
+
/** Default cache tags for invalidation */
|
|
17
|
+
tags?: string[];
|
|
18
|
+
/**
|
|
19
|
+
* When true, a stale entry encountered during a server action's revalidation
|
|
20
|
+
* render is re-executed in the FOREGROUND (the action response reflects the
|
|
21
|
+
* refreshed value) instead of being served stale + revalidated in the
|
|
22
|
+
* background. Only the store write is deferred. Use this for mutation-related
|
|
23
|
+
* cached data that an action should refresh immediately; ordinary navigations
|
|
24
|
+
* always keep SWR. Default false: incidental TTL staleness must not turn an
|
|
25
|
+
* unrelated action into a synchronous cache-refresh barrier — for strong
|
|
26
|
+
* read-your-own-writes after a mutation, prefer updateTag() (a hard purge, so
|
|
27
|
+
* the action's own re-render is a fresh foreground miss).
|
|
28
|
+
*
|
|
29
|
+
* Honored only by `"use cache"` functions; route-level `cache()` and cached
|
|
30
|
+
* loaders ignore it (use updateTag() there).
|
|
31
|
+
*/
|
|
32
|
+
foregroundOnAction?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const DEFAULT_PROFILE: CacheProfile = { ttl: 900, swr: 1800 };
|
|
36
|
+
|
|
37
|
+
const PROFILE_NAME_RE = /^[a-zA-Z0-9_-]+$/;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Validate and merge user profiles with the default profile.
|
|
41
|
+
* Returns a new object suitable for both DSL-time and request-scoped use.
|
|
42
|
+
*
|
|
43
|
+
* Used by createRouter() to compute the resolved profile map once,
|
|
44
|
+
* stored on the router instance and passed to every request context.
|
|
45
|
+
*/
|
|
46
|
+
export function resolveCacheProfiles(
|
|
47
|
+
profiles?: Record<string, CacheProfile>,
|
|
48
|
+
): Record<string, CacheProfile> {
|
|
49
|
+
const merged: Record<string, CacheProfile> = {
|
|
50
|
+
default: DEFAULT_PROFILE,
|
|
51
|
+
};
|
|
52
|
+
if (profiles) {
|
|
53
|
+
for (const name of Object.keys(profiles)) {
|
|
54
|
+
if (!PROFILE_NAME_RE.test(name)) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Invalid cache profile name "${name}". ` +
|
|
57
|
+
`Profile names must match [a-zA-Z0-9_-]+.`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
const profile = profiles[name];
|
|
61
|
+
// Validate ttl/swr VALUES, not just the name. An unvalidated NaN/Infinity
|
|
62
|
+
// ttl flows into computeExpiration -> staleAt/expiresAt = NaN, and every
|
|
63
|
+
// expiry check (`now > NaN`) is false, so the entry never evicts and never
|
|
64
|
+
// revalidates: it is served fresh forever and accumulates unbounded. A
|
|
65
|
+
// negative ttl makes every read a guaranteed miss. This guard's policy is
|
|
66
|
+
// to FAIL FAST at config time on any non-finite/negative ttl — distinct
|
|
67
|
+
// from prefetch-cache-ttl.ts (falls back to the default) and defer.ts
|
|
68
|
+
// (treats Infinity as an intentional disable). Do not conflate them.
|
|
69
|
+
if (!Number.isFinite(profile.ttl) || profile.ttl < 0) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`Invalid cache profile "${name}": ttl must be a finite non-negative ` +
|
|
72
|
+
`number (got ${profile.ttl}).`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
if (
|
|
76
|
+
profile.swr !== undefined &&
|
|
77
|
+
(!Number.isFinite(profile.swr) || profile.swr < 0)
|
|
78
|
+
) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Invalid cache profile "${name}": swr must be a finite non-negative ` +
|
|
81
|
+
`number (got ${profile.swr}).`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
merged[name] = profile;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return merged;
|
|
88
|
+
}
|