@timber-js/app 0.2.0-alpha.185 → 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 (418) 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-Byy425ng.js.map → segment-classify-C539Pa2O.js.map} +1 -1
  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-_6zKFlch.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 +865 -306
  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 +38 -25
  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 +906 -321
  136. package/dist/index.js.map +1 -1
  137. package/dist/plugin-context.d.ts +31 -0
  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 +136 -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/walkers.d.ts +6 -0
  173. package/dist/routing/walkers.d.ts.map +1 -1
  174. package/dist/search-params/define.d.ts +0 -6
  175. package/dist/search-params/define.d.ts.map +1 -1
  176. package/dist/search-params/index.js +176 -2
  177. package/dist/search-params/index.js.map +1 -1
  178. package/dist/segment-params/define.d.ts +0 -6
  179. package/dist/segment-params/define.d.ts.map +1 -1
  180. package/dist/segment-params/index.js +69 -1
  181. package/dist/segment-params/index.js.map +1 -0
  182. package/dist/server/actions.d.ts +6 -3
  183. package/dist/server/actions.d.ts.map +1 -1
  184. package/dist/server/als-registry.d.ts +2 -1
  185. package/dist/server/als-registry.d.ts.map +1 -1
  186. package/dist/server/build-manifest.d.ts +13 -1
  187. package/dist/server/build-manifest.d.ts.map +1 -1
  188. package/dist/server/chain-url-parts.d.ts +82 -0
  189. package/dist/server/chain-url-parts.d.ts.map +1 -0
  190. package/dist/server/children-interception.d.ts +49 -0
  191. package/dist/server/children-interception.d.ts.map +1 -0
  192. package/dist/server/default-status-page.d.ts +45 -0
  193. package/dist/server/default-status-page.d.ts.map +1 -0
  194. package/dist/server/deny-renderer.d.ts +14 -3
  195. package/dist/server/deny-renderer.d.ts.map +1 -1
  196. package/dist/server/head-response.d.ts +35 -0
  197. package/dist/server/head-response.d.ts.map +1 -0
  198. package/dist/server/index.js +2 -2
  199. package/dist/server/internal.js +393 -212
  200. package/dist/server/internal.js.map +1 -1
  201. package/dist/server/param-coercion.d.ts +10 -1
  202. package/dist/server/param-coercion.d.ts.map +1 -1
  203. package/dist/server/pipeline-helpers.d.ts +0 -18
  204. package/dist/server/pipeline-helpers.d.ts.map +1 -1
  205. package/dist/server/pipeline-interception.d.ts +59 -9
  206. package/dist/server/pipeline-interception.d.ts.map +1 -1
  207. package/dist/server/pipeline-outcome.d.ts.map +1 -1
  208. package/dist/server/pipeline-phases.d.ts.map +1 -1
  209. package/dist/server/pipeline.d.ts +11 -0
  210. package/dist/server/pipeline.d.ts.map +1 -1
  211. package/dist/server/publish-params.d.ts +33 -0
  212. package/dist/server/publish-params.d.ts.map +1 -0
  213. package/dist/server/request-context.d.ts +48 -0
  214. package/dist/server/request-context.d.ts.map +1 -1
  215. package/dist/server/route-element-builder.d.ts +32 -2
  216. package/dist/server/route-element-builder.d.ts.map +1 -1
  217. package/dist/server/route-handler.d.ts.map +1 -1
  218. package/dist/server/route-matcher.d.ts +13 -0
  219. package/dist/server/route-matcher.d.ts.map +1 -1
  220. package/dist/server/rsc-cache-key-guard.d.ts +78 -0
  221. package/dist/server/rsc-cache-key-guard.d.ts.map +1 -0
  222. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
  223. package/dist/server/rsc-entry/error-renderer.d.ts +14 -2
  224. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  225. package/dist/server/rsc-entry/helpers.d.ts +2 -10
  226. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  227. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  228. package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
  229. package/dist/server/rsc-entry/rsc-payload.d.ts +3 -2
  230. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  231. package/dist/server/rsc-entry/rsc-stream.d.ts +3 -2
  232. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  233. package/dist/server/rsc-entry/ssr-renderer.d.ts +2 -0
  234. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  235. package/dist/server/skippable-prefix.d.ts +23 -0
  236. package/dist/server/skippable-prefix.d.ts.map +1 -0
  237. package/dist/server/slot-interception.d.ts +80 -0
  238. package/dist/server/slot-interception.d.ts.map +1 -0
  239. package/dist/server/slot-resolver.d.ts +70 -8
  240. package/dist/server/slot-resolver.d.ts.map +1 -1
  241. package/dist/server/ssr-bridge-types.d.ts +6 -0
  242. package/dist/server/ssr-bridge-types.d.ts.map +1 -1
  243. package/dist/server/ssr-entry.d.ts.map +1 -1
  244. package/dist/server/ssr-wrappers.d.ts +24 -13
  245. package/dist/server/ssr-wrappers.d.ts.map +1 -1
  246. package/dist/server/state-tree-diff.d.ts +6 -27
  247. package/dist/server/state-tree-diff.d.ts.map +1 -1
  248. package/dist/server/static-generator.d.ts +6 -0
  249. package/dist/server/static-generator.d.ts.map +1 -1
  250. package/dist/server/static-not-found.d.ts +52 -0
  251. package/dist/server/static-not-found.d.ts.map +1 -0
  252. package/dist/shared/als-slots.d.ts +41 -0
  253. package/dist/shared/als-slots.d.ts.map +1 -0
  254. package/dist/shared/base-path.d.ts +56 -0
  255. package/dist/shared/base-path.d.ts.map +1 -0
  256. package/dist/shared/param-value.d.ts +80 -0
  257. package/dist/shared/param-value.d.ts.map +1 -0
  258. package/dist/shared/payload-root.d.ts +108 -0
  259. package/dist/shared/payload-root.d.ts.map +1 -0
  260. package/dist/shared/rsc-cache-key.d.ts +163 -0
  261. package/dist/shared/rsc-cache-key.d.ts.map +1 -0
  262. package/dist/shared/rsc-error-envelope.d.ts +25 -0
  263. package/dist/shared/rsc-error-envelope.d.ts.map +1 -0
  264. package/dist/shared/rsc-manifest.d.ts +28 -0
  265. package/dist/shared/rsc-manifest.d.ts.map +1 -0
  266. package/dist/shared/rsc-payload-path.d.ts +42 -0
  267. package/dist/shared/rsc-payload-path.d.ts.map +1 -0
  268. package/dist/shared/segment-info.d.ts +59 -0
  269. package/dist/shared/segment-info.d.ts.map +1 -0
  270. package/dist/shared/slot-params.d.ts +51 -0
  271. package/dist/shared/slot-params.d.ts.map +1 -0
  272. package/dist/shared/static-platform-files.d.ts +71 -0
  273. package/dist/shared/static-platform-files.d.ts.map +1 -0
  274. package/docs/api/31-api-client.mdx +4 -2
  275. package/docs/learn/02-pages-and-layouts.mdx +1 -1
  276. package/docs/learn/11-error-handling.mdx +2 -0
  277. package/docs/learn/12-client-navigation.mdx +2 -2
  278. package/docs/learn/14-deploying.mdx +23 -0
  279. package/docs/more/01-advanced-routing.mdx +172 -2
  280. package/docs/more/04-metadata-and-fonts.mdx +1 -1
  281. package/package.json +2 -2
  282. package/src/adapters/build-output-helper.ts +116 -3
  283. package/src/adapters/cloudflare.ts +86 -11
  284. package/src/adapters/fs-identity.ts +156 -0
  285. package/src/adapters/nitro.ts +6 -6
  286. package/src/adapters/shared.ts +196 -21
  287. package/src/adapters/types.ts +13 -1
  288. package/src/cache/fast-hash.ts +5 -0
  289. package/src/client/browser-entry/action-dispatch.ts +9 -4
  290. package/src/client/browser-entry/hydrate.ts +40 -29
  291. package/src/client/browser-entry/index.ts +15 -1
  292. package/src/client/browser-entry/post-hydration.ts +10 -9
  293. package/src/client/browser-entry/router-init.ts +104 -48
  294. package/src/client/browser-entry/rsc-stream.ts +11 -2
  295. package/src/client/error-boundary.tsx +20 -15
  296. package/src/client/history.ts +13 -6
  297. package/src/client/internal.ts +4 -3
  298. package/src/client/link.tsx +55 -8
  299. package/src/client/navigation-api.ts +3 -3
  300. package/src/client/navigation-commit.ts +174 -0
  301. package/src/client/navigation-context.ts +40 -67
  302. package/src/client/navigation-root.tsx +326 -101
  303. package/src/client/params-context.ts +192 -0
  304. package/src/client/router-effects.ts +231 -0
  305. package/src/client/router-lifecycle.ts +288 -0
  306. package/src/client/router-pipeline.ts +389 -0
  307. package/src/client/router-ref.ts +1 -1
  308. package/src/client/router-skew.ts +30 -0
  309. package/src/client/router-types.ts +230 -0
  310. package/src/client/router.ts +242 -707
  311. package/src/client/rsc-fetch.ts +169 -102
  312. package/src/client/segment-cache.ts +121 -44
  313. package/src/client/ssr-data.ts +6 -0
  314. package/src/client/stale-client.ts +191 -0
  315. package/src/client/state.ts +14 -1
  316. package/src/client/top-loader.tsx +2 -1
  317. package/src/client/unload-guard.ts +34 -2
  318. package/src/client/use-cookie.ts +9 -2
  319. package/src/client/use-router.ts +1 -1
  320. package/src/client/use-segment-params.ts +41 -18
  321. package/src/index.ts +5 -1
  322. package/src/plugin-context.ts +32 -0
  323. package/src/plugins/adapter-build.ts +4 -0
  324. package/src/plugins/build-manifest.ts +34 -13
  325. package/src/plugins/entries.ts +4 -0
  326. package/src/plugins/fonts.ts +11 -8
  327. package/src/plugins/request-dep/analysis.ts +447 -0
  328. package/src/plugins/request-dep/index.ts +376 -0
  329. package/src/plugins/request-dep/origin.ts +310 -0
  330. package/src/plugins/routing.ts +59 -2
  331. package/src/plugins/static-build.ts +315 -73
  332. package/src/routing/codegen-types.ts +18 -0
  333. package/src/routing/codegen.ts +50 -10
  334. package/src/routing/collision-probe.ts +438 -0
  335. package/src/routing/collision-spaces.ts +281 -0
  336. package/src/routing/interception.ts +896 -50
  337. package/src/routing/manifest-codegen.ts +5 -3
  338. package/src/routing/scanner.ts +114 -89
  339. package/src/routing/segment-classify.ts +57 -0
  340. package/src/routing/segment-keys.ts +146 -0
  341. package/src/routing/slot-placement.ts +97 -0
  342. package/src/routing/walkers.ts +8 -0
  343. package/src/search-params/define.ts +6 -29
  344. package/src/segment-params/define.ts +4 -27
  345. package/src/server/action-handler.ts +3 -1
  346. package/src/server/actions.ts +6 -3
  347. package/src/server/als-registry.ts +2 -1
  348. package/src/server/build-manifest.ts +14 -1
  349. package/src/server/chain-url-parts.ts +163 -0
  350. package/src/server/children-interception.ts +115 -0
  351. package/src/server/default-status-page.ts +127 -0
  352. package/src/server/deny-renderer.ts +81 -20
  353. package/src/server/head-response.ts +54 -0
  354. package/src/server/param-coercion.ts +48 -18
  355. package/src/server/pipeline-helpers.ts +0 -45
  356. package/src/server/pipeline-interception.ts +81 -32
  357. package/src/server/pipeline-outcome.ts +2 -4
  358. package/src/server/pipeline-phases.ts +68 -16
  359. package/src/server/pipeline.ts +57 -10
  360. package/src/server/prebuilt-runtime.ts +4 -4
  361. package/src/server/publish-params.ts +42 -0
  362. package/src/server/request-context.ts +97 -18
  363. package/src/server/route-element-builder.ts +89 -98
  364. package/src/server/route-handler.ts +8 -8
  365. package/src/server/route-matcher.ts +21 -9
  366. package/src/server/rsc-cache-key-guard.ts +170 -0
  367. package/src/server/rsc-entry/api-handler.ts +6 -8
  368. package/src/server/rsc-entry/error-renderer.ts +64 -25
  369. package/src/server/rsc-entry/helpers.ts +31 -16
  370. package/src/server/rsc-entry/index.ts +36 -28
  371. package/src/server/rsc-entry/render-route.ts +9 -4
  372. package/src/server/rsc-entry/revalidate-renderer.ts +1 -1
  373. package/src/server/rsc-entry/rsc-payload.ts +14 -25
  374. package/src/server/rsc-entry/rsc-stream.ts +4 -3
  375. package/src/server/rsc-entry/ssr-renderer.ts +13 -9
  376. package/src/server/skippable-prefix.ts +92 -0
  377. package/src/server/slot-interception.ts +168 -0
  378. package/src/server/slot-resolver.ts +253 -156
  379. package/src/server/ssr-bridge-types.ts +6 -0
  380. package/src/server/ssr-entry.ts +12 -4
  381. package/src/server/ssr-wrappers.tsx +33 -32
  382. package/src/server/state-tree-diff.ts +14 -55
  383. package/src/server/static-generator.ts +65 -27
  384. package/src/server/static-not-found.ts +134 -0
  385. package/src/shared/als-slots.ts +72 -0
  386. package/src/shared/base-path.ts +80 -0
  387. package/src/shared/param-value.ts +244 -0
  388. package/src/shared/payload-root.ts +151 -0
  389. package/src/shared/rsc-cache-key.ts +269 -0
  390. package/src/shared/rsc-error-envelope.ts +31 -0
  391. package/src/shared/rsc-manifest.ts +29 -0
  392. package/src/shared/rsc-payload-path.ts +45 -0
  393. package/src/shared/segment-info.ts +60 -0
  394. package/src/shared/slot-params.ts +111 -0
  395. package/src/shared/static-platform-files.ts +75 -0
  396. package/dist/_chunks/build-output-helper-DOGYFb_X.js +0 -196
  397. package/dist/_chunks/build-output-helper-DOGYFb_X.js.map +0 -1
  398. package/dist/_chunks/canonicalize-DQHyFClh.js.map +0 -1
  399. package/dist/_chunks/cli-schema-sync-BTWEKJXo.js +0 -668
  400. package/dist/_chunks/cli-schema-sync-BTWEKJXo.js.map +0 -1
  401. package/dist/_chunks/define-COtkxMRT.js +0 -191
  402. package/dist/_chunks/define-COtkxMRT.js.map +0 -1
  403. package/dist/_chunks/define-c4au4I9R.js +0 -82
  404. package/dist/_chunks/define-c4au4I9R.js.map +0 -1
  405. package/dist/_chunks/fast-hash-D6hIVt1Y.js.map +0 -1
  406. package/dist/_chunks/logger-AWfuX-KJ.js.map +0 -1
  407. package/dist/_chunks/navigation-root-bUDZLw74.js +0 -124
  408. package/dist/_chunks/navigation-root-bUDZLw74.js.map +0 -1
  409. package/dist/_chunks/plugin-context---kTF5v8.js.map +0 -1
  410. package/dist/_chunks/router-ref-CtmF-aPv.js.map +0 -1
  411. package/dist/_chunks/ssr-data-BOWsq18U.js.map +0 -1
  412. package/dist/_chunks/use-segment-params-D1CzlgBo.js +0 -151
  413. package/dist/_chunks/use-segment-params-D1CzlgBo.js.map +0 -1
  414. package/dist/_chunks/walkers-_6zKFlch.js.map +0 -1
  415. package/dist/client/error-boundary.js.map +0 -1
  416. package/dist/plugins/request-dep.d.ts +0 -32
  417. package/dist/plugins/request-dep.d.ts.map +0 -1
  418. 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,20 +163,10 @@ export function isStaticMode(): boolean {
107
163
  }
