@timber-js/app 0.2.0-alpha.10 → 0.2.0-alpha.100

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 (722) hide show
  1. package/LICENSE +8 -0
  2. package/dist/_chunks/actions-CQ8Z8VGL.js +1061 -0
  3. package/dist/_chunks/actions-CQ8Z8VGL.js.map +1 -0
  4. package/dist/_chunks/build-output-helper-DXnW0qjz.js +61 -0
  5. package/dist/_chunks/build-output-helper-DXnW0qjz.js.map +1 -0
  6. package/dist/_chunks/chunk-BYIpzuS7.js +39 -0
  7. package/dist/_chunks/define-B-Q_UMOD.js +195 -0
  8. package/dist/_chunks/define-B-Q_UMOD.js.map +1 -0
  9. package/dist/_chunks/define-CfBPoJb0.js +123 -0
  10. package/dist/_chunks/define-CfBPoJb0.js.map +1 -0
  11. package/dist/_chunks/define-cookie-BjpIt4UC.js +194 -0
  12. package/dist/_chunks/define-cookie-BjpIt4UC.js.map +1 -0
  13. package/dist/_chunks/format-Bcn-Iv1x.js +14 -0
  14. package/dist/_chunks/format-Bcn-Iv1x.js.map +1 -0
  15. package/dist/_chunks/handler-store-B-lqaGyh.js +54 -0
  16. package/dist/_chunks/handler-store-B-lqaGyh.js.map +1 -0
  17. package/dist/_chunks/logger-0m8MsKdc.js +291 -0
  18. package/dist/_chunks/logger-0m8MsKdc.js.map +1 -0
  19. package/dist/_chunks/merge-search-params-BphMdht_.js +122 -0
  20. package/dist/_chunks/merge-search-params-BphMdht_.js.map +1 -0
  21. package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-BU684ls2.js} +1 -1
  22. package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js.map → metadata-routes-BU684ls2.js.map} +1 -1
  23. package/dist/_chunks/navigation-root-BCYczjml.js +96 -0
  24. package/dist/_chunks/navigation-root-BCYczjml.js.map +1 -0
  25. package/dist/_chunks/registry-I2ss-lvy.js +20 -0
  26. package/dist/_chunks/registry-I2ss-lvy.js.map +1 -0
  27. package/dist/_chunks/router-ref-h3-UaCQv.js +28 -0
  28. package/dist/_chunks/router-ref-h3-UaCQv.js.map +1 -0
  29. package/dist/_chunks/schema-bridge-Cxu4l-7p.js +86 -0
  30. package/dist/_chunks/schema-bridge-Cxu4l-7p.js.map +1 -0
  31. package/dist/_chunks/segment-classify-BjfuctV2.js +137 -0
  32. package/dist/_chunks/segment-classify-BjfuctV2.js.map +1 -0
  33. package/dist/_chunks/segment-context-Dx_OizxD.js +34 -0
  34. package/dist/_chunks/segment-context-Dx_OizxD.js.map +1 -0
  35. package/dist/_chunks/{ssr-data-MjmprTmO.js → ssr-data-B4CdH7rE.js} +1 -1
  36. package/dist/_chunks/ssr-data-B4CdH7rE.js.map +1 -0
  37. package/dist/_chunks/stale-reload-Bab885FO.js +64 -0
  38. package/dist/_chunks/stale-reload-Bab885FO.js.map +1 -0
  39. package/dist/_chunks/tracing-C8V-YGsP.js +329 -0
  40. package/dist/_chunks/tracing-C8V-YGsP.js.map +1 -0
  41. package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-B2XTqxDR.js} +7 -20
  42. package/dist/_chunks/use-query-states-B2XTqxDR.js.map +1 -0
  43. package/dist/_chunks/use-segment-params-BkpKAQ7D.js +185 -0
  44. package/dist/_chunks/use-segment-params-BkpKAQ7D.js.map +1 -0
  45. package/dist/_chunks/walkers-Tg0Alwcg.js +850 -0
  46. package/dist/_chunks/walkers-Tg0Alwcg.js.map +1 -0
  47. package/dist/_chunks/{format-DviM89f0.js → warnings-Cg47l5sk.js} +6 -45
  48. package/dist/_chunks/warnings-Cg47l5sk.js.map +1 -0
  49. package/dist/adapters/build-output-helper.d.ts +28 -0
  50. package/dist/adapters/build-output-helper.d.ts.map +1 -0
  51. package/dist/adapters/cloudflare-dev.d.ts +109 -0
  52. package/dist/adapters/cloudflare-dev.d.ts.map +1 -0
  53. package/dist/adapters/cloudflare-dev.js +73 -0
  54. package/dist/adapters/cloudflare-dev.js.map +1 -0
  55. package/dist/adapters/cloudflare-kv-cache.d.ts +64 -0
  56. package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -0
  57. package/dist/adapters/cloudflare-kv-cache.js +95 -0
  58. package/dist/adapters/cloudflare-kv-cache.js.map +1 -0
  59. package/dist/adapters/cloudflare.d.ts +148 -12
  60. package/dist/adapters/cloudflare.d.ts.map +1 -1
  61. package/dist/adapters/cloudflare.js +142 -38
  62. package/dist/adapters/cloudflare.js.map +1 -1
  63. package/dist/adapters/compress-module.d.ts.map +1 -1
  64. package/dist/adapters/nitro.d.ts +17 -1
  65. package/dist/adapters/nitro.d.ts.map +1 -1
  66. package/dist/adapters/nitro.js +143 -45
  67. package/dist/adapters/nitro.js.map +1 -1
  68. package/dist/adapters/shared.d.ts +16 -0
  69. package/dist/adapters/shared.d.ts.map +1 -0
  70. package/dist/cache/cache-api.d.ts +24 -0
  71. package/dist/cache/cache-api.d.ts.map +1 -0
  72. package/dist/cache/handler-store.d.ts +31 -0
  73. package/dist/cache/handler-store.d.ts.map +1 -0
  74. package/dist/cache/index.d.ts +23 -7
  75. package/dist/cache/index.d.ts.map +1 -1
  76. package/dist/cache/index.js +150 -81
  77. package/dist/cache/index.js.map +1 -1
  78. package/dist/cache/singleflight.d.ts +18 -1
  79. package/dist/cache/singleflight.d.ts.map +1 -1
  80. package/dist/cache/sizeof.d.ts +22 -0
  81. package/dist/cache/sizeof.d.ts.map +1 -0
  82. package/dist/cache/timber-cache.d.ts +1 -1
  83. package/dist/cache/timber-cache.d.ts.map +1 -1
  84. package/dist/cli.d.ts +6 -1
  85. package/dist/cli.d.ts.map +1 -1
  86. package/dist/cli.js +8 -3
  87. package/dist/cli.js.map +1 -1
  88. package/dist/client/browser-dev.d.ts +34 -1
  89. package/dist/client/browser-dev.d.ts.map +1 -1
  90. package/dist/client/browser-entry/action-dispatch.d.ts +17 -0
  91. package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -0
  92. package/dist/client/browser-entry/hmr.d.ts +21 -0
  93. package/dist/client/browser-entry/hmr.d.ts.map +1 -0
  94. package/dist/client/browser-entry/hydrate.d.ts +46 -0
  95. package/dist/client/browser-entry/hydrate.d.ts.map +1 -0
  96. package/dist/client/browser-entry/index.d.ts +30 -0
  97. package/dist/client/browser-entry/index.d.ts.map +1 -0
  98. package/dist/client/browser-entry/post-hydration.d.ts +26 -0
  99. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -0
  100. package/dist/client/browser-entry/router-init.d.ts +23 -0
  101. package/dist/client/browser-entry/router-init.d.ts.map +1 -0
  102. package/dist/client/browser-entry/rsc-stream.d.ts +24 -0
  103. package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -0
  104. package/dist/client/browser-entry/scroll.d.ts +19 -0
  105. package/dist/client/browser-entry/scroll.d.ts.map +1 -0
  106. package/dist/client/error-boundary.d.ts +12 -5
  107. package/dist/client/error-boundary.d.ts.map +1 -1
  108. package/dist/client/error-boundary.js +26 -5
  109. package/dist/client/error-boundary.js.map +1 -1
  110. package/dist/client/error-reconstituter.d.ts +54 -0
  111. package/dist/client/error-reconstituter.d.ts.map +1 -0
  112. package/dist/client/form.d.ts +16 -27
  113. package/dist/client/form.d.ts.map +1 -1
  114. package/dist/client/history.d.ts +19 -4
  115. package/dist/client/history.d.ts.map +1 -1
  116. package/dist/client/index.d.ts +9 -24
  117. package/dist/client/index.d.ts.map +1 -1
  118. package/dist/client/index.js +261 -1100
  119. package/dist/client/index.js.map +1 -1
  120. package/dist/client/internal.d.ts +19 -0
  121. package/dist/client/internal.d.ts.map +1 -0
  122. package/dist/client/internal.js +964 -0
  123. package/dist/client/internal.js.map +1 -0
  124. package/dist/client/link-pending-store.d.ts +63 -0
  125. package/dist/client/link-pending-store.d.ts.map +1 -0
  126. package/dist/client/link.d.ts +62 -55
  127. package/dist/client/link.d.ts.map +1 -1
  128. package/dist/client/nav-link-store.d.ts +36 -0
  129. package/dist/client/nav-link-store.d.ts.map +1 -0
  130. package/dist/client/navigation-api-types.d.ts +90 -0
  131. package/dist/client/navigation-api-types.d.ts.map +1 -0
  132. package/dist/client/navigation-api.d.ts +115 -0
  133. package/dist/client/navigation-api.d.ts.map +1 -0
  134. package/dist/client/navigation-context.d.ts +13 -2
  135. package/dist/client/navigation-context.d.ts.map +1 -1
  136. package/dist/client/{transition-root.d.ts → navigation-root.d.ts} +42 -8
  137. package/dist/client/navigation-root.d.ts.map +1 -0
  138. package/dist/client/nuqs-adapter.d.ts.map +1 -1
  139. package/dist/client/router-ref.d.ts +1 -1
  140. package/dist/client/router.d.ts +70 -4
  141. package/dist/client/router.d.ts.map +1 -1
  142. package/dist/client/rsc-fetch.d.ts +38 -3
  143. package/dist/client/rsc-fetch.d.ts.map +1 -1
  144. package/dist/client/segment-cache.d.ts +1 -1
  145. package/dist/client/segment-cache.d.ts.map +1 -1
  146. package/dist/client/segment-outlet.d.ts +63 -0
  147. package/dist/client/segment-outlet.d.ts.map +1 -0
  148. package/dist/client/ssr-data.d.ts +13 -4
  149. package/dist/client/ssr-data.d.ts.map +1 -1
  150. package/dist/client/stale-reload.d.ts +15 -0
  151. package/dist/client/stale-reload.d.ts.map +1 -1
  152. package/dist/client/state.d.ts +1 -1
  153. package/dist/client/top-loader.d.ts +5 -5
  154. package/dist/client/top-loader.d.ts.map +1 -1
  155. package/dist/client/use-cookie.d.ts +8 -0
  156. package/dist/client/use-cookie.d.ts.map +1 -1
  157. package/dist/client/use-link-status.d.ts +5 -5
  158. package/dist/client/use-link-status.d.ts.map +1 -1
  159. package/dist/client/{use-navigation-pending.d.ts → use-pending-navigation.d.ts} +4 -4
  160. package/dist/client/use-pending-navigation.d.ts.map +1 -0
  161. package/dist/client/use-query-states.d.ts +1 -1
  162. package/dist/client/use-query-states.d.ts.map +1 -1
  163. package/dist/client/use-router.d.ts +1 -1
  164. package/dist/client/{use-params.d.ts → use-segment-params.d.ts} +7 -5
  165. package/dist/client/use-segment-params.d.ts.map +1 -0
  166. package/dist/codec.d.ts +33 -0
  167. package/dist/codec.d.ts.map +1 -0
  168. package/dist/codec.js +2 -0
  169. package/dist/config-types.d.ts +294 -0
  170. package/dist/config-types.d.ts.map +1 -0
  171. package/dist/config-validation.d.ts +51 -0
  172. package/dist/config-validation.d.ts.map +1 -0
  173. package/dist/content/index.d.ts +1 -10
  174. package/dist/content/index.d.ts.map +1 -1
  175. package/dist/content/index.js +0 -2
  176. package/dist/cookies/define-cookie.d.ts +98 -25
  177. package/dist/cookies/define-cookie.d.ts.map +1 -1
  178. package/dist/cookies/index.d.ts +2 -1
  179. package/dist/cookies/index.d.ts.map +1 -1
  180. package/dist/cookies/index.js +36 -72
  181. package/dist/cookies/index.js.map +1 -1
  182. package/dist/cookies/json-cookie.d.ts +64 -0
  183. package/dist/cookies/json-cookie.d.ts.map +1 -0
  184. package/dist/cookies/validation.d.ts +46 -0
  185. package/dist/cookies/validation.d.ts.map +1 -0
  186. package/dist/dev-tools/404-page.d.ts +46 -0
  187. package/dist/dev-tools/404-page.d.ts.map +1 -0
  188. package/dist/dev-tools/browser-logs.d.ts +84 -0
  189. package/dist/dev-tools/browser-logs.d.ts.map +1 -0
  190. package/dist/dev-tools/error-page.d.ts +58 -0
  191. package/dist/dev-tools/error-page.d.ts.map +1 -0
  192. package/dist/dev-tools/holding-server.d.ts +54 -0
  193. package/dist/dev-tools/holding-server.d.ts.map +1 -0
  194. package/dist/dev-tools/index.d.ts +31 -0
  195. package/dist/dev-tools/index.d.ts.map +1 -0
  196. package/dist/{server/dev-span-processor.d.ts → dev-tools/instrumentation.d.ts} +26 -6
  197. package/dist/dev-tools/instrumentation.d.ts.map +1 -0
  198. package/dist/{server/dev-logger.d.ts → dev-tools/logger.d.ts} +1 -1
  199. package/dist/dev-tools/logger.d.ts.map +1 -0
  200. package/dist/{plugins/dev-logs.d.ts → dev-tools/logs.d.ts} +2 -2
  201. package/dist/dev-tools/logs.d.ts.map +1 -0
  202. package/dist/dev-tools/overlay.d.ts +91 -0
  203. package/dist/dev-tools/overlay.d.ts.map +1 -0
  204. package/dist/dev-tools/stack-classifier.d.ts +34 -0
  205. package/dist/dev-tools/stack-classifier.d.ts.map +1 -0
  206. package/dist/dev-tools/terminal.d.ts +28 -0
  207. package/dist/dev-tools/terminal.d.ts.map +1 -0
  208. package/dist/{server/dev-warnings.d.ts → dev-tools/warnings.d.ts} +2 -22
  209. package/dist/dev-tools/warnings.d.ts.map +1 -0
  210. package/dist/fonts/bundle.d.ts +48 -0
  211. package/dist/fonts/bundle.d.ts.map +1 -0
  212. package/dist/fonts/css.d.ts +1 -0
  213. package/dist/fonts/css.d.ts.map +1 -1
  214. package/dist/fonts/dev-middleware.d.ts +22 -0
  215. package/dist/fonts/dev-middleware.d.ts.map +1 -0
  216. package/dist/fonts/pipeline.d.ts +167 -0
  217. package/dist/fonts/pipeline.d.ts.map +1 -0
  218. package/dist/fonts/transform.d.ts +64 -0
  219. package/dist/fonts/transform.d.ts.map +1 -0
  220. package/dist/fonts/types.d.ts +45 -1
  221. package/dist/fonts/types.d.ts.map +1 -1
  222. package/dist/fonts/virtual-modules.d.ts +103 -0
  223. package/dist/fonts/virtual-modules.d.ts.map +1 -0
  224. package/dist/index.d.ts +46 -190
  225. package/dist/index.d.ts.map +1 -1
  226. package/dist/index.js +14569 -12451
  227. package/dist/index.js.map +1 -1
  228. package/dist/plugin-context.d.ts +107 -0
  229. package/dist/plugin-context.d.ts.map +1 -0
  230. package/dist/plugins/adapter-build.d.ts +1 -1
  231. package/dist/plugins/adapter-build.d.ts.map +1 -1
  232. package/dist/plugins/build-manifest.d.ts +2 -2
  233. package/dist/plugins/build-manifest.d.ts.map +1 -1
  234. package/dist/plugins/build-report.d.ts +9 -7
  235. package/dist/plugins/build-report.d.ts.map +1 -1
  236. package/dist/plugins/client-chunks.d.ts +32 -0
  237. package/dist/plugins/client-chunks.d.ts.map +1 -0
  238. package/dist/plugins/content.d.ts +1 -1
  239. package/dist/plugins/content.d.ts.map +1 -1
  240. package/dist/plugins/dev-server.d.ts +1 -1
  241. package/dist/plugins/dev-server.d.ts.map +1 -1
  242. package/dist/plugins/entries.d.ts +1 -1
  243. package/dist/plugins/entries.d.ts.map +1 -1
  244. package/dist/plugins/fonts.d.ts +17 -73
  245. package/dist/plugins/fonts.d.ts.map +1 -1
  246. package/dist/plugins/mdx.d.ts +1 -1
  247. package/dist/plugins/mdx.d.ts.map +1 -1
  248. package/dist/plugins/routing.d.ts +1 -1
  249. package/dist/plugins/routing.d.ts.map +1 -1
  250. package/dist/plugins/server-bundle.d.ts.map +1 -1
  251. package/dist/plugins/shims.d.ts +6 -5
  252. package/dist/plugins/shims.d.ts.map +1 -1
  253. package/dist/plugins/static-build.d.ts +4 -4
  254. package/dist/plugins/static-build.d.ts.map +1 -1
  255. package/dist/routing/codegen-shared.d.ts +55 -0
  256. package/dist/routing/codegen-shared.d.ts.map +1 -0
  257. package/dist/routing/codegen-types.d.ts +52 -0
  258. package/dist/routing/codegen-types.d.ts.map +1 -0
  259. package/dist/routing/codegen.d.ts +2 -2
  260. package/dist/routing/codegen.d.ts.map +1 -1
  261. package/dist/routing/convention-lint.d.ts +41 -0
  262. package/dist/routing/convention-lint.d.ts.map +1 -0
  263. package/dist/routing/index.d.ts +5 -1
  264. package/dist/routing/index.d.ts.map +1 -1
  265. package/dist/routing/index.js +3 -2
  266. package/dist/routing/link-codegen.d.ts +90 -0
  267. package/dist/routing/link-codegen.d.ts.map +1 -0
  268. package/dist/routing/scanner.d.ts +1 -10
  269. package/dist/routing/scanner.d.ts.map +1 -1
  270. package/dist/routing/segment-classify.d.ts +75 -0
  271. package/dist/routing/segment-classify.d.ts.map +1 -0
  272. package/dist/routing/status-file-lint.d.ts +2 -1
  273. package/dist/routing/status-file-lint.d.ts.map +1 -1
  274. package/dist/routing/types.d.ts +77 -25
  275. package/dist/routing/types.d.ts.map +1 -1
  276. package/dist/routing/walkers.d.ts +51 -0
  277. package/dist/routing/walkers.d.ts.map +1 -0
  278. package/dist/rsc-runtime/rsc.d.ts +1 -1
  279. package/dist/rsc-runtime/rsc.d.ts.map +1 -1
  280. package/dist/rsc-runtime/ssr.d.ts +12 -0
  281. package/dist/rsc-runtime/ssr.d.ts.map +1 -1
  282. package/dist/schema-bridge.d.ts +76 -0
  283. package/dist/schema-bridge.d.ts.map +1 -0
  284. package/dist/search-params/define.d.ts +157 -0
  285. package/dist/search-params/define.d.ts.map +1 -0
  286. package/dist/search-params/index.d.ts +4 -7
  287. package/dist/search-params/index.d.ts.map +1 -1
  288. package/dist/search-params/index.js +34 -441
  289. package/dist/search-params/index.js.map +1 -1
  290. package/dist/search-params/registry.d.ts +2 -2
  291. package/dist/search-params/registry.d.ts.map +1 -1
  292. package/dist/search-params/wrappers.d.ts +53 -0
  293. package/dist/search-params/wrappers.d.ts.map +1 -0
  294. package/dist/segment-params/define.d.ts +95 -0
  295. package/dist/segment-params/define.d.ts.map +1 -0
  296. package/dist/segment-params/index.d.ts +3 -0
  297. package/dist/segment-params/index.d.ts.map +1 -0
  298. package/dist/segment-params/index.js +2 -0
  299. package/dist/server/access-gate.d.ts +8 -3
  300. package/dist/server/access-gate.d.ts.map +1 -1
  301. package/dist/server/action-client.d.ts +41 -6
  302. package/dist/server/action-client.d.ts.map +1 -1
  303. package/dist/server/action-encryption.d.ts +76 -0
  304. package/dist/server/action-encryption.d.ts.map +1 -0
  305. package/dist/server/action-handler.d.ts +35 -1
  306. package/dist/server/action-handler.d.ts.map +1 -1
  307. package/dist/server/actions.d.ts +3 -6
  308. package/dist/server/actions.d.ts.map +1 -1
  309. package/dist/server/als-registry.d.ts +33 -5
  310. package/dist/server/als-registry.d.ts.map +1 -1
  311. package/dist/server/asset-headers.d.ts +1 -15
  312. package/dist/server/asset-headers.d.ts.map +1 -1
  313. package/dist/server/build-manifest.d.ts +2 -2
  314. package/dist/server/build-manifest.d.ts.map +1 -1
  315. package/dist/server/cookie-context.d.ts +170 -0
  316. package/dist/server/cookie-context.d.ts.map +1 -0
  317. package/dist/server/cookie-parsing.d.ts +51 -0
  318. package/dist/server/cookie-parsing.d.ts.map +1 -0
  319. package/dist/server/debug.d.ts +1 -1
  320. package/dist/server/default-logger.d.ts +22 -0
  321. package/dist/server/default-logger.d.ts.map +1 -0
  322. package/dist/server/deny-boundary.d.ts +90 -0
  323. package/dist/server/deny-boundary.d.ts.map +1 -0
  324. package/dist/server/deny-renderer.d.ts.map +1 -1
  325. package/dist/server/dev-source-map.d.ts +22 -0
  326. package/dist/server/dev-source-map.d.ts.map +1 -0
  327. package/dist/server/early-hints-sender.d.ts.map +1 -1
  328. package/dist/server/early-hints.d.ts +13 -5
  329. package/dist/server/early-hints.d.ts.map +1 -1
  330. package/dist/server/error-boundary-wrapper.d.ts +7 -1
  331. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  332. package/dist/server/fallback-error.d.ts +12 -7
  333. package/dist/server/fallback-error.d.ts.map +1 -1
  334. package/dist/server/flight-injection-state.d.ts +66 -0
  335. package/dist/server/flight-injection-state.d.ts.map +1 -0
  336. package/dist/server/flight-scripts.d.ts +42 -0
  337. package/dist/server/flight-scripts.d.ts.map +1 -0
  338. package/dist/server/flush.d.ts.map +1 -1
  339. package/dist/server/form-data.d.ts +29 -0
  340. package/dist/server/form-data.d.ts.map +1 -1
  341. package/dist/server/html-injector-core.d.ts +212 -0
  342. package/dist/server/html-injector-core.d.ts.map +1 -0
  343. package/dist/server/html-injectors.d.ts +81 -41
  344. package/dist/server/html-injectors.d.ts.map +1 -1
  345. package/dist/server/index.d.ts +9 -46
  346. package/dist/server/index.d.ts.map +1 -1
  347. package/dist/server/index.js +155 -2905
  348. package/dist/server/index.js.map +1 -1
  349. package/dist/server/internal.d.ts +48 -0
  350. package/dist/server/internal.d.ts.map +1 -0
  351. package/dist/server/internal.js +2900 -0
  352. package/dist/server/internal.js.map +1 -0
  353. package/dist/server/logger.d.ts +39 -7
  354. package/dist/server/logger.d.ts.map +1 -1
  355. package/dist/server/middleware-runner.d.ts +36 -4
  356. package/dist/server/middleware-runner.d.ts.map +1 -1
  357. package/dist/server/node-stream-transforms.d.ts +110 -0
  358. package/dist/server/node-stream-transforms.d.ts.map +1 -0
  359. package/dist/server/param-coercion.d.ts +26 -0
  360. package/dist/server/param-coercion.d.ts.map +1 -0
  361. package/dist/server/pipeline-helpers.d.ts +95 -0
  362. package/dist/server/pipeline-helpers.d.ts.map +1 -0
  363. package/dist/server/pipeline-interception.d.ts +1 -1
  364. package/dist/server/pipeline-interception.d.ts.map +1 -1
  365. package/dist/server/pipeline-metadata.d.ts +6 -0
  366. package/dist/server/pipeline-metadata.d.ts.map +1 -1
  367. package/dist/server/pipeline-outcome.d.ts +49 -0
  368. package/dist/server/pipeline-outcome.d.ts.map +1 -0
  369. package/dist/server/pipeline-phases.d.ts +52 -0
  370. package/dist/server/pipeline-phases.d.ts.map +1 -0
  371. package/dist/server/pipeline.d.ts +93 -23
  372. package/dist/server/pipeline.d.ts.map +1 -1
  373. package/dist/server/port-resolution.d.ts +117 -0
  374. package/dist/server/port-resolution.d.ts.map +1 -0
  375. package/dist/server/primitives.d.ts +69 -18
  376. package/dist/server/primitives.d.ts.map +1 -1
  377. package/dist/server/render-timeout.d.ts +51 -0
  378. package/dist/server/render-timeout.d.ts.map +1 -0
  379. package/dist/server/request-context.d.ts +41 -109
  380. package/dist/server/request-context.d.ts.map +1 -1
  381. package/dist/server/route-element-builder.d.ts +27 -1
  382. package/dist/server/route-element-builder.d.ts.map +1 -1
  383. package/dist/server/route-handler.d.ts.map +1 -1
  384. package/dist/server/route-matcher.d.ts +21 -34
  385. package/dist/server/route-matcher.d.ts.map +1 -1
  386. package/dist/server/rsc-entry/action-middleware-runner.d.ts +66 -0
  387. package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -0
  388. package/dist/server/rsc-entry/api-handler.d.ts +2 -2
  389. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
  390. package/dist/server/rsc-entry/error-renderer.d.ts +26 -13
  391. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  392. package/dist/server/rsc-entry/helpers.d.ts +49 -6
  393. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  394. package/dist/server/rsc-entry/index.d.ts +20 -3
  395. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  396. package/dist/server/rsc-entry/render-route.d.ts +50 -0
  397. package/dist/server/rsc-entry/render-route.d.ts.map +1 -0
  398. package/dist/server/rsc-entry/rsc-payload.d.ts +3 -3
  399. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  400. package/dist/server/rsc-entry/rsc-stream.d.ts +14 -1
  401. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  402. package/dist/server/rsc-entry/ssr-bridge.d.ts +1 -1
  403. package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
  404. package/dist/server/rsc-entry/ssr-renderer.d.ts +19 -4
  405. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  406. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +119 -0
  407. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -0
  408. package/dist/server/safe-load.d.ts +46 -0
  409. package/dist/server/safe-load.d.ts.map +1 -0
  410. package/dist/server/sensitive-fields.d.ts +74 -0
  411. package/dist/server/sensitive-fields.d.ts.map +1 -0
  412. package/dist/server/sitemap-generator.d.ts +129 -0
  413. package/dist/server/sitemap-generator.d.ts.map +1 -0
  414. package/dist/server/sitemap-handler.d.ts +22 -0
  415. package/dist/server/sitemap-handler.d.ts.map +1 -0
  416. package/dist/server/slot-resolver.d.ts +1 -1
  417. package/dist/server/slot-resolver.d.ts.map +1 -1
  418. package/dist/server/ssr-entry.d.ts +23 -0
  419. package/dist/server/ssr-entry.d.ts.map +1 -1
  420. package/dist/server/ssr-render.d.ts +39 -21
  421. package/dist/server/ssr-render.d.ts.map +1 -1
  422. package/dist/server/ssr-wrappers.d.ts +50 -0
  423. package/dist/server/ssr-wrappers.d.ts.map +1 -0
  424. package/dist/server/state-tree-diff.d.ts.map +1 -1
  425. package/dist/server/status-code-resolver.d.ts +16 -11
  426. package/dist/server/status-code-resolver.d.ts.map +1 -1
  427. package/dist/server/stream-utils.d.ts +36 -0
  428. package/dist/server/stream-utils.d.ts.map +1 -0
  429. package/dist/server/tracing.d.ts +5 -5
  430. package/dist/server/tracing.d.ts.map +1 -1
  431. package/dist/server/tree-builder.d.ts +62 -30
  432. package/dist/server/tree-builder.d.ts.map +1 -1
  433. package/dist/server/types.d.ts +49 -4
  434. package/dist/server/types.d.ts.map +1 -1
  435. package/dist/server/utils/escape-html.d.ts +14 -0
  436. package/dist/server/utils/escape-html.d.ts.map +1 -0
  437. package/dist/server/version-skew.d.ts +61 -0
  438. package/dist/server/version-skew.d.ts.map +1 -0
  439. package/dist/shared/merge-search-params.d.ts +22 -0
  440. package/dist/shared/merge-search-params.d.ts.map +1 -0
  441. package/dist/shims/font-google.d.ts +1 -1
  442. package/dist/shims/font-google.d.ts.map +1 -1
  443. package/dist/shims/font-google.js +42 -0
  444. package/dist/shims/font-google.js.map +1 -0
  445. package/dist/shims/font-local.d.ts +26 -0
  446. package/dist/shims/font-local.d.ts.map +1 -0
  447. package/dist/shims/font-local.js +20 -0
  448. package/dist/shims/font-local.js.map +1 -0
  449. package/dist/shims/headers.d.ts +2 -1
  450. package/dist/shims/headers.d.ts.map +1 -1
  451. package/dist/shims/navigation-client.d.ts +3 -1
  452. package/dist/shims/navigation-client.d.ts.map +1 -1
  453. package/dist/shims/navigation.d.ts +11 -5
  454. package/dist/shims/navigation.d.ts.map +1 -1
  455. package/dist/utils/directive-parser.d.ts +1 -43
  456. package/dist/utils/directive-parser.d.ts.map +1 -1
  457. package/dist/utils/state-machine.d.ts +80 -0
  458. package/dist/utils/state-machine.d.ts.map +1 -0
  459. package/package.json +56 -22
  460. package/src/adapters/build-output-helper.ts +77 -0
  461. package/src/adapters/cloudflare-dev.ts +177 -0
  462. package/src/adapters/cloudflare-kv-cache.ts +142 -0
  463. package/src/adapters/cloudflare.ts +350 -76
  464. package/src/adapters/compress-module.ts +24 -4
  465. package/src/adapters/nitro.ts +142 -59
  466. package/src/adapters/shared.ts +40 -0
  467. package/src/adapters/wrangler.d.ts +7 -0
  468. package/src/cache/cache-api.ts +38 -0
  469. package/src/cache/handler-store.ts +68 -0
  470. package/src/cache/index.ts +81 -18
  471. package/src/cache/singleflight.ts +62 -4
  472. package/src/cache/sizeof.ts +31 -0
  473. package/src/cache/timber-cache.ts +25 -20
  474. package/src/cli.ts +16 -6
  475. package/src/client/browser-dev.ts +153 -1
  476. package/src/client/browser-entry/action-dispatch.ts +116 -0
  477. package/src/client/browser-entry/hmr.ts +81 -0
  478. package/src/client/browser-entry/hydrate.ts +145 -0
  479. package/src/client/browser-entry/index.ts +143 -0
  480. package/src/client/browser-entry/post-hydration.ts +119 -0
  481. package/src/client/browser-entry/router-init.ts +193 -0
  482. package/src/client/browser-entry/rsc-stream.ts +157 -0
  483. package/src/client/browser-entry/scroll.ts +27 -0
  484. package/src/client/error-boundary.tsx +97 -20
  485. package/src/client/error-reconstituter.tsx +65 -0
  486. package/src/client/form.tsx +31 -32
  487. package/src/client/history.ts +26 -4
  488. package/src/client/index.ts +77 -43
  489. package/src/client/internal.ts +58 -0
  490. package/src/client/link-pending-store.ts +111 -0
  491. package/src/client/link.tsx +342 -113
  492. package/src/client/nav-link-store.ts +47 -0
  493. package/src/client/navigation-api-types.ts +112 -0
  494. package/src/client/navigation-api.ts +332 -0
  495. package/src/client/navigation-context.ts +31 -6
  496. package/src/client/navigation-root.tsx +342 -0
  497. package/src/client/nuqs-adapter.tsx +16 -3
  498. package/src/client/router-ref.ts +1 -1
  499. package/src/client/router.ts +300 -73
  500. package/src/client/rsc-fetch.ts +112 -8
  501. package/src/client/segment-cache.ts +1 -1
  502. package/src/client/segment-outlet.tsx +86 -0
  503. package/src/client/ssr-data.ts +13 -5
  504. package/src/client/stale-reload.ts +72 -3
  505. package/src/client/state.ts +2 -2
  506. package/src/client/top-loader.tsx +18 -6
  507. package/src/client/use-cookie.ts +29 -0
  508. package/src/client/use-link-status.ts +7 -7
  509. package/src/client/{use-navigation-pending.ts → use-pending-navigation.ts} +6 -6
  510. package/src/client/use-query-states.ts +9 -3
  511. package/src/client/use-router.ts +1 -1
  512. package/src/client/{use-params.ts → use-segment-params.ts} +7 -5
  513. package/src/codec.ts +45 -0
  514. package/src/config-types.ts +292 -0
  515. package/src/config-validation.ts +303 -0
  516. package/src/content/index.ts +5 -13
  517. package/src/cookies/define-cookie.ts +301 -55
  518. package/src/cookies/index.ts +12 -1
  519. package/src/cookies/json-cookie.ts +105 -0
  520. package/src/cookies/validation.ts +134 -0
  521. package/src/dev-tools/404-page.ts +387 -0
  522. package/src/dev-tools/browser-logs.ts +288 -0
  523. package/src/dev-tools/error-page.ts +509 -0
  524. package/src/dev-tools/holding-server.ts +187 -0
  525. package/src/dev-tools/index.ts +90 -0
  526. package/src/dev-tools/instrumentation.ts +176 -0
  527. package/src/{plugins/dev-logs.ts → dev-tools/logs.ts} +3 -3
  528. package/src/dev-tools/overlay.ts +456 -0
  529. package/src/dev-tools/stack-classifier.ts +75 -0
  530. package/src/dev-tools/terminal.ts +183 -0
  531. package/src/{server/dev-warnings.ts → dev-tools/warnings.ts} +5 -50
  532. package/src/fonts/bundle.ts +142 -0
  533. package/src/fonts/css.ts +2 -1
  534. package/src/fonts/dev-middleware.ts +74 -0
  535. package/src/fonts/pipeline.ts +314 -0
  536. package/src/fonts/transform.ts +406 -0
  537. package/src/fonts/types.ts +50 -1
  538. package/src/fonts/virtual-modules.ts +227 -0
  539. package/src/index.ts +376 -356
  540. package/src/plugin-context.ts +240 -0
  541. package/src/plugins/adapter-build.ts +12 -4
  542. package/src/plugins/build-manifest.ts +13 -2
  543. package/src/plugins/build-report.ts +16 -25
  544. package/src/plugins/client-chunks.ts +65 -0
  545. package/src/plugins/content.ts +1 -1
  546. package/src/plugins/dev-server.ts +154 -21
  547. package/src/plugins/entries.ts +111 -10
  548. package/src/plugins/fonts.ts +155 -654
  549. package/src/plugins/mdx.ts +43 -10
  550. package/src/plugins/routing.ts +225 -41
  551. package/src/plugins/server-action-exports.ts +1 -1
  552. package/src/plugins/server-bundle.ts +32 -1
  553. package/src/plugins/shims.ts +136 -35
  554. package/src/plugins/static-build.ts +17 -11
  555. package/src/routing/codegen-shared.ts +98 -0
  556. package/src/routing/codegen-types.ts +53 -0
  557. package/src/routing/codegen.ts +143 -176
  558. package/src/routing/convention-lint.ts +357 -0
  559. package/src/routing/index.ts +5 -1
  560. package/src/routing/interception.ts +1 -1
  561. package/src/routing/link-codegen.ts +294 -0
  562. package/src/routing/scanner.ts +107 -110
  563. package/src/routing/segment-classify.ts +188 -0
  564. package/src/routing/status-file-lint.ts +10 -7
  565. package/src/routing/types.ts +78 -25
  566. package/src/routing/walkers.ts +90 -0
  567. package/src/rsc-runtime/rsc.ts +2 -0
  568. package/src/rsc-runtime/ssr.ts +50 -0
  569. package/src/rsc-runtime/vendor-types.d.ts +7 -0
  570. package/src/{search-params/codecs.ts → schema-bridge.ts} +57 -20
  571. package/src/search-params/define.ts +538 -0
  572. package/src/search-params/index.ts +14 -20
  573. package/src/search-params/registry.ts +2 -2
  574. package/src/search-params/wrappers.ts +92 -0
  575. package/src/segment-params/define.ts +332 -0
  576. package/src/segment-params/index.ts +9 -0
  577. package/src/server/access-gate.tsx +76 -34
  578. package/src/server/action-client.ts +88 -15
  579. package/src/server/action-encryption.ts +144 -0
  580. package/src/server/action-handler.ts +112 -14
  581. package/src/server/actions.ts +10 -9
  582. package/src/server/als-registry.ts +35 -7
  583. package/src/server/asset-headers.ts +8 -34
  584. package/src/server/build-manifest.ts +10 -4
  585. package/src/server/compress.ts +25 -7
  586. package/src/server/cookie-context.ts +468 -0
  587. package/src/server/cookie-parsing.ts +135 -0
  588. package/src/server/debug.ts +1 -1
  589. package/src/server/default-logger.ts +99 -0
  590. package/src/server/deny-boundary.ts +218 -0
  591. package/src/server/deny-renderer.ts +31 -50
  592. package/src/server/dev-source-map.ts +31 -0
  593. package/src/server/early-hints-sender.ts +3 -2
  594. package/src/server/early-hints.ts +36 -15
  595. package/src/server/error-boundary-wrapper.ts +74 -22
  596. package/src/server/fallback-error.ts +74 -102
  597. package/src/server/flight-injection-state.ts +113 -0
  598. package/src/server/flight-scripts.ts +62 -0
  599. package/src/server/flush.ts +2 -1
  600. package/src/server/form-data.ts +76 -0
  601. package/src/server/html-injector-core.ts +403 -0
  602. package/src/server/html-injectors.ts +244 -223
  603. package/src/server/index.ts +31 -184
  604. package/src/server/internal.ts +176 -0
  605. package/src/server/logger.ts +67 -36
  606. package/src/server/middleware-runner.ts +75 -4
  607. package/src/server/node-stream-transforms.ts +369 -0
  608. package/src/server/param-coercion.ts +76 -0
  609. package/src/server/pipeline-helpers.ts +204 -0
  610. package/src/server/pipeline-interception.ts +17 -16
  611. package/src/server/pipeline-metadata.ts +13 -0
  612. package/src/server/pipeline-outcome.ts +167 -0
  613. package/src/server/pipeline-phases.ts +409 -0
  614. package/src/server/pipeline.ts +128 -394
  615. package/src/server/port-resolution.ts +215 -0
  616. package/src/server/primitives.ts +111 -28
  617. package/src/server/render-timeout.ts +108 -0
  618. package/src/server/request-context.ts +94 -338
  619. package/src/server/route-element-builder.ts +288 -192
  620. package/src/server/route-handler.ts +24 -4
  621. package/src/server/route-matcher.ts +44 -65
  622. package/src/server/rsc-entry/action-middleware-runner.ts +167 -0
  623. package/src/server/rsc-entry/api-handler.ts +16 -17
  624. package/src/server/rsc-entry/error-renderer.ts +305 -89
  625. package/src/server/rsc-entry/helpers.ts +136 -12
  626. package/src/server/rsc-entry/index.ts +312 -341
  627. package/src/server/rsc-entry/render-route.ts +304 -0
  628. package/src/server/rsc-entry/rsc-payload.ts +65 -18
  629. package/src/server/rsc-entry/rsc-stream.ts +81 -13
  630. package/src/server/rsc-entry/ssr-bridge.ts +14 -5
  631. package/src/server/rsc-entry/ssr-renderer.ts +171 -38
  632. package/src/server/rsc-entry/wrap-action-dispatch.ts +449 -0
  633. package/src/server/safe-load.ts +60 -0
  634. package/src/server/sensitive-fields.ts +230 -0
  635. package/src/server/sitemap-generator.ts +338 -0
  636. package/src/server/sitemap-handler.ts +126 -0
  637. package/src/server/slot-resolver.ts +245 -230
  638. package/src/server/ssr-entry.ts +215 -32
  639. package/src/server/ssr-render.ts +289 -67
  640. package/src/server/ssr-wrappers.tsx +139 -0
  641. package/src/server/state-tree-diff.ts +4 -1
  642. package/src/server/status-code-resolver.ts +112 -128
  643. package/src/server/stream-utils.ts +213 -0
  644. package/src/server/tracing.ts +21 -10
  645. package/src/server/tree-builder.ts +202 -90
  646. package/src/server/types.ts +55 -6
  647. package/src/server/utils/escape-html.ts +20 -0
  648. package/src/server/version-skew.ts +104 -0
  649. package/src/shared/merge-search-params.ts +55 -0
  650. package/src/shims/font-google.ts +1 -1
  651. package/src/shims/font-local.ts +34 -0
  652. package/src/shims/headers.ts +5 -1
  653. package/src/shims/navigation-client.ts +4 -3
  654. package/src/shims/navigation.ts +14 -7
  655. package/src/utils/directive-parser.ts +1 -390
  656. package/src/utils/state-machine.ts +111 -0
  657. package/dist/_chunks/als-registry-B7DbZ2hS.js +0 -41
  658. package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
  659. package/dist/_chunks/debug-gwlJkDuf.js +0 -108
  660. package/dist/_chunks/debug-gwlJkDuf.js.map +0 -1
  661. package/dist/_chunks/format-DviM89f0.js.map +0 -1
  662. package/dist/_chunks/interception-BOoWmLUA.js +0 -676
  663. package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
  664. package/dist/_chunks/request-context-DIkVh_jG.js +0 -330
  665. package/dist/_chunks/request-context-DIkVh_jG.js.map +0 -1
  666. package/dist/_chunks/ssr-data-MjmprTmO.js.map +0 -1
  667. package/dist/_chunks/tracing-CemImE6h.js +0 -187
  668. package/dist/_chunks/tracing-CemImE6h.js.map +0 -1
  669. package/dist/_chunks/use-cookie-DX-l1_5E.js +0 -91
  670. package/dist/_chunks/use-cookie-DX-l1_5E.js.map +0 -1
  671. package/dist/_chunks/use-query-states-D5KaffOK.js.map +0 -1
  672. package/dist/cache/register-cached-function.d.ts +0 -17
  673. package/dist/cache/register-cached-function.d.ts.map +0 -1
  674. package/dist/client/browser-entry.d.ts +0 -21
  675. package/dist/client/browser-entry.d.ts.map +0 -1
  676. package/dist/client/link-status-provider.d.ts +0 -11
  677. package/dist/client/link-status-provider.d.ts.map +0 -1
  678. package/dist/client/transition-root.d.ts.map +0 -1
  679. package/dist/client/use-navigation-pending.d.ts.map +0 -1
  680. package/dist/client/use-params.d.ts.map +0 -1
  681. package/dist/plugins/cache-transform.d.ts +0 -36
  682. package/dist/plugins/cache-transform.d.ts.map +0 -1
  683. package/dist/plugins/dev-error-overlay.d.ts +0 -60
  684. package/dist/plugins/dev-error-overlay.d.ts.map +0 -1
  685. package/dist/plugins/dev-logs.d.ts.map +0 -1
  686. package/dist/plugins/dynamic-transform.d.ts +0 -72
  687. package/dist/plugins/dynamic-transform.d.ts.map +0 -1
  688. package/dist/search-params/analyze.d.ts +0 -54
  689. package/dist/search-params/analyze.d.ts.map +0 -1
  690. package/dist/search-params/builtin-codecs.d.ts +0 -105
  691. package/dist/search-params/builtin-codecs.d.ts.map +0 -1
  692. package/dist/search-params/codecs.d.ts +0 -53
  693. package/dist/search-params/codecs.d.ts.map +0 -1
  694. package/dist/search-params/create.d.ts +0 -106
  695. package/dist/search-params/create.d.ts.map +0 -1
  696. package/dist/server/dev-fetch-instrumentation.d.ts +0 -22
  697. package/dist/server/dev-fetch-instrumentation.d.ts.map +0 -1
  698. package/dist/server/dev-logger.d.ts.map +0 -1
  699. package/dist/server/dev-span-processor.d.ts.map +0 -1
  700. package/dist/server/dev-warnings.d.ts.map +0 -1
  701. package/dist/server/manifest-status-resolver.d.ts +0 -58
  702. package/dist/server/manifest-status-resolver.d.ts.map +0 -1
  703. package/dist/server/prerender.d.ts +0 -77
  704. package/dist/server/prerender.d.ts.map +0 -1
  705. package/dist/server/response-cache.d.ts +0 -54
  706. package/dist/server/response-cache.d.ts.map +0 -1
  707. package/src/cache/register-cached-function.ts +0 -103
  708. package/src/client/browser-entry.ts +0 -678
  709. package/src/client/link-status-provider.tsx +0 -30
  710. package/src/client/transition-root.tsx +0 -166
  711. package/src/plugins/cache-transform.ts +0 -199
  712. package/src/plugins/dev-error-overlay.ts +0 -230
  713. package/src/plugins/dynamic-transform.ts +0 -161
  714. package/src/search-params/analyze.ts +0 -192
  715. package/src/search-params/builtin-codecs.ts +0 -228
  716. package/src/search-params/create.ts +0 -321
  717. package/src/server/dev-fetch-instrumentation.ts +0 -96
  718. package/src/server/dev-span-processor.ts +0 -78
  719. package/src/server/manifest-status-resolver.ts +0 -215
  720. package/src/server/prerender.ts +0 -139
  721. package/src/server/response-cache.ts +0 -410
  722. /package/src/{server/dev-logger.ts → dev-tools/logger.ts} +0 -0
