@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
@@ -1,128 +1,4 @@
1
1
  "use client";
2
2
  "use client";
3
- import { n as getSsrData } from "../_chunks/ssr-data-MjmprTmO.js";
4
- import { Component, createElement } from "react";
5
- //#region src/client/error-boundary.tsx
6
- /**
7
- * Framework-injected React error boundary.
8
- *
9
- * Catches errors thrown by children and renders a fallback component
10
- * with the appropriate props based on error type:
11
- * - DenySignal (4xx) → { status, dangerouslyPassData }
12
- * - RenderError (5xx) → { error, digest, reset }
13
- * - Unhandled error → { error, digest: null, reset }
14
- *
15
- * The `status` prop controls which errors this boundary catches:
16
- * - Specific code (e.g. 403) → only that status
17
- * - Category (400) → any 4xx
18
- * - Category (500) → any 5xx
19
- * - Omitted → catches everything (error.tsx behavior)
20
- *
21
- * See design/10-error-handling.md §"Status-Code Files"
22
- */
23
- var _isUnloading = false;
24
- if (typeof window !== "undefined") {
25
- window.addEventListener("beforeunload", () => {
26
- _isUnloading = true;
27
- });
28
- window.addEventListener("pagehide", () => {
29
- _isUnloading = true;
30
- });
31
- }
32
- var TimberErrorBoundary = class extends Component {
33
- constructor(props) {
34
- super(props);
35
- this.state = {
36
- hasError: false,
37
- error: null
38
- };
39
- }
40
- static getDerivedStateFromError(error) {
41
- if (_isUnloading) return {
42
- hasError: false,
43
- error: null
44
- };
45
- return {
46
- hasError: true,
47
- error
48
- };
49
- }
50
- componentDidUpdate(prevProps) {
51
- if (this.state.hasError && prevProps.children !== this.props.children) this.setState({
52
- hasError: false,
53
- error: null
54
- });
55
- }
56
- /** Reset the error state so children re-render. */
57
- reset = () => {
58
- this.setState({
59
- hasError: false,
60
- error: null
61
- });
62
- };
63
- render() {
64
- if (!this.state.hasError || !this.state.error) return this.props.children;
65
- const error = this.state.error;
66
- const parsed = parseDigest(error);
67
- if (parsed?.type === "redirect") throw error;
68
- if (this.props.status != null) {
69
- const errorStatus = getErrorStatus(parsed, error);
70
- if (errorStatus == null || !statusMatches(this.props.status, errorStatus)) throw error;
71
- }
72
- if (parsed?.type === "deny" && this.props.isSlotBoundary) {
73
- const ssrData = getSsrData();
74
- if (ssrData?._navContext) ssrData._navContext._denyHandledByBoundary = true;
75
- }
76
- if (parsed?.type === "deny") return createElement(this.props.fallbackComponent, {
77
- status: parsed.status,
78
- dangerouslyPassData: parsed.data
79
- });
80
- const digest = parsed?.type === "render-error" ? {
81
- code: parsed.code,
82
- data: parsed.data
83
- } : null;
84
- return createElement(this.props.fallbackComponent, {
85
- error,
86
- digest,
87
- reset: this.reset
88
- });
89
- }
90
- };
91
- /**
92
- * Parse the structured digest from the error.
93
- * React sets `error.digest` from the string returned by RSC's onError.
94
- */
95
- function parseDigest(error) {
96
- const raw = error.digest;
97
- if (typeof raw !== "string") return null;
98
- try {
99
- const parsed = JSON.parse(raw);
100
- if (parsed && typeof parsed === "object" && typeof parsed.type === "string") return parsed;
101
- } catch {}
102
- return null;
103
- }
104
- /**
105
- * Extract the HTTP status code from a parsed digest or error message.
106
- * Falls back to message pattern matching for errors without a digest.
107
- */
108
- function getErrorStatus(parsed, error) {
109
- if (parsed?.type === "deny") return parsed.status;
110
- if (parsed?.type === "render-error") return parsed.status;
111
- if (parsed?.type === "redirect") return parsed.status;
112
- const match = error.message.match(/^Access denied with status (\d+)$/);
113
- if (match) return parseInt(match[1], 10);
114
- return 500;
115
- }
116
- /**
117
- * Check whether an error's status matches the boundary's status filter.
118
- * Category markers (400, 500) match any status in that range.
119
- */
120
- function statusMatches(boundaryStatus, errorStatus) {
121
- if (boundaryStatus === 400) return errorStatus >= 400 && errorStatus <= 499;
122
- if (boundaryStatus === 500) return errorStatus >= 500 && errorStatus <= 599;
123
- return boundaryStatus === errorStatus;
124
- }
125
- //#endregion
3
+ import { t as TimberErrorBoundary } from "../_chunks/error-boundary-BAN3751q.js";
126
4
  export { TimberErrorBoundary };
