@rangojs/router 0.24.3 → 0.25.0

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 (274) hide show
  1. package/README.md +1 -0
  2. package/dist/bin/rango.js +363 -39
  3. package/dist/testing/vitest.js +37 -4
  4. package/dist/types/browser/event-controller.d.ts +24 -5
  5. package/dist/types/browser/intercept-utils.d.ts +5 -2
  6. package/dist/types/browser/merge-segment-loaders.d.ts +0 -23
  7. package/dist/types/browser/navigation-transaction.d.ts +5 -5
  8. package/dist/types/browser/partial-update.d.ts +21 -13
  9. package/dist/types/browser/prefetch/cache.d.ts +8 -0
  10. package/dist/types/browser/rango-marker.d.ts +7 -0
  11. package/dist/types/browser/react/filter-segment-order.d.ts +10 -0
  12. package/dist/types/browser/react/use-handle.d.ts +20 -0
  13. package/dist/types/browser/react/use-href.d.ts +6 -0
  14. package/dist/types/browser/react/use-router.d.ts +6 -1
  15. package/dist/types/browser/response-adapter.d.ts +5 -3
  16. package/dist/types/browser/scroll-restoration.d.ts +9 -3
  17. package/dist/types/browser/segment-reconciler.d.ts +10 -13
  18. package/dist/types/browser/types.d.ts +38 -8
  19. package/dist/types/build/route-types/per-module-writer.d.ts +12 -0
  20. package/dist/types/cache/cf/cf-shell-frame.d.ts +2 -0
  21. package/dist/types/cache/types.d.ts +8 -0
  22. package/dist/types/client-urls/client-root.d.ts +4 -1
  23. package/dist/types/client-urls/client-urls.d.ts +28 -2
  24. package/dist/types/client-urls/navigation.d.ts +6 -8
  25. package/dist/types/client-urls/revalidate-registry.d.ts +1 -1
  26. package/dist/types/client-urls/server-projection.d.ts +20 -5
  27. package/dist/types/client-urls/types.d.ts +35 -17
  28. package/dist/types/client.d.ts +2 -2
  29. package/dist/types/client.rsc.d.ts +2 -2
  30. package/dist/types/decode-loader-results.d.ts +28 -7
  31. package/dist/types/href-client.d.ts +3 -1
  32. package/dist/types/index.rsc.d.ts +1 -1
  33. package/dist/types/loader-redirect.d.ts +2 -3
  34. package/dist/types/outlet-context.d.ts +7 -6
  35. package/dist/types/prefetch-markers.d.ts +14 -5
  36. package/dist/types/route-content-wrapper.d.ts +17 -14
  37. package/dist/types/route-definition/helpers-types.d.ts +7 -2
  38. package/dist/types/route-definition/resolve-handler-use.d.ts +6 -1
  39. package/dist/types/route-definition/rewrite.d.ts +48 -5
  40. package/dist/types/route-types.d.ts +2 -2
  41. package/dist/types/router/handler-context.d.ts +14 -2
  42. package/dist/types/router/intercept-resolution.d.ts +19 -46
  43. package/dist/types/router/lazy-includes.d.ts +7 -0
  44. package/dist/types/router/match-middleware/intercept-resolution.d.ts +2 -2
  45. package/dist/types/router/middleware.d.ts +0 -16
  46. package/dist/types/router/navigation-snapshot.d.ts +2 -0
  47. package/dist/types/router/prefetch-cache-ttl.d.ts +8 -11
  48. package/dist/types/router/revalidation.d.ts +8 -1
  49. package/dist/types/router/router-context.d.ts +4 -16
  50. package/dist/types/router/router-interfaces.d.ts +0 -6
  51. package/dist/types/router/router-options.d.ts +4 -7
  52. package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +31 -8
  53. package/dist/types/router/segment-resolution/revalidation.d.ts +38 -4
  54. package/dist/types/router/segment-resolution.d.ts +1 -1
  55. package/dist/types/router/segment-wrappers.d.ts +3 -5
  56. package/dist/types/router/substitute-pattern-params.d.ts +19 -2
  57. package/dist/types/router/tracing.d.ts +2 -2
  58. package/dist/types/rsc/full-payload.d.ts +14 -2
  59. package/dist/types/rsc/helpers.d.ts +8 -0
  60. package/dist/types/rsc/manifest-init.d.ts +20 -0
  61. package/dist/types/rsc/prefetch-render-error.d.ts +22 -0
  62. package/dist/types/rsc/progressive-enhancement.d.ts +20 -0
  63. package/dist/types/rsc/rewrite-passes.d.ts +12 -2
  64. package/dist/types/rsc/server-action.d.ts +6 -0
  65. package/dist/types/rsc/shell-capture-constants.d.ts +10 -1
  66. package/dist/types/rsc/shell-capture.d.ts +7 -0
  67. package/dist/types/rsc/types.d.ts +5 -0
  68. package/dist/types/segment-system.d.ts +15 -0
  69. package/dist/types/server/context.d.ts +64 -4
  70. package/dist/types/server/request-context.d.ts +41 -4
  71. package/dist/types/server/request-headers.d.ts +12 -0
  72. package/dist/types/server/rewrite-trail.d.ts +8 -1
  73. package/dist/types/testing/e2e/index.d.ts +1 -1
  74. package/dist/types/testing/flight.entry.d.ts +1 -1
  75. package/dist/types/testing/index.d.ts +1 -1
  76. package/dist/types/testing/internal/client-urls-projection.d.ts +4 -3
  77. package/dist/types/testing/render-route.d.ts +21 -11
  78. package/dist/types/testing/run-loader.d.ts +7 -0
  79. package/dist/types/testing/run-middleware.d.ts +7 -0
  80. package/dist/types/testing/run-revalidate.d.ts +5 -4
  81. package/dist/types/testing/serve-shell-request.d.ts +41 -4
  82. package/dist/types/testing/shell-status.d.ts +17 -0
  83. package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
  84. package/dist/types/testing/vitest.d.ts +10 -1
  85. package/dist/types/thenable-status.d.ts +0 -6
  86. package/dist/types/types/handler-context.d.ts +12 -1
  87. package/dist/types/types/loader-types.d.ts +4 -3
  88. package/dist/types/types/route-entry.d.ts +2 -1
  89. package/dist/types/urls/path-helper-types.d.ts +4 -2
  90. package/dist/types/urls/path-helper.d.ts +5 -0
  91. package/dist/types/vite/discovery/client-urls-projection.d.ts +55 -5
  92. package/dist/types/vite/discovery/state.d.ts +20 -0
  93. package/dist/types/vite/plugins/client-urls-revalidate-hoist.d.ts +28 -16
  94. package/dist/types/vite/plugins/hoist/source.d.ts +8 -7
  95. package/dist/types/vite/plugins/virtual-entries.d.ts +6 -0
  96. package/dist/types/vite/utils/prerender-utils.d.ts +7 -0
  97. package/dist/vite/index.js +6816 -6547
  98. package/package.json +1 -1
  99. package/skills/app-structure/SKILL.md +255 -0
  100. package/skills/app-structure/content-site.md +333 -0
  101. package/skills/app-structure/e-commerce.md +435 -0
  102. package/skills/app-structure/large-app.md +885 -0
  103. package/skills/app-structure/multi-app.md +280 -0
  104. package/skills/breadcrumbs/SKILL.md +6 -1
  105. package/skills/caching/SKILL.md +3 -1
  106. package/skills/catalog.json +12 -0
  107. package/skills/client-urls/SKILL.md +195 -32
  108. package/skills/composability/SKILL.md +10 -8
  109. package/skills/handler-use/SKILL.md +48 -9
  110. package/skills/hooks/handle-and-actions.md +104 -0
  111. package/skills/hooks/navigation.md +24 -10
  112. package/skills/hooks/urls.md +1 -1
  113. package/skills/i18n/SKILL.md +2 -1
  114. package/skills/intercept/SKILL.md +60 -29
  115. package/skills/layout/SKILL.md +6 -3
  116. package/skills/links/SKILL.md +151 -17
  117. package/skills/loader/SKILL.md +32 -10
  118. package/skills/middleware/SKILL.md +111 -15
  119. package/skills/observability/SKILL.md +11 -0
  120. package/skills/parallel/SKILL.md +5 -5
  121. package/skills/ppr/SKILL.md +43 -8
  122. package/skills/prerender/SKILL.md +1 -2
  123. package/skills/rango/SKILL.md +3 -0
  124. package/skills/response-routes/SKILL.md +4 -1
  125. package/skills/scripts/SKILL.md +12 -1
  126. package/skills/server-actions/SKILL.md +60 -20
  127. package/skills/testing/SKILL.md +1 -0
  128. package/skills/testing/client-components.md +13 -10
  129. package/skills/testing/loader.md +25 -24
  130. package/skills/testing/middleware.md +11 -11
  131. package/skills/testing/response-routes.md +4 -1
  132. package/skills/testing/serve-shell-request.md +85 -21
  133. package/skills/ux-architecture/SKILL.md +980 -0
  134. package/skills/view-transitions/SKILL.md +43 -10
  135. package/src/__augment-tests__/augmented.check.ts +28 -0
  136. package/src/browser/event-controller.ts +44 -18
  137. package/src/browser/intercept-utils.ts +3 -2
  138. package/src/browser/merge-segment-loaders.ts +0 -85
  139. package/src/browser/navigation-bridge.ts +119 -98
  140. package/src/browser/navigation-client.ts +20 -15
  141. package/src/browser/navigation-store.ts +4 -2
  142. package/src/browser/navigation-transaction.ts +9 -8
  143. package/src/browser/partial-update.ts +90 -32
  144. package/src/browser/prefetch/cache.ts +8 -0
  145. package/src/browser/prefetch/fetch.ts +13 -3
  146. package/src/browser/rango-marker.ts +13 -0
  147. package/src/browser/react/NavigationProvider.tsx +25 -7
  148. package/src/browser/react/filter-segment-order.ts +13 -0
  149. package/src/browser/react/use-action.ts +9 -15
  150. package/src/browser/react/use-handle.ts +63 -71
  151. package/src/browser/react/use-href.tsx +24 -5
  152. package/src/browser/react/use-link-status.ts +1 -1
  153. package/src/browser/react/use-navigation.ts +30 -40
  154. package/src/browser/react/use-reverse.ts +4 -17
  155. package/src/browser/react/use-router.ts +37 -6
  156. package/src/browser/react/use-segments.ts +8 -12
  157. package/src/browser/response-adapter.ts +11 -5
  158. package/src/browser/rsc-router.tsx +3 -1
  159. package/src/browser/scroll-restoration.ts +34 -20
  160. package/src/browser/segment-reconciler.ts +11 -29
  161. package/src/browser/server-action-bridge.ts +52 -16
  162. package/src/browser/types.ts +40 -8
  163. package/src/build/generate-manifest.ts +12 -1
  164. package/src/build/route-types/codegen.ts +6 -2
  165. package/src/build/route-types/per-module-writer.ts +94 -61
  166. package/src/cache/cache-runtime.ts +9 -0
  167. package/src/cache/cache-scope.ts +2 -1
  168. package/src/cache/cf/cf-cache-store.ts +2 -0
  169. package/src/cache/cf/cf-shell-frame.ts +3 -0
  170. package/src/cache/types.ts +8 -0
  171. package/src/cache/vercel/vercel-cache-store.ts +6 -1
  172. package/src/client-urls/client-root.tsx +10 -5
  173. package/src/client-urls/client-urls.ts +246 -166
  174. package/src/client-urls/navigation.ts +45 -38
  175. package/src/client-urls/revalidate-registry.ts +12 -3
  176. package/src/client-urls/server-projection.ts +286 -75
  177. package/src/client-urls/types.ts +40 -16
  178. package/src/client.rsc.tsx +11 -1
  179. package/src/client.tsx +9 -1
  180. package/src/decode-loader-results.ts +64 -31
  181. package/src/href-client.ts +3 -1
  182. package/src/index.rsc.ts +1 -0
  183. package/src/index.ts +12 -4
  184. package/src/loader-redirect.tsx +2 -3
  185. package/src/outlet-context.ts +7 -6
  186. package/src/prefetch-markers.ts +15 -5
  187. package/src/reverse.ts +3 -3
  188. package/src/route-content-wrapper.tsx +19 -17
  189. package/src/route-definition/dsl-helpers.ts +27 -7
  190. package/src/route-definition/helpers-types.ts +7 -2
  191. package/src/route-definition/resolve-handler-use.ts +23 -10
  192. package/src/route-definition/rewrite.ts +86 -19
  193. package/src/route-types.ts +1 -2
  194. package/src/router/find-match.ts +37 -1
  195. package/src/router/handler-context.ts +85 -40
  196. package/src/router/intercept-resolution.ts +161 -307
  197. package/src/router/lazy-includes.ts +43 -27
  198. package/src/router/loader-resolution.ts +21 -1
  199. package/src/router/manifest.ts +6 -8
  200. package/src/router/match-api.ts +11 -1
  201. package/src/router/match-middleware/background-revalidation.ts +2 -4
  202. package/src/router/match-middleware/cache-lookup.ts +16 -20
  203. package/src/router/match-middleware/intercept-resolution.ts +41 -60
  204. package/src/router/match-result.ts +31 -2
  205. package/src/router/middleware.ts +6 -147
  206. package/src/router/navigation-snapshot.ts +38 -2
  207. package/src/router/prefetch-cache-ttl.ts +10 -18
  208. package/src/router/prerender-match.ts +2 -1
  209. package/src/router/revalidation.ts +24 -3
  210. package/src/router/router-context.ts +5 -18
  211. package/src/router/router-interfaces.ts +0 -7
  212. package/src/router/router-options.ts +4 -7
  213. package/src/router/segment-resolution/fresh.ts +6 -2
  214. package/src/router/segment-resolution/helpers.ts +7 -1
  215. package/src/router/segment-resolution/prefetch-deferral.ts +62 -12
  216. package/src/router/segment-resolution/revalidation.ts +75 -35
  217. package/src/router/segment-resolution.ts +0 -1
  218. package/src/router/segment-wrappers.ts +8 -13
  219. package/src/router/substitute-pattern-params.ts +37 -2
  220. package/src/router/tracing.ts +2 -2
  221. package/src/router.ts +2 -9
  222. package/src/rsc/full-payload.ts +26 -4
  223. package/src/rsc/handler.ts +83 -41
  224. package/src/rsc/helpers.ts +13 -0
  225. package/src/rsc/manifest-init.ts +56 -3
  226. package/src/rsc/prefetch-render-error.ts +38 -0
  227. package/src/rsc/progressive-enhancement.ts +161 -211
  228. package/src/rsc/rewrite-passes.ts +56 -9
  229. package/src/rsc/rsc-rendering.ts +79 -23
  230. package/src/rsc/server-action.ts +25 -2
  231. package/src/rsc/shell-capture-constants.ts +22 -0
  232. package/src/rsc/shell-capture.ts +11 -0
  233. package/src/rsc/types.ts +5 -0
  234. package/src/segment-system.tsx +125 -119
  235. package/src/server/context.ts +96 -5
  236. package/src/server/handle-store.ts +3 -2
  237. package/src/server/request-context.ts +42 -3
  238. package/src/server/request-headers.ts +32 -0
  239. package/src/server/rewrite-trail.ts +55 -9
  240. package/src/ssr/ssr-root.tsx +6 -10
  241. package/src/testing/dispatch.ts +4 -5
  242. package/src/testing/e2e/index.ts +1 -0
  243. package/src/testing/flight.entry.ts +1 -0
  244. package/src/testing/index.ts +1 -0
  245. package/src/testing/internal/client-urls-projection.ts +31 -11
  246. package/src/testing/render-route.tsx +83 -40
  247. package/src/testing/run-loader.ts +26 -2
  248. package/src/testing/run-middleware.ts +10 -0
  249. package/src/testing/run-revalidate.ts +13 -6
  250. package/src/testing/serve-shell-request.ts +77 -4
  251. package/src/testing/shell-status.ts +40 -0
  252. package/src/testing/vitest-stubs/version.ts +1 -0
  253. package/src/testing/vitest.ts +74 -7
  254. package/src/thenable-status.ts +0 -11
  255. package/src/types/handler-context.ts +12 -1
  256. package/src/types/loader-types.ts +4 -3
  257. package/src/types/route-entry.ts +2 -1
  258. package/src/urls/include-helper.ts +38 -11
  259. package/src/urls/path-helper-types.ts +4 -2
  260. package/src/urls/path-helper.ts +16 -3
  261. package/src/vite/discovery/client-urls-projection.ts +205 -16
  262. package/src/vite/discovery/discover-routers.ts +2 -2
  263. package/src/vite/discovery/prerender-collection.ts +2 -6
  264. package/src/vite/discovery/state.ts +26 -0
  265. package/src/vite/plugins/client-urls-revalidate-hoist.ts +190 -77
  266. package/src/vite/plugins/expose-internal-ids.ts +46 -40
  267. package/src/vite/plugins/hoist/source.ts +8 -7
  268. package/src/vite/plugins/transition-when-hoist.ts +1 -0
  269. package/src/vite/plugins/version-plugin.ts +7 -0
  270. package/src/vite/plugins/version.d.ts +7 -0
  271. package/src/vite/plugins/virtual-entries.ts +20 -0
  272. package/src/vite/rango.ts +4 -1
  273. package/src/vite/router-discovery.ts +39 -7
  274. package/src/vite/utils/prerender-utils.ts +17 -0
