tinyweb-office-cells 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 aspose-cells-node contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,615 @@
1
+ # tinyweb-cells
2
+
3
+ > Open-source Node.js/TypeScript library for reading, writing, and manipulating Excel XLSX files with full formatting, formula evaluation, and Aspose Cells-compatible API.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/tinyweb-cells.svg)](https://www.npmjs.com/package/tinyweb-cells)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Features
9
+
10
+ - ✅ **Read/Write XLSX files** — full round-trip fidelity (preserves unmodified XML)
11
+ - ✅ **Cell formatting** — fonts, colors, borders, fills, number formats, alignment
12
+ - ✅ **Merge cells** — column widths, row heights
13
+ - ✅ **Formula evaluation** — 43 built-in Excel functions
14
+ - ✅ **Data validation** — dropdown lists, numeric ranges, date ranges, custom formulas
15
+ - ✅ **Conditional formatting** — cell value rules, color scales, data bars, formulas
16
+ - ✅ **Named ranges** — workbook and sheet-scoped defined names
17
+ - ✅ **Hyperlinks** — external URLs, internal sheet references, email links
18
+ - ✅ **Comments** — cell comments with author and size support
19
+ - ✅ **AutoFilter** — column filters with custom, color, dynamic, and top-10 filters
20
+ - ✅ **Page breaks** — horizontal and vertical page break management
21
+ - ✅ **Sheet/workbook protection** — password-based protection with granular permissions
22
+ - ✅ **Document properties** — title, author, subject, keywords, and custom properties
23
+ - ✅ **Page setup** — orientation, paper size, margins, print area, headers/footers
24
+ - ✅ **Freeze panes** — freeze rows and columns
25
+ - ✅ **Dual API naming** — both `camelCase` and `snake_case` method names
26
+ - ✅ **TypeScript-first** — full type declarations included
27
+ - ✅ **Zero native dependencies** — pure JavaScript, works everywhere Node.js runs
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ npm install tinyweb-cells
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ### Create a Workbook and Save
38
+
39
+ ```typescript
40
+ import { Workbook, SaveFormat } from 'tinyweb-cells';
41
+
42
+ // Create a new workbook
43
+ const workbook = new Workbook();
44
+ const sheet = workbook.worksheets.get(0);
45
+
46
+ // Set cell values
47
+ sheet.cells.get('A1').putValue('Name');
48
+ sheet.cells.get('B1').putValue('Score');
49
+ sheet.cells.get('A2').putValue('Alice');
50
+ sheet.cells.get('B2').putValue(95);
51
+ sheet.cells.get('A3').putValue('Bob');
52
+ sheet.cells.get('B3').putValue(87);
53
+
54
+ // Add a formula
55
+ sheet.cells.get('B4').setFormula('=SUM(B2:B3)');
56
+
57
+ // Save to file
58
+ await workbook.save('output.xlsx');
59
+ ```
60
+
61
+ ### Read an Existing File
62
+
63
+ ```typescript
64
+ import { Workbook } from 'tinyweb-cells';
65
+
66
+ const workbook = new Workbook();
67
+ await workbook.loadFile('input.xlsx');
68
+
69
+ const sheet = workbook.worksheets.get(0);
70
+ console.log(sheet.cells.get('A1').value); // Read cell value
71
+ console.log(sheet.name); // Sheet name
72
+ ```
73
+
74
+ ### Apply Formatting
75
+
76
+ ```typescript
77
+ import { Workbook } from 'tinyweb-cells';
78
+
79
+ const workbook = new Workbook();
80
+ const sheet = workbook.worksheets.get(0);
81
+ const cell = sheet.cells.get('A1');
82
+ cell.putValue('Hello, Excel!');
83
+
84
+ // Get cell style and modify
85
+ const style = cell.getStyle();
86
+ style.font.name = 'Arial';
87
+ style.font.size = 14;
88
+ style.font.bold = true;
89
+ style.font.color = '#FF0000';
90
+ style.fill.patternType = 'solid';
91
+ style.fill.fgColor = '#FFFF00';
92
+ style.borders.top.lineStyle = 'thin';
93
+ style.borders.top.color = '#000000';
94
+ style.alignment.horizontal = 'center';
95
+ cell.setStyle(style);
96
+
97
+ await workbook.save('formatted.xlsx');
98
+ ```
99
+
100
+ ## API Reference
101
+
102
+ ### Workbook
103
+
104
+ The main entry point for creating and manipulating Excel files.
105
+
106
+ ```typescript
107
+ const workbook = new Workbook();
108
+
109
+ // Load from file
110
+ await workbook.loadFile('path/to/file.xlsx');
111
+
112
+ // Load from buffer
113
+ await workbook.loadBuffer(buffer);
114
+
115
+ // Save to file
116
+ await workbook.save('output.xlsx');
117
+ await workbook.save('output.xlsx', SaveFormat.XLSX);
118
+
119
+ // Save to buffer
120
+ const buffer = await workbook.saveToBuffer(SaveFormat.XLSX);
121
+
122
+ // Access worksheets
123
+ workbook.worksheets; // WorksheetCollection
124
+ workbook.worksheets.get(0); // By index
125
+ workbook.worksheets.get('Sheet1'); // By name
126
+ workbook.worksheets.add('NewSheet');
127
+
128
+ // Workbook protection
129
+ workbook.properties.protection.lockStructure = true;
130
+ workbook.properties.protection.password = 'secret';
131
+
132
+ // Document properties
133
+ workbook.documentProperties.title = 'My Report';
134
+ workbook.documentProperties.author = 'John Doe';
135
+ workbook.documentProperties.subject = 'Q4 Sales';
136
+ ```
137
+
138
+ ### Worksheet
139
+
140
+ Represents a single sheet in the workbook.
141
+
142
+ ```typescript
143
+ const sheet = workbook.worksheets.get(0);
144
+
145
+ // Properties
146
+ sheet.name = 'DataSheet';
147
+ sheet.isVisible = true;
148
+ sheet.tabColor = '#FF0000';
149
+
150
+ // Cells
151
+ sheet.cells.get('A1').putValue('Hello');
152
+
153
+ // Merge cells
154
+ sheet.cells.merge(0, 0, 2, 3); // startRow, startCol, rowCount, colCount
155
+
156
+ // Column width and row height
157
+ sheet.cells.setColumnWidth(0, 20); // Column A = 20
158
+ sheet.cells.setRowHeight(0, 30); // Row 1 = 30
159
+
160
+ // Freeze panes
161
+ sheet.freezePanes(1, 0); // Freeze first row
162
+
163
+ // Page setup
164
+ sheet.pageSetup.orientation = 'landscape';
165
+ sheet.pageSetup.paperSize = 'A4';
166
+
167
+ // Sheet protection
168
+ sheet.protect('password');
169
+ sheet.protection.sheet = true;
170
+ sheet.protection.formatCells = false;
171
+ ```
172
+
173
+ ### Cell / Cells
174
+
175
+ Access and manipulate individual cells.
176
+
177
+ ```typescript
178
+ const cells = sheet.cells;
179
+
180
+ // Get a cell (creates if not exists)
181
+ const cell = cells.get('A1');
182
+ // Also supports: cells.get(0, 0)
183
+
184
+ // Set values
185
+ cell.putValue('text'); // String
186
+ cell.putValue(42); // Number
187
+ cell.putValue(true); // Boolean
188
+ cell.putValue(new Date()); // Date
189
+
190
+ // Read values
191
+ cell.value; // Raw value
192
+ cell.stringValue; // String representation
193
+ cell.type; // CellDataType
194
+
195
+ // Formulas
196
+ cell.setFormula('=SUM(A1:A10)');
197
+ cell.formula; // Get formula string
198
+ cell.hasFormula(); // Check if cell has formula
199
+
200
+ // Styling
201
+ const style = cell.getStyle();
202
+ style.font.bold = true;
203
+ cell.setStyle(style);
204
+
205
+ // Comments
206
+ cell.comment = { text: 'Note', author: 'Admin' };
207
+ ```
208
+
209
+ ### Style (Font, Fill, Borders, Alignment, NumberFormat)
210
+
211
+ Full cell formatting support.
212
+
213
+ ```typescript
214
+ const style = cell.getStyle();
215
+
216
+ // Font
217
+ style.font.name = 'Calibri';
218
+ style.font.size = 12;
219
+ style.font.bold = true;
220
+ style.font.italic = true;
221
+ style.font.underline = 'single';
222
+ style.font.strikethrough = true;
223
+ style.font.color = '#0000FF';
224
+
225
+ // Fill
226
+ style.fill.patternType = 'solid';
227
+ style.fill.fgColor = '#FFFF00';
228
+ style.fill.bgColor = '#FFFFFF';
229
+
230
+ // Borders
231
+ style.borders.top.lineStyle = 'thin'; // thin, medium, thick, dashed, dotted, double
232
+ style.borders.top.color = '#000000';
233
+ style.borders.bottom.lineStyle = 'double';
234
+ style.borders.left.lineStyle = 'medium';
235
+ style.borders.right.lineStyle = 'thin';
236
+ style.borders.diagonal.lineStyle = 'thin';
237
+
238
+ // Alignment
239
+ style.alignment.horizontal = 'center'; // left, center, right, fill, justify
240
+ style.alignment.vertical = 'center'; // top, center, bottom
241
+ style.alignment.wrapText = true;
242
+ style.alignment.textRotation = 45;
243
+ style.alignment.indent = 2;
244
+ style.alignment.shrinkToFit = true;
245
+
246
+ // Number format
247
+ style.numberFormat.formatCode = '#,##0.00';
248
+ style.numberFormat.formatCode = '0.00%';
249
+ style.numberFormat.formatCode = 'yyyy-mm-dd';
250
+ style.numberFormat.formatCode = '$#,##0.00';
251
+
252
+ // Protection
253
+ style.protection.locked = true;
254
+ style.protection.hidden = true;
255
+
256
+ cell.setStyle(style);
257
+ ```
258
+
259
+ ### FormulaEvaluator
260
+
261
+ Evaluate formulas in cells.
262
+
263
+ ```typescript
264
+ import { FormulaEvaluator } from 'tinyweb-cells';
265
+
266
+ // Set up formulas
267
+ sheet.cells.get('A1').putValue(10);
268
+ sheet.cells.get('A2').putValue(20);
269
+ sheet.cells.get('A3').setFormula('=SUM(A1:A2)');
270
+
271
+ // Evaluate all formulas
272
+ const evaluator = new FormulaEvaluator(workbook);
273
+ evaluator.evaluateAll();
274
+
275
+ console.log(sheet.cells.get('A3').value); // 30
276
+ ```
277
+
278
+ ### Data Validation
279
+
280
+ Add data validation rules to cells.
281
+
282
+ ```typescript
283
+ import { DataValidation, DataValidationType, DataValidationOperator } from 'tinyweb-cells';
284
+
285
+ const validations = sheet.dataValidations;
286
+
287
+ // Dropdown list
288
+ const dv = validations.add();
289
+ dv.sqref = 'A1:A10';
290
+ dv.type = DataValidationType.LIST;
291
+ dv.formula1 = '"Apple,Banana,Cherry"';
292
+
293
+ // Numeric range
294
+ const numDv = validations.add();
295
+ numDv.sqref = 'B1:B10';
296
+ numDv.type = DataValidationType.WHOLE;
297
+ numDv.operator = DataValidationOperator.BETWEEN;
298
+ numDv.formula1 = '1';
299
+ numDv.formula2 = '100';
300
+ numDv.showErrorMessage = true;
301
+ numDv.errorTitle = 'Invalid Input';
302
+ numDv.error = 'Please enter a number between 1 and 100';
303
+ ```
304
+
305
+ ### Conditional Formatting
306
+
307
+ Apply conditional formatting rules.
308
+
309
+ ```typescript
310
+ const cfCollection = sheet.conditionalFormattings;
311
+
312
+ // Cell value rule
313
+ const cf = cfCollection.add();
314
+ cf.sqref = 'A1:A20';
315
+ cf.type = 'cellIs';
316
+ cf.operator = 'greaterThan';
317
+ cf.formula1 = '90';
318
+ cf.priority = 1;
319
+ cf.dxf = {
320
+ font: { color: '#006100' },
321
+ fill: { bgColor: '#C6EFCE' },
322
+ };
323
+ ```
324
+
325
+ ### Hyperlinks
326
+
327
+ Add hyperlinks to cells.
328
+
329
+ ```typescript
330
+ const hyperlinks = sheet.hyperlinks;
331
+
332
+ // External URL
333
+ hyperlinks.add('A1', 1, 1, 'https://example.com');
334
+ const link = hyperlinks.get(0);
335
+ link.textToDisplay = 'Visit Example';
336
+ link.screenTip = 'Click to open';
337
+
338
+ // Internal reference
339
+ hyperlinks.add('A2', 1, 1, '#Sheet2!A1');
340
+ ```
341
+
342
+ ### Named Ranges
343
+
344
+ Define and use named ranges.
345
+
346
+ ```typescript
347
+ // Add a defined name
348
+ workbook.definedNames.add('SalesData', 'Sheet1!$A$1:$D$100');
349
+ workbook.definedNames.add('TaxRate', '0.08');
350
+
351
+ // Use in formulas
352
+ sheet.cells.get('E1').setFormula('=SUM(SalesData)');
353
+ ```
354
+
355
+ ### AutoFilter
356
+
357
+ Apply auto-filtering to data ranges.
358
+
359
+ ```typescript
360
+ const autoFilter = sheet.autoFilter;
361
+ autoFilter.range = 'A1:D100';
362
+
363
+ // Add a filter column
364
+ const fc = autoFilter.addFilterColumn(0);
365
+ fc.setValues(['Active', 'Pending']);
366
+ ```
367
+
368
+ ### Comments
369
+
370
+ Add comments to cells.
371
+
372
+ ```typescript
373
+ const cell = sheet.cells.get('A1');
374
+ cell.comment = {
375
+ text: 'This is a comment',
376
+ author: 'Admin',
377
+ width: 200,
378
+ height: 100,
379
+ };
380
+ ```
381
+
382
+ ### Page Setup
383
+
384
+ Configure print settings.
385
+
386
+ ```typescript
387
+ sheet.pageSetup.orientation = 'landscape';
388
+ sheet.pageSetup.paperSize = 'A4';
389
+ sheet.pageSetup.printArea = 'A1:H50';
390
+ sheet.pageSetup.fitToPagesTall = 1;
391
+ sheet.pageSetup.fitToPagesWide = 1;
392
+ ```
393
+
394
+ ## Formula Support
395
+
396
+ The built-in formula evaluator supports **43 Excel functions** across 7 categories:
397
+
398
+ ### Math & Statistics (16 functions)
399
+ `SUM` · `AVERAGE` · `MIN` · `MAX` · `COUNT` · `COUNTA` · `ABS` · `ROUND` · `ROUNDUP` · `ROUNDDOWN` · `INT` · `MOD` · `POWER` · `SQRT` · `CEILING` · `FLOOR`
400
+
401
+ ### String Functions (11 functions)
402
+ `CONCATENATE` · `CONCAT` · `TEXT` · `LEN` · `TRIM` · `UPPER` · `LOWER` · `LEFT` · `RIGHT` · `MID` · `SUBSTITUTE` · `REPT`
403
+
404
+ ### Logic Functions (4 functions)
405
+ `IF` · `AND` · `OR` · `NOT`
406
+
407
+ ### Lookup & Reference (4 functions)
408
+ `VLOOKUP` · `HLOOKUP` · `INDEX` · `MATCH`
409
+
410
+ ### Date & Time (6 functions)
411
+ `TODAY` · `NOW` · `DATE` · `YEAR` · `MONTH` · `DAY`
412
+
413
+ ### Information Functions (5 functions)
414
+ `ISNUMBER` · `ISTEXT` · `ISBLANK` · `ISERROR` · `ISNA`
415
+
416
+ ### Other Functions (2 functions)
417
+ `VALUE` · `CHOOSE`
418
+
419
+ ### Also Supports
420
+ - Arithmetic operators: `+` `-` `*` `/` `^`
421
+ - Comparison operators: `=` `<>` `<` `>` `<=` `>=`
422
+ - String concatenation: `&`
423
+ - Cell references: `A1`, `$A$1`, `Sheet1!A1`
424
+ - Range references: `A1:B5`
425
+ - Defined names / named ranges
426
+ - Nested formulas with recursive evaluation
427
+ - Error values: `#VALUE!`, `#REF!`, `#NAME?`, `#DIV/0!`, `#N/A`
428
+
429
+ ## Examples
430
+
431
+ ### Create and Save a Workbook
432
+
433
+ ```typescript
434
+ import { Workbook } from 'tinyweb-cells';
435
+
436
+ const wb = new Workbook();
437
+ const ws = wb.worksheets.get(0);
438
+ ws.name = 'Sales Report';
439
+
440
+ // Headers
441
+ ['Product', 'Q1', 'Q2', 'Q3', 'Q4', 'Total'].forEach((h, i) => {
442
+ const cell = ws.cells.get(0, i);
443
+ cell.putValue(h);
444
+ const style = cell.getStyle();
445
+ style.font.bold = true;
446
+ style.fill.patternType = 'solid';
447
+ style.fill.fgColor = '#4472C4';
448
+ style.font.color = '#FFFFFF';
449
+ cell.setStyle(style);
450
+ });
451
+
452
+ // Data
453
+ const data = [
454
+ ['Widget A', 1200, 1350, 1100, 1500],
455
+ ['Widget B', 800, 920, 870, 1050],
456
+ ];
457
+
458
+ data.forEach((row, r) => {
459
+ row.forEach((val, c) => {
460
+ ws.cells.get(r + 1, c).putValue(val);
461
+ });
462
+ // Total formula
463
+ ws.cells.get(r + 1, 5).setFormula(`=SUM(B${r + 2}:E${r + 2})`);
464
+ });
465
+
466
+ await wb.save('sales-report.xlsx');
467
+ ```
468
+
469
+ ### Read and Modify an Existing File
470
+
471
+ ```typescript
472
+ import { Workbook } from 'tinyweb-cells';
473
+
474
+ const wb = new Workbook();
475
+ await wb.loadFile('existing.xlsx');
476
+
477
+ const ws = wb.worksheets.get(0);
478
+
479
+ // Read all data
480
+ for (let r = 0; r < 100; r++) {
481
+ for (let c = 0; c < 10; c++) {
482
+ const cell = ws.cells.get(r, c);
483
+ if (cell.value !== null) {
484
+ console.log(`${cell.name}: ${cell.value}`);
485
+ }
486
+ }
487
+ }
488
+
489
+ // Modify a cell
490
+ ws.cells.get('A1').putValue('Updated!');
491
+ await wb.save('modified.xlsx');
492
+ ```
493
+
494
+ ### Data Validation with Dropdown
495
+
496
+ ```typescript
497
+ import { Workbook, DataValidationType } from 'tinyweb-cells';
498
+
499
+ const wb = new Workbook();
500
+ const ws = wb.worksheets.get(0);
501
+
502
+ ws.cells.get('A1').putValue('Select a fruit:');
503
+
504
+ const dv = ws.dataValidations.add();
505
+ dv.sqref = 'B1';
506
+ dv.type = DataValidationType.LIST;
507
+ dv.formula1 = '"Apple,Banana,Cherry,Date,Elderberry"';
508
+ dv.showDropDown = true;
509
+ dv.showInputMessage = true;
510
+ dv.inputTitle = 'Fruit Selection';
511
+ dv.inputMessage = 'Choose a fruit from the list';
512
+
513
+ await wb.save('dropdown.xlsx');
514
+ ```
515
+
516
+ ### Conditional Formatting
517
+
518
+ ```typescript
519
+ import { Workbook } from 'tinyweb-cells';
520
+
521
+ const wb = new Workbook();
522
+ const ws = wb.worksheets.get(0);
523
+
524
+ // Add some scores
525
+ for (let i = 0; i < 20; i++) {
526
+ ws.cells.get(i, 0).putValue(Math.floor(Math.random() * 100));
527
+ }
528
+
529
+ // Highlight scores above 80 in green
530
+ const cf = ws.conditionalFormattings.add();
531
+ cf.sqref = 'A1:A20';
532
+ cf.type = 'cellIs';
533
+ cf.operator = 'greaterThanOrEqual';
534
+ cf.formula1 = '80';
535
+ cf.dxf = {
536
+ fill: { bgColor: '#C6EFCE' },
537
+ font: { color: '#006100' },
538
+ };
539
+
540
+ await wb.save('conditional.xlsx');
541
+ ```
542
+
543
+ ## API Compatibility
544
+
545
+ This library provides **dual naming conventions** for maximum compatibility:
546
+
547
+ | camelCase (TypeScript) | snake_case (Python-compat) |
548
+ |---|---|
549
+ | `cell.putValue(v)` | `cell.put_value(v)` |
550
+ | `cell.getStyle()` | `cell.get_style()` |
551
+ | `cell.setStyle(s)` | `cell.set_style(s)` |
552
+ | `cell.hasFormula()` | `cell.has_formula()` |
553
+ | `cell.setFormula(f)` | `cell.set_formula(f)` |
554
+ | `cell.stringValue` | `cell.string_value` |
555
+ | `cells.setColumnWidth()` | `cells.set_column_width()` |
556
+ | `cells.setRowHeight()` | `cells.set_row_height()` |
557
+ | `workbook.loadFile()` | `workbook.load_file()` |
558
+ | `workbook.saveToBuffer()` | `workbook.save_to_buffer()` |
559
+
560
+ Both naming styles work identically — use whichever fits your codebase.
561
+
562
+ ## TypeScript
563
+
564
+ Full TypeScript declarations are included. All classes, interfaces, enums, and types are exported:
565
+
566
+ ```typescript
567
+ import {
568
+ Workbook,
569
+ Worksheet,
570
+ Cell,
571
+ Cells,
572
+ Style,
573
+ Font,
574
+ Fill,
575
+ Border,
576
+ Borders,
577
+ Alignment,
578
+ NumberFormat,
579
+ Protection,
580
+ SaveFormat,
581
+ FormulaEvaluator,
582
+ DataValidation,
583
+ DataValidationCollection,
584
+ DataValidationType,
585
+ DataValidationOperator,
586
+ ConditionalFormat,
587
+ ConditionalFormatCollection,
588
+ Hyperlink,
589
+ HyperlinkCollection,
590
+ AutoFilter,
591
+ FilterColumn,
592
+ DefinedName,
593
+ DefinedNameCollection,
594
+ HorizontalPageBreakCollection,
595
+ VerticalPageBreakCollection,
596
+ SheetProtection,
597
+ PageSetup,
598
+ FreezePane,
599
+ WorkbookProtection,
600
+ DocumentProperties,
601
+ } from 'tinyweb-cells';
602
+ ```
603
+
604
+ ## Dependencies
605
+
606
+ | Package | Purpose |
607
+ |---|---|
608
+ | [jszip](https://www.npmjs.com/package/jszip) | XLSX ZIP container read/write |
609
+ | [fast-xml-parser](https://www.npmjs.com/package/fast-xml-parser) | XML parsing and generation |
610
+
611
+ No native/binary dependencies — works on any platform where Node.js runs.
612
+
613
+ ## License
614
+
615
+ [MIT](./LICENSE)