@rangojs/router 0.0.0-experimental.97 → 0.0.0-experimental.98914650

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 (356) hide show
  1. package/README.md +24 -9
  2. package/dist/bin/rango.js +157 -63
  3. package/dist/testing/vitest.js +82 -0
  4. package/dist/vite/index.js +1584 -639
  5. package/package.json +71 -21
  6. package/skills/api-client/SKILL.md +211 -0
  7. package/skills/breadcrumbs/SKILL.md +60 -0
  8. package/skills/bundle-analysis/SKILL.md +159 -0
  9. package/skills/cache-guide/SKILL.md +222 -30
  10. package/skills/caching/SKILL.md +263 -8
  11. package/skills/composability/SKILL.md +27 -2
  12. package/skills/css/SKILL.md +76 -0
  13. package/skills/document-cache/SKILL.md +78 -55
  14. package/skills/handler-use/SKILL.md +3 -1
  15. package/skills/hooks/SKILL.md +235 -28
  16. package/skills/host-router/SKILL.md +122 -22
  17. package/skills/i18n/SKILL.md +276 -0
  18. package/skills/intercept/SKILL.md +29 -5
  19. package/skills/layout/SKILL.md +13 -9
  20. package/skills/links/SKILL.md +173 -17
  21. package/skills/loader/SKILL.md +170 -23
  22. package/skills/middleware/SKILL.md +16 -10
  23. package/skills/migrate-nextjs/SKILL.md +38 -16
  24. package/skills/mime-routes/SKILL.md +27 -0
  25. package/skills/observability/SKILL.md +137 -0
  26. package/skills/parallel/SKILL.md +11 -7
  27. package/skills/prerender/SKILL.md +14 -33
  28. package/skills/rango/SKILL.md +250 -25
  29. package/skills/react-compiler/SKILL.md +168 -0
  30. package/skills/response-routes/SKILL.md +114 -47
  31. package/skills/route/SKILL.md +42 -5
  32. package/skills/router-setup/SKILL.md +3 -3
  33. package/skills/server-actions/SKILL.md +78 -42
  34. package/skills/tailwind/SKILL.md +27 -3
  35. package/skills/testing/SKILL.md +129 -0
  36. package/skills/testing/bindings.md +89 -0
  37. package/skills/testing/cache-prerender.md +124 -0
  38. package/skills/testing/client-components.md +122 -0
  39. package/skills/testing/e2e-parity.md +125 -0
  40. package/skills/testing/flight.md +92 -0
  41. package/skills/testing/handles.md +129 -0
  42. package/skills/testing/loader.md +128 -0
  43. package/skills/testing/middleware.md +99 -0
  44. package/skills/testing/render-handler.md +121 -0
  45. package/skills/testing/response-routes.md +95 -0
  46. package/skills/testing/reverse-and-types.md +84 -0
  47. package/skills/testing/server-actions.md +107 -0
  48. package/skills/testing/server-tree.md +128 -0
  49. package/skills/testing/setup.md +120 -0
  50. package/skills/typesafety/SKILL.md +316 -26
  51. package/skills/use-cache/SKILL.md +36 -5
  52. package/skills/vercel/SKILL.md +107 -0
  53. package/skills/view-transitions/SKILL.md +294 -0
  54. package/src/__augment-tests__/augment.ts +81 -0
  55. package/src/__augment-tests__/augmented.check.ts +116 -0
  56. package/src/__internal.ts +0 -65
  57. package/src/browser/action-coordinator.ts +53 -36
  58. package/src/browser/action-fence.ts +47 -0
  59. package/src/browser/app-shell.ts +14 -27
  60. package/src/browser/cookie-name.ts +140 -0
  61. package/src/browser/event-controller.ts +37 -143
  62. package/src/browser/history-state.ts +21 -0
  63. package/src/browser/index.ts +3 -3
  64. package/src/browser/invalidate-client-cache.ts +52 -0
  65. package/src/browser/navigation-bridge.ts +30 -59
  66. package/src/browser/navigation-client.ts +96 -84
  67. package/src/browser/navigation-store-handle.ts +38 -0
  68. package/src/browser/navigation-store.ts +32 -82
  69. package/src/browser/navigation-transaction.ts +9 -59
  70. package/src/browser/partial-update.ts +60 -127
  71. package/src/browser/prefetch/cache.ts +82 -72
  72. package/src/browser/prefetch/fetch.ts +108 -33
  73. package/src/browser/prefetch/queue.ts +6 -3
  74. package/src/browser/rango-state.ts +157 -115
  75. package/src/browser/react/Link.tsx +0 -2
  76. package/src/browser/react/NavigationProvider.tsx +41 -48
  77. package/src/browser/react/ScrollRestoration.tsx +10 -6
  78. package/src/browser/react/filter-segment-order.ts +0 -2
  79. package/src/browser/react/index.ts +0 -48
  80. package/src/browser/react/location-state-shared.ts +166 -8
  81. package/src/browser/react/location-state.ts +39 -14
  82. package/src/browser/react/use-action.ts +6 -15
  83. package/src/browser/react/use-handle.ts +17 -14
  84. package/src/browser/react/use-link-status.ts +0 -4
  85. package/src/browser/react/use-navigation.ts +0 -3
  86. package/src/browser/react/use-params.ts +11 -11
  87. package/src/browser/react/use-reverse.ts +106 -0
  88. package/src/browser/react/use-router.ts +20 -5
  89. package/src/browser/react/use-search-params.ts +0 -5
  90. package/src/browser/react/use-segments.ts +0 -13
  91. package/src/browser/response-adapter.ts +52 -1
  92. package/src/browser/rsc-router.tsx +70 -34
  93. package/src/browser/scroll-restoration.ts +22 -14
  94. package/src/browser/segment-structure-assert.ts +2 -2
  95. package/src/browser/server-action-bridge.ts +168 -44
  96. package/src/browser/types.ts +36 -21
  97. package/src/browser/validate-redirect-origin.ts +43 -16
  98. package/src/build/collect-fallback-refs.ts +107 -0
  99. package/src/build/generate-manifest.ts +60 -35
  100. package/src/build/generate-route-types.ts +3 -0
  101. package/src/build/index.ts +8 -2
  102. package/src/build/prefix-tree-utils.ts +123 -0
  103. package/src/build/route-trie.ts +89 -10
  104. package/src/build/route-types/codegen.ts +4 -4
  105. package/src/build/route-types/include-resolution.ts +1 -1
  106. package/src/build/route-types/param-extraction.ts +6 -3
  107. package/src/build/route-types/per-module-writer.ts +7 -4
  108. package/src/build/route-types/router-processing.ts +122 -22
  109. package/src/build/route-types/scan-filter.ts +1 -1
  110. package/src/build/route-types/source-scan.ts +118 -0
  111. package/src/build/runtime-discovery.ts +9 -20
  112. package/src/cache/cache-error.ts +104 -0
  113. package/src/cache/cache-policy.ts +68 -28
  114. package/src/cache/cache-runtime.ts +134 -32
  115. package/src/cache/cache-scope.ts +100 -74
  116. package/src/cache/cache-tag.ts +98 -0
  117. package/src/cache/cf/cf-cache-store.ts +2255 -238
  118. package/src/cache/cf/index.ts +6 -16
  119. package/src/cache/document-cache.ts +61 -20
  120. package/src/cache/handle-snapshot.ts +63 -0
  121. package/src/cache/index.ts +22 -20
  122. package/src/cache/memory-segment-store.ts +136 -37
  123. package/src/cache/profile-registry.ts +6 -30
  124. package/src/cache/read-through-swr.ts +41 -11
  125. package/src/cache/segment-codec.ts +0 -16
  126. package/src/cache/tag-invalidation.ts +230 -0
  127. package/src/cache/types.ts +33 -100
  128. package/src/cache/vercel/index.ts +11 -0
  129. package/src/cache/vercel/vercel-cache-store.ts +799 -0
  130. package/src/client.rsc.tsx +6 -21
  131. package/src/client.tsx +25 -61
  132. package/src/component-utils.ts +19 -0
  133. package/src/context-var.ts +17 -5
  134. package/src/decode-loader-results.ts +36 -0
  135. package/src/defer.ts +196 -0
  136. package/src/deps/ssr.ts +0 -1
  137. package/src/errors.ts +30 -4
  138. package/src/handle.ts +31 -23
  139. package/src/handles/MetaTags.tsx +0 -14
  140. package/src/handles/breadcrumbs.ts +16 -5
  141. package/src/handles/meta.ts +0 -39
  142. package/src/host/cookie-handler.ts +0 -36
  143. package/src/host/errors.ts +0 -24
  144. package/src/host/index.ts +8 -2
  145. package/src/host/pattern-matcher.ts +7 -50
  146. package/src/host/router.ts +107 -99
  147. package/src/host/testing.ts +40 -27
  148. package/src/host/types.ts +37 -4
  149. package/src/host/utils.ts +1 -1
  150. package/src/href-client.ts +137 -22
  151. package/src/index.rsc.ts +63 -9
  152. package/src/index.ts +64 -9
  153. package/src/internal-debug.ts +2 -4
  154. package/src/loader-store.ts +500 -0
  155. package/src/loader.rsc.ts +20 -13
  156. package/src/loader.ts +12 -11
  157. package/src/missing-id-error.ts +68 -0
  158. package/src/network-error-thrower.tsx +1 -6
  159. package/src/outlet-provider.tsx +1 -5
  160. package/src/prerender/param-hash.ts +10 -11
  161. package/src/prerender/store.ts +32 -37
  162. package/src/prerender.ts +61 -6
  163. package/src/redirect-origin.ts +100 -0
  164. package/src/response-utils.ts +9 -0
  165. package/src/reverse.ts +65 -40
  166. package/src/root-error-boundary.tsx +1 -19
  167. package/src/route-content-wrapper.tsx +7 -72
  168. package/src/route-definition/dsl-helpers.ts +244 -281
  169. package/src/route-definition/helper-factories.ts +29 -139
  170. package/src/route-definition/helpers-types.ts +40 -17
  171. package/src/route-definition/redirect.ts +43 -9
  172. package/src/route-definition/resolve-handler-use.ts +6 -0
  173. package/src/route-definition/use-item-types.ts +32 -0
  174. package/src/route-map-builder.ts +0 -16
  175. package/src/route-types.ts +19 -41
  176. package/src/router/basename.ts +14 -0
  177. package/src/router/content-negotiation.ts +15 -15
  178. package/src/router/error-handling.ts +13 -17
  179. package/src/router/find-match.ts +44 -23
  180. package/src/router/handler-context.ts +4 -41
  181. package/src/router/intercept-resolution.ts +14 -19
  182. package/src/router/lazy-includes.ts +9 -46
  183. package/src/router/loader-resolution.ts +91 -46
  184. package/src/router/logging.ts +0 -6
  185. package/src/router/manifest.ts +18 -29
  186. package/src/router/match-api.ts +0 -20
  187. package/src/router/match-context.ts +0 -22
  188. package/src/router/match-handlers.ts +57 -58
  189. package/src/router/match-middleware/background-revalidation.ts +0 -7
  190. package/src/router/match-middleware/cache-lookup.ts +150 -271
  191. package/src/router/match-middleware/cache-store.ts +3 -33
  192. package/src/router/match-middleware/intercept-resolution.ts +0 -22
  193. package/src/router/match-middleware/segment-resolution.ts +0 -22
  194. package/src/router/match-pipelines.ts +1 -42
  195. package/src/router/match-result.ts +31 -80
  196. package/src/router/metrics.ts +0 -34
  197. package/src/router/middleware-types.ts +5 -112
  198. package/src/router/middleware.ts +118 -133
  199. package/src/router/navigation-snapshot.ts +0 -51
  200. package/src/router/params-util.ts +23 -0
  201. package/src/router/pattern-matching.ts +62 -67
  202. package/src/router/prerender-match.ts +99 -63
  203. package/src/router/preview-match.ts +3 -1
  204. package/src/router/request-classification.ts +28 -62
  205. package/src/router/revalidation.ts +50 -56
  206. package/src/router/route-snapshot.ts +0 -1
  207. package/src/router/router-context.ts +0 -27
  208. package/src/router/router-interfaces.ts +68 -35
  209. package/src/router/router-options.ts +55 -1
  210. package/src/router/router-registry.ts +2 -5
  211. package/src/router/segment-resolution/fresh.ts +44 -63
  212. package/src/router/segment-resolution/helpers.ts +34 -0
  213. package/src/router/segment-resolution/loader-cache.ts +40 -37
  214. package/src/router/segment-resolution/revalidation.ts +203 -285
  215. package/src/router/segment-resolution/static-store.ts +19 -5
  216. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  217. package/src/router/segment-resolution/view-transition-default.ts +36 -0
  218. package/src/router/segment-resolution.ts +4 -1
  219. package/src/router/segment-wrappers.ts +0 -3
  220. package/src/router/state-cookie-name.ts +33 -0
  221. package/src/router/substitute-pattern-params.ts +56 -0
  222. package/src/router/telemetry-otel.ts +0 -20
  223. package/src/router/telemetry.ts +96 -19
  224. package/src/router/timeout.ts +0 -20
  225. package/src/router/trie-matching.ts +87 -48
  226. package/src/router/types.ts +9 -63
  227. package/src/router/url-params.ts +0 -5
  228. package/src/router.ts +80 -41
  229. package/src/rsc/handler-context.ts +3 -2
  230. package/src/rsc/handler.ts +83 -78
  231. package/src/rsc/helpers.ts +93 -5
  232. package/src/rsc/index.ts +1 -1
  233. package/src/rsc/json-route-result.ts +38 -0
  234. package/src/rsc/manifest-init.ts +28 -41
  235. package/src/rsc/origin-guard.ts +39 -25
  236. package/src/rsc/progressive-enhancement.ts +12 -1
  237. package/src/rsc/redirect-guard.ts +99 -0
  238. package/src/rsc/response-error.ts +79 -12
  239. package/src/rsc/response-route-handler.ts +76 -62
  240. package/src/rsc/rsc-rendering.ts +41 -60
  241. package/src/rsc/runtime-warnings.ts +23 -10
  242. package/src/rsc/server-action.ts +62 -67
  243. package/src/rsc/ssr-setup.ts +16 -0
  244. package/src/rsc/types.ts +10 -5
  245. package/src/runtime-env.ts +18 -0
  246. package/src/search-params.ts +4 -20
  247. package/src/segment-loader-promise.ts +14 -2
  248. package/src/segment-system.tsx +199 -142
  249. package/src/serialize.ts +243 -0
  250. package/src/server/context.ts +150 -51
  251. package/src/server/cookie-store.ts +80 -5
  252. package/src/server/handle-store.ts +7 -24
  253. package/src/server/loader-registry.ts +5 -24
  254. package/src/server/request-context.ts +165 -87
  255. package/src/ssr/index.tsx +14 -14
  256. package/src/static-handler.ts +10 -13
  257. package/src/testing/cache-status.ts +162 -0
  258. package/src/testing/collect-handle.ts +40 -0
  259. package/src/testing/dispatch.ts +618 -0
  260. package/src/testing/dom.entry.ts +22 -0
  261. package/src/testing/e2e/fixture.ts +188 -0
  262. package/src/testing/e2e/index.ts +128 -0
  263. package/src/testing/e2e/matchers.ts +35 -0
  264. package/src/testing/e2e/page-helpers.ts +272 -0
  265. package/src/testing/e2e/parity.ts +387 -0
  266. package/src/testing/e2e/server.ts +195 -0
  267. package/src/testing/flight-matchers.ts +97 -0
  268. package/src/testing/flight-normalize.ts +11 -0
  269. package/src/testing/flight-runtime.d.ts +57 -0
  270. package/src/testing/flight-tree.ts +682 -0
  271. package/src/testing/flight.entry.ts +52 -0
  272. package/src/testing/flight.ts +232 -0
  273. package/src/testing/generated-routes.ts +183 -0
  274. package/src/testing/index.ts +99 -0
  275. package/src/testing/internal/context.ts +348 -0
  276. package/src/testing/internal/flight-client-globals.ts +30 -0
  277. package/src/testing/internal/seed-vars.ts +54 -0
  278. package/src/testing/render-handler.ts +330 -0
  279. package/src/testing/render-route.tsx +566 -0
  280. package/src/testing/run-loader.ts +378 -0
  281. package/src/testing/run-middleware.ts +205 -0
  282. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  283. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  284. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  285. package/src/testing/vitest-stubs/version.ts +5 -0
  286. package/src/testing/vitest.ts +305 -0
  287. package/src/theme/ThemeProvider.tsx +0 -52
  288. package/src/theme/ThemeScript.tsx +0 -6
  289. package/src/theme/constants.ts +0 -12
  290. package/src/theme/index.ts +0 -7
  291. package/src/theme/theme-context.ts +1 -5
  292. package/src/theme/theme-script.ts +0 -14
  293. package/src/theme/use-theme.ts +0 -3
  294. package/src/types/boundaries.ts +0 -35
  295. package/src/types/cache-types.ts +13 -4
  296. package/src/types/error-types.ts +30 -90
  297. package/src/types/global-namespace.ts +54 -41
  298. package/src/types/handler-context.ts +97 -22
  299. package/src/types/index.ts +1 -10
  300. package/src/types/loader-types.ts +6 -3
  301. package/src/types/request-scope.ts +0 -19
  302. package/src/types/route-config.ts +6 -50
  303. package/src/types/route-entry.ts +0 -6
  304. package/src/types/segments.ts +18 -14
  305. package/src/urls/include-helper.ts +9 -56
  306. package/src/urls/index.ts +1 -11
  307. package/src/urls/path-helper-types.ts +19 -5
  308. package/src/urls/path-helper.ts +17 -106
  309. package/src/urls/pattern-types.ts +36 -19
  310. package/src/urls/response-types.ts +20 -19
  311. package/src/urls/type-extraction.ts +58 -139
  312. package/src/urls/urls-function.ts +1 -18
  313. package/src/use-loader.tsx +292 -107
  314. package/src/vite/debug.ts +1 -0
  315. package/src/vite/discovery/bundle-postprocess.ts +8 -7
  316. package/src/vite/discovery/discover-routers.ts +95 -82
  317. package/src/vite/discovery/discovery-errors.ts +194 -0
  318. package/src/vite/discovery/prerender-collection.ts +26 -34
  319. package/src/vite/discovery/route-types-writer.ts +40 -84
  320. package/src/vite/discovery/state.ts +39 -1
  321. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  322. package/src/vite/index.ts +4 -0
  323. package/src/vite/plugin-types.ts +185 -10
  324. package/src/vite/plugins/cjs-to-esm.ts +3 -18
  325. package/src/vite/plugins/client-ref-dedup.ts +0 -11
  326. package/src/vite/plugins/client-ref-hashing.ts +12 -11
  327. package/src/vite/plugins/cloudflare-protocol-stub.ts +1 -21
  328. package/src/vite/plugins/expose-action-id.ts +4 -75
  329. package/src/vite/plugins/expose-id-utils.ts +3 -54
  330. package/src/vite/plugins/expose-ids/export-analysis.ts +76 -34
  331. package/src/vite/plugins/expose-ids/handler-transform.ts +6 -74
  332. package/src/vite/plugins/expose-ids/loader-transform.ts +3 -20
  333. package/src/vite/plugins/expose-ids/router-transform.ts +0 -13
  334. package/src/vite/plugins/expose-internal-ids.ts +57 -67
  335. package/src/vite/plugins/performance-tracks.ts +9 -16
  336. package/src/vite/plugins/refresh-cmd.ts +1 -1
  337. package/src/vite/plugins/use-cache-transform.ts +26 -49
  338. package/src/vite/plugins/vercel-output.ts +258 -0
  339. package/src/vite/plugins/version-injector.ts +2 -32
  340. package/src/vite/plugins/version-plugin.ts +32 -23
  341. package/src/vite/plugins/virtual-entries.ts +35 -17
  342. package/src/vite/rango.ts +148 -115
  343. package/src/vite/router-discovery.ts +220 -68
  344. package/src/vite/utils/ast-handler-extract.ts +15 -31
  345. package/src/vite/utils/bundle-analysis.ts +10 -15
  346. package/src/vite/utils/client-chunks.ts +184 -0
  347. package/src/vite/utils/forward-user-plugins.ts +171 -0
  348. package/src/vite/utils/manifest-utils.ts +4 -59
  349. package/src/vite/utils/package-resolution.ts +1 -73
  350. package/src/vite/utils/prerender-utils.ts +0 -34
  351. package/src/vite/utils/shared-utils.ts +95 -43
  352. package/src/browser/action-response-classifier.ts +0 -99
  353. package/src/browser/react/use-client-cache.ts +0 -58
  354. package/src/browser/shallow.ts +0 -40
  355. package/src/handles/index.ts +0 -7
  356. package/src/router/middleware-cookies.ts +0 -55
