@timber-js/app 0.2.0-alpha.1 → 0.2.0-alpha.100

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 (742) hide show
  1. package/LICENSE +8 -0
  2. package/dist/_chunks/actions-CQ8Z8VGL.js +1061 -0
  3. package/dist/_chunks/actions-CQ8Z8VGL.js.map +1 -0
  4. package/dist/_chunks/build-output-helper-DXnW0qjz.js +61 -0
  5. package/dist/_chunks/build-output-helper-DXnW0qjz.js.map +1 -0
  6. package/dist/_chunks/chunk-BYIpzuS7.js +39 -0
  7. package/dist/_chunks/define-B-Q_UMOD.js +195 -0
  8. package/dist/_chunks/define-B-Q_UMOD.js.map +1 -0
  9. package/dist/_chunks/define-CfBPoJb0.js +123 -0
  10. package/dist/_chunks/define-CfBPoJb0.js.map +1 -0
  11. package/dist/_chunks/define-cookie-BjpIt4UC.js +194 -0
  12. package/dist/_chunks/define-cookie-BjpIt4UC.js.map +1 -0
  13. package/dist/_chunks/format-Bcn-Iv1x.js +14 -0
  14. package/dist/_chunks/format-Bcn-Iv1x.js.map +1 -0
  15. package/dist/_chunks/handler-store-B-lqaGyh.js +54 -0
  16. package/dist/_chunks/handler-store-B-lqaGyh.js.map +1 -0
  17. package/dist/_chunks/logger-0m8MsKdc.js +291 -0
  18. package/dist/_chunks/logger-0m8MsKdc.js.map +1 -0
  19. package/dist/_chunks/merge-search-params-BphMdht_.js +122 -0
  20. package/dist/_chunks/merge-search-params-BphMdht_.js.map +1 -0
  21. package/dist/_chunks/{metadata-routes-CQCnF4VK.js → metadata-routes-BU684ls2.js} +1 -1
  22. package/dist/_chunks/{metadata-routes-CQCnF4VK.js.map → metadata-routes-BU684ls2.js.map} +1 -1
  23. package/dist/_chunks/navigation-root-BCYczjml.js +96 -0
  24. package/dist/_chunks/navigation-root-BCYczjml.js.map +1 -0
  25. package/dist/_chunks/registry-I2ss-lvy.js +20 -0
  26. package/dist/_chunks/registry-I2ss-lvy.js.map +1 -0
  27. package/dist/_chunks/router-ref-h3-UaCQv.js +28 -0
  28. package/dist/_chunks/router-ref-h3-UaCQv.js.map +1 -0
  29. package/dist/_chunks/schema-bridge-Cxu4l-7p.js +86 -0
  30. package/dist/_chunks/schema-bridge-Cxu4l-7p.js.map +1 -0
  31. package/dist/_chunks/segment-classify-BjfuctV2.js +137 -0
  32. package/dist/_chunks/segment-classify-BjfuctV2.js.map +1 -0
  33. package/dist/_chunks/segment-context-Dx_OizxD.js +34 -0
  34. package/dist/_chunks/segment-context-Dx_OizxD.js.map +1 -0
  35. package/dist/_chunks/{ssr-data-DLnbYpj1.js → ssr-data-B4CdH7rE.js} +1 -1
  36. package/dist/_chunks/ssr-data-B4CdH7rE.js.map +1 -0
  37. package/dist/_chunks/stale-reload-Bab885FO.js +64 -0
  38. package/dist/_chunks/stale-reload-Bab885FO.js.map +1 -0
  39. package/dist/_chunks/tracing-C8V-YGsP.js +329 -0
  40. package/dist/_chunks/tracing-C8V-YGsP.js.map +1 -0
  41. package/dist/_chunks/{use-query-states-DAhgj8Gx.js → use-query-states-B2XTqxDR.js} +7 -20
  42. package/dist/_chunks/use-query-states-B2XTqxDR.js.map +1 -0
  43. package/dist/_chunks/use-segment-params-BkpKAQ7D.js +185 -0
  44. package/dist/_chunks/use-segment-params-BkpKAQ7D.js.map +1 -0
  45. package/dist/_chunks/walkers-Tg0Alwcg.js +850 -0
  46. package/dist/_chunks/walkers-Tg0Alwcg.js.map +1 -0
  47. package/dist/_chunks/{format-DNt20Kt8.js → warnings-Cg47l5sk.js} +7 -45
  48. package/dist/_chunks/warnings-Cg47l5sk.js.map +1 -0
  49. package/dist/adapters/build-output-helper.d.ts +28 -0
  50. package/dist/adapters/build-output-helper.d.ts.map +1 -0
  51. package/dist/adapters/cloudflare-dev.d.ts +109 -0
  52. package/dist/adapters/cloudflare-dev.d.ts.map +1 -0
  53. package/dist/adapters/cloudflare-dev.js +73 -0
  54. package/dist/adapters/cloudflare-dev.js.map +1 -0
  55. package/dist/adapters/cloudflare-kv-cache.d.ts +64 -0
  56. package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -0
  57. package/dist/adapters/cloudflare-kv-cache.js +95 -0
  58. package/dist/adapters/cloudflare-kv-cache.js.map +1 -0
  59. package/dist/adapters/cloudflare.d.ts +148 -12
  60. package/dist/adapters/cloudflare.d.ts.map +1 -1
  61. package/dist/adapters/cloudflare.js +142 -38
  62. package/dist/adapters/cloudflare.js.map +1 -1
  63. package/dist/adapters/compress-module.d.ts.map +1 -1
  64. package/dist/adapters/nitro.d.ts +17 -1
  65. package/dist/adapters/nitro.d.ts.map +1 -1
  66. package/dist/adapters/nitro.js +143 -45
  67. package/dist/adapters/nitro.js.map +1 -1
  68. package/dist/adapters/shared.d.ts +16 -0
  69. package/dist/adapters/shared.d.ts.map +1 -0
  70. package/dist/cache/cache-api.d.ts +24 -0
  71. package/dist/cache/cache-api.d.ts.map +1 -0
  72. package/dist/cache/fast-hash.d.ts +22 -0
  73. package/dist/cache/fast-hash.d.ts.map +1 -0
  74. package/dist/cache/handler-store.d.ts +31 -0
  75. package/dist/cache/handler-store.d.ts.map +1 -0
  76. package/dist/cache/index.d.ts +23 -7
  77. package/dist/cache/index.d.ts.map +1 -1
  78. package/dist/cache/index.js +195 -84
  79. package/dist/cache/index.js.map +1 -1
  80. package/dist/cache/singleflight.d.ts +18 -1
  81. package/dist/cache/singleflight.d.ts.map +1 -1
  82. package/dist/cache/sizeof.d.ts +22 -0
  83. package/dist/cache/sizeof.d.ts.map +1 -0
  84. package/dist/cache/timber-cache.d.ts +1 -1
  85. package/dist/cache/timber-cache.d.ts.map +1 -1
  86. package/dist/cli.d.ts +6 -1
  87. package/dist/cli.d.ts.map +1 -1
  88. package/dist/cli.js +8 -3
  89. package/dist/cli.js.map +1 -1
  90. package/dist/client/browser-dev.d.ts +34 -1
  91. package/dist/client/browser-dev.d.ts.map +1 -1
  92. package/dist/client/browser-entry/action-dispatch.d.ts +17 -0
  93. package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -0
  94. package/dist/client/browser-entry/hmr.d.ts +21 -0
  95. package/dist/client/browser-entry/hmr.d.ts.map +1 -0
  96. package/dist/client/browser-entry/hydrate.d.ts +46 -0
  97. package/dist/client/browser-entry/hydrate.d.ts.map +1 -0
  98. package/dist/client/browser-entry/index.d.ts +30 -0
  99. package/dist/client/browser-entry/index.d.ts.map +1 -0
  100. package/dist/client/browser-entry/post-hydration.d.ts +26 -0
  101. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -0
  102. package/dist/client/browser-entry/router-init.d.ts +23 -0
  103. package/dist/client/browser-entry/router-init.d.ts.map +1 -0
  104. package/dist/client/browser-entry/rsc-stream.d.ts +24 -0
  105. package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -0
  106. package/dist/client/browser-entry/scroll.d.ts +19 -0
  107. package/dist/client/browser-entry/scroll.d.ts.map +1 -0
  108. package/dist/client/error-boundary.d.ts +12 -5
  109. package/dist/client/error-boundary.d.ts.map +1 -1
  110. package/dist/client/error-boundary.js +26 -5
  111. package/dist/client/error-boundary.js.map +1 -1
  112. package/dist/client/error-reconstituter.d.ts +54 -0
  113. package/dist/client/error-reconstituter.d.ts.map +1 -0
  114. package/dist/client/form.d.ts +16 -27
  115. package/dist/client/form.d.ts.map +1 -1
  116. package/dist/client/history.d.ts +19 -4
  117. package/dist/client/history.d.ts.map +1 -1
  118. package/dist/client/index.d.ts +9 -24
  119. package/dist/client/index.d.ts.map +1 -1
  120. package/dist/client/index.js +312 -1109
  121. package/dist/client/index.js.map +1 -1
  122. package/dist/client/internal.d.ts +19 -0
  123. package/dist/client/internal.d.ts.map +1 -0
  124. package/dist/client/internal.js +964 -0
  125. package/dist/client/internal.js.map +1 -0
  126. package/dist/client/link-pending-store.d.ts +63 -0
  127. package/dist/client/link-pending-store.d.ts.map +1 -0
  128. package/dist/client/link.d.ts +73 -63
  129. package/dist/client/link.d.ts.map +1 -1
  130. package/dist/client/nav-link-store.d.ts +36 -0
  131. package/dist/client/nav-link-store.d.ts.map +1 -0
  132. package/dist/client/navigation-api-types.d.ts +90 -0
  133. package/dist/client/navigation-api-types.d.ts.map +1 -0
  134. package/dist/client/navigation-api.d.ts +115 -0
  135. package/dist/client/navigation-api.d.ts.map +1 -0
  136. package/dist/client/navigation-context.d.ts +22 -5
  137. package/dist/client/navigation-context.d.ts.map +1 -1
  138. package/dist/client/{transition-root.d.ts → navigation-root.d.ts} +42 -8
  139. package/dist/client/navigation-root.d.ts.map +1 -0
  140. package/dist/client/nuqs-adapter.d.ts.map +1 -1
  141. package/dist/client/router-ref.d.ts +1 -1
  142. package/dist/client/router.d.ts +70 -4
  143. package/dist/client/router.d.ts.map +1 -1
  144. package/dist/client/rsc-fetch.d.ts +38 -3
  145. package/dist/client/rsc-fetch.d.ts.map +1 -1
  146. package/dist/client/segment-cache.d.ts +1 -1
  147. package/dist/client/segment-cache.d.ts.map +1 -1
  148. package/dist/client/segment-context.d.ts +1 -1
  149. package/dist/client/segment-context.d.ts.map +1 -1
  150. package/dist/client/segment-merger.d.ts.map +1 -1
  151. package/dist/client/segment-outlet.d.ts +63 -0
  152. package/dist/client/segment-outlet.d.ts.map +1 -0
  153. package/dist/client/ssr-data.d.ts +13 -4
  154. package/dist/client/ssr-data.d.ts.map +1 -1
  155. package/dist/client/stale-reload.d.ts +15 -0
  156. package/dist/client/stale-reload.d.ts.map +1 -1
  157. package/dist/client/state.d.ts +1 -1
  158. package/dist/client/top-loader.d.ts +5 -5
  159. package/dist/client/top-loader.d.ts.map +1 -1
  160. package/dist/client/use-cookie.d.ts +8 -0
  161. package/dist/client/use-cookie.d.ts.map +1 -1
  162. package/dist/client/use-link-status.d.ts +5 -5
  163. package/dist/client/use-link-status.d.ts.map +1 -1
  164. package/dist/client/{use-navigation-pending.d.ts → use-pending-navigation.d.ts} +4 -4
  165. package/dist/client/use-pending-navigation.d.ts.map +1 -0
  166. package/dist/client/use-query-states.d.ts +1 -1
  167. package/dist/client/use-query-states.d.ts.map +1 -1
  168. package/dist/client/use-router.d.ts +1 -1
  169. package/dist/client/{use-params.d.ts → use-segment-params.d.ts} +7 -5
  170. package/dist/client/use-segment-params.d.ts.map +1 -0
  171. package/dist/codec.d.ts +33 -0
  172. package/dist/codec.d.ts.map +1 -0
  173. package/dist/codec.js +2 -0
  174. package/dist/config-types.d.ts +294 -0
  175. package/dist/config-types.d.ts.map +1 -0
  176. package/dist/config-validation.d.ts +51 -0
  177. package/dist/config-validation.d.ts.map +1 -0
  178. package/dist/content/index.d.ts +1 -10
  179. package/dist/content/index.d.ts.map +1 -1
  180. package/dist/content/index.js +0 -2
  181. package/dist/cookies/define-cookie.d.ts +98 -25
  182. package/dist/cookies/define-cookie.d.ts.map +1 -1
  183. package/dist/cookies/index.d.ts +2 -1
  184. package/dist/cookies/index.d.ts.map +1 -1
  185. package/dist/cookies/index.js +36 -72
  186. package/dist/cookies/index.js.map +1 -1
  187. package/dist/cookies/json-cookie.d.ts +64 -0
  188. package/dist/cookies/json-cookie.d.ts.map +1 -0
  189. package/dist/cookies/validation.d.ts +46 -0
  190. package/dist/cookies/validation.d.ts.map +1 -0
  191. package/dist/dev-tools/404-page.d.ts +46 -0
  192. package/dist/dev-tools/404-page.d.ts.map +1 -0
  193. package/dist/dev-tools/browser-logs.d.ts +84 -0
  194. package/dist/dev-tools/browser-logs.d.ts.map +1 -0
  195. package/dist/dev-tools/error-page.d.ts +58 -0
  196. package/dist/dev-tools/error-page.d.ts.map +1 -0
  197. package/dist/dev-tools/holding-server.d.ts +54 -0
  198. package/dist/dev-tools/holding-server.d.ts.map +1 -0
  199. package/dist/dev-tools/index.d.ts +31 -0
  200. package/dist/dev-tools/index.d.ts.map +1 -0
  201. package/dist/{server/dev-span-processor.d.ts → dev-tools/instrumentation.d.ts} +26 -6
  202. package/dist/dev-tools/instrumentation.d.ts.map +1 -0
  203. package/dist/{server/dev-logger.d.ts → dev-tools/logger.d.ts} +1 -1
  204. package/dist/dev-tools/logger.d.ts.map +1 -0
  205. package/dist/{plugins/dev-logs.d.ts → dev-tools/logs.d.ts} +2 -2
  206. package/dist/dev-tools/logs.d.ts.map +1 -0
  207. package/dist/dev-tools/overlay.d.ts +91 -0
  208. package/dist/dev-tools/overlay.d.ts.map +1 -0
  209. package/dist/dev-tools/stack-classifier.d.ts +34 -0
  210. package/dist/dev-tools/stack-classifier.d.ts.map +1 -0
  211. package/dist/dev-tools/terminal.d.ts +28 -0
  212. package/dist/dev-tools/terminal.d.ts.map +1 -0
  213. package/dist/{server/dev-warnings.d.ts → dev-tools/warnings.d.ts} +2 -22
  214. package/dist/dev-tools/warnings.d.ts.map +1 -0
  215. package/dist/fonts/bundle.d.ts +48 -0
  216. package/dist/fonts/bundle.d.ts.map +1 -0
  217. package/dist/fonts/css.d.ts +1 -0
  218. package/dist/fonts/css.d.ts.map +1 -1
  219. package/dist/fonts/dev-middleware.d.ts +22 -0
  220. package/dist/fonts/dev-middleware.d.ts.map +1 -0
  221. package/dist/fonts/local.d.ts +4 -2
  222. package/dist/fonts/local.d.ts.map +1 -1
  223. package/dist/fonts/pipeline.d.ts +167 -0
  224. package/dist/fonts/pipeline.d.ts.map +1 -0
  225. package/dist/fonts/transform.d.ts +64 -0
  226. package/dist/fonts/transform.d.ts.map +1 -0
  227. package/dist/fonts/types.d.ts +45 -1
  228. package/dist/fonts/types.d.ts.map +1 -1
  229. package/dist/fonts/virtual-modules.d.ts +103 -0
  230. package/dist/fonts/virtual-modules.d.ts.map +1 -0
  231. package/dist/index.d.ts +46 -152
  232. package/dist/index.d.ts.map +1 -1
  233. package/dist/index.js +14702 -12350
  234. package/dist/index.js.map +1 -1
  235. package/dist/plugin-context.d.ts +107 -0
  236. package/dist/plugin-context.d.ts.map +1 -0
  237. package/dist/plugins/adapter-build.d.ts +1 -1
  238. package/dist/plugins/adapter-build.d.ts.map +1 -1
  239. package/dist/plugins/build-manifest.d.ts +2 -2
  240. package/dist/plugins/build-manifest.d.ts.map +1 -1
  241. package/dist/plugins/build-report.d.ts +16 -4
  242. package/dist/plugins/build-report.d.ts.map +1 -1
  243. package/dist/plugins/chunks.d.ts +9 -1
  244. package/dist/plugins/chunks.d.ts.map +1 -1
  245. package/dist/plugins/client-chunks.d.ts +32 -0
  246. package/dist/plugins/client-chunks.d.ts.map +1 -0
  247. package/dist/plugins/content.d.ts +1 -1
  248. package/dist/plugins/content.d.ts.map +1 -1
  249. package/dist/plugins/dev-server.d.ts +1 -1
  250. package/dist/plugins/dev-server.d.ts.map +1 -1
  251. package/dist/plugins/entries.d.ts +8 -1
  252. package/dist/plugins/entries.d.ts.map +1 -1
  253. package/dist/plugins/fonts.d.ts +17 -65
  254. package/dist/plugins/fonts.d.ts.map +1 -1
  255. package/dist/plugins/mdx.d.ts +7 -1
  256. package/dist/plugins/mdx.d.ts.map +1 -1
  257. package/dist/plugins/routing.d.ts +1 -1
  258. package/dist/plugins/routing.d.ts.map +1 -1
  259. package/dist/plugins/server-bundle.d.ts.map +1 -1
  260. package/dist/plugins/shims.d.ts +6 -5
  261. package/dist/plugins/shims.d.ts.map +1 -1
  262. package/dist/plugins/static-build.d.ts +4 -4
  263. package/dist/plugins/static-build.d.ts.map +1 -1
  264. package/dist/routing/codegen-shared.d.ts +55 -0
  265. package/dist/routing/codegen-shared.d.ts.map +1 -0
  266. package/dist/routing/codegen-types.d.ts +52 -0
  267. package/dist/routing/codegen-types.d.ts.map +1 -0
  268. package/dist/routing/codegen.d.ts +2 -2
  269. package/dist/routing/codegen.d.ts.map +1 -1
  270. package/dist/routing/convention-lint.d.ts +41 -0
  271. package/dist/routing/convention-lint.d.ts.map +1 -0
  272. package/dist/routing/index.d.ts +5 -1
  273. package/dist/routing/index.d.ts.map +1 -1
  274. package/dist/routing/index.js +3 -2
  275. package/dist/routing/link-codegen.d.ts +90 -0
  276. package/dist/routing/link-codegen.d.ts.map +1 -0
  277. package/dist/routing/scanner.d.ts +1 -10
  278. package/dist/routing/scanner.d.ts.map +1 -1
  279. package/dist/routing/segment-classify.d.ts +75 -0
  280. package/dist/routing/segment-classify.d.ts.map +1 -0
  281. package/dist/routing/status-file-lint.d.ts +2 -1
  282. package/dist/routing/status-file-lint.d.ts.map +1 -1
  283. package/dist/routing/types.d.ts +77 -25
  284. package/dist/routing/types.d.ts.map +1 -1
  285. package/dist/routing/walkers.d.ts +51 -0
  286. package/dist/routing/walkers.d.ts.map +1 -0
  287. package/dist/rsc-runtime/rsc.d.ts +1 -1
  288. package/dist/rsc-runtime/rsc.d.ts.map +1 -1
  289. package/dist/rsc-runtime/ssr.d.ts +12 -0
  290. package/dist/rsc-runtime/ssr.d.ts.map +1 -1
  291. package/dist/schema-bridge.d.ts +76 -0
  292. package/dist/schema-bridge.d.ts.map +1 -0
  293. package/dist/search-params/define.d.ts +157 -0
  294. package/dist/search-params/define.d.ts.map +1 -0
  295. package/dist/search-params/index.d.ts +4 -7
  296. package/dist/search-params/index.d.ts.map +1 -1
  297. package/dist/search-params/index.js +34 -441
  298. package/dist/search-params/index.js.map +1 -1
  299. package/dist/search-params/registry.d.ts +2 -2
  300. package/dist/search-params/registry.d.ts.map +1 -1
  301. package/dist/search-params/wrappers.d.ts +53 -0
  302. package/dist/search-params/wrappers.d.ts.map +1 -0
  303. package/dist/segment-params/define.d.ts +95 -0
  304. package/dist/segment-params/define.d.ts.map +1 -0
  305. package/dist/segment-params/index.d.ts +3 -0
  306. package/dist/segment-params/index.d.ts.map +1 -0
  307. package/dist/segment-params/index.js +2 -0
  308. package/dist/server/access-gate.d.ts +8 -3
  309. package/dist/server/access-gate.d.ts.map +1 -1
  310. package/dist/server/action-client.d.ts +41 -6
  311. package/dist/server/action-client.d.ts.map +1 -1
  312. package/dist/server/action-encryption.d.ts +76 -0
  313. package/dist/server/action-encryption.d.ts.map +1 -0
  314. package/dist/server/action-handler.d.ts +35 -1
  315. package/dist/server/action-handler.d.ts.map +1 -1
  316. package/dist/server/actions.d.ts +3 -6
  317. package/dist/server/actions.d.ts.map +1 -1
  318. package/dist/server/als-registry.d.ts +33 -5
  319. package/dist/server/als-registry.d.ts.map +1 -1
  320. package/dist/server/asset-headers.d.ts +1 -15
  321. package/dist/server/asset-headers.d.ts.map +1 -1
  322. package/dist/server/build-manifest.d.ts +2 -2
  323. package/dist/server/build-manifest.d.ts.map +1 -1
  324. package/dist/server/cookie-context.d.ts +170 -0
  325. package/dist/server/cookie-context.d.ts.map +1 -0
  326. package/dist/server/cookie-parsing.d.ts +51 -0
  327. package/dist/server/cookie-parsing.d.ts.map +1 -0
  328. package/dist/server/debug.d.ts +82 -0
  329. package/dist/server/debug.d.ts.map +1 -0
  330. package/dist/server/default-logger.d.ts +22 -0
  331. package/dist/server/default-logger.d.ts.map +1 -0
  332. package/dist/server/deny-boundary.d.ts +90 -0
  333. package/dist/server/deny-boundary.d.ts.map +1 -0
  334. package/dist/server/deny-renderer.d.ts.map +1 -1
  335. package/dist/server/dev-source-map.d.ts +22 -0
  336. package/dist/server/dev-source-map.d.ts.map +1 -0
  337. package/dist/server/early-hints-sender.d.ts.map +1 -1
  338. package/dist/server/early-hints.d.ts +13 -5
  339. package/dist/server/early-hints.d.ts.map +1 -1
  340. package/dist/server/error-boundary-wrapper.d.ts +7 -1
  341. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  342. package/dist/server/fallback-error.d.ts +12 -7
  343. package/dist/server/fallback-error.d.ts.map +1 -1
  344. package/dist/server/flight-injection-state.d.ts +66 -0
  345. package/dist/server/flight-injection-state.d.ts.map +1 -0
  346. package/dist/server/flight-scripts.d.ts +42 -0
  347. package/dist/server/flight-scripts.d.ts.map +1 -0
  348. package/dist/server/flush.d.ts.map +1 -1
  349. package/dist/server/form-data.d.ts +29 -0
  350. package/dist/server/form-data.d.ts.map +1 -1
  351. package/dist/server/html-injector-core.d.ts +212 -0
  352. package/dist/server/html-injector-core.d.ts.map +1 -0
  353. package/dist/server/html-injectors.d.ts +81 -41
  354. package/dist/server/html-injectors.d.ts.map +1 -1
  355. package/dist/server/index.d.ts +9 -46
  356. package/dist/server/index.d.ts.map +1 -1
  357. package/dist/server/index.js +158 -2899
  358. package/dist/server/index.js.map +1 -1
  359. package/dist/server/internal.d.ts +48 -0
  360. package/dist/server/internal.d.ts.map +1 -0
  361. package/dist/server/internal.js +2900 -0
  362. package/dist/server/internal.js.map +1 -0
  363. package/dist/server/logger.d.ts +39 -7
  364. package/dist/server/logger.d.ts.map +1 -1
  365. package/dist/server/middleware-runner.d.ts +36 -4
  366. package/dist/server/middleware-runner.d.ts.map +1 -1
  367. package/dist/server/node-stream-transforms.d.ts +110 -0
  368. package/dist/server/node-stream-transforms.d.ts.map +1 -0
  369. package/dist/server/param-coercion.d.ts +26 -0
  370. package/dist/server/param-coercion.d.ts.map +1 -0
  371. package/dist/server/pipeline-helpers.d.ts +95 -0
  372. package/dist/server/pipeline-helpers.d.ts.map +1 -0
  373. package/dist/server/pipeline-interception.d.ts +1 -1
  374. package/dist/server/pipeline-interception.d.ts.map +1 -1
  375. package/dist/server/pipeline-metadata.d.ts +6 -0
  376. package/dist/server/pipeline-metadata.d.ts.map +1 -1
  377. package/dist/server/pipeline-outcome.d.ts +49 -0
  378. package/dist/server/pipeline-outcome.d.ts.map +1 -0
  379. package/dist/server/pipeline-phases.d.ts +52 -0
  380. package/dist/server/pipeline-phases.d.ts.map +1 -0
  381. package/dist/server/pipeline.d.ts +93 -23
  382. package/dist/server/pipeline.d.ts.map +1 -1
  383. package/dist/server/port-resolution.d.ts +117 -0
  384. package/dist/server/port-resolution.d.ts.map +1 -0
  385. package/dist/server/primitives.d.ts +69 -18
  386. package/dist/server/primitives.d.ts.map +1 -1
  387. package/dist/server/render-timeout.d.ts +51 -0
  388. package/dist/server/render-timeout.d.ts.map +1 -0
  389. package/dist/server/request-context.d.ts +41 -109
  390. package/dist/server/request-context.d.ts.map +1 -1
  391. package/dist/server/route-element-builder.d.ts +27 -1
  392. package/dist/server/route-element-builder.d.ts.map +1 -1
  393. package/dist/server/route-handler.d.ts.map +1 -1
  394. package/dist/server/route-matcher.d.ts +21 -34
  395. package/dist/server/route-matcher.d.ts.map +1 -1
  396. package/dist/server/rsc-entry/action-middleware-runner.d.ts +66 -0
  397. package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -0
  398. package/dist/server/rsc-entry/api-handler.d.ts +2 -2
  399. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
  400. package/dist/server/rsc-entry/error-renderer.d.ts +26 -13
  401. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  402. package/dist/server/rsc-entry/helpers.d.ts +49 -6
  403. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  404. package/dist/server/rsc-entry/index.d.ts +20 -3
  405. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  406. package/dist/server/rsc-entry/render-route.d.ts +50 -0
  407. package/dist/server/rsc-entry/render-route.d.ts.map +1 -0
  408. package/dist/server/rsc-entry/rsc-payload.d.ts +3 -3
  409. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  410. package/dist/server/rsc-entry/rsc-stream.d.ts +20 -1
  411. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  412. package/dist/server/rsc-entry/ssr-bridge.d.ts +1 -1
  413. package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
  414. package/dist/server/rsc-entry/ssr-renderer.d.ts +19 -4
  415. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  416. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +119 -0
  417. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -0
  418. package/dist/server/rsc-prop-warnings.d.ts.map +1 -1
  419. package/dist/server/safe-load.d.ts +46 -0
  420. package/dist/server/safe-load.d.ts.map +1 -0
  421. package/dist/server/sensitive-fields.d.ts +74 -0
  422. package/dist/server/sensitive-fields.d.ts.map +1 -0
  423. package/dist/server/sitemap-generator.d.ts +129 -0
  424. package/dist/server/sitemap-generator.d.ts.map +1 -0
  425. package/dist/server/sitemap-handler.d.ts +22 -0
  426. package/dist/server/sitemap-handler.d.ts.map +1 -0
  427. package/dist/server/slot-resolver.d.ts +1 -1
  428. package/dist/server/slot-resolver.d.ts.map +1 -1
  429. package/dist/server/ssr-entry.d.ts +23 -0
  430. package/dist/server/ssr-entry.d.ts.map +1 -1
  431. package/dist/server/ssr-render.d.ts +39 -21
  432. package/dist/server/ssr-render.d.ts.map +1 -1
  433. package/dist/server/ssr-wrappers.d.ts +50 -0
  434. package/dist/server/ssr-wrappers.d.ts.map +1 -0
  435. package/dist/server/state-tree-diff.d.ts.map +1 -1
  436. package/dist/server/status-code-resolver.d.ts +16 -11
  437. package/dist/server/status-code-resolver.d.ts.map +1 -1
  438. package/dist/server/stream-utils.d.ts +36 -0
  439. package/dist/server/stream-utils.d.ts.map +1 -0
  440. package/dist/server/tracing.d.ts +15 -5
  441. package/dist/server/tracing.d.ts.map +1 -1
  442. package/dist/server/tree-builder.d.ts +62 -30
  443. package/dist/server/tree-builder.d.ts.map +1 -1
  444. package/dist/server/types.d.ts +49 -4
  445. package/dist/server/types.d.ts.map +1 -1
  446. package/dist/server/utils/escape-html.d.ts +14 -0
  447. package/dist/server/utils/escape-html.d.ts.map +1 -0
  448. package/dist/server/version-skew.d.ts +61 -0
  449. package/dist/server/version-skew.d.ts.map +1 -0
  450. package/dist/server/waituntil-bridge.d.ts.map +1 -1
  451. package/dist/shared/merge-search-params.d.ts +22 -0
  452. package/dist/shared/merge-search-params.d.ts.map +1 -0
  453. package/dist/shims/font-google.d.ts +1 -1
  454. package/dist/shims/font-google.d.ts.map +1 -1
  455. package/dist/shims/font-google.js +42 -0
  456. package/dist/shims/font-google.js.map +1 -0
  457. package/dist/shims/font-local.d.ts +26 -0
  458. package/dist/shims/font-local.d.ts.map +1 -0
  459. package/dist/shims/font-local.js +20 -0
  460. package/dist/shims/font-local.js.map +1 -0
  461. package/dist/shims/headers.d.ts +2 -1
  462. package/dist/shims/headers.d.ts.map +1 -1
  463. package/dist/shims/navigation-client.d.ts +3 -1
  464. package/dist/shims/navigation-client.d.ts.map +1 -1
  465. package/dist/shims/navigation.d.ts +11 -5
  466. package/dist/shims/navigation.d.ts.map +1 -1
  467. package/dist/utils/directive-parser.d.ts +1 -43
  468. package/dist/utils/directive-parser.d.ts.map +1 -1
  469. package/dist/utils/state-machine.d.ts +80 -0
  470. package/dist/utils/state-machine.d.ts.map +1 -0
  471. package/package.json +56 -22
  472. package/src/adapters/build-output-helper.ts +77 -0
  473. package/src/adapters/cloudflare-dev.ts +177 -0
  474. package/src/adapters/cloudflare-kv-cache.ts +142 -0
  475. package/src/adapters/cloudflare.ts +350 -76
  476. package/src/adapters/compress-module.ts +24 -4
  477. package/src/adapters/nitro.ts +148 -60
  478. package/src/adapters/shared.ts +40 -0
  479. package/src/adapters/wrangler.d.ts +7 -0
  480. package/src/cache/cache-api.ts +38 -0
  481. package/src/cache/fast-hash.ts +34 -0
  482. package/src/cache/handler-store.ts +68 -0
  483. package/src/cache/index.ts +81 -18
  484. package/src/cache/singleflight.ts +62 -4
  485. package/src/cache/sizeof.ts +31 -0
  486. package/src/cache/timber-cache.ts +41 -29
  487. package/src/cli.ts +16 -6
  488. package/src/client/browser-dev.ts +153 -1
  489. package/src/client/browser-entry/action-dispatch.ts +116 -0
  490. package/src/client/browser-entry/hmr.ts +81 -0
  491. package/src/client/browser-entry/hydrate.ts +145 -0
  492. package/src/client/browser-entry/index.ts +143 -0
  493. package/src/client/browser-entry/post-hydration.ts +119 -0
  494. package/src/client/browser-entry/router-init.ts +193 -0
  495. package/src/client/browser-entry/rsc-stream.ts +157 -0
  496. package/src/client/browser-entry/scroll.ts +27 -0
  497. package/src/client/error-boundary.tsx +97 -20
  498. package/src/client/error-reconstituter.tsx +65 -0
  499. package/src/client/form.tsx +31 -32
  500. package/src/client/history.ts +26 -4
  501. package/src/client/index.ts +77 -43
  502. package/src/client/internal.ts +58 -0
  503. package/src/client/link-pending-store.ts +111 -0
  504. package/src/client/link.tsx +432 -140
  505. package/src/client/nav-link-store.ts +47 -0
  506. package/src/client/navigation-api-types.ts +112 -0
  507. package/src/client/navigation-api.ts +332 -0
  508. package/src/client/navigation-context.ts +83 -18
  509. package/src/client/navigation-root.tsx +342 -0
  510. package/src/client/nuqs-adapter.tsx +16 -3
  511. package/src/client/router-ref.ts +1 -1
  512. package/src/client/router.ts +303 -78
  513. package/src/client/rsc-fetch.ts +112 -8
  514. package/src/client/segment-cache.ts +1 -1
  515. package/src/client/segment-context.ts +6 -1
  516. package/src/client/segment-merger.ts +2 -8
  517. package/src/client/segment-outlet.tsx +86 -0
  518. package/src/client/ssr-data.ts +13 -5
  519. package/src/client/stale-reload.ts +74 -7
  520. package/src/client/state.ts +2 -2
  521. package/src/client/top-loader.tsx +41 -25
  522. package/src/client/use-cookie.ts +29 -0
  523. package/src/client/use-link-status.ts +7 -7
  524. package/src/client/{use-navigation-pending.ts → use-pending-navigation.ts} +6 -6
  525. package/src/client/use-query-states.ts +9 -3
  526. package/src/client/use-router.ts +1 -1
  527. package/src/client/{use-params.ts → use-segment-params.ts} +7 -5
  528. package/src/codec.ts +45 -0
  529. package/src/config-types.ts +292 -0
  530. package/src/config-validation.ts +303 -0
  531. package/src/content/index.ts +5 -13
  532. package/src/cookies/define-cookie.ts +301 -55
  533. package/src/cookies/index.ts +12 -1
  534. package/src/cookies/json-cookie.ts +105 -0
  535. package/src/cookies/validation.ts +134 -0
  536. package/src/dev-tools/404-page.ts +387 -0
  537. package/src/dev-tools/browser-logs.ts +288 -0
  538. package/src/dev-tools/error-page.ts +509 -0
  539. package/src/dev-tools/holding-server.ts +187 -0
  540. package/src/dev-tools/index.ts +90 -0
  541. package/src/dev-tools/instrumentation.ts +176 -0
  542. package/src/{plugins/dev-logs.ts → dev-tools/logs.ts} +3 -3
  543. package/src/dev-tools/overlay.ts +456 -0
  544. package/src/dev-tools/stack-classifier.ts +75 -0
  545. package/src/dev-tools/terminal.ts +183 -0
  546. package/src/{server/dev-warnings.ts → dev-tools/warnings.ts} +6 -50
  547. package/src/fonts/bundle.ts +142 -0
  548. package/src/fonts/css.ts +2 -1
  549. package/src/fonts/dev-middleware.ts +74 -0
  550. package/src/fonts/local.ts +7 -3
  551. package/src/fonts/pipeline.ts +314 -0
  552. package/src/fonts/transform.ts +406 -0
  553. package/src/fonts/types.ts +50 -1
  554. package/src/fonts/virtual-modules.ts +227 -0
  555. package/src/index.ts +376 -315
  556. package/src/plugin-context.ts +240 -0
  557. package/src/plugins/adapter-build.ts +12 -4
  558. package/src/plugins/build-manifest.ts +13 -2
  559. package/src/plugins/build-report.ts +32 -21
  560. package/src/plugins/chunks.ts +9 -1
  561. package/src/plugins/client-chunks.ts +65 -0
  562. package/src/plugins/content.ts +1 -1
  563. package/src/plugins/dev-server.ts +154 -21
  564. package/src/plugins/entries.ts +119 -12
  565. package/src/plugins/fonts.ts +177 -524
  566. package/src/plugins/mdx.ts +52 -15
  567. package/src/plugins/routing.ts +225 -41
  568. package/src/plugins/server-action-exports.ts +1 -1
  569. package/src/plugins/server-bundle.ts +36 -1
  570. package/src/plugins/shims.ts +136 -35
  571. package/src/plugins/static-build.ts +17 -11
  572. package/src/routing/codegen-shared.ts +98 -0
  573. package/src/routing/codegen-types.ts +53 -0
  574. package/src/routing/codegen.ts +143 -176
  575. package/src/routing/convention-lint.ts +357 -0
  576. package/src/routing/index.ts +5 -1
  577. package/src/routing/interception.ts +1 -1
  578. package/src/routing/link-codegen.ts +294 -0
  579. package/src/routing/scanner.ts +107 -110
  580. package/src/routing/segment-classify.ts +188 -0
  581. package/src/routing/status-file-lint.ts +10 -7
  582. package/src/routing/types.ts +78 -25
  583. package/src/routing/walkers.ts +90 -0
  584. package/src/rsc-runtime/rsc.ts +2 -0
  585. package/src/rsc-runtime/ssr.ts +50 -0
  586. package/src/rsc-runtime/vendor-types.d.ts +7 -0
  587. package/src/{search-params/codecs.ts → schema-bridge.ts} +57 -20
  588. package/src/search-params/define.ts +538 -0
  589. package/src/search-params/index.ts +14 -20
  590. package/src/search-params/registry.ts +2 -2
  591. package/src/search-params/wrappers.ts +92 -0
  592. package/src/segment-params/define.ts +332 -0
  593. package/src/segment-params/index.ts +9 -0
  594. package/src/server/access-gate.tsx +79 -36
  595. package/src/server/action-client.ts +103 -20
  596. package/src/server/action-encryption.ts +144 -0
  597. package/src/server/action-handler.ts +112 -14
  598. package/src/server/actions.ts +10 -9
  599. package/src/server/als-registry.ts +35 -7
  600. package/src/server/asset-headers.ts +8 -34
  601. package/src/server/build-manifest.ts +10 -4
  602. package/src/server/compress.ts +25 -7
  603. package/src/server/cookie-context.ts +468 -0
  604. package/src/server/cookie-parsing.ts +135 -0
  605. package/src/server/debug.ts +137 -0
  606. package/src/server/default-logger.ts +99 -0
  607. package/src/server/deny-boundary.ts +218 -0
  608. package/src/server/deny-renderer.ts +33 -51
  609. package/src/server/dev-source-map.ts +31 -0
  610. package/src/server/early-hints-sender.ts +3 -2
  611. package/src/server/early-hints.ts +36 -15
  612. package/src/server/error-boundary-wrapper.ts +74 -22
  613. package/src/server/fallback-error.ts +74 -102
  614. package/src/server/flight-injection-state.ts +113 -0
  615. package/src/server/flight-scripts.ts +62 -0
  616. package/src/server/flush.ts +2 -1
  617. package/src/server/form-data.ts +76 -0
  618. package/src/server/html-injector-core.ts +403 -0
  619. package/src/server/html-injectors.ts +244 -223
  620. package/src/server/index.ts +31 -184
  621. package/src/server/internal.ts +176 -0
  622. package/src/server/logger.ts +67 -35
  623. package/src/server/middleware-runner.ts +75 -4
  624. package/src/server/node-stream-transforms.ts +369 -0
  625. package/src/server/param-coercion.ts +76 -0
  626. package/src/server/pipeline-helpers.ts +204 -0
  627. package/src/server/pipeline-interception.ts +17 -16
  628. package/src/server/pipeline-metadata.ts +13 -0
  629. package/src/server/pipeline-outcome.ts +167 -0
  630. package/src/server/pipeline-phases.ts +409 -0
  631. package/src/server/pipeline.ts +128 -387
  632. package/src/server/port-resolution.ts +215 -0
  633. package/src/server/primitives.ts +113 -29
  634. package/src/server/render-timeout.ts +108 -0
  635. package/src/server/request-context.ts +94 -337
  636. package/src/server/route-element-builder.ts +289 -198
  637. package/src/server/route-handler.ts +24 -4
  638. package/src/server/route-matcher.ts +44 -65
  639. package/src/server/rsc-entry/action-middleware-runner.ts +167 -0
  640. package/src/server/rsc-entry/api-handler.ts +16 -17
  641. package/src/server/rsc-entry/error-renderer.ts +305 -89
  642. package/src/server/rsc-entry/helpers.ts +136 -12
  643. package/src/server/rsc-entry/index.ts +330 -312
  644. package/src/server/rsc-entry/render-route.ts +304 -0
  645. package/src/server/rsc-entry/rsc-payload.ts +100 -18
  646. package/src/server/rsc-entry/rsc-stream.ts +90 -17
  647. package/src/server/rsc-entry/ssr-bridge.ts +14 -5
  648. package/src/server/rsc-entry/ssr-renderer.ts +177 -37
  649. package/src/server/rsc-entry/wrap-action-dispatch.ts +449 -0
  650. package/src/server/rsc-prop-warnings.ts +3 -1
  651. package/src/server/safe-load.ts +60 -0
  652. package/src/server/sensitive-fields.ts +230 -0
  653. package/src/server/sitemap-generator.ts +338 -0
  654. package/src/server/sitemap-handler.ts +126 -0
  655. package/src/server/slot-resolver.ts +245 -230
  656. package/src/server/ssr-entry.ts +215 -32
  657. package/src/server/ssr-render.ts +289 -67
  658. package/src/server/ssr-wrappers.tsx +139 -0
  659. package/src/server/state-tree-diff.ts +4 -1
  660. package/src/server/status-code-resolver.ts +112 -128
  661. package/src/server/stream-utils.ts +213 -0
  662. package/src/server/tracing.ts +44 -10
  663. package/src/server/tree-builder.ts +202 -90
  664. package/src/server/types.ts +55 -6
  665. package/src/server/utils/escape-html.ts +20 -0
  666. package/src/server/version-skew.ts +104 -0
  667. package/src/server/waituntil-bridge.ts +4 -1
  668. package/src/shared/merge-search-params.ts +55 -0
  669. package/src/shims/font-google.ts +1 -1
  670. package/src/shims/font-local.ts +34 -0
  671. package/src/shims/headers.ts +5 -1
  672. package/src/shims/navigation-client.ts +4 -3
  673. package/src/shims/navigation.ts +14 -7
  674. package/src/utils/directive-parser.ts +1 -390
  675. package/src/utils/state-machine.ts +111 -0
  676. package/dist/_chunks/als-registry-k-AtAQ9R.js +0 -41
  677. package/dist/_chunks/als-registry-k-AtAQ9R.js.map +0 -1
  678. package/dist/_chunks/format-DNt20Kt8.js.map +0 -1
  679. package/dist/_chunks/interception-DGDIjDbR.js +0 -676
  680. package/dist/_chunks/interception-DGDIjDbR.js.map +0 -1
  681. package/dist/_chunks/request-context-CRj2Zh1E.js +0 -329
  682. package/dist/_chunks/request-context-CRj2Zh1E.js.map +0 -1
  683. package/dist/_chunks/ssr-data-DLnbYpj1.js.map +0 -1
  684. package/dist/_chunks/tracing-DF0G3FB7.js +0 -173
  685. package/dist/_chunks/tracing-DF0G3FB7.js.map +0 -1
  686. package/dist/_chunks/use-cookie-dDbpCTx-.js +0 -91
  687. package/dist/_chunks/use-cookie-dDbpCTx-.js.map +0 -1
  688. package/dist/_chunks/use-query-states-DAhgj8Gx.js.map +0 -1
  689. package/dist/cache/register-cached-function.d.ts +0 -17
  690. package/dist/cache/register-cached-function.d.ts.map +0 -1
  691. package/dist/client/browser-entry.d.ts +0 -22
  692. package/dist/client/browser-entry.d.ts.map +0 -1
  693. package/dist/client/browser-links.d.ts +0 -32
  694. package/dist/client/browser-links.d.ts.map +0 -1
  695. package/dist/client/link-navigate-interceptor.d.ts +0 -28
  696. package/dist/client/link-navigate-interceptor.d.ts.map +0 -1
  697. package/dist/client/link-status-provider.d.ts +0 -11
  698. package/dist/client/link-status-provider.d.ts.map +0 -1
  699. package/dist/client/transition-root.d.ts.map +0 -1
  700. package/dist/client/use-navigation-pending.d.ts.map +0 -1
  701. package/dist/client/use-params.d.ts.map +0 -1
  702. package/dist/plugins/cache-transform.d.ts +0 -36
  703. package/dist/plugins/cache-transform.d.ts.map +0 -1
  704. package/dist/plugins/dev-error-overlay.d.ts +0 -60
  705. package/dist/plugins/dev-error-overlay.d.ts.map +0 -1
  706. package/dist/plugins/dev-logs.d.ts.map +0 -1
  707. package/dist/plugins/dynamic-transform.d.ts +0 -72
  708. package/dist/plugins/dynamic-transform.d.ts.map +0 -1
  709. package/dist/search-params/analyze.d.ts +0 -54
  710. package/dist/search-params/analyze.d.ts.map +0 -1
  711. package/dist/search-params/builtin-codecs.d.ts +0 -105
  712. package/dist/search-params/builtin-codecs.d.ts.map +0 -1
  713. package/dist/search-params/codecs.d.ts +0 -53
  714. package/dist/search-params/codecs.d.ts.map +0 -1
  715. package/dist/search-params/create.d.ts +0 -106
  716. package/dist/search-params/create.d.ts.map +0 -1
  717. package/dist/server/dev-fetch-instrumentation.d.ts +0 -22
  718. package/dist/server/dev-fetch-instrumentation.d.ts.map +0 -1
  719. package/dist/server/dev-logger.d.ts.map +0 -1
  720. package/dist/server/dev-span-processor.d.ts.map +0 -1
  721. package/dist/server/dev-warnings.d.ts.map +0 -1
  722. package/dist/server/manifest-status-resolver.d.ts +0 -58
  723. package/dist/server/manifest-status-resolver.d.ts.map +0 -1
  724. package/dist/server/prerender.d.ts +0 -77
  725. package/dist/server/prerender.d.ts.map +0 -1
  726. package/src/cache/register-cached-function.ts +0 -99
  727. package/src/client/browser-entry.ts +0 -686
  728. package/src/client/browser-links.ts +0 -90
  729. package/src/client/link-navigate-interceptor.tsx +0 -62
  730. package/src/client/link-status-provider.tsx +0 -30
  731. package/src/client/transition-root.tsx +0 -160
  732. package/src/plugins/cache-transform.ts +0 -199
  733. package/src/plugins/dev-error-overlay.ts +0 -230
  734. package/src/plugins/dynamic-transform.ts +0 -161
  735. package/src/search-params/analyze.ts +0 -192
  736. package/src/search-params/builtin-codecs.ts +0 -228
  737. package/src/search-params/create.ts +0 -321
  738. package/src/server/dev-fetch-instrumentation.ts +0 -96
  739. package/src/server/dev-span-processor.ts +0 -78
  740. package/src/server/manifest-status-resolver.ts +0 -215
  741. package/src/server/prerender.ts +0 -139
  742. /package/src/{server/dev-logger.ts → dev-tools/logger.ts} +0 -0