127
-
128
- //# sourceMappingURL=error-boundary.js.map
@@ -1,9 +1,10 @@
1
1
  export type { JsonSerializable, RenderErrorDigest } from './types';
2
2
  export { Link, interpolateParams, resolveHref, validateLinkHref, buildLinkProps } from './link';
3
+ export { mergePreservedSearchParams } from '#/shared/merge-search-params.js';
3
4
  export type { LinkProps, LinkPropsWithHref, LinkPropsWithParams } from './link';
4
5
  export type { OnNavigateHandler, OnNavigateEvent } from './link';
5
6
  export { createRouter } from './router';
6
- export type { RouterInstance, NavigationOptions, RouterDeps, RscDecoder, RootRenderer, } from './router';
7
+ export type { RouterInstance, NavigationOptions, RouterDeps, RouterPhase, RscDecoder, RootRenderer, } from './router';
7
8
  export { useNavigationPending } from './use-navigation-pending';
8
9
  export { useLinkStatus, LinkStatusContext } from './use-link-status';
9
10
  export type { LinkStatus } from './use-link-status';
@@ -21,7 +22,7 @@ export { HistoryStack } from './history';
21
22
  export type { HistoryEntry } from './history';
22
23
  export { useActionState, useFormAction, useFormErrors } from './form';
23
24
  export type { UseActionStateFn, UseActionStateReturn, FormErrorsResult } from './form';
24
- export { useParams, setCurrentParams } from './use-params';
25
+ export { useSegmentParams, setCurrentParams } from './use-params';
25
26
  export { NavigationProvider, getNavigationState, setNavigationState } from './navigation-context';
26
27
  export type { NavigationState } from './navigation-context';
27
28
  export { useQueryStates, bindUseQueryStates } from './use-query-states';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAGnE,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,WAAW,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAChG,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAChF,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EACV,cAAc,EACd,iBAAiB,EACjB,UAAU,EACV,UAAU,EACV,YAAY,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACrE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAGpG,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACvE,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAG7D,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC9D,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG9D,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAG9C,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACtE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAGvF,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAG3D,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAClG,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAG5D,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGxE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,YAAY,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAGtE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAClE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,YAAY,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAGnE,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,WAAW,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAChG,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAChF,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EACV,cAAc,EACd,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,UAAU,EACV,YAAY,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACrE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAGpG,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACvE,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAG7D,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC9D,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG9D,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAG9C,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACtE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAGvF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGlE,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAClG,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAG5D,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGxE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,YAAY,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAUtE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAClE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,YAAY,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -1,9 +1,10 @@
1
1
  "use client";
2
- import { a as _setCurrentParams, c as cachedSearchParams, i as _setCachedSearch, l as currentParams, n as getSsrData, o as _setGlobalRouter, r as setSsrData, s as cachedSearch, t as clearSsrData, u as globalRouter } from "../_chunks/ssr-data-MjmprTmO.js";
3
- import { n as useQueryStates, t as bindUseQueryStates } from "../_chunks/use-query-states-D5KaffOK.js";
4
- import { t as useCookie } from "../_chunks/use-cookie-DX-l1_5E.js";
5
- import { TimberErrorBoundary } from "./error-boundary.js";
6
- import React, { cloneElement, createContext, createElement, isValidElement, useActionState as useActionState$1, useContext, useMemo, useSyncExternalStore, useTransition } from "react";
2
+ import { n as __exportAll } from "../_chunks/chunk-DYhsFzuS.js";
3
+ import { n as useSegmentContext, r as mergePreservedSearchParams, t as SegmentProvider } from "../_chunks/segment-context-C6byCyZU.js";
4
+ import { a as _setCachedSearch, c as cachedSearch, d as globalRouter, i as setSsrData, l as cachedSearchParams, n as clearSsrData, o as _setCurrentParams, r as getSsrData, s as _setGlobalRouter, t as TimberErrorBoundary, u as currentParams } from "../_chunks/error-boundary-BAN3751q.js";
5
+ import { n as useQueryStates, t as bindUseQueryStates } from "../_chunks/use-query-states-BvW0TKDn.js";
6
+ import { t as _registerUseCookieModule } from "../_chunks/define-cookie-BmKbSyp0.js";
7
+ import React, { cloneElement, createContext, createElement, isValidElement, useActionState as useActionState$1, useContext, useSyncExternalStore, useTransition } from "react";
7
8
  import { jsx } from "react/jsx-runtime";
