@timber-js/app 0.2.0-alpha.184 → 0.2.0-alpha.186

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 (441) hide show
  1. package/dist/_chunks/{actions-O_LsyCE4.js → actions-C-Rw9vPc.js} +3 -3
  2. package/dist/_chunks/{actions-O_LsyCE4.js.map → actions-C-Rw9vPc.js.map} +1 -1
  3. package/dist/_chunks/als-slots-mFweg276.js +27 -0
  4. package/dist/_chunks/als-slots-mFweg276.js.map +1 -0
  5. package/dist/_chunks/base-path-DaQrzbez.js +80 -0
  6. package/dist/_chunks/base-path-DaQrzbez.js.map +1 -0
  7. package/dist/_chunks/build-manifest-DWppEdLB.js +86 -0
  8. package/dist/_chunks/build-manifest-DWppEdLB.js.map +1 -0
  9. package/dist/_chunks/build-output-helper-C3DrfzZR.js +369 -0
  10. package/dist/_chunks/build-output-helper-C3DrfzZR.js.map +1 -0
  11. package/dist/_chunks/{cache-api-B-lhk9p4.js → cache-api-Cd0VZ_Pd.js} +3 -3
  12. package/dist/_chunks/{cache-api-B-lhk9p4.js.map → cache-api-Cd0VZ_Pd.js.map} +1 -1
  13. package/dist/_chunks/cli-schema-sync-CKgHC2MB.js +1857 -0
  14. package/dist/_chunks/cli-schema-sync-CKgHC2MB.js.map +1 -0
  15. package/dist/_chunks/{cloudflare-CnT5Lr7U.js → cloudflare-Cs0uZXea.js} +89 -12
  16. package/dist/_chunks/{cloudflare-CnT5Lr7U.js.map → cloudflare-Cs0uZXea.js.map} +1 -1
  17. package/dist/_chunks/error-boundary-DpYRI_I1.js +330 -0
  18. package/dist/_chunks/error-boundary-DpYRI_I1.js.map +1 -0
  19. package/dist/_chunks/{fast-hash-D6hIVt1Y.js → fast-hash-C-tVYrLb.js} +6 -1
  20. package/dist/_chunks/fast-hash-C-tVYrLb.js.map +1 -0
  21. package/dist/_chunks/fs-identity-D7vb6GdI.js +208 -0
  22. package/dist/_chunks/fs-identity-D7vb6GdI.js.map +1 -0
  23. package/dist/_chunks/{logger-AWfuX-KJ.js → logger-N7e5auP0.js} +9 -13
  24. package/dist/_chunks/logger-N7e5auP0.js.map +1 -0
  25. package/dist/_chunks/navigation-root-B29qg0_T.js +274 -0
  26. package/dist/_chunks/navigation-root-B29qg0_T.js.map +1 -0
  27. package/dist/_chunks/param-value-C8TNYchQ.js +66 -0
  28. package/dist/_chunks/param-value-C8TNYchQ.js.map +1 -0
  29. package/dist/_chunks/{plugin-context---kTF5v8.js → plugin-context-DEGLSJs3.js} +2 -1
  30. package/dist/_chunks/plugin-context-DEGLSJs3.js.map +1 -0
  31. package/dist/_chunks/{router-ref-CtmF-aPv.js → router-ref-DuYuV_0Q.js} +2 -2
  32. package/dist/_chunks/router-ref-DuYuV_0Q.js.map +1 -0
  33. package/dist/_chunks/rsc-cache-key-DD0fl_-s.js +243 -0
  34. package/dist/_chunks/rsc-cache-key-DD0fl_-s.js.map +1 -0
  35. package/dist/_chunks/rsc-error-envelope-tT5PJs4q.js +40 -0
  36. package/dist/_chunks/rsc-error-envelope-tT5PJs4q.js.map +1 -0
  37. package/dist/_chunks/rsc-payload-path-B_LBodc2.js +48 -0
  38. package/dist/_chunks/rsc-payload-path-B_LBodc2.js.map +1 -0
  39. package/dist/_chunks/{segment-classify-Byy425ng.js → segment-classify-C539Pa2O.js} +23 -2
  40. package/dist/_chunks/segment-classify-C539Pa2O.js.map +1 -0
  41. package/dist/_chunks/{canonicalize-DQHyFClh.js → segment-keys-BawYuNFO.js} +61 -2
  42. package/dist/_chunks/segment-keys-BawYuNFO.js.map +1 -0
  43. package/dist/_chunks/slot-params-BCTmZkQB.js +76 -0
  44. package/dist/_chunks/slot-params-BCTmZkQB.js.map +1 -0
  45. package/dist/_chunks/{ssr-data-BOWsq18U.js → ssr-data-14MXm7Pj.js} +17 -2
  46. package/dist/_chunks/ssr-data-14MXm7Pj.js.map +1 -0
  47. package/dist/_chunks/use-segment-params-ClyUNq4d.js +128 -0
  48. package/dist/_chunks/use-segment-params-ClyUNq4d.js.map +1 -0
  49. package/dist/_chunks/{walkers-BtTlKviE.js → walkers-BhhwI9TD.js} +21 -87
  50. package/dist/_chunks/walkers-BhhwI9TD.js.map +1 -0
  51. package/dist/adapters/build-output-helper.d.ts +37 -0
  52. package/dist/adapters/build-output-helper.d.ts.map +1 -1
  53. package/dist/adapters/cloudflare-dev.js +1 -1
  54. package/dist/adapters/cloudflare-kv-cache.js +1 -1
  55. package/dist/adapters/cloudflare.d.ts.map +1 -1
  56. package/dist/adapters/cloudflare.js +1 -1
  57. package/dist/adapters/fs-identity.d.ts +64 -0
  58. package/dist/adapters/fs-identity.d.ts.map +1 -0
  59. package/dist/adapters/nitro.d.ts.map +1 -1
  60. package/dist/adapters/nitro.js +2 -3
  61. package/dist/adapters/nitro.js.map +1 -1
  62. package/dist/adapters/shared.d.ts +57 -1
  63. package/dist/adapters/shared.d.ts.map +1 -1
  64. package/dist/adapters/types.d.ts +13 -1
  65. package/dist/adapters/types.d.ts.map +1 -1
  66. package/dist/cache/fast-hash.d.ts +5 -0
  67. package/dist/cache/fast-hash.d.ts.map +1 -1
  68. package/dist/cache/index.js +1 -1
  69. package/dist/cli.js +2 -2
  70. package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -1
  71. package/dist/client/browser-entry/hydrate.d.ts +20 -9
  72. package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
  73. package/dist/client/browser-entry/post-hydration.d.ts +4 -1
  74. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
  75. package/dist/client/browser-entry/router-init.d.ts +3 -0
  76. package/dist/client/browser-entry/router-init.d.ts.map +1 -1
  77. package/dist/client/browser-entry/rsc-stream.d.ts +8 -0
  78. package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -1
  79. package/dist/client/error-boundary.d.ts +1 -0
  80. package/dist/client/error-boundary.d.ts.map +1 -1
  81. package/dist/client/error-boundary.js +1 -162
  82. package/dist/client/history.d.ts +13 -6
  83. package/dist/client/history.d.ts.map +1 -1
  84. package/dist/client/index.js +18 -11
  85. package/dist/client/index.js.map +1 -1
  86. package/dist/client/internal.d.ts +4 -3
  87. package/dist/client/internal.d.ts.map +1 -1
  88. package/dist/client/internal.js +872 -305
  89. package/dist/client/internal.js.map +1 -1
  90. package/dist/client/link.d.ts.map +1 -1
  91. package/dist/client/navigation-api.d.ts +2 -2
  92. package/dist/client/navigation-commit.d.ts +71 -0
  93. package/dist/client/navigation-commit.d.ts.map +1 -0
  94. package/dist/client/navigation-context.d.ts +37 -20
  95. package/dist/client/navigation-context.d.ts.map +1 -1
  96. package/dist/client/navigation-root.d.ts +63 -23
  97. package/dist/client/navigation-root.d.ts.map +1 -1
  98. package/dist/client/params-context.d.ts +81 -0
  99. package/dist/client/params-context.d.ts.map +1 -0
  100. package/dist/client/router-effects.d.ts +129 -0
  101. package/dist/client/router-effects.d.ts.map +1 -0
  102. package/dist/client/router-lifecycle.d.ts +55 -0
  103. package/dist/client/router-lifecycle.d.ts.map +1 -0
  104. package/dist/client/router-pipeline.d.ts +84 -0
  105. package/dist/client/router-pipeline.d.ts.map +1 -0
  106. package/dist/client/router-ref.d.ts +1 -1
  107. package/dist/client/router-ref.d.ts.map +1 -1
  108. package/dist/client/router-skew.d.ts +21 -0
  109. package/dist/client/router-skew.d.ts.map +1 -0
  110. package/dist/client/router-types.d.ts +207 -0
  111. package/dist/client/router-types.d.ts.map +1 -0
  112. package/dist/client/router.d.ts +1 -182
  113. package/dist/client/router.d.ts.map +1 -1
  114. package/dist/client/rsc-fetch.d.ts +39 -16
  115. package/dist/client/rsc-fetch.d.ts.map +1 -1
  116. package/dist/client/segment-cache.d.ts +79 -34
  117. package/dist/client/segment-cache.d.ts.map +1 -1
  118. package/dist/client/ssr-data.d.ts +6 -0
  119. package/dist/client/ssr-data.d.ts.map +1 -1
  120. package/dist/client/stale-client.d.ts +69 -0
  121. package/dist/client/stale-client.d.ts.map +1 -0
  122. package/dist/client/state.d.ts +10 -1
  123. package/dist/client/state.d.ts.map +1 -1
  124. package/dist/client/top-loader.d.ts +2 -1
  125. package/dist/client/top-loader.d.ts.map +1 -1
  126. package/dist/client/unload-guard.d.ts +5 -0
  127. package/dist/client/unload-guard.d.ts.map +1 -1
  128. package/dist/client/use-cookie.d.ts.map +1 -1
  129. package/dist/client/use-router.d.ts +1 -1
  130. package/dist/client/use-segment-params.d.ts +18 -8
  131. package/dist/client/use-segment-params.d.ts.map +1 -1
  132. package/dist/cookies/index.js +3 -3
  133. package/dist/cookies/index.js.map +1 -1
  134. package/dist/index.d.ts.map +1 -1
  135. package/dist/index.js +915 -320
  136. package/dist/index.js.map +1 -1
  137. package/dist/plugin-context.d.ts +33 -1
  138. package/dist/plugin-context.d.ts.map +1 -1
  139. package/dist/plugins/adapter-build.d.ts.map +1 -1
  140. package/dist/plugins/build-manifest.d.ts +1 -1
  141. package/dist/plugins/build-manifest.d.ts.map +1 -1
  142. package/dist/plugins/entries.d.ts.map +1 -1
  143. package/dist/plugins/fonts.d.ts.map +1 -1
  144. package/dist/plugins/request-dep/analysis.d.ts +106 -0
  145. package/dist/plugins/request-dep/analysis.d.ts.map +1 -0
  146. package/dist/plugins/request-dep/index.d.ts +75 -0
  147. package/dist/plugins/request-dep/index.d.ts.map +1 -0
  148. package/dist/plugins/request-dep/origin.d.ts +61 -0
  149. package/dist/plugins/request-dep/origin.d.ts.map +1 -0
  150. package/dist/plugins/routing.d.ts +9 -0
  151. package/dist/plugins/routing.d.ts.map +1 -1
  152. package/dist/plugins/static-build.d.ts +149 -8
  153. package/dist/plugins/static-build.d.ts.map +1 -1
  154. package/dist/routing/codegen-types.d.ts +18 -0
  155. package/dist/routing/codegen-types.d.ts.map +1 -1
  156. package/dist/routing/codegen.d.ts.map +1 -1
  157. package/dist/routing/collision-probe.d.ts +104 -0
  158. package/dist/routing/collision-probe.d.ts.map +1 -0
  159. package/dist/routing/collision-spaces.d.ts +26 -0
  160. package/dist/routing/collision-spaces.d.ts.map +1 -0
  161. package/dist/routing/index.js +3 -3
  162. package/dist/routing/interception.d.ts +235 -6
  163. package/dist/routing/interception.d.ts.map +1 -1
  164. package/dist/routing/manifest-codegen.d.ts.map +1 -1
  165. package/dist/routing/scanner.d.ts.map +1 -1
  166. package/dist/routing/segment-classify.d.ts +33 -0
  167. package/dist/routing/segment-classify.d.ts.map +1 -1
  168. package/dist/routing/segment-keys.d.ts +95 -0
  169. package/dist/routing/segment-keys.d.ts.map +1 -0
  170. package/dist/routing/slot-placement.d.ts +48 -0
  171. package/dist/routing/slot-placement.d.ts.map +1 -0
  172. package/dist/routing/types.d.ts +1 -3
  173. package/dist/routing/types.d.ts.map +1 -1
  174. package/dist/routing/walkers.d.ts +6 -0
  175. package/dist/routing/walkers.d.ts.map +1 -1
  176. package/dist/search-params/define.d.ts +0 -6
  177. package/dist/search-params/define.d.ts.map +1 -1
  178. package/dist/search-params/index.js +176 -2
  179. package/dist/search-params/index.js.map +1 -1
  180. package/dist/segment-params/define.d.ts +0 -6
  181. package/dist/segment-params/define.d.ts.map +1 -1
  182. package/dist/segment-params/index.js +69 -1
  183. package/dist/segment-params/index.js.map +1 -0
  184. package/dist/server/actions.d.ts +6 -3
  185. package/dist/server/actions.d.ts.map +1 -1
  186. package/dist/server/als-registry.d.ts +2 -1
  187. package/dist/server/als-registry.d.ts.map +1 -1
  188. package/dist/server/build-manifest.d.ts +13 -1
  189. package/dist/server/build-manifest.d.ts.map +1 -1
  190. package/dist/server/chain-url-parts.d.ts +82 -0
  191. package/dist/server/chain-url-parts.d.ts.map +1 -0
  192. package/dist/server/children-interception.d.ts +49 -0
  193. package/dist/server/children-interception.d.ts.map +1 -0
  194. package/dist/server/default-status-page.d.ts +45 -0
  195. package/dist/server/default-status-page.d.ts.map +1 -0
  196. package/dist/server/deny-boundary.d.ts +4 -7
  197. package/dist/server/deny-boundary.d.ts.map +1 -1
  198. package/dist/server/deny-renderer.d.ts +14 -3
  199. package/dist/server/deny-renderer.d.ts.map +1 -1
  200. package/dist/server/head-response.d.ts +35 -0
  201. package/dist/server/head-response.d.ts.map +1 -0
  202. package/dist/server/index.js +2 -2
  203. package/dist/server/internal.js +528 -373
  204. package/dist/server/internal.js.map +1 -1
  205. package/dist/server/param-coercion.d.ts +10 -1
  206. package/dist/server/param-coercion.d.ts.map +1 -1
  207. package/dist/server/pipeline-helpers.d.ts +0 -18
  208. package/dist/server/pipeline-helpers.d.ts.map +1 -1
  209. package/dist/server/pipeline-interception.d.ts +59 -9
  210. package/dist/server/pipeline-interception.d.ts.map +1 -1
  211. package/dist/server/pipeline-outcome.d.ts.map +1 -1
  212. package/dist/server/pipeline-phases.d.ts.map +1 -1
  213. package/dist/server/pipeline.d.ts +11 -0
  214. package/dist/server/pipeline.d.ts.map +1 -1
  215. package/dist/server/prebuilt-builder.d.ts.map +1 -1
  216. package/dist/server/primitives.d.ts +0 -12
  217. package/dist/server/primitives.d.ts.map +1 -1
  218. package/dist/server/publish-params.d.ts +33 -0
  219. package/dist/server/publish-params.d.ts.map +1 -0
  220. package/dist/server/request-context.d.ts +48 -0
  221. package/dist/server/request-context.d.ts.map +1 -1
  222. package/dist/server/route-element-builder.d.ts +32 -2
  223. package/dist/server/route-element-builder.d.ts.map +1 -1
  224. package/dist/server/route-handler.d.ts.map +1 -1
  225. package/dist/server/route-matcher.d.ts +13 -0
  226. package/dist/server/route-matcher.d.ts.map +1 -1
  227. package/dist/server/rsc-cache-key-guard.d.ts +78 -0
  228. package/dist/server/rsc-cache-key-guard.d.ts.map +1 -0
  229. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
  230. package/dist/server/rsc-entry/error-renderer.d.ts +14 -2
  231. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  232. package/dist/server/rsc-entry/helpers.d.ts +2 -10
  233. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  234. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  235. package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
  236. package/dist/server/rsc-entry/rsc-payload.d.ts +3 -2
  237. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  238. package/dist/server/rsc-entry/rsc-stream.d.ts +3 -2
  239. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  240. package/dist/server/rsc-entry/ssr-renderer.d.ts +2 -0
  241. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  242. package/dist/server/sitemap-generator.d.ts.map +1 -1
  243. package/dist/server/skippable-prefix.d.ts +23 -0
  244. package/dist/server/skippable-prefix.d.ts.map +1 -0
  245. package/dist/server/slot-interception.d.ts +80 -0
  246. package/dist/server/slot-interception.d.ts.map +1 -0
  247. package/dist/server/slot-resolver.d.ts +70 -8
  248. package/dist/server/slot-resolver.d.ts.map +1 -1
  249. package/dist/server/ssr-bridge-types.d.ts +6 -0
  250. package/dist/server/ssr-bridge-types.d.ts.map +1 -1
  251. package/dist/server/ssr-entry.d.ts.map +1 -1
  252. package/dist/server/ssr-wrappers.d.ts +24 -13
  253. package/dist/server/ssr-wrappers.d.ts.map +1 -1
  254. package/dist/server/state-tree-diff.d.ts +6 -27
  255. package/dist/server/state-tree-diff.d.ts.map +1 -1
  256. package/dist/server/static-generator.d.ts +6 -0
  257. package/dist/server/static-generator.d.ts.map +1 -1
  258. package/dist/server/static-not-found.d.ts +52 -0
  259. package/dist/server/static-not-found.d.ts.map +1 -0
  260. package/dist/server/status-code-resolver.d.ts +3 -10
  261. package/dist/server/status-code-resolver.d.ts.map +1 -1
  262. package/dist/shared/als-slots.d.ts +41 -0
  263. package/dist/shared/als-slots.d.ts.map +1 -0
  264. package/dist/shared/base-path.d.ts +56 -0
  265. package/dist/shared/base-path.d.ts.map +1 -0
  266. package/dist/shared/param-value.d.ts +80 -0
  267. package/dist/shared/param-value.d.ts.map +1 -0
  268. package/dist/shared/payload-root.d.ts +108 -0
  269. package/dist/shared/payload-root.d.ts.map +1 -0
  270. package/dist/shared/rsc-cache-key.d.ts +163 -0
  271. package/dist/shared/rsc-cache-key.d.ts.map +1 -0
  272. package/dist/shared/rsc-error-envelope.d.ts +25 -0
  273. package/dist/shared/rsc-error-envelope.d.ts.map +1 -0
  274. package/dist/shared/rsc-manifest.d.ts +28 -0
  275. package/dist/shared/rsc-manifest.d.ts.map +1 -0
  276. package/dist/shared/rsc-payload-path.d.ts +42 -0
  277. package/dist/shared/rsc-payload-path.d.ts.map +1 -0
  278. package/dist/shared/segment-info.d.ts +59 -0
  279. package/dist/shared/segment-info.d.ts.map +1 -0
  280. package/dist/shared/slot-params.d.ts +51 -0
  281. package/dist/shared/slot-params.d.ts.map +1 -0
  282. package/dist/shared/static-platform-files.d.ts +71 -0
  283. package/dist/shared/static-platform-files.d.ts.map +1 -0
  284. package/dist/shims/navigation-client.d.ts +8 -19
  285. package/dist/shims/navigation-client.d.ts.map +1 -1
  286. package/dist/shims/navigation.d.ts +0 -1
  287. package/dist/shims/navigation.d.ts.map +1 -1
  288. package/docs/api/31-api-client.mdx +4 -2
  289. package/docs/learn/02-pages-and-layouts.mdx +1 -1
  290. package/docs/learn/11-error-handling.mdx +4 -3
  291. package/docs/learn/12-client-navigation.mdx +2 -2
  292. package/docs/learn/14-deploying.mdx +23 -0
  293. package/docs/more/01-advanced-routing.mdx +172 -2
  294. package/docs/more/04-metadata-and-fonts.mdx +1 -1
  295. package/package.json +2 -2
  296. package/src/adapters/build-output-helper.ts +116 -3
  297. package/src/adapters/cloudflare.ts +86 -11
  298. package/src/adapters/fs-identity.ts +156 -0
  299. package/src/adapters/nitro.ts +6 -6
  300. package/src/adapters/shared.ts +196 -21
  301. package/src/adapters/types.ts +13 -1
  302. package/src/cache/fast-hash.ts +5 -0
  303. package/src/client/browser-entry/action-dispatch.ts +9 -4
  304. package/src/client/browser-entry/hydrate.ts +40 -29
  305. package/src/client/browser-entry/index.ts +15 -1
  306. package/src/client/browser-entry/post-hydration.ts +10 -9
  307. package/src/client/browser-entry/router-init.ts +104 -48
  308. package/src/client/browser-entry/rsc-stream.ts +11 -2
  309. package/src/client/error-boundary.tsx +20 -15
  310. package/src/client/history.ts +13 -6
  311. package/src/client/internal.ts +4 -3
  312. package/src/client/link.tsx +55 -8
  313. package/src/client/navigation-api.ts +3 -3
  314. package/src/client/navigation-commit.ts +174 -0
  315. package/src/client/navigation-context.ts +40 -67
  316. package/src/client/navigation-root.tsx +326 -101
  317. package/src/client/params-context.ts +192 -0
  318. package/src/client/router-effects.ts +231 -0
  319. package/src/client/router-lifecycle.ts +288 -0
  320. package/src/client/router-pipeline.ts +389 -0
  321. package/src/client/router-ref.ts +1 -1
  322. package/src/client/router-skew.ts +30 -0
  323. package/src/client/router-types.ts +230 -0
  324. package/src/client/router.ts +242 -690
  325. package/src/client/rsc-fetch.ts +180 -122
  326. package/src/client/segment-cache.ts +121 -44
  327. package/src/client/ssr-data.ts +6 -0
  328. package/src/client/stale-client.ts +191 -0
  329. package/src/client/state.ts +14 -1
  330. package/src/client/top-loader.tsx +2 -1
  331. package/src/client/unload-guard.ts +34 -2
  332. package/src/client/use-cookie.ts +9 -2
  333. package/src/client/use-router.ts +1 -1
  334. package/src/client/use-segment-params.ts +41 -18
  335. package/src/index.ts +5 -1
  336. package/src/plugin-context.ts +34 -1
  337. package/src/plugins/adapter-build.ts +4 -0
  338. package/src/plugins/build-manifest.ts +34 -13
  339. package/src/plugins/entries.ts +4 -0
  340. package/src/plugins/fonts.ts +11 -8
  341. package/src/plugins/request-dep/analysis.ts +447 -0
  342. package/src/plugins/request-dep/index.ts +376 -0
  343. package/src/plugins/request-dep/origin.ts +310 -0
  344. package/src/plugins/routing.ts +60 -3
  345. package/src/plugins/static-build.ts +333 -63
  346. package/src/routing/codegen-types.ts +18 -0
  347. package/src/routing/codegen.ts +50 -10
  348. package/src/routing/collision-probe.ts +438 -0
  349. package/src/routing/collision-spaces.ts +281 -0
  350. package/src/routing/interception.ts +896 -50
  351. package/src/routing/manifest-codegen.ts +6 -9
  352. package/src/routing/scanner.ts +114 -113
  353. package/src/routing/segment-classify.ts +57 -0
  354. package/src/routing/segment-keys.ts +146 -0
  355. package/src/routing/slot-placement.ts +97 -0
  356. package/src/routing/types.ts +1 -3
  357. package/src/routing/walkers.ts +8 -0
  358. package/src/search-params/define.ts +6 -29
  359. package/src/segment-params/define.ts +4 -27
  360. package/src/server/action-handler.ts +3 -1
  361. package/src/server/actions.ts +6 -3
  362. package/src/server/als-registry.ts +2 -1
  363. package/src/server/build-manifest.ts +14 -1
  364. package/src/server/chain-url-parts.ts +163 -0
  365. package/src/server/children-interception.ts +115 -0
  366. package/src/server/default-status-page.ts +127 -0
  367. package/src/server/deny-boundary.ts +5 -31
  368. package/src/server/deny-renderer.ts +83 -22
  369. package/src/server/head-response.ts +54 -0
  370. package/src/server/param-coercion.ts +48 -18
  371. package/src/server/pipeline-helpers.ts +0 -45
  372. package/src/server/pipeline-interception.ts +81 -32
  373. package/src/server/pipeline-outcome.ts +3 -5
  374. package/src/server/pipeline-phases.ts +68 -16
  375. package/src/server/pipeline.ts +57 -10
  376. package/src/server/prebuilt-builder.ts +1 -7
  377. package/src/server/prebuilt-runtime.ts +4 -4
  378. package/src/server/primitives.ts +0 -18
  379. package/src/server/publish-params.ts +42 -0
  380. package/src/server/request-context.ts +97 -18
  381. package/src/server/route-element-builder.ts +89 -98
  382. package/src/server/route-handler.ts +8 -8
  383. package/src/server/route-matcher.ts +21 -9
  384. package/src/server/rsc-cache-key-guard.ts +170 -0
  385. package/src/server/rsc-entry/api-handler.ts +6 -8
  386. package/src/server/rsc-entry/error-renderer.ts +64 -25
  387. package/src/server/rsc-entry/helpers.ts +31 -16
  388. package/src/server/rsc-entry/index.ts +36 -28
  389. package/src/server/rsc-entry/render-route.ts +12 -7
  390. package/src/server/rsc-entry/revalidate-renderer.ts +1 -1
  391. package/src/server/rsc-entry/rsc-payload.ts +14 -25
  392. package/src/server/rsc-entry/rsc-stream.ts +4 -3
  393. package/src/server/rsc-entry/ssr-renderer.ts +13 -9
  394. package/src/server/sitemap-generator.ts +1 -7
  395. package/src/server/skippable-prefix.ts +92 -0
  396. package/src/server/slot-interception.ts +168 -0
  397. package/src/server/slot-resolver.ts +253 -156
  398. package/src/server/ssr-bridge-types.ts +6 -0
  399. package/src/server/ssr-entry.ts +12 -4
  400. package/src/server/ssr-wrappers.tsx +33 -32
  401. package/src/server/state-tree-diff.ts +14 -55
  402. package/src/server/static-generator.ts +67 -45
  403. package/src/server/static-not-found.ts +134 -0
  404. package/src/server/status-code-resolver.ts +4 -47
  405. package/src/shared/als-slots.ts +72 -0
  406. package/src/shared/base-path.ts +80 -0
  407. package/src/shared/param-value.ts +244 -0
  408. package/src/shared/payload-root.ts +151 -0
  409. package/src/shared/rsc-cache-key.ts +269 -0
  410. package/src/shared/rsc-error-envelope.ts +31 -0
  411. package/src/shared/rsc-manifest.ts +29 -0
  412. package/src/shared/rsc-payload-path.ts +45 -0
  413. package/src/shared/segment-info.ts +60 -0
  414. package/src/shared/slot-params.ts +111 -0
  415. package/src/shared/static-platform-files.ts +75 -0
  416. package/src/shims/navigation-client.ts +8 -26
  417. package/src/shims/navigation.ts +0 -4
  418. package/dist/_chunks/build-output-helper-DOGYFb_X.js +0 -196
  419. package/dist/_chunks/build-output-helper-DOGYFb_X.js.map +0 -1
  420. package/dist/_chunks/canonicalize-DQHyFClh.js.map +0 -1
  421. package/dist/_chunks/cli-schema-sync-D3naS3eH.js +0 -688
  422. package/dist/_chunks/cli-schema-sync-D3naS3eH.js.map +0 -1
  423. package/dist/_chunks/define-COtkxMRT.js +0 -191
  424. package/dist/_chunks/define-COtkxMRT.js.map +0 -1
  425. package/dist/_chunks/define-c4au4I9R.js +0 -82
  426. package/dist/_chunks/define-c4au4I9R.js.map +0 -1
  427. package/dist/_chunks/fast-hash-D6hIVt1Y.js.map +0 -1
  428. package/dist/_chunks/logger-AWfuX-KJ.js.map +0 -1
  429. package/dist/_chunks/navigation-root-bUDZLw74.js +0 -124
  430. package/dist/_chunks/navigation-root-bUDZLw74.js.map +0 -1
  431. package/dist/_chunks/plugin-context---kTF5v8.js.map +0 -1
  432. package/dist/_chunks/router-ref-CtmF-aPv.js.map +0 -1
  433. package/dist/_chunks/segment-classify-Byy425ng.js.map +0 -1
  434. package/dist/_chunks/ssr-data-BOWsq18U.js.map +0 -1
  435. package/dist/_chunks/use-segment-params-D1CzlgBo.js +0 -151
  436. package/dist/_chunks/use-segment-params-D1CzlgBo.js.map +0 -1
  437. package/dist/_chunks/walkers-BtTlKviE.js.map +0 -1
  438. package/dist/client/error-boundary.js.map +0 -1
  439. package/dist/plugins/request-dep.d.ts +0 -32
  440. package/dist/plugins/request-dep.d.ts.map +0 -1
  441. package/src/plugins/request-dep.ts +0 -499
