@timber-js/app 0.2.0-alpha.185 → 0.2.0-alpha.187

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (570) hide show
  1. package/dist/_chunks/{actions-O_LsyCE4.js → actions-35jnMdeJ.js} +4 -3
  2. package/dist/_chunks/{actions-O_LsyCE4.js.map → actions-35jnMdeJ.js.map} +1 -1
  3. package/dist/_chunks/als-registry-C6kcfprT.js +41 -0
  4. package/dist/_chunks/als-registry-C6kcfprT.js.map +1 -0
  5. package/dist/_chunks/als-slots-BEEIPKYm.js +26 -0
  6. package/dist/_chunks/als-slots-BEEIPKYm.js.map +1 -0
  7. package/dist/_chunks/base-path-DaQrzbez.js +80 -0
  8. package/dist/_chunks/base-path-DaQrzbez.js.map +1 -0
  9. package/dist/_chunks/build-manifest-DWppEdLB.js +86 -0
  10. package/dist/_chunks/build-manifest-DWppEdLB.js.map +1 -0
  11. package/dist/_chunks/build-output-helper-C3DrfzZR.js +369 -0
  12. package/dist/_chunks/build-output-helper-C3DrfzZR.js.map +1 -0
  13. package/dist/_chunks/{cache-api-B-lhk9p4.js → cache-api-DjNrIWRR.js} +8 -14
  14. package/dist/_chunks/cache-api-DjNrIWRR.js.map +1 -0
  15. package/dist/_chunks/cli-check-CpmN7Nh-.js +256 -0
  16. package/dist/_chunks/cli-check-CpmN7Nh-.js.map +1 -0
  17. package/dist/_chunks/cli-schema-sync-CGMp_Psg.js +298 -0
  18. package/dist/_chunks/cli-schema-sync-CGMp_Psg.js.map +1 -0
  19. package/dist/_chunks/{cloudflare-CnT5Lr7U.js → cloudflare-CGP6BZKO.js} +92 -14
  20. package/dist/_chunks/{cloudflare-CnT5Lr7U.js.map → cloudflare-CGP6BZKO.js.map} +1 -1
  21. package/dist/_chunks/convention-lint-kXsgc_-7.js +784 -0
  22. package/dist/_chunks/convention-lint-kXsgc_-7.js.map +1 -0
  23. package/dist/_chunks/error-boundary-D-ODYX41.js +353 -0
  24. package/dist/_chunks/error-boundary-D-ODYX41.js.map +1 -0
  25. package/dist/_chunks/{fast-hash-D6hIVt1Y.js → fast-hash-C-tVYrLb.js} +6 -1
  26. package/dist/_chunks/fast-hash-C-tVYrLb.js.map +1 -0
  27. package/dist/_chunks/file-cache-DmX7OqZP.js +454 -0
  28. package/dist/_chunks/file-cache-DmX7OqZP.js.map +1 -0
  29. package/dist/_chunks/fs-identity-D7vb6GdI.js +208 -0
  30. package/dist/_chunks/fs-identity-D7vb6GdI.js.map +1 -0
  31. package/dist/_chunks/{logger-AWfuX-KJ.js → logger-D8xJZXIN.js} +34 -51
  32. package/dist/_chunks/logger-D8xJZXIN.js.map +1 -0
  33. package/dist/_chunks/mdx-file-C005ay-P.js +25 -0
  34. package/dist/_chunks/mdx-file-C005ay-P.js.map +1 -0
  35. package/dist/_chunks/{plugin-context---kTF5v8.js → plugin-context-rCinWLiE.js} +8 -2
  36. package/dist/_chunks/plugin-context-rCinWLiE.js.map +1 -0
  37. package/dist/_chunks/purge-store-Byr8XOjU.js +14 -0
  38. package/dist/_chunks/purge-store-Byr8XOjU.js.map +1 -0
  39. package/dist/_chunks/{resolve-schema-Dz3fcFUo.js → resolve-schema-5ma5pp1b.js} +2 -2
  40. package/dist/_chunks/{resolve-schema-Dz3fcFUo.js.map → resolve-schema-5ma5pp1b.js.map} +1 -1
  41. package/dist/_chunks/{router-ref-CtmF-aPv.js → router-ref-DuYuV_0Q.js} +2 -2
  42. package/dist/_chunks/router-ref-DuYuV_0Q.js.map +1 -0
  43. package/dist/_chunks/rsc-cache-key-DD0fl_-s.js +243 -0
  44. package/dist/_chunks/rsc-cache-key-DD0fl_-s.js.map +1 -0
  45. package/dist/_chunks/rsc-error-envelope-tT5PJs4q.js +40 -0
  46. package/dist/_chunks/rsc-error-envelope-tT5PJs4q.js.map +1 -0
  47. package/dist/_chunks/rsc-media-type-DRqE_lD_.js +46 -0
  48. package/dist/_chunks/rsc-media-type-DRqE_lD_.js.map +1 -0
  49. package/dist/_chunks/rsc-payload-path-B_LBodc2.js +48 -0
  50. package/dist/_chunks/rsc-payload-path-B_LBodc2.js.map +1 -0
  51. package/dist/_chunks/scanner-C8b0Gcw3.js +1564 -0
  52. package/dist/_chunks/scanner-C8b0Gcw3.js.map +1 -0
  53. package/dist/_chunks/schema-bridge-Cc2Gngu1.js +199 -0
  54. package/dist/_chunks/schema-bridge-Cc2Gngu1.js.map +1 -0
  55. package/dist/_chunks/{segment-classify-Byy425ng.js → segment-classify-C539Pa2O.js} +23 -2
  56. package/dist/_chunks/segment-classify-C539Pa2O.js.map +1 -0
  57. package/dist/_chunks/segment-context-CjOlyB8Y.js +97 -0
  58. package/dist/_chunks/segment-context-CjOlyB8Y.js.map +1 -0
  59. package/dist/_chunks/{canonicalize-DQHyFClh.js → segment-keys-BawYuNFO.js} +61 -2
  60. package/dist/_chunks/segment-keys-BawYuNFO.js.map +1 -0
  61. package/dist/_chunks/slot-params-BCTmZkQB.js +76 -0
  62. package/dist/_chunks/slot-params-BCTmZkQB.js.map +1 -0
  63. package/dist/_chunks/{ssr-data-BOWsq18U.js → ssr-data-14MXm7Pj.js} +17 -2
  64. package/dist/_chunks/ssr-data-14MXm7Pj.js.map +1 -0
  65. package/dist/_chunks/{use-query-states-DFvWd-EA.js → use-query-states-BbU5Ge1V.js} +74 -26
  66. package/dist/_chunks/use-query-states-BbU5Ge1V.js.map +1 -0
  67. package/dist/_chunks/use-segment-params-C4r4BD9T.js +398 -0
  68. package/dist/_chunks/use-segment-params-C4r4BD9T.js.map +1 -0
  69. package/dist/_chunks/walkers-RzN6AFjr.js +141 -0
  70. package/dist/_chunks/walkers-RzN6AFjr.js.map +1 -0
  71. package/dist/adapters/build-output-helper.d.ts +37 -0
  72. package/dist/adapters/build-output-helper.d.ts.map +1 -1
  73. package/dist/adapters/cloudflare-dev.js +1 -1
  74. package/dist/adapters/cloudflare-kv-cache.js +1 -1
  75. package/dist/adapters/cloudflare.d.ts.map +1 -1
  76. package/dist/adapters/cloudflare.js +1 -1
  77. package/dist/adapters/compress-module.d.ts +12 -0
  78. package/dist/adapters/compress-module.d.ts.map +1 -1
  79. package/dist/adapters/fs-identity.d.ts +64 -0
  80. package/dist/adapters/fs-identity.d.ts.map +1 -0
  81. package/dist/adapters/nitro.d.ts.map +1 -1
  82. package/dist/adapters/nitro.js +56 -5
  83. package/dist/adapters/nitro.js.map +1 -1
  84. package/dist/adapters/shared.d.ts +57 -1
  85. package/dist/adapters/shared.d.ts.map +1 -1
  86. package/dist/adapters/types.d.ts +13 -1
  87. package/dist/adapters/types.d.ts.map +1 -1
  88. package/dist/cache/fast-hash.d.ts +5 -0
  89. package/dist/cache/fast-hash.d.ts.map +1 -1
  90. package/dist/cache/index.js +1 -1
  91. package/dist/cdn/cloudflare-purge.js +30 -0
  92. package/dist/cdn/cloudflare-purge.js.map +1 -0
  93. package/dist/cdn/fastly-purge.js +33 -0
  94. package/dist/cdn/fastly-purge.js.map +1 -0
  95. package/dist/cdn/index.js +94 -0
  96. package/dist/cdn/index.js.map +1 -0
  97. package/dist/cdn/workers-cache-purge.js +35 -0
  98. package/dist/cdn/workers-cache-purge.js.map +1 -0
  99. package/dist/cli-check.d.ts +153 -0
  100. package/dist/cli-check.d.ts.map +1 -0
  101. package/dist/cli.d.ts +34 -8
  102. package/dist/cli.d.ts.map +1 -1
  103. package/dist/cli.js +46 -21
  104. package/dist/cli.js.map +1 -1
  105. package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -1
  106. package/dist/client/browser-entry/hydrate.d.ts +20 -9
  107. package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
  108. package/dist/client/browser-entry/index.d.ts +1 -1
  109. package/dist/client/browser-entry/index.d.ts.map +1 -1
  110. package/dist/client/browser-entry/post-hydration.d.ts +4 -1
  111. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
  112. package/dist/client/browser-entry/router-init.d.ts +3 -0
  113. package/dist/client/browser-entry/router-init.d.ts.map +1 -1
  114. package/dist/client/browser-entry/rsc-stream.d.ts +8 -0
  115. package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -1
  116. package/dist/client/error-boundary.d.ts +7 -0
  117. package/dist/client/error-boundary.d.ts.map +1 -1
  118. package/dist/client/error-boundary.js +1 -162
  119. package/dist/client/history.d.ts +13 -6
  120. package/dist/client/history.d.ts.map +1 -1
  121. package/dist/client/index.d.ts +1 -0
  122. package/dist/client/index.d.ts.map +1 -1
  123. package/dist/client/index.js +42 -41
  124. package/dist/client/index.js.map +1 -1
  125. package/dist/client/internal.d.ts +4 -3
  126. package/dist/client/internal.d.ts.map +1 -1
  127. package/dist/client/internal.js +877 -311
  128. package/dist/client/internal.js.map +1 -1
  129. package/dist/client/link.d.ts +1 -7
  130. package/dist/client/link.d.ts.map +1 -1
  131. package/dist/client/navigation-api.d.ts +2 -2
  132. package/dist/client/navigation-commit.d.ts +87 -0
  133. package/dist/client/navigation-commit.d.ts.map +1 -0
  134. package/dist/client/navigation-context.d.ts +37 -20
  135. package/dist/client/navigation-context.d.ts.map +1 -1
  136. package/dist/client/navigation-root.d.ts +63 -23
  137. package/dist/client/navigation-root.d.ts.map +1 -1
  138. package/dist/client/params-context.d.ts +81 -0
  139. package/dist/client/params-context.d.ts.map +1 -0
  140. package/dist/client/router-effects.d.ts +129 -0
  141. package/dist/client/router-effects.d.ts.map +1 -0
  142. package/dist/client/router-lifecycle.d.ts +55 -0
  143. package/dist/client/router-lifecycle.d.ts.map +1 -0
  144. package/dist/client/router-pipeline.d.ts +84 -0
  145. package/dist/client/router-pipeline.d.ts.map +1 -0
  146. package/dist/client/router-ref.d.ts +1 -1
  147. package/dist/client/router-ref.d.ts.map +1 -1
  148. package/dist/client/router-skew.d.ts +21 -0
  149. package/dist/client/router-skew.d.ts.map +1 -0
  150. package/dist/client/router-types.d.ts +207 -0
  151. package/dist/client/router-types.d.ts.map +1 -0
  152. package/dist/client/router.d.ts +1 -182
  153. package/dist/client/router.d.ts.map +1 -1
  154. package/dist/client/rsc-fetch.d.ts +47 -26
  155. package/dist/client/rsc-fetch.d.ts.map +1 -1
  156. package/dist/client/segment-cache.d.ts +87 -34
  157. package/dist/client/segment-cache.d.ts.map +1 -1
  158. package/dist/client/ssr-data.d.ts +6 -0
  159. package/dist/client/ssr-data.d.ts.map +1 -1
  160. package/dist/client/stale-client.d.ts +69 -0
  161. package/dist/client/stale-client.d.ts.map +1 -0
  162. package/dist/client/state.d.ts +10 -1
  163. package/dist/client/state.d.ts.map +1 -1
  164. package/dist/client/top-loader.d.ts +2 -1
  165. package/dist/client/top-loader.d.ts.map +1 -1
  166. package/dist/client/unload-guard.d.ts +5 -0
  167. package/dist/client/unload-guard.d.ts.map +1 -1
  168. package/dist/client/use-cookie.d.ts.map +1 -1
  169. package/dist/client/use-query-states.d.ts +9 -3
  170. package/dist/client/use-query-states.d.ts.map +1 -1
  171. package/dist/client/use-router.d.ts +1 -1
  172. package/dist/client/use-segment-params.d.ts +18 -8
  173. package/dist/client/use-segment-params.d.ts.map +1 -1
  174. package/dist/codec.js +1 -1
  175. package/dist/cookies/define-cookie.d.ts.map +1 -1
  176. package/dist/cookies/index.js +5 -5
  177. package/dist/cookies/index.js.map +1 -1
  178. package/dist/index.d.ts +4 -1
  179. package/dist/index.d.ts.map +1 -1
  180. package/dist/index.js +1009 -759
  181. package/dist/index.js.map +1 -1
  182. package/dist/params/index.js +1 -1
  183. package/dist/plugin-context.d.ts +46 -0
  184. package/dist/plugin-context.d.ts.map +1 -1
  185. package/dist/plugins/adapter-build.d.ts.map +1 -1
  186. package/dist/plugins/build-manifest.d.ts +1 -1
  187. package/dist/plugins/build-manifest.d.ts.map +1 -1
  188. package/dist/plugins/entries.d.ts.map +1 -1
  189. package/dist/plugins/fonts.d.ts.map +1 -1
  190. package/dist/plugins/request-dep/analysis.d.ts +106 -0
  191. package/dist/plugins/request-dep/analysis.d.ts.map +1 -0
  192. package/dist/plugins/request-dep/index.d.ts +75 -0
  193. package/dist/plugins/request-dep/index.d.ts.map +1 -0
  194. package/dist/plugins/request-dep/origin.d.ts +61 -0
  195. package/dist/plugins/request-dep/origin.d.ts.map +1 -0
  196. package/dist/plugins/routing.d.ts.map +1 -1
  197. package/dist/plugins/shims.d.ts.map +1 -1
  198. package/dist/plugins/static-build.d.ts +160 -8
  199. package/dist/plugins/static-build.d.ts.map +1 -1
  200. package/dist/routing/codegen-shared.d.ts +3 -44
  201. package/dist/routing/codegen-shared.d.ts.map +1 -1
  202. package/dist/routing/codegen-types.d.ts +28 -31
  203. package/dist/routing/codegen-types.d.ts.map +1 -1
  204. package/dist/routing/codegen-write.d.ts +51 -0
  205. package/dist/routing/codegen-write.d.ts.map +1 -0
  206. package/dist/routing/codegen.d.ts.map +1 -1
  207. package/dist/routing/collision-probe.d.ts +104 -0
  208. package/dist/routing/collision-probe.d.ts.map +1 -0
  209. package/dist/routing/collision-spaces.d.ts +26 -0
  210. package/dist/routing/collision-spaces.d.ts.map +1 -0
  211. package/dist/routing/convention-lint.d.ts +18 -4
  212. package/dist/routing/convention-lint.d.ts.map +1 -1
  213. package/dist/routing/export-detect.d.ts +16 -0
  214. package/dist/routing/export-detect.d.ts.map +1 -1
  215. package/dist/routing/index.js +4 -3
  216. package/dist/routing/interception.d.ts +235 -6
  217. package/dist/routing/interception.d.ts.map +1 -1
  218. package/dist/routing/link-codegen.d.ts +19 -4
  219. package/dist/routing/link-codegen.d.ts.map +1 -1
  220. package/dist/routing/manifest-codegen.d.ts +1 -7
  221. package/dist/routing/manifest-codegen.d.ts.map +1 -1
  222. package/dist/routing/scanner.d.ts.map +1 -1
  223. package/dist/routing/segment-classify.d.ts +33 -0
  224. package/dist/routing/segment-classify.d.ts.map +1 -1
  225. package/dist/routing/segment-keys.d.ts +95 -0
  226. package/dist/routing/segment-keys.d.ts.map +1 -0
  227. package/dist/routing/slot-placement.d.ts +48 -0
  228. package/dist/routing/slot-placement.d.ts.map +1 -0
  229. package/dist/routing/types.d.ts +0 -6
  230. package/dist/routing/types.d.ts.map +1 -1
  231. package/dist/routing/walkers.d.ts +6 -0
  232. package/dist/routing/walkers.d.ts.map +1 -1
  233. package/dist/schema-bridge.d.ts +60 -9
  234. package/dist/schema-bridge.d.ts.map +1 -1
  235. package/dist/search-params/define.d.ts +62 -14
  236. package/dist/search-params/define.d.ts.map +1 -1
  237. package/dist/search-params/index.d.ts +0 -1
  238. package/dist/search-params/index.d.ts.map +1 -1
  239. package/dist/search-params/index.js +221 -10
  240. package/dist/search-params/index.js.map +1 -1
  241. package/dist/search-params/parse-total.d.ts +70 -0
  242. package/dist/search-params/parse-total.d.ts.map +1 -0
  243. package/dist/search-params/wrappers.d.ts +26 -3
  244. package/dist/search-params/wrappers.d.ts.map +1 -1
  245. package/dist/server/access-gate.d.ts +19 -8
  246. package/dist/server/access-gate.d.ts.map +1 -1
  247. package/dist/server/action-handler.d.ts.map +1 -1
  248. package/dist/server/actions.d.ts +6 -3
  249. package/dist/server/actions.d.ts.map +1 -1
  250. package/dist/server/als-registry.d.ts +18 -1
  251. package/dist/server/als-registry.d.ts.map +1 -1
  252. package/dist/server/build-manifest.d.ts +13 -1
  253. package/dist/server/build-manifest.d.ts.map +1 -1
  254. package/dist/server/chain-url-parts.d.ts +82 -0
  255. package/dist/server/chain-url-parts.d.ts.map +1 -0
  256. package/dist/server/children-interception.d.ts +49 -0
  257. package/dist/server/children-interception.d.ts.map +1 -0
  258. package/dist/server/compress.d.ts.map +1 -1
  259. package/dist/server/default-status-page.d.ts +45 -0
  260. package/dist/server/default-status-page.d.ts.map +1 -0
  261. package/dist/server/deny-boundary.d.ts +148 -15
  262. package/dist/server/deny-boundary.d.ts.map +1 -1
  263. package/dist/server/deny-renderer.d.ts +15 -4
  264. package/dist/server/deny-renderer.d.ts.map +1 -1
  265. package/dist/server/error-boundary-wrapper.d.ts +85 -15
  266. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  267. package/dist/server/head-response.d.ts +35 -0
  268. package/dist/server/head-response.d.ts.map +1 -0
  269. package/dist/server/index.d.ts +0 -1
  270. package/dist/server/index.d.ts.map +1 -1
  271. package/dist/server/index.js +3 -2
  272. package/dist/server/index.js.map +1 -1
  273. package/dist/server/internal.d.ts +3 -1
  274. package/dist/server/internal.d.ts.map +1 -1
  275. package/dist/server/internal.js +712 -418
  276. package/dist/server/internal.js.map +1 -1
  277. package/dist/server/metadata-collector.d.ts +52 -0
  278. package/dist/server/metadata-collector.d.ts.map +1 -0
  279. package/dist/server/param-coercion.d.ts +22 -6
  280. package/dist/server/param-coercion.d.ts.map +1 -1
  281. package/dist/server/pipeline-helpers.d.ts +0 -18
  282. package/dist/server/pipeline-helpers.d.ts.map +1 -1
  283. package/dist/server/pipeline-interception.d.ts +59 -9
  284. package/dist/server/pipeline-interception.d.ts.map +1 -1
  285. package/dist/server/pipeline-outcome.d.ts.map +1 -1
  286. package/dist/server/pipeline-phases.d.ts.map +1 -1
  287. package/dist/server/pipeline.d.ts +11 -0
  288. package/dist/server/pipeline.d.ts.map +1 -1
  289. package/dist/server/primitives.d.ts +23 -0
  290. package/dist/server/primitives.d.ts.map +1 -1
  291. package/dist/server/publish-params.d.ts +33 -0
  292. package/dist/server/publish-params.d.ts.map +1 -0
  293. package/dist/server/request-context.d.ts +48 -0
  294. package/dist/server/request-context.d.ts.map +1 -1
  295. package/dist/server/route-element-builder.d.ts +22 -3
  296. package/dist/server/route-element-builder.d.ts.map +1 -1
  297. package/dist/server/route-handler.d.ts.map +1 -1
  298. package/dist/server/route-matcher.d.ts +13 -0
  299. package/dist/server/route-matcher.d.ts.map +1 -1
  300. package/dist/server/rsc-cache-key-guard.d.ts +78 -0
  301. package/dist/server/rsc-cache-key-guard.d.ts.map +1 -0
  302. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
  303. package/dist/server/rsc-entry/deny-fallback.d.ts.map +1 -1
  304. package/dist/server/rsc-entry/error-renderer.d.ts +15 -3
  305. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  306. package/dist/server/rsc-entry/helpers.d.ts +2 -17
  307. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  308. package/dist/server/rsc-entry/index.d.ts +0 -1
  309. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  310. package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
  311. package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -1
  312. package/dist/server/rsc-entry/rsc-payload.d.ts +1 -2
  313. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  314. package/dist/server/rsc-entry/rsc-stream.d.ts +15 -2
  315. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  316. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  317. package/dist/server/skippable-prefix.d.ts +23 -0
  318. package/dist/server/skippable-prefix.d.ts.map +1 -0
  319. package/dist/server/slot-interception.d.ts +80 -0
  320. package/dist/server/slot-interception.d.ts.map +1 -0
  321. package/dist/server/slot-resolver.d.ts +70 -8
  322. package/dist/server/slot-resolver.d.ts.map +1 -1
  323. package/dist/server/ssr-bridge-types.d.ts +17 -0
  324. package/dist/server/ssr-bridge-types.d.ts.map +1 -1
  325. package/dist/server/ssr-entry.d.ts +0 -1
  326. package/dist/server/ssr-entry.d.ts.map +1 -1
  327. package/dist/server/ssr-wrappers.d.ts +24 -13
  328. package/dist/server/ssr-wrappers.d.ts.map +1 -1
  329. package/dist/server/state-tree-diff.d.ts +6 -27
  330. package/dist/server/state-tree-diff.d.ts.map +1 -1
  331. package/dist/server/static-generator.d.ts +6 -0
  332. package/dist/server/static-generator.d.ts.map +1 -1
  333. package/dist/server/static-not-found.d.ts +52 -0
  334. package/dist/server/static-not-found.d.ts.map +1 -0
  335. package/dist/server/status-code-resolver.d.ts +8 -1
  336. package/dist/server/status-code-resolver.d.ts.map +1 -1
  337. package/dist/server/tree-builder.d.ts +28 -36
  338. package/dist/server/tree-builder.d.ts.map +1 -1
  339. package/dist/server/types.d.ts +12 -7
  340. package/dist/server/types.d.ts.map +1 -1
  341. package/dist/server/utils/element-type.d.ts +40 -0
  342. package/dist/server/utils/element-type.d.ts.map +1 -0
  343. package/dist/shared/als-slots.d.ts +41 -0
  344. package/dist/shared/als-slots.d.ts.map +1 -0
  345. package/dist/shared/base-path.d.ts +56 -0
  346. package/dist/shared/base-path.d.ts.map +1 -0
  347. package/dist/shared/param-value.d.ts +80 -0
  348. package/dist/shared/param-value.d.ts.map +1 -0
  349. package/dist/shared/payload-root.d.ts +108 -0
  350. package/dist/shared/payload-root.d.ts.map +1 -0
  351. package/dist/shared/rsc-cache-key.d.ts +163 -0
  352. package/dist/shared/rsc-cache-key.d.ts.map +1 -0
  353. package/dist/shared/rsc-error-envelope.d.ts +25 -0
  354. package/dist/shared/rsc-error-envelope.d.ts.map +1 -0
  355. package/dist/shared/rsc-manifest.d.ts +28 -0
  356. package/dist/shared/rsc-manifest.d.ts.map +1 -0
  357. package/dist/shared/rsc-media-type.d.ts +40 -0
  358. package/dist/shared/rsc-media-type.d.ts.map +1 -0
  359. package/dist/shared/rsc-payload-path.d.ts +42 -0
  360. package/dist/shared/rsc-payload-path.d.ts.map +1 -0
  361. package/dist/shared/segment-info.d.ts +59 -0
  362. package/dist/shared/segment-info.d.ts.map +1 -0
  363. package/dist/shared/slot-params.d.ts +51 -0
  364. package/dist/shared/slot-params.d.ts.map +1 -0
  365. package/dist/shared/static-platform-files.d.ts +71 -0
  366. package/dist/shared/static-platform-files.d.ts.map +1 -0
  367. package/docs/api/30-api-server.mdx +1 -1
  368. package/docs/api/31-api-client.mdx +4 -2
  369. package/docs/api/33-api-search-params.mdx +38 -16
  370. package/docs/api/35-api-typescript.mdx +3 -3
  371. package/docs/api/36-cli.mdx +34 -7
  372. package/docs/learn/00-introduction.mdx +1 -1
  373. package/docs/learn/02-pages-and-layouts.mdx +2 -2
  374. package/docs/learn/05-typed-params.mdx +8 -8
  375. package/docs/learn/07-typed-routes.mdx +12 -7
  376. package/docs/learn/11-error-handling.mdx +18 -0
  377. package/docs/learn/12-client-navigation.mdx +2 -2
  378. package/docs/learn/14-deploying.mdx +23 -0
  379. package/docs/more/01-advanced-routing.mdx +173 -3
  380. package/docs/more/03-coming-from-nextjs.mdx +2 -2
  381. package/docs/more/04-metadata-and-fonts.mdx +1 -1
  382. package/docs/more/50-ai-agent-instructions.mdx +6 -4
  383. package/package.json +9 -6
  384. package/src/adapters/build-output-helper.ts +116 -3
  385. package/src/adapters/cloudflare.ts +90 -12
  386. package/src/adapters/compress-module.ts +79 -1
  387. package/src/adapters/fs-identity.ts +156 -0
  388. package/src/adapters/nitro.ts +6 -6
  389. package/src/adapters/shared.ts +196 -21
  390. package/src/adapters/types.ts +13 -1
  391. package/src/cache/fast-hash.ts +5 -0
  392. package/src/cli-check.ts +458 -0
  393. package/src/cli.ts +59 -24
  394. package/src/client/browser-entry/action-dispatch.ts +11 -5
  395. package/src/client/browser-entry/hydrate.ts +40 -29
  396. package/src/client/browser-entry/index.ts +15 -6
  397. package/src/client/browser-entry/post-hydration.ts +10 -9
  398. package/src/client/browser-entry/router-init.ts +104 -48
  399. package/src/client/browser-entry/rsc-stream.ts +11 -2
  400. package/src/client/error-boundary.tsx +85 -16
  401. package/src/client/history.ts +13 -6
  402. package/src/client/index.ts +14 -3
  403. package/src/client/internal.ts +4 -3
  404. package/src/client/link.tsx +120 -72
  405. package/src/client/navigation-api.ts +3 -3
  406. package/src/client/navigation-commit.ts +197 -0
  407. package/src/client/navigation-context.ts +40 -67
  408. package/src/client/navigation-root.tsx +326 -101
  409. package/src/client/params-context.ts +192 -0
  410. package/src/client/router-effects.ts +231 -0
  411. package/src/client/router-lifecycle.ts +288 -0
  412. package/src/client/router-pipeline.ts +393 -0
  413. package/src/client/router-ref.ts +1 -1
  414. package/src/client/router-skew.ts +30 -0
  415. package/src/client/router-types.ts +230 -0
  416. package/src/client/router.ts +243 -707
  417. package/src/client/rsc-fetch.ts +183 -106
  418. package/src/client/segment-cache.ts +129 -44
  419. package/src/client/ssr-data.ts +6 -0
  420. package/src/client/stale-client.ts +191 -0
  421. package/src/client/state.ts +14 -1
  422. package/src/client/top-loader.tsx +2 -1
  423. package/src/client/unload-guard.ts +34 -2
  424. package/src/client/use-cookie.ts +9 -2
  425. package/src/client/use-query-states.ts +102 -39
  426. package/src/client/use-router.ts +1 -1
  427. package/src/client/use-segment-params.ts +41 -18
  428. package/src/cookies/define-cookie.ts +6 -1
  429. package/src/index.ts +25 -4
  430. package/src/plugin-context.ts +58 -0
  431. package/src/plugins/adapter-build.ts +4 -0
  432. package/src/plugins/build-manifest.ts +34 -13
  433. package/src/plugins/entries.ts +4 -0
  434. package/src/plugins/fonts.ts +11 -8
  435. package/src/plugins/request-dep/analysis.ts +447 -0
  436. package/src/plugins/request-dep/index.ts +376 -0
  437. package/src/plugins/request-dep/origin.ts +310 -0
  438. package/src/plugins/routing.ts +97 -102
  439. package/src/plugins/shims.ts +0 -1
  440. package/src/plugins/static-build.ts +391 -95
  441. package/src/routing/codegen-shared.ts +3 -79
  442. package/src/routing/codegen-types.ts +28 -31
  443. package/src/routing/codegen-write.ts +139 -0
  444. package/src/routing/codegen.ts +86 -172
  445. package/src/routing/collision-probe.ts +438 -0
  446. package/src/routing/collision-spaces.ts +281 -0
  447. package/src/routing/convention-lint.ts +139 -40
  448. package/src/routing/export-detect.ts +151 -7
  449. package/src/routing/interception.ts +896 -50
  450. package/src/routing/link-codegen.ts +32 -65
  451. package/src/routing/manifest-codegen.ts +6 -62
  452. package/src/routing/scanner.ts +117 -92
  453. package/src/routing/segment-classify.ts +57 -0
  454. package/src/routing/segment-keys.ts +146 -0
  455. package/src/routing/slot-placement.ts +97 -0
  456. package/src/routing/types.ts +0 -6
  457. package/src/routing/walkers.ts +8 -0
  458. package/src/schema-bridge.ts +180 -58
  459. package/src/search-params/define.ts +108 -66
  460. package/src/search-params/index.ts +0 -1
  461. package/src/search-params/parse-total.ts +78 -0
  462. package/src/search-params/wrappers.ts +60 -11
  463. package/src/server/access-gate.tsx +60 -40
  464. package/src/server/action-handler.ts +4 -5
  465. package/src/server/actions.ts +6 -3
  466. package/src/server/als-registry.ts +18 -1
  467. package/src/server/build-manifest.ts +14 -1
  468. package/src/server/chain-url-parts.ts +163 -0
  469. package/src/server/children-interception.ts +115 -0
  470. package/src/server/compress.ts +9 -1
  471. package/src/server/default-status-page.ts +127 -0
  472. package/src/server/deny-boundary.ts +269 -41
  473. package/src/server/deny-renderer.ts +111 -39
  474. package/src/server/error-boundary-wrapper.ts +166 -79
  475. package/src/server/head-response.ts +54 -0
  476. package/src/server/index.ts +1 -3
  477. package/src/server/internal.ts +2 -2
  478. package/src/server/metadata-collector.ts +115 -0
  479. package/src/server/param-coercion.ts +51 -69
  480. package/src/server/pipeline-helpers.ts +2 -47
  481. package/src/server/pipeline-interception.ts +81 -32
  482. package/src/server/pipeline-outcome.ts +4 -5
  483. package/src/server/pipeline-phases.ts +77 -25
  484. package/src/server/pipeline.ts +57 -10
  485. package/src/server/prebuilt-runtime.ts +4 -4
  486. package/src/server/primitives.ts +25 -0
  487. package/src/server/publish-params.ts +42 -0
  488. package/src/server/request-context.ts +97 -18
  489. package/src/server/route-element-builder.ts +241 -253
  490. package/src/server/route-handler.ts +8 -8
  491. package/src/server/route-matcher.ts +21 -9
  492. package/src/server/rsc-cache-key-guard.ts +164 -0
  493. package/src/server/rsc-entry/api-handler.ts +6 -8
  494. package/src/server/rsc-entry/deny-fallback.ts +3 -2
  495. package/src/server/rsc-entry/error-renderer.ts +94 -35
  496. package/src/server/rsc-entry/helpers.ts +33 -28
  497. package/src/server/rsc-entry/index.ts +36 -33
  498. package/src/server/rsc-entry/render-route.ts +18 -10
  499. package/src/server/rsc-entry/revalidate-renderer.ts +3 -2
  500. package/src/server/rsc-entry/rsc-payload.ts +113 -40
  501. package/src/server/rsc-entry/rsc-stream.ts +29 -5
  502. package/src/server/rsc-entry/ssr-renderer.ts +38 -22
  503. package/src/server/skippable-prefix.ts +92 -0
  504. package/src/server/slot-interception.ts +168 -0
  505. package/src/server/slot-resolver.ts +263 -158
  506. package/src/server/ssr-bridge-types.ts +18 -0
  507. package/src/server/ssr-entry.ts +20 -10
  508. package/src/server/ssr-wrappers.tsx +33 -32
  509. package/src/server/state-tree-diff.ts +14 -55
  510. package/src/server/static-generator.ts +68 -29
  511. package/src/server/static-not-found.ts +134 -0
  512. package/src/server/status-code-resolver.ts +28 -11
  513. package/src/server/tree-builder.ts +35 -218
  514. package/src/server/types.ts +12 -7
  515. package/src/server/utils/element-type.ts +72 -0
  516. package/src/shared/als-slots.ts +72 -0
  517. package/src/shared/base-path.ts +80 -0
  518. package/src/shared/param-value.ts +244 -0
  519. package/src/shared/payload-root.ts +151 -0
  520. package/src/shared/rsc-cache-key.ts +269 -0
  521. package/src/shared/rsc-error-envelope.ts +31 -0
  522. package/src/shared/rsc-manifest.ts +29 -0
  523. package/src/shared/rsc-media-type.ts +43 -0
  524. package/src/shared/rsc-payload-path.ts +45 -0
  525. package/src/shared/segment-info.ts +60 -0
  526. package/src/shared/slot-params.ts +111 -0
  527. package/src/shared/static-platform-files.ts +75 -0
  528. package/dist/_chunks/build-output-helper-DOGYFb_X.js +0 -196
  529. package/dist/_chunks/build-output-helper-DOGYFb_X.js.map +0 -1
  530. package/dist/_chunks/cache-api-B-lhk9p4.js.map +0 -1
  531. package/dist/_chunks/canonicalize-DQHyFClh.js.map +0 -1
  532. package/dist/_chunks/cli-schema-sync-BTWEKJXo.js +0 -668
  533. package/dist/_chunks/cli-schema-sync-BTWEKJXo.js.map +0 -1
  534. package/dist/_chunks/define-COtkxMRT.js +0 -191
  535. package/dist/_chunks/define-COtkxMRT.js.map +0 -1
  536. package/dist/_chunks/define-c4au4I9R.js +0 -82
  537. package/dist/_chunks/define-c4au4I9R.js.map +0 -1
  538. package/dist/_chunks/fast-hash-D6hIVt1Y.js.map +0 -1
  539. package/dist/_chunks/logger-AWfuX-KJ.js.map +0 -1
  540. package/dist/_chunks/navigation-root-bUDZLw74.js +0 -124
  541. package/dist/_chunks/navigation-root-bUDZLw74.js.map +0 -1
  542. package/dist/_chunks/plugin-context---kTF5v8.js.map +0 -1
  543. package/dist/_chunks/registry-DbJPKoBp.js +0 -20
  544. package/dist/_chunks/registry-DbJPKoBp.js.map +0 -1
  545. package/dist/_chunks/router-ref-CtmF-aPv.js.map +0 -1
  546. package/dist/_chunks/schema-bridge-DT_Tn0Xf.js +0 -119
  547. package/dist/_chunks/schema-bridge-DT_Tn0Xf.js.map +0 -1
  548. package/dist/_chunks/segment-classify-Byy425ng.js.map +0 -1
  549. package/dist/_chunks/segment-context-ZDnXDkbz.js +0 -34
  550. package/dist/_chunks/segment-context-ZDnXDkbz.js.map +0 -1
  551. package/dist/_chunks/ssr-data-BOWsq18U.js.map +0 -1
  552. package/dist/_chunks/use-query-states-DFvWd-EA.js.map +0 -1
  553. package/dist/_chunks/use-segment-params-D1CzlgBo.js +0 -151
  554. package/dist/_chunks/use-segment-params-D1CzlgBo.js.map +0 -1
  555. package/dist/_chunks/walkers-_6zKFlch.js +0 -1002
  556. package/dist/_chunks/walkers-_6zKFlch.js.map +0 -1
  557. package/dist/client/error-boundary.js.map +0 -1
  558. package/dist/plugins/request-dep.d.ts +0 -32
  559. package/dist/plugins/request-dep.d.ts.map +0 -1
  560. package/dist/search-params/registry.d.ts +0 -20
  561. package/dist/search-params/registry.d.ts.map +0 -1
  562. package/dist/segment-params/define.d.ts +0 -89
  563. package/dist/segment-params/define.d.ts.map +0 -1
  564. package/dist/segment-params/index.d.ts +0 -3
  565. package/dist/segment-params/index.d.ts.map +0 -1
  566. package/dist/segment-params/index.js +0 -2
  567. package/src/plugins/request-dep.ts +0 -499
  568. package/src/search-params/registry.ts +0 -31
  569. package/src/segment-params/define.ts +0 -249
  570. package/src/segment-params/index.ts +0 -9
