@rangojs/router 0.0.0-experimental.0f44aca1
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 +5 -0
- package/README.md +899 -0
- package/dist/bin/rango.js +1601 -0
- package/dist/vite/index.js +5214 -0
- package/package.json +176 -0
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +220 -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 +645 -0
- package/src/browser/navigation-client.ts +215 -0
- package/src/browser/navigation-store.ts +806 -0
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +550 -0
- package/src/browser/prefetch/cache.ts +146 -0
- package/src/browser/prefetch/fetch.ts +135 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/prefetch/queue.ts +88 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +360 -0
- package/src/browser/react/NavigationProvider.tsx +386 -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 +431 -0
- package/src/browser/scroll-restoration.ts +400 -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 +538 -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 +469 -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 +540 -0
- package/src/cache/cf/index.ts +25 -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 +43 -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 +275 -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 +158 -0
- package/src/router/handler-context.ts +451 -0
- package/src/router/intercept-resolution.ts +395 -0
- package/src/router/lazy-includes.ts +234 -0
- package/src/router/loader-resolution.ts +420 -0
- package/src/router/logging.ts +248 -0
- package/src/router/manifest.ts +267 -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 +192 -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 +748 -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 +316 -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 +1239 -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 +289 -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 +1002 -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 +235 -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 +914 -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 +102 -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 +110 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +16 -0
- package/src/vite/plugin-types.ts +131 -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 +365 -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 +254 -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 +510 -0
- package/src/vite/router-discovery.ts +785 -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,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "use cache" Runtime
|
|
3
|
+
*
|
|
4
|
+
* Provides the runtime wrapper for functions marked with "use cache" directive.
|
|
5
|
+
* The Vite transform plugin wraps exports with registerCachedFunction().
|
|
6
|
+
*
|
|
7
|
+
* On cache miss: executes the function, serializes the result via RSC Flight
|
|
8
|
+
* protocol, captures handle data if tainted ctx is detected, and stores in
|
|
9
|
+
* the SegmentCacheStore.
|
|
10
|
+
*
|
|
11
|
+
* On cache hit: deserializes the cached result, restores handle data if present.
|
|
12
|
+
*
|
|
13
|
+
* On stale hit: returns stale data immediately, triggers background
|
|
14
|
+
* re-execution via waitUntil().
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/// <reference types="@vitejs/plugin-rsc/types" />
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
encodeReply,
|
|
21
|
+
createClientTemporaryReferenceSet,
|
|
22
|
+
} from "@vitejs/plugin-rsc/rsc";
|
|
23
|
+
import { getRequestContext } from "../server/request-context.js";
|
|
24
|
+
import {
|
|
25
|
+
isTainted,
|
|
26
|
+
CACHED_FN_SYMBOL,
|
|
27
|
+
isCachedFunction,
|
|
28
|
+
stampCacheExec,
|
|
29
|
+
unstampCacheExec,
|
|
30
|
+
} from "./taint.js";
|
|
31
|
+
|
|
32
|
+
export { isCachedFunction };
|
|
33
|
+
import { serializeResult, deserializeResult } from "./segment-codec.js";
|
|
34
|
+
import { createHandleStore } from "../server/handle-store.js";
|
|
35
|
+
import { restoreHandles } from "./handle-snapshot.js";
|
|
36
|
+
import { startHandleCapture, type HandleCapture } from "./handle-capture.js";
|
|
37
|
+
import { sortedSearchString } from "./cache-key-utils.js";
|
|
38
|
+
import { runBackground } from "./background-task.js";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Convert encodeReply result to a stable string key.
|
|
42
|
+
* encodeReply may return string or FormData — normalize to string.
|
|
43
|
+
*/
|
|
44
|
+
async function replyToCacheKey(encoded: string | FormData): Promise<string> {
|
|
45
|
+
if (typeof encoded === "string") return encoded;
|
|
46
|
+
// FormData: convert to Response body, then to string for deterministic key
|
|
47
|
+
const text = await new Response(encoded).text();
|
|
48
|
+
return text;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Core: registerCachedFunction
|
|
53
|
+
// ============================================================================
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Register a function as a cached function.
|
|
57
|
+
* Called by the Vite transform for each "use cache" function.
|
|
58
|
+
*
|
|
59
|
+
* @param fn - The original async function
|
|
60
|
+
* @param id - Stable identifier (module path + export name)
|
|
61
|
+
* @param profileName - Cache profile name (from "use cache: profileName" or "default")
|
|
62
|
+
*/
|
|
63
|
+
export function registerCachedFunction<T extends (...args: any[]) => any>(
|
|
64
|
+
fn: T,
|
|
65
|
+
id: string,
|
|
66
|
+
profileName: string,
|
|
67
|
+
): T {
|
|
68
|
+
const wrapped = async function (this: any, ...args: any[]): Promise<any> {
|
|
69
|
+
const requestCtx = getRequestContext();
|
|
70
|
+
const store = requestCtx?._cacheStore;
|
|
71
|
+
const resolvedProfileName = profileName || "default";
|
|
72
|
+
|
|
73
|
+
// Bypass: no store or no getItem support
|
|
74
|
+
if (!store?.getItem) {
|
|
75
|
+
return fn.apply(this, args);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Resolve profile strictly from request-scoped config (set by the
|
|
79
|
+
// active router via createRequestContext). No global fallback —
|
|
80
|
+
// global profile state is only for DSL-time cache("profileName").
|
|
81
|
+
const profile = requestCtx?._cacheProfiles?.[resolvedProfileName];
|
|
82
|
+
|
|
83
|
+
if (!profile) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`[use cache] "${id}" uses unknown cache profile "${resolvedProfileName}". ` +
|
|
86
|
+
`Define it in createRouter({ cacheProfiles: { "${resolvedProfileName}": { ttl: ... } } }).`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Separate tainted args (ctx, env, req) from key-generating args.
|
|
91
|
+
// For tainted objects that carry route context (params, pathname,
|
|
92
|
+
// searchParams), extract serializable values into the key so
|
|
93
|
+
// different routes, param combinations, and query variants produce
|
|
94
|
+
// distinct cache entries.
|
|
95
|
+
const keyArgs: unknown[] = [];
|
|
96
|
+
let hasTaintedArgs = false;
|
|
97
|
+
for (const arg of args) {
|
|
98
|
+
if (isTainted(arg)) {
|
|
99
|
+
hasTaintedArgs = true;
|
|
100
|
+
const ctx = arg as any;
|
|
101
|
+
if (ctx.params && typeof ctx.params === "object") {
|
|
102
|
+
// Include host to prevent cross-host cache collisions (same
|
|
103
|
+
// pattern as route-level cache-scope.ts key generation).
|
|
104
|
+
if (ctx.url?.host) {
|
|
105
|
+
keyArgs.push(ctx.url.host);
|
|
106
|
+
}
|
|
107
|
+
// Include route name to prevent collisions when the same cached
|
|
108
|
+
// function is reused across routes with identical pathname/params
|
|
109
|
+
// but different local reverse() scope.
|
|
110
|
+
if (ctx._routeName) {
|
|
111
|
+
keyArgs.push(ctx._routeName);
|
|
112
|
+
}
|
|
113
|
+
keyArgs.push(ctx.pathname, ctx.params);
|
|
114
|
+
if (ctx._responseType) {
|
|
115
|
+
keyArgs.push(ctx._responseType);
|
|
116
|
+
}
|
|
117
|
+
// Include user-facing search params (exclude internal _rsc*/__ params)
|
|
118
|
+
if (ctx.searchParams instanceof URLSearchParams) {
|
|
119
|
+
const normalized = sortedSearchString(ctx.searchParams);
|
|
120
|
+
if (normalized) {
|
|
121
|
+
keyArgs.push(normalized);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
keyArgs.push(arg);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// If tainted args are present, we need the handle store for capture/restore.
|
|
131
|
+
// During late streaming (Suspense boundary resolution), ALS context may be
|
|
132
|
+
// gone. Throw early rather than silently dropping handle side effects.
|
|
133
|
+
if (hasTaintedArgs && !requestCtx?._handleStore) {
|
|
134
|
+
throw new Error(
|
|
135
|
+
`[use cache] "${id}" receives a tainted argument (ctx/env/req) but the ` +
|
|
136
|
+
`HandleStore is not available. This typically happens when a "use cache" ` +
|
|
137
|
+
`function with ctx runs outside the request context (e.g., during late ` +
|
|
138
|
+
`streaming after AsyncLocalStorage context is lost). Move the "use cache" ` +
|
|
139
|
+
`directive to a function that does not receive request-scoped objects, or ` +
|
|
140
|
+
`use the route-level cache() DSL instead.`,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Generate cache key
|
|
145
|
+
let cacheKey: string;
|
|
146
|
+
try {
|
|
147
|
+
if (keyArgs.length > 0) {
|
|
148
|
+
const tempRefs = createClientTemporaryReferenceSet();
|
|
149
|
+
const encoded = await encodeReply(keyArgs as unknown[], {
|
|
150
|
+
temporaryReferences: tempRefs,
|
|
151
|
+
});
|
|
152
|
+
const argsKey = await replyToCacheKey(encoded);
|
|
153
|
+
cacheKey = `use-cache:${id}:${argsKey}`;
|
|
154
|
+
} else {
|
|
155
|
+
cacheKey = `use-cache:${id}`;
|
|
156
|
+
}
|
|
157
|
+
} catch {
|
|
158
|
+
// Non-serializable args: run uncached
|
|
159
|
+
return fn.apply(this, args);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Cache lookup
|
|
163
|
+
const cached = await store.getItem(cacheKey);
|
|
164
|
+
|
|
165
|
+
if (cached && !cached.shouldRevalidate) {
|
|
166
|
+
// Fresh hit: deserialize and return
|
|
167
|
+
try {
|
|
168
|
+
const result = await deserializeResult(cached.value);
|
|
169
|
+
// Restore handle data if present
|
|
170
|
+
if (cached.handles && hasTaintedArgs) {
|
|
171
|
+
const handleStore = requestCtx?._handleStore;
|
|
172
|
+
if (handleStore) {
|
|
173
|
+
restoreHandles(cached.handles, handleStore);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return result;
|
|
177
|
+
} catch {
|
|
178
|
+
// Deserialization failed, fall through to fresh execution
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (cached?.shouldRevalidate) {
|
|
183
|
+
// Stale hit: return stale value, revalidate in background
|
|
184
|
+
try {
|
|
185
|
+
const result = await deserializeResult(cached.value);
|
|
186
|
+
if (cached.handles && hasTaintedArgs) {
|
|
187
|
+
const handleStore = requestCtx?._handleStore;
|
|
188
|
+
if (handleStore) {
|
|
189
|
+
restoreHandles(cached.handles, handleStore);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Background revalidation — must capture handles if tainted args present.
|
|
193
|
+
// Use an isolated handle store so background pushes don't pollute the
|
|
194
|
+
// live response or throw LateHandlePushError on the completed store.
|
|
195
|
+
// Same isolation pattern as route-level background-revalidation.ts.
|
|
196
|
+
runBackground(requestCtx, async () => {
|
|
197
|
+
// Reuse closure-captured requestCtx instead of calling
|
|
198
|
+
// getRequestContext() — ALS context may be gone inside waitUntil.
|
|
199
|
+
let originalHandleStore:
|
|
200
|
+
| ReturnType<typeof createHandleStore>
|
|
201
|
+
| undefined;
|
|
202
|
+
if (hasTaintedArgs && requestCtx) {
|
|
203
|
+
originalHandleStore = requestCtx._handleStore;
|
|
204
|
+
requestCtx._handleStore = createHandleStore();
|
|
205
|
+
}
|
|
206
|
+
const bgHandleStore = hasTaintedArgs
|
|
207
|
+
? requestCtx?._handleStore
|
|
208
|
+
: undefined;
|
|
209
|
+
let bgCapture: HandleCapture | undefined;
|
|
210
|
+
let bgStopCapture: (() => void) | undefined;
|
|
211
|
+
if (bgHandleStore) {
|
|
212
|
+
const c = startHandleCapture(bgHandleStore);
|
|
213
|
+
bgCapture = c.capture;
|
|
214
|
+
bgStopCapture = c.stop;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Stamp tainted args and RequestContext so request-scoped
|
|
218
|
+
// reads (cookies, headers) and side effects (ctx.set, etc.)
|
|
219
|
+
// throw inside background revalidation, same as the miss path.
|
|
220
|
+
// Uses ref-counted stamp/unstamp so overlapping executions
|
|
221
|
+
// sharing the same ctx don't clear each other's guards.
|
|
222
|
+
const bgTaintedArgs: unknown[] = [];
|
|
223
|
+
for (const arg of args) {
|
|
224
|
+
if (isTainted(arg)) {
|
|
225
|
+
stampCacheExec(arg as object);
|
|
226
|
+
bgTaintedArgs.push(arg);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (requestCtx) {
|
|
230
|
+
stampCacheExec(requestCtx as object);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
const freshResult = await fn.apply(this, args);
|
|
235
|
+
bgStopCapture?.();
|
|
236
|
+
const serialized = await serializeResult(freshResult);
|
|
237
|
+
if (serialized !== null) {
|
|
238
|
+
await store.setItem!(cacheKey, serialized, {
|
|
239
|
+
handles: bgCapture?.data,
|
|
240
|
+
ttl: profile.ttl,
|
|
241
|
+
swr: profile.swr,
|
|
242
|
+
tags: profile.tags,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
} catch (bgError) {
|
|
246
|
+
bgStopCapture?.();
|
|
247
|
+
requestCtx?._reportBackgroundError?.(bgError, "stale-revalidation");
|
|
248
|
+
} finally {
|
|
249
|
+
for (const arg of bgTaintedArgs) {
|
|
250
|
+
unstampCacheExec(arg as object);
|
|
251
|
+
}
|
|
252
|
+
if (requestCtx) {
|
|
253
|
+
unstampCacheExec(requestCtx as object);
|
|
254
|
+
}
|
|
255
|
+
// Restore original handle store
|
|
256
|
+
if (originalHandleStore && requestCtx) {
|
|
257
|
+
requestCtx._handleStore = originalHandleStore;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
return result;
|
|
262
|
+
} catch {
|
|
263
|
+
// Deserialization of stale value failed, fall through
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Cache miss: execute, serialize, store
|
|
268
|
+
const handleStore = hasTaintedArgs ? requestCtx?._handleStore : undefined;
|
|
269
|
+
let capture: HandleCapture | undefined;
|
|
270
|
+
let stopCapture: (() => void) | undefined;
|
|
271
|
+
if (handleStore && hasTaintedArgs) {
|
|
272
|
+
const c = startHandleCapture(handleStore);
|
|
273
|
+
capture = c.capture;
|
|
274
|
+
stopCapture = c.stop;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Stamp tainted args so ctx.set(), ctx.header(), etc. throw if called
|
|
278
|
+
// inside the cached function body (those side effects are lost on hit).
|
|
279
|
+
// Uses ref-counted stamp/unstamp so overlapping executions
|
|
280
|
+
// sharing the same ctx don't clear each other's guards.
|
|
281
|
+
const taintedArgs: unknown[] = [];
|
|
282
|
+
for (const arg of args) {
|
|
283
|
+
if (isTainted(arg)) {
|
|
284
|
+
stampCacheExec(arg as object);
|
|
285
|
+
taintedArgs.push(arg);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Always stamp the ALS RequestContext so cookies()/headers() guards fire
|
|
289
|
+
// even when the cached function receives no tainted args. The guard in
|
|
290
|
+
// cookie-store.ts checks RequestContext, not function args.
|
|
291
|
+
if (requestCtx) {
|
|
292
|
+
stampCacheExec(requestCtx as object);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
let result: any;
|
|
296
|
+
try {
|
|
297
|
+
result = await fn.apply(this, args);
|
|
298
|
+
} finally {
|
|
299
|
+
// Decrement ref count; symbol is deleted when it reaches zero
|
|
300
|
+
for (const arg of taintedArgs) {
|
|
301
|
+
unstampCacheExec(arg as object);
|
|
302
|
+
}
|
|
303
|
+
if (requestCtx) {
|
|
304
|
+
unstampCacheExec(requestCtx as object);
|
|
305
|
+
}
|
|
306
|
+
// Remove this capture token (order-independent, safe for concurrent use)
|
|
307
|
+
stopCapture?.();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Serialize and store — fully non-blocking when waitUntil is available.
|
|
311
|
+
// The response does not need to wait for serialization or the store write.
|
|
312
|
+
const cacheWrite = async () => {
|
|
313
|
+
try {
|
|
314
|
+
const serialized = await serializeResult(result);
|
|
315
|
+
if (serialized !== null) {
|
|
316
|
+
await store.setItem!(cacheKey, serialized, {
|
|
317
|
+
handles: capture?.data,
|
|
318
|
+
ttl: profile.ttl,
|
|
319
|
+
swr: profile.swr,
|
|
320
|
+
tags: profile.tags,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
} catch (writeError) {
|
|
324
|
+
requestCtx?._reportBackgroundError?.(writeError, "cache-write");
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
await runBackground(requestCtx, cacheWrite, true);
|
|
329
|
+
|
|
330
|
+
return result;
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// Brand the wrapper so it can be detected at runtime (e.g., to prevent
|
|
334
|
+
// accidental use as middleware).
|
|
335
|
+
(wrapped as any)[CACHED_FN_SYMBOL] = true;
|
|
336
|
+
|
|
337
|
+
return wrapped as unknown as T;
|
|
338
|
+
}
|