@rangojs/router 0.0.0-experimental.002d056c

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 (305) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +899 -0
  3. package/dist/bin/rango.js +1606 -0
  4. package/dist/vite/index.js +5153 -0
  5. package/package.json +177 -0
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +262 -0
  8. package/skills/caching/SKILL.md +253 -0
  9. package/skills/composability/SKILL.md +172 -0
  10. package/skills/debug-manifest/SKILL.md +112 -0
  11. package/skills/document-cache/SKILL.md +182 -0
  12. package/skills/fonts/SKILL.md +167 -0
  13. package/skills/hooks/SKILL.md +704 -0
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +313 -0
  16. package/skills/layout/SKILL.md +310 -0
  17. package/skills/links/SKILL.md +239 -0
  18. package/skills/loader/SKILL.md +596 -0
  19. package/skills/middleware/SKILL.md +339 -0
  20. package/skills/mime-routes/SKILL.md +128 -0
  21. package/skills/parallel/SKILL.md +305 -0
  22. package/skills/prerender/SKILL.md +643 -0
  23. package/skills/rango/SKILL.md +118 -0
  24. package/skills/response-routes/SKILL.md +411 -0
  25. package/skills/route/SKILL.md +385 -0
  26. package/skills/router-setup/SKILL.md +439 -0
  27. package/skills/tailwind/SKILL.md +129 -0
  28. package/skills/theme/SKILL.md +79 -0
  29. package/skills/typesafety/SKILL.md +623 -0
  30. package/skills/use-cache/SKILL.md +324 -0
  31. package/src/__internal.ts +273 -0
  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 +899 -0
  36. package/src/browser/history-state.ts +80 -0
  37. package/src/browser/index.ts +18 -0
  38. package/src/browser/intercept-utils.ts +52 -0
  39. package/src/browser/link-interceptor.ts +141 -0
  40. package/src/browser/logging.ts +55 -0
  41. package/src/browser/merge-segment-loaders.ts +134 -0
  42. package/src/browser/navigation-bridge.ts +638 -0
  43. package/src/browser/navigation-client.ts +261 -0
  44. package/src/browser/navigation-store.ts +806 -0
  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 +582 -0
  48. package/src/browser/prefetch/cache.ts +206 -0
  49. package/src/browser/prefetch/fetch.ts +145 -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 +128 -0
  53. package/src/browser/rango-state.ts +112 -0
  54. package/src/browser/react/Link.tsx +368 -0
  55. package/src/browser/react/NavigationProvider.tsx +413 -0
  56. package/src/browser/react/ScrollRestoration.tsx +94 -0
  57. package/src/browser/react/context.ts +59 -0
  58. package/src/browser/react/filter-segment-order.ts +11 -0
  59. package/src/browser/react/index.ts +52 -0
  60. package/src/browser/react/location-state-shared.ts +162 -0
  61. package/src/browser/react/location-state.ts +107 -0
  62. package/src/browser/react/mount-context.ts +37 -0
  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 +218 -0
  66. package/src/browser/react/use-client-cache.ts +58 -0
  67. package/src/browser/react/use-handle.ts +162 -0
  68. package/src/browser/react/use-href.tsx +40 -0
  69. package/src/browser/react/use-link-status.ts +135 -0
  70. package/src/browser/react/use-mount.ts +31 -0
  71. package/src/browser/react/use-navigation.ts +99 -0
  72. package/src/browser/react/use-params.ts +65 -0
  73. package/src/browser/react/use-pathname.ts +47 -0
  74. package/src/browser/react/use-router.ts +63 -0
  75. package/src/browser/react/use-search-params.ts +56 -0
  76. package/src/browser/react/use-segments.ts +171 -0
  77. package/src/browser/response-adapter.ts +73 -0
  78. package/src/browser/rsc-router.tsx +464 -0
  79. package/src/browser/scroll-restoration.ts +397 -0
  80. package/src/browser/segment-reconciler.ts +216 -0
  81. package/src/browser/segment-structure-assert.ts +83 -0
  82. package/src/browser/server-action-bridge.ts +667 -0
  83. package/src/browser/shallow.ts +40 -0
  84. package/src/browser/types.ts +547 -0
  85. package/src/browser/validate-redirect-origin.ts +29 -0
  86. package/src/build/generate-manifest.ts +438 -0
  87. package/src/build/generate-route-types.ts +36 -0
  88. package/src/build/index.ts +35 -0
  89. package/src/build/route-trie.ts +265 -0
  90. package/src/build/route-types/ast-helpers.ts +25 -0
  91. package/src/build/route-types/ast-route-extraction.ts +98 -0
  92. package/src/build/route-types/codegen.ts +102 -0
  93. package/src/build/route-types/include-resolution.ts +411 -0
  94. package/src/build/route-types/param-extraction.ts +48 -0
  95. package/src/build/route-types/per-module-writer.ts +128 -0
  96. package/src/build/route-types/router-processing.ts +479 -0
  97. package/src/build/route-types/scan-filter.ts +78 -0
  98. package/src/build/runtime-discovery.ts +231 -0
  99. package/src/cache/background-task.ts +34 -0
  100. package/src/cache/cache-key-utils.ts +44 -0
  101. package/src/cache/cache-policy.ts +125 -0
  102. package/src/cache/cache-runtime.ts +338 -0
  103. package/src/cache/cache-scope.ts +382 -0
  104. package/src/cache/cf/cf-cache-store.ts +982 -0
  105. package/src/cache/cf/index.ts +29 -0
  106. package/src/cache/document-cache.ts +369 -0
  107. package/src/cache/handle-capture.ts +81 -0
  108. package/src/cache/handle-snapshot.ts +41 -0
  109. package/src/cache/index.ts +44 -0
  110. package/src/cache/memory-segment-store.ts +328 -0
  111. package/src/cache/profile-registry.ts +73 -0
  112. package/src/cache/read-through-swr.ts +134 -0
  113. package/src/cache/segment-codec.ts +256 -0
  114. package/src/cache/taint.ts +98 -0
  115. package/src/cache/types.ts +342 -0
  116. package/src/client.rsc.tsx +85 -0
  117. package/src/client.tsx +601 -0
  118. package/src/component-utils.ts +76 -0
  119. package/src/components/DefaultDocument.tsx +27 -0
  120. package/src/context-var.ts +86 -0
  121. package/src/debug.ts +243 -0
  122. package/src/default-error-boundary.tsx +88 -0
  123. package/src/deps/browser.ts +8 -0
  124. package/src/deps/html-stream-client.ts +2 -0
  125. package/src/deps/html-stream-server.ts +2 -0
  126. package/src/deps/rsc.ts +10 -0
  127. package/src/deps/ssr.ts +2 -0
  128. package/src/errors.ts +365 -0
  129. package/src/handle.ts +135 -0
  130. package/src/handles/MetaTags.tsx +246 -0
  131. package/src/handles/breadcrumbs.ts +66 -0
  132. package/src/handles/index.ts +7 -0
  133. package/src/handles/meta.ts +264 -0
  134. package/src/host/cookie-handler.ts +165 -0
  135. package/src/host/errors.ts +97 -0
  136. package/src/host/index.ts +53 -0
  137. package/src/host/pattern-matcher.ts +214 -0
  138. package/src/host/router.ts +352 -0
  139. package/src/host/testing.ts +79 -0
  140. package/src/host/types.ts +146 -0
  141. package/src/host/utils.ts +25 -0
  142. package/src/href-client.ts +222 -0
  143. package/src/index.rsc.ts +233 -0
  144. package/src/index.ts +277 -0
  145. package/src/internal-debug.ts +11 -0
  146. package/src/loader.rsc.ts +89 -0
  147. package/src/loader.ts +64 -0
  148. package/src/network-error-thrower.tsx +23 -0
  149. package/src/outlet-context.ts +15 -0
  150. package/src/outlet-provider.tsx +45 -0
  151. package/src/prerender/param-hash.ts +37 -0
  152. package/src/prerender/store.ts +185 -0
  153. package/src/prerender.ts +463 -0
  154. package/src/reverse.ts +330 -0
  155. package/src/root-error-boundary.tsx +289 -0
  156. package/src/route-content-wrapper.tsx +196 -0
  157. package/src/route-definition/dsl-helpers.ts +934 -0
  158. package/src/route-definition/helper-factories.ts +200 -0
  159. package/src/route-definition/helpers-types.ts +430 -0
  160. package/src/route-definition/index.ts +52 -0
  161. package/src/route-definition/redirect.ts +93 -0
  162. package/src/route-definition.ts +1 -0
  163. package/src/route-map-builder.ts +281 -0
  164. package/src/route-name.ts +53 -0
  165. package/src/route-types.ts +259 -0
  166. package/src/router/content-negotiation.ts +116 -0
  167. package/src/router/debug-manifest.ts +72 -0
  168. package/src/router/error-handling.ts +287 -0
  169. package/src/router/find-match.ts +160 -0
  170. package/src/router/handler-context.ts +451 -0
  171. package/src/router/intercept-resolution.ts +397 -0
  172. package/src/router/lazy-includes.ts +236 -0
  173. package/src/router/loader-resolution.ts +420 -0
  174. package/src/router/logging.ts +251 -0
  175. package/src/router/manifest.ts +269 -0
  176. package/src/router/match-api.ts +620 -0
  177. package/src/router/match-context.ts +266 -0
  178. package/src/router/match-handlers.ts +440 -0
  179. package/src/router/match-middleware/background-revalidation.ts +223 -0
  180. package/src/router/match-middleware/cache-lookup.ts +634 -0
  181. package/src/router/match-middleware/cache-store.ts +295 -0
  182. package/src/router/match-middleware/index.ts +81 -0
  183. package/src/router/match-middleware/intercept-resolution.ts +306 -0
  184. package/src/router/match-middleware/segment-resolution.ts +193 -0
  185. package/src/router/match-pipelines.ts +179 -0
  186. package/src/router/match-result.ts +219 -0
  187. package/src/router/metrics.ts +282 -0
  188. package/src/router/middleware-cookies.ts +55 -0
  189. package/src/router/middleware-types.ts +222 -0
  190. package/src/router/middleware.ts +749 -0
  191. package/src/router/pattern-matching.ts +563 -0
  192. package/src/router/prerender-match.ts +402 -0
  193. package/src/router/preview-match.ts +170 -0
  194. package/src/router/revalidation.ts +289 -0
  195. package/src/router/router-context.ts +320 -0
  196. package/src/router/router-interfaces.ts +452 -0
  197. package/src/router/router-options.ts +592 -0
  198. package/src/router/router-registry.ts +24 -0
  199. package/src/router/segment-resolution/fresh.ts +570 -0
  200. package/src/router/segment-resolution/helpers.ts +263 -0
  201. package/src/router/segment-resolution/loader-cache.ts +198 -0
  202. package/src/router/segment-resolution/revalidation.ts +1242 -0
  203. package/src/router/segment-resolution/static-store.ts +67 -0
  204. package/src/router/segment-resolution.ts +21 -0
  205. package/src/router/segment-wrappers.ts +291 -0
  206. package/src/router/telemetry-otel.ts +299 -0
  207. package/src/router/telemetry.ts +300 -0
  208. package/src/router/timeout.ts +148 -0
  209. package/src/router/trie-matching.ts +239 -0
  210. package/src/router/types.ts +170 -0
  211. package/src/router.ts +1006 -0
  212. package/src/rsc/handler-context.ts +45 -0
  213. package/src/rsc/handler.ts +1089 -0
  214. package/src/rsc/helpers.ts +198 -0
  215. package/src/rsc/index.ts +36 -0
  216. package/src/rsc/loader-fetch.ts +209 -0
  217. package/src/rsc/manifest-init.ts +86 -0
  218. package/src/rsc/nonce.ts +32 -0
  219. package/src/rsc/origin-guard.ts +141 -0
  220. package/src/rsc/progressive-enhancement.ts +379 -0
  221. package/src/rsc/response-error.ts +37 -0
  222. package/src/rsc/response-route-handler.ts +347 -0
  223. package/src/rsc/rsc-rendering.ts +237 -0
  224. package/src/rsc/runtime-warnings.ts +42 -0
  225. package/src/rsc/server-action.ts +348 -0
  226. package/src/rsc/ssr-setup.ts +128 -0
  227. package/src/rsc/types.ts +263 -0
  228. package/src/search-params.ts +230 -0
  229. package/src/segment-system.tsx +454 -0
  230. package/src/server/context.ts +591 -0
  231. package/src/server/cookie-store.ts +190 -0
  232. package/src/server/fetchable-loader-store.ts +37 -0
  233. package/src/server/handle-store.ts +308 -0
  234. package/src/server/loader-registry.ts +133 -0
  235. package/src/server/request-context.ts +920 -0
  236. package/src/server/root-layout.tsx +10 -0
  237. package/src/server/tsconfig.json +14 -0
  238. package/src/server.ts +51 -0
  239. package/src/ssr/index.tsx +365 -0
  240. package/src/static-handler.ts +114 -0
  241. package/src/theme/ThemeProvider.tsx +297 -0
  242. package/src/theme/ThemeScript.tsx +61 -0
  243. package/src/theme/constants.ts +62 -0
  244. package/src/theme/index.ts +48 -0
  245. package/src/theme/theme-context.ts +44 -0
  246. package/src/theme/theme-script.ts +155 -0
  247. package/src/theme/types.ts +182 -0
  248. package/src/theme/use-theme.ts +44 -0
  249. package/src/types/boundaries.ts +158 -0
  250. package/src/types/cache-types.ts +198 -0
  251. package/src/types/error-types.ts +192 -0
  252. package/src/types/global-namespace.ts +100 -0
  253. package/src/types/handler-context.ts +687 -0
  254. package/src/types/index.ts +88 -0
  255. package/src/types/loader-types.ts +183 -0
  256. package/src/types/route-config.ts +170 -0
  257. package/src/types/route-entry.ts +109 -0
  258. package/src/types/segments.ts +148 -0
  259. package/src/types.ts +1 -0
  260. package/src/urls/include-helper.ts +197 -0
  261. package/src/urls/index.ts +53 -0
  262. package/src/urls/path-helper-types.ts +339 -0
  263. package/src/urls/path-helper.ts +329 -0
  264. package/src/urls/pattern-types.ts +95 -0
  265. package/src/urls/response-types.ts +106 -0
  266. package/src/urls/type-extraction.ts +372 -0
  267. package/src/urls/urls-function.ts +98 -0
  268. package/src/urls.ts +1 -0
  269. package/src/use-loader.tsx +354 -0
  270. package/src/vite/discovery/bundle-postprocess.ts +184 -0
  271. package/src/vite/discovery/discover-routers.ts +344 -0
  272. package/src/vite/discovery/prerender-collection.ts +385 -0
  273. package/src/vite/discovery/route-types-writer.ts +258 -0
  274. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  275. package/src/vite/discovery/state.ts +108 -0
  276. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  277. package/src/vite/index.ts +16 -0
  278. package/src/vite/plugin-types.ts +48 -0
  279. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  280. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  281. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  282. package/src/vite/plugins/expose-action-id.ts +363 -0
  283. package/src/vite/plugins/expose-id-utils.ts +287 -0
  284. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  285. package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
  286. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  287. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  288. package/src/vite/plugins/expose-ids/types.ts +45 -0
  289. package/src/vite/plugins/expose-internal-ids.ts +569 -0
  290. package/src/vite/plugins/refresh-cmd.ts +65 -0
  291. package/src/vite/plugins/use-cache-transform.ts +323 -0
  292. package/src/vite/plugins/version-injector.ts +83 -0
  293. package/src/vite/plugins/version-plugin.ts +266 -0
  294. package/src/vite/plugins/version.d.ts +12 -0
  295. package/src/vite/plugins/virtual-entries.ts +123 -0
  296. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  297. package/src/vite/rango.ts +445 -0
  298. package/src/vite/router-discovery.ts +777 -0
  299. package/src/vite/utils/ast-handler-extract.ts +517 -0
  300. package/src/vite/utils/banner.ts +36 -0
  301. package/src/vite/utils/bundle-analysis.ts +137 -0
  302. package/src/vite/utils/manifest-utils.ts +70 -0
  303. package/src/vite/utils/package-resolution.ts +121 -0
  304. package/src/vite/utils/prerender-utils.ts +189 -0
  305. package/src/vite/utils/shared-utils.ts +169 -0