@@ -1,48 +1,29 @@
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-DLnbYpj1.js";
3
- import { n as useQueryStates, t as bindUseQueryStates } from "../_chunks/use-query-states-DAhgj8Gx.js";
4
- import { t as useCookie } from "../_chunks/use-cookie-dDbpCTx-.js";
5
- import { TimberErrorBoundary } from "./error-boundary.js";
6
- import React, { cloneElement, createContext, createElement, isValidElement, useActionState as useActionState$1, useContext, useEffect, useMemo, useRef, useSyncExternalStore, useTransition } from "react";
2
+ import { r as __exportAll } from "../_chunks/chunk-BYIpzuS7.js";
3
+ import { n as classifyUrlSegment } from "../_chunks/segment-classify-BjfuctV2.js";
4
+ import { t as getSearchParamsDefinition } from "../_chunks/registry-I2ss-lvy.js";
5
+ import { n as fromSchema } from "../_chunks/schema-bridge-Cxu4l-7p.js";
6
+ import { t as _registerUseSegmentParams } from "../_chunks/define-CfBPoJb0.js";
7
+ import { n as assertValidCookieName, t as mergePreservedSearchParams } from "../_chunks/merge-search-params-BphMdht_.js";
8
+ import { r as _registerUseCookieModule, t as _registerFromSchema } from "../_chunks/define-cookie-BjpIt4UC.js";
9
+ import { n as getSsrData } from "../_chunks/ssr-data-B4CdH7rE.js";
10
+ import { n as getRouterOrNull } from "../_chunks/router-ref-h3-UaCQv.js";
11
+ import { n as useSegmentContext } from "../_chunks/segment-context-Dx_OizxD.js";
12
+ import { i as unmountLinkForCurrentNavigation, n as LINK_IDLE, r as setLinkForCurrentNavigation } from "../_chunks/navigation-root-BCYczjml.js";
13
+ import { c as usePendingNavigationUrl, n as useSegmentParams, s as useNavigationContext } from "../_chunks/use-segment-params-BkpKAQ7D.js";
14
+ import { createContext, useActionState as useActionState$1, useContext, useEffect, useOptimistic, useRef, useSyncExternalStore, useTransition } from "react";
7
15
  import { jsx } from "react/jsx-runtime";
