@justybase/spreadsheet-tasks 2.0.0 → 2.1.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/docs/API.md CHANGED
@@ -6,6 +6,8 @@
6
6
  - [XlsxWriter](#xlsxwriter)
7
7
  - [XlsbReader](#xlsbreader)
8
8
  - [XlsxReader](#xlsxreader)
9
+ - [XlsxUpdater](#xlsxupdater)
10
+ - [XlsbUpdater](#xlsbupdater)
9
11
  - [ReaderFactory](#readerfactory)
10
12
  - [Types](#types)
11
13
 
@@ -224,6 +226,162 @@ Gets value at the specified column index.
224
226
 
225
227
  ---
226
228
 
229
+ ## XlsxUpdater
230
+
231
+ Updates the data of a worksheet inside an existing `.xlsx` file without rebuilding
232
+ the workbook. Everything outside the target sheet's cell data — pivot tables and
233
+ their caches, other sheets, styles, themes, defined names — is preserved, which
234
+ makes it possible to refresh a data sheet that pivot tables / charts / formulas
235
+ are wired to.
236
+
237
+ ### Constructor
238
+
239
+ ```typescript
240
+ new XlsxUpdater(filePath: string)
241
+ ```
242
+
243
+ **Parameters:**
244
+ - `filePath` - Path to an existing `.xlsx` file
245
+
246
+ Throws if the file does not exist, is not a valid XLSX, or is an XLSB
247
+ (XLSB updates are not supported yet).
248
+
249
+ **Example:**
250
+ ```typescript
251
+ const updater = new XlsxUpdater('report.xlsx');
252
+ ```
253
+
254
+ ### Methods
255
+
256
+ #### `getSheetNames(): string[]`
257
+
258
+ Returns the worksheet names in workbook order.
259
+
260
+ #### `replaceSheetData(sheetName: string, rows: CellValue[][], options?: ReplaceSheetDataOptions): void`
261
+
262
+ Clears the entire cell data of the target sheet and writes new rows in its place.
263
+
264
+ **Parameters:**
265
+ - `sheetName` - Name of the worksheet to replace. Throws if not found.
266
+ - `rows` - 2D array of new data (`null` / `undefined` cells stay empty).
267
+ Trailing rows that contain no non-empty cells are trimmed so the written
268
+ range matches the actual data (pivot tables won't pick up "(blank)" items
269
+ from padding rows).
270
+ - `options` - Optional object:
271
+ - `headers?: string[]` - Optional header row written into row 1
272
+ - `styleFallback?: 'inherit' | 'general'` - Style strategy for new cells
273
+ (default `'inherit'`)
274
+
275
+ **Behavior:**
276
+ - The sheet's `<dimension>` and `<autoFilter>` ranges are recalculated and end
277
+ exactly at the last data row.
278
+ - When `headers` are omitted, the first data row lands in row 1 and becomes the
279
+ pivot's field names — always pass `headers` matching the original columns.
280
+ - New strings are appended to `xl/sharedStrings.xml` (existing entries and their
281
+ indices are never modified); files without a shared string table use inline strings.
282
+ - With `styleFallback: 'inherit'` (default), new cells reuse the dominant style
283
+ of their column from the existing data, and `Date` cells fall back to a date
284
+ style found in `styles.xml` when the column has none.
285
+ - Pivot tables whose `worksheetSource` references the updated sheet get their
286
+ `ref` and `recordCount` adjusted, and `refreshOnLoad="1"` is added to the
287
+ pivot table definitions so Excel refreshes them on open.
288
+
289
+ **Example:**
290
+ ```typescript
291
+ const updater = new XlsxUpdater('report.xlsx');
292
+ updater.replaceSheetData('data1', rows, { headers: ['ID', 'NAME'] });
293
+ updater.save('report_new.xlsx');
294
+ ```
295
+
296
+ #### `save(outputPath?: string): void`
297
+
298
+ Writes the updated workbook to disk. When `outputPath` is omitted, the source
299
+ file is overwritten in place.
300
+
301
+ #### `toBuffer(): Buffer`
302
+
303
+ Returns the updated workbook as an in-memory ZIP buffer.
304
+
305
+ ---
306
+
307
+ ## XlsbUpdater
308
+
309
+ Updates the data of a worksheet inside an existing `.xlsb` file without
310
+ rebuilding the workbook. Everything outside the target sheet's rows — pivot
311
+ tables and their caches, other sheets, styles, column widths, views — is
312
+ preserved byte-for-byte.
313
+
314
+ The API is identical to [XlsxUpdater](#xlsxupdater); see that section for the
315
+ behavior of `replaceSheetData` (inherited column styles, shared-strings
316
+ appending, pivot cache range + `refreshOnLoad`).
317
+
318
+ ### Constructor
319
+
320
+ ```typescript
321
+ new XlsbUpdater(filePath: string)
322
+ ```
323
+
324
+ **Parameters:**
325
+ - `filePath` - Path to an existing `.xlsb` file
326
+
327
+ Throws if the file does not exist, is not a valid XLSB, or is an XLSX
328
+ (XLSB-only updater).
329
+
330
+ ### Methods
331
+
332
+ #### `getSheetNames(): string[]`
333
+
334
+ Returns the worksheet names in workbook order.
335
+
336
+ #### `replaceSheetData(sheetName: string, rows: CellValue[][], options?: ReplaceSheetDataOptions): void`
337
+
338
+ Clears the entire cell data of the target sheet and writes new rows in its place.
339
+
340
+ **Parameters:**
341
+ - `sheetName` - Name of the worksheet to replace. Throws if not found.
342
+ - `rows` - 2D array of new data (`null` / `undefined` cells stay empty).
343
+ Trailing rows that contain no non-empty cells are trimmed so the written
344
+ range matches the actual data (pivot tables won't pick up "(blank)" items
345
+ from padding rows).
346
+ - `options` - Optional object:
347
+ - `headers?: string[]` - Optional header row written into row 1
348
+ - `styleFallback?: 'inherit' | 'general'` - Style strategy for new cells
349
+ (default `'inherit'`)
350
+
351
+ **Behavior:**
352
+ - Only the worksheet's row records are replaced; column widths, views, and all
353
+ other sheet records are preserved. Auto-filter ranges are recalculated.
354
+ - The sheet's dimension record and the pivot cache source range end exactly at
355
+ the last data row.
356
+ - When `headers` are omitted, the first data row lands in row 1 and becomes the
357
+ pivot's field names — always pass `headers` matching the original columns.
358
+ - New strings are appended to `xl/sharedStrings.bin` (existing entries and their
359
+ indices are never modified).
360
+ - With `styleFallback: 'inherit'` (default), new cells reuse the dominant cell
361
+ style (xf) of their column from the existing data, and `Date` cells fall back
362
+ to a date style found in `styles.bin` when the column has none.
363
+ - Pivot caches whose `worksheetSource` references the updated sheet get their
364
+ range and record count adjusted, and the `refreshOnLoad` flag is set so Excel
365
+ refreshes them on open.
366
+
367
+ **Example:**
368
+ ```typescript
369
+ const updater = new XlsbUpdater('report.xlsb');
370
+ updater.replaceSheetData('data1', rows, { headers: ['ID', 'NAME'] });
371
+ updater.save('report_new.xlsb');
372
+ ```
373
+
374
+ #### `save(outputPath?: string): void`
375
+
376
+ Writes the updated workbook to disk. When `outputPath` is omitted, the source
377
+ file is overwritten in place.
378
+
379
+ #### `toBuffer(): Buffer`
380
+
381
+ Returns the updated workbook as an in-memory ZIP buffer.
382
+
383
+ ---
384
+
227
385
  ## ReaderFactory
228
386
 
229
387
  Factory class for creating appropriate reader based on file extension.
@@ -7,7 +7,7 @@
7
7
  * Run with: npx ts-node examples/basic-read.ts
8
8
  */
9
9
 
10
- import { XlsbReader, XlsxReader, ReaderFactory } from '../dist';
10
+ import { XlsbReader, XlsxReader, ReaderFactory } from '../dist/cjs';
11
11
  import * as path from 'path';
12
12
 
13
13
  async function readXlsbExample() {
@@ -7,7 +7,7 @@
7
7
  * Run with: npx ts-node examples/basic-write.ts
8
8
  */
9
9
 
10
- import { XlsbWriter, XlsxWriter } from '../dist';
10
+ import { XlsbWriter, XlsxWriter } from '../dist/cjs';
11
11
  import * as path from 'path';
12
12
  import * as fs from 'fs';
13
13
 
@@ -8,7 +8,7 @@
8
8
  * Run with: npx ts-node examples/datetime-widths.ts
9
9
  */
10
10
 
11
- import { XlsbWriter, XlsxWriter } from '../dist';
11
+ import { XlsbWriter, XlsxWriter } from '../dist/cjs';
12
12
  import * as path from 'path';
13
13
  import * as fs from 'fs';
14
14
 
@@ -7,7 +7,7 @@
7
7
  * Run with: npx ts-node examples/large-dataset.ts
8
8
  */
9
9
 
10
- import { XlsbWriter, XlsxWriter, XlsbReader, XlsxReader } from '../dist';
10
+ import { XlsbWriter, XlsxWriter, XlsbReader, XlsxReader } from '../dist/cjs';
11
11
  import * as path from 'path';
12
12
  import * as fs from 'fs';
13
13
 
@@ -7,7 +7,7 @@
7
7
  * Run with: npx ts-node examples/multiple-sheets.ts
8
8
  */
9
9
 
10
- import { XlsbWriter, XlsxWriter } from '../dist';
10
+ import { XlsbWriter, XlsxWriter } from '../dist/cjs';
11
11
  import * as path from 'path';
12
12
  import * as fs from 'fs';
13
13
 
@@ -7,13 +7,15 @@
7
7
  * Run with: npx ts-node examples/streaming-example.ts
8
8
  */
9
9
 
10
- import { XlsbWriter } from '../dist/XlsbWriter';
10
+ import { XlsbWriter } from '../dist/cjs';
11
11
  import * as path from 'path';
12
+ import * as fs from 'fs';
12
13
 
13
14
  async function streamingExample() {
14
15
  console.log('=== XlsbWriter Streaming Example ===\n');
15
16
 
16
17
  const outputPath = path.join(__dirname, '..', 'output', 'streaming-example.xlsb');
18
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
17
19
  console.log(`Output file: ${outputPath}\n`);
18
20
 
19
21
  const writer = new XlsbWriter(outputPath);
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Update Existing Workbook Example
3
+ *
4
+ * This example demonstrates how to replace the data of a worksheet inside an
5
+ * existing Excel file (e.g. a report whose pivot tables are wired to a "data1"
6
+ * sheet) without rebuilding the workbook. The same API works for XLSX and XLSB.
7
+ *
8
+ * Run with: npx ts-node examples/update-existing.ts
9
+ */
10
+
11
+ import { XlsxUpdater, XlsbUpdater } from '../dist/cjs';
12
+ import * as path from 'path';
13
+ import * as fs from 'fs';
14
+
15
+ const outputDir = path.join(__dirname, 'output');
16
+ if (!fs.existsSync(outputDir)) {
17
+ fs.mkdirSync(outputDir, { recursive: true });
18
+ }
19
+
20
+ async function updateExistingWorkbook() {
21
+ // New data for the "data1" sheet (same columns as before)
22
+ const rows = [
23
+ [new Date('2026-01-01'), 'PL', 'Warsaw', 1200.5, true],
24
+ [new Date('2026-01-02'), 'DE', 'Berlin', 980.25, true],
25
+ [new Date('2026-01-03'), 'PL', 'Krakow', 1530.0, false],
26
+ ];
27
+ const headers = ['DATEKEY', 'COUNTRY', 'CITY', 'AMOUNT', 'ACTIVE'];
28
+
29
+ for (const ext of ['xlsx', 'xlsb']) {
30
+ const input = path.join(__dirname, '..', `report.${ext}`);
31
+ const output = path.join(outputDir, `report_updated.${ext}`);
32
+
33
+ if (!fs.existsSync(input)) {
34
+ console.log(`Template workbook not found: ${input} (skipping)`);
35
+ continue;
36
+ }
37
+
38
+ // Open the existing workbook (everything outside the replaced sheet
39
+ // — pivot tables, other sheets, styles — is preserved)
40
+ const updater = ext === 'xlsb' ? new XlsbUpdater(input) : new XlsxUpdater(input);
41
+ console.log(`[${ext}] Sheets:`, updater.getSheetNames());
42
+
43
+ updater.replaceSheetData('data1', rows, { headers });
44
+
45
+ // Write the updated workbook to a new file (or omit the path to
46
+ // overwrite the source in place)
47
+ updater.save(output);
48
+ console.log(`[${ext}] Workbook updated: ${output}`);
49
+ }
50
+ }
51
+
52
+ updateExistingWorkbook();
53
+
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@justybase/spreadsheet-tasks",
3
- "version": "2.0.0",
4
- "description": "High-performance TypeScript library for reading and writing Excel files in XLSB and XLSX formats. XLSB is 3.3x faster to write and 2.1x faster to read than XLSX.",
3
+ "version": "2.1.0",
4
+ "description": "High-performance TypeScript library for reading and writing Excel files in XLSB and XLSX formats. XLSB is 3.3x faster to write and 2.3x faster to read than XLSX.",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",
7
7
  "types": "./dist/cjs/index.d.ts",
@@ -30,9 +30,9 @@
30
30
  "test": "npm run typecheck && npm run test:smoke && npm run test:unit && npm run test:integration && npm run test:dist",
31
31
  "test:smoke": "npx ts-node tests/smoke.test.ts",
32
32
  "test:unit": "npx ts-node tests/unit/bigbuffer.test.ts && npx ts-node tests/unit/biff-reader-writer.test.ts && npx ts-node tests/unit/xlsb-writer.test.ts",
33
- "test:integration": "npx ts-node tests/integration/xlsb-writer.test.ts && npx ts-node tests/integration/xlsx-writer.test.ts",
33
+ "test:integration": "npx ts-node tests/integration/xlsb-writer.test.ts && npx ts-node tests/integration/xlsx-writer.test.ts && npx ts-node tests/integration/xlsx-updater.test.ts && npx ts-node tests/integration/xlsb-updater.test.ts",
34
34
  "test:dist": "npm run build && npx ts-node tests/dist-surface.test.ts",
35
- "test:xlsb": "npx ts-node tests/integration/xlsb-writer.test.ts",
35
+ "test:xlsb": "npx ts-node tests/integration/xlsb-writer.test.ts && npx ts-node tests/integration/xlsb-updater.test.ts",
36
36
  "test:xlsx": "npx ts-node tests/integration/xlsx-writer.test.ts",
37
37
  "benchmark": "npm run build && npx ts-node benchmarks/performance.bench.ts",
38
38
  "benchmark:comparison": "npm run build && npx ts-node benchmarks/comparison.bench.ts",