@timber-js/app 0.2.0-alpha.6 → 0.2.0-alpha.61

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 (406) 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-gwlJkDuf.js → debug-ECi_61pb.js} +2 -2
  6. package/dist/_chunks/debug-ECi_61pb.js.map +1 -0
  7. package/dist/_chunks/define-CT98cU9c.js +121 -0
  8. package/dist/_chunks/define-CT98cU9c.js.map +1 -0
  9. package/dist/_chunks/define-TK8C1M3x.js +279 -0
  10. package/dist/_chunks/define-TK8C1M3x.js.map +1 -0
  11. package/dist/_chunks/define-cookie-BWr_52kY.js +93 -0
  12. package/dist/_chunks/define-cookie-BWr_52kY.js.map +1 -0
  13. package/dist/_chunks/error-boundary-DpZJBCqh.js +211 -0
  14. package/dist/_chunks/error-boundary-DpZJBCqh.js.map +1 -0
  15. package/dist/_chunks/{format-DviM89f0.js → format-cX7wzEp2.js} +2 -2
  16. package/dist/_chunks/{format-DviM89f0.js.map → format-cX7wzEp2.js.map} +1 -1
  17. package/dist/_chunks/{interception-BOoWmLUA.js → interception-Cey5DCGr.js} +129 -77
  18. package/dist/_chunks/interception-Cey5DCGr.js.map +1 -0
  19. package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-BU684ls2.js} +1 -1
  20. package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js.map → metadata-routes-BU684ls2.js.map} +1 -1
  21. package/dist/_chunks/{request-context-DIkVh_jG.js → request-context-rju2rbga.js} +97 -69
  22. package/dist/_chunks/request-context-rju2rbga.js.map +1 -0
  23. package/dist/_chunks/segment-context-CyaM1mrD.js +72 -0
  24. package/dist/_chunks/segment-context-CyaM1mrD.js.map +1 -0
  25. package/dist/_chunks/stale-reload-BSSym1MJ.js +64 -0
  26. package/dist/_chunks/stale-reload-BSSym1MJ.js.map +1 -0
  27. package/dist/_chunks/{tracing-Cwn7697K.js → tracing-VYETCQsg.js} +17 -3
  28. package/dist/_chunks/{tracing-Cwn7697K.js.map → tracing-VYETCQsg.js.map} +1 -1
  29. package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-wEXY2JQB.js} +1 -1
  30. package/dist/_chunks/use-query-states-wEXY2JQB.js.map +1 -0
  31. package/dist/_chunks/wrappers-BaG1bnM3.js +63 -0
  32. package/dist/_chunks/wrappers-BaG1bnM3.js.map +1 -0
  33. package/dist/adapters/compress-module.d.ts.map +1 -1
  34. package/dist/adapters/nitro.d.ts +17 -1
  35. package/dist/adapters/nitro.d.ts.map +1 -1
  36. package/dist/adapters/nitro.js +56 -13
  37. package/dist/adapters/nitro.js.map +1 -1
  38. package/dist/cache/fast-hash.d.ts +22 -0
  39. package/dist/cache/fast-hash.d.ts.map +1 -0
  40. package/dist/cache/index.d.ts +5 -2
  41. package/dist/cache/index.d.ts.map +1 -1
  42. package/dist/cache/index.js +90 -20
  43. package/dist/cache/index.js.map +1 -1
  44. package/dist/cache/register-cached-function.d.ts.map +1 -1
  45. package/dist/cache/singleflight.d.ts +18 -1
  46. package/dist/cache/singleflight.d.ts.map +1 -1
  47. package/dist/cache/timber-cache.d.ts +1 -1
  48. package/dist/cache/timber-cache.d.ts.map +1 -1
  49. package/dist/client/error-boundary.d.ts +10 -1
  50. package/dist/client/error-boundary.d.ts.map +1 -1
  51. package/dist/client/error-boundary.js +1 -125
  52. package/dist/client/error-reconstituter.d.ts +54 -0
  53. package/dist/client/error-reconstituter.d.ts.map +1 -0
  54. package/dist/client/form.d.ts +2 -2
  55. package/dist/client/form.d.ts.map +1 -1
  56. package/dist/client/index.d.ts +3 -2
  57. package/dist/client/index.d.ts.map +1 -1
  58. package/dist/client/index.js +433 -252
  59. package/dist/client/index.js.map +1 -1
  60. package/dist/client/link-pending-store.d.ts +78 -0
  61. package/dist/client/link-pending-store.d.ts.map +1 -0
  62. package/dist/client/link.d.ts +23 -9
  63. package/dist/client/link.d.ts.map +1 -1
  64. package/dist/client/navigation-context.d.ts +2 -2
  65. package/dist/client/navigation-context.d.ts.map +1 -1
  66. package/dist/client/router.d.ts +25 -3
  67. package/dist/client/router.d.ts.map +1 -1
  68. package/dist/client/rsc-fetch.d.ts +36 -2
  69. package/dist/client/rsc-fetch.d.ts.map +1 -1
  70. package/dist/client/segment-cache.d.ts +1 -1
  71. package/dist/client/segment-cache.d.ts.map +1 -1
  72. package/dist/client/segment-context.d.ts +1 -1
  73. package/dist/client/segment-context.d.ts.map +1 -1
  74. package/dist/client/segment-merger.d.ts.map +1 -1
  75. package/dist/client/segment-outlet.d.ts +63 -0
  76. package/dist/client/segment-outlet.d.ts.map +1 -0
  77. package/dist/client/stale-reload.d.ts +15 -0
  78. package/dist/client/stale-reload.d.ts.map +1 -1
  79. package/dist/client/top-loader.d.ts +1 -1
  80. package/dist/client/top-loader.d.ts.map +1 -1
  81. package/dist/client/transition-root.d.ts +1 -1
  82. package/dist/client/transition-root.d.ts.map +1 -1
  83. package/dist/client/use-params.d.ts +3 -3
  84. package/dist/client/use-params.d.ts.map +1 -1
  85. package/dist/client/use-query-states.d.ts +1 -1
  86. package/dist/client/use-query-states.d.ts.map +1 -1
  87. package/dist/codec.d.ts +21 -0
  88. package/dist/codec.d.ts.map +1 -0
  89. package/dist/cookies/define-cookie.d.ts +34 -13
  90. package/dist/cookies/define-cookie.d.ts.map +1 -1
  91. package/dist/cookies/index.js +1 -83
  92. package/dist/fonts/css.d.ts +1 -0
  93. package/dist/fonts/css.d.ts.map +1 -1
  94. package/dist/index.d.ts +127 -35
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +665 -242
  97. package/dist/index.js.map +1 -1
  98. package/dist/params/define.d.ts +100 -0
  99. package/dist/params/define.d.ts.map +1 -0
  100. package/dist/params/index.d.ts +8 -0
  101. package/dist/params/index.d.ts.map +1 -0
  102. package/dist/params/index.js +4 -0
  103. package/dist/plugins/adapter-build.d.ts +1 -1
  104. package/dist/plugins/adapter-build.d.ts.map +1 -1
  105. package/dist/plugins/build-manifest.d.ts +2 -2
  106. package/dist/plugins/build-manifest.d.ts.map +1 -1
  107. package/dist/plugins/build-report.d.ts +3 -3
  108. package/dist/plugins/build-report.d.ts.map +1 -1
  109. package/dist/plugins/client-chunks.d.ts +32 -0
  110. package/dist/plugins/client-chunks.d.ts.map +1 -0
  111. package/dist/plugins/content.d.ts +1 -1
  112. package/dist/plugins/content.d.ts.map +1 -1
  113. package/dist/plugins/dev-browser-logs.d.ts +84 -0
  114. package/dist/plugins/dev-browser-logs.d.ts.map +1 -0
  115. package/dist/plugins/dev-error-overlay.d.ts +26 -1
  116. package/dist/plugins/dev-error-overlay.d.ts.map +1 -1
  117. package/dist/plugins/dev-logs.d.ts +1 -1
  118. package/dist/plugins/dev-logs.d.ts.map +1 -1
  119. package/dist/plugins/dev-server.d.ts +1 -1
  120. package/dist/plugins/dev-server.d.ts.map +1 -1
  121. package/dist/plugins/entries.d.ts +1 -1
  122. package/dist/plugins/entries.d.ts.map +1 -1
  123. package/dist/plugins/fonts.d.ts +9 -2
  124. package/dist/plugins/fonts.d.ts.map +1 -1
  125. package/dist/plugins/mdx.d.ts +1 -1
  126. package/dist/plugins/mdx.d.ts.map +1 -1
  127. package/dist/plugins/routing.d.ts +1 -1
  128. package/dist/plugins/routing.d.ts.map +1 -1
  129. package/dist/plugins/server-bundle.d.ts.map +1 -1
  130. package/dist/plugins/shims.d.ts +6 -5
  131. package/dist/plugins/shims.d.ts.map +1 -1
  132. package/dist/plugins/static-build.d.ts +1 -1
  133. package/dist/plugins/static-build.d.ts.map +1 -1
  134. package/dist/routing/codegen.d.ts +2 -2
  135. package/dist/routing/codegen.d.ts.map +1 -1
  136. package/dist/routing/index.js +1 -1
  137. package/dist/routing/scanner.d.ts.map +1 -1
  138. package/dist/routing/status-file-lint.d.ts +2 -1
  139. package/dist/routing/status-file-lint.d.ts.map +1 -1
  140. package/dist/routing/types.d.ts +16 -4
  141. package/dist/routing/types.d.ts.map +1 -1
  142. package/dist/rsc-runtime/rsc.d.ts +1 -1
  143. package/dist/rsc-runtime/rsc.d.ts.map +1 -1
  144. package/dist/rsc-runtime/ssr.d.ts +12 -0
  145. package/dist/rsc-runtime/ssr.d.ts.map +1 -1
  146. package/dist/search-params/codecs.d.ts +1 -1
  147. package/dist/search-params/define.d.ts +159 -0
  148. package/dist/search-params/define.d.ts.map +1 -0
  149. package/dist/search-params/index.d.ts +4 -5
  150. package/dist/search-params/index.d.ts.map +1 -1
  151. package/dist/search-params/index.js +4 -474
  152. package/dist/search-params/registry.d.ts +1 -1
  153. package/dist/search-params/wrappers.d.ts +53 -0
  154. package/dist/search-params/wrappers.d.ts.map +1 -0
  155. package/dist/server/access-gate.d.ts +4 -0
  156. package/dist/server/access-gate.d.ts.map +1 -1
  157. package/dist/server/action-client.d.ts.map +1 -1
  158. package/dist/server/action-encryption.d.ts +76 -0
  159. package/dist/server/action-encryption.d.ts.map +1 -0
  160. package/dist/server/action-handler.d.ts.map +1 -1
  161. package/dist/server/actions.d.ts +1 -1
  162. package/dist/server/actions.d.ts.map +1 -1
  163. package/dist/server/als-registry.d.ts +25 -4
  164. package/dist/server/als-registry.d.ts.map +1 -1
  165. package/dist/server/build-manifest.d.ts +2 -2
  166. package/dist/server/build-manifest.d.ts.map +1 -1
  167. package/dist/server/debug.d.ts +1 -1
  168. package/dist/server/default-logger.d.ts +22 -0
  169. package/dist/server/default-logger.d.ts.map +1 -0
  170. package/dist/server/deny-renderer.d.ts.map +1 -1
  171. package/dist/server/early-hints.d.ts +13 -5
  172. package/dist/server/early-hints.d.ts.map +1 -1
  173. package/dist/server/error-boundary-wrapper.d.ts +4 -0
  174. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  175. package/dist/server/fallback-error.d.ts +4 -3
  176. package/dist/server/fallback-error.d.ts.map +1 -1
  177. package/dist/server/flight-injection-state.d.ts +66 -0
  178. package/dist/server/flight-injection-state.d.ts.map +1 -0
  179. package/dist/server/flight-scripts.d.ts +42 -0
  180. package/dist/server/flight-scripts.d.ts.map +1 -0
  181. package/dist/server/flush.d.ts.map +1 -1
  182. package/dist/server/form-data.d.ts +29 -0
  183. package/dist/server/form-data.d.ts.map +1 -1
  184. package/dist/server/html-injectors.d.ts +51 -11
  185. package/dist/server/html-injectors.d.ts.map +1 -1
  186. package/dist/server/index.d.ts +4 -2
  187. package/dist/server/index.d.ts.map +1 -1
  188. package/dist/server/index.js +1977 -1648
  189. package/dist/server/index.js.map +1 -1
  190. package/dist/server/logger.d.ts +25 -7
  191. package/dist/server/logger.d.ts.map +1 -1
  192. package/dist/server/node-stream-transforms.d.ts +113 -0
  193. package/dist/server/node-stream-transforms.d.ts.map +1 -0
  194. package/dist/server/pipeline-interception.d.ts +1 -1
  195. package/dist/server/pipeline-interception.d.ts.map +1 -1
  196. package/dist/server/pipeline.d.ts +20 -6
  197. package/dist/server/pipeline.d.ts.map +1 -1
  198. package/dist/server/primitives.d.ts +30 -3
  199. package/dist/server/primitives.d.ts.map +1 -1
  200. package/dist/server/render-timeout.d.ts +51 -0
  201. package/dist/server/render-timeout.d.ts.map +1 -0
  202. package/dist/server/request-context.d.ts +65 -38
  203. package/dist/server/request-context.d.ts.map +1 -1
  204. package/dist/server/route-element-builder.d.ts +7 -0
  205. package/dist/server/route-element-builder.d.ts.map +1 -1
  206. package/dist/server/route-handler.d.ts.map +1 -1
  207. package/dist/server/route-matcher.d.ts +9 -2
  208. package/dist/server/route-matcher.d.ts.map +1 -1
  209. package/dist/server/rsc-entry/api-handler.d.ts +2 -2
  210. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
  211. package/dist/server/rsc-entry/error-renderer.d.ts +26 -13
  212. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  213. package/dist/server/rsc-entry/helpers.d.ts +48 -5
  214. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  215. package/dist/server/rsc-entry/index.d.ts +8 -3
  216. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  217. package/dist/server/rsc-entry/rsc-payload.d.ts +3 -3
  218. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  219. package/dist/server/rsc-entry/rsc-stream.d.ts +10 -1
  220. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  221. package/dist/server/rsc-entry/ssr-bridge.d.ts +1 -1
  222. package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
  223. package/dist/server/rsc-entry/ssr-renderer.d.ts +19 -4
  224. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  225. package/dist/server/slot-resolver.d.ts +1 -1
  226. package/dist/server/slot-resolver.d.ts.map +1 -1
  227. package/dist/server/ssr-entry.d.ts +22 -0
  228. package/dist/server/ssr-entry.d.ts.map +1 -1
  229. package/dist/server/ssr-render.d.ts +39 -21
  230. package/dist/server/ssr-render.d.ts.map +1 -1
  231. package/dist/server/ssr-wrappers.d.ts +50 -0
  232. package/dist/server/ssr-wrappers.d.ts.map +1 -0
  233. package/dist/server/status-code-resolver.d.ts +1 -1
  234. package/dist/server/status-code-resolver.d.ts.map +1 -1
  235. package/dist/server/stream-utils.d.ts +36 -0
  236. package/dist/server/stream-utils.d.ts.map +1 -0
  237. package/dist/server/tracing.d.ts +10 -0
  238. package/dist/server/tracing.d.ts.map +1 -1
  239. package/dist/server/tree-builder.d.ts +20 -13
  240. package/dist/server/tree-builder.d.ts.map +1 -1
  241. package/dist/server/types.d.ts +1 -3
  242. package/dist/server/types.d.ts.map +1 -1
  243. package/dist/server/version-skew.d.ts +61 -0
  244. package/dist/server/version-skew.d.ts.map +1 -0
  245. package/dist/server/waituntil-bridge.d.ts.map +1 -1
  246. package/dist/shared/merge-search-params.d.ts +22 -0
  247. package/dist/shared/merge-search-params.d.ts.map +1 -0
  248. package/dist/shims/font-google.d.ts +1 -1
  249. package/dist/shims/font-google.d.ts.map +1 -1
  250. package/dist/shims/navigation-client.d.ts +1 -1
  251. package/dist/shims/navigation-client.d.ts.map +1 -1
  252. package/dist/shims/navigation.d.ts +1 -1
  253. package/dist/shims/navigation.d.ts.map +1 -1
  254. package/dist/utils/state-machine.d.ts +80 -0
  255. package/dist/utils/state-machine.d.ts.map +1 -0
  256. package/package.json +17 -17
  257. package/src/adapters/compress-module.ts +24 -4
  258. package/src/adapters/nitro.ts +58 -9
  259. package/src/cache/fast-hash.ts +34 -0
  260. package/src/cache/index.ts +5 -2
  261. package/src/cache/register-cached-function.ts +7 -3
  262. package/src/cache/singleflight.ts +62 -4
  263. package/src/cache/timber-cache.ts +40 -29
  264. package/src/cli.ts +0 -0
  265. package/src/client/browser-entry.ts +151 -99
  266. package/src/client/error-boundary.tsx +18 -1
  267. package/src/client/error-reconstituter.tsx +65 -0
  268. package/src/client/form.tsx +2 -2
  269. package/src/client/index.ts +10 -1
  270. package/src/client/link-pending-store.ts +136 -0
  271. package/src/client/link.tsx +137 -22
  272. package/src/client/navigation-context.ts +6 -5
  273. package/src/client/router.ts +117 -60
  274. package/src/client/rsc-fetch.ts +90 -2
  275. package/src/client/segment-cache.ts +1 -1
  276. package/src/client/segment-context.ts +6 -1
  277. package/src/client/segment-merger.ts +2 -8
  278. package/src/client/segment-outlet.tsx +86 -0
  279. package/src/client/stale-reload.ts +73 -6
  280. package/src/client/top-loader.tsx +10 -9
  281. package/src/client/transition-root.tsx +20 -2
  282. package/src/client/use-params.ts +4 -4
  283. package/src/client/use-query-states.ts +2 -2
  284. package/src/codec.ts +21 -0
  285. package/src/cookies/define-cookie.ts +71 -20
  286. package/src/fonts/css.ts +2 -1
  287. package/src/index.ts +297 -85
  288. package/src/params/define.ts +327 -0
  289. package/src/params/index.ts +28 -0
  290. package/src/plugins/adapter-build.ts +8 -2
  291. package/src/plugins/build-manifest.ts +13 -2
  292. package/src/plugins/build-report.ts +3 -3
  293. package/src/plugins/cache-transform.ts +1 -1
  294. package/src/plugins/client-chunks.ts +65 -0
  295. package/src/plugins/content.ts +1 -1
  296. package/src/plugins/dev-browser-logs.ts +284 -0
  297. package/src/plugins/dev-error-overlay.ts +70 -1
  298. package/src/plugins/dev-logs.ts +1 -1
  299. package/src/plugins/dev-server.ts +41 -7
  300. package/src/plugins/entries.ts +6 -8
  301. package/src/plugins/fonts.ts +102 -55
  302. package/src/plugins/mdx.ts +1 -1
  303. package/src/plugins/routing.ts +57 -17
  304. package/src/plugins/server-action-exports.ts +1 -1
  305. package/src/plugins/server-bundle.ts +32 -1
  306. package/src/plugins/shims.ts +69 -31
  307. package/src/plugins/static-build.ts +10 -6
  308. package/src/routing/codegen.ts +109 -88
  309. package/src/routing/scanner.ts +86 -7
  310. package/src/routing/status-file-lint.ts +3 -2
  311. package/src/routing/types.ts +17 -4
  312. package/src/rsc-runtime/rsc.ts +2 -0
  313. package/src/rsc-runtime/ssr.ts +50 -0
  314. package/src/rsc-runtime/vendor-types.d.ts +7 -0
  315. package/src/search-params/codecs.ts +1 -1
  316. package/src/search-params/define.ts +518 -0
  317. package/src/search-params/index.ts +12 -18
  318. package/src/search-params/registry.ts +1 -1
  319. package/src/search-params/wrappers.ts +85 -0
  320. package/src/server/access-gate.tsx +40 -9
  321. package/src/server/action-client.ts +8 -2
  322. package/src/server/action-encryption.ts +144 -0
  323. package/src/server/action-handler.ts +20 -3
  324. package/src/server/actions.ts +1 -1
  325. package/src/server/als-registry.ts +25 -4
  326. package/src/server/build-manifest.ts +10 -4
  327. package/src/server/compress.ts +25 -7
  328. package/src/server/debug.ts +1 -1
  329. package/src/server/default-logger.ts +99 -0
  330. package/src/server/deny-renderer.ts +5 -3
  331. package/src/server/early-hints.ts +36 -15
  332. package/src/server/error-boundary-wrapper.ts +58 -15
  333. package/src/server/fallback-error.ts +29 -14
  334. package/src/server/flight-injection-state.ts +113 -0
  335. package/src/server/flight-scripts.ts +62 -0
  336. package/src/server/flush.ts +2 -1
  337. package/src/server/form-data.ts +76 -0
  338. package/src/server/html-injectors.ts +277 -117
  339. package/src/server/index.ts +9 -4
  340. package/src/server/logger.ts +44 -36
  341. package/src/server/node-stream-transforms.ts +509 -0
  342. package/src/server/pipeline-interception.ts +1 -1
  343. package/src/server/pipeline.ts +148 -41
  344. package/src/server/primitives.ts +47 -5
  345. package/src/server/render-timeout.ts +108 -0
  346. package/src/server/request-context.ts +125 -119
  347. package/src/server/route-element-builder.ts +107 -115
  348. package/src/server/route-handler.ts +2 -1
  349. package/src/server/route-matcher.ts +9 -2
  350. package/src/server/rsc-entry/api-handler.ts +8 -8
  351. package/src/server/rsc-entry/error-renderer.ts +286 -81
  352. package/src/server/rsc-entry/helpers.ts +134 -5
  353. package/src/server/rsc-entry/index.ts +177 -76
  354. package/src/server/rsc-entry/rsc-payload.ts +91 -18
  355. package/src/server/rsc-entry/rsc-stream.ts +74 -18
  356. package/src/server/rsc-entry/ssr-bridge.ts +2 -2
  357. package/src/server/rsc-entry/ssr-renderer.ts +152 -34
  358. package/src/server/slot-resolver.ts +231 -220
  359. package/src/server/ssr-entry.ts +211 -32
  360. package/src/server/ssr-render.ts +289 -67
  361. package/src/server/ssr-wrappers.tsx +139 -0
  362. package/src/server/status-code-resolver.ts +1 -1
  363. package/src/server/stream-utils.ts +213 -0
  364. package/src/server/tracing.ts +23 -0
  365. package/src/server/tree-builder.ts +92 -58
  366. package/src/server/types.ts +1 -3
  367. package/src/server/version-skew.ts +104 -0
  368. package/src/server/waituntil-bridge.ts +4 -1
  369. package/src/shared/merge-search-params.ts +55 -0
  370. package/src/shims/font-google.ts +1 -1
  371. package/src/shims/navigation-client.ts +1 -1
  372. package/src/shims/navigation.ts +2 -1
  373. package/src/utils/state-machine.ts +111 -0
  374. package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
  375. package/dist/_chunks/debug-gwlJkDuf.js.map +0 -1
  376. package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
  377. package/dist/_chunks/request-context-DIkVh_jG.js.map +0 -1
  378. package/dist/_chunks/ssr-data-MjmprTmO.js +0 -88
  379. package/dist/_chunks/ssr-data-MjmprTmO.js.map +0 -1
  380. package/dist/_chunks/use-cookie-DX-l1_5E.js +0 -91
  381. package/dist/_chunks/use-cookie-DX-l1_5E.js.map +0 -1
  382. package/dist/_chunks/use-query-states-D5KaffOK.js.map +0 -1
  383. package/dist/client/error-boundary.js.map +0 -1
  384. package/dist/client/link-status-provider.d.ts +0 -11
  385. package/dist/client/link-status-provider.d.ts.map +0 -1
  386. package/dist/cookies/index.js.map +0 -1
  387. package/dist/plugins/dynamic-transform.d.ts +0 -72
  388. package/dist/plugins/dynamic-transform.d.ts.map +0 -1
  389. package/dist/search-params/analyze.d.ts +0 -54
  390. package/dist/search-params/analyze.d.ts.map +0 -1
  391. package/dist/search-params/builtin-codecs.d.ts +0 -105
  392. package/dist/search-params/builtin-codecs.d.ts.map +0 -1
  393. package/dist/search-params/create.d.ts +0 -106
  394. package/dist/search-params/create.d.ts.map +0 -1
  395. package/dist/search-params/index.js.map +0 -1
  396. package/dist/server/prerender.d.ts +0 -77
  397. package/dist/server/prerender.d.ts.map +0 -1
  398. package/dist/server/response-cache.d.ts +0 -53
  399. package/dist/server/response-cache.d.ts.map +0 -1
  400. package/src/client/link-status-provider.tsx +0 -30
  401. package/src/plugins/dynamic-transform.ts +0 -161
  402. package/src/search-params/analyze.ts +0 -192
  403. package/src/search-params/builtin-codecs.ts +0 -228
  404. package/src/search-params/create.ts +0 -321
  405. package/src/server/prerender.ts +0 -139
  406. 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-DviM89f0.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-Cey5DCGr.js";
