@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
@@ -20,115 +20,36 @@ import { createElement, Fragment } from 'react';
20
20
  import { withSpan } from './tracing.js';
21
21
  import type { RouteMatch } from './pipeline.js';
22
22
  import type { ManifestSegmentNode } from './route-matcher.js';
23
- import { resolveMetadata, renderMetadataToElements } from './metadata.js';
24
23
  import type { Metadata } from './types.js';
25
- import { collectMetadataRouteHeadElements } from './metadata-routes.js';
26
24
  import { DenySignal, RedirectSignal } from './primitives.js';
25
+ import { MetadataCollector, MetadataHead } from './metadata-collector.js';
27
26
  import { AccessGate } from './access-gate.js';
28
27
  import { requestContextAls } from './als-registry.js';
29
- import {
30
- PageDenyBoundary,
31
- buildDenyPageChain,
32
- renderMatchingDenyPage,
33
- setDenyStatus,
34
- } from './deny-boundary.js';
28
+ import { PageDenyBoundary, buildDenyPageChain, handleCaughtDeny } from './deny-boundary.js';
35
29
  import type { DenyPageEntry } from './deny-boundary.js';
36
30
  import { LAYOUT_CHILDREN_SLOT } from './prebuilt/slots.js';
37
- import { resolveSlotProps, extractUrlParts, type SlotSkipEntry } from './slot-resolver.js';
31
+ import { resolveSlotProps, publishSlotSegmentParams, type SlotSkipEntry } from './slot-resolver.js';
32
+ import { consumedUrlParts } from './chain-url-parts.js';
33
+ import { withPublishedParams } from './publish-params.js';
34
+ import type { RscPayloadRoot } from '../shared/payload-root.js';
38
35
  import { SegmentProvider } from '../client/segment-context.js';
39
36
  import { SegmentOutlet } from '../client/segment-outlet.js';
40
37
  import { SlotOutlet } from '../client/slot-outlet.js';
41
38
  import { SlotsProvider } from '../client/slot-provider.js';
42
39
 
40
+ import { TimberErrorBoundary } from '../client/error-boundary.js';
43
41
  import { wrapSegmentWithErrorBoundaries } from './error-boundary-wrapper.js';
44
42
  import type { InterceptionContext } from './pipeline.js';
43
+ import { shouldSkipSegment, type ClientStateTree } from './state-tree-diff.js';
44
+ import { truncateSkippableForChangedSlots } from './skippable-prefix.js';
45
45
  import {
46
- shouldSkipSegment,
47
- shouldSkipSlot,
48
46
  computeSegmentKeys,
47
+ computeSegmentTreePaths,
49
48
  computeSlotKey,
50
- type ClientStateTree,
51
- } from './state-tree-diff.js';
52
- import { headElementsToReact } from './metadata.js';
49
+ } from '../routing/segment-keys.js';
50
+ import { setFirstDeniedSegmentIndex } from './deny-boundary.js';
53
51
  import { loadModule } from './safe-load.js';
54
52
 
