@utilix-tech/sdk 0.23.0 → 0.25.0
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/CHANGELOG.md +8 -0
- package/README.md +31 -8
- package/dist/{chunk-TINQFTLO.js → chunk-66QHV5KW.js} +178 -1
- package/dist/{chunk-2I4O5YCT.js → chunk-AFEZ6N4A.js} +43 -1
- package/dist/{chunk-AIKSKJ77.js → chunk-FVKNR3PV.js} +126 -1
- package/dist/{data-D8XTI7Du.d.ts → data-XRb0D7Zn.d.cts} +75 -2
- package/dist/{data-D8XTI7Du.d.cts → data-XRb0D7Zn.d.ts} +75 -2
- package/dist/index.cjs +344 -0
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/{text-DS70aMY5.d.cts → text-D1fySkCB.d.cts} +30 -2
- package/dist/{text-DS70aMY5.d.ts → text-D1fySkCB.d.ts} +30 -2
- package/dist/tools/data.cjs +177 -0
- package/dist/tools/data.d.cts +1 -1
- package/dist/tools/data.d.ts +1 -1
- package/dist/tools/data.js +1 -1
- package/dist/tools/text.cjs +42 -0
- package/dist/tools/text.d.cts +1 -1
- package/dist/tools/text.d.ts +1 -1
- package/dist/tools/text.js +1 -1
- package/dist/tools/units.cjs +125 -0
- package/dist/tools/units.d.cts +1 -1
- package/dist/tools/units.d.ts +1 -1
- package/dist/tools/units.js +1 -1
- package/dist/{units-h4rHQGp5.d.cts → units-DUqjk24x.d.cts} +95 -2
- package/dist/{units-h4rHQGp5.d.ts → units-DUqjk24x.d.ts} +95 -2
- package/package.json +1 -1
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* - env-parser : parseEnv, envToJson, envToExport, validateEnvKey
|
|
13
13
|
* - env-diff-checker: diffEnvExample
|
|
14
14
|
* - ndjson-formatter: validateNdjson, minifyNdjson, formatNdjson, ndjsonToJsonArray, jsonArrayToNdjson
|
|
15
|
+
* - subtitle-converter: parseSubtitleTimestamp, formatSubtitleTimestamp, detectSubtitleFormat, parseSubtitles, serializeSubtitles, convertSubtitles, shiftSubtitles
|
|
15
16
|
*/
|
|
16
17
|
interface ValidationResult {
|
|
17
18
|
valid: boolean;
|
|
@@ -210,7 +211,69 @@ declare function ndjsonToJsonArray(input: string): string | {
|
|
|
210
211
|
declare function jsonArrayToNdjson(input: string): string | {
|
|
211
212
|
error: string;
|
|
212
213
|
};
|
|
214
|
+
type SubtitleFormat = "srt" | "vtt";
|
|
215
|
+
interface SubtitleCue {
|
|
216
|
+
index: number;
|
|
217
|
+
/** Cue identifier line, when the source had one (WebVTT only). */
|
|
218
|
+
id?: string;
|
|
219
|
+
/** Start time in seconds. */
|
|
220
|
+
start: number;
|
|
221
|
+
/** End time in seconds. */
|
|
222
|
+
end: number;
|
|
223
|
+
/** WebVTT cue settings that followed the timestamp (e.g. `line:0 align:start`). */
|
|
224
|
+
settings?: string;
|
|
225
|
+
text: string;
|
|
226
|
+
}
|
|
227
|
+
interface ParsedSubtitles {
|
|
228
|
+
format: SubtitleFormat;
|
|
229
|
+
cues: SubtitleCue[];
|
|
230
|
+
cueCount: number;
|
|
231
|
+
/** End time of the last cue, in seconds. */
|
|
232
|
+
duration: number;
|
|
233
|
+
warnings: string[];
|
|
234
|
+
}
|
|
235
|
+
interface ConvertedSubtitles {
|
|
236
|
+
format: SubtitleFormat;
|
|
237
|
+
sourceFormat: SubtitleFormat;
|
|
238
|
+
output: string;
|
|
239
|
+
cueCount: number;
|
|
240
|
+
duration: number;
|
|
241
|
+
warnings: string[];
|
|
242
|
+
}
|
|
243
|
+
/** Parse `HH:MM:SS,mmm`, `HH:MM:SS.mmm`, or WebVTT's `MM:SS.mmm` into seconds. */
|
|
244
|
+
declare function parseSubtitleTimestamp(raw: string): number | null;
|
|
245
|
+
/** Format seconds as `HH:MM:SS,mmm` (SRT) or `HH:MM:SS.mmm` (WebVTT). */
|
|
246
|
+
declare function formatSubtitleTimestamp(totalSeconds: number, format: SubtitleFormat): string;
|
|
247
|
+
/** Auto-detect the source format from the WEBVTT signature or timestamp separator. */
|
|
248
|
+
declare function detectSubtitleFormat(input: string): SubtitleFormat;
|
|
249
|
+
/**
|
|
250
|
+
* Parse an SRT or WebVTT file into normalised cues. The format is auto-detected
|
|
251
|
+
* unless you pass one. Malformed blocks are reported as warnings rather than
|
|
252
|
+
* throwing, so a mostly-good file still converts.
|
|
253
|
+
*/
|
|
254
|
+
declare function parseSubtitles(input: string, format?: SubtitleFormat): ParsedSubtitles | {
|
|
255
|
+
error: string;
|
|
256
|
+
};
|
|
257
|
+
/** Serialise parsed cues back into SRT or WebVTT text. */
|
|
258
|
+
declare function serializeSubtitles(cues: SubtitleCue[], format: SubtitleFormat): string;
|
|
259
|
+
/**
|
|
260
|
+
* Convert a subtitle file to the target format. Converting WebVTT to SRT drops
|
|
261
|
+
* cue identifiers, cue settings, and NOTE/STYLE/REGION blocks, since SRT has no
|
|
262
|
+
* equivalent for any of them; each drop is reported as a warning.
|
|
263
|
+
*/
|
|
264
|
+
declare function convertSubtitles(input: string, target: SubtitleFormat, sourceFormat?: SubtitleFormat): ConvertedSubtitles | {
|
|
265
|
+
error: string;
|
|
266
|
+
};
|
|
267
|
+
/**
|
|
268
|
+
* Shift every cue by a fixed offset in seconds, keeping the source format
|
|
269
|
+
* unless a target is given. Negative offsets clamp at zero, so a cue that would
|
|
270
|
+
* start before the video does starts at 00:00:00 instead.
|
|
271
|
+
*/
|
|
272
|
+
declare function shiftSubtitles(input: string, offsetSeconds: number, target?: SubtitleFormat): ConvertedSubtitles | {
|
|
273
|
+
error: string;
|
|
274
|
+
};
|
|
213
275
|
|
|
276
|
+
type data_ConvertedSubtitles = ConvertedSubtitles;
|
|
214
277
|
type data_CsvParseResult = CsvParseResult;
|
|
215
278
|
type data_EnvDiffError = EnvDiffError;
|
|
216
279
|
type data_EnvDiffResult = EnvDiffResult;
|
|
@@ -221,16 +284,22 @@ type data_NdjsonLineResult = NdjsonLineResult;
|
|
|
221
284
|
type data_NdjsonValidateResult = NdjsonValidateResult;
|
|
222
285
|
type data_ParseOptions = ParseOptions;
|
|
223
286
|
type data_ParsedIni = ParsedIni;
|
|
287
|
+
type data_ParsedSubtitles = ParsedSubtitles;
|
|
288
|
+
type data_SubtitleCue = SubtitleCue;
|
|
289
|
+
type data_SubtitleFormat = SubtitleFormat;
|
|
224
290
|
type data_TomlFormatResult = TomlFormatResult;
|
|
225
291
|
type data_ValidationResult = ValidationResult;
|
|
226
292
|
type data_XmlFormatResult = XmlFormatResult;
|
|
227
293
|
type data_XmlToJsonOptions = XmlToJsonOptions;
|
|
294
|
+
declare const data_convertSubtitles: typeof convertSubtitles;
|
|
228
295
|
declare const data_csvToJson: typeof csvToJson;
|
|
229
296
|
declare const data_detectDelimiter: typeof detectDelimiter;
|
|
297
|
+
declare const data_detectSubtitleFormat: typeof detectSubtitleFormat;
|
|
230
298
|
declare const data_diffEnvExample: typeof diffEnvExample;
|
|
231
299
|
declare const data_envToExport: typeof envToExport;
|
|
232
300
|
declare const data_envToJson: typeof envToJson;
|
|
233
301
|
declare const data_formatNdjson: typeof formatNdjson;
|
|
302
|
+
declare const data_formatSubtitleTimestamp: typeof formatSubtitleTimestamp;
|
|
234
303
|
declare const data_formatToml: typeof formatToml;
|
|
235
304
|
declare const data_formatXml: typeof formatXml;
|
|
236
305
|
declare const data_getSection: typeof getSection;
|
|
@@ -250,7 +319,11 @@ declare const data_ndjsonToJsonArray: typeof ndjsonToJsonArray;
|
|
|
250
319
|
declare const data_parseCsv: typeof parseCsv;
|
|
251
320
|
declare const data_parseEnv: typeof parseEnv;
|
|
252
321
|
declare const data_parseIni: typeof parseIni;
|
|
322
|
+
declare const data_parseSubtitleTimestamp: typeof parseSubtitleTimestamp;
|
|
323
|
+
declare const data_parseSubtitles: typeof parseSubtitles;
|
|
253
324
|
declare const data_parseXmlToObject: typeof parseXmlToObject;
|
|
325
|
+
declare const data_serializeSubtitles: typeof serializeSubtitles;
|
|
326
|
+
declare const data_shiftSubtitles: typeof shiftSubtitles;
|
|
254
327
|
declare const data_stringifyIni: typeof stringifyIni;
|
|
255
328
|
declare const data_toCsvString: typeof toCsvString;
|
|
256
329
|
declare const data_tomlToJson: typeof tomlToJson;
|
|
@@ -262,7 +335,7 @@ declare const data_validateXml: typeof validateXml;
|
|
|
262
335
|
declare const data_validateYaml: typeof validateYaml;
|
|
263
336
|
declare const data_xmlToJson: typeof xmlToJson;
|
|
264
337
|
declare namespace data {
|
|
265
|
-
export { type data_CsvParseResult as CsvParseResult, type data_EnvDiffError as EnvDiffError, type data_EnvDiffResult as EnvDiffResult, type data_EnvEntry as EnvEntry, type data_EnvParseResult as EnvParseResult, type data_IniSection as IniSection, type data_NdjsonLineResult as NdjsonLineResult, type data_NdjsonValidateResult as NdjsonValidateResult, type data_ParseOptions as ParseOptions, type data_ParsedIni as ParsedIni, type data_TomlFormatResult as TomlFormatResult, type data_ValidationResult as ValidationResult, type data_XmlFormatResult as XmlFormatResult, type data_XmlToJsonOptions as XmlToJsonOptions, data_csvToJson as csvToJson, data_detectDelimiter as detectDelimiter, data_diffEnvExample as diffEnvExample, data_envToExport as envToExport, data_envToJson as envToJson, data_formatNdjson as formatNdjson, data_formatToml as formatToml, data_formatXml as formatXml, data_getSection as getSection, data_getSectionNames as getSectionNames, data_getTomlStats as getTomlStats, data_getXmlStats as getXmlStats, data_getYamlStats as getYamlStats, data_iniToJson as iniToJson, data_jsonArrayToNdjson as jsonArrayToNdjson, data_jsonToIni as jsonToIni, data_jsonToToml as jsonToToml, data_jsonToXml as jsonToXml, data_minifyNdjson as minifyNdjson, data_minifyToml as minifyToml, data_minifyXml as minifyXml, data_ndjsonToJsonArray as ndjsonToJsonArray, data_parseCsv as parseCsv, data_parseEnv as parseEnv, data_parseIni as parseIni, data_parseXmlToObject as parseXmlToObject, data_stringifyIni as stringifyIni, data_toCsvString as toCsvString, data_tomlToJson as tomlToJson, data_validateEnvKey as validateEnvKey, data_validateIni as validateIni, data_validateNdjson as validateNdjson, data_validateToml as validateToml, data_validateXml as validateXml, data_validateYaml as validateYaml, data_xmlToJson as xmlToJson };
|
|
338
|
+
export { type data_ConvertedSubtitles as ConvertedSubtitles, type data_CsvParseResult as CsvParseResult, type data_EnvDiffError as EnvDiffError, type data_EnvDiffResult as EnvDiffResult, type data_EnvEntry as EnvEntry, type data_EnvParseResult as EnvParseResult, type data_IniSection as IniSection, type data_NdjsonLineResult as NdjsonLineResult, type data_NdjsonValidateResult as NdjsonValidateResult, type data_ParseOptions as ParseOptions, type data_ParsedIni as ParsedIni, type data_ParsedSubtitles as ParsedSubtitles, type data_SubtitleCue as SubtitleCue, type data_SubtitleFormat as SubtitleFormat, type data_TomlFormatResult as TomlFormatResult, type data_ValidationResult as ValidationResult, type data_XmlFormatResult as XmlFormatResult, type data_XmlToJsonOptions as XmlToJsonOptions, data_convertSubtitles as convertSubtitles, data_csvToJson as csvToJson, data_detectDelimiter as detectDelimiter, data_detectSubtitleFormat as detectSubtitleFormat, data_diffEnvExample as diffEnvExample, data_envToExport as envToExport, data_envToJson as envToJson, data_formatNdjson as formatNdjson, data_formatSubtitleTimestamp as formatSubtitleTimestamp, data_formatToml as formatToml, data_formatXml as formatXml, data_getSection as getSection, data_getSectionNames as getSectionNames, data_getTomlStats as getTomlStats, data_getXmlStats as getXmlStats, data_getYamlStats as getYamlStats, data_iniToJson as iniToJson, data_jsonArrayToNdjson as jsonArrayToNdjson, data_jsonToIni as jsonToIni, data_jsonToToml as jsonToToml, data_jsonToXml as jsonToXml, data_minifyNdjson as minifyNdjson, data_minifyToml as minifyToml, data_minifyXml as minifyXml, data_ndjsonToJsonArray as ndjsonToJsonArray, data_parseCsv as parseCsv, data_parseEnv as parseEnv, data_parseIni as parseIni, data_parseSubtitleTimestamp as parseSubtitleTimestamp, data_parseSubtitles as parseSubtitles, data_parseXmlToObject as parseXmlToObject, data_serializeSubtitles as serializeSubtitles, data_shiftSubtitles as shiftSubtitles, data_stringifyIni as stringifyIni, data_toCsvString as toCsvString, data_tomlToJson as tomlToJson, data_validateEnvKey as validateEnvKey, data_validateIni as validateIni, data_validateNdjson as validateNdjson, data_validateToml as validateToml, data_validateXml as validateXml, data_validateYaml as validateYaml, data_xmlToJson as xmlToJson };
|
|
266
339
|
}
|
|
267
340
|
|
|
268
|
-
export {
|
|
341
|
+
export { toCsvString as $, iniToJson as A, jsonArrayToNdjson as B, type ConvertedSubtitles as C, jsonToIni as D, type EnvDiffError as E, jsonToToml as F, jsonToXml as G, minifyNdjson as H, type IniSection as I, minifyToml as J, minifyXml as K, ndjsonToJsonArray as L, parseCsv as M, type NdjsonLineResult as N, parseEnv as O, type ParseOptions as P, parseIni as Q, parseSubtitleTimestamp as R, type SubtitleCue as S, type TomlFormatResult as T, parseSubtitles as U, type ValidationResult as V, parseXmlToObject as W, type XmlFormatResult as X, serializeSubtitles as Y, shiftSubtitles as Z, stringifyIni as _, type CsvParseResult as a, tomlToJson as a0, validateEnvKey as a1, validateIni as a2, validateNdjson as a3, validateToml as a4, validateXml as a5, validateYaml as a6, xmlToJson as a7, type EnvDiffResult as b, type EnvEntry as c, data as d, type EnvParseResult as e, type NdjsonValidateResult as f, type ParsedIni as g, type ParsedSubtitles as h, type SubtitleFormat as i, type XmlToJsonOptions as j, convertSubtitles as k, csvToJson as l, detectDelimiter as m, detectSubtitleFormat as n, diffEnvExample as o, envToExport as p, envToJson as q, formatNdjson as r, formatSubtitleTimestamp as s, formatToml as t, formatXml as u, getSection as v, getSectionNames as w, getTomlStats as x, getXmlStats as y, getYamlStats as z };
|
package/dist/index.cjs
CHANGED
|
@@ -1920,6 +1920,7 @@ __export(text_exports, {
|
|
|
1920
1920
|
analyzeText: () => analyzeText,
|
|
1921
1921
|
applyOp: () => applyOp,
|
|
1922
1922
|
applyOps: () => applyOps,
|
|
1923
|
+
calculateStringDistance: () => calculateStringDistance,
|
|
1923
1924
|
centerText: () => centerText,
|
|
1924
1925
|
convertAll: () => convertAll,
|
|
1925
1926
|
convertCase: () => convertCase,
|
|
@@ -4463,16 +4464,60 @@ function detectFillerWords(text) {
|
|
|
4463
4464
|
const density = totalWords > 0 ? Math.round(totalMatches / totalWords * 1e3) / 10 : 0;
|
|
4464
4465
|
return { totalWords, totalMatches, fillerCount, hedgeCount, clicheCount, density, matches };
|
|
4465
4466
|
}
|
|
4467
|
+
var MAX_STRING_DISTANCE_LENGTH = 5e3;
|
|
4468
|
+
function calculateStringDistance(a, b, options) {
|
|
4469
|
+
if (typeof a !== "string" || typeof b !== "string") {
|
|
4470
|
+
return { error: "Both a and b must be strings" };
|
|
4471
|
+
}
|
|
4472
|
+
if (a.length > MAX_STRING_DISTANCE_LENGTH || b.length > MAX_STRING_DISTANCE_LENGTH) {
|
|
4473
|
+
return { error: `Strings must be at most ${MAX_STRING_DISTANCE_LENGTH} characters` };
|
|
4474
|
+
}
|
|
4475
|
+
const mode = options?.mode ?? "levenshtein";
|
|
4476
|
+
const caseSensitive = options?.caseSensitive ?? true;
|
|
4477
|
+
const s1 = caseSensitive ? a : a.toLowerCase();
|
|
4478
|
+
const s2 = caseSensitive ? b : b.toLowerCase();
|
|
4479
|
+
const m = s1.length;
|
|
4480
|
+
const n = s2.length;
|
|
4481
|
+
const maxLength = Math.max(m, n);
|
|
4482
|
+
if (m === 0 || n === 0) {
|
|
4483
|
+
return { a, b, distance: maxLength, similarity: maxLength === 0 ? 1 : 0, maxLength, mode };
|
|
4484
|
+
}
|
|
4485
|
+
const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
|
|
4486
|
+
for (let i = 0; i <= m; i++) dp[i][0] = i;
|
|
4487
|
+
for (let j = 0; j <= n; j++) dp[0][j] = j;
|
|
4488
|
+
for (let i = 1; i <= m; i++) {
|
|
4489
|
+
for (let j = 1; j <= n; j++) {
|
|
4490
|
+
const cost = s1[i - 1] === s2[j - 1] ? 0 : 1;
|
|
4491
|
+
dp[i][j] = Math.min(
|
|
4492
|
+
dp[i - 1][j] + 1,
|
|
4493
|
+
// deletion
|
|
4494
|
+
dp[i][j - 1] + 1,
|
|
4495
|
+
// insertion
|
|
4496
|
+
dp[i - 1][j - 1] + cost
|
|
4497
|
+
// substitution
|
|
4498
|
+
);
|
|
4499
|
+
if (mode === "damerau" && i > 1 && j > 1 && s1[i - 1] === s2[j - 2] && s1[i - 2] === s2[j - 1]) {
|
|
4500
|
+
dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + 1);
|
|
4501
|
+
}
|
|
4502
|
+
}
|
|
4503
|
+
}
|
|
4504
|
+
const distance = dp[m][n];
|
|
4505
|
+
const similarity = maxLength === 0 ? 1 : Math.round((1 - distance / maxLength) * 1e4) / 1e4;
|
|
4506
|
+
return { a, b, distance, similarity, maxLength, mode };
|
|
4507
|
+
}
|
|
4466
4508
|
|
|
4467
4509
|
// src/tools/data.ts
|
|
4468
4510
|
var data_exports = {};
|
|
4469
4511
|
__export(data_exports, {
|
|
4512
|
+
convertSubtitles: () => convertSubtitles,
|
|
4470
4513
|
csvToJson: () => csvToJson2,
|
|
4471
4514
|
detectDelimiter: () => detectDelimiter2,
|
|
4515
|
+
detectSubtitleFormat: () => detectSubtitleFormat,
|
|
4472
4516
|
diffEnvExample: () => diffEnvExample,
|
|
4473
4517
|
envToExport: () => envToExport,
|
|
4474
4518
|
envToJson: () => envToJson,
|
|
4475
4519
|
formatNdjson: () => formatNdjson,
|
|
4520
|
+
formatSubtitleTimestamp: () => formatSubtitleTimestamp,
|
|
4476
4521
|
formatToml: () => formatToml,
|
|
4477
4522
|
formatXml: () => formatXml,
|
|
4478
4523
|
getSection: () => getSection,
|
|
@@ -4492,7 +4537,11 @@ __export(data_exports, {
|
|
|
4492
4537
|
parseCsv: () => parseCsv,
|
|
4493
4538
|
parseEnv: () => parseEnv,
|
|
4494
4539
|
parseIni: () => parseIni,
|
|
4540
|
+
parseSubtitleTimestamp: () => parseSubtitleTimestamp,
|
|
4541
|
+
parseSubtitles: () => parseSubtitles,
|
|
4495
4542
|
parseXmlToObject: () => parseXmlToObject,
|
|
4543
|
+
serializeSubtitles: () => serializeSubtitles,
|
|
4544
|
+
shiftSubtitles: () => shiftSubtitles,
|
|
4496
4545
|
stringifyIni: () => stringifyIni,
|
|
4497
4546
|
toCsvString: () => toCsvString,
|
|
4498
4547
|
tomlToJson: () => tomlToJson,
|
|
@@ -5619,6 +5668,176 @@ function jsonArrayToNdjson(input) {
|
|
|
5619
5668
|
if (!Array.isArray(parsed)) return { error: "Input must be a JSON array" };
|
|
5620
5669
|
return parsed.map((item) => JSON.stringify(item)).join("\n");
|
|
5621
5670
|
}
|
|
5671
|
+
var _SUBTITLE_TIMESTAMP_RE = /(\d+):(\d{1,2}):(\d{1,2})[.,](\d{1,3})|(\d{1,3}):(\d{1,2})[.,](\d{1,3})/;
|
|
5672
|
+
var _VTT_BLOCK_KEYWORDS = ["NOTE", "STYLE", "REGION"];
|
|
5673
|
+
function parseSubtitleTimestamp(raw) {
|
|
5674
|
+
const m = _SUBTITLE_TIMESTAMP_RE.exec(raw.trim());
|
|
5675
|
+
if (!m || m.index !== 0) return null;
|
|
5676
|
+
let hours;
|
|
5677
|
+
let minutes;
|
|
5678
|
+
let seconds;
|
|
5679
|
+
let millis;
|
|
5680
|
+
if (m[1] !== void 0) {
|
|
5681
|
+
hours = Number(m[1]);
|
|
5682
|
+
minutes = Number(m[2]);
|
|
5683
|
+
seconds = Number(m[3]);
|
|
5684
|
+
millis = m[4];
|
|
5685
|
+
} else {
|
|
5686
|
+
hours = 0;
|
|
5687
|
+
minutes = Number(m[5]);
|
|
5688
|
+
seconds = Number(m[6]);
|
|
5689
|
+
millis = m[7];
|
|
5690
|
+
}
|
|
5691
|
+
if (minutes > 59 || seconds > 59) return null;
|
|
5692
|
+
return hours * 3600 + minutes * 60 + seconds + Number(millis.padEnd(3, "0")) / 1e3;
|
|
5693
|
+
}
|
|
5694
|
+
function formatSubtitleTimestamp(totalSeconds, format) {
|
|
5695
|
+
const totalMs = Math.round(Math.max(0, totalSeconds) * 1e3);
|
|
5696
|
+
const ms = totalMs % 1e3;
|
|
5697
|
+
const totalSecs = Math.floor(totalMs / 1e3);
|
|
5698
|
+
const pad = (n, width = 2) => String(n).padStart(width, "0");
|
|
5699
|
+
return `${pad(Math.floor(totalSecs / 3600))}:${pad(Math.floor(totalSecs / 60) % 60)}:${pad(totalSecs % 60)}${format === "srt" ? "," : "."}${pad(ms, 3)}`;
|
|
5700
|
+
}
|
|
5701
|
+
function _stripBom(input) {
|
|
5702
|
+
return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
|
|
5703
|
+
}
|
|
5704
|
+
function detectSubtitleFormat(input) {
|
|
5705
|
+
const text = _stripBom(input).trimStart();
|
|
5706
|
+
if (/^WEBVTT/.test(text)) return "vtt";
|
|
5707
|
+
const arrow = /(\d{1,3}):(\d{1,2})(?::(\d{1,2}))?([.,])(\d{1,3})\s*-->/.exec(text);
|
|
5708
|
+
if (arrow && arrow[4] === ".") return "vtt";
|
|
5709
|
+
return "srt";
|
|
5710
|
+
}
|
|
5711
|
+
function parseSubtitles(input, format) {
|
|
5712
|
+
if (typeof input !== "string" || input.trim().length === 0) {
|
|
5713
|
+
return { error: "Subtitle input is empty" };
|
|
5714
|
+
}
|
|
5715
|
+
const sourceFormat = format ?? detectSubtitleFormat(input);
|
|
5716
|
+
const normalised = _stripBom(input).replace(/\r\n?/g, "\n");
|
|
5717
|
+
const warnings = [];
|
|
5718
|
+
let body = normalised;
|
|
5719
|
+
if (sourceFormat === "vtt") {
|
|
5720
|
+
const headerMatch = /^WEBVTT[^\n]*\n?/.exec(body.trimStart());
|
|
5721
|
+
if (headerMatch) {
|
|
5722
|
+
body = body.trimStart().slice(headerMatch[0].length);
|
|
5723
|
+
} else {
|
|
5724
|
+
warnings.push('WebVTT input is missing its "WEBVTT" header line');
|
|
5725
|
+
}
|
|
5726
|
+
}
|
|
5727
|
+
const blocks = body.split(/\n[ \t]*\n+/).map((b) => b.trim()).filter((b) => b.length > 0);
|
|
5728
|
+
const cues = [];
|
|
5729
|
+
for (const block of blocks) {
|
|
5730
|
+
const lines = block.split("\n");
|
|
5731
|
+
const arrowIdx = lines.findIndex((l) => l.includes("-->"));
|
|
5732
|
+
if (arrowIdx === -1) {
|
|
5733
|
+
const keyword = _VTT_BLOCK_KEYWORDS.find((k) => lines[0] === k || lines[0].startsWith(`${k} `));
|
|
5734
|
+
if (sourceFormat === "vtt" && keyword) {
|
|
5735
|
+
warnings.push(`Dropped a WebVTT ${keyword} block, which has no SRT equivalent`);
|
|
5736
|
+
} else {
|
|
5737
|
+
warnings.push(`Skipped a block with no "-->" timing line: ${JSON.stringify(lines[0].slice(0, 40))}`);
|
|
5738
|
+
}
|
|
5739
|
+
continue;
|
|
5740
|
+
}
|
|
5741
|
+
const timingLine = lines[arrowIdx];
|
|
5742
|
+
const parts = timingLine.split("-->");
|
|
5743
|
+
const start = parseSubtitleTimestamp(parts[0] ?? "");
|
|
5744
|
+
const rightTrimmed = (parts[1] ?? "").trim();
|
|
5745
|
+
const end = parseSubtitleTimestamp(rightTrimmed);
|
|
5746
|
+
if (start === null || end === null) {
|
|
5747
|
+
warnings.push(`Skipped a cue with an unparseable timing line: ${JSON.stringify(timingLine.slice(0, 60))}`);
|
|
5748
|
+
continue;
|
|
5749
|
+
}
|
|
5750
|
+
const endMatch = _SUBTITLE_TIMESTAMP_RE.exec(rightTrimmed);
|
|
5751
|
+
const settings = endMatch ? rightTrimmed.slice(endMatch[0].length).trim() : "";
|
|
5752
|
+
const idLines = lines.slice(0, arrowIdx).filter((l) => l.trim().length > 0);
|
|
5753
|
+
const text = lines.slice(arrowIdx + 1).join("\n").trim();
|
|
5754
|
+
const cue = { index: cues.length + 1, start, end, text };
|
|
5755
|
+
if (sourceFormat === "vtt" && idLines.length > 0) cue.id = idLines[idLines.length - 1].trim();
|
|
5756
|
+
if (settings.length > 0) cue.settings = settings;
|
|
5757
|
+
if (end < start) warnings.push(`Cue ${cue.index} ends before it starts`);
|
|
5758
|
+
if (text.length === 0) warnings.push(`Cue ${cue.index} has no text`);
|
|
5759
|
+
if (cues.length > 0 && start < cues[cues.length - 1].end) {
|
|
5760
|
+
warnings.push(`Cue ${cue.index} starts before cue ${cue.index - 1} ends (overlapping cues)`);
|
|
5761
|
+
}
|
|
5762
|
+
cues.push(cue);
|
|
5763
|
+
}
|
|
5764
|
+
if (cues.length === 0) {
|
|
5765
|
+
return { error: "No subtitle cues found: check that the input is a valid SRT or WebVTT file" };
|
|
5766
|
+
}
|
|
5767
|
+
return {
|
|
5768
|
+
format: sourceFormat,
|
|
5769
|
+
cues,
|
|
5770
|
+
cueCount: cues.length,
|
|
5771
|
+
duration: Math.round(Math.max(...cues.map((c) => c.end)) * 1e3) / 1e3,
|
|
5772
|
+
warnings
|
|
5773
|
+
};
|
|
5774
|
+
}
|
|
5775
|
+
function serializeSubtitles(cues, format) {
|
|
5776
|
+
const blocks = cues.map((cue, i) => {
|
|
5777
|
+
const time = `${formatSubtitleTimestamp(cue.start, format)} --> ${formatSubtitleTimestamp(cue.end, format)}`;
|
|
5778
|
+
if (format === "srt") return `${i + 1}
|
|
5779
|
+
${time}
|
|
5780
|
+
${cue.text}`;
|
|
5781
|
+
const timing = cue.settings ? `${time} ${cue.settings}` : time;
|
|
5782
|
+
return cue.id ? `${cue.id}
|
|
5783
|
+
${timing}
|
|
5784
|
+
${cue.text}` : `${timing}
|
|
5785
|
+
${cue.text}`;
|
|
5786
|
+
});
|
|
5787
|
+
const body = blocks.join("\n\n");
|
|
5788
|
+
return format === "vtt" ? `WEBVTT
|
|
5789
|
+
|
|
5790
|
+
${body}
|
|
5791
|
+
` : `${body}
|
|
5792
|
+
`;
|
|
5793
|
+
}
|
|
5794
|
+
function convertSubtitles(input, target, sourceFormat) {
|
|
5795
|
+
if (target !== "srt" && target !== "vtt") return { error: 'target must be "srt" or "vtt"' };
|
|
5796
|
+
const parsed = parseSubtitles(input, sourceFormat);
|
|
5797
|
+
if ("error" in parsed) return parsed;
|
|
5798
|
+
const warnings = [...parsed.warnings];
|
|
5799
|
+
if (target === "srt") {
|
|
5800
|
+
const droppedSettings = parsed.cues.filter((c) => c.settings).length;
|
|
5801
|
+
const droppedIds = parsed.cues.filter((c) => c.id).length;
|
|
5802
|
+
if (droppedSettings > 0) {
|
|
5803
|
+
warnings.push(`Dropped WebVTT cue settings on ${droppedSettings} cue(s): SRT has no equivalent`);
|
|
5804
|
+
}
|
|
5805
|
+
if (droppedIds > 0) {
|
|
5806
|
+
warnings.push(`Dropped ${droppedIds} WebVTT cue identifier(s): SRT numbers cues sequentially instead`);
|
|
5807
|
+
}
|
|
5808
|
+
}
|
|
5809
|
+
return {
|
|
5810
|
+
format: target,
|
|
5811
|
+
sourceFormat: parsed.format,
|
|
5812
|
+
output: serializeSubtitles(parsed.cues, target),
|
|
5813
|
+
cueCount: parsed.cueCount,
|
|
5814
|
+
duration: parsed.duration,
|
|
5815
|
+
warnings
|
|
5816
|
+
};
|
|
5817
|
+
}
|
|
5818
|
+
function shiftSubtitles(input, offsetSeconds, target) {
|
|
5819
|
+
if (!Number.isFinite(offsetSeconds)) return { error: "offsetSeconds must be a finite number" };
|
|
5820
|
+
const parsed = parseSubtitles(input);
|
|
5821
|
+
if ("error" in parsed) return parsed;
|
|
5822
|
+
const warnings = [...parsed.warnings];
|
|
5823
|
+
let clamped = 0;
|
|
5824
|
+
const shifted = parsed.cues.map((cue) => {
|
|
5825
|
+
const start = cue.start + offsetSeconds;
|
|
5826
|
+
const end = cue.end + offsetSeconds;
|
|
5827
|
+
if (start < 0) clamped += 1;
|
|
5828
|
+
return { ...cue, start: Math.max(0, start), end: Math.max(0, end) };
|
|
5829
|
+
});
|
|
5830
|
+
if (clamped > 0) warnings.push(`${clamped} cue(s) would start before zero and were clamped to 00:00:00`);
|
|
5831
|
+
const outFormat = target ?? parsed.format;
|
|
5832
|
+
return {
|
|
5833
|
+
format: outFormat,
|
|
5834
|
+
sourceFormat: parsed.format,
|
|
5835
|
+
output: serializeSubtitles(shifted, outFormat),
|
|
5836
|
+
cueCount: shifted.length,
|
|
5837
|
+
duration: Math.round(Math.max(...shifted.map((c) => c.end)) * 1e3) / 1e3,
|
|
5838
|
+
warnings
|
|
5839
|
+
};
|
|
5840
|
+
}
|
|
5622
5841
|
|
|
5623
5842
|
// src/tools/generators.ts
|
|
5624
5843
|
var generators_exports = {};
|
|
@@ -6924,11 +7143,14 @@ __export(units_exports, {
|
|
|
6924
7143
|
COMMON_TAX_RATES: () => COMMON_TAX_RATES,
|
|
6925
7144
|
COMMON_TIP_PERCENTAGES: () => COMMON_TIP_PERCENTAGES,
|
|
6926
7145
|
COMMON_VIEWPORTS: () => COMMON_VIEWPORTS,
|
|
7146
|
+
applySuccessiveDiscounts: () => applySuccessiveDiscounts,
|
|
6927
7147
|
autoFormat: () => autoFormat,
|
|
6928
7148
|
bumpVersion: () => bumpVersion,
|
|
6929
7149
|
calcAspectRatio: () => calcAspectRatio,
|
|
6930
7150
|
calcSubnet: () => calcSubnet,
|
|
7151
|
+
calculateBreakEven: () => calculateBreakEven,
|
|
6931
7152
|
calculateCompoundInterest: () => calculateCompoundInterest,
|
|
7153
|
+
calculateDiscount: () => calculateDiscount,
|
|
6932
7154
|
calculateLoan: () => calculateLoan,
|
|
6933
7155
|
calculateMarginMarkup: () => calculateMarginMarkup,
|
|
6934
7156
|
calculatePercentageChange: () => calculatePercentageChange,
|
|
@@ -8374,6 +8596,128 @@ function markupToMargin(markupPercent) {
|
|
|
8374
8596
|
}
|
|
8375
8597
|
return Math.round(markupPercent / (100 + markupPercent) * 100 * 1e4) / 1e4;
|
|
8376
8598
|
}
|
|
8599
|
+
var _DISCOUNT_FIELDS = ["discountPercent", "discountAmount", "finalPrice"];
|
|
8600
|
+
function calculateDiscount(input) {
|
|
8601
|
+
const provided = (v) => typeof v === "number" && Number.isFinite(v);
|
|
8602
|
+
if (!provided(input.originalPrice)) {
|
|
8603
|
+
return { error: "originalPrice is required and must be a finite number" };
|
|
8604
|
+
}
|
|
8605
|
+
const originalPrice = input.originalPrice;
|
|
8606
|
+
if (originalPrice <= 0) return { error: "Original price must be greater than zero" };
|
|
8607
|
+
const given = _DISCOUNT_FIELDS.filter((f) => provided(input[f]));
|
|
8608
|
+
if (given.length !== 1) {
|
|
8609
|
+
return { error: `Provide exactly one of discountPercent, discountAmount, finalPrice (got ${given.length})` };
|
|
8610
|
+
}
|
|
8611
|
+
const field = given[0];
|
|
8612
|
+
let discountAmount;
|
|
8613
|
+
if (field === "discountPercent") {
|
|
8614
|
+
const pct = input.discountPercent;
|
|
8615
|
+
if (pct < 0) return { error: "Discount percent cannot be negative" };
|
|
8616
|
+
if (pct > 100) return { error: "Discount percent cannot exceed 100" };
|
|
8617
|
+
discountAmount = originalPrice * (pct / 100);
|
|
8618
|
+
} else if (field === "discountAmount") {
|
|
8619
|
+
const amt = input.discountAmount;
|
|
8620
|
+
if (amt < 0) return { error: "Discount amount cannot be negative" };
|
|
8621
|
+
if (amt > originalPrice) return { error: "Discount amount cannot exceed the original price" };
|
|
8622
|
+
discountAmount = amt;
|
|
8623
|
+
} else {
|
|
8624
|
+
const fin = input.finalPrice;
|
|
8625
|
+
if (fin < 0) return { error: "Final price cannot be negative" };
|
|
8626
|
+
if (fin > originalPrice) {
|
|
8627
|
+
return { error: "Final price cannot exceed the original price: that is a price increase, not a discount" };
|
|
8628
|
+
}
|
|
8629
|
+
discountAmount = originalPrice - fin;
|
|
8630
|
+
}
|
|
8631
|
+
const round4 = (n) => Math.round(n * 1e4) / 1e4;
|
|
8632
|
+
const finalPrice = originalPrice - discountAmount;
|
|
8633
|
+
return {
|
|
8634
|
+
originalPrice: round4(originalPrice),
|
|
8635
|
+
finalPrice: round4(finalPrice),
|
|
8636
|
+
discountAmount: round4(discountAmount),
|
|
8637
|
+
discountPercent: round4(discountAmount / originalPrice * 100),
|
|
8638
|
+
given: field,
|
|
8639
|
+
solvedFor: _DISCOUNT_FIELDS.filter((f) => f !== field)
|
|
8640
|
+
};
|
|
8641
|
+
}
|
|
8642
|
+
function applySuccessiveDiscounts(originalPrice, discountPercents) {
|
|
8643
|
+
if (!Number.isFinite(originalPrice) || originalPrice <= 0) {
|
|
8644
|
+
return { error: "Original price must be a finite number greater than zero" };
|
|
8645
|
+
}
|
|
8646
|
+
if (!Array.isArray(discountPercents) || discountPercents.length === 0) {
|
|
8647
|
+
return { error: "Provide at least one discount percent" };
|
|
8648
|
+
}
|
|
8649
|
+
if (discountPercents.length > 20) {
|
|
8650
|
+
return { error: "Provide at most 20 successive discounts" };
|
|
8651
|
+
}
|
|
8652
|
+
const round4 = (n) => Math.round(n * 1e4) / 1e4;
|
|
8653
|
+
const steps = [];
|
|
8654
|
+
let running = originalPrice;
|
|
8655
|
+
let sumOfDiscountPercents = 0;
|
|
8656
|
+
for (const pct of discountPercents) {
|
|
8657
|
+
if (!Number.isFinite(pct)) return { error: "Every discount percent must be a finite number" };
|
|
8658
|
+
if (pct < 0) return { error: "Discount percent cannot be negative" };
|
|
8659
|
+
if (pct > 100) return { error: "Discount percent cannot exceed 100" };
|
|
8660
|
+
const priceBefore = running;
|
|
8661
|
+
const saved = priceBefore * (pct / 100);
|
|
8662
|
+
running = priceBefore - saved;
|
|
8663
|
+
sumOfDiscountPercents += pct;
|
|
8664
|
+
steps.push({
|
|
8665
|
+
discountPercent: round4(pct),
|
|
8666
|
+
priceBefore: round4(priceBefore),
|
|
8667
|
+
priceAfter: round4(running),
|
|
8668
|
+
saved: round4(saved)
|
|
8669
|
+
});
|
|
8670
|
+
}
|
|
8671
|
+
const totalSaved = originalPrice - running;
|
|
8672
|
+
return {
|
|
8673
|
+
originalPrice: round4(originalPrice),
|
|
8674
|
+
finalPrice: round4(running),
|
|
8675
|
+
totalSaved: round4(totalSaved),
|
|
8676
|
+
effectiveDiscountPercent: round4(totalSaved / originalPrice * 100),
|
|
8677
|
+
sumOfDiscountPercents: round4(sumOfDiscountPercents),
|
|
8678
|
+
steps
|
|
8679
|
+
};
|
|
8680
|
+
}
|
|
8681
|
+
function calculateBreakEven(input) {
|
|
8682
|
+
const { fixedCosts, variableCostPerUnit, pricePerUnit, salesVolume } = input;
|
|
8683
|
+
if (!Number.isFinite(fixedCosts)) return { error: "fixedCosts must be a finite number" };
|
|
8684
|
+
if (!Number.isFinite(variableCostPerUnit)) return { error: "variableCostPerUnit must be a finite number" };
|
|
8685
|
+
if (!Number.isFinite(pricePerUnit)) return { error: "pricePerUnit must be a finite number" };
|
|
8686
|
+
if (fixedCosts < 0) return { error: "fixedCosts cannot be negative" };
|
|
8687
|
+
if (variableCostPerUnit < 0) return { error: "variableCostPerUnit cannot be negative" };
|
|
8688
|
+
if (pricePerUnit <= 0) return { error: "pricePerUnit must be greater than zero" };
|
|
8689
|
+
if (pricePerUnit <= variableCostPerUnit) {
|
|
8690
|
+
return { error: "pricePerUnit must be greater than variableCostPerUnit, otherwise every unit sold loses money and break-even is never reached" };
|
|
8691
|
+
}
|
|
8692
|
+
const round4 = (n) => Math.round(n * 1e4) / 1e4;
|
|
8693
|
+
const contributionMarginPerUnit = pricePerUnit - variableCostPerUnit;
|
|
8694
|
+
const contributionMarginRatio = contributionMarginPerUnit / pricePerUnit * 100;
|
|
8695
|
+
const breakEvenUnits = fixedCosts / contributionMarginPerUnit;
|
|
8696
|
+
const breakEvenUnitsRounded = Math.ceil(breakEvenUnits);
|
|
8697
|
+
const breakEvenRevenue = breakEvenUnits * pricePerUnit;
|
|
8698
|
+
const result = {
|
|
8699
|
+
fixedCosts: round4(fixedCosts),
|
|
8700
|
+
variableCostPerUnit: round4(variableCostPerUnit),
|
|
8701
|
+
pricePerUnit: round4(pricePerUnit),
|
|
8702
|
+
contributionMarginPerUnit: round4(contributionMarginPerUnit),
|
|
8703
|
+
contributionMarginRatio: round4(contributionMarginRatio),
|
|
8704
|
+
breakEvenUnits: round4(breakEvenUnits),
|
|
8705
|
+
breakEvenUnitsRounded,
|
|
8706
|
+
breakEvenRevenue: round4(breakEvenRevenue)
|
|
8707
|
+
};
|
|
8708
|
+
if (typeof salesVolume === "number" && Number.isFinite(salesVolume)) {
|
|
8709
|
+
if (salesVolume < 0) return { error: "salesVolume cannot be negative" };
|
|
8710
|
+
const revenueAtSalesVolume = salesVolume * pricePerUnit;
|
|
8711
|
+
const profitAtSalesVolume = salesVolume * contributionMarginPerUnit - fixedCosts;
|
|
8712
|
+
const marginOfSafetyUnits = salesVolume - breakEvenUnits;
|
|
8713
|
+
result.salesVolume = round4(salesVolume);
|
|
8714
|
+
result.revenueAtSalesVolume = round4(revenueAtSalesVolume);
|
|
8715
|
+
result.profitAtSalesVolume = round4(profitAtSalesVolume);
|
|
8716
|
+
result.marginOfSafetyUnits = round4(marginOfSafetyUnits);
|
|
8717
|
+
result.marginOfSafetyPercent = salesVolume > 0 ? round4(marginOfSafetyUnits / salesVolume * 100) : 0;
|
|
8718
|
+
}
|
|
8719
|
+
return result;
|
|
8720
|
+
}
|
|
8377
8721
|
|
|
8378
8722
|
// src/tools/network.ts
|
|
8379
8723
|
var network_exports = {};
|
package/dist/index.d.cts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export { j as json } from './json-DEVFIpsC.cjs';
|
|
2
2
|
export { e as encoding } from './encoding-Dk3wpmb3.cjs';
|
|
3
3
|
export { h as hashing } from './hashing-CnetQFD_.cjs';
|
|
4
|
-
export { t as text } from './text-
|
|
5
|
-
export { d as data } from './data-
|
|
4
|
+
export { t as text } from './text-D1fySkCB.cjs';
|
|
5
|
+
export { d as data } from './data-XRb0D7Zn.cjs';
|
|
6
6
|
export { g as generators } from './generators-BGtRGpJZ.cjs';
|
|
7
7
|
export { t as time } from './time-KqJjdqII.cjs';
|
|
8
|
-
export { u as units } from './units-
|
|
8
|
+
export { u as units } from './units-DUqjk24x.cjs';
|
|
9
9
|
export { n as network } from './network-BJOjJnmY.cjs';
|
|
10
10
|
export { a as api } from './api-Xl7OzIgq.cjs';
|
|
11
11
|
export { c as code } from './code-DaRvff2v.cjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export { j as json } from './json-DEVFIpsC.js';
|
|
2
2
|
export { e as encoding } from './encoding-Dk3wpmb3.js';
|
|
3
3
|
export { h as hashing } from './hashing-CnetQFD_.js';
|
|
4
|
-
export { t as text } from './text-
|
|
5
|
-
export { d as data } from './data-
|
|
4
|
+
export { t as text } from './text-D1fySkCB.js';
|
|
5
|
+
export { d as data } from './data-XRb0D7Zn.js';
|
|
6
6
|
export { g as generators } from './generators-BGtRGpJZ.js';
|
|
7
7
|
export { t as time } from './time-KqJjdqII.js';
|
|
8
|
-
export { u as units } from './units-
|
|
8
|
+
export { u as units } from './units-DUqjk24x.js';
|
|
9
9
|
export { n as network } from './network-BJOjJnmY.js';
|
|
10
10
|
export { a as api } from './api-Xl7OzIgq.js';
|
|
11
11
|
export { c as code } from './code-DaRvff2v.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { media_exports as media } from './chunk-XMKJYVKB.js';
|
|
2
|
-
export { units_exports as units } from './chunk-
|
|
2
|
+
export { units_exports as units } from './chunk-FVKNR3PV.js';
|
|
3
3
|
export { network_exports as network } from './chunk-Y5OOP6WN.js';
|
|
4
4
|
export { api_exports as api } from './chunk-Q3B3YWOA.js';
|
|
5
5
|
export { code_exports as code } from './chunk-LXKIPPDG.js';
|
|
@@ -10,8 +10,8 @@ export { ai_agent_exports as ai_agent } from './chunk-265NPGKH.js';
|
|
|
10
10
|
export { json_exports as json } from './chunk-WO3LJMVV.js';
|
|
11
11
|
export { encoding_exports as encoding } from './chunk-DOWQZUTP.js';
|
|
12
12
|
export { hashing_exports as hashing } from './chunk-GEFVMGZR.js';
|
|
13
|
-
export { text_exports as text } from './chunk-
|
|
14
|
-
export { data_exports as data } from './chunk-
|
|
13
|
+
export { text_exports as text } from './chunk-AFEZ6N4A.js';
|
|
14
|
+
export { data_exports as data } from './chunk-66QHV5KW.js';
|
|
15
15
|
export { generators_exports as generators } from './chunk-2CJSLYWI.js';
|
|
16
16
|
export { time_exports as time } from './chunk-IEPWZERQ.js';
|
|
17
17
|
import './chunk-MLKGABMK.js';
|