@shbernal/ts-xlsx 2.1.0 → 3.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.
Files changed (225) hide show
  1. package/README.md +4 -1
  2. package/dist/bytes.d.ts +7 -0
  3. package/dist/bytes.js +22 -0
  4. package/dist/core/address.d.ts +60 -2
  5. package/dist/core/address.js +40 -24
  6. package/dist/core/autofilter.d.ts +5 -4
  7. package/dist/core/autofilter.js +34 -35
  8. package/dist/core/axis-handle.d.ts +10 -0
  9. package/dist/core/axis-handle.js +17 -0
  10. package/dist/core/cell.d.ts +25 -0
  11. package/dist/core/cell.js +9 -3
  12. package/dist/core/clone.d.ts +29 -0
  13. package/dist/core/clone.js +24 -0
  14. package/dist/core/color-resolution.js +2 -4
  15. package/dist/core/column.d.ts +5 -1
  16. package/dist/core/column.js +34 -35
  17. package/dist/core/comment-thread.js +2 -1
  18. package/dist/core/conditional-formatting-overlay.d.ts +2 -1
  19. package/dist/core/conditional-formatting-overlay.js +2 -2
  20. package/dist/core/conditional-formatting.d.ts +14 -6
  21. package/dist/core/conditional-formatting.js +45 -43
  22. package/dist/core/containers.d.ts +12 -0
  23. package/dist/core/containers.js +5 -0
  24. package/dist/core/data-validation-overlay.d.ts +2 -1
  25. package/dist/core/data-validation-overlay.js +4 -4
  26. package/dist/core/data-validation.d.ts +9 -3
  27. package/dist/core/data-validation.js +21 -18
  28. package/dist/core/date-format.d.ts +20 -0
  29. package/dist/core/date-format.js +160 -0
  30. package/dist/core/date.d.ts +43 -8
  31. package/dist/core/date.js +18 -2
  32. package/dist/core/formula.d.ts +26 -0
  33. package/dist/core/formula.js +20 -4
  34. package/dist/core/grid-edits.d.ts +2 -3
  35. package/dist/core/grid-edits.js +69 -70
  36. package/dist/core/grid-shift.d.ts +58 -9
  37. package/dist/core/grid-shift.js +26 -4
  38. package/dist/core/image.d.ts +14 -10
  39. package/dist/core/image.js +9 -9
  40. package/dist/core/internal.d.ts +8 -2
  41. package/dist/core/limits.d.ts +23 -0
  42. package/dist/core/limits.js +4 -0
  43. package/dist/core/merge-index.d.ts +31 -0
  44. package/dist/core/merge-index.js +60 -0
  45. package/dist/core/merge.d.ts +5 -14
  46. package/dist/core/merge.js +18 -27
  47. package/dist/core/page-setup.d.ts +72 -2
  48. package/dist/core/page-setup.js +34 -8
  49. package/dist/core/pivot-table.d.ts +1 -1
  50. package/dist/core/pivot-table.js +67 -53
  51. package/dist/core/protection.js +2 -11
  52. package/dist/core/range.js +34 -30
  53. package/dist/core/row-input.js +2 -2
  54. package/dist/core/row.d.ts +5 -1
  55. package/dist/core/row.js +22 -23
  56. package/dist/core/style.d.ts +134 -75
  57. package/dist/core/style.js +112 -53
  58. package/dist/core/table-style.d.ts +1 -1
  59. package/dist/core/table-style.js +4 -4
  60. package/dist/core/table.d.ts +28 -10
  61. package/dist/core/table.js +54 -39
  62. package/dist/core/theme.js +6 -5
  63. package/dist/core/used-extent.d.ts +44 -0
  64. package/dist/core/used-extent.js +102 -0
  65. package/dist/core/value.d.ts +9 -0
  66. package/dist/core/value.js +36 -34
  67. package/dist/core/workbook-media.d.ts +34 -0
  68. package/dist/core/workbook-media.js +44 -0
  69. package/dist/core/workbook-styles.d.ts +67 -1
  70. package/dist/core/workbook-vba.js +3 -2
  71. package/dist/core/workbook.d.ts +23 -2
  72. package/dist/core/workbook.js +24 -40
  73. package/dist/core/worksheet-comments.d.ts +2 -1
  74. package/dist/core/worksheet-comments.js +20 -13
  75. package/dist/core/worksheet-merges.d.ts +44 -0
  76. package/dist/core/worksheet-merges.js +59 -0
  77. package/dist/core/worksheet-model.d.ts +9 -1
  78. package/dist/core/worksheet-model.js +24 -9
  79. package/dist/core/worksheet.d.ts +72 -12
  80. package/dist/core/worksheet.js +68 -70
  81. package/dist/customui/ribbon.js +6 -7
  82. package/dist/entries/core.d.ts +18 -13
  83. package/dist/entries/core.js +12 -6
  84. package/dist/entries/xlsx.d.ts +1 -1
  85. package/dist/errors.d.ts +64 -0
  86. package/dist/errors.js +22 -0
  87. package/dist/hex.d.ts +8 -0
  88. package/dist/hex.js +3 -0
  89. package/dist/io/csv/delimiter.d.ts +9 -0
  90. package/dist/io/csv/delimiter.js +6 -0
  91. package/dist/io/csv/read.js +11 -4
  92. package/dist/io/csv/write.d.ts +9 -2
  93. package/dist/io/csv/write.js +9 -38
  94. package/dist/io/opc/inflate.js +15 -1
  95. package/dist/io/opc/namespaces.d.ts +16 -0
  96. package/dist/io/opc/namespaces.js +4 -0
  97. package/dist/io/opc/part-paths.d.ts +10 -0
  98. package/dist/io/opc/part-paths.js +16 -1
  99. package/dist/io/opc/read-opc.d.ts +69 -10
  100. package/dist/io/opc/read-opc.js +49 -55
  101. package/dist/io/opc/read-options.d.ts +5 -1
  102. package/dist/io/opc/rels.d.ts +1 -6
  103. package/dist/io/opc/rels.js +1 -4
  104. package/dist/io/opc/sniff-format.d.ts +3 -3
  105. package/dist/io/opc/sniff-format.js +2 -2
  106. package/dist/io/style/xf-style.d.ts +48 -2
  107. package/dist/io/style/xf-style.js +14 -0
  108. package/dist/io/xlsb/formula.d.ts +2 -2
  109. package/dist/io/xlsb/formula.js +49 -34
  110. package/dist/io/xlsb/primitives.js +8 -14
  111. package/dist/io/xlsb/ptg-functions.d.ts +19 -10
  112. package/dist/io/xlsb/ptg-functions.js +385 -596
  113. package/dist/io/xlsb/read-styles.js +50 -83
  114. package/dist/io/xlsb/read-worksheet.d.ts +20 -1
  115. package/dist/io/xlsb/read-worksheet.js +73 -89
  116. package/dist/io/xlsb/read.d.ts +6 -3
  117. package/dist/io/xlsb/read.js +47 -29
  118. package/dist/io/xlsb/record-stream.d.ts +30 -0
  119. package/dist/io/xlsb/record-stream.js +20 -0
  120. package/dist/io/xlsb/record-types.d.ts +1 -5
  121. package/dist/io/xlsb/record-types.js +1 -5
  122. package/dist/io/xlsx/cell-accumulator.d.ts +9 -2
  123. package/dist/io/xlsx/cell-accumulator.js +32 -9
  124. package/dist/io/xlsx/cell-style-resolution.d.ts +25 -0
  125. package/dist/io/xlsx/cell-style-resolution.js +29 -0
  126. package/dist/io/xlsx/cell-value.d.ts +5 -3
  127. package/dist/io/xlsx/cell-value.js +13 -15
  128. package/dist/io/xlsx/color-xml.js +3 -2
  129. package/dist/io/xlsx/column-budget.d.ts +26 -0
  130. package/dist/io/xlsx/column-budget.js +22 -0
  131. package/dist/io/xlsx/comments.d.ts +6 -2
  132. package/dist/io/xlsx/comments.js +36 -31
  133. package/dist/io/xlsx/conditional-formatting.d.ts +9 -5
  134. package/dist/io/xlsx/conditional-formatting.js +51 -46
  135. package/dist/io/xlsx/data-validation.d.ts +9 -7
  136. package/dist/io/xlsx/data-validation.js +38 -35
  137. package/dist/io/xlsx/edit-vba.d.ts +7 -2
  138. package/dist/io/xlsx/edit-vba.js +15 -29
  139. package/dist/io/xlsx/font-xml.d.ts +7 -0
  140. package/dist/io/xlsx/font-xml.js +104 -0
  141. package/dist/io/xlsx/hyperlinks.d.ts +41 -7
  142. package/dist/io/xlsx/hyperlinks.js +40 -28
  143. package/dist/io/xlsx/images.js +24 -25
  144. package/dist/io/xlsx/namespaces.d.ts +16 -0
  145. package/dist/io/xlsx/namespaces.js +7 -0
  146. package/dist/io/xlsx/package-plan.d.ts +23 -3
  147. package/dist/io/xlsx/package-plan.js +66 -25
  148. package/dist/io/xlsx/part-names.d.ts +41 -0
  149. package/dist/io/xlsx/part-names.js +46 -0
  150. package/dist/io/xlsx/pivot.js +8 -7
  151. package/dist/io/xlsx/read-parts.d.ts +59 -0
  152. package/dist/io/xlsx/read-parts.js +252 -0
  153. package/dist/io/xlsx/read-pivot.js +3 -1
  154. package/dist/io/xlsx/read-repair.d.ts +26 -0
  155. package/dist/io/xlsx/read-repair.js +39 -0
  156. package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +1 -1
  157. package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +7 -3
  158. package/dist/io/xlsx/read-rows.d.ts +18 -5
  159. package/dist/io/xlsx/read-rows.js +50 -37
  160. package/dist/io/xlsx/read-shared-strings.js +5 -3
  161. package/dist/io/xlsx/read-styles.d.ts +1 -6
  162. package/dist/io/xlsx/read-styles.js +65 -109
  163. package/dist/io/xlsx/read-workbook-xml.d.ts +36 -0
  164. package/dist/io/xlsx/read-workbook-xml.js +170 -0
  165. package/dist/io/xlsx/read-worksheet.d.ts +11 -1
  166. package/dist/io/xlsx/read-worksheet.js +51 -193
  167. package/dist/io/xlsx/read.d.ts +5 -13
  168. package/dist/io/xlsx/read.js +111 -436
  169. package/dist/io/xlsx/rich-text.js +1 -1
  170. package/dist/io/xlsx/row-position.d.ts +18 -0
  171. package/dist/io/xlsx/row-position.js +11 -0
  172. package/dist/io/xlsx/row-xml.d.ts +128 -0
  173. package/dist/io/xlsx/row-xml.js +203 -0
  174. package/dist/io/xlsx/shared-formulas.js +2 -2
  175. package/dist/io/xlsx/sheet-properties.d.ts +10 -1
  176. package/dist/io/xlsx/sheet-properties.js +166 -37
  177. package/dist/io/xlsx/style-elements.d.ts +43 -0
  178. package/dist/io/xlsx/style-elements.js +156 -0
  179. package/dist/io/xlsx/styles.d.ts +12 -3
  180. package/dist/io/xlsx/styles.js +5 -204
  181. package/dist/io/xlsx/tables.js +11 -14
  182. package/dist/io/xlsx/theme-xml.d.ts +26 -4
  183. package/dist/io/xlsx/theme-xml.js +107 -69
  184. package/dist/io/xlsx/threaded-comments.d.ts +8 -10
  185. package/dist/io/xlsx/threaded-comments.js +3 -1
  186. package/dist/io/xlsx/workbook-xml.d.ts +54 -4
  187. package/dist/io/xlsx/workbook-xml.js +84 -57
  188. package/dist/io/xlsx/worksheet-xml.d.ts +23 -70
  189. package/dist/io/xlsx/worksheet-xml.js +59 -242
  190. package/dist/io/xlsx/write-stream.d.ts +71 -16
  191. package/dist/io/xlsx/write-stream.js +79 -41
  192. package/dist/io/xlsx/write.d.ts +6 -2
  193. package/dist/io/xlsx/write.js +217 -153
  194. package/dist/rel-type.d.ts +16 -0
  195. package/dist/rel-type.js +6 -0
  196. package/dist/token-set.d.ts +28 -0
  197. package/dist/token-set.js +7 -0
  198. package/dist/vba/bytes.d.ts +14 -5
  199. package/dist/vba/bytes.js +16 -6
  200. package/dist/vba/cfb-format.d.ts +32 -0
  201. package/dist/vba/cfb-format.js +13 -0
  202. package/dist/vba/cfb-writer.d.ts +9 -0
  203. package/dist/vba/cfb-writer.js +28 -25
  204. package/dist/vba/cfb.d.ts +13 -1
  205. package/dist/vba/cfb.js +73 -57
  206. package/dist/vba/codepage.js +2 -1
  207. package/dist/vba/errors.d.ts +7 -0
  208. package/dist/vba/ms-ovba.js +79 -23
  209. package/dist/vba/project-editor.js +35 -40
  210. package/dist/vba/project.d.ts +11 -1
  211. package/dist/vba/project.js +40 -21
  212. package/dist/vba/vba-encoding.js +5 -6
  213. package/dist/xml/xml-attrs.d.ts +37 -0
  214. package/dist/xml/xml-attrs.js +30 -0
  215. package/dist/xml/xml-chars.d.ts +40 -0
  216. package/dist/xml/xml-chars.js +12 -0
  217. package/dist/xml/xml-namespaces.d.ts +31 -0
  218. package/dist/xml/xml-namespaces.js +50 -0
  219. package/dist/xml/xml-read.d.ts +128 -81
  220. package/dist/xml/xml-read.js +161 -178
  221. package/dist/xml/xml-scan.d.ts +78 -0
  222. package/dist/xml/xml-scan.js +180 -0
  223. package/dist/xml/xml.d.ts +29 -28
  224. package/dist/xml/xml.js +24 -24
  225. package/package.json +13 -9
