@rangojs/router 0.0.0-experimental.5 → 0.0.0-experimental.50
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 +884 -4
- package/dist/bin/rango.js +1606 -0
- package/dist/vite/index.js +4567 -769
- package/package.json +77 -58
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +85 -23
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +12 -8
- package/skills/document-cache/SKILL.md +18 -16
- package/skills/fonts/SKILL.md +167 -0
- package/skills/hooks/SKILL.md +334 -72
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +131 -8
- package/skills/layout/SKILL.md +100 -3
- package/skills/links/SKILL.md +89 -30
- package/skills/loader/SKILL.md +388 -38
- package/skills/middleware/SKILL.md +171 -34
- package/skills/mime-routes/SKILL.md +128 -0
- package/skills/parallel/SKILL.md +204 -1
- package/skills/prerender/SKILL.md +643 -0
- package/skills/rango/SKILL.md +85 -16
- package/skills/response-routes/SKILL.md +411 -0
- package/skills/route/SKILL.md +226 -14
- package/skills/router-setup/SKILL.md +123 -30
- package/skills/tailwind/SKILL.md +129 -0
- package/skills/theme/SKILL.md +9 -8
- package/skills/typesafety/SKILL.md +318 -89
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +102 -4
- 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 +92 -64
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +24 -4
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +20 -12
- package/src/browser/navigation-bridge.ts +282 -557
- package/src/browser/navigation-client.ts +157 -71
- package/src/browser/navigation-store.ts +33 -50
- package/src/browser/navigation-transaction.ts +297 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +303 -310
- package/src/browser/prefetch/cache.ts +206 -0
- package/src/browser/prefetch/fetch.ts +144 -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 +193 -73
- package/src/browser/react/NavigationProvider.tsx +160 -13
- package/src/browser/react/context.ts +6 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +12 -12
- package/src/browser/react/location-state-shared.ts +95 -53
- package/src/browser/react/location-state.ts +60 -15
- package/src/browser/react/mount-context.ts +24 -1
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +29 -51
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +32 -79
- package/src/browser/react/use-href.tsx +2 -2
- package/src/browser/react/use-link-status.ts +6 -5
- package/src/browser/react/use-navigation.ts +22 -63
- 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 +80 -97
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +188 -55
- package/src/browser/scroll-restoration.ts +117 -44
- package/src/browser/segment-reconciler.ts +221 -0
- package/src/browser/segment-structure-assert.ts +16 -0
- package/src/browser/server-action-bridge.ts +504 -599
- package/src/browser/shallow.ts +6 -1
- package/src/browser/types.ts +118 -47
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +235 -24
- package/src/build/generate-route-types.ts +36 -0
- package/src/build/index.ts +13 -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 +342 -0
- package/src/cache/cache-scope.ts +167 -309
- package/src/cache/cf/cf-cache-store.ts +571 -17
- package/src/cache/cf/index.ts +13 -3
- package/src/cache/document-cache.ts +116 -77
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +1 -15
- package/src/cache/memory-segment-store.ts +191 -13
- 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 +72 -122
- package/src/client.rsc.tsx +3 -1
- package/src/client.tsx +106 -126
- package/src/component-utils.ts +4 -4
- package/src/components/DefaultDocument.tsx +5 -1
- package/src/context-var.ts +86 -0
- package/src/debug.ts +19 -9
- package/src/errors.ts +108 -2
- package/src/handle.ts +15 -29
- package/src/handles/MetaTags.tsx +73 -20
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/handles/meta.ts +30 -13
- 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 +119 -29
- package/src/index.rsc.ts +153 -19
- package/src/index.ts +211 -30
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +26 -147
- package/src/loader.ts +27 -10
- package/src/network-error-thrower.tsx +3 -1
- 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 +41 -29
- package/src/route-content-wrapper.tsx +7 -4
- package/src/route-definition/dsl-helpers.ts +959 -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 -1428
- package/src/route-map-builder.ts +217 -123
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +59 -8
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +9 -9
- package/src/router/find-match.ts +160 -0
- package/src/router/handler-context.ts +374 -81
- package/src/router/intercept-resolution.ts +397 -0
- package/src/router/lazy-includes.ts +237 -0
- package/src/router/loader-resolution.ts +215 -122
- package/src/router/logging.ts +251 -0
- package/src/router/manifest.ts +154 -35
- package/src/router/match-api.ts +620 -0
- package/src/router/match-context.ts +5 -3
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +108 -93
- package/src/router/match-middleware/cache-lookup.ts +440 -10
- package/src/router/match-middleware/cache-store.ts +98 -26
- package/src/router/match-middleware/intercept-resolution.ts +57 -17
- package/src/router/match-middleware/segment-resolution.ts +27 -6
- package/src/router/match-pipelines.ts +10 -45
- package/src/router/match-result.ts +55 -33
- package/src/router/metrics.ts +240 -15
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +327 -369
- package/src/router/pattern-matching.ts +211 -43
- package/src/router/prerender-match.ts +402 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +137 -38
- package/src/router/router-context.ts +41 -21
- 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 +677 -0
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +199 -0
- package/src/router/segment-resolution/revalidation.ts +1296 -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 +77 -3
- package/src/router.ts +665 -4182
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +764 -754
- package/src/rsc/helpers.ts +140 -6
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +14 -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 +38 -11
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +172 -21
- package/src/server/context.ts +266 -58
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +94 -15
- package/src/server/loader-registry.ts +15 -56
- package/src/server/request-context.ts +439 -73
- package/src/server.ts +35 -128
- package/src/ssr/index.tsx +101 -31
- package/src/static-handler.ts +114 -0
- package/src/theme/ThemeProvider.tsx +21 -15
- package/src/theme/ThemeScript.tsx +5 -5
- package/src/theme/constants.ts +5 -2
- package/src/theme/index.ts +4 -14
- package/src/theme/theme-context.ts +4 -30
- package/src/theme/theme-script.ts +21 -18
- 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 +773 -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 +150 -0
- package/src/types.ts +1 -1623
- 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 -802
- package/src/use-loader.tsx +85 -77
- 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 +11 -782
- 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/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -53
- 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/{virtual-entries.ts → plugins/virtual-entries.ts} +27 -16
- 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/{package-resolution.ts → utils/package-resolution.ts} +25 -29
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -0
- package/CLAUDE.md +0 -43
- package/src/browser/lru-cache.ts +0 -69
- package/src/browser/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- package/src/href.ts +0 -255
- package/src/server/route-manifest-cache.ts +0 -173
- package/src/vite/expose-handle-id.ts +0 -209
- package/src/vite/expose-loader-id.ts +0 -426
- package/src/vite/expose-location-state-id.ts +0 -177
- package/src/warmup/connection-warmup.tsx +0 -94
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
|
@@ -6,6 +6,30 @@
|
|
|
6
6
|
|
|
7
7
|
import type { ResolvedSegment, HandlerContext } from "../types";
|
|
8
8
|
import type { ActionContext } from "./types";
|
|
9
|
+
import {
|
|
10
|
+
debugLog,
|
|
11
|
+
pushRevalidationTraceEntry,
|
|
12
|
+
isTraceActive,
|
|
13
|
+
} from "./logging.js";
|
|
14
|
+
import type { RevalidationTraceEntry } from "./logging.js";
|
|
15
|
+
import { _getRequestContext } from "../server/request-context.js";
|
|
16
|
+
import { isAutoGeneratedRouteName } from "../route-name.js";
|
|
17
|
+
|
|
18
|
+
function paramsEqual(
|
|
19
|
+
a: Record<string, string>,
|
|
20
|
+
b: Record<string, string>,
|
|
21
|
+
): boolean {
|
|
22
|
+
if (a === b) return true;
|
|
23
|
+
|
|
24
|
+
const keysA = Object.keys(a);
|
|
25
|
+
if (keysA.length !== Object.keys(b).length) return false;
|
|
26
|
+
|
|
27
|
+
for (const key of keysA) {
|
|
28
|
+
if (a[key] !== b[key]) return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
9
33
|
|
|
10
34
|
/**
|
|
11
35
|
* Options for revalidation evaluation
|
|
@@ -33,6 +57,8 @@ interface EvaluateRevalidationOptions<TEnv> {
|
|
|
33
57
|
actionContext?: ActionContext;
|
|
34
58
|
/** If true, this is a stale cache revalidation request */
|
|
35
59
|
stale?: boolean;
|
|
60
|
+
/** Trace source hint for the revalidation trace */
|
|
61
|
+
traceSource?: RevalidationTraceEntry["source"];
|
|
36
62
|
}
|
|
37
63
|
|
|
38
64
|
/**
|
|
@@ -40,7 +66,7 @@ interface EvaluateRevalidationOptions<TEnv> {
|
|
|
40
66
|
* Optimized to use prevParams directly and avoid building previous segments
|
|
41
67
|
*/
|
|
42
68
|
export async function evaluateRevalidation<TEnv>(
|
|
43
|
-
options: EvaluateRevalidationOptions<TEnv
|
|
69
|
+
options: EvaluateRevalidationOptions<TEnv>,
|
|
44
70
|
): Promise<boolean> {
|
|
45
71
|
const {
|
|
46
72
|
segment,
|
|
@@ -54,71 +80,118 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
54
80
|
context,
|
|
55
81
|
actionContext,
|
|
56
82
|
stale,
|
|
83
|
+
traceSource,
|
|
57
84
|
} = options;
|
|
58
85
|
const nextParams = segment.params || {};
|
|
59
|
-
const paramsChanged =
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
86
|
+
const paramsChanged = !paramsEqual(nextParams, prevParams);
|
|
87
|
+
const searchChanged = prevUrl.search !== nextUrl.search;
|
|
88
|
+
|
|
89
|
+
// Trace helper: push a structured entry to the request-scoped trace buffer.
|
|
90
|
+
// Guarded by isTraceActive() so object construction is skipped in production.
|
|
91
|
+
function pushTrace(
|
|
92
|
+
defaultVal: boolean,
|
|
93
|
+
finalVal: boolean,
|
|
94
|
+
reason: string,
|
|
95
|
+
): void {
|
|
96
|
+
if (!isTraceActive()) return;
|
|
97
|
+
pushRevalidationTraceEntry({
|
|
98
|
+
segmentId: segment.id,
|
|
99
|
+
segmentType: segment.type,
|
|
100
|
+
belongsToRoute: segment.belongsToRoute ?? false,
|
|
101
|
+
source: traceSource ?? "segment-resolution",
|
|
102
|
+
defaultShouldRevalidate: defaultVal,
|
|
103
|
+
finalShouldRevalidate: finalVal,
|
|
104
|
+
reason,
|
|
105
|
+
customRevalidators: revalidations.length || undefined,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
64
108
|
|
|
65
109
|
// Calculate default revalidation based on segment type and request method
|
|
66
110
|
let defaultShouldRevalidate: boolean;
|
|
111
|
+
let defaultReason: string;
|
|
67
112
|
|
|
68
113
|
if (request.method === "POST") {
|
|
69
114
|
// Actions: revalidate segments that belong to the route, skip parent chain
|
|
70
115
|
if (segment.type === "route") {
|
|
71
116
|
// Route segment always revalidates on actions
|
|
72
117
|
defaultShouldRevalidate = true;
|
|
118
|
+
defaultReason = "action:route-segment";
|
|
73
119
|
} else if (segment.type === "loader") {
|
|
74
120
|
// Loaders always revalidate on actions - they often contain action-sensitive data
|
|
75
121
|
// (e.g., cart count after add-to-cart action)
|
|
76
122
|
defaultShouldRevalidate = true;
|
|
123
|
+
defaultReason = "action:loader-segment";
|
|
77
124
|
} else if (segment.belongsToRoute) {
|
|
78
125
|
// Segment belongs to route (orphan layouts/parallels) - revalidate
|
|
79
126
|
defaultShouldRevalidate = true;
|
|
127
|
+
defaultReason = "action:belongs-to-route";
|
|
80
128
|
} else {
|
|
81
129
|
// Parent chain segment (shared layouts/parallels) - don't revalidate
|
|
82
130
|
defaultShouldRevalidate = false;
|
|
131
|
+
defaultReason = "action:parent-chain-skip";
|
|
83
132
|
}
|
|
84
133
|
} else {
|
|
85
134
|
// Navigation (GET): Conservative defaults to minimize unnecessary revalidations
|
|
86
135
|
// Only the route segment revalidates by default - all others require explicit opt-in
|
|
87
136
|
|
|
88
137
|
if (segment.type === "route") {
|
|
89
|
-
// Route segments revalidate when params change
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
138
|
+
// Route segments revalidate when path params OR search params change.
|
|
139
|
+
// Search params (e.g., ?page=2&sort=price) are server-parsed via ctx.search,
|
|
140
|
+
// so the handler must re-execute to produce updated content.
|
|
141
|
+
const routeChanged = paramsChanged || searchChanged;
|
|
142
|
+
defaultShouldRevalidate = routeChanged;
|
|
143
|
+
defaultReason = paramsChanged
|
|
144
|
+
? "nav:params-changed"
|
|
145
|
+
: searchChanged
|
|
146
|
+
? "nav:search-changed"
|
|
147
|
+
: "nav:params-unchanged";
|
|
148
|
+
if (routeChanged) {
|
|
149
|
+
debugLog("revalidation", "route revalidating", {
|
|
150
|
+
segmentId: segment.id,
|
|
151
|
+
paramsChanged,
|
|
152
|
+
searchChanged,
|
|
153
|
+
});
|
|
96
154
|
}
|
|
155
|
+
} else if (segment.belongsToRoute && (paramsChanged || searchChanged)) {
|
|
156
|
+
// Children of the route path (loaders, orphan layouts/parallels)
|
|
157
|
+
// revalidate when path params or search params change
|
|
158
|
+
defaultShouldRevalidate = true;
|
|
159
|
+
defaultReason = paramsChanged
|
|
160
|
+
? "nav:route-child-params-changed"
|
|
161
|
+
: "nav:route-child-search-changed";
|
|
162
|
+
debugLog("revalidation", "route child revalidating", {
|
|
163
|
+
segmentId: segment.id,
|
|
164
|
+
segmentType: segment.type,
|
|
165
|
+
paramsChanged,
|
|
166
|
+
searchChanged,
|
|
167
|
+
});
|
|
97
168
|
} else {
|
|
98
|
-
//
|
|
169
|
+
// Parent layouts and parallels default to no revalidation
|
|
99
170
|
// Cannot assume these segments depend on params without explicit declaration
|
|
100
171
|
// Use custom revalidation functions to opt-in when needed
|
|
101
172
|
defaultShouldRevalidate = false;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
);
|
|
173
|
+
defaultReason = "nav:non-route-skip";
|
|
174
|
+
debugLog("revalidation", "non-route segment skipped by default", {
|
|
175
|
+
segmentId: segment.id,
|
|
176
|
+
segmentType: segment.type,
|
|
177
|
+
});
|
|
107
178
|
}
|
|
108
179
|
}
|
|
109
180
|
|
|
110
181
|
// No custom revalidations defined - return default behavior without prev segment
|
|
111
182
|
if (revalidations.length === 0) {
|
|
112
183
|
if (defaultShouldRevalidate) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
184
|
+
debugLog("revalidation", "default revalidate=true", {
|
|
185
|
+
segmentId: segment.id,
|
|
186
|
+
prevParams,
|
|
187
|
+
nextParams,
|
|
188
|
+
});
|
|
117
189
|
} else {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
);
|
|
190
|
+
debugLog("revalidation", "default revalidate=false", {
|
|
191
|
+
segmentId: segment.id,
|
|
192
|
+
});
|
|
121
193
|
}
|
|
194
|
+
pushTrace(defaultShouldRevalidate, defaultShouldRevalidate, defaultReason);
|
|
122
195
|
return defaultShouldRevalidate;
|
|
123
196
|
}
|
|
124
197
|
|
|
@@ -129,6 +202,16 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
129
202
|
// Execute revalidation functions with soft/hard decision pattern
|
|
130
203
|
let currentSuggestion = defaultShouldRevalidate;
|
|
131
204
|
|
|
205
|
+
// Compute public route names (filtered: undefined for auto-generated routes)
|
|
206
|
+
const toRouteName =
|
|
207
|
+
routeKey && !isAutoGeneratedRouteName(routeKey) ? routeKey : undefined;
|
|
208
|
+
const reqCtx = _getRequestContext();
|
|
209
|
+
const prevRouteKey = reqCtx?._prevRouteKey;
|
|
210
|
+
const fromRouteName =
|
|
211
|
+
prevRouteKey && !isAutoGeneratedRouteName(prevRouteKey)
|
|
212
|
+
? prevRouteKey
|
|
213
|
+
: undefined;
|
|
214
|
+
|
|
132
215
|
for (const { name, fn } of revalidations) {
|
|
133
216
|
const result = fn({
|
|
134
217
|
currentParams: prevSegment?.params || prevParams, // Use segment params if available, else route params
|
|
@@ -147,7 +230,9 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
147
230
|
actionResult: actionContext?.actionResult,
|
|
148
231
|
formData: actionContext?.formData,
|
|
149
232
|
method: request.method, // GET for navigation, POST for actions
|
|
150
|
-
routeName:
|
|
233
|
+
routeName: toRouteName, // Navigation target route name (filtered)
|
|
234
|
+
fromRouteName, // Navigation source route name (filtered)
|
|
235
|
+
toRouteName, // Navigation target route name (filtered)
|
|
151
236
|
// Stale cache context (only true for background revalidation after stale cache render)
|
|
152
237
|
stale,
|
|
153
238
|
});
|
|
@@ -158,9 +243,12 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
158
243
|
// - null/undefined: use default behavior (equivalent to returning { defaultShouldRevalidate })
|
|
159
244
|
if (typeof result === "boolean") {
|
|
160
245
|
// Hard decision - short-circuit
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
246
|
+
debugLog("revalidation", "hard decision", {
|
|
247
|
+
segmentId: segment.id,
|
|
248
|
+
revalidator: name,
|
|
249
|
+
revalidate: result,
|
|
250
|
+
});
|
|
251
|
+
pushTrace(defaultShouldRevalidate, result, `hard:${name}`);
|
|
164
252
|
return result;
|
|
165
253
|
} else if (
|
|
166
254
|
result &&
|
|
@@ -169,22 +257,33 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
169
257
|
) {
|
|
170
258
|
// Soft decision - update suggestion and continue
|
|
171
259
|
currentSuggestion = result.defaultShouldRevalidate;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
260
|
+
debugLog("revalidation", "soft decision", {
|
|
261
|
+
segmentId: segment.id,
|
|
262
|
+
revalidator: name,
|
|
263
|
+
revalidate: currentSuggestion,
|
|
264
|
+
});
|
|
175
265
|
} else if (result === null || result === undefined) {
|
|
176
266
|
// Defer to default - equivalent to { defaultShouldRevalidate: currentSuggestion }
|
|
177
267
|
// This means "I don't care, use whatever the default is"
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
268
|
+
debugLog("revalidation", "deferred to current default", {
|
|
269
|
+
segmentId: segment.id,
|
|
270
|
+
revalidator: name,
|
|
271
|
+
revalidate: currentSuggestion,
|
|
272
|
+
});
|
|
181
273
|
// currentSuggestion stays the same, continue to next function
|
|
182
274
|
}
|
|
183
275
|
}
|
|
184
276
|
|
|
185
277
|
// All revalidators completed - use final suggestion
|
|
186
|
-
|
|
187
|
-
|
|
278
|
+
debugLog("revalidation", "final decision", {
|
|
279
|
+
segmentId: segment.id,
|
|
280
|
+
revalidate: currentSuggestion,
|
|
281
|
+
});
|
|
282
|
+
const softNames = revalidations.map((r) => r.name).join(",");
|
|
283
|
+
pushTrace(
|
|
284
|
+
defaultShouldRevalidate,
|
|
285
|
+
currentSuggestion,
|
|
286
|
+
`soft-chain:${softNames}`,
|
|
188
287
|
);
|
|
189
288
|
return currentSuggestion;
|
|
190
289
|
}
|
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
ShouldRevalidateFn,
|
|
19
19
|
} from "../types.js";
|
|
20
20
|
import type { RouteMatchResult } from "./pattern-matching.js";
|
|
21
|
+
import type { TelemetrySink } from "./telemetry.js";
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Revalidation context passed to segment resolution
|
|
@@ -62,7 +63,7 @@ export interface RouterContext<TEnv = any> {
|
|
|
62
63
|
routeKey: string,
|
|
63
64
|
pathname: string,
|
|
64
65
|
metricsStore?: MetricsStore,
|
|
65
|
-
isSSR?: boolean
|
|
66
|
+
isSSR?: boolean,
|
|
66
67
|
) => Promise<EntryData>;
|
|
67
68
|
|
|
68
69
|
// Entry traversal
|
|
@@ -77,18 +78,20 @@ export interface RouterContext<TEnv = any> {
|
|
|
77
78
|
url: URL,
|
|
78
79
|
bindings?: any,
|
|
79
80
|
routeMap?: Record<string, string>,
|
|
80
|
-
routeName?: string
|
|
81
|
+
routeName?: string,
|
|
82
|
+
responseType?: string,
|
|
83
|
+
isPassthroughRoute?: boolean,
|
|
81
84
|
) => HandlerContext<any, TEnv>;
|
|
82
85
|
|
|
83
86
|
// Loader setup
|
|
84
87
|
setupLoaderAccess: (
|
|
85
88
|
ctx: HandlerContext<any, TEnv>,
|
|
86
|
-
loaderPromises: Map<string, Promise<any
|
|
89
|
+
loaderPromises: Map<string, Promise<any>>,
|
|
87
90
|
) => void;
|
|
88
91
|
|
|
89
92
|
setupLoaderAccessSilent: (
|
|
90
93
|
ctx: HandlerContext<any, TEnv>,
|
|
91
|
-
loaderPromises: Map<string, Promise<any
|
|
94
|
+
loaderPromises: Map<string, Promise<any>>,
|
|
92
95
|
) => void;
|
|
93
96
|
|
|
94
97
|
// Context access
|
|
@@ -98,7 +101,7 @@ export interface RouterContext<TEnv = any> {
|
|
|
98
101
|
store: any,
|
|
99
102
|
namespace: string,
|
|
100
103
|
parent: any,
|
|
101
|
-
fn: () => T
|
|
104
|
+
fn: () => T,
|
|
102
105
|
) => T;
|
|
103
106
|
};
|
|
104
107
|
|
|
@@ -108,7 +111,7 @@ export interface RouterContext<TEnv = any> {
|
|
|
108
111
|
// Cache
|
|
109
112
|
createCacheScope: (
|
|
110
113
|
cacheConfig: any,
|
|
111
|
-
parent: CacheScope | null
|
|
114
|
+
parent: CacheScope | null,
|
|
112
115
|
) => CacheScope | null;
|
|
113
116
|
|
|
114
117
|
// Intercept detection
|
|
@@ -116,7 +119,7 @@ export interface RouterContext<TEnv = any> {
|
|
|
116
119
|
routeKey: string,
|
|
117
120
|
parentEntry: EntryData | null,
|
|
118
121
|
selectorContext: InterceptSelectorContext,
|
|
119
|
-
isAction: boolean
|
|
122
|
+
isAction: boolean,
|
|
120
123
|
) => InterceptResult | null;
|
|
121
124
|
|
|
122
125
|
// Segment resolution (with revalidation)
|
|
@@ -134,7 +137,8 @@ export interface RouterContext<TEnv = any> {
|
|
|
134
137
|
actionContext: any | undefined,
|
|
135
138
|
interceptResult: InterceptResult | null,
|
|
136
139
|
localRouteName: string,
|
|
137
|
-
pathname: string
|
|
140
|
+
pathname: string,
|
|
141
|
+
stale?: boolean,
|
|
138
142
|
) => Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }>;
|
|
139
143
|
|
|
140
144
|
// Generator-based segment resolution (for pipeline)
|
|
@@ -149,7 +153,7 @@ export interface RouterContext<TEnv = any> {
|
|
|
149
153
|
prevUrl: URL,
|
|
150
154
|
nextUrl: URL,
|
|
151
155
|
loaderPromises: Map<string, Promise<any>>,
|
|
152
|
-
actionContext?: any
|
|
156
|
+
actionContext?: any,
|
|
153
157
|
) => AsyncGenerator<ResolvedSegment | { __type: "id"; id: string }>;
|
|
154
158
|
|
|
155
159
|
// Intercept resolution
|
|
@@ -159,12 +163,12 @@ export interface RouterContext<TEnv = any> {
|
|
|
159
163
|
params: Record<string, string>,
|
|
160
164
|
handlerContext: HandlerContext<any, TEnv>,
|
|
161
165
|
belongsToRoute: boolean,
|
|
162
|
-
revalidationContext?: RevalidationContext
|
|
166
|
+
revalidationContext?: RevalidationContext,
|
|
163
167
|
) => Promise<ResolvedSegment[]>;
|
|
164
168
|
|
|
165
169
|
// Collect with markers
|
|
166
170
|
collectWithMarkers?: <T>(
|
|
167
|
-
gen: AsyncGenerator<T | { __type: "id"; id: string }
|
|
171
|
+
gen: AsyncGenerator<T | { __type: "id"; id: string }>,
|
|
168
172
|
) => Promise<{ items: T[]; matchedIds: string[] }>;
|
|
169
173
|
|
|
170
174
|
// Revalidation evaluation
|
|
@@ -180,6 +184,15 @@ export interface RouterContext<TEnv = any> {
|
|
|
180
184
|
context: HandlerContext<any, TEnv>;
|
|
181
185
|
actionContext?: any;
|
|
182
186
|
stale?: boolean;
|
|
187
|
+
traceSource?:
|
|
188
|
+
| "segment-resolution"
|
|
189
|
+
| "cache-hit"
|
|
190
|
+
| "loader"
|
|
191
|
+
| "parallel"
|
|
192
|
+
| "orphan-layout"
|
|
193
|
+
| "route-handler"
|
|
194
|
+
| "layout-handler"
|
|
195
|
+
| "intercept-loader";
|
|
183
196
|
}) => Promise<boolean>;
|
|
184
197
|
|
|
185
198
|
// Request context
|
|
@@ -196,7 +209,8 @@ export interface RouterContext<TEnv = any> {
|
|
|
196
209
|
routeKey: string,
|
|
197
210
|
params: Record<string, string>,
|
|
198
211
|
handlerContext: HandlerContext<any, TEnv>,
|
|
199
|
-
loaderPromises: Map<string, Promise<any
|
|
212
|
+
loaderPromises: Map<string, Promise<any>>,
|
|
213
|
+
options?: { skipLoaders?: boolean },
|
|
200
214
|
) => Promise<ResolvedSegment[]>;
|
|
201
215
|
|
|
202
216
|
// Generator-based simple resolution
|
|
@@ -205,12 +219,12 @@ export interface RouterContext<TEnv = any> {
|
|
|
205
219
|
routeKey: string,
|
|
206
220
|
params: Record<string, string>,
|
|
207
221
|
handlerContext: HandlerContext<any, TEnv>,
|
|
208
|
-
loaderPromises: Map<string, Promise<any
|
|
222
|
+
loaderPromises: Map<string, Promise<any>>,
|
|
209
223
|
) => AsyncGenerator<ResolvedSegment | { __type: "id"; id: string }>;
|
|
210
224
|
|
|
211
225
|
// Collect segments from generator
|
|
212
226
|
collectSegmentsFromGenerator?: <T>(
|
|
213
|
-
gen: AsyncGenerator<T | { __type: "id"; id: string }
|
|
227
|
+
gen: AsyncGenerator<T | { __type: "id"; id: string }>,
|
|
214
228
|
) => Promise<T[]>;
|
|
215
229
|
|
|
216
230
|
// Handle store
|
|
@@ -219,7 +233,7 @@ export interface RouterContext<TEnv = any> {
|
|
|
219
233
|
// Loaders-only resolution (for full match cache hit - no revalidation)
|
|
220
234
|
resolveLoadersOnly?: (
|
|
221
235
|
entries: EntryData[],
|
|
222
|
-
handlerContext: HandlerContext<any, TEnv
|
|
236
|
+
handlerContext: HandlerContext<any, TEnv>,
|
|
223
237
|
) => Promise<ResolvedSegment[]>;
|
|
224
238
|
|
|
225
239
|
// Loaders-only resolution (for cache hit scenarios)
|
|
@@ -232,14 +246,21 @@ export interface RouterContext<TEnv = any> {
|
|
|
232
246
|
prevUrl: URL,
|
|
233
247
|
nextUrl: URL,
|
|
234
248
|
routeKey: string,
|
|
235
|
-
actionContext?: any
|
|
249
|
+
actionContext?: any,
|
|
250
|
+
stale?: boolean,
|
|
236
251
|
) => Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }>;
|
|
237
252
|
|
|
238
253
|
// Entry revalidation map
|
|
239
254
|
buildEntryRevalidateMap?: (
|
|
240
|
-
entries: EntryData[]
|
|
255
|
+
entries: EntryData[],
|
|
241
256
|
) => Map<string, { revalidate: ShouldRevalidateFn[] }>;
|
|
242
257
|
|
|
258
|
+
// Telemetry sink (optional, no-op when undefined)
|
|
259
|
+
telemetry?: TelemetrySink;
|
|
260
|
+
|
|
261
|
+
// Request ID for telemetry span correlation (set per-request in match handlers)
|
|
262
|
+
requestId?: string;
|
|
263
|
+
|
|
243
264
|
// Intercept loaders only (for cache hit + intercept scenarios)
|
|
244
265
|
resolveInterceptLoadersOnly?: (
|
|
245
266
|
intercept: InterceptEntry,
|
|
@@ -256,7 +277,7 @@ export interface RouterContext<TEnv = any> {
|
|
|
256
277
|
routeKey: string;
|
|
257
278
|
actionContext?: any;
|
|
258
279
|
stale?: boolean;
|
|
259
|
-
}
|
|
280
|
+
},
|
|
260
281
|
) => Promise<{
|
|
261
282
|
loaderDataPromise: Promise<any[]> | any[];
|
|
262
283
|
loaderIds: string[];
|
|
@@ -276,7 +297,7 @@ export function getRouterContext<TEnv = any>(): RouterContext<TEnv> {
|
|
|
276
297
|
if (!deps) {
|
|
277
298
|
throw new Error(
|
|
278
299
|
"getRouterContext() called outside of router context. " +
|
|
279
|
-
"Ensure code is running inside runWithRouterContext()."
|
|
300
|
+
"Ensure code is running inside runWithRouterContext().",
|
|
280
301
|
);
|
|
281
302
|
}
|
|
282
303
|
return deps as RouterContext<TEnv>;
|
|
@@ -294,8 +315,7 @@ export function getRouterContext<TEnv = any>(): RouterContext<TEnv> {
|
|
|
294
315
|
*/
|
|
295
316
|
export function runWithRouterContext<T, TEnv = any>(
|
|
296
317
|
deps: RouterContext<TEnv>,
|
|
297
|
-
fn: () => T
|
|
318
|
+
fn: () => T,
|
|
298
319
|
): T {
|
|
299
320
|
return routerContext.run(deps, fn);
|
|
300
321
|
}
|
|
301
|
-
|