hyperframes 0.8.181 → 0.8.184

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 (218) hide show
  1. package/bin/hyperframes.mjs +3 -1
  2. package/dist/{add-AWQ2EGBU.js → add-5ZTD3MUZ.js} +22 -22
  3. package/dist/{animatedGifPrep-SENXJ7TN.js → animatedGifPrep-AGASIWVA.js} +14 -13
  4. package/dist/api.js +29 -28
  5. package/dist/{asset-XBH3MN3Y.js → asset-LSTEMPIB.js} +3 -3
  6. package/dist/{assetCataloger-KH4X2CRO.js → assetCataloger-BKBMNL3A.js} +16 -13
  7. package/dist/{audio-Q7I473UN.js → audio-KGK44XE4.js} +31 -30
  8. package/dist/{auth-I7CBTEUS.js → auth-MJWKYK77.js} +4 -4
  9. package/dist/{autoUpdate-KY5TLSN7.js → autoUpdate-2GGDKYR2.js} +2 -2
  10. package/dist/{backgroundChecks-4Q3VVK5W.js → backgroundChecks-7GRX5VQY.js} +3 -3
  11. package/dist/backgroundChecksWorker.js +3 -3
  12. package/dist/{batchRender-3AYDQRBI.js → batchRender-BCV2LELR.js} +7 -7
  13. package/dist/{beats-P4LENNVT.js → beats-7BCFRBAL.js} +12 -12
  14. package/dist/{benchmark-AXOAECN3.js → benchmark-2R6CN5MQ.js} +24 -24
  15. package/dist/{browser-XZKB5HGJ.js → browser-6AFGOI7G.js} +20 -20
  16. package/dist/{bundleWithLocalizedFonts-2WCR57IN.js → bundleWithLocalizedFonts-2EGPA5ZO.js} +2 -2
  17. package/dist/{capture-NWARHRHR.js → capture-HA2UHLIU.js} +8 -5
  18. package/dist/{capture-4RN4APDF.js → capture-HOIAW6MO.js} +50 -41
  19. package/dist/{captureCompositionFrame-QOF5BRF4.js → captureCompositionFrame-4236FVWJ.js} +22 -20
  20. package/dist/{captureTimeout-K2JNVVCI.js → captureTimeout-RIMG6IYH.js} +1 -1
  21. package/dist/{catalog-CRPOPVD5.js → catalog-A6CZAWW4.js} +42 -42
  22. package/dist/{catch-up-MHWGUQ42.js → catch-up-TNA4DJOA.js} +15 -15
  23. package/dist/check-5E5U2EQZ.js +117 -0
  24. package/dist/checkBrowser-FNINXBMZ.js +118 -0
  25. package/dist/{chunk-OJTDUF4W.js → chunk-2PVE4WS2.js} +1 -1
  26. package/dist/{checkBrowser-VES2NF3K.js → chunk-2ULXZ4PG.js} +187 -114
  27. package/dist/{chunk-WITHUCCB.js → chunk-2XA7KXQO.js} +2 -2
  28. package/dist/{chunk-HODBPO4A.js → chunk-3MDJIWOV.js} +626 -328
  29. package/dist/{chunk-JBCU6NR5.js → chunk-3QVMJ7GX.js} +56 -55
  30. package/dist/{chunk-OR5WCFWZ.js → chunk-3TXNNBI2.js} +2 -2
  31. package/dist/{chunk-IZM562YJ.js → chunk-4KKKBEP3.js} +4 -4
  32. package/dist/{chunk-XEWHUPFS.js → chunk-4N4P3ZWL.js} +2 -2
  33. package/dist/{chunk-537IYAEE.js → chunk-4UMPIPCQ.js} +9 -1
  34. package/dist/{chunk-J4L5TPLS.js → chunk-5UDRPPN2.js} +48 -6
  35. package/dist/{chunk-L3YOGIPG.js → chunk-66LREXGS.js} +1 -1
  36. package/dist/{chunk-6I7V6Q7F.js → chunk-74JMKLVU.js} +103 -20
  37. package/dist/{chunk-3PULCIY5.js → chunk-7HJALUIO.js} +5 -5
  38. package/dist/{chunk-GZVP4TWN.js → chunk-7JAD4MEW.js} +1 -1
  39. package/dist/{chunk-NAMGMP3R.js → chunk-7KVVNF7B.js} +1 -1
  40. package/dist/{chunk-TO6JNTML.js → chunk-7MB2ZAQF.js} +13 -8
  41. package/dist/{chunk-55IG255C.js → chunk-7NRI7HYX.js} +1 -1
  42. package/dist/{chunk-VAJIPSN6.js → chunk-ABZYYUUH.js} +1 -1
  43. package/dist/{chunk-GK2FIKIQ.js → chunk-ADDHW2FS.js} +214 -37
  44. package/dist/{chunk-ZUOSILUE.js → chunk-BCHSTE7U.js} +1 -1
  45. package/dist/{chunk-AOWDVB2T.js → chunk-CXABAXB3.js} +1 -1
  46. package/dist/{chunk-E24HSMZV.js → chunk-CYBXXSC4.js} +65 -14
  47. package/dist/{chunk-NZZYJOMJ.js → chunk-CZUAX3BV.js} +6 -5
  48. package/dist/{chunk-GV5R5B7Q.js → chunk-DGECWPTL.js} +1 -1
  49. package/dist/{chunk-BRHI7EIA.js → chunk-DKPNNYBZ.js} +4 -0
  50. package/dist/{chunk-UIDZRCBG.js → chunk-DWUGKEHR.js} +1 -1
  51. package/dist/{chunk-D3ZWBHZP.js → chunk-DYAPHNV6.js} +3 -2
  52. package/dist/{chunk-T7UHTUUE.js → chunk-E32ZQ2WO.js} +9 -9
  53. package/dist/{chunk-RGHQID2E.js → chunk-EHSBBBR2.js} +5 -3
  54. package/dist/{chunk-DJJ7SK4F.js → chunk-EM7C7H7S.js} +8 -8
  55. package/dist/{chunk-XSRUVMXK.js → chunk-ES426XRJ.js} +4 -4
  56. package/dist/chunk-FG4BBFZI.js +122 -0
  57. package/dist/{chunk-JMFJI26U.js → chunk-FGJEUV4S.js} +2 -2
  58. package/dist/{check-DJCUOUDI.js → chunk-GDEV42FA.js} +198 -142
  59. package/dist/{chunk-RXQHSGAQ.js → chunk-HR23IKLZ.js} +2 -2
  60. package/dist/{chunk-KMAAOHLO.js → chunk-HRPWRZGF.js} +1 -1
  61. package/dist/{chunk-UPFUC4DB.js → chunk-HTIUNMTP.js} +1 -1
  62. package/dist/{chunk-V2UNZ7QP.js → chunk-ICJEAFPD.js} +25 -14
  63. package/dist/{chunk-EOADENKV.js → chunk-IWTNTUPQ.js} +16 -14
  64. package/dist/chunk-IZCW2NKT.js +29 -0
  65. package/dist/{chunk-XFCLWWL3.js → chunk-J36JVBLR.js} +11 -4
  66. package/dist/{chunk-LBQFLUIY.js → chunk-J3QM7AHP.js} +9 -49
  67. package/dist/{chunk-DUN63LHY.js → chunk-JB6ARMQS.js} +2 -2
  68. package/dist/{chunk-5E5EYPZO.js → chunk-JIUTXKWA.js} +7 -7
  69. package/dist/{chunk-YJDB4FLT.js → chunk-JQYJXUJW.js} +1 -1
  70. package/dist/{chunk-PBN6GBJE.js → chunk-JVBRJMOP.js} +14 -1
  71. package/dist/{chunk-RHWJKSGH.js → chunk-KANNRHY7.js} +1 -1
  72. package/dist/{chunk-IL3GTCQY.js → chunk-KN46TGHV.js} +72 -140
  73. package/dist/{chunk-OFY6JTZC.js → chunk-LLC4CS6I.js} +2 -2
  74. package/dist/{chunk-OJDXABUF.js → chunk-LRGYPXHF.js} +2 -2
  75. package/dist/{chunk-GNE57O7U.js → chunk-MFAETWIF.js} +1 -1
  76. package/dist/{chunk-32GKETR7.js → chunk-MMQ52NIZ.js} +74 -0
  77. package/dist/chunk-MOEGCMKI.js +180 -0
  78. package/dist/{chunk-E6RPWJVC.js → chunk-NAUKCKCJ.js} +5 -1
  79. package/dist/{chunk-B4VT6YU2.js → chunk-OLZENUPI.js} +50 -7
  80. package/dist/{chunk-H4YZZ2VQ.js → chunk-PFD25NAC.js} +1 -1
  81. package/dist/{chunk-XH262OWN.js → chunk-PFYWVUHA.js} +1 -1
  82. package/dist/{chunk-S3RYRF3W.js → chunk-PSKEKWVU.js} +1 -1
  83. package/dist/{chunk-N5SXQCJL.js → chunk-Q33P4EAC.js} +3 -3
  84. package/dist/{chunk-3DKN5WKL.js → chunk-QH3T4WI2.js} +77 -6
  85. package/dist/{chunk-O5AE7HOP.js → chunk-QL2PNIXC.js} +37 -33
  86. package/dist/{chunk-WUUCEFL5.js → chunk-R2BFTECP.js} +5 -5
  87. package/dist/chunk-R2SQBPMH.js +3805 -0
  88. package/dist/{chunk-YPBIAONI.js → chunk-RGXVEYHI.js} +230 -162
  89. package/dist/{chunk-YMS6CJCD.js → chunk-SAX7HQ5E.js} +1 -1
  90. package/dist/{chunk-KIO3R4QN.js → chunk-SN7KI5JA.js} +27 -14
  91. package/dist/{chunk-4USWWI2Y.js → chunk-TE427YID.js} +2 -2
  92. package/dist/{chunk-ICMXC3MI.js → chunk-U6RXZNZE.js} +31 -18
  93. package/dist/{chunk-X7CCSMRR.js → chunk-UJKUB2LH.js} +16 -16
  94. package/dist/{chunk-K26FR4SM.js → chunk-UKOTVJFV.js} +12 -12
  95. package/dist/{chunk-4OT3FWVV.js → chunk-UNNXFKVG.js} +2 -2
  96. package/dist/{chunk-PF7OWVTL.js → chunk-VDM3FEDP.js} +2 -2
  97. package/dist/{chunk-Y5GY6Y7A.js → chunk-VNVQGDPC.js} +2 -2
  98. package/dist/{chunk-22QHO26F.js → chunk-W64L7IFS.js} +1 -1
  99. package/dist/{chunk-Y2RXRPL2.js → chunk-WHPN2TIT.js} +5 -5
  100. package/dist/{chunk-65WIUIHL.js → chunk-WTFUDNW2.js} +7 -15
  101. package/dist/{chunk-3XDOGSRD.js → chunk-X3CG5YQA.js} +3 -3
  102. package/dist/{chunk-UZOZ2W2E.js → chunk-XGHHVYC7.js} +96 -34
  103. package/dist/{chunk-4IEH6EAC.js → chunk-YALM6XN4.js} +203 -58
  104. package/dist/{chunk-B6JLCPT3.js → chunk-YOZPQCSR.js} +6 -6
  105. package/dist/{chunk-5JMZLQT6.js → chunk-YWHCXIMD.js} +16 -16
  106. package/dist/{chunk-UVEQLDKB.js → chunk-YWNJTZGN.js} +3 -2
  107. package/dist/{chunk-WUQUR33W.js → chunk-ZOCHDXDG.js} +1 -1
  108. package/dist/{chunk-IWTRHPTP.js → chunk-ZRHO6YMM.js} +8 -8
  109. package/dist/{clean-GJNDVVDI.js → clean-HGASSMWI.js} +41 -40
  110. package/dist/cli.js +68 -64
  111. package/dist/{cloud-JCWNMKXC.js → cloud-JPDVNXHN.js} +4 -4
  112. package/dist/{cloudrun-2BKGY53X.js → cloudrun-AMMGSDRP.js} +8 -8
  113. package/dist/commands/layout-audit.browser.js +15 -4
  114. package/dist/commands/motion-signature.browser.js +52 -0
  115. package/dist/{compare-XWM7FAYM.js → compare-LHHVHUN3.js} +51 -47
  116. package/dist/{compiler-U3W6CJW7.js → compiler-Q3N6QXMT.js} +6 -6
  117. package/dist/{component-CBRAE325.js → component-D72J32Q7.js} +4 -4
  118. package/dist/{compositions-EZYJEHQ3.js → compositions-UQRJ5ROG.js} +29 -29
  119. package/dist/{contactSheet-27SRU7A4.js → contactSheet-CAGMKI3E.js} +3 -1
  120. package/dist/{delete-JWWMOITI.js → delete-P42VIDDA.js} +9 -9
  121. package/dist/{deploy-UGOV76OH.js → deploy-WZTH57O4.js} +11 -11
  122. package/dist/{destroy-QBLMYDBF.js → destroy-4B5HW5FH.js} +5 -5
  123. package/dist/{deterministicFonts-L7YEEFGI.js → deterministicFonts-VQ5UBMXY.js} +2 -2
  124. package/dist/{dist-VO7OXOFK.js → dist-DYS24AMA.js} +5 -5
  125. package/dist/{dist-2FMTAVGL.js → dist-SENYPQGW.js} +4 -4
  126. package/dist/{docs-MPOM3PHH.js → docs-UEWLIP6D.js} +1 -1
  127. package/dist/{doctor-XSKSZRR7.js → doctor-KEJIG2SK.js} +28 -27
  128. package/dist/{events-RGS4MAH4.js → events-CYKSNR7N.js} +6 -6
  129. package/dist/{events-AIZODZMP.js → events-UZXFQPMT.js} +9 -9
  130. package/dist/{feedback-GE3VDZOH.js → feedback-LXNQF7CA.js} +35 -35
  131. package/dist/{ffmpeg-SCXZTR6X.js → ffmpeg-BYHON577.js} +16 -15
  132. package/dist/{figma-3R4XST4Y.js → figma-EIQ35PS6.js} +3 -3
  133. package/dist/fontLocalizeCli.js +24 -23
  134. package/dist/{get-4FKJP6IM.js → get-CC2YL7QO.js} +9 -9
  135. package/dist/{gpuPolicy-4NNXESM7.js → gpuPolicy-VFSACUQV.js} +14 -13
  136. package/dist/{grade-compare-FAYVPXUR.js → grade-compare-TUQL7IV2.js} +56 -50
  137. package/dist/{help-C3UQMYXK.js → help-ROROO5K6.js} +1 -1
  138. package/dist/{history-LDGQ2IZJ.js → history-PNELYLXX.js} +15 -15
  139. package/dist/hyperframe.manifest.json +1 -1
  140. package/dist/hyperframe.runtime.iife.js +59 -59
  141. package/dist/hyperframes-player.global.js +8 -3
  142. package/dist/{info-KMXFLD3Q.js → info-4FZTH6AS.js} +20 -20
  143. package/dist/{init-D7GYHSX5.js → init-I43GHHVP.js} +69 -68
  144. package/dist/{inspect-CUKWHCGS.js → inspect-IT4TYIZ3.js} +46 -44
  145. package/dist/{installer-EPRSH6QI.js → installer-CWOJVK5K.js} +7 -7
  146. package/dist/{keyframes-XCUPHBW3.js → keyframes-3FBOGVD4.js} +16 -16
  147. package/dist/{lambda-IMORFJHN.js → lambda-H3V32I7M.js} +15 -15
  148. package/dist/{layout-JD2IOBMS.js → layout-BZ5LYJ5C.js} +46 -44
  149. package/dist/{lint-FHLER36Y.js → lint-ZTNSGP2S.js} +23 -23
  150. package/dist/{lintProject-ARKJXDQF.js → lintProject-C2ONSH2U.js} +4 -4
  151. package/dist/{list-MGCFG5MA.js → list-XM4GWJYY.js} +10 -10
  152. package/dist/{login-QX6M4UTR.js → login-H6HYKMWX.js} +7 -7
  153. package/dist/{logout-DKJANASY.js → logout-DDC4QLVK.js} +3 -3
  154. package/dist/{manager-IETAVBZS.js → manager-TWVDSJXO.js} +17 -16
  155. package/dist/{manager-V2QCCPOJ.js → manager-UHWEKPXC.js} +8 -3
  156. package/dist/{media-treatment-MDAQRITV.js → media-treatment-N7MY5NXB.js} +34 -33
  157. package/dist/{media-use-4XXGABIL.js → media-use-JBN57JE3.js} +1 -1
  158. package/dist/{models-NZAOSI5A.js → models-WEMIMYBD.js} +25 -24
  159. package/dist/{motionShot-6ZDUM2K4.js → motionShot-RHICTVPU.js} +29 -26
  160. package/dist/{normalize-audio-7PRYX6QM.js → normalize-audio-ZFUZCJE3.js} +12 -12
  161. package/dist/{open-I254NY5J.js → open-IBJ4FL3G.js} +24 -24
  162. package/dist/{parakeetRunner-TL3TOGUS.js → parakeetRunner-XXO4WSLR.js} +26 -25
  163. package/dist/{pipeline-YPERVOQM.js → pipeline-6ML3LUVK.js} +19 -18
  164. package/dist/{play-MYI7BYKS.js → play-RV4NAVF5.js} +40 -38
  165. package/dist/{policies-5NTZOHJA.js → policies-XXNGXWHV.js} +1 -1
  166. package/dist/{preflight-HRCSZ52D.js → preflight-R3DEFMLA.js} +19 -18
  167. package/dist/{present-WYXQDV4Q.js → present-5TBJGE6B.js} +34 -33
  168. package/dist/{preview-535HTWQO.js → preview-FLKZ5CXJ.js} +56 -55
  169. package/dist/{producer-C76CRJ5O.js → producer-7XYSPVKH.js} +1 -1
  170. package/dist/{progress-I37JB7R4.js → progress-NHJIHAAG.js} +2 -2
  171. package/dist/{projectConfig-5QCNTGQT.js → projectConfig-TBU2VWZX.js} +9 -9
  172. package/dist/{publication-2QT7VVE2.js → publication-CWWUJYJ5.js} +5 -5
  173. package/dist/{publish-53EBL7OO.js → publish-FSNE4FEB.js} +28 -27
  174. package/dist/{refresh-MGCVWX56.js → refresh-RXTOPRBB.js} +3 -3
  175. package/dist/{remove-background-OBYHMPD4.js → remove-background-MDFWRWCT.js} +32 -31
  176. package/dist/render-DBGQZAZT.js +1516 -0
  177. package/dist/{render-3MELF6Y3.js → render-F4JQ553X.js} +102 -96
  178. package/dist/{render-75CRB3BW.js → render-LWZKXCLY.js} +8 -8
  179. package/dist/{render-batch-CEVWUYBP.js → render-batch-PU7NMLIJ.js} +8 -8
  180. package/dist/renderSetupWorker.js +12 -9
  181. package/dist/{resolver-4ICE2AT6.js → resolver-RUBGFXPC.js} +7 -7
  182. package/dist/{screenshotCapture-HXEZ3TNJ.js → screenshotCapture-UBY6TT75.js} +33 -6
  183. package/dist/{sherpa-L4UACXT3.js → sherpa-X2TBNO6P.js} +25 -24
  184. package/dist/{sites-3ZBYB5X4.js → sites-MAVH57MO.js} +2 -2
  185. package/dist/skills/hyperframes/references/review-loop.md +1 -1
  186. package/dist/{skills-DXTQPCAO.js → skills-WHKMWO2D.js} +17 -17
  187. package/dist/{skillsUpdateCheck-ULXRGLDU.js → skillsUpdateCheck-XQGZGJJG.js} +3 -3
  188. package/dist/{snapshot-PWFHQU5E.js → snapshot-M4NUUSNU.js} +73 -71
  189. package/dist/{src-RDSWHMJS.js → src-4UYM2K3S.js} +34 -26
  190. package/dist/{src-S7NVL5VF.js → src-6MSC62U2.js} +23 -19
  191. package/dist/{state-KAXGHF77.js → state-ORRPOLMQ.js} +2 -2
  192. package/dist/{staticProjectServer-GYPUQGI7.js → staticProjectServer-ERDTFP45.js} +21 -20
  193. package/dist/{status-BV7QAUQD.js → status-CM7IQTU2.js} +6 -6
  194. package/dist/studio/assets/{hyperframes-player-DcD3NXpX.js → hyperframes-player-DEncBxR7.js} +7 -2
  195. package/dist/studio/assets/{index-CFrZAwKo.js → index-BABXr78Q.js} +4 -4
  196. package/dist/studio/assets/{index-BIDKYk0h.js → index-Cc22mgvY.js} +1 -1
  197. package/dist/studio/index.html +1 -1
  198. package/dist/{studio-api-IXGX5ZFI.js → studio-api-SAKD3VKC.js} +18 -17
  199. package/dist/{studioServer-LA5Z7RSS.js → studioServer-IRI7EVOM.js} +91 -84
  200. package/dist/{telemetry-STEMAMAX.js → telemetry-F4XADVAF.js} +10 -10
  201. package/dist/{telemetry-CQYHCCPL.js → telemetry-QG5AANZ4.js} +1 -1
  202. package/dist/{timeline-KLD24EN6.js → timeline-5I444RRE.js} +42 -41
  203. package/dist/{tokens-ICLORAA4.js → tokens-LRRYM35A.js} +3 -3
  204. package/dist/{transcribe-A6IKK7VS.js → transcribe-44XDFNVU.js} +48 -47
  205. package/dist/{transcribe-CTHZLRXF.js → transcribe-WHYHWAB2.js} +20 -19
  206. package/dist/{tts-EDK3QS7M.js → tts-A2WILYGN.js} +6 -6
  207. package/dist/{updateCheck-FHCAAAXA.js → updateCheck-7CPH3CAZ.js} +2 -2
  208. package/dist/{upgrade-7EV4F2R7.js → upgrade-WW4GQ2RI.js} +3 -3
  209. package/dist/{urlDownloader-MIMGVASJ.js → urlDownloader-OHLLP45A.js} +13 -12
  210. package/dist/{validate-LZF46W3V.js → validate-VNCDH2LW.js} +40 -38
  211. package/dist/{video-ZC47NFOL.js → video-5OHTLL2D.js} +17 -14
  212. package/dist/warmCheck.js +228 -0
  213. package/dist/warmCheckClient.js +139 -0
  214. package/package.json +4 -2
  215. package/dist/chunk-BAZ5LCR3.js +0 -3805
  216. package/dist/hyperframe-runtime.js +0 -1125
  217. package/dist/render-5BXTEEG7.js +0 -719
  218. package/dist/{synthesize-I2C24XJ5.js → synthesize-PKTB5ATG.js} +1 -1