8
- //#region src/client/link-navigate-interceptor.tsx
9
- /** Symbol used to store the onNavigate callback on anchor elements. */
10
- var ON_NAVIGATE_KEY = "__timberOnNavigate";
11
- /**
12
- * Client component rendered inside <Link> that attaches the onNavigate
13
- * callback to the closest <a> ancestor via a DOM property. The callback
14
- * is cleaned up on unmount.
15
- *
16
- * Renders no extra DOM — just a transparent wrapper.
17
- */
18
- function LinkNavigateInterceptor({ onNavigate, children }) {
19
- const ref = useRef(null);
20
- useEffect(() => {
21
- const anchor = ref.current?.closest("a");
22
- if (!anchor) return;
23
- anchor[ON_NAVIGATE_KEY] = onNavigate;
24
- return () => {
25
- delete anchor[ON_NAVIGATE_KEY];
26
- };
27
- }, [onNavigate]);
28
- return /* @__PURE__ */ jsx("span", {
29
- ref,
30
- style: { display: "contents" },
31
- children
32
- });
33
- }
34
- //#endregion
35
16
  //#region src/client/use-link-status.ts
36
17
  /**
37
18
  * React context provided by <Link>. Holds the pending status
38
19
  * for that specific link's navigation.
39
20
  */
