@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.
Files changed (301) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +884 -4
  3. package/dist/bin/rango.js +1606 -0
  4. package/dist/vite/index.js +4567 -769
  5. package/package.json +77 -58
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +262 -0
  8. package/skills/caching/SKILL.md +85 -23
  9. package/skills/composability/SKILL.md +172 -0
  10. package/skills/debug-manifest/SKILL.md +12 -8
  11. package/skills/document-cache/SKILL.md +18 -16
  12. package/skills/fonts/SKILL.md +167 -0
  13. package/skills/hooks/SKILL.md +334 -72
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +131 -8
  16. package/skills/layout/SKILL.md +100 -3
  17. package/skills/links/SKILL.md +89 -30
  18. package/skills/loader/SKILL.md +388 -38
  19. package/skills/middleware/SKILL.md +171 -34
  20. package/skills/mime-routes/SKILL.md +128 -0
  21. package/skills/parallel/SKILL.md +204 -1
  22. package/skills/prerender/SKILL.md +643 -0
  23. package/skills/rango/SKILL.md +85 -16
  24. package/skills/response-routes/SKILL.md +411 -0
  25. package/skills/route/SKILL.md +226 -14
  26. package/skills/router-setup/SKILL.md +123 -30
  27. package/skills/tailwind/SKILL.md +129 -0
  28. package/skills/theme/SKILL.md +9 -8
  29. package/skills/typesafety/SKILL.md +318 -89
  30. package/skills/use-cache/SKILL.md +324 -0
  31. package/src/__internal.ts +102 -4
  32. package/src/bin/rango.ts +321 -0
  33. package/src/browser/action-coordinator.ts +97 -0
  34. package/src/browser/action-response-classifier.ts +99 -0
  35. package/src/browser/event-controller.ts +92 -64
  36. package/src/browser/history-state.ts +80 -0
  37. package/src/browser/intercept-utils.ts +52 -0
  38. package/src/browser/link-interceptor.ts +24 -4
  39. package/src/browser/logging.ts +55 -0
  40. package/src/browser/merge-segment-loaders.ts +20 -12
  41. package/src/browser/navigation-bridge.ts +282 -557
  42. package/src/browser/navigation-client.ts +157 -71
  43. package/src/browser/navigation-store.ts +33 -50
  44. package/src/browser/navigation-transaction.ts +297 -0
  45. package/src/browser/network-error-handler.ts +61 -0
  46. package/src/browser/partial-update.ts +303 -310
  47. package/src/browser/prefetch/cache.ts +206 -0
  48. package/src/browser/prefetch/fetch.ts +144 -0
  49. package/src/browser/prefetch/observer.ts +65 -0
  50. package/src/browser/prefetch/policy.ts +48 -0
  51. package/src/browser/prefetch/queue.ts +128 -0
  52. package/src/browser/rango-state.ts +112 -0
  53. package/src/browser/react/Link.tsx +193 -73
  54. package/src/browser/react/NavigationProvider.tsx +160 -13
  55. package/src/browser/react/context.ts +6 -0
  56. package/src/browser/react/filter-segment-order.ts +11 -0
  57. package/src/browser/react/index.ts +12 -12
  58. package/src/browser/react/location-state-shared.ts +95 -53
  59. package/src/browser/react/location-state.ts +60 -15
  60. package/src/browser/react/mount-context.ts +24 -1
  61. package/src/browser/react/nonce-context.ts +23 -0
  62. package/src/browser/react/shallow-equal.ts +27 -0
  63. package/src/browser/react/use-action.ts +29 -51
  64. package/src/browser/react/use-client-cache.ts +5 -3
  65. package/src/browser/react/use-handle.ts +32 -79
  66. package/src/browser/react/use-href.tsx +2 -2
  67. package/src/browser/react/use-link-status.ts +6 -5
  68. package/src/browser/react/use-navigation.ts +22 -63
  69. package/src/browser/react/use-params.ts +65 -0
  70. package/src/browser/react/use-pathname.ts +47 -0
  71. package/src/browser/react/use-router.ts +63 -0
  72. package/src/browser/react/use-search-params.ts +56 -0
  73. package/src/browser/react/use-segments.ts +80 -97
  74. package/src/browser/response-adapter.ts +73 -0
  75. package/src/browser/rsc-router.tsx +188 -55
  76. package/src/browser/scroll-restoration.ts +117 -44
  77. package/src/browser/segment-reconciler.ts +221 -0
  78. package/src/browser/segment-structure-assert.ts +16 -0
  79. package/src/browser/server-action-bridge.ts +504 -599
  80. package/src/browser/shallow.ts +6 -1
  81. package/src/browser/types.ts +118 -47
  82. package/src/browser/validate-redirect-origin.ts +29 -0
  83. package/src/build/generate-manifest.ts +235 -24
  84. package/src/build/generate-route-types.ts +36 -0
  85. package/src/build/index.ts +13 -0
  86. package/src/build/route-trie.ts +265 -0
  87. package/src/build/route-types/ast-helpers.ts +25 -0
  88. package/src/build/route-types/ast-route-extraction.ts +98 -0
  89. package/src/build/route-types/codegen.ts +102 -0
  90. package/src/build/route-types/include-resolution.ts +411 -0
  91. package/src/build/route-types/param-extraction.ts +48 -0
  92. package/src/build/route-types/per-module-writer.ts +128 -0
  93. package/src/build/route-types/router-processing.ts +479 -0
  94. package/src/build/route-types/scan-filter.ts +78 -0
  95. package/src/build/runtime-discovery.ts +231 -0
  96. package/src/cache/background-task.ts +34 -0
  97. package/src/cache/cache-key-utils.ts +44 -0
  98. package/src/cache/cache-policy.ts +125 -0
  99. package/src/cache/cache-runtime.ts +342 -0
  100. package/src/cache/cache-scope.ts +167 -309
  101. package/src/cache/cf/cf-cache-store.ts +571 -17
  102. package/src/cache/cf/index.ts +13 -3
  103. package/src/cache/document-cache.ts +116 -77
  104. package/src/cache/handle-capture.ts +81 -0
  105. package/src/cache/handle-snapshot.ts +41 -0
  106. package/src/cache/index.ts +1 -15
  107. package/src/cache/memory-segment-store.ts +191 -13
  108. package/src/cache/profile-registry.ts +73 -0
  109. package/src/cache/read-through-swr.ts +134 -0
  110. package/src/cache/segment-codec.ts +256 -0
  111. package/src/cache/taint.ts +98 -0
  112. package/src/cache/types.ts +72 -122
  113. package/src/client.rsc.tsx +3 -1
  114. package/src/client.tsx +106 -126
  115. package/src/component-utils.ts +4 -4
  116. package/src/components/DefaultDocument.tsx +5 -1
  117. package/src/context-var.ts +86 -0
  118. package/src/debug.ts +19 -9
  119. package/src/errors.ts +108 -2
  120. package/src/handle.ts +15 -29
  121. package/src/handles/MetaTags.tsx +73 -20
  122. package/src/handles/breadcrumbs.ts +66 -0
  123. package/src/handles/index.ts +1 -0
  124. package/src/handles/meta.ts +30 -13
  125. package/src/host/cookie-handler.ts +165 -0
  126. package/src/host/errors.ts +97 -0
  127. package/src/host/index.ts +53 -0
  128. package/src/host/pattern-matcher.ts +214 -0
  129. package/src/host/router.ts +352 -0
  130. package/src/host/testing.ts +79 -0
  131. package/src/host/types.ts +146 -0
  132. package/src/host/utils.ts +25 -0
  133. package/src/href-client.ts +119 -29
  134. package/src/index.rsc.ts +153 -19
  135. package/src/index.ts +211 -30
  136. package/src/internal-debug.ts +11 -0
  137. package/src/loader.rsc.ts +26 -147
  138. package/src/loader.ts +27 -10
  139. package/src/network-error-thrower.tsx +3 -1
  140. package/src/outlet-provider.tsx +45 -0
  141. package/src/prerender/param-hash.ts +37 -0
  142. package/src/prerender/store.ts +185 -0
  143. package/src/prerender.ts +463 -0
  144. package/src/reverse.ts +330 -0
  145. package/src/root-error-boundary.tsx +41 -29
  146. package/src/route-content-wrapper.tsx +7 -4
  147. package/src/route-definition/dsl-helpers.ts +959 -0
  148. package/src/route-definition/helper-factories.ts +200 -0
  149. package/src/route-definition/helpers-types.ts +430 -0
  150. package/src/route-definition/index.ts +52 -0
  151. package/src/route-definition/redirect.ts +93 -0
  152. package/src/route-definition.ts +1 -1428
  153. package/src/route-map-builder.ts +217 -123
  154. package/src/route-name.ts +53 -0
  155. package/src/route-types.ts +59 -8
  156. package/src/router/content-negotiation.ts +116 -0
  157. package/src/router/debug-manifest.ts +72 -0
  158. package/src/router/error-handling.ts +9 -9
  159. package/src/router/find-match.ts +160 -0
  160. package/src/router/handler-context.ts +374 -81
  161. package/src/router/intercept-resolution.ts +397 -0
  162. package/src/router/lazy-includes.ts +237 -0
  163. package/src/router/loader-resolution.ts +215 -122
  164. package/src/router/logging.ts +251 -0
  165. package/src/router/manifest.ts +154 -35
  166. package/src/router/match-api.ts +620 -0
  167. package/src/router/match-context.ts +5 -3
  168. package/src/router/match-handlers.ts +440 -0
  169. package/src/router/match-middleware/background-revalidation.ts +108 -93
  170. package/src/router/match-middleware/cache-lookup.ts +440 -10
  171. package/src/router/match-middleware/cache-store.ts +98 -26
  172. package/src/router/match-middleware/intercept-resolution.ts +57 -17
  173. package/src/router/match-middleware/segment-resolution.ts +27 -6
  174. package/src/router/match-pipelines.ts +10 -45
  175. package/src/router/match-result.ts +55 -33
  176. package/src/router/metrics.ts +240 -15
  177. package/src/router/middleware-cookies.ts +55 -0
  178. package/src/router/middleware-types.ts +222 -0
  179. package/src/router/middleware.ts +327 -369
  180. package/src/router/pattern-matching.ts +211 -43
  181. package/src/router/prerender-match.ts +402 -0
  182. package/src/router/preview-match.ts +170 -0
  183. package/src/router/revalidation.ts +137 -38
  184. package/src/router/router-context.ts +41 -21
  185. package/src/router/router-interfaces.ts +452 -0
  186. package/src/router/router-options.ts +592 -0
  187. package/src/router/router-registry.ts +24 -0
  188. package/src/router/segment-resolution/fresh.ts +677 -0
  189. package/src/router/segment-resolution/helpers.ts +263 -0
  190. package/src/router/segment-resolution/loader-cache.ts +199 -0
  191. package/src/router/segment-resolution/revalidation.ts +1296 -0
  192. package/src/router/segment-resolution/static-store.ts +67 -0
  193. package/src/router/segment-resolution.ts +21 -0
  194. package/src/router/segment-wrappers.ts +291 -0
  195. package/src/router/telemetry-otel.ts +299 -0
  196. package/src/router/telemetry.ts +300 -0
  197. package/src/router/timeout.ts +148 -0
  198. package/src/router/trie-matching.ts +239 -0
  199. package/src/router/types.ts +77 -3
  200. package/src/router.ts +665 -4182
  201. package/src/rsc/handler-context.ts +45 -0
  202. package/src/rsc/handler.ts +764 -754
  203. package/src/rsc/helpers.ts +140 -6
  204. package/src/rsc/index.ts +0 -20
  205. package/src/rsc/loader-fetch.ts +209 -0
  206. package/src/rsc/manifest-init.ts +86 -0
  207. package/src/rsc/nonce.ts +14 -0
  208. package/src/rsc/origin-guard.ts +141 -0
  209. package/src/rsc/progressive-enhancement.ts +379 -0
  210. package/src/rsc/response-error.ts +37 -0
  211. package/src/rsc/response-route-handler.ts +347 -0
  212. package/src/rsc/rsc-rendering.ts +237 -0
  213. package/src/rsc/runtime-warnings.ts +42 -0
  214. package/src/rsc/server-action.ts +348 -0
  215. package/src/rsc/ssr-setup.ts +128 -0
  216. package/src/rsc/types.ts +38 -11
  217. package/src/search-params.ts +230 -0
  218. package/src/segment-system.tsx +172 -21
  219. package/src/server/context.ts +266 -58
  220. package/src/server/cookie-store.ts +190 -0
  221. package/src/server/fetchable-loader-store.ts +37 -0
  222. package/src/server/handle-store.ts +94 -15
  223. package/src/server/loader-registry.ts +15 -56
  224. package/src/server/request-context.ts +439 -73
  225. package/src/server.ts +35 -128
  226. package/src/ssr/index.tsx +101 -31
  227. package/src/static-handler.ts +114 -0
  228. package/src/theme/ThemeProvider.tsx +21 -15
  229. package/src/theme/ThemeScript.tsx +5 -5
  230. package/src/theme/constants.ts +5 -2
  231. package/src/theme/index.ts +4 -14
  232. package/src/theme/theme-context.ts +4 -30
  233. package/src/theme/theme-script.ts +21 -18
  234. package/src/types/boundaries.ts +158 -0
  235. package/src/types/cache-types.ts +198 -0
  236. package/src/types/error-types.ts +192 -0
  237. package/src/types/global-namespace.ts +100 -0
  238. package/src/types/handler-context.ts +773 -0
  239. package/src/types/index.ts +88 -0
  240. package/src/types/loader-types.ts +183 -0
  241. package/src/types/route-config.ts +170 -0
  242. package/src/types/route-entry.ts +109 -0
  243. package/src/types/segments.ts +150 -0
  244. package/src/types.ts +1 -1623
  245. package/src/urls/include-helper.ts +197 -0
  246. package/src/urls/index.ts +53 -0
  247. package/src/urls/path-helper-types.ts +339 -0
  248. package/src/urls/path-helper.ts +329 -0
  249. package/src/urls/pattern-types.ts +95 -0
  250. package/src/urls/response-types.ts +106 -0
  251. package/src/urls/type-extraction.ts +372 -0
  252. package/src/urls/urls-function.ts +98 -0
  253. package/src/urls.ts +1 -802
  254. package/src/use-loader.tsx +85 -77
  255. package/src/vite/discovery/bundle-postprocess.ts +184 -0
  256. package/src/vite/discovery/discover-routers.ts +344 -0
  257. package/src/vite/discovery/prerender-collection.ts +385 -0
  258. package/src/vite/discovery/route-types-writer.ts +258 -0
  259. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  260. package/src/vite/discovery/state.ts +108 -0
  261. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  262. package/src/vite/index.ts +11 -782
  263. package/src/vite/plugin-types.ts +48 -0
  264. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  265. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  266. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  267. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -53
  268. package/src/vite/plugins/expose-id-utils.ts +287 -0
  269. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  270. package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
  271. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  272. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  273. package/src/vite/plugins/expose-ids/types.ts +45 -0
  274. package/src/vite/plugins/expose-internal-ids.ts +569 -0
  275. package/src/vite/plugins/refresh-cmd.ts +65 -0
  276. package/src/vite/plugins/use-cache-transform.ts +323 -0
  277. package/src/vite/plugins/version-injector.ts +83 -0
  278. package/src/vite/plugins/version-plugin.ts +266 -0
  279. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +27 -16
  280. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  281. package/src/vite/rango.ts +445 -0
  282. package/src/vite/router-discovery.ts +777 -0
  283. package/src/vite/utils/ast-handler-extract.ts +517 -0
  284. package/src/vite/utils/banner.ts +36 -0
  285. package/src/vite/utils/bundle-analysis.ts +137 -0
  286. package/src/vite/utils/manifest-utils.ts +70 -0
  287. package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
  288. package/src/vite/utils/prerender-utils.ts +189 -0
  289. package/src/vite/utils/shared-utils.ts +169 -0
  290. package/CLAUDE.md +0 -43
  291. package/src/browser/lru-cache.ts +0 -69
  292. package/src/browser/request-controller.ts +0 -164
  293. package/src/cache/memory-store.ts +0 -253
  294. package/src/href-context.ts +0 -33
  295. package/src/href.ts +0 -255
  296. package/src/server/route-manifest-cache.ts +0 -173
  297. package/src/vite/expose-handle-id.ts +0 -209
  298. package/src/vite/expose-loader-id.ts +0 -426
  299. package/src/vite/expose-location-state-id.ts +0 -177
  300. package/src/warmup/connection-warmup.tsx +0 -94
  301. /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
