@timber-js/app 0.2.0-alpha.208 → 0.2.0-alpha.209

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 (520) hide show
  1. package/dist/_chunks/{primitives-DLAnvsrk.js → actions-BerlqoXA.js} +640 -410
  2. package/dist/_chunks/actions-BerlqoXA.js.map +1 -0
  3. package/dist/_chunks/{als-registry-C6kcfprT.js → als-registry-DaxkVjt5.js} +6 -2
  4. package/dist/_chunks/als-registry-DaxkVjt5.js.map +1 -0
  5. package/dist/_chunks/{build-manifest-DTmSGLRz.js → build-manifest-DWppEdLB.js} +2 -51
  6. package/dist/_chunks/build-manifest-DWppEdLB.js.map +1 -0
  7. package/dist/_chunks/{build-output-helper-9dqCv1pZ.js → build-output-helper-RV_BRcF1.js} +50 -53
  8. package/dist/_chunks/build-output-helper-RV_BRcF1.js.map +1 -0
  9. package/dist/_chunks/{cache-api-CUD9Ezaq.js → cache-api-CR23J_NC.js} +64 -32
  10. package/dist/_chunks/cache-api-CR23J_NC.js.map +1 -0
  11. package/dist/_chunks/{chains-B4bgOl2g.js → chains-CpFg56UB.js} +2 -2
  12. package/dist/_chunks/{chains-B4bgOl2g.js.map → chains-CpFg56UB.js.map} +1 -1
  13. package/dist/_chunks/{cli-check-G8o30ldg.js → cli-check-C6Ev6wBO.js} +11 -52
  14. package/dist/_chunks/cli-check-C6Ev6wBO.js.map +1 -0
  15. package/dist/_chunks/cli-context-By7k2xVo.js +70 -0
  16. package/dist/_chunks/cli-context-By7k2xVo.js.map +1 -0
  17. package/dist/_chunks/{cli-schema-sync--LZKs8Zn.js → cli-schema-sync-CbT2AUUI.js} +3 -3
  18. package/dist/_chunks/{cli-schema-sync--LZKs8Zn.js.map → cli-schema-sync-CbT2AUUI.js.map} +1 -1
  19. package/dist/_chunks/{cloudflare-BFb__LYG.js → cloudflare-Byxl8FWP.js} +96 -53
  20. package/dist/_chunks/{cloudflare-BFb__LYG.js.map → cloudflare-Byxl8FWP.js.map} +1 -1
  21. package/dist/_chunks/{convention-lint-5KzoMg_A.js → convention-lint-jKTwKwPe.js} +21 -6
  22. package/dist/_chunks/convention-lint-jKTwKwPe.js.map +1 -0
  23. package/dist/_chunks/{dev-server-BzBxgYAV.js → dev-server-C4WZdB7L.js} +15 -167
  24. package/dist/_chunks/dev-server-C4WZdB7L.js.map +1 -0
  25. package/dist/_chunks/{error-boundary-BrHmG-2X.js → error-boundary-tA7kVfs4.js} +84 -104
  26. package/dist/_chunks/error-boundary-tA7kVfs4.js.map +1 -0
  27. package/dist/_chunks/{fs-identity-D7vb6GdI.js → fs-identity-BRKuL215.js} +3 -4
  28. package/dist/_chunks/fs-identity-BRKuL215.js.map +1 -0
  29. package/dist/_chunks/{graph-cache-CP4GEmf9.js → graph-cache-CYiUS2aS.js} +4 -5
  30. package/dist/_chunks/graph-cache-CYiUS2aS.js.map +1 -0
  31. package/dist/_chunks/{json-lossy-check-ClNvBM_3.js → json-lossy-check-ip0Qi0MT.js} +6 -3
  32. package/dist/_chunks/json-lossy-check-ip0Qi0MT.js.map +1 -0
  33. package/dist/_chunks/{live-graph-Bm5WTvcr.js → live-graph-Dv-JJCZw.js} +3 -3
  34. package/dist/_chunks/{live-graph-Bm5WTvcr.js.map → live-graph-Dv-JJCZw.js.map} +1 -1
  35. package/dist/_chunks/{logger-k2DQ4EUf.js → logger-DiDt5ppH.js} +192 -51
  36. package/dist/_chunks/logger-DiDt5ppH.js.map +1 -0
  37. package/dist/_chunks/{navigation-root-mHSK9psY.js → navigation-root-BQfo1-kG.js} +2 -2
  38. package/dist/_chunks/{navigation-root-mHSK9psY.js.map → navigation-root-BQfo1-kG.js.map} +1 -1
  39. package/dist/_chunks/{plugin-context-8idfR3UZ.js → plugin-context-BPBPc3lL.js} +28 -4
  40. package/dist/_chunks/plugin-context-BPBPc3lL.js.map +1 -0
  41. package/dist/_chunks/{poison-scan-DG5eI2_z.js → poison-scan-CpeT6_OJ.js} +2 -2
  42. package/dist/_chunks/{poison-scan-DG5eI2_z.js.map → poison-scan-CpeT6_OJ.js.map} +1 -1
  43. package/dist/_chunks/render-timeout-DOf_BBcO.js +46 -0
  44. package/dist/_chunks/render-timeout-DOf_BBcO.js.map +1 -0
  45. package/dist/_chunks/{rsc-payload-path-B_LBodc2.js → rsc-payload-path-dffcs4jp.js} +9 -3
  46. package/dist/_chunks/{rsc-payload-path-B_LBodc2.js.map → rsc-payload-path-dffcs4jp.js.map} +1 -1
  47. package/dist/_chunks/{scanner-BQ5sPIJ7.js → scanner-Bw0oq1HB.js} +2 -2
  48. package/dist/_chunks/{scanner-BQ5sPIJ7.js.map → scanner-Bw0oq1HB.js.map} +1 -1
  49. package/dist/_chunks/{navigation-context-D0TU0Jog.js → segment-context-xtPUGfdq.js} +36 -34
  50. package/dist/_chunks/segment-context-xtPUGfdq.js.map +1 -0
  51. package/dist/_chunks/{singleflight-2lUWfcAk.js → singleflight-BeVMMKi0.js} +4 -3
  52. package/dist/_chunks/{singleflight-2lUWfcAk.js.map → singleflight-BeVMMKi0.js.map} +1 -1
  53. package/dist/_chunks/{sizeof-lXffQ1Vr.js → sizeof-UwzwB1uM.js} +3 -2
  54. package/dist/_chunks/sizeof-UwzwB1uM.js.map +1 -0
  55. package/dist/_chunks/ssr-data-nA3I70_n.js +49 -0
  56. package/dist/_chunks/ssr-data-nA3I70_n.js.map +1 -0
  57. package/dist/_chunks/{state-FippDgxN.js → state-BhNGPsdi.js} +6 -8
  58. package/dist/_chunks/state-BhNGPsdi.js.map +1 -0
  59. package/dist/_chunks/{status-page-marker-CNbpUOPB.js → status-page-marker-gaihi0KZ.js} +53 -20
  60. package/dist/_chunks/status-page-marker-gaihi0KZ.js.map +1 -0
  61. package/dist/_chunks/{walkers-CN2N_bA-.js → walkers-BXExhzzk.js} +2 -2
  62. package/dist/_chunks/{walkers-CN2N_bA-.js.map → walkers-BXExhzzk.js.map} +1 -1
  63. package/dist/_chunks/write-file-atomic-D1qWqHb4.js +32 -0
  64. package/dist/_chunks/write-file-atomic-D1qWqHb4.js.map +1 -0
  65. package/dist/adapters/build-output-helper.d.ts +7 -7
  66. package/dist/adapters/build-output-helper.d.ts.map +1 -1
  67. package/dist/adapters/canonicalize-module.d.ts +8 -0
  68. package/dist/adapters/canonicalize-module.d.ts.map +1 -0
  69. package/dist/adapters/cloudflare-dev.js +1 -1
  70. package/dist/adapters/cloudflare-kv-cache.js +2 -2
  71. package/dist/adapters/cloudflare.d.ts +30 -8
  72. package/dist/adapters/cloudflare.d.ts.map +1 -1
  73. package/dist/adapters/cloudflare.js +2 -2
  74. package/dist/adapters/compress-module.d.ts +8 -0
  75. package/dist/adapters/compress-module.d.ts.map +1 -1
  76. package/dist/adapters/fs-identity.d.ts +2 -3
  77. package/dist/adapters/fs-identity.d.ts.map +1 -1
  78. package/dist/adapters/nitro-presets.d.ts +0 -6
  79. package/dist/adapters/nitro-presets.d.ts.map +1 -1
  80. package/dist/adapters/nitro-preview.d.ts +43 -11
  81. package/dist/adapters/nitro-preview.d.ts.map +1 -1
  82. package/dist/adapters/nitro.d.ts +1 -13
  83. package/dist/adapters/nitro.d.ts.map +1 -1
  84. package/dist/adapters/nitro.js +291 -176
  85. package/dist/adapters/nitro.js.map +1 -1
  86. package/dist/adapters/shared.d.ts +7 -2
  87. package/dist/adapters/shared.d.ts.map +1 -1
  88. package/dist/adapters/types.d.ts +105 -21
  89. package/dist/adapters/types.d.ts.map +1 -1
  90. package/dist/adapters/types.js +0 -0
  91. package/dist/analyze/crawl-entry.js +3 -3
  92. package/dist/analyze/graph-cache.d.ts.map +1 -1
  93. package/dist/analyze/graph-command.js +4 -4
  94. package/dist/cache/index.js +2 -2
  95. package/dist/cache/json-lossy-check.d.ts.map +1 -1
  96. package/dist/cache/singleflight.d.ts.map +1 -1
  97. package/dist/cache/sizeof.d.ts.map +1 -1
  98. package/dist/cache/stores/memory.js +1 -1
  99. package/dist/cache/tag-aware-handler.d.ts.map +1 -1
  100. package/dist/cdn/workers-cache-purge.js +1 -1
  101. package/dist/cli-check.d.ts +7 -37
  102. package/dist/cli-check.d.ts.map +1 -1
  103. package/dist/cli-context.d.ts +54 -0
  104. package/dist/cli-context.d.ts.map +1 -0
  105. package/dist/cli.d.ts +9 -9
  106. package/dist/cli.d.ts.map +1 -1
  107. package/dist/cli.js +20 -24
  108. package/dist/cli.js.map +1 -1
  109. package/dist/client/browser-dev.d.ts +1 -1
  110. package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -1
  111. package/dist/client/browser-entry/hydrate.d.ts +19 -29
  112. package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
  113. package/dist/client/browser-entry/index.d.ts +7 -7
  114. package/dist/client/browser-entry/post-hydration.d.ts +6 -9
  115. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
  116. package/dist/client/browser-entry/router-init.d.ts +6 -11
  117. package/dist/client/browser-entry/router-init.d.ts.map +1 -1
  118. package/dist/client/error-boundary.d.ts +4 -3
  119. package/dist/client/error-boundary.d.ts.map +1 -1
  120. package/dist/client/error-boundary.js +1 -1
  121. package/dist/client/error-reconstituter.d.ts +3 -9
  122. package/dist/client/error-reconstituter.d.ts.map +1 -1
  123. package/dist/client/history.d.ts +12 -11
  124. package/dist/client/history.d.ts.map +1 -1
  125. package/dist/client/index.d.ts +0 -1
  126. package/dist/client/index.d.ts.map +1 -1
  127. package/dist/client/index.js +5 -6
  128. package/dist/client/index.js.map +1 -1
  129. package/dist/client/internal.d.ts +3 -3
  130. package/dist/client/internal.d.ts.map +1 -1
  131. package/dist/client/internal.js +68 -104
  132. package/dist/client/internal.js.map +1 -1
  133. package/dist/client/navigation-commit.d.ts +16 -7
  134. package/dist/client/navigation-commit.d.ts.map +1 -1
  135. package/dist/client/navigation-context.d.ts +3 -5
  136. package/dist/client/navigation-context.d.ts.map +1 -1
  137. package/dist/client/react-root.d.ts +3 -2
  138. package/dist/client/react-root.d.ts.map +1 -1
  139. package/dist/client/router-pipeline.d.ts +1 -6
  140. package/dist/client/router-pipeline.d.ts.map +1 -1
  141. package/dist/client/router-types.d.ts +45 -39
  142. package/dist/client/router-types.d.ts.map +1 -1
  143. package/dist/client/router-url.d.ts +24 -0
  144. package/dist/client/router-url.d.ts.map +1 -0
  145. package/dist/client/router.d.ts.map +1 -1
  146. package/dist/client/rsc-fetch.d.ts +8 -4
  147. package/dist/client/rsc-fetch.d.ts.map +1 -1
  148. package/dist/client/ssr-data.d.ts +18 -64
  149. package/dist/client/ssr-data.d.ts.map +1 -1
  150. package/dist/client/state.d.ts +2 -5
  151. package/dist/client/state.d.ts.map +1 -1
  152. package/dist/client/use-pathname.d.ts +1 -1
  153. package/dist/config-types.d.ts +2 -1
  154. package/dist/config-types.d.ts.map +1 -1
  155. package/dist/config-validation.d.ts.map +1 -1
  156. package/dist/cookies/index.js +1 -1
  157. package/dist/dev-tools/dev-page-shell.d.ts +17 -0
  158. package/dist/dev-tools/dev-page-shell.d.ts.map +1 -1
  159. package/dist/dev-tools/index.d.ts +1 -1
  160. package/dist/dev-tools/index.d.ts.map +1 -1
  161. package/dist/dev-tools/instrumentation.d.ts +17 -2
  162. package/dist/dev-tools/instrumentation.d.ts.map +1 -1
  163. package/dist/dev-tools/overlay.d.ts +6 -27
  164. package/dist/dev-tools/overlay.d.ts.map +1 -1
  165. package/dist/index.d.ts.map +1 -1
  166. package/dist/index.js +531 -373
  167. package/dist/index.js.map +1 -1
  168. package/dist/plugin-context.d.ts +26 -40
  169. package/dist/plugin-context.d.ts.map +1 -1
  170. package/dist/plugins/build-manifest.d.ts.map +1 -1
  171. package/dist/plugins/build-report.d.ts +15 -2
  172. package/dist/plugins/build-report.d.ts.map +1 -1
  173. package/dist/plugins/dev-server.d.ts.map +1 -1
  174. package/dist/plugins/entries.d.ts.map +1 -1
  175. package/dist/plugins/fonts.d.ts +19 -1
  176. package/dist/plugins/fonts.d.ts.map +1 -1
  177. package/dist/plugins/post-build.d.ts +110 -0
  178. package/dist/plugins/post-build.d.ts.map +1 -0
  179. package/dist/plugins/prebuilt-capture.d.ts +11 -4
  180. package/dist/plugins/prebuilt-capture.d.ts.map +1 -1
  181. package/dist/plugins/prebuilt.d.ts +17 -1
  182. package/dist/plugins/prebuilt.d.ts.map +1 -1
  183. package/dist/plugins/prerender-sugar.d.ts.map +1 -1
  184. package/dist/plugins/rsc-assets-manifest.d.ts +33 -0
  185. package/dist/plugins/rsc-assets-manifest.d.ts.map +1 -0
  186. package/dist/plugins/shims.d.ts.map +1 -1
  187. package/dist/plugins/static-build.d.ts +32 -9
  188. package/dist/plugins/static-build.d.ts.map +1 -1
  189. package/dist/routing/codegen-write.d.ts +0 -9
  190. package/dist/routing/codegen-write.d.ts.map +1 -1
  191. package/dist/routing/index.js +2 -2
  192. package/dist/routing/segment-codegen.d.ts.map +1 -1
  193. package/dist/server/access-gate.d.ts.map +1 -1
  194. package/dist/server/action-handler.d.ts +5 -4
  195. package/dist/server/action-handler.d.ts.map +1 -1
  196. package/dist/server/actions.d.ts +46 -7
  197. package/dist/server/actions.d.ts.map +1 -1
  198. package/dist/server/als-registry.d.ts +58 -5
  199. package/dist/server/als-registry.d.ts.map +1 -1
  200. package/dist/server/children-interception.d.ts.map +1 -1
  201. package/dist/server/cookie-context.d.ts +2 -23
  202. package/dist/server/cookie-context.d.ts.map +1 -1
  203. package/dist/server/csp-nonce.d.ts +0 -9
  204. package/dist/server/csp-nonce.d.ts.map +1 -1
  205. package/dist/server/csrf.d.ts +27 -4
  206. package/dist/server/csrf.d.ts.map +1 -1
  207. package/dist/server/debug.d.ts +0 -1
  208. package/dist/server/debug.d.ts.map +1 -1
  209. package/dist/server/default-logger.d.ts +2 -1
  210. package/dist/server/default-logger.d.ts.map +1 -1
  211. package/dist/server/deny-boundary.d.ts +12 -6
  212. package/dist/server/deny-boundary.d.ts.map +1 -1
  213. package/dist/server/deny-renderer.d.ts +2 -2
  214. package/dist/server/deny-renderer.d.ts.map +1 -1
  215. package/dist/server/error-boundary-wrapper.d.ts +7 -3
  216. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  217. package/dist/server/fallback-error.d.ts +9 -2
  218. package/dist/server/fallback-error.d.ts.map +1 -1
  219. package/dist/server/head-response.d.ts +4 -4
  220. package/dist/server/html-injectors.d.ts.map +1 -1
  221. package/dist/server/index.d.ts +0 -1
  222. package/dist/server/index.d.ts.map +1 -1
  223. package/dist/server/index.js +3 -4
  224. package/dist/server/index.js.map +1 -1
  225. package/dist/server/instrumentation.d.ts +10 -3
  226. package/dist/server/instrumentation.d.ts.map +1 -1
  227. package/dist/server/internal.d.ts +1 -4
  228. package/dist/server/internal.d.ts.map +1 -1
  229. package/dist/server/internal.js +961 -581
  230. package/dist/server/internal.js.map +1 -1
  231. package/dist/server/json-status-response.d.ts +34 -0
  232. package/dist/server/json-status-response.d.ts.map +1 -0
  233. package/dist/server/logger.d.ts +26 -0
  234. package/dist/server/logger.d.ts.map +1 -1
  235. package/dist/server/param-coercion.d.ts +11 -6
  236. package/dist/server/param-coercion.d.ts.map +1 -1
  237. package/dist/server/pipeline-helpers.d.ts +95 -7
  238. package/dist/server/pipeline-helpers.d.ts.map +1 -1
  239. package/dist/server/pipeline-interception.d.ts +11 -0
  240. package/dist/server/pipeline-interception.d.ts.map +1 -1
  241. package/dist/server/pipeline-outcome.d.ts +11 -1
  242. package/dist/server/pipeline-outcome.d.ts.map +1 -1
  243. package/dist/server/pipeline-phases.d.ts +2 -2
  244. package/dist/server/pipeline-phases.d.ts.map +1 -1
  245. package/dist/server/pipeline.d.ts +23 -35
  246. package/dist/server/pipeline.d.ts.map +1 -1
  247. package/dist/server/prebuilt/capture-render.d.ts +25 -0
  248. package/dist/server/prebuilt/capture-render.d.ts.map +1 -0
  249. package/dist/server/prebuilt/capture-routes.d.ts +37 -0
  250. package/dist/server/prebuilt/capture-routes.d.ts.map +1 -0
  251. package/dist/server/prebuilt/capture-state.d.ts +79 -35
  252. package/dist/server/prebuilt/capture-state.d.ts.map +1 -1
  253. package/dist/server/prebuilt/lifetime.d.ts +38 -0
  254. package/dist/server/prebuilt/lifetime.d.ts.map +1 -0
  255. package/dist/server/prebuilt/slots.d.ts +7 -0
  256. package/dist/server/prebuilt/slots.d.ts.map +1 -1
  257. package/dist/server/prebuilt/synthetic-store.d.ts.map +1 -1
  258. package/dist/server/prebuilt-builder.d.ts.map +1 -1
  259. package/dist/server/prebuilt-runtime.d.ts.map +1 -1
  260. package/dist/server/primitives.d.ts +6 -81
  261. package/dist/server/primitives.d.ts.map +1 -1
  262. package/dist/server/react-cache-bridge.d.ts +59 -0
  263. package/dist/server/react-cache-bridge.d.ts.map +1 -0
  264. package/dist/server/react-cache-scope.d.ts +49 -0
  265. package/dist/server/react-cache-scope.d.ts.map +1 -0
  266. package/dist/server/render-timeout.d.ts +15 -0
  267. package/dist/server/render-timeout.d.ts.map +1 -1
  268. package/dist/server/render-utils.d.ts +9 -0
  269. package/dist/server/render-utils.d.ts.map +1 -1
  270. package/dist/server/request-context.d.ts +26 -5
  271. package/dist/server/request-context.d.ts.map +1 -1
  272. package/dist/server/route-element-builder.d.ts.map +1 -1
  273. package/dist/server/route-handler.d.ts.map +1 -1
  274. package/dist/server/route-matcher.d.ts +7 -0
  275. package/dist/server/route-matcher.d.ts.map +1 -1
  276. package/dist/server/rsc-entry/action-middleware-runner.d.ts +22 -22
  277. package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -1
  278. package/dist/server/rsc-entry/api-handler.d.ts +7 -0
  279. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
  280. package/dist/server/rsc-entry/deny-fallback.d.ts +32 -6
  281. package/dist/server/rsc-entry/deny-fallback.d.ts.map +1 -1
  282. package/dist/server/rsc-entry/error-renderer.d.ts +11 -18
  283. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  284. package/dist/server/rsc-entry/helpers.d.ts +0 -1
  285. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  286. package/dist/server/rsc-entry/index.d.ts +5 -15
  287. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  288. package/dist/server/rsc-entry/render-route.d.ts +5 -6
  289. package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
  290. package/dist/server/rsc-entry/render-signals.d.ts +65 -0
  291. package/dist/server/rsc-entry/render-signals.d.ts.map +1 -0
  292. package/dist/server/rsc-entry/revalidate-renderer.d.ts +6 -16
  293. package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -1
  294. package/dist/server/rsc-entry/rsc-payload.d.ts +7 -3
  295. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  296. package/dist/server/rsc-entry/rsc-stream.d.ts +8 -53
  297. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  298. package/dist/server/rsc-entry/ssr-bridge.d.ts +2 -2
  299. package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
  300. package/dist/server/rsc-entry/ssr-renderer.d.ts +9 -4
  301. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  302. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +1 -1
  303. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -1
  304. package/dist/server/signal-identity.d.ts +43 -0
  305. package/dist/server/signal-identity.d.ts.map +1 -0
  306. package/dist/server/slot-resolver.d.ts +4 -3
  307. package/dist/server/slot-resolver.d.ts.map +1 -1
  308. package/dist/server/slot-subtree-contain.d.ts.map +1 -1
  309. package/dist/server/ssr-bridge-types.d.ts +91 -53
  310. package/dist/server/ssr-bridge-types.d.ts.map +1 -1
  311. package/dist/server/ssr-entry.d.ts +8 -5
  312. package/dist/server/ssr-entry.d.ts.map +1 -1
  313. package/dist/server/ssr-render.d.ts +26 -40
  314. package/dist/server/ssr-render.d.ts.map +1 -1
  315. package/dist/server/ssr-wrappers.d.ts +1 -1
  316. package/dist/server/static-generator.d.ts.map +1 -1
  317. package/dist/server/stream-utils.d.ts +12 -0
  318. package/dist/server/stream-utils.d.ts.map +1 -1
  319. package/dist/server/tracing.d.ts +12 -16
  320. package/dist/server/tracing.d.ts.map +1 -1
  321. package/dist/server/types.d.ts +1 -1
  322. package/dist/server/version-skew.d.ts +3 -3
  323. package/dist/shared/csrf-reject.d.ts +16 -0
  324. package/dist/shared/csrf-reject.d.ts.map +1 -0
  325. package/dist/shared/error-digest.d.ts +28 -0
  326. package/dist/shared/error-digest.d.ts.map +1 -0
  327. package/dist/shared/payload-root.d.ts +11 -10
  328. package/dist/shared/payload-root.d.ts.map +1 -1
  329. package/dist/shared/rsc-payload-path.d.ts +6 -0
  330. package/dist/shared/rsc-payload-path.d.ts.map +1 -1
  331. package/dist/utils/write-file-atomic.d.ts +17 -0
  332. package/dist/utils/write-file-atomic.d.ts.map +1 -0
  333. package/docs/api/30-api-server.mdx +17 -14
  334. package/docs/api/32-api-cache.mdx +51 -23
  335. package/docs/api/34-api-config.mdx +34 -6
  336. package/docs/api/36-cli.mdx +11 -1
  337. package/docs/learn/03-fetching-data.mdx +17 -1
  338. package/docs/learn/04-access-control.mdx +1 -1
  339. package/docs/learn/10-caching.mdx +29 -11
  340. package/docs/learn/11-middleware.mdx +8 -2
  341. package/docs/learn/12-error-handling.mdx +6 -1
  342. package/package.json +5 -1
  343. package/src/adapters/build-output-helper.ts +55 -53
  344. package/src/adapters/canonicalize-module.ts +29 -0
  345. package/src/adapters/cloudflare.ts +105 -63
  346. package/src/adapters/compress-module.ts +16 -5
  347. package/src/adapters/fs-identity.ts +2 -3
  348. package/src/adapters/nitro-presets.ts +0 -15
  349. package/src/adapters/nitro-preview.ts +272 -136
  350. package/src/adapters/nitro.ts +119 -82
  351. package/src/adapters/shared.ts +23 -14
  352. package/src/adapters/types.ts +110 -21
  353. package/src/analyze/graph-cache.ts +3 -4
  354. package/src/cache/json-lossy-check.ts +3 -9
  355. package/src/cache/singleflight.ts +5 -2
  356. package/src/cache/sizeof.ts +3 -1
  357. package/src/cache/tag-aware-handler.ts +43 -26
  358. package/src/cli-check.ts +8 -98
  359. package/src/cli-context.ts +119 -0
  360. package/src/cli.ts +19 -28
  361. package/src/client/browser-dev.ts +1 -1
  362. package/src/client/browser-entry/action-dispatch.ts +26 -4
  363. package/src/client/browser-entry/hydrate.ts +53 -58
  364. package/src/client/browser-entry/index.ts +16 -27
  365. package/src/client/browser-entry/post-hydration.ts +6 -40
  366. package/src/client/browser-entry/router-init.ts +66 -84
  367. package/src/client/error-boundary.tsx +9 -41
  368. package/src/client/error-reconstituter.tsx +10 -5
  369. package/src/client/history.ts +18 -21
  370. package/src/client/index.ts +0 -1
  371. package/src/client/internal.ts +2 -7
  372. package/src/client/navigation-commit.ts +35 -18
  373. package/src/client/navigation-context.ts +3 -39
  374. package/src/client/react-root.ts +3 -2
  375. package/src/client/router-pipeline.ts +65 -128
  376. package/src/client/router-types.ts +46 -44
  377. package/src/client/router-url.ts +32 -0
  378. package/src/client/router.ts +43 -22
  379. package/src/client/rsc-fetch.ts +117 -155
  380. package/src/client/ssr-data.ts +19 -80
  381. package/src/client/state.ts +2 -9
  382. package/src/client/use-pathname.ts +1 -1
  383. package/src/config-types.ts +2 -1
  384. package/src/config-validation.ts +2 -1
  385. package/src/dev-tools/dev-page-shell.ts +18 -0
  386. package/src/dev-tools/index.ts +0 -2
  387. package/src/dev-tools/instrumentation.ts +50 -2
  388. package/src/dev-tools/overlay.ts +8 -238
  389. package/src/index.ts +24 -14
  390. package/src/plugin-context.ts +39 -40
  391. package/src/plugins/build-manifest.ts +4 -11
  392. package/src/plugins/build-report.ts +70 -70
  393. package/src/plugins/dev-server.ts +24 -44
  394. package/src/plugins/entries.ts +2 -1
  395. package/src/plugins/fonts.ts +35 -7
  396. package/src/plugins/post-build.ts +327 -0
  397. package/src/plugins/prebuilt-capture.ts +28 -32
  398. package/src/plugins/prebuilt.ts +54 -39
  399. package/src/plugins/prerender-sugar.ts +0 -4
  400. package/src/plugins/{adapter-build.ts → rsc-assets-manifest.ts} +20 -109
  401. package/src/plugins/shims.ts +1 -0
  402. package/src/plugins/static-build.ts +42 -74
  403. package/src/routing/codegen-write.ts +25 -3
  404. package/src/routing/segment-codegen.ts +3 -2
  405. package/src/rsc-runtime/vendor-types.d.ts +8 -0
  406. package/src/server/access-gate.tsx +2 -1
  407. package/src/server/action-client.ts +1 -1
  408. package/src/server/action-handler.ts +253 -110
  409. package/src/server/actions.ts +73 -25
  410. package/src/server/als-registry.ts +73 -5
  411. package/src/server/chain-url-parts.ts +1 -1
  412. package/src/server/children-interception.ts +2 -1
  413. package/src/server/cookie-context.ts +18 -48
  414. package/src/server/csp-nonce.ts +0 -12
  415. package/src/server/csrf.ts +140 -8
  416. package/src/server/debug.ts +0 -1
  417. package/src/server/default-logger.ts +2 -1
  418. package/src/server/deny-boundary.ts +25 -13
  419. package/src/server/deny-renderer.ts +30 -97
  420. package/src/server/error-boundary-wrapper.ts +7 -3
  421. package/src/server/fallback-error.ts +26 -11
  422. package/src/server/flight-scripts.ts +1 -1
  423. package/src/server/head-response.ts +4 -4
  424. package/src/server/html-injector-core.ts +2 -2
  425. package/src/server/html-injectors.ts +2 -4
  426. package/src/server/index.ts +0 -4
  427. package/src/server/instrumentation.ts +10 -3
  428. package/src/server/internal.ts +1 -6
  429. package/src/server/json-status-response.ts +105 -0
  430. package/src/server/logger.ts +39 -3
  431. package/src/server/param-coercion.ts +15 -12
  432. package/src/server/pipeline-helpers.ts +257 -12
  433. package/src/server/pipeline-interception.ts +133 -0
  434. package/src/server/pipeline-outcome.ts +103 -31
  435. package/src/server/pipeline-phases.ts +127 -237
  436. package/src/server/pipeline.ts +204 -207
  437. package/src/server/prebuilt/capture-render.ts +118 -0
  438. package/src/server/prebuilt/capture-routes.ts +104 -0
  439. package/src/server/prebuilt/capture-state.ts +155 -48
  440. package/src/server/prebuilt/lifetime.ts +151 -0
  441. package/src/server/prebuilt/slots.ts +14 -2
  442. package/src/server/prebuilt/synthetic-store.ts +3 -0
  443. package/src/server/prebuilt-builder.ts +30 -320
  444. package/src/server/prebuilt-runtime.ts +125 -129
  445. package/src/server/primitives.ts +8 -168
  446. package/src/server/react-cache-bridge.ts +124 -0
  447. package/src/server/react-cache-scope.ts +104 -0
  448. package/src/server/render-timeout.ts +20 -0
  449. package/src/server/render-utils.ts +11 -1
  450. package/src/server/request-context.ts +36 -9
  451. package/src/server/route-element-builder.ts +11 -6
  452. package/src/server/route-handler.ts +36 -23
  453. package/src/server/route-matcher.ts +15 -1
  454. package/src/server/rsc-entry/action-middleware-runner.ts +79 -123
  455. package/src/server/rsc-entry/api-handler.ts +27 -103
  456. package/src/server/rsc-entry/deny-fallback.ts +93 -26
  457. package/src/server/rsc-entry/error-renderer.ts +39 -91
  458. package/src/server/rsc-entry/helpers.ts +2 -4
  459. package/src/server/rsc-entry/index.ts +64 -58
  460. package/src/server/rsc-entry/render-route.ts +15 -80
  461. package/src/server/rsc-entry/render-signals.ts +108 -0
  462. package/src/server/rsc-entry/revalidate-renderer.ts +56 -57
  463. package/src/server/rsc-entry/rsc-payload.ts +28 -61
  464. package/src/server/rsc-entry/rsc-stream.ts +113 -213
  465. package/src/server/rsc-entry/ssr-bridge.ts +2 -2
  466. package/src/server/rsc-entry/ssr-renderer.ts +182 -263
  467. package/src/server/rsc-entry/wrap-action-dispatch.ts +12 -14
  468. package/src/server/signal-identity.ts +112 -0
  469. package/src/server/slot-resolver.ts +18 -18
  470. package/src/server/slot-subtree-contain.ts +2 -1
  471. package/src/server/ssr-bridge-types.ts +92 -56
  472. package/src/server/ssr-entry.ts +87 -111
  473. package/src/server/ssr-render.ts +55 -64
  474. package/src/server/ssr-wrappers.tsx +2 -2
  475. package/src/server/static-generator.ts +7 -42
  476. package/src/server/static-not-found.ts +3 -3
  477. package/src/server/stream-utils.ts +51 -0
  478. package/src/server/tracing.ts +58 -75
  479. package/src/server/types.ts +1 -1
  480. package/src/server/version-skew.ts +5 -5
  481. package/src/shared/csrf-reject.ts +15 -0
  482. package/src/shared/error-digest.ts +46 -0
  483. package/src/shared/payload-root.ts +11 -10
  484. package/src/shared/rsc-payload-path.ts +8 -1
  485. package/src/utils/write-file-atomic.ts +32 -0
  486. package/dist/_chunks/actions-jNrXdzgs.js +0 -346
  487. package/dist/_chunks/actions-jNrXdzgs.js.map +0 -1
  488. package/dist/_chunks/als-registry-C6kcfprT.js.map +0 -1
  489. package/dist/_chunks/build-manifest-DTmSGLRz.js.map +0 -1
  490. package/dist/_chunks/build-output-helper-9dqCv1pZ.js.map +0 -1
  491. package/dist/_chunks/cache-api-CUD9Ezaq.js.map +0 -1
  492. package/dist/_chunks/cli-check-G8o30ldg.js.map +0 -1
  493. package/dist/_chunks/convention-lint-5KzoMg_A.js.map +0 -1
  494. package/dist/_chunks/dev-server-BzBxgYAV.js.map +0 -1
  495. package/dist/_chunks/error-boundary-BrHmG-2X.js.map +0 -1
  496. package/dist/_chunks/fs-identity-D7vb6GdI.js.map +0 -1
  497. package/dist/_chunks/graph-cache-CP4GEmf9.js.map +0 -1
  498. package/dist/_chunks/json-lossy-check-ClNvBM_3.js.map +0 -1
  499. package/dist/_chunks/logger-k2DQ4EUf.js.map +0 -1
  500. package/dist/_chunks/navigation-context-D0TU0Jog.js.map +0 -1
  501. package/dist/_chunks/plugin-context-8idfR3UZ.js.map +0 -1
  502. package/dist/_chunks/primitives-DLAnvsrk.js.map +0 -1
  503. package/dist/_chunks/segment-context-D9_89u34.js +0 -34
  504. package/dist/_chunks/segment-context-D9_89u34.js.map +0 -1
  505. package/dist/_chunks/sizeof-lXffQ1Vr.js.map +0 -1
  506. package/dist/_chunks/ssr-data-D6T6Y3ef.js +0 -60
  507. package/dist/_chunks/ssr-data-D6T6Y3ef.js.map +0 -1
  508. package/dist/_chunks/state-FippDgxN.js.map +0 -1
  509. package/dist/_chunks/status-page-marker-CNbpUOPB.js.map +0 -1
  510. package/dist/client/types.d.ts +0 -14
  511. package/dist/client/types.d.ts.map +0 -1
  512. package/dist/plugins/adapter-build.d.ts +0 -38
  513. package/dist/plugins/adapter-build.d.ts.map +0 -1
  514. package/dist/server/asset-headers.d.ts +0 -32
  515. package/dist/server/asset-headers.d.ts.map +0 -1
  516. package/dist/server/dev-source-map.d.ts +0 -22
  517. package/dist/server/dev-source-map.d.ts.map +0 -1
  518. package/src/client/types.ts +0 -21
  519. package/src/server/asset-headers.ts +0 -59
  520. package/src/server/dev-source-map.ts +0 -31
