@rangojs/router 0.0.0-experimental.8 → 0.0.0-experimental.8a4d0430

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/AGENTS.md +5 -0
  2. package/README.md +884 -4
  3. package/dist/bin/rango.js +1601 -0
  4. package/dist/vite/index.js +4474 -867
  5. package/package.json +60 -51
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +262 -0
  8. package/skills/caching/SKILL.md +50 -21
  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 +78 -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 +87 -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 +285 -553
  42. package/src/browser/navigation-client.ts +124 -71
  43. package/src/browser/navigation-store.ts +33 -50
  44. package/src/browser/navigation-transaction.ts +295 -0
  45. package/src/browser/network-error-handler.ts +61 -0
  46. package/src/browser/partial-update.ts +258 -308
  47. package/src/browser/prefetch/cache.ts +146 -0
  48. package/src/browser/prefetch/fetch.ts +135 -0
  49. package/src/browser/prefetch/observer.ts +65 -0
  50. package/src/browser/prefetch/policy.ts +42 -0
  51. package/src/browser/prefetch/queue.ts +88 -0
  52. package/src/browser/rango-state.ts +112 -0
  53. package/src/browser/react/Link.tsx +185 -73
  54. package/src/browser/react/NavigationProvider.tsx +51 -11
  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 +6 -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 +107 -26
  76. package/src/browser/scroll-restoration.ts +92 -16
  77. package/src/browser/segment-reconciler.ts +216 -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 +109 -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 +469 -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 +338 -0
  100. package/src/cache/cache-scope.ts +120 -303
  101. package/src/cache/cf/cf-cache-store.ts +119 -7
  102. package/src/cache/cf/index.ts +8 -2
  103. package/src/cache/document-cache.ts +101 -72
  104. package/src/cache/handle-capture.ts +81 -0
  105. package/src/cache/handle-snapshot.ts +41 -0
  106. package/src/cache/index.ts +0 -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 +17 -7
  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 +21 -15
  126. package/src/host/errors.ts +8 -8
  127. package/src/host/index.ts +4 -7
  128. package/src/host/pattern-matcher.ts +27 -27
  129. package/src/host/router.ts +61 -39
  130. package/src/host/testing.ts +8 -8
  131. package/src/host/types.ts +15 -7
  132. package/src/host/utils.ts +1 -1
  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 -157
  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 +934 -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 +211 -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 +158 -0
  160. package/src/router/handler-context.ts +374 -81
  161. package/src/router/intercept-resolution.ts +395 -0
  162. package/src/router/lazy-includes.ts +234 -0
  163. package/src/router/loader-resolution.ts +215 -122
  164. package/src/router/logging.ts +248 -0
  165. package/src/router/manifest.ts +148 -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 +80 -93
  170. package/src/router/match-middleware/cache-lookup.ts +382 -9
  171. package/src/router/match-middleware/cache-store.ts +51 -22
  172. package/src/router/match-middleware/intercept-resolution.ts +55 -17
  173. package/src/router/match-middleware/segment-resolution.ts +24 -6
  174. package/src/router/match-pipelines.ts +10 -45
  175. package/src/router/match-result.ts +34 -28
  176. package/src/router/metrics.ts +235 -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 +324 -367
  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 +36 -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 +570 -0
  189. package/src/router/segment-resolution/helpers.ts +263 -0
  190. package/src/router/segment-resolution/loader-cache.ts +198 -0
  191. package/src/router/segment-resolution/revalidation.ts +1241 -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 +289 -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 +692 -4257
  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 +235 -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 +25 -13
  219. package/src/server/context.ts +182 -51
  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 +430 -70
  225. package/src/server.ts +35 -130
  226. package/src/ssr/index.tsx +100 -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 +687 -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 +102 -0
  243. package/src/types/segments.ts +148 -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 +110 -0
  261. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  262. package/src/vite/index.ts +11 -1133
  263. package/src/vite/plugin-types.ts +131 -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 -51
  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 +254 -0
  279. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  280. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  281. package/src/vite/rango.ts +510 -0
  282. package/src/vite/router-discovery.ts +785 -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/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -14,6 +14,8 @@ import { extractStaticPrefix } from "../router/pattern-matching.js";
