@particle-academy/holy-sheet 2.10.1 → 2.10.2
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 +23 -0
- package/README.md +7 -0
- package/dist/index.cjs +299 -98
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -4
- package/dist/index.d.ts +18 -4
- package/dist/index.js +299 -99
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -240,12 +240,12 @@ function parseRows(csv, delimiter, quote2) {
|
|
|
240
240
|
// src/workbook/cell-address.ts
|
|
241
241
|
var CellAddress = {
|
|
242
242
|
/** 0-based column index → Excel-style letters. */
|
|
243
|
-
letter(
|
|
244
|
-
if (
|
|
245
|
-
throw new Error(`[holy-sheet] column index must be \u2265 0, got ${
|
|
243
|
+
letter(index2) {
|
|
244
|
+
if (index2 < 0) {
|
|
245
|
+
throw new Error(`[holy-sheet] column index must be \u2265 0, got ${index2}`);
|
|
246
246
|
}
|
|
247
247
|
let letters = "";
|
|
248
|
-
let n =
|
|
248
|
+
let n = index2;
|
|
249
249
|
do {
|
|
250
250
|
letters = String.fromCharCode(65 + n % 26) + letters;
|
|
251
251
|
n = Math.floor(n / 26) - 1;
|
|
@@ -298,8 +298,8 @@ var ColumnWidths = {
|
|
|
298
298
|
return Number.isInteger(key) && key >= 0 && key <= ColumnWidths.MAX_INDEX ? key : null;
|
|
299
299
|
}
|
|
300
300
|
if (typeof key === "string" && /^[0-9]{1,5}$/.test(key)) {
|
|
301
|
-
const
|
|
302
|
-
return
|
|
301
|
+
const index2 = parseInt(key, 10);
|
|
302
|
+
return index2 <= ColumnWidths.MAX_INDEX ? index2 : null;
|
|
303
303
|
}
|
|
304
304
|
return null;
|
|
305
305
|
},
|
|
@@ -558,6 +558,14 @@ var DateConverter = {
|
|
|
558
558
|
};
|
|
559
559
|
|
|
560
560
|
// src/exceptions.ts
|
|
561
|
+
var ArchiveRefused = class _ArchiveRefused extends Error {
|
|
562
|
+
constructor(reason) {
|
|
563
|
+
super(`Source archive refused: ${reason}`);
|
|
564
|
+
this.reason = reason;
|
|
565
|
+
this.name = "ArchiveRefused";
|
|
566
|
+
Object.setPrototypeOf(this, _ArchiveRefused.prototype);
|
|
567
|
+
}
|
|
568
|
+
};
|
|
561
569
|
var SchemaException = class _SchemaException extends Error {
|
|
562
570
|
constructor(errors, message) {
|
|
563
571
|
super(message ?? summarize(errors));
|
|
@@ -798,23 +806,23 @@ var Normalizer = class {
|
|
|
798
806
|
for (const [colIdx, columnDef] of (sheet.columns ?? []).entries()) {
|
|
799
807
|
if (columnDef === null || typeof columnDef !== "object") continue;
|
|
800
808
|
if (columnDef.width === void 0) continue;
|
|
801
|
-
const
|
|
809
|
+
const index2 = ColumnWidths.index(colIdx);
|
|
802
810
|
const width = ColumnWidths.width(columnDef.width);
|
|
803
|
-
if (
|
|
804
|
-
out.set(
|
|
811
|
+
if (index2 === null || width === null) continue;
|
|
812
|
+
out.set(index2, width);
|
|
805
813
|
}
|
|
806
|
-
for (const [
|
|
807
|
-
out.set(
|
|
814
|
+
for (const [index2, width] of this.normalizeColumnWidths(sheet.columnWidths ?? {})) {
|
|
815
|
+
out.set(index2, width);
|
|
808
816
|
}
|
|
809
817
|
return new Map([...out.entries()].sort((a, b) => a[0] - b[0]));
|
|
810
818
|
}
|
|
811
819
|
normalizeColumnWidths(widths) {
|
|
812
820
|
const out = /* @__PURE__ */ new Map();
|
|
813
821
|
for (const [key, px] of Object.entries(widths)) {
|
|
814
|
-
const
|
|
822
|
+
const index2 = ColumnWidths.index(key);
|
|
815
823
|
const width = ColumnWidths.width(px);
|
|
816
|
-
if (
|
|
817
|
-
out.set(
|
|
824
|
+
if (index2 === null || width === null) continue;
|
|
825
|
+
out.set(index2, width);
|
|
818
826
|
}
|
|
819
827
|
return out;
|
|
820
828
|
}
|
|
@@ -926,14 +934,14 @@ function normalizePoints(points2, count) {
|
|
|
926
934
|
const byIndex = /* @__PURE__ */ new Map();
|
|
927
935
|
for (const p of Object.values(points2)) {
|
|
928
936
|
const rec = isPlainObject(p) || Array.isArray(p) ? p : null;
|
|
929
|
-
const
|
|
937
|
+
const index2 = rec ? rec.index : void 0;
|
|
930
938
|
const color2 = rec ? rec.color : void 0;
|
|
931
939
|
const m = typeof color2 === "string" ? /^#([0-9A-Fa-f]{6})$/.exec(color2) : null;
|
|
932
|
-
if (typeof
|
|
933
|
-
byIndex.set(
|
|
940
|
+
if (typeof index2 === "number" && Number.isInteger(index2) && index2 >= 0 && index2 < count && m) {
|
|
941
|
+
byIndex.set(index2, m[1].toUpperCase());
|
|
934
942
|
}
|
|
935
943
|
}
|
|
936
|
-
return [...byIndex.entries()].sort((a, b) => a[0] - b[0]).map(([
|
|
944
|
+
return [...byIndex.entries()].sort((a, b) => a[0] - b[0]).map(([index2, color2]) => ({ index: index2, color: color2 }));
|
|
937
945
|
}
|
|
938
946
|
var CHART_TYPES = ["column", "bar", "line", "pie"];
|
|
939
947
|
function normalizeCharts(charts, sheetName2, cells) {
|
|
@@ -1067,13 +1075,13 @@ var FormulaLinter = class {
|
|
|
1067
1075
|
return issues;
|
|
1068
1076
|
}
|
|
1069
1077
|
buildIndex(wb) {
|
|
1070
|
-
const
|
|
1078
|
+
const index2 = /* @__PURE__ */ new Map();
|
|
1071
1079
|
for (const sheet of wb.sheets) {
|
|
1072
1080
|
for (const [address, cell] of sheet.cells) {
|
|
1073
|
-
|
|
1081
|
+
index2.set(sheet.name + "!" + address, cell);
|
|
1074
1082
|
}
|
|
1075
1083
|
}
|
|
1076
|
-
return
|
|
1084
|
+
return index2;
|
|
1077
1085
|
}
|
|
1078
1086
|
isError(v) {
|
|
1079
1087
|
return typeof v === "string" && v.length > 0 && v[0] === "#";
|
|
@@ -1587,21 +1595,21 @@ var Repairer = class {
|
|
|
1587
1595
|
const widths = {};
|
|
1588
1596
|
let changed = false;
|
|
1589
1597
|
for (const [key, px] of Object.entries(sheet.columnWidths)) {
|
|
1590
|
-
let
|
|
1591
|
-
if (
|
|
1598
|
+
let index2 = ColumnWidths.index(key);
|
|
1599
|
+
if (index2 === null) {
|
|
1592
1600
|
const letters = ColumnWidths.fromLetters(key);
|
|
1593
1601
|
if (letters !== null) {
|
|
1594
|
-
|
|
1595
|
-
this.repairs.push(`converted '${path}.columnWidths.${key}' to column index ${
|
|
1602
|
+
index2 = letters;
|
|
1603
|
+
this.repairs.push(`converted '${path}.columnWidths.${key}' to column index ${index2}`);
|
|
1596
1604
|
changed = true;
|
|
1597
1605
|
}
|
|
1598
1606
|
}
|
|
1599
|
-
if (
|
|
1607
|
+
if (index2 === null || ColumnWidths.width(px) === null) {
|
|
1600
1608
|
this.repairs.push(`dropped '${path}.columnWidths.${key}' (not a column index and a width)`);
|
|
1601
1609
|
changed = true;
|
|
1602
1610
|
continue;
|
|
1603
1611
|
}
|
|
1604
|
-
widths[String(
|
|
1612
|
+
widths[String(index2)] = px;
|
|
1605
1613
|
}
|
|
1606
1614
|
if (changed) {
|
|
1607
1615
|
sheet.columnWidths = widths;
|
|
@@ -2025,14 +2033,14 @@ function validatePoints(points2, at2, values2) {
|
|
|
2025
2033
|
const errors = [];
|
|
2026
2034
|
points2.forEach((p, k) => {
|
|
2027
2035
|
const rec = isPlainObject(p) || Array.isArray(p) ? p : null;
|
|
2028
|
-
const
|
|
2036
|
+
const index2 = rec ? rec.index ?? null : null;
|
|
2029
2037
|
const color2 = rec ? rec.color ?? null : null;
|
|
2030
|
-
if (typeof
|
|
2038
|
+
if (typeof index2 !== "number" || !Number.isInteger(index2) || index2 < 0 || index2 >= count) {
|
|
2031
2039
|
errors.push(error(
|
|
2032
2040
|
`${at2}[${k}].index`,
|
|
2033
2041
|
"an index within values (0 to " + Math.max(0, count - 1) + ")",
|
|
2034
|
-
typeOf(
|
|
2035
|
-
|
|
2042
|
+
typeOf(index2),
|
|
2043
|
+
index2,
|
|
2036
2044
|
'The 0-based position of the point in its values range: "B5:B7" has points 0, 1 and 2.'
|
|
2037
2045
|
));
|
|
2038
2046
|
}
|
|
@@ -2427,8 +2435,17 @@ function buildTree(t, lengths, off, num) {
|
|
|
2427
2435
|
let i;
|
|
2428
2436
|
let sum = 0;
|
|
2429
2437
|
for (i = 0; i < 16; i++) t.table[i] = 0;
|
|
2430
|
-
for (i = 0; i < num; i++)
|
|
2438
|
+
for (i = 0; i < num; i++) {
|
|
2439
|
+
const length = lengths[off + i];
|
|
2440
|
+
if (length === void 0 || length > 15) throw new Error("holy-sheet: invalid DEFLATE tree");
|
|
2441
|
+
t.table[length]++;
|
|
2442
|
+
}
|
|
2431
2443
|
t.table[0] = 0;
|
|
2444
|
+
let slots = 1;
|
|
2445
|
+
for (i = 1; i <= 15; i++) {
|
|
2446
|
+
slots = slots * 2 - t.table[i];
|
|
2447
|
+
if (slots < 0) throw new Error("holy-sheet: oversubscribed DEFLATE tree");
|
|
2448
|
+
}
|
|
2432
2449
|
for (i = 0; i < 16; i++) {
|
|
2433
2450
|
OFFS[i] = sum;
|
|
2434
2451
|
sum += t.table[i];
|
|
@@ -2448,6 +2465,7 @@ var Reader = class {
|
|
|
2448
2465
|
};
|
|
2449
2466
|
function getBit(d) {
|
|
2450
2467
|
if (d.bitcount-- === 0) {
|
|
2468
|
+
if (d.index >= d.source.length) throw new Error("holy-sheet: truncated DEFLATE input");
|
|
2451
2469
|
d.tag = d.source[d.index++];
|
|
2452
2470
|
d.bitcount = 7;
|
|
2453
2471
|
}
|
|
@@ -2468,6 +2486,7 @@ function decodeSymbol(d, t) {
|
|
|
2468
2486
|
do {
|
|
2469
2487
|
cur = 2 * cur + getBit(d);
|
|
2470
2488
|
len++;
|
|
2489
|
+
if (len > 15) throw new Error("holy-sheet: invalid DEFLATE code");
|
|
2471
2490
|
sum += t.table[len];
|
|
2472
2491
|
cur -= t.table[len];
|
|
2473
2492
|
} while (cur >= 0);
|
|
@@ -2478,6 +2497,7 @@ function decodeTrees(d, lt, dt) {
|
|
|
2478
2497
|
const hlit = readBits(d, 5, 257);
|
|
2479
2498
|
const hdist = readBits(d, 5, 1);
|
|
2480
2499
|
const hclen = readBits(d, 4, 4);
|
|
2500
|
+
if (hlit > 286) throw new Error("holy-sheet: invalid DEFLATE literal count");
|
|
2481
2501
|
let i;
|
|
2482
2502
|
for (i = 0; i < 19; i++) lengths[i] = 0;
|
|
2483
2503
|
for (i = 0; i < hclen; i++) {
|
|
@@ -2490,32 +2510,44 @@ function decodeTrees(d, lt, dt) {
|
|
|
2490
2510
|
const sym = decodeSymbol(d, codeTree);
|
|
2491
2511
|
switch (sym) {
|
|
2492
2512
|
case 16: {
|
|
2513
|
+
if (num === 0) throw new Error("holy-sheet: invalid DEFLATE repeat");
|
|
2493
2514
|
const prev = lengths[num - 1];
|
|
2494
|
-
|
|
2515
|
+
const repeat = readBits(d, 2, 3);
|
|
2516
|
+
if (num + repeat > hlit + hdist) throw new Error("holy-sheet: invalid DEFLATE repeat");
|
|
2517
|
+
for (let length = repeat; length; length--) lengths[num++] = prev;
|
|
2495
2518
|
break;
|
|
2496
2519
|
}
|
|
2497
|
-
case 17:
|
|
2498
|
-
|
|
2520
|
+
case 17: {
|
|
2521
|
+
const repeat = readBits(d, 3, 3);
|
|
2522
|
+
if (num + repeat > hlit + hdist) throw new Error("holy-sheet: invalid DEFLATE repeat");
|
|
2523
|
+
for (let length = repeat; length; length--) lengths[num++] = 0;
|
|
2499
2524
|
break;
|
|
2500
|
-
|
|
2501
|
-
|
|
2525
|
+
}
|
|
2526
|
+
case 18: {
|
|
2527
|
+
const repeat = readBits(d, 7, 11);
|
|
2528
|
+
if (num + repeat > hlit + hdist) throw new Error("holy-sheet: invalid DEFLATE repeat");
|
|
2529
|
+
for (let length = repeat; length; length--) lengths[num++] = 0;
|
|
2502
2530
|
break;
|
|
2531
|
+
}
|
|
2503
2532
|
default:
|
|
2504
2533
|
lengths[num++] = sym;
|
|
2505
2534
|
break;
|
|
2506
2535
|
}
|
|
2507
2536
|
}
|
|
2537
|
+
if (lengths[256] === 0) throw new Error("holy-sheet: missing DEFLATE end code");
|
|
2508
2538
|
buildTree(lt, lengths, 0, hlit);
|
|
2509
2539
|
buildTree(dt, lengths, hlit, hdist);
|
|
2510
2540
|
}
|
|
2511
2541
|
var Out = class {
|
|
2512
|
-
constructor() {
|
|
2513
|
-
this.
|
|
2542
|
+
constructor(limit) {
|
|
2543
|
+
this.limit = limit;
|
|
2514
2544
|
this.len = 0;
|
|
2545
|
+
this.buf = new Uint8Array(Math.min(1024, limit));
|
|
2515
2546
|
}
|
|
2516
2547
|
push(b) {
|
|
2548
|
+
if (this.len >= this.limit) throw new Error("holy-sheet: DEFLATE output limit exceeded");
|
|
2517
2549
|
if (this.len >= this.buf.length) {
|
|
2518
|
-
const next = new Uint8Array(this.buf.length * 2);
|
|
2550
|
+
const next = new Uint8Array(Math.min(this.limit, Math.max(1, this.buf.length * 2)));
|
|
2519
2551
|
next.set(this.buf);
|
|
2520
2552
|
this.buf = next;
|
|
2521
2553
|
}
|
|
@@ -2530,9 +2562,12 @@ function inflateBlockData(d, out, lt, dt) {
|
|
|
2530
2562
|
out.push(sym);
|
|
2531
2563
|
} else {
|
|
2532
2564
|
const lengthSym = sym - 257;
|
|
2565
|
+
if (lengthSym < 0 || lengthSym >= LENGTH_BITS.length) throw new Error("holy-sheet: invalid DEFLATE length");
|
|
2533
2566
|
const length = readBits(d, LENGTH_BITS[lengthSym], LENGTH_BASE[lengthSym]);
|
|
2534
2567
|
const distSym = decodeSymbol(d, dt);
|
|
2568
|
+
if (distSym < 0 || distSym >= DIST_BITS.length) throw new Error("holy-sheet: invalid DEFLATE distance");
|
|
2535
2569
|
const dist = readBits(d, DIST_BITS[distSym], DIST_BASE[distSym]);
|
|
2570
|
+
if (dist > out.len) throw new Error("holy-sheet: DEFLATE distance before output");
|
|
2536
2571
|
const offs = out.len - dist;
|
|
2537
2572
|
for (let i = 0; i < length; i++) out.push(out.buf[offs + i]);
|
|
2538
2573
|
}
|
|
@@ -2540,13 +2575,20 @@ function inflateBlockData(d, out, lt, dt) {
|
|
|
2540
2575
|
}
|
|
2541
2576
|
function inflateUncompressedBlock(d, out) {
|
|
2542
2577
|
d.bitcount = 0;
|
|
2578
|
+
if (d.index + 4 > d.source.length) throw new Error("holy-sheet: truncated DEFLATE input");
|
|
2543
2579
|
let length = d.source[d.index + 1] * 256 + d.source[d.index];
|
|
2580
|
+
const complement = d.source[d.index + 3] * 256 + d.source[d.index + 2];
|
|
2581
|
+
if ((length ^ complement) !== 65535) throw new Error("holy-sheet: invalid DEFLATE stored length");
|
|
2544
2582
|
d.index += 4;
|
|
2583
|
+
if (d.index + length > d.source.length) throw new Error("holy-sheet: truncated DEFLATE input");
|
|
2545
2584
|
for (; length; length--) out.push(d.source[d.index++]);
|
|
2546
2585
|
}
|
|
2547
|
-
function inflateRaw(source) {
|
|
2586
|
+
function inflateRaw(source, maxOutputBytes = Number.POSITIVE_INFINITY) {
|
|
2587
|
+
if (maxOutputBytes !== Number.POSITIVE_INFINITY && (!Number.isSafeInteger(maxOutputBytes) || maxOutputBytes < 0)) {
|
|
2588
|
+
throw new Error("holy-sheet: invalid DEFLATE output limit");
|
|
2589
|
+
}
|
|
2548
2590
|
const d = new Reader(source);
|
|
2549
|
-
const out = new Out();
|
|
2591
|
+
const out = new Out(maxOutputBytes);
|
|
2550
2592
|
const lt = new Tree();
|
|
2551
2593
|
const dt = new Tree();
|
|
2552
2594
|
let bfinal;
|
|
@@ -2568,41 +2610,166 @@ function inflateRaw(source) {
|
|
|
2568
2610
|
return out.buf.subarray(0, out.len);
|
|
2569
2611
|
}
|
|
2570
2612
|
|
|
2571
|
-
// src/zip/
|
|
2572
|
-
var
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2613
|
+
// src/zip/source-archive.ts
|
|
2614
|
+
var DEFAULT_ARCHIVE_LIMITS = Object.freeze({
|
|
2615
|
+
maxArchiveBytes: 67108864,
|
|
2616
|
+
maxEntries: 16384,
|
|
2617
|
+
maxPartBytes: 67108864,
|
|
2618
|
+
maxExpandedBytes: 268435456,
|
|
2619
|
+
maxCompressionRatio: 1e3
|
|
2620
|
+
});
|
|
2621
|
+
var corrupt = () => {
|
|
2622
|
+
throw new ArchiveRefused("archive_corrupt");
|
|
2623
|
+
};
|
|
2624
|
+
var SourceArchive = class _SourceArchive {
|
|
2625
|
+
constructor(original, parts) {
|
|
2626
|
+
this.original = original;
|
|
2627
|
+
this.parts = parts;
|
|
2628
|
+
}
|
|
2629
|
+
static open(input, options = {}) {
|
|
2630
|
+
for (const [key, value] of Object.entries(options)) {
|
|
2631
|
+
if (!Object.prototype.hasOwnProperty.call(DEFAULT_ARCHIVE_LIMITS, key) || !Number.isSafeInteger(value) || value < 1) throw new ArchiveRefused("archive_limits");
|
|
2632
|
+
}
|
|
2633
|
+
const limits = { ...DEFAULT_ARCHIVE_LIMITS, ...options };
|
|
2634
|
+
if (input.length > limits.maxArchiveBytes) throw new ArchiveRefused("archive_limit");
|
|
2635
|
+
const bytes = input.slice();
|
|
2636
|
+
const records = index(bytes, limits);
|
|
2637
|
+
const parts = /* @__PURE__ */ new Map();
|
|
2638
|
+
for (const record of records) {
|
|
2639
|
+
const compressed = bytes.subarray(record.start, record.start + record.compressed);
|
|
2640
|
+
let payload;
|
|
2641
|
+
try {
|
|
2642
|
+
payload = record.method === 0 ? compressed.slice() : inflateRaw(compressed, record.size + 1);
|
|
2643
|
+
} catch {
|
|
2644
|
+
throw new ArchiveRefused("archive_corrupt");
|
|
2645
|
+
}
|
|
2646
|
+
if (payload.length !== record.size || crc32(payload) !== record.crc) corrupt();
|
|
2647
|
+
parts.set(record.name, payload);
|
|
2648
|
+
}
|
|
2649
|
+
return new _SourceArchive(bytes, parts);
|
|
2650
|
+
}
|
|
2651
|
+
names() {
|
|
2652
|
+
return [...this.parts.keys()];
|
|
2653
|
+
}
|
|
2654
|
+
read(name) {
|
|
2655
|
+
return this.parts.get(name)?.slice() ?? null;
|
|
2656
|
+
}
|
|
2657
|
+
toBytes() {
|
|
2658
|
+
return this.original.slice();
|
|
2659
|
+
}
|
|
2660
|
+
};
|
|
2661
|
+
function index(bytes, limits) {
|
|
2662
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
2663
|
+
const bounds = (at3, size, end2 = bytes.length) => {
|
|
2664
|
+
if (at3 < 0 || size < 0 || at3 + size > end2 || at3 + size > bytes.length) corrupt();
|
|
2665
|
+
};
|
|
2666
|
+
const u16 = (at3) => {
|
|
2667
|
+
bounds(at3, 2);
|
|
2668
|
+
return view.getUint16(at3, true);
|
|
2669
|
+
};
|
|
2670
|
+
const u32 = (at3) => {
|
|
2671
|
+
bounds(at3, 4);
|
|
2672
|
+
return view.getUint32(at3, true);
|
|
2673
|
+
};
|
|
2674
|
+
let end = -1;
|
|
2675
|
+
for (let at3 = bytes.length - 22; at3 >= Math.max(0, bytes.length - 65557); at3--) {
|
|
2676
|
+
if (u32(at3) === 101010256 && at3 + 22 + u16(at3 + 20) === bytes.length && u32(at3 + 16) + u32(at3 + 12) === at3) {
|
|
2677
|
+
end = at3;
|
|
2579
2678
|
break;
|
|
2580
2679
|
}
|
|
2581
2680
|
}
|
|
2582
|
-
if (
|
|
2583
|
-
const count =
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
const
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2681
|
+
if (end === -1) corrupt();
|
|
2682
|
+
const count = u16(end + 10), directory = u32(end + 16);
|
|
2683
|
+
if (u16(end + 4) !== 0 || u16(end + 6) !== 0 || u16(end + 8) !== count || count === 65535) throw new ArchiveRefused("archive_unsupported");
|
|
2684
|
+
if (count > limits.maxEntries) throw new ArchiveRefused("archive_limit");
|
|
2685
|
+
if (end - directory < count * 46) corrupt();
|
|
2686
|
+
const records = [], seen = /* @__PURE__ */ new Set(), regions = [];
|
|
2687
|
+
let at2 = directory, expanded = 0;
|
|
2688
|
+
const extra = (start, length) => {
|
|
2689
|
+
const finish = start + length;
|
|
2690
|
+
while (start < finish) {
|
|
2691
|
+
bounds(start, 4, finish);
|
|
2692
|
+
const id = u16(start), size = u16(start + 2);
|
|
2693
|
+
bounds(start + 4, size, finish);
|
|
2694
|
+
if (id === 1 || id === 39169) throw new ArchiveRefused("archive_unsupported");
|
|
2695
|
+
start += 4 + size;
|
|
2696
|
+
}
|
|
2697
|
+
};
|
|
2698
|
+
for (let i = 0; i < count; i++) {
|
|
2699
|
+
bounds(at2, 46, end);
|
|
2700
|
+
if (u32(at2) !== 33639248) corrupt();
|
|
2701
|
+
const flags = u16(at2 + 8), method = u16(at2 + 10), crc = u32(at2 + 16);
|
|
2702
|
+
const compressed = u32(at2 + 20), size = u32(at2 + 24), local = u32(at2 + 42);
|
|
2703
|
+
const nameLength = u16(at2 + 28), extraLength = u16(at2 + 30), commentLength = u16(at2 + 32);
|
|
2704
|
+
if ((flags & -2063) !== 0 || ![0, 8].includes(method) || method === 0 && (flags & 6) !== 0 || u16(at2 + 34) !== 0 || [compressed, size, local].includes(4294967295) || u16(at2 + 6) > 20) throw new ArchiveRefused("archive_unsupported");
|
|
2705
|
+
bounds(at2 + 46, nameLength + extraLength + commentLength, end);
|
|
2706
|
+
const name = partName(bytes.subarray(at2 + 46, at2 + 46 + nameLength), flags);
|
|
2707
|
+
if (seen.has(name)) throw new ArchiveRefused("archive_duplicate");
|
|
2708
|
+
seen.add(name);
|
|
2709
|
+
expanded += size;
|
|
2710
|
+
if (size > limits.maxPartBytes || expanded > limits.maxExpandedBytes || size > Math.max(1, compressed) * limits.maxCompressionRatio) throw new ArchiveRefused("archive_limit");
|
|
2711
|
+
if (method === 0 && compressed !== size) corrupt();
|
|
2712
|
+
extra(at2 + 46 + nameLength, extraLength);
|
|
2713
|
+
bounds(local, 30, directory);
|
|
2714
|
+
if (u32(local) !== 67324752 || u16(local + 4) !== u16(at2 + 6) || u16(local + 6) !== flags || u16(local + 8) !== method) corrupt();
|
|
2715
|
+
const localName2 = u16(local + 26), localExtra = u16(local + 28);
|
|
2716
|
+
const start = local + 30 + localName2 + localExtra;
|
|
2717
|
+
bounds(local + 30, localName2 + localExtra + compressed, directory);
|
|
2718
|
+
const rawName = bytes.subarray(local + 30, local + 30 + localName2);
|
|
2719
|
+
const centralName = bytes.subarray(at2 + 46, at2 + 46 + nameLength);
|
|
2720
|
+
if (rawName.length !== centralName.length || !rawName.every((byte, pos) => byte === centralName[pos])) corrupt();
|
|
2721
|
+
extra(local + 30 + localName2, localExtra);
|
|
2722
|
+
for (const [offset, expected] of [[14, crc], [18, compressed], [22, size]]) {
|
|
2723
|
+
const value = u32(local + offset);
|
|
2724
|
+
if (value !== expected && !((flags & 8) !== 0 && value === 0)) corrupt();
|
|
2725
|
+
}
|
|
2726
|
+
let finish = start + compressed;
|
|
2727
|
+
if ((flags & 8) !== 0) {
|
|
2728
|
+
let found = false;
|
|
2729
|
+
for (const prefix of [0, 4]) {
|
|
2730
|
+
if (finish + prefix + 12 <= directory && (prefix === 0 || u32(finish) === 134695760) && u32(finish + prefix) === crc && u32(finish + prefix + 4) === compressed && u32(finish + prefix + 8) === size) {
|
|
2731
|
+
finish += prefix + 12;
|
|
2732
|
+
found = true;
|
|
2733
|
+
break;
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
if (!found) corrupt();
|
|
2737
|
+
}
|
|
2738
|
+
records.push({ name, method, crc, size, compressed, start });
|
|
2739
|
+
regions.push([local, finish]);
|
|
2740
|
+
at2 += 46 + nameLength + extraLength + commentLength;
|
|
2741
|
+
}
|
|
2742
|
+
if (at2 !== end) corrupt();
|
|
2743
|
+
regions.sort((a, b) => a[0] - b[0]);
|
|
2744
|
+
let previous = 0;
|
|
2745
|
+
for (const [start, finish] of regions) {
|
|
2746
|
+
if (start < previous) corrupt();
|
|
2747
|
+
previous = finish;
|
|
2748
|
+
}
|
|
2749
|
+
return records;
|
|
2750
|
+
}
|
|
2751
|
+
function partName(bytes, flags) {
|
|
2752
|
+
let name;
|
|
2753
|
+
try {
|
|
2754
|
+
name = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
2755
|
+
} catch {
|
|
2756
|
+
throw new ArchiveRefused("archive_path");
|
|
2757
|
+
}
|
|
2758
|
+
if (name === "" || name.startsWith("/") || name.includes(String.fromCharCode(92)) || name.includes(":")) throw new ArchiveRefused("archive_path");
|
|
2759
|
+
for (const byte of bytes) {
|
|
2760
|
+
if (byte < 32 || byte === 127) throw new ArchiveRefused("archive_path");
|
|
2761
|
+
if (byte > 127 && (flags & 2048) === 0) throw new ArchiveRefused("archive_unsupported");
|
|
2605
2762
|
}
|
|
2763
|
+
const parts = (name.endsWith("/") ? name.slice(0, -1) : name).split("/");
|
|
2764
|
+
if (parts.some((part2) => part2 === "" || part2 === "." || part2 === "..")) throw new ArchiveRefused("archive_path");
|
|
2765
|
+
return name;
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2768
|
+
// src/zip/zip-reader.ts
|
|
2769
|
+
function unzipSync(data, limits = {}) {
|
|
2770
|
+
const source = SourceArchive.open(data, limits);
|
|
2771
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
2772
|
+
for (const name of source.names()) result[name] = source.read(name);
|
|
2606
2773
|
return result;
|
|
2607
2774
|
}
|
|
2608
2775
|
|
|
@@ -2650,23 +2817,56 @@ var ODS_MIMETYPES = /* @__PURE__ */ new Set([
|
|
|
2650
2817
|
"application/vnd.oasis.opendocument.spreadsheet",
|
|
2651
2818
|
"application/vnd.oasis.opendocument.spreadsheet-template"
|
|
2652
2819
|
]);
|
|
2653
|
-
var
|
|
2820
|
+
var decoder = new TextDecoder();
|
|
2654
2821
|
var FormatSniffer = {
|
|
2655
2822
|
sniff(bytes, path = null) {
|
|
2656
2823
|
let files;
|
|
2657
2824
|
try {
|
|
2658
2825
|
files = unzipSync(bytes);
|
|
2659
2826
|
} catch (e) {
|
|
2827
|
+
if (e instanceof ArchiveRefused && (e.reason !== "archive_corrupt" || bytes[0] === 80 && bytes[1] === 75)) throw e;
|
|
2660
2828
|
if (e instanceof Error && e.message.includes("unsupported zip method")) throw e;
|
|
2661
2829
|
throw UnsupportedFormatException.notAZip(path);
|
|
2662
2830
|
}
|
|
2663
|
-
const mimetype = files["mimetype"] ? phpTrim(
|
|
2831
|
+
const mimetype = files["mimetype"] ? phpTrim(decoder.decode(files["mimetype"])) : null;
|
|
2664
2832
|
if (mimetype !== null && ODS_MIMETYPES.has(mimetype)) return { format: "ods", files };
|
|
2665
2833
|
if (files["xl/workbook.xml"]) return { format: "xlsx", files };
|
|
2666
2834
|
throw UnsupportedFormatException.unknownPackage(path, mimetype);
|
|
2667
2835
|
}
|
|
2668
2836
|
};
|
|
2669
2837
|
|
|
2838
|
+
// src/reader/file-input.ts
|
|
2839
|
+
async function readFileBounded(path) {
|
|
2840
|
+
const fs = await import('fs');
|
|
2841
|
+
if (!fs.statSync(path).isFile()) throw new ArchiveRefused("archive_path");
|
|
2842
|
+
const fd = fs.openSync(path, "r");
|
|
2843
|
+
const limit = DEFAULT_ARCHIVE_LIMITS.maxArchiveBytes;
|
|
2844
|
+
try {
|
|
2845
|
+
const stat = fs.fstatSync(fd);
|
|
2846
|
+
if (!stat.isFile()) throw new ArchiveRefused("archive_path");
|
|
2847
|
+
if (stat.size > limit) throw new ArchiveRefused("archive_limit");
|
|
2848
|
+
const chunks = [];
|
|
2849
|
+
let total = 0;
|
|
2850
|
+
for (; ; ) {
|
|
2851
|
+
const chunk = new Uint8Array(Math.min(65536, limit + 1 - total));
|
|
2852
|
+
const count = fs.readSync(fd, chunk, 0, chunk.length, null);
|
|
2853
|
+
if (count === 0) break;
|
|
2854
|
+
if (total + count > limit) throw new ArchiveRefused("archive_limit");
|
|
2855
|
+
chunks.push(chunk.subarray(0, count));
|
|
2856
|
+
total += count;
|
|
2857
|
+
}
|
|
2858
|
+
const bytes = new Uint8Array(total);
|
|
2859
|
+
let at2 = 0;
|
|
2860
|
+
for (const chunk of chunks) {
|
|
2861
|
+
bytes.set(chunk, at2);
|
|
2862
|
+
at2 += chunk.length;
|
|
2863
|
+
}
|
|
2864
|
+
return bytes;
|
|
2865
|
+
} finally {
|
|
2866
|
+
fs.closeSync(fd);
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2670
2870
|
// src/reader/ods/ods-formula.ts
|
|
2671
2871
|
function odsFormulaToA1(formula2) {
|
|
2672
2872
|
let grammar = "of";
|
|
@@ -3446,7 +3646,7 @@ function at(node, name) {
|
|
|
3446
3646
|
var MAX_ROWS = 1048576;
|
|
3447
3647
|
var MAX_COLUMNS = 16384;
|
|
3448
3648
|
var UNIX_EPOCH_SERIAL = 25569;
|
|
3449
|
-
var
|
|
3649
|
+
var decoder2 = new TextDecoder();
|
|
3450
3650
|
var OdsReader = class {
|
|
3451
3651
|
describe(bytes) {
|
|
3452
3652
|
return WorkbookSchema.fromWorkbook(this.readWorkbook(bytes));
|
|
@@ -3456,7 +3656,7 @@ var OdsReader = class {
|
|
|
3456
3656
|
}
|
|
3457
3657
|
/** Read an already-unzipped package (name -> bytes). */
|
|
3458
3658
|
readFiles(files) {
|
|
3459
|
-
const getText = (name) => files[name] ?
|
|
3659
|
+
const getText = (name) => files[name] ? decoder2.decode(files[name]) : null;
|
|
3460
3660
|
const content = getText("content.xml");
|
|
3461
3661
|
if (content === null) throw new Error("[holy-sheet] missing content.xml");
|
|
3462
3662
|
const contentDoc = parseXml(content, { namespaces: true });
|
|
@@ -3818,7 +4018,7 @@ function points(ser) {
|
|
|
3818
4018
|
byIndex.set(parseInt(idx, 10), color2);
|
|
3819
4019
|
}
|
|
3820
4020
|
}
|
|
3821
|
-
return [...byIndex.entries()].sort((a, b) => a[0] - b[0]).map(([
|
|
4021
|
+
return [...byIndex.entries()].sort((a, b) => a[0] - b[0]).map(([index2, color2]) => ({ index: index2, color: color2 }));
|
|
3822
4022
|
}
|
|
3823
4023
|
function seriesColor(ser) {
|
|
3824
4024
|
const spPr = el(ser, "spPr");
|
|
@@ -4126,7 +4326,7 @@ function parseFrozen(xml) {
|
|
|
4126
4326
|
}
|
|
4127
4327
|
|
|
4128
4328
|
// src/reader/xlsx-reader.ts
|
|
4129
|
-
var
|
|
4329
|
+
var decoder3 = new TextDecoder();
|
|
4130
4330
|
var XlsxReader = class {
|
|
4131
4331
|
describe(bytes) {
|
|
4132
4332
|
return WorkbookSchema.fromWorkbook(this.readWorkbook(bytes));
|
|
@@ -4136,7 +4336,7 @@ var XlsxReader = class {
|
|
|
4136
4336
|
}
|
|
4137
4337
|
/** Read an already-unzipped package (name -> bytes), so a caller that sniffed it does not unzip twice. */
|
|
4138
4338
|
readFiles(files) {
|
|
4139
|
-
const getText = (name) => files[name] ?
|
|
4339
|
+
const getText = (name) => files[name] ? decoder3.decode(files[name]) : null;
|
|
4140
4340
|
const stylesXml = getText("xl/styles.xml");
|
|
4141
4341
|
const stylesIndex = stylesXml ? StylesParser.parse(stylesXml) : [];
|
|
4142
4342
|
const sharedStringsXml = getText("xl/sharedStrings.xml");
|
|
@@ -5006,11 +5206,11 @@ var SheetReducer = {
|
|
|
5006
5206
|
if (!isArr(get(op, "sheet"))) {
|
|
5007
5207
|
return schema;
|
|
5008
5208
|
}
|
|
5009
|
-
const
|
|
5010
|
-
if (
|
|
5209
|
+
const index2 = has(op, "index") ? phpInteger(op.index) : sheets.length;
|
|
5210
|
+
if (index2 === null) {
|
|
5011
5211
|
return schema;
|
|
5012
5212
|
}
|
|
5013
|
-
sheets.splice(Math.max(0, Math.min(sheets.length,
|
|
5213
|
+
sheets.splice(Math.max(0, Math.min(sheets.length, index2)), 0, op.sheet);
|
|
5014
5214
|
return { ...schema, sheets };
|
|
5015
5215
|
}
|
|
5016
5216
|
const at2 = find(sheets, phpString(get(op, "sheet") ?? ""));
|
|
@@ -5186,11 +5386,11 @@ function shiftColumns(sheet, at2, delta) {
|
|
|
5186
5386
|
});
|
|
5187
5387
|
if (isArr(get(sheet, "columnWidths"))) {
|
|
5188
5388
|
const widths = /* @__PURE__ */ new Map();
|
|
5189
|
-
for (const [
|
|
5190
|
-
if (!isIndexKey(
|
|
5389
|
+
for (const [index2, width] of entriesOf(sheet.columnWidths)) {
|
|
5390
|
+
if (!isIndexKey(index2)) {
|
|
5191
5391
|
continue;
|
|
5192
5392
|
}
|
|
5193
|
-
const i = phpInt2(
|
|
5393
|
+
const i = phpInt2(index2);
|
|
5194
5394
|
const number = i + 1;
|
|
5195
5395
|
if (number < at2) {
|
|
5196
5396
|
widths.set(i, width);
|
|
@@ -5199,8 +5399,8 @@ function shiftColumns(sheet, at2, delta) {
|
|
|
5199
5399
|
}
|
|
5200
5400
|
}
|
|
5201
5401
|
const sorted = {};
|
|
5202
|
-
for (const [
|
|
5203
|
-
sorted[
|
|
5402
|
+
for (const [index2, width] of [...widths.entries()].sort((x, y) => x[0] - y[0])) {
|
|
5403
|
+
sorted[index2] = width;
|
|
5204
5404
|
}
|
|
5205
5405
|
sheet = setOrUnset(sheet, "columnWidths", sorted, EMPTY);
|
|
5206
5406
|
}
|
|
@@ -5463,22 +5663,22 @@ function granular(a, b) {
|
|
|
5463
5663
|
ops.push({ type: "rename_sheet", sheet: name, name: renames.get(name) });
|
|
5464
5664
|
}
|
|
5465
5665
|
}
|
|
5466
|
-
namesB.forEach((name,
|
|
5666
|
+
namesB.forEach((name, index2) => {
|
|
5467
5667
|
if (unmatchedAdded.includes(name)) {
|
|
5468
|
-
ops.push({ type: "add_sheet", index, sheet: sheetsB[
|
|
5668
|
+
ops.push({ type: "add_sheet", index: index2, sheet: sheetsB[index2] });
|
|
5469
5669
|
}
|
|
5470
5670
|
});
|
|
5471
5671
|
let state = SheetReducer.applyAll(a, ops);
|
|
5472
|
-
namesB.forEach((name,
|
|
5672
|
+
namesB.forEach((name, index2) => {
|
|
5473
5673
|
const current = new Map(entriesOf(get(state, "sheets") ?? []).map(([key, sheet]) => [key, sheetName(sheet)]));
|
|
5474
|
-
if ((current.get(String(
|
|
5475
|
-
const op = { type: "move_sheet", sheet: name, toIndex:
|
|
5674
|
+
if ((current.get(String(index2)) ?? null) !== name) {
|
|
5675
|
+
const op = { type: "move_sheet", sheet: name, toIndex: index2 };
|
|
5476
5676
|
ops.push(op);
|
|
5477
5677
|
state = SheetReducer.apply(state, op);
|
|
5478
5678
|
}
|
|
5479
5679
|
});
|
|
5480
|
-
sheetsB.forEach((target,
|
|
5481
|
-
const current = state.sheets[
|
|
5680
|
+
sheetsB.forEach((target, index2) => {
|
|
5681
|
+
const current = state.sheets[index2];
|
|
5482
5682
|
if (SheetDiff.same(current, target)) {
|
|
5483
5683
|
return;
|
|
5484
5684
|
}
|
|
@@ -6129,7 +6329,7 @@ var holy_sheet_schema_default = {
|
|
|
6129
6329
|
};
|
|
6130
6330
|
|
|
6131
6331
|
// src/agent.ts
|
|
6132
|
-
var VERSION = "2.10.
|
|
6332
|
+
var VERSION = "2.10.2";
|
|
6133
6333
|
function toU8(input) {
|
|
6134
6334
|
return input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
6135
6335
|
}
|
|
@@ -6174,8 +6374,8 @@ var Agent = {
|
|
|
6174
6374
|
async describe(path) {
|
|
6175
6375
|
const fs = await import('fs');
|
|
6176
6376
|
if (!fs.existsSync(path)) return { error: "not_found", path };
|
|
6177
|
-
const bytes =
|
|
6178
|
-
return readSpreadsheet(
|
|
6377
|
+
const bytes = await readFileBounded(path);
|
|
6378
|
+
return readSpreadsheet(bytes, path);
|
|
6179
6379
|
},
|
|
6180
6380
|
/** Validate + conservative repairs in one call. */
|
|
6181
6381
|
validateAndRepair(schema) {
|
|
@@ -6292,6 +6492,6 @@ var HolySheet = class {
|
|
|
6292
6492
|
};
|
|
6293
6493
|
HolySheet.VERSION = VERSION;
|
|
6294
6494
|
|
|
6295
|
-
export { Agent, ArrayBuilder, CellAddress, CsvBuilder, FormatSniffer, FormulaLinter, HolySheet, Inference, Normalizer, OdsReader, Repairer, SchemaException, SheetDiff, SheetOpSchema, SheetReducer, Theme, UnsupportedFormatException, VERSION, Validator, XlsxReader, XlsxWriter, unzipSync, zipSync };
|
|
6495
|
+
export { Agent, ArchiveRefused, ArrayBuilder, CellAddress, CsvBuilder, FormatSniffer, FormulaLinter, HolySheet, Inference, Normalizer, OdsReader, Repairer, SchemaException, SheetDiff, SheetOpSchema, SheetReducer, Theme, UnsupportedFormatException, VERSION, Validator, XlsxReader, XlsxWriter, unzipSync, zipSync };
|
|
6296
6496
|
//# sourceMappingURL=index.js.map
|
|
6297
6497
|
//# sourceMappingURL=index.js.map
|