@timber-js/app 0.2.0-alpha.16 → 0.2.0-alpha.160

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 (985) hide show
  1. package/LICENSE +8 -0
  2. package/agent-skill.md +344 -0
  3. package/dist/_chunks/actions-C9yAuoX3.js +314 -0
  4. package/dist/_chunks/actions-C9yAuoX3.js.map +1 -0
  5. package/dist/_chunks/build-output-helper-DF5uScqS.js +158 -0
  6. package/dist/_chunks/build-output-helper-DF5uScqS.js.map +1 -0
  7. package/dist/_chunks/cache-api-swbefQ6F.js +1125 -0
  8. package/dist/_chunks/cache-api-swbefQ6F.js.map +1 -0
  9. package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → canonicalize-P41GR6tY.js} +94 -18
  10. package/dist/_chunks/canonicalize-P41GR6tY.js.map +1 -0
  11. package/dist/_chunks/cli-schema-sync-BhYDz_-x.js +638 -0
  12. package/dist/_chunks/cli-schema-sync-BhYDz_-x.js.map +1 -0
  13. package/dist/_chunks/codec-registry-oOUxugz3.js +21 -0
  14. package/dist/_chunks/codec-registry-oOUxugz3.js.map +1 -0
  15. package/dist/_chunks/define-Bssfp6ot.js +82 -0
  16. package/dist/_chunks/define-Bssfp6ot.js.map +1 -0
  17. package/dist/_chunks/define-CFmvb4Bt.js +203 -0
  18. package/dist/_chunks/define-CFmvb4Bt.js.map +1 -0
  19. package/dist/_chunks/dist-BA3u1z3W.js +423 -0
  20. package/dist/_chunks/dist-BA3u1z3W.js.map +1 -0
  21. package/dist/_chunks/fast-hash-D6hIVt1Y.js +36 -0
  22. package/dist/_chunks/fast-hash-D6hIVt1Y.js.map +1 -0
  23. package/dist/_chunks/format-CfwjgPz9.js +14 -0
  24. package/dist/_chunks/format-CfwjgPz9.js.map +1 -0
  25. package/dist/_chunks/href-validation-CMc5JRls.js +141 -0
  26. package/dist/_chunks/href-validation-CMc5JRls.js.map +1 -0
  27. package/dist/_chunks/logger-CPoQkGK6.js +1523 -0
  28. package/dist/_chunks/logger-CPoQkGK6.js.map +1 -0
  29. package/dist/_chunks/navigation-root-bUDZLw74.js +124 -0
  30. package/dist/_chunks/navigation-root-bUDZLw74.js.map +1 -0
  31. package/dist/_chunks/plugin-context-DeAxFRMq.js +280 -0
  32. package/dist/_chunks/plugin-context-DeAxFRMq.js.map +1 -0
  33. package/dist/_chunks/registry-DbJPKoBp.js +20 -0
  34. package/dist/_chunks/registry-DbJPKoBp.js.map +1 -0
  35. package/dist/_chunks/resolve-schema-3iUvBV5T.js +50 -0
  36. package/dist/_chunks/resolve-schema-3iUvBV5T.js.map +1 -0
  37. package/dist/_chunks/router-ref-CtmF-aPv.js +28 -0
  38. package/dist/_chunks/router-ref-CtmF-aPv.js.map +1 -0
  39. package/dist/_chunks/schema-bridge-BY3QLBL7.js +119 -0
  40. package/dist/_chunks/schema-bridge-BY3QLBL7.js.map +1 -0
  41. package/dist/_chunks/segment-classify-CDDRVKs7.js +137 -0
  42. package/dist/_chunks/segment-classify-CDDRVKs7.js.map +1 -0
  43. package/dist/_chunks/segment-context-ZDnXDkbz.js +34 -0
  44. package/dist/_chunks/segment-context-ZDnXDkbz.js.map +1 -0
  45. package/dist/_chunks/sizeof-lXffQ1Vr.js +35 -0
  46. package/dist/_chunks/sizeof-lXffQ1Vr.js.map +1 -0
  47. package/dist/_chunks/{ssr-data-MjmprTmO.js → ssr-data-BOWsq18U.js} +1 -1
  48. package/dist/_chunks/ssr-data-BOWsq18U.js.map +1 -0
  49. package/dist/_chunks/tree-match-D2l830j2.js +102 -0
  50. package/dist/_chunks/tree-match-D2l830j2.js.map +1 -0
  51. package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-CbeQmext.js} +7 -20
  52. package/dist/_chunks/use-query-states-CbeQmext.js.map +1 -0
  53. package/dist/_chunks/use-segment-params-D1CzlgBo.js +151 -0
  54. package/dist/_chunks/use-segment-params-D1CzlgBo.js.map +1 -0
  55. package/dist/_chunks/walkers-c8aReVgo.js +988 -0
  56. package/dist/_chunks/walkers-c8aReVgo.js.map +1 -0
  57. package/dist/adapters/build-output-helper.d.ts +29 -0
  58. package/dist/adapters/build-output-helper.d.ts.map +1 -0
  59. package/dist/adapters/cloudflare-dev.d.ts +109 -0
  60. package/dist/adapters/cloudflare-dev.d.ts.map +1 -0
  61. package/dist/adapters/cloudflare-dev.js +73 -0
  62. package/dist/adapters/cloudflare-dev.js.map +1 -0
  63. package/dist/adapters/cloudflare-kv-cache.d.ts +64 -0
  64. package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -0
  65. package/dist/adapters/cloudflare-kv-cache.js +117 -0
  66. package/dist/adapters/cloudflare-kv-cache.js.map +1 -0
  67. package/dist/adapters/cloudflare.d.ts +177 -20
  68. package/dist/adapters/cloudflare.d.ts.map +1 -1
  69. package/dist/adapters/cloudflare.js +342 -67
  70. package/dist/adapters/cloudflare.js.map +1 -1
  71. package/dist/adapters/compress-module.d.ts +5 -4
  72. package/dist/adapters/compress-module.d.ts.map +1 -1
  73. package/dist/adapters/nitro.d.ts +1 -3
  74. package/dist/adapters/nitro.d.ts.map +1 -1
  75. package/dist/adapters/nitro.js +143 -144
  76. package/dist/adapters/nitro.js.map +1 -1
  77. package/dist/adapters/shared.d.ts +6 -0
  78. package/dist/adapters/shared.d.ts.map +1 -0
  79. package/dist/cache/cache-api.d.ts +26 -0
  80. package/dist/cache/cache-api.d.ts.map +1 -0
  81. package/dist/cache/handler-store.d.ts +33 -0
  82. package/dist/cache/handler-store.d.ts.map +1 -0
  83. package/dist/cache/index.d.ts +27 -7
  84. package/dist/cache/index.d.ts.map +1 -1
  85. package/dist/cache/index.js +3 -326
  86. package/dist/cache/invalidation-epoch.d.ts +43 -0
  87. package/dist/cache/invalidation-epoch.d.ts.map +1 -0
  88. package/dist/cache/redis-handler.d.ts +97 -8
  89. package/dist/cache/redis-handler.d.ts.map +1 -1
  90. package/dist/cache/singleflight.d.ts +19 -2
  91. package/dist/cache/singleflight.d.ts.map +1 -1
  92. package/dist/cache/sizeof.d.ts +22 -0
  93. package/dist/cache/sizeof.d.ts.map +1 -0
  94. package/dist/cache/stable-stringify.d.ts +9 -0
  95. package/dist/cache/stable-stringify.d.ts.map +1 -1
  96. package/dist/cache/store.d.ts +53 -0
  97. package/dist/cache/store.d.ts.map +1 -0
  98. package/dist/cache/stores/cloudflare-kv.d.ts +38 -0
  99. package/dist/cache/stores/cloudflare-kv.d.ts.map +1 -0
  100. package/dist/cache/stores/cloudflare-kv.js +29 -0
  101. package/dist/cache/stores/cloudflare-kv.js.map +1 -0
  102. package/dist/cache/stores/memory.d.ts +42 -0
  103. package/dist/cache/stores/memory.d.ts.map +1 -0
  104. package/dist/cache/stores/memory.js +90 -0
  105. package/dist/cache/stores/memory.js.map +1 -0
  106. package/dist/cache/stores/redis.d.ts +69 -0
  107. package/dist/cache/stores/redis.d.ts.map +1 -0
  108. package/dist/cache/stores/redis.js +35 -0
  109. package/dist/cache/stores/redis.js.map +1 -0
  110. package/dist/cache/stores/vercel.d.ts +36 -0
  111. package/dist/cache/stores/vercel.d.ts.map +1 -0
  112. package/dist/cache/stores/vercel.js +33 -0
  113. package/dist/cache/stores/vercel.js.map +1 -0
  114. package/dist/cache/tag-aware-handler.d.ts +51 -0
  115. package/dist/cache/tag-aware-handler.d.ts.map +1 -0
  116. package/dist/cache/timber-cache.d.ts +20 -10
  117. package/dist/cache/timber-cache.d.ts.map +1 -1
  118. package/dist/cdn/cloudflare-purge.d.ts +23 -0
  119. package/dist/cdn/cloudflare-purge.d.ts.map +1 -0
  120. package/dist/cdn/fastly-purge.d.ts +25 -0
  121. package/dist/cdn/fastly-purge.d.ts.map +1 -0
  122. package/dist/cdn/headers.d.ts +97 -0
  123. package/dist/cdn/headers.d.ts.map +1 -0
  124. package/dist/cdn/index.d.ts +5 -0
  125. package/dist/cdn/index.d.ts.map +1 -0
  126. package/dist/cdn/purge-store.d.ts +23 -0
  127. package/dist/cdn/purge-store.d.ts.map +1 -0
  128. package/dist/cdn/workers-cache-purge.d.ts +16 -0
  129. package/dist/cdn/workers-cache-purge.d.ts.map +1 -0
  130. package/dist/cli-schema-sync.d.ts +59 -0
  131. package/dist/cli-schema-sync.d.ts.map +1 -0
  132. package/dist/cli.d.ts +34 -2
  133. package/dist/cli.d.ts.map +1 -1
  134. package/dist/cli.js +107 -33
  135. package/dist/cli.js.map +1 -1
  136. package/dist/client/browser-dev.d.ts +37 -1
  137. package/dist/client/browser-dev.d.ts.map +1 -1
  138. package/dist/client/browser-entry/action-dispatch.d.ts +17 -0
  139. package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -0
  140. package/dist/client/browser-entry/hmr.d.ts +21 -0
  141. package/dist/client/browser-entry/hmr.d.ts.map +1 -0
  142. package/dist/client/browser-entry/hydrate.d.ts +46 -0
  143. package/dist/client/browser-entry/hydrate.d.ts.map +1 -0
  144. package/dist/client/browser-entry/index.d.ts +29 -0
  145. package/dist/client/browser-entry/index.d.ts.map +1 -0
  146. package/dist/client/browser-entry/post-hydration.d.ts +26 -0
  147. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -0
  148. package/dist/client/browser-entry/router-init.d.ts +26 -0
  149. package/dist/client/browser-entry/router-init.d.ts.map +1 -0
  150. package/dist/client/browser-entry/rsc-stream.d.ts +24 -0
  151. package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -0
  152. package/dist/client/browser-entry/scroll.d.ts +28 -0
  153. package/dist/client/browser-entry/scroll.d.ts.map +1 -0
  154. package/dist/client/error-boundary.d.ts +12 -5
  155. package/dist/client/error-boundary.d.ts.map +1 -1
  156. package/dist/client/error-boundary.js +47 -10
  157. package/dist/client/error-boundary.js.map +1 -1
  158. package/dist/client/error-reconstituter.d.ts +61 -0
  159. package/dist/client/error-reconstituter.d.ts.map +1 -0
  160. package/dist/client/form.d.ts +16 -27
  161. package/dist/client/form.d.ts.map +1 -1
  162. package/dist/client/history.d.ts +11 -6
  163. package/dist/client/history.d.ts.map +1 -1
  164. package/dist/client/index.d.ts +10 -24
  165. package/dist/client/index.d.ts.map +1 -1
  166. package/dist/client/index.js +182 -1134
  167. package/dist/client/index.js.map +1 -1
  168. package/dist/client/internal.d.ts +19 -0
  169. package/dist/client/internal.d.ts.map +1 -0
  170. package/dist/client/internal.js +825 -0
  171. package/dist/client/internal.js.map +1 -0
  172. package/dist/client/link.d.ts +69 -60
  173. package/dist/client/link.d.ts.map +1 -1
  174. package/dist/client/navigation-api.d.ts +120 -0
  175. package/dist/client/navigation-api.d.ts.map +1 -0
  176. package/dist/client/navigation-context.d.ts +3 -2
  177. package/dist/client/navigation-context.d.ts.map +1 -1
  178. package/dist/client/navigation-root.d.ts +119 -0
  179. package/dist/client/navigation-root.d.ts.map +1 -0
  180. package/dist/client/nuqs-adapter.d.ts +1 -1
  181. package/dist/client/nuqs-adapter.d.ts.map +1 -1
  182. package/dist/client/router-ref.d.ts +1 -1
  183. package/dist/client/router.d.ts +87 -14
  184. package/dist/client/router.d.ts.map +1 -1
  185. package/dist/client/rsc-fetch.d.ts +47 -14
  186. package/dist/client/rsc-fetch.d.ts.map +1 -1
  187. package/dist/client/segment-cache.d.ts +4 -4
  188. package/dist/client/segment-cache.d.ts.map +1 -1
  189. package/dist/client/segment-outlet.d.ts +49 -0
  190. package/dist/client/segment-outlet.d.ts.map +1 -0
  191. package/dist/client/segment-update-context.d.ts +21 -0
  192. package/dist/client/segment-update-context.d.ts.map +1 -0
  193. package/dist/client/shallow-url.d.ts +10 -0
  194. package/dist/client/shallow-url.d.ts.map +1 -0
  195. package/dist/client/ssr-data.d.ts +13 -4
  196. package/dist/client/ssr-data.d.ts.map +1 -1
  197. package/dist/client/state.d.ts +1 -1
  198. package/dist/client/top-loader.d.ts +5 -5
  199. package/dist/client/top-loader.d.ts.map +1 -1
  200. package/dist/client/use-cookie.d.ts +12 -0
  201. package/dist/client/use-cookie.d.ts.map +1 -1
  202. package/dist/client/use-link-status.d.ts +5 -5
  203. package/dist/client/use-link-status.d.ts.map +1 -1
  204. package/dist/client/use-pathname.d.ts.map +1 -1
  205. package/dist/client/use-pending-navigation.d.ts +23 -0
  206. package/dist/client/use-pending-navigation.d.ts.map +1 -0
  207. package/dist/client/use-query-states.d.ts +1 -1
  208. package/dist/client/use-query-states.d.ts.map +1 -1
  209. package/dist/client/use-router.d.ts +1 -1
  210. package/dist/client/use-router.d.ts.map +1 -1
  211. package/dist/client/use-search-params.d.ts +12 -5
  212. package/dist/client/use-search-params.d.ts.map +1 -1
  213. package/dist/client/{use-params.d.ts → use-segment-params.d.ts} +8 -6
  214. package/dist/client/use-segment-params.d.ts.map +1 -0
  215. package/dist/codec.d.ts +69 -0
  216. package/dist/codec.d.ts.map +1 -0
  217. package/dist/codec.js +75 -0
  218. package/dist/codec.js.map +1 -0
  219. package/dist/config-types.d.ts +304 -0
  220. package/dist/config-types.d.ts.map +1 -0
  221. package/dist/config-validation.d.ts +51 -0
  222. package/dist/config-validation.d.ts.map +1 -0
  223. package/dist/content/index.d.ts +1 -10
  224. package/dist/content/index.d.ts.map +1 -1
  225. package/dist/content/index.js +0 -2
  226. package/dist/cookies/define-cookie.d.ts +58 -25
  227. package/dist/cookies/define-cookie.d.ts.map +1 -1
  228. package/dist/cookies/index.d.ts +2 -1
  229. package/dist/cookies/index.d.ts.map +1 -1
  230. package/dist/cookies/index.js +253 -36
  231. package/dist/cookies/index.js.map +1 -1
  232. package/dist/cookies/json-cookie.d.ts +64 -0
  233. package/dist/cookies/json-cookie.d.ts.map +1 -0
  234. package/dist/cookies/validation.d.ts +34 -0
  235. package/dist/cookies/validation.d.ts.map +1 -0
  236. package/dist/dev-tools/404-page.d.ts +50 -0
  237. package/dist/dev-tools/404-page.d.ts.map +1 -0
  238. package/dist/dev-tools/browser-logs.d.ts +40 -0
  239. package/dist/dev-tools/browser-logs.d.ts.map +1 -0
  240. package/dist/dev-tools/dev-page-shell.d.ts +78 -0
  241. package/dist/dev-tools/dev-page-shell.d.ts.map +1 -0
  242. package/dist/dev-tools/error-page.d.ts +32 -0
  243. package/dist/dev-tools/error-page.d.ts.map +1 -0
  244. package/dist/dev-tools/holding-server.d.ts +54 -0
  245. package/dist/dev-tools/holding-server.d.ts.map +1 -0
  246. package/dist/dev-tools/hydration-overlay.d.ts +11 -0
  247. package/dist/dev-tools/hydration-overlay.d.ts.map +1 -0
  248. package/dist/dev-tools/index.d.ts +31 -0
  249. package/dist/dev-tools/index.d.ts.map +1 -0
  250. package/dist/dev-tools/instrumentation.d.ts +59 -0
  251. package/dist/dev-tools/instrumentation.d.ts.map +1 -0
  252. package/dist/{server/dev-logger.d.ts → dev-tools/logger.d.ts} +1 -1
  253. package/dist/dev-tools/logger.d.ts.map +1 -0
  254. package/dist/{plugins/dev-logs.d.ts → dev-tools/logs.d.ts} +2 -2
  255. package/dist/dev-tools/logs.d.ts.map +1 -0
  256. package/dist/dev-tools/overlay.d.ts +91 -0
  257. package/dist/dev-tools/overlay.d.ts.map +1 -0
  258. package/dist/dev-tools/stack-classifier.d.ts +34 -0
  259. package/dist/dev-tools/stack-classifier.d.ts.map +1 -0
  260. package/dist/dev-tools/terminal.d.ts +28 -0
  261. package/dist/dev-tools/terminal.d.ts.map +1 -0
  262. package/dist/fonts/ast-walk.d.ts +68 -0
  263. package/dist/fonts/ast-walk.d.ts.map +1 -0
  264. package/dist/fonts/ast.d.ts +110 -6
  265. package/dist/fonts/ast.d.ts.map +1 -1
  266. package/dist/fonts/bundle.d.ts +50 -0
  267. package/dist/fonts/bundle.d.ts.map +1 -0
  268. package/dist/fonts/dev-middleware.d.ts +22 -0
  269. package/dist/fonts/dev-middleware.d.ts.map +1 -0
  270. package/dist/fonts/fallbacks.d.ts +0 -7
  271. package/dist/fonts/fallbacks.d.ts.map +1 -1
  272. package/dist/fonts/google.d.ts +4 -2
  273. package/dist/fonts/google.d.ts.map +1 -1
  274. package/dist/fonts/local.d.ts +4 -4
  275. package/dist/fonts/local.d.ts.map +1 -1
  276. package/dist/fonts/paths.d.ts +43 -0
  277. package/dist/fonts/paths.d.ts.map +1 -0
  278. package/dist/fonts/pipeline.d.ts +158 -0
  279. package/dist/fonts/pipeline.d.ts.map +1 -0
  280. package/dist/fonts/transform.d.ts +82 -0
  281. package/dist/fonts/transform.d.ts.map +1 -0
  282. package/dist/fonts/types.d.ts +47 -1
  283. package/dist/fonts/types.d.ts.map +1 -1
  284. package/dist/fonts/virtual-modules.d.ts +77 -0
  285. package/dist/fonts/virtual-modules.d.ts.map +1 -0
  286. package/dist/index.d.ts +46 -180
  287. package/dist/index.d.ts.map +1 -1
  288. package/dist/index.js +7807 -13691
  289. package/dist/index.js.map +1 -1
  290. package/dist/params/codec-registry.d.ts +26 -0
  291. package/dist/params/codec-registry.d.ts.map +1 -0
  292. package/dist/params/define-schema.d.ts +81 -0
  293. package/dist/params/define-schema.d.ts.map +1 -0
  294. package/dist/params/index.d.ts +5 -0
  295. package/dist/params/index.d.ts.map +1 -0
  296. package/dist/params/index.js +34 -0
  297. package/dist/params/index.js.map +1 -0
  298. package/dist/params/resolve-schema.d.ts +34 -0
  299. package/dist/params/resolve-schema.d.ts.map +1 -0
  300. package/dist/plugin-context.d.ts +190 -0
  301. package/dist/plugin-context.d.ts.map +1 -0
  302. package/dist/plugins/adapter-build.d.ts +20 -1
  303. package/dist/plugins/adapter-build.d.ts.map +1 -1
  304. package/dist/plugins/build-manifest.d.ts +7 -3
  305. package/dist/plugins/build-manifest.d.ts.map +1 -1
  306. package/dist/plugins/build-report.d.ts +43 -7
  307. package/dist/plugins/build-report.d.ts.map +1 -1
  308. package/dist/plugins/cache.d.ts +64 -0
  309. package/dist/plugins/cache.d.ts.map +1 -0
  310. package/dist/plugins/callsite-ast.d.ts +95 -0
  311. package/dist/plugins/callsite-ast.d.ts.map +1 -0
  312. package/dist/plugins/callsite-jsx.d.ts +35 -0
  313. package/dist/plugins/callsite-jsx.d.ts.map +1 -0
  314. package/dist/plugins/chunks.d.ts +11 -4
  315. package/dist/plugins/chunks.d.ts.map +1 -1
  316. package/dist/plugins/client-chunks.d.ts +40 -0
  317. package/dist/plugins/client-chunks.d.ts.map +1 -0
  318. package/dist/plugins/cloudflare-wasm.d.ts +21 -0
  319. package/dist/plugins/cloudflare-wasm.d.ts.map +1 -0
  320. package/dist/plugins/content.d.ts +1 -1
  321. package/dist/plugins/content.d.ts.map +1 -1
  322. package/dist/plugins/dev-server.d.ts +17 -1
  323. package/dist/plugins/dev-server.d.ts.map +1 -1
  324. package/dist/plugins/entries.d.ts +1 -1
  325. package/dist/plugins/entries.d.ts.map +1 -1
  326. package/dist/plugins/fonts.d.ts +16 -73
  327. package/dist/plugins/fonts.d.ts.map +1 -1
  328. package/dist/plugins/mdx.d.ts +1 -1
  329. package/dist/plugins/mdx.d.ts.map +1 -1
  330. package/dist/plugins/prebuilt-capture.d.ts +60 -0
  331. package/dist/plugins/prebuilt-capture.d.ts.map +1 -0
  332. package/dist/plugins/prebuilt.d.ts +82 -0
  333. package/dist/plugins/prebuilt.d.ts.map +1 -0
  334. package/dist/plugins/prerender-sugar.d.ts +42 -0
  335. package/dist/plugins/prerender-sugar.d.ts.map +1 -0
  336. package/dist/plugins/routing.d.ts +1 -1
  337. package/dist/plugins/routing.d.ts.map +1 -1
  338. package/dist/plugins/server-bundle.d.ts.map +1 -1
  339. package/dist/plugins/shims.d.ts +29 -5
  340. package/dist/plugins/shims.d.ts.map +1 -1
  341. package/dist/plugins/static-build.d.ts +5 -26
  342. package/dist/plugins/static-build.d.ts.map +1 -1
  343. package/dist/routing/codegen-shared.d.ts +67 -0
  344. package/dist/routing/codegen-shared.d.ts.map +1 -0
  345. package/dist/routing/codegen-types.d.ts +64 -0
  346. package/dist/routing/codegen-types.d.ts.map +1 -0
  347. package/dist/routing/codegen.d.ts +7 -2
  348. package/dist/routing/codegen.d.ts.map +1 -1
  349. package/dist/routing/convention-lint.d.ts +41 -0
  350. package/dist/routing/convention-lint.d.ts.map +1 -0
  351. package/dist/routing/export-detect.d.ts +56 -0
  352. package/dist/routing/export-detect.d.ts.map +1 -0
  353. package/dist/routing/file-cache.d.ts +8 -0
  354. package/dist/routing/file-cache.d.ts.map +1 -0
  355. package/dist/routing/index.d.ts +7 -1
  356. package/dist/routing/index.d.ts.map +1 -1
  357. package/dist/routing/index.js +4 -2
  358. package/dist/routing/interception.d.ts +0 -5
  359. package/dist/routing/interception.d.ts.map +1 -1
  360. package/dist/routing/link-codegen.d.ts +90 -0
  361. package/dist/routing/link-codegen.d.ts.map +1 -0
  362. package/dist/routing/manifest-codegen.d.ts +32 -0
  363. package/dist/routing/manifest-codegen.d.ts.map +1 -0
  364. package/dist/routing/scanner.d.ts +1 -10
  365. package/dist/routing/scanner.d.ts.map +1 -1
  366. package/dist/routing/schema-validation.d.ts +35 -0
  367. package/dist/routing/schema-validation.d.ts.map +1 -0
  368. package/dist/routing/segment-classify.d.ts +75 -0
  369. package/dist/routing/segment-classify.d.ts.map +1 -0
  370. package/dist/routing/segment-codegen.d.ts +38 -0
  371. package/dist/routing/segment-codegen.d.ts.map +1 -0
  372. package/dist/routing/types.d.ts +77 -25
  373. package/dist/routing/types.d.ts.map +1 -1
  374. package/dist/routing/walkers.d.ts +51 -0
  375. package/dist/routing/walkers.d.ts.map +1 -0
  376. package/dist/rsc-runtime/rsc.d.ts +1 -1
  377. package/dist/rsc-runtime/rsc.d.ts.map +1 -1
  378. package/dist/rsc-runtime/ssr.d.ts +12 -0
  379. package/dist/rsc-runtime/ssr.d.ts.map +1 -1
  380. package/dist/schema-bridge.d.ts +91 -0
  381. package/dist/schema-bridge.d.ts.map +1 -0
  382. package/dist/search-params/define.d.ts +157 -0
  383. package/dist/search-params/define.d.ts.map +1 -0
  384. package/dist/search-params/index.d.ts +4 -7
  385. package/dist/search-params/index.d.ts.map +1 -1
  386. package/dist/search-params/index.js +37 -440
  387. package/dist/search-params/index.js.map +1 -1
  388. package/dist/search-params/registry.d.ts +2 -2
  389. package/dist/search-params/registry.d.ts.map +1 -1
  390. package/dist/search-params/wrappers.d.ts +53 -0
  391. package/dist/search-params/wrappers.d.ts.map +1 -0
  392. package/dist/segment-params/define.d.ts +89 -0
  393. package/dist/segment-params/define.d.ts.map +1 -0
  394. package/dist/segment-params/index.d.ts +3 -0
  395. package/dist/segment-params/index.d.ts.map +1 -0
  396. package/dist/segment-params/index.js +2 -0
  397. package/dist/server/access-gate.d.ts +8 -3
  398. package/dist/server/access-gate.d.ts.map +1 -1
  399. package/dist/server/action-client.d.ts +41 -6
  400. package/dist/server/action-client.d.ts.map +1 -1
  401. package/dist/server/action-encryption.d.ts +76 -0
  402. package/dist/server/action-encryption.d.ts.map +1 -0
  403. package/dist/server/action-handler.d.ts +35 -1
  404. package/dist/server/action-handler.d.ts.map +1 -1
  405. package/dist/server/actions.d.ts +6 -30
  406. package/dist/server/actions.d.ts.map +1 -1
  407. package/dist/server/als-registry.d.ts +85 -11
  408. package/dist/server/als-registry.d.ts.map +1 -1
  409. package/dist/server/asset-headers.d.ts +8 -18
  410. package/dist/server/asset-headers.d.ts.map +1 -1
  411. package/dist/server/body-limits.d.ts.map +1 -1
  412. package/dist/server/build-manifest.d.ts +6 -29
  413. package/dist/server/build-manifest.d.ts.map +1 -1
  414. package/dist/server/canonicalize.d.ts +10 -0
  415. package/dist/server/canonicalize.d.ts.map +1 -1
  416. package/dist/server/coalesce-tick.d.ts +40 -0
  417. package/dist/server/coalesce-tick.d.ts.map +1 -0
  418. package/dist/server/cookie-context.d.ts +170 -0
  419. package/dist/server/cookie-context.d.ts.map +1 -0
  420. package/dist/server/cookie-parsing.d.ts +65 -0
  421. package/dist/server/cookie-parsing.d.ts.map +1 -0
  422. package/dist/server/csrf.d.ts +3 -2
  423. package/dist/server/csrf.d.ts.map +1 -1
  424. package/dist/server/default-logger.d.ts +22 -0
  425. package/dist/server/default-logger.d.ts.map +1 -0
  426. package/dist/server/deny-boundary.d.ts +115 -0
  427. package/dist/server/deny-boundary.d.ts.map +1 -0
  428. package/dist/server/deny-renderer.d.ts +2 -2
  429. package/dist/server/deny-renderer.d.ts.map +1 -1
  430. package/dist/server/dev-source-map.d.ts +22 -0
  431. package/dist/server/dev-source-map.d.ts.map +1 -0
  432. package/dist/server/early-hints-sender.d.ts.map +1 -1
  433. package/dist/server/early-hints.d.ts +13 -11
  434. package/dist/server/early-hints.d.ts.map +1 -1
  435. package/dist/server/error-boundary-wrapper.d.ts +7 -1
  436. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  437. package/dist/server/fallback-error.d.ts +12 -7
  438. package/dist/server/fallback-error.d.ts.map +1 -1
  439. package/dist/server/flight-injection-state.d.ts +66 -0
  440. package/dist/server/flight-injection-state.d.ts.map +1 -0
  441. package/dist/server/flight-scripts.d.ts +42 -0
  442. package/dist/server/flight-scripts.d.ts.map +1 -0
  443. package/dist/server/form-data.d.ts +41 -0
  444. package/dist/server/form-data.d.ts.map +1 -1
  445. package/dist/server/html-injector-core.d.ts +221 -0
  446. package/dist/server/html-injector-core.d.ts.map +1 -0
  447. package/dist/server/html-injectors.d.ts +82 -41
  448. package/dist/server/html-injectors.d.ts.map +1 -1
  449. package/dist/server/index.d.ts +10 -46
  450. package/dist/server/index.d.ts.map +1 -1
  451. package/dist/server/index.js +170 -2911
  452. package/dist/server/index.js.map +1 -1
  453. package/dist/server/internal.d.ts +49 -0
  454. package/dist/server/internal.d.ts.map +1 -0
  455. package/dist/server/internal.js +3577 -0
  456. package/dist/server/internal.js.map +1 -0
  457. package/dist/server/logger.d.ts +39 -7
  458. package/dist/server/logger.d.ts.map +1 -1
  459. package/dist/server/metadata-routes.d.ts +23 -7
  460. package/dist/server/metadata-routes.d.ts.map +1 -1
  461. package/dist/server/middleware-runner.d.ts +36 -4
  462. package/dist/server/middleware-runner.d.ts.map +1 -1
  463. package/dist/server/node-stream-transforms.d.ts +119 -0
  464. package/dist/server/node-stream-transforms.d.ts.map +1 -0
  465. package/dist/server/param-coercion.d.ts +54 -0
  466. package/dist/server/param-coercion.d.ts.map +1 -0
  467. package/dist/server/pipeline-helpers.d.ts +95 -0
  468. package/dist/server/pipeline-helpers.d.ts.map +1 -0
  469. package/dist/server/pipeline-interception.d.ts +1 -1
  470. package/dist/server/pipeline-interception.d.ts.map +1 -1
  471. package/dist/server/pipeline-metadata.d.ts +6 -0
  472. package/dist/server/pipeline-metadata.d.ts.map +1 -1
  473. package/dist/server/pipeline-outcome.d.ts +49 -0
  474. package/dist/server/pipeline-outcome.d.ts.map +1 -0
  475. package/dist/server/pipeline-phases.d.ts +56 -0
  476. package/dist/server/pipeline-phases.d.ts.map +1 -0
  477. package/dist/server/pipeline.d.ts +122 -19
  478. package/dist/server/pipeline.d.ts.map +1 -1
  479. package/dist/server/port-resolution.d.ts +117 -0
  480. package/dist/server/port-resolution.d.ts.map +1 -0
  481. package/dist/server/prebuilt/cache-key.d.ts +58 -0
  482. package/dist/server/prebuilt/cache-key.d.ts.map +1 -0
  483. package/dist/server/prebuilt/capture-state.d.ts +57 -0
  484. package/dist/server/prebuilt/capture-state.d.ts.map +1 -0
  485. package/dist/server/prebuilt/manifest-tag-index.d.ts +27 -0
  486. package/dist/server/prebuilt/manifest-tag-index.d.ts.map +1 -0
  487. package/dist/server/prebuilt/overlay.d.ts +100 -0
  488. package/dist/server/prebuilt/overlay.d.ts.map +1 -0
  489. package/dist/server/prebuilt/payload-source.d.ts +78 -0
  490. package/dist/server/prebuilt/payload-source.d.ts.map +1 -0
  491. package/dist/server/prebuilt/synthetic-store.d.ts +66 -0
  492. package/dist/server/prebuilt/synthetic-store.d.ts.map +1 -0
  493. package/dist/server/prebuilt-builder.d.ts +120 -0
  494. package/dist/server/prebuilt-builder.d.ts.map +1 -0
  495. package/dist/server/prebuilt-runtime.d.ts +101 -0
  496. package/dist/server/prebuilt-runtime.d.ts.map +1 -0
  497. package/dist/server/primitives.d.ts +83 -31
  498. package/dist/server/primitives.d.ts.map +1 -1
  499. package/dist/server/render-timeout.d.ts +51 -0
  500. package/dist/server/render-timeout.d.ts.map +1 -0
  501. package/dist/server/render-utils.d.ts +25 -0
  502. package/dist/server/render-utils.d.ts.map +1 -0
  503. package/dist/server/request-context.d.ts +62 -107
  504. package/dist/server/request-context.d.ts.map +1 -1
  505. package/dist/server/route-element-builder.d.ts +39 -25
  506. package/dist/server/route-element-builder.d.ts.map +1 -1
  507. package/dist/server/route-handler.d.ts.map +1 -1
  508. package/dist/server/route-matcher.d.ts +25 -36
  509. package/dist/server/route-matcher.d.ts.map +1 -1
  510. package/dist/server/rsc-entry/action-middleware-runner.d.ts +66 -0
  511. package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -0
  512. package/dist/server/rsc-entry/api-handler.d.ts +12 -3
  513. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
  514. package/dist/server/rsc-entry/error-renderer.d.ts +26 -13
  515. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  516. package/dist/server/rsc-entry/helpers.d.ts +51 -28
  517. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  518. package/dist/server/rsc-entry/index.d.ts +30 -8
  519. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  520. package/dist/server/rsc-entry/render-route.d.ts +50 -0
  521. package/dist/server/rsc-entry/render-route.d.ts.map +1 -0
  522. package/dist/server/rsc-entry/revalidate-renderer.d.ts +108 -0
  523. package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -0
  524. package/dist/server/rsc-entry/rsc-payload.d.ts +5 -6
  525. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  526. package/dist/server/rsc-entry/rsc-stream.d.ts +17 -1
  527. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  528. package/dist/server/rsc-entry/ssr-bridge.d.ts +5 -1
  529. package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
  530. package/dist/server/rsc-entry/ssr-renderer.d.ts +34 -4
  531. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  532. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +119 -0
  533. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -0
  534. package/dist/server/safe-load.d.ts +46 -0
  535. package/dist/server/safe-load.d.ts.map +1 -0
  536. package/dist/server/sensitive-fields.d.ts +74 -0
  537. package/dist/server/sensitive-fields.d.ts.map +1 -0
  538. package/dist/server/sitemap-generator.d.ts +129 -0
  539. package/dist/server/sitemap-generator.d.ts.map +1 -0
  540. package/dist/server/sitemap-handler.d.ts +22 -0
  541. package/dist/server/sitemap-handler.d.ts.map +1 -0
  542. package/dist/server/slot-resolver.d.ts +7 -5
  543. package/dist/server/slot-resolver.d.ts.map +1 -1
  544. package/dist/server/ssr-bridge-types.d.ts +121 -0
  545. package/dist/server/ssr-bridge-types.d.ts.map +1 -0
  546. package/dist/server/ssr-entry.d.ts +2 -43
  547. package/dist/server/ssr-entry.d.ts.map +1 -1
  548. package/dist/server/ssr-render.d.ts +64 -21
  549. package/dist/server/ssr-render.d.ts.map +1 -1
  550. package/dist/server/ssr-wrappers.d.ts +50 -0
  551. package/dist/server/ssr-wrappers.d.ts.map +1 -0
  552. package/dist/server/state-tree-diff.d.ts +12 -5
  553. package/dist/server/state-tree-diff.d.ts.map +1 -1
  554. package/dist/server/status-code-resolver.d.ts +22 -11
  555. package/dist/server/status-code-resolver.d.ts.map +1 -1
  556. package/dist/server/stream-utils.d.ts +47 -0
  557. package/dist/server/stream-utils.d.ts.map +1 -0
  558. package/dist/server/tracing.d.ts +44 -9
  559. package/dist/server/tracing.d.ts.map +1 -1
  560. package/dist/server/tree-builder.d.ts +62 -30
  561. package/dist/server/tree-builder.d.ts.map +1 -1
  562. package/dist/server/tree-match.d.ts +49 -0
  563. package/dist/server/tree-match.d.ts.map +1 -0
  564. package/dist/server/types.d.ts +71 -6
  565. package/dist/server/types.d.ts.map +1 -1
  566. package/dist/server/utils/escape-html.d.ts +14 -0
  567. package/dist/server/utils/escape-html.d.ts.map +1 -0
  568. package/dist/server/utils/mdx-file.d.ts +15 -0
  569. package/dist/server/utils/mdx-file.d.ts.map +1 -0
  570. package/dist/server/version-skew.d.ts +61 -0
  571. package/dist/server/version-skew.d.ts.map +1 -0
  572. package/dist/server/workers-cache-bridge.d.ts +26 -0
  573. package/dist/server/workers-cache-bridge.d.ts.map +1 -0
  574. package/dist/shared/href-validation.d.ts +49 -0
  575. package/dist/shared/href-validation.d.ts.map +1 -0
  576. package/dist/shared/merge-search-params.d.ts +22 -0
  577. package/dist/shared/merge-search-params.d.ts.map +1 -0
  578. package/dist/shared/redirect-type.d.ts +17 -0
  579. package/dist/shared/redirect-type.d.ts.map +1 -0
  580. package/dist/shims/font-google.d.ts +1 -1
  581. package/dist/shims/font-google.d.ts.map +1 -1
  582. package/dist/shims/font-google.js +42 -0
  583. package/dist/shims/font-google.js.map +1 -0
  584. package/dist/shims/font-local.d.ts +26 -0
  585. package/dist/shims/font-local.d.ts.map +1 -0
  586. package/dist/shims/font-local.js +20 -0
  587. package/dist/shims/font-local.js.map +1 -0
  588. package/dist/shims/headers.d.ts +53 -1
  589. package/dist/shims/headers.d.ts.map +1 -1
  590. package/dist/shims/image.d.ts +16 -16
  591. package/dist/shims/navigation-client.d.ts +35 -11
  592. package/dist/shims/navigation-client.d.ts.map +1 -1
  593. package/dist/shims/navigation.d.ts +11 -5
  594. package/dist/shims/navigation.d.ts.map +1 -1
  595. package/dist/utils/state-machine.d.ts +80 -0
  596. package/dist/utils/state-machine.d.ts.map +1 -0
  597. package/docs/api/30-api-server.mdx +233 -0
  598. package/docs/api/31-api-client.mdx +161 -0
  599. package/docs/api/32-api-cache.mdx +199 -0
  600. package/docs/api/33-api-search-params.mdx +141 -0
  601. package/docs/api/34-api-config.mdx +189 -0
  602. package/docs/api/35-api-typescript.mdx +170 -0
  603. package/docs/api/36-cli.mdx +143 -0
  604. package/docs/learn/00-introduction.mdx +76 -0
  605. package/docs/learn/01-quick-start.mdx +94 -0
  606. package/docs/learn/02-pages-and-layouts.mdx +147 -0
  607. package/docs/learn/03-fetching-data.mdx +211 -0
  608. package/docs/learn/03b-access-control.mdx +130 -0
  609. package/docs/learn/04-loading-states.mdx +67 -0
  610. package/docs/learn/04b-the-flush-point.mdx +115 -0
  611. package/docs/learn/05-typed-params.mdx +275 -0
  612. package/docs/learn/06-forms-and-actions.mdx +146 -0
  613. package/docs/learn/07-typed-routes.mdx +127 -0
  614. package/docs/learn/08-streaming.mdx +146 -0
  615. package/docs/learn/09-caching.mdx +136 -0
  616. package/docs/learn/10-middleware.mdx +157 -0
  617. package/docs/learn/11-error-handling.mdx +156 -0
  618. package/docs/learn/12-client-navigation.mdx +168 -0
  619. package/docs/learn/13-configuration.mdx +145 -0
  620. package/docs/learn/14-deploying.mdx +264 -0
  621. package/docs/more/01-advanced-routing.mdx +139 -0
  622. package/docs/more/02-advanced-forms.mdx +137 -0
  623. package/docs/more/03-coming-from-nextjs.mdx +186 -0
  624. package/docs/more/04-metadata-and-fonts.mdx +193 -0
  625. package/docs/more/04b-mdx.mdx +209 -0
  626. package/docs/more/05-content-collections.mdx +90 -0
  627. package/docs/more/06-instrumentation.mdx +214 -0
  628. package/docs/more/07-security.mdx +129 -0
  629. package/docs/more/08-developer-experience.mdx +134 -0
  630. package/docs/more/40-why-timber.mdx +50 -0
  631. package/docs/more/41-timber-vs-nextjs.mdx +81 -0
  632. package/docs/more/42-timber-vs-others.mdx +68 -0
  633. package/docs/more/50-ai-agent-instructions.mdx +169 -0
  634. package/llms.txt +55 -0
  635. package/package.json +102 -26
  636. package/src/adapters/build-output-helper.ts +199 -0
  637. package/src/adapters/cloudflare-dev.ts +177 -0
  638. package/src/adapters/cloudflare-kv-cache.ts +179 -0
  639. package/src/adapters/cloudflare.ts +594 -119
  640. package/src/adapters/compress-module.ts +27 -92
  641. package/src/adapters/nitro.ts +131 -96
  642. package/src/adapters/raw-imports.d.ts +5 -0
  643. package/src/adapters/shared.ts +68 -0
  644. package/src/adapters/wrangler.d.ts +7 -0
  645. package/src/cache/cache-api.ts +107 -0
  646. package/src/cache/handler-store.ts +76 -0
  647. package/src/cache/index.ts +88 -18
  648. package/src/cache/invalidation-epoch.ts +93 -0
  649. package/src/cache/redis-handler.ts +122 -15
  650. package/src/cache/singleflight.ts +64 -6
  651. package/src/cache/sizeof.ts +33 -0
  652. package/src/cache/stable-stringify.ts +65 -2
  653. package/src/cache/store.ts +71 -0
  654. package/src/cache/stores/cloudflare-kv.ts +52 -0
  655. package/src/cache/stores/memory.ts +125 -0
  656. package/src/cache/stores/redis.ts +97 -0
  657. package/src/cache/stores/vercel.ts +59 -0
  658. package/src/cache/tag-aware-handler.ts +233 -0
  659. package/src/cache/timber-cache.ts +142 -36
  660. package/src/cdn/cloudflare-purge.ts +50 -0
  661. package/src/cdn/fastly-purge.ts +63 -0
  662. package/src/cdn/headers.ts +153 -0
  663. package/src/cdn/index.ts +17 -0
  664. package/src/cdn/purge-store.ts +34 -0
  665. package/src/cdn/workers-cache-purge.ts +30 -0
  666. package/src/cli-schema-sync.ts +388 -0
  667. package/src/cli.ts +157 -34
  668. package/src/client/browser-dev.ts +156 -1
  669. package/src/client/browser-entry/action-dispatch.ts +124 -0
  670. package/src/client/browser-entry/hmr.ts +70 -0
  671. package/src/client/browser-entry/hydrate.ts +187 -0
  672. package/src/client/browser-entry/index.ts +111 -0
  673. package/src/client/browser-entry/post-hydration.ts +103 -0
  674. package/src/client/browser-entry/router-init.ts +287 -0
  675. package/src/client/browser-entry/rsc-stream.ts +157 -0
  676. package/src/client/browser-entry/scroll.ts +50 -0
  677. package/src/client/error-boundary.tsx +127 -22
  678. package/src/client/error-reconstituter.tsx +79 -0
  679. package/src/client/form.tsx +31 -32
  680. package/src/client/history.ts +11 -7
  681. package/src/client/index.ts +67 -45
  682. package/src/client/internal.ts +58 -0
  683. package/src/client/link.tsx +346 -150
  684. package/src/client/navigation-api.ts +341 -0
  685. package/src/client/navigation-context.ts +15 -7
  686. package/src/client/navigation-root.tsx +355 -0
  687. package/src/client/nuqs-adapter.tsx +16 -3
  688. package/src/client/router-ref.ts +1 -1
  689. package/src/client/router.ts +482 -166
  690. package/src/client/rsc-fetch.ts +214 -33
  691. package/src/client/segment-cache.ts +7 -7
  692. package/src/client/segment-outlet.tsx +77 -0
  693. package/src/client/segment-update-context.ts +42 -0
  694. package/src/client/shallow-url.ts +22 -0
  695. package/src/client/ssr-data.ts +13 -5
  696. package/src/client/state.ts +2 -2
  697. package/src/client/top-loader.tsx +11 -9
  698. package/src/client/use-cookie.ts +49 -14
  699. package/src/client/use-link-status.ts +7 -7
  700. package/src/client/use-pathname.ts +1 -0
  701. package/src/client/use-pending-navigation.ts +40 -0
  702. package/src/client/use-query-states.ts +9 -3
  703. package/src/client/use-router.ts +4 -1
  704. package/src/client/use-search-params.ts +31 -44
  705. package/src/client/{use-params.ts → use-segment-params.ts} +9 -6
  706. package/src/codec.ts +147 -0
  707. package/src/config-types.ts +302 -0
  708. package/src/config-validation.ts +328 -0
  709. package/src/content/index.ts +5 -13
  710. package/src/cookies/define-cookie.ts +217 -53
  711. package/src/cookies/index.ts +12 -1
  712. package/src/cookies/json-cookie.ts +105 -0
  713. package/src/cookies/validation.ts +192 -0
  714. package/src/dev-tools/404-page.ts +286 -0
  715. package/src/dev-tools/browser-logs.ts +55 -0
  716. package/src/dev-tools/dev-page-shell.ts +260 -0
  717. package/src/dev-tools/error-page.ts +331 -0
  718. package/src/dev-tools/holding-server.ts +187 -0
  719. package/src/dev-tools/hydration-overlay.ts +158 -0
  720. package/src/dev-tools/index.ts +83 -0
  721. package/src/dev-tools/instrumentation.ts +213 -0
  722. package/src/{server/dev-logger.ts → dev-tools/logger.ts} +33 -51
  723. package/src/{plugins/dev-logs.ts → dev-tools/logs.ts} +9 -5
  724. package/src/dev-tools/overlay.ts +469 -0
  725. package/src/dev-tools/stack-classifier.ts +75 -0
  726. package/src/dev-tools/terminal.ts +182 -0
  727. package/src/fonts/ast-walk.ts +235 -0
  728. package/src/fonts/ast.ts +477 -126
  729. package/src/fonts/bundle.ts +138 -0
  730. package/src/fonts/dev-middleware.ts +85 -0
  731. package/src/fonts/fallbacks.ts +0 -23
  732. package/src/fonts/google.ts +63 -8
  733. package/src/fonts/local.ts +9 -8
  734. package/src/fonts/paths.ts +58 -0
  735. package/src/fonts/pipeline.ts +316 -0
  736. package/src/fonts/transform.ts +305 -0
  737. package/src/fonts/types.ts +52 -1
  738. package/src/fonts/virtual-modules.ts +179 -0
  739. package/src/index.ts +431 -352
  740. package/src/params/codec-registry.ts +34 -0
  741. package/src/params/define-schema.ts +115 -0
  742. package/src/params/index.ts +17 -0
  743. package/src/params/resolve-schema.ts +84 -0
  744. package/src/plugin-context.ts +378 -0
  745. package/src/plugins/adapter-build.ts +98 -4
  746. package/src/plugins/build-manifest.ts +168 -23
  747. package/src/plugins/build-report.ts +253 -35
  748. package/src/plugins/cache.ts +186 -0
  749. package/src/plugins/callsite-ast.ts +167 -0
  750. package/src/plugins/callsite-jsx.ts +243 -0
  751. package/src/plugins/chunks.ts +74 -5
  752. package/src/plugins/client-chunks.ts +144 -0
  753. package/src/plugins/cloudflare-wasm.ts +141 -0
  754. package/src/plugins/content.ts +1 -1
  755. package/src/plugins/dev-server.ts +293 -46
  756. package/src/plugins/entries.ts +129 -8
  757. package/src/plugins/fonts.ts +191 -664
  758. package/src/plugins/mdx.ts +47 -10
  759. package/src/plugins/prebuilt-capture.ts +210 -0
  760. package/src/plugins/prebuilt.ts +700 -0
  761. package/src/plugins/prerender-sugar.ts +329 -0
  762. package/src/plugins/routing.ts +237 -241
  763. package/src/plugins/server-bundle.ts +97 -12
  764. package/src/plugins/shims.ts +217 -64
  765. package/src/plugins/static-build.ts +15 -90
  766. package/src/routing/codegen-shared.ts +115 -0
  767. package/src/routing/codegen-types.ts +65 -0
  768. package/src/routing/codegen.ts +360 -178
  769. package/src/routing/convention-lint.ts +394 -0
  770. package/src/routing/export-detect.ts +304 -0
  771. package/src/routing/file-cache.ts +23 -0
  772. package/src/routing/index.ts +10 -1
  773. package/src/routing/interception.ts +3 -22
  774. package/src/routing/link-codegen.ts +310 -0
  775. package/src/routing/manifest-codegen.ts +281 -0
  776. package/src/routing/scanner.ts +248 -188
  777. package/src/routing/schema-validation.ts +149 -0
  778. package/src/routing/segment-classify.ts +188 -0
  779. package/src/routing/segment-codegen.ts +98 -0
  780. package/src/routing/types.ts +78 -25
  781. package/src/routing/walkers.ts +90 -0
  782. package/src/rsc-runtime/rsc.ts +9 -0
  783. package/src/rsc-runtime/ssr.ts +50 -0
  784. package/src/rsc-runtime/vendor-types.d.ts +7 -0
  785. package/src/schema-bridge.ts +252 -0
  786. package/src/search-params/define.ts +570 -0
  787. package/src/search-params/index.ts +14 -20
  788. package/src/search-params/registry.ts +2 -2
  789. package/src/search-params/wrappers.ts +110 -0
  790. package/src/segment-params/define.ts +249 -0
  791. package/src/segment-params/index.ts +9 -0
  792. package/src/server/access-gate.tsx +84 -34
  793. package/src/server/action-client.ts +119 -28
  794. package/src/server/action-encryption.ts +144 -0
  795. package/src/server/action-handler.ts +123 -33
  796. package/src/server/actions.ts +39 -54
  797. package/src/server/als-registry.ts +129 -20
  798. package/src/server/asset-headers.ts +15 -37
  799. package/src/server/body-limits.ts +10 -2
  800. package/src/server/build-manifest.ts +13 -56
  801. package/src/server/canonicalize.ts +6 -3
  802. package/src/server/coalesce-tick.ts +75 -0
  803. package/src/server/compress.ts +34 -7
  804. package/src/server/cookie-context.ts +484 -0
  805. package/src/server/cookie-parsing.ts +163 -0
  806. package/src/server/csrf.ts +39 -6
  807. package/src/server/default-logger.ts +99 -0
  808. package/src/server/deny-boundary.ts +311 -0
  809. package/src/server/deny-renderer.ts +85 -77
  810. package/src/server/dev-source-map.ts +31 -0
  811. package/src/server/early-hints-sender.ts +3 -2
  812. package/src/server/early-hints.ts +37 -27
  813. package/src/server/error-boundary-wrapper.ts +61 -22
  814. package/src/server/fallback-error.ts +79 -102
  815. package/src/server/flight-injection-state.ts +113 -0
  816. package/src/server/flight-scripts.ts +62 -0
  817. package/src/server/form-data.ts +124 -12
  818. package/src/server/html-injector-core.ts +437 -0
  819. package/src/server/html-injectors.ts +267 -254
  820. package/src/server/index.ts +29 -183
  821. package/src/server/internal.ts +181 -0
  822. package/src/server/logger.ts +72 -36
  823. package/src/server/metadata-routes.ts +44 -19
  824. package/src/server/middleware-runner.ts +75 -4
  825. package/src/server/node-stream-transforms.ts +331 -0
  826. package/src/server/param-coercion.ts +206 -0
  827. package/src/server/pipeline-helpers.ts +204 -0
  828. package/src/server/pipeline-interception.ts +32 -18
  829. package/src/server/pipeline-metadata.ts +14 -0
  830. package/src/server/pipeline-outcome.ts +197 -0
  831. package/src/server/pipeline-phases.ts +538 -0
  832. package/src/server/pipeline.ts +226 -382
  833. package/src/server/port-resolution.ts +215 -0
  834. package/src/server/prebuilt/cache-key.ts +224 -0
  835. package/src/server/prebuilt/capture-state.ts +89 -0
  836. package/src/server/prebuilt/manifest-tag-index.ts +46 -0
  837. package/src/server/prebuilt/overlay.ts +256 -0
  838. package/src/server/prebuilt/payload-source.ts +201 -0
  839. package/src/server/prebuilt/synthetic-store.ts +121 -0
  840. package/src/server/prebuilt-builder.ts +696 -0
  841. package/src/server/prebuilt-runtime.ts +708 -0
  842. package/src/server/primitives.ts +155 -63
  843. package/src/server/render-timeout.ts +108 -0
  844. package/src/server/render-utils.ts +31 -0
  845. package/src/server/request-context.ts +168 -337
  846. package/src/server/route-element-builder.ts +523 -233
  847. package/src/server/route-handler.ts +31 -4
  848. package/src/server/route-matcher.ts +144 -277
  849. package/src/server/rsc-entry/action-middleware-runner.ts +167 -0
  850. package/src/server/rsc-entry/api-handler.ts +69 -32
  851. package/src/server/rsc-entry/error-renderer.ts +306 -89
  852. package/src/server/rsc-entry/helpers.ts +160 -76
  853. package/src/server/rsc-entry/index.ts +393 -329
  854. package/src/server/rsc-entry/render-route.ts +284 -0
  855. package/src/server/rsc-entry/revalidate-renderer.ts +349 -0
  856. package/src/server/rsc-entry/rsc-payload.ts +72 -33
  857. package/src/server/rsc-entry/rsc-stream.ts +85 -13
  858. package/src/server/rsc-entry/ssr-bridge.ts +18 -5
  859. package/src/server/rsc-entry/ssr-renderer.ts +273 -43
  860. package/src/server/rsc-entry/wrap-action-dispatch.ts +449 -0
  861. package/src/server/safe-load.ts +60 -0
  862. package/src/server/sensitive-fields.ts +230 -0
  863. package/src/server/sitemap-generator.ts +344 -0
  864. package/src/server/sitemap-handler.ts +157 -0
  865. package/src/server/slot-resolver.ts +258 -257
  866. package/src/server/ssr-bridge-types.ts +124 -0
  867. package/src/server/ssr-entry.ts +206 -78
  868. package/src/server/ssr-render.ts +345 -82
  869. package/src/server/ssr-wrappers.tsx +141 -0
  870. package/src/server/state-tree-diff.ts +30 -23
  871. package/src/server/status-code-resolver.ts +115 -130
  872. package/src/server/stream-utils.ts +286 -0
  873. package/src/server/tracing.ts +126 -14
  874. package/src/server/tree-builder.ts +204 -90
  875. package/src/server/tree-match.ts +220 -0
  876. package/src/server/types.ts +78 -9
  877. package/src/server/utils/escape-html.ts +20 -0
  878. package/src/server/utils/mdx-file.ts +22 -0
  879. package/src/server/version-skew.ts +104 -0
  880. package/src/server/workers-cache-bridge.ts +33 -0
  881. package/src/shared/href-validation.ts +159 -0
  882. package/src/shared/merge-search-params.ts +55 -0
  883. package/src/shared/redirect-type.ts +17 -0
  884. package/src/shims/font-google.ts +1 -1
  885. package/src/shims/font-local.ts +34 -0
  886. package/src/shims/headers.ts +122 -1
  887. package/src/shims/navigation-client.ts +51 -29
  888. package/src/shims/navigation.ts +14 -7
  889. package/src/utils/state-machine.ts +111 -0
  890. package/dist/_chunks/als-registry-B7DbZ2hS.js +0 -41
  891. package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
  892. package/dist/_chunks/debug-B3Gypr3D.js +0 -108
  893. package/dist/_chunks/debug-B3Gypr3D.js.map +0 -1
  894. package/dist/_chunks/format-RyoGQL74.js +0 -164
  895. package/dist/_chunks/format-RyoGQL74.js.map +0 -1
  896. package/dist/_chunks/interception-BOoWmLUA.js +0 -676
  897. package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
  898. package/dist/_chunks/metadata-routes-Cjmvi3rQ.js.map +0 -1
  899. package/dist/_chunks/request-context-BQUC8PHn.js +0 -330
  900. package/dist/_chunks/request-context-BQUC8PHn.js.map +0 -1
  901. package/dist/_chunks/ssr-data-MjmprTmO.js.map +0 -1
  902. package/dist/_chunks/tracing-CemImE6h.js +0 -187
  903. package/dist/_chunks/tracing-CemImE6h.js.map +0 -1
  904. package/dist/_chunks/use-cookie-DX-l1_5E.js +0 -91
  905. package/dist/_chunks/use-cookie-DX-l1_5E.js.map +0 -1
  906. package/dist/_chunks/use-query-states-D5KaffOK.js.map +0 -1
  907. package/dist/cache/index.js.map +0 -1
  908. package/dist/cache/register-cached-function.d.ts +0 -17
  909. package/dist/cache/register-cached-function.d.ts.map +0 -1
  910. package/dist/client/browser-entry.d.ts +0 -21
  911. package/dist/client/browser-entry.d.ts.map +0 -1
  912. package/dist/client/head.d.ts +0 -16
  913. package/dist/client/head.d.ts.map +0 -1
  914. package/dist/client/link-status-provider.d.ts +0 -11
  915. package/dist/client/link-status-provider.d.ts.map +0 -1
  916. package/dist/client/segment-merger.d.ts +0 -119
  917. package/dist/client/segment-merger.d.ts.map +0 -1
  918. package/dist/client/stale-reload.d.ts +0 -44
  919. package/dist/client/stale-reload.d.ts.map +0 -1
  920. package/dist/client/transition-root.d.ts +0 -74
  921. package/dist/client/transition-root.d.ts.map +0 -1
  922. package/dist/client/use-navigation-pending.d.ts +0 -26
  923. package/dist/client/use-navigation-pending.d.ts.map +0 -1
  924. package/dist/client/use-params.d.ts.map +0 -1
  925. package/dist/plugins/cache-transform.d.ts +0 -36
  926. package/dist/plugins/cache-transform.d.ts.map +0 -1
  927. package/dist/plugins/dev-error-overlay.d.ts +0 -60
  928. package/dist/plugins/dev-error-overlay.d.ts.map +0 -1
  929. package/dist/plugins/dev-logs.d.ts.map +0 -1
  930. package/dist/plugins/dynamic-transform.d.ts +0 -72
  931. package/dist/plugins/dynamic-transform.d.ts.map +0 -1
  932. package/dist/plugins/server-action-exports.d.ts +0 -26
  933. package/dist/plugins/server-action-exports.d.ts.map +0 -1
  934. package/dist/routing/status-file-lint.d.ts +0 -33
  935. package/dist/routing/status-file-lint.d.ts.map +0 -1
  936. package/dist/search-params/analyze.d.ts +0 -54
  937. package/dist/search-params/analyze.d.ts.map +0 -1
  938. package/dist/search-params/builtin-codecs.d.ts +0 -105
  939. package/dist/search-params/builtin-codecs.d.ts.map +0 -1
  940. package/dist/search-params/codecs.d.ts +0 -53
  941. package/dist/search-params/codecs.d.ts.map +0 -1
  942. package/dist/search-params/create.d.ts +0 -106
  943. package/dist/search-params/create.d.ts.map +0 -1
  944. package/dist/server/client-module-map.d.ts +0 -47
  945. package/dist/server/client-module-map.d.ts.map +0 -1
  946. package/dist/server/dev-fetch-instrumentation.d.ts +0 -22
  947. package/dist/server/dev-fetch-instrumentation.d.ts.map +0 -1
  948. package/dist/server/dev-logger.d.ts.map +0 -1
  949. package/dist/server/dev-span-processor.d.ts +0 -29
  950. package/dist/server/dev-span-processor.d.ts.map +0 -1
  951. package/dist/server/dev-warnings.d.ts +0 -129
  952. package/dist/server/dev-warnings.d.ts.map +0 -1
  953. package/dist/server/flush.d.ts +0 -74
  954. package/dist/server/flush.d.ts.map +0 -1
  955. package/dist/server/manifest-status-resolver.d.ts +0 -58
  956. package/dist/server/manifest-status-resolver.d.ts.map +0 -1
  957. package/dist/server/prerender.d.ts +0 -77
  958. package/dist/server/prerender.d.ts.map +0 -1
  959. package/dist/utils/directive-parser.d.ts +0 -70
  960. package/dist/utils/directive-parser.d.ts.map +0 -1
  961. package/src/cache/register-cached-function.ts +0 -103
  962. package/src/client/browser-entry.ts +0 -678
  963. package/src/client/head.ts +0 -61
  964. package/src/client/link-status-provider.tsx +0 -30
  965. package/src/client/segment-merger.ts +0 -339
  966. package/src/client/stale-reload.ts +0 -87
  967. package/src/client/transition-root.tsx +0 -166
  968. package/src/client/use-navigation-pending.ts +0 -38
  969. package/src/plugins/cache-transform.ts +0 -199
  970. package/src/plugins/dev-error-overlay.ts +0 -230
  971. package/src/plugins/dynamic-transform.ts +0 -161
  972. package/src/plugins/server-action-exports.ts +0 -220
  973. package/src/routing/status-file-lint.ts +0 -112
  974. package/src/search-params/analyze.ts +0 -192
  975. package/src/search-params/builtin-codecs.ts +0 -228
  976. package/src/search-params/codecs.ts +0 -153
  977. package/src/search-params/create.ts +0 -321
  978. package/src/server/client-module-map.ts +0 -58
  979. package/src/server/dev-fetch-instrumentation.ts +0 -96
  980. package/src/server/dev-span-processor.ts +0 -78
  981. package/src/server/dev-warnings.ts +0 -283
  982. package/src/server/flush.ts +0 -182
  983. package/src/server/manifest-status-resolver.ts +0 -215
  984. package/src/server/prerender.ts +0 -139
  985. package/src/utils/directive-parser.ts +0 -529
