@justybase/spreadsheet-tasks 3.1.1 → 3.2.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.
@@ -9,7 +9,7 @@ declare class BigBuffer {
9
9
  ensureCapacity(size: number): void;
10
10
  private _ensureCapacity;
11
11
  private _flush;
12
- write(buffer: Buffer$1): void;
12
+ write(buffer: Uint8Array): void;
13
13
  writeByte(val: number): void;
14
14
  writeUnsafeByte(val: number): void;
15
15
  writeInt32LE(val: number): void;
@@ -19,15 +19,117 @@ declare class BigBuffer {
19
19
  writeString(str: string): void;
20
20
  writeUtf16LE(str: string): void;
21
21
  getChunks(): Buffer$1[];
22
+ /** Overwrite a previously written 32-bit integer at an absolute offset. */
23
+ patchInt32LE(offset: number, value: number): boolean;
22
24
  reset(): void;
23
25
  }
24
26
 
25
- type PrimitiveCellValue = string | number | boolean | Date | bigint | null | undefined;
27
+ /**
28
+ * An Excel error value (`#DIV/0!`, `#N/A`, ...). Strings are always text, so
29
+ * writing an error requires this marker; readers return the display text
30
+ * (`"#DIV/0!"`) for compatibility with plain value consumers.
31
+ */
32
+ declare class ExcelError {
33
+ readonly code: string;
34
+ constructor(code: string);
35
+ toString(): string;
36
+ }
37
+ type PrimitiveCellValue = string | number | boolean | Date | bigint | ExcelError | null | undefined;
38
+ /** RGB/ARGB hex (`'FF0000'`, `'#FF0000'`, `'FFFF0000'`) or an indexed/theme color. */
39
+ type StyleColor = string | {
40
+ indexed: number;
41
+ } | {
42
+ theme: number;
43
+ tint?: number;
44
+ };
45
+ type FontUnderline = 'single' | 'double' | 'singleAccounting' | 'doubleAccounting';
46
+ interface FontStyle {
47
+ name?: string;
48
+ size?: number;
49
+ bold?: boolean;
50
+ italic?: boolean;
51
+ underline?: FontUnderline | boolean;
52
+ strike?: boolean;
53
+ color?: StyleColor;
54
+ family?: number;
55
+ scheme?: 'major' | 'minor';
56
+ vertAlign?: 'baseline' | 'superscript' | 'subscript';
57
+ outline?: boolean;
58
+ shadow?: boolean;
59
+ condense?: boolean;
60
+ extend?: boolean;
61
+ charset?: number;
62
+ }
63
+ type PatternType = 'none' | 'solid' | 'mediumGray' | 'darkGray' | 'lightGray' | 'darkHorizontal' | 'darkVertical' | 'darkDown' | 'darkUp' | 'darkGrid' | 'darkTrellis' | 'lightHorizontal' | 'lightVertical' | 'lightDown' | 'lightUp' | 'lightGrid' | 'lightTrellis' | 'gray125' | 'gray0625';
64
+ interface FillStyle {
65
+ patternType?: PatternType;
66
+ fgColor?: StyleColor;
67
+ bgColor?: StyleColor;
68
+ }
69
+ type BorderLineStyle = 'thin' | 'medium' | 'dashed' | 'dotted' | 'thick' | 'double' | 'hair' | 'mediumDashed' | 'dashDot' | 'mediumDashDot' | 'dashDotDot' | 'mediumDashDotDot' | 'slantDashDot';
70
+ interface BorderSide {
71
+ style?: BorderLineStyle;
72
+ color?: StyleColor;
73
+ }
74
+ interface BorderStyle {
75
+ top?: BorderSide;
76
+ bottom?: BorderSide;
77
+ left?: BorderSide;
78
+ right?: BorderSide;
79
+ diagonal?: BorderSide;
80
+ diagonalUp?: boolean;
81
+ diagonalDown?: boolean;
82
+ outline?: boolean;
83
+ }
84
+ interface AlignmentStyle {
85
+ horizontal?: 'general' | 'left' | 'center' | 'right' | 'fill' | 'justify' | 'centerContinuous' | 'distributed';
86
+ vertical?: 'top' | 'center' | 'bottom' | 'justify' | 'distributed';
87
+ wrapText?: boolean;
88
+ shrinkToFit?: boolean;
89
+ indent?: number;
90
+ textRotation?: number;
91
+ justifyLastLine?: boolean;
92
+ readingOrder?: 0 | 1 | 2;
93
+ }
94
+ interface ProtectionStyle {
95
+ locked?: boolean;
96
+ hidden?: boolean;
97
+ }
98
+ /** Full cell style. Only the properties that are set are applied. */
99
+ interface CellStyle {
100
+ numFmt?: string;
101
+ font?: FontStyle;
102
+ fill?: FillStyle;
103
+ border?: BorderStyle;
104
+ alignment?: AlignmentStyle;
105
+ protection?: ProtectionStyle;
106
+ }
107
+ interface StyledCell {
108
+ value: PrimitiveCellValue | RichText;
109
+ style: CellStyle;
110
+ }
111
+ /** A formula and an optional cached value. The formula text may start with `=`. */
112
+ interface FormulaCell {
113
+ formula: string;
114
+ value?: PrimitiveCellValue;
115
+ style?: CellStyle;
116
+ }
117
+ interface RichTextRun {
118
+ text: string;
119
+ /** Run formatting; only font properties are used. */
120
+ style?: CellStyle;
121
+ }
122
+ /** Rich text split into formatting runs. */
123
+ declare class RichText {
124
+ readonly runs: RichTextRun[];
125
+ constructor(runs: RichTextRun[]);
126
+ toString(): string;
127
+ }
26
128
  interface FormattedCell {
27
129
  value: PrimitiveCellValue;
28
130
  format: string;
29
131
  }
30
- type CellValue = PrimitiveCellValue | FormattedCell;
132
+ type CellValue = PrimitiveCellValue | RichText | FormattedCell | StyledCell | FormulaCell;
31
133
  /** Built-in Excel number-format strings for {@link FormattedCell}. */
32
134
  declare const F: {
33
135
  THOUSANDS_SEP: string;
@@ -56,7 +158,18 @@ declare const F: {
56
158
  TIME_MS: string;
57
159
  };
58
160
  declare function isFormattedCell(val: unknown): val is FormattedCell;
59
- declare function unwrapCell(val: CellValue): PrimitiveCellValue;
161
+ declare function isRichText(val: unknown): val is RichText;
162
+ declare function isStyledCell(val: unknown): val is StyledCell;
163
+ declare function isFormulaCell(val: unknown): val is FormulaCell;
164
+ /**
165
+ * Style carried by a cell. `FormattedCell` maps to `{ numFmt }`; every other
166
+ * value has no style.
167
+ */
168
+ declare function getCellStyle(val: CellValue): CellStyle | null;
169
+ /** Style with the legacy `FormattedCell.format` merged into `numFmt`. */
170
+ declare function mergeCellStyle(base: CellStyle | null | undefined, extra: CellStyle | null | undefined): CellStyle | null;
171
+ declare function getFormula(val: CellValue): string | null;
172
+ declare function unwrapCell(val: CellValue): PrimitiveCellValue | RichText;
60
173
  declare function getFormat(val: CellValue): string | null;
61
174
 
62
175
  /**
@@ -145,59 +258,136 @@ declare class XlsbReader extends ExcelReaderAbstract {
145
258
  private sheetNames;
146
259
  private sheets;
147
260
  private _currentSheetIndex;
148
- private _reader;
261
+ private _sheetBuffer;
262
+ private _pos;
149
263
  private _currentRow;
264
+ private _currentStyles;
150
265
  private _pendingRowIndex;
151
266
  private _nextExpectedRow;
152
267
  private _eof;
268
+ private readonly _rkScratch;
153
269
  xfIdToNumFmtId: number[];
154
270
  customDateFormats: Set<number> | null;
271
+ /** One flag per xf id: 1 when its number format is a date. */
272
+ private _dateXf;
273
+ private _xfStyles;
155
274
  constructor();
156
275
  open(path: string, readSharedStrings?: boolean): Promise<void>;
157
- private _readSharedStrings;
158
- private _readStyles;
276
+ /** Open a workbook already held in memory (ZIP bytes). */
277
+ openBuffer(buffer: Buffer, readSharedStrings?: boolean): Promise<void>;
278
+ private _resetState;
279
+ private _load;
280
+ private _buildDateXf;
281
+ private _isDateStyle;
159
282
  getSheetNames(): string[];
160
283
  selectSheet(nameOrIndex: string | number): Promise<void>;
161
284
  nextResult(): Promise<boolean>;
162
285
  read(): Promise<boolean>;
286
+ /**
287
+ * Synchronous row access for batch consumers. Returns the internal row
288
+ * array, valid until the next call; `readAll`/`rows` return copies.
289
+ * Select a sheet (or call `read()` once) before using it.
290
+ */
291
+ readRow(): CellValue[] | null;
292
+ /** Collect every remaining row of the current sheet. */
293
+ readAll(): Promise<CellValue[][]>;
294
+ /** Stream every remaining row of the current sheet. */
295
+ rows(): AsyncGenerator<CellValue[]>;
296
+ private _readNext;
297
+ private _finishEof;
298
+ private _numberValue;
299
+ private _doubleValue;
300
+ private _asDate;
163
301
  private _initSheet;
302
+ /** Style of the current row cell at column `i`, or null for the default style. */
303
+ getStyle(i: number): CellStyle | null;
164
304
  getValue(i: number): CellValue;
165
305
  }
166
306
 
167
307
  declare class XlsxReader extends ExcelReaderAbstract {
168
308
  private zipfile;
309
+ /** Keeps the yauzl source buffer alive when opened from memory. */
310
+ private _zipBuffer;
169
311
  private sharedStrings;
170
312
  private sheetNames;
171
313
  private sheets;
172
314
  private entries;
173
315
  private _currentSheetIndex;
316
+ /** Buffer-based fast path for the current sheet. */
317
+ private _sheetBuffer;
318
+ /** String fallback for unusual markup (prefixed tags, etc.). */
174
319
  private _sheetXml;
175
320
  private _xmlPos;
176
- /** End of the sheetData element; row tags must not be searched past it. */
177
321
  private _xmlEnd;
178
322
  private _currentRow;
323
+ private _currentFormulas;
324
+ private _currentStyleIndices;
325
+ private _currentSharedIndices;
326
+ private _currentRichTexts;
179
327
  private _expectedRowNum;
180
328
  private _pendingEmptyCount;
329
+ private _xfStyles;
330
+ private _sharedRichTexts;
181
331
  xfIdToNumFmtId: number[];
182
332
  customDateFormats: Set<number>;
333
+ /** One flag per xf id: 1 when the style's number format is a date. */
334
+ private _dateXf;
183
335
  constructor();
184
336
  open(path: string, readSharedStrings?: boolean): Promise<void>;
337
+ /** Open a workbook already held in memory (ZIP bytes). */
338
+ openBuffer(buffer: Buffer, readSharedStrings?: boolean): Promise<void>;
339
+ private _openWith;
340
+ private _resetState;
341
+ private _loadWorkbookStructure;
185
342
  close(): Promise<void>;
186
343
  private _readZipEntryContent;
344
+ /**
345
+ * Read one ZIP member into a Buffer. The uncompressed size from the
346
+ * central directory is used to allocate once instead of concatenating
347
+ * stream chunks.
348
+ */
349
+ private _readZipEntryBuffer;
187
350
  private _parseSharedStrings;
188
351
  private _parseStyles;
352
+ private _isDateNumFmt;
353
+ private _isDateStyle;
189
354
  getSheetNames(): string[];
355
+ /** Advance to the next row. Always use `await`. */
190
356
  read(): Promise<boolean>;
357
+ /**
358
+ * Synchronous row access for batch consumers. Returns the internal row
359
+ * array, valid until the next call; `readAll`/`rows` return copies.
360
+ * Select a sheet (or call `read()` once) before using it.
361
+ */
362
+ readRow(): CellValue[] | null;
363
+ /** Collect every remaining row of the current sheet. */
364
+ readAll(): Promise<CellValue[][]>;
365
+ /** Stream every remaining row of the current sheet. */
366
+ rows(): AsyncGenerator<CellValue[]>;
191
367
  selectSheet(nameOrIndex: string | number): Promise<void>;
192
368
  nextResult(): Promise<boolean>;
193
369
  private _initSheet;
194
- private _readNextRow;
195
- /**
196
- * Find the next `<row …>` start, ignoring similarly named elements such as
197
- * `<rowBreaks>` that follow `</sheetData>`.
198
- */
199
- private _findRowTagStart;
200
- private _parseRowByIndex;
370
+ /** Set `_xmlPos/_xmlEnd` for the buffer fast path; false when unsupported. */
371
+ private _initBufferSheet;
372
+ private _initStringSheetRange;
373
+ private _readNextRowBuffer;
374
+ private _findRowTagStartBuffer;
375
+ private _parseRowBuffer;
376
+ /** Concatenated `<t>` runs of an inline-string cell body. */
377
+ private _decodeInlineBuffer;
378
+ private _extractFormulaBuffer;
379
+ private _decodeCellValue;
380
+ private _readNextRowString;
381
+ private _findRowTagStartString;
382
+ private _parseRowString;
383
+ private _extractFormulaString;
384
+ /** Style of the current row cell at column `i`, or null for the default style. */
385
+ getStyle(i: number): CellStyle | null;
386
+ /** Formula text of the current row cell at column `i`, or null. */
387
+ getFormula(i: number): string | null;
388
+ /** Rich-text runs of the current row cell at column `i`, or null. */
389
+ getRichText(i: number): RichText | null;
390
+ private _resetCurrentRow;
201
391
  getValue(i: number): CellValue;
202
392
  }
203
393
 
@@ -212,6 +402,128 @@ declare class ReaderFactory {
212
402
  static create(filePath: string): ExcelReaderAbstract;
213
403
  }
214
404
 
405
+ /** Sink used by the XLSX core; XML is always written as UTF-8 text. */
406
+ interface TextByteSink {
407
+ writeString(str: string): void;
408
+ }
409
+
410
+ /**
411
+ * Shared XLSX package serialization.
412
+ *
413
+ * Everything in this module is pure string/byte generation with no Node or
414
+ * browser imports: the Node writer and the browser writer both call it, which
415
+ * is what keeps their unpacked parts identical.
416
+ */
417
+
418
+ type SharedStringValue = string | RichText;
419
+ interface HyperlinkSpec {
420
+ /** Cell reference, e.g. `A1`. */
421
+ ref: string;
422
+ /** External URL (needs a worksheet relationship). */
423
+ target?: string;
424
+ /** Internal location, e.g. `Other!A1`. */
425
+ location?: string;
426
+ tooltip?: string;
427
+ display?: string;
428
+ }
429
+ interface DataValidationSpec {
430
+ sqref: string;
431
+ type: 'whole' | 'decimal' | 'list' | 'date' | 'time' | 'textLength' | 'custom';
432
+ operator?: 'between' | 'notBetween' | 'equal' | 'notEqual' | 'lessThan' | 'lessThanOrEqual' | 'greaterThan' | 'greaterThanOrEqual';
433
+ formula1?: string;
434
+ formula2?: string;
435
+ /** For `type: 'list'`: literal values, emitted as a quoted comma list. */
436
+ values?: string[];
437
+ allowBlank?: boolean;
438
+ showDropdown?: boolean;
439
+ showInputMessage?: boolean;
440
+ promptTitle?: string;
441
+ prompt?: string;
442
+ showErrorMessage?: boolean;
443
+ errorTitle?: string;
444
+ error?: string;
445
+ errorStyle?: 'stop' | 'warning' | 'information';
446
+ }
447
+ type ConditionalFormatRule = {
448
+ type: 'cellIs';
449
+ operator: 'between' | 'notBetween' | 'equal' | 'notEqual' | 'lessThan' | 'lessThanOrEqual' | 'greaterThan' | 'greaterThanOrEqual';
450
+ formula: string[];
451
+ style?: CellStyle;
452
+ stopIfTrue?: boolean;
453
+ } | {
454
+ type: 'expression';
455
+ formula: string;
456
+ style?: CellStyle;
457
+ stopIfTrue?: boolean;
458
+ } | {
459
+ type: 'colorScale';
460
+ cfvo: Array<{
461
+ type: 'min' | 'max' | 'num' | 'percent' | 'percentile' | 'formula';
462
+ value?: string;
463
+ }>;
464
+ colors: string[];
465
+ } | {
466
+ type: 'dataBar';
467
+ cfvo: Array<{
468
+ type: 'min' | 'max' | 'num' | 'percent' | 'percentile' | 'formula';
469
+ value?: string;
470
+ }>;
471
+ color: string;
472
+ showValue?: boolean;
473
+ } | {
474
+ type: 'iconSet';
475
+ iconSet?: '3Arrows' | '3ArrowsGray' | '3Flags' | '3TrafficLights1' | '3TrafficLights2' | '3Signs' | '3Symbols' | '3Symbols2' | '4Arrows' | '4ArrowsGray' | '4RedToBlack' | '4Rating' | '4TrafficLights' | '5Arrows' | '5ArrowsGray' | '5Rating' | '5Quarters';
476
+ cfvo?: Array<{
477
+ type: 'percent' | 'num' | 'percentile';
478
+ value?: string;
479
+ }>;
480
+ showValue?: boolean;
481
+ reverse?: boolean;
482
+ } | {
483
+ type: 'top10';
484
+ rank?: number;
485
+ percent?: boolean;
486
+ bottom?: boolean;
487
+ style?: CellStyle;
488
+ } | {
489
+ type: 'duplicateValues' | 'uniqueValues';
490
+ style?: CellStyle;
491
+ } | {
492
+ type: 'containsText' | 'notContainsText' | 'beginsWith' | 'endsWith';
493
+ text: string;
494
+ style?: CellStyle;
495
+ };
496
+ interface ConditionalFormatSpec {
497
+ sqref: string;
498
+ rules: ConditionalFormatRule[];
499
+ }
500
+ interface CommentSpec {
501
+ ref: string;
502
+ text: string;
503
+ author?: string;
504
+ /** Server-side comment GUID, when the comment came from another system. */
505
+ guid?: string;
506
+ }
507
+ interface XlsxSheetFeatures {
508
+ merges?: string[];
509
+ hyperlinks?: HyperlinkSpec[];
510
+ dataValidations?: DataValidationSpec[];
511
+ conditionalFormats?: ConditionalFormatSpec[];
512
+ comments?: CommentSpec[];
513
+ }
514
+
515
+ /** Options accepted by the XLSB streaming and batch sheet APIs. */
516
+ interface XlsbSheetOptions extends XlsxSheetFeatures {
517
+ hidden?: boolean;
518
+ doAutofilter?: boolean;
519
+ sampleRows?: unknown[][];
520
+ /** Conservative autofit from headers and the first rows (default true). */
521
+ autofit?: boolean;
522
+ /** Rows measured by autofit (default 200); 0 measures only headers/samples. */
523
+ autofitRows?: number;
524
+ /** Fixed widths in Excel character units; skips autofit when provided. */
525
+ columnWidths?: Array<number | null | undefined>;
526
+ }
215
527
  /**
216
528
  * High-performance XLSB (Excel Binary Workbook) writer.
217
529
  * Prefer this format for large datasets — typically faster and smaller than XLSX.
@@ -230,9 +542,8 @@ declare class XlsbWriter {
230
542
  private sstCntAll;
231
543
  private _autofilterIsOn;
232
544
  private _oaEpoch;
233
- private _nextFmtId;
234
- private _nextXfIdx;
235
- private _stFmtRecords;
545
+ private readonly _useSharedStrings;
546
+ private readonly _styles;
236
547
  /** Worksheet binary template (BrtBeginSheet … dimension / views / cols placeholders). */
237
548
  private _sheet1Bytes;
238
549
  private _workbookBinStart;
@@ -251,6 +562,11 @@ declare class XlsbWriter {
251
562
  private _magicFilterExcel2016Fix1;
252
563
  private _magicFilterExcel2016Fix2;
253
564
  private readonly stream;
565
+ private _streamPending;
566
+ private _streamHeadFlushed;
567
+ private _streamHasHeaders;
568
+ private _streamDataRows;
569
+ private _streamAutofitRows;
254
570
  /**
255
571
  * @param filePath Destination `.xlsb` path on disk.
256
572
  */
@@ -260,20 +576,27 @@ declare class XlsbWriter {
260
576
  */
261
577
  constructor(filePath: string, options?: {
262
578
  compressionLevel?: number;
579
+ useSharedStrings?: boolean;
263
580
  });
264
581
  /** @deprecated Internal; kept for unit-test access. Prefer sanitizeSheetName(). */
265
582
  private _sanitizeSheetName;
266
- private _registerFormat;
267
- private static _buildBrtRecord;
268
- private static _encodeVlq;
269
- private static _buildStFmtRecord;
270
- private static _buildXfRecord;
271
583
  private _buildStylesBin;
272
584
  /**
273
- * Register a worksheet (metadata only). Prefer `writeSheet` / `startSheet` for data.
274
- * @param sheetName Display name (sanitized to Excel rules)
275
- * @param hidden Whether the sheet is hidden in Excel
585
+ * Resolve column widths: explicit `columnWidths` win, otherwise plain
586
+ * autofit from headers and up to 200 sample rows, capped at 200.
276
587
  */
588
+ private _resolveColumnWidths;
589
+ /** Store supported sheet features; reject the ones XLSB does not emit yet. */
590
+ private _applySheetFeatures;
591
+ /** Merged cells, validations and hyperlinks go after the autoFilter. */
592
+ private _writeFeatureRecords;
593
+ /** Write one cell using the current style registry and formula compiler. */
594
+ private _writeCell;
595
+ /**
596
+ * Register a worksheet (metadata only). Prefer `writeSheet` / `startSheet` for data.
597
+ * @param sheetName Display name (sanitized to Excel rules)
598
+ * @param hidden Whether the sheet is hidden in Excel
599
+ */
277
600
  addSheet(sheetName: string, hidden?: boolean): void;
278
601
  /**
279
602
  * Start a new sheet in streaming mode. Call writeRow() for each row, then endSheet() when done.
@@ -282,11 +605,10 @@ declare class XlsbWriter {
282
605
  * @param headers Optional header row
283
606
  * @param options Optional settings (hidden, doAutofilter)
284
607
  */
285
- startSheet(sheetName: string, columnCount: number, headers?: string[], options?: {
286
- hidden?: boolean;
287
- doAutofilter?: boolean;
288
- sampleRows?: unknown[][];
289
- }): void;
608
+ startSheet(sheetName: string, columnCount: number, headers?: string[], options?: XlsbSheetOptions): void;
609
+ /** Write the delayed sheet head and everything buffered before it. */
610
+ private _flushStreamHead;
611
+ private _writeStreamRow;
290
612
  /**
291
613
  * Write a single row in streaming mode. Must be called between startSheet() and endSheet().
292
614
  * @param row Array of cell values
@@ -299,17 +621,19 @@ declare class XlsbWriter {
299
621
  endSheet(): void;
300
622
  /**
301
623
  * Write an entire sheet in one call (batch mode).
302
- * @param rows Data rows (`CellValue` cells; null/undefined = blank)
303
- * @param headers Optional header row (bold + frozen + autofilter when enabled)
304
- * @param doAutofilter Enable autofilter when headers are present (default true)
624
+ *
625
+ * The third argument keeps the legacy `doAutofilter` boolean; pass
626
+ * {@link XlsbSheetOptions} instead to add merges, hyperlinks, validations
627
+ * and width control.
305
628
  */
306
- writeSheet(rows: CellValue[][], headers?: string[] | null, doAutofilter?: boolean): void;
629
+ writeSheet(rows: CellValue[][], headers?: string[] | null, doAutofilterOrOptions?: boolean | XlsbSheetOptions, extraOptions?: XlsbSheetOptions): void;
307
630
  createRowHeader(bigBuf: BigBuffer, rowNumber: number, startCol: number, endCol: number): void;
308
631
  writeRkNumberInteger(bigBuf: BigBuffer, val: number, colNum: number, styleNum?: number): void;
309
632
  writeDouble(bigBuf: BigBuffer, val: number, colNum: number, styleNum?: number): void;
310
- writeBool(bigBuf: BigBuffer, val: boolean, colNum: number): void;
633
+ writeBool(bigBuf: BigBuffer, val: boolean, colNum: number, styleNum?: number): void;
634
+ /** BrtCellError: column, style (24-bit) and the BIFF12 error code. */
635
+ writeError(bigBuf: BigBuffer, code: string, colNum: number, styleNum?: number): void;
311
636
  writeDateTime(bigBuf: BigBuffer, date: Date, colNum: number): void;
312
- private _getDateWidth;
313
637
  writeString(bigBuf: BigBuffer, val: string, colNum: number, bolded?: boolean, styleOverride?: number): void;
314
638
  private saveSst;
315
639
  private _writeFilterDefinedName;
@@ -320,6 +644,17 @@ declare class XlsbWriter {
320
644
  finalize(): Promise<void>;
321
645
  }
322
646
 
647
+ interface XlsxSheetOptions extends XlsxSheetFeatures {
648
+ hidden?: boolean;
649
+ doAutofilter?: boolean;
650
+ sampleRows?: unknown[][];
651
+ /** Conservative autofit from headers and the first rows (default true). */
652
+ autofit?: boolean;
653
+ /** Rows measured by autofit (default 200); 0 measures only headers/samples. */
654
+ autofitRows?: number;
655
+ /** Fixed widths in Excel character units; skips autofit when provided. */
656
+ columnWidths?: Array<number | null | undefined>;
657
+ }
323
658
  /**
324
659
  * XLSX (Office Open XML) writer with streaming ZIP output.
325
660
  */
@@ -334,15 +669,18 @@ declare class XlsxWriter {
334
669
  private sheetList;
335
670
  private sstArray;
336
671
  private sstMap;
672
+ private readonly _styles;
337
673
  private sstCntAll;
338
674
  private _autofilterIsOn;
339
675
  private _oaEpoch;
340
- private _formatRegistry;
341
- private _formatXfMap;
342
- private _nextNumfmtId;
343
- private _nextXfIndex;
676
+ private readonly _useSharedStrings;
344
677
  private readonly stream;
345
678
  private currentColLetters;
679
+ private _streamPending;
680
+ private _streamHeadFlushed;
681
+ private _streamHasHeaders;
682
+ private _streamDataRows;
683
+ private _streamAutofitRows;
346
684
  /**
347
685
  * @param filePath Destination `.xlsx` path on disk.
348
686
  */
@@ -352,39 +690,47 @@ declare class XlsxWriter {
352
690
  */
353
691
  constructor(filePath: string, options?: {
354
692
  compressionLevel?: number;
693
+ useSharedStrings?: boolean;
355
694
  });
356
695
  private _getColumnLetter;
357
696
  /** @deprecated Internal; kept for unit-test access. Prefer sanitizeSheetName(). */
358
697
  private _sanitizeSheetName;
359
- private _escapeSheetNameForXml;
360
- private _formatSheetNameForFormula;
361
698
  /**
362
699
  * Register a worksheet (metadata only). Prefer `writeSheet` / `startSheet` for data.
363
700
  */
364
701
  addSheet(sheetName: string, hidden?: boolean): void;
365
- private _needsEscape;
366
- private _escape;
367
702
  /**
368
- * Start streaming a sheet. Call `writeRow` for each row, then `endSheet`.
703
+ * Resolve column widths with the conservative autofit: header text and up
704
+ * to 200 sample rows, calibrated character costs plus padding, capped at
705
+ * 200. Fixed `columnWidths` win over autofit.
369
706
  */
370
- startSheet(sheetName: string, columnCount: number, headers?: string[], options?: {
371
- hidden?: boolean;
372
- doAutofilter?: boolean;
373
- sampleRows?: unknown[][];
374
- }): void;
707
+ private _resolveColumnWidths;
708
+ /** Store sheet features and build the worksheet rels/comments parts. */
709
+ private _applySheetFeatures;
710
+ private _appendExtraParts;
711
+ startSheet(sheetName: string, columnCount: number, headers?: string[], options?: XlsxSheetOptions): void;
712
+ /** Write the delayed sheet head and everything buffered before it. */
713
+ private _flushStreamHead;
714
+ private _writeStreamRow;
375
715
  /** Write one data row in streaming mode. */
376
716
  writeRow(row: CellValue[]): void;
377
717
  /** Finish the current streaming sheet and append it to the archive. */
378
718
  endSheet(): void;
379
719
  /**
380
720
  * Write an entire sheet in one call (batch mode).
721
+ *
722
+ * The third argument keeps the legacy `doAutofilter` boolean; pass
723
+ * {@link XlsxSheetOptions} instead to add features (merges, hyperlinks,
724
+ * validations, conditional formats, comments) and width control.
381
725
  */
382
- writeSheet(rows: CellValue[][], headers?: string[] | null, doAutofilter?: boolean): void;
726
+ writeSheet(rows: CellValue[][], headers?: string[] | null, doAutofilterOrOptions?: boolean | XlsxSheetOptions, extraOptions?: XlsxSheetOptions): void;
383
727
  /** `ref` is the cell's `r` attribute from `cellRefAttr`, or '' when omitted. */
384
728
  private _writeStringCell;
385
- private _registerFormat;
729
+ writeStringCell(bigBuf: TextByteSink, val: SharedStringValue, ref: string, styleOverride?: number): void;
730
+ registerStyle(style: CellStyle): number;
731
+ registerFormat(fmtString: string): number;
732
+ toOaDate(date: Date): number;
386
733
  private _toOADate;
387
- private _getDateWidth;
388
734
  /** Finalize the ZIP package and close the output stream. */
389
735
  finalize(): Promise<void>;
390
736
  private _writeSharedStrings;
@@ -675,18 +1021,63 @@ declare function assertRowFitsColumns(kind: string, length: number, columnCount:
675
1021
  declare function assertRowCount(totalRows: number): void;
676
1022
  /** Create column-width scratch array filled with -1 (unknown). */
677
1023
  declare function initColWidths(columnCount: number): number[];
1024
+ /** Excel's default column width in characters (Calibri 11, 96 DPI). */
1025
+ declare const DEFAULT_COLUMN_WIDTH = 8.43;
1026
+ /** Hard cap for automatic column widths, in Excel character units. */
1027
+ declare const MAX_AUTOFIT_WIDTH = 200;
1028
+ /**
1029
+ * Excel stores the column width plus the 5-pixel cell padding, i.e. 5/7 of a
1030
+ * character in Calibri 11 at 96 DPI. Automatic widths include it; explicit
1031
+ * `columnWidths` are written unchanged.
1032
+ */
1033
+ declare const COLUMN_WIDTH_PADDING: number;
1034
+ /** Room Excel reserves for the autofilter drop-down button, in characters. */
1035
+ declare const AUTOFILTER_BUTTON_WIDTH = 2.4;
1036
+ /**
1037
+ * Rows measured by automatic widths. xlspy defaults to 1000; 200 keeps the
1038
+ * width estimate representative while cutting the write-time cost and the
1039
+ * streaming buffer (measured widths are dominated by the header and the first
1040
+ * rows anyway).
1041
+ */
1042
+ declare const AUTOFIT_SAMPLE_ROWS = 200;
1043
+ /** Validate the `autofitRows` option (undefined: 200). */
1044
+ declare function resolveAutofitRows(value?: number): number;
1045
+ /**
1046
+ * Conservative displayed-width units for `text`, calibrated so that the
1047
+ * resulting autofit width is not below xlspy's for the same text and font
1048
+ * (up to the 200-character cap). CJK/fullwidth characters count as 2.2 units,
1049
+ * emoji as 3.4, combining marks and control characters as zero. The optional
1050
+ * `fontName` selects the narrow (Calibri-like) or worst-case cost table.
1051
+ */
1052
+ declare function textDisplayUnits(text: string, fontName?: string | null): number;
1053
+ /** Conservative autofit width in Excel character units (before file padding). */
1054
+ declare function autofitWidth(text: string, font?: FontStyle | null, indentChars?: number): number;
1055
+ /** Autofit width as stored in the file: displayed width plus cell padding. */
1056
+ declare function autofitFileWidth(text: string, font?: FontStyle | null, indentChars?: number): number;
1057
+ /** Options for {@link applyHeaderWidths}. */
1058
+ interface HeaderWidthOptions {
1059
+ /** Header cells use a bold font. */
1060
+ bold?: boolean;
1061
+ /** Add room for the autofilter drop-down button. */
1062
+ filterButton?: boolean;
1063
+ }
678
1064
  /** Apply autofit widths from header labels. */
679
- declare function applyHeaderWidths(colWidths: number[], headers: string[], columnCount: number): void;
1065
+ declare function applyHeaderWidths(colWidths: number[], headers: string[], columnCount: number, options?: HeaderWidthOptions): void;
680
1066
  /**
681
1067
  * Sample up to `sampleLimit` rows and expand column widths (autofit).
682
- * Skips null/undefined cells; Dates use a fixed display length of 20.
1068
+ * Skips null/undefined cells; understands styled, formula and rich-text cells.
683
1069
  */
684
1070
  declare function updateColWidthsFromRows(colWidths: number[], rows: unknown[][], sampleLimit?: number): void;
685
1071
  /**
686
- * Resolve a stored autofit width to the value written into the file.
687
- * XLSB uses floored integers; XLSX keeps the float.
1072
+ * Resolve a stored width to the value written into the file. The fallback is
1073
+ * Excel's default width plus cell padding, matching the old 10-character
1074
+ * default while staying close to xlspy/Excel.
688
1075
  */
689
1076
  declare function defaultColWidth(width: number, floor?: boolean): number;
1077
+ /** Validate one explicit column width (xlspy accepts 0.5..255 characters). */
1078
+ declare function assertColumnWidth(width: number, index: number): number;
1079
+ /** Compact XML attribute value for an XLSX column width (2 decimals max). */
1080
+ declare function formatColumnWidth(width: number): string;
690
1081
 
691
1082
  /**
692
1083
  * Shared streaming-sheet session state for Node writers.
@@ -750,5 +1141,13 @@ declare function trimTrailingEmptyRows<T>(rows: T[][]): T[][];
750
1141
  * Phonetic `<rPh>` blocks are skipped (xlspy parity) and self-closing `<si/>` yields "".
751
1142
  */
752
1143
  declare function parseSharedStringsXml(xml: string): string[];
1144
+ /**
1145
+ * Buffer-based equivalent of {@link parseSharedStringsXml} for the common
1146
+ * Excel markup: unprefixed `<si>`/`<t>`, plain or rich runs, self-closing
1147
+ * `<si/>`, and `<rPh>` blocks that must be skipped. Falls back to the regex
1148
+ * parser for anything it does not recognise (CDATA, comments, prefixes,
1149
+ * attributes on `<si>`).
1150
+ */
1151
+ declare function parseSharedStringsBuffer(buf: Buffer): string[];
753
1152
 
754
- export { BiffReaderWriter, BigBuffer, type CellValue, type ExcelConversionOptions, ExcelReaderAbstract, F, type FormattedCell, INVALID_SHEET_NAME_CHARS, MAX_CELL_CHARS, MAX_COLS, MAX_ROWS, type PrimitiveCellValue, ReaderFactory, type ReplaceSheetDataOptions$1 as ReplaceSheetDataOptions, type RowSource$1 as RowSource, StreamingSheetState, XlsbReader, type ReplaceSheetDataOptions as XlsbReplaceSheetDataOptions, XlsbUpdater, XlsbWriter, XlsxUpdater as XlsmUpdater, XlsxReader, XlsxUpdater, XlsxWriter, applyHeaderWidths, assertCellTextLength, assertColumnCount, assertRowCount, assertRowFitsColumns, columnIndexToLetter, columnLetterToIndex, convertXlsbToXlsx, convertXlsxToXlsb, defaultColWidth, escapeXlsxText, escapeXmlText, getFormat, initColWidths, isFormattedCell, parseSharedStringsXml, parseTagAttributes, resolveRelTarget, sanitizeSheetName, trimTrailingEmptyRows, unescapeXml, unescapeXmlText, unwrapCell, updateColWidthsFromRows };
1153
+ export { AUTOFILTER_BUTTON_WIDTH, AUTOFIT_SAMPLE_ROWS, type AlignmentStyle, BiffReaderWriter, BigBuffer, type BorderLineStyle, type BorderSide, type BorderStyle, COLUMN_WIDTH_PADDING, type CellStyle, type CellValue, type CommentSpec, type ConditionalFormatRule, type ConditionalFormatSpec, DEFAULT_COLUMN_WIDTH, type DataValidationSpec, type ExcelConversionOptions, ExcelError, ExcelReaderAbstract, F, type FillStyle, type FontStyle, type FontUnderline, type FormattedCell, type FormulaCell, type HeaderWidthOptions, type HyperlinkSpec, INVALID_SHEET_NAME_CHARS, MAX_AUTOFIT_WIDTH, MAX_CELL_CHARS, MAX_COLS, MAX_ROWS, type PatternType, type PrimitiveCellValue, type ProtectionStyle, ReaderFactory, type ReplaceSheetDataOptions$1 as ReplaceSheetDataOptions, RichText, type RichTextRun, type RowSource$1 as RowSource, StreamingSheetState, type StyleColor, type StyledCell, XlsbReader, type ReplaceSheetDataOptions as XlsbReplaceSheetDataOptions, type XlsbSheetOptions, XlsbUpdater, XlsbWriter, XlsxUpdater as XlsmUpdater, XlsxReader, type XlsxSheetFeatures, type XlsxSheetOptions, XlsxUpdater, XlsxWriter, applyHeaderWidths, assertCellTextLength, assertColumnCount, assertColumnWidth, assertRowCount, assertRowFitsColumns, autofitFileWidth, autofitWidth, columnIndexToLetter, columnLetterToIndex, convertXlsbToXlsx, convertXlsxToXlsb, defaultColWidth, escapeXlsxText, escapeXmlText, formatColumnWidth, getCellStyle, getFormat, getFormula, initColWidths, isFormattedCell, isFormulaCell, isRichText, isStyledCell, mergeCellStyle, parseSharedStringsBuffer, parseSharedStringsXml, parseTagAttributes, resolveAutofitRows, resolveRelTarget, sanitizeSheetName, textDisplayUnits, trimTrailingEmptyRows, unescapeXml, unescapeXmlText, unwrapCell, updateColWidthsFromRows };