hyperframes 0.8.139 → 0.8.140

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 (195) hide show
  1. package/dist/{add-VYJQ4S3P.js → add-7SPPQB64.js} +20 -22
  2. package/dist/{animatedGifPrep-IBMW6FZB.js → animatedGifPrep-HN3CJSFX.js} +15 -8
  3. package/dist/{asset-FLSUS73X.js → asset-AZMFUTRL.js} +3 -3
  4. package/dist/{assetCataloger-TGD7TRCR.js → assetCataloger-TQUWYLHE.js} +6 -3
  5. package/dist/{auth-EZXIN4M4.js → auth-XGY74R4A.js} +7 -7
  6. package/dist/{autoUpdate-ZPMY5T3X.js → autoUpdate-44NTJBQU.js} +2 -2
  7. package/dist/{backgroundChecks-JIDTAUFE.js → backgroundChecks-ET4DJEPG.js} +3 -3
  8. package/dist/backgroundChecksWorker.js +3 -3
  9. package/dist/{batchRender-FNZ6TJYY.js → batchRender-HTWWNF3P.js} +11 -8
  10. package/dist/beat-analyzer.global.js +5 -2
  11. package/dist/{beats-EAA4U3YN.js → beats-4GYTZUZG.js} +24 -17
  12. package/dist/{benchmark-KKZQVK2Y.js → benchmark-CJQHE3QN.js} +20 -23
  13. package/dist/{browser-ZIVVJLJT.js → browser-NXOVPGDG.js} +14 -28
  14. package/dist/{bundleWithLocalizedFonts-2XWC3PEM.js → bundleWithLocalizedFonts-6PJP54TC.js} +5 -5
  15. package/dist/{capture-MKJHABUL.js → capture-5EHGRJZM.js} +2 -2
  16. package/dist/{capture-EFYPXI42.js → capture-EHOMO4MP.js} +22 -15
  17. package/dist/{catalog-EWX4BBI2.js → catalog-PTX5Z52R.js} +28 -30
  18. package/dist/{catch-up-FJFH7T4W.js → catch-up-I45SPSCU.js} +11 -25
  19. package/dist/{check-YJNVDCBP.js → check-LJ7LG7OI.js} +72 -54
  20. package/dist/{checkBrowser-I6SPPEGD.js → checkBrowser-6EK5E4PG.js} +81 -36
  21. package/dist/chunk-24OWOBQH.js +3656 -0
  22. package/dist/{chunk-SIKMUO45.js → chunk-2GMKWYEH.js} +12 -12
  23. package/dist/{chunk-GIDHR5OL.js → chunk-5IKJONRY.js} +1 -1
  24. package/dist/{chunk-GWV2Y36Q.js → chunk-6U3MN4HW.js} +5 -106
  25. package/dist/chunk-A4KQZXZI.js +116 -0
  26. package/dist/{chunk-PFI3VD77.js → chunk-ABJNXQUA.js} +10 -10
  27. package/dist/{chunk-HOIHGPWO.js → chunk-ASW6NWON.js} +1 -1
  28. package/dist/chunk-B3I3XJ47.js +3711 -0
  29. package/dist/{chunk-3RT6D4P6.js → chunk-BJ5JWOES.js} +1 -1
  30. package/dist/{chunk-4VZBKOQJ.js → chunk-BQKEGPPL.js} +1 -1
  31. package/dist/{chunk-SHB5VDMH.js → chunk-BRITLL56.js} +1 -1
  32. package/dist/{chunk-7SI7GCX6.js → chunk-C2JUHIG5.js} +6 -6
  33. package/dist/{chunk-TCWL4BKK.js → chunk-DDQ4LOML.js} +1 -1
  34. package/dist/{chunk-CDJ7FPPJ.js → chunk-DQNOPYAG.js} +1 -1
  35. package/dist/{chunk-JH556Q42.js → chunk-EDYBFSPE.js} +2 -2
  36. package/dist/{chunk-W7SAWBQC.js → chunk-F3SPK6SO.js} +1 -1
  37. package/dist/chunk-FXMKHHPO.js +123 -0
  38. package/dist/chunk-G75ENCEZ.js +283 -0
  39. package/dist/{chunk-PLNWWGE7.js → chunk-GQJZORQP.js} +1 -1
  40. package/dist/{chunk-Y6W4DVPS.js → chunk-HKCNZITN.js} +1 -1
  41. package/dist/{chunk-U3GEXWI5.js → chunk-IFY57LUW.js} +1 -1
  42. package/dist/{chunk-UQXOHEQ7.js → chunk-IRBESGJ7.js} +4 -4
  43. package/dist/chunk-JFKL3WAE.js +161 -0
  44. package/dist/{chunk-25DHUDOK.js → chunk-JSGANFFH.js} +12 -3
  45. package/dist/{chunk-UGI77E35.js → chunk-KECV5G4V.js} +165 -59
  46. package/dist/{chunk-EZ2P7LAP.js → chunk-KFQNMY36.js} +8 -8
  47. package/dist/{chunk-FLRCVYNE.js → chunk-KJLY2SDJ.js} +2 -2
  48. package/dist/{chunk-XOZ3J3LB.js → chunk-KMKWRIMC.js} +1 -1
  49. package/dist/{chunk-O6K5DWXM.js → chunk-KOEWEFHK.js} +1 -1
  50. package/dist/{chunk-IBRZUVTH.js → chunk-L2H2L22P.js} +2 -2
  51. package/dist/{chunk-BC3OY2ED.js → chunk-LQG6GPVK.js} +1 -1
  52. package/dist/{chunk-Z67RZ4XO.js → chunk-MHK5DE2P.js} +263 -1960
  53. package/dist/{chunk-A4X2YWNQ.js → chunk-MTVDXB6S.js} +18 -289
  54. package/dist/{chunk-CDMB3PRC.js → chunk-MU6C2VP6.js} +7 -7
  55. package/dist/{chunk-UMWROSIP.js → chunk-MUINFURL.js} +2 -2
  56. package/dist/{chunk-JTAYFSLO.js → chunk-PB3P4MKP.js} +1 -1
  57. package/dist/{chunk-JIIIBTN2.js → chunk-PE2I3AFA.js} +1 -1
  58. package/dist/{chunk-OENM2JVZ.js → chunk-PFJFHNU3.js} +3 -3
  59. package/dist/{chunk-EH4VUY27.js → chunk-PPKFM5WE.js} +2 -2
  60. package/dist/{chunk-DW32KBHN.js → chunk-PUBKHEEA.js} +13 -13
  61. package/dist/{chunk-DUQJF7FK.js → chunk-QBK7WKHT.js} +8 -8
  62. package/dist/{chunk-U3HX7WE6.js → chunk-QG3PDRSO.js} +1 -1
  63. package/dist/{chunk-EJ7OWYF2.js → chunk-QP3BRKGF.js} +1 -163
  64. package/dist/chunk-QZ3AZTXI.js +1740 -0
  65. package/dist/{chunk-YOL4QRSL.js → chunk-SLEPCESN.js} +2 -2
  66. package/dist/chunk-T76PLPD6.js +57 -0
  67. package/dist/{chunk-QXOFUKBF.js → chunk-TPECLKPA.js} +168 -165
  68. package/dist/{chunk-R7LWDIKB.js → chunk-TYT2TLJR.js} +1 -1
  69. package/dist/{chunk-Q73ISOVQ.js → chunk-U7E736AJ.js} +1 -1
  70. package/dist/chunk-USDEMXXL.js +110 -0
  71. package/dist/{chunk-VTVS4IIP.js → chunk-VVJFZU6B.js} +2 -2
  72. package/dist/{chunk-JX7Y57YY.js → chunk-WKTHUC2R.js} +1 -1
  73. package/dist/{chunk-NTIOLSFE.js → chunk-WQBA3MH4.js} +3 -3
  74. package/dist/{chunk-STA43FMY.js → chunk-WY5OODN5.js} +71 -3855
  75. package/dist/{chunk-X3TMMIDP.js → chunk-X3X3VETN.js} +8 -8
  76. package/dist/{chunk-ZTZ7NCJT.js → chunk-XTFJOR3K.js} +1 -1
  77. package/dist/chunk-YA62XGXS.js +45 -0
  78. package/dist/{clean-P4KTCOUA.js → clean-Y3AQITOX.js} +29 -20
  79. package/dist/cli.js +55 -55
  80. package/dist/{cloud-PDY7S4HU.js → cloud-TDPOMBZF.js} +7 -7
  81. package/dist/{cloudrun-54MA2W5H.js → cloudrun-FPAVAVHP.js} +11 -8
  82. package/dist/commands/layout-audit.browser.js +37 -8
  83. package/dist/commands/motion-signature.browser.js +51 -2
  84. package/dist/{compare-LDWVSPC5.js → compare-LHL225ND.js} +40 -31
  85. package/dist/{compiler-CWSZ4W7J.js → compiler-RTU3KTLF.js} +7 -3
  86. package/dist/{component-FIEGC22U.js → component-L2YX7J56.js} +4 -4
  87. package/dist/{compositions-JR7PX356.js → compositions-V2UZKG7S.js} +23 -16
  88. package/dist/{delete-FXA2IHOS.js → delete-OXZEXUQY.js} +8 -8
  89. package/dist/{dist-4JZWRWS6.js → dist-N33CM63P.js} +21 -15
  90. package/dist/{dist-OJTEVNJR.js → dist-NZSQCYOF.js} +3 -1
  91. package/dist/{doctor-OTDHPM6K.js → doctor-MWTPSIVD.js} +20 -13
  92. package/dist/{events-UAFTI24U.js → events-4TZGQZWF.js} +6 -22
  93. package/dist/{events-6T7M2VCO.js → events-7VJJGC3D.js} +10 -23
  94. package/dist/{feedback-ZGZODHB6.js → feedback-HSGAPVN7.js} +67 -45
  95. package/dist/{figma-D26EJ7ZB.js → figma-AMC6FAK3.js} +6 -6
  96. package/dist/fontLocalizeCli.js +17 -10
  97. package/dist/{get-IAREU7IK.js → get-Y7DFEFYM.js} +8 -8
  98. package/dist/{gpuPolicy-NS7WZRDJ.js → gpuPolicy-A45VSUKR.js} +15 -8
  99. package/dist/{grade-compare-ZXHFHCD2.js → grade-compare-ERSA3OUI.js} +40 -31
  100. package/dist/{help-RVNYHW3N.js → help-TW2AADLZ.js} +4 -4
  101. package/dist/{history-THP4VNMF.js → history-DKGDSVWF.js} +12 -30
  102. package/dist/hyperframe-runtime.js +59 -59
  103. package/dist/hyperframe.manifest.json +1 -1
  104. package/dist/hyperframe.runtime.iife.js +59 -59
  105. package/dist/hyperframes-player.global.js +3 -3
  106. package/dist/{info-EOFRHN5A.js → info-I3NRRNX4.js} +17 -20
  107. package/dist/{init-YLIJE4CG.js → init-5Y3ZWZNF.js} +48 -41
  108. package/dist/{inspect-3D56ONJD.js → inspect-BJQKBB3X.js} +39 -30
  109. package/dist/{installer-GRR2GDZK.js → installer-27K4JS2I.js} +8 -5
  110. package/dist/{keyframes-NXNCTQPK.js → keyframes-6V2NZO22.js} +14 -25
  111. package/dist/{lambda-VGRZD7TR.js → lambda-AIMF3K4A.js} +16 -13
  112. package/dist/{layout-Z5DOS6DU.js → layout-YAPEBTU4.js} +39 -30
  113. package/dist/{lint-YD6NJGR2.js → lint-3LWWUIV7.js} +17 -23
  114. package/dist/{lintProject-ANMPLBIT.js → lintProject-LM3XZRPZ.js} +3 -1
  115. package/dist/{list-XGDKILYN.js → list-NTSGEZJ6.js} +8 -8
  116. package/dist/{login-GD5SA4RD.js → login-ZTDFGCCW.js} +9 -9
  117. package/dist/{logout-Q654A3SI.js → logout-GJXZQKOA.js} +5 -5
  118. package/dist/{media-treatment-TY4X4GQQ.js → media-treatment-ARHX446F.js} +21 -20
  119. package/dist/{models-MXRDH4WJ.js → models-VHNQVTHG.js} +19 -12
  120. package/dist/{motionShot-6LKR3C2E.js → motionShot-35HCSNCI.js} +22 -15
  121. package/dist/{normalize-O76AC4HE.js → normalize-IGLHCWTF.js} +0 -4
  122. package/dist/{normalize-audio-5ADDZKPJ.js → normalize-audio-AUC73JH3.js} +13 -26
  123. package/dist/{open-JXAMO325.js → open-C5LVNU3U.js} +11 -25
  124. package/dist/{pipeline-Q5MOPIIX.js → pipeline-4GDTK54T.js} +15 -8
  125. package/dist/{play-3SPH5GWZ.js → play-U7UANF7Z.js} +38 -32
  126. package/dist/{preflight-2PWAPKWC.js → preflight-Y5DN5D37.js} +3 -18
  127. package/dist/{present-ZTJQDKXZ.js → present-X6V6TX3E.js} +32 -26
  128. package/dist/{preview-4A3XZR6J.js → preview-K43RTRU2.js} +33 -26
  129. package/dist/{producer-5ZKKKQPB.js → producer-PVMFMW23.js} +1 -1
  130. package/dist/{projectConfig-WA4CS4SN.js → projectConfig-PI6MEN2T.js} +10 -7
  131. package/dist/{publication-CLG2FRRI.js → publication-67C7KVTL.js} +6 -3
  132. package/dist/{publish-YTT2WMXO.js → publish-RBV7EAMH.js} +27 -20
  133. package/dist/{refresh-K24ETHMA.js → refresh-MFITBIKV.js} +5 -5
  134. package/dist/{remove-background-W35INR3B.js → remove-background-Z2ECZX2Q.js} +22 -15
  135. package/dist/{render-7A4P5WMU.js → render-2GQMQBU3.js} +10 -7
  136. package/dist/{render-KICXUPNR.js → render-EFU4BZRZ.js} +23 -26
  137. package/dist/{render-WUBFQMAH.js → render-YBS3XGRJ.js} +52 -32
  138. package/dist/{render-batch-N7EFGE6S.js → render-batch-QC64OTB5.js} +11 -8
  139. package/dist/{renderCancellation-LYQPPFAO.js → renderCancellation-NTVU7IMB.js} +15 -8
  140. package/dist/renderSetupWorker.js +4 -2
  141. package/dist/{resolver-YTT2NENF.js → resolver-GBWYG3EI.js} +8 -5
  142. package/dist/{sherpa-353MW4UV.js → sherpa-ZW6PWJS4.js} +16 -9
  143. package/dist/skills/hyperframes/references/capability-menu.md +1 -1
  144. package/dist/skills/hyperframes-cli/SKILL.md +5 -0
  145. package/dist/skills/hyperframes-cli/references/lint-validate-inspect.md +1 -1
  146. package/dist/skills/hyperframes-cli/references/preview-render.md +1 -1
  147. package/dist/skills/media-use/SKILL.md +6 -4
  148. package/dist/skills/media-use/audio/scripts/lib/host-audio.mjs +28 -0
  149. package/dist/skills/media-use/audio/scripts/lib/host-audio.test.mjs +106 -0
  150. package/dist/skills/media-use/references/setup-providers.md +3 -2
  151. package/dist/skills/media-use/scripts/lib/heygen-cli.mjs +21 -1
  152. package/dist/skills/media-use/scripts/lib/heygen-cli.test.mjs +14 -0
  153. package/dist/skills/media-use/scripts/resolve.mjs +11 -6
  154. package/dist/{skills-2XJIOEBT.js → skills-VKZAX5AS.js} +15 -30
  155. package/dist/{skillsUpdateCheck-LMCCWGNO.js → skillsUpdateCheck-PAY2OPDE.js} +3 -3
  156. package/dist/{snapshot-WBRSTLJ2.js → snapshot-SD5R3NWX.js} +41 -32
  157. package/dist/{src-YWFJILQF.js → src-7SSERHNP.js} +19 -11
  158. package/dist/{src-ZS3BLTZ5.js → src-RXTXL3VL.js} +16 -9
  159. package/dist/{staticProjectServer-4VKVHPNG.js → staticProjectServer-XELJXX4J.js} +24 -16
  160. package/dist/{status-73MBN37B.js → status-JA6SKQVH.js} +7 -24
  161. package/dist/studio/assets/{hyperframes-player-CckOoCJ8.js → hyperframes-player-BLuyxnK7.js} +1 -1
  162. package/dist/studio/assets/{index-DsEkE5_G.js → index-CWfuDCoA.js} +1 -1
  163. package/dist/studio/assets/{index-BmIPrb8I.js → index-Dr3Bo9uH.js} +1 -1
  164. package/dist/studio/assets/{index-Br1Whc0f.js → index-VAHDM_zL.js} +204 -204
  165. package/dist/studio/{chunk-E422JDF5.js → chunk-H2DUOXTF.js} +2 -1
  166. package/dist/studio/index.d.ts +19 -3
  167. package/dist/studio/index.html +1 -1
  168. package/dist/studio/index.js +6498 -5813
  169. package/dist/studio/index.js.map +1 -1
  170. package/dist/studio/player.js +1 -1
  171. package/dist/{studio-api-5V4TVSQV.js → studio-api-4FNUZPJ4.js} +40 -28
  172. package/dist/{studioServer-M5XY3HYC.js → studioServer-GHLL3F2M.js} +99 -68
  173. package/dist/{system-RCRNX5VF.js → system-OQLOGGPJ.js} +2 -19
  174. package/dist/{telemetry-LOBRQGUD.js → telemetry-7AO77PMH.js} +8 -23
  175. package/dist/{timeline-3JCP6PXU.js → timeline-6LTHSRXM.js} +32 -22
  176. package/dist/{tokens-MLMIJBVF.js → tokens-K6B46KNL.js} +3 -3
  177. package/dist/{transcribe-KNFXDEAX.js → transcribe-CLLISRLZ.js} +16 -9
  178. package/dist/{transcribe-POH25ZPX.js → transcribe-K2OU6FZF.js} +20 -44
  179. package/dist/{updateCheck-5ITBSBST.js → updateCheck-X6UQBS6I.js} +2 -2
  180. package/dist/{upgrade-K6OK4FKT.js → upgrade-BZFKEADI.js} +5 -5
  181. package/dist/{urlDownloader-TJJQKADT.js → urlDownloader-VAVUXYEQ.js} +14 -7
  182. package/dist/{validate-PPPOBJHB.js → validate-VO5KEXWN.js} +26 -19
  183. package/dist/{video-OB22HWXZ.js → video-JV6Y4UJR.js} +18 -11
  184. package/package.json +1 -1
  185. package/dist/chunk-HYYSJI5D.js +0 -3742
  186. package/dist/{chunk-I465DBM6.js → chunk-246MOR5U.js} +3 -3
  187. package/dist/{deploy-MX76RZ36.js → deploy-5G4Z6LPA.js} +1 -1
  188. package/dist/{destroy-H2TZBYHH.js → destroy-GFQLTIY3.js} +1 -1
  189. package/dist/{docs-QAPJEGLS.js → docs-MPOM3PHH.js} +3 -3
  190. package/dist/{policies-FSDZVS2E.js → policies-4FEXMXPK.js} +3 -3
  191. package/dist/{progress-FBIEEB7Y.js → progress-5AM2MZ7V.js} +3 -3
  192. package/dist/{sites-TNVKZS73.js → sites-HZMADOA3.js} +1 -1
  193. package/dist/studio/{chunk-E422JDF5.js.map → chunk-H2DUOXTF.js.map} +0 -0
  194. package/dist/{telemetry-2YC4SGQU.js → telemetry-W5AKPI4R.js} +3 -3
  195. package/dist/{tts-FU35O6YB.js → tts-TOHSPWEG.js} +4 -4
@@ -21,14 +21,27 @@ import {
21
21
  splitElementInHtml,
22
22
  unwrapElementsFromHtml,
23
23
  wrapElementsInHtml
24
- } from "./chunk-DUQJF7FK.js";
24
+ } from "./chunk-QBK7WKHT.js";
25
+ import {
26
+ MAX_WINDOW_IDLE_MS,
27
+ STUDIO_SIGNATURE_MANIFEST_PATHS,
28
+ UNDO_MODES,
29
+ clearFileWriteReceipt,
30
+ createProjectSignature,
31
+ createWriteToken,
32
+ fileContentVersion,
33
+ recordFileWriteReceipt,
34
+ resolveProjectAndSignature,
35
+ resolveProjectSignature,
36
+ settledFileTag
37
+ } from "./chunk-QZ3AZTXI.js";
25
38
  import {
26
39
  DEFAULT_TARGET_LUFS,
27
40
  absoluteLoudnessPlan,
28
41
  measureAudio,
29
42
  requiredFfmpeg,
30
43
  resolveLocalAudioPath
31
- } from "./chunk-Q73ISOVQ.js";
44
+ } from "./chunk-U7E736AJ.js";
32
45
  import {
33
46
  PROXY_PARAMS_VERSION,
34
47
  PROXY_PENDING_RETRY_AFTER_SECONDS,
@@ -36,34 +49,35 @@ import {
36
49
  ProxyCapacityError,
37
50
  ProxyTranscodeError,
38
51
  ProxyWaitTimeoutError,
39
- STUDIO_MOTION_PATH,
40
52
  createMediaCodecProbeCache,
41
- createStudioMotionRenderBodyScript,
42
53
  decideMediaProxyEligibility,
43
- isInHiddenOrVendorDir,
44
- isPrivateProjectFile,
45
- isPrivateProjectPath,
46
54
  isProxyVariantRequest,
47
- pinWithinProject,
48
55
  probeAssetCodec,
49
56
  probeMediaMetadata,
50
57
  proxyActivityMark,
51
58
  proxyVariantFor,
52
- realFilePath,
53
59
  recordProxyPrewarm,
54
60
  recordProxyRequest,
55
61
  resolveProxy,
56
62
  resolveProxyVariantRequest,
57
- resolveWithinProject,
58
63
  scanProjectMediaCodecMap,
59
64
  shouldPrewarmProxy,
60
- waitForProxy,
65
+ waitForProxy
66
+ } from "./chunk-MTVDXB6S.js";
67
+ import {
68
+ STUDIO_MOTION_PATH,
69
+ createStudioMotionRenderBodyScript,
70
+ isInHiddenOrVendorDir,
71
+ isPrivateProjectFile,
72
+ isPrivateProjectPath,
73
+ pinWithinProject,
74
+ realFilePath,
75
+ resolveWithinProject,
61
76
  walkDir
62
- } from "./chunk-A4X2YWNQ.js";
77
+ } from "./chunk-G75ENCEZ.js";
63
78
  import {
64
79
  atomicTempPath,
65
80
  createFileAtomically,
66
- isAtomicTempPath,
67
81
  replaceFileAtomically
68
82
  } from "./chunk-EOVYQ3IG.js";
69
83
  import {
@@ -80,22 +94,24 @@ import {
80
94
  getSystemProfilerFamilies,
81
95
  locateSystemFont
82
96
  } from "./chunk-TJSTGSAW.js";
97
+ import {
98
+ addScenePartsManifest
99
+ } from "./chunk-6U3MN4HW.js";
100
+ import {
101
+ ensureHfIds
102
+ } from "./chunk-A4KQZXZI.js";
83
103
  import {
84
104
  formatRenderOutputTimestamp,
85
105
  gsapCdnDist,
86
106
  motionPathPluginUrl,
87
107
  parseFps,
88
108
  sameInstant
89
- } from "./chunk-EJ7OWYF2.js";
109
+ } from "./chunk-QP3BRKGF.js";
90
110
  import {
91
111
  MagicString,
92
112
  VALID_CANVAS_RESOLUTIONS,
93
113
  unrollComputedTimeline
94
114
  } from "./chunk-OZUQF4NB.js";
