@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,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CacheScope - Runtime cache scope for iterator-based caching
|
|
3
|
+
*
|
|
4
|
+
* Each cache() boundary in the route tree creates a new CacheScope.
|
|
5
|
+
* The scope owns: config, key management, and storage operations.
|
|
6
|
+
*
|
|
7
|
+
* Serialization is delegated to segment-codec.ts.
|
|
8
|
+
* Handle data capture/restore is delegated to handle-snapshot.ts.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { PartialCacheOptions } from "../types.js";
|
|
12
|
+
import type { ResolvedSegment } from "../types.js";
|
|
13
|
+
import type { SegmentCacheStore, CachedEntryData } from "./types.js";
|
|
14
|
+
import { INTERNAL_RANGO_DEBUG } from "../internal-debug.js";
|
|
15
|
+
import {
|
|
16
|
+
getRequestContext,
|
|
17
|
+
_getRequestContext,
|
|
18
|
+
} from "../server/request-context.js";
|
|
19
|
+
import { serializeSegments, deserializeSegments } from "./segment-codec.js";
|
|
20
|
+
import { captureHandles, restoreHandles } from "./handle-snapshot.js";
|
|
21
|
+
import { sortedSearchString, sortedRouteParams } from "./cache-key-utils.js";
|
|
22
|
+
import {
|
|
23
|
+
DEFAULT_ROUTE_TTL,
|
|
24
|
+
resolveCacheKey,
|
|
25
|
+
resolveCacheStore,
|
|
26
|
+
} from "./cache-policy.js";
|
|
27
|
+
|
|
28
|
+
function debugCacheLog(message: string): void {
|
|
29
|
+
if (INTERNAL_RANGO_DEBUG) {
|
|
30
|
+
console.log(message);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// Key Generation (internal)
|
|
36
|
+
// ============================================================================
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generate cache key base from host, pathname, route params, and search params.
|
|
40
|
+
* Host is included to prevent cross-host cache collisions on shared stores.
|
|
41
|
+
* Route params and search params are sorted alphabetically for deterministic keys.
|
|
42
|
+
* Internal _rsc* and __* query params are excluded.
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
function getCacheKeyBase(
|
|
46
|
+
host: string,
|
|
47
|
+
pathname: string,
|
|
48
|
+
params?: Record<string, string>,
|
|
49
|
+
searchParams?: URLSearchParams,
|
|
50
|
+
): string {
|
|
51
|
+
const paramStr = sortedRouteParams(params);
|
|
52
|
+
const searchStr = searchParams ? sortedSearchString(searchParams) : "";
|
|
53
|
+
|
|
54
|
+
let key = `${host}${pathname}`;
|
|
55
|
+
if (paramStr) key += `:${paramStr}`;
|
|
56
|
+
if (searchStr) key += `?${searchStr}`;
|
|
57
|
+
return key;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Generate default cache key for a route request.
|
|
62
|
+
* Includes pathname, route params, and user-facing search params for
|
|
63
|
+
* correct scoping. Internal _rsc* params are excluded.
|
|
64
|
+
* Includes request type prefix since they produce different segment sets:
|
|
65
|
+
* - doc: document requests (full page load)
|
|
66
|
+
* - partial: navigation requests (client-side navigation)
|
|
67
|
+
* - intercept: intercept navigation (modal/overlay routes)
|
|
68
|
+
* @internal
|
|
69
|
+
*/
|
|
70
|
+
function getDefaultRouteCacheKey(
|
|
71
|
+
pathname: string,
|
|
72
|
+
params?: Record<string, string>,
|
|
73
|
+
isIntercept?: boolean,
|
|
74
|
+
): string {
|
|
75
|
+
const ctx = getRequestContext();
|
|
76
|
+
const isPartial = ctx?.url.searchParams.has("_rsc_partial") ?? false;
|
|
77
|
+
const searchParams = ctx?.url.searchParams;
|
|
78
|
+
const host = ctx?.url.host ?? "localhost";
|
|
79
|
+
|
|
80
|
+
// Intercept navigations get their own cache namespace
|
|
81
|
+
const prefix = isIntercept ? "intercept" : isPartial ? "partial" : "doc";
|
|
82
|
+
|
|
83
|
+
return `${prefix}:${getCacheKeyBase(host, pathname, params, searchParams)}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ============================================================================
|
|
87
|
+
// CacheScope
|
|
88
|
+
// ============================================================================
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* CacheScope represents a cache boundary in the route tree.
|
|
92
|
+
*
|
|
93
|
+
* When withCache encounters an entry with cache config, it creates
|
|
94
|
+
* a new CacheScope. The scope owns key management, TTL resolution,
|
|
95
|
+
* and storage operations. Serialization is handled by segment-codec.ts.
|
|
96
|
+
*
|
|
97
|
+
* Store resolution priority:
|
|
98
|
+
* 1. Explicit store in cache() options
|
|
99
|
+
* 2. App-level store from handler config
|
|
100
|
+
*
|
|
101
|
+
* TTL resolution priority:
|
|
102
|
+
* 1. Explicit value in cache() options
|
|
103
|
+
* 2. Explicit store's defaults (if store specified)
|
|
104
|
+
* 3. App-level store's defaults
|
|
105
|
+
* 4. Hardcoded fallback (60 seconds)
|
|
106
|
+
*/
|
|
107
|
+
export class CacheScope {
|
|
108
|
+
readonly config: PartialCacheOptions | false;
|
|
109
|
+
readonly parent: CacheScope | null;
|
|
110
|
+
/** Explicit store from cache() options, if specified */
|
|
111
|
+
private readonly explicitStore: SegmentCacheStore | undefined;
|
|
112
|
+
|
|
113
|
+
constructor(
|
|
114
|
+
config: PartialCacheOptions | false,
|
|
115
|
+
parent: CacheScope | null = null,
|
|
116
|
+
) {
|
|
117
|
+
this.config = config;
|
|
118
|
+
this.parent = parent;
|
|
119
|
+
// Extract and store explicit store reference
|
|
120
|
+
this.explicitStore = config !== false ? config.store : undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Whether caching is enabled for this scope
|
|
125
|
+
*/
|
|
126
|
+
get enabled(): boolean {
|
|
127
|
+
return this.config !== false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get effective TTL from config or store defaults
|
|
132
|
+
*/
|
|
133
|
+
get ttl(): number {
|
|
134
|
+
if (this.config === false) return 0;
|
|
135
|
+
|
|
136
|
+
// Explicit TTL in cache() options
|
|
137
|
+
if (this.config.ttl !== undefined) {
|
|
138
|
+
return this.config.ttl;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Fall back to store defaults (explicit store first, then app-level)
|
|
142
|
+
const store = this.getStore();
|
|
143
|
+
if (store?.defaults?.ttl !== undefined) {
|
|
144
|
+
return store.defaults.ttl;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Hardcoded fallback
|
|
148
|
+
return DEFAULT_ROUTE_TTL;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get SWR window from config or store defaults
|
|
153
|
+
*/
|
|
154
|
+
get swr(): number | undefined {
|
|
155
|
+
if (this.config === false) return undefined;
|
|
156
|
+
|
|
157
|
+
// Explicit SWR in cache() options
|
|
158
|
+
if (this.config.swr !== undefined) {
|
|
159
|
+
return this.config.swr;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Fall back to store defaults
|
|
163
|
+
const store = this.getStore();
|
|
164
|
+
return store?.defaults?.swr;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get the cache store - resolution priority:
|
|
169
|
+
* 1. Explicit store from cache() options
|
|
170
|
+
* 2. App-level store from request context
|
|
171
|
+
*/
|
|
172
|
+
getStore(): SegmentCacheStore | null {
|
|
173
|
+
return resolveCacheStore(this.explicitStore);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Resolve the cache key using the shared 3-tier priority.
|
|
178
|
+
* @internal
|
|
179
|
+
*/
|
|
180
|
+
private async resolveKey(
|
|
181
|
+
pathname: string,
|
|
182
|
+
params: Record<string, string>,
|
|
183
|
+
isIntercept?: boolean,
|
|
184
|
+
): Promise<string> {
|
|
185
|
+
const defaultKey = getDefaultRouteCacheKey(pathname, params, isIntercept);
|
|
186
|
+
const keyFn = this.config !== false ? this.config.key : undefined;
|
|
187
|
+
return resolveCacheKey(keyFn, this.getStore(), defaultKey, "CacheScope");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Lookup cached segments for a route (single cache entry per request).
|
|
192
|
+
* Returns { segments, shouldRevalidate } or null if cache miss.
|
|
193
|
+
*
|
|
194
|
+
* @param pathname - URL pathname for cache key generation
|
|
195
|
+
* @param params - Route params for cache key generation
|
|
196
|
+
* @param isIntercept - Whether this is an intercept navigation (uses different cache key)
|
|
197
|
+
*/
|
|
198
|
+
async lookupRoute(
|
|
199
|
+
pathname: string,
|
|
200
|
+
params: Record<string, string>,
|
|
201
|
+
isIntercept?: boolean,
|
|
202
|
+
): Promise<{
|
|
203
|
+
segments: ResolvedSegment[];
|
|
204
|
+
shouldRevalidate: boolean;
|
|
205
|
+
} | null> {
|
|
206
|
+
if (!this.enabled) return null;
|
|
207
|
+
|
|
208
|
+
// Evaluate condition — skip cache read when condition returns false
|
|
209
|
+
if (this.config !== false && this.config.condition) {
|
|
210
|
+
const requestCtx = getRequestContext();
|
|
211
|
+
if (requestCtx) {
|
|
212
|
+
try {
|
|
213
|
+
if (!this.config.condition(requestCtx)) {
|
|
214
|
+
debugCacheLog(
|
|
215
|
+
`[CacheScope] condition returned false, skipping cache read`,
|
|
216
|
+
);
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
} catch (error) {
|
|
220
|
+
console.error(
|
|
221
|
+
`[CacheScope] condition function threw, skipping cache read:`,
|
|
222
|
+
error,
|
|
223
|
+
);
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const store = this.getStore();
|
|
230
|
+
if (!store) return null;
|
|
231
|
+
|
|
232
|
+
// Resolve cache key (may use custom key functions)
|
|
233
|
+
const key = await this.resolveKey(pathname, params, isIntercept);
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
const result = await store.get(key);
|
|
237
|
+
|
|
238
|
+
if (!result) {
|
|
239
|
+
debugCacheLog(`[CacheScope] MISS: ${key}`);
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const { data: cached, shouldRevalidate } = result;
|
|
244
|
+
|
|
245
|
+
// Deserialize segments
|
|
246
|
+
const segments = await deserializeSegments(cached.segments);
|
|
247
|
+
|
|
248
|
+
// Replay handle data
|
|
249
|
+
const handleStore = _getRequestContext()?._handleStore;
|
|
250
|
+
if (handleStore) {
|
|
251
|
+
restoreHandles(cached.handles, handleStore);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (INTERNAL_RANGO_DEBUG) {
|
|
255
|
+
const segmentTypes = segments.map((s) =>
|
|
256
|
+
s.type === "parallel" ? s.slot : s.type,
|
|
257
|
+
);
|
|
258
|
+
debugCacheLog(
|
|
259
|
+
`[CacheScope] ${shouldRevalidate ? "STALE" : "HIT"}: ${key} (${segmentTypes.join(", ")})`,
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return { segments, shouldRevalidate };
|
|
264
|
+
} catch (error) {
|
|
265
|
+
console.error(`[CacheScope] Failed to lookup ${key}:`, error);
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Cache all segments for a route (non-blocking via waitUntil)
|
|
272
|
+
* Single cache entry per route request.
|
|
273
|
+
* Loaders are excluded - they're always fresh unless they have their own cache() config.
|
|
274
|
+
*
|
|
275
|
+
* @param pathname - URL pathname for cache key generation
|
|
276
|
+
* @param params - Route params for cache key generation
|
|
277
|
+
* @param segments - All resolved segments to cache
|
|
278
|
+
* @param isIntercept - Whether this is an intercept navigation (uses different cache key)
|
|
279
|
+
*/
|
|
280
|
+
async cacheRoute(
|
|
281
|
+
pathname: string,
|
|
282
|
+
params: Record<string, string>,
|
|
283
|
+
segments: ResolvedSegment[],
|
|
284
|
+
isIntercept?: boolean,
|
|
285
|
+
): Promise<void> {
|
|
286
|
+
if (!this.enabled || segments.length === 0) return;
|
|
287
|
+
|
|
288
|
+
// Evaluate condition — skip cache write when condition returns false
|
|
289
|
+
if (this.config !== false && this.config.condition) {
|
|
290
|
+
const conditionCtx = getRequestContext();
|
|
291
|
+
if (conditionCtx) {
|
|
292
|
+
try {
|
|
293
|
+
if (!this.config.condition(conditionCtx)) {
|
|
294
|
+
debugCacheLog(
|
|
295
|
+
`[CacheScope] condition returned false, skipping cache write`,
|
|
296
|
+
);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
} catch (error) {
|
|
300
|
+
console.error(
|
|
301
|
+
`[CacheScope] condition function threw, skipping cache write:`,
|
|
302
|
+
error,
|
|
303
|
+
);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const store = this.getStore();
|
|
310
|
+
if (!store) return;
|
|
311
|
+
|
|
312
|
+
const requestCtx = getRequestContext();
|
|
313
|
+
const handleStore = requestCtx?._handleStore;
|
|
314
|
+
|
|
315
|
+
if (!handleStore || !requestCtx) return;
|
|
316
|
+
|
|
317
|
+
// Exclude loader segments - loaders are always fresh by default
|
|
318
|
+
// Loaders can opt-in to caching with their own cache() config
|
|
319
|
+
const nonLoaderSegments = segments.filter((s) => s.type !== "loader");
|
|
320
|
+
if (nonLoaderSegments.length === 0) return;
|
|
321
|
+
|
|
322
|
+
const ttl = this.ttl;
|
|
323
|
+
const swr = this.swr;
|
|
324
|
+
|
|
325
|
+
// Resolve cache key early (while request context is available)
|
|
326
|
+
const key = await this.resolveKey(pathname, params, isIntercept);
|
|
327
|
+
|
|
328
|
+
// Check if this is a partial request (navigation) vs document request
|
|
329
|
+
const isPartial = requestCtx.url.searchParams.has("_rsc_partial");
|
|
330
|
+
|
|
331
|
+
requestCtx.waitUntil(async () => {
|
|
332
|
+
await handleStore.settled;
|
|
333
|
+
|
|
334
|
+
// For document requests: only cache if ALL segments have components (complete render)
|
|
335
|
+
// For partial requests: null components are expected (client already has them)
|
|
336
|
+
if (!isPartial) {
|
|
337
|
+
const hasAllComponents = nonLoaderSegments.every(
|
|
338
|
+
(s) => s.component !== null,
|
|
339
|
+
);
|
|
340
|
+
if (!hasAllComponents) return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Collect handle data for non-loader segments only
|
|
344
|
+
const handles = captureHandles(nonLoaderSegments, handleStore);
|
|
345
|
+
|
|
346
|
+
try {
|
|
347
|
+
// Serialize non-loader segments only
|
|
348
|
+
const serializedSegments = await serializeSegments(nonLoaderSegments);
|
|
349
|
+
|
|
350
|
+
const data: CachedEntryData = {
|
|
351
|
+
segments: serializedSegments,
|
|
352
|
+
handles,
|
|
353
|
+
expiresAt: Date.now() + ttl * 1000,
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
await store.set(key, data, ttl, swr);
|
|
357
|
+
|
|
358
|
+
if (INTERNAL_RANGO_DEBUG) {
|
|
359
|
+
const segmentTypes = nonLoaderSegments.map((s) =>
|
|
360
|
+
s.type === "parallel" ? s.slot : s.type,
|
|
361
|
+
);
|
|
362
|
+
debugCacheLog(
|
|
363
|
+
`[CacheScope] Cached: ${key} (${segmentTypes.join(", ")}) ttl=${ttl}s [loaders excluded]`,
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
} catch (error) {
|
|
367
|
+
console.error(`[CacheScope] Failed to cache ${key}:`, error);
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Create a cache scope from entry's cache config
|
|
375
|
+
*/
|
|
376
|
+
export function createCacheScope(
|
|
377
|
+
config: { options: PartialCacheOptions | false } | undefined,
|
|
378
|
+
parent: CacheScope | null = null,
|
|
379
|
+
): CacheScope | null {
|
|
380
|
+
if (!config) return parent; // No config, inherit parent
|
|
381
|
+
return new CacheScope(config.options, parent);
|
|
382
|
+
}
|