@rangojs/router 0.0.0-experimental.8 → 0.0.0-experimental.8a4d0430
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 +884 -4
- package/dist/bin/rango.js +1601 -0
- package/dist/vite/index.js +4474 -867
- package/package.json +60 -51
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +50 -21
- 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 +78 -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 +87 -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 +285 -553
- package/src/browser/navigation-client.ts +124 -71
- package/src/browser/navigation-store.ts +33 -50
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +258 -308
- 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 +185 -73
- package/src/browser/react/NavigationProvider.tsx +51 -11
- 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 +6 -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 +107 -26
- package/src/browser/scroll-restoration.ts +92 -16
- package/src/browser/segment-reconciler.ts +216 -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 +109 -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 +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 +120 -303
- package/src/cache/cf/cf-cache-store.ts +119 -7
- package/src/cache/cf/index.ts +8 -2
- package/src/cache/document-cache.ts +101 -72
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +0 -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 +17 -7
- 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 +21 -15
- package/src/host/errors.ts +8 -8
- package/src/host/index.ts +4 -7
- package/src/host/pattern-matcher.ts +27 -27
- package/src/host/router.ts +61 -39
- package/src/host/testing.ts +8 -8
- package/src/host/types.ts +15 -7
- package/src/host/utils.ts +1 -1
- 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 -157
- 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 +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 -1428
- package/src/route-map-builder.ts +211 -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 +158 -0
- package/src/router/handler-context.ts +374 -81
- package/src/router/intercept-resolution.ts +395 -0
- package/src/router/lazy-includes.ts +234 -0
- package/src/router/loader-resolution.ts +215 -122
- package/src/router/logging.ts +248 -0
- package/src/router/manifest.ts +148 -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 +80 -93
- package/src/router/match-middleware/cache-lookup.ts +382 -9
- package/src/router/match-middleware/cache-store.ts +51 -22
- package/src/router/match-middleware/intercept-resolution.ts +55 -17
- package/src/router/match-middleware/segment-resolution.ts +24 -6
- package/src/router/match-pipelines.ts +10 -45
- package/src/router/match-result.ts +34 -28
- package/src/router/metrics.ts +235 -15
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +324 -367
- 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 +36 -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 +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 +1241 -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 +77 -3
- package/src/router.ts +692 -4257
- 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 +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 +38 -11
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +25 -13
- package/src/server/context.ts +182 -51
- 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 +430 -70
- package/src/server.ts +35 -130
- package/src/ssr/index.tsx +100 -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 +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 -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 +110 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +11 -1133
- 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/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -51
- 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/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
- 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/{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/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,7 @@ 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,
|
|
138
141
|
) => Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }>;
|
|
139
142
|
|
|
140
143
|
// Generator-based segment resolution (for pipeline)
|
|
@@ -149,7 +152,7 @@ export interface RouterContext<TEnv = any> {
|
|
|
149
152
|
prevUrl: URL,
|
|
150
153
|
nextUrl: URL,
|
|
151
154
|
loaderPromises: Map<string, Promise<any>>,
|
|
152
|
-
actionContext?: any
|
|
155
|
+
actionContext?: any,
|
|
153
156
|
) => AsyncGenerator<ResolvedSegment | { __type: "id"; id: string }>;
|
|
154
157
|
|
|
155
158
|
// Intercept resolution
|
|
@@ -159,12 +162,12 @@ export interface RouterContext<TEnv = any> {
|
|
|
159
162
|
params: Record<string, string>,
|
|
160
163
|
handlerContext: HandlerContext<any, TEnv>,
|
|
161
164
|
belongsToRoute: boolean,
|
|
162
|
-
revalidationContext?: RevalidationContext
|
|
165
|
+
revalidationContext?: RevalidationContext,
|
|
163
166
|
) => Promise<ResolvedSegment[]>;
|
|
164
167
|
|
|
165
168
|
// Collect with markers
|
|
166
169
|
collectWithMarkers?: <T>(
|
|
167
|
-
gen: AsyncGenerator<T | { __type: "id"; id: string }
|
|
170
|
+
gen: AsyncGenerator<T | { __type: "id"; id: string }>,
|
|
168
171
|
) => Promise<{ items: T[]; matchedIds: string[] }>;
|
|
169
172
|
|
|
170
173
|
// Revalidation evaluation
|
|
@@ -180,6 +183,12 @@ export interface RouterContext<TEnv = any> {
|
|
|
180
183
|
context: HandlerContext<any, TEnv>;
|
|
181
184
|
actionContext?: any;
|
|
182
185
|
stale?: boolean;
|
|
186
|
+
traceSource?:
|
|
187
|
+
| "segment-resolution"
|
|
188
|
+
| "cache-hit"
|
|
189
|
+
| "loader"
|
|
190
|
+
| "parallel"
|
|
191
|
+
| "orphan-layout";
|
|
183
192
|
}) => Promise<boolean>;
|
|
184
193
|
|
|
185
194
|
// Request context
|
|
@@ -196,7 +205,7 @@ export interface RouterContext<TEnv = any> {
|
|
|
196
205
|
routeKey: string,
|
|
197
206
|
params: Record<string, string>,
|
|
198
207
|
handlerContext: HandlerContext<any, TEnv>,
|
|
199
|
-
loaderPromises: Map<string, Promise<any
|
|
208
|
+
loaderPromises: Map<string, Promise<any>>,
|
|
200
209
|
) => Promise<ResolvedSegment[]>;
|
|
201
210
|
|
|
202
211
|
// Generator-based simple resolution
|
|
@@ -205,12 +214,12 @@ export interface RouterContext<TEnv = any> {
|
|
|
205
214
|
routeKey: string,
|
|
206
215
|
params: Record<string, string>,
|
|
207
216
|
handlerContext: HandlerContext<any, TEnv>,
|
|
208
|
-
loaderPromises: Map<string, Promise<any
|
|
217
|
+
loaderPromises: Map<string, Promise<any>>,
|
|
209
218
|
) => AsyncGenerator<ResolvedSegment | { __type: "id"; id: string }>;
|
|
210
219
|
|
|
211
220
|
// Collect segments from generator
|
|
212
221
|
collectSegmentsFromGenerator?: <T>(
|
|
213
|
-
gen: AsyncGenerator<T | { __type: "id"; id: string }
|
|
222
|
+
gen: AsyncGenerator<T | { __type: "id"; id: string }>,
|
|
214
223
|
) => Promise<T[]>;
|
|
215
224
|
|
|
216
225
|
// Handle store
|
|
@@ -219,7 +228,7 @@ export interface RouterContext<TEnv = any> {
|
|
|
219
228
|
// Loaders-only resolution (for full match cache hit - no revalidation)
|
|
220
229
|
resolveLoadersOnly?: (
|
|
221
230
|
entries: EntryData[],
|
|
222
|
-
handlerContext: HandlerContext<any, TEnv
|
|
231
|
+
handlerContext: HandlerContext<any, TEnv>,
|
|
223
232
|
) => Promise<ResolvedSegment[]>;
|
|
224
233
|
|
|
225
234
|
// Loaders-only resolution (for cache hit scenarios)
|
|
@@ -232,14 +241,21 @@ export interface RouterContext<TEnv = any> {
|
|
|
232
241
|
prevUrl: URL,
|
|
233
242
|
nextUrl: URL,
|
|
234
243
|
routeKey: string,
|
|
235
|
-
actionContext?: any
|
|
244
|
+
actionContext?: any,
|
|
245
|
+
stale?: boolean,
|
|
236
246
|
) => Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }>;
|
|
237
247
|
|
|
238
248
|
// Entry revalidation map
|
|
239
249
|
buildEntryRevalidateMap?: (
|
|
240
|
-
entries: EntryData[]
|
|
250
|
+
entries: EntryData[],
|
|
241
251
|
) => Map<string, { revalidate: ShouldRevalidateFn[] }>;
|
|
242
252
|
|
|
253
|
+
// Telemetry sink (optional, no-op when undefined)
|
|
254
|
+
telemetry?: TelemetrySink;
|
|
255
|
+
|
|
256
|
+
// Request ID for telemetry span correlation (set per-request in match handlers)
|
|
257
|
+
requestId?: string;
|
|
258
|
+
|
|
243
259
|
// Intercept loaders only (for cache hit + intercept scenarios)
|
|
244
260
|
resolveInterceptLoadersOnly?: (
|
|
245
261
|
intercept: InterceptEntry,
|
|
@@ -256,7 +272,7 @@ export interface RouterContext<TEnv = any> {
|
|
|
256
272
|
routeKey: string;
|
|
257
273
|
actionContext?: any;
|
|
258
274
|
stale?: boolean;
|
|
259
|
-
}
|
|
275
|
+
},
|
|
260
276
|
) => Promise<{
|
|
261
277
|
loaderDataPromise: Promise<any[]> | any[];
|
|
262
278
|
loaderIds: string[];
|
|
@@ -276,7 +292,7 @@ export function getRouterContext<TEnv = any>(): RouterContext<TEnv> {
|
|
|
276
292
|
if (!deps) {
|
|
277
293
|
throw new Error(
|
|
278
294
|
"getRouterContext() called outside of router context. " +
|
|
279
|
-
"Ensure code is running inside runWithRouterContext()."
|
|
295
|
+
"Ensure code is running inside runWithRouterContext().",
|
|
280
296
|
);
|
|
281
297
|
}
|
|
282
298
|
return deps as RouterContext<TEnv>;
|
|
@@ -294,8 +310,7 @@ export function getRouterContext<TEnv = any>(): RouterContext<TEnv> {
|
|
|
294
310
|
*/
|
|
295
311
|
export function runWithRouterContext<T, TEnv = any>(
|
|
296
312
|
deps: RouterContext<TEnv>,
|
|
297
|
-
fn: () => T
|
|
313
|
+
fn: () => T,
|
|
298
314
|
): T {
|
|
299
315
|
return routerContext.run(deps, fn);
|
|
300
316
|
}
|
|
301
|
-
|