@@ -9,14 +9,18 @@
9
9
  * NavigationRoot holds the current element in React state. Navigation
10
10
  * updates call startTransition(() => setState(newElement)), which IS
11
11
  * a transition update. React keeps the old committed tree visible while
12
- * any new Suspense boundaries in the transition resolve.
12
+ * the new tree resolves, instead of hiding it behind a Suspense fallback.
13
13
  *
14
- * Also manages `pendingUrl` as React state with an urgent/transition split:
15
- * - Navigation START: `setPendingUrl(url)` is an urgent update — React
16
- * commits it before the next paint, showing the spinner immediately.
17
- * - Navigation END: `setPendingUrl(null)` is inside `startTransition`
18
- * alongside `setElement(newTree)` — both commit atomically, so the
19
- * spinner disappears in the same frame as the new content appears.
14
+ * The navigation's async work runs OUTSIDE the transition scope and every
15
+ * state update it schedules gets its own synchronous `startTransition` — a
16
+ * transition scope does not survive an `await`, and an async callback that
17
+ * returns a thenable defers the commit past the promise callers await. See
18
+ * the long comment on `_navigateTransition` (TIM-1306).
19
+ *
20
+ * This component holds no pending state. The `TopLoader` it renders and the
21
+ * public `usePendingNavigation()` both subscribe to the router's external
22
+ * pending store; NavigationRoot's own `pendingUrl` was a second, unread
23
+ * representation of the same fact and is gone (TIM-1307).
20
24
  *
