@timber-js/app 0.2.0-alpha.21 → 0.2.0-alpha.211

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 (1393) hide show
  1. package/LICENSE +8 -0
  2. package/agent-skill.md +346 -0
  3. package/bin/timber.mjs +15 -1
  4. package/dist/_chunks/actions-Rjk4htmA.js +1199 -0
  5. package/dist/_chunks/actions-Rjk4htmA.js.map +1 -0
  6. package/dist/_chunks/als-registry-DaxkVjt5.js +45 -0
  7. package/dist/_chunks/als-registry-DaxkVjt5.js.map +1 -0
  8. package/dist/_chunks/als-slots-BEEIPKYm.js +26 -0
  9. package/dist/_chunks/als-slots-BEEIPKYm.js.map +1 -0
  10. package/dist/_chunks/base-path-DaQrzbez.js +80 -0
  11. package/dist/_chunks/base-path-DaQrzbez.js.map +1 -0
  12. package/dist/_chunks/build-manifest-DWppEdLB.js +86 -0
  13. package/dist/_chunks/build-manifest-DWppEdLB.js.map +1 -0
  14. package/dist/_chunks/build-output-helper-RV_BRcF1.js +367 -0
  15. package/dist/_chunks/build-output-helper-RV_BRcF1.js.map +1 -0
  16. package/dist/_chunks/cache-api-DGdYfNJn.js +1182 -0
  17. package/dist/_chunks/cache-api-DGdYfNJn.js.map +1 -0
  18. package/dist/_chunks/canonicalize-CgHoscYO.js +66 -0
  19. package/dist/_chunks/canonicalize-CgHoscYO.js.map +1 -0
  20. package/dist/_chunks/chains-DGX9zmg9.js +272 -0
  21. package/dist/_chunks/chains-DGX9zmg9.js.map +1 -0
  22. package/dist/_chunks/classify-QwG5rxKI.js +147 -0
  23. package/dist/_chunks/classify-QwG5rxKI.js.map +1 -0
  24. package/dist/_chunks/cli-check-ajNY3B2e.js +252 -0
  25. package/dist/_chunks/cli-check-ajNY3B2e.js.map +1 -0
  26. package/dist/_chunks/cli-context-By7k2xVo.js +70 -0
  27. package/dist/_chunks/cli-context-By7k2xVo.js.map +1 -0
  28. package/dist/_chunks/cli-schema-sync-D2eI8jEg.js +299 -0
  29. package/dist/_chunks/cli-schema-sync-D2eI8jEg.js.map +1 -0
  30. package/dist/_chunks/client-dep-entries-2HCF09no.js +66 -0
  31. package/dist/_chunks/client-dep-entries-2HCF09no.js.map +1 -0
  32. package/dist/_chunks/cloudflare-Byxl8FWP.js +1333 -0
  33. package/dist/_chunks/cloudflare-Byxl8FWP.js.map +1 -0
  34. package/dist/_chunks/codec-registry-oOUxugz3.js +21 -0
  35. package/dist/_chunks/codec-registry-oOUxugz3.js.map +1 -0
  36. package/dist/_chunks/codegen-Bps1sLKJ.js +432 -0
  37. package/dist/_chunks/codegen-Bps1sLKJ.js.map +1 -0
  38. package/dist/_chunks/convention-lint-DLmhGsRS.js +590 -0
  39. package/dist/_chunks/convention-lint-DLmhGsRS.js.map +1 -0
  40. package/dist/_chunks/dev-server-TFpEwm3H.js +2039 -0
  41. package/dist/_chunks/dev-server-TFpEwm3H.js.map +1 -0
  42. package/dist/_chunks/dist-DtrJyBbw.js +386 -0
  43. package/dist/_chunks/dist-DtrJyBbw.js.map +1 -0
  44. package/dist/_chunks/error-boundary-DsNScGRM.js +1024 -0
  45. package/dist/_chunks/error-boundary-DsNScGRM.js.map +1 -0
  46. package/dist/_chunks/fast-hash-C-tVYrLb.js +41 -0
  47. package/dist/_chunks/fast-hash-C-tVYrLb.js.map +1 -0
  48. package/dist/_chunks/format-BKclgVbk.js +14 -0
  49. package/dist/_chunks/format-BKclgVbk.js.map +1 -0
  50. package/dist/_chunks/fs-identity-BRKuL215.js +207 -0
  51. package/dist/_chunks/fs-identity-BRKuL215.js.map +1 -0
  52. package/dist/_chunks/graph-cache-CYiUS2aS.js +303 -0
  53. package/dist/_chunks/graph-cache-CYiUS2aS.js.map +1 -0
  54. package/dist/_chunks/href-validation-BIrxavIy.js +213 -0
  55. package/dist/_chunks/href-validation-BIrxavIy.js.map +1 -0
  56. package/dist/_chunks/json-lossy-check-CVuRs2hG.js +66 -0
  57. package/dist/_chunks/json-lossy-check-CVuRs2hG.js.map +1 -0
  58. package/dist/_chunks/live-graph-cNuWMYQI.js +151 -0
  59. package/dist/_chunks/live-graph-cNuWMYQI.js.map +1 -0
  60. package/dist/_chunks/logger-BP0LN6vP.js +662 -0
  61. package/dist/_chunks/logger-BP0LN6vP.js.map +1 -0
  62. package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-DSDjM_hJ.js} +71 -27
  63. package/dist/_chunks/metadata-routes-DSDjM_hJ.js.map +1 -0
  64. package/dist/_chunks/navigation-root-BQfo1-kG.js +126 -0
  65. package/dist/_chunks/navigation-root-BQfo1-kG.js.map +1 -0
  66. package/dist/_chunks/param-value-C8TNYchQ.js +66 -0
  67. package/dist/_chunks/param-value-C8TNYchQ.js.map +1 -0
  68. package/dist/_chunks/plugin-context-BPBPc3lL.js +345 -0
  69. package/dist/_chunks/plugin-context-BPBPc3lL.js.map +1 -0
  70. package/dist/_chunks/poison-rules-DoEhbqaY.js +110 -0
  71. package/dist/_chunks/poison-rules-DoEhbqaY.js.map +1 -0
  72. package/dist/_chunks/poison-scan-CfQ3unZR.js +99 -0
  73. package/dist/_chunks/poison-scan-CfQ3unZR.js.map +1 -0
  74. package/dist/_chunks/purge-store-Byr8XOjU.js +14 -0
  75. package/dist/_chunks/purge-store-Byr8XOjU.js.map +1 -0
  76. package/dist/_chunks/render-timeout-DOf_BBcO.js +46 -0
  77. package/dist/_chunks/render-timeout-DOf_BBcO.js.map +1 -0
  78. package/dist/_chunks/resolve-schema-CBR6Lm4i.js +50 -0
  79. package/dist/_chunks/resolve-schema-CBR6Lm4i.js.map +1 -0
  80. package/dist/_chunks/rolldown-runtime-D7D4PA-g.js +13 -0
  81. package/dist/_chunks/rsc-cache-key-ClUiXQnK.js +243 -0
  82. package/dist/_chunks/rsc-cache-key-ClUiXQnK.js.map +1 -0
  83. package/dist/_chunks/rsc-media-type-DDc7duTD.js +61 -0
  84. package/dist/_chunks/rsc-media-type-DDc7duTD.js.map +1 -0
  85. package/dist/_chunks/rsc-payload-path-dffcs4jp.js +54 -0
  86. package/dist/_chunks/rsc-payload-path-dffcs4jp.js.map +1 -0
  87. package/dist/_chunks/scan-BoP17W3w.js +140 -0
  88. package/dist/_chunks/scan-BoP17W3w.js.map +1 -0
  89. package/dist/_chunks/scanner-DmqdxzbW.js +2056 -0
  90. package/dist/_chunks/scanner-DmqdxzbW.js.map +1 -0
  91. package/dist/_chunks/schema-bridge-C83xa9lT.js +199 -0
  92. package/dist/_chunks/schema-bridge-C83xa9lT.js.map +1 -0
  93. package/dist/_chunks/segment-classify-C539Pa2O.js +197 -0
  94. package/dist/_chunks/segment-classify-C539Pa2O.js.map +1 -0
  95. package/dist/_chunks/segment-context-xtPUGfdq.js +137 -0
  96. package/dist/_chunks/segment-context-xtPUGfdq.js.map +1 -0
  97. package/dist/_chunks/segment-keys-D5hu1hz4.js +62 -0
  98. package/dist/_chunks/segment-keys-D5hu1hz4.js.map +1 -0
  99. package/dist/_chunks/singleflight-BeVMMKi0.js +55 -0
  100. package/dist/_chunks/singleflight-BeVMMKi0.js.map +1 -0
  101. package/dist/_chunks/sizeof-BM1409x2.js +36 -0
  102. package/dist/_chunks/sizeof-BM1409x2.js.map +1 -0
  103. package/dist/_chunks/ssr-data-nA3I70_n.js +49 -0
  104. package/dist/_chunks/ssr-data-nA3I70_n.js.map +1 -0
  105. package/dist/_chunks/state-BhNGPsdi.js +50 -0
  106. package/dist/_chunks/state-BhNGPsdi.js.map +1 -0
  107. package/dist/_chunks/status-page-marker-BRX9Ib-d.js +564 -0
  108. package/dist/_chunks/status-page-marker-BRX9Ib-d.js.map +1 -0
  109. package/dist/_chunks/tree-match-6yihorhk.js +169 -0
  110. package/dist/_chunks/tree-match-6yihorhk.js.map +1 -0
  111. package/dist/_chunks/use-query-states-I3JMng6J.js +226 -0
  112. package/dist/_chunks/use-query-states-I3JMng6J.js.map +1 -0
  113. package/dist/_chunks/walkers-Czu2jXFq.js +144 -0
  114. package/dist/_chunks/walkers-Czu2jXFq.js.map +1 -0
  115. package/dist/_chunks/write-file-atomic-D1qWqHb4.js +32 -0
  116. package/dist/_chunks/write-file-atomic-D1qWqHb4.js.map +1 -0
  117. package/dist/adapters/build-output-helper.d.ts +72 -0
  118. package/dist/adapters/build-output-helper.d.ts.map +1 -0
  119. package/dist/adapters/canonicalize-module.d.ts +8 -0
  120. package/dist/adapters/canonicalize-module.d.ts.map +1 -0
  121. package/dist/adapters/cloudflare-dev.d.ts +109 -0
  122. package/dist/adapters/cloudflare-dev.d.ts.map +1 -0
  123. package/dist/adapters/cloudflare-dev.js +74 -0
  124. package/dist/adapters/cloudflare-dev.js.map +1 -0
  125. package/dist/adapters/cloudflare-kv-cache.d.ts +65 -0
  126. package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -0
  127. package/dist/adapters/cloudflare-kv-cache.js +123 -0
  128. package/dist/adapters/cloudflare-kv-cache.js.map +1 -0
  129. package/dist/adapters/cloudflare.d.ts +224 -24
  130. package/dist/adapters/cloudflare.d.ts.map +1 -1
  131. package/dist/adapters/cloudflare.js +2 -188
  132. package/dist/adapters/compress-module.d.ts +25 -4
  133. package/dist/adapters/compress-module.d.ts.map +1 -1
  134. package/dist/adapters/fs-identity.d.ts +63 -0
  135. package/dist/adapters/fs-identity.d.ts.map +1 -0
  136. package/dist/adapters/nitro-presets.d.ts +31 -0
  137. package/dist/adapters/nitro-presets.d.ts.map +1 -0
  138. package/dist/adapters/nitro-preview.d.ts +64 -0
  139. package/dist/adapters/nitro-preview.d.ts.map +1 -0
  140. package/dist/adapters/nitro.d.ts +54 -37
  141. package/dist/adapters/nitro.d.ts.map +1 -1
  142. package/dist/adapters/nitro.js +631 -359
  143. package/dist/adapters/nitro.js.map +1 -1
  144. package/dist/adapters/shared.d.ts +67 -0
  145. package/dist/adapters/shared.d.ts.map +1 -0
  146. package/dist/adapters/types.d.ts +122 -16
  147. package/dist/adapters/types.d.ts.map +1 -1
  148. package/dist/adapters/types.js +0 -0
  149. package/dist/analyze/chains.d.ts +124 -0
  150. package/dist/analyze/chains.d.ts.map +1 -0
  151. package/dist/analyze/classify.d.ts +153 -0
  152. package/dist/analyze/classify.d.ts.map +1 -0
  153. package/dist/analyze/crawl-entry.d.ts +17 -0
  154. package/dist/analyze/crawl-entry.d.ts.map +1 -0
  155. package/dist/analyze/crawl-entry.js +314 -0
  156. package/dist/analyze/crawl-entry.js.map +1 -0
  157. package/dist/analyze/crawl.d.ts +134 -0
  158. package/dist/analyze/crawl.d.ts.map +1 -0
  159. package/dist/analyze/graph-cache.d.ts +104 -0
  160. package/dist/analyze/graph-cache.d.ts.map +1 -0
  161. package/dist/analyze/graph-command.d.ts +43 -0
  162. package/dist/analyze/graph-command.d.ts.map +1 -0
  163. package/dist/analyze/graph-command.js +472 -0
  164. package/dist/analyze/graph-command.js.map +1 -0
  165. package/dist/analyze/live-graph.d.ts +81 -0
  166. package/dist/analyze/live-graph.d.ts.map +1 -0
  167. package/dist/analyze/poison-rules.d.ts +72 -0
  168. package/dist/analyze/poison-rules.d.ts.map +1 -0
  169. package/dist/analyze/poison-scan.d.ts +48 -0
  170. package/dist/analyze/poison-scan.d.ts.map +1 -0
  171. package/dist/analyze/scan.d.ts +73 -0
  172. package/dist/analyze/scan.d.ts.map +1 -0
  173. package/dist/cache/cache-api.d.ts +48 -0
  174. package/dist/cache/cache-api.d.ts.map +1 -0
  175. package/dist/cache/fast-hash.d.ts +5 -0
  176. package/dist/cache/fast-hash.d.ts.map +1 -1
  177. package/dist/cache/handler-store.d.ts +33 -0
  178. package/dist/cache/handler-store.d.ts.map +1 -0
  179. package/dist/cache/index.d.ts +32 -9
  180. package/dist/cache/index.d.ts.map +1 -1
  181. package/dist/cache/index.js +3 -326
  182. package/dist/cache/invalidation-epoch.d.ts +43 -0
  183. package/dist/cache/invalidation-epoch.d.ts.map +1 -0
  184. package/dist/cache/json-lossy-check.d.ts +11 -0
  185. package/dist/cache/json-lossy-check.d.ts.map +1 -0
  186. package/dist/cache/redis-handler.d.ts +141 -9
  187. package/dist/cache/redis-handler.d.ts.map +1 -1
  188. package/dist/cache/singleflight.d.ts +21 -2
  189. package/dist/cache/singleflight.d.ts.map +1 -1
  190. package/dist/cache/sizeof.d.ts +22 -0
  191. package/dist/cache/sizeof.d.ts.map +1 -0
  192. package/dist/cache/stable-stringify.d.ts +9 -0
  193. package/dist/cache/stable-stringify.d.ts.map +1 -1
  194. package/dist/cache/store.d.ts +53 -0
  195. package/dist/cache/store.d.ts.map +1 -0
  196. package/dist/cache/stores/cloudflare-kv.d.ts +38 -0
  197. package/dist/cache/stores/cloudflare-kv.d.ts.map +1 -0
  198. package/dist/cache/stores/cloudflare-kv.js +29 -0
  199. package/dist/cache/stores/cloudflare-kv.js.map +1 -0
  200. package/dist/cache/stores/memory.d.ts +42 -0
  201. package/dist/cache/stores/memory.d.ts.map +1 -0
  202. package/dist/cache/stores/memory.js +90 -0
  203. package/dist/cache/stores/memory.js.map +1 -0
  204. package/dist/cache/stores/redis.d.ts +69 -0
  205. package/dist/cache/stores/redis.d.ts.map +1 -0
  206. package/dist/cache/stores/redis.js +35 -0
  207. package/dist/cache/stores/redis.js.map +1 -0
  208. package/dist/cache/stores/vercel.d.ts +36 -0
  209. package/dist/cache/stores/vercel.d.ts.map +1 -0
  210. package/dist/cache/stores/vercel.js +33 -0
  211. package/dist/cache/stores/vercel.js.map +1 -0
  212. package/dist/cache/tag-aware-handler.d.ts +52 -0
  213. package/dist/cache/tag-aware-handler.d.ts.map +1 -0
  214. package/dist/cache/timber-cache.d.ts +21 -11
  215. package/dist/cache/timber-cache.d.ts.map +1 -1
  216. package/dist/cdn/cloudflare-purge.d.ts +23 -0
  217. package/dist/cdn/cloudflare-purge.d.ts.map +1 -0
  218. package/dist/cdn/cloudflare-purge.js +30 -0
  219. package/dist/cdn/cloudflare-purge.js.map +1 -0
  220. package/dist/cdn/fastly-purge.d.ts +25 -0
  221. package/dist/cdn/fastly-purge.d.ts.map +1 -0
  222. package/dist/cdn/fastly-purge.js +33 -0
  223. package/dist/cdn/fastly-purge.js.map +1 -0
  224. package/dist/cdn/headers.d.ts +97 -0
  225. package/dist/cdn/headers.d.ts.map +1 -0
  226. package/dist/cdn/index.d.ts +5 -0
  227. package/dist/cdn/index.d.ts.map +1 -0
  228. package/dist/cdn/index.js +94 -0
  229. package/dist/cdn/index.js.map +1 -0
  230. package/dist/cdn/purge-store.d.ts +23 -0
  231. package/dist/cdn/purge-store.d.ts.map +1 -0
  232. package/dist/cdn/workers-cache-purge.d.ts +20 -0
  233. package/dist/cdn/workers-cache-purge.d.ts.map +1 -0
  234. package/dist/cdn/workers-cache-purge.js +35 -0
  235. package/dist/cdn/workers-cache-purge.js.map +1 -0
  236. package/dist/cli-check.d.ts +133 -0
  237. package/dist/cli-check.d.ts.map +1 -0
  238. package/dist/cli-context.d.ts +54 -0
  239. package/dist/cli-context.d.ts.map +1 -0
  240. package/dist/cli-schema-sync.d.ts +59 -0
  241. package/dist/cli-schema-sync.d.ts.map +1 -0
  242. package/dist/cli.d.ts +85 -13
  243. package/dist/cli.d.ts.map +1 -1
  244. package/dist/cli.js +224 -63
  245. package/dist/cli.js.map +1 -1
  246. package/dist/client/browser-dev.d.ts +42 -2
  247. package/dist/client/browser-dev.d.ts.map +1 -1
  248. package/dist/client/browser-entry/action-dispatch.d.ts +19 -0
  249. package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -0
  250. package/dist/client/browser-entry/action-queue.d.ts +63 -0
  251. package/dist/client/browser-entry/action-queue.d.ts.map +1 -0
  252. package/dist/client/browser-entry/hmr.d.ts +21 -0
  253. package/dist/client/browser-entry/hmr.d.ts.map +1 -0
  254. package/dist/client/browser-entry/hydrate.d.ts +53 -0
  255. package/dist/client/browser-entry/hydrate.d.ts.map +1 -0
  256. package/dist/client/browser-entry/index.d.ts +30 -0
  257. package/dist/client/browser-entry/index.d.ts.map +1 -0
  258. package/dist/client/browser-entry/post-hydration.d.ts +26 -0
  259. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -0
  260. package/dist/client/browser-entry/router-init.d.ts +41 -0
  261. package/dist/client/browser-entry/router-init.d.ts.map +1 -0
  262. package/dist/client/browser-entry/rsc-stream.d.ts +32 -0
  263. package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -0
  264. package/dist/client/browser-entry/scroll.d.ts +28 -0
  265. package/dist/client/browser-entry/scroll.d.ts.map +1 -0
  266. package/dist/client/deny-last-resort.d.ts +29 -0
  267. package/dist/client/deny-last-resort.d.ts.map +1 -0
  268. package/dist/client/error-boundary.d.ts +58 -9
  269. package/dist/client/error-boundary.d.ts.map +1 -1
  270. package/dist/client/error-boundary.js +2 -126
  271. package/dist/client/error-reconstituter.d.ts +55 -0
  272. package/dist/client/error-reconstituter.d.ts.map +1 -0
  273. package/dist/client/form.d.ts +16 -27
  274. package/dist/client/form.d.ts.map +1 -1
  275. package/dist/client/global-context.d.ts +15 -0
  276. package/dist/client/global-context.d.ts.map +1 -0
  277. package/dist/client/history.d.ts +50 -9
  278. package/dist/client/history.d.ts.map +1 -1
  279. package/dist/client/index.d.ts +20 -33
  280. package/dist/client/index.d.ts.map +1 -1
  281. package/dist/client/index.js +330 -1150
  282. package/dist/client/index.js.map +1 -1
  283. package/dist/client/internal.d.ts +20 -0
  284. package/dist/client/internal.d.ts.map +1 -0
  285. package/dist/client/internal.js +792 -0
  286. package/dist/client/internal.js.map +1 -0
  287. package/dist/client/link.d.ts +85 -60
  288. package/dist/client/link.d.ts.map +1 -1
  289. package/dist/client/location-search.d.ts +12 -0
  290. package/dist/client/location-search.d.ts.map +1 -0
  291. package/dist/client/navigation-api.d.ts +133 -0
  292. package/dist/client/navigation-api.d.ts.map +1 -0
  293. package/dist/client/navigation-commit.d.ts +111 -0
  294. package/dist/client/navigation-commit.d.ts.map +1 -0
  295. package/dist/client/navigation-context.d.ts +54 -36
  296. package/dist/client/navigation-context.d.ts.map +1 -1
  297. package/dist/client/navigation-root.d.ts +106 -0
  298. package/dist/client/navigation-root.d.ts.map +1 -0
  299. package/dist/client/navigation-transition.d.ts +192 -0
  300. package/dist/client/navigation-transition.d.ts.map +1 -0
  301. package/dist/client/nuqs-adapter.d.ts +1 -1
  302. package/dist/client/nuqs-adapter.d.ts.map +1 -1
  303. package/dist/client/params-context.d.ts +81 -0
  304. package/dist/client/params-context.d.ts.map +1 -0
  305. package/dist/client/react-root.d.ts +45 -0
  306. package/dist/client/react-root.d.ts.map +1 -0
  307. package/dist/client/router-effects.d.ts +137 -0
  308. package/dist/client/router-effects.d.ts.map +1 -0
  309. package/dist/client/router-lifecycle.d.ts +98 -0
  310. package/dist/client/router-lifecycle.d.ts.map +1 -0
  311. package/dist/client/router-pipeline.d.ts +84 -0
  312. package/dist/client/router-pipeline.d.ts.map +1 -0
  313. package/dist/client/router-ref.d.ts +2 -2
  314. package/dist/client/router-ref.d.ts.map +1 -1
  315. package/dist/client/router-skew.d.ts +21 -0
  316. package/dist/client/router-skew.d.ts.map +1 -0
  317. package/dist/client/router-types.d.ts +334 -0
  318. package/dist/client/router-types.d.ts.map +1 -0
  319. package/dist/client/router-url.d.ts +24 -0
  320. package/dist/client/router-url.d.ts.map +1 -0
  321. package/dist/client/router.d.ts +1 -97
  322. package/dist/client/router.d.ts.map +1 -1
  323. package/dist/client/rsc-fetch.d.ts +110 -28
  324. package/dist/client/rsc-fetch.d.ts.map +1 -1
  325. package/dist/client/segment-cache.d.ts +186 -25
  326. package/dist/client/segment-cache.d.ts.map +1 -1
  327. package/dist/client/segment-context.d.ts.map +1 -1
  328. package/dist/client/segment-outlet.d.ts +68 -0
  329. package/dist/client/segment-outlet.d.ts.map +1 -0
  330. package/dist/client/segment-update-context.d.ts +15 -0
  331. package/dist/client/segment-update-context.d.ts.map +1 -0
  332. package/dist/client/shallow-url.d.ts +10 -0
  333. package/dist/client/shallow-url.d.ts.map +1 -0
  334. package/dist/client/slot-content-cache-context.d.ts +35 -0
  335. package/dist/client/slot-content-cache-context.d.ts.map +1 -0
  336. package/dist/client/slot-context.d.ts +31 -0
  337. package/dist/client/slot-context.d.ts.map +1 -0
  338. package/dist/client/slot-outlet.d.ts +16 -0
  339. package/dist/client/slot-outlet.d.ts.map +1 -0
  340. package/dist/client/slot-provider.d.ts +25 -0
  341. package/dist/client/slot-provider.d.ts.map +1 -0
  342. package/dist/client/ssr-data.d.ts +26 -50
  343. package/dist/client/ssr-data.d.ts.map +1 -1
  344. package/dist/client/stale-client.d.ts +69 -0
  345. package/dist/client/stale-client.d.ts.map +1 -0
  346. package/dist/client/state.d.ts +21 -13
  347. package/dist/client/state.d.ts.map +1 -1
  348. package/dist/client/status-page-marker.d.ts +25 -0
  349. package/dist/client/status-page-marker.d.ts.map +1 -0
  350. package/dist/client/top-loader.d.ts +7 -6
  351. package/dist/client/top-loader.d.ts.map +1 -1
  352. package/dist/client/unload-guard.d.ts +5 -0
  353. package/dist/client/unload-guard.d.ts.map +1 -1
  354. package/dist/client/use-cookie.d.ts +12 -0
  355. package/dist/client/use-cookie.d.ts.map +1 -1
  356. package/dist/client/use-link-status.d.ts +5 -5
  357. package/dist/client/use-link-status.d.ts.map +1 -1
  358. package/dist/client/use-pathname.d.ts +13 -11
  359. package/dist/client/use-pathname.d.ts.map +1 -1
  360. package/dist/client/use-pending-navigation.d.ts +23 -0
  361. package/dist/client/use-pending-navigation.d.ts.map +1 -0
  362. package/dist/client/use-query-states.d.ts +10 -4
  363. package/dist/client/use-query-states.d.ts.map +1 -1
  364. package/dist/client/use-router.d.ts +15 -8
  365. package/dist/client/use-router.d.ts.map +1 -1
  366. package/dist/client/use-search-params.d.ts +15 -8
  367. package/dist/client/use-search-params.d.ts.map +1 -1
  368. package/dist/client/use-segment-params.d.ts +55 -0
  369. package/dist/client/use-segment-params.d.ts.map +1 -0
  370. package/dist/codec.d.ts +69 -0
  371. package/dist/codec.d.ts.map +1 -0
  372. package/dist/codec.js +76 -0
  373. package/dist/codec.js.map +1 -0
  374. package/dist/config-types.d.ts +323 -0
  375. package/dist/config-types.d.ts.map +1 -0
  376. package/dist/config-validation.d.ts +68 -0
  377. package/dist/config-validation.d.ts.map +1 -0
  378. package/dist/cookies/define-cookie.d.ts +58 -25
  379. package/dist/cookies/define-cookie.d.ts.map +1 -1
  380. package/dist/cookies/index.d.ts +3 -2
  381. package/dist/cookies/index.d.ts.map +1 -1
  382. package/dist/cookies/index.js +256 -36
  383. package/dist/cookies/index.js.map +1 -1
  384. package/dist/cookies/json-cookie.d.ts +64 -0
  385. package/dist/cookies/json-cookie.d.ts.map +1 -0
  386. package/dist/cookies/validation.d.ts +34 -0
  387. package/dist/cookies/validation.d.ts.map +1 -0
  388. package/dist/dev-tools/404-page.d.ts +50 -0
  389. package/dist/dev-tools/404-page.d.ts.map +1 -0
  390. package/dist/dev-tools/browser-logs.d.ts +40 -0
  391. package/dist/dev-tools/browser-logs.d.ts.map +1 -0
  392. package/dist/dev-tools/dev-page-shell.d.ts +95 -0
  393. package/dist/dev-tools/dev-page-shell.d.ts.map +1 -0
  394. package/dist/dev-tools/error-page.d.ts +32 -0
  395. package/dist/dev-tools/error-page.d.ts.map +1 -0
  396. package/dist/dev-tools/holding-server.d.ts +46 -0
  397. package/dist/dev-tools/holding-server.d.ts.map +1 -0
  398. package/dist/dev-tools/hydration-overlay.d.ts +11 -0
  399. package/dist/dev-tools/hydration-overlay.d.ts.map +1 -0
  400. package/dist/dev-tools/index.d.ts +31 -0
  401. package/dist/dev-tools/index.d.ts.map +1 -0
  402. package/dist/dev-tools/instrumentation.d.ts +74 -0
  403. package/dist/dev-tools/instrumentation.d.ts.map +1 -0
  404. package/dist/{server/dev-logger.d.ts → dev-tools/logger.d.ts} +1 -1
  405. package/dist/dev-tools/logger.d.ts.map +1 -0
  406. package/dist/{plugins/dev-logs.d.ts → dev-tools/logs.d.ts} +17 -3
  407. package/dist/dev-tools/logs.d.ts.map +1 -0
  408. package/dist/{plugins/dev-error-overlay.d.ts → dev-tools/overlay.d.ts} +15 -25
  409. package/dist/dev-tools/overlay.d.ts.map +1 -0
  410. package/dist/dev-tools/stack-classifier.d.ts +34 -0
  411. package/dist/dev-tools/stack-classifier.d.ts.map +1 -0
  412. package/dist/dev-tools/terminal.d.ts +28 -0
  413. package/dist/dev-tools/terminal.d.ts.map +1 -0
  414. package/dist/fonts/ast-walk.d.ts +68 -0
  415. package/dist/fonts/ast-walk.d.ts.map +1 -0
  416. package/dist/fonts/ast.d.ts +112 -8
  417. package/dist/fonts/ast.d.ts.map +1 -1
  418. package/dist/fonts/bundle.d.ts +50 -0
  419. package/dist/fonts/bundle.d.ts.map +1 -0
  420. package/dist/fonts/css.d.ts +1 -1
  421. package/dist/fonts/dev-middleware.d.ts +22 -0
  422. package/dist/fonts/dev-middleware.d.ts.map +1 -0
  423. package/dist/fonts/fallbacks.d.ts +0 -7
  424. package/dist/fonts/fallbacks.d.ts.map +1 -1
  425. package/dist/fonts/google.d.ts +5 -3
  426. package/dist/fonts/google.d.ts.map +1 -1
  427. package/dist/fonts/local.d.ts +5 -5
  428. package/dist/fonts/local.d.ts.map +1 -1
  429. package/dist/fonts/paths.d.ts +43 -0
  430. package/dist/fonts/paths.d.ts.map +1 -0
  431. package/dist/fonts/pipeline.d.ts +177 -0
  432. package/dist/fonts/pipeline.d.ts.map +1 -0
  433. package/dist/fonts/transform.d.ts +82 -0
  434. package/dist/fonts/transform.d.ts.map +1 -0
  435. package/dist/fonts/types.d.ts +47 -1
  436. package/dist/fonts/types.d.ts.map +1 -1
  437. package/dist/fonts/virtual-modules.d.ts +77 -0
  438. package/dist/fonts/virtual-modules.d.ts.map +1 -0
  439. package/dist/index.d.ts +35 -181
  440. package/dist/index.d.ts.map +1 -1
  441. package/dist/index.js +8871 -14233
  442. package/dist/index.js.map +1 -1
  443. package/dist/params/codec-registry.d.ts +26 -0
  444. package/dist/params/codec-registry.d.ts.map +1 -0
  445. package/dist/params/define-schema.d.ts +81 -0
  446. package/dist/params/define-schema.d.ts.map +1 -0
  447. package/dist/params/index.d.ts +5 -0
  448. package/dist/params/index.d.ts.map +1 -0
  449. package/dist/params/index.js +34 -0
  450. package/dist/params/index.js.map +1 -0
  451. package/dist/params/resolve-schema.d.ts +34 -0
  452. package/dist/params/resolve-schema.d.ts.map +1 -0
  453. package/dist/plugin-context.d.ts +262 -0
  454. package/dist/plugin-context.d.ts.map +1 -0
  455. package/dist/plugins/build-manifest.d.ts +7 -3
  456. package/dist/plugins/build-manifest.d.ts.map +1 -1
  457. package/dist/plugins/build-report.d.ts +61 -11
  458. package/dist/plugins/build-report.d.ts.map +1 -1
  459. package/dist/plugins/cache.d.ts +64 -0
  460. package/dist/plugins/cache.d.ts.map +1 -0
  461. package/dist/plugins/callsite-ast.d.ts +117 -0
  462. package/dist/plugins/callsite-ast.d.ts.map +1 -0
  463. package/dist/plugins/callsite-jsx.d.ts +35 -0
  464. package/dist/plugins/callsite-jsx.d.ts.map +1 -0
  465. package/dist/plugins/chunks.d.ts +11 -4
  466. package/dist/plugins/chunks.d.ts.map +1 -1
  467. package/dist/plugins/client-chunks.d.ts +40 -0
  468. package/dist/plugins/client-chunks.d.ts.map +1 -0
  469. package/dist/plugins/client-dep-entries.d.ts +44 -0
  470. package/dist/plugins/client-dep-entries.d.ts.map +1 -0
  471. package/dist/plugins/cloudflare-wasm.d.ts +21 -0
  472. package/dist/plugins/cloudflare-wasm.d.ts.map +1 -0
  473. package/dist/plugins/content.d.ts +1 -1
  474. package/dist/plugins/content.d.ts.map +1 -1
  475. package/dist/plugins/dev-discovery.d.ts +123 -0
  476. package/dist/plugins/dev-discovery.d.ts.map +1 -0
  477. package/dist/plugins/dev-server.d.ts +24 -1
  478. package/dist/plugins/dev-server.d.ts.map +1 -1
  479. package/dist/plugins/entries.d.ts +34 -4
  480. package/dist/plugins/entries.d.ts.map +1 -1
  481. package/dist/plugins/fonts.d.ts +35 -74
  482. package/dist/plugins/fonts.d.ts.map +1 -1
  483. package/dist/plugins/graph-endpoint.d.ts +112 -0
  484. package/dist/plugins/graph-endpoint.d.ts.map +1 -0
  485. package/dist/plugins/mdx.d.ts +18 -5
  486. package/dist/plugins/mdx.d.ts.map +1 -1
  487. package/dist/plugins/post-build.d.ts +110 -0
  488. package/dist/plugins/post-build.d.ts.map +1 -0
  489. package/dist/plugins/prebuilt-capture.d.ts +67 -0
  490. package/dist/plugins/prebuilt-capture.d.ts.map +1 -0
  491. package/dist/plugins/prebuilt-options-analysis.d.ts +40 -0
  492. package/dist/plugins/prebuilt-options-analysis.d.ts.map +1 -0
  493. package/dist/plugins/prebuilt.d.ts +98 -0
  494. package/dist/plugins/prebuilt.d.ts.map +1 -0
  495. package/dist/plugins/prerender-sugar.d.ts +39 -0
  496. package/dist/plugins/prerender-sugar.d.ts.map +1 -0
  497. package/dist/plugins/react-resolve.d.ts +49 -0
  498. package/dist/plugins/react-resolve.d.ts.map +1 -0
  499. package/dist/plugins/request-dep/analysis.d.ts +106 -0
  500. package/dist/plugins/request-dep/analysis.d.ts.map +1 -0
  501. package/dist/plugins/request-dep/index.d.ts +75 -0
  502. package/dist/plugins/request-dep/index.d.ts.map +1 -0
  503. package/dist/plugins/request-dep/origin.d.ts +61 -0
  504. package/dist/plugins/request-dep/origin.d.ts.map +1 -0
  505. package/dist/plugins/routing.d.ts +1 -1
  506. package/dist/plugins/routing.d.ts.map +1 -1
  507. package/dist/plugins/rsc-assets-manifest.d.ts +33 -0
  508. package/dist/plugins/rsc-assets-manifest.d.ts.map +1 -0
  509. package/dist/plugins/server-bundle.d.ts.map +1 -1
  510. package/dist/plugins/shims.d.ts +29 -5
  511. package/dist/plugins/shims.d.ts.map +1 -1
  512. package/dist/plugins/static-build.d.ts +246 -26
  513. package/dist/plugins/static-build.d.ts.map +1 -1
  514. package/dist/routing/codegen-shared.d.ts +26 -0
  515. package/dist/routing/codegen-shared.d.ts.map +1 -0
  516. package/dist/routing/codegen-types.d.ts +61 -0
  517. package/dist/routing/codegen-types.d.ts.map +1 -0
  518. package/dist/routing/codegen-write.d.ts +42 -0
  519. package/dist/routing/codegen-write.d.ts.map +1 -0
  520. package/dist/routing/codegen.d.ts +8 -3
  521. package/dist/routing/codegen.d.ts.map +1 -1
  522. package/dist/routing/collision-probe.d.ts +104 -0
  523. package/dist/routing/collision-probe.d.ts.map +1 -0
  524. package/dist/routing/collision-spaces.d.ts +26 -0
  525. package/dist/routing/collision-spaces.d.ts.map +1 -0
  526. package/dist/routing/convention-lint.d.ts +55 -0
  527. package/dist/routing/convention-lint.d.ts.map +1 -0
  528. package/dist/routing/export-detect.d.ts +91 -0
  529. package/dist/routing/export-detect.d.ts.map +1 -0
  530. package/dist/routing/file-cache.d.ts +8 -0
  531. package/dist/routing/file-cache.d.ts.map +1 -0
  532. package/dist/routing/index.d.ts +13 -7
  533. package/dist/routing/index.d.ts.map +1 -1
  534. package/dist/routing/index.js +5 -2
  535. package/dist/routing/interception-overlap.d.ts +35 -0
  536. package/dist/routing/interception-overlap.d.ts.map +1 -0
  537. package/dist/routing/interception.d.ts +234 -10
  538. package/dist/routing/interception.d.ts.map +1 -1
  539. package/dist/routing/link-codegen.d.ts +105 -0
  540. package/dist/routing/link-codegen.d.ts.map +1 -0
  541. package/dist/routing/manifest-codegen.d.ts +26 -0
  542. package/dist/routing/manifest-codegen.d.ts.map +1 -0
  543. package/dist/routing/scanner.d.ts +14 -10
  544. package/dist/routing/scanner.d.ts.map +1 -1
  545. package/dist/routing/schema-validation.d.ts +35 -0
  546. package/dist/routing/schema-validation.d.ts.map +1 -0
  547. package/dist/routing/segment-classify.d.ts +114 -0
  548. package/dist/routing/segment-classify.d.ts.map +1 -0
  549. package/dist/routing/segment-codegen.d.ts +38 -0
  550. package/dist/routing/segment-codegen.d.ts.map +1 -0
  551. package/dist/routing/segment-keys.d.ts +118 -0
  552. package/dist/routing/segment-keys.d.ts.map +1 -0
  553. package/dist/routing/slot-placement.d.ts +48 -0
  554. package/dist/routing/slot-placement.d.ts.map +1 -0
  555. package/dist/routing/types.d.ts +81 -26
  556. package/dist/routing/types.d.ts.map +1 -1
  557. package/dist/routing/walkers.d.ts +57 -0
  558. package/dist/routing/walkers.d.ts.map +1 -0
  559. package/dist/rsc-runtime/rsc.d.ts +1 -1
  560. package/dist/rsc-runtime/rsc.d.ts.map +1 -1
  561. package/dist/rsc-runtime/ssr.d.ts +14 -0
  562. package/dist/rsc-runtime/ssr.d.ts.map +1 -1
  563. package/dist/rsc-runtime/transforms.d.ts +13 -0
  564. package/dist/rsc-runtime/transforms.d.ts.map +1 -0
  565. package/dist/schema-bridge.d.ts +145 -0
  566. package/dist/schema-bridge.d.ts.map +1 -0
  567. package/dist/search-params/define.d.ts +262 -0
  568. package/dist/search-params/define.d.ts.map +1 -0
  569. package/dist/search-params/index.d.ts +3 -7
  570. package/dist/search-params/index.d.ts.map +1 -1
  571. package/dist/search-params/index.js +179 -360
  572. package/dist/search-params/index.js.map +1 -1
  573. package/dist/search-params/parse-total.d.ts +81 -0
  574. package/dist/search-params/parse-total.d.ts.map +1 -0
  575. package/dist/search-params/serialize-equal.d.ts +9 -0
  576. package/dist/search-params/serialize-equal.d.ts.map +1 -0
  577. package/dist/search-params/wrappers.d.ts +76 -0
  578. package/dist/search-params/wrappers.d.ts.map +1 -0
  579. package/dist/server/access-gate.d.ts +98 -10
  580. package/dist/server/access-gate.d.ts.map +1 -1
  581. package/dist/server/action-client.d.ts +44 -9
  582. package/dist/server/action-client.d.ts.map +1 -1
  583. package/dist/server/action-encryption.d.ts +76 -0
  584. package/dist/server/action-encryption.d.ts.map +1 -0
  585. package/dist/server/action-handler.d.ts +40 -5
  586. package/dist/server/action-handler.d.ts.map +1 -1
  587. package/dist/server/actions.d.ts +78 -40
  588. package/dist/server/actions.d.ts.map +1 -1
  589. package/dist/server/als-registry.d.ts +166 -16
  590. package/dist/server/als-registry.d.ts.map +1 -1
  591. package/dist/server/body-limits.d.ts.map +1 -1
  592. package/dist/server/build-manifest.d.ts +25 -30
  593. package/dist/server/build-manifest.d.ts.map +1 -1
  594. package/dist/server/canonicalize.d.ts +10 -0
  595. package/dist/server/canonicalize.d.ts.map +1 -1
  596. package/dist/server/chain-url-parts.d.ts +82 -0
  597. package/dist/server/chain-url-parts.d.ts.map +1 -0
  598. package/dist/server/children-interception.d.ts +49 -0
  599. package/dist/server/children-interception.d.ts.map +1 -0
  600. package/dist/server/coalesce-tick.d.ts +40 -0
  601. package/dist/server/coalesce-tick.d.ts.map +1 -0
  602. package/dist/server/compress.d.ts.map +1 -1
  603. package/dist/server/cookie-context.d.ts +149 -0
  604. package/dist/server/cookie-context.d.ts.map +1 -0
  605. package/dist/server/cookie-parsing.d.ts +65 -0
  606. package/dist/server/cookie-parsing.d.ts.map +1 -0
  607. package/dist/server/csp-nonce.d.ts +36 -0
  608. package/dist/server/csp-nonce.d.ts.map +1 -0
  609. package/dist/server/csrf.d.ts +58 -15
  610. package/dist/server/csrf.d.ts.map +1 -1
  611. package/dist/server/debug.d.ts +0 -1
  612. package/dist/server/debug.d.ts.map +1 -1
  613. package/dist/server/default-logger.d.ts +23 -0
  614. package/dist/server/default-logger.d.ts.map +1 -0
  615. package/dist/server/default-status-page.d.ts +45 -0
  616. package/dist/server/default-status-page.d.ts.map +1 -0
  617. package/dist/server/deny-boundary.d.ts +257 -0
  618. package/dist/server/deny-boundary.d.ts.map +1 -0
  619. package/dist/server/deny-renderer.d.ts +21 -15
  620. package/dist/server/deny-renderer.d.ts.map +1 -1
  621. package/dist/server/early-hints-sender.d.ts.map +1 -1
  622. package/dist/server/early-hints.d.ts +14 -12
  623. package/dist/server/early-hints.d.ts.map +1 -1
  624. package/dist/server/error-boundary-wrapper.d.ts +102 -10
  625. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  626. package/dist/server/error-response-headers.d.ts +3 -0
  627. package/dist/server/error-response-headers.d.ts.map +1 -0
  628. package/dist/server/fallback-error.d.ts +19 -7
  629. package/dist/server/fallback-error.d.ts.map +1 -1
  630. package/dist/server/flight-injection-state.d.ts +66 -0
  631. package/dist/server/flight-injection-state.d.ts.map +1 -0
  632. package/dist/server/flight-scripts.d.ts +45 -0
  633. package/dist/server/flight-scripts.d.ts.map +1 -0
  634. package/dist/server/form-data.d.ts +41 -0
  635. package/dist/server/form-data.d.ts.map +1 -1
  636. package/dist/server/form-flash.d.ts +2 -2
  637. package/dist/server/head-response.d.ts +35 -0
  638. package/dist/server/head-response.d.ts.map +1 -0
  639. package/dist/server/html-injector-core.d.ts +257 -0
  640. package/dist/server/html-injector-core.d.ts.map +1 -0
  641. package/dist/server/html-injectors.d.ts +84 -42
  642. package/dist/server/html-injectors.d.ts.map +1 -1
  643. package/dist/server/index.d.ts +17 -54
  644. package/dist/server/index.d.ts.map +1 -1
  645. package/dist/server/index.js +195 -2922
  646. package/dist/server/index.js.map +1 -1
  647. package/dist/server/instrumentation.d.ts +11 -4
  648. package/dist/server/instrumentation.d.ts.map +1 -1
  649. package/dist/server/internal.d.ts +47 -0
  650. package/dist/server/internal.d.ts.map +1 -0
  651. package/dist/server/internal.js +4645 -0
  652. package/dist/server/internal.js.map +1 -0
  653. package/dist/server/json-status-response.d.ts +34 -0
  654. package/dist/server/json-status-response.d.ts.map +1 -0
  655. package/dist/server/logger.d.ts +78 -7
  656. package/dist/server/logger.d.ts.map +1 -1
  657. package/dist/server/metadata-collector.d.ts +73 -0
  658. package/dist/server/metadata-collector.d.ts.map +1 -0
  659. package/dist/server/metadata-platform.d.ts +2 -2
  660. package/dist/server/metadata-render.d.ts +2 -2
  661. package/dist/server/metadata-render.d.ts.map +1 -1
  662. package/dist/server/metadata-routes.d.ts +71 -11
  663. package/dist/server/metadata-routes.d.ts.map +1 -1
  664. package/dist/server/metadata-social.d.ts +2 -2
  665. package/dist/server/metadata.d.ts +10 -2
  666. package/dist/server/metadata.d.ts.map +1 -1
  667. package/dist/server/middleware-runner.d.ts +20 -5
  668. package/dist/server/middleware-runner.d.ts.map +1 -1
  669. package/dist/server/node-stream-transforms.d.ts +105 -0
  670. package/dist/server/node-stream-transforms.d.ts.map +1 -0
  671. package/dist/server/nuqs-ssr-provider.d.ts +7 -3
  672. package/dist/server/nuqs-ssr-provider.d.ts.map +1 -1
  673. package/dist/server/param-coercion.d.ts +83 -0
  674. package/dist/server/param-coercion.d.ts.map +1 -0
  675. package/dist/server/pipeline-helpers.d.ts +193 -0
  676. package/dist/server/pipeline-helpers.d.ts.map +1 -0
  677. package/dist/server/pipeline-interception.d.ts +70 -9
  678. package/dist/server/pipeline-interception.d.ts.map +1 -1
  679. package/dist/server/pipeline-metadata.d.ts +7 -1
  680. package/dist/server/pipeline-metadata.d.ts.map +1 -1
  681. package/dist/server/pipeline-outcome.d.ts +59 -0
  682. package/dist/server/pipeline-outcome.d.ts.map +1 -0
  683. package/dist/server/pipeline-phases.d.ts +70 -0
  684. package/dist/server/pipeline-phases.d.ts.map +1 -0
  685. package/dist/server/pipeline.d.ts +147 -23
  686. package/dist/server/pipeline.d.ts.map +1 -1
  687. package/dist/server/port-resolution.d.ts +31 -0
  688. package/dist/server/port-resolution.d.ts.map +1 -0
  689. package/dist/server/prebuilt/cache-key.d.ts +63 -0
  690. package/dist/server/prebuilt/cache-key.d.ts.map +1 -0
  691. package/dist/server/prebuilt/capture-render.d.ts +25 -0
  692. package/dist/server/prebuilt/capture-render.d.ts.map +1 -0
  693. package/dist/server/prebuilt/capture-routes.d.ts +37 -0
  694. package/dist/server/prebuilt/capture-routes.d.ts.map +1 -0
  695. package/dist/server/prebuilt/capture-state.d.ts +101 -0
  696. package/dist/server/prebuilt/capture-state.d.ts.map +1 -0
  697. package/dist/server/prebuilt/key-discipline.d.ts +52 -0
  698. package/dist/server/prebuilt/key-discipline.d.ts.map +1 -0
  699. package/dist/server/prebuilt/lifetime.d.ts +38 -0
  700. package/dist/server/prebuilt/lifetime.d.ts.map +1 -0
  701. package/dist/server/prebuilt/manifest-tag-index.d.ts +27 -0
  702. package/dist/server/prebuilt/manifest-tag-index.d.ts.map +1 -0
  703. package/dist/server/prebuilt/overlay.d.ts +100 -0
  704. package/dist/server/prebuilt/overlay.d.ts.map +1 -0
  705. package/dist/server/prebuilt/payload-source.d.ts +79 -0
  706. package/dist/server/prebuilt/payload-source.d.ts.map +1 -0
  707. package/dist/server/prebuilt/slots.d.ts +106 -0
  708. package/dist/server/prebuilt/slots.d.ts.map +1 -0
  709. package/dist/server/prebuilt/synthetic-store.d.ts +67 -0
  710. package/dist/server/prebuilt/synthetic-store.d.ts.map +1 -0
  711. package/dist/server/prebuilt-builder.d.ts +121 -0
  712. package/dist/server/prebuilt-builder.d.ts.map +1 -0
  713. package/dist/server/prebuilt-runtime.d.ts +111 -0
  714. package/dist/server/prebuilt-runtime.d.ts.map +1 -0
  715. package/dist/server/primitives.d.ts +101 -90
  716. package/dist/server/primitives.d.ts.map +1 -1
  717. package/dist/server/publish-params.d.ts +33 -0
  718. package/dist/server/publish-params.d.ts.map +1 -0
  719. package/dist/server/react-cache-bridge.d.ts +59 -0
  720. package/dist/server/react-cache-bridge.d.ts.map +1 -0
  721. package/dist/server/react-cache-scope.d.ts +49 -0
  722. package/dist/server/react-cache-scope.d.ts.map +1 -0
  723. package/dist/server/render-timeout.d.ts +66 -0
  724. package/dist/server/render-timeout.d.ts.map +1 -0
  725. package/dist/server/render-utils.d.ts +35 -0
  726. package/dist/server/render-utils.d.ts.map +1 -0
  727. package/dist/server/request-context.d.ts +128 -103
  728. package/dist/server/request-context.d.ts.map +1 -1
  729. package/dist/server/request-dep.d.ts +15 -0
  730. package/dist/server/request-dep.d.ts.map +1 -0
  731. package/dist/server/response-cache-policy.d.ts +3 -0
  732. package/dist/server/response-cache-policy.d.ts.map +1 -0
  733. package/dist/server/route-element-builder.d.ts +57 -30
  734. package/dist/server/route-element-builder.d.ts.map +1 -1
  735. package/dist/server/route-element-helpers.d.ts +12 -0
  736. package/dist/server/route-element-helpers.d.ts.map +1 -0
  737. package/dist/server/route-handler.d.ts +1 -1
  738. package/dist/server/route-handler.d.ts.map +1 -1
  739. package/dist/server/route-matcher.d.ts +55 -38
  740. package/dist/server/route-matcher.d.ts.map +1 -1
  741. package/dist/server/route-module-loader.d.ts +37 -0
  742. package/dist/server/route-module-loader.d.ts.map +1 -0
  743. package/dist/server/rsc-cache-key-guard.d.ts +78 -0
  744. package/dist/server/rsc-cache-key-guard.d.ts.map +1 -0
  745. package/dist/server/rsc-entry/action-dispatcher.d.ts +67 -0
  746. package/dist/server/rsc-entry/action-dispatcher.d.ts.map +1 -0
  747. package/dist/server/rsc-entry/action-middleware-runner.d.ts +66 -0
  748. package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -0
  749. package/dist/server/rsc-entry/api-handler.d.ts +19 -3
  750. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
  751. package/dist/server/rsc-entry/deny-fallback.d.ts +55 -0
  752. package/dist/server/rsc-entry/deny-fallback.d.ts.map +1 -0
  753. package/dist/server/rsc-entry/error-renderer.d.ts +35 -21
  754. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  755. package/dist/server/rsc-entry/helpers.d.ts +14 -59
  756. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  757. package/dist/server/rsc-entry/index.d.ts +30 -6
  758. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  759. package/dist/server/rsc-entry/render-route.d.ts +50 -0
  760. package/dist/server/rsc-entry/render-route.d.ts.map +1 -0
  761. package/dist/server/rsc-entry/render-signals.d.ts +65 -0
  762. package/dist/server/rsc-entry/render-signals.d.ts.map +1 -0
  763. package/dist/server/rsc-entry/revalidate-renderer.d.ts +98 -0
  764. package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -0
  765. package/dist/server/rsc-entry/rsc-payload.d.ts +30 -6
  766. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  767. package/dist/server/rsc-entry/rsc-stream.d.ts +12 -30
  768. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  769. package/dist/server/rsc-entry/ssr-bridge.d.ts +6 -2
  770. package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
  771. package/dist/server/rsc-entry/ssr-renderer.d.ts +44 -7
  772. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  773. package/dist/server/rsc-error-envelope.d.ts +11 -0
  774. package/dist/server/rsc-error-envelope.d.ts.map +1 -0
  775. package/dist/server/safe-load.d.ts +39 -0
  776. package/dist/server/safe-load.d.ts.map +1 -0
  777. package/dist/server/sensitive-fields.d.ts +74 -0
  778. package/dist/server/sensitive-fields.d.ts.map +1 -0
  779. package/dist/server/signal-identity.d.ts +43 -0
  780. package/dist/server/signal-identity.d.ts.map +1 -0
  781. package/dist/server/sitemap-generator.d.ts +130 -0
  782. package/dist/server/sitemap-generator.d.ts.map +1 -0
  783. package/dist/server/sitemap-handler.d.ts +22 -0
  784. package/dist/server/sitemap-handler.d.ts.map +1 -0
  785. package/dist/server/skippable-prefix.d.ts +29 -0
  786. package/dist/server/skippable-prefix.d.ts.map +1 -0
  787. package/dist/server/slot-interception.d.ts +80 -0
  788. package/dist/server/slot-interception.d.ts.map +1 -0
  789. package/dist/server/slot-resolver.d.ts +138 -7
  790. package/dist/server/slot-resolver.d.ts.map +1 -1
  791. package/dist/server/slot-subtree-contain.d.ts +54 -0
  792. package/dist/server/slot-subtree-contain.d.ts.map +1 -0
  793. package/dist/server/ssr-bridge-types.d.ts +198 -0
  794. package/dist/server/ssr-bridge-types.d.ts.map +1 -0
  795. package/dist/server/ssr-entry.d.ts +8 -47
  796. package/dist/server/ssr-entry.d.ts.map +1 -1
  797. package/dist/server/ssr-render.d.ts +60 -27
  798. package/dist/server/ssr-render.d.ts.map +1 -1
  799. package/dist/server/ssr-wrappers.d.ts +93 -0
  800. package/dist/server/ssr-wrappers.d.ts.map +1 -0
  801. package/dist/server/state-tree-diff.d.ts +44 -9
  802. package/dist/server/state-tree-diff.d.ts.map +1 -1
  803. package/dist/server/static-generator.d.ts +112 -0
  804. package/dist/server/static-generator.d.ts.map +1 -0
  805. package/dist/server/static-not-found.d.ts +52 -0
  806. package/dist/server/static-not-found.d.ts.map +1 -0
  807. package/dist/server/status-code-resolver.d.ts +26 -15
  808. package/dist/server/status-code-resolver.d.ts.map +1 -1
  809. package/dist/server/stream-utils.d.ts +59 -0
  810. package/dist/server/stream-utils.d.ts.map +1 -0
  811. package/dist/server/tracing.d.ts +57 -26
  812. package/dist/server/tracing.d.ts.map +1 -1
  813. package/dist/server/tree-match.d.ts +59 -0
  814. package/dist/server/tree-match.d.ts.map +1 -0
  815. package/dist/server/types.d.ts +89 -8
  816. package/dist/server/types.d.ts.map +1 -1
  817. package/dist/server/utils/element-type.d.ts +50 -0
  818. package/dist/server/utils/element-type.d.ts.map +1 -0
  819. package/dist/server/utils/escape-html.d.ts +14 -0
  820. package/dist/server/utils/escape-html.d.ts.map +1 -0
  821. package/dist/server/version-skew.d.ts +61 -0
  822. package/dist/server/version-skew.d.ts.map +1 -0
  823. package/dist/server/workers-cache-bridge.d.ts +26 -0
  824. package/dist/server/workers-cache-bridge.d.ts.map +1 -0
  825. package/dist/shared/als-slots.d.ts +42 -0
  826. package/dist/shared/als-slots.d.ts.map +1 -0
  827. package/dist/shared/base-path.d.ts +56 -0
  828. package/dist/shared/base-path.d.ts.map +1 -0
  829. package/dist/shared/csrf-reject.d.ts +16 -0
  830. package/dist/shared/csrf-reject.d.ts.map +1 -0
  831. package/dist/shared/error-digest.d.ts +28 -0
  832. package/dist/shared/error-digest.d.ts.map +1 -0
  833. package/dist/shared/href-validation.d.ts +49 -0
  834. package/dist/shared/href-validation.d.ts.map +1 -0
  835. package/dist/shared/merge-search-params.d.ts +22 -0
  836. package/dist/shared/merge-search-params.d.ts.map +1 -0
  837. package/dist/shared/param-value.d.ts +104 -0
  838. package/dist/shared/param-value.d.ts.map +1 -0
  839. package/dist/shared/payload-root.d.ts +110 -0
  840. package/dist/shared/payload-root.d.ts.map +1 -0
  841. package/dist/shared/redirect-type.d.ts +17 -0
  842. package/dist/shared/redirect-type.d.ts.map +1 -0
  843. package/dist/shared/rsc-cache-key.d.ts +163 -0
  844. package/dist/shared/rsc-cache-key.d.ts.map +1 -0
  845. package/dist/shared/rsc-error-envelope.d.ts +16 -0
  846. package/dist/shared/rsc-error-envelope.d.ts.map +1 -0
  847. package/dist/shared/rsc-manifest.d.ts +28 -0
  848. package/dist/shared/rsc-manifest.d.ts.map +1 -0
  849. package/dist/shared/rsc-media-type.d.ts +52 -0
  850. package/dist/shared/rsc-media-type.d.ts.map +1 -0
  851. package/dist/shared/rsc-payload-path.d.ts +48 -0
  852. package/dist/shared/rsc-payload-path.d.ts.map +1 -0
  853. package/dist/shared/segment-info.d.ts +66 -0
  854. package/dist/shared/segment-info.d.ts.map +1 -0
  855. package/dist/shared/slot-params.d.ts +52 -0
  856. package/dist/shared/slot-params.d.ts.map +1 -0
  857. package/dist/shared/static-platform-files.d.ts +71 -0
  858. package/dist/shared/static-platform-files.d.ts.map +1 -0
  859. package/dist/shared/status-reason-phrase.d.ts +26 -0
  860. package/dist/shared/status-reason-phrase.d.ts.map +1 -0
  861. package/dist/shims/font-google.d.ts +1 -1
  862. package/dist/shims/font-google.d.ts.map +1 -1
  863. package/dist/shims/font-google.js +42 -0
  864. package/dist/shims/font-google.js.map +1 -0
  865. package/dist/shims/font-local.d.ts +26 -0
  866. package/dist/shims/font-local.d.ts.map +1 -0
  867. package/dist/shims/font-local.js +20 -0
  868. package/dist/shims/font-local.js.map +1 -0
  869. package/dist/shims/headers.d.ts +53 -1
  870. package/dist/shims/headers.d.ts.map +1 -1
  871. package/dist/shims/image.d.ts +120 -120
  872. package/dist/shims/image.d.ts.map +1 -1
  873. package/dist/shims/navigation-client.d.ts +25 -12
  874. package/dist/shims/navigation-client.d.ts.map +1 -1
  875. package/dist/shims/navigation-rsc.d.ts +21 -0
  876. package/dist/shims/navigation-rsc.d.ts.map +1 -0
  877. package/dist/shims/navigation.d.ts +10 -5
  878. package/dist/shims/navigation.d.ts.map +1 -1
  879. package/dist/utils/state-machine.d.ts +80 -0
  880. package/dist/utils/state-machine.d.ts.map +1 -0
  881. package/dist/utils/write-file-atomic.d.ts +17 -0
  882. package/dist/utils/write-file-atomic.d.ts.map +1 -0
  883. package/docs/api/30-api-server.mdx +239 -0
  884. package/docs/api/31-api-client.mdx +170 -0
  885. package/docs/api/32-api-cache.mdx +339 -0
  886. package/docs/api/33-api-search-params.mdx +176 -0
  887. package/docs/api/34-api-config.mdx +359 -0
  888. package/docs/api/35-api-typescript.mdx +91 -0
  889. package/docs/api/36-cli.mdx +202 -0
  890. package/docs/learn/00-introduction.mdx +109 -0
  891. package/docs/learn/01-quick-start.mdx +94 -0
  892. package/docs/learn/02-pages-and-layouts.mdx +147 -0
  893. package/docs/learn/03-fetching-data.mdx +227 -0
  894. package/docs/learn/04-access-control.mdx +115 -0
  895. package/docs/learn/05-the-flush-point.mdx +175 -0
  896. package/docs/learn/06-typed-params.mdx +275 -0
  897. package/docs/learn/07-typed-routes.mdx +108 -0
  898. package/docs/learn/08-forms-and-actions.mdx +146 -0
  899. package/docs/learn/09-streaming.mdx +140 -0
  900. package/docs/learn/10-caching.mdx +270 -0
  901. package/docs/learn/11-middleware.mdx +164 -0
  902. package/docs/learn/12-error-handling.mdx +192 -0
  903. package/docs/learn/13-deploying.mdx +287 -0
  904. package/llms.txt +55 -0
  905. package/package.json +120 -29
  906. package/src/adapters/build-output-helper.ts +353 -0
  907. package/src/adapters/canonicalize-module.ts +29 -0
  908. package/src/adapters/cloudflare-dev.ts +183 -0
  909. package/src/adapters/cloudflare-kv-cache.ts +207 -0
  910. package/src/adapters/cloudflare.ts +809 -150
  911. package/src/adapters/compress-module.ts +110 -86
  912. package/src/adapters/fs-identity.ts +155 -0
  913. package/src/adapters/nitro-presets.ts +124 -0
  914. package/src/adapters/nitro-preview.ts +476 -0
  915. package/src/adapters/nitro.ts +317 -457
  916. package/src/adapters/raw-imports.d.ts +5 -0
  917. package/src/adapters/shared.ts +283 -0
  918. package/src/adapters/types.ts +127 -17
  919. package/src/adapters/wrangler.d.ts +7 -0
  920. package/src/analyze/chains.ts +411 -0
  921. package/src/analyze/classify.ts +259 -0
  922. package/src/analyze/crawl-entry.ts +68 -0
  923. package/src/analyze/crawl.ts +500 -0
  924. package/src/analyze/graph-cache.ts +331 -0
  925. package/src/analyze/graph-command.ts +762 -0
  926. package/src/analyze/live-graph.ts +232 -0
  927. package/src/analyze/poison-rules.ts +165 -0
  928. package/src/analyze/poison-scan.ts +159 -0
  929. package/src/analyze/scan.ts +149 -0
  930. package/src/cache/cache-api.ts +107 -0
  931. package/src/cache/fast-hash.ts +5 -0
  932. package/src/cache/handler-store.ts +76 -0
  933. package/src/cache/index.ts +115 -22
  934. package/src/cache/invalidation-epoch.ts +93 -0
  935. package/src/cache/json-lossy-check.ts +69 -0
  936. package/src/cache/redis-handler.ts +198 -20
  937. package/src/cache/singleflight.ts +72 -6
  938. package/src/cache/sizeof.ts +35 -0
  939. package/src/cache/stable-stringify.ts +65 -2
  940. package/src/cache/store.ts +71 -0
  941. package/src/cache/stores/cloudflare-kv.ts +52 -0
  942. package/src/cache/stores/memory.ts +125 -0
  943. package/src/cache/stores/redis.ts +97 -0
  944. package/src/cache/stores/vercel.ts +59 -0
  945. package/src/cache/tag-aware-handler.ts +254 -0
  946. package/src/cache/timber-cache.ts +165 -40
  947. package/src/cdn/cloudflare-purge.ts +50 -0
  948. package/src/cdn/fastly-purge.ts +63 -0
  949. package/src/cdn/headers.ts +153 -0
  950. package/src/cdn/index.ts +23 -0
  951. package/src/cdn/purge-store.ts +34 -0
  952. package/src/cdn/workers-cache-purge.ts +34 -0
  953. package/src/cli-check.ts +417 -0
  954. package/src/cli-context.ts +119 -0
  955. package/src/cli-schema-sync.ts +401 -0
  956. package/src/cli.ts +330 -78
  957. package/src/client/browser-dev.ts +192 -5
  958. package/src/client/browser-entry/action-dispatch.ts +237 -0
  959. package/src/client/browser-entry/action-queue.ts +152 -0
  960. package/src/client/browser-entry/hmr.ts +70 -0
  961. package/src/client/browser-entry/hydrate.ts +143 -0
  962. package/src/client/browser-entry/index.ts +120 -0
  963. package/src/client/browser-entry/post-hydration.ts +99 -0
  964. package/src/client/browser-entry/router-init.ts +406 -0
  965. package/src/client/browser-entry/rsc-stream.ts +145 -0
  966. package/src/client/browser-entry/scroll.ts +50 -0
  967. package/src/client/deny-last-resort.tsx +54 -0
  968. package/src/client/error-boundary.tsx +279 -61
  969. package/src/client/error-reconstituter.tsx +84 -0
  970. package/src/client/form.tsx +36 -32
  971. package/src/client/global-context.ts +31 -0
  972. package/src/client/history.ts +74 -11
  973. package/src/client/index.ts +89 -53
  974. package/src/client/internal.ts +54 -0
  975. package/src/client/link.tsx +468 -151
  976. package/src/client/location-search.ts +15 -0
  977. package/src/client/navigation-api.ts +380 -0
  978. package/src/client/navigation-commit.ts +318 -0
  979. package/src/client/navigation-context.ts +67 -134
  980. package/src/client/navigation-root.tsx +236 -0
  981. package/src/client/navigation-transition.ts +337 -0
  982. package/src/client/nuqs-adapter.tsx +21 -9
  983. package/src/client/params-context.ts +187 -0
  984. package/src/client/react-root.ts +82 -0
  985. package/src/client/router-effects.ts +235 -0
  986. package/src/client/router-lifecycle.ts +354 -0
  987. package/src/client/router-pipeline.ts +458 -0
  988. package/src/client/router-ref.ts +3 -3
  989. package/src/client/router-skew.ts +30 -0
  990. package/src/client/router-types.ts +370 -0
  991. package/src/client/router-url.ts +32 -0
  992. package/src/client/router.ts +491 -424
  993. package/src/client/rsc-fetch.ts +510 -109
  994. package/src/client/segment-cache.ts +362 -41
  995. package/src/client/segment-context.ts +7 -4
  996. package/src/client/segment-outlet.tsx +96 -0
  997. package/src/client/segment-update-context.ts +23 -0
  998. package/src/client/shallow-url.ts +22 -0
  999. package/src/client/slot-content-cache-context.ts +43 -0
  1000. package/src/client/slot-context.ts +50 -0
  1001. package/src/client/slot-outlet.tsx +22 -0
  1002. package/src/client/slot-provider.tsx +32 -0
  1003. package/src/client/ssr-data.ts +27 -67
  1004. package/src/client/stale-client.ts +191 -0
  1005. package/src/client/state.ts +31 -24
  1006. package/src/client/status-page-marker.tsx +32 -0
  1007. package/src/client/top-loader.tsx +13 -10
  1008. package/src/client/unload-guard.ts +35 -3
  1009. package/src/client/use-cookie.ts +70 -18
  1010. package/src/client/use-link-status.ts +7 -7
  1011. package/src/client/use-pathname.ts +22 -31
  1012. package/src/client/use-pending-navigation.ts +40 -0
  1013. package/src/client/use-query-states.ts +206 -36
  1014. package/src/client/use-router.ts +31 -9
  1015. package/src/client/use-search-params.ts +38 -49
  1016. package/src/client/use-segment-params.ts +69 -0
  1017. package/src/client/use-selected-layout-segment.ts +2 -2
  1018. package/src/codec.ts +149 -0
  1019. package/src/config-types.ts +323 -0
  1020. package/src/config-validation.ts +464 -0
  1021. package/src/cookies/define-cookie.ts +222 -53
  1022. package/src/cookies/index.ts +13 -2
  1023. package/src/cookies/json-cookie.ts +105 -0
  1024. package/src/cookies/validation.ts +192 -0
  1025. package/src/dev-tools/404-page.ts +286 -0
  1026. package/src/dev-tools/browser-logs.ts +55 -0
  1027. package/src/dev-tools/dev-page-shell.ts +278 -0
  1028. package/src/dev-tools/error-page.ts +331 -0
  1029. package/src/dev-tools/holding-server.ts +185 -0
  1030. package/src/dev-tools/hydration-overlay.ts +158 -0
  1031. package/src/dev-tools/index.ts +79 -0
  1032. package/src/dev-tools/instrumentation.ts +261 -0
  1033. package/src/{server/dev-logger.ts → dev-tools/logger.ts} +33 -51
  1034. package/src/{plugins/dev-logs.ts → dev-tools/logs.ts} +122 -26
  1035. package/src/{plugins/dev-error-overlay.ts → dev-tools/overlay.ts} +25 -80
  1036. package/src/dev-tools/stack-classifier.ts +75 -0
  1037. package/src/dev-tools/terminal.ts +182 -0
  1038. package/src/fonts/ast-walk.ts +235 -0
  1039. package/src/fonts/ast.ts +479 -128
  1040. package/src/fonts/bundle.ts +138 -0
  1041. package/src/fonts/css.ts +1 -1
  1042. package/src/fonts/dev-middleware.ts +85 -0
  1043. package/src/fonts/fallbacks.ts +0 -23
  1044. package/src/fonts/google.ts +101 -19
  1045. package/src/fonts/local.ts +12 -11
  1046. package/src/fonts/paths.ts +58 -0
  1047. package/src/fonts/pipeline.ts +348 -0
  1048. package/src/fonts/transform.ts +311 -0
  1049. package/src/fonts/types.ts +52 -1
  1050. package/src/fonts/virtual-modules.ts +179 -0
  1051. package/src/index.ts +513 -380
  1052. package/src/params/codec-registry.ts +34 -0
  1053. package/src/params/define-schema.ts +115 -0
  1054. package/src/params/index.ts +17 -0
  1055. package/src/params/resolve-schema.ts +84 -0
  1056. package/src/plugin-context.ts +502 -0
  1057. package/src/plugins/build-manifest.ts +204 -36
  1058. package/src/plugins/build-report.ts +299 -87
  1059. package/src/plugins/cache.ts +243 -0
  1060. package/src/plugins/callsite-ast.ts +472 -0
  1061. package/src/plugins/callsite-jsx.ts +243 -0
  1062. package/src/plugins/chunks.ts +74 -5
  1063. package/src/plugins/client-chunks.ts +185 -0
  1064. package/src/plugins/client-dep-entries.ts +78 -0
  1065. package/src/plugins/cloudflare-wasm.ts +141 -0
  1066. package/src/plugins/content.ts +31 -2
  1067. package/src/plugins/dev-discovery.ts +218 -0
  1068. package/src/plugins/dev-server.ts +360 -97
  1069. package/src/plugins/entries.ts +149 -16
  1070. package/src/plugins/fonts.ts +260 -657
  1071. package/src/plugins/graph-endpoint.ts +532 -0
  1072. package/src/plugins/mdx.ts +108 -35
  1073. package/src/plugins/post-build.ts +327 -0
  1074. package/src/plugins/prebuilt-capture.ts +213 -0
  1075. package/src/plugins/prebuilt-options-analysis.ts +175 -0
  1076. package/src/plugins/prebuilt.ts +670 -0
  1077. package/src/plugins/prerender-sugar.ts +316 -0
  1078. package/src/plugins/react-resolve.ts +149 -0
  1079. package/src/plugins/request-dep/analysis.ts +447 -0
  1080. package/src/plugins/request-dep/index.ts +376 -0
  1081. package/src/plugins/request-dep/origin.ts +310 -0
  1082. package/src/plugins/routing.ts +273 -277
  1083. package/src/plugins/rsc-assets-manifest.ts +251 -0
  1084. package/src/plugins/server-bundle.ts +99 -14
  1085. package/src/plugins/shims.ts +260 -97
  1086. package/src/plugins/static-build.ts +681 -71
  1087. package/src/react-canary.d.ts +2 -0
  1088. package/src/routing/codegen-shared.ts +39 -0
  1089. package/src/routing/codegen-types.ts +62 -0
  1090. package/src/routing/codegen-write.ts +163 -0
  1091. package/src/routing/codegen.ts +349 -232
  1092. package/src/routing/collision-probe.ts +438 -0
  1093. package/src/routing/collision-spaces.ts +281 -0
  1094. package/src/routing/convention-lint.ts +651 -0
  1095. package/src/routing/export-detect.ts +536 -0
  1096. package/src/routing/file-cache.ts +23 -0
  1097. package/src/routing/index.ts +16 -7
  1098. package/src/routing/interception-overlap.ts +141 -0
  1099. package/src/routing/interception.ts +1044 -67
  1100. package/src/routing/link-codegen.ts +280 -0
  1101. package/src/routing/manifest-codegen.ts +232 -0
  1102. package/src/routing/scanner.ts +417 -223
  1103. package/src/routing/schema-validation.ts +155 -0
  1104. package/src/routing/segment-classify.ts +280 -0
  1105. package/src/routing/segment-codegen.ts +99 -0
  1106. package/src/routing/segment-keys.ts +186 -0
  1107. package/src/routing/slot-placement.ts +97 -0
  1108. package/src/routing/types.ts +82 -26
  1109. package/src/routing/walkers.ts +98 -0
  1110. package/src/rsc-runtime/rsc.ts +9 -0
  1111. package/src/rsc-runtime/ssr.ts +51 -0
  1112. package/src/rsc-runtime/transforms.ts +13 -0
  1113. package/src/rsc-runtime/vendor-types.d.ts +27 -0
  1114. package/src/schema-bridge.ts +382 -0
  1115. package/src/search-params/define.ts +677 -0
  1116. package/src/search-params/index.ts +14 -20
  1117. package/src/search-params/parse-total.ts +91 -0
  1118. package/src/search-params/serialize-equal.ts +14 -0
  1119. package/src/search-params/wrappers.ts +150 -0
  1120. package/src/server/access-gate.tsx +195 -56
  1121. package/src/server/action-client.ts +126 -33
  1122. package/src/server/action-encryption.ts +144 -0
  1123. package/src/server/action-handler.ts +457 -111
  1124. package/src/server/actions.ts +172 -85
  1125. package/src/server/als-registry.ts +224 -25
  1126. package/src/server/body-limits.ts +10 -2
  1127. package/src/server/build-manifest.ts +33 -57
  1128. package/src/server/canonicalize.ts +6 -3
  1129. package/src/server/chain-url-parts.ts +163 -0
  1130. package/src/server/children-interception.ts +116 -0
  1131. package/src/server/coalesce-tick.ts +75 -0
  1132. package/src/server/compress.ts +43 -8
  1133. package/src/server/cookie-context.ts +454 -0
  1134. package/src/server/cookie-parsing.ts +163 -0
  1135. package/src/server/csp-nonce.ts +58 -0
  1136. package/src/server/csrf.ts +228 -35
  1137. package/src/server/debug.ts +0 -1
  1138. package/src/server/default-logger.ts +100 -0
  1139. package/src/server/default-status-page.ts +88 -0
  1140. package/src/server/deny-boundary.ts +527 -0
  1141. package/src/server/deny-renderer.ts +201 -179
  1142. package/src/server/early-hints-sender.ts +4 -3
  1143. package/src/server/early-hints.ts +39 -29
  1144. package/src/server/error-boundary-wrapper.ts +160 -44
  1145. package/src/server/error-response-headers.ts +18 -0
  1146. package/src/server/fallback-error.ts +88 -104
  1147. package/src/server/flight-injection-state.ts +113 -0
  1148. package/src/server/flight-scripts.ts +67 -0
  1149. package/src/server/form-data.ts +124 -12
  1150. package/src/server/form-flash.ts +3 -3
  1151. package/src/server/head-response.ts +54 -0
  1152. package/src/server/html-injector-core.ts +512 -0
  1153. package/src/server/html-injectors.ts +270 -255
  1154. package/src/server/index.ts +36 -192
  1155. package/src/server/instrumentation.ts +11 -4
  1156. package/src/server/internal.ts +168 -0
  1157. package/src/server/json-status-response.ts +105 -0
  1158. package/src/server/logger.ts +126 -38
  1159. package/src/server/metadata-collector.ts +177 -0
  1160. package/src/server/metadata-platform.ts +2 -2
  1161. package/src/server/metadata-render.ts +30 -8
  1162. package/src/server/metadata-routes.ts +220 -24
  1163. package/src/server/metadata-social.ts +2 -2
  1164. package/src/server/metadata.ts +23 -2
  1165. package/src/server/middleware-runner.ts +32 -5
  1166. package/src/server/node-stream-transforms.ts +323 -0
  1167. package/src/server/nuqs-ssr-provider.tsx +8 -7
  1168. package/src/server/param-coercion.ts +201 -0
  1169. package/src/server/pipeline-helpers.ts +485 -0
  1170. package/src/server/pipeline-interception.ts +241 -32
  1171. package/src/server/pipeline-metadata.ts +15 -1
  1172. package/src/server/pipeline-outcome.ts +290 -0
  1173. package/src/server/pipeline-phases.ts +496 -0
  1174. package/src/server/pipeline.ts +385 -427
  1175. package/src/server/port-resolution.ts +53 -0
  1176. package/src/server/prebuilt/cache-key.ts +299 -0
  1177. package/src/server/prebuilt/capture-render.ts +118 -0
  1178. package/src/server/prebuilt/capture-routes.ts +104 -0
  1179. package/src/server/prebuilt/capture-state.ts +196 -0
  1180. package/src/server/prebuilt/key-discipline.ts +122 -0
  1181. package/src/server/prebuilt/lifetime.ts +151 -0
  1182. package/src/server/prebuilt/manifest-tag-index.ts +46 -0
  1183. package/src/server/prebuilt/overlay.ts +256 -0
  1184. package/src/server/prebuilt/payload-source.ts +202 -0
  1185. package/src/server/prebuilt/slots.ts +202 -0
  1186. package/src/server/prebuilt/synthetic-store.ts +125 -0
  1187. package/src/server/prebuilt-builder.ts +514 -0
  1188. package/src/server/prebuilt-runtime.ts +813 -0
  1189. package/src/server/primitives.ts +153 -171
  1190. package/src/server/publish-params.ts +42 -0
  1191. package/src/server/react-cache-bridge.ts +124 -0
  1192. package/src/server/react-cache-scope.ts +104 -0
  1193. package/src/server/render-timeout.ts +128 -0
  1194. package/src/server/render-utils.ts +45 -0
  1195. package/src/server/request-context.ts +269 -332
  1196. package/src/server/request-dep.ts +37 -0
  1197. package/src/server/response-cache-policy.ts +45 -0
  1198. package/src/server/route-element-builder.ts +514 -355
  1199. package/src/server/route-element-helpers.ts +37 -0
  1200. package/src/server/route-handler.ts +67 -27
  1201. package/src/server/route-matcher.ts +238 -267
  1202. package/src/server/route-module-loader.ts +161 -0
  1203. package/src/server/rsc-cache-key-guard.ts +122 -0
  1204. package/src/server/rsc-entry/action-dispatcher.ts +142 -0
  1205. package/src/server/rsc-entry/action-middleware-runner.ts +130 -0
  1206. package/src/server/rsc-entry/api-handler.ts +49 -81
  1207. package/src/server/rsc-entry/deny-fallback.ts +151 -0
  1208. package/src/server/rsc-entry/error-renderer.ts +301 -113
  1209. package/src/server/rsc-entry/helpers.ts +90 -115
  1210. package/src/server/rsc-entry/index.ts +370 -350
  1211. package/src/server/rsc-entry/render-route.ts +229 -0
  1212. package/src/server/rsc-entry/render-signals.ts +108 -0
  1213. package/src/server/rsc-entry/revalidate-renderer.ts +349 -0
  1214. package/src/server/rsc-entry/rsc-payload.ts +192 -72
  1215. package/src/server/rsc-entry/rsc-stream.ts +128 -131
  1216. package/src/server/rsc-entry/ssr-bridge.ts +19 -6
  1217. package/src/server/rsc-entry/ssr-renderer.ts +300 -137
  1218. package/src/server/rsc-error-envelope.ts +18 -0
  1219. package/src/server/rsc-prop-warnings.ts +1 -1
  1220. package/src/server/safe-load.ts +53 -0
  1221. package/src/server/sensitive-fields.ts +230 -0
  1222. package/src/server/server-only-guard-noop.js +3 -0
  1223. package/src/server/server-timing.ts +1 -1
  1224. package/src/server/signal-identity.ts +112 -0
  1225. package/src/server/sitemap-generator.ts +337 -0
  1226. package/src/server/sitemap-handler.ts +157 -0
  1227. package/src/server/skippable-prefix.ts +172 -0
  1228. package/src/server/slot-interception.ts +168 -0
  1229. package/src/server/slot-resolver.ts +707 -300
  1230. package/src/server/slot-subtree-contain.ts +273 -0
  1231. package/src/server/ssr-bridge-types.ts +201 -0
  1232. package/src/server/ssr-entry.ts +225 -104
  1233. package/src/server/ssr-render.ts +354 -91
  1234. package/src/server/ssr-wrappers.tsx +162 -0
  1235. package/src/server/state-tree-diff.ts +85 -30
  1236. package/src/server/static-generator.ts +680 -0
  1237. package/src/server/static-not-found.ts +134 -0
  1238. package/src/server/status-code-resolver.ts +107 -148
  1239. package/src/server/stream-utils.ts +343 -0
  1240. package/src/server/tracing.ts +163 -68
  1241. package/src/server/tree-match.ts +494 -0
  1242. package/src/server/types.ts +96 -11
  1243. package/src/server/utils/element-type.ts +88 -0
  1244. package/src/server/utils/escape-html.ts +20 -0
  1245. package/src/server/version-skew.ts +104 -0
  1246. package/src/server/waituntil-bridge.ts +1 -1
  1247. package/src/server/workers-cache-bridge.ts +33 -0
  1248. package/src/shared/als-slots.ts +74 -0
  1249. package/src/shared/base-path.ts +80 -0
  1250. package/src/shared/csrf-reject.ts +15 -0
  1251. package/src/shared/error-digest.ts +46 -0
  1252. package/src/shared/href-validation.ts +159 -0
  1253. package/src/shared/merge-search-params.ts +55 -0
  1254. package/src/shared/param-value.ts +281 -0
  1255. package/src/shared/payload-root.ts +153 -0
  1256. package/src/shared/redirect-type.ts +17 -0
  1257. package/src/shared/rsc-cache-key.ts +269 -0
  1258. package/src/shared/rsc-error-envelope.ts +16 -0
  1259. package/src/shared/rsc-manifest.ts +29 -0
  1260. package/src/shared/rsc-media-type.ts +59 -0
  1261. package/src/shared/rsc-payload-path.ts +52 -0
  1262. package/src/shared/segment-info.ts +67 -0
  1263. package/src/shared/slot-params.ts +109 -0
  1264. package/src/shared/static-platform-files.ts +75 -0
  1265. package/src/shared/status-reason-phrase.ts +61 -0
  1266. package/src/shims/font-google.ts +1 -1
  1267. package/src/shims/font-local.ts +34 -0
  1268. package/src/shims/headers.ts +122 -1
  1269. package/src/shims/navigation-client.ts +36 -32
  1270. package/src/shims/navigation-rsc.ts +47 -0
  1271. package/src/shims/navigation.ts +10 -7
  1272. package/src/utils/state-machine.ts +111 -0
  1273. package/src/utils/write-file-atomic.ts +32 -0
  1274. package/dist/_chunks/als-registry-B7DbZ2hS.js +0 -41
  1275. package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
  1276. package/dist/_chunks/debug-B3Gypr3D.js +0 -108
  1277. package/dist/_chunks/debug-B3Gypr3D.js.map +0 -1
  1278. package/dist/_chunks/format-RyoGQL74.js +0 -164
  1279. package/dist/_chunks/format-RyoGQL74.js.map +0 -1
  1280. package/dist/_chunks/interception-BOoWmLUA.js +0 -676
  1281. package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
  1282. package/dist/_chunks/metadata-routes-Cjmvi3rQ.js.map +0 -1
  1283. package/dist/_chunks/request-context-BQUC8PHn.js +0 -330
  1284. package/dist/_chunks/request-context-BQUC8PHn.js.map +0 -1
  1285. package/dist/_chunks/ssr-data-MjmprTmO.js +0 -88
  1286. package/dist/_chunks/ssr-data-MjmprTmO.js.map +0 -1
  1287. package/dist/_chunks/tracing-CemImE6h.js +0 -187
  1288. package/dist/_chunks/tracing-CemImE6h.js.map +0 -1
  1289. package/dist/_chunks/use-cookie-DX-l1_5E.js +0 -91
  1290. package/dist/_chunks/use-cookie-DX-l1_5E.js.map +0 -1
  1291. package/dist/_chunks/use-query-states-D5KaffOK.js +0 -109
  1292. package/dist/_chunks/use-query-states-D5KaffOK.js.map +0 -1
  1293. package/dist/adapters/cloudflare.js.map +0 -1
  1294. package/dist/cache/index.js.map +0 -1
  1295. package/dist/cache/register-cached-function.d.ts +0 -17
  1296. package/dist/cache/register-cached-function.d.ts.map +0 -1
  1297. package/dist/client/browser-entry.d.ts +0 -21
  1298. package/dist/client/browser-entry.d.ts.map +0 -1
  1299. package/dist/client/error-boundary.js.map +0 -1
  1300. package/dist/client/head.d.ts +0 -16
  1301. package/dist/client/head.d.ts.map +0 -1
  1302. package/dist/client/link-status-provider.d.ts +0 -11
  1303. package/dist/client/link-status-provider.d.ts.map +0 -1
  1304. package/dist/client/segment-merger.d.ts +0 -119
  1305. package/dist/client/segment-merger.d.ts.map +0 -1
  1306. package/dist/client/stale-reload.d.ts +0 -44
  1307. package/dist/client/stale-reload.d.ts.map +0 -1
  1308. package/dist/client/transition-root.d.ts +0 -74
  1309. package/dist/client/transition-root.d.ts.map +0 -1
  1310. package/dist/client/types.d.ts +0 -14
  1311. package/dist/client/types.d.ts.map +0 -1
  1312. package/dist/client/use-navigation-pending.d.ts +0 -26
  1313. package/dist/client/use-navigation-pending.d.ts.map +0 -1
  1314. package/dist/client/use-params.d.ts +0 -92
  1315. package/dist/client/use-params.d.ts.map +0 -1
  1316. package/dist/content/index.d.ts +0 -11
  1317. package/dist/content/index.d.ts.map +0 -1
  1318. package/dist/content/index.js +0 -2
  1319. package/dist/plugins/adapter-build.d.ts +0 -18
  1320. package/dist/plugins/adapter-build.d.ts.map +0 -1
  1321. package/dist/plugins/cache-transform.d.ts +0 -36
  1322. package/dist/plugins/cache-transform.d.ts.map +0 -1
  1323. package/dist/plugins/dev-error-overlay.d.ts.map +0 -1
  1324. package/dist/plugins/dev-logs.d.ts.map +0 -1
  1325. package/dist/plugins/dynamic-transform.d.ts +0 -72
  1326. package/dist/plugins/dynamic-transform.d.ts.map +0 -1
  1327. package/dist/plugins/server-action-exports.d.ts +0 -26
  1328. package/dist/plugins/server-action-exports.d.ts.map +0 -1
  1329. package/dist/routing/status-file-lint.d.ts +0 -33
  1330. package/dist/routing/status-file-lint.d.ts.map +0 -1
  1331. package/dist/search-params/analyze.d.ts +0 -54
  1332. package/dist/search-params/analyze.d.ts.map +0 -1
  1333. package/dist/search-params/builtin-codecs.d.ts +0 -105
  1334. package/dist/search-params/builtin-codecs.d.ts.map +0 -1
  1335. package/dist/search-params/codecs.d.ts +0 -53
  1336. package/dist/search-params/codecs.d.ts.map +0 -1
  1337. package/dist/search-params/create.d.ts +0 -106
  1338. package/dist/search-params/create.d.ts.map +0 -1
  1339. package/dist/search-params/registry.d.ts +0 -20
  1340. package/dist/search-params/registry.d.ts.map +0 -1
  1341. package/dist/server/asset-headers.d.ts +0 -42
  1342. package/dist/server/asset-headers.d.ts.map +0 -1
  1343. package/dist/server/client-module-map.d.ts +0 -47
  1344. package/dist/server/client-module-map.d.ts.map +0 -1
  1345. package/dist/server/dev-fetch-instrumentation.d.ts +0 -22
  1346. package/dist/server/dev-fetch-instrumentation.d.ts.map +0 -1
  1347. package/dist/server/dev-logger.d.ts.map +0 -1
  1348. package/dist/server/dev-span-processor.d.ts +0 -29
  1349. package/dist/server/dev-span-processor.d.ts.map +0 -1
  1350. package/dist/server/dev-warnings.d.ts +0 -129
  1351. package/dist/server/dev-warnings.d.ts.map +0 -1
  1352. package/dist/server/flush.d.ts +0 -74
  1353. package/dist/server/flush.d.ts.map +0 -1
  1354. package/dist/server/manifest-status-resolver.d.ts +0 -58
  1355. package/dist/server/manifest-status-resolver.d.ts.map +0 -1
  1356. package/dist/server/prerender.d.ts +0 -77
  1357. package/dist/server/prerender.d.ts.map +0 -1
  1358. package/dist/server/tree-builder.d.ts +0 -126
  1359. package/dist/server/tree-builder.d.ts.map +0 -1
  1360. package/dist/utils/directive-parser.d.ts +0 -70
  1361. package/dist/utils/directive-parser.d.ts.map +0 -1
  1362. package/src/cache/register-cached-function.ts +0 -103
  1363. package/src/client/browser-entry.ts +0 -678
  1364. package/src/client/head.ts +0 -61
  1365. package/src/client/link-status-provider.tsx +0 -30
  1366. package/src/client/segment-merger.ts +0 -339
  1367. package/src/client/stale-reload.ts +0 -87
  1368. package/src/client/transition-root.tsx +0 -166
  1369. package/src/client/types.ts +0 -21
  1370. package/src/client/use-navigation-pending.ts +0 -38
  1371. package/src/client/use-params.ts +0 -141
  1372. package/src/content/index.ts +0 -13
  1373. package/src/plugins/adapter-build.ts +0 -118
  1374. package/src/plugins/cache-transform.ts +0 -199
  1375. package/src/plugins/dynamic-transform.ts +0 -161
  1376. package/src/plugins/server-action-exports.ts +0 -220
  1377. package/src/routing/status-file-lint.ts +0 -112
  1378. package/src/search-params/analyze.ts +0 -192
  1379. package/src/search-params/builtin-codecs.ts +0 -228
  1380. package/src/search-params/codecs.ts +0 -153
  1381. package/src/search-params/create.ts +0 -321
  1382. package/src/search-params/registry.ts +0 -31
  1383. package/src/server/asset-headers.ts +0 -81
  1384. package/src/server/client-module-map.ts +0 -58
  1385. package/src/server/dev-fetch-instrumentation.ts +0 -96
  1386. package/src/server/dev-span-processor.ts +0 -78
  1387. package/src/server/dev-warnings.ts +0 -283
  1388. package/src/server/flush.ts +0 -182
  1389. package/src/server/manifest-status-resolver.ts +0 -215
  1390. package/src/server/prerender.ts +0 -139
  1391. package/src/server/tree-builder.ts +0 -382
  1392. package/src/shims/server-only-noop.js +0 -6
  1393. package/src/utils/directive-parser.ts +0 -529
