@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,134 +1,294 @@
1
- import { r as requestContextAls, s as waitUntilAls } from "./als-registry-C6kcfprT.js";
1
+ import { r as canonicalize } from "./canonicalize-CgHoscYO.js";
2
+ import { a as requestContextAls, i as reactCacheScopeAls, o as revalidationAls } from "./als-registry-DaxkVjt5.js";
3
+ import { C as isDebug, P as withSpan, S as isRedirectSignal, v as DENY_BRAND, w as isDevMode, x as isDenySignal, y as REDIRECT_BRAND } from "./logger-DiDt5ppH.js";
2
4
  import { n as _setGetSegmentParamsFn, t as _setGetSearchParamsFn } from "./als-slots-BEEIPKYm.js";
3
- import { a as assertValidCookieOptions, i as assertValidCookieName, n as parseSetCookie$1, o as assertValidCookieValue, r as stringifySetCookie, t as parseCookie } from "./dist-DtrJyBbw.js";
4
5
  import { r as appVisibleSearch } from "./rsc-cache-key-ClUiXQnK.js";
5
6
  import { a as mergePreservedSearchParams, o as resolveSegmentParams, r as validateExternalRedirectUrl, t as assertRelativeRedirectPath } from "./href-validation-BIrxavIy.js";
6
- //#region src/server/waituntil-bridge.ts
7
+ import { a as assertValidCookieOptions, i as assertValidCookieName, n as parseSetCookie$1, o as assertValidCookieValue, r as stringifySetCookie, t as parseCookie } from "./dist-DtrJyBbw.js";
8
+ import { t as cache, x as getWaitUntil } from "./cache-api-CR23J_NC.js";
9
+ //#region src/server/react-cache-scope.ts
7
10
  /**
8
- * Per-request waitUntil bridge — ALS bridge for platform adapters.
11
+ * React.cache scopes — opening, resuming, and leaving the per-request
12
+ * `React.cache` scope that `react-cache-bridge.ts` makes Flight read.
9
13
  *
10
- * The generated entry point (Nitro, Cloudflare) wraps the handler with
11
- * `runWithWaitUntil`, binding the platform's lifecycle extension function
12
- * (e.g., h3's `event.waitUntil()` or CF's `ctx.waitUntil()`) for the
13
- * request duration. The `waitUntil()` primitive reads from this ALS to
14
- * dispatch background work to the correct platform API.
15
- *
16
- * Design doc: design/11-platform.md §"waitUntil()"
14
+ * Kept apart from the bridge so request-context and cookie code can use
15
+ * it without importing `react`. See design/spike-TIM-1529-react-cache-bridge.md.
16
+ */
17
+ /** A new, empty `React.cache` scope. */
18
+ function createReactCacheScope() {
19
+ return /* @__PURE__ */ new Map();
20
+ }
21
+ /**
22
+ * Run `fn` in a `React.cache` scope — a fresh one unless `scope` is given.
23
+ * Every `React.cache` call made from `fn` — synchronously, in its async
24
+ * continuations, and in any Flight render it starts — shares one memo table.
25
+ *
26
+ * Opened by `runWithRequestContext`, so each request context gets its own
27
+ * scope. Pass `scope` to resume one across two ALS runs: the post-action
28
+ * revalidation opens a fresh scope for the target route's middleware and
29
+ * renders the revalidated tree in that same scope later.
17
30
  */
31
+ function runWithReactCacheScope(fn, scope = createReactCacheScope()) {
32
+ return reactCacheScopeAls.run(scope, fn);
33
+ }
18
34
  /**
19
- * Get the current request's waitUntil function, if available.
20
- *
21
- * Returns undefined when no platform adapter has installed a waitUntil
22
- * handler for the current request (e.g., on platforms that don't support
23
- * lifecycle extension, or outside a request context).
35
+ * Run `fn` with no timber scope, so `React.cache` behaves as stock React:
36
+ * no dedupe outside a render, Flight's own per-render cache inside one.
37
+ *
38
+ * Used for server action bodies — an action reads, writes cookies, and
39
+ * mutates, so memoized reads would go stale mid-action (the same choice
40
+ * Next.js makes) — and for renders whose output outlives the request
41
+ * (`cache.component` capture), which must not bake request-memoized values
42
+ * into a cross-request cache entry.
24
43
  */