@@ -0,0 +1,1516 @@
1
+ import { createRequire as __hf_createRequire } from "node:module";
2
+ import { fileURLToPath as __hf_fileURLToPath } from "node:url";
3
+ import { dirname as __hf_dirname } from "node:path";
4
+ var require = __hf_createRequire(import.meta.url);
5
+ var __filename = __hf_fileURLToPath(import.meta.url);
6
+ var __dirname = __hf_dirname(__filename);
7
+ import {
8
+ parseEnumFlag,
9
+ parseIntFlag,
10
+ parseNumericFlag
11
+ } from "./chunk-DGECWPTL.js";
12
+ import {
13
+ colorStatus
14
+ } from "./chunk-U7CZBDMV.js";
15
+ import {
16
+ DEFAULT_MAX_WAIT_MS,
17
+ DEFAULT_POLL_INTERVAL_MS,
18
+ PollTimeoutError,
19
+ createCloudClient,
20
+ pollUntilTerminal,
21
+ reportApiError
22
+ } from "./chunk-J36JVBLR.js";
23
+ import {
24
+ uploadAssetViaDirectUpload,
25
+ uploadZipViaDirectUpload
26
+ } from "./chunk-5UDRPPN2.js";
27
+ import "./chunk-5UBZPG74.js";
28
+ import {
29
+ TEXT_FILE_EXTS,
30
+ assertUnchanged,
31
+ readChecked,
32
+ readStaged,
33
+ rewriteCssUrls,
34
+ rewriteHtmlAttributes,
35
+ rewriteStyleBlocks,
36
+ stagePublishFiles,
37
+ stagedSize,
38
+ zipPublishFileMap
39
+ } from "./chunk-XGHHVYC7.js";
40
+ import "./chunk-JQYJXUJW.js";
41
+ import {
42
+ resolveCloudBaseUrl
43
+ } from "./chunk-HRPWRZGF.js";
44
+ import "./chunk-7KVVNF7B.js";
45
+ import {
46
+ reportVariableIssues,
47
+ resolveVariablesArg,
48
+ validateVariablesAgainstProject
49
+ } from "./chunk-3TXNNBI2.js";
50
+ import {
51
+ downloadToFile
52
+ } from "./chunk-CZUAX3BV.js";
53
+ import {
54
+ findFFmpeg,
55
+ findFFprobe,
56
+ getFFmpegInstallHint
57
+ } from "./chunk-7HJALUIO.js";
58
+ import {
59
+ resolveProject
60
+ } from "./chunk-LRGYPXHF.js";
61
+ import {
62
+ errorBox,
63
+ formatBytes,
64
+ formatDuration
65
+ } from "./chunk-K2FLNJVB.js";
66
+ import "./chunk-442JNXRM.js";
67
+ import "./chunk-EOVYQ3IG.js";
68
+ import "./chunk-JIUTXKWA.js";
69
+ import "./chunk-UKOTVJFV.js";
70
+ import "./chunk-VWG7OQKU.js";
71
+ import {
72
+ defineCommand
73
+ } from "./chunk-S3S4TGEH.js";
74
+ import "./chunk-54HJ63Q2.js";
75
+ import {
76
+ failCommand
77
+ } from "./chunk-JVBRJMOP.js";
78
+ import {
79
+ c
80
+ } from "./chunk-ZTZA4UWZ.js";
81
+ import "./chunk-RGXVEYHI.js";
82
+ import "./chunk-IZCW2NKT.js";
83
+ import {
84
+ parseAnimatedGifMetadata
85
+ } from "./chunk-J3QM7AHP.js";
86
+ import "./chunk-RQVDTPGD.js";
87
+ import "./chunk-YALM6XN4.js";
88
+ import "./chunk-R2SQBPMH.js";
89
+ import "./chunk-VC76R6W2.js";
90
+ import "./chunk-UJKQWGFF.js";
91
+ import "./chunk-YMS5FOMC.js";
92
+ import "./chunk-OFQWTKPW.js";
93
+ import "./chunk-DPVXBVLL.js";
94
+ import "./chunk-3GXQPCJC.js";
95
+ import "./chunk-634HSJ3J.js";
96
+ import "./chunk-FXMKHHPO.js";
97
+ import "./chunk-DYAPHNV6.js";
98
+ import "./chunk-T6AGUTZJ.js";
99
+ import "./chunk-XT6MXC7A.js";
100
+ import "./chunk-4UMPIPCQ.js";
101
+ import "./chunk-ADDHW2FS.js";
102
+ import "./chunk-VAS6HQX3.js";
103
+ import {
104
+ cleanAssetUrl,
105
+ isRemoteOrInlineUrl,
106
+ resolveLocalAssetCandidates
107
+ } from "./chunk-6BNTGNGD.js";
108
+ import "./chunk-OYLO6TYK.js";
109
+ import "./chunk-MMQ52NIZ.js";
110
+ import {
111
+ readAuthoredDurationSeconds,
112
+ readMediaOffsetSeconds,
113
+ readPlaybackRate,
114
+ rewriteAssetPath
115
+ } from "./chunk-BSQ7XANM.js";
116
+ import {
117
+ parseHTML
118
+ } from "./chunk-SDYDNHEE.js";
119
+ import "./chunk-IXLUTE6N.js";
120
+ import "./chunk-TYXH3EKP.js";
121
+ import "./chunk-W2LGJSEN.js";
122
+ import {
123
+ withMeta
124
+ } from "./chunk-ABZYYUUH.js";
125
+ import "./chunk-K4WIOZFP.js";
126
+ import "./chunk-XUXGIA7H.js";
127
+ import "./chunk-WERLJGIC.js";
128
+ import "./chunk-ISN5GRYI.js";
129
+ import "./chunk-4N4P3ZWL.js";
130
+ import {
131
+ normalizeErrorMessage
132
+ } from "./chunk-KNU5COPB.js";
133
+ import "./chunk-KGPLIH7B.js";
134
+ import "./chunk-M2WBOQ2Q.js";
135
+
136
+ // src/cloud/detectAspectRatio.ts
137
+ import { readFileSync } from "fs";
138
+ var RATIO_TOLERANCE = 0.05;
139
+ var SUPPORTED_RATIOS = [
140
+ { value: "16:9", ratio: 16 / 9 },
141
+ { value: "9:16", ratio: 9 / 16 },
142
+ { value: "1:1", ratio: 1 }
143
+ ];
144
+ var ROOT_COMPOSITION_DIV_RE = /<div\b[^>]*?\bdata-composition-id\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)[^>]*>/i;
145
+ var DATA_WIDTH_RE = /\bdata-width\s*=\s*(?:"(\d+(?:\.\d+)?)"|'(\d+(?:\.\d+)?)'|(\d+(?:\.\d+)?))(?=\s|>|\/)/i;
146
+ var DATA_HEIGHT_RE = /\bdata-height\s*=\s*(?:"(\d+(?:\.\d+)?)"|'(\d+(?:\.\d+)?)'|(\d+(?:\.\d+)?))(?=\s|>|\/)/i;
147
+ function extractAttributeNumber(tag, re) {
148
+ const match = tag.match(re);
149
+ if (!match) return null;
150
+ const raw = match[1] ?? match[2] ?? match[3];
151
+ if (raw === void 0) return null;
152
+ const value = Number(raw);
153
+ return Number.isFinite(value) ? value : null;
154
+ }
155
+ function detectAspectRatioFromHtml(entryHtmlPath) {
156
+ let html;
157
+ try {
158
+ html = readFileSync(entryHtmlPath, "utf-8");
159
+ } catch (err) {
160
+ return { kind: "read-error", error: normalizeErrorMessage(err) };
161
+ }
162
+ return detectAspectRatioFromHtmlString(html);
163
+ }
164
+ function detectAspectRatioFromHtmlString(html) {
165
+ const tagMatch = html.match(ROOT_COMPOSITION_DIV_RE);
166
+ if (!tagMatch) return { kind: "no-root-div" };
167
+ const openTag = tagMatch[0];
168
+ const width = extractAttributeNumber(openTag, DATA_WIDTH_RE);
169
+ const height = extractAttributeNumber(openTag, DATA_HEIGHT_RE);
170
+ if (width === null || height === null) return { kind: "no-dims" };
171
+ if (width <= 0 || height <= 0) return { kind: "invalid-dims", width, height };
172
+ const ratio = width / height;
173
+ for (const candidate of SUPPORTED_RATIOS) {
174
+ if (Math.abs(ratio - candidate.ratio) <= RATIO_TOLERANCE) {
175
+ return { kind: "matched", aspectRatio: candidate.value, width, height };
176
+ }
177
+ }
178
+ return { kind: "no-match", width, height, ratio };
179
+ }
180
+
181
+ // src/cloud/offload.ts
182
+ import { execFile } from "child_process";
183
+ import { createHash } from "crypto";
184
+ import {
185
+ copyFileSync,
186
+ createReadStream,
187
+ mkdirSync,
188
+ mkdtempSync,
189
+ readFileSync as readFileSync2,
190
+ renameSync,
191
+ rmSync,
192
+ writeFileSync
193
+ } from "fs";
194
+ import { homedir, tmpdir } from "os";
195
+ import { basename as basename2, dirname, extname, join, posix, relative, resolve } from "path";
196
+ import { promisify } from "util";
197
+
198
+ // src/cloud/offloadTrim.ts
199
+ import { basename } from "path";
200
+ var TRIM_MARGIN_SECONDS = 0.5;
201
+ var RETIMING_SCRIPT_RE = /data-(?:playback-start|media-start|start|duration|end|playback-rate)|\b(?:dataset|setAttribute|currentTime|playbackRate)\b|\.(?:src|loop)\b/;
202
+ function mediaSources(el) {
203
+ const own = el.getAttribute("src");
204
+ const nested = [...el.querySelectorAll("source[src]")].map((s) => s.getAttribute("src"));
205
+ return [...own ? [own] : [], ...nested];
206
+ }
207
+ function hasTimedAncestor(el) {
208
+ for (let p = el.parentElement; p; p = p.parentElement) {
209
+ const get = (name) => p.getAttribute(name);
210
+ if (readMediaOffsetSeconds(get) > 0) return true;
211
+ if (p.hasAttribute("data-composition-id") && p.parentElement?.closest("[data-composition-id]")) {
212
+ return true;
213
+ }
214
+ }
215
+ return false;
216
+ }
217
+ function playedWindow(el) {
218
+ if (el.hasAttribute("loop") || el.hasAttribute("data-automation") || el.hasAttribute("data-var-src") || hasTimedAncestor(el)) {
219
+ return null;
220
+ }
221
+ const get = (name) => el.getAttribute(name);
222
+ const start = readMediaOffsetSeconds(get);
223
+ const duration = readAuthoredDurationSeconds(get, Number(get("data-start") ?? 0));
224
+ if (duration === null || !(duration > 0)) return null;
225
+ return { start, end: start + duration * readPlaybackRate(get) };
226
+ }
227
+ function mergeWindows(windows, gridPerSecond) {
228
+ const down = (t) => Math.floor(t * gridPerSecond) / gridPerSecond;
229
+ const up = (t) => Math.ceil(t * gridPerSecond) / gridPerSecond;
230
+ const merged = [];
231
+ for (const w of [...windows].sort((a, b) => a.start - b.start)) {
232
+ const start = down(Math.max(0, w.start - TRIM_MARGIN_SECONDS));
233
+ const end = up(w.end + TRIM_MARGIN_SECONDS);
234
+ const last = merged.at(-1);
235
+ if (last && start <= last.end) last.end = Math.max(last.end, end);
236
+ else merged.push({ start, end });
237
+ }
238
+ return merged;
239
+ }
240
+ function shiftMediaStart(el, segments) {
241
+ const get = (name) => el.getAttribute(name);
242
+ const start = readMediaOffsetSeconds(get);
243
+ let offset = 0;
244
+ for (const segment of segments) {
245
+ if (start >= segment.start && start < segment.end) {
246
+ const attr = Number.parseFloat(get("data-playback-start") ?? "") >= 0 ? "data-playback-start" : "data-media-start";
247
+ el.setAttribute(attr, String(offset + (start - segment.start)));
248
+ return;
249
+ }
250
+ offset += segment.end - segment.start;
251
+ }
252
+ }
253
+ function planTrim(input) {
254
+ if (input.mentionedElsewhere || RETIMING_SCRIPT_RE.test(input.scriptCode)) return null;
255
+ const { document } = parseHTML(input.entryHtml);
256
+ const uses = [];
257
+ let sources = 0;
258
+ for (const el of document.querySelectorAll("video, audio")) {
259
+ const matching = mediaSources(el).filter((src) => input.resolveKey(src) === input.path);
260
+ if (matching.length === 0) continue;
261
+ if (matching.some((src) => src.includes("#"))) return null;
262
+ uses.push(el);
263
+ sources += matching.length;
264
+ }
265
+ if (uses.length === 0 || input.entryHtml.split(basename(input.path)).length - 1 !== sources) {
266
+ return null;
267
+ }
268
+ const windows = uses.map(playedWindow);
269
+ if (windows.some((w) => w === null)) return null;
270
+ const segments = mergeWindows(windows, input.gridPerSecond);
271
+ for (const el of uses) shiftMediaStart(el, segments);
272
+ return { segments, html: document.toString() };
273
+ }
274
+
275
+ // src/cloud/offload.ts
276
+ var execFileAsync = promisify(execFile);
277
+ var OFFLOAD_MIN_BYTES = 5 * 1024 * 1024;
278
+ var MiB = 1024 * 1024;
279
+ var AUDIO_MAX_BYTES = 15e8;
280
+ var ACCEPTED = {
281
+ ".mp4": { contentType: "video/mp4", maxBytes: 200 * MiB },
282
+ ".m4v": { contentType: "video/mp4", maxBytes: 200 * MiB },
283
+ ".mov": { contentType: "video/quicktime", maxBytes: 200 * MiB },
284
+ ".webm": { contentType: "video/webm", maxBytes: 200 * MiB },
285
+ ".png": { contentType: "image/png", maxBytes: 200 * MiB },
286
+ ".jpg": { contentType: "image/jpeg", maxBytes: 200 * MiB },
287
+ ".jpeg": { contentType: "image/jpeg", maxBytes: 200 * MiB },
288
+ ".webp": { contentType: "image/webp", maxBytes: 200 * MiB },
289
+ ".wav": { contentType: "audio/wav", maxBytes: AUDIO_MAX_BYTES },
290
+ ".mp3": { contentType: "audio/mpeg", maxBytes: AUDIO_MAX_BYTES }
291
+ };
292
+ var VIDEO_EXTS = /* @__PURE__ */ new Set([
293
+ ".mp4",
294
+ ".m4v",
295
+ ".mov",
296
+ ".webm",
297
+ ".mkv",
298
+ ".avi",
299
+ ".mpg",
300
+ ".mpeg",
301
+ ".ts",
302
+ ".m2ts",
303
+ ".mts",
304
+ ".flv",
305
+ ".wmv",
306
+ ".ogv",
307
+ ".3gp"
308
+ ]);
309
+ var AUDIO_EXTS = /* @__PURE__ */ new Set([
310
+ ".wav",
311
+ ".mp3",
312
+ ".m4a",
313
+ ".aac",
314
+ ".ogg",
315
+ ".oga",
316
+ ".opus",
317
+ ".flac",
318
+ ".aif",
319
+ ".aiff",
320
+ ".wma"
321
+ ]);
322
+ var CONVERTIBLE_IMAGE_EXTS = /* @__PURE__ */ new Set([".gif", ".avif", ".bmp", ".tif", ".tiff"]);
323
+ var REENCODE_PRESETS = {
324
+ video: {
325
+ ext: ".mp4",
326
+ contentType: "video/mp4",
327
+ args: ["-c:v", "libx264", "-preset", "slow", "-crf", "18", "-pix_fmt", "yuv420p"],
328
+ audio: ["-c:a", "aac", "-b:a", "320k", "-movflags", "+faststart"]
329
+ },
330
+ hdrVideo: {
331
+ ext: ".mp4",
332
+ contentType: "video/mp4",
333
+ args: ["-c:v", "libx265", "-preset", "slow", "-crf", "18", "-pix_fmt", "yuv420p10le"],
334
+ audio: ["-tag:v", "hvc1", "-c:a", "aac", "-b:a", "320k", "-movflags", "+faststart"]
335
+ },
336
+ alphaVideo: {
337
+ ext: ".webm",
338
+ contentType: "video/webm",
339
+ args: ["-c:v", "libvpx-vp9", "-crf", "18", "-b:v", "0", "-pix_fmt", "yuva420p"],
340
+ audio: ["-c:a", "libopus", "-b:a", "256k"]
341
+ },
342
+ audio: {
343
+ ext: ".wav",
344
+ contentType: "audio/wav",
345
+ args: ["-vn"],
346
+ audio: ["-c:a", "pcm_s16le"]
347
+ },
348
+ image: {
349
+ ext: ".png",
350
+ contentType: "image/png",
351
+ args: ["-frames:v", "1"],
352
+ audio: []
353
+ }
354
+ };
355
+ function defaultOffloadCachePath() {
356
+ return join(homedir(), ".cache", "hyperframes", "cloud-assets.json");
357
+ }
358
+ function readCache(path) {
359
+ try {
360
+ return JSON.parse(readFileSync2(path, "utf-8")) ?? {};
361
+ } catch {
362
+ return {};
363
+ }
364
+ }
365
+ function writeCache(path, cache) {
366
+ mkdirSync(dirname(path), { recursive: true });
367
+ const tmp = `${path}.${process.pid}.tmp`;
368
+ writeFileSync(tmp, JSON.stringify(cache, null, 2));
369
+ renameSync(tmp, path);
370
+ }
371
+ async function stillServed(entry, fetchImpl) {
372
+ try {
373
+ const res = await fetchImpl(entry.url, { method: "HEAD" });
374
+ return res.ok && Number(res.headers.get("content-length")) === entry.size_bytes;
375
+ } catch {
376
+ return false;
377
+ }
378
+ }
379
+ function mediaKind(path) {
380
+ const ext = extname(path).toLowerCase();
381
+ if (VIDEO_EXTS.has(ext)) return "video";
382
+ if (AUDIO_EXTS.has(ext)) return "audio";
383
+ if (ACCEPTED[ext] || CONVERTIBLE_IMAGE_EXTS.has(ext)) return "image";
384
+ return null;
385
+ }
386
+ async function probeVideo(path) {
387
+ const ffprobe = findFFprobe();
388
+ if (!ffprobe)
389
+ throw new Error(`Re-encoding needs ffprobe. Install ffmpeg: ${getFFmpegInstallHint()}`);
390
+ const { stdout } = await execFileAsync(ffprobe, [
391
+ "-v",
392
+ "error",
393
+ "-select_streams",
394
+ "v:0",
395
+ "-show_entries",
396
+ "stream=codec_name,pix_fmt,color_primaries,color_transfer,color_space:stream_tags",
397
+ "-of",
398
+ "json",
399
+ "--",
400
+ path
401
+ ]);
402
+ const stream = JSON.parse(stdout).streams?.[0] ?? {};
403
+ const tags = stream.tags ?? {};
404
+ const alphaTag = Object.entries(tags).some(
405
+ ([k, v]) => k.toLowerCase() === "alpha_mode" && v === "1"
406
+ );
407
+ const transfer = stream.color_transfer;
408
+ return {
409
+ // VP9 alpha keeps pix_fmt yuv420p and flags the plane only in the ALPHA_MODE tag.
410
+ alpha: alphaTag || /^(yuva|rgba|argb|bgra|abgr|gbrap|ya)/.test(String(stream.pix_fmt ?? "")),
411
+ hdr: transfer === "smpte2084" || transfer === "arib-std-b67",
412
+ codec: stream.codec_name,
413
+ color: {
414
+ primaries: stream.color_primaries,
415
+ transfer,
416
+ space: stream.color_space
417
+ }
418
+ };
419
+ }
420
+ function alphaDecoderArgs(probe) {
421
+ if (!probe.alpha) return [];
422
+ if (probe.codec === "vp9") return ["-c:v", "libvpx-vp9"];
423
+ if (probe.codec === "vp8") return ["-c:v", "libvpx"];
424
+ return [];
425
+ }
426
+ function hdrColorArgs({ color }) {
427
+ return [
428
+ ...color.primaries ? ["-color_primaries", color.primaries] : [],
429
+ ...color.transfer ? ["-color_trc", color.transfer] : [],
430
+ ...color.space ? ["-colorspace", color.space] : []
431
+ ];
432
+ }
433
+ async function hasAudioStream(ffprobe, path) {
434
+ const { stdout } = await execFileAsync(ffprobe, [
435
+ "-v",
436
+ "error",
437
+ "-select_streams",
438
+ "a",
439
+ "-show_entries",
440
+ "stream=index",
441
+ "-of",
442
+ "csv=p=0",
443
+ "--",
444
+ path
445
+ ]);
446
+ return stdout.trim() !== "";
447
+ }
448
+ var seconds = (t) => (Math.floor(t * 1e6) / 1e6).toFixed(6);
449
+ async function segmentArgs(sourcePath, decoder, segments, audioOnly) {
450
+ if (!segments?.length) return [...decoder, "-i", sourcePath];
451
+ const inputs = segments.flatMap((s) => [
452
+ ...decoder,
453
+ "-ss",
454
+ seconds(s.start),
455
+ "-t",
456
+ seconds(s.end - s.start),
457
+ "-i",
458
+ sourcePath
459
+ ]);
460
+ if (segments.length === 1) return inputs;
461
+ const audio = audioOnly || await hasAudioStream(findFFprobe(), sourcePath);
462
+ return [...inputs, ...concatArgs(segments.length, !audioOnly, audio)];
463
+ }
464
+ function concatArgs(count, video, audio) {
465
+ const pad = (i) => (video ? `[${i}:v]` : "") + (audio ? `[${i}:a]` : "");
466
+ const pads = Array.from({ length: count }, (_, i) => pad(i)).join("");
467
+ const flags = `n=${count}:v=${video ? 1 : 0}:a=${audio ? 1 : 0}`;
468
+ return [
469
+ "-filter_complex",
470
+ `${pads}concat=${flags}${video ? "[v]" : ""}${audio ? "[a]" : ""}`,
471
+ ...video ? ["-map", "[v]"] : [],
472
+ ...audio ? ["-map", "[a]"] : []
473
+ ];
474
+ }
475
+ async function ffmpegReencode(sourcePath, presetName, segments) {
476
+ const ffmpeg = findFFmpeg();
477
+ if (!ffmpeg) throw new Error(`Re-encoding needs ffmpeg. Install it: ${getFFmpegInstallHint()}`);
478
+ const preset = REENCODE_PRESETS[presetName];
479
+ const probe = preset.contentType.startsWith("video/") ? await probeVideo(sourcePath) : void 0;
480
+ const decoder = probe ? alphaDecoderArgs(probe) : [];
481
+ const color = presetName === "hdrVideo" && probe ? hdrColorArgs(probe) : [];
482
+ const workDir = mkdtempSync(join(tmpdir(), "hf-offload-"));
483
+ const outPath = join(workDir, `${basename2(sourcePath, extname(sourcePath))}${preset.ext}`);
484
+ try {
485
+ await execFileAsync(
486
+ ffmpeg,
487
+ [
488
+ "-v",
489
+ "error",
490
+ "-y",
491
+ ...await segmentArgs(sourcePath, decoder, segments, presetName === "audio"),
492
+ ...preset.args,
493
+ ...color,
494
+ ...preset.audio,
495
+ outPath
496
+ ],
497
+ { maxBuffer: 16 * MiB }
498
+ );
499
+ return {
500
+ bytes: readChecked(outPath, ({ size }) => assertFitsAsset(sourcePath, outPath, size)),
501
+ filename: basename2(outPath),
502
+ contentType: preset.contentType
503
+ };
504
+ } finally {
505
+ rmSync(workDir, { recursive: true, force: true });
506
+ }
507
+ }
508
+ async function choosePreset(sourcePath, kind) {
509
+ if (kind === "audio") return "audio";
510
+ if (kind === "image") return await isAnimatedImage(sourcePath) ? null : "image";
511
+ const probe = await probeVideo(sourcePath);
512
+ return probe.alpha ? "alphaVideo" : probe.hdr ? "hdrVideo" : "video";
513
+ }
514
+ async function isAnimatedImage(path) {
515
+ if (extname(path).toLowerCase() === ".gif") {
516
+ return parseAnimatedGifMetadata(readFileSync2(path))?.animated ?? true;
517
+ }
518
+ const ffprobe = findFFprobe();
519
+ if (!ffprobe) return true;
520
+ try {
521
+ const { stdout } = await execFileAsync(ffprobe, [
522
+ "-v",
523
+ "error",
524
+ "-show_entries",
525
+ "stream=nb_frames",
526
+ "-of",
527
+ "csv=p=0",
528
+ "--",
529
+ path
530
+ ]);
531
+ return stdout.split("\n").some((line) => Number(line) > 1);
532
+ } catch {
533
+ return true;
534
+ }
535
+ }
536
+ async function trimGrid(sourcePath, kind) {
537
+ if (kind === "audio") return 1e3;
538
+ if (kind !== "video") return null;
539
+ const ffprobe = findFFprobe();
540
+ if (!ffprobe) return null;
541
+ const { stdout } = await execFileAsync(ffprobe, [
542
+ "-v",
543
+ "error",
544
+ "-select_streams",
545
+ "v:0",
546
+ "-show_entries",
547
+ "stream=r_frame_rate,avg_frame_rate",
548
+ "-of",
549
+ "csv=p=0",
550
+ "--",
551
+ sourcePath
552
+ ]);
553
+ const rate = (fraction) => {
554
+ const [num, den] = (fraction ?? "").split("/").map(Number);
555
+ return num && den ? num / den : NaN;
556
+ };
557
+ const [real, average] = stdout.trim().split(",");
558
+ const fps = rate(real);
559
+ return Number.isFinite(fps) && Math.abs(fps - rate(average)) < 0.01 ? fps : null;
560
+ }
561
+ var ffmpegReencoder = { choose: choosePreset, trimGrid, run: ffmpegReencode };
562
+ async function stagedSha256(file) {
563
+ if (Buffer.isBuffer(file)) return createHash("sha256").update(file).digest("hex");
564
+ const hash = createHash("sha256");
565
+ for await (const chunk of createReadStream(file.absPath)) hash.update(chunk);
566
+ assertUnchanged(file);
567
+ return hash.digest("hex");
568
+ }
569
+ function snapshotPath(path, file, tempFiles) {
570
+ return Buffer.isBuffer(file) ? stagedCopy(path, file, tempFiles) : file.absPath;
571
+ }
572
+ function stagedCopy(path, file, tempFiles) {
573
+ const dir = mkdtempSync(join(tmpdir(), "hf-offload-src-"));
574
+ tempFiles.push(dir);
575
+ const copy = join(dir, basename2(path));
576
+ if (Buffer.isBuffer(file)) writeFileSync(copy, file);
577
+ else {
578
+ copyFileSync(file.absPath, copy);
579
+ assertUnchanged(file);
580
+ }
581
+ return copy;
582
+ }
583
+ function textOf(path, file) {
584
+ return TEXT_FILE_EXTS.test(path) && Buffer.isBuffer(file) ? file.toString("utf-8") : void 0;
585
+ }
586
+ function scriptCode(fileMap) {
587
+ const parts = [];
588
+ for (const [path, content] of fileMap) {
589
+ const text = textOf(path, content);
590
+ if (text === void 0) continue;
591
+ if (/\.html?$/i.test(path)) {
592
+ for (const script of parseHTML(text).document.querySelectorAll("script:not([src])")) {
593
+ parts.push(script.textContent ?? "");
594
+ }
595
+ } else if (/\.(m?js|cjs|json)$/i.test(path)) parts.push(text);
596
+ }
597
+ return parts.join("\n");
598
+ }
599
+ function assertFitsAsset(path, filename, size) {
600
+ const maxBytes = ACCEPTED[extname(filename).toLowerCase()]?.maxBytes ?? 0;
601
+ if (size > maxBytes) {
602
+ throw new Error(
603
+ `${path} is ${formatBytes(size)} after re-encoding at visually lossless quality, over the ${formatBytes(maxBytes)} a HeyGen asset can hold.`
604
+ );
605
+ }
606
+ }
607
+ async function planFile(ctx, path, file) {
608
+ const kind = mediaKind(path);
609
+ const size = stagedSize(file);
610
+ if (!kind || size < OFFLOAD_MIN_BYTES) return null;
611
+ const sha256 = await stagedSha256(file);
612
+ const keyFor = (variant2) => `${ctx.opts.apiBaseUrl} ${sha256} ${variant2}`;
613
+ const accepted = ACCEPTED[extname(path).toLowerCase()];
614
+ if (accepted && size <= accepted.maxBytes) {
615
+ return { key: keyFor("as-is"), sourcePath: "", contentType: accepted.contentType };
616
+ }
617
+ const sourcePath = snapshotPath(path, file, ctx.tempFiles);
618
+ const preset = await ctx.reencoder.choose(sourcePath, kind);
619
+ if (!preset) return null;
620
+ const segments = kind === "image" ? null : await planFileTrim(ctx, path, sourcePath, kind);
621
+ const variant = JSON.stringify([REENCODE_PRESETS[preset], segments]);
622
+ return { key: keyFor(variant), preset, sourcePath, segments: segments ?? void 0 };
623
+ }
624
+ async function planFileTrim(ctx, path, sourcePath, kind) {
625
+ const entryPath = ctx.opts.entryPath ?? "index.html";
626
+ const entryHtml = textOf(entryPath, ctx.fileMap.get(entryPath));
627
+ const gridPerSecond = entryHtml ? await ctx.reencoder.trimGrid?.(sourcePath, kind) : null;
628
+ if (!entryHtml || !gridPerSecond) return null;
629
+ const name = basename2(path);
630
+ const plan = planTrim({
631
+ entryHtml,
632
+ path,
633
+ gridPerSecond,
634
+ resolveKey: (raw) => htmlSrcKey(ctx.absDir, entryPath, raw, ctx.fileMap),
635
+ mentionedElsewhere: [...ctx.fileMap].some(
636
+ ([other, content]) => other !== entryPath && other !== path && !!textOf(other, content)?.includes(name)
637
+ ),
638
+ scriptCode: scriptCode(ctx.fileMap)
639
+ });
640
+ if (!plan) return null;
641
+ ctx.fileMap.set(entryPath, Buffer.from(plan.html, "utf-8"));
642
+ return plan.segments;
643
+ }
644
+ async function offloadFile(ctx, path, file) {
645
+ const plan = await planFile(ctx, path, file);
646
+ if (!plan) return null;
647
+ const item = {
648
+ path,
649
+ size_bytes: stagedSize(file),
650
+ action: plan.preset ? "reencode" : "upload",
651
+ ...plan.segments && {
652
+ trimmed_to_seconds: plan.segments.reduce((sum, s) => sum + s.end - s.start, 0)
653
+ }
654
+ };
655
+ const hit = ctx.cache[plan.key];
656
+ if (hit && (!ctx.opts.upload || await stillServed(hit, ctx.fetchImpl))) {
657
+ return { ...item, action: "cached", url: hit.url };
658
+ }
659
+ if (!ctx.opts.upload) return item;
660
+ return { ...item, url: await uploadPlanned(ctx, ctx.opts.upload, path, file, plan) };
661
+ }
662
+ async function uploadPlanned(ctx, upload, path, staged, plan) {
663
+ const filename = basename2(path);
664
+ const file = plan.preset ? await ctx.reencoder.run(plan.sourcePath, plan.preset, plan.segments) : Buffer.isBuffer(staged) ? { bytes: staged, filename, contentType: plan.contentType } : { path: staged.absPath, size: staged.size, filename, contentType: plan.contentType };
665
+ if (plan.preset && !Buffer.isBuffer(staged)) assertUnchanged(staged);
666
+ const size = "bytes" in file ? file.bytes.byteLength : file.size;
667
+ assertFitsAsset(path, file.filename, size);
668
+ const asset = await upload(file);
669
+ if (!Buffer.isBuffer(staged)) assertUnchanged(staged);
670
+ ctx.cache[plan.key] = {
671
+ asset_id: asset.asset_id,
672
+ url: asset.url,
673
+ size_bytes: size
674
+ };
675
+ writeCache(ctx.cachePath, ctx.cache);
676
+ return asset.url;
677
+ }
678
+ var GIF_PREFIX = "_animated_gif";
679
+ async function prepareEntryGifs(ctx, fileMap) {
680
+ const entryPath = ctx.opts.entryPath ?? "index.html";
681
+ const html = textOf(entryPath, fileMap.get(entryPath));
682
+ if (!html || !/\.gif\b/i.test(html)) return;
683
+ const sourceAssets = entryGifSources(ctx, entryPath, html);
684
+ if (!sourceAssets) return;
685
+ const prep = ctx.opts.gifPrep ?? (await import("./src-6MSC62U2.js")).prepareAnimatedGifInputs;
686
+ const outputDir = mkdtempSync(join(tmpdir(), "hf-offload-gif-"));
687
+ ctx.tempFiles.push(outputDir);
688
+ const result = await prep(html, {
689
+ projectDir: ctx.absDir,
690
+ downloadDir: outputDir,
691
+ outputDir,
692
+ outputSrcPrefix: posix.relative(posix.dirname(entryPath), GIF_PREFIX),
693
+ cacheDir: join(homedir(), ".cache", "hyperframes", "animated-gif"),
694
+ sourceAssets
695
+ });
696
+ if (result.preparedGifs.length === 0) return;
697
+ fileMap.set(entryPath, Buffer.from(result.html, "utf-8"));
698
+ for (const gif of result.preparedGifs) {
699
+ fileMap.set(`${GIF_PREFIX}/${basename2(gif.outputPath)}`, readFileSync2(gif.outputPath));
700
+ const source = htmlSrcKey(ctx.absDir, entryPath, gif.sourceSrc.trim(), fileMap);
701
+ if (source) ctx.preparedGifSources.push(source);
702
+ }
703
+ }
704
+ function entryGifSources(ctx, entryPath, html) {
705
+ const sourceAssets = /* @__PURE__ */ new Map();
706
+ let heavy = false;
707
+ for (const img of parseHTML(html).document.querySelectorAll("img[src]")) {
708
+ const src = img.getAttribute("src").trim();
709
+ const key = htmlSrcKey(ctx.absDir, entryPath, src, ctx.fileMap);
710
+ if (!key?.toLowerCase().endsWith(".gif")) continue;
711
+ const file = ctx.fileMap.get(key);
712
+ sourceAssets.set(src, stagedCopy(key, file, ctx.tempFiles));
713
+ heavy ||= stagedSize(file) >= OFFLOAD_MIN_BYTES;
714
+ }
715
+ return heavy ? sourceAssets : null;
716
+ }
717
+ async function offloadHeavyMedia(projectDir, fileMap, opts) {
718
+ const cachePath = opts.cachePath ?? defaultOffloadCachePath();
719
+ const ctx = {
720
+ absDir: resolve(projectDir),
721
+ opts,
722
+ cachePath,
723
+ cache: readCache(cachePath),
724
+ fetchImpl: opts.fetchImpl ?? fetch,
725
+ reencoder: opts.reencoder ?? ffmpegReencoder,
726
+ tempFiles: [],
727
+ preparedGifSources: [],
728
+ fileMap
729
+ };
730
+ const urls = /* @__PURE__ */ new Map();
731
+ const report = [];
732
+ try {
733
+ await prepareEntryGifs(ctx, fileMap);
734
+ const referenced = referencedFiles(ctx.absDir, fileMap);
735
+ for (const [path, file] of [...fileMap].sort(([a], [b]) => a.localeCompare(b))) {
736
+ if (!referenced.has(path)) continue;
737
+ const item = await offloadFile(ctx, path, file);
738
+ if (!item) continue;
739
+ urls.set(path, item.url ?? `https://assets.invalid/${report.length}`);
740
+ report.push(item);
741
+ opts.onFile?.(item);
742
+ }
743
+ } finally {
744
+ for (const dir of ctx.tempFiles) rmSync(dir, { recursive: true, force: true });
745
+ }
746
+ if (urls.size > 0) rewriteReferences(ctx.absDir, fileMap, (key) => urls.get(key) ?? null);
747
+ dropReplacedFiles(fileMap, report, ctx.preparedGifSources, [...urls.values()]);
748
+ return report;
749
+ }
750
+ function dropReplacedFiles(fileMap, report, preparedGifSources, assetUrls) {
751
+ const withoutUrls = (text) => assetUrls.reduce((acc, url) => acc.replaceAll(url, ""), text);
752
+ const code = withoutUrls(scriptCode(fileMap));
753
+ for (const item of report) {
754
+ if (isStillMentioned(fileMap, item.path, code, withoutUrls)) item.kept_in_archive = true;
755
+ else fileMap.delete(item.path);
756
+ }
757
+ for (const gif of preparedGifSources) {
758
+ if (!isStillMentioned(fileMap, gif, code, withoutUrls)) fileMap.delete(gif);
759
+ }
760
+ }
761
+ function isStillMentioned(fileMap, path, code, withoutUrls) {
762
+ const ext = extname(path);
763
+ if (code.includes(basename2(path, ext)) && code.includes(ext.slice(1))) return true;
764
+ const name = basename2(path);
765
+ for (const [entryPath, content] of fileMap) {
766
+ const contentText = textOf(entryPath, content);
767
+ if (entryPath === path || !contentText?.includes(name)) continue;
768
+ if (withoutUrls(contentText).includes(name)) return true;
769
+ }
770
+ return false;
771
+ }
772
+ function htmlSrcKey(absDir, entryPath, raw, fileMap) {
773
+ const clean = cleanAssetUrl(raw);
774
+ if (!clean || isRemoteOrInlineUrl(clean)) return null;
775
+ const rootRelative = clean.startsWith("/") ? clean : rewriteAssetPath(entryPath, clean, (p) => fileMap.has(p));
776
+ for (const candidate of resolveLocalAssetCandidates(absDir, rootRelative)) {
777
+ const key = relative(absDir, candidate).replaceAll("\\", "/");
778
+ if (fileMap.has(key)) return key;
779
+ }
780
+ return null;
781
+ }
782
+ function referencedFiles(absDir, fileMap) {
783
+ const keys = /* @__PURE__ */ new Set();
784
+ rewriteReferences(absDir, fileMap, (key) => {
785
+ if (fileMap.has(key)) keys.add(key);
786
+ return null;
787
+ });
788
+ return keys;
789
+ }
790
+ function rewriteReferences(absDir, fileMap, urlForKey) {
791
+ const urlFor = (rootRelative) => {
792
+ for (const candidate of resolveLocalAssetCandidates(absDir, rootRelative)) {
793
+ const url = urlForKey(relative(absDir, candidate).replaceAll("\\", "/"));
794
+ if (url) return url;
795
+ }
796
+ return null;
797
+ };
798
+ for (const [entryPath, content] of [...fileMap]) {
799
+ const rewrite = rewriterFor(absDir, entryPath, urlFor, fileMap);
800
+ const text = rewrite ? textOf(entryPath, content) : void 0;
801
+ const next = rewrite && text !== void 0 ? rewrite(text) : null;
802
+ if (next !== null) fileMap.set(entryPath, Buffer.from(next, "utf-8"));
803
+ }
804
+ }
805
+ function rewriterFor(absDir, entryPath, urlFor, fileMap) {
806
+ if (/\.html?$/i.test(entryPath)) {
807
+ return (text) => rewriteHtmlEntry(absDir, entryPath, text, urlFor, fileMap);
808
+ }
809
+ if (/\.css$/i.test(entryPath)) return (text) => rewriteCssEntry(absDir, entryPath, text, urlFor);
810
+ if (/\.(m?js|cjs|json)$/i.test(entryPath)) return (text) => rewriteStringLiterals(text, urlFor);
811
+ return null;
812
+ }
813
+ function localTarget(raw, toRootRelative, urlFor) {
814
+ const clean = cleanAssetUrl(raw);
815
+ if (!clean || isRemoteOrInlineUrl(clean)) return null;
816
+ const url = urlFor(clean.startsWith("/") ? clean : toRootRelative(clean));
817
+ return url && url + raw.trim().slice(clean.length);
818
+ }
819
+ function rewriteCssEntry(absDir, entryPath, css, urlFor) {
820
+ const result = rewriteCssUrls(
821
+ css,
822
+ resolve(absDir, dirname(entryPath)),
823
+ entryPath,
824
+ (raw) => localTarget(raw, (clean) => posix.join(posix.dirname(entryPath), clean), urlFor)
825
+ );
826
+ return result.modified ? result.css : null;
827
+ }
828
+ function rewriteHtmlEntry(absDir, entryPath, html, urlFor, fileMap) {
829
+ const referrerAbsDir = resolve(absDir, dirname(entryPath));
830
+ const resolveTarget = (raw) => localTarget(raw, (clean) => rewriteAssetPath(entryPath, clean, (p) => fileMap.has(p)), urlFor);
831
+ const { document } = parseHTML(html);
832
+ const attrs = rewriteHtmlAttributes(document, referrerAbsDir, entryPath, resolveTarget, {
833
+ attrs: ["src", "poster", "href"]
834
+ });
835
+ const srcsets = rewriteSrcsets(document, resolveTarget);
836
+ const styles = rewriteStyleBlocks(document, referrerAbsDir, entryPath, resolveTarget);
837
+ let scripts = false;
838
+ for (const script of document.querySelectorAll("script")) {
839
+ if (script.hasAttribute("src")) continue;
840
+ const next = rewriteStringLiterals(script.textContent ?? "", urlFor);
841
+ if (next === null) continue;
842
+ script.textContent = next;
843
+ scripts = true;
844
+ }
845
+ return attrs || srcsets || styles || scripts ? document.toString() : null;
846
+ }
847
+ var STRING_LITERAL_RE = /(["'`])([^"'`\\\n$]+)\1/g;
848
+ function rewriteStringLiterals(code, urlFor) {
849
+ let modified = false;
850
+ const next = code.replace(STRING_LITERAL_RE, (full, quote, value) => {
851
+ const url = localTarget(value, (clean) => clean, urlFor);
852
+ if (!url) return full;
853
+ modified = true;
854
+ return `${quote}${url}${quote}`;
855
+ });
856
+ return modified ? next : null;
857
+ }
858
+ function rewriteSrcsets(document, resolveTarget) {
859
+ let modified = false;
860
+ for (const el of document.querySelectorAll("[srcset]")) {
861
+ const srcset = el.getAttribute("srcset") ?? "";
862
+ if (srcset.includes("data:")) continue;
863
+ let changed = false;
864
+ const next = srcset.split(",").map((part) => {
865
+ const [url, ...descriptors] = part.trim().split(/\s+/);
866
+ const target = url ? resolveTarget(url, "") : null;
867
+ if (!target) return part.trim();
868
+ changed = true;
869
+ return [target, ...descriptors].join(" ");
870
+ }).join(", ");
871
+ if (changed) {
872
+ el.setAttribute("srcset", next);
873
+ modified = true;
874
+ }
875
+ }
876
+ return modified;
877
+ }
878
+
879
+ // src/commands/cloud/render.ts
880
+ import { isAbsolute, relative as relative2, resolve as resolvePath } from "path";
881
+ import { existsSync } from "fs";
882
+ var VALID_QUALITY = ["draft", "standard", "high"];
883
+ var VALID_FORMAT = ["mp4", "webm", "mov"];
884
+ var VALID_RESOLUTION = ["1080p", "4k"];
885
+ var VALID_ASPECT_RATIO = ["16:9", "9:16", "1:1"];
886
+ var DIRECT_UPLOAD_LIMIT_BYTES = 200 * 1024 * 1024;
887
+ var ARCHIVE_DIAGNOSTIC_THRESHOLD_BYTES = 150 * 1024 * 1024;
888
+ var LARGEST_FILE_COUNT = 10;
889
+ var FORMAT_EXT = { mp4: ".mp4", webm: ".webm", mov: ".mov" };
890
+ var examples = [
891
+ ["Render the current directory in the cloud", "hyperframes cloud render"],
892
+ ["Inspect archive size without uploading", "hyperframes cloud render --dry-run"],
893
+ [
894
+ "Pick a specific composition + output path",
895
+ "hyperframes cloud render . --composition compositions/intro.html -o ./renders/intro.mp4"
896
+ ],
897
+ ["Higher quality, 60fps", "hyperframes cloud render --quality high --fps 60"],
898
+ [
899
+ "Submit and exit; webhook fires when the render terminates",
900
+ "hyperframes cloud render --callback-url https://example.com/hook --no-wait"
901
+ ],
902
+ [
903
+ "Override variables (parametrized render)",
904
+ `hyperframes cloud render --variables '{"title":"Q4 Recap","theme":"dark"}'`
905
+ ],
906
+ ["Re-render an already-uploaded zip", "hyperframes cloud render --asset-id asst_abc123"]
907
+ ];
908
+ var render_default = defineCommand({
909
+ meta: { name: "render", description: "Render a HyperFrames composition in the cloud" },
910
+ args: {
911
+ dir: { type: "positional", required: false, description: "Project directory (default: .)" },
912
+ fps: { type: "string", description: "Frames per second (1-240). Default: 30." },
913
+ quality: { type: "string", description: "draft | standard | high (default: standard)" },
914
+ format: { type: "string", description: "mp4 | webm | mov (default: mp4)" },
915
+ resolution: {
916
+ type: "string",
917
+ description: "Resolution tier: 1080p | 4k (default: 1080p; 4k is billed at 1.5x)"
918
+ },
919
+ "aspect-ratio": {
920
+ type: "string",
921
+ description: "Aspect ratio: 16:9 | 9:16 | 1:1 (default: 16:9)"
922
+ },
923
+ composition: {
924
+ type: "string",
925
+ alias: "c",
926
+ description: "Entry HTML file inside the zip (default: index.html)"
927
+ },
928
+ variables: {
929
+ type: "string",
930
+ description: `Inline JSON object overriding data-composition-variables. Example: --variables '{"title":"X"}'`
931
+ },
932
+ "variables-file": {
933
+ type: "string",
934
+ description: "Path to a JSON file with variable values (alternative to --variables)"
935
+ },
936
+ "strict-variables": {
937
+ type: "boolean",
938
+ description: "Fail when --variables keys are undeclared or have the wrong type",
939
+ default: false
940
+ },
941
+ title: {
942
+ type: "string",
943
+ description: "Free-text label echoed back in detail responses"
944
+ },
945
+ "callback-url": {
946
+ type: "string",
947
+ description: "HTTPS webhook fired when the render terminates. Fires regardless of whether the CLI is still polling \u2014 combine with --no-wait for true fire-and-forget."
948
+ },
949
+ "callback-id": {
950
+ type: "string",
951
+ description: "Opaque tracking ID echoed in webhook payloads"
952
+ },
953
+ "asset-id": {
954
+ type: "string",
955
+ description: "Skip zip+upload and submit an already-uploaded composition. Mutually exclusive with --url and the project dir."
956
+ },
957
+ url: {
958
+ type: "string",
959
+ description: "Public HTTPS URL of a composition zip. Mutually exclusive with --asset-id and the project dir."
960
+ },
961
+ // Citty parses `--no-FOO` as `--FOO=false`. A flag literally named
962
+ // "no-wait" gets routed as `args.wait=false`, leaving
963
+ // `args["no-wait"]` undefined and the early-return for
964
+ // fire-and-forget mode unreachable. Named the arg `wait` so the
965
+ // user-facing `--no-wait` flag works via citty's negation; the
966
+ // run() body checks `if (!args.wait)`.
967
+ wait: {
968
+ type: "boolean",
969
+ description: "Poll until completion and download the video (default: true). Pass `--no-wait` for fire-and-forget \u2014 submits and exits with the render_id.",
970
+ default: true
971
+ },
972
+ output: {
973
+ type: "string",
974
+ alias: "o",
975
+ description: "Destination path for the downloaded video (default: renders/<render_id>.<ext>)"
976
+ },
977
+ "poll-interval": {
978
+ type: "string",
979
+ description: `Poll cadence in seconds (default: ${DEFAULT_POLL_INTERVAL_MS / 1e3})`
980
+ },
981
+ "max-wait": {
982
+ type: "string",
983
+ description: `Max poll duration in minutes (default: ${DEFAULT_MAX_WAIT_MS / 6e4})`
984
+ },
985
+ json: {
986
+ type: "boolean",
987
+ description: "Emit machine-readable JSON instead of human-friendly progress",
988
+ default: false
989
+ },
990
+ "dry-run": {
991
+ type: "boolean",
992
+ description: "Build and inspect the project zip without authenticating or uploading",
993
+ default: false
994
+ },
995
+ "idempotency-key": {
996
+ type: "string",
997
+ description: "Optional Idempotency-Key for safe retries (1-255 chars from [A-Za-z0-9_:.-])"
998
+ }
999
+ },
1000
+ // fallow-ignore-next-line complexity
1001
+ async run({ args }) {
1002
+ const asJson = Boolean(args.json);
1003
+ const fps = parseIntFlag(args.fps, { flag: "--fps", min: 1, max: 240 });
1004
+ const quality = parseEnumFlag(args.quality, VALID_QUALITY, { flag: "--quality" });
1005
+ const format = parseEnumFlag(args.format, VALID_FORMAT, { flag: "--format" });
1006
+ const resolution = parseEnumFlag(args.resolution, VALID_RESOLUTION, {
1007
+ flag: "--resolution"
1008
+ });
1009
+ const explicitAspectRatio = parseEnumFlag(args["aspect-ratio"], VALID_ASPECT_RATIO, {
1010
+ flag: "--aspect-ratio"
1011
+ });
1012
+ const pollIntervalMs = parsePollIntervalMs(args["poll-interval"]);
1013
+ const maxWaitMs = parseMaxWaitMs(args["max-wait"]);
1014
+ validateIdempotencyKey(args["idempotency-key"]);
1015
+ const project = resolveProjectInput({
1016
+ dir: args.dir,
1017
+ assetId: args["asset-id"],
1018
+ url: args.url
1019
+ });
1020
+ validateDryRunSource(project, args["dry-run"] ?? false);
1021
+ validateResolutionFormatCombo(resolution, format);
1022
+ const aspectRatio = resolveAspectRatioForSubmit(
1023
+ project,
1024
+ args.composition,
1025
+ explicitAspectRatio,
1026
+ asJson
1027
+ );
1028
+ if (args["dry-run"]) {
1029
+ if (project.kind !== "dir") throw new Error("Dry-run project must be a local directory");
1030
+ reportDryRun(await prepareLocalArchive(project, args.composition, asJson), asJson);
1031
+ return;
1032
+ }
1033
+ const variables = resolveVariablesAndValidateIfLocal(
1034
+ args.variables,
1035
+ args["variables-file"],
1036
+ args["strict-variables"] ?? false,
1037
+ project
1038
+ );
1039
+ const client = await createCloudClient();
1040
+ const preparedArchive = project.kind === "dir" ? await prepareLocalArchive(
1041
+ project,
1042
+ args.composition,
1043
+ asJson,
1044
+ (file) => uploadAssetViaDirectUpload({ client, ...file })
1045
+ ) : void 0;
1046
+ const upload = await maybeUploadProject(
1047
+ client,
1048
+ project,
1049
+ preparedArchive,
1050
+ asJson,
1051
+ args["idempotency-key"]
1052
+ );
1053
+ const submitted = await submitRender(client, {
1054
+ projectInput: upload.projectInput,
1055
+ fps,
1056
+ quality,
1057
+ format,
1058
+ resolution,
1059
+ aspectRatio,
1060
+ composition: args.composition,
1061
+ variables,
1062
+ title: args.title,
1063
+ callbackUrl: args["callback-url"],
1064
+ callbackId: args["callback-id"],
1065
+ idempotencyKey: args["idempotency-key"]
1066
+ });
1067
+ const renderId = submitted.render_id;
1068
+ if (!args.wait) {
1069
+ if (asJson) {
1070
+ console.log(
1071
+ JSON.stringify(
1072
+ withMeta({ render: { render_id: renderId, status: "queued" } }),
1073
+ null,
1074
+ 2
1075
+ )
1076
+ );
1077
+ } else {
1078
+ console.log("");
1079
+ console.log(`${c.success("\u2713")} Submitted ${c.accent(renderId)}`);
1080
+ console.log(c.dim(` Poll with: hyperframes cloud get ${renderId}`));
1081
+ }
1082
+ return;
1083
+ }
1084
+ if (!asJson) {
1085
+ console.log("");
1086
+ console.log(c.dim(` Polling ${renderId} every ${pollIntervalMs / 1e3}s \u2026`));
1087
+ }
1088
+ const detail = await pollWithProgress(client, renderId, asJson, {
1089
+ intervalMs: pollIntervalMs,
1090
+ maxWaitMs
1091
+ });
1092
+ if (detail.status === "failed") {
1093
+ handleFailedRender(detail, asJson);
1094
+ }
1095
+ if (!detail.video_url) {
1096
+ errorBox(
1097
+ "Render completed but returned no video_url",
1098
+ `render_id: ${renderId}. Try \`hyperframes cloud get ${renderId}\` to inspect raw fields.`
1099
+ );
1100
+ failCommand();
1101
+ }
1102
+ const outputPath = resolveOutputPath(args.output, renderId, detail.format);
1103
+ const downloadResult = await streamVideo(detail.video_url, outputPath, asJson);
1104
+ if (asJson) {
1105
+ console.log(
1106
+ JSON.stringify(
1107
+ withMeta({
1108
+ render: detail,
1109
+ output_path: outputPath,
1110
+ bytes_written: downloadResult.bytes
1111
+ }),
1112
+ null,
1113
+ 2
1114
+ )
1115
+ );
1116
+ }
1117
+ }
1118
+ });
1119
+ function parsePollIntervalMs(raw) {
1120
+ const n = parseNumericFlag(raw, { flag: "--poll-interval", min: 1 });
1121
+ return n === void 0 ? DEFAULT_POLL_INTERVAL_MS : Math.round(n * 1e3);
1122
+ }
1123
+ function parseMaxWaitMs(raw) {
1124
+ const n = parseNumericFlag(raw, { flag: "--max-wait", min: 1e-4 });
1125
+ return n === void 0 ? DEFAULT_MAX_WAIT_MS : Math.round(n * 6e4);
1126
+ }
1127
+ var IDEMPOTENCY_KEY_RE = /^[A-Za-z0-9_:.-]{1,255}$/;
1128
+ function validateIdempotencyKey(key) {
1129
+ if (key === void 0) return;
1130
+ if (!IDEMPOTENCY_KEY_RE.test(key)) {
1131
+ errorBox("Invalid --idempotency-key", `Got "${key}". Must be 1-255 chars from [A-Za-z0-9_:.-]`);
1132
+ failCommand();
1133
+ }
1134
+ }
1135
+ function validateDryRunSource(source, dryRun) {
1136
+ if (!dryRun || source.kind === "dir") return;
1137
+ errorBox(
1138
+ "Invalid --dry-run input",
1139
+ "--dry-run inspects a local project directory and cannot be combined with --asset-id or --url."
1140
+ );
1141
+ failCommand();
1142
+ }
1143
+ function resolveProjectInput(opts) {
1144
+ const explicit = {
1145
+ dir: opts.dir !== void 0 && opts.dir !== "",
1146
+ assetId: opts.assetId !== void 0 && opts.assetId !== "",
1147
+ url: opts.url !== void 0 && opts.url !== ""
1148
+ };
1149
+ const count = Number(explicit.dir) + Number(explicit.assetId) + Number(explicit.url);
1150
+ if (count > 1) {
1151
+ errorBox("Conflicting inputs", "Pass only one of: project dir, --asset-id, --url.");
1152
+ failCommand();
1153
+ }
1154
+ if (explicit.assetId) return { kind: "asset_id", assetId: opts.assetId };
1155
+ if (explicit.url) return { kind: "url", url: opts.url };
1156
+ return { kind: "dir", dir: opts.dir ?? "." };
1157
+ }
1158
+ function resolveAspectRatioForSubmit(project, compositionArg, explicit, asJson) {
1159
+ if (project.kind !== "dir") {
1160
+ if (!explicit) {
1161
+ const reason = project.kind === "asset_id" ? "--asset-id" : "--url";
1162
+ logDetection(asJson, `Auto-detect skipped (project is ${reason})`);
1163
+ }
1164
+ return explicit;
1165
+ }
1166
+ const dir = project.dir ?? ".";
1167
+ const entryRelative = compositionArg ?? "index.html";
1168
+ const entryPath = resolvePath(dir, entryRelative);
1169
+ if (!existsSync(entryPath)) {
1170
+ errorBox(
1171
+ "Composition not found",
1172
+ `Entry file "${entryRelative}" does not exist in ${dir}.`,
1173
+ "Pass --composition with a path that exists inside the project, or omit it to use index.html."
1174
+ );
1175
+ failCommand();
1176
+ }
1177
+ const detection = detectAspectRatioFromHtml(entryPath);
1178
+ if (explicit) {
1179
+ const conflictDetail = detection.kind === "matched" && detection.aspectRatio !== explicit ? `${detection.width}\xD7${detection.height} \u2192 ${detection.aspectRatio}` : detection.kind === "no-match" ? `${detection.width}\xD7${detection.height}, ratio ${detection.ratio.toFixed(2)} \u2014 not a supported ratio` : void 0;
1180
+ if (conflictDetail) {
1181
+ errorBox(
1182
+ "Aspect ratio mismatch",
1183
+ `--aspect-ratio ${explicit} doesn't match the composition (${conflictDetail}).`,
1184
+ "The renderer matches the composition's authored aspect ratio \u2014 it can't reshape it. Drop --aspect-ratio (it's auto-detected) or re-author the composition at the target ratio."
1185
+ );
1186
+ failCommand();
1187
+ }
1188
+ return explicit;
1189
+ }
1190
+ logDetection(asJson, summarizeDetection(detection, entryRelative));
1191
+ return detection.kind === "matched" ? detection.aspectRatio : void 0;
1192
+ }
1193
+ function validateResolutionFormatCombo(resolution, format) {
1194
+ if (resolution === "4k" && (format === "webm" || format === "mov")) {
1195
+ errorBox(
1196
+ "Unsupported combination",
1197
+ `--resolution 4k cannot be combined with --format ${format}.`,
1198
+ "The alpha (webm/mov) capture path doesn't support 4k supersampling. Render 4k as mp4, or render alpha at composition resolution."
1199
+ );
1200
+ failCommand();
1201
+ }
1202
+ }
1203
+ var ASPECT_FALLBACK_HINT = "server will default aspect_ratio to 16:9. Pass --aspect-ratio to override.";
1204
+ function logDetection(asJson, message) {
1205
+ if (asJson) return;
1206
+ const suffix = message.startsWith("Detected aspect ratio") ? "" : `; ${ASPECT_FALLBACK_HINT}`;
1207
+ console.log(c.dim(` ${message}${suffix}`));
1208
+ }
1209
+ function summarizeDetection(detection, entryRelative) {
1210
+ switch (detection.kind) {
1211
+ case "matched":
1212
+ return `Detected aspect ratio: ${detection.aspectRatio} (from ${entryRelative} dims ${detection.width}\xD7${detection.height})`;
1213
+ case "no-root-div":
1214
+ return `No <div data-composition-id> found in ${entryRelative}`;
1215
+ case "no-dims":
1216
+ return `${entryRelative} root composition has no data-width / data-height`;
1217
+ case "invalid-dims":
1218
+ return `${entryRelative} root has invalid dims (${detection.width}\xD7${detection.height})`;
1219
+ case "no-match":
1220
+ return `${entryRelative} dims ${detection.width}\xD7${detection.height} (ratio ${detection.ratio.toFixed(2)}) don't match 16:9, 9:16, or 1:1`;
1221
+ case "read-error":
1222
+ return `Couldn't read ${entryRelative} for aspect-ratio detection (${detection.error})`;
1223
+ }
1224
+ }
1225
+ function resolveVariablesAndValidateIfLocal(inline, filePath, strict, source) {
1226
+ const variables = resolveVariablesArg(inline, filePath);
1227
+ if (!variables || Object.keys(variables).length === 0) return variables;
1228
+ if (source.kind !== "dir") return variables;
1229
+ const { indexPath } = resolveProject(source.dir);
1230
+ const issues = validateVariablesAgainstProject(indexPath, variables);
1231
+ reportVariableIssues(issues, { strict, quiet: false });
1232
+ return variables;
1233
+ }
1234
+ function summarizeLargestFiles(fileMap) {
1235
+ return [...fileMap.entries()].map(([path, content]) => ({ path, size_bytes: stagedSize(content) })).sort((a, b) => b.size_bytes - a.size_bytes || a.path.localeCompare(b.path)).slice(0, LARGEST_FILE_COUNT);
1236
+ }
1237
+ async function prepareLocalArchive(source, composition, asJson, upload) {
1238
+ const project = resolveProject(source.dir);
1239
+ if (!asJson) {
1240
+ console.log("");
1241
+ console.log(`${c.accent("\u25C6")} Zipping ${c.accent(project.name)}`);
1242
+ }
1243
+ const { fileMap, entryPath } = stageProjectFiles(project, composition);
1244
+ const offloaded = await offloadMedia(project, fileMap, entryPath, asJson, upload);
1245
+ const onDiskBytes = [...fileMap.values()].filter((file) => !Buffer.isBuffer(file)).reduce((sum, file) => sum + stagedSize(file), 0);
1246
+ const archive = onDiskBytes > DIRECT_UPLOAD_LIMIT_BYTES ? void 0 : zipPublishFileMap(new Map([...fileMap].map(([path, file]) => [path, readStaged(file)])));
1247
+ const sizeBytes = archive?.buffer.byteLength ?? onDiskBytes;
1248
+ if (!asJson) {
1249
+ console.log(c.dim(` ${fileMap.size} files \xB7 ${formatBytes(sizeBytes)}`));
1250
+ }
1251
+ const largestFiles = summarizeLargestFiles(fileMap);
1252
+ const prepared = {
1253
+ project,
1254
+ archive,
1255
+ sizeBytes,
1256
+ fileCount: fileMap.size,
1257
+ largestFiles,
1258
+ offloaded
1259
+ };
1260
+ if (upload) assertArchiveFits(sizeBytes, largestFiles);
1261
+ if (!asJson && sizeBytes >= ARCHIVE_DIAGNOSTIC_THRESHOLD_BYTES) {
1262
+ reportLargestFiles(prepared);
1263
+ }
1264
+ return prepared;
1265
+ }
1266
+ function stageProjectFiles(project, composition) {
1267
+ try {
1268
+ const fileMap = stagePublishFiles(project.dir, OFFLOAD_MIN_BYTES);
1269
+ const entryPath = relative2(
1270
+ project.dir,
1271
+ resolvePath(project.dir, composition ?? "index.html")
1272
+ ).replaceAll("\\", "/");
1273
+ if (!fileMap.has(entryPath)) {
1274
+ throw new Error(
1275
+ `Composition "${composition ?? "index.html"}" is excluded from the archive. Check .hyperframesignore.`
1276
+ );
1277
+ }
1278
+ return { fileMap, entryPath };
1279
+ } catch (err) {
1280
+ const msg = normalizeErrorMessage(err);
1281
+ errorBox("Zip failed", msg, "Check the project and .hyperframesignore for missing files.");
1282
+ failCommand();
1283
+ }
1284
+ }
1285
+ async function offloadMedia(project, fileMap, entryPath, asJson, upload) {
1286
+ let offloaded;
1287
+ try {
1288
+ offloaded = await offloadHeavyMedia(project.dir, fileMap, {
1289
+ apiBaseUrl: resolveCloudBaseUrl(),
1290
+ upload,
1291
+ entryPath,
1292
+ onFile: asJson ? void 0 : (item) => console.log(c.dim(` ${describeOffload(item, !upload)}`))
1293
+ });
1294
+ } catch (err) {
1295
+ reportApiError("Media upload failed", err);
1296
+ }
1297
+ if (!asJson) {
1298
+ for (const item of offloaded.filter((entry) => entry.kept_in_archive)) {
1299
+ console.log(
1300
+ c.dim(
1301
+ ` ${item.path} also stays in the zip: a script builds its path in a way that cannot be rewritten`
1302
+ )
1303
+ );
1304
+ }
1305
+ }
1306
+ return offloaded;
1307
+ }
1308
+ function assertArchiveFits(sizeBytes, largestFiles) {
1309
+ if (sizeBytes <= DIRECT_UPLOAD_LIMIT_BYTES) return;
1310
+ errorBox(
1311
+ "Project too large for cloud render",
1312
+ `The zip is ${formatBytes(sizeBytes)} after moving media to HeyGen assets, over the ${formatBytes(DIRECT_UPLOAD_LIMIT_BYTES)} upload limit. Largest files left in it:
1313
+ ${largestFiles.map((file) => ` ${formatBytes(file.size_bytes).padStart(9)} ${file.path}`).join("\n")}`,
1314
+ "Exclude files the render does not need with .hyperframesignore."
1315
+ );
1316
+ failCommand();
1317
+ }
1318
+ function describeOffload(item, dryRun) {
1319
+ const verb = item.action === "cached" ? "already uploaded" : item.action === "reencode" ? dryRun ? "would re-encode and upload" : "re-encoded and uploaded" : dryRun ? "would upload" : "uploaded";
1320
+ return `${formatBytes(item.size_bytes).padStart(9)} ${item.path} \xB7 ${verb}`;
1321
+ }
1322
+ function reportLargestFiles(prepared) {
1323
+ console.log(c.dim(" Largest included files:"));
1324
+ for (const file of prepared.largestFiles) {
1325
+ console.log(c.dim(` ${formatBytes(file.size_bytes).padStart(9)} ${file.path}`));
1326
+ }
1327
+ console.log(c.dim(" Exclude verified-unneeded files with .hyperframesignore."));
1328
+ }
1329
+ function reportDryRun(prepared, asJson) {
1330
+ const { sizeBytes } = prepared;
1331
+ if (asJson) {
1332
+ console.log(
1333
+ JSON.stringify(
1334
+ withMeta({
1335
+ archive: {
1336
+ project: prepared.project.name,
1337
+ file_count: prepared.fileCount,
1338
+ size_bytes: sizeBytes,
1339
+ upload_limit_bytes: DIRECT_UPLOAD_LIMIT_BYTES,
1340
+ exceeds_upload_limit: sizeBytes > DIRECT_UPLOAD_LIMIT_BYTES,
1341
+ largest_files: prepared.largestFiles,
1342
+ offloaded_media: prepared.offloaded
1343
+ }
1344
+ }),
1345
+ null,
1346
+ 2
1347
+ )
1348
+ );
1349
+ return;
1350
+ }
1351
+ if (sizeBytes < ARCHIVE_DIAGNOSTIC_THRESHOLD_BYTES) reportLargestFiles(prepared);
1352
+ console.log("");
1353
+ console.log(`${c.success("\u2713")} Dry run complete \u2014 nothing uploaded`);
1354
+ if (sizeBytes > DIRECT_UPLOAD_LIMIT_BYTES) {
1355
+ console.log(c.warn(` Archive exceeds the 200 MB direct-upload limit.`));
1356
+ }
1357
+ }
1358
+ async function maybeUploadProject(client, source, preparedArchive, asJson, idempotencyKey) {
1359
+ if (source.kind === "asset_id") {
1360
+ return { projectInput: { type: "asset_id", asset_id: source.assetId } };
1361
+ }
1362
+ if (source.kind === "url") {
1363
+ return { projectInput: { type: "url", url: source.url } };
1364
+ }
1365
+ if (!preparedArchive) throw new Error("Local project archive was not prepared");
1366
+ const { project, archive } = preparedArchive;
1367
+ if (!archive) throw new Error("Local project archive is over the upload limit");
1368
+ if (!asJson) {
1369
+ console.log("");
1370
+ console.log(`${c.accent("\u25C6")} Uploading (direct-to-S3)`);
1371
+ }
1372
+ const uploadStart = Date.now();
1373
+ let uploaded;
1374
+ try {
1375
+ uploaded = await uploadZipViaDirectUpload({
1376
+ client,
1377
+ bytes: archive.buffer,
1378
+ filename: `${project.name}.zip`,
1379
+ idempotencyKey,
1380
+ onProgress: !asJson ? (ev) => {
1381
+ if (ev.phase === "initialize") {
1382
+ console.log(c.dim(` initializing\u2026`));
1383
+ } else if (ev.phase === "upload" && ev.percent === 0) {
1384
+ console.log(c.dim(` uploading to S3\u2026`));
1385
+ } else if (ev.phase === "complete") {
1386
+ console.log(c.dim(` finalizing\u2026`));
1387
+ }
1388
+ } : void 0
1389
+ });
1390
+ } catch (err) {
1391
+ reportApiError("Upload failed", err);
1392
+ }
1393
+ if (!asJson) {
1394
+ console.log(
1395
+ c.dim(
1396
+ ` asset_id: ${c.accent(uploaded.asset_id)} \xB7 ${formatDuration(Date.now() - uploadStart)}`
1397
+ )
1398
+ );
1399
+ }
1400
+ return { projectInput: { type: "asset_id", asset_id: uploaded.asset_id } };
1401
+ }
1402
+ async function submitRender(client, opts) {
1403
+ const body = buildRenderBody(opts);
1404
+ try {
1405
+ return await client.createRender({ body, idempotencyKey: opts.idempotencyKey });
1406
+ } catch (err) {
1407
+ reportApiError("Submit failed", err);
1408
+ }
1409
+ }
1410
+ function buildRenderBody(opts) {
1411
+ const body = { project: opts.projectInput };
1412
+ if (opts.fps !== void 0) body.fps = opts.fps;
1413
+ if (opts.quality !== void 0) body.quality = opts.quality;
1414
+ if (opts.format !== void 0) body.format = opts.format;
1415
+ if (opts.resolution !== void 0) body.resolution = opts.resolution;
1416
+ if (opts.aspectRatio !== void 0) body.aspect_ratio = opts.aspectRatio;
1417
+ if (opts.composition !== void 0) body.composition = opts.composition;
1418
+ if (opts.variables !== void 0) body.variables = opts.variables;
1419
+ if (opts.title !== void 0) body.title = opts.title;
1420
+ if (opts.callbackUrl !== void 0) body.callback_url = opts.callbackUrl;
1421
+ if (opts.callbackId !== void 0) body.callback_id = opts.callbackId;
1422
+ return body;
1423
+ }
1424
+ async function pollWithProgress(client, renderId, asJson, poll) {
1425
+ const interactive = !asJson && process.stdout.isTTY === true;
1426
+ let lastStatus = "";
1427
+ try {
1428
+ return await pollUntilTerminal(client, renderId, {
1429
+ intervalMs: poll.intervalMs,
1430
+ maxWaitMs: poll.maxWaitMs,
1431
+ // fallow-ignore-next-line complexity
1432
+ onTick: (detail, elapsedMs) => {
1433
+ if (asJson) return;
1434
+ if (interactive) {
1435
+ if (detail.status === lastStatus) {
1436
+ process.stdout.write(`\r\x1B[2K ${formatTickLine(detail, elapsedMs)}`);
1437
+ } else {
1438
+ if (lastStatus) process.stdout.write("\n");
1439
+ process.stdout.write(` ${formatTickLine(detail, elapsedMs)}`);
1440
+ lastStatus = detail.status;
1441
+ }
1442
+ } else if (detail.status !== lastStatus) {
1443
+ console.log(` ${formatTickLine(detail, elapsedMs)}`);
1444
+ lastStatus = detail.status;
1445
+ }
1446
+ }
1447
+ });
1448
+ } catch (err) {
1449
+ if (!asJson && lastStatus && interactive) process.stdout.write("\n");
1450
+ if (err instanceof PollTimeoutError) {
1451
+ errorBox(
1452
+ "Poll timed out",
1453
+ err.message,
1454
+ `The render may still complete. Resume with: hyperframes cloud get ${renderId}`
1455
+ );
1456
+ failCommand();
1457
+ }
1458
+ return reportApiError("API error during poll", err, {
1459
+ suggestion: `The render may still be running. Resume with: hyperframes cloud get ${renderId}`
1460
+ });
1461
+ } finally {
1462
+ if (!asJson && lastStatus && interactive) process.stdout.write("\n");
1463
+ }
1464
+ }
1465
+ function formatTickLine(detail, elapsedMs) {
1466
+ const status = colorStatus(detail.status);
1467
+ return `${status} ${c.dim(formatDuration(elapsedMs))}`;
1468
+ }
1469
+ function handleFailedRender(detail, asJson) {
1470
+ if (asJson) {
1471
+ console.log(JSON.stringify(withMeta({ render: detail }), null, 2));
1472
+ failCommand();
1473
+ }
1474
+ errorBox(
1475
+ "Render failed",
1476
+ detail.failure_message ?? "(no failure_message returned)",
1477
+ `Inspect: hyperframes cloud get ${detail.render_id}`
1478
+ );
1479
+ failCommand();
1480
+ }
1481
+ function resolveOutputPath(output, renderId, format) {
1482
+ if (output) {
1483
+ return isAbsolute(output) ? output : resolvePath(process.cwd(), output);
1484
+ }
1485
+ const ext = FORMAT_EXT[format] ?? `.${format}`;
1486
+ return resolvePath(process.cwd(), "renders", `${renderId}${ext}`);
1487
+ }
1488
+ async function streamVideo(url, destPath, asJson) {
1489
+ if (!asJson) {
1490
+ console.log("");
1491
+ console.log(`${c.accent("\u25C6")} Downloading to ${c.accent(destPath)}`);
1492
+ }
1493
+ try {
1494
+ const result = await downloadToFile(url, destPath);
1495
+ if (!asJson) {
1496
+ console.log(c.dim(` ${formatBytes(result.bytes)} written`));
1497
+ }
1498
+ return { bytes: result.bytes };
1499
+ } catch (err) {
1500
+ const message = normalizeErrorMessage(err);
1501
+ errorBox(
1502
+ "Download failed",
1503
+ message,
1504
+ "The presigned URL is short-lived; re-fetch with `hyperframes cloud get`."
1505
+ );
1506
+ failCommand();
1507
+ }
1508
+ }
1509
+ export {
1510
+ assertArchiveFits,
1511
+ render_default as default,
1512
+ examples,
1513
+ resolveAspectRatioForSubmit,
1514
+ validateDryRunSource,
1515
+ validateResolutionFormatCombo
1516
+ };