@@ -17,14 +17,17 @@ 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
29
  import { createDebugChannelSink, isRscPayloadRequest } from './helpers.js';
30
+ import { withPublishedParams } from '../publish-params.js';
28
31
  import { getCookiesForSsr } from '../cookie-context.js';
29
32
  import { callSsr } from './ssr-bridge.js';
30
33
  import { teeWithErrorPropagation } from '../stream-utils.js';
@@ -34,6 +37,8 @@ import type { SerializableError } from '../../client/error-reconstituter.js';
34
37
  import { loadModule } from '../safe-load.js';
35
38
  import { sourceMapError } from '../dev-source-map.js';
36
39
  import { isMdxFilePath as isMdxFile } from '../utils/mdx-file.js';
40
+ import { defaultStatusPageResponse } from '../default-status-page.js';
41
+ import { rscErrorEnvelope } from '../../shared/rsc-error-envelope.js';
37
42
 
38
43
  /**
39
44
  * A manifest file reference with lazy import and path.
@@ -155,6 +160,18 @@ function toSerializableError(error: unknown): SerializableError {
155
160
  * All error pages (TSX and MDX) go through RSC → SSR:
156
161
  * - TSX: ErrorReconstituter wrapper converts SerializableError → real Error
157
162
  * - MDX: Plain props ({ status }), no wrapper needed
163
+ *
164
+ * ...except for a client-navigation request, which gets the hard-navigate
165
+ * envelope instead. Every tier below produces an HTML document, and the
166
+ * client's Flight decoder is waiting for `text/x-component`; handing it HTML
167
+ * crashes the decode. The check lives here rather than at the call sites
168
+ * because it holds for all five of them, and the deny path's equivalent split
169
+ * (`renderDenyPage` / `renderDenyPageAsRsc`) had already been forgotten at one
170
+ * of this function's — the synchronous render-error exit in `render-route.ts`
171
+ * (TIM-1322). Four of the five are unreachable for an RSC request today
172
+ * (`render-route.ts` returns the payload before it reaches SSR, and
173
+ * `outcomeToResponse` answers RSC errors itself), so this fires on one path
174
+ * and guards the rest against drift.
158
175
  */
