@timber-js/app 0.2.0-alpha.4 → 0.2.0-alpha.40

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 (336) hide show
  1. package/LICENSE +8 -0
  2. package/dist/_chunks/{als-registry-B7DbZ2hS.js → als-registry-Ba7URUIn.js} +1 -1
  3. package/dist/_chunks/als-registry-Ba7URUIn.js.map +1 -0
  4. package/dist/_chunks/chunk-DYhsFzuS.js +33 -0
  5. package/dist/_chunks/debug-ECi_61pb.js +108 -0
  6. package/dist/_chunks/debug-ECi_61pb.js.map +1 -0
  7. package/dist/_chunks/define-cookie-BmKbSyp0.js +93 -0
  8. package/dist/_chunks/define-cookie-BmKbSyp0.js.map +1 -0
  9. package/dist/_chunks/error-boundary-BAN3751q.js +211 -0
  10. package/dist/_chunks/error-boundary-BAN3751q.js.map +1 -0
  11. package/dist/_chunks/{format-CwdaB0_2.js → format-cX7wzEp2.js} +2 -2
  12. package/dist/_chunks/{format-CwdaB0_2.js.map → format-cX7wzEp2.js.map} +1 -1
  13. package/dist/_chunks/{interception-BOoWmLUA.js → interception-D2djYaIm.js} +112 -77
  14. package/dist/_chunks/interception-D2djYaIm.js.map +1 -0
  15. package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-BU684ls2.js} +1 -1
  16. package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js.map → metadata-routes-BU684ls2.js.map} +1 -1
  17. package/dist/_chunks/{request-context-CZJi4CuK.js → request-context-BxYIJM24.js} +93 -69
  18. package/dist/_chunks/request-context-BxYIJM24.js.map +1 -0
  19. package/dist/_chunks/segment-context-C6byCyZU.js +69 -0
  20. package/dist/_chunks/segment-context-C6byCyZU.js.map +1 -0
  21. package/dist/_chunks/stale-reload-C0ValzG7.js +47 -0
  22. package/dist/_chunks/stale-reload-C0ValzG7.js.map +1 -0
  23. package/dist/_chunks/{tracing-Cwn7697K.js → tracing-CuXiCP5p.js} +17 -3
  24. package/dist/_chunks/{tracing-Cwn7697K.js.map → tracing-CuXiCP5p.js.map} +1 -1
  25. package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-BvW0TKDn.js} +1 -1
  26. package/dist/_chunks/{use-query-states-D5KaffOK.js.map → use-query-states-BvW0TKDn.js.map} +1 -1
  27. package/dist/_chunks/wrappers-C6J0nNji.js +331 -0
  28. package/dist/_chunks/wrappers-C6J0nNji.js.map +1 -0
  29. package/dist/adapters/compress-module.d.ts.map +1 -1
  30. package/dist/adapters/nitro.d.ts +17 -1
  31. package/dist/adapters/nitro.d.ts.map +1 -1
  32. package/dist/adapters/nitro.js +56 -13
  33. package/dist/adapters/nitro.js.map +1 -1
  34. package/dist/cache/fast-hash.d.ts +22 -0
  35. package/dist/cache/fast-hash.d.ts.map +1 -0
  36. package/dist/cache/index.d.ts +5 -2
  37. package/dist/cache/index.d.ts.map +1 -1
  38. package/dist/cache/index.js +88 -18
  39. package/dist/cache/index.js.map +1 -1
  40. package/dist/cache/register-cached-function.d.ts.map +1 -1
  41. package/dist/cache/singleflight.d.ts +18 -1
  42. package/dist/cache/singleflight.d.ts.map +1 -1
  43. package/dist/cache/timber-cache.d.ts.map +1 -1
  44. package/dist/client/error-boundary.d.ts +10 -1
  45. package/dist/client/error-boundary.d.ts.map +1 -1
  46. package/dist/client/error-boundary.js +1 -125
  47. package/dist/client/index.d.ts +3 -2
  48. package/dist/client/index.d.ts.map +1 -1
  49. package/dist/client/index.js +213 -93
  50. package/dist/client/index.js.map +1 -1
  51. package/dist/client/link.d.ts +22 -8
  52. package/dist/client/link.d.ts.map +1 -1
  53. package/dist/client/navigation-context.d.ts +2 -2
  54. package/dist/client/router.d.ts +25 -3
  55. package/dist/client/router.d.ts.map +1 -1
  56. package/dist/client/rsc-fetch.d.ts +23 -2
  57. package/dist/client/rsc-fetch.d.ts.map +1 -1
  58. package/dist/client/segment-cache.d.ts +1 -1
  59. package/dist/client/segment-cache.d.ts.map +1 -1
  60. package/dist/client/segment-context.d.ts +1 -1
  61. package/dist/client/segment-context.d.ts.map +1 -1
  62. package/dist/client/segment-merger.d.ts.map +1 -1
  63. package/dist/client/stale-reload.d.ts +15 -0
  64. package/dist/client/stale-reload.d.ts.map +1 -1
  65. package/dist/client/top-loader.d.ts +1 -1
  66. package/dist/client/top-loader.d.ts.map +1 -1
  67. package/dist/client/transition-root.d.ts +1 -1
  68. package/dist/client/transition-root.d.ts.map +1 -1
  69. package/dist/client/use-params.d.ts +2 -2
  70. package/dist/client/use-params.d.ts.map +1 -1
  71. package/dist/client/use-query-states.d.ts +1 -1
  72. package/dist/codec.d.ts +21 -0
  73. package/dist/codec.d.ts.map +1 -0
  74. package/dist/cookies/define-cookie.d.ts +33 -12
  75. package/dist/cookies/define-cookie.d.ts.map +1 -1
  76. package/dist/cookies/index.js +1 -83
  77. package/dist/fonts/css.d.ts +1 -0
  78. package/dist/fonts/css.d.ts.map +1 -1
  79. package/dist/fonts/local.d.ts +4 -2
  80. package/dist/fonts/local.d.ts.map +1 -1
  81. package/dist/index.d.ts +112 -35
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +635 -233
  84. package/dist/index.js.map +1 -1
  85. package/dist/params/define.d.ts +76 -0
  86. package/dist/params/define.d.ts.map +1 -0
  87. package/dist/params/index.d.ts +8 -0
  88. package/dist/params/index.d.ts.map +1 -0
  89. package/dist/params/index.js +104 -0
  90. package/dist/params/index.js.map +1 -0
  91. package/dist/plugins/adapter-build.d.ts.map +1 -1
  92. package/dist/plugins/build-manifest.d.ts.map +1 -1
  93. package/dist/plugins/client-chunks.d.ts +32 -0
  94. package/dist/plugins/client-chunks.d.ts.map +1 -0
  95. package/dist/plugins/dev-error-overlay.d.ts +26 -1
  96. package/dist/plugins/dev-error-overlay.d.ts.map +1 -1
  97. package/dist/plugins/entries.d.ts +7 -0
  98. package/dist/plugins/entries.d.ts.map +1 -1
  99. package/dist/plugins/fonts.d.ts +9 -1
  100. package/dist/plugins/fonts.d.ts.map +1 -1
  101. package/dist/plugins/mdx.d.ts +6 -0
  102. package/dist/plugins/mdx.d.ts.map +1 -1
  103. package/dist/plugins/routing.d.ts.map +1 -1
  104. package/dist/plugins/server-bundle.d.ts.map +1 -1
  105. package/dist/plugins/static-build.d.ts.map +1 -1
  106. package/dist/routing/codegen.d.ts +2 -2
  107. package/dist/routing/codegen.d.ts.map +1 -1
  108. package/dist/routing/index.js +1 -1
  109. package/dist/routing/scanner.d.ts.map +1 -1
  110. package/dist/routing/status-file-lint.d.ts +2 -1
  111. package/dist/routing/status-file-lint.d.ts.map +1 -1
  112. package/dist/routing/types.d.ts +6 -4
  113. package/dist/routing/types.d.ts.map +1 -1
  114. package/dist/rsc-runtime/rsc.d.ts +1 -1
  115. package/dist/rsc-runtime/rsc.d.ts.map +1 -1
  116. package/dist/rsc-runtime/ssr.d.ts +12 -0
  117. package/dist/rsc-runtime/ssr.d.ts.map +1 -1
  118. package/dist/search-params/codecs.d.ts +1 -1
  119. package/dist/search-params/define.d.ts +153 -0
  120. package/dist/search-params/define.d.ts.map +1 -0
  121. package/dist/search-params/index.d.ts +4 -5
  122. package/dist/search-params/index.d.ts.map +1 -1
  123. package/dist/search-params/index.js +3 -474
  124. package/dist/search-params/registry.d.ts +1 -1
  125. package/dist/search-params/wrappers.d.ts +53 -0
  126. package/dist/search-params/wrappers.d.ts.map +1 -0
  127. package/dist/server/access-gate.d.ts +4 -0
  128. package/dist/server/access-gate.d.ts.map +1 -1
  129. package/dist/server/action-client.d.ts.map +1 -1
  130. package/dist/server/action-encryption.d.ts +76 -0
  131. package/dist/server/action-encryption.d.ts.map +1 -0
  132. package/dist/server/action-handler.d.ts.map +1 -1
  133. package/dist/server/als-registry.d.ts +18 -4
  134. package/dist/server/als-registry.d.ts.map +1 -1
  135. package/dist/server/build-manifest.d.ts +2 -2
  136. package/dist/server/debug.d.ts +46 -15
  137. package/dist/server/debug.d.ts.map +1 -1
  138. package/dist/server/default-logger.d.ts +22 -0
  139. package/dist/server/default-logger.d.ts.map +1 -0
  140. package/dist/server/deny-renderer.d.ts.map +1 -1
  141. package/dist/server/early-hints.d.ts +13 -5
  142. package/dist/server/early-hints.d.ts.map +1 -1
  143. package/dist/server/error-boundary-wrapper.d.ts +4 -0
  144. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  145. package/dist/server/flight-injection-state.d.ts +78 -0
  146. package/dist/server/flight-injection-state.d.ts.map +1 -0
  147. package/dist/server/flight-scripts.d.ts +39 -0
  148. package/dist/server/flight-scripts.d.ts.map +1 -0
  149. package/dist/server/flush.d.ts.map +1 -1
  150. package/dist/server/form-data.d.ts +29 -0
  151. package/dist/server/form-data.d.ts.map +1 -1
  152. package/dist/server/html-injectors.d.ts +5 -11
  153. package/dist/server/html-injectors.d.ts.map +1 -1
  154. package/dist/server/index.d.ts +4 -2
  155. package/dist/server/index.d.ts.map +1 -1
  156. package/dist/server/index.js +1975 -1649
  157. package/dist/server/index.js.map +1 -1
  158. package/dist/server/logger.d.ts +24 -7
  159. package/dist/server/logger.d.ts.map +1 -1
  160. package/dist/server/node-stream-transforms.d.ts +77 -0
  161. package/dist/server/node-stream-transforms.d.ts.map +1 -0
  162. package/dist/server/pipeline.d.ts +7 -4
  163. package/dist/server/pipeline.d.ts.map +1 -1
  164. package/dist/server/primitives.d.ts +30 -3
  165. package/dist/server/primitives.d.ts.map +1 -1
  166. package/dist/server/render-timeout.d.ts +51 -0
  167. package/dist/server/render-timeout.d.ts.map +1 -0
  168. package/dist/server/request-context.d.ts +65 -38
  169. package/dist/server/request-context.d.ts.map +1 -1
  170. package/dist/server/route-element-builder.d.ts +7 -0
  171. package/dist/server/route-element-builder.d.ts.map +1 -1
  172. package/dist/server/route-handler.d.ts.map +1 -1
  173. package/dist/server/route-matcher.d.ts +2 -2
  174. package/dist/server/route-matcher.d.ts.map +1 -1
  175. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  176. package/dist/server/rsc-entry/helpers.d.ts +46 -3
  177. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  178. package/dist/server/rsc-entry/index.d.ts +6 -1
  179. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  180. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  181. package/dist/server/rsc-entry/rsc-stream.d.ts +9 -0
  182. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  183. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  184. package/dist/server/slot-resolver.d.ts +1 -1
  185. package/dist/server/slot-resolver.d.ts.map +1 -1
  186. package/dist/server/ssr-entry.d.ts +22 -0
  187. package/dist/server/ssr-entry.d.ts.map +1 -1
  188. package/dist/server/ssr-render.d.ts +39 -21
  189. package/dist/server/ssr-render.d.ts.map +1 -1
  190. package/dist/server/tracing.d.ts +10 -0
  191. package/dist/server/tracing.d.ts.map +1 -1
  192. package/dist/server/tree-builder.d.ts +19 -12
  193. package/dist/server/tree-builder.d.ts.map +1 -1
  194. package/dist/server/types.d.ts +1 -3
  195. package/dist/server/types.d.ts.map +1 -1
  196. package/dist/server/version-skew.d.ts +61 -0
  197. package/dist/server/version-skew.d.ts.map +1 -0
  198. package/dist/server/waituntil-bridge.d.ts.map +1 -1
  199. package/dist/shared/merge-search-params.d.ts +22 -0
  200. package/dist/shared/merge-search-params.d.ts.map +1 -0
  201. package/dist/shims/navigation-client.d.ts +1 -1
  202. package/dist/shims/navigation-client.d.ts.map +1 -1
  203. package/dist/shims/navigation.d.ts +1 -1
  204. package/dist/shims/navigation.d.ts.map +1 -1
  205. package/dist/utils/state-machine.d.ts +80 -0
  206. package/dist/utils/state-machine.d.ts.map +1 -0
  207. package/package.json +17 -14
  208. package/src/adapters/compress-module.ts +24 -4
  209. package/src/adapters/nitro.ts +58 -9
  210. package/src/cache/fast-hash.ts +34 -0
  211. package/src/cache/index.ts +5 -2
  212. package/src/cache/register-cached-function.ts +7 -3
  213. package/src/cache/singleflight.ts +62 -4
  214. package/src/cache/timber-cache.ts +34 -26
  215. package/src/cli.ts +0 -0
  216. package/src/client/browser-entry.ts +94 -90
  217. package/src/client/error-boundary.tsx +18 -1
  218. package/src/client/index.ts +10 -1
  219. package/src/client/link.tsx +78 -19
  220. package/src/client/navigation-context.ts +2 -2
  221. package/src/client/router.ts +105 -60
  222. package/src/client/rsc-fetch.ts +63 -2
  223. package/src/client/segment-cache.ts +1 -1
  224. package/src/client/segment-context.ts +6 -1
  225. package/src/client/segment-merger.ts +2 -8
  226. package/src/client/stale-reload.ts +32 -6
  227. package/src/client/top-loader.tsx +10 -9
  228. package/src/client/transition-root.tsx +7 -1
  229. package/src/client/use-params.ts +3 -3
  230. package/src/client/use-query-states.ts +1 -1
  231. package/src/codec.ts +21 -0
  232. package/src/cookies/define-cookie.ts +69 -18
  233. package/src/fonts/css.ts +2 -1
  234. package/src/fonts/local.ts +7 -3
  235. package/src/index.ts +280 -85
  236. package/src/params/define.ts +260 -0
  237. package/src/params/index.ts +28 -0
  238. package/src/plugins/adapter-build.ts +6 -0
  239. package/src/plugins/build-manifest.ts +11 -0
  240. package/src/plugins/client-chunks.ts +65 -0
  241. package/src/plugins/dev-error-overlay.ts +70 -1
  242. package/src/plugins/dev-server.ts +38 -4
  243. package/src/plugins/entries.ts +12 -11
  244. package/src/plugins/fonts.ts +171 -19
  245. package/src/plugins/mdx.ts +9 -5
  246. package/src/plugins/routing.ts +40 -14
  247. package/src/plugins/server-bundle.ts +32 -1
  248. package/src/plugins/shims.ts +1 -1
  249. package/src/plugins/static-build.ts +8 -4
  250. package/src/routing/codegen.ts +109 -88
  251. package/src/routing/scanner.ts +55 -6
  252. package/src/routing/status-file-lint.ts +2 -1
  253. package/src/routing/types.ts +7 -4
  254. package/src/rsc-runtime/rsc.ts +2 -0
  255. package/src/rsc-runtime/ssr.ts +50 -0
  256. package/src/rsc-runtime/vendor-types.d.ts +7 -0
  257. package/src/search-params/codecs.ts +1 -1
  258. package/src/search-params/define.ts +504 -0
  259. package/src/search-params/index.ts +12 -18
  260. package/src/search-params/registry.ts +1 -1
  261. package/src/search-params/wrappers.ts +85 -0
  262. package/src/server/access-gate.tsx +40 -9
  263. package/src/server/action-client.ts +14 -5
  264. package/src/server/action-encryption.ts +144 -0
  265. package/src/server/action-handler.ts +19 -2
  266. package/src/server/als-registry.ts +18 -4
  267. package/src/server/build-manifest.ts +4 -4
  268. package/src/server/compress.ts +25 -7
  269. package/src/server/debug.ts +55 -17
  270. package/src/server/default-logger.ts +98 -0
  271. package/src/server/deny-renderer.ts +2 -1
  272. package/src/server/early-hints.ts +36 -15
  273. package/src/server/error-boundary-wrapper.ts +57 -14
  274. package/src/server/flight-injection-state.ts +152 -0
  275. package/src/server/flight-scripts.ts +59 -0
  276. package/src/server/flush.ts +2 -1
  277. package/src/server/form-data.ts +76 -0
  278. package/src/server/html-injectors.ts +103 -66
  279. package/src/server/index.ts +9 -4
  280. package/src/server/logger.ts +38 -35
  281. package/src/server/node-stream-transforms.ts +381 -0
  282. package/src/server/pipeline.ts +131 -39
  283. package/src/server/primitives.ts +47 -5
  284. package/src/server/render-timeout.ts +108 -0
  285. package/src/server/request-context.ts +112 -119
  286. package/src/server/route-element-builder.ts +106 -114
  287. package/src/server/route-handler.ts +2 -1
  288. package/src/server/route-matcher.ts +2 -2
  289. package/src/server/rsc-entry/error-renderer.ts +5 -3
  290. package/src/server/rsc-entry/helpers.ts +122 -3
  291. package/src/server/rsc-entry/index.ts +125 -49
  292. package/src/server/rsc-entry/rsc-payload.ts +52 -12
  293. package/src/server/rsc-entry/rsc-stream.ts +33 -8
  294. package/src/server/rsc-entry/ssr-renderer.ts +40 -13
  295. package/src/server/slot-resolver.ts +199 -210
  296. package/src/server/ssr-entry.ts +169 -17
  297. package/src/server/ssr-render.ts +266 -67
  298. package/src/server/tracing.ts +23 -0
  299. package/src/server/tree-builder.ts +91 -57
  300. package/src/server/types.ts +1 -3
  301. package/src/server/version-skew.ts +104 -0
  302. package/src/server/waituntil-bridge.ts +4 -1
  303. package/src/shared/merge-search-params.ts +48 -0
  304. package/src/shims/navigation-client.ts +1 -1
  305. package/src/shims/navigation.ts +1 -1
  306. package/src/utils/state-machine.ts +111 -0
  307. package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
  308. package/dist/_chunks/debug-B4WUeqJ-.js +0 -75
  309. package/dist/_chunks/debug-B4WUeqJ-.js.map +0 -1
  310. package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
  311. package/dist/_chunks/request-context-CZJi4CuK.js.map +0 -1
  312. package/dist/_chunks/ssr-data-MjmprTmO.js +0 -88
  313. package/dist/_chunks/ssr-data-MjmprTmO.js.map +0 -1
  314. package/dist/_chunks/use-cookie-DX-l1_5E.js +0 -91
  315. package/dist/_chunks/use-cookie-DX-l1_5E.js.map +0 -1
  316. package/dist/client/error-boundary.js.map +0 -1
  317. package/dist/cookies/index.js.map +0 -1
  318. package/dist/plugins/dynamic-transform.d.ts +0 -72
  319. package/dist/plugins/dynamic-transform.d.ts.map +0 -1
  320. package/dist/search-params/analyze.d.ts +0 -54
  321. package/dist/search-params/analyze.d.ts.map +0 -1
  322. package/dist/search-params/builtin-codecs.d.ts +0 -105
  323. package/dist/search-params/builtin-codecs.d.ts.map +0 -1
  324. package/dist/search-params/create.d.ts +0 -106
  325. package/dist/search-params/create.d.ts.map +0 -1
  326. package/dist/search-params/index.js.map +0 -1
  327. package/dist/server/prerender.d.ts +0 -77
  328. package/dist/server/prerender.d.ts.map +0 -1
  329. package/dist/server/response-cache.d.ts +0 -53
  330. package/dist/server/response-cache.d.ts.map +0 -1
  331. package/src/plugins/dynamic-transform.ts +0 -161
  332. package/src/search-params/analyze.ts +0 -192
  333. package/src/search-params/builtin-codecs.ts +0 -228
  334. package/src/search-params/create.ts +0 -321
  335. package/src/server/prerender.ts +0 -139
  336. package/src/server/response-cache.ts +0 -277
