@cj-tech-master/excelts 1.0.0 → 1.4.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 (69) hide show
  1. package/dist/browser/excelts.iife.js +2568 -1188
  2. package/dist/browser/excelts.iife.js.map +1 -1
  3. package/dist/browser/excelts.iife.min.js +21 -19
  4. package/dist/cjs/index.js +1 -0
  5. package/dist/cjs/stream/xlsx/workbook-reader.js +2 -2
  6. package/dist/cjs/stream/xlsx/workbook-writer.js +8 -4
  7. package/dist/cjs/utils/cell-format.js +815 -0
  8. package/dist/cjs/utils/cell-matrix.js +37 -2
  9. package/dist/cjs/utils/parse-sax.js +2 -2
  10. package/dist/cjs/utils/sheet-utils.js +615 -0
  11. package/dist/cjs/utils/stream-buf.js +15 -4
  12. package/dist/cjs/utils/unzip/buffer-stream.js +27 -0
  13. package/dist/cjs/utils/unzip/index.js +23 -0
  14. package/dist/cjs/utils/unzip/noop-stream.js +20 -0
  15. package/dist/cjs/utils/unzip/parse-buffer.js +60 -0
  16. package/dist/cjs/utils/unzip/parse-datetime.js +23 -0
  17. package/dist/cjs/utils/unzip/parse-extra-field.js +52 -0
  18. package/dist/cjs/utils/unzip/parse.js +340 -0
  19. package/dist/cjs/utils/unzip/pull-stream.js +145 -0
  20. package/dist/cjs/utils/utils.js +13 -17
  21. package/dist/cjs/utils/zip-stream.js +29 -33
  22. package/dist/cjs/xlsx/xlsx.js +1 -2
  23. package/dist/esm/index.browser.js +1 -0
  24. package/dist/esm/index.js +1 -0
  25. package/dist/esm/stream/xlsx/workbook-reader.js +2 -2
  26. package/dist/esm/stream/xlsx/workbook-writer.js +9 -5
  27. package/dist/esm/utils/cell-format.js +810 -0
  28. package/dist/esm/utils/cell-matrix.js +37 -2
  29. package/dist/esm/utils/parse-sax.js +1 -1
  30. package/dist/esm/utils/sheet-utils.js +595 -0
  31. package/dist/esm/utils/stream-buf.js +15 -4
  32. package/dist/esm/utils/unzip/buffer-stream.js +24 -0
  33. package/dist/esm/utils/unzip/index.js +12 -0
  34. package/dist/esm/utils/unzip/noop-stream.js +16 -0
  35. package/dist/esm/utils/unzip/parse-buffer.js +57 -0
  36. package/dist/esm/utils/unzip/parse-datetime.js +20 -0
  37. package/dist/esm/utils/unzip/parse-extra-field.js +49 -0
  38. package/dist/esm/utils/unzip/parse.js +332 -0
  39. package/dist/esm/utils/unzip/pull-stream.js +141 -0
  40. package/dist/esm/utils/utils.js +12 -16
  41. package/dist/esm/utils/zip-stream.js +30 -34
  42. package/dist/esm/xlsx/xlsx.js +1 -2
  43. package/dist/types/doc/column.d.ts +1 -1
  44. package/dist/types/doc/worksheet.d.ts +2 -2
  45. package/dist/types/index.browser.d.ts +1 -0
  46. package/dist/types/index.d.ts +1 -0
  47. package/dist/types/stream/xlsx/workbook-writer.d.ts +1 -0
  48. package/dist/types/utils/cell-format.d.ts +32 -0
  49. package/dist/types/utils/sheet-utils.d.ts +203 -0
  50. package/dist/types/utils/unzip/buffer-stream.d.ts +9 -0
  51. package/dist/types/utils/unzip/index.d.ts +12 -0
  52. package/dist/types/utils/unzip/noop-stream.d.ts +13 -0
  53. package/dist/types/utils/unzip/parse-buffer.d.ts +24 -0
  54. package/dist/types/utils/unzip/parse-datetime.d.ts +12 -0
  55. package/dist/types/utils/unzip/parse-extra-field.d.ts +18 -0
  56. package/dist/types/utils/unzip/parse.d.ts +70 -0
  57. package/dist/types/utils/unzip/pull-stream.d.ts +24 -0
  58. package/dist/types/utils/utils.d.ts +5 -2
  59. package/dist/types/utils/zip-stream.d.ts +5 -1
  60. package/package.json +35 -32
  61. package/dist/cjs/utils/browser-buffer-decode.js +0 -13
  62. package/dist/cjs/utils/browser-buffer-encode.js +0 -13
  63. package/dist/cjs/utils/browser.js +0 -6
  64. package/dist/esm/utils/browser-buffer-decode.js +0 -11
  65. package/dist/esm/utils/browser-buffer-encode.js +0 -11
  66. package/dist/esm/utils/browser.js +0 -3
  67. package/dist/types/utils/browser-buffer-decode.d.ts +0 -2
  68. package/dist/types/utils/browser-buffer-encode.d.ts +0 -2
  69. package/dist/types/utils/browser.d.ts +0 -1