@@ -4,11 +4,20 @@
4
4
  import { SegmentCache, PrefetchCache, buildSegmentTree } from './segment-cache';
5
5
  import type { SegmentInfo } from './segment-cache';
6
6
  import { HistoryStack } from './history';
7
- import type { HeadElement } from './head';
8
- import { setCurrentParams } from './use-params.js';
9
- import { setNavigationState } from './navigation-context.js';
10
- import { SegmentElementCache, cacheSegmentElements, mergeSegmentTree } from './segment-merger.js';
11
- import { fetchRscPayload, RedirectError } from './rsc-fetch.js';
7
+ import { setCurrentParams } from './use-segment-params.js';
8
+ import {
9
+ setNavigationState,
10
+ getNavigationState,
11
+ type NavigationState,
12
+ } from './navigation-context.js';
13
+
14
+ import {
15
+ fetchRscPayload,
16
+ RedirectError,
17
+ ServerErrorResponse,
18
+ VersionSkewError,
19
+ } from './rsc-fetch.js';
20
+ import { setHardNavigating, supersedeNavigationTransitions } from './navigation-root.js';
12
21
  import type { FetchResult } from './rsc-fetch.js';
13
22
 
14
23
  // ─── Types ───────────────────────────────────────────────────────
@@ -18,6 +27,19 @@ export interface NavigationOptions {
18
27
  scroll?: boolean;
19
28
  /** Use replaceState instead of pushState (replaces current history entry) */
20
29
  replace?: boolean;
30
+ /**
31
+ * @internal AbortSignal from the Navigation API's NavigateEvent.
32
+ * When provided, the signal is linked to the router's per-navigation
33
+ * AbortController so in-flight RSC fetches are cancelled when a new
34
+ * navigation starts.
35
+ */
36
+ _signal?: AbortSignal;
37
+ /**
38
+ * @internal Skip pushState/replaceState — the Navigation API has already
39
+ * updated the URL via event.intercept(). Used for external navigations
40
+ * intercepted by the navigate event handler.
41
+ */
42
+ _skipHistory?: boolean;
21
43
  }
