hyperframes 0.8.127 → 0.8.128

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 (171) hide show
  1. package/dist/{add-7CE5XYK2.js → add-DI4T5YLH.js} +15 -15
  2. package/dist/{animatedGifPrep-4VAIRX7D.js → animatedGifPrep-TSK322QL.js} +6 -6
  3. package/dist/{asset-22WQELZE.js → asset-CFW5RUI3.js} +2 -2
  4. package/dist/{assetCataloger-A2AITAHA.js → assetCataloger-MDMKZM4O.js} +2 -2
  5. package/dist/{auth-33GV2LO2.js → auth-7GR2JYK4.js} +2 -2
  6. package/dist/{autoUpdate-OIXGTNLS.js → autoUpdate-VPEWPSH6.js} +2 -2
  7. package/dist/{backgroundChecks-BVP36PXK.js → backgroundChecks-XU7Y6JEZ.js} +3 -3
  8. package/dist/backgroundChecksWorker.js +3 -3
  9. package/dist/{batchRender-URT4LT44.js → batchRender-HIRFZIUO.js} +3 -3
  10. package/dist/{beats-U34XHTAA.js → beats-6EJDWT63.js} +14 -14
  11. package/dist/{benchmark-FNGERKUQ.js → benchmark-F7U6WZGQ.js} +12 -12
  12. package/dist/{browser-35LEQMCO.js → browser-3ECFI3DA.js} +9 -9
  13. package/dist/{bundleWithLocalizedFonts-4CGUDHCA.js → bundleWithLocalizedFonts-OVFXAXV3.js} +2 -2
  14. package/dist/{capture-GZKMVNKK.js → capture-AC5TRHS5.js} +2 -2
  15. package/dist/{capture-M2JJS4F2.js → capture-YH5MUSL7.js} +9 -9
  16. package/dist/{catalog-OVTLU2MC.js → catalog-TRZAPAXM.js} +15 -15
  17. package/dist/{check-DBVGHGKO.js → check-HP7N4TPB.js} +34 -34
  18. package/dist/{checkBrowser-2SQIUOXA.js → checkBrowser-BHLSXZ3V.js} +35 -35
  19. package/dist/{chunk-CX7NQZDE.js → chunk-2CACHG7K.js} +3 -3
  20. package/dist/{chunk-LLU2BJYY.js → chunk-34C66AH5.js} +2 -2
  21. package/dist/{chunk-T6HJCRJC.js → chunk-3PDQ4PS2.js} +1 -1
  22. package/dist/{chunk-L32XKBOF.js → chunk-4AH73ZQW.js} +1 -1
  23. package/dist/{chunk-S2WNVYLQ.js → chunk-4DGJUQFZ.js} +1 -1
  24. package/dist/{chunk-MK2HBL6E.js → chunk-4IZNC32H.js} +1 -1
  25. package/dist/{chunk-RUYSBG3F.js → chunk-4NWOGZZS.js} +32 -30
  26. package/dist/{chunk-P4PMYDLR.js → chunk-4QJQOVN4.js} +1 -1
  27. package/dist/{chunk-HZHXHO4V.js → chunk-56P5SEOM.js} +1 -1
  28. package/dist/{chunk-Q3I4JEGM.js → chunk-5NZVDRRZ.js} +204 -133
  29. package/dist/{chunk-PVVUMILD.js → chunk-6F63NYFZ.js} +1 -1
  30. package/dist/{chunk-CSHKZ3SL.js → chunk-6RGOHYPP.js} +1 -1
  31. package/dist/{chunk-EKGD52A2.js → chunk-72WHJNAR.js} +2 -2
  32. package/dist/{chunk-D4NPKZFY.js → chunk-77EJSKGP.js} +2 -2
  33. package/dist/{chunk-ZOUDTLLH.js → chunk-A7VIBZLC.js} +2 -2
  34. package/dist/{chunk-QR7IRRUX.js → chunk-AQUXI6ZL.js} +5 -5
  35. package/dist/{chunk-7DPBFAYT.js → chunk-AVUUN2XD.js} +33 -31
  36. package/dist/{chunk-F4WYYNNG.js → chunk-BELUXCL4.js} +1 -1
  37. package/dist/{chunk-TL7Y6IBY.js → chunk-BK2KLAYO.js} +3 -3
  38. package/dist/{chunk-IAKZWKNV.js → chunk-BW773MMX.js} +4 -4
  39. package/dist/{chunk-RHBM33JF.js → chunk-CF6QQ6CL.js} +7 -7
  40. package/dist/{chunk-OE66T7R2.js → chunk-COD4GMYB.js} +1 -1
  41. package/dist/{chunk-ZLJXATSQ.js → chunk-FI4I7CME.js} +1 -1
  42. package/dist/{chunk-7YJ7CCKW.js → chunk-FN7KZDH7.js} +1 -1
  43. package/dist/{chunk-6ACXAUBT.js → chunk-H5TVAQAK.js} +4 -4
  44. package/dist/{chunk-GBTGTS35.js → chunk-IKSUSTJT.js} +1 -1
  45. package/dist/{chunk-YII7JZ2C.js → chunk-JDNYDR3Y.js} +1 -1
  46. package/dist/{chunk-6252EFFW.js → chunk-KC5COKLP.js} +1 -1
  47. package/dist/{chunk-V6LLKVWS.js → chunk-KUZTSKD2.js} +1 -1
  48. package/dist/{chunk-G33WTKF3.js → chunk-L5IFUB5G.js} +2 -2
  49. package/dist/{chunk-ZZAHIKFK.js → chunk-LGTAOCIM.js} +1 -1
  50. package/dist/{chunk-NDPSG5WQ.js → chunk-LRDOFHS5.js} +2 -2
  51. package/dist/{chunk-HXGPETEM.js → chunk-OESXJGYM.js} +1 -1
  52. package/dist/{chunk-7TSIAYXZ.js → chunk-PDAMK4Q5.js} +1 -1
  53. package/dist/{chunk-SC4T53X4.js → chunk-Q767NO4V.js} +3 -3
  54. package/dist/{chunk-K3ZB5A3K.js → chunk-QFQET76X.js} +54 -13
  55. package/dist/{chunk-YSCN66NT.js → chunk-QPRIMML2.js} +1 -1
  56. package/dist/{chunk-PZSNUSBM.js → chunk-RLK4ARFU.js} +2 -2
  57. package/dist/{chunk-BYI2K4RN.js → chunk-UROSVQOH.js} +2 -2
  58. package/dist/{chunk-S4AMCVAK.js → chunk-VKBSHU24.js} +5 -5
  59. package/dist/{chunk-KPY6JX4K.js → chunk-VQME2GRN.js} +11 -11
  60. package/dist/{chunk-32MY4AZN.js → chunk-WPHMNO44.js} +1345 -23
  61. package/dist/{chunk-TP4PR3SL.js → chunk-WULYBA5Y.js} +3 -3
  62. package/dist/{chunk-OLVR6P6R.js → chunk-WVU5CJXG.js} +1 -1
  63. package/dist/{chunk-AGNZ2QLE.js → chunk-YRKBFZDQ.js} +1 -1
  64. package/dist/{chunk-TPWWGKU7.js → chunk-ZJUKFZ2P.js} +2 -2
  65. package/dist/{chunk-OQN3AUOV.js → chunk-ZT4WM4B6.js} +12 -12
  66. package/dist/{clean-STQ2XPVL.js → clean-6Q36AWQL.js} +18 -18
  67. package/dist/cli.js +52 -52
  68. package/dist/{cloud-OQNTIR2N.js → cloud-GQ5UHZZN.js} +4 -4
  69. package/dist/{cloudrun-ELOBBQHC.js → cloudrun-JD7H36SS.js} +4 -4
  70. package/dist/{compare-7PHURXFI.js → compare-YONA7IFI.js} +26 -26
  71. package/dist/{compiler-A3FX5DBC.js → compiler-GNV7YQRO.js} +2 -2
  72. package/dist/{component-56DVF6XU.js → component-BXHWGVG4.js} +3 -3
  73. package/dist/{compositions-2DMUUOFR.js → compositions-XC7Y2FPL.js} +11 -11
  74. package/dist/{delete-EECQVAVJ.js → delete-73UVP2DH.js} +3 -3
  75. package/dist/{deterministicFonts-64U7A25I.js → deterministicFonts-HAEMV4TM.js} +5 -4
  76. package/dist/{dist-OJYO32WN.js → dist-FNNGCKFS.js} +1 -1
  77. package/dist/{dist-IPEAV427.js → dist-NDHKRZTY.js} +2 -2
  78. package/dist/{doctor-F3CXFLFV.js → doctor-J76TZOKL.js} +10 -10
  79. package/dist/{events-2LJGIZKG.js → events-FB2W7SMV.js} +9 -9
  80. package/dist/{events-H5RGIYVK.js → events-I3JMSDHY.js} +8 -8
  81. package/dist/{feedback-VCMOOK2O.js → feedback-WSIA7ROU.js} +12 -12
  82. package/dist/{figma-BL4HCJ5P.js → figma-YOJEBRSS.js} +3 -3
  83. package/dist/{fontData.generated-BW5QEPUT.js → fontData.generated-NXAOI4GH.js} +1 -1
  84. package/dist/fontLocalizeCli.js +10 -11
  85. package/dist/{get-HWN5Z3BL.js → get-DXEKFVNL.js} +3 -3
  86. package/dist/{gpuPolicy-37CE57HN.js → gpuPolicy-IGXJ6JGL.js} +6 -6
  87. package/dist/{grade-compare-3OPZYNNZ.js → grade-compare-PXBFW53F.js} +26 -26
  88. package/dist/{gsapParser-LOSAMHNR.js → gsapParser-DIYYX6JT.js} +63 -41
  89. package/dist/{gsapParserAcorn-ZO2LPEZY.js → gsapParserAcorn-E3GEIRZB.js} +1 -1
  90. package/dist/{help-X3EPTRKV.js → help-Z6TEXIAO.js} +1 -1
  91. package/dist/{history-YR6YTOLT.js → history-EFWP3CMR.js} +18 -18
  92. package/dist/hyperframes-player.global.js +1 -1
  93. package/dist/{info-QG7OXNW6.js → info-AQ2ZLWSB.js} +11 -11
  94. package/dist/{init-JUHW2ZN3.js → init-6ZEBBSR5.js} +22 -22
  95. package/dist/{inspect-LIDYSXAG.js → inspect-EUOV4DGL.js} +27 -27
  96. package/dist/{installer-YH76H5ML.js → installer-WI6EKOWS.js} +4 -4
  97. package/dist/{keyframes-ZLHVLMRA.js → keyframes-U6LVMYGO.js} +44 -42
  98. package/dist/{lambda-T33I4AX6.js → lambda-F6SI24C7.js} +5 -5
  99. package/dist/{layout-VD36CWWV.js → layout-VQRYMZRP.js} +27 -27
  100. package/dist/{lint-JAWDGLYW.js → lint-EBHMQRKO.js} +14 -14
  101. package/dist/{lintProject-IOQ4DBS4.js → lintProject-OHBBM3DJ.js} +1 -1
  102. package/dist/{list-24TGFCB3.js → list-EUOXTLX6.js} +3 -3
  103. package/dist/{login-E2PE6CAD.js → login-OXGFQNHT.js} +4 -4
  104. package/dist/{media-treatment-2BMUTUGD.js → media-treatment-TVCTGGDO.js} +14 -14
  105. package/dist/{models-TS5PWNPS.js → models-XNX7UHKU.js} +7 -7
  106. package/dist/{motionShot-RU4MTVOO.js → motionShot-JXQIO5VJ.js} +12 -12
  107. package/dist/{normalize-audio-245DPF4X.js → normalize-audio-L5LTPJIR.js} +13 -13
  108. package/dist/{open-RH7YWUVH.js → open-BON6TWUG.js} +10 -10
  109. package/dist/{pipeline-ZK3JBXX5.js → pipeline-NID4HZDH.js} +6 -6
  110. package/dist/{play-VK6IF765.js → play-GH43JSBS.js} +29 -29
  111. package/dist/{preflight-H7YMOAYQ.js → preflight-VSZD4AND.js} +7 -7
  112. package/dist/{present-CLKL6QSK.js → present-7PTSYTYU.js} +20 -20
  113. package/dist/{preview-GEPWX2GX.js → preview-X2WVPV66.js} +20 -20
  114. package/dist/{producer-HBP36IDM.js → producer-TLATG6GZ.js} +1 -1
  115. package/dist/{projectConfig-FPSBKFQO.js → projectConfig-KKPSSHER.js} +6 -6
  116. package/dist/{publication-ACSV54DD.js → publication-6BFZTAYA.js} +2 -2
  117. package/dist/{publish-L2NGI7MV.js → publish-ZDRTAWEH.js} +11 -11
  118. package/dist/{remove-background-JRRBJN4U.js → remove-background-OZ4OR3B2.js} +7 -7
  119. package/dist/{render-LZEOB6JN.js → render-U3BRPPSW.js} +3 -3
  120. package/dist/{render-RXMA4QYX.js → render-ZH5ZCLWA.js} +17 -17
  121. package/dist/{render-TDSGRTQQ.js → render-ZTIEKMAN.js} +26 -26
  122. package/dist/{render-batch-ZJQ5VHY6.js → render-batch-3JALVFQN.js} +3 -3
  123. package/dist/{renderCancellation-MS5MAATT.js → renderCancellation-NHRJYSHT.js} +6 -6
  124. package/dist/renderSetupWorker.js +1 -1
  125. package/dist/{resolver-WODAPAQC.js → resolver-Z2T2GU2O.js} +4 -4
  126. package/dist/{sherpa-B3IHJHTK.js → sherpa-DRIVXEAC.js} +7 -7
  127. package/dist/{skills-56DFZVPS.js → skills-XLN4DLZP.js} +11 -11
  128. package/dist/{skillsUpdateCheck-AXKUBY44.js → skillsUpdateCheck-CDL66I4W.js} +3 -3
  129. package/dist/{snapshot-NYQJL43J.js → snapshot-63YZCABA.js} +32 -32
  130. package/dist/{src-4K2RSSO5.js → src-3M44XVWE.js} +5 -5
  131. package/dist/{src-UQ72QBPU.js → src-3Q2T2TAT.js} +11 -12
  132. package/dist/{staticProjectServer-OJH6TFA3.js → staticProjectServer-F563BICS.js} +14 -14
  133. package/dist/{status-MZT3S6U2.js → status-HPLBWFSU.js} +6 -6
  134. package/dist/studio/assets/{hyperframes-player-uqzDaxVL.js → hyperframes-player-CKCx0CFA.js} +1 -1
  135. package/dist/studio/assets/{index-DXSYlqmV.js → index-CF31Pq1E.js} +1 -1
  136. package/dist/studio/assets/{index-COhbL1xq.js → index-ClLzyuwb.js} +171 -171
  137. package/dist/studio/assets/{index-CLBNYlDY.js → index-CwS08gWW.js} +1 -1
  138. package/dist/studio/{chunk-3F47EFCT.js → chunk-7UQWVN52.js} +17 -10
  139. package/dist/studio/chunk-7UQWVN52.js.map +1 -0
  140. package/dist/studio/{chunk-246VB2G3.js → chunk-DRVWEPO5.js} +35 -7
  141. package/dist/studio/chunk-DRVWEPO5.js.map +1 -0
  142. package/dist/studio/{chunk-5EKWJZLM.js → chunk-TDWGXBUT.js} +1 -1
  143. package/dist/studio/chunk-TDWGXBUT.js.map +1 -0
  144. package/dist/studio/{domEditingLayers-OL7GLSS4.js → domEditingLayers-IEVLO54Z.js} +3 -3
  145. package/dist/studio/index.d.ts +8 -9
  146. package/dist/studio/index.html +1 -1
  147. package/dist/studio/index.js +1193 -869
  148. package/dist/studio/index.js.map +1 -1
  149. package/dist/studio/player.js +2 -2
  150. package/dist/{studio-api-IT5SYLXJ.js → studio-api-PH5PMXQW.js} +8 -8
  151. package/dist/{studioServer-KHYH2K3Q.js → studioServer-4CEIYT2G.js} +47 -47
  152. package/dist/{system-4KER4Y62.js → system-G6PL23LG.js} +6 -6
  153. package/dist/{telemetry-G3NYQGWI.js → telemetry-ILX6SWZM.js} +9 -9
  154. package/dist/{timeline-CXK4RVZE.js → timeline-3K3WNFPP.js} +18 -18
  155. package/dist/{tokens-RLU7WBIC.js → tokens-GKA5I6SX.js} +2 -2
  156. package/dist/{transcribe-SOY2USBA.js → transcribe-P76R64RU.js} +7 -7
  157. package/dist/{transcribe-Y37DWCAV.js → transcribe-ZZSCMKNC.js} +12 -12
  158. package/dist/{tts-CGUXR7R4.js → tts-P3LIPIL5.js} +1 -1
  159. package/dist/{updateCheck-EJ5SAGDC.js → updateCheck-QBLDWYDK.js} +2 -2
  160. package/dist/{upgrade-MBWWBNGK.js → upgrade-RDBANWHF.js} +2 -2
  161. package/dist/{urlDownloader-ZJ2TZQ7C.js → urlDownloader-VVG2YURO.js} +5 -5
  162. package/dist/{validate-4J3OCFET.js → validate-55V46LZZ.js} +16 -16
  163. package/dist/{video-43437E6M.js → video-S2W6UQ4H.js} +6 -6
  164. package/package.json +1 -1
  165. package/dist/chunk-4C656PVL.js +0 -1393
  166. package/dist/fontCompression-ISNK7STS.js +0 -15
  167. package/dist/studio/chunk-246VB2G3.js.map +0 -1
  168. package/dist/studio/chunk-3F47EFCT.js.map +0 -1
  169. package/dist/studio/chunk-5EKWJZLM.js.map +0 -1
  170. package/dist/studio/{domEditingLayers-OL7GLSS4.js.map → domEditingLayers-IEVLO54Z.js.map} +0 -0
  171. package/dist/{synthesize-BDXNRYZN.js → synthesize-K62TIZWI.js} +4 -4
