@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.
Files changed (79) hide show
  1. package/README.md +99 -577
  2. package/README_zh.md +101 -577
  3. package/dist/browser/index.browser.d.ts +3 -0
  4. package/dist/browser/index.browser.js +2 -0
  5. package/dist/browser/index.d.ts +3 -0
  6. package/dist/browser/index.js +2 -0
  7. package/dist/browser/modules/archive/compression/compress.browser.js +4 -4
  8. package/dist/browser/modules/archive/compression/deflate-fallback.d.ts +24 -22
  9. package/dist/browser/modules/archive/compression/deflate-fallback.js +664 -360
  10. package/dist/browser/modules/archive/compression/streaming-compress.browser.d.ts +7 -0
  11. package/dist/browser/modules/archive/compression/streaming-compress.browser.js +15 -3
  12. package/dist/browser/modules/archive/compression/streaming-compress.d.ts +5 -0
  13. package/dist/browser/modules/archive/compression/streaming-compress.js +7 -0
  14. package/dist/browser/modules/archive/zip/stream.js +27 -3
  15. package/dist/browser/modules/excel/workbook.browser.d.ts +72 -0
  16. package/dist/browser/modules/excel/workbook.browser.js +226 -0
  17. package/dist/browser/modules/excel/workbook.d.ts +32 -1
  18. package/dist/browser/modules/excel/workbook.js +47 -2
  19. package/dist/browser/modules/excel/xlsx/xlsx.browser.js +42 -4
  20. package/dist/browser/modules/markdown/constants.d.ts +30 -0
  21. package/dist/browser/modules/markdown/constants.js +30 -0
  22. package/dist/browser/modules/markdown/errors.d.ts +21 -0
  23. package/dist/browser/modules/markdown/errors.js +23 -0
  24. package/dist/browser/modules/markdown/format/index.d.ts +54 -0
  25. package/dist/browser/modules/markdown/format/index.js +307 -0
  26. package/dist/browser/modules/markdown/index.d.ts +15 -0
  27. package/dist/browser/modules/markdown/index.js +22 -0
  28. package/dist/browser/modules/markdown/parse/index.d.ts +70 -0
  29. package/dist/browser/modules/markdown/parse/index.js +428 -0
  30. package/dist/browser/modules/markdown/types.d.ts +130 -0
  31. package/dist/browser/modules/markdown/types.js +6 -0
  32. package/dist/cjs/index.js +5 -1
  33. package/dist/cjs/modules/archive/compression/compress.browser.js +4 -4
  34. package/dist/cjs/modules/archive/compression/deflate-fallback.js +664 -360
  35. package/dist/cjs/modules/archive/compression/streaming-compress.browser.js +15 -2
  36. package/dist/cjs/modules/archive/compression/streaming-compress.js +8 -0
  37. package/dist/cjs/modules/archive/zip/stream.js +26 -2
  38. package/dist/cjs/modules/excel/workbook.browser.js +226 -0
  39. package/dist/cjs/modules/excel/workbook.js +46 -1
  40. package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +42 -4
  41. package/dist/cjs/modules/markdown/constants.js +33 -0
  42. package/dist/cjs/modules/markdown/errors.js +28 -0
  43. package/dist/cjs/modules/markdown/format/index.js +310 -0
  44. package/dist/cjs/modules/markdown/index.js +30 -0
  45. package/dist/cjs/modules/markdown/parse/index.js +432 -0
  46. package/dist/cjs/modules/markdown/types.js +7 -0
  47. package/dist/esm/index.browser.js +2 -0
  48. package/dist/esm/index.js +2 -0
  49. package/dist/esm/modules/archive/compression/compress.browser.js +4 -4
  50. package/dist/esm/modules/archive/compression/deflate-fallback.js +664 -360
  51. package/dist/esm/modules/archive/compression/streaming-compress.browser.js +15 -3
  52. package/dist/esm/modules/archive/compression/streaming-compress.js +7 -0
  53. package/dist/esm/modules/archive/zip/stream.js +27 -3
  54. package/dist/esm/modules/excel/workbook.browser.js +226 -0
  55. package/dist/esm/modules/excel/workbook.js +47 -2
  56. package/dist/esm/modules/excel/xlsx/xlsx.browser.js +42 -4
  57. package/dist/esm/modules/markdown/constants.js +30 -0
  58. package/dist/esm/modules/markdown/errors.js +23 -0
  59. package/dist/esm/modules/markdown/format/index.js +307 -0
  60. package/dist/esm/modules/markdown/index.js +22 -0
  61. package/dist/esm/modules/markdown/parse/index.js +428 -0
  62. package/dist/esm/modules/markdown/types.js +6 -0
  63. package/dist/iife/excelts.iife.js +1342 -283
  64. package/dist/iife/excelts.iife.js.map +1 -1
  65. package/dist/iife/excelts.iife.min.js +38 -34
  66. package/dist/types/index.browser.d.ts +3 -0
  67. package/dist/types/index.d.ts +3 -0
  68. package/dist/types/modules/archive/compression/deflate-fallback.d.ts +24 -22
  69. package/dist/types/modules/archive/compression/streaming-compress.browser.d.ts +7 -0
  70. package/dist/types/modules/archive/compression/streaming-compress.d.ts +5 -0
  71. package/dist/types/modules/excel/workbook.browser.d.ts +72 -0
  72. package/dist/types/modules/excel/workbook.d.ts +32 -1
  73. package/dist/types/modules/markdown/constants.d.ts +30 -0
  74. package/dist/types/modules/markdown/errors.d.ts +21 -0
  75. package/dist/types/modules/markdown/format/index.d.ts +54 -0
  76. package/dist/types/modules/markdown/index.d.ts +15 -0
  77. package/dist/types/modules/markdown/parse/index.d.ts +70 -0
  78. package/dist/types/modules/markdown/types.d.ts +130 -0
  79. package/package.json +56 -32
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * @cj-tech-master/excelts v7.6.0
3
- * TypeScript Excel Workbook Manager - Read and Write xlsx and csv Files.
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 fixed Huffman codes.
24432
+ * Compress data using DEFLATE with Dynamic Huffman codes (BTYPE=2).
24398
24433
  *