22
44
 
23
45
  /**
@@ -31,8 +53,12 @@ export type RscDecoder = (fetchPromise: Promise<Response>) => unknown;
31
53
  * Function that renders a decoded RSC element tree into the DOM.
32
54
  * In production: reactRoot.render(element).
33
55
  * In tests: a no-op or mock.
56
+ *
57
+ * Receives the current NavigationState explicitly — no temporal
58
+ * coupling with setNavigationState/getNavigationState. The renderer
59
+ * wraps the element in NavigationProvider with this state.
34
60
  */
35
- export type RootRenderer = (element: unknown) => void;
61
+ export type RootRenderer = (element: unknown, navState: NavigationState) => void;
36
62
 
37
63
  /**
38
64
  * Platform dependencies injected for testability. In production these
@@ -55,8 +81,6 @@ export interface RouterDeps {
55
81
  * In tests, this runs the callback synchronously.
56
82
  */
57
83
  afterPaint?: (callback: () => void) => void;
58
- /** Apply resolved head elements (title, meta tags) to the DOM after navigation. */
59
- applyHead?: (elements: HeadElement[]) => void;
60
84
  /**
61
85
  * Run a navigation inside a React transition with optimistic pending URL.
62
86
  * The pending URL shows immediately (useOptimistic urgent update) and
@@ -64,14 +88,73 @@ export interface RouterDeps {
64
88
  *
65
89
  * The `perform` callback receives a `wrapPayload` function to wrap the
66
90
  * decoded RSC payload with NavigationProvider + NuqsAdapter before
67
- * TransitionRoot sets it as the new element.
91
+ * NavigationRoot sets it as the new element. The `wrapPayload` function
92
+ * receives the NavigationState explicitly — no temporal coupling with
93
+ * getNavigationState().
68
94
  *
69
95
  * If not provided (tests), the router falls back to renderRoot.
70
96
  */
