@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
@@ -8,10 +8,14 @@
8
8
  * `DenyPageEntry` records ordered by specificity (specific status →
9
9
  * category catch-all → `error.tsx`).
10
10
  *
11
- * 2. **Runtime matching** (`renderMatchingDenyPage`) — picks the first
12
- * chain entry whose status filter matches the thrown DenySignal and
13
- * returns a React element for the matching component. Used by
14
- * `AccessGate` and `PageDenyBoundary` when they catch a deny.
11
+ * 2. **Runtime matching and placement** (`matchDenyEntry`, `placeDenyPage`,
12
+ * `handleCaughtDeny`) — picks the first chain entry whose status filter
13
+ * matches the thrown DenySignal, then decides whether the catch site may
14
+ * render it where it stands. A chain spans the catcher outward to root,
15
+ * so a match may belong to an ancestor segment; rendering it in place
16
+ * would wrap it in every layout in between. Ancestor-owned matches are
17
+ * re-thrown with `ownerKey` stamped on the signal so the error boundary
18
+ * at the owning segment renders it. See TIM-1356.
15
19
  *
16
20
  * 3. **The page boundary itself** (`PageDenyBoundary`) — the async server
17
21
  * component that wraps a server-component page, calls it, and catches
@@ -36,7 +40,9 @@ import type { SerializableError } from '../client/error-reconstituter.js';
36
40
  import { requestContextAls } from './als-registry.js';
37
41
  import { DenySignal } from './primitives.js';
38
42
  import { loadModule } from './safe-load.js';
43
+ import type { ManifestLoader } from './safe-load.js';
39
44
  import { withSpan } from './tracing.js';
45
+ import { isValidElementType, type LoadedComponent } from './utils/element-type.js';
40
46
  import { isMdxFilePath } from './utils/mdx-file.js';
41
47
  import type { ManifestSegmentNode } from './route-matcher.js';
42
48
 
@@ -56,27 +62,106 @@ export interface DenyPageEntry {
56
62
  kind: 'status' | 'error';
57
63
  /** MDX files are server components — rendered with plain props, never ErrorReconstituter. */
58
64
  isMdx: boolean;
65
+ /**
66
+ * Position in the segment chain (0 = root, ascending toward leaf).
67
+ * Used by `matchDenyEntry` to reconstruct per-segment ordering for 5xx
68
+ * statuses — `resolve5xx` walks per-segment (status files then error.tsx
69
+ * at each segment before moving outward), while the chain is stored in
70
+ * the 4xx two-pass order. Without this, `deny(503)` resolves to a
71
+ * different file depending on which path serves it. See TIM-1360.
72
+ */
73
+ segmentIndex: number;
74
+ /**
75
+ * Segment key of the segment whose directory holds this file — where the
76
+ * deny page must render, which is not necessarily where it was caught.
77
+ *
78
+ * A chain flattens every segment from the catcher outward to root, so
79
+ * without this a catcher cannot tell a file it owns from an ancestor's,
80
+ * and renders both at its own position — wrapping the deny page in every
81
+ * layout in between (TIM-1356).
82
+ *
83
+ * A **key**, not an index: `computeSegmentKeys` produces the identity the
84
+ * client segment cache already navigates by, and it survives the trip
85
+ * through the Flight digest to a boundary the client mounted on an
86
+ * earlier navigation. A chain position would not — depth varies with
87
+ * route groups and interception.
88
+ *
89
+ * It inherits that key's known collision rather than fixing it: keys reset
90
+ * to `urlPath` at each URL-visible segment, so `(browse)/feed` and
91
+ * `(landing)/feed` are both `/feed` (see `routing/segment-keys.ts`). Two
92
+ * such branches are already indistinguishable to the skip decision and the
93
+ * client cache, so a deny addressed across them can land in the wrong
94
+ * branch's layout. Fixing that means fixing the key for every reader, not
95
+ * minting a second identity here.
96
+ */
97
+ ownerKey: string;
59
98
  }
60
99
 