21
25
  * Hard navigation guard: When a hard navigation is triggered (500 error,
22
26
  * version skew), the component throws an unresolved thenable AFTER all
@@ -31,10 +35,56 @@
31
35
  * See design/19-client-navigation.md §"NavigationContext"
32
36
  */
33
37
 
34
- import { createElement, Fragment, startTransition, useState, type ReactNode } from 'react';
35
- import { PendingNavigationProvider } from './navigation-context.js';
38
+ import {
39
+ createElement,
40
+ Fragment,
41
+ startTransition,
42
+ useLayoutEffect,
43
+ useRef,
44
+ useState,
45
+ type ReactNode,
46
+ } from 'react';
36
47
  import { TopLoader, type TopLoaderConfig } from './top-loader.js';
37
48
 
49
+ // ─── Transition Result ──────────────────────────────────────────
50
+
51
+ /**
52
+ * What a navigation's `perform()` hands back to the transition.
53
+ *
54
+ * Declared once and shared by every layer that passes it along — the router's
55
+ * `RouterDeps.navigateTransition` imports it too — so a field cannot be added
56
+ * to the producer and dropped by the adapter in between. `E` is the element
57
+ * type: `ReactNode` here, `unknown` in the router, which never touches it.
58
+ */
59
+ export interface TransitionResult<E = ReactNode> {
60
+ /** The wrapped tree to render. */
61
+ element: E;
62
+ /** Resolves when the Flight stream finishes decoding, or null. */
63
+ decodePromise: Promise<void> | null;
64
+ /**
65
+ * Publishes the navigation's state — segment cache, pathname, address bar,
66
+ * history stack, and the client's record of the mounted tree — and
67
+ * announces it to listeners outside React.
68
+ *
69
+ * Run when React commits this tree, not when the tree is handed over: a
70
+ * tree React is still waiting on, or one a later navigation replaces first,
71
+ * has been given to React without being on screen. A superseded navigation
72
+ * never runs it and leaves every one of those consumers describing the
73
+ * route still on screen (TIM-1301).
74
+ */
75
+ commit: () => void;
76
+ }
77
+
78
+ /**
79
+ * The tree NavigationRoot holds in state, and the state publish that belongs
80
+ * to it. They travel as one object so React's commit of the tree is the event
81
+ * that publishes — see the effect in NavigationRoot.
82
+ */
83
+ interface RenderedTree {
84
+ element: ReactNode;
85
+ publish: (() => void) | null;
86
+ }
87
+
38
88
  // ─── Navigation Transition Counter ──────────────────────────────