95
- import {
96
- addScenePartsManifest,
97
- ensureHfIds
98
- } from "./chunk-GWV2Y36Q.js";
99
115
  import {
100
116
  AFTER_FONTS_SCRIPT_TYPE,
101
117
  RUNTIME_BOOTSTRAP_ATTR,
@@ -109,7 +125,7 @@ import {
109
125
  resolveRateSpec,
110
126
  sourceTimeAt,
111
127
  stripEmbeddedRuntimeScripts
112
- } from "./chunk-HYYSJI5D.js";
128
+ } from "./chunk-B3I3XJ47.js";
113
129
  import {
114
130
  ProjectRootMissingError,
115
131
  folderGone,
@@ -121,7 +137,7 @@ import {
121
137
  import {
122
138
  isWithinProjectRoot,
123
139
  readProjectFile
124
- } from "./chunk-STA43FMY.js";
140
+ } from "./chunk-JFKL3WAE.js";
125
141
  import {
126
142
  FFPROBE_PATH_ENV,
127
143
  findFfBinary
@@ -226,1701 +242,6 @@ function stampFileHfIds(filePath) {
226
242
  }
227
243
  }
228
244
 
229
- // ../studio-server/dist/chunk-K3H6HTVC.js
230
- import { randomUUID as randomUUID6 } from "crypto";
231
- import { mkdir as mkdir2, readdir as readdir3, rm as rm3, rmdir, writeFile } from "fs/promises";
232
- import {
233
- existsSync as existsSync2,
234
- lstatSync as lstatSync2,
235
- statSync as statSync3,
236
- mkdirSync as mkdirSync4,
237
- readdirSync as readdirSync3,
238
- readFileSync as readFileSync6,
239
- realpathSync,
240
- rmSync as rmSync2
241
- } from "fs";
242
- import { homedir } from "os";
243
- import { dirname as dirname4, isAbsolute as isAbsolute2, join as join5, relative as relative2, resolve as resolve3, sep as sep2 } from "path";
244
- import { createHash as createHash2, randomUUID } from "crypto";
245
- import { createHash as createHash22 } from "crypto";
246
- import { lstatSync, readFileSync as readFileSync2, readdirSync } from "fs";
247
- import { extname, isAbsolute, relative, resolve, sep } from "path";
248
- import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
249
- import { constants as constants2, createReadStream, renameSync } from "fs";
250
- import { copyFile, mkdir, readdir, readFile, rename, rm, stat } from "fs/promises";
251
- import { dirname as dirname2, join as join2 } from "path";
252
- import { randomUUID as randomUUID5 } from "crypto";
253
- import { readdirSync as readdirSync2, readFileSync as readFileSync4, renameSync as renameSync2, statSync as statSync2, writeFileSync as writeFileSync3 } from "fs";
254
- import { lstat, readdir as readdir2, rm as rm2 } from "fs/promises";
255
- import { tmpdir } from "os";
256
- import { dirname as dirname22, join as join4 } from "path";
257
- import { randomUUID as randomUUID3 } from "crypto";
258
- import { existsSync, mkdirSync, readFileSync as readFileSync22, statSync, writeFileSync as writeFileSync2 } from "fs";
259
- import { basename, join as join22, resolve as resolve2 } from "path";
260
- import { randomUUID as randomUUID4 } from "crypto";
261
- import { linkSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync22 } from "fs";
262
- import { join as join3 } from "path";
263
- import { closeSync as closeSync2, constants as constants22, mkdirSync as mkdirSync3, openSync as openSync2, readFileSync as readFileSync5, writeSync } from "fs";
264
- import { dirname as dirname3 } from "path";
265
- var RECEIPT_TTL_MS = 1e4;
266
- var receipts = /* @__PURE__ */ new Map();
267
- var overwriteListeners = /* @__PURE__ */ new Set();
268
- function fileContentVersion(content) {
269
- return hashVersion(createHash2("sha256").update(content).digest("hex"));
270
- }
271
- var DELETED_VERSION = '"deleted"';
272
- function hashVersion(hex) {
273
- return `"sha256:${hex}"`;
274
- }
275
- function hashOfVersion(version) {
276
- return /^"sha256:([0-9a-f]{64})"$/.exec(version)?.[1];
277
- }
278
- function settledFileTag(stat22, now = Date.now()) {
279
- if (now - stat22.ctimeMs < 3e3) return null;
280
- return [stat22.ino, stat22.ctimeMs, stat22.size].map((n) => n.toString(36)).join("-");
281
- }
282
- function createWriteToken(requestToken) {
283
- const token = requestToken?.trim();
284
- return token && token.length <= 200 ? token : randomUUID();
285
- }
286
- function onFileOverwritten(listener) {
287
- overwriteListeners.add(listener);
288
- return () => overwriteListeners.delete(listener);
289
- }
290
- function recordFileWriteReceipt(filePath, {
291
- overwrote,
292
- path,
293
- version,
294
- writeToken
295
- }) {
296
- const receipt = { path, version, writeToken };
297
- const absPath = realFilePath(filePath);
298
- if (overwrote !== void 0)
299
- for (const listener of overwriteListeners) listener(absPath, receipt.version, overwrote);
300
- const now = Date.now();
301
- for (const [path2, list] of receipts) {
302
- const live = list.filter((entry) => now - entry.recordedAt < RECEIPT_TTL_MS);
303
- if (live.length > 0) receipts.set(path2, live);
304
- else receipts.delete(path2);
305
- }
306
- const from = overwrote === void 0 ? void 0 : fileContentVersion(overwrote);
307
- receipts.set(absPath, [
308
- ...receipts.get(absPath) ?? [],
309
- { ...receipt, ...from && { from }, recordedAt: now }
310
- ]);
311
- }
312
- function clearFileWriteReceipt(filePath, version, writeToken) {
313
- const absPath = realFilePath(filePath);
314
- const current = (receipts.get(absPath) ?? []).filter(
315
- (entry) => entry.version !== version || entry.writeToken !== writeToken
316
- );
317
- if (current.length > 0) receipts.set(absPath, current);
318
- else receipts.delete(absPath);
319
- }
320
- function identifyFileWrite(filePath, expectedVersion) {
321
- const receipt = newestReceipt(realFilePath(filePath), expectedVersion);
322
- if (!receipt) return null;
323
- const { path, version, writeToken, from } = receipt;
324
- return { path, version, writeToken, ...from && { from } };
325
- }
326
- function newestReceipt(absPath, expectedVersion) {
327
- const now = Date.now();
328
- const current = (receipts.get(absPath) ?? []).filter(
329
- (entry) => now - entry.recordedAt < RECEIPT_TTL_MS
330
- );
331
- if (current.length > 0) receipts.set(absPath, current);
332
- else receipts.delete(absPath);
333
- let receipt;
334
- for (let i = current.length - 1; i >= 0 && !receipt; i -= 1) {
335
- if (current[i]?.version === expectedVersion) receipt = current[i];
336
- }
337
- return receipt;
338
- }
339
- var consumeFileWriteReceipt = identifyFileWrite;
340
- var SIGNATURE_TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
341
- ".cjs",
342
- ".css",
343
- ".html",
344
- ".js",
345
- ".json",
346
- ".jsx",
347
- ".mjs",
348
- ".svg",
349
- ".ts",
350
- ".tsx"
351
- ]);
352
- var SIGNATURE_EXCLUDED_DIRS = /* @__PURE__ */ new Set([
353
- ".cache",
354
- ".git",
355
- ".hyperframes",
356
- ".next",
357
- ".thumbnails",
358
- ".transcode-cache",
359
- ".vite",
360
- ".waveform-cache",
361
- "build",
362
- "coverage",
363
- "dist",
364
- "node_modules",
365
- "outputs",
366
- "renders"
367
- ]);
368
- var MAX_SIGNATURE_TEXT_BYTES = 2e6;
369
- var STUDIO_SIGNATURE_MANIFEST_PATHS = [
370
- ".hyperframes/studio-manual-edits.json",
371
- ".hyperframes/studio-motion.json"
372
- ];
373
- function affectsProjectSignature(projectDir, changedPath) {
374
- const relativePath = relative(resolve(projectDir), resolve(changedPath));
375
- if (relativePath === "" || relativePath.startsWith("..") || isAbsolute(relativePath) || isAtomicTempPath(relativePath)) {
376
- return false;
377
- }
378
- const segments = relativePath.split(sep);
379
- if (STUDIO_SIGNATURE_MANIFEST_PATHS.includes(segments.join("/"))) return true;
380
- return !segments.some((segment) => SIGNATURE_EXCLUDED_DIRS.has(segment));
381
- }
382
- var projectSignatureCache = /* @__PURE__ */ new Map();
383
- var COARSEST_FILE_TIME_TICK_MS = 2e3;
384
- function isPathWithin(parentDir, childPath) {
385
- const childRelativePath = relative(parentDir, childPath);
386
- return childRelativePath === "" || !childRelativePath.startsWith("..") && !isAbsolute(childRelativePath);
387
- }
388
- function isTextContentEligible(file, size) {
389
- return SIGNATURE_TEXT_EXTENSIONS.has(extname(file).toLowerCase()) && size <= MAX_SIGNATURE_TEXT_BYTES;
390
- }
391
- var isSkippedEntry = (entry) => SIGNATURE_EXCLUDED_DIRS.has(entry) || isAtomicTempPath(entry);
392
- function collectProjectSignatureFiles(projectDir, dir, files) {
393
- let entries;
394
- try {
395
- entries = readdirSync(dir).sort();
396
- } catch {
397
- return;
398
- }
399
- for (const entry of entries) {
400
- if (isSkippedEntry(entry)) continue;
401
- const file = resolve(dir, entry);
402
- if (!isPathWithin(projectDir, file)) continue;
403
- let stat22;
404
- try {
405
- stat22 = lstatSync(file);
406
- } catch {
407
- continue;
408
- }
409
- if (stat22.isSymbolicLink()) continue;
410
- if (stat22.isDirectory()) {
411
- collectProjectSignatureFiles(projectDir, file, files);
412
- } else if (stat22.isFile()) {
413
- files.push({
414
- file,
415
- mtimeMs: stat22.mtimeMs,
416
- ctimeMs: stat22.ctimeMs,
417
- size: stat22.size,
418
- textContentEligible: isTextContentEligible(file, stat22.size)
419
- });
420
- }
421
- }
422
- }
423
- function collectProjectSignatureManifestFiles(projectDir, files) {
424
- const seen = new Set(files.map((entry) => entry.file));
425
- for (const manifestPath of STUDIO_SIGNATURE_MANIFEST_PATHS) {
426
- const file = resolve(projectDir, manifestPath);
427
- if (seen.has(file) || !isPathWithin(projectDir, file)) continue;
428
- let stat22;
429
- try {
430
- stat22 = lstatSync(file);
431
- } catch {
432
- continue;
433
- }
434
- if (stat22.isSymbolicLink() || !stat22.isFile()) continue;
435
- files.push({
436
- file,
437
- mtimeMs: stat22.mtimeMs,
438
- ctimeMs: stat22.ctimeMs,
439
- size: stat22.size,
440
- textContentEligible: isTextContentEligible(file, stat22.size)
441
- });
442
- seen.add(file);
443
- }
444
- }
445
- function createProjectFingerprint(projectDir, files) {
446
- const hash = createHash22("sha256");
447
- for (const entry of files) {
448
- hash.update(relative(projectDir, entry.file));
449
- hash.update("\0");
450
- hash.update(String(entry.size));
451
- hash.update("\0");
452
- hash.update(String(entry.mtimeMs));
453
- hash.update("\0");
454
- hash.update(String(entry.ctimeMs));
455
- hash.update("\0");
456
- hash.update(entry.textContentEligible ? "text" : "binary");
457
- hash.update("\0");
458
- }
459
- return hash.digest("hex").slice(0, 24);
460
- }
461
- function collectProjectFiles(normalizedProjectDir) {
462
- const collected = [];
463
- collectProjectSignatureFiles(normalizedProjectDir, normalizedProjectDir, collected);
464
- collectProjectSignatureManifestFiles(normalizedProjectDir, collected);
465
- return collected;
466
- }
467
- function listProjectFiles(projectDir) {
468
- const normalizedProjectDir = resolve(projectDir);
469
- return collectProjectFiles(normalizedProjectDir).map((entry) => ({
470
- path: relative(normalizedProjectDir, entry.file).split(sep).join("/"),
471
- size: entry.size,
472
- mtimeMs: entry.mtimeMs,
473
- ctimeMs: entry.ctimeMs
474
- }));
475
- }
476
- function resolveProjectSignature(adapter, projectDir) {
477
- return adapter.getProjectSignature?.(projectDir) ?? createProjectSignature(projectDir);
478
- }
479
- async function resolveProjectAndSignature(adapter, projectId) {
480
- const project = await adapter.resolveProject(projectId);
481
- if (!project) return null;
482
- return { project, signature: resolveProjectSignature(adapter, project.dir) };
483
- }
484
- function createProjectSignature(projectDir, excluding = /* @__PURE__ */ new Set()) {
485
- const normalizedProjectDir = resolve(projectDir);
486
- const signedAt = Date.now();
487
- const collected = collectProjectFiles(normalizedProjectDir);
488
- const files = collected.filter(
489
- (entry) => !excluding.has(relative(normalizedProjectDir, entry.file).split(sep).join("/"))
490
- );
491
- files.sort((a, b) => a.file.localeCompare(b.file));
492
- const fingerprint = createProjectFingerprint(normalizedProjectDir, files);
493
- const cacheKey = excluding.size ? `${normalizedProjectDir}\0${createHash22("sha256").update([...excluding].sort().join("\0")).digest("hex")}` : normalizedProjectDir;
494
- const cached = projectSignatureCache.get(cacheKey);
495
- if (cached?.fingerprint === fingerprint) return cached.signature;
496
- const hash = createHash22("sha256");
497
- for (const entry of files) {
498
- const relativePath = relative(normalizedProjectDir, entry.file);
499
- hash.update(relativePath);
500
- hash.update("\0");
501
- hash.update(String(entry.size));
502
- hash.update("\0");
503
- if (entry.textContentEligible) {
504
- try {
505
- hash.update(readFileSync2(entry.file));
506
- } catch {
507
- hash.update(String(entry.mtimeMs));
508
- }
509
- } else {
510
- hash.update(String(entry.mtimeMs));
511
- }
512
- hash.update("\0");
513
- }
514
- const signature = hash.digest("hex").slice(0, 24);
515
- const settledATickBeforeSigning = files.every(
516
- (entry) => Math.max(entry.mtimeMs, entry.ctimeMs) < signedAt - COARSEST_FILE_TIME_TICK_MS
517
- );
518
- if (settledATickBeforeSigning) projectSignatureCache.set(cacheKey, { fingerprint, signature });
519
- return signature;
520
- }
521
- var BLOB_HASH = /^[0-9a-f]{64}$/;
522
- async function hashFile(path) {
523
- const hash = createHash3("sha256");
524
- for await (const chunk of createReadStream(path)) hash.update(chunk);
525
- return hash.digest("hex");
526
- }
527
- async function cloneOrCopy(from, to, beforeReplace) {
528
- await mkdir(dirname2(to), { recursive: true });
529
- const temp = atomicTempPath(to);
530
- try {
531
- await copyFile(from, temp, constants2.COPYFILE_FICLONE);
532
- beforeReplace?.();
533
- renameSync(temp, to);
534
- } catch (error) {
535
- await rm(temp, { force: true });
536
- throw error;
537
- }
538
- }
539
- async function openBlobStore(dir) {
540
- const sizes = /* @__PURE__ */ new Map();
541
- await mkdir(dir, { recursive: true });
542
- for (const shard of await readdir(dir))
543
- for (const hash of await readdir(join2(dir, shard)).catch(() => []))
544
- sizes.set(hash, (await stat(join2(dir, shard, hash))).size);
545
- const pathOf = (hash) => {
546
- if (!BLOB_HASH.test(hash)) throw new Error("That is not a history blob.");
547
- return join2(dir, hash.slice(0, 2), hash);
548
- };
549
- let total = [...sizes.values()].reduce((sum, size) => sum + size, 0);
550
- return {
551
- async put(absPath) {
552
- const temp = join2(dir, `incoming-${randomUUID2()}`);
553
- await mkdir(dir, { recursive: true });
554
- await copyFile(absPath, temp, constants2.COPYFILE_FICLONE);
555
- const hash = await hashFile(temp);
556
- if (sizes.has(hash)) {
557
- await rm(temp, { force: true });
558
- return hash;
559
- }
560
- await mkdir(dirname2(pathOf(hash)), { recursive: true });
561
- const size = (await stat(temp)).size;
562
- await rename(temp, pathOf(hash));
563
- sizes.set(hash, size);
564
- total += size;
565
- return hash;
566
- },
567
- has: (hash) => sizes.has(hash),
568
- read: async (hash) => readFile(pathOf(hash)),
569
- writeTo: async (hash, absPath, beforeReplace) => cloneOrCopy(pathOf(hash), absPath, beforeReplace),
570
- bytes: () => total,
571
- size: (hash) => sizes.get(hash) ?? 0,
572
- async prune(keep) {
573
- for (const [hash, size] of sizes) {
574
- if (keep.has(hash)) continue;
575
- await rm(pathOf(hash), { force: true });
576
- sizes.delete(hash);
577
- total -= size;
578
- }
579
- }
580
- };
581
- }
582
- var ID_PATH = join22(".hyperframes", "history-id");
583
- var ID_SHAPE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
584
- var isHistoryId = (name) => ID_SHAPE.test(name);
585
- function readId(projectDir) {
586
- try {
587
- const id = readFileSync22(join22(projectDir, ID_PATH), "utf-8").trim();
588
- return ID_SHAPE.test(id) ? id : null;
589
- } catch {
590
- return null;
591
- }
592
- }
593
- var sameFolder = (a, b) => a.ino === b.ino && a.birthtimeMs === b.birthtimeMs;
594
- function isRecordedFolder(historyDir, folder) {
595
- const was = readRecord(historyDir);
596
- if (typeof was?.ino === "number")
597
- return sameFolder({ ino: was.ino, birthtimeMs: was.born ?? NaN }, folder);
598
- const unreadableSo0878KeptTheId = typeof was?.dir !== "string";
599
- if (unreadableSo0878KeptTheId) return true;
600
- return !isCopyOf0878Folder(was.dir, basename(historyDir), folder);
601
- }
602
- function readRecord(historyDir) {
603
- try {
604
- return JSON.parse(readFileSync22(join22(historyDir, "project.json"), "utf-8"));
605
- } catch {
606
- return null;
607
- }
608
- }
609
- function isCopyOf0878Folder(recordedDir, id, folder) {
610
- let there;
611
- try {
612
- there = statSync(recordedDir, { throwIfNoEntry: false });
613
- } catch {
614
- return false;
615
- }
616
- return !!there && !sameFolder(there, folder) && readId(recordedDir) === id;
617
- }
618
- function projectHistoryId(projectDir, historyRoot) {
619
- const dir = resolve2(projectDir);
620
- const folder = statSync(dir);
621
- let id = readId(dir);
622
- if (!id && existsSync(join22(dir, ID_PATH)))
623
- throw new HistoryIdError(
624
- `${join22(dir, ID_PATH)} holds no history id this version can read; move it aside to start anew.`
625
- );
626
- if (!id || existsSync(join22(historyRoot, id)) && !isRecordedFolder(join22(historyRoot, id), folder)) {
627
- id = randomUUID3();
628
- mkdirWithinProject(dir, join22(dir, ".hyperframes"));
629
- writeFileSync2(join22(dir, ID_PATH), `${id}
630
- `);
631
- }
632
- recordProject(join22(historyRoot, id), dir, folder);
633
- return id;
634
- }
635
- var HistoryIdError = class extends Error {
636
- constructor(message) {
637
- super(message);
638
- this.name = "HistoryIdError";
639
- }
640
- };
641
- function recordProject(historyDir, dir, folder) {
642
- const record = { dir, ino: folder.ino, born: folder.birthtimeMs, dev: folder.dev };
643
- mkdirSync(historyDir, { recursive: true });
644
- replaceFileAtomically(join22(historyDir, "project.json"), JSON.stringify(record), 420);
645
- }
646
- var HistoryBusyError = class extends Error {
647
- constructor(pid) {
648
- super(`This project's history is open in another process (pid ${pid}).`);
649
- this.pid = pid;
650
- this.name = "HistoryBusyError";
651
- }
652
- pid;
653
- };
654
- function alive(pid) {
655
- if (!Number.isInteger(pid) || pid <= 0) return false;
656
- try {
657
- process.kill(pid, 0);
658
- return true;
659
- } catch (error) {
660
- return error.code === "EPERM";
661
- }
662
- }
663
- function ownerOf(file) {
664
- try {
665
- const text2 = readFileSync3(file, "utf-8");
666
- return /^\d+$/.test(text2) ? Number(text2) : Number.NaN;
667
- } catch (error) {
668
- if (error.code === "ENOENT") return null;
669
- throw error;
670
- }
671
- }
672
- function claim(file) {
673
- const draft = `${file}-${randomUUID4()}.tmp`;
674
- writeFileSync22(draft, String(process.pid));
675
- try {
676
- linkSync(draft, file);
677
- return true;
678
- } catch (error) {
679
- if (error.code !== "EEXIST") throw error;
680
- return false;
681
- } finally {
682
- rmSync(draft, { force: true });
683
- }
684
- }
685
- function releaseOwn(file) {
686
- if (ownerOf(file) === process.pid) rmSync(file, { force: true });
687
- }
688
- function evictDeadOwner(file) {
689
- const evictor = `${file}.evict`;
690
- if (!claim(evictor)) {
691
- const holder = ownerOf(evictor);
692
- if (holder !== null && !alive(holder)) rmSync(evictor, { force: true });
693
- return false;
694
- }
695
- try {
696
- const pid = ownerOf(file);
697
- if (pid !== null && !alive(pid)) rmSync(file, { force: true });
698
- return true;
699
- } finally {
700
- releaseOwn(evictor);
701
- }
702
- }
703
- async function takeHistoryOwnership(home, waitMs) {
704
- const file = join3(home, "owner.pid");
705
- const deadline = Date.now() + waitMs;
706
- mkdirSync2(home, { recursive: true });
707
- for (; ; ) {
708
- if (claim(file)) {
709
- let held = true;
710
- return () => {
711
- if (held) releaseOwn(file);
712
- held = false;
713
- };
714
- }
715
- const pid = ownerOf(file);
716
- if (pid === null) continue;
717
- if (!alive(pid) && evictDeadOwner(file)) continue;
718
- if (Date.now() >= deadline) throw new HistoryBusyError(pid);
719
- await new Promise((settle) => setTimeout(settle, 50));
720
- }
721
- }
722
- var DAY_MS = 24 * 60 * 60 * 1e3;
723
- var KEEP_GONE_PROJECT_HISTORY_MS = 14 * DAY_MS;
724
- var PRUNED_PREFIX = ".pruned-";
725
- var LAST_PRUNE_FILE = ".last-prune";
726
- function listProjectHistories(historyRoot) {
727
- return namesIn(historyRoot).filter(isHistoryId).flatMap((id) => {
728
- const home = join4(historyRoot, id);
729
- const projectDir = readRecord(home)?.dir;
730
- if (typeof projectDir !== "string" || readId(projectDir) !== id) return [];
731
- return [{ id, projectDir, lastUsedMs: lastUsed(home) }];
732
- });
733
- }
734
- function namesIn(historyRoot) {
735
- try {
736
- return readdirSync2(historyRoot);
737
- } catch (error) {
738
- if (error.code === "ENOENT") return [];
739
- throw error;
740
- }
741
- }
742
- async function pruneGoneProjectHistories(historyRoot, options = {}) {
743
- return (await pruneWithin(historyRoot, options)).pruned;
744
- }
745
- async function pruneWithin(historyRoot, {
746
- now = Date.now(),
747
- dryRun = false,
748
- tempDir = tmpdir(),
749
- onError = (error) => console.warn("[history] could not prune a history:", error),
750
- budgetMs = Infinity,
751
- measure = true
752
- }) {
753
- const started = performance.now();
754
- const names = namesIn(historyRoot);
755
- const pruned = [];
756
- for (const name of names) {
757
- if (performance.now() - started >= budgetMs) return { pruned, finished: false };
758
- try {
759
- const gone = await pruneEntry(historyRoot, name, { now, dryRun, tempDir, measure });
760
- if (gone) pruned.push(gone);
761
- } catch (error) {
762
- onError(error);
763
- }
764
- }
765
- return { pruned, finished: true };
766
- }
767
- async function pruneEntry(historyRoot, name, run) {
768
- if (name.startsWith(PRUNED_PREFIX)) {
769
- if (!run.dryRun) await rm2(join4(historyRoot, name), { recursive: true, force: true });
770
- return null;
771
- }
772
- return isHistoryId(name) ? pruneOne(historyRoot, name, run) : null;
773
- }
774
- async function pruneOne(historyRoot, id, run) {
775
- const home = join4(historyRoot, id);
776
- if (abandonedProject(home, id, run.now, run.tempDir) === null) return null;
777
- const release = await takeUnlessBusy(home);
778
- if (!release) return null;
779
- let taken;
780
- try {
781
- taken = recheckAndMoveAway(historyRoot, id, run);
782
- } catch (error) {
783
- release();
784
- throw error;
785
- }
786
- if (!taken?.trash) release();
787
- if (!taken) return null;
788
- const bytes = await clearAway(taken.trash, home, run.measure);
789
- return { id, projectDir: taken.projectDir, bytes };
790
- }
791
- async function clearAway(trash, home, measure) {
792
- const bytes = measure ? await bytesIn(trash ?? home) : 0;
793
- if (trash) await rm2(trash, { recursive: true, force: true });
794
- return bytes;
795
- }
796
- async function takeUnlessBusy(home) {
797
- try {
798
- return await takeHistoryOwnership(home, 0);
799
- } catch (error) {
800
- if (error instanceof HistoryBusyError) return null;
801
- throw error;
802
- }
803
- }
804
- function recheckAndMoveAway(historyRoot, id, { now, dryRun, tempDir }) {
805
- const home = join4(historyRoot, id);
806
- const projectDir = abandonedProject(home, id, now, tempDir);
807
- if (projectDir === null) return null;
808
- if (dryRun) return { projectDir, trash: null };
809
- const trash = join4(historyRoot, `${PRUNED_PREFIX}${id}-${randomUUID5()}`);
810
- renameSync2(home, trash);
811
- return { projectDir, trash };
812
- }
813
- function abandonedProject(home, id, now, tempDir) {
814
- const record = readRecord(home);
815
- const dir = record?.dir;
816
- if (typeof dir !== "string") return null;
817
- if (now - lastUsed(home) < KEEP_GONE_PROJECT_HISTORY_MS && !isSafePath(tempDir, dir)) return null;
818
- return projectGone(dir, id, record?.dev, tempDir) ? dir : null;
819
- }
820
- function projectGone(dir, id, dev, tempDir) {
821
- if (!statSync2(join4(dir, ID_PATH), { throwIfNoEntry: false }))
822
- return diskStillHere(dir, dev, tempDir);
823
- const there = readId(dir);
824
- return there !== null && there !== id;
825
- }
826
- function diskStillHere(dir, dev, tempDir) {
827
- let at = dir;
828
- let stat22 = statSync2(at, { throwIfNoEntry: false });
829
- while (!stat22) {
830
- if (dirname22(at) === at) return false;
831
- at = dirname22(at);
832
- stat22 = statSync2(at, { throwIfNoEntry: false });
833
- }
834
- if (typeof dev === "number") return stat22.dev === dev;
835
- return at === dir || isSafePath(tempDir, at);
836
- }
837
- function lastUsed(home) {
838
- const mtime = (file) => statSync2(join4(home, file), { throwIfNoEntry: false })?.mtimeMs ?? 0;
839
- return Math.max(mtime("project.json"), mtime("log.jsonl"));
840
- }
841
- async function bytesIn(dir) {
842
- let total = 0;
843
- for (const entry of await readdir2(dir, { withFileTypes: true })) {
844
- const path = join4(dir, entry.name);
845
- total += entry.isDirectory() ? await bytesIn(path) : (await lstat(path)).size;
846
- }
847
- return total;
848
- }
849
- function prunedWithinADay(stamp, now) {
850
- try {
851
- const last = Number(readFileSync4(stamp, "utf-8"));
852
- return last <= now && now - last < DAY_MS;
853
- } catch {
854
- return false;
855
- }
856
- }
857
- var pruning = /* @__PURE__ */ new Set();
858
- function pruneGoneProjectHistoriesDaily(historyRoot, now, { onError, budgetMs } = {}) {
859
- const stamp = join4(historyRoot, LAST_PRUNE_FILE);
860
- if (pruning.has(historyRoot) || prunedWithinADay(stamp, now)) return;
861
- pruning.add(historyRoot);
862
- setImmediate(() => {
863
- pruneWithin(historyRoot, { now, onError, budgetMs, measure: false }).then(({ finished }) => finished && writeFileSync3(stamp, String(now))).catch((error) => onError?.(error)).finally(() => pruning.delete(historyRoot));
864
- });
865
- }
866
- var START = "start";
867
- function parseRecord(line) {
868
- try {
869
- return JSON.parse(line);
870
- } catch {
871
- return null;
872
- }
873
- }
874
- function readLog(file, onUnreadable) {
875
- let text2;
876
- try {
877
- text2 = readFileSync5(file, "utf-8");
878
- } catch {
879
- return null;
880
- }
881
- const log = { baseline: /* @__PURE__ */ new Map(), entries: [], pins: /* @__PURE__ */ new Set() };
882
- const lines = text2.split("\n");
883
- lines.forEach((line, index) => {
884
- const record = line ? parseRecord(line) : null;
885
- if (record) applyRecord(log, record);
886
- else if (line && index < lines.length - 1) onUnreadable(index + 1);
887
- });
888
- return log;
889
- }
890
- function applyRecord(log, record) {
891
- if (record.type === "baseline") {
892
- log.baseline = new Map(Object.entries(record.files));
893
- log.keepsLedger = record.keepsLedger === true;
894
- } else if (record.type === "entry") log.entries.push(record.entry);
895
- else if (record.pinned) log.pins.add(record.id);
896
- else log.pins.delete(record.id);
897
- }
898
- function saveRecord(file, log, record) {
899
- let fd;
900
- try {
901
- fd = openSync2(file, constants22.O_WRONLY | constants22.O_APPEND);
902
- } catch (error) {
903
- if (error.code !== "ENOENT") throw error;
904
- return writeLog(file, log);
905
- }
906
- try {
907
- writeSync(fd, `${JSON.stringify(record)}
908
- `);
909
- } finally {
910
- closeSync2(fd);
911
- }
912
- }
913
- function baselineRecord(log) {
914
- return {
915
- type: "baseline",
916
- files: Object.fromEntries(log.baseline),
917
- ...log.keepsLedger && { keepsLedger: true }
918
- };
919
- }
920
- function writeLog(file, log) {
921
- const records = [
922
- baselineRecord(log),
923
- ...log.entries.map((entry) => ({ type: "entry", entry })),
924
- ...[...log.pins].map((id) => ({ type: "pin", id, pinned: true }))
925
- ];
926
- mkdirSync3(dirname3(file), { recursive: true });
927
- replaceFileAtomically(file, records.map((r) => `${JSON.stringify(r)}
928
- `).join(""), 420);
929
- }
930
- function applyEntry(manifest, entry, side) {
931
- for (const file of entry.files) {
932
- const hash = file[side];
933
- if (hash === null) manifest.delete(file.path);
934
- else manifest.set(file.path, hash);
935
- }
936
- }
937
- function manifestAt(log, point) {
938
- return point === START ? new Map(log.baseline) : manifestAround(log, point, "after");
939
- }
940
- function manifestAround(log, id, side) {
941
- const manifest = new Map(log.baseline);
942
- for (const entry of log.entries) {
943
- applyEntry(manifest, entry, entry.id === id ? side : "after");
944
- if (entry.id === id) return manifest;
945
- }
946
- return null;
947
- }
948
- function undoneIds(entries) {
949
- const undone = /* @__PURE__ */ new Set();
950
- for (let i = entries.length - 1; i >= 0; i -= 1) {
951
- const entry = entries[i];
952
- if (entry.undoes && !undone.has(entry.id)) undone.add(entry.undoes);
953
- }
954
- return undone;
955
- }
956
- function stepTarget(entries, direction, mine) {
957
- const undone = undoneIds(entries);
958
- const byId = new Map(entries.map((entry) => [entry.id, entry]));
959
- const owns = ownerOf2(entries, byId, mine);
960
- const newestFirst = [...entries].reverse();
961
- if (direction === "back")
962
- return newestFirst.find((entry) => !entry.undoes && !undone.has(entry.id) && owns(entry));
963
- for (const entry of newestFirst) {
964
- if (!entry.undoes) {
965
- if (owns(entry)) return void 0;
966
- continue;
967
- }
968
- const target = byId.get(entry.undoes);
969
- if (mine(entry) && !undone.has(entry.id) && target && !target.undoes) return entry;
970
- }
971
- return void 0;
972
- }
973
- function ownerOf2(entries, byId, mine) {
974
- const redoneBy = /* @__PURE__ */ new Map();
975
- for (const entry of entries) {
976
- const target = entry.undoes ? byId.get(entry.undoes) : void 0;
977
- if (target?.undoes) redoneBy.set(target.undoes, entry);
978
- }
979
- return (entry) => mine(entry) || mine(redoneBy.get(entry.id) ?? entry);
980
- }
981
- function foldOldest(log) {
982
- const oldest = log.entries[0];
983
- if (!oldest || log.pins.has(oldest.id)) return false;
984
- applyEntry(log.baseline, oldest, "after");
985
- log.entries.shift();
986
- return true;
987
- }
988
- function referencedHashes(log, current) {
989
- const keep = /* @__PURE__ */ new Set([...log.baseline.values(), ...current.values()]);
990
- for (const entry of log.entries)
991
- for (const file of entry.files) {
992
- if (file.before) keep.add(file.before);
993
- if (file.after) keep.add(file.after);
994
- }
995
- return keep;
996
- }
997
- var DEFAULT_HISTORY_ROOT = join5(homedir(), ".cache", "hyperframes", "history");
998
- var MAX_WINDOW_IDLE_MS = 10 * 6e4;
999
- var UNDO_MODES = ["just-this", "back-to-before", "keep-later-edits"];
1000
- var OUTSIDE = { kind: "outside", name: "Outside" };
1001
- var OUTSIDE_LABEL = "Changed outside the app";
1002
- var RACY_MS = 2e3;
1003
- var statKey = (file, sweptAt) => sweptAt - Math.max(file.mtimeMs, file.ctimeMs) < RACY_MS ? "" : `${file.size}:${file.mtimeMs}:${file.ctimeMs}`;
1004
- var MEDIA_LEDGER = ".media/manifest.jsonl";
1005
- var KEPT_HIDDEN_PATHS = /* @__PURE__ */ new Set([...STUDIO_SIGNATURE_MANIFEST_PATHS, MEDIA_LEDGER]);
1006
- function isHistoryPath(path) {
1007
- if (KEPT_HIDDEN_PATHS.has(path)) return true;
1008
- return !path.split("/").some((segment) => segment.startsWith("."));
1009
- }
1010
- function withoutHiddenPaths(log) {
1011
- for (const path of log.baseline.keys()) if (!isHistoryPath(path)) log.baseline.delete(path);
1012
- for (const entry of log.entries)
1013
- entry.files = entry.files.filter((file) => isHistoryPath(file.path));
1014
- return log;
1015
- }
1016
- var historyFiles = (dir) => listProjectFiles(dir).filter((file) => isHistoryPath(file.path));
1017
- var COPY_LATER_ABOVE_BYTES = 1024 ** 2;
1018
- var sameWho = (a, b) => a.kind === b.kind && a.name === b.name;
1019
- function splitAt(change, at, kept) {
1020
- if (at === change.after) return [change, null];
1021
- if (!at || at === change.before || !kept) return [null, change];
1022
- return [
1023
- { ...change, after: at },
1024
- { ...change, before: at }
1025
- ];
1026
- }
1027
- var isMissingFolder = (error) => ["ENOENT", "ENOTDIR"].includes(error.code ?? "");
1028
- async function removeEmptyFolders(dir) {
1029
- let entries;
1030
- try {
1031
- entries = await readdir3(dir, { withFileTypes: true });
1032
- } catch (error) {
1033
- if (isMissingFolder(error)) return;
1034
- throw error;
1035
- }
1036
- for (const entry of entries)
1037
- if (entry.isDirectory()) await removeEmptyFolders(join5(dir, entry.name));
1038
- await rmdir(dir);
1039
- }
1040
- function fileLeftIn(dir, at, deleted) {
1041
- for (const entry of readdirSync3(dir, { withFileTypes: true })) {
1042
- const path = `${at}/${entry.name}`;
1043
- const left = entry.isDirectory() ? fileLeftIn(join5(dir, entry.name), path, deleted) : deleted.has(path) ? void 0 : path;
1044
- if (left) return left;
1045
- }
1046
- return void 0;
1047
- }
1048
- function ignoresCase(dir) {
1049
- const self = statSync3(join5(dir, ID_PATH), { throwIfNoEntry: false });
1050
- const other = statSync3(join5(dir, ID_PATH.toUpperCase()), { throwIfNoEntry: false });
1051
- return self !== void 0 && other?.ino === self.ino && other.dev === self.dev;
1052
- }
1053
- var blocks = (removed, added) => added.startsWith(`${removed}/`) || removed.startsWith(`${added}/`);
1054
- var MAX_TIMER_MS = 2 ** 31 - 1;
1055
- var takesWrite = (window2, at) => window2.idleMs === void 0 || at - (window2.lastWriteAt ?? at) <= window2.idleMs;
1056
- function chainBackFrom(byAfter, hash) {
1057
- const chain = /* @__PURE__ */ new Map();
1058
- let at = hash;
1059
- let prior = byAfter?.get(at);
1060
- while (prior !== void 0 && !chain.has(at)) {
1061
- chain.set(at, prior);
1062
- at = prior;
1063
- prior = byAfter?.get(at);
1064
- }
1065
- return chain;
1066
- }
1067
- function addChange(group, path, before, after) {
1068
- const earlier = group.changes.get(path);
1069
- const from = earlier ? earlier.before : before;
1070
- if (from === after) group.changes.delete(path);
1071
- else group.changes.set(path, { path, before: from, after });
1072
- }
1073
- var Engine = class {
1074
- constructor(options, projectId, blobs) {
1075
- this.options = options;
1076
- this.projectId = projectId;
1077
- this.blobs = blobs;
1078
- this.dir = resolve3(options.projectDir);
1079
- this.home = join5(options.historyRoot, projectId);
1080
- this.foldsCase = ignoresCase(this.dir);
1081
- const folder = statSync3(this.dir, { throwIfNoEntry: false });
1082
- if (!folder || readId(this.dir) !== projectId || !isRecordedFolder(this.home, folder))
1083
- throw this.replaced();
1084
- this.folder = { dev: folder.dev, ino: folder.ino, birthtimeMs: folder.birthtimeMs };
1085
- }
1086
- options;
1087
- projectId;
1088
- blobs;
1089
- dir;
1090
- foldsCase;
1091
- folder;
1092
- home;
1093
- log = { baseline: /* @__PURE__ */ new Map(), entries: [], pins: /* @__PURE__ */ new Set() };
1094
- tracked = /* @__PURE__ */ new Map();
1095
- windows = [];
1096
- outside = null;
1097
- /** A coalescing claim, open until another key, its idle timer, an operation, a window, or another write. */
1098
- claimed = null;
1099
- adopting = /* @__PURE__ */ new Map();
1100
- /** Per path and hash an API write left, the hash of the bytes it replaced, until walked or written past. */
1101
- overwritten = /* @__PURE__ */ new Map();
1102
- stopHearing;
1103
- writeToken;
1104
- quietTimer;
1105
- maxTimer;
1106
- notedTimer = null;
1107
- listeners = /* @__PURE__ */ new Set();
1108
- tail = Promise.resolve();
1109
- closed = false;
1110
- closing;
1111
- /** A path as the project's disk compares names: folded where `A` and `a` are one name. */
1112
- nameKey(path) {
1113
- return this.foldsCase ? path.toLowerCase() : path;
1114
- }
1115
- get logFile() {
1116
- return join5(this.home, "log.jsonl");
1117
- }
1118
- now() {
1119
- return (this.options.now ?? Date.now)();
1120
- }
1121
- /** One operation at a time, in order: sweeps, windows and undos never interleave. */
1122
- queue(task) {
1123
- if (this.closed)
1124
- return Promise.reject(new HistoryClosedError("This project's history is closed."));
1125
- const run = this.tail.then(task);
1126
- this.tail = run.catch(() => void 0);
1127
- return run;
1128
- }
1129
- /** Another project if the folder or its id changed, or the id is gone: a recreated folder may reuse the inode. */
1130
- whereFolder() {
1131
- const now = statSync3(this.dir, { throwIfNoEntry: false });
1132
- if (!now) return "gone";
1133
- const same = now.dev === this.folder.dev && sameFolder(now, this.folder);
1134
- return same && readId(this.dir) === this.projectId ? "here" : "replaced";
1135
- }
1136
- assertWritable() {
1137
- const folder = this.whereFolder();
1138
- if (folder === "gone") throw new Error(`The project folder is gone: ${this.dir}`);
1139
- if (folder === "replaced") throw this.replaced();
1140
- }
1141
- persistLog(record) {
1142
- if (record) saveRecord(this.logFile, this.log, record);
1143
- else writeLog(this.logFile, this.log);
1144
- if (!existsSync2(join5(this.home, "project.json")))
1145
- recordProject(this.home, this.dir, this.folder);
1146
- }
1147
- replaced() {
1148
- return new HistoryClosedError(`${this.dir} is now another project.`);
1149
- }
1150
- assertOpen() {
1151
- if (this.closed) throw new HistoryClosedError("This project's history is closed.");
1152
- if (this.whereFolder() === "replaced") throw this.replaced();
1153
- }
1154
- async start() {
1155
- const log = readLog(
1156
- this.logFile,
1157
- (line) => this.options.onError?.(
1158
- new Error(`History log line ${line} could not be read and was skipped.`)
1159
- )
1160
- );
1161
- if (!log) return this.firstOpen();
1162
- this.log = withoutHiddenPaths(log);
1163
- if (!this.log.keepsLedger) await this.takeInUnnamedLedger();
1164
- const cache = this.readStatCache();
1165
- const last = this.log.entries.at(-1)?.id ?? START;
1166
- for (const [path, hash] of manifestAt(this.log, last) ?? []) {
1167
- const cached = cache.get(path);
1168
- this.tracked.set(path, { hash, stat: cached?.hash === hash ? cached.stat : "" });
1169
- }
1170
- const left = this.readAdopting();
1171
- const unadopted = (file) => left.has(file.path) && !this.tracked.has(file.path);
1172
- this.adoptMediaInBackground(historyFiles(this.dir).filter(unadopted), this.now());
1173
- this.reopenClosedWindow();
1174
- await this.settleAll();
1175
- }
1176
- hearWrites() {
1177
- const realDir = realFilePath(this.dir);
1178
- this.stopHearing = onFileOverwritten((absPath, version, bytes) => {
1179
- const after = hashOfVersion(version);
1180
- if (!after || !affectsProjectSignature(realDir, absPath)) return;
1181
- const replaced = hashOfVersion(fileContentVersion(bytes));
1182
- if (!replaced || replaced === after) return;
1183
- const path = relative2(realDir, absPath).split(sep2).join("/");
1184
- if (!isHistoryPath(path)) return;
1185
- this.overwritten.set(
1186
- path,
1187
- chainBackFrom(this.overwritten.get(path), replaced).set(after, replaced)
1188
- );
1189
- this.queue(async () => {
1190
- if (!this.blobs.has(replaced)) await this.storeBytes(bytes);
1191
- }).catch((error) => this.options.onError?.(error));
1192
- });
1193
- }
1194
- async storeBytes(bytes) {
1195
- const staged = join5(this.home, `overwrote-${randomUUID6()}`);
1196
- await mkdir2(this.home, { recursive: true });
1197
- await writeFile(staged, bytes);
1198
- try {
1199
- await this.blobs.put(staged);
1200
- } finally {
1201
- await rm3(staged, { force: true });
1202
- }
1203
- }
1204
- /** Read at a state no API write left, with no write heard since `heard`: no walk passes it, so forget the rest. */
1205
- forgetWritesBefore(path, hash, heard) {
1206
- if (this.overwritten.get(path) === heard && !(hash && heard?.has(hash)))
1207
- this.overwritten.delete(path);
1208
- }
1209
- reopenClosedWindow() {
1210
- const closed = this.options.closedWindow;
1211
- if (!closed || this.log.entries.some((entry) => entry.id === closed.id)) return;
1212
- const { id, who, label, startedAt, lastWriteAt, idleMs } = closed;
1213
- this.windows.push({ id, who, label, startedAt, lastWriteAt, idleMs, changes: /* @__PURE__ */ new Map() });
1214
- }
1215
- async takeInUnnamedLedger() {
1216
- const named = (path) => this.log.entries.some((entry) => entry.files.some((file) => file.path === path));
1217
- const listed = () => historyFiles(this.dir).some((file) => file.path === MEDIA_LEDGER);
1218
- const hash = this.log.baseline.has(MEDIA_LEDGER) || named(MEDIA_LEDGER) || !listed() ? null : await this.storeIfPresent(MEDIA_LEDGER);
1219
- if (hash !== null) this.log.baseline.set(MEDIA_LEDGER, hash);
1220
- this.log.keepsLedger = true;
1221
- this.persistLog();
1222
- }
1223
- async firstOpen() {
1224
- const sweptAt = this.now();
1225
- const files = historyFiles(this.dir);
1226
- for (const file of files.filter((file2) => file2.size <= COPY_LATER_ABOVE_BYTES)) {
1227
- const hash = await this.storeIfPresent(file.path);
1228
- if (this.whereFolder() !== "here") throw this.replaced();
1229
- if (hash !== null) this.tracked.set(file.path, { hash, stat: statKey(file, sweptAt) });
1230
- }
1231
- this.log.baseline = this.manifest();
1232
- this.log.keepsLedger = true;
1233
- this.persistLog();
1234
- this.saveStatCache();
1235
- this.adoptMediaInBackground(
1236
- files.filter((file) => file.size > COPY_LATER_ABOVE_BYTES),
1237
- sweptAt
1238
- );
1239
- }
1240
- adoptMediaInBackground(files, sweptAt) {
1241
- let copied = Promise.resolve();
1242
- for (const file of files) {
1243
- copied = copied.then(() => this.adopt(file, sweptAt));
1244
- this.adopting.set(file.path, copied);
1245
- }
1246
- this.saveAdopting();
1247
- }
1248
- saveAdopting() {
1249
- const file = join5(this.home, "adopting.json");
1250
- if (!this.adopting.size) return rmSync2(file, { force: true });
1251
- mkdirSync4(this.home, { recursive: true });
1252
- replaceFileAtomically(file, JSON.stringify([...this.adopting.keys()]), 420);
1253
- }
1254
- async adopt(file, sweptAt) {
1255
- try {
1256
- if (this.whereFolder() !== "here") return;
1257
- const stored = await this.storeIfPresent(file.path);
1258
- await this.queue(async () => {
1259
- await this.recordAdopted(file, sweptAt, stored);
1260
- this.adopting.delete(file.path);
1261
- this.saveAdopting();
1262
- });
1263
- } catch (error) {
1264
- if (!(error instanceof HistoryClosedError)) this.options.onError?.(error);
1265
- }
1266
- }
1267
- async recordAdopted(file, sweptAt, stored) {
1268
- if (stored === null || this.whereFolder() !== "here") return;
1269
- const hash = this.blobs.has(stored) ? stored : await this.storeIfPresent(file.path);
1270
- if (hash === null) return;
1271
- const change = { path: file.path, before: null, after: hash };
1272
- const found = this.overwrittenBy(change, void 0, /* @__PURE__ */ new Set());
1273
- const start = found && this.blobs.has(found) ? found : hash;
1274
- this.tracked.set(file.path, { hash, stat: statKey(file, sweptAt) });
1275
- this.log.baseline.set(file.path, start);
1276
- if (start !== hash) addChange(this.outsideGroup(), file.path, start, hash);
1277
- this.persistLog(baselineRecord(this.log));
1278
- this.saveStatCache();
1279
- }
1280
- manifest() {
1281
- return new Map([...this.tracked].map(([path, file]) => [path, file.hash]));
1282
- }
1283
- readStatCache() {
1284
- try {
1285
- const files = JSON.parse(readFileSync6(join5(this.home, "stat.json"), "utf-8"));
1286
- return new Map(Object.entries(files));
1287
- } catch {
1288
- return /* @__PURE__ */ new Map();
1289
- }
1290
- }
1291
- readAdopting() {
1292
- try {
1293
- return new Set(
1294
- JSON.parse(readFileSync6(join5(this.home, "adopting.json"), "utf-8"))
1295
- );
1296
- } catch {
1297
- return /* @__PURE__ */ new Set();
1298
- }
1299
- }
1300
- saveStatCache() {
1301
- const files = JSON.stringify(Object.fromEntries(this.tracked));
1302
- mkdirSync4(this.home, { recursive: true });
1303
- replaceFileAtomically(join5(this.home, "stat.json"), files, 420);
1304
- }
1305
- /**
1306
- * Takes in every write since the last sweep and files each change to its writer. ponytail: always the whole
1307
- * project (a stat per file, a hash only when the stat moved); per-path sweeps if projects reach tens of thousands.
1308
- */
1309
- async sweep() {
1310
- if (this.whereFolder() !== "here") return;
1311
- const sweptAt = this.now();
1312
- const changedAt = (file) => Math.min(sweptAt, Math.max(file.mtimeMs, file.ctimeMs));
1313
- const seen = historyFiles(this.dir).filter((file) => !this.adopting.has(file.path));
1314
- const heard = new Map(this.overwritten);
1315
- const present = new Set(seen.map((file) => file.path));
1316
- const removed = [...this.tracked.keys()].filter((path) => !present.has(path)).map((path) => {
1317
- const standing = this.standingAt(path);
1318
- return { at: standing ? changedAt(standing.stat) : sweptAt, path, file: null };
1319
- });
1320
- const appearedAt = (file) => Math.max(
1321
- changedAt(file),
1322
- ...removed.filter((gone) => blocks(this.nameKey(gone.path), this.nameKey(file.path))).map((gone) => gone.at)
1323
- );
1324
- const events = [
1325
- ...removed,
1326
- ...seen.map((file) => ({ at: appearedAt(file), path: file.path, file }))
1327
- ].sort((a, b) => a.at - b.at);
1328
- let changed = false;
1329
- for (const { at, path, file } of events) {
1330
- if (file) {
1331
- changed = await this.observe(path, statKey(file, sweptAt), at) || changed;
1332
- continue;
1333
- }
1334
- const known = this.tracked.get(path);
1335
- this.tracked.delete(path);
1336
- this.forgetWritesBefore(path, null, heard.get(path));
1337
- await this.record(path, known.hash, null, at);
1338
- changed = true;
1339
- }
1340
- if (changed) this.saveStatCache();
1341
- }
1342
- /** The file's hash once copied in; null when it was removed before the copy (the next sweep records that). */
1343
- async storeIfPresent(path) {
1344
- try {
1345
- return await this.blobs.put(join5(this.dir, path));
1346
- } catch (error) {
1347
- if (error.code === "ENOENT") return null;
1348
- throw error;
1349
- }
1350
- }
1351
- async observe(path, stat22, at) {
1352
- const known = this.tracked.get(path) ?? { hash: null, stat: null };
1353
- if (stat22 && known.stat === stat22) return false;
1354
- const heard = this.overwritten.get(path);
1355
- const hash = await this.storeIfPresent(path);
1356
- if (hash === null || this.whereFolder() !== "here") return false;
1357
- this.tracked.set(path, { hash, stat: stat22 });
1358
- this.forgetWritesBefore(path, hash, heard);
1359
- if (known.hash !== hash) await this.record(path, known.hash, hash, at);
1360
- return known.hash !== hash || known.stat !== stat22;
1361
- }
1362
- async record(path, before, after, at) {
1363
- const endedBeforeThisWrite = this.windows.filter((open2) => !takesWrite(open2, at));
1364
- if (endedBeforeThisWrite.length) await this.commitOutside();
1365
- for (const window22 of endedBeforeThisWrite) await this.endWindow(window22);
1366
- const window2 = this.windows.at(-1);
1367
- if (window2) this.touch(window2, at);
1368
- addChange(window2 ?? this.outsideGroup(), path, before, after);
1369
- }
1370
- /** ponytail: another's write between Studio's write and its sweep folds into Studio's entry (per-write tokens). */
1371
- async claimNow(who, label, paths, { coalesceKey, idleMs, overwrote = {} }) {
1372
- await this.sweep();
1373
- const taken = await this.takeClaimed(who, paths, overwrote);
1374
- if (!taken.length) return this.claimNothing(coalesceKey);
1375
- const held = this.heldFor(coalesceKey, taken);
1376
- if (!held) await this.commitClaim();
1377
- await this.commitPending();
1378
- const group = held ?? this.newGroup(who, label);
1379
- for (const change of taken) addChange(group, change.path, change.before, change.after);
1380
- if (coalesceKey) return this.holdClaim(group, coalesceKey, idleMs);
1381
- const entry = await this.commit(group);
1382
- return entry && { id: entry.id };
1383
- }
1384
- /** A claim that took nothing still ends a held claim under another key. */
1385
- async claimNothing(coalesceKey) {
1386
- if (coalesceKey !== this.claimed?.key) await this.commitClaim();
1387
- return null;
1388
- }
1389
- /** Commits every other writer's pending changes, oldest first; open windows stay open. */
1390
- async commitPending() {
1391
- await this.commitOutside();
1392
- for (const window2 of [...this.windows])
1393
- if (window2.changes.size) await this.commitCut(window2, [...window2.changes.keys()]);
1394
- }
1395
- pendingElsewhere() {
1396
- return Boolean(this.outside?.changes.size) || this.windows.some((open2) => open2.changes.size > 0);
1397
- }
1398
- /** The held claim `taken` continues: same key, each file from its own last change, no held file changed since. */
1399
- heldFor(key, taken) {
1400
- const group = key && this.claimed?.key === key && !this.pendingElsewhere() ? this.claimed.group : null;
1401
- const after = (path, before) => {
1402
- const own = group?.changes.get(path);
1403
- return own ? own.after : before;
1404
- };
1405
- const takenPaths = new Set(taken.map((change) => change.path));
1406
- const untouched = (change) => takenPaths.has(change.path) || (this.tracked.get(change.path)?.hash ?? null) === change.after;
1407
- return group && taken.every((change) => after(change.path, change.before) === change.before) && [...group.changes.values()].every(untouched) ? group : null;
1408
- }
1409
- logPath(path) {
1410
- return relative2(this.dir, resolve3(this.dir, path)).split(sep2).join("/");
1411
- }
1412
- /** Takes `paths`' uncommitted changes from others, cut at what `who` overwrote. */
1413
- async takeClaimed(who, paths, overwrote) {
1414
- const wanted = new Set(paths.map((path) => this.logPath(path)));
1415
- const at = new Map(
1416
- Object.entries(overwrote).map(([path, version]) => [
1417
- this.logPath(path),
1418
- hashOfVersion(version)
1419
- ])
1420
- );
1421
- const others = this.windows.filter((open2) => !sameWho(open2.who, who));
1422
- const groups = this.outside ? [this.outside, ...others] : others;
1423
- const hits = groups.flatMap(
1424
- (group) => [...group.changes.values()].filter((change) => wanted.has(change.path)).map((change) => ({ group, change }))
1425
- );
1426
- const cuts = [];
1427
- const walked = hits.map(() => /* @__PURE__ */ new Set());
1428
- for (const [i, { change }] of hits.entries()) {
1429
- const told = at.get(change.path);
1430
- cuts.push(this.overwrittenBy(change, told, walked[i]) ?? told);
1431
- }
1432
- hits.forEach(({ change }, i) => this.forgetOverwritten(change.path, walked[i]));
1433
- return hits.flatMap(({ group, change }, i) => this.cutOut(group, change, cuts[i]) ?? []);
1434
- }
1435
- /** Walks the server's writes back from `change.after` to the stored bytes they replaced. */
1436
- overwrittenBy(change, told, seen) {
1437
- let found;
1438
- for (let hash = change.after; hash; ) {
1439
- if (seen.has(hash)) return void 0;
1440
- seen.add(hash);
1441
- const replaced = this.overwritten.get(change.path)?.get(hash);
1442
- if (replaced === void 0) break;
1443
- found = hash = replaced;
1444
- if (hash === change.before || hash === told) return hash;
1445
- }
1446
- return found;
1447
- }
1448
- forgetOverwritten(path, hashes) {
1449
- const byAfter = this.overwritten.get(path);
1450
- for (const hash of hashes) byAfter?.delete(hash);
1451
- if (byAfter?.size === 0) this.overwritten.delete(path);
1452
- }
1453
- cutOut(group, change, cut) {
1454
- const [kept, claimed] = splitAt(change, cut, cut !== void 0 && this.blobs.has(cut));
1455
- group.changes.delete(change.path);
1456
- if (kept) group.changes.set(kept.path, kept);
1457
- return claimed;
1458
- }
1459
- /** Commits the window's cut `paths` as their own entry, other files staying: a window holds one change per file,
1460
- * so the writer's next write to a cut file would otherwise span the claimer's edit. */
1461
- async commitCut(window2, paths) {
1462
- const part = { ...this.newGroup(window2.who, window2.label), startedAt: window2.startedAt };
1463
- for (const path of paths) {
1464
- part.changes.set(path, window2.changes.get(path));
1465
- window2.changes.delete(path);
1466
- }
1467
- (window2.parts ??= /* @__PURE__ */ new Set()).add(part.id);
1468
- window2.entry = await this.commit(part);
1469
- }
1470
- holdClaim(group, key, idleMs = this.options.quietMs ?? 2e3) {
1471
- clearTimeout(this.claimed?.timer);
1472
- const timer = Number.isFinite(idleMs) ? setTimeout(() => this.background(() => this.commitClaim()), idleMs) : void 0;
1473
- timer?.unref?.();
1474
- group.lastWriteAt = this.now();
1475
- this.claimed = { group, key, timer };
1476
- return group.changes.size ? { id: group.id } : null;
1477
- }
1478
- async commitClaim() {
1479
- const held = this.claimed;
1480
- this.claimed = null;
1481
- if (!held) return;
1482
- clearTimeout(held.timer);
1483
- await this.commit(held.group);
1484
- }
1485
- outsideGroup() {
1486
- const { quietMs = 2e3, maxGroupMs = 3e4 } = this.options;
1487
- const closeIn = (ms) => {
1488
- const timer = setTimeout(() => this.background(() => this.commitOutside()), ms);
1489
- timer.unref?.();
1490
- return timer;
1491
- };
1492
- if (!this.outside) {
1493
- this.outside = this.newGroup(OUTSIDE, OUTSIDE_LABEL);
1494
- this.maxTimer = closeIn(maxGroupMs);
1495
- }
1496
- clearTimeout(this.quietTimer);
1497
- this.quietTimer = closeIn(quietMs);
1498
- return this.outside;
1499
- }
1500
- newGroup(who, label) {
1501
- return { id: randomUUID6(), who, label, startedAt: this.now(), changes: /* @__PURE__ */ new Map() };
1502
- }
1503
- async commitOutside() {
1504
- if (this.outside?.changes.size) await this.commitClaim();
1505
- clearTimeout(this.quietTimer);
1506
- clearTimeout(this.maxTimer);
1507
- const group = this.outside;
1508
- this.outside = null;
1509
- if (group) await this.commit(group);
1510
- }
1511
- async commit(group, extra = {}) {
1512
- const undoesEmptied = this.log.entries.find((entry2) => entry2.id === extra.undoes)?.files.length === 0;
1513
- if (!group.changes.size && !undoesEmptied) return null;
1514
- const pending = this.pendingEntry(group);
1515
- const endedAt = Math.max(pending.endedAt, this.log.entries.at(-1)?.endedAt ?? 0);
1516
- const entry = { ...pending, endedAt, ...extra };
1517
- this.log.entries.push(entry);
1518
- try {
1519
- this.persistLog({ type: "entry", entry });
1520
- } catch (error) {
1521
- this.log.entries.pop();
1522
- throw error;
1523
- }
1524
- for (const listener of this.listeners) listener(entry);
1525
- for (const change of group.changes.values()) {
1526
- const walked = /* @__PURE__ */ new Set();
1527
- this.overwrittenBy(change, void 0, walked);
1528
- this.forgetOverwritten(change.path, walked);
1529
- }
1530
- await this.keepWithinBudget();
1531
- return entry;
1532
- }
1533
- /** Stored bytes beyond what the current files need: a project larger than the budget still keeps its history. */
1534
- historyBytes() {
1535
- let current = 0;
1536
- for (const hash of new Set([...this.tracked.values()].map((file) => file.hash)))
1537
- current += this.blobs.size(hash);
1538
- return this.blobs.bytes() - current;
1539
- }
1540
- async keepWithinBudget() {
1541
- const budget = this.options.budgetBytes ?? 2 * 1024 ** 3;
1542
- let folded = false;
1543
- while (this.historyBytes() > budget) {
1544
- await this.blobs.prune(
1545
- /* @__PURE__ */ new Set([
1546
- ...referencedHashes(this.log, this.manifest()),
1547
- ...this.pendingHashes(),
1548
- ...[...this.overwritten.values()].flatMap((byAfter) => [...byAfter.values()])
1549
- ])
1550
- );
1551
- if (this.historyBytes() <= budget || !foldOldest(this.log)) break;
1552
- folded = true;
1553
- }
1554
- if (folded) this.persistLog();
1555
- }
1556
- /** Hashes only pending changes point at yet, such as a claim's stored cut. */
1557
- pendingHashes() {
1558
- const changes = [...this.windows, this.outside, this.claimed?.group].flatMap((group) => [
1559
- ...group?.changes.values() ?? []
1560
- ]);
1561
- return changes.flatMap((change) => [change.before, change.after]).filter((hash) => hash !== null);
1562
- }
1563
- /** Before an operation or a window: every write is filed and every pending change committed, in write order. */
1564
- async settle() {
1565
- await this.sweep();
1566
- await this.commitClaim();
1567
- await this.commitPending();
1568
- }
1569
- /** A watcher saw a write: one sweep per burst takes it in (a deleted folder is reported by its name alone). */
1570
- noteChange(path) {
1571
- const absolute = resolve3(this.dir, path);
1572
- if (this.notedTimer || !affectsProjectSignature(this.dir, absolute)) return;
1573
- if (!isHistoryPath(relative2(this.dir, absolute).split(sep2).join("/"))) return;
1574
- this.notedTimer = setTimeout(() => {
1575
- this.notedTimer = null;
1576
- this.background(() => this.sweep());
1577
- }, 20);
1578
- this.notedTimer.unref?.();
1579
- }
1580
- background(task) {
1581
- this.queue(task).catch((error) => {
1582
- if (!(error instanceof HistoryClosedError)) this.options.onError?.(error);
1583
- });
1584
- }
1585
- beginWindow(who, label, idleMs) {
1586
- return this.queue(async () => {
1587
- await this.settle();
1588
- const window2 = { ...this.newGroup(who, label), idleMs };
1589
- this.windows.push(window2);
1590
- this.touch(window2, this.now());
1591
- const close = () => this.queue(() => this.sweepAndEnd(window2));
1592
- return { id: window2.id, startedAt: window2.startedAt, close };
1593
- });
1594
- }
1595
- /** A window with no write for its idleMs ends, so a close that never comes cannot hold every later write. */
1596
- touch(window2, at) {
1597
- window2.lastWriteAt = Math.max(window2.lastWriteAt ?? at, at);
1598
- this.endWhenIdle(window2, window2.idleMs);
1599
- }
1600
- endWhenIdle(window2, delay) {
1601
- clearTimeout(window2.idleTimer);
1602
- if (window2.idleMs === void 0 || !Number.isFinite(window2.idleMs) || delay === void 0)
1603
- return;
1604
- window2.idleTimer = setTimeout(
1605
- () => {
1606
- const left = window2.idleMs - (this.now() - (window2.lastWriteAt ?? this.now()));
1607
- if (left > 0) this.endWhenIdle(window2, left);
1608
- else this.background(() => this.sweepAndEnd(window2));
1609
- },
1610
- Math.min(delay, MAX_TIMER_MS)
1611
- );
1612
- window2.idleTimer.unref?.();
1613
- }
1614
- async sweepAndEnd(window2) {
1615
- await this.sweep();
1616
- return this.endWindow(window2);
1617
- }
1618
- /** Commits an open window once; ending it again returns the entry it became. */
1619
- async endWindow(window2) {
1620
- if (!this.windows.includes(window2)) return window2.entry ?? null;
1621
- clearTimeout(window2.idleTimer);
1622
- await this.commitClaim();
1623
- this.windows = this.windows.filter((open2) => open2 !== window2);
1624
- window2.entry = await this.commit(window2) ?? window2.entry ?? null;
1625
- return window2.entry;
1626
- }
1627
- /** Every pending write, open window and outside group, committed: for flush and close. */
1628
- async settleAll() {
1629
- await this.sweep();
1630
- await this.commitClaim();
1631
- for (const window2 of [...this.windows]) await this.endWindow(window2);
1632
- await this.commitOutside();
1633
- }
1634
- /** What took a removed path's place, dating its removal: an entry at the path, or a file at a folder above it. */
1635
- standingAt(path) {
1636
- const parts = path.split("/");
1637
- for (let depth = 1; depth <= parts.length; depth += 1) {
1638
- const at = parts.slice(0, depth).join("/");
1639
- const stat22 = lstatSync2(join5(this.dir, at), { throwIfNoEntry: false });
1640
- if (!stat22) return void 0;
1641
- if (at === path || !stat22.isDirectory()) return { at, stat: stat22 };
1642
- }
1643
- return void 0;
1644
- }
1645
- /** What a write of `path` would have to delete: a file or link at or above it, or a file in a folder at it. */
1646
- inTheWay(path, deleted) {
1647
- const standing = this.standingAt(path);
1648
- if (!standing) return void 0;
1649
- if (standing.stat.isSymbolicLink()) return standing.at;
1650
- if (standing.at === path && !standing.stat.isDirectory()) return void 0;
1651
- const real = realpathSync.native(join5(this.dir, standing.at));
1652
- const at = relative2(realpathSync.native(this.dir), real).split(sep2).join("/");
1653
- if (standing.at !== path) return deleted.has(at) ? void 0 : at;
1654
- return fileLeftIn(real, at, deleted);
1655
- }
1656
- /** Writes `target` (path to hash, null deletes) as one entry of `who`'s. */
1657
- async writeAs(who, label, target, extra) {
1658
- this.assertWritable();
1659
- const changes = [...target].filter(([path, hash]) => (this.tracked.get(path)?.hash ?? null) !== hash).sort(([, a], [, b]) => Number(a !== null) - Number(b !== null));
1660
- const deleted = new Set(changes.filter(([, hash]) => hash === null).map(([path]) => path));
1661
- const blocked = changes.filter(([, hash]) => hash !== null).map(([path]) => this.inTheWay(path, deleted)).find(Boolean);
1662
- if (blocked) throw new Error(`${blocked} is in the way; move or delete it, then try again.`);
1663
- const group = this.newGroup(who, label);
1664
- this.windows.push(group);
1665
- try {
1666
- for (const [path, hash] of changes) await this.writeProjectFile(path, hash);
1667
- await this.sweep();
1668
- } finally {
1669
- this.windows = this.windows.filter((open2) => open2 !== group);
1670
- }
1671
- return this.commit(group, extra);
1672
- }
1673
- /** Writes one project file (null deletes it), first leaving the running operation's receipt for its echo. */
1674
- async writeProjectFile(path, hash) {
1675
- this.assertWritable();
1676
- const absPath = join5(this.dir, path);
1677
- const { writeToken } = this;
1678
- if (writeToken) {
1679
- const version = hash === null ? DELETED_VERSION : hashVersion(hash);
1680
- recordFileWriteReceipt(absPath, { path, version, writeToken });
1681
- }
1682
- if (hash === null) {
1683
- this.assertWritable();
1684
- rmSync2(absPath, { force: true });
1685
- } else {
1686
- await removeEmptyFolders(absPath);
1687
- await this.blobs.writeTo(hash, absPath, () => this.assertWritable());
1688
- }
1689
- }
1690
- entry(id) {
1691
- const entry = this.log.entries.find((candidate) => candidate.id === id);
1692
- if (!entry) throw new Error("That change is no longer kept in this project's history.");
1693
- return entry;
1694
- }
1695
- undoLabel(entry) {
1696
- if (!entry.undoes) return `Undid: ${entry.label}`;
1697
- const original = this.log.entries.find((candidate) => candidate.id === entry.undoes);
1698
- return `Redid: ${original?.label ?? entry.label}`;
1699
- }
1700
- async undoNow(id, who, mode) {
1701
- const entry = this.entry(id);
1702
- const changed = this.movedOn(entry);
1703
- if (changed.length && !mode) return { ok: false, conflict: this.conflict(entry, changed) };
1704
- if (mode === "back-to-before") {
1705
- const index = this.log.entries.indexOf(entry);
1706
- const point = index > 0 ? this.log.entries[index - 1].id : START;
1707
- return {
1708
- ok: true,
1709
- entry: await this.restoreNow(point, who, `Went back to before: ${entry.label}`)
1710
- };
1711
- }
1712
- const target = new Map(
1713
- entry.files.filter((file) => mode !== "keep-later-edits" || !changed.includes(file)).map((file) => [file.path, file.before])
1714
- );
1715
- return {
1716
- ok: true,
1717
- entry: await this.writeAs(who, this.undoLabel(entry), target, { undoes: id })
1718
- };
1719
- }
1720
- movedOn(entry) {
1721
- return entry.files.filter((file) => (this.tracked.get(file.path)?.hash ?? null) !== file.after);
1722
- }
1723
- next(direction, who) {
1724
- const mine = (author) => sameWho(author, who) || sameWho(author, OUTSIDE);
1725
- const pending = [...[...this.windows].reverse(), this.outside, this.claimed?.group].find(
1726
- (group) => group?.changes.size && mine(group.who)
1727
- );
1728
- if (pending) return direction === "back" ? this.pendingEntry(pending) : void 0;
1729
- const everyone = this.options.undoScope === "everyone";
1730
- const ofOpenTurn = (entry) => this.windows.some((open2) => open2.parts?.has(entry.id));
1731
- return stepTarget(
1732
- this.log.entries,
1733
- direction,
1734
- (entry) => entry.files.length > 0 && (mine(entry.who) || everyone && !ofOpenTurn(entry))
1735
- );
1736
- }
1737
- /** A pending group as the entry it becomes once committed (a window's part gets a fresh id). */
1738
- pendingEntry(group) {
1739
- const files = [...group.changes.values()].sort((a, b) => a.path.localeCompare(b.path));
1740
- const { id, who, label, startedAt, lastWriteAt } = group;
1741
- return { id, who, label, startedAt, endedAt: lastWriteAt ?? this.now(), files };
1742
- }
1743
- conflict(entry, changed) {
1744
- const paths = new Set(changed.map((file) => file.path));
1745
- const after = this.log.entries.slice(this.log.entries.indexOf(entry) + 1);
1746
- const newer = after.filter((later) => later.files.some((file) => paths.has(file.path)));
1747
- return { files: [...paths], newer: newer.map((later) => later.id) };
1748
- }
1749
- async restoreNow(point, who, label) {
1750
- const files = manifestAt(this.log, point);
1751
- if (!files) throw new Error("That point is no longer kept in this project's history.");
1752
- const target = new Map(
1753
- [...this.tracked.keys()].map((path) => [path, null])
1754
- );
1755
- for (const [path, hash] of files) target.set(path, hash);
1756
- return this.writeAs(who, label, target, { restoredTo: point });
1757
- }
1758
- /** Settles, then runs `task` with its writes labelled `writeToken`. */
1759
- operation(writeToken, task) {
1760
- return this.queue(async () => {
1761
- await this.settle();
1762
- this.writeToken = writeToken;
1763
- try {
1764
- return await task();
1765
- } finally {
1766
- this.writeToken = void 0;
1767
- }
1768
- });
1769
- }
1770
- pointLabel(point) {
1771
- return point === START ? "the start" : this.entry(point).label;
1772
- }
1773
- api() {
1774
- return {
1775
- projectId: this.projectId,
1776
- beginWindow: (who, label, options = {}) => this.beginWindow(who, label, options.idleMs ?? this.options.maxGroupMs ?? 3e4),
1777
- claim: async (who, label, paths, options = {}) => {
1778
- await Promise.all(paths.map((path) => this.adopting.get(this.logPath(path))));
1779
- return this.queue(() => this.claimNow(who, label, paths, options));
1780
- },
1781
- noteChange: (path) => {
1782
- if (!this.closed) this.noteChange(path);
1783
- },
1784
- list: () => {
1785
- this.assertOpen();
1786
- const undone = undoneIds(this.log.entries);
1787
- return this.log.entries.map((entry) => ({
1788
- ...entry,
1789
- pinned: this.log.pins.has(entry.id),
1790
- undone: undone.has(entry.id)
1791
- }));
1792
- },
1793
- step: (direction, who, { writeToken } = {}) => this.operation(writeToken, async () => {
1794
- const target = this.next(direction, who);
1795
- return target ? this.undoNow(target.id, who) : { ok: true, entry: null };
1796
- }),
1797
- undo: (id, { who, mode, writeToken }) => this.operation(writeToken, () => this.undoNow(id, who, mode)),
1798
- restore: (point, who, { writeToken } = {}) => this.operation(
1799
- writeToken,
1800
- () => this.restoreNow(point, who, `Restored: ${this.pointLabel(point)}`)
1801
- ),
1802
- peek: (point) => {
1803
- this.assertOpen();
1804
- const files = manifestAt(this.log, point);
1805
- return files && Object.fromEntries(files);
1806
- },
1807
- checkout: (entryId, side, emptyDir) => this.queue(async () => {
1808
- this.entry(entryId);
1809
- if (!existsSync2(this.dir)) throw new Error(`The project folder is gone: ${this.dir}`);
1810
- const dest = resolve3(emptyDir);
1811
- if (isWithin(this.dir, dest))
1812
- throw new Error(`Checkout writes outside the project only: ${dest}`);
1813
- if ((await readdir3(dest).catch(missingIsEmpty)).length > 0)
1814
- throw new Error(`Checkout writes into an empty folder only: ${dest}`);
1815
- const files = manifestAround(this.log, entryId, side);
1816
- try {
1817
- for (const [path, hash] of files) await this.blobs.writeTo(hash, join5(dest, path));
1818
- } catch (error) {
1819
- for (const name of await readdir3(dest).catch(() => []))
1820
- await rm3(join5(dest, name), { recursive: true, force: true });
1821
- throw error;
1822
- }
1823
- }),
1824
- next: (direction, who) => {
1825
- this.assertOpen();
1826
- return this.next(direction, who);
1827
- },
1828
- readBlob: async (hash) => {
1829
- this.assertOpen();
1830
- return this.blobs.read(hash);
1831
- },
1832
- pin: (id, pinned) => {
1833
- this.assertOpen();
1834
- this.entry(id);
1835
- if (pinned) this.log.pins.add(id);
1836
- else this.log.pins.delete(id);
1837
- this.persistLog({ type: "pin", id, pinned });
1838
- },
1839
- onEntry: (listener) => {
1840
- this.listeners.add(listener);
1841
- return () => this.listeners.delete(listener);
1842
- },
1843
- flush: () => this.queue(() => this.settleAll()),
1844
- replacedAtPath: () => this.whereFolder() === "replaced",
1845
- close: () => this.closing ??= (async () => {
1846
- await Promise.all(this.adopting.values());
1847
- if (this.notedTimer) clearTimeout(this.notedTimer);
1848
- this.stopHearing?.();
1849
- const settled = this.queue(() => this.settleAll());
1850
- this.closed = true;
1851
- await settled;
1852
- })()
1853
- };
1854
- }
1855
- };
1856
- function isWithin(dir, path) {
1857
- let probe = resolve3(path);
1858
- while (!existsSync2(probe) && dirname4(probe) !== probe) probe = dirname4(probe);
1859
- const rel = relative2(realpathSync.native(dir), realpathSync.native(probe));
1860
- return rel === "" || rel !== ".." && !rel.startsWith(`..${sep2}`) && !isAbsolute2(rel);
1861
- }
1862
- function missingIsEmpty(error) {
1863
- if (error.code === "ENOENT") return [];
1864
- throw error;
1865
- }
1866
- var HistoryClosedError = class extends Error {
1867
- constructor(message) {
1868
- super(message);
1869
- this.name = "HistoryClosedError";
1870
- }
1871
- };
1872
- async function openProjectHistory(options) {
1873
- if (!existsSync2(options.projectDir))
1874
- throw new HistoryClosedError(`${options.projectDir} is gone or now another project.`);
1875
- const projectId = projectHistoryId(options.projectDir, options.historyRoot);
1876
- const release = await takeHistoryOwnership(
1877
- join5(options.historyRoot, projectId),
1878
- options.ownerWaitMs ?? 5e3
1879
- );
1880
- try {
1881
- const blobs = await openBlobStore(join5(options.historyRoot, projectId, "blobs"));
1882
- const engine = new Engine(options, projectId, blobs);
1883
- await engine.queue(async () => {
1884
- await engine.start();
1885
- engine.hearWrites();
1886
- });
1887
- pruneGoneProjectHistoriesDaily(options.historyRoot, engine.now(), {
1888
- onError: options.onError,
1889
- budgetMs: options.pruneGoneProjectsBudgetMs
1890
- });
1891
- const api = engine.api();
1892
- return {
1893
- ...api,
1894
- close: () => api.close().finally(release)
1895
- };
1896
- } catch (error) {
1897
- release();
1898
- throw error;
1899
- }
1900
- }
1901
- function historyCache(open2) {
1902
- const histories = /* @__PURE__ */ new Map();
1903
- const get = async (projectDir) => {
1904
- let cached = histories.get(projectDir);
1905
- if (!cached) histories.set(projectDir, cached = open2(projectDir));
1906
- const opened = await cached;
1907
- if (!opened?.replacedAtPath()) return opened;
1908
- const reopen = () => open2(projectDir);
1909
- if (histories.get(projectDir) === cached)
1910
- histories.set(projectDir, opened.close().then(reopen, reopen));
1911
- return get(projectDir);
1912
- };
1913
- return {
1914
- get,
1915
- /** The history if one was opened, without opening it. */
1916
- peek: (projectDir) => histories.get(projectDir),
1917
- forget: (projectDir) => histories.delete(projectDir),
1918
- closeAll: () => Promise.all(
1919
- [...histories.values()].map((opened) => opened.then((history) => history?.close()))
1920
- )
1921
- };
1922
- }
1923
-
1924
245
  // ../studio-server/dist/chunk-IMG2GSY6.js
