@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
@@ -11,6 +11,7 @@
11
11
  import type { RouteContext } from './types.js';
12
12
  import { logRouteError } from './logger.js';
13
13
  import { DenySignal, RedirectSignal } from './primitives.js';
14
+ import { stripHeadBody } from './head-response.js';
14
15
 
15
16
  // ─── Types ───────────────────────────────────────────────────────────────
16
17
 
@@ -89,18 +90,17 @@ export async function handleRouteRequest(mod: RouteModule, ctx: RouteContext): P
89
90
  });
90
91
  }
91
92
 
92
- // HEAD fallback — run GET, strip body
93
+ // HEAD fallback — dispatch to GET when the module exports no HEAD, so the
94
+ // headers are the ones a GET would produce. The body is stripped by the
95
+ // shared helper (also applied at the pipeline exit, where it is a no-op the
96
+ // second time) rather than open-coded here — one implementation means the
97
+ // discarded stream is cancelled on every path. See TIM-1324.
93
98
  if (method === 'HEAD') {
94
99
  if (mod.HEAD) {
95
- return runHandler(mod.HEAD, ctx);
100
+ return stripHeadBody(await runHandler(mod.HEAD, ctx), method);
96
101
  }
97
102
  if (mod.GET) {
98
- const res = await runHandler(mod.GET, ctx);
99
- // Return headers + status but no body
100
- return new Response(null, {
101
- status: res.status,
102
- headers: res.headers,
103
- });
103
+ return stripHeadBody(await runHandler(mod.GET, ctx), method);
104
104
  }
105
105
  }
106
106
 
@@ -83,10 +83,27 @@ function matchPathname(root: ManifestSegmentNode, pathname: string): RouteMatch
83
83
  const result = matchUrlParts(root, parts);
84
84
  if (!result) return null;
85
85
 