@@ -3,13 +3,15 @@ import type { AutoFilter } from '../../core/autofilter.ts';
3
3
  import type { Cell } from '../../core/cell.ts';
4
4
  import type { ConditionalFormatting } from '../../core/conditional-formatting.ts';
5
5
  import type { DataValidation } from '../../core/data-validation.ts';
6
+ import type { DateEpoch } from '../../core/date.ts';
6
7
  import type { AnchorPoint } from '../../core/image.ts';
8
+ import { INTERNAL } from '../../core/internal.ts';
7
9
  import type { SheetProtectionOptions } from '../../core/protection.ts';
8
10
  import { type CellValue } from '../../core/value.ts';
9
11
  import { type AddImageOptions, type AddWorksheetOptions, Workbook } from '../../core/workbook.ts';
10
12
  import type { Worksheet } from '../../core/worksheet.ts';
13
+ import { type FlushedSheet } from './row-xml.ts';
11
14
  import type { StyleRegistry } from './styles.ts';
12
- import { type FlushedSheet } from './write.ts';
13
15
  /** Calculation settings applied to the streamed workbook. Mirrors the {@link Workbook} flags. */
14
16
  export interface CalcProperties {
15
17
  /** Ask the consumer to recalculate every formula on open: the OOXML `fullCalcOnLoad` flag. */
@@ -45,6 +47,13 @@ export type WorkbookStreamWriterOptions = SinkOptions & {
45
47
  * same {@link WriteOptions.useSharedStrings} the buffered writer exposes. Off by default.
46
48
  */
47
49
  readonly useSharedStrings?: boolean;
50
+ /**
51
+ * Which date system this workbook's serials count in ({@link Workbook.dateEpoch}). A construction
52
+ * option rather than a settable property, unlike on the buffered writer: an eager writer serialises
53
+ * each row as it is committed, so a system changed part-way through would leave the rows before the
54
+ * change counting from a different day than the rows after it.
55
+ */
56
+ readonly dateEpoch?: DateEpoch;
48
57
  };
49
58
  /**
50
59
  * A row appended to a {@link WorksheetStreamWriter}. Style its cells through {@link cells}, then call
@@ -68,7 +77,13 @@ export declare class StreamedRow {
68
77
  */
69
78
  export declare class WorksheetStreamWriter {
70
79
  #private;
71
- constructor(sheet: Worksheet, eager: boolean, styles: StyleRegistry);
80
+ private constructor();
81
+ /**
82
+ * The workbook writer's construction channel. A static member keyed by the same symbol as the
83
+ * instance one below, so both halves of this class's internal wiring are reached the one way; see
84
+ * `core/internal.ts`.
85
+ */
86
+ static readonly [INTERNAL]: WorksheetStreamWriterFactory;
72
87
  /** The sheet's name. */
73
88
  get name(): string;
74
89
  /** The number of rows written so far. Spans gaps and formatted-only rows, like the model, and
@@ -78,16 +93,6 @@ export declare class WorksheetStreamWriter {
78
93
  addRow(values: CellValue[]): StreamedRow;
79
94
  /** Append a batch of rows in one call, each landing directly below the previous. */
80
95
  addRows(rows: CellValue[][]): StreamedRow[];
81
- /**
82
- * Serialise an eagerly-committed row and release its cells from the model. Called by
83
- * {@link StreamedRow.commit}; the row's `<row>` XML is retained (interned into the workbook's live
84
- * style registry so its ids stay valid) and the cell graph is dropped, bounding peak memory.
85
- *
86
- * @throws {AuthoringError} if the row carries a shared-formula cell: a finished row cannot join the
87
- * whole-sheet formula planning, so shared formulas must be authored through {@link getCell}.
88
- */
89
- flushRow(number: number, cells: readonly Cell[]): void;
90
- flushedSheet(): FlushedSheet | undefined;
91
96
  /**
92
97
  * Address a cell by its A1 reference to read or style it before the sheet is committed. The row it
93
98
  * names must still be live: a row whose {@link StreamedRow.commit} has run is finished.
@@ -139,6 +144,41 @@ export declare class WorksheetStreamWriter {
139
144
  /** Whether the sheet has been committed. */
140
145
  get committed(): boolean;
141
146
  get model(): Worksheet;
147
+ readonly [INTERNAL]: WorksheetStreamWriterInternals;
148
+ }
149
+ /**
150
+ * How a {@link WorksheetStreamWriter} comes into being. Its parameters are the writer's own plumbing
151
+ * -- the interning table its rows' style ids are assigned from, the epoch its dates are serialised
152
+ * against -- and a consumer supplies neither, because a consumer receives the sheet writer from
153
+ * {@link WorkbookStreamWriter.addWorksheet}. Reached as `WorksheetStreamWriter[INTERNAL]`.
154
+ */
155
+ export interface WorksheetStreamWriterFactory {
156
+ create(sheet: Worksheet, eager: boolean, styles: StyleRegistry, dateEpoch: DateEpoch): WorksheetStreamWriter;
157
+ }
158
+ /**
159
+ * What the streaming writer's own machinery may do to a sheet writer that a caller may not, the same
160
+ * boundary `sheet[INTERNAL]` draws around the model. Reached as `writer[INTERNAL]`; see
161
+ * `core/internal.ts`.
162
+ *
163
+ * Both members were public until they were not. Neither is callable by anyone but this module, and
164
+ * between them they named seven writer-internal types -- `StyleRegistry`, `FlushedSheet` and the
165
+ * shapes those reach -- on a published class's signature, so a consumer could hold values whose types
166
+ * the package would not export. They carried `@unpublished` to record that, which is a note rather
167
+ * than a boundary; this is the boundary.
168
+ */
169
+ export interface WorksheetStreamWriterInternals {
170
+ /**
171
+ * Serialise an eagerly-committed row and release its cells from the model. Called by
172
+ * {@link StreamedRow.commit}; the row's `<row>` XML is retained (interned into the workbook's live
173
+ * style registry so its ids stay valid) and the cell graph is dropped, bounding peak memory.
174
+ *
175
+ * @throws {AuthoringError} if the row carries a shared-formula cell: a finished row cannot join the
176
+ * whole-sheet formula planning, so shared formulas must be authored through
177
+ * {@link WorksheetStreamWriter.getCell}.
178
+ */
179
+ flushRow(number: number, cells: readonly Cell[]): void;
180
+ /** The rows this writer flushed, or undefined if none. Handed to `buildPackageParts` at commit. */
181
+ flushedSheet(): FlushedSheet | undefined;
142
182
  }
143
183
  /**
144
184
  * A workbook written incrementally to a Node stream. Add worksheets, append their rows, commit each
@@ -169,10 +209,25 @@ export declare class WorkbookStreamWriter {
169
209
  /** Create a worksheet and append it to the workbook. */
170
210
  addWorksheet(name: string, options?: AddWorksheetOptions): WorksheetStreamWriter;
171
211
  /**
172
- * Assemble the workbook into its package, stream the bytes through {@link stream}, and resolve with
173
- * the same bytes. Every sheet is frozen first, so a row added after this rejects legibly. Idempotent
174
- * only in that a second call throws rather than re-emitting.
212
+ * Assemble the workbook into its package and stream the bytes out. Every sheet is frozen first, so
213
+ * a row added after this rejects legibly. Idempotent only in that a second call throws rather than
214
+ * re-emitting.
215
+ *
216
+ * **What it resolves with, and why it can be `undefined`.** The archive is handed back only when
217
+ * somebody asked for it: when no sink was supplied, or when {@link stream} was touched, since a
218
+ * `PassThrough` nobody drained would otherwise be the only copy. A caller who passed `{filename}`
219
+ * or their own `stream` and never reached for {@link stream} gets `undefined`, and the package is
220
+ * never materialised as a whole: retaining every chunk to concatenate them at the end costs a
221
+ * second full-size buffer on top of the part map and the archive itself, which is exactly the
222
+ * memory the caller passed a sink to avoid. The type says so rather than the prose alone, because
223
+ * a promise that resolves with a value the caller was told to ignore is a promise they will use.
224
+ *
225
+ * If assembling or zipping the package fails, the returned promise rejects *and* every stream this
226
+ * writer was given or handed out is destroyed with that error. A caller piping {@link stream}, or
227
+ * awaiting its own sink, therefore sees an `error` rather than waiting on a stream that will never
228
+ * end; and it is an `error` rather than a clean `end` because the bytes written so far are a
229
+ * truncated package that must not be read as a whole one.
175
230
  */
176
- commit(): Promise<Uint8Array>;
231
+ commit(): Promise<Uint8Array | undefined>;
177
232
  }