@@ -62,70 +62,107 @@ export function findUrlsVariableNames(code: string): string[] {
62
62
  return names;
63
63
  }
64
64
 
65
+ export interface PerModuleRouteTypesPlan {
66
+ genPath: string;
67
+ /** A urls()/clientUrls() variable resolved to zero routes; content is empty. */
68
+ isPlaceholder: boolean;
69
+ content: string;
70
+ }
71
+
65
72
  /**
66
- * Generate per-module route types for a single url module file.
67
- * Follows include() calls recursively to produce the full route tree.
68
- * No-ops if the file doesn't contain `urls(`/`clientUrls(` or has no named
69
- * routes.
73
+ * Pure half of the per-module writer: reads sources, never writes. Returns
74
+ * null when the file has no `urls(`/`clientUrls(` or no named routes and no
75
+ * urls() variable. Throws on failure.
70
76
  */
71
- export function writePerModuleRouteTypesForFile(filePath: string): void {
72
- try {
73
- const source = readFileSync(filePath, "utf-8");
74
- if (!source.includes("urls(") && !source.includes("clientUrls(")) return;
77
+ export function planPerModuleRouteTypesForFile(
78
+ filePath: string,
79
+ ): PerModuleRouteTypesPlan | null {
80
+ const source = readFileSync(filePath, "utf-8");
81
+ if (!source.includes("urls(") && !source.includes("clientUrls(")) return null;
75
82
 
76
- const varNames = findUrlsVariableNames(source);
83
+ const varNames = findUrlsVariableNames(source);
77
84
 
78
- type Route = {
79
- name: string;
80
- pattern: string;
81
- params?: Record<string, string>;
82
- search?: Record<string, string>;
83
- };
84
- let routes: Route[];
85
+ type Route = {
86
+ name: string;
87
+ pattern: string;
88
+ params?: Record<string, string>;
89
+ search?: Record<string, string>;
90
+ };
91
+ let routes: Route[];
85
92
 
86
- if (varNames.length > 0) {
87
- // Follow includes recursively via the combined route map builder.
88
- // The visited set in buildCombinedRouteMapWithSearch prevents infinite loops.
89
- // Share one per-file scan memo across all urls() variables so a shared
90
- // include target is read+parsed once for this file, not once per variable.
91
- routes = [];
92
- const memo = createScanMemo();
93
- for (const varName of varNames) {
94
- const { routes: routeMap, searchSchemas } =
95
- buildCombinedRouteMapWithSearch(
96
- filePath,
97
- varName,
98
- undefined,
99
- undefined,
100
- undefined,
101
- memo,
102
- );
103
- for (const [name, pattern] of Object.entries(routeMap)) {
104
- const params = extractParamsFromPattern(pattern);
105
- routes.push({
106
- name,
107
- pattern,
108
- ...(params ? { params } : {}),
109
- ...(searchSchemas[name] ? { search: searchSchemas[name] } : {}),
110
- });
111
- }
93
+ if (varNames.length > 0) {
94
+ // Follow includes recursively via the combined route map builder.
95
+ // The visited set in buildCombinedRouteMapWithSearch prevents infinite loops.
96
+ // Share one per-file scan memo across all urls() variables so a shared
97
+ // include target is read+parsed once for this file, not once per variable.
98
+ routes = [];
99
+ const memo = createScanMemo();
100
+ for (const varName of varNames) {
101
+ const { routes: routeMap, searchSchemas } =
102
+ buildCombinedRouteMapWithSearch(
103
+ filePath,
104
+ varName,
105
+ undefined,
106
+ undefined,
107
+ undefined,
108
+ memo,
109
+ );
110
+ for (const [name, pattern] of Object.entries(routeMap)) {
111
+ const params = extractParamsFromPattern(pattern);
112
+ routes.push({
113
+ name,
114
+ pattern,
115
+ ...(params ? { params } : {}),
116
+ ...(searchSchemas[name] ? { search: searchSchemas[name] } : {}),
117
+ });
112
118
  }
113
- } else {
114
- // Fallback: no urls() variable found, extract path() calls directly
115
- routes = extractRoutesFromSource(source);
116
119
  }
120
+ } else {
121
+ // Fallback: no urls() variable found, extract path() calls directly
122
+ routes = extractRoutesFromSource(source);
123
+ }
124
+
125
+ // Match .ts/.tsx/.js/.jsx (same as router-processing.ts / router-transform.ts).
126
+ // Without the jsx? branch a .jsx/.js source produced genPath === filePath,
127
+ // overwriting the source file instead of writing a sibling .gen.ts.
128
+ const genPath = filePath.replace(/\.(tsx?|jsx?)$/, ".gen.ts");
117
129
 
118
- // Match .ts/.tsx/.js/.jsx (same as router-processing.ts / router-transform.ts).
119
- // Without the jsx? branch a .jsx/.js source produced genPath === filePath,
120
- // overwriting the source file instead of writing a sibling .gen.ts.
121
- const genPath = filePath.replace(/\.(tsx?|jsx?)$/, ".gen.ts");
130
+ if (routes.length === 0) {
131
+ if (varNames.length === 0) return null;
132
+ return {
133
+ genPath,
134
+ isPlaceholder: true,
135
+ content: generatePerModuleTypesSource([]),
136
+ };
137
+ }
138
+ return {
139
+ genPath,
140
+ isPlaceholder: false,
141
+ content: generatePerModuleTypesSource(routes),
142
+ };
143
+ }
144
+
145
+ /**
146
+ * Generate per-module route types for a single url module file.
147
+ * Follows include() calls recursively to produce the full route tree.
148
+ * No-ops if the file doesn't contain `urls(`/`clientUrls(` or has no named
149
+ * routes.
150
+ */
151
+ export function writePerModuleRouteTypesForFile(filePath: string): void {
152
+ try {
153
+ const plan = planPerModuleRouteTypesForFile(filePath);
154
+ if (!plan) return;
155
+ const { genPath, isPlaceholder, content } = plan;
156
+ const exists = existsSync(genPath);
122
157
 
123
- // When a urls() variable was found but static resolution yields zero
124
- // routes, write an empty placeholder so generated imports stay
125
- // resolvable until runtime discovery fills them in.
126
- if (routes.length === 0) {
127
- if (varNames.length > 0 && !existsSync(genPath)) {
128
- writeFileSync(genPath, generatePerModuleTypesSource([]));
158
+ // A urls() variable whose static resolution yields zero routes gets an
159
+ // empty placeholder so an import of the sibling .gen.ts resolves. Nothing
160
+ // fills it in later: runtime discovery writes only *.named-routes.gen.ts
161
+ // (runtime-discovery.ts), so it stays `routes = {}` until a static run can
162
+ // resolve the module's includes and overwrites it.
163
+ if (isPlaceholder) {
164
+ if (!exists) {
165
+ writeFileSync(genPath, content);
129
166
  console.log(
130
167
  `[rango] Generated route types (placeholder) -> ${genPath}`,
131
168
  );
@@ -133,12 +170,8 @@ export function writePerModuleRouteTypesForFile(filePath: string): void {
133
170
  return;
134
171
  }
135
172
 
136
- const genSource = generatePerModuleTypesSource(routes);
137
- const existing = existsSync(genPath)
138
- ? readFileSync(genPath, "utf-8")
139
- : null;
140
- if (existing !== genSource) {
141
- writeFileSync(genPath, genSource);
173
+ if (!exists || readFileSync(genPath, "utf-8") !== content) {
174
+ writeFileSync(genPath, content);
142
175
  console.log(`[rango] Generated route types -> ${genPath}`);
143
176
  }
144
177
  } catch (err) {
@@ -508,6 +508,15 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
508
508
  if (ctx._routeName) {
509
509
  keyArgs.push(ctx._routeName);
510
510
  }
511
+ // A layout's / parallel's / their loader's ctx resolves dot names
512
+ // in its own module (withReverseScope), so one route's segments
513
+ // can reverse the same name differently.
514
+ const scope = ctx._reverseScope;
515
+ if (scope) {
516
+ keyArgs.push(
517
+ `reverse-scope:${scope.prefix ?? ""}:${scope.rootScoped}`,
518
+ );
519
+ }
511
520
  keyArgs.push(ctx.pathname, ctx.params);
512
521
  if (ctx._responseType) {
513
522
  keyArgs.push(ctx._responseType);
@@ -24,6 +24,7 @@ import {
24
24
  runInSegmentTagScope,
25
25
  } from "./cache-tag.js";
26
26
  import { reportCacheError } from "./cache-error.js";
27
+ import { isInterceptSegment } from "../browser/intercept-utils.js";
27
28
  import {
28
29
  predatesInvalidation,
29
30
  type ExecutionStart,
@@ -373,7 +374,7 @@ export class CacheScope {
373
374
  const next = id.charCodeAt(boundary.length);
374
375
  return !(next >= 48 && next <= 57);
375
376
  }
376
- return namespace?.startsWith("intercept:") === true;
377
+ return isInterceptSegment({ namespace });
377
378
  }
378
379
 
379
380
  /**
@@ -422,6 +422,7 @@ function shellHeadToEntry(head: ShellFrameHead): ShellEntryHead {
422
422
  reactVersion: head.rv,
423
423
  buildVersion: head.bv,
424
424
  initialTheme: head.i,
425
+ pathname: head.lp,
425
426
  docKey: head.dk,
426
427
  prunedRecords: head.pr,
427
428
  navigationOnly: head.no,
@@ -2695,6 +2696,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
2695
2696
  t: tags,
2696
2697
  ta: taggedAt,
2697
2698
  i: entry.initialTheme,
2699
+ lp: entry.pathname,
2698
2700
  dk: entry.docKey,
2699
2701
  pr: entry.prunedRecords,
2700
2702
  no: entry.navigationOnly,
@@ -47,6 +47,8 @@ export interface ShellFrameHead {
47
47
  ta?: number;
48
48
  /** ShellCacheEntry.initialTheme */
49
49
  i?: string;
50
+ /** ShellCacheEntry.pathname (a visitor-keyed shell's rendered location). */
51
+ lp?: string;
50
52
  /** ShellCacheEntry.docKey; navigation replay needs it after any round trip. */
51
53
  dk?: string;
52
54
  /** ShellCacheEntry.prunedRecords (diagnostic). */
@@ -79,6 +81,7 @@ function isShellFrameHead(value: unknown): value is ShellFrameHead {
79
81
  head.t.every((tag) => typeof tag === "string"))) &&
80
82
  (head.ta === undefined || isFiniteNumber(head.ta)) &&
81
83
  (head.i === undefined || typeof head.i === "string") &&
84
+ (head.lp === undefined || typeof head.lp === "string") &&
82
85
  (head.dk === undefined || typeof head.dk === "string") &&
83
86
  (head.pr === undefined || typeof head.pr === "string") &&
84
87
  (head.no === undefined || head.no === true) &&
@@ -468,6 +468,14 @@ export interface ShellCacheEntry {
468
468
  * ThemeProvider.
469
469
  */
470
470
  initialTheme?: string;
471
+ /**
472
+ * The payload pathname of a shell keyed by the visitor's URL
473
+ * (`rewrite(...).shell("visitor")`, visitorShellKey); absent when the
474
+ * capture rendered its key's own URL. The serve tail replays it as it does
475
+ * `initialTheme`, and the serve gate serves the entry only to a request
476
+ * that renders this location.
477
+ */
478
+ pathname?: string;
471
479
  /**
472
480
  * The CAPTURE DATA SNAPSHOT, in stored/serialized form: the doc segment
473
481
  * record every HIT replays the handler layer from (`docKey`) and the
@@ -271,6 +271,8 @@ interface VercelShellEnvelope {
271
271
  t?: string[];
272
272
  /** initialTheme the capture render was built with (resume theme fidelity). */
273
273
  i?: string;
274
+ /** ShellCacheEntry.pathname (a visitor-keyed shell's rendered location). */
275
+ lp?: string;
274
276
  /** Capture data snapshot (ShellCacheEntry.snapshot). */
275
277
  sn: ShellSnapshotRecord[];
276
278
  /**
@@ -1158,6 +1160,7 @@ export class VercelCacheStore<
1158
1160
  reactVersion: env.rv,
1159
1161
  buildVersion: env.bv,
1160
1162
  initialTheme: env.i,
1163
+ pathname: env.lp,
1161
1164
  snapshot: env.sn,
1162
1165
  docKey: env.dk,
1163
1166
  prunedRecords: env.pr,
@@ -1326,6 +1329,7 @@ export class VercelCacheStore<
1326
1329
  e: Math.min(expiresAt, now + retentionTtl * 1000),
1327
1330
  t: safeTags.length > 0 ? safeTags : undefined,
1328
1331
  i: entry.initialTheme,
1332
+ lp: entry.pathname,
1329
1333
  sn: entry.snapshot,
1330
1334
  dk: entry.docKey,
1331
1335
  pr: entry.prunedRecords,
@@ -1743,7 +1747,7 @@ export class VercelCacheStore<
1743
1747
 
1744
1748
  private asShellEnvelope(raw: unknown): VercelShellEnvelope | null {
1745
1749
  if (!isRecord(raw)) return null;
1746
- const { p, po, rv, bv, c, s, e, t, i, sn, dk, pr, no } = raw;
1750
+ const { p, po, rv, bv, c, s, e, t, i, lp, sn, dk, pr, no } = raw;
1747
1751
  if (typeof rv !== "string" || typeof bv !== "string") return null;
1748
1752
  // Document half: required unless navigationOnly (`no`), which stores
1749
1753
  // neither field. Tolerate a legacy navigationOnly envelope that still
@@ -1766,6 +1770,7 @@ export class VercelCacheStore<
1766
1770
  e,
1767
1771
  t: Array.isArray(t) ? (t as string[]) : undefined,
1768
1772
  i: typeof i === "string" ? i : undefined,
1773
+ lp: typeof lp === "string" ? lp : undefined,
1769
1774
  // A non-array field reads as no records.
1770
1775
  sn: Array.isArray(sn) ? (sn as ShellSnapshotRecord[]) : [],
1771
1776
  dk: typeof dk === "string" ? dk : undefined,
@@ -108,17 +108,21 @@ export function ClientUrlsRoot({
108
108
  definition,
109
109
  routeId,
110
110
  namePrefix = "",
111
+ foreign,
111
112
  }: {
112
113
  definition: ClientUrlPatterns;
113
114
  routeId: string;
114
115
  /** include() route-name prefix, injected at materialization for canonical
115
116
  * name composition (intercept-target coordination). */
116
117
  namePrefix?: string;
118
+ /** The server's routes that compete with the group's for a URL
119
+ * (competingPatterns in server-projection.ts). */
120
+ foreign?: readonly string[];
117
121
  }): ReactNode {
118
- // The include() mount prefix this group renders under ("/" at root). The
119
- // module trie is built from definition-LOCAL patterns; navigation strips
120
- // this prefix before matching (see stripMountPrefix in navigation.ts), the
121
- // same way client href()/useMount resolve include-relative URLs.
122
+ // The include() mount pattern this group renders under ("/" at root).
123
+ // Navigation matches an absolute pathname against the definition's
124
+ // patterns joined to it (buildClientUrlsTrie in client-urls.ts), as
125
+ // useReverse joins it.
122
126
  const mount = useMount();
123
127
  const [intent, setIntent] = useState<ClientUrlNavigationIntent | null>(null);
124
128
 
@@ -155,8 +159,9 @@ export function ClientUrlsRoot({
155
159
  namePrefix,
156
160
  setIntent,
157
161
  optimisticRoute ? undefined : routeId,
162
+ foreign,
158
163
  ),
159
- [definition, mount, namePrefix, routeId, optimisticRoute],
164
+ [definition, mount, namePrefix, routeId, optimisticRoute, foreign],
160
165
  );
161
166
 
162
167
  // Pending entries for the destination's loaders: useLoader use()s a Promise