@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
@@ -11,14 +11,40 @@
11
11
  * See design/19-client-navigation.md §"RSC Payload Handling"
12
12
  */
13
13
 
14
- import type { SegmentInfo } from './segment-cache';
15
- import type { RouterDeps } from './router';
14
+ import {
15
+ rscCacheKey,
16
+ recordLookup,
17
+ randomRscCacheKey,
18
+ RSC_KEY_PARAM,
19
+ } from '../shared/rsc-cache-key.js';
20
+ import type { RscManifest, RscManifestEntry } from '../shared/rsc-manifest.js';
21
+ import {
22
+ splitPayloadRoot,
23
+ readPublishedParams,
24
+ type PublishedParams,
25
+ } from '../shared/payload-root.js';
26
+ import { normalizeBasePath, stripBasePath, withBasePath } from '../shared/base-path.js';
27
+ import { rscPayloadPath } from '../shared/rsc-payload-path.js';
28
+ import type { SegmentInfo, StateTree } from '../shared/segment-info.js';
29
+ import { TIMBER_ERROR_HEADER } from '../shared/rsc-error-envelope.js';
30
+ import type { RouterDeps } from './router-types.js';
16
31
 
17
32
  // ─── Types ───────────────────────────────────────────────────────
18
33
 
19
34
  /** Result of fetching an RSC payload — includes segment metadata. */
20
35
  export interface FetchResult {
36
+ /** The renderable tree, split off the payload root. */
21
37
  payload: unknown;
38
+ /**
39
+ * The params the same payload published beside that tree.
40
+ *
41
+ * Not a separate channel: both fields come out of one `splitPayloadRoot`
42
+ * call on one decoded root, so a response cannot deliver a tree and another
43
+ * route's params. They used to arrive on their own header, which is how a
44
+ * navigation could render one route and publish another's (TIM-1285,
45
+ * TIM-1294).
46
+ */
47
+ params: PublishedParams | Promise<PublishedParams>;
22
48
  /**
23
49
  * Promise that settles when the RSC decode completes or fails.
24
50
  * The payload thenable is NOT awaited before returning (for streaming),
@@ -29,8 +55,6 @@ export interface FetchResult {
29
55
  decodePromise: Promise<void> | null;
30
56
  /** Segment metadata from X-Timber-Segments header for populating the segment cache. */
31
57
  segmentInfo: SegmentInfo[] | null;
32
- /** Route params from X-Timber-Params header for populating useSegmentParams(). */
33
- params: Record<string, string | string[]> | null;
34
58
  /** Segment paths that were skipped by the server (for client-side merging). */
35
59
  skippedSegments: string[] | null;
36
60
  }
@@ -42,31 +66,39 @@ export const RSC_CONTENT_TYPE = 'text/x-component';
42
66
  // ─── URL Helpers ─────────────────────────────────────────────────
43
67
 
44
68
  /**
45
- * Generate a short random cache-busting ID (5 chars, a-z0-9).
46
- * Matches the format Next.js uses for _rsc params.
47
- */
48
- function generateCacheBustId(): string {
49
- const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
50
- let id = '';
51
- for (let i = 0; i < 5; i++) {
52
- id += chars[(Math.random() * 36) | 0];
53
- }
54
- return id;
55
- }
56
-
57
- /**
58
- * Append a `_rsc=<id>` query parameter to the URL.
59
- * Follows Next.js's pattern — prevents CDN/browser from serving cached HTML
60
- * for RSC navigation requests and signals that this is an RSC fetch.
69
+ * Append a `_rsc=<key>` query parameter to the URL (TIM-1268).
70
+ *
71
+ * The key is a deterministic hash of the RSC request headers, so identical
72
+ * requests share a URL and the payload becomes cacheable. It also keeps the
73
+ * document and the payload on different URLs, so a shared cache cannot
74
+ * return HTML for an RSC request (or vice versa) even where `Vary` is
75
+ * opt-in configuration rather than default behaviour — Cloudflare and
76
+ * CloudFront both. See GHSA-wfc6-r584-vfw7, design/13-security.md.
77
+ *
78
+ * The origin recomputes this key from the received headers and refuses to
79
+ * let a mismatched response be shared-cached, so a caller cannot claim
80
+ * another client's key while sending its own headers.
81
+ *
82
+ * Falls back to a random value when no key can be derived — `crypto.subtle`
83
+ * is absent in non-secure browsing contexts (plain `http://` on a LAN
84
+ * address). Dropping the parameter instead would put the payload back on
85
+ * the document URL, where a query-keyed cache that ignores `Vary` could
86
+ * store a Flight response under `/about` and serve it to an HTML
87
+ * navigation. The random parameter this feature replaced did separate those
88
+ * representations, and the fallback must not be worse than what it
89
+ * replaced. The origin cannot verify a random key, so it refuses to
90
+ * share-cache the response — which is exactly the old behaviour, where a
91
+ * unique URL was never a cache hit anyway.
61
92
  *
62
93
  * Strips any #fragment before appending — fragments are client-only and
63
94
  * fetch() discards them, so _rsc would land inside the hash and be lost.
64
95
  */
