@timber-js/app 0.2.0-alpha.207 → 0.2.0-alpha.209

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (526) hide show
  1. package/dist/_chunks/{primitives-DLAnvsrk.js → actions-BerlqoXA.js} +640 -410
  2. package/dist/_chunks/actions-BerlqoXA.js.map +1 -0
  3. package/dist/_chunks/{als-registry-C6kcfprT.js → als-registry-DaxkVjt5.js} +6 -2
  4. package/dist/_chunks/als-registry-DaxkVjt5.js.map +1 -0
  5. package/dist/_chunks/{build-manifest-DTmSGLRz.js → build-manifest-DWppEdLB.js} +2 -51
  6. package/dist/_chunks/build-manifest-DWppEdLB.js.map +1 -0
  7. package/dist/_chunks/{build-output-helper-9dqCv1pZ.js → build-output-helper-RV_BRcF1.js} +50 -53
  8. package/dist/_chunks/build-output-helper-RV_BRcF1.js.map +1 -0
  9. package/dist/_chunks/{cache-api-CUD9Ezaq.js → cache-api-CR23J_NC.js} +64 -32
  10. package/dist/_chunks/cache-api-CR23J_NC.js.map +1 -0
  11. package/dist/_chunks/{chains-B4bgOl2g.js → chains-CpFg56UB.js} +2 -2
  12. package/dist/_chunks/{chains-B4bgOl2g.js.map → chains-CpFg56UB.js.map} +1 -1
  13. package/dist/_chunks/{cli-check-G8o30ldg.js → cli-check-C6Ev6wBO.js} +11 -52
  14. package/dist/_chunks/cli-check-C6Ev6wBO.js.map +1 -0
  15. package/dist/_chunks/cli-context-By7k2xVo.js +70 -0
  16. package/dist/_chunks/cli-context-By7k2xVo.js.map +1 -0
  17. package/dist/_chunks/{cli-schema-sync--LZKs8Zn.js → cli-schema-sync-CbT2AUUI.js} +3 -3
  18. package/dist/_chunks/{cli-schema-sync--LZKs8Zn.js.map → cli-schema-sync-CbT2AUUI.js.map} +1 -1
  19. package/dist/_chunks/{cloudflare-BFb__LYG.js → cloudflare-Byxl8FWP.js} +96 -53
  20. package/dist/_chunks/{cloudflare-BFb__LYG.js.map → cloudflare-Byxl8FWP.js.map} +1 -1
  21. package/dist/_chunks/{convention-lint-5KzoMg_A.js → convention-lint-jKTwKwPe.js} +21 -6
  22. package/dist/_chunks/convention-lint-jKTwKwPe.js.map +1 -0
  23. package/dist/_chunks/{dev-server-BzBxgYAV.js → dev-server-C4WZdB7L.js} +15 -167
  24. package/dist/_chunks/dev-server-C4WZdB7L.js.map +1 -0
  25. package/dist/_chunks/{error-boundary-BndF-3Td.js → error-boundary-tA7kVfs4.js} +84 -104
  26. package/dist/_chunks/error-boundary-tA7kVfs4.js.map +1 -0
  27. package/dist/_chunks/{fs-identity-D7vb6GdI.js → fs-identity-BRKuL215.js} +3 -4
  28. package/dist/_chunks/fs-identity-BRKuL215.js.map +1 -0
  29. package/dist/_chunks/{graph-cache-CP4GEmf9.js → graph-cache-CYiUS2aS.js} +4 -5
  30. package/dist/_chunks/graph-cache-CYiUS2aS.js.map +1 -0
  31. package/dist/_chunks/{json-lossy-check-ClNvBM_3.js → json-lossy-check-ip0Qi0MT.js} +6 -3
  32. package/dist/_chunks/json-lossy-check-ip0Qi0MT.js.map +1 -0
  33. package/dist/_chunks/{live-graph-Bm5WTvcr.js → live-graph-Dv-JJCZw.js} +3 -3
  34. package/dist/_chunks/{live-graph-Bm5WTvcr.js.map → live-graph-Dv-JJCZw.js.map} +1 -1
  35. package/dist/_chunks/{logger-k2DQ4EUf.js → logger-DiDt5ppH.js} +192 -51
  36. package/dist/_chunks/logger-DiDt5ppH.js.map +1 -0
  37. package/dist/_chunks/{navigation-root-mHSK9psY.js → navigation-root-BQfo1-kG.js} +2 -2
  38. package/dist/_chunks/{navigation-root-mHSK9psY.js.map → navigation-root-BQfo1-kG.js.map} +1 -1
  39. package/dist/_chunks/{plugin-context-8idfR3UZ.js → plugin-context-BPBPc3lL.js} +28 -4
  40. package/dist/_chunks/plugin-context-BPBPc3lL.js.map +1 -0
  41. package/dist/_chunks/{poison-scan-DG5eI2_z.js → poison-scan-CpeT6_OJ.js} +2 -2
  42. package/dist/_chunks/{poison-scan-DG5eI2_z.js.map → poison-scan-CpeT6_OJ.js.map} +1 -1
  43. package/dist/_chunks/render-timeout-DOf_BBcO.js +46 -0
  44. package/dist/_chunks/render-timeout-DOf_BBcO.js.map +1 -0
  45. package/dist/_chunks/{rsc-payload-path-B_LBodc2.js → rsc-payload-path-dffcs4jp.js} +9 -3
  46. package/dist/_chunks/{rsc-payload-path-B_LBodc2.js.map → rsc-payload-path-dffcs4jp.js.map} +1 -1
  47. package/dist/_chunks/{scanner-BQ5sPIJ7.js → scanner-Bw0oq1HB.js} +2 -2
  48. package/dist/_chunks/{scanner-BQ5sPIJ7.js.map → scanner-Bw0oq1HB.js.map} +1 -1
  49. package/dist/_chunks/{navigation-context-D0TU0Jog.js → segment-context-xtPUGfdq.js} +36 -34
  50. package/dist/_chunks/segment-context-xtPUGfdq.js.map +1 -0
  51. package/dist/_chunks/{singleflight-2lUWfcAk.js → singleflight-BeVMMKi0.js} +4 -3
  52. package/dist/_chunks/{singleflight-2lUWfcAk.js.map → singleflight-BeVMMKi0.js.map} +1 -1
  53. package/dist/_chunks/{sizeof-lXffQ1Vr.js → sizeof-UwzwB1uM.js} +3 -2
  54. package/dist/_chunks/sizeof-UwzwB1uM.js.map +1 -0
  55. package/dist/_chunks/ssr-data-nA3I70_n.js +49 -0
  56. package/dist/_chunks/ssr-data-nA3I70_n.js.map +1 -0
  57. package/dist/_chunks/{state-FippDgxN.js → state-BhNGPsdi.js} +6 -8
  58. package/dist/_chunks/state-BhNGPsdi.js.map +1 -0
  59. package/dist/_chunks/{status-page-marker-DwQBrLBz.js → status-page-marker-gaihi0KZ.js} +140 -28
  60. package/dist/_chunks/status-page-marker-gaihi0KZ.js.map +1 -0
  61. package/dist/_chunks/{walkers-CN2N_bA-.js → walkers-BXExhzzk.js} +2 -2
  62. package/dist/_chunks/{walkers-CN2N_bA-.js.map → walkers-BXExhzzk.js.map} +1 -1
  63. package/dist/_chunks/write-file-atomic-D1qWqHb4.js +32 -0
  64. package/dist/_chunks/write-file-atomic-D1qWqHb4.js.map +1 -0
  65. package/dist/adapters/build-output-helper.d.ts +7 -7
  66. package/dist/adapters/build-output-helper.d.ts.map +1 -1
  67. package/dist/adapters/canonicalize-module.d.ts +8 -0
  68. package/dist/adapters/canonicalize-module.d.ts.map +1 -0
  69. package/dist/adapters/cloudflare-dev.js +1 -1
  70. package/dist/adapters/cloudflare-kv-cache.js +2 -2
  71. package/dist/adapters/cloudflare.d.ts +30 -8
  72. package/dist/adapters/cloudflare.d.ts.map +1 -1
  73. package/dist/adapters/cloudflare.js +2 -2
  74. package/dist/adapters/compress-module.d.ts +8 -0
  75. package/dist/adapters/compress-module.d.ts.map +1 -1
  76. package/dist/adapters/fs-identity.d.ts +2 -3
  77. package/dist/adapters/fs-identity.d.ts.map +1 -1
  78. package/dist/adapters/nitro-presets.d.ts +0 -6
  79. package/dist/adapters/nitro-presets.d.ts.map +1 -1
  80. package/dist/adapters/nitro-preview.d.ts +43 -11
  81. package/dist/adapters/nitro-preview.d.ts.map +1 -1
  82. package/dist/adapters/nitro.d.ts +1 -13
  83. package/dist/adapters/nitro.d.ts.map +1 -1
  84. package/dist/adapters/nitro.js +291 -176
  85. package/dist/adapters/nitro.js.map +1 -1
  86. package/dist/adapters/shared.d.ts +7 -2
  87. package/dist/adapters/shared.d.ts.map +1 -1
  88. package/dist/adapters/types.d.ts +105 -21
  89. package/dist/adapters/types.d.ts.map +1 -1
  90. package/dist/adapters/types.js +0 -0
  91. package/dist/analyze/crawl-entry.js +3 -3
  92. package/dist/analyze/graph-cache.d.ts.map +1 -1
  93. package/dist/analyze/graph-command.js +4 -4
  94. package/dist/cache/index.js +2 -2
  95. package/dist/cache/json-lossy-check.d.ts.map +1 -1
  96. package/dist/cache/singleflight.d.ts.map +1 -1
  97. package/dist/cache/sizeof.d.ts.map +1 -1
  98. package/dist/cache/stores/memory.js +1 -1
  99. package/dist/cache/tag-aware-handler.d.ts.map +1 -1
  100. package/dist/cdn/workers-cache-purge.js +1 -1
  101. package/dist/cli-check.d.ts +7 -37
  102. package/dist/cli-check.d.ts.map +1 -1
  103. package/dist/cli-context.d.ts +54 -0
  104. package/dist/cli-context.d.ts.map +1 -0
  105. package/dist/cli.d.ts +9 -9
  106. package/dist/cli.d.ts.map +1 -1
  107. package/dist/cli.js +20 -24
  108. package/dist/cli.js.map +1 -1
  109. package/dist/client/browser-dev.d.ts +1 -1
  110. package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -1
  111. package/dist/client/browser-entry/hydrate.d.ts +19 -29
  112. package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
  113. package/dist/client/browser-entry/index.d.ts +7 -7
  114. package/dist/client/browser-entry/post-hydration.d.ts +6 -9
  115. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
  116. package/dist/client/browser-entry/router-init.d.ts +6 -11
  117. package/dist/client/browser-entry/router-init.d.ts.map +1 -1
  118. package/dist/client/error-boundary.d.ts +4 -3
  119. package/dist/client/error-boundary.d.ts.map +1 -1
  120. package/dist/client/error-boundary.js +1 -1
  121. package/dist/client/error-reconstituter.d.ts +3 -9
  122. package/dist/client/error-reconstituter.d.ts.map +1 -1
  123. package/dist/client/history.d.ts +20 -11
  124. package/dist/client/history.d.ts.map +1 -1
  125. package/dist/client/index.d.ts +0 -1
  126. package/dist/client/index.d.ts.map +1 -1
  127. package/dist/client/index.js +5 -6
  128. package/dist/client/index.js.map +1 -1
  129. package/dist/client/internal.d.ts +3 -3
  130. package/dist/client/internal.d.ts.map +1 -1
  131. package/dist/client/internal.js +95 -108
  132. package/dist/client/internal.js.map +1 -1
  133. package/dist/client/navigation-commit.d.ts +20 -7
  134. package/dist/client/navigation-commit.d.ts.map +1 -1
  135. package/dist/client/navigation-context.d.ts +3 -5
  136. package/dist/client/navigation-context.d.ts.map +1 -1
  137. package/dist/client/react-root.d.ts +3 -2
  138. package/dist/client/react-root.d.ts.map +1 -1
  139. package/dist/client/router-pipeline.d.ts +3 -8
  140. package/dist/client/router-pipeline.d.ts.map +1 -1
  141. package/dist/client/router-types.d.ts +45 -39
  142. package/dist/client/router-types.d.ts.map +1 -1
  143. package/dist/client/router-url.d.ts +24 -0
  144. package/dist/client/router-url.d.ts.map +1 -0
  145. package/dist/client/router.d.ts.map +1 -1
  146. package/dist/client/rsc-fetch.d.ts +8 -4
  147. package/dist/client/rsc-fetch.d.ts.map +1 -1
  148. package/dist/client/segment-cache.d.ts +44 -4
  149. package/dist/client/segment-cache.d.ts.map +1 -1
  150. package/dist/client/ssr-data.d.ts +18 -64
  151. package/dist/client/ssr-data.d.ts.map +1 -1
  152. package/dist/client/state.d.ts +2 -5
  153. package/dist/client/state.d.ts.map +1 -1
  154. package/dist/client/use-pathname.d.ts +1 -1
  155. package/dist/config-types.d.ts +2 -1
  156. package/dist/config-types.d.ts.map +1 -1
  157. package/dist/config-validation.d.ts.map +1 -1
  158. package/dist/cookies/index.js +1 -1
  159. package/dist/dev-tools/dev-page-shell.d.ts +17 -0
  160. package/dist/dev-tools/dev-page-shell.d.ts.map +1 -1
  161. package/dist/dev-tools/index.d.ts +1 -1
  162. package/dist/dev-tools/index.d.ts.map +1 -1
  163. package/dist/dev-tools/instrumentation.d.ts +17 -2
  164. package/dist/dev-tools/instrumentation.d.ts.map +1 -1
  165. package/dist/dev-tools/overlay.d.ts +6 -27
  166. package/dist/dev-tools/overlay.d.ts.map +1 -1
  167. package/dist/index.d.ts.map +1 -1
  168. package/dist/index.js +632 -373
  169. package/dist/index.js.map +1 -1
  170. package/dist/plugin-context.d.ts +26 -40
  171. package/dist/plugin-context.d.ts.map +1 -1
  172. package/dist/plugins/build-manifest.d.ts.map +1 -1
  173. package/dist/plugins/build-report.d.ts +15 -2
  174. package/dist/plugins/build-report.d.ts.map +1 -1
  175. package/dist/plugins/dev-server.d.ts.map +1 -1
  176. package/dist/plugins/entries.d.ts.map +1 -1
  177. package/dist/plugins/fonts.d.ts +19 -1
  178. package/dist/plugins/fonts.d.ts.map +1 -1
  179. package/dist/plugins/post-build.d.ts +110 -0
  180. package/dist/plugins/post-build.d.ts.map +1 -0
  181. package/dist/plugins/prebuilt-capture.d.ts +11 -4
  182. package/dist/plugins/prebuilt-capture.d.ts.map +1 -1
  183. package/dist/plugins/prebuilt.d.ts +17 -1
  184. package/dist/plugins/prebuilt.d.ts.map +1 -1
  185. package/dist/plugins/prerender-sugar.d.ts.map +1 -1
  186. package/dist/plugins/react-resolve.d.ts +49 -0
  187. package/dist/plugins/react-resolve.d.ts.map +1 -0
  188. package/dist/plugins/rsc-assets-manifest.d.ts +33 -0
  189. package/dist/plugins/rsc-assets-manifest.d.ts.map +1 -0
  190. package/dist/plugins/shims.d.ts.map +1 -1
  191. package/dist/plugins/static-build.d.ts +32 -9
  192. package/dist/plugins/static-build.d.ts.map +1 -1
  193. package/dist/routing/codegen-write.d.ts +0 -9
  194. package/dist/routing/codegen-write.d.ts.map +1 -1
  195. package/dist/routing/index.js +2 -2
  196. package/dist/routing/segment-codegen.d.ts.map +1 -1
  197. package/dist/server/access-gate.d.ts.map +1 -1
  198. package/dist/server/action-handler.d.ts +5 -4
  199. package/dist/server/action-handler.d.ts.map +1 -1
  200. package/dist/server/actions.d.ts +46 -7
  201. package/dist/server/actions.d.ts.map +1 -1
  202. package/dist/server/als-registry.d.ts +58 -5
  203. package/dist/server/als-registry.d.ts.map +1 -1
  204. package/dist/server/children-interception.d.ts.map +1 -1
  205. package/dist/server/cookie-context.d.ts +2 -23
  206. package/dist/server/cookie-context.d.ts.map +1 -1
  207. package/dist/server/csp-nonce.d.ts +0 -9
  208. package/dist/server/csp-nonce.d.ts.map +1 -1
  209. package/dist/server/csrf.d.ts +27 -4
  210. package/dist/server/csrf.d.ts.map +1 -1
  211. package/dist/server/debug.d.ts +0 -1
  212. package/dist/server/debug.d.ts.map +1 -1
  213. package/dist/server/default-logger.d.ts +2 -1
  214. package/dist/server/default-logger.d.ts.map +1 -1
  215. package/dist/server/deny-boundary.d.ts +12 -6
  216. package/dist/server/deny-boundary.d.ts.map +1 -1
  217. package/dist/server/deny-renderer.d.ts +2 -2
  218. package/dist/server/deny-renderer.d.ts.map +1 -1
  219. package/dist/server/error-boundary-wrapper.d.ts +7 -3
  220. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  221. package/dist/server/fallback-error.d.ts +9 -2
  222. package/dist/server/fallback-error.d.ts.map +1 -1
  223. package/dist/server/head-response.d.ts +4 -4
  224. package/dist/server/html-injectors.d.ts.map +1 -1
  225. package/dist/server/index.d.ts +0 -1
  226. package/dist/server/index.d.ts.map +1 -1
  227. package/dist/server/index.js +3 -4
  228. package/dist/server/index.js.map +1 -1
  229. package/dist/server/instrumentation.d.ts +10 -3
  230. package/dist/server/instrumentation.d.ts.map +1 -1
  231. package/dist/server/internal.d.ts +1 -4
  232. package/dist/server/internal.d.ts.map +1 -1
  233. package/dist/server/internal.js +961 -581
  234. package/dist/server/internal.js.map +1 -1
  235. package/dist/server/json-status-response.d.ts +34 -0
  236. package/dist/server/json-status-response.d.ts.map +1 -0
  237. package/dist/server/logger.d.ts +26 -0
  238. package/dist/server/logger.d.ts.map +1 -1
  239. package/dist/server/param-coercion.d.ts +11 -6
  240. package/dist/server/param-coercion.d.ts.map +1 -1
  241. package/dist/server/pipeline-helpers.d.ts +95 -7
  242. package/dist/server/pipeline-helpers.d.ts.map +1 -1
  243. package/dist/server/pipeline-interception.d.ts +11 -0
  244. package/dist/server/pipeline-interception.d.ts.map +1 -1
  245. package/dist/server/pipeline-outcome.d.ts +11 -1
  246. package/dist/server/pipeline-outcome.d.ts.map +1 -1
  247. package/dist/server/pipeline-phases.d.ts +2 -2
  248. package/dist/server/pipeline-phases.d.ts.map +1 -1
  249. package/dist/server/pipeline.d.ts +23 -35
  250. package/dist/server/pipeline.d.ts.map +1 -1
  251. package/dist/server/prebuilt/capture-render.d.ts +25 -0
  252. package/dist/server/prebuilt/capture-render.d.ts.map +1 -0
  253. package/dist/server/prebuilt/capture-routes.d.ts +37 -0
  254. package/dist/server/prebuilt/capture-routes.d.ts.map +1 -0
  255. package/dist/server/prebuilt/capture-state.d.ts +79 -35
  256. package/dist/server/prebuilt/capture-state.d.ts.map +1 -1
  257. package/dist/server/prebuilt/lifetime.d.ts +38 -0
  258. package/dist/server/prebuilt/lifetime.d.ts.map +1 -0
  259. package/dist/server/prebuilt/slots.d.ts +7 -0
  260. package/dist/server/prebuilt/slots.d.ts.map +1 -1
  261. package/dist/server/prebuilt/synthetic-store.d.ts.map +1 -1
  262. package/dist/server/prebuilt-builder.d.ts.map +1 -1
  263. package/dist/server/prebuilt-runtime.d.ts.map +1 -1
  264. package/dist/server/primitives.d.ts +6 -81
  265. package/dist/server/primitives.d.ts.map +1 -1
  266. package/dist/server/react-cache-bridge.d.ts +59 -0
  267. package/dist/server/react-cache-bridge.d.ts.map +1 -0
  268. package/dist/server/react-cache-scope.d.ts +49 -0
  269. package/dist/server/react-cache-scope.d.ts.map +1 -0
  270. package/dist/server/render-timeout.d.ts +15 -0
  271. package/dist/server/render-timeout.d.ts.map +1 -1
  272. package/dist/server/render-utils.d.ts +9 -0
  273. package/dist/server/render-utils.d.ts.map +1 -1
  274. package/dist/server/request-context.d.ts +26 -5
  275. package/dist/server/request-context.d.ts.map +1 -1
  276. package/dist/server/route-element-builder.d.ts.map +1 -1
  277. package/dist/server/route-handler.d.ts.map +1 -1
  278. package/dist/server/route-matcher.d.ts +7 -0
  279. package/dist/server/route-matcher.d.ts.map +1 -1
  280. package/dist/server/rsc-entry/action-middleware-runner.d.ts +22 -22
  281. package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -1
  282. package/dist/server/rsc-entry/api-handler.d.ts +7 -0
  283. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
  284. package/dist/server/rsc-entry/deny-fallback.d.ts +32 -6
  285. package/dist/server/rsc-entry/deny-fallback.d.ts.map +1 -1
  286. package/dist/server/rsc-entry/error-renderer.d.ts +11 -18
  287. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  288. package/dist/server/rsc-entry/helpers.d.ts +0 -1
  289. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  290. package/dist/server/rsc-entry/index.d.ts +5 -15
  291. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  292. package/dist/server/rsc-entry/render-route.d.ts +5 -6
  293. package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
  294. package/dist/server/rsc-entry/render-signals.d.ts +65 -0
  295. package/dist/server/rsc-entry/render-signals.d.ts.map +1 -0
  296. package/dist/server/rsc-entry/revalidate-renderer.d.ts +6 -16
  297. package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -1
  298. package/dist/server/rsc-entry/rsc-payload.d.ts +7 -3
  299. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  300. package/dist/server/rsc-entry/rsc-stream.d.ts +8 -53
  301. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  302. package/dist/server/rsc-entry/ssr-bridge.d.ts +2 -2
  303. package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
  304. package/dist/server/rsc-entry/ssr-renderer.d.ts +9 -4
  305. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  306. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +1 -1
  307. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -1
  308. package/dist/server/signal-identity.d.ts +43 -0
  309. package/dist/server/signal-identity.d.ts.map +1 -0
  310. package/dist/server/slot-resolver.d.ts +4 -3
  311. package/dist/server/slot-resolver.d.ts.map +1 -1
  312. package/dist/server/slot-subtree-contain.d.ts.map +1 -1
  313. package/dist/server/ssr-bridge-types.d.ts +91 -53
  314. package/dist/server/ssr-bridge-types.d.ts.map +1 -1
  315. package/dist/server/ssr-entry.d.ts +8 -5
  316. package/dist/server/ssr-entry.d.ts.map +1 -1
  317. package/dist/server/ssr-render.d.ts +26 -40
  318. package/dist/server/ssr-render.d.ts.map +1 -1
  319. package/dist/server/ssr-wrappers.d.ts +1 -1
  320. package/dist/server/static-generator.d.ts.map +1 -1
  321. package/dist/server/stream-utils.d.ts +12 -0
  322. package/dist/server/stream-utils.d.ts.map +1 -1
  323. package/dist/server/tracing.d.ts +12 -16
  324. package/dist/server/tracing.d.ts.map +1 -1
  325. package/dist/server/types.d.ts +1 -1
  326. package/dist/server/version-skew.d.ts +3 -3
  327. package/dist/shared/csrf-reject.d.ts +16 -0
  328. package/dist/shared/csrf-reject.d.ts.map +1 -0
  329. package/dist/shared/error-digest.d.ts +28 -0
  330. package/dist/shared/error-digest.d.ts.map +1 -0
  331. package/dist/shared/payload-root.d.ts +11 -10
  332. package/dist/shared/payload-root.d.ts.map +1 -1
  333. package/dist/shared/rsc-payload-path.d.ts +6 -0
  334. package/dist/shared/rsc-payload-path.d.ts.map +1 -1
  335. package/dist/utils/write-file-atomic.d.ts +17 -0
  336. package/dist/utils/write-file-atomic.d.ts.map +1 -0
  337. package/docs/api/30-api-server.mdx +17 -14
  338. package/docs/api/32-api-cache.mdx +51 -23
  339. package/docs/api/34-api-config.mdx +34 -6
  340. package/docs/api/36-cli.mdx +11 -1
  341. package/docs/learn/03-fetching-data.mdx +17 -1
  342. package/docs/learn/04-access-control.mdx +1 -1
  343. package/docs/learn/10-caching.mdx +29 -11
  344. package/docs/learn/11-middleware.mdx +8 -2
  345. package/docs/learn/12-error-handling.mdx +6 -1
  346. package/package.json +7 -3
  347. package/src/adapters/build-output-helper.ts +55 -53
  348. package/src/adapters/canonicalize-module.ts +29 -0
  349. package/src/adapters/cloudflare.ts +105 -63
  350. package/src/adapters/compress-module.ts +16 -5
  351. package/src/adapters/fs-identity.ts +2 -3
  352. package/src/adapters/nitro-presets.ts +0 -15
  353. package/src/adapters/nitro-preview.ts +272 -136
  354. package/src/adapters/nitro.ts +119 -82
  355. package/src/adapters/shared.ts +23 -14
  356. package/src/adapters/types.ts +110 -21
  357. package/src/analyze/graph-cache.ts +3 -4
  358. package/src/cache/json-lossy-check.ts +3 -9
  359. package/src/cache/singleflight.ts +5 -2
  360. package/src/cache/sizeof.ts +3 -1
  361. package/src/cache/tag-aware-handler.ts +43 -26
  362. package/src/cli-check.ts +8 -98
  363. package/src/cli-context.ts +119 -0
  364. package/src/cli.ts +19 -28
  365. package/src/client/browser-dev.ts +1 -1
  366. package/src/client/browser-entry/action-dispatch.ts +26 -4
  367. package/src/client/browser-entry/hydrate.ts +53 -58
  368. package/src/client/browser-entry/index.ts +16 -27
  369. package/src/client/browser-entry/post-hydration.ts +6 -40
  370. package/src/client/browser-entry/router-init.ts +66 -84
  371. package/src/client/error-boundary.tsx +9 -41
  372. package/src/client/error-reconstituter.tsx +10 -5
  373. package/src/client/history.ts +35 -21
  374. package/src/client/index.ts +0 -1
  375. package/src/client/internal.ts +2 -7
  376. package/src/client/navigation-commit.ts +41 -18
  377. package/src/client/navigation-context.ts +3 -39
  378. package/src/client/react-root.ts +3 -2
  379. package/src/client/router-pipeline.ts +96 -132
  380. package/src/client/router-types.ts +46 -44
  381. package/src/client/router-url.ts +32 -0
  382. package/src/client/router.ts +62 -21
  383. package/src/client/rsc-fetch.ts +117 -155
  384. package/src/client/segment-cache.ts +110 -11
  385. package/src/client/ssr-data.ts +19 -80
  386. package/src/client/state.ts +2 -9
  387. package/src/client/use-pathname.ts +1 -1
  388. package/src/config-types.ts +2 -1
  389. package/src/config-validation.ts +2 -1
  390. package/src/dev-tools/dev-page-shell.ts +18 -0
  391. package/src/dev-tools/index.ts +0 -2
  392. package/src/dev-tools/instrumentation.ts +50 -2
  393. package/src/dev-tools/overlay.ts +8 -238
  394. package/src/index.ts +26 -14
  395. package/src/plugin-context.ts +39 -40
  396. package/src/plugins/build-manifest.ts +4 -11
  397. package/src/plugins/build-report.ts +70 -70
  398. package/src/plugins/dev-server.ts +24 -44
  399. package/src/plugins/entries.ts +2 -1
  400. package/src/plugins/fonts.ts +35 -7
  401. package/src/plugins/post-build.ts +327 -0
  402. package/src/plugins/prebuilt-capture.ts +28 -32
  403. package/src/plugins/prebuilt.ts +54 -39
  404. package/src/plugins/prerender-sugar.ts +0 -4
  405. package/src/plugins/react-resolve.ts +149 -0
  406. package/src/plugins/{adapter-build.ts → rsc-assets-manifest.ts} +20 -109
  407. package/src/plugins/shims.ts +1 -0
  408. package/src/plugins/static-build.ts +42 -74
  409. package/src/routing/codegen-write.ts +25 -3
  410. package/src/routing/segment-codegen.ts +3 -2
  411. package/src/rsc-runtime/vendor-types.d.ts +8 -0
  412. package/src/server/access-gate.tsx +2 -1
  413. package/src/server/action-client.ts +1 -1
  414. package/src/server/action-handler.ts +253 -110
  415. package/src/server/actions.ts +73 -25
  416. package/src/server/als-registry.ts +73 -5
  417. package/src/server/chain-url-parts.ts +1 -1
  418. package/src/server/children-interception.ts +2 -1
  419. package/src/server/cookie-context.ts +18 -48
  420. package/src/server/csp-nonce.ts +0 -12
  421. package/src/server/csrf.ts +140 -8
  422. package/src/server/debug.ts +0 -1
  423. package/src/server/default-logger.ts +2 -1
  424. package/src/server/deny-boundary.ts +25 -13
  425. package/src/server/deny-renderer.ts +30 -97
  426. package/src/server/error-boundary-wrapper.ts +7 -3
  427. package/src/server/fallback-error.ts +26 -11
  428. package/src/server/flight-scripts.ts +1 -1
  429. package/src/server/head-response.ts +4 -4
  430. package/src/server/html-injector-core.ts +2 -2
  431. package/src/server/html-injectors.ts +2 -4
  432. package/src/server/index.ts +0 -4
  433. package/src/server/instrumentation.ts +10 -3
  434. package/src/server/internal.ts +1 -6
  435. package/src/server/json-status-response.ts +105 -0
  436. package/src/server/logger.ts +39 -3
  437. package/src/server/param-coercion.ts +15 -12
  438. package/src/server/pipeline-helpers.ts +257 -12
  439. package/src/server/pipeline-interception.ts +133 -0
  440. package/src/server/pipeline-outcome.ts +103 -31
  441. package/src/server/pipeline-phases.ts +127 -237
  442. package/src/server/pipeline.ts +204 -207
  443. package/src/server/prebuilt/capture-render.ts +118 -0
  444. package/src/server/prebuilt/capture-routes.ts +104 -0
  445. package/src/server/prebuilt/capture-state.ts +155 -48
  446. package/src/server/prebuilt/lifetime.ts +151 -0
  447. package/src/server/prebuilt/slots.ts +14 -2
  448. package/src/server/prebuilt/synthetic-store.ts +3 -0
  449. package/src/server/prebuilt-builder.ts +30 -320
  450. package/src/server/prebuilt-runtime.ts +125 -129
  451. package/src/server/primitives.ts +8 -168
  452. package/src/server/react-cache-bridge.ts +124 -0
  453. package/src/server/react-cache-scope.ts +104 -0
  454. package/src/server/render-timeout.ts +20 -0
  455. package/src/server/render-utils.ts +11 -1
  456. package/src/server/request-context.ts +36 -9
  457. package/src/server/route-element-builder.ts +11 -6
  458. package/src/server/route-handler.ts +36 -23
  459. package/src/server/route-matcher.ts +15 -1
  460. package/src/server/rsc-entry/action-middleware-runner.ts +79 -123
  461. package/src/server/rsc-entry/api-handler.ts +27 -103
  462. package/src/server/rsc-entry/deny-fallback.ts +93 -26
  463. package/src/server/rsc-entry/error-renderer.ts +39 -91
  464. package/src/server/rsc-entry/helpers.ts +2 -4
  465. package/src/server/rsc-entry/index.ts +64 -58
  466. package/src/server/rsc-entry/render-route.ts +15 -80
  467. package/src/server/rsc-entry/render-signals.ts +108 -0
  468. package/src/server/rsc-entry/revalidate-renderer.ts +56 -57
  469. package/src/server/rsc-entry/rsc-payload.ts +28 -61
  470. package/src/server/rsc-entry/rsc-stream.ts +113 -213
  471. package/src/server/rsc-entry/ssr-bridge.ts +2 -2
  472. package/src/server/rsc-entry/ssr-renderer.ts +182 -263
  473. package/src/server/rsc-entry/wrap-action-dispatch.ts +12 -14
  474. package/src/server/signal-identity.ts +112 -0
  475. package/src/server/slot-resolver.ts +18 -18
  476. package/src/server/slot-subtree-contain.ts +2 -1
  477. package/src/server/ssr-bridge-types.ts +92 -56
  478. package/src/server/ssr-entry.ts +87 -111
  479. package/src/server/ssr-render.ts +55 -64
  480. package/src/server/ssr-wrappers.tsx +2 -2
  481. package/src/server/static-generator.ts +7 -42
  482. package/src/server/static-not-found.ts +3 -3
  483. package/src/server/stream-utils.ts +51 -0
  484. package/src/server/tracing.ts +58 -75
  485. package/src/server/types.ts +1 -1
  486. package/src/server/version-skew.ts +5 -5
  487. package/src/shared/csrf-reject.ts +15 -0
  488. package/src/shared/error-digest.ts +46 -0
  489. package/src/shared/payload-root.ts +11 -10
  490. package/src/shared/rsc-payload-path.ts +8 -1
  491. package/src/utils/write-file-atomic.ts +32 -0
  492. package/dist/_chunks/actions-jNrXdzgs.js +0 -346
  493. package/dist/_chunks/actions-jNrXdzgs.js.map +0 -1
  494. package/dist/_chunks/als-registry-C6kcfprT.js.map +0 -1
  495. package/dist/_chunks/build-manifest-DTmSGLRz.js.map +0 -1
  496. package/dist/_chunks/build-output-helper-9dqCv1pZ.js.map +0 -1
  497. package/dist/_chunks/cache-api-CUD9Ezaq.js.map +0 -1
  498. package/dist/_chunks/cli-check-G8o30ldg.js.map +0 -1
  499. package/dist/_chunks/convention-lint-5KzoMg_A.js.map +0 -1
  500. package/dist/_chunks/dev-server-BzBxgYAV.js.map +0 -1
  501. package/dist/_chunks/error-boundary-BndF-3Td.js.map +0 -1
  502. package/dist/_chunks/fs-identity-D7vb6GdI.js.map +0 -1
  503. package/dist/_chunks/graph-cache-CP4GEmf9.js.map +0 -1
  504. package/dist/_chunks/json-lossy-check-ClNvBM_3.js.map +0 -1
  505. package/dist/_chunks/logger-k2DQ4EUf.js.map +0 -1
  506. package/dist/_chunks/navigation-context-D0TU0Jog.js.map +0 -1
  507. package/dist/_chunks/plugin-context-8idfR3UZ.js.map +0 -1
  508. package/dist/_chunks/primitives-DLAnvsrk.js.map +0 -1
  509. package/dist/_chunks/segment-context-D9_89u34.js +0 -34
  510. package/dist/_chunks/segment-context-D9_89u34.js.map +0 -1
  511. package/dist/_chunks/sizeof-lXffQ1Vr.js.map +0 -1
  512. package/dist/_chunks/ssr-data-D6T6Y3ef.js +0 -60
  513. package/dist/_chunks/ssr-data-D6T6Y3ef.js.map +0 -1
  514. package/dist/_chunks/state-FippDgxN.js.map +0 -1
  515. package/dist/_chunks/status-page-marker-DwQBrLBz.js.map +0 -1
  516. package/dist/client/types.d.ts +0 -14
  517. package/dist/client/types.d.ts.map +0 -1
  518. package/dist/plugins/adapter-build.d.ts +0 -38
  519. package/dist/plugins/adapter-build.d.ts.map +0 -1
  520. package/dist/server/asset-headers.d.ts +0 -32
  521. package/dist/server/asset-headers.d.ts.map +0 -1
  522. package/dist/server/dev-source-map.d.ts +0 -22
  523. package/dist/server/dev-source-map.d.ts.map +0 -1
  524. package/src/client/types.ts +0 -21
  525. package/src/server/asset-headers.ts +0 -59
  526. package/src/server/dev-source-map.ts +0 -31