- Object.keys(nextParams).length !== Object.keys(prevParams).length ||
61
- Object.keys(nextParams).some(
62
- (key) => nextParams[key] !== prevParams[key]
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
- // Routes are the primary param-dependent content and always need updates
91
- defaultShouldRevalidate = paramsChanged;
92
- if (paramsChanged) {
93
- console.log(
94
- `[Router.evaluateRevalidation] ${segment.id}: ROUTE - params changed, revalidating`
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
- // Layouts and parallels default to no revalidation
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
- console.log(
103
- `[Router.evaluateRevalidation] ${
104
- segment.id
105
- }: ${segment.type.toUpperCase()} segment - skipping (override with custom revalidation if needed)`
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
- console.log(
114
- `[Router.evaluateRevalidation] ${segment.id}: PARAMS CHANGED (default) - revalidating`,
115
- { prev: prevParams, next: nextParams }
116
- );
184
+ debugLog("revalidation", "default revalidate=true", {
185
+ segmentId: segment.id,
186
+ prevParams,
187
+ nextParams,
188
+ });
117
189
  } else {
118
- console.log(
119
- `[Router.evaluateRevalidation] ${segment.id}: UNCHANGED (default) - skipping`
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: routeKey, // User-friendly route name (e.g., "products.detail")
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
- console.log(
162
- `[Router.evaluateRevalidation] ${segment.id}: REVALIDATE (${name}) HARD: ${result}`
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
- console.log(
173
- `[Router.evaluateRevalidation] ${segment.id}: REVALIDATE (${name}) SOFT: ${currentSuggestion}`
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
- console.log(
179
- `[Router.evaluateRevalidation] ${segment.id}: REVALIDATE (${name}) DEFER to default: ${currentSuggestion}`
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
- console.log(
187
- `[Router.evaluateRevalidation] ${segment.id}: Final decision: ${currentSuggestion}`
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
-