@@ -1,4 +1,4 @@
1
- import { createContext, createElement, useContext } from "react";
1
+ import { createContext, createElement, useContext, useMemo } from "react";
2
2
  //#region src/client/navigation-context.ts
3
3
  /**
4
4
  * NavigationContext — React context for navigation state.
@@ -12,8 +12,8 @@ import { createContext, createElement, useContext } from "react";
12
12
  *
13
13
  * Segment params are NOT here — see the note on NavigationState below.
14
14
  *
15
- * By wrapping the RSC payload element in NavigationProvider inside
16
- * renderRoot(), the context value and the element tree are passed to
15
+ * By wrapping the RSC payload element in NavigationProvider inside the
16
+ * router's `renderTree` (browser-entry/router-init.ts), the context value and the element tree are passed to
17
17
  * reactRoot.render() in the same call — atomic by construction.
18
18
  * All consumers (usePathname, useSearchParams) see the new values in the
19
19
  * same render pass as the new tree.
@@ -76,41 +76,13 @@ function useNavigationContext() {
76
76
  /**
77
77
  * Wraps children with NavigationContext.Provider.
78
78
  *
79
- * Used in browser-entry.ts renderRoot to wrap the RSC payload element
79
+ * Used in browser-entry/router-init.ts `renderTree` to wrap the RSC payload element
80
80
  * so that navigation state updates atomically with the tree render.
81
81
  */