package/dist/index.js CHANGED
@@ -1,15 +1,17 @@
1
- import { a as formatMissingPeers, i as formatConfigErrors, n as timberDevServer, o as validateConfig, r as checkPeerDependencies } from "./_chunks/dev-server-BzBxgYAV.js";
1
+ import { a as formatMissingPeers, i as formatConfigErrors, n as timberDevServer, o as validateConfig, r as checkPeerDependencies } from "./_chunks/dev-server-C4WZdB7L.js";
2
+ import { i as resolveRenderTimeoutMs } from "./_chunks/render-timeout-DOf_BBcO.js";
3
+ import { t as writeFileAtomic } from "./_chunks/write-file-atomic-D1qWqHb4.js";
2
4
  import { a as isDynamicMetadataExtension } from "./_chunks/metadata-routes-DSDjM_hJ.js";
3
- import { n as scanRoutes, r as collectInterceptionRewrites } from "./_chunks/scanner-BQ5sPIJ7.js";
4
- import { h as swallow } from "./_chunks/logger-k2DQ4EUf.js";
5
- import { c as writeRouteCodegen, i as getPrerenderExport, l as buildDirToRouteMap, n as formatConventionWarnings, r as lintConventions, s as flushCodegenWrites, t as checkAppDirExists } from "./_chunks/convention-lint-5KzoMg_A.js";
6
- import { c as resolveClientJavascript, i as mergeFileConfig, l as createNoopTimer, o as resolveAppDir, r as loadTimberConfigFile, s as resolveBuildDir, t as createPluginContext } from "./_chunks/plugin-context-8idfR3UZ.js";
7
- import { r as validateSchemaAgainstRoutes, t as collectLeafRoutes } from "./_chunks/walkers-CN2N_bA-.js";
8
- import { i as poisonPillSource, r as markerFor, t as EXPLICIT_MARKERS } from "./_chunks/poison-rules-DoEhbqaY.js";
5
+ import { n as scanRoutes, r as collectInterceptionRewrites } from "./_chunks/scanner-Bw0oq1HB.js";
6
+ import { _ as swallow } from "./_chunks/logger-DiDt5ppH.js";
7
+ import { c as writeRouteCodegen, i as getPrerenderExport, l as buildDirToRouteMap, n as formatConventionWarnings, r as lintConventions, s as flushCodegenWrites, t as checkAppDirExists } from "./_chunks/convention-lint-jKTwKwPe.js";
8
+ import { a as loadTimberConfigFile, c as resolveAppDir, d as createNoopTimer, l as resolveBuildDir, n as contextAdapter, o as mergeFileConfig, r as createPluginContext, t as adapterConfig, u as resolveClientJavascript } from "./_chunks/plugin-context-BPBPc3lL.js";
9
9
  import { i as withBasePath, n as normalizeBasePath, t as isRelativeBase } from "./_chunks/base-path-DaQrzbez.js";