71
97
  navigateTransition?: (
72
98
  pendingUrl: string,
73
- perform: (wrapPayload: (payload: unknown) => unknown) => Promise<unknown>
99
+ perform: (
100
+ wrapPayload: (
101
+ payload: unknown,
102
+ navState: NavigationState,
103
+ segmentUpdates?: Map<string, unknown>
104
+ ) => unknown
105
+ ) => Promise<{ element: unknown; decodePromise: Promise<void> | null }>
74
106
  ) => Promise<void>;
107
+
108
+ /**
109
+ * Whether the Navigation API is active and handling traversals.
110
+ * When true, the popstate handler is a no-op — the Navigation API's
111
+ * navigate event covers back/forward button presses.
112
+ */
113
+ navigationApiActive?: boolean;
114
+
115
+ /**
116
+ * Called around pushState/replaceState to set a flag that prevents
117
+ * the Navigation API's navigate listener from double-handling
118
+ * router-initiated navigations.
119
+ */
120
+ setRouterNavigating?: (value: boolean) => void;
121
+
122
+ /**
123
+ * Save scroll position via the Navigation API's per-entry state.
124
+ * When provided, used instead of history.replaceState for scroll storage.
125
+ */
126
+ saveNavigationEntryScroll?: (scrollY: number) => void;
127
+
128
+ /**
129
+ * Signal that a router-initiated navigation has completed. Resolves the
130
+ * deferred promise that ties the browser's native loading state to the
131
+ * navigation lifecycle. Called in the finally block of navigate/refresh,
132
+ * aligned with when the TopLoader's pendingUrl clears.
133
+ */
134
+ completeRouterNavigation?: () => void;
135
+
136
+ /**
137
+ * Get the current unwrapped RSC payload element. Used for partial
138
+ * navigation: the router re-wraps the same element with new
139
+ * NavigationProvider context so mounted SegmentOutlets are preserved.
140
+ */
141
+ _getCurrentPayload?: () => unknown;
142
+
143
+ /**
144
+ * Initiate a navigation via the Navigation API (`navigation.navigate()`).
145
+ * Fires the navigate event BEFORE committing the URL, allowing Chrome
146
+ * to show its native loading indicator. Falls back to pushState when
147
+ * unavailable.
148
+ */
149
+ navigationNavigate?: (url: string, replace: boolean) => void;
150
+
151
+ /**
152
+ * Scroll the element matching a URL #fragment into view. Returns true
153
+ * when a matching element was found and scrolled. When absent or false,
154
+ * the router falls back to scroll-to-top on forward navigation — same
155
+ * as a full page load with an unknown fragment landing at the top.
156
+ */
157
+ scrollToHash?: (hash: string) => boolean;
75
158
  }
