@cj-tech-master/excelts 1.1.0 → 1.4.1

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 (41) hide show
  1. package/dist/browser/excelts.iife.js +1089 -568
  2. package/dist/browser/excelts.iife.js.map +1 -1
  3. package/dist/browser/excelts.iife.min.js +18 -18
  4. package/dist/cjs/doc/data-validations.js +29 -1
  5. package/dist/cjs/index.js +1 -1
  6. package/dist/cjs/utils/cell-format.js +815 -0
  7. package/dist/cjs/utils/parse-sax.js +2 -2
  8. package/dist/cjs/utils/{extra-utils.js → sheet-utils.js} +114 -89
  9. package/dist/cjs/utils/stream-buf.js +15 -4
  10. package/dist/cjs/utils/unzip/parse.js +82 -1
  11. package/dist/cjs/utils/utils.js +13 -17
  12. package/dist/cjs/utils/zip-stream.js +20 -32
  13. package/dist/cjs/xlsx/xform/sheet/data-validations-xform.js +46 -7
  14. package/dist/cjs/xlsx/xlsx.js +1 -2
  15. package/dist/esm/doc/data-validations.js +29 -1
  16. package/dist/esm/index.browser.js +1 -1
  17. package/dist/esm/index.js +1 -1
  18. package/dist/esm/utils/cell-format.js +810 -0
  19. package/dist/esm/utils/parse-sax.js +1 -1
  20. package/dist/esm/utils/{extra-utils.js → sheet-utils.js} +97 -72
  21. package/dist/esm/utils/stream-buf.js +15 -4
  22. package/dist/esm/utils/unzip/parse.js +83 -2
  23. package/dist/esm/utils/utils.js +12 -16
  24. package/dist/esm/utils/zip-stream.js +20 -32
  25. package/dist/esm/xlsx/xform/sheet/data-validations-xform.js +46 -7
  26. package/dist/esm/xlsx/xlsx.js +1 -2
  27. package/dist/types/index.browser.d.ts +1 -1
  28. package/dist/types/index.d.ts +1 -1
  29. package/dist/types/utils/cell-format.d.ts +32 -0
  30. package/dist/types/utils/{extra-utils.d.ts → sheet-utils.d.ts} +51 -52
  31. package/dist/types/utils/utils.d.ts +5 -2
  32. package/package.json +5 -5
  33. package/dist/cjs/utils/browser-buffer-decode.js +0 -13
  34. package/dist/cjs/utils/browser-buffer-encode.js +0 -13
  35. package/dist/cjs/utils/browser.js +0 -6
  36. package/dist/esm/utils/browser-buffer-decode.js +0 -11
  37. package/dist/esm/utils/browser-buffer-encode.js +0 -11
  38. package/dist/esm/utils/browser.js +0 -3
  39. package/dist/types/utils/browser-buffer-decode.d.ts +0 -2
  40. package/dist/types/utils/browser-buffer-encode.d.ts +0 -2
  41. package/dist/types/utils/browser.d.ts +0 -1
@@ -1,5 +1,5 @@
1
1
  import { SaxesParser } from "saxes";