@@ -0,0 +1,406 @@
1
+ /**
2
+ * Transform-hook logic for the timber-fonts plugin.
3
+ *
4
+ * Scans source files for font function calls (`Inter({...})`,
5
+ * `localFont({...})`), validates that they are statically analysable,
6
+ * registers the extracted fonts in the `FontPipeline`, and rewrites the
7
+ * call sites to static `FontResult` literals so the runtime never has to
8
+ * evaluate the font function.
9
+ *
10
+ * Design doc: 24-fonts.md
11
+ */
12
+
13
+ import type { ExtractedFont, GoogleFontConfig } from './types.js';
14
+ import { buildFontStack } from './fallbacks.js';
15
+ import { processLocalFont } from './local.js';
16
+ import {
17
+ extractFontConfigAst,
18
+ extractLocalFontConfigAst,
19
+ detectDynamicFontCallAst,
20
+ } from './ast.js';
21
+ import type { FontPipeline } from './pipeline.js';
22
+
23
+ /**
24
+ * Regex that matches imports from either `@timber/fonts/google` or
25
+ * `next/font/google` (which the shims plugin resolves to the same virtual
26
+ * module). The transform hook still needs to recognise both spellings in
27
+ * source code.
28
+ */
29
+ const GOOGLE_FONT_IMPORT_RE =
30
+ /import\s*\{([^}]+)\}\s*from\s*['"](?:@timber\/fonts\/google|@timber-js\/app\/fonts\/google|next\/font\/google)['"]/g;
31
+
32
+ /**
33
+ * Convert a font family name to a scoped class name.
34
+ * e.g. "JetBrains Mono" → "timber-font-jetbrains-mono"
35
+ */
36
+ function familyToClassName(family: string): string {
37
+ return `timber-font-${family.toLowerCase().replace(/\s+/g, '-')}`;
38
+ }
39
+
40
+ /** Normalize a string or string array to an array (default `['400']`). */
41
+ function normalizeWeightArray(value: string | string[] | undefined): string[] {
42
+ if (!value) return ['400'];
43
+ return Array.isArray(value) ? value : [value];
44
+ }
45
+
46
+ /** Normalize style to an array (default `['normal']`). */
47
+ function normalizeStyleArray(value: string | string[] | undefined): string[] {
48
+ if (!value) return ['normal'];
49
+ return Array.isArray(value) ? value : [value];
50
+ }
51
+
52
+ /**
53
+ * Generate a unique font ID from family + config hash.
54
+ *
55
+ * The ID intentionally includes every property that affects the rendered
56
+ * `@font-face` output (`weight`, `style`, `subsets`, `display`) so that an
57
+ * HMR edit changing any of those produces a new ID and the registry is
58
+ * forced to re-emit fresh CSS.
59
+ */
60
+ export function generateFontId(family: string, config: GoogleFontConfig): string {
61
+ const weights = normalizeWeightArray(config.weight);
62
+ const styles = normalizeStyleArray(config.style);
63
+ const subsets = config.subsets ?? ['latin'];
64
+ const display = config.display ?? 'swap';
65
+ return `${family.toLowerCase()}-${weights.join(',')}-${styles.join(',')}-${subsets.join(',')}-${display}`;
66
+ }
67
+
68
+ /**
69
+ * Extract static font config from a font function call source.
70
+ *
71
+ * Returns `null` if the call cannot be statically analysed.
72
+ */
73
+ export function extractFontConfig(callSource: string): GoogleFontConfig | null {
74
+ return extractFontConfigAst(callSource);
75
+ }
76
+
77
+ /**
78
+ * Detect dynamic/computed font function calls that cannot be statically
79
+ * analysed. Returns the offending expression text, or `null` if all calls
80
+ * are static.
81
+ */
82
+ export function detectDynamicFontCall(source: string, importedNames: string[]): string | null {
83
+ return detectDynamicFontCallAst(source, importedNames);
84
+ }
85
+
86
+ /**
87
+ * Parse the local names imported from `@timber/fonts/google` /
88
+ * `next/font/google`. e.g. `import { Inter, JetBrains_Mono as Mono }` →
89
+ * `['Inter', 'Mono']`.
90
+ */
91
+ export function parseGoogleFontImports(source: string): string[] {
92
+ const importPattern = new RegExp(GOOGLE_FONT_IMPORT_RE.source, 'g');
93
+ const names: string[] = [];
94
+
95
+ let match;
96
+ while ((match = importPattern.exec(source)) !== null) {
97
+ const specifiers = match[1]
98
+ .split(',')
99
+ .map((s) => s.trim())
100
+ .filter(Boolean);
101
+ for (const spec of specifiers) {
102
+ // Handle `Inter as MyInter` — we want the local name
103
+ const parts = spec.split(/\s+as\s+/);
104
+ names.push(parts[parts.length - 1].trim());
105
+ }
106
+ }
107
+
108
+ return names;
109
+ }
110
+
111
+ /**
112
+ * Parse Google font imports into a `localName → familyName` map.
113
+ *
114
+ * e.g. `import { JetBrains_Mono as Mono }` → `{ Mono: 'JetBrains Mono' }`.
115
+ * The original (pre-`as`) name is converted from PascalCase-with-underscores
116
+ * to the human-readable family name by replacing `_` with a space.
117
+ */
118
+ export function parseGoogleFontFamilies(source: string): Map<string, string> {
119
+ const importPattern = new RegExp(GOOGLE_FONT_IMPORT_RE.source, 'g');
120
+ const families = new Map<string, string>();
121
+
122
+ let match;
123
+ while ((match = importPattern.exec(source)) !== null) {
124
+ const specifiers = match[1]
125
+ .split(',')
126
+ .map((s) => s.trim())
127
+ .filter(Boolean);
128
+ for (const spec of specifiers) {
129
+ const parts = spec.split(/\s+as\s+/);
130
+ const originalName = parts[0].trim();
131
+ const localName = parts[parts.length - 1].trim();
132
+ // Convert export name back to family name: JetBrains_Mono → JetBrains Mono
133
+ const family = originalName.replace(/_/g, ' ');
134
+ families.set(localName, family);
135
+ }
136
+ }
137
+
138
+ return families;
139
+ }
140
+
141
+ /**
142
+ * Parse the local name used for the default import of `@timber/fonts/local`.
143
+ *
144
+ * Handles either spelling and arbitrary local names:
145
+ * import localFont from '@timber/fonts/local'
146
+ * import myLoader from 'next/font/local'
147
+ */
148
+ export function parseLocalFontImportName(source: string): string | null {
149
+ const match = source.match(
150
+ /import\s+(\w+)\s+from\s*['"](?:@timber\/fonts\/local|@timber-js\/app\/fonts\/local|next\/font\/local)['"]/
151
+ );
152
+ return match ? match[1] : null;
153
+ }
154
+
155
+ /** Build the static `FontResult` literal we substitute for a font call. */
156
+ function buildFontResultLiteral(extracted: ExtractedFont): string {
157
+ return extracted.variable
158
+ ? `{ className: "${extracted.className}", style: { fontFamily: "${extracted.fontFamily}" }, variable: "${extracted.variable}" }`
159
+ : `{ className: "${extracted.className}", style: { fontFamily: "${extracted.fontFamily}" } }`;
160
+ }
161
+
162
+ /** Type for the Vite plugin's `this.error` callback. */
163
+ type EmitError = (msg: string) => never;
164
+
165
+ /**
166
+ * Find Google font calls in `originalCode`, register them in the pipeline,
167
+ * and replace each call site in `transformedCode` with a static FontResult.
168
+ */
169
+ function transformGoogleFonts(
170
+ transformedCode: string,
171
+ originalCode: string,
172
+ importerId: string,
173
+ pipeline: FontPipeline,
174
+ emitError: EmitError
175
+ ): string {
176
+ const families = parseGoogleFontFamilies(originalCode);
177
+ if (families.size === 0) return transformedCode;
178
+
179
+ const importedNames = [...families.keys()];
180
+
181
+ const dynamicCall = detectDynamicFontCall(originalCode, importedNames);
182
+ if (dynamicCall) {
183
+ emitError(
184
+ `Font function calls must be statically analyzable. ` +
185
+ `Found dynamic call: ${dynamicCall}. ` +
186
+ `Pass a literal object with string/array values instead.`
187
+ );
188
+ }
189
+
190
+ for (const [localName, family] of families) {
191
+ const callPattern = new RegExp(
192
+ `(?:const|let|var)\\s+(\\w+)\\s*=\\s*${localName}\\s*\\(\\s*(\\{[\\s\\S]*?\\})\\s*\\)`,
193
+ 'g'
194
+ );
195
+
196
+ let callMatch;
197
+ while ((callMatch = callPattern.exec(originalCode)) !== null) {
198
+ const varName = callMatch[1];
199
+ const configSource = callMatch[2];
200
+ const fullMatch = callMatch[0];
201
+
202
+ const config = extractFontConfig(`(${configSource})`);
203
+ if (!config) {
204
+ emitError(
205
+ `Could not statically analyze font config for ${family}. ` +
206
+ `Ensure all config values are string literals or arrays of string literals.`
207
+ );
208
+ }
209
+
210
+ const fontId = generateFontId(family, config!);
211
+ const className = familyToClassName(family);
212
+ const fontStack = buildFontStack(family);
213
+ const display = config!.display ?? 'swap';
214
+
215
+ const extracted: ExtractedFont = {
216
+ id: fontId,
217
+ family,
218
+ provider: 'google',
219
+ weights: normalizeWeightArray(config!.weight),
220
+ styles: normalizeStyleArray(config!.style),
221
+ subsets: config!.subsets ?? ['latin'],
222
+ display,
223
+ variable: config!.variable,
224
+ className,
225
+ fontFamily: fontStack,
226
+ importer: importerId,
227
+ };
228
+
229
+ pipeline.pruneFor(importerId, family);
230
+ pipeline.register(extracted);
231
+
232
+ const replacement = `const ${varName} = ${buildFontResultLiteral(extracted)}`;
233
+ transformedCode = transformedCode.replace(fullMatch, replacement);
234
+ }
235
+ }
236
+
237
+ // Strip the import statement; the values it referred to are now inlined.
238
+ transformedCode = transformedCode.replace(
239
+ /import\s*\{[^}]+\}\s*from\s*['"](?:@timber\/fonts\/google|@timber-js\/app\/fonts\/google|next\/font\/google)['"];?\s*\n?/g,
240
+ ''
241
+ );
242
+
243
+ return transformedCode;
244
+ }
245
+
246
+ /**
247
+ * Find local font calls in `originalCode`, register them in the pipeline,
248
+ * and replace each call site in `transformedCode` with a static FontResult.
249
+ */
250
+ function transformLocalFonts(
251
+ transformedCode: string,
252
+ originalCode: string,
253
+ importerId: string,
254
+ pipeline: FontPipeline,
255
+ emitError: EmitError
256
+ ): string {
257
+ const localName = parseLocalFontImportName(originalCode);
258
+ if (!localName) return transformedCode;
259
+
260
+ const dynamicCall = detectDynamicFontCall(originalCode, [localName]);
261
+ if (dynamicCall) {
262
+ emitError(
263
+ `Font function calls must be statically analyzable. ` +
264
+ `Found dynamic call: ${dynamicCall}. ` +
265
+ `Pass a literal object with string/array values instead.`
266
+ );
267
+ }
268
+
269
+ const callPattern = new RegExp(
270
+ `(?:const|let|var)\\s+(\\w+)\\s*=\\s*${localName}\\s*\\(\\s*(\\{[\\s\\S]*?\\})\\s*\\)`,
271
+ 'g'
272
+ );
273
+
274
+ let callMatch;
275
+ while ((callMatch = callPattern.exec(originalCode)) !== null) {
276
+ const varName = callMatch[1];
277
+ const configSource = callMatch[2];
278
+ const fullMatch = callMatch[0];
279
+
280
+ const config = extractLocalFontConfigAst(`(${configSource})`);
281
+ if (!config) {
282
+ emitError(
283
+ `Could not statically analyze local font config. ` +
284
+ `Ensure src is a string or array of { path, weight?, style? } objects.`
285
+ );
286
+ }
287
+
288
+ const extracted = processLocalFont(config!, importerId);
289
+ pipeline.pruneFor(importerId, extracted.family);
290
+ pipeline.register(extracted);
291
+
292
+ const replacement = `const ${varName} = ${buildFontResultLiteral(extracted)}`;
293
+ transformedCode = transformedCode.replace(fullMatch, replacement);
294
+ }
295
+
296
+ // Strip the import statement; the values it referred to are now inlined.
297
+ transformedCode = transformedCode.replace(
298
+ /import\s+\w+\s+from\s*['"](?:@timber\/fonts\/local|@timber-js\/app\/fonts\/local|next\/font\/local)['"];?\s*\n?/g,
299
+ ''
300
+ );
301
+
302
+ return transformedCode;
303
+ }
304
+
305
+ /**
306
+ * Run the timber-fonts transform pass on a single source file.
307
+ *
308
+ * Returns the rewritten code (with font calls inlined and a side-effect
309
+ * `import 'virtual:timber-font-css/<hash>.css'` prepended so Vite's CSS
310
+ * pipeline owns the font CSS) or `null` if the file does not import from
311
+ * any timber-fonts virtual module and therefore needs no transformation.
312
+ */
313
+ /**
314
+ * File extensions that may contain real font imports. Only JS/TS module
315
+ * variants are scanned. Excluding non-JS extensions (.mdx, .md, .css,
316
+ * .json, etc.) prevents false-positive matches on text that mentions a
317
+ * font specifier inside documentation, code fences, or asset metadata.
318
+ *
319
+ * Why this matters: the heuristic below is a substring check (`code.includes`),
320
+ * not an AST parse. An MDX file documenting Next.js font compatibility
321
+ * (e.g. a code fence containing `import { Inter } from 'next/font/google'`)
322
+ * would trigger the same match as a real import. The transform then prepends
323
+ * `import 'virtual:timber-font-css/<hash>.css';` to the file, which breaks
324
+ * downstream parsers that require the file to start with their own delimiter
325
+ * — most notably MDX frontmatter, which must start at line 1 col 1 for
326
+ * `remark-frontmatter` to recognise it. See TIM-840.
327
+ */
328
+ const FONT_IMPORT_SCANNABLE_EXTENSIONS = new Set([
329
+ '.js',
330
+ '.jsx',
331
+ '.ts',
332
+ '.tsx',
333
+ '.mjs',
334
+ '.cjs',
335
+ '.mts',
336
+ '.cts',
337
+ ]);
338
+
339
+ function hasScannableExtension(id: string): boolean {
340
+ // Strip Vite query suffixes (?v=..., ?import, ?worker, etc.) before
341
+ // matching so files imported with a query string are still scanned.
342
+ const path = id.split('?', 1)[0];
343
+ const dot = path.lastIndexOf('.');
344
+ if (dot === -1) return false;
345
+ return FONT_IMPORT_SCANNABLE_EXTENSIONS.has(path.slice(dot).toLowerCase());
346
+ }
347
+
348
+ export function runFontsTransform(
349
+ code: string,
350
+ id: string,
351
+ pipeline: FontPipeline,
352
+ emitError: EmitError
353
+ ): { code: string; map: null } | null {
354
+ // Skip virtual modules and node_modules
355
+ if (id.startsWith('\0') || id.includes('node_modules')) return null;
356
+
357
+ // Only scan JS/TS module variants. .mdx, .md, .css, .json, etc. may
358
+ // mention font specifiers in documentation or asset metadata without
359
+ // ever importing them — a substring match would be a false positive
360
+ // and the prepended virtual CSS import would corrupt downstream
361
+ // parsers (e.g. MDX frontmatter, which must start at line 1).
362
+ // See TIM-840.
363
+ if (!hasScannableExtension(id)) return null;
364
+
365
+ const hasGoogleImport =
366
+ code.includes('@timber/fonts/google') ||
367
+ code.includes('@timber-js/app/fonts/google') ||
368
+ code.includes('next/font/google');
369
+ const hasLocalImport =
370
+ code.includes('@timber/fonts/local') ||
371
+ code.includes('@timber-js/app/fonts/local') ||
372
+ code.includes('next/font/local');
373
+ if (!hasGoogleImport && !hasLocalImport) return null;
374
+
375
+ let transformedCode = code;
376
+
377
+ if (hasGoogleImport) {
378
+ transformedCode = transformGoogleFonts(transformedCode, code, id, pipeline, emitError);
379
+ }
380
+
381
+ if (hasLocalImport) {
382
+ transformedCode = transformLocalFonts(transformedCode, code, id, pipeline, emitError);
383
+ }
384
+
385
+ if (transformedCode !== code) {
386
+ // Inject a side-effect import for this file's per-importer virtual
387
+ // CSS module. The `.css` suffix is what makes Vite's CSS pipeline
388
+ // own the module — in dev that gives us HMR + source maps, and in
389
+ // production @vitejs/plugin-rsc walks the RSC module graph via
390
+ // `collectCss`, finds this virtual CSS import, and injects a
391
+ // `<link rel="stylesheet" data-rsc-css-href=...>` through React
392
+ // Float's preinit mechanism (the same path component CSS rides on).
393
+ //
394
+ // The per-importer id guarantees two concurrent requests rendering
395
+ // different routes never see each other's font CSS: each importer's
396
+ // virtual module only resolves to the fonts that importer registered
397
+ // via `FontPipeline.getCssForSegment(importer)`. See TIM-828.
398
+ if (pipeline.size() > 0) {
399
+ const virtualCssId = pipeline.cssIdMap.idFor(id);
400
+ transformedCode = `import '${virtualCssId}';\n` + transformedCode;
401
+ }
402
+ return { code: transformedCode, map: null };
403
+ }
404
+
405
+ return null;
406
+ }
@@ -7,6 +7,10 @@
7
7
  * Design doc: 24-fonts.md
8
8
  */
9
9
 
10
+ // `CachedFont` lives in `google.ts`. Importing it here is type-only so
11
+ // there is no runtime dependency cycle — TypeScript erases the import.
12
+ import type { CachedFont } from './google.js';
13
+
10
14
  /** Configuration passed to a Google font function (e.g. `Inter({ ... })`). */
11
15
  export interface GoogleFontConfig {
12
16
  weight?: string | string[];
@@ -47,7 +51,16 @@ export interface FontResult {
47
51
  variable?: string;
48
52
  }
49
53
 
50
- /** Internal representation of a font extracted during static analysis. */
54
+ /**
55
+ * Internal representation of a font extracted during static analysis.
56
+ *
57
+ * `ExtractedFont` is the **registration input** to `FontPipeline.register()`.
58
+ * Once a font has been registered the pipeline stores a `FontEntry` (which
59
+ * extends `ExtractedFont` with optional resolved `@font-face` descriptors
60
+ * and cached binary files). External code that needs to model a font that
61
+ * is not yet inside a pipeline (e.g. test fixtures) should use
62
+ * `ExtractedFont` directly.
63
+ */
51
64
  export interface ExtractedFont {
52
65
  /** Unique identifier for this font instance (e.g. `inter-400-normal-latin`). */
53
66
  id: string;
@@ -86,3 +99,39 @@ export interface FontFaceDescriptor {
86
99
  display?: string;
87
100
  unicodeRange?: string;
88
101
  }
102
+
103
+ /**
104
+ * The unified per-font store entry held inside `FontPipeline`.
105
+ *
106
+ * Combines what used to live in three parallel maps:
107
+ *
108
+ * - `registry: Map<string, ExtractedFont>` → the base extracted config
109
+ * - `googleFontFacesMap: Map<string, FontFaceDescriptor[]>` → `faces`
110
+ * - `cachedFonts: CachedFont[]` (flat, family-grouped) → `cachedFiles`
111
+ *
112
+ * `pruneFor` drops the entry **and** all of its attached state in one
113
+ * operation, so future code paths cannot reintroduce a TIM-824-style
114
+ * desync between parallel maps.
115
+ *
116
+ * See TIM-829.
117
+ */
118
+ export interface FontEntry extends ExtractedFont {
119
+ /**
120
+ * Pre-resolved `@font-face` descriptors. Populated in `buildStart`
121
+ * (production) or lazily in `load` (dev). For local fonts the descriptors
122
+ * are derived from the source list at CSS-generation time and are not
123
+ * stored here.
124
+ */
125
+ faces?: FontFaceDescriptor[];
126
+
127
+ /**
128
+ * Downloaded Google Font binaries that back this entry's `@font-face`
129
+ * declarations. Empty for local fonts and in dev mode (where Google fonts
130
+ * are served straight from the CDN).
131
+ *
132
+ * Multiple entries that share the same `family` will hold references to
133
+ * the same `CachedFont` objects. The bundle emitter dedupes by
134
+ * `hashedFilename` so each binary is written exactly once.
135
+ */
136
+ cachedFiles?: CachedFont[];
137
+ }
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Virtual module source generators for the timber-fonts plugin.
3
+ *
4
+ * The plugin exposes three virtual modules:
5
+ *
6
+ * - `@timber/fonts/google` — exports Google font loader functions. The
7
+ * transform hook replaces actual call sites with static `FontResult`
8
+ * objects, so this module is only loaded as a fallback (e.g. when the
9
+ * transform hook hasn't run yet, or when an unknown font is referenced
10
+ * via the default export proxy).
11
+ * - `@timber/fonts/local` — exports `localFont()` as a fallback default.
12
+ * - `virtual:timber-font-css/<hash>.css` — per-importer CSS module that
13
+ * holds the combined `@font-face` + fallback + class CSS for every
14
+ * font registered under that importer. The transform hook injects a
15
+ * side-effect `import 'virtual:timber-font-css/<hash>.css'` into each
16
+ * file that calls a font function, so Vite's CSS pipeline picks it up
17
+ * like any other CSS asset. @vitejs/plugin-rsc's `collectCss` walks
18
+ * the RSC module graph, finds the virtual module, and injects a
19
+ * `<link rel="stylesheet" data-rsc-css-href=...>` via React Float —
20
+ * the same machinery component CSS rides on (TIM-828).
21
+ *
22
+ * The `.css` suffix is load-bearing: Vite's `isCSSRequest` regex matches
23
+ * on file extension, so keeping it in the virtual id is how we tell
24
+ * Vite's CSS pipeline (dev HMR + build asset emission) that this
25
+ * module is CSS. The old `virtual:timber-font-css-register` side-effect
26
+ * module, `globalThis.__timber_font_css` channel, and inline `<style>`
27
+ * tag in the RSC entry were all removed in TIM-828.
28
+ *
29
+ * Design doc: 24-fonts.md
30
+ */
31
+
32
+ import { createHash } from 'node:crypto';
33
+ import type { ExtractedFont, FontFaceDescriptor } from './types.js';
34
+ import { generateVariableClass, generateFontFamilyClass, generateFontFaces } from './css.js';
35
+ import { generateFallbackCss } from './fallbacks.js';
36
+ import { generateLocalFontFaces } from './local.js';
37
+
38
+ export const VIRTUAL_GOOGLE = '@timber/fonts/google';
39
+ export const VIRTUAL_LOCAL = '@timber/fonts/local';
40
+ export const RESOLVED_GOOGLE = '\0@timber/fonts/google';
41
+ export const RESOLVED_LOCAL = '\0@timber/fonts/local';
42
+
43
+ /**
44
+ * Prefix used for the per-importer virtual CSS modules. The full id is
45
+ * `virtual:timber-font-css/<hash>.css` where `<hash>` is a short SHA-256
46
+ * digest of the importer path. We encode the hash (not the path itself)
47
+ * so the id stays filesystem-safe and opaque to downstream consumers.
48
+ */
49
+ export const VIRTUAL_FONT_CSS_PREFIX = 'virtual:timber-font-css/';
50
+ export const VIRTUAL_FONT_CSS_SUFFIX = '.css';
51
+ export const RESOLVED_FONT_CSS_PREFIX = '\0' + VIRTUAL_FONT_CSS_PREFIX;
52
+
53
+ /**
54
+ * In-memory map from the short hash used in virtual module ids back to
55
+ * the absolute importer path. Populated by `virtualFontCssIdFor()` when
56
+ * the transform hook registers an importer and read by the plugin's
57
+ * `load` hook when Vite asks for the CSS content.
58
+ *
59
+ * Scoped to a single `FontPipeline` instance to avoid cross-plugin-
60
+ * instance collisions during multi-build pipelines.
61
+ */
62
+ export class VirtualFontCssIdMap {
63
+ private readonly map = new Map<string, string>();
64
+
65
+ /**
66
+ * Return a stable virtual module id for `importer`. Calling this twice
67
+ * with the same importer returns the same id. Registering under an
68
+ * existing hash is idempotent.
69
+ */
70
+ idFor(importer: string): string {
71
+ const hash = createHash('sha256').update(importer).digest('hex').slice(0, 16);
72
+ this.map.set(hash, importer);
73
+ return `${VIRTUAL_FONT_CSS_PREFIX}${hash}${VIRTUAL_FONT_CSS_SUFFIX}`;
74
+ }
75
+
76
+ /** Decode the importer from a resolved (or unresolved) virtual css id. */
77
+ importerFor(id: string): string | undefined {
78
+ const body = id.startsWith('\0') ? id.slice(1) : id;
79
+ if (!body.startsWith(VIRTUAL_FONT_CSS_PREFIX)) return undefined;
80
+ if (!body.endsWith(VIRTUAL_FONT_CSS_SUFFIX)) return undefined;
81
+ const hash = body.slice(
82
+ VIRTUAL_FONT_CSS_PREFIX.length,
83
+ body.length - VIRTUAL_FONT_CSS_SUFFIX.length
84
+ );
85
+ return this.map.get(hash);
86
+ }
87
+
88
+ /** True if `id` is any form of the virtual font css module. */
89
+ matches(id: string): boolean {
90
+ const body = id.startsWith('\0') ? id.slice(1) : id;
91
+ return body.startsWith(VIRTUAL_FONT_CSS_PREFIX) && body.endsWith(VIRTUAL_FONT_CSS_SUFFIX);
92
+ }
93
+
94
+ /** Drop every registered mapping. Used by tests. */
95
+ clear(): void {
96
+ this.map.clear();
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Convert a font family name to a PascalCase export name.
102
+ * e.g. "JetBrains Mono" → "JetBrains_Mono"
103
+ */
104
+ function familyToExportName(family: string): string {
105
+ return family.replace(/\s+/g, '_');
106
+ }
107
+
108
+ /**
109
+ * Generate the virtual module source for `@timber/fonts/google`.
110
+ *
111
+ * The transform hook replaces real call sites at build time, so this
112
+ * module only matters as a runtime fallback. We export a Proxy default
113
+ * that handles any font name plus named exports for known families
114
+ * (for tree-shaking).
115
+ */
116
+ export function generateGoogleVirtualModule(fonts: Iterable<ExtractedFont>): string {
117
+ const families = new Set<string>();
118
+ for (const font of fonts) {
119
+ if (font.provider === 'google') families.add(font.family);
120
+ }
121
+
122
+ const lines = [
123
+ '// Auto-generated virtual module: @timber/fonts/google',
124
+ '// Each export is a font loader function that returns a FontResult.',
125
+ '',
126
+ 'function createFontResult(family, config) {',
127
+ ' return {',
128
+ ' className: `timber-font-${family.toLowerCase().replace(/\\s+/g, "-")}`,',
129
+ ' style: { fontFamily: family },',
130
+ ' variable: config?.variable,',
131
+ ' };',
132
+ '}',
133
+ '',
134
+ 'export default new Proxy({}, {',
135
+ ' get(_, prop) {',
136
+ ' if (typeof prop === "string") {',
137
+ ' return (config) => createFontResult(prop.replace(/_/g, " "), config);',
138
+ ' }',
139
+ ' }',
140
+ '});',
141
+ ];
142
+
143
+ for (const family of families) {
144
+ const exportName = familyToExportName(family);
145
+ lines.push('');
146
+ lines.push(`export function ${exportName}(config) {`);
147
+ lines.push(` return createFontResult('${family}', config);`);
148
+ lines.push('}');
149
+ }
150
+
151
+ return lines.join('\n');
152
+ }
153
+
154
+ /**
155
+ * Generate the virtual module source for `@timber/fonts/local`.
156
+ *
157
+ * Like the google virtual module, this is a runtime fallback. The
158
+ * transform hook normally replaces `localFont(...)` calls with static
159
+ * `FontResult` objects at build time.
160
+ */
161
+ export function generateLocalVirtualModule(): string {
162
+ return [
163
+ '// Auto-generated virtual module: @timber/fonts/local',
164
+ '',
165
+ 'export default function localFont(config) {',
166
+ ' const family = config?.family || "Local Font";',
167
+ ' return {',
168
+ ' className: `timber-font-${family.toLowerCase().replace(/\\s+/g, "-")}`,',
169
+ ' style: { fontFamily: family },',
170
+ ' variable: config?.variable,',
171
+ ' };',
172
+ '}',
173
+ ].join('\n');
174
+ }
175
+
176
+ /**
177
+ * Generate combined CSS for a single extracted font.
178
+ *
179
+ * Includes `@font-face` rules (local or Google), the size-adjusted fallback
180
+ * `@font-face`, and the scoped class rule. For Google fonts the resolved
181
+ * `FontFaceDescriptor[]` must be passed in (from `generateProductionFontFaces`
182
+ * in production or `resolveDevFontFaces` in dev).
183
+ */
184
+ export function generateFontCss(font: ExtractedFont, googleFaces?: FontFaceDescriptor[]): string {
185
+ const cssParts: string[] = [];
186
+
187
+ if (font.provider === 'local' && font.localSources) {
188
+ const faces = generateLocalFontFaces(font.family, font.localSources, font.display);
189
+ const faceCss = generateFontFaces(faces);
190
+ if (faceCss) cssParts.push(faceCss);
191
+ }
192
+
193
+ if (font.provider === 'google' && googleFaces && googleFaces.length > 0) {
194
+ const faceCss = generateFontFaces(googleFaces);
195
+ if (faceCss) cssParts.push(faceCss);
196
+ }
197
+
198
+ const fallbackCss = generateFallbackCss(font.family);
199
+ if (fallbackCss) cssParts.push(fallbackCss);
200
+
201
+ if (font.variable) {
202
+ cssParts.push(generateVariableClass(font.className, font.variable, font.fontFamily));
203
+ } else {
204
+ cssParts.push(generateFontFamilyClass(font.className, font.fontFamily));
205
+ }
206
+
207
+ return cssParts.join('\n\n');
208
+ }
209
+
210
+ /**
211
+ * Generate the CSS output for all extracted fonts.
212
+ *
213
+ * Includes `@font-face` rules for local and Google fonts, fallback rules,
214
+ * and scoped classes. `googleFontFacesMap` provides pre-resolved
215
+ * `FontFaceDescriptor[]` for each Google font (keyed by `ExtractedFont.id`).
216
+ */
217
+ export function generateAllFontCss(
218
+ registry: Map<string, ExtractedFont>,
219
+ googleFontFacesMap?: Map<string, FontFaceDescriptor[]>
220
+ ): string {
221
+ const cssParts: string[] = [];
222
+ for (const font of registry.values()) {
223
+ const googleFaces = googleFontFacesMap?.get(font.id);
224
+ cssParts.push(generateFontCss(font, googleFaces));
225
+ }
226
+ return cssParts.join('\n\n');
227
+ }