3
4
  import { existsSync, readFileSync } from "node:fs";
4
5
  import { dirname, extname, join, normalize, resolve } from "node:path";
5
- import { fileURLToPath, pathToFileURL } from "node:url";
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.",
@@ -12158,7 +12189,8 @@ var CLIENT_REQUIRED_EXTENSIONS = new Set([
12158
12189
  * that are missing it.
12159
12190
  *
12160
12191
  * MDX and JSON status files are excluded — MDX files are server components
12161
- * 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.
12162
12194
  */
12163
12195
  function lintStatusFileDirectives(tree) {
12164
12196
  const warnings = [];
@@ -12203,7 +12235,7 @@ var RESOLVED_VIRTUAL_ID$1 = `\0${VIRTUAL_MODULE_ID$1}`;
12203
12235
  /**
12204
12236
  * File convention names we track for changes that require manifest regeneration.
12205
12237
  */
12206
- 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|global-error|default|denied|params|\d{3}|[45]xx|not-found|forbidden|unauthorized|sitemap|robots|manifest|favicon|icon|opengraph-image|twitter-image|apple-icon)\./;
12207
12239
  /**
12208
12240
  * Create the timber-routing Vite plugin.
12209
12241
  *
@@ -12278,14 +12310,40 @@ function timberRouting(ctx) {
12278
12310
  configureServer(devServer) {
12279
12311
  rescan();
12280
12312
  devServer.watcher.add(ctx.appDir);
12281
- 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) => {
12282
12320
  if (!filePath.startsWith(ctx.appDir)) return;
12283
12321
  if (!ROUTE_FILE_PATTERNS.test(filePath)) return;
12284
12322
  rescan();
12323
+ lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : "";
12285
12324
  invalidateManifest(devServer);
12286
12325
  };
12287
- devServer.watcher.on("add", handleFileChange);
12288
- 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);
12289
12347
  }
12290
12348
  };
12291
12349
  }
@@ -12366,9 +12424,9 @@ function generateManifestModule(tree) {
12366
12424
  const v = addImport(node.denied);
12367
12425
  parts.push(`${nextIndent}denied: { load: ${v}, filePath: ${JSON.stringify(node.denied.filePath)} },`);
12368
12426
  }
12369
- if (node.searchParams) {
12370
- const v = addImport(node.searchParams);
12371
- parts.push(`${nextIndent}searchParams: { load: ${v}, filePath: ${JSON.stringify(node.searchParams.filePath)} },`);
12427
+ if (node.params) {
12428
+ const v = addImport(node.params);
12429
+ parts.push(`${nextIndent}params: { load: ${v}, filePath: ${JSON.stringify(node.params.filePath)} },`);
12372
12430
  }
12373
12431
  if (node.statusFiles && node.statusFiles.size > 0) {
12374
12432
  const statusEntries = [];
@@ -12416,6 +12474,8 @@ function generateManifestModule(tree) {
12416
12474
  const rootSerialized = serializeNode(tree.root, " ");
12417
12475
  let proxyLine = "";
12418
12476
  if (tree.proxy) proxyLine = ` proxy: { load: ${addImport(tree.proxy)}, filePath: ${JSON.stringify(tree.proxy.filePath)} },`;
12477
+ let globalErrorLine = "";
12478
+ if (tree.globalError) globalErrorLine = ` globalError: { load: ${addImport(tree.globalError)}, filePath: ${JSON.stringify(tree.globalError.filePath)} },`;
12419
12479
  const rewrites = collectInterceptionRewrites(tree.root);
12420
12480
  const rewritesLine = rewrites.length > 0 ? ` interceptionRewrites: ${JSON.stringify(rewrites.map((r) => ({
12421
12481
  interceptedPattern: r.interceptedPattern,
@@ -12429,6 +12489,7 @@ function generateManifestModule(tree) {
12429
12489
  "",
12430
12490
  "const manifest = {",
12431
12491
  proxyLine,
12492
+ globalErrorLine,
12432
12493
  rewritesLine,
12433
12494
  ` root: ${rootSerialized},`,
12434
12495
  "};",
@@ -12451,6 +12512,23 @@ var SHIMS_DIR = resolve(PKG_ROOT, "src", "shims");
12451
12512
  var SERVER_ONLY_VIRTUAL = "\0timber:server-only";
12452
12513
  var CLIENT_ONLY_VIRTUAL = "\0timber:client-only";
12453
12514
  /**
12515
+ * Map from @timber-js/app/<subpath> to relative src/ file paths.
12516
+ *
12517
+ * Used by the catch-all resolveId rule to remap subpath imports to src/
12518
+ * in server environments. When adding a new subpath export to package.json,
12519
+ * add it here too. See TIM-568.
12520
+ */
12521
+ var SUBPATH_SRC_MAP = {
12522
+ "cache": "cache/index.ts",
12523
+ "content": "content/index.ts",
12524
+ "cookies": "cookies/index.ts",
12525
+ "params": "params/index.ts",
12526
+ "search-params": "search-params/index.ts",
12527
+ "routing": "routing/index.ts",
12528
+ "adapters/cloudflare": "adapters/cloudflare.ts",
12529
+ "adapters/nitro": "adapters/nitro.ts"
12530
+ };
12531
+ /**
12454
12532
  * Map from next/* import specifiers to shim file paths.
12455
12533
  *
12456
12534
  * The shim map is a separate data structure (not embedded in the plugin)
@@ -12496,6 +12574,13 @@ function timberShims(_ctx) {
12496
12574
  }
12497
12575
  if (cleanId === "@timber-js/app/client") {
12498
12576
  if (this.environment?.name === "ssr") return resolve(PKG_ROOT, "src", "client", "index.ts");
12577
+ return null;
12578
+ }
12579
+ if (cleanId.startsWith("@timber-js/app/") && cleanId !== "@timber-js/app/package.json") {
12580
+ if (this.environment?.name !== "client") {
12581
+ const srcPath = SUBPATH_SRC_MAP[cleanId.slice(15)];
12582
+ if (srcPath) return resolve(PKG_ROOT, "src", srcPath);
12583
+ }
12499
12584
  }
12500
12585
  return null;
12501
12586
  },
@@ -12576,11 +12661,12 @@ function generateFontFaces(descriptors) {
12576
12661
  * ```css
12577
12662
  * .timber-font-inter {
12578
12663
  * --font-sans: 'Inter', 'Inter Fallback', system-ui, sans-serif;
12664
+ * font-family: 'Inter', 'Inter Fallback', system-ui, sans-serif;
12579
12665
  * }
12580
12666
  * ```
12581
12667
  */
12582
12668
  function generateVariableClass(className, variable, fontFamily) {
12583
- return `.${className} {\n ${variable}: ${fontFamily};\n}`;
12669
+ return `.${className} {\n ${variable}: ${fontFamily};\n font-family: ${fontFamily};\n}`;
12584
12670
  }
12585
12671
  /**
12586
12672
  * Generate a scoped CSS class that applies font-family directly.
@@ -13357,10 +13443,24 @@ async function isCacheHit(metaPath, dataPath) {
13357
13443
  //#region src/plugins/fonts.ts
13358
13444
  var VIRTUAL_GOOGLE = "@timber/fonts/google";
13359
13445
  var VIRTUAL_LOCAL = "@timber/fonts/local";
13360
- var VIRTUAL_FONT_CSS = "virtual:timber-fonts.css";
13361
13446
  var RESOLVED_GOOGLE = "\0@timber/fonts/google";
13362
13447
  var RESOLVED_LOCAL = "\0@timber/fonts/local";
13363
- var RESOLVED_FONT_CSS = "\0virtual:timber-fonts.css";
13448
+ /**
13449
+ * Virtual side-effect module that registers font CSS on globalThis.
13450
+ *
13451
+ * When a file calls localFont() or a Google font function, the transform
13452
+ * hook injects `import 'virtual:timber-font-css-register'` into that file.
13453
+ * This virtual module sets `globalThis.__timber_font_css` with the combined
13454
+ * @font-face CSS. The RSC entry reads it at render time to inline a <style> tag.
13455
+ *
13456
+ * This approach avoids timing issues because:
13457
+ * 1. The font file is in the RSC module graph (imported by layout.tsx)
13458
+ * 2. The side-effect import is added to the font file during transform
13459
+ * 3. When layout.tsx is loaded, fonts.ts runs → side-effect module runs → globalThis is set
13460
+ * 4. RSC entry renders → reads globalThis → inlines <style>
13461
+ */
13462
+ var VIRTUAL_FONT_CSS_REGISTER = "virtual:timber-font-css-register";
13463
+ var RESOLVED_FONT_CSS_REGISTER = "\0virtual:timber-font-css-register";
13364
13464
  /**
13365
13465
  * Convert a font family name to a PascalCase export name.
13366
13466
  * e.g. "JetBrains Mono" → "JetBrains_Mono"
@@ -13508,6 +13608,24 @@ function generateLocalVirtualModule() {
13508
13608
  ].join("\n");
13509
13609
  }
13510
13610
  /**
13611
+ * Generate CSS for a single extracted font.
13612
+ *
13613
+ * Includes @font-face rules (for local fonts), fallback @font-face,
13614
+ * and the scoped class rule.
13615
+ */
13616
+ function generateFontCss(font) {
13617
+ const cssParts = [];
13618
+ if (font.provider === "local" && font.localSources) {
13619
+ const faceCss = generateFontFaces(generateLocalFontFaces(font.family, font.localSources, font.display));
13620
+ if (faceCss) cssParts.push(faceCss);
13621
+ }
13622
+ const fallbackCss = generateFallbackCss(font.family);
13623
+ if (fallbackCss) cssParts.push(fallbackCss);
13624
+ if (font.variable) cssParts.push(generateVariableClass(font.className, font.variable, font.fontFamily));
13625
+ else cssParts.push(generateFontFamilyClass(font.className, font.fontFamily));
13626
+ return cssParts.join("\n\n");
13627
+ }
13628
+ /**
13511
13629
  * Generate the CSS output for all extracted fonts.
13512
13630
  *
13513
13631
  * Includes @font-face rules for local fonts, fallback @font-face rules,
@@ -13515,16 +13633,7 @@ function generateLocalVirtualModule() {
13515
13633
  */
13516
13634
  function generateAllFontCss(registry) {
13517
13635
  const cssParts = [];
13518
- for (const font of registry.values()) {
13519
- if (font.provider === "local" && font.localSources) {
13520
- const faceCss = generateFontFaces(generateLocalFontFaces(font.family, font.localSources, font.display));
13521
- if (faceCss) cssParts.push(faceCss);
13522
- }
13523
- const fallbackCss = generateFallbackCss(font.family);
13524
- if (fallbackCss) cssParts.push(fallbackCss);
13525
- if (font.variable) cssParts.push(generateVariableClass(font.className, font.variable, font.fontFamily));
13526
- else cssParts.push(generateFontFamilyClass(font.className, font.fontFamily));
13527
- }
13636
+ for (const font of registry.values()) cssParts.push(generateFontCss(font));
13528
13637
  return cssParts.join("\n\n");
13529
13638
  }
13530
13639
  /**
@@ -13578,15 +13687,24 @@ function timberFonts(ctx) {
13578
13687
  return {
13579
13688
  name: "timber-fonts",
13580
13689
  resolveId(id) {
13581
- if (id === VIRTUAL_GOOGLE) return RESOLVED_GOOGLE;
13582
- if (id === VIRTUAL_LOCAL) return RESOLVED_LOCAL;
13583
- if (id === VIRTUAL_FONT_CSS) return RESOLVED_FONT_CSS;
13690
+ let cleanId = id.startsWith("\0") ? id.slice(1) : id;
13691
+ if (cleanId.startsWith(ctx.root)) {
13692
+ const stripped = cleanId.slice(ctx.root.length);
13693
+ if (stripped.startsWith("/") || stripped.startsWith("\\")) cleanId = stripped.slice(1);
13694
+ else cleanId = stripped;
13695
+ }
13696
+ if (cleanId === VIRTUAL_GOOGLE) return RESOLVED_GOOGLE;
13697
+ if (cleanId === VIRTUAL_LOCAL) return RESOLVED_LOCAL;
13698
+ if (cleanId === VIRTUAL_FONT_CSS_REGISTER) return RESOLVED_FONT_CSS_REGISTER;
13584
13699
  return null;
13585
13700
  },
13586
13701
  load(id) {
13587
13702
  if (id === RESOLVED_GOOGLE) return generateGoogleVirtualModule(registry);
13588
13703
  if (id === RESOLVED_LOCAL) return generateLocalVirtualModule();
13589
- if (id === RESOLVED_FONT_CSS) return generateAllFontCss(registry);
13704
+ if (id === RESOLVED_FONT_CSS_REGISTER) {
13705
+ const css = generateAllFontCss(registry);
13706
+ return `globalThis.__timber_font_css = ${JSON.stringify(css)};`;
13707
+ }
13590
13708
  return null;
13591
13709
  },
13592
13710
  configureServer(server) {
@@ -13682,7 +13800,7 @@ function timberFonts(ctx) {
13682
13800
  }
13683
13801
  if (hasLocalImport) transformedCode = transformLocalFonts(transformedCode, code, id, registry, this.error.bind(this));
13684
13802
  if (transformedCode !== code) {
13685
- transformedCode = `import '${VIRTUAL_FONT_CSS}';\n` + transformedCode;
13803
+ if (registry.size > 0) transformedCode = `import '${VIRTUAL_FONT_CSS_REGISTER}';\n` + transformedCode;
13686
13804
  return {
13687
13805
  code: transformedCode,
13688
13806
  map: null
@@ -13713,12 +13831,6 @@ function timberFonts(ctx) {
13713
13831
  });
13714
13832
  }
13715
13833
  }
13716
- const fontCss = generateAllFontCss(registry);
13717
- if (fontCss) this.emitFile({
13718
- type: "asset",
13719
- fileName: "_timber/fonts/fonts.css",
13720
- source: fontCss
13721
- });
13722
13834
  if (!ctx.buildManifest) return;
13723
13835
  const cachedByFamily = /* @__PURE__ */ new Map();
13724
13836
  for (const cf of cachedFonts) {
@@ -13845,16 +13957,14 @@ function validateStaticMode(code, fileId, options) {
13845
13957
  * - transform: Validates source files for static mode violations
13846
13958
  */
13847
13959
  function timberStaticBuild(ctx) {
13848
- const isStatic = ctx.config.output === "static";
13849
- const clientJavascriptDisabled = ctx.clientJavascript.disabled;
13850
13960
  return {
13851
13961
  name: "timber-static-build",
13852
13962
  transform(code, id) {
13853
- if (!isStatic) return null;
13963
+ if (!(ctx.config.output === "static")) return null;
13854
13964
  if (id.includes("node_modules")) return null;
13855
13965
  if (!id.includes("/app/") && !id.startsWith("app/")) return null;
13856
13966
  if (!/\.[jt]sx?$/.test(id)) return null;
13857
- const errors = validateStaticMode(code, id, { clientJavascriptDisabled });
13967
+ const errors = validateStaticMode(code, id, { clientJavascriptDisabled: ctx.clientJavascript.disabled });
13858
13968
  if (errors.length > 0) {
13859
13969
  const messages = errors.map((e) => `[timber] Static mode error in ${e.file}${e.line ? `:${e.line}` : ""}: ${e.message}`);
13860
13970
  this.error(messages.join("\n\n"));
@@ -13864,95 +13974,6 @@ function timberStaticBuild(ctx) {
13864
13974
  };
13865
13975
  }
13866
13976
  //#endregion
13867
- //#region src/plugins/dynamic-transform.ts
13868
- /**
13869
- * Quick check: does this source file contain 'use dynamic' anywhere?
13870
- * Used as a fast bail-out before doing expensive AST parsing.
13871
- */
13872
- function containsUseDynamic(code) {
13873
- return containsDirective(code, "use dynamic");
13874
- }
13875
- /**
13876
- * Find function declarations/expressions containing 'use dynamic' and
13877
- * transform them into markDynamic() calls.
13878
- *
13879
- * Input:
13880
- * ```tsx
13881
- * export default async function AddToCartButton({ productId }) {
13882
- * 'use dynamic'
13883
- * const user = await getUser()
13884
- * return <button>Add to cart</button>
13885
- * }
13886
- * ```
13887
- *
13888
- * Output:
13889
- * ```tsx
13890
- * import { markDynamic as __markDynamic } from '@timber-js/app/runtime';
13891
- * export default async function AddToCartButton({ productId }) {
13892
- * __markDynamic();
13893
- * const user = await getUser()
13894
- * return <button>Add to cart</button>
13895
- * }
13896
- * ```
13897
- *
13898
- * The markDynamic() call registers the component boundary as dynamic
13899
- * at render time. The pre-render pass uses this to know which subtrees
13900
- * to skip and leave as holes for per-request rendering.
13901
- */
13902
- function transformUseDynamic(code) {
13903
- if (!containsUseDynamic(code)) return null;
13904
- const functions = findFunctionsWithDirective(code, "use dynamic");
13905
- if (functions.length === 0) return null;
13906
- let result = code;
13907
- for (const fn of functions) {
13908
- const cleanBody = fn.bodyContent.replace(/['"]use dynamic['"];?/, "__markDynamic();");
13909
- result = result.slice(0, fn.bodyStart) + cleanBody + result.slice(fn.bodyEnd);
13910
- }
13911
- result = `import { markDynamic as __markDynamic } from '@timber-js/app/runtime';\n` + result;
13912
- return {
13913
- code: result,
13914
- map: null
13915
- };
13916
- }
13917
- /**
13918
- * In `output: 'static'` mode, `'use dynamic'` is a build error.
13919
- * Static mode renders everything at build time — there is no per-request
13920
- * rendering to opt into.
13921
- */
13922
- function validateNoDynamicInStaticMode(code) {
13923
- if (!containsUseDynamic(code)) return null;
13924
- const functions = findFunctionsWithDirective(code, "use dynamic");
13925
- if (functions.length === 0) return null;
13926
- return {
13927
- 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'.",
13928
- line: functions[functions.length - 1].directiveLine
13929
- };
13930
- }
13931
- /**
13932
- * Create the timber-dynamic-transform Vite plugin.
13933
- *
13934
- * In server mode: transforms 'use dynamic' into markDynamic() calls.
13935
- * In static mode: rejects 'use dynamic' as a build error.
13936
- */
13937
- function timberDynamicTransform(ctx) {
13938
- const isStatic = ctx.config.output === "static";
13939
- return {
13940
- name: "timber-dynamic-transform",
13941
- transform(code, id) {
13942
- if (id.includes("node_modules")) return null;
13943
- if (!id.includes("/app/") && !id.startsWith("app/")) return null;
13944
- if (!/\.[jt]sx?$/.test(id)) return null;
13945
- if (!containsUseDynamic(code)) return null;
13946
- if (isStatic) {
13947
- const error = validateNoDynamicInStaticMode(code);
13948
- if (error) this.error(`[timber] Static mode error in ${id}${error.line ? `:${error.line}` : ""}: ${error.message}`);
13949
- return null;
13950
- }
13951
- return transformUseDynamic(code);
13952
- }
13953
- };
13954
- }
13955
- //#endregion
13956
13977
  //#region src/plugins/server-action-exports.ts
13957
13978
  var jsxParser = Parser.extend((0, import_acorn_jsx.default)());
13958
13979
  /**
@@ -14092,6 +14113,23 @@ function rewriteServerActionExportsFallback(code) {
14092
14113
  }
14093
14114
  //#endregion
14094
14115
  //#region src/plugins/build-manifest.ts
14116
+ /**
14117
+ * timber-build-manifest — Vite sub-plugin for build asset manifest generation.
14118
+ *
14119
+ * Provides `virtual:timber-build-manifest` which exports a BuildManifest
14120
+ * mapping route segment file paths to their CSS, JS, and modulepreload
14121
+ * output chunks.
14122
+ *
14123
+ * - Dev mode: exports an empty manifest (Vite HMR handles CSS/JS).
14124
+ * - Build mode: virtual module reads from globalThis.__TIMBER_BUILD_MANIFEST__
14125
+ * at runtime. The actual manifest data is injected by the adapter via a
14126
+ * _timber-manifest-init.js module that runs before the RSC handler.
14127
+ *
14128
+ * The generateBundle hook (client env only) extracts CSS/JS/modulepreload
14129
+ * data from the Rollup bundle and populates ctx.buildManifest.
14130
+ *
14131
+ * Design docs: 18-build-system.md §"Build Manifest", 02-rendering-pipeline.md §"Early Hints"
14132
+ */
14095
14133
  var VIRTUAL_MODULE_ID = "virtual:timber-build-manifest";
14096
14134
  var RESOLVED_VIRTUAL_ID = `\0${VIRTUAL_MODULE_ID}`;
14097
14135
  /**
@@ -14167,6 +14205,7 @@ function timberBuildManifest(ctx) {
14167
14205
  configResolved(config) {
14168
14206
  resolvedBase = config.base;
14169
14207
  isDev = config.command === "serve";
14208
+ if (!isDev && !ctx.deploymentId) ctx.deploymentId = randomUUID().replace(/-/g, "").slice(0, 16);
14170
14209
  },
14171
14210
  resolveId(id) {
14172
14211
  const cleanId = id.startsWith("\0") ? id.slice(1) : id;
@@ -14372,6 +14411,179 @@ function timberDevLogs(_ctx) {
14372
14411
  };
14373
14412
  }
14374
14413
  //#endregion
14414
+ //#region src/plugins/dev-browser-logs.ts
14415
+ /** Max message size in bytes before truncation. */
14416
+ var MAX_MESSAGE_BYTES = 2048;
14417
+ /** HMR event name for browser→server log forwarding. */
14418
+ var HMR_EVENT = "timber:browser-log";
14419
+ /** ANSI color codes for terminal output. */
14420
+ var COLORS = {
14421
+ red: "\x1B[31m",
14422
+ yellow: "\x1B[33m",
14423
+ blue: "\x1B[34m",
14424
+ dim: "\x1B[2m",
14425
+ reset: "\x1B[0m"
14426
+ };
14427
+ /** Level severity ordering for threshold comparison. */
14428
+ var LEVEL_SEVERITY = {
14429
+ none: 0,
14430
+ info: 1,
14431
+ warn: 2,
14432
+ error: 3
14433
+ };
14434
+ /**
14435
+ * Format a browser log payload for server terminal output.
14436
+ *
14437
+ * Produces color-coded output like:
14438
+ * [browser:error] Uncaught TypeError: x is not a function
14439
+ * at App (app.tsx:10:5)
14440
+ * [browser:warn] Deprecation warning (app/page.tsx:42:12)
14441
+ */
14442
+ function formatBrowserLog(payload) {
14443
+ const { level, message, stack, source } = payload;
14444
+ let output = `${`${level === "error" ? COLORS.red : level === "warn" ? COLORS.yellow : COLORS.blue}[browser:${level}]${COLORS.reset}`} ${message}${source ? ` ${COLORS.dim}(${source})${COLORS.reset}` : ""}`;
14445
+ if (stack) {
14446
+ const indented = stack.split("\n").map((line) => ` ${COLORS.dim}${line}${COLORS.reset}`).join("\n");
14447
+ output += `\n${indented}`;
14448
+ }
14449
+ return output;
14450
+ }
14451
+ /**
14452
+ * Check if a log at `level` should be forwarded given the configured threshold.
14453
+ *
14454
+ * The threshold acts as a minimum severity:
14455
+ * - 'error' → only errors
14456
+ * - 'warn' → errors + warnings
14457
+ * - 'info' → errors + warnings + info
14458
+ * - 'none' → nothing
14459
+ */
14460
+ function shouldForwardLevel(level, threshold) {
14461
+ if (threshold === "none") return false;
14462
+ return LEVEL_SEVERITY[level] >= LEVEL_SEVERITY[threshold];
14463
+ }
14464
+ /**
14465
+ * Truncate a message to `maxBytes` to avoid flooding the terminal.
14466
+ * Appends a suffix indicating how many bytes were dropped.
14467
+ */
14468
+ function truncateMessage(message, maxBytes) {
14469
+ if (message.length <= maxBytes) return message;
14470
+ return `${message.slice(0, maxBytes)}… [truncated ${message.length - maxBytes} bytes]`;
14471
+ }
14472
+ /**
14473
+ * Generate the inline script injected into the browser in dev mode.
14474
+ *
14475
+ * This script:
14476
+ * 1. Saves references to the original console methods
14477
+ * 2. Wraps `console.error`, `console.warn`, `console.info`
14478
+ * 3. Calls the original first (browser devtools still work)
14479
+ * 4. Serializes and forwards via `import.meta.hot.send()`
14480
+ * 5. Truncates messages to MAX_MESSAGE_BYTES
14481
+ *
14482
+ * The script is minimal and self-contained — no imports, no dependencies.
14483
+ */
14484
+ function generateClientScript(threshold) {
14485
+ if (threshold === "none") return "";
14486
+ const levels = [
14487
+ "error",
14488
+ "warn",
14489
+ "info"
14490
+ ].filter((l) => shouldForwardLevel(l, threshold));
14491
+ if (levels.length === 0) return "";
14492
+ return `
14493
+ (function() {
14494
+ if (!import.meta.hot) return;
14495
+ var MAX_BYTES = ${MAX_MESSAGE_BYTES};
14496
+ var levels = ${JSON.stringify(levels)};
14497
+ var originals = {};
14498
+
14499
+ function serialize(arg) {
14500
+ if (arg === null) return 'null';
14501
+ if (arg === undefined) return 'undefined';
14502
+ if (arg instanceof Error) return arg.stack || arg.message || String(arg);
14503
+ if (typeof arg === 'object') {
14504
+ try { return JSON.stringify(arg); } catch(e) { return String(arg); }
14505
+ }
14506
+ return String(arg);
14507
+ }
14508
+
14509
+ function truncate(s) {
14510
+ if (s.length <= MAX_BYTES) return s;
14511
+ return s.slice(0, MAX_BYTES) + '... [truncated ' + (s.length - MAX_BYTES) + ' bytes]';
14512
+ }
14513
+
14514
+ levels.forEach(function(level) {
14515
+ originals[level] = console[level];
14516
+ console[level] = function() {
14517
+ originals[level].apply(console, arguments);
14518
+ try {
14519
+ var args = Array.prototype.slice.call(arguments);
14520
+ var firstArg = args[0];
14521
+ var stack = null;
14522
+ var source = null;
14523
+
14524
+ if (firstArg instanceof Error) {
14525
+ stack = firstArg.stack || null;
14526
+ source = null;
14527
+ }
14528
+
14529
+ var message = args.map(serialize).join(' ');
14530
+ message = truncate(message);
14531
+
14532
+ import.meta.hot.send('${HMR_EVENT}', {
14533
+ level: level,
14534
+ message: message,
14535
+ stack: stack,
14536
+ source: source,
14537
+ timestamp: Date.now()
14538
+ });
14539
+ } catch(e) {
14540
+ // Never let log forwarding break the page
14541
+ }
14542
+ };
14543
+ });
14544
+ })();
14545
+ `.trim();
14546
+ }
14547
+ /**
14548
+ * Create the timber-dev-browser-logs Vite plugin.
14549
+ *
14550
+ * - `configureServer`: Listens for HMR messages and prints them to the terminal
14551
+ * - `transformIndexHtml`: Injects the client-side interception script
14552
+ *
14553
+ * Only active during `vite dev` (apply: 'serve').
14554
+ */
14555
+ function timberDevBrowserLogs(ctx) {
14556
+ return {
14557
+ name: "timber-dev-browser-logs",
14558
+ apply: "serve",
14559
+ configureServer(server) {
14560
+ const threshold = ctx.config.devBrowserLogs ?? "warn";
14561
+ if (threshold === "none") return;
14562
+ const script = generateClientScript(threshold);
14563
+ if (script) globalThis.__timber_dev_browser_log_script = `<script type="module">${script}<\/script>`;
14564
+ server.hot.on(HMR_EVENT, (payload) => {
14565
+ try {
14566
+ if (!shouldForwardLevel(payload.level, threshold)) return;
14567
+ payload.message = truncateMessage(payload.message, MAX_MESSAGE_BYTES);
14568
+ const formatted = formatBrowserLog(payload);
14569
+ if (payload.level === "error") process.stderr.write(formatted + "\n");
14570
+ else process.stdout.write(formatted + "\n");
14571
+ } catch {}
14572
+ });
14573
+ },
14574
+ transformIndexHtml() {
14575
+ const script = generateClientScript(ctx.config.devBrowserLogs ?? "warn");
14576
+ if (!script) return [];
14577
+ return [{
14578
+ tag: "script",
14579
+ attrs: { type: "module" },
14580
+ children: script,
14581
+ injectTo: "head"
14582
+ }];
14583
+ }
14584
+ };
14585
+ }
14586
+ //#endregion
14375
14587
  //#region src/plugins/react-prod.ts
14376
14588
  /**
14377
14589
  * Redirect React's CJS development bundles to production bundles.
@@ -14437,55 +14649,115 @@ function timberChunks() {
14437
14649
  return { name: "timber-chunks" };
14438
14650
  }
14439
14651
  //#endregion
14652
+ //#region src/plugins/client-chunks.ts
14653
+ /**
14654
+ * Client chunk grouping strategy for @vitejs/plugin-rsc.
14655
+ *
14656
+ * Groups client reference modules by layout boundary to balance route-scoped
14657
+ * code splitting with HTTP request count. A constant group name would collapse
14658
+ * all routes into one chunk (every page downloads every client component).
14659
+ * Per-serverChunk grouping creates many sub-500B files. Layout-boundary
14660
+ * grouping is the middle ground.
14661
+ *
14662
+ * See design/27-chunking-strategy.md, TIM-440, TIM-499.
14663
+ */
14664
+ /**
14665
+ * Derive a chunk group name for a client reference module.
14666
+ *
14667
+ * Groups by the first non-group route segment under appDir so that all
14668
+ * client components belonging to the same layout boundary land in one
14669
+ * chunk. For example:
14670
+ * - `facade:app/dashboard/settings/page.tsx` → `"client-dashboard"`
14671
+ * - `facade:app/(group-a)/group-page-a/page.tsx` → `"client-group-page-a"`
14672
+ * - `facade:app/layout.tsx` (root layout) → `"client-shared"`
14673
+ * - `shared:...` (shared across chunks) → `"client-shared"`
14674
+ *
14675
+ * This balances route-scoped code splitting with HTTP request count:
14676
+ * fewer chunks than per-serverChunk, but still avoids downloading every
14677
+ * client component on every page.
14678
+ */
14679
+ function clientChunkGroup(meta, appDir) {
14680
+ const { normalizedId, serverChunk } = meta;
14681
+ if (serverChunk.startsWith("shared:")) return "client-shared";
14682
+ const relPath = normalizedId.replace(/\\/g, "/");
14683
+ const appDirName = appDir.replace(/\\/g, "/").split("/").pop() || "app";
14684
+ const appIdx = relPath.indexOf(appDirName + "/");
14685
+ if (appIdx === -1) return "client-shared";
14686
+ const segments = relPath.slice(appIdx + appDirName.length + 1).split("/");
14687
+ for (const seg of segments) {
14688
+ if (seg.includes(".")) break;
14689
+ if (seg.startsWith("(") && seg.endsWith(")")) continue;
14690
+ return `client-${seg}`;
14691
+ }
14692
+ return "client-shared";
14693
+ }
14694
+ //#endregion
14440
14695
  //#region src/plugins/server-bundle.ts
14441
14696
  function timberServerBundle() {
14442
- return [{
14443
- name: "timber-server-bundle",
14444
- config(_cfg, { command }) {
14445
- if (command === "serve") return { environments: {
14446
- rsc: { resolve: { noExternal: ["server-only", "client-only"] } },
14447
- ssr: { resolve: { noExternal: [
14448
- "server-only",
14449
- "client-only",
14450
- "nuqs"
14451
- ] } }
14452
- } };
14453
- const serverDefine = { "process.env.NODE_ENV": JSON.stringify("production") };
14454
- return {
14455
- ssr: { target: "webworker" },
14456
- environments: {
14457
- rsc: {
14458
- resolve: { noExternal: true },
14459
- define: serverDefine
14460
- },
14461
- ssr: {
14462
- resolve: { noExternal: true },
14463
- define: serverDefine
14697
+ return [
14698
+ {
14699
+ name: "timber-server-bundle",
14700
+ config(_cfg, { command }) {
14701
+ if (command === "serve") return { environments: {
14702
+ rsc: { resolve: { noExternal: ["server-only", "client-only"] } },
14703
+ ssr: { resolve: { noExternal: [
14704
+ "server-only",
14705
+ "client-only",
14706
+ "nuqs"
14707
+ ] } }
14708
+ } };
14709
+ const serverDefine = { "process.env.NODE_ENV": JSON.stringify("production") };
14710
+ return {
14711
+ ssr: { target: "webworker" },
14712
+ environments: {
14713
+ rsc: {
14714
+ resolve: { noExternal: true },
14715
+ define: serverDefine
14716
+ },
14717
+ ssr: {
14718
+ resolve: { noExternal: true },
14719
+ define: serverDefine
14720
+ }
14464
14721
  }
14465
- }
14466
- };
14467
- }
14468
- }, {
14469
- name: "timber-esm-init-fix",
14470
- applyToEnvironment(environment) {
14471
- return environment.name === "rsc" || environment.name === "ssr";
14722
+ };
14723
+ }
14472
14724
  },
14473
- renderChunk(code) {
14474
- const lazy = "var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);";
14475
- if (!code.includes(lazy)) return null;
14476
- const eager = [
14477
- "var __esmMin = (fn, res) => {",
14478
- " var l = () => { if (fn) { var f = fn; try { res = f(); fn = 0; } catch(e) {} } return res; };",
14479
- " l();",
14480
- " return l;",
14481
- "};"
14482
- ].join(" ");
14483
- return {
14484
- code: code.replace(lazy, eager),
14485
- map: null
14486
- };
14725
+ {
14726
+ name: "timber-esm-init-fix",
14727
+ applyToEnvironment(environment) {
14728
+ return environment.name === "rsc" || environment.name === "ssr";
14729
+ },
14730
+ renderChunk(code) {
14731
+ const lazy = "var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);";
14732
+ if (!code.includes(lazy)) return null;
14733
+ const eager = [
14734
+ "var __esmMin = (fn, res) => {",
14735
+ " var l = () => { if (fn) { var f = fn; try { res = f(); fn = 0; } catch(e) {} } return res; };",
14736
+ " l();",
14737
+ " return l;",
14738
+ "};"
14739
+ ].join(" ");
14740
+ return {
14741
+ code: code.replace(lazy, eager),
14742
+ map: null
14743
+ };
14744
+ }
14745
+ },
14746
+ {
14747
+ name: "timber-create-require-fix",
14748
+ applyToEnvironment(environment) {
14749
+ return environment.name === "rsc" || environment.name === "ssr";
14750
+ },
14751
+ renderChunk(code) {
14752
+ const pattern = "createRequire(import.meta.url)";
14753
+ if (!code.includes(pattern)) return null;
14754
+ return {
14755
+ code: code.replace(pattern, "createRequire(import.meta.url || \"file:///app\")"),
14756
+ map: null
14757
+ };
14758
+ }
14487
14759
  }
14488
- }];
14760
+ ];
14489
14761
  }
14490
14762
  //#endregion
14491
14763
  //#region src/plugins/adapter-build.ts
@@ -14507,6 +14779,7 @@ function timberAdapterBuild(ctx) {
14507
14779
  modulepreload: {}
14508
14780
  } : ctx.buildManifest;
14509
14781
  manifestInit = `globalThis.__TIMBER_BUILD_MANIFEST__ = ${JSON.stringify(manifest)};\n`;
14782
+ if (ctx.deploymentId) manifestInit += `globalThis.__TIMBER_DEPLOYMENT_ID__ = ${JSON.stringify(ctx.deploymentId)};\n`;
14510
14783
  }
14511
14784
  if (ctx.clientJavascript.disabled) await stripJsFromRscAssetsManifests(buildDir);
14512
14785
  const adapterConfig = {
@@ -14863,6 +15136,65 @@ function createNoopTimer() {
14863
15136
  };
14864
15137
  }
14865
15138
  //#endregion
15139
+ //#region src/server/action-encryption.ts
15140
+ /**
15141
+ * Regex for safe `defineEncryptionKey` expressions.
15142
+ *
15143
+ * The RSC plugin inlines this expression verbatim into generated JavaScript.
15144
+ * We restrict it to `process.env.<UPPER_SNAKE_CASE>` to prevent code injection.
15145
+ * See "Known Security Considerations" at the top of this file.
15146
+ */
15147
+ var SAFE_KEY_EXPR = /^process\.env\.[A-Z_][A-Z0-9_]*$/;
15148
+ /**
15149
+ * Build the `defineEncryptionKey` expression for the RSC plugin.
15150
+ *
15151
+ * The RSC plugin accepts a JavaScript expression string that will be
15152
+ * inlined into the encryption runtime module. At runtime, this expression
15153
+ * must evaluate to the base64-encoded encryption key.
15154
+ *
15155
+ * Priority:
15156
+ * 1. `TIMBER_ACTIONS_ENCRYPTION_KEY` env var (for cross-build key sharing
15157
+ * in rolling/blue-green deployments)
15158
+ * 2. Auto-generated at build time (RSC plugin default — embedded in bundle,
15159
+ * consistent across all instances of the same build)
15160
+ *
15161
+ * For env var keys, we generate a runtime expression that reads the env var.
15162
+ * For auto-generated keys, we return undefined and let the RSC plugin handle it.
15163
+ */
15164
+ function resolveEncryptionKeyExpression() {
15165
+ const envKey = process.env.TIMBER_ACTIONS_ENCRYPTION_KEY;
15166
+ if (envKey) {
15167
+ validateKeyFormat(envKey);
15168
+ const expr = "process.env.TIMBER_ACTIONS_ENCRYPTION_KEY";
15169
+ if (!SAFE_KEY_EXPR.test(expr)) throw new Error(`Unsafe encryption key expression: ${expr}`);
15170
+ return expr;
15171
+ }
15172
+ }
15173
+ /**
15174
+ * Determine whether action encryption should be enabled.
15175
+ *
15176
+ * Encryption is always enabled in production. In dev mode, it's enabled
15177
+ * by default but can be disabled via config for debugging.
15178
+ */
15179
+ function shouldEnableEncryption(isDev, config) {
15180
+ if (!isDev) return true;
15181
+ if (config?.disableInDev) return false;
15182
+ return true;
15183
+ }
15184
+ /**
15185
+ * Validate that a key string is a valid base64-encoded 256-bit key.
15186
+ * Throws a descriptive error if the key is malformed.
15187
+ */
15188
+ function validateKeyFormat(key) {
15189
+ try {
15190
+ const bytes = atob(key).length;
15191
+ 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'))"`);
15192
+ } catch (error) {
15193
+ if (error instanceof Error && error.message.includes("TIMBER_ACTIONS_ENCRYPTION_KEY")) throw error;
15194
+ 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'))\"");
15195
+ }
15196
+ }
15197
+ //#endregion
14866
15198
  //#region src/index.ts
14867
15199
  /**
14868
15200
  * Resolve `clientJavascript` into a fully resolved config.
@@ -14905,10 +15237,7 @@ function resolveAppDir(root, configAppDir) {
14905
15237
  }
14906
15238
  function createPluginContext(config, root) {
14907
15239
  const projectRoot = root ?? process.cwd();
14908
- const resolvedConfig = {
14909
- output: "server",
14910
- ...config
14911
- };
15240
+ const resolvedConfig = { ...config };
14912
15241
  return {
14913
15242
  config: resolvedConfig,
14914
15243
  clientJavascript: resolveClientJavascript(resolvedConfig),
@@ -14917,22 +15246,28 @@ function createPluginContext(config, root) {
14917
15246
  root: projectRoot,
14918
15247
  dev: false,
14919
15248
  buildManifest: null,
15249
+ deploymentId: null,
14920
15250
  timer: createStartupTimer()
14921
15251
  };
14922
15252
  }
14923
15253
  /**
14924
15254
  * Load timber.config.ts (or .js, .mjs) from the project root.
14925
15255
  * Returns the config object or null if no config file is found.
15256
+ *
15257
+ * Uses require() which works for ESM modules on Node 22.12+.
15258
+ * This keeps timber() synchronous — no async config loading needed.
14926
15259
  */
14927
- async function loadTimberConfigFile(root) {
14928
- for (const name of [
15260
+ function loadTimberConfigFile(root) {
15261
+ const configNames = [
14929
15262
  "timber.config.ts",
14930
15263
  "timber.config.js",
14931
15264
  "timber.config.mjs"
14932
- ]) {
15265
+ ];
15266
+ const req = createRequire(join(root, "package.json"));
15267
+ for (const name of configNames) {
14933
15268
  const configPath = join(root, name);
14934
15269
  if (existsSync(configPath)) {
14935
- const mod = await import(pathToFileURL(configPath).href);
15270
+ const mod = req(configPath);
14936
15271
  return mod.default ?? mod;
14937
15272
  }
14938
15273
  }
@@ -14947,10 +15282,7 @@ async function loadTimberConfigFile(root) {
14947
15282
  */
14948
15283
  function warnConfigConflicts(inline, fileConfig) {
14949
15284
  const conflicts = [];
14950
- for (const key of Object.keys(fileConfig)) {
14951
- if (key === "output") continue;
14952
- if (key in inline && inline[key] !== void 0) conflicts.push(key);
14953
- }
15285
+ for (const key of Object.keys(fileConfig)) if (key in inline && inline[key] !== void 0) conflicts.push(key);
14954
15286
  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.`);
14955
15287
  return conflicts;
14956
15288
  }
@@ -14978,21 +15310,92 @@ function mergeFileConfig(ctx, fileConfig) {
14978
15310
  } } : {}
14979
15311
  };
14980
15312
  }
15313
+ /**
15314
+ * Resolve the React Compiler plugin via @rolldown/plugin-babel.
15315
+ *
15316
+ * Uses the `reactCompilerPreset` from @vitejs/plugin-react, which:
15317
+ * - Uses Babel ONLY for the compiler pass (OXC handles JSX)
15318
+ * - Automatically scopes to client environment via applyToEnvironmentHook
15319
+ * - Uses react/compiler-runtime built into React 19
15320
+ *
15321
+ * @rolldown/plugin-babel and babel-plugin-react-compiler are optional peer deps.
15322
+ * If either is missing, require() fails with a clear error message.
15323
+ */
15324
+ function resolveReactCompilerPlugin(config, req) {
15325
+ let babel;
15326
+ try {
15327
+ babel = req("@rolldown/plugin-babel");
15328
+ } catch {
15329
+ throw new Error("[timber] reactCompiler requires @rolldown/plugin-babel. Install it: pnpm add -D @rolldown/plugin-babel babel-plugin-react-compiler");
15330
+ }
15331
+ const options = typeof config === "object" ? config : {};
15332
+ return (babel.default ?? babel)({ presets: [reactCompilerPreset(options)] });
15333
+ }
15334
+ /**
15335
+ * Build the options object for @vitejs/plugin-rsc.
15336
+ *
15337
+ * Uses a getter for `enableActionEncryption` so the RSC plugin reads
15338
+ * the value lazily — after ctx.dev is set in configResolved. This lets
15339
+ * `actionEncryption.disableInDev` work correctly even though the RSC
15340
+ * plugin is created before Vite resolves the command.
15341
+ */
15342
+ function createRscOptions(ctx, encryptionKeyExpr) {
15343
+ const options = {
15344
+ serverHandler: false,
15345
+ customClientEntry: true,
15346
+ entries: {
15347
+ rsc: "virtual:timber-rsc-entry",
15348
+ ssr: "virtual:timber-ssr-entry",
15349
+ client: "virtual:timber-browser-entry"
15350
+ },
15351
+ clientChunks: (meta) => clientChunkGroup(meta, ctx.appDir)
15352
+ };
15353
+ Object.defineProperty(options, "enableActionEncryption", {
15354
+ get() {
15355
+ return shouldEnableEncryption(ctx.dev, ctx.config.actionEncryption);
15356
+ },
15357
+ enumerable: true
15358
+ });
15359
+ if (encryptionKeyExpr) options.defineEncryptionKey = encryptionKeyExpr;
15360
+ return options;
15361
+ }
14981
15362
  function timberCache(_ctx) {
14982
15363
  return cacheTransformPlugin();
14983
15364
  }
15365
+ /**
15366
+ * Create the timber Vite plugin array.
15367
+ *
15368
+ * Loads timber.config.ts and all dependencies synchronously before
15369
+ * constructing the plugin array. This ensures ALL plugins — including
15370
+ * the RSC plugin and React Compiler — see the fully merged config
15371
+ * (inline + file-based). No async, no deferred config, no stale reads.
15372
+ *
15373
+ * Requires Node >= 22.12 for synchronous require() of ESM modules
15374
+ * (@vitejs/plugin-rsc is ESM-only).
15375
+ *
15376
+ * Previous versions used async loading and deferred config merging,
15377
+ * causing file-based config for reactCompiler, actionEncryption, and
15378
+ * output mode to be silently ignored. See TIM-451.
15379
+ */
14984
15380
  function timber(config) {
14985
15381
  const ctx = createPluginContext(config);
15382
+ const consumerRequire = createRequire(join(process.cwd(), "package.json"));
15383
+ ctx.timer.start("rsc-plugin-import");
15384
+ const rscMod = consumerRequire("@vitejs/plugin-rsc");
15385
+ const vitePluginRsc = rscMod.default ?? rscMod;
15386
+ ctx.timer.end("rsc-plugin-import");
15387
+ const encryptionKeyExpr = resolveEncryptionKeyExpression();
14986
15388
  const rootSync = {
14987
15389
  name: "timber-root-sync",
14988
- async config(userConfig, { command }) {
14989
- const root = userConfig.root ?? process.cwd();
15390
+ config(userConfig, { command }) {
15391
+ const viteRoot = resolve(userConfig.root ?? process.cwd());
14990
15392
  ctx.timer.start("config-load");
14991
- const fileConfig = await loadTimberConfigFile(root);
15393
+ const fileConfig = loadTimberConfigFile(viteRoot);
14992
15394
  if (fileConfig) {
14993
15395
  mergeFileConfig(ctx, fileConfig);
14994
15396
  ctx.clientJavascript = resolveClientJavascript(ctx.config);
14995
15397
  }
15398
+ ctx.config.output ??= "server";
14996
15399
  ctx.timer.end("config-load");
14997
15400
  if (command === "build") return { oxc: { jsx: { development: false } } };
14998
15401
  },
@@ -15004,33 +15407,31 @@ function timber(config) {
15004
15407
  else ctx.timer.start("dev-server-setup");
15005
15408
  }
15006
15409
  };
15007
- const rscPluginPath = createRequire(join(process.cwd(), "package.json")).resolve("@vitejs/plugin-rsc");
15008
- ctx.timer.start("rsc-plugin-import");
15009
- const rscPluginsPromise = import(pathToFileURL(rscPluginPath).href).then(({ default: vitePluginRsc }) => {
15010
- ctx.timer.end("rsc-plugin-import");
15011
- return vitePluginRsc({
15012
- serverHandler: false,
15013
- customClientEntry: true,
15014
- entries: {
15015
- rsc: "virtual:timber-rsc-entry",
15016
- ssr: "virtual:timber-ssr-entry",
15017
- client: "virtual:timber-browser-entry"
15018
- }
15019
- });
15020
- });
15410
+ const reactCompilerPlugins = [];
15411
+ if (config?.reactCompiler) reactCompilerPlugins.push(resolveReactCompilerPlugin(config.reactCompiler, consumerRequire));
15412
+ const lazyReactCompiler = {
15413
+ name: "timber-react-compiler",
15414
+ configResolved() {
15415
+ if (config?.reactCompiler || !ctx.config.reactCompiler) return;
15416
+ const resolved = resolveReactCompilerPlugin(ctx.config.reactCompiler, consumerRequire);
15417
+ for (const key of Object.keys(resolved)) if (key !== "name") lazyReactCompiler[key] = resolved[key];
15418
+ }
15419
+ };
15420
+ // @vitejs/plugin-rsc handles:
15021
15421
  return [
15022
15422
  rootSync,
15023
15423
  timberReactProd(),
15024
15424
  react(),
15425
+ ...reactCompilerPlugins,
15426
+ lazyReactCompiler,
15025
15427
  timberServerActionExports(),
15026
- rscPluginsPromise,
15428
+ vitePluginRsc(createRscOptions(ctx, encryptionKeyExpr)),
15027
15429
  timberShims(ctx),
15028
15430
  timberRouting(ctx),
15029
15431
  timberEntries(ctx),
15030
15432
  timberBuildManifest(ctx),
15031
15433
  timberCache(ctx),
15032
15434
  timberStaticBuild(ctx),
15033
- timberDynamicTransform(ctx),
15034
15435
  timberFonts(ctx),
15035
15436
  timberMdx(ctx),
15036
15437
  timberContent(ctx),
@@ -15039,10 +15440,32 @@ function timber(config) {
15039
15440
  timberBuildReport(ctx),
15040
15441
  timberAdapterBuild(ctx),
15041
15442
  timberDevLogs(ctx),
15443
+ timberDevBrowserLogs(ctx),
15042
15444
  timberDevServer(ctx)
15043
15445
  ];
15044
15446
  }
15447
+ /**
15448
+ * Type-safe helper for timber.config.ts files.
15449
+ *
15450
+ * A pass-through identity function that provides autocomplete and
15451
+ * type checking for timber configuration. No runtime validation —
15452
+ * purely a DX convenience (same pattern as Vite's defineConfig).
15453
+ *
15454
+ * @example
15455
+ * ```ts
15456
+ * // timber.config.ts
15457
+ * import { defineConfig } from '@timber-js/app';
15458
+ *
15459
+ * export default defineConfig({
15460
+ * output: 'server',
15461
+ * pageExtensions: ['tsx', 'ts', 'mdx'],
15462
+ * });
15463
+ * ```
15464
+ */
15465
+ function defineConfig(config) {
15466
+ return config;
15467
+ }
15045
15468
  //#endregion
15046
- export { timber as default, timber, resolveAppDir, resolveClientJavascript, warnConfigConflicts };
15469
+ export { timber as default, timber, defineConfig, loadTimberConfigFile, resolveAppDir, resolveClientJavascript, warnConfigConflicts };
15047
15470
 
15048
15471
  //# sourceMappingURL=index.js.map