@@ -4,6 +4,20 @@ import { dirname as __hf_dirname } from "node:path";
4
4
  var require = __hf_createRequire(import.meta.url);
5
5
  var __filename = __hf_fileURLToPath(import.meta.url);
6
6
  var __dirname = __hf_dirname(__filename);
7
+ import {
8
+ SYSTEM_FONT_SIZE_LIMIT,
9
+ locateSystemFontVariants
10
+ } from "./chunk-TJSTGSAW.js";
11
+ import {
12
+ EMBEDDED_FONT_DATA
13
+ } from "./chunk-FI4I7CME.js";
14
+ import {
15
+ FONT_ALIAS_MAP,
16
+ resolveAliasDisplayName
17
+ } from "./chunk-AVUUN2XD.js";
18
+ import {
19
+ parseHTML
20
+ } from "./chunk-GK4AIFIH.js";
7
21
  import {
8
22
  __commonJS,
9
23
  __require,
@@ -3116,18 +3130,17 @@ var require_wawoff2 = __commonJS({
3116
3130
  }
3117
3131
  });
3118
3132
 
3119
- // ../producer/src/services/fontCompression.ts
3133
+ // ../core/dist/fonts/deterministicFonts.js
3134
+ import { createHash as createHash2 } from "crypto";
3135
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, mkdtempSync as mkdtempSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
3136
+ import { homedir as homedir2, tmpdir as tmpdir2 } from "os";
3137
+ import { join as join2 } from "path";
3138
+ import postcss2 from "postcss";
3139
+
3140
+ // ../core/dist/fonts/fontCompression.js
3120
3141
  var import_wawoff2 = __toESM(require_wawoff2(), 1);
3121
3142
  import { createHash } from "crypto";
3122
- import {
3123
- existsSync,
3124
- mkdirSync,
3125
- mkdtempSync,
3126
- readFileSync,
3127
- renameSync,
3128
- rmSync,
3129
- writeFileSync
3130
- } from "fs";
3143
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
3131
3144
  import { homedir, tmpdir } from "os";
3132
3145
  import { dirname, join } from "path";
3133
3146
  var { compress } = import_wawoff2.default;
@@ -3151,7 +3164,8 @@ function cachedCompressionPath(input, originalFormat, cacheDir) {
3151
3164
  }
