@timber-js/app 0.2.0-alpha.5 → 0.2.0-alpha.51

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 (334) hide show
  1. package/LICENSE +8 -0
  2. package/dist/_chunks/{als-registry-B7DbZ2hS.js → als-registry-Ba7URUIn.js} +1 -1
  3. package/dist/_chunks/als-registry-Ba7URUIn.js.map +1 -0
  4. package/dist/_chunks/chunk-DYhsFzuS.js +33 -0
  5. package/dist/_chunks/{debug-gwlJkDuf.js → debug-ECi_61pb.js} +2 -2
  6. package/dist/_chunks/debug-ECi_61pb.js.map +1 -0
  7. package/dist/_chunks/define-TK8C1M3x.js +279 -0
  8. package/dist/_chunks/define-TK8C1M3x.js.map +1 -0
  9. package/dist/_chunks/define-cookie-k9btcEfI.js +93 -0
  10. package/dist/_chunks/define-cookie-k9btcEfI.js.map +1 -0
  11. package/dist/_chunks/error-boundary-B9vT_YK_.js +211 -0
  12. package/dist/_chunks/error-boundary-B9vT_YK_.js.map +1 -0
  13. package/dist/_chunks/{format-DviM89f0.js → format-cX7wzEp2.js} +2 -2
  14. package/dist/_chunks/{format-DviM89f0.js.map → format-cX7wzEp2.js.map} +1 -1
  15. package/dist/_chunks/{interception-BOoWmLUA.js → interception-D2djYaIm.js} +112 -77
  16. package/dist/_chunks/interception-D2djYaIm.js.map +1 -0
  17. package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-BU684ls2.js} +1 -1
  18. package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js.map → metadata-routes-BU684ls2.js.map} +1 -1
  19. package/dist/_chunks/{request-context-DIkVh_jG.js → request-context-0h-6Voad.js} +95 -69
  20. package/dist/_chunks/request-context-0h-6Voad.js.map +1 -0
  21. package/dist/_chunks/segment-context-DBn-nrMN.js +69 -0
  22. package/dist/_chunks/segment-context-DBn-nrMN.js.map +1 -0
  23. package/dist/_chunks/stale-reload-4L-_skC7.js +47 -0
  24. package/dist/_chunks/stale-reload-4L-_skC7.js.map +1 -0
  25. package/dist/_chunks/{tracing-Cwn7697K.js → tracing-JI4cYUdz.js} +17 -3
  26. package/dist/_chunks/{tracing-Cwn7697K.js.map → tracing-JI4cYUdz.js.map} +1 -1
  27. package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-wEXY2JQB.js} +1 -1
  28. package/dist/_chunks/{use-query-states-D5KaffOK.js.map → use-query-states-wEXY2JQB.js.map} +1 -1
  29. package/dist/_chunks/wrappers-C9XPg7-U.js +63 -0
  30. package/dist/_chunks/wrappers-C9XPg7-U.js.map +1 -0
  31. package/dist/adapters/compress-module.d.ts.map +1 -1
  32. package/dist/adapters/nitro.d.ts +17 -1
  33. package/dist/adapters/nitro.d.ts.map +1 -1
  34. package/dist/adapters/nitro.js +56 -13
  35. package/dist/adapters/nitro.js.map +1 -1
  36. package/dist/cache/fast-hash.d.ts +22 -0
  37. package/dist/cache/fast-hash.d.ts.map +1 -0
  38. package/dist/cache/index.d.ts +5 -2
  39. package/dist/cache/index.d.ts.map +1 -1
  40. package/dist/cache/index.js +90 -20
  41. package/dist/cache/index.js.map +1 -1
  42. package/dist/cache/register-cached-function.d.ts.map +1 -1
  43. package/dist/cache/singleflight.d.ts +18 -1
  44. package/dist/cache/singleflight.d.ts.map +1 -1
  45. package/dist/cache/timber-cache.d.ts +1 -1
  46. package/dist/cache/timber-cache.d.ts.map +1 -1
  47. package/dist/client/error-boundary.d.ts +10 -1
  48. package/dist/client/error-boundary.d.ts.map +1 -1
  49. package/dist/client/error-boundary.js +1 -125
  50. package/dist/client/index.d.ts +3 -2
  51. package/dist/client/index.d.ts.map +1 -1
  52. package/dist/client/index.js +213 -93
  53. package/dist/client/index.js.map +1 -1
  54. package/dist/client/link.d.ts +22 -8
  55. package/dist/client/link.d.ts.map +1 -1
  56. package/dist/client/navigation-context.d.ts +2 -2
  57. package/dist/client/router.d.ts +25 -3
  58. package/dist/client/router.d.ts.map +1 -1
  59. package/dist/client/rsc-fetch.d.ts +23 -2
  60. package/dist/client/rsc-fetch.d.ts.map +1 -1
  61. package/dist/client/segment-cache.d.ts +1 -1
  62. package/dist/client/segment-cache.d.ts.map +1 -1
  63. package/dist/client/segment-context.d.ts +1 -1
  64. package/dist/client/segment-context.d.ts.map +1 -1
  65. package/dist/client/segment-merger.d.ts.map +1 -1
  66. package/dist/client/stale-reload.d.ts +15 -0
  67. package/dist/client/stale-reload.d.ts.map +1 -1
  68. package/dist/client/top-loader.d.ts +1 -1
  69. package/dist/client/top-loader.d.ts.map +1 -1
  70. package/dist/client/transition-root.d.ts +1 -1
  71. package/dist/client/transition-root.d.ts.map +1 -1
  72. package/dist/client/use-params.d.ts +2 -2
  73. package/dist/client/use-params.d.ts.map +1 -1
  74. package/dist/client/use-query-states.d.ts +1 -1
  75. package/dist/codec.d.ts +21 -0
  76. package/dist/codec.d.ts.map +1 -0
  77. package/dist/cookies/define-cookie.d.ts +33 -12
  78. package/dist/cookies/define-cookie.d.ts.map +1 -1
  79. package/dist/cookies/index.js +1 -83
  80. package/dist/fonts/css.d.ts +1 -0
  81. package/dist/fonts/css.d.ts.map +1 -1
  82. package/dist/index.d.ts +112 -35
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +467 -246
  85. package/dist/index.js.map +1 -1
  86. package/dist/params/define.d.ts +76 -0
  87. package/dist/params/define.d.ts.map +1 -0
  88. package/dist/params/index.d.ts +8 -0
  89. package/dist/params/index.d.ts.map +1 -0
  90. package/dist/params/index.js +105 -0
  91. package/dist/params/index.js.map +1 -0
  92. package/dist/plugins/adapter-build.d.ts.map +1 -1
  93. package/dist/plugins/build-manifest.d.ts.map +1 -1
  94. package/dist/plugins/client-chunks.d.ts +32 -0
  95. package/dist/plugins/client-chunks.d.ts.map +1 -0
  96. package/dist/plugins/dev-error-overlay.d.ts +26 -1
  97. package/dist/plugins/dev-error-overlay.d.ts.map +1 -1
  98. package/dist/plugins/entries.d.ts.map +1 -1
  99. package/dist/plugins/fonts.d.ts +7 -0
  100. package/dist/plugins/fonts.d.ts.map +1 -1
  101. package/dist/plugins/routing.d.ts.map +1 -1
  102. package/dist/plugins/server-bundle.d.ts.map +1 -1
  103. package/dist/plugins/static-build.d.ts.map +1 -1
  104. package/dist/routing/codegen.d.ts +2 -2
  105. package/dist/routing/codegen.d.ts.map +1 -1
  106. package/dist/routing/index.js +1 -1
  107. package/dist/routing/scanner.d.ts.map +1 -1
  108. package/dist/routing/status-file-lint.d.ts +2 -1
  109. package/dist/routing/status-file-lint.d.ts.map +1 -1
  110. package/dist/routing/types.d.ts +6 -4
  111. package/dist/routing/types.d.ts.map +1 -1
  112. package/dist/rsc-runtime/rsc.d.ts +1 -1
  113. package/dist/rsc-runtime/rsc.d.ts.map +1 -1
  114. package/dist/rsc-runtime/ssr.d.ts +12 -0
  115. package/dist/rsc-runtime/ssr.d.ts.map +1 -1
  116. package/dist/search-params/codecs.d.ts +1 -1
  117. package/dist/search-params/define.d.ts +159 -0
  118. package/dist/search-params/define.d.ts.map +1 -0
  119. package/dist/search-params/index.d.ts +4 -5
  120. package/dist/search-params/index.d.ts.map +1 -1
  121. package/dist/search-params/index.js +4 -474
  122. package/dist/search-params/registry.d.ts +1 -1
  123. package/dist/search-params/wrappers.d.ts +53 -0
  124. package/dist/search-params/wrappers.d.ts.map +1 -0
  125. package/dist/server/access-gate.d.ts +4 -0
  126. package/dist/server/access-gate.d.ts.map +1 -1
  127. package/dist/server/action-client.d.ts.map +1 -1
  128. package/dist/server/action-encryption.d.ts +76 -0
  129. package/dist/server/action-encryption.d.ts.map +1 -0
  130. package/dist/server/action-handler.d.ts.map +1 -1
  131. package/dist/server/als-registry.d.ts +18 -4
  132. package/dist/server/als-registry.d.ts.map +1 -1
  133. package/dist/server/build-manifest.d.ts +2 -2
  134. package/dist/server/build-manifest.d.ts.map +1 -1
  135. package/dist/server/debug.d.ts +1 -1
  136. package/dist/server/default-logger.d.ts +22 -0
  137. package/dist/server/default-logger.d.ts.map +1 -0
  138. package/dist/server/deny-renderer.d.ts.map +1 -1
  139. package/dist/server/early-hints.d.ts +13 -5
  140. package/dist/server/early-hints.d.ts.map +1 -1
  141. package/dist/server/error-boundary-wrapper.d.ts +4 -0
  142. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  143. package/dist/server/flight-injection-state.d.ts +66 -0
  144. package/dist/server/flight-injection-state.d.ts.map +1 -0
  145. package/dist/server/flight-scripts.d.ts +39 -0
  146. package/dist/server/flight-scripts.d.ts.map +1 -0
  147. package/dist/server/flush.d.ts.map +1 -1
  148. package/dist/server/form-data.d.ts +29 -0
  149. package/dist/server/form-data.d.ts.map +1 -1
  150. package/dist/server/html-injectors.d.ts +51 -11
  151. package/dist/server/html-injectors.d.ts.map +1 -1
  152. package/dist/server/index.d.ts +4 -2
  153. package/dist/server/index.d.ts.map +1 -1
  154. package/dist/server/index.js +1974 -1648
  155. package/dist/server/index.js.map +1 -1
  156. package/dist/server/logger.d.ts +24 -7
  157. package/dist/server/logger.d.ts.map +1 -1
  158. package/dist/server/node-stream-transforms.d.ts +113 -0
  159. package/dist/server/node-stream-transforms.d.ts.map +1 -0
  160. package/dist/server/pipeline.d.ts +7 -4
  161. package/dist/server/pipeline.d.ts.map +1 -1
  162. package/dist/server/primitives.d.ts +30 -3
  163. package/dist/server/primitives.d.ts.map +1 -1
  164. package/dist/server/render-timeout.d.ts +51 -0
  165. package/dist/server/render-timeout.d.ts.map +1 -0
  166. package/dist/server/request-context.d.ts +65 -38
  167. package/dist/server/request-context.d.ts.map +1 -1
  168. package/dist/server/route-element-builder.d.ts +7 -0
  169. package/dist/server/route-element-builder.d.ts.map +1 -1
  170. package/dist/server/route-handler.d.ts.map +1 -1
  171. package/dist/server/route-matcher.d.ts +2 -2
  172. package/dist/server/route-matcher.d.ts.map +1 -1
  173. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  174. package/dist/server/rsc-entry/helpers.d.ts +46 -3
  175. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  176. package/dist/server/rsc-entry/index.d.ts +6 -1
  177. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  178. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  179. package/dist/server/rsc-entry/rsc-stream.d.ts +9 -0
  180. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  181. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  182. package/dist/server/slot-resolver.d.ts +1 -1
  183. package/dist/server/slot-resolver.d.ts.map +1 -1
  184. package/dist/server/ssr-entry.d.ts +22 -0
  185. package/dist/server/ssr-entry.d.ts.map +1 -1
  186. package/dist/server/ssr-render.d.ts +39 -21
  187. package/dist/server/ssr-render.d.ts.map +1 -1
  188. package/dist/server/ssr-wrappers.d.ts +50 -0
  189. package/dist/server/ssr-wrappers.d.ts.map +1 -0
  190. package/dist/server/tracing.d.ts +10 -0
  191. package/dist/server/tracing.d.ts.map +1 -1
  192. package/dist/server/tree-builder.d.ts +19 -12
  193. package/dist/server/tree-builder.d.ts.map +1 -1
  194. package/dist/server/types.d.ts +1 -3
  195. package/dist/server/types.d.ts.map +1 -1
  196. package/dist/server/version-skew.d.ts +61 -0
  197. package/dist/server/version-skew.d.ts.map +1 -0
  198. package/dist/server/waituntil-bridge.d.ts.map +1 -1
  199. package/dist/shared/merge-search-params.d.ts +22 -0
  200. package/dist/shared/merge-search-params.d.ts.map +1 -0
  201. package/dist/shims/navigation-client.d.ts +1 -1
  202. package/dist/shims/navigation-client.d.ts.map +1 -1
  203. package/dist/shims/navigation.d.ts +1 -1
  204. package/dist/shims/navigation.d.ts.map +1 -1
  205. package/dist/utils/state-machine.d.ts +80 -0
  206. package/dist/utils/state-machine.d.ts.map +1 -0
  207. package/package.json +17 -14
  208. package/src/adapters/compress-module.ts +24 -4
  209. package/src/adapters/nitro.ts +58 -9
  210. package/src/cache/fast-hash.ts +34 -0
  211. package/src/cache/index.ts +5 -2
  212. package/src/cache/register-cached-function.ts +7 -3
  213. package/src/cache/singleflight.ts +62 -4
  214. package/src/cache/timber-cache.ts +40 -29
  215. package/src/cli.ts +0 -0
  216. package/src/client/browser-entry.ts +133 -93
  217. package/src/client/error-boundary.tsx +18 -1
  218. package/src/client/index.ts +10 -1
  219. package/src/client/link.tsx +78 -19
  220. package/src/client/navigation-context.ts +2 -2
  221. package/src/client/router.ts +105 -60
  222. package/src/client/rsc-fetch.ts +63 -2
  223. package/src/client/segment-cache.ts +1 -1
  224. package/src/client/segment-context.ts +6 -1
  225. package/src/client/segment-merger.ts +2 -8
  226. package/src/client/stale-reload.ts +32 -6
  227. package/src/client/top-loader.tsx +10 -9
  228. package/src/client/transition-root.tsx +7 -1
  229. package/src/client/use-params.ts +3 -3
  230. package/src/client/use-query-states.ts +1 -1
  231. package/src/codec.ts +21 -0
  232. package/src/cookies/define-cookie.ts +69 -18
  233. package/src/fonts/css.ts +2 -1
  234. package/src/index.ts +280 -85
  235. package/src/params/define.ts +260 -0
  236. package/src/params/index.ts +28 -0
  237. package/src/plugins/adapter-build.ts +6 -0
  238. package/src/plugins/build-manifest.ts +11 -0
  239. package/src/plugins/client-chunks.ts +65 -0
  240. package/src/plugins/dev-error-overlay.ts +70 -1
  241. package/src/plugins/dev-server.ts +38 -4
  242. package/src/plugins/entries.ts +5 -7
  243. package/src/plugins/fonts.ts +93 -42
  244. package/src/plugins/routing.ts +40 -14
  245. package/src/plugins/server-bundle.ts +32 -1
  246. package/src/plugins/shims.ts +1 -1
  247. package/src/plugins/static-build.ts +8 -4
  248. package/src/routing/codegen.ts +109 -88
  249. package/src/routing/scanner.ts +55 -6
  250. package/src/routing/status-file-lint.ts +2 -1
  251. package/src/routing/types.ts +7 -4
  252. package/src/rsc-runtime/rsc.ts +2 -0
  253. package/src/rsc-runtime/ssr.ts +50 -0
  254. package/src/rsc-runtime/vendor-types.d.ts +7 -0
  255. package/src/search-params/codecs.ts +1 -1
  256. package/src/search-params/define.ts +518 -0
  257. package/src/search-params/index.ts +12 -18
  258. package/src/search-params/registry.ts +1 -1
  259. package/src/search-params/wrappers.ts +85 -0
  260. package/src/server/access-gate.tsx +40 -9
  261. package/src/server/action-client.ts +7 -1
  262. package/src/server/action-encryption.ts +144 -0
  263. package/src/server/action-handler.ts +19 -2
  264. package/src/server/als-registry.ts +18 -4
  265. package/src/server/build-manifest.ts +16 -5
  266. package/src/server/compress.ts +25 -7
  267. package/src/server/debug.ts +1 -1
  268. package/src/server/default-logger.ts +98 -0
  269. package/src/server/deny-renderer.ts +2 -1
  270. package/src/server/early-hints.ts +36 -15
  271. package/src/server/error-boundary-wrapper.ts +57 -14
  272. package/src/server/flight-injection-state.ts +113 -0
  273. package/src/server/flight-scripts.ts +59 -0
  274. package/src/server/flush.ts +2 -1
  275. package/src/server/form-data.ts +76 -0
  276. package/src/server/html-injectors.ts +261 -117
  277. package/src/server/index.ts +9 -4
  278. package/src/server/logger.ts +38 -35
  279. package/src/server/node-stream-transforms.ts +504 -0
  280. package/src/server/pipeline.ts +131 -39
  281. package/src/server/primitives.ts +47 -5
  282. package/src/server/render-timeout.ts +108 -0
  283. package/src/server/request-context.ts +119 -119
  284. package/src/server/route-element-builder.ts +106 -114
  285. package/src/server/route-handler.ts +2 -1
  286. package/src/server/route-matcher.ts +2 -2
  287. package/src/server/rsc-entry/error-renderer.ts +5 -3
  288. package/src/server/rsc-entry/helpers.ts +122 -3
  289. package/src/server/rsc-entry/index.ts +108 -43
  290. package/src/server/rsc-entry/rsc-payload.ts +52 -12
  291. package/src/server/rsc-entry/rsc-stream.ts +49 -12
  292. package/src/server/rsc-entry/ssr-renderer.ts +40 -13
  293. package/src/server/slot-resolver.ts +222 -217
  294. package/src/server/ssr-entry.ts +209 -30
  295. package/src/server/ssr-render.ts +289 -67
  296. package/src/server/ssr-wrappers.tsx +139 -0
  297. package/src/server/tracing.ts +23 -0
  298. package/src/server/tree-builder.ts +91 -57
  299. package/src/server/types.ts +1 -3
  300. package/src/server/version-skew.ts +104 -0
  301. package/src/server/waituntil-bridge.ts +4 -1
  302. package/src/shared/merge-search-params.ts +48 -0
  303. package/src/shims/navigation-client.ts +1 -1
  304. package/src/shims/navigation.ts +1 -1
  305. package/src/utils/state-machine.ts +111 -0
  306. package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
  307. package/dist/_chunks/debug-gwlJkDuf.js.map +0 -1
  308. package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
  309. package/dist/_chunks/request-context-DIkVh_jG.js.map +0 -1
  310. package/dist/_chunks/ssr-data-MjmprTmO.js +0 -88
  311. package/dist/_chunks/ssr-data-MjmprTmO.js.map +0 -1
  312. package/dist/_chunks/use-cookie-DX-l1_5E.js +0 -91
  313. package/dist/_chunks/use-cookie-DX-l1_5E.js.map +0 -1
  314. package/dist/client/error-boundary.js.map +0 -1
  315. package/dist/cookies/index.js.map +0 -1
  316. package/dist/plugins/dynamic-transform.d.ts +0 -72
  317. package/dist/plugins/dynamic-transform.d.ts.map +0 -1
  318. package/dist/search-params/analyze.d.ts +0 -54
  319. package/dist/search-params/analyze.d.ts.map +0 -1
  320. package/dist/search-params/builtin-codecs.d.ts +0 -105
  321. package/dist/search-params/builtin-codecs.d.ts.map +0 -1
  322. package/dist/search-params/create.d.ts +0 -106
  323. package/dist/search-params/create.d.ts.map +0 -1
  324. package/dist/search-params/index.js.map +0 -1
  325. package/dist/server/prerender.d.ts +0 -77
  326. package/dist/server/prerender.d.ts.map +0 -1
  327. package/dist/server/response-cache.d.ts +0 -53
  328. package/dist/server/response-cache.d.ts.map +0 -1
  329. package/src/plugins/dynamic-transform.ts +0 -161
  330. package/src/search-params/analyze.ts +0 -192
  331. package/src/search-params/builtin-codecs.ts +0 -228
  332. package/src/search-params/create.ts +0 -321
  333. package/src/server/prerender.ts +0 -139
  334. package/src/server/response-cache.ts +0 -277