1925
246
  function getElementScreenshotClip(selector, selectorIndex) {
1926
247
  let matches;
@@ -1968,7 +289,7 @@ function clearElementScreenshotIsolation() {
1968
289
  }
1969
290
 
1970
291
  // ../studio-server/dist/chunk-G7FEKQ32.js
1971
- import { resolve as resolve4 } from "path";
292
+ import { resolve } from "path";
1972
293
  function isAutoProxyEnabled(adapter) {
1973
294
  return adapter.autoProxy !== false;
1974
295
  }
@@ -1996,7 +317,7 @@ async function injectMediaCodecMapIntoHtml(html, projectDir, htmlSources, probeC
1996
317
  recordProxyPrewarm();
1997
318
  resolveProxy(
1998
319
  projectDir,
1999
- resolve4(projectDir, rootRelativePathname.replace(/^\/+/, "")),
320
+ resolve(projectDir, rootRelativePathname.replace(/^\/+/, "")),
2000
321
  proxyVariantFor(facts)
2001
322
  ).catch(() => {
2002
323
  });
@@ -2039,52 +360,52 @@ function findUnsafeDomPatchValues(value) {
2039
360
 
2040
361
  // ../studio-server/dist/index.js
2041
362
  import { Hono } from "hono";
2042
- import { readFile as readFile2 } from "fs/promises";
2043
- import { join as join6 } from "path";
363
+ import { readFile } from "fs/promises";
364
+ import { join as join2 } from "path";
2044
365
  import { bodyLimit } from "hono/body-limit";
2045
366
  import {
2046
- closeSync as closeSync3,
367
+ closeSync as closeSync2,
2047
368
  existsSync as existsSync4,
2048
- lstatSync as lstatSync22,
2049
- openSync as openSync3,
2050
- readFileSync as readFileSync52,
369
+ lstatSync as lstatSync2,
370
+ openSync as openSync2,
371
+ readFileSync as readFileSync5,
2051
372
  readlinkSync,
2052
373
  unlinkSync as unlinkSync2,
2053
374
  rmSync as rmSync3,
2054
- statSync as statSync32,
375
+ statSync as statSync3,
2055
376
  fstatSync as fstatSync2,
2056
- renameSync as renameSync22,
2057
- readdirSync as readdirSync22,
2058
- realpathSync as realpathSync2
377
+ renameSync as renameSync2,
378
+ readdirSync as readdirSync2,
379
+ realpathSync
2059
380
  } from "fs";
2060
- import { resolve as resolve22, dirname as dirname32, join as join62 } from "path";
2061
- import { readFileSync as readFileSync23 } from "fs";
2062
- import { createHash as createHash4 } from "crypto";
2063
- import { existsSync as existsSync3, readFileSync as readFileSync7 } from "fs";
2064
- import { join as join23 } from "path";
2065
- import { existsSync as existsSync22 } from "fs";
2066
- import { randomUUID as randomUUID7 } from "crypto";
2067
- import { dirname as dirname5, relative as relative3, resolve as resolve5, sep as sep3 } from "path";
381
+ import { resolve as resolve2, dirname as dirname3, join as join6 } from "path";
382
+ import { readFileSync as readFileSync2 } from "fs";
383
+ import { createHash as createHash2 } from "crypto";
384
+ import { existsSync, readFileSync as readFileSync3 } from "fs";
385
+ import { join as join22 } from "path";
386
+ import { existsSync as existsSync2 } from "fs";
387
+ import { randomUUID } from "crypto";
388
+ import { dirname as dirname2, relative, resolve as resolve3, sep } from "path";
2068
389
  import { spawn } from "child_process";
2069
390
  import {
2070
- existsSync as existsSync32,
2071
- writeFileSync as writeFileSync4,
2072
- mkdirSync as mkdirSync5,
2073
- lstatSync as lstatSync3,
391
+ existsSync as existsSync3,
392
+ writeFileSync as writeFileSync2,
393
+ mkdirSync,
394
+ lstatSync,
2074
395
  mkdtempSync,
2075
- renameSync as renameSync3,
2076
- rmSync as rmSync4,
2077
- statSync as statSync4
396
+ renameSync,
397
+ rmSync,
398
+ statSync
2078
399
  } from "fs";
2079
- import { dirname as dirname23, join as join32 } from "path";
400
+ import { dirname as dirname22, join as join3 } from "path";
2080
401
  import { spawnSync } from "child_process";
2081
- import { mkdtempSync as mkdtempSync2, rmSync as rmSync22, writeFileSync as writeFileSync23 } from "fs";
2082
- import { tmpdir as tmpdir2 } from "os";
2083
- import { basename as basename2, join as join42 } from "path";
2084
- import { readdirSync as readdirSync4, readFileSync as readFileSync32, unlinkSync, writeFileSync as writeFileSync32 } from "fs";
402
+ import { mkdtempSync as mkdtempSync2, rmSync as rmSync2, writeFileSync as writeFileSync22 } from "fs";
403
+ import { tmpdir } from "os";
404
+ import { basename, join as join4 } from "path";
405
+ import { readdirSync, readFileSync as readFileSync32, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
2085
406
  import { Buffer as Buffer2 } from "buffer";
2086
- import { join as join52, relative as relative22 } from "path";
2087
- import { readFileSync as readFileSync42, statSync as statSync22 } from "fs";
407
+ import { join as join5, relative as relative2 } from "path";
408
+ import { readFileSync as readFileSync4, statSync as statSync2 } from "fs";
2088
409
 
2089
410
  // ../parsers/dist/gsapConstants.js
2090
411
  var PROPERTY_GROUPS = {
@@ -3626,8 +1947,8 @@ function upsertProp(ms, objNode, key, value) {
3626
1947
  if (existing) {
3627
1948
  ms.overwrite(existing.value.start, existing.value.end, valueToCode(value));
3628
1949
  } else {
3629
- const sep4 = objNode.properties.length > 0 ? ", " : "";
3630
- ms.appendLeft(objNode.end - 1, `${sep4}${safeKey(key)}: ${valueToCode(value)}`);
1950
+ const sep3 = objNode.properties.length > 0 ? ", " : "";
1951
+ ms.appendLeft(objNode.end - 1, `${sep3}${safeKey(key)}: ${valueToCode(value)}`);
3631
1952
  }
3632
1953
  }
3633
1954
  function isEditableVarKey(key) {
@@ -4245,8 +2566,8 @@ function insertNewKeyframe(ms, kfNode, percentage, pctKey, valueCode) {
4245
2566
  if (insertBeforeProp) {
4246
2567
  ms.appendLeft(insertBeforeProp.start, `${JSON.stringify(pctKey)}: ${valueCode}, `);
4247
2568
  } else {
4248
- const sep4 = allProps.length > 0 ? ", " : "";
4249
- ms.appendLeft(kfNode.end - 1, `${sep4}${JSON.stringify(pctKey)}: ${valueCode}`);
2569
+ const sep3 = allProps.length > 0 ? ", " : "";
2570
+ ms.appendLeft(kfNode.end - 1, `${sep3}${JSON.stringify(pctKey)}: ${valueCode}`);
4250
2571
  }
4251
2572
  }
4252
2573
  function collapseKeyframesToFlat(ms, varsNode, source, remainingRecord) {
@@ -4724,8 +3045,8 @@ function enableArcPath(ms, call, animation, config) {
4724
3045
  const k = propKeyName2(p);
4725
3046
  return k !== "x" && k !== "y";
4726
3047
  });
4727
- const sep4 = survivesRemoval ? ", " : "";
4728
- ms.appendRight(vars.start + 1, ` motionPath: ${motionPathCode}${sep4}`);
3048
+ const sep3 = survivesRemoval ? ", " : "";
3049
+ ms.appendRight(vars.start + 1, ` motionPath: ${motionPathCode}${sep3}`);
4729
3050
  stripXYFromKeyframes(ms, findPropertyNode2(call.varsArg, "keyframes"));
4730
3051
  removePropsByKey(ms, call.varsArg, /* @__PURE__ */ new Set(["x", "y"]));
4731
3052
  return true;
@@ -5249,10 +3570,10 @@ function unrollDynamicAnimations(script, animationId, elements) {
5249
3570
  }
5250
3571
 
5251
3572
  // ../studio-server/dist/index.js
5252
- import { createReadStream as createReadStream2, existsSync as existsSync5, readFileSync as readFileSync62, statSync as statSync42 } from "fs";
3573
+ import { createReadStream, existsSync as existsSync5, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
5253
3574
  import { Readable } from "stream";
5254
- import { join as join8, resolve as resolve42 } from "path";
5255
- import { createHash as createHash23 } from "crypto";
3575
+ import { join as join8, resolve as resolve4 } from "path";
3576
+ import { createHash as createHash22 } from "crypto";
5256
3577
 
5257
3578
  // ../core/dist/studioPreviewMark.js
5258
3579
  var STUDIO_PREVIEW_MARK_META = "hyperframes-studio-preview";
@@ -5261,46 +3582,46 @@ var STUDIO_PREVIEW_ERRORS = "__hfPreviewErrors";
5261
3582
 
5262
3583
  // ../studio-server/dist/index.js
5263
3584
  import { readlinkSync as readlinkSync2 } from "fs";
5264
- import { dirname as dirname42, join as join7, parse as parse2, relative as relative32, resolve as resolve32 } from "path";
5265
- import { readFileSync as readFileSync72 } from "fs";
3585
+ import { dirname as dirname4, join as join7, parse as parse2, relative as relative3, resolve as resolve32 } from "path";
3586
+ import { readFileSync as readFileSync7 } from "fs";
5266
3587
  import { join as join9 } from "path";
5267
3588
  import { streamSSE } from "hono/streaming";
5268
- import { existsSync as existsSync6, lstatSync as lstatSync32, readFileSync as readFileSync8, unlinkSync as unlinkSync3, readdirSync as readdirSync32, statSync as statSync5 } from "fs";
3589
+ import { existsSync as existsSync6, lstatSync as lstatSync3, readFileSync as readFileSync8, unlinkSync as unlinkSync3, readdirSync as readdirSync3, statSync as statSync5 } from "fs";
5269
3590
  import { join as join10 } from "path";
5270
3591
  import { HTTPException } from "hono/http-exception";
5271
- import { open, stat as stat2 } from "fs/promises";
5272
- import { createHash as createHash32 } from "crypto";
3592
+ import { open, stat } from "fs/promises";
3593
+ import { createHash as createHash3 } from "crypto";
5273
3594
  import {
5274
3595
  closeSync as closeSync22,
5275
3596
  fstatSync as fstatSync22,
5276
3597
  openSync as openSync22,
5277
3598
  readFileSync as readFileSync9,
5278
- readdirSync as readdirSync42,
5279
- renameSync as renameSync32,
5280
- rmSync as rmSync42,
3599
+ readdirSync as readdirSync4,
3600
+ renameSync as renameSync3,
3601
+ rmSync as rmSync4,
5281
3602
  statSync as statSync6,
5282
3603
  unlinkSync as unlinkSync4,
5283
- writeFileSync as writeFileSync42
3604
+ writeFileSync as writeFileSync4
5284
3605
  } from "fs";
5285
3606
  import { join as join11 } from "path";
5286
- import { createHash as createHash42, randomUUID as randomUUID22 } from "crypto";
3607
+ import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
5287
3608
  import { existsSync as existsSync7, readFileSync as readFileSync10, statSync as statSync7 } from "fs";
5288
- import { join as join12, resolve as resolve52 } from "path";
3609
+ import { join as join12, resolve as resolve5 } from "path";
5289
3610
  import { execFile } from "child_process";
5290
3611
  import { readFileSync as readFileSync11, rmSync as rmSync5, statSync as statSync8 } from "fs";
5291
- import { basename as basename22, dirname as dirname52, join as join13, relative as relative4, sep as sep22 } from "path";
3612
+ import { basename as basename2, dirname as dirname5, join as join13, relative as relative4, sep as sep2 } from "path";
5292
3613
  import { createHash as createHash5, randomBytes } from "crypto";
5293
3614
  import { existsSync as existsSync8 } from "fs";
5294
3615
  import { existsSync as existsSync9, readFileSync as readFileSync12, statSync as statSync9 } from "fs";
5295
3616
  import { join as join14, resolve as resolve6 } from "path";
5296
3617
  import { execFile as execFile2, spawn as spawn2 } from "child_process";
5297
3618
  import { promisify } from "util";
5298
- import { closeSync as closeSync32, constants as constants3, fstatSync as fstatSync3, openSync as openSync32, readSync } from "fs";
3619
+ import { closeSync as closeSync3, constants as constants2, fstatSync as fstatSync3, openSync as openSync3, readSync } from "fs";
5299
3620
  import { streamSSE as streamSSE2 } from "hono/streaming";
5300
3621
  import { existsSync as existsSync10 } from "fs";
5301
- import { basename as basename3, dirname as dirname6, extname as extname2, join as join15, posix } from "path";
3622
+ import { basename as basename3, dirname as dirname6, extname, join as join15, posix } from "path";
5302
3623
  import { existsSync as existsSync11, readFileSync as readFileSync13 } from "fs";
5303
- import { homedir as homedir2 } from "os";
3624
+ import { homedir } from "os";
5304
3625
  import { join as join16 } from "path";
5305
3626
  import { createHash as createHash6 } from "crypto";
5306
3627
  import { readFileSync as readFileSync14, readdirSync as readdirSync5, rmSync as rmSync6 } from "fs";
@@ -5310,7 +3631,7 @@ async function filterCompositionFiles(projectDir, files) {
5310
3631
  const checks = await Promise.all(
5311
3632
  htmlFiles.map(async (f) => {
5312
3633
  try {
5313
- const content = await readFile2(join6(projectDir, f), "utf-8");
3634
+ const content = await readFile(join2(projectDir, f), "utf-8");
5314
3635
  return isCompositionSource(content);
5315
3636
  } catch {
5316
3637
  return false;
@@ -5391,7 +3712,7 @@ function isAudioFile(name) {
5391
3712
  return (getMimeType(name) ?? "").startsWith("audio/");
5392
3713
  }
5393
3714
  function rewriteRelativePaths(root, compPath, projectDir) {
5394
- const assetExists = (path) => existsSync3(join23(projectDir, path));
3715
+ const assetExists = (path) => existsSync(join22(projectDir, path));
5395
3716
  rewriteAssetPaths(
5396
3717
  root.querySelectorAll("[src], [href]"),
5397
3718
  compPath,
@@ -5491,9 +3812,9 @@ function rootHeadContent(rootHtml) {
5491
3812
  return rootHtml.match(/<head[^>]*>([\s\S]*?)<\/head>/i)?.[1] ?? "";
5492
3813
  }
5493
3814
  function buildSubCompositionHtml(projectDir, compPath, runtimeUrl, baseHref, rawOverride) {
5494
- const compFile = join23(projectDir, compPath);
5495
- if (!existsSync3(compFile)) return null;
5496
- const rawComp = rawOverride ?? readFileSync7(compFile, "utf-8");
3815
+ const compFile = join22(projectDir, compPath);
3816
+ if (!existsSync(compFile)) return null;
3817
+ const rawComp = rawOverride ?? readFileSync3(compFile, "utf-8");
5497
3818
  let compHeadContent = "";
5498
3819
  let rewrittenContent;
5499
3820
  let htmlAttrs = "";
@@ -5528,9 +3849,9 @@ function buildSubCompositionHtml(projectDir, compPath, runtimeUrl, baseHref, raw
5528
3849
  deferScriptsUntilFonts(scriptsDoc);
5529
3850
  rewrittenContent = scriptsDoc.body.innerHTML;
5530
3851
  rewrittenContent = tagRootCompositionFile(rewrittenContent, compPath);
5531
- const indexPath = join23(projectDir, "index.html");
3852
+ const indexPath = join22(projectDir, "index.html");
5532
3853
  let headContent = "";
5533
- if (existsSync3(indexPath)) headContent = rootHeadContent(readFileSync7(indexPath, "utf-8"));
3854
+ if (existsSync(indexPath)) headContent = rootHeadContent(readFileSync3(indexPath, "utf-8"));
5534
3855
  if (baseHref && !hasBaseElement(headContent)) {
5535
3856
  headContent = `<base href="${baseHref}">
5536
3857
  ${headContent}`;
@@ -5592,7 +3913,7 @@ function canonicalProjectPath(projectDir, candidate) {
5592
3913
  if (folderGone(projectDir)) throw new ProjectRootMissingError(projectDir);
5593
3914
  throw new CompositionInsertionError("Composition source escapes the project", 400);
5594
3915
  }
5595
- if (!existsSync22(candidate)) {
3916
+ if (!existsSync2(candidate)) {
5596
3917
  throw new CompositionInsertionError("Composition source was not found", 404);
5597
3918
  }
5598
3919
  const canonical = realpath(candidate);
@@ -5624,7 +3945,7 @@ function canonicalDependency(projectDir, ownerAbs, sourcePath) {
5624
3945
  validateSourcePath(sourcePath);
5625
3946
  return canonicalProjectPath(
5626
3947
  projectDir,
5627
- resolveWithinProject(projectDir, relative3(projectDir, resolve5(dirname5(ownerAbs), sourcePath)))
3948
+ resolveWithinProject(projectDir, relative(projectDir, resolve3(dirname2(ownerAbs), sourcePath)))
5628
3949
  );
5629
3950
  }
5630
3951
  function validateDependencyGraph(projectDir, targetAbs, sourceAbs) {
@@ -5697,7 +4018,7 @@ function uniqueHostId(root, base) {
5697
4018
  return `${base}_${suffix}`;
5698
4019
  }
5699
4020
  function relativeSourcePath(targetAbs, sourceAbs) {
5700
- return relative3(dirname5(targetAbs), sourceAbs).split(sep3).join("/");
4021
+ return relative(dirname2(targetAbs), sourceAbs).split(sep).join("/");
5701
4022
  }
5702
4023
  function insertCompositionIntoSource(input) {
5703
4024
  const targetAbs = canonicalProjectFile(input.projectDir, input.targetPath);
@@ -5732,7 +4053,7 @@ function insertCompositionIntoSource(input) {
5732
4053
  const host = document2.createElement("div");
5733
4054
  host.id = hostId;
5734
4055
  host.className = "clip";
5735
- host.setAttribute("data-hf-id", `hf-${randomUUID7()}`);
4056
+ host.setAttribute("data-hf-id", `hf-${randomUUID()}`);
5736
4057
  host.setAttribute("data-composition-id", hostId);
5737
4058
  host.setAttribute("data-composition-src", relativeSourcePath(targetAbs, sourceAbs));
5738
4059
  host.setAttribute("data-start", String(Math.round(input.start * 100) / 100));
@@ -5761,7 +4082,7 @@ function projectReader(projectDir) {
5761
4082
  const file = resolveWithinProject(projectDir, compPath);
5762
4083
  let source = null;
5763
4084
  try {
5764
- source = file ? readFileSync23(file, "utf-8") : null;
4085
+ source = file ? readFileSync2(file, "utf-8") : null;
5765
4086
  } catch {
5766
4087
  }
5767
4088
  sources.set(compPath, source);
@@ -5813,7 +4134,7 @@ function lengthIsTimeline(projectDir, compPath) {
5813
4134
  }
5814
4135
  var inputSignatures = /* @__PURE__ */ new Map();
5815
4136
  var rootMemos = /* @__PURE__ */ new Map();
5816
- var sha1 = (text2) => createHash4("sha1").update(text2).digest("hex");
4137
+ var sha1 = (text2) => createHash2("sha1").update(text2).digest("hex");
5817
4138
  function rootHashes(read) {
5818
4139
  const source = read(ROOT_COMPOSITION) ?? "";
5819
4140
  return { root: sha1(source), head: sha1(rootHeadContent(source)) };
@@ -5919,36 +4240,36 @@ function decodeAudioPeaks(audioPath) {
5919
4240
  });
5920
4241
  }
5921
4242
  async function generateWaveformCache(projectDir, assetPath) {
5922
- const audioPath = join32(projectDir, assetPath);
5923
- if (!existsSync32(audioPath)) return;
5924
- const stats = statSync4(audioPath);
5925
- const cacheDir = join32(projectDir, ".waveform-cache");
5926
- const cachePath = join32(cacheDir, buildWaveformCacheKey(assetPath, stats));
5927
- if (isWaveformCacheDirectory(cacheDir) && existsSync32(cachePath)) return;
4243
+ const audioPath = join3(projectDir, assetPath);
4244
+ if (!existsSync3(audioPath)) return;
4245
+ const stats = statSync(audioPath);
4246
+ const cacheDir = join3(projectDir, ".waveform-cache");
4247
+ const cachePath = join3(cacheDir, buildWaveformCacheKey(assetPath, stats));
4248
+ if (isWaveformCacheDirectory(cacheDir) && existsSync3(cachePath)) return;
5928
4249
  const peaks = await decodeAudioPeaks(audioPath);
5929
4250
  writeWaveformCache(cachePath, peaks);
5930
4251
  }
5931
4252
  function isWaveformCacheDirectory(cacheDir) {
5932
- return lstatSync3(cacheDir, { throwIfNoEntry: false })?.isDirectory() ?? false;
4253
+ return lstatSync(cacheDir, { throwIfNoEntry: false })?.isDirectory() ?? false;
5933
4254
  }
5934
4255
  function writeWaveformCache(cachePath, peaks) {
5935
- const cacheDir = dirname23(cachePath);
4256
+ const cacheDir = dirname22(cachePath);
5936
4257
  try {
5937
- mkdirSync5(cacheDir, { mode: 448 });
4258
+ mkdirSync(cacheDir, { mode: 448 });
5938
4259
  } catch (error) {
5939
4260
  if (!(error instanceof Error && "code" in error && error.code === "EEXIST")) throw error;
5940
4261
  }
5941
4262
  if (!isWaveformCacheDirectory(cacheDir)) {
5942
4263
  throw new Error("Waveform cache must be a directory, not a symlink");
5943
4264
  }
5944
- const stagingDir = mkdtempSync(join32(cacheDir, ".waveform-"));
4265
+ const stagingDir = mkdtempSync(join3(cacheDir, ".waveform-"));
5945
4266
  try {
5946
- const stagingPath = join32(stagingDir, "peaks.json");
5947
- writeFileSync4(stagingPath, JSON.stringify(peaks), { flag: "wx" });
5948
- renameSync3(stagingPath, cachePath);
4267
+ const stagingPath = join3(stagingDir, "peaks.json");
4268
+ writeFileSync2(stagingPath, JSON.stringify(peaks), { flag: "wx" });
4269
+ renameSync(stagingPath, cachePath);
5949
4270
  } finally {
5950
4271
  try {
5951
- rmSync4(stagingDir, { recursive: true, force: true });
4272
+ rmSync(stagingDir, { recursive: true, force: true });
5952
4273
  } catch {
5953
4274
  }
5954
4275
  }
@@ -6002,13 +4323,13 @@ function checkStreams(stdout, isVideo, isAudio) {
6002
4323
  }
6003
4324
  }
6004
4325
  function validateUploadedMediaBuffer(fileName, buffer, runner = defaultRunner) {
6005
- const tempDir = mkdtempSync2(join42(tmpdir2(), "hyperframes-upload-"));
6006
- const tempPath = join42(tempDir, basename2(fileName));
4326
+ const tempDir = mkdtempSync2(join4(tmpdir(), "hyperframes-upload-"));
4327
+ const tempPath = join4(tempDir, basename(fileName));
6007
4328
  try {
6008
- writeFileSync23(tempPath, buffer);
4329
+ writeFileSync22(tempPath, buffer);
6009
4330
  return validateUploadedMedia(tempPath, runner);
6010
4331
  } finally {
6011
- rmSync22(tempDir, { recursive: true, force: true });
4332
+ rmSync2(tempDir, { recursive: true, force: true });
6012
4333
  }
6013
4334
  }
6014
4335
  var DEFAULT_KEEP_PER_FILE = 10;
@@ -6020,7 +4341,7 @@ function timestampPrefix() {
6020
4341
  }
6021
4342
  function backupPathForResponse(projectDir, backupPath) {
6022
4343
  if (!backupPath) return null;
6023
- const rel = relative22(projectDir, backupPath);
4344
+ const rel = relative2(projectDir, backupPath);
6024
4345
  if (!rel || rel.startsWith("..")) return null;
6025
4346
  return rel.split("\\").join("/");
6026
4347
  }
@@ -6028,12 +4349,12 @@ function snapshotBeforeWrite(projectDir, absPath, options = {}) {
6028
4349
  if (!isSafePath(projectDir, absPath)) return { backupPath: null };
6029
4350
  try {
6030
4351
  const content = readFileSync32(absPath);
6031
- const relativePath = relative22(projectDir, absPath);
6032
- const backupDir = join52(projectDir, ".hyperframes", "backup");
4352
+ const relativePath = relative2(projectDir, absPath);
4353
+ const backupDir = join5(projectDir, ".hyperframes", "backup");
6033
4354
  mkdirWithinProject(projectDir, backupDir);
6034
4355
  const backupKey = backupKeyForPath(relativePath);
6035
4356
  const backupPath = nextBackupPath(backupDir, backupKey);
6036
- writeFileSync32(backupPath, content);
4357
+ writeFileSync3(backupPath, content);
6037
4358
  pruneBackups(backupDir, backupKey, options.keepPerFile ?? DEFAULT_KEEP_PER_FILE);
6038
4359
  return { backupPath };
6039
4360
  } catch (error) {
@@ -6046,7 +4367,7 @@ function snapshotBeforeWrite(projectDir, absPath, options = {}) {
6046
4367
  }
6047
4368
  function nextBackupPath(backupDir, backupKey) {
6048
4369
  const base = `${timestampPrefix()}-${backupKey}`;
6049
- let candidate = join52(backupDir, base);
4370
+ let candidate = join5(backupDir, base);
6050
4371
  let counter = 2;
6051
4372
  while (true) {
6052
4373
  try {
@@ -6057,7 +4378,7 @@ function nextBackupPath(backupDir, backupKey) {
6057
4378
  }
6058
4379
  throw error;
6059
4380
  }
6060
- candidate = join52(backupDir, `${base}-${counter}`);
4381
+ candidate = join5(backupDir, `${base}-${counter}`);
6061
4382
  counter += 1;
6062
4383
  }
6063
4384
  }
@@ -6065,7 +4386,7 @@ function pruneBackups(backupDir, backupKey, keepPerFile) {
6065
4386
  const keep = Math.max(1, Math.floor(keepPerFile));
6066
4387
  const suffix = `-${backupKey}`;
6067
4388
  const numberedSuffix = new RegExp(`-${backupKey}-\\d+$`);
6068
- const matches = readdirSync4(backupDir).filter((name) => name.endsWith(suffix) || numberedSuffix.test(name)).map((name) => join52(backupDir, name)).sort((a, b) => {
4389
+ const matches = readdirSync(backupDir).filter((name) => name.endsWith(suffix) || numberedSuffix.test(name)).map((name) => join5(backupDir, name)).sort((a, b) => {
6069
4390
  return b.localeCompare(a);
6070
4391
  });
6071
4392
  for (const file of matches.slice(keep)) {
@@ -6082,26 +4403,26 @@ function replaceWithProjectDirMissing(c, hostHeaders) {
6082
4403
  c.res = void 0;
6083
4404
  c.res = Response.json(PROJECT_DIR_MISSING, { status: 404, headers: hostHeaders });
6084
4405
  }
6085
- function applyFileMutations(projectDir, mutations, requestToken, writeFile2 = (path, content) => replaceFileAtomically(path, content, statSync22(path).mode)) {
4406
+ function applyFileMutations(projectDir, mutations, requestToken, writeFile = (path, content) => replaceFileAtomically(path, content, statSync2(path).mode)) {
6086
4407
  const prepared = mutations.map((mutation) => ({
6087
4408
  ...mutation,
6088
- before: mutation.before ?? readFileSync42(mutation.absPath, "utf-8")
4409
+ before: mutation.before ?? readFileSync4(mutation.absPath, "utf-8")
6089
4410
  }));
6090
4411
  const results = [];
6091
4412
  const attempted = [];
6092
4413
  for (const mutation of prepared)
6093
- assertExpectedVersion(mutation, readFileSync42(mutation.absPath, "utf-8"));
4414
+ assertExpectedVersion(mutation, readFileSync4(mutation.absPath, "utf-8"));
6094
4415
  try {
6095
4416
  for (const mutation of prepared) {
6096
- results.push(applyOneMutation(projectDir, mutation, requestToken, writeFile2, attempted));
4417
+ results.push(applyOneMutation(projectDir, mutation, requestToken, writeFile, attempted));
6097
4418
  }
6098
4419
  return results;
6099
4420
  } catch (error) {
6100
4421
  const rollbackErrors = [];
6101
4422
  for (const mutation of attempted.reverse()) {
6102
4423
  try {
6103
- if (!mutation.written || readFileSync42(mutation.absPath, "utf-8") === mutation.after)
6104
- writeFile2(mutation.absPath, mutation.before, "utf-8");
4424
+ if (!mutation.written || readFileSync4(mutation.absPath, "utf-8") === mutation.after)
4425
+ writeFile(mutation.absPath, mutation.before, "utf-8");
6105
4426
  clearFileWriteReceipt(mutation.absPath, mutation.version, mutation.writeToken);
6106
4427
  } catch (rollbackError) {
6107
4428
  rollbackErrors.push(rollbackError);
@@ -6116,8 +4437,8 @@ function applyFileMutations(projectDir, mutations, requestToken, writeFile2 = (p
6116
4437
  throw error;
6117
4438
  }
6118
4439
  }
6119
- function applyOneMutation(projectDir, mutation, requestToken, writeFile2, attempted) {
6120
- const current = readFileSync42(mutation.absPath, "utf-8");
4440
+ function applyOneMutation(projectDir, mutation, requestToken, writeFile, attempted) {
4441
+ const current = readFileSync4(mutation.absPath, "utf-8");
6121
4442
  assertExpectedVersion(mutation, current);
6122
4443
  if (mutation.after === mutation.before) {
6123
4444
  return {
@@ -6131,13 +4452,13 @@ function applyOneMutation(projectDir, mutation, requestToken, writeFile2, attemp
6131
4452
  }
6132
4453
  const backup = snapshotBeforeWrite(projectDir, mutation.absPath);
6133
4454
  if (backup.error) throw new Error(`backup failed: ${backup.error}`);
6134
- assertExpectedVersion(mutation, readFileSync42(mutation.absPath, "utf-8"));
4455
+ assertExpectedVersion(mutation, readFileSync4(mutation.absPath, "utf-8"));
6135
4456
  const before = current;
6136
4457
  const version = fileContentVersion(mutation.after);
6137
4458
  const writeToken = createWriteToken(requestToken);
6138
4459
  const attempt = { ...mutation, before, version, writeToken, written: false };
6139
4460
  attempted.push(attempt);
6140
- writeFile2(mutation.absPath, mutation.after, "utf-8");
4461
+ writeFile(mutation.absPath, mutation.after, "utf-8");
6141
4462
  attempt.written = true;
6142
4463
  recordFileWriteReceipt(mutation.absPath, {
6143
4464
  path: mutation.sourceFile,
@@ -6258,18 +4579,18 @@ async function loadGsapParser() {
6258
4579
  return import("./gsapParser-UP3PNPC2.js");
6259
4580
  }
6260
4581
  function isDanglingSymlinkInProject(projectDir, lexicalPath) {
6261
- if (!isSafePath(projectDir, dirname32(lexicalPath))) return false;
4582
+ if (!isSafePath(projectDir, dirname3(lexicalPath))) return false;
6262
4583
  let stats;
6263
4584
  try {
6264
- stats = lstatSync22(lexicalPath);
4585
+ stats = lstatSync2(lexicalPath);
6265
4586
  } catch {
6266
4587
  return false;
6267
4588
  }
6268
4589
  if (!stats.isSymbolicLink()) return false;
6269
- const target = resolve22(dirname32(lexicalPath), readlinkSync(lexicalPath));
4590
+ const target = resolve2(dirname3(lexicalPath), readlinkSync(lexicalPath));
6270
4591
  if (!isSafePath(projectDir, target)) return false;
6271
4592
  try {
6272
- statSync32(lexicalPath);
4593
+ statSync3(lexicalPath);
6273
4594
  return false;
6274
4595
  } catch {
6275
4596
  return true;
@@ -6290,7 +4611,7 @@ async function resolveProjectPath(c, adapter, route, opts) {
6290
4611
  }
6291
4612
  const absPath = (opts?.pin ? pinWithinProject : resolveWithinProject)(project.dir, filePath);
6292
4613
  if (!absPath) {
6293
- if (isDanglingSymlinkInProject(project.dir, resolve22(project.dir, filePath))) {
4614
+ if (isDanglingSymlinkInProject(project.dir, resolve2(project.dir, filePath))) {
6294
4615
  return { error: c.json({ error: "not found", why: "dangling_symlink" }, 404) };
6295
4616
  }
6296
4617
  return { error: c.json({ error: "forbidden", why: "outside_project" }, 403) };
@@ -6361,7 +4682,7 @@ function projectHtmlIds(projectDir) {
6361
4682
  if (!rel.endsWith(".html") || isInHiddenOrVendorDir(rel)) continue;
6362
4683
  let html;
6363
4684
  try {
6364
- html = readFileSync52(join62(projectDir, rel), "utf-8");
4685
+ html = readFileSync5(join6(projectDir, rel), "utf-8");
6365
4686
  } catch {
6366
4687
  continue;
6367
4688
  }
@@ -6369,10 +4690,10 @@ function projectHtmlIds(projectDir) {
6369
4690
  }
6370
4691
  return ids;
6371
4692
  }
6372
- function commitElementPatchBatches(projectDir, batches, writeFile2 = (path, content) => replaceFileAtomically(path, content, statSync32(path).mode), requestToken) {
4693
+ function commitElementPatchBatches(projectDir, batches, writeFile = (path, content) => replaceFileAtomically(path, content, statSync3(path).mode), requestToken) {
6373
4694
  for (let attempt = 1; ; attempt += 1) {
6374
4695
  try {
6375
- return foldAndCommitElementPatchBatches(projectDir, batches, writeFile2, requestToken);
4696
+ return foldAndCommitElementPatchBatches(projectDir, batches, writeFile, requestToken);
6376
4697
  } catch (error) {
6377
4698
  if (!(error instanceof FileChangedError)) throw error;
6378
4699
  if (attempt === PATCH_CONFLICT_ATTEMPTS)
@@ -6380,7 +4701,7 @@ function commitElementPatchBatches(projectDir, batches, writeFile2 = (path, cont
6380
4701
  }
6381
4702
  }
6382
4703
  }
6383
- function foldAndCommitElementPatchBatches(projectDir, batches, writeFile2, requestToken) {
4704
+ function foldAndCommitElementPatchBatches(projectDir, batches, writeFile, requestToken) {
6384
4705
  const resolvedPaths = /* @__PURE__ */ new Set();
6385
4706
  const prepared = [];
6386
4707
  for (const batch of batches) {
@@ -6390,7 +4711,7 @@ function foldAndCommitElementPatchBatches(projectDir, batches, writeFile2, reque
6390
4711
  resolvedPaths.add(absPath);
6391
4712
  let before;
6392
4713
  try {
6393
- before = readFileSync52(absPath, "utf-8");
4714
+ before = readFileSync5(absPath, "utf-8");
6394
4715
  } catch {
6395
4716
  return { error: "not-found", sourceFile: batch.sourceFile };
6396
4717
  }
@@ -6425,7 +4746,7 @@ function foldAndCommitElementPatchBatches(projectDir, batches, writeFile2, reque
6425
4746
  after
6426
4747
  })),
6427
4748
  requestToken,
6428
- writeFile2
4749
+ writeFile
6429
4750
  );
6430
4751
  const files = applied.map((file, index) => ({
6431
4752
  sourceFile: file.sourceFile,
@@ -6446,8 +4767,8 @@ function commitElementPatchBatchesWithReceipts(c, projectDir, batches) {
6446
4767
  );
6447
4768
  }
6448
4769
  function writeFileWithReceipt(c, filePath, absPath, html) {
6449
- const overwrote = readFileSync52(absPath);
6450
- replaceFileAtomically(absPath, html, statSync32(absPath).mode);
4770
+ const overwrote = readFileSync5(absPath);
4771
+ replaceFileAtomically(absPath, html, statSync3(absPath).mode);
6451
4772
  const version = fileContentVersion(html);
6452
4773
  const writeToken = createWriteToken(c.req.header("X-Hyperframes-Write-Token"));
6453
4774
  recordFileWriteReceipt(absPath, { path: filePath, version, writeToken, overwrote });
@@ -6456,7 +4777,7 @@ function writeFileWithReceipt(c, filePath, absPath, html) {
6456
4777
  function writeMutationResult(c, projectDir, filePath, absPath, html, original) {
6457
4778
  const backup = snapshotBeforeWrite(projectDir, absPath);
6458
4779
  if (backup.error) return c.json({ error: `backup failed: ${backup.error}` }, 500);
6459
- if (readFileSync52(absPath, "utf-8") !== original) {
4780
+ if (readFileSync5(absPath, "utf-8") !== original) {
6460
4781
  return c.json({ error: "file changed", conflict: true, path: filePath }, 409);
6461
4782
  }
6462
4783
  const { version } = writeFileWithReceipt(c, filePath, absPath, html);
@@ -6502,7 +4823,7 @@ async function parseMutationBody(c) {
6502
4823
  return { target: body.target, body };
6503
4824
  }
6504
4825
  function ensureDir(projectDir, filePath) {
6505
- mkdirWithinProject(projectDir, dirname32(filePath));
4826
+ mkdirWithinProject(projectDir, dirname3(filePath));
6506
4827
  }
6507
4828
  function generateCopyPath(projectDir, originalPath) {
6508
4829
  const ext = originalPath.includes(".") ? "." + originalPath.split(".").pop() : "";
@@ -6511,7 +4832,7 @@ function generateCopyPath(projectDir, originalPath) {
6511
4832
  const cleanBase = copyMatch ? base.slice(0, -copyMatch[0].length) : base;
6512
4833
  let num = copyMatch ? copyMatch[1] ? parseInt(copyMatch[1]) + 1 : 2 : 1;
6513
4834
  let candidate = num === 1 ? `${cleanBase} (copy)${ext}` : `${cleanBase} (copy ${num})${ext}`;
6514
- while (existsSync4(resolve22(projectDir, candidate))) {
4835
+ while (existsSync4(resolve2(projectDir, candidate))) {
6515
4836
  num++;
6516
4837
  candidate = `${cleanBase} (copy ${num})${ext}`;
6517
4838
  }
@@ -6520,7 +4841,7 @@ function generateCopyPath(projectDir, originalPath) {
6520
4841
  function walkFiles(dir, filter) {
6521
4842
  const results = [];
6522
4843
  for (const entry of readableEntries(dir)) {
6523
- const full2 = join62(dir, entry.name);
4844
+ const full2 = join6(dir, entry.name);
6524
4845
  if (entry.isDirectory()) {
6525
4846
  if (entry.name === "node_modules" || entry.name === ".thumbnails" || entry.name === "renders" || entry.name === ".transcode-cache")
6526
4847
  continue;
@@ -6537,7 +4858,7 @@ function skippable(error) {
6537
4858
  }
6538
4859
  function readableEntries(dir) {
6539
4860
  try {
6540
- return readdirSync22(dir, { withFileTypes: true });
4861
+ return readdirSync2(dir, { withFileTypes: true });
6541
4862
  } catch (error) {
6542
4863
  if (skippable(error)) return [];
6543
4864
  throw error;
@@ -6545,7 +4866,7 @@ function readableEntries(dir) {
6545
4866
  }
6546
4867
  function readableText(file) {
6547
4868
  try {
6548
- return readFileSync52(file, "utf-8");
4869
+ return readFileSync5(file, "utf-8");
6549
4870
  } catch (error) {
6550
4871
  if (skippable(error)) return null;
6551
4872
  throw error;
@@ -6603,7 +4924,7 @@ function occurrences(text2, part) {
6603
4924
  }
6604
4925
  function projectPaths(root, dir = root, prefix = "", ancestors = /* @__PURE__ */ new Set()) {
6605
4926
  return readableEntries(dir).flatMap((entry) => {
6606
- const [path, full2] = [`${prefix}${entry.name}`, join62(dir, entry.name)];
4927
+ const [path, full2] = [`${prefix}${entry.name}`, join6(dir, entry.name)];
6607
4928
  const real = entry.name === "node_modules" ? null : directoryReal(entry, full2);
6608
4929
  if (real === null || ancestors.has(real) || !isSafePath(root, full2)) return [path];
6609
4930
  return [path, ...projectPaths(root, full2, `${path}/`, /* @__PURE__ */ new Set([...ancestors, real]))];
@@ -6611,7 +4932,7 @@ function projectPaths(root, dir = root, prefix = "", ancestors = /* @__PURE__ */
6611
4932
  }
6612
4933
  function directoryReal(entry, full2) {
6613
4934
  try {
6614
- return entry.isDirectory() || entry.isSymbolicLink() && statSync32(full2).isDirectory() ? realpathSync2(full2) : null;
4935
+ return entry.isDirectory() || entry.isSymbolicLink() && statSync3(full2).isDirectory() ? realpathSync(full2) : null;
6615
4936
  } catch {
6616
4937
  return null;
6617
4938
  }
@@ -6629,7 +4950,7 @@ function updateReferences(projectDir, oldPath, newPath, isDirectory) {
6629
4950
  if (content === null) continue;
6630
4951
  const updated = rewrite(content);
6631
4952
  if (updated !== content) {
6632
- replaceFileAtomically(file, updated, statSync32(file).mode);
4953
+ replaceFileAtomically(file, updated, statSync3(file).mode);
6633
4954
  updatedCount++;
6634
4955
  }
6635
4956
  }
@@ -6911,12 +5232,12 @@ function containsRawGsapExpression(value) {
6911
5232
  return Object.values(value).some(containsRawGsapExpression);
6912
5233
  }
6913
5234
  async function prepareGsapMutationScript(c, res, firstMutation) {
6914
- const beforeHtml = readFileSync52(res.absPath, "utf-8");
5235
+ const beforeHtml = readFileSync5(res.absPath, "utf-8");
6915
5236
  let html = beforeHtml;
6916
5237
  let block = extractGsapScriptBlock(html);
6917
5238
  if (!block && (firstMutation.type === "add" || firstMutation.type === "add-with-keyframes")) {
6918
5239
  const compId = html.match(/data-composition-id="([^"]+)"/)?.[1] ?? "main";
6919
- const { GSAP_CDN } = await import("./dist-4JZWRWS6.js");
5240
+ const { GSAP_CDN } = await import("./dist-N33CM63P.js");
6920
5241
  const bootstrap = [
6921
5242
  `<script src="${GSAP_CDN}"></script>`,
6922
5243
  "<script>",
@@ -7028,7 +5349,7 @@ async function applyGsapMutations(c, res, mutations) {
7028
5349
  const changed = block.scriptText !== initialScript;
7029
5350
  const newHtml = changed ? block.replaceScript(block.scriptText) : html;
7030
5351
  let backupPath = null;
7031
- if (readFileSync52(res.absPath, "utf-8") !== beforeHtml) {
5352
+ if (readFileSync5(res.absPath, "utf-8") !== beforeHtml) {
7032
5353
  return c.json({ error: "file changed during GSAP mutation", conflict: true }, 409);
7033
5354
  }
7034
5355
  if (changed) {
@@ -7749,7 +6070,7 @@ async function processUploadedFiles(formData, targetDir, projectDir) {
7749
6070
  skipped.push(name);
7750
6071
  continue;
7751
6072
  }
7752
- const destPath = resolve22(targetDir, name);
6073
+ const destPath = resolve2(targetDir, name);
7753
6074
  if (!isSafePath(projectDir, destPath)) continue;
7754
6075
  let finalPath = destPath;
7755
6076
  let finalName = name;
@@ -7760,13 +6081,13 @@ async function processUploadedFiles(formData, targetDir, projectDir) {
7760
6081
  let n = 1;
7761
6082
  if (existsSync4(finalPath)) {
7762
6083
  n = 2;
7763
- while (n < MAX_COPY_INDEX && existsSync4(resolve22(targetDir, `${base} (${n})${ext}`))) n++;
6084
+ while (n < MAX_COPY_INDEX && existsSync4(resolve2(targetDir, `${base} (${n})${ext}`))) n++;
7764
6085
  if (n >= MAX_COPY_INDEX) {
7765
6086
  skipped.push(name);
7766
6087
  continue;
7767
6088
  }
7768
6089
  finalName = `${base} (${n})${ext}`;
7769
- finalPath = resolve22(targetDir, finalName);
6090
+ finalPath = resolve2(targetDir, finalName);
7770
6091
  }
7771
6092
  if (!isSafePath(projectDir, finalPath) || isPrivateProjectFile(projectDir, finalPath)) continue;
7772
6093
  const buffer = Buffer.from(await value.arrayBuffer());
@@ -7787,14 +6108,14 @@ async function processUploadedFiles(formData, targetDir, projectDir) {
7787
6108
  }
7788
6109
  n++;
7789
6110
  finalName = `${base} (${n})${ext}`;
7790
- finalPath = resolve22(targetDir, finalName);
6111
+ finalPath = resolve2(targetDir, finalName);
7791
6112
  }
7792
6113
  }
7793
6114
  if (!written) {
7794
6115
  if (n >= MAX_COPY_INDEX) skipped.push(name);
7795
6116
  continue;
7796
6117
  }
7797
- const relativePath = subDir ? join62(subDir, finalName) : finalName;
6118
+ const relativePath = subDir ? join6(subDir, finalName) : finalName;
7798
6119
  uploaded.push(relativePath);
7799
6120
  if (validation.unchecked) unchecked.push({ name: finalName, reason: validation.unchecked });
7800
6121
  if (isAudioFile(finalName)) {
@@ -7812,7 +6133,7 @@ function registerFileRoutes(api, adapter) {
7812
6133
  if ("error" in res) return res.error;
7813
6134
  let fd;
7814
6135
  try {
7815
- fd = openSync3(res.absPath, "r");
6136
+ fd = openSync2(res.absPath, "r");
7816
6137
  } catch {
7817
6138
  if (c.req.query("optional") === "1") {
7818
6139
  return c.json({ filename: res.filePath, content: "", missing: true });
@@ -7820,14 +6141,14 @@ function registerFileRoutes(api, adapter) {
7820
6141
  return c.json({ error: "not found" }, 404);
7821
6142
  }
7822
6143
  try {
7823
- const stat22 = fstatSync2(fd);
7824
- if (!stat22.isFile()) {
6144
+ const stat2 = fstatSync2(fd);
6145
+ if (!stat2.isFile()) {
7825
6146
  return c.json({ error: "not found", why: "not_a_file" }, 404);
7826
6147
  }
7827
- if (stat22.size > MAX_TEXT_READ_BYTES) {
6148
+ if (stat2.size > MAX_TEXT_READ_BYTES) {
7828
6149
  return c.json({ error: "too large to read as text", why: "too_large" }, 413);
7829
6150
  }
7830
- const content = readFileSync52(fd);
6151
+ const content = readFileSync5(fd);
7831
6152
  const version = fileContentVersion(content);
7832
6153
  c.header("ETag", version);
7833
6154
  if (content.subarray(0, GIT_BINARY_SNIFF_BYTES).includes(0)) {
@@ -7840,7 +6161,7 @@ function registerFileRoutes(api, adapter) {
7840
6161
  missing: false
7841
6162
  });
7842
6163
  } finally {
7843
- closeSync3(fd);
6164
+ closeSync2(fd);
7844
6165
  }
7845
6166
  });
7846
6167
  api.put("/projects/:id/files/*", async (c) => {
@@ -7852,7 +6173,7 @@ function registerFileRoutes(api, adapter) {
7852
6173
  if (expectedVersion === null && !createOnly) {
7853
6174
  let currentContent = null;
7854
6175
  try {
7855
- currentContent = readFileSync52(res.absPath);
6176
+ currentContent = readFileSync5(res.absPath);
7856
6177
  } catch (error) {
7857
6178
  if (!error || typeof error !== "object" || !("code" in error) || error.code !== "ENOENT") {
7858
6179
  throw error;
@@ -7878,7 +6199,7 @@ function registerFileRoutes(api, adapter) {
7878
6199
  if (!error || typeof error !== "object" || !("code" in error) || error.code !== "EEXIST") {
7879
6200
  throw error;
7880
6201
  }
7881
- const currentContent = readFileSync52(res.absPath);
6202
+ const currentContent = readFileSync5(res.absPath);
7882
6203
  return c.json(
7883
6204
  {
7884
6205
  error: "file conflict",
@@ -7892,7 +6213,7 @@ function registerFileRoutes(api, adapter) {
7892
6213
  } else {
7893
6214
  let fd;
7894
6215
  try {
7895
- fd = openSync3(res.absPath, "r+");
6216
+ fd = openSync2(res.absPath, "r+");
7896
6217
  } catch (error) {
7897
6218
  if (!error || typeof error !== "object" || !("code" in error) || error.code !== "ENOENT") {
7898
6219
  throw error;
@@ -7908,7 +6229,7 @@ function registerFileRoutes(api, adapter) {
7908
6229
  );
7909
6230
  }
7910
6231
  try {
7911
- const currentContent = readFileSync52(fd);
6232
+ const currentContent = readFileSync5(fd);
7912
6233
  overwrote = currentContent;
7913
6234
  const currentVersion = fileContentVersion(currentContent);
7914
6235
  if (expectedVersion !== currentVersion) {
@@ -7925,11 +6246,11 @@ function registerFileRoutes(api, adapter) {
7925
6246
  backup = snapshotBeforeWrite(res.project.dir, res.absPath);
7926
6247
  if (backup.error) return c.json({ error: `backup failed: ${backup.error}` }, 500);
7927
6248
  const mode = fstatSync2(fd).mode;
7928
- closeSync3(fd);
6249
+ closeSync2(fd);
7929
6250
  fd = null;
7930
6251
  replaceFileAtomically(res.absPath, body, mode);
7931
6252
  } finally {
7932
- if (fd !== null) closeSync3(fd);
6253
+ if (fd !== null) closeSync2(fd);
7933
6254
  }
7934
6255
  }
7935
6256
  const version = fileContentVersion(body);
@@ -7962,10 +6283,10 @@ function registerFileRoutes(api, adapter) {
7962
6283
  api.delete("/projects/:id/files/*", async (c) => {
7963
6284
  const res = await resolveProjectFile(c, adapter, { mustExist: true });
7964
6285
  if ("error" in res) return res.error;
7965
- const stat22 = statSync32(res.absPath);
6286
+ const stat2 = statSync3(res.absPath);
7966
6287
  const backup = snapshotBeforeWrite(res.project.dir, res.absPath);
7967
6288
  if (backup.error) return c.json({ error: `backup failed: ${backup.error}` }, 500);
7968
- if (stat22.isDirectory()) {
6289
+ if (stat2.isDirectory()) {
7969
6290
  rmSync3(res.absPath, { recursive: true });
7970
6291
  } else {
7971
6292
  unlinkSync2(res.absPath);
@@ -7984,7 +6305,7 @@ function registerFileRoutes(api, adapter) {
7984
6305
  }
7985
6306
  let before;
7986
6307
  try {
7987
- before = readFileSync52(ctx.absPath, "utf-8");
6308
+ before = readFileSync5(ctx.absPath, "utf-8");
7988
6309
  } catch (error) {
7989
6310
  if (!error || typeof error !== "object" || !("code" in error) || error.code !== "ENOENT") {
7990
6311
  throw error;
@@ -8013,7 +6334,7 @@ function registerFileRoutes(api, adapter) {
8013
6334
  }
8014
6335
  const backup = snapshotBeforeWrite(ctx.project.dir, ctx.absPath);
8015
6336
  if (backup.error) return c.json({ error: `backup failed: ${backup.error}` }, 500);
8016
- const current = readFileSync52(ctx.absPath, "utf-8");
6337
+ const current = readFileSync5(ctx.absPath, "utf-8");
8017
6338
  if (current !== before) {
8018
6339
  const currentVersion2 = fileContentVersion(current);
8019
6340
  return c.json({ error: "file conflict", currentVersion: currentVersion2, currentContent: current }, 409);
@@ -8046,7 +6367,7 @@ function registerFileRoutes(api, adapter) {
8046
6367
  }
8047
6368
  const parsed = await parseMutationBody(c);
8048
6369
  if ("error" in parsed) return parsed.error;
8049
- const originalContent = readFileSync52(ctx.absPath, "utf-8");
6370
+ const originalContent = readFileSync5(ctx.absPath, "utf-8");
8050
6371
  return writeIfChanged(
8051
6372
  c,
8052
6373
  ctx.project.dir,
@@ -8067,7 +6388,7 @@ function registerFileRoutes(api, adapter) {
8067
6388
  if (!Array.isArray(targets) || targets.length === 0) {
8068
6389
  return c.json({ error: "targets required" }, 400);
8069
6390
  }
8070
- const originalContent = readFileSync52(ctx.absPath, "utf-8");
6391
+ const originalContent = readFileSync5(ctx.absPath, "utf-8");
8071
6392
  return writeIfChanged(
8072
6393
  c,
8073
6394
  ctx.project.dir,
@@ -8103,7 +6424,7 @@ function registerFileRoutes(api, adapter) {
8103
6424
  seen.add(absPath);
8104
6425
  let before;
8105
6426
  try {
8106
- before = readFileSync52(absPath, "utf-8");
6427
+ before = readFileSync5(absPath, "utf-8");
8107
6428
  } catch {
8108
6429
  return c.json({ error: `not found: ${file.path}` }, 404);
8109
6430
  }
@@ -8130,7 +6451,7 @@ function registerFileRoutes(api, adapter) {
8130
6451
  prepared.push(folded);
8131
6452
  }
8132
6453
  for (const file of prepared) {
8133
- const current = readFileSync52(file.absPath, "utf-8");
6454
+ const current = readFileSync5(file.absPath, "utf-8");
8134
6455
  if (current !== file.before) {
8135
6456
  return c.json(
8136
6457
  {
@@ -8160,7 +6481,7 @@ function registerFileRoutes(api, adapter) {
8160
6481
  const written = [];
8161
6482
  try {
8162
6483
  for (const file of prepared) {
8163
- replaceFileAtomically(file.absPath, file.after, statSync32(file.absPath).mode);
6484
+ replaceFileAtomically(file.absPath, file.after, statSync3(file.absPath).mode);
8164
6485
  written.push(file);
8165
6486
  recordFileWriteReceipt(file.absPath, {
8166
6487
  path: file.path,
@@ -8173,12 +6494,12 @@ function registerFileRoutes(api, adapter) {
8173
6494
  const conflicts = [];
8174
6495
  for (const file of written.reverse()) {
8175
6496
  try {
8176
- const current = readFileSync52(file.absPath, "utf-8");
6497
+ const current = readFileSync5(file.absPath, "utf-8");
8177
6498
  if (current !== file.after) {
8178
6499
  conflicts.push(file.path);
8179
6500
  continue;
8180
6501
  }
8181
- replaceFileAtomically(file.absPath, file.before, statSync32(file.absPath).mode);
6502
+ replaceFileAtomically(file.absPath, file.before, statSync3(file.absPath).mode);
8182
6503
  recordFileWriteReceipt(file.absPath, {
8183
6504
  path: file.path,
8184
6505
  version: fileContentVersion(file.before),
@@ -8221,7 +6542,7 @@ function registerFileRoutes(api, adapter) {
8221
6542
  const fallbackTiming = typeof parsed.body.elementStart === "number" && typeof parsed.body.elementDuration === "number" ? { start: parsed.body.elementStart, duration: parsed.body.elementDuration } : void 0;
8222
6543
  let originalContent;
8223
6544
  try {
8224
- originalContent = readFileSync52(ctx.absPath, "utf-8");
6545
+ originalContent = readFileSync5(ctx.absPath, "utf-8");
8225
6546
  } catch {
8226
6547
  return c.json({ error: "not found" }, 404);
8227
6548
  }
@@ -8283,7 +6604,7 @@ function registerFileRoutes(api, adapter) {
8283
6604
  for (let attempt = 1; ; attempt += 1) {
8284
6605
  let originalContent;
8285
6606
  try {
8286
- originalContent = readFileSync52(ctx.absPath, "utf-8");
6607
+ originalContent = readFileSync5(ctx.absPath, "utf-8");
8287
6608
  } catch {
8288
6609
  return c.json({ error: "not found" }, 404);
8289
6610
  }
@@ -8395,7 +6716,7 @@ function registerFileRoutes(api, adapter) {
8395
6716
  }
8396
6717
  let originalContent;
8397
6718
  try {
8398
- originalContent = readFileSync52(ctx.absPath, "utf-8");
6719
+ originalContent = readFileSync5(ctx.absPath, "utf-8");
8399
6720
  } catch {
8400
6721
  return c.json({ error: "not found" }, 404);
8401
6722
  }
@@ -8451,7 +6772,7 @@ function registerFileRoutes(api, adapter) {
8451
6772
  ).map((entry) => ({ target: entry.target, track: entry.track }));
8452
6773
  let originalContent;
8453
6774
  try {
8454
- originalContent = readFileSync52(ctx.absPath, "utf-8");
6775
+ originalContent = readFileSync5(ctx.absPath, "utf-8");
8455
6776
  } catch {
8456
6777
  return c.json({ error: "not found" }, 404);
8457
6778
  }
@@ -8480,7 +6801,7 @@ function registerFileRoutes(api, adapter) {
8480
6801
  if ("error" in parsed) return parsed.error;
8481
6802
  let content;
8482
6803
  try {
8483
- content = readFileSync52(ctx.absPath, "utf-8");
6804
+ content = readFileSync5(ctx.absPath, "utf-8");
8484
6805
  } catch {
8485
6806
  return c.json({ exists: false });
8486
6807
  }
@@ -8494,7 +6815,7 @@ function registerFileRoutes(api, adapter) {
8494
6815
  if (!Array.isArray(body?.targets)) return c.json({ error: "targets required" }, 400);
8495
6816
  let content;
8496
6817
  try {
8497
- content = readFileSync52(ctx.absPath, "utf-8");
6818
+ content = readFileSync5(ctx.absPath, "utf-8");
8498
6819
  } catch {
8499
6820
  return c.json({ exists: body.targets.map(() => false) });
8500
6821
  }
@@ -8514,9 +6835,9 @@ function registerFileRoutes(api, adapter) {
8514
6835
  if (existsSync4(newAbs)) {
8515
6836
  return c.json({ error: "already exists" }, 409);
8516
6837
  }
8517
- const isDirectory = statSync32(res.absPath, { throwIfNoEntry: false })?.isDirectory() ?? false;
6838
+ const isDirectory = statSync3(res.absPath, { throwIfNoEntry: false })?.isDirectory() ?? false;
8518
6839
  ensureDir(res.project.dir, newAbs);
8519
- renameSync22(res.absPath, newAbs);
6840
+ renameSync2(res.absPath, newAbs);
8520
6841
  const updatedFiles = updateReferences(res.project.dir, res.filePath, body.newPath, isDirectory);
8521
6842
  return c.json({ ok: true, path: body.newPath, updatedReferences: updatedFiles });
8522
6843
  });
@@ -8538,7 +6859,7 @@ function registerFileRoutes(api, adapter) {
8538
6859
  }
8539
6860
  ensureDir(project.dir, destAbs);
8540
6861
  try {
8541
- createFileAtomically(destAbs, readFileSync52(srcAbs));
6862
+ createFileAtomically(destAbs, readFileSync5(srcAbs));
8542
6863
  } catch (error) {
8543
6864
  if (!error || typeof error !== "object" || !("code" in error) || error.code !== "EEXIST") {
8544
6865
  throw error;
@@ -8581,7 +6902,7 @@ function registerFileRoutes(api, adapter) {
8581
6902
  mustExist: true
8582
6903
  });
8583
6904
  if ("error" in res) return res.error;
8584
- const html = readFileSync52(res.absPath, "utf-8");
6905
+ const html = readFileSync5(res.absPath, "utf-8");
8585
6906
  const etag = `${fileContentVersion(html).slice(0, -1)}:gsap-animations:v1"`;
8586
6907
  const headers = { ETag: etag, "Cache-Control": "no-cache" };
8587
6908
  if (c.req.header("If-None-Match") === etag) return new Response(null, { status: 304, headers });
@@ -8638,11 +6959,11 @@ function registerFileRoutes(api, adapter) {
8638
6959
  if (!body || typeof body.expected !== "string" || typeof body.restore !== "string") {
8639
6960
  return c.json({ error: "expected and restore contents required" }, 400);
8640
6961
  }
8641
- const current = readFileSync52(res.absPath, "utf-8");
6962
+ const current = readFileSync5(res.absPath, "utf-8");
8642
6963
  if (current !== body.expected) {
8643
6964
  return c.json({ ok: true, restored: false, conflict: true });
8644
6965
  }
8645
- replaceFileAtomically(res.absPath, body.restore, statSync32(res.absPath).mode);
6966
+ replaceFileAtomically(res.absPath, body.restore, statSync3(res.absPath).mode);
8646
6967
  return c.json({ ok: true, restored: true, conflict: false });
8647
6968
  });
8648
6969
  }
@@ -8656,7 +6977,7 @@ var pathKey = process.platform === "darwin" || process.platform === "win32" ? (p
8656
6977
  var readsByProject = /* @__PURE__ */ new Map();
8657
6978
  var builtProjects = /* @__PURE__ */ new Set();
8658
6979
  var projectKey = (projectDir) => pathKey(realFilePath(resolve32(projectDir)));
8659
- var inRealProject = (projectDir, path) => join7(realFilePath(resolve32(projectDir)), relative32(resolve32(projectDir), resolve32(projectDir, path)));
6980
+ var inRealProject = (projectDir, path) => join7(realFilePath(resolve32(projectDir)), relative3(resolve32(projectDir), resolve32(projectDir, path)));
8660
6981
  var readLink = (path) => {
8661
6982
  try {
8662
6983
  return readlinkSync2(path);
@@ -8672,7 +6993,7 @@ function linksOnTheWay(path) {
8672
6993
  const part = rest.shift();
8673
6994
  if (!part || part === ".") continue;
8674
6995
  if (part === "..") {
8675
- at = dirname42(at);
6996
+ at = dirname4(at);
8676
6997
  continue;
8677
6998
  }
8678
6999
  const next = join7(at, part);
@@ -8699,7 +7020,7 @@ function recordPreviewRead(projectDir, filePath) {
8699
7020
  const read = inRealProject(projectDir, filePath);
8700
7021
  if (read.length > MAX_PREVIEW_PATH_LENGTH) return;
8701
7022
  for (const found of [read, ...linksOnTheWay(read)]) {
8702
- for (let path = pathKey(found); !reads.has(path); path = dirname42(path)) reads.add(path);
7023
+ for (let path = pathKey(found); !reads.has(path); path = dirname4(path)) reads.add(path);
8703
7024
  }
8704
7025
  }
8705
7026
  function recordPreviewReferences(projectDir, html) {
@@ -8810,7 +7131,7 @@ function readStudioMotionManifestContent(projectDir) {
8810
7131
  const manifestPath = join8(projectDir, STUDIO_MOTION_PATH);
8811
7132
  if (!existsSync5(manifestPath)) return "";
8812
7133
  try {
8813
- return readFileSync62(manifestPath, "utf-8");
7134
+ return readFileSync6(manifestPath, "utf-8");
8814
7135
  } catch {
8815
7136
  return "";
8816
7137
  }
@@ -8954,7 +7275,7 @@ function parsePreviewVariablesParam(raw) {
8954
7275
  }
8955
7276
  function variablesEtagSalt(raw) {
8956
7277
  if (!raw) return "";
8957
- return `:vars:${createHash23("sha1").update(raw).digest("hex").slice(0, 12)}`;
7278
+ return `:vars:${createHash22("sha1").update(raw).digest("hex").slice(0, 12)}`;
8958
7279
  }
8959
7280
  function previewVariablesFromRequest(rawVariables) {
8960
7281
  const parse22 = parsePreviewVariablesParam(rawVariables);
@@ -8999,13 +7320,13 @@ function resolveProjectMainHtml(projectDir, projectId) {
8999
7320
  recordPreviewRead(projectDir, blockHtmlPath);
9000
7321
  if (existsSync5(indexPath)) {
9001
7322
  return {
9002
- html: readFileSync62(indexPath, "utf-8"),
7323
+ html: readFileSync6(indexPath, "utf-8"),
9003
7324
  compositionPath: "index.html"
9004
7325
  };
9005
7326
  }
9006
7327
  if (existsSync5(blockHtmlPath)) {
9007
7328
  return {
9008
- html: readFileSync62(blockHtmlPath, "utf-8"),
7329
+ html: readFileSync6(blockHtmlPath, "utf-8"),
9009
7330
  compositionPath: `${projectId}.html`
9010
7331
  };
9011
7332
  }
@@ -9136,7 +7457,7 @@ ${etag}`;
9136
7457
  function pinSubCompHfIds(compFile, compPath) {
9137
7458
  if (!/\.html?$/i.test(compPath)) return void 0;
9138
7459
  try {
9139
- return ensureHfIds(readFileSync62(compFile, "utf-8"));
7460
+ return ensureHfIds(readFileSync6(compFile, "utf-8"));
9140
7461
  } catch {
9141
7462
  return null;
9142
7463
  }
@@ -9152,10 +7473,10 @@ ${etag}`;
9152
7473
  const compFile = resolveWithinProject(project.dir, compPath);
9153
7474
  if (compFile) recordPreviewRead(project.dir, compFile);
9154
7475
  recordPreviewRead(project.dir, "index.html");
9155
- if (!compFile || !existsSync5(compFile) || !statSync42(compFile).isFile()) {
7476
+ if (!compFile || !existsSync5(compFile) || !statSync4(compFile).isFile()) {
9156
7477
  return c.text("not found", 404);
9157
7478
  }
9158
- const compPathHash = createHash23("sha1").update(compPath).digest("hex");
7479
+ const compPathHash = createHash22("sha1").update(compPath).digest("hex");
9159
7480
  const capture = c.req.query(PREVIEW_CAPTURE_PARAM) !== void 0;
9160
7481
  const etag = `"comp:v2:${compPathHash}:${signature}${variablesEtagSalt(vars.raw)}${capture ? ":capture" : ""}"`;
9161
7482
  const ifNoneMatch = c.req.header("If-None-Match");
@@ -9187,14 +7508,14 @@ ${etag}`;
9187
7508
  const project = await adapter.resolveProject(c.req.param("id"));
9188
7509
  if (!project) return c.json({ error: "not found" }, 404);
9189
7510
  const subPath = requestSubPath(c.req.url, "projects/:id/preview");
9190
- const candidate = resolve42(project.dir, subPath);
7511
+ const candidate = resolve4(project.dir, subPath);
9191
7512
  const file = isWithinProjectRoot(project.dir, candidate) ? candidate : null;
9192
7513
  if (!file || isPrivateProjectFile(project.dir, file)) {
9193
7514
  return c.text("not found", 404);
9194
7515
  }
9195
7516
  recordPreviewRead(project.dir, file);
9196
- const stat22 = existsSync5(file) ? statSync42(file) : null;
9197
- if (!stat22?.isFile()) {
7517
+ const stat2 = existsSync5(file) ? statSync4(file) : null;
7518
+ if (!stat2?.isFile()) {
9198
7519
  return c.text("not found", 404);
9199
7520
  }
9200
7521
  const contentType = getMimeType(subPath);
@@ -9216,7 +7537,7 @@ ${etag}`;
9216
7537
  return c.text("media proxy variant does not match asset", 422);
9217
7538
  }
9218
7539
  }
9219
- const tag = settledFileTag(stat22);
7540
+ const tag = settledFileTag(stat2);
9220
7541
  const etag = tag && `"${tag}${proxyEtagSalt(proxyVariant)}"`;
9221
7542
  const cacheHeaders = isText ? { "Cache-Control": "no-store" } : { "Cache-Control": "private, no-cache", ...etag && { ETag: etag } };
9222
7543
  if (!isText && etag) {
@@ -9247,13 +7568,13 @@ ${etag}`;
9247
7568
  recordProxyRequest();
9248
7569
  servedContentType = PROXY_VARIANT_CONFIG[proxyVariant].contentType;
9249
7570
  }
9250
- const textBuffer = isText ? Buffer.from(readFileSync62(file, "utf-8"), "utf-8") : null;
9251
- const totalSize = textBuffer ? textBuffer.length : statSync42(servedPath).size;
7571
+ const textBuffer = isText ? Buffer.from(readFileSync6(file, "utf-8"), "utf-8") : null;
7572
+ const totalSize = textBuffer ? textBuffer.length : statSync4(servedPath).size;
9252
7573
  const bodyFor = (start, end) => textBuffer ? new Uint8Array(textBuffer.subarray(start, end + 1)) : (
9253
7574
  // Node's web stream type and the DOM one do not overlap for tsc on
9254
7575
  // every platform's lib set; the double cast is the documented bridge.
9255
7576
  Readable.toWeb(
9256
- createReadStream2(servedPath, { start, end })
7577
+ createReadStream(servedPath, { start, end })
9257
7578
  )
9258
7579
  );
9259
7580
  const rangeHeader = c.req.header("Range");
@@ -9306,7 +7627,7 @@ async function lintUncoveredHtml(adapter, projectDir, htmlFiles, coveredFiles) {
9306
7627
  const findings = [];
9307
7628
  for (const file of htmlFiles) {
9308
7629
  if (coveredFiles.has(file)) continue;
9309
- const content = readFileSync72(join9(projectDir, file), "utf-8");
7630
+ const content = readFileSync7(join9(projectDir, file), "utf-8");
9310
7631
  const result = await adapter.lint(content, { filePath: file, isSubComposition: true });
9311
7632
  for (const finding of result?.findings ?? []) {
9312
7633
  findings.push({ ...finding, file });
@@ -9462,7 +7783,7 @@ function registerRenderRoutes(api, adapter) {
9462
7783
  const RENDER_EXTENSIONS = Object.keys(RENDER_MIME);
9463
7784
  const isPlainFile = (path) => {
9464
7785
  try {
9465
- return lstatSync32(path).isFile();
7786
+ return lstatSync3(path).isFile();
9466
7787
  } catch {
9467
7788
  return false;
9468
7789
  }
@@ -9545,11 +7866,11 @@ function registerRenderRoutes(api, adapter) {
9545
7866
  if (!project) return c.json({ error: "not found" }, 404);
9546
7867
  const rendersDir = adapter.rendersDir(project);
9547
7868
  if (!existsSync6(rendersDir)) return c.json({ renders: [] });
9548
- const files = readdirSync32(rendersDir).filter((f) => f.endsWith(".mp4") || f.endsWith(".webm") || f.endsWith(".mov")).flatMap((f) => {
7869
+ const files = readdirSync3(rendersDir).filter((f) => f.endsWith(".mp4") || f.endsWith(".webm") || f.endsWith(".mov")).flatMap((f) => {
9549
7870
  const fp = resolveWithinProject(rendersDir, f);
9550
7871
  return fp && !isPrivateProjectFile(project.dir, fp) ? [{ f, fp }] : [];
9551
7872
  }).map(({ f, fp }) => {
9552
- const stat22 = statSync5(fp);
7873
+ const stat2 = statSync5(fp);
9553
7874
  const rid = f.replace(/\.(mp4|webm|mov)$/, "");
9554
7875
  const metaPath = join10(rendersDir, `${rid}.meta.json`);
9555
7876
  let status = "complete";
@@ -9568,8 +7889,8 @@ function registerRenderRoutes(api, adapter) {
9568
7889
  id: rid,
9569
7890
  filename: f,
9570
7891
  path: fp,
9571
- size: stat22.size,
9572
- createdAt: stat22.mtimeMs,
7892
+ size: stat2.size,
7893
+ createdAt: stat2.mtimeMs,
9573
7894
  status,
9574
7895
  durationMs,
9575
7896
  ...audioLoweredDb !== void 0 ? { audioLoweredDb } : {}
@@ -9740,7 +8061,7 @@ async function resolveSource(adapter, id, url) {
9740
8061
  if (!/\.jpe?g$/i.test(relative5)) throw new HTTPException(415);
9741
8062
  const path = pinWithinProject(project.dir, relative5);
9742
8063
  if (!path) throw new HTTPException(404);
9743
- const info = await stat2(path).catch(() => {
8064
+ const info = await stat(path).catch(() => {
9744
8065
  throw new HTTPException(404);
9745
8066
  });
9746
8067
  if (!info.isFile()) throw new HTTPException(404);
@@ -9766,7 +8087,7 @@ function registerImageThumbnailRoutes(api, adapter) {
9766
8087
  api.get("/projects/:id/image-thumbnail/*", async (c) => {
9767
8088
  try {
9768
8089
  const { path, info } = await resolveSource(adapter, c.req.param("id"), c.req.url);
9769
- const key = createHash32("sha256").update(JSON.stringify([path, info.dev, info.ino, info.size, info.mtimeMs, info.ctimeMs])).digest("hex");
8090
+ const key = createHash3("sha256").update(JSON.stringify([path, info.dev, info.ino, info.size, info.mtimeMs, info.ctimeMs])).digest("hex");
9770
8091
  const etag = `"${key}"`;
9771
8092
  c.header("ETag", etag);
9772
8093
  c.header("Cache-Control", "private, no-cache");
@@ -9800,7 +8121,7 @@ var prunedCacheDirs = /* @__PURE__ */ new Set();
9800
8121
  function pruneThumbnailCache(cacheDir, protectedPaths, now = Date.now()) {
9801
8122
  let entries;
9802
8123
  try {
9803
- entries = readdirSync42(cacheDir, { withFileTypes: true });
8124
+ entries = readdirSync4(cacheDir, { withFileTypes: true });
9804
8125
  } catch (err) {
9805
8126
  if (err.code === "ENOENT") return;
9806
8127
  throw err;
@@ -9818,7 +8139,7 @@ function pruneThumbnailCache(cacheDir, protectedPaths, now = Date.now()) {
9818
8139
  const retained = [];
9819
8140
  for (const file of files) {
9820
8141
  if (!protectedPaths.has(file.path) && now - file.mtimeMs > THUMBNAIL_CACHE_MAX_AGE_MS) {
9821
- rmSync42(file.path, { force: true });
8142
+ rmSync4(file.path, { force: true });
9822
8143
  } else {
9823
8144
  retained.push(file);
9824
8145
  }
@@ -9835,12 +8156,12 @@ function pruneThumbnailCache(cacheDir, protectedPaths, now = Date.now()) {
9835
8156
  }
9836
8157
  }
9837
8158
  function writeThumbnailAtomically(path, buffer) {
9838
- const temporaryPath = `${path}.${process.pid}.${randomUUID22()}.tmp`;
8159
+ const temporaryPath = `${path}.${process.pid}.${randomUUID2()}.tmp`;
9839
8160
  try {
9840
- writeFileSync42(temporaryPath, buffer, { flag: "wx" });
9841
- renameSync32(temporaryPath, path);
8161
+ writeFileSync4(temporaryPath, buffer, { flag: "wx" });
8162
+ renameSync3(temporaryPath, path);
9842
8163
  } finally {
9843
- rmSync42(temporaryPath, { force: true });
8164
+ rmSync4(temporaryPath, { force: true });
9844
8165
  }
9845
8166
  }
9846
8167
  function readFileOnce(file) {
@@ -9854,9 +8175,9 @@ function readFileOnce(file) {
9854
8175
  throw err;
9855
8176
  }
9856
8177
  try {
9857
- const stat22 = fstatSync22(fd);
9858
- if (!stat22.isFile()) return "not-a-file";
9859
- return { data: readFileSync9(fd), mtimeMs: stat22.mtimeMs };
8178
+ const stat2 = fstatSync22(fd);
8179
+ if (!stat2.isFile()) return "not-a-file";
8180
+ return { data: readFileSync9(fd), mtimeMs: stat2.mtimeMs };
9860
8181
  } finally {
9861
8182
  closeSync22(fd);
9862
8183
  }
@@ -9864,7 +8185,7 @@ function readFileOnce(file) {
9864
8185
  function manifestKey(file) {
9865
8186
  const manifest = readFileOnce(file);
9866
8187
  if (typeof manifest === "string") return { key: "", mtimeMs: -Infinity };
9867
- const hash = createHash42("sha1").update(manifest.data).digest("hex").slice(0, 16);
8188
+ const hash = createHash4("sha1").update(manifest.data).digest("hex").slice(0, 16);
9868
8189
  return { key: `_${hash}`, mtimeMs: Math.round(manifest.mtimeMs) };
9869
8190
  }
9870
8191
  function registerThumbnailRoutes(api, adapter) {
@@ -9901,7 +8222,7 @@ function registerThumbnailRoutes(api, adapter) {
9901
8222
  let sourceKey = "";
9902
8223
  if (source !== "missing") {
9903
8224
  const html = source.data.toString("utf-8");
9904
- sourceKey = `_${createHash42("sha1").update(html).digest("hex").slice(0, 16)}`;
8225
+ sourceKey = `_${createHash4("sha1").update(html).digest("hex").slice(0, 16)}`;
9905
8226
  sourceMtime = Math.round(source.mtimeMs);
9906
8227
  if (!vpWidth) {
9907
8228
  const wMatch = html.match(/data-width=["'](\d+)["']/);
@@ -9917,9 +8238,9 @@ function registerThumbnailRoutes(api, adapter) {
9917
8238
  const previewPath = compPath === "index.html" ? `${projectUrl}/preview` : `${projectUrl}/preview/comp/${compPath.split("/").map(encodeURIComponent).join("/")}`;
9918
8239
  const previewUrl = `${previewPath}?${PREVIEW_CAPTURE_PARAM}=1`;
9919
8240
  const cacheDir = join11(project.dir, ".thumbnails");
9920
- const selectorKey = selector ? `_${createHash42("sha1").update(selector).digest("hex").slice(0, 16)}_${selectorIndex ?? 0}` : "";
8241
+ const selectorKey = selector ? `_${createHash4("sha1").update(selector).digest("hex").slice(0, 16)}_${selectorIndex ?? 0}` : "";
9921
8242
  const urlVersionKey = urlVersion ? `_${urlVersion.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32)}` : "";
9922
- const inputSignatureKey = `_${createHash42("sha1").update(inputSignature).digest("hex").slice(0, 16)}`;
8243
+ const inputSignatureKey = `_${createHash4("sha1").update(inputSignature).digest("hex").slice(0, 16)}`;
9923
8244
  const outputScale = outputMode === "source" ? 1 : Math.min(1, THUMBNAIL_MAX_OUTPUT_WIDTH / compW, THUMBNAIL_MAX_OUTPUT_HEIGHT / compH);
9924
8245
  const outputWidth = Math.max(1, Math.round(compW * outputScale));
9925
8246
  const outputHeight = Math.max(1, Math.round(compH * outputScale));
@@ -9997,7 +8318,7 @@ function registerWaveformRoutes(api, adapter) {
9997
8318
  const project = await adapter.resolveProject(c.req.param("id"));
9998
8319
  if (!project) return c.json({ error: "not found" }, 404);
9999
8320
  const assetPath = requestSubPath(c.req.url, "projects/:id/waveform");
10000
- const audioPath = resolve52(project.dir, assetPath);
8321
+ const audioPath = resolve5(project.dir, assetPath);
10001
8322
  if (!isWithinProjectRoot(project.dir, audioPath)) {
10002
8323
  return c.json({ error: "file not found" }, 404);
10003
8324
  }
@@ -10250,20 +8571,20 @@ async function extractThenPublishWhole(extract, mediaPath, mediaTime, imagePath)
10250
8571
  return { error: `Could not extract the frame: ${reason}`, status: 500 };
10251
8572
  }
10252
8573
  if (publishStill(imagePath, frame)) return null;
10253
- return { error: `freeze still already exists: ${basename22(imagePath)}`, status: 409 };
8574
+ return { error: `freeze still already exists: ${basename2(imagePath)}`, status: 409 };
10254
8575
  } finally {
10255
8576
  rmSync5(unseenByHistory, { force: true });
10256
8577
  }
10257
8578
  }
10258
8579
  async function extractStill(projectDir, absPath, source, playhead, tools) {
10259
- const fileDir = dirname52(absPath);
8580
+ const fileDir = dirname5(absPath);
10260
8581
  const mediaPath = pinWithinProject(projectDir, relative4(projectDir, join13(fileDir, source.src)));
10261
8582
  if (!mediaPath) return { error: `forbidden media path: ${source.src}`, status: 403 };
10262
8583
  const freezeDir = join13(projectDir, ...FREEZE_DIR);
10263
8584
  const fileName = freezeStillFileName(source.id, playhead, tools.stillToken());
10264
8585
  mkdirWithinProject(projectDir, freezeDir);
10265
8586
  const imagePath = pinWithinProject(projectDir, join13(...FREEZE_DIR, fileName));
10266
- if (!imagePath || dirname52(imagePath) !== freezeDir) {
8587
+ if (!imagePath || dirname5(imagePath) !== freezeDir) {
10267
8588
  return { error: `forbidden freeze path: ${fileName}`, status: 403 };
10268
8589
  }
10269
8590
  const failed = await extractThenPublishWhole(
@@ -10273,7 +8594,7 @@ async function extractStill(projectDir, absPath, source, playhead, tools) {
10273
8594
  imagePath
10274
8595
  );
10275
8596
  if (failed) return failed;
10276
- const depth = relative4(projectDir, fileDir).split(sep22).filter(Boolean).length;
8597
+ const depth = relative4(projectDir, fileDir).split(sep2).filter(Boolean).length;
10277
8598
  const stillPath = `${FREEZE_DIR.join("/")}/${fileName}`;
10278
8599
  return { imageSrc: `${"../".repeat(depth)}${stillPath}`, stillPath, imagePath };
10279
8600
  }
@@ -10643,17 +8964,17 @@ function registerFontRoutes(api) {
10643
8964
  if (!located) return c.json({ error: "font not found" }, 404);
10644
8965
  let fd;
10645
8966
  try {
10646
- fd = openSync32(located.path, constants3.O_RDONLY | constants3.O_NOFOLLOW);
8967
+ fd = openSync3(located.path, constants2.O_RDONLY | constants2.O_NOFOLLOW);
10647
8968
  } catch {
10648
8969
  return c.json({ error: "font file not accessible" }, 404);
10649
8970
  }
10650
8971
  try {
10651
- const stat22 = fstatSync3(fd);
10652
- if (stat22.size > SYSTEM_FONT_SIZE_LIMIT) {
8972
+ const stat2 = fstatSync3(fd);
8973
+ if (stat2.size > SYSTEM_FONT_SIZE_LIMIT) {
10653
8974
  return c.json({ error: "font file too large" }, 413);
10654
8975
  }
10655
- const buffer = Buffer.alloc(stat22.size);
10656
- readSync(fd, buffer, 0, stat22.size, 0);
8976
+ const buffer = Buffer.alloc(stat2.size);
8977
+ readSync(fd, buffer, 0, stat2.size, 0);
10657
8978
  const mimeType = located.format === "otf" ? "font/otf" : located.format === "woff2" ? "font/woff2" : located.format === "woff" ? "font/woff" : located.format === "ttc" ? "font/collection" : "font/ttf";
10658
8979
  const fileName = `${family.replace(/[^a-zA-Z0-9 -]/g, "")}.${located.format}`;
10659
8980
  return new Response(buffer, {
@@ -10665,7 +8986,7 @@ function registerFontRoutes(api) {
10665
8986
  } catch {
10666
8987
  return c.json({ error: "failed to read font file" }, 500);
10667
8988
  } finally {
10668
- closeSync32(fd);
8989
+ closeSync3(fd);
10669
8990
  }
10670
8991
  });
10671
8992
  }
@@ -10811,10 +9132,10 @@ var VIDEO_OUTPUT_EXTENSIONS = /* @__PURE__ */ new Set([".webm", ".mov"]);
10811
9132
  var QUALITIES = /* @__PURE__ */ new Set(["fast", "balanced", "best"]);
10812
9133
  var DEVICES = /* @__PURE__ */ new Set(["auto", "cpu", "coreml", "cuda"]);
10813
9134
  function isVideoPath(path) {
10814
- return VIDEO_EXTENSIONS.has(extname2(path).toLowerCase());
9135
+ return VIDEO_EXTENSIONS.has(extname(path).toLowerCase());
10815
9136
  }
10816
9137
  function isImagePath(path) {
10817
- return IMAGE_EXTENSIONS.has(extname2(path).toLowerCase());
9138
+ return IMAGE_EXTENSIONS.has(extname(path).toLowerCase());
10818
9139
  }
10819
9140
  function normalizeProjectAssetPath(path) {
10820
9141
  return path.trim().replace(/^[.]\//, "").replace(/[?#].*$/, "");
@@ -10823,11 +9144,11 @@ function containsNullByte(path) {
10823
9144
  return path.includes("\0");
10824
9145
  }
10825
9146
  function slugFileBase(path) {
10826
- const name = basename3(path, extname2(path)).replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
9147
+ const name = basename3(path, extname(path)).replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
10827
9148
  return name || "media";
10828
9149
  }
10829
9150
  function uniqueAssetPath(projectDir, assetPath, taken) {
10830
- const ext = extname2(assetPath);
9151
+ const ext = extname(assetPath);
10831
9152
  const withoutExt = assetPath.slice(0, -ext.length);
10832
9153
  let candidate = assetPath;
10833
9154
  for (let index = 2; taken.has(candidate) || existsSync10(join15(projectDir, candidate)); index++) {
@@ -10930,10 +9251,10 @@ function registerMediaRoutes(api, adapter, options = {}) {
10930
9251
  const outputAssetPath = requestedOutput ? uniqueAssetPath(project.dir, requestedOutput, taken) : defaultOutputPath(project.dir, inputAssetPath, taken);
10931
9252
  const outputPath = pinWithinProject(project.dir, outputAssetPath);
10932
9253
  if (!outputPath) return c.json({ error: "forbidden" }, 403);
10933
- if (inputIsVideo && !VIDEO_OUTPUT_EXTENSIONS.has(extname2(outputAssetPath).toLowerCase())) {
9254
+ if (inputIsVideo && !VIDEO_OUTPUT_EXTENSIONS.has(extname(outputAssetPath).toLowerCase())) {
10934
9255
  return c.json({ error: "video background removal output must be .webm or .mov" }, 400);
10935
9256
  }
10936
- if (inputIsImage && extname2(outputAssetPath).toLowerCase() !== ".png") {
9257
+ if (inputIsImage && extname(outputAssetPath).toLowerCase() !== ".png") {
10937
9258
  return c.json({ error: "image background removal output must be .png" }, 400);
10938
9259
  }
10939
9260
  let backgroundOutputAssetPath;
@@ -11005,7 +9326,7 @@ function registerMediaRoutes(api, adapter, options = {}) {
11005
9326
  });
11006
9327
  });
11007
9328
  }
11008
- function readGlobalAssets(home = homedir2()) {
9329
+ function readGlobalAssets(home = homedir()) {
11009
9330
  const manifestPath = join16(home, ".media", "manifest.jsonl");
11010
9331
  if (!existsSync11(manifestPath)) return [];
11011
9332
  const out = [];
@@ -11342,24 +9663,6 @@ function updateBackgroundRemovalProgress(state, event) {
11342
9663
 
11343
9664
  export {
11344
9665
  stampProjectHfIds,
11345
- fileContentVersion,
11346
- DELETED_VERSION,
11347
- settledFileTag,
11348
- identifyFileWrite,
11349
- consumeFileWriteReceipt,
11350
- affectsProjectSignature,
11351
- createProjectSignature,
11352
- HistoryIdError,
11353
- HistoryBusyError,
11354
- listProjectHistories,
11355
- pruneGoneProjectHistories,
11356
- START,
11357
- DEFAULT_HISTORY_ROOT,
11358
- MAX_WINDOW_IDLE_MS,
11359
- UNDO_MODES,
11360
- HistoryClosedError,
11361
- openProjectHistory,
11362
- historyCache,
11363
9666
  getElementScreenshotClip,
11364
9667
  clearElementScreenshotIsolation,
11365
9668
  injectMediaCodecMapIntoHtml,