76
159
 
77
160
  export interface RouterInstance {
@@ -80,7 +163,7 @@ export interface RouterInstance {
80
163
  /** Full re-render of the current URL — no state tree sent */
81
164
  refresh(): Promise<void>;
82
165
  /** Handle a popstate event (back/forward button). scrollY is read from history.state. */
83
- handlePopState(url: string, scrollY?: number): Promise<void>;
166
+ handlePopState(url: string, scrollY?: number, externalSignal?: AbortSignal): Promise<void>;
84
167
  /** Whether a navigation is currently in flight */
85
168
  isPending(): boolean;
86
169
  /** The URL currently being navigated to, or null if idle */
@@ -94,18 +177,13 @@ export interface RouterInstance {
94
177
  * Renders the element tree and updates head elements without a server fetch.
95
178
  * See design/08-forms-and-actions.md §"Single-Roundtrip Revalidation".
96
179
  */
97
- applyRevalidation(element: unknown, headElements: HeadElement[] | null): void;
180
+ applyRevalidation(element: unknown): void;
98
181
  /**
99
182
  * Populate the segment cache from server-provided segment metadata.
100
183
  * Called on initial hydration with segment info embedded in the HTML.
101
184
  */
102
185
  initSegmentCache(segments: SegmentInfo[]): void;
103
- /**
104
- * Cache segment elements from a decoded RSC element tree.
105
- * Called on initial hydration to populate the element cache so the
106
- * first client navigation can use partial payloads.
107
- */
108
- cacheElementTree(element: unknown): void;
186
+
109
187
  /** The segment cache (exposed for tests and <Link> prefetch) */
110
188
  segmentCache: SegmentCache;
111
189
  /** The prefetch cache (exposed for tests and <Link> prefetch) */
@@ -130,24 +208,83 @@ function isAbortError(error: unknown): boolean {
130
208
  * Create a router instance. In production, called once at app hydration
131
209
  * with real browser APIs. In tests, called with mock dependencies.
132
210
  */
211
+ /**
212
+ * Router navigation phase — discriminated union replacing scattered
213
+ * `pending` + `pendingUrl` boolean flags.
214
+ *
215
+ * - `idle`: No navigation in flight. The committed params/pathname
216
+ * are current.
217
+ * - `navigating`: A fetch or render is in progress. `targetUrl` is
218
+ * the destination being navigated to.
219
+ */
220
+ export type RouterPhase = { phase: 'idle' } | { phase: 'navigating'; targetUrl: string };
221
+
133
222
  export function createRouter(deps: RouterDeps): RouterInstance {
134
223
  const segmentCache = new SegmentCache();
135
224
  const prefetchCache = new PrefetchCache();
136
225
  const historyStack = new HistoryStack();
137
- const segmentElementCache = new SegmentElementCache();
138
-
139
- let pending = false;
140
- let pendingUrl: string | null = null;
226
+ let routerPhase: RouterPhase = { phase: 'idle' };
141
227
  const pendingListeners = new Set<(pending: boolean) => void>();
142
228
 
229
+ // AbortController for the current in-flight navigation.
230
+ // When a new navigation starts, the previous controller is aborted,
231
+ // cancelling any in-progress RSC fetch. This provides automatic
232
+ // cancellation of stale fetches regardless of Navigation API support.
233
+ let currentNavAbort: AbortController | null = null;
234
+
235
+ /**
236
+ * Create a new AbortController for a navigation, superseding any
237
+ * previous in-flight navigation. Optionally links to an external
238
+ * signal (e.g., from the Navigation API's NavigateEvent.signal).
239
+ *
240
+ * Superseding is one operation with three parts:
241
+ * 1. Abort the previous navigation's fetch.
242
+ * 2. Invalidate its render transition so a response that already
243
+ * arrived can't commit a stale tree (NavigationRoot's transId guard).
244
+ * 3. Resolve its Navigation API deferred — the superseded navigation's
245
+ * finally block is staleness-guarded (see TIM-1034) and no longer
246
+ * cleans up after itself, so the browser's native loading state for
247
+ * the dead navigation is cleared here.
248
+ */
249
+ function createNavAbort(externalSignal?: AbortSignal): AbortController {
250
+ if (currentNavAbort) {
251
+ currentNavAbort.abort();
252
+ supersedeNavigationTransitions();
253
+ deps.completeRouterNavigation?.();
254
+ }
255
+ const controller = new AbortController();
256
+ currentNavAbort = controller;
257
+
258
+ // If an external signal is provided (e.g., Navigation API),
259
+ // forward its abort to our controller.
260
+ if (externalSignal) {
261
+ if (externalSignal.aborted) {
262
+ controller.abort();
263
+ } else {
264
+ externalSignal.addEventListener('abort', () => controller.abort(), { once: true });
265
+ }
266
+ }
267
+
268
+ return controller;
269
+ }
270
+
143
271
  function setPending(value: boolean, url?: string): void {
144
- const newPendingUrl = value && url ? url : null;
145
- if (pending === value && pendingUrl === newPendingUrl) return;
146
- pending = value;
147
- pendingUrl = newPendingUrl;
272
+ const next: RouterPhase =
273
+ value && url ? { phase: 'navigating', targetUrl: url } : { phase: 'idle' };
274
+ // Skip no-op updates
275
+ if (
276
+ routerPhase.phase === next.phase &&
277
+ (routerPhase.phase === 'idle' ||
278
+ (routerPhase.phase === 'navigating' &&
279
+ next.phase === 'navigating' &&
280
+ routerPhase.targetUrl === next.targetUrl))
281
+ ) {
282
+ return;
283
+ }
284
+ routerPhase = next;
148
285
  // Notify external store listeners (non-React consumers).
149
286
  // React-facing pending state is handled by useOptimistic in
150
- // TransitionRoot via navigateTransition — not this function.
287
+ // NavigationRoot via navigateTransition — not this function.
151
288
  for (const listener of pendingListeners) {
152
289
  listener(value);
153
290
  }
@@ -163,100 +300,124 @@ export function createRouter(deps: RouterDeps): RouterInstance {
163
300
  }
164
301
 
165
302
  /** Render a decoded RSC payload into the DOM if a renderer is available. */
166
- function renderPayload(payload: unknown): void {
303
+ function renderPayload(payload: unknown, navState: NavigationState): void {
167
304
  if (deps.renderRoot) {
168
- deps.renderRoot(payload);
305
+ deps.renderRoot(payload, navState);
169
306
  }
170
307
  }
171
308
 
309
+ function isPartialNavigation(skippedSegments: string[] | null | undefined): boolean {
310
+ return skippedSegments != null && skippedSegments.length > 0;
311
+ }
312
+
172
313
  /**
173
- * Merge a partial RSC payload with cached segment elements if segments
174
- * were skipped, then cache segments from the (merged) payload.
175
- * Returns the merged payload ready for rendering.
314
+ * Build a segment updates map for partial navigation. Identifies the
315
+ * first non-skipped segment and maps it to the payload content.
176
316
  */
177
- function mergeAndCachePayload(
178
- payload: unknown,
179
- skippedSegments: string[] | null | undefined
180
- ): unknown {
181
- let merged = payload;
182
-
183
- // If segments were skipped, merge the partial payload with cached segments
184
- if (skippedSegments && skippedSegments.length > 0) {
185
- merged = mergeSegmentTree(payload, skippedSegments, segmentElementCache);
317
+ function buildSegmentUpdates(result: FetchResult): Map<string, unknown> {
318
+ const skipped = new Set(result.skippedSegments!);
319
+ const segmentInfo = result.segmentInfo;
320
+ const updates = new Map<string, unknown>();
321
+ if (segmentInfo) {
322
+ for (const info of segmentInfo) {
323
+ if (!skipped.has(info.segmentId ?? info.path)) {
324
+ // Use segmentId (outlet key) when available — route groups
325
+ // use a disambiguated key like "/(marketing)" instead of "/".
326
+ updates.set(info.segmentId ?? info.path, result.payload);
327
+ break;
328
+ }
329
+ }
186
330
  }
187
-
188
- // Cache segment elements from the (merged) payload for future merges
189
- cacheSegmentElements(merged, segmentElementCache);
190
-
191
- return merged;
331
+ return updates;
192
332
  }
193
333
 
194
334
  /**
195
335
  * Update navigation state (params + pathname) for the next render.
196
336
  *
197
- * Sets both the module-level fallback (for tests and SSR) and the
198
- * navigation context state (read by renderRoot to wrap the element
199
- * in NavigationProvider). The context update is atomic with the tree
200
- * render — both are passed to reactRoot.render() in the same call.
337
+ * Sets the module-level fallback (for tests and SSR) and the
338
+ * globalThis bridge, then returns the NavigationState so callers
339
+ * can pass it explicitly to renderRoot/wrapPayload — eliminating
340
+ * temporal coupling with getNavigationState().
201
341
  */
202
342
  function updateNavigationState(
203
343
  params: Record<string, string | string[]> | null | undefined,
204
344
  url: string
205
- ): void {
345
+ ): NavigationState {
206
346
  const resolvedParams = params ?? {};
207
347
  // Module-level fallback for tests (no NavigationProvider) and SSR
208
348
  setCurrentParams(resolvedParams);
209
- // Navigation context — read by renderRoot to wrap the RSC element
210
- const pathname = url.startsWith('http') ? new URL(url).pathname : url.split('?')[0] || '/';
211
- setNavigationState({ params: resolvedParams, pathname });
349
+ // globalThis bridge — kept for backward compat
350
+ const parsed = new URL(url, 'http://localhost');
351
+ const pathname = parsed.pathname || '/';
352
+ const search = parsed.search;
353
+ const navState: NavigationState = { params: resolvedParams, pathname, search };
354
+ setNavigationState(navState);
355
+ return navState;
212
356
  }
213
357
 
214
358
  /**
215
359
  * Render a payload via navigateTransition (production) or renderRoot (tests).
216
- * The perform callback should fetch data, update state, and return the payload.
217
- * In production, the entire callback runs inside a React transition with
218
- * useOptimistic for the pending URL. In tests, the payload is rendered directly.
360
+ * The perform callback should fetch data, update state, and return the
361
+ * FetchResult plus the NavigationState (so it can be passed explicitly
362
+ * to wrapPayload/renderRoot without temporal coupling).
219
363
  */
220
364
  async function renderViaTransition(
221
365
  url: string,
222
- perform: () => Promise<FetchResult>
223
- ): Promise<HeadElement[] | null> {
366
+ perform: () => Promise<FetchResult & { navState: NavigationState }>
367
+ ): Promise<void> {
224
368
  if (deps.navigateTransition) {
225
- let headElements: HeadElement[] | null = null;
226
369
  await deps.navigateTransition(url, async (wrapPayload) => {
227
370
  const result = await perform();
228
- headElements = result.headElements;
229
- // Merge partial payload with cached segments before wrapping
230
- const merged = mergeAndCachePayload(result.payload, result.skippedSegments);
231
- // Store the MERGED payload in history — not the partial pre-merge tree.
232
- // This ensures handlePopState replays the complete tree on back/forward.
371
+
372
+ if (isPartialNavigation(result.skippedSegments)) {
373
+ const segmentUpdates = buildSegmentUpdates(result);
374
+
375
+ historyStack.push(url, {
376
+ payload: null,
377
+ params: result.params,
378
+ });
379
+
380
+ // Re-wrap the CURRENT element with new context values.
381
+ // SegmentOutlets read updates from SegmentUpdateContext;
382
+ // NavigationProvider gets new params/pathname.
383
+ const element = wrapPayload(
384
+ deps._getCurrentPayload?.() ?? result.payload,
385
+ result.navState,
386
+ segmentUpdates
387
+ );
388
+ return { element, decodePromise: result.decodePromise };
389
+ }
390
+
391
+ // Full navigation — empty updates, render the new tree.
233
392
  historyStack.push(url, {
234
- payload: merged,
235
- headElements: result.headElements,
393
+ payload: result.payload,
236
394
  params: result.params,
237
395
  });
238
- return wrapPayload(merged);
396
+ const element = wrapPayload(result.payload, result.navState);
397
+ return { element, decodePromise: result.decodePromise };
239
398
  });
240
- return headElements;
399
+ return;
241
400
  }
242
401
  // Fallback: no transition (tests, no React tree)
243
402
  const result = await perform();
244
- // Merge partial payload with cached segments before rendering
245
- const merged = mergeAndCachePayload(result.payload, result.skippedSegments);
246
- // Store merged payload in history
247
- historyStack.push(url, {
248
- payload: merged,
249
- headElements: result.headElements,
250
- params: result.params,
251
- });
252
- renderPayload(merged);
253
- return result.headElements;
254
- }
255
-
256
- /** Apply head elements (title, meta tags) to the DOM if available. */
257
- function applyHead(elements: HeadElement[] | null | undefined): void {
258
- if (elements && deps.applyHead) {
259
- deps.applyHead(elements);
403
+ if (isPartialNavigation(result.skippedSegments)) {
404
+ historyStack.push(url, {
405
+ payload: null,
406
+ params: result.params,
407
+ });
408
+ } else {
409
+ // Resolve thenables before storing/rendering in the fallback path
410
+ // (tests without navigateTransition). In production, React handles
411
+ // thenables via Suspense; in tests, renderRoot expects resolved values.
412
+ const payload =
413
+ result.payload != null && typeof result.payload === 'object' && 'then' in result.payload
414
+ ? await (result.payload as PromiseLike<unknown>)
415
+ : result.payload;
416
+ historyStack.push(url, {
417
+ payload,
418
+ params: result.params,
419
+ });
420
+ renderPayload(payload, result.navState);
260
421
  }
261
422
  }
262
423
 
@@ -269,21 +430,47 @@ export function createRouter(deps: RouterDeps): RouterInstance {
269
430
  }
270
431
  }
271
432
 
433
+ /**
434
+ * Schedule scroll restoration after the next paint and fire the
435
+ * scroll-restored event. Used by navigate, popstate, and refresh.
436
+ */
437
+ function restoreScrollAfterPaint(scrollY: number): void {
438
+ afterPaint(() => {
439
+ deps.scrollTo(0, scrollY);
440
+ window.dispatchEvent(new Event('timber:scroll-restored'));
441
+ });
442
+ }
443
+
444
+ /**
445
+ * Scroll to the element matching the URL #fragment after paint, falling
446
+ * back to scroll-to-top when no element matches (matching full-page-load
447
+ * behavior for an unknown fragment). Used by forward navigation to a
448
+ * hash-bearing URL (TIM-1035).
449
+ */
450
+ function scrollToHashAfterPaint(hash: string): void {
451
+ afterPaint(() => {
452
+ if (deps.scrollToHash?.(hash) !== true) {
453
+ deps.scrollTo(0, 0);
454
+ }
455
+ window.dispatchEvent(new Event('timber:scroll-restored'));
456
+ });
457
+ }
458
+
272
459
  /**
273
460
  * Core navigation logic shared between the transition and fallback paths.
274
461
  * Fetches the RSC payload, updates all state, and returns the result.
275
462
  */
276
463
  async function performNavigationFetch(
277
464
  url: string,
278
- options: { replace: boolean }
279
- ): Promise<FetchResult> {
465
+ options: { replace: boolean; commitUrl?: string; signal?: AbortSignal; skipHistory?: boolean }
466
+ ): Promise<FetchResult & { navState: NavigationState }> {
280
467
  // Check prefetch cache first. PrefetchResult has optional segmentInfo/params
281
468
  // fields — normalize to null for FetchResult compatibility.
282
469
  const prefetched = prefetchCache.consume(url);
283
470
  let result: FetchResult | undefined = prefetched
284
471
  ? {
285
472
  payload: prefetched.payload,
286
- headElements: prefetched.headElements,
473
+ decodePromise: null,
287
474
  segmentInfo: prefetched.segmentInfo ?? null,
288
475
  params: prefetched.params ?? null,
289
476
  skippedSegments: prefetched.skippedSegments ?? null,
@@ -293,19 +480,29 @@ export function createRouter(deps: RouterDeps): RouterInstance {
293
480
  if (result === undefined) {
294
481
  // Fetch RSC payload with state tree for partial rendering.
295
482
  // Send current URL for intercepting route resolution (modal pattern).
296
- const stateTree = segmentCache.serializeStateTree(segmentElementCache.getMergeablePaths());
483
+ const stateTree = segmentCache.serializeStateTree();
297
484
  const rawCurrentUrl = deps.getCurrentUrl();
298
485
  const currentUrl = rawCurrentUrl.startsWith('http')
299
486
  ? new URL(rawCurrentUrl).pathname
300
487
  : new URL(rawCurrentUrl, 'http://localhost').pathname;
301
- result = await fetchRscPayload(url, deps, stateTree, currentUrl);
488
+ result = await fetchRscPayload(url, deps, stateTree, currentUrl, options.signal);
302
489
  }
303
490
 
304
- // Update the browser history — replace mode overwrites the current entry
305
- if (options.replace) {
306
- deps.replaceState({ timber: true, scrollY: 0 }, '', url);
307
- } else {
308
- deps.pushState({ timber: true, scrollY: 0 }, '', url);
491
+ // Update the browser history — skip when the Navigation API has already
492
+ // updated the URL via event.intercept() (external navigations).
493
+ // The committed URL keeps the #fragment (commitUrl) even though the
494
+ // fetch/history-stack URL is hash-less (TIM-1035).
495
+ if (!options.skipHistory) {
496
+ const commitUrl = options.commitUrl ?? url;
497
+ // Set the router-navigating flag so the Navigation API's navigate
498
+ // listener doesn't double-intercept this pushState/replaceState.
499
+ deps.setRouterNavigating?.(true);
500
+ if (options.replace) {
501
+ deps.replaceState({ timber: true, scrollY: 0 }, '', commitUrl);
502
+ } else {
503
+ deps.pushState({ timber: true, scrollY: 0 }, '', commitUrl);
504
+ }
505
+ deps.setRouterNavigating?.(false);
309
506
  }
310
507
 
311
508
  // NOTE: History push is deferred — the merged payload (after segment
@@ -316,124 +513,230 @@ export function createRouter(deps: RouterDeps): RouterInstance {
316
513
  // Update the segment cache with the new route's segment tree.
317
514
  updateSegmentCache(result.segmentInfo);
318
515
 
319
- // Update navigation state (params + pathname) before rendering.
320
- updateNavigationState(result.params, url);
516
+ // Update navigation state and capture it for explicit passing.
517
+ const navState = updateNavigationState(result.params, url);
321
518
 
322
- return result;
519
+ return { ...result, navState };
323
520
  }
324
521
 
325
522
  async function navigate(url: string, options: NavigationOptions = {}): Promise<void> {
326
523
  const scroll = options.scroll !== false;
327
524
  const replace = options.replace === true;
525
+ const externalSignal = options._signal as AbortSignal | undefined;
526
+ const skipHistory = options._skipHistory === true;
527
+
528
+ // Split the #fragment off the navigation URL (TIM-1035). The full URL
529
+ // (with hash) is committed to the address bar; the hash-less URL is used
530
+ // for the RSC fetch (fragments are client-only — keeping it would also
531
+ // swallow the ?_rsc cache-bust param into the fragment) and for
532
+ // history-stack/prefetch keys (popstate lookups use pathname + search).
533
+ const hashIndex = url.indexOf('#');
534
+ const hash = hashIndex === -1 ? '' : url.slice(hashIndex);
535
+ const fetchUrl = hashIndex === -1 ? url : url.slice(0, hashIndex);
536
+
537
+ // Create an abort controller for this navigation. Links to the external
538
+ // signal (Navigation API's event.signal) when provided.
539
+ const navAbort = createNavAbort(externalSignal);
328
540
 
329
541
  // Capture the departing page's scroll position for scroll={false} preservation.
330
542
  const currentScrollY = deps.getScrollY();
331
543
 
332
- // Save the departing page's scroll position in history.state before
333
- // pushing a new entry. This ensures back/forward navigation can restore
334
- // the correct scroll position from the browser's per-entry state.
335
- deps.replaceState({ timber: true, scrollY: currentScrollY }, '', deps.getCurrentUrl());
544
+ // Save the departing page's scroll position — use Navigation API entry
545
+ // state when available, otherwise fall back to history.state.
546
+ if (deps.saveNavigationEntryScroll) {
547
+ deps.saveNavigationEntryScroll(currentScrollY);
548
+ } else {
549
+ deps.replaceState({ timber: true, scrollY: currentScrollY }, '', deps.getCurrentUrl());
550
+ }
551
+
552
+ // When Navigation API is active, initiate the navigation via
553
+ // navigation.navigate() BEFORE the fetch. Unlike history.pushState()
554
+ // which commits the URL synchronously (so Chrome sees it as "done"),
555
+ // navigation.navigate() fires the navigate event before committing.
556
+ // Our handler intercepts with a deferred promise, and Chrome shows
557
+ // its native loading indicator until completeRouterNavigation()
558
+ // resolves it in the finally block (same time as TopLoader clears).
559
+ let effectiveSkipHistory = skipHistory;
560
+ if (!skipHistory && deps.navigationNavigate) {
561
+ deps.setRouterNavigating?.(true);
562
+ deps.navigationNavigate(url, replace);
563
+ deps.setRouterNavigating?.(false);
564
+ effectiveSkipHistory = true;
565
+ }
336
566
 
337
567
  setPending(true, url);
338
568
 
339
569
  try {
340
- const headElements = await renderViaTransition(url, () =>
341
- performNavigationFetch(url, { replace })
570
+ await renderViaTransition(fetchUrl, () =>
571
+ performNavigationFetch(fetchUrl, {
572
+ replace,
573
+ commitUrl: url,
574
+ signal: navAbort.signal,
575
+ skipHistory: effectiveSkipHistory,
576
+ })
342
577
  );
343
578
 
344
- // Update document.title and <meta> tags with the new page's metadata
345
- applyHead(headElements);
346
-
347
579
  // Notify nuqs adapter (and any other listeners) that navigation completed.
348
580
  window.dispatchEvent(new Event('timber:navigation-end'));
349
581
 
350
- // Scroll-to-top on forward navigation, or restore captured position
351
- // for scroll={false}. React's render() on the document root can reset
352
- // scroll during DOM reconciliation, so all scroll must be actively managed.
353
- afterPaint(() => {
354
- if (scroll) {
355
- deps.scrollTo(0, 0);
356
- } else {
357
- deps.scrollTo(0, currentScrollY);
358
- }
359
- window.dispatchEvent(new Event('timber:scroll-restored'));
360
- });
582
+ // Scroll-to-top on forward navigation, scroll to the #fragment target
583
+ // when the URL has one, or restore captured position for scroll={false}.
584
+ // React's render() on the document root can reset scroll during DOM
585
+ // reconciliation, so all scroll must be actively managed.
586
+ if (scroll && hash) {
587
+ scrollToHashAfterPaint(hash);
588
+ } else {
589
+ restoreScrollAfterPaint(scroll ? 0 : currentScrollY);
590
+ }
361
591
  } catch (error) {
592
+ // Version skew — server has been redeployed. Full page reload
593
+ // so the browser fetches the new bundle. See TIM-446.
594
+ if (error instanceof VersionSkewError) {
595
+ setHardNavigating(true);
596
+ window.location.reload();
597
+ return new Promise(() => {}) as never;
598
+ }
362
599
  // Server-side redirect during RSC fetch → soft router navigation.
600
+ // The redirect navigate will push/replace its own URL.
363
601
  if (error instanceof RedirectError) {
364
- setPending(false);
602
+ // Drop the redirect if a newer navigation superseded this one
603
+ // while the redirect response was in flight — the newer
604
+ // navigation wins, same as the stale-transition guard.
605
+ if (currentNavAbort !== navAbort) return;
606
+ // The recursive navigate() supersedes this one via createNavAbort,
607
+ // which also resolves this navigation's Navigation API deferred.
365
608
  await navigate(error.redirectUrl, { replace: true });
366
609
  return;
367
610
  }
611
+ // Server 5xx error — hard-navigate so the server renders the
612
+ // error page as HTML. See design/10-error-handling.md
613
+ // §"Error Page Rendering for Client Navigation".
614
+ //
615
+ // Set hard-navigating flag BEFORE setting window.location.href:
616
+ // 1. Prevents Navigation API from intercepting → infinite loop
617
+ // 2. Causes NavigationRoot to throw unresolvedThenable → prevents
618
+ // React from rendering children during page teardown (avoids
619
+ // "Rendered more hooks" crashes). See TIM-626.
620
+ if (error instanceof ServerErrorResponse) {
621
+ setHardNavigating(true);
622
+ window.location.href = error.url;
623
+ return new Promise(() => {}) as never;
624
+ }
368
625
  // Abort errors are not application errors — swallow silently.
369
626
  if (isAbortError(error)) return;
370
627
  throw error;
371
628
  } finally {
372
- setPending(false);
629
+ // Staleness guard (TIM-1034): only clean up when this navigation
630
+ // still owns the abort controller. When a newer navigation
631
+ // superseded this one, createNavAbort() already replaced
632
+ // currentNavAbort — running setPending(false) /
633
+ // completeRouterNavigation() here would clear the NEWER
634
+ // navigation's pending state and resolve its Navigation API
635
+ // deferred. The superseding navigation resolved OUR deferred in
636
+ // createNavAbort().
637
+ if (currentNavAbort === navAbort) {
638
+ // Clear the abort controller so we don't abort a completed
639
+ // navigation when the next one starts. In dev mode, the RSC body
640
+ // stream stays open after data arrives (React's Flight client
641
+ // waits for debug rows). Aborting a "completed" navigation kills
642
+ // the open stream reader → "BodyStreamBuffer was aborted".
643
+ currentNavAbort = null;
644
+ setPending(false);
645
+ // Resolve the Navigation API deferred — clears the browser's
646
+ // native loading state (tab spinner) at the same time as the
647
+ // TopLoader.
648
+ deps.completeRouterNavigation?.();
649
+ }
373
650
  }
374
651
  }
375
652
 
376
653
  async function refresh(): Promise<void> {
377
654
  const currentUrl = deps.getCurrentUrl();
655
+ const navAbort = createNavAbort();
378
656
 
379
657
  setPending(true, currentUrl);
380
658
 
381
659
  try {
382
- const headElements = await renderViaTransition(currentUrl, async () => {
660
+ await renderViaTransition(currentUrl, async () => {
383
661
  // No state tree sent — server renders the complete RSC payload
384
- const result = await fetchRscPayload(currentUrl, deps);
662
+ const result = await fetchRscPayload(
663
+ currentUrl,
664
+ deps,
665
+ undefined,
666
+ undefined,
667
+ navAbort.signal
668
+ );
385
669
  // History push handled by renderViaTransition (stores merged payload)
386
670
  updateSegmentCache(result.segmentInfo);
387
- updateNavigationState(result.params, currentUrl);
388
- return result;
671
+ const navState = updateNavigationState(result.params, currentUrl);
672
+ return { ...result, navState };
389
673
  });
390
-
391
- applyHead(headElements);
674
+ } catch (error) {
675
+ if (isAbortError(error)) return;
676
+ throw error;
392
677
  } finally {
393
- setPending(false);
678
+ // Staleness guard (TIM-1034) — see navigate()'s finally block.
679
+ if (currentNavAbort === navAbort) {
680
+ currentNavAbort = null;
681
+ setPending(false);
682
+ deps.completeRouterNavigation?.();
683
+ }
394
684
  }
395
685
  }
396
686
 
397
- async function handlePopState(url: string, scrollY: number = 0): Promise<void> {
687
+ async function handlePopState(
688
+ url: string,
689
+ scrollY: number = 0,
690
+ externalSignal?: AbortSignal
691
+ ): Promise<void> {
398
692
  // Scroll position is read from history.state by the caller (browser-entry.ts)
399
693
  // and passed in. This is more reliable than tracking scroll per-URL in memory
400
694
  // because the browser maintains per-entry state even with duplicate URLs.
401
695
  const entry = historyStack.get(url);
402
696
 
403
697
  if (entry && entry.payload !== null) {
404
- // Replay cached payload — no server roundtrip
405
- updateNavigationState(entry.params, url);
406
- renderPayload(entry.payload);
407
- applyHead(entry.headElements);
408
- afterPaint(() => {
409
- deps.scrollTo(0, scrollY);
410
- window.dispatchEvent(new Event('timber:scroll-restored'));
411
- });
698
+ // Replay cached payload — no server roundtrip.
699
+ //
700
+ // A forward navigation may still be in flight (back pressed while a
701
+ // slow RSC fetch was pending). createNavAbort() supersedes it: aborts
702
+ // its fetch and invalidates its render transition so the stale
703
+ // forward payload can't commit over this replay (TIM-1022). The
704
+ // replay itself is synchronous — nothing to abort later — so the
705
+ // controller is cleared immediately, and any pending state left by
706
+ // the superseded navigation is reset.
707
+ createNavAbort(externalSignal);
708
+ currentNavAbort = null;
709
+ setPending(false);
710
+ const navState = updateNavigationState(entry.params, url);
711
+ renderPayload(entry.payload, navState);
712
+ restoreScrollAfterPaint(scrollY);
412
713
  } else {
413
714
  // No cached payload — fetch from server.
414
715
  // This happens when navigating back to the initial SSR'd page
415
716
  // (its payload is null since it was rendered via SSR, not RSC fetch)
416
717
  // or when the entry doesn't exist at all.
718
+ const navAbort = createNavAbort(externalSignal);
417
719
  setPending(true, url);
418
720
  try {
419
- const headElements = await renderViaTransition(url, async () => {
420
- const stateTree = segmentCache.serializeStateTree(
421
- segmentElementCache.getMergeablePaths()
422
- );
423
- const result = await fetchRscPayload(url, deps, stateTree);
721
+ await renderViaTransition(url, async () => {
722
+ const stateTree = segmentCache.serializeStateTree();
723
+ const result = await fetchRscPayload(url, deps, stateTree, undefined, navAbort.signal);
424
724
  updateSegmentCache(result.segmentInfo);
425
- updateNavigationState(result.params, url);
725
+ const navState = updateNavigationState(result.params, url);
426
726
  // History push handled by renderViaTransition (stores merged payload)
427
- return result;
727
+ return { ...result, navState };
428
728
  });
429
729
 
430
- applyHead(headElements);
431
- afterPaint(() => {
432
- deps.scrollTo(0, scrollY);
433
- window.dispatchEvent(new Event('timber:scroll-restored'));
434
- });
730
+ restoreScrollAfterPaint(scrollY);
731
+ } catch (error) {
732
+ if (isAbortError(error)) return;
733
+ throw error;
435
734
  } finally {
436
- setPending(false);
735
+ // Staleness guard (TIM-1034) — see navigate()'s finally block.
736
+ if (currentNavAbort === navAbort) {
737
+ currentNavAbort = null;
738
+ setPending(false);
739
+ }
437
740
  }
438
741
  }
439
742
  }
@@ -443,15 +746,21 @@ export function createRouter(deps: RouterDeps): RouterInstance {
443
746
  * Called on hover of <Link prefetch> elements.
444
747
  */
445
748
  function prefetch(url: string): void {
749
+ // Strip fragment — it's client-only and would swallow the _rsc cache-bust
750
+ // param into the hash. The hash-less key also matches navigate()'s fetchUrl.
751
+ const hashIndex = url.indexOf('#');
752
+ const fetchUrl = hashIndex === -1 ? url : url.slice(0, hashIndex);
753
+
446
754
  // Don't prefetch if already cached
447
- if (prefetchCache.get(url) !== undefined) return;
448
- if (historyStack.has(url)) return;
755
+ if (prefetchCache.get(fetchUrl) !== undefined) return;
756
+ if (historyStack.has(fetchUrl)) return;
449
757
 
450
758
  // Fire-and-forget fetch
451
- const stateTree = segmentCache.serializeStateTree(segmentElementCache.getMergeablePaths());
452
- void fetchRscPayload(url, deps, stateTree).then(
759
+ const stateTree = segmentCache.serializeStateTree();
760
+ void fetchRscPayload(fetchUrl, deps, stateTree).then(
453
761
  (result) => {
454
- prefetchCache.set(url, result);
762
+ result.decodePromise?.catch(() => {});
763
+ prefetchCache.set(fetchUrl, result);
455
764
  },
456
765
  () => {
457
766
  // Prefetch failure is non-fatal — navigation will fetch fresh
@@ -463,29 +772,36 @@ export function createRouter(deps: RouterDeps): RouterInstance {
463
772
  navigate,
464
773
  refresh,
465
774
  handlePopState,
466
- isPending: () => pending,
467
- getPendingUrl: () => pendingUrl,
775
+ isPending: () => routerPhase.phase === 'navigating',
776
+ getPendingUrl: () => (routerPhase.phase === 'navigating' ? routerPhase.targetUrl : null),
468
777
  onPendingChange(listener) {
469
778
  pendingListeners.add(listener);
470
779
  return () => pendingListeners.delete(listener);
471
780
  },
472
781
  prefetch,
473
- applyRevalidation(element: unknown, headElements: HeadElement[] | null): void {
782
+ applyRevalidation(element: unknown): void {
474
783
  // Render the piggybacked element tree from a server action response.
475
- // Updates the current history entry with the fresh payload and applies
476
- // head elements — same as refresh() but without a server fetch.
477
- // Cache segment elements for future partial merges.
784
+ // Updates the current history entry with the fresh payload —
785
+ // same as refresh() but without a server fetch.
786
+ // Revalidation payloads are already resolved (no Flight thenable),
787
+ // so no segment merging is needed — just cache and render.
478
788
  const currentUrl = deps.getCurrentUrl();
479
- const merged = mergeAndCachePayload(element, null);
789
+
790
+ // Revalidation doesn't change params/pathname — preserve current state.
791
+ // DO NOT call updateNavigationState(null, ...) here: that normalizes
792
+ // params to {}, clearing dynamic route params on every action response.
793
+ const navState = getNavigationState();
794
+
795
+ // Keep params on the overwritten entry — dropping them would make
796
+ // away-and-back navigation replay this page with params normalized
797
+ // to {} (TIM-1037).
480
798
  historyStack.push(currentUrl, {
481
- payload: merged,
482
- headElements,
799
+ payload: element,
800
+ params: navState.params,
483
801
  });
484
- renderPayload(merged);
485
- applyHead(headElements);
802
+ renderPayload(element, navState);
486
803
  },
487
804
  initSegmentCache: (segments: SegmentInfo[]) => updateSegmentCache(segments),
488
- cacheElementTree: (element: unknown) => cacheSegmentElements(element, segmentElementCache),
489
805
  segmentCache,
490
806
  prefetchCache,
491
807
  historyStack,