@rangojs/router 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (331) hide show
  1. package/dist/bin/rango.js +39 -15
  2. package/dist/types/browser/event-controller.d.ts +49 -0
  3. package/dist/types/browser/history-state.d.ts +16 -4
  4. package/dist/types/browser/navigation-store.d.ts +9 -0
  5. package/dist/types/browser/navigation-transaction.d.ts +7 -0
  6. package/dist/types/browser/partial-update.d.ts +2 -1
  7. package/dist/types/browser/prefetch/default-strategy.d.ts +16 -20
  8. package/dist/types/browser/rango-state.d.ts +3 -1
  9. package/dist/types/browser/react/NavigationProvider.d.ts +14 -1
  10. package/dist/types/browser/react/context.d.ts +25 -0
  11. package/dist/types/browser/react/location-state-shared.d.ts +84 -10
  12. package/dist/types/browser/react/location-state.d.ts +7 -2
  13. package/dist/types/browser/rsc-router.d.ts +13 -2
  14. package/dist/types/browser/types.d.ts +10 -0
  15. package/dist/types/build/generate-manifest.d.ts +2 -0
  16. package/dist/types/cache/background-task.d.ts +22 -0
  17. package/dist/types/cache/cache-key-utils.d.ts +50 -5
  18. package/dist/types/cache/cache-policy.d.ts +17 -0
  19. package/dist/types/cache/cache-scope.d.ts +5 -6
  20. package/dist/types/cache/cf/cf-cache-store.d.ts +34 -7
  21. package/dist/types/cache/cf/cf-cache-types.d.ts +12 -4
  22. package/dist/types/cache/document-cache.d.ts +8 -5
  23. package/dist/types/cache/handle-snapshot.d.ts +59 -50
  24. package/dist/types/cache/index.d.ts +3 -1
  25. package/dist/types/cache/invalidation-order.d.ts +14 -0
  26. package/dist/types/cache/memory-segment-store.d.ts +33 -1
  27. package/dist/types/cache/shell-snapshot.d.ts +29 -4
  28. package/dist/types/cache/store-scope.d.ts +22 -0
  29. package/dist/types/cache/tag-invalidation.d.ts +7 -6
  30. package/dist/types/cache/types.d.ts +84 -24
  31. package/dist/types/cache/vercel/vercel-cache-store.d.ts +35 -17
  32. package/dist/types/client-urls/navigation.d.ts +7 -1
  33. package/dist/types/client-urls/optimistic-location.d.ts +10 -4
  34. package/dist/types/errors.d.ts +6 -0
  35. package/dist/types/handles/deferred-resolution.d.ts +18 -1
  36. package/dist/types/internal-suspense-audit.d.ts +1 -0
  37. package/dist/types/loader-store.d.ts +4 -8
  38. package/dist/types/outlet-provider.d.ts +2 -1
  39. package/dist/types/prerender/build-shell-capture.d.ts +8 -6
  40. package/dist/types/prerender/cloudflare.d.ts +49 -0
  41. package/dist/types/prerender/create-prerender-trigger.d.ts +121 -0
  42. package/dist/types/prerender/index.d.ts +12 -0
  43. package/dist/types/prerender/memory-prerender-store.d.ts +29 -0
  44. package/dist/types/prerender/on-demand.d.ts +272 -0
  45. package/dist/types/prerender/producer-guard.d.ts +33 -0
  46. package/dist/types/prerender/shell-manifest-key.d.ts +1 -18
  47. package/dist/types/prerender/warm-request.d.ts +65 -0
  48. package/dist/types/prerender/warm.d.ts +65 -0
  49. package/dist/types/prerender/writable-store.d.ts +170 -0
  50. package/dist/types/prerender.d.ts +27 -0
  51. package/dist/types/reverse.d.ts +1 -1
  52. package/dist/types/route-content-wrapper.d.ts +3 -1
  53. package/dist/types/router/error-handling.d.ts +2 -0
  54. package/dist/types/router/handler-context.d.ts +1 -1
  55. package/dist/types/router/match-middleware/cache-lookup.d.ts +15 -1
  56. package/dist/types/router/navigation-snapshot.d.ts +8 -1
  57. package/dist/types/router/pattern-matching.d.ts +2 -0
  58. package/dist/types/router/prefetch-default.d.ts +4 -4
  59. package/dist/types/router/prerender-match.d.ts +23 -1
  60. package/dist/types/router/route-trie-builder.d.ts +8 -1
  61. package/dist/types/router/router-context.d.ts +1 -0
  62. package/dist/types/router/router-interfaces.d.ts +32 -1
  63. package/dist/types/router/router-options.d.ts +39 -5
  64. package/dist/types/router/segment-resolution/fresh.d.ts +7 -0
  65. package/dist/types/router/segment-resolution/loader-cache.d.ts +23 -10
  66. package/dist/types/router/segment-resolution/revalidation.d.ts +5 -1
  67. package/dist/types/router/segment-resolution/streamed-handler-boundary.d.ts +43 -0
  68. package/dist/types/router/trie-matching.d.ts +2 -0
  69. package/dist/types/router-versions.d.ts +39 -0
  70. package/dist/types/rsc/handler-context.d.ts +5 -0
  71. package/dist/types/rsc/shell-build-manifest.d.ts +9 -6
  72. package/dist/types/rsc/shell-capture-constants.d.ts +20 -5
  73. package/dist/types/rsc/shell-capture.d.ts +14 -1
  74. package/dist/types/rsc/shell-serve.d.ts +2 -2
  75. package/dist/types/rsc/ssr-module-loader.d.ts +15 -0
  76. package/dist/types/rsc/types.d.ts +12 -6
  77. package/dist/types/segment-boundary-content.d.ts +17 -0
  78. package/dist/types/segment-loader-promise.d.ts +2 -6
  79. package/dist/types/server/build-version-table.d.ts +53 -0
  80. package/dist/types/server/context.d.ts +12 -4
  81. package/dist/types/server/handle-store.d.ts +89 -46
  82. package/dist/types/server/request-context.d.ts +84 -9
  83. package/dist/types/suspense-audit-react.d.ts +26 -0
  84. package/dist/types/suspense-audit.d.ts +157 -0
  85. package/dist/types/testing/action-form.d.ts +34 -0
  86. package/dist/types/testing/build-versions.d.ts +32 -0
  87. package/dist/types/testing/dom.entry.d.ts +1 -1
  88. package/dist/types/testing/flight.entry.d.ts +3 -1
  89. package/dist/types/testing/index.d.ts +6 -0
  90. package/dist/types/testing/internal/seed-vars.d.ts +7 -2
  91. package/dist/types/testing/internal/server-modules.d.ts +8 -0
  92. package/dist/types/testing/location-state-key.d.ts +5 -1
  93. package/dist/types/testing/render-route.d.ts +77 -6
  94. package/dist/types/testing/serve-shell-request.d.ts +55 -4
  95. package/dist/types/testing/shell-status.d.ts +14 -6
  96. package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +9 -6
  97. package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
  98. package/dist/types/thenable-status.d.ts +6 -0
  99. package/dist/types/types/handler-context.d.ts +15 -5
  100. package/dist/types/types/route-entry.d.ts +5 -0
  101. package/dist/types/urls/path-helper.d.ts +18 -0
  102. package/dist/types/vite/discovery/build-versions.d.ts +393 -0
  103. package/dist/types/vite/discovery/installed-packages.d.ts +26 -0
  104. package/dist/types/vite/discovery/router-versions-phase.d.ts +52 -0
  105. package/dist/types/vite/discovery/shell-prerender-phase.d.ts +7 -17
  106. package/dist/types/vite/discovery/state.d.ts +25 -0
  107. package/dist/types/vite/encryption-key.d.ts +18 -0
  108. package/dist/types/vite/inject-client-debug.d.ts +6 -25
  109. package/dist/types/vite/plugin-types.d.ts +27 -0
  110. package/dist/types/vite/plugins/{version-injector.d.ts → entry-bootstrap-injector.d.ts} +4 -6
  111. package/dist/types/vite/plugins/expose-ids/export-analysis.d.ts +1 -0
  112. package/dist/types/vite/plugins/expose-ids/router-transform.d.ts +14 -0
  113. package/dist/types/vite/plugins/stable-rsc-output.d.ts +51 -0
  114. package/dist/types/vite/plugins/version-plugin.d.ts +9 -11
  115. package/dist/types/vite/plugins/virtual-entries.d.ts +18 -1
  116. package/dist/types/vite/router-discovery.d.ts +6 -0
  117. package/dist/types/vite/utils/bundle-analysis.d.ts +6 -2
  118. package/dist/types/vite/utils/client-chunks.d.ts +2 -0
  119. package/dist/types/vite/utils/compare-strings.d.ts +7 -0
  120. package/dist/types/vite/utils/package-resolution.d.ts +8 -0
  121. package/dist/types/vite/utils/prerender-utils.d.ts +2 -0
  122. package/dist/vite/index.js +1707 -931
  123. package/package.json +12 -3
  124. package/skills/cache-guide/SKILL.md +102 -37
  125. package/skills/caching/SKILL.md +56 -9
  126. package/skills/catalog.json +1 -1
  127. package/skills/client-urls/SKILL.md +16 -0
  128. package/skills/cloudflare/SKILL.md +133 -1
  129. package/skills/comparison/references/framework-comparison.md +17 -13
  130. package/skills/deployment-caching/SKILL.md +33 -12
  131. package/skills/document-cache/SKILL.md +16 -14
  132. package/skills/hooks/SKILL.md +25 -0
  133. package/skills/hooks/handle-and-actions.md +5 -0
  134. package/skills/hooks/navigation.md +38 -0
  135. package/skills/hooks/state.md +213 -11
  136. package/skills/hooks/urls.md +40 -1
  137. package/skills/host-router/SKILL.md +31 -0
  138. package/skills/intercept/SKILL.md +6 -0
  139. package/skills/loader/SKILL.md +25 -5
  140. package/skills/middleware/SKILL.md +2 -0
  141. package/skills/migrate-nextjs/SKILL.md +82 -9
  142. package/skills/ppr/SKILL.md +150 -48
  143. package/skills/prerender/SKILL.md +651 -10
  144. package/skills/rango/SKILL.md +9 -7
  145. package/skills/response-routes/SKILL.md +5 -5
  146. package/skills/route/SKILL.md +29 -0
  147. package/skills/router-setup/SKILL.md +15 -1
  148. package/skills/testing/SKILL.md +32 -27
  149. package/skills/testing/cache-prerender.md +18 -17
  150. package/skills/testing/client-components.md +193 -8
  151. package/skills/testing/handles.md +2 -0
  152. package/skills/testing/loader.md +1 -1
  153. package/skills/testing/middleware.md +1 -1
  154. package/skills/testing/render-handler.md +18 -18
  155. package/skills/testing/serve-shell-request.md +167 -22
  156. package/skills/testing/server-actions.md +17 -17
  157. package/skills/testing/server-tree.md +1 -1
  158. package/skills/typesafety/generated-files-and-cli.md +1 -1
  159. package/skills/use-cache/SKILL.md +25 -10
  160. package/skills/vercel/SKILL.md +5 -3
  161. package/skills/view-transitions/SKILL.md +9 -4
  162. package/src/bin/rango.ts +1 -1
  163. package/src/browser/event-controller.ts +119 -2
  164. package/src/browser/history-state.ts +39 -17
  165. package/src/browser/link-interceptor.ts +2 -2
  166. package/src/browser/navigation-bridge.ts +104 -41
  167. package/src/browser/navigation-store.ts +26 -0
  168. package/src/browser/navigation-transaction.ts +24 -12
  169. package/src/browser/network-error-handler.ts +2 -0
  170. package/src/browser/partial-update.ts +58 -14
  171. package/src/browser/prefetch/default-strategy.ts +22 -42
  172. package/src/browser/rango-state.ts +3 -1
  173. package/src/browser/react/Link.tsx +37 -43
  174. package/src/browser/react/NavigationProvider.tsx +85 -1
  175. package/src/browser/react/context.ts +30 -0
  176. package/src/browser/react/location-state-shared.ts +200 -34
  177. package/src/browser/react/location-state.ts +28 -77
  178. package/src/browser/react/use-action.ts +4 -1
  179. package/src/browser/react/use-handle.ts +3 -0
  180. package/src/browser/react/use-link-status.ts +4 -3
  181. package/src/browser/react/use-navigation.ts +4 -1
  182. package/src/browser/react/use-params.ts +4 -1
  183. package/src/browser/react/use-pathname.ts +4 -1
  184. package/src/browser/react/use-search-params.ts +5 -3
  185. package/src/browser/react/use-segments.ts +36 -54
  186. package/src/browser/rsc-router.tsx +93 -31
  187. package/src/browser/segment-reconciler.ts +3 -3
  188. package/src/browser/server-action-bridge.ts +43 -7
  189. package/src/browser/types.ts +11 -0
  190. package/src/build/generate-manifest.ts +17 -0
  191. package/src/build/merge-full-manifests.ts +5 -0
  192. package/src/build/route-trie.ts +1 -0
  193. package/src/cache/background-task.ts +61 -0
  194. package/src/cache/cache-key-utils.ts +91 -12
  195. package/src/cache/cache-policy.ts +11 -2
  196. package/src/cache/cache-runtime.ts +23 -23
  197. package/src/cache/cache-scope.ts +48 -36
  198. package/src/cache/cf/cf-cache-store.ts +91 -66
  199. package/src/cache/cf/cf-cache-types.ts +12 -4
  200. package/src/cache/document-cache.ts +35 -14
  201. package/src/cache/handle-snapshot.ts +118 -86
  202. package/src/cache/index.ts +5 -0
  203. package/src/cache/invalidation-order.ts +53 -0
  204. package/src/cache/memory-segment-store.ts +52 -1
  205. package/src/cache/shell-snapshot.ts +52 -5
  206. package/src/cache/store-scope.ts +36 -0
  207. package/src/cache/tag-invalidation.ts +7 -5
  208. package/src/cache/types.ts +90 -24
  209. package/src/cache/vercel/vercel-cache-store.ts +68 -26
  210. package/src/client-urls/client-root.tsx +1 -0
  211. package/src/client-urls/navigation.ts +10 -0
  212. package/src/client-urls/optimistic-location.ts +10 -4
  213. package/src/client.tsx +40 -3
  214. package/src/errors.ts +13 -6
  215. package/src/handles/deferred-resolution.ts +36 -0
  216. package/src/internal-suspense-audit.ts +12 -0
  217. package/src/loader-store.ts +4 -9
  218. package/src/outlet-provider.tsx +25 -1
  219. package/src/prerender/build-shell-capture.ts +11 -9
  220. package/src/prerender/cloudflare.ts +130 -0
  221. package/src/prerender/create-prerender-trigger.ts +780 -0
  222. package/src/prerender/index.ts +39 -0
  223. package/src/prerender/memory-prerender-store.ts +115 -0
  224. package/src/prerender/on-demand.ts +339 -0
  225. package/src/prerender/producer-guard.ts +66 -0
  226. package/src/prerender/shell-manifest-key.ts +12 -17
  227. package/src/prerender/warm-request.ts +154 -0
  228. package/src/prerender/warm.ts +193 -0
  229. package/src/prerender/writable-store.ts +278 -0
  230. package/src/prerender.ts +29 -0
  231. package/src/reverse.ts +1 -1
  232. package/src/route-content-wrapper.tsx +107 -13
  233. package/src/router/error-handling.ts +12 -12
  234. package/src/router/find-match.ts +1 -0
  235. package/src/router/handler-context.ts +2 -0
  236. package/src/router/intercept-resolution.ts +15 -8
  237. package/src/router/loader-resolution.ts +2 -10
  238. package/src/router/manifest.ts +2 -0
  239. package/src/router/match-middleware/background-revalidation.ts +2 -0
  240. package/src/router/match-middleware/cache-lookup.ts +239 -54
  241. package/src/router/navigation-snapshot.ts +25 -6
  242. package/src/router/pattern-matching.ts +14 -0
  243. package/src/router/prefetch-default.ts +4 -4
  244. package/src/router/prerender-match.ts +460 -352
  245. package/src/router/request-classification.ts +15 -12
  246. package/src/router/route-trie-builder.ts +9 -0
  247. package/src/router/router-context.ts +1 -0
  248. package/src/router/router-interfaces.ts +35 -1
  249. package/src/router/router-options.ts +45 -5
  250. package/src/router/segment-resolution/fresh.ts +20 -19
  251. package/src/router/segment-resolution/loader-cache.ts +110 -77
  252. package/src/router/segment-resolution/revalidation.ts +34 -24
  253. package/src/router/segment-resolution/streamed-handler-boundary.ts +133 -0
  254. package/src/router/trie-matching.ts +3 -0
  255. package/src/router-versions.ts +60 -0
  256. package/src/router.ts +107 -1
  257. package/src/rsc/full-payload.ts +6 -2
  258. package/src/rsc/handler-context.ts +5 -0
  259. package/src/rsc/handler.ts +64 -7
  260. package/src/rsc/progressive-enhancement.ts +5 -1
  261. package/src/rsc/response-cache-serve.ts +10 -4
  262. package/src/rsc/rsc-rendering.ts +65 -12
  263. package/src/rsc/shell-build-manifest.ts +17 -12
  264. package/src/rsc/shell-capture-constants.ts +39 -7
  265. package/src/rsc/shell-capture.ts +144 -45
  266. package/src/rsc/shell-serve.ts +12 -6
  267. package/src/rsc/ssr-module-loader.ts +26 -0
  268. package/src/rsc/types.ts +12 -6
  269. package/src/segment-boundary-content.ts +25 -0
  270. package/src/segment-loader-promise.ts +28 -38
  271. package/src/segment-system.tsx +117 -57
  272. package/src/server/build-version-table.ts +130 -0
  273. package/src/server/context.ts +40 -17
  274. package/src/server/cookie-store.ts +5 -0
  275. package/src/server/handle-store.ts +357 -127
  276. package/src/server/request-context.ts +149 -12
  277. package/src/ssr/ssr-root.tsx +32 -13
  278. package/src/suspense-audit-react.tsx +98 -0
  279. package/src/suspense-audit.ts +1030 -0
  280. package/src/testing/action-form.ts +66 -0
  281. package/src/testing/build-versions.ts +64 -0
  282. package/src/testing/dispatch.ts +6 -0
  283. package/src/testing/dom.entry.ts +1 -0
  284. package/src/testing/flight-runtime.d.ts +27 -0
  285. package/src/testing/flight.entry.ts +4 -0
  286. package/src/testing/index.ts +22 -0
  287. package/src/testing/internal/context.ts +2 -1
  288. package/src/testing/internal/seed-vars.ts +14 -2
  289. package/src/testing/internal/server-modules.ts +32 -0
  290. package/src/testing/location-state-key.ts +18 -8
  291. package/src/testing/render-handler.ts +2 -1
  292. package/src/testing/render-route.tsx +293 -46
  293. package/src/testing/serve-shell-request.ts +262 -37
  294. package/src/testing/shell-status.ts +17 -6
  295. package/src/testing/vitest-stubs/plugin-rsc.ts +37 -11
  296. package/src/testing/vitest-stubs/version.ts +4 -1
  297. package/src/thenable-status.ts +10 -0
  298. package/src/types/handler-context.ts +15 -5
  299. package/src/types/route-entry.ts +6 -0
  300. package/src/urls/path-helper.ts +69 -9
  301. package/src/use-loader.tsx +57 -30
  302. package/src/vite/discovery/build-versions.ts +1281 -0
  303. package/src/vite/discovery/bundle-postprocess.ts +4 -0
  304. package/src/vite/discovery/discover-routers.ts +15 -0
  305. package/src/vite/discovery/installed-packages.ts +159 -0
  306. package/src/vite/discovery/prerender-collection.ts +38 -1
  307. package/src/vite/discovery/router-versions-phase.ts +336 -0
  308. package/src/vite/discovery/shell-prerender-phase.ts +33 -43
  309. package/src/vite/discovery/state.ts +36 -1
  310. package/src/vite/discovery/virtual-module-codegen.ts +7 -2
  311. package/src/vite/encryption-key.ts +101 -9
  312. package/src/vite/inject-client-debug.ts +42 -20
  313. package/src/vite/plugin-types.ts +28 -0
  314. package/src/vite/plugins/{version-injector.ts → entry-bootstrap-injector.ts} +7 -22
  315. package/src/vite/plugins/expose-ids/export-analysis.ts +1 -1
  316. package/src/vite/plugins/expose-ids/router-transform.ts +36 -15
  317. package/src/vite/plugins/expose-internal-ids.ts +12 -3
  318. package/src/vite/plugins/stable-rsc-output.ts +147 -0
  319. package/src/vite/plugins/version-plugin.ts +19 -24
  320. package/src/vite/plugins/version.d.ts +17 -2
  321. package/src/vite/plugins/virtual-entries.ts +39 -6
  322. package/src/vite/rango.ts +28 -3
  323. package/src/vite/router-discovery.ts +96 -13
  324. package/src/vite/utils/bundle-analysis.ts +39 -7
  325. package/src/vite/utils/client-chunks.ts +5 -1
  326. package/src/vite/utils/compare-strings.ts +9 -0
  327. package/src/vite/utils/package-resolution.ts +9 -0
  328. package/src/vite/utils/prerender-utils.ts +4 -1
  329. package/src/vite/utils/shared-utils.ts +1 -1
  330. package/dist/types/segment-content-promise.d.ts +0 -13
  331. package/src/segment-content-promise.ts +0 -67
@@ -22,26 +22,26 @@ function createRangoDebugger(namespace) {
22
22
  if (shadow.enabled) return shadow;
23
23
  return void 0;
24
24
  }