@@ -0,0 +1,344 @@
1
+ /**
2
+ * Router Discovery
3
+ *
4
+ * Core discovery logic: imports the user's entry file via the RSC
5
+ * environment's module runner, generates manifests for each discovered
6
+ * router, and builds route tries for O(path_length) matching.
7
+ */
8
+
9
+ import {
10
+ buildCombinedRouteMapForRouterFile,
11
+ formatNestedRouterConflictError,
12
+ findNestedRouterConflict,
13
+ } from "../../build/generate-route-types.js";
14
+ import {
15
+ flattenLeafEntries,
16
+ buildRouteToStaticPrefix,
17
+ } from "../utils/manifest-utils.js";
18
+ import type { DiscoveryState, PrecomputedEntry } from "./state.js";
19
+ import {
20
+ expandPrerenderRoutes,
21
+ renderStaticHandlers,
22
+ } from "./prerender-collection.js";
23
+
24
+ /**
25
+ * Import the user's entry via RSC runner, generate manifests for each
26
+ * discovered router, build route tries, and optionally run prerender
27
+ * expansion and static handler rendering (build mode only).
28
+ *
29
+ * Returns the imported `@rangojs/router/server` module so the caller
30
+ * can access the RouterRegistry and manifest setters.
31
+ */
32
+ export async function discoverRouters(
33
+ state: DiscoveryState,
34
+ rscEnv: any,
35
+ ): Promise<any> {
36
+ if (!state.resolvedEntryPath) return;
37
+
38
+ // Import the entry file via RSC environment.
39
+ // For node preset: this is the router file (createRouter() registers in RouterRegistry).
40
+ // For cloudflare preset: this is the worker entry (which imports the router).
41
+ await rscEnv.runner.import(state.resolvedEntryPath);
42
+
43
+ // Import the router package to access the registry
44
+ const serverMod = await rscEnv.runner.import("@rangojs/router/server");
45
+ let registry: Map<string, any> = serverMod.RouterRegistry;
46
+
47
+ if (!registry || registry.size === 0) {
48
+ // No RSC routers found directly. Check for host routers with lazy handlers
49
+ // that need to be resolved to trigger sub-app createRouter() calls.
50
+ try {
51
+ const hostRegistry: Map<string, any> | undefined =
52
+ serverMod.HostRouterRegistry;
53
+
54
+ if (hostRegistry && hostRegistry.size > 0) {
55
+ console.log(
56
+ `[rsc-router] Found ${hostRegistry.size} host router(s), resolving lazy handlers...`,
57
+ );
58
+
59
+ for (const [, entry] of hostRegistry) {
60
+ for (const route of entry.routes) {
61
+ if (typeof route.handler === "function") {
62
+ try {
63
+ await route.handler();
64
+ } catch {
65
+ // Lazy handler may fail in temp server context, that's OK
66
+ }
67
+ }
68
+ }
69
+ if (entry.fallback && typeof entry.fallback.handler === "function") {
70
+ try {
71
+ await entry.fallback.handler();
72
+ } catch {
73
+ // Fallback handler may fail in temp server context
74
+ }
75
+ }
76
+ }
77
+
78
+ // Re-read RouterRegistry - sub-app createRouter() calls should have populated it
79
+ const freshServerMod = await rscEnv.runner.import(
80
+ "@rangojs/router/server",
81
+ );
82
+ const freshRegistry: Map<string, any> = freshServerMod.RouterRegistry;
83
+
84
+ if (freshRegistry && freshRegistry.size > 0) {
85
+ // Update references so the manifest generation below uses the fresh data
86
+ Object.assign(serverMod, freshServerMod);
87
+ registry = freshRegistry;
88
+ }
89
+ }
90
+ } catch {
91
+ // Host-router discovery is best-effort; skip if unavailable
92
+ }
93
+
94
+ // If still no routers after host router resolution, fail
95
+ if (!registry || registry.size === 0) {
96
+ throw new Error(
97
+ `[rsc-router] No routers found in registry after importing ${state.resolvedEntryPath}`,
98
+ );
99
+ }
100
+ }
101
+
102
+ // Import build utilities for manifest generation
103
+ const buildMod = await rscEnv.runner.import("@rangojs/router/build");
104
+ const generateManifestFull = buildMod.generateManifestFull;
105
+
106
+ const nestedRouterConflict = findNestedRouterConflict(
107
+ [...registry.values()]
108
+ .map((router) => router.__sourceFile)
109
+ .filter(
110
+ (sourceFile): sourceFile is string => typeof sourceFile === "string",
111
+ ),
112
+ );
113
+ if (nestedRouterConflict) {
114
+ throw new Error(formatNestedRouterConflictError(nestedRouterConflict));
115
+ }
116
+
117
+ // Build into local variables first. Only commit to state after the
118
+ // full pass succeeds, so a failed re-discovery preserves the last
119
+ // known-good state instead of leaving it partially wiped.
120
+ const newMergedRouteManifest: Record<string, string> = {};
121
+ const newMergedPrecomputedEntries: PrecomputedEntry[] = [];
122
+ const newPerRouterManifests: typeof state.perRouterManifests = [];
123
+ const newPerRouterManifestDataMap = new Map<string, any>();
124
+ const newPerRouterPrecomputedMap = new Map<string, PrecomputedEntry[]>();
125
+ const newPerRouterTrieMap = new Map<string, any>();
126
+ let mergedRouteAncestry: Record<string, string[]> = {};
127
+ let mergedRouteTrailingSlash: Record<string, string> = {};
128
+
129
+ let routerMountIndex = 0;
130
+ // Collect all manifests for trie building (avoid re-running generateManifest)
131
+ const allManifests: Array<{ id: string; manifest: any }> = [];
132
+
133
+ for (const [id, router] of registry) {
134
+ if (!router.urlpatterns || !generateManifestFull) {
135
+ continue;
136
+ }
137
+
138
+ const manifest = generateManifestFull(router.urlpatterns, routerMountIndex);
139
+ routerMountIndex++;
140
+ allManifests.push({ id, manifest });
141
+ const routeCount = Object.keys(manifest.routeManifest).length;
142
+ const staticRoutes = Object.values(manifest.routeManifest).filter(
143
+ (p: any) => !p.includes(":") && !p.includes("*"),
144
+ ).length;
145
+ const dynamicRoutes = routeCount - staticRoutes;
146
+
147
+ // Merge into the combined manifest
148
+ Object.assign(newMergedRouteManifest, manifest.routeManifest);
149
+
150
+ // Compute factory-only prefixes: dot-prefixed groups in the runtime
151
+ // manifest that the static parser cannot see. These are routes created
152
+ // by factory functions (e.g. createDocsPatterns()) and should always be
153
+ // supplemented on file change since HMR won't re-discover them.
154
+ let factoryOnlyPrefixes: Set<string> | undefined;
155
+ if (router.__sourceFile) {
156
+ const staticParsed = buildCombinedRouteMapForRouterFile(
157
+ router.__sourceFile,
158
+ );
159
+ const staticNames = new Set(Object.keys(staticParsed.routes));
160
+ factoryOnlyPrefixes = new Set<string>();
161
+ for (const name of Object.keys(manifest.routeManifest)) {
162
+ if (staticNames.has(name)) continue;
163
+ const dotIdx = name.indexOf(".");
164
+ if (dotIdx <= 0) continue;
165
+ const prefix = name.substring(0, dotIdx + 1);
166
+ if ([...staticNames].some((n) => n.startsWith(prefix))) continue;
167
+ factoryOnlyPrefixes.add(prefix);
168
+ }
169
+ if (factoryOnlyPrefixes.size === 0) factoryOnlyPrefixes = undefined;
170
+ }
171
+
172
+ newPerRouterManifests.push({
173
+ id,
174
+ routeManifest: manifest.routeManifest,
175
+ routeSearchSchemas: manifest.routeSearchSchemas,
176
+ sourceFile: router.__sourceFile,
177
+ factoryOnlyPrefixes,
178
+ });
179
+
180
+ // Merge ancestry (internal field, used only for trie building)
181
+ if (manifest._routeAncestry) {
182
+ Object.assign(mergedRouteAncestry, manifest._routeAncestry);
183
+ }
184
+ // Merge trailing slash config
185
+ if (manifest.routeTrailingSlash) {
186
+ Object.assign(mergedRouteTrailingSlash, manifest.routeTrailingSlash);
187
+ }
188
+
189
+ // Flatten prefix tree leaf nodes into precomputed entries.
190
+ // Leaf nodes (no children) can have their routes used directly by
191
+ // evaluateLazyEntry() without running the handler at runtime.
192
+ flattenLeafEntries(
193
+ manifest.prefixTree,
194
+ manifest.routeManifest,
195
+ newMergedPrecomputedEntries,
196
+ );
197
+
198
+ // Store per-router manifest and precomputed entries for isolated virtual modules.
199
+ newPerRouterManifestDataMap.set(id, manifest.routeManifest);
200
+ const routerPrecomputed: PrecomputedEntry[] = [];
201
+ flattenLeafEntries(
202
+ manifest.prefixTree,
203
+ manifest.routeManifest,
204
+ routerPrecomputed,
205
+ );
206
+ newPerRouterPrecomputedMap.set(id, routerPrecomputed);
207
+
208
+ console.log(
209
+ `[rsc-router] Router "${id}" -> ${routeCount} routes ` +
210
+ `(${staticRoutes} static, ${dynamicRoutes} dynamic)`,
211
+ );
212
+ }
213
+
214
+ // Warn if multiple routers use auto-generated IDs (router_0, router_1, ...).
215
+ // Auto-IDs are assigned by counter and depend on module evaluation order,
216
+ // which can differ between build time and runtime (especially with dynamic
217
+ // imports in host routers). This causes per-router data to be loaded into
218
+ // the wrong router at runtime.
219
+ if (registry.size > 1) {
220
+ const autoIds = [...registry.keys()].filter((id) =>
221
+ /^router_\d+$/.test(id),
222
+ );
223
+ if (autoIds.length > 1) {
224
+ console.warn(
225
+ `[rsc-router] WARNING: ${autoIds.length} routers use auto-generated IDs (${autoIds.join(", ")}). ` +
226
+ `In multi-router setups, each createRouter() must have an explicit \`id\` option ` +
227
+ `to ensure per-router manifest data is matched correctly at runtime. ` +
228
+ `Example: createRouter({ id: "site", ... })`,
229
+ );
230
+ }
231
+ }
232
+
233
+ // Build route trie from merged manifest + ancestry
234
+ let newMergedRouteTrie: any = null;
235
+ if (Object.keys(newMergedRouteManifest).length > 0) {
236
+ const buildRouteTrie = buildMod.buildRouteTrie;
237
+ if (buildRouteTrie && mergedRouteAncestry) {
238
+ // Build routeToStaticPrefix from saved manifests
239
+ const routeToStaticPrefix: Record<string, string> = {};
240
+ for (const { manifest } of allManifests) {
241
+ // Root-level routes have empty static prefix
242
+ for (const name of Object.keys(manifest.routeManifest)) {
243
+ if (!(name in routeToStaticPrefix)) {
244
+ routeToStaticPrefix[name] = "";
245
+ }
246
+ }
247
+ buildRouteToStaticPrefix(manifest.prefixTree, routeToStaticPrefix);
248
+ }
249
+
250
+ // Collect prerender route names and response type routes from all manifests
251
+ const prerenderRouteNames = new Set<string>();
252
+ const passthroughRouteNames = new Set<string>();
253
+ const mergedResponseTypeRoutes: Record<string, string> = {};
254
+ for (const { manifest } of allManifests) {
255
+ if (manifest.prerenderRoutes) {
256
+ for (const name of manifest.prerenderRoutes) {
257
+ prerenderRouteNames.add(name);
258
+ }
259
+ }
260
+ if (manifest.passthroughRoutes) {
261
+ for (const name of manifest.passthroughRoutes) {
262
+ passthroughRouteNames.add(name);
263
+ }
264
+ }
265
+ if (manifest.responseTypeRoutes) {
266
+ Object.assign(mergedResponseTypeRoutes, manifest.responseTypeRoutes);
267
+ }
268
+ }
269
+
270
+ newMergedRouteTrie = buildRouteTrie(
271
+ newMergedRouteManifest,
272
+ mergedRouteAncestry,
273
+ routeToStaticPrefix,
274
+ Object.keys(mergedRouteTrailingSlash).length > 0
275
+ ? mergedRouteTrailingSlash
276
+ : undefined,
277
+ prerenderRouteNames.size > 0 ? prerenderRouteNames : undefined,
278
+ passthroughRouteNames.size > 0 ? passthroughRouteNames : undefined,
279
+ Object.keys(mergedResponseTypeRoutes).length > 0
280
+ ? mergedResponseTypeRoutes
281
+ : undefined,
282
+ );
283
+
284
+ // Build per-router tries for multi-router isolation.
285
+ for (const { id, manifest } of allManifests) {
286
+ if (
287
+ !manifest._routeAncestry ||
288
+ Object.keys(manifest._routeAncestry).length === 0
289
+ )
290
+ continue;
291
+ const perRouterStaticPrefix: Record<string, string> = {};
292
+ for (const name of Object.keys(manifest.routeManifest)) {
293
+ perRouterStaticPrefix[name] = "";
294
+ }
295
+ buildRouteToStaticPrefix(manifest.prefixTree, perRouterStaticPrefix);
296
+
297
+ const perRouterPrerenderNames = manifest.prerenderRoutes
298
+ ? new Set<string>(manifest.prerenderRoutes)
299
+ : undefined;
300
+ const perRouterPassthroughNames = manifest.passthroughRoutes
301
+ ? new Set<string>(manifest.passthroughRoutes)
302
+ : undefined;
303
+
304
+ const perRouterTrie = buildRouteTrie(
305
+ manifest.routeManifest,
306
+ manifest._routeAncestry,
307
+ perRouterStaticPrefix,
308
+ manifest.routeTrailingSlash &&
309
+ Object.keys(manifest.routeTrailingSlash).length > 0
310
+ ? manifest.routeTrailingSlash
311
+ : undefined,
312
+ perRouterPrerenderNames && perRouterPrerenderNames.size > 0
313
+ ? perRouterPrerenderNames
314
+ : undefined,
315
+ perRouterPassthroughNames && perRouterPassthroughNames.size > 0
316
+ ? perRouterPassthroughNames
317
+ : undefined,
318
+ manifest.responseTypeRoutes &&
319
+ Object.keys(manifest.responseTypeRoutes).length > 0
320
+ ? manifest.responseTypeRoutes
321
+ : undefined,
322
+ );
323
+ newPerRouterTrieMap.set(id, perRouterTrie);
324
+ }
325
+ }
326
+ }
327
+
328
+ // Commit all local state to the shared discovery state atomically.
329
+ // This ensures a failed re-discovery (e.g. from a transient module
330
+ // evaluation error) preserves the last known-good state.
331
+ state.mergedRouteManifest = newMergedRouteManifest;
332
+ state.mergedPrecomputedEntries = newMergedPrecomputedEntries;
333
+ state.perRouterManifests = newPerRouterManifests;
334
+ state.perRouterManifestDataMap = newPerRouterManifestDataMap;
335
+ state.perRouterPrecomputedMap = newPerRouterPrecomputedMap;
336
+ state.perRouterTrieMap = newPerRouterTrieMap;
337
+ state.mergedRouteTrie = newMergedRouteTrie;
338
+
339
+ // Expand prerender routes and render static handlers (build mode only)
340
+ await expandPrerenderRoutes(state, rscEnv, registry, allManifests);
341
+ await renderStaticHandlers(state, rscEnv, registry);
342
+
343
+ return serverMod;
344
+ }