@rangojs/router 0.0.0-experimental.7 → 0.0.0-experimental.70

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 (307) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +942 -4
  3. package/dist/bin/rango.js +1689 -0
  4. package/dist/vite/index.js +4951 -930
  5. package/package.json +70 -60
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +294 -0
  8. package/skills/caching/SKILL.md +93 -23
  9. package/skills/composability/SKILL.md +172 -0
  10. package/skills/debug-manifest/SKILL.md +12 -8
  11. package/skills/document-cache/SKILL.md +18 -16
  12. package/skills/fonts/SKILL.md +167 -0
  13. package/skills/hooks/SKILL.md +334 -72
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +131 -8
  16. package/skills/layout/SKILL.md +100 -3
  17. package/skills/links/SKILL.md +92 -31
  18. package/skills/loader/SKILL.md +404 -44
  19. package/skills/middleware/SKILL.md +173 -34
  20. package/skills/mime-routes/SKILL.md +128 -0
  21. package/skills/parallel/SKILL.md +204 -1
  22. package/skills/prerender/SKILL.md +685 -0
  23. package/skills/rango/SKILL.md +85 -16
  24. package/skills/response-routes/SKILL.md +411 -0
  25. package/skills/route/SKILL.md +257 -14
  26. package/skills/router-setup/SKILL.md +210 -32
  27. package/skills/tailwind/SKILL.md +129 -0
  28. package/skills/theme/SKILL.md +9 -8
  29. package/skills/typesafety/SKILL.md +328 -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/app-version.ts +14 -0
  36. package/src/browser/event-controller.ts +92 -64
  37. package/src/browser/history-state.ts +80 -0
  38. package/src/browser/intercept-utils.ts +52 -0
  39. package/src/browser/link-interceptor.ts +24 -4
  40. package/src/browser/logging.ts +55 -0
  41. package/src/browser/merge-segment-loaders.ts +20 -12
  42. package/src/browser/navigation-bridge.ts +296 -558
  43. package/src/browser/navigation-client.ts +179 -69
  44. package/src/browser/navigation-store.ts +73 -55
  45. package/src/browser/navigation-transaction.ts +297 -0
  46. package/src/browser/network-error-handler.ts +61 -0
  47. package/src/browser/partial-update.ts +328 -313
  48. package/src/browser/prefetch/cache.ts +206 -0
  49. package/src/browser/prefetch/fetch.ts +150 -0
  50. package/src/browser/prefetch/observer.ts +65 -0
  51. package/src/browser/prefetch/policy.ts +48 -0
  52. package/src/browser/prefetch/queue.ts +160 -0
  53. package/src/browser/prefetch/resource-ready.ts +77 -0
  54. package/src/browser/rango-state.ts +112 -0
  55. package/src/browser/react/Link.tsx +230 -74
  56. package/src/browser/react/NavigationProvider.tsx +87 -11
  57. package/src/browser/react/context.ts +11 -0
  58. package/src/browser/react/filter-segment-order.ts +11 -0
  59. package/src/browser/react/index.ts +12 -12
  60. package/src/browser/react/location-state-shared.ts +95 -53
  61. package/src/browser/react/location-state.ts +60 -15
  62. package/src/browser/react/mount-context.ts +6 -1
  63. package/src/browser/react/nonce-context.ts +23 -0
  64. package/src/browser/react/shallow-equal.ts +27 -0
  65. package/src/browser/react/use-action.ts +29 -51
  66. package/src/browser/react/use-client-cache.ts +5 -3
  67. package/src/browser/react/use-handle.ts +30 -126
  68. package/src/browser/react/use-href.tsx +2 -2
  69. package/src/browser/react/use-link-status.ts +6 -5
  70. package/src/browser/react/use-navigation.ts +22 -63
  71. package/src/browser/react/use-params.ts +65 -0
  72. package/src/browser/react/use-pathname.ts +47 -0
  73. package/src/browser/react/use-router.ts +76 -0
  74. package/src/browser/react/use-search-params.ts +56 -0
  75. package/src/browser/react/use-segments.ts +80 -97
  76. package/src/browser/response-adapter.ts +73 -0
  77. package/src/browser/rsc-router.tsx +214 -58
  78. package/src/browser/scroll-restoration.ts +127 -52
  79. package/src/browser/segment-reconciler.ts +221 -0
  80. package/src/browser/segment-structure-assert.ts +16 -0
  81. package/src/browser/server-action-bridge.ts +510 -603
  82. package/src/browser/shallow.ts +6 -1
  83. package/src/browser/types.ts +141 -48
  84. package/src/browser/validate-redirect-origin.ts +29 -0
  85. package/src/build/generate-manifest.ts +235 -24
  86. package/src/build/generate-route-types.ts +39 -0
  87. package/src/build/index.ts +13 -0
  88. package/src/build/route-trie.ts +265 -0
  89. package/src/build/route-types/ast-helpers.ts +25 -0
  90. package/src/build/route-types/ast-route-extraction.ts +98 -0
  91. package/src/build/route-types/codegen.ts +102 -0
  92. package/src/build/route-types/include-resolution.ts +418 -0
  93. package/src/build/route-types/param-extraction.ts +48 -0
  94. package/src/build/route-types/per-module-writer.ts +128 -0
  95. package/src/build/route-types/router-processing.ts +618 -0
  96. package/src/build/route-types/scan-filter.ts +85 -0
  97. package/src/build/runtime-discovery.ts +231 -0
  98. package/src/cache/background-task.ts +34 -0
  99. package/src/cache/cache-key-utils.ts +44 -0
  100. package/src/cache/cache-policy.ts +125 -0
  101. package/src/cache/cache-runtime.ts +342 -0
  102. package/src/cache/cache-scope.ts +167 -309
  103. package/src/cache/cf/cf-cache-store.ts +571 -17
  104. package/src/cache/cf/index.ts +13 -3
  105. package/src/cache/document-cache.ts +116 -77
  106. package/src/cache/handle-capture.ts +81 -0
  107. package/src/cache/handle-snapshot.ts +41 -0
  108. package/src/cache/index.ts +1 -15
  109. package/src/cache/memory-segment-store.ts +191 -13
  110. package/src/cache/profile-registry.ts +73 -0
  111. package/src/cache/read-through-swr.ts +134 -0
  112. package/src/cache/segment-codec.ts +256 -0
  113. package/src/cache/taint.ts +153 -0
  114. package/src/cache/types.ts +72 -122
  115. package/src/client.rsc.tsx +3 -1
  116. package/src/client.tsx +105 -179
  117. package/src/component-utils.ts +4 -4
  118. package/src/components/DefaultDocument.tsx +5 -1
  119. package/src/context-var.ts +156 -0
  120. package/src/debug.ts +19 -9
  121. package/src/errors.ts +108 -2
  122. package/src/handle.ts +55 -29
  123. package/src/handles/MetaTags.tsx +73 -20
  124. package/src/handles/breadcrumbs.ts +66 -0
  125. package/src/handles/index.ts +1 -0
  126. package/src/handles/meta.ts +30 -13
  127. package/src/host/cookie-handler.ts +21 -15
  128. package/src/host/errors.ts +8 -8
  129. package/src/host/index.ts +4 -7
  130. package/src/host/pattern-matcher.ts +27 -27
  131. package/src/host/router.ts +61 -39
  132. package/src/host/testing.ts +8 -8
  133. package/src/host/types.ts +15 -7
  134. package/src/host/utils.ts +1 -1
  135. package/src/href-client.ts +119 -29
  136. package/src/index.rsc.ts +155 -19
  137. package/src/index.ts +223 -30
  138. package/src/internal-debug.ts +11 -0
  139. package/src/loader.rsc.ts +26 -157
  140. package/src/loader.ts +27 -10
  141. package/src/network-error-thrower.tsx +3 -1
  142. package/src/outlet-provider.tsx +45 -0
  143. package/src/prerender/param-hash.ts +37 -0
  144. package/src/prerender/store.ts +186 -0
  145. package/src/prerender.ts +524 -0
  146. package/src/reverse.ts +351 -0
  147. package/src/root-error-boundary.tsx +41 -29
  148. package/src/route-content-wrapper.tsx +7 -4
  149. package/src/route-definition/dsl-helpers.ts +982 -0
  150. package/src/route-definition/helper-factories.ts +200 -0
  151. package/src/route-definition/helpers-types.ts +434 -0
  152. package/src/route-definition/index.ts +55 -0
  153. package/src/route-definition/redirect.ts +101 -0
  154. package/src/route-definition/resolve-handler-use.ts +149 -0
  155. package/src/route-definition.ts +1 -1428
  156. package/src/route-map-builder.ts +217 -123
  157. package/src/route-name.ts +53 -0
  158. package/src/route-types.ts +70 -8
  159. package/src/router/content-negotiation.ts +215 -0
  160. package/src/router/debug-manifest.ts +72 -0
  161. package/src/router/error-handling.ts +9 -9
  162. package/src/router/find-match.ts +160 -0
  163. package/src/router/handler-context.ts +435 -86
  164. package/src/router/intercept-resolution.ts +402 -0
  165. package/src/router/lazy-includes.ts +237 -0
  166. package/src/router/loader-resolution.ts +356 -128
  167. package/src/router/logging.ts +251 -0
  168. package/src/router/manifest.ts +154 -35
  169. package/src/router/match-api.ts +555 -0
  170. package/src/router/match-context.ts +5 -3
  171. package/src/router/match-handlers.ts +440 -0
  172. package/src/router/match-middleware/background-revalidation.ts +108 -93
  173. package/src/router/match-middleware/cache-lookup.ts +459 -10
  174. package/src/router/match-middleware/cache-store.ts +98 -26
  175. package/src/router/match-middleware/intercept-resolution.ts +57 -17
  176. package/src/router/match-middleware/segment-resolution.ts +80 -6
  177. package/src/router/match-pipelines.ts +10 -45
  178. package/src/router/match-result.ts +55 -33
  179. package/src/router/metrics.ts +240 -15
  180. package/src/router/middleware-cookies.ts +55 -0
  181. package/src/router/middleware-types.ts +220 -0
  182. package/src/router/middleware.ts +324 -369
  183. package/src/router/navigation-snapshot.ts +182 -0
  184. package/src/router/pattern-matching.ts +211 -43
  185. package/src/router/prerender-match.ts +502 -0
  186. package/src/router/preview-match.ts +98 -0
  187. package/src/router/request-classification.ts +310 -0
  188. package/src/router/revalidation.ts +137 -38
  189. package/src/router/route-snapshot.ts +245 -0
  190. package/src/router/router-context.ts +41 -21
  191. package/src/router/router-interfaces.ts +484 -0
  192. package/src/router/router-options.ts +618 -0
  193. package/src/router/router-registry.ts +24 -0
  194. package/src/router/segment-resolution/fresh.ts +743 -0
  195. package/src/router/segment-resolution/helpers.ts +268 -0
  196. package/src/router/segment-resolution/loader-cache.ts +199 -0
  197. package/src/router/segment-resolution/revalidation.ts +1373 -0
  198. package/src/router/segment-resolution/static-store.ts +67 -0
  199. package/src/router/segment-resolution.ts +21 -0
  200. package/src/router/segment-wrappers.ts +291 -0
  201. package/src/router/telemetry-otel.ts +299 -0
  202. package/src/router/telemetry.ts +300 -0
  203. package/src/router/timeout.ts +148 -0
  204. package/src/router/trie-matching.ts +239 -0
  205. package/src/router/types.ts +78 -3
  206. package/src/router.ts +740 -4252
  207. package/src/rsc/handler-context.ts +45 -0
  208. package/src/rsc/handler.ts +907 -797
  209. package/src/rsc/helpers.ts +140 -6
  210. package/src/rsc/index.ts +0 -20
  211. package/src/rsc/loader-fetch.ts +229 -0
  212. package/src/rsc/manifest-init.ts +90 -0
  213. package/src/rsc/nonce.ts +14 -0
  214. package/src/rsc/origin-guard.ts +141 -0
  215. package/src/rsc/progressive-enhancement.ts +391 -0
  216. package/src/rsc/response-error.ts +37 -0
  217. package/src/rsc/response-route-handler.ts +347 -0
  218. package/src/rsc/rsc-rendering.ts +246 -0
  219. package/src/rsc/runtime-warnings.ts +42 -0
  220. package/src/rsc/server-action.ts +356 -0
  221. package/src/rsc/ssr-setup.ts +128 -0
  222. package/src/rsc/types.ts +46 -11
  223. package/src/search-params.ts +230 -0
  224. package/src/segment-system.tsx +165 -17
  225. package/src/server/context.ts +315 -58
  226. package/src/server/cookie-store.ts +190 -0
  227. package/src/server/fetchable-loader-store.ts +37 -0
  228. package/src/server/handle-store.ts +113 -15
  229. package/src/server/loader-registry.ts +24 -64
  230. package/src/server/request-context.ts +607 -81
  231. package/src/server.ts +35 -130
  232. package/src/ssr/index.tsx +103 -30
  233. package/src/static-handler.ts +126 -0
  234. package/src/theme/ThemeProvider.tsx +21 -15
  235. package/src/theme/ThemeScript.tsx +5 -5
  236. package/src/theme/constants.ts +5 -2
  237. package/src/theme/index.ts +4 -14
  238. package/src/theme/theme-context.ts +4 -30
  239. package/src/theme/theme-script.ts +21 -18
  240. package/src/types/boundaries.ts +158 -0
  241. package/src/types/cache-types.ts +198 -0
  242. package/src/types/error-types.ts +192 -0
  243. package/src/types/global-namespace.ts +100 -0
  244. package/src/types/handler-context.ts +791 -0
  245. package/src/types/index.ts +88 -0
  246. package/src/types/loader-types.ts +210 -0
  247. package/src/types/route-config.ts +170 -0
  248. package/src/types/route-entry.ts +109 -0
  249. package/src/types/segments.ts +150 -0
  250. package/src/types.ts +1 -1623
  251. package/src/urls/include-helper.ts +197 -0
  252. package/src/urls/index.ts +53 -0
  253. package/src/urls/path-helper-types.ts +346 -0
  254. package/src/urls/path-helper.ts +364 -0
  255. package/src/urls/pattern-types.ts +107 -0
  256. package/src/urls/response-types.ts +116 -0
  257. package/src/urls/type-extraction.ts +372 -0
  258. package/src/urls/urls-function.ts +98 -0
  259. package/src/urls.ts +1 -802
  260. package/src/use-loader.tsx +161 -81
  261. package/src/vite/discovery/bundle-postprocess.ts +181 -0
  262. package/src/vite/discovery/discover-routers.ts +348 -0
  263. package/src/vite/discovery/prerender-collection.ts +439 -0
  264. package/src/vite/discovery/route-types-writer.ts +258 -0
  265. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  266. package/src/vite/discovery/state.ts +117 -0
  267. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  268. package/src/vite/index.ts +15 -1129
  269. package/src/vite/plugin-types.ts +103 -0
  270. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  271. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  272. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  273. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -53
  274. package/src/vite/plugins/expose-id-utils.ts +299 -0
  275. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  276. package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
  277. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  278. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  279. package/src/vite/plugins/expose-ids/types.ts +45 -0
  280. package/src/vite/plugins/expose-internal-ids.ts +786 -0
  281. package/src/vite/plugins/performance-tracks.ts +88 -0
  282. package/src/vite/plugins/refresh-cmd.ts +127 -0
  283. package/src/vite/plugins/use-cache-transform.ts +323 -0
  284. package/src/vite/plugins/version-injector.ts +83 -0
  285. package/src/vite/plugins/version-plugin.ts +266 -0
  286. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  287. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  288. package/src/vite/rango.ts +462 -0
  289. package/src/vite/router-discovery.ts +918 -0
  290. package/src/vite/utils/ast-handler-extract.ts +517 -0
  291. package/src/vite/utils/banner.ts +36 -0
  292. package/src/vite/utils/bundle-analysis.ts +137 -0
  293. package/src/vite/utils/manifest-utils.ts +70 -0
  294. package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
  295. package/src/vite/utils/prerender-utils.ts +207 -0
  296. package/src/vite/utils/shared-utils.ts +170 -0
  297. package/CLAUDE.md +0 -43
  298. package/src/browser/lru-cache.ts +0 -69
  299. package/src/browser/request-controller.ts +0 -164
  300. package/src/cache/memory-store.ts +0 -253
  301. package/src/href-context.ts +0 -33
  302. package/src/href.ts +0 -255
  303. package/src/server/route-manifest-cache.ts +0 -173
  304. package/src/vite/expose-handle-id.ts +0 -209
  305. package/src/vite/expose-loader-id.ts +0 -426
  306. package/src/vite/expose-location-state-id.ts +0 -177
  307. /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 wrapped with Passthrough() (live handler for runtime 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,48 @@ 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 (passthroughRoutes && entry.isPassthrough === true) {
154
+ passthroughRoutes.push(name);
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ // Collect response type routes from manifest entries
161
+ if (responseTypeRoutes) {
162
+ for (const [name, entry] of manifest) {
163
+ if (entry.type === "route" && entry.responseType) {
164
+ responseTypeRoutes[name] = entry.responseType;
165
+ }
166
+ }
167
+ }
168
+
169
+ // Build children from tracked nested includes.
170
+ // Multiple includes can share the same fullPrefix (e.g., include("/", patternsA),
171
+ // include("/", patternsB)). Merge their routes instead of overwriting.
104
172
  const children: Record<string, PrefixTreeNode> = {};
105
173
 
106
174
  for (const include of trackedIncludes) {
@@ -109,12 +177,31 @@ function buildPrefixTreeNode(
109
177
  include.namePrefix,
110
178
  include.patterns as UrlPatterns<any>,
111
179
  routeManifest,
112
- visited
180
+ routeAncestry,
181
+ mountIndex,
182
+ visited,
183
+ routeTrailingSlash,
184
+ prerenderRoutes,
185
+ prerenderDefs,
186
+ passthroughRoutes,
187
+ responseTypeRoutes,
188
+ routeSearchSchemas,
113
189
  );
114
190
 
115
- children[include.fullPrefix] = childNode;
191
+ const existing = children[include.fullPrefix];
192
+ if (existing) {
193
+ existing.routes.push(...childNode.routes);
194
+ Object.assign(existing.children, childNode.children);
195
+ } else {
196
+ children[include.fullPrefix] = childNode;
197
+ }
116
198
  }
117
199
 
200
+ // Remove from visited so sibling branches can reuse the same patterns
201
+ // without false circular-include detection. Only ancestors in the current
202
+ // recursion path should trigger the cycle guard.
203
+ visited.delete(patterns);
204
+
118
205
  return {
119
206
  staticPrefix: extractStaticPrefix(urlPrefix),
120
207
  fullPrefix: urlPrefix,
@@ -125,11 +212,40 @@ function buildPrefixTreeNode(
125
212
  }
126
213
 
127
214
  /**
128
- * Generate manifest from UrlPatterns
215
+ * Walk parent chains of route entries to extract ancestry shortCodes.
216
+ */
217
+ function captureAncestry(
218
+ manifest: Map<string, EntryData>,
219
+ routeAncestry: Record<string, string[]>,
220
+ ): void {
221
+ for (const [routeName, entry] of manifest) {
222
+ if (entry.type === "route") {
223
+ const ancestry: string[] = [];
224
+ let current: EntryData | null = entry;
225
+ while (current) {
226
+ ancestry.unshift(current.shortCode);
227
+ current = current.parent;
228
+ }
229
+ routeAncestry[routeName] = ancestry;
230
+ }
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Internal manifest result including build-pipeline-only fields.
236
+ * Not part of the public API — use generateManifest() for the public surface.
237
+ */
238
+ export interface FullManifest extends GeneratedManifest {
239
+ _routeAncestry: Record<string, string[]>;
240
+ _prerenderDefs?: Record<string, any>;
241
+ }
242
+
243
+ /**
244
+ * Generate manifest from UrlPatterns (public API).
129
245
  *
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()
246
+ * Returns only the public GeneratedManifest fields. Internal build pipeline
247
+ * consumers that need _routeAncestry or _prerenderDefs should use
248
+ * generateManifestFull() instead.
133
249
  *
134
250
  * @example
135
251
  * ```typescript
@@ -145,9 +261,31 @@ function buildPrefixTreeNode(
145
261
  * ```
146
262
  */
147
263
  export function generateManifest<TEnv>(
148
- urlpatterns: UrlPatterns<TEnv, any>
264
+ urlpatterns: UrlPatterns<TEnv, any>,
265
+ mountIndex: number = 0,
149
266
  ): GeneratedManifest {
267
+ const {
268
+ _routeAncestry: _,
269
+ _prerenderDefs: __,
270
+ ...publicManifest
271
+ } = generateManifestFull(urlpatterns, mountIndex);
272
+ return publicManifest;
273
+ }
274
+
275
+ /**
276
+ * Generate manifest with internal build-pipeline fields.
277
+ *
278
+ * Used by the Vite plugin (discover-routers via dynamic import through
279
+ * @rangojs/router/build), manifest-init (direct import), and trie
280
+ * building. Not intended for external use.
281
+ */
282
+ export function generateManifestFull<TEnv>(
283
+ urlpatterns: UrlPatterns<TEnv, any>,
284
+ mountIndex: number = 0,
285
+ options?: { urlPrefix?: string },
286
+ ): FullManifest {
150
287
  const routeManifest: Record<string, string> = {};
288
+ const routeAncestry: Record<string, string[]> = {};
151
289
  const prefixTree: Record<string, PrefixTreeNode> = {};
152
290
 
153
291
  // Run the root patterns handler with tracking enabled
@@ -155,6 +293,7 @@ export function generateManifest<TEnv>(
155
293
  const patternsMap = new Map<string, string>();
156
294
  const patternsByPrefix = new Map<string, Map<string, string>>();
157
295
  const trailingSlashMap = new Map<string, TrailingSlashMode>();
296
+ const searchSchemasMap = new Map<string, Record<string, string>>();
158
297
  const trackedIncludes: TrackedInclude[] = [];
159
298
 
160
299
  RSCRouterContext.run(
@@ -163,22 +302,63 @@ export function generateManifest<TEnv>(
163
302
  patterns: patternsMap,
164
303
  patternsByPrefix,
165
304
  trailingSlash: trailingSlashMap,
305
+ searchSchemas: searchSchemasMap,
166
306
  namespace: "build",
167
307
  parent: null,
168
308
  counters: {},
309
+ mountIndex,
169
310
  trackedIncludes, // Enable include tracking
311
+ // basename sets the initial URL prefix for all path() registrations
312
+ ...(options?.urlPrefix ? { urlPrefix: options.urlPrefix } : {}),
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.isPassthrough === 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,39 @@
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
+ extractUrlsFromRouter,
29
+ extractBasenameFromRouter,
30
+ type UrlsExtractionResult,
31
+ buildCombinedRouteMapForRouterFile,
32
+ detectUnresolvableIncludes,
33
+ detectUnresolvableIncludesForUrlsFile,
34
+ findNestedRouterConflict,
35
+ formatNestedRouterConflictError,
36
+ findRouterFiles,
37
+ writeCombinedRouteTypes,
38
+ } from "./route-types/router-processing.js";
39
+ 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";