@@ -14,7 +14,6 @@ import { traverseBack } from "./pattern-matching.js";
14
14
  import type { RouteMatchResult } from "./pattern-matching.js";
15
15
  import type { RouteSnapshot } from "./route-snapshot.js";
16
16
 
17
- // Response type -> MIME type used for Accept header matching
18
17
  export const RESPONSE_TYPE_MIME: Record<string, string> = {
19
18
  json: "application/json",
20
19
  text: "text/plain",
@@ -23,7 +22,6 @@ export const RESPONSE_TYPE_MIME: Record<string, string> = {
23
22
  md: "text/markdown",
24
23
  };
25
24
 
26
- // Reverse lookup: MIME type -> response type tag (e.g. "text/html" -> "html")
27
25
  export const MIME_RESPONSE_TYPE: Record<string, string> = Object.fromEntries(
28
26
  Object.entries(RESPONSE_TYPE_MIME).map(([tag, mime]) => [mime, tag]),
29
27
  );
@@ -71,12 +69,10 @@ export function parseAcceptTypes(accept: string): AcceptEntry[] {
71
69
  }
72
70
  entries.push({ mime, q, order: i });
73
71
  }
74
- // Sort: highest q first, then lowest client order first (stable)
75
72
  entries.sort((a, b) => b.q - a.q || a.order - b.order);
