@rangojs/router 0.0.0-experimental.002d056c
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 +9 -0
- package/README.md +899 -0
- package/dist/bin/rango.js +1606 -0
- package/dist/vite/index.js +5153 -0
- package/package.json +177 -0
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +253 -0
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +112 -0
- package/skills/document-cache/SKILL.md +182 -0
- package/skills/fonts/SKILL.md +167 -0
- package/skills/hooks/SKILL.md +704 -0
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +313 -0
- package/skills/layout/SKILL.md +310 -0
- package/skills/links/SKILL.md +239 -0
- package/skills/loader/SKILL.md +596 -0
- package/skills/middleware/SKILL.md +339 -0
- package/skills/mime-routes/SKILL.md +128 -0
- package/skills/parallel/SKILL.md +305 -0
- package/skills/prerender/SKILL.md +643 -0
- package/skills/rango/SKILL.md +118 -0
- package/skills/response-routes/SKILL.md +411 -0
- package/skills/route/SKILL.md +385 -0
- package/skills/router-setup/SKILL.md +439 -0
- package/skills/tailwind/SKILL.md +129 -0
- package/skills/theme/SKILL.md +79 -0
- package/skills/typesafety/SKILL.md +623 -0
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +273 -0
- package/src/bin/rango.ts +321 -0
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/event-controller.ts +899 -0
- package/src/browser/history-state.ts +80 -0
- package/src/browser/index.ts +18 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +141 -0
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +134 -0
- package/src/browser/navigation-bridge.ts +638 -0
- package/src/browser/navigation-client.ts +261 -0
- package/src/browser/navigation-store.ts +806 -0
- package/src/browser/navigation-transaction.ts +297 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +582 -0
- package/src/browser/prefetch/cache.ts +206 -0
- package/src/browser/prefetch/fetch.ts +145 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +128 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +368 -0
- package/src/browser/react/NavigationProvider.tsx +413 -0
- package/src/browser/react/ScrollRestoration.tsx +94 -0
- package/src/browser/react/context.ts +59 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +52 -0
- package/src/browser/react/location-state-shared.ts +162 -0
- package/src/browser/react/location-state.ts +107 -0
- package/src/browser/react/mount-context.ts +37 -0
- 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 +218 -0
- package/src/browser/react/use-client-cache.ts +58 -0
- package/src/browser/react/use-handle.ts +162 -0
- package/src/browser/react/use-href.tsx +40 -0
- package/src/browser/react/use-link-status.ts +135 -0
- package/src/browser/react/use-mount.ts +31 -0
- package/src/browser/react/use-navigation.ts +99 -0
- package/src/browser/react/use-params.ts +65 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-router.ts +63 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +171 -0
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +464 -0
- package/src/browser/scroll-restoration.ts +397 -0
- package/src/browser/segment-reconciler.ts +216 -0
- package/src/browser/segment-structure-assert.ts +83 -0
- package/src/browser/server-action-bridge.ts +667 -0
- package/src/browser/shallow.ts +40 -0
- package/src/browser/types.ts +547 -0
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +438 -0
- package/src/build/generate-route-types.ts +36 -0
- package/src/build/index.ts +35 -0
- package/src/build/route-trie.ts +265 -0
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +98 -0
- package/src/build/route-types/codegen.ts +102 -0
- package/src/build/route-types/include-resolution.ts +411 -0
- package/src/build/route-types/param-extraction.ts +48 -0
- package/src/build/route-types/per-module-writer.ts +128 -0
- package/src/build/route-types/router-processing.ts +479 -0
- package/src/build/route-types/scan-filter.ts +78 -0
- package/src/build/runtime-discovery.ts +231 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +338 -0
- package/src/cache/cache-scope.ts +382 -0
- package/src/cache/cf/cf-cache-store.ts +982 -0
- package/src/cache/cf/index.ts +29 -0
- package/src/cache/document-cache.ts +369 -0
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +44 -0
- package/src/cache/memory-segment-store.ts +328 -0
- package/src/cache/profile-registry.ts +73 -0
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +256 -0
- package/src/cache/taint.ts +98 -0
- package/src/cache/types.ts +342 -0
- package/src/client.rsc.tsx +85 -0
- package/src/client.tsx +601 -0
- package/src/component-utils.ts +76 -0
- package/src/components/DefaultDocument.tsx +27 -0
- package/src/context-var.ts +86 -0
- package/src/debug.ts +243 -0
- package/src/default-error-boundary.tsx +88 -0
- package/src/deps/browser.ts +8 -0
- package/src/deps/html-stream-client.ts +2 -0
- package/src/deps/html-stream-server.ts +2 -0
- package/src/deps/rsc.ts +10 -0
- package/src/deps/ssr.ts +2 -0
- package/src/errors.ts +365 -0
- package/src/handle.ts +135 -0
- package/src/handles/MetaTags.tsx +246 -0
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +7 -0
- package/src/handles/meta.ts +264 -0
- package/src/host/cookie-handler.ts +165 -0
- package/src/host/errors.ts +97 -0
- package/src/host/index.ts +53 -0
- package/src/host/pattern-matcher.ts +214 -0
- package/src/host/router.ts +352 -0
- package/src/host/testing.ts +79 -0
- package/src/host/types.ts +146 -0
- package/src/host/utils.ts +25 -0
- package/src/href-client.ts +222 -0
- package/src/index.rsc.ts +233 -0
- package/src/index.ts +277 -0
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +89 -0
- package/src/loader.ts +64 -0
- package/src/network-error-thrower.tsx +23 -0
- package/src/outlet-context.ts +15 -0
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +37 -0
- package/src/prerender/store.ts +185 -0
- package/src/prerender.ts +463 -0
- package/src/reverse.ts +330 -0
- package/src/root-error-boundary.tsx +289 -0
- package/src/route-content-wrapper.tsx +196 -0
- package/src/route-definition/dsl-helpers.ts +934 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +430 -0
- package/src/route-definition/index.ts +52 -0
- package/src/route-definition/redirect.ts +93 -0
- package/src/route-definition.ts +1 -0
- package/src/route-map-builder.ts +281 -0
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +259 -0
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +287 -0
- package/src/router/find-match.ts +160 -0
- package/src/router/handler-context.ts +451 -0
- package/src/router/intercept-resolution.ts +397 -0
- package/src/router/lazy-includes.ts +236 -0
- package/src/router/loader-resolution.ts +420 -0
- package/src/router/logging.ts +251 -0
- package/src/router/manifest.ts +269 -0
- package/src/router/match-api.ts +620 -0
- package/src/router/match-context.ts +266 -0
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +223 -0
- package/src/router/match-middleware/cache-lookup.ts +634 -0
- package/src/router/match-middleware/cache-store.ts +295 -0
- package/src/router/match-middleware/index.ts +81 -0
- package/src/router/match-middleware/intercept-resolution.ts +306 -0
- package/src/router/match-middleware/segment-resolution.ts +193 -0
- package/src/router/match-pipelines.ts +179 -0
- package/src/router/match-result.ts +219 -0
- package/src/router/metrics.ts +282 -0
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +749 -0
- package/src/router/pattern-matching.ts +563 -0
- package/src/router/prerender-match.ts +402 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +289 -0
- package/src/router/router-context.ts +320 -0
- package/src/router/router-interfaces.ts +452 -0
- package/src/router/router-options.ts +592 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +570 -0
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +198 -0
- package/src/router/segment-resolution/revalidation.ts +1242 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -0
- package/src/router/segment-wrappers.ts +291 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +239 -0
- package/src/router/types.ts +170 -0
- package/src/router.ts +1006 -0
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +1089 -0
- package/src/rsc/helpers.ts +198 -0
- package/src/rsc/index.ts +36 -0
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +32 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +379 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +237 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +348 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +263 -0
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +454 -0
- package/src/server/context.ts +591 -0
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +308 -0
- package/src/server/loader-registry.ts +133 -0
- package/src/server/request-context.ts +920 -0
- package/src/server/root-layout.tsx +10 -0
- package/src/server/tsconfig.json +14 -0
- package/src/server.ts +51 -0
- package/src/ssr/index.tsx +365 -0
- package/src/static-handler.ts +114 -0
- package/src/theme/ThemeProvider.tsx +297 -0
- package/src/theme/ThemeScript.tsx +61 -0
- package/src/theme/constants.ts +62 -0
- package/src/theme/index.ts +48 -0
- package/src/theme/theme-context.ts +44 -0
- package/src/theme/theme-script.ts +155 -0
- package/src/theme/types.ts +182 -0
- package/src/theme/use-theme.ts +44 -0
- package/src/types/boundaries.ts +158 -0
- package/src/types/cache-types.ts +198 -0
- package/src/types/error-types.ts +192 -0
- package/src/types/global-namespace.ts +100 -0
- package/src/types/handler-context.ts +687 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +183 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +109 -0
- package/src/types/segments.ts +148 -0
- package/src/types.ts +1 -0
- package/src/urls/include-helper.ts +197 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +339 -0
- package/src/urls/path-helper.ts +329 -0
- package/src/urls/pattern-types.ts +95 -0
- package/src/urls/response-types.ts +106 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -0
- package/src/use-loader.tsx +354 -0
- package/src/vite/discovery/bundle-postprocess.ts +184 -0
- package/src/vite/discovery/discover-routers.ts +344 -0
- package/src/vite/discovery/prerender-collection.ts +385 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +47 -0
- package/src/vite/discovery/state.ts +108 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +16 -0
- package/src/vite/plugin-types.ts +48 -0
- package/src/vite/plugins/cjs-to-esm.ts +93 -0
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +105 -0
- package/src/vite/plugins/expose-action-id.ts +363 -0
- package/src/vite/plugins/expose-id-utils.ts +287 -0
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +569 -0
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +323 -0
- package/src/vite/plugins/version-injector.ts +83 -0
- package/src/vite/plugins/version-plugin.ts +266 -0
- package/src/vite/plugins/version.d.ts +12 -0
- package/src/vite/plugins/virtual-entries.ts +123 -0
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +445 -0
- package/src/vite/router-discovery.ts +777 -0
- package/src/vite/utils/ast-handler-extract.ts +517 -0
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +137 -0
- package/src/vite/utils/manifest-utils.ts +70 -0
- package/src/vite/utils/package-resolution.ts +121 -0
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Segment Codec
|
|
3
|
+
*
|
|
4
|
+
* RSC serialization/deserialization for cached segments.
|
|
5
|
+
* Handles the Flight protocol stream <-> string conversion
|
|
6
|
+
* and the segment-level encode/decode lifecycle.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/// <reference types="@vitejs/plugin-rsc/types" />
|
|
10
|
+
|
|
11
|
+
import type { ResolvedSegment } from "../types.js";
|
|
12
|
+
import type { SerializedSegmentData } from "./types.js";
|
|
13
|
+
import {
|
|
14
|
+
renderToReadableStream,
|
|
15
|
+
createTemporaryReferenceSet,
|
|
16
|
+
} from "@vitejs/plugin-rsc/rsc";
|
|
17
|
+
import { createFromReadableStream } from "@vitejs/plugin-rsc/rsc";
|
|
18
|
+
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Stream Utilities (internal)
|
|
21
|
+
// ============================================================================
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Convert a ReadableStream to a string.
|
|
25
|
+
*/
|
|
26
|
+
export async function streamToString(
|
|
27
|
+
stream: ReadableStream<Uint8Array>,
|
|
28
|
+
): Promise<string> {
|
|
29
|
+
const reader = stream.getReader();
|
|
30
|
+
const decoder = new TextDecoder();
|
|
31
|
+
let result = "";
|
|
32
|
+
|
|
33
|
+
while (true) {
|
|
34
|
+
const { done, value } = await reader.read();
|
|
35
|
+
if (done) break;
|
|
36
|
+
result += decoder.decode(value, { stream: true });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
result += decoder.decode(); // flush
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Convert a string to a ReadableStream.
|
|
45
|
+
*/
|
|
46
|
+
export function stringToStream(str: string): ReadableStream<Uint8Array> {
|
|
47
|
+
const encoder = new TextEncoder();
|
|
48
|
+
const uint8 = encoder.encode(str);
|
|
49
|
+
|
|
50
|
+
return new ReadableStream({
|
|
51
|
+
start(controller) {
|
|
52
|
+
controller.enqueue(uint8);
|
|
53
|
+
controller.close();
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ============================================================================
|
|
59
|
+
// RSC Serialization Primitives (internal)
|
|
60
|
+
// ============================================================================
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* RSC-serialize a value using React Server Components stream.
|
|
64
|
+
* Used for serializing loaderData, layout, loading components etc.
|
|
65
|
+
*
|
|
66
|
+
* Returns undefined for null/undefined inputs (component fields that are absent).
|
|
67
|
+
* For contexts where null is a valid result (loader caching, "use cache"),
|
|
68
|
+
* use serializeResult() instead which preserves null through RSC Flight.
|
|
69
|
+
*/
|
|
70
|
+
export async function rscSerialize(
|
|
71
|
+
value: unknown,
|
|
72
|
+
): Promise<string | undefined> {
|
|
73
|
+
if (value === undefined || value === null) return undefined;
|
|
74
|
+
|
|
75
|
+
const temporaryReferences = createTemporaryReferenceSet();
|
|
76
|
+
const stream = renderToReadableStream(value, { temporaryReferences });
|
|
77
|
+
return streamToString(stream);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* RSC-deserialize a value from a stored string.
|
|
82
|
+
*/
|
|
83
|
+
export async function rscDeserialize<T>(
|
|
84
|
+
encoded: string | undefined,
|
|
85
|
+
): Promise<T | undefined> {
|
|
86
|
+
if (!encoded) return undefined;
|
|
87
|
+
|
|
88
|
+
const temporaryReferences = createTemporaryReferenceSet();
|
|
89
|
+
const stream = stringToStream(encoded);
|
|
90
|
+
return createFromReadableStream<T>(stream, { temporaryReferences });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// Null-Preserving RSC Serialization (for caching)
|
|
95
|
+
// ============================================================================
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* RSC-serialize any value including null.
|
|
99
|
+
* Unlike rscSerialize(), this does NOT skip null — it serializes it through
|
|
100
|
+
* RSC Flight so that a loader returning null produces a valid cached entry
|
|
101
|
+
* rather than a permanent cache miss.
|
|
102
|
+
*
|
|
103
|
+
* Returns null only on serialization failure.
|
|
104
|
+
*/
|
|
105
|
+
export async function serializeResult(value: unknown): Promise<string | null> {
|
|
106
|
+
try {
|
|
107
|
+
const temporaryReferences = createTemporaryReferenceSet();
|
|
108
|
+
const stream = renderToReadableStream(value, { temporaryReferences });
|
|
109
|
+
return await streamToString(stream);
|
|
110
|
+
} catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* RSC-deserialize a cached result string.
|
|
117
|
+
* Counterpart to serializeResult() — always receives a non-empty string.
|
|
118
|
+
*/
|
|
119
|
+
export async function deserializeResult<T>(encoded: string): Promise<T> {
|
|
120
|
+
const temporaryReferences = createTemporaryReferenceSet();
|
|
121
|
+
const stream = stringToStream(encoded);
|
|
122
|
+
return createFromReadableStream<T>(stream, { temporaryReferences });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ============================================================================
|
|
126
|
+
// Public API
|
|
127
|
+
// ============================================================================
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* RSC-deserialize a single encoded component string back to a React element.
|
|
131
|
+
* Used by the static handler runtime to revive pre-rendered components.
|
|
132
|
+
* Identical to deserializeResult<unknown>.
|
|
133
|
+
*/
|
|
134
|
+
export const deserializeComponent: (encoded: string) => Promise<unknown> =
|
|
135
|
+
deserializeResult;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Serialize segments for storage.
|
|
139
|
+
* Each segment's component, layout, loading, and loaderData are RSC-serialized.
|
|
140
|
+
* Metadata is preserved as-is.
|
|
141
|
+
*/
|
|
142
|
+
export async function serializeSegments(
|
|
143
|
+
segments: ResolvedSegment[],
|
|
144
|
+
): Promise<SerializedSegmentData[]> {
|
|
145
|
+
return Promise.all(
|
|
146
|
+
segments.map(async (segment): Promise<SerializedSegmentData> => {
|
|
147
|
+
const temporaryReferences = createTemporaryReferenceSet();
|
|
148
|
+
|
|
149
|
+
// Await component if it's a Promise (intercepts with loading keep component as Promise)
|
|
150
|
+
const componentResolved =
|
|
151
|
+
segment.component instanceof Promise
|
|
152
|
+
? await segment.component
|
|
153
|
+
: segment.component;
|
|
154
|
+
|
|
155
|
+
// Serialize the component to RSC stream
|
|
156
|
+
const stream = renderToReadableStream(componentResolved, {
|
|
157
|
+
temporaryReferences,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// RSC-serialize loading: "null" string distinguishes explicit null from undefined
|
|
161
|
+
const encodedLoading =
|
|
162
|
+
segment.loading !== undefined
|
|
163
|
+
? segment.loading === null
|
|
164
|
+
? "null"
|
|
165
|
+
: await rscSerialize(segment.loading)
|
|
166
|
+
: undefined;
|
|
167
|
+
|
|
168
|
+
// Await loaderData / loaderDataPromise if they're Promises
|
|
169
|
+
const loaderDataResolved =
|
|
170
|
+
segment.loaderData instanceof Promise
|
|
171
|
+
? await segment.loaderData
|
|
172
|
+
: segment.loaderData;
|
|
173
|
+
const loaderDataPromiseResolved =
|
|
174
|
+
segment.loaderDataPromise instanceof Promise
|
|
175
|
+
? await segment.loaderDataPromise
|
|
176
|
+
: segment.loaderDataPromise;
|
|
177
|
+
|
|
178
|
+
// Parallelize stream-to-string and RSC serialization of sub-fields
|
|
179
|
+
const [
|
|
180
|
+
encoded,
|
|
181
|
+
encodedLayout,
|
|
182
|
+
encodedLoaderData,
|
|
183
|
+
encodedLoaderDataPromise,
|
|
184
|
+
] = await Promise.all([
|
|
185
|
+
streamToString(stream),
|
|
186
|
+
segment.layout ? rscSerialize(segment.layout) : undefined,
|
|
187
|
+
rscSerialize(loaderDataResolved),
|
|
188
|
+
rscSerialize(loaderDataPromiseResolved),
|
|
189
|
+
]);
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
encoded,
|
|
193
|
+
encodedLayout,
|
|
194
|
+
encodedLoading,
|
|
195
|
+
encodedLoaderData,
|
|
196
|
+
encodedLoaderDataPromise,
|
|
197
|
+
metadata: {
|
|
198
|
+
id: segment.id,
|
|
199
|
+
type: segment.type,
|
|
200
|
+
namespace: segment.namespace,
|
|
201
|
+
index: segment.index,
|
|
202
|
+
params: segment.params,
|
|
203
|
+
slot: segment.slot,
|
|
204
|
+
belongsToRoute: segment.belongsToRoute,
|
|
205
|
+
layoutName: segment.layoutName,
|
|
206
|
+
parallelName: segment.parallelName,
|
|
207
|
+
loaderId: segment.loaderId,
|
|
208
|
+
loaderIds: segment.loaderIds,
|
|
209
|
+
transition: segment.transition,
|
|
210
|
+
mountPath: segment.mountPath,
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
}),
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Deserialize segments from storage.
|
|
219
|
+
* Reconstructs ResolvedSegment objects from RSC-serialized data.
|
|
220
|
+
*/
|
|
221
|
+
export async function deserializeSegments(
|
|
222
|
+
data: SerializedSegmentData[],
|
|
223
|
+
): Promise<ResolvedSegment[]> {
|
|
224
|
+
return Promise.all(
|
|
225
|
+
data.map(async (item): Promise<ResolvedSegment> => {
|
|
226
|
+
const temporaryReferences = createTemporaryReferenceSet();
|
|
227
|
+
|
|
228
|
+
// Handle the "null" sentinel for loading before RSC deserialization.
|
|
229
|
+
// During serialization, loading: null is stored as the string "null" to
|
|
230
|
+
// distinguish it from undefined.
|
|
231
|
+
const loadingIsNullSentinel = item.encodedLoading === "null";
|
|
232
|
+
|
|
233
|
+
const [component, layout, loaderData, loaderDataPromise, loadingData] =
|
|
234
|
+
await Promise.all([
|
|
235
|
+
createFromReadableStream(stringToStream(item.encoded), {
|
|
236
|
+
temporaryReferences,
|
|
237
|
+
}),
|
|
238
|
+
rscDeserialize(item.encodedLayout),
|
|
239
|
+
rscDeserialize(item.encodedLoaderData),
|
|
240
|
+
rscDeserialize(item.encodedLoaderDataPromise),
|
|
241
|
+
loadingIsNullSentinel
|
|
242
|
+
? (null as any)
|
|
243
|
+
: rscDeserialize(item.encodedLoading),
|
|
244
|
+
]);
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
...item.metadata,
|
|
248
|
+
component,
|
|
249
|
+
layout,
|
|
250
|
+
loading: loadingData,
|
|
251
|
+
loaderData,
|
|
252
|
+
loaderDataPromise,
|
|
253
|
+
} as ResolvedSegment;
|
|
254
|
+
}),
|
|
255
|
+
);
|
|
256
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Taint symbol for request-scoped objects.
|
|
3
|
+
*
|
|
4
|
+
* Objects branded with NOCACHE_SYMBOL (ctx, env, req) are excluded from
|
|
5
|
+
* "use cache" cache keys and trigger handle capture mode so that side
|
|
6
|
+
* effects (breadcrumbs, metadata) are recorded and replayed on cache hit.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const NOCACHE_SYMBOL: unique symbol = Symbol.for("rango:nocache") as any;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Check if a value is tainted (request-scoped, should not be in cache key).
|
|
13
|
+
*/
|
|
14
|
+
export function isTainted(value: unknown): boolean {
|
|
15
|
+
return (
|
|
16
|
+
value !== null &&
|
|
17
|
+
value !== undefined &&
|
|
18
|
+
typeof value === "object" &&
|
|
19
|
+
(NOCACHE_SYMBOL as symbol) in (value as Record<symbol, unknown>)
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Symbol stamped on tainted ctx during "use cache" function execution.
|
|
25
|
+
* cookies(), headers(), ctx.set(), ctx.header(), etc. check this flag and
|
|
26
|
+
* throw if present — reads would cache per-request data under a shared key,
|
|
27
|
+
* and side effects would be lost on cache hit.
|
|
28
|
+
*
|
|
29
|
+
* The value is a numeric reference count, not a boolean. Multiple concurrent
|
|
30
|
+
* cached functions sharing the same ctx/requestCtx each increment on entry
|
|
31
|
+
* and decrement on exit. Guards fire when count > 0.
|
|
32
|
+
*/
|
|
33
|
+
export const INSIDE_CACHE_EXEC: unique symbol = Symbol.for(
|
|
34
|
+
"rango:inside-cache-exec",
|
|
35
|
+
) as any;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Increment the INSIDE_CACHE_EXEC ref count on an object.
|
|
39
|
+
*/
|
|
40
|
+
export function stampCacheExec(obj: object): void {
|
|
41
|
+
const current = (obj as any)[INSIDE_CACHE_EXEC] ?? 0;
|
|
42
|
+
(obj as any)[INSIDE_CACHE_EXEC] = current + 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Decrement the INSIDE_CACHE_EXEC ref count on an object.
|
|
47
|
+
* Deletes the symbol when the count reaches zero so the `in` check
|
|
48
|
+
* used by guards no longer fires.
|
|
49
|
+
*/
|
|
50
|
+
export function unstampCacheExec(obj: object): void {
|
|
51
|
+
const current = (obj as any)[INSIDE_CACHE_EXEC] ?? 0;
|
|
52
|
+
if (current <= 1) {
|
|
53
|
+
delete (obj as any)[INSIDE_CACHE_EXEC];
|
|
54
|
+
} else {
|
|
55
|
+
(obj as any)[INSIDE_CACHE_EXEC] = current - 1;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Throw if ctx is inside a "use cache" execution.
|
|
61
|
+
* Call from side-effecting ctx methods (set, header, etc.) and cookie mutations.
|
|
62
|
+
*/
|
|
63
|
+
export function assertNotInsideCacheExec(
|
|
64
|
+
ctx: unknown,
|
|
65
|
+
methodName: string,
|
|
66
|
+
): void {
|
|
67
|
+
if (
|
|
68
|
+
ctx !== null &&
|
|
69
|
+
ctx !== undefined &&
|
|
70
|
+
typeof ctx === "object" &&
|
|
71
|
+
(INSIDE_CACHE_EXEC as symbol) in (ctx as Record<symbol, unknown>)
|
|
72
|
+
) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`ctx.${methodName}() cannot be called inside a "use cache" function. ` +
|
|
75
|
+
`Side effects on the request context are lost on cache hit because ` +
|
|
76
|
+
`the function body is skipped. Extract the data fetch into a separate ` +
|
|
77
|
+
`cached function and call ctx.${methodName}() outside it, or use the ` +
|
|
78
|
+
`route-level cache() DSL which caches all segments (handler + children) ` +
|
|
79
|
+
`together.`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Brand symbol for functions wrapped by registerCachedFunction().
|
|
86
|
+
* Used at runtime to detect when a "use cache" function is misused
|
|
87
|
+
* (e.g., passed as middleware).
|
|
88
|
+
*/
|
|
89
|
+
export const CACHED_FN_SYMBOL: unique symbol = Symbol.for(
|
|
90
|
+
"rango:cached-fn",
|
|
91
|
+
) as any;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Check if a value is a "use cache" wrapped function.
|
|
95
|
+
*/
|
|
96
|
+
export function isCachedFunction(value: unknown): boolean {
|
|
97
|
+
return typeof value === "function" && (CACHED_FN_SYMBOL as symbol) in value;
|
|
98
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Store Types
|
|
3
|
+
*
|
|
4
|
+
* Generic caching interface supporting multiple value types.
|
|
5
|
+
* Designed to be implemented by different backends:
|
|
6
|
+
* - MemoryCacheStore (dev/testing)
|
|
7
|
+
* - Cloudflare Cache API adapter
|
|
8
|
+
* - Cloudflare KV adapter
|
|
9
|
+
* - Redis adapter
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { ResolvedSegment } from "../types.js";
|
|
13
|
+
import type { RequestContext } from "../server/request-context.js";
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Segment Cache Store (low-level storage interface)
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Result from cache get() including data and revalidation status
|
|
21
|
+
*/
|
|
22
|
+
export interface CacheGetResult {
|
|
23
|
+
/** The cached entry data */
|
|
24
|
+
data: CachedEntryData;
|
|
25
|
+
/**
|
|
26
|
+
* Whether the caller should trigger background revalidation.
|
|
27
|
+
* True when entry is stale AND not already being revalidated.
|
|
28
|
+
* The store atomically marks the entry as REVALIDATING when returning true.
|
|
29
|
+
*/
|
|
30
|
+
shouldRevalidate: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Low-level segment cache store interface.
|
|
35
|
+
*
|
|
36
|
+
* Implementations handle the actual storage (memory, KV, Redis, etc.).
|
|
37
|
+
* The store deals with serialized data - RSC serialization is handled
|
|
38
|
+
* by the cache provider layer.
|
|
39
|
+
*
|
|
40
|
+
* @typeParam TEnv - Platform bindings type (e.g., Cloudflare env)
|
|
41
|
+
*/
|
|
42
|
+
export interface SegmentCacheStore<TEnv = unknown> {
|
|
43
|
+
/**
|
|
44
|
+
* Default cache options for this store.
|
|
45
|
+
* Used by cache() boundaries when ttl/swr are not explicitly specified.
|
|
46
|
+
*/
|
|
47
|
+
readonly defaults?: CacheDefaults;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Custom key generator applied to all cache operations using this store.
|
|
51
|
+
* Receives the full RequestContext and the default-generated key.
|
|
52
|
+
* Return value becomes the final cache key (unless route overrides with `key` option).
|
|
53
|
+
*
|
|
54
|
+
* Resolution priority:
|
|
55
|
+
* 1. Route-level `key` function (full override)
|
|
56
|
+
* 2. Store-level `keyGenerator` (modifies default key)
|
|
57
|
+
* 3. Default key generation (prefix:pathname:params)
|
|
58
|
+
*
|
|
59
|
+
* @example Using headers for cache segmentation
|
|
60
|
+
* ```typescript
|
|
61
|
+
* keyGenerator: (ctx, defaultKey) => {
|
|
62
|
+
* const segment = ctx.request.headers.get('x-user-segment') || 'default';
|
|
63
|
+
* return `${segment}:${defaultKey}`;
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* @example Using env bindings (Cloudflare)
|
|
68
|
+
* ```typescript
|
|
69
|
+
* keyGenerator: (ctx, defaultKey) => {
|
|
70
|
+
* const region = ctx.env.REGION || 'us';
|
|
71
|
+
* return `${region}:${defaultKey}`;
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
75
|
+
* @example Using cookies for locale
|
|
76
|
+
* ```typescript
|
|
77
|
+
* keyGenerator: (ctx, defaultKey) => {
|
|
78
|
+
* const locale = cookies().get('locale')?.value || 'en';
|
|
79
|
+
* return `${locale}:${defaultKey}`;
|
|
80
|
+
* }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
readonly keyGenerator?: (
|
|
84
|
+
ctx: RequestContext<TEnv>,
|
|
85
|
+
defaultKey: string,
|
|
86
|
+
) => string | Promise<string>;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get cached entry data by key
|
|
90
|
+
* @returns Cache result with data and staleness, or null if not found/expired
|
|
91
|
+
*/
|
|
92
|
+
get(key: string): Promise<CacheGetResult | null>;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Store entry data with TTL
|
|
96
|
+
* @param key - Cache key
|
|
97
|
+
* @param data - Serialized entry data
|
|
98
|
+
* @param ttl - Time-to-live in seconds
|
|
99
|
+
* @param swr - Optional stale-while-revalidate window in seconds
|
|
100
|
+
*/
|
|
101
|
+
set(
|
|
102
|
+
key: string,
|
|
103
|
+
data: CachedEntryData,
|
|
104
|
+
ttl: number,
|
|
105
|
+
swr?: number,
|
|
106
|
+
): Promise<void>;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Delete a cached entry
|
|
110
|
+
* @returns true if deleted, false if not found
|
|
111
|
+
*/
|
|
112
|
+
delete(key: string): Promise<boolean>;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Clear all cached entries (optional, for testing)
|
|
116
|
+
*/
|
|
117
|
+
clear?(): Promise<void>;
|
|
118
|
+
|
|
119
|
+
// ============================================================================
|
|
120
|
+
// Document Cache Methods (optional)
|
|
121
|
+
// ============================================================================
|
|
122
|
+
// These methods are for caching full HTTP responses (document-level caching).
|
|
123
|
+
// Stores that support response caching should implement these methods.
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get a cached Response by key.
|
|
127
|
+
* Returns the response and whether it should be revalidated (SWR).
|
|
128
|
+
*/
|
|
129
|
+
getResponse?(
|
|
130
|
+
key: string,
|
|
131
|
+
): Promise<{ response: Response; shouldRevalidate: boolean } | null>;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Store a Response with TTL and optional SWR window.
|
|
135
|
+
* @param key - Cache key
|
|
136
|
+
* @param response - Response to cache (will be cloned)
|
|
137
|
+
* @param ttl - Time-to-live in seconds
|
|
138
|
+
* @param swr - Optional stale-while-revalidate window in seconds
|
|
139
|
+
*/
|
|
140
|
+
putResponse?(
|
|
141
|
+
key: string,
|
|
142
|
+
response: Response,
|
|
143
|
+
ttl: number,
|
|
144
|
+
swr?: number,
|
|
145
|
+
): Promise<void>;
|
|
146
|
+
|
|
147
|
+
// ============================================================================
|
|
148
|
+
// Function Cache Methods (optional, for "use cache" directive)
|
|
149
|
+
// ============================================================================
|
|
150
|
+
// These methods cache individual function/component return values.
|
|
151
|
+
// Stores that support "use cache" should implement these methods.
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get a cached function result by key.
|
|
155
|
+
* Returns the serialized value, optional handle data, and staleness flag.
|
|
156
|
+
*/
|
|
157
|
+
getItem?(key: string): Promise<CacheItemResult | null>;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Store a function result with TTL and optional SWR window.
|
|
161
|
+
* @param key - Cache key (format: use-cache:{functionId}:{serializedArgs})
|
|
162
|
+
* @param value - RSC-serialized return value
|
|
163
|
+
* @param options - TTL, SWR, handle data, and tags
|
|
164
|
+
*/
|
|
165
|
+
setItem?(
|
|
166
|
+
key: string,
|
|
167
|
+
value: string,
|
|
168
|
+
options?: CacheItemOptions,
|
|
169
|
+
): Promise<void>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Result from getItem() for function-level caching ("use cache").
|
|
174
|
+
*/
|
|
175
|
+
export interface CacheItemResult {
|
|
176
|
+
/** RSC-serialized return value */
|
|
177
|
+
value: string;
|
|
178
|
+
/** Handle data captured during execution (breadcrumbs, metadata, etc.) */
|
|
179
|
+
handles?: Record<string, SegmentHandleData>;
|
|
180
|
+
/** Whether the entry is stale and should be revalidated */
|
|
181
|
+
shouldRevalidate: boolean;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Options for setItem() for function-level caching ("use cache").
|
|
186
|
+
*/
|
|
187
|
+
export interface CacheItemOptions {
|
|
188
|
+
/** Handle data to store alongside the value */
|
|
189
|
+
handles?: Record<string, SegmentHandleData>;
|
|
190
|
+
/** Time-to-live in seconds */
|
|
191
|
+
ttl?: number;
|
|
192
|
+
/** Stale-while-revalidate window in seconds */
|
|
193
|
+
swr?: number;
|
|
194
|
+
/** Cache tags for invalidation */
|
|
195
|
+
tags?: string[];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Serialized segment data stored in cache
|
|
200
|
+
* Note: loading is preserved to ensure consistent tree structure between cached and fresh renders
|
|
201
|
+
*
|
|
202
|
+
* @internal This type is an implementation detail and may change without notice.
|
|
203
|
+
*/
|
|
204
|
+
export interface SerializedSegmentData {
|
|
205
|
+
/** RSC-encoded component string */
|
|
206
|
+
encoded: string;
|
|
207
|
+
/** RSC-encoded layout string (if present) */
|
|
208
|
+
encodedLayout?: string;
|
|
209
|
+
/** RSC-encoded loading skeleton string (if present), or "null" for explicit null */
|
|
210
|
+
encodedLoading?: string;
|
|
211
|
+
/** RSC-encoded loaderData (if present) */
|
|
212
|
+
encodedLoaderData?: string;
|
|
213
|
+
/** RSC-encoded loaderDataPromise (if present) */
|
|
214
|
+
encodedLoaderDataPromise?: string;
|
|
215
|
+
/** Segment metadata (everything except component, layout, loading, and loader data) */
|
|
216
|
+
metadata: Omit<
|
|
217
|
+
ResolvedSegment,
|
|
218
|
+
"component" | "layout" | "loading" | "loaderData" | "loaderDataPromise"
|
|
219
|
+
>;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Raw data stored in cache for an entry
|
|
224
|
+
*
|
|
225
|
+
* @internal This type is an implementation detail and may change without notice.
|
|
226
|
+
*/
|
|
227
|
+
export interface CachedEntryData {
|
|
228
|
+
/** Serialized segments for this entry */
|
|
229
|
+
segments: SerializedSegmentData[];
|
|
230
|
+
/** Handle data keyed by segment ID */
|
|
231
|
+
handles: Record<string, SegmentHandleData>;
|
|
232
|
+
/** Expiration timestamp (ms since epoch) */
|
|
233
|
+
expiresAt: number;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ============================================================================
|
|
237
|
+
// Cache Configuration
|
|
238
|
+
// ============================================================================
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Default cache options applied to all cache() boundaries.
|
|
242
|
+
* Individual cache() calls can override any of these values.
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* ```ts
|
|
246
|
+
* const store = new CFCacheStore({
|
|
247
|
+
* defaults: { ttl: 60, swr: 300 }
|
|
248
|
+
* });
|
|
249
|
+
* ```
|
|
250
|
+
*/
|
|
251
|
+
export interface CacheDefaults {
|
|
252
|
+
/**
|
|
253
|
+
* Default time-to-live in seconds.
|
|
254
|
+
* After TTL expires, cached entry is considered stale.
|
|
255
|
+
*/
|
|
256
|
+
ttl?: number;
|
|
257
|
+
/**
|
|
258
|
+
* Default stale-while-revalidate window in seconds.
|
|
259
|
+
* During SWR window, stale content is served while revalidating in background.
|
|
260
|
+
*/
|
|
261
|
+
swr?: number;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Cache configuration for RSC handler
|
|
266
|
+
*/
|
|
267
|
+
export interface CacheConfig {
|
|
268
|
+
/** Cache store implementation (includes defaults) */
|
|
269
|
+
store: SegmentCacheStore;
|
|
270
|
+
/** Enable/disable caching (default: true) */
|
|
271
|
+
enabled?: boolean;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Cache configuration - can be static or a function receiving env
|
|
276
|
+
*/
|
|
277
|
+
export type CacheConfigOrFactory<TEnv> =
|
|
278
|
+
| CacheConfig
|
|
279
|
+
| ((env: TEnv) => CacheConfig);
|
|
280
|
+
|
|
281
|
+
// ============================================================================
|
|
282
|
+
// Segment Cache Provider (request-level interface)
|
|
283
|
+
// ============================================================================
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Handle data for a single segment
|
|
287
|
+
* Structure: { handleName: [values...] }
|
|
288
|
+
*/
|
|
289
|
+
export type SegmentHandleData = Record<string, unknown[]>;
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Result from cache get() including segments and their handle data
|
|
293
|
+
* Each entry can produce multiple segments (main + parallels)
|
|
294
|
+
*/
|
|
295
|
+
export interface CachedEntryResult {
|
|
296
|
+
/** All segments for this entry (main segment + parallels) */
|
|
297
|
+
segments: ResolvedSegment[];
|
|
298
|
+
/** Handle data keyed by segment ID */
|
|
299
|
+
handles: Record<string, SegmentHandleData>;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Segment cache provider interface
|
|
304
|
+
*
|
|
305
|
+
* Used by router to check/store segment cache during matching.
|
|
306
|
+
* Accessed via request context - if not present, caching is disabled.
|
|
307
|
+
*
|
|
308
|
+
* @internal Not currently implemented - CacheScope is used directly.
|
|
309
|
+
* Reserved for future extensibility.
|
|
310
|
+
*/
|
|
311
|
+
export interface SegmentCacheProvider {
|
|
312
|
+
/** Whether caching is enabled for this request */
|
|
313
|
+
readonly enabled: boolean;
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Get cached segments and restore handles/loaders.
|
|
317
|
+
*
|
|
318
|
+
* Combines cache get with handle replay and loader data restoration.
|
|
319
|
+
* Returns tuple of [segments, segmentIds] if cache hit, null if miss or disabled.
|
|
320
|
+
*
|
|
321
|
+
* @param cacheKey - Cache key to look up
|
|
322
|
+
* @param params - Route params for cache key generation
|
|
323
|
+
* @param loaderPromises - Map to restore loader data into
|
|
324
|
+
* @returns Tuple of [segments, segmentIds] or null if miss
|
|
325
|
+
*/
|
|
326
|
+
restore(
|
|
327
|
+
cacheKey: string,
|
|
328
|
+
params: Record<string, string>,
|
|
329
|
+
loaderPromises: Map<string, Promise<any>>,
|
|
330
|
+
): Promise<[ResolvedSegment[], string[]] | null>;
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Cache entry with automatic handle collection (non-blocking).
|
|
334
|
+
*
|
|
335
|
+
* Schedules caching via waitUntil - handles are collected after they settle.
|
|
336
|
+
* Validates segments have actual components before caching.
|
|
337
|
+
*
|
|
338
|
+
* @param cacheKey - The cache key to store under
|
|
339
|
+
* @param segments - All resolved segments for this entry
|
|
340
|
+
*/
|
|
341
|
+
cacheEntry(cacheKey: string, segments: ResolvedSegment[]): void;
|
|
342
|
+
}
|