@timber-js/app 0.2.0-alpha.98 → 0.2.0-alpha.99

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 (322) hide show
  1. package/LICENSE +8 -0
  2. package/dist/_chunks/actions-CQ8Z8VGL.js +1061 -0
  3. package/dist/_chunks/actions-CQ8Z8VGL.js.map +1 -0
  4. package/dist/_chunks/build-output-helper-DXnW0qjz.js +61 -0
  5. package/dist/_chunks/build-output-helper-DXnW0qjz.js.map +1 -0
  6. package/dist/_chunks/{define-Itxvcd7F.js → define-B-Q_UMOD.js} +19 -23
  7. package/dist/_chunks/define-B-Q_UMOD.js.map +1 -0
  8. package/dist/_chunks/{define-C77ScO0m.js → define-CfBPoJb0.js} +24 -7
  9. package/dist/_chunks/define-CfBPoJb0.js.map +1 -0
  10. package/dist/_chunks/define-cookie-BjpIt4UC.js +194 -0
  11. package/dist/_chunks/define-cookie-BjpIt4UC.js.map +1 -0
  12. package/dist/_chunks/{format-CYBGxKtc.js → format-Bcn-Iv1x.js} +1 -1
  13. package/dist/_chunks/{format-CYBGxKtc.js.map → format-Bcn-Iv1x.js.map} +1 -1
  14. package/dist/_chunks/handler-store-B-lqaGyh.js +54 -0
  15. package/dist/_chunks/handler-store-B-lqaGyh.js.map +1 -0
  16. package/dist/_chunks/logger-0m8MsKdc.js +291 -0
  17. package/dist/_chunks/logger-0m8MsKdc.js.map +1 -0
  18. package/dist/_chunks/merge-search-params-BphMdht_.js +122 -0
  19. package/dist/_chunks/merge-search-params-BphMdht_.js.map +1 -0
  20. package/dist/_chunks/navigation-root-BCYczjml.js +96 -0
  21. package/dist/_chunks/navigation-root-BCYczjml.js.map +1 -0
  22. package/dist/_chunks/registry-I2ss-lvy.js +20 -0
  23. package/dist/_chunks/registry-I2ss-lvy.js.map +1 -0
  24. package/dist/_chunks/router-ref-h3-UaCQv.js +28 -0
  25. package/dist/_chunks/router-ref-h3-UaCQv.js.map +1 -0
  26. package/dist/_chunks/{schema-bridge-C3xl_vfb.js → schema-bridge-Cxu4l-7p.js} +1 -1
  27. package/dist/_chunks/{schema-bridge-C3xl_vfb.js.map → schema-bridge-Cxu4l-7p.js.map} +1 -1
  28. package/dist/_chunks/{segment-context-fHFLF1PE.js → segment-context-Dx_OizxD.js} +1 -1
  29. package/dist/_chunks/{segment-context-fHFLF1PE.js.map → segment-context-Dx_OizxD.js.map} +1 -1
  30. package/dist/_chunks/{router-ref-C8OCm7g7.js → ssr-data-B4CdH7rE.js} +2 -26
  31. package/dist/_chunks/ssr-data-B4CdH7rE.js.map +1 -0
  32. package/dist/_chunks/{stale-reload-BX5gL1r-.js → stale-reload-Bab885FO.js} +1 -1
  33. package/dist/_chunks/{stale-reload-BX5gL1r-.js.map → stale-reload-Bab885FO.js.map} +1 -1
  34. package/dist/_chunks/tracing-C8V-YGsP.js +329 -0
  35. package/dist/_chunks/tracing-C8V-YGsP.js.map +1 -0
  36. package/dist/_chunks/{use-query-states-BiV5GJgm.js → use-query-states-B2XTqxDR.js} +3 -19
  37. package/dist/_chunks/use-query-states-B2XTqxDR.js.map +1 -0
  38. package/dist/_chunks/{use-params-IOPu7E8t.js → use-segment-params-BkpKAQ7D.js} +9 -95
  39. package/dist/_chunks/use-segment-params-BkpKAQ7D.js.map +1 -0
  40. package/dist/_chunks/{walkers-VOXgavMF.js → walkers-Tg0Alwcg.js} +6 -3
  41. package/dist/_chunks/walkers-Tg0Alwcg.js.map +1 -0
  42. package/dist/_chunks/{dev-warnings-DpGRGoDi.js → warnings-Cg47l5sk.js} +3 -3
  43. package/dist/_chunks/warnings-Cg47l5sk.js.map +1 -0
  44. package/dist/adapters/build-output-helper.d.ts +28 -0
  45. package/dist/adapters/build-output-helper.d.ts.map +1 -0
  46. package/dist/adapters/cloudflare.d.ts.map +1 -1
  47. package/dist/adapters/cloudflare.js +8 -28
  48. package/dist/adapters/cloudflare.js.map +1 -1
  49. package/dist/adapters/nitro.d.ts.map +1 -1
  50. package/dist/adapters/nitro.js +8 -26
  51. package/dist/adapters/nitro.js.map +1 -1
  52. package/dist/adapters/shared.d.ts +16 -0
  53. package/dist/adapters/shared.d.ts.map +1 -0
  54. package/dist/cache/index.js +9 -2
  55. package/dist/cache/index.js.map +1 -1
  56. package/dist/cache/timber-cache.d.ts.map +1 -1
  57. package/dist/client/error-boundary.js +2 -1
  58. package/dist/client/error-boundary.js.map +1 -1
  59. package/dist/client/form.d.ts +10 -24
  60. package/dist/client/form.d.ts.map +1 -1
  61. package/dist/client/index.d.ts +1 -5
  62. package/dist/client/index.d.ts.map +1 -1
  63. package/dist/client/index.js +40 -90
  64. package/dist/client/index.js.map +1 -1
  65. package/dist/client/internal.d.ts +2 -1
  66. package/dist/client/internal.d.ts.map +1 -1
  67. package/dist/client/internal.js +81 -7
  68. package/dist/client/internal.js.map +1 -1
  69. package/dist/client/rsc-fetch.d.ts.map +1 -1
  70. package/dist/client/state.d.ts +1 -1
  71. package/dist/client/use-cookie.d.ts +8 -0
  72. package/dist/client/use-cookie.d.ts.map +1 -1
  73. package/dist/client/{use-params.d.ts → use-segment-params.d.ts} +1 -1
  74. package/dist/client/use-segment-params.d.ts.map +1 -0
  75. package/dist/codec.d.ts +1 -1
  76. package/dist/codec.d.ts.map +1 -1
  77. package/dist/codec.js +2 -2
  78. package/dist/config-types.d.ts +28 -0
  79. package/dist/config-types.d.ts.map +1 -1
  80. package/dist/cookies/define-cookie.d.ts +87 -35
  81. package/dist/cookies/define-cookie.d.ts.map +1 -1
  82. package/dist/cookies/index.d.ts +2 -1
  83. package/dist/cookies/index.d.ts.map +1 -1
  84. package/dist/cookies/index.js +48 -2
  85. package/dist/cookies/index.js.map +1 -0
  86. package/dist/cookies/json-cookie.d.ts +64 -0
  87. package/dist/cookies/json-cookie.d.ts.map +1 -0
  88. package/dist/cookies/validation.d.ts +46 -0
  89. package/dist/cookies/validation.d.ts.map +1 -0
  90. package/dist/{plugins/dev-404-page.d.ts → dev-tools/404-page.d.ts} +1 -1
  91. package/dist/dev-tools/404-page.d.ts.map +1 -0
  92. package/dist/{plugins/dev-browser-logs.d.ts → dev-tools/browser-logs.d.ts} +1 -1
  93. package/dist/dev-tools/browser-logs.d.ts.map +1 -0
  94. package/dist/{plugins/dev-error-page.d.ts → dev-tools/error-page.d.ts} +2 -2
  95. package/dist/dev-tools/error-page.d.ts.map +1 -0
  96. package/dist/{server/dev-holding-server.d.ts → dev-tools/holding-server.d.ts} +1 -1
  97. package/dist/dev-tools/holding-server.d.ts.map +1 -0
  98. package/dist/dev-tools/index.d.ts +31 -0
  99. package/dist/dev-tools/index.d.ts.map +1 -0
  100. package/dist/{server/dev-span-processor.d.ts → dev-tools/instrumentation.d.ts} +26 -6
  101. package/dist/dev-tools/instrumentation.d.ts.map +1 -0
  102. package/dist/{server/dev-logger.d.ts → dev-tools/logger.d.ts} +1 -1
  103. package/dist/dev-tools/logger.d.ts.map +1 -0
  104. package/dist/{plugins/dev-logs.d.ts → dev-tools/logs.d.ts} +1 -1
  105. package/dist/dev-tools/logs.d.ts.map +1 -0
  106. package/dist/{plugins/dev-error-overlay.d.ts → dev-tools/overlay.d.ts} +3 -12
  107. package/dist/dev-tools/overlay.d.ts.map +1 -0
  108. package/dist/dev-tools/stack-classifier.d.ts +34 -0
  109. package/dist/dev-tools/stack-classifier.d.ts.map +1 -0
  110. package/dist/{plugins/dev-terminal-error.d.ts → dev-tools/terminal.d.ts} +2 -2
  111. package/dist/dev-tools/terminal.d.ts.map +1 -0
  112. package/dist/{server/dev-warnings.d.ts → dev-tools/warnings.d.ts} +1 -1
  113. package/dist/dev-tools/warnings.d.ts.map +1 -0
  114. package/dist/index.d.ts +1 -0
  115. package/dist/index.d.ts.map +1 -1
  116. package/dist/index.js +97 -72
  117. package/dist/index.js.map +1 -1
  118. package/dist/plugin-context.d.ts +1 -1
  119. package/dist/plugin-context.d.ts.map +1 -1
  120. package/dist/plugins/adapter-build.d.ts.map +1 -1
  121. package/dist/routing/convention-lint.d.ts.map +1 -1
  122. package/dist/routing/index.js +1 -1
  123. package/dist/routing/scanner.d.ts.map +1 -1
  124. package/dist/routing/status-file-lint.d.ts.map +1 -1
  125. package/dist/search-params/define.d.ts +25 -7
  126. package/dist/search-params/define.d.ts.map +1 -1
  127. package/dist/search-params/index.js +5 -3
  128. package/dist/search-params/index.js.map +1 -1
  129. package/dist/search-params/wrappers.d.ts +2 -2
  130. package/dist/search-params/wrappers.d.ts.map +1 -1
  131. package/dist/segment-params/define.d.ts +23 -6
  132. package/dist/segment-params/define.d.ts.map +1 -1
  133. package/dist/segment-params/index.js +1 -1
  134. package/dist/server/access-gate.d.ts +4 -3
  135. package/dist/server/access-gate.d.ts.map +1 -1
  136. package/dist/server/action-handler.d.ts +15 -6
  137. package/dist/server/action-handler.d.ts.map +1 -1
  138. package/dist/server/als-registry.d.ts +5 -5
  139. package/dist/server/als-registry.d.ts.map +1 -1
  140. package/dist/server/asset-headers.d.ts +1 -15
  141. package/dist/server/asset-headers.d.ts.map +1 -1
  142. package/dist/server/cookie-context.d.ts +170 -0
  143. package/dist/server/cookie-context.d.ts.map +1 -0
  144. package/dist/server/cookie-parsing.d.ts +51 -0
  145. package/dist/server/cookie-parsing.d.ts.map +1 -0
  146. package/dist/server/deny-boundary.d.ts +90 -0
  147. package/dist/server/deny-boundary.d.ts.map +1 -0
  148. package/dist/server/deny-renderer.d.ts.map +1 -1
  149. package/dist/server/early-hints-sender.d.ts.map +1 -1
  150. package/dist/server/index.d.ts +5 -4
  151. package/dist/server/index.d.ts.map +1 -1
  152. package/dist/server/index.js +4 -149
  153. package/dist/server/index.js.map +1 -1
  154. package/dist/server/internal.d.ts +6 -4
  155. package/dist/server/internal.d.ts.map +1 -1
  156. package/dist/server/internal.js +261 -408
  157. package/dist/server/internal.js.map +1 -1
  158. package/dist/server/logger.d.ts +14 -0
  159. package/dist/server/logger.d.ts.map +1 -1
  160. package/dist/server/middleware-runner.d.ts +17 -0
  161. package/dist/server/middleware-runner.d.ts.map +1 -1
  162. package/dist/server/param-coercion.d.ts +26 -0
  163. package/dist/server/param-coercion.d.ts.map +1 -0
  164. package/dist/server/pipeline-helpers.d.ts +14 -7
  165. package/dist/server/pipeline-helpers.d.ts.map +1 -1
  166. package/dist/server/pipeline-outcome.d.ts +49 -0
  167. package/dist/server/pipeline-outcome.d.ts.map +1 -0
  168. package/dist/server/pipeline-phases.d.ts +4 -49
  169. package/dist/server/pipeline-phases.d.ts.map +1 -1
  170. package/dist/server/pipeline.d.ts +0 -2
  171. package/dist/server/pipeline.d.ts.map +1 -1
  172. package/dist/server/request-context.d.ts +22 -159
  173. package/dist/server/request-context.d.ts.map +1 -1
  174. package/dist/server/route-element-builder.d.ts.map +1 -1
  175. package/dist/server/rsc-entry/action-middleware-runner.d.ts +66 -0
  176. package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -0
  177. package/dist/server/rsc-entry/helpers.d.ts +1 -1
  178. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  179. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  180. package/dist/server/rsc-entry/render-route.d.ts +50 -0
  181. package/dist/server/rsc-entry/render-route.d.ts.map +1 -0
  182. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +59 -14
  183. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -1
  184. package/dist/server/state-tree-diff.d.ts.map +1 -1
  185. package/dist/server/tracing.d.ts +1 -1
  186. package/dist/server/tracing.d.ts.map +1 -1
  187. package/dist/server/tree-builder.d.ts +45 -16
  188. package/dist/server/tree-builder.d.ts.map +1 -1
  189. package/dist/server/types.d.ts +48 -0
  190. package/dist/server/types.d.ts.map +1 -1
  191. package/dist/server/utils/escape-html.d.ts +14 -0
  192. package/dist/server/utils/escape-html.d.ts.map +1 -0
  193. package/dist/shims/headers.d.ts +2 -2
  194. package/dist/shims/headers.d.ts.map +1 -1
  195. package/dist/shims/navigation-client.d.ts +3 -1
  196. package/dist/shims/navigation-client.d.ts.map +1 -1
  197. package/dist/shims/navigation.d.ts +9 -4
  198. package/dist/shims/navigation.d.ts.map +1 -1
  199. package/package.json +6 -7
  200. package/src/adapters/build-output-helper.ts +77 -0
  201. package/src/adapters/cloudflare.ts +10 -50
  202. package/src/adapters/nitro.ts +11 -45
  203. package/src/adapters/shared.ts +40 -0
  204. package/src/cache/timber-cache.ts +3 -2
  205. package/src/cli.ts +0 -0
  206. package/src/client/form.tsx +17 -25
  207. package/src/client/index.ts +16 -9
  208. package/src/client/internal.ts +3 -2
  209. package/src/client/router.ts +1 -1
  210. package/src/client/rsc-fetch.ts +15 -0
  211. package/src/client/state.ts +2 -2
  212. package/src/client/use-cookie.ts +29 -0
  213. package/src/codec.ts +3 -7
  214. package/src/config-types.ts +28 -0
  215. package/src/cookies/define-cookie.ts +271 -78
  216. package/src/cookies/index.ts +11 -8
  217. package/src/cookies/json-cookie.ts +105 -0
  218. package/src/cookies/validation.ts +134 -0
  219. package/src/{plugins/dev-404-page.ts → dev-tools/404-page.ts} +2 -7
  220. package/src/{plugins/dev-error-page.ts → dev-tools/error-page.ts} +5 -32
  221. package/src/dev-tools/index.ts +90 -0
  222. package/src/dev-tools/instrumentation.ts +176 -0
  223. package/src/{plugins/dev-logs.ts → dev-tools/logs.ts} +2 -2
  224. package/src/{plugins/dev-error-overlay.ts → dev-tools/overlay.ts} +5 -23
  225. package/src/dev-tools/stack-classifier.ts +75 -0
  226. package/src/{plugins/dev-terminal-error.ts → dev-tools/terminal.ts} +4 -38
  227. package/src/{server/dev-warnings.ts → dev-tools/warnings.ts} +1 -1
  228. package/src/index.ts +11 -3
  229. package/src/plugin-context.ts +1 -1
  230. package/src/plugins/adapter-build.ts +3 -1
  231. package/src/plugins/dev-server.ts +3 -3
  232. package/src/plugins/shims.ts +1 -1
  233. package/src/plugins/static-build.ts +1 -1
  234. package/src/routing/convention-lint.ts +5 -4
  235. package/src/routing/scanner.ts +5 -2
  236. package/src/routing/status-file-lint.ts +4 -2
  237. package/src/search-params/define.ts +71 -15
  238. package/src/search-params/wrappers.ts +9 -2
  239. package/src/segment-params/define.ts +66 -13
  240. package/src/server/access-gate.tsx +9 -8
  241. package/src/server/action-handler.ts +28 -38
  242. package/src/server/als-registry.ts +5 -5
  243. package/src/server/asset-headers.ts +8 -34
  244. package/src/server/cookie-context.ts +468 -0
  245. package/src/server/cookie-parsing.ts +135 -0
  246. package/src/server/{deny-page-resolver.ts → deny-boundary.ts} +78 -14
  247. package/src/server/deny-renderer.ts +2 -7
  248. package/src/server/early-hints-sender.ts +3 -2
  249. package/src/server/fallback-error.ts +1 -1
  250. package/src/server/index.ts +13 -14
  251. package/src/server/internal.ts +10 -3
  252. package/src/server/logger.ts +23 -0
  253. package/src/server/middleware-runner.ts +44 -0
  254. package/src/server/param-coercion.ts +76 -0
  255. package/src/server/pipeline-helpers.ts +37 -13
  256. package/src/server/pipeline-outcome.ts +167 -0
  257. package/src/server/pipeline-phases.ts +27 -209
  258. package/src/server/pipeline.ts +2 -9
  259. package/src/server/request-context.ts +46 -451
  260. package/src/server/route-element-builder.ts +7 -3
  261. package/src/server/rsc-entry/action-middleware-runner.ts +167 -0
  262. package/src/server/rsc-entry/error-renderer.ts +1 -1
  263. package/src/server/rsc-entry/helpers.ts +2 -7
  264. package/src/server/rsc-entry/index.ts +34 -273
  265. package/src/server/rsc-entry/render-route.ts +304 -0
  266. package/src/server/rsc-entry/rsc-payload.ts +1 -1
  267. package/src/server/rsc-entry/ssr-renderer.ts +2 -2
  268. package/src/server/rsc-entry/wrap-action-dispatch.ts +316 -23
  269. package/src/server/ssr-entry.ts +1 -1
  270. package/src/server/state-tree-diff.ts +4 -1
  271. package/src/server/tracing.ts +3 -3
  272. package/src/server/tree-builder.ts +128 -52
  273. package/src/server/types.ts +52 -0
  274. package/src/server/utils/escape-html.ts +20 -0
  275. package/src/shims/headers.ts +3 -3
  276. package/src/shims/navigation-client.ts +4 -3
  277. package/src/shims/navigation.ts +9 -7
  278. package/dist/_chunks/actions-DLnUaR65.js +0 -421
  279. package/dist/_chunks/actions-DLnUaR65.js.map +0 -1
  280. package/dist/_chunks/als-registry-HS0LGUl2.js +0 -41
  281. package/dist/_chunks/als-registry-HS0LGUl2.js.map +0 -1
  282. package/dist/_chunks/debug-ECi_61pb.js +0 -108
  283. package/dist/_chunks/debug-ECi_61pb.js.map +0 -1
  284. package/dist/_chunks/define-C77ScO0m.js.map +0 -1
  285. package/dist/_chunks/define-Itxvcd7F.js.map +0 -1
  286. package/dist/_chunks/define-cookie-BowvzoP0.js +0 -94
  287. package/dist/_chunks/define-cookie-BowvzoP0.js.map +0 -1
  288. package/dist/_chunks/dev-warnings-DpGRGoDi.js.map +0 -1
  289. package/dist/_chunks/merge-search-params-Cm_KIWDX.js +0 -41
  290. package/dist/_chunks/merge-search-params-Cm_KIWDX.js.map +0 -1
  291. package/dist/_chunks/request-context-CK5tZqIP.js +0 -478
  292. package/dist/_chunks/request-context-CK5tZqIP.js.map +0 -1
  293. package/dist/_chunks/router-ref-C8OCm7g7.js.map +0 -1
  294. package/dist/_chunks/tracing-CCYbKn5n.js +0 -238
  295. package/dist/_chunks/tracing-CCYbKn5n.js.map +0 -1
  296. package/dist/_chunks/use-params-IOPu7E8t.js.map +0 -1
  297. package/dist/_chunks/use-query-states-BiV5GJgm.js.map +0 -1
  298. package/dist/_chunks/walkers-VOXgavMF.js.map +0 -1
  299. package/dist/client/use-params.d.ts.map +0 -1
  300. package/dist/plugins/dev-404-page.d.ts.map +0 -1
  301. package/dist/plugins/dev-browser-logs.d.ts.map +0 -1
  302. package/dist/plugins/dev-error-overlay.d.ts.map +0 -1
  303. package/dist/plugins/dev-error-page.d.ts.map +0 -1
  304. package/dist/plugins/dev-logs.d.ts.map +0 -1
  305. package/dist/plugins/dev-terminal-error.d.ts.map +0 -1
  306. package/dist/server/deny-page-resolver.d.ts +0 -52
  307. package/dist/server/deny-page-resolver.d.ts.map +0 -1
  308. package/dist/server/dev-fetch-instrumentation.d.ts +0 -22
  309. package/dist/server/dev-fetch-instrumentation.d.ts.map +0 -1
  310. package/dist/server/dev-holding-server.d.ts.map +0 -1
  311. package/dist/server/dev-logger.d.ts.map +0 -1
  312. package/dist/server/dev-span-processor.d.ts.map +0 -1
  313. package/dist/server/dev-warnings.d.ts.map +0 -1
  314. package/dist/server/page-deny-boundary.d.ts +0 -31
  315. package/dist/server/page-deny-boundary.d.ts.map +0 -1
  316. package/src/server/dev-fetch-instrumentation.ts +0 -96
  317. package/src/server/dev-span-processor.ts +0 -78
  318. package/src/server/page-deny-boundary.tsx +0 -56
  319. /package/src/client/{use-params.ts → use-segment-params.ts} +0 -0
  320. /package/src/{plugins/dev-browser-logs.ts → dev-tools/browser-logs.ts} +0 -0
  321. /package/src/{server/dev-holding-server.ts → dev-tools/holding-server.ts} +0 -0
  322. /package/src/{server/dev-logger.ts → dev-tools/logger.ts} +0 -0
