@timber-js/app 0.2.0-alpha.4 → 0.2.0-alpha.40

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 (336) hide show
  1. package/LICENSE +8 -0
  2. package/dist/_chunks/{als-registry-B7DbZ2hS.js → als-registry-Ba7URUIn.js} +1 -1
  3. package/dist/_chunks/als-registry-Ba7URUIn.js.map +1 -0
  4. package/dist/_chunks/chunk-DYhsFzuS.js +33 -0
  5. package/dist/_chunks/debug-ECi_61pb.js +108 -0
  6. package/dist/_chunks/debug-ECi_61pb.js.map +1 -0
  7. package/dist/_chunks/define-cookie-BmKbSyp0.js +93 -0
  8. package/dist/_chunks/define-cookie-BmKbSyp0.js.map +1 -0
  9. package/dist/_chunks/error-boundary-BAN3751q.js +211 -0
  10. package/dist/_chunks/error-boundary-BAN3751q.js.map +1 -0
  11. package/dist/_chunks/{format-CwdaB0_2.js → format-cX7wzEp2.js} +2 -2
  12. package/dist/_chunks/{format-CwdaB0_2.js.map → format-cX7wzEp2.js.map} +1 -1
  13. package/dist/_chunks/{interception-BOoWmLUA.js → interception-D2djYaIm.js} +112 -77
  14. package/dist/_chunks/interception-D2djYaIm.js.map +1 -0
  15. package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-BU684ls2.js} +1 -1
  16. package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js.map → metadata-routes-BU684ls2.js.map} +1 -1
  17. package/dist/_chunks/{request-context-CZJi4CuK.js → request-context-BxYIJM24.js} +93 -69
  18. package/dist/_chunks/request-context-BxYIJM24.js.map +1 -0
  19. package/dist/_chunks/segment-context-C6byCyZU.js +69 -0
  20. package/dist/_chunks/segment-context-C6byCyZU.js.map +1 -0
  21. package/dist/_chunks/stale-reload-C0ValzG7.js +47 -0
  22. package/dist/_chunks/stale-reload-C0ValzG7.js.map +1 -0
  23. package/dist/_chunks/{tracing-Cwn7697K.js → tracing-CuXiCP5p.js} +17 -3
  24. package/dist/_chunks/{tracing-Cwn7697K.js.map → tracing-CuXiCP5p.js.map} +1 -1
  25. package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-BvW0TKDn.js} +1 -1
  26. package/dist/_chunks/{use-query-states-D5KaffOK.js.map → use-query-states-BvW0TKDn.js.map} +1 -1
  27. package/dist/_chunks/wrappers-C6J0nNji.js +331 -0
  28. package/dist/_chunks/wrappers-C6J0nNji.js.map +1 -0
  29. package/dist/adapters/compress-module.d.ts.map +1 -1
  30. package/dist/adapters/nitro.d.ts +17 -1
  31. package/dist/adapters/nitro.d.ts.map +1 -1
  32. package/dist/adapters/nitro.js +56 -13
  33. package/dist/adapters/nitro.js.map +1 -1
  34. package/dist/cache/fast-hash.d.ts +22 -0
  35. package/dist/cache/fast-hash.d.ts.map +1 -0
  36. package/dist/cache/index.d.ts +5 -2
  37. package/dist/cache/index.d.ts.map +1 -1
  38. package/dist/cache/index.js +88 -18
  39. package/dist/cache/index.js.map +1 -1
  40. package/dist/cache/register-cached-function.d.ts.map +1 -1
  41. package/dist/cache/singleflight.d.ts +18 -1
  42. package/dist/cache/singleflight.d.ts.map +1 -1
  43. package/dist/cache/timber-cache.d.ts.map +1 -1
  44. package/dist/client/error-boundary.d.ts +10 -1
  45. package/dist/client/error-boundary.d.ts.map +1 -1
  46. package/dist/client/error-boundary.js +1 -125
  47. package/dist/client/index.d.ts +3 -2
  48. package/dist/client/index.d.ts.map +1 -1
  49. package/dist/client/index.js +213 -93
  50. package/dist/client/index.js.map +1 -1
  51. package/dist/client/link.d.ts +22 -8
  52. package/dist/client/link.d.ts.map +1 -1
  53. package/dist/client/navigation-context.d.ts +2 -2
  54. package/dist/client/router.d.ts +25 -3
  55. package/dist/client/router.d.ts.map +1 -1
  56. package/dist/client/rsc-fetch.d.ts +23 -2
  57. package/dist/client/rsc-fetch.d.ts.map +1 -1
  58. package/dist/client/segment-cache.d.ts +1 -1
  59. package/dist/client/segment-cache.d.ts.map +1 -1
  60. package/dist/client/segment-context.d.ts +1 -1
  61. package/dist/client/segment-context.d.ts.map +1 -1
  62. package/dist/client/segment-merger.d.ts.map +1 -1
  63. package/dist/client/stale-reload.d.ts +15 -0
  64. package/dist/client/stale-reload.d.ts.map +1 -1
  65. package/dist/client/top-loader.d.ts +1 -1
  66. package/dist/client/top-loader.d.ts.map +1 -1
  67. package/dist/client/transition-root.d.ts +1 -1
  68. package/dist/client/transition-root.d.ts.map +1 -1
  69. package/dist/client/use-params.d.ts +2 -2
  70. package/dist/client/use-params.d.ts.map +1 -1
  71. package/dist/client/use-query-states.d.ts +1 -1
  72. package/dist/codec.d.ts +21 -0
  73. package/dist/codec.d.ts.map +1 -0
  74. package/dist/cookies/define-cookie.d.ts +33 -12
  75. package/dist/cookies/define-cookie.d.ts.map +1 -1
  76. package/dist/cookies/index.js +1 -83
  77. package/dist/fonts/css.d.ts +1 -0
  78. package/dist/fonts/css.d.ts.map +1 -1
  79. package/dist/fonts/local.d.ts +4 -2
  80. package/dist/fonts/local.d.ts.map +1 -1
  81. package/dist/index.d.ts +112 -35
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +635 -233
  84. package/dist/index.js.map +1 -1
  85. package/dist/params/define.d.ts +76 -0
  86. package/dist/params/define.d.ts.map +1 -0
  87. package/dist/params/index.d.ts +8 -0
  88. package/dist/params/index.d.ts.map +1 -0
  89. package/dist/params/index.js +104 -0
  90. package/dist/params/index.js.map +1 -0
  91. package/dist/plugins/adapter-build.d.ts.map +1 -1
  92. package/dist/plugins/build-manifest.d.ts.map +1 -1
  93. package/dist/plugins/client-chunks.d.ts +32 -0
  94. package/dist/plugins/client-chunks.d.ts.map +1 -0
  95. package/dist/plugins/dev-error-overlay.d.ts +26 -1
  96. package/dist/plugins/dev-error-overlay.d.ts.map +1 -1
  97. package/dist/plugins/entries.d.ts +7 -0
  98. package/dist/plugins/entries.d.ts.map +1 -1
  99. package/dist/plugins/fonts.d.ts +9 -1
  100. package/dist/plugins/fonts.d.ts.map +1 -1
  101. package/dist/plugins/mdx.d.ts +6 -0
  102. package/dist/plugins/mdx.d.ts.map +1 -1
  103. package/dist/plugins/routing.d.ts.map +1 -1
  104. package/dist/plugins/server-bundle.d.ts.map +1 -1
  105. package/dist/plugins/static-build.d.ts.map +1 -1
  106. package/dist/routing/codegen.d.ts +2 -2
  107. package/dist/routing/codegen.d.ts.map +1 -1
  108. package/dist/routing/index.js +1 -1
  109. package/dist/routing/scanner.d.ts.map +1 -1
  110. package/dist/routing/status-file-lint.d.ts +2 -1
  111. package/dist/routing/status-file-lint.d.ts.map +1 -1
  112. package/dist/routing/types.d.ts +6 -4
  113. package/dist/routing/types.d.ts.map +1 -1
  114. package/dist/rsc-runtime/rsc.d.ts +1 -1
  115. package/dist/rsc-runtime/rsc.d.ts.map +1 -1
  116. package/dist/rsc-runtime/ssr.d.ts +12 -0
  117. package/dist/rsc-runtime/ssr.d.ts.map +1 -1
  118. package/dist/search-params/codecs.d.ts +1 -1
  119. package/dist/search-params/define.d.ts +153 -0
  120. package/dist/search-params/define.d.ts.map +1 -0
  121. package/dist/search-params/index.d.ts +4 -5
  122. package/dist/search-params/index.d.ts.map +1 -1
  123. package/dist/search-params/index.js +3 -474
  124. package/dist/search-params/registry.d.ts +1 -1
  125. package/dist/search-params/wrappers.d.ts +53 -0
  126. package/dist/search-params/wrappers.d.ts.map +1 -0
  127. package/dist/server/access-gate.d.ts +4 -0
  128. package/dist/server/access-gate.d.ts.map +1 -1
  129. package/dist/server/action-client.d.ts.map +1 -1
  130. package/dist/server/action-encryption.d.ts +76 -0
  131. package/dist/server/action-encryption.d.ts.map +1 -0
  132. package/dist/server/action-handler.d.ts.map +1 -1
  133. package/dist/server/als-registry.d.ts +18 -4
  134. package/dist/server/als-registry.d.ts.map +1 -1
  135. package/dist/server/build-manifest.d.ts +2 -2
  136. package/dist/server/debug.d.ts +46 -15
  137. package/dist/server/debug.d.ts.map +1 -1
  138. package/dist/server/default-logger.d.ts +22 -0
  139. package/dist/server/default-logger.d.ts.map +1 -0
  140. package/dist/server/deny-renderer.d.ts.map +1 -1
  141. package/dist/server/early-hints.d.ts +13 -5
  142. package/dist/server/early-hints.d.ts.map +1 -1
  143. package/dist/server/error-boundary-wrapper.d.ts +4 -0
  144. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  145. package/dist/server/flight-injection-state.d.ts +78 -0
  146. package/dist/server/flight-injection-state.d.ts.map +1 -0
  147. package/dist/server/flight-scripts.d.ts +39 -0
  148. package/dist/server/flight-scripts.d.ts.map +1 -0
  149. package/dist/server/flush.d.ts.map +1 -1
  150. package/dist/server/form-data.d.ts +29 -0
  151. package/dist/server/form-data.d.ts.map +1 -1
  152. package/dist/server/html-injectors.d.ts +5 -11
  153. package/dist/server/html-injectors.d.ts.map +1 -1
  154. package/dist/server/index.d.ts +4 -2
  155. package/dist/server/index.d.ts.map +1 -1
  156. package/dist/server/index.js +1975 -1649
  157. package/dist/server/index.js.map +1 -1
  158. package/dist/server/logger.d.ts +24 -7
  159. package/dist/server/logger.d.ts.map +1 -1
  160. package/dist/server/node-stream-transforms.d.ts +77 -0
  161. package/dist/server/node-stream-transforms.d.ts.map +1 -0
  162. package/dist/server/pipeline.d.ts +7 -4
  163. package/dist/server/pipeline.d.ts.map +1 -1
  164. package/dist/server/primitives.d.ts +30 -3
  165. package/dist/server/primitives.d.ts.map +1 -1
  166. package/dist/server/render-timeout.d.ts +51 -0
  167. package/dist/server/render-timeout.d.ts.map +1 -0
  168. package/dist/server/request-context.d.ts +65 -38
  169. package/dist/server/request-context.d.ts.map +1 -1
  170. package/dist/server/route-element-builder.d.ts +7 -0
  171. package/dist/server/route-element-builder.d.ts.map +1 -1
  172. package/dist/server/route-handler.d.ts.map +1 -1
  173. package/dist/server/route-matcher.d.ts +2 -2
  174. package/dist/server/route-matcher.d.ts.map +1 -1
  175. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  176. package/dist/server/rsc-entry/helpers.d.ts +46 -3
  177. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  178. package/dist/server/rsc-entry/index.d.ts +6 -1
  179. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  180. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  181. package/dist/server/rsc-entry/rsc-stream.d.ts +9 -0
  182. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  183. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  184. package/dist/server/slot-resolver.d.ts +1 -1
  185. package/dist/server/slot-resolver.d.ts.map +1 -1
  186. package/dist/server/ssr-entry.d.ts +22 -0
  187. package/dist/server/ssr-entry.d.ts.map +1 -1
  188. package/dist/server/ssr-render.d.ts +39 -21
  189. package/dist/server/ssr-render.d.ts.map +1 -1
  190. package/dist/server/tracing.d.ts +10 -0
  191. package/dist/server/tracing.d.ts.map +1 -1
  192. package/dist/server/tree-builder.d.ts +19 -12
  193. package/dist/server/tree-builder.d.ts.map +1 -1
  194. package/dist/server/types.d.ts +1 -3
  195. package/dist/server/types.d.ts.map +1 -1
  196. package/dist/server/version-skew.d.ts +61 -0
  197. package/dist/server/version-skew.d.ts.map +1 -0
  198. package/dist/server/waituntil-bridge.d.ts.map +1 -1
  199. package/dist/shared/merge-search-params.d.ts +22 -0
  200. package/dist/shared/merge-search-params.d.ts.map +1 -0
  201. package/dist/shims/navigation-client.d.ts +1 -1
  202. package/dist/shims/navigation-client.d.ts.map +1 -1
  203. package/dist/shims/navigation.d.ts +1 -1
  204. package/dist/shims/navigation.d.ts.map +1 -1
  205. package/dist/utils/state-machine.d.ts +80 -0
  206. package/dist/utils/state-machine.d.ts.map +1 -0
  207. package/package.json +17 -14
  208. package/src/adapters/compress-module.ts +24 -4
  209. package/src/adapters/nitro.ts +58 -9
  210. package/src/cache/fast-hash.ts +34 -0
  211. package/src/cache/index.ts +5 -2
  212. package/src/cache/register-cached-function.ts +7 -3
  213. package/src/cache/singleflight.ts +62 -4
  214. package/src/cache/timber-cache.ts +34 -26
  215. package/src/cli.ts +0 -0
  216. package/src/client/browser-entry.ts +94 -90
  217. package/src/client/error-boundary.tsx +18 -1
  218. package/src/client/index.ts +10 -1
  219. package/src/client/link.tsx +78 -19
  220. package/src/client/navigation-context.ts +2 -2
  221. package/src/client/router.ts +105 -60
  222. package/src/client/rsc-fetch.ts +63 -2
  223. package/src/client/segment-cache.ts +1 -1
  224. package/src/client/segment-context.ts +6 -1
  225. package/src/client/segment-merger.ts +2 -8
  226. package/src/client/stale-reload.ts +32 -6
  227. package/src/client/top-loader.tsx +10 -9
  228. package/src/client/transition-root.tsx +7 -1
  229. package/src/client/use-params.ts +3 -3
  230. package/src/client/use-query-states.ts +1 -1
  231. package/src/codec.ts +21 -0
  232. package/src/cookies/define-cookie.ts +69 -18
  233. package/src/fonts/css.ts +2 -1
  234. package/src/fonts/local.ts +7 -3
  235. package/src/index.ts +280 -85
  236. package/src/params/define.ts +260 -0
  237. package/src/params/index.ts +28 -0
  238. package/src/plugins/adapter-build.ts +6 -0
  239. package/src/plugins/build-manifest.ts +11 -0
  240. package/src/plugins/client-chunks.ts +65 -0
  241. package/src/plugins/dev-error-overlay.ts +70 -1
  242. package/src/plugins/dev-server.ts +38 -4
  243. package/src/plugins/entries.ts +12 -11
  244. package/src/plugins/fonts.ts +171 -19
  245. package/src/plugins/mdx.ts +9 -5
  246. package/src/plugins/routing.ts +40 -14
  247. package/src/plugins/server-bundle.ts +32 -1
  248. package/src/plugins/shims.ts +1 -1
  249. package/src/plugins/static-build.ts +8 -4
  250. package/src/routing/codegen.ts +109 -88
  251. package/src/routing/scanner.ts +55 -6
  252. package/src/routing/status-file-lint.ts +2 -1
  253. package/src/routing/types.ts +7 -4
  254. package/src/rsc-runtime/rsc.ts +2 -0
  255. package/src/rsc-runtime/ssr.ts +50 -0
  256. package/src/rsc-runtime/vendor-types.d.ts +7 -0
  257. package/src/search-params/codecs.ts +1 -1
  258. package/src/search-params/define.ts +504 -0
  259. package/src/search-params/index.ts +12 -18
  260. package/src/search-params/registry.ts +1 -1
  261. package/src/search-params/wrappers.ts +85 -0
  262. package/src/server/access-gate.tsx +40 -9
  263. package/src/server/action-client.ts +14 -5
  264. package/src/server/action-encryption.ts +144 -0
  265. package/src/server/action-handler.ts +19 -2
  266. package/src/server/als-registry.ts +18 -4
  267. package/src/server/build-manifest.ts +4 -4
  268. package/src/server/compress.ts +25 -7
  269. package/src/server/debug.ts +55 -17
  270. package/src/server/default-logger.ts +98 -0
  271. package/src/server/deny-renderer.ts +2 -1
  272. package/src/server/early-hints.ts +36 -15
  273. package/src/server/error-boundary-wrapper.ts +57 -14
  274. package/src/server/flight-injection-state.ts +152 -0
  275. package/src/server/flight-scripts.ts +59 -0
  276. package/src/server/flush.ts +2 -1
  277. package/src/server/form-data.ts +76 -0
  278. package/src/server/html-injectors.ts +103 -66
  279. package/src/server/index.ts +9 -4
  280. package/src/server/logger.ts +38 -35
  281. package/src/server/node-stream-transforms.ts +381 -0
  282. package/src/server/pipeline.ts +131 -39
  283. package/src/server/primitives.ts +47 -5
  284. package/src/server/render-timeout.ts +108 -0
  285. package/src/server/request-context.ts +112 -119
  286. package/src/server/route-element-builder.ts +106 -114
  287. package/src/server/route-handler.ts +2 -1
  288. package/src/server/route-matcher.ts +2 -2
  289. package/src/server/rsc-entry/error-renderer.ts +5 -3
  290. package/src/server/rsc-entry/helpers.ts +122 -3
  291. package/src/server/rsc-entry/index.ts +125 -49
  292. package/src/server/rsc-entry/rsc-payload.ts +52 -12
  293. package/src/server/rsc-entry/rsc-stream.ts +33 -8
  294. package/src/server/rsc-entry/ssr-renderer.ts +40 -13
  295. package/src/server/slot-resolver.ts +199 -210
  296. package/src/server/ssr-entry.ts +169 -17
  297. package/src/server/ssr-render.ts +266 -67
  298. package/src/server/tracing.ts +23 -0
  299. package/src/server/tree-builder.ts +91 -57
  300. package/src/server/types.ts +1 -3
  301. package/src/server/version-skew.ts +104 -0
  302. package/src/server/waituntil-bridge.ts +4 -1
  303. package/src/shared/merge-search-params.ts +48 -0
  304. package/src/shims/navigation-client.ts +1 -1
  305. package/src/shims/navigation.ts +1 -1
  306. package/src/utils/state-machine.ts +111 -0
  307. package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
  308. package/dist/_chunks/debug-B4WUeqJ-.js +0 -75
  309. package/dist/_chunks/debug-B4WUeqJ-.js.map +0 -1
  310. package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
  311. package/dist/_chunks/request-context-CZJi4CuK.js.map +0 -1
  312. package/dist/_chunks/ssr-data-MjmprTmO.js +0 -88
  313. package/dist/_chunks/ssr-data-MjmprTmO.js.map +0 -1
  314. package/dist/_chunks/use-cookie-DX-l1_5E.js +0 -91
  315. package/dist/_chunks/use-cookie-DX-l1_5E.js.map +0 -1
  316. package/dist/client/error-boundary.js.map +0 -1
  317. package/dist/cookies/index.js.map +0 -1
  318. package/dist/plugins/dynamic-transform.d.ts +0 -72
  319. package/dist/plugins/dynamic-transform.d.ts.map +0 -1
  320. package/dist/search-params/analyze.d.ts +0 -54
  321. package/dist/search-params/analyze.d.ts.map +0 -1
  322. package/dist/search-params/builtin-codecs.d.ts +0 -105
  323. package/dist/search-params/builtin-codecs.d.ts.map +0 -1
  324. package/dist/search-params/create.d.ts +0 -106
  325. package/dist/search-params/create.d.ts.map +0 -1
  326. package/dist/search-params/index.js.map +0 -1
  327. package/dist/server/prerender.d.ts +0 -77
  328. package/dist/server/prerender.d.ts.map +0 -1
  329. package/dist/server/response-cache.d.ts +0 -53
  330. package/dist/server/response-cache.d.ts.map +0 -1
  331. package/src/plugins/dynamic-transform.ts +0 -161
  332. package/src/search-params/analyze.ts +0 -192
  333. package/src/search-params/builtin-codecs.ts +0 -228
  334. package/src/search-params/create.ts +0 -321
  335. package/src/server/prerender.ts +0 -139
  336. package/src/server/response-cache.ts +0 -277