39
89
  // Monotonically increasing counter that increments each time
40
90
  // navigateTransition() is called. Used to detect stale transitions:
@@ -50,12 +100,37 @@ import { TopLoader, type TopLoaderConfig } from './top-loader.js';
50
100
 
51
101
  const NAV_TRANSITION_KEY = Symbol.for('__timber_nav_transition_counter');
52
102
 
53
- function getTransitionCounter(): { id: number } {
103
+ /**
104
+ * `waiters` are woken on every bump so an in-flight navigation can stop
105
+ * waiting on its own payload the moment it is superseded — see
106
+ * `settleOnDecodeOrSupersession`.
107
+ */
108
+ interface TransitionCounter {
109
+ id: number;
110
+ waiters: Set<() => void>;
111
+ }
112
+
113
+ function getTransitionCounter(): TransitionCounter {
54
114
  const g = globalThis as Record<symbol, unknown>;
55
- if (!g[NAV_TRANSITION_KEY]) {
56
- g[NAV_TRANSITION_KEY] = { id: 0 };
115
+ const existing = g[NAV_TRANSITION_KEY] as Partial<TransitionCounter> | undefined;
116
+ if (!existing) {
117
+ const created: TransitionCounter = { id: 0, waiters: new Set() };
118
+ g[NAV_TRANSITION_KEY] = created;
119
+ return created;
57
120
  }
58
- return g[NAV_TRANSITION_KEY] as { id: number };
121
+ // A duplicated copy of this module may have created the singleton before
122
+ // `waiters` existed. The object is shared across chunks, so fill it in
123
+ // rather than replacing it — replacing would strand the other copy's id.
124
+ existing.waiters ??= new Set();
125
+ return existing as TransitionCounter;
126
+ }
127
+
128
+ /** Bump the counter and wake everything waiting on an older transition. */
129
+ function bumpTransitionCounter(): number {
130
+ const counter = getTransitionCounter();
131
+ counter.id += 1;
132
+ for (const wake of [...counter.waiters]) wake();
133
+ return counter.id;
59
134
  }
60
135
 
61
136
  /**
@@ -71,7 +146,54 @@ function getTransitionCounter(): { id: number } {
71
146
  * replayed back page (TIM-1022).
72
147
  */
73
148
  export function supersedeNavigationTransitions(): void {
74
- getTransitionCounter().id += 1;
149
+ bumpTransitionCounter();
150
+ }
151
+
152
+ /**
153
+ * Wait for the payload to finish decoding, OR for this transition to be
154
+ * superseded — whichever happens first.
155
+ *
156
+ * A superseded navigation must stop waiting on its own stream. The stream is
157
+ * deliberately NOT aborted once its tree has been handed to React (the tree
158
+ * may be on screen with boundaries still feeding from it — see
159
+ * `handedOffNavAbort` in `client/router.ts`), so there is nothing left to make
160
+ * `decodePromise` settle promptly. Awaiting it bare would keep the loser's
161
+ * `router.navigate()` promise pending for the rest of the stream — and
162
+ * forever if it stalls — which is what `<Link>`'s `isPending` is timed
163
+ * against, so the losing link would sit spinning while the winner loaded
164
+ * (codex on #1004).
165
+ *
166
+ * A decode *failure* still propagates: it is a real error for this
167
+ * navigation, and the caller's recovery is timed against it.
168
+ */
169
+ function settleOnDecodeOrSupersession(
170
+ decodePromise: Promise<void>,
171
+ counter: TransitionCounter,
172
+ transId: number
173
+ ): Promise<void> {
174
+ if (counter.id !== transId) return Promise.resolve();
175
+ return new Promise<void>((resolve, reject) => {
176
+ const stopWaiting = (): void => {
177
+ counter.waiters.delete(wake);
178
+ };
179
+ const wake = (): void => {
180
+ if (counter.id !== transId) {
181
+ stopWaiting();
182
+ resolve();
183
+ }
184
+ };
185
+ counter.waiters.add(wake);
186
+ decodePromise.then(
187
+ () => {
188
+ stopWaiting();
189
+ resolve();
190
+ },
191
+ (error: unknown) => {
192
+ stopWaiting();
193
+ reject(error instanceof Error ? error : new Error(String(error)));
194
+ }
195
+ );
196
+ });
75
197
  }
76
198
 
77
199
  // ─── Hard Navigation Guard ──────────────────────────────────────
@@ -143,14 +265,13 @@ let _transitionRender: ((element: ReactNode) => void) | null = null;
143
265
 
144
266
  /**
145
267
  * Module-level reference to the navigation transition function.
146
- * Wraps a full navigation (fetch + render) in a single startTransition
147
- * with the pending URL.
268
+ *
269
+ * Runs the fetch OUTSIDE any transition scope and wraps only the state update
270
+ * that hands the resulting tree to React — describing this as "a full
271
+ * navigation in a single startTransition" is the shape TIM-1306 removed.
148
272
  */
149
273
  let _navigateTransition:
150
- | ((
151
- pendingUrl: string,
152
- perform: () => Promise<{ element: ReactNode; decodePromise: Promise<void> | null }>
153
- ) => Promise<void>)
274
+ | ((url: string, perform: () => Promise<TransitionResult>) => Promise<void>)
154
275
  | null = null;
155
276
 
156
277
  // ─── Component ───────────────────────────────────────────────────
@@ -158,9 +279,8 @@ let _navigateTransition:
158
279
  /**
159
280
  * Root wrapper component that enables transition-based rendering.
160
281
  *
161
- * Renders PendingNavigationProvider around children for the pending URL
162
- * context. The DOM tree matches the server-rendered HTML during hydration
163
- * (the provider renders no extra DOM elements).
282
+ * Renders the TopLoader alongside the tree it holds in state. Neither adds a
283
+ * DOM element on the hydration path, so the tree matches the server HTML.
164
284
  *
165
285
  * Usage in browser-entry.ts:
166
286
  * const rootEl = createElement(NavigationRoot, { initial: wrapped });
@@ -179,8 +299,39 @@ export function NavigationRoot({
179
299
  initial: ReactNode;
180
300
  topLoaderConfig?: TopLoaderConfig;
181
301
  }): ReactNode {
182
- const [element, setElement] = useState<ReactNode>(initial);
183
- const [pendingUrl, setPendingUrl] = useState<string | null>(null);
302
+ const [rendered, setRendered] = useState<RenderedTree>({ element: initial, publish: null });
303
+
304
+ // Publish the navigation's state when React commits its tree — not when the
305
+ // tree is handed over. A tree React is still waiting on, or one a later
306
+ // navigation replaces before React renders it, has been *given* to React
307
+ // without being on screen; publishing then describes a route the user never
308
+ // saw, which is the defect this ordering exists to prevent (TIM-1301).
309
+ //
310
+ // A **layout** effect, so the publish lands before the browser paints and,
311
+ // more importantly, before every descendant's passive effect. A destination
312
+ // component that navigates from a mount effect — a redirect guard, a
313
+ // `refresh()` on mount — would otherwise start its navigation while the
314
+ // segment cache, address bar and pathname still described the departing
315
+ // route, and send an X-Timber-State-Tree for a tree that is no longer
316
+ // mounted (codex on #998).
317
+ //
318
+ // Descendant *layout* effects still run first: React runs layout effects
319
+ // child-before-parent, and the only way to precede them would be a fiber
320
+ // rendered as an earlier sibling of the payload. That is not free here —
321
+ // `server/ssr-wrappers.tsx` mirrors this component's fiber shape so `useId`
322
+ // agrees across hydration, and an extra sibling shifts every id in the
323
+ // payload subtree. A layout effect that navigates on mount is the price.
324
+ //
325
+ // Keyed on the tree object rather than on the effect run. A publish moves
326
+ // the address bar, which is not idempotent, so any second invocation for the
327
+ // same tree — an effect re-run React is entitled to perform — must be a
328
+ // no-op rather than a second history entry.
329
+ const publishedRef = useRef<RenderedTree | null>(null);
330
+ useLayoutEffect(() => {
331
+ if (publishedRef.current === rendered) return;
332
+ publishedRef.current = rendered;
333
+ rendered.publish?.();
334
+ }, [rendered]);
184
335
 
185
336
  // NOTE: We use standalone `startTransition` (imported from 'react'),
186
337
  // NOT `useTransition`. The `useTransition` hook's `startTransition`
@@ -192,67 +343,118 @@ export function NavigationRoot({
192
343
  //
193
344
  // Standalone `startTransition` creates independent transition lanes
194
345
  // for each call, so concurrent navigations don't interfere. We don't
195
- // need useTransition's `isPending` — we track pending state via our
196
- // own `pendingUrl` useState.
346
+ // need useTransition's `isPending` — pending state lives in the router's
347
+ // external store, which TopLoader and usePendingNavigation() subscribe to.
197
348
  //
198
349
  // This matches the Next.js pattern (TIM-625): "No useTransition in
199
350
  // the router at all — only standalone startTransition."
200
351
 
201
352
  // Non-navigation render (revalidation, popstate cached replay).
353
+ // Non-navigation render (revalidation, popstate cached replay). Both publish
354
+ // synchronously in the router before calling this — neither has an in-flight
355
+ // window in which it could be superseded — so there is nothing to publish
356
+ // on commit.
202
357
  _transitionRender = (newElement: ReactNode) => {
203
358
  startTransition(() => {
204
- setElement(newElement);
359
+ setRendered({ element: newElement, publish: null });
205
360
  });
206
361
  };
207
362
 
208
363
  // Full navigation transition.
209
- // setPendingUrl(url) is an URGENT update — React commits it before the next
210
- // paint, so the pending spinner appears immediately when navigation starts.
211
- // Inside startTransition: the async fetch + setElement + setPendingUrl(null)
212
- // are deferred. When the transition commits, the new tree and pendingUrl=null
213
- // both apply in the same React commit — making the pending→active transition
214
- // atomic (no frame where pending is false but the old tree is still visible).
215
- _navigateTransition = (
216
- url: string,
217
- perform: () => Promise<{ element: ReactNode; decodePromise: Promise<void> | null }>
218
- ) => {
219
- // Increment the transition counter SYNCHRONOUSLY (before startTransition
220
- // schedules the async work). Each call gets a unique transId; the counter
221
- // is the same globalThis singleton, so a newer call always has a higher id.
364
+ //
365
+ // The async work runs OUTSIDE any transition scope, and each state update is
366
+ // its own synchronous `startTransition`. Two React behaviours force that
367
+ // shape; both were measured on React 19.2.7 (tests/navigation-transition-
368
+ // suspense.test.ts pins the observable half).
369
+ //
370
+ // 1. A transition scope does not survive an `await`. `startTransition`
371
+ // restores the previous scope in its `finally`, which for an async
372
+ // callback runs when that callback RETURNS — i.e. at its first `await`.
373
+ // So an update scheduled past an await inside `startTransition(async …)`
374
+ // is an ordinary urgent update:
375
+ //
376
+ // startTransition(() => setEl(x)) -> 'HOME' held
377
+ // startTransition(async () => { await p; setEl(x) }) -> fallback shown
378
+ //
379
+ // Left uncorrected, every navigation whose new tree suspends replaces the
380
+ // visible page with a Suspense fallback — the exact thing this component
381
+ // exists to prevent (TIM-1306). React documents the caveat under
382
+ // `startTransition`: updates after an await need their own transition.
383
+ //
384
+ // 2. Re-wrapping *inside* the async callback is not enough. Returning a
385
+ // thenable from `startTransition` hands it to `ReactSharedInternals.S`,
386
+ // which calls react-dom's `entangleAsyncAction`. That opens an action
387
+ // scope: `currentEntangledLane` collects EVERY transition update
388
+ // scheduled while the scope is open — including one from a nested,
389
+ // fully synchronous `startTransition` — and rendering that lane throws
390
+ // `currentEntangledActionThenable`, suspending until the action settles.
391
+ // Which is after the promise `navigateTransition` hands back, so a caller
392
+ // that awaits a navigation and then reads the DOM sees the departing
393
+ // page, and the TIM-1301 publish (a layout effect on the commit) is just
394
+ // as late. (Not `ReactSharedInternals.asyncTransitions` — that counter is
395
+ // write-only in 19.2.7.)
396
+ //
397
+ // No async action is created here, so every navigation commits as soon as
398
+ // React can render its destination — where TIM-1301 put it. That
399
+ // now holds for ALL of them. It used not to hold for `<Link>`, which wrapped
400
+ // `router.navigate()` in its own `useTransition`: that action scope
401
+ // entangled this component's updates just the same, so a Link navigation
402
+ // committed only once the payload had finished decoding, and with it
403
+ // `pushState`, the segment cache and `timber:navigation-end`. Link now calls
404
+ // `router.navigate()` outside any transition and tracks its own `isPending`
405
+ // with a plain `useState` (TIM-1307).
406
+ //
407
+ // Nothing here may reopen an action scope: no `startTransition` callback in
408
+ // this path may return a thenable, and no caller may invoke this from inside
409
+ // a React action scope. That is a rule about `startTransition`, not about
410
+ // `perform` — `perform` is async by contract and is awaited OUTSIDE any
411
+ // transition scope, which is exactly why it is safe.
412
+ //
413
+ // Standalone `startTransition` rather than `useTransition`'s is still
414
+ // deliberate — see the TIM-625 note above; each navigation needs an
415
+ // independent lane.
416
+ //
417
+ // `url` is unused: it named the pending state this component used to hold,
418
+ // and the router already publishes the same URL to its own pending store
419
+ // before calling here. It stays in the signature because the router's
420
+ // `RouterDeps.navigateTransition` adapter passes it and the argument reads
421
+ // at the call site.
422
+ _navigateTransition = (_url: string, perform: () => Promise<TransitionResult>) => {
423
+ // Increment the transition counter SYNCHRONOUSLY (before any await). Each
424
+ // call gets a unique transId; the counter is the same globalThis
425
+ // singleton, so a newer call always has a higher id.
222
426
  const counter = getTransitionCounter();
223
- const transId = ++counter.id;
224
-
225
- // NO urgent setPendingUrl here. Per-link pending state is driven by
226
- // Link's own useTransition — only the clicked Link re-renders for
227
- // isPending. setPendingUrl is set INSIDE startTransition so the
228
- // TopLoader activates as a transition update (no full-tree re-render).
229
-
230
- return new Promise<void>((resolve, reject) => {
231
- startTransition(async () => {
232
- setPendingUrl(url);
233
- try {
234
- const { element, decodePromise } = await perform();
235
- if (counter.id !== transId) {
236
- decodePromise?.catch(() => {});
237
- reject(new DOMException('Navigation superseded', 'AbortError'));
238
- return;
239
- }
240
- setElement(element);
241
- if (decodePromise) await decodePromise;
242
- if (counter.id === transId) {
243
- setPendingUrl(null);
244
- resolve();
245
- } else {
246
- reject(new DOMException('Navigation superseded', 'AbortError'));
247
- }
248
- } catch (err) {
249
- if (counter.id === transId) {
250
- setPendingUrl(null);
251
- }
252
- reject(err);
253
- }
427
+ const transId = bumpTransitionCounter();
428
+
429
+ const superseded = () => new DOMException('Navigation superseded', 'AbortError');
430
+
431
+ return (async () => {
432
+ const { element, decodePromise, commit } = await perform();
433
+ if (counter.id !== transId) {
434
+ decodePromise?.catch(() => {});
435
+ throw superseded();
436
+ }
437
+ // Hand the tree over with its commit attached. The effect above runs
438
+ // it if and when React commits this tree, so a navigation that is
439
+ // superseded while its payload streams publishes nothing (TIM-1301).
440
+ //
441
+ // This is THE update that must not hide the departing page (TIM-1306).
442
+ startTransition(() => {
443
+ setRendered({ element, publish: commit });
254
444
  });
255
- });
445
+ // React may commit the tree before this settles — that is the point:
446
+ // the destination reveals as React is able to render it
447
+ // rather than waiting for the whole Flight stream. The await is here so
448
+ // the promise this function hands back still means "the payload is
449
+ // decoded", which is what the router's scroll restoration, the
450
+ // Navigation API deferred and `<Link>`'s `isPending` are timed against.
451
+ //
452
+ // ...unless this navigation loses first, in which case it stops waiting
453
+ // on a stream that is no longer its business. See
454
+ // `settleOnDecodeOrSupersession`.
455
+ if (decodePromise) await settleOnDecodeOrSupersession(decodePromise, counter, transId);
456
+ if (counter.id !== transId) throw superseded();
457
+ })();
256
458
  };
257
459
 
258
460
  // ─── Hard navigation guard ─────────────────────────────────
@@ -261,10 +463,10 @@ export function NavigationRoot({
261
463
  // page teardown. This avoids "Rendered more hooks" crashes in
262
464
  // CHILD components whose hook counts may shift during teardown.
263
465
  //
264
- // CRITICAL: This throw MUST come AFTER all hooks (the two
265
- // useState calls above). React requires the same hooks to run on
266
- // every render. If we threw before hooks, React would see 0 hooks
267
- // on the re-render vs 2 hooks on the initial render — triggering
466
+ // CRITICAL: This throw MUST come AFTER all hooks (the useState,
467
+ // useRef and useLayoutEffect above). React requires the same hooks
468
+ // to run on every render. If we threw before hooks, React would see
469
+ // 0 hooks on the re-render vs 3 on the initial render — triggering
268
470
  // the exact "Rendered more hooks" error we're trying to prevent.
269
471
  //
270
472
  // By placing it after hooks but before the return, all hooks
@@ -275,14 +477,23 @@ export function NavigationRoot({
275
477
  throw unresolvedThenable;
276
478
  }
277
479
 
278
- // Inject TopLoader alongside the element tree inside PendingNavigationProvider.
279
- // The TopLoader reads pendingUrl from context to show/hide the progress bar.
280
- // It is rendered only when not explicitly disabled via config.
480
+ // Inject TopLoader alongside the element tree. It subscribes to the router's
481
+ // pending store itself, so it takes no props from here beyond its config,
482
+ // and only it re-renders when a navigation starts or ends. Rendered only
483
+ // when not explicitly disabled via config.
484
+ //
485
+ // This Fragment is a FORK — two children — so it advances React's tree
486
+ // context and shifts every `useId` below it. `server/ssr-wrappers.tsx`
487
+ // mirrors it for exactly that reason; a single-child wrapper would not need
488
+ // a mirror. See tests/ssr-wrapper-parity.test.ts.
281
489
  const showTopLoader = topLoaderConfig?.enabled !== false;
282
- const children = showTopLoader
283
- ? createElement(Fragment, null, createElement(TopLoader, { config: topLoaderConfig }), element)
284
- : element;
285
- return createElement(PendingNavigationProvider, { value: pendingUrl }, children);
490
+ if (!showTopLoader) return rendered.element;
491
+ return createElement(
492
+ Fragment,
493
+ null,
494
+ createElement(TopLoader, { config: topLoaderConfig }),
495
+ rendered.element
496
+ );
286
497
  }
287
498
 
288
499
  // ─── Public API ──────────────────────────────────────────────────
@@ -301,27 +512,43 @@ export function transitionRender(element: ReactNode): void {
301
512
  }
302
513
 
303
514
  /**
304
- * Run a full navigation inside a React transition with optimistic pending URL.
515
+ * Run a full navigation, handing its tree to React in a transition.
516
+ *
517
+ * The `perform` callback fetches the RSC payload, updates router state, and
518
+ * returns the wrapped React element. `perform` is async by contract and runs
519
+ * OUTSIDE any transition scope — only the `setRendered` that hands its result
520
+ * over is wrapped, in a synchronous `startTransition`.
521
+ *
522
+ * Do not call this from inside a React action scope, and never let a
523
+ * `startTransition` callback in this path return a thenable: either opens an
524
+ * action scope that entangles the update and defers the commit until the
525
+ * action settles (TIM-1306, TIM-1307).
305
526
  *
306
- * The `perform` callback runs inside `startTransition` — it should fetch the
307
- * RSC payload, update router state, and return the wrapped React element.
308
- * The pending URL shows immediately (urgent update) and reverts
309
- * to null when the transition commits (atomic with the new tree).
527
+ * Returns a Promise that resolves when the async work completes — the payload
528
+ * is fetched, decoded, and handed to React. It does **not** wait for React to
529
+ * commit the tree, so the state that publishes on that commit (address bar,
530
+ * segment cache, history entry, pathname) may still be a beat behind when it
531
+ * resolves. Anything that needs the destination to be current should listen
532
+ * for `timber:navigation-end`, which is dispatched by the publish itself.
310
533
  *
311
- * Returns a Promise that resolves when the async work completes (note: the
312
- * React transition may not have committed yet, but all state updates are done).
534
+ * Awaiting the publish here was tried and rejected: it makes the promise
535
+ * depend on a React commit, which never arrives if the root unmounts
536
+ * mid-navigation, and deadlocks any caller that awaits a navigation inside
537
+ * `act()`.
313
538
  *
314
539
  * Used for: navigate(), refresh(), popstate with fetch.
315
540
  */
316
541
  export function navigateTransition(
317
- pendingUrl: string,
318
- perform: () => Promise<{ element: ReactNode; decodePromise: Promise<void> | null }>
542
+ url: string,
543
+ perform: () => Promise<TransitionResult>
319
544
  ): Promise<void> {
320
545
  if (_navigateTransition) {
321
- return _navigateTransition(pendingUrl, perform);
546
+ return _navigateTransition(url, perform);
322
547
  }
323
- // Fallback: no NavigationRoot mounted (shouldn't happen in production)
324
- return perform().then(() => {});
548
+ // Fallback: no NavigationRoot mounted (shouldn't happen in production).
549
+ // Nothing can supersede a transition that does not exist, so the commit
550
+ // runs unconditionally.
551
+ return perform().then((result) => result.commit());
325
552
  }
326
553
 
327
554
  /**
@@ -345,11 +572,9 @@ export function installDeferredNavigation(createAndMount: (initial: ReactNode) =
345
572
  _transitionRender = (element: ReactNode) => {
346
573
  mountOnce(element);
347
574
  };
348
- _navigateTransition = async (
349
- _pendingUrl: string,
350
- perform: () => Promise<{ element: ReactNode; decodePromise: Promise<void> | null }>
351
- ) => {
352
- const { element } = await perform();
575
+ _navigateTransition = async (_url: string, perform: () => Promise<TransitionResult>) => {
576
+ const { element, commit } = await perform();
577
+ commit();
353
578
  mountOnce(element);
354
579
  };
355
580
  }