@timber-js/app 0.2.0-alpha.18 → 0.2.0-alpha.181

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