@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
@@ -3,28 +3,12 @@
3
3
  // Primary deployment target. Generates a Workers-compatible entry point
4
4
  // and wrangler.jsonc configuration. See design/11-platform.md §"Cloudflare Workers".
5
5
 
6
- import { writeFile, mkdir, cp } from 'node:fs/promises';
6
+ import { writeFile } from 'node:fs/promises';
7
7
  import { execFile } from 'node:child_process';
8
8
  import { join, relative } from 'node:path';
9
9
  import { AsyncLocalStorage } from 'node:async_hooks';
10
10
  import type { TimberPlatformAdapter, TimberConfig } from './types';
11
- // Inlined from server/asset-headers.ts — adapters are loaded by Node at
12
- // Vite startup time, before Vite's module resolver is available, so cross-
13
- // directory .ts imports don't resolve.
14
- const IMMUTABLE_CACHE = 'public, max-age=31536000, immutable';
15
- const STATIC_CACHE = 'public, max-age=3600, must-revalidate';
16
-
17
- function generateHeadersFile(): string {
18
- return `# Auto-generated by @timber-js/app — static asset cache headers.
19
- # See design/25-production-deployments.md §"CDN / Edge Cache"
20
-
21
- /assets/*
22
- Cache-Control: ${IMMUTABLE_CACHE}
23
-
24
- /*
25
- Cache-Control: ${STATIC_CACHE}
26
- `;
27
- }
11
+ import { runSharedBuildSteps } from './build-output-helper.js';
28
12
 
29
13
  // ─── Bindings passthrough ─────────────────────────────────────────────────
30
14
  // ALS stores the env object per-request so server components and middleware
@@ -243,39 +227,15 @@ export function cloudflare(options: CloudflareAdapterOptions = {}): TimberPlatfo
243
227
 