@@ -2,6 +2,29 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CellMatrix = void 0;
4
4
  const col_cache_js_1 = require("./col-cache");
5
+ // Helper to check for prototype pollution
6
+ function isSafeKey(key) {
7
+ if (typeof key === "number") {
8
+ return true;
9
+ }
10
+ return key !== "__proto__" && key !== "constructor" && key !== "prototype";
11
+ }
12
+ // Safe deep clone that filters out prototype pollution keys
13
+ function safeDeepClone(obj) {
14
+ if (obj === null || typeof obj !== "object") {
15
+ return obj;
16
+ }
17
+ if (Array.isArray(obj)) {
18
+ return obj.map(item => safeDeepClone(item));
19
+ }
20
+ const result = {};
21
+ for (const key of Object.keys(obj)) {
22
+ if (isSafeKey(key)) {
23
+ result[key] = safeDeepClone(obj[key]);
24
+ }
25
+ }
26
+ return result;
27
+ }
5
28
  class CellMatrix {
6
29
  constructor(template) {
7
30
  this.template = template;
@@ -42,6 +65,9 @@ class CellMatrix {
42
65
  return this.findRowCell(row, address, create);
43
66
  }
44
67
  getCellAt(sheetName, rowNumber, colNumber) {
68
+ if (!isSafeKey(sheetName)) {
69
+ throw new Error(`Invalid sheet name: ${sheetName}`);
70
+ }
45
71
  const sheet = this.sheets[sheetName] || (this.sheets[sheetName] = []);
46
72
  const row = sheet[rowNumber] || (sheet[rowNumber] = []);
47
73
  const cell = row[colNumber] ||
@@ -92,7 +118,10 @@ class CellMatrix {
92
118
  }
93
119
  findSheet(address, create) {
94
120
  const name = address.sheetName;
95
- if (this.sheets[name]) {
121
+ if (!isSafeKey(name)) {
122
+ throw new Error(`Invalid sheet name: ${name}`);
123
+ }
124
+ if (Object.prototype.hasOwnProperty.call(this.sheets, name)) {
96
125
  return this.sheets[name];
97
126
  }
98
127
  if (create) {
@@ -102,6 +131,9 @@ class CellMatrix {
102
131
  }
103
132
  findSheetRow(sheet, address, create) {
104
133
  const { row } = address;
134
+ if (!isSafeKey(row)) {
135
+ throw new Error(`Invalid row: ${row}`);
136
+ }
105
137
  if (sheet && sheet[row]) {
106
138
  return sheet[row];
107
139
  }
@@ -112,12 +144,15 @@ class CellMatrix {
112
144
  }
113
145
  findRowCell(row, address, create) {
114
146
  const { col } = address;
147
+ if (!isSafeKey(col)) {
148
+ throw new Error(`Invalid column: ${col}`);
149
+ }
115
150
  if (row && row[col]) {
116
151
  return row[col];
117
152
  }
118
153
  if (create) {
119
154
  return (row[col] = this.template
120
- ? Object.assign(address, JSON.parse(JSON.stringify(this.template)))
155
+ ? { ...address, ...safeDeepClone(this.template) }
121
156
  : address);
122
157
  }
123
158
  return undefined;
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseSax = parseSax;
4
4
  const saxes_1 = require("saxes");
5
- const browser_buffer_decode_js_1 = require("./browser-buffer-decode");
5
+ const utils_js_1 = require("./utils");
6
6
  async function* parseSax(iterable) {
7
7
  const saxesParser = new saxes_1.SaxesParser({
8
8
  xmlns: false,
@@ -17,7 +17,7 @@ async function* parseSax(iterable) {
17
17
  saxesParser.on("text", (value) => events.push({ eventType: "text", value }));
18
18
  saxesParser.on("closetag", (value) => events.push({ eventType: "closetag", value }));
19
19
  for await (const chunk of iterable) {
20
- saxesParser.write((0, browser_buffer_decode_js_1.bufferToString)(chunk));
20
+ saxesParser.write((0, utils_js_1.bufferToString)(chunk));
21
21
  // saxesParser.write and saxesParser.on() are synchronous,
22
22
  // so we can only reach the below line once all events have been emitted
23
23
  if (error) {
@@ -0,0 +1,615 @@
1
+ "use strict";
2
+ /**
3
+ * Utility functions for ExcelTS
4
+ * Provides convenient helper functions for common spreadsheet operations
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.utils = void 0;
8
+ exports.decodeCol = decodeCol;
9
+ exports.encodeCol = encodeCol;
10
+ exports.decodeRow = decodeRow;
11
+ exports.encodeRow = encodeRow;
12
+ exports.decodeCell = decodeCell;
13
+ exports.encodeCell = encodeCell;
14
+ exports.decodeRange = decodeRange;
15
+ exports.encodeRange = encodeRange;
16
+ exports.jsonToSheet = jsonToSheet;
17
+ exports.sheetAddJson = sheetAddJson;
18
+ exports.sheetToJson = sheetToJson;
19
+ exports.sheetToCsv = sheetToCsv;
20
+ exports.bookNew = bookNew;
21
+ exports.bookAppendSheet = bookAppendSheet;
22
+ exports.aoaToSheet = aoaToSheet;
23
+ exports.sheetAddAoa = sheetAddAoa;
24
+ exports.sheetToAoa = sheetToAoa;
25
+ const workbook_js_1 = require("../doc/workbook");
26
+ const col_cache_js_1 = require("./col-cache");
27
+ const utils_js_1 = require("./utils");
28
+ const cell_format_js_1 = require("./cell-format");
29
+ /**
30
+ * Get formatted display text for a cell value
31
+ * Returns the value formatted according to the cell's numFmt
32
+ * This matches Excel's display exactly
33
+ */
34
+ function getCellDisplayText(cell) {
35
+ const value = cell.value;
36
+ const fmt = cell.numFmt || "General";
37
+ // Null/undefined
38
+ if (value == null) {
39
+ return "";
40
+ }
41
+ // Date object - convert to Excel serial number
42
+ if (value instanceof Date) {
43
+ const serial = (0, utils_js_1.dateToExcel)(value, false);
44
+ return (0, cell_format_js_1.format)(fmt, serial);
45
+ }
46
+ // Number/Boolean/String - let cellFormat handle it
47
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "string") {
48
+ return (0, cell_format_js_1.format)(fmt, value);
49
+ }
50
+ // Fallback to cell.text for other types (rich text, hyperlink, error, formula, etc.)
51
+ return cell.text;
52
+ }
53
+ // =============================================================================
54
+ // Cell Address Encoding/Decoding
55
+ // =============================================================================
56
+ /**
57
+ * Decode column string to 0-indexed number
58
+ * @example decodeCol("A") => 0, decodeCol("Z") => 25, decodeCol("AA") => 26
59
+ */
60
+ function decodeCol(colstr) {
61
+ return col_cache_js_1.colCache.l2n(colstr.toUpperCase()) - 1;
62
+ }
63
+ /**
64
+ * Encode 0-indexed column number to string
65
+ * @example encodeCol(0) => "A", encodeCol(25) => "Z", encodeCol(26) => "AA"
66
+ */
67
+ function encodeCol(col) {
68
+ return col_cache_js_1.colCache.n2l(col + 1);
69
+ }
70
+ /**
71
+ * Decode row string to 0-indexed number
72
+ * @example decodeRow("1") => 0, decodeRow("10") => 9
73
+ */
74
+ function decodeRow(rowstr) {
75
+ return parseInt(rowstr, 10) - 1;
76
+ }
77
+ /**
78
+ * Encode 0-indexed row number to string
79
+ * @example encodeRow(0) => "1", encodeRow(9) => "10"
80
+ */
81
+ function encodeRow(row) {
82
+ return String(row + 1);
83
+ }
84
+ /**
85
+ * Decode cell address string to CellAddress object
86
+ * @example decodeCell("A1") => {c: 0, r: 0}, decodeCell("B2") => {c: 1, r: 1}
87
+ */
88
+ function decodeCell(cstr) {
89
+ const addr = col_cache_js_1.colCache.decodeAddress(cstr.toUpperCase());
90
+ return { c: addr.col - 1, r: addr.row - 1 };
91
+ }
92
+ /**
93
+ * Encode CellAddress object to cell address string
94
+ * @example encodeCell({c: 0, r: 0}) => "A1", encodeCell({c: 1, r: 1}) => "B2"
95
+ */
96
+ function encodeCell(cell) {
97
+ return col_cache_js_1.colCache.encodeAddress(cell.r + 1, cell.c + 1);
98
+ }
99
+ /**
100
+ * Decode range string to Range object
101
+ * @example decodeRange("A1:B2") => {s: {c: 0, r: 0}, e: {c: 1, r: 1}}
102
+ */
103
+ function decodeRange(range) {
104
+ const idx = range.indexOf(":");
105
+ if (idx === -1) {
106
+ const cell = decodeCell(range);
107
+ return { s: cell, e: { ...cell } };
108
+ }
109
+ return {
110
+ s: decodeCell(range.slice(0, idx)),
111
+ e: decodeCell(range.slice(idx + 1))
112
+ };
113
+ }
114
+ function encodeRange(startOrRange, end) {
115
+ if (end === undefined) {
116
+ const range = startOrRange;
117
+ return encodeRange(range.s, range.e);
118
+ }
119
+ const start = startOrRange;
120
+ const startStr = encodeCell(start);
121
+ const endStr = encodeCell(end);
122
+ return startStr === endStr ? startStr : `${startStr}:${endStr}`;
123
+ }
124
+ /**
125
+ * Create a worksheet from an array of objects (xlsx compatible)
126
+ * @example
127
+ * const ws = jsonToSheet([{name: "Alice", age: 30}, {name: "Bob", age: 25}])
128
+ */
129
+ function jsonToSheet(data, opts) {
130
+ const o = opts || {};
131
+ // Create a temporary workbook to get a worksheet
132
+ const tempWb = new workbook_js_1.Workbook();
133
+ const worksheet = tempWb.addWorksheet("Sheet1");
134
+ if (data.length === 0) {
135
+ return worksheet;
136
+ }
137
+ // Determine headers - use provided header or Object.keys from first object
138
+ const allKeys = new Set();
139
+ data.forEach(row => Object.keys(row).forEach(k => allKeys.add(k)));
140
+ const headers = o.header ? [...o.header] : [...allKeys];
141
+ // Add any missing keys from data that weren't in header
142
+ if (o.header) {
143
+ allKeys.forEach(k => {
144
+ if (!headers.includes(k)) {
145
+ headers.push(k);
146
+ }
147
+ });
148
+ }
149
+ let rowNum = 1;
150
+ // Write header row
151
+ if (!o.skipHeader) {
152
+ headers.forEach((h, colIdx) => {
153
+ worksheet.getCell(rowNum, colIdx + 1).value = h;
154
+ });
155
+ rowNum++;
156
+ }
157
+ // Write data rows
158
+ for (const row of data) {
159
+ headers.forEach((key, colIdx) => {
160
+ const val = row[key];
161
+ if (val === null && o.nullError) {
162
+ worksheet.getCell(rowNum, colIdx + 1).value = { error: "#NULL!" };
163
+ }
164
+ else if (val !== undefined && val !== null) {
165
+ worksheet.getCell(rowNum, colIdx + 1).value = val;
166
+ }
167
+ });
168
+ rowNum++;
169
+ }
170
+ return worksheet;
171
+ }
172
+ /**
173
+ * Add data from an array of objects to an existing worksheet (xlsx compatible)
174
+ */
175
+ function sheetAddJson(worksheet, data, opts) {
176
+ const o = opts || {};
177
+ if (data.length === 0) {
178
+ return worksheet;
179
+ }
180
+ // Determine starting position
181
+ let startRow = 1;
182
+ let startCol = 1;
183
+ if (o.origin !== undefined) {
184
+ if (typeof o.origin === "string") {
185
+ const addr = decodeCell(o.origin);
186
+ startRow = addr.r + 1;
187
+ startCol = addr.c + 1;
188
+ }
189
+ else if (typeof o.origin === "number") {
190
+ if (o.origin === -1) {
191
+ // Append to bottom
192
+ startRow = worksheet.rowCount + 1;
193
+ }
194
+ else {
195
+ startRow = o.origin + 1; // 0-indexed row
196
+ }
197
+ }
198
+ else {
199
+ startRow = o.origin.r + 1;
200
+ startCol = o.origin.c + 1;
201
+ }
202
+ }
203
+ // Determine headers
204
+ const allKeys = new Set();
205
+ data.forEach(row => Object.keys(row).forEach(k => allKeys.add(k)));
206
+ const headers = o.header ? [...o.header] : [...allKeys];
207
+ if (o.header) {
208
+ allKeys.forEach(k => {
209
+ if (!headers.includes(k)) {
210
+ headers.push(k);
211
+ }
212
+ });
213
+ }
214
+ let rowNum = startRow;
215
+ // Write header row
216
+ if (!o.skipHeader) {
217
+ headers.forEach((h, colIdx) => {
218
+ worksheet.getCell(rowNum, startCol + colIdx).value = h;
219
+ });
220
+ rowNum++;
221
+ }
222
+ // Write data rows
223
+ for (const row of data) {
224
+ headers.forEach((key, colIdx) => {
225
+ const val = row[key];
226
+ if (val === null && o.nullError) {
227
+ worksheet.getCell(rowNum, startCol + colIdx).value = { error: "#NULL!" };
228
+ }
229
+ else if (val !== undefined && val !== null) {
230
+ worksheet.getCell(rowNum, startCol + colIdx).value = val;
231
+ }
232
+ });
233
+ rowNum++;
234
+ }
235
+ return worksheet;
236
+ }
237
+ /**
238
+ * Convert worksheet to JSON array (xlsx compatible)
239
+ * @example
240
+ * // Default: array of objects with first row as headers
241
+ * const data = sheetToJson(worksheet)
242
+ * // => [{name: "Alice", age: 30}, {name: "Bob", age: 25}]
243
+ *
244
+ * // Array of arrays
245
+ * const aoa = sheetToJson(worksheet, { header: 1 })
246
+ * // => [["name", "age"], ["Alice", 30], ["Bob", 25]]
247
+ *
248
+ * // Column letters as keys
249
+ * const cols = sheetToJson(worksheet, { header: "A" })
250
+ * // => [{A: "name", B: "age"}, {A: "Alice", B: 30}]
251
+ */
252
+ function sheetToJson(worksheet, opts) {
253
+ const o = opts || {};
254
+ // Determine range
255
+ let startRow = 1;
256
+ let endRow = worksheet.rowCount;
257
+ let startCol = 1;
258
+ let endCol = worksheet.columnCount;
259
+ if (o.range !== undefined) {
260
+ if (typeof o.range === "number") {
261
+ startRow = o.range + 1; // 0-indexed to 1-indexed
262
+ }
263
+ else if (typeof o.range === "string") {
264
+ const r = decodeRange(o.range);
265
+ startRow = r.s.r + 1;
266
+ endRow = r.e.r + 1;
267
+ startCol = r.s.c + 1;
268
+ endCol = r.e.c + 1;
269
+ }
270
+ }
271
+ if (endRow < startRow || endCol < startCol) {
272
+ return [];
273
+ }
274
+ // Handle header option
275
+ const headerOpt = o.header;
276
+ // header: 1 - return array of arrays
277
+ if (headerOpt === 1) {
278
+ const result = [];
279
+ // Default for header:1 is to include blank rows
280
+ const includeBlank = o.blankrows !== false;
281
+ for (let row = startRow; row <= endRow; row++) {
282
+ const rowData = [];
283
+ let isEmpty = true;
284
+ for (let col = startCol; col <= endCol; col++) {
285
+ const cell = worksheet.getCell(row, col);
286
+ const val = o.raw === false ? getCellDisplayText(cell).trim() : cell.value;
287
+ if (val != null && val !== "") {
288
+ rowData[col - startCol] = val;
289
+ isEmpty = false;
290
+ }
291
+ else if (o.defval !== undefined) {
292
+ rowData[col - startCol] = o.defval;
293
+ }
294
+ else {
295
+ rowData[col - startCol] = null;
296
+ }
297
+ }
298
+ if (!isEmpty || includeBlank) {
299
+ result.push(rowData);
300
+ }
301
+ }
302
+ return result;
303
+ }
304
+ // header: "A" - use column letters as keys
305
+ if (headerOpt === "A") {
306
+ const result = [];
307
+ // Default for header:"A" is to skip blank rows
308
+ const includeBlank = o.blankrows === true;
309
+ for (let row = startRow; row <= endRow; row++) {
310
+ const rowData = {};
311
+ let isEmpty = true;
312
+ for (let col = startCol; col <= endCol; col++) {
313
+ const cell = worksheet.getCell(row, col);
314
+ const val = o.raw === false ? getCellDisplayText(cell).trim() : cell.value;
315
+ const key = encodeCol(col - 1); // 0-indexed for encodeCol
316
+ if (val != null && val !== "") {
317
+ rowData[key] = val;
318
+ isEmpty = false;
319
+ }
320
+ else if (o.defval !== undefined) {
321
+ rowData[key] = o.defval;
322
+ }
323
+ }
324
+ if (!isEmpty || includeBlank) {
325
+ result.push(rowData);
326
+ }
327
+ }
328
+ return result;
329
+ }
330
+ // header: string[] - use provided array as keys
331
+ if (Array.isArray(headerOpt)) {
332
+ const result = [];
333
+ const includeBlank = o.blankrows === true;
334
+ for (let row = startRow; row <= endRow; row++) {
335
+ const rowData = {};
336
+ let isEmpty = true;
337
+ for (let col = startCol; col <= endCol; col++) {
338
+ const colIdx = col - startCol;
339
+ const key = headerOpt[colIdx] ?? `__EMPTY_${colIdx}`;
340
+ const cell = worksheet.getCell(row, col);
341
+ const val = o.raw === false ? getCellDisplayText(cell).trim() : cell.value;
342
+ if (val != null && val !== "") {
343
+ rowData[key] = val;
344
+ isEmpty = false;
345
+ }
346
+ else if (o.defval !== undefined) {
347
+ rowData[key] = o.defval;
348
+ }
349
+ }
350
+ if (!isEmpty || includeBlank) {
351
+ result.push(rowData);
352
+ }
353
+ }
354
+ return result;
355
+ }
356
+ // Default: first row as header, disambiguate duplicates
357
+ const headers = [];
358
+ const headerCounts = {};
359
+ for (let col = startCol; col <= endCol; col++) {
360
+ const cell = worksheet.getCell(startRow, col);
361
+ const val = cell.value;
362
+ let header = val != null ? String(val) : `__EMPTY_${col - startCol}`;
363
+ // Disambiguate duplicate headers
364
+ if (headerCounts[header] !== undefined) {
365
+ headerCounts[header]++;
366
+ header = `${header}_${headerCounts[header]}`;
367
+ }
368
+ else {
369
+ headerCounts[header] = 0;
370
+ }
371
+ headers.push(header);
372
+ }
373
+ // Read data rows (skip header row)
374
+ const result = [];
375
+ const dataStartRow = startRow + 1;
376
+ // Default for objects is to skip blank rows
377
+ const includeBlank = o.blankrows === true;
378
+ for (let row = dataStartRow; row <= endRow; row++) {
379
+ const rowData = {};
380
+ let isEmpty = true;
381
+ for (let col = startCol; col <= endCol; col++) {
382
+ const cell = worksheet.getCell(row, col);
383
+ const val = o.raw === false ? getCellDisplayText(cell).trim() : cell.value;
384
+ const key = headers[col - startCol];
385
+ if (val != null && val !== "") {
386
+ rowData[key] = val;
387
+ isEmpty = false;
388
+ }
389
+ else if (o.defval !== undefined) {
390
+ rowData[key] = o.defval;
391
+ }
392
+ }
393
+ if (!isEmpty || includeBlank) {
394
+ result.push(rowData);
395
+ }
396
+ }
397
+ return result;
398
+ }
399
+ /**
400
+ * Convert worksheet to CSV string
401
+ */
402
+ function sheetToCsv(worksheet, opts) {
403
+ const o = opts || {};
404
+ const FS = o.FS ?? ",";
405
+ const RS = o.RS ?? "\n";
406
+ const rows = [];
407
+ worksheet.eachRow({ includeEmpty: o.blankrows !== false }, (row, rowNumber) => {
408
+ const cells = [];
409
+ let isEmpty = true;
410
+ row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
411
+ let val = "";
412
+ if (cell.value != null) {
413
+ if (cell.value instanceof Date) {
414
+ val = cell.value.toISOString();
415
+ }
416
+ else if (typeof cell.value === "object") {
417
+ // Handle rich text, formula results, etc.
418
+ if ("result" in cell.value) {
419
+ val = String(cell.value.result ?? "");
420
+ }
421
+ else if ("text" in cell.value) {
422
+ val = String(cell.value.text ?? "");
423
+ }
424
+ else if ("richText" in cell.value) {
425
+ val = cell.value.richText.map(r => r.text).join("");
426
+ }
427
+ else {
428
+ val = String(cell.value);
429
+ }
430
+ }
431
+ else {
432
+ val = String(cell.value);
433
+ }
434
+ isEmpty = false;
435
+ }
436
+ // Quote if necessary
437
+ const needsQuote = o.forceQuotes ||
438
+ val.includes(FS) ||
439
+ val.includes('"') ||
440
+ val.includes("\n") ||
441
+ val.includes("\r");
442
+ if (needsQuote) {
443
+ val = `"${val.replace(/"/g, '""')}"`;
444
+ }
445
+ cells.push(val);
446
+ });
447
+ // Pad cells to match column count
448
+ while (cells.length < worksheet.columnCount) {
449
+ cells.push("");
450
+ }
451
+ if (!isEmpty || o.blankrows !== false) {
452
+ rows.push(cells.join(FS));
453
+ }
454
+ });
455
+ return rows.join(RS);
456
+ }
457
+ // =============================================================================
458
+ // Workbook Functions
459
+ // =============================================================================
460
+ /**
461
+ * Create a new workbook
462
+ */
463
+ function bookNew() {
464
+ return new workbook_js_1.Workbook();
465
+ }
466
+ /**
467
+ * Append worksheet to workbook (xlsx compatible)
468
+ * @example
469
+ * const wb = bookNew();
470
+ * const ws = jsonToSheet([{a: 1, b: 2}]);
471
+ * bookAppendSheet(wb, ws, "Sheet1");
472
+ */
473
+ function bookAppendSheet(workbook, worksheet, name) {
474
+ // Copy the worksheet data to a new sheet in the workbook
475
+ const newWs = workbook.addWorksheet(name);
476
+ // Copy all cells from source worksheet
477
+ worksheet.eachRow({ includeEmpty: true }, (row, rowNumber) => {
478
+ row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
479
+ const newCell = newWs.getCell(rowNumber, colNumber);
480
+ newCell.value = cell.value;
481
+ if (cell.style) {
482
+ newCell.style = cell.style;
483
+ }
484
+ });
485
+ });
486
+ // Copy column properties
487
+ worksheet.columns?.forEach((col, idx) => {
488
+ if (col && newWs.columns[idx]) {
489
+ if (col.width) {
490
+ newWs.getColumn(idx + 1).width = col.width;
491
+ }
492
+ }
493
+ });
494
+ }
495
+ /**
496
+ * Create a worksheet from an array of arrays (xlsx compatible)
497
+ * @example
498
+ * const ws = aoaToSheet([["Name", "Age"], ["Alice", 30], ["Bob", 25]])
499
+ */
500
+ function aoaToSheet(data, opts) {
501
+ const tempWb = new workbook_js_1.Workbook();
502
+ const worksheet = tempWb.addWorksheet("Sheet1");
503
+ if (data.length === 0) {
504
+ return worksheet;
505
+ }
506
+ // Determine starting position
507
+ let startRow = 1;
508
+ let startCol = 1;
509
+ if (opts?.origin !== undefined) {
510
+ if (typeof opts.origin === "string") {
511
+ const addr = decodeCell(opts.origin);
512
+ startRow = addr.r + 1;
513
+ startCol = addr.c + 1;
514
+ }
515
+ else if (typeof opts.origin === "number") {
516
+ startRow = opts.origin + 1; // 0-indexed row
517
+ }
518
+ else {
519
+ startRow = opts.origin.r + 1;
520
+ startCol = opts.origin.c + 1;
521
+ }
522
+ }
523
+ data.forEach((row, rowIdx) => {
524
+ if (!row) {
525
+ return;
526
+ }
527
+ row.forEach((val, colIdx) => {
528
+ if (val !== undefined && val !== null) {
529
+ worksheet.getCell(startRow + rowIdx, startCol + colIdx).value = val;
530
+ }
531
+ });
532
+ });
533
+ return worksheet;
534
+ }
535
+ /**
536
+ * Add data from an array of arrays to an existing worksheet (xlsx compatible)
537
+ */
538
+ function sheetAddAoa(worksheet, data, opts) {
539
+ if (data.length === 0) {
540
+ return worksheet;
541
+ }
542
+ // Determine starting position
543
+ let startRow = 1;
544
+ let startCol = 1;
545
+ if (opts?.origin !== undefined) {
546
+ if (typeof opts.origin === "string") {
547
+ const addr = decodeCell(opts.origin);
548
+ startRow = addr.r + 1;
549
+ startCol = addr.c + 1;
550
+ }
551
+ else if (typeof opts.origin === "number") {
552
+ if (opts.origin === -1) {
553
+ // Append to bottom
554
+ startRow = worksheet.rowCount + 1;
555
+ }
556
+ else {
557
+ startRow = opts.origin + 1; // 0-indexed row
558
+ }
559
+ }
560
+ else {
561
+ startRow = opts.origin.r + 1;
562
+ startCol = opts.origin.c + 1;
563
+ }
564
+ }
565
+ data.forEach((row, rowIdx) => {
566
+ if (!row) {
567
+ return;
568
+ }
569
+ row.forEach((val, colIdx) => {
570
+ if (val !== undefined && val !== null) {
571
+ worksheet.getCell(startRow + rowIdx, startCol + colIdx).value = val;
572
+ }
573
+ });
574
+ });
575
+ return worksheet;
576
+ }
577
+ /**
578
+ * Convert worksheet to array of arrays
579
+ */
580
+ function sheetToAoa(worksheet) {
581
+ const result = [];
582
+ worksheet.eachRow({ includeEmpty: true }, (row, rowNumber) => {
583
+ const rowData = [];
584
+ row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
585
+ rowData[colNumber - 1] = cell.value;
586
+ });
587
+ result[rowNumber - 1] = rowData;
588
+ });
589
+ return result;
590
+ }
591
+ // =============================================================================
592
+ // Export utils object
593
+ // =============================================================================
594
+ exports.utils = {
595
+ // Cell encoding/decoding (camelCase)
596
+ decodeCol,
597
+ encodeCol,
598
+ decodeRow,
599
+ encodeRow,
600
+ decodeCell,
601
+ encodeCell,
602
+ decodeRange,
603
+ encodeRange,
604
+ // Sheet/JSON conversion (camelCase)
605
+ jsonToSheet,
606
+ sheetAddJson,
607
+ sheetToJson,
608
+ sheetToCsv,
609
+ aoaToSheet,
610
+ sheetAddAoa,
611
+ sheetToAoa,
612
+ // Workbook functions (camelCase)
613
+ bookNew,
614
+ bookAppendSheet
615
+ };