@cj-tech-master/excelts 7.6.0 → 8.0.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/README.md +99 -577
- package/README_zh.md +101 -577
- package/dist/browser/index.browser.d.ts +3 -0
- package/dist/browser/index.browser.js +2 -0
- package/dist/browser/index.d.ts +3 -0
- package/dist/browser/index.js +2 -0
- package/dist/browser/modules/archive/compression/compress.browser.js +4 -4
- package/dist/browser/modules/archive/compression/deflate-fallback.d.ts +24 -22
- package/dist/browser/modules/archive/compression/deflate-fallback.js +664 -360
- package/dist/browser/modules/archive/compression/streaming-compress.browser.d.ts +7 -0
- package/dist/browser/modules/archive/compression/streaming-compress.browser.js +15 -3
- package/dist/browser/modules/archive/compression/streaming-compress.d.ts +5 -0
- package/dist/browser/modules/archive/compression/streaming-compress.js +7 -0
- package/dist/browser/modules/archive/zip/stream.js +27 -3
- package/dist/browser/modules/excel/workbook.browser.d.ts +72 -0
- package/dist/browser/modules/excel/workbook.browser.js +226 -0
- package/dist/browser/modules/excel/workbook.d.ts +32 -1
- package/dist/browser/modules/excel/workbook.js +47 -2
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +42 -4
- package/dist/browser/modules/markdown/constants.d.ts +30 -0
- package/dist/browser/modules/markdown/constants.js +30 -0
- package/dist/browser/modules/markdown/errors.d.ts +21 -0
- package/dist/browser/modules/markdown/errors.js +23 -0
- package/dist/browser/modules/markdown/format/index.d.ts +54 -0
- package/dist/browser/modules/markdown/format/index.js +307 -0
- package/dist/browser/modules/markdown/index.d.ts +15 -0
- package/dist/browser/modules/markdown/index.js +22 -0
- package/dist/browser/modules/markdown/parse/index.d.ts +70 -0
- package/dist/browser/modules/markdown/parse/index.js +428 -0
- package/dist/browser/modules/markdown/types.d.ts +130 -0
- package/dist/browser/modules/markdown/types.js +6 -0
- package/dist/cjs/index.js +5 -1
- package/dist/cjs/modules/archive/compression/compress.browser.js +4 -4
- package/dist/cjs/modules/archive/compression/deflate-fallback.js +664 -360
- package/dist/cjs/modules/archive/compression/streaming-compress.browser.js +15 -2
- package/dist/cjs/modules/archive/compression/streaming-compress.js +8 -0
- package/dist/cjs/modules/archive/zip/stream.js +26 -2
- package/dist/cjs/modules/excel/workbook.browser.js +226 -0
- package/dist/cjs/modules/excel/workbook.js +46 -1
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +42 -4
- package/dist/cjs/modules/markdown/constants.js +33 -0
- package/dist/cjs/modules/markdown/errors.js +28 -0
- package/dist/cjs/modules/markdown/format/index.js +310 -0
- package/dist/cjs/modules/markdown/index.js +30 -0
- package/dist/cjs/modules/markdown/parse/index.js +432 -0
- package/dist/cjs/modules/markdown/types.js +7 -0
- package/dist/esm/index.browser.js +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/modules/archive/compression/compress.browser.js +4 -4
- package/dist/esm/modules/archive/compression/deflate-fallback.js +664 -360
- package/dist/esm/modules/archive/compression/streaming-compress.browser.js +15 -3
- package/dist/esm/modules/archive/compression/streaming-compress.js +7 -0
- package/dist/esm/modules/archive/zip/stream.js +27 -3
- package/dist/esm/modules/excel/workbook.browser.js +226 -0
- package/dist/esm/modules/excel/workbook.js +47 -2
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +42 -4
- package/dist/esm/modules/markdown/constants.js +30 -0
- package/dist/esm/modules/markdown/errors.js +23 -0
- package/dist/esm/modules/markdown/format/index.js +307 -0
- package/dist/esm/modules/markdown/index.js +22 -0
- package/dist/esm/modules/markdown/parse/index.js +428 -0
- package/dist/esm/modules/markdown/types.js +6 -0
- package/dist/iife/excelts.iife.js +1342 -283
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +38 -34
- package/dist/types/index.browser.d.ts +3 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/modules/archive/compression/deflate-fallback.d.ts +24 -22
- package/dist/types/modules/archive/compression/streaming-compress.browser.d.ts +7 -0
- package/dist/types/modules/archive/compression/streaming-compress.d.ts +5 -0
- package/dist/types/modules/excel/workbook.browser.d.ts +72 -0
- package/dist/types/modules/excel/workbook.d.ts +32 -1
- package/dist/types/modules/markdown/constants.d.ts +30 -0
- package/dist/types/modules/markdown/errors.d.ts +21 -0
- package/dist/types/modules/markdown/format/index.d.ts +54 -0
- package/dist/types/modules/markdown/index.d.ts +15 -0
- package/dist/types/modules/markdown/parse/index.d.ts +70 -0
- package/dist/types/modules/markdown/types.d.ts +130 -0
- package/package.json +56 -32
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @cj-tech-master/excelts
|
|
3
|
-
* TypeScript Excel
|
|
2
|
+
* @cj-tech-master/excelts v8.0.0
|
|
3
|
+
* Zero-dependency TypeScript toolkit — Excel (XLSX), PDF, CSV, Markdown, XML, ZIP/TAR, and streaming.
|
|
4
4
|
* (c) 2026 cjnoname
|
|
5
5
|
* Released under the MIT License
|
|
6
6
|
*/
|
|
@@ -24380,11 +24380,46 @@ var ExcelTS = (function(exports) {
|
|
|
24380
24380
|
}
|
|
24381
24381
|
const HASH_SIZE = 32768;
|
|
24382
24382
|
const HASH_MASK = HASH_SIZE - 1;
|
|
24383
|
-
const MAX_CHAIN_LEN = 64;
|
|
24384
24383
|
const MIN_MATCH = 3;
|
|
24385
24384
|
const MAX_MATCH = 258;
|
|
24386
24385
|
const MAX_DIST = 32768;
|
|
24387
24386
|
/**
|
|
24387
|
+
* Get LZ77 configuration for the given compression level (1-9).
|
|
24388
|
+
* Modelled after zlib's configuration_table.
|
|
24389
|
+
*/
|
|
24390
|
+
function getLZ77Config(level) {
|
|
24391
|
+
if (level <= 1) return {
|
|
24392
|
+
maxChainLen: 4,
|
|
24393
|
+
goodLen: 4,
|
|
24394
|
+
niceLen: 8,
|
|
24395
|
+
lazy: false
|
|
24396
|
+
};
|
|
24397
|
+
if (level <= 3) return {
|
|
24398
|
+
maxChainLen: 8,
|
|
24399
|
+
goodLen: 8,
|
|
24400
|
+
niceLen: 32,
|
|
24401
|
+
lazy: true
|
|
24402
|
+
};
|
|
24403
|
+
if (level <= 5) return {
|
|
24404
|
+
maxChainLen: 32,
|
|
24405
|
+
goodLen: 16,
|
|
24406
|
+
niceLen: 128,
|
|
24407
|
+
lazy: true
|
|
24408
|
+
};
|
|
24409
|
+
if (level <= 7) return {
|
|
24410
|
+
maxChainLen: 64,
|
|
24411
|
+
goodLen: 32,
|
|
24412
|
+
niceLen: 258,
|
|
24413
|
+
lazy: true
|
|
24414
|
+
};
|
|
24415
|
+
return {
|
|
24416
|
+
maxChainLen: 128,
|
|
24417
|
+
goodLen: 64,
|
|
24418
|
+
niceLen: 258,
|
|
24419
|
+
lazy: true
|
|
24420
|
+
};
|
|
24421
|
+
}
|
|
24422
|
+
/**
|
|
24388
24423
|
* Hash function for 3-byte sequences.
|
|
24389
24424
|
* Uses a multiplicative hash for better distribution than the naive
|
|
24390
24425
|
* shift-or approach. The constant 0x1e35a7bd is chosen for good avalanche
|
|
@@ -24394,91 +24429,22 @@ var ExcelTS = (function(exports) {
|
|
|
24394
24429
|
return (a << 16 | b << 8 | c) * 506832829 >>> 17 & HASH_MASK;
|
|
24395
24430
|
}
|
|
24396
24431
|
/**
|
|
24397
|
-
* Compress data using DEFLATE with
|
|
24432
|
+
* Compress data using DEFLATE with Dynamic Huffman codes (BTYPE=2).
|
|
24398
24433
|
*
|
|
24399
|
-
* Uses LZ77 with hash chains and lazy matching for
|
|
24400
|
-
*
|
|
24401
|
-
* after zlib's "fast" and "slow" deflate strategies.
|
|
24434
|
+
* Uses LZ77 with hash chains and lazy matching for match finding, then builds
|
|
24435
|
+
* optimal Huffman trees from the symbol frequencies for entropy coding.
|
|
24402
24436
|
*
|
|
24403
24437
|
* @param data - Data to compress
|
|
24438
|
+
* @param level - Compression level (1-9, default 6)
|
|
24404
24439
|
* @returns Compressed data in deflate-raw format
|
|
24405
24440
|
*/
|
|
24406
|
-
function deflateRawCompressed(data) {
|
|
24441
|
+
function deflateRawCompressed(data, level = 6) {
|
|
24407
24442
|
if (data.length === 0) return new Uint8Array([3, 0]);
|
|
24408
24443
|
if (data.length < 100) return deflateRawStore(data);
|
|
24444
|
+
const config = getLZ77Config(level);
|
|
24445
|
+
const lz77Symbols = lz77Compress(data, 0, data.length, config, null);
|
|
24409
24446
|
const output = new BitWriter();
|
|
24410
|
-
output
|
|
24411
|
-
output.writeBits(1, 2);
|
|
24412
|
-
const head = new Int32Array(HASH_SIZE);
|
|
24413
|
-
const prev = new Int32Array(MAX_DIST);
|
|
24414
|
-
let pos = 0;
|
|
24415
|
-
let prevMatchLen = 0;
|
|
24416
|
-
let prevMatchDist = 0;
|
|
24417
|
-
let prevLiteral = 0;
|
|
24418
|
-
let hasPrevMatch = false;
|
|
24419
|
-
while (pos < data.length) {
|
|
24420
|
-
let bestLen = 0;
|
|
24421
|
-
let bestDist = 0;
|
|
24422
|
-
if (pos + 2 < data.length) {
|
|
24423
|
-
const h = hash3(data[pos], data[pos + 1], data[pos + 2]);
|
|
24424
|
-
let chainLen = MAX_CHAIN_LEN;
|
|
24425
|
-
let matchHead = head[h];
|
|
24426
|
-
while (matchHead > 0 && chainLen-- > 0) {
|
|
24427
|
-
const mPos = matchHead - 1;
|
|
24428
|
-
const dist = pos - mPos;
|
|
24429
|
-
if (dist > MAX_DIST || dist <= 0) break;
|
|
24430
|
-
if (bestLen >= MIN_MATCH && data[mPos + bestLen] !== data[pos + bestLen]) {
|
|
24431
|
-
matchHead = prev[mPos & MAX_DIST - 1];
|
|
24432
|
-
continue;
|
|
24433
|
-
}
|
|
24434
|
-
let len = 0;
|
|
24435
|
-
const maxLen = Math.min(MAX_MATCH, data.length - pos);
|
|
24436
|
-
while (len < maxLen && data[mPos + len] === data[pos + len]) len++;
|
|
24437
|
-
if (len > bestLen) {
|
|
24438
|
-
bestLen = len;
|
|
24439
|
-
bestDist = dist;
|
|
24440
|
-
if (len >= MAX_MATCH) break;
|
|
24441
|
-
}
|
|
24442
|
-
matchHead = prev[mPos & MAX_DIST - 1];
|
|
24443
|
-
}
|
|
24444
|
-
prev[pos & MAX_DIST - 1] = head[h];
|
|
24445
|
-
head[h] = pos + 1;
|
|
24446
|
-
}
|
|
24447
|
-
if (hasPrevMatch) if (bestLen > prevMatchLen) {
|
|
24448
|
-
writeLiteralCode(output, prevLiteral);
|
|
24449
|
-
prevMatchLen = bestLen;
|
|
24450
|
-
prevMatchDist = bestDist;
|
|
24451
|
-
prevLiteral = data[pos];
|
|
24452
|
-
pos++;
|
|
24453
|
-
} else {
|
|
24454
|
-
writeLengthCode(output, prevMatchLen);
|
|
24455
|
-
writeDistanceCode(output, prevMatchDist);
|
|
24456
|
-
const matchEnd = pos - 1 + prevMatchLen;
|
|
24457
|
-
for (let i = pos; i < matchEnd && i + 2 < data.length; i++) {
|
|
24458
|
-
const h = hash3(data[i], data[i + 1], data[i + 2]);
|
|
24459
|
-
prev[i & MAX_DIST - 1] = head[h];
|
|
24460
|
-
head[h] = i + 1;
|
|
24461
|
-
}
|
|
24462
|
-
pos = matchEnd;
|
|
24463
|
-
hasPrevMatch = false;
|
|
24464
|
-
prevMatchLen = 0;
|
|
24465
|
-
}
|
|
24466
|
-
else if (bestLen >= MIN_MATCH) {
|
|
24467
|
-
hasPrevMatch = true;
|
|
24468
|
-
prevMatchLen = bestLen;
|
|
24469
|
-
prevMatchDist = bestDist;
|
|
24470
|
-
prevLiteral = data[pos];
|
|
24471
|
-
pos++;
|
|
24472
|
-
} else {
|
|
24473
|
-
writeLiteralCode(output, data[pos]);
|
|
24474
|
-
pos++;
|
|
24475
|
-
}
|
|
24476
|
-
}
|
|
24477
|
-
if (hasPrevMatch) {
|
|
24478
|
-
writeLengthCode(output, prevMatchLen);
|
|
24479
|
-
writeDistanceCode(output, prevMatchDist);
|
|
24480
|
-
}
|
|
24481
|
-
writeLiteralCode(output, 256);
|
|
24447
|
+
emitDynamicBlock(output, lz77Symbols, true);
|
|
24482
24448
|
return output.finish();
|
|
24483
24449
|
}
|
|
24484
24450
|
/**
|
|
@@ -24491,6 +24457,12 @@ var ExcelTS = (function(exports) {
|
|
|
24491
24457
|
this.bitBuf = 0;
|
|
24492
24458
|
this.bitCount = 0;
|
|
24493
24459
|
}
|
|
24460
|
+
/**
|
|
24461
|
+
* Align to the next byte boundary by padding with zero bits.
|
|
24462
|
+
*/
|
|
24463
|
+
alignToByte() {
|
|
24464
|
+
if (this.bitCount > 0) this.writeBits(0, 8 - this.bitCount);
|
|
24465
|
+
}
|
|
24494
24466
|
writeBits(value, count) {
|
|
24495
24467
|
this.bitBuf |= value << this.bitCount;
|
|
24496
24468
|
this.bitCount += count;
|
|
@@ -24563,73 +24535,484 @@ var ExcelTS = (function(exports) {
|
|
|
24563
24535
|
output.writeBitsReverse(code, len);
|
|
24564
24536
|
}
|
|
24565
24537
|
/**
|
|
24566
|
-
*
|
|
24567
|
-
|
|
24568
|
-
|
|
24569
|
-
|
|
24570
|
-
let
|
|
24571
|
-
|
|
24572
|
-
|
|
24573
|
-
|
|
24574
|
-
|
|
24575
|
-
|
|
24576
|
-
|
|
24577
|
-
|
|
24578
|
-
|
|
24579
|
-
|
|
24580
|
-
|
|
24581
|
-
|
|
24582
|
-
|
|
24583
|
-
|
|
24584
|
-
|
|
24585
|
-
|
|
24586
|
-
|
|
24587
|
-
const
|
|
24588
|
-
|
|
24589
|
-
|
|
24590
|
-
|
|
24591
|
-
|
|
24592
|
-
|
|
24593
|
-
code = 277 + Math.floor(base / 16);
|
|
24594
|
-
extraBits = 4;
|
|
24595
|
-
extraValue = base % 16;
|
|
24596
|
-
} else if (length <= 257) {
|
|
24597
|
-
const base = length - 131;
|
|
24598
|
-
code = 281 + Math.floor(base / 32);
|
|
24599
|
-
extraBits = 5;
|
|
24600
|
-
extraValue = base % 32;
|
|
24601
|
-
} else {
|
|
24602
|
-
code = 285;
|
|
24603
|
-
extraBits = 0;
|
|
24604
|
-
extraValue = 0;
|
|
24538
|
+
* Compute the DEFLATE length code (257..285) and extra bits for a given
|
|
24539
|
+
* match length (3..258).
|
|
24540
|
+
*/
|
|
24541
|
+
function getLengthSymbol(length) {
|
|
24542
|
+
for (let i = 0; i < LENGTH_BASE.length; i++) if (i === LENGTH_BASE.length - 1 || length < LENGTH_BASE[i + 1]) return {
|
|
24543
|
+
code: 257 + i,
|
|
24544
|
+
extra: length - LENGTH_BASE[i],
|
|
24545
|
+
extraBits: LENGTH_EXTRA[i]
|
|
24546
|
+
};
|
|
24547
|
+
return {
|
|
24548
|
+
code: 285,
|
|
24549
|
+
extra: 0,
|
|
24550
|
+
extraBits: 0
|
|
24551
|
+
};
|
|
24552
|
+
}
|
|
24553
|
+
/**
|
|
24554
|
+
* Compute the DEFLATE distance code (0..29) and extra bits for a given
|
|
24555
|
+
* distance (1..32768).
|
|
24556
|
+
*/
|
|
24557
|
+
function getDistSymbol(distance) {
|
|
24558
|
+
for (let i = 0; i < DIST_TABLE.length; i++) {
|
|
24559
|
+
const [maxDist, c, extraBitsCount] = DIST_TABLE[i];
|
|
24560
|
+
if (distance <= maxDist) return {
|
|
24561
|
+
code: c,
|
|
24562
|
+
extra: distance - (i === 0 ? 1 : DIST_TABLE[i - 1][0] + 1),
|
|
24563
|
+
extraBits: extraBitsCount
|
|
24564
|
+
};
|
|
24605
24565
|
}
|
|
24606
|
-
|
|
24607
|
-
|
|
24566
|
+
return {
|
|
24567
|
+
code: 29,
|
|
24568
|
+
extra: 0,
|
|
24569
|
+
extraBits: 13
|
|
24570
|
+
};
|
|
24608
24571
|
}
|
|
24609
24572
|
/**
|
|
24610
|
-
*
|
|
24573
|
+
* Build canonical Huffman code lengths from symbol frequencies.
|
|
24574
|
+
* Uses a bottom-up approach: build a Huffman tree from a priority queue,
|
|
24575
|
+
* then extract depths. Limits maximum code length to maxBits using
|
|
24576
|
+
* the algorithm from zlib's build_tree() / gen_bitlen().
|
|
24577
|
+
*
|
|
24578
|
+
* Returns an array of code lengths indexed by symbol.
|
|
24611
24579
|
*/
|
|
24612
|
-
function
|
|
24580
|
+
function buildCodeLengths(freqs, maxBits) {
|
|
24581
|
+
const n = freqs.length;
|
|
24582
|
+
const codeLens = new Uint8Array(n);
|
|
24583
|
+
const activeSymbols = [];
|
|
24584
|
+
for (let i = 0; i < n; i++) if (freqs[i] > 0) activeSymbols.push({
|
|
24585
|
+
sym: i,
|
|
24586
|
+
freq: freqs[i]
|
|
24587
|
+
});
|
|
24588
|
+
if (activeSymbols.length === 0) return codeLens;
|
|
24589
|
+
if (activeSymbols.length === 1) {
|
|
24590
|
+
codeLens[activeSymbols[0].sym] = 1;
|
|
24591
|
+
return codeLens;
|
|
24592
|
+
}
|
|
24593
|
+
activeSymbols.sort((a, b) => a.freq - b.freq || a.sym - b.sym);
|
|
24594
|
+
const nodes = activeSymbols.map((s) => ({
|
|
24595
|
+
freq: s.freq,
|
|
24596
|
+
sym: s.sym,
|
|
24597
|
+
left: null,
|
|
24598
|
+
right: null
|
|
24599
|
+
}));
|
|
24600
|
+
let leafIdx = 0;
|
|
24601
|
+
let intIdx = 0;
|
|
24602
|
+
const intNodes = [];
|
|
24603
|
+
function getMin() {
|
|
24604
|
+
const hasLeaf = leafIdx < nodes.length;
|
|
24605
|
+
const hasInt = intIdx < intNodes.length;
|
|
24606
|
+
if (hasLeaf && hasInt) {
|
|
24607
|
+
if (nodes[leafIdx].freq <= intNodes[intIdx].freq) return nodes[leafIdx++];
|
|
24608
|
+
return intNodes[intIdx++];
|
|
24609
|
+
}
|
|
24610
|
+
if (hasLeaf) return nodes[leafIdx++];
|
|
24611
|
+
return intNodes[intIdx++];
|
|
24612
|
+
}
|
|
24613
|
+
const totalNodes = activeSymbols.length;
|
|
24614
|
+
for (let i = 0; i < totalNodes - 1; i++) {
|
|
24615
|
+
const a = getMin();
|
|
24616
|
+
const b = getMin();
|
|
24617
|
+
const merged = {
|
|
24618
|
+
freq: a.freq + b.freq,
|
|
24619
|
+
sym: -1,
|
|
24620
|
+
left: a,
|
|
24621
|
+
right: b
|
|
24622
|
+
};
|
|
24623
|
+
intNodes.push(merged);
|
|
24624
|
+
}
|
|
24625
|
+
const root = intNodes[intNodes.length - 1];
|
|
24626
|
+
function extractDepths(node, depth) {
|
|
24627
|
+
if (node.sym >= 0) {
|
|
24628
|
+
codeLens[node.sym] = depth;
|
|
24629
|
+
return;
|
|
24630
|
+
}
|
|
24631
|
+
if (node.left) extractDepths(node.left, depth + 1);
|
|
24632
|
+
if (node.right) extractDepths(node.right, depth + 1);
|
|
24633
|
+
}
|
|
24634
|
+
extractDepths(root, 0);
|
|
24635
|
+
const blCount = new Uint16Array(maxBits + 1);
|
|
24636
|
+
for (let i = 0; i < n; i++) if (codeLens[i] > 0) if (codeLens[i] > maxBits) {
|
|
24637
|
+
blCount[maxBits]++;
|
|
24638
|
+
codeLens[i] = maxBits;
|
|
24639
|
+
} else blCount[codeLens[i]]++;
|
|
24640
|
+
let kraft = 0;
|
|
24641
|
+
for (let bits = 1; bits <= maxBits; bits++) kraft += blCount[bits] << maxBits - bits;
|
|
24642
|
+
const target = 1 << maxBits;
|
|
24643
|
+
if (kraft === target) return codeLens;
|
|
24644
|
+
while (kraft > target) {
|
|
24645
|
+
let bits = maxBits - 1;
|
|
24646
|
+
while (bits > 0 && blCount[bits] === 0) bits--;
|
|
24647
|
+
if (bits === 0) break;
|
|
24648
|
+
blCount[bits]--;
|
|
24649
|
+
blCount[maxBits]++;
|
|
24650
|
+
kraft -= (1 << maxBits - bits) - 1;
|
|
24651
|
+
}
|
|
24652
|
+
while (kraft < target) {
|
|
24653
|
+
blCount[maxBits]++;
|
|
24654
|
+
kraft++;
|
|
24655
|
+
}
|
|
24656
|
+
const symbolsByLen = [];
|
|
24657
|
+
for (let i = 0; i < n; i++) if (codeLens[i] > 0) symbolsByLen.push({
|
|
24658
|
+
sym: i,
|
|
24659
|
+
origLen: codeLens[i],
|
|
24660
|
+
freq: freqs[i]
|
|
24661
|
+
});
|
|
24662
|
+
symbolsByLen.sort((a, b) => b.origLen - a.origLen || a.freq - b.freq);
|
|
24663
|
+
codeLens.fill(0);
|
|
24664
|
+
let symIdx = 0;
|
|
24665
|
+
for (let bits = maxBits; bits >= 1; bits--) for (let count = blCount[bits]; count > 0; count--) if (symIdx < symbolsByLen.length) {
|
|
24666
|
+
codeLens[symbolsByLen[symIdx].sym] = bits;
|
|
24667
|
+
symIdx++;
|
|
24668
|
+
}
|
|
24669
|
+
return codeLens;
|
|
24670
|
+
}
|
|
24671
|
+
/**
|
|
24672
|
+
* Build canonical Huffman codes from code lengths (RFC 1951 §3.2.2).
|
|
24673
|
+
* Returns [code, length] pairs indexed by symbol.
|
|
24674
|
+
*/
|
|
24675
|
+
function buildCanonicalCodes(codeLens) {
|
|
24676
|
+
const n = codeLens.length;
|
|
24677
|
+
const codes = new Array(n);
|
|
24678
|
+
const blCount = new Uint16Array(16);
|
|
24679
|
+
for (let i = 0; i < n; i++) if (codeLens[i] > 0) blCount[codeLens[i]]++;
|
|
24680
|
+
const nextCode = new Uint16Array(16);
|
|
24613
24681
|
let code = 0;
|
|
24614
|
-
let
|
|
24615
|
-
|
|
24616
|
-
|
|
24617
|
-
|
|
24618
|
-
|
|
24619
|
-
|
|
24620
|
-
|
|
24621
|
-
|
|
24682
|
+
for (let bits = 1; bits <= 15; bits++) {
|
|
24683
|
+
code = code + blCount[bits - 1] << 1;
|
|
24684
|
+
nextCode[bits] = code;
|
|
24685
|
+
}
|
|
24686
|
+
for (let i = 0; i < n; i++) {
|
|
24687
|
+
const len = codeLens[i];
|
|
24688
|
+
if (len > 0) codes[i] = [nextCode[len]++, len];
|
|
24689
|
+
else codes[i] = [0, 0];
|
|
24690
|
+
}
|
|
24691
|
+
return codes;
|
|
24692
|
+
}
|
|
24693
|
+
/**
|
|
24694
|
+
* Emit a Dynamic Huffman DEFLATE block (BTYPE=2).
|
|
24695
|
+
*
|
|
24696
|
+
* Takes the LZ77 symbol sequence, builds optimal Huffman trees,
|
|
24697
|
+
* encodes the tree descriptions, then encodes the symbols.
|
|
24698
|
+
*/
|
|
24699
|
+
function emitDynamicBlock(out, symbols, isFinal) {
|
|
24700
|
+
const litLenFreqs = new Uint32Array(286);
|
|
24701
|
+
const distFreqs = new Uint32Array(30);
|
|
24702
|
+
litLenFreqs[256] = 1;
|
|
24703
|
+
for (const sym of symbols) if (sym.dist === 0) litLenFreqs[sym.litOrLen]++;
|
|
24704
|
+
else {
|
|
24705
|
+
const ls = getLengthSymbol(sym.litOrLen);
|
|
24706
|
+
litLenFreqs[ls.code]++;
|
|
24707
|
+
const ds = getDistSymbol(sym.dist);
|
|
24708
|
+
distFreqs[ds.code]++;
|
|
24709
|
+
}
|
|
24710
|
+
const litLenLens = buildCodeLengths(litLenFreqs, 15);
|
|
24711
|
+
let distLens = buildCodeLengths(distFreqs, 15);
|
|
24712
|
+
let hasDistCodes = false;
|
|
24713
|
+
for (let i = 0; i < distLens.length; i++) if (distLens[i] > 0) {
|
|
24714
|
+
hasDistCodes = true;
|
|
24715
|
+
break;
|
|
24716
|
+
}
|
|
24717
|
+
if (!hasDistCodes) {
|
|
24718
|
+
distLens = new Uint8Array(30);
|
|
24719
|
+
distLens[0] = 1;
|
|
24720
|
+
distLens[1] = 1;
|
|
24721
|
+
}
|
|
24722
|
+
const litLenCodes = buildCanonicalCodes(litLenLens);
|
|
24723
|
+
const distCodes = buildCanonicalCodes(distLens);
|
|
24724
|
+
let hlit = 286;
|
|
24725
|
+
while (hlit > 257 && litLenLens[hlit - 1] === 0) hlit--;
|
|
24726
|
+
let hdist = 30;
|
|
24727
|
+
while (hdist > 1 && distLens[hdist - 1] === 0) hdist--;
|
|
24728
|
+
const combined = new Uint8Array(hlit + hdist);
|
|
24729
|
+
combined.set(litLenLens.subarray(0, hlit));
|
|
24730
|
+
combined.set(distLens.subarray(0, hdist), hlit);
|
|
24731
|
+
const clSymbols = [];
|
|
24732
|
+
const clFreqs = new Uint32Array(19);
|
|
24733
|
+
for (let i = 0; i < combined.length;) {
|
|
24734
|
+
const val = combined[i];
|
|
24735
|
+
if (val === 0) {
|
|
24736
|
+
let run = 1;
|
|
24737
|
+
while (i + run < combined.length && combined[i + run] === 0) run++;
|
|
24738
|
+
while (run > 0) if (run >= 11) {
|
|
24739
|
+
const repeat = Math.min(run, 138);
|
|
24740
|
+
clSymbols.push({
|
|
24741
|
+
sym: 18,
|
|
24742
|
+
extra: repeat - 11,
|
|
24743
|
+
extraBits: 7
|
|
24744
|
+
});
|
|
24745
|
+
clFreqs[18]++;
|
|
24746
|
+
run -= repeat;
|
|
24747
|
+
i += repeat;
|
|
24748
|
+
} else if (run >= 3) {
|
|
24749
|
+
const repeat = Math.min(run, 10);
|
|
24750
|
+
clSymbols.push({
|
|
24751
|
+
sym: 17,
|
|
24752
|
+
extra: repeat - 3,
|
|
24753
|
+
extraBits: 3
|
|
24754
|
+
});
|
|
24755
|
+
clFreqs[17]++;
|
|
24756
|
+
run -= repeat;
|
|
24757
|
+
i += repeat;
|
|
24758
|
+
} else {
|
|
24759
|
+
clSymbols.push({
|
|
24760
|
+
sym: 0,
|
|
24761
|
+
extra: 0,
|
|
24762
|
+
extraBits: 0
|
|
24763
|
+
});
|
|
24764
|
+
clFreqs[0]++;
|
|
24765
|
+
run--;
|
|
24766
|
+
i++;
|
|
24767
|
+
}
|
|
24768
|
+
} else {
|
|
24769
|
+
clSymbols.push({
|
|
24770
|
+
sym: val,
|
|
24771
|
+
extra: 0,
|
|
24772
|
+
extraBits: 0
|
|
24773
|
+
});
|
|
24774
|
+
clFreqs[val]++;
|
|
24775
|
+
i++;
|
|
24776
|
+
let run = 0;
|
|
24777
|
+
while (i + run < combined.length && combined[i + run] === val) run++;
|
|
24778
|
+
while (run >= 3) {
|
|
24779
|
+
const repeat = Math.min(run, 6);
|
|
24780
|
+
clSymbols.push({
|
|
24781
|
+
sym: 16,
|
|
24782
|
+
extra: repeat - 3,
|
|
24783
|
+
extraBits: 2
|
|
24784
|
+
});
|
|
24785
|
+
clFreqs[16]++;
|
|
24786
|
+
run -= repeat;
|
|
24787
|
+
i += repeat;
|
|
24788
|
+
}
|
|
24789
|
+
while (run > 0) {
|
|
24790
|
+
clSymbols.push({
|
|
24791
|
+
sym: val,
|
|
24792
|
+
extra: 0,
|
|
24793
|
+
extraBits: 0
|
|
24794
|
+
});
|
|
24795
|
+
clFreqs[val]++;
|
|
24796
|
+
run--;
|
|
24797
|
+
i++;
|
|
24798
|
+
}
|
|
24622
24799
|
}
|
|
24623
|
-
baseDistance = maxDist + 1;
|
|
24624
24800
|
}
|
|
24625
|
-
const
|
|
24626
|
-
|
|
24627
|
-
|
|
24801
|
+
const clLens = buildCodeLengths(clFreqs, 7);
|
|
24802
|
+
const clCodes = buildCanonicalCodes(clLens);
|
|
24803
|
+
let hclen = 19;
|
|
24804
|
+
while (hclen > 4 && clLens[CODE_LENGTH_ORDER[hclen - 1]] === 0) hclen--;
|
|
24805
|
+
out.writeBits(isFinal ? 1 : 0, 1);
|
|
24806
|
+
out.writeBits(2, 2);
|
|
24807
|
+
out.writeBits(hlit - 257, 5);
|
|
24808
|
+
out.writeBits(hdist - 1, 5);
|
|
24809
|
+
out.writeBits(hclen - 4, 4);
|
|
24810
|
+
for (let i = 0; i < hclen; i++) out.writeBits(clLens[CODE_LENGTH_ORDER[i]], 3);
|
|
24811
|
+
for (const cls of clSymbols) {
|
|
24812
|
+
const [clCode, clLen] = clCodes[cls.sym];
|
|
24813
|
+
out.writeBitsReverse(clCode, clLen);
|
|
24814
|
+
if (cls.extraBits > 0) out.writeBits(cls.extra, cls.extraBits);
|
|
24815
|
+
}
|
|
24816
|
+
for (const sym of symbols) if (sym.dist === 0) {
|
|
24817
|
+
const [lCode, lLen] = litLenCodes[sym.litOrLen];
|
|
24818
|
+
out.writeBitsReverse(lCode, lLen);
|
|
24819
|
+
} else {
|
|
24820
|
+
const ls = getLengthSymbol(sym.litOrLen);
|
|
24821
|
+
const [lCode, lLen] = litLenCodes[ls.code];
|
|
24822
|
+
out.writeBitsReverse(lCode, lLen);
|
|
24823
|
+
if (ls.extraBits > 0) out.writeBits(ls.extra, ls.extraBits);
|
|
24824
|
+
const ds = getDistSymbol(sym.dist);
|
|
24825
|
+
const [dCode, dLen] = distCodes[ds.code];
|
|
24826
|
+
out.writeBitsReverse(dCode, dLen);
|
|
24827
|
+
if (ds.extraBits > 0) out.writeBits(ds.extra, ds.extraBits);
|
|
24828
|
+
}
|
|
24829
|
+
const [eobCode, eobLen] = litLenCodes[256];
|
|
24830
|
+
out.writeBitsReverse(eobCode, eobLen);
|
|
24831
|
+
}
|
|
24832
|
+
/**
|
|
24833
|
+
* Run LZ77 match-finding on `data[start..end)`.
|
|
24834
|
+
*
|
|
24835
|
+
* When `state` is null, performs one-shot compression with fresh hash tables.
|
|
24836
|
+
* When `state` is provided, maintains sliding window and hash chains across calls.
|
|
24837
|
+
*
|
|
24838
|
+
* Returns an array of LZ77 symbols (literals + length/distance pairs).
|
|
24839
|
+
*/
|
|
24840
|
+
function lz77Compress(data, start, end, config, state) {
|
|
24841
|
+
const symbols = [];
|
|
24842
|
+
const maxChainLen = config.maxChainLen;
|
|
24843
|
+
const goodLen = config.goodLen;
|
|
24844
|
+
const niceLen = config.niceLen;
|
|
24845
|
+
const useLazy = config.lazy;
|
|
24846
|
+
let head;
|
|
24847
|
+
let prevArr;
|
|
24848
|
+
let window;
|
|
24849
|
+
let wLen;
|
|
24850
|
+
let totalIn;
|
|
24851
|
+
let hasPrevMatch;
|
|
24852
|
+
let prevMatchLen;
|
|
24853
|
+
let prevMatchDist;
|
|
24854
|
+
let prevLiteral;
|
|
24855
|
+
if (state) {
|
|
24856
|
+
head = state.head;
|
|
24857
|
+
prevArr = state.prev;
|
|
24858
|
+
window = state.window;
|
|
24859
|
+
wLen = state.windowLen;
|
|
24860
|
+
totalIn = state.totalIn;
|
|
24861
|
+
hasPrevMatch = state.hasPrevMatch;
|
|
24862
|
+
prevMatchLen = state.prevMatchLen;
|
|
24863
|
+
prevMatchDist = state.prevMatchDist;
|
|
24864
|
+
prevLiteral = state.prevLiteral;
|
|
24865
|
+
} else {
|
|
24866
|
+
head = new Int32Array(HASH_SIZE);
|
|
24867
|
+
prevArr = new Int32Array(MAX_DIST);
|
|
24868
|
+
window = null;
|
|
24869
|
+
wLen = 0;
|
|
24870
|
+
totalIn = 0;
|
|
24871
|
+
hasPrevMatch = false;
|
|
24872
|
+
prevMatchLen = 0;
|
|
24873
|
+
prevMatchDist = 0;
|
|
24874
|
+
prevLiteral = 0;
|
|
24875
|
+
}
|
|
24876
|
+
const getByte = state ? (globalPos) => {
|
|
24877
|
+
const localPos = globalPos - totalIn;
|
|
24878
|
+
if (localPos >= start && localPos < end) return data[localPos];
|
|
24879
|
+
return window[globalPos & MAX_DIST - 1];
|
|
24880
|
+
} : (globalPos) => data[globalPos];
|
|
24881
|
+
const insertHash = state ? (localPos) => {
|
|
24882
|
+
if (localPos + 2 >= end) return;
|
|
24883
|
+
const h = hash3(data[localPos], data[localPos + 1], data[localPos + 2]);
|
|
24884
|
+
const gp = totalIn + localPos;
|
|
24885
|
+
prevArr[gp & MAX_DIST - 1] = head[h];
|
|
24886
|
+
head[h] = gp + 1;
|
|
24887
|
+
} : (localPos) => {
|
|
24888
|
+
if (localPos + 2 >= end) return;
|
|
24889
|
+
const h = hash3(data[localPos], data[localPos + 1], data[localPos + 2]);
|
|
24890
|
+
prevArr[localPos & MAX_DIST - 1] = head[h];
|
|
24891
|
+
head[h] = localPos + 1;
|
|
24892
|
+
};
|
|
24893
|
+
const insertWindow = state ? (localPos, count) => {
|
|
24894
|
+
for (let i = 0; i < count; i++) window[wLen + i & MAX_DIST - 1] = data[localPos + i];
|
|
24895
|
+
wLen += count;
|
|
24896
|
+
} : (_localPos, _count) => {};
|
|
24897
|
+
let pos = start;
|
|
24898
|
+
for (; pos < end;) {
|
|
24899
|
+
let bestLen = 0;
|
|
24900
|
+
let bestDist = 0;
|
|
24901
|
+
if (pos + 2 < end) {
|
|
24902
|
+
const h = hash3(data[pos], data[pos + 1], data[pos + 2]);
|
|
24903
|
+
const globalPos = state ? totalIn + pos : pos;
|
|
24904
|
+
let chainRemaining = useLazy && hasPrevMatch && prevMatchLen >= goodLen ? maxChainLen >> 2 : maxChainLen;
|
|
24905
|
+
let matchHead = head[h];
|
|
24906
|
+
while (matchHead > 0 && chainRemaining-- > 0) {
|
|
24907
|
+
const mGlobalPos = matchHead - 1;
|
|
24908
|
+
const dist = globalPos - mGlobalPos;
|
|
24909
|
+
if (dist > MAX_DIST || dist <= 0) break;
|
|
24910
|
+
if (bestLen >= MIN_MATCH) {
|
|
24911
|
+
if (getByte(mGlobalPos + bestLen) !== data[pos + bestLen]) {
|
|
24912
|
+
matchHead = prevArr[mGlobalPos & MAX_DIST - 1];
|
|
24913
|
+
continue;
|
|
24914
|
+
}
|
|
24915
|
+
}
|
|
24916
|
+
const maxLen = Math.min(MAX_MATCH, end - pos);
|
|
24917
|
+
let len = 0;
|
|
24918
|
+
while (len < maxLen) {
|
|
24919
|
+
if (getByte(mGlobalPos + len) !== data[pos + len]) break;
|
|
24920
|
+
len++;
|
|
24921
|
+
}
|
|
24922
|
+
if (len > bestLen) {
|
|
24923
|
+
bestLen = len;
|
|
24924
|
+
bestDist = dist;
|
|
24925
|
+
if (len >= niceLen) break;
|
|
24926
|
+
}
|
|
24927
|
+
matchHead = prevArr[mGlobalPos & MAX_DIST - 1];
|
|
24928
|
+
}
|
|
24929
|
+
if (state) {
|
|
24930
|
+
prevArr[globalPos & MAX_DIST - 1] = head[h];
|
|
24931
|
+
head[h] = globalPos + 1;
|
|
24932
|
+
} else {
|
|
24933
|
+
prevArr[pos & MAX_DIST - 1] = head[h];
|
|
24934
|
+
head[h] = pos + 1;
|
|
24935
|
+
}
|
|
24936
|
+
}
|
|
24937
|
+
if (useLazy && hasPrevMatch) if (bestLen > prevMatchLen) {
|
|
24938
|
+
symbols.push({
|
|
24939
|
+
litOrLen: prevLiteral,
|
|
24940
|
+
dist: 0
|
|
24941
|
+
});
|
|
24942
|
+
prevMatchLen = bestLen;
|
|
24943
|
+
prevMatchDist = bestDist;
|
|
24944
|
+
prevLiteral = data[pos];
|
|
24945
|
+
insertWindow(pos, 1);
|
|
24946
|
+
pos++;
|
|
24947
|
+
} else {
|
|
24948
|
+
symbols.push({
|
|
24949
|
+
litOrLen: prevMatchLen,
|
|
24950
|
+
dist: prevMatchDist
|
|
24951
|
+
});
|
|
24952
|
+
const matchEnd = Math.min(pos - 1 + prevMatchLen, end);
|
|
24953
|
+
for (let i = pos; i < matchEnd; i++) insertHash(i);
|
|
24954
|
+
insertWindow(pos, matchEnd - pos);
|
|
24955
|
+
pos = matchEnd;
|
|
24956
|
+
hasPrevMatch = false;
|
|
24957
|
+
prevMatchLen = 0;
|
|
24958
|
+
}
|
|
24959
|
+
else if (bestLen >= MIN_MATCH) if (useLazy) {
|
|
24960
|
+
hasPrevMatch = true;
|
|
24961
|
+
prevMatchLen = bestLen;
|
|
24962
|
+
prevMatchDist = bestDist;
|
|
24963
|
+
prevLiteral = data[pos];
|
|
24964
|
+
insertWindow(pos, 1);
|
|
24965
|
+
pos++;
|
|
24966
|
+
} else {
|
|
24967
|
+
symbols.push({
|
|
24968
|
+
litOrLen: bestLen,
|
|
24969
|
+
dist: bestDist
|
|
24970
|
+
});
|
|
24971
|
+
const matchEnd = Math.min(pos + bestLen, end);
|
|
24972
|
+
for (let i = pos + 1; i < matchEnd; i++) insertHash(i);
|
|
24973
|
+
insertWindow(pos, matchEnd - pos);
|
|
24974
|
+
pos = matchEnd;
|
|
24975
|
+
}
|
|
24976
|
+
else {
|
|
24977
|
+
if (hasPrevMatch) {
|
|
24978
|
+
symbols.push({
|
|
24979
|
+
litOrLen: prevMatchLen,
|
|
24980
|
+
dist: prevMatchDist
|
|
24981
|
+
});
|
|
24982
|
+
hasPrevMatch = false;
|
|
24983
|
+
prevMatchLen = 0;
|
|
24984
|
+
}
|
|
24985
|
+
symbols.push({
|
|
24986
|
+
litOrLen: data[pos],
|
|
24987
|
+
dist: 0
|
|
24988
|
+
});
|
|
24989
|
+
insertWindow(pos, 1);
|
|
24990
|
+
pos++;
|
|
24991
|
+
}
|
|
24992
|
+
}
|
|
24993
|
+
if (hasPrevMatch) {
|
|
24994
|
+
symbols.push({
|
|
24995
|
+
litOrLen: prevMatchLen,
|
|
24996
|
+
dist: prevMatchDist
|
|
24997
|
+
});
|
|
24998
|
+
const matchEnd = Math.min(pos - 1 + prevMatchLen, end);
|
|
24999
|
+
for (let i = pos; i < matchEnd; i++) insertHash(i);
|
|
25000
|
+
insertWindow(pos, matchEnd - pos);
|
|
25001
|
+
hasPrevMatch = false;
|
|
25002
|
+
prevMatchLen = 0;
|
|
25003
|
+
}
|
|
25004
|
+
if (state) {
|
|
25005
|
+
state.windowLen = wLen;
|
|
25006
|
+
state.totalIn = totalIn + (end - start);
|
|
25007
|
+
state.hasPrevMatch = hasPrevMatch;
|
|
25008
|
+
state.prevMatchLen = prevMatchLen;
|
|
25009
|
+
state.prevMatchDist = prevMatchDist;
|
|
25010
|
+
state.prevLiteral = prevLiteral;
|
|
25011
|
+
}
|
|
25012
|
+
return symbols;
|
|
24628
25013
|
}
|
|
24629
|
-
/** Maximum LZ77 sliding window size (32 KB per RFC 1951). */
|
|
24630
|
-
const WINDOW_SIZE = 32768;
|
|
24631
25014
|
/**
|
|
24632
|
-
* Stateful synchronous DEFLATE compressor.
|
|
25015
|
+
* Stateful synchronous DEFLATE compressor with Dynamic Huffman encoding.
|
|
24633
25016
|
*
|
|
24634
25017
|
* Unlike `deflateRawCompressed` (which is a one-shot function), this class
|
|
24635
25018
|
* maintains state across multiple `write()` calls:
|
|
@@ -24637,30 +25020,39 @@ var ExcelTS = (function(exports) {
|
|
|
24637
25020
|
* - **LZ77 sliding window**: back-references can span across chunks.
|
|
24638
25021
|
* - **Hash chains**: match positions persist across chunks with typed-array
|
|
24639
25022
|
* hash tables for fast lookup.
|
|
24640
|
-
* - **Lazy matching**:
|
|
24641
|
-
*
|
|
25023
|
+
* - **Lazy matching**: configurable per compression level.
|
|
25024
|
+
* - **Dynamic Huffman**: each block builds optimal Huffman trees from
|
|
25025
|
+
* actual symbol frequencies (BTYPE=2), producing significantly smaller
|
|
25026
|
+
* output than fixed Huffman (BTYPE=1).
|
|
24642
25027
|
* - **Bit writer**: bit position is preserved, so consecutive blocks form
|
|
24643
25028
|
* a single valid DEFLATE bit-stream without alignment issues.
|
|
24644
25029
|
*
|
|
24645
|
-
* Each `write()` emits one non-final
|
|
24646
|
-
* `finish()` emits a final empty block (BFINAL=1)
|
|
25030
|
+
* Each `write()` emits one non-final Dynamic Huffman block (BFINAL=0).
|
|
25031
|
+
* `finish()` emits a final empty fixed-Huffman block (BFINAL=1).
|
|
24647
25032
|
*
|
|
24648
25033
|
* This is the pure-JS equivalent of Node.js `zlib.deflateRawSync` with
|
|
24649
25034
|
* `Z_SYNC_FLUSH`, used by the streaming ZIP writer (`pushSync`) to achieve
|
|
24650
25035
|
* constant-memory streaming in both Node.js and browsers.
|
|
25036
|
+
*
|
|
25037
|
+
* @param level - Compression level (0-9). Level 0 emits STORE blocks.
|
|
25038
|
+
* Default: 6 (matching zlib default).
|
|
24651
25039
|
*/
|
|
24652
25040
|
var SyncDeflater = class {
|
|
24653
|
-
constructor() {
|
|
25041
|
+
constructor(level = 6) {
|
|
24654
25042
|
this._output = new BitWriter();
|
|
24655
|
-
this.
|
|
24656
|
-
|
|
24657
|
-
|
|
24658
|
-
|
|
24659
|
-
|
|
24660
|
-
|
|
24661
|
-
|
|
24662
|
-
|
|
24663
|
-
|
|
25043
|
+
this._state = {
|
|
25044
|
+
head: new Int32Array(HASH_SIZE),
|
|
25045
|
+
prev: new Int32Array(MAX_DIST),
|
|
25046
|
+
window: new Uint8Array(MAX_DIST),
|
|
25047
|
+
windowLen: 0,
|
|
25048
|
+
totalIn: 0,
|
|
25049
|
+
hasPrevMatch: false,
|
|
25050
|
+
prevMatchLen: 0,
|
|
25051
|
+
prevMatchDist: 0,
|
|
25052
|
+
prevLiteral: 0
|
|
25053
|
+
};
|
|
25054
|
+
this._level = Math.max(0, Math.min(9, level));
|
|
25055
|
+
this._config = getLZ77Config(this._level);
|
|
24664
25056
|
}
|
|
24665
25057
|
/**
|
|
24666
25058
|
* Compress a chunk and return the compressed bytes produced so far.
|
|
@@ -24669,123 +25061,11 @@ var ExcelTS = (function(exports) {
|
|
|
24669
25061
|
write(data) {
|
|
24670
25062
|
if (data.length === 0) return new Uint8Array(0);
|
|
24671
25063
|
const out = this._output;
|
|
24672
|
-
|
|
24673
|
-
|
|
24674
|
-
|
|
24675
|
-
let wLen = this._windowLen;
|
|
24676
|
-
const head = this._head;
|
|
24677
|
-
const prevArr = this._prev;
|
|
24678
|
-
const totalIn = this._totalIn;
|
|
24679
|
-
let hasPrevMatch = this._hasPrevMatch;
|
|
24680
|
-
let prevMatchLen = this._prevMatchLen;
|
|
24681
|
-
let prevMatchDist = this._prevMatchDist;
|
|
24682
|
-
let prevLiteral = this._prevLiteral;
|
|
24683
|
-
/**
|
|
24684
|
-
* Insert a global position into the hash chain and the sliding window.
|
|
24685
|
-
*/
|
|
24686
|
-
const insertHash = (localPos) => {
|
|
24687
|
-
if (localPos + 2 >= data.length) return;
|
|
24688
|
-
const h = hash3(data[localPos], data[localPos + 1], data[localPos + 2]);
|
|
24689
|
-
const globalPos = totalIn + localPos;
|
|
24690
|
-
prevArr[globalPos & MAX_DIST - 1] = head[h];
|
|
24691
|
-
head[h] = globalPos + 1;
|
|
24692
|
-
};
|
|
24693
|
-
const insertWindow = (localPos, count) => {
|
|
24694
|
-
for (let i = 0; i < count; i++) window[wLen + i & WINDOW_SIZE - 1] = data[localPos + i];
|
|
24695
|
-
wLen += count;
|
|
24696
|
-
};
|
|
24697
|
-
let pos = 0;
|
|
24698
|
-
for (; pos < data.length;) {
|
|
24699
|
-
let bestLen = 0;
|
|
24700
|
-
let bestDist = 0;
|
|
24701
|
-
if (pos + 2 < data.length) {
|
|
24702
|
-
const h = hash3(data[pos], data[pos + 1], data[pos + 2]);
|
|
24703
|
-
const globalPos = totalIn + pos;
|
|
24704
|
-
let chainLen = MAX_CHAIN_LEN;
|
|
24705
|
-
let matchHead = head[h];
|
|
24706
|
-
while (matchHead > 0 && chainLen-- > 0) {
|
|
24707
|
-
const mGlobalPos = matchHead - 1;
|
|
24708
|
-
const dist = globalPos - mGlobalPos;
|
|
24709
|
-
if (dist > MAX_DIST || dist <= 0) break;
|
|
24710
|
-
const maxLen = Math.min(MAX_MATCH, data.length - pos);
|
|
24711
|
-
let len = 0;
|
|
24712
|
-
if (bestLen >= MIN_MATCH) {
|
|
24713
|
-
const checkOffset = mGlobalPos + bestLen;
|
|
24714
|
-
let checkByte;
|
|
24715
|
-
const checkLocal = checkOffset - totalIn;
|
|
24716
|
-
if (checkLocal >= 0 && checkLocal < data.length) checkByte = data[checkLocal];
|
|
24717
|
-
else checkByte = window[checkOffset & WINDOW_SIZE - 1];
|
|
24718
|
-
if (checkByte !== data[pos + bestLen]) {
|
|
24719
|
-
matchHead = prevArr[mGlobalPos & MAX_DIST - 1];
|
|
24720
|
-
continue;
|
|
24721
|
-
}
|
|
24722
|
-
}
|
|
24723
|
-
while (len < maxLen) {
|
|
24724
|
-
const matchOffset = mGlobalPos + len;
|
|
24725
|
-
let matchByte;
|
|
24726
|
-
const matchLocal = matchOffset - totalIn;
|
|
24727
|
-
if (matchLocal >= 0 && matchLocal < data.length) matchByte = data[matchLocal];
|
|
24728
|
-
else matchByte = window[matchOffset & WINDOW_SIZE - 1];
|
|
24729
|
-
if (matchByte !== data[pos + len]) break;
|
|
24730
|
-
len++;
|
|
24731
|
-
}
|
|
24732
|
-
if (len > bestLen) {
|
|
24733
|
-
bestLen = len;
|
|
24734
|
-
bestDist = dist;
|
|
24735
|
-
if (len >= MAX_MATCH) break;
|
|
24736
|
-
}
|
|
24737
|
-
matchHead = prevArr[mGlobalPos & MAX_DIST - 1];
|
|
24738
|
-
}
|
|
24739
|
-
prevArr[globalPos & MAX_DIST - 1] = head[h];
|
|
24740
|
-
head[h] = globalPos + 1;
|
|
24741
|
-
}
|
|
24742
|
-
if (hasPrevMatch) if (bestLen > prevMatchLen) {
|
|
24743
|
-
writeLiteralCode(out, prevLiteral);
|
|
24744
|
-
prevMatchLen = bestLen;
|
|
24745
|
-
prevMatchDist = bestDist;
|
|
24746
|
-
prevLiteral = data[pos];
|
|
24747
|
-
insertWindow(pos, 1);
|
|
24748
|
-
pos++;
|
|
24749
|
-
} else {
|
|
24750
|
-
writeLengthCode(out, prevMatchLen);
|
|
24751
|
-
writeDistanceCode(out, prevMatchDist);
|
|
24752
|
-
const matchEnd = pos - 1 + prevMatchLen;
|
|
24753
|
-
const insertEnd = Math.min(matchEnd, data.length);
|
|
24754
|
-
for (let i = pos; i < insertEnd; i++) insertHash(i);
|
|
24755
|
-
insertWindow(pos, insertEnd - pos);
|
|
24756
|
-
pos = insertEnd;
|
|
24757
|
-
hasPrevMatch = false;
|
|
24758
|
-
prevMatchLen = 0;
|
|
24759
|
-
}
|
|
24760
|
-
else if (bestLen >= MIN_MATCH) {
|
|
24761
|
-
hasPrevMatch = true;
|
|
24762
|
-
prevMatchLen = bestLen;
|
|
24763
|
-
prevMatchDist = bestDist;
|
|
24764
|
-
prevLiteral = data[pos];
|
|
24765
|
-
insertWindow(pos, 1);
|
|
24766
|
-
pos++;
|
|
24767
|
-
} else {
|
|
24768
|
-
writeLiteralCode(out, data[pos]);
|
|
24769
|
-
insertWindow(pos, 1);
|
|
24770
|
-
pos++;
|
|
24771
|
-
}
|
|
25064
|
+
if (this._level === 0) {
|
|
25065
|
+
this._writeStore(data);
|
|
25066
|
+
return out.flushBytes();
|
|
24772
25067
|
}
|
|
24773
|
-
|
|
24774
|
-
writeLengthCode(out, prevMatchLen);
|
|
24775
|
-
writeDistanceCode(out, prevMatchDist);
|
|
24776
|
-
const matchEnd = Math.min(pos - 1 + prevMatchLen, data.length);
|
|
24777
|
-
for (let i = pos; i < matchEnd; i++) insertHash(i);
|
|
24778
|
-
insertWindow(pos, matchEnd - pos);
|
|
24779
|
-
hasPrevMatch = false;
|
|
24780
|
-
prevMatchLen = 0;
|
|
24781
|
-
}
|
|
24782
|
-
writeLiteralCode(out, 256);
|
|
24783
|
-
this._windowLen = wLen;
|
|
24784
|
-
this._totalIn = totalIn + data.length;
|
|
24785
|
-
this._hasPrevMatch = hasPrevMatch;
|
|
24786
|
-
this._prevMatchLen = prevMatchLen;
|
|
24787
|
-
this._prevMatchDist = prevMatchDist;
|
|
24788
|
-
this._prevLiteral = prevLiteral;
|
|
25068
|
+
emitDynamicBlock(out, lz77Compress(data, 0, data.length, this._config, this._state), false);
|
|
24789
25069
|
return out.flushBytes();
|
|
24790
25070
|
}
|
|
24791
25071
|
/**
|
|
@@ -24799,6 +25079,29 @@ var ExcelTS = (function(exports) {
|
|
|
24799
25079
|
writeLiteralCode(out, 256);
|
|
24800
25080
|
return out.finish();
|
|
24801
25081
|
}
|
|
25082
|
+
/**
|
|
25083
|
+
* Write STORE (uncompressed) blocks for level=0.
|
|
25084
|
+
* Each block is non-final (BFINAL=0); the final block is emitted by finish().
|
|
25085
|
+
*/
|
|
25086
|
+
_writeStore(data) {
|
|
25087
|
+
const out = this._output;
|
|
25088
|
+
const MAX_BLOCK_SIZE = 65535;
|
|
25089
|
+
let offset = 0;
|
|
25090
|
+
while (offset < data.length) {
|
|
25091
|
+
const remaining = data.length - offset;
|
|
25092
|
+
const blockSize = Math.min(MAX_BLOCK_SIZE, remaining);
|
|
25093
|
+
out.alignToByte();
|
|
25094
|
+
out.writeBits(0, 1);
|
|
25095
|
+
out.writeBits(0, 2);
|
|
25096
|
+
out.alignToByte();
|
|
25097
|
+
out.writeBits(blockSize & 255, 8);
|
|
25098
|
+
out.writeBits(blockSize >> 8 & 255, 8);
|
|
25099
|
+
out.writeBits(~blockSize & 255, 8);
|
|
25100
|
+
out.writeBits(~blockSize >> 8 & 255, 8);
|
|
25101
|
+
for (let i = 0; i < blockSize; i++) out.writeBits(data[offset + i], 8);
|
|
25102
|
+
offset += blockSize;
|
|
25103
|
+
}
|
|
25104
|
+
}
|
|
24802
25105
|
};
|
|
24803
25106
|
//#endregion
|
|
24804
25107
|
//#region src/modules/archive/shared/bytes.ts
|
|
@@ -26831,7 +27134,7 @@ self.onmessage = async function(event) {
|
|
|
26831
27134
|
signal: options.signal,
|
|
26832
27135
|
allowTransfer: options.allowTransfer
|
|
26833
27136
|
});
|
|
26834
|
-
return strategy.jsFallback(data);
|
|
27137
|
+
return strategy.jsFallback(data, options.level);
|
|
26835
27138
|
}
|
|
26836
27139
|
/**
|
|
26837
27140
|
* Decompress data using browser's native DecompressionStream or JS fallback
|
|
@@ -26872,7 +27175,7 @@ self.onmessage = async function(event) {
|
|
|
26872
27175
|
*/
|
|
26873
27176
|
function zlibSync(data, options = {}) {
|
|
26874
27177
|
const level = options.level ?? 6;
|
|
26875
|
-
return wrapZlib(level === 0 ? deflateRawStore(data) : deflateRawCompressed(data), data, level);
|
|
27178
|
+
return wrapZlib(level === 0 ? deflateRawStore(data) : deflateRawCompressed(data, level), data, level);
|
|
26876
27179
|
}
|
|
26877
27180
|
//#endregion
|
|
26878
27181
|
//#region src/modules/archive/compression/streaming-compress.browser.ts
|
|
@@ -27063,8 +27366,8 @@ self.onmessage = async function(event) {
|
|
|
27063
27366
|
function createStreamCodec(type, options) {
|
|
27064
27367
|
const level = type === "deflate" ? options.level ?? 6 : void 0;
|
|
27065
27368
|
if (options.useWorker && hasWorkerSupport()) return createWorkerStreamCodec(type, options.workerPool, level, options.allowTransfer);
|
|
27066
|
-
if (hasDeflateRawWebStreams()) return createNativeWebStreamCodec("deflate-raw", type === "deflate");
|
|
27067
|
-
return new BufferedCodec(type === "deflate" ? deflateRawCompressed : inflateRaw);
|
|
27369
|
+
if (type === "deflate" ? hasDeflateRawCompressionStream() : hasDeflateRawWebStreams()) return createNativeWebStreamCodec("deflate-raw", type === "deflate");
|
|
27370
|
+
return new BufferedCodec(type === "deflate" ? (data) => deflateRawCompressed(data, level) : inflateRaw);
|
|
27068
27371
|
}
|
|
27069
27372
|
/**
|
|
27070
27373
|
* Create a streaming DEFLATE compressor
|
|
@@ -27073,6 +27376,15 @@ self.onmessage = async function(event) {
|
|
|
27073
27376
|
return createStreamCodec("deflate", options);
|
|
27074
27377
|
}
|
|
27075
27378
|
/**
|
|
27379
|
+
* Returns true when the browser supports native `CompressionStream("deflate-raw")`,
|
|
27380
|
+
* signalling that `push()` should prefer the async path over `SyncDeflater`.
|
|
27381
|
+
*
|
|
27382
|
+
* Only checks for compression support — decompression is not needed for writing.
|
|
27383
|
+
*/
|
|
27384
|
+
function hasNativeAsyncDeflate() {
|
|
27385
|
+
return hasDeflateRawCompressionStream();
|
|
27386
|
+
}
|
|
27387
|
+
/**
|
|
27076
27388
|
* Initialize ZipCrypto state with a password.
|
|
27077
27389
|
*/
|
|
27078
27390
|
function zipCryptoInitKeys(password) {
|
|
@@ -28629,6 +28941,11 @@ self.onmessage = async function(event) {
|
|
|
28629
28941
|
this._tapCallback(promise, callback);
|
|
28630
28942
|
return promise;
|
|
28631
28943
|
}
|
|
28944
|
+
if (this._completeError) {
|
|
28945
|
+
const promise = Promise.reject(this._completeError);
|
|
28946
|
+
this._tapCallback(promise, callback);
|
|
28947
|
+
return promise;
|
|
28948
|
+
}
|
|
28632
28949
|
if (this._deflateWanted === null) {
|
|
28633
28950
|
this._accumulateSampleLen(data);
|
|
28634
28951
|
if (!this._shouldDecide(final)) {
|
|
@@ -28665,7 +28982,7 @@ self.onmessage = async function(event) {
|
|
|
28665
28982
|
* memory growth when callers push data in a tight synchronous loop.
|
|
28666
28983
|
*/
|
|
28667
28984
|
push(data, final = false, callback) {
|
|
28668
|
-
if (!this._deflate && this._encryptionMethod === "none") {
|
|
28985
|
+
if (!this._deflate && this._encryptionMethod === "none" && !hasNativeAsyncDeflate()) {
|
|
28669
28986
|
try {
|
|
28670
28987
|
this._pushSyncPath(data, final);
|
|
28671
28988
|
callback?.();
|
|
@@ -28675,7 +28992,7 @@ self.onmessage = async function(event) {
|
|
|
28675
28992
|
}
|
|
28676
28993
|
return Promise.resolve();
|
|
28677
28994
|
}
|
|
28678
|
-
const promise = this._pushChain = this._pushChain.then(() => this._pushUnchained(data, final, callback));
|
|
28995
|
+
const promise = this._pushChain = this._pushChain.then(() => this._pushUnchained(data, final, callback), () => this._pushUnchained(data, final, callback));
|
|
28679
28996
|
promise.catch(() => {});
|
|
28680
28997
|
return promise;
|
|
28681
28998
|
}
|
|
@@ -28723,6 +29040,7 @@ self.onmessage = async function(event) {
|
|
|
28723
29040
|
this._syncDeflater = null;
|
|
28724
29041
|
}
|
|
28725
29042
|
this._emitDataDescriptor();
|
|
29043
|
+
if (this._completeError) throw this._completeError;
|
|
28726
29044
|
}
|
|
28727
29045
|
/**
|
|
28728
29046
|
* Emit local file header with Data Descriptor flag
|
|
@@ -34583,6 +34901,9 @@ self.onmessage = async function(event) {
|
|
|
34583
34901
|
this.finalized = false;
|
|
34584
34902
|
this._needsDrain = false;
|
|
34585
34903
|
this._drainResolvers = [];
|
|
34904
|
+
this._pendingWrites = 0;
|
|
34905
|
+
this._pendingWriteResolvers = [];
|
|
34906
|
+
this._earlyError = null;
|
|
34586
34907
|
this.level = options?.level ?? 6;
|
|
34587
34908
|
this.modTime = options?.modTime;
|
|
34588
34909
|
this.timestamps = options?.timestamps;
|
|
@@ -34602,6 +34923,13 @@ self.onmessage = async function(event) {
|
|
|
34602
34923
|
});
|
|
34603
34924
|
}
|
|
34604
34925
|
_emit(event, ...args) {
|
|
34926
|
+
if (event === "error") {
|
|
34927
|
+
const callbacks = this.events.get(event);
|
|
34928
|
+
if (!callbacks || callbacks.size === 0) {
|
|
34929
|
+
this._earlyError = args[0] instanceof Error ? args[0] : new Error(String(args[0]));
|
|
34930
|
+
return;
|
|
34931
|
+
}
|
|
34932
|
+
}
|
|
34605
34933
|
const callbacks = this.events.get(event);
|
|
34606
34934
|
if (!callbacks) return;
|
|
34607
34935
|
for (const cb of callbacks) cb(...args);
|
|
@@ -34612,9 +34940,16 @@ self.onmessage = async function(event) {
|
|
|
34612
34940
|
*/
|
|
34613
34941
|
_checkBackpressure(ok) {
|
|
34614
34942
|
if (ok instanceof Promise) {
|
|
34943
|
+
this._pendingWrites++;
|
|
34615
34944
|
ok.then((result) => {
|
|
34616
34945
|
if (result === false) this._needsDrain = true;
|
|
34617
|
-
}, () => {})
|
|
34946
|
+
}, () => {}).finally(() => {
|
|
34947
|
+
this._pendingWrites--;
|
|
34948
|
+
if (this._pendingWrites === 0) {
|
|
34949
|
+
const resolvers = this._pendingWriteResolvers.splice(0);
|
|
34950
|
+
for (const resolve of resolvers) resolve();
|
|
34951
|
+
}
|
|
34952
|
+
});
|
|
34618
34953
|
return;
|
|
34619
34954
|
}
|
|
34620
34955
|
if (ok === false) this._needsDrain = true;
|
|
@@ -34623,6 +34958,11 @@ self.onmessage = async function(event) {
|
|
|
34623
34958
|
const callbacks = this.events.get(event) || /* @__PURE__ */ new Set();
|
|
34624
34959
|
callbacks.add(callback);
|
|
34625
34960
|
this.events.set(event, callbacks);
|
|
34961
|
+
if (event === "error" && this._earlyError) {
|
|
34962
|
+
const err = this._earlyError;
|
|
34963
|
+
this._earlyError = null;
|
|
34964
|
+
callback(err);
|
|
34965
|
+
}
|
|
34626
34966
|
return this;
|
|
34627
34967
|
}
|
|
34628
34968
|
once(event, callback) {
|
|
@@ -34649,10 +34989,14 @@ self.onmessage = async function(event) {
|
|
|
34649
34989
|
}
|
|
34650
34990
|
/**
|
|
34651
34991
|
* Wait for the downstream writable to drain if it signaled backpressure.
|
|
34652
|
-
*
|
|
34992
|
+
* If any write() calls are still in-flight (returned a Promise that hasn't
|
|
34993
|
+
* settled), waits for all of them first so the backpressure signal isn't missed.
|
|
34653
34994
|
*/
|
|
34654
|
-
waitForDrain() {
|
|
34655
|
-
if (
|
|
34995
|
+
async waitForDrain() {
|
|
34996
|
+
if (this._pendingWrites > 0) await new Promise((resolve) => {
|
|
34997
|
+
this._pendingWriteResolvers.push(resolve);
|
|
34998
|
+
});
|
|
34999
|
+
if (!this._needsDrain || !this.pipedStream) return;
|
|
34656
35000
|
return new Promise((resolve) => {
|
|
34657
35001
|
this._drainResolvers.push(resolve);
|
|
34658
35002
|
});
|
|
@@ -39531,7 +39875,7 @@ onmessage = async (ev) => {
|
|
|
39531
39875
|
* @param shouldSkipEmpty - true, false, or "greedy"
|
|
39532
39876
|
* @returns true if the row should be skipped
|
|
39533
39877
|
*/
|
|
39534
|
-
function isEmptyRow(row, shouldSkipEmpty) {
|
|
39878
|
+
function isEmptyRow$1(row, shouldSkipEmpty) {
|
|
39535
39879
|
if (!shouldSkipEmpty) return false;
|
|
39536
39880
|
if (shouldSkipEmpty === "greedy") {
|
|
39537
39881
|
for (const field of row) if (NON_WHITESPACE_REGEX.test(field)) return false;
|
|
@@ -39546,7 +39890,7 @@ onmessage = async (ev) => {
|
|
|
39546
39890
|
* @returns true if all fields are empty strings
|
|
39547
39891
|
*/
|
|
39548
39892
|
function hasAllEmptyValues(row) {
|
|
39549
|
-
return isEmptyRow(row, true);
|
|
39893
|
+
return isEmptyRow$1(row, true);
|
|
39550
39894
|
}
|
|
39551
39895
|
//#endregion
|
|
39552
39896
|
//#region src/modules/csv/errors.ts
|
|
@@ -40497,7 +40841,7 @@ onmessage = async (ev) => {
|
|
|
40497
40841
|
*/
|
|
40498
40842
|
function shouldSkipRow(row, comment, shouldSkipEmpty, skipRecordsWithEmptyValues) {
|
|
40499
40843
|
if (comment && row[0]?.trimStart().startsWith(comment)) return true;
|
|
40500
|
-
if (isEmptyRow(row, shouldSkipEmpty)) return true;
|
|
40844
|
+
if (isEmptyRow$1(row, shouldSkipEmpty)) return true;
|
|
40501
40845
|
if (skipRecordsWithEmptyValues && hasAllEmptyValues(row)) return true;
|
|
40502
40846
|
return false;
|
|
40503
40847
|
}
|
|
@@ -41305,7 +41649,7 @@ onmessage = async (ev) => {
|
|
|
41305
41649
|
currentCharOffset += lineCharLength;
|
|
41306
41650
|
continue;
|
|
41307
41651
|
}
|
|
41308
|
-
if (config.shouldSkipEmpty && isEmptyRow(trimmedRow, config.shouldSkipEmpty)) {
|
|
41652
|
+
if (config.shouldSkipEmpty && isEmptyRow$1(trimmedRow, config.shouldSkipEmpty)) {
|
|
41309
41653
|
currentCharOffset += lineCharLength;
|
|
41310
41654
|
continue;
|
|
41311
41655
|
}
|
|
@@ -41369,7 +41713,7 @@ onmessage = async (ev) => {
|
|
|
41369
41713
|
pos = scanResult.endPos;
|
|
41370
41714
|
continue;
|
|
41371
41715
|
}
|
|
41372
|
-
if (config.shouldSkipEmpty && isEmptyRow(row, config.shouldSkipEmpty)) {
|
|
41716
|
+
if (config.shouldSkipEmpty && isEmptyRow$1(row, config.shouldSkipEmpty)) {
|
|
41373
41717
|
pos = scanResult.endPos;
|
|
41374
41718
|
continue;
|
|
41375
41719
|
}
|
|
@@ -42751,6 +43095,545 @@ onmessage = async (ev) => {
|
|
|
42751
43095
|
return new CsvFormatterStream(options);
|
|
42752
43096
|
}
|
|
42753
43097
|
//#endregion
|
|
43098
|
+
//#region src/modules/markdown/constants.ts
|
|
43099
|
+
/**
|
|
43100
|
+
* Markdown Module Constants
|
|
43101
|
+
*
|
|
43102
|
+
* Shared constants used across the Markdown module.
|
|
43103
|
+
*/
|
|
43104
|
+
/**
|
|
43105
|
+
* Pre-compiled regex for line splitting (matches CR, LF, or CRLF)
|
|
43106
|
+
*/
|
|
43107
|
+
const LINEBREAK_REGEX = /\r\n|\r|\n/;
|
|
43108
|
+
/**
|
|
43109
|
+
* Characters that need escaping in Markdown table cells.
|
|
43110
|
+
* Also matches CRLF/CR/LF so that escaping + newline conversion
|
|
43111
|
+
* can be done in a single `replace()` call.
|
|
43112
|
+
* Note: `\r\n` must come before `\r` to match CRLF as a single unit.
|
|
43113
|
+
*/
|
|
43114
|
+
const ESCAPE_AND_NEWLINE = /\r\n|[|\\\r\n]/g;
|
|
43115
|
+
/**
|
|
43116
|
+
* Regex to unescape Markdown table cell content (`\|` → `|`, `\\` → `\`)
|
|
43117
|
+
*/
|
|
43118
|
+
const UNESCAPE_REGEX = /\\([|\\])/g;
|
|
43119
|
+
/**
|
|
43120
|
+
* Regex to match `<br>`, `<br/>`, or `<br />` tags (case-insensitive).
|
|
43121
|
+
* Used to convert multiline cell representations back to newlines during parsing.
|
|
43122
|
+
*/
|
|
43123
|
+
const BR_TAG_REGEX = /<br\s*\/?>/gi;
|
|
43124
|
+
/**
|
|
43125
|
+
* Regex to match literal newlines (CR, LF, or CRLF) in cell content.
|
|
43126
|
+
* Used when escaping is disabled but newline conversion is still needed.
|
|
43127
|
+
*/
|
|
43128
|
+
const NEWLINE_IN_CELL = /\r\n|\r|\n/g;
|
|
43129
|
+
//#endregion
|
|
43130
|
+
//#region src/modules/markdown/errors.ts
|
|
43131
|
+
/**
|
|
43132
|
+
* Markdown module error types.
|
|
43133
|
+
*/
|
|
43134
|
+
/**
|
|
43135
|
+
* Base class for all Markdown-related errors.
|
|
43136
|
+
*/
|
|
43137
|
+
var MarkdownError = class extends BaseError {
|
|
43138
|
+
constructor(..._args) {
|
|
43139
|
+
super(..._args);
|
|
43140
|
+
this.name = "MarkdownError";
|
|
43141
|
+
}
|
|
43142
|
+
};
|
|
43143
|
+
/**
|
|
43144
|
+
* Error thrown when Markdown parsing fails.
|
|
43145
|
+
*/
|
|
43146
|
+
var MarkdownParseError = class extends MarkdownError {
|
|
43147
|
+
constructor(message, line, options) {
|
|
43148
|
+
super(`Line ${line}: ${message}`, options);
|
|
43149
|
+
this.name = "MarkdownParseError";
|
|
43150
|
+
this.line = line;
|
|
43151
|
+
}
|
|
43152
|
+
};
|
|
43153
|
+
//#endregion
|
|
43154
|
+
//#region src/modules/markdown/parse/index.ts
|
|
43155
|
+
const CH_PIPE = 124;
|
|
43156
|
+
const CH_BACKSLASH = 92;
|
|
43157
|
+
const CH_COLON = 58;
|
|
43158
|
+
const CH_DASH = 45;
|
|
43159
|
+
const CH_SPACE = 32;
|
|
43160
|
+
const CH_TAB = 9;
|
|
43161
|
+
function resolveParseOpts(options) {
|
|
43162
|
+
return {
|
|
43163
|
+
trim: options.trim !== false,
|
|
43164
|
+
unescape: options.unescape !== false,
|
|
43165
|
+
skipEmpty: options.skipEmptyRows !== false,
|
|
43166
|
+
maxRows: options.maxRows,
|
|
43167
|
+
convertBr: options.convertBr === true
|
|
43168
|
+
};
|
|
43169
|
+
}
|
|
43170
|
+
/**
|
|
43171
|
+
* Split a Markdown table row into cell values.
|
|
43172
|
+
* Handles escaped pipes (`\|`) correctly by scanning character by character.
|
|
43173
|
+
*
|
|
43174
|
+
* Optimized: uses start/end index tracking with `slice()` instead of
|
|
43175
|
+
* character-by-character string concatenation to avoid O(n²) worst case.
|
|
43176
|
+
*/
|
|
43177
|
+
function splitRow(line) {
|
|
43178
|
+
const cells = [];
|
|
43179
|
+
const len = line.length;
|
|
43180
|
+
let i = len > 0 && line.charCodeAt(0) === CH_PIPE ? 1 : 0;
|
|
43181
|
+
let end = len;
|
|
43182
|
+
if (len > 1 && line.charCodeAt(len - 1) === CH_PIPE) {
|
|
43183
|
+
let backslashCount = 0;
|
|
43184
|
+
let k = len - 2;
|
|
43185
|
+
while (k >= 0 && line.charCodeAt(k) === CH_BACKSLASH) {
|
|
43186
|
+
backslashCount++;
|
|
43187
|
+
k--;
|
|
43188
|
+
}
|
|
43189
|
+
if (backslashCount % 2 === 0) end = len - 1;
|
|
43190
|
+
}
|
|
43191
|
+
let segStart = i;
|
|
43192
|
+
let current = "";
|
|
43193
|
+
let hasEscape = false;
|
|
43194
|
+
while (i < end) {
|
|
43195
|
+
const ch = line.charCodeAt(i);
|
|
43196
|
+
if (ch === CH_BACKSLASH && i + 1 < end) {
|
|
43197
|
+
hasEscape = true;
|
|
43198
|
+
if (i > segStart) current += line.slice(segStart, i);
|
|
43199
|
+
current += line.slice(i, i + 2);
|
|
43200
|
+
i += 2;
|
|
43201
|
+
segStart = i;
|
|
43202
|
+
} else if (ch === CH_PIPE) {
|
|
43203
|
+
if (hasEscape) {
|
|
43204
|
+
if (i > segStart) current += line.slice(segStart, i);
|
|
43205
|
+
cells.push(current);
|
|
43206
|
+
current = "";
|
|
43207
|
+
hasEscape = false;
|
|
43208
|
+
} else cells.push(line.slice(segStart, i));
|
|
43209
|
+
i++;
|
|
43210
|
+
segStart = i;
|
|
43211
|
+
} else i++;
|
|
43212
|
+
}
|
|
43213
|
+
if (hasEscape) {
|
|
43214
|
+
if (end > segStart) current += line.slice(segStart, end);
|
|
43215
|
+
cells.push(current);
|
|
43216
|
+
} else cells.push(line.slice(segStart, end));
|
|
43217
|
+
return cells;
|
|
43218
|
+
}
|
|
43219
|
+
/**
|
|
43220
|
+
* Determine column alignment from a separator cell.
|
|
43221
|
+
*
|
|
43222
|
+
* - `:---:` → center
|
|
43223
|
+
* - `:---` → left
|
|
43224
|
+
* - `---:` → right
|
|
43225
|
+
* - `---` → none
|
|
43226
|
+
*/
|
|
43227
|
+
function parseAlignment(cell) {
|
|
43228
|
+
const trimmed = cell.trim();
|
|
43229
|
+
const tLen = trimmed.length;
|
|
43230
|
+
if (tLen === 0) return "none";
|
|
43231
|
+
const leftColon = trimmed.charCodeAt(0) === CH_COLON;
|
|
43232
|
+
const rightColon = trimmed.charCodeAt(tLen - 1) === CH_COLON;
|
|
43233
|
+
if (leftColon && rightColon) return "center";
|
|
43234
|
+
if (leftColon) return "left";
|
|
43235
|
+
if (rightColon) return "right";
|
|
43236
|
+
return "none";
|
|
43237
|
+
}
|
|
43238
|
+
/**
|
|
43239
|
+
* Check if a cell string is a valid separator cell.
|
|
43240
|
+
* Hand-rolled check replacing regex for better performance.
|
|
43241
|
+
* Pattern: optional whitespace, optional colon, one or more dashes, optional colon, optional whitespace.
|
|
43242
|
+
*/
|
|
43243
|
+
function isSeparatorCell(cell) {
|
|
43244
|
+
const len = cell.length;
|
|
43245
|
+
let i = 0;
|
|
43246
|
+
while (i < len) {
|
|
43247
|
+
const ch = cell.charCodeAt(i);
|
|
43248
|
+
if (ch !== CH_SPACE && ch !== CH_TAB) break;
|
|
43249
|
+
i++;
|
|
43250
|
+
}
|
|
43251
|
+
if (i < len && cell.charCodeAt(i) === CH_COLON) i++;
|
|
43252
|
+
const dashStart = i;
|
|
43253
|
+
while (i < len && cell.charCodeAt(i) === CH_DASH) i++;
|
|
43254
|
+
if (i === dashStart) return false;
|
|
43255
|
+
if (i < len && cell.charCodeAt(i) === CH_COLON) i++;
|
|
43256
|
+
while (i < len) {
|
|
43257
|
+
const ch = cell.charCodeAt(i);
|
|
43258
|
+
if (ch !== CH_SPACE && ch !== CH_TAB) return false;
|
|
43259
|
+
i++;
|
|
43260
|
+
}
|
|
43261
|
+
return true;
|
|
43262
|
+
}
|
|
43263
|
+
/**
|
|
43264
|
+
* Check if a line is a valid separator row.
|
|
43265
|
+
* A separator row consists entirely of cells matching the pattern `:?-+:?`.
|
|
43266
|
+
*/
|
|
43267
|
+
function isSeparatorRow(cells) {
|
|
43268
|
+
if (cells.length === 0) return false;
|
|
43269
|
+
for (let i = 0; i < cells.length; i++) if (!isSeparatorCell(cells[i])) return false;
|
|
43270
|
+
return true;
|
|
43271
|
+
}
|
|
43272
|
+
/**
|
|
43273
|
+
* Process cell content: trim, optionally unescape, and optionally convert `<br>` to newlines.
|
|
43274
|
+
*/
|
|
43275
|
+
function processCell(value, opts) {
|
|
43276
|
+
let result = opts.trim ? value.trim() : value;
|
|
43277
|
+
if (opts.unescape) result = result.replace(UNESCAPE_REGEX, "$1");
|
|
43278
|
+
if (opts.convertBr) result = result.replace(BR_TAG_REGEX, "\n");
|
|
43279
|
+
return result;
|
|
43280
|
+
}
|
|
43281
|
+
/**
|
|
43282
|
+
* Normalize a row to the expected column count.
|
|
43283
|
+
* - If row has fewer cells, pad with empty strings
|
|
43284
|
+
* - If row has more cells, truncate
|
|
43285
|
+
*/
|
|
43286
|
+
function normalizeRow(cells, columnCount, opts) {
|
|
43287
|
+
const row = new Array(columnCount);
|
|
43288
|
+
for (let i = 0; i < columnCount; i++) row[i] = i < cells.length ? processCell(cells[i], opts) : "";
|
|
43289
|
+
return row;
|
|
43290
|
+
}
|
|
43291
|
+
/**
|
|
43292
|
+
* Check if a row is empty (all cells are empty strings).
|
|
43293
|
+
*/
|
|
43294
|
+
function isEmptyRow(row) {
|
|
43295
|
+
for (let i = 0; i < row.length; i++) if (row[i] !== "") return false;
|
|
43296
|
+
return true;
|
|
43297
|
+
}
|
|
43298
|
+
/**
|
|
43299
|
+
* Check if a line could be part of a table (contains a pipe character).
|
|
43300
|
+
*/
|
|
43301
|
+
function isTableLine(line) {
|
|
43302
|
+
return line.indexOf("|") !== -1;
|
|
43303
|
+
}
|
|
43304
|
+
/**
|
|
43305
|
+
* Check if a line starts with a pipe (after optional leading whitespace).
|
|
43306
|
+
* Used to determine the table's "piped" style for data row validation.
|
|
43307
|
+
*/
|
|
43308
|
+
function startsWithPipe(line) {
|
|
43309
|
+
const len = line.length;
|
|
43310
|
+
let i = 0;
|
|
43311
|
+
while (i < len) {
|
|
43312
|
+
const ch = line.charCodeAt(i);
|
|
43313
|
+
if (ch !== CH_SPACE && ch !== CH_TAB) return ch === CH_PIPE;
|
|
43314
|
+
i++;
|
|
43315
|
+
}
|
|
43316
|
+
return false;
|
|
43317
|
+
}
|
|
43318
|
+
/**
|
|
43319
|
+
* Check if a line could be a separator candidate (contains a dash).
|
|
43320
|
+
*/
|
|
43321
|
+
function hasDash(line) {
|
|
43322
|
+
return line.indexOf("-") !== -1;
|
|
43323
|
+
}
|
|
43324
|
+
/**
|
|
43325
|
+
* Attempt to parse a table starting at line index `startLine`.
|
|
43326
|
+
*
|
|
43327
|
+
* Returns `{ result, endLine }` if a valid table starts here, or `null` otherwise.
|
|
43328
|
+
*/
|
|
43329
|
+
function parseTableAt(lines, startLine, lineCount, opts) {
|
|
43330
|
+
if (startLine >= lineCount - 1) return null;
|
|
43331
|
+
const line = lines[startLine].trim();
|
|
43332
|
+
if (line === "" || !isTableLine(line)) return null;
|
|
43333
|
+
const headerCells = splitRow(line);
|
|
43334
|
+
if (headerCells.length < 1) return null;
|
|
43335
|
+
const separatorLine = lines[startLine + 1].trim();
|
|
43336
|
+
if (separatorLine === "" || !hasDash(separatorLine)) return null;
|
|
43337
|
+
const separatorCells = splitRow(separatorLine);
|
|
43338
|
+
if (!isSeparatorRow(separatorCells)) return null;
|
|
43339
|
+
const columnCount = headerCells.length;
|
|
43340
|
+
const headers = new Array(columnCount);
|
|
43341
|
+
const alignments = new Array(columnCount);
|
|
43342
|
+
for (let c = 0; c < columnCount; c++) {
|
|
43343
|
+
headers[c] = processCell(headerCells[c], opts);
|
|
43344
|
+
alignments[c] = c < separatorCells.length ? parseAlignment(separatorCells[c]) : "none";
|
|
43345
|
+
}
|
|
43346
|
+
const piped = startsWithPipe(line);
|
|
43347
|
+
const rows = [];
|
|
43348
|
+
let j = startLine + 2;
|
|
43349
|
+
for (; j < lineCount; j++) {
|
|
43350
|
+
const dataLine = lines[j].trim();
|
|
43351
|
+
if (dataLine === "" || !isTableLine(dataLine)) break;
|
|
43352
|
+
if (piped && !startsWithPipe(dataLine)) break;
|
|
43353
|
+
if (opts.maxRows !== void 0 && rows.length >= opts.maxRows) {
|
|
43354
|
+
while (j < lineCount) {
|
|
43355
|
+
const remaining = lines[j].trim();
|
|
43356
|
+
if (remaining === "" || !isTableLine(remaining)) break;
|
|
43357
|
+
if (piped && !startsWithPipe(remaining)) break;
|
|
43358
|
+
j++;
|
|
43359
|
+
}
|
|
43360
|
+
break;
|
|
43361
|
+
}
|
|
43362
|
+
const row = normalizeRow(splitRow(dataLine), columnCount, opts);
|
|
43363
|
+
if (opts.skipEmpty && isEmptyRow(row)) continue;
|
|
43364
|
+
rows.push(row);
|
|
43365
|
+
}
|
|
43366
|
+
return {
|
|
43367
|
+
result: {
|
|
43368
|
+
headers,
|
|
43369
|
+
rows,
|
|
43370
|
+
alignments
|
|
43371
|
+
},
|
|
43372
|
+
endLine: j
|
|
43373
|
+
};
|
|
43374
|
+
}
|
|
43375
|
+
/**
|
|
43376
|
+
* Parse a Markdown table string into structured data.
|
|
43377
|
+
*
|
|
43378
|
+
* The parser looks for the GFM table pattern:
|
|
43379
|
+
* 1. A header row (pipe-delimited cells)
|
|
43380
|
+
* 2. A separator row (dashes with optional colons for alignment)
|
|
43381
|
+
* 3. Zero or more data rows
|
|
43382
|
+
*
|
|
43383
|
+
* Non-table content before and after the table is ignored.
|
|
43384
|
+
*
|
|
43385
|
+
* @param input - Markdown string containing a table
|
|
43386
|
+
* @param options - Parse options
|
|
43387
|
+
* @returns Parsed table data with headers, rows, and alignments
|
|
43388
|
+
*
|
|
43389
|
+
* @throws {MarkdownParseError} When no valid table is found in the input
|
|
43390
|
+
*
|
|
43391
|
+
* @example
|
|
43392
|
+
* ```ts
|
|
43393
|
+
* // Basic table
|
|
43394
|
+
* const result = parseMarkdown("| Name | Age |\n| --- | --- |\n| Alice | 30 |");
|
|
43395
|
+
*
|
|
43396
|
+
* // With alignment
|
|
43397
|
+
* const result = parseMarkdown("| Left | Center | Right |\n|:---|:---:|---:|\n|a|b|c|");
|
|
43398
|
+
* // result.alignments = ["left", "center", "right"]
|
|
43399
|
+
*
|
|
43400
|
+
* // From a larger Markdown document
|
|
43401
|
+
* const result = parseMarkdown(markdownDoc); // Finds the first table
|
|
43402
|
+
*
|
|
43403
|
+
* // With options
|
|
43404
|
+
* const result = parseMarkdown(input, { trim: false, maxRows: 100 });
|
|
43405
|
+
* ```
|
|
43406
|
+
*/
|
|
43407
|
+
function parseMarkdown(input, options = {}) {
|
|
43408
|
+
const opts = resolveParseOpts(options);
|
|
43409
|
+
const lines = input.split(LINEBREAK_REGEX);
|
|
43410
|
+
const lineCount = lines.length;
|
|
43411
|
+
for (let i = 0; i < lineCount - 1; i++) {
|
|
43412
|
+
const parsed = parseTableAt(lines, i, lineCount, opts);
|
|
43413
|
+
if (parsed) return parsed.result;
|
|
43414
|
+
}
|
|
43415
|
+
throw new MarkdownParseError("No valid Markdown table found in input", lineCount > 0 ? lineCount : 1);
|
|
43416
|
+
}
|
|
43417
|
+
/**
|
|
43418
|
+
* Parse all Markdown tables from a document.
|
|
43419
|
+
*
|
|
43420
|
+
* @param input - Markdown string containing one or more tables
|
|
43421
|
+
* @param options - Parse options (maxRows applies per table)
|
|
43422
|
+
* @returns Array of parsed tables
|
|
43423
|
+
*
|
|
43424
|
+
* @example
|
|
43425
|
+
* ```ts
|
|
43426
|
+
* const tables = parseMarkdownAll(markdownDoc);
|
|
43427
|
+
* console.log(`Found ${tables.length} tables`);
|
|
43428
|
+
* tables.forEach((t, i) => console.log(`Table ${i}: ${t.headers.join(", ")}`));
|
|
43429
|
+
* ```
|
|
43430
|
+
*/
|
|
43431
|
+
function parseMarkdownAll(input, options = {}) {
|
|
43432
|
+
const opts = resolveParseOpts(options);
|
|
43433
|
+
const lines = input.split(LINEBREAK_REGEX);
|
|
43434
|
+
const lineCount = lines.length;
|
|
43435
|
+
const tables = [];
|
|
43436
|
+
let i = 0;
|
|
43437
|
+
while (i < lineCount - 1) {
|
|
43438
|
+
const parsed = parseTableAt(lines, i, lineCount, opts);
|
|
43439
|
+
if (parsed) {
|
|
43440
|
+
tables.push(parsed.result);
|
|
43441
|
+
i = parsed.endLine;
|
|
43442
|
+
} else i++;
|
|
43443
|
+
}
|
|
43444
|
+
return tables;
|
|
43445
|
+
}
|
|
43446
|
+
//#endregion
|
|
43447
|
+
//#region src/modules/markdown/format/index.ts
|
|
43448
|
+
/**
|
|
43449
|
+
* Calculate the display width of a string in a monospace terminal.
|
|
43450
|
+
* CJK characters, fullwidth forms, and most emoji are 2 columns wide.
|
|
43451
|
+
* This enables proper column alignment in tables containing these characters.
|
|
43452
|
+
*/
|
|
43453
|
+
function displayWidth(str) {
|
|
43454
|
+
let width = 0;
|
|
43455
|
+
for (let i = 0; i < str.length; i++) {
|
|
43456
|
+
const code = str.charCodeAt(i);
|
|
43457
|
+
if (code >= 55296 && code <= 56319 && i + 1 < str.length) {
|
|
43458
|
+
const low = str.charCodeAt(i + 1);
|
|
43459
|
+
if (low >= 56320 && low <= 57343) {
|
|
43460
|
+
width += 2;
|
|
43461
|
+
i++;
|
|
43462
|
+
continue;
|
|
43463
|
+
}
|
|
43464
|
+
}
|
|
43465
|
+
if (code === 8203 || code === 8204 || code === 8205 || code === 65279) continue;
|
|
43466
|
+
if (code >= 768 && code <= 879 || code >= 6832 && code <= 6911 || code >= 7616 && code <= 7679 || code >= 8400 && code <= 8447 || code >= 65056 && code <= 65071) continue;
|
|
43467
|
+
if (code >= 4352 && code <= 4447 || code >= 11904 && code <= 12350 || code >= 12352 && code <= 13247 || code >= 13312 && code <= 19903 || code >= 19968 && code <= 42191 || code >= 43360 && code <= 43391 || code >= 44032 && code <= 55215 || code >= 63744 && code <= 64255 || code >= 65040 && code <= 65049 || code >= 65072 && code <= 65135 || code >= 65281 && code <= 65376 || code >= 65504 && code <= 65510) {
|
|
43468
|
+
width += 2;
|
|
43469
|
+
continue;
|
|
43470
|
+
}
|
|
43471
|
+
width += 1;
|
|
43472
|
+
}
|
|
43473
|
+
return width;
|
|
43474
|
+
}
|
|
43475
|
+
/**
|
|
43476
|
+
* Default value-to-string converter.
|
|
43477
|
+
*/
|
|
43478
|
+
function defaultStringify(value) {
|
|
43479
|
+
if (value === null || value === void 0) return "";
|
|
43480
|
+
if (value instanceof Date) return value.toISOString();
|
|
43481
|
+
if (typeof value === "object") try {
|
|
43482
|
+
return JSON.stringify(value);
|
|
43483
|
+
} catch {
|
|
43484
|
+
return "[object Object]";
|
|
43485
|
+
}
|
|
43486
|
+
return String(value);
|
|
43487
|
+
}
|
|
43488
|
+
/**
|
|
43489
|
+
* Escape pipe characters, backslashes, and convert newlines to `<br>` in a single pass.
|
|
43490
|
+
* `|` → `\|`, `\` → `\\`, `\r\n`/`\r`/`\n` → `<br>`
|
|
43491
|
+
*/
|
|
43492
|
+
function escapeCell(value) {
|
|
43493
|
+
return value.replace(ESCAPE_AND_NEWLINE, (ch) => ch === "|" || ch === "\\" ? "\\" + ch : "<br>");
|
|
43494
|
+
}
|
|
43495
|
+
/**
|
|
43496
|
+
* Convert literal newlines to `<br>` without escaping pipes/backslashes.
|
|
43497
|
+
*/
|
|
43498
|
+
function convertNewlines(value) {
|
|
43499
|
+
if (value.indexOf("\n") !== -1 || value.indexOf("\r") !== -1) return value.replace(NEWLINE_IN_CELL, "<br>");
|
|
43500
|
+
return value;
|
|
43501
|
+
}
|
|
43502
|
+
/**
|
|
43503
|
+
* Build the separator cell for a column based on alignment and width.
|
|
43504
|
+
*
|
|
43505
|
+
* Examples (width=5):
|
|
43506
|
+
* - none: `-----`
|
|
43507
|
+
* - left: `:----`
|
|
43508
|
+
* - right: `----:`
|
|
43509
|
+
* - center: `:---:`
|
|
43510
|
+
*/
|
|
43511
|
+
function buildSeparator(alignment, width) {
|
|
43512
|
+
switch (alignment) {
|
|
43513
|
+
case "left": return ":" + "-".repeat(width - 1);
|
|
43514
|
+
case "right": return "-".repeat(width - 1) + ":";
|
|
43515
|
+
case "center": return ":" + "-".repeat(Math.max(width - 2, 1)) + ":";
|
|
43516
|
+
default: return "-".repeat(width);
|
|
43517
|
+
}
|
|
43518
|
+
}
|
|
43519
|
+
/**
|
|
43520
|
+
* Pad a cell value to the target display width with alignment.
|
|
43521
|
+
* Uses displayWidth() for proper CJK/emoji handling.
|
|
43522
|
+
*/
|
|
43523
|
+
function padCell(value, targetWidth, alignment) {
|
|
43524
|
+
const len = displayWidth(value);
|
|
43525
|
+
if (len >= targetWidth) return value;
|
|
43526
|
+
const diff = targetWidth - len;
|
|
43527
|
+
switch (alignment) {
|
|
43528
|
+
case "right": return " ".repeat(diff) + value;
|
|
43529
|
+
case "center": {
|
|
43530
|
+
const left = Math.floor(diff / 2);
|
|
43531
|
+
const right = diff - left;
|
|
43532
|
+
return " ".repeat(left) + value + " ".repeat(right);
|
|
43533
|
+
}
|
|
43534
|
+
default: return value + " ".repeat(diff);
|
|
43535
|
+
}
|
|
43536
|
+
}
|
|
43537
|
+
/**
|
|
43538
|
+
* Resolve column configuration from options.
|
|
43539
|
+
*/
|
|
43540
|
+
function resolveColumns(headers, options) {
|
|
43541
|
+
const columnCount = headers.length;
|
|
43542
|
+
const defaultAlignment = options.alignment ?? "left";
|
|
43543
|
+
const displayHeaders = new Array(columnCount);
|
|
43544
|
+
const alignments = new Array(columnCount);
|
|
43545
|
+
const minWidths = new Array(columnCount);
|
|
43546
|
+
if (options.columns && options.columns.length > 0) for (let i = 0; i < columnCount; i++) {
|
|
43547
|
+
const col = i < options.columns.length ? options.columns[i] : void 0;
|
|
43548
|
+
if (typeof col === "string") {
|
|
43549
|
+
displayHeaders[i] = col;
|
|
43550
|
+
alignments[i] = defaultAlignment;
|
|
43551
|
+
minWidths[i] = 3;
|
|
43552
|
+
} else if (col) {
|
|
43553
|
+
displayHeaders[i] = col.header;
|
|
43554
|
+
alignments[i] = col.alignment ?? defaultAlignment;
|
|
43555
|
+
minWidths[i] = col.minWidth ?? 3;
|
|
43556
|
+
} else {
|
|
43557
|
+
displayHeaders[i] = headers[i] ?? "";
|
|
43558
|
+
alignments[i] = defaultAlignment;
|
|
43559
|
+
minWidths[i] = 3;
|
|
43560
|
+
}
|
|
43561
|
+
}
|
|
43562
|
+
else for (let i = 0; i < columnCount; i++) {
|
|
43563
|
+
displayHeaders[i] = headers[i] ?? "";
|
|
43564
|
+
alignments[i] = defaultAlignment;
|
|
43565
|
+
minWidths[i] = 3;
|
|
43566
|
+
}
|
|
43567
|
+
return {
|
|
43568
|
+
displayHeaders,
|
|
43569
|
+
alignments,
|
|
43570
|
+
minWidths
|
|
43571
|
+
};
|
|
43572
|
+
}
|
|
43573
|
+
/**
|
|
43574
|
+
* Format data as a Markdown table string.
|
|
43575
|
+
*
|
|
43576
|
+
* @param headers - Column header strings
|
|
43577
|
+
* @param rows - Data rows (each row is an array of cell values)
|
|
43578
|
+
* @param options - Formatting options
|
|
43579
|
+
* @returns Formatted Markdown table string
|
|
43580
|
+
*
|
|
43581
|
+
* @example
|
|
43582
|
+
* ```ts
|
|
43583
|
+
* formatMarkdown(
|
|
43584
|
+
* ["Name", "Age", "City"],
|
|
43585
|
+
* [
|
|
43586
|
+
* ["Alice", 30, "New York"],
|
|
43587
|
+
* ["Bob", 25, "London"]
|
|
43588
|
+
* ]
|
|
43589
|
+
* );
|
|
43590
|
+
* ```
|
|
43591
|
+
*/
|
|
43592
|
+
function formatMarkdown(headers, rows, options = {}) {
|
|
43593
|
+
const { padding = true, trailingNewline = true, escapeContent = true, stringify = defaultStringify } = options;
|
|
43594
|
+
const columnCount = headers.length;
|
|
43595
|
+
if (columnCount === 0) return "";
|
|
43596
|
+
const { displayHeaders, alignments, minWidths } = resolveColumns(headers, options);
|
|
43597
|
+
const headerStrings = new Array(columnCount);
|
|
43598
|
+
const widths = new Array(columnCount);
|
|
43599
|
+
for (let i = 0; i < columnCount; i++) {
|
|
43600
|
+
const h = escapeContent ? escapeCell(displayHeaders[i]) : convertNewlines(displayHeaders[i]);
|
|
43601
|
+
headerStrings[i] = h;
|
|
43602
|
+
widths[i] = padding ? Math.max(displayWidth(h), minWidths[i]) : Math.max(minWidths[i], 3);
|
|
43603
|
+
}
|
|
43604
|
+
const rowStrings = new Array(rows.length);
|
|
43605
|
+
for (let r = 0; r < rows.length; r++) {
|
|
43606
|
+
const row = rows[r];
|
|
43607
|
+
const cells = new Array(columnCount);
|
|
43608
|
+
for (let c = 0; c < columnCount; c++) {
|
|
43609
|
+
const raw = c < row.length ? stringify(row[c]) : "";
|
|
43610
|
+
const cell = escapeContent ? escapeCell(raw) : convertNewlines(raw);
|
|
43611
|
+
cells[c] = cell;
|
|
43612
|
+
if (padding) {
|
|
43613
|
+
const cellWidth = displayWidth(cell);
|
|
43614
|
+
if (cellWidth > widths[c]) widths[c] = cellWidth;
|
|
43615
|
+
}
|
|
43616
|
+
}
|
|
43617
|
+
rowStrings[r] = cells;
|
|
43618
|
+
}
|
|
43619
|
+
const totalLines = 2 + rowStrings.length;
|
|
43620
|
+
const lines = new Array(totalLines);
|
|
43621
|
+
const headerParts = new Array(columnCount);
|
|
43622
|
+
for (let c = 0; c < columnCount; c++) headerParts[c] = padding ? " " + padCell(headerStrings[c], widths[c], alignments[c]) + " " : " " + headerStrings[c] + " ";
|
|
43623
|
+
lines[0] = "|" + headerParts.join("|") + "|";
|
|
43624
|
+
const sepParts = new Array(columnCount);
|
|
43625
|
+
for (let c = 0; c < columnCount; c++) sepParts[c] = buildSeparator(alignments[c], widths[c] + 2);
|
|
43626
|
+
lines[1] = "|" + sepParts.join("|") + "|";
|
|
43627
|
+
for (let r = 0; r < rowStrings.length; r++) {
|
|
43628
|
+
const rowParts = new Array(columnCount);
|
|
43629
|
+
for (let c = 0; c < columnCount; c++) rowParts[c] = padding ? " " + padCell(rowStrings[r][c], widths[c], alignments[c]) + " " : " " + rowStrings[r][c] + " ";
|
|
43630
|
+
lines[r + 2] = "|" + rowParts.join("|") + "|";
|
|
43631
|
+
}
|
|
43632
|
+
let result = lines.join("\n");
|
|
43633
|
+
if (trailingNewline) result += "\n";
|
|
43634
|
+
return result;
|
|
43635
|
+
}
|
|
43636
|
+
//#endregion
|
|
42754
43637
|
//#region src/modules/excel/workbook.browser.ts
|
|
42755
43638
|
/**
|
|
42756
43639
|
* Workbook - Cross-platform Excel Workbook (Browser Version)
|
|
@@ -42821,6 +43704,33 @@ onmessage = async (ev) => {
|
|
|
42821
43704
|
return value;
|
|
42822
43705
|
};
|
|
42823
43706
|
}
|
|
43707
|
+
/**
|
|
43708
|
+
* Create a stringify function for Markdown output.
|
|
43709
|
+
* Handles hyperlinks, formulas, rich text, dates, errors, and objects.
|
|
43710
|
+
*/
|
|
43711
|
+
function createMarkdownStringify(dateFormat, dateUTC) {
|
|
43712
|
+
const formatter = dateFormat ? DateFormatter.create(dateFormat, { utc: dateUTC }) : DateFormatter.iso(dateUTC);
|
|
43713
|
+
return function stringify(value) {
|
|
43714
|
+
if (value === null || value === void 0) return "";
|
|
43715
|
+
if (typeof value === "string") return value;
|
|
43716
|
+
if (typeof value === "number" || typeof value === "bigint") return String(value);
|
|
43717
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
43718
|
+
if (value instanceof Date) return formatter.format(value);
|
|
43719
|
+
if (typeof value === "object") {
|
|
43720
|
+
const v = value;
|
|
43721
|
+
if (v.text || v.hyperlink) return v.hyperlink || v.text || "";
|
|
43722
|
+
if (v.formula || v.result) return v.result != null ? String(v.result) : "";
|
|
43723
|
+
if (v.richText && Array.isArray(v.richText)) return v.richText.map((r) => r.text).join("");
|
|
43724
|
+
if (v.error) return v.error;
|
|
43725
|
+
try {
|
|
43726
|
+
return JSON.stringify(value);
|
|
43727
|
+
} catch {
|
|
43728
|
+
return "[object Object]";
|
|
43729
|
+
}
|
|
43730
|
+
}
|
|
43731
|
+
return String(value);
|
|
43732
|
+
};
|
|
43733
|
+
}
|
|
42824
43734
|
function isUrl(input) {
|
|
42825
43735
|
return typeof input === "string" && /^https?:\/\//i.test(input);
|
|
42826
43736
|
}
|
|
@@ -43175,6 +44085,153 @@ onmessage = async (ev) => {
|
|
|
43175
44085
|
await pipelinePromise;
|
|
43176
44086
|
}
|
|
43177
44087
|
/**
|
|
44088
|
+
* Populate a worksheet from a parsed Markdown table result.
|
|
44089
|
+
* Shared by readMarkdown and readMarkdownAll.
|
|
44090
|
+
*/
|
|
44091
|
+
_populateMarkdownWorksheet(worksheet, result, map) {
|
|
44092
|
+
worksheet.addRow(result.headers);
|
|
44093
|
+
worksheet._markdownAlignments = result.alignments;
|
|
44094
|
+
for (const row of result.rows) if (map) worksheet.addRow(row.map((v, i) => map(v, i)));
|
|
44095
|
+
else worksheet.addRow(row);
|
|
44096
|
+
}
|
|
44097
|
+
/**
|
|
44098
|
+
* Read a Markdown table and add as worksheet.
|
|
44099
|
+
*
|
|
44100
|
+
* @example
|
|
44101
|
+
* ```ts
|
|
44102
|
+
* // From a Markdown string
|
|
44103
|
+
* workbook.readMarkdown("| Name | Age |\n| --- | --- |\n| Alice | 30 |");
|
|
44104
|
+
*
|
|
44105
|
+
* // With options
|
|
44106
|
+
* workbook.readMarkdown(markdownString, { sheetName: "Data", map: (v, col) => Number(v) || v });
|
|
44107
|
+
* ```
|
|
44108
|
+
*/
|
|
44109
|
+
readMarkdown(input, options) {
|
|
44110
|
+
const parseResult = parseMarkdown(input, {
|
|
44111
|
+
trim: options?.trim,
|
|
44112
|
+
unescape: options?.unescape,
|
|
44113
|
+
skipEmptyRows: options?.skipEmptyRows,
|
|
44114
|
+
maxRows: options?.maxRows,
|
|
44115
|
+
convertBr: options?.convertBr
|
|
44116
|
+
});
|
|
44117
|
+
const worksheet = this.addWorksheet(options?.sheetName);
|
|
44118
|
+
this._populateMarkdownWorksheet(worksheet, parseResult, options?.map);
|
|
44119
|
+
return worksheet;
|
|
44120
|
+
}
|
|
44121
|
+
/**
|
|
44122
|
+
* Read all Markdown tables from a document, each becoming a separate worksheet.
|
|
44123
|
+
*
|
|
44124
|
+
* @param input - Markdown string containing one or more tables
|
|
44125
|
+
* @param options - Parse options (sheetName is used as prefix: "sheetName", "sheetName_2", ...)
|
|
44126
|
+
* @returns Array of created worksheets (empty if no tables found)
|
|
44127
|
+
*
|
|
44128
|
+
* @example
|
|
44129
|
+
* ```ts
|
|
44130
|
+
* // Parse a document with multiple tables
|
|
44131
|
+
* const sheets = workbook.readMarkdownAll(markdownDoc);
|
|
44132
|
+
* console.log(`Created ${sheets.length} worksheets`);
|
|
44133
|
+
*
|
|
44134
|
+
* // With a naming prefix
|
|
44135
|
+
* const sheets = workbook.readMarkdownAll(markdownDoc, { sheetName: "Table" });
|
|
44136
|
+
* // Creates "Table", "Table_2", "Table_3", ...
|
|
44137
|
+
* ```
|
|
44138
|
+
*/
|
|
44139
|
+
readMarkdownAll(input, options) {
|
|
44140
|
+
const parseResults = parseMarkdownAll(input, {
|
|
44141
|
+
trim: options?.trim,
|
|
44142
|
+
unescape: options?.unescape,
|
|
44143
|
+
skipEmptyRows: options?.skipEmptyRows,
|
|
44144
|
+
maxRows: options?.maxRows,
|
|
44145
|
+
convertBr: options?.convertBr
|
|
44146
|
+
});
|
|
44147
|
+
const baseName = options?.sheetName;
|
|
44148
|
+
const map = options?.map;
|
|
44149
|
+
const worksheets = [];
|
|
44150
|
+
for (let t = 0; t < parseResults.length; t++) {
|
|
44151
|
+
const name = baseName ? t === 0 ? baseName : `${baseName}_${t + 1}` : void 0;
|
|
44152
|
+
const worksheet = this.addWorksheet(name);
|
|
44153
|
+
this._populateMarkdownWorksheet(worksheet, parseResults[t], map);
|
|
44154
|
+
worksheets.push(worksheet);
|
|
44155
|
+
}
|
|
44156
|
+
return worksheets;
|
|
44157
|
+
}
|
|
44158
|
+
/**
|
|
44159
|
+
* Write worksheet as a Markdown table string.
|
|
44160
|
+
*
|
|
44161
|
+
* @example
|
|
44162
|
+
* ```ts
|
|
44163
|
+
* // Write first worksheet
|
|
44164
|
+
* const markdownText = workbook.writeMarkdown();
|
|
44165
|
+
*
|
|
44166
|
+
* // Write specific worksheet with options
|
|
44167
|
+
* const markdownText = workbook.writeMarkdown({ sheetName: "Data", padding: true });
|
|
44168
|
+
* ```
|
|
44169
|
+
*/
|
|
44170
|
+
writeMarkdown(options) {
|
|
44171
|
+
const worksheet = this.getWorksheet(options?.sheetName || options?.sheetId);
|
|
44172
|
+
if (!worksheet) return "";
|
|
44173
|
+
const dateFormat = options?.dateFormat;
|
|
44174
|
+
const dateUTC = options?.dateUTC;
|
|
44175
|
+
const includeEmptyRows = options?.includeEmptyRows !== false;
|
|
44176
|
+
const stringify = options?.stringify ?? createMarkdownStringify(dateFormat, dateUTC);
|
|
44177
|
+
const allRows = [];
|
|
44178
|
+
let lastRow = 1;
|
|
44179
|
+
worksheet.eachRow((row, rowNumber) => {
|
|
44180
|
+
if (includeEmptyRows) while (lastRow++ < rowNumber - 1) allRows.push([]);
|
|
44181
|
+
const values = Array.from(row.values).slice(1);
|
|
44182
|
+
allRows.push(values);
|
|
44183
|
+
lastRow = rowNumber;
|
|
44184
|
+
});
|
|
44185
|
+
if (allRows.length === 0) return "";
|
|
44186
|
+
const headers = allRows[0].map((v) => stringify(v));
|
|
44187
|
+
const dataRows = allRows.slice(1);
|
|
44188
|
+
const storedAlignments = worksheet._markdownAlignments;
|
|
44189
|
+
const columns = options?.columns;
|
|
44190
|
+
let resolvedColumns;
|
|
44191
|
+
if (!columns && storedAlignments) resolvedColumns = headers.map((h, i) => ({
|
|
44192
|
+
header: h,
|
|
44193
|
+
alignment: i < storedAlignments.length ? storedAlignments[i] : void 0
|
|
44194
|
+
}));
|
|
44195
|
+
return formatMarkdown(headers, dataRows, {
|
|
44196
|
+
columns: resolvedColumns ?? columns,
|
|
44197
|
+
alignment: options?.alignment,
|
|
44198
|
+
padding: options?.padding,
|
|
44199
|
+
trailingNewline: options?.trailingNewline,
|
|
44200
|
+
escapeContent: options?.escapeContent,
|
|
44201
|
+
stringify
|
|
44202
|
+
});
|
|
44203
|
+
}
|
|
44204
|
+
/**
|
|
44205
|
+
* Write worksheet to Markdown buffer (Uint8Array).
|
|
44206
|
+
*
|
|
44207
|
+
* @example
|
|
44208
|
+
* ```ts
|
|
44209
|
+
* const buffer = workbook.writeMarkdownBuffer();
|
|
44210
|
+
* ```
|
|
44211
|
+
*/
|
|
44212
|
+
writeMarkdownBuffer(options) {
|
|
44213
|
+
const markdownString = this.writeMarkdown(options);
|
|
44214
|
+
return new TextEncoder().encode(markdownString);
|
|
44215
|
+
}
|
|
44216
|
+
/**
|
|
44217
|
+
* Read Markdown from file (Node.js only - throws in browser)
|
|
44218
|
+
*/
|
|
44219
|
+
async readMarkdownFile(_filename, _options) {
|
|
44220
|
+
throw new ExcelNotSupportedError("readMarkdownFile()", "not available in browser. Use readMarkdown(string) instead.");
|
|
44221
|
+
}
|
|
44222
|
+
/**
|
|
44223
|
+
* Read all Markdown tables from file (Node.js only - throws in browser)
|
|
44224
|
+
*/
|
|
44225
|
+
async readMarkdownAllFile(_filename, _options) {
|
|
44226
|
+
throw new ExcelNotSupportedError("readMarkdownAllFile()", "not available in browser. Use readMarkdownAll(string) instead.");
|
|
44227
|
+
}
|
|
44228
|
+
/**
|
|
44229
|
+
* Write Markdown to file (Node.js only - throws in browser)
|
|
44230
|
+
*/
|
|
44231
|
+
async writeMarkdownFile(_filename, _options) {
|
|
44232
|
+
throw new ExcelNotSupportedError("writeMarkdownFile()", "not available in browser. Use writeMarkdown() and trigger a download instead.");
|
|
44233
|
+
}
|
|
44234
|
+
/**
|
|
43178
44235
|
* Create a streaming workbook writer for large files.
|
|
43179
44236
|
* This is more memory-efficient than using Workbook for large datasets.
|
|
43180
44237
|
*
|
|
@@ -48640,6 +49697,8 @@ onmessage = async (ev) => {
|
|
|
48640
49697
|
exports.ImageError = ImageError;
|
|
48641
49698
|
exports.InvalidAddressError = InvalidAddressError;
|
|
48642
49699
|
exports.InvalidValueTypeError = InvalidValueTypeError;
|
|
49700
|
+
exports.MarkdownError = MarkdownError;
|
|
49701
|
+
exports.MarkdownParseError = MarkdownParseError;
|
|
48643
49702
|
exports.MaxItemsExceededError = MaxItemsExceededError;
|
|
48644
49703
|
exports.MergeConflictError = MergeConflictError;
|
|
48645
49704
|
exports.PageSizes = PageSizes;
|