24399
- * Uses LZ77 with hash chains and lazy matching for significantly better
24400
- * compression than a single-entry hash table. The algorithm is modelled
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.writeBits(1, 1);
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
- * Write a length code (257-285)
24567
- */
24568
- function writeLengthCode(output, length) {
24569
- let code;
24570
- let extraBits;
24571
- let extraValue;
24572
- if (length <= 10) {
24573
- code = 257 + length - 3;
24574
- extraBits = 0;
24575
- extraValue = 0;
24576
- } else if (length <= 18) {
24577
- const base = length - 11;
24578
- code = 265 + Math.floor(base / 2);
24579
- extraBits = 1;
24580
- extraValue = base % 2;
24581
- } else if (length <= 34) {
24582
- const base = length - 19;
24583
- code = 269 + Math.floor(base / 4);
24584
- extraBits = 2;
24585
- extraValue = base % 4;
24586
- } else if (length <= 66) {
24587
- const base = length - 35;
24588
- code = 273 + Math.floor(base / 8);
24589
- extraBits = 3;
24590
- extraValue = base % 8;
24591
- } else if (length <= 130) {
24592
- const base = length - 67;
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
- writeLiteralCode(output, code);
24607
- if (extraBits > 0) output.writeBits(extraValue, extraBits);
24566
+ return {
24567
+ code: 29,
24568
+ extra: 0,
24569
+ extraBits: 13
24570
+ };
24608
24571
  }
24609
24572
  /**
24610
- * Write a distance code
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 writeDistanceCode(output, distance) {
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 extraBits = 0;
24615
- let baseDistance = 1;
24616
- for (let i = 0; i < DIST_TABLE.length; i++) {
24617
- const [maxDist, c, extra] = DIST_TABLE[i];
24618
- if (distance <= maxDist) {
24619
- code = c;
24620
- extraBits = extra;
24621
- break;
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 extraValue = distance - baseDistance;
24626
- output.writeBitsReverse(code, 5);
24627
- if (extraBits > 0) output.writeBits(extraValue, extraBits);
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**: each match is compared with the next position's match
24641
- * to pick the longer one.
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 fixed-Huffman block (BFINAL=0).
24646
- * `finish()` emits a final empty block (BFINAL=1) and returns the tail bytes.
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._head = new Int32Array(HASH_SIZE);
24656
- this._prev = new Int32Array(MAX_DIST);
24657
- this._window = new Uint8Array(WINDOW_SIZE);
24658
- this._windowLen = 0;
24659
- this._totalIn = 0;
24660
- this._hasPrevMatch = false;
24661
- this._prevMatchLen = 0;
24662
- this._prevMatchDist = 0;
24663
- this._prevLiteral = 0;
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
- out.writeBits(0, 1);
24673
- out.writeBits(1, 2);
24674
- const window = this._window;
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
- if (hasPrevMatch) {
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
- * Resolves immediately if no backpressure is active.
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 (!this._needsDrain || !this.pipedStream) return Promise.resolve();
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;