244
228
  async buildOutput(config: TimberConfig, buildDir: string) {
245
229
  const outDir = join(buildDir, 'cloudflare');
246
- await mkdir(outDir, { recursive: true });
247
-
248
- // Copy client assets to static output.
249
- // When client JavaScript is disabled, skip .js files — only CSS,
250
- // fonts, images, and other static assets are needed.
251
- const clientDir = join(buildDir, 'client');
252
- const staticDir = join(outDir, 'static');
253
- await mkdir(staticDir, { recursive: true });
254
- await cp(clientDir, staticDir, {
255
- recursive: true,
256
- filter: config.clientJavascriptDisabled ? (src: string) => !src.endsWith('.js') : undefined,
257
- }).catch(() => {
258
- // Client dir may not exist when client JavaScript is disabled
259
- });
260
230
 
261
- // Write _headers file for static asset cache control.
262
- // Cloudflare Workers Static Assets reads this to set Cache-Control
263
- // headers on responses. Hashed assets get immutable; others get 1h.
264
- await writeFile(join(staticDir, '_headers'), generateHeadersFile());
265
-
266
- // Copy server bundles (rsc + ssr) into the output directory.
267
- // These are already fully bundled by Vite with resolve.noExternal: true.
268
- const rscDir = join(buildDir, 'rsc');
269
- const ssrDir = join(buildDir, 'ssr');
270
- await cp(rscDir, join(outDir, 'rsc'), { recursive: true });
271
- await cp(ssrDir, join(outDir, 'ssr'), { recursive: true });
272
-
273
- // Write the build manifest init module (if manifest data was produced).
274
- // This must be imported before the RSC handler so the global is set
275
- // when virtual:timber-build-manifest evaluates.
276
- if (config.manifestInit) {
277
- await writeFile(join(outDir, '_timber-manifest-init.js'), config.manifestInit);
278
- }
231
+ // Steps 1–5: shared across all adapters (mkdir, copy client/rsc/ssr,
232
+ // write _headers, write manifest-init).
233
+ await runSharedBuildSteps({
234
+ config,
235
+ buildDir,
236
+ outDir,
237
+ publicDirName: 'static',
238
+ });
279
239
 
280
240
  // Compile optional worker handlers (queue, scheduled, etc.)
281
241
  // Uses Vite's build API to bundle the TypeScript source into ESM.
@@ -5,28 +5,13 @@
5
5
  // compression, graceful shutdown, static file serving, and platform quirks.
6
6
  // See design/11-platform.md and design/25-production-deployments.md.
7
7
 
8
- import { writeFile, readFile, mkdir, cp } from 'node:fs/promises';
8
+ import { writeFile, readFile } from 'node:fs/promises';
9
9
  import { execFile } from 'node:child_process';
10
10
  import { join, relative } from 'node:path';
11
11
  import type { TimberPlatformAdapter, TimberConfig } from './types';
12
12
  import { generateCompressModule } from './compress-module.js';
13
- // Inlined from server/asset-headers.ts — adapters are loaded by Node at
14
- // Vite startup time, before Vite's module resolver is available, so cross-
15
- // directory .ts imports don't resolve.
16
- const IMMUTABLE_CACHE = 'public, max-age=31536000, immutable';
17
- const STATIC_CACHE = 'public, max-age=3600, must-revalidate';
18
-
19
- function generateHeadersFile(): string {
20
- return `# Auto-generated by @timber-js/app — static asset cache headers.
21
- # See design/25-production-deployments.md §"CDN / Edge Cache"
22
-
23
- /assets/*
24
- Cache-Control: ${IMMUTABLE_CACHE}
25
-
26
- /*
27
- Cache-Control: ${STATIC_CACHE}
28
- `;
29
- }
13
+ import { IMMUTABLE_CACHE } from './shared.js';
14
+ import { runSharedBuildSteps } from './build-output-helper.js';
30
15
 
31
16
  // ─── Presets ─────────────────────────────────────────────────────────────────
32
17
 
@@ -202,40 +187,21 @@ export function nitro(options: NitroAdapterOptions = {}): TimberPlatformAdapter
202
187
 
203
188
  async buildOutput(config: TimberConfig, buildDir: string) {
204
189
  const outDir = join(buildDir, 'nitro');
205
- await mkdir(outDir, { recursive: true });
206
-
207
- // Copy client assets to public directory.
208
- // When client JavaScript is disabled, skip .js files — only CSS,
209
- // fonts, images, and other static assets are needed.
210
- const clientDir = join(buildDir, 'client');
211
- const publicDir = join(outDir, 'public');
212
- await mkdir(publicDir, { recursive: true });
213
- await cp(clientDir, publicDir, {
214
- recursive: true,
215
- filter: config.clientJavascriptDisabled ? (src: string) => !src.endsWith('.js') : undefined,
216
- }).catch(() => {
217
- // Client dir may not exist when client JavaScript is disabled
218
- });
219
190
 
220
- // Write _headers file for platforms that support it (Netlify, etc.).
221
- // See design/25-production-deployments.md §"CDN / Edge Cache"
222
- await writeFile(join(publicDir, '_headers'), generateHeadersFile());
223
-
224
- // Write the build manifest init module (if manifest data was produced).
225
- if (config.manifestInit) {
226
- await writeFile(join(outDir, '_timber-manifest-init.js'), config.manifestInit);
227
- }
191
+ // Steps 1–5: shared across all adapters (mkdir, copy client/rsc/ssr,
192
+ // write _headers, write manifest-init).
193
+ await runSharedBuildSteps({
194
+ config,
195
+ buildDir,
196
+ outDir,
197
+ publicDirName: 'public',
198
+ });
228
199
 
229
200
  // Write the compression helper module for runtime use.
230
201
  // See design/25-production-deployments.md — self-hosted deployments
231
202
  // need application-level compression (Cloudflare handles it at the edge).
232
203
  await writeFile(join(outDir, '_compress.mjs'), generateCompressModule());
233
204
 
234
- // Copy rsc/ssr build output into the nitro dir so imports stay local
235
- // during the Nitro bundling step (avoids broken relative paths in output).
236
- await cp(join(buildDir, 'rsc'), join(outDir, 'rsc'), { recursive: true });
237
- await cp(join(buildDir, 'ssr'), join(outDir, 'ssr'), { recursive: true }).catch(() => {});
238
-
239
205
  // Prepend the manifest assignment directly into the RSC entry so
240
206
  // globalThis.__TIMBER_BUILD_MANIFEST__ is set before any module reads it.
241
207
  // This must be top-level code, not an import, because rollup tree-shakes
@@ -0,0 +1,40 @@
1
+ // Shared adapter utilities — leaf module with zero deps on server/, client/,
2
+ // or any Vite-dependent code.
3
+ //
4
+ // Adapters are loaded by Node at Vite startup time, before Vite's module
5
+ // resolver is available. This module uses only Node built-ins and can be
6
+ // imported by both adapters via a relative path.
7
+ //
8
+ // IMPORTANT: Do NOT add imports from ../server/, ../client/, ../plugins/,
9
+ // or any module that transitively depends on Vite. A test in
10
+ // tests/adapters/shared-no-forbidden-imports.test.ts enforces this.
11
+
12
+ // ─── Static asset cache headers ──────────────────────────────────────────
13
+
14
+ /** Cache-Control value for hashed (immutable) assets. */
15
+ export const IMMUTABLE_CACHE = 'public, max-age=31536000, immutable';
16
+
17
+ /** Cache-Control value for unhashed static assets. */
18
+ export const STATIC_CACHE = 'public, max-age=3600, must-revalidate';
19
+
20
+ /**
21
+ * Generate a `_headers` file for static asset cache control.
22
+ *
23
+ * The `_headers` file is a platform convention supported by Cloudflare Workers
24
+ * Static Assets, Cloudflare Pages, and Netlify. It maps URL patterns to
25
+ * HTTP response headers.
26
+ *
27
+ * Vite places all hashed chunks under `/assets/` — these get immutable caching.
28
+ * Everything else (favicon.ico, robots.txt, etc.) gets a shorter cache.
29
+ */
30
+ export function generateHeadersFile(): string {
31
+ return `# Auto-generated by @timber-js/app — static asset cache headers.
32
+ # See design/25-production-deployments.md §"CDN / Edge Cache"
33
+
34
+ /assets/*
35
+ Cache-Control: ${IMMUTABLE_CACHE}
36
+
37
+ /*
38
+ Cache-Control: ${STATIC_CACHE}
39
+ `;
40
+ }
@@ -2,6 +2,7 @@ import type { CacheHandler, CacheOptions } from './index';
2
2
  import { stableStringify } from './stable-stringify';
3
3
  import { createSingleflight } from './singleflight';
4
4
  import { addSpanEventSync } from '../server/tracing.js';
5
+ import { logSwrRefetchFailed } from '../server/logger.js';
5
6
  import { fnv1aHash } from './fast-hash.js';
6
7
 
7
8
  const defaultSingleflight = createSingleflight();
@@ -91,9 +92,9 @@ export function createCache<Fn extends (...args: any[]) => Promise<any>>(
91
92
  const fresh = await fn(...args);
92
93
  const tags = resolveTags(opts, args);
93
94
  await handler.set(key, fresh, { ttl: opts.ttl, tags });
94
- } catch {
95
+ } catch (err) {
95
96
  // Failed refetch — stale entry continues to be served.
96
- // Error is swallowed per design doc: "Error is logged."
97
+ logSwrRefetchFailed({ cacheKey: key, error: err });
97
98
  }
98
99
  }).catch(() => {
99
100
  // Singleflight promise rejection handled — stale continues.
package/src/cli.ts CHANGED
File without changes
@@ -27,13 +27,15 @@ export type UseActionStateFn<TData> = (
27
27
  ) => Promise<ActionResult<TData>>;
28
28
 
29
29
  /**
30
- * Return type of useActionState — matches React 19's useActionState return.
31
- * [result, formAction, isPending]
30
+ * Return type of useActionState.
31
+ * [result, formAction, isPending, errors]
32
+ * The 4th element is auto-derived from result via useFormErrors logic.
32
33
  */
33
34
  export type UseActionStateReturn<TData> = [
34
35
  result: ActionResult<TData> | null,
35
36
  formAction: (formData: FormData) => void,
36
37
  isPending: boolean,
38
+ errors: FormErrorsResult,
37
39
  ];
38
40
 
39
41
  // ─── useActionState ──────────────────────────────────────────────────────
@@ -73,7 +75,13 @@ export function useActionState<TData>(
73
75
  ): UseActionStateReturn<TData> {
74
76
  // FormFlashData is structurally compatible with ActionResult at runtime —
75
77
  // the cast satisfies React's generic inference which would otherwise widen TData.
76
- return reactUseActionState(action, initialState as ActionResult<TData> | null, permalink);
78
+ const [result, formAction, isPending] = reactUseActionState(
79
+ action,
80
+ initialState as ActionResult<TData> | null,
81
+ permalink
82
+ );
83
+ const errors = deriveFormErrors(result);
84
+ return [result, formAction, isPending, errors];
77
85
  }
78
86
 
79
87
  // ─── useFormAction ───────────────────────────────────────────────────────
@@ -114,9 +122,9 @@ export function useFormAction<TData = unknown, TInput = unknown>(
114
122
  return [execute, isPending];
115
123
  }
116
124
 
117
- // ─── useFormErrors ──────────────────────────────────────────────────────
125
+ // ─── Form error extraction ────────────────────────────────────────────────
118
126
 
119
- /** Return type of useFormErrors(). */
127
+ /** Return type of the errors element in useActionState. */
120
128
  export interface FormErrorsResult {
121
129
  /** Per-field validation errors keyed by field name. */
122
130
  fieldErrors: Record<string, string[]>;
@@ -131,27 +139,11 @@ export interface FormErrorsResult {
131
139
  }
132
140
 
133
141
  /**
134
- * Extract per-field and form-level errors from an ActionResult.
135
- *
136
- * Pure function (no internal hooks) follows React naming convention
137
- * since it's used in render. Accepts the result from `useActionState`
138
- * or flash data from `getFormFlash()`.
139
- *
140
- * @example
141
- * ```tsx
142
- * const [result, action, isPending] = useActionState(createTodo, null)
143
- * const errors = useFormErrors(result)
144
- *
145
- * return (
146
- * <form action={action}>
147
- * <input name="title" />
148
- * {errors.getFieldError('title') && <p>{errors.getFieldError('title')}</p>}
149
- * {errors.formErrors.map(e => <p key={e}>{e}</p>)}
150
- * </form>
151
- * )
152
- * ```
142
+ * Derive FormErrorsResult from an action result.
143
+ * Used internally by useActionState 4th tuple element.
144
+ * @internal — exported for test access only.
153
145
  */
154
- export function useFormErrors<TData>(
146
+ export function deriveFormErrors<TData>(
155
147
  result:
156
148
  | ActionResult<TData>
157
149
  | {
@@ -66,26 +66,33 @@ export type { LinkStatus } from './use-link-status';
66
66
  export { useRouter } from './use-router';
67
67
  export type { AppRouterInstance } from './use-router';
68
68
  export { usePathname } from './use-pathname';
69
- export { useSearchParams } from './use-search-params';
69
+ // useSearchParams removed from public exports — lives in next/navigation shim only.
70
70
  export { useSelectedLayoutSegment, useSelectedLayoutSegments } from './use-selected-layout-segment';
71
71
 
72
72
  // Forms
73
- export { useActionState, useFormAction, useFormErrors } from './form';
73
+ export { useActionState, useFormAction } from './form';
74
74
  export type { UseActionStateFn, UseActionStateReturn, FormErrorsResult } from './form';
75
75
 
76
- // Params
77
- export { useSegmentParams } from './use-params';
76
+ // Params — useSegmentParams lives on defineSegmentParams().useSegmentParams()
77
+ // and in the next/navigation shim for library compat.
78
78
 
79
- // Query states (URL-synced search params)
80
- export { useQueryStates } from './use-query-states';
79
+ // Query states useQueryStates lives on defineSearchParams().useQueryStates()
80
+ // and is available via direct import for advanced cases.
81
81
 
82
- // Cookies
83
- export { useCookie } from './use-cookie';
82
+ // Cookies — useCookie lives on defineCookie().useCookie().
83
+ // Types still exported for advanced use.
84
84
  export type { ClientCookieOptions, CookieSetter } from './use-cookie';
85
85
 
86
86
  // Register the client cookie module with defineCookie's lazy reference.
87
87
  // This runs at module load time in the client/SSR environment, wiring up
88
88
  // the useCookie hook without a top-level import in define-cookie.ts.
89
89
  import * as _useCookieMod from './use-cookie.js';
90
- import { _registerUseCookieModule } from '../cookies/define-cookie.js';
90
+ import { _registerUseCookieModule, _registerFromSchema } from '../cookies/define-cookie.js';
91
+ import { fromSchema } from '../schema-bridge.js';
91
92
  _registerUseCookieModule(_useCookieMod);
93
+ _registerFromSchema(fromSchema);
94
+
95
+ // Register the client useSegmentParams hook with defineSegmentParams.
96
+ import { useSegmentParams as _useSegmentParams } from './use-segment-params.js';
97
+ import { _registerUseSegmentParams } from '../segment-params/define.js';
98
+ _registerUseSegmentParams(_useSegmentParams);
@@ -38,8 +38,9 @@ export type { SegmentNode, StateTree } from './segment-cache.js';
38
38
  export { HistoryStack } from './history.js';
39
39
  export type { HistoryEntry } from './history.js';
40
40
 
41
- // ── Params (internal setter) ─────────────────────────────────────────────
42
- export { setCurrentParams } from './use-params.js';
41
+ // ── Params (internal setter + raw hooks) ─────────────────────────────────
42
+ export { setCurrentParams, useSegmentParams } from './use-segment-params.js';
43
+ export { useSearchParams } from './use-search-params.js';
43
44
 
44
45
  // ── Navigation context ───────────────────────────────────────────────────
45
46
  export {
@@ -5,7 +5,7 @@ import { SegmentCache, PrefetchCache, buildSegmentTree } from './segment-cache';
5
5
  import type { SegmentInfo } from './segment-cache';
6
6
  import { HistoryStack } from './history';
7
7
  import type { HeadElement } from './head';
8
- import { setCurrentParams } from './use-params.js';
8
+ import { setCurrentParams } from './use-segment-params.js';
9
9
  import {
10
10
  setNavigationState,
11
11
  getNavigationState,
@@ -124,6 +124,17 @@ export function buildRscHeaders(
124
124
 
125
125
  // ─── Response Header Extraction ──────────────────────────────────
126
126
 
127
+ /** Dev-only warning for malformed framework headers. Tree-shaken in production. */
128
+ function warnMalformedHeader(headerName: string, raw: string): void {
129
+ if (process.env.NODE_ENV !== 'production') {
130
+ const preview = raw.length > 200 ? raw.slice(0, 200) + '…' : raw;
131
+ console.warn(
132
+ `[timber] Malformed ${headerName} header \u2014 JSON.parse failed. ` +
133
+ `This indicates a framework bug or header corruption. Raw (first 200 chars): ${preview}`
134
+ );
135
+ }
136
+ }
137
+
127
138
  /**
128
139
  * Extract head elements from the X-Timber-Head response header.
129
140
  * Returns null if the header is missing or malformed.
@@ -134,6 +145,7 @@ export function extractHeadElements(response: Response): HeadElement[] | null {
134
145
  try {
135
146
  return JSON.parse(decodeURIComponent(header));
136
147
  } catch {
148
+ warnMalformedHeader('X-Timber-Head', header);
137
149
  return null;
138
150
  }
139
151
  }
@@ -152,6 +164,7 @@ export function extractSegmentInfo(response: Response): SegmentInfo[] | null {
152
164
  try {
153
165
  return JSON.parse(header);
154
166
  } catch {
167
+ warnMalformedHeader('X-Timber-Segments', header);
155
168
  return null;
156
169
  }
157
170
  }
@@ -171,6 +184,7 @@ export function extractSkippedSegments(response: Response): string[] | null {
171
184
  const parsed = JSON.parse(header);
172
185
  return Array.isArray(parsed) ? parsed : null;
173
186
  } catch {
187
+ warnMalformedHeader('X-Timber-Skipped-Segments', header);
174
188
  return null;
175
189
  }
176
190
  }
@@ -187,6 +201,7 @@ export function extractParams(response: Response): Record<string, string | strin
187
201
  try {
188
202
  return JSON.parse(header);
189
203
  } catch {
204
+ warnMalformedHeader('X-Timber-Params', header);
190
205
  return null;
191
206
  }
192
207
  }
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * ALL mutable module-level state that must have singleton semantics across
5
5
  * the client bundle lives here. Individual modules (router-ref.ts, ssr-data.ts,
6
- * use-params.ts, use-search-params.ts, unload-guard.ts) import from this file
6
+ * use-segment-params.ts, use-search-params.ts, unload-guard.ts) import from this file
7
7
  * and re-export thin wrapper functions.
8
8
  *
9
9
  * Why: In Vite dev, a module is instantiated separately if reached via different
@@ -50,7 +50,7 @@ export function _setCurrentSsrData(data: SsrData | undefined): void {
50
50
  currentSsrData = data;
51
51
  }
52
52
 
53
- // ─── Route Params (from use-params.ts) ──────────────────────────────────────
53
+ // ─── Route Params (from use-segment-params.ts) ──────────────────────────────────────
54
54
 
55
55
  /** Current route params snapshot — replaced (not mutated) on each navigation. */
56
56
  export let currentParams: Record<string, string | string[]> = {};
@@ -10,6 +10,7 @@
10
10
 
11
11
  import { useSyncExternalStore } from 'react';
12
12
  import { getSsrData } from './ssr-data.js';
13
+ import { assertValidCookieName } from '../cookies/validation.js';
13
14
 
14
15
  // ─── Types ────────────────────────────────────────────────────────────────
15
16
 
@@ -68,6 +69,17 @@ function notify(name: string): void {
68
69
  }
69
70
  }
70
71
 
72
+ /**
73
+ * Notify useCookie subscribers that a cookie value changed.
74
+ * Called by defineCookie's imperative client .set()/.delete() methods
75
+ * so mounted useCookie() consumers re-render.
76
+ *
77
+ * @internal — framework use only
78
+ */
79
+ export function notifyCookieChange(name: string): void {
80
+ notify(name);
81
+ }
82
+
71
83
  // ─── Hook ─────────────────────────────────────────────────────────────────
72
84
 
73
85
  /**
@@ -103,8 +115,25 @@ export function useCookie(
103
115
 
104
116
  const value = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
105
117
 
118
+ // Validate the name once per hook instance — names are typically
119
+ // stable string literals, so this catches the bug at first render
120
+ // rather than on every write. Throws via `assertValidCookieName` with
121
+ // a precise error if the name violates RFC 7230 token rules.
122
+ assertValidCookieName(name);
123
+
106
124
  const setCookie: CookieSetter = (newValue: string, options?: ClientCookieOptions) => {
125
+ if (typeof newValue !== 'string') {
126
+ throw new Error(
127
+ `[timber] useCookie(${JSON.stringify(name)}): value must be a string, got ${typeof newValue}.\n` +
128
+ ` To store a JSON-serializable value, use defineCookie + jsonCookieCodec from\n` +
129
+ ` '@timber-js/app/cookies' and call its useCookie() hook instead.`
130
+ );
131
+ }
107
132
  const merged = { ...defaultOptions, ...options };
133
+ // Auto-encode mirrors the server's `cookies().set()` contract:
134
+ // values are URL-encoded on write, URL-decoded on read, so the
135
+ // logical bytes round-trip losslessly. See design/29-cookies.md
136
+ // §"Encoding Contract".
108
137
  document.cookie = `${name}=${encodeURIComponent(newValue)}${serializeOptions(merged)}`;
109
138
  notify(name);
110
139
  };
package/src/codec.ts CHANGED
@@ -39,11 +39,7 @@ export type JsonSerializable =
39
39
 
40
40
  // ─── Standard Schema bridge ──────────────────────────────────────────────
41
41
 
42
- export {
43
- fromSchema,
44
- fromArraySchema,
45
- validateSync,
46
- isStandardSchema,
47
- isCodec,
48
- } from './schema-bridge.js';
42
+ // fromSchema is internal — defineSearchParams and defineCookie auto-detect
43
+ // Standard Schema objects. Users should pass raw schemas directly.
44
+ export { fromArraySchema, validateSync, isStandardSchema, isCodec } from './schema-bridge.js';
49
45
  export type { StandardSchemaV1, StandardSchemaResult } from './schema-bridge.js';
@@ -60,6 +60,34 @@ export interface TimberUserConfig {
60
60
  * See design/08-forms-and-actions.md §"Validation errors" and
61
61
  * design/13-security.md §"Sensitive field stripping".
62
62
  */
63
+ /**
64
+ * Server action runtime behavior.
65
+ *
66
+ * See design/08-forms-and-actions.md §"Middleware for Server Actions".
67
+ */
68
+ actions?: {
69
+ /**
70
+ * Run `middleware.ts` on server action requests before dispatching.
71
+ *
72
+ * **Default: `true`** — middleware runs on every action POST so
73
+ * authentication, rate limiting, tenant isolation, IP allow-listing,
74
+ * and request-header injection apply uniformly to page renders, route
75
+ * handlers, and server actions. This closes the auth-bypass class of
76
+ * issue identified by Next.js CVE-2025-29927: developers can put a
77
+ * single auth check in `middleware.ts` and trust that it gates every
78
+ * unsafe-method request.
79
+ *
80
+ * Set to `false` to restore the legacy behavior where actions skip
81
+ * middleware entirely. This is **not recommended** outside of niche
82
+ * cases (e.g. middleware that rewrites POST bodies and would corrupt
83
+ * action submissions). When false, you are responsible for placing
84
+ * auth, rate limiting, and other cross-cutting checks inside every
85
+ * action — typically via `createActionClient({ middleware: ... })`.
86
+ *
87
+ * See TIM-871.
88
+ */
89
+ runMiddleware?: boolean;
90
+ };
63
91
  forms?: {
64
92
  /**
65
93
  * Strip sensitive fields (passwords, tokens, CVV, SSN, etc.) from the