40
- var LinkStatusContext = createContext({ pending: false });
21
+ var LinkStatusContext = createContext({ isPending: false });
41
22
  /**
42
- * Returns `{ pending: true }` while the nearest parent `<Link>` component's
23
+ * Returns `{ isPending: true }` while the nearest parent `<Link>` component's
43
24
  * navigation is in flight. Must be used inside a `<Link>` component's children.
44
25
  *
45
- * Unlike `useNavigationPending()` which is global, this hook is scoped to
26
+ * Unlike `usePendingNavigation()` which is global, this hook is scoped to
46
27
  * the nearest parent `<Link>` — only the link the user clicked shows pending.
47
28
  *
48
29
  * ```tsx
@@ -50,8 +31,8 @@ var LinkStatusContext = createContext({ pending: false });
50
31
  * import { Link, useLinkStatus } from '@timber-js/app/client'
51
32
  *
52
33
  * function Hint() {
53
- * const { pending } = useLinkStatus()
54
- * return <span className={pending ? 'opacity-50' : ''} />
34
+ * const { isPending } = useLinkStatus()
35
+ * return <span className={isPending ? 'opacity-50' : ''} />
55
36
  * }
56
37
  *
57
38
  * export function NavLink({ href, children }) {
@@ -66,135 +47,35 @@ var LinkStatusContext = createContext({ pending: false });
66
47
  function useLinkStatus() {
67
48
  return useContext(LinkStatusContext);
68
49
  }
69
- //#endregion
70
- //#region src/client/navigation-context.ts
71
50
  /**
72
- * NavigationContext — React context for navigation state.
73
- *
74
- * Holds the current route params and pathname, updated atomically
75
- * with the RSC tree on each navigation. This replaces the previous
76
- * useSyncExternalStore approach for useParams() and usePathname(),
77
- * which suffered from a timing gap: the new tree could commit before
78
- * the external store re-renders fired, causing a frame where both
79
- * old and new active states were visible simultaneously.
80
- *
81
- * By wrapping the RSC payload element in NavigationProvider inside
82
- * renderRoot(), the context value and the element tree are passed to
83
- * reactRoot.render() in the same call — atomic by construction.
84
- * All consumers (useParams, usePathname) see the new values in the
85
- * same render pass as the new tree.
86
- *
87
- * During SSR, no NavigationProvider is mounted. Hooks fall back to
88
- * the ALS-backed getSsrData() for per-request isolation.
89
- *
90
- * IMPORTANT: createContext and useContext are NOT available in the RSC
91
- * environment (React Server Components use a stripped-down React).
92
- * The context is lazily initialized on first access, and all functions
93
- * that depend on these APIs are safe to call from any environment —
94
- * they return null or no-op when the APIs aren't available.
95
- *
96
- * Singleton guarantee: With the simplified chunking strategy (LOCAL-337),
97
- * this module lives in exactly one client chunk. The previous globalThis +
98
- * Symbol.for workaround for cross-chunk duplication is no longer needed.
99
- * See design/27-chunking-strategy.md.
100
- *
101
- * See design/19-client-navigation.md §"NavigationContext"
51
+ * Store metadata from Link's onClick for the next navigate event.
52
+ * Called synchronously in the click handler — the navigate event
53
+ * fires synchronously after onClick returns.
102
54
  */
55
+ function setNavLinkMetadata(metadata) {}
56
+ //#endregion
57
+ //#region src/client/navigation-api.ts
103
58
  /**
104
- * The context is created lazily to avoid calling createContext at module
105
- * level. In the RSC environment, React.createContext doesn't exist —
106
- * calling it at import time would crash the server.
107
- */
108
- var _navCtx;
109
- var _pendingNavCtx;
110
- function getOrCreateContext() {
111
- if (_navCtx !== void 0) return _navCtx;
112
- if (typeof React.createContext === "function") {
113
- _navCtx = React.createContext(null);
114
- return _navCtx;
115
- }
116
- }
117
- /**
118
- * Read the navigation context. Returns null during SSR (no provider)
119
- * or in the RSC environment (no context available).
120
- * Internal — used by useParams() and usePathname().
121
- */
122
- function useNavigationContext() {
123
- const ctx = getOrCreateContext();
124
- if (!ctx) return null;
125
- if (typeof React.useContext !== "function") return null;
126
- return React.useContext(ctx);
127
- }
128
- /**
129
- * Wraps children with NavigationContext.Provider.
130
- *
131
- * Used in browser-entry.ts renderRoot to wrap the RSC payload element
132
- * so that navigation state updates atomically with the tree render.
133
- */
134
- function NavigationProvider({ value, children }) {
135
- const ctx = getOrCreateContext();
136
- if (!ctx) return children;
137
- return createElement(ctx.Provider, { value }, children);
138
- }
139
- /**
140
- * Navigation state communicated between the router and renderRoot.
141
- *
142
- * The router calls setNavigationState() before renderRoot(). The
143
- * renderRoot callback reads via getNavigationState() to create the
144
- * NavigationProvider with the correct params/pathname.
145
- *
146
- * This is NOT used by hooks directly — hooks read from React context.
147
- */
148
- var _navState = {
149
- params: {},
150
- pathname: "/"
151
- };
152
- function setNavigationState(state) {
153
- _navState = state;
154
- }
155
- function getNavigationState() {
156
- return _navState;
157
- }
158
- /**
159
- * Separate context for the in-flight navigation URL. Provided by
160
- * TransitionRoot (urgent useState), consumed by LinkStatusProvider
161
- * and useNavigationPending.
162
- */
163
- function getOrCreatePendingContext() {
164
- if (_pendingNavCtx !== void 0) return _pendingNavCtx;
165
- if (typeof React.createContext === "function") {
166
- _pendingNavCtx = React.createContext(null);
167
- return _pendingNavCtx;
168
- }
169
- }
170
- /**
171
- * Read the pending navigation URL from context.
172
- * Returns null during SSR (no provider) or in the RSC environment.
59
+ * Returns true if the Navigation API is available in the current environment.
60
+ * Feature-detected at runtime — no polyfill.
173
61
  */