10
- import { t as EMPTY_BUILD_MANIFEST } from "./_chunks/build-manifest-DTmSGLRz.js";
11
- import { t as rscPayloadPath } from "./_chunks/rsc-payload-path-B_LBodc2.js";
12
- import { a as REDIRECTS_FILE, n as detectFsIdentity, o as RESERVED_OUTPUT_PATHS, r as fsKey, t as EXACT_FS_IDENTITY } from "./_chunks/fs-identity-D7vb6GdI.js";
10
+ import { r as validateSchemaAgainstRoutes, t as collectLeafRoutes } from "./_chunks/walkers-BXExhzzk.js";
11
+ import { i as poisonPillSource, r as markerFor, t as EXPLICIT_MARKERS } from "./_chunks/poison-rules-DoEhbqaY.js";
12
+ import { n as rscPayloadPath, t as RSC_PAYLOAD_DIR } from "./_chunks/rsc-payload-path-dffcs4jp.js";
13
+ import { a as REDIRECTS_FILE, n as detectFsIdentity, o as RESERVED_OUTPUT_PATHS, r as fsKey, t as EXACT_FS_IDENTITY } from "./_chunks/fs-identity-BRKuL215.js";
14
+ import { t as EMPTY_BUILD_MANIFEST } from "./_chunks/build-manifest-DWppEdLB.js";
13
15
  import { t as fnv1aHash } from "./_chunks/fast-hash-C-tVYrLb.js";
14
16
  import { t as formatSize } from "./_chunks/format-BKclgVbk.js";
15
17
  import { createLogger, parseAst } from "vite";