159
176
  export async function renderErrorPage(
160
177
  error: unknown,
@@ -167,6 +184,13 @@ export async function renderErrorPage(
167
184
  clientBootstrap: ClientBootstrapConfig,
168
185
  globalError?: GlobalErrorFile
169
186
  ): Promise<Response> {
187
+ // The error itself is not logged here — every producer of a `renderError`
188
+ // signal already logged it at the point it was caught (`rsc-stream.ts`'s
189
+ // `onError`, `outcomeToResponse`), so a log on this exit would double up.
190
+ if (isRscPayloadRequest(req)) {
191
+ return rscErrorEnvelope(status, responseHeaders);
192
+ }
193
+
170
194
  // Source-map the error's stack trace in dev mode so the error page
171
195
  // shows original source positions instead of transpiled/bundled ones.
172
196
  // See TIM-811.
@@ -187,9 +211,13 @@ export async function renderErrorPage(
187
211
  );
188
212
  }
189
213
 
190
- // Tier 3: No error component found at any level — bare response
214
+ // Tier 3: No error component found at any level — framework default page.
215
+ // The deny chain's terminal is the same page, so an app with no error files
216
+ // gets a readable status page for both 4xx and 5xx rather than a blank
217
+ // document. It carries the status and nothing else — never the error
218
+ // (design/13-security.md principle 4). See TIM-1258.
191
219
  if (!resolution) {
192
- return new Response(null, { status, headers: responseHeaders });
220
+ return defaultStatusPageResponse(status, responseHeaders);
193
221
  }
194
222
 
195
223
  const { component: errorComponent, segmentIndex, isMdx } = resolution;
@@ -257,9 +285,12 @@ async function renderErrorPageViaRsc(
257
285
  element = h(component, null, element);
258
286
  }
259
287
 
260
- // Render through RSC → SSR (same path as deny pages)
288
+ // Render through RSC → SSR (same path as deny pages) — including the params
289
+ // wrap, which every payload the server sends needs. An error page on a
290
+ // dynamic route reads params like any other page, and SSR would get them
291
+ // from the request ALS while the hydrated client got an empty record.
261
292
  try {
262
- const rscStream = renderToReadableStream(element, {
293
+ const rscStream = renderToReadableStream(withPublishedParams(element), {
263
294
  signal: req.signal,
264
295
  onError(err: unknown) {
265
296
  logRenderError({ method: req.method, path: url.pathname, error: err });
@@ -269,32 +300,35 @@ async function renderErrorPageViaRsc(
269
300
 
270
301
  const [ssrStream, inlineStream] = teeWithErrorPropagation(rscStream);
271
302
 
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>`;
303
+ // Params ride the payload root, beside the tree, so there is no
304
+ // bootstrap script. Read here for the SSR-side ALS context below.
305
+ const slotParams = getSlotParamsForClient();
278
306
 
279
307
  const navContext: NavContext = {
280
308
  pathname: url.pathname,
281
309
  segmentParams: match.segmentParams,
282
- searchParams: Object.fromEntries(url.searchParams),
310
+ searchParams: Object.fromEntries(appVisibleSearch(url).params),
311
+ slotParams,
283
312
  statusCode: status,
284
313
  responseHeaders,
285
- headHtml: paramsScript + flightInitScript(),
314
+ headHtml: flightInitScript(),
286
315
  bootstrapScriptContent: clientBootstrap.bootstrapScriptContent,
287
316
  rscStream: inlineStream,
288
317
  signal: req.signal,
289
318
  cookies: getCookiesForSsr(),
290
319
  };
291
320
 
292
- return callSsr(ssrStream, navContext);
321
+ // `await` inside the try on purpose: SSR of the error page fails
322
+ // asynchronously far more often than synchronously, and returning the
323
+ // promise bare let every such rejection escape this guard.
324
+ return await callSsr(ssrStream, navContext);
293
325
  } 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.
326
+ // The error page itself failed to render — guards against recursive
327
+ // errors in user error page code. Terminates in the framework default
328
+ // page, not a blank body: a crashing error page is precisely when the
329
+ // reader has nothing else to go on (TIM-1258).
296
330
  logRenderError({ method: req.method, path: url.pathname, error: renderError });
297
- return new Response(null, { status, headers: responseHeaders });
331
+ return defaultStatusPageResponse(status, responseHeaders);
298
332
  }
299
333
  }
300
334
 
@@ -315,11 +349,12 @@ async function renderGlobalErrorPage(
315
349
  responseHeaders: Headers,
316
350
  clientBootstrap: ClientBootstrapConfig
317
351
  ): 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.
352
+ // .catch: if global-error.tsx fails to load or exports no component, it is
353
+ // not a usable Tier 2 match — fall through to the framework default page
354
+ // rather than a blank body. See TIM-584, TIM-1258.
320
355
  const mod = await loadModule(globalError).catch(() => null);
321
356
  if (!mod?.default) {
322
- return new Response(null, { status, headers: responseHeaders });
357
+ return defaultStatusPageResponse(status, responseHeaders);
323
358
  }
324
359
 
325
360
  const component = mod.default as (...args: unknown[]) => unknown;
@@ -350,15 +385,18 @@ export async function renderNoMatchPage(
350
385
  req: Request,
351
386
  rootSegment: ManifestSegmentNode,
352
387
  responseHeaders: Headers,
353
- clientBootstrap: ClientBootstrapConfig
388
+ clientBootstrap: ClientBootstrapConfig,
389
+ defaultPage?: DefaultPageRenderer
354
390
  ): Promise<Response> {
355
391
  const segments = [rootSegment];
356
392
 
357
- // Load root layout if present
393
+ // Load root layout if present. A layout that fails to import or evaluate
394
+ // must not take the 404 down with it — render the status page unwrapped,
395
+ // as renderFallbackError does for the same reason.
358
396
  const layoutComponents: LayoutEntry[] = [];
359
397
  if (rootSegment.layout) {
360
- const mod = await loadModule(rootSegment.layout);
361
- if (mod.default) {
398
+ const mod = await loadModule(rootSegment.layout).catch(() => null);
399
+ if (mod?.default) {
362
400
  layoutComponents.push({
363
401
  component: mod.default as (...args: unknown[]) => unknown,
364
402
  segment: rootSegment,
@@ -389,6 +427,7 @@ export async function renderNoMatchPage(
389
427
  responseHeaders,
390
428
  clientBootstrap,
391
429
  createDebugChannelSink,
392
- callSsr
430
+ callSsr,
431
+ defaultPage
393
432
  );
394
433
  }
@@ -6,7 +6,8 @@
6
6
 
7
7
  import type { ManifestSegmentNode } from '../route-matcher.js';
8
8
  import { swallow } from '../logger.js';
9
- import { computeSegmentKeys } from '../state-tree-diff.js';
9
+ import { computeSegmentKeys, computeSegmentTreePaths } from '../../routing/segment-keys.js';
10
+ import type { SegmentInfo } from '../../shared/segment-info.js';
10
11
  import type { SlotSkipEntry } from '../slot-resolver.js';
11
12
  import { isClientReference } from '../route-element-builder.js';
12
13
  import { isStaticRequestDependent } from '../request-dep.js';
@@ -180,16 +181,6 @@ export function parseDebugRows(text: string): DebugComponentEntry[] {
180
181
  *
181
182
  * See design/07-routing.md §"Server Diffing Rules".
182
183
  */
183
- interface SegmentInfoEntry {
184
- path: string;
185
- segmentId?: string;
186
- isRequestDependent: boolean;
187
- slot?: boolean;
188
- parentSegment?: string;
189
- denied?: boolean;
190
- skipped?: boolean;
191
- }
192
-
193
184
  export function buildSegmentInfo(
194
185
  segments: ManifestSegmentNode[],
195
186
  layoutComponents: Array<{
@@ -197,17 +188,32 @@ export function buildSegmentInfo(
197
188
  segment: ManifestSegmentNode;
198
189
  }>,
199
190
  slotSkipInfo?: SlotSkipEntry[],
200
- skippedSegmentKeys?: string[]
201
- ): SegmentInfoEntry[] {
191
+ skippedSegmentKeys?: string[],
192
+ firstDeniedIndex = Infinity
193
+ ): SegmentInfo[] {
202
194
  const layoutBySegment = new Map(
203
195
  layoutComponents.map(({ component, segment }) => [segment, component])
204
196
  );
205
197
 
206
198
  const segmentKeys = computeSegmentKeys(segments);
199
+ // The scope identity interception compares against. Emitted for every
200
+ // layout-bearing segment, which is exactly the set that can own a scope:
201
+ // slots are only resolved for segments whose layout renders
202
+ // (`route-element-builder.ts`), so a segment with no layout can never hold
203
+ // an intercepting slot. See TIM-1282.
204
+ const treePaths = computeSegmentTreePaths(segments);
207
205
  const skippedSet = skippedSegmentKeys ? new Set(skippedSegmentKeys) : null;
208
- const result: SegmentInfoEntry[] = [];
206
+ const result: SegmentInfo[] = [];
209
207
 
210
208
  for (let i = 0; i < segments.length; i++) {
209
+ // A denied segment's AccessGate renders the deny page in place of the
210
+ // layout, so this segment and everything below it never mounted. Their
211
+ // layouts were still loaded — `layoutComponents` holds every module the
212
+ // chain pulled in, not every component that rendered — so without this
213
+ // the client is told it has a tree it does not, and `treePaths` reports a
214
+ // slot owner whose slot was never resolved (TIM-1282).
215
+ if (i >= firstDeniedIndex) break;
216
+
211
217
  const segment = segments[i];
212
218
  const component = layoutBySegment.get(segment);
213
219
 
@@ -225,8 +231,9 @@ export function buildSegmentInfo(
225
231
  isRequestDependent = true;
226
232
  }
227
233
 
228
- const entry: SegmentInfoEntry = {
234
+ const entry: SegmentInfo = {
229
235
  path: segment.urlPath,
236
+ treePath: treePaths[i],
230
237
  isRequestDependent,
231
238
  };
232
239
  if (segmentId !== segment.urlPath) {
@@ -237,7 +244,7 @@ export function buildSegmentInfo(
237
244
 
238
245
  if (slotSkipInfo) {
239
246
  for (const slot of slotSkipInfo) {
240
- const entry: SegmentInfoEntry = {
247
+ const entry: SegmentInfo = {
241
248
  path: slot.slotKey,
242
249
  isRequestDependent: slot.isRequestDependent,
243
250
  slot: true,
@@ -270,3 +277,11 @@ export { isAbortError } from '../render-utils.js';
270
277
 
271
278
  // escapeHtml is now imported from server/utils/escape-html.ts
272
279
  export { escapeHtml } from '../utils/escape-html.js';
280
+
281
+ // Params no longer travel beside the payload. The payload root is the
282
+ // outermost node of every tree the server sends (`server/publish-params.ts`),
283
+ // so the record arrives as props React Flight serializes — which is what
284
+ // makes a `Date`- or `bigint`-valued codec result survive the trip, where
285
+ // `JSON.stringify` flattened it or threw. The response headers, the inline
286
+ // bootstrap scripts, and the two build-time manifest fields that used to
287
+ // carry it are all gone. See TIM-1294, design/41-global-params.md.
@@ -54,6 +54,7 @@ import type { InterceptionContext, PipelineConfig, RouteMatch } from '../pipelin
54
54
  import { createPipeline } from '../pipeline.js';
55
55
  import { coerceSegmentParams, setGlobalSchemaCodecs } from '../param-coercion.js';
56
56
  import { createMetadataRouteMatcher, createRouteMatcher } from '../route-matcher.js';
57
+ import { createInterceptedRouteMatcher } from '../children-interception.js';
57
58
  import { initDevTracing } from '../tracing.js';
58
59
  import { createRevalidateRendererFactory } from './revalidate-renderer.js';
59
60
 
@@ -307,38 +308,45 @@ async function createRequestHandler(manifest: typeof routeManifest, runtimeConfi
307
308
  );
308
309
  },
309
310
  renderNoMatch: async (req: Request, responseHeaders: Headers) => {
310
- const response = await renderNoMatchPage(
311
- req,
312
- manifest.root,
313
- responseHeaders,
314
- clientBootstrap
315
- );
316
-
317
- // In dev mode, if the pipeline returned a bare 404 (no body — meaning
318
- // no user-defined 404.tsx was found), replace it with a helpful dev
319
- // page that lists available routes and suggests similar paths.
320
- // Only for GET requests that accept HTML — HEAD/API/RSC clients
321
- // should receive the bare 404 without an HTML body (TIM-793).
311
+ // In dev mode, when the app authored no 404 page, substitute a helpful
312
+ // page listing available routes and suggesting similar paths in place
313
+ // of the framework default. Only for GET requests that accept HTML —
314
+ // HEAD/API/RSC clients must not receive an HTML body (TIM-793).
315
+ //
316
+ // This is passed *into* the chain rather than applied to its result:
317
+ // the old version detected "no user-defined 404.tsx" by testing the
318
+ // response for an empty body, which stopped being true once the chain
319
+ // grew a real terminal (TIM-1258).
322
320
  const acceptsHtml = (req.headers.get('accept') ?? '').includes('text/html');
323
- if (isDev && !response.body && req.method === 'GET' && acceptsHtml) {
324
- const { generateDev404Page, collectRoutes } = await import('../../dev-tools/404-page.js');
325
- const routes = collectRoutes(manifest.root);
326
- const pathname = new URL(req.url).pathname;
327
- // devHmrOptions is set by the dev server after this module is
328
- // imported (read at request time, not closure-capture time).
329
- const html = generateDev404Page(pathname, routes, pipelineConfig.devHmrOptions);
330
- return new Response(html, {
331
- status: 404,
332
- headers: {
333
- ...Object.fromEntries(responseHeaders.entries()),
334
- 'content-type': 'text/html; charset=utf-8',
335
- },
336
- });
337
- }
321
+ const wantsDev404 = isDev && req.method === 'GET' && acceptsHtml;
322
+
323
+ // The dev page is loaded up front: the chain's terminal is synchronous,
324
+ // and dev-tools must stay out of the production bundle (dynamic import).
325
+ const devPage = wantsDev404
326
+ ? await import('../../dev-tools/404-page.js').catch(() => null)
327
+ : null;
328
+
329
+ const defaultPage = devPage
330
+ ? (_status: number, headers: Headers) => {
331
+ const routes = devPage.collectRoutes(manifest.root);
332
+ const pathname = new URL(req.url).pathname;
333
+ // devHmrOptions is set by the dev server after this module is
334
+ // imported (read at request time, not closure-capture time).
335
+ const html = devPage.generateDev404Page(pathname, routes, pipelineConfig.devHmrOptions);
336
+ return new Response(html, {
337
+ status: 404,
338
+ headers: {
339
+ ...Object.fromEntries(headers.entries()),
340
+ 'content-type': 'text/html; charset=utf-8',
341
+ },
342
+ });
343
+ }
344
+ : undefined;
338
345
 
339
- return response;
346
+ return renderNoMatchPage(req, manifest.root, responseHeaders, clientBootstrap, defaultPage);
340
347
  },
341
348
  interceptionRewrites: manifest.interceptionRewrites,
349
+ matchInterceptedRoute: createInterceptedRouteMatcher(manifest.root),
342
350
  // Slow request threshold from timber.config.ts. Default 3000ms, 0 to disable.
343
351
  // See design/17-logging.md §"slowRequestMs"
344
352
  slowRequestMs: (runtimeConfig as Record<string, unknown>).slowRequestMs as number | undefined,
@@ -115,10 +115,10 @@ export async function renderRoute(
115
115
  buildManifest.metadataRouteHashes
116
116
  );
117
117
  } catch (error) {
118
- // Param coercion failed — render the custom 404 page (status files / not-found).
118
+ // Param coercion failed — render the custom 404 page via status files.
119
119
  // Previously returned a bare Response(null, { status: 404 }) which bypassed
120
- // custom not-found pages. Now routes through renderNoMatchPage so apps with
121
- // 404.tsx / not-found status files render their custom page.
120
+ // custom 404 pages. Now routes through renderNoMatchPage so apps with
121
+ // 404.tsx render their custom page.
122
122
  if (error instanceof ParamCoercionError) {
123
123
  return renderNoMatchPage(req, rootSegment, responseHeaders, clientBootstrap);
124
124
  }
@@ -140,12 +140,13 @@ export async function renderRoute(
140
140
  });
141
141
 
142
142
  const {
143
- element,
143
+ payload,
144
144
  layoutComponents,
145
145
  deferSuspenseFor,
146
146
  skippedSegments,
147
147
  shellSettled,
148
148
  slotSkipInfo,
149
+ firstDeniedIndex,
149
150
  } = routeResult;
150
151
 
151
152
  // Build head HTML for injection into the SSR output.
@@ -199,7 +200,7 @@ export async function renderRoute(
199
200
 
200
201
  // Render to RSC Flight stream with signal tracking.
201
202
  const _rscStart = performance.now();
202
- const { rscStream, signals, getDebugComponents } = renderRscStream(element, req);
203
+ const { rscStream, signals, getDebugComponents } = renderRscStream(payload, req);
203
204
  if (shellSettled) signals.shellSettled = shellSettled;
204
205
 
205
206
  // Store the debug components getter in ALS so onPipelineError can
@@ -250,6 +251,9 @@ export async function renderRoute(
250
251
  // Synchronous render error — renderToReadableStream threw before
251
252
  // creating the stream. Render the error page with correct 5xx status.
252
253
  // (Async render errors are tracked in onError and handled after SSR.)
254
+ // An RSC payload request gets the hard-navigate envelope rather than an
255
+ // error page — `renderErrorPage` owns that branch, so every one of its call
256
+ // sites is covered rather than this one alone (TIM-1322).
253
257
  if (signals.renderError && !rscStream) {
254
258
  return renderErrorPage(
255
259
  signals.renderError.error,
@@ -274,10 +278,10 @@ export async function renderRoute(
274
278
  signals,
275
279
  segments,
276
280
  layoutComponents,
277
- match,
278
281
  responseHeaders,
279
282
  skippedSegments,
280
- slotSkipInfo
283
+ slotSkipInfo,
284
+ firstDeniedIndex
281
285
  );
282
286
  }
283
287
 
@@ -296,5 +300,6 @@ export async function renderRoute(
296
300
  deferSuspenseFor,
297
301
  globalError,
298
302
  slotSkipInfo,
303
+ firstDeniedIndex,
299
304
  });
300
305
  }
@@ -342,7 +342,7 @@ export function createRevalidateRendererFactory(
342
342
  deps.buildManifest.metadataRouteHashes
343
343
  );
344
344
  return {
345
- element: routeResult.element,
345
+ payload: routeResult.payload,
346
346
  };
347
347
  };
348
348
  };
@@ -10,7 +10,6 @@
10
10
 
11
11
  import type { LayoutEntry } from '../deny-renderer.js';
12
12
  import { renderDenyPageAsRsc } from '../deny-renderer.js';
13
- import type { RouteMatch } from '../pipeline.js';
14
13
  import type { RedirectSignal } from '../primitives.js';
15
14
  import type { LayoutComponentEntry } from '../route-element-builder.js';
16
15
  import type { ManifestSegmentNode } from '../route-matcher.js';
@@ -23,6 +22,8 @@ import {
23
22
  RSC_CONTENT_TYPE,
24
23
  } from './helpers.js';
25
24
 
25
+ import { rscErrorEnvelope } from '../../shared/rsc-error-envelope.js';
26
+
26
27
  import type { RenderSignals } from './rsc-stream.js';
27
28
 
28
29
  /**
@@ -40,10 +41,11 @@ export async function buildRscPayloadResponse(
40
41
  signals: RenderSignals,
41
42
  segments: ManifestSegmentNode[],
42
43
  layoutComponents: LayoutComponentEntry[],
43
- match: RouteMatch,
44
44
  responseHeaders: Headers,
45
45
  skippedSegments?: string[],
46
- slotSkipInfo?: SlotSkipEntry[]
46
+ slotSkipInfo?: SlotSkipEntry[],
47
+ /** Index of the first denied segment — see RouteElementResult.firstDeniedIndex. */
48
+ firstDeniedIndex = Infinity
47
49
  ): Promise<Response> {
48
50
  // Read the first chunk from the RSC stream before committing headers.
49
51
  // Race the first read against signal detection — if an async component
@@ -82,12 +84,7 @@ export async function buildRscPayloadResponse(
82
84
  // SSR-rendered error page. See design/10-error-handling.md.
83
85
  reader.cancel().catch(() => {});
84
86
  signals.onSignal = undefined;
85
- responseHeaders.set('X-Timber-Error', '1');
86
- responseHeaders.set('content-type', 'application/json; charset=utf-8');
87
- return new Response(JSON.stringify({ error: true, status: 500 }), {
88
- status: 500,
89
- headers: responseHeaders,
90
- });
87
+ return rscErrorEnvelope(500, responseHeaders);
91
88
  }
92
89
 
93
90
  // If data arrived first, still check signals — they may have fired
@@ -143,15 +140,7 @@ export async function buildRscPayloadResponse(
143
140
  // §"Error Page Rendering for Client Navigation".
144
141
  if (signals.renderError) {
145
142
  reader.cancel().catch(() => {});
146
- responseHeaders.set('X-Timber-Error', '1');
147
- responseHeaders.set('content-type', 'application/json; charset=utf-8');
148
- return new Response(
149
- JSON.stringify({
150
- error: true,
151
- status: signals.renderError.status,
152
- }),
153
- { status: signals.renderError.status, headers: responseHeaders }
154
- );
143
+ return rscErrorEnvelope(signals.renderError.status, responseHeaders);
155
144
  }
156
145
 
157
146
  // Extract the first chunk from the race result.
@@ -200,7 +189,13 @@ export async function buildRscPayloadResponse(
200
189
  // for state tree diffing on subsequent navigations. Request-dependency
201
190
  // is determined by static module-graph analysis (not runtime taint).
202
191
  // See design/19-client-navigation.md §"X-Timber-State-Tree Header"
203
- const segmentInfo = buildSegmentInfo(segments, layoutComponents, slotSkipInfo, skippedSegments);
192
+ const segmentInfo = buildSegmentInfo(
193
+ segments,
194
+ layoutComponents,
195
+ slotSkipInfo,
196
+ skippedSegments,
197
+ firstDeniedIndex
198
+ );
204
199
  responseHeaders.set('X-Timber-Segments', JSON.stringify(segmentInfo));
205
200
 
206
201
  // Send skipped segments so the client can merge the partial RSC payload
@@ -209,12 +204,6 @@ export async function buildRscPayloadResponse(
209
204
  responseHeaders.set('X-Timber-Skipped-Segments', JSON.stringify(skippedSegments));
210
205
  }
211
206
 
212
- // Send route params so the client can populate useSegmentParams() after
213
- // SPA navigation. Without this, useSegmentParams() returns {}.
214
- if (Object.keys(match.segmentParams).length > 0) {
215
- responseHeaders.set('X-Timber-Params', JSON.stringify(match.segmentParams));
216
- }
217
-
218
207
  // Check if a DenySignal was caught in-tree by AccessGate or
219
208
  // PageDenyBoundary. The deny page is in the Flight stream as a normal
220
209
  // element — use the deny status for the HTTP response. See TIM-666.
@@ -11,6 +11,7 @@
11
11
  */
12
12
 
13
13
  import { renderToReadableStream } from '../../rsc-runtime/rsc.js';
14
+ import type { RscPayloadRoot } from '../../shared/payload-root.js';
14
15
 
15
16
  import { randomUUID } from 'node:crypto';
16
17
 
@@ -74,7 +75,7 @@ export interface RscStreamResult {
74
75
  }
75
76
 
76
77
  /**
77
- * Render a React element tree to an RSC Flight stream.
78
+ * Render a payload root (tree plus its params) to an RSC Flight stream.
78
79
  *
79
80
  * The stream serializes server components as rendered output and client
80
81
  * components ("use client") as serialized references with module ID + export name.
@@ -84,7 +85,7 @@ export interface RscStreamResult {
84
85
  * fires onError during stream consumption. Signals are captured in the
85
86
  * returned `signals` object for the caller to handle.
86
87
  */
87
- export function renderRscStream(element: React.ReactElement, req: Request): RscStreamResult {
88
+ export function renderRscStream(payload: RscPayloadRoot, req: Request): RscStreamResult {
88
89
  const signals: RenderSignals = {
89
90
  denySignal: null,
90
91
  redirectSignal: null,
@@ -100,7 +101,7 @@ export function renderRscStream(element: React.ReactElement, req: Request): RscS
100
101
 
101
102
  try {
102
103
  rscStream = renderToReadableStream(
103
- element,
104
+ payload,
104
105
  {
105
106
  signal: req.signal,
106
107
  onError(error: unknown) {
@@ -14,6 +14,8 @@
14
14
 
15
15
  import type { ClientBootstrapConfig } from '../html-injectors.js';
16
16
  import { flightInitScript } from '../flight-scripts.js';
17
+ import { appVisibleSearch } from '../../shared/rsc-cache-key.js';
18
+ import { getSlotParamsForClient } from '../request-context.js';
17
19
  import type { LayoutEntry } from '../deny-renderer.js';
18
20
  import { renderDenyPage } from '../deny-renderer.js';
19
21
  import type { RouteMatch } from '../pipeline.js';
@@ -102,6 +104,8 @@ interface SsrRenderOptions {
102
104
  /** Tier 2 global-error.tsx file, if present in app/. */
103
105
  globalError?: GlobalErrorFile;
104
106
  slotSkipInfo?: SlotSkipEntry[];
107
+ /** Index of the first denied segment — see RouteElementResult.firstDeniedIndex. */
108
+ firstDeniedIndex?: number;
105
109
  }
106
110
 
107
111
  /**
@@ -170,26 +174,26 @@ export async function renderSsrResponse(opts: SsrRenderOptions): Promise<Respons
170
174
  // Skipped when client JS is disabled — no client JS to consume it.
171
175
  const segmentScript = clientJsDisabled
172
176
  ? ''
173
- : `<script>self.__timber_segments=${htmlEscapeJsonString(JSON.stringify(buildSegmentInfo(segments, layoutComponents, opts.slotSkipInfo)))}</script>`;
177
+ : `<script>self.__timber_segments=${htmlEscapeJsonString(JSON.stringify(buildSegmentInfo(segments, layoutComponents, opts.slotSkipInfo, undefined, opts.firstDeniedIndex)))}</script>`;
174
178
 
175
- // Embed route params in HTML so useSegmentParams() works on initial hydration.
176
- // Without this, useSegmentParams() returns {} until the first client navigation.
177
- const paramsScript =
178
- clientJsDisabled || Object.keys(match.segmentParams).length === 0
179
- ? ''
180
- : `<script>self.__timber_params=${htmlEscapeJsonString(JSON.stringify(match.segmentParams))}</script>`;
179
+ // Params reach the browser inside the payload — a field on the root row of
180
+ // the same Flight stream that is inlined for hydration. SSR itself renders
181
+ // only the tree, and the client components it renders read params from the
182
+ // ALS context seeded below, which is also where server components answer
183
+ // `getSegmentParams(slotPath)` from.
184
+ const slotParams = getSlotParamsForClient();
181
185
 
182
186
  const navContext: NavContext = {
183
187
  pathname: new URL(req.url).pathname,
184
188
  segmentParams: match.segmentParams,
185
- searchParams: Object.fromEntries(new URL(req.url).searchParams),
189
+ searchParams: Object.fromEntries(appVisibleSearch(new URL(req.url)).params),
190
+ slotParams,
186
191
  statusCode: 200,
187
192
  responseHeaders,
188
193
  headHtml:
189
194
  headHtml +
190
195
  clientBootstrap.preloadLinks +
191
196
  segmentScript +
192
- paramsScript +
193
197
  // Initialize __timber_f in <head> so it exists before any streaming
194
198
  // chunk scripts arrive in <body>. See flight-scripts.ts, LOCAL-415.
195
199
  (clientJsDisabled ? '' : flightInitScript()),
@@ -187,13 +187,7 @@ export async function resolveSitemapUrls(
187
187
  // Dynamic route — try to call generateStaticSegmentParams
188
188
  try {
189
189
  const mod = await loadModule(route.pageLoader);
190
- let generateFn = mod.generateStaticSegmentParams;
191
- if (typeof generateFn !== 'function' && typeof mod.generateStaticParams === 'function') {
192
- generateFn = mod.generateStaticParams;
193
- console.warn(
194
- `[timber] ${route.pageLoader.filePath}: generateStaticParams is deprecated — rename to generateStaticSegmentParams`
195
- );
196
- }
190
+ const generateFn = mod.generateStaticSegmentParams;
197
191
  if (typeof generateFn !== 'function') {
198
192
  // No generateStaticSegmentParams export — skip this dynamic route
199
193
  continue;