14
14
  import { RSCRouterContext, runWithPrefixes } from "../server/context.js";
15
15
  import type { EntryData, TrackedInclude } from "../server/context.js";
16
16
  import type { TrailingSlashMode } from "../types.js";
17
+ import { createRouteHelpers } from "../route-definition.js";
18
+ import MapRootLayout from "../server/root-layout.js";
17
19
 
18
20
  /**
19
21
  * Node in the prefix tree
@@ -39,8 +41,16 @@ export interface GeneratedManifest {
39
41
  prefixTree: Record<string, PrefixTreeNode>;
40
42
  /** Complete route name → pattern mapping for href() */
41
43
  routeManifest: Record<string, string>;
42
- /** Generation timestamp */
43
- generatedAt: string;
44
+ /** Route name → trailing slash mode for trie redirect handling */
45
+ routeTrailingSlash?: Record<string, string>;
46
+ /** Route names using Prerender (for dev-mode Node.js delegation) */
47
+ prerenderRoutes?: string[];
48
+ /** Route names with passthrough: true (handler kept in bundle for live fallback) */
49
+ passthroughRoutes?: string[];
50
+ /** Route name → response type for non-RSC routes */
51
+ responseTypeRoutes?: Record<string, string>;
52
+ /** Route name -> search schema descriptor for typed URL helpers */
53
+ routeSearchSchemas?: Record<string, Record<string, string>>;
44
54
  }
45
55
 