8
9
  //#region src/client/use-link-status.ts
9
10
  /**
@@ -46,7 +47,7 @@ function useLinkStatus() {
46
47
  *
47
48
  * Holds the current route params and pathname, updated atomically
48
49
  * with the RSC tree on each navigation. This replaces the previous
49
- * useSyncExternalStore approach for useParams() and usePathname(),
50
+ * useSyncExternalStore approach for useSegmentParams() and usePathname(),
50
51
  * which suffered from a timing gap: the new tree could commit before
51
52
  * the external store re-renders fired, causing a frame where both
52
53
  * old and new active states were visible simultaneously.
@@ -107,7 +108,7 @@ function getOrCreateContext() {
107
108
  /**
108
109
  * Read the navigation context. Returns null during SSR (no provider)
109
110
  * or in the RSC environment (no context available).
110
- * Internal — used by useParams() and usePathname().
111
+ * Internal — used by useSegmentParams() and usePathname().
111
112
  */
112
113
  function useNavigationContext() {
113
114
  const ctx = getOrCreateContext();
@@ -225,6 +226,18 @@ function getRouterOrNull() {
225
226
  //#endregion
226
227
  //#region src/client/link.tsx
227
228
  /**
229
+ * Read the current URL's search string without requiring a React hook.
230
+ * On the client, reads window.location.search. During SSR, reads from
231
+ * the request context (getSsrData). Returns empty string if unavailable.
232
+ */
233
+ function getCurrentSearch() {
234
+ if (typeof window !== "undefined") return window.location.search;
235
+ const data = getSsrData();
236
+ if (!data) return "";
237
+ const str = new URLSearchParams(data.searchParams).toString();
238
+ return str ? `?${str}` : "";
239
+ }
240
+ /**
228
241
  * Reject dangerous URL schemes that could execute script.
229
242
  * Security: design/13-security.md § Link scheme injection (test #9)
230
243
  */
@@ -324,15 +337,16 @@ function shouldInterceptClick(event, resolvedHref) {
324
337
  * its own click handling.
325
338
  *
326
339
  * Supports typed routes via codegen overloads. At runtime:
327
- * - `params` prop interpolates dynamic segments in the href pattern
340
+ * - `segmentParams` prop interpolates dynamic segments in the href pattern
328
341
  * - `searchParams` prop serializes query parameters via a SearchParamsDefinition
329
342
  */
330
- function Link({ href, prefetch, scroll, params, searchParams, onNavigate, onClick: userOnClick, onMouseEnter: userOnMouseEnter, children, ...rest }) {
331
- const { href: resolvedHref } = buildLinkProps({
343
+ function Link({ href, prefetch, scroll, segmentParams, searchParams, preserveSearchParams, onNavigate, onClick: userOnClick, onMouseEnter: userOnMouseEnter, children, ...rest }) {
344
+ const { href: baseHref } = buildLinkProps({
332
345
  href,
333
- params,
346
+ params: segmentParams,
334
347
  searchParams
335
348
  });
349
+ const resolvedHref = preserveSearchParams ? mergePreservedSearchParams(baseHref, getCurrentSearch(), preserveSearchParams) : baseHref;
336
350
  const internal = isInternalHref(resolvedHref);
337
351
  const handleClick = internal ? (event) => {
338
352
  userOnClick?.(event);
@@ -351,12 +365,16 @@ function Link({ href, prefetch, scroll, params, searchParams, onNavigate, onClic
351
365
  if (!router) return;
352
366
  event.preventDefault();
353
367
  const shouldScroll = scroll !== false;
354
- router.navigate(resolvedHref, { scroll: shouldScroll });
368
+ const navHref = preserveSearchParams ? mergePreservedSearchParams(baseHref, getCurrentSearch(), preserveSearchParams) : resolvedHref;
369
+ router.navigate(navHref, { scroll: shouldScroll });
355
370
  } : userOnClick;
356
371
  const handleMouseEnter = internal && prefetch ? (event) => {
357
372
  userOnMouseEnter?.(event);
358
373
  const router = getRouterOrNull();
359
- if (router) router.prefetch(resolvedHref);
374
+ if (router) {
375
+ const prefetchHref = preserveSearchParams ? mergePreservedSearchParams(baseHref, getCurrentSearch(), preserveSearchParams) : resolvedHref;
376
+ router.prefetch(prefetchHref);
377
+ }
360
378
  } : userOnMouseEnter;
361
379
  return /* @__PURE__ */ jsx("a", {
362
380
  ...rest,
@@ -520,7 +538,7 @@ var HistoryStack = class {
520
538
  function setCurrentParams(params) {
521
539
  _setCurrentParams(params);
522
540
  }
523
- function useParams(_route) {
541
+ function useSegmentParams(_route) {
524
542
  try {
525
543
  const navContext = useNavigationContext();
526
544
  if (navContext !== null) return navContext.params;
@@ -759,10 +777,28 @@ function generateCacheBustId() {
759
777
  function appendRscParam(url) {
760
778
  return `${url}${url.includes("?") ? "&" : "?"}_rsc=${generateCacheBustId()}`;
761
779
  }
780
+ /**
781
+ * The client's deployment ID, set at bootstrap from the runtime config.
782
+ * Sent with every RSC/action request for version skew detection.
783
+ * Null in dev mode. See TIM-446.
784
+ */
785
+ var clientDeploymentId = null;
786
+ /** Header name used by the server to signal a version skew reload. */
787
+ var RELOAD_HEADER = "X-Timber-Reload";
788
+ /** Header name for the client's deployment ID. */
789
+ var DEPLOYMENT_ID_HEADER = "X-Timber-Deployment-Id";
790
+ /**
791
+ * Check if a response signals a version skew reload.
792
+ * Triggers a full page reload if the server indicates the client is stale.
793
+ */
794
+ function checkReloadSignal(response) {
795
+ return response.headers.get(RELOAD_HEADER) === "1";
796
+ }
762
797
  function buildRscHeaders(stateTree, currentUrl) {
763
798
  const headers = { Accept: RSC_CONTENT_TYPE };
764
799
  if (stateTree) headers["X-Timber-State-Tree"] = JSON.stringify(stateTree);
765
800
  if (currentUrl) headers["X-Timber-URL"] = currentUrl;
801
+ if (clientDeploymentId) headers[DEPLOYMENT_ID_HEADER] = clientDeploymentId;
766
802
  return headers;
767
803
  }
768
804
  /**
@@ -817,7 +853,7 @@ function extractSkippedSegments(response) {
817
853
  * Extract route params from the X-Timber-Params response header.
818
854
  * Returns null if the header is missing or malformed.
819
855
  *
820
- * Used to populate useParams() after client-side navigation.
856
+ * Used to populate useSegmentParams() after client-side navigation.
821
857
  */
822
858
  function extractParams(response) {
823
859
  const header = response.headers.get("X-Timber-Params");
@@ -840,6 +876,16 @@ var RedirectError = class extends Error {
840
876
  }
841
877
  };
842
878
  /**
879
+ * Thrown when the server signals a version skew (X-Timber-Reload header).
880
+ * Caught in navigate() to trigger a full page reload via triggerStaleReload().
881
+ * See TIM-446.
882
+ */
883
+ var VersionSkewError = class extends Error {
884
+ constructor() {
885
+ super("Version skew detected — server has been redeployed");
886
+ }
887
+ };
888
+ /**
843
889
  * Fetch an RSC payload from the server. If a decodeRsc function is provided,
844
890
  * the response is decoded into a React element tree via createFromFetch.
845
891
  * Otherwise, the raw response text is returned (test mode).
@@ -860,6 +906,7 @@ async function fetchRscPayload(url, deps, stateTree, currentUrl) {
860
906
  let params = null;
861
907
  let skippedSegments = null;
862
908
  const wrappedPromise = fetchPromise.then((response) => {
909
+ if (checkReloadSignal(response)) throw new VersionSkewError();
863
910
  const redirectLocation = response.headers.get("X-Timber-Redirect") || (response.status >= 300 && response.status < 400 ? response.headers.get("Location") : null);
864
911
  if (redirectLocation) throw new RedirectError(redirectLocation);
865
912
  headElements = extractHeadElements(response);
@@ -904,23 +951,20 @@ function isAbortError(error) {
904
951
  if (error instanceof Error && error.name === "AbortError") return true;
905
952
  return false;
906
953
  }
907
- /**
908
- * Create a router instance. In production, called once at app hydration
909
- * with real browser APIs. In tests, called with mock dependencies.
910
- */
911
954
  function createRouter(deps) {
912
955
  const segmentCache = new SegmentCache();
913
956
  const prefetchCache = new PrefetchCache();
914
957
  const historyStack = new HistoryStack();
915
958
  const segmentElementCache = new SegmentElementCache();
916
- let pending = false;
917
- let pendingUrl = null;
959
+ let routerPhase = { phase: "idle" };
918
960
  const pendingListeners = /* @__PURE__ */ new Set();
919
961
  function setPending(value, url) {
920
- const newPendingUrl = value && url ? url : null;
921
- if (pending === value && pendingUrl === newPendingUrl) return;
922
- pending = value;
923
- pendingUrl = newPendingUrl;
962
+ const next = value && url ? {
963
+ phase: "navigating",
964
+ targetUrl: url
965
+ } : { phase: "idle" };
966
+ if (routerPhase.phase === next.phase && (routerPhase.phase === "idle" || routerPhase.phase === "navigating" && next.phase === "navigating" && routerPhase.targetUrl === next.targetUrl)) return;
967
+ routerPhase = next;
924
968
  for (const listener of pendingListeners) listener(value);
925
969
  }
926
970
  /** Update the segment cache from server-provided segment metadata. */
@@ -930,8 +974,8 @@ function createRouter(deps) {
930
974
  if (tree) segmentCache.set("/", tree);
931
975
  }
932
976
  /** Render a decoded RSC payload into the DOM if a renderer is available. */
933
- function renderPayload(payload) {
934
- if (deps.renderRoot) deps.renderRoot(payload);
977
+ function renderPayload(payload, navState) {
978
+ if (deps.renderRoot) deps.renderRoot(payload, navState);
935
979
  }
936
980
  /**
937
981
  * Merge a partial RSC payload with cached segment elements if segments
@@ -947,24 +991,26 @@ function createRouter(deps) {
947
991
  /**
948
992
  * Update navigation state (params + pathname) for the next render.
949
993
  *
950
- * Sets both the module-level fallback (for tests and SSR) and the
951
- * navigation context state (read by renderRoot to wrap the element
952
- * in NavigationProvider). The context update is atomic with the tree
953
- * render both are passed to reactRoot.render() in the same call.
994
+ * Sets the module-level fallback (for tests and SSR) and the
995
+ * globalThis bridge, then returns the NavigationState so callers
996
+ * can pass it explicitly to renderRoot/wrapPayload eliminating
997
+ * temporal coupling with getNavigationState().
954
998
  */
955
999
  function updateNavigationState(params, url) {
956
1000
  const resolvedParams = params ?? {};
957
1001
  setCurrentParams(resolvedParams);
958
- setNavigationState({
1002
+ const navState = {
959
1003
  params: resolvedParams,
960
1004
  pathname: url.startsWith("http") ? new URL(url).pathname : url.split("?")[0] || "/"
961
- });
1005
+ };
1006
+ setNavigationState(navState);
1007
+ return navState;
962
1008
  }
963
1009
  /**
964
1010
  * Render a payload via navigateTransition (production) or renderRoot (tests).
965
- * The perform callback should fetch data, update state, and return the payload.
966
- * In production, the entire callback runs inside a React transition with
967
- * useOptimistic for the pending URL. In tests, the payload is rendered directly.
1011
+ * The perform callback should fetch data, update state, and return the
1012
+ * FetchResult plus the NavigationState (so it can be passed explicitly
1013
+ * to wrapPayload/renderRoot without temporal coupling).
968
1014
  */
969
1015
  async function renderViaTransition(url, perform) {
970
1016
  if (deps.navigateTransition) {
@@ -978,7 +1024,7 @@ function createRouter(deps) {
978
1024
  headElements: result.headElements,
979
1025
  params: result.params
980
1026
  });
981
- return wrapPayload(merged);
1027
+ return wrapPayload(merged, result.navState);
982
1028
  });
983
1029
  return headElements;
984
1030
  }
@@ -989,7 +1035,7 @@ function createRouter(deps) {
989
1035
  headElements: result.headElements,
990
1036
  params: result.params
991
1037
  });
992
- renderPayload(merged);
1038
+ renderPayload(merged, result.navState);
993
1039
  return result.headElements;
994
1040
  }
995
1041
  /** Apply head elements (title, meta tags) to the DOM if available. */
@@ -1002,6 +1048,16 @@ function createRouter(deps) {
1002
1048
  else callback();
1003
1049
  }
1004
1050
  /**
1051
+ * Schedule scroll restoration after the next paint and fire the
1052
+ * scroll-restored event. Used by navigate, popstate, and refresh.
1053
+ */
1054
+ function restoreScrollAfterPaint(scrollY) {
1055
+ afterPaint(() => {
1056
+ deps.scrollTo(0, scrollY);
1057
+ window.dispatchEvent(new Event("timber:scroll-restored"));
1058
+ });
1059
+ }
1060
+ /**
1005
1061
  * Core navigation logic shared between the transition and fallback paths.
1006
1062
  * Fetches the RSC payload, updates all state, and returns the result.
1007
1063
  */
@@ -1028,8 +1084,11 @@ function createRouter(deps) {
1028
1084
  scrollY: 0
1029
1085
  }, "", url);
1030
1086
  updateSegmentCache(result.segmentInfo);
1031
- updateNavigationState(result.params, url);
1032
- return result;
1087
+ const navState = updateNavigationState(result.params, url);
1088
+ return {
1089
+ ...result,
1090
+ navState
1091
+ };
1033
1092
  }
1034
1093
  async function navigate(url, options = {}) {
1035
1094
  const scroll = options.scroll !== false;
@@ -1043,12 +1102,13 @@ function createRouter(deps) {
1043
1102
  try {
1044
1103
  applyHead(await renderViaTransition(url, () => performNavigationFetch(url, { replace })));
1045
1104
  window.dispatchEvent(new Event("timber:navigation-end"));
1046
- afterPaint(() => {
1047
- if (scroll) deps.scrollTo(0, 0);
1048
- else deps.scrollTo(0, currentScrollY);
1049
- window.dispatchEvent(new Event("timber:scroll-restored"));
1050
- });
1105
+ restoreScrollAfterPaint(scroll ? 0 : currentScrollY);
1051
1106
  } catch (error) {
1107
+ if (error instanceof VersionSkewError) {
1108
+ const { triggerStaleReload } = await import("../_chunks/stale-reload-C0ValzG7.js");
1109
+ triggerStaleReload();
1110
+ return new Promise(() => {});
1111
+ }
1052
1112
  if (error instanceof RedirectError) {
1053
1113
  setPending(false);
1054
1114
  await navigate(error.redirectUrl, { replace: true });
@@ -1067,8 +1127,11 @@ function createRouter(deps) {
1067
1127
  applyHead(await renderViaTransition(currentUrl, async () => {
1068
1128
  const result = await fetchRscPayload(currentUrl, deps);
1069
1129
  updateSegmentCache(result.segmentInfo);
1070
- updateNavigationState(result.params, currentUrl);
1071
- return result;
1130
+ const navState = updateNavigationState(result.params, currentUrl);
1131
+ return {
1132
+ ...result,
1133
+ navState
1134
+ };
1072
1135
  }));
1073
1136
  } finally {
1074
1137
  setPending(false);
@@ -1077,26 +1140,23 @@ function createRouter(deps) {
1077
1140
  async function handlePopState(url, scrollY = 0) {
1078
1141
  const entry = historyStack.get(url);
1079
1142
  if (entry && entry.payload !== null) {
1080
- updateNavigationState(entry.params, url);
1081
- renderPayload(entry.payload);
1143
+ const navState = updateNavigationState(entry.params, url);
1144
+ renderPayload(entry.payload, navState);
1082
1145
  applyHead(entry.headElements);
1083
- afterPaint(() => {
1084
- deps.scrollTo(0, scrollY);
1085
- window.dispatchEvent(new Event("timber:scroll-restored"));
1086
- });
1146
+ restoreScrollAfterPaint(scrollY);
1087
1147
  } else {
1088
1148
  setPending(true, url);
1089
1149
  try {
1090
1150
  applyHead(await renderViaTransition(url, async () => {
1091
1151
  const result = await fetchRscPayload(url, deps, segmentCache.serializeStateTree(segmentElementCache.getMergeablePaths()));
1092
1152
  updateSegmentCache(result.segmentInfo);
1093
- updateNavigationState(result.params, url);
1094
- return result;
1153
+ const navState = updateNavigationState(result.params, url);
1154
+ return {
1155
+ ...result,
1156
+ navState
1157
+ };
1095
1158
  }));
1096
- afterPaint(() => {
1097
- deps.scrollTo(0, scrollY);
1098
- window.dispatchEvent(new Event("timber:scroll-restored"));
1099
- });
1159
+ restoreScrollAfterPaint(scrollY);
1100
1160
  } finally {
1101
1161
  setPending(false);
1102
1162
  }
@@ -1117,8 +1177,8 @@ function createRouter(deps) {
1117
1177
  navigate,
1118
1178
  refresh,
1119
1179
  handlePopState,
1120
- isPending: () => pending,
1121
- getPendingUrl: () => pendingUrl,
1180
+ isPending: () => routerPhase.phase === "navigating",
1181
+ getPendingUrl: () => routerPhase.phase === "navigating" ? routerPhase.targetUrl : null,
1122
1182
  onPendingChange(listener) {
1123
1183
  pendingListeners.add(listener);
1124
1184
  return () => pendingListeners.delete(listener);
@@ -1131,7 +1191,7 @@ function createRouter(deps) {
1131
1191
  payload: merged,
1132
1192
  headElements
1133
1193
  });
1134
- renderPayload(merged);
1194
+ renderPayload(merged, getNavigationState());
1135
1195
  applyHead(headElements);
1136
1196
  },
1137
1197
  initSegmentCache: (segments) => updateSegmentCache(segments),
@@ -1354,36 +1414,6 @@ function useSearchParams() {
1354
1414
  return typeof window !== "undefined" ? getSearchParams() : getServerSearchParams();
1355
1415
  }
1356
1416
  //#endregion
1357
- //#region src/client/segment-context.ts
1358
- /**
1359
- * Segment Context — provides layout segment position for useSelectedLayoutSegment hooks.
1360
- *
1361
- * Each layout in the segment tree is wrapped with a SegmentProvider that stores
1362
- * the URL segments from root to the current layout level. The hooks read this
1363
- * context to determine which child segments are active below the calling layout.
1364
- *
1365
- * The context value is intentionally minimal: just the segment path array and
1366
- * parallel route keys. No internal cache details are exposed.
1367
- *
1368
- * Design docs: design/19-client-navigation.md, design/14-ecosystem.md
1369
- */
1370
- var SegmentContext = createContext(null);
1371
- /** Read the segment context. Returns null if no provider is above this component. */
1372
- function useSegmentContext() {
1373
- return useContext(SegmentContext);
1374
- }
1375
- /**
1376
- * Wraps each layout to provide segment position context.
1377
- * Injected by rsc-entry.ts during element tree construction.
1378
- */
1379
- function SegmentProvider({ segments, segmentId: _segmentId, parallelRouteKeys, children }) {
1380
- const value = useMemo(() => ({
1381
- segments,
1382
- parallelRouteKeys
1383
- }), [segments.join("/"), parallelRouteKeys.join(",")]);
1384
- return createElement(SegmentContext.Provider, { value }, children);
1385
- }
1386
- //#endregion
1387
1417
  //#region src/client/use-selected-layout-segment.ts
1388
1418
  /**
1389
1419
  * useSelectedLayoutSegment / useSelectedLayoutSegments — client-side hooks
@@ -1589,6 +1619,96 @@ function useFormErrors(result) {
1589
1619
  };
1590
1620
  }
1591
1621
  //#endregion
1592
- export { HistoryStack, Link, LinkStatusContext, NavigationProvider, PrefetchCache, SegmentCache, SegmentProvider, TimberErrorBoundary, bindUseQueryStates, buildLinkProps, clearSsrData, createRouter, getNavigationState, getRouter, getSsrData, interpolateParams, resolveHref, setCurrentParams, setGlobalRouter, setNavigationState, setSsrData, useActionState, useCookie, useFormAction, useFormErrors, useLinkStatus, useNavigationPending, useParams, usePathname, useQueryStates, useRouter, useSearchParams, useSegmentContext, useSelectedLayoutSegment, useSelectedLayoutSegments, validateLinkHref };
1622
+ //#region src/client/use-cookie.ts
1623
+ /**
1624
+ * useCookie — reactive client-side cookie hook.
1625
+ *
1626
+ * Uses useSyncExternalStore for SSR-safe, reactive cookie access.
1627
+ * All components reading the same cookie name re-render on change.
1628
+ * No cross-tab sync (intentional — see design/29-cookies.md).
1629
+ *
1630
+ * See design/29-cookies.md §"useCookie(name) Hook"
1631
+ */
1632
+ var use_cookie_exports = /* @__PURE__ */ __exportAll({ useCookie: () => useCookie });
1633
+ /** Per-name subscriber sets. */
1634
+ var listeners = /* @__PURE__ */ new Map();
1635
+ /** Parse a cookie name from document.cookie. */
1636
+ function getCookieValue(name) {
1637
+ if (typeof document === "undefined") return void 0;
1638
+ const match = document.cookie.match(new RegExp("(?:^|;\\s*)" + name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\s*=\\s*([^;]*)"));
1639
+ return match ? decodeURIComponent(match[1]) : void 0;
1640
+ }
1641
+ /** Serialize options into a cookie string suffix. */
1642
+ function serializeOptions(options) {
1643
+ if (!options) return "; Path=/; SameSite=Lax";
1644
+ const parts = [];
1645
+ parts.push(`Path=${options.path ?? "/"}`);
1646
+ if (options.domain) parts.push(`Domain=${options.domain}`);
1647
+ if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
1648
+ if (options.expires) parts.push(`Expires=${options.expires.toUTCString()}`);
1649
+ const sameSite = options.sameSite ?? "lax";
1650
+ parts.push(`SameSite=${sameSite.charAt(0).toUpperCase()}${sameSite.slice(1)}`);
1651
+ if (options.secure) parts.push("Secure");
1652
+ return "; " + parts.join("; ");
1653
+ }
1654
+ /** Notify all subscribers for a given cookie name. */
1655
+ function notify(name) {
1656
+ const subs = listeners.get(name);
1657
+ if (subs) for (const fn of subs) fn();
1658
+ }
1659
+ /**
1660
+ * Reactive hook for reading/writing a client-side cookie.
1661
+ *
1662
+ * Returns `[value, setCookie, deleteCookie]`:
1663
+ * - `value`: current cookie value (string | undefined)
1664
+ * - `setCookie`: sets the cookie and triggers re-renders
1665
+ * - `deleteCookie`: deletes the cookie and triggers re-renders
1666
+ *
1667
+ * @param name - Cookie name.
1668
+ * @param defaultOptions - Default options for setCookie calls.
1669
+ */
1670
+ function useCookie(name, defaultOptions) {
1671
+ const subscribe = (callback) => {
1672
+ let subs = listeners.get(name);
1673
+ if (!subs) {
1674
+ subs = /* @__PURE__ */ new Set();
1675
+ listeners.set(name, subs);
1676
+ }
1677
+ subs.add(callback);
1678
+ return () => {
1679
+ subs.delete(callback);
1680
+ if (subs.size === 0) listeners.delete(name);
1681
+ };
1682
+ };
1683
+ const getSnapshot = () => getCookieValue(name);
1684
+ const getServerSnapshot = () => getSsrData()?.cookies.get(name);
1685
+ const value = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
1686
+ const setCookie = (newValue, options) => {
1687
+ const merged = {
1688
+ ...defaultOptions,
1689
+ ...options
1690
+ };
1691
+ document.cookie = `${name}=${encodeURIComponent(newValue)}${serializeOptions(merged)}`;
1692
+ notify(name);
1693
+ };
1694
+ const deleteCookie = () => {
1695
+ const path = defaultOptions?.path ?? "/";
1696
+ const domain = defaultOptions?.domain;
1697
+ let cookieStr = `${name}=; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=${path}`;
1698
+ if (domain) cookieStr += `; Domain=${domain}`;
1699
+ document.cookie = cookieStr;
1700
+ notify(name);
1701
+ };
1702
+ return [
1703
+ value,
1704
+ setCookie,
1705
+ deleteCookie
1706
+ ];
1707
+ }
1708
+ //#endregion
1709
+ //#region src/client/index.ts
1710
+ _registerUseCookieModule(use_cookie_exports);
1711
+ //#endregion
1712
+ export { HistoryStack, Link, LinkStatusContext, NavigationProvider, PrefetchCache, SegmentCache, SegmentProvider, TimberErrorBoundary, bindUseQueryStates, buildLinkProps, clearSsrData, createRouter, getNavigationState, getRouter, getSsrData, interpolateParams, mergePreservedSearchParams, resolveHref, setCurrentParams, setGlobalRouter, setNavigationState, setSsrData, useActionState, useCookie, useFormAction, useFormErrors, useLinkStatus, useNavigationPending, usePathname, useQueryStates, useRouter, useSearchParams, useSegmentContext, useSegmentParams, useSelectedLayoutSegment, useSelectedLayoutSegments, validateLinkHref };
1593
1713
 
1594
1714
  //# sourceMappingURL=index.js.map