@cj-tech-master/excelts 1.4.2 → 1.4.4

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 (49) hide show
  1. package/README.md +3 -3
  2. package/README_zh.md +3 -3
  3. package/dist/browser/excelts.iife.js +8135 -2722
  4. package/dist/browser/excelts.iife.js.map +1 -1
  5. package/dist/browser/excelts.iife.min.js +86 -23
  6. package/dist/cjs/stream/xlsx/workbook-writer.js +3 -2
  7. package/dist/cjs/utils/cell-format.js +13 -9
  8. package/dist/cjs/utils/sheet-utils.js +125 -15
  9. package/dist/cjs/utils/unzip/extract.js +166 -0
  10. package/dist/cjs/utils/unzip/index.js +7 -1
  11. package/dist/cjs/utils/xml-stream.js +25 -3
  12. package/dist/cjs/utils/zip/compress.js +261 -0
  13. package/dist/cjs/utils/zip/crc32.js +154 -0
  14. package/dist/cjs/utils/zip/index.js +70 -0
  15. package/dist/cjs/utils/zip/zip-builder.js +378 -0
  16. package/dist/cjs/utils/zip-stream.js +30 -34
  17. package/dist/cjs/xlsx/xform/book/defined-name-xform.js +36 -2
  18. package/dist/cjs/xlsx/xform/list-xform.js +6 -0
  19. package/dist/cjs/xlsx/xform/sheet/cell-xform.js +6 -1
  20. package/dist/cjs/xlsx/xform/sheet/row-xform.js +24 -2
  21. package/dist/cjs/xlsx/xform/table/filter-column-xform.js +4 -0
  22. package/dist/esm/stream/xlsx/workbook-writer.js +3 -2
  23. package/dist/esm/utils/cell-format.js +13 -9
  24. package/dist/esm/utils/sheet-utils.js +125 -15
  25. package/dist/esm/utils/unzip/extract.js +160 -0
  26. package/dist/esm/utils/unzip/index.js +2 -0
  27. package/dist/esm/utils/xml-stream.js +25 -3
  28. package/dist/esm/utils/zip/compress.js +220 -0
  29. package/dist/esm/utils/zip/crc32.js +116 -0
  30. package/dist/esm/utils/zip/index.js +55 -0
  31. package/dist/esm/utils/zip/zip-builder.js +372 -0
  32. package/dist/esm/utils/zip-stream.js +30 -34
  33. package/dist/esm/xlsx/xform/book/defined-name-xform.js +36 -2
  34. package/dist/esm/xlsx/xform/list-xform.js +6 -0
  35. package/dist/esm/xlsx/xform/sheet/cell-xform.js +6 -1
  36. package/dist/esm/xlsx/xform/sheet/row-xform.js +24 -2
  37. package/dist/esm/xlsx/xform/table/filter-column-xform.js +4 -0
  38. package/dist/types/utils/sheet-utils.d.ts +8 -2
  39. package/dist/types/utils/unzip/extract.d.ts +92 -0
  40. package/dist/types/utils/unzip/index.d.ts +1 -0
  41. package/dist/types/utils/xml-stream.d.ts +2 -0
  42. package/dist/types/utils/zip/compress.d.ts +83 -0
  43. package/dist/types/utils/zip/crc32.d.ts +55 -0
  44. package/dist/types/utils/zip/index.d.ts +52 -0
  45. package/dist/types/utils/zip/zip-builder.d.ts +110 -0
  46. package/dist/types/utils/zip-stream.d.ts +6 -12
  47. package/dist/types/xlsx/xform/list-xform.d.ts +1 -0
  48. package/dist/types/xlsx/xform/sheet/row-xform.d.ts +2 -0
  49. package/package.json +1 -1
@@ -42,16 +42,50 @@ class DefinedNamesXform extends BaseXform {
42
42
  return false;
43
43
  }
44
44
  }