3152
3165
  function readCachedCompression(path) {
3153
3166
  try {
3154
- if (!existsSync(path)) return null;
3167
+ if (!existsSync(path))
3168
+ return null;
3155
3169
  const cached = readFileSync(path);
3156
3170
  return cached.length > 0 ? cached : null;
3157
3171
  } catch {
@@ -3169,7 +3183,8 @@ function cacheCompression(path, compressed) {
3169
3183
  } catch {
3170
3184
  } finally {
3171
3185
  try {
3172
- if (tempDir) rmSync(tempDir, { recursive: true, force: true });
3186
+ if (tempDir)
3187
+ rmSync(tempDir, { recursive: true, force: true });
3173
3188
  } catch {
3174
3189
  }
3175
3190
  }
@@ -3182,25 +3197,1332 @@ async function fontToDataUri(input, originalFormat, options = {}) {
3182
3197
  return `data:font/collection;base64,${input.toString("base64")}`;
3183
3198
  }
3184
3199
  try {
3185
- const cachePath = cachedCompressionPath(
3186
- input,
3187
- originalFormat,
3188
- options.cacheDir ?? defaultCacheDir()
3189
- );
3200
+ const cachePath = cachedCompressionPath(input, originalFormat, options.cacheDir ?? defaultCacheDir());
3190
3201
  const cached = readCachedCompression(cachePath);
3191
- if (cached) return `data:font/woff2;base64,${cached.toString("base64")}`;
3202
+ if (cached)
3203
+ return `data:font/woff2;base64,${cached.toString("base64")}`;
3192
3204
  const compressed = await (options.compressImpl ?? compressToWoff2)(input);
3193
3205
  cacheCompression(cachePath, compressed);
3194
3206
  return `data:font/woff2;base64,${compressed.toString("base64")}`;
3195
3207
  } catch {
3196
- console.warn(
3197
- `[fontCompression] woff2 compression failed for ${originalFormat} font, embedding raw format`
3198
- );
3208
+ console.warn(`[fontCompression] woff2 compression failed for ${originalFormat} font, embedding raw format`);
3199
3209
  return `data:${rawMimeType(originalFormat)};base64,${input.toString("base64")}`;
3200
3210
  }
3201
3211
  }
3202
3212
 
3213
+ // ../core/dist/fonts/authoredGoogleFonts.js
3214
+ import postcss from "postcss";
3215
+ function screenStylesheet(media) {
3216
+ return !media?.trim() || media.split(",").some((query) => /^(all|screen)$/i.test(query.trim()));
3217
+ }
3218
+ function googleStylesheetFamilies(urlText) {
3219
+ let parsed;
3220
+ try {
3221
+ parsed = new URL(urlText);
3222
+ } catch {
3223
+ return [];
3224
+ }
3225
+ if (parsed.protocol !== "https:" || parsed.hostname !== "fonts.googleapis.com")
3226
+ return [];
3227
+ return parsed.searchParams.getAll("family").flatMap((family) => family.split("|")).map((family) => family.split(":")[0]?.trim().toLowerCase() ?? "").filter(Boolean);
3228
+ }
3229
+ function importUrls(css) {
3230
+ let root;
3231
+ try {
3232
+ root = postcss.parse(css);
3233
+ } catch {
3234
+ return [];
3235
+ }
3236
+ const urls = [];
3237
+ for (const node of root.nodes) {
3238
+ if (node.type !== "atrule" || node.name.toLowerCase() !== "import")
3239
+ continue;
3240
+ const url = unconditionalImportUrl(node.params);
3241
+ if (url)
3242
+ urls.push(url);
3243
+ }
3244
+ return urls;
3245
+ }
3246
+ function unconditionalImportUrl(params) {
3247
+ const match = /^(?:url\(\s*['"]?([^'")\s]+)['"]?\s*\)|['"]([^'"]+)['"])\s*(.*)$/i.exec(params);
3248
+ if (!match || !screenStylesheet(match[3] ?? null))
3249
+ return void 0;
3250
+ return match[1] ?? match[2];
3251
+ }
3252
+ function activeStylesheet(element) {
3253
+ if (element.hasAttribute("disabled") || !screenStylesheet(element.getAttribute("media")))
3254
+ return false;
3255
+ const type = element.getAttribute("type")?.trim().toLowerCase();
3256
+ return !type || type === "text/css";
3257
+ }
3258
+ function stylesheetUrls(element) {
3259
+ if (!activeStylesheet(element))
3260
+ return [];
3261
+ if (element.localName !== "link")
3262
+ return importUrls(element.textContent ?? "");
3263
+ const rel = (element.getAttribute("rel") ?? "").toLowerCase().split(/\s+/);
3264
+ if (!rel.includes("stylesheet") || rel.includes("alternate"))
3265
+ return [];
3266
+ const href = element.getAttribute("href");
3267
+ return href ? [href] : [];
3268
+ }
3269
+ function authoredGoogleFontStylesheets(html) {
3270
+ const byFamily = /* @__PURE__ */ new Map();
3271
+ const { document: document2 } = parseHTML(html);
3272
+ for (const element of document2.querySelectorAll("link, style")) {
3273
+ for (const url of stylesheetUrls(element)) {
3274
+ for (const family of googleStylesheetFamilies(url)) {
3275
+ const previous = byFamily.get(family) ?? [];
3276
+ byFamily.set(family, [...previous.filter((existing) => existing !== url), url]);
3277
+ }
3278
+ }
3279
+ }
3280
+ return byFamily;
3281
+ }
3282
+ function withPageText(url, fontText) {
3283
+ const parsed = new URL(url);
3284
+ if (!fontText || parsed.searchParams.has("text"))
3285
+ return url;
3286
+ const withoutFragment = url.split("#")[0] ?? url;
3287
+ const joiner = parsed.search ? "&" : "?";
3288
+ return `${withoutFragment}${joiner}text=${encodeURIComponent(fontText)}${parsed.hash}`;
3289
+ }
3290
+
3291
+ // ../core/dist/fonts/deterministicFonts.js
3292
+ var GENERIC_FAMILIES = /* @__PURE__ */ new Set([
3293
+ "sans-serif",
3294
+ "serif",
3295
+ "monospace",
3296
+ "cursive",
3297
+ "fantasy",
3298
+ "system-ui",
3299
+ "ui-sans-serif",
3300
+ "ui-serif",
3301
+ "ui-monospace",
3302
+ "emoji",
3303
+ "math",
3304
+ "fangsong",
3305
+ "-apple-system",
3306
+ "blinkmacsystemfont"
3307
+ ]);
3308
+ var CSS_WIDE_KEYWORDS = /* @__PURE__ */ new Set([
3309
+ "inherit",
3310
+ "initial",
3311
+ "unset",
3312
+ "revert",
3313
+ "revert-layer"
3314
+ ]);
3315
+ function parseFontFamilyValue(value) {
3316
+ const pieces = [];
3317
+ let start = 0;
3318
+ let depth = 0;
3319
+ let quote = null;
3320
+ for (let index = 0; index < value.length; index += 1) {
3321
+ const char = value[index];
3322
+ if (char === "\\") {
3323
+ index += 1;
3324
+ continue;
3325
+ }
3326
+ if (quote) {
3327
+ if (char === quote)
3328
+ quote = null;
3329
+ continue;
3330
+ }
3331
+ if (char === "'" || char === '"') {
3332
+ quote = char;
3333
+ continue;
3334
+ }
3335
+ if (char === "(") {
3336
+ depth += 1;
3337
+ continue;
3338
+ }
3339
+ if (char === ")") {
3340
+ depth = Math.max(0, depth - 1);
3341
+ continue;
3342
+ }
3343
+ if (char !== "," || depth !== 0)
3344
+ continue;
3345
+ pieces.push(value.slice(start, index));
3346
+ start = index + 1;
3347
+ }
3348
+ pieces.push(value.slice(start));
3349
+ return pieces.map((piece) => piece.trim().replace(/^['"]/, "").replace(/['"]$/, "").trim()).filter((piece) => piece.length > 0);
3350
+ }
3351
+ function systemPrimaryReplacement(value, deterministicPrimary, customProperties) {
3352
+ const variable = primaryCssVariable(value);
3353
+ if (variable) {
3354
+ if (customProperties.get(variable.name) || variable.fallback === null)
3355
+ return null;
3356
+ const fallback = systemPrimaryReplacement(variable.fallback, deterministicPrimary, customProperties);
3357
+ return fallback ? `var(${variable.name}, ${fallback})${variable.rest}` : null;
3358
+ }
3359
+ const families = parseFontFamilyValue(value);
3360
+ if (families.length === 0)
3361
+ return null;
3362
+ if (!GENERIC_FAMILIES.has(normalizeFamilyName(families[0])))
3363
+ return null;
3364
+ return `${deterministicPrimary}, ${value.trim()}`;
3365
+ }
3366
+ function parseCssRoot(css) {
3367
+ try {
3368
+ return postcss2.parse(css);
3369
+ } catch {
3370
+ return null;
3371
+ }
3372
+ }
3373
+ function isFontFaceDeclaration(decl) {
3374
+ const parent = decl.parent;
3375
+ return parent?.type === "atrule" && parent.name.toLowerCase() === "font-face";
3376
+ }
3377
+ function normalizeCssDeclarations(root, replacePrimary) {
3378
+ let changed = false;
3379
+ root.walkDecls((decl) => {
3380
+ if (decl.prop.startsWith("--")) {
3381
+ const replacement2 = replacePrimary(decl.value);
3382
+ if (!replacement2)
3383
+ return;
3384
+ decl.value = replacement2;
3385
+ changed = true;
3386
+ return;
3387
+ }
3388
+ if (decl.prop.toLowerCase() !== "font-family")
3389
+ return;
3390
+ if (isFontFaceDeclaration(decl)) {
3391
+ return;
3392
+ }
3393
+ const replacement = replacePrimary(decl.value);
3394
+ if (!replacement)
3395
+ return;
3396
+ decl.value = replacement;
3397
+ changed = true;
3398
+ });
3399
+ return changed;
3400
+ }
3401
+ function normalizeCssFontFamilyDeclarations(css, replacePrimary) {
3402
+ const root = parseCssRoot(css);
3403
+ if (!root)
3404
+ return css;
3405
+ const changed = normalizeCssDeclarations(root, replacePrimary);
3406
+ return changed ? root.toString() : css;
3407
+ }
3408
+ function normalizeInlineStyleAttribute(style, replacePrimary) {
3409
+ const root = parseCssRoot(`*{${style}}`);
3410
+ if (!root)
3411
+ return style;
3412
+ const rule = root.first;
3413
+ if (rule?.type !== "rule")
3414
+ return style;
3415
+ const before = rule.toString();
3416
+ normalizeCssDeclarations(root, replacePrimary);
3417
+ if (rule.toString() === before)
3418
+ return style;
3419
+ const serialized = (rule.nodes ?? []).map((node) => node.toString()).join("; ");
3420
+ return serialized.endsWith(";") ? serialized : `${serialized};`;
3421
+ }
3422
+ function normalizeSystemFontPrimaryFamilies(html, deterministicPrimary = "Inter") {
3423
+ const { document: document2 } = parseHTML(html);
3424
+ const customProperties = collectFontFamilyCustomProperties(html);
3425
+ const replacePrimary = (value) => systemPrimaryReplacement(value, deterministicPrimary, customProperties);
3426
+ let changed = false;
3427
+ for (const styleEl of Array.from(document2.querySelectorAll("style"))) {
3428
+ const current = styleEl.textContent ?? "";
3429
+ const next = normalizeCssFontFamilyDeclarations(current, replacePrimary);
3430
+ if (next === current)
3431
+ continue;
3432
+ styleEl.textContent = next;
3433
+ changed = true;
3434
+ }
3435
+ for (const el of Array.from(document2.querySelectorAll("[style]"))) {
3436
+ const current = el.getAttribute("style") ?? "";
3437
+ const next = normalizeInlineStyleAttribute(current, replacePrimary);
3438
+ if (next === current)
3439
+ continue;
3440
+ el.setAttribute("style", next);
3441
+ changed = true;
3442
+ }
3443
+ for (const el of Array.from(document2.querySelectorAll("[data-font-family]"))) {
3444
+ const current = el.getAttribute("data-font-family") ?? "";
3445
+ const next = replacePrimary(current);
3446
+ if (!next)
3447
+ continue;
3448
+ el.setAttribute("data-font-family", next);
3449
+ changed = true;
3450
+ }
3451
+ return changed ? document2.toString() : html;
3452
+ }
3453
+ function collectCssCustomProperties(css, customProperties) {
3454
+ const root = parseCssRoot(css);
3455
+ if (!root)
3456
+ return;
3457
+ root.walkDecls((decl) => {
3458
+ if (!decl.prop.startsWith("--"))
3459
+ return;
3460
+ customProperties.set(decl.prop, decl.value);
3461
+ });
3462
+ }
3463
+ function* iterateCssRootFontFamilyDeclarations(root) {
3464
+ const declarations = [];
3465
+ root.walkDecls((decl) => {
3466
+ if (decl.prop.toLowerCase() !== "font-family")
3467
+ return;
3468
+ if (isFontFaceDeclaration(decl))
3469
+ return;
3470
+ const declaration = decl.value;
3471
+ declarations.push({
3472
+ surface: "font-family",
3473
+ declaration,
3474
+ families: parseFontFamilyValue(declaration)
3475
+ });
3476
+ });
3477
+ yield* declarations;
3478
+ }
3479
+ function* iterateCssFontFamilyDeclarations(css) {
3480
+ const root = parseCssRoot(css);
3481
+ if (!root)
3482
+ return;
3483
+ yield* iterateCssRootFontFamilyDeclarations(root);
3484
+ }
3485
+ function* iterateInlineStyleFontFamilyDeclarations(style) {
3486
+ const root = parseCssRoot(`*{${style}}`);
3487
+ if (!root)
3488
+ return;
3489
+ yield* iterateCssRootFontFamilyDeclarations(root);
3490
+ }
3491
+ function collectFontFamilyCustomProperties(html) {
3492
+ const { document: document2 } = parseHTML(html);
3493
+ const customProperties = /* @__PURE__ */ new Map();
3494
+ for (const styleEl of Array.from(document2.querySelectorAll("style"))) {
3495
+ collectCssCustomProperties(styleEl.textContent ?? "", customProperties);
3496
+ }
3497
+ for (const el of Array.from(document2.querySelectorAll("[style]"))) {
3498
+ collectCssCustomProperties(`*{${el.getAttribute("style") ?? ""}}`, customProperties);
3499
+ }
3500
+ return customProperties;
3501
+ }
3502
+ function primaryCssVariable(value) {
3503
+ const trimmed = value.trim();
3504
+ if (!trimmed.toLowerCase().startsWith("var("))
3505
+ return null;
3506
+ let depth = 0;
3507
+ for (let index = 0; index < trimmed.length; index += 1) {
3508
+ const char = trimmed[index];
3509
+ if (char === "(") {
3510
+ depth += 1;
3511
+ continue;
3512
+ }
3513
+ if (char !== ")")
3514
+ continue;
3515
+ depth -= 1;
3516
+ if (depth !== 0)
3517
+ continue;
3518
+ const inner = trimmed.slice(4, index).trim();
3519
+ const commaIndex = inner.indexOf(",");
3520
+ const name = (commaIndex === -1 ? inner : inner.slice(0, commaIndex)).trim();
3521
+ if (!/^--[A-Za-z0-9_-]+$/.test(name))
3522
+ return null;
3523
+ return {
3524
+ name,
3525
+ fallback: commaIndex === -1 ? null : inner.slice(commaIndex + 1),
3526
+ rest: trimmed.slice(index + 1)
3527
+ };
3528
+ }
3529
+ return null;
3530
+ }
3531
+ var MAX_CSS_VARIABLE_DEPTH = 8;
3532
+ function resolveDeclaredFamilies(declaration, customProperties, optional = false, depth = 0) {
3533
+ const tag = (families2) => families2.map((family) => ({ family, optional }));
3534
+ const families = parseFontFamilyValue(declaration);
3535
+ const variable = depth < MAX_CSS_VARIABLE_DEPTH ? primaryCssVariable(declaration) : null;
3536
+ if (!variable)
3537
+ return tag(families);
3538
+ const resolved = customProperties.get(variable.name);
3539
+ const source = resolved || variable.fallback;
3540
+ if (source === null)
3541
+ return tag(families);
3542
+ const primary = resolveDeclaredFamilies(source, customProperties, !resolved || depth > 0, depth + 1);
3543
+ if (primary.length === 0)
3544
+ return [];
3545
+ return [...primary, ...tag(families.slice(1))];
3546
+ }
3547
+ function resolveFontFamilyDeclarationFamilies(declaration, customProperties) {
3548
+ return resolveDeclaredFamilies(declaration, customProperties).map(({ family }) => family);
3549
+ }
3550
+ function* iterateFontFamilyDeclarations(html) {
3551
+ const { document: document2 } = parseHTML(html);
3552
+ for (const styleEl of Array.from(document2.querySelectorAll("style"))) {
3553
+ yield* iterateCssFontFamilyDeclarations(styleEl.textContent ?? "");
3554
+ }
3555
+ for (const el of Array.from(document2.querySelectorAll("[style]"))) {
3556
+ yield* iterateInlineStyleFontFamilyDeclarations(el.getAttribute("style") ?? "");
3557
+ }
3558
+ for (const el of Array.from(document2.querySelectorAll("[data-font-family]"))) {
3559
+ const declaration = el.getAttribute("data-font-family") ?? "";
3560
+ yield { surface: "data-font-family", declaration, families: parseFontFamilyValue(declaration) };
3561
+ }
3562
+ }
3563
+ var CANONICAL_FONTS = {
3564
+ inter: {
3565
+ packageName: "@fontsource/inter",
3566
+ faces: [{ weight: "400" }, { weight: "700" }, { weight: "900" }]
3567
+ },
3568
+ montserrat: {
3569
+ packageName: "@fontsource/montserrat",
3570
+ faces: [{ weight: "400" }, { weight: "700" }, { weight: "900" }]
3571
+ },
3572
+ outfit: {
3573
+ packageName: "@fontsource/outfit",
3574
+ faces: [{ weight: "400" }, { weight: "700" }, { weight: "900" }]
3575
+ },
3576
+ nunito: {
3577
+ packageName: "@fontsource/nunito",
3578
+ faces: [{ weight: "400" }, { weight: "700" }, { weight: "900" }]
3579
+ },
3580
+ oswald: {
3581
+ packageName: "@fontsource/oswald",
3582
+ faces: [{ weight: "400" }, { weight: "700" }]
3583
+ },
3584
+ "league-gothic": {
3585
+ packageName: "@fontsource/league-gothic",
3586
+ faces: [{ weight: "400" }]
3587
+ },
3588
+ "archivo-black": {
3589
+ packageName: "@fontsource/archivo-black",
3590
+ faces: [{ weight: "400" }]
3591
+ },
3592
+ "space-mono": {
3593
+ packageName: "@fontsource/space-mono",
3594
+ faces: [{ weight: "400" }, { weight: "700" }]
3595
+ },
3596
+ "ibm-plex-mono": {
3597
+ packageName: "@fontsource/ibm-plex-mono",
3598
+ faces: [{ weight: "400" }, { weight: "700" }]
3599
+ },
3600
+ "jetbrains-mono": {
3601
+ packageName: "@fontsource/jetbrains-mono",
3602
+ faces: [{ weight: "400" }, { weight: "700" }]
3603
+ },
3604
+ "eb-garamond": {
3605
+ packageName: "@fontsource/eb-garamond",
3606
+ faces: [{ weight: "400" }, { weight: "700" }]
3607
+ },
3608
+ "playfair-display": {
3609
+ packageName: "@fontsource/playfair-display",
3610
+ faces: [{ weight: "400" }, { weight: "700" }, { weight: "900" }]
3611
+ },
3612
+ "source-code-pro": {
3613
+ packageName: "@fontsource/source-code-pro",
3614
+ faces: [{ weight: "400" }, { weight: "700" }]
3615
+ },
3616
+ "noto-sans-jp": {
3617
+ packageName: "@fontsource/noto-sans-jp",
3618
+ faces: [{ weight: "400" }, { weight: "700" }]
3619
+ },
3620
+ roboto: {
3621
+ packageName: "@fontsource/roboto",
3622
+ faces: [{ weight: "400" }, { weight: "700" }, { weight: "900" }]
3623
+ },
3624
+ "open-sans": {
3625
+ packageName: "@fontsource/open-sans",
3626
+ faces: [{ weight: "400" }, { weight: "700" }]
3627
+ },
3628
+ lato: {
3629
+ packageName: "@fontsource/lato",
3630
+ faces: [{ weight: "400" }, { weight: "700" }, { weight: "900" }]
3631
+ },
3632
+ poppins: {
3633
+ packageName: "@fontsource/poppins",
3634
+ faces: [{ weight: "400" }, { weight: "700" }, { weight: "900" }]
3635
+ }
3636
+ };
3637
+ var FONT_ALIASES = FONT_ALIAS_MAP;
3638
+ function normalizeFamilyName(family) {
3639
+ return family.trim().replace(/^['"]|['"]$/g, "").trim().toLowerCase();
3640
+ }
3641
+ function fontDataUri(packageName, weight, style = "normal") {
3642
+ const key = `${packageName}:${weight}:${style}`;
3643
+ const uri = EMBEDDED_FONT_DATA.get(key);
3644
+ if (!uri) {
3645
+ throw new Error(`No embedded font data for ${key}. Regenerate with: tsx scripts/generate-font-data.ts`);
3646
+ }
3647
+ return uri;
3648
+ }
3649
+ function extractExistingFontFaces(html) {
3650
+ const families = /* @__PURE__ */ new Set();
3651
+ const opening = /@font-face\s*\{/gi;
3652
+ const family = /font-family\s*:/gi;
3653
+ while (opening.exec(html)) {
3654
+ family.lastIndex = opening.lastIndex;
3655
+ let declaration = family.exec(html);
3656
+ while (declaration && html[family.lastIndex] === ";") {
3657
+ declaration = family.exec(html);
3658
+ }
3659
+ if (!declaration)
3660
+ break;
3661
+ const valueStart = family.lastIndex;
3662
+ const semicolon = html.indexOf(";", valueStart);
3663
+ if (semicolon < 0)
3664
+ break;
3665
+ const end = html.indexOf("}", semicolon + 1);
3666
+ if (end < 0)
3667
+ break;
3668
+ const normalized = normalizeFamilyName(html.slice(valueStart, semicolon));
3669
+ if (normalized)
3670
+ families.add(normalized);
3671
+ opening.lastIndex = end + 1;
3672
+ }
3673
+ return families;
3674
+ }
3675
+ function isFetchableFamilyName(normalized) {
3676
+ if (!normalized || normalized.startsWith("var("))
3677
+ return false;
3678
+ return !GENERIC_FAMILIES.has(normalized) && !CSS_WIDE_KEYWORDS.has(normalized);
3679
+ }
3680
+ function extractRequestedFontFamilies(html) {
3681
+ const requested = /* @__PURE__ */ new Map();
3682
+ const customProperties = collectFontFamilyCustomProperties(html);
3683
+ for (const { declaration } of iterateFontFamilyDeclarations(html)) {
3684
+ for (const { family, optional } of resolveDeclaredFamilies(declaration, customProperties)) {
3685
+ const normalized = family.toLowerCase();
3686
+ if (!isFetchableFamilyName(normalized))
3687
+ continue;
3688
+ const seen = requested.get(normalized);
3689
+ requested.set(normalized, {
3690
+ family: seen?.family ?? family,
3691
+ optional: (seen?.optional ?? true) && optional
3692
+ });
3693
+ }
3694
+ }
3695
+ return requested;
3696
+ }
3697
+ function fontFormatHint(src) {
3698
+ return src.startsWith("data:font/collection;") ? "collection" : "woff2";
3699
+ }
3700
+ var BUNDLED_SUBSET_UNICODE_RANGE = "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD";
3701
+ function isBundledSubsetRange(unicodeRange) {
3702
+ const normalize = (range) => range.toLowerCase().replace(/\s+/g, "");
3703
+ return unicodeRange !== void 0 && normalize(unicodeRange) === normalize(BUNDLED_SUBSET_UNICODE_RANGE);
3704
+ }
3705
+ function buildFontFaceRule(familyName, src, weight, style, unicodeRange) {
3706
+ return [
3707
+ "@font-face {",
3708
+ ` font-family: "${familyName}";`,
3709
+ ` src: url("${src}") format("${fontFormatHint(src)}");`,
3710
+ ` font-style: ${style};`,
3711
+ ` font-weight: ${weight};`,
3712
+ " font-display: block;",
3713
+ // Preserve the subset's unicode-range so the browser selects the right
3714
+ // per-codepoint subset (matching Google Fonts' own CSS semantics).
3715
+ ...unicodeRange ? [` unicode-range: ${unicodeRange};`] : [],
3716
+ "}"
3717
+ ].join("\n");
3718
+ }
3719
+ function normalizeWeightKey(weight) {
3720
+ const numeric = Number(weight);
3721
+ return Number.isFinite(numeric) ? String(numeric) : weight.trim().toLowerCase();
3722
+ }
3723
+ function coverageKey(weight, style) {
3724
+ return `${normalizeWeightKey(weight)}:${style}`;
3725
+ }
3726
+ function groupFacesBySource(faces) {
3727
+ const groups = /* @__PURE__ */ new Map();
3728
+ for (const face of faces) {
3729
+ const key = [face.dataUri, face.style, face.unicodeRange ?? ""].join("\0");
3730
+ const existing = groups.get(key);
3731
+ if (existing)
3732
+ existing.push(face);
3733
+ else
3734
+ groups.set(key, [face]);
3735
+ }
3736
+ return [...groups.values()];
3737
+ }
3738
+ function spansCoveredWeight(from, to, coveredWeights) {
3739
+ const start = Number(from.weight);
3740
+ const end = Number(to.weight);
3741
+ if (!Number.isFinite(start) || !Number.isFinite(end))
3742
+ return true;
3743
+ const low = Math.min(start, end);
3744
+ const high = Math.max(start, end);
3745
+ for (const covered of coveredWeights) {
3746
+ const [weight, style] = covered.split(":");
3747
+ if (style !== from.style)
3748
+ continue;
3749
+ const value = Number(weight);
3750
+ if (Number.isFinite(value) && value > low && value < high)
3751
+ return true;
3752
+ }
3753
+ return false;
3754
+ }
3755
+ function partitionWeightRuns(faces, coveredWeights) {
3756
+ const runs = [];
3757
+ const sortable = faces.filter((face) => Number.isFinite(Number(face.weight)));
3758
+ const unsortable = faces.filter((face) => !Number.isFinite(Number(face.weight)));
3759
+ let current = [];
3760
+ for (const face of [...sortable].sort((a, b) => Number(a.weight) - Number(b.weight))) {
3761
+ const previous = current[current.length - 1];
3762
+ if (previous && spansCoveredWeight(previous, face, coveredWeights)) {
3763
+ runs.push(current);
3764
+ current = [];
3765
+ }
3766
+ current.push(face);
3767
+ }
3768
+ if (current.length > 0)
3769
+ runs.push(current);
3770
+ for (const face of unsortable)
3771
+ runs.push([face]);
3772
+ return runs;
3773
+ }
3774
+ async function bundledFamilyFaceRules(canonical, normalizedFamily, emitFamily, optional, options, fontText) {
3775
+ const rules = [];
3776
+ const coveredWeights = /* @__PURE__ */ new Set();
3777
+ const bundledRules = [];
3778
+ for (const face of canonical.faces) {
3779
+ const style = face.style || "normal";
3780
+ const src = fontDataUri(canonical.packageName, face.weight, style);
3781
+ bundledRules.push(buildFontFaceRule(emitFamily, src, face.weight, style, BUNDLED_SUBSET_UNICODE_RANGE));
3782
+ coveredWeights.add(coverageKey(face.weight, style));
3783
+ }
3784
+ const canonicalFamily = resolveAliasDisplayName(normalizedFamily);
3785
+ const googleFaces = canonicalFamily ? await fetchFamilyFaces(canonicalFamily, optional, options, fontText) : [];
3786
+ const supplementary = googleFaces.filter((face) => !coveredWeights.has(coverageKey(face.weight, face.style)) || !isBundledSubsetRange(face.unicodeRange));
3787
+ const runs = groupFacesBySource(supplementary).flatMap((group) => partitionWeightRuns(group, coveredWeights));
3788
+ const firstAppearance = (run) => Math.min(...run.map((face) => supplementary.indexOf(face)));
3789
+ for (const run of [...runs].sort((a, b) => firstAppearance(a) - firstAppearance(b))) {
3790
+ const first = run[0];
3791
+ const last = run[run.length - 1];
3792
+ if (!first || !last)
3793
+ continue;
3794
+ const weight = run.length > 1 ? `${first.weight} ${last.weight}` : first.weight;
3795
+ rules.push(buildFontFaceRule(emitFamily, first.dataUri, weight, first.style, first.unicodeRange));
3796
+ }
3797
+ rules.push(...bundledRules);
3798
+ return rules;
3799
+ }
3800
+ async function googleFamilyFaceRules(lookupFamily, emitFamily, optional, options, fontText) {
3801
+ const googleFaces = await fetchFamilyFaces(lookupFamily, optional, options, fontText);
3802
+ if (googleFaces.length === 0)
3803
+ return null;
3804
+ return googleFaces.map((face) => buildFontFaceRule(emitFamily, face.dataUri, face.weight, face.style, face.unicodeRange));
3805
+ }
3806
+ async function systemFamilyFaceRules(lookupFamily, emitFamily, log) {
3807
+ const variants = locateSystemFontVariants(lookupFamily);
3808
+ if (variants.length === 0)
3809
+ return null;
3810
+ const rules = [];
3811
+ let totalBytes = 0;
3812
+ for (const variant of variants) {
3813
+ const fontBuffer = readFileSync2(variant.path);
3814
+ totalBytes += fontBuffer.length;
3815
+ const dataUri = await fontToDataUri(fontBuffer, variant.format);
3816
+ rules.push(buildFontFaceRule(emitFamily, dataUri, variant.weight, variant.style));
3817
+ }
3818
+ if (totalBytes > SYSTEM_FONT_SIZE_LIMIT) {
3819
+ log.warn(`[Compiler] System font "${lookupFamily}" is large (${(totalBytes / 1024 / 1024).toFixed(1)} MB total across ${variants.length} variant(s)) \u2014 embedding anyway. Consider font subsetting for production.`);
3820
+ }
3821
+ log.info(`[Compiler] Embedded system font "${lookupFamily}" \u2014 ${variants.length} variant(s), ${(totalBytes / 1024).toFixed(0)} KB total`);
3822
+ return rules;
3823
+ }
3824
+ async function resolveFamilyFaceRules(normalizedFamily, lookupFamily, emitFamily, optional, options, fontText) {
3825
+ const canonicalKey = FONT_ALIASES[normalizedFamily];
3826
+ if (canonicalKey) {
3827
+ const canonical = CANONICAL_FONTS[canonicalKey];
3828
+ if (!canonical)
3829
+ return [];
3830
+ return bundledFamilyFaceRules(canonical, normalizedFamily, emitFamily, optional, options, fontText);
3831
+ }
3832
+ return await googleFamilyFaceRules(lookupFamily, emitFamily, optional, options, fontText) ?? (options.allowSystemFontCapture && !pageNamedThisFile(lookupFamily, options) ? await systemFamilyFaceRules(lookupFamily, emitFamily, options.log) : null);
3833
+ }
3834
+ function googleFontsUrlFamilies(rawUrl) {
3835
+ let url;
3836
+ try {
3837
+ url = new URL(rawUrl.trim(), "https://document.invalid/");
3838
+ } catch {
3839
+ return [];
3840
+ }
3841
+ if (url.hostname !== "fonts.googleapis.com")
3842
+ return [];
3843
+ if (url.pathname !== "/css" && url.pathname !== "/css2")
3844
+ return [];
3845
+ return url.searchParams.getAll("family").flatMap((value) => value.split("|")).map((entry) => (entry.split(":", 1)[0] ?? "").trim()).filter((family) => family.length > 0);
3846
+ }
3847
+ function importRuleUrl(params) {
3848
+ const trimmed = params.trim();
3849
+ if (trimmed.toLowerCase().startsWith("url(")) {
3850
+ const close = trimmed.indexOf(")");
3851
+ if (close < 0 || !isUnconditionalMedia(trimmed.slice(close + 1)))
3852
+ return null;
3853
+ return trimmed.slice(4, close).trim().replace(/^['"]/, "").replace(/['"]$/, "");
3854
+ }
3855
+ const quote = trimmed[0];
3856
+ if (quote !== '"' && quote !== "'")
3857
+ return null;
3858
+ const end = trimmed.indexOf(quote, 1);
3859
+ return end > 0 && isUnconditionalMedia(trimmed.slice(end + 1)) ? trimmed.slice(1, end) : null;
3860
+ }
3861
+ function fontFaceRuleFamily(rule) {
3862
+ let family;
3863
+ rule.walkDecls((decl) => {
3864
+ if (decl.prop.toLowerCase() === "font-family")
3865
+ family = parseFontFamilyValue(decl.value)[0];
3866
+ });
3867
+ return family;
3868
+ }
3869
+ function isUnconditionalMedia(media) {
3870
+ const normalized = (media ?? "").trim().toLowerCase();
3871
+ return normalized === "" || normalized === "all";
3872
+ }
3873
+ function isUnconditionalStylesheet(element) {
3874
+ const type = (element.getAttribute("type") ?? "").trim().toLowerCase();
3875
+ return isUnconditionalMedia(element.getAttribute("media")) && (type === "" || type === "text/css") && !(element.getAttribute("title") ?? "").trim() && !element.hasAttribute("disabled");
3876
+ }
3877
+ function declareStyleFontFamilies(styleEl, register) {
3878
+ if (!isUnconditionalStylesheet(styleEl))
3879
+ return;
3880
+ const root = parseCssRoot(styleEl.textContent ?? "");
3881
+ if (!root)
3882
+ return;
3883
+ root.walkAtRules((atRule) => {
3884
+ if (atRule.parent?.type !== "root")
3885
+ return;
3886
+ const name = atRule.name.toLowerCase();
3887
+ if (name === "import") {
3888
+ const url = importRuleUrl(atRule.params);
3889
+ if (url)
3890
+ for (const family2 of googleFontsUrlFamilies(url))
3891
+ register(family2);
3892
+ return;
3893
+ }
3894
+ if (name !== "font-face")
3895
+ return;
3896
+ const family = fontFaceRuleFamily(atRule);
3897
+ if (family)
3898
+ register(family, atRule);
3899
+ });
3900
+ }
3901
+ function declareLinkedFontFamilies(link, register) {
3902
+ const rel = (link.getAttribute("rel") ?? "").toLowerCase().split(/\s+/);
3903
+ if (!rel.includes("stylesheet") || rel.includes("alternate") || !isUnconditionalStylesheet(link))
3904
+ return;
3905
+ for (const family of googleFontsUrlFamilies(link.getAttribute("href") ?? ""))
3906
+ register(family);
3907
+ }
3908
+ function collectDeclaredFontFamilies(html) {
3909
+ const { document: document2 } = parseHTML(html);
3910
+ const declared = /* @__PURE__ */ new Map();
3911
+ const register = (family, fontFaceRule) => {
3912
+ const key = normalizeFamilyName(family);
3913
+ if (!key || GENERIC_FAMILIES.has(key))
3914
+ return;
3915
+ let entry = declared.get(key);
3916
+ if (!entry) {
3917
+ entry = { family, fontFaceRules: [] };
3918
+ declared.set(key, entry);
3919
+ }
3920
+ if (fontFaceRule)
3921
+ entry.fontFaceRules.push(fontFaceRule);
3922
+ };
3923
+ for (const styleEl of Array.from(document2.querySelectorAll("style"))) {
3924
+ declareStyleFontFamilies(styleEl, register);
3925
+ }
3926
+ for (const link of Array.from(document2.querySelectorAll("link[href]"))) {
3927
+ declareLinkedFontFamilies(link, register);
3928
+ }
3929
+ return declared;
3930
+ }
3931
+ function familySpellingKey(family) {
3932
+ return family.toLowerCase().replace(/[\s_+-]+/g, "");
3933
+ }
3934
+ function findDeclaredFamilyAlias(authoredFamily, declaredFamilies) {
3935
+ if (declaredFamilies.has(normalizeFamilyName(authoredFamily)))
3936
+ return null;
3937
+ const key = familySpellingKey(authoredFamily);
3938
+ if (!key)
3939
+ return null;
3940
+ let match = null;
3941
+ for (const entry of declaredFamilies.values()) {
3942
+ if (familySpellingKey(entry.family) !== key)
3943
+ continue;
3944
+ if (match)
3945
+ return null;
3946
+ match = entry;
3947
+ }
3948
+ return match;
3949
+ }
3950
+ async function resolveDeclaredFamilyAlias(authoredFamily, declaredFamilies, optional, options, fontText) {
3951
+ const declared = findDeclaredFamilyAlias(authoredFamily, declaredFamilies);
3952
+ if (!declared)
3953
+ return null;
3954
+ let rules;
3955
+ if (declared.fontFaceRules.length > 0) {
3956
+ rules = declared.fontFaceRules.map((rule) => {
3957
+ const renamed = rule.clone();
3958
+ renamed.walkDecls((decl) => {
3959
+ if (decl.prop.toLowerCase() === "font-family")
3960
+ decl.value = `"${authoredFamily}"`;
3961
+ });
3962
+ return renamed.toString();
3963
+ });
3964
+ } else {
3965
+ rules = await resolveFamilyFaceRules(normalizeFamilyName(declared.family), declared.family, authoredFamily, optional, options, fontText);
3966
+ }
3967
+ if (!rules)
3968
+ return null;
3969
+ options.log.warn(`[Compiler] font-family "${authoredFamily}" is not a known family; resolved it to "${declared.family}", which this document declares. Correct the authored font-family to "${declared.family}".`);
3970
+ return rules;
3971
+ }
3972
+ async function buildFontFaceCss(requestedFamilies, options, declaredFamilies, fontText) {
3973
+ const rules = [];
3974
+ const unresolved = [];
3975
+ const requiredFirst = [...requestedFamilies].sort(([, a], [, b]) => Number(a.optional) - Number(b.optional));
3976
+ for (const [normalizedFamily, { family: originalCaseFamily, optional }] of requiredFirst) {
3977
+ const familyRules = await resolveFamilyFaceRules(normalizedFamily, originalCaseFamily, originalCaseFamily, optional, options, fontText) ?? // Last, so a family that already resolves keeps exactly the faces it had.
3978
+ await resolveDeclaredFamilyAlias(originalCaseFamily, declaredFamilies(), optional, options, fontText);
3979
+ if (familyRules) {
3980
+ rules.push(...familyRules);
3981
+ continue;
3982
+ }
3983
+ unresolved.push(originalCaseFamily);
3984
+ }
3985
+ return {
3986
+ css: rules.join("\n\n").trim(),
3987
+ unresolved: unresolved.sort()
3988
+ };
3989
+ }
3990
+ function warnUnresolvedFonts(unresolved, log) {
3991
+ const mapped = Object.entries(FONT_ALIASES).reduce((acc, [alias, canonical]) => {
3992
+ const display = alias === canonical ? alias : `${alias} \u2192 ${canonical}`;
3993
+ if (!acc.includes(display))
3994
+ acc.push(display);
3995
+ return acc;
3996
+ }, []).sort();
3997
+ log.warn(`[Compiler] No deterministic font mapping for: ${unresolved.join(", ")}
3998
+ Mapped fonts: ${mapped.join(", ")}
3999
+ To fix, pick one:
4000
+ 1. Use a mapped font name instead (see list above)
4001
+ 2. Add a @font-face block in your HTML with a local or hosted font file
4002
+ 3. Install the font locally on the render machine (Docker: add to Dockerfile)
4003
+ 4. Add an alias to FONT_ALIAS_MAP in packages/core/src/fonts/aliases.ts (for contributors)
4004
+ Docs: https://hyperframes.heygen.com/docs/fonts`);
4005
+ }
4006
+ var lambdaFontCacheRoot;
4007
+ function resolveFontCacheRoot() {
4008
+ if (process.env.HYPERFRAMES_FONT_CACHE_DIR) {
4009
+ return process.env.HYPERFRAMES_FONT_CACHE_DIR;
4010
+ }
4011
+ if (process.env.AWS_LAMBDA_FUNCTION_NAME) {
4012
+ lambdaFontCacheRoot ??= mkdtempSync2(join2(tmpdir2(), "hyperframes-fonts-"));
4013
+ return lambdaFontCacheRoot;
4014
+ }
4015
+ return join2(homedir2(), ".cache", "hyperframes", "fonts");
4016
+ }
4017
+ var WOFF2_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
4018
+ function fontSlug(familyName) {
4019
+ return familyName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
4020
+ }
4021
+ var ephemeralFontCacheRoot;
4022
+ function fontCacheDir(slug) {
4023
+ const dir = join2(resolveFontCacheRoot(), slug);
4024
+ if (!existsSync2(dir)) {
4025
+ try {
4026
+ mkdirSync2(dir, { recursive: true });
4027
+ } catch {
4028
+ const firstFallback = ephemeralFontCacheRoot === void 0;
4029
+ ephemeralFontCacheRoot ??= mkdtempSync2(join2(tmpdir2(), "hyperframes-fonts-"));
4030
+ const fallback = join2(ephemeralFontCacheRoot, slug);
4031
+ mkdirSync2(fallback, { recursive: true });
4032
+ if (firstFallback) {
4033
+ STDERR_LOGGER.warn(`Font cache directory is unwritable (${dir}). Using temporary fallback \u2014 fonts will re-download each run. Fix with: chmod 755 ${resolveFontCacheRoot()}`);
4034
+ }
4035
+ return fallback;
4036
+ }
4037
+ }
4038
+ return dir;
4039
+ }
4040
+ function subsetToken(woff2Url) {
4041
+ return createHash2("sha1").update(woff2Url).digest("hex").slice(0, 12);
4042
+ }
4043
+ function cachedWoff2Path(slug, weight, style, subset) {
4044
+ return join2(fontCacheDir(slug), `${weight}-${style}-${subset}.woff2`);
4045
+ }
4046
+ var FONT_FETCH_FAILED = "FONT_FETCH_FAILED";
4047
+ var FONT_FETCH_UNAVAILABLE = "FONT_FETCH_UNAVAILABLE";
4048
+ var FontFetchError = class extends Error {
4049
+ code;
4050
+ familyName;
4051
+ url;
4052
+ cause;
4053
+ /** Unlike `message`, holds no URLs: safe for callers that must not echo resolver output. */
4054
+ unresolvedFamilies;
4055
+ constructor(familyName, url, message, cause, code = FONT_FETCH_FAILED, unresolvedFamilies = []) {
4056
+ super(message);
4057
+ this.name = "FontFetchError";
4058
+ this.code = code;
4059
+ this.familyName = familyName;
4060
+ this.url = url;
4061
+ this.cause = cause;
4062
+ this.unresolvedFamilies = unresolvedFamilies;
4063
+ }
4064
+ };
4065
+ var FontFetchUnavailableError = class extends FontFetchError {
4066
+ constructor(familyName, url, message, cause) {
4067
+ super(familyName, url, message, cause, FONT_FETCH_UNAVAILABLE);
4068
+ this.name = "FontFetchUnavailableError";
4069
+ }
4070
+ };
4071
+ var DEFAULT_FONT_FETCH_RETRY_POLICY = {
4072
+ maxAttempts: 2,
4073
+ attemptTimeoutMs: 8e3,
4074
+ maxElapsedMs: 2e4,
4075
+ baseDelayMs: 250
4076
+ };
4077
+ function fontFetchError(familyName, url, what, cause, unavailable = false) {
4078
+ const reason = "status" in cause ? `returned HTTP ${cause.status}` : `failed: ${cause.error instanceof Error ? cause.error.message : String(cause.error)}`;
4079
+ const message = `[deterministicFonts] ${what} fetch for ${JSON.stringify(familyName)} ${reason}. Distributed renders require deterministic fonts; system-font fallback would produce non-byte-identical output.`;
4080
+ const errorCause = "error" in cause ? cause.error : void 0;
4081
+ return unavailable ? new FontFetchUnavailableError(familyName, url, message, errorCause) : new FontFetchError(familyName, url, message, errorCause);
4082
+ }
4083
+ function isRetryableFontFetchStatus(status) {
4084
+ return status === 408 || status === 425 || status === 429 || status >= 500;
4085
+ }
4086
+ function retryAfterMs(response) {
4087
+ const value = response.headers.get("retry-after");
4088
+ if (value === null)
4089
+ return null;
4090
+ const seconds = Number(value);
4091
+ if (Number.isFinite(seconds))
4092
+ return Math.max(0, seconds * 1e3);
4093
+ const date = Date.parse(value);
4094
+ return Number.isNaN(date) ? null : Math.max(0, date - Date.now());
4095
+ }
4096
+ function callerAbortReason(signal) {
4097
+ return signal.reason ?? new DOMException("Font fetch cancelled", "AbortError");
4098
+ }
4099
+ function throwIfCallerAborted(signal) {
4100
+ if (signal?.aborted)
4101
+ throw callerAbortReason(signal);
4102
+ }
4103
+ async function waitForFontFetchRetry(delayMs, signal) {
4104
+ throwIfCallerAborted(signal);
4105
+ if (delayMs <= 0)
4106
+ return;
4107
+ await new Promise((resolve, reject) => {
4108
+ const timeout = setTimeout(() => {
4109
+ signal?.removeEventListener("abort", onAbort);
4110
+ resolve();
4111
+ }, delayMs);
4112
+ const onAbort = () => {
4113
+ clearTimeout(timeout);
4114
+ reject(signal ? callerAbortReason(signal) : new DOMException("Cancelled", "AbortError"));
4115
+ };
4116
+ signal?.addEventListener("abort", onAbort, { once: true });
4117
+ if (signal?.aborted)
4118
+ onAbort();
4119
+ });
4120
+ }
4121
+ function retryDelayMs(response, attempt, baseDelayMs) {
4122
+ const requestedDelay = response ? retryAfterMs(response) : null;
4123
+ if (requestedDelay !== null)
4124
+ return requestedDelay;
4125
+ const ceiling = baseDelayMs * 2 ** attempt;
4126
+ return Math.floor(Math.random() * (ceiling + 1));
4127
+ }
4128
+ function cancelResponseBody(response) {
4129
+ try {
4130
+ void response.body?.cancel().catch(() => {
4131
+ });
4132
+ } catch {
4133
+ }
4134
+ }
4135
+ async function runFontFetchAttempt(url, init, readBody, options, remainingMs) {
4136
+ const timeoutSignal = AbortSignal.timeout(Math.max(1, Math.min(options.retryPolicy.attemptTimeoutMs, remainingMs)));
4137
+ const signal = options.abortSignal ? AbortSignal.any([options.abortSignal, timeoutSignal]) : timeoutSignal;
4138
+ let response;
4139
+ try {
4140
+ response = await options.fetchImpl(url, { ...init, signal });
4141
+ if (!isRetryableFontFetchStatus(response.status)) {
4142
+ if (!response.ok)
4143
+ return { completed: true, result: { ok: false, response } };
4144
+ const body = await readBody(response);
4145
+ return { completed: true, result: { ok: true, response, body } };
4146
+ }
4147
+ cancelResponseBody(response);
4148
+ return { completed: false, response, cause: { status: response.status } };
4149
+ } catch (error) {
4150
+ throwIfCallerAborted(options.abortSignal);
4151
+ return { completed: false, response, cause: { error } };
4152
+ }
4153
+ }
4154
+ async function fetchFontResource(url, init, readBody, familyName, what, options) {
4155
+ if (!options.failClosedFontFetch) {
4156
+ const response = await options.fetchImpl(url, { ...init, signal: options.abortSignal });
4157
+ if (!response.ok)
4158
+ return { ok: false, response };
4159
+ return { ok: true, response, body: await readBody(response) };
4160
+ }
4161
+ let lastCause = {
4162
+ error: new DOMException("Font fetch budget exhausted", "TimeoutError")
4163
+ };
4164
+ for (let attempt = 0; attempt < options.retryPolicy.maxAttempts; attempt += 1) {
4165
+ throwIfCallerAborted(options.abortSignal);
4166
+ const remainingMs = options.retryDeadlineMs - Date.now();
4167
+ if (remainingMs <= 0)
4168
+ break;
4169
+ const attemptResult = await runFontFetchAttempt(url, init, readBody, options, remainingMs);
4170
+ if (attemptResult.completed)
4171
+ return attemptResult.result;
4172
+ lastCause = attemptResult.cause;
4173
+ if (attempt + 1 >= options.retryPolicy.maxAttempts)
4174
+ break;
4175
+ const delayMs = retryDelayMs(attemptResult.response, attempt, options.retryPolicy.baseDelayMs);
4176
+ if (delayMs >= options.retryDeadlineMs - Date.now())
4177
+ break;
4178
+ await waitForFontFetchRetry(delayMs, options.abortSignal);
4179
+ }
4180
+ throw fontFetchError(familyName, url, what, lastCause, true);
4181
+ }
4182
+ async function ensureWoff2DataUri(cachePath, woff2Url, familyName, weight, style, options) {
4183
+ try {
4184
+ return `data:font/woff2;base64,${readFileSync2(cachePath).toString("base64")}`;
4185
+ } catch {
4186
+ }
4187
+ const woff2What = `Google Fonts woff2 (${weight}/${style})`;
4188
+ try {
4189
+ const fontResult = await fetchFontResource(woff2Url, void 0, (response) => response.arrayBuffer(), familyName, woff2What, options);
4190
+ if (!fontResult.ok)
4191
+ return null;
4192
+ writeFileSync2(cachePath, Buffer.from(fontResult.body), { flag: "wx", mode: 420 });
4193
+ } catch (err) {
4194
+ throwIfCallerAborted(options.abortSignal);
4195
+ if (err instanceof FontFetchError)
4196
+ throw err;
4197
+ if (err.code === "EEXIST") {
4198
+ } else if (options.failClosedFontFetch) {
4199
+ throw fontFetchError(familyName, woff2Url, woff2What, { error: err });
4200
+ } else {
4201
+ return null;
4202
+ }
4203
+ }
4204
+ return `data:font/woff2;base64,${readFileSync2(cachePath).toString("base64")}`;
4205
+ }
4206
+ var googleFontCssCache = /* @__PURE__ */ new Map();
4207
+ function _clearGoogleFontCssCacheForTests() {
4208
+ googleFontCssCache.clear();
4209
+ }
4210
+ function raceAgainstAbort(promise, signal) {
4211
+ if (!signal)
4212
+ return promise;
4213
+ if (signal.aborted)
4214
+ return Promise.reject(callerAbortReason(signal));
4215
+ return new Promise((resolve, reject) => {
4216
+ const onAbort = () => reject(callerAbortReason(signal));
4217
+ signal.addEventListener("abort", onAbort, { once: true });
4218
+ promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
4219
+ });
4220
+ }
4221
+ function fetchGoogleFontCss(url, familyName, options) {
4222
+ const mode = options.failClosedFontFetch ? `closed${options.retryPolicy.maxAttempts}` : "open";
4223
+ const key = `${mode}:${url}`;
4224
+ let shared = googleFontCssCache.get(key);
4225
+ if (!shared) {
4226
+ shared = fetchFontResource(url, { headers: { "User-Agent": WOFF2_USER_AGENT } }, (response) => response.text(), familyName, "Google Fonts CSS", { ...options, abortSignal: void 0 }).then((result) => {
4227
+ if (result.ok)
4228
+ return { ok: true, body: result.body };
4229
+ if (isRetryableFontFetchStatus(result.response.status))
4230
+ googleFontCssCache.delete(key);
4231
+ return { ok: false };
4232
+ });
4233
+ googleFontCssCache.set(key, shared);
4234
+ shared.catch(() => googleFontCssCache.delete(key));
4235
+ }
4236
+ return raceAgainstAbort(shared, options.abortSignal);
4237
+ }
4238
+ function pageNamedThisFile(familyName, options) {
4239
+ return options.authoredStylesheets.has(normalizeFamilyName(familyName.replace(/\+/g, " ")));
4240
+ }
4241
+ function declaredFaceFamily(block) {
4242
+ const found = /font-family:\s*['"]([^'"]+)['"]/i.exec(block);
4243
+ return found?.[1];
4244
+ }
4245
+ async function fetchGoogleFont(familyName, options, fontText) {
4246
+ const googleFamilyName = familyName.replace(/\+/g, " ");
4247
+ const encodedFamily = encodeURIComponent(googleFamilyName);
4248
+ const textParam = fontText ? `&text=${encodeURIComponent(fontText)}` : "";
4249
+ const normalizedFamily = normalizeFamilyName(googleFamilyName);
4250
+ const authoredStylesheet = FONT_ALIASES[normalizedFamily] ? void 0 : options.authoredStylesheets.get(normalizedFamily);
4251
+ const urls = authoredStylesheet ? authoredStylesheet.map((url) => withPageText(url, fontText)) : [
4252
+ `https://fonts.googleapis.com/css2?family=${encodedFamily}:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,400;1,700${textParam}`
4253
+ ];
4254
+ const faces = [];
4255
+ for (const url of urls) {
4256
+ faces.push(...await fetchGoogleFontStylesheet(googleFamilyName, url, options));
4257
+ }
4258
+ return faces;
4259
+ }
4260
+ async function readGoogleFontStylesheet(familyName, url, options) {
4261
+ try {
4262
+ const cssResult = await fetchGoogleFontCss(url, familyName, options);
4263
+ if (!cssResult.ok) {
4264
+ return null;
4265
+ }
4266
+ return cssResult.body;
4267
+ } catch (err) {
4268
+ throwIfCallerAborted(options.abortSignal);
4269
+ if (err instanceof FontFetchError)
4270
+ throw err;
4271
+ if (options.failClosedFontFetch) {
4272
+ throw fontFetchError(familyName, url, "Google Fonts CSS", { error: err });
4273
+ }
4274
+ return null;
4275
+ }
4276
+ }
4277
+ function parseGoogleFontSource(match, familyName) {
4278
+ const declared = declaredFaceFamily(match[0]);
4279
+ if (declared && normalizeFamilyName(declared) !== normalizeFamilyName(familyName))
4280
+ return null;
4281
+ const url = match[3];
4282
+ if (!url)
4283
+ return null;
4284
+ return {
4285
+ style: match[1] || "normal",
4286
+ weight: (match[2] || "400").replace(/\s+/g, " "),
4287
+ url,
4288
+ unicodeRange: match[4]?.trim() || void 0
4289
+ };
4290
+ }
4291
+ async function fetchGoogleFontStylesheet(familyName, url, options) {
4292
+ const slug = fontSlug(familyName);
4293
+ const cssText = await readGoogleFontStylesheet(familyName, url, options);
4294
+ if (!cssText)
4295
+ return [];
4296
+ const faceRegex = /@font-face\s*\{[^}]*font-style:\s*(normal|italic|oblique(?:\s+-?\d+(?:\.\d+)?deg){0,2})\s*;[^}]*font-weight:\s*(\d+(?:\s+\d+)?)[^}]*src:\s*url\(([^)]+)\)\s*format\(['"]woff2['"]\)(?:[^}]*?unicode-range:\s*([^;}]+))?[^}]*\}/gi;
4297
+ const faces = [];
4298
+ for (const match of cssText.matchAll(faceRegex)) {
4299
+ const source = parseGoogleFontSource(match, familyName);
4300
+ if (!source)
4301
+ continue;
4302
+ const { weight, style, unicodeRange, url: woff2Url } = source;
4303
+ const cachePath = cachedWoff2Path(slug, weight, style, subsetToken(woff2Url));
4304
+ const dataUri = await ensureWoff2DataUri(cachePath, woff2Url, familyName, weight, style, options);
4305
+ if (dataUri)
4306
+ faces.push({ weight, style, dataUri, unicodeRange });
4307
+ }
4308
+ if (faces.length > 0) {
4309
+ options.log.info(`[Compiler] Fetched ${faces.length} font face(s) for "${familyName}" from Google Fonts (cached to ${fontCacheDir(slug)})`);
4310
+ }
4311
+ return faces;
4312
+ }
4313
+ async function fetchFamilyFaces(familyName, optional, options, fontText) {
4314
+ if (!optional)
4315
+ return fetchGoogleFont(familyName, options, fontText);
4316
+ const maxAttempts = Math.min(options.retryPolicy.maxAttempts, 2);
4317
+ try {
4318
+ return await fetchGoogleFont(familyName, { ...options, retryPolicy: { ...options.retryPolicy, maxAttempts } }, fontText);
4319
+ } catch (err) {
4320
+ if (!(err instanceof FontFetchUnavailableError))
4321
+ throw err;
4322
+ options.log.warn(`[Compiler] Optional font "${familyName}" is unavailable, rendering its fallback: ${err.message}`);
4323
+ return [];
4324
+ }
4325
+ }
4326
+ var STDERR_LOGGER = {
4327
+ warn: (message) => console.warn(`[WARN] ${message}`),
4328
+ info: (message) => console.error(`[INFO] ${message}`)
4329
+ };
4330
+ var GOOGLE_FONTS_TEXT_MAX_ENCODED_LENGTH = 1700;
4331
+ var SMALL_TO_FULL_KANA = /* @__PURE__ */ new Map([
4332
+ ["\u3041", "\u3042"],
4333
+ ["\u3043", "\u3044"],
4334
+ ["\u3045", "\u3046"],
4335
+ ["\u3047", "\u3048"],
4336
+ ["\u3049", "\u304A"],
4337
+ ["\u3063", "\u3064"],
4338
+ ["\u3083", "\u3084"],
4339
+ ["\u3085", "\u3086"],
4340
+ ["\u3087", "\u3088"],
4341
+ ["\u308E", "\u308F"],
4342
+ ["\u30A1", "\u30A2"],
4343
+ ["\u30A3", "\u30A4"],
4344
+ ["\u30A5", "\u30A6"],
4345
+ ["\u30A7", "\u30A8"],
4346
+ ["\u30A9", "\u30AA"],
4347
+ ["\u30C3", "\u30C4"],
4348
+ ["\u30E3", "\u30E4"],
4349
+ ["\u30E5", "\u30E6"],
4350
+ ["\u30E7", "\u30E8"],
4351
+ ["\u30EE", "\u30EF"],
4352
+ ["\u30F5", "\u30AB"],
4353
+ ["\u30F6", "\u30B1"]
4354
+ ]);
4355
+ function collectLangAttributes(document2) {
4356
+ const locales = /* @__PURE__ */ new Set();
4357
+ for (const element of document2.querySelectorAll("[lang]")) {
4358
+ const lang = element.getAttribute("lang");
4359
+ if (!lang)
4360
+ continue;
4361
+ const primary = lang.split("-")[0].toLowerCase();
4362
+ try {
4363
+ Intl.getCanonicalLocales(primary);
4364
+ locales.add(primary);
4365
+ } catch {
4366
+ }
4367
+ }
4368
+ return locales;
4369
+ }
4370
+ function addCaseClosure(out, character, locales) {
4371
+ out.add(character);
4372
+ for (const variant of `${character.toUpperCase()}${character.toLowerCase()}`) {
4373
+ out.add(variant);
4374
+ }
4375
+ for (const locale of locales) {
4376
+ for (const variant of `${character.toLocaleUpperCase(locale)}${character.toLocaleLowerCase(locale)}`) {
4377
+ out.add(variant);
4378
+ }
4379
+ }
4380
+ }
4381
+ function addFullwidthVariants(chars) {
4382
+ for (const character of [...chars]) {
4383
+ const code = character.codePointAt(0) ?? 0;
4384
+ if (code >= 33 && code <= 126) {
4385
+ chars.add(String.fromCodePoint(code + 65248));
4386
+ }
4387
+ }
4388
+ }
4389
+ function addFullSizeKanaVariants(chars) {
4390
+ for (const character of [...chars]) {
4391
+ const full = SMALL_TO_FULL_KANA.get(character);
4392
+ if (full)
4393
+ chars.add(full);
4394
+ }
4395
+ }
4396
+ var FULL_WIDTH_KEYWORD_RE = /\bfull-width\b/;
4397
+ var FULL_SIZE_KANA_KEYWORD_RE = /\bfull-size-kana\b/;
4398
+ var DECLARATION_BOUNDARY_RE = /[;{}]/;
4399
+ function skipWhitespace(s, pos) {
4400
+ while (pos < s.length && " \n\r\f\v".includes(s[pos]))
4401
+ pos += 1;
4402
+ return pos;
4403
+ }
4404
+ function findDeclarationEnd(s, pos) {
4405
+ const match = DECLARATION_BOUNDARY_RE.exec(s.slice(pos));
4406
+ return match ? pos + match.index : s.length;
4407
+ }
4408
+ function hasTextTransformKeyword(html, keyword) {
4409
+ const haystack = html.toLowerCase();
4410
+ const property = "text-transform";
4411
+ let from = 0;
4412
+ for (; ; ) {
4413
+ const at = haystack.indexOf(property, from);
4414
+ if (at === -1)
4415
+ return false;
4416
+ const afterProp = skipWhitespace(haystack, at + property.length);
4417
+ if (haystack[afterProp] !== ":") {
4418
+ from = at + property.length;
4419
+ continue;
4420
+ }
4421
+ const end = findDeclarationEnd(haystack, afterProp + 1);
4422
+ if (keyword.test(haystack.slice(afterProp + 1, end)))
4423
+ return true;
4424
+ from = end;
4425
+ }
4426
+ }
4427
+ function extractGoogleFontsText(html) {
4428
+ const { document: document2 } = parseHTML(html);
4429
+ const decodedBodyText = document2.body?.textContent ?? "";
4430
+ const locales = collectLangAttributes(document2);
4431
+ const uniqueCharacters = /* @__PURE__ */ new Set();
4432
+ for (const character of /* @__PURE__ */ new Set([...Array.from(html), ...Array.from(decodedBodyText)])) {
4433
+ addCaseClosure(uniqueCharacters, character, locales);
4434
+ }
4435
+ if (hasTextTransformKeyword(html, FULL_WIDTH_KEYWORD_RE))
4436
+ addFullwidthVariants(uniqueCharacters);
4437
+ if (hasTextTransformKeyword(html, FULL_SIZE_KANA_KEYWORD_RE))
4438
+ addFullSizeKanaVariants(uniqueCharacters);
4439
+ const fontText = [...uniqueCharacters].join("");
4440
+ return encodeURIComponent(fontText).length <= GOOGLE_FONTS_TEXT_MAX_ENCODED_LENGTH ? fontText : void 0;
4441
+ }
4442
+ function resolveFontFetchRetryPolicy(configured) {
4443
+ return {
4444
+ maxAttempts: Math.max(1, Math.floor(configured?.maxAttempts ?? DEFAULT_FONT_FETCH_RETRY_POLICY.maxAttempts)),
4445
+ attemptTimeoutMs: Math.max(1, configured?.attemptTimeoutMs ?? DEFAULT_FONT_FETCH_RETRY_POLICY.attemptTimeoutMs),
4446
+ maxElapsedMs: Math.max(1, configured?.maxElapsedMs ?? DEFAULT_FONT_FETCH_RETRY_POLICY.maxElapsedMs),
4447
+ baseDelayMs: Math.max(0, configured?.baseDelayMs ?? DEFAULT_FONT_FETCH_RETRY_POLICY.baseDelayMs)
4448
+ };
4449
+ }
4450
+ function placeAfterEveryStylesheet(document2, head, styleEl) {
4451
+ const stylesheets = document2.querySelectorAll("style, link[rel~=stylesheet]");
4452
+ const lastStylesheet = stylesheets[stylesheets.length - 1];
4453
+ if (lastStylesheet)
4454
+ lastStylesheet.after(styleEl);
4455
+ else
4456
+ head.appendChild(styleEl);
4457
+ }
4458
+ async function injectDeterministicFontFaces(html, options = {}) {
4459
+ const failClosedFontFetch = options.failClosedFontFetch === true;
4460
+ const fetchImpl = options.fetchImpl ?? fetch;
4461
+ const allowSystemFontCapture = options.allowSystemFontCapture !== false;
4462
+ const retryPolicy = resolveFontFetchRetryPolicy(options.fontFetchRetryPolicy);
4463
+ const fetchOptions = {
4464
+ log: options.logger ?? STDERR_LOGGER,
4465
+ failClosedFontFetch,
4466
+ fetchImpl,
4467
+ allowSystemFontCapture,
4468
+ abortSignal: options.abortSignal,
4469
+ retryPolicy,
4470
+ retryDeadlineMs: Date.now() + retryPolicy.maxElapsedMs,
4471
+ authoredStylesheets: authoredGoogleFontStylesheets(html)
4472
+ };
4473
+ const existingFaces = extractExistingFontFaces(html);
4474
+ const requestedFamilies = extractRequestedFontFamilies(html);
4475
+ const pendingFamilies = /* @__PURE__ */ new Map();
4476
+ for (const [normalizedFamily, requested] of requestedFamilies) {
4477
+ if (!existingFaces.has(normalizedFamily)) {
4478
+ pendingFamilies.set(normalizedFamily, requested);
4479
+ }
4480
+ }
4481
+ if (pendingFamilies.size === 0) {
4482
+ return html;
4483
+ }
4484
+ let declaredFamilies;
4485
+ const { css, unresolved } = await buildFontFaceCss(pendingFamilies, fetchOptions, () => declaredFamilies ??= collectDeclaredFontFamilies(html), extractGoogleFontsText(html));
4486
+ const required = unresolved.filter((family) => !pendingFamilies.get(family.toLowerCase())?.optional);
4487
+ if (required.length > 0 && options.failClosedFontFetch) {
4488
+ throw new FontFetchError(required.join(", "), "", `[Compiler] Unresolved fonts in fail-closed mode: ${required.join(", ")}. Distributed renders require all fonts to be resolvable.`, void 0, FONT_FETCH_FAILED, unresolved);
4489
+ }
4490
+ if (!css) {
4491
+ if (unresolved.length > 0) {
4492
+ warnUnresolvedFonts(unresolved, fetchOptions.log);
4493
+ }
4494
+ return html;
4495
+ }
4496
+ const { document: document2 } = parseHTML(html);
4497
+ const head = document2.querySelector("head");
4498
+ if (!head) {
4499
+ return html;
4500
+ }
4501
+ const styleEl = document2.createElement("style");
4502
+ styleEl.setAttribute("data-hyperframes-deterministic-fonts", "true");
4503
+ styleEl.textContent = css;
4504
+ placeAfterEveryStylesheet(document2, head, styleEl);
4505
+ fetchOptions.log.info(`[Compiler] Injected deterministic @font-face rules for ${pendingFamilies.size - unresolved.length} requested font families`);
4506
+ if (unresolved.length > 0) {
4507
+ warnUnresolvedFonts(unresolved, fetchOptions.log);
4508
+ }
4509
+ return document2.toString();
4510
+ }
4511
+
3203
4512
  export {
3204
- compressToWoff2,
3205
- fontToDataUri
4513
+ fontToDataUri,
4514
+ GENERIC_FAMILIES,
4515
+ parseFontFamilyValue,
4516
+ normalizeSystemFontPrimaryFamilies,
4517
+ collectFontFamilyCustomProperties,
4518
+ resolveFontFamilyDeclarationFamilies,
4519
+ iterateFontFamilyDeclarations,
4520
+ FONT_ALIASES,
4521
+ fontFormatHint,
4522
+ FONT_FETCH_FAILED,
4523
+ FONT_FETCH_UNAVAILABLE,
4524
+ FontFetchError,
4525
+ FontFetchUnavailableError,
4526
+ _clearGoogleFontCssCacheForTests,
4527
+ injectDeterministicFontFaces
3206
4528
  };