46
56
  /**
@@ -52,11 +62,19 @@ function buildPrefixTreeNode(
52
62
  namePrefix: string | undefined,
53
63
  patterns: UrlPatterns<any>,
54
64
  routeManifest: Record<string, string>,
55
- visited: Set<unknown> = new Set()
65
+ routeAncestry: Record<string, string[]>, // internal: feeds trie building, not exported
66
+ mountIndex: number,
67
+ visited: Set<unknown> = new Set(),
68
+ routeTrailingSlash?: Record<string, string>,
69
+ prerenderRoutes?: string[],
70
+ prerenderDefs?: Record<string, any>,
71
+ passthroughRoutes?: string[],
72
+ responseTypeRoutes?: Record<string, string>,
73
+ routeSearchSchemas?: Record<string, Record<string, string>>,
56
74
  ): PrefixTreeNode {
57
75
  if (visited.has(patterns)) {
58
76
  console.warn(
59
- `[@rangojs/router] Circular include detected at prefix "${urlPrefix}". Skipping.`
77
+ `[@rangojs/router] Circular include detected at prefix "${urlPrefix}". Skipping.`,
60
78
  );
61
79
  return {
62
80
  staticPrefix: extractStaticPrefix(urlPrefix),
@@ -72,6 +90,7 @@ function buildPrefixTreeNode(
72
90
  const patternsMap = new Map<string, string>();
73
91
  const patternsByPrefix = new Map<string, Map<string, string>>();
74
92
  const trailingSlashMap = new Map<string, TrailingSlashMode>();
93
+ const searchSchemasMap = new Map<string, Record<string, string>>();
75
94
  const trackedIncludes: TrackedInclude[] = [];
76
95
 
77
96
  RSCRouterContext.run(
@@ -80,17 +99,25 @@ function buildPrefixTreeNode(
80
99
  patterns: patternsMap,
81
100
  patternsByPrefix,
82
101
  trailingSlash: trailingSlashMap,
102
+ searchSchemas: searchSchemasMap,
83
103
  namespace: "build",
84
104
  parent: null,
85
105
  counters: {},
106
+ mountIndex,
86
107
  trackedIncludes, // Enable nested include tracking
87
108
  },
88
109
  () => {
89
- // Run patterns with the URL and name prefixes
90
- runWithPrefixes(urlPrefix, namePrefix, () => {
110
+ const helpers = createRouteHelpers();
111
+ // Wrap in root layout for correct parent hierarchy (matches runtime)
112
+ helpers.layout(MapRootLayout, () => {
113
+ if (urlPrefix || namePrefix) {
114
+ return runWithPrefixes(urlPrefix, namePrefix, () => {
115
+ return patterns.handler() as AllUseItems[];
116
+ });
117
+ }
91
118
  return patterns.handler() as AllUseItems[];
92
119
  });
93
- }
120
+ },
94
121
  );
95
122
 
96
123
  // Collect route names defined in this include (routes have prefixes applied)
@@ -100,7 +127,51 @@ function buildPrefixTreeNode(
100
127
  routeManifest[name] = pattern;
101
128
  }
102
129
 
103
- // Build children from tracked nested includes
130
+ // Collect trailing slash config
131
+ if (routeTrailingSlash) {
132
+ for (const [name, mode] of trailingSlashMap.entries()) {
133
+ routeTrailingSlash[name] = mode;
134
+ }
135
+ }
136
+ if (routeSearchSchemas) {
137
+ for (const [name, schema] of searchSchemasMap.entries()) {
138
+ routeSearchSchemas[name] = schema;
139
+ }
140
+ }
141
+
142
+ // Capture ancestry from manifest entries' parent chains
143
+ captureAncestry(manifest, routeAncestry);
144
+
145
+ // Collect prerender route names and handler definitions from manifest entries
146
+ if (prerenderRoutes) {
147
+ for (const [name, entry] of manifest) {
148
+ if (entry.type === "route" && entry.isPrerender) {
149
+ prerenderRoutes.push(name);
150
+ if (prerenderDefs && entry.prerenderDef) {
151
+ prerenderDefs[name] = entry.prerenderDef;
152
+ }
153
+ if (
154
+ passthroughRoutes &&
155
+ entry.prerenderDef?.options?.passthrough === true
156
+ ) {
157
+ passthroughRoutes.push(name);
158
+ }
159
+ }
160
+ }
161
+ }
162
+
163
+ // Collect response type routes from manifest entries
164
+ if (responseTypeRoutes) {
165
+ for (const [name, entry] of manifest) {
166
+ if (entry.type === "route" && entry.responseType) {
167
+ responseTypeRoutes[name] = entry.responseType;
168
+ }
169
+ }
170
+ }
171
+
172
+ // Build children from tracked nested includes.
173
+ // Multiple includes can share the same fullPrefix (e.g., include("/", patternsA),
174
+ // include("/", patternsB)). Merge their routes instead of overwriting.
104
175
  const children: Record<string, PrefixTreeNode> = {};
105
176
 
106
177
  for (const include of trackedIncludes) {
@@ -109,12 +180,31 @@ function buildPrefixTreeNode(
109
180
  include.namePrefix,
110
181
  include.patterns as UrlPatterns<any>,
111
182
  routeManifest,
112
- visited
183
+ routeAncestry,
184
+ mountIndex,
185
+ visited,
186
+ routeTrailingSlash,
187
+ prerenderRoutes,
188
+ prerenderDefs,
189
+ passthroughRoutes,
190
+ responseTypeRoutes,
191
+ routeSearchSchemas,
113
192
  );
114
193
 
115
- children[include.fullPrefix] = childNode;
194
+ const existing = children[include.fullPrefix];
195
+ if (existing) {
196
+ existing.routes.push(...childNode.routes);
197
+ Object.assign(existing.children, childNode.children);
198
+ } else {
199
+ children[include.fullPrefix] = childNode;
200
+ }
116
201
  }
117
202
 
203
+ // Remove from visited so sibling branches can reuse the same patterns
204
+ // without false circular-include detection. Only ancestors in the current
205
+ // recursion path should trigger the cycle guard.
206
+ visited.delete(patterns);
207
+
118
208
  return {
119
209
  staticPrefix: extractStaticPrefix(urlPrefix),
120
210
  fullPrefix: urlPrefix,
@@ -125,11 +215,40 @@ function buildPrefixTreeNode(
125
215
  }
126
216
 
127
217
  /**
128
- * Generate manifest from UrlPatterns
218
+ * Walk parent chains of route entries to extract ancestry shortCodes.
219
+ */
220
+ function captureAncestry(
221
+ manifest: Map<string, EntryData>,
222
+ routeAncestry: Record<string, string[]>,
223
+ ): void {
224
+ for (const [routeName, entry] of manifest) {
225
+ if (entry.type === "route") {
226
+ const ancestry: string[] = [];
227
+ let current: EntryData | null = entry;
228
+ while (current) {
229
+ ancestry.unshift(current.shortCode);
230
+ current = current.parent;
231
+ }
232
+ routeAncestry[routeName] = ancestry;
233
+ }
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Internal manifest result including build-pipeline-only fields.
239
+ * Not part of the public API — use generateManifest() for the public surface.
240
+ */
241
+ export interface FullManifest extends GeneratedManifest {
242
+ _routeAncestry: Record<string, string[]>;
243
+ _prerenderDefs?: Record<string, any>;
244
+ }
245
+
246
+ /**
247
+ * Generate manifest from UrlPatterns (public API).
129
248
  *
130
- * This runs all patterns (including lazy ones) at build time to extract:
131
- * - The complete prefix tree for short-circuit optimization
132
- * - The complete route manifest for href()
249
+ * Returns only the public GeneratedManifest fields. Internal build pipeline
250
+ * consumers that need _routeAncestry or _prerenderDefs should use
251
+ * generateManifestFull() instead.
133
252
  *
134
253
  * @example
135
254
  * ```typescript
@@ -145,9 +264,30 @@ function buildPrefixTreeNode(
145
264
  * ```
146
265
  */
147
266
  export function generateManifest<TEnv>(
148
- urlpatterns: UrlPatterns<TEnv, any>
267
+ urlpatterns: UrlPatterns<TEnv, any>,
268
+ mountIndex: number = 0,
149
269
  ): GeneratedManifest {
270
+ const {
271
+ _routeAncestry: _,
272
+ _prerenderDefs: __,
273
+ ...publicManifest
274
+ } = generateManifestFull(urlpatterns, mountIndex);
275
+ return publicManifest;
276
+ }
277
+
278
+ /**
279
+ * Generate manifest with internal build-pipeline fields.
280
+ *
281
+ * Used by the Vite plugin (discover-routers via dynamic import through
282
+ * @rangojs/router/build), manifest-init (direct import), and trie
283
+ * building. Not intended for external use.
284
+ */
285
+ export function generateManifestFull<TEnv>(
286
+ urlpatterns: UrlPatterns<TEnv, any>,
287
+ mountIndex: number = 0,
288
+ ): FullManifest {
150
289
  const routeManifest: Record<string, string> = {};
290
+ const routeAncestry: Record<string, string[]> = {};
151
291
  const prefixTree: Record<string, PrefixTreeNode> = {};
152
292
 
153
293
  // Run the root patterns handler with tracking enabled
@@ -155,6 +295,7 @@ export function generateManifest<TEnv>(
155
295
  const patternsMap = new Map<string, string>();
156
296
  const patternsByPrefix = new Map<string, Map<string, string>>();
157
297
  const trailingSlashMap = new Map<string, TrailingSlashMode>();
298
+ const searchSchemasMap = new Map<string, Record<string, string>>();
158
299
  const trackedIncludes: TrackedInclude[] = [];
159
300
 
160
301
  RSCRouterContext.run(
@@ -163,22 +304,61 @@ export function generateManifest<TEnv>(
163
304
  patterns: patternsMap,
164
305
  patternsByPrefix,
165
306
  trailingSlash: trailingSlashMap,
307
+ searchSchemas: searchSchemasMap,
166
308
  namespace: "build",
167
309
  parent: null,
168
310
  counters: {},
311
+ mountIndex,
169
312
  trackedIncludes, // Enable include tracking
170
313
  },
171
314
  () => {
172
- urlpatterns.handler();
173
- }
315
+ const helpers = createRouteHelpers();
316
+ // Wrap in root layout for correct parent hierarchy (matches runtime)
317
+ helpers.layout(MapRootLayout, () => {
318
+ return urlpatterns.handler() as AllUseItems[];
319
+ });
320
+ },
174
321
  );
175
322
 
176
- // Collect root-level routes
323
+ // Collect root-level routes and trailing slash config
324
+ const routeTrailingSlash: Record<string, string> = {};
177
325
  for (const [name, pattern] of patternsMap.entries()) {
178
326
  routeManifest[name] = pattern;
179
327
  }
328
+ for (const [name, mode] of trailingSlashMap.entries()) {
329
+ routeTrailingSlash[name] = mode;
330
+ }
331
+ const routeSearchSchemas: Record<string, Record<string, string>> = {};
332
+ for (const [name, schema] of searchSchemasMap.entries()) {
333
+ routeSearchSchemas[name] = schema;
334
+ }
335
+
336
+ // Capture ancestry from manifest entries' parent chains
337
+ captureAncestry(manifest, routeAncestry);
338
+
339
+ // Collect prerender route names and handler definitions across all levels
340
+ const prerenderRoutes: string[] = [];
341
+ const prerenderDefs: Record<string, any> = {};
342
+ const passthroughRoutes: string[] = [];
343
+ const responseTypeRoutes: Record<string, string> = {};
344
+ for (const [name, entry] of manifest) {
345
+ if (entry.type === "route" && entry.isPrerender) {
346
+ prerenderRoutes.push(name);
347
+ if (entry.prerenderDef) {
348
+ prerenderDefs[name] = entry.prerenderDef;
349
+ }
350
+ if (entry.prerenderDef?.options?.passthrough === true) {
351
+ passthroughRoutes.push(name);
352
+ }
353
+ }
354
+ if (entry.type === "route" && entry.responseType) {
355
+ responseTypeRoutes[name] = entry.responseType;
356
+ }
357
+ }
180
358
 
181
- // Build prefix tree from tracked includes (shared visited set for cycle detection)
359
+ // Build prefix tree from tracked includes (shared visited set for cycle detection).
360
+ // Multiple includes can share the same fullPrefix (e.g., include("/", patternsA),
361
+ // include("/", patternsB)). Merge their routes instead of overwriting.
182
362
  const visited = new Set<unknown>();
183
363
  for (const include of trackedIncludes) {
184
364
  const node = buildPrefixTreeNode(
@@ -186,16 +366,48 @@ export function generateManifest<TEnv>(
186
366
  include.namePrefix,
187
367
  include.patterns as UrlPatterns<any>,
188
368
  routeManifest,
189
- visited
369
+ routeAncestry,
370
+ mountIndex,
371
+ visited,
372
+ routeTrailingSlash,
373
+ prerenderRoutes,
374
+ prerenderDefs,
375
+ passthroughRoutes,
376
+ responseTypeRoutes,
377
+ routeSearchSchemas,
190
378
  );
191
379
 
192
- prefixTree[include.fullPrefix] = node;
380
+ const existing = prefixTree[include.fullPrefix];
381
+ if (existing) {
382
+ existing.routes.push(...node.routes);
383
+ Object.assign(existing.children, node.children);
384
+ } else {
385
+ prefixTree[include.fullPrefix] = node;
386
+ }
193
387
  }
194
388
 
195
389
  return {
196
390
  prefixTree,
197
391
  routeManifest,
198
- generatedAt: new Date().toISOString(),
392
+ routeTrailingSlash:
393
+ Object.keys(routeTrailingSlash).length > 0
394
+ ? routeTrailingSlash
395
+ : undefined,
396
+ prerenderRoutes: prerenderRoutes.length > 0 ? prerenderRoutes : undefined,
397
+ passthroughRoutes:
398
+ passthroughRoutes.length > 0 ? passthroughRoutes : undefined,
399
+ responseTypeRoutes:
400
+ Object.keys(responseTypeRoutes).length > 0
401
+ ? responseTypeRoutes
402
+ : undefined,
403
+ routeSearchSchemas:
404
+ Object.keys(routeSearchSchemas).length > 0
405
+ ? routeSearchSchemas
406
+ : undefined,
407
+ _routeAncestry: routeAncestry,
408
+ // Internal: prerender handler definitions for build-time getParams() access
409
+ _prerenderDefs:
410
+ Object.keys(prerenderDefs).length > 0 ? prerenderDefs : undefined,
199
411
  };
200
412
  }
201
413
 
@@ -209,13 +421,12 @@ export function generateManifest<TEnv>(
209
421
  * ```
210
422
  */
211
423
  export function generateManifestCode<TEnv>(
212
- urlpatterns: UrlPatterns<TEnv, any>
424
+ urlpatterns: UrlPatterns<TEnv, any>,
213
425
  ): string {
214
426
  const manifest = generateManifest(urlpatterns);
215
427
 
216
428
  return `/**
217
429
  * Auto-generated route manifest
218
- * Generated at: ${manifest.generatedAt}
219
430
  *
220
431
  * DO NOT EDIT - This file is generated by @rangojs/router
221
432
  */
@@ -0,0 +1,36 @@
1
+ // Barrel re-export -- see route-types/ for implementations.
2
+ export {
3
+ extractParamsFromPattern,
4
+ formatRouteEntry,
5
+ } from "./route-types/param-extraction.js";
6
+ export { extractRoutesFromSource } from "./route-types/ast-route-extraction.js";
7
+ export {
8
+ generatePerModuleTypesSource,
9
+ generateRouteTypesSource,
10
+ } from "./route-types/codegen.js";
11
+ export {
12
+ DEFAULT_EXCLUDE_PATTERNS,
13
+ type ScanFilter,
14
+ createScanFilter,
15
+ findTsFiles,
16
+ } from "./route-types/scan-filter.js";
17
+ export {
18
+ writePerModuleRouteTypes,
19
+ writePerModuleRouteTypesForFile,
20
+ } from "./route-types/per-module-writer.js";
21
+ export {
22
+ type UnresolvableReason,
23
+ type UnresolvableInclude,
24
+ extractIncludesWithDiagnostics,
25
+ } from "./route-types/include-resolution.js";
26
+ export {
27
+ extractUrlsVariableFromRouter,
28
+ buildCombinedRouteMapForRouterFile,
29
+ detectUnresolvableIncludes,
30
+ detectUnresolvableIncludesForUrlsFile,
31
+ findNestedRouterConflict,
32
+ formatNestedRouterConflictError,
33
+ findRouterFiles,
34
+ writeCombinedRouteTypes,
35
+ } from "./route-types/router-processing.js";
36
+ export { findUrlsVariableNames } from "./route-types/per-module-writer.js";
@@ -17,6 +17,19 @@
17
17
 
18
18
  export {
19
19
  generateManifest,
20
+ generateManifestFull,
20
21
  generateManifestCode,
21
22
  type GeneratedManifest,
22
23
  } from "./generate-manifest.js";
24
+
25
+ export { buildRouteTrie, type TrieNode, type TrieLeaf } from "./route-trie.js";
26
+
27
+ export {
28
+ writePerModuleRouteTypes,
29
+ extractRoutesFromSource,
30
+ generatePerModuleTypesSource,
31
+ createScanFilter,
32
+ type ScanFilter,
33
+ } from "./generate-route-types.js";
34
+
35
+ export { hashParams } from "../prerender/param-hash.js";