@@ -1,421 +0,0 @@
1
- import { t as isDebug } from "./debug-ECi_61pb.js";
2
- import { i as revalidationAls, s as waitUntilAls } from "./als-registry-HS0LGUl2.js";
3
- import { o as getRequestSearchString } from "./request-context-CK5tZqIP.js";
4
- import { t as mergePreservedSearchParams } from "./merge-search-params-Cm_KIWDX.js";
5
- import { d as withSpan, f as getCacheHandler } from "./tracing-CCYbKn5n.js";
6
- //#region src/server/waituntil-bridge.ts
7
- /**
8
- * Per-request waitUntil bridge — ALS bridge for platform adapters.
9
- *
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()"
17
- */
18
- /**
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).
24
- */
25
- function getWaitUntil() {
26
- return waitUntilAls.getStore();
27
- }
28
- //#endregion
29
- //#region src/server/primitives.ts
30
- /**
31
- * Check if a value is JSON-serializable without data loss.
32
- * Returns a description of the first non-serializable value found, or null if OK.
33
- *
34
- * @internal Exported for testing only.
35
- */
36
- function findNonSerializable(value, path = "data") {
37
- if (value === null || value === void 0) return null;
38
- switch (typeof value) {
39
- case "string":
40
- case "number":
41
- case "boolean": return null;
42
- case "bigint": return `${path} contains a BigInt — BigInt throws in JSON.stringify`;
43
- case "function": return `${path} is a function — functions are not JSON-serializable`;
44
- case "symbol": return `${path} is a symbol — symbols are not JSON-serializable`;
45
- case "object": break;
46
- default: return `${path} has unsupported type "${typeof value}"`;
47
- }
48
- if (value instanceof Date) return `${path} is a Date — Dates silently coerce to strings in JSON.stringify`;
49
- if (value instanceof Map) return `${path} is a Map — Maps serialize as {} in JSON.stringify (data loss)`;
50
- if (value instanceof Set) return `${path} is a Set — Sets serialize as {} in JSON.stringify (data loss)`;
51
- if (value instanceof RegExp) return `${path} is a RegExp — RegExps serialize as {} in JSON.stringify`;
52
- if (value instanceof Error) return `${path} is an Error — Errors serialize as {} in JSON.stringify`;
53
- if (Array.isArray(value)) {
54
- for (let i = 0; i < value.length; i++) {
55
- const result = findNonSerializable(value[i], `${path}[${i}]`);
56
- if (result) return result;
57
- }
58
- return null;
59
- }
60
- const proto = Object.getPrototypeOf(value);
61
- if (proto === null) return `${path} is a null-prototype object — React Flight rejects null prototypes`;
62
- if (proto !== Object.prototype) return `${path} is a ${value.constructor?.name ?? "unknown"} instance — class instances may lose data in JSON.stringify`;
63
- for (const key of Object.keys(value)) {
64
- const result = findNonSerializable(value[key], `${path}.${key}`);
65
- if (result) return result;
66
- }
67
- return null;
68
- }
69
- /**
70
- * Emit a dev-mode warning if data is not JSON-serializable.
71
- * No-op in production.
72
- */
73
- function warnIfNotSerializable(data, callerName) {
74
- if (!isDebug()) return;
75
- if (data === void 0) return;
76
- const issue = findNonSerializable(data);
77
- 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.`);
78
- }
79
- /**
80
- * Render-phase signal thrown by `deny()`. Caught by the framework to produce
81
- * the correct HTTP status code (segment context) or graceful degradation (slot context).
82
- */
83
- var DenySignal = class extends Error {
84
- status;
85
- data;
86
- constructor(status, data) {
87
- super(`Access denied with status ${status}`);
88
- this.name = "DenySignal";
89
- this.status = status;
90
- this.data = data;
91
- }
92
- /**
93
- * Extract the file that called deny() from the stack trace.
94
- * Returns a short path (e.g. "app/auth/access.ts") or undefined if
95
- * the stack can't be parsed. Dev-only — used for dev log output.
96
- */
97
- get sourceFile() {
98
- if (!this.stack) return void 0;
99
- const frames = this.stack.split("\n");
100
- for (let i = 2; i < frames.length; i++) {
101
- const frame = frames[i];
102
- if (!frame) continue;
103
- if (frame.includes("primitives.ts") || frame.includes("node_modules")) continue;
104
- const match = frame.match(/\(([^)]+?)(?::\d+:\d+)\)/) ?? frame.match(/at\s+([^\s]+?)(?::\d+:\d+)/);
105
- if (match?.[1]) {
106
- const full = match[1];
107
- const appIdx = full.indexOf("/app/");
108
- return appIdx >= 0 ? full.slice(appIdx + 1) : full;
109
- }
110
- }
111
- }
112
- };
113
- /**
114
- * Universal denial/error primitive. Throws a `DenySignal` that the framework catches.
115
- *
116
- * - In segment context (outside Suspense): produces HTTP status code
117
- * - In slot context: graceful degradation → denied.tsx → default.tsx → null
118
- * - Inside Suspense (hold window): promoted to pre-flush behavior
119
- * - Inside Suspense (after flush): error boundary + noindex meta
120
- *
121
- * Supports both positional and object signatures:
122
- * ```ts
123
- * deny() // 403 (default)
124
- * deny(404) // 404
125
- * deny(503, { retry: true }) // 503 with data
126
- * deny({ status: 503, message: 'Maintenance' }) // object form
127
- * ```
128
- *
129
- * Accepts any 4xx or 5xx status code. This replaces the need for
130
- * `throw new RenderError(...)` in user code — RenderError is now an
131
- * internal pipeline detail.
132
- *
133
- * @param statusOrOptions - Status code (number) or options object. Default: 403.
134
- * @param data - Optional JSON-serializable data (positional form only).
135
- */
136
- function deny(statusOrOptions, data) {
137
- let status;
138
- let resolvedData;
139
- if (typeof statusOrOptions === "object" && statusOrOptions !== null) {
140
- status = statusOrOptions.status ?? 403;
141
- resolvedData = statusOrOptions.data;
142
- } else {
143
- status = statusOrOptions ?? 403;
144
- resolvedData = data;
145
- }
146
- if (status < 400 || status > 599) throw new Error(`deny() requires a 4xx or 5xx status code, got ${status}.`);
147
- warnIfNotSerializable(resolvedData, "deny()");
148
- throw new DenySignal(status, resolvedData);
149
- }
150
- /**
151
- * Next.js redirect type discriminator.
152
- *
153
- * Provided for API compatibility with libraries that import `RedirectType`
154
- * from `next/navigation`. In timber, `redirect()` always uses `replace`
155
- * semantics (no history entry for the redirect itself).
156
- */
157
- var RedirectType = {
158
- push: "push",
159
- replace: "replace"
160
- };
161
- /**
162
- * Render-phase signal thrown by `redirect()` and `redirectExternal()`.
163
- * Caught by the framework to produce a 3xx response or client-side navigation.
164
- */
165
- var RedirectSignal = class extends Error {
166
- location;
167
- status;
168
- constructor(location, status) {
169
- super(`Redirect to ${location}`);
170
- this.name = "RedirectSignal";
171
- this.location = location;
172
- this.status = status;
173
- }
174
- };
175
- /** Pattern matching absolute URLs: http(s):// or protocol-relative // */
176
- var ABSOLUTE_URL_RE = /^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/)/;
177
- /**
178
- * Redirect to a relative path. Rejects absolute and protocol-relative URLs.
179
- * Use `redirectExternal()` for external redirects with an allow-list.
180
- *
181
- * @param path - Relative path (e.g. '/login', 'settings', '/login?returnTo=/dash')
182
- * @param statusOrOptions - HTTP status code (3xx, default 302) or options object.
183
- *
184
- * @example
185
- * // Simple redirect
186
- * redirect('/login');
187
- *
188
- * // With status code
189
- * redirect('/login', 301);
190
- *
191
- * // With preserved search params
192
- * redirect(`/docs/${version}/${slug}`, { preserveSearchParams: ['foo'] });
193
- */
194
- function redirect(path, statusOrOptions) {
195
- let status;
196
- let preserveSearchParams;
197
- if (typeof statusOrOptions === "number") status = statusOrOptions;
198
- else if (statusOrOptions) {
199
- status = statusOrOptions.status ?? (statusOrOptions.permanent ? 308 : 302);
200
- preserveSearchParams = statusOrOptions.preserveSearchParams;
201
- } else status = 302;
202
- if (status < 300 || status > 399) throw new Error(`redirect() requires a 3xx status code, got ${status}.`);
203
- if (ABSOLUTE_URL_RE.test(path)) throw new Error(`redirect() only accepts relative URLs. Got absolute URL: "${path}". Use redirectExternal(url, allowList) for external redirects.`);
204
- let resolvedPath = path;
205
- if (preserveSearchParams) resolvedPath = mergePreservedSearchParams(path, getRequestSearchString(), preserveSearchParams);
206
- throw new RedirectSignal(resolvedPath, status);
207
- }
208
- /**
209
- * Redirect to an external URL. The hostname must be in the provided allow-list.
210
- *
211
- * @param url - Absolute URL to redirect to.
212
- * @param allowList - Array of allowed hostnames (e.g. ['example.com', 'auth.example.com']).
213
- * @param status - HTTP redirect status code (3xx). Defaults to 302.
214
- */
215
- function redirectExternal(url, allowList, status = 302) {
216
- if (status < 300 || status > 399) throw new Error(`redirectExternal() requires a 3xx status code, got ${status}.`);
217
- let hostname;
218
- try {
219
- hostname = new URL(url).hostname;
220
- } catch {
221
- throw new Error(`redirectExternal() received an invalid URL: "${url}"`);
222
- }
223
- if (!allowList.includes(hostname)) throw new Error(`redirectExternal() target "${hostname}" is not in the allow-list. Allowed: [${allowList.join(", ")}]`);
224
- throw new RedirectSignal(url, status);
225
- }
226
- /**
227
- * Typed throw for render-phase errors that carry structured context to error boundaries.
228
- *
229
- * The `digest` (code + data) is serialized into the RSC stream separately from the
230
- * Error instance — only the digest crosses the RSC → client boundary.
231
- *
232
- * @example
233
- * ```ts
234
- * throw new RenderError('PRODUCT_NOT_FOUND', {
235
- * title: 'Product not found',
236
- * resourceId: params.id,
237
- * })
238
- * ```
239
- */
240
- var RenderError = class extends Error {
241
- code;
242
- digest;
243
- status;
244
- constructor(code, data, options) {
245
- super(`RenderError: ${code}`);
246
- this.name = "RenderError";
247
- this.code = code;
248
- this.digest = {
249
- code,
250
- data
251
- };
252
- warnIfNotSerializable(data, "RenderError");
253
- const status = options?.status ?? 500;
254
- if (status < 400 || status > 599) throw new Error(`RenderError status must be 4xx or 5xx, got ${status}.`);
255
- this.status = status;
256
- }
257
- };
258
- var _waitUntilWarned = false;
259
- /**
260
- * Register a promise to be kept alive after the response is sent.
261
- * Maps to `ctx.waitUntil()` on Cloudflare Workers and similar platforms.
262
- *
263
- * In production, the platform adapter installs a per-request waitUntil
264
- * function via ALS (see waituntil-bridge.ts). This function checks the
265
- * ALS bridge first, then falls back to the legacy adapter argument.
266
- *
267
- * If neither is available, a warning is logged once and the promise is
268
- * left to resolve (or reject) without being tracked.
269
- *
270
- * @param promise - The background work to keep alive.
271
- * @param adapter - Optional legacy adapter (prefer ALS bridge in production).
272
- */
273
- function waitUntil(promise, adapter) {
274
- const alsFn = getWaitUntil();
275
- if (alsFn) {
276
- alsFn(promise);
277
- return;
278
- }
279
- if (adapter && typeof adapter.waitUntil === "function") {
280
- adapter.waitUntil(promise);
281
- return;
282
- }
283
- if (!_waitUntilWarned) {
284
- _waitUntilWarned = true;
285
- console.warn("[timber] waitUntil() is not supported by the current adapter. Background work will not be tracked. This warning is shown once.");
286
- }
287
- }
288
- //#endregion
289
- //#region src/server/actions.ts
290
- /**
291
- * Server action primitives: revalidatePath, revalidateTag, and the action handler.
292
- *
293
- * - revalidatePath(path) re-renders the route at that path and returns the RSC
294
- * flight payload for inline reconciliation.
295
- * - revalidateTag(tag) invalidates timber.cache entries by tag.
296
- *
297
- * Both are callable from anywhere on the server — actions, API routes, handlers.
298
- *
299
- * The action handler processes incoming action requests, validates CSRF,
300
- * enforces body limits, executes the action, and returns the response
301
- * (with piggybacked RSC payload if revalidatePath was called).
302
- *
303
- * See design/08-forms-and-actions.md
304
- */
305
- /**
306
- * Get the current revalidation state. Throws if called outside an action context.
307
- * @internal
308
- */
309
- function getRevalidationState() {
310
- const state = revalidationAls.getStore();
311
- if (!state) throw new Error("revalidatePath/revalidateTag called outside of a server action context. These functions can only be called during action execution.");
312
- return state;
313
- }
314
- /**
315
- * Re-render the route at `path` and include the RSC flight payload in the
316
- * action response. The client reconciles inline — no separate fetch needed.
317
- *
318
- * Can be called from server actions, API routes, or any server-side context.
319
- *
320
- * @param path - The path to re-render (e.g. '/dashboard', '/todos').
321
- */
322
- function revalidatePath(path) {
323
- const state = getRevalidationState();
324
- if (!state.paths.includes(path)) state.paths.push(path);
325
- }
326
- /**
327
- * Invalidate all timber.cache entries tagged with `tag`.
328
- * Does not return a payload — the next request for an invalidated entry re-executes.
329
- *
330
- * @param tag - The cache tag to invalidate (e.g. 'products', 'user:123').
331
- */
332
- function revalidateTag(tag) {
333
- const state = getRevalidationState();
334
- if (!state.tags.includes(tag)) state.tags.push(tag);
335
- }
336
- /**
337
- * Execute a server action and process revalidation.
338
- *
339
- * 1. Sets up revalidation state
340
- * 2. Calls the action function
341
- * 3. Processes revalidateTag calls (invalidates cache entries)
342
- * 4. Processes revalidatePath calls (re-renders and captures RSC payload)
343
- * 5. Returns the action result + optional RSC payload
344
- *
345
- * @param actionFn - The server action function to execute.
346
- * @param args - Arguments to pass to the action.
347
- * @param config - Handler configuration (cache handler, renderer).
348
- */
349
- async function executeAction(actionFn, args, config = {}, spanMeta) {
350
- const state = {
351
- paths: [],
352
- tags: []
353
- };
354
- let actionResult;
355
- let redirectTo;
356
- let redirectStatus;
357
- await revalidationAls.run(state, async () => {
358
- try {
359
- actionResult = await withSpan("timber.action", {
360
- ...spanMeta?.actionFile ? { "timber.action_file": spanMeta.actionFile } : {},
361
- ...spanMeta?.actionName ? { "timber.action_name": spanMeta.actionName } : {}
362
- }, () => actionFn(...args));
363
- } catch (error) {
364
- if (error instanceof RedirectSignal) {
365
- redirectTo = error.location;
366
- redirectStatus = error.status;
367
- } else throw error;
368
- }
369
- });
370
- if (state.tags.length > 0) {
371
- const handler = getCacheHandler();
372
- await Promise.all(state.tags.map((tag) => handler.invalidate({ tag })));
373
- }
374
- let revalidation;
375
- if (state.paths.length > 0 && config.renderer) {
376
- const path = state.paths[0];
377
- try {
378
- revalidation = await config.renderer(path);
379
- } catch (renderError) {
380
- if (renderError instanceof RedirectSignal) {
381
- redirectTo = renderError.location;
382
- redirectStatus = renderError.status;
383
- } else console.error("[timber] revalidatePath render failed:", renderError);
384
- }
385
- }
386
- return {
387
- actionResult,
388
- revalidation,
389
- ...redirectTo ? {
390
- redirectTo,
391
- redirectStatus
392
- } : {}
393
- };
394
- }
395
- /**
396
- * Build an HTTP Response for a no-JS form submission.
397
- * Standard POST → 302 redirect pattern.
398
- *
399
- * @param redirectPath - Where to redirect after the action executes.
400
- */
401
- function buildNoJsResponse(redirectPath, status = 302) {
402
- return new Response(null, {
403
- status,
404
- headers: { Location: redirectPath }
405
- });
406
- }
407
- /**
408
- * Detect whether the incoming request is an RSC action request (with JS)
409
- * or a plain HTML form POST (no JS).
410
- *
411
- * RSC action requests use Accept: text/x-component or Content-Type: text/x-component.
412
- */
413
- function isRscActionRequest(req) {
414
- const accept = req.headers.get("Accept") ?? "";
415
- const contentType = req.headers.get("Content-Type") ?? "";
416
- return accept.includes("text/x-component") || contentType.includes("text/x-component");
417
- }
418
- //#endregion
419
- export { revalidateTag as a, RedirectType as c, redirect as d, redirectExternal as f, revalidatePath as i, RenderError as l, executeAction as n, DenySignal as o, waitUntil as p, isRscActionRequest as r, RedirectSignal as s, buildNoJsResponse as t, deny as u };
420
-
421
- //# sourceMappingURL=actions-DLnUaR65.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"actions-DLnUaR65.js","names":[],"sources":["../../src/server/waituntil-bridge.ts","../../src/server/primitives.ts","../../src/server/actions.ts"],"sourcesContent":["/**\n * Per-request waitUntil bridge — ALS bridge for platform adapters.\n *\n * The generated entry point (Nitro, Cloudflare) wraps the handler with\n * `runWithWaitUntil`, binding the platform's lifecycle extension function\n * (e.g., h3's `event.waitUntil()` or CF's `ctx.waitUntil()`) for the\n * request duration. The `waitUntil()` primitive reads from this ALS to\n * dispatch background work to the correct platform API.\n *\n * Design doc: design/11-platform.md §\"waitUntil()\"\n */\n\nimport { waitUntilAls } from './als-registry.js';\n\n/**\n * Run a function with a per-request waitUntil handler installed.\n *\n * Called by generated entry points (Nitro node-server/bun, Cloudflare)\n * to bind the platform's lifecycle extension for the request duration.\n */\nexport function runWithWaitUntil<T>(\n waitUntilFn: (promise: Promise<unknown>) => void,\n fn: () => T\n): T {\n return waitUntilAls.run(waitUntilFn, fn);\n}\n\n/**\n * Get the current request's waitUntil function, if available.\n *\n * Returns undefined when no platform adapter has installed a waitUntil\n * handler for the current request (e.g., on platforms that don't support\n * lifecycle extension, or outside a request context).\n */\nexport function getWaitUntil(): ((promise: Promise<unknown>) => void) | undefined {\n return waitUntilAls.getStore();\n}\n","// Server-side primitives: deny, redirect, redirectExternal, RenderError, waitUntil, SsrStreamError\n//\n// These are the core runtime signals that components, middleware, and access gates\n// use to control request flow. See design/10-error-handling.md.\n\nimport type { JsonSerializable } from './types.js';\nimport { getWaitUntil as _getWaitUntil } from './waituntil-bridge.js';\nimport { isDebug } from './debug.js';\nimport { getRequestSearchString } from './request-context.js';\nimport { mergePreservedSearchParams } from '../shared/merge-search-params.js';\n\n// ─── Dev-mode validation ────────────────────────────────────────────────────\n\n/**\n * Check if a value is JSON-serializable without data loss.\n * Returns a description of the first non-serializable value found, or null if OK.\n *\n * @internal Exported for testing only.\n */\nexport function findNonSerializable(value: unknown, path = 'data'): string | null {\n if (value === null || value === undefined) return null;\n\n switch (typeof value) {\n case 'string':\n case 'number':\n case 'boolean':\n return null;\n case 'bigint':\n return `${path} contains a BigInt — BigInt throws in JSON.stringify`;\n case 'function':\n return `${path} is a function — functions are not JSON-serializable`;\n case 'symbol':\n return `${path} is a symbol — symbols are not JSON-serializable`;\n case 'object':\n break;\n default:\n return `${path} has unsupported type \"${typeof value}\"`;\n }\n\n if (value instanceof Date) {\n return `${path} is a Date — Dates silently coerce to strings in JSON.stringify`;\n }\n if (value instanceof Map) {\n return `${path} is a Map — Maps serialize as {} in JSON.stringify (data loss)`;\n }\n if (value instanceof Set) {\n return `${path} is a Set — Sets serialize as {} in JSON.stringify (data loss)`;\n }\n if (value instanceof RegExp) {\n return `${path} is a RegExp — RegExps serialize as {} in JSON.stringify`;\n }\n if (value instanceof Error) {\n return `${path} is an Error — Errors serialize as {} in JSON.stringify`;\n }\n\n if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) {\n const result = findNonSerializable(value[i], `${path}[${i}]`);\n if (result) return result;\n }\n return null;\n }\n\n // Plain object — only Object.prototype is safe. Null-prototype objects\n // (Object.create(null)) survive JSON.stringify but React Flight rejects\n // them with \"Classes or null prototypes are not supported\", so the\n // pre-flush deny path (renderDenyPage → renderToReadableStream) would throw.\n const proto = Object.getPrototypeOf(value);\n if (proto === null) {\n return `${path} is a null-prototype object — React Flight rejects null prototypes`;\n }\n if (proto !== Object.prototype) {\n const name = (value as object).constructor?.name ?? 'unknown';\n return `${path} is a ${name} instance — class instances may lose data in JSON.stringify`;\n }\n\n for (const key of Object.keys(value as Record<string, unknown>)) {\n const result = findNonSerializable((value as Record<string, unknown>)[key], `${path}.${key}`);\n if (result) return result;\n }\n return null;\n}\n\n/**\n * Emit a dev-mode warning if data is not JSON-serializable.\n * No-op in production.\n */\nfunction warnIfNotSerializable(data: unknown, callerName: string): void {\n if (!isDebug()) return;\n if (data === undefined) return;\n\n const issue = findNonSerializable(data);\n if (issue) {\n console.warn(\n `[timber] ${callerName}: ${issue}. ` +\n 'Data passed to deny() or RenderError must be JSON-serializable because ' +\n 'the post-flush path uses JSON.stringify, not React Flight.'\n );\n }\n}\n\n// ─── DenySignal ─────────────────────────────────────────────────────────────\n\n/**\n * Render-phase signal thrown by `deny()`. Caught by the framework to produce\n * the correct HTTP status code (segment context) or graceful degradation (slot context).\n */\nexport class DenySignal extends Error {\n readonly status: number;\n readonly data: JsonSerializable | undefined;\n\n constructor(status: number, data?: JsonSerializable) {\n super(`Access denied with status ${status}`);\n this.name = 'DenySignal';\n this.status = status;\n this.data = data;\n }\n\n /**\n * Extract the file that called deny() from the stack trace.\n * Returns a short path (e.g. \"app/auth/access.ts\") or undefined if\n * the stack can't be parsed. Dev-only — used for dev log output.\n */\n get sourceFile(): string | undefined {\n if (!this.stack) return undefined;\n const frames = this.stack.split('\\n');\n // Skip the Error line and the deny() frame — the caller is the 3rd line.\n // Stack format: \" at FnName (file:line:col)\" or \" at file:line:col\"\n for (let i = 2; i < frames.length; i++) {\n const frame = frames[i];\n if (!frame) continue;\n // Skip framework internals\n if (frame.includes('primitives.ts') || frame.includes('node_modules')) continue;\n // Extract file path from the frame\n const match =\n frame.match(/\\(([^)]+?)(?::\\d+:\\d+)\\)/) ?? frame.match(/at\\s+([^\\s]+?)(?::\\d+:\\d+)/);\n if (match?.[1]) {\n // Shorten to app-relative path\n const full = match[1];\n const appIdx = full.indexOf('/app/');\n return appIdx >= 0 ? full.slice(appIdx + 1) : full;\n }\n }\n return undefined;\n }\n}\n\n/** Options for deny() when using the object form. */\nexport interface DenyOptions {\n /** HTTP status code (4xx or 5xx). Default: 403. */\n status?: number;\n /** Human-readable message (logged server-side, not sent to client). */\n message?: string;\n /** JSON-serializable data passed as `dangerouslyPassData` prop to status-code files. */\n data?: JsonSerializable;\n}\n\n/**\n * Universal denial/error primitive. Throws a `DenySignal` that the framework catches.\n *\n * - In segment context (outside Suspense): produces HTTP status code\n * - In slot context: graceful degradation → denied.tsx → default.tsx → null\n * - Inside Suspense (hold window): promoted to pre-flush behavior\n * - Inside Suspense (after flush): error boundary + noindex meta\n *\n * Supports both positional and object signatures:\n * ```ts\n * deny() // 403 (default)\n * deny(404) // 404\n * deny(503, { retry: true }) // 503 with data\n * deny({ status: 503, message: 'Maintenance' }) // object form\n * ```\n *\n * Accepts any 4xx or 5xx status code. This replaces the need for\n * `throw new RenderError(...)` in user code — RenderError is now an\n * internal pipeline detail.\n *\n * @param statusOrOptions - Status code (number) or options object. Default: 403.\n * @param data - Optional JSON-serializable data (positional form only).\n */\nexport function deny(statusOrOptions?: number | DenyOptions, data?: JsonSerializable): never {\n let status: number;\n let resolvedData: JsonSerializable | undefined;\n\n if (typeof statusOrOptions === 'object' && statusOrOptions !== null) {\n status = statusOrOptions.status ?? 403;\n resolvedData = statusOrOptions.data;\n } else {\n status = statusOrOptions ?? 403;\n resolvedData = data;\n }\n\n if (status < 400 || status > 599) {\n throw new Error(`deny() requires a 4xx or 5xx status code, got ${status}.`);\n }\n warnIfNotSerializable(resolvedData, 'deny()');\n throw new DenySignal(status, resolvedData);\n}\n\n/**\n * @deprecated Use `deny(404)` instead.\n * Kept for internal use by the Next.js shim layer.\n * @internal\n */\nexport function notFound(): never {\n deny(404);\n}\n\n/**\n * Next.js redirect type discriminator.\n *\n * Provided for API compatibility with libraries that import `RedirectType`\n * from `next/navigation`. In timber, `redirect()` always uses `replace`\n * semantics (no history entry for the redirect itself).\n */\nexport const RedirectType = {\n push: 'push',\n replace: 'replace',\n} as const;\n\n// ─── RedirectSignal ─────────────────────────────────────────────────────────\n\n/**\n * Render-phase signal thrown by `redirect()` and `redirectExternal()`.\n * Caught by the framework to produce a 3xx response or client-side navigation.\n */\nexport class RedirectSignal extends Error {\n readonly location: string;\n readonly status: number;\n\n constructor(location: string, status: number) {\n super(`Redirect to ${location}`);\n this.name = 'RedirectSignal';\n this.location = location;\n this.status = status;\n }\n}\n\n/** Pattern matching absolute URLs: http(s):// or protocol-relative // */\nconst ABSOLUTE_URL_RE = /^(?:[a-zA-Z][a-zA-Z\\d+\\-.]*:|\\/\\/)/;\n\n/**\n * Options for redirect() — alternative to passing a bare status code.\n */\nexport interface RedirectOptions {\n /** HTTP redirect status code (3xx). Defaults to 302 (or 308 when `permanent: true`). */\n status?: number;\n /**\n * When true, defaults the status to 308 (Permanent Redirect, preserves HTTP method).\n * If `status` is also provided, `status` takes precedence.\n *\n * @example\n * redirect('/new-path', { permanent: true }); // 308\n * redirect('/new-path', { permanent: true, status: 301 }); // 301\n */\n permanent?: boolean;\n /**\n * Preserve search params from the current request URL on the redirect target.\n *\n * - `true` — preserve ALL current search params (target params take precedence)\n * - `string[]` — preserve only the named params (e.g. `['private', 'token']`)\n *\n * Target path's own query params always take precedence over preserved ones.\n */\n preserveSearchParams?: true | string[];\n}\n\n/**\n * Redirect to a relative path. Rejects absolute and protocol-relative URLs.\n * Use `redirectExternal()` for external redirects with an allow-list.\n *\n * @param path - Relative path (e.g. '/login', 'settings', '/login?returnTo=/dash')\n * @param statusOrOptions - HTTP status code (3xx, default 302) or options object.\n *\n * @example\n * // Simple redirect\n * redirect('/login');\n *\n * // With status code\n * redirect('/login', 301);\n *\n * // With preserved search params\n * redirect(`/docs/${version}/${slug}`, { preserveSearchParams: ['foo'] });\n */\nexport function redirect(path: string, statusOrOptions?: number | RedirectOptions): never {\n let status: number;\n let preserveSearchParams: true | string[] | undefined;\n\n if (typeof statusOrOptions === 'number') {\n status = statusOrOptions;\n } else if (statusOrOptions) {\n // Explicit status wins. Otherwise permanent: true → 308, default → 302.\n status = statusOrOptions.status ?? (statusOrOptions.permanent ? 308 : 302);\n preserveSearchParams = statusOrOptions.preserveSearchParams;\n } else {\n status = 302;\n }\n\n if (status < 300 || status > 399) {\n throw new Error(`redirect() requires a 3xx status code, got ${status}.`);\n }\n if (ABSOLUTE_URL_RE.test(path)) {\n throw new Error(\n `redirect() only accepts relative URLs. Got absolute URL: \"${path}\". ` +\n 'Use redirectExternal(url, allowList) for external redirects.'\n );\n }\n\n let resolvedPath = path;\n if (preserveSearchParams) {\n const currentSearch = getRequestSearchString();\n resolvedPath = mergePreservedSearchParams(path, currentSearch, preserveSearchParams);\n }\n\n throw new RedirectSignal(resolvedPath, status);\n}\n\n/**\n * @deprecated Use `redirect(path, { permanent: true })` instead.\n * Kept for internal use by the Next.js shim layer.\n * @internal\n */\nexport function permanentRedirect(path: string, options?: Omit<RedirectOptions, 'status'>): never {\n redirect(path, { permanent: true, ...options });\n}\n\n/**\n * Redirect to an external URL. The hostname must be in the provided allow-list.\n *\n * @param url - Absolute URL to redirect to.\n * @param allowList - Array of allowed hostnames (e.g. ['example.com', 'auth.example.com']).\n * @param status - HTTP redirect status code (3xx). Defaults to 302.\n */\nexport function redirectExternal(url: string, allowList: string[], status: number = 302): never {\n if (status < 300 || status > 399) {\n throw new Error(`redirectExternal() requires a 3xx status code, got ${status}.`);\n }\n\n let hostname: string;\n try {\n hostname = new URL(url).hostname;\n } catch {\n throw new Error(`redirectExternal() received an invalid URL: \"${url}\"`);\n }\n\n if (!allowList.includes(hostname)) {\n throw new Error(\n `redirectExternal() target \"${hostname}\" is not in the allow-list. ` +\n `Allowed: [${allowList.join(', ')}]`\n );\n }\n\n throw new RedirectSignal(url, status);\n}\n\n// ─── RenderError ────────────────────────────────────────────────────────────\n\n/**\n * Typed digest that crosses the RSC → client boundary.\n * The `code` identifies the error class; `data` carries JSON-serializable context.\n */\nexport interface RenderErrorDigest<\n TCode extends string = string,\n TData extends JsonSerializable = JsonSerializable,\n> {\n code: TCode;\n data: TData;\n}\n\n/**\n * Typed throw for render-phase errors that carry structured context to error boundaries.\n *\n * The `digest` (code + data) is serialized into the RSC stream separately from the\n * Error instance — only the digest crosses the RSC → client boundary.\n *\n * @example\n * ```ts\n * throw new RenderError('PRODUCT_NOT_FOUND', {\n * title: 'Product not found',\n * resourceId: params.id,\n * })\n * ```\n */\nexport class RenderError<\n TCode extends string = string,\n TData extends JsonSerializable = JsonSerializable,\n> extends Error {\n readonly code: TCode;\n readonly digest: RenderErrorDigest<TCode, TData>;\n readonly status: number;\n\n constructor(code: TCode, data: TData, options?: { status?: number }) {\n super(`RenderError: ${code}`);\n this.name = 'RenderError';\n this.code = code;\n this.digest = { code, data };\n\n warnIfNotSerializable(data, 'RenderError');\n\n const status = options?.status ?? 500;\n if (status < 400 || status > 599) {\n throw new Error(`RenderError status must be 4xx or 5xx, got ${status}.`);\n }\n this.status = status;\n }\n}\n\n// ─── waitUntil ──────────────────────────────────────────────────────────────\n\n/** Minimal interface for adapters that support background work. */\nexport interface WaitUntilAdapter {\n waitUntil?(promise: Promise<unknown>): void;\n}\n\n// Intentional per-app singleton — warn-once flag that persists for the\n// lifetime of the process/isolate. Not per-request; do not migrate to ALS.\nlet _waitUntilWarned = false;\n\n/**\n * Register a promise to be kept alive after the response is sent.\n * Maps to `ctx.waitUntil()` on Cloudflare Workers and similar platforms.\n *\n * In production, the platform adapter installs a per-request waitUntil\n * function via ALS (see waituntil-bridge.ts). This function checks the\n * ALS bridge first, then falls back to the legacy adapter argument.\n *\n * If neither is available, a warning is logged once and the promise is\n * left to resolve (or reject) without being tracked.\n *\n * @param promise - The background work to keep alive.\n * @param adapter - Optional legacy adapter (prefer ALS bridge in production).\n */\nexport function waitUntil(promise: Promise<unknown>, adapter?: WaitUntilAdapter): void {\n // Check ALS bridge first (installed by generated entry points)\n const alsFn = _getWaitUntil();\n if (alsFn) {\n alsFn(promise);\n return;\n }\n\n // Fall back to legacy adapter argument\n if (adapter && typeof adapter.waitUntil === 'function') {\n adapter.waitUntil(promise);\n return;\n }\n\n if (!_waitUntilWarned) {\n _waitUntilWarned = true;\n console.warn(\n '[timber] waitUntil() is not supported by the current adapter. ' +\n 'Background work will not be tracked. This warning is shown once.'\n );\n }\n}\n\n/**\n * Reset the waitUntil warning state. Exported for testing only.\n * @internal\n */\nexport function _resetWaitUntilWarning(): void {\n _waitUntilWarned = false;\n}\n\n// ─── SsrStreamError ─────────────────────────────────────────────────────────\n\n/**\n * Error thrown when SSR's renderToReadableStream fails due to an error\n * in the decoded RSC stream (e.g., uncontained slot errors).\n *\n * The RSC entry checks for this error type in its catch block to avoid\n * re-executing server components via renderDenyPage. Instead, it renders\n * a bare deny/error page without layout wrapping.\n *\n * Defined in primitives.ts (not ssr-entry.ts) because ssr-entry.ts imports\n * react-dom/server which cannot be loaded in the RSC environment.\n */\nexport class SsrStreamError extends Error {\n constructor(\n message: string,\n public readonly cause: unknown\n ) {\n super(message);\n this.name = 'SsrStreamError';\n }\n}\n","/**\n * Server action primitives: revalidatePath, revalidateTag, and the action handler.\n *\n * - revalidatePath(path) re-renders the route at that path and returns the RSC\n * flight payload for inline reconciliation.\n * - revalidateTag(tag) invalidates timber.cache entries by tag.\n *\n * Both are callable from anywhere on the server — actions, API routes, handlers.\n *\n * The action handler processes incoming action requests, validates CSRF,\n * enforces body limits, executes the action, and returns the response\n * (with piggybacked RSC payload if revalidatePath was called).\n *\n * See design/08-forms-and-actions.md\n */\n\nimport { getCacheHandler } from '../cache/handler-store';\nimport { RedirectSignal } from './primitives';\nimport { withSpan } from './tracing';\nimport { revalidationAls, type RevalidationState } from './als-registry.js';\n\n// ─── Types ───────────────────────────────────────────────────────────────\n\n/** Result of rendering a revalidation — element tree before RSC serialization. */\nexport interface RevalidationResult {\n /** React element tree (pre-serialization — passed to renderToReadableStream). */\n element: unknown;\n /** Resolved head elements for metadata. */\n headElements: unknown[];\n}\n\n/** Renderer function that builds a React element tree for a given path. */\nexport type RevalidateRenderer = (path: string) => Promise<RevalidationResult>;\n\n// Re-export the type from the registry for public API consumers.\nexport type { RevalidationState } from './als-registry.js';\n\n/** Options for creating the action handler. */\nexport interface ActionHandlerConfig {\n /** Renderer for producing RSC payloads during revalidation. */\n renderer?: RevalidateRenderer;\n}\n\n/** Result of handling a server action request. */\nexport interface ActionHandlerResult {\n /** The action's return value (serialized). */\n actionResult: unknown;\n /** Revalidation result if revalidatePath was called (element tree, not yet serialized). */\n revalidation?: RevalidationResult;\n /** Redirect location if a RedirectSignal was thrown during revalidation. */\n redirectTo?: string;\n /** Redirect status code. */\n redirectStatus?: number;\n}\n\n// ─── Revalidation State ──────────────────────────────────────────────────\n\n// Per-request revalidation state stored in AsyncLocalStorage (from als-registry.ts).\n// This ensures concurrent requests never share or overwrite each other's state\n// (the previous module-level global was vulnerable to cross-request pollution).\n\n/**\n * Set the revalidation state for the current action execution.\n * @internal — kept for test compatibility; prefer executeAction() which uses ALS.\n */\nexport function _setRevalidationState(state: RevalidationState): void {\n // Enter ALS scope — this is only used by tests that call revalidatePath/Tag\n // directly without going through executeAction().\n revalidationAls.enterWith(state);\n}\n\n/**\n * Clear the revalidation state after action execution.\n * @internal — kept for test compatibility.\n */\nexport function _clearRevalidationState(): void {\n revalidationAls.enterWith(undefined as unknown as RevalidationState);\n}\n\n/**\n * Get the current revalidation state. Throws if called outside an action context.\n * @internal\n */\nfunction getRevalidationState(): RevalidationState {\n const state = revalidationAls.getStore();\n if (!state) {\n throw new Error(\n 'revalidatePath/revalidateTag called outside of a server action context. ' +\n 'These functions can only be called during action execution.'\n );\n }\n return state;\n}\n\n// ─── Public API ──────────────────────────────────────────────────────────\n\n/**\n * Re-render the route at `path` and include the RSC flight payload in the\n * action response. The client reconciles inline — no separate fetch needed.\n *\n * Can be called from server actions, API routes, or any server-side context.\n *\n * @param path - The path to re-render (e.g. '/dashboard', '/todos').\n */\nexport function revalidatePath(path: string): void {\n const state = getRevalidationState();\n if (!state.paths.includes(path)) {\n state.paths.push(path);\n }\n}\n\n/**\n * Invalidate all timber.cache entries tagged with `tag`.\n * Does not return a payload — the next request for an invalidated entry re-executes.\n *\n * @param tag - The cache tag to invalidate (e.g. 'products', 'user:123').\n */\nexport function revalidateTag(tag: string): void {\n const state = getRevalidationState();\n if (!state.tags.includes(tag)) {\n state.tags.push(tag);\n }\n}\n\n// ─── Action Handler ──────────────────────────────────────────────────────\n\n/**\n * Execute a server action and process revalidation.\n *\n * 1. Sets up revalidation state\n * 2. Calls the action function\n * 3. Processes revalidateTag calls (invalidates cache entries)\n * 4. Processes revalidatePath calls (re-renders and captures RSC payload)\n * 5. Returns the action result + optional RSC payload\n *\n * @param actionFn - The server action function to execute.\n * @param args - Arguments to pass to the action.\n * @param config - Handler configuration (cache handler, renderer).\n */\nexport async function executeAction(\n actionFn: (...args: unknown[]) => Promise<unknown>,\n args: unknown[],\n config: ActionHandlerConfig = {},\n spanMeta?: { actionFile?: string; actionName?: string }\n): Promise<ActionHandlerResult> {\n const state: RevalidationState = { paths: [], tags: [] };\n let actionResult: unknown;\n let redirectTo: string | undefined;\n let redirectStatus: number | undefined;\n\n // Run the action inside ALS scope so revalidatePath/Tag resolve to this\n // request's state object — concurrent requests each get their own scope.\n await revalidationAls.run(state, async () => {\n try {\n actionResult = await withSpan(\n 'timber.action',\n {\n ...(spanMeta?.actionFile ? { 'timber.action_file': spanMeta.actionFile } : {}),\n ...(spanMeta?.actionName ? { 'timber.action_name': spanMeta.actionName } : {}),\n },\n () => actionFn(...args)\n );\n } catch (error) {\n if (error instanceof RedirectSignal) {\n redirectTo = error.location;\n redirectStatus = error.status;\n } else {\n throw error;\n }\n }\n });\n\n // Process tag invalidation via the module-level cache handler singleton.\n // setCacheHandler() is called at boot from rsc-entry when timber.config.ts\n // provides a cacheHandler; otherwise falls back to in-memory LRU (TIM-599).\n if (state.tags.length > 0) {\n const handler = getCacheHandler();\n await Promise.all(state.tags.map((tag) => handler.invalidate({ tag })));\n }\n\n // Process path revalidation — build element tree (not yet serialized)\n let revalidation: RevalidationResult | undefined;\n if (state.paths.length > 0 && config.renderer) {\n // For now, render the first revalidated path.\n // Multiple paths could be supported via multipart streaming in the future.\n const path = state.paths[0];\n try {\n revalidation = await config.renderer(path);\n } catch (renderError) {\n if (renderError instanceof RedirectSignal) {\n // Revalidation triggered a redirect (e.g., session expired)\n redirectTo = renderError.location;\n redirectStatus = renderError.status;\n } else {\n // Log but don't fail the action — revalidation is best-effort\n console.error('[timber] revalidatePath render failed:', renderError);\n }\n }\n }\n\n return {\n actionResult,\n revalidation,\n ...(redirectTo ? { redirectTo, redirectStatus } : {}),\n };\n}\n\n/**\n * Build an HTTP Response for a no-JS form submission.\n * Standard POST → 302 redirect pattern.\n *\n * @param redirectPath - Where to redirect after the action executes.\n */\nexport function buildNoJsResponse(redirectPath: string, status: number = 302): Response {\n return new Response(null, {\n status,\n headers: { Location: redirectPath },\n });\n}\n\n/**\n * Detect whether the incoming request is an RSC action request (with JS)\n * or a plain HTML form POST (no JS).\n *\n * RSC action requests use Accept: text/x-component or Content-Type: text/x-component.\n */\nexport function isRscActionRequest(req: Request): boolean {\n const accept = req.headers.get('Accept') ?? '';\n const contentType = req.headers.get('Content-Type') ?? '';\n return accept.includes('text/x-component') || contentType.includes('text/x-component');\n}\n"],"mappings":"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"}
@@ -1,41 +0,0 @@
1
- import { AsyncLocalStorage } from "node:async_hooks";
2
- //#region src/server/als-registry.ts
3
- /**
4
- * Centralized AsyncLocalStorage registry for server-side per-request state.
5
- *
6
- * ALL ALS instances used by the server framework live here. Individual
7
- * modules (request-context.ts, tracing.ts, actions.ts, etc.) import from
8
- * this registry and re-export public accessor functions.
9
- *
10
- * Why: ALS instances require singleton semantics — if two copies of the
11
- * same ALS exist (one from a relative import, one from a barrel import),
12
- * one module writes to its copy and another reads from an empty copy.
13
- * Centralizing ALS creation in a single module eliminates this class of bug.
14
- *
15
- * The `timber-shims` plugin ensures `@timber-js/app/server` resolves to
16
- * src/ in RSC and SSR environments, so all import paths converge here.
17
- *
18
- * DO NOT create ALS instances outside this file. If you need a new ALS,
19
- * add it here and import from `./als-registry.js` in the consuming module.
20
- *
21
- * See design/18-build-system.md §"Module Singleton Strategy" and
22
- * §"Singleton State Registry".
23
- */
24
- /** @internal — import via request-context.ts public API */
25
- var requestContextAls = new AsyncLocalStorage();
26
- /** @internal — import via tracing.ts public API */
27
- var traceAls = new AsyncLocalStorage();
28
- /** @internal — import via server-timing.ts public API */
29
- var timingAls = new AsyncLocalStorage();
30
- /** @internal — import via actions.ts public API */
31
- var revalidationAls = new AsyncLocalStorage();
32
- /** @internal — import via form-flash.ts public API */
33
- var formFlashAls = new AsyncLocalStorage();
34
- /** @internal — import via early-hints-sender.ts public API */
35
- var earlyHintsSenderAls = new AsyncLocalStorage();
36
- /** @internal — import via waituntil-bridge.ts public API */
37
- var waitUntilAls = new AsyncLocalStorage();
38
- //#endregion
39
- export { timingAls as a, revalidationAls as i, formFlashAls as n, traceAls as o, requestContextAls as r, waitUntilAls as s, earlyHintsSenderAls as t };
40
-
41
- //# sourceMappingURL=als-registry-HS0LGUl2.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"als-registry-HS0LGUl2.js","names":[],"sources":["../../src/server/als-registry.ts"],"sourcesContent":["/**\n * Centralized AsyncLocalStorage registry for server-side per-request state.\n *\n * ALL ALS instances used by the server framework live here. Individual\n * modules (request-context.ts, tracing.ts, actions.ts, etc.) import from\n * this registry and re-export public accessor functions.\n *\n * Why: ALS instances require singleton semantics — if two copies of the\n * same ALS exist (one from a relative import, one from a barrel import),\n * one module writes to its copy and another reads from an empty copy.\n * Centralizing ALS creation in a single module eliminates this class of bug.\n *\n * The `timber-shims` plugin ensures `@timber-js/app/server` resolves to\n * src/ in RSC and SSR environments, so all import paths converge here.\n *\n * DO NOT create ALS instances outside this file. If you need a new ALS,\n * add it here and import from `./als-registry.js` in the consuming module.\n *\n * See design/18-build-system.md §\"Module Singleton Strategy\" and\n * §\"Singleton State Registry\".\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport type { DebugComponentEntry } from './rsc-entry/helpers.js';\n\n// ─── Request Context ──────────────────────────────────────────────────────\n// Used by: request-context.ts (getHeaders(), getCookies(), getSearchParams())\n// Design doc: design/04-authorization.md\n\n/** @internal — import via request-context.ts public API */\nexport const requestContextAls = new AsyncLocalStorage<RequestContextStore>();\n\nexport interface RequestContextStore {\n /** Incoming request headers (read-only view). */\n headers: Headers;\n /** Raw cookie header string, parsed lazily into a Map on first access. */\n cookieHeader: string;\n /** Lazily-parsed cookie map (mutable — reflects write-overlay from set()). */\n parsedCookies?: Map<string, string>;\n /** Original (pre-overlay) frozen headers, kept for overlay merging. */\n originalHeaders: Headers;\n /**\n * Promise resolving to the raw URLSearchParams for the current request.\n * To get typed parsed params, import a search params definition and\n * call `.parse(searchParams())`.\n */\n searchParamsPromise: Promise<URLSearchParams>;\n /**\n * Raw search string from the request URL (e.g. \"?foo=bar&baz=1\").\n * Available synchronously for use in `redirect()` with `preserveSearchParams`.\n */\n searchString: string;\n /**\n * Promise resolving to the coerced segment params for the current request.\n * Set by the pipeline after route matching and param coercion, before\n * middleware and rendering. Pages and layouts read params via\n * `getSegmentParams()` instead of receiving them as a prop.\n *\n * See design/07-routing.md §\"params.ts — Convention File for Typed Params\"\n */\n segmentParamsPromise?: Promise<Record<string, string | string[]>>;\n /** Outgoing Set-Cookie entries (name → serialized value + options). Last write wins. */\n cookieJar: Map<string, CookieEntry>;\n /** Whether the response has flushed (headers committed). */\n flushed: boolean;\n /** Whether the current context allows cookie mutation. */\n mutableContext: boolean;\n /**\n * Set by AccessGate or PageDenyBoundary when a DenySignal is caught\n * server-side (inside the React tree, before React Flight sees it).\n * The pipeline reads this after render to set the HTTP status code.\n * See TIM-666.\n */\n denyStatus?: number;\n /**\n * Dev-only: getter for the current request's RSC debug components.\n * Set by renderRoute() so onPipelineError can include component tree\n * context for render-phase errors without module-level shared state.\n */\n debugComponentsGetter?: () => DebugComponentEntry[];\n}\n\n/** A single outgoing cookie entry in the cookie jar. */\nexport interface CookieEntry {\n name: string;\n value: string;\n options: import('./request-context.js').CookieOptions;\n}\n\n// ─── Tracing ──────────────────────────────────────────────────────────────\n// Used by: tracing.ts (getTraceId(), getSpanId())\n// Design doc: design/17-logging.md\n\nexport interface TraceStore {\n /** 32-char lowercase hex trace ID (OTEL or UUID fallback). */\n traceId: string;\n /** OTEL span ID if available, undefined otherwise. */\n spanId?: string;\n}\n\n/** @internal — import via tracing.ts public API */\nexport const traceAls = new AsyncLocalStorage<TraceStore>();\n\n// ─── Server-Timing ────────────────────────────────────────────────────────\n// Used by: server-timing.ts (recordTiming(), withTiming())\n// Design doc: (dev-only performance instrumentation)\n\nexport interface TimingStore {\n entries: import('./server-timing.js').TimingEntry[];\n}\n\n/** @internal — import via server-timing.ts public API */\nexport const timingAls = new AsyncLocalStorage<TimingStore>();\n\n// ─── Revalidation ─────────────────────────────────────────────────────────\n// Used by: actions.ts (revalidatePath(), revalidateTag())\n// Design doc: design/08-forms-and-actions.md\n\nexport interface RevalidationState {\n /** Paths to re-render (populated by revalidatePath calls). */\n paths: string[];\n /** Tags to invalidate (populated by revalidateTag calls). */\n tags: string[];\n}\n\n/** @internal — import via actions.ts public API */\nexport const revalidationAls = new AsyncLocalStorage<RevalidationState>();\n\n// ─── Form Flash ───────────────────────────────────────────────────────────\n// Used by: form-flash.ts (getFormFlash())\n// Design doc: design/08-forms-and-actions.md §\"No-JS Error Round-Trip\"\n\n/** @internal — import via form-flash.ts public API */\nexport const formFlashAls = new AsyncLocalStorage<import('./form-flash.js').FormFlashData>();\n\n// ─── Early Hints Sender ──────────────────────────────────────────────────\n// Used by: early-hints-sender.ts (sendEarlyHints103())\n// Design doc: design/02-rendering-pipeline.md §\"Early Hints (103)\"\n\n/** Function that sends Link header values as a 103 Early Hints response. */\nexport type EarlyHintsSenderFn = (links: string[]) => void;\n\n/** @internal — import via early-hints-sender.ts public API */\nexport const earlyHintsSenderAls = new AsyncLocalStorage<EarlyHintsSenderFn>();\n\n// ─── waitUntil Bridge ────────────────────────────────────────────────────\n// Used by: waituntil-bridge.ts (waitUntil())\n// Design doc: design/11-platform.md §\"waitUntil()\"\n\n/** Function that extends the request lifecycle with a background promise. */\nexport type WaitUntilFn = (promise: Promise<unknown>) => void;\n\n/** @internal — import via waituntil-bridge.ts public API */\nexport const waitUntilAls = new AsyncLocalStorage<WaitUntilFn>();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA8BA,IAAa,oBAAoB,IAAI,mBAAwC;;AAuE7E,IAAa,WAAW,IAAI,mBAA+B;;AAW3D,IAAa,YAAY,IAAI,mBAAgC;;AAc7D,IAAa,kBAAkB,IAAI,mBAAsC;;AAOzE,IAAa,eAAe,IAAI,mBAA4D;;AAU5F,IAAa,sBAAsB,IAAI,mBAAuC;;AAU9E,IAAa,eAAe,IAAI,mBAAgC"}
@@ -1,108 +0,0 @@
1
- //#region src/server/debug.ts
2
- /**
3
- * Runtime debug flag for timber.js.
4
- *
5
- * Two distinct functions for two distinct security levels:
6
- *
7
- * ## `isDebug()` — server-side logging only
8
- *
9
- * Returns true when timber's debug/warning messages should be written to
10
- * stderr / the server console. This NEVER affects what is sent to the
11
- * client (no error details, no timing headers, no stack traces).
12
- *
13
- * Active when any of:
14
- * - `NODE_ENV !== 'production'` (standard dev mode)
15
- * - `TIMBER_DEBUG` env var is set to a truthy value at runtime
16
- * - `timber.config.ts` has `debug: true`
17
- *
18
- * ## `isDevMode()` — client-visible dev behavior
19
- *
20
- * Returns true ONLY when `NODE_ENV !== 'production'`. This gates anything
21
- * that changes what clients can observe:
22
- * - Dev error pages with stack traces (fallback-error.ts)
23
- * - Detailed Server-Timing headers (pipeline.ts)
24
- * - Error messages in action INTERNAL_ERROR payloads (action-client.ts)
25
- * - Pipeline error handler wiring (Vite overlay)
26
- *
27
- * `isDevMode()` is statically replaced in production builds → the guarded
28
- * code is tree-shaken to zero bytes. TIMBER_DEBUG cannot enable it.
29
- *
30
- * Usage:
31
- * In Cloudflare Workers wrangler.toml:
32
- * [vars]
33
- * TIMBER_DEBUG = "1"
34
- *
35
- * In Node.js:
36
- * TIMBER_DEBUG=1 node server.js
37
- *
38
- * In timber.config.ts:
39
- * export default { debug: true }
40
- *
41
- * See design/13-security.md for the security taxonomy.
42
- * See design/18-build-system.md for build pipeline details.
43
- */
44
- /**
45
- * Check if the application is running in development mode.
46
- *
47
- * This is the ONLY function that should gate client-visible dev behavior:
48
- * - Dev error pages with stack traces
49
- * - Server-Timing mode default (`'detailed'` in dev, `'total'` in prod)
50
- * - Error messages in action `INTERNAL_ERROR` payloads
51
- * - Pipeline error handler wiring (Vite overlay)
52
- *
53
- * Returns `process.env.NODE_ENV !== 'production'`, which is statically
54
- * replaced by the bundler in production builds. Code guarded by this
55
- * function is tree-shaken to zero bytes in production.
56
- *
57
- * TIMBER_DEBUG does NOT enable this — that would leak server internals
58
- * to clients. Use `isDebug()` for server-side-only logging.
59
- */
60
- function isDevMode() {
61
- return process.env.NODE_ENV !== "production";
62
- }
63
- /**
64
- * Config-level debug override. Set via `setDebugFromConfig()` during
65
- * initialization when timber.config.ts has `debug: true`.
66
- */
67
- var _configDebug = false;
68
- /**
69
- * Check if timber debug logging is active (server-side only).
70
- *
71
- * Returns true if ANY of these conditions hold:
72
- * - NODE_ENV is not 'production' (standard dev mode)
73
- * - TIMBER_DEBUG environment variable is set to a truthy value at runtime
74
- * - timber.config.ts has `debug: true`
75
- *
76
- * This function controls ONLY server-side logging — messages written to
77
- * stderr or the server console. It NEVER affects client-visible behavior
78
- * (error pages, response headers, action payloads). For client-visible
79
- * behavior, use `isDevMode()`.
80
- *
81
- * The TIMBER_DEBUG check is deliberately written as a dynamic property
82
- * access so bundlers cannot statically replace it.
83
- */
84
- function isDebug() {
85
- if (process.env.NODE_ENV !== "production") return true;
86
- if (_configDebug) return true;
87
- return _readTimberDebugEnv();
88
- }
89
- /**
90
- * Read TIMBER_DEBUG from the environment at runtime.
91
- *
92
- * Extracted to a separate function to:
93
- * 1. Prevent bundler inlining (cross-module function calls are not inlined)
94
- * 2. Handle platforms where `process` may not exist (Cloudflare Workers)
95
- * 3. Support globalThis.__TIMBER_DEBUG for programmatic control
96
- */
97
- function _readTimberDebugEnv() {
98
- if (globalThis.__TIMBER_DEBUG) return true;
99
- try {
100
- const val = typeof process !== "undefined" && process.env ? process.env["TIMBER_DEBUG"] : void 0;
101
- if (val && val !== "0" && val !== "false") return true;
102
- } catch {}
103
- return false;
104
- }
105
- //#endregion
106
- export { isDevMode as n, isDebug as t };
107
-
108
- //# sourceMappingURL=debug-ECi_61pb.js.map