@@ -0,0 +1,2056 @@
1
+ import { i as DEFAULT_PAGE_EXTENSIONS, n as classifyUrlSegment, r as effectiveUrlSegment, t as classifySegment } from "./segment-classify-C539Pa2O.js";
2
+ import { n as treePathDepth, t as computeSegmentTreePaths } from "./segment-keys-D5hu1hz4.js";
3
+ import { t as matchUrlParts } from "./tree-match-6yihorhk.js";
4
+ import { a as isDynamicMetadataExtension, n as classifyMetadataRoute } from "./metadata-routes-DSDjM_hJ.js";
5
+ import { n as NULL_BYTE_RE, t as ENCODED_SEPARATOR_RE } from "./canonicalize-CgHoscYO.js";
6
+ import { v as swallow } from "./logger-BP0LN6vP.js";
7
+ import { parseAst } from "vite";
8
+ import { basename, extname, join } from "node:path";
9
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
10
+ //#region src/routing/collision-probe.ts
11
+ /**
12
+ * Find a URL in this collision space whose winner depends on `children` order,
13
+ * or null if every probed URL resolves the same way in both orderings.
14
+ *
15
+ * `spaceRoot` is the node `matchUrlParts` starts from — the app root, a slot
16
+ * node, or an intercepting directory. `base` is the URL that root sits at, used
17
+ * only to render the reported URL.
18
+ */
19
+ function findOrderDependentCollision(spaceRoot, leaves, base) {
20
+ const reversed = withReversedChildren(spaceRoot);
21
+ const longTail = longTailLength(spaceRoot) + 1;
22
+ const minLength = longestStaticName(spaceRoot);
23
+ const seen = /* @__PURE__ */ new Set();
24
+ for (const leaf of leaves) for (const { parts, display } of probeUrlsForLeaf(spaceRoot, reversed, leaf, longTail, minLength)) {
25
+ const key = parts.join("/");
26
+ if (seen.has(key)) continue;
27
+ seen.add(key);
28
+ const first = matchedLeafFile(spaceRoot, parts);
29
+ const second = matchedLeafFile(reversed, parts);
30
+ if (!first || !second || first === second) continue;
31
+ return {
32
+ url: describeProbeUrl(base, display),
33
+ first,
34
+ second
35
+ };
36
+ }
37
+ return null;
38
+ }
39
+ /** Join a probe's display parts onto its space's base. */
40
+ function describeProbeUrl(base, display) {
41
+ if (display.length === 0) return base || "/";
42
+ return `${base === "/" ? "" : base}/${display.join("/")}`;
43
+ }
44
+ /**
45
+ * The file `matchUrlParts` resolves for a URL, or null if nothing does.
46
+ *
47
+ * A `route.ts` counts: `isTerminal` accepts either, so a route handler is a
48
+ * legitimate winner of the walk and an ambiguity between a page and a route
49
+ * handler is as real as one between two pages.
50
+ */
51
+ function matchedLeafFile(node, parts) {
52
+ const match = matchUrlParts(node, parts);
53
+ if (!match) return null;
54
+ const leaf = match.chain[match.chain.length - 1];
55
+ return leaf.page?.filePath ?? leaf.route?.filePath ?? null;
56
+ }
57
+ /**
58
+ * The values a probe's dynamic parts are tried with, in order.
59
+ *
60
+ * A probe stands in for "any value" at a dynamic position, so it is only useful
61
+ * if it reaches the leaf it was built for. A *static* sibling's literal name or
62
+ * a more specific *affixed* sibling's pattern can claim it instead, and that
63
+ * higher-priority segment then wins in both orderings — a genuine tie
64
+ * underneath goes unreported, which is safe but is the whole thing these
65
+ * validators are for.
66
+ *
67
+ * Which values are claimable is a question about `matchNode`'s precedence, so
68
+ * `probeUrlsForLeaf` asks `matchUrlParts` rather than deciding it here. This
69
+ * only has to offer enough shapes for the search to find one that works: an
70
+ * affixed sibling constrains the head of the value, its tail, or both, so head
71
+ * and tail are varied over an alphanumeric alphabet. The head varies fastest
72
+ * because a prefix is the more common affix. Exhausting the list is a missed
73
+ * collision like any other inadequate probe, so this is a search and not a
74
+ * proof — nothing downstream may treat it as one.
75
+ *
76
+ * Exported so a test that has to *name* a capturing static sibling can derive
77
+ * the name from this list rather than restate it — a fixture spelling the value
78
+ * out by hand stops being a capture the moment the list changes, and passes.
79
+ *
80
+ * Three revisions of a hand-built value each dodged one more capture and were
81
+ * each followed by another (avoid static names; avoid affix boundary
82
+ * characters; avoid re-affixed forms of static names — then a strictly more
83
+ * specific affixed sibling, TIM-1313). Each rule was modelling affix precedence
84
+ * by hand, which is what the match-shape model was deleted for.
85
+ */
86
+ function* probeCandidates(minLength = 0) {
87
+ const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
88
+ const base = "timber-probe";
89
+ function* wave(core) {
90
+ for (const tail of alphabet) for (const head of alphabet) yield `${head}${core}${tail}`;
91
+ }
92
+ yield* wave(base);
93
+ const padded = base.padEnd(Math.max(12, minLength), "x");
94
+ if (padded !== base) yield* wave(padded);
95
+ }
96
+ /**
97
+ * The longest static segment name anywhere in the space.
98
+ *
99
+ * Read through `effectiveUrlSegment` so an intercepting directory counts as the
100
+ * segment it intercepts, and only static kinds count: a dynamic sibling claims
101
+ * a probe by pattern rather than by equality, which no length can escape.
102
+ */
103
+ function longestStaticName(node) {
104
+ let longest = 0;
105
+ for (const child of node.children) {
106
+ const seg = effectiveUrlSegment(child);
107
+ if (seg.segmentType === "static") longest = Math.max(longest, seg.segmentName.length);
108
+ longest = Math.max(longest, longestStaticName(child));
109
+ }
110
+ return longest;
111
+ }
112
+ /**
113
+ * The probe URLs for one leaf, built from the first candidate value that
114
+ * actually reaches it.
115
+ *
116
+ * "Reaches it" is `matchUrlParts` resolving one of the leaf's own URLs to the
117
+ * leaf's file, in either ordering — either is enough, because a leaf that wins
118
+ * only in the reversed ordering is precisely the contention being looked for.
119
+ * A catch-all leaf may be reached by its long probe and not its short one (at
120
+ * one part it loses to a bare dynamic sibling deterministically), so any one of
121
+ * the generated URLs landing is enough to accept the value.
122
+ *
123
+ * A leaf that no candidate reaches is shadowed at its position for every value
124
+ * tried — most often because it sits below a catch-all, which `matchNode`
125
+ * accepts only as a terminal and never descends past. Its probes are still
126
+ * emitted, from the first candidate: a probe that misses its own leaf is never
127
+ * *wrong* — it is a real URL, and a winner that differs between the two
128
+ * orderings is a real ambiguity whichever leaf it belongs to. Nothing here may
129
+ * report the leaf as unreachable; that is the universal claim these validators
130
+ * do not make.
131
+ */
132
+ function probeUrlsForLeaf(root, reversed, leaf, longTail, minLength) {
133
+ let fallback = null;
134
+ for (const value of probeCandidates(minLength)) {
135
+ const urls = probeUrlsFor(leaf.chain, value, longTail);
136
+ if (urls.some(({ parts }) => matchedLeafFile(root, parts) === leaf.filePath || matchedLeafFile(reversed, parts) === leaf.filePath)) return urls;
137
+ fallback ??= urls;
138
+ if (!urls.some(({ dynamic }) => dynamic)) break;
139
+ }
140
+ return fallback ?? [];
141
+ }
142
+ /**
143
+ * The URL part one segment contributes to a probe.
144
+ *
145
+ * A dynamic segment stands for any single value, so it contributes the
146
+ * candidate value — re-affixed, since an affixed segment only matches parts
147
+ * that carry its prefix and suffix, so a probe without them could not reach it.
148
+ * A catch-all stands for one or more parts, and one is enough to reach the leaf.
149
+ *
150
+ * The re-affixed part is the reason a value cannot be checked once for the
151
+ * whole space: it exposes the value to every affixed sibling that *extends*
152
+ * this segment's affixes, which differs per position. `probeUrlsForLeaf` asks
153
+ * the matcher whether the assembled URL reaches its leaf, and moves to the next
154
+ * candidate when it does not.
155
+ *
156
+ * Segments are read through `effectiveUrlSegment` so an intercepting directory
157
+ * contributes the part the segment it intercepts would.
158
+ */
159
+ function probePartFor(node, value) {
160
+ const seg = effectiveUrlSegment(node);
161
+ switch (seg.segmentType) {
162
+ case "dynamic": {
163
+ const prefix = seg.paramPrefix ?? "";
164
+ const suffix = seg.paramSuffix ?? "";
165
+ return {
166
+ part: `${prefix}${value}${suffix}`,
167
+ shown: `${prefix}<any>${suffix}`,
168
+ dynamic: true
169
+ };
170
+ }
171
+ case "catch-all":
172
+ case "optional-catch-all": return {
173
+ part: value,
174
+ shown: "<any>",
175
+ dynamic: true
176
+ };
177
+ default: return {
178
+ part: seg.segmentName,
179
+ shown: seg.segmentName,
180
+ dynamic: false
181
+ };
182
+ }
183
+ }
184
+ /**
185
+ * The URLs that reach a leaf.
186
+ *
187
+ * An **optional catch-all also matches zero parts**, so it answers the URL its
188
+ * parent sits at as well as longer ones. Both are probed: whether the shorter
189
+ * one is actually *its* to answer is `matchUrlParts`' business, not something
190
+ * to decide here — a page above it, or in a route group beside it, may take
191
+ * that URL deterministically, and the probe will simply report the same winner
192
+ * in both orderings.
193
+ */
194
+ function probeUrlsFor(chain, value, longTail) {
195
+ const probeParts = chain.map((node) => probePartFor(node, value));
196
+ const url = (ps) => ({
197
+ parts: ps.map((p) => p.part),
198
+ display: ps.map((p) => p.shown),
199
+ dynamic: ps.some((p) => p.dynamic)
200
+ });
201
+ const leaf = chain[chain.length - 1];
202
+ const kind = leaf && effectiveUrlSegment(leaf).segmentType;
203
+ if (!(kind === "catch-all" || kind === "optional-catch-all")) return [url(probeParts)];
204
+ const head = probeParts.slice(0, -1);
205
+ const urls = [url(probeParts)];
206
+ if (longTail > 1) {
207
+ const tail = Array.from({ length: longTail }, () => ({
208
+ part: value,
209
+ shown: "<any>",
210
+ dynamic: true
211
+ }));
212
+ urls.push(url([...head, ...tail]));
213
+ }
214
+ if (kind === "optional-catch-all") urls.push(url(head));
215
+ return urls;
216
+ }
217
+ /**
218
+ * One more than the longest fixed-length path in the space.
219
+ *
220
+ * A probe of this length cannot be claimed by any branch that consumes a fixed
221
+ * number of URL parts, so whatever matches it is a catch-all — which is
222
+ * exactly the competition a short probe hides.
223
+ */
224
+ function longTailLength(node) {
225
+ let deepest = 0;
226
+ for (const child of node.children) {
227
+ const own = child.segmentType === "group" ? 0 : 1;
228
+ deepest = Math.max(deepest, own + longTailLength(child));
229
+ }
230
+ return deepest;
231
+ }
232
+ /**
233
+ * The subtree with every `children` array reversed, top to bottom.
234
+ *
235
+ * Nodes are shallow-copied, so files and slots are shared with the original —
236
+ * only the ordering `matchNode` reads is different. Slots are not reversed
237
+ * because `matchNode` never walks them; each slot is validated as its own
238
+ * collision space, with its own reversed clone.
239
+ */
240
+ function withReversedChildren(node) {
241
+ return {
242
+ ...node,
243
+ children: [...node.children].reverse().map(withReversedChildren)
244
+ };
245
+ }
246
+ //#endregion
247
+ //#region src/routing/interception-overlap.ts
248
+ /**
249
+ * Pattern intersection for intercepted URL patterns.
250
+ *
251
+ * Checks whether two intercepted URL patterns can both match the same concrete
252
+ * URL. Used by the cross-mode claim check (TIM-1319) to detect when a
253
+ * children-path interceptor and a slot interceptor from the same owner overlap.
254
+ *
255
+ * This is not the order-dependence oracle (`findOrderDependentCollision`) the
256
+ * within-mode collision checks use. `findInterceptionMatch` picks the mode by
257
+ * first-match on `pathnameMatchesPattern`, which has no priority tiers, so a
258
+ * static `/feed/foo` and a dynamic `/feed/[id]` both match `/feed/foo` even
259
+ * though `matchUrlParts` would resolve the static deterministically. The
260
+ * intersection test catches that: it walks both patterns segment by segment and
261
+ * asks whether any value satisfies both.
262
+ *
263
+ * This is not a model of `matchNode` precedence — that decides *which*
264
+ * candidate wins when several match one URL. This decides the prior question:
265
+ * *do* they both match one URL at all? The two questions are independent, and
266
+ * conflating them is what caused TIM-1308's five-round drift.
267
+ */
268
+ /**
269
+ * Check whether two intercepted URL patterns can both match the same concrete
270
+ * URL. Returns a display string for the overlapping URL, or null.
271
+ *
272
+ * Walks both patterns segment by segment:
273
+ * - Two statics: must be identical.
274
+ * - Static + dynamic: static must satisfy the dynamic's affix constraints.
275
+ * - Two dynamics: affix constraints must be compatible.
276
+ * - A catch-all at either side: compatible with everything remaining.
277
+ *
278
+ * If the patterns differ in length, they can only overlap if the longer one's
279
+ * extra part is an optional catch-all (which also matches zero segments).
280
+ */
281
+ function interceptedPatternsOverlap(p1, p2) {
282
+ if (p1 === p2) return p1;
283
+ const parts1 = p1 === "/" ? [] : p1.slice(1).split("/");
284
+ const parts2 = p2 === "/" ? [] : p2.slice(1).split("/");
285
+ const display = [];
286
+ let i = 0;
287
+ while (i < parts1.length && i < parts2.length) {
288
+ const seg1 = classifyUrlSegment(parts1[i]);
289
+ const seg2 = classifyUrlSegment(parts2[i]);
290
+ if (seg1.kind === "catch-all" || seg1.kind === "optional-catch-all" || seg2.kind === "catch-all" || seg2.kind === "optional-catch-all") {
291
+ display.push("<any>");
292
+ return "/" + display.join("/");
293
+ }
294
+ const part = segmentsOverlap(seg1, seg2);
295
+ if (part === null) return null;
296
+ display.push(part);
297
+ i++;
298
+ }
299
+ const remaining = i < parts1.length ? parts1 : i < parts2.length ? parts2 : null;
300
+ if (remaining) {
301
+ if (classifyUrlSegment(remaining[i]).kind !== "optional-catch-all") return null;
302
+ }
303
+ return display.length === 0 ? "/" : "/" + display.join("/");
304
+ }
305
+ /**
306
+ * Can some concrete value satisfy both classified URL segments?
307
+ *
308
+ * An affixed dynamic (`a-[id].png`) constrains both the prefix and suffix of
309
+ * the value, so a static segment must satisfy those constraints and two
310
+ * dynamics must have compatible affixes — prefixes where one extends the other,
311
+ * and likewise for suffixes. Without this check, `/feed/photo.jpg` beside
312
+ * `/feed/asset-[id].png` would be falsely rejected.
313
+ */
314
+ function segmentsOverlap(seg1, seg2) {
315
+ if (seg1.kind === "static" && seg2.kind === "static") return seg1.value === seg2.value ? seg1.value : null;
316
+ if (seg1.kind === "static" && seg2.kind === "dynamic") return staticMatchesAffix(seg1.value, seg2) ? seg1.value : null;
317
+ if (seg2.kind === "static" && seg1.kind === "dynamic") return staticMatchesAffix(seg2.value, seg1) ? seg2.value : null;
318
+ if (seg1.kind === "dynamic" && seg2.kind === "dynamic") return dynamicAffixesCompatible(seg1, seg2) ? "<any>" : null;
319
+ return "<any>";
320
+ }
321
+ /**
322
+ * Does a static value satisfy an affixed dynamic's prefix, suffix, and
323
+ * non-empty capture constraints?
324
+ *
325
+ * Mirrors the check `pathnameMatchesPattern` applies: the value must start with
326
+ * the prefix, end with the suffix, and be strictly longer than their combined
327
+ * length (a capture of zero characters is not a match).
328
+ */
329
+ function staticMatchesAffix(value, dynamic) {
330
+ const prefix = dynamic.prefix ?? "";
331
+ const suffix = dynamic.suffix ?? "";
332
+ if (!prefix && !suffix) return true;
333
+ if (prefix && !value.startsWith(prefix)) return false;
334
+ if (suffix && !value.endsWith(suffix)) return false;
335
+ return value.length > prefix.length + suffix.length;
336
+ }
337
+ /**
338
+ * Can some value simultaneously satisfy two affixed dynamics?
339
+ *
340
+ * Two prefix constraints are compatible when one extends the other (a value
341
+ * starting with the longer one also starts with the shorter). Suffixes are
342
+ * analogous. Without affixes on either side, every value works.
343
+ */
344
+ function dynamicAffixesCompatible(d1, d2) {
345
+ const p1 = d1.prefix ?? "", s1 = d1.suffix ?? "";
346
+ const p2 = d2.prefix ?? "", s2 = d2.suffix ?? "";
347
+ if (p1 && p2 && !p1.startsWith(p2) && !p2.startsWith(p1)) return false;
348
+ if (s1 && s2 && !s1.endsWith(s2) && !s2.endsWith(s1)) return false;
349
+ return true;
350
+ }
351
+ //#endregion
352
+ //#region src/routing/interception.ts
353
+ /**
354
+ * Group an owner's intercepting children by the URL position they contend for.
355
+ *
356
+ * This is the definition of *which interceptors compete*, and it has exactly
357
+ * one caller shape on each side of the build: `findInterceptingMatch` resolves
358
+ * a group with one `matchUrlParts` walk, and
359
+ * `assertNoInterceptedPatternCollisions` validates the same group as one
360
+ * collision space. Written twice, the two would be free to disagree about what
361
+ * competes — and a resolver that walks a wider set than the validator checked
362
+ * is precisely the order-dependence the validator exists to reject (TIM-1310).
363
+ *
364
+ * Contention is keyed by the computed intercepted **base**, not by the marker:
365
+ * under an owner at `/feed`, `(..)` and `(...)` both base at `/` and are both
366
+ * matched at index 0, so they are one group. Different bases are different
367
+ * positions — a `(.)` interceptor can never claim a target that belongs to a
368
+ * `(..)` one beside it, because the base must also match the target's leading
369
+ * parts.
370
+ *
371
+ * The base is a URL *pattern*, so its length is its part count only because
372
+ * every pattern part stands for exactly one URL part. A catch-all in the base
373
+ * breaks that, and `assertInterceptedBaseIsCountable` rejects the shape at
374
+ * build time.
375
+ *
376
+ * A node missing either marker field is skipped, which `classifySegment` never
377
+ * produces — it sets both whenever it reports `'intercepting'`. Stating the
378
+ * filter here rather than defaulting the marker is what makes the validator and
379
+ * the resolver see the same set: such a node renders nothing either way, and a
380
+ * validator more permissive than the resolver would be checking a candidate
381
+ * that never competes while the resolver walks one nobody checked.
382
+ *
383
+ * `children` is read directly rather than flattened through route groups,
384
+ * matching both resolvers: the slot resolver reads `slotNode.children`, and an
385
+ * interceptor deeper inside a slot subtree is rejected by
386
+ * `validateInterceptionPlacement`. On the children path an interceptor under a
387
+ * route group is legal and the *group* is its owner, so it is grouped with that
388
+ * group's other interceptors — which is again exactly what the resolver walks,
389
+ * since it resolves the owner by tree path.
390
+ */
391
+ function groupByInterceptedAnchor(ownerUrlPath, children) {
392
+ const byBase = /* @__PURE__ */ new Map();
393
+ for (const child of children) {
394
+ if (child.segmentType !== "intercepting") continue;
395
+ if (!child.interceptionMarker || !child.interceptedSegmentName) continue;
396
+ const base = computeInterceptedBase(ownerUrlPath, child.interceptionMarker);
397
+ const group = byBase.get(base);
398
+ if (group) group.interceptors.push(child);
399
+ else byBase.set(base, {
400
+ base,
401
+ anchorIndex: base === "/" ? 0 : base.slice(1).split("/").length,
402
+ interceptors: [child]
403
+ });
404
+ }
405
+ return [...byBase.values()].sort((a, b) => b.anchorIndex - a.anchorIndex);
406
+ }
407
+ /**
408
+ * Collect all interception rewrite rules from the route tree.
409
+ *
410
+ * Walks the tree recursively. For each intercepting segment, computes the
411
+ * intercepted URL based on the marker and the segment's position.
412
+ *
413
+ * The list is returned **deepest scope first**, which is what makes
414
+ * `findInterceptionMatch`'s first-match rule deterministic; see
415
+ * `sortByScopeDepth`.
416
+ */
417
+ function collectInterceptionRewrites(root) {
418
+ const rewrites = [];
419
+ walkForInterceptions(root, [root], false, rewrites);
420
+ return sortByScopeDepth(rewrites);
421
+ }
422
+ /**
423
+ * Order the rewrites deepest scope first, so the request-time first-match rule
424
+ * resolves two applicable rewrites by *nesting* rather than by tree-walk order.
425
+ *
426
+ * `findInterceptionMatch` returns the first rewrite whose pattern matches the
427
+ * target and whose scope the source route reports. More than one can satisfy
428
+ * both, and the case is not exotic — an owner and a directory *beneath* it may
429
+ * each hold an interceptor for one URL:
430
+ *
431
+ * ```
432
+ * app/feed/(.)photo/page.tsx scope /feed
433
+ * app/feed/(a)/(.)photo/page.tsx scope /feed/(a)
434
+ * ```
435
+ *
436
+ * A page inside `(a)` renders both owners' layouts, so the client reports both
437
+ * tree paths and both scopes pass. Unsorted, the winner is whichever the
438
+ * filesystem enumerated first — renaming the group flips which page a click
439
+ * opens, the defect class TIM-1305/TIM-1308/TIM-1310 each rejected in a
440
+ * different space. Here it is an ambiguity between *rewrites* rather than
441
+ * between pages inside one walk, so no collision space sees it.
442
+ *
443
+ * **Deepest wins, and it is specified rather than rejected** because the shape
444
+ * is one a correct app writes on purpose: a nested layout refining the
445
+ * interception its parent declares, exactly as `groupByInterceptedAnchor`
446
+ * already prefers the deepest *anchor* within one owner. The two rules are the
447
+ * same preference — the more local interception is the author's more specific
448
+ * statement — applied at the two levels the mechanism has.
449
+ *
450
+ * Depth is counted on the **tree path**, the identity that never collapses
451
+ * (TIM-1279/TIM-1280); a `urlPath` prefix test would relate `(browse)` and
452
+ * `(landing)`, which render different layouts and share no chain. Two scopes on
453
+ * one rendered chain are strictly nested, so depth orders them totally, and two
454
+ * scopes that are *not* on one chain — sibling groups, `feed/(a)` beside
455
+ * `feed/(b)` — never both pass the scope check, so their relative order is not
456
+ * observable. That is why this sorts on depth alone rather than testing
457
+ * ancestry: it is a total order over the pairs that can compete, and it costs
458
+ * nothing per request.
459
+ *
460
+ * Sorting is stable, so rewrites sharing one scope keep tree-walk order. Equal
461
+ * scopes mean one owner, and of the three pairings that produces only one was
462
+ * ambiguous — a children-path interceptor beside a slot one — and that is now
463
+ * rejected at build time by `assertNoCrossModeClaims` (TIM-1319). The
464
+ * enumeration is in design/07-routing.md §"Selection Between Owners" rather
465
+ * than repeated here, so there is one place to correct if a rewrite ever grows
466
+ * a field that makes two of them distinguishable.
467
+ */
468
+ function sortByScopeDepth(rewrites) {
469
+ return rewrites.sort((a, b) => treePathDepth(b.interceptingScope) - treePathDepth(a.interceptingScope));
470
+ }
471
+ /**
472
+ * Validate that every intercepting segment sits where it can actually render.
473
+ *
474
+ * There are exactly two positions a renderer exists for, and they are the two
475
+ * shapes interception has:
476
+ *
477
+ * 1. **Direct child of a parallel slot** — the modal overlay. The slot
478
+ * resolver scans a slot node's *direct children* for an intercepting
479
+ * segment (`server/slot-interception.ts`). Nothing between the slot and
480
+ * the intercepting directory is allowed, because nothing walks it.
481
+ * 2. **Direct child of an ordinary segment or route group** — the sibling
482
+ * layout swap (TIM-1280). The pipeline resolves the owner by tree path and
483
+ * builds the rendered chain through the intercepting node itself
484
+ * (`server/children-interception.ts`).
485
+ *
486
+ * Everywhere else, rewrite generation still emits a rewrite and nothing
487
+ * renders it: the rewrite matches the soft navigation, and the user is served
488
+ * the page they were already on under a changed URL (TIM-1278). The rejected
489
+ * positions are:
490
+ *
491
+ * - **Inside a slot subtree but not directly under the slot**
492
+ * (`@modal/(inner)/(.)photo`). Neither renderer applies: the slot resolver
493
+ * only reads direct children, and the children-path resolver builds a
494
+ * *chain*, which cannot express a descent through a slot.
495
+ * - **Inside another intercepting subtree** (`(.)a/(.)b`). Left unvalidated
496
+ * `collectLeavesWithRewrites` walks the inner one as an ordinary URL
497
+ * segment and emits a pattern containing a literal "(.)b".
498
+ *
499
+ * Two further shapes are rejected here (TIM-1288). Both are about a catch-all,
500
+ * and both are specific to interception: elsewhere in the tree the same
501
+ * shapes are inert or fail safely, and the general "nothing below a catch-all
502
+ * is reachable" rule is tracked as TIM-1286.
503
+ *
504
+ * And one is specific to the children path: an owner chain that binds params
505
+ * (see `assertOwnerChainBindsNoParams`).
506
+ */
507
+ function validateInterceptionPlacement(root) {
508
+ walkForPlacement(root, "", {
509
+ parentIsSlot: false,
510
+ inSlotSubtree: false,
511
+ inInterceptingSubtree: false,
512
+ ancestorsBindParams: false
513
+ });
514
+ }
515
+ /**
516
+ * Reject a children-path interception whose owner chain binds params.
517
+ *
518
+ * The rendered chain is `[…owner ancestors, interceptingNode, …descendants]`,
519
+ * and the only pathname available to bind params from is the **target** — the
520
+ * URL being navigated to. The intercepting subtree's own params come from it
521
+ * by construction. The owner chain's do not, in general:
522
+ *
523
+ * - With a climbing marker the owner's URL segments are not in the target at
524
+ * all. `app/[team]/(..)photo/[id]` bases at "/", so the target is
525
+ * `/photo/9` — there is no team value anywhere in it, and `[team]`'s layout
526
+ * would render with the param it declares missing.
527
+ * - With `(.)` the base *is* the owner's `urlPath`, so the target's leading
528
+ * parts would bind it. Supporting only that half means one marker behaves
529
+ * differently from its siblings for reasons the author cannot see from the
530
+ * directory names, so the rule is stated once, on the owner chain.
531
+ *
532
+ * A layout that silently renders without the param it declares is the failure
533
+ * this prevents; it is rejected where it can be explained rather than left to
534
+ * surface as `undefined` deep inside user code. Route groups and static
535
+ * segments bind nothing and are unrestricted, which is the whole motivating
536
+ * shape (`app/(browse)/(...)[artistSlug]/…`).
537
+ *
538
+ * The rule stops at a slot boundary: a slot-mode interception re-matches the
539
+ * *source* route, which supplies the owner's params, so a dynamic owner is
540
+ * fine there and always has been.
541
+ */
542
+ /**
543
+ * Require a children-path interception's owner to have a `layout.tsx`.
544
+ *
545
+ * The scope is the owner's tree path, and the client reports which tree paths
546
+ * it has mounted from `X-Timber-Segments` — which carries **one entry per
547
+ * layout-bearing segment**, because those are the segments the client needs an
548
+ * outlet and a cache node for. `buildSegmentInfo` skips the rest.
549
+ *
550
+ * For the modal shape that set is exactly the set of possible scope owners, by
551
+ * construction: a slot is only resolved inside a rendering layout
552
+ * (`route-element-builder.ts`), so a layoutless segment can never own one. A
553
+ * children-path interception broke that equivalence — its owner is an ordinary
554
+ * directory and needs no layout.
555
+ *
556
+ * Left unchecked the result is a swap that works exactly once: the first soft
557
+ * navigation renders, then the client reports no such tree path, and the next
558
+ * navigation fails the scope check and falls back to the canonical page. That
559
+ * is fail-safe but silent, and the address-bar channel cannot recover it —
560
+ * once the swap is on screen the address holds the *intercepted* URL, which
561
+ * re-matches the canonical chain (the TIM-1282 problem, in the one shape the
562
+ * rendered-scope fix does not reach).
563
+ *
564
+ * Requiring the layout restores the invariant the state tree is built on
565
+ * rather than widening the wire format to carry segments that own no outlet
566
+ * and no payload. It also costs the author nothing they were not already
567
+ * doing: the point of this shape is to render inside the owner's own layout,
568
+ * so an owner with no layout has no chrome to preserve.
569
+ */
570
+ function assertOwnerHasLayout(ownerPath, childPath) {
571
+ const owner = ownerPath || "(app root)";
572
+ throw new Error(`Build error: intercepting route whose owner has no layout.\n Directory: app/${childPath}\n Owner: app/${owner}\nAn intercepting directory outside a parallel slot renders inside its owner's layout chain, and the owner must have a layout.tsx: the client reports which directories it has mounted from its layout segments, and a scope it cannot report would let the interception fire once and then silently stop.\nAdd app/${owner}/layout.tsx (a pass-through returning {children} is enough), or move the interception under a directory that has one.\nSee design/07-routing.md §"Intercepting Routes".`);
573
+ }
574
+ function assertOwnerChainBindsNoParams(childPath, marker) {
575
+ throw new Error(`Build error: intercepting route under a dynamic segment.\n Directory: app/${childPath}\nAn intercepting directory outside a parallel slot renders inside its own layout chain, and the only pathname available to bind params from is the intercepted URL — which does not carry the values of the segments above "${marker}".\nMove the interception so every directory above it is a static segment or a route group, or place it inside a parallel slot (@modal/) to render it as an overlay over the source route instead.\nSee design/07-routing.md §"Intercepting Routes".`);
576
+ }
577
+ /**
578
+ * Reject an intercepting route whose intercepted level is not well-defined.
579
+ *
580
+ * "One level up" is counted in URL segments, which works because every
581
+ * pattern part stands for exactly one URL part — except a catch-all, which
582
+ * stands for a number that varies per request. `anchorIndex` fixes the anchor
583
+ * at the base's pattern length, so with an owner at `/docs/[...path]` the
584
+ * interception resolves when the catch-all consumed exactly one segment
585
+ * (`/docs/a/photo/5`) and is dropped when it consumed more
586
+ * (`/docs/a/b/photo/5`), because the base no longer matches the target's
587
+ * leading parts.
588
+ *
589
+ * Dropping is fail-safe — the canonical route renders, as on hard navigation
590
+ * — so this is never a wrong page. But it is a modal that opens at one depth
591
+ * and silently does not at another, with nothing to tell the author why, and
592
+ * no request-time input can fix it: the only available source depth is
593
+ * `X-Timber-URL`, which is the browser's address bar, and once a modal is
594
+ * open that holds the *intercepted* URL rather than the source.
595
+ *
596
+ * So the shape is rejected where it can be explained. A marker that climbs
597
+ * *past* the catch-all is fine — `(..)` from `/docs/[...path]` bases at
598
+ * `/docs` — and so is `(...)`, which always bases at the root. The rule is on
599
+ * the *computed* base, not on having a catch-all anywhere above.
600
+ */
601
+ function assertInterceptedBaseIsCountable(child, ownerUrlPath, childPath) {
602
+ const marker = child.interceptionMarker ?? "(.)";
603
+ const base = computeInterceptedBase(ownerUrlPath, marker);
604
+ const ambiguous = (base === "/" ? [] : base.slice(1).split("/")).find((part) => {
605
+ const kind = classifyUrlSegment(part).kind;
606
+ return kind === "catch-all" || kind === "optional-catch-all";
607
+ });
608
+ if (!ambiguous) return;
609
+ throw new Error(`Build error: intercepting route below a catch-all segment.\n Directory: app/${childPath}\n Intercepted base: ${base}\nA catch-all (${ambiguous}) matches a number of URL segments that varies per request, so "${marker}" cannot identify which segment of a target pathname the interception starts at — the modal would open at one depth and silently not at another.\nMove the interception above the catch-all, or use a marker that climbs past it ((..) or (...)).\nSee design/07-routing.md §"Intercepting Routes".`);
610
+ }
611
+ /**
612
+ * Reject a catch-all inside an intercepting subtree that has directories
613
+ * below it.
614
+ *
615
+ * A catch-all consumes the whole remaining path, so nothing under it is
616
+ * reachable — `matchUrlParts` only accepts a catch-all as a terminal leaf and
617
+ * never descends past one. Rewrite generation is not so strict: it walks the
618
+ * subtree and emits `/feed/[...rest]/child`, and `pathnameMatchesPattern`
619
+ * returns as soon as it reaches the catch-all without ever looking at
620
+ * `child`. So the rewrite matches the navigation and then no renderer exists
621
+ * — the TIM-1278 failure mode reached by a different route, with the user
622
+ * served the page they were already on under a changed URL.
623
+ *
624
+ * The check covers the whole intercepting subtree, not just the intercepted
625
+ * segment: `(.)photo/[...rest]/child` emits `/feed/photo/[...rest]/child` and
626
+ * hijacks `/feed/photo/a/b` by exactly the same mechanism. And it does not
627
+ * exempt a catch-all that carries its own page — that one does render, but
628
+ * the rewrite generated for the unreachable descendant is broader than any
629
+ * route in the tree, so the tail modal fires on navigations no directory
630
+ * describes.
631
+ *
632
+ * Outside a slot the same shape merely 404s, which is why the general rule is
633
+ * tracked separately (TIM-1286); here it is unsafe, and it only became
634
+ * reachable when catch-all intercepted segments started matching at all
635
+ * (TIM-1281).
636
+ *
637
+ * The walk descends `children` and not `slots`, mirroring
638
+ * `collectLeavesWithRewrites` exactly: what this rejects is precisely the set
639
+ * of paths rewrite generation emits, so the two cannot disagree about which
640
+ * shapes exist. A slot nested inside an intercepting subtree produces no
641
+ * rewrite and so cannot hijack anything.
642
+ */
643
+ function assertInterceptedCatchAllsAreTerminal(node, nodePath) {
644
+ const { segmentType, segmentName } = effectiveUrlSegment(node);
645
+ if ((segmentType === "catch-all" || segmentType === "optional-catch-all") && node.children.length > 0) throw new Error(`Build error: catch-all with nested segments inside an intercepting route.\n Directory: app/${nodePath}\n Nested: ${node.children.map((c) => c.segmentName).join(", ")}\nA catch-all (${segmentName}) consumes every remaining URL segment, so nothing below it can ever match — but the interception rewrite would still be generated and would hijack the navigation with no page to render.\nMove the nested routes above the catch-all, or intercept a named segment instead.\nSee design/07-routing.md §"Intercepting Routes".`);
646
+ for (const child of node.children) assertInterceptedCatchAllsAreTerminal(child, `${nodePath}/${child.segmentName}`);
647
+ }
648
+ /**
649
+ * Reject a `route.ts` anywhere inside an intercepting subtree.
650
+ *
651
+ * An interception is a soft-navigation variant of a page — it swaps which
652
+ * page component renders. A route handler returns a `Response` and cannot
653
+ * be an interception target: `walkInterceptedLeaves` visits `node.page`
654
+ * only, so no rewrite is emitted for it, but `isTerminal` in
655
+ * `tree-match.ts` counts both `page` and `route`, so `matchUrlParts` can
656
+ * resolve to the route handler. `slot-interception.ts` then drops the
657
+ * match (`if (!leaf.page) continue`), silently disabling the page's
658
+ * interception. Fail-safe (the canonical route renders) but silent.
659
+ *
660
+ * The walk mirrors `assertInterceptedCatchAllsAreTerminal`: it descends
661
+ * `children` (not `slots`), matching `collectLeavesWithRewrites` exactly.
662
+ */
663
+ function assertNoRouteHandlersInSubtree(node, nodePath) {
664
+ if (node.route) throw new Error(`Build error: route handler inside an intercepting route.\n File: ${node.route.filePath}\n Directory: app/${nodePath}\nAn intercepting subtree renders a page component on soft navigation — a route handler returns a Response and cannot be an interception target. The route handler would silently shadow the interception: the resolver would match it and then discard it because it has no page, so the modal would stop opening with no diagnostic.\nRemove the route.ts, or move it outside the intercepting subtree.\nSee design/07-routing.md §"Intercepting Routes".`);
665
+ for (const child of node.children) assertNoRouteHandlersInSubtree(child, `${nodePath}/${child.segmentName}`);
666
+ }
667
+ /**
668
+ * Reject two pages that answer one URL among the interceptors an owner
669
+ * resolves in a single walk.
670
+ *
671
+ * `validateRouteGroupCollisions` in the scanner returns early on an
672
+ * intercepting node, so nothing else validates in here. That exemption exists
673
+ * for a real reason — an interception's computed URL legitimately mirrors the
674
+ * canonical route it replaces, and interceptors at *different* anchors
675
+ * deliberately target one URL, which the resolver then orders (deepest anchor
676
+ * first). But the exemption was broader than its reason. Two shapes slip
677
+ * through it, and they are the same shape seen from two levels:
678
+ *
679
+ * feed/(.)photo/(a)/page.tsx feed/@modal/(.)[a]/page.tsx
680
+ * feed/(.)photo/(b)/page.tsx feed/@modal/(.)[b]/page.tsx
681
+ *
682
+ * Both pairs answer one URL, and in both `matchUrlParts` resolves the tie by
683
+ * whichever `children` array lists first — so the winner is whichever the
684
+ * filesystem enumerated first, and renaming a directory flips it. That is
685
+ * exactly what `validateRouteGroupCollisions` and
686
+ * `validateAffixedSiblingConflicts` exist to reject for ordinary siblings.
687
+ *
688
+ * **The collision space is the anchor group, not the directory.** An earlier
689
+ * revision ran once per intercepting directory, which sees the left-hand pair
690
+ * and not the right-hand one: those leaves live in two different directories.
691
+ * The space is `groupByInterceptedAnchor`'s output — the same grouping
692
+ * `findInterceptingMatch` hands to one `matchUrlParts` call — so what is
693
+ * validated together is exactly what is resolved together. Interceptors at
694
+ * different anchors stay independent, which is the documented case, and two
695
+ * slots on one owner are two owners and so two spaces.
696
+ *
697
+ * **Two checks, and the difference between them matters.** A duplicate
698
+ * intercepted *pattern* is decided by string equality, which is exact: two
699
+ * leaves with one pattern claim one set of URLs, whatever the precedence rules
700
+ * are. Everything else is decided by asking the matcher — `collision-probe.ts`,
701
+ * the same oracle the main tree and every slot use, so the spaces cannot
702
+ * disagree about whether a pair of pages collides.
703
+ *
704
+ * Not covered: two affixed dynamics that tie on specificity (`(x)/a-[id]` vs
705
+ * `(y)/[slug]-b`, ambiguous only for a part shaped `a-…-b`). No single probe
706
+ * URL exercises both, and `validateAffixedSiblingConflicts` already rejects
707
+ * that pair by affix length — including through groups, inside intercepting
708
+ * subtrees, and between two interceptors sharing an anchor.
709
+ *
710
+ * The shape applies to both interception mechanisms — a slot interceptor and a
711
+ * children-path one produce their leaves through the same walk, and both are
712
+ * resolved by `findInterceptingMatch` — so this is called from the one place in
713
+ * `walkForPlacement` that sees every owner regardless of position.
714
+ */
715
+ function assertNoInterceptedPatternCollisions(owner, ownerPath) {
716
+ for (const group of groupByInterceptedAnchor(owner.urlPath, owner.children)) assertAnchorGroupHasNoCollisions(group, ownerPath);
717
+ }
718
+ /**
719
+ * Reject a children-path interceptor and a slot interceptor from the same owner
720
+ * that claim URLs in common.
721
+ *
722
+ * Within one mode the ambiguity is either non-existent (two children-path
723
+ * rewrites are interchangeable, §"Selection Between Owners") or harmless (two
724
+ * slots on one owner both render). Across modes one is a layout swap and the
725
+ * other is a modal overlay, and the walk order picks the children-path shape —
726
+ * making the modal dead, silently and deterministically (TIM-1319).
727
+ *
728
+ * Overlap is a **pattern intersection** test: do two intercepted URL patterns
729
+ * share any concrete URL? This is not the same question the within-mode
730
+ * collision oracle answers. `findOrderDependentCollision` checks whether two
731
+ * candidates in one `matchUrlParts` walk produce different winners when
732
+ * `children` order is reversed — that is, order-dependence. A static segment
733
+ * always beats a dynamic one deterministically (no order-dependence), so
734
+ * `/feed/foo` and `/feed/[id]` pass the oracle even though
735
+ * `pathnameMatchesPattern` — the actual rewrite matcher — accepts both. The
736
+ * pattern intersection test catches that: it walks both patterns segment by
737
+ * segment and asks whether any value satisfies both.
738
+ *
739
+ * The check collects intercepted patterns from children-path interceptors and
740
+ * from each slot's interceptors independently. Slots are checked one at a time
741
+ * so two slot interceptors from *different* slots claiming one URL — the legal
742
+ * "two slots on one owner" case — are never compared.
743
+ */
744
+ function assertNoCrossModeClaims(node, ownerPath) {
745
+ const childrenLeaves = [];
746
+ for (const child of node.children) {
747
+ if (child.segmentType !== "intercepting" || !child.interceptionMarker || !child.interceptedSegmentName) continue;
748
+ walkInterceptedLeaves(child, computeInterceptedUrlBase(node.urlPath, child.interceptionMarker, child.interceptedSegmentName), [child], (page, leaf) => {
749
+ childrenLeaves.push({
750
+ pattern: leaf.interceptedPattern,
751
+ filePath: page.filePath
752
+ });
753
+ });
754
+ }
755
+ if (childrenLeaves.length === 0) return;
756
+ const owner = ownerPath || "(app root)";
757
+ for (const slot of Object.values(node.slots)) {
758
+ const slotLeaves = [];
759
+ for (const child of slot.children) {
760
+ if (child.segmentType !== "intercepting" || !child.interceptionMarker || !child.interceptedSegmentName) continue;
761
+ walkInterceptedLeaves(child, computeInterceptedUrlBase(node.urlPath, child.interceptionMarker, child.interceptedSegmentName), [child], (page, leaf) => {
762
+ slotLeaves.push({
763
+ pattern: leaf.interceptedPattern,
764
+ filePath: page.filePath
765
+ });
766
+ });
767
+ }
768
+ for (const cl of childrenLeaves) for (const sl of slotLeaves) {
769
+ const overlap = interceptedPatternsOverlap(cl.pattern, sl.pattern);
770
+ if (overlap !== null) throwCrossModeClaim(owner, overlap, cl.filePath, sl.filePath);
771
+ }
772
+ }
773
+ }
774
+ function throwCrossModeClaim(owner, url, childFile, slotFile) {
775
+ throw new Error(`Build error: a children-path interception and a slot interception both claim the same URL.
776
+ Owner: app/${owner}\n URL: ${url}\n Children: ${childFile}\n Slot: ${slotFile}\nThe children-path interceptor replaces the owner's children (layout swap) while the slot interceptor renders inside a parallel slot (modal overlay). Both apply from the same scope at the same URL, and which one wins would depend on internal walk order rather than an explicit rule.\nRemove one of the two, or move them to different scopes.\nSee design/07-routing.md §"Selection Between Owners".`);
777
+ }
778
+ /**
779
+ * The URL-matching identity of an intercepting node, as a node.
780
+ *
781
+ * `matchUrlParts` reads `segmentType` directly and knows nothing about
782
+ * interception, so a marker'd directory has to be handed to it under the
783
+ * identity of the segment it intercepts — `(.)[id]` as `[id]`. Priority
784
+ * ordering, affix stripping and catch-all handling then come from the shared
785
+ * walker rather than from a second classifier here.
786
+ *
787
+ * Shared with `findInterceptingMatch` for the same reason the grouping is: the
788
+ * collision space is the resolver's synthetic anchor parent, so its children
789
+ * have to carry the identity the resolver gives them or the validator would be
790
+ * probing a tree that does not match the one served. The caller keeps its own
791
+ * map back to the real node — the proxy is a matching identity, not a
792
+ * replacement (tree paths still need the directory name).
793
+ */
794
+ function interceptorProxy(node) {
795
+ return {
796
+ ...node,
797
+ ...effectiveUrlSegment(node)
798
+ };
799
+ }
800
+ function assertAnchorGroupHasNoCollisions(group, ownerPath) {
801
+ const { base } = group;
802
+ const owner = ownerPath || "(app root)";
803
+ const proxies = group.interceptors.map(interceptorProxy);
804
+ const anchorNode = {
805
+ segmentName: "",
806
+ segmentType: "static",
807
+ urlPath: base,
808
+ children: proxies,
809
+ slots: {}
810
+ };
811
+ const patterns = /* @__PURE__ */ new Map();
812
+ const probeLeaves = [];
813
+ for (const proxy of proxies) walkInterceptedLeaves(proxy, joinInterceptedBase(base, proxy.segmentName), [proxy], (page, leaf) => {
814
+ const duplicate = patterns.get(leaf.interceptedPattern);
815
+ if (duplicate) throw new Error(`Build error: route collision between intercepting routes — two pages intercept the same URL.\n Owner: app/${owner}\n Intercepted base: ${base}\n URL: ${leaf.interceptedPattern}\n File 1: ${duplicate}\n File 2: ${page.filePath}\nA route group is transparent for matching, so moving a page into one does not give it a URL of its own — these two claim the same one. Rename or move one of them.\nSee design/07-routing.md §"Collisions Among Intercepting Routes".`);
816
+ patterns.set(leaf.interceptedPattern, page.filePath);
817
+ probeLeaves.push({
818
+ filePath: page.filePath,
819
+ chain: leaf.chain
820
+ });
821
+ });
822
+ const collision = findOrderDependentCollision(anchorNode, probeLeaves, base);
823
+ if (collision) throw new Error(`Build error: route collision between intercepting routes — two pages answer the same URL.\n Owner: app/${owner}\n Intercepted base: ${base}\n URL: ${collision.url}\n File 1: ${collision.first}\n File 2: ${collision.second}\nBoth match it, so which one renders would be decided by filesystem enumeration order — renaming a route group can flip it. Rename or move one of them.\nPages are compared by the URLs they match, not by how they are spelled: [id] and [slug] match the same URLs, and an optional catch-all also matches with zero segments.\nIntercepting directories based at *different* URLs may target one page — the resolver tries the deepest first — so this rejects only an ambiguity among the ones based at ${base}.\nSee design/07-routing.md §"Collisions Among Intercepting Routes".`);
824
+ }
825
+ function walkForPlacement(node, path, ctx) {
826
+ for (const child of node.children) {
827
+ const childPath = path ? `${path}/${child.segmentName}` : child.segmentName;
828
+ if (child.segmentType === "intercepting") {
829
+ const marker = child.interceptionMarker ?? "(.)";
830
+ if (ctx.inInterceptingSubtree) throw new Error(`Build error: intercepting route nested inside another intercepting route.\n Directory: app/${childPath}\nAn intercepting directory (${marker}) resolves against the URL its own parent renders at, and an intercepting subtree has no such URL — nothing walks it, so the rewrite would match the navigation and then have nothing to render.\nSee design/07-routing.md §"Intercepting Routes".`);
831
+ if (ctx.inSlotSubtree && !ctx.parentIsSlot) throw new Error(`Build error: intercepting route inside a parallel slot but not directly under it.\n Directory: app/${childPath}\nAn intercepting directory (${marker}) inside an @slot must be a *direct* child of that slot, e.g. app/feed/@modal/${child.segmentName}/page.tsx. The slot resolver scans the slot's direct children only; elsewhere the rewrite would match the navigation and then have nothing to render.\nSee design/07-routing.md §"Intercepting Routes".`);
832
+ if (!ctx.parentIsSlot && ctx.ancestorsBindParams) assertOwnerChainBindsNoParams(childPath, marker);
833
+ if (!ctx.parentIsSlot && !node.layout) assertOwnerHasLayout(path, childPath);
834
+ assertInterceptedBaseIsCountable(child, node.urlPath, childPath);
835
+ assertInterceptedCatchAllsAreTerminal(child, childPath);
836
+ assertNoRouteHandlersInSubtree(child, childPath);
837
+ }
838
+ walkForPlacement(child, childPath, {
839
+ parentIsSlot: false,
840
+ inSlotSubtree: ctx.inSlotSubtree,
841
+ inInterceptingSubtree: ctx.inInterceptingSubtree || child.segmentType === "intercepting",
842
+ ancestorsBindParams: ctx.ancestorsBindParams || !!child.paramName
843
+ });
844
+ }
845
+ assertNoInterceptedPatternCollisions(node, path);
846
+ for (const [slotName, slotNode] of Object.entries(node.slots)) walkForPlacement(slotNode, path ? `${path}/@${slotName}` : `@${slotName}`, {
847
+ parentIsSlot: true,
848
+ inSlotSubtree: true,
849
+ inInterceptingSubtree: ctx.inInterceptingSubtree,
850
+ ancestorsBindParams: false
851
+ });
852
+ assertNoCrossModeClaims(node, path);
853
+ }
854
+ /**
855
+ * Recursively walk the segment tree to find intercepting routes.
856
+ *
857
+ * `ancestors` deliberately excludes slot nodes: a slot doesn't appear in a
858
+ * matched route chain, so the last entry is always the nearest chain-visible
859
+ * ancestor — the segment that owns the slot, and therefore the segment whose
860
+ * presence in the source route gates the interception.
861
+ *
862
+ * Intercepting nodes only ever appear as a direct child of a slot or of an
863
+ * ordinary chain segment in a valid tree; `validateInterceptionPlacement`
864
+ * rejects every other placement at scan time, so this walk never has to
865
+ * decide what an unrenderable intercepting node should generate.
866
+ *
867
+ * `parentIsSlot` is what distinguishes the two shapes, and it is the only
868
+ * input that does: the owner, the scope and the intercepted pattern are all
869
+ * computed identically either way.
870
+ */
871
+ function walkForInterceptions(node, ancestors, parentIsSlot, rewrites) {
872
+ for (const child of node.children) if (child.segmentType === "intercepting" && child.interceptionMarker) collectFromInterceptingNode(child, ancestors, parentIsSlot, rewrites);
873
+ else walkForInterceptions(child, [...ancestors, child], false, rewrites);
874
+ for (const slot of Object.values(node.slots)) walkForInterceptions(slot, ancestors, true, rewrites);
875
+ }
876
+ /**
877
+ * For an intercepting segment, find all leaf pages in its sub-tree and
878
+ * generate rewrite rules for each.
879
+ */
880
+ function collectFromInterceptingNode(interceptingNode, ancestors, parentIsSlot, rewrites) {
881
+ const marker = interceptingNode.interceptionMarker;
882
+ const segmentName = interceptingNode.interceptedSegmentName;
883
+ const parentUrlPath = ancestors[ancestors.length - 1].urlPath;
884
+ const interceptedUrlBase = computeInterceptedUrlBase(parentUrlPath, marker, segmentName);
885
+ const ancestorPaths = computeSegmentTreePaths(ancestors);
886
+ const interceptingScope = ancestorPaths[ancestorPaths.length - 1];
887
+ collectLeavesWithRewrites(interceptingNode, interceptedUrlBase, parentIsSlot ? {
888
+ mode: "slot",
889
+ interceptingPrefix: parentUrlPath,
890
+ interceptingScope
891
+ } : {
892
+ mode: "children",
893
+ interceptingScope
894
+ }, rewrites);
895
+ }
896
+ /**
897
+ * Walk an intercepting sub-tree and visit every page-bearing node with the URL
898
+ * pattern it is intercepted at, and URLs that reach it.
899
+ *
900
+ * Route groups are transparent, exactly as they are in the main tree — which
901
+ * is what makes two groups under one interceptor able to claim one URL, and is
902
+ * why the collision validator has to walk the same shape this does.
903
+ *
904
+ * Rewrite generation and that validator are the two callers. They share this
905
+ * walk rather than each having their own so they cannot disagree about which
906
+ * URLs an interceptor claims: a leaf the validator did not see is a rewrite
907
+ * nobody checked, and a leaf only the validator sees is a build error for a
908
+ * route that does not exist. Slots are not descended, here or in either caller
909
+ * — a slot nested inside an intercepting subtree produces no rewrite.
910
+ *
911
+ * That answers the rewrite question only, and the resolution question is
912
+ * answered elsewhere: `validateSlotsInsideInterceptingSubtree`
913
+ * (`collision-spaces.ts`) gives each such slot its own collision space, and
914
+ * `validateSlotPlacement` (`slot-placement.ts`) rejects the ones no renderer
915
+ * reaches. Between them the slots this walk skips are covered; do not conclude
916
+ * from the skip here that they are unvalidated (TIM-1308, TIM-1311).
917
+ *
918
+ * The walk reports the chain of URL-contributing nodes rather than building
919
+ * probe URLs itself: choosing a probe value means running the matcher over the
920
+ * whole subtree, which is the validator's business and not something rewrite
921
+ * generation should be paying for or passing dummy arguments around.
922
+ */
923
+ function walkInterceptedLeaves(node, interceptedUrlPath, chain, visit) {
924
+ if (node.page) visit(node.page, {
925
+ interceptedPattern: interceptedUrlPath,
926
+ chain
927
+ });
928
+ for (const child of node.children) {
929
+ const isGroup = child.segmentType === "group";
930
+ walkInterceptedLeaves(child, isGroup ? interceptedUrlPath : `${interceptedUrlPath}/${child.segmentName}`, isGroup ? chain : [...chain, child], visit);
931
+ }
932
+ }
933
+ /**
934
+ * Recursively find leaf pages in an intercepting sub-tree and generate
935
+ * rewrite rules for each.
936
+ */
937
+ function collectLeavesWithRewrites(node, interceptedUrlPath, shape, rewrites) {
938
+ walkInterceptedLeaves(node, interceptedUrlPath, [], (_page, leaf) => {
939
+ rewrites.push({
940
+ ...shape,
941
+ interceptedPattern: leaf.interceptedPattern
942
+ });
943
+ });
944
+ }
945
+ /**
946
+ * How many URL levels a marker climbs from the slot owner to reach the
947
+ * intercepted segment, or `'root'` for a marker that climbs all the way.
948
+ *
949
+ * Level counting operates on URL path segments, NOT filesystem directories.
950
+ * Route groups and parallel slots are already excluded from urlPath (they
951
+ * don't add URL depth), so (..) correctly climbs visible segments. This
952
+ * avoids the Vinext bug where path.dirname() on filesystem paths would
953
+ * waste climbs on invisible route groups.
954
+ *
955
+ * Used only by `computeInterceptedBase` below — both rewrite generation and
956
+ * the slot resolver go through that, so the arithmetic exists once.
957
+ */
958
+ function interceptionClimb(marker) {
959
+ switch (marker) {
960
+ case "(.)": return 0;
961
+ case "(..)": return 1;
962
+ case "(..)(..)": return 2;
963
+ case "(...)": return "root";
964
+ }
965
+ }
966
+ /**
967
+ * Compute the base URL that an intercepting route intercepts, given the
968
+ * parent's URL path and the interception marker.
969
+ *
970
+ * - (.) — same level: parent's URL path
971
+ * - (..) — one level up: parent's parent URL path
972
+ * - (...) — root level: /
973
+ * - (..)(..) — two levels up: parent's grandparent URL path
974
+ *
975
+ * The result is a URL **pattern**, not a URL — `parentUrlPath` carries the
976
+ * directory names, so a dynamic ancestor appears as `/docs/[...path]`.
977
+ * Rewrite generation wants exactly that. The slot resolver imports this to
978
+ * locate the intercepted segment in a concrete target pathname, and must
979
+ * therefore account for a bracket part standing for a different number of
980
+ * URL parts than itself (TIM-1281).
981
+ *
982
+ * Climbing past the root clamps to `/` rather than erroring, so generation
983
+ * and resolution never disagree about what an over-reaching marker
984
+ * intercepts.
985
+ */
986
+ /**
987
+ * The URL pattern an intercepting directory's own page sits at — its
988
+ * `computeInterceptedBase` joined with the segment it intercepts.
989
+ *
990
+ * The root case is why this is a function rather than a template literal at
991
+ * each caller: a base of `/` must not produce `//photo`. Rewrite generation
992
+ * and the collision validator both start their walk from this value, so the
993
+ * two describe the same URLs by construction.
994
+ */
995
+ function computeInterceptedUrlBase(parentUrlPath, marker, interceptedSegmentName) {
996
+ return joinInterceptedBase(computeInterceptedBase(parentUrlPath, marker), interceptedSegmentName);
997
+ }
998
+ /**
999
+ * An intercepted base joined with one segment beneath it.
1000
+ *
1001
+ * The whole content of the "function rather than a template literal" rule
1002
+ * above: a base of `/` must not produce `//photo`. The collision validator has
1003
+ * the base in hand already (it groups by it) and joins through this rather than
1004
+ * re-deriving it from an owner path, so both callers spell the root case once.
1005
+ */
1006
+ function joinInterceptedBase(base, segmentName) {
1007
+ return base === "/" ? `/${segmentName}` : `${base}/${segmentName}`;
1008
+ }
1009
+ function computeInterceptedBase(parentUrlPath, marker) {
1010
+ const climb = interceptionClimb(marker);
1011
+ if (climb === "root") return "/";
1012
+ const parts = parentUrlPath.split("/").filter(Boolean);
1013
+ for (let i = 0; i < climb; i++) parts.pop();
1014
+ return parts.length === 0 ? "/" : `/${parts.join("/")}`;
1015
+ }
1016
+ //#endregion
1017
+ //#region src/routing/collision-spaces.ts
1018
+ /**
1019
+ * Validate that no two pages/routes in one collision space answer the same URL.
1020
+ *
1021
+ * Two route groups like (auth)/login/page.tsx and (marketing)/login/page.tsx both claim
1022
+ * /login — the scanner must detect and reject this at build time.
1023
+ *
1024
+ * Each parallel slot is validated as its OWN collision space: a slot's pages
1025
+ * intentionally share URL paths with the parent tree (that's the point of
1026
+ * parallel routes), but two route groups INSIDE one slot claiming the same
1027
+ * URL — e.g. @side/(a)/x/page.tsx + @side/(b)/x/page.tsx — still collide.
1028
+ */
1029
+ function validateRouteGroupCollisions(root) {
1030
+ validateCollisionSpace(root, "", root.urlPath);
1031
+ }
1032
+ /**
1033
+ * Validate one collision space — the main tree, or one parallel slot.
1034
+ *
1035
+ * **Two checks, and the difference between them matters.** Two leaves at the
1036
+ * same `urlPath` are decided by string equality, which is exact and needs no
1037
+ * probing: `(auth)/login` and `(marketing)/login` claim one set of URLs
1038
+ * whatever the precedence rules are, and naming the shared path is the clearest
1039
+ * thing to tell the author.
1040
+ *
1041
+ * Everything else is decided by asking the matcher, through the shared oracle
1042
+ * in `collision-probe.ts` (TIM-1308). Comparing `urlPath` spellings misses
1043
+ * every pair that matches the same URLs while being *spelled* differently:
1044
+ * `x/(a)/[id]` beside `x/(b)/[slug]` are two bare dynamics in one priority
1045
+ * tier, and `x/(a)/page.tsx` beside `x/(b)/[[...rest]]` both answer `/x`
1046
+ * because an optional catch-all also matches zero parts. Both built before
1047
+ * TIM-1308 with filesystem enumeration order deciding which page renders,
1048
+ * which is precisely what this validator and `validateAffixedSiblingConflicts`
1049
+ * exist to reject.
1050
+ *
1051
+ * Segment *kinds* stay distinct without any special-casing here, because the
1052
+ * oracle inherits `matchNode`'s priority ordering: `x/(a)/[id]` beside
1053
+ * `x/(b)/[...rest]` resolves the same way in either `children` order, so it is
1054
+ * not reported. So does an affixed sibling against a bare one; the
1055
+ * equal-specificity affix tie is `validateAffixedSiblingConflicts`' job, and
1056
+ * duplicating it here would double-report.
1057
+ */
1058
+ function validateCollisionSpace(spaceRoot, segmentPath, base) {
1059
+ const leaves = [];
1060
+ collectRoutableLeaves(spaceRoot, /* @__PURE__ */ new Map(), segmentPath, [], leaves, base);
1061
+ const collision = findOrderDependentCollision(spaceRoot, leaves, base);
1062
+ if (!collision) return;
1063
+ throw new Error(`Build error: route collision — two pages/routes answer the same URL.\n URL: ${collision.url}\n File 1: ${collision.first}\n File 2: ${collision.second}\nBoth match it, so which one renders would be decided by filesystem enumeration order — renaming a route group can flip it. Rename or move one of them.\nRoutes are compared by the URLs they match, not by how they are spelled: [id] and [slug] match the same URLs, and an optional catch-all also matches with zero segments.`);
1064
+ }
1065
+ /**
1066
+ * Walk one collision space and collect its routable leaves (page or route
1067
+ * files), throwing on an exact `urlPath` collision. Each slot recurses as its
1068
+ * OWN space — slots are parallel routes that intentionally share URL paths with
1069
+ * their parent tree, but collisions within a single slot are real errors.
1070
+ *
1071
+ * The early return at an intercepting node drops that subtree's own pages from
1072
+ * this space, but not its slots: a slot is its own collision space wherever it
1073
+ * sits, so `validateSlotsInsideInterceptingSubtree` picks those up before
1074
+ * returning.
1075
+ *
1076
+ * `chain` accumulates the URL-contributing nodes from the space root down, for
1077
+ * the probe oracle. Route groups are excluded because they are transparent for
1078
+ * matching, and the space root itself is excluded because `matchUrlParts`
1079
+ * matches parts against its *children* — the root consumes nothing.
1080
+ */
1081
+ function collectRoutableLeaves(node, seen, segmentPath, chain, leaves, base) {
1082
+ const currentPath = segmentPath ? `${segmentPath}/${node.segmentName}` : node.segmentName || "(root)";
1083
+ if (node.segmentType === "intercepting") {
1084
+ validateSlotsInsideInterceptingSubtree(node, currentPath, interceptedBaseOf(node, node.urlPath));
1085
+ return;
1086
+ }
1087
+ const routableFile = node.page ?? node.route;
1088
+ if (routableFile) {
1089
+ const existing = seen.get(node.urlPath);
1090
+ if (existing) throw new Error(`Build error: route collision — multiple route groups produce a page/route at the same URL path.\n URL path: ${urlFromChain(base, chain)}\n File 1: ${existing.filePath} (via ${existing.segmentPath})\n File 2: ${routableFile.filePath} (via ${currentPath})\nEach URL path must map to exactly one page or route handler. Rename or move one of the conflicting files.`);
1091
+ seen.set(node.urlPath, {
1092
+ filePath: routableFile.filePath,
1093
+ segmentPath: currentPath
1094
+ });
1095
+ leaves.push({
1096
+ filePath: routableFile.filePath,
1097
+ chain
1098
+ });
1099
+ }
1100
+ for (const child of node.children) collectRoutableLeaves(child, seen, currentPath, child.segmentType === "group" ? chain : [...chain, child], leaves, base);
1101
+ for (const slotNode of Object.values(node.slots)) validateCollisionSpace(slotNode, currentPath, urlFromChain(base, chain));
1102
+ }
1103
+ /**
1104
+ * Validate every parallel slot nested anywhere inside an intercepting subtree.
1105
+ *
1106
+ * Two walks each skip these slots for their own good reason, and together they
1107
+ * left them checked by nothing: `collectRoutableLeaves` returns at the
1108
+ * intercepting node before it reaches them, and `walkInterceptedLeaves`
1109
+ * deliberately does not descend slots — correct for rewrite generation, since a
1110
+ * slot inside an intercepting subtree produces no rewrite. But "produces no
1111
+ * rewrite" answers the rewrite question and not the resolution one, so
1112
+ * `@side/(a)/x/page.tsx` beside `@side/(b)/x/page.tsx` under an interceptor
1113
+ * built with filesystem order picking the page — the ordinary per-slot
1114
+ * collision that fails the build anywhere else in the tree (codex, on PR #1002).
1115
+ *
1116
+ * A slot is its own collision space wherever it sits, so each one is validated
1117
+ * as one.
1118
+ *
1119
+ * **Which interception shape this still applies to.** TIM-1311 answered the
1120
+ * question this was written under — whether such a slot renders at all — and
1121
+ * the answer is per-shape:
1122
+ *
1123
+ * - **Children path** (`(browse)/(...)photo/@side`): the intercepting node
1124
+ * joins the main route chain, so `route-element-builder.ts` resolves slots on
1125
+ * it and below it like any other chain segment. They render, and this is the
1126
+ * space where the check is load-bearing.
1127
+ * - **Modal** (`feed/@modal/(.)photo/@side`): the slot sits inside another
1128
+ * slot's subtree, which no renderer descends for slots — so it never renders,
1129
+ * and `validateSlotPlacement` now rejects the placement before this runs.
1130
+ * That rule subsumes this one for the modal shape rather than conflicting
1131
+ * with it, exactly as anticipated.
1132
+ *
1133
+ * So this walk is reached only for children-path interceptors in a valid tree.
1134
+ * It is not narrowed to them: the rule it states — a slot is its own collision
1135
+ * space wherever it sits — is shape-independent, and making it conditional on a
1136
+ * property the placement validator already guarantees would just be a second
1137
+ * place for the two to disagree.
1138
+ *
1139
+ * The walk covers the whole subtree, not just the interceptor's direct slot
1140
+ * children: `(...)photo/[id]/@side` is reached by neither walk for exactly the
1141
+ * same reason as `(...)photo/@side`.
1142
+ */
1143
+ function validateSlotsInsideInterceptingSubtree(node, segmentPath, base) {
1144
+ for (const slotNode of Object.values(node.slots)) validateCollisionSpace(slotNode, segmentPath, base);
1145
+ for (const child of node.children) validateSlotsInsideInterceptingSubtree(child, `${segmentPath}/${child.segmentName}`, childInterceptedBase(child, base));
1146
+ }
1147
+ /**
1148
+ * The URL a leaf answers, built from its space's base and its own chain.
1149
+ *
1150
+ * Read instead of `node.urlPath` because `urlPath` inside an intercepting
1151
+ * subtree is parent-relative and is not an address — the collision URL has to
1152
+ * be one the author can actually find. Outside an intercepting subtree the two
1153
+ * are identical by construction: the base is the space root's `urlPath` and the
1154
+ * chain is every URL-contributing segment below it.
1155
+ */
1156
+ function urlFromChain(base, chain) {
1157
+ if (chain.length === 0) return base || "/";
1158
+ const tail = chain.map((n) => n.segmentName).join("/");
1159
+ return `${base === "/" ? "" : base}/${tail}`;
1160
+ }
1161
+ /**
1162
+ * The intercepted URL an intercepting directory's own page sits at.
1163
+ *
1164
+ * `urlPath` inside an intercepting subtree is deliberately parent-relative and
1165
+ * is **not** an address (see design/07-routing.md §"Why urlPath Stays
1166
+ * Parent-Relative Inside an Intercepting Subtree"), so a collision reported
1167
+ * from one names a URL the author cannot find. The intercepted base is the real
1168
+ * address, and it is what rewrite generation and the interception collision
1169
+ * validator both already use.
1170
+ */
1171
+ function interceptedBaseOf(node, ownerUrlPath) {
1172
+ return computeInterceptedUrlBase(ownerUrlPath, node.interceptionMarker ?? "(.)", node.interceptedSegmentName ?? node.segmentName);
1173
+ }
1174
+ /**
1175
+ * Extend an intercepted base by one child segment.
1176
+ *
1177
+ * Route groups are transparent and contribute nothing. A nested intercepting
1178
+ * node re-bases from its own marker rather than appending — that tree is
1179
+ * rejected by `validateInterceptionPlacement`, but collision validation runs
1180
+ * first, so it still has to produce a sensible URL rather than one containing a
1181
+ * literal "(.)b".
1182
+ */
1183
+ function childInterceptedBase(child, base) {
1184
+ if (child.segmentType === "group") return base;
1185
+ if (child.segmentType === "intercepting") return interceptedBaseOf(child, base);
1186
+ return base === "/" ? `/${child.segmentName}` : `${base}/${child.segmentName}`;
1187
+ }
1188
+ //#endregion
1189
+ //#region src/routing/slot-placement.ts
1190
+ /**
1191
+ * Reject every parallel slot that no renderer reaches.
1192
+ *
1193
+ * **Where slots are resolved.** Two places read `segment.slots`, and both walk
1194
+ * the main route chain: `route-element-builder.ts` (the RSC render).
1195
+ * It is not reachable from inside a slot. The slot's own
1196
+ * subtree is rendered by `slot-resolver.ts`, which walks the slot's matched
1197
+ * chain to apply that chain's layouts, error boundaries and access gates
1198
+ * (`wrapWithIntermediateSegments`, `wrapWithLayout`) and never reads `.slots`
1199
+ * on any of those nodes. So `app/feed/@modal/photo/@side/page.tsx` and
1200
+ * everything below it is dead: the file is scanned, typed, code-split and
1201
+ * shipped, and no request can render it.
1202
+ *
1203
+ * That is the TIM-1278 failure — a directory that silently never renders —
1204
+ * and the reason it is a build error rather than a documented limit is that
1205
+ * nothing at request time can report it. A slot that does not match renders
1206
+ * `default.tsx` or `null`, which is exactly what a slot the resolver never
1207
+ * looked at also produces, so the author sees the *designed* behaviour of a
1208
+ * non-matching slot and has no signal to distinguish it from a slot that
1209
+ * cannot match at all.
1210
+ *
1211
+ * **Interception is incidental.** TIM-1311 arrived as
1212
+ * `feed/@modal/(.)photo/@side`, and the interception contributes nothing: the
1213
+ * identical `feed/@modal/photo/@side` never renders either, for the same
1214
+ * reason and with no marker anywhere. Rejecting only the intercepting spelling
1215
+ * would have drawn a line the author cannot see from the directory names.
1216
+ *
1217
+ * The children-path interception shape is **not** affected and must not be:
1218
+ * its intercepting node joins the main chain (`server/children-interception.ts`
1219
+ * splices `[…owner ancestors, interceptingNode, …descendants]` into an ordinary
1220
+ * `RouteMatch`), so `route-element-builder.ts` resolves slots on it and below
1221
+ * it exactly as it does for any other chain segment. `(browse)/(...)photo/[id]/@side`
1222
+ * renders; `feed/@modal/(.)photo/@side` does not. The discriminator is the slot
1223
+ * boundary, which is what this walk tracks — not the marker.
1224
+ */
1225
+ function validateSlotPlacement(root) {
1226
+ walkForSlotPlacement(root, "", null);
1227
+ }
1228
+ /**
1229
+ * `enclosingSlotPath` is the `app/`-relative path of the nearest slot at or
1230
+ * above `node`, or null on the main chain. It is carried rather than recomputed
1231
+ * so the error can name the slot that swallows the offender — "this is inside
1232
+ * @modal" is the part of the diagnosis the author cannot see from the offending
1233
+ * path alone.
1234
+ */
1235
+ function walkForSlotPlacement(node, path, enclosingSlotPath) {
1236
+ for (const [slotName, slotNode] of Object.entries(node.slots)) {
1237
+ const slotPath = path ? `${path}/@${slotName}` : `@${slotName}`;
1238
+ if (enclosingSlotPath !== null) throwNestedSlot(slotPath, enclosingSlotPath);
1239
+ walkForSlotPlacement(slotNode, slotPath, slotPath);
1240
+ }
1241
+ for (const child of node.children) walkForSlotPlacement(child, path ? `${path}/${child.segmentName}` : child.segmentName, enclosingSlotPath);
1242
+ }
1243
+ function throwNestedSlot(slotPath, enclosingSlotPath) {
1244
+ throw new Error(`Build error: parallel slot nested inside another parallel slot.\n Directory: app/${slotPath}\n Inside: app/${enclosingSlotPath}\nA parallel slot is resolved only for a segment on the matched route chain, and a slot's own subtree is never on that chain — the slot resolver walks the slot's matched chain to apply its layouts, error boundaries and access gates, and never looks at slots declared inside it. So app/${slotPath} and everything below it can never render, and no request-time signal can tell you: an unreachable slot produces exactly what a slot that simply did not match produces.\nDeclare the slot on a segment of the main route chain instead, or move its pages into app/${enclosingSlotPath}'s own subtree.\nSee design/07-routing.md §"Parallel Routes".`);
1245
+ }
1246
+ //#endregion
1247
+ //#region src/routing/file-cache.ts
1248
+ var cache = /* @__PURE__ */ new Map();
1249
+ /**
1250
+ * Read a file's text content, returning a cached copy when the file
1251
+ * hasn't been modified since the last read (checked via mtime + size).
1252
+ *
1253
+ * Falls back to a fresh read on any stat/read error.
1254
+ */
1255
+ function readFileCached(filePath) {
1256
+ try {
1257
+ const stat = statSync(filePath);
1258
+ const entry = cache.get(filePath);
1259
+ if (entry && entry.mtimeMs === stat.mtimeMs && entry.size === stat.size) return entry.content;
1260
+ const content = readFileSync(filePath, "utf-8");
1261
+ cache.set(filePath, {
1262
+ mtimeMs: stat.mtimeMs,
1263
+ size: stat.size,
1264
+ content
1265
+ });
1266
+ return content;
1267
+ } catch {
1268
+ cache.delete(filePath);
1269
+ return readFileSync(filePath, "utf-8");
1270
+ }
1271
+ }
1272
+ //#endregion
1273
+ //#region src/routing/export-detect.ts
1274
+ /**
1275
+ * AST-based export detection for route files.
1276
+ *
1277
+ * Uses Vite's `parseAst` (backed by oxc) to precisely detect named and
1278
+ * default exports, replacing the regex-based approach that was fragile
1279
+ * with TypeScript syntax (type exports, `as` aliases, comments, etc.).
1280
+ */
1281
+ function tryParse(source) {
1282
+ try {
1283
+ return parseAst(source, { lang: "tsx" });
1284
+ } catch {
1285
+ return null;
1286
+ }
1287
+ }
1288
+ /**
1289
+ * Collect all named export identifiers from a parsed program.
1290
+ * Handles: `export function X`, `export const X`, `export { X }`,
1291
+ * `export { X } from '...'`, `export { Y as X }`.
1292
+ */
1293
+ function collectNamedExports(program) {
1294
+ const names = /* @__PURE__ */ new Set();
1295
+ for (const stmt of program.body) {
1296
+ if (stmt.type !== "ExportNamedDeclaration") continue;
1297
+ if (stmt.exportKind === "type") continue;
1298
+ if (stmt.declaration) {
1299
+ if (stmt.declaration.id?.name) names.add(stmt.declaration.id.name);
1300
+ if (stmt.declaration.declarations) {
1301
+ for (const decl of stmt.declaration.declarations) if (decl.id?.name) names.add(decl.id.name);
1302
+ }
1303
+ }
1304
+ if (stmt.specifiers) for (const spec of stmt.specifiers) {
1305
+ if (spec.exportKind === "type") continue;
1306
+ if (spec.exported?.name && spec.exported.name !== "default") names.add(spec.exported.name);
1307
+ }
1308
+ }
1309
+ return names;
1310
+ }
1311
+ /**
1312
+ * The specifier exporting a *runtime* default from this statement, if any.
1313
+ *
1314
+ * Type-only exports erase, so they are not default exports at runtime — and
1315
+ * TypeScript spells that two ways, on two different nodes: `export type { X as
1316
+ * default }` marks the **statement**, `export { type X as default }` marks the
1317
+ * **specifier**. Both have to be checked, or a type alias reads as a component:
1318
+ * a status file whose only default is a type has nothing for the error boundary
1319
+ * to receive (the renderer's `mod?.default` skips it), so treating it as one
1320
+ * would fail a build over a file that cannot break anything.
1321
+ *
1322
+ * One implementation because two callers must agree on what "has a default
1323
+ * export" means: `hasDefaultExport` decides whether to warn that a page has
1324
+ * none, and `fileDefaultExportIsLocalValue` decides whether to fail the build.
1325
+ */
1326
+ function defaultSpecifier(stmt) {
1327
+ if (stmt.type !== "ExportNamedDeclaration" || !stmt.specifiers) return null;
1328
+ if (stmt.exportKind === "type") return null;
1329
+ for (const spec of stmt.specifiers) {
1330
+ if (spec.exportKind === "type") continue;
1331
+ if (spec.exported?.name === "default") return spec;
1332
+ }
1333
+ return null;
1334
+ }
1335
+ /**
1336
+ * Check whether a program has a default export.
1337
+ * Handles: `export default ...`, `export { X as default }`,
1338
+ * `export { default } from '...'`. Type-only defaults do not count —
1339
+ * see `defaultSpecifier`.
1340
+ */
1341
+ function hasDefaultExport(program) {
1342
+ for (const stmt of program.body) {
1343
+ if (stmt.type === "ExportDefaultDeclaration") return true;
1344
+ if (defaultSpecifier(stmt)) return true;
1345
+ }
1346
+ return false;
1347
+ }
1348
+ /**
1349
+ * Check whether a program has any bare `export * from '...'` declarations.
1350
+ * Excludes `export * as X from '...'` (namespace re-exports) since those
1351
+ * create a namespace object, not individual top-level exports.
1352
+ */
1353
+ function hasStarExport(program) {
1354
+ return program.body.some((stmt) => stmt.type === "ExportAllDeclaration" && !stmt.exported && stmt.exportKind !== "type");
1355
+ }
1356
+ /**
1357
+ * Check if a file has any of the given named exports.
1358
+ * Returns the set of matching export names.
1359
+ */
1360
+ function fileHasAnyExport(filePath, exportNames) {
1361
+ const matches = /* @__PURE__ */ new Set();
1362
+ try {
1363
+ const program = tryParse(readFileCached(filePath));
1364
+ if (!program) return matches;
1365
+ const exports = collectNamedExports(program);
1366
+ for (const name of exportNames) if (exports.has(name)) matches.add(name);
1367
+ } catch {}
1368
+ return matches;
1369
+ }
1370
+ /**
1371
+ * Check if a file has a default export.
1372
+ * Returns false if the file doesn't exist or can't be parsed.
1373
+ */
1374
+ function fileHasDefaultExport(filePath) {
1375
+ if (!existsSync(filePath)) return false;
1376
+ try {
1377
+ const program = tryParse(readFileCached(filePath));
1378
+ if (!program) return false;
1379
+ return hasDefaultExport(program);
1380
+ } catch {
1381
+ return false;
1382
+ }
1383
+ }
1384
+ /** Node types whose value is unambiguously constructed by this module. */
1385
+ var FUNCTION_LIKE = /* @__PURE__ */ new Set([
1386
+ "FunctionDeclaration",
1387
+ "FunctionExpression",
1388
+ "ArrowFunctionExpression",
1389
+ "ClassDeclaration",
1390
+ "ClassExpression"
1391
+ ]);
1392
+ /**
1393
+ * Expression wrappers that erase at runtime and so cannot change where a value
1394
+ * was constructed: `X as T`, `X satisfies T`, `<T>X`, `X!`, `X<T>`, `(X)`.
1395
+ */
1396
+ var TRANSPARENT_WRAPPERS = /* @__PURE__ */ new Set([
1397
+ "TSAsExpression",
1398
+ "TSSatisfiesExpression",
1399
+ "TSTypeAssertion",
1400
+ "TSNonNullExpression",
1401
+ "TSInstantiationExpression",
1402
+ "ParenthesizedExpression"
1403
+ ]);
1404
+ /**
1405
+ * Strip runtime-transparent wrappers so the node underneath can be classified.
1406
+ *
1407
+ * Without this, `(() => <h1 />) satisfies React.FC` reads as a
1408
+ * `TSSatisfiesExpression` rather than the arrow function it compiles to, and a
1409
+ * provably-local component is misread as one whose origin cannot be
1410
+ * determined — turning a build error into a warning.
1411
+ */
1412
+ function unwrapTransparent(node) {
1413
+ let current = node;
1414
+ while (TRANSPARENT_WRAPPERS.has(current.type) && current.expression) current = current.expression;
1415
+ return current;
1416
+ }
1417
+ /**
1418
+ * Whether `name` is bound at module top level to a function or class written
1419
+ * in this file. An imported binding can never match: imports are
1420
+ * `ImportDeclaration` specifiers, not function/class/variable declarations,
1421
+ * and a module-level name cannot be both.
1422
+ */
1423
+ function bindsLocalFunction(program, name) {
1424
+ for (const stmt of program.body) {
1425
+ const decl = stmt.type === "ExportNamedDeclaration" ? stmt.declaration : stmt;
1426
+ if (!decl) continue;
1427
+ if ((decl.type === "FunctionDeclaration" || decl.type === "ClassDeclaration") && decl.id?.name === name) return true;
1428
+ if (decl.type === "VariableDeclaration") {
1429
+ for (const d of decl.declarations ?? []) if (d.id?.name === name && d.init && FUNCTION_LIKE.has(unwrapTransparent(d.init).type)) return true;
1430
+ }
1431
+ }
1432
+ return false;
1433
+ }
1434
+ /**
1435
+ * Whether the default export is *provably* a value constructed by this module:
1436
+ * `export default function/class/() => {}`, or a name bound to a function or
1437
+ * class declared here — whether it leaves via `export default Name` or
1438
+ * `export { Name as default }`. Both spellings are the same fact about where
1439
+ * the value was built, so both must answer the same way.
1440
+ *
1441
+ * This is the sound half of "is this a server component?". A file with no
1442
+ * `'use client'` directive whose default export is built here is a server
1443
+ * component, full stop. Everything else — `export { default } from
1444
+ * './client-thing'`, `export default Imported`, `export default memo(X)`,
1445
+ * an unparseable file — returns false, because the exported value may have
1446
+ * been minted in a module that *does* carry the directive. Callers must not
1447
+ * infer "server component" from a missing directive alone.
1448
+ */
1449
+ function fileDefaultExportIsLocalValue(filePath) {
1450
+ if (!existsSync(filePath)) return false;
1451
+ try {
1452
+ const program = tryParse(readFileCached(filePath));
1453
+ if (!program) return false;
1454
+ for (const stmt of program.body) {
1455
+ if (stmt.type === "ExportDefaultDeclaration") {
1456
+ const decl = stmt.declaration && unwrapTransparent(stmt.declaration);
1457
+ if (!decl) return false;
1458
+ if (FUNCTION_LIKE.has(decl.type)) return true;
1459
+ if (decl.type === "Identifier" && decl.name) return bindsLocalFunction(program, decl.name);
1460
+ return false;
1461
+ }
1462
+ const spec = defaultSpecifier(stmt);
1463
+ if (spec) {
1464
+ if (stmt.source) return false;
1465
+ return spec.local?.name ? bindsLocalFunction(program, spec.local.name) : false;
1466
+ }
1467
+ }
1468
+ return false;
1469
+ } catch {
1470
+ return false;
1471
+ }
1472
+ }
1473
+ /**
1474
+ * Check if a file has any `export * from '...'` declarations.
1475
+ * Returns false if the file doesn't exist or can't be parsed.
1476
+ */
1477
+ function fileHasStarExport(filePath) {
1478
+ if (!existsSync(filePath)) return false;
1479
+ try {
1480
+ const program = tryParse(readFileCached(filePath));
1481
+ if (!program) return false;
1482
+ return hasStarExport(program);
1483
+ } catch {
1484
+ return false;
1485
+ }
1486
+ }
1487
+ function extractLiteralValue(node) {
1488
+ if (!node) return void 0;
1489
+ const n = node;
1490
+ if (n.type === "Literal") return n.value;
1491
+ if (n.type === "UnaryExpression" && n.operator === "-" && n.argument?.type === "Literal") return -n.argument.value;
1492
+ if (n.type === "ArrayExpression") {
1493
+ const elements = n.elements;
1494
+ if (!elements) return void 0;
1495
+ const result = [];
1496
+ for (const el of elements) {
1497
+ const v = extractLiteralValue(el);
1498
+ if (v === void 0) return void 0;
1499
+ result.push(v);
1500
+ }
1501
+ return result;
1502
+ }
1503
+ }
1504
+ function extractObjectLiteral(node) {
1505
+ if (node.type !== "ObjectExpression") return void 0;
1506
+ const props = node.properties;
1507
+ if (!props) return void 0;
1508
+ const result = {};
1509
+ for (const prop of props) {
1510
+ const p = prop;
1511
+ if (p.type === "SpreadElement") return void 0;
1512
+ if (p.type !== "Property" || p.computed) return void 0;
1513
+ const key = p.key?.type === "Identifier" ? p.key.name : p.key?.type === "Literal" ? String(p.key.value) : void 0;
1514
+ if (!key) return void 0;
1515
+ const value = extractLiteralValue(p.value);
1516
+ if (value === void 0) return void 0;
1517
+ result[key] = value;
1518
+ }
1519
+ return result;
1520
+ }
1521
+ /**
1522
+ * Extract the value of `export const prerender` from a file.
1523
+ * Returns undefined if the file doesn't export `prerender` or can't be parsed.
1524
+ *
1525
+ * Supports:
1526
+ * export const prerender = true;
1527
+ * export const prerender = false;
1528
+ * export const prerender = { ttl: 3600, tags: ['docs'] };
1529
+ */
1530
+ function getPrerenderExport(filePath) {
1531
+ if (!existsSync(filePath)) return void 0;
1532
+ try {
1533
+ const program = tryParse(readFileCached(filePath));
1534
+ if (!program) return void 0;
1535
+ for (const stmt of program.body) {
1536
+ if (stmt.type !== "ExportNamedDeclaration") continue;
1537
+ if (stmt.exportKind === "type") continue;
1538
+ if (!stmt.declaration) continue;
1539
+ const decl = stmt.declaration;
1540
+ if (decl.type !== "VariableDeclaration") continue;
1541
+ const declarations = decl.declarations;
1542
+ if (!declarations) continue;
1543
+ for (const d of declarations) {
1544
+ if (d.id?.name !== "prerender" || !d.init) continue;
1545
+ const init = d.init;
1546
+ if (init.type === "Literal" && typeof init.value === "boolean") return init.value;
1547
+ if (init.type === "ObjectExpression") {
1548
+ const obj = extractObjectLiteral(init);
1549
+ if (!obj) return void 0;
1550
+ const result = {};
1551
+ if ("ttl" in obj && typeof obj.ttl === "number") result.ttl = obj.ttl;
1552
+ if ("tags" in obj && Array.isArray(obj.tags) && obj.tags.every((t) => typeof t === "string")) result.tags = obj.tags;
1553
+ return result;
1554
+ }
1555
+ return;
1556
+ }
1557
+ }
1558
+ return;
1559
+ } catch {
1560
+ return;
1561
+ }
1562
+ }
1563
+ function getCsrfExport(filePath) {
1564
+ if (!existsSync(filePath)) return "absent";
1565
+ const program = tryParse(readFileCached(filePath));
1566
+ if (!program) return "absent";
1567
+ let found = "absent";
1568
+ for (const stmt of program.body) {
1569
+ if (stmt.type !== "ExportNamedDeclaration" || stmt.exportKind === "type") continue;
1570
+ for (const spec of stmt.specifiers ?? []) if (spec.exportKind !== "type" && specifierName(spec.exported) === "csrf") return "invalid";
1571
+ const decl = stmt.declaration;
1572
+ if (!decl) continue;
1573
+ if (decl.id?.name === "csrf") return "invalid";
1574
+ if (decl.type !== "VariableDeclaration") continue;
1575
+ for (const d of decl.declarations ?? []) {
1576
+ const id = d.id;
1577
+ if (id?.type === "Identifier") {
1578
+ if (id.name !== "csrf") continue;
1579
+ const init = d.init && unwrapTransparent(d.init);
1580
+ const literalFalse = init?.type === "Literal" && init.value === false;
1581
+ if (decl.kind !== "const" || !literalFalse) return "invalid";
1582
+ found = "exempt";
1583
+ } else if (id && patternBinds(id, "csrf")) return "invalid";
1584
+ }
1585
+ }
1586
+ return found;
1587
+ }
1588
+ /** An export specifier's name: `csrf` or the string literal `"csrf"`. */
1589
+ function specifierName(node) {
1590
+ if (!node) return void 0;
1591
+ return typeof node.value === "string" ? node.value : node.name;
1592
+ }
1593
+ /** Whether a destructuring pattern binds `name` anywhere inside it. */
1594
+ function patternBinds(node, name) {
1595
+ if (!node || typeof node !== "object") return false;
1596
+ const n = node;
1597
+ switch (n.type) {
1598
+ case "Identifier": return n.name === name;
1599
+ case "ObjectPattern": return (n.properties ?? []).some((prop) => {
1600
+ const p = prop;
1601
+ return patternBinds(p.type === "RestElement" ? p.argument : p.value, name);
1602
+ });
1603
+ case "ArrayPattern": return (n.elements ?? []).some((el) => patternBinds(el, name));
1604
+ case "RestElement": return patternBinds(n.argument, name);
1605
+ case "AssignmentPattern": return patternBinds(n.left, name);
1606
+ default: return false;
1607
+ }
1608
+ }
1609
+ /**
1610
+ * Check if a file starts with a specific directive (e.g. "use client").
1611
+ * Directives are string literal expression statements at the top of the file.
1612
+ */
1613
+ function fileHasDirective(filePath, directive) {
1614
+ if (!existsSync(filePath)) return false;
1615
+ try {
1616
+ const program = tryParse(readFileCached(filePath));
1617
+ if (!program) return false;
1618
+ for (const stmt of program.body) {
1619
+ if (stmt.type !== "ExpressionStatement") break;
1620
+ const expr = stmt.expression;
1621
+ if (expr?.type !== "Literal" || typeof expr.value !== "string") break;
1622
+ if (expr.value === directive) return true;
1623
+ }
1624
+ return false;
1625
+ } catch {
1626
+ return false;
1627
+ }
1628
+ }
1629
+ //#endregion
1630
+ //#region src/routing/scanner.ts
1631
+ /**
1632
+ * Route discovery scanner.
1633
+ *
1634
+ * Pure function: (appDir, config) → RouteTree
1635
+ *
1636
+ * Scans the app/ directory and builds a segment tree recognizing all
1637
+ * timber.js file conventions. Does NOT handle request matching — this
1638
+ * is discovery only.
1639
+ */
1640
+ /**
1641
+ * File convention names that use pageExtensions (can be .tsx, .ts, .jsx, .js, .mdx, etc.)
1642
+ * `error` is excluded — it only accepts script extensions (TIM-1505).
1643
+ */
1644
+ var PAGE_EXT_CONVENTIONS = /* @__PURE__ */ new Set([
1645
+ "page",
1646
+ "layout",
1647
+ "default",
1648
+ "denied"
1649
+ ]);
1650
+ /**
1651
+ * File convention names that are always .ts/.tsx (never .mdx etc.)
1652
+ */
1653
+ var FIXED_CONVENTIONS = /* @__PURE__ */ new Set([
1654
+ "middleware",
1655
+ "access",
1656
+ "route"
1657
+ ]);
1658
+ /**
1659
+ * Status-code file patterns:
1660
+ * - Exact 3-digit codes: 401.tsx, 429.tsx, 503.tsx
1661
+ * - Category catch-alls: 4xx.tsx, 5xx.tsx
1662
+ */
1663
+ var STATUS_CODE_PATTERN = /^(\d{3}|[45]xx)$/;
1664
+ /**
1665
+ * Whether a basename + extension pair is a route-convention file the
1666
+ * framework itself imports: page/layout/default/denied on pageExtensions,
1667
+ * error/global-error and status-code files on script extensions only,
1668
+ * middleware/access/route/proxy on .ts/.tsx/.js/.jsx only, and
1669
+ * status-code .json files — mirroring this scanner's own recognition
1670
+ * rules per family.
1671
+ *
1672
+ * Exported for the analyze classifier so its provisional classification
1673
+ * (design/47 §2) derives from the scanner's real conventions instead of
1674
+ * a duplicate list that can drift.
1675
+ */
1676
+ function isRouteConventionFile(base, ext, pageExtensions = DEFAULT_PAGE_EXTENSIONS) {
1677
+ const isPageExt = pageExtensions.includes(ext);
1678
+ const isScriptExt = /^[jt]sx?$/.test(ext);
1679
+ if (PAGE_EXT_CONVENTIONS.has(base) && isPageExt) return true;
1680
+ if ((base === "error" || base === "global-error") && isScriptExt) return true;
1681
+ if ((FIXED_CONVENTIONS.has(base) || base === "proxy") && isScriptExt) return true;
1682
+ if (STATUS_CODE_PATTERN.test(base) && (isScriptExt || ext === "json")) return true;
1683
+ return false;
1684
+ }
1685
+ /**
1686
+ * Scan the app/ directory and build the route tree.
1687
+ *
1688
+ * @param appDir - Absolute path to the app/ directory
1689
+ * @param config - Scanner configuration
1690
+ * @returns The complete route tree
1691
+ */
1692
+ function scanRoutes(appDir, config = {}) {
1693
+ const pageExtensions = config.pageExtensions ?? DEFAULT_PAGE_EXTENSIONS;
1694
+ const extSet = new Set(pageExtensions);
1695
+ const tree = { root: createSegmentNode("", "static", "/") };
1696
+ const proxyFile = findFixedFile(appDir, "proxy");
1697
+ if (proxyFile) tree.proxy = proxyFile;
1698
+ const globalErrorFile = findScriptExtFile(appDir, "global-error");
1699
+ if (globalErrorFile) tree.globalError = globalErrorFile;
1700
+ scanSegmentFiles(appDir, tree.root, extSet);
1701
+ scanChildren(appDir, tree.root, extSet);
1702
+ validateSlotPlacement(tree.root);
1703
+ validateRouteGroupCollisions(tree.root);
1704
+ validateDuplicateParamNames(tree.root);
1705
+ validateUniqueBracketForms(tree.root);
1706
+ validateAffixedSiblingConflicts(tree.root);
1707
+ validateInterceptionPlacement(tree.root);
1708
+ return tree;
1709
+ }
1710
+ /**
1711
+ * Create an empty segment node.
1712
+ */
1713
+ function createSegmentNode(segmentName, segmentType, urlPath, paramName, interceptionMarker, interceptedSegmentName, paramPrefix, paramSuffix) {
1714
+ return {
1715
+ segmentName,
1716
+ segmentType,
1717
+ urlPath,
1718
+ paramName,
1719
+ paramPrefix,
1720
+ paramSuffix,
1721
+ interceptionMarker,
1722
+ interceptedSegmentName,
1723
+ children: [],
1724
+ slots: {}
1725
+ };
1726
+ }
1727
+ /**
1728
+ * Compute the URL path for a child segment given its parent's URL path.
1729
+ * Route groups, slots, and intercepting routes do NOT add URL depth.
1730
+ */
1731
+ function computeUrlPath(parentUrlPath, dirName, segmentType) {
1732
+ if (segmentType === "group" || segmentType === "slot" || segmentType === "intercepting") return parentUrlPath;
1733
+ return `${parentUrlPath === "/" ? "" : parentUrlPath}/${dirName}`;
1734
+ }
1735
+ /**
1736
+ * Scan a directory for file conventions and populate the segment node.
1737
+ */
1738
+ function scanSegmentFiles(dirPath, node, extSet) {
1739
+ let entries;
1740
+ try {
1741
+ entries = readdirSync(dirPath);
1742
+ } catch (err) {
1743
+ swallow(err, `scanSegmentFiles: unreadable directory ${dirPath}`, { level: "warn" });
1744
+ return;
1745
+ }
1746
+ for (const entry of entries) {
1747
+ const fullPath = join(dirPath, entry);
1748
+ try {
1749
+ if (statSync(fullPath).isDirectory()) continue;
1750
+ } catch {
1751
+ continue;
1752
+ }
1753
+ const ext = extname(entry).slice(1);
1754
+ const name = basename(entry, `.${ext}`);
1755
+ if (PAGE_EXT_CONVENTIONS.has(name) && extSet.has(ext)) {
1756
+ const key = name;
1757
+ const existing = node[key];
1758
+ if (existing) throwDuplicateConvention(name, existing.filePath, fullPath);
1759
+ node[key] = {
1760
+ filePath: fullPath,
1761
+ extension: ext
1762
+ };
1763
+ continue;
1764
+ }
1765
+ if (name === "error" && /^[jt]sx?$/.test(ext)) {
1766
+ const existing = node.error;
1767
+ if (existing) throwDuplicateConvention("error", existing.filePath, fullPath);
1768
+ node.error = {
1769
+ filePath: fullPath,
1770
+ extension: ext
1771
+ };
1772
+ continue;
1773
+ }
1774
+ if (FIXED_CONVENTIONS.has(name) && /^[jt]sx?$/.test(ext)) {
1775
+ const key = name;
1776
+ const existing = node[key];
1777
+ if (existing) throwDuplicateConvention(name, existing.filePath, fullPath);
1778
+ node[key] = {
1779
+ filePath: fullPath,
1780
+ extension: ext
1781
+ };
1782
+ continue;
1783
+ }
1784
+ if (STATUS_CODE_PATTERN.test(name) && ext === "json") {
1785
+ if (!node.jsonStatusFiles) node.jsonStatusFiles = {};
1786
+ node.jsonStatusFiles[name] = {
1787
+ filePath: fullPath,
1788
+ extension: ext
1789
+ };
1790
+ continue;
1791
+ }
1792
+ if (STATUS_CODE_PATTERN.test(name) && /^[jt]sx?$/.test(ext)) {
1793
+ if (!node.statusFiles) node.statusFiles = {};
1794
+ const existing = node.statusFiles[name];
1795
+ if (existing) throwDuplicateConvention(name, existing.filePath, fullPath);
1796
+ node.statusFiles[name] = {
1797
+ filePath: fullPath,
1798
+ extension: ext
1799
+ };
1800
+ continue;
1801
+ }
1802
+ const metaInfo = classifyMetadataRoute(entry);
1803
+ if (metaInfo) {
1804
+ if (!metaInfo.nestable && node.segmentName !== "") throw new Error(`Build error: '${name}' is a root-only metadata convention and must be in the app root directory.\n File: ${fullPath}\n Move this file to the app root (not a route group or nested segment).`);
1805
+ if (!node.metadataRoutes) node.metadataRoutes = {};
1806
+ const existing = node.metadataRoutes[name];
1807
+ if (existing) {
1808
+ const existingIsDynamic = isDynamicMetadataExtension(name, existing.extension);
1809
+ const newIsDynamic = isDynamicMetadataExtension(name, ext);
1810
+ if (existingIsDynamic === newIsDynamic) throwDuplicateConvention(name, existing.filePath, fullPath);
1811
+ if (newIsDynamic) node.metadataRoutes[name] = {
1812
+ filePath: fullPath,
1813
+ extension: ext
1814
+ };
1815
+ } else node.metadataRoutes[name] = {
1816
+ filePath: fullPath,
1817
+ extension: ext
1818
+ };
1819
+ }
1820
+ }
1821
+ if (node.route && node.page) throw new Error(`Build error: route.ts and page.* cannot coexist in the same segment.\n route.ts: ${node.route.filePath}\n page: ${node.page.filePath}\nA URL is either an API endpoint or a rendered page, not both.`);
1822
+ if (node.route && getCsrfExport(node.route.filePath) === "exempt") node.csrfExempt = true;
1823
+ }
1824
+ /**
1825
+ * Throw a build error for two files claiming the same convention slot in
1826
+ * one segment (e.g. page.tsx + page.jsx). Which file readdir returns first
1827
+ * is platform-dependent (APFS sorts entries, ext4 does not) — silently
1828
+ * picking one would let macOS dev and Linux CI serve different
1829
+ * implementations of the same route.
1830
+ */
1831
+ function throwDuplicateConvention(convention, firstPath, secondPath) {
1832
+ throw new Error(`Build error: duplicate '${convention}' files in the same route segment.\n File 1: ${firstPath}\n File 2: ${secondPath}\nEach segment may define '${convention}' at most once. Remove one of the files.`);
1833
+ }
1834
+ /**
1835
+ * Recursively scan child directories and build the segment tree.
1836
+ */
1837
+ function scanChildren(dirPath, parentNode, extSet) {
1838
+ let entries;
1839
+ try {
1840
+ entries = readdirSync(dirPath);
1841
+ } catch (err) {
1842
+ swallow(err, `scanChildren: unreadable directory ${dirPath}`, { level: "warn" });
1843
+ return;
1844
+ }
1845
+ for (const entry of entries) {
1846
+ const fullPath = join(dirPath, entry);
1847
+ try {
1848
+ if (!statSync(fullPath).isDirectory()) continue;
1849
+ } catch {
1850
+ continue;
1851
+ }
1852
+ if (ENCODED_SEPARATOR_RE.test(entry)) throw new Error(`Build error: directory name contains an encoded path delimiter (%2F or %5C).\n Directory: ${fullPath}\nEncoded separators in directory names cause route collisions when decoded. Rename the directory to remove the encoded delimiter.`);
1853
+ if (NULL_BYTE_RE.test(entry)) throw new Error(`Build error: directory name contains an encoded null byte (%00).\n Directory: ${fullPath}\nEncoded null bytes in directory names are not allowed. Rename the directory to remove the null byte encoding.`);
1854
+ const { type, paramName, paramPrefix, paramSuffix, interceptionMarker, interceptedSegmentName } = classifySegment(entry);
1855
+ if (type === "private") continue;
1856
+ const childNode = createSegmentNode(entry, type, computeUrlPath(parentNode.urlPath, entry, type), paramName, interceptionMarker, interceptedSegmentName, paramPrefix, paramSuffix);
1857
+ scanSegmentFiles(fullPath, childNode, extSet);
1858
+ scanChildren(fullPath, childNode, extSet);
1859
+ if (type === "slot") {
1860
+ const slotName = entry.slice(1);
1861
+ parentNode.slots[slotName] = childNode;
1862
+ } else parentNode.children.push(childNode);
1863
+ }
1864
+ }
1865
+ /**
1866
+ * Validate that no route chain contains duplicate dynamic param names.
1867
+ *
1868
+ * Example violation:
1869
+ * app/[id]/items/[id]/page.tsx — 'id' appears twice in the ancestor chain.
1870
+ *
1871
+ * Route groups are transparent — params accumulate through them.
1872
+ * Slots are independent — duplicate detection does NOT cross slot boundaries.
1873
+ *
1874
+ * See design/07-routing.md §"Duplicate Param Name Detection"
1875
+ */
1876
+ function validateDuplicateParamNames(root) {
1877
+ walkForDuplicateParams(root, /* @__PURE__ */ new Map());
1878
+ }
1879
+ /**
1880
+ * Recursively walk the segment tree, tracking seen param names → segment paths.
1881
+ * Throws on the first duplicate found.
1882
+ */
1883
+ function walkForDuplicateParams(node, seen) {
1884
+ const { paramName } = effectiveUrlSegment(node);
1885
+ if (paramName) {
1886
+ const existing = seen.get(paramName);
1887
+ if (existing) throw new Error(`[timber] Duplicate param name '${paramName}' in route chain.\n First defined at: ${existing}\n Duplicate at: ${node.urlPath || "/"}\n Rename one of the segments to avoid ambiguity.`);
1888
+ seen = new Map(seen);
1889
+ seen.set(paramName, node.urlPath || "/");
1890
+ }
1891
+ for (const child of node.children) walkForDuplicateParams(child, seen);
1892
+ for (const slotNode of Object.values(node.slots)) walkForDuplicateParams(slotNode, new Map(seen));
1893
+ }
1894
+ /**
1895
+ * Validate that each param name uses a single bracket form across the entire route tree.
1896
+ *
1897
+ * Different bracket forms produce different types:
1898
+ * [id] → string
1899
+ * [...id] → string[]
1900
+ * [[...id]] → string[] | undefined
1901
+ *
1902
+ * If the same param name appears with multiple forms, the no-argument
1903
+ * getSegmentParams() / useSegmentParams() return type becomes ambiguous.
1904
+ *
1905
+ * Same name + same form across different routes is fine and expected.
1906
+ */
1907
+ function validateUniqueBracketForms(root) {
1908
+ collectBracketForms(root, /* @__PURE__ */ new Map());
1909
+ }
1910
+ function collectBracketForms(node, forms) {
1911
+ const { paramName, segmentType } = effectiveUrlSegment(node);
1912
+ if (paramName && segmentType !== "static" && segmentType !== "group") {
1913
+ const existing = forms.get(paramName);
1914
+ if (existing && existing.segmentType !== segmentType) {
1915
+ const bracketExamples = {
1916
+ "dynamic": `[${paramName}]`,
1917
+ "catch-all": `[...${paramName}]`,
1918
+ "optional-catch-all": `[[...${paramName}]]`
1919
+ };
1920
+ throw new Error(`[timber] Param name '${paramName}' uses multiple bracket forms.\n ${bracketExamples[existing.segmentType] ?? existing.segmentType} at: ${existing.urlPath}\n ${bracketExamples[segmentType] ?? segmentType} at: ${node.urlPath || "/"}\n Each param name must use a single bracket form across all routes.\n Rename one of the segments to fix this (e.g., [${paramName}] → [${paramName}Id]).`);
1921
+ }
1922
+ if (!existing) forms.set(paramName, {
1923
+ segmentType,
1924
+ urlPath: node.urlPath || "/"
1925
+ });
1926
+ }
1927
+ for (const child of node.children) collectBracketForms(child, forms);
1928
+ for (const slotNode of Object.values(node.slots)) collectBracketForms(slotNode, forms);
1929
+ }
1930
+ /**
1931
+ * Validate that no sibling affixed dynamic segments have equal specificity.
1932
+ *
1933
+ * Among affixed siblings, longer combined prefix+suffix length wins. When two
1934
+ * siblings tie (same total affix length), the router would fall back to
1935
+ * filesystem scan order — which is platform-dependent. This is a build error.
1936
+ *
1937
+ * Route groups are URL-transparent, so affixed segments inside different groups
1938
+ * at the same level still conflict with each other.
1939
+ *
1940
+ * Ambiguity requires the two segments to be candidates for the *same* URL
1941
+ * part, which is narrower than "same parent" once interception is involved:
1942
+ *
1943
+ * - Plain children and intercepting children never compete. They are resolved
1944
+ * by different walks — `findSlotMatch` matches the slot subtree (where
1945
+ * `tree-match.ts` ignores intercepting nodes outright) and
1946
+ * `findInterceptingMatch` builds a synthetic parent holding only
1947
+ * interceptors.
1948
+ * - Two interceptors compete only at the same anchor. `(.)a-[id]` and
1949
+ * `(..)[slug]-b` in one slot are based at different URL depths, so no
1950
+ * pathname reaches both, and `anchorIndex` rejects the one whose base does
1951
+ * not match the target. Grouping them together would fail the build for a
1952
+ * pair that can never be ambiguous.
1953
+ *
1954
+ * So candidates are keyed by the position they contend for, then by affix
1955
+ * length. See design/07-routing.md §"Priority" and §"Intercepting Routes".
1956
+ */
1957
+ function validateAffixedSiblingConflicts(node) {
1958
+ const affixed = collectEffectiveAffixed(node);
1959
+ if (affixed.length >= 2) {
1960
+ const byLength = /* @__PURE__ */ new Map();
1961
+ for (const child of affixed) {
1962
+ const key = `${contentionKey(node, child)}\0${affixLengthOf(child)}`;
1963
+ const group = byLength.get(key);
1964
+ if (group) group.push(child);
1965
+ else byLength.set(key, [child]);
1966
+ }
1967
+ for (const [, group] of byLength) if (group.length >= 2) throw new Error(`Build error: ambiguous affixed dynamic siblings with equal specificity.\n Parent: ${node.urlPath || "/"}\n Segments: ${group.map((s) => s.segmentName).join(", ")}\n Combined prefix+suffix length: ${affixLengthOf(group[0])}\nThe router cannot deterministically choose between these — filesystem scan order is platform-dependent.\nRename one segment to give it a longer or shorter combined affix.`);
1968
+ }
1969
+ for (const child of node.children) validateAffixedSiblingConflicts(child);
1970
+ for (const slotNode of Object.values(node.slots)) validateAffixedSiblingConflicts(slotNode);
1971
+ }
1972
+ /**
1973
+ * The URL position a child contends for, as a comparable key.
1974
+ *
1975
+ * Plain children contend for the parent's next part. An intercepting child
1976
+ * contends for a part determined by its marker's intercepted base, so two
1977
+ * markers of different depth are simply different positions — and two markers
1978
+ * that compute the *same* base do contend, which is why the key is the base
1979
+ * rather than the marker (`(..)` and `(...)` under `/feed` both base at `/`).
1980
+ *
1981
+ * `groupByInterceptedAnchor` (`routing/interception.ts`) is the canonical
1982
+ * statement of that grouping — it is what the resolver walks and what the
1983
+ * collision validator checks. This computes the same key from the same
1984
+ * `computeInterceptedBase` rather than calling it, because affix ambiguity is
1985
+ * decided per *child* and `collectEffectiveAffixed` looks through route groups,
1986
+ * so the two do not partition the same set of nodes.
1987
+ */
1988
+ function contentionKey(parent, child) {
1989
+ if (child.segmentType !== "intercepting") return "child";
1990
+ return `intercept:${computeInterceptedBase(parent.urlPath, child.interceptionMarker ?? "(.)")}`;
1991
+ }
1992
+ /** Combined affix length of a node, read through its effective identity. */
1993
+ function affixLengthOf(node) {
1994
+ const { paramPrefix, paramSuffix } = effectiveUrlSegment(node);
1995
+ return (paramPrefix?.length ?? 0) + (paramSuffix?.length ?? 0);
1996
+ }
1997
+ /**
1998
+ * Collect affixed dynamic segments that are effective children of a node.
1999
+ * Route groups are URL-transparent, so we look through them recursively.
2000
+ *
2001
+ * Segments are read through `effectiveUrlSegment`, so an intercepting
2002
+ * directory counts as the affixed segment it intercepts: `(.)a-[id]` is
2003
+ * `a-[id]`. The proxy the slot resolver builds hands those affixes to
2004
+ * `matchUrlParts`, whose equal-length sort is not stable across equal
2005
+ * specificity — so two intercepting siblings with the same combined affix
2006
+ * length would resolve by filesystem enumeration order, which is precisely
2007
+ * what this validator exists to prevent for ordinary siblings (TIM-1288).
2008
+ */
2009
+ function collectEffectiveAffixed(node) {
2010
+ const result = [];
2011
+ for (const child of node.children) {
2012
+ const { segmentType, paramPrefix, paramSuffix } = effectiveUrlSegment(child);
2013
+ if (segmentType === "dynamic" && (paramPrefix || paramSuffix)) result.push(child);
2014
+ else if (segmentType === "group") result.push(...collectEffectiveAffixed(child));
2015
+ }
2016
+ return result;
2017
+ }
2018
+ /**
2019
+ * Find a fixed-extension file (proxy.ts) in a directory.
2020
+ */
2021
+ function findFixedFile(dirPath, name) {
2022
+ for (const ext of ["ts", "tsx"]) {
2023
+ const fullPath = join(dirPath, `${name}.${ext}`);
2024
+ try {
2025
+ if (statSync(fullPath).isFile()) return {
2026
+ filePath: fullPath,
2027
+ extension: ext
2028
+ };
2029
+ } catch {}
2030
+ }
2031
+ }
2032
+ /**
2033
+ * Find a file with script extensions (tsx, ts, jsx, js) in a directory.
2034
+ * Used for app-root conventions like global-error that only accept script
2035
+ * extensions (TIM-1505).
2036
+ */
2037
+ function findScriptExtFile(dirPath, name) {
2038
+ for (const ext of [
2039
+ "tsx",
2040
+ "ts",
2041
+ "jsx",
2042
+ "js"
2043
+ ]) {
2044
+ const fullPath = join(dirPath, `${name}.${ext}`);
2045
+ try {
2046
+ if (statSync(fullPath).isFile()) return {
2047
+ filePath: fullPath,
2048
+ extension: ext
2049
+ };
2050
+ } catch {}
2051
+ }
2052
+ }
2053
+ //#endregion
2054
+ export { fileHasDefaultExport as a, getCsrfExport as c, collectInterceptionRewrites as d, fileHasAnyExport as i, getPrerenderExport as l, scanRoutes as n, fileHasDirective as o, fileDefaultExportIsLocalValue as r, fileHasStarExport as s, isRouteConventionFile as t, readFileCached as u };
2055
+
2056
+ //# sourceMappingURL=scanner-DmqdxzbW.js.map