@utilix-tech/sdk 0.23.0 → 0.24.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 +4 -0
- package/README.md +20 -5
- package/dist/{chunk-TINQFTLO.js → chunk-66QHV5KW.js} +178 -1
- package/dist/{chunk-AIKSKJ77.js → chunk-HZIS2DMJ.js} +85 -1
- package/dist/{data-D8XTI7Du.d.cts → data-XRb0D7Zn.d.cts} +75 -2
- package/dist/{data-D8XTI7Du.d.ts → data-XRb0D7Zn.d.ts} +75 -2
- package/dist/index.cjs +261 -0
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -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/units.cjs +84 -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-DAoSM2g8.d.cts} +58 -2
- package/dist/{units-h4rHQGp5.d.ts → units-DAoSM2g8.d.ts} +58 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4467,12 +4467,15 @@ function detectFillerWords(text) {
|
|
|
4467
4467
|
// src/tools/data.ts
|
|
4468
4468
|
var data_exports = {};
|
|
4469
4469
|
__export(data_exports, {
|
|
4470
|
+
convertSubtitles: () => convertSubtitles,
|
|
4470
4471
|
csvToJson: () => csvToJson2,
|
|
4471
4472
|
detectDelimiter: () => detectDelimiter2,
|
|
4473
|
+
detectSubtitleFormat: () => detectSubtitleFormat,
|
|
4472
4474
|
diffEnvExample: () => diffEnvExample,
|
|
4473
4475
|
envToExport: () => envToExport,
|
|
4474
4476
|
envToJson: () => envToJson,
|
|
4475
4477
|
formatNdjson: () => formatNdjson,
|
|
4478
|
+
formatSubtitleTimestamp: () => formatSubtitleTimestamp,
|
|
4476
4479
|
formatToml: () => formatToml,
|
|
4477
4480
|
formatXml: () => formatXml,
|
|
4478
4481
|
getSection: () => getSection,
|
|
@@ -4492,7 +4495,11 @@ __export(data_exports, {
|
|
|
4492
4495
|
parseCsv: () => parseCsv,
|
|
4493
4496
|
parseEnv: () => parseEnv,
|
|
4494
4497
|
parseIni: () => parseIni,
|
|
4498
|
+
parseSubtitleTimestamp: () => parseSubtitleTimestamp,
|
|
4499
|
+
parseSubtitles: () => parseSubtitles,
|
|
4495
4500
|
parseXmlToObject: () => parseXmlToObject,
|
|
4501
|
+
serializeSubtitles: () => serializeSubtitles,
|
|
4502
|
+
shiftSubtitles: () => shiftSubtitles,
|
|
4496
4503
|
stringifyIni: () => stringifyIni,
|
|
4497
4504
|
toCsvString: () => toCsvString,
|
|
4498
4505
|
tomlToJson: () => tomlToJson,
|
|
@@ -5619,6 +5626,176 @@ function jsonArrayToNdjson(input) {
|
|
|
5619
5626
|
if (!Array.isArray(parsed)) return { error: "Input must be a JSON array" };
|
|
5620
5627
|
return parsed.map((item) => JSON.stringify(item)).join("\n");
|
|
5621
5628
|
}
|
|
5629
|
+
var _SUBTITLE_TIMESTAMP_RE = /(\d+):(\d{1,2}):(\d{1,2})[.,](\d{1,3})|(\d{1,3}):(\d{1,2})[.,](\d{1,3})/;
|
|
5630
|
+
var _VTT_BLOCK_KEYWORDS = ["NOTE", "STYLE", "REGION"];
|
|
5631
|
+
function parseSubtitleTimestamp(raw) {
|
|
5632
|
+
const m = _SUBTITLE_TIMESTAMP_RE.exec(raw.trim());
|
|
5633
|
+
if (!m || m.index !== 0) return null;
|
|
5634
|
+
let hours;
|
|
5635
|
+
let minutes;
|
|
5636
|
+
let seconds;
|
|
5637
|
+
let millis;
|
|
5638
|
+
if (m[1] !== void 0) {
|
|
5639
|
+
hours = Number(m[1]);
|
|
5640
|
+
minutes = Number(m[2]);
|
|
5641
|
+
seconds = Number(m[3]);
|
|
5642
|
+
millis = m[4];
|
|
5643
|
+
} else {
|
|
5644
|
+
hours = 0;
|
|
5645
|
+
minutes = Number(m[5]);
|
|
5646
|
+
seconds = Number(m[6]);
|
|
5647
|
+
millis = m[7];
|
|
5648
|
+
}
|
|
5649
|
+
if (minutes > 59 || seconds > 59) return null;
|
|
5650
|
+
return hours * 3600 + minutes * 60 + seconds + Number(millis.padEnd(3, "0")) / 1e3;
|
|
5651
|
+
}
|
|
5652
|
+
function formatSubtitleTimestamp(totalSeconds, format) {
|
|
5653
|
+
const totalMs = Math.round(Math.max(0, totalSeconds) * 1e3);
|
|
5654
|
+
const ms = totalMs % 1e3;
|
|
5655
|
+
const totalSecs = Math.floor(totalMs / 1e3);
|
|
5656
|
+
const pad = (n, width = 2) => String(n).padStart(width, "0");
|
|
5657
|
+
return `${pad(Math.floor(totalSecs / 3600))}:${pad(Math.floor(totalSecs / 60) % 60)}:${pad(totalSecs % 60)}${format === "srt" ? "," : "."}${pad(ms, 3)}`;
|
|
5658
|
+
}
|
|
5659
|
+
function _stripBom(input) {
|
|
5660
|
+
return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
|
|
5661
|
+
}
|
|
5662
|
+
function detectSubtitleFormat(input) {
|
|
5663
|
+
const text = _stripBom(input).trimStart();
|
|
5664
|
+
if (/^WEBVTT/.test(text)) return "vtt";
|
|
5665
|
+
const arrow = /(\d{1,3}):(\d{1,2})(?::(\d{1,2}))?([.,])(\d{1,3})\s*-->/.exec(text);
|
|
5666
|
+
if (arrow && arrow[4] === ".") return "vtt";
|
|
5667
|
+
return "srt";
|
|
5668
|
+
}
|
|
5669
|
+
function parseSubtitles(input, format) {
|
|
5670
|
+
if (typeof input !== "string" || input.trim().length === 0) {
|
|
5671
|
+
return { error: "Subtitle input is empty" };
|
|
5672
|
+
}
|
|
5673
|
+
const sourceFormat = format ?? detectSubtitleFormat(input);
|
|
5674
|
+
const normalised = _stripBom(input).replace(/\r\n?/g, "\n");
|
|
5675
|
+
const warnings = [];
|
|
5676
|
+
let body = normalised;
|
|
5677
|
+
if (sourceFormat === "vtt") {
|
|
5678
|
+
const headerMatch = /^WEBVTT[^\n]*\n?/.exec(body.trimStart());
|
|
5679
|
+
if (headerMatch) {
|
|
5680
|
+
body = body.trimStart().slice(headerMatch[0].length);
|
|
5681
|
+
} else {
|
|
5682
|
+
warnings.push('WebVTT input is missing its "WEBVTT" header line');
|
|
5683
|
+
}
|
|
5684
|
+
}
|
|
5685
|
+
const blocks = body.split(/\n[ \t]*\n+/).map((b) => b.trim()).filter((b) => b.length > 0);
|
|
5686
|
+
const cues = [];
|
|
5687
|
+
for (const block of blocks) {
|
|
5688
|
+
const lines = block.split("\n");
|
|
5689
|
+
const arrowIdx = lines.findIndex((l) => l.includes("-->"));
|
|
5690
|
+
if (arrowIdx === -1) {
|
|
5691
|
+
const keyword = _VTT_BLOCK_KEYWORDS.find((k) => lines[0] === k || lines[0].startsWith(`${k} `));
|
|
5692
|
+
if (sourceFormat === "vtt" && keyword) {
|
|
5693
|
+
warnings.push(`Dropped a WebVTT ${keyword} block, which has no SRT equivalent`);
|
|
5694
|
+
} else {
|
|
5695
|
+
warnings.push(`Skipped a block with no "-->" timing line: ${JSON.stringify(lines[0].slice(0, 40))}`);
|
|
5696
|
+
}
|
|
5697
|
+
continue;
|
|
5698
|
+
}
|
|
5699
|
+
const timingLine = lines[arrowIdx];
|
|
5700
|
+
const parts = timingLine.split("-->");
|
|
5701
|
+
const start = parseSubtitleTimestamp(parts[0] ?? "");
|
|
5702
|
+
const rightTrimmed = (parts[1] ?? "").trim();
|
|
5703
|
+
const end = parseSubtitleTimestamp(rightTrimmed);
|
|
5704
|
+
if (start === null || end === null) {
|
|
5705
|
+
warnings.push(`Skipped a cue with an unparseable timing line: ${JSON.stringify(timingLine.slice(0, 60))}`);
|
|
5706
|
+
continue;
|
|
5707
|
+
}
|
|
5708
|
+
const endMatch = _SUBTITLE_TIMESTAMP_RE.exec(rightTrimmed);
|
|
5709
|
+
const settings = endMatch ? rightTrimmed.slice(endMatch[0].length).trim() : "";
|
|
5710
|
+
const idLines = lines.slice(0, arrowIdx).filter((l) => l.trim().length > 0);
|
|
5711
|
+
const text = lines.slice(arrowIdx + 1).join("\n").trim();
|
|
5712
|
+
const cue = { index: cues.length + 1, start, end, text };
|
|
5713
|
+
if (sourceFormat === "vtt" && idLines.length > 0) cue.id = idLines[idLines.length - 1].trim();
|
|
5714
|
+
if (settings.length > 0) cue.settings = settings;
|
|
5715
|
+
if (end < start) warnings.push(`Cue ${cue.index} ends before it starts`);
|
|
5716
|
+
if (text.length === 0) warnings.push(`Cue ${cue.index} has no text`);
|
|
5717
|
+
if (cues.length > 0 && start < cues[cues.length - 1].end) {
|
|
5718
|
+
warnings.push(`Cue ${cue.index} starts before cue ${cue.index - 1} ends (overlapping cues)`);
|
|
5719
|
+
}
|
|
5720
|
+
cues.push(cue);
|
|
5721
|
+
}
|
|
5722
|
+
if (cues.length === 0) {
|
|
5723
|
+
return { error: "No subtitle cues found: check that the input is a valid SRT or WebVTT file" };
|
|
5724
|
+
}
|
|
5725
|
+
return {
|
|
5726
|
+
format: sourceFormat,
|
|
5727
|
+
cues,
|
|
5728
|
+
cueCount: cues.length,
|
|
5729
|
+
duration: Math.round(Math.max(...cues.map((c) => c.end)) * 1e3) / 1e3,
|
|
5730
|
+
warnings
|
|
5731
|
+
};
|
|
5732
|
+
}
|
|
5733
|
+
function serializeSubtitles(cues, format) {
|
|
5734
|
+
const blocks = cues.map((cue, i) => {
|
|
5735
|
+
const time = `${formatSubtitleTimestamp(cue.start, format)} --> ${formatSubtitleTimestamp(cue.end, format)}`;
|
|
5736
|
+
if (format === "srt") return `${i + 1}
|
|
5737
|
+
${time}
|
|
5738
|
+
${cue.text}`;
|
|
5739
|
+
const timing = cue.settings ? `${time} ${cue.settings}` : time;
|
|
5740
|
+
return cue.id ? `${cue.id}
|
|
5741
|
+
${timing}
|
|
5742
|
+
${cue.text}` : `${timing}
|
|
5743
|
+
${cue.text}`;
|
|
5744
|
+
});
|
|
5745
|
+
const body = blocks.join("\n\n");
|
|
5746
|
+
return format === "vtt" ? `WEBVTT
|
|
5747
|
+
|
|
5748
|
+
${body}
|
|
5749
|
+
` : `${body}
|
|
5750
|
+
`;
|
|
5751
|
+
}
|
|
5752
|
+
function convertSubtitles(input, target, sourceFormat) {
|
|
5753
|
+
if (target !== "srt" && target !== "vtt") return { error: 'target must be "srt" or "vtt"' };
|
|
5754
|
+
const parsed = parseSubtitles(input, sourceFormat);
|
|
5755
|
+
if ("error" in parsed) return parsed;
|
|
5756
|
+
const warnings = [...parsed.warnings];
|
|
5757
|
+
if (target === "srt") {
|
|
5758
|
+
const droppedSettings = parsed.cues.filter((c) => c.settings).length;
|
|
5759
|
+
const droppedIds = parsed.cues.filter((c) => c.id).length;
|
|
5760
|
+
if (droppedSettings > 0) {
|
|
5761
|
+
warnings.push(`Dropped WebVTT cue settings on ${droppedSettings} cue(s): SRT has no equivalent`);
|
|
5762
|
+
}
|
|
5763
|
+
if (droppedIds > 0) {
|
|
5764
|
+
warnings.push(`Dropped ${droppedIds} WebVTT cue identifier(s): SRT numbers cues sequentially instead`);
|
|
5765
|
+
}
|
|
5766
|
+
}
|
|
5767
|
+
return {
|
|
5768
|
+
format: target,
|
|
5769
|
+
sourceFormat: parsed.format,
|
|
5770
|
+
output: serializeSubtitles(parsed.cues, target),
|
|
5771
|
+
cueCount: parsed.cueCount,
|
|
5772
|
+
duration: parsed.duration,
|
|
5773
|
+
warnings
|
|
5774
|
+
};
|
|
5775
|
+
}
|
|
5776
|
+
function shiftSubtitles(input, offsetSeconds, target) {
|
|
5777
|
+
if (!Number.isFinite(offsetSeconds)) return { error: "offsetSeconds must be a finite number" };
|
|
5778
|
+
const parsed = parseSubtitles(input);
|
|
5779
|
+
if ("error" in parsed) return parsed;
|
|
5780
|
+
const warnings = [...parsed.warnings];
|
|
5781
|
+
let clamped = 0;
|
|
5782
|
+
const shifted = parsed.cues.map((cue) => {
|
|
5783
|
+
const start = cue.start + offsetSeconds;
|
|
5784
|
+
const end = cue.end + offsetSeconds;
|
|
5785
|
+
if (start < 0) clamped += 1;
|
|
5786
|
+
return { ...cue, start: Math.max(0, start), end: Math.max(0, end) };
|
|
5787
|
+
});
|
|
5788
|
+
if (clamped > 0) warnings.push(`${clamped} cue(s) would start before zero and were clamped to 00:00:00`);
|
|
5789
|
+
const outFormat = target ?? parsed.format;
|
|
5790
|
+
return {
|
|
5791
|
+
format: outFormat,
|
|
5792
|
+
sourceFormat: parsed.format,
|
|
5793
|
+
output: serializeSubtitles(shifted, outFormat),
|
|
5794
|
+
cueCount: shifted.length,
|
|
5795
|
+
duration: Math.round(Math.max(...shifted.map((c) => c.end)) * 1e3) / 1e3,
|
|
5796
|
+
warnings
|
|
5797
|
+
};
|
|
5798
|
+
}
|
|
5622
5799
|
|
|
5623
5800
|
// src/tools/generators.ts
|
|
5624
5801
|
var generators_exports = {};
|
|
@@ -6924,11 +7101,13 @@ __export(units_exports, {
|
|
|
6924
7101
|
COMMON_TAX_RATES: () => COMMON_TAX_RATES,
|
|
6925
7102
|
COMMON_TIP_PERCENTAGES: () => COMMON_TIP_PERCENTAGES,
|
|
6926
7103
|
COMMON_VIEWPORTS: () => COMMON_VIEWPORTS,
|
|
7104
|
+
applySuccessiveDiscounts: () => applySuccessiveDiscounts,
|
|
6927
7105
|
autoFormat: () => autoFormat,
|
|
6928
7106
|
bumpVersion: () => bumpVersion,
|
|
6929
7107
|
calcAspectRatio: () => calcAspectRatio,
|
|
6930
7108
|
calcSubnet: () => calcSubnet,
|
|
6931
7109
|
calculateCompoundInterest: () => calculateCompoundInterest,
|
|
7110
|
+
calculateDiscount: () => calculateDiscount,
|
|
6932
7111
|
calculateLoan: () => calculateLoan,
|
|
6933
7112
|
calculateMarginMarkup: () => calculateMarginMarkup,
|
|
6934
7113
|
calculatePercentageChange: () => calculatePercentageChange,
|
|
@@ -8374,6 +8553,88 @@ function markupToMargin(markupPercent) {
|
|
|
8374
8553
|
}
|
|
8375
8554
|
return Math.round(markupPercent / (100 + markupPercent) * 100 * 1e4) / 1e4;
|
|
8376
8555
|
}
|
|
8556
|
+
var _DISCOUNT_FIELDS = ["discountPercent", "discountAmount", "finalPrice"];
|
|
8557
|
+
function calculateDiscount(input) {
|
|
8558
|
+
const provided = (v) => typeof v === "number" && Number.isFinite(v);
|
|
8559
|
+
if (!provided(input.originalPrice)) {
|
|
8560
|
+
return { error: "originalPrice is required and must be a finite number" };
|
|
8561
|
+
}
|
|
8562
|
+
const originalPrice = input.originalPrice;
|
|
8563
|
+
if (originalPrice <= 0) return { error: "Original price must be greater than zero" };
|
|
8564
|
+
const given = _DISCOUNT_FIELDS.filter((f) => provided(input[f]));
|
|
8565
|
+
if (given.length !== 1) {
|
|
8566
|
+
return { error: `Provide exactly one of discountPercent, discountAmount, finalPrice (got ${given.length})` };
|
|
8567
|
+
}
|
|
8568
|
+
const field = given[0];
|
|
8569
|
+
let discountAmount;
|
|
8570
|
+
if (field === "discountPercent") {
|
|
8571
|
+
const pct = input.discountPercent;
|
|
8572
|
+
if (pct < 0) return { error: "Discount percent cannot be negative" };
|
|
8573
|
+
if (pct > 100) return { error: "Discount percent cannot exceed 100" };
|
|
8574
|
+
discountAmount = originalPrice * (pct / 100);
|
|
8575
|
+
} else if (field === "discountAmount") {
|
|
8576
|
+
const amt = input.discountAmount;
|
|
8577
|
+
if (amt < 0) return { error: "Discount amount cannot be negative" };
|
|
8578
|
+
if (amt > originalPrice) return { error: "Discount amount cannot exceed the original price" };
|
|
8579
|
+
discountAmount = amt;
|
|
8580
|
+
} else {
|
|
8581
|
+
const fin = input.finalPrice;
|
|
8582
|
+
if (fin < 0) return { error: "Final price cannot be negative" };
|
|
8583
|
+
if (fin > originalPrice) {
|
|
8584
|
+
return { error: "Final price cannot exceed the original price: that is a price increase, not a discount" };
|
|
8585
|
+
}
|
|
8586
|
+
discountAmount = originalPrice - fin;
|
|
8587
|
+
}
|
|
8588
|
+
const round4 = (n) => Math.round(n * 1e4) / 1e4;
|
|
8589
|
+
const finalPrice = originalPrice - discountAmount;
|
|
8590
|
+
return {
|
|
8591
|
+
originalPrice: round4(originalPrice),
|
|
8592
|
+
finalPrice: round4(finalPrice),
|
|
8593
|
+
discountAmount: round4(discountAmount),
|
|
8594
|
+
discountPercent: round4(discountAmount / originalPrice * 100),
|
|
8595
|
+
given: field,
|
|
8596
|
+
solvedFor: _DISCOUNT_FIELDS.filter((f) => f !== field)
|
|
8597
|
+
};
|
|
8598
|
+
}
|
|
8599
|
+
function applySuccessiveDiscounts(originalPrice, discountPercents) {
|
|
8600
|
+
if (!Number.isFinite(originalPrice) || originalPrice <= 0) {
|
|
8601
|
+
return { error: "Original price must be a finite number greater than zero" };
|
|
8602
|
+
}
|
|
8603
|
+
if (!Array.isArray(discountPercents) || discountPercents.length === 0) {
|
|
8604
|
+
return { error: "Provide at least one discount percent" };
|
|
8605
|
+
}
|
|
8606
|
+
if (discountPercents.length > 20) {
|
|
8607
|
+
return { error: "Provide at most 20 successive discounts" };
|
|
8608
|
+
}
|
|
8609
|
+
const round4 = (n) => Math.round(n * 1e4) / 1e4;
|
|
8610
|
+
const steps = [];
|
|
8611
|
+
let running = originalPrice;
|
|
8612
|
+
let sumOfDiscountPercents = 0;
|
|
8613
|
+
for (const pct of discountPercents) {
|
|
8614
|
+
if (!Number.isFinite(pct)) return { error: "Every discount percent must be a finite number" };
|
|
8615
|
+
if (pct < 0) return { error: "Discount percent cannot be negative" };
|
|
8616
|
+
if (pct > 100) return { error: "Discount percent cannot exceed 100" };
|
|
8617
|
+
const priceBefore = running;
|
|
8618
|
+
const saved = priceBefore * (pct / 100);
|
|
8619
|
+
running = priceBefore - saved;
|
|
8620
|
+
sumOfDiscountPercents += pct;
|
|
8621
|
+
steps.push({
|
|
8622
|
+
discountPercent: round4(pct),
|
|
8623
|
+
priceBefore: round4(priceBefore),
|
|
8624
|
+
priceAfter: round4(running),
|
|
8625
|
+
saved: round4(saved)
|
|
8626
|
+
});
|
|
8627
|
+
}
|
|
8628
|
+
const totalSaved = originalPrice - running;
|
|
8629
|
+
return {
|
|
8630
|
+
originalPrice: round4(originalPrice),
|
|
8631
|
+
finalPrice: round4(running),
|
|
8632
|
+
totalSaved: round4(totalSaved),
|
|
8633
|
+
effectiveDiscountPercent: round4(totalSaved / originalPrice * 100),
|
|
8634
|
+
sumOfDiscountPercents: round4(sumOfDiscountPercents),
|
|
8635
|
+
steps
|
|
8636
|
+
};
|
|
8637
|
+
}
|
|
8377
8638
|
|
|
8378
8639
|
// src/tools/network.ts
|
|
8379
8640
|
var network_exports = {};
|
package/dist/index.d.cts
CHANGED
|
@@ -2,10 +2,10 @@ 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
4
|
export { t as text } from './text-DS70aMY5.cjs';
|
|
5
|
-
export { d as data } from './data-
|
|
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-DAoSM2g8.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
|
@@ -2,10 +2,10 @@ 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
4
|
export { t as text } from './text-DS70aMY5.js';
|
|
5
|
-
export { d as data } from './data-
|
|
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-DAoSM2g8.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-HZIS2DMJ.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';
|
|
@@ -11,7 +11,7 @@ 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
13
|
export { text_exports as text } from './chunk-2I4O5YCT.js';
|
|
14
|
-
export { data_exports as data } from './chunk-
|
|
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';
|
package/dist/tools/data.cjs
CHANGED
|
@@ -1139,13 +1139,186 @@ function jsonArrayToNdjson(input) {
|
|
|
1139
1139
|
if (!Array.isArray(parsed)) return { error: "Input must be a JSON array" };
|
|
1140
1140
|
return parsed.map((item) => JSON.stringify(item)).join("\n");
|
|
1141
1141
|
}
|
|
1142
|
+
var _SUBTITLE_TIMESTAMP_RE = /(\d+):(\d{1,2}):(\d{1,2})[.,](\d{1,3})|(\d{1,3}):(\d{1,2})[.,](\d{1,3})/;
|
|
1143
|
+
var _VTT_BLOCK_KEYWORDS = ["NOTE", "STYLE", "REGION"];
|
|
1144
|
+
function parseSubtitleTimestamp(raw) {
|
|
1145
|
+
const m = _SUBTITLE_TIMESTAMP_RE.exec(raw.trim());
|
|
1146
|
+
if (!m || m.index !== 0) return null;
|
|
1147
|
+
let hours;
|
|
1148
|
+
let minutes;
|
|
1149
|
+
let seconds;
|
|
1150
|
+
let millis;
|
|
1151
|
+
if (m[1] !== void 0) {
|
|
1152
|
+
hours = Number(m[1]);
|
|
1153
|
+
minutes = Number(m[2]);
|
|
1154
|
+
seconds = Number(m[3]);
|
|
1155
|
+
millis = m[4];
|
|
1156
|
+
} else {
|
|
1157
|
+
hours = 0;
|
|
1158
|
+
minutes = Number(m[5]);
|
|
1159
|
+
seconds = Number(m[6]);
|
|
1160
|
+
millis = m[7];
|
|
1161
|
+
}
|
|
1162
|
+
if (minutes > 59 || seconds > 59) return null;
|
|
1163
|
+
return hours * 3600 + minutes * 60 + seconds + Number(millis.padEnd(3, "0")) / 1e3;
|
|
1164
|
+
}
|
|
1165
|
+
function formatSubtitleTimestamp(totalSeconds, format) {
|
|
1166
|
+
const totalMs = Math.round(Math.max(0, totalSeconds) * 1e3);
|
|
1167
|
+
const ms = totalMs % 1e3;
|
|
1168
|
+
const totalSecs = Math.floor(totalMs / 1e3);
|
|
1169
|
+
const pad = (n, width = 2) => String(n).padStart(width, "0");
|
|
1170
|
+
return `${pad(Math.floor(totalSecs / 3600))}:${pad(Math.floor(totalSecs / 60) % 60)}:${pad(totalSecs % 60)}${format === "srt" ? "," : "."}${pad(ms, 3)}`;
|
|
1171
|
+
}
|
|
1172
|
+
function _stripBom(input) {
|
|
1173
|
+
return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
|
|
1174
|
+
}
|
|
1175
|
+
function detectSubtitleFormat(input) {
|
|
1176
|
+
const text = _stripBom(input).trimStart();
|
|
1177
|
+
if (/^WEBVTT/.test(text)) return "vtt";
|
|
1178
|
+
const arrow = /(\d{1,3}):(\d{1,2})(?::(\d{1,2}))?([.,])(\d{1,3})\s*-->/.exec(text);
|
|
1179
|
+
if (arrow && arrow[4] === ".") return "vtt";
|
|
1180
|
+
return "srt";
|
|
1181
|
+
}
|
|
1182
|
+
function parseSubtitles(input, format) {
|
|
1183
|
+
if (typeof input !== "string" || input.trim().length === 0) {
|
|
1184
|
+
return { error: "Subtitle input is empty" };
|
|
1185
|
+
}
|
|
1186
|
+
const sourceFormat = format ?? detectSubtitleFormat(input);
|
|
1187
|
+
const normalised = _stripBom(input).replace(/\r\n?/g, "\n");
|
|
1188
|
+
const warnings = [];
|
|
1189
|
+
let body = normalised;
|
|
1190
|
+
if (sourceFormat === "vtt") {
|
|
1191
|
+
const headerMatch = /^WEBVTT[^\n]*\n?/.exec(body.trimStart());
|
|
1192
|
+
if (headerMatch) {
|
|
1193
|
+
body = body.trimStart().slice(headerMatch[0].length);
|
|
1194
|
+
} else {
|
|
1195
|
+
warnings.push('WebVTT input is missing its "WEBVTT" header line');
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
const blocks = body.split(/\n[ \t]*\n+/).map((b) => b.trim()).filter((b) => b.length > 0);
|
|
1199
|
+
const cues = [];
|
|
1200
|
+
for (const block of blocks) {
|
|
1201
|
+
const lines = block.split("\n");
|
|
1202
|
+
const arrowIdx = lines.findIndex((l) => l.includes("-->"));
|
|
1203
|
+
if (arrowIdx === -1) {
|
|
1204
|
+
const keyword = _VTT_BLOCK_KEYWORDS.find((k) => lines[0] === k || lines[0].startsWith(`${k} `));
|
|
1205
|
+
if (sourceFormat === "vtt" && keyword) {
|
|
1206
|
+
warnings.push(`Dropped a WebVTT ${keyword} block, which has no SRT equivalent`);
|
|
1207
|
+
} else {
|
|
1208
|
+
warnings.push(`Skipped a block with no "-->" timing line: ${JSON.stringify(lines[0].slice(0, 40))}`);
|
|
1209
|
+
}
|
|
1210
|
+
continue;
|
|
1211
|
+
}
|
|
1212
|
+
const timingLine = lines[arrowIdx];
|
|
1213
|
+
const parts = timingLine.split("-->");
|
|
1214
|
+
const start = parseSubtitleTimestamp(parts[0] ?? "");
|
|
1215
|
+
const rightTrimmed = (parts[1] ?? "").trim();
|
|
1216
|
+
const end = parseSubtitleTimestamp(rightTrimmed);
|
|
1217
|
+
if (start === null || end === null) {
|
|
1218
|
+
warnings.push(`Skipped a cue with an unparseable timing line: ${JSON.stringify(timingLine.slice(0, 60))}`);
|
|
1219
|
+
continue;
|
|
1220
|
+
}
|
|
1221
|
+
const endMatch = _SUBTITLE_TIMESTAMP_RE.exec(rightTrimmed);
|
|
1222
|
+
const settings = endMatch ? rightTrimmed.slice(endMatch[0].length).trim() : "";
|
|
1223
|
+
const idLines = lines.slice(0, arrowIdx).filter((l) => l.trim().length > 0);
|
|
1224
|
+
const text = lines.slice(arrowIdx + 1).join("\n").trim();
|
|
1225
|
+
const cue = { index: cues.length + 1, start, end, text };
|
|
1226
|
+
if (sourceFormat === "vtt" && idLines.length > 0) cue.id = idLines[idLines.length - 1].trim();
|
|
1227
|
+
if (settings.length > 0) cue.settings = settings;
|
|
1228
|
+
if (end < start) warnings.push(`Cue ${cue.index} ends before it starts`);
|
|
1229
|
+
if (text.length === 0) warnings.push(`Cue ${cue.index} has no text`);
|
|
1230
|
+
if (cues.length > 0 && start < cues[cues.length - 1].end) {
|
|
1231
|
+
warnings.push(`Cue ${cue.index} starts before cue ${cue.index - 1} ends (overlapping cues)`);
|
|
1232
|
+
}
|
|
1233
|
+
cues.push(cue);
|
|
1234
|
+
}
|
|
1235
|
+
if (cues.length === 0) {
|
|
1236
|
+
return { error: "No subtitle cues found: check that the input is a valid SRT or WebVTT file" };
|
|
1237
|
+
}
|
|
1238
|
+
return {
|
|
1239
|
+
format: sourceFormat,
|
|
1240
|
+
cues,
|
|
1241
|
+
cueCount: cues.length,
|
|
1242
|
+
duration: Math.round(Math.max(...cues.map((c) => c.end)) * 1e3) / 1e3,
|
|
1243
|
+
warnings
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
function serializeSubtitles(cues, format) {
|
|
1247
|
+
const blocks = cues.map((cue, i) => {
|
|
1248
|
+
const time = `${formatSubtitleTimestamp(cue.start, format)} --> ${formatSubtitleTimestamp(cue.end, format)}`;
|
|
1249
|
+
if (format === "srt") return `${i + 1}
|
|
1250
|
+
${time}
|
|
1251
|
+
${cue.text}`;
|
|
1252
|
+
const timing = cue.settings ? `${time} ${cue.settings}` : time;
|
|
1253
|
+
return cue.id ? `${cue.id}
|
|
1254
|
+
${timing}
|
|
1255
|
+
${cue.text}` : `${timing}
|
|
1256
|
+
${cue.text}`;
|
|
1257
|
+
});
|
|
1258
|
+
const body = blocks.join("\n\n");
|
|
1259
|
+
return format === "vtt" ? `WEBVTT
|
|
1260
|
+
|
|
1261
|
+
${body}
|
|
1262
|
+
` : `${body}
|
|
1263
|
+
`;
|
|
1264
|
+
}
|
|
1265
|
+
function convertSubtitles(input, target, sourceFormat) {
|
|
1266
|
+
if (target !== "srt" && target !== "vtt") return { error: 'target must be "srt" or "vtt"' };
|
|
1267
|
+
const parsed = parseSubtitles(input, sourceFormat);
|
|
1268
|
+
if ("error" in parsed) return parsed;
|
|
1269
|
+
const warnings = [...parsed.warnings];
|
|
1270
|
+
if (target === "srt") {
|
|
1271
|
+
const droppedSettings = parsed.cues.filter((c) => c.settings).length;
|
|
1272
|
+
const droppedIds = parsed.cues.filter((c) => c.id).length;
|
|
1273
|
+
if (droppedSettings > 0) {
|
|
1274
|
+
warnings.push(`Dropped WebVTT cue settings on ${droppedSettings} cue(s): SRT has no equivalent`);
|
|
1275
|
+
}
|
|
1276
|
+
if (droppedIds > 0) {
|
|
1277
|
+
warnings.push(`Dropped ${droppedIds} WebVTT cue identifier(s): SRT numbers cues sequentially instead`);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
return {
|
|
1281
|
+
format: target,
|
|
1282
|
+
sourceFormat: parsed.format,
|
|
1283
|
+
output: serializeSubtitles(parsed.cues, target),
|
|
1284
|
+
cueCount: parsed.cueCount,
|
|
1285
|
+
duration: parsed.duration,
|
|
1286
|
+
warnings
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
function shiftSubtitles(input, offsetSeconds, target) {
|
|
1290
|
+
if (!Number.isFinite(offsetSeconds)) return { error: "offsetSeconds must be a finite number" };
|
|
1291
|
+
const parsed = parseSubtitles(input);
|
|
1292
|
+
if ("error" in parsed) return parsed;
|
|
1293
|
+
const warnings = [...parsed.warnings];
|
|
1294
|
+
let clamped = 0;
|
|
1295
|
+
const shifted = parsed.cues.map((cue) => {
|
|
1296
|
+
const start = cue.start + offsetSeconds;
|
|
1297
|
+
const end = cue.end + offsetSeconds;
|
|
1298
|
+
if (start < 0) clamped += 1;
|
|
1299
|
+
return { ...cue, start: Math.max(0, start), end: Math.max(0, end) };
|
|
1300
|
+
});
|
|
1301
|
+
if (clamped > 0) warnings.push(`${clamped} cue(s) would start before zero and were clamped to 00:00:00`);
|
|
1302
|
+
const outFormat = target ?? parsed.format;
|
|
1303
|
+
return {
|
|
1304
|
+
format: outFormat,
|
|
1305
|
+
sourceFormat: parsed.format,
|
|
1306
|
+
output: serializeSubtitles(shifted, outFormat),
|
|
1307
|
+
cueCount: shifted.length,
|
|
1308
|
+
duration: Math.round(Math.max(...shifted.map((c) => c.end)) * 1e3) / 1e3,
|
|
1309
|
+
warnings
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1142
1312
|
|
|
1313
|
+
exports.convertSubtitles = convertSubtitles;
|
|
1143
1314
|
exports.csvToJson = csvToJson;
|
|
1144
1315
|
exports.detectDelimiter = detectDelimiter;
|
|
1316
|
+
exports.detectSubtitleFormat = detectSubtitleFormat;
|
|
1145
1317
|
exports.diffEnvExample = diffEnvExample;
|
|
1146
1318
|
exports.envToExport = envToExport;
|
|
1147
1319
|
exports.envToJson = envToJson;
|
|
1148
1320
|
exports.formatNdjson = formatNdjson;
|
|
1321
|
+
exports.formatSubtitleTimestamp = formatSubtitleTimestamp;
|
|
1149
1322
|
exports.formatToml = formatToml;
|
|
1150
1323
|
exports.formatXml = formatXml;
|
|
1151
1324
|
exports.getSection = getSection;
|
|
@@ -1165,7 +1338,11 @@ exports.ndjsonToJsonArray = ndjsonToJsonArray;
|
|
|
1165
1338
|
exports.parseCsv = parseCsv;
|
|
1166
1339
|
exports.parseEnv = parseEnv;
|
|
1167
1340
|
exports.parseIni = parseIni;
|
|
1341
|
+
exports.parseSubtitleTimestamp = parseSubtitleTimestamp;
|
|
1342
|
+
exports.parseSubtitles = parseSubtitles;
|
|
1168
1343
|
exports.parseXmlToObject = parseXmlToObject;
|
|
1344
|
+
exports.serializeSubtitles = serializeSubtitles;
|
|
1345
|
+
exports.shiftSubtitles = shiftSubtitles;
|
|
1169
1346
|
exports.stringifyIni = stringifyIni;
|
|
1170
1347
|
exports.toCsvString = toCsvString;
|
|
1171
1348
|
exports.tomlToJson = tomlToJson;
|
package/dist/tools/data.d.cts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { C as CsvParseResult, E as EnvDiffError,
|
|
1
|
+
export { C as ConvertedSubtitles, a as CsvParseResult, E as EnvDiffError, b as EnvDiffResult, c as EnvEntry, e as EnvParseResult, I as IniSection, N as NdjsonLineResult, f as NdjsonValidateResult, P as ParseOptions, g as ParsedIni, h as ParsedSubtitles, S as SubtitleCue, i as SubtitleFormat, T as TomlFormatResult, V as ValidationResult, X as XmlFormatResult, j as XmlToJsonOptions, k as convertSubtitles, l as csvToJson, m as detectDelimiter, n as detectSubtitleFormat, o as diffEnvExample, p as envToExport, q as envToJson, r as formatNdjson, s as formatSubtitleTimestamp, t as formatToml, u as formatXml, v as getSection, w as getSectionNames, x as getTomlStats, y as getXmlStats, z as getYamlStats, A as iniToJson, B as jsonArrayToNdjson, D as jsonToIni, F as jsonToToml, G as jsonToXml, H as minifyNdjson, J as minifyToml, K as minifyXml, L as ndjsonToJsonArray, M as parseCsv, O as parseEnv, Q as parseIni, R as parseSubtitleTimestamp, U as parseSubtitles, W as parseXmlToObject, Y as serializeSubtitles, Z as shiftSubtitles, _ as stringifyIni, $ as toCsvString, a0 as tomlToJson, a1 as validateEnvKey, a2 as validateIni, a3 as validateNdjson, a4 as validateToml, a5 as validateXml, a6 as validateYaml, a7 as xmlToJson } from '../data-XRb0D7Zn.cjs';
|
package/dist/tools/data.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { C as CsvParseResult, E as EnvDiffError,
|
|
1
|
+
export { C as ConvertedSubtitles, a as CsvParseResult, E as EnvDiffError, b as EnvDiffResult, c as EnvEntry, e as EnvParseResult, I as IniSection, N as NdjsonLineResult, f as NdjsonValidateResult, P as ParseOptions, g as ParsedIni, h as ParsedSubtitles, S as SubtitleCue, i as SubtitleFormat, T as TomlFormatResult, V as ValidationResult, X as XmlFormatResult, j as XmlToJsonOptions, k as convertSubtitles, l as csvToJson, m as detectDelimiter, n as detectSubtitleFormat, o as diffEnvExample, p as envToExport, q as envToJson, r as formatNdjson, s as formatSubtitleTimestamp, t as formatToml, u as formatXml, v as getSection, w as getSectionNames, x as getTomlStats, y as getXmlStats, z as getYamlStats, A as iniToJson, B as jsonArrayToNdjson, D as jsonToIni, F as jsonToToml, G as jsonToXml, H as minifyNdjson, J as minifyToml, K as minifyXml, L as ndjsonToJsonArray, M as parseCsv, O as parseEnv, Q as parseIni, R as parseSubtitleTimestamp, U as parseSubtitles, W as parseXmlToObject, Y as serializeSubtitles, Z as shiftSubtitles, _ as stringifyIni, $ as toCsvString, a0 as tomlToJson, a1 as validateEnvKey, a2 as validateIni, a3 as validateNdjson, a4 as validateToml, a5 as validateXml, a6 as validateYaml, a7 as xmlToJson } from '../data-XRb0D7Zn.js';
|
package/dist/tools/data.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { csvToJson, detectDelimiter, diffEnvExample, envToExport, envToJson, formatNdjson, formatToml, formatXml, getSection, getSectionNames, getTomlStats, getXmlStats, getYamlStats, iniToJson, jsonArrayToNdjson, jsonToIni, jsonToToml, jsonToXml, minifyNdjson, minifyToml, minifyXml, ndjsonToJsonArray, parseCsv, parseEnv, parseIni, parseXmlToObject, stringifyIni, toCsvString, tomlToJson, validateEnvKey, validateIni, validateNdjson, validateToml, validateXml, validateYaml, xmlToJson } from '../chunk-
|
|
1
|
+
export { convertSubtitles, csvToJson, detectDelimiter, detectSubtitleFormat, diffEnvExample, envToExport, envToJson, formatNdjson, formatSubtitleTimestamp, formatToml, formatXml, getSection, getSectionNames, getTomlStats, getXmlStats, getYamlStats, iniToJson, jsonArrayToNdjson, jsonToIni, jsonToToml, jsonToXml, minifyNdjson, minifyToml, minifyXml, ndjsonToJsonArray, parseCsv, parseEnv, parseIni, parseSubtitleTimestamp, parseSubtitles, parseXmlToObject, serializeSubtitles, shiftSubtitles, stringifyIni, toCsvString, tomlToJson, validateEnvKey, validateIni, validateNdjson, validateToml, validateXml, validateYaml, xmlToJson } from '../chunk-66QHV5KW.js';
|
|
2
2
|
import '../chunk-MLKGABMK.js';
|