package/dist/index.js CHANGED
@@ -1,37 +1,16 @@
1
- import { r as setViteServer, t as formatSize } from "./_chunks/format-CwdaB0_2.js";
2
- import { i as scanRoutes, n as generateRouteMap, t as collectInterceptionRewrites } from "./_chunks/interception-BOoWmLUA.js";
1
+ import { r as __toESM, t as __commonJSMin } from "./_chunks/chunk-DYhsFzuS.js";
2
+ import { r as setViteServer, t as formatSize } from "./_chunks/format-cX7wzEp2.js";
3
+ import { i as scanRoutes, n as generateRouteMap, t as collectInterceptionRewrites } from "./_chunks/interception-D2djYaIm.js";
3
4
  import { existsSync, readFileSync } from "node:fs";
4
- import { dirname, extname, join, resolve } from "node:path";
5
- import { fileURLToPath, pathToFileURL } from "node:url";
5
+ import { dirname, extname, join, normalize, resolve } from "node:path";
6
6
  import { createRequire } from "node:module";
7
- import react from "@vitejs/plugin-react";
7
+ import react, { reactCompilerPreset } from "@vitejs/plugin-react";
8
+ import { constants, createGzip, gzipSync } from "node:zlib";
9
+ import { Readable } from "node:stream";
10
+ import { fileURLToPath } from "node:url";
8
11
  import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