82
82
  function NavigationProvider({ value, children }) {
83
83
  return createElement(getOrCreateContext().Provider, { value }, children);
84
84
  }
85
85
  /**
86
- * Navigation state communicated between the router and renderRoot.
87
- *
88
- * The router calls setNavigationState() before renderRoot(). The
89
- * renderRoot callback reads via getNavigationState() to create the
90
- * NavigationProvider with the correct params/pathname.
91
- *
92
- * This is NOT used by hooks directly — hooks read from React context.
93
- *
94
- * Stored on globalThis (like the context instances above) because the
95
- * router lives in one chunk while renderRoot lives in another. Module-
96
- * level variables would be separate per chunk.
97
- */
98
- var NAV_STATE_KEY = Symbol.for("__timber_nav_state");
99
- function _getNavStateStore() {
100
- const g = globalThis;
101
- if (!g[NAV_STATE_KEY]) g[NAV_STATE_KEY] = { current: {
102
- pathname: "/",
103
- search: ""
104
- } };
105
- return g[NAV_STATE_KEY];
106
- }
107
- function setNavigationState(state) {
108
- _getNavStateStore().current = state;
109
- }
110
- function getNavigationState() {
111
- return _getNavStateStore().current;
112
- }
113
- /**
114
86
  * There was a second React context here — `PendingNavigationContext`, holding
115
87
  * the in-flight navigation URL, provided by `NavigationRoot` out of a
116
88
  * `useState` — plus a `usePendingNavigationUrl()` reader for it. Both are gone
@@ -130,6 +102,36 @@ function getNavigationState() {
130
102
  * design/19-client-navigation.md §"How Pending State Works".
131
103
  */
132
104
  //#endregion
133
- export { useNavigationContext as i, getNavigationState as n, setNavigationState as r, NavigationProvider as t };
105
+ //#region src/client/segment-context.ts
106
+ /**
107
+ * Segment Context — provides layout segment position for useSelectedLayoutSegment hooks.
108
+ *
109
+ * Each layout in the segment tree is wrapped with a SegmentProvider that stores
110
+ * the URL segments from root to the current layout level. The hooks read this
111
+ * context to determine which child segments are active below the calling layout.
112
+ *
113
+ * The context value is intentionally minimal: just the segment path array and
114
+ * parallel route keys. No internal cache details are exposed.
115
+ *
116
+ * Design docs: design/19-client-navigation.md, design/14-ecosystem.md
117
+ */
118
+ var SegmentContext = createContext(null);
119
+ /** Read the segment context. Returns null if no provider is above this component. */
120
+ function useSegmentContext() {
121
+ return useContext(SegmentContext);
122
+ }
123
+ /**
124
+ * Wraps each layout to provide segment position context.
125
+ * Injected by rsc-entry.ts during element tree construction.
126
+ */
127
+ function SegmentProvider({ segments, segmentId: _segmentId, parallelRouteKeys, children }) {
128
+ const value = useMemo(() => ({
129
+ segments,
130
+ parallelRouteKeys
131
+ }), [segments, parallelRouteKeys]);
132
+ return createElement(SegmentContext.Provider, { value }, children);
133
+ }
134
+ //#endregion
135
+ export { useNavigationContext as i, useSegmentContext as n, NavigationProvider as r, SegmentProvider as t };
134
136
 
