@timber-js/app 0.2.0-alpha.185 → 0.2.0-alpha.187

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 (570) hide show
  1. package/dist/_chunks/{actions-O_LsyCE4.js → actions-35jnMdeJ.js} +4 -3
  2. package/dist/_chunks/{actions-O_LsyCE4.js.map → actions-35jnMdeJ.js.map} +1 -1
  3. package/dist/_chunks/als-registry-C6kcfprT.js +41 -0
  4. package/dist/_chunks/als-registry-C6kcfprT.js.map +1 -0
  5. package/dist/_chunks/als-slots-BEEIPKYm.js +26 -0
  6. package/dist/_chunks/als-slots-BEEIPKYm.js.map +1 -0
  7. package/dist/_chunks/base-path-DaQrzbez.js +80 -0
  8. package/dist/_chunks/base-path-DaQrzbez.js.map +1 -0
  9. package/dist/_chunks/build-manifest-DWppEdLB.js +86 -0
  10. package/dist/_chunks/build-manifest-DWppEdLB.js.map +1 -0
  11. package/dist/_chunks/build-output-helper-C3DrfzZR.js +369 -0
  12. package/dist/_chunks/build-output-helper-C3DrfzZR.js.map +1 -0
  13. package/dist/_chunks/{cache-api-B-lhk9p4.js → cache-api-DjNrIWRR.js} +8 -14
  14. package/dist/_chunks/cache-api-DjNrIWRR.js.map +1 -0
  15. package/dist/_chunks/cli-check-CpmN7Nh-.js +256 -0
  16. package/dist/_chunks/cli-check-CpmN7Nh-.js.map +1 -0
  17. package/dist/_chunks/cli-schema-sync-CGMp_Psg.js +298 -0
  18. package/dist/_chunks/cli-schema-sync-CGMp_Psg.js.map +1 -0
  19. package/dist/_chunks/{cloudflare-CnT5Lr7U.js → cloudflare-CGP6BZKO.js} +92 -14
  20. package/dist/_chunks/{cloudflare-CnT5Lr7U.js.map → cloudflare-CGP6BZKO.js.map} +1 -1
  21. package/dist/_chunks/convention-lint-kXsgc_-7.js +784 -0
  22. package/dist/_chunks/convention-lint-kXsgc_-7.js.map +1 -0
  23. package/dist/_chunks/error-boundary-D-ODYX41.js +353 -0
  24. package/dist/_chunks/error-boundary-D-ODYX41.js.map +1 -0
  25. package/dist/_chunks/{fast-hash-D6hIVt1Y.js → fast-hash-C-tVYrLb.js} +6 -1
  26. package/dist/_chunks/fast-hash-C-tVYrLb.js.map +1 -0
  27. package/dist/_chunks/file-cache-DmX7OqZP.js +454 -0
  28. package/dist/_chunks/file-cache-DmX7OqZP.js.map +1 -0
  29. package/dist/_chunks/fs-identity-D7vb6GdI.js +208 -0
  30. package/dist/_chunks/fs-identity-D7vb6GdI.js.map +1 -0
  31. package/dist/_chunks/{logger-AWfuX-KJ.js → logger-D8xJZXIN.js} +34 -51
  32. package/dist/_chunks/logger-D8xJZXIN.js.map +1 -0
  33. package/dist/_chunks/mdx-file-C005ay-P.js +25 -0
  34. package/dist/_chunks/mdx-file-C005ay-P.js.map +1 -0
  35. package/dist/_chunks/{plugin-context---kTF5v8.js → plugin-context-rCinWLiE.js} +8 -2
  36. package/dist/_chunks/plugin-context-rCinWLiE.js.map +1 -0
  37. package/dist/_chunks/purge-store-Byr8XOjU.js +14 -0
  38. package/dist/_chunks/purge-store-Byr8XOjU.js.map +1 -0
  39. package/dist/_chunks/{resolve-schema-Dz3fcFUo.js → resolve-schema-5ma5pp1b.js} +2 -2
  40. package/dist/_chunks/{resolve-schema-Dz3fcFUo.js.map → resolve-schema-5ma5pp1b.js.map} +1 -1
  41. package/dist/_chunks/{router-ref-CtmF-aPv.js → router-ref-DuYuV_0Q.js} +2 -2
  42. package/dist/_chunks/router-ref-DuYuV_0Q.js.map +1 -0
  43. package/dist/_chunks/rsc-cache-key-DD0fl_-s.js +243 -0
  44. package/dist/_chunks/rsc-cache-key-DD0fl_-s.js.map +1 -0
  45. package/dist/_chunks/rsc-error-envelope-tT5PJs4q.js +40 -0
  46. package/dist/_chunks/rsc-error-envelope-tT5PJs4q.js.map +1 -0
  47. package/dist/_chunks/rsc-media-type-DRqE_lD_.js +46 -0
  48. package/dist/_chunks/rsc-media-type-DRqE_lD_.js.map +1 -0
  49. package/dist/_chunks/rsc-payload-path-B_LBodc2.js +48 -0
  50. package/dist/_chunks/rsc-payload-path-B_LBodc2.js.map +1 -0
  51. package/dist/_chunks/scanner-C8b0Gcw3.js +1564 -0
  52. package/dist/_chunks/scanner-C8b0Gcw3.js.map +1 -0
  53. package/dist/_chunks/schema-bridge-Cc2Gngu1.js +199 -0
  54. package/dist/_chunks/schema-bridge-Cc2Gngu1.js.map +1 -0
  55. package/dist/_chunks/{segment-classify-Byy425ng.js → segment-classify-C539Pa2O.js} +23 -2
  56. package/dist/_chunks/segment-classify-C539Pa2O.js.map +1 -0
  57. package/dist/_chunks/segment-context-CjOlyB8Y.js +97 -0
  58. package/dist/_chunks/segment-context-CjOlyB8Y.js.map +1 -0
  59. package/dist/_chunks/{canonicalize-DQHyFClh.js → segment-keys-BawYuNFO.js} +61 -2
  60. package/dist/_chunks/segment-keys-BawYuNFO.js.map +1 -0
  61. package/dist/_chunks/slot-params-BCTmZkQB.js +76 -0
  62. package/dist/_chunks/slot-params-BCTmZkQB.js.map +1 -0
  63. package/dist/_chunks/{ssr-data-BOWsq18U.js → ssr-data-14MXm7Pj.js} +17 -2
  64. package/dist/_chunks/ssr-data-14MXm7Pj.js.map +1 -0
  65. package/dist/_chunks/{use-query-states-DFvWd-EA.js → use-query-states-BbU5Ge1V.js} +74 -26
  66. package/dist/_chunks/use-query-states-BbU5Ge1V.js.map +1 -0
  67. package/dist/_chunks/use-segment-params-C4r4BD9T.js +398 -0
  68. package/dist/_chunks/use-segment-params-C4r4BD9T.js.map +1 -0
  69. package/dist/_chunks/walkers-RzN6AFjr.js +141 -0
  70. package/dist/_chunks/walkers-RzN6AFjr.js.map +1 -0
  71. package/dist/adapters/build-output-helper.d.ts +37 -0
  72. package/dist/adapters/build-output-helper.d.ts.map +1 -1
  73. package/dist/adapters/cloudflare-dev.js +1 -1
  74. package/dist/adapters/cloudflare-kv-cache.js +1 -1
  75. package/dist/adapters/cloudflare.d.ts.map +1 -1
  76. package/dist/adapters/cloudflare.js +1 -1
  77. package/dist/adapters/compress-module.d.ts +12 -0
  78. package/dist/adapters/compress-module.d.ts.map +1 -1
  79. package/dist/adapters/fs-identity.d.ts +64 -0
  80. package/dist/adapters/fs-identity.d.ts.map +1 -0
  81. package/dist/adapters/nitro.d.ts.map +1 -1
  82. package/dist/adapters/nitro.js +56 -5
  83. package/dist/adapters/nitro.js.map +1 -1
  84. package/dist/adapters/shared.d.ts +57 -1
  85. package/dist/adapters/shared.d.ts.map +1 -1
  86. package/dist/adapters/types.d.ts +13 -1
  87. package/dist/adapters/types.d.ts.map +1 -1
  88. package/dist/cache/fast-hash.d.ts +5 -0
  89. package/dist/cache/fast-hash.d.ts.map +1 -1
  90. package/dist/cache/index.js +1 -1
  91. package/dist/cdn/cloudflare-purge.js +30 -0
  92. package/dist/cdn/cloudflare-purge.js.map +1 -0
  93. package/dist/cdn/fastly-purge.js +33 -0
  94. package/dist/cdn/fastly-purge.js.map +1 -0
  95. package/dist/cdn/index.js +94 -0
  96. package/dist/cdn/index.js.map +1 -0
  97. package/dist/cdn/workers-cache-purge.js +35 -0
  98. package/dist/cdn/workers-cache-purge.js.map +1 -0
  99. package/dist/cli-check.d.ts +153 -0
  100. package/dist/cli-check.d.ts.map +1 -0
  101. package/dist/cli.d.ts +34 -8
  102. package/dist/cli.d.ts.map +1 -1
  103. package/dist/cli.js +46 -21
  104. package/dist/cli.js.map +1 -1
  105. package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -1
  106. package/dist/client/browser-entry/hydrate.d.ts +20 -9
  107. package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
  108. package/dist/client/browser-entry/index.d.ts +1 -1
  109. package/dist/client/browser-entry/index.d.ts.map +1 -1
  110. package/dist/client/browser-entry/post-hydration.d.ts +4 -1
  111. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
  112. package/dist/client/browser-entry/router-init.d.ts +3 -0
  113. package/dist/client/browser-entry/router-init.d.ts.map +1 -1
  114. package/dist/client/browser-entry/rsc-stream.d.ts +8 -0
  115. package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -1
  116. package/dist/client/error-boundary.d.ts +7 -0
  117. package/dist/client/error-boundary.d.ts.map +1 -1
  118. package/dist/client/error-boundary.js +1 -162
  119. package/dist/client/history.d.ts +13 -6
  120. package/dist/client/history.d.ts.map +1 -1
  121. package/dist/client/index.d.ts +1 -0
  122. package/dist/client/index.d.ts.map +1 -1
  123. package/dist/client/index.js +42 -41
  124. package/dist/client/index.js.map +1 -1
  125. package/dist/client/internal.d.ts +4 -3
  126. package/dist/client/internal.d.ts.map +1 -1
  127. package/dist/client/internal.js +877 -311
  128. package/dist/client/internal.js.map +1 -1
  129. package/dist/client/link.d.ts +1 -7
  130. package/dist/client/link.d.ts.map +1 -1
  131. package/dist/client/navigation-api.d.ts +2 -2
  132. package/dist/client/navigation-commit.d.ts +87 -0
  133. package/dist/client/navigation-commit.d.ts.map +1 -0
  134. package/dist/client/navigation-context.d.ts +37 -20
  135. package/dist/client/navigation-context.d.ts.map +1 -1
  136. package/dist/client/navigation-root.d.ts +63 -23
  137. package/dist/client/navigation-root.d.ts.map +1 -1
  138. package/dist/client/params-context.d.ts +81 -0
  139. package/dist/client/params-context.d.ts.map +1 -0
  140. package/dist/client/router-effects.d.ts +129 -0
  141. package/dist/client/router-effects.d.ts.map +1 -0
  142. package/dist/client/router-lifecycle.d.ts +55 -0
  143. package/dist/client/router-lifecycle.d.ts.map +1 -0
  144. package/dist/client/router-pipeline.d.ts +84 -0
  145. package/dist/client/router-pipeline.d.ts.map +1 -0
  146. package/dist/client/router-ref.d.ts +1 -1
  147. package/dist/client/router-ref.d.ts.map +1 -1
  148. package/dist/client/router-skew.d.ts +21 -0
  149. package/dist/client/router-skew.d.ts.map +1 -0
  150. package/dist/client/router-types.d.ts +207 -0
  151. package/dist/client/router-types.d.ts.map +1 -0
  152. package/dist/client/router.d.ts +1 -182
  153. package/dist/client/router.d.ts.map +1 -1
  154. package/dist/client/rsc-fetch.d.ts +47 -26
  155. package/dist/client/rsc-fetch.d.ts.map +1 -1
  156. package/dist/client/segment-cache.d.ts +87 -34
  157. package/dist/client/segment-cache.d.ts.map +1 -1
  158. package/dist/client/ssr-data.d.ts +6 -0
  159. package/dist/client/ssr-data.d.ts.map +1 -1
  160. package/dist/client/stale-client.d.ts +69 -0
  161. package/dist/client/stale-client.d.ts.map +1 -0
  162. package/dist/client/state.d.ts +10 -1
  163. package/dist/client/state.d.ts.map +1 -1
  164. package/dist/client/top-loader.d.ts +2 -1
  165. package/dist/client/top-loader.d.ts.map +1 -1
  166. package/dist/client/unload-guard.d.ts +5 -0
  167. package/dist/client/unload-guard.d.ts.map +1 -1
  168. package/dist/client/use-cookie.d.ts.map +1 -1
  169. package/dist/client/use-query-states.d.ts +9 -3
  170. package/dist/client/use-query-states.d.ts.map +1 -1
  171. package/dist/client/use-router.d.ts +1 -1
  172. package/dist/client/use-segment-params.d.ts +18 -8
  173. package/dist/client/use-segment-params.d.ts.map +1 -1
  174. package/dist/codec.js +1 -1
  175. package/dist/cookies/define-cookie.d.ts.map +1 -1
  176. package/dist/cookies/index.js +5 -5
  177. package/dist/cookies/index.js.map +1 -1
  178. package/dist/index.d.ts +4 -1
  179. package/dist/index.d.ts.map +1 -1
  180. package/dist/index.js +1009 -759
  181. package/dist/index.js.map +1 -1
  182. package/dist/params/index.js +1 -1
  183. package/dist/plugin-context.d.ts +46 -0
  184. package/dist/plugin-context.d.ts.map +1 -1
  185. package/dist/plugins/adapter-build.d.ts.map +1 -1
  186. package/dist/plugins/build-manifest.d.ts +1 -1
  187. package/dist/plugins/build-manifest.d.ts.map +1 -1
  188. package/dist/plugins/entries.d.ts.map +1 -1
  189. package/dist/plugins/fonts.d.ts.map +1 -1
  190. package/dist/plugins/request-dep/analysis.d.ts +106 -0
  191. package/dist/plugins/request-dep/analysis.d.ts.map +1 -0
  192. package/dist/plugins/request-dep/index.d.ts +75 -0
  193. package/dist/plugins/request-dep/index.d.ts.map +1 -0
  194. package/dist/plugins/request-dep/origin.d.ts +61 -0
  195. package/dist/plugins/request-dep/origin.d.ts.map +1 -0
  196. package/dist/plugins/routing.d.ts.map +1 -1
  197. package/dist/plugins/shims.d.ts.map +1 -1
  198. package/dist/plugins/static-build.d.ts +160 -8
  199. package/dist/plugins/static-build.d.ts.map +1 -1
  200. package/dist/routing/codegen-shared.d.ts +3 -44
  201. package/dist/routing/codegen-shared.d.ts.map +1 -1
  202. package/dist/routing/codegen-types.d.ts +28 -31
  203. package/dist/routing/codegen-types.d.ts.map +1 -1
  204. package/dist/routing/codegen-write.d.ts +51 -0
  205. package/dist/routing/codegen-write.d.ts.map +1 -0
  206. package/dist/routing/codegen.d.ts.map +1 -1
  207. package/dist/routing/collision-probe.d.ts +104 -0
  208. package/dist/routing/collision-probe.d.ts.map +1 -0
  209. package/dist/routing/collision-spaces.d.ts +26 -0
  210. package/dist/routing/collision-spaces.d.ts.map +1 -0
  211. package/dist/routing/convention-lint.d.ts +18 -4
  212. package/dist/routing/convention-lint.d.ts.map +1 -1
  213. package/dist/routing/export-detect.d.ts +16 -0
  214. package/dist/routing/export-detect.d.ts.map +1 -1
  215. package/dist/routing/index.js +4 -3
  216. package/dist/routing/interception.d.ts +235 -6
  217. package/dist/routing/interception.d.ts.map +1 -1
  218. package/dist/routing/link-codegen.d.ts +19 -4
  219. package/dist/routing/link-codegen.d.ts.map +1 -1
  220. package/dist/routing/manifest-codegen.d.ts +1 -7
  221. package/dist/routing/manifest-codegen.d.ts.map +1 -1
  222. package/dist/routing/scanner.d.ts.map +1 -1
  223. package/dist/routing/segment-classify.d.ts +33 -0
  224. package/dist/routing/segment-classify.d.ts.map +1 -1
  225. package/dist/routing/segment-keys.d.ts +95 -0
  226. package/dist/routing/segment-keys.d.ts.map +1 -0
  227. package/dist/routing/slot-placement.d.ts +48 -0
  228. package/dist/routing/slot-placement.d.ts.map +1 -0
  229. package/dist/routing/types.d.ts +0 -6
  230. package/dist/routing/types.d.ts.map +1 -1
  231. package/dist/routing/walkers.d.ts +6 -0
  232. package/dist/routing/walkers.d.ts.map +1 -1
  233. package/dist/schema-bridge.d.ts +60 -9
  234. package/dist/schema-bridge.d.ts.map +1 -1
  235. package/dist/search-params/define.d.ts +62 -14
  236. package/dist/search-params/define.d.ts.map +1 -1
  237. package/dist/search-params/index.d.ts +0 -1
  238. package/dist/search-params/index.d.ts.map +1 -1
  239. package/dist/search-params/index.js +221 -10
  240. package/dist/search-params/index.js.map +1 -1
  241. package/dist/search-params/parse-total.d.ts +70 -0
  242. package/dist/search-params/parse-total.d.ts.map +1 -0
  243. package/dist/search-params/wrappers.d.ts +26 -3
  244. package/dist/search-params/wrappers.d.ts.map +1 -1
  245. package/dist/server/access-gate.d.ts +19 -8
  246. package/dist/server/access-gate.d.ts.map +1 -1
  247. package/dist/server/action-handler.d.ts.map +1 -1
  248. package/dist/server/actions.d.ts +6 -3
  249. package/dist/server/actions.d.ts.map +1 -1
  250. package/dist/server/als-registry.d.ts +18 -1
  251. package/dist/server/als-registry.d.ts.map +1 -1
  252. package/dist/server/build-manifest.d.ts +13 -1
  253. package/dist/server/build-manifest.d.ts.map +1 -1
  254. package/dist/server/chain-url-parts.d.ts +82 -0
  255. package/dist/server/chain-url-parts.d.ts.map +1 -0
  256. package/dist/server/children-interception.d.ts +49 -0
  257. package/dist/server/children-interception.d.ts.map +1 -0
  258. package/dist/server/compress.d.ts.map +1 -1
  259. package/dist/server/default-status-page.d.ts +45 -0
  260. package/dist/server/default-status-page.d.ts.map +1 -0
  261. package/dist/server/deny-boundary.d.ts +148 -15
  262. package/dist/server/deny-boundary.d.ts.map +1 -1
  263. package/dist/server/deny-renderer.d.ts +15 -4
  264. package/dist/server/deny-renderer.d.ts.map +1 -1
  265. package/dist/server/error-boundary-wrapper.d.ts +85 -15
  266. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  267. package/dist/server/head-response.d.ts +35 -0
  268. package/dist/server/head-response.d.ts.map +1 -0
  269. package/dist/server/index.d.ts +0 -1
  270. package/dist/server/index.d.ts.map +1 -1
  271. package/dist/server/index.js +3 -2
  272. package/dist/server/index.js.map +1 -1
  273. package/dist/server/internal.d.ts +3 -1
  274. package/dist/server/internal.d.ts.map +1 -1
  275. package/dist/server/internal.js +712 -418
  276. package/dist/server/internal.js.map +1 -1
  277. package/dist/server/metadata-collector.d.ts +52 -0
  278. package/dist/server/metadata-collector.d.ts.map +1 -0
  279. package/dist/server/param-coercion.d.ts +22 -6
  280. package/dist/server/param-coercion.d.ts.map +1 -1
  281. package/dist/server/pipeline-helpers.d.ts +0 -18
  282. package/dist/server/pipeline-helpers.d.ts.map +1 -1
  283. package/dist/server/pipeline-interception.d.ts +59 -9
  284. package/dist/server/pipeline-interception.d.ts.map +1 -1
  285. package/dist/server/pipeline-outcome.d.ts.map +1 -1
  286. package/dist/server/pipeline-phases.d.ts.map +1 -1
  287. package/dist/server/pipeline.d.ts +11 -0
  288. package/dist/server/pipeline.d.ts.map +1 -1
  289. package/dist/server/primitives.d.ts +23 -0
  290. package/dist/server/primitives.d.ts.map +1 -1
  291. package/dist/server/publish-params.d.ts +33 -0
  292. package/dist/server/publish-params.d.ts.map +1 -0
  293. package/dist/server/request-context.d.ts +48 -0
  294. package/dist/server/request-context.d.ts.map +1 -1
  295. package/dist/server/route-element-builder.d.ts +22 -3
  296. package/dist/server/route-element-builder.d.ts.map +1 -1
  297. package/dist/server/route-handler.d.ts.map +1 -1
  298. package/dist/server/route-matcher.d.ts +13 -0
  299. package/dist/server/route-matcher.d.ts.map +1 -1
  300. package/dist/server/rsc-cache-key-guard.d.ts +78 -0
  301. package/dist/server/rsc-cache-key-guard.d.ts.map +1 -0
  302. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
  303. package/dist/server/rsc-entry/deny-fallback.d.ts.map +1 -1
  304. package/dist/server/rsc-entry/error-renderer.d.ts +15 -3
  305. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  306. package/dist/server/rsc-entry/helpers.d.ts +2 -17
  307. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  308. package/dist/server/rsc-entry/index.d.ts +0 -1
  309. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  310. package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
  311. package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -1
  312. package/dist/server/rsc-entry/rsc-payload.d.ts +1 -2
  313. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  314. package/dist/server/rsc-entry/rsc-stream.d.ts +15 -2
  315. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  316. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  317. package/dist/server/skippable-prefix.d.ts +23 -0
  318. package/dist/server/skippable-prefix.d.ts.map +1 -0
  319. package/dist/server/slot-interception.d.ts +80 -0
  320. package/dist/server/slot-interception.d.ts.map +1 -0
  321. package/dist/server/slot-resolver.d.ts +70 -8
  322. package/dist/server/slot-resolver.d.ts.map +1 -1
  323. package/dist/server/ssr-bridge-types.d.ts +17 -0
  324. package/dist/server/ssr-bridge-types.d.ts.map +1 -1
  325. package/dist/server/ssr-entry.d.ts +0 -1
  326. package/dist/server/ssr-entry.d.ts.map +1 -1
  327. package/dist/server/ssr-wrappers.d.ts +24 -13
  328. package/dist/server/ssr-wrappers.d.ts.map +1 -1
  329. package/dist/server/state-tree-diff.d.ts +6 -27
  330. package/dist/server/state-tree-diff.d.ts.map +1 -1
  331. package/dist/server/static-generator.d.ts +6 -0
  332. package/dist/server/static-generator.d.ts.map +1 -1
  333. package/dist/server/static-not-found.d.ts +52 -0
  334. package/dist/server/static-not-found.d.ts.map +1 -0
  335. package/dist/server/status-code-resolver.d.ts +8 -1
  336. package/dist/server/status-code-resolver.d.ts.map +1 -1
  337. package/dist/server/tree-builder.d.ts +28 -36
  338. package/dist/server/tree-builder.d.ts.map +1 -1
  339. package/dist/server/types.d.ts +12 -7
  340. package/dist/server/types.d.ts.map +1 -1
  341. package/dist/server/utils/element-type.d.ts +40 -0
  342. package/dist/server/utils/element-type.d.ts.map +1 -0
  343. package/dist/shared/als-slots.d.ts +41 -0
  344. package/dist/shared/als-slots.d.ts.map +1 -0
  345. package/dist/shared/base-path.d.ts +56 -0
  346. package/dist/shared/base-path.d.ts.map +1 -0
  347. package/dist/shared/param-value.d.ts +80 -0
  348. package/dist/shared/param-value.d.ts.map +1 -0
  349. package/dist/shared/payload-root.d.ts +108 -0
  350. package/dist/shared/payload-root.d.ts.map +1 -0
  351. package/dist/shared/rsc-cache-key.d.ts +163 -0
  352. package/dist/shared/rsc-cache-key.d.ts.map +1 -0
  353. package/dist/shared/rsc-error-envelope.d.ts +25 -0
  354. package/dist/shared/rsc-error-envelope.d.ts.map +1 -0
  355. package/dist/shared/rsc-manifest.d.ts +28 -0
  356. package/dist/shared/rsc-manifest.d.ts.map +1 -0
  357. package/dist/shared/rsc-media-type.d.ts +40 -0
  358. package/dist/shared/rsc-media-type.d.ts.map +1 -0
  359. package/dist/shared/rsc-payload-path.d.ts +42 -0
  360. package/dist/shared/rsc-payload-path.d.ts.map +1 -0
  361. package/dist/shared/segment-info.d.ts +59 -0
  362. package/dist/shared/segment-info.d.ts.map +1 -0
  363. package/dist/shared/slot-params.d.ts +51 -0
  364. package/dist/shared/slot-params.d.ts.map +1 -0
  365. package/dist/shared/static-platform-files.d.ts +71 -0
  366. package/dist/shared/static-platform-files.d.ts.map +1 -0
  367. package/docs/api/30-api-server.mdx +1 -1
  368. package/docs/api/31-api-client.mdx +4 -2
  369. package/docs/api/33-api-search-params.mdx +38 -16
  370. package/docs/api/35-api-typescript.mdx +3 -3
  371. package/docs/api/36-cli.mdx +34 -7
  372. package/docs/learn/00-introduction.mdx +1 -1
  373. package/docs/learn/02-pages-and-layouts.mdx +2 -2
  374. package/docs/learn/05-typed-params.mdx +8 -8
  375. package/docs/learn/07-typed-routes.mdx +12 -7
  376. package/docs/learn/11-error-handling.mdx +18 -0
  377. package/docs/learn/12-client-navigation.mdx +2 -2
  378. package/docs/learn/14-deploying.mdx +23 -0
  379. package/docs/more/01-advanced-routing.mdx +173 -3
  380. package/docs/more/03-coming-from-nextjs.mdx +2 -2
  381. package/docs/more/04-metadata-and-fonts.mdx +1 -1
  382. package/docs/more/50-ai-agent-instructions.mdx +6 -4
  383. package/package.json +9 -6
  384. package/src/adapters/build-output-helper.ts +116 -3
  385. package/src/adapters/cloudflare.ts +90 -12
  386. package/src/adapters/compress-module.ts +79 -1
  387. package/src/adapters/fs-identity.ts +156 -0
  388. package/src/adapters/nitro.ts +6 -6
  389. package/src/adapters/shared.ts +196 -21
  390. package/src/adapters/types.ts +13 -1
  391. package/src/cache/fast-hash.ts +5 -0
  392. package/src/cli-check.ts +458 -0
  393. package/src/cli.ts +59 -24
  394. package/src/client/browser-entry/action-dispatch.ts +11 -5
  395. package/src/client/browser-entry/hydrate.ts +40 -29
  396. package/src/client/browser-entry/index.ts +15 -6
  397. package/src/client/browser-entry/post-hydration.ts +10 -9
  398. package/src/client/browser-entry/router-init.ts +104 -48
  399. package/src/client/browser-entry/rsc-stream.ts +11 -2
  400. package/src/client/error-boundary.tsx +85 -16
  401. package/src/client/history.ts +13 -6
  402. package/src/client/index.ts +14 -3
  403. package/src/client/internal.ts +4 -3
  404. package/src/client/link.tsx +120 -72
  405. package/src/client/navigation-api.ts +3 -3
  406. package/src/client/navigation-commit.ts +197 -0
  407. package/src/client/navigation-context.ts +40 -67
  408. package/src/client/navigation-root.tsx +326 -101
  409. package/src/client/params-context.ts +192 -0
  410. package/src/client/router-effects.ts +231 -0
  411. package/src/client/router-lifecycle.ts +288 -0
  412. package/src/client/router-pipeline.ts +393 -0
  413. package/src/client/router-ref.ts +1 -1
  414. package/src/client/router-skew.ts +30 -0
  415. package/src/client/router-types.ts +230 -0
  416. package/src/client/router.ts +243 -707
  417. package/src/client/rsc-fetch.ts +183 -106
  418. package/src/client/segment-cache.ts +129 -44
  419. package/src/client/ssr-data.ts +6 -0
  420. package/src/client/stale-client.ts +191 -0
  421. package/src/client/state.ts +14 -1
  422. package/src/client/top-loader.tsx +2 -1
  423. package/src/client/unload-guard.ts +34 -2
  424. package/src/client/use-cookie.ts +9 -2
  425. package/src/client/use-query-states.ts +102 -39
  426. package/src/client/use-router.ts +1 -1
  427. package/src/client/use-segment-params.ts +41 -18
  428. package/src/cookies/define-cookie.ts +6 -1
  429. package/src/index.ts +25 -4
  430. package/src/plugin-context.ts +58 -0
  431. package/src/plugins/adapter-build.ts +4 -0
  432. package/src/plugins/build-manifest.ts +34 -13
  433. package/src/plugins/entries.ts +4 -0
  434. package/src/plugins/fonts.ts +11 -8
  435. package/src/plugins/request-dep/analysis.ts +447 -0
  436. package/src/plugins/request-dep/index.ts +376 -0
  437. package/src/plugins/request-dep/origin.ts +310 -0
  438. package/src/plugins/routing.ts +97 -102
  439. package/src/plugins/shims.ts +0 -1
  440. package/src/plugins/static-build.ts +391 -95
  441. package/src/routing/codegen-shared.ts +3 -79
  442. package/src/routing/codegen-types.ts +28 -31
  443. package/src/routing/codegen-write.ts +139 -0
  444. package/src/routing/codegen.ts +86 -172
  445. package/src/routing/collision-probe.ts +438 -0
  446. package/src/routing/collision-spaces.ts +281 -0
  447. package/src/routing/convention-lint.ts +139 -40
  448. package/src/routing/export-detect.ts +151 -7
  449. package/src/routing/interception.ts +896 -50
  450. package/src/routing/link-codegen.ts +32 -65
  451. package/src/routing/manifest-codegen.ts +6 -62
  452. package/src/routing/scanner.ts +117 -92
  453. package/src/routing/segment-classify.ts +57 -0
  454. package/src/routing/segment-keys.ts +146 -0
  455. package/src/routing/slot-placement.ts +97 -0
  456. package/src/routing/types.ts +0 -6
  457. package/src/routing/walkers.ts +8 -0
  458. package/src/schema-bridge.ts +180 -58
  459. package/src/search-params/define.ts +108 -66
  460. package/src/search-params/index.ts +0 -1
  461. package/src/search-params/parse-total.ts +78 -0
  462. package/src/search-params/wrappers.ts +60 -11
  463. package/src/server/access-gate.tsx +60 -40
  464. package/src/server/action-handler.ts +4 -5
  465. package/src/server/actions.ts +6 -3
  466. package/src/server/als-registry.ts +18 -1
  467. package/src/server/build-manifest.ts +14 -1
  468. package/src/server/chain-url-parts.ts +163 -0
  469. package/src/server/children-interception.ts +115 -0
  470. package/src/server/compress.ts +9 -1
  471. package/src/server/default-status-page.ts +127 -0
  472. package/src/server/deny-boundary.ts +269 -41
  473. package/src/server/deny-renderer.ts +111 -39
  474. package/src/server/error-boundary-wrapper.ts +166 -79
  475. package/src/server/head-response.ts +54 -0
  476. package/src/server/index.ts +1 -3
  477. package/src/server/internal.ts +2 -2
  478. package/src/server/metadata-collector.ts +115 -0
  479. package/src/server/param-coercion.ts +51 -69
  480. package/src/server/pipeline-helpers.ts +2 -47
  481. package/src/server/pipeline-interception.ts +81 -32
  482. package/src/server/pipeline-outcome.ts +4 -5
  483. package/src/server/pipeline-phases.ts +77 -25
  484. package/src/server/pipeline.ts +57 -10
  485. package/src/server/prebuilt-runtime.ts +4 -4
  486. package/src/server/primitives.ts +25 -0
  487. package/src/server/publish-params.ts +42 -0
  488. package/src/server/request-context.ts +97 -18
  489. package/src/server/route-element-builder.ts +241 -253
  490. package/src/server/route-handler.ts +8 -8
  491. package/src/server/route-matcher.ts +21 -9
  492. package/src/server/rsc-cache-key-guard.ts +164 -0
  493. package/src/server/rsc-entry/api-handler.ts +6 -8
  494. package/src/server/rsc-entry/deny-fallback.ts +3 -2
  495. package/src/server/rsc-entry/error-renderer.ts +94 -35
  496. package/src/server/rsc-entry/helpers.ts +33 -28
  497. package/src/server/rsc-entry/index.ts +36 -33
  498. package/src/server/rsc-entry/render-route.ts +18 -10
  499. package/src/server/rsc-entry/revalidate-renderer.ts +3 -2
  500. package/src/server/rsc-entry/rsc-payload.ts +113 -40
  501. package/src/server/rsc-entry/rsc-stream.ts +29 -5
  502. package/src/server/rsc-entry/ssr-renderer.ts +38 -22
  503. package/src/server/skippable-prefix.ts +92 -0
  504. package/src/server/slot-interception.ts +168 -0
  505. package/src/server/slot-resolver.ts +263 -158
  506. package/src/server/ssr-bridge-types.ts +18 -0
  507. package/src/server/ssr-entry.ts +20 -10
  508. package/src/server/ssr-wrappers.tsx +33 -32
  509. package/src/server/state-tree-diff.ts +14 -55
  510. package/src/server/static-generator.ts +68 -29
  511. package/src/server/static-not-found.ts +134 -0
  512. package/src/server/status-code-resolver.ts +28 -11
  513. package/src/server/tree-builder.ts +35 -218
  514. package/src/server/types.ts +12 -7
  515. package/src/server/utils/element-type.ts +72 -0
  516. package/src/shared/als-slots.ts +72 -0
  517. package/src/shared/base-path.ts +80 -0
  518. package/src/shared/param-value.ts +244 -0
  519. package/src/shared/payload-root.ts +151 -0
  520. package/src/shared/rsc-cache-key.ts +269 -0
  521. package/src/shared/rsc-error-envelope.ts +31 -0
  522. package/src/shared/rsc-manifest.ts +29 -0
  523. package/src/shared/rsc-media-type.ts +43 -0
  524. package/src/shared/rsc-payload-path.ts +45 -0
  525. package/src/shared/segment-info.ts +60 -0
  526. package/src/shared/slot-params.ts +111 -0
  527. package/src/shared/static-platform-files.ts +75 -0
  528. package/dist/_chunks/build-output-helper-DOGYFb_X.js +0 -196
  529. package/dist/_chunks/build-output-helper-DOGYFb_X.js.map +0 -1
  530. package/dist/_chunks/cache-api-B-lhk9p4.js.map +0 -1
  531. package/dist/_chunks/canonicalize-DQHyFClh.js.map +0 -1
  532. package/dist/_chunks/cli-schema-sync-BTWEKJXo.js +0 -668
  533. package/dist/_chunks/cli-schema-sync-BTWEKJXo.js.map +0 -1
  534. package/dist/_chunks/define-COtkxMRT.js +0 -191
  535. package/dist/_chunks/define-COtkxMRT.js.map +0 -1
  536. package/dist/_chunks/define-c4au4I9R.js +0 -82
  537. package/dist/_chunks/define-c4au4I9R.js.map +0 -1
  538. package/dist/_chunks/fast-hash-D6hIVt1Y.js.map +0 -1
  539. package/dist/_chunks/logger-AWfuX-KJ.js.map +0 -1
  540. package/dist/_chunks/navigation-root-bUDZLw74.js +0 -124
  541. package/dist/_chunks/navigation-root-bUDZLw74.js.map +0 -1
  542. package/dist/_chunks/plugin-context---kTF5v8.js.map +0 -1
  543. package/dist/_chunks/registry-DbJPKoBp.js +0 -20
  544. package/dist/_chunks/registry-DbJPKoBp.js.map +0 -1
  545. package/dist/_chunks/router-ref-CtmF-aPv.js.map +0 -1
  546. package/dist/_chunks/schema-bridge-DT_Tn0Xf.js +0 -119
  547. package/dist/_chunks/schema-bridge-DT_Tn0Xf.js.map +0 -1
  548. package/dist/_chunks/segment-classify-Byy425ng.js.map +0 -1
  549. package/dist/_chunks/segment-context-ZDnXDkbz.js +0 -34
  550. package/dist/_chunks/segment-context-ZDnXDkbz.js.map +0 -1
  551. package/dist/_chunks/ssr-data-BOWsq18U.js.map +0 -1
  552. package/dist/_chunks/use-query-states-DFvWd-EA.js.map +0 -1
  553. package/dist/_chunks/use-segment-params-D1CzlgBo.js +0 -151
  554. package/dist/_chunks/use-segment-params-D1CzlgBo.js.map +0 -1
  555. package/dist/_chunks/walkers-_6zKFlch.js +0 -1002
  556. package/dist/_chunks/walkers-_6zKFlch.js.map +0 -1
  557. package/dist/client/error-boundary.js.map +0 -1
  558. package/dist/plugins/request-dep.d.ts +0 -32
  559. package/dist/plugins/request-dep.d.ts.map +0 -1
  560. package/dist/search-params/registry.d.ts +0 -20
  561. package/dist/search-params/registry.d.ts.map +0 -1
  562. package/dist/segment-params/define.d.ts +0 -89
  563. package/dist/segment-params/define.d.ts.map +0 -1
  564. package/dist/segment-params/index.d.ts +0 -3
  565. package/dist/segment-params/index.d.ts.map +0 -1
  566. package/dist/segment-params/index.js +0 -2
  567. package/src/plugins/request-dep.ts +0 -499
  568. package/src/search-params/registry.ts +0 -31
  569. package/src/segment-params/define.ts +0 -249
  570. package/src/segment-params/index.ts +0 -9
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Route collision spaces.
3
+ *
4
+ * A collision space is a set of routes that compete for URLs with each other,
5
+ * and there are three: the main tree, each parallel slot, and each intercepting
6
+ * subtree. This module owns the first two; `interception.ts` owns the third.
7
+ * All of them decide order-dependence with the one oracle in
8
+ * `collision-probe.ts`, so they cannot drift on what counts as two routes
9
+ * answering a single URL.
10
+ *
11
+ * See design/07-routing.md §"Route Collisions".
12
+ */
13
+
14
+ import type { SegmentNode } from './types.js';
15
+ import { computeInterceptedUrlBase } from './interception.js';
16
+ import { findOrderDependentCollision, type ProbeLeaf } from './collision-probe.js';
17
+
18
+ /**
19
+ * Validate that no two pages/routes in one collision space answer the same URL.
20
+ *
21
+ * Two route groups like (auth)/login/page.tsx and (marketing)/login/page.tsx both claim
22
+ * /login — the scanner must detect and reject this at build time.
23
+ *
24
+ * Each parallel slot is validated as its OWN collision space: a slot's pages
25
+ * intentionally share URL paths with the parent tree (that's the point of
26
+ * parallel routes), but two route groups INSIDE one slot claiming the same
27
+ * URL — e.g. @side/(a)/x/page.tsx + @side/(b)/x/page.tsx — still collide.
28
+ */
29
+ export function validateRouteGroupCollisions(root: SegmentNode): void {
30
+ validateCollisionSpace(root, '', root.urlPath);
31
+ }
32
+
33
+ /**
34
+ * Validate one collision space — the main tree, or one parallel slot.
35
+ *
36
+ * **Two checks, and the difference between them matters.** Two leaves at the
37
+ * same `urlPath` are decided by string equality, which is exact and needs no
38
+ * probing: `(auth)/login` and `(marketing)/login` claim one set of URLs
39
+ * whatever the precedence rules are, and naming the shared path is the clearest
40
+ * thing to tell the author.
41
+ *
42
+ * Everything else is decided by asking the matcher, through the shared oracle
43
+ * in `collision-probe.ts` (TIM-1308). Comparing `urlPath` spellings misses
44
+ * every pair that matches the same URLs while being *spelled* differently:
45
+ * `x/(a)/[id]` beside `x/(b)/[slug]` are two bare dynamics in one priority
46
+ * tier, and `x/(a)/page.tsx` beside `x/(b)/[[...rest]]` both answer `/x`
47
+ * because an optional catch-all also matches zero parts. Both built before
48
+ * TIM-1308 with filesystem enumeration order deciding which page renders,
49
+ * which is precisely what this validator and `validateAffixedSiblingConflicts`
50
+ * exist to reject.
51
+ *
52
+ * Segment *kinds* stay distinct without any special-casing here, because the
53
+ * oracle inherits `matchNode`'s priority ordering: `x/(a)/[id]` beside
54
+ * `x/(b)/[...rest]` resolves the same way in either `children` order, so it is
55
+ * not reported. So does an affixed sibling against a bare one; the
56
+ * equal-specificity affix tie is `validateAffixedSiblingConflicts`' job, and
57
+ * duplicating it here would double-report.
58
+ */
59
+ function validateCollisionSpace(spaceRoot: SegmentNode, segmentPath: string, base: string): void {
60
+ const leaves: ProbeLeaf[] = [];
61
+ collectRoutableLeaves(spaceRoot, new Map(), segmentPath, [], leaves, base);
62
+
63
+ const collision = findOrderDependentCollision(spaceRoot, leaves, base);
64
+ if (!collision) return;
65
+
66
+ throw new Error(
67
+ `Build error: route collision — two pages/routes answer the same URL.\n` +
68
+ ` URL: ${collision.url}\n` +
69
+ ` File 1: ${collision.first}\n` +
70
+ ` File 2: ${collision.second}\n` +
71
+ `Both match it, so which one renders would be decided by filesystem enumeration ` +
72
+ `order — renaming a route group can flip it. Rename or move one of them.\n` +
73
+ `Routes are compared by the URLs they match, not by how they are spelled: [id] and ` +
74
+ `[slug] match the same URLs, and an optional catch-all also matches with zero ` +
75
+ `segments.`
76
+ );
77
+ }
78
+
79
+ /**
80
+ * Walk one collision space and collect its routable leaves (page or route
81
+ * files), throwing on an exact `urlPath` collision. Each slot recurses as its
82
+ * OWN space — slots are parallel routes that intentionally share URL paths with
83
+ * their parent tree, but collisions within a single slot are real errors.
84
+ *
85
+ * The early return at an intercepting node drops that subtree's own pages from
86
+ * this space, but not its slots: a slot is its own collision space wherever it
87
+ * sits, so `validateSlotsInsideInterceptingSubtree` picks those up before
88
+ * returning.
89
+ *
90
+ * `chain` accumulates the URL-contributing nodes from the space root down, for
91
+ * the probe oracle. Route groups are excluded because they are transparent for
92
+ * matching, and the space root itself is excluded because `matchUrlParts`
93
+ * matches parts against its *children* — the root consumes nothing.
94
+ */
95
+ function collectRoutableLeaves(
96
+ node: SegmentNode,
97
+ seen: Map<string, { filePath: string; segmentPath: string }>,
98
+ segmentPath: string,
99
+ chain: SegmentNode[],
100
+ leaves: ProbeLeaf[],
101
+ base: string
102
+ ): void {
103
+ // Intercepting routes are resolved by the interception resolvers, not URL
104
+ // matching, and their computed urlPaths are not addresses — an interception
105
+ // mirrors the canonical route it replaces, so comparing those paths here
106
+ // would reject every correct interception. Skipping is not "unvalidated":
107
+ // `assertNoInterceptedPatternCollisions` gives each intercepting subtree its
108
+ // own collision space, keyed by the intercepted pattern rather than by these
109
+ // paths (TIM-1305). See design/07-routing.md §"Collisions Inside an
110
+ // Intercepting Subtree" and §"Why urlPath Stays Parent-Relative Inside an
111
+ // Intercepting Subtree".
112
+ const currentPath = segmentPath
113
+ ? `${segmentPath}/${node.segmentName}`
114
+ : node.segmentName || '(root)';
115
+
116
+ // The intercepting subtree's own pages leave this space — but its SLOTS do
117
+ // not leave every space, so they are picked up before returning. See
118
+ // `validateSlotsInsideInterceptingSubtree`.
119
+ if (node.segmentType === 'intercepting') {
120
+ // `node.urlPath` on an intercepting node is its OWNER's path — the input
121
+ // `computeInterceptedUrlBase` wants — because an intercepting segment adds
122
+ // no URL depth.
123
+ validateSlotsInsideInterceptingSubtree(
124
+ node,
125
+ currentPath,
126
+ interceptedBaseOf(node, node.urlPath)
127
+ );
128
+ return;
129
+ }
130
+
131
+ const routableFile = node.page ?? node.route;
132
+ if (routableFile) {
133
+ const existing = seen.get(node.urlPath);
134
+ if (existing) {
135
+ throw new Error(
136
+ `Build error: route collision — multiple route groups produce a page/route at the same URL path.\n` +
137
+ ` URL path: ${urlFromChain(base, chain)}\n` +
138
+ ` File 1: ${existing.filePath} (via ${existing.segmentPath})\n` +
139
+ ` File 2: ${routableFile.filePath} (via ${currentPath})\n` +
140
+ `Each URL path must map to exactly one page or route handler. ` +
141
+ `Rename or move one of the conflicting files.`
142
+ );
143
+ }
144
+ seen.set(node.urlPath, { filePath: routableFile.filePath, segmentPath: currentPath });
145
+ leaves.push({ filePath: routableFile.filePath, chain });
146
+ }
147
+
148
+ // Recurse into children. A route group contributes no URL part, so it is
149
+ // absent from the chain — exactly as `collectEffectiveChildren` flattens it
150
+ // away in the matcher.
151
+ for (const child of node.children) {
152
+ const isGroup = child.segmentType === 'group';
153
+ collectRoutableLeaves(
154
+ child,
155
+ seen,
156
+ currentPath,
157
+ isGroup ? chain : [...chain, child],
158
+ leaves,
159
+ base
160
+ );
161
+ }
162
+
163
+ // Recurse into slots — each slot is its own collision space, with its own
164
+ // seen map, its own leaf set and its own probe run.
165
+ //
166
+ // The base is accumulated from this space's own base rather than read off
167
+ // `slotNode.urlPath`, because this walk also runs on slot spaces nested inside
168
+ // an intercepting subtree, where `urlPath` is parent-relative and not an
169
+ // address. Outside one the two are identical by construction.
170
+ for (const slotNode of Object.values(node.slots)) {
171
+ validateCollisionSpace(slotNode, currentPath, urlFromChain(base, chain));
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Validate every parallel slot nested anywhere inside an intercepting subtree.
177
+ *
178
+ * Two walks each skip these slots for their own good reason, and together they
179
+ * left them checked by nothing: `collectRoutableLeaves` returns at the
180
+ * intercepting node before it reaches them, and `walkInterceptedLeaves`
181
+ * deliberately does not descend slots — correct for rewrite generation, since a
182
+ * slot inside an intercepting subtree produces no rewrite. But "produces no
183
+ * rewrite" answers the rewrite question and not the resolution one, so
184
+ * `@side/(a)/x/page.tsx` beside `@side/(b)/x/page.tsx` under an interceptor
185
+ * built with filesystem order picking the page — the ordinary per-slot
186
+ * collision that fails the build anywhere else in the tree (codex, on PR #1002).
187
+ *
188
+ * A slot is its own collision space wherever it sits, so each one is validated
189
+ * as one.
190
+ *
191
+ * **Which interception shape this still applies to.** TIM-1311 answered the
192
+ * question this was written under — whether such a slot renders at all — and
193
+ * the answer is per-shape:
194
+ *
195
+ * - **Children path** (`(browse)/(...)photo/@side`): the intercepting node
196
+ * joins the main route chain, so `route-element-builder.ts` resolves slots on
197
+ * it and below it like any other chain segment. They render, and this is the
198
+ * space where the check is load-bearing.
199
+ * - **Modal** (`feed/@modal/(.)photo/@side`): the slot sits inside another
200
+ * slot's subtree, which no renderer descends for slots — so it never renders,
201
+ * and `validateSlotPlacement` now rejects the placement before this runs.
202
+ * That rule subsumes this one for the modal shape rather than conflicting
203
+ * with it, exactly as anticipated.
204
+ *
205
+ * So this walk is reached only for children-path interceptors in a valid tree.
206
+ * It is not narrowed to them: the rule it states — a slot is its own collision
207
+ * space wherever it sits — is shape-independent, and making it conditional on a
208
+ * property the placement validator already guarantees would just be a second
209
+ * place for the two to disagree.
210
+ *
211
+ * The walk covers the whole subtree, not just the interceptor's direct slot
212
+ * children: `(...)photo/[id]/@side` is reached by neither walk for exactly the
213
+ * same reason as `(...)photo/@side`.
214
+ */
215
+ function validateSlotsInsideInterceptingSubtree(
216
+ node: SegmentNode,
217
+ segmentPath: string,
218
+ base: string
219
+ ): void {
220
+ for (const slotNode of Object.values(node.slots)) {
221
+ // A slot adds no URL depth, so it answers its owner's base.
222
+ validateCollisionSpace(slotNode, segmentPath, base);
223
+ }
224
+ for (const child of node.children) {
225
+ validateSlotsInsideInterceptingSubtree(
226
+ child,
227
+ `${segmentPath}/${child.segmentName}`,
228
+ childInterceptedBase(child, base)
229
+ );
230
+ }
231
+ }
232
+
233
+ /**
234
+ * The URL a leaf answers, built from its space's base and its own chain.
235
+ *
236
+ * Read instead of `node.urlPath` because `urlPath` inside an intercepting
237
+ * subtree is parent-relative and is not an address — the collision URL has to
238
+ * be one the author can actually find. Outside an intercepting subtree the two
239
+ * are identical by construction: the base is the space root's `urlPath` and the
240
+ * chain is every URL-contributing segment below it.
241
+ */
242
+ function urlFromChain(base: string, chain: SegmentNode[]): string {
243
+ if (chain.length === 0) return base || '/';
244
+ const tail = chain.map((n) => n.segmentName).join('/');
245
+ return `${base === '/' ? '' : base}/${tail}`;
246
+ }
247
+
248
+ /**
249
+ * The intercepted URL an intercepting directory's own page sits at.
250
+ *
251
+ * `urlPath` inside an intercepting subtree is deliberately parent-relative and
252
+ * is **not** an address (see design/07-routing.md §"Why urlPath Stays
253
+ * Parent-Relative Inside an Intercepting Subtree"), so a collision reported
254
+ * from one names a URL the author cannot find. The intercepted base is the real
255
+ * address, and it is what rewrite generation and the interception collision
256
+ * validator both already use.
257
+ */
258
+ function interceptedBaseOf(node: SegmentNode, ownerUrlPath: string): string {
259
+ return computeInterceptedUrlBase(
260
+ ownerUrlPath,
261
+ node.interceptionMarker ?? '(.)',
262
+ node.interceptedSegmentName ?? node.segmentName
263
+ );
264
+ }
265
+
266
+ /**
267
+ * Extend an intercepted base by one child segment.
268
+ *
269
+ * Route groups are transparent and contribute nothing. A nested intercepting
270
+ * node re-bases from its own marker rather than appending — that tree is
271
+ * rejected by `validateInterceptionPlacement`, but collision validation runs
272
+ * first, so it still has to produce a sensible URL rather than one containing a
273
+ * literal "(.)b".
274
+ */
275
+ function childInterceptedBase(child: SegmentNode, base: string): string {
276
+ // Slots are not in `children` — `scanChildren` files them under `slots` — so
277
+ // only groups are transparent here.
278
+ if (child.segmentType === 'group') return base;
279
+ if (child.segmentType === 'intercepting') return interceptedBaseOf(child, base);
280
+ return base === '/' ? `/${child.segmentName}` : `${base}/${child.segmentName}`;
281
+ }
@@ -4,17 +4,25 @@
4
4
  * Runs at scan time (build and dev startup). Each check produces a warning
5
5
  * with the file path, what's wrong, and what to do about it.
6
6
  *
7
- * These are warnings, not errors — they don't block the build. The goal is
8
- * to catch issues that would otherwise produce cryptic runtime behavior
9
- * (silent 404s, empty pages, confusing React errors).
7
+ * Most checks are warnings — they don't block the build. The goal is to catch
8
+ * issues that would otherwise produce cryptic runtime behavior (silent 404s,
9
+ * empty pages, confusing React errors).
10
+ *
11
+ * A check emits `level: 'error'` only when the misconfiguration is *proven*
12
+ * (never inferred from an absence the analysis cannot see through) and fatal
13
+ * to rendering. `plugins/routing.ts` turns those into a thrown build error,
14
+ * so a false positive there refuses a working app — see
15
+ * `classifyClientBoundaryFile` for how that bar is met.
10
16
  *
11
17
  * Design doc: 07-routing.md, 10-error-handling.md
12
18
  */
13
19
 
14
20
  import { existsSync } from 'node:fs';
15
- import type { RouteTree, SegmentNode } from './types.js';
21
+ import type { RouteFile, RouteTree, SegmentNode } from './types.js';
16
22
  import { swallow } from '../server/logger.js';
23
+ import { isMdxFilePath } from '../server/utils/mdx-file.js';
17
24
  import {
25
+ fileDefaultExportIsLocalValue,
18
26
  fileHasAnyExport,
19
27
  fileHasDefaultExport,
20
28
  fileHasDirective,
@@ -61,10 +69,10 @@ export function lintConventions(tree: RouteTree, appDir: string): ConventionWarn
61
69
  // Check 5: page.tsx / layout.tsx without default export
62
70
  checkDefaultExports(tree.root, warnings);
63
71
 
64
- // Check 6: error.tsx / global-error.tsx without 'use client' directive
65
- checkErrorDirectives(tree.root, warnings);
66
- if (tree.globalError && isScriptExtension(tree.globalError.extension)) {
67
- checkFileUseClientDirective(tree.globalError.filePath, 'global-error', warnings);
72
+ // Check 6: error.tsx / 404.tsx / 4xx.tsx / … without 'use client' directive
73
+ checkClientBoundaryFiles(tree.root, warnings);
74
+ if (tree.globalError) {
75
+ checkFileUseClientDirective(tree.globalError, 'global-error', warnings);
68
76
  }
69
77
 
70
78
  return warnings;
@@ -272,54 +280,132 @@ function checkFileDefaultExport(
272
280
  }
273
281
  }
274
282
 
275
- // ─── Check: Error Directive ─────────────────────────────────────────────────
283
+ // ─── Check: Client Boundary Directive ───────────────────────────────────────
276
284
 
277
285
  /**
278
- * Warn when error.tsx or global-error.tsx is missing 'use client'.
286
+ * Check every file the renderer hands to `TimberErrorBoundary` as a component
287
+ * prop: `error.tsx`, `global-error.tsx`, and the status-code family
288
+ * (`404.tsx`, `4xx.tsx`, `503.tsx`, …).
289
+ *
290
+ * `TimberErrorBoundary` is a client component, so a server component passed as
291
+ * `fallbackComponent` is a function crossing the RSC → client boundary. React
292
+ * Flight refuses to serialize it, and because the boundary wraps the segment's
293
+ * *children*, the failure hits every page under that segment — not just the
294
+ * error path. The raw message ("Functions cannot be passed directly to Client
295
+ * Components") names neither the file nor the fix (TIM-1329).
279
296
  *
280
- * React's error boundary API requires client components. Without the directive,
281
- * the error boundary silently fails — the component renders as a server component
282
- * with no error catching. Users discover this only via cryptic runtime errors.
297
+ * MDX status files are exempt: they are server components by design and the
298
+ * renderer pre-renders them into `fallbackElement` instead. See TIM-503 and
299
+ * `server/error-boundary-wrapper.ts`.
300
+ *
301
+ * `.json` status files never reach React at all, and are not in `statusFiles`.
283
302
  */
284
- function checkErrorDirectives(node: SegmentNode, warnings: ConventionWarning[]): void {
285
- if (node.error && isScriptExtension(node.error.extension)) {
286
- checkFileUseClientDirective(node.error.filePath, 'error', warnings);
303
+ function checkClientBoundaryFiles(node: SegmentNode, warnings: ConventionWarning[]): void {
304
+ if (node.error) {
305
+ checkFileUseClientDirective(node.error, 'error', warnings);
306
+ }
307
+
308
+ for (const [key, file] of Object.entries(node.statusFiles ?? {})) {
309
+ checkFileUseClientDirective(file, key, warnings);
287
310
  }
288
311
 
289
312
  for (const child of node.children) {
290
- checkErrorDirectives(child, warnings);
313
+ checkClientBoundaryFiles(child, warnings);
291
314
  }
292
315
  for (const slot of Object.values(node.slots)) {
293
- checkErrorDirectives(slot, warnings);
316
+ checkClientBoundaryFiles(slot, warnings);
294
317
  }
295
318
  }
296
319
 
320
+ /**
321
+ * `'client'` — carries the directive.
322
+ * `'server'` — proven server component: no directive, and the default export is
323
+ * a function or class built by this very module, so no other file's directive
324
+ * can be governing it.
325
+ * `'unknown'` — no directive, but the exported value may come from elsewhere
326
+ * (`export { default } from './client-thing'`), or the file could not be
327
+ * read or parsed. Never escalated to a build error.
328
+ */
329
+ type ClientBoundaryVerdict = 'client' | 'server' | 'unknown';
330
+
331
+ function classifyClientBoundaryFile(filePath: string): ClientBoundaryVerdict {
332
+ if (fileHasDirective(filePath, 'use client')) return 'client';
333
+ if (fileDefaultExportIsLocalValue(filePath)) return 'server';
334
+ return 'unknown';
335
+ }
336
+
337
+ /** Example component name for the fix snippet, keyed by convention name. */
338
+ function exampleComponentName(convention: string): string {
339
+ if (convention === 'global-error') return 'GlobalError';
340
+ if (convention === 'error') return 'ErrorBoundary';
341
+ if (convention === '404') return 'NotFound';
342
+ return 'StatusPage';
343
+ }
344
+
345
+ /**
346
+ * @param file the route file (its extension decides whether the check applies)
347
+ * @param convention the convention name — `error`, `global-error`, `404`, `4xx`, …
348
+ */
297
349
  function checkFileUseClientDirective(
298
- filePath: string,
299
- fileType: string,
350
+ file: RouteFile,
351
+ convention: string,
300
352
  warnings: ConventionWarning[]
301
353
  ): void {
354
+ // MDX/markdown status and error files are server components by design — the
355
+ // renderer pre-renders them into `fallbackElement`, so they never cross the
356
+ // boundary as a function.
357
+ //
358
+ // The exemption is the *complement* of the renderer's own test, using the
359
+ // renderer's own helper: everything `error-boundary-wrapper.ts` and
360
+ // `error-renderer.ts` do not treat as MDX, they pass as a component. An
361
+ // independent "is this a script extension?" list here would be a second
362
+ // expression of that fact, free to drift into a gap where a file is a
363
+ // component to the renderer and exempt to the linter.
364
+ if (isMdxFilePath(file.filePath)) return;
365
+
366
+ const filePath = file.filePath;
367
+ const label = `${convention}.${file.extension}`;
368
+ let verdict: ClientBoundaryVerdict;
302
369
  try {
303
- if (!fileHasDirective(filePath, 'use client')) {
304
- warnings.push({
305
- id: `ERROR_NO_USE_CLIENT:${filePath}`,
306
- summary: `${fileType}.tsx is missing 'use client': ${filePath}`,
307
- details:
308
- ` File: ${filePath}\n\n` +
309
- ` ${fileType}.tsx must start with 'use client' because React error\n` +
310
- ' boundaries require client components. Without it, the error boundary\n' +
311
- ' silently fails and errors are uncaught.\n\n' +
312
- " To fix: Add 'use client' as the first line of the file:\n\n" +
313
- " 'use client';\n\n" +
314
- ` export default function ${fileType === 'global-error' ? 'GlobalError' : 'ErrorBoundary'}({ error, reset }) {\n` +
315
- ' return <div><h2>Something went wrong</h2><button onClick={reset}>Try again</button></div>;\n' +
316
- ' }\n',
317
- level: 'warn',
318
- });
319
- }
370
+ verdict = classifyClientBoundaryFile(filePath);
320
371
  } catch (err) {
321
- swallow(err, `convention-lint: unreadable ${fileType} file ${filePath}`);
372
+ swallow(err, `convention-lint: unreadable ${label} file ${filePath}`);
373
+ return;
322
374
  }
375
+ if (verdict === 'client') return;
376
+
377
+ const isError = verdict === 'server';
378
+ const name = exampleComponentName(convention);
379
+ const props =
380
+ convention === 'error' || convention === 'global-error' ? '{ error, reset }' : '{ status }';
381
+
382
+ warnings.push({
383
+ id: `NEEDS_USE_CLIENT:${filePath}`,
384
+ summary: isError
385
+ ? `${label} is a server component and must be a client component: ${filePath}`
386
+ : `${label} may be missing 'use client': ${filePath}`,
387
+ details:
388
+ ` File: ${filePath}\n\n` +
389
+ ` ${label} is handed to timber's error boundary — a client\n` +
390
+ ' component — as a prop. A server component cannot cross that boundary:\n' +
391
+ ' React fails with "Functions cannot be passed directly to Client\n' +
392
+ ' Components", and every page under this segment fails to render.\n\n' +
393
+ (isError
394
+ ? " To fix: Add 'use client' as the first line of the file:\n\n" +
395
+ " 'use client';\n\n" +
396
+ ` export default function ${name}(${props}) {\n` +
397
+ ' return <h1>…</h1>;\n' +
398
+ ' }\n'
399
+ : // The default export comes from somewhere this analysis cannot follow
400
+ // — a re-export, an imported binding, a wrapper call. It may well
401
+ // already be a client component, so say what was checked rather than
402
+ // assert a break.
403
+ " This file has no 'use client' directive, and its default export\n" +
404
+ ' comes from elsewhere, so timber cannot tell which it is. If the\n' +
405
+ " module it re-exports starts with 'use client', nothing is wrong\n" +
406
+ ' and you can ignore this. Otherwise add the directive there.\n'),
407
+ level: isError ? 'error' : 'warn',
408
+ });
323
409
  }
324
410
 
325
411
  // ─── Check: App Directory Exists ────────────────────────────────────────────
@@ -352,14 +438,27 @@ import { styleText } from 'node:util';
352
438
 
353
439
  type Format = InspectColor | readonly InspectColor[];
354
440
  const noValidate = { validateStream: false } as const;
355
- const style = (fmt: Format, text: string) => styleText(fmt, text, noValidate);
441
+
442
+ export interface FormatOptions {
443
+ /**
444
+ * Emit ANSI escapes. Off when the text becomes an `Error` message —
445
+ * whoever prints it decides its formatting, and colored escapes inside
446
+ * an exception leak into logs and stack traces.
447
+ */
448
+ color?: boolean;
449
+ }
356
450
 
357
451
  /**
358
452
  * Format warnings for terminal output.
359
453
  *
360
454
  * Groups by severity, uses colors, and includes fix suggestions.
361
455
  */
362
- export function formatConventionWarnings(warnings: ConventionWarning[]): string {
456
+ export function formatConventionWarnings(
457
+ warnings: ConventionWarning[],
458
+ options: FormatOptions = {}
459
+ ): string {
460
+ const { color = true } = options;
461
+ const style = (fmt: Format, text: string) => (color ? styleText(fmt, text, noValidate) : text);
363
462
  if (warnings.length === 0) return '';
364
463
 
365
464
  const errors = warnings.filter((w) => w.level === 'error');