45
+ // Regex to validate cell range format:
46
+ // - Cell: $A$1 or A1
47
+ // - Range: $A$1:$B$10 or A1:B10
48
+ // - Row range: $1:$2 (for print titles)
49
+ // - Column range: $A:$B (for print titles)
50
+ const cellRangeRegexp = /^[$]?[A-Za-z]{1,3}[$]?\d+(:[$]?[A-Za-z]{1,3}[$]?\d+)?$/;
51
+ const rowRangeRegexp = /^[$]?\d+:[$]?\d+$/;
52
+ const colRangeRegexp = /^[$]?[A-Za-z]{1,3}:[$]?[A-Za-z]{1,3}$/;
45
53
  function isValidRange(range) {
54
+ // Skip array constants wrapped in {} - these are not valid cell ranges
55
+ // e.g., {"'Sheet1'!$A$1:$B$10"} or {#N/A,#N/A,FALSE,"text"}
56
+ if (range.startsWith("{") || range.endsWith("}")) {
57
+ return false;
58
+ }
59
+ // Extract the cell reference part (after the sheet name if present)
60
+ const cellRef = range.split("!").pop() || "";
61
+ // Must match one of the valid patterns
62
+ if (!cellRangeRegexp.test(cellRef) &&
63
+ !rowRangeRegexp.test(cellRef) &&
64
+ !colRangeRegexp.test(cellRef)) {
65
+ return false;
66
+ }
46
67
  try {
47
- colCache.decodeEx(range);
48
- return true;
68
+ const decoded = colCache.decodeEx(range);
69
+ // For cell ranges: row/col or top/bottom/left/right should be valid numbers
70
+ // For row ranges ($1:$2): top/bottom are numbers, left/right are null
71
+ // For column ranges ($A:$B): left/right are numbers, top/bottom are null
72
+ if (("row" in decoded && typeof decoded.row === "number") ||
73
+ ("top" in decoded && typeof decoded.top === "number") ||
74
+ ("left" in decoded && typeof decoded.left === "number")) {
75
+ return true;
76
+ }
77
+ return false;
49
78
  }
50
79
  catch {
51
80
  return false;
52
81
  }
53
82
  }
54
83
  function extractRanges(parsedText) {
84
+ // Skip if the entire text is wrapped in {} (array constant)
85
+ const trimmed = parsedText.trim();
86
+ if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
87
+ return [];
88
+ }
55
89
  const ranges = [];
56
90
  let quotesOpened = false;
57
91
  let last = "";
@@ -79,5 +79,11 @@ class ListXform extends BaseXform {
79
79
  });
80
80
  }
81
81
  }
82
+ reset() {
83
+ super.reset();
84
+ if (this.childXform) {
85
+ this.childXform.reset();
86
+ }
87
+ }
82
88
  }
83
89
  export { ListXform };
@@ -407,7 +407,12 @@ class CellXform extends BaseXform {
407
407
  }
408
408
  break;
409
409
  case Enums.ValueType.Formula:
410
- if (model.result !== undefined && style && isDateFmt(style.numFmt)) {
410
+ // Only convert formula result to date if the result is a number
411
+ // String results (t="str") should not be converted even if the cell has a date format
412
+ if (model.result !== undefined &&
413
+ typeof model.result === "number" &&
414
+ style &&
415
+ isDateFmt(style.numFmt)) {
411
416
  model.result = excelToDate(model.result, options.date1904);
412
417
  }
413
418
  if (model.shareType === "shared") {
@@ -1,6 +1,7 @@
1
1
  import { BaseXform } from "../base-xform.js";
2
2
  import { CellXform } from "./cell-xform.js";
3
3
  import { parseBoolean } from "../../../utils/utils.js";
4
+ import { colCache } from "../../../utils/col-cache.js";
4
5
  class RowXform extends BaseXform {
5
6
  constructor(options) {
6
7
  super();
@@ -12,6 +13,11 @@ class RowXform extends BaseXform {
12
13
  get tag() {
13
14
  return "row";
14
15
  }
16
+ reset() {
17
+ super.reset();
18
+ this.numRowsSeen = 0;
19
+ this.lastCellCol = 0;
20
+ }
15
21
  prepare(model, options) {
16
22
  const styleId = options.styles.addStyleModel(model.style);
17
23
  if (styleId) {
@@ -62,11 +68,15 @@ class RowXform extends BaseXform {
62
68
  }
63
69
  if (node.name === "row") {
64
70
  this.numRowsSeen += 1;
71
+ // Reset lastCellCol for each new row
72
+ this.lastCellCol = 0;
65
73
  const spans = node.attributes.spans
66
74
  ? node.attributes.spans.split(":").map((span) => parseInt(span, 10))
67
75
  : [undefined, undefined];
76
+ // If r attribute is missing, use numRowsSeen as the row number
77
+ const rowNumber = node.attributes.r ? parseInt(node.attributes.r, 10) : this.numRowsSeen;
68
78
  const model = (this.model = {
69
- number: parseInt(node.attributes.r, 10),
79
+ number: rowNumber,
70
80
  min: spans[0],
71
81
  max: spans[1],
72
82
  cells: []
@@ -106,7 +116,19 @@ class RowXform extends BaseXform {
106
116
  parseClose(name) {
107
117
  if (this.parser) {
108
118
  if (!this.parser.parseClose(name)) {
109
- this.model.cells.push(this.parser.model);
119
+ const cellModel = this.parser.model;
120
+ // If cell has address, extract column number from it
121
+ // Otherwise, calculate address based on position
122
+ if (cellModel.address) {
123
+ const decoded = colCache.decodeAddress(cellModel.address);
124
+ this.lastCellCol = decoded.col;
125
+ }
126
+ else {
127
+ // No r attribute, calculate address from position
128
+ this.lastCellCol += 1;
129
+ cellModel.address = colCache.encodeAddress(this.model.number, this.lastCellCol);
130
+ }
131
+ this.model.cells.push(cellModel);
110
132
  if (this.maxItems && this.model.cells.length > this.maxItems) {
111
133
  throw new Error(`Max column count (${this.maxItems}) exceeded`);
112
134
  }
@@ -54,6 +54,10 @@ class FilterColumnXform extends BaseXform {
54
54
  filterButton: attributes.hiddenButton === "0"
55
55
  };
56
56
  return true;
57
+ case "dynamicFilter":
58
+ // Ignore dynamicFilter nodes - we don't need to preserve them for reading
59
+ // See: https://github.com/exceljs/exceljs/issues/2972
60
+ return true;
57
61
  default:
58
62
  this.parser = this.map[node.name];
59
63
  if (this.parser) {
@@ -73,7 +73,7 @@ export interface JSON2SheetOpts {
73
73
  /** Use specified field order (default Object.keys) */
74
74
  header?: string[];
75
75
  /** Use specified date format in string output */
76
- dateNF?: string;
76
+ dateFormat?: string;
77
77
  /** Store dates as type d (default is n) */
78
78
  cellDates?: boolean;
79
79
  /** If true, do not include header row in output */
@@ -117,6 +117,12 @@ export interface Sheet2JSONOpts {
117
117
  defval?: CellValue;
118
118
  /** Include blank lines in the output */
119
119
  blankrows?: boolean;
120
+ /**
121
+ * Override format for date values (not applied to time-only formats).
122
+ * When provided, Date values will be formatted using this format string.
123
+ * @example "dd/mm/yyyy" or "yyyy-mm-dd"
124
+ */
125
+ dateFormat?: string;
120
126
  }
121
127
  /**
122
128
  * Convert worksheet to JSON array (xlsx compatible)
@@ -164,7 +170,7 @@ export interface AOA2SheetOpts {
164
170
  /** Use specified cell as starting point */
165
171
  origin?: Origin;
166
172
  /** Use specified date format in string output */
167
- dateNF?: string;
173
+ dateFormat?: string;
168
174
  /** Store dates as type d (default is n) */
169
175
  cellDates?: boolean;
170
176
  }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Simple ZIP extraction utilities
3
+ * Provides easy-to-use Promise-based API for extracting ZIP files
4
+ */
5
+ import { type ZipEntry } from "./parse.js";
6
+ /**
7
+ * Extracted file entry
8
+ */
9
+ export interface ExtractedFile {
10
+ /** File path within the ZIP */
11
+ path: string;
12
+ /** File content as Buffer */
13
+ data: Buffer;
14
+ /** Whether this is a directory */
15
+ isDirectory: boolean;
16
+ /** Uncompressed size */
17
+ size: number;
18
+ }
19
+ /**
20
+ * Extract all files from a ZIP buffer
21
+ *
22
+ * @param zipData - ZIP file data as Buffer or Uint8Array
23
+ * @returns Map of file paths to their content
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import { extractAll } from "./utils/unzip/extract.js";
28
+ *
29
+ * const zipData = fs.readFileSync("archive.zip");
30
+ * const files = await extractAll(zipData);
31
+ *
32
+ * for (const [path, file] of files) {
33
+ * console.log(`${path}: ${file.data.length} bytes`);
34
+ * }
35
+ * ```
36
+ */
37
+ export declare function extractAll(zipData: Buffer | Uint8Array): Promise<Map<string, ExtractedFile>>;
38
+ /**
39
+ * Extract a single file from a ZIP buffer
40
+ *
41
+ * @param zipData - ZIP file data as Buffer or Uint8Array
42
+ * @param filePath - Path of the file to extract
43
+ * @returns File content as Buffer, or null if not found
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * import { extractFile } from "./utils/unzip/extract.js";
48
+ *
49
+ * const zipData = fs.readFileSync("archive.zip");
50
+ * const content = await extractFile(zipData, "readme.txt");
51
+ * if (content) {
52
+ * console.log(content.toString("utf-8"));
53
+ * }
54
+ * ```
55
+ */
56
+ export declare function extractFile(zipData: Buffer | Uint8Array, filePath: string): Promise<Buffer | null>;
57
+ /**
58
+ * List all file paths in a ZIP buffer (without extracting content)
59
+ *
60
+ * @param zipData - ZIP file data as Buffer or Uint8Array
61
+ * @returns Array of file paths
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * import { listFiles } from "./utils/unzip/extract.js";
66
+ *
67
+ * const zipData = fs.readFileSync("archive.zip");
68
+ * const paths = await listFiles(zipData);
69
+ * console.log(paths); // ["file1.txt", "folder/file2.txt", ...]
70
+ * ```
71
+ */
72
+ export declare function listFiles(zipData: Buffer | Uint8Array): Promise<string[]>;
73
+ /**
74
+ * Iterate over ZIP entries with a callback (memory efficient for large ZIPs)
75
+ *
76
+ * @param zipData - ZIP file data as Buffer or Uint8Array
77
+ * @param callback - Async callback for each entry, return false to stop iteration
78
+ *
79
+ * @example
80
+ * ```ts
81
+ * import { forEachEntry } from "./utils/unzip/extract.js";
82
+ *
83
+ * await forEachEntry(zipData, async (path, getData) => {
84
+ * if (path.endsWith(".xml")) {
85
+ * const content = await getData();
86
+ * console.log(content.toString("utf-8"));
87
+ * }
88
+ * return true; // continue iteration
89
+ * });
90
+ * ```
91
+ */
92
+ export declare function forEachEntry(zipData: Buffer | Uint8Array, callback: (path: string, getData: () => Promise<Buffer>, entry: ZipEntry) => Promise<boolean | void>): Promise<void>;
@@ -10,3 +10,4 @@ export { bufferStream } from "./buffer-stream.js";
10
10
  export { parse as parseBuffer } from "./parse-buffer.js";
11
11
  export { parseDateTime } from "./parse-datetime.js";
12
12
  export { parseExtraField, type ExtraField, type ZipVars } from "./parse-extra-field.js";
13
+ export { extractAll, extractFile, listFiles, forEachEntry, type ExtractedFile } from "./extract.js";
@@ -3,11 +3,13 @@ interface Attributes {
3
3
  }
4
4
  declare class XmlStream {
5
5
  private _xml;
6
+ private _chunks;
6
7
  private _stack;
7
8
  private _rollbacks;
8
9
  leaf?: boolean;
9
10
  open?: boolean;
10
11
  constructor();
12
+ private _consolidate;
11
13
  get tos(): string | undefined;
12
14
  get cursor(): number;
13
15
  openXml(docAttributes?: Attributes): void;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Native compression utilities using platform APIs
3
+ *
4
+ * - Node.js: Uses native zlib module (C++ implementation, fastest)
5
+ * - Browser: Uses CompressionStream API (Chrome 80+, Firefox 113+, Safari 16.4+)
6
+ *
7
+ * Both use "deflate-raw" format which is required for ZIP files
8
+ * (raw DEFLATE without zlib header/trailer)
9
+ */
10
+ import type * as zlibType from "zlib";
11
+ /**
12
+ * Compression options
13
+ */
14
+ export interface CompressOptions {
15
+ /**
16
+ * Compression level (0-9)
17
+ * - 0: No compression (STORE)
18
+ * - 1: Fastest compression
19
+ * - 6: Default compression (good balance)
20
+ * - 9: Best compression (slowest)
21
+ *
22
+ * Note: CompressionStream does not support level configuration,
23
+ * it uses a fixed level (~6)
24
+ */
25
+ level?: number;
26
+ }
27
+ /**
28
+ * Ensure zlib is loaded (Node.js only)
29
+ * Call this before using sync methods if you need to guarantee availability
30
+ */
31
+ export declare function ensureZlib(): Promise<typeof zlibType | null>;
32
+ /**
33
+ * Check if native zlib is available (Node.js)
34
+ */
35
+ export declare function hasNativeZlib(): boolean;
36
+ /**
37
+ * Check if CompressionStream is available (Browser/Node.js 17+)
38
+ */
39
+ export declare function hasCompressionStream(): boolean;
40
+ /**
41
+ * Compress data using the best available native method
42
+ *
43
+ * Priority:
44
+ * 1. Node.js zlib (if available) - fastest, supports compression levels
45
+ * 2. CompressionStream (browser/Node.js 17+) - no level support
46
+ * 3. Return uncompressed data (fallback)
47
+ *
48
+ * @param data - Data to compress
49
+ * @param options - Compression options
50
+ * @returns Compressed data
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * const data = new TextEncoder().encode("Hello, World!");
55
+ * const compressed = await compress(data, { level: 6 });
56
+ * ```
57
+ */
58
+ export declare function compress(data: Uint8Array, options?: CompressOptions): Promise<Uint8Array>;
59
+ /**
60
+ * Compress data synchronously using Node.js zlib
61
+ * Only available in Node.js environment
62
+ *
63
+ * @param data - Data to compress
64
+ * @param options - Compression options
65
+ * @returns Compressed data
66
+ * @throws Error if not in Node.js environment
67
+ */
68
+ export declare function compressSync(data: Uint8Array, options?: CompressOptions): Uint8Array;
69
+ /**
70
+ * Decompress data using the best available native method
71
+ *
72
+ * @param data - Compressed data (deflate-raw format)
73
+ * @returns Decompressed data
74
+ */
75
+ export declare function decompress(data: Uint8Array): Promise<Uint8Array>;
76
+ /**
77
+ * Decompress data synchronously using Node.js zlib
78
+ *
79
+ * @param data - Compressed data (deflate-raw format)
80
+ * @returns Decompressed data
81
+ * @throws Error if not in Node.js environment
82
+ */
83
+ export declare function decompressSync(data: Uint8Array): Uint8Array;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * CRC32 calculation utility for ZIP files
3
+ *
4
+ * - Node.js: Uses native zlib.crc32 (C++ implementation, ~100x faster)
5
+ * - Browser: Uses lookup table optimization
6
+ *
7
+ * The polynomial used is the standard CRC-32 IEEE 802.3:
8
+ * x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
9
+ * Represented as 0xEDB88320 in reversed (LSB-first) form
10
+ */
11
+ /**
12
+ * Calculate CRC32 checksum for the given data
13
+ * Uses native zlib.crc32 in Node.js for ~100x better performance
14
+ *
15
+ * @param data - Input data as Uint8Array or Buffer
16
+ * @returns CRC32 checksum as unsigned 32-bit integer
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const data = new TextEncoder().encode("Hello, World!");
21
+ * const checksum = crc32(data);
22
+ * console.log(checksum.toString(16)); // "ec4ac3d0"
23
+ * ```
24
+ */
25
+ export declare function crc32(data: Uint8Array): number;
26
+ /**
27
+ * Ensure zlib is loaded (for use before calling crc32)
28
+ */
29
+ export declare function ensureCrc32(): Promise<void>;
30
+ /**
31
+ * Calculate CRC32 incrementally (useful for streaming)
32
+ * Call with initial crc of 0xffffffff, then finalize with crc32Finalize
33
+ * Note: This always uses JS implementation for consistency in streaming
34
+ *
35
+ * @param crc - Current CRC value (start with 0xffffffff)
36
+ * @param data - Input data chunk
37
+ * @returns Updated CRC value (not finalized)
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * let crc = 0xffffffff;
42
+ * crc = crc32Update(crc, chunk1);
43
+ * crc = crc32Update(crc, chunk2);
44
+ * const checksum = crc32Finalize(crc);
45
+ * ```
46
+ */
47
+ export declare function crc32Update(crc: number, data: Uint8Array): number;
48
+ /**
49
+ * Finalize CRC32 calculation
50
+ * XOR with 0xffffffff and convert to unsigned 32-bit
51
+ *
52
+ * @param crc - CRC value from crc32Update
53
+ * @returns Final CRC32 checksum
54
+ */
55
+ export declare function crc32Finalize(crc: number): number;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Native ZIP utilities - Pure native implementation without third-party dependencies
3
+ *
4
+ * This module provides ZIP file creation using only native platform APIs:
5
+ * - Node.js: Uses native zlib module (C++ implementation, fastest)
6
+ * - Browser: Uses CompressionStream API (Chrome 80+, Firefox 113+, Safari 16.4+)
7
+ *
8
+ * Features:
9
+ * - Full ZIP format support (Local File Headers, Central Directory, EOCD)
10
+ * - DEFLATE compression (level 0-9 on Node.js, fixed level on browser)
11
+ * - STORE mode (no compression)
12
+ * - UTF-8 filename support
13
+ * - File comments and ZIP comments
14
+ * - Streaming API for large files
15
+ * - Both sync (Node.js) and async APIs
16
+ *
17
+ * @example Basic usage
18
+ * ```ts
19
+ * import { createZip } from "./utils/zip/index.js";
20
+ *
21
+ * const zipData = await createZip([
22
+ * { name: "hello.txt", data: new TextEncoder().encode("Hello!") },
23
+ * { name: "folder/nested.txt", data: new TextEncoder().encode("Nested file") }
24
+ * ], { level: 6 });
25
+ *
26
+ * // Write to file (Node.js)
27
+ * fs.writeFileSync("output.zip", zipData);
28
+ * ```
29
+ *
30
+ * @example Streaming usage
31
+ * ```ts
32
+ * import { ZipBuilder } from "./utils/zip/index.js";
33
+ *
34
+ * const builder = new ZipBuilder({ level: 1 });
35
+ *
36
+ * // Add files one by one
37
+ * const [header1, data1] = await builder.addFile({
38
+ * name: "file1.txt",
39
+ * data: new TextEncoder().encode("File 1 content")
40
+ * });
41
+ * stream.write(header1);
42
+ * stream.write(data1);
43
+ *
44
+ * // Finalize and write central directory
45
+ * for (const chunk of builder.finalize()) {
46
+ * stream.write(chunk);
47
+ * }
48
+ * ```
49
+ */
50
+ export { crc32, crc32Update, crc32Finalize } from "./crc32.js";
51
+ export { compress, compressSync, decompress, decompressSync, hasNativeZlib, hasCompressionStream, type CompressOptions } from "./compress.js";
52
+ export { createZip, createZipSync, ZipBuilder, type ZipEntry, type ZipOptions } from "./zip-builder.js";
@@ -0,0 +1,110 @@
1
+ /**
2
+ * ZIP file format builder
3
+ *
4
+ * Implements ZIP file structure according to PKWARE's APPNOTE.TXT specification
5
+ * https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
6
+ *
7
+ * ZIP file structure:
8
+ * ┌──────────────────────────┐
9
+ * │ Local File Header 1 │
10
+ * │ File Data 1 │
11
+ * ├──────────────────────────┤
12
+ * │ Local File Header 2 │
13
+ * │ File Data 2 │
14
+ * ├──────────────────────────┤
15
+ * │ ... │
16
+ * ├──────────────────────────┤
17
+ * │ Central Directory 1 │
18
+ * │ Central Directory 2 │
19
+ * │ ... │
20
+ * ├──────────────────────────┤
21
+ * │ End of Central Directory │
22
+ * └──────────────────────────┘
23
+ */
24
+ import { type CompressOptions } from "./compress.js";
25
+ /**
26
+ * ZIP file entry
27
+ */
28
+ export interface ZipEntry {
29
+ /** File name (can include directory path, use forward slashes) */
30
+ name: string;
31
+ /** File data (will be compressed unless level=0) */
32
+ data: Uint8Array;
33
+ /** File modification time (optional, defaults to current time) */
34
+ modTime?: Date;
35
+ /** File comment (optional) */
36
+ comment?: string;
37
+ }
38
+ /**
39
+ * ZIP builder options
40
+ */
41
+ export interface ZipOptions extends CompressOptions {
42
+ /** ZIP file comment (optional) */
43
+ comment?: string;
44
+ }
45
+ /**
46
+ * Create a ZIP file from entries (async)
47
+ *
48
+ * @param entries - Files to include in ZIP
49
+ * @param options - ZIP options
50
+ * @returns ZIP file as Uint8Array
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * const zip = await createZip([
55
+ * { name: "hello.txt", data: new TextEncoder().encode("Hello!") },
56
+ * { name: "folder/file.txt", data: new TextEncoder().encode("Nested!") }
57
+ * ], { level: 6 });
58
+ * ```
59
+ */
60
+ export declare function createZip(entries: ZipEntry[], options?: ZipOptions): Promise<Uint8Array>;
61
+ /**
62
+ * Create a ZIP file from entries (sync, Node.js only)
63
+ *
64
+ * @param entries - Files to include in ZIP
65
+ * @param options - ZIP options
66
+ * @returns ZIP file as Uint8Array
67
+ * @throws Error if not in Node.js environment
68
+ */
69
+ export declare function createZipSync(entries: ZipEntry[], options?: ZipOptions): Uint8Array;
70
+ /**
71
+ * Streaming ZIP builder for large files
72
+ * Writes chunks to a callback as they are generated
73
+ */
74
+ export declare class ZipBuilder {
75
+ private entries;
76
+ private currentOffset;
77
+ private level;
78
+ private zipComment;
79
+ private finalized;
80
+ /**
81
+ * Create a new ZIP builder
82
+ * @param options - ZIP options
83
+ */
84
+ constructor(options?: ZipOptions);
85
+ /**
86
+ * Add a file to the ZIP (async)
87
+ * @param entry - File entry
88
+ * @returns Local file header and compressed data chunks
89
+ */
90
+ addFile(entry: ZipEntry): Promise<Uint8Array[]>;
91
+ /**
92
+ * Add a file to the ZIP (sync, Node.js only)
93
+ * @param entry - File entry
94
+ * @returns Local file header and compressed data chunks
95
+ */
96
+ addFileSync(entry: ZipEntry): Uint8Array[];
97
+ /**
98
+ * Finalize the ZIP and return central directory + end record
99
+ * @returns Central directory and end of central directory chunks
100
+ */
101
+ finalize(): Uint8Array[];
102
+ /**
103
+ * Get current number of entries
104
+ */
105
+ get entryCount(): number;
106
+ /**
107
+ * Get current ZIP data size (without central directory)
108
+ */
109
+ get dataSize(): number;
110
+ }
@@ -1,9 +1,9 @@
1
1
  import events from "events";
2
- import { Zip } from "fflate";
3
2
  interface ZipWriterOptions {
4
- type?: string;
3
+ /** Compression method: "DEFLATE" (default) or "STORE" (no compression) */
5
4
  compression?: "DEFLATE" | "STORE";
6
5
  compressionOptions?: {
6
+ /** Compression level 0-9: 0=none, 1=fast (default), 9=best */
7
7
  level?: number;
8
8
  };
9
9
  }
@@ -11,17 +11,11 @@ interface AppendOptions {
11
11
  name: string;
12
12
  base64?: boolean;
13
13
  }
14
- interface ZipFile {
15
- data: Uint8Array;
16
- isStream?: boolean;
17
- }
18
14
  declare class ZipWriter extends events.EventEmitter {
19
- options: ZipWriterOptions;
20
- files: Record<string, ZipFile>;
21
- stream: any;
22
- zip: Zip;
23
- finalized: boolean;
24
- compressionLevel: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
15
+ private stream;
16
+ private zipBuilder;
17
+ private finalized;
18
+ private pendingWrites;
25
19
  constructor(options?: ZipWriterOptions);
26
20
  append(data: any, options: AppendOptions): void;
27
21
  push(chunk: any): boolean;
@@ -27,5 +27,6 @@ declare class ListXform extends BaseXform {
27
27
  parseText(text: string): void;
28
28
  parseClose(name: string): boolean;
29
29
  reconcile(model: any[], options: any): void;
30
+ reset(): void;
30
31
  }
31
32
  export { ListXform };
@@ -23,8 +23,10 @@ declare class RowXform extends BaseXform {
23
23
  model: RowModel;
24
24
  parser: any;
25
25
  private numRowsSeen;
26
+ private lastCellCol;
26
27
  constructor(options?: RowXformOptions);
27
28
  get tag(): string;
29
+ reset(): void;
28
30
  prepare(model: RowModel, options: any): void;
29
31
  render(xmlStream: any, model?: RowModel, options?: any): void;
30
32
  parseOpen(node: any): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cj-tech-master/excelts",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "description": "TypeScript Excel Workbook Manager - Read and Write xlsx and csv Files.",
5
5
  "private": false,
6
6
  "publishConfig": {