9
- import { createHash } from "node:crypto";
10
- import { gzipSync } from "node:zlib";
12
+ import { createHash, randomUUID } from "node:crypto";
11
13
  import { performance as performance$1 } from "node:perf_hooks";
12
- //#region \0rolldown/runtime.js
13
- var __create = Object.create;
14
- var __defProp = Object.defineProperty;
15
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
16
- var __getOwnPropNames = Object.getOwnPropertyNames;
17
- var __getProtoOf = Object.getPrototypeOf;
18
- var __hasOwnProp = Object.prototype.hasOwnProperty;
19
- var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
20
- var __copyProps = (to, from, except, desc) => {
21
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
22
- key = keys[i];
23
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
24
- get: ((k) => from[k]).bind(null, key),
25
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
26
- });
27
- }
28
- return to;
29
- };
30
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
31
- value: mod,
32
- enumerable: true
33
- }) : target, mod));
34
- //#endregion
35
14
  //#region ../../node_modules/.pnpm/acorn@8.16.0/node_modules/acorn/dist/acorn.mjs
36
15
  var astralIdentifierCodes = [
37
16
  509,
@@ -11486,14 +11465,51 @@ function formatTerminalError(error, phase, projectRoot) {
11486
11465
  return lines.join("\n");
11487
11466
  }
11488
11467
  /**
11468
+ * Format RSC debug component info into a readable string for the overlay.
11469
+ *
11470
+ * Renders the server component tree that was active when an error occurred,
11471
+ * including component names and source locations from stack frames. This
11472
+ * gives developers visibility into which server components were rendering
11473
+ * without exposing source code.
11474
+ *
11475
+ * Returns an empty string if no components are provided.
11476
+ */
11477
+ function formatRscDebugContext(components) {
11478
+ if (!components || components.length === 0) return "";
11479
+ const seen = /* @__PURE__ */ new Set();
11480
+ const unique = [];
11481
+ for (const c of components) if (!seen.has(c.name)) {
11482
+ seen.add(c.name);
11483
+ unique.push(c);
11484
+ }
11485
+ const lines = ["Server Component Tree:"];
11486
+ for (let i = 0; i < unique.length; i++) {
11487
+ const c = unique[i];
11488
+ const indent = " ".repeat(i + 1);
11489
+ const envLabel = c.env ? ` [${c.env}]` : "";
11490
+ let locStr = "";
11491
+ if (c.stack && c.stack.length > 0) {
11492
+ const frame = c.stack[0];
11493
+ if (Array.isArray(frame) && frame.length >= 3) locStr = ` (${frame[1]}:${frame[2]})`;
11494
+ }
11495
+ lines.push(`${indent}${c.name}${envLabel}${locStr}`);
11496
+ }
11497
+ return lines.join("\n");
11498
+ }
11499
+ /**
11489
11500
  * Send an error to Vite's browser overlay and log it to stderr.
11490
11501
  *
11491
11502
  * Uses `server.ssrFixStacktrace()` to map stack traces back to source,
11492
11503
  * then sends the error via `server.hot.send()` for the browser overlay.
11493
11504
  *
11505
+ * When `rscDebugComponents` is provided (dev mode only), the server
11506
+ * component tree context is appended to the error message. This helps
11507
+ * developers identify which server component caused the error without
11508
+ * exposing source code.
11509
+ *
11494
11510
  * The dev server remains running — errors are handled, not fatal.
11495
11511
  */
11496
- function sendErrorToOverlay(server, error, phase, projectRoot) {
11512
+ function sendErrorToOverlay(server, error, phase, projectRoot, rscDebugComponents) {
11497
11513
  server.ssrFixStacktrace(error);
11498
11514
  const formatted = formatTerminalError(error, phase, projectRoot);
11499
11515
  process.stderr.write(`${formatted}\n`);
@@ -11501,6 +11517,8 @@ function sendErrorToOverlay(server, error, phase, projectRoot) {
11501
11517
  const componentStack = extractComponentStack(error);
11502
11518
  let message = error.message;
11503
11519
  if (componentStack) message = `${error.message}\n\nComponent Stack:\n${componentStack.trim()}`;
11520
+ const debugContext = formatRscDebugContext(rscDebugComponents ?? []);
11521
+ if (debugContext) message = `${message}\n\n${debugContext}`;
11504
11522
  try {
11505
11523
  server.hot.send({
11506
11524
  type: "error",
@@ -11624,12 +11642,16 @@ function compressResponse(request, response) {
11624
11642
  });
11625
11643
  }
11626
11644
  /**
11627
- * Compress a ReadableStream with gzip using the Web Platform CompressionStream API.
11628
- * Available in Node 18+, Bun, and Deno — no npm dependency needed.
11645
+ * Compress a ReadableStream with gzip, flushing each chunk immediately.
11646
+ *
11647
+ * Uses node:zlib's createGzip with Z_SYNC_FLUSH to ensure each HTML chunk
11648
+ * (shell, Suspense resolution, RSC payload) is delivered to the browser
11649
+ * as soon as it's available — preserving streaming semantics.
11629
11650
  */
11630
11651
  function compressWithGzip(body) {
11631
- const compressionStream = new CompressionStream("gzip");
11632
- return body.pipeThrough(compressionStream);
11652
+ const gzip = createGzip({ flush: constants.Z_SYNC_FLUSH });
11653
+ Readable.fromWeb(body).pipe(gzip);
11654
+ return Readable.toWeb(gzip);
11633
11655
  }
11634
11656
  //#endregion
11635
11657
  //#region src/plugins/dev-server.ts
@@ -11714,8 +11736,8 @@ function createTimberMiddleware(server, projectRoot) {
11714
11736
  const rscModule = await rscEnv.runner.import(RSC_ENTRY_ID);
11715
11737
  handler = rscModule.default;
11716
11738
  const setHandler = rscModule.setDevPipelineErrorHandler;
11717
- if (typeof setHandler === "function") setHandler((error) => {
11718
- sendErrorToOverlay(server, error, classifyErrorPhase(error, projectRoot), projectRoot);
11739
+ if (typeof setHandler === "function") setHandler((error, _phase, debugComponents) => {
11740
+ sendErrorToOverlay(server, error, classifyErrorPhase(error, projectRoot), projectRoot, debugComponents);
11719
11741
  });
11720
11742
  } catch (error) {
11721
11743
  if (error instanceof Error) sendErrorToOverlay(server, error, "module-transform", projectRoot);
@@ -11804,15 +11826,24 @@ async function sendWebResponse(nodeRes, webResponse) {
11804
11826
  }
11805
11827
  nodeRes.flushHeaders();
11806
11828
  const reader = webResponse.body.getReader();
11829
+ let clientDisconnected = false;
11830
+ const onClose = () => {
11831
+ clientDisconnected = true;
11832
+ reader.cancel("Client disconnected").catch(() => {});
11833
+ };
11834
+ nodeRes.on("close", onClose);
11807
11835
  try {
11808
11836
  while (true) {
11809
11837
  const { done, value } = await reader.read();
11810
11838
  if (done) break;
11811
11839
  nodeRes.write(value);
11812
11840
  }
11841
+ } catch (err) {
11842
+ if (!clientDisconnected) throw err;
11813
11843
  } finally {
11844
+ nodeRes.off("close", onClose);
11814
11845
  reader.releaseLock();
11815
- nodeRes.end();
11846
+ if (!nodeRes.writableEnded) nodeRes.end();
11816
11847
  }
11817
11848
  }
11818
11849
  /**
@@ -11923,7 +11954,6 @@ function stripRootPrefix(id, root) {
11923
11954
  * Serializes output mode and feature flags for runtime consumption.
11924
11955
  */
11925
11956
  function generateConfigModule(ctx) {
11926
- const cookieSecrets = ctx.config.cookies?.secrets ?? (ctx.config.cookies?.secret ? [ctx.config.cookies.secret] : void 0);
11927
11957
  const runtimeConfig = {
11928
11958
  output: ctx.config.output ?? "server",
11929
11959
  csrf: ctx.config.csrf ?? true,
@@ -11932,10 +11962,11 @@ function generateConfigModule(ctx) {
11932
11962
  dev: ctx.dev ?? false,
11933
11963
  slowPhaseMs: ctx.config.dev?.slowPhaseMs ?? 200,
11934
11964
  slowRequestMs: ctx.config.slowRequestMs ?? 3e3,
11935
- cookieSecrets,
11936
11965
  topLoader: ctx.config.topLoader,
11937
- responseCache: ctx.config.responseCache,
11938
- debug: ctx.config.debug ?? false
11966
+ debug: ctx.config.debug ?? false,
11967
+ serverTiming: ctx.config.serverTiming,
11968
+ renderTimeoutMs: ctx.config.renderTimeoutMs ?? 3e4,
11969
+ deploymentId: ctx.deploymentId ?? null
11939
11970
  };
11940
11971
  return [
11941
11972
  "// Auto-generated runtime config — do not edit.",
@@ -11953,12 +11984,14 @@ function generateConfigModule(ctx) {
11953
11984
  * extensions as timber.config.ts detection.
11954
11985
  */
11955
11986
  function detectInstrumentationFile(root) {
11956
- for (const ext of [
11987
+ const extensions = [
11957
11988
  ".ts",
11958
11989
  ".js",
11959
11990
  ".mjs"
11960
- ]) {
11961
- const candidate = resolve(root, `instrumentation${ext}`);
11991
+ ];
11992
+ const dirs = [root, resolve(root, "src")];
11993
+ for (const dir of dirs) for (const ext of extensions) {
11994
+ const candidate = resolve(dir, `instrumentation${ext}`);
11962
11995
  if (existsSync(candidate)) return candidate;
11963
11996
  }
11964
11997
  return null;
@@ -12018,17 +12051,20 @@ function timberEntries(ctx) {
12018
12051
  //#region src/plugins/mdx.ts
12019
12052
  var MDX_EXTENSIONS = ["mdx", "md"];
12020
12053
  /**
12021
- * Check if mdx-components.tsx (or .ts, .jsx, .js) exists at the project root.
12054
+ * Check if mdx-components.tsx (or .ts, .jsx, .js) exists at the project root
12055
+ * or in src/. Root takes precedence, matching Next.js behavior.
12022
12056
  * Returns the absolute path if found, otherwise undefined.
12023
12057
  */
12024
12058
  function findMdxComponents(root) {
12025
- for (const name of [
12059
+ const candidates = [
12026
12060
  "mdx-components.tsx",
12027
12061
  "mdx-components.ts",
12028
12062
  "mdx-components.jsx",
12029
12063
  "mdx-components.js"
12030
- ]) {
12031
- const p = join(root, name);
12064
+ ];
12065
+ const dirs = [root, join(root, "src")];
12066
+ for (const dir of dirs) for (const name of candidates) {
12067
+ const p = join(dir, name);
12032
12068
  if (existsSync(p)) return p;
12033
12069
  }
12034
12070
  }
@@ -12153,7 +12189,8 @@ var CLIENT_REQUIRED_EXTENSIONS = new Set([
12153
12189
  * that are missing it.
12154
12190
  *
12155
12191
  * MDX and JSON status files are excluded — MDX files are server components
12156
- * by design, and JSON files are data, not components.
12192
+ * by design (pre-rendered as elements via fallbackElement, see TIM-503),
12193
+ * and JSON files are data, not components.
12157
12194
  */
12158
12195
  function lintStatusFileDirectives(tree) {
12159
12196
  const warnings = [];
@@ -12198,7 +12235,7 @@ var RESOLVED_VIRTUAL_ID$1 = `\0${VIRTUAL_MODULE_ID$1}`;
12198
12235
  /**
12199
12236
  * File convention names we track for changes that require manifest regeneration.
12200
12237
  */
12201
- var ROUTE_FILE_PATTERNS = /\/(page|layout|middleware|access|route|error|default|denied|search-params|\d{3}|[45]xx|not-found|forbidden|unauthorized|sitemap|robots|manifest|favicon|icon|opengraph-image|twitter-image|apple-icon)\./;
12238
+ var ROUTE_FILE_PATTERNS = /\/(page|layout|middleware|access|route|error|default|denied|\d{3}|[45]xx|not-found|forbidden|unauthorized|sitemap|robots|manifest|favicon|icon|opengraph-image|twitter-image|apple-icon)\./;
12202
12239
  /**
12203
12240
  * Create the timber-routing Vite plugin.
12204
12241
  *
@@ -12273,14 +12310,40 @@ function timberRouting(ctx) {
12273
12310
  configureServer(devServer) {
12274
12311
  rescan();
12275
12312
  devServer.watcher.add(ctx.appDir);
12276
- const handleFileChange = (filePath) => {
12313
+ /** Snapshot of the last generated manifest, used to detect structural changes. */
12314
+ let lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : "";
12315
+ /**
12316
+ * Handle a route-significant file being added or removed.
12317
+ * Always triggers a full-reload since the route tree structure changed.
12318
+ */
12319
+ const handleStructuralChange = (filePath) => {
12277
12320
  if (!filePath.startsWith(ctx.appDir)) return;
12278
12321
  if (!ROUTE_FILE_PATTERNS.test(filePath)) return;
12279
12322
  rescan();
12323
+ lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : "";
12280
12324
  invalidateManifest(devServer);
12281
12325
  };
12282
- devServer.watcher.on("add", handleFileChange);
12283
- devServer.watcher.on("unlink", handleFileChange);
12326
+ /**
12327
+ * Handle a route file's content changing.
12328
+ *
12329
+ * Most content edits (JSX changes, fixing typos) don't affect route
12330
+ * metadata — Vite's React Fast Refresh handles those via normal HMR.
12331
+ * Only rescan and full-reload when route metadata actually changed
12332
+ * (e.g., searchParams export added/removed, metadata export changed).
12333
+ */
12334
+ const handleContentChange = (filePath) => {
12335
+ if (!filePath.startsWith(ctx.appDir)) return;
12336
+ if (!ROUTE_FILE_PATTERNS.test(filePath)) return;
12337
+ rescan();
12338
+ const newManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : "";
12339
+ if (newManifest !== lastManifest) {
12340
+ lastManifest = newManifest;
12341
+ invalidateManifest(devServer);
12342
+ }
12343
+ };
12344
+ devServer.watcher.on("add", handleStructuralChange);
12345
+ devServer.watcher.on("unlink", handleStructuralChange);
12346
+ devServer.watcher.on("change", handleContentChange);
12284
12347
  }
12285
12348
  };
12286
12349
  }
@@ -12361,10 +12424,6 @@ function generateManifestModule(tree) {
12361
12424
  const v = addImport(node.denied);
12362
12425
  parts.push(`${nextIndent}denied: { load: ${v}, filePath: ${JSON.stringify(node.denied.filePath)} },`);
12363
12426
  }
12364
- if (node.searchParams) {
12365
- const v = addImport(node.searchParams);
12366
- parts.push(`${nextIndent}searchParams: { load: ${v}, filePath: ${JSON.stringify(node.searchParams.filePath)} },`);
12367
- }
12368
12427
  if (node.statusFiles && node.statusFiles.size > 0) {
12369
12428
  const statusEntries = [];
12370
12429
  for (const [code, file] of node.statusFiles) {
@@ -12542,6 +12601,59 @@ export const coerce = stub;
12542
12601
  };
12543
12602
  }
12544
12603
  //#endregion
12604
+ //#region src/fonts/css.ts
12605
+ /**
12606
+ * Generate a single `@font-face` CSS rule from a descriptor.
12607
+ */
12608
+ function generateFontFace(desc) {
12609
+ const lines = [];
12610
+ lines.push("@font-face {");
12611
+ lines.push(` font-family: '${desc.family}';`);
12612
+ lines.push(` src: ${desc.src};`);
12613
+ if (desc.weight) lines.push(` font-weight: ${desc.weight};`);
12614
+ if (desc.style) lines.push(` font-style: ${desc.style};`);
12615
+ if (desc.display) lines.push(` font-display: ${desc.display};`);
12616
+ if (desc.unicodeRange) lines.push(` unicode-range: ${desc.unicodeRange};`);
12617
+ lines.push("}");
12618
+ return lines.join("\n");
12619
+ }
12620
+ /**
12621
+ * Generate multiple `@font-face` rules from an array of descriptors.
12622
+ */
12623
+ function generateFontFaces(descriptors) {
12624
+ return descriptors.map(generateFontFace).join("\n\n");
12625
+ }
12626
+ /**
12627
+ * Generate a scoped CSS class that sets a CSS custom property for the font.
12628
+ *
12629
+ * Example output:
12630
+ * ```css
12631
+ * .timber-font-inter {
12632
+ * --font-sans: 'Inter', 'Inter Fallback', system-ui, sans-serif;
12633
+ * font-family: 'Inter', 'Inter Fallback', system-ui, sans-serif;
12634
+ * }
12635
+ * ```
12636
+ */
12637
+ function generateVariableClass(className, variable, fontFamily) {
12638
+ return `.${className} {\n ${variable}: ${fontFamily};\n font-family: ${fontFamily};\n}`;
12639
+ }
12640
+ /**
12641
+ * Generate a scoped CSS class that applies font-family directly.
12642
+ *
12643
+ * Used when no `variable` is specified — the className applies
12644
+ * the font-family inline instead of through a CSS custom property.
12645
+ *
12646
+ * Example output:
12647
+ * ```css
12648
+ * .timber-font-inter {
12649
+ * font-family: 'Inter', 'Inter Fallback', system-ui, sans-serif;
12650
+ * }
12651
+ * ```
12652
+ */
12653
+ function generateFontFamilyClass(className, fontFamily) {
12654
+ return `.${className} {\n font-family: ${fontFamily};\n}`;
12655
+ }
12656
+ //#endregion
12545
12657
  //#region src/fonts/fallbacks.ts
12546
12658
  /**
12547
12659
  * Lookup table for commonly used Google Fonts.
@@ -12680,6 +12792,26 @@ function getGenericFamily(family) {
12680
12792
  return "sans-serif";
12681
12793
  }
12682
12794
  /**
12795
+ * Generate the full CSS for a size-adjusted fallback font.
12796
+ *
12797
+ * This produces a complete @font-face block with override descriptors
12798
+ * that FontFaceDescriptor doesn't natively support.
12799
+ */
12800
+ function generateFallbackCss(family) {
12801
+ const metrics = FALLBACK_METRICS[family.toLowerCase()];
12802
+ if (!metrics) return null;
12803
+ return [
12804
+ "@font-face {",
12805
+ ` font-family: '${`${family} Fallback`}';`,
12806
+ ` src: local('${metrics.fallbackFont}');`,
12807
+ ` size-adjust: ${metrics.sizeAdjust}%;`,
12808
+ ` ascent-override: ${metrics.ascentOverride}%;`,
12809
+ ` descent-override: ${metrics.descentOverride}%;`,
12810
+ ` line-gap-override: ${metrics.lineGapOverride}%;`,
12811
+ "}"
12812
+ ].join("\n");
12813
+ }
12814
+ /**
12683
12815
  * Check whether fallback metrics are available for a font family.
12684
12816
  */
12685
12817
  function hasFallbackMetrics(family) {
@@ -13028,6 +13160,26 @@ function generateFamilyName(sources) {
13028
13160
  return stem.replace(/[-_]?(Regular|Bold|Italic|Light|Medium|SemiBold|ExtraBold|Thin|Black|Heavy)$/i, "") || stem;
13029
13161
  }
13030
13162
  /**
13163
+ * Generate @font-face descriptors for local font sources.
13164
+ *
13165
+ * Each source entry produces one @font-face rule. The `src` descriptor
13166
+ * uses a `url()` pointing to the served path under `/_timber/fonts/`.
13167
+ * The `urlPrefix` defaults to `/_timber/fonts` — the path used by both
13168
+ * the dev server middleware and the production build output.
13169
+ */
13170
+ function generateLocalFontFaces(family, sources, display, urlPrefix = "/_timber/fonts") {
13171
+ return sources.map((entry) => {
13172
+ const format = inferFontFormat(entry.path);
13173
+ return {
13174
+ family,
13175
+ src: `url('${urlPrefix}/${entry.path.split("/").pop() ?? entry.path}') format('${format}')`,
13176
+ weight: entry.weight,
13177
+ style: entry.style,
13178
+ display
13179
+ };
13180
+ });
13181
+ }
13182
+ /**
13031
13183
  * Build the className for a local font, following the same convention
13032
13184
  * as Google fonts: `timber-font-<lowercase-hyphenated-family>`.
13033
13185
  */
@@ -13263,6 +13415,22 @@ var VIRTUAL_LOCAL = "@timber/fonts/local";
13263
13415
  var RESOLVED_GOOGLE = "\0@timber/fonts/google";
13264
13416
  var RESOLVED_LOCAL = "\0@timber/fonts/local";
13265
13417
  /**
13418
+ * Virtual side-effect module that registers font CSS on globalThis.
13419
+ *
13420
+ * When a file calls localFont() or a Google font function, the transform
13421
+ * hook injects `import 'virtual:timber-font-css-register'` into that file.
13422
+ * This virtual module sets `globalThis.__timber_font_css` with the combined
13423
+ * @font-face CSS. The RSC entry reads it at render time to inline a <style> tag.
13424
+ *
13425
+ * This approach avoids timing issues because:
13426
+ * 1. The font file is in the RSC module graph (imported by layout.tsx)
13427
+ * 2. The side-effect import is added to the font file during transform
13428
+ * 3. When layout.tsx is loaded, fonts.ts runs → side-effect module runs → globalThis is set
13429
+ * 4. RSC entry renders → reads globalThis → inlines <style>
13430
+ */
13431
+ var VIRTUAL_FONT_CSS_REGISTER = "virtual:timber-font-css-register";
13432
+ var RESOLVED_FONT_CSS_REGISTER = "\0virtual:timber-font-css-register";
13433
+ /**
13266
13434
  * Convert a font family name to a PascalCase export name.
13267
13435
  * e.g. "JetBrains Mono" → "JetBrains_Mono"
13268
13436
  */
@@ -13409,6 +13577,35 @@ function generateLocalVirtualModule() {
13409
13577
  ].join("\n");
13410
13578
  }
13411
13579
  /**
13580
+ * Generate CSS for a single extracted font.
13581
+ *
13582
+ * Includes @font-face rules (for local fonts), fallback @font-face,
13583
+ * and the scoped class rule.
13584
+ */
13585
+ function generateFontCss(font) {
13586
+ const cssParts = [];
13587
+ if (font.provider === "local" && font.localSources) {
13588
+ const faceCss = generateFontFaces(generateLocalFontFaces(font.family, font.localSources, font.display));
13589
+ if (faceCss) cssParts.push(faceCss);
13590
+ }
13591
+ const fallbackCss = generateFallbackCss(font.family);
13592
+ if (fallbackCss) cssParts.push(fallbackCss);
13593
+ if (font.variable) cssParts.push(generateVariableClass(font.className, font.variable, font.fontFamily));
13594
+ else cssParts.push(generateFontFamilyClass(font.className, font.fontFamily));
13595
+ return cssParts.join("\n\n");
13596
+ }
13597
+ /**
13598
+ * Generate the CSS output for all extracted fonts.
13599
+ *
13600
+ * Includes @font-face rules for local fonts, fallback @font-face rules,
13601
+ * and scoped classes.
13602
+ */
13603
+ function generateAllFontCss(registry) {
13604
+ const cssParts = [];
13605
+ for (const font of registry.values()) cssParts.push(generateFontCss(font));
13606
+ return cssParts.join("\n\n");
13607
+ }
13608
+ /**
13412
13609
  * Parse the local name used for the default import of `@timber/fonts/local`.
13413
13610
  *
13414
13611
  * Handles:
@@ -13459,15 +13656,63 @@ function timberFonts(ctx) {
13459
13656
  return {
13460
13657
  name: "timber-fonts",
13461
13658
  resolveId(id) {
13462
- if (id === VIRTUAL_GOOGLE) return RESOLVED_GOOGLE;
13463
- if (id === VIRTUAL_LOCAL) return RESOLVED_LOCAL;
13659
+ let cleanId = id.startsWith("\0") ? id.slice(1) : id;
13660
+ if (cleanId.startsWith(ctx.root)) {
13661
+ const stripped = cleanId.slice(ctx.root.length);
13662
+ if (stripped.startsWith("/") || stripped.startsWith("\\")) cleanId = stripped.slice(1);
13663
+ else cleanId = stripped;
13664
+ }
13665
+ if (cleanId === VIRTUAL_GOOGLE) return RESOLVED_GOOGLE;
13666
+ if (cleanId === VIRTUAL_LOCAL) return RESOLVED_LOCAL;
13667
+ if (cleanId === VIRTUAL_FONT_CSS_REGISTER) return RESOLVED_FONT_CSS_REGISTER;
13464
13668
  return null;
13465
13669
  },
13466
13670
  load(id) {
13467
13671
  if (id === RESOLVED_GOOGLE) return generateGoogleVirtualModule(registry);
13468
13672
  if (id === RESOLVED_LOCAL) return generateLocalVirtualModule();
13673
+ if (id === RESOLVED_FONT_CSS_REGISTER) {
13674
+ const css = generateAllFontCss(registry);
13675
+ return `globalThis.__timber_font_css = ${JSON.stringify(css)};`;
13676
+ }
13469
13677
  return null;
13470
13678
  },
13679
+ configureServer(server) {
13680
+ server.middlewares.use((req, res, next) => {
13681
+ const url = req.url;
13682
+ if (!url || !url.startsWith("/_timber/fonts/")) return next();
13683
+ const requestedFilename = url.slice(15);
13684
+ if (requestedFilename.includes("..") || requestedFilename.includes("/")) {
13685
+ res.statusCode = 400;
13686
+ res.end("Bad request");
13687
+ return;
13688
+ }
13689
+ for (const font of registry.values()) {
13690
+ if (font.provider !== "local" || !font.localSources) continue;
13691
+ for (const src of font.localSources) if ((src.path.split("/").pop() ?? "") === requestedFilename) {
13692
+ const absolutePath = normalize(resolve(src.path));
13693
+ if (!existsSync(absolutePath)) {
13694
+ res.statusCode = 404;
13695
+ res.end("Not found");
13696
+ return;
13697
+ }
13698
+ const data = readFileSync(absolutePath);
13699
+ const ext = absolutePath.split(".").pop()?.toLowerCase();
13700
+ res.setHeader("Content-Type", {
13701
+ woff2: "font/woff2",
13702
+ woff: "font/woff",
13703
+ ttf: "font/ttf",
13704
+ otf: "font/otf",
13705
+ eot: "application/vnd.ms-fontopen"
13706
+ }[ext ?? ""] ?? "application/octet-stream");
13707
+ res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
13708
+ res.setHeader("Access-Control-Allow-Origin", "*");
13709
+ res.end(data);
13710
+ return;
13711
+ }
13712
+ }
13713
+ next();
13714
+ });
13715
+ },
13471
13716
  async buildStart() {
13472
13717
  if (ctx.dev) return;
13473
13718
  const googleFonts = [...registry.values()].filter((f) => f.provider === "google");
@@ -13523,10 +13768,13 @@ function timberFonts(ctx) {
13523
13768
  }
13524
13769
  }
13525
13770
  if (hasLocalImport) transformedCode = transformLocalFonts(transformedCode, code, id, registry, this.error.bind(this));
13526
- if (transformedCode !== code) return {
13527
- code: transformedCode,
13528
- map: null
13529
- };
13771
+ if (transformedCode !== code) {
13772
+ if (registry.size > 0) transformedCode = `import '${VIRTUAL_FONT_CSS_REGISTER}';\n` + transformedCode;
13773
+ return {
13774
+ code: transformedCode,
13775
+ map: null
13776
+ };
13777
+ }
13530
13778
  return null;
13531
13779
  },
13532
13780
  generateBundle() {
@@ -13535,6 +13783,23 @@ function timberFonts(ctx) {
13535
13783
  fileName: `_timber/fonts/${cf.hashedFilename}`,
13536
13784
  source: cf.data
13537
13785
  });
13786
+ for (const font of registry.values()) {
13787
+ if (font.provider !== "local" || !font.localSources) continue;
13788
+ for (const src of font.localSources) {
13789
+ const absolutePath = normalize(resolve(src.path));
13790
+ if (!existsSync(absolutePath)) {
13791
+ this.warn(`Local font file not found: ${absolutePath}`);
13792
+ continue;
13793
+ }
13794
+ const basename = src.path.split("/").pop() ?? src.path;
13795
+ const data = readFileSync(absolutePath);
13796
+ this.emitFile({
13797
+ type: "asset",
13798
+ fileName: `_timber/fonts/${basename}`,
13799
+ source: data
13800
+ });
13801
+ }
13802
+ }
13538
13803
  if (!ctx.buildManifest) return;
13539
13804
  const cachedByFamily = /* @__PURE__ */ new Map();
13540
13805
  for (const cf of cachedFonts) {
@@ -13661,16 +13926,14 @@ function validateStaticMode(code, fileId, options) {
13661
13926
  * - transform: Validates source files for static mode violations
13662
13927
  */
13663
13928
  function timberStaticBuild(ctx) {
13664
- const isStatic = ctx.config.output === "static";
13665
- const clientJavascriptDisabled = ctx.clientJavascript.disabled;
13666
13929
  return {
13667
13930
  name: "timber-static-build",
13668
13931
  transform(code, id) {
13669
- if (!isStatic) return null;
13932
+ if (!(ctx.config.output === "static")) return null;
13670
13933
  if (id.includes("node_modules")) return null;
13671
13934
  if (!id.includes("/app/") && !id.startsWith("app/")) return null;
13672
13935
  if (!/\.[jt]sx?$/.test(id)) return null;
13673
- const errors = validateStaticMode(code, id, { clientJavascriptDisabled });
13936
+ const errors = validateStaticMode(code, id, { clientJavascriptDisabled: ctx.clientJavascript.disabled });
13674
13937
  if (errors.length > 0) {
13675
13938
  const messages = errors.map((e) => `[timber] Static mode error in ${e.file}${e.line ? `:${e.line}` : ""}: ${e.message}`);
13676
13939
  this.error(messages.join("\n\n"));
@@ -13680,95 +13943,6 @@ function timberStaticBuild(ctx) {
13680
13943
  };
13681
13944
  }
13682
13945
  //#endregion
13683
- //#region src/plugins/dynamic-transform.ts
13684
- /**
13685
- * Quick check: does this source file contain 'use dynamic' anywhere?
13686
- * Used as a fast bail-out before doing expensive AST parsing.
13687
- */
13688
- function containsUseDynamic(code) {
13689
- return containsDirective(code, "use dynamic");
13690
- }
13691
- /**
13692
- * Find function declarations/expressions containing 'use dynamic' and
13693
- * transform them into markDynamic() calls.
13694
- *
13695
- * Input:
13696
- * ```tsx
13697
- * export default async function AddToCartButton({ productId }) {
13698
- * 'use dynamic'
13699
- * const user = await getUser()
13700
- * return <button>Add to cart</button>
13701
- * }
13702
- * ```
13703
- *
13704
- * Output:
13705
- * ```tsx
13706
- * import { markDynamic as __markDynamic } from '@timber-js/app/runtime';
13707
- * export default async function AddToCartButton({ productId }) {
13708
- * __markDynamic();
13709
- * const user = await getUser()
13710
- * return <button>Add to cart</button>
13711
- * }
13712
- * ```
13713
- *
13714
- * The markDynamic() call registers the component boundary as dynamic
13715
- * at render time. The pre-render pass uses this to know which subtrees
13716
- * to skip and leave as holes for per-request rendering.
13717
- */
13718
- function transformUseDynamic(code) {
13719
- if (!containsUseDynamic(code)) return null;
13720
- const functions = findFunctionsWithDirective(code, "use dynamic");
13721
- if (functions.length === 0) return null;
13722
- let result = code;
13723
- for (const fn of functions) {
13724
- const cleanBody = fn.bodyContent.replace(/['"]use dynamic['"];?/, "__markDynamic();");
13725
- result = result.slice(0, fn.bodyStart) + cleanBody + result.slice(fn.bodyEnd);
13726
- }
13727
- result = `import { markDynamic as __markDynamic } from '@timber-js/app/runtime';\n` + result;
13728
- return {
13729
- code: result,
13730
- map: null
13731
- };
13732
- }
13733
- /**
13734
- * In `output: 'static'` mode, `'use dynamic'` is a build error.
13735
- * Static mode renders everything at build time — there is no per-request
13736
- * rendering to opt into.
13737
- */
13738
- function validateNoDynamicInStaticMode(code) {
13739
- if (!containsUseDynamic(code)) return null;
13740
- const functions = findFunctionsWithDirective(code, "use dynamic");
13741
- if (functions.length === 0) return null;
13742
- return {
13743
- message: "'use dynamic' cannot be used in static mode (output: 'static'). Static mode renders all content at build time — there is no per-request rendering. Remove the directive or switch to output: 'server'.",
13744
- line: functions[functions.length - 1].directiveLine
13745
- };
13746
- }
13747
- /**
13748
- * Create the timber-dynamic-transform Vite plugin.
13749
- *
13750
- * In server mode: transforms 'use dynamic' into markDynamic() calls.
13751
- * In static mode: rejects 'use dynamic' as a build error.
13752
- */
13753
- function timberDynamicTransform(ctx) {
13754
- const isStatic = ctx.config.output === "static";
13755
- return {
13756
- name: "timber-dynamic-transform",
13757
- transform(code, id) {
13758
- if (id.includes("node_modules")) return null;
13759
- if (!id.includes("/app/") && !id.startsWith("app/")) return null;
13760
- if (!/\.[jt]sx?$/.test(id)) return null;
13761
- if (!containsUseDynamic(code)) return null;
13762
- if (isStatic) {
13763
- const error = validateNoDynamicInStaticMode(code);
13764
- if (error) this.error(`[timber] Static mode error in ${id}${error.line ? `:${error.line}` : ""}: ${error.message}`);
13765
- return null;
13766
- }
13767
- return transformUseDynamic(code);
13768
- }
13769
- };
13770
- }
13771
- //#endregion
13772
13946
  //#region src/plugins/server-action-exports.ts
13773
13947
  var jsxParser = Parser.extend((0, import_acorn_jsx.default)());
13774
13948
  /**
@@ -13908,6 +14082,23 @@ function rewriteServerActionExportsFallback(code) {
13908
14082
  }
13909
14083
  //#endregion
13910
14084
  //#region src/plugins/build-manifest.ts
14085
+ /**
14086
+ * timber-build-manifest — Vite sub-plugin for build asset manifest generation.
14087
+ *
14088
+ * Provides `virtual:timber-build-manifest` which exports a BuildManifest
14089
+ * mapping route segment file paths to their CSS, JS, and modulepreload
14090
+ * output chunks.
14091
+ *
14092
+ * - Dev mode: exports an empty manifest (Vite HMR handles CSS/JS).
14093
+ * - Build mode: virtual module reads from globalThis.__TIMBER_BUILD_MANIFEST__
14094
+ * at runtime. The actual manifest data is injected by the adapter via a
14095
+ * _timber-manifest-init.js module that runs before the RSC handler.
14096
+ *
14097
+ * The generateBundle hook (client env only) extracts CSS/JS/modulepreload
14098
+ * data from the Rollup bundle and populates ctx.buildManifest.
14099
+ *
14100
+ * Design docs: 18-build-system.md §"Build Manifest", 02-rendering-pipeline.md §"Early Hints"
14101
+ */
13911
14102
  var VIRTUAL_MODULE_ID = "virtual:timber-build-manifest";
13912
14103
  var RESOLVED_VIRTUAL_ID = `\0${VIRTUAL_MODULE_ID}`;
13913
14104
  /**
@@ -13983,6 +14174,7 @@ function timberBuildManifest(ctx) {
13983
14174
  configResolved(config) {
13984
14175
  resolvedBase = config.base;
13985
14176
  isDev = config.command === "serve";
14177
+ if (!isDev && !ctx.deploymentId) ctx.deploymentId = randomUUID().replace(/-/g, "").slice(0, 16);
13986
14178
  },
13987
14179
  resolveId(id) {
13988
14180
  const cleanId = id.startsWith("\0") ? id.slice(1) : id;
@@ -14253,55 +14445,115 @@ function timberChunks() {
14253
14445
  return { name: "timber-chunks" };
14254
14446
  }
14255
14447
  //#endregion
14448
+ //#region src/plugins/client-chunks.ts
14449
+ /**
14450
+ * Client chunk grouping strategy for @vitejs/plugin-rsc.
14451
+ *
14452
+ * Groups client reference modules by layout boundary to balance route-scoped
14453
+ * code splitting with HTTP request count. A constant group name would collapse
14454
+ * all routes into one chunk (every page downloads every client component).
14455
+ * Per-serverChunk grouping creates many sub-500B files. Layout-boundary
14456
+ * grouping is the middle ground.
14457
+ *
14458
+ * See design/27-chunking-strategy.md, TIM-440, TIM-499.
14459
+ */
14460
+ /**
14461
+ * Derive a chunk group name for a client reference module.
14462
+ *
14463
+ * Groups by the first non-group route segment under appDir so that all
14464
+ * client components belonging to the same layout boundary land in one
14465
+ * chunk. For example:
14466
+ * - `facade:app/dashboard/settings/page.tsx` → `"client-dashboard"`
14467
+ * - `facade:app/(group-a)/group-page-a/page.tsx` → `"client-group-page-a"`
14468
+ * - `facade:app/layout.tsx` (root layout) → `"client-shared"`
14469
+ * - `shared:...` (shared across chunks) → `"client-shared"`
14470
+ *
14471
+ * This balances route-scoped code splitting with HTTP request count:
14472
+ * fewer chunks than per-serverChunk, but still avoids downloading every
14473
+ * client component on every page.
14474
+ */
14475
+ function clientChunkGroup(meta, appDir) {
14476
+ const { normalizedId, serverChunk } = meta;
14477
+ if (serverChunk.startsWith("shared:")) return "client-shared";
14478
+ const relPath = normalizedId.replace(/\\/g, "/");
14479
+ const appDirName = appDir.replace(/\\/g, "/").split("/").pop() || "app";
14480
+ const appIdx = relPath.indexOf(appDirName + "/");
14481
+ if (appIdx === -1) return "client-shared";
14482
+ const segments = relPath.slice(appIdx + appDirName.length + 1).split("/");
14483
+ for (const seg of segments) {
14484
+ if (seg.includes(".")) break;
14485
+ if (seg.startsWith("(") && seg.endsWith(")")) continue;
14486
+ return `client-${seg}`;
14487
+ }
14488
+ return "client-shared";
14489
+ }
14490
+ //#endregion
14256
14491
  //#region src/plugins/server-bundle.ts
14257
14492
  function timberServerBundle() {
14258
- return [{
14259
- name: "timber-server-bundle",
14260
- config(_cfg, { command }) {
14261
- if (command === "serve") return { environments: {
14262
- rsc: { resolve: { noExternal: ["server-only", "client-only"] } },
14263
- ssr: { resolve: { noExternal: [
14264
- "server-only",
14265
- "client-only",
14266
- "nuqs"
14267
- ] } }
14268
- } };
14269
- const serverDefine = { "process.env.NODE_ENV": JSON.stringify("production") };
14270
- return {
14271
- ssr: { target: "webworker" },
14272
- environments: {
14273
- rsc: {
14274
- resolve: { noExternal: true },
14275
- define: serverDefine
14276
- },
14277
- ssr: {
14278
- resolve: { noExternal: true },
14279
- define: serverDefine
14493
+ return [
14494
+ {
14495
+ name: "timber-server-bundle",
14496
+ config(_cfg, { command }) {
14497
+ if (command === "serve") return { environments: {
14498
+ rsc: { resolve: { noExternal: ["server-only", "client-only"] } },
14499
+ ssr: { resolve: { noExternal: [
14500
+ "server-only",
14501
+ "client-only",
14502
+ "nuqs"
14503
+ ] } }
14504
+ } };
14505
+ const serverDefine = { "process.env.NODE_ENV": JSON.stringify("production") };
14506
+ return {
14507
+ ssr: { target: "webworker" },
14508
+ environments: {
14509
+ rsc: {
14510
+ resolve: { noExternal: true },
14511
+ define: serverDefine
14512
+ },
14513
+ ssr: {
14514
+ resolve: { noExternal: true },
14515
+ define: serverDefine
14516
+ }
14280
14517
  }
14281
- }
14282
- };
14283
- }
14284
- }, {
14285
- name: "timber-esm-init-fix",
14286
- applyToEnvironment(environment) {
14287
- return environment.name === "rsc" || environment.name === "ssr";
14518
+ };
14519
+ }
14288
14520
  },
14289
- renderChunk(code) {
14290
- const lazy = "var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);";
14291
- if (!code.includes(lazy)) return null;
14292
- const eager = [
14293
- "var __esmMin = (fn, res) => {",
14294
- " var l = () => { if (fn) { var f = fn; try { res = f(); fn = 0; } catch(e) {} } return res; };",
14295
- " l();",
14296
- " return l;",
14297
- "};"
14298
- ].join(" ");
14299
- return {
14300
- code: code.replace(lazy, eager),
14301
- map: null
14302
- };
14521
+ {
14522
+ name: "timber-esm-init-fix",
14523
+ applyToEnvironment(environment) {
14524
+ return environment.name === "rsc" || environment.name === "ssr";
14525
+ },
14526
+ renderChunk(code) {
14527
+ const lazy = "var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);";
14528
+ if (!code.includes(lazy)) return null;
14529
+ const eager = [
14530
+ "var __esmMin = (fn, res) => {",
14531
+ " var l = () => { if (fn) { var f = fn; try { res = f(); fn = 0; } catch(e) {} } return res; };",
14532
+ " l();",
14533
+ " return l;",
14534
+ "};"
14535
+ ].join(" ");
14536
+ return {
14537
+ code: code.replace(lazy, eager),
14538
+ map: null
14539
+ };
14540
+ }
14541
+ },
14542
+ {
14543
+ name: "timber-create-require-fix",
14544
+ applyToEnvironment(environment) {
14545
+ return environment.name === "rsc" || environment.name === "ssr";
14546
+ },
14547
+ renderChunk(code) {
14548
+ const pattern = "createRequire(import.meta.url)";
14549
+ if (!code.includes(pattern)) return null;
14550
+ return {
14551
+ code: code.replace(pattern, "createRequire(import.meta.url || \"file:///app\")"),
14552
+ map: null
14553
+ };
14554
+ }
14303
14555
  }
14304
- }];
14556
+ ];
14305
14557
  }
14306
14558
  //#endregion
14307
14559
  //#region src/plugins/adapter-build.ts
@@ -14323,6 +14575,7 @@ function timberAdapterBuild(ctx) {
14323
14575
  modulepreload: {}
14324
14576
  } : ctx.buildManifest;
14325
14577
  manifestInit = `globalThis.__TIMBER_BUILD_MANIFEST__ = ${JSON.stringify(manifest)};\n`;
14578
+ if (ctx.deploymentId) manifestInit += `globalThis.__TIMBER_DEPLOYMENT_ID__ = ${JSON.stringify(ctx.deploymentId)};\n`;
14326
14579
  }
14327
14580
  if (ctx.clientJavascript.disabled) await stripJsFromRscAssetsManifests(buildDir);
14328
14581
  const adapterConfig = {
@@ -14679,6 +14932,65 @@ function createNoopTimer() {
14679
14932
  };
14680
14933
  }
14681
14934
  //#endregion
14935
+ //#region src/server/action-encryption.ts
14936
+ /**
14937
+ * Regex for safe `defineEncryptionKey` expressions.
14938
+ *
14939
+ * The RSC plugin inlines this expression verbatim into generated JavaScript.
14940
+ * We restrict it to `process.env.<UPPER_SNAKE_CASE>` to prevent code injection.
14941
+ * See "Known Security Considerations" at the top of this file.
14942
+ */
14943
+ var SAFE_KEY_EXPR = /^process\.env\.[A-Z_][A-Z0-9_]*$/;
14944
+ /**
14945
+ * Build the `defineEncryptionKey` expression for the RSC plugin.
14946
+ *
14947
+ * The RSC plugin accepts a JavaScript expression string that will be
14948
+ * inlined into the encryption runtime module. At runtime, this expression
14949
+ * must evaluate to the base64-encoded encryption key.
14950
+ *
14951
+ * Priority:
14952
+ * 1. `TIMBER_ACTIONS_ENCRYPTION_KEY` env var (for cross-build key sharing
14953
+ * in rolling/blue-green deployments)
14954
+ * 2. Auto-generated at build time (RSC plugin default — embedded in bundle,
14955
+ * consistent across all instances of the same build)
14956
+ *
14957
+ * For env var keys, we generate a runtime expression that reads the env var.
14958
+ * For auto-generated keys, we return undefined and let the RSC plugin handle it.
14959
+ */
14960
+ function resolveEncryptionKeyExpression() {
14961
+ const envKey = process.env.TIMBER_ACTIONS_ENCRYPTION_KEY;
14962
+ if (envKey) {
14963
+ validateKeyFormat(envKey);
14964
+ const expr = "process.env.TIMBER_ACTIONS_ENCRYPTION_KEY";
14965
+ if (!SAFE_KEY_EXPR.test(expr)) throw new Error(`Unsafe encryption key expression: ${expr}`);
14966
+ return expr;
14967
+ }
14968
+ }
14969
+ /**
14970
+ * Determine whether action encryption should be enabled.
14971
+ *
14972
+ * Encryption is always enabled in production. In dev mode, it's enabled
14973
+ * by default but can be disabled via config for debugging.
14974
+ */
14975
+ function shouldEnableEncryption(isDev, config) {
14976
+ if (!isDev) return true;
14977
+ if (config?.disableInDev) return false;
14978
+ return true;
14979
+ }
14980
+ /**
14981
+ * Validate that a key string is a valid base64-encoded 256-bit key.
14982
+ * Throws a descriptive error if the key is malformed.
14983
+ */
14984
+ function validateKeyFormat(key) {
14985
+ try {
14986
+ const bytes = atob(key).length;
14987
+ if (bytes !== 32) throw new Error(`TIMBER_ACTIONS_ENCRYPTION_KEY must be a base64-encoded 256-bit (32-byte) key. Got ${bytes} bytes. Generate one with: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"`);
14988
+ } catch (error) {
14989
+ if (error instanceof Error && error.message.includes("TIMBER_ACTIONS_ENCRYPTION_KEY")) throw error;
14990
+ throw new Error("TIMBER_ACTIONS_ENCRYPTION_KEY is not valid base64. Generate a key with: node -e \"console.log(require('crypto').randomBytes(32).toString('base64'))\"");
14991
+ }
14992
+ }
14993
+ //#endregion
14682
14994
  //#region src/index.ts
14683
14995
  /**
14684
14996
  * Resolve `clientJavascript` into a fully resolved config.
@@ -14721,10 +15033,7 @@ function resolveAppDir(root, configAppDir) {
14721
15033
  }
14722
15034
  function createPluginContext(config, root) {
14723
15035
  const projectRoot = root ?? process.cwd();
14724
- const resolvedConfig = {
14725
- output: "server",
14726
- ...config
14727
- };
15036
+ const resolvedConfig = { ...config };
14728
15037
  return {
14729
15038
  config: resolvedConfig,
14730
15039
  clientJavascript: resolveClientJavascript(resolvedConfig),
@@ -14733,22 +15042,28 @@ function createPluginContext(config, root) {
14733
15042
  root: projectRoot,
14734
15043
  dev: false,
14735
15044
  buildManifest: null,
15045
+ deploymentId: null,
14736
15046
  timer: createStartupTimer()
14737
15047
  };
14738
15048
  }
14739
15049
  /**
14740
15050
  * Load timber.config.ts (or .js, .mjs) from the project root.
14741
15051
  * Returns the config object or null if no config file is found.
15052
+ *
15053
+ * Uses require() which works for ESM modules on Node 22.12+.
15054
+ * This keeps timber() synchronous — no async config loading needed.
14742
15055
  */
14743
- async function loadTimberConfigFile(root) {
14744
- for (const name of [
15056
+ function loadTimberConfigFile(root) {
15057
+ const configNames = [
14745
15058
  "timber.config.ts",
14746
15059
  "timber.config.js",
14747
15060
  "timber.config.mjs"
14748
- ]) {
15061
+ ];
15062
+ const req = createRequire(join(root, "package.json"));
15063
+ for (const name of configNames) {
14749
15064
  const configPath = join(root, name);
14750
15065
  if (existsSync(configPath)) {
14751
- const mod = await import(pathToFileURL(configPath).href);
15066
+ const mod = req(configPath);
14752
15067
  return mod.default ?? mod;
14753
15068
  }
14754
15069
  }
@@ -14763,10 +15078,7 @@ async function loadTimberConfigFile(root) {
14763
15078
  */
14764
15079
  function warnConfigConflicts(inline, fileConfig) {
14765
15080
  const conflicts = [];
14766
- for (const key of Object.keys(fileConfig)) {
14767
- if (key === "output") continue;
14768
- if (key in inline && inline[key] !== void 0) conflicts.push(key);
14769
- }
15081
+ for (const key of Object.keys(fileConfig)) if (key in inline && inline[key] !== void 0) conflicts.push(key);
14770
15082
  if (conflicts.length > 0) console.warn(`[timber] Config conflict: ${conflicts.map((k) => `"${k}"`).join(", ")} set in both vite.config.ts (inline) and timber.config.ts. Move all config to timber.config.ts to avoid confusion. The inline value from vite.config.ts will be used.`);
14771
15083
  return conflicts;
14772
15084
  }
@@ -14794,21 +15106,92 @@ function mergeFileConfig(ctx, fileConfig) {
14794
15106
  } } : {}
14795
15107
  };
14796
15108
  }
15109
+ /**
15110
+ * Resolve the React Compiler plugin via @rolldown/plugin-babel.
15111
+ *
15112
+ * Uses the `reactCompilerPreset` from @vitejs/plugin-react, which:
15113
+ * - Uses Babel ONLY for the compiler pass (OXC handles JSX)
15114
+ * - Automatically scopes to client environment via applyToEnvironmentHook
15115
+ * - Uses react/compiler-runtime built into React 19
15116
+ *
15117
+ * @rolldown/plugin-babel and babel-plugin-react-compiler are optional peer deps.
15118
+ * If either is missing, require() fails with a clear error message.
15119
+ */
15120
+ function resolveReactCompilerPlugin(config, req) {
15121
+ let babel;
15122
+ try {
15123
+ babel = req("@rolldown/plugin-babel");
15124
+ } catch {
15125
+ throw new Error("[timber] reactCompiler requires @rolldown/plugin-babel. Install it: pnpm add -D @rolldown/plugin-babel babel-plugin-react-compiler");
15126
+ }
15127
+ const options = typeof config === "object" ? config : {};
15128
+ return (babel.default ?? babel)({ presets: [reactCompilerPreset(options)] });
15129
+ }
15130
+ /**
15131
+ * Build the options object for @vitejs/plugin-rsc.
15132
+ *
15133
+ * Uses a getter for `enableActionEncryption` so the RSC plugin reads
15134
+ * the value lazily — after ctx.dev is set in configResolved. This lets
15135
+ * `actionEncryption.disableInDev` work correctly even though the RSC
15136
+ * plugin is created before Vite resolves the command.
15137
+ */
15138
+ function createRscOptions(ctx, encryptionKeyExpr) {
15139
+ const options = {
15140
+ serverHandler: false,
15141
+ customClientEntry: true,
15142
+ entries: {
15143
+ rsc: "virtual:timber-rsc-entry",
15144
+ ssr: "virtual:timber-ssr-entry",
15145
+ client: "virtual:timber-browser-entry"
15146
+ },
15147
+ clientChunks: (meta) => clientChunkGroup(meta, ctx.appDir)
15148
+ };
15149
+ Object.defineProperty(options, "enableActionEncryption", {
15150
+ get() {
15151
+ return shouldEnableEncryption(ctx.dev, ctx.config.actionEncryption);
15152
+ },
15153
+ enumerable: true
15154
+ });
15155
+ if (encryptionKeyExpr) options.defineEncryptionKey = encryptionKeyExpr;
15156
+ return options;
15157
+ }
14797
15158
  function timberCache(_ctx) {
14798
15159
  return cacheTransformPlugin();
14799
15160
  }
15161
+ /**
15162
+ * Create the timber Vite plugin array.
15163
+ *
15164
+ * Loads timber.config.ts and all dependencies synchronously before
15165
+ * constructing the plugin array. This ensures ALL plugins — including
15166
+ * the RSC plugin and React Compiler — see the fully merged config
15167
+ * (inline + file-based). No async, no deferred config, no stale reads.
15168
+ *
15169
+ * Requires Node >= 22.12 for synchronous require() of ESM modules
15170
+ * (@vitejs/plugin-rsc is ESM-only).
15171
+ *
15172
+ * Previous versions used async loading and deferred config merging,
15173
+ * causing file-based config for reactCompiler, actionEncryption, and
15174
+ * output mode to be silently ignored. See TIM-451.
15175
+ */
14800
15176
  function timber(config) {
14801
15177
  const ctx = createPluginContext(config);
15178
+ const consumerRequire = createRequire(join(process.cwd(), "package.json"));
15179
+ ctx.timer.start("rsc-plugin-import");
15180
+ const rscMod = consumerRequire("@vitejs/plugin-rsc");
15181
+ const vitePluginRsc = rscMod.default ?? rscMod;
15182
+ ctx.timer.end("rsc-plugin-import");
15183
+ const encryptionKeyExpr = resolveEncryptionKeyExpression();
14802
15184
  const rootSync = {
14803
15185
  name: "timber-root-sync",
14804
- async config(userConfig, { command }) {
14805
- const root = userConfig.root ?? process.cwd();
15186
+ config(userConfig, { command }) {
15187
+ const viteRoot = resolve(userConfig.root ?? process.cwd());
14806
15188
  ctx.timer.start("config-load");
14807
- const fileConfig = await loadTimberConfigFile(root);
15189
+ const fileConfig = loadTimberConfigFile(viteRoot);
14808
15190
  if (fileConfig) {
14809
15191
  mergeFileConfig(ctx, fileConfig);
14810
15192
  ctx.clientJavascript = resolveClientJavascript(ctx.config);
14811
15193
  }
15194
+ ctx.config.output ??= "server";
14812
15195
  ctx.timer.end("config-load");
14813
15196
  if (command === "build") return { oxc: { jsx: { development: false } } };
14814
15197
  },
@@ -14820,33 +15203,31 @@ function timber(config) {
14820
15203
  else ctx.timer.start("dev-server-setup");
14821
15204
  }
14822
15205
  };
14823
- const rscPluginPath = createRequire(join(process.cwd(), "package.json")).resolve("@vitejs/plugin-rsc");
14824
- ctx.timer.start("rsc-plugin-import");
14825
- const rscPluginsPromise = import(pathToFileURL(rscPluginPath).href).then(({ default: vitePluginRsc }) => {
14826
- ctx.timer.end("rsc-plugin-import");
14827
- return vitePluginRsc({
14828
- serverHandler: false,
14829
- customClientEntry: true,
14830
- entries: {
14831
- rsc: "virtual:timber-rsc-entry",
14832
- ssr: "virtual:timber-ssr-entry",
14833
- client: "virtual:timber-browser-entry"
14834
- }
14835
- });
14836
- });
15206
+ const reactCompilerPlugins = [];
15207
+ if (config?.reactCompiler) reactCompilerPlugins.push(resolveReactCompilerPlugin(config.reactCompiler, consumerRequire));
15208
+ const lazyReactCompiler = {
15209
+ name: "timber-react-compiler",
15210
+ configResolved() {
15211
+ if (config?.reactCompiler || !ctx.config.reactCompiler) return;
15212
+ const resolved = resolveReactCompilerPlugin(ctx.config.reactCompiler, consumerRequire);
15213
+ for (const key of Object.keys(resolved)) if (key !== "name") lazyReactCompiler[key] = resolved[key];
15214
+ }
15215
+ };
15216
+ // @vitejs/plugin-rsc handles:
14837
15217
  return [
14838
15218
  rootSync,
14839
15219
  timberReactProd(),
14840
15220
  react(),
15221
+ ...reactCompilerPlugins,
15222
+ lazyReactCompiler,
14841
15223
  timberServerActionExports(),
14842
- rscPluginsPromise,
15224
+ vitePluginRsc(createRscOptions(ctx, encryptionKeyExpr)),
14843
15225
  timberShims(ctx),
14844
15226
  timberRouting(ctx),
14845
15227
  timberEntries(ctx),
14846
15228
  timberBuildManifest(ctx),
14847
15229
  timberCache(ctx),
14848
15230
  timberStaticBuild(ctx),
14849
- timberDynamicTransform(ctx),
14850
15231
  timberFonts(ctx),
14851
15232
  timberMdx(ctx),
14852
15233
  timberContent(ctx),
@@ -14858,7 +15239,28 @@ function timber(config) {
14858
15239
  timberDevServer(ctx)
14859
15240
  ];
14860
15241
  }
15242
+ /**
15243
+ * Type-safe helper for timber.config.ts files.
15244
+ *
15245
+ * A pass-through identity function that provides autocomplete and
15246
+ * type checking for timber configuration. No runtime validation —
15247
+ * purely a DX convenience (same pattern as Vite's defineConfig).
15248
+ *
15249
+ * @example
15250
+ * ```ts
15251
+ * // timber.config.ts
15252
+ * import { defineConfig } from '@timber-js/app';
15253
+ *
15254
+ * export default defineConfig({
15255
+ * output: 'server',
15256
+ * pageExtensions: ['tsx', 'ts', 'mdx'],
15257
+ * });
15258
+ * ```
15259
+ */
15260
+ function defineConfig(config) {
15261
+ return config;
15262
+ }
14861
15263
  //#endregion
14862
- export { timber as default, timber, resolveAppDir, resolveClientJavascript, warnConfigConflicts };
15264
+ export { timber as default, timber, defineConfig, loadTimberConfigFile, resolveAppDir, resolveClientJavascript, warnConfigConflicts };
14863
15265
 
14864
15266
  //# sourceMappingURL=index.js.map