76
73
  return entries;
77
74
  }
78
75
 
79
- // Sentinel response type for RSC routes in negotiation candidates
80
76
  export const RSC_RESPONSE_TYPE = "__rsc__";
81
77
 
82
78
  /**
@@ -89,7 +85,6 @@ export function pickNegotiateVariant(
89
85
  acceptEntries: AcceptEntry[],
90
86
  candidates: Array<{ routeKey: string; responseType: string }>,
91
87
  ): { routeKey: string; responseType: string } {
92
- // Build a MIME -> candidate lookup for O(1) matching
93
88
  const byCandidateMime = new Map<
94
89
  string,
95
90
  { routeKey: string; responseType: string }
@@ -106,9 +101,7 @@ export function pickNegotiateVariant(
106
101
 
107
102
  for (const entry of acceptEntries) {
108
103
  if (entry.q === 0) continue;
109
- // Wildcard matches first candidate
110
104
  if (entry.mime === "*/*") return candidates[0]!;
111
- // Type wildcard (e.g. "text/*") -- match first candidate with that type
112
105
  if (entry.mime.endsWith("/*")) {
113
106
  const typePrefix = entry.mime.slice(0, entry.mime.indexOf("/"));
114
107
  for (const [mime, candidate] of byCandidateMime) {
@@ -119,7 +112,6 @@ export function pickNegotiateVariant(
119
112
  const match = byCandidateMime.get(entry.mime);
120
113
  if (match) return match;
121
114
  }
122
- // No match -- use first candidate as default
123
115
  return candidates[0]!;
124
116
  }
125
117
 
@@ -135,8 +127,8 @@ export interface NegotiationResult {
135
127
  manifestEntry: EntryData;
136
128
  /** Route middleware for the winning variant */
137
129
  routeMiddleware: CollectedMiddleware[];
138
- /** Always true negotiation occurred */
139
- negotiated: true;
130
+ /** True when negotiation selected a variant; false for a plain response route. */
131
+ negotiated: boolean;
140
132
  }
141
133
 
142
134
  /**
@@ -155,12 +147,24 @@ export async function negotiateRoute(
155
147
  ): Promise<NegotiationResult | null> {
156
148
  const { matched, manifestEntry, routeMiddleware, responseType } = snapshot;
157
149
  if (!matched.negotiateVariants || matched.negotiateVariants.length === 0) {
150
+ // No variants: a plain response route still yields a result (negotiated:false)
151
+ // so callers don't re-derive it; RSC routes (no responseType/handler) -> null.
152
+ const handler =
153
+ manifestEntry.type === "route" ? manifestEntry.handler : undefined;
154
+ if (responseType && handler) {
155
+ return {
156
+ responseType,
157
+ handler: handler as Function,
158
+ manifestEntry,
159
+ routeMiddleware,
160
+ negotiated: false,
161
+ };
162
+ }
158
163
  return null;
159
164
  }
160
165
 
161
166
  const acceptEntries = parseAcceptTypes(request.headers.get("accept") || "");
162
167
 
163
- // Build candidate list preserving definition order.
164
168
  const variants = matched.negotiateVariants;
165
169
  let candidates: Array<{ routeKey: string; responseType: string }>;
166
170
  if (responseType) {
@@ -177,12 +181,10 @@ export async function negotiateRoute(
177
181
 
178
182
  const variant = pickNegotiateVariant(acceptEntries, candidates);
179
183
 
180
- // RSC won negotiation
181
184
  if (variant.responseType === RSC_RESPONSE_TYPE) {
182
185
  return null;
183
186
  }
184
187
 
185
- // Primary response-type won — use existing manifest entry and middleware
186
188
  if (responseType && variant.routeKey === matched.routeKey) {
187
189
  return {
188
190
  responseType,
@@ -192,8 +194,6 @@ export async function negotiateRoute(
192
194
  negotiated: true,
193
195
  };
194
196
  }
195
-
196
- // Different variant won — load its manifest entry
197
197
  const negotiateEntry = await loadManifest(
198
198
  matched.entry,
199
199
  variant.routeKey,
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Router Error Handling Utilities
3
3
  *
4
- * Error boundary and not-found boundary handling for RSC Router.
4
+ * Error boundary and not-found boundary handling for Rango.
5
5
  * Also includes the shared invokeOnError utility for error callback invocation.
6
6
  */
7
7
 
@@ -117,16 +117,10 @@ export function findNearestErrorBoundary(
117
117
  let current: EntryData | null = entry;
118
118
 
119
119
  while (current) {
120
- // Check if this entry has error boundaries defined
121
120
  if (current.errorBoundary && current.errorBoundary.length > 0) {
122
- // Return the last error boundary (most recently defined takes precedence)
123
121
  return current.errorBoundary[current.errorBoundary.length - 1];
124
122
  }
125
123
 
126
- // Check orphan layouts for error boundaries
127
- // Orphan layouts are siblings that render alongside the main route chain
128
- // They can define error boundaries that catch errors from routes in the same route group
129
- // Check from first to last (first sibling takes precedence as the "outer" wrapper)
130
124
  if (current.layout && current.layout.length > 0) {
131
125
  for (const orphan of current.layout) {
132
126
  if (orphan.errorBoundary && orphan.errorBoundary.length > 0) {
@@ -153,11 +147,21 @@ export function findNearestNotFoundBoundary(
153
147
  let current: EntryData | null = entry;
154
148
 
155
149
  while (current) {
156
- // Check if this entry has notFound boundaries defined
157
150
  if (current.notFoundBoundary && current.notFoundBoundary.length > 0) {
158
- // Return the last notFound boundary (most recently defined takes precedence)
159
151
  return current.notFoundBoundary[current.notFoundBoundary.length - 1];
160
152
  }
153
+
154
+ // Check orphan layouts mirroring findNearestErrorBoundary: notFoundBoundary
155
+ // attaches identically (onto parent.notFoundBoundary), and an orphan layout
156
+ // (parent=null) is reachable only via this scan. First sibling is "outer".
157
+ if (current.layout && current.layout.length > 0) {
158
+ for (const orphan of current.layout) {
159
+ if (orphan.notFoundBoundary && orphan.notFoundBoundary.length > 0) {
160
+ return orphan.notFoundBoundary[orphan.notFoundBoundary.length - 1];
161
+ }
162
+ }
163
+ }
164
+
161
165
  current = current.parent;
162
166
  }
163
167
 
@@ -207,22 +211,17 @@ export function createErrorSegment(
207
211
  entry: EntryData,
208
212
  params: Record<string, string>,
209
213
  ): ResolvedSegment {
210
- // Determine the component to render
211
214
  let component: ReactNode;
212
215
 
213
216
  if (typeof fallback === "function") {
214
- // ErrorBoundaryHandler - call with error info
215
217
  const props: ErrorBoundaryFallbackProps = {
216
218
  error: errorInfo,
217
219
  };
218
220
  component = fallback(props);
219
221
  } else {
220
- // Static ReactNode fallback
221
222
  component = fallback;
222
223
  }
223
224
 
224
- // Error segment uses the same ID as the layout that has the error boundary
225
- // The error boundary content replaces the layout's outlet content
226
225
  return {
227
226
  id: entry.shortCode,
228
227
  namespace: entry.id,
@@ -261,17 +260,14 @@ export function createNotFoundSegment(
261
260
  entry: EntryData,
262
261
  params: Record<string, string>,
263
262
  ): ResolvedSegment {
264
- // Determine the component to render
265
263
  let component: ReactNode;
266
264
 
267
265
  if (typeof fallback === "function") {
268
- // NotFoundBoundaryHandler - call with props
269
266
  const props: NotFoundBoundaryFallbackProps = {
270
267
  notFound: notFoundInfo,
271
268
  };
272
269
  component = fallback(props);
273
270
  } else {
274
- // Static ReactNode fallback
275
271
  component = fallback;
276
272
  }
277
273
 
@@ -1,5 +1,5 @@
1
1
  import { tryTrieMatch } from "./trie-matching.js";
2
- import { getRouteTrie, getRouterTrie } from "../route-map-builder.js";
2
+ import { getRouterTrie } from "../route-map-builder.js";
3
3
  import {
4
4
  findMatch as findRouteMatch,
5
5
  isLazyEvaluationNeeded,
@@ -8,6 +8,16 @@ import {
8
8
  import type { MetricsStore } from "../server/context";
9
9
  import type { RouteEntry } from "../types";
10
10
 
11
+ // The single-entry cache is module-lifetime, keyed only on pathname, so the same
12
+ // result object is handed to every same-pathname request. ctx.params aliases
13
+ // this object, so handlers mutating it would corrupt the cache for later requests.
14
+ // Clone params; entry/flags are read-only and shared safely.
15
+ function cloneMatchResult<TEnv>(
16
+ r: RouteMatchResult<TEnv> | null,
17
+ ): RouteMatchResult<TEnv> | null {
18
+ return r ? { ...r, params: { ...r.params } } : null;
19
+ }
20
+
11
21
  export interface FindMatchDeps<TEnv = any> {
12
22
  routesEntries: RouteEntry<TEnv>[];
13
23
  evaluateLazyEntry: (entry: RouteEntry<TEnv>) => void;
@@ -27,20 +37,14 @@ export function createFindMatch<TEnv = any>(
27
37
  let lastFindMatchPathname: string | null = null;
28
38
  let lastFindMatchResult: RouteMatchResult<TEnv> | null = null;
29
39
 
30
- // Wrapper for findMatch that uses routesEntries
31
- // Handles lazy evaluation by evaluating lazy entries on first match.
32
- // Phase 1: try O(path_length) trie match.
33
- // Phase 2: fall back to regex iteration.
34
40
  return function findMatch(
35
41
  pathname: string,
36
42
  ms?: MetricsStore,
37
43
  ): RouteMatchResult<TEnv> | null {
38
- // Return cached result if same pathname (avoids double-match per request)
39
44
  if (lastFindMatchPathname === pathname) {
40
- return lastFindMatchResult;
45
+ return cloneMatchResult(lastFindMatchResult);
41
46
  }
42
47
 
43
- // Helper to push sub-metrics
44
48
  const pushMetric = ms
45
49
  ? (label: string, start: number) => {
46
50
  ms.metrics.push({
@@ -51,17 +55,15 @@ export function createFindMatch<TEnv = any>(
51
55
  }
52
56
  : undefined;
53
57
 
54
- // Phase 1: Try trie match (O(path_length))
55
- // Only use the per-router trie. The global trie merges routes from ALL
56
- // routers and must not be used — in multi-router setups (host routing)
57
- // overlapping paths like "/" would match the wrong app's route.
58
58
  const routeTrie = getRouterTrie(deps.routerId);
59
+ let trieMatched = false;
59
60
  if (routeTrie) {
60
61
  const trieStart = performance.now();
61
62
  const trieResult = tryTrieMatch(routeTrie, pathname);
62
63
  pushMetric?.("match:trie", trieStart);
63
64
 
64
65
  if (trieResult) {
66
+ trieMatched = true;
65
67
  // Find the RouteEntry that contains this route.
66
68
  // Multiple entries can share the same staticPrefix (e.g., several
67
69
  // include("/", patterns) calls all produce staticPrefix=""). Evaluate
@@ -83,12 +85,8 @@ export function createFindMatch<TEnv = any>(
83
85
  }
84
86
  }
85
87
 
86
- // If no entry had the route in its routes map, use the first matching
87
- // entry as fallback (handles main entry with inline routes not yet
88
- // reflected in its routes object).
89
88
  if (!entry) entry = fallbackEntry;
90
89
 
91
- // If entry not found (nested include not yet discovered), evaluate parent
92
90
  if (!entry) {
93
91
  const parent = deps.routesEntries.find(
94
92
  (e) =>
@@ -112,9 +110,7 @@ export function createFindMatch<TEnv = any>(
112
110
  entry,
113
111
  routeKey: trieResult.routeKey,
114
112
  params: trieResult.params,
115
- optionalParams: new Set(trieResult.optionalParams || []),
116
113
  redirectTo: trieResult.redirectTo,
117
- ancestry: trieResult.ancestry,
118
114
  ...(trieResult.pr ? { pr: true } : {}),
119
115
  ...(trieResult.pt ? { pt: true } : {}),
120
116
  ...(trieResult.responseType
@@ -125,17 +121,14 @@ export function createFindMatch<TEnv = any>(
125
121
  : {}),
126
122
  ...(trieResult.rscFirst ? { rscFirst: true } : {}),
127
123
  };
128
- return lastFindMatchResult;
124
+ return cloneMatchResult(lastFindMatchResult);
129
125
  }
130
126
  }
131
127
  }
132
128
 
133
- // Phase 2: Fall back to existing matching (regex iteration)
134
129
  const regexStart = performance.now();
135
130
  let result = findRouteMatch(pathname, deps.routesEntries);
136
131
 
137
- // If we hit a lazy entry that needs evaluation, evaluate and retry.
138
- // Cap iterations to prevent infinite loops from pathological nesting.
139
132
  const MAX_LAZY_ITERATIONS = 100;
140
133
  let iterations = 0;
141
134
  while (isLazyEvaluationNeeded(result)) {
@@ -153,8 +146,36 @@ export function createFindMatch<TEnv = any>(
153
146
  }
154
147
  pushMetric?.("match:regex-fallback", regexStart);
155
148
 
149
+ // The trie is the single source of truth and is built before findMatch in
150
+ // both dev (handler rebuild) and production (ensureRouterManifest). If the
151
+ // trie was present yet the regex fallback resolved a real match, the trie
152
+ // has a gap (e.g. a route shape it cannot represent) and dev/prod could
153
+ // diverge if the trie were ever absent. Surface it in dev; folded out in
154
+ // production builds.
155
+ //
156
+ // Suppress when the trie DID match (`trieMatched`): that path falls through
157
+ // to the regex fallback only on the first request to a not-yet-spliced lazy
158
+ // entry (e.g. a 2+-level nested include whose deeper parent has not been
159
+ // evaluated). The trie knew the route; runtime lazy discovery simply lagged.
160
+ // That is the supported lazy-include flow, not a trie gap, so warning on it
161
+ // is a false positive (it manufactures bug reports and erodes the signal).
162
+ if (
163
+ process.env.NODE_ENV !== "production" &&
164
+ routeTrie &&
165
+ !trieMatched &&
166
+ result &&
167
+ !isLazyEvaluationNeeded(result)
168
+ ) {
169
+ console.warn(
170
+ `[@rangojs/router] Route "${pathname}" resolved via the regex fallback ` +
171
+ `even though the route trie was present. The trie should be the single ` +
172
+ `matching source of truth; this indicates a trie gap. Please report this ` +
173
+ `with your route configuration.`,
174
+ );
175
+ }
176
+
156
177
  lastFindMatchPathname = pathname;
157
178
  lastFindMatchResult = result;
158
- return result;
179
+ return cloneMatchResult(result);
159
180
  };
160
181
  }
@@ -18,7 +18,7 @@ import { isInsideCacheScope } from "../server/context.js";
18
18
  import { NOCACHE_SYMBOL, assertNotInsideCacheExec } from "../cache/taint.js";
19
19
  import { isAutoGeneratedRouteName } from "../route-name.js";
20
20
  import { PRERENDER_PASSTHROUGH } from "../prerender.js";
21
- import { encodePathSegment } from "./url-params.js";
21
+ import { substitutePatternParams } from "./substitute-pattern-params.js";
22
22
  import { fireAndForgetWaitUntil } from "../types/request-scope.js";
23
23
 
24
24
  /**
@@ -160,51 +160,14 @@ export function createReverseFunction(
160
160
  );
161
161
  }
162
162
 
163
- let result = pattern;
164
-
165
163
  // Merge current request params as defaults, explicit params override
166
164
  const effectiveParams = currentParams
167
165
  ? { ...currentParams, ...hrefParams }
168
166
  : hrefParams;
169
167
 
170
- // Substitute params (strip constraint and optional syntax: :param(a|b)? -> value)
171
- // Optional params (:param?) are omitted when not provided
172
- if (effectiveParams) {
173
- let hadOmittedOptional = false;
174
- // First pass: optional params (trailing ?)
175
- result = result.replace(
176
- /:([a-zA-Z_][a-zA-Z0-9_]*)(\([^)]*\))?(\?)/g,
177
- (_, key) => {
178
- const value = effectiveParams[key];
179
- // Empty string is treated as omitted — the trie matcher fills
180
- // unmatched optional params with "" (not undefined), so reverse
181
- // must collapse those segments instead of leaving empty slots.
182
- if (value === undefined || value === "") {
183
- hadOmittedOptional = true;
184
- return "";
185
- }
186
- return encodePathSegment(value);
187
- },
188
- );
189
- // Second pass: required params (no trailing ?)
190
- result = result.replace(
191
- /:([a-zA-Z_][a-zA-Z0-9_]*)(\([^)]*\))?(?!\?)/g,
192
- (_, key) => {
193
- const value = effectiveParams[key];
194
- if (value === undefined) {
195
- throw new Error(`Missing param "${key}" for route "${name}"`);
196
- }
197
- return encodePathSegment(value);
198
- },
199
- );
200
- // Clean up slashes only when an optional param was actually omitted,
201
- // so intentional trailing-slash patterns like "/blog/" are preserved.
202
- if (hadOmittedOptional) {
203
- const hadTrailingSlash = pattern.length > 1 && pattern.endsWith("/");
204
- result = result.replace(/\/\/+/g, "/").replace(/\/+$/, "") || "/";
205
- if (hadTrailingSlash && !result.endsWith("/")) result += "/";
206
- }
207
- }
168
+ let result = effectiveParams
169
+ ? substitutePatternParams(pattern, effectiveParams, name)
170
+ : pattern;
208
171
 
209
172
  // Append search params as query string
210
173
  if (search) {
@@ -21,7 +21,10 @@ import { getRequestContext } from "../server/request-context.js";
21
21
  import { executeInterceptMiddleware } from "./middleware.js";
22
22
  import { createReverseFunction } from "./handler-context.js";
23
23
  import { getGlobalRouteMap } from "../route-map-builder.js";
24
- import { handleHandlerResult } from "./segment-resolution.js";
24
+ import {
25
+ handleHandlerResult,
26
+ warnOnStreamedResponse,
27
+ } from "./segment-resolution.js";
25
28
  import type { SegmentResolutionDeps } from "./types.js";
26
29
  import { debugLog } from "./logging.js";
27
30
  import { runInsideLoaderScope } from "../server/context.js";
@@ -66,28 +69,14 @@ export function findInterceptForRoute(
66
69
  let current: EntryData | null = fromEntry;
67
70
 
68
71
  while (current) {
69
- if (current.intercept && current.intercept.length > 0) {
70
- for (const intercept of current.intercept) {
72
+ // current first, then its sibling layouts — same order as before.
73
+ for (const source of [current, ...current.layout]) {
74
+ for (const intercept of source.intercept) {
71
75
  if (
72
76
  intercept.routeName === targetRouteKey &&
73
77
  evaluateInterceptWhen(intercept, selectorContext, isAction)
74
78
  ) {
75
- return { intercept, entry: current };
76
- }
77
- }
78
- }
79
-
80
- if (current.layout && current.layout.length > 0) {
81
- for (const siblingLayout of current.layout) {
82
- if (siblingLayout.intercept && siblingLayout.intercept.length > 0) {
83
- for (const intercept of siblingLayout.intercept) {
84
- if (
85
- intercept.routeName === targetRouteKey &&
86
- evaluateInterceptWhen(intercept, selectorContext, isAction)
87
- ) {
88
- return { intercept, entry: siblingLayout };
89
- }
90
- }
79
+ return { intercept, entry: source };
91
80
  }
92
81
  }
93
82
  }
@@ -242,6 +231,12 @@ export async function resolveInterceptEntry<TEnv>(
242
231
  let loaderDataPromise: Promise<any[]> | any[] | undefined;
243
232
 
244
233
  if (interceptEntry.loading && loaderPromises.length > 0) {
234
+ if (handlerResult instanceof Promise) {
235
+ warnOnStreamedResponse(
236
+ handlerResult,
237
+ `intercept ${interceptEntry.slotName}`,
238
+ );
239
+ }
245
240
  component =
246
241
  handlerResult instanceof Promise
247
242
  ? handlerResult
@@ -1,8 +1,8 @@
1
1
  import { registerRouteMap } from "../route-map-builder.js";
2
- import { extractStaticPrefix } from "./pattern-matching.js";
2
+ import { extractStaticPrefix, joinPrefix } from "./pattern-matching.js";
3
3
  import {
4
4
  type EntryData,
5
- RSCRouterContext,
5
+ RangoContext,
6
6
  runWithPrefixes,
7
7
  getIsolatedLazyParent,
8
8
  } from "../server/context";
@@ -18,9 +18,6 @@ export interface LazyEvalDeps<TEnv = any> {
18
18
  routerId?: string;
19
19
  }
20
20
 
21
- // Detect lazy includes in handler result and create placeholder entries
22
- // Lazy includes are IncludeItem with lazy: true and _lazyContext
23
- // Moved to outer scope so it can be reused by evaluateLazyEntry for nested includes
24
21
  export function findLazyIncludes<TEnv = any>(
25
22
  items: AllUseItems[],
26
23
  ): Array<{
@@ -56,7 +53,6 @@ export function findLazyIncludes<TEnv = any>(
56
53
  });
57
54
  }
58
55
  }
59
- // Recursively check nested items (in layouts, etc.)
60
56
  if ((item as any).uses && Array.isArray((item as any).uses)) {
61
57
  lazyItems.push(...findLazyIncludes((item as any).uses));
62
58
  }
@@ -78,14 +74,6 @@ export function evaluateLazyEntry<TEnv = any>(
78
74
  return;
79
75
  }
80
76
 
81
- // Check for pre-computed routes from build-time data.
82
- // Only leaf nodes (no nested includes) are precomputed, so entries with
83
- // nested lazy includes fall through to the handler below.
84
- // When multiple entries share the same staticPrefix (e.g., several
85
- // include("/", ...) calls), the precomputed data merges all their routes
86
- // into one entry. Assigning that merged set to the first matching entry
87
- // causes findMatch to pick the wrong handler for routes belonging to a
88
- // different include. Skip the shortcut when the prefix is shared.
89
77
  const currentPrecomputed = deps.getPrecomputedByPrefix();
90
78
  if (currentPrecomputed) {
91
79
  const routes = currentPrecomputed.get(entry.staticPrefix);
@@ -105,25 +93,18 @@ export function evaluateLazyEntry<TEnv = any>(
105
93
  }
106
94
  }
107
95
 
108
- // Mark as evaluated immediately to prevent concurrent evaluation.
109
- // JS is single-threaded but handlers.handler() could theoretically yield,
110
- // and the while-loop in findMatch retries after evaluation.
111
96
  entry.lazyEvaluated = true;
112
97
 
113
98
  const lazyPatterns = entry.lazyPatterns as UrlPatterns<TEnv>;
114
99
  const lazyContext = entry.lazyContext;
115
100
 
116
- // Create a new context for evaluating the lazy patterns
117
101
  const manifest = new Map<string, EntryData>();
118
102
  const patterns = new Map<string, string>();
119
103
  const patternsByPrefix = new Map<string, Map<string, string>>();
120
104
  const trailingSlashMap = new Map<string, TrailingSlashMode>();
121
105
 
122
- // Capture the handler result to detect nested lazy includes
123
106
  let handlerResult: AllUseItems[] = [];
124
107
 
125
- // Merge captured counters from include() to maintain consistent
126
- // shortCode indices with sibling entries from pattern extraction
127
108
  const lazyCounters: Record<string, number> = {};
128
109
  if (lazyContext?.counters) {
129
110
  for (const [key, value] of Object.entries(lazyContext.counters)) {
@@ -131,7 +112,7 @@ export function evaluateLazyEntry<TEnv = any>(
131
112
  }
132
113
  }
133
114
 
134
- RSCRouterContext.run(
115
+ RangoContext.run(
135
116
  {
136
117
  manifest,
137
118
  patterns,
@@ -145,10 +126,8 @@ export function evaluateLazyEntry<TEnv = any>(
145
126
  includeScope: lazyContext?.includeScope,
146
127
  },
147
128
  () => {
148
- // Run the lazy patterns handler with the original context prefixes
149
- // The prefix comes from the IncludeItem stored in lazyPatterns
150
129
  const includePrefix = (entry as any)._lazyPrefix || "";
151
- const fullPrefix = (lazyContext?.urlPrefix || "") + includePrefix;
130
+ const fullPrefix = joinPrefix(lazyContext?.urlPrefix, includePrefix);
152
131
 
153
132
  if (fullPrefix || lazyContext?.namePrefix) {
154
133
  runWithPrefixes(fullPrefix, lazyContext?.namePrefix, () => {
@@ -160,11 +139,9 @@ export function evaluateLazyEntry<TEnv = any>(
160
139
  },
161
140
  );
162
141
 
163
- // Populate the entry's routes from the patterns
164
142
  const routesObject: Record<string, string> = {};
165
143
  for (const [name, pattern] of patterns.entries()) {
166
144
  routesObject[name] = pattern;
167
- // Also add to merged route map for reverse() support
168
145
  const existingPattern = deps.mergedRouteMap[name];
169
146
  if (existingPattern !== undefined && existingPattern !== pattern) {
170
147
  console.warn(
@@ -175,46 +152,33 @@ export function evaluateLazyEntry<TEnv = any>(
175
152
  deps.mergedRouteMap[name] = pattern;
176
153
  }
177
154
 
178
- // Update the entry in-place
179
155
  entry.routes = routesObject as ResolvedRouteMap<any>;
180
156
 
181
- // Note: Do NOT clear lazyPatterns/lazyContext here.
182
- // loadManifest() needs them on every request to re-run the handler
183
- // in the correct AsyncLocalStorage context (Store.manifest).
184
-
185
- // Update trailing slash config if available
186
157
  if (trailingSlashMap.size > 0) {
187
158
  entry.trailingSlash = Object.fromEntries(trailingSlashMap);
188
159
  }
189
160
 
190
- // Detect nested lazy includes and register them as new entries
191
161
  const nestedLazyIncludes = findLazyIncludes(handlerResult);
192
162
  for (const lazyInclude of nestedLazyIncludes) {
193
- // Compute the full URL prefix (combining parent prefix if any)
194
- const fullPrefix = lazyInclude.context.urlPrefix
195
- ? lazyInclude.context.urlPrefix + lazyInclude.prefix
196
- : lazyInclude.prefix;
163
+ const fullPrefix = joinPrefix(
164
+ lazyInclude.context.urlPrefix,
165
+ lazyInclude.prefix,
166
+ );
197
167
 
198
168
  const nestedEntry: RouteEntry<TEnv> & { _lazyPrefix?: string } = {
199
169
  prefix: "",
200
170
  staticPrefix: extractStaticPrefix(fullPrefix),
201
- routes: {} as ResolvedRouteMap<any>, // Empty until first match
171
+ routes: {} as ResolvedRouteMap<any>,
202
172
  trailingSlash: entry.trailingSlash,
203
173
  handler: (lazyInclude.patterns as UrlPatterns<TEnv>).handler,
204
174
  mountIndex: deps.nextMountIndex(),
205
175
  routerId: deps.routerId,
206
- // Lazy evaluation fields
207
176
  lazy: true,
208
177
  lazyPatterns: lazyInclude.patterns,
209
178
  lazyContext: lazyInclude.context,
210
179
  lazyEvaluated: false,
211
- // Store the include prefix for evaluation
212
180
  _lazyPrefix: lazyInclude.prefix,
213
181
  };
214
- // Insert nested lazy entry before any entry whose staticPrefix is a
215
- // prefix of (but shorter than) this lazy entry's staticPrefix.
216
- // This ensures more specific lazy includes are matched before
217
- // less specific eager entries (e.g., "/href/nested" before "/href/:id").
218
182
  const nestedPrefix = nestedEntry.staticPrefix;
219
183
  let insertIndex = deps.routesEntries.length;
220
184
  if (nestedPrefix) {
@@ -232,6 +196,5 @@ export function evaluateLazyEntry<TEnv = any>(
232
196
  deps.routesEntries.splice(insertIndex, 0, nestedEntry);
233
197
  }
234
198
 
235
- // Re-register route map for runtime reverse() usage
236
199
  registerRouteMap(deps.mergedRouteMap);
237
200
  }