100
+ /**
101
+ * What `renderDenyEntry` needs to build the element: which component, and
102
+ * which props contract it expects. Deliberately excludes `ownerKey` — the
103
+ * re-render fallback path builds an entry from a `resolveStatusFile`
104
+ * resolution that already carries the position, and would otherwise have to
105
+ * invent an owner it never reads.
106
+ */
107
+ export type DenyEntryShape = Pick<DenyPageEntry, 'component' | 'kind' | 'isMdx'>;
108
+
109
+ /**
110
+ * Where a matched deny page must render, relative to the catcher that
111
+ * matched it.
112
+ *
113
+ * `render` — the catcher owns the file and is already at the right
114
+ * position. `hoist` — an ancestor owns it; the catcher must re-throw so the
115
+ * boundary at `ownerKey` renders it, outside the layouts in between.
116
+ */
117
+ export type DenyPlacement =
118
+ | { kind: 'render'; element: React.ReactElement }
119
+ | { kind: 'hoist'; ownerKey: string };
120
+
61
121
  // ─── Chain Construction ──────────────────────────────────────────────────
62
122
 
123
+ /**
124
+ * Load a candidate's default export, or null if it cannot be a component.
125
+ *
126
+ * Gated on `isValidElementType` — the same predicate `error-boundary-wrapper.ts`
127
+ * uses to decide whether to create a boundary at all, and deliberately not a
128
+ * second expression of it. A file this walk accepts but that one rejects
129
+ * yields a chain entry whose owner has no boundary, so a deny addressed to it
130
+ * passes every boundary and escapes with nothing to catch it. (Before the two
131
+ * agreed, such an entry instead crashed inside `createElement` when rendered
132
+ * in place.)
133
+ *
134
+ * Agreement is per-file, which is not the same as per-segment: this walk runs
135
+ * over every segment in the chain, while the boundary walk is skipped for a
136
+ * segment the client already has cached. See the skip branch in
137
+ * `route-element-builder.ts` for why that case still lands.
138
+ *
139
+ * See TIM-1356.
140
+ */
141
+ async function loadChainComponent(file: ManifestLoader): Promise<LoadedComponent | null> {
142
+ const mod = await loadModule(file).catch(() => null);
143
+ return isValidElementType(mod?.default) ? mod.default : null;
144
+ }
145
+
63
146
  /**
64
147
  * Build the deny page fallback chain from the segment chain.
65
148
  *
66
149
  * Walks segments from `startIndex` outward (toward root) and collects
67
150
  * status-code file components in fallback order:
68
151
  * 1. Specific status files (403.tsx, 404.tsx) — exact match
69
- * 2. Category catch-alls (4xx.tsx) — matches any 4xx
152
+ * 2. Category catch-alls (4xx.tsx, 5xx.tsx) — matches any in category
70
153
  * 3. error.tsx — catches everything
71
154
  *
72
- * Each segment is checked in this order. The chain is ordered so the
73
- * FIRST match wins at catch time. This mirrors resolveStatusFile's 4xx
74
- * component chain (status-code-resolver.ts) — the two walks must stay in
75
- * sync or in-tree and re-render deny paths resolve different files.
155
+ * The chain is stored in the 4xx two-pass order (all status files across
156
+ * segments, then all error.tsx), which mirrors `resolve4xx`. For 5xx,
157
+ * `matchDenyEntry` reconstructs the per-segment walk from each entry's
158
+ * `segmentIndex` — matching `resolve5xx`'s single-pass ordering where a
159
+ * leaf `error.tsx` beats a root `5xx.tsx`. See TIM-1360.
76
160
  */