65
- function appendRscParam(url: string): string {
96
+ async function appendRscParam(url: string, headers: Record<string, string>): Promise<string> {
66
97
  const hashIndex = url.indexOf('#');
67
98
  const urlWithoutHash = hashIndex === -1 ? url : url.slice(0, hashIndex);
99
+ const key = (await rscCacheKey(recordLookup(headers))) ?? randomRscCacheKey();
68
100
  const separator = urlWithoutHash.includes('?') ? '&' : '?';
69
- return `${urlWithoutHash}${separator}_rsc=${generateCacheBustId()}`;
101
+ return `${urlWithoutHash}${separator}${RSC_KEY_PARAM}=${key}`;
70
102
  }
71
103
 
72
104
  // ─── Deployment ID ───────────────────────────────────────────────
@@ -88,6 +120,30 @@ export function getClientDeploymentId(): string | null {
88
120
  return clientDeploymentId;
89
121
  }
90
122
 
123
+ // ─── Base Path ───────────────────────────────────────────────────
124
+
125
+ /**
126
+ * The deployment base path (Vite's resolved `base`), normalized.
127
+ * Set at bootstrap from `virtual:timber-config`; `'/'` unless the app is
128
+ * deployed under a sub-path. See design/11-platform.md, TIM-1261.
129
+ *
130
+ * **The base is applied in exactly one place: at fetch time**
131
+ * (`toStaticRscUrl`). The static build writes the RSC manifest with
132
+ * root-relative keys *and* root-relative URLs, so the generated site stays
133
+ * relocatable and there is no second copy of the base to drift from this one.
134
+ */
135
+ let basePath = '/';
136
+
137
+ /** Set the deployment base path. Called once at bootstrap. */
138
+ export function setBasePath(base: string | undefined): void {
139
+ basePath = normalizeBasePath(base);
140
+ }
141
+
142
+ /** Get the normalized deployment base path. @internal Exported for testing. */
143
+ export function getBasePath(): string {
144
+ return basePath;
145
+ }
146
+
91
147
  // ─── Static Mode ────────────────────────────────────────────────
92
148
 
93
149
  /**
@@ -107,13 +163,14 @@ export function isStaticMode(): boolean {
107
163
  }
108
164
 
109
165
  /**
110
- * RSC manifest mapping unhashed → hashed URLs. Populated from
111
- * `window.__TIMBER_RSC_MANIFEST__` (injected into HTML during
112
- * static generation). See TIM-1254.
166
+ * RSC manifest mapping unhashed → content-hashed `.rsc` URLs. Populated from
167
+ * `window.__TIMBER_RSC_MANIFEST__` (injected into HTML during static
168
+ * generation). Shape shared with the static build that writes it — see
169
+ * `shared/rsc-manifest.ts`. See TIM-1254.
113
170
  */
114
- let rscManifest: Record<string, string> | null = null;
171
+ let rscManifest: RscManifest | null = null;
115
172
 
116
- function getRscManifest(): Record<string, string> | null {
173
+ function getRscManifest(): RscManifest | null {
117
174
  if (rscManifest) return rscManifest;
118
175
  if (typeof window !== 'undefined' && (window as any).__TIMBER_RSC_MANIFEST__) {
119
176
  rscManifest = (window as any).__TIMBER_RSC_MANIFEST__;
@@ -122,28 +179,49 @@ function getRscManifest(): Record<string, string> | null {
122
179
  }
123
180
 
124
181
  /** @internal Exported for testing. */
125
- export function setRscManifest(manifest: Record<string, string> | null): void {
182
+ export function setRscManifest(manifest: RscManifest | null): void {
126
183
  rscManifest = manifest;
127
184
  }
128
185
 
186
+ /** A resolved static payload target: the URL to fetch, and how it was resolved. */
187
+ interface StaticRscTarget {
188
+ /** Fetchable URL — base-prefixed. */
189
+ url: string;
190
+ /**
191
+ * True when the RSC manifest supplied a content-hashed filename. The hash
192
+ * guarantees freshness, so no `_rsc` cache key is appended (TIM-1254).
193
+ */
194
+ hashed: boolean;
195
+ }
196
+
129
197
  /**
130
- * Convert a route URL to the corresponding _rsc/*.rsc file path.
131
- * Mirrors the naming in plugins/static-build.ts staticOutputPath.
198
+ * Resolve a route URL to the `_rsc/*.rsc` file to fetch. The naming rule is
199
+ * `shared/rsc-payload-path.ts`, shared with the build that writes the files.
132
200
  *
133
201
  * When an RSC manifest is available (hashed filenames from TIM-1254),
134
202
  * the manifest is consulted to resolve to the hashed path.
135
203
  *
136
204
  * / → /_rsc/index.rsc (or /_rsc/index-B7YxEKdN.rsc with manifest)
137
- * /about → /_rsc/about.rsc (or /_rsc/about-C8ZzFLfO.rsc with manifest)
138
- * /blog/hello → /_rsc/blog/hello.rsc
205
+ * /about → /_rsc/about/index.rsc (or /_rsc/about/index-C8ZzFLfO.rsc)
206
+ * /blog/hello → /_rsc/blog/hello/index.rsc
207
+ *
208
+ * The manifest is written and read in route space (no base prefix), so the
209
+ * base is applied here — once, on the way out. See client/base-path.ts.
139
210
  */
140
- function toStaticRscUrl(url: string): string {
211
+ function toStaticRscUrl(url: string): StaticRscTarget {
141
212
  const unhashed = toUnhashedRscUrl(url);
142
- return manifestLookup(unhashed) ?? unhashed;
213
+ const hashedUrl = manifestLookup(unhashed);
214
+ return { url: withBasePath(basePath, hashedUrl ?? unhashed), hashed: hashedUrl !== null };
143
215
  }
144
216
 
145
217
  /**
146
- * Compute the unhashed _rsc/*.rsc URL for a route path.
218
+ * Compute the unhashed `_rsc/*.rsc` path for a route URL.
219
+ *
220
+ * Returns a **route-space** path: the deployment base is stripped from the
221
+ * incoming URL and is NOT re-applied, because this value doubles as the RSC
222
+ * manifest key, and the manifest is base-less. `toStaticRscUrl` applies the
223
+ * base to whatever is actually fetched.
224
+ *
147
225
  * @internal Exported for testing.
148
226
  */
149
227
  export function toUnhashedRscUrl(url: string): string {
@@ -152,27 +230,19 @@ export function toUnhashedRscUrl(url: string): string {
152
230
  const hashEnd = hashIndex === -1 ? url.length : hashIndex;
153
231
  const queryEnd = queryIndex === -1 ? url.length : queryIndex;
154
232
  const end = Math.min(hashEnd, queryEnd);
155
- let pathname = url.slice(0, end);
233
+ let pathname = stripBasePath(basePath, url.slice(0, end));
156
234
  // Strip trailing slash (unless root) to match static build output naming
157
235
  if (pathname.length > 1 && pathname.endsWith('/')) {
158
236
  pathname = pathname.slice(0, -1);
159
237
  }
160
- const rscPath = pathname === '/' ? '/index' : pathname;
161
- return `/_rsc${rscPath}.rsc`;
238
+ return rscPayloadPath(pathname);
162
239
  }
163
240
 
164
- /**
165
- * Convert a route URL to the corresponding _rsc/*.params.json sidecar path.
166
- * Used in static mode to fetch route params that are normally carried
167
- * in the X-Timber-Params response header. See TIM-1246.
168
- *
169
- * Consults the RSC manifest for hashed filenames (TIM-1254).
170
- */
171
241
  /**
172
242
  * Look up a key in the RSC manifest, falling back to percent-decoded
173
- * lookup for encoded browser URLs. Returns null if not found.
243
+ * lookup for encoded browser URLs. Returns the entry or null.
174
244
  */
175
- function manifestLookup(key: string): string | null {
245
+ function manifestEntry(key: string): RscManifestEntry | null {
176
246
  const manifest = getRscManifest();
177
247
  if (!manifest) return null;
178
248
  if (manifest[key]) return manifest[key];
@@ -185,11 +255,19 @@ function manifestLookup(key: string): string | null {
185
255
  return null;
186
256
  }
187
257
 
188
- function toStaticParamsUrl(url: string): string {
189
- const unhashed = toUnhashedRscUrl(url).replace(/\.rsc$/, '.params.json');
190
- return manifestLookup(unhashed) ?? unhashed;
258
+ /**
259
+ * Look up the hashed URL for an unhashed RSC path.
260
+ */
261
+ function manifestLookup(key: string): string | null {
262
+ return manifestEntry(key)?.url ?? null;
191
263
  }
192
264
 
265
+ // The manifest used to inline each route's params, because a generated
266
+ // `.rsc` file carries no response headers and the header channel therefore
267
+ // did not exist on the static path (TIM-1255, TIM-1285). The `.rsc` file now
268
+ // carries them itself — they are props inside the payload — so the static
269
+ // path needs no special case at all.
270
+
193
271
  // ─── Reload Signal ───────────────────────────────────────────────
194
272
 
195
273
  /** Header name used by the server to signal a version skew reload. */
@@ -208,8 +286,19 @@ export function checkReloadSignal(response: Response): boolean {
208
286
 
209
287
  // ─── Header Builder ──────────────────────────────────────────────
210
288
 
289
+ /**
290
+ * Build the headers for an RSC payload request.
291
+ *
292
+ * **Every header added here is hashed into the payload URL** as the `_rsc`
293
+ * cache key (see `rscCacheKey`), and URLs are logged by proxies, CDNs, and
294
+ * origin access logs. So: no credentials, no session identifiers, no
295
+ * user-supplied content. `fnv1aHash` is a cache-key hash, not a KDF — a
296
+ * low-entropy secret would be recoverable from the URL by brute force.
297
+ * Authentication already travels on cookies, which are not part of this
298
+ * object and never enter the key.
299
+ */
211
300
  export function buildRscHeaders(
212
- stateTree: { segments: string[] } | undefined,
301
+ stateTree: StateTree | undefined,
213
302
  currentUrl?: string
214
303
  ): Record<string, string> {
215
304
  const headers: Record<string, string> = {
@@ -287,23 +376,6 @@ export function extractSkippedSegments(response: Response): string[] | null {
287
376
  }
288
377
  }
289
378
 
290
- /**
291
- * Extract route params from the X-Timber-Params response header.
292
- * Returns null if the header is missing or malformed.
293
- *
294
- * Used to populate useSegmentParams() after client-side navigation.
295
- */
296
- export function extractParams(response: Response): Record<string, string | string[]> | null {
297
- const header = response.headers.get('X-Timber-Params');
298
- if (!header) return null;
299
- try {
300
- return JSON.parse(header);
301
- } catch {
302
- warnMalformedHeader('X-Timber-Params', header);
303
- return null;
304
- }
305
- }
306
-
307
379
  // ─── Redirect Error ──────────────────────────────────────────────
308
380
 
309
381
  /**
@@ -374,9 +446,10 @@ export class NonRscResponse extends Error {
374
446
  * a `done` promise that resolves when the last chunk is read (or
375
447
  * rejects if the stream errors).
376
448
  *
377
- * Used to keep React transitions open for the full RSC stream
378
- * duration — createFromFetch's thenable resolves on shell arrival,
379
- * but we need stream completion for useOptimistic pending state.
449
+ * createFromFetch's thenable resolves on shell arrival, but callers need to
450
+ * know when the stream is fully decoded: that is what `navigateTransition`'s
451
+ * returned promise means, and what the router's pending store, the Navigation
452
+ * API deferred and `<Link>`'s `isPending` are all timed against.
380
453
  */
381
454
  function trackStreamCompletion(body: ReadableStream<Uint8Array>): {
382
455
  body: ReadableStream<Uint8Array>;
@@ -431,13 +504,23 @@ export async function fetchRscPayload(
431
504
  // In static mode, fetch the pre-generated _rsc/*.rsc file directly
432
505
  // instead of the route URL with Accept headers. Static hosts ignore
433
506
  // Accept headers, so the route URL would return HTML.
434
- const fetchTarget = staticMode ? toStaticRscUrl(url) : url;
435
- // Skip the cache-bust param when the manifest supplies a content-hashed
507
+ const staticTarget = staticMode ? toStaticRscUrl(url) : null;
508
+ const fetchTarget = staticTarget ? staticTarget.url : url;
509
+ // Static hosts resolve neither partial payloads nor intercepting routes,
510
+ // so neither the state tree nor the source URL is sent — and neither may
511
+ // fragment the cache key of a file that does not vary on them.
512
+ const headers = staticMode
513
+ ? buildRscHeaders(undefined, undefined)
514
+ : buildRscHeaders(stateTree, currentUrl);
515
+ // Skip the cache key when the manifest supplies a content-hashed
436
516
  // URL — the hash itself guarantees freshness, and the param would defeat
437
517
  // the immutable cache headers on /_rsc/* (TIM-1254).
438
- const isHashedFromManifest = staticMode && fetchTarget !== toUnhashedRscUrl(url);
439
- const rscUrl = isHashedFromManifest ? fetchTarget : appendRscParam(fetchTarget);
440
- const headers = buildRscHeaders(staticMode ? undefined : stateTree, currentUrl);
518
+ const rscUrl = staticTarget?.hashed ? fetchTarget : await appendRscParam(fetchTarget, headers);
519
+ // Deriving the cache key is async (SubtleCrypto), so the fetch is no
520
+ // longer issued in the same tick as the call. A navigation superseded
521
+ // during that window would otherwise still hit the network with an
522
+ // already-aborted signal.
523
+ signal?.throwIfAborted();
441
524
  if (deps.decodeRsc) {
442
525
  // Production path: use createFromFetch for streaming RSC decoding.
443
526
  // createFromFetch takes a Promise<Response> and progressively parses
@@ -446,23 +529,7 @@ export async function fetchRscPayload(
446
529
  // Intercept the response to read segment metadata before createFromFetch
447
530
  // consumes the body. Reading headers does NOT consume the body stream.
448
531
  const fetchPromise = deps.fetch(rscUrl, { headers, redirect: 'manual', signal });
449
- // In static mode, fetch the params sidecar in parallel. The .rsc
450
- // file has no HTTP headers, so params come from a .params.json
451
- // sidecar written at build time. See TIM-1246.
452
- const staticParamsFetch = staticMode
453
- ? deps
454
- .fetch(toStaticParamsUrl(url), { signal })
455
- .then((r) => (r.ok ? (r.json() as Promise<Record<string, string | string[]>>) : null))
456
- .catch((e) => {
457
- if (e instanceof DOMException && e.name === 'AbortError') throw e;
458
- return null;
459
- })
460
- : null;
461
- // Observe the sidecar promise so its rejection doesn't become
462
- // unhandled if wrappedPromise rejects first (TIM-1248).
463
- staticParamsFetch?.catch(() => {});
464
532
  let segmentInfo: SegmentInfo[] | null = null;
465
- let params: Record<string, string | string[]> | null = null;
466
533
  let skippedSegments: string[] | null = null;
467
534
  // Track when the full RSC body stream is consumed (not just shell).
468
535
  // Initialized to resolved for bodyless responses; overwritten when
@@ -490,7 +557,7 @@ export async function fetchRscPayload(
490
557
  // with a JSON body instead of a broken RSC stream for any RenderError
491
558
  // (4xx or 5xx). Hard-navigate so the server renders the error page as HTML.
492
559
  // See design/10-error-handling.md §"Error Page Rendering for Client Navigation"
493
- if (response.headers.get('X-Timber-Error') === '1') {
560
+ if (response.headers.get(TIMBER_ERROR_HEADER) === '1') {
494
561
  throw new ServerErrorResponse(response.status, url);
495
562
  }
496
563
  // Content-Type guard: reject non-RSC responses before createFromFetch
@@ -518,13 +585,12 @@ export async function fetchRscPayload(
518
585
  // Metadata (<title>/<meta>/<link>) now rides the RSC Flight payload
519
586
  // as React elements — React 19 Float handles them. See TIM-1151.
520
587
  segmentInfo = extractSegmentInfo(response);
521
- params = extractParams(response);
522
588
  skippedSegments = extractSkippedSegments(response);
523
589
 
524
590
  // Wrap the body to track full stream consumption. createFromFetch's
525
591
  // thenable resolves when the root model (shell) arrives, but we need
526
- // to know when ALL chunks are read so the React transition stays
527
- // open for the full streaming duration (keeps useOptimistic alive).
592
+ // to know when ALL chunks are read — that is what the navigation
593
+ // promise resolving means, and what keeps the pending store set.
528
594
  if (response.body) {
529
595
  const tracked = trackStreamCompletion(response.body);
530
596
  streamDone = tracked.done;
@@ -536,33 +602,37 @@ export async function fetchRscPayload(
536
602
  }
537
603
  return response;
538
604
  });
539
- // Await headers so segmentInfo/params are populated.
605
+ // Await headers so segmentInfo is populated.
540
606
  await wrappedPromise;
541
- // In static mode, params come from the sidecar (header extraction
542
- // returns null because .rsc files have no HTTP headers). TIM-1246.
543
- if (staticParamsFetch && !params) {
544
- params = await staticParamsFetch;
545
- }
546
607
  // Start decoding but do NOT await — return the in-progress thenable.
547
608
  // React can render a Flight thenable directly: it suspends on unresolved
548
609
  // parts and progressively renders as chunks arrive, spreading work across
549
610
  // frames instead of blocking the main thread in one burst.
550
- const payload = deps.decodeRsc(wrappedPromise);
611
+ const root = deps.decodeRsc(wrappedPromise);
612
+ // One split, one root: the tree and the params it was rendered with come
613
+ // apart here and nowhere else, so nothing downstream has to keep them
614
+ // paired by hand.
615
+ const { tree: payload, params } = splitPayloadRoot(root);
551
616
  // Combine stream completion with payload error propagation.
552
- // streamDone keeps the transition open for the full RSC stream
553
- // duration (useOptimistic pending state). payloadError propagates
617
+ // streamDone marks the end of the RSC stream, which is what the
618
+ // navigation promise resolves on. payloadError propagates
554
619
  // decode failures (stale client references, Flight parse errors)
555
620
  // so the router's catch block can trigger recovery (stale reload).
556
621
  const payloadError = new Promise<void>((_, reject) => {
557
- Promise.resolve(payload).then(() => {}, reject);
622
+ Promise.resolve(root).then(() => {}, reject);
558
623
  });
559
624
  payloadError.catch(() => {});
560
625
  const decodePromise = Promise.race([streamDone, payloadError]);
626
+ // Mark handled, like payloadError above. The same rejection reaches the
627
+ // router through `payload`, and a navigation that recovers by hard
628
+ // navigating (version skew, non-RSC response) hangs deliberately and never
629
+ // awaits this one — leaving it to surface as an unhandled rejection.
630
+ decodePromise.catch(() => {});
561
631
  return {
562
632
  payload,
633
+ params,
563
634
  decodePromise,
564
635
  segmentInfo,
565
- params,
566
636
  skippedSegments,
567
637
  };
568
638
  }
@@ -576,7 +646,7 @@ export async function fetchRscPayload(
576
646
  }
577
647
  }
578
648
  // Server error guard (same as production path above).
579
- if (response.headers.get('X-Timber-Error') === '1') {
649
+ if (response.headers.get(TIMBER_ERROR_HEADER) === '1') {
580
650
  throw new ServerErrorResponse(response.status, url);
581
651
  }
582
652
  // Content-Type guard (same as production path above). See TIM-1231, TIM-1243, TIM-1247.
@@ -600,24 +670,12 @@ export async function fetchRscPayload(
600
670
  throw new NonRscResponse(url);
601
671
  }
602
672
  }
603
- let fallbackParams = extractParams(response);
604
- // In static mode, params come from the sidecar. TIM-1246.
605
- if (staticMode && !fallbackParams) {
606
- try {
607
- const paramsResponse = await deps.fetch(toStaticParamsUrl(url), { signal });
608
- if (paramsResponse.ok) {
609
- fallbackParams = (await paramsResponse.json()) as Record<string, string | string[]>;
610
- }
611
- } catch (e) {
612
- if (e instanceof DOMException && e.name === 'AbortError') throw e;
613
- // No params sidecar — non-dynamic route, params stay null
614
- }
615
- }
673
+ // Test/fallback path: raw text carries no payload root, so no params.
616
674
  return {
617
675
  payload: await response.text(),
676
+ params: readPublishedParams(undefined),
618
677
  decodePromise: null,
619
678
  segmentInfo: extractSegmentInfo(response),
620
- params: fallbackParams,
621
679
  skippedSegments: extractSkippedSegments(response),
622
680
  };
623
681
  }