135
- //# sourceMappingURL=navigation-context-D0TU0Jog.js.map
137
+ //# sourceMappingURL=segment-context-xtPUGfdq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"segment-context-xtPUGfdq.js","names":[],"sources":["../../src/client/navigation-context.ts","../../src/client/segment-context.ts"],"sourcesContent":["'use client';\n\n/**\n * NavigationContext — React context for navigation state.\n *\n * Holds the current pathname and search, updated atomically with the RSC\n * tree on each navigation. This replaces the previous useSyncExternalStore\n * approach for usePathname() and useSearchParams(), which suffered from a\n * timing gap: the new tree could commit before the external store\n * re-renders fired, causing a frame where both old and new active states\n * were visible simultaneously.\n *\n * Segment params are NOT here — see the note on NavigationState below.\n *\n * By wrapping the RSC payload element in NavigationProvider inside the\n * router's `renderTree` (browser-entry/router-init.ts), the context value and the element tree are passed to\n * reactRoot.render() in the same call — atomic by construction.\n * All consumers (usePathname, useSearchParams) see the new values in the\n * same render pass as the new tree.\n *\n * During SSR, the wrapper chain mounts this same provider with the\n * request's pathname and raw search string (TIM-1424), so the hooks\n * resolve through the identical code path on both sides. Per-request\n * isolation is by construction — the value is part of the request's\n * element tree.\n *\n * This module is `'use client'` and is never evaluated in the RSC\n * environment. The shims plugin resolves next/navigation to\n * navigation-rsc.ts in RSC, which has throwing stubs (TIM-1420).\n *\n * SINGLETON GUARANTEE: All shared mutable state uses globalThis via\n * Symbol.for keys. The RSC client bundler can duplicate this module\n * across chunks (browser-entry graph + client-reference graph). With\n * ESM output, each chunk gets its own module scope — module-level\n * variables would create separate singleton instances per chunk.\n * globalThis guarantees a single instance regardless of duplication.\n *\n * This workaround will be removed when Rolldown ships `format: 'app'`\n * (module registry format that deduplicates like webpack/Turbopack).\n * See design/27-chunking-strategy.md.\n *\n * See design/19-client-navigation.md §\"NavigationContext\"\n */\n\nimport { createContext, useContext, createElement, type ReactNode } from 'react';\n\n// ---------------------------------------------------------------------------\n// Context type\n// ---------------------------------------------------------------------------\n\n/**\n * What the browser knows about the current location.\n *\n * Params are deliberately absent: they are a property of the tree the server\n * rendered, not of the address bar, and they now travel inside the payload as\n * the payload root. Keeping a copy here meant the router had to thread\n * the record from a response header into context on every path — navigation,\n * traversal, prefetch, revalidation — and each of those was a place to drop it\n * (TIM-1294).\n */\nexport interface NavigationState {\n pathname: string;\n search: string;\n}\n\n// ---------------------------------------------------------------------------\n// Lazy context initialization\n// ---------------------------------------------------------------------------\n\n/**\n * Context instances are stored on globalThis (NOT in module-level\n * variables) because the ESM bundler can duplicate this module across\n * chunks. Module-level variables would create separate instances per\n * chunk — the provider in NavigationRoot (index chunk) would use\n * context A while the consumer in usePendingNavigation (shared chunk)\n * reads from context B. globalThis guarantees a single instance.\n *\n * This module is `'use client'` so it is never evaluated in the RSC\n * environment — `createContext` and `useContext` are always available.\n * The shims plugin resolves next/navigation to navigation-rsc.ts in RSC,\n * which has throwing stubs for the hooks (TIM-1420).\n *\n * See design/27-chunking-strategy.md §\"Singleton Safety\"\n */\n\nconst NAV_CTX_KEY = Symbol.for('__timber_nav_ctx');\n\nfunction getOrCreateContext(): import('react').Context<NavigationState | null> {\n const existing = (globalThis as Record<symbol, unknown>)[NAV_CTX_KEY] as\n | import('react').Context<NavigationState | null>\n | undefined;\n if (existing !== undefined) return existing;\n const ctx = createContext<NavigationState | null>(null);\n (globalThis as Record<symbol, unknown>)[NAV_CTX_KEY] = ctx;\n return ctx;\n}\n\n/**\n * Read the navigation context. Returns null only when no provider is above\n * the caller — a component rendered outside the timber app.\n *\n * Internal — used by usePathname() and useSearchParams().\n */\nexport function useNavigationContext(): NavigationState | null {\n return useContext(getOrCreateContext());\n}\n\n// ---------------------------------------------------------------------------\n// Provider component\n// ---------------------------------------------------------------------------\n\nexport interface NavigationProviderProps {\n value: NavigationState;\n children?: ReactNode;\n}\n\n/**\n * Wraps children with NavigationContext.Provider.\n *\n * Used in browser-entry/router-init.ts `renderTree` to wrap the RSC payload element\n * so that navigation state updates atomically with the tree render.\n */\nexport function NavigationProvider({\n value,\n children,\n}: NavigationProviderProps): import('react').ReactElement {\n return createElement(getOrCreateContext().Provider, { value }, children);\n}\n\n// ---------------------------------------------------------------------------\n// Pending navigation state lives in the router, not here\n// ---------------------------------------------------------------------------\n\n/**\n * There was a second React context here — `PendingNavigationContext`, holding\n * the in-flight navigation URL, provided by `NavigationRoot` out of a\n * `useState` — plus a `usePendingNavigationUrl()` reader for it. Both are gone\n * (TIM-1307).\n *\n * Nothing read them. `usePendingNavigation()` (use-pending-navigation.ts) and\n * `TopLoader` both subscribe to the router's external pending store via\n * `useSyncExternalStore`: sync priority, immune to transition entanglement,\n * and cleared by `runNavigation`'s supersession-guarded `finally` (TIM-1034).\n * The context was a parallel representation of the same fact that no consumer\n * ever migrated to, and one that could not even agree with the store — a\n * navigation superseded by a cached popstate replay left its URL set until the\n * next full navigation, because the staleness guard skipped the clear and no\n * other path touched it.\n *\n * One representation, and it is the router's. See\n * design/19-client-navigation.md §\"How Pending State Works\".\n */\n","/**\n * Segment Context — provides layout segment position for useSelectedLayoutSegment hooks.\n *\n * Each layout in the segment tree is wrapped with a SegmentProvider that stores\n * the URL segments from root to the current layout level. The hooks read this\n * context to determine which child segments are active below the calling layout.\n *\n * The context value is intentionally minimal: just the segment path array and\n * parallel route keys. No internal cache details are exposed.\n *\n * Design docs: design/19-client-navigation.md, design/14-ecosystem.md\n */\n\n'use client';\n\nimport { createContext, useContext, createElement, useMemo } from 'react';\n\n// ─── Types ───────────────────────────────────────────────────────\n\nexport interface SegmentContextValue {\n /** URL segments from root to this layout (e.g. ['', 'dashboard', 'settings']) */\n segments: string[];\n /** Parallel route slot keys available at this layout level (e.g. ['sidebar', 'modal']) */\n parallelRouteKeys: string[];\n}\n\n// ─── Context ─────────────────────────────────────────────────────\n\nconst SegmentContext = createContext<SegmentContextValue | null>(null);\n\n/** Read the segment context. Returns null if no provider is above this component. */\nexport function useSegmentContext(): SegmentContextValue | null {\n return useContext(SegmentContext);\n}\n\n// ─── Provider ────────────────────────────────────────────────────\n\ninterface SegmentProviderProps {\n segments: string[];\n /**\n * Unique identifier for this segment, used by the client-side segment\n * merger for element caching. For route groups this includes the group\n * name (e.g., \"/(marketing)\") since groups share their parent's urlPath.\n * Falls back to the reconstructed path from `segments` if not provided.\n */\n segmentId?: string;\n parallelRouteKeys: string[];\n children: React.ReactNode;\n}\n\n/**\n * Wraps each layout to provide segment position context.\n * Injected by rsc-entry.ts during element tree construction.\n */\nexport function SegmentProvider({\n segments,\n segmentId: _segmentId,\n parallelRouteKeys,\n children,\n}: SegmentProviderProps) {\n const value = useMemo(\n () => ({ segments, parallelRouteKeys }),\n // segments and parallelRouteKeys are static per layout — the layout's\n // position in the tree is fixed. The arrays are used directly as deps\n // (React Compiler requires simple expressions here). Referential stability\n // of the context value doesn't matter: every consumer\n // (useSelectedLayoutSegment/useSelectedLayoutSegments) also calls\n // usePathname(), so it re-renders on every navigation regardless.\n [segments, parallelRouteKeys]\n );\n return createElement(SegmentContext.Provider, { value }, children);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqFA,IAAM,cAAc,OAAO,IAAI,kBAAkB;AAEjD,SAAS,qBAAsE;CAC7E,MAAM,WAAY,WAAuC;CAGzD,IAAI,aAAa,KAAA,GAAW,OAAO;CACnC,MAAM,MAAM,cAAsC,IAAI;CACtD,WAAwC,eAAe;CACvD,OAAO;AACT;;;;;;;AAQA,SAAgB,uBAA+C;CAC7D,OAAO,WAAW,mBAAmB,CAAC;AACxC;;;;;;;AAiBA,SAAgB,mBAAmB,EACjC,OACA,YACwD;CACxD,OAAO,cAAc,mBAAmB,CAAC,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ;AACzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACnGA,IAAM,iBAAiB,cAA0C,IAAI;;AAGrE,SAAgB,oBAAgD;CAC9D,OAAO,WAAW,cAAc;AAClC;;;;;AAqBA,SAAgB,gBAAgB,EAC9B,UACA,WAAW,YACX,mBACA,YACuB;CACvB,MAAM,QAAQ,eACL;EAAE;EAAU;CAAkB,IAOrC,CAAC,UAAU,iBAAiB,CAC9B;CACA,OAAO,cAAc,eAAe,UAAU,EAAE,MAAM,GAAG,QAAQ;AACnE"}
@@ -20,8 +20,9 @@ function createSingleflight(opts) {
20
20
  let promise;
21
21
  if (timeoutMs != null && timeoutMs > 0) promise = new Promise((resolve, reject) => {
22
22
  const timer = setTimeout(() => {
23
- ac.abort();
24
- reject(new SingleflightTimeoutError(key, timeoutMs));
23
+ const timeout = new SingleflightTimeoutError(key, timeoutMs);
24
+ ac.abort(timeout);
25
+ reject(timeout);
25
26
  }, timeoutMs);
26
27
  try {
27
28
  fn(ac.signal).then((value) => {
@@ -51,4 +52,4 @@ function createSingleflight(opts) {
51
52
  //#endregion
52
53
  export { createSingleflight as n, SingleflightTimeoutError as t };
53
54
 
54
- //# sourceMappingURL=singleflight-2lUWfcAk.js.map
55
+ //# sourceMappingURL=singleflight-BeVMMKi0.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"singleflight-2lUWfcAk.js","names":[],"sources":["../../src/cache/singleflight.ts"],"sourcesContent":["/**\n * Singleflight coalesces concurrent calls with the same key into a single\n * execution. All callers receive the same result (or error).\n *\n * Per-process, in-memory. Each process coalesces independently.\n *\n * An optional `timeoutMs` prevents hung `fn()` calls from permanently\n * blocking all future callers for that key. When set, `fn()` is raced\n * against a timeout — if the timeout fires first, the promise rejects\n * with `SingleflightTimeoutError`, `finally` cleans up the key, and\n * subsequent callers can retry. See TIM-518.\n */\n\nexport interface SingleflightOptions {\n /** Maximum time (ms) a coalesced call may run before being rejected. */\n timeoutMs?: number;\n}\n\nexport interface Singleflight {\n do<T>(key: string, fn: (signal: AbortSignal) => Promise<T>): Promise<T>;\n /** Return the in-flight promise for a key, or undefined if none. */\n get(key: string): Promise<unknown> | undefined;\n}\n\n/**\n * Error thrown when a singleflight call exceeds `timeoutMs`.\n * Exported so callers can distinguish timeout from other errors.\n */\nexport class SingleflightTimeoutError extends Error {\n constructor(key: string, timeoutMs: number) {\n super(`Singleflight timeout: key \"${key}\" exceeded ${timeoutMs}ms`);\n this.name = 'SingleflightTimeoutError';\n }\n}\n\nexport function createSingleflight(opts?: SingleflightOptions): Singleflight {\n const inflight = new Map<string, Promise<unknown>>();\n const timeoutMs = opts?.timeoutMs;\n\n return {\n do<T>(key: string, fn: (signal: AbortSignal) => Promise<T>): Promise<T> {\n const existing = inflight.get(key);\n if (existing) return existing as Promise<T>;\n\n const ac = new AbortController();\n let promise: Promise<T>;\n\n if (timeoutMs != null && timeoutMs > 0) {\n // Race fn() against a timeout to prevent hung calls from\n // permanently blocking the key. See TIM-518.\n // Abort the signal on timeout so the callback can skip\n // side effects (e.g. cache writes). See TIM-1092.\n promise = new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n ac.abort();\n reject(new SingleflightTimeoutError(key, timeoutMs));\n }, timeoutMs);\n try {\n fn(ac.signal).then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (err) => {\n clearTimeout(timer);\n reject(err);\n }\n );\n } catch (err) {\n clearTimeout(timer);\n reject(err);\n }\n });\n } else {\n promise = fn(ac.signal);\n }\n\n const tracked = promise.finally(() => {\n inflight.delete(key);\n });\n\n inflight.set(key, tracked);\n return tracked as Promise<T>;\n },\n get(key: string): Promise<unknown> | undefined {\n return inflight.get(key);\n },\n };\n}\n"],"mappings":";;;;;AA4BA,IAAa,2BAAb,cAA8C,MAAM;CAClD,YAAY,KAAa,WAAmB;EAC1C,MAAM,8BAA8B,IAAI,aAAa,UAAU,GAAG;EAClE,KAAK,OAAO;CACd;AACF;AAEA,SAAgB,mBAAmB,MAA0C;CAC3E,MAAM,2BAAW,IAAI,IAA8B;CACnD,MAAM,YAAY,MAAM;CAExB,OAAO;EACL,GAAM,KAAa,IAAqD;GACtE,MAAM,WAAW,SAAS,IAAI,GAAG;GACjC,IAAI,UAAU,OAAO;GAErB,MAAM,KAAK,IAAI,gBAAgB;GAC/B,IAAI;GAEJ,IAAI,aAAa,QAAQ,YAAY,GAKnC,UAAU,IAAI,SAAY,SAAS,WAAW;IAC5C,MAAM,QAAQ,iBAAiB;KAC7B,GAAG,MAAM;KACT,OAAO,IAAI,yBAAyB,KAAK,SAAS,CAAC;IACrD,GAAG,SAAS;IACZ,IAAI;KACF,GAAG,GAAG,MAAM,CAAC,CAAC,MACX,UAAU;MACT,aAAa,KAAK;MAClB,QAAQ,KAAK;KACf,IACC,QAAQ;MACP,aAAa,KAAK;MAClB,OAAO,GAAG;KACZ,CACF;IACF,SAAS,KAAK;KACZ,aAAa,KAAK;KAClB,OAAO,GAAG;IACZ;GACF,CAAC;QAED,UAAU,GAAG,GAAG,MAAM;GAGxB,MAAM,UAAU,QAAQ,cAAc;IACpC,SAAS,OAAO,GAAG;GACrB,CAAC;GAED,SAAS,IAAI,KAAK,OAAO;GACzB,OAAO;EACT;EACA,IAAI,KAA2C;GAC7C,OAAO,SAAS,IAAI,GAAG;EACzB;CACF;AACF"}
1
+ {"version":3,"file":"singleflight-BeVMMKi0.js","names":[],"sources":["../../src/cache/singleflight.ts"],"sourcesContent":["/**\n * Singleflight coalesces concurrent calls with the same key into a single\n * execution. All callers receive the same result (or error).\n *\n * Per-process, in-memory. Each process coalesces independently.\n *\n * An optional `timeoutMs` prevents hung `fn()` calls from permanently\n * blocking all future callers for that key. When set, `fn()` is raced\n * against a timeout — if the timeout fires first, the promise rejects\n * with `SingleflightTimeoutError`, `finally` cleans up the key, and\n * subsequent callers can retry. See TIM-518.\n */\n\nexport interface SingleflightOptions {\n /** Maximum time (ms) a coalesced call may run before being rejected. */\n timeoutMs?: number;\n}\n\nexport interface Singleflight {\n do<T>(key: string, fn: (signal: AbortSignal) => Promise<T>): Promise<T>;\n /** Return the in-flight promise for a key, or undefined if none. */\n get(key: string): Promise<unknown> | undefined;\n}\n\n/**\n * Error thrown when a singleflight call exceeds `timeoutMs`.\n * Exported so callers can distinguish timeout from other errors.\n */\nexport class SingleflightTimeoutError extends Error {\n constructor(key: string, timeoutMs: number) {\n super(`Singleflight timeout: key \"${key}\" exceeded ${timeoutMs}ms`);\n this.name = 'SingleflightTimeoutError';\n }\n}\n\nexport function createSingleflight(opts?: SingleflightOptions): Singleflight {\n const inflight = new Map<string, Promise<unknown>>();\n const timeoutMs = opts?.timeoutMs;\n\n return {\n do<T>(key: string, fn: (signal: AbortSignal) => Promise<T>): Promise<T> {\n const existing = inflight.get(key);\n if (existing) return existing as Promise<T>;\n\n const ac = new AbortController();\n let promise: Promise<T>;\n\n if (timeoutMs != null && timeoutMs > 0) {\n // Race fn() against a timeout to prevent hung calls from\n // permanently blocking the key. See TIM-518.\n // Abort the signal on timeout so the callback can skip\n // side effects (e.g. cache writes). See TIM-1092.\n promise = new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n // The timeout error is also the signal's reason, so the body\n // can tell why it was aborted (runBackgroundFlight reports it).\n const timeout = new SingleflightTimeoutError(key, timeoutMs);\n ac.abort(timeout);\n reject(timeout);\n }, timeoutMs);\n try {\n fn(ac.signal).then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (err) => {\n clearTimeout(timer);\n reject(err);\n }\n );\n } catch (err) {\n clearTimeout(timer);\n reject(err);\n }\n });\n } else {\n promise = fn(ac.signal);\n }\n\n const tracked = promise.finally(() => {\n inflight.delete(key);\n });\n\n inflight.set(key, tracked);\n return tracked as Promise<T>;\n },\n get(key: string): Promise<unknown> | undefined {\n return inflight.get(key);\n },\n };\n}\n"],"mappings":";;;;;AA4BA,IAAa,2BAAb,cAA8C,MAAM;CAClD,YAAY,KAAa,WAAmB;EAC1C,MAAM,8BAA8B,IAAI,aAAa,UAAU,GAAG;EAClE,KAAK,OAAO;CACd;AACF;AAEA,SAAgB,mBAAmB,MAA0C;CAC3E,MAAM,2BAAW,IAAI,IAA8B;CACnD,MAAM,YAAY,MAAM;CAExB,OAAO;EACL,GAAM,KAAa,IAAqD;GACtE,MAAM,WAAW,SAAS,IAAI,GAAG;GACjC,IAAI,UAAU,OAAO;GAErB,MAAM,KAAK,IAAI,gBAAgB;GAC/B,IAAI;GAEJ,IAAI,aAAa,QAAQ,YAAY,GAKnC,UAAU,IAAI,SAAY,SAAS,WAAW;IAC5C,MAAM,QAAQ,iBAAiB;KAG7B,MAAM,UAAU,IAAI,yBAAyB,KAAK,SAAS;KAC3D,GAAG,MAAM,OAAO;KAChB,OAAO,OAAO;IAChB,GAAG,SAAS;IACZ,IAAI;KACF,GAAG,GAAG,MAAM,CAAC,CAAC,MACX,UAAU;MACT,aAAa,KAAK;MAClB,QAAQ,KAAK;KACf,IACC,QAAQ;MACP,aAAa,KAAK;MAClB,OAAO,GAAG;KACZ,CACF;IACF,SAAS,KAAK;KACZ,aAAa,KAAK;KAClB,OAAO,GAAG;IACZ;GACF,CAAC;QAED,UAAU,GAAG,GAAG,MAAM;GAGxB,MAAM,UAAU,QAAQ,cAAc;IACpC,SAAS,OAAO,GAAG;GACrB,CAAC;GAED,SAAS,IAAI,KAAK,OAAO;GACzB,OAAO;EACT;EACA,IAAI,KAA2C;GAC7C,OAAO,SAAS,IAAI,GAAG;EACzB;CACF;AACF"}
@@ -1,3 +1,4 @@
1
+ import { _ as swallow } from "./logger-DiDt5ppH.js";
1
2
  //#region src/cache/sizeof.ts
2
3
  /**
3
4
  * Lightweight byte-size estimation for cache entries.
@@ -25,11 +26,11 @@ function estimateByteSize(value) {
25
26
  if (json === void 0) return 0;
26
27
  return json.length * 2;
27
28
  } catch (e) {
28
- console.debug("[timber] sizeof estimation failed:", e instanceof Error ? e.message : e);
29
+ swallow(e, "sizeof estimation failed", { level: "debug" });
29
30
  return 0;
30
31
  }
31
32
  }
32
33
  //#endregion
33
34
  export { estimateByteSize as t };
34
35
 
35
- //# sourceMappingURL=sizeof-lXffQ1Vr.js.map
36
+ //# sourceMappingURL=sizeof-UwzwB1uM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sizeof-UwzwB1uM.js","names":[],"sources":["../../src/cache/sizeof.ts"],"sourcesContent":["/**\n * Lightweight byte-size estimation for cache entries.\n *\n * Estimates the in-memory byte cost of a JavaScript value using\n * JSON.stringify().length * 2 (UTF-16 char width). This is a rough\n * approximation — it doesn't account for V8 object overhead, Map\n * metadata, or non-serializable values — but it's fast and good\n * enough for cache budget enforcement.\n *\n * Values that fail JSON serialization (circular references, BigInt,\n * etc.) return 0, allowing the entry to be cached without counting\n * toward the byte budget. This is a conservative choice: it's better\n * to cache and undercount than to reject the entry.\n */\n\nimport { swallow } from '../server/logger.ts';\n\n/**\n * Estimate the byte size of a value.\n *\n * Uses `JSON.stringify(value).length * 2` to approximate UTF-16\n * in-memory size. Returns 0 if the value is not serializable.\n */\nexport function estimateByteSize(value: unknown): number {\n try {\n const json = JSON.stringify(value);\n if (json === undefined) return 0;\n // Each JS string character is 2 bytes (UTF-16)\n return json.length * 2;\n } catch (e) {\n // Circular reference or non-serializable value\n swallow(e, 'sizeof estimation failed', { level: 'debug' });\n return 0;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,iBAAiB,OAAwB;CACvD,IAAI;EACF,MAAM,OAAO,KAAK,UAAU,KAAK;EACjC,IAAI,SAAS,KAAA,GAAW,OAAO;EAE/B,OAAO,KAAK,SAAS;CACvB,SAAS,GAAG;EAEV,QAAQ,GAAG,4BAA4B,EAAE,OAAO,QAAQ,CAAC;EACzD,OAAO;CACT;AACF"}
@@ -0,0 +1,49 @@
1
+ import { d as ssrDataProvider, i as _setSsrDataProvider } from "./state-BhNGPsdi.js";
2
+ //#region src/client/ssr-data.ts
3
+ /**
4
+ * SSR Data — per-request state for client hooks during server-side rendering.
5
+ *
6
+ * `useCookie()` and `<Link preserveSearchParams>` have no React context to
7
+ * read their request values from during SSR, so the SSR entry hands them
8
+ * the request's cookies and search string through this module.
9
+ * `useSearchParams()` reads NavigationContext and uses getSsrData() only to
10
+ * detect that it is rendering on the server. Route
11
+ * params do not travel here — client components read them from
12
+ * `ParamsContext`, decoded from the payload root.
13
+ *
14
+ * Request isolation: ssr-entry.ts registers an ALS-backed provider via
15
+ * registerSsrDataProvider(), so getSsrData() reads the current request's
16
+ * store even when Suspense boundaries resolve asynchronously across
17
+ * concurrent requests. There is no module-level fallback: outside an SSR
18
+ * render (in the browser, or before the provider is registered) there is no
19
+ * request, and getSsrData() returns undefined.
20
+ *
21
+ * IMPORTANT: This module must NOT import node:async_hooks or any Node.js-only
22
+ * APIs, as it's imported by 'use client' hooks that are bundled for the browser.
23
+ * The ALS instance lives in ssr-entry.ts (server-only); this module only holds
24
+ * a reference to the provider function.
25
+ *
26
+ * All mutable state is delegated to client/state.ts for singleton guarantees.
27
+ * See design/18-build-system.md §"Singleton State Registry"
28
+ */
29
+ /**
30
+ * Register the SSR data provider. Called once at module load by
31
+ * ssr-entry.ts to wire up per-request data via AsyncLocalStorage; tests
32
+ * register their own provider (and `undefined` to reset).
33
+ */
34
+ function registerSsrDataProvider(provider) {
35
+ _setSsrDataProvider(provider);
36
+ }
37
+ /**
38
+ * Read the current request's SSR data. Returns undefined when called
39
+ * outside an SSR render (i.e. on the client after hydration).
40
+ *
41
+ * Used by client hooks' server snapshot functions.
42
+ */
43
+ function getSsrData() {
44
+ return ssrDataProvider?.();
45
+ }
46
+ //#endregion
47
+ export { registerSsrDataProvider as n, getSsrData as t };
48
+
49
+ //# sourceMappingURL=ssr-data-nA3I70_n.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssr-data-nA3I70_n.js","names":[],"sources":["../../src/client/ssr-data.ts"],"sourcesContent":["/**\n * SSR Data — per-request state for client hooks during server-side rendering.\n *\n * `useCookie()` and `<Link preserveSearchParams>` have no React context to\n * read their request values from during SSR, so the SSR entry hands them\n * the request's cookies and search string through this module.\n * `useSearchParams()` reads NavigationContext and uses getSsrData() only to\n * detect that it is rendering on the server. Route\n * params do not travel here — client components read them from\n * `ParamsContext`, decoded from the payload root.\n *\n * Request isolation: ssr-entry.ts registers an ALS-backed provider via\n * registerSsrDataProvider(), so getSsrData() reads the current request's\n * store even when Suspense boundaries resolve asynchronously across\n * concurrent requests. There is no module-level fallback: outside an SSR\n * render (in the browser, or before the provider is registered) there is no\n * request, and getSsrData() returns undefined.\n *\n * IMPORTANT: This module must NOT import node:async_hooks or any Node.js-only\n * APIs, as it's imported by 'use client' hooks that are bundled for the browser.\n * The ALS instance lives in ssr-entry.ts (server-only); this module only holds\n * a reference to the provider function.\n *\n * All mutable state is delegated to client/state.ts for singleton guarantees.\n * See design/18-build-system.md §\"Singleton State Registry\"\n */\n\nimport { ssrDataProvider, _setSsrDataProvider } from './state.ts';\n\n// ─── Types ────────────────────────────────────────────────────────\n\nexport interface SsrData {\n /**\n * The request's raw app-visible query string (`''` or `?…`), matching what\n * `window.location.search` will hold after hydration. The raw string is\n * the only representation carried here: it preserves repeated keys\n * (`?tag=a&tag=b`), which a collapsed `Record<string, string>` cannot\n * (TIM-1428). Parse with `new URLSearchParams(search)` where needed.\n */\n search: string;\n /** The request's cookies as name→value pairs */\n cookies: Map<string, string>;\n}\n\n// ─── ALS-Backed Provider ─────────────────────────────────────────\n//\n// Server-side code (ssr-entry.ts) registers a provider that reads\n// from AsyncLocalStorage. This avoids importing node:async_hooks\n// in this browser-bundled module.\n//\n// Module singleton guarantee: In Vite's SSR environment, both\n// ssr-entry.ts (via #/client/ssr-data.js) and client component hooks\n// (via @timber-js/app/client) must resolve to the SAME module instance\n// of this file. The timber-shims plugin ensures this by remapping\n// @timber-js/app/client → src/client/index.ts in the SSR environment.\n// Without this remap, @timber-js/app/client resolves to dist/ (via\n// package.json exports), creating a split where registerSsrDataProvider\n// writes to one instance but getSsrData reads from another.\n// See timber-shims plugin resolveId for details.\n\n/**\n * Register the SSR data provider. Called once at module load by\n * ssr-entry.ts to wire up per-request data via AsyncLocalStorage; tests\n * register their own provider (and `undefined` to reset).\n */\nexport function registerSsrDataProvider(provider: (() => SsrData | undefined) | undefined): void {\n _setSsrDataProvider(provider);\n}\n\n/**\n * Read the current request's SSR data. Returns undefined when called\n * outside an SSR render (i.e. on the client after hydration).\n *\n * Used by client hooks' server snapshot functions.\n */\nexport function getSsrData(): SsrData | undefined {\n return ssrDataProvider?.();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiEA,SAAgB,wBAAwB,UAAyD;CAC/F,oBAAoB,QAAQ;AAC9B;;;;;;;AAQA,SAAgB,aAAkC;CAChD,OAAO,kBAAkB;AAC3B"}
@@ -5,14 +5,12 @@ function _setGlobalRouter(router) {
5
5
  globalRouter = router;
6
6
  }
7
7
  /**
8
- * ALS-backed SSR data provider. When registered, getSsrData() reads from
9
- * this function (ALS store) instead of module-level currentSsrData.
8
+ * ALS-backed SSR data provider, registered by the SSR entry. getSsrData()
9
+ * reads through it; unset in the browser.
10
10
  */
11
11
  var ssrDataProvider;
12
- /** Fallback SSR data for tests and environments without ALS. */
13
- var currentSsrData;
14
- function _setCurrentSsrData(data) {
15
- currentSsrData = data;
12
+ function _setSsrDataProvider(provider) {
13
+ ssrDataProvider = provider;
16
14
  }
17
15
  /** Cached search string — avoids reparsing when URL hasn't changed. */
18
16
  var cachedSearch = "";
@@ -47,6 +45,6 @@ function _setOnStatusPageRendered(handler) {
47
45
  onStatusPageRendered = handler;
48
46
  }
49
47
  //#endregion
50
- export { _setStatusPageRendered as a, cachedSearchParams as c, onStatusPageRendered as d, ssrDataProvider as f, _setOnStatusPageRendered as i, currentSsrData as l, unloading as m, _setCurrentSsrData as n, _setUnloading as o, statusPageRendered as p, _setGlobalRouter as r, cachedSearch as s, _setCachedSearch as t, globalRouter as u };
48
+ export { _setStatusPageRendered as a, cachedSearchParams as c, ssrDataProvider as d, statusPageRendered as f, _setSsrDataProvider as i, globalRouter as l, _setGlobalRouter as n, _setUnloading as o, unloading as p, _setOnStatusPageRendered as r, cachedSearch as s, _setCachedSearch as t, onStatusPageRendered as u };
51
49
 
52
- //# sourceMappingURL=state-FippDgxN.js.map
50
+ //# sourceMappingURL=state-BhNGPsdi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-BhNGPsdi.js","names":[],"sources":["../../src/client/state.ts"],"sourcesContent":["/**\n * Centralized client singleton state registry.\n *\n * ALL mutable module-level state that must have singleton semantics across\n * the client bundle lives here. Individual modules (router-ref.ts, ssr-data.ts,\n * use-segment-params.ts, use-search-params.ts, unload-guard.ts) import from this file\n * and re-export thin wrapper functions.\n *\n * Why: In Vite dev, a module is instantiated separately if reached via different\n * import paths (e.g., relative `./foo.js` vs barrel `@timber-js/app/client`).\n * By centralizing all mutable state in a single module that is always reached\n * through the same dependency chain (barrel → wrapper → state.ts), we guarantee\n * a single instance of every piece of shared state.\n *\n * DO NOT import this file from outside client/. Server code must never depend\n * on client state. The barrel (client/index.ts) is the public entry point.\n *\n * See design/18-build-system.md §\"Module Singleton Strategy\" and\n * §\"Singleton State Registry\".\n */\n\nimport type { RouterInstance } from './router-types.ts';\nimport type { SsrData } from './ssr-data.ts';\n\n// ─── Router (from router-ref.ts) ──────────────────────────────────────────\n\n/** The global router singleton — set once during bootstrap. */\nexport let globalRouter: RouterInstance | null = null;\n\nexport function _setGlobalRouter(router: RouterInstance | null): void {\n globalRouter = router;\n}\n\n// ─── SSR Data Provider (from ssr-data.ts) ──────────────────────────────────\n\n/**\n * ALS-backed SSR data provider, registered by the SSR entry. getSsrData()\n * reads through it; unset in the browser.\n */\nexport let ssrDataProvider: (() => SsrData | undefined) | undefined;\n\nexport function _setSsrDataProvider(provider: (() => SsrData | undefined) | undefined): void {\n ssrDataProvider = provider;\n}\n\n// ─── Search Params Cache (from use-search-params.ts) ────────────────────────\n\n/** Cached search string — avoids reparsing when URL hasn't changed. */\nexport let cachedSearch = '';\nexport let cachedSearchParams = new URLSearchParams();\n\nexport function _setCachedSearch(search: string, params: URLSearchParams): void {\n cachedSearch = search;\n cachedSearchParams = params;\n}\n\n// ─── Unload Guard (from unload-guard.ts) ─────────────────────────────────────\n\n/** Whether the page is currently being unloaded. */\nexport let unloading = false;\n\nexport function _setUnloading(value: boolean): void {\n unloading = value;\n}\n\n// ─── Status Page Commit Flag (from navigation-commit.ts) ────────────────────\n\n/**\n * Whether a status page for the main route has rendered since the last\n * navigation was prepared. Raised by `StatusPageMarker`, consumed by the\n * navigation's commit. See navigation-commit.ts §\"What a status page\n * teaches the cache\".\n */\nexport let statusPageRendered = false;\n\nexport function _setStatusPageRendered(value: boolean): void {\n statusPageRendered = value;\n}\n\n/**\n * What a status page report does beyond raising the flag: registered by the\n * router's committer so the report can clear the segment cache at once, for\n * the commits no navigation consumes (a row arriving inside Suspense after\n * its navigation published, a status document being hydrated).\n */\nexport let onStatusPageRendered: (() => void) | undefined;\n\nexport function _setOnStatusPageRendered(handler: (() => void) | undefined): void {\n onStatusPageRendered = handler;\n}\n"],"mappings":";;AA2BA,IAAW,eAAsC;AAEjD,SAAgB,iBAAiB,QAAqC;CACpE,eAAe;AACjB;;;;;AAQA,IAAW;AAEX,SAAgB,oBAAoB,UAAyD;CAC3F,kBAAkB;AACpB;;AAKA,IAAW,eAAe;AAC1B,IAAW,qBAAqB,IAAI,gBAAgB;AAEpD,SAAgB,iBAAiB,QAAgB,QAA+B;CAC9E,eAAe;CACf,qBAAqB;AACvB;;AAKA,IAAW,YAAY;AAEvB,SAAgB,cAAc,OAAsB;CAClD,YAAY;AACd;;;;;;;AAUA,IAAW,qBAAqB;AAEhC,SAAgB,uBAAuB,OAAsB;CAC3D,qBAAqB;AACvB;;;;;;;AAQA,IAAW;AAEX,SAAgB,yBAAyB,SAAyC;CAChF,uBAAuB;AACzB"}
@@ -1,6 +1,5 @@
1
- import { n as createSingleflight } from "./singleflight-2lUWfcAk.js";
2
- import { r as setNavigationState } from "./navigation-context-D0TU0Jog.js";
3
- import { a as _setStatusPageRendered, d as onStatusPageRendered, i as _setOnStatusPageRendered, p as statusPageRendered } from "./state-FippDgxN.js";
1
+ import { n as createSingleflight } from "./singleflight-BeVMMKi0.js";
2
+ import { a as _setStatusPageRendered, f as statusPageRendered, r as _setOnStatusPageRendered, u as onStatusPageRendered } from "./state-BhNGPsdi.js";
4
3
  import { useLayoutEffect } from "react";
5
4
  //#region src/shared/rsc-error-envelope.ts
6
5
  /**
@@ -388,6 +387,37 @@ var PrefetchCache = class PrefetchCache {
388
387
  }
389
388
  };
390
389
  //#endregion
390
+ //#region src/client/router-url.ts
391
+ /**
392
+ * How the client router reads the URLs it is handed.
393
+ *
394
+ * One parser, so the places that key or compare by URL — the history stack,
395
+ * the navigation state a page renders with, the departing page sent as
396
+ * `X-Timber-URL` — cannot disagree about what a URL says. They did: a
397
+ * history entry stored under `navigate('/café')`'s spelling was looked up
398
+ * under the address bar's `/caf%C3%A9`, and missed (TIM-1541).
399
+ */
400
+ var BASE = "http://localhost";
401
+ /**
402
+ * Parse a router URL. A URL that starts with "/" is a path — including one
403
+ * that starts with "//": that is the pathname a document at
404
+ * `https://host//a/b` reports, and resolved against a base it would parse as
405
+ * a scheme-relative reference to host `a`. Anything else resolves against a
406
+ * placeholder origin, so an absolute URL keeps its own.
407
+ */
408
+ function parseRouterUrl(url) {
409
+ return url.startsWith("/") ? new URL(BASE + url) : new URL(url, BASE);
410
+ }
411
+ /**
412
+ * The spelling a URL is stored and looked up under: the browser's own
413
+ * pathname + search, so `/a?` and `/a`, `/café` and `/caf%C3%A9`, are one
414
+ * key. The fragment is dropped — it is client-only.
415
+ */
416
+ function routerUrlKey(url) {
417
+ const parsed = parseRouterUrl(url);
418
+ return parsed.pathname + parsed.search;
419
+ }
420
+ //#endregion
391
421
  //#region src/client/navigation-commit.ts
392
422
  /**
393
423
  * What it means for a page to be "current" on the client.
@@ -425,10 +455,11 @@ function hasSkippedSlot(segmentInfo) {
425
455
  return segmentInfo?.some((s) => s.slot && s.skipped) ?? false;
426
456
  }
427
457
  /**
428
- * The metadata a history entry keeps. `skipped` describes the *response*
429
- * — "this slot's content was omitted" — and the entry stores no such
430
- * payload (it stores null). Left on the entry, the flag outlives the
431
- * response: `applyActionResult()` reuses the entry's metadata beside a full
458
+ * The metadata a committed page keeps — on its history entry, and as the
459
+ * current page's `committedSegmentInfo()`. `skipped` describes the
460
+ * *response* — "this slot's content was omitted" — and neither stores such
461
+ * a payload (the entry stores null). Kept, the flag outlives the response:
462
+ * `applyActionResult()` reuses the current page's metadata beside a full
432
463
  * re-render and would have that judged non-replayable too.
433
464
  */
434
465
  function storedSegmentInfo(segmentInfo) {
@@ -438,14 +469,12 @@ function storedSegmentInfo(segmentInfo) {
438
469
  /**
439
470
  * Derive the navigation state (pathname + search) a URL renders with.
440
471
  *
441
- * Pure: publishing it — the module-level fallback for tests and SSR, and the
442
- * globalThis bridge — is `prepareNavigation`'s commit, which runs only once
443
- * the render is known to have won (TIM-1301). Callers pass the returned value
444
- * explicitly to renderRoot/wrapPayload, so a render never depends on the
445
- * publish having happened first.
472
+ * Pure. The value travels with the tree it describes — callers pass it to
473
+ * `wrapPayload`, and the router's record of the displayed tree keeps it
474
+ * beside that tree — so there is no store to publish it to (TIM-1541).
446
475
  */
447
476
  function deriveNavigationState(url) {
448
- const parsed = new URL(url, "http://localhost");
477
+ const parsed = parseRouterUrl(url);
449
478
  return {
450
479
  pathname: parsed.pathname || "/",
451
480
  search: parsed.search
@@ -453,6 +482,7 @@ function deriveNavigationState(url) {
453
482
  }
454
483
  function createNavigationCommitter(deps) {
455
484
  const { segmentCache, historyStack } = deps;
485
+ let committedSegmentInfo = null;
456
486
  _setOnStatusPageRendered(() => segmentCache.clear());
457
487
  /**
458
488
  * Update the segment cache from server-provided segment metadata.
@@ -463,12 +493,11 @@ function createNavigationCommitter(deps) {
463
493
  * `currentStateTree`.
464
494
  */
465
495
  function updateSegmentCache(segmentInfo) {
466
- if (!segmentInfo || segmentInfo.length === 0) return;
467
496
  const tree = buildSegmentTree(segmentInfo);
468
497
  if (tree) segmentCache.set("/", tree);
469
498
  }
470
499
  return {
471
- updateSegmentCache,
500
+ committedSegmentInfo: () => committedSegmentInfo,
472
501
  isSegmentReuseSuppressed: () => segmentCache.isReuseSuppressed(),
473
502
  restoreSegmentReuse: () => segmentCache.restoreReuse(),
474
503
  /**
@@ -503,9 +532,13 @@ function createNavigationCommitter(deps) {
503
532
  * the URL and is what the incoming tree is *rendered* with, plus a
504
533
  * `commit` thunk that performs the writes that make a page current:
505
534
  * 1. Segment cache — update from server-provided segment metadata
506
- * 2. Navigation state — pathname/search for usePathname/useSearchParams
535
+ * 2. The current page's segment metadata (`committedSegmentInfo`)
507
536
  * 3. History stack — store the payload for instant back/forward replay
508
537
  *
538
+ * Pathname and search are not written here: they are the returned
539
+ * `navState`, which the tree renders with and the router's record of the
540
+ * displayed tree keeps (browser-entry/router-init.ts `publishTree`).
541
+ *
509
542
  * Nothing is written until `commit()` runs, and on a transitioned
510
543
  * navigation it runs only once the transition is known to have won — see
511
544
  * `renderViaTransition` and `NavigationRoot`. A navigation superseded
@@ -530,11 +563,11 @@ function createNavigationCommitter(deps) {
530
563
  const segmentInfo = cacheable ? opts.segmentInfo : [];
531
564
  if (segmentInfo && segmentInfo.length > 0) updateSegmentCache(segmentInfo);
532
565
  else if (segmentInfo?.length === 0 || opts.clearSegmentCacheOnEmpty) segmentCache.clear();
533
- setNavigationState(navState);
566
+ committedSegmentInfo = storedSegmentInfo(segmentInfo) ?? null;
534
567
  historyStack.push(url, {
535
568
  payload,
536
569
  params: opts.params,
537
- segmentInfo: storedSegmentInfo(segmentInfo)
570
+ segmentInfo: committedSegmentInfo
538
571
  });
539
572
  }
540
573
  };
@@ -570,6 +603,6 @@ function StatusPageMarker({ children }) {
570
603
  return children;
571
604
  }
572
605
  //#endregion
573
- export { SegmentCache as a, PrefetchCache as i, createNavigationCommitter as n, prefetchScopeOf as o, isPartialNavigation as r, TIMBER_ERROR_HEADER as s, StatusPageMarker as t };
606
+ export { routerUrlKey as a, prefetchScopeOf as c, parseRouterUrl as i, TIMBER_ERROR_HEADER as l, createNavigationCommitter as n, PrefetchCache as o, isPartialNavigation as r, SegmentCache as s, StatusPageMarker as t };
574
607
 
575
- //# sourceMappingURL=status-page-marker-CNbpUOPB.js.map
608
+ //# sourceMappingURL=status-page-marker-gaihi0KZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-page-marker-gaihi0KZ.js","names":[],"sources":["../../src/shared/rsc-error-envelope.ts","../../src/client/segment-cache.ts","../../src/client/router-url.ts","../../src/client/navigation-commit.ts","../../src/client/status-page-marker.tsx"],"sourcesContent":["/**\n * The hard-navigate error envelope shared by the server and the client router.\n *\n * A client-navigation (RSC) request cannot carry an error *page* — the response\n * body must be Flight data or the decoder crashes. Instead the server answers\n * with this envelope: an `X-Timber-Error: 1` header and a minimal JSON body.\n * The client treats it as \"give up on SPA navigation and hard-navigate\", and\n * the resulting document request renders the real error page as HTML.\n *\n * The header name lives here so the four server producers and the single\n * client consumer cannot drift apart. See design/10-error-handling.md\n * §\"Error Page Rendering for Client Navigation\".\n */\n\n/** Header the client checks to decide whether to hard-navigate. */\nexport const TIMBER_ERROR_HEADER = 'X-Timber-Error';\n","// Segment Cache — stores the mounted segment tree and prefetched payloads\n// See design/19-client-navigation.md for architecture details.\n\nimport type { PublishedParams } from '../shared/payload-root.ts';\nimport type { SegmentInfo, StateTree } from '../shared/segment-info.ts';\nimport { createSingleflight } from '../cache/singleflight.ts';\n\n// ─── Types ───────────────────────────────────────────────────────\n\n/** A prefetched RSC result with optional segment metadata. */\nexport interface PrefetchResult {\n payload: unknown;\n /**\n * Completion of the full Flight stream, when the prefetch is still decoding.\n * Carried through to the navigation that consumes the entry so a decode\n * failure reaches *that* navigation's recovery rather than dying with the\n * prefetch (TIM-1275).\n */\n decodePromise?: Promise<void> | null;\n /** Segment metadata from X-Timber-Segments header for populating the segment cache. */\n segmentInfo?: SegmentInfo[] | null;\n /**\n * The params published beside `payload`, split off the same payload root.\n * Carried so a consumed prefetch publishes the params of the tree it is\n * about to render, and not the ones already on screen.\n */\n params?: PublishedParams | Promise<PublishedParams>;\n /** Segment paths skipped by the server (for client-side merging). */\n skippedSegments?: string[] | null;\n}\n\n/**\n * A node in the client-side segment tree. Each node represents a mounted\n * layout or page segment with its RSC flight payload.\n */\nexport interface SegmentNode {\n /** The segment's URL pattern (e.g., \"/\", \"/dashboard\", \"/projects/[id]\") */\n segment: string;\n /**\n * The segment's `app/` directory path, groups included (e.g. `/(browse)/feed`).\n * Undefined for slot nodes and for servers that predate the field.\n */\n treePath?: string;\n /** The RSC flight payload for this segment (opaque to the cache) */\n payload: unknown;\n /**\n * Whether the segment/slot is request-dependent (calls getHeaders,\n * getSearchParams, cookies, etc.). Request-dependent segments always\n * re-render on navigation. For segments, this is still based on the\n * AsyncFunction heuristic (to be replaced separately). For slots,\n * this is taint-tracked via ALS.\n */\n isRequestDependent: boolean;\n /** Child segments keyed by segment path */\n children: Map<string, SegmentNode>;\n /** Parallel route slots keyed by slot path (e.g., \"/@sidebar\") */\n slots?: Map<string, SegmentNode>;\n /** Whether this slot's access.ts denied on its last render. */\n denied?: boolean;\n /**\n * Content key for slot nodes — opaque string the server computed from\n * owner parts, slot name, entry file, and slot params. Advertised back\n * to the server so it can decide skips by key membership (TIM-1370).\n */\n contentKey?: string;\n}\n\n// ─── Segment Cache ───────────────────────────────────────────────\n\n/**\n * Maintains the client-side segment tree representing currently mounted\n * layouts and pages. Used for navigation reconciliation — the router diffs\n * new routes against this tree to determine which segments to re-fetch.\n */\nexport class SegmentCache {\n private root: SegmentNode | undefined;\n /**\n * Monotonic counter. Bumped by `invalidateReuse()` (TIM-1466).\n * `serializeStateTree` suppresses `segments` and `slots` when\n * `reuseInvalidatedAt > reuseRestoredAt`. Only `restoreReuse()`\n * advances `reuseRestoredAt` — `set()` does not, because a\n * pre-invalidation navigation's commit thunk would lift suppression\n * before a post-invalidation request has been sent (codex on #1167).\n */\n private reuseInvalidatedAt = 0;\n private reuseRestoredAt = 0;\n\n get(segment: string): SegmentNode | undefined {\n if (segment === '/' || segment === this.root?.segment) {\n return this.root;\n }\n return undefined;\n }\n\n set(segment: string, node: SegmentNode): void {\n if (segment === '/' || !this.root) {\n this.root = node;\n }\n }\n\n clear(): void {\n this.root = undefined;\n this.reuseRestoredAt = this.reuseInvalidatedAt;\n }\n\n /**\n * Suppress segment and slot reuse without destroying the tree\n * structure (TIM-1466). `serializeStateTree` sends empty\n * `segments`/`slots` (forcing a full render) while `treePaths`\n * still reflects the mounted tree for interception scoping.\n * Call `restoreReuse()` once post-invalidation data has been\n * fetched and committed.\n */\n invalidateReuse(): void {\n this.reuseInvalidatedAt++;\n }\n\n /**\n * Lift reuse suppression. Called after the segment cache has been\n * rebuilt from a response that was fetched with suppression active\n * (i.e. the server sent a full render). Safe to call idempotently.\n */\n restoreReuse(): void {\n this.reuseRestoredAt = this.reuseInvalidatedAt;\n }\n\n /** Whether segment/slot reuse is currently suppressed (TIM-1466). */\n isReuseSuppressed(): boolean {\n return this.reuseInvalidatedAt > this.reuseRestoredAt;\n }\n\n /**\n * Serialize the mounted segment tree for the X-Timber-State-Tree header.\n * Only includes sync segments — async segments are excluded because the\n * server must always re-render them (they may depend on request context).\n *\n * When mergeableFilter is provided, only segments whose paths are in the\n * set are included. This ensures the server only skips segments that the\n * client can actually merge (i.e., segments whose cached element tree\n * contains an inner SegmentProvider the merger can splice into).\n *\n * `treePaths` is collected UNFILTERED, unlike `segments` and `slots`. Those\n * two answer \"what may the server skip re-rendering?\", so every reason a\n * segment cannot be reused is a reason to leave it out. `treePaths` answers\n * \"what is mounted right now?\" for interception scoping — a request-dependent\n * layout is still on screen, and dropping it would silently narrow the scope\n * the server sees and cancel a modal that should open (TIM-1282).\n *\n * This is a performance optimization only, NOT a security boundary.\n * The server always runs all access.ts files regardless of the state tree.\n */\n serializeStateTree(mergeableFilter?: Set<string>): StateTree {\n const segments: string[] = [];\n const slots: string[] = [];\n const treePaths: string[] = [];\n const suppressed = this.reuseInvalidatedAt > this.reuseRestoredAt;\n if (this.root) {\n if (!suppressed) {\n collectSyncSegments(this.root, segments, mergeableFilter);\n collectSyncSlots(this.root, slots);\n }\n collectTreePaths(this.root, treePaths);\n }\n const tree: StateTree = { segments };\n if (slots.length > 0) {\n tree.slots = slots;\n }\n if (treePaths.length > 0) {\n tree.treePaths = treePaths;\n }\n return tree;\n }\n}\n\n/** Recursively collect sync segment paths from the tree */\nfunction collectSyncSegments(\n node: SegmentNode,\n out: string[],\n mergeableFilter?: Set<string>\n): void {\n if (!node.isRequestDependent && (!mergeableFilter || mergeableFilter.has(node.segment))) {\n out.push(node.segment);\n }\n for (const child of node.children.values()) {\n collectSyncSegments(child, out, mergeableFilter);\n }\n}\n\n/**\n * Recursively collect the `app/` directory path of every mounted segment.\n *\n * Slots are not walked: a slot's own sub-tree can never own an intercepting\n * slot's scope, because scopes are always ordinary segments (`interception.ts`\n * derives one from the slot owner's ancestor chain).\n */\nfunction collectTreePaths(node: SegmentNode, out: string[]): void {\n if (node.treePath) out.push(node.treePath);\n for (const child of node.children.values()) {\n collectTreePaths(child, out);\n }\n}\n\n/**\n * Recursively collect content keys from cacheable slots.\n *\n * The server advertises a content key per slot on every render (TIM-1370),\n * encoding the owner's URL parts, the slot name, the matched entry file,\n * and the slot's params. The client stores this key and sends it back on\n * the next navigation so the server can decide skips by key membership —\n * no departing URL reconstruction needed.\n */\nfunction collectSyncSlots(node: SegmentNode, out: string[]): void {\n if (node.slots) {\n for (const slot of node.slots.values()) {\n if (!slot.isRequestDependent && !slot.denied && slot.contentKey) {\n out.push(slot.contentKey);\n }\n }\n }\n for (const child of node.children.values()) {\n collectSyncSlots(child, out);\n }\n}\n\n// ─── Segment Tree Builder ────────────────────────────────────────\n\n/**\n * Build a SegmentNode tree from flat segment metadata.\n *\n * Takes an ordered list of segment descriptors (root → leaf) from the\n * server's X-Timber-Segments header and constructs the hierarchical\n * tree structure that SegmentCache expects.\n *\n * Each segment is nested as a child of the previous one, forming a\n * linear chain from root to leaf. The leaf segment (page) is excluded\n * from the tree — pages are never cached across navigations.\n */\nexport function buildSegmentTree(segments: SegmentInfo[]): SegmentNode | undefined {\n // Need at least a root segment to build a tree\n if (segments.length === 0) return undefined;\n\n // Separate slot entries from segment entries. Slots are attached to\n // their parent segment node after the main chain is built.\n const segmentEntries: SegmentInfo[] = [];\n const slotEntries: SegmentInfo[] = [];\n for (const info of segments) {\n if (info.slot) {\n slotEntries.push(info);\n } else {\n segmentEntries.push(info);\n }\n }\n\n // Build the main segment chain.\n let root: SegmentNode | undefined;\n let parent: SegmentNode | undefined;\n const nodeById = new Map<string, SegmentNode>();\n\n for (const info of segmentEntries) {\n const id = info.segmentId ?? info.path;\n const node: SegmentNode = {\n segment: id,\n treePath: info.treePath,\n payload: null,\n isRequestDependent: info.isRequestDependent,\n children: new Map(),\n };\n\n nodeById.set(id, node);\n\n if (!root) {\n root = node;\n }\n\n if (parent) {\n parent.children.set(id, node);\n }\n\n parent = node;\n }\n\n // Attach slot entries to their parent segment nodes.\n for (const slotInfo of slotEntries) {\n const parentId = slotInfo.parentSegment;\n const parentNode = parentId ? nodeById.get(parentId) : root;\n if (!parentNode) continue;\n\n const slotId = slotInfo.segmentId ?? slotInfo.path;\n const slotNode: SegmentNode = {\n segment: slotId,\n payload: null,\n isRequestDependent: slotInfo.isRequestDependent,\n children: new Map(),\n denied: slotInfo.denied,\n contentKey: slotInfo.contentKey,\n };\n\n if (!parentNode.slots) {\n parentNode.slots = new Map();\n }\n parentNode.slots.set(slotId, slotNode);\n }\n\n return root;\n}\n\n// ─── Prefetch Cache ──────────────────────────────────────────────\n\ninterface ReadyEntry {\n result: PrefetchResult;\n expiresAt: number;\n}\n\n/** Sentinel value for negative cache entries (URL is not a route). */\nconst NEGATIVE_ENTRY: PrefetchResult = Object.freeze({ payload: null });\n\n/**\n * Timeout for the in-flight singleflight (TIM-1438). Per CLAUDE.md's\n * singleflight rule: \"Never write a coalescing Map without a timeout.\"\n * A hung prefetch cannot block clicks or suppress hovers forever.\n */\nconst PREFETCH_SINGLEFLIGHT_TIMEOUT_MS = 5_000;\n\n/**\n * Identifies a prefetched payload. A payload is addressed by *every* input that\n * changes the server's answer, not by the destination alone.\n *\n * The server's answer for a destination depends on where the request came\n * from — that is the whole mechanism behind intercepting routes, and it also\n * drives slot-skip resolution. A prefetch issued from `/gallery` is a modal\n * payload; the same URL prefetched from `/` is the full page. Keying on the\n * destination alone let the first click after a hover be answered with\n * whichever variant happened to be fetched (TIM-1265).\n *\n * `from` alone stopped being enough in TIM-1282. It is a pathname, and once\n * interception scope comes from the rendered tree rather than the address, one\n * pathname no longer names one source tree: `/photo/1` is the address both for\n * a modal open over `(browse)` and for `(landing)`'s canonical page. A key\n * without the scope hands the modal payload to the canonical page's click,\n * which renders it without the server's scope check ever running.\n *\n * The two reads happen at different instants — `scope` is sampled at hover and\n * again at click — which is why a tree that changed in between produces a\n * *miss* and a refetch rather than a wrong hit.\n */\nexport interface PrefetchKey {\n /** Destination URL — hash-less `pathname + search`, as navigate() fetches it. */\n url: string;\n /** Departing pathname — the `X-Timber-URL` the prefetch was issued with. */\n from: string;\n /**\n * The rendered scope the prefetch was issued with — the `treePaths` half of\n * `X-Timber-State-Tree`, joined. Empty when none crossed.\n */\n scope: string;\n}\n\n/**\n * Compose the map key. Length-prefixed rather than delimiter-joined for the\n * same reason `shared/rsc-cache-key.ts` is: any delimiter is forgeable by a\n * value containing it, and a URL may contain any character a delimiter could.\n */\nfunction prefetchMapKey(key: PrefetchKey): string {\n return `${key.from.length}:${key.from}:${key.scope.length}:${key.scope}:${key.url}`;\n}\n\n/**\n * Extract the destination URL from a length-prefixed map key.\n * Key format: `${from.length}:${from}:${scope.length}:${scope}:${url}`\n */\nfunction extractUrlFromMapKey(mapKey: string): string | null {\n // Parse from-length prefix\n const firstColon = mapKey.indexOf(':');\n if (firstColon === -1) return null;\n const fromLen = parseInt(mapKey.slice(0, firstColon), 10);\n if (Number.isNaN(fromLen)) return null;\n // Skip past `from.length:from:`\n const scopeStart = firstColon + 1 + fromLen + 1;\n // Parse scope-length prefix\n const secondColon = mapKey.indexOf(':', scopeStart);\n if (secondColon === -1) return null;\n const scopeLen = parseInt(mapKey.slice(scopeStart, secondColon), 10);\n if (Number.isNaN(scopeLen)) return null;\n // Skip past `scope.length:scope:` — remainder is the URL\n return mapKey.slice(secondColon + 1 + scopeLen + 1);\n}\n\n/**\n * The `scope` half of a `PrefetchKey`, derived from the state tree that will\n * actually be sent. Taking it from the header value rather than re-walking the\n * cache is the point: the key varies by exactly what the request varies by.\n */\nexport function prefetchScopeOf(stateTree: StateTree | undefined): string {\n return stateTree?.treePaths?.join('\\u0000') ?? '';\n}\n\n/** Outcome of a prefetch flight — discriminated so NonRscResponse is a\n * success (stores a negative entry) rather than a rejection.\n * `startGen` is the eviction generation at flight start — joiners that\n * started after a gen bump must reject pre-invalidation results (TIM-1464). */\nexport type FlightOutcome =\n | { kind: 'ready'; result: PrefetchResult; startGen: number }\n | { kind: 'non-route'; startGen: number };\n\n/**\n * Short-lived cache for hover-triggered prefetches. Entries expire after\n * 30 seconds. When a link is clicked, the prefetched payload is consumed\n * (moved to the history stack) and removed from this cache.\n *\n * In-flight dedup (TIM-1438): concurrent fetches for the same key are\n * coalesced by a `createSingleflight` instance. A hover starts a flight;\n * a click for the same key joins it instead of issuing a duplicate. The\n * singleflight enforces a 5-second timeout and cleans up automatically\n * on settlement.\n *\n * timber.js does NOT prefetch on viewport intersection — only explicit\n * hover on <Link prefetch> triggers a prefetch.\n */\nexport class PrefetchCache {\n private static readonly TTL_MS = 30_000;\n private entries = new Map<string, ReadyEntry>();\n private flights = createSingleflight({ timeoutMs: PREFETCH_SINGLEFLIGHT_TIMEOUT_MS });\n private evictionGen = 0;\n\n set(key: PrefetchKey, result: PrefetchResult): void {\n this.entries.set(prefetchMapKey(key), {\n result,\n expiresAt: Date.now() + PrefetchCache.TTL_MS,\n });\n }\n\n get(key: PrefetchKey): PrefetchResult | undefined {\n const mapKey = prefetchMapKey(key);\n const entry = this.entries.get(mapKey);\n if (!entry) return undefined;\n if (Date.now() >= entry.expiresAt) {\n this.entries.delete(mapKey);\n return undefined;\n }\n return entry.result;\n }\n\n /** True if a ready or negative entry exists for this key. */\n has(key: PrefetchKey): boolean {\n return this.get(key) !== undefined;\n }\n\n /** Get and remove the entry (used when navigation consumes a prefetch) */\n consume(key: PrefetchKey): PrefetchResult | undefined {\n const result = this.get(key);\n if (result !== undefined) {\n this.entries.delete(prefetchMapKey(key));\n }\n return result;\n }\n\n /**\n * Fetch or coalesce with an in-flight fetch for this key (TIM-1438).\n *\n * Concurrent callers (hover + click, repeated hovers) get the same\n * promise. On success the result is stored as a ready entry. On\n * NonRscResponse a negative entry is stored and the outcome is\n * `{ kind: 'non-route' }`. Other errors (network, version skew)\n * reject — the singleflight cleans up the key and subsequent callers\n * retry.\n *\n * The singleflight signal is passed to `doFetch` so a timed-out flight\n * aborts the underlying fetch rather than leaving it running.\n */\n fetchOrCoalesce(\n key: PrefetchKey,\n doFetch: (signal: AbortSignal) => Promise<PrefetchResult>,\n isNonRoute: (error: unknown) => boolean\n ): Promise<FlightOutcome> {\n // Capture the eviction generation before the flight starts. If an\n // action evicts caches while this flight is in progress, the gen\n // will have bumped and the stale result is discarded (TIM-1476).\n // The starting gen is also included in the outcome so joiners\n // that started after a gen bump can detect pre-invalidation\n // results (TIM-1464).\n const genAtStart = this.evictionGen;\n return this.flights.do(prefetchMapKey(key), async (signal) => {\n try {\n const result = await doFetch(signal);\n if (!signal.aborted && this.evictionGen === genAtStart) this.set(key, result);\n return { kind: 'ready' as const, result, startGen: genAtStart };\n } catch (err) {\n if (isNonRoute(err)) {\n if (!signal.aborted && this.evictionGen === genAtStart) this.setNegative(key);\n return { kind: 'non-route' as const, startGen: genAtStart };\n }\n throw err;\n }\n });\n }\n\n /**\n * Join an in-flight singleflight fetch if one exists for this key.\n * Returns the in-flight promise or undefined. Used by the click path to\n * coalesce with a hover prefetch without starting a new flight — when no\n * flight exists, the click issues its own fetch with the navigation's\n * abort signal so superseded navigations abort immediately (TIM-1438).\n */\n joinInflight(key: PrefetchKey): Promise<FlightOutcome> | undefined {\n return this.flights.get(prefetchMapKey(key)) as Promise<FlightOutcome> | undefined;\n }\n\n /**\n * Store a negative entry — the URL is not a route (non-RSC Content-Type).\n *\n * Keyed like every other entry even though \"not a route\" does not actually\n * vary by departing URL: a source-independent negative would be a second\n * keying rule to keep correct, and the only cost of the uniform one is a\n * repeated fetch for a link hovered from a second page.\n */\n setNegative(key: PrefetchKey): void {\n this.set(key, NEGATIVE_ENTRY);\n }\n\n /** Check if the entry is a negative cache entry (URL is not a route). */\n isNegative(key: PrefetchKey): boolean {\n return this.get(key) === NEGATIVE_ENTRY;\n }\n\n /**\n * Invalidate entries whose destination URL matches (TIM-1454).\n *\n * When `url` has no search string, evicts all entries sharing that\n * pathname — `/products` evicts `/products?page=1` too, because\n * `revalidatePath('/products')` invalidates the route regardless of\n * query (TIM-1465).\n *\n * When `url` has a search string, only the exact URL is evicted.\n *\n * Also bumps the eviction generation so in-flight singleflight\n * flights for the invalidated URL(s) discard their results on\n * completion (TIM-1464).\n */\n invalidateUrl(url: string): void {\n const hasSearch = url.includes('?');\n const pathname = hasSearch ? null : url;\n\n for (const [mapKey] of this.entries) {\n // The map key is `${from.length}:${from}:${scope.length}:${scope}:${url}`.\n // The destination URL is the suffix after the last unescaped colon pair.\n if (hasSearch) {\n if (mapKey.endsWith(':' + url)) {\n this.entries.delete(mapKey);\n }\n } else {\n // Extract the URL suffix — everything after the scope block.\n // Match any entry whose URL pathname matches.\n const entryUrl = extractUrlFromMapKey(mapKey);\n if (entryUrl !== null) {\n const qIndex = entryUrl.indexOf('?');\n const entryPathname = qIndex === -1 ? entryUrl : entryUrl.slice(0, qIndex);\n if (entryPathname === pathname) {\n this.entries.delete(mapKey);\n }\n }\n }\n }\n\n // Fence in-flight flights so a prefetch that started before\n // invalidation cannot store its stale result (TIM-1464).\n this.evictionGen++;\n }\n\n /**\n * Evict all ready entries and fence in-flight singleflights (TIM-1476).\n * Called after a server action that revalidated data — the mutation may\n * affect any route, not just the paths the server named. In-flight\n * singleflights are not aborted (they may carry post-mutation data),\n * but their results are discarded if they complete after eviction:\n * `fetchOrCoalesce` captures the generation before the flight and\n * skips the store when it has been bumped.\n *\n * Consumers that join an in-flight flight via `joinInflight` must\n * check `getEvictionGen()` before and after the await: if the gen\n * changed, the result predates the eviction and must be discarded\n * (codex on #1131 round 2).\n */\n clearReady(): void {\n this.entries.clear();\n this.evictionGen++;\n }\n\n getEvictionGen(): number {\n return this.evictionGen;\n }\n}\n","/**\n * How the client router reads the URLs it is handed.\n *\n * One parser, so the places that key or compare by URL — the history stack,\n * the navigation state a page renders with, the departing page sent as\n * `X-Timber-URL` — cannot disagree about what a URL says. They did: a\n * history entry stored under `navigate('/café')`'s spelling was looked up\n * under the address bar's `/caf%C3%A9`, and missed (TIM-1541).\n */\n\nconst BASE = 'http://localhost';\n\n/**\n * Parse a router URL. A URL that starts with \"/\" is a path — including one\n * that starts with \"//\": that is the pathname a document at\n * `https://host//a/b` reports, and resolved against a base it would parse as\n * a scheme-relative reference to host `a`. Anything else resolves against a\n * placeholder origin, so an absolute URL keeps its own.\n */\nexport function parseRouterUrl(url: string): URL {\n return url.startsWith('/') ? new URL(BASE + url) : new URL(url, BASE);\n}\n\n/**\n * The spelling a URL is stored and looked up under: the browser's own\n * pathname + search, so `/a?` and `/a`, `/café` and `/caf%C3%A9`, are one\n * key. The fragment is dropped — it is client-only.\n */\nexport function routerUrlKey(url: string): string {\n const parsed = parseRouterUrl(url);\n return parsed.pathname + parsed.search;\n}\n","/**\n * What it means for a page to be \"current\" on the client.\n *\n * One module owns every write that makes a navigation's destination the\n * current page, and the read that describes the current page back to the\n * server. Keeping them together is the point: `X-Timber-State-Tree` is\n * derived from the segment cache, so the thing that publishes and the thing\n * that reports have to agree about when a page becomes current.\n *\n * See design/19-client-navigation.md §\"State Update Invariants\".\n */\n\nimport { buildSegmentTree, type SegmentCache } from './segment-cache.ts';\nimport type { HistoryStack } from './history.ts';\nimport type { SegmentInfo, StateTree } from '../shared/segment-info.ts';\nimport type { ParamsSource } from '../shared/payload-root.ts';\nimport type { NavigationState } from './navigation-context.ts';\nimport { parseRouterUrl } from './router-url.ts';\nimport {\n statusPageRendered,\n _setStatusPageRendered,\n onStatusPageRendered,\n _setOnStatusPageRendered,\n} from './state.ts';\n\n// ─── What a status page teaches the cache ─────────────────────────────────\n//\n// A status page replaces a subtree: a deny page lands at the segment owning\n// the matched file and nothing below it mounts; an `error.tsx` fallback\n// discards everything under the boundary that rendered it. `X-Timber-Segments`\n// nonetheless describes the whole chain the payload was built against — it\n// must, because a partial navigation picks its merge point out of it — so the\n// cache has to be told not to learn from such a tree.\n//\n// The HTTP status used to say so, and could not be trusted to: an RSC payload\n// response commits one macrotask after its first chunk, and a nested\n// component that denies after I/O puts its row in the stream after the\n// status line has gone out (TIM-1489). The only thing that reliably knows a\n// status page rendered is the status page itself, and the only moment that\n// matters is the commit that puts it on screen — so every status page the\n// framework produces is wrapped in `StatusPageMarker`, whose layout effect\n// reports on each commit that renders it. Layout effects run child-before-\n// parent, so the report lands before `NavigationRoot`'s publish of that\n// same commit, and never for a render React discards. Without Suspense\n// around the row, the transition cannot commit until the row has arrived, so\n// this covers the delayed nested case too, which no status ever could.\n//\n// The report also clears the cache on the spot, because not every commit\n// has a navigation to consume it: a deny row arriving inside a Suspense\n// boundary after its navigation published, or a status document being\n// hydrated, would otherwise leave a tree in the cache that is no longer on\n// screen. Consuming the flag at commit is what keeps the same navigation from\n// re-seeding it a moment later. Both are over-approximations — a page-level\n// deny leaves every layout standing — at the cost of one full render on the\n// next navigation.\n//\n// Slot boundaries do not report: a slot degrading is the page staying, and\n// the chain it mounted is the chain the header describes. See TIM-1356,\n// TIM-1495, design/04-authorization.md §\"Where a Deny Page Renders\".\n\n/**\n * Record that a status page for the main route is on screen. Called from\n * `StatusPageMarker`'s layout effect; consumed by the commit of the\n * navigation whose tree it belongs to, and acted on immediately for the\n * commits that have none.\n */\nexport function reportStatusPageRendered(): void {\n _setStatusPageRendered(true);\n onStatusPageRendered?.();\n}\n\n/** Everything a commit needs to describe the page it makes current. */\nexport interface NavigationCommitInput {\n payload: unknown;\n /** The params that payload published — replayed with it on popstate. */\n params: ParamsSource;\n /**\n * The rendered chain's metadata, or null/undefined when this commit\n * carries none at all — a revalidation payload, or a history entry\n * stored before the channel existed.\n *\n * An empty *array* is a different statement from an absent one: the\n * server rendered a route with no layout-bearing segments (a layoutless\n * app — the missing-root-layout check is a warning, not an error), so\n * the correct tree is the empty one. Collapsing the two left the\n * departing route's tree in place, which `treePaths` then reported as\n * mounted — enough to select an intercepting rewrite whose slot is not\n * on screen (TIM-1282).\n */\n segmentInfo?: SegmentInfo[] | null;\n /** When true, clear the segment cache if segmentInfo is absent\n * (popstate replay for entries without layout metadata). */\n clearSegmentCacheOnEmpty?: boolean;\n /**\n * Layouts the server skipped on this response (`X-Timber-Skipped-Segments`),\n * when it came from a fetch. Omitted by replays and revalidation.\n *\n * Together with `skipped` slot entries in `segmentInfo`, this decides\n * whether the payload can be *replayed*. A partial payload only renders\n * merged over the tree on screen, and a `skip=true` outlet resolves against\n * the slot content cache at render time — so a stored copy of either,\n * replayed later, shows whatever the client holds *then*, not what was on\n * screen when the entry committed. Such entries store a null payload and\n * traversals to them fetch instead. Decided here, not at the fetch call\n * sites, because there are three of those (`performNavigationFetch`,\n * `refresh()`, and uncached traversal) and a rule applied at one of them\n * holds by accident: `performNavigationFetch` had the check and\n * `fetchCommitAndRender` did not, so a back/forward fetch that was\n * answered with skips stored the holes (TIM-1432).\n */\n skippedSegments?: string[] | null;\n}\n\n/** Whether a response skipped layouts — its payload merges over the tree on screen. */\nexport function isPartialNavigation(skippedSegments: string[] | null | undefined): boolean {\n return skippedSegments != null && skippedSegments.length > 0;\n}\n\n/**\n * Whether a payload can stand on its own when replayed from the history\n * stack. See `NavigationCommitInput.skippedSegments`.\n */\nfunction isReplayable(opts: NavigationCommitInput): boolean {\n return !isPartialNavigation(opts.skippedSegments) && !hasSkippedSlot(opts.segmentInfo);\n}\n\nfunction hasSkippedSlot(segmentInfo: SegmentInfo[] | null | undefined): boolean {\n return segmentInfo?.some((s) => s.slot && s.skipped) ?? false;\n}\n\n/**\n * The metadata a committed page keeps — on its history entry, and as the\n * current page's `committedSegmentInfo()`. `skipped` describes the\n * *response* — \"this slot's content was omitted\" — and neither stores such\n * a payload (the entry stores null). Kept, the flag outlives the response:\n * `applyActionResult()` reuses the current page's metadata beside a full\n * re-render and would have that judged non-replayable too.\n */\nfunction storedSegmentInfo(segmentInfo: SegmentInfo[] | null | undefined) {\n if (!hasSkippedSlot(segmentInfo)) return segmentInfo;\n return segmentInfo!.map(({ skipped: _skipped, ...rest }) => rest);\n}\n\n/**\n * A navigation's destination, split into what a render needs and what a\n * commit publishes.\n */\nexport interface PreparedNavigation {\n /** The state the incoming tree renders with. Pure — safe before winning. */\n navState: NavigationState;\n /** Publishes the destination. Call only once the render has won. */\n commit: () => void;\n}\n\nexport interface NavigationCommitter {\n prepareNavigation(url: string, opts: NavigationCommitInput): PreparedNavigation;\n currentStateTree(): StateTree | undefined;\n /**\n * The segment metadata of the page the last commit made current, as that\n * commit recorded it — null when it carried none.\n *\n * Server-action responses carry no segment metadata, so a revalidation\n * re-renders the current page with this. It is the committer's own record\n * rather than a history-stack lookup: the stack is keyed by the URL string\n * a navigation was asked for, and the current page is known by\n * `window.location`'s spelling of it, and the two need not agree\n * (`/a?` against `/a`, a percent-encoded path against a decoded one).\n */\n committedSegmentInfo(): SegmentInfo[] | null;\n /** Whether segment reuse is currently suppressed (TIM-1466). */\n isSegmentReuseSuppressed(): boolean;\n /** Lift segment reuse suppression after fresh data has been fetched (TIM-1466). */\n restoreSegmentReuse(): void;\n}\n\n/**\n * Derive the navigation state (pathname + search) a URL renders with.\n *\n * Pure. The value travels with the tree it describes — callers pass it to\n * `wrapPayload`, and the router's record of the displayed tree keeps it\n * beside that tree — so there is no store to publish it to (TIM-1541).\n */\nexport function deriveNavigationState(url: string): NavigationState {\n // Params are not part of it. They travel with the payload and are published\n // by the one client-owned `ParamsProvider` as the new tree renders, which is\n // also what clears the previous route's — a route with no params of its own\n // still publishes an empty record (TIM-1294/TIM-1297, and the clearing\n // requirement from TIM-1285).\n const parsed = parseRouterUrl(url);\n return { pathname: parsed.pathname || '/', search: parsed.search };\n}\n\nexport function createNavigationCommitter(deps: {\n segmentCache: SegmentCache;\n historyStack: HistoryStack;\n /** Read at call time — the router's `clientSegmentCache` opt-in. */\n clientSegmentCache: () => boolean | undefined;\n}): NavigationCommitter {\n const { segmentCache, historyStack } = deps;\n\n // See `committedSegmentInfo`. Written only by a commit.\n let committedSegmentInfo: SegmentInfo[] | null = null;\n\n // See `reportStatusPageRendered`: a status page that commits with no\n // navigation to consume the report still empties the cache.\n _setOnStatusPageRendered(() => segmentCache.clear());\n\n /**\n * Update the segment cache from server-provided segment metadata.\n *\n * Runs whether or not `clientSegmentCache` is enabled: the tree is also\n * where the mounted tree paths come from, and those cross on every\n * navigation. What the flag gates is which parts get serialized — see\n * `currentStateTree`.\n */\n function updateSegmentCache(segmentInfo: SegmentInfo[]): void {\n const tree = buildSegmentTree(segmentInfo);\n if (tree) {\n segmentCache.set('/', tree);\n }\n }\n\n return {\n committedSegmentInfo: () => committedSegmentInfo,\n isSegmentReuseSuppressed: () => segmentCache.isReuseSuppressed(),\n restoreSegmentReuse: () => segmentCache.restoreReuse(),\n\n /**\n * The X-Timber-State-Tree to send with the next RSC request.\n *\n * Two different things travel on one header. `segments`/`slots` are the\n * caching opt-in and are omitted unless `clientSegmentCache` is on.\n * `treePaths` is how the server learns which route is actually mounted,\n * which stops being derivable from the address bar as soon as a modal is\n * open — so it crosses regardless (TIM-1282). Undefined when there is\n * nothing to say, so a no-op header never enters the `_rsc` cache key.\n *\n * Reads what the last *committed* navigation published, so a superseded\n * navigation's destination can never end up describing the mounted tree\n * (TIM-1301).\n */\n currentStateTree(): StateTree | undefined {\n const tree = segmentCache.serializeStateTree();\n if (deps.clientSegmentCache()) return tree;\n return tree.treePaths ? { segments: [], treePaths: tree.treePaths } : undefined;\n },\n\n /**\n * Prepare all navigation-owned state for a new page — without publishing\n * any of it. Every code path that changes the \"current page\" must go\n * through this function, which is what makes \"forgot a field\" impossible\n * by construction.\n *\n * Returns the destination's `NavigationState`, which is a pure function of\n * the URL and is what the incoming tree is *rendered* with, plus a\n * `commit` thunk that performs the writes that make a page current:\n * 1. Segment cache — update from server-provided segment metadata\n * 2. The current page's segment metadata (`committedSegmentInfo`)\n * 3. History stack — store the payload for instant back/forward replay\n *\n * Pathname and search are not written here: they are the returned\n * `navState`, which the tree renders with and the router's record of the\n * displayed tree keeps (browser-entry/router-init.ts `publishTree`).\n *\n * Nothing is written until `commit()` runs, and on a transitioned\n * navigation it runs only once the transition is known to have won — see\n * `renderViaTransition` and `NavigationRoot`. A navigation superseded\n * while its payload was in flight never calls it, so the cache, the\n * pathname the client reports and the history stack all keep describing\n * the route still on screen (TIM-1301).\n *\n * Callers with state of their own to publish — the address bar, the\n * client's record of the mounted tree — wrap this thunk rather than\n * commit beside it, so there stays exactly one moment at which a page\n * becomes current.\n */\n prepareNavigation(url: string, opts: NavigationCommitInput): PreparedNavigation {\n const navState = deriveNavigationState(url);\n const payload = isReplayable(opts) ? opts.payload : null;\n\n // The tree this navigation renders has not rendered yet. Whatever a\n // boundary reported before now belongs to a commit that had no\n // navigation to consume it — a hydration, a row that arrived inside a\n // Suspense boundary after its navigation had published — and must not\n // be charged to this one.\n _setStatusPageRendered(false);\n\n return {\n navState,\n commit() {\n // A tree in which a boundary rendered a status page is not one to\n // cache — see the module note. Read at commit rather than at\n // prepare because that is when the boundary has spoken: the row it\n // renders may not have existed when the response headers were read.\n // The history entry records the same thing, or a traversal back to\n // this URL would replay the metadata this drops.\n const cacheable = !statusPageRendered;\n _setStatusPageRendered(false);\n const segmentInfo = cacheable ? opts.segmentInfo : [];\n\n if (segmentInfo && segmentInfo.length > 0) {\n updateSegmentCache(segmentInfo);\n } else if (segmentInfo?.length === 0 || opts.clearSegmentCacheOnEmpty) {\n segmentCache.clear();\n }\n\n committedSegmentInfo = storedSegmentInfo(segmentInfo) ?? null;\n\n historyStack.push(url, {\n payload,\n params: opts.params,\n segmentInfo: committedSegmentInfo,\n });\n },\n };\n },\n };\n}\n","'use client';\n\n/**\n * The one way a status page tells the router it is on screen.\n *\n * A status page replaces a subtree, and the segment cache must not learn a\n * chain that never mounted (see `navigation-commit.ts` §\"What a status page\n * teaches the cache\"). Status pages are produced in three places — an\n * in-tree catch site rendering the page it owns, the server's fallback\n * renderers, and a client boundary rendering its fallback for a Flight row —\n * and every one of them wraps the page in this marker, so the report does\n * not depend on which producer it was. In particular the in-tree case, the\n * common `access.ts` deny, never involves a client boundary at all.\n *\n * A layout effect, on every commit that renders the marker rather than on\n * mount only: React reuses the fiber when one status page is replaced by\n * another at the same position, and the second one must report too. Layout\n * effects run child-before-parent, so the report lands before the ancestor\n * `NavigationRoot` publishes the same commit, and never for a render React\n * discards. Idempotent, so StrictMode's double invocation and a status page\n * re-rendering on its own state are harmless. See TIM-1495.\n */\n\nimport { useLayoutEffect, type ReactNode } from 'react';\nimport { reportStatusPageRendered } from './navigation-commit.ts';\n\nexport function StatusPageMarker({ children }: { children: ReactNode }): ReactNode {\n useLayoutEffect(() => {\n reportStatusPageRendered();\n });\n return children;\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { n as classifyUrlSegment } from "./segment-classify-C539Pa2O.js";
2
2
  import { t as readFileCached } from "./file-cache-Dw6BJPG7.js";
3
- import { n as findSchemaFile, r as parseExistingSchemaKeys } from "./cli-schema-sync--LZKs8Zn.js";
3
+ import { n as findSchemaFile, r as parseExistingSchemaKeys } from "./cli-schema-sync-CbT2AUUI.js";
4
4
  //#region src/routing/schema-validation.ts
5
5
  /**
6
6
  * Schema key/value validation against the filesystem route tree.
@@ -141,4 +141,4 @@ function walk(node, chain, result, includeSlots) {
141
141
  //#endregion
142
142
  export { collectDynamicSegmentsFromTree as n, validateSchemaAgainstRoutes as r, collectLeafRoutes as t };
143
143
 
144
- //# sourceMappingURL=walkers-CN2N_bA-.js.map
144
+ //# sourceMappingURL=walkers-BXExhzzk.js.map