77
161
  export async function buildDenyPageChain(
78
162
  segments: ManifestSegmentNode[],
79
- startIndex: number
163
+ startIndex: number,
164
+ segmentKeys: string[]
80
165
  ): Promise<DenyPageEntry[]> {
81
166
  const chain: DenyPageEntry[] = [];
82
167
 
@@ -93,13 +178,15 @@ export async function buildDenyPageChain(
93
178
  if (key !== '4xx' && key !== '5xx') {
94
179
  const status = parseInt(key, 10);
95
180
  if (!isNaN(status)) {
96
- const mod = await loadModule(file).catch(() => null);
97
- if (mod?.default) {
181
+ const component = await loadChainComponent(file);
182
+ if (component) {
98
183
  chain.push({
99
184
  status,
100
- component: mod.default as (...args: unknown[]) => unknown,
185
+ component,
101
186
  kind: 'status',
102
187
  isMdx: isMdxFilePath(file.filePath),
188
+ segmentIndex: i,
189
+ ownerKey: segmentKeys[i],
103
190
  });
104
191
  }
105
192
  }
@@ -109,14 +196,16 @@ export async function buildDenyPageChain(
109
196
  // Category catch-alls (4xx.tsx, 5xx.tsx)
110
197
  for (const [key, file] of Object.entries(segment.statusFiles)) {
111
198
  if (key === '4xx' || key === '5xx') {
112
- const mod = await loadModule(file).catch(() => null);
113
- if (mod?.default) {
199
+ const component = await loadChainComponent(file);
200
+ if (component) {
114
201
  const categoryStatus = key === '4xx' ? 400 : 500;
115
202
  chain.push({
116
203
  status: categoryStatus,
117
- component: mod.default as (...args: unknown[]) => unknown,
204
+ component,
118
205
  kind: 'status',
119
206
  isMdx: isMdxFilePath(file.filePath),
207
+ segmentIndex: i,
208
+ ownerKey: segmentKeys[i],
120
209
  });
121
210
  }
122
211
  }
@@ -129,13 +218,15 @@ export async function buildDenyPageChain(
129
218
  for (let i = startIndex; i >= 0; i--) {
130
219
  const segment = segments[i];
131
220
  if (segment.error) {
132
- const mod = await loadModule(segment.error).catch(() => null);
133
- if (mod?.default) {
221
+ const component = await loadChainComponent(segment.error);
222
+ if (component) {
134
223
  chain.push({
135
224
  status: null,
136
- component: mod.default as (...args: unknown[]) => unknown,
225
+ component,
137
226
  kind: 'error',
138
227
  isMdx: isMdxFilePath(segment.error.filePath),
228
+ segmentIndex: i,
229
+ ownerKey: segmentKeys[i],
139
230
  });
140
231
  }
141
232
  }
@@ -147,31 +238,106 @@ export async function buildDenyPageChain(
147
238
  // ─── Runtime Matcher ──────────────────────────────────────────────────────
148
239
 
149
240
  /**
150
- * Find the first deny page in the chain that matches the given status code.
151
- * Returns a React element for the matching component, or null if no match.
241
+ * Find the first deny page in the chain whose status filter matches.
242
+ * Returns null when nothing in the chain accepts this status.
243
+ *
244
+ * For 4xx statuses, the chain's stored order is correct: every status file
245
+ * across all segments (inner→outer) before any error.tsx — so a root
246
+ * `404.tsx` beats a leaf `error.tsx`. This matches `resolve4xx`.
247
+ *
248
+ * For 5xx statuses, the match walks per-segment (inner→outer): at each
249
+ * segment, status files (`503.tsx` → `5xx.tsx`) then `error.tsx`, before
250
+ * moving to the next segment outward. A leaf `error.tsx` therefore beats a
251
+ * root `5xx.tsx` — the intentional inverse of the 4xx chain, matching
252
+ * `resolve5xx`. See TIM-1360.
152
253
  */
153
- export function renderMatchingDenyPage(
154
- chain: DenyPageEntry[],
155
- status: number,
156
- data: unknown
157
- ): React.ReactElement | null {
254
+ export function matchDenyEntry(chain: DenyPageEntry[], status: number): DenyPageEntry | null {
255
+ if (status >= 500) return matchDenyEntry5xx(chain, status);
256
+
158
257
  for (const entry of chain) {
159
- if (entry.status === status) {
160
- return renderDenyEntry(entry, status, data);
161
- }
162
- if (entry.status === 400 && status >= 400 && status <= 499) {
163
- return renderDenyEntry(entry, status, data);
258
+ if (entry.status === status) return entry;
259
+ if (entry.status === 400 && status >= 400 && status <= 499) return entry;
260
+ if (entry.status === null) return entry;
261
+ }
262
+ return null;
263
+ }
264
+
265
+ /**
266
+ * 5xx per-segment walk: at each segment (inner→outer), check status files
267
+ * then error.tsx before moving outward. Mirrors `resolve5xx`.
268
+ */
269
+ function matchDenyEntry5xx(chain: DenyPageEntry[], status: number): DenyPageEntry | null {
270
+ // Collect unique segment indices, sorted descending (inner→outer).
271
+ const segmentIndices: number[] = [];
272
+ for (const entry of chain) {
273
+ if (!segmentIndices.includes(entry.segmentIndex)) {
274
+ segmentIndices.push(entry.segmentIndex);
164
275
  }
165
- if (entry.status === 500 && status >= 500 && status <= 599) {
166
- return renderDenyEntry(entry, status, data);
276
+ }
277
+ segmentIndices.sort((a, b) => b - a);
278
+
279
+ for (const seg of segmentIndices) {
280
+ // Status files at this segment (exact then category).
281
+ for (const entry of chain) {
282
+ if (entry.segmentIndex !== seg || entry.kind === 'error') continue;
283
+ if (entry.status === status) return entry;
284
+ if (entry.status === 500 && status >= 500 && status <= 599) return entry;
167
285
  }
168
- if (entry.status === null) {
169
- return renderDenyEntry(entry, status, data);
286
+ // error.tsx at this segment.
287
+ for (const entry of chain) {
288
+ if (entry.segmentIndex !== seg || entry.kind !== 'error') continue;
289
+ return entry;
170
290
  }
171
291
  }
172
292
  return null;
173
293
  }
174
294
 
295
+ /**
296
+ * Decide where the deny page for `status` renders, from the perspective of
297
+ * the catch site at `selfKey`.
298
+ *
299
+ * The match itself is unchanged — first entry in fallback order wins, so
300
+ * every path picks the same file. What this adds is the position: a chain
301
+ * built at segment i spans i outward to root, so a matched entry is owned
302
+ * either by the catcher or by one of its ancestors.
303
+ *
304
+ * - Owned by the catcher → `render`. The catcher already sits at the
305
+ * owning segment's position.
306
+ * - Owned by an ancestor → `hoist`. Rendering here would wrap the deny page
307
+ * in every layout between the owner and the catcher. The caller re-throws
308
+ * with `ownerKey` stamped on the signal so the boundary at that segment
309
+ * renders it instead — inside the owner's layout, outside the rest.
310
+ *
311
+ * Returns null when no entry matches; the caller re-throws unannotated and
312
+ * the re-render fallback resolves it.
313
+ *
314
+ * See design/04-authorization.md §"Where a Deny Page Renders", TIM-1356.
315
+ */
316
+ export function placeDenyPage(
317
+ chain: DenyPageEntry[],
318
+ status: number,
319
+ data: unknown,
320
+ selfKey: string | undefined
321
+ ): DenyPlacement | null {
322
+ const entry = matchDenyEntry(chain, status);
323
+ if (!entry) return null;
324
+
325
+ if (entry.ownerKey !== selfKey) {
326
+ // An MDX owner cannot be addressed. Its boundary holds a `fallbackElement`
327
+ // pre-rendered at tree-build time — before this status or its data
328
+ // existed — so hoisting to it would render a page that never learns what
329
+ // was denied (a category `4xx.mdx` gets no `status` prop at all). Leave it
330
+ // unaddressed so the re-render fallback serves it, which is the documented
331
+ // MDX path and resolves the position correctly on its own. See TIM-503.
332
+ if (entry.isMdx) return null;
333
+ return { kind: 'hoist', ownerKey: entry.ownerKey };
334
+ }
335
+
336
+ // Rendering in place is unaffected: `renderDenyEntry` builds the MDX element
337
+ // here, at catch time, with the real status.
338
+ return { kind: 'render', element: renderDenyEntry(entry, status, data) };
339
+ }
340
+
175
341
  /**
176
342
  * Build the element for a matched deny chain entry with the props contract
177
343
  * the file's convention documents:
@@ -184,7 +350,7 @@ export function renderMatchingDenyPage(
184
350
  * - error.mdx: plain { status } — server component, static content.
185
351
  */
186
352
  export function renderDenyEntry(
187
- entry: DenyPageEntry,
353
+ entry: DenyEntryShape,
188
354
  status: number,
189
355
  data: unknown
190
356
  ): React.ReactElement {
@@ -216,6 +382,43 @@ export function renderDenyEntry(
216
382
  });
217
383
  }
218
384
 
385
+ /**
386
+ * The whole of what an in-tree catch site does with a `DenySignal`, shared
387
+ * by all three of them (`PageDenyBoundary`, `TracedLayout`, `AccessGate`).
388
+ *
389
+ * Returns the deny page element when this catch site owns the matched file.
390
+ * Otherwise it throws — annotated with `ownerKey` when an ancestor owns the
391
+ * file, bare when nothing matched at all.
392
+ *
393
+ * `setDenyStatus` runs on the hoist path too, and must run *before* the
394
+ * throw: on the RSC payload path no boundary executes server-side, so the
395
+ * ALS status is the only thing standing between the response and a 200
396
+ * (`rsc-payload.ts` reads it after the stream settles).
397
+ *
398
+ * Either branch leaves part of the matched chain unmounted — hoisting sends
399
+ * the render to a boundary that discards everything below it, and a layout
400
+ * that denies returns the deny page instead of its own child outlet. What the
401
+ * response then says about the tree is decided from the deny status alone in
402
+ * `rsc-payload.ts`, not tracked here; see the note there for why.
403
+ */
404
+ export function handleCaughtDeny(
405
+ chain: DenyPageEntry[] | undefined,
406
+ error: DenySignal,
407
+ selfKey: string | undefined
408
+ ): React.ReactElement {
409
+ const placement = chain ? placeDenyPage(chain, error.status, error.data, selfKey) : null;
410
+
411
+ if (placement?.kind === 'render') {
412
+ setDenyStatus(error.status);
413
+ return placement.element;
414
+ }
415
+ if (placement?.kind === 'hoist') {
416
+ setDenyStatus(error.status);
417
+ error.ownerKey = placement.ownerKey;
418
+ }
419
+ throw error;
420
+ }
421
+
219
422
  // ─── Page Boundary ────────────────────────────────────────────────────────
220
423
 
221
424
  /**
@@ -236,6 +439,7 @@ export async function PageDenyBoundary({
236
439
  Page,
237
440
  route,
238
441
  denyPages,
442
+ segmentKey,
239
443
  onSettled,
240
444
  }: {
241
445
  /** The page server component function. */
@@ -244,6 +448,12 @@ export async function PageDenyBoundary({
244
448
  route: string;
245
449
  /** Deny page fallback chain from the segment chain. */
246
450
  denyPages: DenyPageEntry[];
451
+ /**
452
+ * Segment key of the leaf. The page renders inside the leaf's layout, so
453
+ * a leaf-owned deny page is already at the right position; anything the
454
+ * chain matched further up is hoisted to its owner.
455
+ */
456
+ segmentKey: string;
247
457
  /** Called when the page component finishes (success or deny). See TIM-1045. */
248
458
  onSettled?: () => void;
249
459
  }): Promise<React.ReactElement> {
@@ -252,11 +462,7 @@ export async function PageDenyBoundary({
252
462
  return result as React.ReactElement;
253
463
  } catch (error: unknown) {
254
464
  if (error instanceof DenySignal) {
255
- const denyElement = renderMatchingDenyPage(denyPages, error.status, error.data);
256
- if (denyElement) {
257
- setDenyStatus(error.status);
258
- return denyElement;
259
- }
465
+ return handleCaughtDeny(denyPages, error, segmentKey);
260
466
  }
261
467
  throw error;
262
468
  } finally {
@@ -286,3 +492,25 @@ export function setDenyStatus(status: number): void {
286
492
  export function getDenyStatus(): number | undefined {
287
493
  return requestContextAls.getStore()?.denyStatus;
288
494
  }
495
+
496
+ /**
497
+ * Record the first denied segment index in the request context ALS.
498
+ * Called from AccessGate when a DenySignal is caught during render.
499
+ * Only the first (shallowest) denial is recorded — deeper denials are
500
+ * unreachable because the gate short-circuits the subtree.
501
+ * See TIM-1363.
502
+ */
503
+ export function setFirstDeniedSegmentIndex(index: number): void {
504
+ const store = requestContextAls.getStore();
505
+ if (store && store._firstDeniedSegmentIndex === undefined) {
506
+ store._firstDeniedSegmentIndex = index;
507
+ }
508
+ }
509
+
510
+ /**
511
+ * Read the first denied segment index from the request context ALS.
512
+ * Returns Infinity if no segment denied during render.
513
+ */
514
+ export function getFirstDeniedSegmentIndex(): number {
515
+ return requestContextAls.getStore()?._firstDeniedSegmentIndex ?? Infinity;
516
+ }