55
- /**
56
- * Truncate the skippable prefix when a skippable layout has slots whose
57
- * content changed between the departing and destination URLs (TIM-1232).
58
- *
59
- * Without this, changed slots at skipped layouts are never sent to the
60
- * client — the `continue` in the main loop skips slot resolution entirely.
61
- * Removing the segment from the skippable set forces it to render with
62
- * its slots. The prefix must stay contiguous, so all segments after the
63
- * first changed-slot segment are also removed.
64
- */
65
- function truncateSkippableForChangedSlots(
66
- skippableSet: Set<number>,
67
- segments: ManifestSegmentNode[],
68
- segmentKeys: string[],
69
- match: RouteMatch,
70
- clientStateTree: ClientStateTree,
71
- req: Request
72
- ): void {
73
- const departingUrl = req.headers.get('X-Timber-URL');
74
- let depParsed: URL | null = null;
75
- if (departingUrl) {
76
- try {
77
- depParsed = new URL(departingUrl, 'http://localhost');
78
- } catch {
79
- skippableSet.clear();
80
- return;
81
- }
82
- }
83
- const destPathname = new URL(req.url, 'http://localhost').pathname;
84
- const depPathname = depParsed?.pathname ?? null;
85
- const split = (p: string) => (p === '/' ? [] : p.slice(1).split('/'));
86
- const destAll = split(destPathname);
87
- const depAll = depPathname ? split(depPathname) : null;
88
- const clientSlots = clientStateTree.slots ?? null;
89
- const rawParams = match.rawSegmentParams ?? match.segmentParams ?? {};
90
-
91
- for (let i = 0; i < segments.length; i++) {
92
- if (!skippableSet.has(i)) continue;
93
- const segment = segments[i];
94
- const slotEntries = Object.entries(segment.slots ?? {});
95
- if (slotEntries.length === 0) continue;
96
-
97
- const segIdx = match.segments.indexOf(segment);
98
- const parents =
99
- segIdx >= 0 ? match.segments.slice(0, segIdx + 1).filter((s) => s.segmentName !== '') : [];
100
- const sliceAt = extractUrlParts(parents, rawParams).length;
101
- const destParts = destAll.slice(sliceAt);
102
- const depParts = depAll ? depAll.slice(sliceAt) : null;
103
- let parentChanged = false;
104
- if (depAll) {
105
- const dp = depAll.slice(0, sliceAt),
106
- dd = destAll.slice(0, sliceAt);
107
- parentChanged = dp.length !== dd.length || dp.some((p, j) => p !== dd[j]);
108
- }
109
-
110
- for (const [slotName, slotNode] of slotEntries) {
111
- const m = slotNode as ManifestSegmentNode;
112
- const k = computeSlotKey(segmentKeys[i], `@${slotName}`);
113
- if (
114
- m.children.some((c) => c.segmentType === 'intercepting') ||
115
- parentChanged ||
116
- depParts === null ||
117
- !shouldSkipSlot({
118
- slotKey: k,
119
- clientSlots,
120
- slotNode: m,
121
- departingUrlParts: depParts,
122
- destinationUrlParts: destParts,
123
- })
124
- ) {
125
- for (let j = i; j < segments.length; j++) skippableSet.delete(j);
126
- return;
127
- }
128
- }
129
- }
130
- }
131
-
132
53
  /**
133
54
  * Replace the outermost SegmentOutlet in a partial payload with its
134
55
  * SegmentProvider child. Walks through AccessGate wrappers (which sit
@@ -141,9 +62,9 @@ function replaceOutermostSegmentOutlet(
141
62
  if (element.type === SegmentOutlet) {
142
63
  return replacement;
143
64
  }
144
- // AccessGate wraps outside SegmentOutlet — recurse through it.
65
+ // AccessGate and MetadataCollector wrap outside SegmentOutlet — recurse through them.
145
66
  const props = element.props as Record<string, unknown>;
146
- if (element.type === AccessGate && props.children) {
67
+ if ((element.type === AccessGate || element.type === MetadataCollector) && props.children) {
147
68
  const inner = replaceOutermostSegmentOutlet(props.children as React.ReactElement, replacement);
148
69
  /* eslint-disable react/no-children-prop -- createElement API */
