hyperframes 0.8.169 → 0.8.170
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.
- package/dist/{add-7GLP4BPM.js → add-KDCZCEMQ.js} +11 -11
- package/dist/{animatedGifPrep-NEBEGVSO.js → animatedGifPrep-MVP5LCEM.js} +5 -5
- package/dist/api.js +11 -11
- package/dist/{asset-RORTT5HS.js → asset-6VH3OCEC.js} +2 -2
- package/dist/{assetCataloger-RLJ6MDGF.js → assetCataloger-RN5A2SVA.js} +5 -5
- package/dist/{audio-OKDGU7TY.js → audio-636CQSXK.js} +11 -11
- package/dist/{auth-QWYNCU5U.js → auth-AHLKM4M2.js} +1 -1
- package/dist/{autoUpdate-EDJJPWAU.js → autoUpdate-5UA6TJW7.js} +2 -2
- package/dist/{backgroundChecks-KQR6FGVU.js → backgroundChecks-DP242536.js} +3 -3
- package/dist/backgroundChecksWorker.js +3 -3
- package/dist/{batchRender-YKTTAIHH.js → batchRender-KWBVAUE3.js} +3 -3
- package/dist/{beats-I6AGLTLW.js → beats-HKY5L463.js} +4 -4
- package/dist/{benchmark-NBSV7PNB.js → benchmark-4YQDVIO5.js} +8 -8
- package/dist/{browser-FPEL5FGW.js → browser-7ZPWXEYW.js} +3 -3
- package/dist/{bundleWithLocalizedFonts-TYQXMNSU.js → bundleWithLocalizedFonts-42V34HJF.js} +2 -2
- package/dist/{capture-FXGOI23U.js → capture-572X5NDD.js} +8 -8
- package/dist/{capture-5BRJJMZB.js → capture-VXMUZSBI.js} +2 -2
- package/dist/{catalog-SG3ZIS45.js → catalog-PUUTHPP7.js} +11 -11
- package/dist/{catch-up-ETXUT6Q5.js → catch-up-K6CV44WQ.js} +4 -4
- package/dist/{check-KXNNIEUR.js → check-MBDLSWCY.js} +32 -32
- package/dist/{checkBrowser-546UMI3N.js → checkBrowser-33UWWDZG.js} +29 -29
- package/dist/{chunk-EELTMBCC.js → chunk-2E7NDKTN.js} +1 -1
- package/dist/{chunk-DCG22HKT.js → chunk-2UD6O6QG.js} +6 -6
- package/dist/{chunk-5SQHHBAR.js → chunk-44GV6ZMC.js} +1 -1
- package/dist/{chunk-FQJDV2RI.js → chunk-4B6PUT4R.js} +1 -1
- package/dist/{chunk-UCNQE52B.js → chunk-4RXJG3SY.js} +197 -26
- package/dist/chunk-6M3IPJS6.js +3803 -0
- package/dist/{chunk-TJSTGSAW.js → chunk-7FMUTGOH.js} +2 -77
- package/dist/{chunk-T4HL3SZH.js → chunk-AGHHZIZN.js} +1 -1
- package/dist/{chunk-CNP6AMFM.js → chunk-AUMJ3QSO.js} +2 -2
- package/dist/{chunk-A6K7M46D.js → chunk-AWSVUIWG.js} +1 -1
- package/dist/{chunk-5ETJPA5Z.js → chunk-BAW7UNLK.js} +9 -9
- package/dist/{chunk-XK5SZNYE.js → chunk-CFBGKAKQ.js} +9 -9
- package/dist/{chunk-VCXVZRR2.js → chunk-EHDKCMKX.js} +1 -1
- package/dist/{chunk-AP5AVVAB.js → chunk-EJYBYR4C.js} +1 -1
- package/dist/{chunk-SJPNVLYS.js → chunk-ERJHLFPD.js} +2 -2
- package/dist/{chunk-P24X6CGY.js → chunk-FAORS3UR.js} +28 -13
- package/dist/{chunk-EEP5Y5IE.js → chunk-FE2XTCTM.js} +18 -1
- package/dist/{chunk-FSX7GTBB.js → chunk-FSP2JNAP.js} +1 -1
- package/dist/{chunk-ZFJ5RI7A.js → chunk-G6GKIAEM.js} +3 -3
- package/dist/{chunk-TVDXMJ43.js → chunk-GBWSBTEE.js} +1 -1
- package/dist/{chunk-K5PU5ISL.js → chunk-GG3V5BHG.js} +1 -1
- package/dist/{chunk-KA4LSAD5.js → chunk-GU5FLCZV.js} +1 -1
- package/dist/{chunk-EFP25BUY.js → chunk-JAERN4RH.js} +1 -1
- package/dist/{chunk-YT6TFGIS.js → chunk-KUFEPCR3.js} +1 -1
- package/dist/{chunk-S6U7YLPJ.js → chunk-KUW4LWY6.js} +1 -1
- package/dist/{chunk-TEFHMDVR.js → chunk-LMMFO5UV.js} +2 -2
- package/dist/{chunk-PVTHDNDZ.js → chunk-LWB3KZ5E.js} +1 -1
- package/dist/{chunk-QD5VJOQK.js → chunk-MSFON5DJ.js} +3 -3
- package/dist/{chunk-XJQ6NN2L.js → chunk-MXMIVNUE.js} +19 -23
- package/dist/{chunk-CESKA6VO.js → chunk-NSJLCWLG.js} +1 -1
- package/dist/{chunk-MCPDZWZY.js → chunk-NYCJCQNY.js} +4 -4
- package/dist/{chunk-RUYYAL3T.js → chunk-OGVQJHNM.js} +2 -2
- package/dist/{chunk-J5Z56PU3.js → chunk-PCX3YF2U.js} +1 -1
- package/dist/{chunk-U7P2XTRY.js → chunk-PECICVTJ.js} +2 -2
- package/dist/{chunk-GVX2443P.js → chunk-PH3RS5X4.js} +2 -2
- package/dist/{chunk-YZRS3COO.js → chunk-PP64Q43K.js} +1 -1
- package/dist/{chunk-Q5K73VW3.js → chunk-PXA4S6OE.js} +2 -2
- package/dist/{chunk-5FWVFCQM.js → chunk-QO3JEPEW.js} +2 -2
- package/dist/{chunk-O3J7BHBU.js → chunk-QZUVPWC4.js} +1 -1
- package/dist/{chunk-2F4HJFGW.js → chunk-T56MM7RC.js} +2 -2
- package/dist/{chunk-RZKQ3G6B.js → chunk-T5LGOTQR.js} +1 -1
- package/dist/{chunk-WAVFNZB7.js → chunk-UDDJQEGS.js} +1 -1
- package/dist/{chunk-BNEKKUPG.js → chunk-UQPH3Y45.js} +2 -2
- package/dist/{chunk-XZNHVWDD.js → chunk-UUP4KOMD.js} +4 -4
- package/dist/{chunk-UISU4SYG.js → chunk-V2T7FFPB.js} +1 -1
- package/dist/{chunk-7FHFPHEM.js → chunk-VKFYI7AQ.js} +182 -77
- package/dist/{chunk-JPN7SVWC.js → chunk-WLNNWLIE.js} +7 -7
- package/dist/{chunk-7635SDI5.js → chunk-WZQSL7UX.js} +2 -2
- package/dist/{chunk-YLAREDAU.js → chunk-X3U6KEID.js} +1 -1
- package/dist/{chunk-M4N6IHCQ.js → chunk-XRQBHOBT.js} +7 -1
- package/dist/{chunk-NKG3LTPI.js → chunk-YFGJVADL.js} +1 -1
- package/dist/{chunk-QLHXC4E6.js → chunk-ZINH7YA2.js} +1 -1
- package/dist/{clean-ARH23EGW.js → clean-5AXXHD45.js} +12 -12
- package/dist/cli.js +53 -53
- package/dist/{cloud-GBY7MRZM.js → cloud-DQCCYKOB.js} +4 -4
- package/dist/{cloudrun-HBVA5UZB.js → cloudrun-CDFL5JBL.js} +9 -11
- package/dist/{compare-Z4YNESYN.js → compare-6RB5X3SO.js} +24 -24
- package/dist/{compiler-6WJ5SVTD.js → compiler-GU5GPO5A.js} +2 -2
- package/dist/{component-AR4MS4R7.js → component-7RRNNC5F.js} +3 -3
- package/dist/{compositions-ERZVZBYW.js → compositions-CANET3EJ.js} +9 -9
- package/dist/{delete-NWSW3BET.js → delete-KEZJPDWJ.js} +2 -2
- package/dist/{deterministicFonts-KXVO7QEL.js → deterministicFonts-RWGQPEZU.js} +2 -2
- package/dist/{dist-VKLSNKRZ.js → dist-IDZRZHGE.js} +2 -2
- package/dist/{doctor-P4QZQRCB.js → doctor-5RAEGA6G.js} +7 -7
- package/dist/{events-F2UVJQIC.js → events-4ZJSJ76H.js} +2 -2
- package/dist/{events-SV46AWMI.js → events-SHOWEHI5.js} +3 -3
- package/dist/{feedback-XS4IK6SW.js → feedback-4IIGUUYZ.js} +6 -6
- package/dist/{figma-DELISEUP.js → figma-BFDECPE5.js} +3 -3
- package/dist/fontLocalizeCli.js +9 -9
- package/dist/{get-DNGXCEFB.js → get-BO32YZUQ.js} +2 -2
- package/dist/{gpuPolicy-JG5PUOJP.js → gpuPolicy-QWJYZLVJ.js} +5 -5
- package/dist/{grade-compare-F46JWE4O.js → grade-compare-SW4SAG5R.js} +24 -24
- package/dist/{help-XVEBMX3U.js → help-7AMSMPUF.js} +1 -1
- package/dist/{history-NS4PKG4S.js → history-VXAQFJJV.js} +6 -6
- package/dist/hyperframe-runtime.js +58 -57
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +58 -57
- package/dist/hyperframes-player.global.js +1 -1
- package/dist/{info-DJ6VNESS.js → info-KN4TX6T5.js} +7 -7
- package/dist/{init-TSRTMVRN.js → init-7A2T7LDW.js} +20 -20
- package/dist/{inspect-VTZXXDJ4.js → inspect-CCCV6JFV.js} +25 -25
- package/dist/{installer-3ERHU4CD.js → installer-ZCCZUOPN.js} +4 -4
- package/dist/{keyframes-6DN7BOYJ.js → keyframes-K6XNZGK3.js} +6 -6
- package/dist/{lambda-RCOIEGL6.js → lambda-B2K237DX.js} +10 -12
- package/dist/{layout-FFGRLVK4.js → layout-HAPR5OV4.js} +25 -25
- package/dist/{lint-MBYIDFKS.js → lint-3COFAXAJ.js} +6 -6
- package/dist/{list-KEZDFW5R.js → list-QCNHO7DR.js} +2 -2
- package/dist/{login-7ZV374QL.js → login-BAFYZS6W.js} +4 -4
- package/dist/{media-treatment-LGWWRXAX.js → media-treatment-C53ZBW2B.js} +8 -8
- package/dist/{models-TC4K6UQP.js → models-INX67ETR.js} +8 -8
- package/dist/{motionShot-4E3RTV5O.js → motionShot-UOS26VLB.js} +12 -12
- package/dist/{normalize-audio-XCGB2IQP.js → normalize-audio-I7HGJGA5.js} +4 -4
- package/dist/{open-JNNJSNTH.js → open-RFXGV4RY.js} +4 -4
- package/dist/{parakeetRunner-S3DG4BJB.js → parakeetRunner-PCCBODBS.js} +8 -8
- package/dist/{pipeline-3SIYCBLU.js → pipeline-GDC7SL5N.js} +5 -5
- package/dist/{play-LBPOOD3V.js → play-DRCWK35L.js} +17 -17
- package/dist/{present-PWEN5SDL.js → present-HUJV553D.js} +13 -13
- package/dist/{preview-IS32RLKM.js → preview-CZENYIGN.js} +15 -15
- package/dist/{producer-4UUUEPPS.js → producer-23NJEJRO.js} +1 -1
- package/dist/{projectConfig-K347VIO3.js → projectConfig-R3FCIARW.js} +6 -6
- package/dist/{publication-4CQS532O.js → publication-GY3OJ6VH.js} +2 -2
- package/dist/{publish-OJYF37FY.js → publish-XBW4NVFY.js} +7 -7
- package/dist/{remove-background-HO6YL2HU.js → remove-background-Q7CQ3PJF.js} +6 -6
- package/dist/{render-L76SEAMC.js → render-24JWWXMA.js} +3 -3
- package/dist/{render-Q6QAXJCN.js → render-DOHHCT2G.js} +8 -8
- package/dist/{render-QBQYF2NC.js → render-WWAXTKYR.js} +22 -22
- package/dist/{render-batch-JLANN5Y2.js → render-batch-EK4ZBQHH.js} +3 -3
- package/dist/renderSetupWorker.js +9 -1
- package/dist/{resolver-EDUM4DHG.js → resolver-5Y6VNMTC.js} +4 -4
- package/dist/{sherpa-QEM5XYHD.js → sherpa-VMADZBF7.js} +7 -7
- package/dist/skills/media-use/SKILL.md +3 -3
- package/dist/skills/media-use/audio/references/requirements.md +7 -7
- package/dist/skills/media-use/audio/references/sfx.md +19 -13
- package/dist/skills/media-use/audio/scripts/audio.mjs +9 -9
- package/dist/skills/media-use/audio/scripts/audio.test.mjs +17 -10
- package/dist/skills/media-use/audio/scripts/lib/media-record.mjs +7 -3
- package/dist/skills/media-use/audio/scripts/lib/media-record.test.mjs +22 -3
- package/dist/skills/media-use/audio/scripts/lib/openverse.mjs +134 -0
- package/dist/skills/media-use/audio/scripts/lib/sfx.mjs +120 -89
- package/dist/skills/media-use/audio/scripts/lib/sfx.test.mjs +222 -12
- package/dist/skills/media-use/references/audio.md +1 -1
- package/dist/skills/media-use/references/resolve.md +1 -1
- package/dist/skills/media-use/references/setup-providers.md +13 -12
- package/dist/skills/media-use/scripts/lib/heygen-cli.mjs +4 -7
- package/dist/skills/media-use/scripts/lib/heygen-cli.test.mjs +5 -5
- package/dist/skills/media-use/scripts/lib/manifest.d.mts +1 -0
- package/dist/skills/media-use/scripts/lib/manifest.mjs +9 -6
- package/dist/skills/media-use/scripts/lib/openverse.mjs +134 -0
- package/dist/skills/media-use/scripts/lib/openverse.test.mjs +219 -0
- package/dist/skills/media-use/scripts/lib/registry.mjs +4 -2
- package/dist/skills/media-use/scripts/lib/registry.test.mjs +13 -13
- package/dist/skills/media-use/scripts/lib/sfx-provider.mjs +23 -13
- package/dist/skills/media-use/scripts/resolve.mjs +45 -55
- package/dist/{skills-FSZK6FIG.js → skills-ONFYYL6R.js} +5 -5
- package/dist/{skillsUpdateCheck-3354HXDJ.js → skillsUpdateCheck-JTHZOHRT.js} +3 -3
- package/dist/{snapshot-5VVMFEMM.js → snapshot-SUA62VQ2.js} +27 -27
- package/dist/{src-RYILCNU7.js → src-J6KABXGZ.js} +8 -8
- package/dist/{src-ZGMLMQ3B.js → src-VFTQPSQD.js} +4 -4
- package/dist/{staticProjectServer-PIVSGEAO.js → staticProjectServer-XQPUABYX.js} +13 -13
- package/dist/studio/assets/{hyperframes-player-B04Yx96S.js → hyperframes-player-Dsmhm7Q8.js} +1 -1
- package/dist/studio/assets/{index-D2e0J6kh.js → index-CV00F8qG.js} +4 -4
- package/dist/studio/assets/{index-D9CDX_Rr.js → index-DT1wigvW.js} +1 -1
- package/dist/studio/index.html +1 -1
- package/dist/{studio-api-PHADR6VB.js → studio-api-MBDDFEA2.js} +7 -7
- package/dist/{studioServer-5AVDKN5V.js → studioServer-7HWDCLKT.js} +37 -37
- package/dist/{telemetry-JCTHMPSB.js → telemetry-763NW3QF.js} +3 -3
- package/dist/{timeline-QU2F6R2M.js → timeline-ZLFPNI27.js} +15 -15
- package/dist/{tokens-YYK4G36C.js → tokens-RXJLVZKD.js} +2 -2
- package/dist/{transcribe-7ORH7R6O.js → transcribe-IM5UNBSH.js} +6 -6
- package/dist/{transcribe-PRG4FLLA.js → transcribe-UDISJXCZ.js} +12 -12
- package/dist/{updateCheck-NV7QNANK.js → updateCheck-V2RXHFQL.js} +2 -2
- package/dist/{upgrade-UZXN36J2.js → upgrade-MJI3EW3W.js} +2 -2
- package/dist/{urlDownloader-AW57H722.js → urlDownloader-BOLOMGC7.js} +4 -4
- package/dist/{validate-QD53ABUM.js → validate-IM5NPJHI.js} +19 -19
- package/dist/{video-VLVPYQQR.js → video-HUNOZDLT.js} +5 -5
- package/package.json +1 -1
- package/dist/chunk-BIV5HVTQ.js +0 -3803
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { strict as assert } from "node:assert";
|
|
2
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { test } from "node:test";
|
|
6
|
+
import {
|
|
7
|
+
OpenverseUnavailableError,
|
|
8
|
+
pickOpenverseSfx,
|
|
9
|
+
searchOpenverseSfx,
|
|
10
|
+
writeSfxCredit,
|
|
11
|
+
} from "./openverse.mjs";
|
|
12
|
+
|
|
13
|
+
const result = (overrides = {}) => ({
|
|
14
|
+
id: "ov-1",
|
|
15
|
+
title: "Deep Whoosh",
|
|
16
|
+
creator: "Kinoton",
|
|
17
|
+
license: "cc0",
|
|
18
|
+
license_url: "https://creativecommons.org/publicdomain/zero/1.0/",
|
|
19
|
+
foreign_landing_url: "https://freesound.org/people/Kinoton/sounds/351256",
|
|
20
|
+
url: "https://cdn.freesound.org/previews/351/351256_2247456-hq.mp3",
|
|
21
|
+
filetype: "mp3",
|
|
22
|
+
duration: 1200,
|
|
23
|
+
attribution: '"Deep Whoosh" by Kinoton is marked with CC0 1.0.',
|
|
24
|
+
...overrides,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("only CC0 and CC BY sounds are ever picked", () => {
|
|
28
|
+
for (const license of ["by-nc", "by-sa", "by-nd", "by-nc-sa", "by-nc-nd", "pdm", "sampling+"])
|
|
29
|
+
assert.equal(pickOpenverseSfx([result({ license })]), null, license);
|
|
30
|
+
assert.equal(pickOpenverseSfx([result({ license: "by" })]).license, "by");
|
|
31
|
+
assert.equal(pickOpenverseSfx([result({ license: "cc0" })]).license, "cc0");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("sounds too long for a cue, or not playable, are skipped", () => {
|
|
35
|
+
assert.equal(pickOpenverseSfx([result({ duration: 15001 })]), null);
|
|
36
|
+
assert.equal(pickOpenverseSfx([result({ duration: null })]), null);
|
|
37
|
+
assert.equal(pickOpenverseSfx([result({ filetype: "aiff" })]), null);
|
|
38
|
+
assert.equal(pickOpenverseSfx([result({ url: "http://insecure.test/a.mp3" })]), null);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("ranking keeps relevance order, then prefers short clips and CC0 over CC BY", () => {
|
|
42
|
+
const pick = (results) => pickOpenverseSfx(results).id;
|
|
43
|
+
assert.equal(pick([result({ id: "first" }), result({ id: "second" })]), "first");
|
|
44
|
+
assert.equal(pick([result({ id: "long", duration: 12000 }), result({ id: "short" })]), "short");
|
|
45
|
+
assert.equal(pick([result({ id: "by", license: "by" }), result({ id: "cc0" })]), "cc0");
|
|
46
|
+
assert.equal(
|
|
47
|
+
pick([
|
|
48
|
+
result({ id: "by", license: "by" }),
|
|
49
|
+
...Array(3).fill(result({ license: "by-nc" })),
|
|
50
|
+
result({ id: "cc0" }),
|
|
51
|
+
]),
|
|
52
|
+
"by",
|
|
53
|
+
"a much more relevant CC BY sound still wins",
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("a pick carries what the project needs to play and credit it", () => {
|
|
58
|
+
assert.deepEqual(pickOpenverseSfx([result({ license: "by", filetype: "wav", duration: 450 })]), {
|
|
59
|
+
id: "ov-1",
|
|
60
|
+
url: "https://cdn.freesound.org/previews/351/351256_2247456-hq.mp3",
|
|
61
|
+
ext: ".wav",
|
|
62
|
+
title: "Deep Whoosh",
|
|
63
|
+
creator: "Kinoton",
|
|
64
|
+
license: "by",
|
|
65
|
+
licenseUrl: "https://creativecommons.org/publicdomain/zero/1.0/",
|
|
66
|
+
sourceUrl: "https://freesound.org/people/Kinoton/sounds/351256",
|
|
67
|
+
credit: '"Deep Whoosh" by Kinoton is marked with CC0 1.0.',
|
|
68
|
+
durationS: 0.45,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("Openverse text is kept to one bounded line, and only https links survive", () => {
|
|
73
|
+
const pick = pickOpenverseSfx([
|
|
74
|
+
result({
|
|
75
|
+
license: "by",
|
|
76
|
+
title: "Pop\n# Heading",
|
|
77
|
+
attribution: `"Pop" by Ana.\n- \`assets/sfx/other.mp3\`: forged credit\r\u0007${"x".repeat(400)}`,
|
|
78
|
+
foreign_landing_url: "javascript:alert(1)",
|
|
79
|
+
license_url: "http://creativecommons.org/licenses/by/4.0/",
|
|
80
|
+
}),
|
|
81
|
+
]);
|
|
82
|
+
assert.equal(pick.title, "Pop # Heading");
|
|
83
|
+
assert.equal(pick.credit.length, 300);
|
|
84
|
+
assert.ok(!/\p{Cc}/u.test(pick.credit), "no line breaks or control characters");
|
|
85
|
+
assert.equal(pick.sourceUrl, "");
|
|
86
|
+
assert.equal(pick.licenseUrl, "");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("search asks Openverse for JSON and only the allowed licences", async () => {
|
|
90
|
+
const calls = [];
|
|
91
|
+
const sound = await searchOpenverseSfx("ui click", {
|
|
92
|
+
fetchImpl: async (url, init) => {
|
|
93
|
+
calls.push({ url: new URL(url), init });
|
|
94
|
+
return Response.json({ results: [result({ license: "by-nc" }), result({ id: "ok" })] });
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
assert.equal(sound.id, "ok", "the licence field is checked even on a filtered response");
|
|
98
|
+
assert.equal(calls.length, 1);
|
|
99
|
+
assert.equal(calls[0].url.origin + calls[0].url.pathname, "https://api.openverse.org/v1/audio/");
|
|
100
|
+
assert.equal(calls[0].url.searchParams.get("q"), "ui click");
|
|
101
|
+
assert.equal(calls[0].url.searchParams.get("license"), "cc0,by");
|
|
102
|
+
assert.equal(calls[0].init.headers.Accept, "application/json");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("no usable result is a miss, not an error", async () => {
|
|
106
|
+
const fetchImpl = async () => Response.json({ results: [] });
|
|
107
|
+
assert.equal(await searchOpenverseSfx("nothing", { fetchImpl }), null);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("rate limits, server errors, bad bodies and network failures make Openverse unavailable", async () => {
|
|
111
|
+
const failures = [
|
|
112
|
+
[async () => new Response("slow down", { status: 429 }), /rate limit/],
|
|
113
|
+
[async () => new Response("down", { status: 503 }), /HTTP 503/],
|
|
114
|
+
[async () => new Response("<html>"), /non-JSON/],
|
|
115
|
+
[
|
|
116
|
+
async () => {
|
|
117
|
+
throw new TypeError("fetch failed");
|
|
118
|
+
},
|
|
119
|
+
/unreachable \(fetch failed\)/,
|
|
120
|
+
],
|
|
121
|
+
];
|
|
122
|
+
for (const [fetchImpl, message] of failures)
|
|
123
|
+
await assert.rejects(searchOpenverseSfx("whoosh", { fetchImpl }), (error) => {
|
|
124
|
+
assert.ok(error instanceof OpenverseUnavailableError);
|
|
125
|
+
assert.match(error.message, message);
|
|
126
|
+
return true;
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("credits list each CC BY file once and leave CC0 out", () => {
|
|
131
|
+
const dir = mkdtempSync(join(tmpdir(), "openverse-credits-"));
|
|
132
|
+
try {
|
|
133
|
+
const path = join(dir, "assets", "sfx", "CREDITS.md");
|
|
134
|
+
const by = {
|
|
135
|
+
license: "by",
|
|
136
|
+
credit: '"Pop" by Ana is licensed under CC BY 4.0.',
|
|
137
|
+
sourceUrl: "https://freesound.org/s/1",
|
|
138
|
+
};
|
|
139
|
+
writeSfxCredit(path, "assets/sfx/zap.mp3", { ...by, license: "cc0" });
|
|
140
|
+
assert.throws(() => readFileSync(path), /ENOENT/);
|
|
141
|
+
writeSfxCredit(path, "assets/sfx/pop.mp3", by);
|
|
142
|
+
writeSfxCredit(path, "assets/sfx/pop.mp3", {
|
|
143
|
+
...by,
|
|
144
|
+
credit: '"Pop 2" by Ana is licensed under CC BY 4.0.',
|
|
145
|
+
});
|
|
146
|
+
writeSfxCredit(path, "assets/sfx/ding.mp3", by);
|
|
147
|
+
const lines = readFileSync(path, "utf8")
|
|
148
|
+
.split("\n")
|
|
149
|
+
.filter((l) => l.startsWith("- "));
|
|
150
|
+
assert.deepEqual(lines, [
|
|
151
|
+
'- `assets/sfx/pop.mp3`: "Pop 2" by Ana is licensed under CC BY 4.0. Source: https://freesound.org/s/1',
|
|
152
|
+
'- `assets/sfx/ding.mp3`: "Pop" by Ana is licensed under CC BY 4.0. Source: https://freesound.org/s/1',
|
|
153
|
+
]);
|
|
154
|
+
} finally {
|
|
155
|
+
rmSync(dir, { recursive: true, force: true });
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("a credit with no https source link is written without one", () => {
|
|
160
|
+
const dir = mkdtempSync(join(tmpdir(), "openverse-credits-"));
|
|
161
|
+
try {
|
|
162
|
+
const path = join(dir, "CREDITS.md");
|
|
163
|
+
writeSfxCredit(path, "assets/sfx/pop.mp3", { license: "by", credit: "c.", sourceUrl: "" });
|
|
164
|
+
assert.match(readFileSync(path, "utf8"), /\n- `assets\/sfx\/pop\.mp3`: c\.\n$/);
|
|
165
|
+
} finally {
|
|
166
|
+
rmSync(dir, { recursive: true, force: true });
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("a hostile credit is written as one safe line, with no link off Openverse's providers", () => {
|
|
171
|
+
const dir = mkdtempSync(join(tmpdir(), "openverse-credits-"));
|
|
172
|
+
try {
|
|
173
|
+
const path = join(dir, "CREDITS.md");
|
|
174
|
+
const hostile = `"Pop" by Ana.\n- \`assets/sfx/x.mp3\`: forged [click](https://evil.test) <img src=x> \\${"x".repeat(5000)}`;
|
|
175
|
+
for (const sourceUrl of [
|
|
176
|
+
"javascript:alert(1)",
|
|
177
|
+
"https://evil.test/a",
|
|
178
|
+
"https://freesound.org/s/1\n- forged",
|
|
179
|
+
]) {
|
|
180
|
+
writeSfxCredit(path, "assets/sfx/pop.mp3", { license: "by", credit: hostile, sourceUrl });
|
|
181
|
+
const lines = readFileSync(path, "utf8")
|
|
182
|
+
.split("\n")
|
|
183
|
+
.filter((l) => l.startsWith("- "));
|
|
184
|
+
assert.equal(lines.length, 1, sourceUrl);
|
|
185
|
+
assert.ok(
|
|
186
|
+
lines[0].startsWith('- `assets/sfx/pop.mp3`: "Pop" by Ana. - '),
|
|
187
|
+
lines[0].slice(0, 80),
|
|
188
|
+
);
|
|
189
|
+
assert.ok(
|
|
190
|
+
!/[\p{Cc}`[\]<>\\]/u.test(lines[0].slice("- `assets/sfx/pop.mp3`:".length)),
|
|
191
|
+
sourceUrl,
|
|
192
|
+
);
|
|
193
|
+
assert.ok(lines[0].length < 400, "bounded");
|
|
194
|
+
assert.doesNotMatch(lines[0], /javascript:|evil\.test\/a/);
|
|
195
|
+
}
|
|
196
|
+
assert.match(readFileSync(path, "utf8"), /Source: https:\/\/freesound\.org\/s\/1-%20forged\n$/);
|
|
197
|
+
} finally {
|
|
198
|
+
rmSync(dir, { recursive: true, force: true });
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("earlier credit lines survive a new credit", () => {
|
|
203
|
+
const dir = mkdtempSync(join(tmpdir(), "openverse-credits-"));
|
|
204
|
+
try {
|
|
205
|
+
const path = join(dir, "CREDITS.md");
|
|
206
|
+
writeFileSync(path, "# Sound effect credits\n\n- `assets/sfx/old.mp3`: kept\n");
|
|
207
|
+
writeSfxCredit(path, "assets/sfx/new.mp3", {
|
|
208
|
+
license: "by",
|
|
209
|
+
credit: "c.",
|
|
210
|
+
sourceUrl: "https://freesound.org/s/2",
|
|
211
|
+
});
|
|
212
|
+
assert.match(
|
|
213
|
+
readFileSync(path, "utf8"),
|
|
214
|
+
/- `assets\/sfx\/old\.mp3`: kept\n- `assets\/sfx\/new\.mp3`: c\. Source: https:\/\/freesound\.org\/s\/2\n$/,
|
|
215
|
+
);
|
|
216
|
+
} finally {
|
|
217
|
+
rmSync(dir, { recursive: true, force: true });
|
|
218
|
+
}
|
|
219
|
+
});
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
// own auth. Providers, by type:
|
|
11
11
|
// - heygen CLI: catalog + TTS, first for every type it serves (OAuth free
|
|
12
12
|
// allowance first, then the user's HeyGen billing path)
|
|
13
|
+
// - Openverse: free, keyless sound effects (CC0 / CC BY), with the bundled
|
|
14
|
+
// library as the offline fallback
|
|
13
15
|
// - mflux: local FLUX-class image gen, spec-selected to the machine's RAM
|
|
14
16
|
// (free, private, offline once cached)
|
|
15
17
|
// - codex CLI: image gen on the user's ChatGPT sub — the better-quality upsell
|
|
@@ -21,7 +23,7 @@
|
|
|
21
23
|
// (e.g. "make an image with codex").
|
|
22
24
|
|
|
23
25
|
import { bgmProvider } from "./bgm-provider.mjs";
|
|
24
|
-
import { sfxProvider } from "./sfx-provider.mjs";
|
|
26
|
+
import { OPENVERSE_SFX_PROVIDER, sfxProvider } from "./sfx-provider.mjs";
|
|
25
27
|
import { bundledSfxProvider } from "./bundled-sfx-provider.mjs";
|
|
26
28
|
import { imageProvider, iconProvider } from "./image-provider.mjs";
|
|
27
29
|
import { brandProvider } from "./brand-provider.mjs";
|
|
@@ -46,8 +48,8 @@ const P = (name, caps) => ({ name, network: true, paid: true, ...caps }); // rem
|
|
|
46
48
|
const REGISTRY = {
|
|
47
49
|
bgm: [N("heygen.audio.sounds", { search: bgmProvider.search })],
|
|
48
50
|
sfx: [
|
|
51
|
+
N(OPENVERSE_SFX_PROVIDER, { search: sfxProvider.search }),
|
|
49
52
|
A("bundled.sfx", { search: bundledSfxProvider.search }),
|
|
50
|
-
N("heygen.audio.sounds", { search: sfxProvider.search }),
|
|
51
53
|
],
|
|
52
54
|
image: [
|
|
53
55
|
N("heygen.asset.search", { search: imageProvider.search }),
|
|
@@ -32,18 +32,18 @@ test("listTypes exposes the v2 media types", () => {
|
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
test("heygen provider is first for every type it serves
|
|
35
|
+
test("heygen provider is first for every type it serves", () => {
|
|
36
36
|
for (const t of ["bgm", "image", "icon"]) {
|
|
37
37
|
const first = getProviders(t)[0];
|
|
38
38
|
assert.ok(first, `no enabled provider for ${t}`);
|
|
39
39
|
assert.match(first.name, /^heygen/, `${t} first provider is ${first.name}`);
|
|
40
40
|
}
|
|
41
|
-
assert.
|
|
41
|
+
assert.ok(!getProviders("sfx").some((p) => p.name.startsWith("heygen")), "sfx never asks HeyGen");
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
test("sanctioned providers only: heygen, local mflux/kokoro/ltx, codex, design spec, logo tiers", () => {
|
|
44
|
+
test("sanctioned providers only: heygen, Openverse, local mflux/kokoro/ltx, codex, design spec, logo tiers", () => {
|
|
45
45
|
const allowed =
|
|
46
|
-
/^heygen|^bundled\.sfx$|^mflux\.local$|^kokoro\.local$|^ltx\.local$|^codex\.image_gen$|^design_spec$|^thesvg$|^github\.avatar$|^favicon\.ddg$|^color_grade\.local$|^cube_lut\.local$/;
|
|
46
|
+
/^heygen|^openverse\.audio$|^bundled\.sfx$|^mflux\.local$|^kokoro\.local$|^ltx\.local$|^codex\.image_gen$|^design_spec$|^thesvg$|^github\.avatar$|^favicon\.ddg$|^color_grade\.local$|^cube_lut\.local$/;
|
|
47
47
|
for (const t of listTypes()) {
|
|
48
48
|
for (const p of getProviders(t)) {
|
|
49
49
|
assert.ok(allowed.test(p.name), `${t} lists unsanctioned provider: ${p.name}`);
|
|
@@ -85,13 +85,14 @@ test("video cascade: HeyGen first, LTX local fallback, generate-only", async ()
|
|
|
85
85
|
assert.equal(await runCapability("video", "search", "x", {}), null);
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
test("sfx cascade:
|
|
88
|
+
test("sfx cascade: Openverse first, the bundled library is the offline fallback", () => {
|
|
89
89
|
const ps = getProviders("sfx");
|
|
90
|
-
assert.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
assert.ok(ps[
|
|
90
|
+
assert.deepEqual(
|
|
91
|
+
ps.map((p) => p.name),
|
|
92
|
+
["openverse.audio", "bundled.sfx"],
|
|
93
|
+
);
|
|
94
|
+
assert.ok(ps[0].network && !ps[0].paid, "Openverse is free and network-only");
|
|
95
|
+
assert.ok(!ps[1].network, "bundled SFX remain available offline");
|
|
95
96
|
});
|
|
96
97
|
|
|
97
98
|
test("ctx.provider forces one generator (e.g. 'make an image WITH codex')", async () => {
|
|
@@ -201,9 +202,8 @@ test("providerTierFor reports the registry's own A/N/P declaration", () => {
|
|
|
201
202
|
});
|
|
202
203
|
|
|
203
204
|
test("providerTierFor agrees across every type that declares the same name", () => {
|
|
204
|
-
// heygen.
|
|
205
|
-
//
|
|
206
|
-
// telemetry property meaningless.
|
|
205
|
+
// heygen.asset.search serves both image and icon. A name whose tier depended
|
|
206
|
+
// on the media type would make the telemetry property meaningless.
|
|
207
207
|
const byName = new Map();
|
|
208
208
|
for (const type of listTypes()) {
|
|
209
209
|
for (const name of providerNamesFor(type)) {
|
|
@@ -1,22 +1,32 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { OpenverseUnavailableError, searchOpenverseSfx } from "./openverse.mjs";
|
|
2
|
+
|
|
3
|
+
export const OPENVERSE_SFX_PROVIDER = "openverse.audio";
|
|
2
4
|
|
|
3
5
|
export const sfxProvider = {
|
|
4
6
|
async search(intent) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
let sound;
|
|
8
|
+
try {
|
|
9
|
+
sound = await searchOpenverseSfx(intent);
|
|
10
|
+
} catch (error) {
|
|
11
|
+
if (!(error instanceof OpenverseUnavailableError)) throw error;
|
|
12
|
+
console.error(`media-use: ${error.message}; using the bundled sound effects`);
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
if (!sound) return null;
|
|
11
16
|
return {
|
|
12
|
-
url:
|
|
17
|
+
url: sound.url,
|
|
18
|
+
ext: sound.ext,
|
|
13
19
|
source: "search",
|
|
14
|
-
// ext derived from audio_url by resolve.mjs — catalog SFX are .mp3 or .wav
|
|
15
20
|
metadata: {
|
|
16
|
-
description:
|
|
17
|
-
duration:
|
|
18
|
-
provider:
|
|
19
|
-
provenance: {
|
|
21
|
+
description: sound.title || intent,
|
|
22
|
+
duration: sound.durationS,
|
|
23
|
+
provider: OPENVERSE_SFX_PROVIDER,
|
|
24
|
+
provenance: {
|
|
25
|
+
openverse_id: sound.id,
|
|
26
|
+
license: sound.license,
|
|
27
|
+
credit: sound.credit,
|
|
28
|
+
source_url: sound.sourceUrl,
|
|
29
|
+
},
|
|
20
30
|
},
|
|
21
31
|
};
|
|
22
32
|
},
|
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
findByPrompt,
|
|
13
13
|
findByEntity,
|
|
14
14
|
nextId,
|
|
15
|
+
typeDirPath,
|
|
16
|
+
withMediaLock,
|
|
15
17
|
withReservedFile,
|
|
16
18
|
withReservedFileSync,
|
|
17
19
|
} from "./lib/manifest.mjs";
|
|
@@ -55,6 +57,8 @@ import {
|
|
|
55
57
|
versionLessThan,
|
|
56
58
|
} from "./lib/heygen-cli.mjs";
|
|
57
59
|
import { BundledSfxAssetsError, inspectBundledSfxAssets } from "./lib/bundled-sfx-provider.mjs";
|
|
60
|
+
import { OPENVERSE_SFX_PROVIDER } from "./lib/sfx-provider.mjs";
|
|
61
|
+
import { writeSfxCredit } from "./lib/openverse.mjs";
|
|
58
62
|
import {
|
|
59
63
|
fetchMediaVectors,
|
|
60
64
|
mediaVectorRows,
|
|
@@ -441,57 +445,43 @@ async function run() {
|
|
|
441
445
|
return resolveColor(type, intent, { projectDir });
|
|
442
446
|
}
|
|
443
447
|
|
|
444
|
-
// SFX search is
|
|
448
|
+
// SFX search is Openverse, the bundled library, then the local media index.
|
|
445
449
|
let searchResult = null;
|
|
446
450
|
let providerFailure = null;
|
|
447
451
|
let localIndexFailure = null;
|
|
448
452
|
try {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
provider: "catalog.local",
|
|
478
|
-
provenance: { library_key: row.id, tier: ranked.tier },
|
|
479
|
-
},
|
|
480
|
-
};
|
|
481
|
-
}
|
|
482
|
-
} catch (error) {
|
|
483
|
-
localIndexFailure = error;
|
|
453
|
+
searchResult = await runCapability(type, "search", intent, ctx);
|
|
454
|
+
if (!searchResult && type === "sfx" && !args.provider && !localOnly) {
|
|
455
|
+
const registry =
|
|
456
|
+
process.env.HYPERFRAMES_REGISTRY ||
|
|
457
|
+
"https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry";
|
|
458
|
+
try {
|
|
459
|
+
await fetchMediaVectors(registry);
|
|
460
|
+
const ranked = await rankMediaRowsWithVectors(
|
|
461
|
+
intent,
|
|
462
|
+
mediaVectorRows().filter((row) => row.kind === "sfx"),
|
|
463
|
+
);
|
|
464
|
+
const row = ranked.rows[0];
|
|
465
|
+
const candidates = row
|
|
466
|
+
? [resolve(row.file), join(import.meta.dirname, "..", "..", "..", row.file)]
|
|
467
|
+
: [];
|
|
468
|
+
const localPath = candidates.find((candidate) => existsSync(candidate));
|
|
469
|
+
if (row && localPath) {
|
|
470
|
+
searchResult = {
|
|
471
|
+
localPath,
|
|
472
|
+
ext: extname(localPath),
|
|
473
|
+
source: "local-index",
|
|
474
|
+
metadata: {
|
|
475
|
+
description: row.description,
|
|
476
|
+
duration: row.duration ?? null,
|
|
477
|
+
provider: "catalog.local",
|
|
478
|
+
provenance: { library_key: row.id, tier: ranked.tier },
|
|
479
|
+
},
|
|
480
|
+
};
|
|
484
481
|
}
|
|
482
|
+
} catch (error) {
|
|
483
|
+
localIndexFailure = error;
|
|
485
484
|
}
|
|
486
|
-
if (!searchResult) searchResult = await runCapability(type, "search", intent, ctx);
|
|
487
|
-
// Keep HeyGen remediation diagnostics while bundled remains authoritative.
|
|
488
|
-
else if (!localOnly)
|
|
489
|
-
await runCapability(type, "search", intent, {
|
|
490
|
-
...ctx,
|
|
491
|
-
provider: "heygen.audio.sounds",
|
|
492
|
-
});
|
|
493
|
-
} else {
|
|
494
|
-
searchResult = await runCapability(type, "search", intent, ctx);
|
|
495
485
|
}
|
|
496
486
|
} catch (error) {
|
|
497
487
|
providerFailure = error;
|
|
@@ -602,16 +592,6 @@ async function run() {
|
|
|
602
592
|
},
|
|
603
593
|
};
|
|
604
594
|
|
|
605
|
-
const heygenRemediation = consumeHeygenRemediation();
|
|
606
|
-
if (
|
|
607
|
-
searchResult.metadata?.provider === "bundled.sfx" &&
|
|
608
|
-
!localOnly &&
|
|
609
|
-
!args.provider &&
|
|
610
|
-
heygenRemediation
|
|
611
|
-
) {
|
|
612
|
-
record.advisory = heygenRemediation;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
595
|
appendRecord(projectDir, record);
|
|
616
596
|
regenerateIndex(projectDir);
|
|
617
597
|
// Promote fetched assets into the global cache for reuse.
|
|
@@ -1304,6 +1284,16 @@ async function reuseGlobal(shaArg) {
|
|
|
1304
1284
|
}
|
|
1305
1285
|
|
|
1306
1286
|
async function result(record, source) {
|
|
1287
|
+
const { provider, license, credit, source_url: sourceUrl } = record.provenance ?? {};
|
|
1288
|
+
// Under the media lock: two resolves at once each read, then rewrite, the whole credits file.
|
|
1289
|
+
if (provider === OPENVERSE_SFX_PROVIDER)
|
|
1290
|
+
withMediaLock(projectDir, () =>
|
|
1291
|
+
writeSfxCredit(join(typeDirPath(projectDir, "sfx"), "CREDITS.md"), record.path, {
|
|
1292
|
+
license,
|
|
1293
|
+
credit,
|
|
1294
|
+
sourceUrl,
|
|
1295
|
+
}),
|
|
1296
|
+
);
|
|
1307
1297
|
// Non-PII usage event: which media type, how it resolved, which provider won.
|
|
1308
1298
|
// Never the intent text or paths. Awaited so a short-lived run flushes it.
|
|
1309
1299
|
await track("media_use_resolve", {
|
|
@@ -6,8 +6,8 @@ var __filename = __hf_fileURLToPath(import.meta.url);
|
|
|
6
6
|
var __dirname = __hf_dirname(__filename);
|
|
7
7
|
import {
|
|
8
8
|
trackSkillsInstallSkipped
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-4B6PUT4R.js";
|
|
10
|
+
import "./chunk-FSP2JNAP.js";
|
|
11
11
|
import {
|
|
12
12
|
diag
|
|
13
13
|
} from "./chunk-VWG7OQKU.js";
|
|
@@ -35,15 +35,15 @@ import {
|
|
|
35
35
|
isCoreSkill,
|
|
36
36
|
presentSkills,
|
|
37
37
|
pruneOrphanedLockEntries
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-EHDKCMKX.js";
|
|
39
39
|
import {
|
|
40
40
|
withMeta
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-GG3V5BHG.js";
|
|
42
42
|
import "./chunk-K4WIOZFP.js";
|
|
43
43
|
import "./chunk-XUXGIA7H.js";
|
|
44
44
|
import "./chunk-WERLJGIC.js";
|
|
45
45
|
import "./chunk-ISN5GRYI.js";
|
|
46
|
-
import "./chunk-
|
|
46
|
+
import "./chunk-UQPH3Y45.js";
|
|
47
47
|
import {
|
|
48
48
|
buildNpxCommand
|
|
49
49
|
} from "./chunk-6BEFEWFB.js";
|
|
@@ -9,13 +9,13 @@ import {
|
|
|
9
9
|
printSkillsUpdateNotice,
|
|
10
10
|
refreshSkillsCache,
|
|
11
11
|
skillsCheckDue
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-EHDKCMKX.js";
|
|
13
|
+
import "./chunk-GG3V5BHG.js";
|
|
14
14
|
import "./chunk-K4WIOZFP.js";
|
|
15
15
|
import "./chunk-XUXGIA7H.js";
|
|
16
16
|
import "./chunk-WERLJGIC.js";
|
|
17
17
|
import "./chunk-ISN5GRYI.js";
|
|
18
|
-
import "./chunk-
|
|
18
|
+
import "./chunk-UQPH3Y45.js";
|
|
19
19
|
import "./chunk-KNU5COPB.js";
|
|
20
20
|
import "./chunk-M2WBOQ2Q.js";
|
|
21
21
|
export {
|