@@ -47,12 +47,24 @@ import {
47
47
  NavigationProvider,
48
48
  getNavigationState,
49
49
  setNavigationState,
50
+ type NavigationState,
50
51
  } from './navigation-context.js';
51
52
  import { setupServerLogReplay, setupClientErrorForwarding } from './browser-dev.js';
52
53
  // browser-links.ts removed — Link components own their click/hover handlers directly.
53
54
  // See LOCAL-340.
54
55
  import { TransitionRoot, transitionRender, navigateTransition } from './transition-root.js';
55
- import { isStaleClientReference, triggerStaleReload, clearStaleReloadFlag } from './stale-reload.js';
56
+ import {
57
+ isStaleClientReference,
58
+ isChunkLoadError,
59
+ triggerStaleReload,
60
+ clearStaleReloadFlag,
61
+ } from './stale-reload.js';
62
+ import {
63
+ setClientDeploymentId,
64
+ getClientDeploymentId,
65
+ DEPLOYMENT_ID_HEADER,
66
+ RELOAD_HEADER,
67
+ } from './rsc-fetch.js';
56
68
 
57
69
  // ─── Server Action Dispatch ──────────────────────────────────────
58
70
 
@@ -81,14 +93,28 @@ setServerCallback(async (id: string, args: unknown[]) => {
81
93
  let hasRedirect = false;
82
94
  let headElementsJson: string | null = null;
83
95
 
96
+ // Build action request headers. Include deployment ID for version
97
+ // skew detection (TIM-446) — the server rejects stale actions gracefully.
98
+ const actionHeaders: Record<string, string> = {
99
+ 'Accept': 'text/x-component',
100
+ 'x-rsc-action': id,
101
+ };
102
+ const actionDeploymentId = getClientDeploymentId();
103
+ if (actionDeploymentId) {
104
+ actionHeaders[DEPLOYMENT_ID_HEADER] = actionDeploymentId;
105
+ }
106
+
84
107
  const response = fetch(window.location.href, {
85
108
  method: 'POST',
86
- headers: {
87
- 'Accept': 'text/x-component',
88
- 'x-rsc-action': id,
89
- },
109
+ headers: actionHeaders,
90
110
  body,
91
111
  }).then((res) => {
112
+ // Version skew detection (TIM-446): if the server signals a reload,
113
+ // trigger a full page load to pick up the new deployment.
114
+ if (res.headers.get(RELOAD_HEADER) === '1') {
115
+ window.location.reload();
116
+ throw new Error('Version skew detected — reloading page');
117
+ }
92
118
  hasRevalidation = res.headers.get('X-Timber-Revalidation') === '1';
93
119
  hasRedirect = res.headers.get('X-Timber-Redirect') != null;
94
120
  headElementsJson = res.headers.get('X-Timber-Head');
@@ -155,7 +181,17 @@ setServerCallback(async (id: string, args: unknown[]) => {
155
181
  * Hydrates the server-rendered HTML with React, then initializes
156
182
  * client-side navigation for SPA transitions.
157
183
  */
158
- /** Read scroll position from window or scroll containers. */
184
+ /**
185
+ * Read the current scroll position.
186
+ *
187
+ * Checks window scroll first, then explicit `data-timber-scroll-restoration`
188
+ * containers. With segment tree merging, shared layouts are reconciled in
189
+ * place via `cloneElement` — React preserves their DOM and scroll state
190
+ * naturally. We don't need to auto-detect overflow containers; only
191
+ * explicitly marked containers are tracked.
192
+ *
193
+ * See design/19-client-navigation.md §"Overflow Scroll Containers".
194
+ */
159
195
  function getScrollY(): number {
160
196
  if (window.scrollY || document.documentElement.scrollTop || document.body.scrollTop) {
161
197
  return window.scrollY || document.documentElement.scrollTop || document.body.scrollTop;
@@ -163,74 +199,24 @@ function getScrollY(): number {
163
199
  for (const el of document.querySelectorAll('[data-timber-scroll-restoration]')) {
164
200
  if ((el as HTMLElement).scrollTop > 0) return (el as HTMLElement).scrollTop;
165
201
  }
166
- // Auto-detect: if window isn't scrolled, check for overflow containers.
167
- // Common pattern: layouts use a scrollable div (overflow-y: auto/scroll)
168
- // inside a fixed-height parent (h-screen). In this case window.scrollY is
169
- // always 0 and the real scroll position lives on the overflow container.
170
- const container = findOverflowContainer();
171
- if (container && container.scrollTop > 0) return container.scrollTop;
172
202
  return 0;
173
203
  }
174
204
 
175
- /**
176
- * Find the primary overflow scroll container in the document.
177
- *
178
- * Walks direct children of body and their immediate children looking for
179
- * an element with overflow-y: auto|scroll that is actually scrollable
180
- * (scrollHeight > clientHeight). Returns the first match, or null.
181
- *
182
- * This heuristic covers the common layout patterns:
183
- * <body> → <root-layout> → <div class="overflow-y-auto">
184
- * <body> → <root-layout> → <main> → <nested-layout overflow-y-auto>
185
- *
186
- * We limit depth to 3 to avoid expensive full-tree traversals while still
187
- * reaching nested layout scroll containers (e.g., parallel route layouts
188
- * inside a root layout's <main> element).
189
- *
190
- * DIVERGENCE FROM NEXT.JS: Next.js's ScrollAndFocusHandler scrolls only
191
- * document.documentElement.scrollTop — it does NOT handle overflow containers.
192
- * Layouts using h-screen + overflow-y-auto have the same scroll bug in Next.js.
193
- * This heuristic is a deliberate improvement. The tradeoff is fragility: depth-3
194
- * traversal may miss deeply nested containers or match the wrong element.
195
- * See design/19-client-navigation.md §"Overflow Scroll Containers".
196
- */
197
- function findOverflowContainer(): HTMLElement | null {
198
- const candidates: HTMLElement[] = [];
199
- // Check body's descendants up to depth 3. Depth 3 covers the common case:
200
- // <body> → <root-layout-div> → <main> → <overflow-container>
201
- // React context providers (SegmentProvider, NavigationProvider) don't add
202
- // DOM elements, so depth 3 from body reaches nested layout scroll containers.
203
- for (const child of document.body.children) {
204
- candidates.push(child as HTMLElement);
205
- for (const grandchild of child.children) {
206
- candidates.push(grandchild as HTMLElement);
207
- for (const greatGrandchild of grandchild.children) {
208
- candidates.push(greatGrandchild as HTMLElement);
209
- }
210
- }
211
- }
212
- for (const el of candidates) {
213
- if (!(el instanceof HTMLElement)) continue;
214
- const style = getComputedStyle(el);
215
- const overflowY = style.overflowY;
216
- if (
217
- (overflowY === 'auto' || overflowY === 'scroll') &&
218
- el.scrollHeight > el.clientHeight
219
- ) {
220
- return el;
221
- }
222
- }
223
- return null;
224
- }
225
-
226
205
  function bootstrap(runtimeConfig: typeof config): void {
227
206
  const _config = runtimeConfig;
228
207
 
229
- // Take manual control of scroll restoration. React's render() on the
230
- // document root resets scroll during DOM reconciliation, so the browser's
231
- // native scroll restoration (scrollRestoration = 'auto') doesn't work
232
- // the browser restores scroll, then React's commit resets it to 0.
233
- // We save/restore scroll positions explicitly in the history stack.
208
+ // Initialize deployment ID for version skew detection (TIM-446).
209
+ // In dev mode this is null skew checks are skipped.
210
+ const deploymentId = (_config as Record<string, unknown>).deploymentId as string | null;
211
+ if (deploymentId) {
212
+ setClientDeploymentId(deploymentId);
213
+ }
214
+
215
+ // Take manual control of scroll restoration. Even though segment tree
216
+ // merging preserves shared layout DOM via cloneElement (so React doesn't
217
+ // reset scroll on those elements), the root-level reactRoot.render() with
218
+ // a new element tree can still cause scroll resets on the document during
219
+ // reconciliation. Manual control ensures consistent behavior.
234
220
  window.history.scrollRestoration = 'manual';
235
221
 
236
222
  // Hydrate the React tree from the RSC payload.
@@ -246,6 +232,12 @@ function bootstrap(runtimeConfig: typeof config): void {
246
232
  // For subsequent navigations, it's fetched from the server.
247
233
  type FlightSegment = [isBootstrap: 0] | [isData: 1, data: string];
248
234
 
235
+ // __timber_f is initialized in <head> via flightInitScript() (see
236
+ // flight-scripts.ts). It should always exist by the time this module
237
+ // runs. Defensive fallback kept for safety.
238
+ if (!(self as unknown as Record<string, unknown>).__timber_f) {
239
+ (self as unknown as Record<string, FlightSegment[]>).__timber_f = [];
240
+ }
249
241
  const timberChunks = (self as unknown as Record<string, FlightSegment[]>).__timber_f;
250
242
 
251
243
  let _reactRoot: Root | null = null;
@@ -347,7 +339,7 @@ function bootstrap(runtimeConfig: typeof config): void {
347
339
 
348
340
  // ── Initialize navigation state BEFORE hydration ───────────────────
349
341
  // Read server-embedded params and set navigation state so that
350
- // useParams() and usePathname() return correct values during hydration.
342
+ // useSegmentParams() and usePathname() return correct values during hydration.
351
343
  // This must happen before hydrateRoot so the NavigationProvider
352
344
  // wrapping the element has the right values on the initial render.
353
345
  const earlyParams = (self as unknown as Record<string, unknown>).__timber_params;
@@ -381,7 +373,10 @@ function bootstrap(runtimeConfig: typeof config): void {
381
373
  element as React.ReactNode
382
374
  );
383
375
  const wrapped = createElement(TimberNuqsAdapter, null, withNav);
384
- const rootElement = createElement(TransitionRoot, { initial: wrapped, topLoaderConfig: _config.topLoader });
376
+ const rootElement = createElement(TransitionRoot, {
377
+ initial: wrapped,
378
+ topLoaderConfig: _config.topLoader,
379
+ });
385
380
  _reactRoot = hydrateRoot(document, rootElement, {
386
381
  // Suppress recoverable hydration errors from deny/error signals
387
382
  // inside Suspense boundaries. The server already handled these
@@ -420,23 +415,20 @@ function bootstrap(runtimeConfig: typeof config): void {
420
415
  pushState: (data, unused, url) => window.history.pushState(data, unused, url),
421
416
  replaceState: (data, unused, url) => window.history.replaceState(data, unused, url),
422
417
  scrollTo: (x, y) => {
418
+ // Scroll the document viewport.
423
419
  window.scrollTo(x, y);
424
420
  document.documentElement.scrollTop = y;
425
421
  document.body.scrollTop = y;
426
- // Also scroll any element explicitly marked as a scroll container.
422
+ // Scroll any element explicitly marked as a scroll container.
423
+ // With segment tree merging, shared layouts (sidebars, nav bars)
424
+ // are reconciled in place via cloneElement — React preserves their
425
+ // DOM and scroll state naturally. We no longer auto-detect overflow
426
+ // containers, which previously found the wrong element (e.g.,
427
+ // scrolling a sidebar instead of the main content area).
428
+ // Use `data-timber-scroll-restoration` to opt in specific containers.
427
429
  for (const el of document.querySelectorAll('[data-timber-scroll-restoration]')) {
428
430
  (el as HTMLElement).scrollTop = y;
429
431
  }
430
- // Auto-detect overflow containers for layouts that scroll inside
431
- // a fixed-height wrapper (e.g., h-screen + overflow-y-auto).
432
- // In these layouts, window.scrollY is always 0 and the real scroll
433
- // lives on the overflow container. Without this, forward navigation
434
- // between pages that share a layout with parallel route slots won't
435
- // scroll to top — the router's window.scrollTo(0,0) is a no-op.
436
- const container = findOverflowContainer();
437
- if (container) {
438
- container.scrollTop = y;
439
- }
440
432
  },
441
433
  getCurrentUrl: () => window.location.pathname + window.location.search,
442
434
  getScrollY,
@@ -471,8 +463,10 @@ function bootstrap(runtimeConfig: typeof config): void {
471
463
  // For navigation renders (navigate, refresh, popstate-with-fetch),
472
464
  // navigateTransition is used instead — it wraps the entire navigation
473
465
  // in a React transition with useOptimistic for the pending URL.
474
- renderRoot: (element: unknown) => {
475
- const navState = getNavigationState();
466
+ //
467
+ // navState is passed explicitly by the router — no temporal coupling
468
+ // with getNavigationState().
469
+ renderRoot: (element: unknown, navState: NavigationState) => {
476
470
  const withNav = createElement(
477
471
  NavigationProvider,
478
472
  { value: navState },
@@ -488,13 +482,11 @@ function bootstrap(runtimeConfig: typeof config): void {
488
482
  // commits (atomic with the new tree + params).
489
483
  //
490
484
  // The perform callback receives a wrapPayload function that wraps the
491
- // decoded RSC payload with NavigationProvider + NuqsAdapter — this must
492
- // happen inside the transition so the NavigationProvider reads the
493
- // UPDATED navigation state (set by the router inside perform).
485
+ // decoded RSC payload with NavigationProvider + NuqsAdapter. navState
486
+ // is passed explicitly by the router no getNavigationState() needed.
494
487
  navigateTransition: (pendingUrl: string, perform) => {
495
488
  return navigateTransition(pendingUrl, async () => {
496
- const payload = await perform((rawPayload: unknown) => {
497
- const navState = getNavigationState();
489
+ const payload = await perform((rawPayload: unknown, navState: NavigationState) => {
498
490
  const withNav = createElement(
499
491
  NavigationProvider,
500
492
  { value: navState },
@@ -586,7 +578,6 @@ function bootstrap(runtimeConfig: typeof config): void {
586
578
  scrollTimer = setTimeout(() => {
587
579
  const state = window.history.state;
588
580
  if (state && typeof state === 'object') {
589
- // Use getScrollY to capture scroll from overflow containers too.
590
581
  window.history.replaceState({ ...state, scrollY: getScrollY() }, '');
591
582
  }
592
583
  }, 100);
@@ -655,8 +646,21 @@ clearStaleReloadFlag();
655
646
  // If the payload references a module ID from a newer deployment, the error
656
647
  // surfaces as an unhandled rejection during React's render/hydration cycle.
657
648
  // This handler catches those errors and triggers a full page reload.
649
+ //
650
+ // Also catches chunk load failures (dynamic import of missing assets after
651
+ // a deployment) — these surface as "Failed to fetch dynamically imported module"
652
+ // or "Loading chunk <name> failed" errors. See TIM-446.
658
653
  window.addEventListener('unhandledrejection', (event) => {
659
- if (isStaleClientReference(event.reason)) {
654
+ if (isStaleClientReference(event.reason) || isChunkLoadError(event.reason)) {
655
+ event.preventDefault();
656
+ triggerStaleReload();
657
+ }
658
+ });
659
+
660
+ // Also catch synchronous errors from chunk loads (some browsers throw
661
+ // TypeError synchronously instead of via unhandled rejection).
662
+ window.addEventListener('error', (event) => {
663
+ if (isChunkLoadError(event.error)) {
660
664
  event.preventDefault();
661
665
  triggerStaleReload();
662
666
  }
@@ -65,7 +65,16 @@ type ParsedDigest = DenyDigest | RenderErrorDigest | RedirectDigest;
65
65
 
66
66
  export interface TimberErrorBoundaryProps {
67
67
  /** The component to render when an error is caught. */
68
- fallbackComponent: (...args: unknown[]) => ReactNode;
68
+ fallbackComponent?: (...args: unknown[]) => ReactNode;
69
+ /**
70
+ * Pre-rendered fallback element. Used for MDX status files which are server
71
+ * components and cannot be passed as function props across the RSC→client
72
+ * boundary. When set, rendered directly instead of calling fallbackComponent.
73
+ *
74
+ * See design/10-error-handling.md §"Status-Code File Variants" — MDX status
75
+ * files are server components by default (zero client JS).
76
+ */
77
+ fallbackElement?: ReactNode;
69
78
  /**
70
79
  * Status code filter. If set, only catches errors matching this status.
71
80
  * 400 = any 4xx, 500 = any 5xx, specific number = exact match.
@@ -162,6 +171,14 @@ export class TimberErrorBoundary extends Component<
162
171
  }
163
172
  }
164
173
 
174
+ // Pre-rendered fallback element (MDX status files) — render directly.
175
+ // MDX components are server components that cannot be passed as function
176
+ // props across the RSC→client boundary. Instead, they are pre-rendered
177
+ // as elements in the RSC environment and passed here as fallbackElement.
178
+ if (this.props.fallbackElement != null) {
179
+ return this.props.fallbackElement;
180
+ }
181
+
165
182
  // Render the fallback component with the right props shape.
166
183
  if (parsed?.type === 'deny') {
167
184
  return createElement(this.props.fallbackComponent as never, {
@@ -5,6 +5,7 @@ export type { JsonSerializable, RenderErrorDigest } from './types';
5
5
 
6
6
  // Navigation
7
7
  export { Link, interpolateParams, resolveHref, validateLinkHref, buildLinkProps } from './link';
8
+ export { mergePreservedSearchParams } from '#/shared/merge-search-params.js';
8
9
  export type { LinkProps, LinkPropsWithHref, LinkPropsWithParams } from './link';
9
10
  export type { OnNavigateHandler, OnNavigateEvent } from './link';
10
11
  export { createRouter } from './router';
@@ -12,6 +13,7 @@ export type {
12
13
  RouterInstance,
13
14
  NavigationOptions,
14
15
  RouterDeps,
16
+ RouterPhase,
15
17
  RscDecoder,
16
18
  RootRenderer,
17
19
  } from './router';
@@ -42,7 +44,7 @@ export { useActionState, useFormAction, useFormErrors } from './form';
42
44
  export type { UseActionStateFn, UseActionStateReturn, FormErrorsResult } from './form';
43
45
 
44
46
  // Params
45
- export { useParams, setCurrentParams } from './use-params';
47
+ export { useSegmentParams, setCurrentParams } from './use-params';
46
48
 
47
49
  // Navigation context (framework-internal, used by browser-entry for atomic updates)
48
50
  export { NavigationProvider, getNavigationState, setNavigationState } from './navigation-context';
@@ -55,6 +57,13 @@ export { useQueryStates, bindUseQueryStates } from './use-query-states';
55
57
  export { useCookie } from './use-cookie';
56
58
  export type { ClientCookieOptions, CookieSetter } from './use-cookie';
57
59
 
60
+ // Register the client cookie module with defineCookie's lazy reference.
61
+ // This runs at module load time in the client/SSR environment, wiring up
62
+ // the useCookie hook without a top-level import in define-cookie.ts.
63
+ import * as _useCookieMod from './use-cookie.js';
64
+ import { _registerUseCookieModule } from '#/cookies/define-cookie.js';
65
+ _registerUseCookieModule(_useCookieMod);
66
+
58
67
  // SSR data (framework-internal, used by ssr-entry to provide request data to hooks)
59
68
  export { setSsrData, clearSsrData, getSsrData } from './ssr-data';
60
69
  export type { SsrData } from './ssr-data';
@@ -19,9 +19,27 @@
19
19
  // - searchParams and inline query string are mutually exclusive
20
20
 
21
21
  import type { AnchorHTMLAttributes, ReactNode, MouseEvent as ReactMouseEvent } from 'react';
22
- import type { SearchParamsDefinition } from '#/search-params/create.js';
22
+ import type { SearchParamsDefinition } from '#/search-params/define.js';
23
23
  import { LinkStatusProvider } from './link-status-provider.js';
24
24
  import { getRouterOrNull } from './router-ref.js';
25
+ import { getSsrData } from './ssr-data.js';
26
+ import { mergePreservedSearchParams } from '#/shared/merge-search-params.js';
27
+
28
+ // ─── Current Search Params ────────────────────────────────────────
29
+
30
+ /**
31
+ * Read the current URL's search string without requiring a React hook.
32
+ * On the client, reads window.location.search. During SSR, reads from
33
+ * the request context (getSsrData). Returns empty string if unavailable.
34
+ */
35
+ function getCurrentSearch(): string {
36
+ if (typeof window !== 'undefined') return window.location.search;
37
+ const data = getSsrData();
38
+ if (!data) return '';
39
+ const sp = new URLSearchParams(data.searchParams);
40
+ const str = sp.toString();
41
+ return str ? `?${str}` : '';
42
+ }
25
43
 
26
44
  // ─── Types ───────────────────────────────────────────────────────
27
45
 
@@ -42,6 +60,20 @@ interface LinkBaseProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'h
42
60
  * Set to false for tabbed interfaces where content changes within a fixed layout.
43
61
  */
44
62
  scroll?: boolean;
63
+ /**
64
+ * Preserve search params from the current URL across navigation.
65
+ *
66
+ * - `true` — preserve ALL current search params (target params take precedence)
67
+ * - `string[]` — preserve only the named params (e.g. `['private', 'token']`)
68
+ *
69
+ * Useful for route-group gating where a search param (e.g. `?private=access`)
70
+ * must persist across internal navigations. The target href's own search params
71
+ * always take precedence over preserved ones.
72
+ *
73
+ * During SSR, reads search params from the request context. On the client,
74
+ * reads from the current URL and updates reactively when the URL changes.
75
+ */
76
+ preserveSearchParams?: true | string[];
45
77
  /**
46
78
  * Called before client-side navigation commits. Call `e.preventDefault()`
47
79
  * to cancel the default navigation — the caller is then responsible for
@@ -61,7 +93,7 @@ interface LinkBaseProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'h
61
93
  */
62
94
  export interface LinkPropsWithHref extends LinkBaseProps {
63
95
  href: string;
64
- params?: never;
96
+ segmentParams?: never;
65
97
  /**
66
98
  * Typed search params — serialized via the route's SearchParamsDefinition.
67
99
  * Mutually exclusive with an inline query string in href.
@@ -73,9 +105,9 @@ export interface LinkPropsWithHref extends LinkBaseProps {
73
105
  }
74
106
 
75
107
  /**
76
- * Link with a route pattern + params for interpolation.
77
- * e.g. <Link href="/products/[id]" params={{ id: "123" }}>
78
- * <Link href="/products/[id]" params={{ id: 123 }}>
108
+ * Link with a route pattern + segmentParams for interpolation.
109
+ * e.g. <Link href="/products/[id]" segmentParams={{ id: "123" }}>
110
+ * <Link href="/products/[id]" segmentParams={{ id: 123 }}>
79
111
  */
80
112
  export interface LinkPropsWithParams extends LinkBaseProps {
81
113
  /** Route pattern with dynamic segments (e.g. "/products/[id]") */
@@ -85,7 +117,7 @@ export interface LinkPropsWithParams extends LinkBaseProps {
85
117
  * Single dynamic segments accept string | number (numbers are stringified).
86
118
  * Catch-all segments accept string[].
87
119
  */
88
- params: Record<string, string | number | string[]>;
120
+ segmentParams: Record<string, string | number | string[]>;
89
121
  /**
90
122
  * Typed search params — serialized via the route's SearchParamsDefinition.
91
123
  */
@@ -303,22 +335,34 @@ function shouldInterceptClick(
303
335
  * its own click handling.
304
336
  *
305
337
  * Supports typed routes via codegen overloads. At runtime:
306
- * - `params` prop interpolates dynamic segments in the href pattern
338
+ * - `segmentParams` prop interpolates dynamic segments in the href pattern
307
339
  * - `searchParams` prop serializes query parameters via a SearchParamsDefinition
308
340
  */
309
341
  export function Link({
310
342
  href,
311
343
  prefetch,
312
344
  scroll,
313
- params,
345
+ segmentParams,
314
346
  searchParams,
347
+ preserveSearchParams,
315
348
  onNavigate,
316
349
  onClick: userOnClick,
317
350
  onMouseEnter: userOnMouseEnter,
318
351
  children,
319
352
  ...rest
320
353
  }: LinkProps) {
321
- const { href: resolvedHref } = buildLinkProps({ href, params, searchParams });
354
+ const { href: baseHref } = buildLinkProps({ href, params: segmentParams, searchParams });
355
+
356
+ // Preserve search params from the current URL when requested.
357
+ // useSearchParams() works during both SSR (reads from request context)
358
+ // and on the client (reads from window.location, reactive to URL changes).
359
+ // We read current search params directly to avoid unconditional hook calls.
360
+ // On the client, window.location.search is always current; during SSR,
361
+ // getSsrData() provides the request's search params.
362
+ const resolvedHref = preserveSearchParams
363
+ ? mergePreservedSearchParams(baseHref, getCurrentSearch(), preserveSearchParams)
364
+ : baseHref;
365
+
322
366
  const internal = isInternalHref(resolvedHref);
323
367
 
324
368
  // ─── Click handler ───────────────────────────────────────────
@@ -335,7 +379,11 @@ export function Link({
335
379
  // Call onNavigate if provided — allows caller to cancel
336
380
  if (onNavigate) {
337
381
  let prevented = false;
338
- onNavigate({ preventDefault: () => { prevented = true; } });
382
+ onNavigate({
383
+ preventDefault: () => {
384
+ prevented = true;
385
+ },
386
+ });
339
387
  if (prevented) {
340
388
  event.preventDefault();
341
389
  return;
@@ -347,20 +395,31 @@ export function Link({
347
395
 
348
396
  event.preventDefault();
349
397
  const shouldScroll = scroll !== false;
350
- void router.navigate(resolvedHref, { scroll: shouldScroll });
398
+
399
+ // Re-merge preserved search params at click time to pick up any
400
+ // URL changes since render (e.g. from other navigations or pushState).
401
+ const navHref = preserveSearchParams
402
+ ? mergePreservedSearchParams(baseHref, getCurrentSearch(), preserveSearchParams)
403
+ : resolvedHref;
404
+ void router.navigate(navHref, { scroll: shouldScroll });
351
405
  }
352
406
  : userOnClick; // External links — just pass through user's onClick
353
407
 
354
408
  // ─── Hover prefetch ──────────────────────────────────────────
355
- const handleMouseEnter = internal && prefetch
356
- ? (event: ReactMouseEvent<HTMLAnchorElement>) => {
357
- userOnMouseEnter?.(event);
358
- const router = getRouterOrNull();
359
- if (router) {
360
- router.prefetch(resolvedHref);
409
+ const handleMouseEnter =
410
+ internal && prefetch
411
+ ? (event: ReactMouseEvent<HTMLAnchorElement>) => {
412
+ userOnMouseEnter?.(event);
413
+ const router = getRouterOrNull();
414
+ if (router) {
415
+ // Re-merge preserved search params at hover time for fresh prefetch URL
416
+ const prefetchHref = preserveSearchParams
417
+ ? mergePreservedSearchParams(baseHref, getCurrentSearch(), preserveSearchParams)
418
+ : resolvedHref;
419
+ router.prefetch(prefetchHref);
420
+ }
361
421
  }
362
- }
363
- : userOnMouseEnter;
422
+ : userOnMouseEnter;
364
423
 
365
424
  return (
366
425
  <a {...rest} href={resolvedHref} onClick={handleClick} onMouseEnter={handleMouseEnter}>
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Holds the current route params and pathname, updated atomically
7
7
  * with the RSC tree on each navigation. This replaces the previous
8
- * useSyncExternalStore approach for useParams() and usePathname(),
8
+ * useSyncExternalStore approach for useSegmentParams() and usePathname(),
9
9
  * which suffered from a timing gap: the new tree could commit before
10
10
  * the external store re-renders fired, causing a frame where both
11
11
  * old and new active states were visible simultaneously.
@@ -90,7 +90,7 @@ function getOrCreateContext(): React.Context<NavigationState | null> | undefined
90
90
  /**
91
91
  * Read the navigation context. Returns null during SSR (no provider)
92
92
  * or in the RSC environment (no context available).
93
- * Internal — used by useParams() and usePathname().
93
+ * Internal — used by useSegmentParams() and usePathname().
94
94
  */
95
95
  export function useNavigationContext(): NavigationState | null {
96
96
  const ctx = getOrCreateContext();