2
- import { bufferToString } from "./browser-buffer-decode.js";
2
+ import { bufferToString } from "./utils.js";
3
3
  async function* parseSax(iterable) {
4
4
  const saxesParser = new SaxesParser({
5
5
  xmlns: false,
@@ -1,87 +1,112 @@
1
1
  /**
2
2
  * Utility functions for ExcelTS
3
3
  * Provides convenient helper functions for common spreadsheet operations
4
- * compatible with xlsx library's utils API but built on excelts native types
5
4
  */
6
5
  import { Workbook } from "../doc/workbook.js";
7
6
  import { colCache } from "./col-cache.js";
7
+ import { dateToExcel } from "./utils.js";
8
+ import { format as cellFormat } from "./cell-format.js";
9
+ /**
10
+ * Get formatted display text for a cell value
11
+ * Returns the value formatted according to the cell's numFmt
12
+ * This matches Excel's display exactly
13
+ */
14
+ function getCellDisplayText(cell) {
15
+ const value = cell.value;
16
+ const fmt = cell.numFmt || "General";
17
+ // Null/undefined
18
+ if (value == null) {
19
+ return "";
20
+ }
21
+ // Date object - convert to Excel serial number
22
+ if (value instanceof Date) {
23
+ const serial = dateToExcel(value, false);
24
+ return cellFormat(fmt, serial);
25
+ }
26
+ // Number/Boolean/String - let cellFormat handle it
27
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "string") {
28
+ return cellFormat(fmt, value);
29
+ }
30
+ // Fallback to cell.text for other types (rich text, hyperlink, error, formula, etc.)
31
+ return cell.text;
32
+ }
8
33
  // =============================================================================
9
34
  // Cell Address Encoding/Decoding
10
35
  // =============================================================================
11
36
  /**
12
37
  * Decode column string to 0-indexed number
13
- * @example decode_col("A") => 0, decode_col("Z") => 25, decode_col("AA") => 26
38
+ * @example decodeCol("A") => 0, decodeCol("Z") => 25, decodeCol("AA") => 26
14
39
  */
15
- export function decode_col(colstr) {
40
+ export function decodeCol(colstr) {
16
41
  return colCache.l2n(colstr.toUpperCase()) - 1;
17
42
  }
18
43
  /**
19
44
  * Encode 0-indexed column number to string
20
- * @example encode_col(0) => "A", encode_col(25) => "Z", encode_col(26) => "AA"
45
+ * @example encodeCol(0) => "A", encodeCol(25) => "Z", encodeCol(26) => "AA"
21
46
  */
22
- export function encode_col(col) {
47
+ export function encodeCol(col) {
23
48
  return colCache.n2l(col + 1);
24
49
  }
25
50
  /**
26
51
  * Decode row string to 0-indexed number
27
- * @example decode_row("1") => 0, decode_row("10") => 9
52
+ * @example decodeRow("1") => 0, decodeRow("10") => 9
28
53
  */
29
- export function decode_row(rowstr) {
54
+ export function decodeRow(rowstr) {
30
55
  return parseInt(rowstr, 10) - 1;
31
56
  }
32
57
  /**
33
58
  * Encode 0-indexed row number to string
34
- * @example encode_row(0) => "1", encode_row(9) => "10"
59
+ * @example encodeRow(0) => "1", encodeRow(9) => "10"
35
60
  */
36
- export function encode_row(row) {
61
+ export function encodeRow(row) {
37
62
  return String(row + 1);
38
63
  }
39
64
  /**
40
65
  * Decode cell address string to CellAddress object
41
- * @example decode_cell("A1") => {c: 0, r: 0}, decode_cell("B2") => {c: 1, r: 1}
66
+ * @example decodeCell("A1") => {c: 0, r: 0}, decodeCell("B2") => {c: 1, r: 1}
42
67
  */
43
- export function decode_cell(cstr) {
68
+ export function decodeCell(cstr) {
44
69
  const addr = colCache.decodeAddress(cstr.toUpperCase());
45
70
  return { c: addr.col - 1, r: addr.row - 1 };
46
71
  }
47
72
  /**
48
73
  * Encode CellAddress object to cell address string
49
- * @example encode_cell({c: 0, r: 0}) => "A1", encode_cell({c: 1, r: 1}) => "B2"
74
+ * @example encodeCell({c: 0, r: 0}) => "A1", encodeCell({c: 1, r: 1}) => "B2"
50
75
  */
51
- export function encode_cell(cell) {
76
+ export function encodeCell(cell) {
52
77
  return colCache.encodeAddress(cell.r + 1, cell.c + 1);
53
78
  }
54
79
  /**
55
80
  * Decode range string to Range object
56
- * @example decode_range("A1:B2") => {s: {c: 0, r: 0}, e: {c: 1, r: 1}}
81
+ * @example decodeRange("A1:B2") => {s: {c: 0, r: 0}, e: {c: 1, r: 1}}
57
82
  */
58
- export function decode_range(range) {
83
+ export function decodeRange(range) {
59
84
  const idx = range.indexOf(":");
60
85
  if (idx === -1) {
61
- const cell = decode_cell(range);
86
+ const cell = decodeCell(range);
62
87
  return { s: cell, e: { ...cell } };
63
88
  }
64
89
  return {
65
- s: decode_cell(range.slice(0, idx)),
66
- e: decode_cell(range.slice(idx + 1))
90
+ s: decodeCell(range.slice(0, idx)),
91
+ e: decodeCell(range.slice(idx + 1))
67
92
  };
68
93
  }
69
- export function encode_range(startOrRange, end) {
94
+ export function encodeRange(startOrRange, end) {
70
95
  if (end === undefined) {
71
96
  const range = startOrRange;
72
- return encode_range(range.s, range.e);
97
+ return encodeRange(range.s, range.e);
73
98
  }
74
99
  const start = startOrRange;
75
- const startStr = encode_cell(start);
76
- const endStr = encode_cell(end);
100
+ const startStr = encodeCell(start);
101
+ const endStr = encodeCell(end);
77
102
  return startStr === endStr ? startStr : `${startStr}:${endStr}`;
78
103
  }
79
104
  /**
80
105
  * Create a worksheet from an array of objects (xlsx compatible)
81
106
  * @example
82
- * const ws = json_to_sheet([{name: "Alice", age: 30}, {name: "Bob", age: 25}])
107
+ * const ws = jsonToSheet([{name: "Alice", age: 30}, {name: "Bob", age: 25}])
83
108
  */
84
- export function json_to_sheet(data, opts) {
109
+ export function jsonToSheet(data, opts) {
85
110
  const o = opts || {};
86
111
  // Create a temporary workbook to get a worksheet
87
112
  const tempWb = new Workbook();
@@ -127,7 +152,7 @@ export function json_to_sheet(data, opts) {
127
152
  /**
128
153
  * Add data from an array of objects to an existing worksheet (xlsx compatible)
129
154
  */
130
- export function sheet_add_json(worksheet, data, opts) {
155
+ export function sheetAddJson(worksheet, data, opts) {
131
156
  const o = opts || {};
132
157
  if (data.length === 0) {
133
158
  return worksheet;
@@ -137,7 +162,7 @@ export function sheet_add_json(worksheet, data, opts) {
137
162
  let startCol = 1;
138
163
  if (o.origin !== undefined) {
139
164
  if (typeof o.origin === "string") {
140
- const addr = decode_cell(o.origin);
165
+ const addr = decodeCell(o.origin);
141
166
  startRow = addr.r + 1;
142
167
  startCol = addr.c + 1;
143
168
  }
@@ -193,18 +218,18 @@ export function sheet_add_json(worksheet, data, opts) {
193
218
  * Convert worksheet to JSON array (xlsx compatible)
194
219
  * @example
195
220
  * // Default: array of objects with first row as headers
196
- * const data = sheet_to_json(worksheet)
221
+ * const data = sheetToJson(worksheet)
197
222
  * // => [{name: "Alice", age: 30}, {name: "Bob", age: 25}]
198
223
  *
199
224
  * // Array of arrays
200
- * const aoa = sheet_to_json(worksheet, { header: 1 })
225
+ * const aoa = sheetToJson(worksheet, { header: 1 })
201
226
  * // => [["name", "age"], ["Alice", 30], ["Bob", 25]]
202
227
  *
203
228
  * // Column letters as keys
204
- * const cols = sheet_to_json(worksheet, { header: "A" })
229
+ * const cols = sheetToJson(worksheet, { header: "A" })
205
230
  * // => [{A: "name", B: "age"}, {A: "Alice", B: 30}]
206
231
  */
207
- export function sheet_to_json(worksheet, opts) {
232
+ export function sheetToJson(worksheet, opts) {
208
233
  const o = opts || {};
209
234
  // Determine range
210
235
  let startRow = 1;
@@ -216,7 +241,7 @@ export function sheet_to_json(worksheet, opts) {
216
241
  startRow = o.range + 1; // 0-indexed to 1-indexed
217
242
  }
218
243
  else if (typeof o.range === "string") {
219
- const r = decode_range(o.range);
244
+ const r = decodeRange(o.range);
220
245
  startRow = r.s.r + 1;
221
246
  endRow = r.e.r + 1;
222
247
  startCol = r.s.c + 1;
@@ -238,8 +263,8 @@ export function sheet_to_json(worksheet, opts) {
238
263
  let isEmpty = true;
239
264
  for (let col = startCol; col <= endCol; col++) {
240
265
  const cell = worksheet.getCell(row, col);
241
- const val = cell.value;
242
- if (val != null) {
266
+ const val = o.raw === false ? getCellDisplayText(cell).trim() : cell.value;
267
+ if (val != null && val !== "") {
243
268
  rowData[col - startCol] = val;
244
269
  isEmpty = false;
245
270
  }
@@ -266,9 +291,9 @@ export function sheet_to_json(worksheet, opts) {
266
291
  let isEmpty = true;
267
292
  for (let col = startCol; col <= endCol; col++) {
268
293
  const cell = worksheet.getCell(row, col);
269
- const val = cell.value;
270
- const key = encode_col(col - 1); // 0-indexed for encode_col
271
- if (val != null) {
294
+ const val = o.raw === false ? getCellDisplayText(cell).trim() : cell.value;
295
+ const key = encodeCol(col - 1); // 0-indexed for encodeCol
296
+ if (val != null && val !== "") {
272
297
  rowData[key] = val;
273
298
  isEmpty = false;
274
299
  }
@@ -293,8 +318,8 @@ export function sheet_to_json(worksheet, opts) {
293
318
  const colIdx = col - startCol;
294
319
  const key = headerOpt[colIdx] ?? `__EMPTY_${colIdx}`;
295
320
  const cell = worksheet.getCell(row, col);
296
- const val = cell.value;
297
- if (val != null) {
321
+ const val = o.raw === false ? getCellDisplayText(cell).trim() : cell.value;
322
+ if (val != null && val !== "") {
298
323
  rowData[key] = val;
299
324
  isEmpty = false;
300
325
  }
@@ -335,9 +360,9 @@ export function sheet_to_json(worksheet, opts) {
335
360
  let isEmpty = true;
336
361
  for (let col = startCol; col <= endCol; col++) {
337
362
  const cell = worksheet.getCell(row, col);
338
- const val = cell.value;
363
+ const val = o.raw === false ? getCellDisplayText(cell).trim() : cell.value;
339
364
  const key = headers[col - startCol];
340
- if (val != null) {
365
+ if (val != null && val !== "") {
341
366
  rowData[key] = val;
342
367
  isEmpty = false;
343
368
  }
@@ -354,7 +379,7 @@ export function sheet_to_json(worksheet, opts) {
354
379
  /**
355
380
  * Convert worksheet to CSV string
356
381
  */
357
- export function sheet_to_csv(worksheet, opts) {
382
+ export function sheetToCsv(worksheet, opts) {
358
383
  const o = opts || {};
359
384
  const FS = o.FS ?? ",";
360
385
  const RS = o.RS ?? "\n";
@@ -415,17 +440,17 @@ export function sheet_to_csv(worksheet, opts) {
415
440
  /**
416
441
  * Create a new workbook
417
442
  */
418
- export function book_new() {
443
+ export function bookNew() {
419
444
  return new Workbook();
420
445
  }
421
446
  /**
422
447
  * Append worksheet to workbook (xlsx compatible)
423
448
  * @example
424
- * const wb = book_new();
425
- * const ws = json_to_sheet([{a: 1, b: 2}]);
426
- * book_append_sheet(wb, ws, "Sheet1");
449
+ * const wb = bookNew();
450
+ * const ws = jsonToSheet([{a: 1, b: 2}]);
451
+ * bookAppendSheet(wb, ws, "Sheet1");
427
452
  */
428
- export function book_append_sheet(workbook, worksheet, name) {
453
+ export function bookAppendSheet(workbook, worksheet, name) {
429
454
  // Copy the worksheet data to a new sheet in the workbook
430
455
  const newWs = workbook.addWorksheet(name);
431
456
  // Copy all cells from source worksheet
@@ -450,9 +475,9 @@ export function book_append_sheet(workbook, worksheet, name) {
450
475
  /**
451
476
  * Create a worksheet from an array of arrays (xlsx compatible)
452
477
  * @example
453
- * const ws = aoa_to_sheet([["Name", "Age"], ["Alice", 30], ["Bob", 25]])
478
+ * const ws = aoaToSheet([["Name", "Age"], ["Alice", 30], ["Bob", 25]])
454
479
  */
455
- export function aoa_to_sheet(data, opts) {
480
+ export function aoaToSheet(data, opts) {
456
481
  const tempWb = new Workbook();
457
482
  const worksheet = tempWb.addWorksheet("Sheet1");
458
483
  if (data.length === 0) {
@@ -463,7 +488,7 @@ export function aoa_to_sheet(data, opts) {
463
488
  let startCol = 1;
464
489
  if (opts?.origin !== undefined) {
465
490
  if (typeof opts.origin === "string") {
466
- const addr = decode_cell(opts.origin);
491
+ const addr = decodeCell(opts.origin);
467
492
  startRow = addr.r + 1;
468
493
  startCol = addr.c + 1;
469
494
  }
@@ -490,7 +515,7 @@ export function aoa_to_sheet(data, opts) {
490
515
  /**
491
516
  * Add data from an array of arrays to an existing worksheet (xlsx compatible)
492
517
  */
493
- export function sheet_add_aoa(worksheet, data, opts) {
518
+ export function sheetAddAoa(worksheet, data, opts) {
494
519
  if (data.length === 0) {
495
520
  return worksheet;
496
521
  }
@@ -499,7 +524,7 @@ export function sheet_add_aoa(worksheet, data, opts) {
499
524
  let startCol = 1;
500
525
  if (opts?.origin !== undefined) {
501
526
  if (typeof opts.origin === "string") {
502
- const addr = decode_cell(opts.origin);
527
+ const addr = decodeCell(opts.origin);
503
528
  startRow = addr.r + 1;
504
529
  startCol = addr.c + 1;
505
530
  }
@@ -532,7 +557,7 @@ export function sheet_add_aoa(worksheet, data, opts) {
532
557
  /**
533
558
  * Convert worksheet to array of arrays
534
559
  */
535
- export function sheet_to_aoa(worksheet) {
560
+ export function sheetToAoa(worksheet) {
536
561
  const result = [];
537
562
  worksheet.eachRow({ includeEmpty: true }, (row, rowNumber) => {
538
563
  const rowData = [];
@@ -547,24 +572,24 @@ export function sheet_to_aoa(worksheet) {
547
572
  // Export utils object
548
573
  // =============================================================================
549
574
  export const utils = {
550
- // Cell encoding/decoding
551
- decode_col,
552
- encode_col,
553
- decode_row,
554
- encode_row,
555
- decode_cell,
556
- encode_cell,
557
- decode_range,
558
- encode_range,
559
- // Sheet/JSON conversion
560
- json_to_sheet,
561
- sheet_add_json,
562
- sheet_to_json,
563
- sheet_to_csv,
564
- aoa_to_sheet,
565
- sheet_add_aoa,
566
- sheet_to_aoa,
567
- // Workbook functions
568
- book_new,
569
- book_append_sheet
575
+ // Cell encoding/decoding (camelCase)
576
+ decodeCol,
577
+ encodeCol,
578
+ decodeRow,
579
+ encodeRow,
580
+ decodeCell,
581
+ encodeCell,
582
+ decodeRange,
583
+ encodeRange,
584
+ // Sheet/JSON conversion (camelCase)
585
+ jsonToSheet,
586
+ sheetAddJson,
587
+ sheetToJson,
588
+ sheetToCsv,
589
+ aoaToSheet,
590
+ sheetAddAoa,
591
+ sheetToAoa,
592
+ // Workbook functions (camelCase)
593
+ bookNew,
594
+ bookAppendSheet
570
595
  };
@@ -198,14 +198,24 @@ inherits(StreamBuf, Duplex, {
198
198
  if (data instanceof StringBuf || (data && data.constructor?.name === "StringBuf")) {
199
199
  chunk = new StringBufChunk(data);
200
200
  }
201
- else if (data instanceof Buffer) {
201
+ else if (Buffer.isBuffer(data)) {
202
+ // Use Buffer.isBuffer() instead of instanceof for cross-realm compatibility
203
+ // (e.g., Web Workers where Buffer polyfill instances may differ)
202
204
  chunk = new BufferChunk(data);
203
205
  }
204
- else if (typeof data === "string" || data instanceof String || data instanceof ArrayBuffer) {
206
+ else if (ArrayBuffer.isView(data)) {
207
+ // Handle typed arrays (Uint8Array, Int8Array, etc.) - cross-realm safe
208
+ chunk = new BufferChunk(Buffer.from(data.buffer, data.byteOffset, data.byteLength));
209
+ }
210
+ else if (data instanceof ArrayBuffer) {
211
+ // Handle ArrayBuffer - convert to Buffer
212
+ chunk = new BufferChunk(Buffer.from(data));
213
+ }
214
+ else if (typeof data === "string" || data instanceof String) {
205
215
  chunk = new StringChunk(String(data), encoding);
206
216
  }
207
217
  else {
208
- throw new Error("Chunk must be one of type String, Buffer or StringBuf.");
218
+ throw new Error("Chunk must be one of type String, Buffer, Uint8Array, ArrayBuffer or StringBuf.");
209
219
  }
210
220
  // now, do something with the chunk
211
221
  if (this.pipes.length) {
@@ -221,7 +231,8 @@ inherits(StreamBuf, Duplex, {
221
231
  }
222
232
  else {
223
233
  this._writeToBuffers(chunk);
224
- process.nextTick(callback);
234
+ // Use queueMicrotask for cross-platform compatibility (ES2020+)
235
+ queueMicrotask(() => callback());
225
236
  }
226
237
  }
227
238
  else {
@@ -6,13 +6,94 @@
6
6
  * Commits in this fork are (c) Ziggy Jonsson (ziggy.jonsson.nyc@gmail.com)
7
7
  */
8
8
  import zlib from "zlib";
9
- import { PassThrough, pipeline } from "stream";
9
+ import { PassThrough, Transform, pipeline } from "stream";
10
10
  import { PullStream } from "./pull-stream.js";
11
11
  import { NoopStream } from "./noop-stream.js";
12
12
  import { bufferStream } from "./buffer-stream.js";
13
13
  import { parseExtraField } from "./parse-extra-field.js";
14
14
  import { parseDateTime } from "./parse-datetime.js";
15
15
  import { parse as parseBuffer } from "./parse-buffer.js";
16
+ // Check if native zlib is available (Node.js environment)
17
+ // In browser with polyfill, createInflateRaw may not exist or may not work properly
18
+ const hasNativeZlib = typeof zlib?.createInflateRaw === "function" &&
19
+ typeof process !== "undefined" &&
20
+ process.versions?.node;
21
+ /**
22
+ * A Transform stream that wraps browser's native DecompressionStream.
23
+ * Used when native zlib is not available (browser environment).
24
+ */
25
+ class BrowserInflateRawStream extends Transform {
26
+ constructor() {
27
+ super();
28
+ this.chunks = [];
29
+ this.totalLength = 0;
30
+ }
31
+ _transform(chunk, _encoding, callback) {
32
+ // Avoid unnecessary copy - Buffer extends Uint8Array
33
+ this.chunks.push(chunk);
34
+ this.totalLength += chunk.length;
35
+ callback();
36
+ }
37
+ _flush(callback) {
38
+ try {
39
+ // Use pre-calculated totalLength for better performance
40
+ const combined = new Uint8Array(this.totalLength);
41
+ let offset = 0;
42
+ for (const chunk of this.chunks) {
43
+ combined.set(chunk, offset);
44
+ offset += chunk.length;
45
+ }
46
+ // Clear chunks to free memory
47
+ this.chunks = [];
48
+ // Use native DecompressionStream
49
+ const ds = new DecompressionStream("deflate-raw");
50
+ const writer = ds.writable.getWriter();
51
+ const reader = ds.readable.getReader();
52
+ // Optimized read loop - collect chunks and concatenate at the end
53
+ const readAll = async () => {
54
+ const results = [];
55
+ let total = 0;
56
+ while (true) {
57
+ const { done, value } = await reader.read();
58
+ if (done) {
59
+ break;
60
+ }
61
+ results.push(value);
62
+ total += value.length;
63
+ }
64
+ // Single allocation for final result
65
+ const result = Buffer.allocUnsafe(total);
66
+ let off = 0;
67
+ for (const r of results) {
68
+ result.set(r, off);
69
+ off += r.length;
70
+ }
71
+ return result;
72
+ };
73
+ writer.write(combined);
74
+ writer.close();
75
+ readAll()
76
+ .then(decompressed => {
77
+ this.push(decompressed);
78
+ callback();
79
+ })
80
+ .catch(callback);
81
+ }
82
+ catch (err) {
83
+ callback(err);
84
+ }
85
+ }
86
+ }
87
+ /**
88
+ * Creates an InflateRaw stream.
89
+ * Uses native zlib in Node.js for best performance, falls back to DecompressionStream in browser.
90
+ */
91
+ function createInflateRaw() {
92
+ if (hasNativeZlib) {
93
+ return zlib.createInflateRaw();
94
+ }
95
+ return new BrowserInflateRawStream();
96
+ }
16
97
  const endDirectorySignature = Buffer.alloc(4);
17
98
  endDirectorySignature.writeUInt32LE(0x06054b50, 0);
18
99
  export class Parse extends PullStream {
@@ -168,7 +249,7 @@ export class Parse extends PullStream {
168
249
  const fileSizeKnown = !((vars.flags || 0) & 0x08) || vars.compressedSize > 0;
169
250
  let eof;
170
251
  entry.__autodraining = __autodraining; // expose __autodraining for test purposes
171
- const inflater = vars.compressionMethod && !__autodraining ? zlib.createInflateRaw() : new PassThrough();
252
+ const inflater = vars.compressionMethod && !__autodraining ? createInflateRaw() : new PassThrough();
172
253
  if (fileSizeKnown) {
173
254
  entry.size = vars.uncompressedSize;
174
255
  eof = vars.compressedSize;
@@ -3,21 +3,6 @@ export function delay(ms) {
3
3
  return new Promise(resolve => setTimeout(resolve, ms));
4
4
  }
5
5
  export function nop() { }
6
- export function promiseImmediate(value) {
7
- return new Promise(resolve => {
8
- if (global.setImmediate) {
9
- setImmediate(() => {
10
- resolve(value);
11
- });
12
- }
13
- else {
14
- // poorman's setImmediate - must wait at least 1ms
15
- setTimeout(() => {
16
- resolve(value);
17
- }, 1);
18
- }
19
- });
20
- }
21
6
  // useful stuff
22
7
  export const inherits = function (cls, superCtor, statics, prototype) {
23
8
  cls.super_ = superCtor;
@@ -189,7 +174,6 @@ export function objectFromProps(props, value = null) {
189
174
  /** @deprecated Import functions directly instead of using the utils object */
190
175
  export const utils = {
191
176
  nop,
192
- promiseImmediate,
193
177
  inherits,
194
178
  dateToExcel,
195
179
  excelToDate,
@@ -208,3 +192,15 @@ export const utils = {
208
192
  toSortedArray,
209
193
  objectFromProps
210
194
  };
195
+ // TextDecoder is available in ES2020+ and all modern browsers/Node.js
196
+ const textDecoder = new TextDecoder("utf-8");
197
+ /**
198
+ * Convert a Buffer or ArrayBuffer to a UTF-8 string
199
+ * Works in both Node.js and browser environments
200
+ */
201
+ export function bufferToString(chunk) {
202
+ if (typeof chunk === "string") {
203
+ return chunk;
204
+ }
205
+ return textDecoder.decode(chunk);
206
+ }
@@ -1,8 +1,6 @@
1
1
  import events from "events";
2
2
  import { Zip, ZipPassThrough, ZipDeflate } from "fflate";
3
3
  import { StreamBuf } from "./stream-buf.js";
4
- import { stringToBuffer } from "./browser-buffer-encode.js";
5
- import { isBrowser } from "./browser.js";
6
4
  // =============================================================================
7
5
  // The ZipWriter class
8
6
  // Packs streamed data into an output zip stream
@@ -36,39 +34,29 @@ class ZipWriter extends events.EventEmitter {
36
34
  append(data, options) {
37
35
  let buffer;
38
36
  if (Object.prototype.hasOwnProperty.call(options, "base64") && options.base64) {
39
- // Use Buffer.from for efficient base64 decoding
37
+ // Decode base64 data - Buffer.from works in both Node.js and browser (via polyfill)
40
38
  const base64Data = typeof data === "string" ? data : data.toString();
41
- if (isBrowser) {
42
- // Browser: use atob with optimized Uint8Array conversion
43
- const binaryString = atob(base64Data);
44
- const len = binaryString.length;
45
- buffer = new Uint8Array(len);
46
- // Use a single loop with cached length for better performance
47
- for (let i = 0; i < len; i++) {
48
- buffer[i] = binaryString.charCodeAt(i);
49
- }
50
- }
51
- else {
52
- // Node.js: use efficient Buffer.from
53
- buffer = Buffer.from(base64Data, "base64");
54
- }
39
+ buffer = Buffer.from(base64Data, "base64");
40
+ }
41
+ else if (typeof data === "string") {
42
+ // Convert string to Buffer - works in both environments
43
+ buffer = Buffer.from(data, "utf8");
44
+ }
45
+ else if (Buffer.isBuffer(data)) {
46
+ // Buffer extends Uint8Array, fflate can use it directly - no copy needed
47
+ buffer = data;
48
+ }
49
+ else if (ArrayBuffer.isView(data)) {
50
+ // Handle typed arrays - create view without copy
51
+ buffer = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
52
+ }
53
+ else if (data instanceof ArrayBuffer) {
54
+ // Handle ArrayBuffer directly
55
+ buffer = new Uint8Array(data);
55
56
  }
56
57
  else {
57
- if (typeof data === "string") {
58
- // Convert string to Uint8Array
59
- if (isBrowser) {
60
- buffer = stringToBuffer(data);
61
- }
62
- else {
63
- buffer = Buffer.from(data, "utf8");
64
- }
65
- }
66
- else if (Buffer.isBuffer(data)) {
67
- buffer = new Uint8Array(data);
68
- }
69
- else {
70
- buffer = data;
71
- }
58
+ // Assume it's already a Uint8Array or compatible type
59
+ buffer = data;
72
60
  }
73
61
  // Add file to zip using streaming API
74
62
  // Use ZipDeflate for compression or ZipPassThrough for no compression