25
- function getWaitUntil() {
26
- return waitUntilAls.getStore();
44
+ function runOutsideReactCacheScope(fn) {
45
+ return reactCacheScopeAls.exit(fn);
46
+ }
47
+ var cookieReads = /* @__PURE__ */ new WeakMap();
48
+ var warnedScopes = /* @__PURE__ */ new WeakSet();
49
+ /**
50
+ * Dev-only: record that cookie `name` (or {@link ANY_COOKIE}) was read
51
+ * while `React.cache` held values in this request — the read may have
52
+ * happened inside a cached function.
53
+ */
54
+ function noteCookieReadForReactCache(name) {
55
+ if (!isDevMode()) return;
56
+ const scope = reactCacheScopeAls.getStore();
57
+ if (!scope || scope.size === 0) return;
58
+ let reads = cookieReads.get(scope);
59
+ if (!reads) {
60
+ reads = /* @__PURE__ */ new Set();
61
+ cookieReads.set(scope, reads);
62
+ }
63
+ reads.add(name);
64
+ }
65
+ /**
66
+ * Dev-only: warn (once per request) when a cookie recorded by
67
+ * {@link noteCookieReadForReactCache} is written. `name` is the written
68
+ * cookie, or {@link ANY_COOKIE} for `clear()`. Dev-only because reads are
69
+ * only recorded in dev.
70
+ */
71
+ function warnIfCachedCookieIsWritten(name) {
72
+ const scope = reactCacheScopeAls.getStore();
73
+ if (!scope || warnedScopes.has(scope)) return;
74
+ const reads = cookieReads.get(scope);
75
+ if (!reads) return;
76
+ if (!(name === "*" ? reads.size > 0 : reads.has(name) || reads.has("*"))) return;
77
+ warnedScopes.add(scope);
78
+ const which = name === "*" ? "Cookies were cleared" : `Cookie "${name}" was written`;
79
+ console.warn(`[timber] ${which} after being read while React.cache held values for this request. A cached function that read it keeps returning the old result — pass the cookie value in as an argument (e.g. getUser(sessionToken)) so a change is a new cache key.`);
27
80
  }
28
81
  //#endregion
29
- //#region src/server/debug.ts
82
+ //#region src/server/request-context.ts
30
83
  /**
31
- * Runtime debug flag for timber.js.
84
+ * Returns a read-only view of the current request's headers.
32
85
  *
33
- * Two distinct functions for two distinct security levels:
86
+ * Available in middleware, access checks, server components, and server actions.
87
+ * Throws if called outside a request context (security principle #2: no global fallback).
88
+ */
89
+ function getHeaders() {
90
+ const store = requestContextAls.getStore();
91
+ if (!store) throw new Error("[timber] getHeaders() called outside of a request context. It can only be used in middleware, access checks, server components, and server actions.");
92
+ return store.headers;
93
+ }
94
+ /**
95
+ * Returns the value of a single request header, or undefined if absent.
34
96
  *
35
- * ## `isDebug()` — server-side logging only
97
+ * Thin wrapper over `getHeaders().get(name)` for the common case where
98
+ * you need exactly one header.
36
99
  *
37
- * Returns true when timber's debug/warning messages should be written to
38
- * stderr / the server console. This NEVER affects what is sent to the
39
- * client (no error details, no timing headers, no stack traces).
100
+ * @internal — not part of the public API. Use `getHeaders().get(name)` instead.
101
+ */
102
+ function getHeader(name) {
103
+ return getHeaders().get(name) ?? void 0;
104
+ }
105
+ /**
106
+ * Returns the current request's raw URLSearchParams.
40
107
  *
41
- * Active when any of:
42
- * - `NODE_ENV !== 'production'` (standard dev mode)
43
- * - `TIMBER_DEBUG` env var is set to a truthy value at runtime
44
- * - `timber.config.ts` has `debug: true`
108
+ * @internal — not part of the public API. Use `defineSearchParams().get()` instead.
109
+ */
110
+ function getSearchParams() {
111
+ const store = requestContextAls.getStore();
112
+ if (!store) throw new Error("[timber] getSearchParams() called outside of a request context. It can only be used in middleware, access checks, server components, and server actions.");
113
+ return store.searchParams;
114
+ }
115
+ _setGetSearchParamsFn(getSearchParams);
116
+ _setGetSegmentParamsFn(getSegmentParams);
117
+ /**
118
+ * Returns the current request's coerced segment params.
45
119
  *
46
- * ## `isDevMode()` — client-visible dev behavior
120
+ * The optional `segmentPath` argument exists only for TypeScript narrowing —
121
+ * it does not affect the runtime return value. Pass a segment path
122
+ * (e.g. `'/(browse)/[artistSlug]/[year]'`) to narrow the return type to the
123
+ * exact params shape for that segment.
47
124
  *
48
- * Returns true ONLY when `NODE_ENV !== 'production'`. This gates anything
49
- * that changes what clients can observe:
50
- * - Dev error pages with stack traces (fallback-error.ts)
51
- * - Detailed Server-Timing headers (pipeline.ts)
52
- * - Error messages in action INTERNAL_ERROR payloads (action-client.ts)
53
- * - Pipeline error handler wiring (Vite overlay)
125
+ * Without an argument, returns all coerced params with optional types.
54
126
  *
55
- * `isDevMode()` is statically replaced in production builds → the guarded
56
- * code is tree-shaken to zero bytes. TIMBER_DEBUG cannot enable it.
127
+ * See design/41-global-params.md §Function Signatures
128
+ */
129
+ function getSegmentParams(segmentPath) {
130
+ const store = requestContextAls.getStore();
131
+ if (!store) throw new Error("[timber] getSegmentParams() called outside of a request context. It can only be used in middleware, access checks, server components, and server actions.");
132
+ if (!store.segmentParams) throw new Error("[timber] getSegmentParams() called before route matching completed. Segment params are not available until after the route is matched.");
133
+ const slotResolved = resolveSegmentParams(store.segmentParams, store.slotParams, segmentPath);
134
+ if (slotResolved !== store.segmentParams) return slotResolved;
135
+ if (segmentPath && isDebug() && store.matchedSegmentPath) {
136
+ const expected = extractDynamicSegments(segmentPath);
137
+ const actual = extractDynamicSegments(store.matchedSegmentPath);
138
+ const missing = expected.filter((s) => !actual.includes(s));
139
+ if (missing.length > 0) console.warn(`[timber] getSegmentParams('${segmentPath}') called but current segment is '${store.matchedSegmentPath}'\n Missing params: ${missing.join(", ")}\n These will be undefined at runtime despite the type annotation.`);
140
+ }
141
+ return store.segmentParams;
142
+ }
143
+ /**
144
+ * Set the segment params on the current request context.
145
+ * Called by the pipeline after route matching and param coercion.
57
146
  *
58
- * Usage:
59
- * In Cloudflare Workers wrangler.toml:
60
- * [vars]
61
- * TIMBER_DEBUG = "1"
147
+ * @internal — framework use only
148
+ */
149
+ function setSegmentParams(params) {
150
+ const store = requestContextAls.getStore();
151
+ if (!store) throw new Error("[timber] setSegmentParams() called outside of a request context.");
152
+ store.segmentParams = params;
153
+ }
154
+ /**
155
+ * Store the matched segment path (e.g. '/(browse)/[artistSlug]/[year]') for
156
+ * dev-mode validation in getSegmentParams().
62
157
  *
63
- * In Node.js:
64
- * TIMBER_DEBUG=1 node server.js
158
+ * @internal — framework use only
159
+ */
160
+ function setMatchedSegmentPath(segmentPath) {
161
+ const store = requestContextAls.getStore();
162
+ if (!store) return;
163
+ store.matchedSegmentPath = segmentPath;
164
+ }
165
+ /**
166
+ * Extract dynamic segment names from a route pattern.
167
+ * '/[artistSlug]/[year]' → ['artistSlug', 'year']
168
+ * '/docs/[...slug]' → ['slug']
169
+ */
170
+ function extractDynamicSegments(route) {
171
+ const segments = [];
172
+ const re = /\[{1,2}\.{0,3}(\w+)\]{1,2}/g;
173
+ let m;
174
+ while ((m = re.exec(route)) !== null) segments.push(m[1]);
175
+ return segments;
176
+ }
177
+ /**
178
+ * Returns the raw search string from the current request URL (e.g. "?foo=bar").
179
+ * Synchronous — safe for use in `redirect()` which throws synchronously.
65
180
  *
66
- * In timber.config.ts:
67
- * export default { debug: true }
181
+ * Returns empty string if called outside a request context (non-throwing for
182
+ * use in redirect's optional preserveSearchParams path).
68
183
  *
69
- * See design/13-security.md for the security taxonomy.
70
- * See design/18-build-system.md for build pipeline details.
184
+ * @internal — used by redirect() for preserveSearchParams support.
71
185
  */
186
+ function getRequestSearchString() {
187
+ return requestContextAls.getStore()?.searchString ?? "";
188
+ }
72
189
  /**
73
- * Check if the application is running in development mode.
190
+ * Run a callback within a request context. Used by the pipeline to establish
191
+ * per-request ALS scope so that `getHeaders()` and `getCookies()` work.
74
192
  *
75
- * This is the ONLY function that should gate client-visible dev behavior:
76
- * - Dev error pages with stack traces
77
- * - Server-Timing mode default (`'detailed'` in dev, `'total'` in prod)
78
- * - Error messages in action `INTERNAL_ERROR` payloads
79
- * - Pipeline error handler wiring (Vite overlay)
193
+ * When `options.cookies` is given, the context's `parsedCookies` map is a
194
+ * copy of it and the raw `cookieHeader` is left empty, so
195
+ * `parseCookieHeader` never runs for that request. Re-entry callers pass the
196
+ * action's post-mutation cookie state this way (TIM-868).
80
197
  *
81
- * Returns `process.env.NODE_ENV !== 'production'`, which is statically
82
- * replaced by the bundler in production builds. Code guarded by this
83
- * function is tree-shaken to zero bytes in production.
198
+ * Each request context also opens its own `React.cache` scope, shared by
199
+ * everything that runs inside it — proxy.ts, middleware.ts, route handlers,
200
+ * and the render (design/02-rendering-pipeline.md §"Cache Scoping Model").
84
201
  *
85
- * TIMBER_DEBUG does NOT enable this — that would leak server internals
86
- * to clients. Use `isDebug()` for server-side-only logging.
202
+ * @param req - The incoming Request object.
203
+ * @param fn - The function to run within the request context.
87
204
  */
88
- function isDevMode() {
89
- return process.env.NODE_ENV !== "production";
205
+ function runWithRequestContext(req, fn, { reactCacheScope, cookies } = {}) {
206
+ const originalCopy = new Headers(req.headers);
207
+ const appVisible = appVisibleSearch(new URL(req.url));
208
+ const seed = cookies ? new Map(cookies) : void 0;
209
+ const store = {
210
+ request: req,
211
+ headers: freezeHeaders(req.headers),
212
+ originalHeaders: originalCopy,
213
+ cookieHeader: seed ? "" : req.headers.get("cookie") ?? "",
214
+ parsedCookies: seed,
215
+ searchParams: appVisible.params,
216
+ searchString: appVisible.search,
217
+ cookieJar: /* @__PURE__ */ new Map(),
218
+ flushed: false,
219
+ mutableContext: false
220
+ };
221
+ return requestContextAls.run(store, () => runWithReactCacheScope(fn, reactCacheScope));
90
222
  }
91
223
  /**
92
- * Config-level debug override. Set via `setDebugFromConfig()` during
93
- * initialization when timber.config.ts has `debug: true`.
224
+ * Enable cookie mutation for the current context. Called by the framework
225
+ * when entering middleware.ts, server actions, or route.ts handlers.
226
+ *
227
+ * See design/29-cookies.md §"Context Tracking"
228
+ */
229
+ function setMutableCookieContext(mutable) {
230
+ const store = requestContextAls.getStore();
231
+ if (store) store.mutableContext = mutable;
232
+ }
233
+ /**
234
+ * Mark the response as flushed (headers committed). After this point,
235
+ * cookie mutations log a warning instead of throwing.
236
+ *
237
+ * See design/29-cookies.md §"Streaming Constraint: Post-Flush Cookie Warning"
94
238
  */
95
- var _configDebug = false;
239
+ function markResponseFlushed() {
240
+ const store = requestContextAls.getStore();
241
+ if (store) store.flushed = true;
242
+ }
96
243
  /**
97
- * Check if timber debug logging is active (server-side only).
244
+ * Apply middleware-injected request headers to the current request context.
98
245
  *
99
- * Returns true if ANY of these conditions hold:
100
- * - NODE_ENV is not 'production' (standard dev mode)
101
- * - TIMBER_DEBUG environment variable is set to a truthy value at runtime
102
- * - timber.config.ts has `debug: true`
246
+ * Called by the pipeline after middleware.ts runs. Merges overlay headers
247
+ * on top of the original request headers so downstream code (access.ts,
248
+ * server components, server actions) sees them via `getHeaders()`.
103
249
  *
104
- * This function controls ONLY server-side logging — messages written to
105
- * stderr or the server console. It NEVER affects client-visible behavior
106
- * (error pages, response headers, action payloads). For client-visible
107
- * behavior, use `isDevMode()`.
250
+ * The original request headers are never mutated — a new frozen Headers
251
+ * object is created with the overlay applied on top.
108
252
  *
109
- * The TIMBER_DEBUG check is deliberately written as a dynamic property
110
- * access so bundlers cannot statically replace it.
253
+ * See design/07-routing.md §"Request Header Injection"
111
254
  */
112
- function isDebug() {
113
- if (process.env.NODE_ENV !== "production") return true;
114
- if (_configDebug) return true;
115
- return _readTimberDebugEnv();
255
+ function applyRequestHeaderOverlay(overlay) {
256
+ const store = requestContextAls.getStore();
257
+ if (!store) throw new Error("[timber] applyRequestHeaderOverlay() called outside of a request context.");
258
+ let hasOverlay = false;
259
+ overlay.forEach(() => {
260
+ hasOverlay = true;
261
+ });
262
+ if (!hasOverlay) return;
263
+ const merged = new Headers(store.originalHeaders);
264
+ overlay.forEach((value, key) => {
265
+ merged.set(key, value);
266
+ });
267
+ store.headers = freezeHeaders(merged);
116
268
  }
269
+ var MUTATING_METHODS = /* @__PURE__ */ new Set([
270
+ "set",
271
+ "append",
272
+ "delete"
273
+ ]);
117
274
  /**
118
- * Read TIMBER_DEBUG from the environment at runtime.
275
+ * Wrap a Headers object in a Proxy that throws on mutating methods.
276
+ * Object.freeze doesn't work on Headers (native internal slots), so we
277
+ * intercept property access and reject set/append/delete at runtime.
119
278
  *
120
- * Extracted to a separate function to:
121
- * 1. Prevent bundler inlining (cross-module function calls are not inlined)
122
- * 2. Handle platforms where `process` may not exist (Cloudflare Workers)
123
- * 3. Support globalThis.__TIMBER_DEBUG for programmatic control
279
+ * Read methods (get, has, entries, etc.) must be bound to the underlying
280
+ * Headers instance because they access private #headersList slots.
124
281
  */
125
- function _readTimberDebugEnv() {
126
- if (globalThis.__TIMBER_DEBUG) return true;
127
- try {
128
- const val = typeof process !== "undefined" && process.env ? process.env["TIMBER_DEBUG"] : void 0;
129
- if (val && val !== "0" && val !== "false") return true;
130
- } catch {}
131
- return false;
282
+ function freezeHeaders(source) {
283
+ const copy = new Headers(source);
284
+ return new Proxy(copy, { get(target, prop) {
285
+ if (typeof prop === "string" && MUTATING_METHODS.has(prop)) return () => {
286
+ throw new Error(`[timber] getHeaders() returns a read-only Headers object. Calling .${prop}() is not allowed. Use ctx.requestHeaders in middleware to inject headers for downstream components.`);
287
+ };
288
+ const value = Reflect.get(target, prop);
289
+ if (typeof value === "function") return value.bind(target);
290
+ return value;
291
+ } });
132
292
  }
133
293
  //#endregion
134
294
  //#region src/server/cookie-parsing.ts
@@ -261,8 +421,8 @@ function parseSetCookie(header) {
261
421
  * Cookie Context — per-request cookie API and on-the-wire helpers.
262
422
  *
263
423
  * Split out of `request-context.ts` (TIM-853) so the cookie subsystem
264
- * — encoding contract, options grammar, parser, serializer, RYW map,
265
- * and rerender seed — lives in one file. The headers/scope/params APIs
424
+ * — encoding contract, options grammar, parser, serializer, and RYW map
425
+ * — lives in one file. The headers/scope/params APIs
266
426
  * stay in `request-context.ts` and call into this module via the
267
427
  * exported helpers.
268
428
  *
@@ -295,18 +455,22 @@ function getCookieJar() {
295
455
  const map = store.parsedCookies;
296
456
  return {
297
457
  get(name) {
458
+ noteCookieReadForReactCache(name);
298
459
  return map.get(name);
299
460
  },
300
461
  has(name) {
462
+ noteCookieReadForReactCache(name);
301
463
  return map.has(name);
302
464
  },
303
465
  getAll() {
466
+ noteCookieReadForReactCache("*");
304
467
  return Array.from(map.entries()).map(([name, value]) => ({
305
468
  name,
306
469
  value
307
470
  }));
308
471
  },
309
472
  get size() {
473
+ noteCookieReadForReactCache("*");
310
474
  return map.size;
311
475
  },
312
476
  set(name, value, options) {
@@ -332,6 +496,7 @@ function getCookieJar() {
332
496
  options: opts
333
497
  });
334
498
  map.set(name, raw ? wireValue : value);
499
+ warnIfCachedCookieIsWritten(name);
335
500
  },
336
501
  setFromHeaders(headers) {
337
502
  assertMutable(store, "setFromHeaders");
@@ -364,6 +529,7 @@ function getCookieJar() {
364
529
  options: opts
365
530
  });
366
531
  map.delete(name);
532
+ warnIfCachedCookieIsWritten(name);
367
533
  },
368
534
  clear() {
369
535
  assertMutable(store, "clear");
@@ -378,8 +544,10 @@ function getCookieJar() {
378
544
  }
379
545
  });
380
546
  map.clear();
547
+ warnIfCachedCookieIsWritten("*");
381
548
  },
382
549
  toString() {
550
+ noteCookieReadForReactCache("*");
383
551
  return Array.from(map.entries()).map(([name, value]) => `${name}=${encodeURIComponent(value)}`).join("; ");
384
552
  }
385
553
  };
@@ -399,32 +567,6 @@ var DEFAULT_COOKIE_OPTIONS = {
399
567
  sameSite: "lax"
400
568
  };
401
569
  /**
402
- * Per-request seed map of cookie name → value, registered out-of-band so the
403
- * pipeline's `runWithRequestContext` call can pick it up without changing
404
- * the function's calling convention. Stored in a WeakMap keyed by the
405
- * synthetic Request object built for the rerender path, so the seed lives
406
- * exactly as long as the request and is collected with it.
407
- *
408
- * The seed exists to eliminate the parse/serialize round-trip on the no-JS
409
- * form-rerender path — see ONGOING_SECURITY.md H-3 (TIM-868). The action's
410
- * post-mutation RYW snapshot is threaded directly into the rerender scope's
411
- * `parsedCookies` map, bypassing `parseCookieHeader` entirely.
412
- */
413
- var seededRequestCookies = /* @__PURE__ */ new WeakMap();
414
- /**
415
- * Pop the seed (if any) for `req` and return it. Called from
416
- * `runWithRequestContext` exactly once per request — the seed is consumed
417
- * eagerly so it cannot leak into a hypothetical future re-use of the same
418
- * Request reference.
419
- *
420
- * @internal — framework use only.
421
- */
422
- function consumeSeededCookies(req) {
423
- const seed = seededRequestCookies.get(req);
424
- if (seed) seededRequestCookies.delete(req);
425
- return seed;
426
- }
427
- /**
428
570
  * Collect all Set-Cookie headers from the cookie jar.
429
571
  * Called by the framework at flush time to apply cookies to the response.
430
572
  *
@@ -456,217 +598,11 @@ function setRaw(store, readMap, name, value, options) {
456
598
  value,
457
599
  options
458
600
  });
601
+ warnIfCachedCookieIsWritten(name);
459
602
  if (options.maxAge === 0) readMap.delete(name);
460
603
  else readMap.set(name, safeDecodeCookieValue(value));
461
604
  }
462
605
  //#endregion
463
- //#region src/server/request-context.ts
464
- /**
465
- * Returns a read-only view of the current request's headers.
466
- *
467
- * Available in middleware, access checks, server components, and server actions.
468
- * Throws if called outside a request context (security principle #2: no global fallback).
469
- */
470
- function getHeaders() {
471
- const store = requestContextAls.getStore();
472
- if (!store) throw new Error("[timber] getHeaders() called outside of a request context. It can only be used in middleware, access checks, server components, and server actions.");
473
- return store.headers;
474
- }
475
- /**
476
- * Returns the value of a single request header, or undefined if absent.
477
- *
478
- * Thin wrapper over `getHeaders().get(name)` for the common case where
479
- * you need exactly one header.
480
- *
481
- * @internal — not part of the public API. Use `getHeaders().get(name)` instead.
482
- */
483
- function getHeader(name) {
484
- return getHeaders().get(name) ?? void 0;
485
- }
486
- /**
487
- * Returns the current request's raw URLSearchParams.
488
- *
489
- * @internal — not part of the public API. Use `defineSearchParams().get()` instead.
490
- */
491
- function getSearchParams() {
492
- const store = requestContextAls.getStore();
493
- if (!store) throw new Error("[timber] getSearchParams() called outside of a request context. It can only be used in middleware, access checks, server components, and server actions.");
494
- return store.searchParams;
495
- }
496
- _setGetSearchParamsFn(getSearchParams);
497
- _setGetSegmentParamsFn(getSegmentParams);
498
- /**
499
- * Returns the current request's coerced segment params.
500
- *
501
- * The optional `segmentPath` argument exists only for TypeScript narrowing —
502
- * it does not affect the runtime return value. Pass a segment path
503
- * (e.g. `'/(browse)/[artistSlug]/[year]'`) to narrow the return type to the
504
- * exact params shape for that segment.
505
- *
506
- * Without an argument, returns all coerced params with optional types.
507
- *
508
- * See design/41-global-params.md §Function Signatures
509
- */
510
- function getSegmentParams(segmentPath) {
511
- const store = requestContextAls.getStore();
512
- if (!store) throw new Error("[timber] getSegmentParams() called outside of a request context. It can only be used in middleware, access checks, server components, and server actions.");
513
- if (!store.segmentParams) throw new Error("[timber] getSegmentParams() called before route matching completed. Segment params are not available until after the route is matched.");
514
- const slotResolved = resolveSegmentParams(store.segmentParams, store.slotParams, segmentPath);
515
- if (slotResolved !== store.segmentParams) return slotResolved;
516
- if (segmentPath && isDebug() && store.matchedSegmentPath) {
517
- const expected = extractDynamicSegments(segmentPath);
518
- const actual = extractDynamicSegments(store.matchedSegmentPath);
519
- const missing = expected.filter((s) => !actual.includes(s));
520
- if (missing.length > 0) console.warn(`[timber] getSegmentParams('${segmentPath}') called but current segment is '${store.matchedSegmentPath}'\n Missing params: ${missing.join(", ")}\n These will be undefined at runtime despite the type annotation.`);
521
- }
522
- return store.segmentParams;
523
- }
524
- /**
525
- * Set the segment params on the current request context.
526
- * Called by the pipeline after route matching and param coercion.
527
- *
528
- * @internal — framework use only
529
- */
530
- function setSegmentParams(params) {
531
- const store = requestContextAls.getStore();
532
- if (!store) throw new Error("[timber] setSegmentParams() called outside of a request context.");
533
- store.segmentParams = params;
534
- }
535
- /**
536
- * Store the matched segment path (e.g. '/(browse)/[artistSlug]/[year]') for
537
- * dev-mode validation in getSegmentParams().
538
- *
539
- * @internal — framework use only
540
- */
541
- function setMatchedSegmentPath(segmentPath) {
542
- const store = requestContextAls.getStore();
543
- if (!store) return;
544
- store.matchedSegmentPath = segmentPath;
545
- }
546
- /**
547
- * Extract dynamic segment names from a route pattern.
548
- * '/[artistSlug]/[year]' → ['artistSlug', 'year']
549
- * '/docs/[...slug]' → ['slug']
550
- */
551
- function extractDynamicSegments(route) {
552
- const segments = [];
553
- const re = /\[{1,2}\.{0,3}(\w+)\]{1,2}/g;
554
- let m;
555
- while ((m = re.exec(route)) !== null) segments.push(m[1]);
556
- return segments;
557
- }
558
- /**
559
- * Returns the raw search string from the current request URL (e.g. "?foo=bar").
560
- * Synchronous — safe for use in `redirect()` which throws synchronously.
561
- *
562
- * Returns empty string if called outside a request context (non-throwing for
563
- * use in redirect's optional preserveSearchParams path).
564
- *
565
- * @internal — used by redirect() for preserveSearchParams support.
566
- */
567
- function getRequestSearchString() {
568
- return requestContextAls.getStore()?.searchString ?? "";
569
- }
570
- /**
571
- * Run a callback within a request context. Used by the pipeline to establish
572
- * per-request ALS scope so that `getHeaders()` and `getCookies()` work.
573
- *
574
- * If the request was previously registered via `seedRequestCookies`, the
575
- * resulting context's `parsedCookies` map is initialized from the seed and
576
- * the raw `cookieHeader` is left empty — `parseCookieHeader` is never called
577
- * for that request. This is the no-JS form-rerender path. See TIM-868.
578
- *
579
- * @param req - The incoming Request object.
580
- * @param fn - The function to run within the request context.
581
- */
582
- function runWithRequestContext(req, fn) {
583
- const originalCopy = new Headers(req.headers);
584
- const appVisible = appVisibleSearch(new URL(req.url));
585
- const seed = consumeSeededCookies(req);
586
- const store = {
587
- headers: freezeHeaders(req.headers),
588
- originalHeaders: originalCopy,
589
- cookieHeader: seed ? "" : req.headers.get("cookie") ?? "",
590
- parsedCookies: seed,
591
- searchParams: appVisible.params,
592
- searchString: appVisible.search,
593
- cookieJar: /* @__PURE__ */ new Map(),
594
- flushed: false,
595
- mutableContext: false
596
- };
597
- return requestContextAls.run(store, fn);
598
- }
599
- /**
600
- * Enable cookie mutation for the current context. Called by the framework
601
- * when entering middleware.ts, server actions, or route.ts handlers.
602
- *
603
- * See design/29-cookies.md §"Context Tracking"
604
- */
605
- function setMutableCookieContext(mutable) {
606
- const store = requestContextAls.getStore();
607
- if (store) store.mutableContext = mutable;
608
- }
609
- /**
610
- * Mark the response as flushed (headers committed). After this point,
611
- * cookie mutations log a warning instead of throwing.
612
- *
613
- * See design/29-cookies.md §"Streaming Constraint: Post-Flush Cookie Warning"
614
- */
615
- function markResponseFlushed() {
616
- const store = requestContextAls.getStore();
617
- if (store) store.flushed = true;
618
- }
619
- /**
620
- * Apply middleware-injected request headers to the current request context.
621
- *
622
- * Called by the pipeline after middleware.ts runs. Merges overlay headers
623
- * on top of the original request headers so downstream code (access.ts,
624
- * server components, server actions) sees them via `getHeaders()`.
625
- *
626
- * The original request headers are never mutated — a new frozen Headers
627
- * object is created with the overlay applied on top.
628
- *
629
- * See design/07-routing.md §"Request Header Injection"
630
- */
631
- function applyRequestHeaderOverlay(overlay) {
632
- const store = requestContextAls.getStore();
633
- if (!store) throw new Error("[timber] applyRequestHeaderOverlay() called outside of a request context.");
634
- let hasOverlay = false;
635
- overlay.forEach(() => {
636
- hasOverlay = true;
637
- });
638
- if (!hasOverlay) return;
639
- const merged = new Headers(store.originalHeaders);
640
- overlay.forEach((value, key) => {
641
- merged.set(key, value);
642
- });
643
- store.headers = freezeHeaders(merged);
644
- }
645
- var MUTATING_METHODS = /* @__PURE__ */ new Set([
646
- "set",
647
- "append",
648
- "delete"
649
- ]);
650
- /**
651
- * Wrap a Headers object in a Proxy that throws on mutating methods.
652
- * Object.freeze doesn't work on Headers (native internal slots), so we
653
- * intercept property access and reject set/append/delete at runtime.
654
- *
655
- * Read methods (get, has, entries, etc.) must be bound to the underlying
656
- * Headers instance because they access private #headersList slots.
657
- */
658
- function freezeHeaders(source) {
659
- const copy = new Headers(source);
660
- return new Proxy(copy, { get(target, prop) {
661
- if (typeof prop === "string" && MUTATING_METHODS.has(prop)) return () => {
662
- throw new Error(`[timber] getHeaders() returns a read-only Headers object. Calling .${prop}() is not allowed. Use ctx.requestHeaders in middleware to inject headers for downstream components.`);
663
- };
664
- const value = Reflect.get(target, prop);
665
- if (typeof value === "function") return value.bind(target);
666
- return value;
667
- } });
668
- }
669
- //#endregion
670
606
  //#region src/server/primitives.ts
671
607
  /**
672
608
  * Check if a value is JSON-serializable without data loss.
@@ -715,36 +651,13 @@ function warnIfNotSerializable(data, callerName) {
715
651
  if (!isDebug()) return;
716
652
  if (data === void 0) return;
717
653
  const issue = findNonSerializable(data);
718
- if (issue) console.warn(`[timber] ${callerName}: ${issue}. Data passed to deny() or RenderError must be JSON-serializable because the post-flush path uses JSON.stringify, not React Flight.`);
719
- }
720
- var DENY_BRAND = Symbol.for("timber:signal.deny");
721
- var REDIRECT_BRAND = Symbol.for("timber:signal.redirect");
722
- var RENDER_ERROR_BRAND = Symbol.for("timber:signal.render-error");
723
- var SSR_STREAM_ERROR_BRAND = Symbol.for("timber:signal.ssr-stream-error");
724
- function hasBrand(value, brand) {
725
- return typeof value === "object" && value !== null && brand in value;
726
- }
727
- /** True for any `DenySignal`, regardless of which module evaluation created it. */
728
- function isDenySignal(error) {
729
- return hasBrand(error, DENY_BRAND);
730
- }
731
- /** True for any `RedirectSignal`, regardless of which module evaluation created it. */
732
- function isRedirectSignal(error) {
733
- return hasBrand(error, REDIRECT_BRAND);
734
- }
735
- /**
736
- * True for any `SsrStreamError`. This one also crosses Vite environments:
737
- * it is thrown in SSR and caught in RSC, which are separate module graphs in
738
- * the same process — the brand is the only identity the two share.
739
- */
740
- function isSsrStreamError(error) {
741
- return hasBrand(error, SSR_STREAM_ERROR_BRAND);
654
+ if (issue) console.warn(`[timber] ${callerName}: ${issue}. Data passed to deny() must be JSON-serializable because the post-flush path uses JSON.stringify, not React Flight.`);
742
655
  }
743
656
  /**
744
657
  * Render-phase signal thrown by `deny()`. Caught by the framework to produce
745
658
  * the correct HTTP status code (segment context) or graceful degradation (slot context).
746
659
  *
747
- * Detect with `isDenySignal()`, never `instanceof` — see "Signal branding" above.
660
+ * Detect with `isDenySignal()`, never `instanceof` — see "Signal branding" in signal-identity.ts.
748
661
  */
749
662
  var DenySignal = class extends Error {
750
663
  [DENY_BRAND] = true;
@@ -814,9 +727,9 @@ var DenySignal = class extends Error {
814
727
  * deny(404, { dangerouslyPassData: { resourceId: params.id } }) // explicit client opt-in
815
728
  * ```
816
729
  *
817
- * Accepts any 4xx or 5xx status code. This replaces the need for
818
- * `throw new RenderError(...)` in user code — RenderError is now an
819
- * internal pipeline detail.
730
+ * Accepts any 4xx or 5xx status code — it is also how a page renders a 5xx
731
+ * status file with data. An unplanned throw needs no primitive: it renders
732
+ * the error page with a 500.
820
733
  *
821
734
  * @param status - HTTP status code (4xx or 5xx). Default: 403.
822
735
  * @param options - Optional message and/or data to pass to the client.
@@ -832,7 +745,7 @@ function deny(status, options) {
832
745
  * Render-phase signal thrown by `redirect()` and `redirectExternal()`.
833
746
  * Caught by the framework to produce a 3xx response or client-side navigation.
834
747
  *
835
- * Detect with `isRedirectSignal()`, never `instanceof` — see "Signal branding" above.
748
+ * Detect with `isRedirectSignal()`, never `instanceof` — see "Signal branding" in signal-identity.ts.
836
749
  */
837
750
  var RedirectSignal = class extends Error {
838
751
  [REDIRECT_BRAND] = true;
@@ -846,24 +759,6 @@ var RedirectSignal = class extends Error {
846
759
  }
847
760
  };
848
761
  /**
849
- * Returns true if the error is a framework control-flow signal (RedirectSignal
850
- * or DenySignal) rather than a genuine application error. Works both for direct
851
- * instances (RSC-side) and deserialized digest errors (SSR cross-boundary).
852
- *
853
- * See also: isFrameworkSignalError() in client/browser-dev.ts (client-only).
854
- */
855
- function isControlFlowSignal(error) {
856
- if (isDenySignal(error) || isRedirectSignal(error)) return true;
857
- if (error && typeof error === "object") {
858
- const digest = error.digest;
859
- if (typeof digest === "string") try {
860
- const parsed = JSON.parse(digest);
861
- if (parsed && typeof parsed === "object") return parsed.type === "redirect" || parsed.type === "deny";
862
- } catch {}
863
- }
864
- return false;
865
- }
866
- /**
867
762
  * Redirect to a relative path. Rejects absolute and protocol-relative URLs.
868
763
  * Use `redirectExternal()` for external redirects with an allow-list.
869
764
  *
@@ -911,41 +806,6 @@ function redirectExternal(url, allowList, status = 302) {
911
806
  if (status < 300 || status > 399) throw new Error(`redirectExternal() requires a 3xx status code, got ${status}.`);
912
807
  throw new RedirectSignal(validateExternalRedirectUrl(url, allowList), status);
913
808
  }
914
- /**
915
- * Typed throw for render-phase errors that carry structured context to error boundaries.
916
- *
917
- * The `digest` (code + data) is serialized into the RSC stream separately from the
918
- * Error instance — only the digest crosses the RSC → client boundary.
919
- *
920
- * @example
921
- * ```ts
922
- * throw new RenderError('PRODUCT_NOT_FOUND', {
923
- * title: 'Product not found',
924
- * resourceId: params.id,
925
- * })
926
- * ```
927
- *
928
- * Detect with `isRenderError()`, never `instanceof` — see "Signal branding" above.
929
- */
930
- var RenderError = class extends Error {
931
- [RENDER_ERROR_BRAND] = true;
932
- code;
933
- digest;
934
- status;
935
- constructor(code, data, options) {
936
- super(`RenderError: ${code}`);
937
- this.name = "RenderError";
938
- this.code = code;
939
- this.digest = {
940
- code,
941
- data
942
- };
943
- warnIfNotSerializable(data, "RenderError");
944
- const status = options?.status ?? 500;
945
- if (status < 400 || status > 599) throw new Error(`RenderError status must be 4xx or 5xx, got ${status}.`);
946
- this.status = status;
947
- }
948
- };
949
809
  var _waitUntilWarned = false;
950
810
  /**
951
811
  * Register a promise to be kept alive after the response is sent.
@@ -970,6 +830,376 @@ function waitUntil(promise) {
970
830
  }
971
831
  }
972
832
  //#endregion
973
- export { getCookieJar as C, getWaitUntil as D, isDevMode as E, getCookie as S, isDebug as T, markResponseFlushed as _, isControlFlowSignal as a, setMutableCookieContext as b, isSsrStreamError as c, waitUntil as d, applyRequestHeaderOverlay as f, getSegmentParams as g, getSearchParams as h, deny as i, redirect as l, getHeaders as m, RedirectSignal as n, isDenySignal as o, getHeader as p, RenderError as r, isRedirectSignal as s, DenySignal as t, redirectExternal as u, runWithRequestContext as v, getSetCookieHeaders as w, setSegmentParams as x, setMatchedSegmentPath as y };
833
+ //#region src/server/form-data.ts
834
+ /**
835
+ * FormData preprocessing — schema-agnostic conversion of FormData to typed objects.
836
+ *
837
+ * FormData is all strings. Schema validation expects typed values. This module
838
+ * bridges the gap with intelligent coercion that runs *before* schema validation.
839
+ *
840
+ * Inspired by zod-form-data, but schema-agnostic — works with any Standard Schema
841
+ * library (Zod, Valibot, ArkType).
842
+ *
843
+ * See design/08-forms-and-actions.md §"parseFormData() and coerce helpers"
844
+ */
845
+ /**
846
+ * Convert FormData into a plain object with intelligent coercion.
847
+ *
848
+ * Handles:
849
+ * - **Duplicate keys → arrays**: `tags=js&tags=ts` → `{ tags: ["js", "ts"] }`
850
+ * - **Nested dot-paths**: `user.name=Alice` → `{ user: { name: "Alice" } }`
851
+ * - **Empty strings → undefined**: Enables `.optional()` semantics in schemas
852
+ * - **Empty Files → undefined**: File inputs with no selection become `undefined`
853
+ * - **Strips `$ACTION_*` fields**: React's internal hidden fields are excluded
854
+ */
855
+ function parseFormData(formData) {
856
+ const flat = {};
857
+ for (const key of new Set(formData.keys())) {
858
+ if (key.startsWith("$ACTION_")) continue;
859
+ if (DANGEROUS_KEYS.has(key)) continue;
860
+ const processed = formData.getAll(key).map(normalizeValue);
861
+ if (processed.length === 1) flat[key] = processed[0];
862
+ else flat[key] = processed.filter((v) => v !== void 0 && v !== "");
863
+ }
864
+ return expandDotPaths(flat);
865
+ }
866
+ /**
867
+ * Normalize a single FormData entry value.
868
+ * - Empty File objects (no selection) → undefined
869
+ * - Strings pass through as-is (empty strings stay as empty strings so
870
+ * schema validators like `z.string().min(1, 'Required')` can produce
871
+ * their custom error messages instead of a generic type mismatch)
872
+ * - Everything else passes through as-is
873
+ */
874
+ function normalizeValue(value) {
875
+ if (value instanceof File && value.size === 0 && value.name === "") return;
876
+ return value;
877
+ }
878
+ /**
879
+ * Expand dot-notation keys into nested objects.
880
+ * `{ "user.name": "Alice", "user.age": "30" }` → `{ user: { name: "Alice", age: "30" } }`
881
+ *
882
+ * Keys without dots are left as-is. Bracket notation (e.g. `items[0]`) is NOT
883
+ * supported — use dot notation (`items.0`) instead.
884
+ */
885
+ function expandDotPaths(flat) {
886
+ const result = {};
887
+ let hasDotPaths = false;
888
+ for (const key of Object.keys(flat)) if (key.includes(".")) {
889
+ hasDotPaths = true;
890
+ break;
891
+ }
892
+ if (!hasDotPaths) return flat;
893
+ for (const [key, value] of Object.entries(flat)) {
894
+ const parts = key.split(".");
895
+ if (parts.some((p) => DANGEROUS_KEYS.has(p))) continue;
896
+ if (parts.length === 1) {
897
+ result[parts[0]] = value;
898
+ continue;
899
+ }
900
+ let current = result;
901
+ for (let i = 0; i < parts.length - 1; i++) {
902
+ const part = parts[i];
903
+ if (current[part] === void 0 || current[part] === null) current[part] = {};
904
+ if (typeof current[part] !== "object" || current[part] instanceof File) current[part] = {};
905
+ current = current[part];
906
+ }
907
+ current[parts[parts.length - 1]] = value;
908
+ }
909
+ return result;
910
+ }
911
+ var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__"]);
912
+ /**
913
+ * Schema-agnostic coercion primitives for common FormData patterns.
914
+ *
915
+ * These are plain transform functions — they compose with any schema library's
916
+ * `transform`/`preprocess` pipeline:
917
+ *
918
+ * ```ts
919
+ * // Zod
920
+ * z.preprocess(coerce.number, z.number())
921
+ * // Valibot
922
+ * v.pipe(v.unknown(), v.transform(coerce.number), v.number())
923
+ * ```
924
+ */
925
+ var coerce = {
926
+ /**
927
+ * Coerce an empty string to undefined for optional text fields.
928
+ * Use with `.optional()` schemas where an empty input means "not provided".
929
+ *
930
+ * ```ts
931
+ * // Zod
932
+ * z.unknown().transform(coerce.text).pipe(z.string().optional())
933
+ * // Valibot
934
+ * v.pipe(v.unknown(), v.transform(coerce.text), v.optional(v.string()))
935
+ * ```
936
+ */
937
+ text(value) {
938
+ if (value === void 0 || value === null || value === "") return void 0;
939
+ if (typeof value === "string") return value;
940
+ },
941
+ /**
942
+ * Coerce a string to a number.
943
+ * - `"42"` → `42`
944
+ * - `"3.14"` → `3.14`
945
+ * - `""` / `undefined` / `null` → `undefined`
946
+ * - Non-numeric strings → `undefined` (schema validation will catch this)
947
+ */
948
+ number(value) {
949
+ if (value === void 0 || value === null || value === "") return void 0;
950
+ if (typeof value === "number") return value;
951
+ if (typeof value !== "string") return void 0;
952
+ const num = Number(value);
953
+ if (Number.isNaN(num)) return void 0;
954
+ return num;
955
+ },
956
+ /**
957
+ * Coerce a checkbox value to a boolean.
958
+ * HTML checkboxes submit "on" when checked and are absent when unchecked.
959
+ * - `"on"` / any truthy string → `true`
960
+ * - `undefined` / `null` / `""` → `false`
961
+ */
962
+ checkbox(value) {
963
+ if (value === void 0 || value === null || value === "") return false;
964
+ if (typeof value === "boolean") return value;
965
+ return typeof value === "string" && value.length > 0;
966
+ },
967
+ /**
968
+ * Parse a JSON string into an object.
969
+ * - Valid JSON string → parsed object
970
+ * - `""` / `undefined` / `null` → `undefined`
971
+ * - Invalid JSON → `undefined` (schema validation will catch this)
972
+ */
973
+ json(value) {
974
+ if (value === void 0 || value === null || value === "") return void 0;
975
+ if (typeof value !== "string") return value;
976
+ try {
977
+ return JSON.parse(value);
978
+ } catch {
979
+ return;
980
+ }
981
+ },
982
+ /**
983
+ * Coerce a date string to a Date object.
984
+ * Handles `<input type="date">` (`"2024-01-15"`), `<input type="datetime-local">`
985
+ * (`"2024-01-15T10:30"`), and full ISO 8601 strings.
986
+ * - Valid date string → `Date`
987
+ * - `""` / `undefined` / `null` → `undefined`
988
+ * - Invalid date strings → `undefined` (schema validation will catch this)
989
+ * - Impossible dates that `new Date()` silently normalizes (e.g. Feb 31) → `undefined`
990
+ */
991
+ date(value) {
992
+ if (value === void 0 || value === null || value === "") return void 0;
993
+ if (value instanceof Date) return value;
994
+ if (typeof value !== "string") return void 0;
995
+ const date = new Date(value);
996
+ if (Number.isNaN(date.getTime())) return void 0;
997
+ const ymdMatch = value.match(/^(\d{4})-(\d{2})-(\d{2})/);
998
+ if (ymdMatch) {
999
+ const inputYear = Number(ymdMatch[1]);
1000
+ const inputMonth = Number(ymdMatch[2]);
1001
+ const inputDay = Number(ymdMatch[3]);
1002
+ const isUTC = value.length === 10 || value.endsWith("Z");
1003
+ const parsedYear = isUTC ? date.getUTCFullYear() : date.getFullYear();
1004
+ const parsedMonth = isUTC ? date.getUTCMonth() + 1 : date.getMonth() + 1;
1005
+ const parsedDay = isUTC ? date.getUTCDate() : date.getDate();
1006
+ if (inputYear !== parsedYear || inputMonth !== parsedMonth || inputDay !== parsedDay) return;
1007
+ }
1008
+ return date;
1009
+ },
1010
+ /**
1011
+ * Create a File coercion function with optional size and mime type validation.
1012
+ * Returns the File if valid, `undefined` otherwise.
1013
+ *
1014
+ * ```ts
1015
+ * // Basic — just checks it's a real File
1016
+ * z.preprocess(coerce.file(), z.instanceof(File))
1017
+ *
1018
+ * // With constraints
1019
+ * z.preprocess(
1020
+ * coerce.file({ maxSize: 5 * 1024 * 1024, accept: ['image/png', 'image/jpeg'] }),
1021
+ * z.instanceof(File)
1022
+ * )
1023
+ * ```
1024
+ */
1025
+ file(options) {
1026
+ return (value) => {
1027
+ if (value === void 0 || value === null || value === "") return void 0;
1028
+ if (!(value instanceof File)) return void 0;
1029
+ if (value.size === 0 && value.name === "") return void 0;
1030
+ if (options?.maxSize !== void 0 && value.size > options.maxSize) return;
1031
+ if (options?.accept !== void 0 && !options.accept.includes(value.type)) return;
1032
+ return value;
1033
+ };
1034
+ }
1035
+ };
1036
+ //#endregion
1037
+ //#region src/server/actions.ts
1038
+ /**
1039
+ * Server action primitives: revalidatePath, revalidateTag, and the action handler.
1040
+ *
1041
+ * - revalidatePath(path) re-renders the route at that path and returns the RSC
1042
+ * flight payload for inline reconciliation. Server actions only.
1043
+ * - revalidateTag(tag) invalidates timber.cache entries by tag. Callable from
1044
+ * any server code: deferred to the end of the action inside one, immediate
1045
+ * anywhere else.
1046
+ *
1047
+ * The action handler processes incoming action requests, validates CSRF,
1048
+ * enforces body limits, executes the action, and returns the response
1049
+ * (with piggybacked RSC payload if revalidatePath was called).
1050
+ *
1051
+ * See design/08-forms-and-actions.md
1052
+ */
1053
+ /**
1054
+ * Thrown by a `RevalidateRenderer` that decided not to render: the path
1055
+ * was rejected, its params failed coercion, or its middleware answered
1056
+ * with a `Response` that cannot become an element tree. `executeAction`
1057
+ * drops the revalidation — the action result is still delivered, with no
1058
+ * `_tree` piggybacked — and, because the drop was a decision, does not
1059
+ * return it as an error to report. Identified by `instanceof`, so the
1060
+ * message text never reaches clients.
1061
+ */
1062
+ var RevalidationDropped = class extends Error {
1063
+ constructor(message) {
1064
+ super(message);
1065
+ this.name = "RevalidationDropped";
1066
+ }
1067
+ };
1068
+ /**
1069
+ * Re-render the route at `path` and include the RSC flight payload in the
1070
+ * action response. The client reconciles inline — no separate fetch needed.
1071
+ *
1072
+ * Only callable inside a server action: it attaches a render to the action
1073
+ * response, and nothing else has one. To refresh cached data from a
1074
+ * `route.ts` handler, a webhook, or a job, use `revalidateTag()` or
1075
+ * `cache.invalidate()`.
1076
+ *
1077
+ * @param path - The path to re-render (e.g. '/dashboard', '/todos').
1078
+ */
1079
+ function revalidatePath(path) {
1080
+ const state = revalidationAls.getStore();
1081
+ if (!state || state.closed) throw new Error("revalidatePath() can only be called inside a server action: it re-renders the route into the action response, and there is no action response here. To refresh cached data from a route handler or other server code, call revalidateTag() or cache.invalidate() for the tags the route reads.");
1082
+ if (!state.paths.includes(path)) state.paths.push(path);
1083
+ }
1084
+ /**
1085
+ * Invalidate all timber.cache entries tagged with `tag`, and tombstone the
1086
+ * pre-rendered component seeds that carry it.
1087
+ * Does not return a payload — the next request for an invalidated entry re-executes.
1088
+ *
1089
+ * Callable from any server code. Inside a server action the invalidation is
1090
+ * deferred until the action body returns, so a `revalidatePath()` render in
1091
+ * the same action reads fresh data; the returned promise resolves at once.
1092
+ * Anywhere else — a `route.ts` webhook, a job, or background work an action
1093
+ * started that runs after the action body returned — it is
1094
+ * `cache.invalidate({ tag })`, and the promise settles when the invalidation
1095
+ * has. Await it before responding.
1096
+ *
1097
+ * @param tag - The cache tag to invalidate (e.g. 'products', 'user:123').
1098
+ */
1099
+ function revalidateTag(tag) {
1100
+ const state = revalidationAls.getStore();
1101
+ if (!state || state.closed) return cache.invalidate({ tag });
1102
+ if (!state.tags.includes(tag)) state.tags.push(tag);
1103
+ return Promise.resolve();
1104
+ }
1105
+ function stripFragment(path) {
1106
+ const idx = path.indexOf("#");
1107
+ return idx >= 0 ? path.slice(0, idx) : path;
1108
+ }
1109
+ /**
1110
+ * Execute a server action and process revalidation.
1111
+ *
1112
+ * 1. Sets up revalidation state
1113
+ * 2. Calls the action function
1114
+ * 3. Processes revalidateTag calls (invalidates cache entries)
1115
+ * 4. Processes revalidatePath calls (re-renders and captures RSC payload)
1116
+ * 5. Returns the action result + optional RSC payload
1117
+ *
1118
+ * @param actionFn - The server action function to execute.
1119
+ * @param args - Arguments to pass to the action.
1120
+ * @param config - Handler configuration (cache handler, renderer).
1121
+ */
1122
+ async function executeAction(actionFn, args, config = {}, spanMeta) {
1123
+ const state = {
1124
+ paths: [],
1125
+ tags: []
1126
+ };
1127
+ const revalidationErrors = [];
1128
+ let actionResult;
1129
+ let redirectTo;
1130
+ let redirectStatus;
1131
+ await revalidationAls.run(state, async () => {
1132
+ try {
1133
+ actionResult = await withSpan("timber.action", {
1134
+ ...spanMeta?.actionFile ? { "timber.action_file": spanMeta.actionFile } : {},
1135
+ ...spanMeta?.actionName ? { "timber.action_name": spanMeta.actionName } : {}
1136
+ }, () => actionFn(...args));
1137
+ } catch (error) {
1138
+ if (isRedirectSignal(error)) {
1139
+ redirectTo = error.location;
1140
+ redirectStatus = error.status;
1141
+ } else throw error;
1142
+ } finally {
1143
+ state.closed = true;
1144
+ }
1145
+ });
1146
+ if (state.tags.length > 0) {
1147
+ const results = await Promise.allSettled(state.tags.map((tag) => cache.invalidate({ tag })));
1148
+ for (const r of results) if (r.status === "rejected") {
1149
+ console.error("[timber] revalidateTag invalidation failed:", r.reason);
1150
+ revalidationErrors.push(r.reason);
1151
+ }
1152
+ }
1153
+ let revalidation;
1154
+ let invalidatedPaths;
1155
+ if (state.paths.length > 0) {
1156
+ const path = state.paths[0];
1157
+ if (config.requestPathname && !revalidationPathMatches(path, config.requestPathname, config.requestSearch)) invalidatedPaths = [stripFragment(path)];
1158
+ else if (config.renderer) try {
1159
+ revalidation = await config.renderer(path);
1160
+ } catch (renderError) {
1161
+ if (isRedirectSignal(renderError)) {
1162
+ redirectTo = renderError.location;
1163
+ redirectStatus = renderError.status;
1164
+ } else if (isDenySignal(renderError)) console.error(`[timber] revalidatePath dropped — target middleware denied (status ${renderError.status})`);
1165
+ else if (renderError instanceof RevalidationDropped) console.error(`[timber] ${renderError.message}`);
1166
+ else {
1167
+ console.error("[timber] revalidatePath render failed:", renderError);
1168
+ revalidationErrors.push(renderError);
1169
+ }
1170
+ }
1171
+ }
1172
+ return {
1173
+ actionResult,
1174
+ revalidation,
1175
+ invalidatedPaths,
1176
+ revalidationErrors,
1177
+ ...redirectTo ? {
1178
+ redirectTo,
1179
+ redirectStatus
1180
+ } : {}
1181
+ };
1182
+ }
1183
+ /**
1184
+ * Check if a `revalidatePath(path)` argument matches the request pathname.
1185
+ /**
1186
+ * Canonicalizes the revalidated path the same way the renderer does so
1187
+ * equivalent paths compare equal. Returns true when the paths match or
1188
+ * when comparison is inconclusive (validation failure → render to be safe).
1189
+ */
1190
+ function revalidationPathMatches(revalidatedPath, requestPathname, requestSearch) {
1191
+ const hashIdx = revalidatedPath.indexOf("#");
1192
+ const noFragment = hashIdx >= 0 ? revalidatedPath.slice(0, hashIdx) : revalidatedPath;
1193
+ const queryIdx = noFragment.indexOf("?");
1194
+ const pathnameOnly = queryIdx >= 0 ? noFragment.slice(0, queryIdx) : noFragment;
1195
+ const search = queryIdx >= 0 ? noFragment.slice(queryIdx) : "";
1196
+ const canonical = canonicalize(pathnameOnly, true);
1197
+ if (!canonical.ok) return true;
1198
+ if (canonical.pathname !== requestPathname) return false;
1199
+ if (search && search !== (requestSearch ?? "")) return false;
1200
+ return true;
1201
+ }
1202
+ //#endregion
1203
+ export { setMutableCookieContext as C, setMatchedSegmentPath as S, runOutsideReactCacheScope as T, getHeaders as _, parseFormData as a, markResponseFlushed as b, deny as c, waitUntil as d, getCookie as f, getHeader as g, applyRequestHeaderOverlay as h, coerce as i, redirect as l, getSetCookieHeaders as m, revalidatePath as n, DenySignal as o, getCookieJar as p, revalidateTag as r, RedirectSignal as s, executeAction as t, redirectExternal as u, getSearchParams as v, setSegmentParams as w, runWithRequestContext as x, getSegmentParams as y };
974
1204
 
975
- //# sourceMappingURL=primitives-DLAnvsrk.js.map
1205
+ //# sourceMappingURL=actions-BerlqoXA.js.map