86
- // Collect middleware from all segments in the chain, root to leaf.
87
- // Each middleware is lazy-loaded via dynamic import on first invocation.
86
+ return {
87
+ segments: result.chain,
88
+ segmentParams: result.params,
89
+ middlewareChain: collectMiddlewareChain(result.chain),
90
+ };
91
+ }
92
+
93
+ /**
94
+ * Collect middleware from a matched segment chain, root to leaf.
95
+ *
96
+ * Each entry is lazy-loaded via dynamic import on first invocation.
97
+ *
98
+ * Shared with the children-path interception resolver
99
+ * (`children-interception.ts`), which assembles a chain from the tree rather
100
+ * than from a pathname but produces the same kind of `RouteMatch` — every
101
+ * `middleware.ts` above an intercepted page must run exactly as it does for
102
+ * the canonical route.
103
+ */
104
+ export function collectMiddlewareChain(chain: ManifestSegmentNode[]): MiddlewareFn[] {
88
105
  const middlewareChain: MiddlewareFn[] = [];
89
- for (const segment of result.chain) {
106
+ for (const segment of chain) {
90
107
  if (segment.middleware) {
91
108
  const loader = segment.middleware.load;
92
109
  middlewareChain.push(async (ctx) => {
@@ -97,12 +114,7 @@ function matchPathname(root: ManifestSegmentNode, pathname: string): RouteMatch
97
114
  });
98
115
  }
99
116
  }
100
-
101
- return {
102
- segments: result.chain,
103
- segmentParams: result.params,
104
- middlewareChain,
105
- };
117
+ return middlewareChain;
106
118
  }
107
119
 
108
120
  // ─── Metadata Route Matcher ─────────────────────────────────────────────
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Origin-side binding of the `_rsc` cache key to the request that produced
3
+ * the response (TIM-1268).
4
+ *
5
+ * The key is deterministic, and deliberately so — that is what makes RSC
6
+ * payloads cacheable. But deterministic also means *predictable*: anyone can
7
+ * compute the key another client will request. Without this guard, a caller
8
+ * could request `/about?_rsc=<victim's key>` while sending its own
9
+ * `X-Timber-State-Tree`, and a shared cache that keys by URL — the exact
10
+ * environment this feature targets — would store that variant under the
11
+ * victim's key. Every subsequent legitimate navigation would then be served
12
+ * a payload rendered for someone else's segment tree or deployment.
13
+ *
14
+ * The guard is a *storability* rule, not a rejection: if the claimed key
15
+ * does not match the key derived from the received headers, the response
16
+ * loses its shared-cache directives. Mismatched requests are still served
17
+ * normally, they just cannot be stored for anyone else.
18
+ *
19
+ * Why not Next.js's 307 self-correction: a redirect is cacheable in its own
20
+ * right, costs a round trip on every legitimate miss, and would make the
21
+ * server's behaviour depend on a parameter it otherwise never reads. This
22
+ * rule closes the same hole by making the poisoned entry unstorable at the
23
+ * source, in the response the developer already has to opt into sharing.
24
+ *
25
+ * See design/19-client-navigation.md §"The `_rsc` cache key".
26
+ */
27
+
28
+ import { rscCacheKey, isRscCacheKeyShape, RSC_KEY_PARAM } from '../shared/rsc-cache-key.js';
29
+ import { RSC_CONTENT_TYPE } from '../shared/rsc-media-type.js';
30
+
31
+ /**
32
+ * Legacy shared-cache control fields that predate RFC 9213 and so do not
33
+ * follow the `<target>-Cache-Control` naming pattern — the one part of this
34
+ * rule that cannot be matched by shape. `X-Accel-Expires` is nginx
35
+ * `proxy_cache`, which gives it precedence over `Cache-Control`.
36
+ *
37
+ * A closed historical set, unlike the vendor `*-Cache-Control` names, which
38
+ * keep being minted and are handled by pattern below.
39
+ */
40
+ const LEGACY_CDN_CACHE_HEADERS = ['Surrogate-Control', 'Edge-Control', 'X-Accel-Expires'];
41
+
42
+ /**
43
+ * Whether a response header overrides `Cache-Control` at a shared cache.
44
+ *
45
+ * Matched by *shape*, not by vendor name. RFC 9213 defines targeted fields
46
+ * as `<target>-Cache-Control`, and every CDN spells its own: `CDN-`,
47
+ * `Cloudflare-CDN-`, `Netlify-CDN-`, `Vercel-CDN-`, and whichever ships
48
+ * next. A vendor list here is a denylist with an unbounded tail — it was
49
+ * extended twice during review, each time by one name, which is the signal
50
+ * that enumeration is the wrong shape. The suffix rule is total.
51
+ *
52
+ * `Cache-Control` itself does not match (no `-` prefix) and is replaced
53
+ * separately.
54
+ */
55
+ function isSharedCacheOverride(name: string): boolean {
56
+ const lowered = name.toLowerCase();
57
+ return (
58
+ lowered.endsWith('-cache-control') ||
59
+ LEGACY_CDN_CACHE_HEADERS.some((legacy) => legacy.toLowerCase() === lowered)
60
+ );
61
+ }
62
+
63
+ /**
64
+ * Whether the request's `_rsc` parameter matches the key derived from its
65
+ * own headers.
66
+ *
67
+ * Requests without the parameter are not claiming a key at all, so they are
68
+ * left alone — the bare document URL is the developer's declared territory
69
+ * (see design/32-response-caching.md, TIM-1264). Neither is a parameter
70
+ * whose value could not be one of our digests: `_rsc` is not reserved, and
71
+ * an application that already uses it for its own search state
72
+ * (`/search?_rsc=foo`) must not have its cache policy silently revoked by a
73
+ * guard that mistook that for a forgery.
74
+ */
75
+ export async function hasValidRscCacheKey(req: Request): Promise<boolean> {
76
+ // Fast path: this runs on every request, and the overwhelming majority
77
+ // (document requests) have no query string at all. Keyed on `?` and not
78
+ // on the literal `_rsc=`, because a substring test is not a parse: a
79
+ // request for `?%5Frsc=<key>` carries no literal `_rsc=`, yet
80
+ // `URLSearchParams` decodes it to one — and a cache that canonicalizes
81
+ // percent-encoded unreserved characters in its key while forwarding the
82
+ // original URL would store the response under the decoded, claimed key.
83
+ // The only sound fast path is one that cannot answer "no key" wrongly.
84
+ if (!req.url.includes('?')) return true;
85
+ let values: string[];
86
+ try {
87
+ values = new URL(req.url).searchParams.getAll(RSC_KEY_PARAM);
88
+ } catch {
89
+ // Unparseable URL — nothing to validate, and nothing safe to assume.
90
+ return false;
91
+ }
92
+ // Every value, not the first. An application that owns `_rsc` for its own
93
+ // search state makes the client emit `?_rsc=foo&_rsc=<key>` — reading only
94
+ // the first would see `foo`, exempt it as application data, and never
95
+ // check the real key sitting beside it.
96
+ const claims = values.filter(isRscCacheKeyShape);
97
+ if (claims.length === 0) return true;
98
+ const expected = await rscCacheKey((name) => req.headers.get(name));
99
+ // No digest available means the claim is unverifiable, which is not the
100
+ // same as verified. Every server runtime timber targets has
101
+ // `crypto.subtle`, so this is a guard against a future one that does not.
102
+ if (expected === null) return false;
103
+ // All of them, because at most one can be legitimate and a cache keys on
104
+ // the whole query string — a second, mismatched key would ride along
105
+ // inside an address that otherwise looks validated.
106
+ return claims.every((claim) => claim === expected);
107
+ }
108
+
109
+ /**
110
+ * Ensure a Flight payload can never be interpreted as a document.
111
+ *
112
+ * Applied here, at the pipeline's single exit, rather than where each
113
+ * payload response is built. Six places build one — the success path, three
114
+ * in `deny-renderer.ts`, the action paths — and hardening them one at a time
115
+ * is how you end up hardening five of six. The response is Flight if it says
116
+ * it is; that is the only test that covers every producer, including ones
117
+ * added later.
118
+ *
119
+ * Matters because the payload URL is observable and, on a shared-cache hit,
120
+ * the origin is never consulted — so a top-level navigation can be handed
121
+ * these bytes with no request-time check available. See design/13-security.md
122
+ * #36c.
123
+ */
124
+ export function hardenRscPayloadResponse(response: Response): void {
125
+ const contentType = response.headers.get('content-type');
126
+ if (!contentType) return;
127
+ if (contentType.split(';')[0].trim().toLowerCase() !== RSC_CONTENT_TYPE) return;
128
+ response.headers.set('X-Content-Type-Options', 'nosniff');
129
+ }
130
+
131
+ /**
132
+ * Make a response unstorable by any shared cache when its request claimed an
133
+ * `_rsc` key that does not match its own headers.
134
+ *
135
+ * The end state is unconditional and therefore statable: **a mismatched
136
+ * response is never stored by any cache.** Every targeted cache-control
137
+ * field is removed and `Cache-Control` is replaced outright, rather than
138
+ * downgrading only when a shared directive is spotted. Conditional variants
139
+ * of this were tried and were wrong twice over — they missed the CDN-
140
+ * specific headers, and leaving `Cache-Control` alone when it looked
141
+ * harmless lets a CDN apply heuristic caching to a response that has none.
142
+ *
143
+ * The cost is that a mismatched request loses developer-chosen *browser*
144
+ * caching too. That is a request no timber client ever makes.
145
+ *
146
+ * Purge tags (`Surrogate-Key`, `Cache-Tag`) are deliberately left alone:
147
+ * they address stored entries, and this response is not stored.
148
+ *
149
+ * Mutates `response.headers` in place; the pipeline guarantees mutable
150
+ * headers at this point (see TIM-866).
151
+ */
152
+ export async function enforceRscCacheKey(req: Request, response: Response): Promise<void> {
153
+ if (await hasValidRscCacheKey(req)) return;
154
+ // Collected before deleting — mutating Headers while iterating its live
155
+ // key iterator can skip entries.
156
+ const overrides: string[] = [];
157
+ for (const name of response.headers.keys()) {
158
+ if (isSharedCacheOverride(name)) overrides.push(name);
159
+ }
160
+ for (const name of overrides) {
161
+ response.headers.delete(name);
162
+ }
163
+ response.headers.set('Cache-Control', 'private, no-store');
164
+ }
@@ -11,6 +11,7 @@ import type { ManifestSegmentNode } from '../route-matcher.js';
11
11
  import { loadModule } from '../safe-load.js';
12
12
  import { DenySignal, RedirectSignal } from '../primitives.js';
13
13
  import { handleRouteRequest } from '../route-handler.js';
14
+ import { stripHeadBody } from '../head-response.js';
14
15
  import type { RouteModule } from '../route-handler.js';
15
16
  import type { RouteContext } from '../types.js';
16
17
 
@@ -89,9 +90,10 @@ export async function handleApiRoute(
89
90
  * method handler into the proper API response: DenySignal → JSON status-file
90
91
  * chain via `renderApiDeny`, RedirectSignal → bare 3xx with Location header.
91
92
  *
92
- * For HEAD requests, strip the deny body to preserve HEAD semantics — the
93
- * normal HEAD path in `handleRouteRequest` strips bodies after the handler
94
- * returns, but signals bypass that codepath.
93
+ * For HEAD requests, strip the deny body to preserve HEAD semantics — signals
94
+ * bypass the HEAD path in `handleRouteRequest`, so this exit needs its own
95
+ * call. Shares `stripHeadBody` with that path and with the pipeline exit; the
96
+ * pipeline applying it again downstream is a no-op (TIM-1324).
95
97
  *
96
98
  * Returns `null` for non-signal errors so the caller knows to rethrow.
97
99
  */
@@ -102,11 +104,7 @@ async function translateApiSignal(
102
104
  responseHeaders: Headers
103
105
  ): Promise<Response | null> {
104
106
  if (error instanceof DenySignal) {
105
- const res = await renderApiDeny(error, segments, responseHeaders);
106
- if (req.method.toUpperCase() === 'HEAD') {
107
- return new Response(null, { status: res.status, headers: res.headers });
108
- }
109
- return res;
107
+ return stripHeadBody(await renderApiDeny(error, segments, responseHeaders), req.method);
110
108
  }
111
109
  if (error instanceof RedirectSignal) {
112
110
  responseHeaders.set('Location', error.location);
@@ -17,7 +17,8 @@ import { logRenderError } from '../logger.js';
17
17
  import type { PipelineConfig, RouteMatch } from '../pipeline.js';
18
18
  import type { ManifestSegmentNode } from '../route-matcher.js';
19
19
  import { loadModule } from '../safe-load.js';
20
- import { createDebugChannelSink, isRscPayloadRequest } from './helpers.js';
20
+ import { createDebugChannelSink } from './helpers.js';
21
+ import { isRscRequest } from '../../shared/rsc-media-type.js';
21
22
  import { callSsr } from './ssr-bridge.js';
22
23
 
23
24
  /**
@@ -67,7 +68,7 @@ export function createDenyFallbackRenderer(options: {
67
68
  // decoder breaks navigation, so dispatch to the RSC renderer when the
68
69
  // request is an RSC payload request — mirroring the normal render path.
69
70
  // See design/19-client-navigation.md §"RSC Payload Handling" and TIM-823.
70
- if (isRscPayloadRequest(req)) {
71
+ if (isRscRequest(req)) {
71
72
  return renderDenyPageAsRsc(
72
73
  deny,
73
74
  chain,
@@ -17,14 +17,18 @@ import { renderToReadableStream } from '../../rsc-runtime/rsc.js';
17
17
 
18
18
  import type { RouteMatch } from '../pipeline.js';
19
19
  import { logRenderError } from '../logger.js';
20
+ import { appVisibleSearch } from '../../shared/rsc-cache-key.js';
21
+ import { getSlotParamsForClient } from '../request-context.js';
20
22
  import type { ManifestSegmentNode } from '../route-matcher.js';
21
23
  import { DenySignal, RenderError } from '../primitives.js';
22
24
  import type { ClientBootstrapConfig } from '../html-injectors.js';
23
25
  import { flightInitScript, htmlEscapeJsonString } from '../flight-scripts.js';
24
26
  import { renderDenyPage, renderDenyPageAsRsc } from '../deny-renderer.js';
25
- import type { LayoutEntry } from '../deny-renderer.js';
27
+ import type { DefaultPageRenderer, LayoutEntry } from '../deny-renderer.js';
26
28
  import type { NavContext } from '../ssr-bridge-types.js';
27
- import { createDebugChannelSink, isRscPayloadRequest } from './helpers.js';
29
+ import { createDebugChannelSink } from './helpers.js';
30
+ import { isRscRequest } from '../../shared/rsc-media-type.js';
31
+ import { withPublishedParams } from '../publish-params.js';
28
32
  import { getCookiesForSsr } from '../cookie-context.js';
29
33
  import { callSsr } from './ssr-bridge.js';
30
34
  import { teeWithErrorPropagation } from '../stream-utils.js';
@@ -34,6 +38,8 @@ import type { SerializableError } from '../../client/error-reconstituter.js';
34
38
  import { loadModule } from '../safe-load.js';
35
39
  import { sourceMapError } from '../dev-source-map.js';
36
40
  import { isMdxFilePath as isMdxFile } from '../utils/mdx-file.js';
41
+ import { defaultStatusPageResponse } from '../default-status-page.js';
42
+ import { rscErrorEnvelope } from '../../shared/rsc-error-envelope.js';
37
43
 
38
44
  /**
39
45
  * A manifest file reference with lazy import and path.
@@ -67,9 +73,12 @@ interface ErrorFileResolution {
67
73
  */
68
74
  async function resolveErrorFile(
69
75
  status: number,
70
- segments: ManifestSegmentNode[]
76
+ segments: ManifestSegmentNode[],
77
+ maxIndex?: number
71
78
  ): Promise<ErrorFileResolution | null> {
72
- for (let i = segments.length - 1; i >= 0; i--) {
79
+ const start =
80
+ maxIndex !== undefined ? Math.min(segments.length - 1, maxIndex) : segments.length - 1;
81
+ for (let i = start; i >= 0; i--) {
73
82
  const segment = segments[i];
74
83
 
75
84
  if (segment.statusFiles) {
@@ -126,9 +135,14 @@ async function resolveErrorFile(
126
135
  function getLayoutsForErrorFile(
127
136
  segments: ManifestSegmentNode[],
128
137
  layoutComponents: LayoutEntry[],
129
- foundSegmentIndex: number
138
+ foundSegmentIndex: number,
139
+ firstDeniedIndex = Infinity
130
140
  ): LayoutEntry[] {
131
- const resolvedSegments = new Set(segments.slice(0, foundSegmentIndex + 1));
141
+ // Clamp so a denied segment's layout never wraps the error page.
142
+ // The file resolution is unbounded (errors may use leaf files), but the
143
+ // layout wrapping must still respect the denial boundary. TIM-1358.
144
+ const ceiling = Math.min(foundSegmentIndex, firstDeniedIndex - 1);
145
+ const resolvedSegments = new Set(segments.slice(0, ceiling + 1));
132
146
  return layoutComponents.filter((lc) => resolvedSegments.has(lc.segment));
133
147
  }
134
148
 
@@ -155,6 +169,18 @@ function toSerializableError(error: unknown): SerializableError {
155
169
  * All error pages (TSX and MDX) go through RSC → SSR:
156
170
  * - TSX: ErrorReconstituter wrapper converts SerializableError → real Error
157
171
  * - MDX: Plain props ({ status }), no wrapper needed
172
+ *
173
+ * ...except for a client-navigation request, which gets the hard-navigate
174
+ * envelope instead. Every tier below produces an HTML document, and the
175
+ * client's Flight decoder is waiting for `text/x-component`; handing it HTML
176
+ * crashes the decode. The check lives here rather than at the call sites
177
+ * because it holds for all five of them, and the deny path's equivalent split
178
+ * (`renderDenyPage` / `renderDenyPageAsRsc`) had already been forgotten at one
179
+ * of this function's — the synchronous render-error exit in `render-route.ts`
180
+ * (TIM-1322). Four of the five are unreachable for an RSC request today
181
+ * (`render-route.ts` returns the payload before it reaches SSR, and
182
+ * `outcomeToResponse` answers RSC errors itself), so this fires on one path
183
+ * and guards the rest against drift.
158
184
  */
159
185
  export async function renderErrorPage(
160
186
  error: unknown,
@@ -165,14 +191,27 @@ export async function renderErrorPage(
165
191
  match: RouteMatch,
166
192
  responseHeaders: Headers,
167
193
  clientBootstrap: ClientBootstrapConfig,
168
- globalError?: GlobalErrorFile
194
+ globalError?: GlobalErrorFile,
195
+ firstDeniedIndex = Infinity
169
196
  ): Promise<Response> {
197
+ // The error itself is not logged here — every producer of a `renderError`
198
+ // signal already logged it at the point it was caught (`rsc-stream.ts`'s
199
+ // `onError`, `outcomeToResponse`), so a log on this exit would double up.
200
+ if (isRscRequest(req)) {
201
+ return rscErrorEnvelope(status, responseHeaders);
202
+ }
203
+
170
204
  // Source-map the error's stack trace in dev mode so the error page
171
205
  // shows original source positions instead of transpiled/bundled ones.
172
206
  // See TIM-811.
173
207
  sourceMapError(error);
174
- // Walk segments from leaf to root to find the error component
175
- const resolution = await resolveErrorFile(status, segments);
208
+ // Walk segments from leaf to root to find the error component.
209
+ // When firstDeniedIndex is finite, bound the walk so error files below
210
+ // the denied segment are not selected — matching the deny renderer's
211
+ // behavior. For plain errors (firstDeniedIndex = Infinity), the full
212
+ // chain is searched. See TIM-1358.
213
+ const errorMaxIndex = firstDeniedIndex < Infinity ? firstDeniedIndex : undefined;
214
+ const resolution = await resolveErrorFile(status, segments, errorMaxIndex);
176
215
 
177
216
  // Tier 2: No segment-level error file — try global-error.tsx
178
217
  if (!resolution && globalError) {
@@ -187,13 +226,22 @@ export async function renderErrorPage(
187
226
  );
188
227
  }
189
228
 
190
- // Tier 3: No error component found at any level — bare response
229
+ // Tier 3: No error component found at any level — framework default page.
230
+ // The deny chain's terminal is the same page, so an app with no error files
231
+ // gets a readable status page for both 4xx and 5xx rather than a blank
232
+ // document. It carries the status and nothing else — never the error
233
+ // (design/13-security.md principle 4). See TIM-1258.
191
234
  if (!resolution) {
192
- return new Response(null, { status, headers: responseHeaders });
235
+ return defaultStatusPageResponse(status, responseHeaders);
193
236
  }
194
237
 
195
238
  const { component: errorComponent, segmentIndex, isMdx } = resolution;
196
- const layoutsToWrap = getLayoutsForErrorFile(segments, layoutComponents, segmentIndex);
239
+ const layoutsToWrap = getLayoutsForErrorFile(
240
+ segments,
241
+ layoutComponents,
242
+ segmentIndex,
243
+ firstDeniedIndex
244
+ );
197
245
 
198
246
  return renderErrorPageViaRsc(
199
247
  error,
@@ -257,9 +305,12 @@ async function renderErrorPageViaRsc(
257
305
  element = h(component, null, element);
258
306
  }
259
307
 
260
- // Render through RSC → SSR (same path as deny pages)
308
+ // Render through RSC → SSR (same path as deny pages) — including the params
309
+ // wrap, which every payload the server sends needs. An error page on a
310
+ // dynamic route reads params like any other page, and SSR would get them
311
+ // from the request ALS while the hydrated client got an empty record.
261
312
  try {
262
- const rscStream = renderToReadableStream(element, {
313
+ const rscStream = renderToReadableStream(withPublishedParams(element), {
263
314
  signal: req.signal,
264
315
  onError(err: unknown) {
265
316
  logRenderError({ method: req.method, path: url.pathname, error: err });
@@ -269,32 +320,35 @@ async function renderErrorPageViaRsc(
269
320
 
270
321
  const [ssrStream, inlineStream] = teeWithErrorPropagation(rscStream);
271
322
 
272
- // Build params bootstrap script so useSegmentParams() works on hydration.
273
- // Without this, error pages on dynamic routes hydrate with empty params.
274
- const paramsScript =
275
- Object.keys(match.segmentParams).length === 0
276
- ? ''
277
- : `<script>self.__timber_params=${htmlEscapeJsonString(JSON.stringify(match.segmentParams))}</script>`;
323
+ // Params ride the payload root, beside the tree, so there is no
324
+ // bootstrap script. Read here for the SSR-side ALS context below.
325
+ const slotParams = getSlotParamsForClient();
278
326
 
279
327
  const navContext: NavContext = {
280
328
  pathname: url.pathname,
281
329
  segmentParams: match.segmentParams,
282
- searchParams: Object.fromEntries(url.searchParams),
330
+ searchParams: Object.fromEntries(appVisibleSearch(url).params),
331
+ slotParams,
283
332
  statusCode: status,
284
333
  responseHeaders,
285
- headHtml: paramsScript + flightInitScript(),
334
+ headHtml: flightInitScript(),
286
335
  bootstrapScriptContent: clientBootstrap.bootstrapScriptContent,
287
336
  rscStream: inlineStream,
288
337
  signal: req.signal,
289
338
  cookies: getCookiesForSsr(),
290
339
  };
291
340
 
292
- return callSsr(ssrStream, navContext);
341
+ // `await` inside the try on purpose: SSR of the error page fails
342
+ // asynchronously far more often than synchronously, and returning the
343
+ // promise bare let every such rejection escape this guard.
344
+ return await callSsr(ssrStream, navContext);
293
345
  } catch (renderError) {
294
- // Error page itself failed to render — fall through to bare response.
295
- // This guards against recursive errors in user error page code.
346
+ // The error page itself failed to render — guards against recursive
347
+ // errors in user error page code. Terminates in the framework default
348
+ // page, not a blank body: a crashing error page is precisely when the
349
+ // reader has nothing else to go on (TIM-1258).
296
350
  logRenderError({ method: req.method, path: url.pathname, error: renderError });
297
- return new Response(null, { status, headers: responseHeaders });
351
+ return defaultStatusPageResponse(status, responseHeaders);
298
352
  }
299
353
  }
300
354
 
@@ -315,11 +369,12 @@ async function renderGlobalErrorPage(
315
369
  responseHeaders: Headers,
316
370
  clientBootstrap: ClientBootstrapConfig
317
371
  ): Promise<Response> {
318
- // .catch: if global-error.tsx fails to load, fall through to bare response.
319
- // This is the last tier — no further recovery. See TIM-584.
372
+ // .catch: if global-error.tsx fails to load or exports no component, it is
373
+ // not a usable Tier 2 match — fall through to the framework default page
374
+ // rather than a blank body. See TIM-584, TIM-1258.
320
375
  const mod = await loadModule(globalError).catch(() => null);
321
376
  if (!mod?.default) {
322
- return new Response(null, { status, headers: responseHeaders });
377
+ return defaultStatusPageResponse(status, responseHeaders);
323
378
  }
324
379
 
325
380
  const component = mod.default as (...args: unknown[]) => unknown;
@@ -350,15 +405,18 @@ export async function renderNoMatchPage(
350
405
  req: Request,
351
406
  rootSegment: ManifestSegmentNode,
352
407
  responseHeaders: Headers,
353
- clientBootstrap: ClientBootstrapConfig
408
+ clientBootstrap: ClientBootstrapConfig,
409
+ defaultPage?: DefaultPageRenderer
354
410
  ): Promise<Response> {
355
411
  const segments = [rootSegment];
356
412
 
357
- // Load root layout if present
413
+ // Load root layout if present. A layout that fails to import or evaluate
414
+ // must not take the 404 down with it — render the status page unwrapped,
415
+ // as renderFallbackError does for the same reason.
358
416
  const layoutComponents: LayoutEntry[] = [];
359
417
  if (rootSegment.layout) {
360
- const mod = await loadModule(rootSegment.layout);
361
- if (mod.default) {
418
+ const mod = await loadModule(rootSegment.layout).catch(() => null);
419
+ if (mod?.default) {
362
420
  layoutComponents.push({
363
421
  component: mod.default as (...args: unknown[]) => unknown,
364
422
  segment: rootSegment,
@@ -369,7 +427,7 @@ export async function renderNoMatchPage(
369
427
  const deny = new DenySignal(404);
370
428
  const match: RouteMatch = { segments, segmentParams: {}, middlewareChain: [] };
371
429
 
372
- if (isRscPayloadRequest(req)) {
430
+ if (isRscRequest(req)) {
373
431
  return renderDenyPageAsRsc(
374
432
  deny,
375
433
  segments,
@@ -389,6 +447,7 @@ export async function renderNoMatchPage(
389
447
  responseHeaders,
390
448
  clientBootstrap,
391
449
  createDebugChannelSink,
392
- callSsr
450
+ callSsr,
451
+ defaultPage
393
452
  );
394
453
  }