@rangojs/router 0.0.0-experimental.bd6e11bc → 0.0.0-experimental.bdaf10aa
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 +8 -4
- package/README.md +296 -887
- package/dist/bin/rango.js +459 -91
- package/dist/testing/vitest.js +36 -2
- package/dist/vite/index.js +1708 -414
- package/package.json +35 -10
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +82 -5
- package/skills/bundle-analysis/SKILL.md +2 -2
- package/skills/cache-guide/SKILL.md +14 -9
- package/skills/caching/SKILL.md +221 -12
- package/skills/catalog.json +271 -0
- 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 +83 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +5 -3
- package/skills/defer-hydration/SKILL.md +235 -0
- package/skills/document-cache/SKILL.md +11 -3
- package/skills/fonts/SKILL.md +1 -1
- package/skills/handler-use/SKILL.md +9 -9
- package/skills/hooks/SKILL.md +73 -900
- package/skills/hooks/data.md +273 -0
- package/skills/hooks/handle-and-actions.md +103 -0
- package/skills/hooks/navigation.md +110 -0
- package/skills/hooks/outlets.md +41 -0
- package/skills/hooks/state.md +228 -0
- package/skills/hooks/urls.md +135 -0
- package/skills/host-router/SKILL.md +84 -7
- package/skills/i18n/SKILL.md +1 -1
- package/skills/intercept/SKILL.md +51 -17
- package/skills/layout/SKILL.md +38 -16
- package/skills/links/SKILL.md +1 -1
- package/skills/loader/SKILL.md +48 -20
- package/skills/middleware/SKILL.md +11 -5
- package/skills/migrate-nextjs/SKILL.md +203 -20
- package/skills/migrate-react-router/SKILL.md +59 -675
- package/skills/migrate-react-router/cloudflare-workers.md +129 -0
- package/skills/migrate-react-router/component-migration.md +196 -0
- package/skills/migrate-react-router/data-and-actions.md +225 -0
- package/skills/migrate-react-router/route-mapping.md +271 -0
- package/skills/mime-routes/SKILL.md +3 -3
- package/skills/observability/SKILL.md +70 -5
- package/skills/parallel/SKILL.md +32 -8
- package/skills/ppr/SKILL.md +622 -0
- package/skills/prerender/SKILL.md +59 -28
- package/skills/rango/SKILL.md +124 -50
- package/skills/response-routes/SKILL.md +78 -46
- package/skills/route/SKILL.md +85 -6
- package/skills/router-setup/SKILL.md +41 -6
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +28 -3
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +1 -1
- package/skills/tailwind/SKILL.md +28 -4
- package/skills/testing/SKILL.md +68 -654
- 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 +1 -1
- package/skills/typesafety/SKILL.md +45 -918
- package/skills/typesafety/env-and-bindings.md +254 -0
- package/skills/typesafety/generated-files-and-cli.md +335 -0
- package/skills/typesafety/params-and-search.md +153 -0
- package/skills/typesafety/route-types.md +209 -0
- package/skills/use-cache/SKILL.md +47 -17
- package/skills/vercel/SKILL.md +128 -0
- package/skills/view-transitions/SKILL.md +44 -1
- package/src/__augment-tests__/augmented.check.ts +2 -3
- package/src/__internal.ts +0 -65
- package/src/browser/action-coordinator.ts +1 -1
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +14 -27
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +178 -100
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/logging.ts +28 -0
- package/src/browser/merge-segment-loaders.ts +6 -4
- package/src/browser/navigation-bridge.ts +81 -68
- package/src/browser/navigation-client.ts +115 -70
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +153 -88
- package/src/browser/navigation-transaction.ts +0 -32
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +157 -144
- package/src/browser/prefetch/cache.ts +148 -81
- package/src/browser/prefetch/fetch.ts +231 -51
- package/src/browser/prefetch/queue.ts +25 -7
- package/src/browser/rango-state.ts +157 -115
- package/src/browser/react/Link.tsx +40 -7
- package/src/browser/react/NavigationProvider.tsx +140 -99
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/filter-segment-order.ts +17 -2
- package/src/browser/react/index.ts +0 -51
- package/src/browser/react/location-state-shared.ts +14 -15
- package/src/browser/react/location-state.ts +0 -1
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +0 -5
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +10 -5
- package/src/browser/react/use-params.ts +0 -2
- package/src/browser/react/use-router.ts +6 -4
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +0 -13
- package/src/browser/response-adapter.ts +74 -8
- package/src/browser/rsc-router.tsx +97 -22
- package/src/browser/scroll-restoration.ts +15 -8
- package/src/browser/segment-reconciler.ts +31 -21
- package/src/browser/server-action-bridge.ts +216 -38
- package/src/browser/types.ts +94 -22
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/generate-manifest.ts +155 -131
- package/src/build/generate-route-types.ts +1 -1
- package/src/build/index.ts +11 -5
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +152 -22
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +12 -1
- package/src/build/route-types/include-resolution.ts +455 -61
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +15 -2
- package/src/build/route-types/router-processing.ts +77 -41
- package/src/build/route-types/source-scan.ts +105 -7
- package/src/build/runtime-discovery.ts +4 -1
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +58 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +454 -101
- package/src/cache/cache-scope.ts +159 -54
- package/src/cache/cache-tag.ts +149 -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 +2170 -377
- 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 +6 -16
- package/src/cache/document-cache.ts +126 -41
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +23 -20
- package/src/cache/memory-segment-store.ts +243 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +13 -21
- package/src/cache/shell-snapshot.ts +417 -0
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +194 -99
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1132 -0
- package/src/client.rsc.tsx +39 -22
- package/src/client.tsx +28 -58
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +108 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +13 -1
- package/src/decode-loader-results.ts +18 -2
- package/src/defer.ts +185 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +0 -3
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +57 -40
- package/src/handles/MetaTags.tsx +24 -53
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +35 -8
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +18 -0
- package/src/handles/meta.ts +14 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +13 -22
- package/src/host/index.ts +7 -0
- package/src/host/pattern-matcher.ts +23 -52
- package/src/host/router.ts +1 -65
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +6 -2
- package/src/href-client.ts +7 -12
- package/src/index.rsc.ts +88 -8
- package/src/index.ts +90 -16
- package/src/internal-debug.ts +11 -10
- package/src/loader.rsc.ts +19 -9
- package/src/loader.ts +12 -4
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +32 -37
- package/src/prerender.ts +75 -7
- package/src/redirect-origin.ts +114 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +25 -0
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +13 -49
- package/src/route-definition/dsl-helpers.ts +60 -53
- package/src/route-definition/helper-factories.ts +0 -2
- package/src/route-definition/helpers-types.ts +46 -46
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/redirect.ts +44 -11
- package/src/route-definition/resolve-handler-use.ts +6 -1
- package/src/route-definition/use-item-types.ts +3 -6
- package/src/route-map-builder.ts +41 -20
- package/src/route-types.ts +0 -5
- package/src/router/content-negotiation.ts +58 -23
- package/src/router/error-handling.ts +44 -17
- package/src/router/find-match.ts +129 -30
- package/src/router/handler-context.ts +6 -1
- package/src/router/instrument.ts +355 -0
- package/src/router/intercept-resolution.ts +35 -2
- package/src/router/lazy-includes.ts +79 -56
- package/src/router/loader-resolution.ts +151 -73
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +74 -40
- package/src/router/match-api.ts +76 -52
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +181 -178
- package/src/router/match-middleware/background-revalidation.ts +40 -24
- package/src/router/match-middleware/cache-lookup.ts +115 -194
- package/src/router/match-middleware/cache-store.ts +61 -50
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -22
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +36 -67
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +0 -116
- package/src/router/middleware.ts +231 -120
- package/src/router/navigation-snapshot.ts +7 -56
- package/src/router/params-util.ts +23 -0
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +99 -152
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +111 -66
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +47 -42
- package/src/router/revalidation.ts +75 -81
- package/src/router/route-snapshot.ts +14 -3
- package/src/router/router-context.ts +6 -29
- package/src/router/router-interfaces.ts +70 -8
- package/src/router/router-options.ts +126 -4
- package/src/router/segment-resolution/fresh.ts +104 -80
- package/src/router/segment-resolution/helpers.ts +86 -6
- package/src/router/segment-resolution/loader-cache.ts +155 -39
- package/src/router/segment-resolution/loader-mask.ts +60 -0
- package/src/router/segment-resolution/loader-snapshot.ts +259 -0
- package/src/router/segment-resolution/mask-nested.ts +83 -0
- package/src/router/segment-resolution/revalidation.ts +215 -304
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +35 -15
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +6 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +54 -35
- package/src/router/telemetry-otel.ts +160 -200
- package/src/router/telemetry.ts +9 -23
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +215 -0
- package/src/router/trie-matching.ts +171 -64
- package/src/router/types.ts +1 -63
- package/src/router/url-params.ts +13 -5
- package/src/router.ts +119 -48
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +1 -0
- package/src/rsc/handler.ts +267 -152
- package/src/rsc/helpers.ts +78 -4
- package/src/rsc/index.ts +1 -4
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +114 -38
- package/src/rsc/manifest-init.ts +29 -42
- package/src/rsc/nonce.ts +10 -1
- package/src/rsc/origin-guard.ts +11 -15
- package/src/rsc/progressive-enhancement.ts +120 -13
- package/src/rsc/redirect-guard.ts +100 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +58 -141
- package/src/rsc/rsc-rendering.ts +492 -49
- package/src/rsc/runtime-warnings.ts +14 -0
- package/src/rsc/server-action.ts +268 -82
- package/src/rsc/shell-capture.ts +1190 -0
- package/src/rsc/shell-serve.ts +181 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +45 -3
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +31 -26
- package/src/segment-loader-promise.ts +49 -4
- package/src/segment-system.tsx +260 -95
- package/src/server/context.ts +99 -9
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +125 -2
- package/src/server/handle-store.ts +21 -38
- package/src/server/loader-registry.ts +33 -42
- package/src/server/request-context.ts +379 -138
- package/src/ssr/index.tsx +491 -182
- package/src/ssr/inject-rsc-eager.ts +167 -0
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/static-handler.ts +10 -13
- package/src/testing/cache-status.ts +44 -48
- package/src/testing/collect-handle.ts +14 -31
- package/src/testing/dispatch.ts +533 -160
- package/src/testing/e2e/fixture.ts +45 -11
- package/src/testing/e2e/index.ts +1 -22
- package/src/testing/e2e/matchers.ts +0 -16
- package/src/testing/e2e/parity.ts +85 -4
- package/src/testing/e2e/server.ts +12 -0
- package/src/testing/flight-matchers.ts +7 -14
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +36 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +30 -0
- package/src/testing/flight.ts +145 -70
- package/src/testing/generated-routes.ts +26 -50
- package/src/testing/index.ts +18 -19
- package/src/testing/internal/context.ts +184 -68
- 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 +134 -115
- package/src/testing/run-loader.ts +140 -51
- package/src/testing/run-middleware.ts +59 -33
- package/src/testing/run-transition-when.ts +164 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +1 -1
- package/src/testing/vitest-stubs/cloudflare-workers.ts +1 -1
- package/src/testing/vitest.ts +138 -16
- package/src/theme/ThemeProvider.tsx +56 -84
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +13 -4
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +15 -15
- package/src/types/handler-context.ts +45 -15
- package/src/types/index.ts +2 -10
- package/src/types/loader-types.ts +6 -3
- package/src/types/request-scope.ts +8 -22
- package/src/types/route-config.ts +20 -52
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +100 -13
- package/src/urls/include-helper.ts +10 -12
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +2 -8
- package/src/urls/path-helper-types.ts +52 -14
- package/src/urls/path-helper.ts +5 -54
- package/src/urls/pattern-types.ts +36 -0
- package/src/urls/type-extraction.ts +76 -42
- package/src/urls/urls-function.ts +0 -14
- package/src/use-loader.tsx +0 -186
- package/src/vercel/index.ts +11 -0
- package/src/vercel/tracing.ts +88 -0
- package/src/vite/discovery/bundle-postprocess.ts +2 -1
- package/src/vite/discovery/dev-prerender-cache.ts +117 -0
- package/src/vite/discovery/discover-routers.ts +34 -43
- package/src/vite/discovery/discovery-errors.ts +61 -0
- package/src/vite/discovery/prerender-collection.ts +33 -46
- package/src/vite/discovery/state.ts +12 -1
- package/src/vite/discovery/virtual-module-codegen.ts +1 -11
- package/src/vite/index.ts +9 -0
- package/src/vite/inject-client-debug.ts +88 -0
- package/src/vite/plugin-types.ts +143 -10
- package/src/vite/plugins/cjs-to-esm.ts +8 -12
- package/src/vite/plugins/client-ref-dedup.ts +0 -11
- package/src/vite/plugins/client-ref-hashing.ts +0 -10
- package/src/vite/plugins/cloudflare-protocol-stub.ts +0 -20
- package/src/vite/plugins/expose-action-id.ts +2 -73
- package/src/vite/plugins/expose-id-utils.ts +85 -56
- package/src/vite/plugins/expose-ids/export-analysis.ts +30 -43
- package/src/vite/plugins/expose-ids/handler-transform.ts +5 -31
- package/src/vite/plugins/expose-ids/loader-transform.ts +12 -20
- package/src/vite/plugins/expose-ids/router-transform.ts +98 -26
- package/src/vite/plugins/expose-internal-ids.ts +10 -1
- package/src/vite/plugins/performance-tracks.ts +0 -3
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +21 -46
- package/src/vite/plugins/vercel-output.ts +384 -0
- package/src/vite/plugins/version-injector.ts +22 -27
- package/src/vite/plugins/version-plugin.ts +6 -66
- package/src/vite/plugins/virtual-entries.ts +137 -26
- package/src/vite/rango.ts +146 -135
- package/src/vite/router-discovery.ts +189 -48
- package/src/vite/utils/ast-handler-extract.ts +11 -20
- package/src/vite/utils/bundle-analysis.ts +6 -13
- package/src/vite/utils/client-chunks.ts +0 -6
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +0 -22
- package/src/vite/utils/manifest-utils.ts +4 -75
- package/src/vite/utils/package-resolution.ts +1 -73
- package/src/vite/utils/prerender-utils.ts +71 -44
- package/src/vite/utils/shared-utils.ts +55 -37
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -10,16 +10,13 @@
|
|
|
10
10
|
|
|
11
11
|
import type { ResolvedSegment } from "../types.js";
|
|
12
12
|
import type { SerializedSegmentData } from "./types.js";
|
|
13
|
+
import { INTERNAL_RANGO_DEBUG } from "../internal-debug.js";
|
|
13
14
|
import {
|
|
14
15
|
renderToReadableStream,
|
|
15
16
|
createTemporaryReferenceSet,
|
|
16
17
|
} from "@vitejs/plugin-rsc/rsc";
|
|
17
18
|
import { createFromReadableStream } from "@vitejs/plugin-rsc/rsc";
|
|
18
19
|
|
|
19
|
-
// ============================================================================
|
|
20
|
-
// Stream Utilities (internal)
|
|
21
|
-
// ============================================================================
|
|
22
|
-
|
|
23
20
|
/**
|
|
24
21
|
* Convert a ReadableStream to a string.
|
|
25
22
|
*/
|
|
@@ -28,16 +25,16 @@ export async function streamToString(
|
|
|
28
25
|
): Promise<string> {
|
|
29
26
|
const reader = stream.getReader();
|
|
30
27
|
const decoder = new TextDecoder();
|
|
31
|
-
|
|
28
|
+
const chunks: string[] = [];
|
|
32
29
|
|
|
33
30
|
while (true) {
|
|
34
31
|
const { done, value } = await reader.read();
|
|
35
32
|
if (done) break;
|
|
36
|
-
|
|
33
|
+
chunks.push(decoder.decode(value, { stream: true }));
|
|
37
34
|
}
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
return
|
|
36
|
+
chunks.push(decoder.decode()); // flush
|
|
37
|
+
return chunks.join("");
|
|
41
38
|
}
|
|
42
39
|
|
|
43
40
|
/**
|
|
@@ -55,10 +52,6 @@ export function stringToStream(str: string): ReadableStream<Uint8Array> {
|
|
|
55
52
|
});
|
|
56
53
|
}
|
|
57
54
|
|
|
58
|
-
// ============================================================================
|
|
59
|
-
// RSC Serialization Primitives (internal)
|
|
60
|
-
// ============================================================================
|
|
61
|
-
|
|
62
55
|
/**
|
|
63
56
|
* RSC-serialize a value using React Server Components stream.
|
|
64
57
|
* Used for serializing loaderData, layout, loading components etc.
|
|
@@ -90,10 +83,6 @@ export async function rscDeserialize<T>(
|
|
|
90
83
|
return createFromReadableStream<T>(stream, { temporaryReferences });
|
|
91
84
|
}
|
|
92
85
|
|
|
93
|
-
// ============================================================================
|
|
94
|
-
// Null-Preserving RSC Serialization (for caching)
|
|
95
|
-
// ============================================================================
|
|
96
|
-
|
|
97
86
|
/**
|
|
98
87
|
* RSC-serialize any value including null.
|
|
99
88
|
* Unlike rscSerialize(), this does NOT skip null — it serializes it through
|
|
@@ -107,7 +96,14 @@ export async function serializeResult(value: unknown): Promise<string | null> {
|
|
|
107
96
|
const temporaryReferences = createTemporaryReferenceSet();
|
|
108
97
|
const stream = renderToReadableStream(value, { temporaryReferences });
|
|
109
98
|
return await streamToString(stream);
|
|
110
|
-
} catch {
|
|
99
|
+
} catch (error) {
|
|
100
|
+
// Returning null silently turns a non-serializable cache value into a
|
|
101
|
+
// permanent miss with no trace. Surface it on the internal debug channel so
|
|
102
|
+
// a failed serialize is diagnosable on wrangler tail, but keep returning
|
|
103
|
+
// null so the caller falls through to an uncached render rather than throws.
|
|
104
|
+
if (INTERNAL_RANGO_DEBUG) {
|
|
105
|
+
console.warn("[segment-codec] serializeResult failed:", error);
|
|
106
|
+
}
|
|
111
107
|
return null;
|
|
112
108
|
}
|
|
113
109
|
}
|
|
@@ -122,10 +118,6 @@ export async function deserializeResult<T>(encoded: string): Promise<T> {
|
|
|
122
118
|
return createFromReadableStream<T>(stream, { temporaryReferences });
|
|
123
119
|
}
|
|
124
120
|
|
|
125
|
-
// ============================================================================
|
|
126
|
-
// Public API
|
|
127
|
-
// ============================================================================
|
|
128
|
-
|
|
129
121
|
/**
|
|
130
122
|
* RSC-deserialize a single encoded component string back to a React element.
|
|
131
123
|
* Used by the static handler runtime to revive pre-rendered components.
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture data snapshot: recording + seeding stores for PPR shell parity.
|
|
3
|
+
*
|
|
4
|
+
* The scar tissue this fixes: a PPR HIT serves frozen prelude bytes, then a
|
|
5
|
+
* FULL FRESH Flight render for hydration. Any shell-baked (non-hole) content
|
|
6
|
+
* that drifts between capture time and hit time — a cache() segment with a
|
|
7
|
+
* shorter ttl than the shell, a tag-invalidated item — makes the fresh payload
|
|
8
|
+
* disagree with the prelude, so React throws a hydration text mismatch and
|
|
9
|
+
* regenerates the tree client-side (wiping the FOUC theme class, flashing
|
|
10
|
+
* content). See docs/design/ppr-shell-resume.md.
|
|
11
|
+
*
|
|
12
|
+
* The fix (Next.js resume-data-cache analog, adapted to Rango's cache rings):
|
|
13
|
+
* the CAPTURE render records every cache-store read-hit and write it performed
|
|
14
|
+
* (the {@link RecordingShellStore}); the record rides inside the ShellCacheEntry
|
|
15
|
+
* as its `snapshot`; on a HIT the tail render reads through a
|
|
16
|
+
* {@link SeededShellStore} overlay that serves those recorded values AS FRESH,
|
|
17
|
+
* so the shell region reproduces byte-identically while everything NOT recorded
|
|
18
|
+
* (the holes — masked loaders were never executed at capture, so their reads
|
|
19
|
+
* were never recorded) stays live.
|
|
20
|
+
*
|
|
21
|
+
* The invariant, verbatim: the snapshot is exactly the set of cache-store reads
|
|
22
|
+
* the capture render performed; replaying them on a HIT reproduces the shell
|
|
23
|
+
* content byte-identically; everything not recorded stays live.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import type {
|
|
27
|
+
SegmentCacheStore,
|
|
28
|
+
CacheGetResult,
|
|
29
|
+
CacheItemResult,
|
|
30
|
+
CacheItemOptions,
|
|
31
|
+
CachedEntryData,
|
|
32
|
+
ShellCacheEntry,
|
|
33
|
+
ShellSnapshotRecord,
|
|
34
|
+
ShellSnapshotItemValue,
|
|
35
|
+
ShellSnapshotResponseValue,
|
|
36
|
+
ShellSnapshotLoaderValue,
|
|
37
|
+
} from "./types.js";
|
|
38
|
+
import { bufferToBase64, base64ToBuffer } from "./cf/cf-base64.js";
|
|
39
|
+
import { isPerClientSignalHeader } from "../browser/cookie-name.js";
|
|
40
|
+
|
|
41
|
+
/** Compose the last-write-wins map key. NUL (`\u0000`) cannot appear in a cache key. */
|
|
42
|
+
function recordKey(family: ShellSnapshotRecord["family"], key: string): string {
|
|
43
|
+
return `${family}\u0000${key}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Serialize a Response to the snapshot's stored shape (base64 body). */
|
|
47
|
+
async function serializeResponse(
|
|
48
|
+
response: Response,
|
|
49
|
+
): Promise<ShellSnapshotResponseValue> {
|
|
50
|
+
const body = await response.clone().arrayBuffer();
|
|
51
|
+
const headers: [string, string][] = [];
|
|
52
|
+
response.headers.forEach((value, name) => {
|
|
53
|
+
// Mirror putResponse: per-client signal headers never enter a shared entry.
|
|
54
|
+
if (isPerClientSignalHeader(name)) return;
|
|
55
|
+
headers.push([name, value]);
|
|
56
|
+
});
|
|
57
|
+
return { status: response.status, headers, body: bufferToBase64(body) };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Rebuild a live Response from a snapshot's stored response shape. */
|
|
61
|
+
function deserializeResponse(value: ShellSnapshotResponseValue): Response {
|
|
62
|
+
return new Response(base64ToBuffer(value.body), {
|
|
63
|
+
status: value.status,
|
|
64
|
+
headers: new Headers(value.headers),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A store wrapper the CAPTURE render reads through. Every call passes through to
|
|
70
|
+
* the underlying store unchanged; for the item/segment/response families it also
|
|
71
|
+
* RECORDS, last-write-wins per (family, key):
|
|
72
|
+
* - read-hits (get/getItem/getResponse returning non-null) — the value that
|
|
73
|
+
* fed the shell,
|
|
74
|
+
* - writes (set/setItem/putResponse) — the value a MISS computed and baked.
|
|
75
|
+
* The shell family (getShell/putShell) is never recorded (the snapshot rides
|
|
76
|
+
* inside a shell entry). Reads that MISS are not recorded (a miss produced no
|
|
77
|
+
* shell content; if the render then computed and wrote, that write is recorded).
|
|
78
|
+
*
|
|
79
|
+
* Deferred writes: cache writes run under waitUntil (fire-and-forget on Node,
|
|
80
|
+
* executionContext on workerd), so their setItem/set calls — hence their records
|
|
81
|
+
* — may land after the shell has quiesced. The capture collects those write
|
|
82
|
+
* promises via {@link trackWrite} and awaits them ({@link settleWrites}) before
|
|
83
|
+
* draining, so a MISS-at-capture value is still pinned.
|
|
84
|
+
*/
|
|
85
|
+
export class RecordingShellStore<
|
|
86
|
+
TEnv = unknown,
|
|
87
|
+
> implements SegmentCacheStore<TEnv> {
|
|
88
|
+
private readonly records = new Map<string, ShellSnapshotRecord>();
|
|
89
|
+
private readonly writes: Promise<unknown>[] = [];
|
|
90
|
+
|
|
91
|
+
constructor(private readonly inner: SegmentCacheStore<TEnv>) {}
|
|
92
|
+
|
|
93
|
+
get defaults(): SegmentCacheStore<TEnv>["defaults"] {
|
|
94
|
+
return this.inner.defaults;
|
|
95
|
+
}
|
|
96
|
+
get keyGenerator(): SegmentCacheStore<TEnv>["keyGenerator"] {
|
|
97
|
+
return this.inner.keyGenerator;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private record(
|
|
101
|
+
family: ShellSnapshotRecord["family"],
|
|
102
|
+
key: string,
|
|
103
|
+
value: ShellSnapshotRecord["value"],
|
|
104
|
+
): void {
|
|
105
|
+
this.records.set(recordKey(family, key), { family, key, value });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Track a deferred cache-write promise so the capture can await it pre-drain. */
|
|
109
|
+
trackWrite(p: Promise<unknown>): void {
|
|
110
|
+
this.writes.push(p);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Await the tracked deferred writes so their records are present before drain.
|
|
115
|
+
* Drains ITERATIVELY: a write task can schedule a NESTED write (the ring-3
|
|
116
|
+
* cacheRoute path schedules its actual store.set in a second waitUntil while the
|
|
117
|
+
* first is running), so each awaited batch may enqueue more. Loop until the
|
|
118
|
+
* queue empties or the deadline passes. Bounded: a pathologically slow write
|
|
119
|
+
* must never stall the capture task, so a key that does not settle in time is
|
|
120
|
+
* left unpinned (it drifts, the pre-snapshot behavior) rather than hanging.
|
|
121
|
+
*/
|
|
122
|
+
async settleWrites(timeoutMs: number): Promise<void> {
|
|
123
|
+
const deadline = Date.now() + timeoutMs;
|
|
124
|
+
while (this.writes.length > 0) {
|
|
125
|
+
const remaining = deadline - Date.now();
|
|
126
|
+
if (remaining <= 0) return;
|
|
127
|
+
// Take the current batch; new writes scheduled while awaiting accumulate in
|
|
128
|
+
// this.writes and are drained on the next iteration.
|
|
129
|
+
const batch = this.writes.splice(0);
|
|
130
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
131
|
+
const guard = new Promise<void>((resolve) => {
|
|
132
|
+
timer = setTimeout(resolve, remaining);
|
|
133
|
+
(timer as { unref?: () => void }).unref?.();
|
|
134
|
+
});
|
|
135
|
+
await Promise.race([Promise.allSettled(batch).then(() => {}), guard]);
|
|
136
|
+
if (timer) clearTimeout(timer);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** The recorded snapshot (last-write-wins per family+key), or undefined if empty. */
|
|
141
|
+
drainSnapshot(): ShellSnapshotRecord[] | undefined {
|
|
142
|
+
return this.records.size > 0 ? [...this.records.values()] : undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async get(key: string): Promise<CacheGetResult | null> {
|
|
146
|
+
const result = await this.inner.get(key);
|
|
147
|
+
if (result) this.record("segment", key, result.data);
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async set(
|
|
152
|
+
key: string,
|
|
153
|
+
data: CachedEntryData,
|
|
154
|
+
ttl: number,
|
|
155
|
+
swr?: number,
|
|
156
|
+
): Promise<void> {
|
|
157
|
+
this.record("segment", key, data);
|
|
158
|
+
return this.inner.set(key, data, ttl, swr);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async delete(key: string): Promise<boolean> {
|
|
162
|
+
return this.inner.delete(key);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async clear(): Promise<void> {
|
|
166
|
+
return this.inner.clear?.();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async getResponse(
|
|
170
|
+
key: string,
|
|
171
|
+
): Promise<{ response: Response; shouldRevalidate: boolean } | null> {
|
|
172
|
+
if (!this.inner.getResponse) return null;
|
|
173
|
+
const result = await this.inner.getResponse(key);
|
|
174
|
+
if (result)
|
|
175
|
+
this.record("response", key, await serializeResponse(result.response));
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async putResponse(
|
|
180
|
+
key: string,
|
|
181
|
+
response: Response,
|
|
182
|
+
ttl: number,
|
|
183
|
+
swr?: number,
|
|
184
|
+
tags?: string[],
|
|
185
|
+
): Promise<void> {
|
|
186
|
+
if (!this.inner.putResponse) return;
|
|
187
|
+
this.record("response", key, await serializeResponse(response));
|
|
188
|
+
return this.inner.putResponse(key, response, ttl, swr, tags);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async getItem(key: string): Promise<CacheItemResult | null> {
|
|
192
|
+
if (!this.inner.getItem) return null;
|
|
193
|
+
const result = await this.inner.getItem(key);
|
|
194
|
+
if (result) {
|
|
195
|
+
const value: ShellSnapshotItemValue = {
|
|
196
|
+
value: result.value,
|
|
197
|
+
handles: result.handles,
|
|
198
|
+
tags: result.tags,
|
|
199
|
+
};
|
|
200
|
+
this.record("item", key, value);
|
|
201
|
+
}
|
|
202
|
+
return result;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async setItem(
|
|
206
|
+
key: string,
|
|
207
|
+
value: string,
|
|
208
|
+
options?: CacheItemOptions,
|
|
209
|
+
): Promise<void> {
|
|
210
|
+
if (!this.inner.setItem) return;
|
|
211
|
+
const stored: ShellSnapshotItemValue = {
|
|
212
|
+
value,
|
|
213
|
+
handles: options?.handles,
|
|
214
|
+
tags: options?.tags,
|
|
215
|
+
};
|
|
216
|
+
this.record("item", key, stored);
|
|
217
|
+
return this.inner.setItem(key, value, options);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async getShell(
|
|
221
|
+
key: string,
|
|
222
|
+
): Promise<{ entry: ShellCacheEntry; shouldRevalidate?: boolean } | null> {
|
|
223
|
+
return this.inner.getShell ? this.inner.getShell(key) : null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async putShell(
|
|
227
|
+
key: string,
|
|
228
|
+
entry: ShellCacheEntry,
|
|
229
|
+
ttlSeconds?: number,
|
|
230
|
+
swrSeconds?: number,
|
|
231
|
+
tags?: string[],
|
|
232
|
+
): Promise<void> {
|
|
233
|
+
return this.inner.putShell?.(key, entry, ttlSeconds, swrSeconds, tags);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async invalidateTags(tags: string[]): Promise<void> {
|
|
237
|
+
return this.inner.invalidateTags?.(tags);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** True iff `store` is a RecordingShellStore (duck-typed across module copies). */
|
|
242
|
+
export function getRecordingStore<TEnv>(
|
|
243
|
+
store: SegmentCacheStore<TEnv> | undefined,
|
|
244
|
+
): RecordingShellStore<TEnv> | undefined {
|
|
245
|
+
return store instanceof RecordingShellStore ? store : undefined;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Materialize the loader-family seed from a shell snapshot for a HIT's tail
|
|
250
|
+
* render: Flight-deserialize each recorded (promise-elided) bake-lane
|
|
251
|
+
* container into a segment-key -> container Map, which serveShellHit assigns
|
|
252
|
+
* to the tail context's `_shellLoaderSeed` for the resolveLoaderData overlay.
|
|
253
|
+
* Lives here so every snapshot family is decoded in this module (the
|
|
254
|
+
* item/segment/response families via {@link SeededShellStore}); the loader
|
|
255
|
+
* family is not a store read, so it seeds the context instead of a store.
|
|
256
|
+
*
|
|
257
|
+
* Deserializations run in parallel; a record that fails to decode is skipped
|
|
258
|
+
* (that loader drifts — the pre-snapshot behavior — instead of failing the
|
|
259
|
+
* HIT). Returns undefined when the snapshot carries no loader records, without
|
|
260
|
+
* touching the Flight codec (kept lazy for cold paths and non-RSC configs).
|
|
261
|
+
*/
|
|
262
|
+
export async function buildShellLoaderSeed(
|
|
263
|
+
snapshot: ShellSnapshotRecord[],
|
|
264
|
+
): Promise<Map<string, unknown> | undefined> {
|
|
265
|
+
const loaderRecords: ShellSnapshotRecord[] = [];
|
|
266
|
+
for (const rec of snapshot) {
|
|
267
|
+
if (rec.family === "loader") loaderRecords.push(rec);
|
|
268
|
+
}
|
|
269
|
+
if (loaderRecords.length === 0) return undefined;
|
|
270
|
+
|
|
271
|
+
const { deserializeResult } = await import("./segment-codec.js");
|
|
272
|
+
const entries = await Promise.all(
|
|
273
|
+
loaderRecords.map(async (rec): Promise<[string, unknown] | null> => {
|
|
274
|
+
try {
|
|
275
|
+
return [
|
|
276
|
+
rec.key,
|
|
277
|
+
await deserializeResult(
|
|
278
|
+
(rec.value as ShellSnapshotLoaderValue).value,
|
|
279
|
+
),
|
|
280
|
+
];
|
|
281
|
+
} catch {
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
}),
|
|
285
|
+
);
|
|
286
|
+
const seed = new Map<string, unknown>();
|
|
287
|
+
for (const entry of entries) {
|
|
288
|
+
if (entry) seed.set(entry[0], entry[1]);
|
|
289
|
+
}
|
|
290
|
+
return seed.size > 0 ? seed : undefined;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* A read-through overlay the HIT tail render reads through. For a key present in
|
|
295
|
+
* the snapshot it serves the recorded value AS FRESH (shouldRevalidate: false —
|
|
296
|
+
* a pinned key must NOT kick SWR background revalidation) so the tail's payload
|
|
297
|
+
* matches the frozen prelude. Every other read falls through to the real store
|
|
298
|
+
* (the holes — masked loaders were never recorded — stay live). ALL writes pass
|
|
299
|
+
* through unchanged: a live hole's loader may legitimately write. The shell
|
|
300
|
+
* family always passes through.
|
|
301
|
+
*/
|
|
302
|
+
export class SeededShellStore<
|
|
303
|
+
TEnv = unknown,
|
|
304
|
+
> implements SegmentCacheStore<TEnv> {
|
|
305
|
+
private readonly items = new Map<string, ShellSnapshotItemValue>();
|
|
306
|
+
private readonly segments = new Map<string, CachedEntryData>();
|
|
307
|
+
private readonly responses = new Map<string, ShellSnapshotResponseValue>();
|
|
308
|
+
|
|
309
|
+
constructor(
|
|
310
|
+
private readonly inner: SegmentCacheStore<TEnv>,
|
|
311
|
+
snapshot: ShellSnapshotRecord[],
|
|
312
|
+
) {
|
|
313
|
+
for (const rec of snapshot) {
|
|
314
|
+
if (rec.family === "item") {
|
|
315
|
+
this.items.set(rec.key, rec.value as ShellSnapshotItemValue);
|
|
316
|
+
} else if (rec.family === "segment") {
|
|
317
|
+
this.segments.set(rec.key, rec.value as CachedEntryData);
|
|
318
|
+
} else if (rec.family === "response") {
|
|
319
|
+
this.responses.set(rec.key, rec.value as ShellSnapshotResponseValue);
|
|
320
|
+
}
|
|
321
|
+
// "loader" family records are not store reads — serveShellHit seeds them
|
|
322
|
+
// onto the tail context (_shellLoaderSeed) for the resolveLoaderData
|
|
323
|
+
// overlay instead.
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
get defaults(): SegmentCacheStore<TEnv>["defaults"] {
|
|
328
|
+
return this.inner.defaults;
|
|
329
|
+
}
|
|
330
|
+
get keyGenerator(): SegmentCacheStore<TEnv>["keyGenerator"] {
|
|
331
|
+
return this.inner.keyGenerator;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async get(key: string): Promise<CacheGetResult | null> {
|
|
335
|
+
const seeded = this.segments.get(key);
|
|
336
|
+
if (seeded) return { data: seeded, shouldRevalidate: false };
|
|
337
|
+
return this.inner.get(key);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async set(
|
|
341
|
+
key: string,
|
|
342
|
+
data: CachedEntryData,
|
|
343
|
+
ttl: number,
|
|
344
|
+
swr?: number,
|
|
345
|
+
): Promise<void> {
|
|
346
|
+
return this.inner.set(key, data, ttl, swr);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async delete(key: string): Promise<boolean> {
|
|
350
|
+
return this.inner.delete(key);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async clear(): Promise<void> {
|
|
354
|
+
return this.inner.clear?.();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async getResponse(
|
|
358
|
+
key: string,
|
|
359
|
+
): Promise<{ response: Response; shouldRevalidate: boolean } | null> {
|
|
360
|
+
const seeded = this.responses.get(key);
|
|
361
|
+
if (seeded) {
|
|
362
|
+
return { response: deserializeResponse(seeded), shouldRevalidate: false };
|
|
363
|
+
}
|
|
364
|
+
return this.inner.getResponse ? this.inner.getResponse(key) : null;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async putResponse(
|
|
368
|
+
key: string,
|
|
369
|
+
response: Response,
|
|
370
|
+
ttl: number,
|
|
371
|
+
swr?: number,
|
|
372
|
+
tags?: string[],
|
|
373
|
+
): Promise<void> {
|
|
374
|
+
return this.inner.putResponse?.(key, response, ttl, swr, tags);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async getItem(key: string): Promise<CacheItemResult | null> {
|
|
378
|
+
const seeded = this.items.get(key);
|
|
379
|
+
if (seeded) {
|
|
380
|
+
return {
|
|
381
|
+
value: seeded.value,
|
|
382
|
+
handles: seeded.handles,
|
|
383
|
+
tags: seeded.tags,
|
|
384
|
+
shouldRevalidate: false,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
return this.inner.getItem ? this.inner.getItem(key) : null;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
async setItem(
|
|
391
|
+
key: string,
|
|
392
|
+
value: string,
|
|
393
|
+
options?: CacheItemOptions,
|
|
394
|
+
): Promise<void> {
|
|
395
|
+
return this.inner.setItem?.(key, value, options);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async getShell(
|
|
399
|
+
key: string,
|
|
400
|
+
): Promise<{ entry: ShellCacheEntry; shouldRevalidate?: boolean } | null> {
|
|
401
|
+
return this.inner.getShell ? this.inner.getShell(key) : null;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
async putShell(
|
|
405
|
+
key: string,
|
|
406
|
+
entry: ShellCacheEntry,
|
|
407
|
+
ttlSeconds?: number,
|
|
408
|
+
swrSeconds?: number,
|
|
409
|
+
tags?: string[],
|
|
410
|
+
): Promise<void> {
|
|
411
|
+
return this.inner.putShell?.(key, entry, ttlSeconds, swrSeconds, tags);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async invalidateTags(tags: string[]): Promise<void> {
|
|
415
|
+
return this.inner.invalidateTags?.(tags);
|
|
416
|
+
}
|
|
417
|
+
}
|