108
164
 
109
165
  /**
110
- * RSC manifest entry: hashed URL with optional inlined route params.
111
- * Params are embedded at build time (TIM-1255) so no sidecar fetch needed.
112
- */
113
- interface RscManifestEntry {
114
- url: string;
115
- params?: Record<string, string | string[]>;
116
- }
117
-
118
- type RscManifest = Record<string, RscManifestEntry>;
119
-
120
- /**
121
- * RSC manifest mapping unhashed → hashed URLs + params. Populated from
122
- * `window.__TIMBER_RSC_MANIFEST__` (injected into HTML during
123
- * static generation). See TIM-1254, TIM-1255.
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.
124
170
  */
125
171
  let rscManifest: RscManifest | null = null;
126
172
 
@@ -137,24 +183,45 @@ export function setRscManifest(manifest: RscManifest | null): void {
137
183
  rscManifest = manifest;
138
184
  }
139
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
+
140
197
  /**
141
- * Convert a route URL to the corresponding _rsc/*.rsc file path.
142
- * 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.
143
200
  *
144
201
  * When an RSC manifest is available (hashed filenames from TIM-1254),
145
202
  * the manifest is consulted to resolve to the hashed path.
146
203
  *
147
204
  * / → /_rsc/index.rsc (or /_rsc/index-B7YxEKdN.rsc with manifest)
148
- * /about → /_rsc/about.rsc (or /_rsc/about-C8ZzFLfO.rsc with manifest)
149
- * /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.
150
210
  */