174
- function usePendingNavigationUrl() {
175
- const ctx = getOrCreatePendingContext();
176
- if (!ctx) return null;
177
- if (typeof React.useContext !== "function") return null;
178
- return React.useContext(ctx);
62
+ function hasNavigationApi() {
63
+ return typeof window !== "undefined" && "navigation" in window && window.navigation != null;
179
64
  }
180
65
  //#endregion
181
- //#region src/client/link-status-provider.tsx
182
- var NOT_PENDING = { pending: false };
183
- var IS_PENDING = { pending: true };
66
+ //#region src/client/link.tsx
184
67
  /**
185
- * Client component that reads the pending URL from PendingNavigationContext
186
- * and provides a scoped LinkStatusContext to children. Renders no extra DOM —
187
- * just a context provider around children.
68
+ * Read the current URL's search string without requiring a React hook.
69
+ * On the client, reads window.location.search. During SSR, reads from
70
+ * the request context (getSsrData). Returns empty string if unavailable.
188
71
  */
189
- function LinkStatusProvider({ href, children }) {
190
- const status = usePendingNavigationUrl() === href ? IS_PENDING : NOT_PENDING;
191
- return /* @__PURE__ */ jsx(LinkStatusContext.Provider, {
192
- value: status,
193
- children
194
- });
72
+ function getCurrentSearch() {
73
+ if (typeof window !== "undefined") return window.location.search;
74
+ const data = getSsrData();
75
+ if (!data) return "";
76
+ const str = new URLSearchParams(data.searchParams).toString();
77
+ return str ? `?${str}` : "";
195
78
  }
196
- //#endregion
197
- //#region src/client/link.tsx
198
79
  /**
199
80
  * Reject dangerous URL schemes that could execute script.
200
81
  * Security: design/13-security.md § Link scheme injection (test #9)
@@ -218,25 +99,53 @@ function isInternalHref(href) {
218
99
  * - [...param] → catch-all (joined with /)
219
100
  * - [[...param]] → optional catch-all (omitted if undefined/empty)
220
101
  */
221
- function interpolateParams(pattern, params) {
222
- return pattern.replace(/\[\[\.\.\.(\w+)\]\]|\[\.\.\.(\w+)\]|\[(\w+)\]/g, (_match, optionalCatchAll, catchAll, single) => {
223
- if (optionalCatchAll) {
224
- const value = params[optionalCatchAll];
225
- if (value === void 0 || Array.isArray(value) && value.length === 0) return "";
102
+ /**
103
+ * Parse a route pattern's path portion into classified segments.
104
+ * Exported for testing. Uses the shared character-based classifier.
105
+ */
106
+ function parseSegments(pattern) {
107
+ return pattern.split("/").filter(Boolean).map(classifyUrlSegment);
108
+ }
109
+ /**
110
+ * Resolve a single classified segment into its string representation.
111
+ * Returns null for optional catch-all with no value (filtered out before join).
112
+ */
113
+ function resolveSegment(seg, params, pattern) {
114
+ switch (seg.kind) {
115
+ case "static": return seg.value;
116
+ case "optional-catch-all": {
117
+ const value = params[seg.name];
118
+ if (value === void 0 || Array.isArray(value) && value.length === 0) return null;
226
119
  return (Array.isArray(value) ? value : [value]).map(encodeURIComponent).join("/");
227
120
  }
228
- if (catchAll) {
229
- const value = params[catchAll];
230
- if (value === void 0) throw new Error(`<Link> missing required catch-all param "${catchAll}" for pattern "${pattern}".`);
121
+ case "catch-all": {
122
+ const value = params[seg.name];
123
+ if (value === void 0) throw new Error(`<Link> missing required catch-all param "${seg.name}" for pattern "${pattern}".`);
231
124
  const segments = Array.isArray(value) ? value : [value];
232
- if (segments.length === 0) throw new Error(`<Link> catch-all param "${catchAll}" must have at least one segment for pattern "${pattern}".`);
125
+ if (segments.length === 0) throw new Error(`<Link> catch-all param "${seg.name}" must have at least one segment for pattern "${pattern}".`);
233
126
  return segments.map(encodeURIComponent).join("/");
234
127
  }
235
- const value = params[single];
236
- if (value === void 0) throw new Error(`<Link> missing required param "${single}" for pattern "${pattern}".`);
237
- if (Array.isArray(value)) throw new Error(`<Link> param "${single}" expected a string but received an array for pattern "${pattern}".`);
238
- return encodeURIComponent(String(value));
239
- }).replace(/\/+$/, "") || "/";
128
+ case "dynamic": {
129
+ const value = params[seg.name];
130
+ if (value === void 0) throw new Error(`<Link> missing required param "${seg.name}" for pattern "${pattern}".`);
131
+ if (Array.isArray(value)) throw new Error(`<Link> param "${seg.name}" expected a string but received an array for pattern "${pattern}".`);
132
+ return encodeURIComponent(String(value));
133
+ }
134
+ }
135
+ }
136
+ /**
137
+ * Split a URL pattern into the path portion and any trailing ?query/#hash suffix.
138
+ * Uses URL parsing for correctness rather than manual index arithmetic.
139
+ */
140
+ function splitPatternSuffix(pattern) {
141
+ if (!pattern.includes("?") && !pattern.includes("#")) return [pattern, ""];
142
+ const url = new URL(pattern, "http://x");
143
+ const suffix = url.search + url.hash;
144
+ return [pattern.slice(0, pattern.length - suffix.length), suffix];
145
+ }
146
+ function interpolateParams(pattern, params) {
147
+ const [pathPart, suffix] = splitPatternSuffix(pattern);
148
+ return ("/" + parseSegments(pathPart).map((seg) => resolveSegment(seg, params, pattern)).filter((s) => s !== null).join("/") || "/") + suffix;
240
149
  }
241
150
  /**
242
151
  * Resolve the final href string from Link props.
@@ -246,12 +155,30 @@ function interpolateParams(pattern, params) {
246
155
  * - searchParams serialization via SearchParamsDefinition
247
156
  * - Validation that searchParams and inline query strings are exclusive
248
157
  */
158
+ /**
159
+ * Runtime discriminator: treat `searchParams` as the legacy wrapped shape
160
+ * only when it literally has a `definition` key. Everything else is the
161
+ * flat `Partial<T>` values shape (TIM-830).
162
+ */
163
+ function isWrappedSearchParamsProp(sp) {
164
+ return "definition" in sp;
165
+ }
249
166
  function resolveHref(href, params, searchParams) {
250
167
  let resolvedPath = href;
251
168
  if (params) resolvedPath = interpolateParams(href, params);
252
169
  if (searchParams) {
253
170
  if (resolvedPath.includes("?")) throw new Error("<Link> received both a searchParams prop and a query string in href. These are mutually exclusive — use one or the other.");
254
- const qs = searchParams.definition.serialize(searchParams.values);
171
+ let definition;
172
+ let values;
173
+ if (isWrappedSearchParamsProp(searchParams)) {
174
+ definition = searchParams.definition;
175
+ values = searchParams.values;
176
+ } else {
177
+ definition = getSearchParamsDefinition(href);
178
+ values = searchParams;
179
+ if (!definition) throw new Error(`<Link> received a flat searchParams object for href "${href}", but no SearchParamsDefinition is registered for that route. Either the route does not export \`searchParams\` from its \`params.ts\`/\`page.tsx\`, or the search-params registry module was not loaded. For external or computed hrefs, pass the legacy \`{ definition, values }\` shape instead.`);
180
+ }
181
+ const qs = definition.serialize(values);
255
182
  if (qs) resolvedPath = `${resolvedPath}?${qs}`;
256
183
  }
257
184
  return resolvedPath;
@@ -263,820 +190,114 @@ function resolveHref(href, params, searchParams) {
263
190
  function buildLinkProps(props) {
264
191
  const resolvedHref = resolveHref(props.href, props.params, props.searchParams);
265
192
  validateLinkHref(resolvedHref);
266
- const output = { href: resolvedHref };
267
- if (isInternalHref(resolvedHref)) {
268
- output["data-timber-link"] = true;
269
- if (props.prefetch) output["data-timber-prefetch"] = true;
270
- if (props.scroll === false) output["data-timber-scroll"] = "false";
271
- }
272
- return output;
193
+ return { href: resolvedHref };
194
+ }
195
+ /**
196
+ * Should this click be intercepted for SPA navigation?
197
+ *
198
+ * Returns false (pass through to browser) when:
199
+ * - Modified keys are held (Ctrl, Meta, Shift, Alt) — open in new tab
200
+ * - The click is not the primary button
201
+ * - The event was already prevented by a parent handler
202
+ * - The link has target="_blank" or similar
203
+ * - The link has a download attribute
204
+ * - The href is external
205
+ */
206
+ function shouldInterceptClick(event, resolvedHref) {
207
+ if (event.button !== 0) return false;
208
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return false;
209
+ if (event.defaultPrevented) return false;
210
+ const anchor = event.currentTarget;
211
+ if (anchor.target && anchor.target !== "_self") return false;
212
+ if (anchor.hasAttribute("download")) return false;
213
+ if (!isInternalHref(resolvedHref)) return false;
214
+ return true;
273
215
  }
274
216
  /**
275
217
  * Navigation link with progressive enhancement.
276
218
  *
277
219
  * Renders as a plain `<a>` tag — works without JavaScript. When the client
278
- * runtime is active, it intercepts clicks on links marked with
279
- * `data-timber-link` to perform RSC-based client navigation.
220
+ * runtime is active, the Link's onClick handler triggers RSC-based client
221
+ * navigation via the router. No global event delegation — each Link owns
222
+ * its own click handling.
280
223
  *
281
- * Supports typed routes via codegen overloads. At runtime:
282
- * - `params` prop interpolates dynamic segments in the href pattern
224
+ * Supports typed routes via the Routes interface (populated by codegen).
225
+ * At runtime:
226
+ * - `segmentParams` prop interpolates dynamic segments in the href pattern
283
227
  * - `searchParams` prop serializes query parameters via a SearchParamsDefinition
228
+ *
229
+ * Typed via the LinkFunction callable interface. The base call signature
230
+ * forbids segmentParams; per-route signatures are added by codegen via
231
+ * interface merging. See TIM-624.
284
232
  */
285
- function Link({ href, prefetch, scroll, params, searchParams, onNavigate, children, ...rest }) {
286
- const linkProps = buildLinkProps({
233
+ var Link = function LinkImpl(props) {
234
+ const { href, prefetch, scroll, segmentParams, searchParams, preserveSearchParams, onNavigate, onClick: userOnClick, onMouseEnter: userOnMouseEnter, children, ...rest } = props;
235
+ const { href: baseHref } = buildLinkProps({
287
236
  href,
288
- prefetch,
289
- scroll,
290
- params,
237
+ params: segmentParams,
291
238
  searchParams
292
239
  });
293
- const inner = /* @__PURE__ */ jsx(LinkStatusProvider, {
294
- href: linkProps.href,
295
- children
296
- });
297
- return /* @__PURE__ */ jsx("a", {
298
- ...rest,
299
- ...linkProps,
300
- children: onNavigate ? /* @__PURE__ */ jsx(LinkNavigateInterceptor, {
301
- onNavigate,
302
- children: inner
303
- }) : inner
304
- });
305
- }
306
- //#endregion
307
- //#region src/client/segment-cache.ts
308
- /**
309
- * Maintains the client-side segment tree representing currently mounted
310
- * layouts and pages. Used for navigation reconciliation — the router diffs
311
- * new routes against this tree to determine which segments to re-fetch.
312
- */
313
- var SegmentCache = class {
314
- root;
315
- get(segment) {
316
- if (segment === "/" || segment === this.root?.segment) return this.root;
317
- }
318
- set(segment, node) {
319
- if (segment === "/" || !this.root) this.root = node;
320
- }
321
- clear() {
322
- this.root = void 0;
323
- }
324
- /**
325
- * Serialize the mounted segment tree for the X-Timber-State-Tree header.
326
- * Only includes sync segments — async segments are excluded because the
327
- * server must always re-render them (they may depend on request context).
328
- *
329
- * When mergeableFilter is provided, only segments whose paths are in the
330
- * set are included. This ensures the server only skips segments that the
331
- * client can actually merge (i.e., segments whose cached element tree
332
- * contains an inner SegmentProvider the merger can splice into).
333
- *
334
- * This is a performance optimization only, NOT a security boundary.
335
- * The server always runs all access.ts files regardless of the state tree.
336
- */
337
- serializeStateTree(mergeableFilter) {
338
- const segments = [];
339
- if (this.root) collectSyncSegments(this.root, segments, mergeableFilter);
340
- return { segments };
341
- }
342
- };
343
- /** Recursively collect sync segment paths from the tree */
344
- function collectSyncSegments(node, out, mergeableFilter) {
345
- if (!node.isAsync && (!mergeableFilter || mergeableFilter.has(node.segment))) out.push(node.segment);
346
- for (const child of node.children.values()) collectSyncSegments(child, out, mergeableFilter);
347
- }
348
- /**
349
- * Build a SegmentNode tree from flat segment metadata.
350
- *
351
- * Takes an ordered list of segment descriptors (root → leaf) from the
352
- * server's X-Timber-Segments header and constructs the hierarchical
353
- * tree structure that SegmentCache expects.
354
- *
355
- * Each segment is nested as a child of the previous one, forming a
356
- * linear chain from root to leaf. The leaf segment (page) is excluded
357
- * from the tree — pages are never cached across navigations.
358
- */
359
- function buildSegmentTree(segments) {
360
- if (segments.length === 0) return void 0;
361
- const layouts = segments.length > 1 ? segments.slice(0, -1) : segments;
362
- let root;
363
- let parent;
364
- for (const info of layouts) {
365
- const node = {
366
- segment: info.path,
367
- payload: null,
368
- isAsync: info.isAsync,
369
- children: /* @__PURE__ */ new Map()
370
- };
371
- if (!root) root = node;
372
- if (parent) parent.children.set(info.path, node);
373
- parent = node;
374
- }
375
- return root;
376
- }
377
- /**
378
- * Short-lived cache for hover-triggered prefetches. Entries expire after
379
- * 30 seconds. When a link is clicked, the prefetched payload is consumed
380
- * (moved to the history stack) and removed from this cache.
381
- *
382
- * timber.js does NOT prefetch on viewport intersection — only explicit
383
- * hover on <Link prefetch> triggers a prefetch.
384
- */
385
- var PrefetchCache = class PrefetchCache {
386
- static TTL_MS = 3e4;
387
- entries = /* @__PURE__ */ new Map();
388
- set(url, result) {
389
- this.entries.set(url, {
390
- result,
391
- expiresAt: Date.now() + PrefetchCache.TTL_MS
392
- });
393
- }
394
- get(url) {
395
- const entry = this.entries.get(url);
396
- if (!entry) return void 0;
397
- if (Date.now() >= entry.expiresAt) {
398
- this.entries.delete(url);
399
- return;
400
- }
401
- return entry.result;
402
- }
403
- /** Get and remove the entry (used when navigation consumes a prefetch) */
404
- consume(url) {
405
- const result = this.get(url);
406
- if (result !== void 0) this.entries.delete(url);
407
- return result;
408
- }
409
- };
410
- //#endregion
411
- //#region src/client/history.ts
412
- /**
413
- * Session-lived history stack keyed by URL. Enables instant back/forward
414
- * navigation without a server roundtrip.
415
- *
416
- * On forward navigation, the new page's payload is pushed onto the stack.
417
- * On popstate, the cached payload is replayed instantly.
418
- *
419
- * Scroll positions are stored in history.state (browser History API),
420
- * not in this stack — see design/19-client-navigation.md §Scroll Restoration.
421
- *
422
- * Entries persist for the session duration (no expiry) and are cleared
423
- * when the tab is closed — matching browser back-button behavior.
424
- */
425
- var HistoryStack = class {
426
- entries = /* @__PURE__ */ new Map();
427
- push(url, entry) {
428
- this.entries.set(url, entry);
429
- }
430
- get(url) {
431
- return this.entries.get(url);
432
- }
433
- has(url) {
434
- return this.entries.has(url);
435
- }
436
- };
437
- //#endregion
438
- //#region src/client/use-params.ts
439
- /**
440
- * Set the current route params in the module-level store.
441
- *
442
- * Called by the router on each navigation. This updates the fallback
443
- * snapshot used by tests and by the hook when called outside a React
444
- * component (no NavigationContext available).
445
- *
446
- * On the client, the primary reactivity path is NavigationContext —
447
- * the router calls setNavigationState() then renderRoot() which wraps
448
- * the element in NavigationProvider. setCurrentParams is still called
449
- * for the module-level fallback.
450
- *
451
- * During SSR, params are also available via getSsrData().params
452
- * (ALS-backed).
453
- */
454
- function setCurrentParams(params) {
455
- _setCurrentParams(params);
456
- }
457
- function useParams(_route) {
458
- try {
459
- const navContext = useNavigationContext();
460
- if (navContext !== null) return navContext.params;
461
- } catch {}
462
- return getSsrData()?.params ?? currentParams;
463
- }
464
- //#endregion
465
- //#region src/client/segment-merger.ts
466
- /**
467
- * Segment Merger — client-side tree merging for partial RSC payloads.
468
- *
469
- * When the server skips rendering sync layouts (because the client already
470
- * has them cached), the RSC payload is missing outer segment wrappers.
471
- * This module reconstructs the full element tree by splicing the partial
472
- * payload into cached segment subtrees.
473
- *
474
- * The approach:
475
- * 1. After each full RSC payload render, walk the decoded element tree
476
- * and cache each segment's subtree (identified by SegmentProvider boundaries)
477
- * 2. When a partial payload arrives, wrap it with cached segment elements
478
- * using React.cloneElement to preserve component identity
479
- *
480
- * React.cloneElement preserves the element's `type` — React sees the same
481
- * component at the same tree position and reconciles (preserving state)
482
- * rather than remounting. This is how layout state survives navigations.
483
- *
484
- * Design docs: 19-client-navigation.md §"Navigation Reconciliation"
485
- * Security: access.ts runs on the server regardless of skipping — this
486
- * is a performance optimization only. See 13-security.md.
487
- */
488
- /**
489
- * Cache of React element subtrees per segment path.
490
- * Updated after each navigation with the full decoded RSC element tree.
491
- */
492
- var SegmentElementCache = class {
493
- entries = /* @__PURE__ */ new Map();
494
- get(segmentPath) {
495
- return this.entries.get(segmentPath);
496
- }
497
- set(segmentPath, entry) {
498
- this.entries.set(segmentPath, entry);
499
- }
500
- has(segmentPath) {
501
- return this.entries.has(segmentPath);
502
- }
503
- clear() {
504
- this.entries.clear();
505
- }
506
- get size() {
507
- return this.entries.size;
508
- }
509
- /**
510
- * Get the set of segment paths that are safe for the server to skip.
511
- * Only segments with an inner SegmentProvider (hasMergeableChild) are
512
- * included — the merger can only replace inner SegmentProviders, not
513
- * pages embedded in layout output. Used to filter the state tree.
514
- *
515
- * Returns an empty set if the element cache is empty (no elements
516
- * cached yet). This is the safe default — an empty set means no
517
- * segments pass the filter, so the state tree is empty and the server
518
- * does a full render. The element cache is populated lazily after the
519
- * first SPA navigation (RSC-decoded elements from hydration are
520
- * thenables that can't be walked until React resolves them).
521
- */
522
- getMergeablePaths() {
523
- const paths = /* @__PURE__ */ new Set();
524
- for (const [, entry] of this.entries) if (entry.hasMergeableChild) paths.add(entry.segmentPath);
525
- return paths;
526
- }
527
- };
528
- /**
529
- * Check if a React element is a SegmentProvider by looking for the
530
- * `segments` prop (an array of path segments). This is the only
531
- * component that receives this prop shape.
532
- */
533
- function isSegmentProvider(element) {
534
- if (!isValidElement(element)) return false;
535
- const props = element.props;
536
- return Array.isArray(props.segments);
537
- }
538
- /**
539
- * Extract the segment path from a SegmentProvider element.
540
- *
541
- * Uses the `segmentId` prop if available (set by the server for route groups
542
- * to distinguish siblings that share the same urlPath). Falls back to
543
- * reconstructing from the `segments` array prop.
544
- */
545
- function getSegmentPath(element) {
546
- const props = element.props;
547
- if (props.segmentId) return props.segmentId;
548
- const filtered = props.segments.filter(Boolean);
549
- return filtered.length === 0 ? "/" : "/" + filtered.join("/");
550
- }
551
- /**
552
- * Walk a React element tree and extract all SegmentProvider boundaries.
553
- * Returns an ordered list of segment entries from outermost to innermost.
554
- *
555
- * This only finds SegmentProviders along the main children path — it does
556
- * not descend into parallel routes/slots (those are separate subtrees).
557
- */
558
- function extractSegments(element) {
559
- const segments = [];
560
- walkForSegments(element, segments);
561
- for (let i = 0; i < segments.length; i++) segments[i].hasMergeableChild = i < segments.length - 1;
562
- return segments;
563
- }
564
- function walkForSegments(node, out) {
565
- if (!isValidElement(node)) return;
566
- const el = node;
567
- const props = el.props;
568
- if (isSegmentProvider(node)) {
569
- out.push({
570
- segmentPath: getSegmentPath(el),
571
- element: el,
572
- hasMergeableChild: false
573
- });
574
- walkChildren(props.children, out);
575
- return;
576
- }
577
- walkChildren(props.children, out);
578
- }
579
- function walkChildren(children, out) {
580
- if (children == null) return;
581
- if (Array.isArray(children)) for (const child of children) walkForSegments(child, out);
582
- else walkForSegments(children, out);
583
- }
584
- /**
585
- * Cache all segment subtrees from a fully-rendered RSC element tree.
586
- * Call this after every full RSC payload render (navigate, refresh, hydration).
587
- */
588
- function cacheSegmentElements(element, cache) {
589
- const segments = extractSegments(element);
590
- for (const entry of segments) cache.set(entry.segmentPath, entry);
591
- }
592
- function findSegmentProviderPath(node, targetPath) {
593
- const children = node.props.children;
594
- if (children == null) return null;
595
- if (Array.isArray(children)) for (let i = 0; i < children.length; i++) {
596
- const child = children[i];
597
- if (!isValidElement(child)) continue;
598
- if (isSegmentProvider(child)) {
599
- if (!targetPath || getSegmentPath(child) === targetPath) return [{
600
- element: node,
601
- childIndex: i
602
- }];
603
- }
604
- const deeper = findSegmentProviderPath(child, targetPath);
605
- if (deeper) return [{
606
- element: node,
607
- childIndex: i
608
- }, ...deeper];
609
- }
610
- else if (isValidElement(children)) {
611
- if (isSegmentProvider(children)) {
612
- if (!targetPath || getSegmentPath(children) === targetPath) return [{
613
- element: node,
614
- childIndex: -1
615
- }];
616
- }
617
- const deeper = findSegmentProviderPath(children, targetPath);
618
- if (deeper) return [{
619
- element: node,
620
- childIndex: -1
621
- }, ...deeper];
622
- }
623
- return null;
624
- }
625
- /**
626
- * Replace a nested SegmentProvider within a cached element tree with
627
- * new content. Uses cloneElement along the path to produce a new tree
628
- * with preserved component identity at every level except the replaced node.
629
- *
630
- * @param cachedElement The cached SegmentProvider element for this segment
631
- * @param newInnerContent The new React element to splice in at the inner segment position
632
- * @param innerSegmentPath The path of the inner segment to replace (optional — replaces first found)
633
- * @returns New element tree with the inner segment replaced
634
- */
635
- function replaceInnerSegment(cachedElement, newInnerContent, innerSegmentPath) {
636
- const path = findSegmentProviderPath(cachedElement, innerSegmentPath);
637
- if (!path || path.length === 0) return cachedElement;
638
- let replacement = newInnerContent;
639
- for (let i = path.length - 1; i >= 0; i--) {
640
- const { element, childIndex } = path[i];
641
- if (childIndex === -1) replacement = cloneElement(element, {}, replacement);
642
- else {
643
- const newChildren = [...element.props.children];
644
- newChildren[childIndex] = replacement;
645
- replacement = cloneElement(element, {}, ...newChildren);
646
- }
647
- }
648
- return replacement;
649
- }
650
- /**
651
- * Merge a partial RSC payload with cached segment elements.
652
- *
653
- * When the server skips segments, the partial payload starts from the
654
- * first non-skipped segment. This function wraps it with cached elements
655
- * for the skipped segments, producing a full tree that React can
656
- * reconcile with the mounted tree (preserving layout state).
657
- *
658
- * @param partialPayload The RSC payload element (may be partial)
659
- * @param skippedSegments Ordered list of segment paths that were skipped (outermost first)
660
- * @param cache The segment element cache
661
- * @returns The merged full element tree, or the partial payload if merging isn't possible
662
- */
663
- function mergeSegmentTree(partialPayload, skippedSegments, cache) {
664
- if (!isValidElement(partialPayload)) return partialPayload;
665
- if (skippedSegments.length === 0) return partialPayload;
666
- let result = partialPayload;
667
- for (let i = skippedSegments.length - 1; i >= 0; i--) {
668
- const segmentPath = skippedSegments[i];
669
- const cached = cache.get(segmentPath);
670
- if (!cached) return partialPayload;
671
- result = replaceInnerSegment(cached.element, result);
672
- }
673
- return result;
674
- }
675
- //#endregion
676
- //#region src/client/rsc-fetch.ts
677
- var RSC_CONTENT_TYPE = "text/x-component";
678
- /**
679
- * Generate a short random cache-busting ID (5 chars, a-z0-9).
680
- * Matches the format Next.js uses for _rsc params.
681
- */
682
- function generateCacheBustId() {
683
- const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
684
- let id = "";
685
- for (let i = 0; i < 5; i++) id += chars[Math.random() * 36 | 0];
686
- return id;
687
- }
688
- /**
689
- * Append a `_rsc=<id>` query parameter to the URL.
690
- * Follows Next.js's pattern — prevents CDN/browser from serving cached HTML
691
- * for RSC navigation requests and signals that this is an RSC fetch.
692
- */
693
- function appendRscParam(url) {
694
- return `${url}${url.includes("?") ? "&" : "?"}_rsc=${generateCacheBustId()}`;
695
- }
696
- function buildRscHeaders(stateTree, currentUrl) {
697
- const headers = { Accept: RSC_CONTENT_TYPE };
698
- if (stateTree) headers["X-Timber-State-Tree"] = JSON.stringify(stateTree);
699
- if (currentUrl) headers["X-Timber-URL"] = currentUrl;
700
- return headers;
701
- }
702
- /**
703
- * Extract head elements from the X-Timber-Head response header.
704
- * Returns null if the header is missing or malformed.
705
- */
706
- function extractHeadElements(response) {
707
- const header = response.headers.get("X-Timber-Head");
708
- if (!header) return null;
709
- try {
710
- return JSON.parse(decodeURIComponent(header));
711
- } catch {
712
- return null;
713
- }
714
- }
715
- /**
716
- * Extract segment metadata from the X-Timber-Segments response header.
717
- * Returns null if the header is missing or malformed.
718
- *
719
- * Format: JSON array of {path, isAsync} objects describing the rendered
720
- * segment chain from root to leaf. Used to populate the client-side
721
- * segment cache for state tree diffing on subsequent navigations.
722
- */
723
- function extractSegmentInfo(response) {
724
- const header = response.headers.get("X-Timber-Segments");
725
- if (!header) return null;
726
- try {
727
- return JSON.parse(header);
728
- } catch {
729
- return null;
730
- }
731
- }
732
- /**
733
- * Extract skipped segment paths from the X-Timber-Skipped-Segments header.
734
- * Returns null if the header is missing or malformed.
735
- *
736
- * When the server skips sync layouts the client already has cached,
737
- * it sends this header listing the skipped segment paths (outermost first).
738
- * The client uses this to merge the partial payload with cached segments.
739
- */
740
- function extractSkippedSegments(response) {
741
- const header = response.headers.get("X-Timber-Skipped-Segments");
742
- if (!header) return null;
743
- try {
744
- const parsed = JSON.parse(header);
745
- return Array.isArray(parsed) ? parsed : null;
746
- } catch {
747
- return null;
748
- }
749
- }
750
- /**
751
- * Extract route params from the X-Timber-Params response header.
752
- * Returns null if the header is missing or malformed.
753
- *
754
- * Used to populate useParams() after client-side navigation.
755
- */
756
- function extractParams(response) {
757
- const header = response.headers.get("X-Timber-Params");
758
- if (!header) return null;
759
- try {
760
- return JSON.parse(header);
761
- } catch {
762
- return null;
763
- }
764
- }
765
- /**
766
- * Thrown when an RSC payload response contains X-Timber-Redirect header.
767
- * Caught in navigate() to trigger a soft router navigation to the redirect target.
768
- */
769
- var RedirectError = class extends Error {
770
- redirectUrl;
771
- constructor(url) {
772
- super(`Server redirect to ${url}`);
773
- this.redirectUrl = url;
774
- }
775
- };
776
- /**
777
- * Fetch an RSC payload from the server. If a decodeRsc function is provided,
778
- * the response is decoded into a React element tree via createFromFetch.
779
- * Otherwise, the raw response text is returned (test mode).
780
- *
781
- * Also extracts head elements from the X-Timber-Head response header
782
- * so the client can update document.title and <meta> tags after navigation.
783
- */
784
- async function fetchRscPayload(url, deps, stateTree, currentUrl) {
785
- const rscUrl = appendRscParam(url);
786
- const headers = buildRscHeaders(stateTree, currentUrl);
787
- if (deps.decodeRsc) {
788
- const fetchPromise = deps.fetch(rscUrl, {
789
- headers,
790
- redirect: "manual"
791
- });
792
- let headElements = null;
793
- let segmentInfo = null;
794
- let params = null;
795
- let skippedSegments = null;
796
- const wrappedPromise = fetchPromise.then((response) => {
797
- const redirectLocation = response.headers.get("X-Timber-Redirect") || (response.status >= 300 && response.status < 400 ? response.headers.get("Location") : null);
798
- if (redirectLocation) throw new RedirectError(redirectLocation);
799
- headElements = extractHeadElements(response);
800
- segmentInfo = extractSegmentInfo(response);
801
- params = extractParams(response);
802
- skippedSegments = extractSkippedSegments(response);
803
- return response;
804
- });
805
- await wrappedPromise;
806
- return {
807
- payload: await deps.decodeRsc(wrappedPromise),
808
- headElements,
809
- segmentInfo,
810
- params,
811
- skippedSegments
240
+ const [linkStatus, setIsPending] = useOptimistic(LINK_IDLE);
241
+ const linkInstanceRef = useRef(null);
242
+ if (!linkInstanceRef.current) linkInstanceRef.current = { setIsPending };
243
+ else linkInstanceRef.current.setIsPending = setIsPending;
244
+ useEffect(() => {
245
+ const instance = linkInstanceRef.current;
246
+ return () => {
247
+ if (instance) unmountLinkForCurrentNavigation(instance);
812
248
  };
813
- }
814
- const response = await deps.fetch(rscUrl, {
815
- headers,
816
- redirect: "manual"
817
- });
818
- if (response.status >= 300 && response.status < 400) {
819
- const location = response.headers.get("Location");
820
- if (location) throw new RedirectError(location);
821
- }
822
- return {
823
- payload: await response.text(),
824
- headElements: extractHeadElements(response),
825
- segmentInfo: extractSegmentInfo(response),
826
- params: extractParams(response),
827
- skippedSegments: extractSkippedSegments(response)
828
- };
829
- }
830
- //#endregion
831
- //#region src/client/router.ts
832
- /**
833
- * Check if an error is an abort error (connection closed / fetch aborted).
834
- * Browsers throw DOMException with name 'AbortError' when a fetch is aborted.
835
- */
836
- function isAbortError(error) {
837
- if (error instanceof DOMException && error.name === "AbortError") return true;
838
- if (error instanceof Error && error.name === "AbortError") return true;
839
- return false;
840
- }
841
- /**
842
- * Create a router instance. In production, called once at app hydration
843
- * with real browser APIs. In tests, called with mock dependencies.
844
- */
845
- function createRouter(deps) {
846
- const segmentCache = new SegmentCache();
847
- const prefetchCache = new PrefetchCache();
848
- const historyStack = new HistoryStack();
849
- const segmentElementCache = new SegmentElementCache();
850
- let pending = false;
851
- let pendingUrl = null;
852
- const pendingListeners = /* @__PURE__ */ new Set();
853
- function setPending(value, url) {
854
- const newPendingUrl = value && url ? url : null;
855
- if (pending === value && pendingUrl === newPendingUrl) return;
856
- pending = value;
857
- pendingUrl = newPendingUrl;
858
- for (const listener of pendingListeners) listener(value);
859
- }
860
- /** Update the segment cache from server-provided segment metadata. */
861
- function updateSegmentCache(segmentInfo) {
862
- if (!segmentInfo || segmentInfo.length === 0) return;
863
- const tree = buildSegmentTree(segmentInfo);
864
- if (tree) segmentCache.set("/", tree);
865
- }
866
- /** Render a decoded RSC payload into the DOM if a renderer is available. */
867
- function renderPayload(payload) {
868
- if (deps.renderRoot) deps.renderRoot(payload);
869
- }
870
- /**
871
- * Merge a partial RSC payload with cached segment elements if segments
872
- * were skipped, then cache segments from the (merged) payload.
873
- * Returns the merged payload ready for rendering.
874
- */
875
- function mergeAndCachePayload(payload, skippedSegments) {
876
- let merged = payload;
877
- if (skippedSegments && skippedSegments.length > 0) merged = mergeSegmentTree(payload, skippedSegments, segmentElementCache);
878
- cacheSegmentElements(merged, segmentElementCache);
879
- return merged;
880
- }
881
- /**
882
- * Update navigation state (params + pathname) for the next render.
883
- *
884
- * Sets both the module-level fallback (for tests and SSR) and the
885
- * navigation context state (read by renderRoot to wrap the element
886
- * in NavigationProvider). The context update is atomic with the tree
887
- * render — both are passed to reactRoot.render() in the same call.
888
- */
889
- function updateNavigationState(params, url) {
890
- const resolvedParams = params ?? {};
891
- setCurrentParams(resolvedParams);
892
- setNavigationState({
893
- params: resolvedParams,
894
- pathname: url.startsWith("http") ? new URL(url).pathname : url.split("?")[0] || "/"
895
- });
896
- }
897
- /**
898
- * Render a payload via navigateTransition (production) or renderRoot (tests).
899
- * The perform callback should fetch data, update state, and return the payload.
900
- * In production, the entire callback runs inside a React transition with
901
- * useOptimistic for the pending URL. In tests, the payload is rendered directly.
902
- */
903
- async function renderViaTransition(url, perform) {
904
- if (deps.navigateTransition) {
905
- let headElements = null;
906
- await deps.navigateTransition(url, async (wrapPayload) => {
907
- const result = await perform();
908
- headElements = result.headElements;
909
- const merged = mergeAndCachePayload(result.payload, result.skippedSegments);
910
- historyStack.push(url, {
911
- payload: merged,
912
- headElements: result.headElements,
913
- params: result.params
914
- });
915
- return wrapPayload(merged);
916
- });
917
- return headElements;
918
- }
919
- const result = await perform();
920
- const merged = mergeAndCachePayload(result.payload, result.skippedSegments);
921
- historyStack.push(url, {
922
- payload: merged,
923
- headElements: result.headElements,
924
- params: result.params
925
- });
926
- renderPayload(merged);
927
- return result.headElements;
928
- }
929
- /** Apply head elements (title, meta tags) to the DOM if available. */
930
- function applyHead(elements) {
931
- if (elements && deps.applyHead) deps.applyHead(elements);
932
- }
933
- /** Run a callback after the next paint (after React commit). */
934
- function afterPaint(callback) {
935
- if (deps.afterPaint) deps.afterPaint(callback);
936
- else callback();
937
- }
938
- /**
939
- * Core navigation logic shared between the transition and fallback paths.
940
- * Fetches the RSC payload, updates all state, and returns the result.
941
- */
942
- async function performNavigationFetch(url, options) {
943
- const prefetched = prefetchCache.consume(url);
944
- let result = prefetched ? {
945
- payload: prefetched.payload,
946
- headElements: prefetched.headElements,
947
- segmentInfo: prefetched.segmentInfo ?? null,
948
- params: prefetched.params ?? null,
949
- skippedSegments: prefetched.skippedSegments ?? null
950
- } : void 0;
951
- if (result === void 0) {
952
- const stateTree = segmentCache.serializeStateTree(segmentElementCache.getMergeablePaths());
953
- const rawCurrentUrl = deps.getCurrentUrl();
954
- result = await fetchRscPayload(url, deps, stateTree, rawCurrentUrl.startsWith("http") ? new URL(rawCurrentUrl).pathname : new URL(rawCurrentUrl, "http://localhost").pathname);
955
- }
956
- if (options.replace) deps.replaceState({
957
- timber: true,
958
- scrollY: 0
959
- }, "", url);
960
- else deps.pushState({
961
- timber: true,
962
- scrollY: 0
963
- }, "", url);
964
- updateSegmentCache(result.segmentInfo);
965
- updateNavigationState(result.params, url);
966
- return result;
967
- }
968
- async function navigate(url, options = {}) {
969
- const scroll = options.scroll !== false;
970
- const replace = options.replace === true;
971
- const currentScrollY = deps.getScrollY();
972
- deps.replaceState({
973
- timber: true,
974
- scrollY: currentScrollY
975
- }, "", deps.getCurrentUrl());
976
- setPending(true, url);
977
- try {
978
- applyHead(await renderViaTransition(url, () => performNavigationFetch(url, { replace })));
979
- window.dispatchEvent(new Event("timber:navigation-end"));
980
- afterPaint(() => {
981
- if (scroll) deps.scrollTo(0, 0);
982
- else deps.scrollTo(0, currentScrollY);
983
- window.dispatchEvent(new Event("timber:scroll-restored"));
984
- });
985
- } catch (error) {
986
- if (error instanceof RedirectError) {
987
- setPending(false);
988
- await navigate(error.redirectUrl, { replace: true });
249
+ }, []);
250
+ const resolvedHref = preserveSearchParams ? mergePreservedSearchParams(baseHref, getCurrentSearch(), preserveSearchParams) : baseHref;
251
+ const internal = isInternalHref(resolvedHref);
252
+ const handleClick = internal ? (event) => {
253
+ userOnClick?.(event);
254
+ if (!shouldInterceptClick(event, resolvedHref)) return;
255
+ if (onNavigate) {
256
+ let prevented = false;
257
+ onNavigate({ preventDefault: () => {
258
+ prevented = true;
259
+ } });
260
+ if (prevented) {
261
+ event.preventDefault();
989
262
  return;
990
263
  }
991
- if (isAbortError(error)) return;
992
- throw error;
993
- } finally {
994
- setPending(false);
995
- }
996
- }
997
- async function refresh() {
998
- const currentUrl = deps.getCurrentUrl();
999
- setPending(true, currentUrl);
1000
- try {
1001
- applyHead(await renderViaTransition(currentUrl, async () => {
1002
- const result = await fetchRscPayload(currentUrl, deps);
1003
- updateSegmentCache(result.segmentInfo);
1004
- updateNavigationState(result.params, currentUrl);
1005
- return result;
1006
- }));
1007
- } finally {
1008
- setPending(false);
1009
264
  }
1010
- }
1011
- async function handlePopState(url, scrollY = 0) {
1012
- const entry = historyStack.get(url);
1013
- if (entry && entry.payload !== null) {
1014
- updateNavigationState(entry.params, url);
1015
- renderPayload(entry.payload);
1016
- applyHead(entry.headElements);
1017
- afterPaint(() => {
1018
- deps.scrollTo(0, scrollY);
1019
- window.dispatchEvent(new Event("timber:scroll-restored"));
265
+ const router = getRouterOrNull();
266
+ if (!router) return;
267
+ const shouldScroll = scroll !== false;
268
+ setLinkForCurrentNavigation(linkInstanceRef.current);
269
+ if (hasNavigationApi()) {
270
+ setNavLinkMetadata({
271
+ scroll: shouldScroll,
272
+ linkInstance: linkInstanceRef.current
1020
273
  });
1021
- } else {
1022
- setPending(true, url);
1023
- try {
1024
- applyHead(await renderViaTransition(url, async () => {
1025
- const result = await fetchRscPayload(url, deps, segmentCache.serializeStateTree(segmentElementCache.getMergeablePaths()));
1026
- updateSegmentCache(result.segmentInfo);
1027
- updateNavigationState(result.params, url);
1028
- return result;
1029
- }));
1030
- afterPaint(() => {
1031
- deps.scrollTo(0, scrollY);
1032
- window.dispatchEvent(new Event("timber:scroll-restored"));
1033
- });
1034
- } finally {
1035
- setPending(false);
1036
- }
274
+ return;
1037
275
  }
1038
- }
1039
- /**
1040
- * Prefetch an RSC payload for a URL and store it in the prefetch cache.
1041
- * Called on hover of <Link prefetch> elements.
1042
- */
1043
- function prefetch(url) {
1044
- if (prefetchCache.get(url) !== void 0) return;
1045
- if (historyStack.has(url)) return;
1046
- fetchRscPayload(url, deps, segmentCache.serializeStateTree(segmentElementCache.getMergeablePaths())).then((result) => {
1047
- prefetchCache.set(url, result);
1048
- }, () => {});
1049
- }
1050
- return {
1051
- navigate,
1052
- refresh,
1053
- handlePopState,
1054
- isPending: () => pending,
1055
- getPendingUrl: () => pendingUrl,
1056
- onPendingChange(listener) {
1057
- pendingListeners.add(listener);
1058
- return () => pendingListeners.delete(listener);
1059
- },
1060
- prefetch,
1061
- applyRevalidation(element, headElements) {
1062
- const currentUrl = deps.getCurrentUrl();
1063
- const merged = mergeAndCachePayload(element, null);
1064
- historyStack.push(currentUrl, {
1065
- payload: merged,
1066
- headElements
1067
- });
1068
- renderPayload(merged);
1069
- applyHead(headElements);
1070
- },
1071
- initSegmentCache: (segments) => updateSegmentCache(segments),
1072
- cacheElementTree: (element) => cacheSegmentElements(element, segmentElementCache),
1073
- segmentCache,
1074
- prefetchCache,
1075
- historyStack
1076
- };
1077
- }
276
+ event.preventDefault();
277
+ const navHref = preserveSearchParams ? mergePreservedSearchParams(baseHref, getCurrentSearch(), preserveSearchParams) : resolvedHref;
278
+ router.navigate(navHref, { scroll: shouldScroll });
279
+ } : userOnClick;
280
+ const handleMouseEnter = internal && prefetch ? (event) => {
281
+ userOnMouseEnter?.(event);
282
+ const router = getRouterOrNull();
283
+ if (router) {
284
+ const prefetchHref = preserveSearchParams ? mergePreservedSearchParams(baseHref, getCurrentSearch(), preserveSearchParams) : resolvedHref;
285
+ router.prefetch(prefetchHref);
286
+ }
287
+ } : userOnMouseEnter;
288
+ return /* @__PURE__ */ jsx("a", {
289
+ ...rest,
290
+ href: resolvedHref,
291
+ onClick: handleClick,
292
+ onMouseEnter: handleMouseEnter,
293
+ children: /* @__PURE__ */ jsx(LinkStatusContext.Provider, {
294
+ value: linkStatus,
295
+ children
296
+ })
297
+ });
298
+ };
1078
299
  //#endregion
1079
- //#region src/client/use-navigation-pending.ts
300
+ //#region src/client/use-pending-navigation.ts
1080
301
  /**
1081
302
  * Returns true while an RSC navigation is in flight.
1082
303
  *
@@ -1089,10 +310,10 @@ function createRouter(deps) {
1089
310
  *
1090
311
  * ```tsx
1091
312
  * 'use client'
1092
- * import { useNavigationPending } from '@timber-js/app/client'
313
+ * import { usePendingNavigation } from '@timber-js/app/client'
1093
314
  *
1094
315
  * export function NavBar() {
1095
- * const isPending = useNavigationPending()
316
+ * const isPending = usePendingNavigation()
1096
317
  * return (
1097
318
  * <nav className={isPending ? 'opacity-50' : ''}>
1098
319
  * <Link href="/dashboard">Dashboard</Link>
@@ -1101,34 +322,10 @@ function createRouter(deps) {
1101
322
  * }
1102
323
  * ```
1103
324
  */
1104
- function useNavigationPending() {
325
+ function usePendingNavigation() {
1105
326
  return usePendingNavigationUrl() !== null;
1106
327
  }
1107
328
  //#endregion
1108
- //#region src/client/router-ref.ts
1109
- /**
1110
- * Set the global router instance. Called once during bootstrap.
1111
- */
1112
- function setGlobalRouter(router) {
1113
- _setGlobalRouter(router);
1114
- }
1115
- /**
1116
- * Get the global router instance. Throws if called before bootstrap.
1117
- * Used by client-side hooks (useNavigationPending, etc.)
1118
- */
1119
- function getRouter() {
1120
- if (!globalRouter) throw new Error("[timber] Router not initialized. getRouter() was called before bootstrap().");
1121
- return globalRouter;
1122
- }
1123
- /**
1124
- * Get the global router instance or null if not yet initialized.
1125
- * Used by useRouter() methods to avoid silent failures — callers
1126
- * can log a meaningful warning instead of silently no-oping.
1127
- */
1128
- function getRouterOrNull() {
1129
- return globalRouter;
1130
- }
1131
- //#endregion
1132
329
  //#region src/client/use-router.ts
1133
330
  /**
1134
331
  * useRouter() — client-side hook for programmatic navigation.
@@ -1152,7 +349,7 @@ function getRouterOrNull() {
1152
349
  *
1153
350
  * For loading UI during navigation, use:
1154
351
  * - useLinkStatus() — per-link pending indicator (inside <Link>)
1155
- * - useNavigationPending() — global navigation pending state
352
+ * - usePendingNavigation() — global navigation pending state
1156
353
  */
1157
354
  /**
1158
355
  * Get a router instance for programmatic navigation.
@@ -1251,97 +448,6 @@ function usePathname() {
1251
448
  return "/";
1252
449
  }
1253
450
  //#endregion
1254
- //#region src/client/use-search-params.ts
1255
- /**
1256
- * useSearchParams() — client-side hook for reading URL search params.
1257
- *
1258
- * Returns a read-only URLSearchParams instance reflecting the current
1259
- * URL's query string. Updates when client-side navigation changes the URL.
1260
- *
1261
- * This is a thin wrapper over window.location.search, provided for
1262
- * Next.js API compatibility (libraries like nuqs import useSearchParams
1263
- * from next/navigation).
1264
- *
1265
- * Unlike Next.js's ReadonlyURLSearchParams, this returns a standard
1266
- * URLSearchParams. Mutation methods (set, delete, append) work on the
1267
- * local copy but do NOT affect the URL — use the router or nuqs for that.
1268
- *
1269
- * During SSR, reads the request search params from the SSR ALS context
1270
- * (populated by ssr-entry.ts) instead of window.location.
1271
- *
1272
- * All mutable state is delegated to client/state.ts for singleton guarantees.
1273
- * See design/18-build-system.md §"Singleton State Registry"
1274
- */
1275
- function getSearch() {
1276
- if (typeof window !== "undefined") return window.location.search;
1277
- const data = getSsrData();
1278
- if (!data) return "";
1279
- const str = new URLSearchParams(data.searchParams).toString();
1280
- return str ? `?${str}` : "";
1281
- }
1282
- function getServerSearch() {
1283
- const data = getSsrData();
1284
- if (!data) return "";
1285
- const str = new URLSearchParams(data.searchParams).toString();
1286
- return str ? `?${str}` : "";
1287
- }
1288
- function subscribe(callback) {
1289
- window.addEventListener("popstate", callback);
1290
- return () => window.removeEventListener("popstate", callback);
1291
- }
1292
- function getSearchParams() {
1293
- const search = getSearch();
1294
- if (search !== cachedSearch) {
1295
- const params = new URLSearchParams(search);
1296
- _setCachedSearch(search, params);
1297
- return params;
1298
- }
1299
- return cachedSearchParams;
1300
- }
1301
- function getServerSearchParams() {
1302
- const data = getSsrData();
1303
- return data ? new URLSearchParams(data.searchParams) : new URLSearchParams();
1304
- }
1305
- /**
1306
- * Read the current URL search params.
1307
- *
1308
- * Compatible with Next.js's `useSearchParams()` from `next/navigation`.
1309
- */
1310
- function useSearchParams() {
1311
- useSyncExternalStore(subscribe, getSearch, getServerSearch);
1312
- return typeof window !== "undefined" ? getSearchParams() : getServerSearchParams();
1313
- }
1314
- //#endregion
1315
- //#region src/client/segment-context.ts
1316
- /**
1317
- * Segment Context — provides layout segment position for useSelectedLayoutSegment hooks.
1318
- *
1319
- * Each layout in the segment tree is wrapped with a SegmentProvider that stores
1320
- * the URL segments from root to the current layout level. The hooks read this
1321
- * context to determine which child segments are active below the calling layout.
1322
- *
1323
- * The context value is intentionally minimal: just the segment path array and
1324
- * parallel route keys. No internal cache details are exposed.
1325
- *
1326
- * Design docs: design/19-client-navigation.md, design/14-ecosystem.md
1327
- */
1328
- var SegmentContext = createContext(null);
1329
- /** Read the segment context. Returns null if no provider is above this component. */
1330
- function useSegmentContext() {
1331
- return useContext(SegmentContext);
1332
- }
1333
- /**
1334
- * Wraps each layout to provide segment position context.
1335
- * Injected by rsc-entry.ts during element tree construction.
1336
- */
1337
- function SegmentProvider({ segments, segmentId: _segmentId, parallelRouteKeys, children }) {
1338
- const value = useMemo(() => ({
1339
- segments,
1340
- parallelRouteKeys
1341
- }), [segments.join("/"), parallelRouteKeys.join(",")]);
1342
- return createElement(SegmentContext.Provider, { value }, children);
1343
- }
1344
- //#endregion
1345
451
  //#region src/client/use-selected-layout-segment.ts
1346
452
  /**
1347
453
  * useSelectedLayoutSegment / useSelectedLayoutSegments — client-side hooks
@@ -1472,7 +578,13 @@ function useSelectedLayoutSegments(parallelRouteKey) {
1472
578
  * ```
1473
579
  */
1474
580
  function useActionState(action, initialState, permalink) {
1475
- return useActionState$1(action, initialState, permalink);
581
+ const [result, formAction, isPending] = useActionState$1(action, initialState, permalink);
582
+ return [
583
+ result,
584
+ formAction,
585
+ isPending,
586
+ deriveFormErrors(result)
587
+ ];
1476
588
  }
1477
589
  /**
1478
590
  * Hook for calling a server action imperatively (not via a form).
@@ -1498,27 +610,11 @@ function useFormAction(action) {
1498
610
  return [execute, isPending];
1499
611
  }
1500
612
  /**
1501
- * Extract per-field and form-level errors from an ActionResult.
1502
- *
1503
- * Pure function (no internal hooks) — follows React naming convention
1504
- * since it's used in render. Accepts the result from `useActionState`
1505
- * or flash data from `getFormFlash()`.
1506
- *
1507
- * @example
1508
- * ```tsx
1509
- * const [result, action, isPending] = useActionState(createTodo, null)
1510
- * const errors = useFormErrors(result)
1511
- *
1512
- * return (
1513
- * <form action={action}>
1514
- * <input name="title" />
1515
- * {errors.getFieldError('title') && <p>{errors.getFieldError('title')}</p>}
1516
- * {errors.formErrors.map(e => <p key={e}>{e}</p>)}
1517
- * </form>
1518
- * )
1519
- * ```
613
+ * Derive FormErrorsResult from an action result.
614
+ * Used internally by useActionState 4th tuple element.
615
+ * @internal — exported for test access only.
1520
616
  */
1521
- function useFormErrors(result) {
617
+ function deriveFormErrors(result) {
1522
618
  const empty = {
1523
619
  fieldErrors: {},
1524
620
  formErrors: [],
@@ -1547,6 +643,113 @@ function useFormErrors(result) {
1547
643
  };
1548
644
  }
1549
645
  //#endregion
1550
- 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 };
646
+ //#region src/client/use-cookie.ts
647
+ /**
648
+ * useCookie — reactive client-side cookie hook.
649
+ *
650
+ * Uses useSyncExternalStore for SSR-safe, reactive cookie access.
651
+ * All components reading the same cookie name re-render on change.
652
+ * No cross-tab sync (intentional — see design/29-cookies.md).
653
+ *
654
+ * See design/29-cookies.md §"useCookie(name) Hook"
655
+ */
656
+ var use_cookie_exports = /* @__PURE__ */ __exportAll({
657
+ notifyCookieChange: () => notifyCookieChange,
658
+ useCookie: () => useCookie
659
+ });
660
+ /** Per-name subscriber sets. */
661
+ var listeners = /* @__PURE__ */ new Map();
662
+ /** Parse a cookie name from document.cookie. */
663
+ function getCookieValue(name) {
664
+ if (typeof document === "undefined") return void 0;
665
+ const match = document.cookie.match(new RegExp("(?:^|;\\s*)" + name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\s*=\\s*([^;]*)"));
666
+ return match ? decodeURIComponent(match[1]) : void 0;
667
+ }
668
+ /** Serialize options into a cookie string suffix. */
669
+ function serializeOptions(options) {
670
+ if (!options) return "; Path=/; SameSite=Lax";
671
+ const parts = [];
672
+ parts.push(`Path=${options.path ?? "/"}`);
673
+ if (options.domain) parts.push(`Domain=${options.domain}`);
674
+ if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
675
+ if (options.expires) parts.push(`Expires=${options.expires.toUTCString()}`);
676
+ const sameSite = options.sameSite ?? "lax";
677
+ parts.push(`SameSite=${sameSite.charAt(0).toUpperCase()}${sameSite.slice(1)}`);
678
+ if (options.secure) parts.push("Secure");
679
+ return "; " + parts.join("; ");
680
+ }
681
+ /** Notify all subscribers for a given cookie name. */
682
+ function notify(name) {
683
+ const subs = listeners.get(name);
684
+ if (subs) for (const fn of subs) fn();
685
+ }
686
+ /**
687
+ * Notify useCookie subscribers that a cookie value changed.
688
+ * Called by defineCookie's imperative client .set()/.delete() methods
689
+ * so mounted useCookie() consumers re-render.
690
+ *
691
+ * @internal — framework use only
692
+ */
693
+ function notifyCookieChange(name) {
694
+ notify(name);
695
+ }
696
+ /**
697
+ * Reactive hook for reading/writing a client-side cookie.
698
+ *
699
+ * Returns `[value, setCookie, deleteCookie]`:
700
+ * - `value`: current cookie value (string | undefined)
701
+ * - `setCookie`: sets the cookie and triggers re-renders
702
+ * - `deleteCookie`: deletes the cookie and triggers re-renders
703
+ *
704
+ * @param name - Cookie name.
705
+ * @param defaultOptions - Default options for setCookie calls.
706
+ */
707
+ function useCookie(name, defaultOptions) {
708
+ const subscribe = (callback) => {
709
+ let subs = listeners.get(name);
710
+ if (!subs) {
711
+ subs = /* @__PURE__ */ new Set();
712
+ listeners.set(name, subs);
713
+ }
714
+ subs.add(callback);
715
+ return () => {
716
+ subs.delete(callback);
717
+ if (subs.size === 0) listeners.delete(name);
718
+ };
719
+ };
720
+ const getSnapshot = () => getCookieValue(name);
721
+ const getServerSnapshot = () => getSsrData()?.cookies.get(name);
722
+ const value = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
723
+ assertValidCookieName(name);
724
+ const setCookie = (newValue, options) => {
725
+ if (typeof newValue !== "string") throw new Error(`[timber] useCookie(${JSON.stringify(name)}): value must be a string, got ${typeof newValue}.\n To store a JSON-serializable value, use defineCookie + jsonCookieCodec from\n '@timber-js/app/cookies' and call its useCookie() hook instead.`);
726
+ const merged = {
727
+ ...defaultOptions,
728
+ ...options
729
+ };
730
+ document.cookie = `${name}=${encodeURIComponent(newValue)}${serializeOptions(merged)}`;
731
+ notify(name);
732
+ };
733
+ const deleteCookie = () => {
734
+ const path = defaultOptions?.path ?? "/";
735
+ const domain = defaultOptions?.domain;
736
+ let cookieStr = `${name}=; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=${path}`;
737
+ if (domain) cookieStr += `; Domain=${domain}`;
738
+ document.cookie = cookieStr;
739
+ notify(name);
740
+ };
741
+ return [
742
+ value,
743
+ setCookie,
744
+ deleteCookie
745
+ ];
746
+ }
747
+ //#endregion
748
+ //#region src/client/index.ts
749
+ _registerUseCookieModule(use_cookie_exports);
750
+ _registerFromSchema(fromSchema);
751
+ _registerUseSegmentParams(useSegmentParams);
752
+ //#endregion
753
+ export { Link, LinkStatusContext, buildLinkProps, interpolateParams, mergePreservedSearchParams, resolveHref, useActionState, useFormAction, useLinkStatus, usePathname, usePendingNavigation, useRouter, useSegmentParams, useSelectedLayoutSegment, useSelectedLayoutSegments, validateLinkHref };
1551
754
 
1552
755
  //# sourceMappingURL=index.js.map