25
- async function timed(debug13, label, fn) {
26
- if (!debug13) return await fn();
25
+ async function timed(debug14, label, fn) {
26
+ if (!debug14) return await fn();
27
27
  const start = performance.now();
28
28
  try {
29
29
  return await fn();
30
30
  } finally {
31
- debug13("%s (%sms)", label, (performance.now() - start).toFixed(1));
31
+ debug14("%s (%sms)", label, (performance.now() - start).toFixed(1));
32
32
  }
33
33
  }
34
- function timedSync(debug13, label, fn) {
35
- if (!debug13) return fn();
34
+ function timedSync(debug14, label, fn) {
35
+ if (!debug14) return fn();
36
36
  const start = performance.now();
37
37
  try {
38
38
  return fn();
39
39
  } finally {
40
- debug13("%s (%sms)", label, (performance.now() - start).toFixed(1));
40
+ debug14("%s (%sms)", label, (performance.now() - start).toFixed(1));
41
41
  }
42
42
  }
43
- function createCounter(debug13, label) {
44
- if (!debug13) return void 0;
43
+ function createCounter(debug14, label) {
44
+ if (!debug14) return void 0;
45
45
  let n = 0;
46
46
  let totalMs = 0;
47
47
  let slowestMs = 0;
@@ -75,7 +75,7 @@ function createCounter(debug13, label) {
75
75
  },
76
76
  flush() {
77
77
  if (n === 0) return;
78
- debug13(
78
+ debug14(
79
79
  "%s: %d files, %sms total, slowest %sms %s",
80
80
  label,
81
81
  n,
@@ -119,7 +119,7 @@ __export(client_ref_hashing_exports, {
119
119
  hashRefKey: () => hashRefKey,
120
120
  transformClientRefs: () => transformClientRefs
121
121
  });
122
- import { relative as relative3 } from "node:path";
122
+ import { relative as relative2 } from "node:path";
123
123
  import { createHash as createHash2 } from "node:crypto";
124
124
  function hashRefKey(relativeId) {
125
125
  return createHash2("sha256").update(relativeId).digest("hex").slice(0, 12);
@@ -132,10 +132,10 @@ function computeProductionHash(projectRoot, refKey) {
132
132
  const absPath = decodeURIComponent(
133
133
  refKey.slice(CLIENT_IN_SERVER_PKG_PROXY_PREFIX.length)
134
134
  );
135
- toHash = relative3(projectRoot, absPath).replaceAll("\\", "/");
135
+ toHash = relative2(projectRoot, absPath).replaceAll("\\", "/");
136
136
  } else if (refKey.startsWith(FS_PREFIX)) {
137
137
  const absPath = refKey.slice(FS_PREFIX.length - 1);
138
- toHash = relative3(projectRoot, absPath).replaceAll("\\", "/");
138
+ toHash = relative2(projectRoot, absPath).replaceAll("\\", "/");
139
139
  } else if (refKey.startsWith("/")) {
140
140
  toHash = refKey.slice(1);
141
141
  } else {
@@ -158,7 +158,7 @@ function transformClientRefs(code, projectRoot) {
158
158
  return hasReplacement ? result : null;
159
159
  }
160
160
  function hashClientRefs(projectRoot) {
161
- const counter = createCounter(debug6, "hash-client-refs");
161
+ const counter = createCounter(debug5, "hash-client-refs");
162
162
  return {
163
163
  name: "@rangojs/router:hash-client-refs",
164
164
  enforce: "post",
@@ -180,12 +180,12 @@ function hashClientRefs(projectRoot) {
180
180
  }
181
181
  };
182
182
  }
183
- var debug6, CLIENT_PKG_PROXY_PREFIX, CLIENT_IN_SERVER_PKG_PROXY_PREFIX, FS_PREFIX, REGISTER_CLIENT_REF_RE;
183
+ var debug5, CLIENT_PKG_PROXY_PREFIX, CLIENT_IN_SERVER_PKG_PROXY_PREFIX, FS_PREFIX, REGISTER_CLIENT_REF_RE;
184
184
  var init_client_ref_hashing = __esm({
185
185
  "src/vite/plugins/client-ref-hashing.ts"() {
186
186
  "use strict";
187
187
  init_debug();
188
- debug6 = createRangoDebugger(NS.transform);
188
+ debug5 = createRangoDebugger(NS.transform);
189
189
  CLIENT_PKG_PROXY_PREFIX = "/@id/__x00__virtual:vite-rsc/client-package-proxy/";
190
190
  CLIENT_IN_SERVER_PKG_PROXY_PREFIX = "/@id/__x00__virtual:vite-rsc/client-in-server-package-proxy/";
191
191
  FS_PREFIX = "/@fs/";
@@ -492,12 +492,12 @@ function waitForViewportImages() {
492
492
  return rect.bottom > 0 && rect.right > 0 && rect.top < window.innerHeight && rect.left < window.innerWidth;
493
493
  });
494
494
  if (pending.length === 0) return Promise.resolve();
495
- return new Promise((resolve14) => {
495
+ return new Promise((resolve15) => {
496
496
  const settled = /* @__PURE__ */ new Set();
497
497
  const settle = (img) => {
498
498
  if (settled.has(img)) return;
499
499
  settled.add(img);
500
- if (settled.size >= pending.length) resolve14();
500
+ if (settled.size >= pending.length) resolve15();
501
501
  };
502
502
  for (const img of pending) {
503
503
  img.addEventListener("load", () => settle(img), { once: true });
@@ -507,16 +507,16 @@ function waitForViewportImages() {
507
507
  });
508
508
  }
509
509
  function wait(ms) {
510
- return new Promise((resolve14) => setTimeout(resolve14, ms));
510
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
511
511
  }
512
512
  function waitForIdle(timeout = 200) {
513
513
  if (typeof window !== "undefined" && "requestIdleCallback" in window) {
514
- return new Promise((resolve14) => {
515
- window.requestIdleCallback(() => resolve14(), { timeout });
514
+ return new Promise((resolve15) => {
515
+ window.requestIdleCallback(() => resolve15(), { timeout });
516
516
  });
517
517
  }
518
- return new Promise((resolve14) => {
519
- setTimeout(resolve14, 0);
518
+ return new Promise((resolve15) => {
519
+ setTimeout(resolve15, 0);
520
520
  });
521
521
  }
522
522
  var init_resource_ready = __esm({
@@ -766,7 +766,7 @@ function isSamePage(url) {
766
766
  return false;
767
767
  }
768
768
  }
769
- function buildPrefetchUrl(url, segmentIds, version, routerId) {
769
+ function buildPrefetchUrl(url, segmentIds, version, routerId2) {
770
770
  let targetUrl;
771
771
  try {
772
772
  targetUrl = new URL(url, window.location.origin);
@@ -783,8 +783,8 @@ function buildPrefetchUrl(url, segmentIds, version, routerId) {
783
783
  if (version) {
784
784
  targetUrl.searchParams.set("_rsc_v", version);
785
785
  }
786
- if (routerId) {
787
- targetUrl.searchParams.set("_rsc_rid", routerId);
786
+ if (routerId2) {
787
+ targetUrl.searchParams.set("_rsc_rid", routerId2);
788
788
  }
789
789
  return targetUrl;
790
790
  }
@@ -843,8 +843,8 @@ function executePrefetchFetch(wildcardKey, sourceKey, fetchUrl, forceSourceScope
843
843
  const storageKey = scope === "source" ? sourceKey : wildcardKey;
844
844
  let resolveStreamComplete;
845
845
  let endedCleanly = false;
846
- const streamComplete = new Promise((resolve14) => {
847
- resolveStreamComplete = resolve14;
846
+ const streamComplete = new Promise((resolve15) => {
847
+ resolveStreamComplete = resolve15;
848
848
  });
849
849
  const tracked = teeWithCompletion(
850
850
  response,
@@ -908,9 +908,9 @@ function executePrefetchFetch(wildcardKey, sourceKey, fetchUrl, forceSourceScope
908
908
  function hasPrefetchHit(forceSourceScope, wildcardKey, sourceKey) {
909
909
  return forceSourceScope ? hasPrefetch(sourceKey) : hasPrefetch(wildcardKey) || hasPrefetch(sourceKey);
910
910
  }
911
- function prefetchDirect(url, segmentIds, version, routerId, prefetchKey) {
911
+ function prefetchDirect(url, segmentIds, version, routerId2, prefetchKey) {
912
912
  if (!shouldPrefetch()) return;
913
- const targetUrl = buildPrefetchUrl(url, segmentIds, version, routerId);
913
+ const targetUrl = buildPrefetchUrl(url, segmentIds, version, routerId2);
914
914
  if (!targetUrl) return;
915
915
  const forceSourceScope = prefetchKey === ":source";
916
916
  if (!forceSourceScope && isSamePage(url)) {
@@ -946,12 +946,12 @@ function prefetchDirect(url, segmentIds, version, routerId, prefetchKey) {
946
946
  targetUrl.toString(),
947
947
  forceSourceScope,
948
948
  rangoState,
949
- routerId
949
+ routerId2
950
950
  );
951
951
  }
952
- function prefetchQueued(url, segmentIds, version, routerId, prefetchKey) {
952
+ function prefetchQueued(url, segmentIds, version, routerId2, prefetchKey) {
953
953
  if (!shouldPrefetch()) return "";
954
- const targetUrl = buildPrefetchUrl(url, segmentIds, version, routerId);
954
+ const targetUrl = buildPrefetchUrl(url, segmentIds, version, routerId2);
955
955
  if (!targetUrl) return "";
956
956
  const forceSourceScope = prefetchKey === ":source";
957
957
  if (!forceSourceScope && isSamePage(url)) {
@@ -987,7 +987,7 @@ function prefetchQueued(url, segmentIds, version, routerId, prefetchKey) {
987
987
  fetchUrlStr,
988
988
  forceSourceScope,
989
989
  rangoState,
990
- routerId,
990
+ routerId2,
991
991
  signal
992
992
  ).then(() => {
993
993
  });
@@ -1061,14 +1061,14 @@ function withRuntime(run) {
1061
1061
  }).catch(() => {
1062
1062
  });
1063
1063
  }
1064
- function prefetchDirect2(url, segmentIds, version, routerId, prefetchKey) {
1064
+ function prefetchDirect2(url, segmentIds, version, routerId2, prefetchKey) {
1065
1065
  withRuntime(
1066
- (loaded) => loaded.prefetchDirect(url, segmentIds, version, routerId, prefetchKey)
1066
+ (loaded) => loaded.prefetchDirect(url, segmentIds, version, routerId2, prefetchKey)
1067
1067
  );
1068
1068
  }
1069
- function prefetchQueued2(url, segmentIds, version, routerId, prefetchKey) {
1069
+ function prefetchQueued2(url, segmentIds, version, routerId2, prefetchKey) {
1070
1070
  withRuntime(
1071
- (loaded) => loaded.prefetchQueued(url, segmentIds, version, routerId, prefetchKey)
1071
+ (loaded) => loaded.prefetchQueued(url, segmentIds, version, routerId2, prefetchKey)
1072
1072
  );
1073
1073
  }
1074
1074
  function schedulePrefetchWhenRouterIdle(eventController, callback) {
@@ -1098,8 +1098,8 @@ var init_loader = __esm({
1098
1098
  });
1099
1099
 
1100
1100
  // src/vite/rango.ts
1101
- import { readFileSync as readFileSync10, existsSync as existsSync11 } from "node:fs";
1102
- import { resolve as resolve13 } from "node:path";
1101
+ import { readFileSync as readFileSync12, existsSync as existsSync13 } from "node:fs";
1102
+ import { resolve as resolve14 } from "node:path";
1103
1103
 
1104
1104
  // src/vite/plugins/expose-action-id.ts
1105
1105
  import { getPluginApi } from "@vitejs/plugin-rsc";
@@ -1569,10 +1569,56 @@ function exposeActionId() {
1569
1569
 
1570
1570
  // src/vite/encryption-key.ts
1571
1571
  import { randomBytes } from "node:crypto";
1572
- var cachedKey;
1572
+ var AES_KEY_BYTES = /* @__PURE__ */ new Set([16, 24, 32]);
1573
+ var EXPECTED = "a base64-encoded AES key of 16, 24 or 32 bytes (openssl rand -base64 32)";
1574
+ var resolvedKey;
1575
+ function assertValidEncryptionKey(value, source) {
1576
+ if (typeof value !== "string") {
1577
+ throw new Error(
1578
+ `[rango] ${source} must be a string, received ${typeof value}. Expected ${EXPECTED}.`
1579
+ );
1580
+ }
1581
+ if (value === "") {
1582
+ throw new Error(
1583
+ `[rango] ${source} is an empty string. Unset it to generate a key per build, or set it to ${EXPECTED}.`
1584
+ );
1585
+ }
1586
+ const bytes = /^[A-Za-z0-9+/]+={0,2}$/.test(value) ? Buffer.from(value, "base64") : void 0;
1587
+ if (!bytes || bytes.toString("base64").replace(/=+$/, "") !== value.replace(/=+$/, "")) {
1588
+ throw new Error(
1589
+ `[rango] ${source} is not valid base64. Expected ${EXPECTED}.`
1590
+ );
1591
+ }
1592
+ if (!AES_KEY_BYTES.has(bytes.length)) {
1593
+ throw new Error(
1594
+ `[rango] ${source} decodes to ${bytes.length} bytes. Expected ${EXPECTED}.`
1595
+ );
1596
+ }
1597
+ }
1598
+ function configureEncryptionKey(option) {
1599
+ if (option !== void 0) {
1600
+ assertValidEncryptionKey(option, "rango({ encryptionKey })");
1601
+ resolvedKey = { key: option, stable: true };
1602
+ return;
1603
+ }
1604
+ const fromEnv = process.env.RANGO_ENCRYPTION_KEY;
1605
+ if (fromEnv !== void 0) {
1606
+ assertValidEncryptionKey(fromEnv, "RANGO_ENCRYPTION_KEY");
1607
+ resolvedKey = { key: fromEnv, stable: true };
1608
+ return;
1609
+ }
1610
+ if (resolvedKey && !resolvedKey.stable) return;
1611
+ resolvedKey = { key: randomBytes(32).toString("base64"), stable: false };
1612
+ }
1613
+ function resolved() {
1614
+ if (!resolvedKey) configureEncryptionKey(void 0);
1615
+ return resolvedKey;
1616
+ }
1573
1617
  function buildEncryptionKey() {
1574
- cachedKey ??= process.env.RANGO_ENCRYPTION_KEY ?? randomBytes(32).toString("base64");
1575
- return cachedKey;
1618
+ return resolved().key;
1619
+ }
1620
+ function isEncryptionKeyStable() {
1621
+ return resolved().stable;
1576
1622
  }
1577
1623
  function defineEncryptionKeyExpr() {
1578
1624
  return JSON.stringify(buildEncryptionKey());
@@ -1587,10 +1633,10 @@ import path4 from "node:path";
1587
1633
  function isDirectivePrologueStatement(node) {
1588
1634
  return node?.type === "ExpressionStatement" && typeof node.directive === "string";
1589
1635
  }
1590
- function findImportInsertionPos(code, parseAst7) {
1636
+ function findImportInsertionPos(code, parseAst8) {
1591
1637
  let program;
1592
1638
  try {
1593
- program = parseAst7(code, { lang: "tsx" });
1639
+ program = parseAst8(code, { lang: "tsx" });
1594
1640
  } catch {
1595
1641
  return 0;
1596
1642
  }
@@ -1627,10 +1673,10 @@ function walkNode(node, parent, ancestors, enter) {
1627
1673
  }
1628
1674
  ancestors.pop();
1629
1675
  }
1630
- function findHandlerCalls(code, fnName, parseAst7) {
1676
+ function findHandlerCalls(code, fnName, parseAst8) {
1631
1677
  let program;
1632
1678
  try {
1633
- program = parseAst7(code, { lang: "tsx" });
1679
+ program = parseAst8(code, { lang: "tsx" });
1634
1680
  } catch {
1635
1681
  return [];
1636
1682
  }
@@ -1702,18 +1748,18 @@ function getImportedLocalNamesFromProgram(program, importedName) {
1702
1748
  }
1703
1749
  return localNames;
1704
1750
  }
1705
- function getImportedLocalNames(code, importedName, parseAst7) {
1751
+ function getImportedLocalNames(code, importedName, parseAst8) {
1706
1752
  try {
1707
- const program = parseAst7(code, { lang: "tsx" });
1753
+ const program = parseAst8(code, { lang: "tsx" });
1708
1754
  return getImportedLocalNamesFromProgram(program, importedName);
1709
1755
  } catch {
1710
1756
  return /* @__PURE__ */ new Set();
1711
1757
  }
1712
1758
  }
1713
- function extractImportDeclarations(code, parseAst7) {
1759
+ function extractImportDeclarations(code, parseAst8) {
1714
1760
  let program;
1715
1761
  try {
1716
- program = parseAst7(code, { lang: "tsx" });
1762
+ program = parseAst8(code, { lang: "tsx" });
1717
1763
  } catch {
1718
1764
  return [];
1719
1765
  }
@@ -1770,10 +1816,10 @@ function isSafeVariableDeclaration(node, handlerNames) {
1770
1816
  (d) => isSafeDeclaratorInit(d.init) && !(d.init?.type === "CallExpression" && d.init.callee?.type === "Identifier" && handlerNames.has(d.init.callee.name))
1771
1817
  );
1772
1818
  }
1773
- function extractModuleLevelDeclarations(code, parseAst7, handlerNames) {
1819
+ function extractModuleLevelDeclarations(code, parseAst8, handlerNames) {
1774
1820
  let program;
1775
1821
  try {
1776
- program = parseAst7(code, { lang: "tsx" });
1822
+ program = parseAst8(code, { lang: "tsx" });
1777
1823
  } catch {
1778
1824
  return [];
1779
1825
  }
@@ -1804,17 +1850,17 @@ function extractModuleLevelDeclarations(code, parseAst7, handlerNames) {
1804
1850
  }
1805
1851
  return declarations;
1806
1852
  }
1807
- function transformInlineHandlers(fnName, virtualPrefix, s, code, filePath, virtualRegistry, moduleId, parseAst7) {
1808
- const sites = findHandlerCalls(code, fnName, parseAst7);
1853
+ function transformInlineHandlers(fnName, virtualPrefix, s, code, filePath, virtualRegistry, moduleId, parseAst8) {
1854
+ const sites = findHandlerCalls(code, fnName, parseAst8);
1809
1855
  const inlineSites = sites.filter((site) => site.exportInfo === null);
1810
1856
  if (inlineSites.length === 0) return false;
1811
- const imports = extractImportDeclarations(code, parseAst7);
1812
- const staticNames = getImportedLocalNames(code, "Static", parseAst7);
1813
- const prerenderNames = getImportedLocalNames(code, "Prerender", parseAst7);
1857
+ const imports = extractImportDeclarations(code, parseAst8);
1858
+ const staticNames = getImportedLocalNames(code, "Static", parseAst8);
1859
+ const prerenderNames = getImportedLocalNames(code, "Prerender", parseAst8);
1814
1860
  const handlerNames = /* @__PURE__ */ new Set([...staticNames, ...prerenderNames]);
1815
1861
  const declarations = extractModuleLevelDeclarations(
1816
1862
  code,
1817
- parseAst7,
1863
+ parseAst8,
1818
1864
  handlerNames
1819
1865
  );
1820
1866
  const importStatements = [];
@@ -1837,7 +1883,7 @@ function transformInlineHandlers(fnName, virtualPrefix, s, code, filePath, virtu
1837
1883
  }
1838
1884
  if (importStatements.length > 0) {
1839
1885
  const importBlock = importStatements.join("\n") + "\n";
1840
- const insertionPos = findImportInsertionPos(code, parseAst7);
1886
+ const insertionPos = findImportInsertionPos(code, parseAst8);
1841
1887
  if (insertionPos === 0) {
1842
1888
  s.prepend(importBlock);
1843
1889
  } else {
@@ -2379,6 +2425,58 @@ ${binding.localName}.$$id = "${handlerId}";`;
2379
2425
  // src/vite/plugins/expose-internal-ids.ts
2380
2426
  init_debug();
2381
2427
 
2428
+ // src/vite/utils/compare-strings.ts
2429
+ function compareStrings(a, b) {
2430
+ return a < b ? -1 : a > b ? 1 : 0;
2431
+ }
2432
+
2433
+ // src/vite/discovery/state.ts
2434
+ var VIRTUAL_ROUTES_MANIFEST_ID = "virtual:rsc-router/routes-manifest";
2435
+ var VIRTUAL_LOADER_MANIFEST_ID = "virtual:rsc-router/loader-manifest";
2436
+ function createDiscoveryState(entryPath, opts) {
2437
+ return {
2438
+ resolvedEntryPath: entryPath,
2439
+ projectRoot: "",
2440
+ isBuildMode: false,
2441
+ userResolveAlias: void 0,
2442
+ userRunnerConfig: void 0,
2443
+ userResolvePlugins: [],
2444
+ scanFilter: void 0,
2445
+ cachedRouterFiles: void 0,
2446
+ opts,
2447
+ mergedRouteManifest: null,
2448
+ perRouterManifests: [],
2449
+ perRouterTrieMap: /* @__PURE__ */ new Map(),
2450
+ perRouterPrecomputedMap: /* @__PURE__ */ new Map(),
2451
+ perRouterManifestDataMap: /* @__PURE__ */ new Map(),
2452
+ clientUrlSourceByReferenceId: /* @__PURE__ */ new Map(),
2453
+ clientUrlProjectionMap: /* @__PURE__ */ new Map(),
2454
+ prerenderManifestEntries: null,
2455
+ staticManifestEntries: null,
2456
+ shellCandidates: null,
2457
+ prerenderPayloadValues: null,
2458
+ shellPhaseTempServer: null,
2459
+ serverBuildGraph: null,
2460
+ clientBuildGraph: null,
2461
+ ssrBundle: null,
2462
+ versionModuleFiles: /* @__PURE__ */ new Map(),
2463
+ buildData: [],
2464
+ handlerChunkInfoMap: /* @__PURE__ */ new Map(),
2465
+ staticHandlerChunkInfoMap: /* @__PURE__ */ new Map(),
2466
+ onDemandHandlerIds: /* @__PURE__ */ new Map(),
2467
+ rscEntryFileName: null,
2468
+ resolvedPrerenderModules: void 0,
2469
+ resolvedStaticModules: void 0,
2470
+ discoveryDone: null,
2471
+ devDiscoveryEpoch: opts?.preset === "cloudflare" ? Date.now() : void 0,
2472
+ devServerOrigin: null,
2473
+ devServer: null,
2474
+ selfWrittenGenFiles: /* @__PURE__ */ new Map(),
2475
+ SELF_WRITE_WINDOW_MS: 5e3,
2476
+ lastDiscoveryError: null
2477
+ };
2478
+ }
2479
+
2382
2480
  // src/vite/plugins/expose-ids/router-transform.ts
2383
2481
  import MagicString2 from "magic-string";
2384
2482
  import path3 from "node:path";
@@ -2401,15 +2499,16 @@ function skipLeadingTrivia(code, start, end) {
2401
2499
  }
2402
2500
  return i;
2403
2501
  }
2502
+ function routerId(filePath, callIndex) {
2503
+ return createHash("sha256").update(`${filePath}#${callIndex}`).digest("hex").slice(0, 8);
2504
+ }
2404
2505
  function transformRouter(code, filePath, routerFnNames, absolutePath, warn) {
2405
- const pat = new RegExp(
2406
- `\\b(?:${routerFnNames.map(escapeRegExp).join("|")})\\b`,
2407
- "g"
2408
- );
2506
+ const pat = createCallPattern(routerFnNames);
2409
2507
  let match;
2410
2508
  const s = new MagicString2(code);
2411
2509
  let changed = false;
2412
2510
  const unsupportedSites = [];
2511
+ let callIndex = -1;
2413
2512
  const basename2 = path3.basename(filePath).replace(/\.(tsx?|jsx?)$/, "");
2414
2513
  const routeNamesImport = `./${basename2}.named-routes.gen.js`;
2415
2514
  const routeNamesVar = `__rsc_rn`;
@@ -2421,13 +2520,12 @@ function transformRouter(code, filePath, routerFnNames, absolutePath, warn) {
2421
2520
  const calleeEnd = match.index + match[0].length;
2422
2521
  const parenPos = findCallParenAfterGenerics(code, calleeEnd);
2423
2522
  if (parenPos === -1) continue;
2523
+ callIndex++;
2424
2524
  const closeParen = findMatchingParen(code, parenPos + 1);
2425
2525
  const callArgs = code.slice(parenPos + 1, closeParen);
2426
2526
  if (callArgs.includes(`$$routeNames: ${routeNamesVar}`)) continue;
2427
2527
  const sourceFilePath = absolutePath ?? filePath;
2428
- const lineNumber = code.slice(0, callStart).split("\n").length;
2429
- const hash = createHash("sha256").update(`${filePath}:${lineNumber}`).digest("hex").slice(0, 8);
2430
- const injected = ` $$id: "${hash}", $$sourceFile: "${sourceFilePath}", $$routeNames: ${routeNamesVar},`;
2528
+ const injected = ` $$id: "${routerId(filePath, callIndex)}", $$sourceFile: "${sourceFilePath}", $$routeNames: ${routeNamesVar},`;
2431
2529
  const argsContentStart = skipLeadingTrivia(code, parenPos + 1, closeParen);
2432
2530
  const firstArgChar = code[argsContentStart];
2433
2531
  if (firstArgChar === "{") {
@@ -2437,9 +2535,7 @@ function transformRouter(code, filePath, routerFnNames, absolutePath, warn) {
2437
2535
  changed = true;
2438
2536
  s.appendRight(parenPos + 1, `{${injected} }`);
2439
2537
  } else {
2440
- const lastNl = code.lastIndexOf("\n", callStart - 1);
2441
- const column = callStart - (lastNl + 1) + 1;
2442
- unsupportedSites.push({ line: lineNumber, column });
2538
+ unsupportedSites.push(offsetToLineColumn(code, callStart));
2443
2539
  }
2444
2540
  }
2445
2541
  if (unsupportedSites.length > 0 && warn) {
@@ -2459,11 +2555,13 @@ function transformRouter(code, filePath, routerFnNames, absolutePath, warn) {
2459
2555
  }
2460
2556
  function exposeRouterId() {
2461
2557
  let projectRoot = "";
2558
+ let rootRelativeSourceFile = false;
2462
2559
  const counter = createCounter(debug2, "expose-router-id");
2463
2560
  return {
2464
2561
  name: "@rangojs/router:expose-router-id",
2465
2562
  configResolved(config) {
2466
2563
  projectRoot = config.root;
2564
+ rootRelativeSourceFile = config.command === "build";
2467
2565
  },
2468
2566
  buildEnd() {
2469
2567
  counter?.flush();
@@ -2485,7 +2583,7 @@ function exposeRouterId() {
2485
2583
  code,
2486
2584
  filePath,
2487
2585
  routerFnNames,
2488
- normalizePath(id),
2586
+ rootRelativeSourceFile ? void 0 : normalizePath(id),
2489
2587
  warn
2490
2588
  );
2491
2589
  } finally {
@@ -2497,7 +2595,7 @@ function exposeRouterId() {
2497
2595
 
2498
2596
  // src/vite/plugins/expose-internal-ids.ts
2499
2597
  var debug3 = createRangoDebugger(NS.transform);
2500
- var VIRTUAL_LOADER_MANIFEST = "virtual:rsc-router/loader-manifest";
2598
+ var VIRTUAL_LOADER_MANIFEST = VIRTUAL_LOADER_MANIFEST_ID;
2501
2599
  var RESOLVED_VIRTUAL_LOADER_MANIFEST = "\0" + VIRTUAL_LOADER_MANIFEST;
2502
2600
  var VIRTUAL_HANDLER_PREFIX = "virtual:handler-extract:";
2503
2601
  function createLoaderScanStubPlugin() {
@@ -2568,8 +2666,8 @@ function exposeInternalIds(options) {
2568
2666
  prerenderHandlerModules,
2569
2667
  staticHandlerModules
2570
2668
  },
2571
- configResolved(resolved) {
2572
- config = resolved;
2669
+ configResolved(resolved2) {
2670
+ config = resolved2;
2573
2671
  isBuild = options?.forceBuild || config.command === "build";
2574
2672
  projectRoot = config.root;
2575
2673
  },
@@ -2607,7 +2705,10 @@ setLoaderImports({});
2607
2705
  `;
2608
2706
  }
2609
2707
  const lazyImports = [];
2610
- for (const [hashedId, { filePath, exportName }] of loaderRegistry) {
2708
+ const sortedLoaders = [...loaderRegistry].sort(
2709
+ ([a], [b]) => compareStrings(a, b)
2710
+ );
2711
+ for (const [hashedId, { filePath, exportName }] of sortedLoaders) {
2611
2712
  lazyImports.push(
2612
2713
  ` "${hashedId}": () => import("/${filePath}").then(m => m.${exportName})`
2613
2714
  );
@@ -3103,8 +3204,8 @@ async function transformUseCache(code, id, options) {
3103
3204
  const { hasDirective: hasDirective2, transformWrapExport, transformHoistInlineDirective } = rscTransforms;
3104
3205
  let ast;
3105
3206
  try {
3106
- const { parseAst: parseAst7 } = await import("vite");
3107
- ast = parseAst7(code, { lang: "tsx" });
3207
+ const { parseAst: parseAst8 } = await import("vite");
3208
+ ast = parseAst8(code, { lang: "tsx" });
3108
3209
  } catch {
3109
3210
  return;
3110
3211
  }
@@ -3395,37 +3496,37 @@ function analyzeScopes(program) {
3395
3496
  if (!pattern) return;
3396
3497
  switch (pattern.type) {
3397
3498
  case "Identifier":
3398
- if (pattern.typeAnnotation) walk2(pattern.typeAnnotation, scope, true);
3499
+ if (pattern.typeAnnotation) walk3(pattern.typeAnnotation, scope, true);
3399
3500
  return;
3400
3501
  case "ObjectPattern":
3401
3502
  for (const prop of pattern.properties) {
3402
3503
  if (prop.type === "RestElement") {
3403
3504
  walkPattern(prop.argument, scope, typeOnly);
3404
3505
  } else {
3405
- if (prop.computed) walk2(prop.key, scope, typeOnly);
3506
+ if (prop.computed) walk3(prop.key, scope, typeOnly);
3406
3507
  walkPattern(prop.value, scope, typeOnly);
3407
3508
  }
3408
3509
  }
3409
- if (pattern.typeAnnotation) walk2(pattern.typeAnnotation, scope, true);
3510
+ if (pattern.typeAnnotation) walk3(pattern.typeAnnotation, scope, true);
3410
3511
  return;
3411
3512
  case "ArrayPattern":
3412
3513
  for (const element of pattern.elements) {
3413
3514
  walkPattern(element, scope, typeOnly);
3414
3515
  }
3415
- if (pattern.typeAnnotation) walk2(pattern.typeAnnotation, scope, true);
3516
+ if (pattern.typeAnnotation) walk3(pattern.typeAnnotation, scope, true);
3416
3517
  return;
3417
3518
  case "RestElement":
3418
3519
  walkPattern(pattern.argument, scope, typeOnly);
3419
3520
  return;
3420
3521
  case "AssignmentPattern":
3421
3522
  walkPattern(pattern.left, scope, typeOnly);
3422
- walk2(pattern.right, scope, typeOnly);
3523
+ walk3(pattern.right, scope, typeOnly);
3423
3524
  return;
3424
3525
  case "TSParameterProperty":
3425
3526
  walkPattern(pattern.parameter, scope, typeOnly);
3426
3527
  return;
3427
3528
  default:
3428
- walk2(pattern, scope, typeOnly);
3529
+ walk3(pattern, scope, typeOnly);
3429
3530
  }
3430
3531
  };
3431
3532
  const hoistDeclarations = (statements, scope) => {
@@ -3459,44 +3560,44 @@ function analyzeScopes(program) {
3459
3560
  }
3460
3561
  }
3461
3562
  };
3462
- const walk2 = (node, scope, typeOnly) => {
3563
+ const walk3 = (node, scope, typeOnly) => {
3463
3564
  if (!node || typeof node !== "object") return;
3464
3565
  if (Array.isArray(node)) {
3465
- for (const item of node) walk2(item, scope, typeOnly);
3566
+ for (const item of node) walk3(item, scope, typeOnly);
3466
3567
  return;
3467
3568
  }
3468
3569
  if (typeof node.type !== "string") return;
3469
3570
  switch (node.type) {
3470
3571
  case "Identifier":
3471
3572
  references.push({ node, scope, typeOnly });
3472
- if (node.typeAnnotation) walk2(node.typeAnnotation, scope, true);
3573
+ if (node.typeAnnotation) walk3(node.typeAnnotation, scope, true);
3473
3574
  return;
3474
3575
  case "ImportDeclaration":
3475
3576
  case "ExportAllDeclaration":
3476
3577
  return;
3477
3578
  case "ExportNamedDeclaration":
3478
- if (node.declaration) walk2(node.declaration, scope, typeOnly);
3579
+ if (node.declaration) walk3(node.declaration, scope, typeOnly);
3479
3580
  else if (!node.source) {
3480
3581
  for (const spec of node.specifiers) {
3481
- walk2(spec.local, scope, typeOnly || node.exportKind === "type");
3582
+ walk3(spec.local, scope, typeOnly || node.exportKind === "type");
3482
3583
  }
3483
3584
  }
3484
3585
  return;
3485
3586
  case "MemberExpression":
3486
- walk2(node.object, scope, typeOnly);
3487
- if (node.computed) walk2(node.property, scope, typeOnly);
3587
+ walk3(node.object, scope, typeOnly);
3588
+ if (node.computed) walk3(node.property, scope, typeOnly);
3488
3589
  return;
3489
3590
  case "Property":
3490
3591
  case "MethodDefinition":
3491
3592
  case "PropertyDefinition":
3492
3593
  case "TSPropertySignature":
3493
3594
  case "TSMethodSignature":
3494
- if (node.computed) walk2(node.key, scope, typeOnly);
3495
- walk2(node.value, scope, typeOnly);
3496
- if (node.typeAnnotation) walk2(node.typeAnnotation, scope, true);
3595
+ if (node.computed) walk3(node.key, scope, typeOnly);
3596
+ walk3(node.value, scope, typeOnly);
3597
+ if (node.typeAnnotation) walk3(node.typeAnnotation, scope, true);
3497
3598
  return;
3498
3599
  case "LabeledStatement":
3499
- walk2(node.body, scope, typeOnly);
3600
+ walk3(node.body, scope, typeOnly);
3500
3601
  return;
3501
3602
  case "BreakStatement":
3502
3603
  case "ContinueStatement":
@@ -3506,25 +3607,25 @@ function analyzeScopes(program) {
3506
3607
  case "JSXClosingElement": {
3507
3608
  const ref = jsxNameReference(node.name);
3508
3609
  if (ref) references.push({ node: ref, scope, typeOnly });
3509
- walk2(node.typeArguments, scope, true);
3510
- walk2(node.attributes, scope, typeOnly);
3610
+ walk3(node.typeArguments, scope, true);
3611
+ walk3(node.attributes, scope, typeOnly);
3511
3612
  return;
3512
3613
  }
3513
3614
  case "TSQualifiedName":
3514
- walk2(node.left, scope, typeOnly);
3615
+ walk3(node.left, scope, typeOnly);
3515
3616
  return;
3516
3617
  case "TSEnumMember":
3517
- walk2(node.initializer, scope, typeOnly);
3618
+ walk3(node.initializer, scope, typeOnly);
3518
3619
  return;
3519
3620
  case "TSTypeAliasDeclaration":
3520
3621
  case "TSInterfaceDeclaration":
3521
- walk2(node.typeParameters, scope, true);
3522
- walk2(node.typeAnnotation ?? node.body, scope, true);
3523
- walk2(node.extends, scope, true);
3622
+ walk3(node.typeParameters, scope, true);
3623
+ walk3(node.typeAnnotation ?? node.body, scope, true);
3624
+ walk3(node.extends, scope, true);
3524
3625
  return;
3525
3626
  case "VariableDeclarator":
3526
3627
  walkPattern(node.id, scope, typeOnly);
3527
- walk2(node.init, scope, typeOnly);
3628
+ walk3(node.init, scope, typeOnly);
3528
3629
  return;
3529
3630
  case "FunctionDeclaration":
3530
3631
  case "FunctionExpression":
@@ -3535,14 +3636,14 @@ function analyzeScopes(program) {
3535
3636
  fnScope.declarations.add(node.id.name);
3536
3637
  }
3537
3638
  for (const param of node.params) declarePattern(param, fnScope);
3538
- walk2(node.typeParameters, fnScope, true);
3639
+ walk3(node.typeParameters, fnScope, true);
3539
3640
  for (const param of node.params) walkPattern(param, fnScope, typeOnly);
3540
- walk2(node.returnType, fnScope, true);
3641
+ walk3(node.returnType, fnScope, true);
3541
3642
  if (node.body?.type === "BlockStatement") {
3542
3643
  hoistDeclarations(node.body.body, fnScope);
3543
- walk2(node.body.body, fnScope, typeOnly);
3644
+ walk3(node.body.body, fnScope, typeOnly);
3544
3645
  } else {
3545
- walk2(node.body, fnScope, typeOnly);
3646
+ walk3(node.body, fnScope, typeOnly);
3546
3647
  }
3547
3648
  return;
3548
3649
  }
@@ -3554,11 +3655,11 @@ function analyzeScopes(program) {
3554
3655
  classScope.declarations.add(node.id.name);
3555
3656
  scopeOf.set(node, classScope);
3556
3657
  }
3557
- walk2(node.superClass, classScope, typeOnly);
3558
- walk2(node.superTypeArguments, classScope, true);
3559
- walk2(node.typeParameters, classScope, true);
3560
- walk2(node.implements, classScope, true);
3561
- walk2(node.body, classScope, typeOnly);
3658
+ walk3(node.superClass, classScope, typeOnly);
3659
+ walk3(node.superTypeArguments, classScope, true);
3660
+ walk3(node.typeParameters, classScope, true);
3661
+ walk3(node.implements, classScope, true);
3662
+ walk3(node.body, classScope, typeOnly);
3562
3663
  return;
3563
3664
  }
3564
3665
  case "BlockStatement":
@@ -3566,7 +3667,7 @@ function analyzeScopes(program) {
3566
3667
  const blockScope = new Scope(scope, false, node);
3567
3668
  scopeOf.set(node, blockScope);
3568
3669
  hoistDeclarations(node.body, blockScope);
3569
- walk2(node.body, blockScope, typeOnly);
3670
+ walk3(node.body, blockScope, typeOnly);
3570
3671
  return;
3571
3672
  }
3572
3673
  case "ForStatement":
@@ -3579,12 +3680,12 @@ function analyzeScopes(program) {
3579
3680
  hoistDeclarations([head], loopScope);
3580
3681
  }
3581
3682
  for (const key of ["init", "left", "right", "test", "update", "body"]) {
3582
- walk2(node[key], loopScope, typeOnly);
3683
+ walk3(node[key], loopScope, typeOnly);
3583
3684
  }
3584
3685
  return;
3585
3686
  }
3586
3687
  case "SwitchStatement": {
3587
- walk2(node.discriminant, scope, typeOnly);
3688
+ walk3(node.discriminant, scope, typeOnly);
3588
3689
  const switchScope = new Scope(scope, false, node);
3589
3690
  scopeOf.set(node, switchScope);
3590
3691
  hoistDeclarations(
@@ -3592,8 +3693,8 @@ function analyzeScopes(program) {
3592
3693
  switchScope
3593
3694
  );
3594
3695
  for (const c of node.cases) {
3595
- walk2(c.test, switchScope, typeOnly);
3596
- walk2(c.consequent, switchScope, typeOnly);
3696
+ walk3(c.test, switchScope, typeOnly);
3697
+ walk3(c.consequent, switchScope, typeOnly);
3597
3698
  }
3598
3699
  return;
3599
3700
  }
@@ -3604,17 +3705,17 @@ function analyzeScopes(program) {
3604
3705
  declarePattern(node.param, catchScope);
3605
3706
  walkPattern(node.param, catchScope, typeOnly);
3606
3707
  }
3607
- walk2(node.body, catchScope, typeOnly);
3708
+ walk3(node.body, catchScope, typeOnly);
3608
3709
  return;
3609
3710
  }
3610
3711
  }
3611
3712
  for (const key of Object.keys(node)) {
3612
3713
  if (key === "type" || key === "start" || key === "end") continue;
3613
- walk2(node[key], scope, typeOnly || TYPE_KEYS.has(key));
3714
+ walk3(node[key], scope, typeOnly || TYPE_KEYS.has(key));
3614
3715
  }
3615
3716
  };
3616
3717
  hoistDeclarations(program.body, moduleScope);
3617
- walk2(program.body, moduleScope, false);
3718
+ walk3(program.body, moduleScope, false);
3618
3719
  return { moduleScope, references, scopeOf };
3619
3720
  }
3620
3721
  function collectImports(program) {
@@ -4044,9 +4145,9 @@ function transitionWhenHoistPlugin() {
4044
4145
  emitted.delete(file);
4045
4146
  const result = await hoistTransitionWhens(code, id, {
4046
4147
  isServerOnlyImport: async (source) => {
4047
- const resolved = await this.resolve(source, id);
4048
- if (!resolved || resolved.external) return false;
4049
- const target = resolved.id.split("?", 1)[0];
4148
+ const resolved2 = await this.resolve(source, id);
4149
+ if (!resolved2 || resolved2.external) return false;
4150
+ const target = resolved2.id.split("?", 1)[0];
4050
4151
  if (target.includes("/node_modules/") || target.startsWith("\0")) {
4051
4152
  return false;
4052
4153
  }
@@ -4207,9 +4308,9 @@ async function resolvePublicSpecifier(source, metadata, installedSpecifier, reso
4207
4308
  source
4208
4309
  )) {
4209
4310
  try {
4210
- const resolved = await resolvePackage(candidate, importer);
4211
- if (!resolved) continue;
4212
- const resolvedId = stripQueryAndHash(resolved);
4311
+ const resolved2 = await resolvePackage(candidate, importer);
4312
+ if (!resolved2) continue;
4313
+ const resolvedId = stripQueryAndHash(resolved2);
4213
4314
  if (normalize(resolvedId) === normalize(source) && belongsToPackage(resolvedId, metadata.root)) {
4214
4315
  return candidate;
4215
4316
  }
@@ -4309,6 +4410,26 @@ function clientRefDedup() {
4309
4410
  };
4310
4411
  }
4311
4412
 
4413
+ // src/vite/plugins/stable-rsc-output.ts
4414
+ import { parseAst as parseAst4 } from "vite";
4415
+
4416
+ // src/vite/discovery/build-versions.ts
4417
+ import { createHash as createHash3 } from "node:crypto";
4418
+ import { isBuiltin } from "node:module";
4419
+ import { posix, relative as relative3 } from "node:path";
4420
+
4421
+ // src/router-versions.ts
4422
+ var DEFAULT_ROUTER_VERSIONS_KEY = "*";
4423
+ function uniformVersions(version) {
4424
+ return { data: version, document: version };
4425
+ }
4426
+ function resolveVersionsFrom(table, routerId2, override) {
4427
+ if (override !== void 0) return uniformVersions(override);
4428
+ if (!table) return void 0;
4429
+ const pair = (routerId2 !== void 0 && Object.hasOwn(table, routerId2) ? table[routerId2] : void 0) ?? table[DEFAULT_ROUTER_VERSIONS_KEY];
4430
+ return pair ? { data: pair[0], document: pair[1] } : void 0;
4431
+ }
4432
+
4312
4433
  // src/vite/plugins/virtual-entries.ts
4313
4434
  var VIRTUAL_ENTRY_BROWSER = `
4314
4435
  import {
@@ -4337,6 +4458,12 @@ async function initializeApp() {
4337
4458
  // is the default; createRouter({ strictMode: false }) ships the opt-out in the
4338
4459
  // payload metadata. StrictMode emits no DOM, so toggling never changes markup.
4339
4460
  const { strictMode, initialPayload } = await initBrowserApp({ rscStream, deps });
4461
+ // Set only on a document a no-JS useActionState POST rendered: React needs
4462
+ // the server's form state to hydrate the hook without resetting it. A state
4463
+ // Flight could not carry rejects: hydrate without it, as before.
4464
+ const formState = await Promise.resolve(initialPayload.formState).catch(
4465
+ () => undefined
4466
+ );
4340
4467
 
4341
4468
  if (import.meta.hot) {
4342
4469
  const { startDevDiscoveryHandshake } = await import(
@@ -4351,7 +4478,8 @@ async function initializeApp() {
4351
4478
  const app = createElement(Rango);
4352
4479
  hydrateRoot(
4353
4480
  document,
4354
- strictMode === false ? app : createElement(StrictMode, null, app)
4481
+ strictMode === false ? app : createElement(StrictMode, null, app),
4482
+ formState ? { formState } : undefined
4355
4483
  );
4356
4484
  }
4357
4485
 
@@ -4457,12 +4585,11 @@ import {
4457
4585
  // consults them at router-module evaluation. Router-first would silently push
4458
4586
  // every clientUrls() app onto the deferred-subscription path and change mount
4459
4587
  // registration order with no error. The custom-entry injector keeps the same
4460
- // guarantee by PREPENDING this list at file top (version-injector.ts).
4588
+ // guarantee by PREPENDING this list at file top (entry-bootstrap-injector.ts).
4461
4589
  ${bootstrapImports}
4462
4590
 
4463
4591
  import { router } from "${routerPath}";
4464
4592
  import { createRSCHandler } from "@rangojs/router/internal/rsc-handler";
4465
- import { VERSION } from "@rangojs/router:version";
4466
4593
 
4467
4594
  // Lazily create the handler on first request so that ESM live bindings
4468
4595
  // have resolved by the time we read \`router\`. During HMR the module may
@@ -4472,7 +4599,6 @@ export default function handler(request, env) {
4472
4599
  if (!_handler) {
4473
4600
  _handler = createRSCHandler({
4474
4601
  router,
4475
- version: VERSION,
4476
4602
  // Forward the router's CSP nonce provider. createRSCHandler reads the
4477
4603
  // provider only from options.nonce; without this, createRouter({ nonce })
4478
4604
  // is silently dropped on the Node preset (the Cloudflare path wires it via
@@ -4572,7 +4698,726 @@ var VIRTUAL_IDS = {
4572
4698
  version: "@rangojs/router:version"
4573
4699
  };
4574
4700
  function getVirtualVersionContent(version) {
4575
- return `export const VERSION = ${JSON.stringify(version)};`;
4701
+ return [
4702
+ `export const VERSION = ${JSON.stringify(version)};`,
4703
+ `export const ROUTER_VERSIONS = undefined;`
4704
+ ].join("\n");
4705
+ }
4706
+ var ROUTER_VERSIONS_PLACEHOLDER = "__RANGO_ROUTER_VERSIONS__";
4707
+ function getVirtualBuildVersionContent() {
4708
+ return [
4709
+ `export const ROUTER_VERSIONS = ${ROUTER_VERSIONS_PLACEHOLDER};`,
4710
+ `export const VERSION = ROUTER_VERSIONS[${JSON.stringify(DEFAULT_ROUTER_VERSIONS_KEY)}][1];`
4711
+ ].join("\n");
4712
+ }
4713
+
4714
+ // src/vite/utils/client-chunks.ts
4715
+ init_debug();
4716
+ init_client_ref_hashing();
4717
+ import { gzipSync } from "node:zlib";
4718
+ var debugChunks = createRangoDebugger(NS.chunks);
4719
+ function isSharedRuntime(meta) {
4720
+ return [meta.id, meta.normalizedId].some(
4721
+ (path6) => path6.includes("/node_modules/") || /\/@rangojs\/router\//.test(path6) || /\/packages\/(rangojs-router|rsc-router)\/(src|dist)\//.test(path6)
4722
+ );
4723
+ }
4724
+ function sanitizeGroup(name) {
4725
+ return name.replace(/[^a-zA-Z0-9_-]+/g, "_").replace(/^_+|_+$/g, "") || "app";
4726
+ }
4727
+ var ROUTE_ROOT_DIRS = /* @__PURE__ */ new Set([
4728
+ "routes",
4729
+ "route",
4730
+ "pages",
4731
+ "page",
4732
+ "app",
4733
+ "features",
4734
+ "feature",
4735
+ "views",
4736
+ "view",
4737
+ "handlers",
4738
+ "urls",
4739
+ "modules",
4740
+ "screens",
4741
+ "sections"
4742
+ ]);
4743
+ var SOURCE_ROOT_DIRS = /* @__PURE__ */ new Set(["app"]);
4744
+ function directoryClientChunks(meta, ctx) {
4745
+ if (isSharedRuntime(meta)) {
4746
+ return void 0;
4747
+ }
4748
+ if (ctx?.fallbackRefs.size && ctx.fallbackRefs.has(hashRefKey(meta.normalizedId))) {
4749
+ debugChunks?.("fallback %s -> app-fallback", meta.normalizedId);
4750
+ return "app-fallback";
4751
+ }
4752
+ const segments = meta.normalizedId.split("/").filter(Boolean);
4753
+ const dirCount = segments.length - 1;
4754
+ const isMarkerAt = (i) => i < dirCount - 1 && ROUTE_ROOT_DIRS.has(segments[i].toLowerCase());
4755
+ for (let i = 0; i < dirCount - 1; i++) {
4756
+ if (!isMarkerAt(i)) continue;
4757
+ const marker = SOURCE_ROOT_DIRS.has(segments[i].toLowerCase()) && isMarkerAt(i + 1) ? i + 1 : i;
4758
+ const group = `app-${sanitizeGroup(segments[marker + 1])}`;
4759
+ debugChunks?.("split %s -> %s", meta.normalizedId, group);
4760
+ return group;
4761
+ }
4762
+ debugChunks?.(
4763
+ "shared %s (no route-root marker; inherits default grouping)",
4764
+ meta.normalizedId
4765
+ );
4766
+ return void 0;
4767
+ }
4768
+ function resolveClientChunks(option, ctx) {
4769
+ if (!option) return void 0;
4770
+ if (option === true) return (meta) => directoryClientChunks(meta, ctx);
4771
+ return option;
4772
+ }
4773
+ var CLIENT_REFERENCES_MODULE_ID = "\0virtual:vite-rsc/client-references";
4774
+ var CLIENT_GROUP_PREFIX = `${CLIENT_REFERENCES_MODULE_ID}/group/`;
4775
+ function clientChunksReport(log = debugChunks) {
4776
+ if (!log) return void 0;
4777
+ return {
4778
+ name: "@rangojs/router:client-chunks-report",
4779
+ apply: "build",
4780
+ applyToEnvironment: (env) => env.name === "client",
4781
+ generateBundle(_options, bundle) {
4782
+ for (const chunk of Object.values(bundle)) {
4783
+ if (chunk.type !== "chunk") continue;
4784
+ const facade = chunk.facadeModuleId;
4785
+ if (!facade?.startsWith(CLIENT_GROUP_PREFIX)) continue;
4786
+ log(
4787
+ "group %s: %d client reference(s), %d B (%d B gzip) -> %s",
4788
+ facade.slice(CLIENT_GROUP_PREFIX.length).replaceAll("\0", "\\0"),
4789
+ this.getModuleInfo(facade)?.importedIds.length ?? 0,
4790
+ Buffer.byteLength(chunk.code),
4791
+ gzipSync(chunk.code).length,
4792
+ chunk.fileName
4793
+ );
4794
+ }
4795
+ }
4796
+ };
4797
+ }
4798
+
4799
+ // src/vite/discovery/build-versions.ts
4800
+ function identifier(name) {
4801
+ return new RegExp(`(?<![\\w$.])${escapeRegExp(name)}(?![\\w$])`, "g");
4802
+ }
4803
+ var ENCRYPTION_RUNTIME_ID = "/utils/encryption-runtime";
4804
+ var ENCRYPTION_RUNTIME_IMPORT = /import\s*\*\s*as\s+([A-Za-z_$][\w$]*)\s+from\s*["'][^"'\n]*\/utils\/encryption-runtime[^"'\n]*["']/;
4805
+ function usesEncryptionRuntime(code) {
4806
+ if (code == null) return true;
4807
+ const local = code.match(ENCRYPTION_RUNTIME_IMPORT)?.[1];
4808
+ if (local === void 0) return true;
4809
+ let occurrences = 0;
4810
+ for (const _ of code.matchAll(identifier(local))) {
4811
+ if (++occurrences > 1) return true;
4812
+ }
4813
+ return false;
4814
+ }
4815
+ function recordModuleEdges(context) {
4816
+ const modules = /* @__PURE__ */ new Map();
4817
+ for (const id of context.getModuleIds()) {
4818
+ const info = context.getModuleInfo(id);
4819
+ const imports = info?.importedIds ?? [];
4820
+ const encryptsBoundArgs = imports.some((imported) => imported.includes(ENCRYPTION_RUNTIME_ID)) && usesEncryptionRuntime(info?.code);
4821
+ modules.set(id, {
4822
+ imports,
4823
+ dynamicImports: info?.dynamicallyImportedIds ?? [],
4824
+ ...encryptsBoundArgs ? { encryptsBoundArgs } : void 0
4825
+ });
4826
+ }
4827
+ return modules;
4828
+ }
4829
+ function recordBundleFiles(bundle) {
4830
+ const chunks = [];
4831
+ const assets = [];
4832
+ for (const file of Object.values(bundle)) {
4833
+ if (file.type === "chunk") {
4834
+ chunks.push({
4835
+ fileName: file.fileName,
4836
+ name: file.name ?? file.fileName,
4837
+ moduleIds: file.moduleIds ?? [],
4838
+ externalImports: [
4839
+ ...file.imports ?? [],
4840
+ ...file.dynamicImports ?? []
4841
+ ].filter((specifier) => !Object.hasOwn(bundle, specifier))
4842
+ });
4843
+ } else {
4844
+ assets.push({
4845
+ fileName: file.fileName,
4846
+ name: file.names?.[0] ?? file.fileName
4847
+ });
4848
+ }
4849
+ }
4850
+ return { chunks, assets };
4851
+ }
4852
+ function recordServerGraph(context, bundle) {
4853
+ return { modules: recordModuleEdges(context), ...recordBundleFiles(bundle) };
4854
+ }
4855
+ function recordClientGraph(context, bundle) {
4856
+ return {
4857
+ modules: recordModuleEdges(context),
4858
+ fileNames: Object.values(bundle).map((file) => file.fileName)
4859
+ };
4860
+ }
4861
+ var SERVER_REFERENCES_MODULE_ID = "\0virtual:vite-rsc/server-references";
4862
+ var LOADER_REGISTRY = "\0" + VIRTUAL_LOADER_MANIFEST_ID;
4863
+ var ROUTES_REGISTRY = "\0" + VIRTUAL_ROUTES_MANIFEST_ID;
4864
+ var VERSION_MODULE = "\0" + VIRTUAL_IDS.version;
4865
+ var ENCRYPTION_KEY_FILE = "__vite_rsc_encryption_key.js";
4866
+ function isRegistryModule(id) {
4867
+ return id === SERVER_REFERENCES_MODULE_ID || id === LOADER_REGISTRY || id === ROUTES_REGISTRY || id.startsWith(ROUTES_REGISTRY + "/") || id === CLIENT_REFERENCES_MODULE_ID || id.startsWith(CLIENT_REFERENCES_MODULE_ID + "/");
4868
+ }
4869
+ function routeManifestModuleId(routerId2) {
4870
+ return `${ROUTES_REGISTRY}/${routerId2}`;
4871
+ }
4872
+ function walk(modules, roots, reached) {
4873
+ const stack = [...roots];
4874
+ while (stack.length > 0) {
4875
+ const id = stack.pop();
4876
+ if (reached.has(id)) continue;
4877
+ reached.add(id);
4878
+ if (isRegistryModule(id)) continue;
4879
+ const edges = modules.get(id);
4880
+ if (!edges) continue;
4881
+ for (const next of edges.imports) {
4882
+ if (!reached.has(next)) stack.push(next);
4883
+ }
4884
+ for (const next of edges.dynamicImports) {
4885
+ if (!reached.has(next)) stack.push(next);
4886
+ }
4887
+ }
4888
+ }
4889
+ function registryTargets(server) {
4890
+ const targets = /* @__PURE__ */ new Set();
4891
+ for (const id of [SERVER_REFERENCES_MODULE_ID, LOADER_REGISTRY]) {
4892
+ for (const target of server.modules.get(id)?.dynamicImports ?? []) {
4893
+ targets.add(target);
4894
+ }
4895
+ }
4896
+ return targets;
4897
+ }
4898
+ function ownersOf(subjects, reachOf) {
4899
+ const owners = /* @__PURE__ */ new Set();
4900
+ for (const [routerId2, reached] of reachOf) {
4901
+ if (subjects.some((id) => reached.has(id))) owners.add(routerId2);
4902
+ }
4903
+ return owners.size > 0 ? owners : void 0;
4904
+ }
4905
+ function extendReach(server, client, targets, reach, roots) {
4906
+ walk(server.modules, roots, reach.server);
4907
+ if (!client || targets.size === 0) return;
4908
+ for (; ; ) {
4909
+ const seeds = [];
4910
+ for (const id of reach.server) {
4911
+ if (!reach.client.has(id) && client.modules.has(id)) seeds.push(id);
4912
+ }
4913
+ if (seeds.length === 0) return;
4914
+ walk(client.modules, seeds, reach.client);
4915
+ const found = [];
4916
+ for (const target of targets) {
4917
+ if (reach.client.has(target) && !reach.server.has(target)) {
4918
+ found.push(target);
4919
+ }
4920
+ }
4921
+ if (found.length === 0) return;
4922
+ walk(server.modules, found, reach.server);
4923
+ }
4924
+ }
4925
+ function findModuleId(modules, path6) {
4926
+ if (modules.has(path6)) return path6;
4927
+ for (const id of modules.keys()) {
4928
+ const query = id.indexOf("?");
4929
+ if (query !== -1 && id.slice(0, query) === path6) return id;
4930
+ }
4931
+ return void 0;
4932
+ }
4933
+ function staticImporters(modules) {
4934
+ const importers = /* @__PURE__ */ new Map();
4935
+ for (const [id, edges] of modules) {
4936
+ if (isRegistryModule(id)) continue;
4937
+ for (const imported of edges.imports) {
4938
+ const list = importers.get(imported);
4939
+ if (list) list.push(id);
4940
+ else importers.set(imported, [id]);
4941
+ }
4942
+ }
4943
+ return importers;
4944
+ }
4945
+ function resolveRouterMembers(server, client, routers) {
4946
+ const targets = registryTargets(server);
4947
+ const importers = staticImporters(server.modules);
4948
+ const reachByRouter = /* @__PURE__ */ new Map();
4949
+ for (const router of routers) {
4950
+ const moduleId = router.moduleId !== void 0 ? findModuleId(server.modules, router.moduleId) : void 0;
4951
+ if (moduleId === void 0) continue;
4952
+ const climbed = /* @__PURE__ */ new Set();
4953
+ const stack = [moduleId];
4954
+ while (stack.length > 0) {
4955
+ const id = stack.pop();
4956
+ if (climbed.has(id)) continue;
4957
+ climbed.add(id);
4958
+ for (const importer of importers.get(id) ?? []) stack.push(importer);
4959
+ }
4960
+ const reach = { server: /* @__PURE__ */ new Set(), client: /* @__PURE__ */ new Set() };
4961
+ extendReach(server, client, targets, reach, climbed);
4962
+ reachByRouter.set(router.id, reach);
4963
+ }
4964
+ const serverReach = new Map(
4965
+ [...reachByRouter].map(([routerId2, reach]) => [routerId2, reach.server])
4966
+ );
4967
+ const unattributed = [...targets].filter(
4968
+ (target) => ownersOf([target], serverReach) === void 0
4969
+ );
4970
+ if (unattributed.length > 0) {
4971
+ for (const reach of reachByRouter.values()) {
4972
+ extendReach(server, client, targets, reach, unattributed);
4973
+ }
4974
+ }
4975
+ const chunkOfModule = /* @__PURE__ */ new Map();
4976
+ for (const chunk of server.chunks) {
4977
+ for (const id of chunk.moduleIds) chunkOfModule.set(id, chunk.fileName);
4978
+ }
4979
+ const members = /* @__PURE__ */ new Map();
4980
+ for (const [routerId2, { server: modules }] of reachByRouter) {
4981
+ const chunkFiles = /* @__PURE__ */ new Set();
4982
+ for (const id of modules) {
4983
+ const fileName = chunkOfModule.get(id);
4984
+ if (fileName !== void 0) chunkFiles.add(fileName);
4985
+ }
4986
+ const manifestChunk = chunkOfModule.get(routeManifestModuleId(routerId2));
4987
+ if (manifestChunk !== void 0) chunkFiles.add(manifestChunk);
4988
+ members.set(routerId2, { modules, chunkFiles });
4989
+ }
4990
+ return members;
4991
+ }
4992
+ function sha256(value) {
4993
+ return createHash3("sha256").update(value).digest("hex");
4994
+ }
4995
+ var baseName = posix.basename;
4996
+ function isAbsolutePath(path6) {
4997
+ return path6.startsWith("/") || /^[A-Za-z]:[\\/]/.test(path6);
4998
+ }
4999
+ var PNPM_PEER_SUFFIX = /(\/\.pnpm\/[^/]+?@[^/_]+)_[^/]+(?=\/)/g;
5000
+ function withoutWorkingDirectory(id) {
5001
+ const kind = /(?:facade|shared):/.exec(id);
5002
+ const virtual = id.indexOf("\0");
5003
+ if (!kind || virtual === -1) return id;
5004
+ const facade = kind.index + kind[0].length;
5005
+ return virtual < facade ? id : id.slice(0, facade) + id.slice(virtual);
5006
+ }
5007
+ function portableModuleId(id, projectRoot) {
5008
+ const path6 = id.startsWith("\0") ? id.slice(1) : id;
5009
+ const nodeModules = path6.indexOf("/node_modules/");
5010
+ if (nodeModules !== -1) {
5011
+ return path6.slice(nodeModules + 1).replace(PNPM_PEER_SUFFIX, "$1");
5012
+ }
5013
+ if (isAbsolutePath(path6)) {
5014
+ return relative3(projectRoot, path6).replaceAll("\\", "/");
5015
+ }
5016
+ return withoutWorkingDirectory(path6).replace(
5017
+ /([?&][^=&]+=)([^&]+)/g,
5018
+ (whole, name, value) => {
5019
+ let decoded;
5020
+ try {
5021
+ decoded = decodeURIComponent(value);
5022
+ } catch {
5023
+ return whole;
5024
+ }
5025
+ return isAbsolutePath(decoded) ? name + portableModuleId(decoded, projectRoot) : whole;
5026
+ }
5027
+ );
5028
+ }
5029
+ var SERVER_CSS_MODULE = /^virtual:vite-rsc\/css\?type=rsc&id=([^&]+)/;
5030
+ function isSourceMap(fileName) {
5031
+ return fileName.endsWith(".map");
5032
+ }
5033
+ var SERVER_REFERENCES_REGION_START = `//#region \\0${SERVER_REFERENCES_MODULE_ID.slice(1)}
5034
+ `;
5035
+ var REGION_END = "//#endregion";
5036
+ function stripServerReferenceMap(source) {
5037
+ const start = source.indexOf(SERVER_REFERENCES_REGION_START);
5038
+ if (start === -1) return source;
5039
+ const bodyStart = start + SERVER_REFERENCES_REGION_START.length;
5040
+ const end = source.indexOf(REGION_END, bodyStart);
5041
+ if (end === -1) return source;
5042
+ return source.slice(0, bodyStart) + source.slice(end);
5043
+ }
5044
+ var REGION_LINE = /^\/\/#region .*$/gm;
5045
+ function stripRegionPaths(source) {
5046
+ return source.replace(REGION_LINE, "//#region");
5047
+ }
5048
+ var FILE_REFERENCE = "\0";
5049
+ var FOREIGN_FILE = "~";
5050
+ function digestBundle(bundle, projectRoot, read, label) {
5051
+ const named = [];
5052
+ for (const chunk of bundle.chunks) {
5053
+ const ids = chunk.moduleIds.map((id) => portableModuleId(id, projectRoot)).sort();
5054
+ const ext = posix.extname(chunk.fileName);
5055
+ named.push({
5056
+ fileName: chunk.fileName,
5057
+ identity: `${chunk.name}~${sha256(ids.join("\n")).slice(0, 8)}${ext}`,
5058
+ hashedName: baseName(chunk.fileName) !== chunk.name + ext,
5059
+ chunk
5060
+ });
5061
+ }
5062
+ for (const asset of bundle.assets) {
5063
+ if (isSourceMap(asset.fileName)) continue;
5064
+ named.push({
5065
+ fileName: asset.fileName,
5066
+ identity: asset.name,
5067
+ hashedName: baseName(asset.fileName) !== baseName(asset.name)
5068
+ });
5069
+ }
5070
+ const fileOfBaseName = /* @__PURE__ */ new Map();
5071
+ for (const file of named) {
5072
+ if (file.hashedName) {
5073
+ fileOfBaseName.set(baseName(file.fileName), file.fileName);
5074
+ }
5075
+ }
5076
+ const reference = fileOfBaseName.size > 0 ? new RegExp(
5077
+ [...fileOfBaseName.keys()].sort((a, b) => b.length - a.length).map(escapeRegExp).join("|"),
5078
+ "g"
5079
+ ) : void 0;
5080
+ const decoder3 = new TextDecoder();
5081
+ return named.map(({ fileName, identity, chunk }) => {
5082
+ const bytes = read(fileName);
5083
+ if (bytes === void 0) {
5084
+ throw new Error(
5085
+ `the ${label} bundle lists ${fileName}, which is not in its output directory`
5086
+ );
5087
+ }
5088
+ if (!chunk) {
5089
+ return {
5090
+ fileName,
5091
+ identity,
5092
+ base: sha256(bytes),
5093
+ references: [],
5094
+ externalImports: []
5095
+ };
5096
+ }
5097
+ const references = [];
5098
+ const text = stripRegionPaths(
5099
+ stripServerReferenceMap(decoder3.decode(bytes))
5100
+ );
5101
+ const blanked = reference ? text.replace(reference, (name) => {
5102
+ references.push(fileOfBaseName.get(name));
5103
+ return FILE_REFERENCE;
5104
+ }) : text;
5105
+ return {
5106
+ fileName,
5107
+ identity,
5108
+ base: sha256(blanked),
5109
+ references,
5110
+ externalImports: chunk.externalImports ?? []
5111
+ };
5112
+ });
5113
+ }
5114
+ function digestAsOwned(file, identityOf, owned) {
5115
+ if (file.references.length === 0) return file.base;
5116
+ return sha256(
5117
+ JSON.stringify([
5118
+ file.base,
5119
+ ...file.references.map(
5120
+ (name) => owned.has(name) ? identityOf.get(name) : FOREIGN_FILE
5121
+ )
5122
+ ])
5123
+ );
5124
+ }
5125
+ var RUNTIME_SCHEME = /^(?:node|cloudflare|workerd|bun):/;
5126
+ var REPLACED_MARKER = /^(?:__CLOUDFLARE_MODULE__.*__CLOUDFLARE_MODULE__|virtual:vite-rsc\/assets-manifest)$/;
5127
+ var PACKAGE_NAME = /^(?:@[\w.~-]+\/)?[A-Za-z0-9~-][\w.~-]*$/;
5128
+ function classifyExternal(specifier) {
5129
+ if (/^[./\\]/.test(specifier) || isAbsolutePath(specifier)) {
5130
+ return { kind: "file" };
5131
+ }
5132
+ if (RUNTIME_SCHEME.test(specifier) || specifier.startsWith("data:") || isBuiltin(specifier) || REPLACED_MARKER.test(specifier)) {
5133
+ return { kind: "provided" };
5134
+ }
5135
+ if (specifier.includes(":")) return { kind: "unknown" };
5136
+ const parts = specifier.split("/");
5137
+ const name = specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
5138
+ return PACKAGE_NAME.test(name) ? { kind: "package", name } : { kind: "unknown" };
5139
+ }
5140
+ function externalInputs(file, context) {
5141
+ const { externals, undetermined } = context;
5142
+ const inputs = [];
5143
+ const add = (name, value) => {
5144
+ if (value === void 0) undetermined.add(name);
5145
+ inputs.push([
5146
+ `external ${name}`,
5147
+ sha256(value ?? `undetermined ${externals.buildId}`)
5148
+ ]);
5149
+ };
5150
+ for (const specifier of file.externalImports) {
5151
+ const external = classifyExternal(specifier);
5152
+ if (external.kind === "package") {
5153
+ add(external.name, externals.describePackage(external.name));
5154
+ } else if (external.kind === "file") {
5155
+ if (isAbsolutePath(specifier)) {
5156
+ const inProject = portableModuleId(specifier, context.projectRoot);
5157
+ add(
5158
+ inProject.startsWith("..") ? specifier : inProject,
5159
+ externals.readFile(specifier)
5160
+ );
5161
+ } else {
5162
+ const fileName = posix.join(
5163
+ posix.dirname(file.fileName),
5164
+ specifier.replaceAll("\\", "/")
5165
+ );
5166
+ add(fileName, context.read(fileName));
5167
+ }
5168
+ } else if (external.kind === "unknown") {
5169
+ add(specifier, void 0);
5170
+ }
5171
+ }
5172
+ return inputs;
5173
+ }
5174
+ var VERSION_HEX_LENGTH = 16;
5175
+ function byName(a, b) {
5176
+ return compareStrings(a[0], b[0]) || compareStrings(a[1], b[1]);
5177
+ }
5178
+ function distinct(inputs) {
5179
+ const sorted = [...inputs].sort(byName);
5180
+ return sorted.filter(
5181
+ (item, index) => index === 0 || byName(item, sorted[index - 1]) !== 0
5182
+ );
5183
+ }
5184
+ function hashInputs(label, inputs) {
5185
+ return sha256(`${label}
5186
+ ${JSON.stringify(inputs)}`).slice(
5187
+ 0,
5188
+ VERSION_HEX_LENGTH
5189
+ );
5190
+ }
5191
+ function serverCssRecords(server, projectRoot, serverResources) {
5192
+ if (serverResources.size === 0) return [];
5193
+ const moduleOf = /* @__PURE__ */ new Map();
5194
+ for (const id of server.modules.keys()) {
5195
+ const key = portableModuleId(id, projectRoot).match(SERVER_CSS_MODULE)?.[1];
5196
+ if (key !== void 0) moduleOf.set(key, id);
5197
+ }
5198
+ return [...serverResources].map(([key, entry]) => ({
5199
+ kind: "server-css",
5200
+ key,
5201
+ digest: sha256(entry),
5202
+ moduleId: moduleOf.get(key)
5203
+ }));
5204
+ }
5205
+ function computeRouterVersions(input) {
5206
+ const { server, projectRoot } = input;
5207
+ const undetermined = /* @__PURE__ */ new Set();
5208
+ const files = digestBundle(server, projectRoot, input.readServerFile, "RSC");
5209
+ const identityOf = new Map(
5210
+ files.map((file) => [file.fileName, file.identity])
5211
+ );
5212
+ const assetFiles = new Set(server.assets.map((asset) => asset.fileName));
5213
+ const externalContext = {
5214
+ projectRoot,
5215
+ externals: input.externals,
5216
+ read: input.readServerFile,
5217
+ undetermined
5218
+ };
5219
+ const externalsOf = new Map(
5220
+ files.map((file) => [file.fileName, externalInputs(file, externalContext)])
5221
+ );
5222
+ const members = resolveRouterMembers(server, input.client, input.routers);
5223
+ const reachOf = new Map(
5224
+ [...members].map(([routerId2, member]) => [routerId2, member.modules])
5225
+ );
5226
+ const owned = [
5227
+ ...input.buildData,
5228
+ ...serverCssRecords(
5229
+ server,
5230
+ projectRoot,
5231
+ input.serverResources ?? /* @__PURE__ */ new Map()
5232
+ )
5233
+ ].map(
5234
+ (record) => ({
5235
+ input: [`${record.kind} ${record.key}`, record.digest],
5236
+ version: record.kind === "prerender" ? "document" : "data",
5237
+ owners: record.routerId !== void 0 ? /* @__PURE__ */ new Set([record.routerId]) : ownersOf(
5238
+ record.moduleId === void 0 ? [] : [record.moduleId],
5239
+ reachOf
5240
+ )
5241
+ })
5242
+ );
5243
+ const encrypting = [...server.modules].filter(([, edges]) => edges.encryptsBoundArgs).map(([id]) => id);
5244
+ const keyFile = input.readServerFile(ENCRYPTION_KEY_FILE);
5245
+ if (keyFile !== void 0) {
5246
+ owned.push({
5247
+ input: ["encryption-key", sha256(keyFile)],
5248
+ version: "data",
5249
+ owners: ownersOf(encrypting, reachOf)
5250
+ });
5251
+ } else if (encrypting.length > 0) {
5252
+ throw new Error(
5253
+ `${encrypting.length} server module(s) encrypt action arguments (${portableModuleId(encrypting[0], projectRoot)}), but ${ENCRYPTION_KEY_FILE} is not in the RSC output directory, so the key cannot be made part of their cache version. The installed @vitejs/plugin-rsc keeps the key somewhere @rangojs/router does not know. Report it with the plugin-rsc version.`
5254
+ );
5255
+ }
5256
+ const keyInput = owned.find((item) => item.input[0] === "encryption-key");
5257
+ const ssrFiles = digestBundle(
5258
+ input.ssr,
5259
+ projectRoot,
5260
+ input.readSsrFile,
5261
+ "SSR"
5262
+ );
5263
+ const ssrIdentityOf = new Map(
5264
+ ssrFiles.map((file) => [file.fileName, file.identity])
5265
+ );
5266
+ const everySsrFile = new Set(ssrFiles.map((file) => file.fileName));
5267
+ const ssrAndClient = {
5268
+ base: input.base,
5269
+ ssr: distinct(
5270
+ ssrFiles.map((file) => [
5271
+ file.identity,
5272
+ digestAsOwned(file, ssrIdentityOf, everySsrFile)
5273
+ ])
5274
+ ),
5275
+ externals: distinct(
5276
+ ssrFiles.flatMap(
5277
+ (file) => externalInputs(file, { ...externalContext, read: input.readSsrFile })
5278
+ )
5279
+ ),
5280
+ client: [...input.client.fileNames].sort()
5281
+ };
5282
+ const documentDigest = sha256(JSON.stringify(ssrAndClient));
5283
+ const table = {};
5284
+ const details = [];
5285
+ const ownedByARouter = /* @__PURE__ */ new Set();
5286
+ const add = (routerId2, chunkFiles) => {
5287
+ const whole = chunkFiles === void 0;
5288
+ const ownedFiles = new Set(
5289
+ chunkFiles ?? files.map((file) => file.fileName)
5290
+ );
5291
+ for (const file of files) {
5292
+ if (!ownedFiles.has(file.fileName)) continue;
5293
+ for (const name of file.references) {
5294
+ if (assetFiles.has(name)) ownedFiles.add(name);
5295
+ }
5296
+ }
5297
+ const inputs = {
5298
+ data: [],
5299
+ document: [["ssr-and-client", documentDigest]]
5300
+ };
5301
+ for (const file of files) {
5302
+ if (!ownedFiles.has(file.fileName)) continue;
5303
+ if (!whole) ownedByARouter.add(file.fileName);
5304
+ inputs.data.push(
5305
+ [`file ${file.identity}`, digestAsOwned(file, identityOf, ownedFiles)],
5306
+ ...externalsOf.get(file.fileName)
5307
+ );
5308
+ }
5309
+ for (const item of owned) {
5310
+ if (whole || item.owners === void 0 || item.owners.has(routerId2)) {
5311
+ inputs[item.version].push(item.input);
5312
+ }
5313
+ }
5314
+ const dataInputs = distinct(inputs.data);
5315
+ const documentInputs = distinct(inputs.document);
5316
+ const data = hashInputs("rango-data-version", dataInputs);
5317
+ const document2 = hashInputs(
5318
+ `rango-document-version
5319
+ ${data}`,
5320
+ documentInputs
5321
+ );
5322
+ table[routerId2] = [data, document2];
5323
+ details.push({
5324
+ routerId: routerId2,
5325
+ data,
5326
+ document: document2,
5327
+ dataInputs,
5328
+ documentInputs,
5329
+ usesEncryptionKey: keyInput !== void 0 && (whole || keyInput.owners === void 0 || keyInput.owners.has(routerId2))
5330
+ });
5331
+ };
5332
+ for (const router of input.routers) {
5333
+ const member = members.get(router.id);
5334
+ if (member) add(router.id, member.chunkFiles);
5335
+ }
5336
+ add(DEFAULT_ROUTER_VERSIONS_KEY);
5337
+ return {
5338
+ table,
5339
+ details,
5340
+ ssrAndClient,
5341
+ unownedFiles: files.filter((file) => !ownedByARouter.has(file.fileName)).map((file) => file.identity).sort(),
5342
+ undetermined: [...undetermined].sort()
5343
+ };
5344
+ }
5345
+ function recordVersionModuleFiles(bundle) {
5346
+ return Object.values(bundle).filter(
5347
+ (file) => file.type === "chunk" && file.moduleIds?.includes(VERSION_MODULE)
5348
+ ).map((file) => file.fileName);
5349
+ }
5350
+ function fillRouterVersions(source, table) {
5351
+ if (!source.includes(ROUTER_VERSIONS_PLACEHOLDER)) return void 0;
5352
+ const json = JSON.stringify(table);
5353
+ return source.replaceAll(ROUTER_VERSIONS_PLACEHOLDER, () => json);
5354
+ }
5355
+
5356
+ // src/vite/plugins/stable-rsc-output.ts
5357
+ function literalProperties(object) {
5358
+ if (object?.type !== "ObjectExpression") return void 0;
5359
+ const properties = [];
5360
+ for (const property of object.properties ?? []) {
5361
+ const key = property?.type === "Property" && !property.computed ? property.key?.value ?? property.key?.name : void 0;
5362
+ if (typeof key !== "string") return void 0;
5363
+ properties.push({
5364
+ key,
5365
+ start: property.start,
5366
+ end: property.end,
5367
+ value: property.value
5368
+ });
5369
+ }
5370
+ return properties;
5371
+ }
5372
+ function defaultExportProperties(code) {
5373
+ let program;
5374
+ try {
5375
+ program = parseAst4(code);
5376
+ } catch {
5377
+ return void 0;
5378
+ }
5379
+ return literalProperties(
5380
+ (program.body ?? []).find(
5381
+ (node) => node?.type === "ExportDefaultDeclaration"
5382
+ )?.declaration
5383
+ );
5384
+ }
5385
+ function sortDefaultExportProperties(code) {
5386
+ const properties = defaultExportProperties(code);
5387
+ if (!properties || properties.length < 2) return void 0;
5388
+ const sorted = [...properties].sort((a, b) => compareStrings(a.key, b.key));
5389
+ if (sorted.every((property, index) => property === properties[index])) {
5390
+ return void 0;
5391
+ }
5392
+ return code.slice(0, properties[0].start) + sorted.map((property) => code.slice(property.start, property.end)).join(",\n") + code.slice(properties[properties.length - 1].end);
5393
+ }
5394
+ var ASSETS_MANIFEST_ID = "virtual:vite-rsc/assets-manifest";
5395
+ var ASSETS_MANIFEST_IMPORT = /import\s+([A-Za-z_$][\w$]*)\s+from\s*["']virtual:vite-rsc\/assets-manifest["']/;
5396
+ var ASSETS_MANIFEST_BINDING = "assetsManifest";
5397
+ var GENERATED_BINDINGS = /* @__PURE__ */ new Set([
5398
+ "__vite_rsc_assets_manifest__",
5399
+ "__vite_rsc_assets_manifest"
5400
+ ]);
5401
+ function identifier2(name) {
5402
+ return new RegExp(`(?<![\\w$.])${escapeRegExp(name)}(?![\\w$])`, "g");
5403
+ }
5404
+ function canonicalAssetsManifestBinding(code) {
5405
+ const local = code.match(ASSETS_MANIFEST_IMPORT)?.[1];
5406
+ if (local === void 0 || !GENERATED_BINDINGS.has(local)) return void 0;
5407
+ if (identifier2(ASSETS_MANIFEST_BINDING).test(code)) return void 0;
5408
+ return code.replace(identifier2(local), ASSETS_MANIFEST_BINDING);
5409
+ }
5410
+ function stableRscOutput() {
5411
+ return {
5412
+ name: "@rangojs/router:stable-rsc-output",
5413
+ apply: "build",
5414
+ // After plugin-rsc's transforms, which add the manifest import.
5415
+ enforce: "post",
5416
+ transform(code, id) {
5417
+ const stable = id === SERVER_REFERENCES_MODULE_ID ? sortDefaultExportProperties(code) : code.includes(ASSETS_MANIFEST_ID) ? canonicalAssetsManifestBinding(code) : void 0;
5418
+ return stable === void 0 ? null : { code: stable, map: null };
5419
+ }
5420
+ };
4576
5421
  }
4577
5422
 
4578
5423
  // src/vite/utils/package-resolution.ts
@@ -4583,7 +5428,7 @@ import { resolve } from "node:path";
4583
5428
  // package.json
4584
5429
  var package_default = {
4585
5430
  name: "@rangojs/router",
4586
- version: "0.20.0",
5431
+ version: "0.22.0",
4587
5432
  description: "Django-inspired RSC router with composable URL patterns",
4588
5433
  keywords: [
4589
5434
  "react",
@@ -4708,6 +5553,16 @@ var package_default = {
4708
5553
  "react-server": "./src/cloudflare/index.ts",
4709
5554
  default: "./src/cloudflare/index.ts"
4710
5555
  },
5556
+ "./prerender": {
5557
+ types: "./dist/types/prerender/index.d.ts",
5558
+ "react-server": "./src/prerender/index.ts",
5559
+ default: "./src/prerender/index.ts"
5560
+ },
5561
+ "./prerender/cloudflare": {
5562
+ types: "./dist/types/prerender/cloudflare.d.ts",
5563
+ "react-server": "./src/prerender/cloudflare.ts",
5564
+ default: "./src/prerender/cloudflare.ts"
5565
+ },
4711
5566
  "./vercel": {
4712
5567
  types: "./dist/types/vercel/index.d.ts",
4713
5568
  "react-server": "./src/vercel/index.ts",
@@ -4806,7 +5661,7 @@ var package_default = {
4806
5661
  jiti: "^2.7.0",
4807
5662
  react: "catalog:",
4808
5663
  "react-dom": "catalog:",
4809
- vitest: "^4.1.9"
5664
+ vitest: "^4.1.11"
4810
5665
  },
4811
5666
  peerDependencies: {
4812
5667
  "@cloudflare/vite-plugin": "^1.42.1",
@@ -4814,7 +5669,6 @@ var package_default = {
4814
5669
  "@playwright/test": "^1.49.1",
4815
5670
  "@testing-library/react": ">=16",
4816
5671
  "@vercel/functions": "^3.0.0",
4817
- "@vitejs/plugin-rsc": "^0.5.35",
4818
5672
  react: ">=19.2.8 <20",
4819
5673
  "react-dom": ">=19.2.8 <20",
4820
5674
  vite: "^8.0.16",
@@ -4894,7 +5748,8 @@ function getVendorAliases() {
4894
5748
  const specs = [
4895
5749
  "@vitejs/plugin-rsc/vendor/react-server-dom/client.browser",
4896
5750
  "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
4897
- "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
5751
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge",
5752
+ "@vitejs/plugin-rsc/vendor/react-server-dom/static.edge"
4898
5753
  ];
4899
5754
  const aliases = {};
4900
5755
  for (const spec of specs) {
@@ -5033,7 +5888,7 @@ declare global {
5033
5888
  }
5034
5889
 
5035
5890
  // src/build/route-types/scan-filter.ts
5036
- import { join as join2, relative as relative2 } from "node:path";
5891
+ import { join as join2, relative as relative4 } from "node:path";
5037
5892
  import { readdirSync } from "node:fs";
5038
5893
  import picomatch from "picomatch";
5039
5894
  var DEFAULT_EXCLUDE_PATTERNS = [
@@ -5053,7 +5908,7 @@ function createScanFilter(root, opts) {
5053
5908
  const includeMatcher = hasInclude ? picomatch(include) : null;
5054
5909
  const excludeMatcher = effectiveExclude.length > 0 ? picomatch(effectiveExclude) : null;
5055
5910
  return (absolutePath) => {
5056
- const rel = relative2(root, absolutePath);
5911
+ const rel = relative4(root, absolutePath);
5057
5912
  if (excludeMatcher && excludeMatcher(rel)) return false;
5058
5913
  if (includeMatcher) return includeMatcher(rel);
5059
5914
  return true;
@@ -5232,7 +6087,7 @@ function extractIncludesWithDiagnostics(code, sourceFileArg) {
5232
6087
  true,
5233
6088
  ts3.ScriptKind.TSX
5234
6089
  );
5235
- const resolved = [];
6090
+ const resolved2 = [];
5236
6091
  const unresolvable = [];
5237
6092
  function visit(node) {
5238
6093
  if (ts3.isCallExpression(node)) {
@@ -5251,13 +6106,13 @@ function extractIncludesWithDiagnostics(code, sourceFileArg) {
5251
6106
  const namePrefix = extractNamePrefixFromInclude(node);
5252
6107
  const dynamicImportSpec = extractDynamicImportSpecifier(secondArg);
5253
6108
  if (ts3.isIdentifier(secondArg)) {
5254
- resolved.push({
6109
+ resolved2.push({
5255
6110
  pathPrefix,
5256
6111
  variableName: secondArg.text,
5257
6112
  namePrefix
5258
6113
  });
5259
6114
  } else if (dynamicImportSpec !== null) {
5260
- resolved.push({
6115
+ resolved2.push({
5261
6116
  pathPrefix,
5262
6117
  moduleSpecifier: dynamicImportSpec,
5263
6118
  namePrefix
@@ -5283,7 +6138,7 @@ function extractIncludesWithDiagnostics(code, sourceFileArg) {
5283
6138
  ts3.forEachChild(node, visit);
5284
6139
  }
5285
6140
  visit(sourceFile);
5286
- return { resolved, unresolvable };
6141
+ return { resolved: resolved2, unresolvable };
5287
6142
  }
5288
6143
  function resolveImportedVariable(code, localName) {
5289
6144
  const defaultImportRegex = /import\s+([\w$]+)\s*(?:,\s*\{[^}]*\})?\s+from\s*["']([^"']+)["']/g;
@@ -5424,7 +6279,7 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
5424
6279
  );
5425
6280
  } else {
5426
6281
  const variableName = def.variableName;
5427
- const resolved = resolveIncludedVariable({
6282
+ const resolved2 = resolveIncludedVariable({
5428
6283
  variableName,
5429
6284
  resolutionFile: targetFile,
5430
6285
  resolutionSource: targetSource,
@@ -5437,12 +6292,12 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
5437
6292
  fileNotFoundDetail: (specifier) => `import("${inc.moduleSpecifier}") default re-exports "${variableName}" from "${specifier}", which resolved to no file`,
5438
6293
  notFoundDetail: () => `import("${inc.moduleSpecifier}") default "${variableName}" not found in its imports or same-file scope`
5439
6294
  });
5440
- if (!resolved) continue;
5441
- childResult = resolved;
6295
+ if (!resolved2) continue;
6296
+ childResult = resolved2;
5442
6297
  }
5443
6298
  } else if (inc.variableName) {
5444
6299
  const variableName = inc.variableName;
5445
- const resolved = resolveIncludedVariable({
6300
+ const resolved2 = resolveIncludedVariable({
5446
6301
  variableName,
5447
6302
  resolutionFile: filePath,
5448
6303
  resolutionSource: fullSource,
@@ -5455,8 +6310,8 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
5455
6310
  fileNotFoundDetail: (specifier) => `import "${specifier}" resolved to no file`,
5456
6311
  notFoundDetail: () => `variable "${variableName}" not found in imports or same-file scope`
5457
6312
  });
5458
- if (!resolved) continue;
5459
- childResult = resolved;
6313
+ if (!resolved2) continue;
6314
+ childResult = resolved2;
5460
6315
  } else {
5461
6316
  continue;
5462
6317
  }
@@ -6041,14 +6896,14 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
6041
6896
  }
6042
6897
 
6043
6898
  // src/vite/plugins/version-plugin.ts
6044
- import { parseAst as parseAst5 } from "vite";
6899
+ import { parseAst as parseAst6 } from "vite";
6045
6900
 
6046
6901
  // src/vite/utils/directive-prologue.ts
6047
- import { parseAst as parseAst4 } from "vite";
6902
+ import { parseAst as parseAst5 } from "vite";
6048
6903
  function hasUseClientDirective2(source) {
6049
6904
  let program;
6050
6905
  try {
6051
- program = parseAst4(source, { lang: "tsx" });
6906
+ program = parseAst5(source, { lang: "tsx" });
6052
6907
  } catch {
6053
6908
  return false;
6054
6909
  }
@@ -6073,7 +6928,7 @@ function getClientModuleSignature(source) {
6073
6928
  if (!hasUseClientDirective2(source)) return void 0;
6074
6929
  let program;
6075
6930
  try {
6076
- program = parseAst5(source, { lang: "tsx" });
6931
+ program = parseAst6(source, { lang: "tsx" });
6077
6932
  } catch {
6078
6933
  return void 0;
6079
6934
  }
@@ -6141,8 +6996,7 @@ function getClientModuleSignature(source) {
6141
6996
  };
6142
6997
  }
6143
6998
  function createVersionPlugin() {
6144
- const buildVersion = Date.now().toString(16);
6145
- let currentVersion3 = buildVersion;
6999
+ let currentVersion3 = Date.now().toString(16);
6146
7000
  let isDev = false;
6147
7001
  let server = null;
6148
7002
  let resolvedCacheDir;
@@ -6162,13 +7016,6 @@ function createVersionPlugin() {
6162
7016
  return {
6163
7017
  name: "@rangojs/router:version",
6164
7018
  enforce: "pre",
6165
- // The build-time shell capture phase (producer B, #699) stamps entries
6166
- // with THIS instance's version — the value folded into the shipped worker
6167
- // — never the discovery temp server's own version-plugin value (a
6168
- // different Date.now() stamp that would fail the serve-side gate).
6169
- api: {
6170
- getBuildVersion: () => currentVersion3
6171
- },
6172
7019
  configResolved(config) {
6173
7020
  isDev = config.command === "serve";
6174
7021
  resolvedCacheDir = config.cacheDir ? String(config.cacheDir).replace(/\\/g, "/") : void 0;
@@ -6190,7 +7037,7 @@ function createVersionPlugin() {
6190
7037
  },
6191
7038
  load(id) {
6192
7039
  if (id === "\0" + VIRTUAL_IDS.version) {
6193
- return getVirtualVersionContent(currentVersion3);
7040
+ return isDev ? getVirtualVersionContent(currentVersion3) : getVirtualBuildVersionContent();
6194
7041
  }
6195
7042
  return null;
6196
7043
  },
@@ -6261,7 +7108,7 @@ import { isAbsolute as isAbsolute2, resolve as resolve4 } from "node:path";
6261
7108
  // src/vite/plugins/performance-tracks.ts
6262
7109
  init_debug();
6263
7110
  import { readFile as readFile2 } from "node:fs/promises";
6264
- var debug5 = createRangoDebugger(NS.transform);
7111
+ var debug6 = createRangoDebugger(NS.transform);
6265
7112
  var RSDW_PATCH_RE = /((?:var|let|const)\s+\w+\s*=\s*root\._children\s*,\s*(\w+)\s*=\s*root\._debugInfo\s*[;,])/;
6266
7113
  function buildPatchReplacement(match, debugInfoVar) {
6267
7114
  return `${match}
@@ -6296,7 +7143,7 @@ function performanceTracksOptimizeDepsPlugin() {
6296
7143
  };
6297
7144
  }
6298
7145
  function performanceTracksPlugin() {
6299
- const counter = createCounter(debug5, "performance-tracks");
7146
+ const counter = createCounter(debug6, "performance-tracks");
6300
7147
  return {
6301
7148
  name: "@rangojs/router:performance-tracks",
6302
7149
  buildEnd() {
@@ -6308,7 +7155,7 @@ function performanceTracksPlugin() {
6308
7155
  try {
6309
7156
  const patched = patchRsdwClientDebugInfoRecovery(code);
6310
7157
  if (!patched.debugInfoVar) return;
6311
- debug5?.("patched RSDW client (var: %s)", patched.debugInfoVar);
7158
+ debug6?.("patched RSDW client (var: %s)", patched.debugInfoVar);
6312
7159
  return patched.code;
6313
7160
  } finally {
6314
7161
  counter?.record(id, performance.now() - start);
@@ -6445,90 +7292,6 @@ function getManualChunks(id) {
6445
7292
  return void 0;
6446
7293
  }
6447
7294
 
6448
- // src/vite/utils/client-chunks.ts
6449
- init_debug();
6450
- init_client_ref_hashing();
6451
- import { gzipSync } from "node:zlib";
6452
- var debugChunks = createRangoDebugger(NS.chunks);
6453
- function isSharedRuntime(meta) {
6454
- return [meta.id, meta.normalizedId].some(
6455
- (path6) => path6.includes("/node_modules/") || /\/@rangojs\/router\//.test(path6) || /\/packages\/(rangojs-router|rsc-router)\/(src|dist)\//.test(path6)
6456
- );
6457
- }
6458
- function sanitizeGroup(name) {
6459
- return name.replace(/[^a-zA-Z0-9_-]+/g, "_").replace(/^_+|_+$/g, "") || "app";
6460
- }
6461
- var ROUTE_ROOT_DIRS = /* @__PURE__ */ new Set([
6462
- "routes",
6463
- "route",
6464
- "pages",
6465
- "page",
6466
- "app",
6467
- "features",
6468
- "feature",
6469
- "views",
6470
- "view",
6471
- "handlers",
6472
- "urls",
6473
- "modules",
6474
- "screens",
6475
- "sections"
6476
- ]);
6477
- var SOURCE_ROOT_DIRS = /* @__PURE__ */ new Set(["app"]);
6478
- function directoryClientChunks(meta, ctx) {
6479
- if (isSharedRuntime(meta)) {
6480
- return void 0;
6481
- }
6482
- if (ctx?.fallbackRefs.size && ctx.fallbackRefs.has(hashRefKey(meta.normalizedId))) {
6483
- debugChunks?.("fallback %s -> app-fallback", meta.normalizedId);
6484
- return "app-fallback";
6485
- }
6486
- const segments = meta.normalizedId.split("/").filter(Boolean);
6487
- const dirCount = segments.length - 1;
6488
- const isMarkerAt = (i) => i < dirCount - 1 && ROUTE_ROOT_DIRS.has(segments[i].toLowerCase());
6489
- for (let i = 0; i < dirCount - 1; i++) {
6490
- if (!isMarkerAt(i)) continue;
6491
- const marker = SOURCE_ROOT_DIRS.has(segments[i].toLowerCase()) && isMarkerAt(i + 1) ? i + 1 : i;
6492
- const group = `app-${sanitizeGroup(segments[marker + 1])}`;
6493
- debugChunks?.("split %s -> %s", meta.normalizedId, group);
6494
- return group;
6495
- }
6496
- debugChunks?.(
6497
- "shared %s (no route-root marker; inherits default grouping)",
6498
- meta.normalizedId
6499
- );
6500
- return void 0;
6501
- }
6502
- function resolveClientChunks(option, ctx) {
6503
- if (!option) return void 0;
6504
- if (option === true) return (meta) => directoryClientChunks(meta, ctx);
6505
- return option;
6506
- }
6507
- var CLIENT_GROUP_PREFIX = "\0virtual:vite-rsc/client-references/group/";
6508
- function clientChunksReport(log = debugChunks) {
6509
- if (!log) return void 0;
6510
- return {
6511
- name: "@rangojs/router:client-chunks-report",
6512
- apply: "build",
6513
- applyToEnvironment: (env) => env.name === "client",
6514
- generateBundle(_options, bundle) {
6515
- for (const chunk of Object.values(bundle)) {
6516
- if (chunk.type !== "chunk") continue;
6517
- const facade = chunk.facadeModuleId;
6518
- if (!facade?.startsWith(CLIENT_GROUP_PREFIX)) continue;
6519
- log(
6520
- "group %s: %d client reference(s), %d B (%d B gzip) -> %s",
6521
- facade.slice(CLIENT_GROUP_PREFIX.length).replaceAll("\0", "\\0"),
6522
- this.getModuleInfo(facade)?.importedIds.length ?? 0,
6523
- Buffer.byteLength(chunk.code),
6524
- gzipSync(chunk.code).length,
6525
- chunk.fileName
6526
- );
6527
- }
6528
- }
6529
- };
6530
- }
6531
-
6532
7295
  // src/vite/plugins/vercel-output.ts
6533
7296
  import { rm, mkdir, cp, writeFile } from "node:fs/promises";
6534
7297
  import { existsSync as existsSync5 } from "node:fs";
@@ -6805,13 +7568,13 @@ ${dim} ${reset}${bold}\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550$
6805
7568
  console.log(banner);
6806
7569
  }
6807
7570
 
6808
- // src/vite/plugins/version-injector.ts
7571
+ // src/vite/plugins/entry-bootstrap-injector.ts
6809
7572
  import { resolve as resolve6 } from "node:path";
6810
7573
  import * as Vite2 from "vite";
6811
- function createVersionInjectorPlugin(rscEntryPath) {
7574
+ function createEntryBootstrapInjectorPlugin(rscEntryPath) {
6812
7575
  let resolvedEntryPath = "";
6813
7576
  return {
6814
- name: "@rangojs/router:version-injector",
7577
+ name: "@rangojs/router:entry-bootstrap-injector",
6815
7578
  enforce: "pre",
6816
7579
  configResolved(config) {
6817
7580
  let entryPath = rscEntryPath;
@@ -6829,17 +7592,8 @@ function createVersionInjectorPlugin(rscEntryPath) {
6829
7592
  }
6830
7593
  const prepend = RSC_ENTRY_BOOTSTRAP_IMPORTS.map(
6831
7594
  (id2) => `import "${id2}";`
6832
- );
6833
- let newCode = code;
6834
- const needsVersion = code.includes("createRSCHandler") && !code.includes("@rangojs/router:version") && /createRSCHandler\s*\(\s*\{/.test(code);
6835
- if (needsVersion) {
6836
- prepend.push(`import { VERSION } from "@rangojs/router:version";`);
6837
- newCode = newCode.replace(
6838
- /createRSCHandler\s*\(\s*\{/,
6839
- "createRSCHandler({\n version: VERSION,"
6840
- );
6841
- }
6842
- const lines = newCode.split("\n");
7595
+ );
7596
+ const lines = code.split("\n");
6843
7597
  let insertAt = 0;
6844
7598
  while (insertAt < lines.length) {
6845
7599
  const trimmed = lines[insertAt].trim();
@@ -6849,7 +7603,7 @@ function createVersionInjectorPlugin(rscEntryPath) {
6849
7603
  break;
6850
7604
  }
6851
7605
  }
6852
- newCode = [
7606
+ const newCode = [
6853
7607
  ...lines.slice(0, insertAt),
6854
7608
  ...prepend,
6855
7609
  ...lines.slice(insertAt)
@@ -6927,8 +7681,8 @@ function createCjsToEsmPlugin() {
6927
7681
 
6928
7682
  // src/vite/router-discovery.ts
6929
7683
  import { createServer as createViteServer } from "vite";
6930
- import { resolve as resolve12 } from "node:path";
6931
- import { readFileSync as readFileSync9 } from "node:fs";
7684
+ import { dirname as dirname7, join as join10, resolve as resolve13 } from "node:path";
7685
+ import { existsSync as existsSync12, readFileSync as readFileSync11 } from "node:fs";
6932
7686
  import { createRequire as createRequire3, register } from "node:module";
6933
7687
  import { pathToFileURL as pathToFileURL2 } from "node:url";
6934
7688
 
@@ -6944,6 +7698,11 @@ import React from "react";
6944
7698
  // src/server/context.ts
6945
7699
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
6946
7700
 
7701
+ // src/cache/cache-key-utils.ts
7702
+ function routerKeyPrefix(routerId2) {
7703
+ return `${encodeURIComponent(routerId2)}@`;
7704
+ }
7705
+
6947
7706
  // src/rsc/shell-capture-constants.ts
6948
7707
  var SHELL_CAPTURE_MAX_WAIT_MS = 15e3;
6949
7708
  var PPR_LANE_HINT = 'Lane rule: only loader(Def, { ssr: false }) executes at capture and bakes its non-promise data; every other loader is live (masked at capture, fresh per request) and needs loading() or an inline <Suspense> above its reader. See the /ppr skill, "The loader lane rule" (node_modules/@rangojs/router/skills/ppr/SKILL.md).';
@@ -7007,15 +7766,28 @@ function normalizeCaptureTimeout(value) {
7007
7766
  var SHELL_PRELUDE_CHUNK_BYTES = 32 * 1024;
7008
7767
 
7009
7768
  // src/vite/inject-client-debug.ts
7010
- function isRouterInternalDebugId(id) {
7011
- if (!id.includes("internal-debug")) return false;
7769
+ var FLAG_MODULE = /\/(internal-debug|internal-suspense-audit)\.[cm]?[jt]sx?(\?|$)/;
7770
+ var FLAG_NAMES = {
7771
+ "internal-debug": "INTERNAL_RANGO_DEBUG",
7772
+ "internal-suspense-audit": "INTERNAL_RANGO_SUSPENSE_AUDIT"
7773
+ };
7774
+ function routerFlagName(id) {
7775
+ if (!id.includes("internal-")) return void 0;
7012
7776
  const norm = id.replace(/\\/g, "/");
7013
- return /\/internal-debug\.[cm]?[jt]sx?(\?|$)/.test(norm) && (norm.includes("/@rangojs/router/") || norm.includes("/packages/rangojs-router/"));
7777
+ if (!norm.includes("/@rangojs/router/") && !norm.includes("/packages/rangojs-router/")) {
7778
+ return void 0;
7779
+ }
7780
+ const match = FLAG_MODULE.exec(norm);
7781
+ return match ? FLAG_NAMES[match[1]] : void 0;
7782
+ }
7783
+ function isRouterInternalDebugId(id) {
7784
+ return routerFlagName(id) !== void 0;
7014
7785
  }
7015
7786
  function injectClientDebugFlag(id) {
7016
- if (!isRouterInternalDebugId(id)) return null;
7787
+ const name = routerFlagName(id);
7788
+ if (name === void 0) return null;
7017
7789
  return {
7018
- code: `export const INTERNAL_RANGO_DEBUG = ${!!process.env.INTERNAL_RANGO_DEBUG};
7790
+ code: `export const ${name} = ${!!process.env[name]};
7019
7791
  `,
7020
7792
  map: null
7021
7793
  };
@@ -7111,7 +7883,7 @@ function createCloudflareProtocolStubPlugin() {
7111
7883
  return null;
7112
7884
  }
7113
7885
  const hits = [];
7114
- walk(ast, (node) => {
7886
+ walk2(ast, (node) => {
7115
7887
  if (!IMPORT_NODE_TYPES.has(node.type)) return;
7116
7888
  const source = node.source;
7117
7889
  if (!source || source.type !== "Literal") return;
@@ -7149,10 +7921,10 @@ function createCloudflareProtocolStubPlugin() {
7149
7921
  }
7150
7922
  };
7151
7923
  }
7152
- function walk(node, visit) {
7924
+ function walk2(node, visit) {
7153
7925
  if (!node || typeof node !== "object") return;
7154
7926
  if (Array.isArray(node)) {
7155
- for (const child of node) walk(child, visit);
7927
+ for (const child of node) walk2(child, visit);
7156
7928
  return;
7157
7929
  }
7158
7930
  const n = node;
@@ -7162,7 +7934,7 @@ function walk(node, visit) {
7162
7934
  if (key === "loc" || key === "start" || key === "end" || key === "range") {
7163
7935
  continue;
7164
7936
  }
7165
- walk(n[key], visit);
7937
+ walk2(n[key], visit);
7166
7938
  }
7167
7939
  }
7168
7940
 
@@ -7228,8 +8000,9 @@ function findMatchingParenInBundle(code, openParenPos) {
7228
8000
  }
7229
8001
  return depth === 0 ? pos : -1;
7230
8002
  }
7231
- function extractHandlerExportsFromChunk(chunkCode, handlerModules, fnName, detectPassthrough) {
8003
+ function extractHandlerExportsFromChunk(chunkCode, handlerModules, fnName, detectPassthrough, onDemandHandlerIds) {
7232
8004
  const handlers = [];
8005
+ const scanPassthrough = detectPassthrough && chunkCode.includes("passthrough");
7233
8006
  for (const [, handlerNames] of handlerModules) {
7234
8007
  for (const name of handlerNames) {
7235
8008
  const eName = escapeRegExp(name);
@@ -7239,7 +8012,7 @@ function extractHandlerExportsFromChunk(chunkCode, handlerModules, fnName, detec
7239
8012
  const match = chunkCode.match(idPattern);
7240
8013
  if (!match) continue;
7241
8014
  let isPassthrough = false;
7242
- if (detectPassthrough) {
8015
+ if (scanPassthrough) {
7243
8016
  const eFnName = escapeRegExp(fnName);
7244
8017
  const callStartRe = new RegExp(
7245
8018
  `(?:const|let|var)\\s+${eName}\\s*=\\s*${eFnName}\\s*(?:<[^>]*>)?\\s*\\(`
@@ -7254,7 +8027,12 @@ function extractHandlerExportsFromChunk(chunkCode, handlerModules, fnName, detec
7254
8027
  }
7255
8028
  }
7256
8029
  }
7257
- handlers.push({ name, handlerId: match[1], passthrough: isPassthrough });
8030
+ handlers.push({
8031
+ name,
8032
+ handlerId: match[1],
8033
+ passthrough: isPassthrough,
8034
+ onDemand: onDemandHandlerIds?.has(match[1]) ?? false
8035
+ });
7258
8036
  }
7259
8037
  }
7260
8038
  return handlers;
@@ -7263,8 +8041,8 @@ function evictHandlerCode(code, exports, fnName, brand) {
7263
8041
  const originalSize = Buffer.byteLength(code);
7264
8042
  let modified = code;
7265
8043
  const eFnName = escapeRegExp(fnName);
7266
- for (const { name, handlerId, passthrough } of exports) {
7267
- if (passthrough) continue;
8044
+ for (const { name, handlerId, passthrough, onDemand } of exports) {
8045
+ if (passthrough || onDemand) continue;
7268
8046
  const eName = escapeRegExp(name);
7269
8047
  const callStartRe = new RegExp(
7270
8048
  `(?:const|let|var)\\s+${eName}\\s*=\\s*${eFnName}\\s*(?:<[^>]*>)?\\s*\\(`
@@ -7294,51 +8072,11 @@ function evictHandlerCode(code, exports, fnName, brand) {
7294
8072
  };
7295
8073
  }
7296
8074
 
7297
- // src/vite/discovery/state.ts
7298
- var VIRTUAL_ROUTES_MANIFEST_ID = "virtual:rsc-router/routes-manifest";
7299
- function createDiscoveryState(entryPath, opts) {
7300
- return {
7301
- resolvedEntryPath: entryPath,
7302
- projectRoot: "",
7303
- isBuildMode: false,
7304
- userResolveAlias: void 0,
7305
- userRunnerConfig: void 0,
7306
- userResolvePlugins: [],
7307
- scanFilter: void 0,
7308
- cachedRouterFiles: void 0,
7309
- opts,
7310
- mergedRouteManifest: null,
7311
- perRouterManifests: [],
7312
- perRouterTrieMap: /* @__PURE__ */ new Map(),
7313
- perRouterPrecomputedMap: /* @__PURE__ */ new Map(),
7314
- perRouterManifestDataMap: /* @__PURE__ */ new Map(),
7315
- clientUrlSourceByReferenceId: /* @__PURE__ */ new Map(),
7316
- clientUrlProjectionMap: /* @__PURE__ */ new Map(),
7317
- prerenderManifestEntries: null,
7318
- staticManifestEntries: null,
7319
- shellCandidates: null,
7320
- prerenderPayloadValues: null,
7321
- shellPhaseTempServer: null,
7322
- handlerChunkInfoMap: /* @__PURE__ */ new Map(),
7323
- staticHandlerChunkInfoMap: /* @__PURE__ */ new Map(),
7324
- rscEntryFileName: null,
7325
- resolvedPrerenderModules: void 0,
7326
- resolvedStaticModules: void 0,
7327
- discoveryDone: null,
7328
- devDiscoveryEpoch: opts?.preset === "cloudflare" ? Date.now() : void 0,
7329
- devServerOrigin: null,
7330
- devServer: null,
7331
- selfWrittenGenFiles: /* @__PURE__ */ new Map(),
7332
- SELF_WRITE_WINDOW_MS: 5e3,
7333
- lastDiscoveryError: null
7334
- };
7335
- }
7336
-
7337
8075
  // src/vite/discovery/self-gen-tracking.ts
7338
- import { createHash as createHash3 } from "node:crypto";
8076
+ import { createHash as createHash4 } from "node:crypto";
7339
8077
  import { readFileSync as readFileSync4 } from "node:fs";
7340
8078
  function markSelfGenWrite(state, filePath, content) {
7341
- const hash = createHash3("sha256").update(content).digest("hex");
8079
+ const hash = createHash4("sha256").update(content).digest("hex");
7342
8080
  state.selfWrittenGenFiles.set(filePath, { at: Date.now(), hash });
7343
8081
  }
7344
8082
  function consumeSelfGenWrite(state, filePath) {
@@ -7356,7 +8094,7 @@ function checkSelfGenWrite(state, filePath, consume) {
7356
8094
  }
7357
8095
  try {
7358
8096
  const current = readFileSync4(filePath, "utf-8");
7359
- const currentHash = createHash3("sha256").update(current).digest("hex");
8097
+ const currentHash = createHash4("sha256").update(current).digest("hex");
7360
8098
  if (currentHash === info.hash) {
7361
8099
  if (consume) state.selfWrittenGenFiles.delete(filePath);
7362
8100
  return true;
@@ -7425,7 +8163,7 @@ function parsePattern(pattern) {
7425
8163
  }
7426
8164
 
7427
8165
  // src/router/route-trie-builder.ts
7428
- function buildRouteTrie(routeManifest, routeToStaticPrefix, routeTrailingSlash, prerenderRouteNames, passthroughRouteNames, responseTypeRoutes) {
8166
+ function buildRouteTrie(routeManifest, routeToStaticPrefix, routeTrailingSlash, prerenderRouteNames, passthroughRouteNames, responseTypeRoutes, onDemandRouteNames) {
7429
8167
  const root = {};
7430
8168
  for (const [routeName, pattern] of Object.entries(routeManifest)) {
7431
8169
  const staticPrefix = routeToStaticPrefix[routeName] || "";
@@ -7440,6 +8178,7 @@ function buildRouteTrie(routeManifest, routeToStaticPrefix, routeTrailingSlash,
7440
8178
  ...trailingSlash ? { ts: trailingSlash } : {},
7441
8179
  ...prerenderRouteNames?.has(routeName) ? { pr: true } : {},
7442
8180
  ...passthroughRouteNames?.has(routeName) ? { pt: true } : {},
8181
+ ...onDemandRouteNames?.has(routeName) ? { od: true } : {},
7443
8182
  ...responseType ? { rt: responseType } : {}
7444
8183
  });
7445
8184
  }
@@ -7632,7 +8371,8 @@ function buildPerRouterTrie(manifest) {
7632
8371
  manifest.routeTrailingSlash,
7633
8372
  manifest.prerenderRoutes ? new Set(manifest.prerenderRoutes) : void 0,
7634
8373
  manifest.passthroughRoutes ? new Set(manifest.passthroughRoutes) : void 0,
7635
- manifest.responseTypeRoutes
8374
+ manifest.responseTypeRoutes,
8375
+ manifest.onDemandRoutes ? new Set(manifest.onDemandRoutes) : void 0
7636
8376
  );
7637
8377
  }
7638
8378
 
@@ -7786,6 +8526,11 @@ function mergeFullManifests(manifests) {
7786
8526
  manifest.passthroughRoutes,
7787
8527
  overwrittenRoutes
7788
8528
  ),
8529
+ onDemandRoutes: mergeRouteList(
8530
+ merged.onDemandRoutes,
8531
+ manifest.onDemandRoutes,
8532
+ overwrittenRoutes
8533
+ ),
7789
8534
  responseTypeRoutes: mergeRouteRecord(
7790
8535
  merged.responseTypeRoutes,
7791
8536
  manifest.responseTypeRoutes,
@@ -7850,7 +8595,7 @@ function contextSet(variables, keyOrVar, value, options) {
7850
8595
  }
7851
8596
 
7852
8597
  // src/vite/utils/prerender-utils.ts
7853
- import { createHash as createHash4 } from "node:crypto";
8598
+ import { createHash as createHash5 } from "node:crypto";
7854
8599
  import {
7855
8600
  copyFileSync,
7856
8601
  existsSync as existsSync6,
@@ -8014,15 +8759,16 @@ function resolvePrerenderError(registry, error, onError, label, elapsed, phase,
8014
8759
  notifyOnError(registry, error, phase, routeKey, pathname);
8015
8760
  throw error;
8016
8761
  }
8762
+ var RANGO_BUILD_DIR = "node_modules/.rangojs-router-build";
8017
8763
  function getStagedAssetDir(projectRoot) {
8018
- return resolve7(projectRoot, "node_modules/.rangojs-router-build/rsc-assets");
8764
+ return resolve7(projectRoot, RANGO_BUILD_DIR, "rsc-assets");
8019
8765
  }
8020
8766
  function resetStagedBuildAssets(projectRoot) {
8021
8767
  rmSync(getStagedAssetDir(projectRoot), { recursive: true, force: true });
8022
8768
  }
8023
8769
  function writeBuildAssetModule(dir, prefix, exportValue) {
8024
8770
  mkdirSync(dir, { recursive: true });
8025
- const contentHash = createHash4("sha256").update(exportValue).digest("hex").slice(0, 8);
8771
+ const contentHash = createHash5("sha256").update(exportValue).digest("hex").slice(0, 8);
8026
8772
  const fileName = `${prefix}-${contentHash}.js`;
8027
8773
  const filePath = resolve7(dir, fileName);
8028
8774
  if (!existsSync6(filePath)) {
@@ -8055,6 +8801,14 @@ function copyStagedBuildAssets(projectRoot, fileNames) {
8055
8801
  // src/vite/discovery/prerender-collection.ts
8056
8802
  init_debug();
8057
8803
  var debug9 = createRangoDebugger(NS.prerender);
8804
+ function recordBuildData(state, kind, key, value, owner) {
8805
+ state.buildData.push({
8806
+ kind,
8807
+ key,
8808
+ digest: sha256(value),
8809
+ ...owner
8810
+ });
8811
+ }
8058
8812
  async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
8059
8813
  if (!state.opts?.enableBuildPrerender || !state.isBuildMode) return;
8060
8814
  const overallStart = debug9 ? performance.now() : 0;
@@ -8231,7 +8985,7 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
8231
8985
  group.concurrency,
8232
8986
  async (entry) => {
8233
8987
  const startUrl = performance.now();
8234
- for (const [, routerInstance] of registry) {
8988
+ for (const [routerId2, routerInstance] of registry) {
8235
8989
  if (!routerInstance.matchForPrerender) continue;
8236
8990
  try {
8237
8991
  const result = await routerInstance.matchForPrerender(
@@ -8261,6 +9015,9 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
8261
9015
  "__pr",
8262
9016
  mainValue
8263
9017
  );
9018
+ recordBuildData(state, "prerender", mainKey, mainValue, {
9019
+ routerId: routerId2
9020
+ });
8264
9021
  if (result.ppr !== void 0 && result.ppr !== false) {
8265
9022
  (state.shellCandidates ??= []).push({
8266
9023
  urlPath: entry.urlPath,
@@ -8286,6 +9043,15 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
8286
9043
  "__pr",
8287
9044
  interceptValue
8288
9045
  );
9046
+ recordBuildData(
9047
+ state,
9048
+ "prerender",
9049
+ interceptKey,
9050
+ interceptValue,
9051
+ {
9052
+ routerId: routerId2
9053
+ }
9054
+ );
8289
9055
  }
8290
9056
  const elapsed = (performance.now() - startUrl).toFixed(0);
8291
9057
  console.log(
@@ -8388,6 +9154,9 @@ async function renderStaticHandlers(state, rscEnv, registry) {
8388
9154
  "__st",
8389
9155
  exportValue
8390
9156
  );
9157
+ recordBuildData(state, "static", def.$$id, exportValue, {
9158
+ moduleId
9159
+ });
8391
9160
  const elapsed = (performance.now() - startHandler).toFixed(0);
8392
9161
  console.log(`[rango] OK ${name.padEnd(40)} (${elapsed}ms)`);
8393
9162
  staticDone++;
@@ -8534,8 +9303,8 @@ init_client_ref_hashing();
8534
9303
 
8535
9304
  // src/vite/discovery/client-urls-projection.ts
8536
9305
  import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
8537
- import { isAbsolute as isAbsolute3, relative as relative4, resolve as resolve8 } from "node:path";
8538
- import { normalizePath as normalizePath5, parseAst as parseAst6 } from "vite";
9306
+ import { isAbsolute as isAbsolute3, relative as relative5, resolve as resolve8 } from "node:path";
9307
+ import { normalizePath as normalizePath5, parseAst as parseAst7 } from "vite";
8539
9308
 
8540
9309
  // src/client-urls/server-projection.ts
8541
9310
  import * as React5 from "react";
@@ -8548,9 +9317,10 @@ function isUnderTestRunner() {
8548
9317
  // src/client.tsx
8549
9318
  import {
8550
9319
  Component as Component3,
8551
- createElement as createElement3,
8552
- useContext as useContext16,
8553
- useMemo as useMemo8,
9320
+ createElement as createElement4,
9321
+ isValidElement as isValidElement2,
9322
+ useContext as useContext17,
9323
+ useMemo as useMemo9,
8554
9324
  Suspense as Suspense2
8555
9325
  } from "react";
8556
9326
 
@@ -8637,6 +9407,8 @@ function createHandle(collect, __injectedId) {
8637
9407
  // src/browser/react/context.ts
8638
9408
  import { createContext } from "react";
8639
9409
  var NavigationStoreContext = createContext(null);
9410
+ var LocationStateContext = createContext(void 0);
9411
+ var HydrationBarrierContext = createContext(0);
8640
9412
 
8641
9413
  // src/handles/meta.ts
8642
9414
  function isUnsetDescriptor(descriptor) {
@@ -8842,17 +9614,31 @@ import { createContext as createContext4 } from "react";
8842
9614
  var OutletContext = createContext4(null);
8843
9615
 
8844
9616
  // src/route-content-wrapper.tsx
8845
- import { Component, Suspense, use } from "react";
9617
+ import { Component, Suspense, use, useMemo as useMemo3 } from "react";
9618
+
9619
+ // src/outlet-provider.tsx
9620
+ import {
9621
+ useContext as useContext4,
9622
+ useMemo
9623
+ } from "react";
9624
+
9625
+ // src/suspense-audit-react.tsx
9626
+ import { useEffect as useEffect3, useLayoutEffect, useRef as useRef2 } from "react";
9627
+
9628
+ // src/suspense-audit.ts
9629
+ init_internal_debug();
9630
+
9631
+ // src/internal-suspense-audit.ts
9632
+ var INTERNAL_RANGO_SUSPENSE_AUDIT = typeof process !== "undefined" && Boolean(process.env?.INTERNAL_RANGO_SUSPENSE_AUDIT);
8846
9633
 
8847
9634
  // src/outlet-provider.tsx
8848
- import { useContext as useContext4, useMemo } from "react";
8849
9635
  import { jsx as jsx3 } from "react/jsx-runtime";
8850
9636
 
8851
9637
  // src/decode-loader-results.ts
8852
9638
  import { createElement as createElement2 } from "react";
8853
9639
 
8854
9640
  // src/loader-redirect.tsx
8855
- import { useEffect as useEffect3 } from "react";
9641
+ import { useEffect as useEffect4 } from "react";
8856
9642
 
8857
9643
  // src/browser/react/use-router.ts
8858
9644
  import { useContext as useContext6, useMemo as useMemo2 } from "react";
@@ -8882,6 +9668,7 @@ init_redirect_origin();
8882
9668
 
8883
9669
  // src/route-content-wrapper.tsx
8884
9670
  import { jsx as jsx4 } from "react/jsx-runtime";
9671
+ import { createElement as createElement3 } from "react";
8885
9672
 
8886
9673
  // src/use-loader.tsx
8887
9674
  import {
@@ -8890,18 +9677,18 @@ import {
8890
9677
  use as use2,
8891
9678
  useCallback,
8892
9679
  useContext as useContext7,
8893
- useEffect as useEffect4,
8894
- useMemo as useMemo3,
9680
+ useEffect as useEffect5,
9681
+ useMemo as useMemo4,
8895
9682
  useOptimistic as useOptimistic2,
8896
- useRef as useRef2,
9683
+ useRef as useRef3,
8897
9684
  useState as useState3
8898
9685
  } from "react";
8899
9686
 
8900
9687
  // src/thenable-status.ts
8901
- function unwrapsSynchronously(stream) {
8902
- const status = stream.status;
8903
- return status === "fulfilled" || status === "resolved_model" || status === "resolved_module";
8904
- }
9688
+ var SETTLED_THENABLE = Object.assign(
9689
+ Promise.resolve(void 0),
9690
+ { status: "fulfilled", value: void 0, _debugInfo: [] }
9691
+ );
8905
9692
 
8906
9693
  // src/loader-store.ts
8907
9694
  var EMPTY_SNAPSHOT = Object.freeze({
@@ -8911,410 +9698,19 @@ var EMPTY_SNAPSHOT = Object.freeze({
8911
9698
  isLoading: false,
8912
9699
  requestId: 0
8913
9700
  });
8914
- function normalizeGroups(group) {
8915
- if (group === void 0) return [];
8916
- if (typeof group === "string") return [group];
8917
- return [...new Set(group)];
8918
- }
8919
- var LoaderStore = class {
8920
- constructor() {
8921
- this.entries = /* @__PURE__ */ new Map();
8922
- /** loader.$$id -> set of bucket keys, so clearFamily() can reach every bucket. */
8923
- this.families = /* @__PURE__ */ new Map();
8924
- /** refresh group name -> set of bucket keys, for refreshGroups(). */
8925
- this.groups = /* @__PURE__ */ new Map();
8926
- /**
8927
- * loader.$$id -> per-loader streams a committed navigation is still
8928
- * receiving. Registered and announced by announcePendingStreams(), which
8929
- * browser/partial-update.ts calls INSIDE every transition commit, so a
8930
- * reader whose content is held on screen by that transition pins
8931
- * `isLoading: true` for the data it is still showing until the transition
8932
- * lands (use-loader.tsx). Family-keyed on purpose: a persisting layout
8933
- * reader of the same createLoader the child is re-running is pinned too
8934
- * (the loader is in flight; not "this segment's copy is being replaced" —
8935
- * issue #862, working as designed). The new tree's own read of the loader
8936
- * suspends at the read site and never observes the flag. A Set, not a
8937
- * flag: overlapping navigations (second click before the first stream
8938
- * lands) keep the family pending until the LAST stream settles.
8939
- */
8940
- this.pendingStreams = /* @__PURE__ */ new Map();
8941
- }
8942
- getOrCreate(bucketKey) {
8943
- let e = this.entries.get(bucketKey);
8944
- if (!e) {
8945
- e = {
8946
- snapshot: EMPTY_SNAPSHOT,
8947
- listeners: /* @__PURE__ */ new Set(),
8948
- latestRequestId: 0,
8949
- loaderId: bucketKey,
8950
- sticky: false,
8951
- pendingClear: false,
8952
- clearWhenSettled: false,
8953
- groups: /* @__PURE__ */ new Map(),
8954
- refetch: void 0,
8955
- streamPendingListeners: /* @__PURE__ */ new Set()
8956
- };
8957
- this.entries.set(bucketKey, e);
8958
- }
8959
- return e;
8960
- }
8961
- registerFamily(loaderId, bucketKey) {
8962
- let fam = this.families.get(loaderId);
8963
- if (!fam) {
8964
- fam = /* @__PURE__ */ new Set();
8965
- this.families.set(loaderId, fam);
8966
- }
8967
- fam.add(bucketKey);
8968
- }
8969
- /**
8970
- * Subscribe to entry changes for `bucketKey`.
8971
- * Returns an unsubscribe function. A sticky bucket's entry is kept around even
8972
- * after the last subscriber leaves so that an in-flight `load()` can still
8973
- * commit if the consumer remounts. An ephemeral bucket is dropped once its
8974
- * last subscriber leaves (deferred, see maybeScheduleRefcountClear).
8975
- */
8976
- subscribe(bucketKey, cb, options) {
8977
- const e = this.getOrCreate(bucketKey);
8978
- const loaderId = options?.loaderId ?? bucketKey;
8979
- e.loaderId = loaderId;
8980
- this.registerFamily(loaderId, bucketKey);
8981
- if (options?.ephemeral !== true) e.sticky = true;
8982
- const groups = normalizeGroups(options?.group);
8983
- for (const group of groups) {
8984
- this.addToGroup(group, bucketKey, e, options?.refetch);
8985
- }
8986
- e.pendingClear = false;
8987
- e.clearWhenSettled = false;
8988
- e.listeners.add(cb);
8989
- const onStreamPending = options?.onStreamPending;
8990
- if (onStreamPending) e.streamPendingListeners.add(onStreamPending);
8991
- return () => {
8992
- e.listeners.delete(cb);
8993
- if (onStreamPending) e.streamPendingListeners.delete(onStreamPending);
8994
- for (const group of groups) this.releaseGroup(group, bucketKey, e);
8995
- this.maybeScheduleRefcountClear(bucketKey, e);
8996
- };
8997
- }
8998
- addToGroup(group, bucketKey, e, refetch) {
8999
- e.groups.set(group, (e.groups.get(group) ?? 0) + 1);
9000
- if (refetch) e.refetch = refetch;
9001
- let members = this.groups.get(group);
9002
- if (!members) {
9003
- members = /* @__PURE__ */ new Set();
9004
- this.groups.set(group, members);
9005
- }
9006
- members.add(bucketKey);
9007
- }
9008
- releaseGroup(group, bucketKey, e) {
9009
- const next = (e.groups.get(group) ?? 0) - 1;
9010
- if (next > 0) {
9011
- e.groups.set(group, next);
9012
- return;
9013
- }
9014
- e.groups.delete(group);
9015
- const members = this.groups.get(group);
9016
- if (members) {
9017
- members.delete(bucketKey);
9018
- if (members.size === 0) this.groups.delete(group);
9019
- }
9020
- }
9021
- /** Remove a bucket from every group it belongs to (used when it is dropped). */
9022
- removeFromAllGroups(bucketKey, e) {
9023
- for (const group of e.groups.keys()) {
9024
- const members = this.groups.get(group);
9025
- if (members) {
9026
- members.delete(bucketKey);
9027
- if (members.size === 0) this.groups.delete(group);
9028
- }
9029
- }
9030
- e.groups.clear();
9031
- }
9032
- /**
9033
- * Refresh every currently-mounted bucket tagged with ANY of the given group
9034
- * names, with a plain GET (no params/body). Accepts a single name or an array
9035
- * of names. A bucket that belongs to more than one of the named groups is
9036
- * refreshed exactly once (members are unioned and deduped by bucket key), as
9037
- * are multiple reads of one bucket. Resolves when all refreshes settle; rejects
9038
- * with an `AggregateError` of the failures if any member fails — each failing
9039
- * member also records its error on its own snapshot.
9040
- */
9041
- async refreshGroups(groups) {
9042
- const names = typeof groups === "string" ? [groups] : groups;
9043
- const buckets = /* @__PURE__ */ new Set();
9044
- for (const name of names) {
9045
- const members = this.groups.get(name);
9046
- if (!members) continue;
9047
- for (const bucketKey of members) buckets.add(bucketKey);
9048
- }
9049
- if (buckets.size === 0) return;
9050
- const thunks = [];
9051
- for (const bucketKey of buckets) {
9052
- const e = this.entries.get(bucketKey);
9053
- if (!e || e.listeners.size === 0 || !e.refetch) continue;
9054
- thunks.push(e.refetch);
9055
- }
9056
- if (thunks.length === 0) return;
9057
- const results = await Promise.allSettled(thunks.map((t) => t()));
9058
- const reasons = results.filter((r) => r.status === "rejected").map((r) => r.reason);
9059
- if (reasons.length > 0) {
9060
- const label = names.map((n) => `"${n}"`).join(", ");
9061
- throw new AggregateError(
9062
- reasons,
9063
- `refreshGroups(${label}) had ${reasons.length} failure(s)`
9064
- );
9065
- }
9066
- }
9067
- /**
9068
- * Schedule a deferred drop for an ad-hoc (never-sticky) bucket whose last
9069
- * subscriber just left. Deferred to a microtask so a StrictMode / transition
9070
- * remount that resubscribes in the same tick cancels it.
9071
- */
9072
- maybeScheduleRefcountClear(bucketKey, e) {
9073
- if (e.listeners.size > 0) return;
9074
- if (e.sticky) return;
9075
- if (e.pendingClear) return;
9076
- e.pendingClear = true;
9077
- queueMicrotask(() => {
9078
- if (!e.pendingClear) return;
9079
- e.pendingClear = false;
9080
- if (e.listeners.size > 0) return;
9081
- if (e.snapshot.isLoading) {
9082
- e.clearWhenSettled = true;
9083
- return;
9084
- }
9085
- this.dropBucket(bucketKey);
9086
- });
9087
- }
9088
- /** Remove a bucket entry entirely and prune it from its family/group indexes. */
9089
- dropBucket(bucketKey) {
9090
- const e = this.entries.get(bucketKey);
9091
- if (!e) return;
9092
- this.removeFromAllGroups(bucketKey, e);
9093
- this.entries.delete(bucketKey);
9094
- const fam = this.families.get(e.loaderId);
9095
- if (fam) {
9096
- fam.delete(bucketKey);
9097
- if (fam.size === 0) this.families.delete(e.loaderId);
9098
- }
9099
- }
9100
- /** Drop an ephemeral bucket that settled with no subscribers left. */
9101
- dropIfSettled(bucketKey, e) {
9102
- if (e.clearWhenSettled && e.listeners.size === 0) {
9103
- e.clearWhenSettled = false;
9104
- this.dropBucket(bucketKey);
9105
- }
9106
- }
9107
- /**
9108
- * Returns the current snapshot for `bucketKey`. Stable reference between
9109
- * mutations — subscribers rely on this to avoid spurious re-renders.
9110
- * Returns `EMPTY_SNAPSHOT` (a singleton) when the entry has never been
9111
- * mutated or has been cleared.
9112
- */
9113
- getSnapshot(bucketKey) {
9114
- return this.entries.get(bucketKey)?.snapshot ?? EMPTY_SNAPSHOT;
9115
- }
9116
- /**
9117
- * Reserve a fresh request id for an upcoming `load()` call. The returned id
9118
- * is the new "latest"; any older in-flight requests will fail their gating
9119
- * check on `finishData` / `finishError` / `finishLoading` and be dropped.
9120
- *
9121
- * Callers should follow with `beginRequest(id, requestId)` to flip the
9122
- * loading flag on AND clear any leftover error from a previous attempt
9123
- * — the latter matters for `throwOnError: false` consumers, which would
9124
- * otherwise keep showing the stale error throughout the retry.
9125
- */
9126
- reserveRequestId(bucketKey) {
9127
- const e = this.getOrCreate(bucketKey);
9128
- e.latestRequestId++;
9129
- return e.latestRequestId;
9130
- }
9131
- /**
9132
- * Mark the request as in-flight: `isLoading = true`, `error = null`.
9133
- * Combines the two operations so a retry doesn't render the previous
9134
- * error during the new request. Gated on `requestId === latestRequestId`
9135
- * for symmetry with the other mutators.
9136
- */
9137
- beginRequest(bucketKey, requestId) {
9138
- const e = this.entries.get(bucketKey);
9139
- if (!e || requestId !== e.latestRequestId) return;
9140
- if (e.snapshot.isLoading && e.snapshot.error === null) return;
9141
- e.snapshot = Object.freeze({
9142
- value: e.snapshot.value,
9143
- hasValue: e.snapshot.hasValue,
9144
- error: null,
9145
- isLoading: true,
9146
- requestId
9147
- });
9148
- this.notify(e);
9149
- }
9150
- /**
9151
- * Commit a successful result. No-op if `requestId` is not the latest
9152
- * (a newer `load()` was issued or `clear()` ran). Clearing `error` is
9153
- * intentional: a successful refetch should hide the previous failure.
9154
- */
9155
- finishData(bucketKey, requestId, value) {
9156
- const e = this.entries.get(bucketKey);
9157
- if (!e || requestId !== e.latestRequestId) return;
9158
- e.snapshot = Object.freeze({
9159
- value,
9160
- hasValue: true,
9161
- error: null,
9162
- isLoading: false,
9163
- requestId
9164
- });
9165
- this.notify(e);
9166
- this.dropIfSettled(bucketKey, e);
9167
- }
9168
- /**
9169
- * Commit an error. Preserves the last good `value` so consumers can keep
9170
- * showing previous data while displaying the error if they choose. No-op
9171
- * if `requestId` is not the latest.
9172
- */
9173
- finishError(bucketKey, requestId, error) {
9174
- const e = this.entries.get(bucketKey);
9175
- if (!e || requestId !== e.latestRequestId) return;
9176
- e.snapshot = Object.freeze({
9177
- value: e.snapshot.value,
9178
- hasValue: e.snapshot.hasValue,
9179
- error,
9180
- isLoading: false,
9181
- requestId
9182
- });
9183
- this.notify(e);
9184
- this.dropIfSettled(bucketKey, e);
9185
- }
9186
- /**
9187
- * Update loading flag. Gated on `requestId` to fix the race where an old
9188
- * load() finishes after a new one started — its `setLoading(false)` would
9189
- * otherwise hide the new request's spinner.
9190
- */
9191
- setLoading(bucketKey, requestId, isLoading) {
9192
- const e = this.entries.get(bucketKey);
9193
- if (!e || requestId !== e.latestRequestId) return;
9194
- if (e.snapshot.isLoading === isLoading) return;
9195
- e.snapshot = Object.freeze({
9196
- ...e.snapshot,
9197
- isLoading
9198
- });
9199
- this.notify(e);
9200
- }
9201
- /**
9202
- * Reset a single bucket entry. Bumps `latestRequestId` so any in-flight
9203
- * `load()` whose promise is still pending will fail its gate when it resolves
9204
- * and be dropped — prevents pre-navigation loads from clobbering the new
9205
- * route's context. The entry itself is kept (sticky-bucket semantics).
9206
- */
9207
- clear(bucketKey) {
9208
- const e = this.entries.get(bucketKey);
9209
- if (!e) return;
9210
- e.latestRequestId++;
9211
- if (e.snapshot === EMPTY_SNAPSHOT) return;
9212
- e.snapshot = EMPTY_SNAPSHOT;
9213
- this.notify(e);
9214
- }
9215
- /**
9216
- * Reset every sticky bucket belonging to `loaderId`. Called on navigation /
9217
- * route-context change: all route-registered reads of the loader (keyed or
9218
- * not) drop back to seeding from fresh `loaderData`. Ephemeral buckets are
9219
- * intentionally left alone — they are governed by subscriber refcount, so a
9220
- * persistent keyed reader outside the outlet keeps its value across a
9221
- * navigation rather than blanking out.
9222
- */
9223
- clearFamily(loaderId) {
9224
- const fam = this.families.get(loaderId);
9225
- if (!fam) return;
9226
- for (const bucketKey of fam) {
9227
- const e = this.entries.get(bucketKey);
9228
- if (!e || !e.sticky) continue;
9229
- this.clear(bucketKey);
9230
- }
9231
- }
9232
- notify(e) {
9233
- for (const cb of e.listeners) cb();
9234
- }
9235
- /**
9236
- * Register a still-streaming per-loader result for `loaderId`. No-op for a
9237
- * settled Flight chunk (unwrapsSynchronously / "rejected"): the forceAwait
9238
- * and action lanes commit settled chunks, and a { ssr: false } loader is
9239
- * awaited before flush. Released as the stream settles, either way.
9240
- */
9241
- trackPendingStream(loaderId, stream) {
9242
- if (unwrapsSynchronously(stream) || stream.status === "rejected") {
9243
- return;
9244
- }
9245
- let set = this.pendingStreams.get(loaderId);
9246
- if (set?.has(stream)) return;
9247
- if (!set) {
9248
- set = /* @__PURE__ */ new Set();
9249
- this.pendingStreams.set(loaderId, set);
9250
- }
9251
- set.add(stream);
9252
- const release = () => {
9253
- const current = this.pendingStreams.get(loaderId);
9254
- if (!current || !current.delete(stream)) return;
9255
- if (current.size === 0) this.pendingStreams.delete(loaderId);
9256
- };
9257
- stream.then(release, release);
9258
- }
9259
- /** True while any navigation stream for `loaderId` is still pending. */
9260
- isStreamPending(loaderId) {
9261
- return this.pendingStreams.has(loaderId);
9262
- }
9263
- /**
9264
- * Register the loader segments of a tree being committed whose data is
9265
- * still a pending promise, then fire `onStreamPending` on every subscriber
9266
- * of every family with a pending stream. MUST run inside the startTransition
9267
- * that commits the tree: the hook answers with a useOptimistic update, which
9268
- * React reverts exactly when that transition commits — so the held reader
9269
- * reports isLoading:true for as long as its data stays on screen and not a
9270
- * frame longer. Outside a transition React reverts an optimistic update on
9271
- * the next render, pinning nothing useful. Registering here rather than in
9272
- * the tree build keeps register + announce in one call; settled chunks
9273
- * (cached/reused segments, forceAwait lanes) are skipped by
9274
- * trackPendingStream.
9275
- */
9276
- announcePendingStreams(segments) {
9277
- for (const segment of segments) {
9278
- if (segment.type === "loader" && segment.loaderId && segment.loaderData instanceof Promise) {
9279
- this.trackPendingStream(segment.loaderId, segment.loaderData);
9280
- }
9281
- }
9282
- for (const loaderId of this.pendingStreams.keys()) {
9283
- const fam = this.families.get(loaderId);
9284
- if (!fam) continue;
9285
- for (const bucketKey of fam) {
9286
- const e = this.entries.get(bucketKey);
9287
- if (!e) continue;
9288
- for (const cb of e.streamPendingListeners) cb();
9289
- }
9290
- }
9291
- }
9292
- /**
9293
- * Test-only escape hatch. Drops every entry. Production code should never
9294
- * call this; the store is process-scoped and lives for the tab's lifetime.
9295
- * @internal
9296
- */
9297
- reset() {
9298
- this.entries.clear();
9299
- this.families.clear();
9300
- this.groups.clear();
9301
- this.pendingStreams.clear();
9302
- }
9303
- };
9304
- var loaderStore = new LoaderStore();
9305
9701
 
9306
9702
  // src/browser/react/use-navigation.ts
9307
9703
  import {
9308
9704
  useContext as useContext8,
9309
9705
  useState as useState4,
9310
- useEffect as useEffect5,
9706
+ useEffect as useEffect6,
9311
9707
  useOptimistic as useOptimistic3,
9312
9708
  startTransition as startTransition3,
9313
- useRef as useRef3
9709
+ useRef as useRef4
9314
9710
  } from "react";
9315
9711
 
9316
9712
  // src/browser/react/use-pathname.ts
9317
- import { useContext as useContext9, useState as useState5, useEffect as useEffect6, useRef as useRef4 } from "react";
9713
+ import { useContext as useContext9, useState as useState5, useEffect as useEffect7, useRef as useRef5 } from "react";
9318
9714
 
9319
9715
  // src/client-urls/optimistic-location.ts
9320
9716
  import { createContext as createContext6 } from "react";
@@ -9324,38 +9720,39 @@ var OptimisticLocationContext = createContext6(null);
9324
9720
  import {
9325
9721
  useCallback as useCallback2,
9326
9722
  useContext as useContext10,
9327
- useEffect as useEffect7,
9328
- useMemo as useMemo4,
9329
- useRef as useRef5,
9723
+ useEffect as useEffect8,
9724
+ useMemo as useMemo5,
9725
+ useRef as useRef6,
9330
9726
  useState as useState6
9331
9727
  } from "react";
9332
9728
 
9333
9729
  // src/browser/react/use-params.ts
9334
- import { useContext as useContext11, useState as useState7, useEffect as useEffect8, useRef as useRef6 } from "react";
9730
+ import { useContext as useContext11, useState as useState7, useEffect as useEffect9, useRef as useRef7 } from "react";
9335
9731
  var EMPTY_PARAMS = Object.freeze({});
9336
9732
 
9337
9733
  // src/browser/react/use-action.ts
9338
9734
  import {
9339
9735
  useContext as useContext12,
9340
9736
  useState as useState8,
9341
- useEffect as useEffect9,
9342
- useRef as useRef7,
9737
+ useEffect as useEffect10,
9738
+ useRef as useRef8,
9343
9739
  useOptimistic as useOptimistic4,
9344
9740
  startTransition as startTransition4
9345
9741
  } from "react";
9346
9742
 
9347
9743
  // src/browser/react/use-segments.ts
9348
- import { useContext as useContext13, useState as useState9, useEffect as useEffect10, useRef as useRef8 } from "react";
9744
+ import { useContext as useContext13, useState as useState9, useEffect as useEffect11, useRef as useRef9 } from "react";
9349
9745
 
9350
9746
  // src/browser/react/NavigationProvider.tsx
9351
9747
  import {
9352
9748
  useState as useState12,
9353
- useEffect as useEffect12,
9354
- useLayoutEffect,
9749
+ useEffect as useEffect13,
9750
+ useLayoutEffect as useLayoutEffect2,
9355
9751
  useCallback as useCallback4,
9356
- useMemo as useMemo6,
9357
- useRef as useRef10,
9358
- use as use3
9752
+ useMemo as useMemo7,
9753
+ useRef as useRef11,
9754
+ use as use3,
9755
+ startTransition as startTransition5
9359
9756
  } from "react";
9360
9757
 
9361
9758
  // src/root-error-boundary.tsx
@@ -9365,8 +9762,8 @@ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
9365
9762
  // src/theme/ThemeProvider.tsx
9366
9763
  import {
9367
9764
  useCallback as useCallback3,
9368
- useEffect as useEffect11,
9369
- useMemo as useMemo5,
9765
+ useEffect as useEffect12,
9766
+ useMemo as useMemo6,
9370
9767
  useState as useState11
9371
9768
  } from "react";
9372
9769
 
@@ -9402,10 +9799,9 @@ import {
9402
9799
  forwardRef,
9403
9800
  useCallback as useCallback5,
9404
9801
  useContext as useContext15,
9405
- useEffect as useEffect14,
9406
- useMemo as useMemo7,
9407
- useRef as useRef12,
9408
- useSyncExternalStore
9802
+ useEffect as useEffect15,
9803
+ useMemo as useMemo8,
9804
+ useRef as useRef13
9409
9805
  } from "react";
9410
9806
 
9411
9807
  // src/browser/react/use-link-status.ts
@@ -9413,10 +9809,10 @@ import {
9413
9809
  createContext as createContext7,
9414
9810
  useContext as useContext14,
9415
9811
  useState as useState13,
9416
- useEffect as useEffect13,
9417
- useRef as useRef11,
9812
+ useEffect as useEffect14,
9813
+ useRef as useRef12,
9418
9814
  useOptimistic as useOptimistic5,
9419
- startTransition as startTransition5
9815
+ startTransition as startTransition6
9420
9816
  } from "react";
9421
9817
  var LinkContext = createContext7(
9422
9818
  null
@@ -9476,24 +9872,18 @@ function subscribeToLocationChange(eventController, listener) {
9476
9872
  }
9477
9873
 
9478
9874
  // src/browser/prefetch/default-strategy.ts
9479
- var hoverNoneQuery = null;
9480
- var defaultStrategy = process.env.NODE_ENV === "production" ? "viewport" : "none";
9481
- function getHoverNoneQuery() {
9482
- if (typeof window === "undefined") return null;
9483
- if (!hoverNoneQuery) {
9484
- hoverNoneQuery = window.matchMedia("(hover: none)");
9485
- }
9486
- return hoverNoneQuery;
9487
- }
9488
- function getDefaultPrefetchStrategy() {
9489
- return defaultStrategy;
9875
+ var ENVIRONMENT_DEFAULT_PREFETCH = process.env.NODE_ENV === "production" ? "viewport" : "none";
9876
+ function hoverNoneQuery() {
9877
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function")
9878
+ return null;
9879
+ return window.matchMedia("(hover: none)");
9490
9880
  }
9491
9881
  function resolveAdaptiveStrategy(strategy) {
9492
9882
  if (strategy !== "adaptive") return strategy;
9493
- return getHoverNoneQuery()?.matches ? "viewport" : "hover";
9883
+ return hoverNoneQuery()?.matches ? "viewport" : "hover";
9494
9884
  }
9495
9885
  function subscribeToAdaptiveStrategyChange(listener) {
9496
- const query = getHoverNoneQuery();
9886
+ const query = hoverNoneQuery();
9497
9887
  if (!query) return () => {
9498
9888
  };
9499
9889
  if (typeof query.addEventListener === "function") {
@@ -9647,8 +10037,6 @@ function isPrefetchScopeDisabled(element) {
9647
10037
  // src/browser/react/Link.tsx
9648
10038
  init_loader();
9649
10039
  import { jsx as jsx8 } from "react/jsx-runtime";
9650
- var IGNORE_STRATEGY_CHANGES = (_listener) => () => {
9651
- };
9652
10040
  function isExternalUrl(href2, origin) {
9653
10041
  if (href2.startsWith("//")) return true;
9654
10042
  if (href2.startsWith("http://") || href2.startsWith("https://")) {
@@ -9679,23 +10067,16 @@ var Link = forwardRef(function Link2({
9679
10067
  ...props
9680
10068
  }, ref) {
9681
10069
  const ctx = useContext15(NavigationStoreContext);
9682
- const storeLocation = ctx?.eventController.getState().location;
9683
- const origin = storeLocation?.origin ?? (typeof window !== "undefined" ? window.location.origin : void 0);
9684
- const isExternal = isExternalUrl(to, origin);
9685
- const resolvedTo = useMemo7(() => {
10070
+ const isExternal = isExternalUrl(to, ctx?.origin);
10071
+ const resolvedTo = useMemo8(() => {
9686
10072
  if (isExternal) return to;
9687
10073
  const bn = ctx?.basename;
9688
10074
  if (!bn || !to.startsWith("/") || to.startsWith(bn + "/") || to === bn)
9689
10075
  return to;
9690
10076
  return to === "/" ? bn : bn + to;
9691
10077
  }, [to, isExternal, ctx?.basename]);
9692
- const configuredStrategy = prefetch ?? ctx?.defaultPrefetch ?? getDefaultPrefetchStrategy();
9693
- const resolvedStrategy = useSyncExternalStore(
9694
- configuredStrategy === "adaptive" ? subscribeToAdaptiveStrategyChange : IGNORE_STRATEGY_CHANGES,
9695
- () => resolveAdaptiveStrategy(configuredStrategy),
9696
- () => configuredStrategy === "adaptive" ? "hover" : configuredStrategy
9697
- );
9698
- const internalRef = useRef12(null);
10078
+ const configuredStrategy = prefetch ?? ctx?.defaultPrefetch ?? ENVIRONMENT_DEFAULT_PREFETCH;
10079
+ const internalRef = useRef13(null);
9699
10080
  const setRef = useCallback5(
9700
10081
  (node) => {
9701
10082
  internalRef.current = node;
@@ -9707,7 +10088,7 @@ var Link = forwardRef(function Link2({
9707
10088
  },
9708
10089
  [ref]
9709
10090
  );
9710
- const stateRef = useRef12(state);
10091
+ const stateRef = useRef13(state);
9711
10092
  stateRef.current = state;
9712
10093
  const handleClick = useCallback5(
9713
10094
  (e) => {
@@ -9752,7 +10133,8 @@ var Link = forwardRef(function Link2({
9752
10133
  );
9753
10134
  const handleMouseEnter = useCallback5(() => {
9754
10135
  const element = internalRef.current;
9755
- if ((resolvedStrategy === "hover" || resolvedStrategy === "viewport") && !isExternal && ctx?.store && (!element || !isHashOnlyNavigation(element) && !isPrefetchScopeDisabled(element))) {
10136
+ const strategy = resolveAdaptiveStrategy(configuredStrategy);
10137
+ if ((strategy === "hover" || strategy === "viewport") && !isExternal && ctx?.store && (!element || !isHashOnlyNavigation(element) && !isPrefetchScopeDisabled(element))) {
9756
10138
  const segmentState = ctx.store.getSegmentState();
9757
10139
  prefetchDirect2(
9758
10140
  resolvedTo,
@@ -9762,13 +10144,18 @@ var Link = forwardRef(function Link2({
9762
10144
  prefetchKey
9763
10145
  );
9764
10146
  }
9765
- }, [resolvedStrategy, resolvedTo, isExternal, ctx, prefetchKey]);
9766
- useEffect14(() => {
10147
+ }, [configuredStrategy, resolvedTo, isExternal, ctx, prefetchKey]);
10148
+ useEffect15(() => {
9767
10149
  if (isExternal || !ctx?.store) return;
9768
- const isViewport = resolvedStrategy === "viewport";
9769
- const isRender = resolvedStrategy === "render";
9770
- if (!isViewport && !isRender) return;
10150
+ if (configuredStrategy !== "viewport" && configuredStrategy !== "render" && configuredStrategy !== "adaptive") {
10151
+ return;
10152
+ }
9771
10153
  const armPrefetch = () => {
10154
+ const strategy = resolveAdaptiveStrategy(configuredStrategy);
10155
+ const isViewport = strategy === "viewport";
10156
+ const isRender = strategy === "render";
10157
+ if (!isViewport && !isRender) return () => {
10158
+ };
9772
10159
  const element2 = internalRef.current;
9773
10160
  if (element2 && (isHashOnlyNavigation(element2) || isPrefetchScopeDisabled(element2))) {
9774
10161
  return () => {
@@ -9813,24 +10200,24 @@ var Link = forwardRef(function Link2({
9813
10200
  };
9814
10201
  };
9815
10202
  let disarmPrefetch = armPrefetch();
9816
- const element = internalRef.current;
9817
- const unsubscribeScope = element ? subscribeToPrefetchScopeChange(element, () => {
10203
+ const rearm = () => {
9818
10204
  disarmPrefetch();
9819
10205
  disarmPrefetch = armPrefetch();
9820
- }) : void 0;
10206
+ };
10207
+ const element = internalRef.current;
10208
+ const unsubscribeScope = element ? subscribeToPrefetchScopeChange(element, rearm) : void 0;
9821
10209
  const unsubscribeLocation = subscribeToLocationChange(
9822
10210
  ctx.eventController,
9823
- () => {
9824
- disarmPrefetch();
9825
- disarmPrefetch = armPrefetch();
9826
- }
10211
+ rearm
9827
10212
  );
10213
+ const unsubscribeAdaptive = configuredStrategy === "adaptive" ? subscribeToAdaptiveStrategyChange(rearm) : void 0;
9828
10214
  return () => {
9829
10215
  unsubscribeScope?.();
9830
10216
  unsubscribeLocation();
10217
+ unsubscribeAdaptive?.();
9831
10218
  disarmPrefetch();
9832
10219
  };
9833
- }, [resolvedStrategy, resolvedTo, isExternal, ctx, prefetchKey]);
10220
+ }, [configuredStrategy, resolvedTo, isExternal, ctx, prefetchKey]);
9834
10221
  return /* @__PURE__ */ jsx8(
9835
10222
  "a",
9836
10223
  {
@@ -9878,7 +10265,7 @@ var Breadcrumbs = createHandle(
9878
10265
  );
9879
10266
 
9880
10267
  // src/browser/react/location-state.ts
9881
- import { useState as useState14, useEffect as useEffect15, useRef as useRef13 } from "react";
10268
+ import { useContext as useContext16, useEffect as useEffect16 } from "react";
9882
10269
 
9883
10270
  // src/href-client.ts
9884
10271
  function href(path6, mount) {
@@ -9947,7 +10334,7 @@ import { AsyncLocalStorage as AsyncLocalStorage5 } from "node:async_hooks";
9947
10334
  init_cookie_name();
9948
10335
 
9949
10336
  // src/router/segment-resolution/mask-nested.ts
9950
- import { cloneElement, isValidElement as isValidElement2 } from "react";
10337
+ import { cloneElement, isValidElement as isValidElement3 } from "react";
9951
10338
 
9952
10339
  // src/server/handle-store.ts
9953
10340
  var LOADER_SLOT = Object.freeze({ loader: true });
@@ -10010,6 +10397,7 @@ var THEME_READ = {
10010
10397
 
10011
10398
  // src/cache/invalidation-order.ts
10012
10399
  var order = globalThis[/* @__PURE__ */ Symbol.for("rangojs-router:tag-invalidation-order")] ??= { seq: 0, tags: /* @__PURE__ */ new Map(), forgotten: 0 };
10400
+ var responseStarts = globalThis[/* @__PURE__ */ Symbol.for("rangojs-router:response-execution-start")] ??= /* @__PURE__ */ new WeakMap();
10013
10401
 
10014
10402
  // src/server/request-context.ts
10015
10403
  var requestContextStorage = new AsyncLocalStorage5();
@@ -10019,17 +10407,17 @@ init_redirect_origin();
10019
10407
 
10020
10408
  // src/client-urls/client-root.tsx
10021
10409
  import {
10022
- createElement as createElement4,
10410
+ createElement as createElement5,
10023
10411
  Fragment as Fragment4,
10024
10412
  Suspense as Suspense3,
10025
10413
  useDeferredValue,
10026
- useEffect as useEffect16,
10027
- useMemo as useMemo9,
10028
- useState as useState15
10414
+ useEffect as useEffect17,
10415
+ useMemo as useMemo10,
10416
+ useState as useState14
10029
10417
  } from "react";
10030
10418
 
10031
10419
  // src/client-urls/navigation.ts
10032
- import { startTransition as startTransition6 } from "react";
10420
+ import { startTransition as startTransition7 } from "react";
10033
10421
 
10034
10422
  // src/client-urls/client-root.tsx
10035
10423
  import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
@@ -10211,7 +10599,7 @@ var debug10 = createRangoDebugger(NS.discovery);
10211
10599
  function hasDefaultExport(code) {
10212
10600
  let program;
10213
10601
  try {
10214
- program = parseAst6(code, { lang: "tsx" });
10602
+ program = parseAst7(code, { lang: "tsx" });
10215
10603
  } catch {
10216
10604
  return false;
10217
10605
  }
@@ -10239,7 +10627,7 @@ function recordClientUrlsModule(state, code, id) {
10239
10627
  }
10240
10628
  const source = normalizeSource(state, id);
10241
10629
  const projectRoot = normalizePath5(resolve8(state.projectRoot));
10242
- const relativeId = normalizePath5(relative4(projectRoot, source));
10630
+ const relativeId = normalizePath5(relative5(projectRoot, source));
10243
10631
  const outsideProject = relativeId === ".." || relativeId.startsWith("../") || isAbsolute3(relativeId);
10244
10632
  const devReferenceKey = outsideProject ? `/@fs${source}` : `/${relativeId}`;
10245
10633
  const sourceByReferenceId = state.clientUrlSourceByReferenceId ??= /* @__PURE__ */ new Map();
@@ -10561,11 +10949,23 @@ async function discoverRouters(state, rscEnv, ssrEnv) {
10561
10949
  "inner: trie build done (%sms)",
10562
10950
  (performance.now() - trieStart).toFixed(1)
10563
10951
  );
10952
+ const newOnDemandHandlerIds = /* @__PURE__ */ new Map();
10953
+ for (const { manifest } of allManifests) {
10954
+ if (!manifest.onDemandRoutes) continue;
10955
+ const defs = manifest._prerenderDefs || {};
10956
+ for (const routeName of manifest.onDemandRoutes) {
10957
+ const handlerId = defs[routeName]?.$$id;
10958
+ if (typeof handlerId === "string") {
10959
+ newOnDemandHandlerIds.set(handlerId, routeName);
10960
+ }
10961
+ }
10962
+ }
10564
10963
  state.mergedRouteManifest = newMergedRouteManifest;
10565
10964
  state.perRouterManifests = newPerRouterManifests;
10566
10965
  state.perRouterManifestDataMap = newPerRouterManifestDataMap;
10567
10966
  state.perRouterPrecomputedMap = newPerRouterPrecomputedMap;
10568
10967
  state.perRouterTrieMap = newPerRouterTrieMap;
10968
+ state.onDemandHandlerIds = newOnDemandHandlerIds;
10569
10969
  const perRouterSetters = [
10570
10970
  [newPerRouterManifestDataMap, "setRouterManifest"],
10571
10971
  [newPerRouterTrieMap, "setRouterTrie"],
@@ -10574,7 +10974,7 @@ async function discoverRouters(state, rscEnv, ssrEnv) {
10574
10974
  for (const [map2, fn] of perRouterSetters) {
10575
10975
  const setter = serverMod[fn];
10576
10976
  if (typeof setter !== "function") continue;
10577
- for (const [routerId, value] of map2) setter(routerId, value);
10977
+ for (const [routerId2, value] of map2) setter(routerId2, value);
10578
10978
  }
10579
10979
  await expandPrerenderRoutes(state, rscEnv, registry, allManifests);
10580
10980
  await renderStaticHandlers(state, rscEnv, registry);
@@ -10582,20 +10982,337 @@ async function discoverRouters(state, rscEnv, ssrEnv) {
10582
10982
  }
10583
10983
 
10584
10984
  // src/vite/discovery/shell-prerender-phase.ts
10585
- import { existsSync as existsSync8, readdirSync as readdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "node:fs";
10586
- import { join as join5, resolve as resolve9 } from "node:path";
10985
+ import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "node:fs";
10986
+ import { join as join7, resolve as resolve10 } from "node:path";
10587
10987
 
10588
10988
  // src/prerender/shell-manifest-key.ts
10589
- function buildShellManifestKey(pathname) {
10590
- return pathname;
10989
+ function buildShellManifestKey(routerId2, pathname) {
10990
+ return `${routerKeyPrefix(routerId2)}${pathname}`;
10991
+ }
10992
+
10993
+ // src/vite/discovery/router-versions-phase.ts
10994
+ import { randomUUID } from "node:crypto";
10995
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "node:fs";
10996
+ import { dirname as dirname4, join as join6, resolve as resolve9 } from "node:path";
10997
+ init_debug();
10998
+
10999
+ // src/vite/discovery/installed-packages.ts
11000
+ import { createHash as createHash6 } from "node:crypto";
11001
+ import {
11002
+ existsSync as existsSync8,
11003
+ readdirSync as readdirSync3,
11004
+ readFileSync as readFileSync6,
11005
+ realpathSync,
11006
+ statSync as statSync2
11007
+ } from "node:fs";
11008
+ import { dirname as dirname3, join as join5, sep as sep2 } from "node:path";
11009
+ function linkTarget(path6) {
11010
+ try {
11011
+ return statSync2(path6);
11012
+ } catch {
11013
+ return void 0;
11014
+ }
11015
+ }
11016
+ function digestDirectory(dir) {
11017
+ const hash = createHash6("sha256");
11018
+ const entered = /* @__PURE__ */ new Set();
11019
+ const visit = (current, prefix) => {
11020
+ const real = realpathSync(current);
11021
+ if (entered.has(real)) return;
11022
+ entered.add(real);
11023
+ const entries = readdirSync3(current, { withFileTypes: true }).sort(
11024
+ (a, b) => compareStrings(a.name, b.name)
11025
+ );
11026
+ for (const entry of entries) {
11027
+ if (entry.name === "node_modules" || entry.name === ".git") continue;
11028
+ const path6 = join5(current, entry.name);
11029
+ const name = `${prefix}${entry.name}`;
11030
+ const kind = entry.isSymbolicLink() ? linkTarget(path6) : entry;
11031
+ if (kind?.isDirectory()) {
11032
+ visit(path6, `${name}/`);
11033
+ } else if (kind?.isFile()) {
11034
+ const bytes = readFileSync6(path6);
11035
+ hash.update(`${name}\0${bytes.byteLength}\0`);
11036
+ hash.update(bytes);
11037
+ }
11038
+ }
11039
+ };
11040
+ visit(dir, "");
11041
+ return hash.digest("hex");
11042
+ }
11043
+ function findPackageDir(name, from) {
11044
+ for (let dir = from; ; ) {
11045
+ const candidate = join5(dir, "node_modules", name);
11046
+ if (existsSync8(join5(candidate, "package.json"))) {
11047
+ return realpathSync(candidate);
11048
+ }
11049
+ const parent = dirname3(dir);
11050
+ if (parent === dir) return void 0;
11051
+ dir = parent;
11052
+ }
11053
+ }
11054
+ function createInstalledPackageDescriber(projectRoot) {
11055
+ const packages = /* @__PURE__ */ new Map();
11056
+ const read = (dir) => {
11057
+ if (packages.has(dir)) return packages.get(dir);
11058
+ let manifest;
11059
+ try {
11060
+ manifest = JSON.parse(readFileSync6(join5(dir, "package.json"), "utf-8"));
11061
+ } catch {
11062
+ packages.set(dir, void 0);
11063
+ return void 0;
11064
+ }
11065
+ const linked = !dir.split(sep2).includes("node_modules");
11066
+ const found = {
11067
+ label: `${manifest.name}@${manifest.version}` + (linked ? ` files:${digestDirectory(dir)}` : ""),
11068
+ dependencies: Object.keys({
11069
+ ...manifest.dependencies,
11070
+ ...manifest.optionalDependencies,
11071
+ ...manifest.peerDependencies
11072
+ })
11073
+ };
11074
+ packages.set(dir, found);
11075
+ return found;
11076
+ };
11077
+ const located = /* @__PURE__ */ new Map();
11078
+ const locate = (name, from) => {
11079
+ const key = `${from}\0${name}`;
11080
+ if (!located.has(key)) located.set(key, findPackageDir(name, from));
11081
+ return located.get(key);
11082
+ };
11083
+ const described = /* @__PURE__ */ new Map();
11084
+ return (name) => {
11085
+ if (described.has(name)) return described.get(name);
11086
+ const labels = /* @__PURE__ */ new Set();
11087
+ const seen = /* @__PURE__ */ new Set();
11088
+ const pending = [[name, projectRoot]];
11089
+ while (pending.length > 0) {
11090
+ const [dependency, from] = pending.pop();
11091
+ const dir = locate(dependency, from);
11092
+ if (dir === void 0 || seen.has(dir)) continue;
11093
+ seen.add(dir);
11094
+ const installed = read(dir);
11095
+ if (!installed) continue;
11096
+ labels.add(installed.label);
11097
+ for (const next of installed.dependencies) pending.push([next, dir]);
11098
+ }
11099
+ const text = labels.size > 0 ? [...labels].sort().join("\n") : void 0;
11100
+ described.set(name, text);
11101
+ return text;
11102
+ };
11103
+ }
11104
+
11105
+ // src/vite/discovery/router-versions-phase.ts
11106
+ var debug12 = createRangoDebugger(NS.build);
11107
+ var ASSETS_MANIFEST_FILE = "__vite_rsc_assets_manifest.js";
11108
+ var EMPTY_SERVER_RESOURCES = /"serverResources":\s*\{\s*\}/;
11109
+ function environmentOutDir(builder, projectRoot, environment) {
11110
+ return resolve9(
11111
+ projectRoot,
11112
+ builder?.environments?.[environment]?.config?.build?.outDir ?? join6("dist", environment)
11113
+ );
11114
+ }
11115
+ var VERSIONS_REPORT_FILE = `${RANGO_BUILD_DIR}/cache-versions.json`;
11116
+ function readIfPresent(path6) {
11117
+ try {
11118
+ return readFileSync7(path6);
11119
+ } catch (err) {
11120
+ if (err?.code === "ENOENT") return void 0;
11121
+ throw err;
11122
+ }
11123
+ }
11124
+ function readServerResources(manifest) {
11125
+ if (EMPTY_SERVER_RESOURCES.test(manifest)) return /* @__PURE__ */ new Map();
11126
+ const entries = literalProperties(
11127
+ defaultExportProperties(manifest)?.find(
11128
+ (property) => property.key === "serverResources"
11129
+ )?.value
11130
+ );
11131
+ if (!entries) {
11132
+ throw new Error(
11133
+ `${ASSETS_MANIFEST_FILE} has no serverResources object literal: the installed @vitejs/plugin-rsc writes a manifest @rangojs/router cannot read. Report it with the plugin-rsc version.`
11134
+ );
11135
+ }
11136
+ return new Map(
11137
+ entries.map((entry) => [
11138
+ entry.key,
11139
+ manifest.slice(entry.value.start, entry.value.end)
11140
+ ])
11141
+ );
11142
+ }
11143
+ function clientUrlRecords(s) {
11144
+ const projectionOf = /* @__PURE__ */ new Map();
11145
+ for (const [referenceId2, projection] of s.clientUrlProjectionMap ?? []) {
11146
+ projectionOf.set(
11147
+ s.clientUrlSourceByReferenceId?.get(referenceId2) ?? referenceId2,
11148
+ JSON.stringify(projection)
11149
+ );
11150
+ }
11151
+ return [...projectionOf].map(([source, projection]) => ({
11152
+ kind: "client-urls",
11153
+ key: portableModuleId(source, s.projectRoot),
11154
+ digest: sha256(projection),
11155
+ moduleId: source
11156
+ }));
11157
+ }
11158
+ function writeVersionsReport(projectRoot, computed, sourceOf) {
11159
+ const routers = {};
11160
+ for (const detail of computed.details) {
11161
+ routers[detail.routerId] = {
11162
+ source: sourceOf.get(detail.routerId) ?? "",
11163
+ data: detail.data,
11164
+ document: detail.document,
11165
+ dataInputs: Object.fromEntries(detail.dataInputs),
11166
+ documentInputs: Object.fromEntries(detail.documentInputs)
11167
+ };
11168
+ }
11169
+ const report = {
11170
+ routers,
11171
+ ssrAndClient: {
11172
+ base: computed.ssrAndClient.base,
11173
+ ssr: Object.fromEntries(computed.ssrAndClient.ssr),
11174
+ externals: Object.fromEntries(computed.ssrAndClient.externals),
11175
+ client: computed.ssrAndClient.client
11176
+ },
11177
+ unownedFiles: computed.unownedFiles
11178
+ };
11179
+ try {
11180
+ const path6 = resolve9(projectRoot, VERSIONS_REPORT_FILE);
11181
+ mkdirSync2(dirname4(path6), { recursive: true });
11182
+ writeFileSync3(path6, JSON.stringify(report, null, 2) + "\n");
11183
+ } catch (err) {
11184
+ debug12?.("could not write %s: %s", VERSIONS_REPORT_FILE, err?.message);
11185
+ }
11186
+ }
11187
+ function runRouterVersionsPhase(s, builder) {
11188
+ if (!s.isBuildMode) return void 0;
11189
+ const { serverBuildGraph: server, clientBuildGraph: client } = s;
11190
+ const ssr = s.ssrBundle;
11191
+ const rscVersionFiles = s.versionModuleFiles.get("rsc") ?? [];
11192
+ if (!server || !client || !ssr || rscVersionFiles.length === 0) {
11193
+ const missing = [
11194
+ !server && "the RSC bundle",
11195
+ !client && "the client bundle",
11196
+ !ssr && "the SSR bundle",
11197
+ rscVersionFiles.length === 0 && "the RSC chunk that holds the version module"
11198
+ ].filter(Boolean);
11199
+ throw new Error(
11200
+ `[rango] The build did not record ${missing.join(", ")}, so the cache versions cannot be computed. This is a bug in @rangojs/router; please report it.`
11201
+ );
11202
+ }
11203
+ const start = performance.now();
11204
+ const rscOutDir = environmentOutDir(builder, s.projectRoot, "rsc");
11205
+ const ssrOutDir = environmentOutDir(builder, s.projectRoot, "ssr");
11206
+ let computed;
11207
+ try {
11208
+ const assetsManifest = readIfPresent(join6(rscOutDir, ASSETS_MANIFEST_FILE));
11209
+ if (assetsManifest === void 0) {
11210
+ throw new Error(
11211
+ `${ASSETS_MANIFEST_FILE} is not in the RSC output directory, so the stylesheets server components link cannot be read`
11212
+ );
11213
+ }
11214
+ computed = computeRouterVersions({
11215
+ projectRoot: s.projectRoot,
11216
+ server,
11217
+ client,
11218
+ // plugin-rsc writes its manifest next to the SSR bundle, not into it.
11219
+ ssr: {
11220
+ chunks: ssr.chunks,
11221
+ assets: [
11222
+ ...ssr.assets.filter(
11223
+ (asset) => asset.fileName !== ASSETS_MANIFEST_FILE
11224
+ ),
11225
+ { fileName: ASSETS_MANIFEST_FILE, name: ASSETS_MANIFEST_FILE }
11226
+ ]
11227
+ },
11228
+ routers: s.perRouterManifests.map((entry) => ({
11229
+ id: entry.id,
11230
+ moduleId: entry.sourceFile
11231
+ })),
11232
+ buildData: [...s.buildData, ...clientUrlRecords(s)],
11233
+ readServerFile: (fileName) => readIfPresent(join6(rscOutDir, fileName)),
11234
+ readSsrFile: (fileName) => readIfPresent(join6(ssrOutDir, fileName)),
11235
+ base: builder?.config?.base ?? "/",
11236
+ serverResources: readServerResources(assetsManifest.toString("utf-8")),
11237
+ externals: {
11238
+ describePackage: createInstalledPackageDescriber(s.projectRoot),
11239
+ readFile: readIfPresent,
11240
+ buildId: randomUUID()
11241
+ }
11242
+ });
11243
+ } catch (err) {
11244
+ throw new Error(
11245
+ `[rango] Could not compute the cache versions: ${err?.message ?? err}`,
11246
+ { cause: err }
11247
+ );
11248
+ }
11249
+ const { table, details } = computed;
11250
+ for (const [environment, fileNames] of s.versionModuleFiles) {
11251
+ const outDir = environmentOutDir(builder, s.projectRoot, environment);
11252
+ let filled = 0;
11253
+ for (const fileName of fileNames) {
11254
+ const path6 = join6(outDir, fileName);
11255
+ const source = readIfPresent(path6);
11256
+ const next = source === void 0 ? void 0 : fillRouterVersions(source.toString("utf-8"), table);
11257
+ if (next === void 0) continue;
11258
+ writeFileSync3(path6, next);
11259
+ filled++;
11260
+ }
11261
+ if (environment === "rsc" && filled === 0) {
11262
+ throw new Error(
11263
+ `[rango] Could not write the cache versions into the server build: no placeholder in ${fileNames.join(", ")} under ${outDir}. The build would fail to start. This is a bug in @rangojs/router; please report it.`
11264
+ );
11265
+ }
11266
+ }
11267
+ const elapsed = (performance.now() - start).toFixed(1);
11268
+ const sourceOf = new Map(
11269
+ s.perRouterManifests.map((entry) => [
11270
+ entry.id,
11271
+ entry.sourceFile ? portableModuleId(entry.sourceFile, s.projectRoot) : ""
11272
+ ])
11273
+ );
11274
+ const detailOf = new Map(details.map((detail) => [detail.routerId, detail]));
11275
+ const wholeBuild = detailOf.get(DEFAULT_ROUTER_VERSIONS_KEY);
11276
+ let keyed = 0;
11277
+ if (s.perRouterManifests.length > 0) {
11278
+ console.log(
11279
+ `[rango] Cache versions for ${s.perRouterManifests.length} router(s), data / document (${elapsed}ms):`
11280
+ );
11281
+ for (const { id } of s.perRouterManifests) {
11282
+ const own = detailOf.get(id);
11283
+ const detail = own ?? wholeBuild;
11284
+ if (detail.usesEncryptionKey) keyed++;
11285
+ console.log(
11286
+ `[rango] ${detail.data} / ${detail.document} ${sourceOf.get(id) || id}${own ? "" : " (whole build)"}`
11287
+ );
11288
+ }
11289
+ }
11290
+ debug12?.(
11291
+ "router versions: %d server file(s), %sms, table=%o",
11292
+ server.chunks.length + server.assets.length,
11293
+ elapsed,
11294
+ table
11295
+ );
11296
+ writeVersionsReport(s.projectRoot, computed, sourceOf);
11297
+ if (computed.undetermined.length > 0) {
11298
+ console.log(
11299
+ `[rango] ${computed.undetermined.length} import(s) the server build leaves external could not be read at build time (${computed.undetermined.join(", ")}): a package that is not installed where the build can find it, a path that does not resolve, or a URL. What they resolve to at run time is unknown, so the routers importing them get a new cache version on every build.`
11300
+ );
11301
+ }
11302
+ if (keyed > 0 && !isEncryptionKeyStable()) {
11303
+ console.log(
11304
+ `[rango] No stable encryption key: ${keyed} router(s) encrypt server-action arguments with a key generated for this build, so their cache version changes on every build and each deploy clears their cache. To keep it, set rango({ encryptionKey: process.env.RANGO_ENCRYPTION_KEY }) with a base64 32-byte key (openssl rand -base64 32).`
11305
+ );
11306
+ }
11307
+ return table;
10591
11308
  }
10592
11309
 
10593
11310
  // src/vite/discovery/shell-prerender-phase.ts
10594
11311
  init_debug();
10595
- var debug12 = createRangoDebugger(NS.prerender);
10596
- async function runShellPrerenderPhase(s, builder) {
11312
+ var debug13 = createRangoDebugger(NS.prerender);
11313
+ async function runShellPrerenderPhase(s, builder, versionsTable) {
10597
11314
  const tempServer = s.shellPhaseTempServer;
10598
- if (!s.isBuildMode || !s.shellCandidates?.length || !tempServer) return;
11315
+ if (!versionsTable || !s.shellCandidates?.length || !tempServer) return;
10599
11316
  const candidates = s.shellCandidates;
10600
11317
  const startTotal = performance.now();
10601
11318
  console.log(`[rango] Shell-prerendering ${candidates.length} URL(s)...`);
@@ -10611,16 +11328,6 @@ async function runShellPrerenderPhase(s, builder) {
10611
11328
  );
10612
11329
  return;
10613
11330
  }
10614
- const versionPlugin = (builder?.config?.plugins ?? []).find(
10615
- (p) => p?.name === "@rangojs/router:version"
10616
- );
10617
- const buildVersion = versionPlugin?.api?.getBuildVersion?.();
10618
- if (!buildVersion) {
10619
- console.warn(
10620
- "[rango] shell prerender: main build version unavailable; skipping \u2014 routes keep runtime shell capture."
10621
- );
10622
- return;
10623
- }
10624
11331
  const payloads = s.prerenderPayloadValues ?? /* @__PURE__ */ new Map();
10625
11332
  const fetchedKeys = /* @__PURE__ */ new Set();
10626
11333
  globalThis.__loadPrerenderManifestModule = async () => ({
@@ -10638,9 +11345,9 @@ async function runShellPrerenderPhase(s, builder) {
10638
11345
  const htmlStream = await ssrRunner.import(
10639
11346
  "@rangojs/router/internal/deps/html-stream-server"
10640
11347
  );
10641
- const clientOutDir = builder?.environments?.client?.config?.build?.outDir ?? resolve9(s.projectRoot, "dist/client");
10642
- const clientAssetsDir = join5(clientOutDir, "assets");
10643
- const entryFile = existsSync8(clientAssetsDir) ? readdirSync3(clientAssetsDir).find((f) => /^index-.*\.js$/.test(f)) : void 0;
11348
+ const clientOutDir = environmentOutDir(builder, s.projectRoot, "client");
11349
+ const clientAssetsDir = join7(clientOutDir, "assets");
11350
+ const entryFile = existsSync9(clientAssetsDir) ? readdirSync4(clientAssetsDir).find((f) => /^index-.*\.js$/.test(f)) : void 0;
10644
11351
  if (!entryFile) {
10645
11352
  console.warn(
10646
11353
  `[rango] shell prerender: built client entry not found under ${clientAssetsDir}; skipping \u2014 routes keep runtime shell capture.`
@@ -10650,7 +11357,7 @@ async function runShellPrerenderPhase(s, builder) {
10650
11357
  const base = builder?.config?.base ?? "/";
10651
11358
  const normalizedBase = base.endsWith("/") ? base : `${base}/`;
10652
11359
  const bootstrapContent = `import("${normalizedBase}assets/${entryFile}")`;
10653
- debug12?.("shell prerender bootstrap: %s", bootstrapContent);
11360
+ debug13?.("shell prerender bootstrap: %s", bootstrapContent);
10654
11361
  const minimalPlugin = (tempServer.config?.plugins ?? []).find(
10655
11362
  (p) => p?.name === "rsc:minimal"
10656
11363
  );
@@ -10721,8 +11428,13 @@ async function runShellPrerenderPhase(s, builder) {
10721
11428
  const mainKey = `${cand.routeName}/${cand.paramHash}`;
10722
11429
  let handled = false;
10723
11430
  const mismatches = [];
10724
- for (const [, routerInstance] of registry) {
11431
+ for (const [routerId2, routerInstance] of registry) {
10725
11432
  if (typeof routerInstance.match !== "function") continue;
11433
+ const versions = resolveVersionsFrom(
11434
+ versionsTable,
11435
+ routerId2,
11436
+ routerInstance.version
11437
+ );
10726
11438
  try {
10727
11439
  const res = await captureMod.captureShellForBuild({
10728
11440
  router: routerInstance,
@@ -10735,9 +11447,9 @@ async function runShellPrerenderPhase(s, builder) {
10735
11447
  maxSnapshotBytes: policy.maxSnapshotBytes,
10736
11448
  captureTimeout: policy.captureTimeout,
10737
11449
  buildEnv: s.resolvedBuildEnv,
10738
- buildVersion,
11450
+ versions,
10739
11451
  captureShellHTML,
10740
- debug: !!debug12
11452
+ debug: !!debug13
10741
11453
  });
10742
11454
  if (res.outcome === "route-mismatch") {
10743
11455
  mismatches.push(res.matchedRouteName ?? "(no match)");
@@ -10765,7 +11477,7 @@ async function runShellPrerenderPhase(s, builder) {
10765
11477
  tags: res.tags
10766
11478
  };
10767
11479
  staged.push({
10768
- key: buildShellManifestKey(cand.urlPath),
11480
+ key: buildShellManifestKey(routerId2, cand.urlPath),
10769
11481
  value: JSON.stringify(value)
10770
11482
  });
10771
11483
  console.log(
@@ -10790,8 +11502,11 @@ async function runShellPrerenderPhase(s, builder) {
10790
11502
  }
10791
11503
  }
10792
11504
  if (staged.length > 0) {
10793
- const rscOutDir = builder?.environments?.rsc?.config?.build?.outDir ?? resolve9(s.projectRoot, "dist/rsc");
10794
- writeShellManifest(s, rscOutDir, staged);
11505
+ writeShellManifest(
11506
+ s,
11507
+ environmentOutDir(builder, s.projectRoot, "rsc"),
11508
+ staged
11509
+ );
10795
11510
  }
10796
11511
  const totalElapsed = (performance.now() - startTotal).toFixed(0);
10797
11512
  const parts = [`${staged.length} done`];
@@ -10810,14 +11525,14 @@ async function runShellPrerenderPhase(s, builder) {
10810
11525
  }
10811
11526
  }
10812
11527
  function writeShellManifest(s, rscOutDir, staged) {
10813
- const rscEntryPath = resolve9(rscOutDir, s.rscEntryFileName ?? "index.js");
10814
- if (!existsSync8(rscEntryPath)) {
11528
+ const rscEntryPath = resolve10(rscOutDir, s.rscEntryFileName ?? "index.js");
11529
+ if (!existsSync9(rscEntryPath)) {
10815
11530
  console.warn(
10816
11531
  `[rango] shell prerender: RSC entry not found at ${rscEntryPath}; entries not persisted \u2014 routes keep runtime capture.`
10817
11532
  );
10818
11533
  return;
10819
11534
  }
10820
- const assetsDir = resolve9(rscOutDir, "assets");
11535
+ const assetsDir = resolve10(rscOutDir, "assets");
10821
11536
  let totalBytes = 0;
10822
11537
  const manifestMap = {};
10823
11538
  const countedFiles = /* @__PURE__ */ new Set();
@@ -10835,13 +11550,13 @@ function writeShellManifest(s, rscOutDir, staged) {
10835
11550
  `export default m;`,
10836
11551
  ""
10837
11552
  ].join("\n");
10838
- writeFileSync3(resolve9(rscOutDir, "__shell-manifest.js"), manifestCode);
11553
+ writeFileSync4(resolve10(rscOutDir, "__shell-manifest.js"), manifestCode);
10839
11554
  totalBytes += Buffer.byteLength(manifestCode);
10840
- const rscCode = readFileSync6(rscEntryPath, "utf-8");
11555
+ const rscCode = readFileSync8(rscEntryPath, "utf-8");
10841
11556
  if (!rscCode.includes("__shell-manifest.js")) {
10842
11557
  const injection = `globalThis.__loadShellManifestModule = () => import("./__shell-manifest.js");
10843
11558
  `;
10844
- writeFileSync3(rscEntryPath, injection + rscCode);
11559
+ writeFileSync4(rscEntryPath, injection + rscCode);
10845
11560
  }
10846
11561
  const totalKB = (totalBytes / 1024).toFixed(1);
10847
11562
  console.log(
@@ -10887,8 +11602,8 @@ function createDevPrerenderCache(maxEntriesPerRouter = MAX_ENTRIES_PER_ROUTER) {
10887
11602
  }
10888
11603
 
10889
11604
  // src/vite/discovery/route-types-writer.ts
10890
- import { dirname as dirname3, join as join6, resolve as resolve10 } from "node:path";
10891
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync9, unlinkSync as unlinkSync2 } from "node:fs";
11605
+ import { dirname as dirname5, join as join8, resolve as resolve11 } from "node:path";
11606
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync10, unlinkSync as unlinkSync2 } from "node:fs";
10892
11607
  function filterUserNamedRoutes(manifest) {
10893
11608
  const filtered = {};
10894
11609
  for (const [name, pattern] of Object.entries(manifest)) {
@@ -10899,10 +11614,10 @@ function filterUserNamedRoutes(manifest) {
10899
11614
  return filtered;
10900
11615
  }
10901
11616
  function writeGenFileIfChanged(state, outPath, source, opts) {
10902
- const existing = existsSync9(outPath) ? readFileSync7(outPath, "utf-8") : null;
11617
+ const existing = existsSync10(outPath) ? readFileSync9(outPath, "utf-8") : null;
10903
11618
  if (existing === source) return;
10904
11619
  markSelfGenWrite(state, outPath, source);
10905
- writeFileSync4(outPath, source);
11620
+ writeFileSync5(outPath, source);
10906
11621
  if (opts?.log) console.log(`[rango] Generated route types -> ${outPath}`);
10907
11622
  }
10908
11623
  function writeCombinedRouteTypesWithTracking(state, opts) {
@@ -10916,11 +11631,11 @@ function writeCombinedRouteTypesWithTracking(state, opts) {
10916
11631
  function writeRouteTypesFiles(state) {
10917
11632
  if (state.perRouterManifests.length === 0) return;
10918
11633
  try {
10919
- const entryDir = dirname3(
10920
- resolve10(state.projectRoot, state.resolvedEntryPath)
11634
+ const entryDir = dirname5(
11635
+ resolve11(state.projectRoot, state.resolvedEntryPath)
10921
11636
  );
10922
- const oldCombinedPath = join6(entryDir, "named-routes.gen.ts");
10923
- if (existsSync9(oldCombinedPath)) {
11637
+ const oldCombinedPath = join8(entryDir, "named-routes.gen.ts");
11638
+ if (existsSync10(oldCombinedPath)) {
10924
11639
  unlinkSync2(oldCombinedPath);
10925
11640
  console.log(
10926
11641
  `[rango] Removed stale combined route types: ${oldCombinedPath}`
@@ -11001,8 +11716,8 @@ function supplementGenFilesWithRuntimeRoutes(state) {
11001
11716
  }
11002
11717
 
11003
11718
  // src/vite/discovery/virtual-module-codegen.ts
11004
- import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync5 } from "node:fs";
11005
- import { dirname as dirname4, basename, join as join7 } from "node:path";
11719
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync6 } from "node:fs";
11720
+ import { dirname as dirname6, basename, join as join9 } from "node:path";
11006
11721
  var MANIFEST_EXTERNALIZE_THRESHOLD = 512 * 1024;
11007
11722
  var TRANSITION_WHEN_VALIDATION = `enableTransitionWhenValidation();`;
11008
11723
  function devDiscoveryBootstrap(state) {
@@ -11021,12 +11736,12 @@ function generateRoutesManifestModule(state) {
11021
11736
  let varIdx = 0;
11022
11737
  for (const entry of state.perRouterManifests) {
11023
11738
  if (entry.sourceFile) {
11024
- const routerDir = dirname4(entry.sourceFile);
11739
+ const routerDir = dirname6(entry.sourceFile);
11025
11740
  const routerBasename = basename(entry.sourceFile).replace(
11026
11741
  /\.(tsx?|jsx?)$/,
11027
11742
  ""
11028
11743
  );
11029
- const genPath = join7(
11744
+ const genPath = join9(
11030
11745
  routerDir,
11031
11746
  `${routerBasename}.named-routes.gen.js`
11032
11747
  ).replaceAll("\\", "/");
@@ -11060,7 +11775,10 @@ function generateRoutesManifestModule(state) {
11060
11775
  ];
11061
11776
  if (hasClientUrlModules) {
11062
11777
  lines2.push(`clearClientUrlProjections();`);
11063
- for (const [referenceId2, projection] of state.clientUrlProjectionMap ?? []) {
11778
+ const projections = [...state.clientUrlProjectionMap ?? []].sort(
11779
+ ([a], [b]) => compareStrings(a, b)
11780
+ );
11781
+ for (const [referenceId2, projection] of projections) {
11064
11782
  lines2.push(
11065
11783
  `setClientUrlProjection(${JSON.stringify(referenceId2)}, ${jsonParseExpression(projection)});`
11066
11784
  );
@@ -11092,9 +11810,9 @@ function generateRoutesManifestModule(state) {
11092
11810
  );
11093
11811
  }
11094
11812
  if (state.isBuildMode) {
11095
- for (const routerId of state.perRouterManifestDataMap.keys()) {
11813
+ for (const routerId2 of state.perRouterManifestDataMap.keys()) {
11096
11814
  lines2.push(
11097
- `registerRouterManifestLoader(${JSON.stringify(routerId)}, () => import(${JSON.stringify(VIRTUAL_ROUTES_MANIFEST_ID + "/" + routerId)}));`
11815
+ `registerRouterManifestLoader(${JSON.stringify(routerId2)}, () => import(${JSON.stringify(VIRTUAL_ROUTES_MANIFEST_ID + "/" + routerId2)}));`
11098
11816
  );
11099
11817
  }
11100
11818
  }
@@ -11131,17 +11849,17 @@ function shortHash(input) {
11131
11849
  }
11132
11850
  return (h >>> 0).toString(36);
11133
11851
  }
11134
- function generatePerRouterModule(state, routerId) {
11852
+ function generatePerRouterModule(state, routerId2) {
11135
11853
  const sourceFile = state.perRouterManifests.find(
11136
- (e) => e.id === routerId
11854
+ (e) => e.id === routerId2
11137
11855
  )?.sourceFile;
11138
- const trie = state.perRouterTrieMap.get(routerId);
11139
- const entries = state.perRouterPrecomputedMap.get(routerId);
11856
+ const trie = state.perRouterTrieMap.get(routerId2);
11857
+ const entries = state.perRouterPrecomputedMap.get(routerId2);
11140
11858
  const lines = [];
11141
11859
  if (sourceFile) {
11142
- const routerDir = dirname4(sourceFile);
11860
+ const routerDir = dirname6(sourceFile);
11143
11861
  const routerBasename = basename(sourceFile).replace(/\.(tsx?|jsx?)$/, "");
11144
- const genPath = join7(
11862
+ const genPath = join9(
11145
11863
  routerDir,
11146
11864
  `${routerBasename}.named-routes.gen.js`
11147
11865
  ).replaceAll("\\", "/");
@@ -11155,11 +11873,11 @@ function generatePerRouterModule(state, routerId) {
11155
11873
  const payloadJson = hasTrie || hasEntries ? JSON.stringify(payload) : "";
11156
11874
  const useTextModule = state.isBuildMode && preset === "cloudflare" && override !== "0" && (hasTrie || hasEntries) && (override === "1" || payloadJson.length >= MANIFEST_EXTERNALIZE_THRESHOLD);
11157
11875
  if (useTextModule) {
11158
- const dir = join7(state.projectRoot, "node_modules", ".rango");
11159
- mkdirSync2(dir, { recursive: true });
11160
- const safeId = `${routerId.replace(/[^a-zA-Z0-9_-]/g, "_")}-${shortHash(routerId)}`;
11161
- const filePath = join7(dir, `manifest-${safeId}.txt`).replaceAll("\\", "/");
11162
- writeFileSync5(filePath, payloadJson);
11876
+ const dir = join9(state.projectRoot, "node_modules", ".rango");
11877
+ mkdirSync3(dir, { recursive: true });
11878
+ const safeId = `${routerId2.replace(/[^a-zA-Z0-9_-]/g, "_")}-${shortHash(routerId2)}`;
11879
+ const filePath = join9(dir, `manifest-${safeId}.txt`).replaceAll("\\", "/");
11880
+ writeFileSync6(filePath, payloadJson);
11163
11881
  lines.push(`import __manifestJson from ${JSON.stringify(filePath)};`);
11164
11882
  lines.push(`const __manifestData = JSON.parse(__manifestJson);`);
11165
11883
  emitManifestExports(lines, hasTrie, hasEntries);
@@ -11188,13 +11906,13 @@ function emitManifestExports(lines, hasTrie, hasEntries) {
11188
11906
  }
11189
11907
 
11190
11908
  // src/vite/discovery/bundle-postprocess.ts
11191
- import { resolve as resolve11 } from "node:path";
11192
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync10 } from "node:fs";
11909
+ import { resolve as resolve12 } from "node:path";
11910
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, existsSync as existsSync11 } from "node:fs";
11193
11911
  function postprocessBundle(state) {
11194
11912
  const hasPrerenderData = state.prerenderManifestEntries && Object.keys(state.prerenderManifestEntries).length > 0;
11195
11913
  const hasStaticData = state.staticManifestEntries && Object.keys(state.staticManifestEntries).length > 0;
11196
11914
  if (!hasPrerenderData && !hasStaticData) return;
11197
- const rscEntryPath = resolve11(
11915
+ const rscEntryPath = resolve12(
11198
11916
  state.projectRoot,
11199
11917
  "dist/rsc",
11200
11918
  state.rscEntryFileName ?? "index.js"
@@ -11215,9 +11933,9 @@ function postprocessBundle(state) {
11215
11933
  ];
11216
11934
  for (const target of evictionTargets) {
11217
11935
  for (const info of target.infos) {
11218
- const chunkPath = resolve11(state.projectRoot, "dist/rsc", info.fileName);
11936
+ const chunkPath = resolve12(state.projectRoot, "dist/rsc", info.fileName);
11219
11937
  try {
11220
- const code = readFileSync8(chunkPath, "utf-8");
11938
+ const code = readFileSync10(chunkPath, "utf-8");
11221
11939
  const result = evictHandlerCode(
11222
11940
  code,
11223
11941
  info.exports,
@@ -11225,7 +11943,7 @@ function postprocessBundle(state) {
11225
11943
  target.brand
11226
11944
  );
11227
11945
  if (result) {
11228
- writeFileSync6(chunkPath, result.code);
11946
+ writeFileSync7(chunkPath, result.code);
11229
11947
  const savedKB = (result.savedBytes / 1024).toFixed(1);
11230
11948
  console.log(
11231
11949
  `[rango] Evicted ${target.label} (${savedKB} KB saved): ${info.fileName}`
@@ -11240,8 +11958,8 @@ function postprocessBundle(state) {
11240
11958
  }
11241
11959
  state.handlerChunkInfoMap.clear();
11242
11960
  state.staticHandlerChunkInfoMap.clear();
11243
- if (hasPrerenderData && existsSync10(rscEntryPath)) {
11244
- const rscCode = readFileSync8(rscEntryPath, "utf-8");
11961
+ if (hasPrerenderData && existsSync11(rscEntryPath)) {
11962
+ const rscCode = readFileSync10(rscEntryPath, "utf-8");
11245
11963
  if (!rscCode.includes("__prerender-manifest.js")) {
11246
11964
  try {
11247
11965
  let totalBytes = copyStagedBuildAssets(
@@ -11260,15 +11978,15 @@ function postprocessBundle(state) {
11260
11978
  `export default m;`,
11261
11979
  ""
11262
11980
  ].join("\n");
11263
- const manifestPath = resolve11(
11981
+ const manifestPath = resolve12(
11264
11982
  state.projectRoot,
11265
11983
  "dist/rsc/__prerender-manifest.js"
11266
11984
  );
11267
- writeFileSync6(manifestPath, manifestCode);
11985
+ writeFileSync7(manifestPath, manifestCode);
11268
11986
  totalBytes += Buffer.byteLength(manifestCode);
11269
11987
  const injection = `globalThis.__loadPrerenderManifestModule = () => import("./__prerender-manifest.js");
11270
11988
  `;
11271
- writeFileSync6(rscEntryPath, injection + rscCode);
11989
+ writeFileSync7(rscEntryPath, injection + rscCode);
11272
11990
  const totalKB = (totalBytes / 1024).toFixed(1);
11273
11991
  console.log(
11274
11992
  `[rango] Wrote prerender assets (${totalKB} KB total, ${Object.keys(state.prerenderManifestEntries).length} entries)`
@@ -11280,8 +11998,8 @@ function postprocessBundle(state) {
11280
11998
  }
11281
11999
  }
11282
12000
  }
11283
- if (hasStaticData && existsSync10(rscEntryPath)) {
11284
- const rscCode = readFileSync8(rscEntryPath, "utf-8");
12001
+ if (hasStaticData && existsSync11(rscEntryPath)) {
12002
+ const rscCode = readFileSync10(rscEntryPath, "utf-8");
11285
12003
  if (!rscCode.includes("__static-manifest.js")) {
11286
12004
  try {
11287
12005
  const manifestEntries = [];
@@ -11298,15 +12016,15 @@ function postprocessBundle(state) {
11298
12016
  }
11299
12017
  const manifestCode = `const m={${manifestEntries.join(",")}};globalThis.__STATIC_MANIFEST=m;export default m;
11300
12018
  `;
11301
- const manifestPath = resolve11(
12019
+ const manifestPath = resolve12(
11302
12020
  state.projectRoot,
11303
12021
  "dist/rsc/__static-manifest.js"
11304
12022
  );
11305
- writeFileSync6(manifestPath, manifestCode);
12023
+ writeFileSync7(manifestPath, manifestCode);
11306
12024
  totalBytes += Buffer.byteLength(manifestCode);
11307
12025
  const injection = `globalThis.__loadStaticManifestModule = () => import("./__static-manifest.js");
11308
12026
  `;
11309
- writeFileSync6(rscEntryPath, injection + rscCode);
12027
+ writeFileSync7(rscEntryPath, injection + rscCode);
11310
12028
  const totalKB = (totalBytes / 1024).toFixed(1);
11311
12029
  console.log(
11312
12030
  `[rango] Wrote static assets (${totalKB} KB total, ${Object.keys(state.staticManifestEntries).length} entries)`
@@ -11321,7 +12039,7 @@ function postprocessBundle(state) {
11321
12039
  }
11322
12040
 
11323
12041
  // src/vite/discovery/gate-state.ts
11324
- function createDiscoveryGate(s, debug13) {
12042
+ function createDiscoveryGate(s, debug14) {
11325
12043
  let gatePending = false;
11326
12044
  let gateResolver = () => {
11327
12045
  };
@@ -11330,15 +12048,15 @@ function createDiscoveryGate(s, debug13) {
11330
12048
  let pendingEvents = false;
11331
12049
  const beginGate = () => {
11332
12050
  if (gatePending) return;
11333
- s.discoveryDone = new Promise((resolve14) => {
11334
- gateResolver = resolve14;
12051
+ s.discoveryDone = new Promise((resolve15) => {
12052
+ gateResolver = resolve15;
11335
12053
  });
11336
12054
  gatePending = true;
11337
12055
  };
11338
12056
  const resolveGate = () => {
11339
12057
  if (!gatePending) return;
11340
12058
  if (inProgress || queued2 || pendingEvents) {
11341
- debug13?.(
12059
+ debug14?.(
11342
12060
  "hmr: resolveGate deferred \u2014 work in flight (inProgress=%s queued=%s pendingEvents=%s)",
11343
12061
  inProgress,
11344
12062
  queued2,
@@ -11347,7 +12065,7 @@ function createDiscoveryGate(s, debug13) {
11347
12065
  return;
11348
12066
  }
11349
12067
  gatePending = false;
11350
- debug13?.("hmr: discoveryDone resolved");
12068
+ debug14?.("hmr: discoveryDone resolved");
11351
12069
  gateResolver();
11352
12070
  };
11353
12071
  const noteRouteEvent = () => {
@@ -11357,7 +12075,7 @@ function createDiscoveryGate(s, debug13) {
11357
12075
  const runRefreshCycle = async (work) => {
11358
12076
  if (inProgress) {
11359
12077
  queued2 = true;
11360
- debug13?.("hmr: rediscovery in flight \u2014 queued for a follow-up cycle");
12078
+ debug14?.("hmr: rediscovery in flight \u2014 queued for a follow-up cycle");
11361
12079
  return;
11362
12080
  }
11363
12081
  pendingEvents = false;
@@ -11368,16 +12086,16 @@ function createDiscoveryGate(s, debug13) {
11368
12086
  inProgress = false;
11369
12087
  if (queued2) {
11370
12088
  queued2 = false;
11371
- debug13?.("hmr: consuming queued rediscovery");
12089
+ debug14?.("hmr: consuming queued rediscovery");
11372
12090
  runRefreshCycle(work).catch((err) => {
11373
- debug13?.(
12091
+ debug14?.(
11374
12092
  "hmr: queued cycle rejected \u2014 releasing gate (%s)",
11375
12093
  err instanceof Error ? err.message : String(err)
11376
12094
  );
11377
12095
  resolveGate();
11378
12096
  });
11379
12097
  } else if (pendingEvents) {
11380
- debug13?.(
12098
+ debug14?.(
11381
12099
  "hmr: holding gate for pending events (debounce not yet fired)"
11382
12100
  );
11383
12101
  } else {
@@ -11423,15 +12141,15 @@ function selectForwardableResolvePlugins(plugins) {
11423
12141
  }
11424
12142
  function pickForwardedRunnerConfig(config) {
11425
12143
  const r = config.resolve ?? {};
11426
- const resolve14 = {};
11427
- if (r.alias !== void 0) resolve14.alias = r.alias;
11428
- if (r.dedupe !== void 0) resolve14.dedupe = r.dedupe;
11429
- if (r.conditions !== void 0) resolve14.conditions = r.conditions;
11430
- if (r.mainFields !== void 0) resolve14.mainFields = r.mainFields;
11431
- if (r.extensions !== void 0) resolve14.extensions = r.extensions;
12144
+ const resolve15 = {};
12145
+ if (r.alias !== void 0) resolve15.alias = r.alias;
12146
+ if (r.dedupe !== void 0) resolve15.dedupe = r.dedupe;
12147
+ if (r.conditions !== void 0) resolve15.conditions = r.conditions;
12148
+ if (r.mainFields !== void 0) resolve15.mainFields = r.mainFields;
12149
+ if (r.extensions !== void 0) resolve15.extensions = r.extensions;
11432
12150
  if (r.preserveSymlinks !== void 0)
11433
- resolve14.preserveSymlinks = r.preserveSymlinks;
11434
- if (r.tsconfigPaths !== void 0) resolve14.tsconfigPaths = r.tsconfigPaths;
12151
+ resolve15.preserveSymlinks = r.preserveSymlinks;
12152
+ if (r.tsconfigPaths !== void 0) resolve15.tsconfigPaths = r.tsconfigPaths;
11435
12153
  const userOxc = config.oxc;
11436
12154
  const userJsx = userOxc && typeof userOxc === "object" && typeof userOxc.jsx === "object" && userOxc.jsx !== null ? userOxc.jsx : {};
11437
12155
  const oxc = userOxc && typeof userOxc === "object" ? {
@@ -11439,7 +12157,7 @@ function pickForwardedRunnerConfig(config) {
11439
12157
  jsx: { ...userJsx, runtime: "automatic", importSource: "react" }
11440
12158
  } : { jsx: { runtime: "automatic", importSource: "react" } };
11441
12159
  return {
11442
- resolve: resolve14,
12160
+ resolve: resolve15,
11443
12161
  define: config.define,
11444
12162
  oxc
11445
12163
  };
@@ -11561,6 +12279,26 @@ async function createTempRscServer(state, options = {}) {
11561
12279
  ]
11562
12280
  });
11563
12281
  }
12282
+ var WRANGLER_CONFIG_NAMES = [
12283
+ "wrangler.json",
12284
+ "wrangler.jsonc",
12285
+ "wrangler.toml"
12286
+ ];
12287
+ function wranglerProxyOptions(root) {
12288
+ const viteRoot = resolve13(root);
12289
+ for (let dir = viteRoot; ; dir = dirname7(dir)) {
12290
+ for (const name of WRANGLER_CONFIG_NAMES) {
12291
+ const configPath = join10(dir, name);
12292
+ if (existsSync12(configPath)) {
12293
+ return {
12294
+ configPath,
12295
+ persist: { path: join10(viteRoot, ".wrangler", "state", "v3") }
12296
+ };
12297
+ }
12298
+ }
12299
+ if (dirname7(dir) === dir) return void 0;
12300
+ }
12301
+ }
11564
12302
  async function resolveBuildEnv(option, factoryCtx) {
11565
12303
  if (!option) return null;
11566
12304
  if (option === "auto") {
@@ -11571,11 +12309,13 @@ async function resolveBuildEnv(option, factoryCtx) {
11571
12309
  }
11572
12310
  try {
11573
12311
  const userRequire = createRequire3(
11574
- resolve12(factoryCtx.root, "package.json")
12312
+ resolve13(factoryCtx.root, "package.json")
11575
12313
  );
11576
12314
  const wranglerPath = userRequire.resolve("wrangler");
11577
12315
  const { getPlatformProxy } = await import(pathToFileURL2(wranglerPath).href);
11578
- const proxy = await getPlatformProxy();
12316
+ const proxy = await getPlatformProxy(
12317
+ wranglerProxyOptions(factoryCtx.root)
12318
+ );
11579
12319
  return {
11580
12320
  env: proxy.env,
11581
12321
  dispose: proxy.dispose
@@ -11613,6 +12353,7 @@ function resetPrerenderCollection(s) {
11613
12353
  s.staticManifestEntries = null;
11614
12354
  s.shellCandidates = null;
11615
12355
  s.prerenderPayloadValues = null;
12356
+ s.buildData = [];
11616
12357
  }
11617
12358
  async function releaseBuildEnv(s) {
11618
12359
  if (s.buildEnvDispose) {
@@ -11757,8 +12498,8 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
11757
12498
  }
11758
12499
  server.middlewares.use(internalDebugNoCacheMiddleware());
11759
12500
  let resolveDiscovery;
11760
- const discoveryPromise = new Promise((resolve14) => {
11761
- resolveDiscovery = resolve14;
12501
+ const discoveryPromise = new Promise((resolve15) => {
12502
+ resolveDiscovery = resolve15;
11762
12503
  });
11763
12504
  const gate = createDiscoveryGate(s, debugDiscovery);
11764
12505
  const beginDiscoveryGate = gate.beginGate;
@@ -11947,6 +12688,7 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
11947
12688
  globalThis.__rscRouterDiscoveryActive ?? false
11948
12689
  );
11949
12690
  s.devServerOrigin = getDevServerOrigin();
12691
+ globalThis.__PRERENDER_DEV_URL = s.devServerOrigin;
11950
12692
  let tempRscEnv;
11951
12693
  let optimizerHashBefore2;
11952
12694
  try {
@@ -12057,7 +12799,7 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
12057
12799
  for (const [map2, fn] of perRouterSetters) {
12058
12800
  const setter = serverMod[fn];
12059
12801
  if (typeof setter !== "function") continue;
12060
- for (const [routerId, value] of map2) setter(routerId, value);
12802
+ for (const [routerId2, value] of map2) setter(routerId2, value);
12061
12803
  }
12062
12804
  };
12063
12805
  server.middlewares.use("/__rsc_prerender", async (req, res) => {
@@ -12340,7 +13082,7 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
12340
13082
  maxSnapshotBytes,
12341
13083
  captureTimeout,
12342
13084
  buildEnv: s.resolvedBuildEnv,
12343
- buildVersion: version,
13085
+ versions: uniformVersions(version),
12344
13086
  captureShellHTML: ssrModule.captureShellHTML,
12345
13087
  debug: !!debugDiscovery
12346
13088
  });
@@ -12525,7 +13267,7 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
12525
13267
  if (devServerClosed || expectedEpoch !== s.devDiscoveryEpoch) {
12526
13268
  return;
12527
13269
  }
12528
- await new Promise((resolve14) => setTimeout(resolve14, 25));
13270
+ await new Promise((resolve15) => setTimeout(resolve15, 25));
12529
13271
  if (devServerClosed || expectedEpoch !== s.devDiscoveryEpoch) {
12530
13272
  return;
12531
13273
  }
@@ -12643,7 +13385,7 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
12643
13385
  }
12644
13386
  const inRecoveryMode = !!s.lastDiscoveryError;
12645
13387
  try {
12646
- const source = readFileSync9(filePath, "utf-8");
13388
+ const source = readFileSync11(filePath, "utf-8");
12647
13389
  const trimmed = source.trimStart();
12648
13390
  const isUseClient = trimmed.startsWith('"use client"') || trimmed.startsWith("'use client'");
12649
13391
  let hasClientUrls = source.includes("clientUrls(");
@@ -12685,11 +13427,11 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
12685
13427
  if (rscGraph?.getModulesByFile) {
12686
13428
  const routerSourceFiles = /* @__PURE__ */ new Set();
12687
13429
  if (s.resolvedEntryPath) {
12688
- routerSourceFiles.add(resolve12(s.resolvedEntryPath));
13430
+ routerSourceFiles.add(resolve13(s.resolvedEntryPath));
12689
13431
  }
12690
13432
  for (const entry of s.perRouterManifests) {
12691
13433
  if (entry.sourceFile) {
12692
- routerSourceFiles.add(resolve12(entry.sourceFile));
13434
+ routerSourceFiles.add(resolve13(entry.sourceFile));
12693
13435
  }
12694
13436
  }
12695
13437
  const seen = /* @__PURE__ */ new Set();
@@ -12718,7 +13460,7 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
12718
13460
  if (hasCreateRouter) {
12719
13461
  const nestedRouterConflict = findNestedRouterConflict([
12720
13462
  ...s.cachedRouterFiles ?? [],
12721
- resolve12(filePath)
13463
+ resolve13(filePath)
12722
13464
  ]);
12723
13465
  if (nestedRouterConflict) {
12724
13466
  server.config.logger.error(
@@ -12841,18 +13583,27 @@ ${details}`,
12841
13583
  );
12842
13584
  }
12843
13585
  },
12844
- // Post-build PPR shell capture (producer B, #699): runs after EVERY
12845
- // environment bundle is written — the shell prelude embeds built client
12846
- // asset URLs (bootstrap entry), which do not exist at buildStart. The
12847
- // kept temp server and the buildEnv were deferred AS A PAIR in
12848
- // buildStart's finally; this finally is the pair's success-path owner
12849
- // (buildEnd below owns the aborted-build path) — the phase itself is a
12850
- // pure producer and tears down only the globals it installs.
13586
+ // Post-build phases, after EVERY environment bundle is written.
13587
+ //
13588
+ // 1. Cache versions (discovery/router-versions-phase.ts): hashes the
13589
+ // server output as postprocessBundle left it and writes the per-router
13590
+ // table into the built version module. First, because phase 2 stamps
13591
+ // its entries with these versions and reads the same RSC out dir.
13592
+ // 2. PPR shell capture (producer B, #699): the shell prelude embeds built
13593
+ // client asset URLs (bootstrap entry), which do not exist at
13594
+ // buildStart. The kept temp server and the buildEnv were deferred AS A
13595
+ // PAIR in buildStart's finally; this finally is the pair's success-path
13596
+ // owner (buildEnd below owns the aborted-build path) — the phase itself
13597
+ // is a pure producer and tears down only the globals it installs.
12851
13598
  buildApp: {
12852
13599
  order: "post",
12853
13600
  async handler(builder) {
12854
13601
  try {
12855
- await runShellPrerenderPhase(s, builder);
13602
+ await runShellPrerenderPhase(
13603
+ s,
13604
+ builder,
13605
+ runRouterVersionsPhase(s, builder)
13606
+ );
12856
13607
  } finally {
12857
13608
  if (s.isBuildMode) {
12858
13609
  const tempServer = s.shellPhaseTempServer;
@@ -12941,11 +13692,11 @@ ${details}`,
12941
13692
  () => s.discoveryDone
12942
13693
  );
12943
13694
  }
12944
- const routerId = id.slice(perRouterPrefix.length);
13695
+ const routerId2 = id.slice(perRouterPrefix.length);
12945
13696
  const code = await timed(
12946
13697
  debugRoutes,
12947
- `generatePerRouterModule ${routerId}`,
12948
- () => generatePerRouterModule(s, routerId)
13698
+ `generatePerRouterModule ${routerId2}`,
13699
+ () => generatePerRouterModule(s, routerId2)
12949
13700
  );
12950
13701
  return code;
12951
13702
  }
@@ -12954,6 +13705,19 @@ ${details}`,
12954
13705
  // Record handler chunk metadata and RSC entry filename during RSC build.
12955
13706
  // Used by closeBundle for handler code eviction and prerender data injection.
12956
13707
  generateBundle(_options, bundle) {
13708
+ if (s.isBuildMode) {
13709
+ const envName = this.environment?.name;
13710
+ if (envName) {
13711
+ s.versionModuleFiles.set(envName, recordVersionModuleFiles(bundle));
13712
+ }
13713
+ if (envName === "rsc") {
13714
+ s.serverBuildGraph = recordServerGraph(this, bundle);
13715
+ } else if (envName === "client") {
13716
+ s.clientBuildGraph = recordClientGraph(this, bundle);
13717
+ } else if (envName === "ssr") {
13718
+ s.ssrBundle = recordBundleFiles(bundle);
13719
+ }
13720
+ }
12957
13721
  if (this.environment?.name !== "rsc") return;
12958
13722
  const genStart = debugBuild ? performance.now() : 0;
12959
13723
  for (const [fileName, chunk] of Object.entries(bundle)) {
@@ -12978,7 +13742,13 @@ ${details}`,
12978
13742
  chunk.code,
12979
13743
  s.resolvedPrerenderModules,
12980
13744
  "Prerender",
12981
- false
13745
+ false,
13746
+ // Mark onDemand exports (retained, not evicted) from the
13747
+ // discovery-derived $$id set — the same evaluated source that sets
13748
+ // the runtime od trie flag, so retention and serve can never
13749
+ // disagree. Populated by discoverRouters (buildStart), which runs
13750
+ // before this generateBundle pass.
13751
+ s.onDemandHandlerIds
12982
13752
  );
12983
13753
  if (handlers.length > 0) {
12984
13754
  const existing = s.handlerChunkInfoMap.get(fileName);
@@ -13043,10 +13813,12 @@ ${details}`,
13043
13813
  init_debug();
13044
13814
  var debugConfig = createRangoDebugger(NS.config);
13045
13815
  var SERVER_BUILD_TARGET = "esnext";
13816
+ var RSC_ENTRY_SIGNATURES = "strict";
13046
13817
  async function rango(options) {
13047
13818
  const rangoStart = performance.now();
13048
13819
  const resolvedOptions = options ?? { preset: "node" };
13049
13820
  const preset = resolvedOptions.preset ?? "node";
13821
+ configureEncryptionKey(resolvedOptions.encryptionKey);
13050
13822
  const showBanner = resolvedOptions.banner ?? true;
13051
13823
  const clientChunksOption = resolvedOptions.clientChunks ?? true;
13052
13824
  const useBuiltInClientChunks = clientChunksOption === true;
@@ -13190,14 +13962,14 @@ async function rango(options) {
13190
13962
  name: "@rangojs/router:auto-discover",
13191
13963
  config(userConfig) {
13192
13964
  if (routerRef.path) return;
13193
- const root = userConfig.root ? resolve13(process.cwd(), userConfig.root) : process.cwd();
13965
+ const root = userConfig.root ? resolve14(process.cwd(), userConfig.root) : process.cwd();
13194
13966
  const toRootRelative = (abs) => (abs.startsWith(root) ? "./" + abs.slice(root.length + 1) : abs).replaceAll("\\", "/");
13195
13967
  const bulletList = (files) => files.map((f) => " - " + toRootRelative(f)).join("\n");
13196
13968
  if (explicitHostRouter) {
13197
13969
  routerRef.path = normalizeHostRouterEntry(
13198
13970
  explicitHostRouter,
13199
13971
  root,
13200
- existsSync11
13972
+ existsSync13
13201
13973
  );
13202
13974
  routerRef.kind = "host";
13203
13975
  return;
@@ -13317,7 +14089,10 @@ If this is a multi-app host router, export a createHostRouter() instance and set
13317
14089
  rsc: {
13318
14090
  ...vercelServerEnv ?? {},
13319
14091
  build: {
13320
- target: SERVER_BUILD_TARGET
14092
+ target: SERVER_BUILD_TARGET,
14093
+ rollupOptions: {
14094
+ preserveEntrySignatures: RSC_ENTRY_SIGNATURES
14095
+ }
13321
14096
  },
13322
14097
  optimizeDeps: {
13323
14098
  entries: [VIRTUAL_IDS.rsc],
@@ -13380,6 +14155,7 @@ If this is a multi-app host router, export a createHostRouter() instance and set
13380
14155
  plugins.push(clientRefDedup());
13381
14156
  }
13382
14157
  plugins.push(clientChunksReport());
14158
+ plugins.push(stableRscOutput());
13383
14159
  plugins.push({
13384
14160
  name: "@rangojs/router:client-component-hmr",
13385
14161
  hotUpdate(ctx) {
@@ -13389,7 +14165,7 @@ If this is a multi-app host router, export a createHostRouter() instance and set
13389
14165
  if (!file.endsWith(".tsx") && !file.endsWith(".ts") && !file.endsWith(".jsx") && !file.endsWith(".js"))
13390
14166
  return;
13391
14167
  try {
13392
- const source = readFileSync10(file, "utf-8");
14168
+ const source = readFileSync12(file, "utf-8");
13393
14169
  if (hasUseClientDirective2(source)) {
13394
14170
  return [];
13395
14171
  }
@@ -13412,7 +14188,7 @@ If this is a multi-app host router, export a createHostRouter() instance and set
13412
14188
  const discoveryEntryPath = preset !== "cloudflare" ? routerRef.path : void 0;
13413
14189
  const discoveryRouterRef = preset !== "cloudflare" ? routerRef : void 0;
13414
14190
  if (preset === "cloudflare") {
13415
- plugins.push(createVersionInjectorPlugin(void 0));
14191
+ plugins.push(createEntryBootstrapInjectorPlugin(void 0));
13416
14192
  }
13417
14193
  plugins.push(createCjsToEsmPlugin());
13418
14194
  plugins.push(
@@ -13449,7 +14225,7 @@ function poke() {
13449
14225
  apply: "serve",
13450
14226
  configureServer(server) {
13451
14227
  const stdin = process.stdin;
13452
- const debug13 = process.env.RANGO_POKE_DEBUG === "1";
14228
+ const debug14 = process.env.RANGO_POKE_DEBUG === "1";
13453
14229
  const triggerReload = (source) => {
13454
14230
  server.hot.send({ type: "full-reload", path: "*" });
13455
14231
  server.config.logger.info(` browser reload (${source})`, {
@@ -13482,7 +14258,7 @@ function poke() {
13482
14258
  lines.pop();
13483
14259
  return lines;
13484
14260
  };
13485
- if (debug13) {
14261
+ if (debug14) {
13486
14262
  server.config.logger.info(
13487
14263
  ` poke debug enabled (isTTY=${stdin.isTTY ? "yes" : "no"}, isRaw=${stdin.isTTY ? stdin.isRaw ? "yes" : "no" : "n/a"})`,
13488
14264
  { timestamp: true }
@@ -13495,7 +14271,7 @@ function poke() {
13495
14271
  );
13496
14272
  }
13497
14273
  const onData = (data) => {
13498
- if (debug13) {
14274
+ if (debug14) {
13499
14275
  server.config.logger.info(` poke stdin ${formatChunk(data)}`, {
13500
14276
  timestamp: true
13501
14277
  });