149
70
  return createElement(element.type as React.FunctionComponent<Record<string, unknown>>, {
@@ -175,6 +96,28 @@ const CLIENT_REFERENCE_TAG = Symbol.for('react.client.reference');
175
96
  * function. Client components must go through createElement only — they are
176
97
  * serialized as references in the RSC Flight stream, not executed on the server.
177
98
  */
99
+ /**
100
+ * The `segments` array handed to a layout's `SegmentProvider` — the value
101
+ * `useSelectedLayoutSegment(s)` reads.
102
+ *
103
+ * Only its **length** matters: both hooks take `segments.length` as a depth and
104
+ * index the live pathname with it. So this is really "how many URL parts had
105
+ * been consumed by the time this layout rendered", which is
106
+ * `consumedUrlParts` — the same derivation slot matching and the slot-skip
107
+ * slicing use, so a layout and the slots beside it cannot disagree about how
108
+ * deep they are. The root consumes nothing → `['']`, length 1.
109
+ *
110
+ * @internal — exported for tests; production has exactly one caller.
111
+ */
112
+ export function computeLayoutSegmentPath(
113
+ segments: ManifestSegmentNode[],
114
+ index: number,
115
+ match: RouteMatch
116
+ ): string[] {
117
+ const parts = consumedUrlParts(segments, index, match);
118
+ return parts.length === 0 ? [''] : ['', ...parts];
119
+ }
120
+
178
121
  export function isClientReference(component: unknown): boolean {
179
122
  return (
180
123
  component != null &&
@@ -186,7 +129,7 @@ export function isClientReference(component: unknown): boolean {
186
129
  // ─── Typed Errors ────────────────────────────────────────────────────────
187
130
 
188
131
  /**
189
- * Thrown when a defineSegmentParams codec's parse() fails.
132
+ * Thrown when an app/schema.ts segment-param codec's parse() fails.
190
133
  * The pipeline catches this and responds with 404.
191
134
  */
192
135
  export class ParamCoercionError extends Error {
@@ -217,8 +160,12 @@ export interface LayoutComponentEntry {
217
160
 
218
161
  /** Result of building a route element tree. */
219
162
  export interface RouteElementResult {
220
- /** The React element tree (page wrapped in layouts, access gates, error boundaries). */
221
- element: React.ReactElement;
163
+ /**
164
+ * The payload root: the React element tree (page wrapped in layouts, access
165
+ * gates, error boundaries) plus the params it rendered with, as siblings.
166
+ * See shared/payload-root.ts for why params are not a wrapper around it.
167
+ */
168
+ payload: RscPayloadRoot;
222
169
  /** Layout components loaded along the segment chain. */
223
170
  layoutComponents: LayoutComponentEntry[];
224
171
  /** Segments from the route match. */
@@ -334,47 +281,19 @@ export async function buildRouteElement(
334
281
  ): Promise<RouteElementResult> {
335
282
  const segments = match.segments;
336
283
 
337
- // ── Access pre-pass ──────────────────────────────────────────────────
338
- // Run access checks eagerly to determine which segments are denied.
339
- // This gates metadata() resolution: denied segments' metadata functions
340
- // are never called, preventing unauthorized side effects (DB queries)
341
- // and metadata content leaking into the <head> of denied responses.
342
- //
343
- // Verdicts are passed to AccessGate for synchronous replay (DenySignal
344
- // renders the deny page in-tree; RedirectSignal propagates). For PASSING
345
- // segments, no verdict is passed — AccessGate re-runs access during
346
- // renderToReadableStream to populate React.cache for layout dedup.
347
- //
348
- // See TIM-1027, design/04-authorization.md, design/13-security.md.
349
- const accessVerdicts = new Map<number, DenySignal | RedirectSignal>();
350
- let firstDeniedIndex = Infinity;
351
-
352
- for (let i = 0; i < segments.length; i++) {
353
- const segment = segments[i];
354
- if (!segment.access || i >= firstDeniedIndex) continue;
355
-
356
- try {
357
- const accessMod = await loadModule(segment.access);
358
- const accessFn = accessMod.default as (() => Promise<void>) | undefined;
359
- if (accessFn) {
360
- await withSpan(
361
- 'timber.access.pre',
362
- { 'timber.segment': segment.segmentName ?? 'unknown' },
363
- () => accessFn()
364
- );
365
- }
366
- } catch (error: unknown) {
367
- if (error instanceof DenySignal || error instanceof RedirectSignal) {
368
- accessVerdicts.set(i, error);
369
- firstDeniedIndex = i;
370
- } else {
371
- throw error;
372
- }
373
- }
284
+ // ── Module loading ──────────────────────────────────────────────────
285
+ // Load layout/page modules and collect metadata sources. Metadata
286
+ // resolution is deferred to render time via MetadataCollector components
287
+ // placed below each AccessGate — denied segments' metadata() never
288
+ // executes because AccessGate prevents MetadataCollector from rendering.
289
+ // No pre-render access pass needed. See TIM-1363.
290
+ interface MetadataSource {
291
+ metadataExport: Metadata | (() => Promise<Metadata>);
292
+ segmentName: string;
293
+ isPage: boolean;
294
+ segmentIndex: number;
374
295
  }
375
-
376
- // ── Module loading + metadata resolution ─────────────────────────────
377
- const metadataEntries: Array<{ metadata: Metadata; isPage: boolean }> = [];
296
+ const metadataSources: MetadataSource[] = [];
378
297
  const layoutComponents: LayoutComponentEntry[] = [];
379
298
  let PageComponent: ((...args: unknown[]) => unknown) | null = null;
380
299
  let deferSuspenseFor = 0;
@@ -382,7 +301,6 @@ export async function buildRouteElement(
382
301
  for (let i = 0; i < segments.length; i++) {
383
302
  const segment = segments[i];
384
303
  const isLeaf = i === segments.length - 1;
385
- const isDenied = i >= firstDeniedIndex;
386
304
 
387
305
  // Load layout
388
306
  if (segment.layout) {
@@ -394,15 +312,14 @@ export async function buildRouteElement(
394
312
  });
395
313
  }
396
314
 
397
- // Param coercion is handled in the pipeline (Stage 2c) before
398
- // middleware and rendering. See coerceSegmentParams() in pipeline.ts.
399
-
400
315
  rejectLegacyGenerateMetadata(mod, segment.layout.filePath ?? segment.urlPath);
401
- if (!isDenied) {
402
- const layoutMetadata = await extractMetadata(mod, segment);
403
- if (layoutMetadata) {
404
- metadataEntries.push({ metadata: layoutMetadata, isPage: false });
405
- }
316
+ if (mod.metadata) {
317
+ metadataSources.push({
318
+ metadataExport: mod.metadata as Metadata | (() => Promise<Metadata>),
319
+ segmentName: segment.segmentName ?? segment.urlPath,
320
+ isPage: false,
321
+ segmentIndex: i,
322
+ });
406
323
  }
407
324
  deferSuspenseFor = extractDeferSuspenseFor(mod, deferSuspenseFor);
408
325
  }
@@ -411,18 +328,17 @@ export async function buildRouteElement(
411
328
  if (isLeaf && segment.page) {
412
329
  const mod = await loadModule(segment.page);
413
330
 
414
- // Param coercion is handled in the pipeline (Stage 2c) before
415
- // middleware and rendering. See coerceSegmentParams() in pipeline.ts.
416
-
417
331
  if (mod.default) {
418
332
  PageComponent = mod.default as (...args: unknown[]) => unknown;
419
333
  }
420
334
  rejectLegacyGenerateMetadata(mod, segment.page.filePath ?? segment.urlPath);
421
- if (!isDenied) {
422
- const pageMetadata = await extractMetadata(mod, segment);
423
- if (pageMetadata) {
424
- metadataEntries.push({ metadata: pageMetadata, isPage: true });
425
- }
335
+ if (mod.metadata) {
336
+ metadataSources.push({
337
+ metadataExport: mod.metadata as Metadata | (() => Promise<Metadata>),
338
+ segmentName: segment.segmentName ?? segment.urlPath,
339
+ isPage: true,
340
+ segmentIndex: i,
341
+ });
426
342
  }
427
343
  deferSuspenseFor = extractDeferSuspenseFor(mod, deferSuspenseFor);
428
344
  }
@@ -440,45 +356,36 @@ export async function buildRouteElement(
440
356
  );
441
357
  }
442
358
 
359
+ // Compute state-tree keys for all segments. Route groups accumulate
360
+ // ancestor group names for globally unique keys (e.g., "/(a)/(shared)"
361
+ // instead of just "/(shared)"). This is the single key computation —
362
+ // the same keys are used for skip decisions, SegmentOutlet props, the
363
+ // X-Timber-Segments header (via buildSegmentInfo), the error boundaries,
364
+ // and deny page ownership.
365
+ //
366
+ // Computed here, before the deny chains below, because each chain entry
367
+ // records the key of the segment that owns it.
368
+ const segmentKeys = computeSegmentKeys(segments);
369
+ // The directory path of each segment — the key `setSlotParams` and codegen
370
+ // both use for a slot beneath it, and the identity interception scoping
371
+ // compares against. One derivation, so a slot cannot be keyed two ways.
372
+ const segmentTreePaths = computeSegmentTreePaths(segments);
373
+
443
374
  // Build deny page fallback chains for each segment position.
444
375
  // When AccessGate or PageDenyBoundary catches a DenySignal, they render
445
376
  // the matching deny page in-tree instead of throwing into React Flight.
446
377
  // The chain walks from the current segment outward to root, collecting
447
- // status-code files (403.tsx → 4xx.tsx → error.tsx) in fallback order.
448
- // See TIM-666.
378
+ // status-code files (403.tsx → 4xx.tsx → error.tsx) in fallback order,
379
+ // each tagged with the key of the segment it came from so the catch site
380
+ // can tell a file it owns from an ancestor's. See TIM-666, TIM-1356.
449
381
  const denyPageChains = new Map<number, DenyPageEntry[]>();
450
382
  for (let i = 0; i < segments.length; i++) {
451
- const chain = await buildDenyPageChain(segments, i);
383
+ const chain = await buildDenyPageChain(segments, i, segmentKeys);
452
384
  if (chain.length > 0) {
453
385
  denyPageChains.set(i, chain);
454
386
  }
455
387
  }
456
388
 
457
- // Resolve metadata and convert to React elements that React 19 Float
458
- // hoists into <head> during both SSR and client rendering. This replaces
459
- // the previous HeadElement descriptor system (string splice for SSR,
460
- // X-Timber-Head header for RSC nav, imperative DOM updates on client).
461
- // React manages <title>/<meta>/<link> lifecycle across navigations.
462
- // See design/16-metadata.md, TIM-1151.
463
- const resolvedMetadata = resolveMetadata(metadataEntries);
464
- const headElements = renderMetadataToElements(resolvedMetadata);
465
-
466
- // Auto-link metadata route files (icon, apple-icon, manifest, opengraph-image).
467
- // See design/16-metadata.md §"Auto-Linking"
468
- const autoLinkedElements = collectMetadataRouteHeadElements(
469
- segments,
470
- firstDeniedIndex,
471
- resolvedMetadata,
472
- new URL(req.url),
473
- metadataRouteHashes
474
- );
475
- headElements.push(...autoLinkedElements);
476
-
477
- // Convert HeadElement descriptors to React elements for Float hoisting.
478
- // Placed at the page segment level so they're always re-rendered on
479
- // SPA navigation (sync layouts may be skipped by segment tree diffing).
480
- const metadataElement = headElementsToReact(headElements);
481
-
482
389
  // Build element tree: page wrapped in layouts (innermost to outermost)
483
390
  const h = createElement as (...args: unknown[]) => React.ReactElement;
484
391
 
@@ -519,6 +426,7 @@ export async function buildRouteElement(
519
426
  Page: PageComponent,
520
427
  route: match.segments[leafIndex]?.urlPath ?? '/',
521
428
  denyPages: leafDenyPages,
429
+ segmentKey: segmentKeys[leafIndex],
522
430
  onSettled,
523
431
  });
524
432
  } else {
@@ -533,11 +441,18 @@ export async function buildRouteElement(
533
441
  element = h(TracedPage, {});
534
442
  }
535
443
 
536
- // Wrap page + metadata in a Fragment so Float-hoisted elements are at
537
- // the page segment level (always re-rendered, never skipped by segment
538
- // tree diffing). React 19 Float hoists the <title>/<meta>/<link>
539
- // children into <head> regardless of where they appear in the tree.
540
- element = h(Fragment, null, metadataElement, element);
444
+ // Wrap page in MetadataHead — a server component that reads all
445
+ // metadata entries collected by MetadataCollector components above it,
446
+ // merges them, and renders Float-hoisted head elements as siblings of
447
+ // the page. Placed at the page level so metadata is always re-rendered
448
+ // on SPA navigation (never skipped by segment tree diffing).
449
+ // See TIM-1363, design/16-metadata.md.
450
+ element = h(MetadataHead, {
451
+ segments,
452
+ requestUrl: req.url,
453
+ metadataRouteHashes,
454
+ children: element,
455
+ });
541
456
 
542
457
  // Build a lookup of layout components by segment for O(1) access.
543
458
  const layoutBySegment = new Map(
@@ -548,13 +463,6 @@ export async function buildRouteElement(
548
463
  // The client uses this to merge the partial payload with its cached segments.
549
464
  const skippedSegments: string[] = [];
550
465
 
551
- // Compute state-tree keys for all segments. Route groups accumulate
552
- // ancestor group names for globally unique keys (e.g., "/(a)/(shared)"
553
- // instead of just "/(shared)"). This is the single key computation —
554
- // the same keys are used for skip decisions, SegmentOutlet props, and
555
- // the X-Timber-Segments header (via buildSegmentInfo).
556
- const segmentKeys = computeSegmentKeys(segments);
557
-
558
466
  // Pre-compute the contiguous skippable prefix (top-down).
559
467
  //
560
468
  // Skipped segments must form a contiguous prefix from root to avoid
@@ -572,10 +480,6 @@ export async function buildRouteElement(
572
480
  const isLeaf = i === segments.length - 1;
573
481
  const layoutComponent = layoutBySegment.get(segments[i]);
574
482
 
575
- // Never skip denied segments — their AccessGate must render the
576
- // deny page, not reuse the client's cached (passing) layout.
577
- if (i >= firstDeniedIndex) break;
578
-
579
483
  if (shouldSkipSegment(segmentKeys[i], layoutComponent, isLeaf, clientStateTree)) {
580
484
  skippableSet.add(i);
581
485
  } else if (layoutComponent) {
@@ -645,8 +549,27 @@ export async function buildRouteElement(
645
549
 
646
550
  // Slot caching: the departing URL (X-Timber-URL header) and the
647
551
  // destination URL (request URL) are compared to determine which
648
- // slots' content has changed. Full URLs (with search) are passed
649
- // so slot pages reading getSearchParams() are correctly re-rendered.
552
+ // slots' content has changed.
553
+ //
554
+ // Pathnames only. The client sends `X-Timber-URL` as a pathname
555
+ // (`client/router.ts` takes `.pathname` on both branches) and
556
+ // `slot-resolver.ts` reads `.pathname` from both sides, so search
557
+ // params take no part in the skip decision.
558
+ //
559
+ // Nothing here can tell whether a slot's content depends on the query.
560
+ // What is supposed to make that safe lives elsewhere: a slot whose server
561
+ // render varies with the query must reach it through a request accessor
562
+ // (page components are built with no props), which the request-dep analysis
563
+ // is meant to catch — and that analysis is incomplete for accessors reached
564
+ // through indirection, so this is currently a real staleness hole. See
565
+ // TIM-1291 and design/19-client-navigation.md. A slot whose *client* subtree
566
+ // varies with the query reads useSearchParams(), which re-renders from
567
+ // NavigationContext whether or not the slot was skipped; that half is
568
+ // unconditional.
569
+ //
570
+ // Do not add search to the comparison by sending the full departing URL:
571
+ // the header is hashed into the public `_rsc` cache key (TIM-1268),
572
+ // and a digest does not anonymize a guessable query string.
650
573
  const departingUrl = req.headers.get('X-Timber-URL');
651
574
  const destinationUrl = req.url;
652
575
  const slotSkipInfo: SlotSkipEntry[] = [];
@@ -661,20 +584,63 @@ export async function buildRouteElement(
661
584
  // Record for X-Timber-Skipped-Segments header (outermost first, so prepend).
662
585
  skippedSegments.unshift(segmentKeys[i]);
663
586
 
587
+ // A skipped layout never reaches resolveSlotProps below, but its slots
588
+ // stay mounted on the client — so their params still have to be
589
+ // published, or useSegmentParams(slotPath) falls back to the main
590
+ // route's record after any navigation that skipped their parent
591
+ // (TIM-1285). Params only: nothing renders and no access chain runs,
592
+ // because an unchanged slot is what made this layout skippable.
593
+ publishSlotSegmentParams(segment, match, destinationUrl, segmentTreePaths[i]);
594
+
595
+ // No error boundaries are wrapped for a skipped segment — the `continue`
596
+ // below returns before that walk. A deny addressed to this segment
597
+ // therefore finds no boundary in the payload, and since TIM-1356 the
598
+ // payload path no longer falls back to a re-render for an addressed
599
+ // deny. What makes that sound is the precondition for skipping at all:
600
+ // `shouldSkipSegment` only returns true when this exact segment key is
601
+ // in the client's state tree, which means the client rendered this
602
+ // segment on an earlier navigation and still has its boundaries mounted,
603
+ // carrying the same key this build would have stamped. The payload
604
+ // splices in below them, so the throw passes through them on its way up.
605
+ //
606
+ // Metadata from skipped segments still needs to be collected (e.g.,
607
+ // root layout title templates). MetadataCollector wraps the element
608
+ // inside AccessGate — if access denies, metadata is excluded.
609
+ const skippedMeta = metadataSources.filter((s) => s.segmentIndex === i).reverse();
610
+ for (const meta of skippedMeta) {
611
+ const isDynamic = typeof meta.metadataExport === 'function';
612
+ element = h(MetadataCollector, {
613
+ metadataExport: meta.metadataExport,
614
+ segmentName: meta.segmentName,
615
+ isPage: meta.isPage,
616
+ ...(isDynamic ? { onSettled: trackShellComponent() } : {}),
617
+ children: element,
618
+ });
619
+ }
620
+
664
621
  // SECURITY: Even though the layout is skipped, AccessGate MUST still
665
622
  // wrap the element tree. access.ts runs on every navigation regardless
666
623
  // of cached layouts or state tree content.
667
624
  // See design/13-security.md §"Auth always runs" (test #11).
625
+ //
626
+ // Skipped segments omit denyPages and set skippedSegment=true. If
627
+ // access denies at a skipped segment, AccessGate records the deny in
628
+ // ALS and rethrows — the response path returns rscErrorEnvelope so the
629
+ // client hard-navigates for a clean full render with correct deny page
630
+ // placement. Rendering the deny page in-tree here would appear inside
631
+ // the client's cached layout chrome (TIM-1074). See TIM-1363.
668
632
  if (segment.access) {
669
633
  const accessMod = await loadModule(segment.access);
670
634
  const accessFn = accessMod.default as (() => unknown) | undefined;
671
635
  if (accessFn) {
672
- const verdict = accessVerdicts.get(i);
636
+ const onGateSettled = trackShellComponent();
673
637
  element = h(AccessGate, {
674
638
  accessFn,
675
639
  segmentName: segment.segmentName,
676
- denyPages: denyPageChains.get(i),
677
- ...(verdict ? { verdict } : {}),
640
+ segmentKey: segmentKeys[i],
641
+ segmentIndex: i,
642
+ skippedSegment: true,
643
+ onSettled: onGateSettled,
678
644
  children: element,
679
645
  });
680
646
  }
@@ -683,12 +649,42 @@ export async function buildRouteElement(
683
649
  continue;
684
650
  }
685
651
 
652
+ // Wrap with MetadataCollector(s) INSIDE error boundaries so a
653
+ // metadata() throw is caught by this segment's status-file boundaries,
654
+ // not an ancestor's. Still inside AccessGate (placed below) — when
655
+ // access denies, neither MetadataCollector nor error boundaries render.
656
+ //
657
+ // Reversed: the loop wraps innermost-to-outermost, so the last-wrapped
658
+ // collector renders first. Reversing ensures layout metadata is pushed
659
+ // before page metadata, preserving the root→page merge order required
660
+ // by resolveMetadata (page-authoritative). See TIM-1363.
661
+ const segmentMetadata = metadataSources.filter((s) => s.segmentIndex === i).reverse();
662
+ for (const meta of segmentMetadata) {
663
+ const isDynamic = typeof meta.metadataExport === 'function';
664
+ element = h(MetadataCollector, {
665
+ metadataExport: meta.metadataExport,
666
+ segmentName: meta.segmentName,
667
+ isPage: meta.isPage,
668
+ ...(isDynamic ? { onSettled: trackShellComponent() } : {}),
669
+ children: element,
670
+ });
671
+ }
672
+
686
673
  // Wrap with error boundaries from this segment (inside layout).
687
674
  // Keep ALL error boundaries (including 4xx) — they're the safety net for
688
675
  // DenySignal from nested server components that escape AccessGate/PageDenyBoundary
689
676
  // try/catch. Status-code files must be 'use client' TSX or MDX to serialize
690
677
  // as error boundary fallbacks. See TIM-666.
691
- element = await wrapSegmentWithErrorBoundaries(segment, element, h);
678
+ // `segmentKey` makes each boundary addressable: a deny hoisted to this
679
+ // segment carries its owner's key, and every other boundary declines it
680
+ // rather than catching it first and rendering at the wrong depth
681
+ // (TIM-1356).
682
+ element = await wrapSegmentWithErrorBoundaries(segment, element, {
683
+ loadModule,
684
+ createElement: h,
685
+ errorBoundaryComponent: TimberErrorBoundary,
686
+ segmentKey: segmentKeys[i],
687
+ });
692
688
 
693
689
  // Wrap with layout BEFORE AccessGate — AccessGate is OUTSIDE the layout.
694
690
  // When AccessGate denies, the layout never renders. The deny page appears
@@ -696,42 +692,26 @@ export async function buildRouteElement(
696
692
  // This prevents leaking layout UI (sidebars, nav) on denied pages.
697
693
  // See design/04-authorization.md §"Access Failure".
698
694
  if (layoutComponent) {
699
- // Resolve parallel slots for this layout, wrapping each in
700
- // SegmentOutlet for client-side caching. Unchanged slots get
701
- // skip=true so the client keeps its cached content.
702
- //
703
- // Skip slot resolution for denied segments (TIM-1229): when a
704
- // parent segment's AccessGate denies, the layout never renders,
705
- // so slot props are never consumed. Resolving them is wasted work
706
- // and could trigger side effects in slot access.ts functions.
695
+ // Resolve parallel slots for this layout. If a parent AccessGate
696
+ // denies during render, the layout never renders and slot props
697
+ // are never consumed. Slot access functions may still fire eagerly
698
+ // for skip-candidate slots — a follow-up will make that lazy.
707
699
  const segmentId = segmentKeys[i];
708
- const parentTreePath =
709
- '/' +
710
- segments
711
- .slice(0, i + 1)
712
- .map((s) => s.segmentName)
713
- .filter(Boolean)
714
- .join('/');
715
- const slotProps =
716
- i >= firstDeniedIndex
717
- ? {}
718
- : await resolveSlotProps({
719
- segment,
720
- segmentId,
721
- match,
722
- h,
723
- interception,
724
- parentTreePath,
725
- departingUrl,
726
- destinationUrl,
727
- clientStateTree: clientStateTree as ClientStateTree | null,
728
- slotSkipInfo,
729
- });
730
-
731
- // '/'.split('/') → ['', ''] (length 2), but root should be [''] (length 1).
732
- // The extra empty string makes useSelectedLayoutSegment skip the first URL segment.
733
- const rawPath = segment.urlPath.split('/');
734
- const segmentPath = rawPath.length === 2 && rawPath[1] === '' ? [''] : rawPath;
700
+ const parentTreePath = segmentTreePaths[i];
701
+ const slotProps = await resolveSlotProps({
702
+ segment,
703
+ segmentId,
704
+ match,
705
+ h,
706
+ interception,
707
+ parentTreePath,
708
+ departingUrl,
709
+ destinationUrl,
710
+ clientStateTree: clientStateTree as ClientStateTree | null,
711
+ slotSkipInfo,
712
+ });
713
+
714
+ const segmentPath = computeLayoutSegmentPath(segments, i, match);
735
715
  const parallelRouteKeys = Object.keys(segment.slots ?? {});
736
716
 
737
717
  // Build the layout element.
@@ -765,11 +745,14 @@ export async function buildRouteElement(
765
745
  (layoutComponentRef as (props: Record<string, unknown>) => unknown)(props)
766
746
  );
767
747
  } catch (error: unknown) {
768
- if (error instanceof DenySignal && layoutDenyPages) {
769
- const denyElement = renderMatchingDenyPage(layoutDenyPages, error.status, error.data);
770
- if (denyElement) {
771
- setDenyStatus(error.status);
772
- return denyElement;
748
+ // A layout that denied never renders its own chrome — it threw
749
+ // instead of returning it — so an entry this segment owns is
750
+ // already correctly placed here. Anything owned further up is
751
+ // hoisted (handleCaughtDeny throws). See TIM-1356.
752
+ if (error instanceof DenySignal) {
753
+ setFirstDeniedSegmentIndex(i);
754
+ if (layoutDenyPages) {
755
+ return handleCaughtDeny(layoutDenyPages, error, segmentId);
773
756
  }
774
757
  }
775
758
  throw error;
@@ -810,23 +793,23 @@ export async function buildRouteElement(
810
793
  outermostSegmentProvider = segmentProviderElement;
811
794
  }
812
795
 
813
- // Wrap in AccessGate OUTSIDE the layout.
814
- // If access denies, the deny page renders here — the layout above
815
- // never executes. Parent layouts (from outer iterations) form the shell.
816
- // See TIM-662, TIM-666, design/04-authorization.md §"Access Failure".
796
+ // Wrap in AccessGate OUTSIDE the layout, error boundaries, and MetadataCollector.
797
+ // If access denies, neither the layout nor MetadataCollector renders.
798
+ // Parent layouts (from outer iterations) form the shell. Access runs
799
+ // exactly once, during render, inside React.cache scope — layouts
800
+ // share the cache. See TIM-1363, design/04-authorization.md.
817
801
  if (segment.access) {
818
802
  const accessMod = await loadModule(segment.access);
819
803
  const accessFn = accessMod.default as (() => unknown) | undefined;
820
804
  if (accessFn) {
821
- // Pass verdict for denied/redirected segments so AccessGate replays
822
- // without re-execution. Passing segments omit verdict so AccessGate
823
- // re-runs access during render for React.cache population.
824
- const verdict = accessVerdicts.get(i);
805
+ const onGateSettled = trackShellComponent();
825
806
  element = h(AccessGate, {
826
807
  accessFn,
827
808
  segmentName: segment.segmentName,
809
+ segmentKey: segmentKeys[i],
810
+ segmentIndex: i,
828
811
  denyPages: denyPageChains.get(i),
829
- ...(verdict ? { verdict } : {}),
812
+ onSettled: onGateSettled,
830
813
  children: element,
831
814
  });
832
815
  }
@@ -854,8 +837,13 @@ export async function buildRouteElement(
854
837
  shellSettled = Promise.race([Promise.all(shellPromises).then(() => {}), shortCircuit]);
855
838
  }
856
839
 
840
+ // Publish the request's params beside the finished tree, as the payload's
841
+ // root row. Last, because the partial-navigation rewrite above rebuilds the
842
+ // tree and this takes it as it finally stands.
843
+ const payload = withPublishedParams(element);
844
+
857
845
  return {
858
- element,
846
+ payload,
859
847
  layoutComponents,
860
848
  segments,
861
849
  deferSuspenseFor,