151
- function toStaticRscUrl(url: string): string {
211
+ function toStaticRscUrl(url: string): StaticRscTarget {
152
212
  const unhashed = toUnhashedRscUrl(url);
153
- return manifestLookup(unhashed) ?? unhashed;
213
+ const hashedUrl = manifestLookup(unhashed);
214
+ return { url: withBasePath(basePath, hashedUrl ?? unhashed), hashed: hashedUrl !== null };
154
215
  }
155
216
 
156
217
  /**
157
- * 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
+ *
158
225
  * @internal Exported for testing.
159
226
  */
160
227
  export function toUnhashedRscUrl(url: string): string {
@@ -163,13 +230,12 @@ export function toUnhashedRscUrl(url: string): string {
163
230
  const hashEnd = hashIndex === -1 ? url.length : hashIndex;
164
231
  const queryEnd = queryIndex === -1 ? url.length : queryIndex;
165
232
  const end = Math.min(hashEnd, queryEnd);
166
- let pathname = url.slice(0, end);
233
+ let pathname = stripBasePath(basePath, url.slice(0, end));
167
234
  // Strip trailing slash (unless root) to match static build output naming
168
235
  if (pathname.length > 1 && pathname.endsWith('/')) {
169
236
  pathname = pathname.slice(0, -1);
170
237
  }
171
- const rscPath = pathname === '/' ? '/index' : pathname;
172
- return `/_rsc${rscPath}.rsc`;
238
+ return rscPayloadPath(pathname);
173
239
  }
174
240
 
175
241
  /**
@@ -196,13 +262,11 @@ function manifestLookup(key: string): string | null {
196
262
  return manifestEntry(key)?.url ?? null;
197
263
  }
198
264
 
199
- /**
200
- * Look up inlined route params for a route from the manifest. TIM-1255.
201
- */
202
- function manifestParams(url: string): Record<string, string | string[]> | null {
203
- const key = toUnhashedRscUrl(url);
204
- return manifestEntry(key)?.params ?? null;
205
- }
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.
206
270
 
207
271
  // ─── Reload Signal ───────────────────────────────────────────────
208
272
 
@@ -222,8 +286,19 @@ export function checkReloadSignal(response: Response): boolean {
222
286
 
223
287
  // ─── Header Builder ──────────────────────────────────────────────
224
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
+ */
225
300
  export function buildRscHeaders(
226
- stateTree: { segments: string[] } | undefined,
301
+ stateTree: StateTree | undefined,
227
302
  currentUrl?: string
228
303
  ): Record<string, string> {
229
304
  const headers: Record<string, string> = {
@@ -301,23 +376,6 @@ export function extractSkippedSegments(response: Response): string[] | null {
301
376
  }
302
377
  }
303
378
 
304
- /**
305
- * Extract route params from the X-Timber-Params response header.
306
- * Returns null if the header is missing or malformed.
307
- *
308
- * Used to populate useSegmentParams() after client-side navigation.
309
- */
310
- export function extractParams(response: Response): Record<string, string | string[]> | null {
311
- const header = response.headers.get('X-Timber-Params');
312
- if (!header) return null;
313
- try {
314
- return JSON.parse(header);
315
- } catch {
316
- warnMalformedHeader('X-Timber-Params', header);
317
- return null;
318
- }
319
- }
320
-
321
379
  // ─── Redirect Error ──────────────────────────────────────────────
322
380
 
323
381
  /**
@@ -388,9 +446,10 @@ export class NonRscResponse extends Error {
388
446
  * a `done` promise that resolves when the last chunk is read (or
389
447
  * rejects if the stream errors).
390
448
  *
391
- * Used to keep React transitions open for the full RSC stream
392
- * duration — createFromFetch's thenable resolves on shell arrival,
393
- * 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.
394
453
  */
395
454
  function trackStreamCompletion(body: ReadableStream<Uint8Array>): {
396
455
  body: ReadableStream<Uint8Array>;
@@ -445,13 +504,23 @@ export async function fetchRscPayload(
445
504
  // In static mode, fetch the pre-generated _rsc/*.rsc file directly
446
505
  // instead of the route URL with Accept headers. Static hosts ignore
447
506
  // Accept headers, so the route URL would return HTML.
448
- const fetchTarget = staticMode ? toStaticRscUrl(url) : url;
449
- // 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
450
516
  // URL — the hash itself guarantees freshness, and the param would defeat
451
517
  // the immutable cache headers on /_rsc/* (TIM-1254).
452
- const isHashedFromManifest = staticMode && fetchTarget !== toUnhashedRscUrl(url);
453
- const rscUrl = isHashedFromManifest ? fetchTarget : appendRscParam(fetchTarget);
454
- 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();
455
524
  if (deps.decodeRsc) {
456
525
  // Production path: use createFromFetch for streaming RSC decoding.
457
526
  // createFromFetch takes a Promise<Response> and progressively parses
@@ -461,7 +530,6 @@ export async function fetchRscPayload(
461
530
  // consumes the body. Reading headers does NOT consume the body stream.
462
531
  const fetchPromise = deps.fetch(rscUrl, { headers, redirect: 'manual', signal });
463
532
  let segmentInfo: SegmentInfo[] | null = null;
464
- let params: Record<string, string | string[]> | null = null;
465
533
  let skippedSegments: string[] | null = null;
466
534
  // Track when the full RSC body stream is consumed (not just shell).
467
535
  // Initialized to resolved for bodyless responses; overwritten when
@@ -489,7 +557,7 @@ export async function fetchRscPayload(
489
557
  // with a JSON body instead of a broken RSC stream for any RenderError
490
558
  // (4xx or 5xx). Hard-navigate so the server renders the error page as HTML.
491
559
  // See design/10-error-handling.md §"Error Page Rendering for Client Navigation"
492
- if (response.headers.get('X-Timber-Error') === '1') {
560
+ if (response.headers.get(TIMBER_ERROR_HEADER) === '1') {
493
561
  throw new ServerErrorResponse(response.status, url);
494
562
  }
495
563
  // Content-Type guard: reject non-RSC responses before createFromFetch
@@ -517,13 +585,12 @@ export async function fetchRscPayload(
517
585
  // Metadata (<title>/<meta>/<link>) now rides the RSC Flight payload
518
586
  // as React elements — React 19 Float handles them. See TIM-1151.
519
587
  segmentInfo = extractSegmentInfo(response);
520
- params = extractParams(response);
521
588
  skippedSegments = extractSkippedSegments(response);
522
589
 
523
590
  // Wrap the body to track full stream consumption. createFromFetch's
524
591
  // thenable resolves when the root model (shell) arrives, but we need
525
- // to know when ALL chunks are read so the React transition stays
526
- // 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.
527
594
  if (response.body) {
528
595
  const tracked = trackStreamCompletion(response.body);
529
596
  streamDone = tracked.done;
@@ -535,33 +602,37 @@ export async function fetchRscPayload(
535
602
  }
536
603
  return response;
537
604
  });
538
- // Await headers so segmentInfo/params are populated.
605
+ // Await headers so segmentInfo is populated.
539
606
  await wrappedPromise;
540
- // In static mode, params come from the manifest (inlined at build
541
- // time) since .rsc files have no HTTP headers. TIM-1255.
542
- if (staticMode && !params) {
543
- params = manifestParams(url);
544
- }
545
607
  // Start decoding but do NOT await — return the in-progress thenable.
546
608
  // React can render a Flight thenable directly: it suspends on unresolved
547
609
  // parts and progressively renders as chunks arrive, spreading work across
548
610
  // frames instead of blocking the main thread in one burst.
549
- 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);
550
616
  // Combine stream completion with payload error propagation.
551
- // streamDone keeps the transition open for the full RSC stream
552
- // 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
553
619
  // decode failures (stale client references, Flight parse errors)
554
620
  // so the router's catch block can trigger recovery (stale reload).
555
621
  const payloadError = new Promise<void>((_, reject) => {
556
- Promise.resolve(payload).then(() => {}, reject);
622
+ Promise.resolve(root).then(() => {}, reject);
557
623
  });
558
624
  payloadError.catch(() => {});
559
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(() => {});
560
631
  return {
561
632
  payload,
633
+ params,
562
634
  decodePromise,
563
635
  segmentInfo,
564
- params,
565
636
  skippedSegments,
566
637
  };
567
638
  }
@@ -575,7 +646,7 @@ export async function fetchRscPayload(
575
646
  }
576
647
  }
577
648
  // Server error guard (same as production path above).
578
- if (response.headers.get('X-Timber-Error') === '1') {
649
+ if (response.headers.get(TIMBER_ERROR_HEADER) === '1') {
579
650
  throw new ServerErrorResponse(response.status, url);
580
651
  }
581
652
  // Content-Type guard (same as production path above). See TIM-1231, TIM-1243, TIM-1247.
@@ -599,16 +670,12 @@ export async function fetchRscPayload(
599
670
  throw new NonRscResponse(url);
600
671
  }
601
672
  }
602
- let fallbackParams = extractParams(response);
603
- // In static mode, params come from the manifest. TIM-1255.
604
- if (staticMode && !fallbackParams) {
605
- fallbackParams = manifestParams(url);
606
- }
673
+ // Test/fallback path: raw text carries no payload root, so no params.
607
674
  return {
608
675
  payload: await response.text(),
676
+ params: readPublishedParams(undefined),
609
677
  decodePromise: null,
610
678
  segmentInfo: extractSegmentInfo(response),
611
- params: fallbackParams,
612
679
  skippedSegments: extractSkippedSegments(response),
613
680
  };
614
681
  }