178
233
  export {};
@@ -6,9 +6,12 @@ import { encodeAddress, tryDecodeCellRef } from '../../core/address.js';
6
6
  import { INTERNAL } from '../../core/internal.js';
7
7
  import { isSharedFormulaValue } from '../../core/value.js';
8
8
  import { Workbook } from '../../core/workbook.js';
9
- import { AuthoringError } from '../../errors.js';
9
+ import { AuthoringError, quoted } from '../../errors.js';
10
10
  import { FIXED_ENTRY_MTIME } from '../opc/zip-mtime.js';
11
- import { buildColumnDefaults, buildPackageParts, createStyleRegistry, Extent, renderRow, } from './write.js';
11
+ import { collectNotes } from './comments.js';
12
+ import { collectHyperlinks } from './hyperlinks.js';
13
+ import { buildColumnDefaults, Extent, renderRow, } from './row-xml.js';
14
+ import { buildPackageParts, createStyleRegistry } from './write.js';
12
15
  export class StreamedRow {
13
16
  #cells;
14
17
  #sheet;
@@ -26,7 +29,7 @@ export class StreamedRow {
26
29
  if (this.#committed)
27
30
  return;
28
31
  this.#committed = true;
29
- this.#sheet?.flushRow(this.#number, this.#cells);
32
+ this.#sheet?.[INTERNAL].flushRow(this.#number, this.#cells);
30
33
  }
31
34
  }
32
35
  export class WorksheetStreamWriter {
@@ -39,12 +42,21 @@ export class WorksheetStreamWriter {
39
42
  #flushedRows = [];
40
43
  #flushedNumbers = new Set();
41
44
  #extent = new Extent();
42
- #maxRowOutlineLevel = 0;
43
- constructor(sheet, eager, styles) {
45
+ #rowOutline = new Map();
46
+ #hyperlinks = [];
47
+ #notes = [];
48
+ #dateEpoch;
49
+ constructor(sheet, eager, styles, dateEpoch) {
44
50
  this.#sheet = sheet;
45
51
  this.#eager = eager;
46
52
  this.#styles = styles;
53
+ this.#dateEpoch = dateEpoch;
47
54
  }
55
+ static [INTERNAL] = {
56
+ create(sheet, eager, styles, dateEpoch) {
57
+ return new WorksheetStreamWriter(sheet, eager, styles, dateEpoch);
58
+ },
59
+ };
48
60
  get name() {
49
61
  return this.#sheet.name;
50
62
  }
@@ -77,45 +89,53 @@ export class WorksheetStreamWriter {
77
89
  });
78
90
  return cells;
79
91
  }
80
- flushRow(number, cells) {
92
+ #flushRow(number, cells) {
81
93
  for (const cell of cells) {
82
94
  if (isSharedFormulaValue(cell.value)) {
83
- throw new AuthoringError(`row ${number} of streamed sheet "${this.#sheet.name}" carries a shared-formula cell; a ` +
95
+ throw new AuthoringError(`row ${number} of streamed sheet ${quoted(this.#sheet.name)} carries a shared-formula cell; a ` +
84
96
  'committed row is finalised before the sheet is planned, so author shared formulas through ' +
85
97
  'getCell (leaving the row uncommitted) instead');
86
98
  }
87
99
  }
88
100
  this.#columnDefaults ??= buildColumnDefaults(this.#sheet);
89
101
  const properties = this.#sheet.getRow(number).properties;
90
- this.#maxRowOutlineLevel = Math.max(this.#maxRowOutlineLevel, properties?.outlineLevel ?? 0);
91
- const { xml, minCol, maxCol } = renderRow({ number, cells, properties }, {
102
+ this.#rowOutline.set(number, {
103
+ outlineLevel: properties?.outlineLevel ?? 0,
104
+ hidden: properties?.hidden ?? false,
105
+ });
106
+ this.#hyperlinks.push(...collectHyperlinks(cells));
107
+ this.#notes.push(...collectNotes(cells));
108
+ const { xml, attrs, minCol, maxCol } = renderRow({ number, cells, properties }, {
92
109
  columnDefaults: this.#columnDefaults,
93
110
  styles: this.#styles,
94
111
  sharedStrings: null,
95
112
  sharedRoles: new Map(),
96
113
  collapsedSummaries: new Set(),
114
+ dateEpoch: this.#dateEpoch,
97
115
  });
98
116
  if (xml !== '') {
99
- this.#flushedRows.push({ number, xml });
117
+ this.#flushedRows.push({ number, xml, attrs });
100
118
  this.#extent.add(number, minCol, maxCol);
101
119
  }
102
120
  this.#flushedNumbers.add(number);
103
121
  this.#sheet[INTERNAL].evictRow(number);
104
122
  }
105
- flushedSheet() {
123
+ #flushedSheet() {
106
124
  if (this.#flushedRows.length === 0)
107
125
  return undefined;
108
126
  return {
109
127
  rows: this.#flushedRows,
110
128
  extent: this.#extent,
111
- maxRowOutlineLevel: this.#maxRowOutlineLevel,
129
+ rowOutline: this.#rowOutline,
130
+ hyperlinks: this.#hyperlinks,
131
+ notes: this.#notes,
112
132
  };
113
133
  }
114
134
  getCell(reference) {
115
135
  this.#assertOpen();
116
136
  const target = tryDecodeCellRef(reference);
117
137
  if (target !== undefined && this.#flushedNumbers.has(target.row)) {
118
- throw new AuthoringError(`cell ${reference} of streamed sheet "${this.#sheet.name}" is in row ${target.row}, which is ` +
138
+ throw new AuthoringError(`cell ${reference} of streamed sheet ${quoted(this.#sheet.name)} is in row ${target.row}, which is ` +
119
139
  'already committed: its bytes are written and its cells released, so this value could only ' +
120
140
  'be emitted as a second row with that number. Style a row before committing it.');
121
141
  }
@@ -153,9 +173,15 @@ export class WorksheetStreamWriter {
153
173
  get model() {
154
174
  return this.#sheet;
155
175
  }
176
+ [INTERNAL] = {
177
+ flushRow: (number, cells) => {
178
+ this.#flushRow(number, cells);
179
+ },
180
+ flushedSheet: () => this.#flushedSheet(),
181
+ };
156
182
  #assertOpen() {
157
183
  if (this.#committed) {
158
- throw new AuthoringError(`worksheet "${this.#sheet.name}" is already committed: its rows are finalised and no more can be added`);
184
+ throw new AuthoringError(`worksheet ${quoted(this.#sheet.name)} is already committed: its rows are finalised and no more can be added`);
159
185
  }
160
186
  }
161
187
  }
@@ -172,6 +198,8 @@ export class WorkbookStreamWriter {
172
198
  constructor(options = {}) {
173
199
  this.#writeOptions = { useSharedStrings: options.useSharedStrings ?? false };
174
200
  this.#eager = !this.#writeOptions.useSharedStrings;
201
+ if (options.dateEpoch !== undefined)
202
+ this.#workbook.dateEpoch = options.dateEpoch;
175
203
  this.#styles = createStyleRegistry(this.#workbook);
176
204
  if (options.stream && options.filename) {
177
205
  throw new AuthoringError('provide either a stream or a filename to the streaming writer, not both');
@@ -187,23 +215,17 @@ export class WorkbookStreamWriter {
187
215
  return this.#stream;
188
216
  }
189
217
  addImage(options) {
190
- if (this.#committed) {
191
- throw new AuthoringError('the workbook is already committed: no more images can be registered');
192
- }
218
+ this.#assertOpen('no more images can be registered');
193
219
  return this.#workbook.addImage(options);
194
220
  }
195
221
  addWorksheet(name, options = {}) {
196
- if (this.#committed) {
197
- throw new AuthoringError('the workbook is already committed: no more worksheets can be added');
198
- }
199
- const sheet = new WorksheetStreamWriter(this.#workbook.addWorksheet(name, options), this.#eager, this.#styles);
222
+ this.#assertOpen('no more worksheets can be added');
223
+ const sheet = WorksheetStreamWriter[INTERNAL].create(this.#workbook.addWorksheet(name, options), this.#eager, this.#styles, this.#workbook.dateEpoch);
200
224
  this.#sheets.push(sheet);
201
225
  return sheet;
202
226
  }
203
227
  async commit() {
204
- if (this.#committed) {
205
- throw new AuthoringError('the workbook is already committed');
206
- }
228
+ this.#assertOpen('its package is already assembled and cannot be re-emitted');
207
229
  this.#committed = true;
208
230
  for (const sheet of this.#sheets)
209
231
  sheet.commit();
@@ -211,26 +233,41 @@ export class WorkbookStreamWriter {
211
233
  this.#workbook.fullCalcOnLoad = true;
212
234
  const flushed = new Map();
213
235
  for (const sheet of this.#sheets) {
214
- const sheetFlushed = sheet.flushedSheet();
236
+ const sheetFlushed = sheet[INTERNAL].flushedSheet();
215
237
  if (sheetFlushed)
216
238
  flushed.set(sheet.model, sheetFlushed);
217
239
  }
218
- const parts = buildPackageParts(this.#workbook, {
219
- ...this.#writeOptions,
220
- styles: this.#styles,
221
- flushed,
222
- });
223
240
  const owned = this.#stream;
224
241
  const sink = this.#sink;
242
+ const collect = sink === undefined || owned !== undefined;
225
243
  const sinkSettled = sink ? settleOnFinish(sink) : undefined;
226
- const bytes = await streamZipPackage(parts, (chunk) => {
227
- owned?.write(chunk);
228
- sink?.write(chunk);
229
- });
230
- owned?.end();
231
- sink?.end();
232
- await sinkSettled;
233
- return bytes;
244
+ try {
245
+ const parts = buildPackageParts(this.#workbook, {
246
+ ...this.#writeOptions,
247
+ styles: this.#styles,
248
+ flushed,
249
+ });
250
+ const bytes = await streamZipPackage(parts, (chunk) => {
251
+ owned?.write(chunk);
252
+ sink?.write(chunk);
253
+ }, collect);
254
+ owned?.end();
255
+ sink?.end();
256
+ await sinkSettled;
257
+ return bytes;
258
+ }
259
+ catch (error) {
260
+ const failure = error instanceof Error ? error : new Error(String(error));
261
+ sinkSettled?.catch(() => { });
262
+ owned?.destroy(failure);
263
+ sink?.destroy(failure);
264
+ throw error;
265
+ }
266
+ }
267
+ #assertOpen(what) {
268
+ if (this.#committed) {
269
+ throw new AuthoringError(`the workbook is already committed: ${what}`);
270
+ }
234
271
  }
235
272
  }
236
273
  function settleOnFinish(sink) {
@@ -239,7 +276,7 @@ function settleOnFinish(sink) {
239
276
  sink.once('error', reject);
240
277
  });
241
278
  }
242
- function streamZipPackage(parts, onChunk) {
279
+ function streamZipPackage(parts, onChunk, collect) {
243
280
  return new Promise((resolve, reject) => {
244
281
  const collected = [];
245
282
  const zip = new Zip((err, chunk, final) => {
@@ -247,10 +284,11 @@ function streamZipPackage(parts, onChunk) {
247
284
  reject(err);
248
285
  return;
249
286
  }
250
- collected.push(chunk);
287
+ if (collect)
288
+ collected.push(chunk);
251
289
  onChunk(chunk);
252
290
  if (final)
253
- resolve(concat(collected));
291
+ resolve(collect ? concat(collected) : undefined);
254
292
  });
255
293
  for (const [name, data] of Object.entries(parts)) {
256
294
  const entry = new ZipDeflate(name, { level: 6 });
@@ -1,8 +1,7 @@
1
1
  import type { Workbook } from '../../core/workbook.ts';
2
2
  import type { Worksheet } from '../../core/worksheet.ts';
3
+ import type { FlushedSheet } from './row-xml.ts';
3
4
  import { StyleRegistry } from './styles.ts';
4
- import { type FlushedSheet } from './worksheet-xml.ts';
5
- export { buildColumnDefaults, Extent, type FlushedSheet, type RowRenderContext, renderRow, } from './worksheet-xml.ts';
6
5
  /** Options controlling how {@link writeXlsx} serialises a workbook. */
7
6
  export interface WriteOptions {
8
7
  /**
@@ -81,6 +80,11 @@ export declare function createStyleRegistry(workbook: Workbook): StyleRegistry;
81
80
  * streaming writer can drive the identical parts through a streamed zip container rather than
82
81
  * `zipSync`. Neither writer duplicates a byte of serialisation.
83
82
  *
83
+ * Three steps, and their order is load-bearing. The package is planned in full before anything is
84
+ * serialised, so a drawing's embed and a pivot's cache can target a part number that already exists.
85
+ * The sheets are serialised before the parts are emitted, because interning a style is a side effect
86
+ * of that pass and `xl/styles.xml` is one of the parts emitted in the third step.
87
+ *
84
88
  * @throws {AuthoringError} if the workbook has no worksheets, or holds a value the writer cannot represent.
85
89
  */
86
90
  export declare function buildPackageParts(workbook: Workbook, options?: InternalWriteOptions): Record<string, Uint8Array>;