@@ -33,10 +33,25 @@ import {
33
33
 
34
34
  const VIRTUAL_GOOGLE = '@timber/fonts/google';
35
35
  const VIRTUAL_LOCAL = '@timber/fonts/local';
36
- const VIRTUAL_FONT_CSS = 'virtual:timber-fonts-css';
37
36
  const RESOLVED_GOOGLE = '\0@timber/fonts/google';
38
37
  const RESOLVED_LOCAL = '\0@timber/fonts/local';
39
- const RESOLVED_FONT_CSS = '\0virtual:timber-fonts-css';
38
+
39
+ /**
40
+ * Virtual side-effect module that registers font CSS on globalThis.
41
+ *
42
+ * When a file calls localFont() or a Google font function, the transform
43
+ * hook injects `import 'virtual:timber-font-css-register'` into that file.
44
+ * This virtual module sets `globalThis.__timber_font_css` with the combined
45
+ * @font-face CSS. The RSC entry reads it at render time to inline a <style> tag.
46
+ *
47
+ * This approach avoids timing issues because:
48
+ * 1. The font file is in the RSC module graph (imported by layout.tsx)
49
+ * 2. The side-effect import is added to the font file during transform
50
+ * 3. When layout.tsx is loaded, fonts.ts runs → side-effect module runs → globalThis is set
51
+ * 4. RSC entry renders → reads globalThis → inlines <style>
52
+ */
53
+ const VIRTUAL_FONT_CSS_REGISTER = 'virtual:timber-font-css-register';
54
+ const RESOLVED_FONT_CSS_REGISTER = '\0virtual:timber-font-css-register';
40
55
 
41
56
  /**
42
57
  * Registry of fonts extracted during transform.
@@ -246,6 +261,33 @@ function generateLocalVirtualModule(): string {
246
261
  ].join('\n');
247
262
  }
248
263
 
264
+ /**
265
+ * Generate CSS for a single extracted font.
266
+ *
267
+ * Includes @font-face rules (for local fonts), fallback @font-face,
268
+ * and the scoped class rule.
269
+ */
270
+ export function generateFontCss(font: ExtractedFont): string {
271
+ const cssParts: string[] = [];
272
+
273
+ if (font.provider === 'local' && font.localSources) {
274
+ const faces = generateLocalFontFaces(font.family, font.localSources, font.display);
275
+ const faceCss = generateFontFaces(faces);
276
+ if (faceCss) cssParts.push(faceCss);
277
+ }
278
+
279
+ const fallbackCss = generateFallbackCss(font.family);
280
+ if (fallbackCss) cssParts.push(fallbackCss);
281
+
282
+ if (font.variable) {
283
+ cssParts.push(generateVariableClass(font.className, font.variable, font.fontFamily));
284
+ } else {
285
+ cssParts.push(generateFontFamilyClass(font.className, font.fontFamily));
286
+ }
287
+
288
+ return cssParts.join('\n\n');
289
+ }
290
+
249
291
  /**
250
292
  * Generate the CSS output for all extracted fonts.
251
293
  *
@@ -254,27 +296,9 @@ function generateLocalVirtualModule(): string {
254
296
  */
255
297
  export function generateAllFontCss(registry: FontRegistry): string {
256
298
  const cssParts: string[] = [];
257
-
258
299
  for (const font of registry.values()) {
259
- // Generate @font-face rules for local fonts
260
- if (font.provider === 'local' && font.localSources) {
261
- const faces = generateLocalFontFaces(font.family, font.localSources, font.display);
262
- const faceCss = generateFontFaces(faces);
263
- if (faceCss) cssParts.push(faceCss);
264
- }
265
-
266
- // Generate fallback @font-face if metrics are available
267
- const fallbackCss = generateFallbackCss(font.family);
268
- if (fallbackCss) cssParts.push(fallbackCss);
269
-
270
- // Generate scoped class
271
- if (font.variable) {
272
- cssParts.push(generateVariableClass(font.className, font.variable, font.fontFamily));
273
- } else {
274
- cssParts.push(generateFontFamilyClass(font.className, font.fontFamily));
275
- }
300
+ cssParts.push(generateFontCss(font));
276
301
  }
277
-
278
302
  return cssParts.join('\n\n');
279
303
  }
280
304
 
@@ -371,34 +395,61 @@ export function timberFonts(ctx: PluginContext): Plugin {
371
395
  name: 'timber-fonts',
372
396
 
373
397
  /**
374
- * Resolve `@timber/fonts/google`, `@timber/fonts/local`, and the
375
- * virtual font CSS module to internal IDs.
398
+ * Resolve `@timber/fonts/google`, `@timber/fonts/local`,
399
+ * and `virtual:timber-font-css` virtual modules.
400
+ *
401
+ * Handles \0 prefix and root prefix stripping for RSC/SSR
402
+ * environments where the RSC plugin re-imports virtual modules
403
+ * with additional prefixes.
376
404
  */
377
405
  resolveId(id: string) {
378
- if (id === VIRTUAL_GOOGLE) return RESOLVED_GOOGLE;
379
- if (id === VIRTUAL_LOCAL) return RESOLVED_LOCAL;
380
- if (id === VIRTUAL_FONT_CSS) return RESOLVED_FONT_CSS;
406
+ // Strip \0 prefix (RSC plugin re-imports)
407
+ let cleanId = id.startsWith('\0') ? id.slice(1) : id;
408
+ // Strip root prefix (SSR build entries)
409
+ if (cleanId.startsWith(ctx.root)) {
410
+ const stripped = cleanId.slice(ctx.root.length);
411
+ if (stripped.startsWith('/') || stripped.startsWith('\\')) {
412
+ cleanId = stripped.slice(1);
413
+ } else {
414
+ cleanId = stripped;
415
+ }
416
+ }
417
+
418
+ if (cleanId === VIRTUAL_GOOGLE) return RESOLVED_GOOGLE;
419
+ if (cleanId === VIRTUAL_LOCAL) return RESOLVED_LOCAL;
420
+ if (cleanId === VIRTUAL_FONT_CSS_REGISTER) return RESOLVED_FONT_CSS_REGISTER;
381
421
  return null;
382
422
  },
383
423
 
384
424
  /**
385
425
  * Return generated source for font virtual modules.
386
426
  *
387
- * The font CSS virtual module returns the combined @font-face rules,
388
- * fallback CSS, and scoped classes for all registered fonts.
427
+ * `virtual:timber-font-css` exports the combined @font-face CSS
428
+ * as a string. The RSC entry imports it and inlines a <style> tag.
429
+ * Because this is loaded lazily (on first request), the font
430
+ * registry is always populated by the time it's needed.
389
431
  */
390
432
  load(id: string) {
391
433
  if (id === RESOLVED_GOOGLE) return generateGoogleVirtualModule(registry);
392
434
  if (id === RESOLVED_LOCAL) return generateLocalVirtualModule();
393
- if (id === RESOLVED_FONT_CSS) return generateAllFontCss(registry);
435
+
436
+ if (id === RESOLVED_FONT_CSS_REGISTER) {
437
+ const css = generateAllFontCss(registry);
438
+ // Side-effect module: sets font CSS on globalThis for the RSC entry to read.
439
+ return `globalThis.__timber_font_css = ${JSON.stringify(css)};`;
440
+ }
394
441
  return null;
395
442
  },
396
443
 
397
444
  /**
398
- * Serve local font files in dev mode under `/_timber/fonts/`.
445
+ * Serve local font files and font CSS in dev mode under `/_timber/fonts/`.
446
+ *
447
+ * Serves:
448
+ * - `/_timber/fonts/fonts.css` — combined @font-face + scoped class CSS
449
+ * - `/_timber/fonts/<filename>` — individual font files from the registry
399
450
  *
400
- * Only files that are registered in the font registry (via localSources)
401
- * are served. Paths are validated to prevent directory traversal.
451
+ * Only files registered in the font registry are served.
452
+ * Paths are validated to prevent directory traversal.
402
453
  */
403
454
  configureServer(server: ViteDevServer) {
404
455
  server.middlewares.use((req, res, next) => {
@@ -413,6 +464,9 @@ export function timberFonts(ctx: PluginContext): Plugin {
413
464
  return;
414
465
  }
415
466
 
467
+ // Font CSS is now injected via Vite's CSS pipeline (virtual:timber-font-css modules).
468
+ // This middleware only serves font binary files (woff2, etc.).
469
+
416
470
  // Find the matching font file in the registry
417
471
  for (const font of registry.values()) {
418
472
  if (font.provider !== 'local' || !font.localSources) continue;
@@ -420,7 +474,6 @@ export function timberFonts(ctx: PluginContext): Plugin {
420
474
  const basename = src.path.split('/').pop() ?? '';
421
475
  if (basename === requestedFilename) {
422
476
  const absolutePath = normalize(resolve(src.path));
423
- // Verify the resolved path hasn't escaped (extra safety)
424
477
  if (!existsSync(absolutePath)) {
425
478
  res.statusCode = 404;
426
479
  res.end('Not found');
@@ -571,6 +624,11 @@ export function timberFonts(ctx: PluginContext): Plugin {
571
624
  }
572
625
 
573
626
  if (transformedCode !== code) {
627
+ // Inject side-effect import that registers font CSS on globalThis.
628
+ // The RSC entry reads globalThis.__timber_font_css to inline a <style> tag.
629
+ if (registry.size > 0) {
630
+ transformedCode = `import '${VIRTUAL_FONT_CSS_REGISTER}';\n` + transformedCode;
631
+ }
574
632
  return { code: transformedCode, map: null };
575
633
  }
576
634
 
@@ -616,15 +674,8 @@ export function timberFonts(ctx: PluginContext): Plugin {
616
674
  }
617
675
  }
618
676
 
619
- // Emit the combined font CSS as an asset
620
- const fontCss = generateAllFontCss(registry);
621
- if (fontCss) {
622
- this.emitFile({
623
- type: 'asset',
624
- fileName: '_timber/fonts/fonts.css',
625
- source: fontCss,
626
- });
627
- }
677
+ // Font CSS is emitted by Vite's CSS pipeline via virtual:timber-font-css modules.
678
+ // We only need to emit font binary files and update the build manifest here.
628
679
 
629
680
  if (!ctx.buildManifest) return;
630
681
 
@@ -28,7 +28,7 @@ const RESOLVED_VIRTUAL_ID = `\0${VIRTUAL_MODULE_ID}`;
28
28
  * File convention names we track for changes that require manifest regeneration.
29
29
  */
30
30
  const ROUTE_FILE_PATTERNS =
31
- /\/(page|layout|middleware|access|route|error|default|denied|search-params|\d{3}|[45]xx|not-found|forbidden|unauthorized|sitemap|robots|manifest|favicon|icon|opengraph-image|twitter-image|apple-icon)\./;
31
+ /\/(page|layout|middleware|access|route|error|default|denied|\d{3}|[45]xx|not-found|forbidden|unauthorized|sitemap|robots|manifest|favicon|icon|opengraph-image|twitter-image|apple-icon)\./;
32
32
 
33
33
  /**
34
34
  * Create the timber-routing Vite plugin.
@@ -168,20 +168,49 @@ export function timberRouting(ctx: PluginContext): Plugin {
168
168
  // Watch the app directory
169
169
  devServer.watcher.add(ctx.appDir);
170
170
 
171
- const handleFileChange = (filePath: string) => {
172
- // Only react to route-significant files in the app directory
171
+ /** Snapshot of the last generated manifest, used to detect structural changes. */
172
+ let lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : '';
173
+
174
+ /**
175
+ * Handle a route-significant file being added or removed.
176
+ * Always triggers a full-reload since the route tree structure changed.
177
+ */
178
+ const handleStructuralChange = (filePath: string) => {
173
179
  if (!filePath.startsWith(ctx.appDir)) return;
174
180
  if (!ROUTE_FILE_PATTERNS.test(filePath)) return;
175
181
 
176
- // Rescan the route tree
177
182
  rescan();
178
-
179
- // Invalidate the virtual module in all environments
183
+ lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : '';
180
184
  invalidateManifest(devServer);
181
185
  };
182
186
 
183
- devServer.watcher.on('add', handleFileChange);
184
- devServer.watcher.on('unlink', handleFileChange);
187
+ /**
188
+ * Handle a route file's content changing.
189
+ *
190
+ * Most content edits (JSX changes, fixing typos) don't affect route
191
+ * metadata — Vite's React Fast Refresh handles those via normal HMR.
192
+ * Only rescan and full-reload when route metadata actually changed
193
+ * (e.g., searchParams export added/removed, metadata export changed).
194
+ */
195
+ const handleContentChange = (filePath: string) => {
196
+ if (!filePath.startsWith(ctx.appDir)) return;
197
+ if (!ROUTE_FILE_PATTERNS.test(filePath)) return;
198
+
199
+ rescan();
200
+ const newManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : '';
201
+ if (newManifest !== lastManifest) {
202
+ lastManifest = newManifest;
203
+ invalidateManifest(devServer);
204
+ }
205
+ // Otherwise: content edit didn't change route metadata — let Vite HMR handle it
206
+ };
207
+
208
+ devServer.watcher.on('add', handleStructuralChange);
209
+ devServer.watcher.on('unlink', handleStructuralChange);
210
+ // Watch content changes to page files — searchParams detection depends
211
+ // on file contents (export const searchParams), not just file presence.
212
+ // But only full-reload when route metadata actually changes.
213
+ devServer.watcher.on('change', handleContentChange);
185
214
  // Also watch renames (which are add+unlink) — handled by the above
186
215
  },
187
216
  };
@@ -301,12 +330,9 @@ function generateManifestModule(tree: RouteTree): string {
301
330
  `${nextIndent}denied: { load: ${v}, filePath: ${JSON.stringify(node.denied.filePath)} },`
302
331
  );
303
332
  }
304
- if (node.searchParams) {
305
- const v = addImport(node.searchParams);
306
- parts.push(
307
- `${nextIndent}searchParams: { load: ${v}, filePath: ${JSON.stringify(node.searchParams.filePath)} },`
308
- );
309
- }
333
+ // searchParams is now a named export from page.tsx, not a separate file.
334
+ // The page module's searchParams export is loaded via the page's lazy import.
335
+ // Runtime registration happens in the route loader using the page module.
310
336
 
311
337
  // Status-code files
312
338
  if (node.statusFiles && node.statusFiles.size > 0) {
@@ -137,5 +137,36 @@ export function timberServerBundle(): Plugin[] {
137
137
  },
138
138
  };
139
139
 
140
- return [bundlePlugin, esmInitFixPlugin];
140
+ // Fix Rolldown's `createRequire(import.meta.url)` CJS interop shim for
141
+ // Cloudflare Workers. Rolldown emits this for CJS dependencies (e.g.
142
+ // @opentelemetry/context-async-hooks) that use `require()`. On Workers,
143
+ // `import.meta.url` is `undefined` for non-entry modules, causing:
144
+ // TypeError: The argument 'path' must be a file URL object, a file URL
145
+ // string, or an absolute path string. Received 'undefined'
146
+ //
147
+ // The fix: provide a fallback URL when `import.meta.url` is undefined.
148
+ // The actual URL doesn't matter — `createRequire` only needs it for
149
+ // resolving relative paths, but the only `__require()` calls are for
150
+ // Node built-ins (events, async_hooks) which resolve from any base.
151
+ //
152
+ // The top-level `ssr: { target: 'webworker' }` was supposed to prevent
153
+ // this, but it doesn't propagate to custom environments (rsc) in Vite's
154
+ // Environment API. See LOCAL-405.
155
+ const createRequireFixPlugin: Plugin = {
156
+ name: 'timber-create-require-fix',
157
+ applyToEnvironment(environment) {
158
+ return environment.name === 'rsc' || environment.name === 'ssr';
159
+ },
160
+ renderChunk(code) {
161
+ const pattern = 'createRequire(import.meta.url)';
162
+ if (!code.includes(pattern)) return null;
163
+
164
+ return {
165
+ code: code.replace(pattern, 'createRequire(import.meta.url || "file:///app")'),
166
+ map: null,
167
+ };
168
+ },
169
+ };
170
+
171
+ return [bundlePlugin, esmInitFixPlugin, createRequireFixPlugin];
141
172
  }
@@ -137,7 +137,7 @@ export function timberShims(_ctx: PluginContext): Plugin {
137
137
  // package.json exports), creating a module instance split: ssr-entry.ts
138
138
  // registers the ALS-backed SSR data provider on the src/ instance of
139
139
  // ssr-data.ts, but client component hooks read getSsrData() from the
140
- // dist/ instance — which has no provider. Result: hooks like useParams()
140
+ // dist/ instance — which has no provider. Result: hooks like useSegmentParams()
141
141
  // return empty defaults during SSR.
142
142
  //
143
143
  // This remap is SSR-only. The RSC environment still resolves to dist/
@@ -163,9 +163,6 @@ export function validateStaticMode(
163
163
  * - transform: Validates source files for static mode violations
164
164
  */
165
165
  export function timberStaticBuild(ctx: PluginContext): Plugin {
166
- const isStatic = ctx.config.output === 'static';
167
- const clientJavascriptDisabled = ctx.clientJavascript.disabled;
168
-
169
166
  return {
170
167
  name: 'timber-static-build',
171
168
 
@@ -177,6 +174,11 @@ export function timberStaticBuild(ctx: PluginContext): Plugin {
177
174
  * - When client JS disabled: 'use client' / 'use server' directives → build error
178
175
  */
179
176
  transform(code: string, id: string) {
177
+ // Read ctx.config lazily inside the hook — not at plugin construction
178
+ // time — so file-based config from timber.config.ts is respected.
179
+ // See TIM-451.
180
+ const isStatic = ctx.config.output === 'static';
181
+
180
182
  // Only active in static mode
181
183
  if (!isStatic) return null;
182
184
 
@@ -189,7 +191,9 @@ export function timberStaticBuild(ctx: PluginContext): Plugin {
189
191
  // Only check JS/TS files
190
192
  if (!/\.[jt]sx?$/.test(id)) return null;
191
193
 
192
- const errors = validateStaticMode(code, id, { clientJavascriptDisabled });
194
+ const errors = validateStaticMode(code, id, {
195
+ clientJavascriptDisabled: ctx.clientJavascript.disabled,
196
+ });
193
197
 
194
198
  if (errors.length > 0) {
195
199
  // Format all errors into a single build error message