@@ -202,7 +204,7 @@ function generateConfigModule(ctx) {
202
204
  topLoader: ctx.config.topLoader,
203
205
  debug: ctx.config.debug ?? false,
204
206
  serverTiming: ctx.config.serverTiming,
205
- renderTimeoutMs: ctx.config.renderTimeoutMs ?? 3e4,
207
+ renderTimeoutMs: resolveRenderTimeoutMs(ctx.config.renderTimeoutMs),
206
208
  sitemap: ctx.config.sitemap,
207
209
  forms: serializeFormsConfig(ctx.config.forms),
208
210
  deploymentId: ctx.deploymentId ?? null,
@@ -962,7 +964,8 @@ var SUBPATH_SRC_MAP = {
962
964
  "adapters/cloudflare": "adapters/cloudflare.ts",
963
965
  "adapters/cloudflare/cache": "adapters/cloudflare-kv-cache.ts",
964
966
  "adapters/cloudflare/dev": "adapters/cloudflare-dev.ts",
965
- "adapters/nitro": "adapters/nitro.ts"
967
+ "adapters/nitro": "adapters/nitro.ts",
968
+ "adapters/types": "adapters/types.ts"
966
969
  };
967
970
  /**
968
971
  * Runtime value exports of @timber-js/app/server, stubbed in the client
@@ -4543,8 +4546,14 @@ function writeFontManifest(pipeline, buildManifest, base = "/") {
4543
4546
  */
4544
4547
  function timberFonts(ctx) {
4545
4548
  const pipeline = new FontPipeline({ hashOnRegister: !ctx.dev });
4549
+ /** Whether the client environment emitted a binary this build. */
4550
+ let emittedToClient = false;
4546
4551
  return {
4547
4552
  name: "timber-fonts",
4553
+ api: {
4554
+ contributeManifest: (manifest) => writeFontManifest(pipeline, manifest, ctx.base),
4555
+ immutableDirs: () => emittedToClient ? [FONT_OUTPUT_DIR] : []
4556
+ },
4548
4557
  /**
4549
4558
  * Push Vite's resolved `base` into the pipeline so every font URL it
4550
4559
  * emits (@font-face src, manifest hrefs, dev middleware matching)
@@ -4724,212 +4733,23 @@ function timberFonts(ctx) {
4724
4733
  }
4725
4734
  },
4726
4735
  /**
4727
- * Emit font binaries and build-manifest entries.
4736
+ * Emit font binaries.
4728
4737
  *
4729
4738
  * All font binaries (Google and local) use content-hashed filenames
4730
4739
  * for immutable caching. Font CSS is handled by Vite's CSS pipeline
4731
- * via virtual modules — we only emit binaries here.
4740
+ * via virtual modules — we only emit binaries here. Manifest entries are
4741
+ * written by the post-build phase through `api.contributeManifest`.
4732
4742
  */
4733
4743
  generateBundle() {
4734
- emitFontAssets(pipeline, (asset) => this.emitFile(asset), (msg) => this.warn(msg));
4735
- if (!ctx.buildManifest) return;
4736
- writeFontManifest(pipeline, ctx.buildManifest, ctx.base);
4737
- }
4738
- };
4739
- }
4740
- //#endregion
4741
- //#region src/plugins/adapter-build.ts
4742
- function timberAdapterBuild(ctx) {
4743
- return {
4744
- name: "timber-adapter-build",
4745
- buildApp: {
4746
- order: "post",
4747
- async handler() {
4748
- if (ctx.dev) return;
4749
- const adapter = ctx.config.adapter;
4750
- if (!adapter || typeof adapter.buildOutput !== "function") return;
4751
- const buildDir = ctx.buildDir;
4752
- if (ctx.buildManifest) await mergeRscAssetsManifest(buildDir, ctx.root, ctx.buildManifest, ctx.routeClientRefKeys);
4753
- let manifestInit;
4754
- if (ctx.buildManifest) {
4755
- const manifest = ctx.clientJavascript.disabled ? {
4756
- ...ctx.buildManifest,
4757
- js: {},
4758
- modulepreload: {}
4759
- } : ctx.buildManifest;
4760
- manifestInit = `globalThis.__TIMBER_BUILD_MANIFEST__ = ${JSON.stringify(manifest)};\n`;
4761
- if (ctx.deploymentId) manifestInit += `globalThis.__TIMBER_DEPLOYMENT_ID__ = ${JSON.stringify(ctx.deploymentId)};\n`;
4762
- }
4763
- if (ctx.clientJavascript.disabled) await stripJsFromRscAssetsManifests(buildDir);
4764
- const adapterConfig = {
4765
- root: ctx.root,
4766
- base: normalizeBasePath(ctx.base),
4767
- output: ctx.config.output ?? "server",
4768
- clientJavascriptDisabled: ctx.clientJavascript.disabled,
4769
- manifestInit,
4770
- staticPagesDir: (ctx.config.output ?? "server") === "static" ? join(buildDir, "static-pages") : void 0,
4771
- staticContentTypes: ctx.staticContentTypes
4772
- };
4773
- await adapter.buildOutput(adapterConfig, buildDir);
4774
- }
4744
+ let emitted = 0;
4745
+ emitFontAssets(pipeline, (asset) => {
4746
+ emitted++;
4747
+ return this.emitFile(asset);
4748
+ }, (msg) => this.warn(msg));
4749
+ if (this.environment?.name === "client") emittedToClient = emitted > 0;
4775
4750
  }
4776
4751
  };
4777
4752
  }
4778
- /**
4779
- * Parse the RSC assets manifest at `manifestPath`. Tries JSON.parse first
4780
- * (fast path for plain-string manifests); falls back to a dynamic import()
4781
- * for manifests that contain runtime expressions from renderBuiltUrl.
4782
- * Returns null if both paths fail.
4783
- */
4784
- async function parseRscManifest(manifestPath, content) {
4785
- const jsonStr = content.replace(/^export default\s*/, "").replace(/;?\s*$/, "");
4786
- try {
4787
- return JSON.parse(jsonStr);
4788
- } catch {}
4789
- try {
4790
- return (await import(`${pathToFileURL(manifestPath).href}?t=${Date.now()}`)).default ?? null;
4791
- } catch (err) {
4792
- swallow(err, `failed to evaluate RSC assets manifest: ${manifestPath}`, { level: "warn" });
4793
- return null;
4794
- }
4795
- }
4796
- /**
4797
- * Replace all `"js": [...]` array contents with `"js": []` using balanced
4798
- * bracket matching. Handles nested brackets from runtime expressions like
4799
- * `__rbu("path/to/[slug]/file.js")`.
4800
- */
4801
- function clearJsArrays(src) {
4802
- const pattern = /"js"\s*:\s*\[/g;
4803
- let result = "";
4804
- let lastEnd = 0;
4805
- let match;
4806
- while ((match = pattern.exec(src)) !== null) {
4807
- const arrayStart = match.index + match[0].length;
4808
- let depth = 1;
4809
- let i = arrayStart;
4810
- while (i < src.length && depth > 0) {
4811
- if (src[i] === "[") depth++;
4812
- else if (src[i] === "]") depth--;
4813
- i++;
4814
- }
4815
- result += src.slice(lastEnd, match.index) + "\"js\": []";
4816
- lastEnd = i;
4817
- }
4818
- result += src.slice(lastEnd);
4819
- return result;
4820
- }
4821
- /**
4822
- * Replace `"bootstrapScriptContent": <value>` with an empty string. The
4823
- * value may be a JS expression (e.g., concatenation with runtime helpers),
4824
- * not a simple string literal. Scans forward from the colon, tracking
4825
- * balanced parens and brackets, and stops at the next unbalanced `,` or `}`.
4826
- */
4827
- function clearBootstrapScriptContent(src) {
4828
- const idx = src.indexOf("\"bootstrapScriptContent\"");
4829
- if (idx === -1) return src;
4830
- const colonIdx = src.indexOf(":", idx + 24);
4831
- if (colonIdx === -1) return src;
4832
- let valStart = colonIdx + 1;
4833
- while (valStart < src.length && /\s/.test(src[valStart])) valStart++;
4834
- let i = valStart;
4835
- let depth = 0;
4836
- let inString = false;
4837
- let stringChar = "";
4838
- while (i < src.length) {
4839
- const ch = src[i];
4840
- if (inString) {
4841
- if (ch === "\\") {
4842
- i += 2;
4843
- continue;
4844
- }
4845
- if (ch === stringChar) inString = false;
4846
- } else if (ch === "\"" || ch === "'") {
4847
- inString = true;
4848
- stringChar = ch;
4849
- } else if (ch === "(" || ch === "[") depth++;
4850
- else if (ch === ")" || ch === "]") depth--;
4851
- else if (depth === 0 && (ch === "," || ch === "}" || ch === "\n")) break;
4852
- i++;
4853
- }
4854
- return src.slice(0, idx) + "\"bootstrapScriptContent\": \"\"" + src.slice(i);
4855
- }
4856
- /**
4857
- * Strip JS references from the RSC plugin's assets manifest files.
4858
- *
4859
- * The RSC plugin writes `__vite_rsc_assets_manifest.js` to rsc/ and ssr/
4860
- * as standalone files (not Rollup chunks), so generateBundle can't
4861
- * intercept them. This rewrites the files on disk after all builds
4862
- * complete but before the adapter copies them to the output directory.
4863
- */
4864
- /**
4865
- * Read the RSC plugin's assets manifest and merge per-route CSS and
4866
- * per-route JS modulepreloads into the build manifest.
4867
- *
4868
- * CSS: `serverResources` maps server component paths to CSS URLs.
4869
- * JS: `clientReferenceDeps` maps reference hashes to JS dep URLs.
4870
- * Combined with `routeClientRefKeys` (from the RSC generateBundle
4871
- * module graph walk) to produce per-route modulepreload entries.
4872
- */
4873
- async function mergeRscAssetsManifest(buildDir, root, buildManifest, routeClientRefKeys) {
4874
- const manifestPath = join(buildDir, "rsc", "__vite_rsc_assets_manifest.js");
4875
- let content;
4876
- try {
4877
- content = await readFile(manifestPath, "utf-8");
4878
- } catch {
4879
- return;
4880
- }
4881
- const manifest = await parseRscManifest(manifestPath, content);
4882
- if (!manifest) return;
4883
- const serverResources = manifest.serverResources;
4884
- if (serverResources) {
4885
- for (const [relativePath, deps] of Object.entries(serverResources)) if (deps.css && deps.css.length > 0) {
4886
- const absolutePath = join(root, relativePath);
4887
- buildManifest.css[absolutePath] = deps.css;
4888
- }
4889
- }
4890
- if (routeClientRefKeys && routeClientRefKeys.size > 0) {
4891
- const clientReferenceDeps = manifest.clientReferenceDeps;
4892
- if (clientReferenceDeps) for (const [filePath, refKeys] of routeClientRefKeys) {
4893
- const jsUrls = /* @__PURE__ */ new Set();
4894
- for (const key of refKeys) {
4895
- const deps = clientReferenceDeps[key];
4896
- if (deps?.js) for (const url of deps.js) jsUrls.add(url);
4897
- }
4898
- if (jsUrls.size > 0) buildManifest.modulepreload[filePath] = Array.from(jsUrls);
4899
- }
4900
- }
4901
- }
4902
- async function stripJsFromRscAssetsManifests(buildDir) {
4903
- const manifestName = "__vite_rsc_assets_manifest.js";
4904
- const paths = [join(buildDir, "rsc", manifestName), join(buildDir, "ssr", manifestName)];
4905
- for (const path of paths) {
4906
- let content;
4907
- try {
4908
- content = await readFile(path, "utf-8");
4909
- } catch {
4910
- continue;
4911
- }
4912
- const jsonStr = content.replace(/^export default\s*/, "").replace(/;?\s*$/, "");
4913
- let isStrictJson = true;
4914
- let manifest = null;
4915
- try {
4916
- manifest = JSON.parse(jsonStr);
4917
- } catch {
4918
- isStrictJson = false;
4919
- }
4920
- if (!isStrictJson) {
4921
- let rewritten = content;
4922
- rewritten = clearJsArrays(rewritten);
4923
- rewritten = clearBootstrapScriptContent(rewritten);
4924
- await writeFile(path, rewritten);
4925
- continue;
4926
- }
4927
- const deps = manifest?.clientReferenceDeps;
4928
- if (deps) for (const entry of Object.values(deps)) entry.js = [];
4929
- if (manifest) manifest.bootstrapScriptContent = "";
4930
- await writeFile(path, `export default ${JSON.stringify(manifest, null, 2)};\n`);
4931
- }
4932
- }
4933
4753
  //#endregion
4934
4754
  //#region src/server/flight-scripts.ts
4935
4755
  /**
@@ -5012,12 +4832,6 @@ function generateRedirectsFile(redirects, base) {
5012
4832
  const target = (to) => to.startsWith("/") && !to.startsWith("//") ? withBasePath(base, to) : to;
5013
4833
  return redirects.map((r) => `${withBasePath(base, r.from)} ${target(r.to)} ${r.status}`).join("\n") + "\n";
5014
4834
  }
5015
- /** Write a file atomically: temp sibling + rename. */
5016
- async function writeFileAtomic$1(path, data) {
5017
- const tmp = `${path}.tmp`;
5018
- await writeFile(tmp, data);
5019
- await rename(tmp, path);
5020
- }
5021
4835
  /**
5022
4836
  * Determine the output file path for a static page entry.
5023
4837
  *
@@ -5056,7 +4870,7 @@ function staticOutputPath(entry) {
5056
4870
  * API route, `about/index.html` from a page). The filesystem does refuse that
5057
4871
  * one — `EISDIR` on the rename, `ENOTDIR` on the mkdir, depending on which
5058
4872
  * lands second — but an errno names a path, and in the rename case the path
5059
- * it names is `about.tmp`. Reported here instead, so the message says which
4873
+ * it names is the write's temp file. Reported here instead, so the message says which
5060
4874
  * two routes.
5061
4875
  *
5062
4876
  * Collisions are **collected, not thrown**. `onEntry` runs inside the
@@ -5145,7 +4959,7 @@ var StaticSiteWriter = class {
5145
4959
  if (rel.split(/[\\/]/)[0] === ".." || isAbsolute(rel) || resolve(this.staticDir, rel) !== fullPath) throw new Error(`[timber] Static generation: output path "${relPath}" escapes the static output directory — check generateStaticSegmentParams for path traversal characters in ${entry.urlPath}`);
5146
4960
  if (!this.paths.claim(rel.replace(/\\/g, "/"), entry)) return;
5147
4961
  await mkdir(dirname(fullPath), { recursive: true });
5148
- await writeFileAtomic$1(fullPath, entry.body);
4962
+ await writeFileAtomic(fullPath, entry.body);
5149
4963
  if (entry.kind === "api" && !extname(relPath) && entry.contentType) this.contentTypes[entry.urlPath] = entry.contentType;
5150
4964
  }
5151
4965
  /** Throw if any two entries could not both be written. */
@@ -5173,7 +4987,7 @@ var StaticSiteWriter = class {
5173
4987
  * @internal Exported for testing.
5174
4988
  */
5175
4989
  async function cleanConditionalArtifacts(staticDir) {
5176
- await rm(join(staticDir, "_rsc"), {
4990
+ await rm(join(staticDir, RSC_PAYLOAD_DIR), {
5177
4991
  recursive: true,
5178
4992
  force: true
5179
4993
  }).catch(() => {});
@@ -5219,19 +5033,21 @@ async function withSchedulerPortsReleased(fn) {
5219
5033
  }
5220
5034
  }
5221
5035
  /**
5222
- * Load the built RSC entry and run the static generation pass.
5036
+ * Load the built RSC entry and run the static generation pass — the static
5037
+ * generation step of the post-build phase (plugins/post-build.ts).
5223
5038
  *
5224
- * Unlike prebuilt capture, this needs the FULL pipeline handler, so
5039
+ * The build manifest it renders with is already final: the phase merged the
5040
+ * RSC assets manifest into it and published it before this step. Unlike
5041
+ * prebuilt capture, this needs the FULL pipeline handler, so
5225
5042
  * __TIMBER_CAPTURE_MODE__ must be false.
5043
+ *
5044
+ * Returns the site only once it is complete, so an adapter is never handed a
5045
+ * directory a failed pass left half-written — a failure throws instead.
5226
5046
  */
5227
- async function runStaticGeneration(ctx) {
5228
- const entryPath = join(ctx.buildDir, "rsc", "index.js");
5229
- if (!existsSync(entryPath)) throw new Error(`[timber] Static generation: built RSC entry not found at ${entryPath} — the build output is missing`);
5230
- const g = globalThis;
5231
- if (ctx.buildManifest) g.__TIMBER_BUILD_MANIFEST__ ??= ctx.buildManifest;
5232
- if (ctx.deploymentId) g.__TIMBER_DEPLOYMENT_ID__ ??= ctx.deploymentId;
5233
- g.__TIMBER_CAPTURE_MODE__ = false;
5234
- if (ctx.buildManifest) await mergeRscAssetsManifest(ctx.buildDir, ctx.root, ctx.buildManifest, ctx.routeClientRefKeys);
5047
+ async function runStaticGeneration(ctx, rscDir) {
5048
+ const entryPath = join(rscDir, "index.js");
5049
+ warnUnusableBase(ctx);
5050
+ globalThis.__TIMBER_CAPTURE_MODE__ = false;
5235
5051
  console.log("[timber] Static generation: building site…");
5236
5052
  const staticDir = join(ctx.buildDir, "static-pages");
5237
5053
  await cleanConditionalArtifacts(staticDir);
@@ -5265,12 +5081,12 @@ async function runStaticGeneration(ctx) {
5265
5081
  throw new Error(`[timber] Static generation failed:\n${details}`);
5266
5082
  }
5267
5083
  writer.assertNoCollisions();
5268
- if (Object.keys(writer.contentTypes).length > 0) ctx.staticContentTypes = writer.contentTypes;
5084
+ const immutableDirs = [];
5269
5085
  if (!ctx.clientJavascript.disabled) {
5270
5086
  const rscManifest = await hashRscPayloads(staticDir);
5271
5087
  if (Object.keys(rscManifest).length > 0) {
5088
+ immutableDirs.push(RSC_PAYLOAD_DIR);
5272
5089
  await injectRscManifestIntoHtml(staticDir, rscManifest);
5273
- ctx.rscManifest = rscManifest;
5274
5090
  console.log(`[timber] Static generation: hashed ${Object.keys(rscManifest).length} RSC flight payloads`);
5275
5091
  }
5276
5092
  }
@@ -5279,14 +5095,18 @@ async function runStaticGeneration(ctx) {
5279
5095
  const redirectsPath = join(staticDir, REDIRECTS_FILE);
5280
5096
  const userRedirects = await readUserRedirects(ctx.root);
5281
5097
  if (userRedirects !== null) {
5282
- await writeFileAtomic$1(redirectsPath, generated + "\n" + userRedirects);
5098
+ await writeFileAtomic(redirectsPath, generated + "\n" + userRedirects);
5283
5099
  console.log(`[timber] Static generation: wrote _redirects with ${summary.redirects.length} generated rule(s), merged with user-authored _redirects`);
5284
5100
  } else {
5285
- await writeFileAtomic$1(redirectsPath, generated);
5101
+ await writeFileAtomic(redirectsPath, generated);
5286
5102
  console.log(`[timber] Static generation: wrote _redirects with ${summary.redirects.length} rule(s)`);
5287
5103
  }
5288
5104
  }
5289
- return summary;
5105
+ return {
5106
+ pages: { path: staticDir },
5107
+ contentTypes: writer.contentTypes,
5108
+ immutableDirs
5109
+ };
5290
5110
  }
5291
5111
  /**
5292
5112
  * Warn when Vite's `base` is document-relative (`'./'`) in static mode.
@@ -5359,7 +5179,7 @@ async function walkDir(dir) {
5359
5179
  * @internal Exported for testing.
5360
5180
  */
5361
5181
  async function hashRscPayloads(staticDir) {
5362
- const files = await walkDir(join(staticDir, "_rsc"));
5182
+ const files = await walkDir(join(staticDir, RSC_PAYLOAD_DIR));
5363
5183
  const manifest = {};
5364
5184
  for (const filePath of files) {
5365
5185
  if (!filePath.endsWith(".rsc")) continue;
@@ -5407,11 +5227,9 @@ async function readUserRedirects(root) {
5407
5227
  /**
5408
5228
  * Create the timber-static-build Vite plugin.
5409
5229
  *
5410
- * Only active when output: 'static' is configured.
5411
- *
5412
- * Hooks:
5413
- * - transform: Validates source files for static mode violations
5414
- * - buildApp (post): Renders all routes to static HTML after the build
5230
+ * Only active when output: 'static' is configured. Validates source files
5231
+ * for static mode violations; rendering the site is `runStaticGeneration`,
5232
+ * which the post-build phase calls.
5415
5233
  */
5416
5234
  function timberStaticBuild(ctx) {
5417
5235
  return {
@@ -5433,22 +5251,6 @@ function timberStaticBuild(ctx) {
5433
5251
  this.error(messages.join("\n\n"));
5434
5252
  }
5435
5253
  return null;
5436
- },
5437
- /**
5438
- * After all environments are built, render every route to static HTML.
5439
- *
5440
- * Runs with order: 'post' so the RSC plugin's buildApp completes first,
5441
- * producing the built entry we import. Must run BEFORE the adapter
5442
- * build copies output to its platform-specific directory.
5443
- */
5444
- buildApp: {
5445
- order: "post",
5446
- async handler() {
5447
- if (ctx.dev) return;
5448
- if (!(ctx.config.output === "static")) return;
5449
- warnUnusableBase(ctx);
5450
- await runStaticGeneration(ctx);
5451
- }
5452
5254
  }
5453
5255
  };
5454
5256
  }
@@ -5658,8 +5460,6 @@ function timberBuildManifest(ctx) {
5658
5460
  if (ctx.requestDepMap === void 0) throw new Error("[timber] Build manifest: the request-dependency map was never computed. The RSC environment's buildEnd hook did not run before the client build, so no segment or slot could be skipped on client navigation. This is a plugin pipeline bug — timber-request-dep must run in the rsc environment.");
5659
5461
  ctx.buildManifest.requestDep = ctx.requestDepMap;
5660
5462
  if (ctx.clientJavascript.disabled) {
5661
- ctx.buildManifest.js = {};
5662
- ctx.buildManifest.modulepreload = {};
5663
5463
  for (const [fileName, item] of Object.entries(bundle)) if (item.type === "chunk") delete bundle[fileName];
5664
5464
  }
5665
5465
  }
@@ -5989,6 +5789,106 @@ function timberReactProd() {
5989
5789
  };
5990
5790
  }
5991
5791
  //#endregion
5792
+ //#region src/plugins/react-resolve.ts
5793
+ /**
5794
+ * timber-react-resolve — Deduplicate React across timber and user project.
5795
+ *
5796
+ * Without this plugin, pnpm's strict isolation can install two physical
5797
+ * copies of React when the user's pinned version differs from timber's
5798
+ * peer dep. Timber's 47 bare `from 'react'` imports resolve to one copy
5799
+ * while react-dom uses the other — causing "Invalid hook call" hydration
5800
+ * crashes with no indication of the real cause.
5801
+ *
5802
+ * The fix: inject `resolve.dedupe` entries so Vite always resolves React
5803
+ * packages from the project root. Unlike `resolve.alias`, `dedupe`
5804
+ * preserves Vite's per-environment export conditions — the RSC
5805
+ * environment still gets the `react-server` build, SSR gets the SSR
5806
+ * build, and the browser gets the browser build. The deduplication
5807
+ * happens at the package level, not the file level.
5808
+ *
5809
+ * A `configResolved` check detects version mismatches between timber's
5810
+ * resolved React and the project's React, and logs an actionable error.
5811
+ *
5812
+ * Design doc: 18-build-system.md §"Module Resolution"
5813
+ * Task: TIM-1520
5814
+ */
5815
+ /**
5816
+ * React packages to deduplicate. Vite's `resolve.dedupe` extracts the
5817
+ * base package name (via `deepImportRE`) before checking, so subpath
5818
+ * specifiers like `react/jsx-runtime` are covered by the base `react`
5819
+ * entry — listing them separately has no effect.
5820
+ */
5821
+ var REACT_DEDUPE_PACKAGES = ["react", "react-dom"];
5822
+ var RED = "\x1B[31m";
5823
+ var BOLD = "\x1B[1m";
5824
+ var DIM = "\x1B[2m";
5825
+ var RESET = "\x1B[0m";
5826
+ /**
5827
+ * Read the version from a package's package.json given a resolve root.
5828
+ */
5829
+ function readPackageVersion(packageName, resolveRoot) {
5830
+ try {
5831
+ const req = createRequire(`${resolveRoot}/package.json`);
5832
+ return req(req.resolve(`${packageName}/package.json`)).version ?? null;
5833
+ } catch {
5834
+ return null;
5835
+ }
5836
+ }
5837
+ /**
5838
+ * Check whether timber's React and the user project's React resolve to
5839
+ * the same version. Returns mismatches if any.
5840
+ */
5841
+ function detectReactMismatch(projectRoot) {
5842
+ const mismatches = [];
5843
+ const timberRoot = dirname(dirname(fileURLToPath(import.meta.url)));
5844
+ for (const pkg of ["react", "react-dom"]) {
5845
+ const timberVersion = readPackageVersion(pkg, timberRoot);
5846
+ const projectVersion = readPackageVersion(pkg, projectRoot);
5847
+ if (timberVersion && projectVersion && timberVersion !== projectVersion) mismatches.push({
5848
+ specifier: pkg,
5849
+ timberVersion,
5850
+ projectVersion
5851
+ });
5852
+ }
5853
+ return mismatches;
5854
+ }
5855
+ /**
5856
+ * Format React version mismatches as an actionable warning.
5857
+ */
5858
+ function formatReactMismatch(mismatches) {
5859
+ if (mismatches.length === 0) return "";
5860
+ const lines = [];
5861
+ lines.push(`${RED}${BOLD}[timber]${RESET} ${RED}React version mismatch detected — this causes silent hydration crashes${RESET}`);
5862
+ lines.push("");
5863
+ for (const m of mismatches) lines.push(` ${RED}✗${RESET} ${m.specifier}: timber has ${BOLD}${m.timberVersion}${RESET}, project has ${BOLD}${m.projectVersion}${RESET}`);
5864
+ lines.push("");
5865
+ lines.push(` ${DIM}This typically causes "Invalid hook call" errors because two copies of${RESET}`);
5866
+ lines.push(` ${DIM}React are loaded. timber's resolve.dedupe should handle this, but${RESET}`);
5867
+ lines.push(` ${DIM}if you see hook errors, ensure your React version satisfies timber's peer dep.${RESET}`);
5868
+ lines.push("");
5869
+ lines.push(` ${DIM}Fix: update React in your project to match —${RESET}`);
5870
+ lines.push(` ${BOLD}pnpm add react@${mismatches[0].timberVersion} react-dom@${mismatches[0].timberVersion}${RESET}`);
5871
+ return lines.join("\n");
5872
+ }
5873
+ /**
5874
+ * Create the timber-react-resolve Vite plugin.
5875
+ */
5876
+ function timberReactResolve() {
5877
+ return {
5878
+ name: "timber-react-resolve",
5879
+ enforce: "pre",
5880
+ config(userConfig) {
5881
+ const merged = [...userConfig.resolve?.dedupe ?? []];
5882
+ for (const pkg of REACT_DEDUPE_PACKAGES) if (!merged.includes(pkg)) merged.push(pkg);
5883
+ return { resolve: { dedupe: merged } };
5884
+ },
5885
+ configResolved(resolved) {
5886
+ const mismatches = detectReactMismatch(resolved.root);
5887
+ if (mismatches.length > 0) process.stderr.write(`${formatReactMismatch(mismatches)}\n\n`);
5888
+ }
5889
+ };
5890
+ }
5891
+ //#endregion
5992
5892
  //#region src/plugins/chunks.ts
5993
5893
  /**
5994
5894
  * Build a map from absolute module IDs in non-facade RSC chunks to the
@@ -7741,8 +7641,8 @@ function mayPrerender(call) {
7741
7641
  /**
7742
7642
  * Post-build prebuilt capture — Node-side orchestration (TIM-1119).
7743
7643
  *
7744
- * Runs from timber-prebuilt's `buildApp` post hook, after Vite's 5-step
7745
- * build sequence. Imports the BUILT `rsc/index.js` (the bundle carries
7644
+ * Runs as the capture step of the post-build phase (plugins/post-build.ts),
7645
+ * after Vite's 5-step build sequence and the manifest finalization step. Imports the BUILT `rsc/index.js` (the bundle carries
7746
7646
  * the react-server condition resolved at build time, so a plain dynamic
7747
7647
  * import works) and calls its `__timber_capturePrebuilt` export — the
7748
7648
  * capture itself must run inside the bundle's module graph, where the
@@ -7771,12 +7671,6 @@ function mayPrerender(call) {
7771
7671
  */
7772
7672
  var COMPONENT_ID_RE = /^[a-z0-9]+_[0-9a-f]{16}$/;
7773
7673
  var CACHE_KEY_RE = /^[0-9a-f]{16}$/;
7774
- /** Write a file atomically: temp sibling + rename. */
7775
- async function writeFileAtomic(path, data) {
7776
- const tmp = `${path}.tmp`;
7777
- await writeFile(tmp, data);
7778
- await rename(tmp, path);
7779
- }
7780
7674
  /**
7781
7675
  * Create the writer for `.timber/dist/prebuilt/`. Storage layout per
7782
7676
  * design/44 §Storage: one directory per component ID, one `.flight` file
@@ -7818,7 +7712,7 @@ function createPrebuiltArtifactWriter(prebuiltDir) {
7818
7712
  * make adapters ship payloads from an OLD build — reviving them serves
7819
7713
  * outdated content with that build's chunk references instead of the
7820
7714
  * intended miss → live render. Runs unconditionally at the start of the
7821
- * post-build hook, BEFORE the capture decision: a build whose app no
7715
+ * capture step, BEFORE the capture decision: a build whose app no
7822
7716
  * longer declares any prebuilt components must also shed the artifacts
7823
7717
  * (codex P2 on PR #836).
7824
7718
  */
@@ -7835,19 +7729,16 @@ async function cleanPrebuiltDir(buildDir) {
7835
7729
  * component). Per-entry data errors skip-and-report per design/44
7836
7730
  * §Build-Phase Error Handling.
7837
7731
  */
7838
- async function runPrebuiltCapture(ctx) {
7732
+ async function runPrebuiltCapture(ctx, rscDir) {
7839
7733
  const routeComponents = ctx.routePrebuiltComponents;
7840
- if (!routeComponents || routeComponents.size === 0) return {
7734
+ if (!routeComponents || routeComponents.size === 0) return { summary: {
7841
7735
  captured: 0,
7842
7736
  skipped: [],
7843
7737
  hardErrors: [],
7844
7738
  nestingWarnings: 0
7845
- };
7846
- const entryPath = join(ctx.buildDir, "rsc", "index.js");
7847
- if (!existsSync(entryPath)) throw new Error(`[timber] prebuilt capture: built RSC entry not found at ${entryPath} — the app declares cache.component({ prerender: true }) call sites but the RSC build output is missing`);
7739
+ } };
7740
+ const entryPath = join(rscDir, "index.js");
7848
7741
  const g = globalThis;
7849
- if (ctx.buildManifest) g.__TIMBER_BUILD_MANIFEST__ ??= ctx.buildManifest;
7850
- if (ctx.deploymentId) g.__TIMBER_DEPLOYMENT_ID__ ??= ctx.deploymentId;
7851
7742
  console.log("[timber] cache.component: capturing prebuilt flight payloads…");
7852
7743
  g.__TIMBER_CAPTURE_MODE__ = true;
7853
7744
  let mod;
@@ -7861,7 +7752,8 @@ async function runPrebuiltCapture(ctx) {
7861
7752
  }
7862
7753
  const capture = mod.__timber_capturePrebuilt;
7863
7754
  if (typeof capture !== "function") throw new Error("[timber] prebuilt capture: built RSC entry does not export __timber_capturePrebuilt — the build output predates this framework version; rebuild from a clean .timber/dist");
7864
- const writer = createPrebuiltArtifactWriter(join(ctx.buildDir, "prebuilt"));
7755
+ const prebuiltDir = join(ctx.buildDir, "prebuilt");
7756
+ const writer = createPrebuiltArtifactWriter(prebuiltDir);
7865
7757
  const summary = await capture({
7866
7758
  fileComponentMap: Object.fromEntries(routeComponents),
7867
7759
  callSiteProps: ctx.prebuiltCallSiteProps ? Object.fromEntries(ctx.prebuiltCallSiteProps) : void 0,
@@ -7872,10 +7764,13 @@ async function runPrebuiltCapture(ctx) {
7872
7764
  const details = summary.hardErrors.map((issue) => ` - ${issue.message}`).join("\n");
7873
7765
  throw new Error(`[timber] prebuilt capture failed: ${summary.hardErrors.length} component(s) read per-request context at build time:\n${details}`);
7874
7766
  }
7875
- await writer.finalize();
7767
+ const written = await writer.finalize();
7876
7768
  const total = summary.captured + summary.skipped.length;
7877
7769
  console.log(`[timber] Prebuilt ${summary.captured}/${total} entries.` + (summary.skipped.length > 0 ? ` ${summary.skipped.length} skipped due to errors.` : ""));
7878
- return summary;
7770
+ return written > 0 ? {
7771
+ summary,
7772
+ artifact: { path: prebuiltDir }
7773
+ } : { summary };
7879
7774
  }
7880
7775
  //#endregion
7881
7776
  //#region src/plugins/prebuilt.ts
@@ -8049,8 +7944,7 @@ function collectPageAndLayoutFiles(root) {
8049
7944
  return files;
8050
7945
  }
8051
7946
  var DEFAULT_SENTINEL = "\0default";
8052
- async function collectCrossModuleProps(ctx, moduleId, getInfo, resolve) {
8053
- const bindingNames = ctx.prebuiltBindingNames;
7947
+ async function collectCrossModuleProps(ctx, bindingNames, moduleId, getInfo, resolve) {
8054
7948
  const info = getInfo(moduleId);
8055
7949
  if (!info) return;
8056
7950
  const declaringImports = info.importedIds.filter((id) => bindingNames.has(id) && id !== moduleId);
@@ -8118,10 +8012,34 @@ async function collectCrossModuleProps(ctx, moduleId, getInfo, resolve) {
8118
8012
  }
8119
8013
  }
8120
8014
  function timberPrebuilt(ctx) {
8015
+ /**
8016
+ * Module ID → prerender-capable component IDs declared in it. Recorded by
8017
+ * the RSC transform, read by the RSC `generateBundle` module-graph walk
8018
+ * that computes `ctx.routePrebuiltComponents`. Build only.
8019
+ */
8020
+ const moduleComponents = /* @__PURE__ */ new Map();
8021
+ /**
8022
+ * Module ID → variable name → component ID, for the prebuilt components
8023
+ * that module declares. Read at `generateBundle` time to trace imports in
8024
+ * other modules back to component IDs (cross-module call-site props).
8025
+ */
8026
+ const bindingNames = /* @__PURE__ */ new Map();
8121
8027
  return {
8122
8028
  name: "timber-prebuilt",
8029
+ api: { async capture(rscDir) {
8030
+ await cleanPrebuiltDir(ctx.buildDir);
8031
+ if (!ctx.routePrebuiltComponents || ctx.routePrebuiltComponents.size === 0) return null;
8032
+ if (ctx.config.output === "static") {
8033
+ console.log(`[timber] cache.component: skipped prerender capture (output: 'static' renders all components at build time)`);
8034
+ return null;
8035
+ }
8036
+ return runPrebuiltCapture(ctx, rscDir);
8037
+ } },
8123
8038
  transform(code, id) {
8124
- if (!ctx.dev && this?.environment?.name === "rsc") ctx.prebuiltModuleComponents?.delete(id);
8039
+ if (!ctx.dev && this?.environment?.name === "rsc") {
8040
+ moduleComponents.delete(id);
8041
+ bindingNames.delete(id);
8042
+ }
8125
8043
  if (!code.includes("@timber-js/app/cache") && !code.includes("timber/cache")) return;
8126
8044
  if (id.includes("node_modules")) return;
8127
8045
  if (this?.environment?.name === "client") return;
@@ -8153,8 +8071,7 @@ function timberPrebuilt(ctx) {
8153
8071
  s.appendLeft(call.arguments[0].start, `${JSON.stringify(firstArg)}, `);
8154
8072
  }
8155
8073
  if (!ctx.dev && componentIds.length > 0 && this?.environment?.name === "rsc") {
8156
- ctx.prebuiltModuleComponents ??= /* @__PURE__ */ new Map();
8157
- ctx.prebuiltModuleComponents.set(id, componentIds);
8074
+ moduleComponents.set(id, componentIds);
8158
8075
  const bindingMap = /* @__PURE__ */ new Map();
8159
8076
  let componentIdIdx = 0;
8160
8077
  for (const { call, stmt, displayName } of callSites) if (!ctx.dev && mayPrerender(call)) {
@@ -8174,10 +8091,7 @@ function timberPrebuilt(ctx) {
8174
8091
  }
8175
8092
  }
8176
8093
  }
8177
- if (bindingMap.size > 0) {
8178
- ctx.prebuiltBindingNames ??= /* @__PURE__ */ new Map();
8179
- ctx.prebuiltBindingNames.set(id, bindingMap);
8180
- }
8094
+ if (bindingMap.size > 0) bindingNames.set(id, bindingMap);
8181
8095
  const declaredBindings = new Set([...bindingMap.keys()].filter((k) => k !== DEFAULT_SENTINEL));
8182
8096
  if (declaredBindings.size > 0) {
8183
8097
  const usages = collectJsxUsages(program, declaredBindings);
@@ -8208,8 +8122,8 @@ function timberPrebuilt(ctx) {
8208
8122
  if (ctx.dev) return;
8209
8123
  if (this?.environment?.name !== "rsc") return;
8210
8124
  ctx.routePrebuiltComponents = void 0;
8211
- const moduleComponents = ctx.prebuiltModuleComponents;
8212
- if (!moduleComponents || moduleComponents.size === 0 || !ctx.routeTree) return;
8125
+ ctx.prebuiltLazyChunks = void 0;
8126
+ if (moduleComponents.size === 0 || !ctx.routeTree) return;
8213
8127
  const self = this;
8214
8128
  const getInfo = (id) => self.getModuleInfo?.(id) ?? null;
8215
8129
  const resolveFn = async (source, importer) => {
@@ -8237,7 +8151,7 @@ function timberPrebuilt(ctx) {
8237
8151
  }
8238
8152
  if (lazyChunks.size > 0) ctx.prebuiltLazyChunks = lazyChunks;
8239
8153
  }
8240
- if (ctx.prebuiltBindingNames && ctx.prebuiltBindingNames.size > 0) {
8154
+ if (bindingNames.size > 0) {
8241
8155
  const scannedModules = /* @__PURE__ */ new Set();
8242
8156
  for (const filePath of routeComponents.keys()) {
8243
8157
  const visited = /* @__PURE__ */ new Set();
@@ -8248,7 +8162,7 @@ function timberPrebuilt(ctx) {
8248
8162
  visited.add(modId);
8249
8163
  if (!scannedModules.has(modId)) {
8250
8164
  scannedModules.add(modId);
8251
- await collectCrossModuleProps(ctx, modId, getInfo, resolveFn);
8165
+ await collectCrossModuleProps(ctx, bindingNames, modId, getInfo, resolveFn);
8252
8166
  }
8253
8167
  const modInfo = getInfo(modId);
8254
8168
  if (!modInfo) continue;
@@ -8258,26 +8172,6 @@ function timberPrebuilt(ctx) {
8258
8172
  }
8259
8173
  }
8260
8174
  } else console.warn(`[timber] cache.component: ${moduleComponents.size} module(s) declare prebuilt components, but none are reachable from any page or layout — no payloads will be prerendered`);
8261
- },
8262
- /**
8263
- * Post-build capture (design/44 §Build Phase). `order: 'post'` runs
8264
- * after the RSC plugin's buildApp — all five build steps are complete —
8265
- * and BEFORE timber-adapter-build's post hook (plugin array order), so
8266
- * `prebuilt/` exists when the adapter assembles its output (TIM-1122).
8267
- */
8268
- buildApp: {
8269
- order: "post",
8270
- async handler() {
8271
- if (ctx.dev) return;
8272
- ctx.captureSummary = void 0;
8273
- await cleanPrebuiltDir(ctx.buildDir);
8274
- if (!ctx.routePrebuiltComponents || ctx.routePrebuiltComponents.size === 0) return;
8275
- if (ctx.config.output === "static") {
8276
- console.log(`[timber] cache.component: skipped prerender capture (output: 'static' renders all components at build time)`);
8277
- return;
8278
- }
8279
- ctx.captureSummary = await runPrebuiltCapture(ctx);
8280
- }
8281
8175
  }
8282
8176
  };
8283
8177
  }
@@ -8430,8 +8324,6 @@ function timberPrerenderSugar(ctx) {
8430
8324
  if (defaultExport.kind === "expression" && defaultExport.node.declaration) {
8431
8325
  if (code.slice(defaultExport.node.declaration.start, defaultExport.node.declaration.end).includes(".component(")) return;
8432
8326
  }
8433
- ctx.prerenderSugarPages ??= /* @__PURE__ */ new Set();
8434
- ctx.prerenderSugarPages.add(id);
8435
8327
  const s = new MagicString(code);
8436
8328
  const optionsSource = buildOptionsSource(value);
8437
8329
  const importAlias = "__timber_prerender_cache__";
@@ -8568,6 +8460,378 @@ function timberCloudflareWasm(ctx) {
8568
8460
  }];
8569
8461
  }
8570
8462
  //#endregion
8463
+ //#region src/plugins/rsc-assets-manifest.ts
8464
+ /**
8465
+ * The RSC plugin's assets manifest (`__vite_rsc_assets_manifest.js`) — read
8466
+ * into the build manifest, and stripped of JavaScript when client JS is off.
8467
+ *
8468
+ * The RSC plugin writes the file to rsc/ and ssr/ as a standalone file, not
8469
+ * a Rollup chunk, so no `generateBundle` can intercept it; both operations
8470
+ * work on the written files. Both run once, from the post-build phase's
8471
+ * manifest finalization step (plugins/post-build.ts), before anything
8472
+ * imports the built bundle.
8473
+ *
8474
+ * Design doc: 18-build-system.md §"Post-Build Phase"
8475
+ */
8476
+ /**
8477
+ * Parse the RSC assets manifest at `manifestPath`. Tries JSON.parse first
8478
+ * (fast path for plain-string manifests); falls back to a dynamic import()
8479
+ * for manifests that contain runtime expressions from renderBuiltUrl.
8480
+ * Returns null if both paths fail.
8481
+ */
8482
+ async function parseRscManifest(manifestPath, content) {
8483
+ const jsonStr = content.replace(/^export default\s*/, "").replace(/;?\s*$/, "");
8484
+ try {
8485
+ return JSON.parse(jsonStr);
8486
+ } catch {}
8487
+ try {
8488
+ return (await import(`${pathToFileURL(manifestPath).href}?t=${Date.now()}`)).default ?? null;
8489
+ } catch (err) {
8490
+ swallow(err, `failed to evaluate RSC assets manifest: ${manifestPath}`, { level: "warn" });
8491
+ return null;
8492
+ }
8493
+ }
8494
+ /**
8495
+ * Replace all `"js": [...]` array contents with `"js": []` using balanced
8496
+ * bracket matching. Handles nested brackets from runtime expressions like
8497
+ * `__rbu("path/to/[slug]/file.js")`.
8498
+ */
8499
+ function clearJsArrays(src) {
8500
+ const pattern = /"js"\s*:\s*\[/g;
8501
+ let result = "";
8502
+ let lastEnd = 0;
8503
+ let match;
8504
+ while ((match = pattern.exec(src)) !== null) {
8505
+ const arrayStart = match.index + match[0].length;
8506
+ let depth = 1;
8507
+ let i = arrayStart;
8508
+ while (i < src.length && depth > 0) {
8509
+ if (src[i] === "[") depth++;
8510
+ else if (src[i] === "]") depth--;
8511
+ i++;
8512
+ }
8513
+ result += src.slice(lastEnd, match.index) + "\"js\": []";
8514
+ lastEnd = i;
8515
+ }
8516
+ result += src.slice(lastEnd);
8517
+ return result;
8518
+ }
8519
+ /**
8520
+ * Replace `"bootstrapScriptContent": <value>` with an empty string. The
8521
+ * value may be a JS expression (e.g., concatenation with runtime helpers),
8522
+ * not a simple string literal. Scans forward from the colon, tracking
8523
+ * balanced parens and brackets, and stops at the next unbalanced `,` or `}`.
8524
+ */
8525
+ function clearBootstrapScriptContent(src) {
8526
+ const idx = src.indexOf("\"bootstrapScriptContent\"");
8527
+ if (idx === -1) return src;
8528
+ const colonIdx = src.indexOf(":", idx + 24);
8529
+ if (colonIdx === -1) return src;
8530
+ let valStart = colonIdx + 1;
8531
+ while (valStart < src.length && /\s/.test(src[valStart])) valStart++;
8532
+ let i = valStart;
8533
+ let depth = 0;
8534
+ let inString = false;
8535
+ let stringChar = "";
8536
+ while (i < src.length) {
8537
+ const ch = src[i];
8538
+ if (inString) {
8539
+ if (ch === "\\") {
8540
+ i += 2;
8541
+ continue;
8542
+ }
8543
+ if (ch === stringChar) inString = false;
8544
+ } else if (ch === "\"" || ch === "'") {
8545
+ inString = true;
8546
+ stringChar = ch;
8547
+ } else if (ch === "(" || ch === "[") depth++;
8548
+ else if (ch === ")" || ch === "]") depth--;
8549
+ else if (depth === 0 && (ch === "," || ch === "}" || ch === "\n")) break;
8550
+ i++;
8551
+ }
8552
+ return src.slice(0, idx) + "\"bootstrapScriptContent\": \"\"" + src.slice(i);
8553
+ }
8554
+ /**
8555
+ * Read the RSC plugin's assets manifest and merge per-route CSS and
8556
+ * per-route JS modulepreloads into the build manifest.
8557
+ *
8558
+ * CSS: `serverResources` maps server component paths to CSS URLs.
8559
+ * JS: `clientReferenceDeps` maps reference hashes to JS dep URLs.
8560
+ * Combined with `routeClientRefKeys` (from the RSC generateBundle
8561
+ * module graph walk) to produce per-route modulepreload entries.
8562
+ */
8563
+ async function mergeRscAssetsManifest(rscDir, root, buildManifest, routeClientRefKeys) {
8564
+ const manifestPath = join(rscDir, "__vite_rsc_assets_manifest.js");
8565
+ let content;
8566
+ try {
8567
+ content = await readFile(manifestPath, "utf-8");
8568
+ } catch {
8569
+ return;
8570
+ }
8571
+ const manifest = await parseRscManifest(manifestPath, content);
8572
+ if (!manifest) return;
8573
+ const serverResources = manifest.serverResources;
8574
+ if (serverResources) {
8575
+ for (const [relativePath, deps] of Object.entries(serverResources)) if (deps.css && deps.css.length > 0) {
8576
+ const absolutePath = join(root, relativePath);
8577
+ buildManifest.css[absolutePath] = deps.css;
8578
+ }
8579
+ }
8580
+ if (routeClientRefKeys && routeClientRefKeys.size > 0) {
8581
+ const clientReferenceDeps = manifest.clientReferenceDeps;
8582
+ if (clientReferenceDeps) for (const [filePath, refKeys] of routeClientRefKeys) {
8583
+ const jsUrls = /* @__PURE__ */ new Set();
8584
+ for (const key of refKeys) {
8585
+ const deps = clientReferenceDeps[key];
8586
+ if (deps?.js) for (const url of deps.js) jsUrls.add(url);
8587
+ }
8588
+ if (jsUrls.size > 0) buildManifest.modulepreload[filePath] = Array.from(jsUrls);
8589
+ }
8590
+ }
8591
+ }
8592
+ /**
8593
+ * Strip JS references from the RSC plugin's assets manifest files.
8594
+ *
8595
+ * The RSC plugin writes `__vite_rsc_assets_manifest.js` to rsc/ and ssr/
8596
+ * as standalone files (not Rollup chunks), so generateBundle can't
8597
+ * intercept them. This rewrites the files on disk after all builds
8598
+ * complete but before the adapter copies them to the output directory.
8599
+ */
8600
+ async function stripJsFromRscAssetsManifests(serverDirs) {
8601
+ const paths = serverDirs.map((dir) => join(dir, "__vite_rsc_assets_manifest.js"));
8602
+ for (const path of paths) {
8603
+ let content;
8604
+ try {
8605
+ content = await readFile(path, "utf-8");
8606
+ } catch {
8607
+ continue;
8608
+ }
8609
+ const jsonStr = content.replace(/^export default\s*/, "").replace(/;?\s*$/, "");
8610
+ let isStrictJson = true;
8611
+ let manifest = null;
8612
+ try {
8613
+ manifest = JSON.parse(jsonStr);
8614
+ } catch {
8615
+ isStrictJson = false;
8616
+ }
8617
+ if (!isStrictJson) {
8618
+ let rewritten = content;
8619
+ rewritten = clearJsArrays(rewritten);
8620
+ rewritten = clearBootstrapScriptContent(rewritten);
8621
+ await writeFile(path, rewritten);
8622
+ continue;
8623
+ }
8624
+ const deps = manifest?.clientReferenceDeps;
8625
+ if (deps) for (const entry of Object.values(deps)) entry.js = [];
8626
+ if (manifest) manifest.bootstrapScriptContent = "";
8627
+ await writeFile(path, `export default ${JSON.stringify(manifest, null, 2)};\n`);
8628
+ }
8629
+ }
8630
+ //#endregion
8631
+ //#region src/plugins/post-build.ts
8632
+ /** The Vite environments whose output adapters consume. */
8633
+ var ARTIFACT_ENVIRONMENTS = [
8634
+ "client",
8635
+ "rsc",
8636
+ "ssr"
8637
+ ];
8638
+ function isArtifactEnvironment(name) {
8639
+ return ARTIFACT_ENVIRONMENTS.includes(name);
8640
+ }
8641
+ function timberPostBuild(ctx, steps) {
8642
+ const environments = {};
8643
+ /** What the client environment's bundle wrote, from its `writeBundle`. */
8644
+ let clientOutput = {
8645
+ assetsDir: "",
8646
+ emitted: /* @__PURE__ */ new Set()
8647
+ };
8648
+ return {
8649
+ name: "timber-post-build",
8650
+ writeBundle(options, bundle) {
8651
+ if (ctx.dev) return;
8652
+ const env = this.environment;
8653
+ if (!isArtifactEnvironment(env.name)) return;
8654
+ environments[env.name] = { path: resolve(env.config.root, env.config.build.outDir) };
8655
+ if (env.name === "client") clientOutput = {
8656
+ assetsDir: hashedAssetsDir(env.config.build.assetsDir, options),
8657
+ emitted: new Set(Object.keys(bundle))
8658
+ };
8659
+ },
8660
+ buildApp: {
8661
+ order: "post",
8662
+ async handler() {
8663
+ if (ctx.dev) return;
8664
+ await runPostBuild(ctx, steps, environments, clientOutput);
8665
+ }
8666
+ }
8667
+ };
8668
+ }
8669
+ /**
8670
+ * The client bundle's assets directory, in the artifacts' form (no leading
8671
+ * or trailing slash) — or `''` when its files are not all content-hashed.
8672
+ *
8673
+ * `assetsDir` names where Vite writes; it is the naming patterns that decide
8674
+ * whether a file name carries a hash. Vite's defaults do
8675
+ * (`<assetsDir>/[name]-[hash].js`), but an app can set
8676
+ * `assetFileNames: 'static/[name][extname]'`, and marking that directory
8677
+ * immutable would pin a changed image under an unchanged URL for a year. A
8678
+ * pattern given as a function cannot be inspected, so it counts as unhashed:
8679
+ * those files fall back to the revalidating per-file rule.
8680
+ *
8681
+ * @internal Exported for testing.
8682
+ */
8683
+ function hashedAssetsDir(assetsDir, output) {
8684
+ const dir = assetsDir.replace(/^\/+|\/+$/g, "");
8685
+ return [
8686
+ output.entryFileNames,
8687
+ output.chunkFileNames,
8688
+ output.assetFileNames
8689
+ ].every((p) => typeof p === "string" && p.includes("[hash")) ? dir : "";
8690
+ }
8691
+ /**
8692
+ * Keep only the directories whose every file the client bundle emitted.
8693
+ *
8694
+ * A producer knows it hashes what *it* writes, not what else lands beside it:
8695
+ * Vite copies `public/` into the client output verbatim, so `public/assets/logo.png`
8696
+ * sits in `assets/` under a stable name, and `public/_rsc/…` would sit among
8697
+ * the hashed payloads. A directory holding any file the bundle did not emit
8698
+ * is not content-hashed as a whole, and marking it immutable would pin that
8699
+ * file for a year — so it is dropped, and its files take the per-file rule.
8700
+ *
8701
+ * @internal Exported for testing.
8702
+ */
8703
+ async function onlyEmittedDirs(clientDir, dirs, emitted) {
8704
+ const kept = [];
8705
+ for (const dir of dirs) {
8706
+ const root = join(clientDir, dir);
8707
+ if (!(await readdir(root, {
8708
+ recursive: true,
8709
+ withFileTypes: true
8710
+ }).catch(() => [])).some((entry) => entry.isFile() && !emitted.has(relative(clientDir, join(entry.parentPath, entry.name)).replace(/\\/g, "/")))) kept.push(dir);
8711
+ }
8712
+ return kept;
8713
+ }
8714
+ async function runPostBuild(ctx, steps, environments, clientOutput) {
8715
+ const output = ctx.config.output ?? "server";
8716
+ const rsc = requireEnvironment(environments, "rsc");
8717
+ const ssr = requireEnvironment(environments, "ssr");
8718
+ const manifest = await finalizeBuildManifest(ctx, steps.fonts, rsc, ssr);
8719
+ if (manifest) Reflect.set(globalThis, "__TIMBER_BUILD_MANIFEST__", manifest);
8720
+ const prebuilt = await steps.prebuilt.capture(rsc.path);
8721
+ const site = output === "static" ? await runStaticGeneration(ctx, rsc.path) : void 0;
8722
+ steps.report.print(prebuilt);
8723
+ const adapter = contextAdapter(ctx);
8724
+ if (!adapter || typeof adapter.buildOutput !== "function") return;
8725
+ await adapter.buildOutput(adapterConfig(ctx), assembleBuildArtifacts({
8726
+ output,
8727
+ outputDir: ctx.buildDir,
8728
+ environments,
8729
+ immutableDirs: await onlyEmittedDirs(requireEnvironment(environments, "client").path, [
8730
+ ...clientOutput.assetsDir ? [clientOutput.assetsDir] : [],
8731
+ ...steps.fonts.immutableDirs(),
8732
+ ...site?.immutableDirs ?? []
8733
+ ], clientOutput.emitted),
8734
+ prebuilt: prebuilt?.artifact,
8735
+ site,
8736
+ manifestInit: manifest ? serializeManifestInit(manifest) : void 0
8737
+ }));
8738
+ }
8739
+ function requireEnvironment(environments, name) {
8740
+ const artifact = environments[name];
8741
+ if (!artifact) throw new Error(`[timber] Build output: the ${name} environment wrote no bundle`);
8742
+ return artifact;
8743
+ }
8744
+ /**
8745
+ * Step 1: bring the build manifest to its final shape, once.
8746
+ *
8747
+ * `timber-build-manifest` creates it from the client bundle; this adds what
8748
+ * only exists once every environment is built — the font entries and the
8749
+ * per-route CSS and modulepreloads in the RSC plugin's assets manifest —
8750
+ * and then, when client JavaScript is disabled, removes every JavaScript
8751
+ * reference, from the manifest and from the assets manifests on disk. The
8752
+ * manifest's `js` / `modulepreload` are cleared after the merge has written
8753
+ * into them, so nothing the merge adds survives.
8754
+ *
8755
+ * Returns null only when no client bundle produced a manifest.
8756
+ *
8757
+ * @internal Exported for testing.
8758
+ */
8759
+ async function finalizeBuildManifest(ctx, fonts, rsc, ssr) {
8760
+ const manifest = ctx.buildManifest;
8761
+ if (manifest) {
8762
+ fonts.contributeManifest(manifest);
8763
+ await mergeRscAssetsManifest(rsc.path, ctx.root, manifest, ctx.routeClientRefKeys);
8764
+ }
8765
+ if (ctx.clientJavascript.disabled) {
8766
+ if (manifest) {
8767
+ manifest.js = {};
8768
+ manifest.modulepreload = {};
8769
+ }
8770
+ await stripJsFromRscAssetsManifests([rsc.path, ssr.path]);
8771
+ }
8772
+ return manifest;
8773
+ }
8774
+ /**
8775
+ * The build manifest as a JS module that sets the global. Adapters write
8776
+ * it as `_timber-manifest-init.js` (or prepend it to the entry) and
8777
+ * evaluate it before the RSC handler, so `virtual:timber-build-manifest`
8778
+ * finds it.
8779
+ */
8780
+ function serializeManifestInit(manifest) {
8781
+ return `globalThis.__TIMBER_BUILD_MANIFEST__ = ${JSON.stringify(manifest)};\n`;
8782
+ }
8783
+ /**
8784
+ * Dedupe the producers' directories and drop any that sits inside another.
8785
+ *
8786
+ * Two producers can name overlapping directories — a custom `assetsDir` of
8787
+ * `_timber` beside the fonts' `_timber/fonts`. Cloudflare and Netlify apply
8788
+ * every matching `_headers` rule, so emitting both would send two
8789
+ * `Cache-Control` values for one file; the outer directory already covers
8790
+ * the inner one.
8791
+ */
8792
+ function canonicalImmutableDirs(dirs) {
8793
+ const unique = [...new Set(dirs)];
8794
+ return unique.filter((dir) => !unique.some((other) => other !== dir && dir.startsWith(`${other}/`)));
8795
+ }
8796
+ /**
8797
+ * Assemble the adapter's view of the build from what each step returned.
8798
+ *
8799
+ * Not a validator: every field is copied from the phase that produced it.
8800
+ * The throws are the states the union cannot represent — a Vite environment
8801
+ * that wrote no bundle, a static build with no site. Each means an earlier
8802
+ * phase failed without failing the build, and a deploy missing its SSR
8803
+ * bundle fails on the first request rather than here.
8804
+ *
8805
+ * @internal Exported for testing.
8806
+ */
8807
+ function assembleBuildArtifacts(input) {
8808
+ const { output, outputDir, environments } = input;
8809
+ const client = requireEnvironment(environments, "client");
8810
+ const immutableDirs = canonicalImmutableDirs(input.immutableDirs);
8811
+ if (output === "static") {
8812
+ if (!input.site) throw new Error("[timber] Build output: output is 'static' but static generation recorded no site");
8813
+ const { pages, contentTypes } = input.site;
8814
+ return {
8815
+ output,
8816
+ outputDir,
8817
+ client,
8818
+ immutableDirs,
8819
+ pages,
8820
+ contentTypes
8821
+ };
8822
+ }
8823
+ return {
8824
+ output,
8825
+ outputDir,
8826
+ client,
8827
+ immutableDirs,
8828
+ rsc: requireEnvironment(environments, "rsc"),
8829
+ ssr: requireEnvironment(environments, "ssr"),
8830
+ prebuilt: input.prebuilt,
8831
+ manifestInit: input.manifestInit
8832
+ };
8833
+ }
8834
+ //#endregion
8571
8835
  //#region src/plugins/build-report.ts
8572
8836
  /**
8573
8837
  * timber-build-report — Post-build route summary table.
@@ -8671,13 +8935,10 @@ function pad(str, width, align = "right") {
8671
8935
  function stripAnsi(str) {
8672
8936
  return str.replace(/\u001b\[\d+m/g, "");
8673
8937
  }
8674
- function loadPrebuiltManifest(buildDir) {
8675
- try {
8676
- const raw = readFileSync(join(buildDir, "prebuilt", "manifest.json"), "utf-8");
8677
- return JSON.parse(raw);
8678
- } catch {
8679
- return null;
8680
- }
8938
+ /** Read the manifest of the prebuilt artifact this build recorded, if any. */
8939
+ function loadPrebuiltManifest(prebuiltDir) {
8940
+ if (!prebuiltDir) return null;
8941
+ return JSON.parse(readFileSync(join(prebuiltDir, "manifest.json"), "utf-8"));
8681
8942
  }
8682
8943
  function buildPrebuiltReportData(manifest, summary) {
8683
8944
  const hasCaptures = summary && (summary.captured > 0 || summary.skipped.length > 0);
@@ -8881,11 +9142,47 @@ function timberBuildReport(ctx) {
8881
9142
  let logger = null;
8882
9143
  let chunkSizes = null;
8883
9144
  let clientBundle = null;
8884
- let reported = false;
8885
- let deferReport = false;
8886
9145
  const buildStart = performance.now();
9146
+ function printRouteTable() {
9147
+ if (!ctx.routeTree || !chunkSizes || !clientBundle || !logger) return;
9148
+ const outputMode = ctx.config.output ?? "server";
9149
+ const { entries, sharedGzip } = ctx.clientJavascript.disabled ? {
9150
+ entries: collectRoutes(ctx.routeTree).map((info) => ({
9151
+ path: info.path,
9152
+ type: classifyRoute(info.segments, outputMode),
9153
+ size: 0,
9154
+ firstLoadSize: 0
9155
+ })),
9156
+ sharedGzip: 0
9157
+ } : buildEntries(ctx.routeTree, chunkSizes, clientBundle, outputMode, ctx.appDir, ctx.root);
9158
+ const elapsed = ((performance.now() - buildStart) / 1e3).toFixed(2);
9159
+ const lines = buildRouteReport(entries, sharedGzip);
9160
+ logger.info("");
9161
+ for (const line of lines) logger.info(line);
9162
+ const warnings = buildWarnings(entries, ctx.config.budget);
9163
+ if (warnings.length > 0) {
9164
+ logger.info("");
9165
+ for (const w of warnings) logger.warn(w);
9166
+ }
9167
+ logger.info("");
9168
+ logger.info(`✓ built ${entries.length} routes for all three environments (rsc, ssr, client) in ${elapsed}s`);
9169
+ logger.info("");
9170
+ }
9171
+ function printPrebuiltReport(prebuilt) {
9172
+ if (!logger || !prebuilt) return;
9173
+ const prebuiltData = buildPrebuiltReportData(loadPrebuiltManifest(prebuilt.artifact?.path), prebuilt.summary);
9174
+ if (!prebuiltData) return;
9175
+ logger.info("");
9176
+ for (const line of buildPrebuiltReport(prebuiltData)) logger.info(line);
9177
+ logger.info("");
9178
+ }
8887
9179
  return {
8888
9180
  name: "timber-build-report",
9181
+ api: { print(prebuilt) {
9182
+ if (ctx.dev) return;
9183
+ printRouteTable();
9184
+ printPrebuiltReport(prebuilt);
9185
+ } },
8889
9186
  config(_cfg, { command }) {
8890
9187
  if (command !== "build") return;
8891
9188
  return { environments: {
@@ -8902,50 +9199,6 @@ function timberBuildReport(ctx) {
8902
9199
  if (this.environment?.name && this.environment.name !== "client") return;
8903
9200
  chunkSizes = collectChunkSizes(bundle);
8904
9201
  clientBundle = { ...bundle };
8905
- deferReport = true;
8906
- },
8907
- closeBundle() {
8908
- if (ctx.dev || reported) return;
8909
- if (deferReport) {
8910
- deferReport = false;
8911
- return;
8912
- }
8913
- if (!ctx.routeTree || !chunkSizes || !clientBundle || !logger) return;
8914
- reported = true;
8915
- const outputMode = ctx.config.output ?? "server";
8916
- const { entries, sharedGzip } = ctx.clientJavascript.disabled ? {
8917
- entries: collectRoutes(ctx.routeTree).map((info) => ({
8918
- path: info.path,
8919
- type: classifyRoute(info.segments, outputMode),
8920
- size: 0,
8921
- firstLoadSize: 0
8922
- })),
8923
- sharedGzip: 0
8924
- } : buildEntries(ctx.routeTree, chunkSizes, clientBundle, outputMode, ctx.appDir, ctx.root);
8925
- const elapsed = ((performance.now() - buildStart) / 1e3).toFixed(2);
8926
- const lines = buildRouteReport(entries, sharedGzip);
8927
- logger.info("");
8928
- for (const line of lines) logger.info(line);
8929
- const warnings = buildWarnings(entries, ctx.config.budget);
8930
- if (warnings.length > 0) {
8931
- logger.info("");
8932
- for (const w of warnings) logger.warn(w);
8933
- }
8934
- logger.info("");
8935
- logger.info(`✓ built ${entries.length} routes for all three environments (rsc, ssr, client) in ${elapsed}s`);
8936
- logger.info("");
8937
- },
8938
- buildApp: {
8939
- order: "post",
8940
- async handler() {
8941
- if (ctx.dev || !logger) return;
8942
- const prebuiltData = buildPrebuiltReportData(loadPrebuiltManifest(ctx.buildDir), ctx.captureSummary);
8943
- if (!prebuiltData) return;
8944
- logger.info("");
8945
- const prebuiltLines = buildPrebuiltReport(prebuiltData);
8946
- for (const line of prebuiltLines) logger.info(line);
8947
- logger.info("");
8948
- }
8949
9202
  }
8950
9203
  };
8951
9204
  }
@@ -9287,10 +9540,8 @@ function timber(config) {
9287
9540
  }
9288
9541
  ctx.config.output ??= "server";
9289
9542
  ctx.timer.end("config-load");
9290
- const viteOutDir = userConfig.build?.outDir;
9291
- const timberBuildDir = ctx.config.buildDir;
9292
9543
  const resolvedRoot = resolve(userConfig.root ?? process.cwd());
9293
- ctx.buildDir = resolveBuildDir(resolvedRoot, timberBuildDir, viteOutDir);
9544
+ const buildDir = resolveBuildDir(resolvedRoot, ctx.config.buildDir, userConfig.build?.outDir);
9294
9545
  if (viteRoot !== process.cwd()) {
9295
9546
  const hasCompilerInReloaded = !config?.reactCompiler && ctx.config.reactCompiler;
9296
9547
  const hadCompilerInEarly = !config?.reactCompiler && earlyFileConfig?.reactCompiler;
@@ -9321,7 +9572,7 @@ function timber(config) {
9321
9572
  resolvedDevPort = start.port;
9322
9573
  resolvedDevPortExplicit = start.explicit;
9323
9574
  }
9324
- const buildOutDir = relative(resolvedRoot, ctx.buildDir);
9575
+ const buildOutDir = relative(resolvedRoot, buildDir);
9325
9576
  const envOutDirs = {};
9326
9577
  for (const envName of [
9327
9578
  "rsc",
@@ -9385,8 +9636,12 @@ function timber(config) {
9385
9636
  };
9386
9637
  const effectiveReactCompiler = config?.reactCompiler ?? earlyFileConfig?.reactCompiler ?? true;
9387
9638
  // @vitejs/plugin-rsc handles:
9639
+ const fonts = timberFonts(ctx);
9640
+ const prebuilt = timberPrebuilt(ctx);
9641
+ const buildReport = timberBuildReport(ctx);
9388
9642
  return [
9389
9643
  rootSync,
9644
+ timberReactResolve(),
9390
9645
  timberReactProd(),
9391
9646
  ...patchReactCompilerForProdJsx(react({ compiler: effectiveReactCompiler || false })),
9392
9647
  vitePluginRsc(createRscOptions(ctx, encryptionKeyExpr)),
@@ -9394,19 +9649,23 @@ function timber(config) {
9394
9649
  timberRouting(ctx),
9395
9650
  timberEntries(ctx),
9396
9651
  timberBuildManifest(ctx),
9397
- timberFonts(ctx),
9652
+ fonts,
9398
9653
  timberMdx(ctx),
9399
9654
  timberRequestDep(ctx),
9400
9655
  timberCacheTransform(ctx),
9401
9656
  timberPrerenderSugar(ctx),
9402
- timberPrebuilt(ctx),
9657
+ prebuilt,
9403
9658
  timberStaticBuild(ctx),
9404
9659
  timberContent(ctx),
9405
9660
  timberServerBundle(),
9406
9661
  timberCloudflareWasm(ctx),
9407
9662
  timberChunks(ctx),
9408
- timberBuildReport(ctx),
9409
- timberAdapterBuild(ctx),
9663
+ buildReport,
9664
+ timberPostBuild(ctx, {
9665
+ fonts: fonts.api,
9666
+ prebuilt: prebuilt.api,
9667
+ report: buildReport.api
9668
+ }),
9410
9669
  timberDevLogs(ctx),
9411
9670
  timberDevServer(ctx)
9412
9671
  ];