@shbernal/ts-xlsx 1.3.1 → 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.
Files changed (186) hide show
  1. package/README.md +20 -10
  2. package/dist/bytes.d.ts +24 -0
  3. package/dist/bytes.js +34 -0
  4. package/dist/core/address.d.ts +72 -3
  5. package/dist/core/address.js +50 -10
  6. package/dist/core/autofilter.d.ts +13 -2
  7. package/dist/core/autofilter.js +35 -10
  8. package/dist/core/cell.d.ts +17 -16
  9. package/dist/core/cell.js +3 -7
  10. package/dist/core/color-resolution.d.ts +8 -8
  11. package/dist/core/color-resolution.js +7 -8
  12. package/dist/core/column.d.ts +8 -9
  13. package/dist/core/column.js +2 -4
  14. package/dist/core/comment-thread.d.ts +32 -23
  15. package/dist/core/comment-thread.js +4 -4
  16. package/dist/core/conditional-formatting-overlay.d.ts +6 -1
  17. package/dist/core/conditional-formatting-overlay.js +11 -0
  18. package/dist/core/conditional-formatting.d.ts +48 -8
  19. package/dist/core/conditional-formatting.js +88 -0
  20. package/dist/core/data-validation-overlay.d.ts +12 -1
  21. package/dist/core/data-validation-overlay.js +21 -2
  22. package/dist/core/data-validation.d.ts +12 -5
  23. package/dist/core/data-validation.js +34 -0
  24. package/dist/core/date.d.ts +3 -3
  25. package/dist/core/formula.d.ts +6 -6
  26. package/dist/core/grid-edits.d.ts +16 -0
  27. package/dist/core/grid-edits.js +29 -11
  28. package/dist/core/grid-shift.d.ts +19 -0
  29. package/dist/core/grid-shift.js +8 -0
  30. package/dist/core/image.d.ts +39 -2
  31. package/dist/core/image.js +10 -0
  32. package/dist/core/internal.d.ts +9 -1
  33. package/dist/core/limits.d.ts +4 -4
  34. package/dist/core/merge.d.ts +17 -12
  35. package/dist/core/merge.js +36 -5
  36. package/dist/core/page-setup.d.ts +20 -6
  37. package/dist/core/page-setup.js +8 -1
  38. package/dist/core/pivot-table.d.ts +4 -4
  39. package/dist/core/pivot-table.js +29 -20
  40. package/dist/core/preserved.d.ts +3 -3
  41. package/dist/core/protection.d.ts +7 -4
  42. package/dist/core/protection.js +20 -14
  43. package/dist/core/range.d.ts +16 -14
  44. package/dist/core/range.js +5 -5
  45. package/dist/core/row-input.d.ts +9 -8
  46. package/dist/core/row-input.js +12 -10
  47. package/dist/core/row.d.ts +7 -8
  48. package/dist/core/row.js +2 -4
  49. package/dist/core/style.d.ts +41 -14
  50. package/dist/core/style.js +82 -67
  51. package/dist/core/table-style.d.ts +9 -9
  52. package/dist/core/table-style.js +1 -1
  53. package/dist/core/table.d.ts +53 -51
  54. package/dist/core/table.js +83 -41
  55. package/dist/core/theme.d.ts +16 -44
  56. package/dist/core/theme.js +6 -148
  57. package/dist/core/value.d.ts +7 -7
  58. package/dist/core/workbook-protection.d.ts +2 -2
  59. package/dist/core/workbook-styles.d.ts +26 -0
  60. package/dist/core/workbook-styles.js +48 -0
  61. package/dist/core/workbook-theme.d.ts +25 -0
  62. package/dist/core/workbook-theme.js +49 -0
  63. package/dist/core/workbook-vba.d.ts +17 -0
  64. package/dist/core/workbook-vba.js +79 -0
  65. package/dist/core/workbook.d.ts +133 -87
  66. package/dist/core/workbook.js +74 -133
  67. package/dist/core/worksheet-comments.d.ts +17 -0
  68. package/dist/core/worksheet-comments.js +70 -0
  69. package/dist/core/worksheet-model.d.ts +4 -4
  70. package/dist/core/worksheet-model.js +1 -0
  71. package/dist/core/worksheet-pictures.d.ts +29 -0
  72. package/dist/core/worksheet-pictures.js +42 -0
  73. package/dist/core/worksheet.d.ts +100 -79
  74. package/dist/core/worksheet.js +63 -142
  75. package/dist/customui/errors.d.ts +1 -1
  76. package/dist/customui/ribbon.d.ts +3 -3
  77. package/dist/customui/ribbon.js +24 -23
  78. package/dist/entries/core.d.ts +7 -7
  79. package/dist/entries/core.js +1 -1
  80. package/dist/entries/csv.d.ts +1 -1
  81. package/dist/entries/node-unavailable.d.ts +15 -0
  82. package/dist/entries/node-unavailable.js +20 -0
  83. package/dist/entries/node.d.ts +1 -0
  84. package/dist/entries/node.js +1 -0
  85. package/dist/entries/vba.d.ts +1 -1
  86. package/dist/entries/vba.js +1 -1
  87. package/dist/entries/xlsx.d.ts +1 -1
  88. package/dist/entries/xlsx.js +1 -1
  89. package/dist/errors.d.ts +12 -12
  90. package/dist/io/csv/read.js +2 -3
  91. package/dist/io/csv/write.d.ts +19 -4
  92. package/dist/io/csv/write.js +34 -4
  93. package/dist/io/opc/errors.d.ts +6 -6
  94. package/dist/io/opc/inflate.d.ts +1 -1
  95. package/dist/io/opc/inflate.js +2 -12
  96. package/dist/io/opc/namespaces.d.ts +1 -1
  97. package/dist/io/opc/read-opc.d.ts +19 -3
  98. package/dist/io/opc/read-opc.js +14 -8
  99. package/dist/io/opc/read-options.d.ts +2 -2
  100. package/dist/io/opc/rels.d.ts +11 -0
  101. package/dist/io/opc/rels.js +3 -2
  102. package/dist/io/opc/sniff-format.d.ts +2 -2
  103. package/dist/io/style/xf-style.d.ts +44 -7
  104. package/dist/io/style/xf-style.js +27 -0
  105. package/dist/io/xlsb/errors.d.ts +1 -1
  106. package/dist/io/xlsb/formula.d.ts +4 -4
  107. package/dist/io/xlsb/primitives.d.ts +8 -8
  108. package/dist/io/xlsb/ptg-functions.d.ts +1 -1
  109. package/dist/io/xlsb/read-styles.js +2 -20
  110. package/dist/io/xlsb/read.d.ts +2 -2
  111. package/dist/io/xlsx/cell-accumulator.d.ts +26 -9
  112. package/dist/io/xlsx/cell-accumulator.js +89 -46
  113. package/dist/io/xlsx/cell-value.d.ts +3 -3
  114. package/dist/io/xlsx/cell-value.js +9 -6
  115. package/dist/io/xlsx/color-xml.d.ts +1 -1
  116. package/dist/io/xlsx/color-xml.js +18 -23
  117. package/dist/io/xlsx/comments.d.ts +5 -5
  118. package/dist/io/xlsx/comments.js +17 -8
  119. package/dist/io/xlsx/conditional-formatting.d.ts +4 -4
  120. package/dist/io/xlsx/conditional-formatting.js +64 -58
  121. package/dist/io/xlsx/data-validation.d.ts +3 -3
  122. package/dist/io/xlsx/data-validation.js +35 -33
  123. package/dist/io/xlsx/errors.d.ts +3 -3
  124. package/dist/io/xlsx/hyperlinks.d.ts +4 -2
  125. package/dist/io/xlsx/hyperlinks.js +10 -18
  126. package/dist/io/xlsx/images.d.ts +1 -1
  127. package/dist/io/xlsx/images.js +25 -28
  128. package/dist/io/xlsx/namespaces.d.ts +2 -2
  129. package/dist/io/xlsx/package-plan.js +5 -2
  130. package/dist/io/xlsx/read-pivot.js +4 -13
  131. package/dist/io/xlsx/read-rows.d.ts +13 -13
  132. package/dist/io/xlsx/read-rows.js +19 -55
  133. package/dist/io/xlsx/read-shared-strings.js +7 -44
  134. package/dist/io/xlsx/read-styles.d.ts +3 -3
  135. package/dist/io/xlsx/read-styles.js +42 -66
  136. package/dist/io/xlsx/read-worksheet.js +73 -129
  137. package/dist/io/xlsx/read.d.ts +4 -6
  138. package/dist/io/xlsx/read.js +104 -142
  139. package/dist/io/xlsx/rich-runs.d.ts +41 -7
  140. package/dist/io/xlsx/rich-runs.js +94 -30
  141. package/dist/io/xlsx/rich-text.d.ts +1 -1
  142. package/dist/io/xlsx/shared-strings.d.ts +2 -2
  143. package/dist/io/xlsx/sheet-properties.d.ts +1 -1
  144. package/dist/io/xlsx/sheet-properties.js +14 -10
  145. package/dist/io/xlsx/styles.d.ts +9 -22
  146. package/dist/io/xlsx/styles.js +65 -89
  147. package/dist/io/xlsx/tables.d.ts +2 -2
  148. package/dist/io/xlsx/tables.js +20 -25
  149. package/dist/io/xlsx/theme-xml.d.ts +37 -0
  150. package/dist/io/xlsx/theme-xml.js +145 -0
  151. package/dist/io/xlsx/threaded-comments.d.ts +18 -27
  152. package/dist/io/xlsx/threaded-comments.js +26 -38
  153. package/dist/io/xlsx/workbook-xml.js +14 -8
  154. package/dist/io/xlsx/worksheet-xml.d.ts +2 -2
  155. package/dist/io/xlsx/worksheet-xml.js +40 -48
  156. package/dist/io/xlsx/write-stream.d.ts +23 -16
  157. package/dist/io/xlsx/write-stream.js +13 -14
  158. package/dist/io/xlsx/write.d.ts +8 -7
  159. package/dist/io/xlsx/write.js +6 -3
  160. package/dist/io/xlsx/x14-ext.d.ts +1 -1
  161. package/dist/sha512.d.ts +2 -0
  162. package/dist/sha512.js +146 -0
  163. package/dist/vba/bytes.d.ts +11 -0
  164. package/dist/vba/bytes.js +29 -0
  165. package/dist/vba/cfb-writer.d.ts +1 -1
  166. package/dist/vba/cfb.d.ts +1 -1
  167. package/dist/vba/cfb.js +15 -51
  168. package/dist/vba/codepage.d.ts +2 -2
  169. package/dist/vba/dir-records.d.ts +53 -0
  170. package/dist/vba/dir-records.js +28 -0
  171. package/dist/vba/errors.d.ts +2 -2
  172. package/dist/vba/index.d.ts +2 -2
  173. package/dist/vba/index.js +2 -2
  174. package/dist/vba/ms-ovba.d.ts +1 -1
  175. package/dist/vba/ms-ovba.js +1 -3
  176. package/dist/vba/project-editor.d.ts +9 -9
  177. package/dist/vba/project-editor.js +6 -44
  178. package/dist/vba/project.d.ts +5 -5
  179. package/dist/vba/project.js +3 -28
  180. package/dist/vba/vba-encoding.d.ts +1 -1
  181. package/dist/xml/errors.d.ts +1 -1
  182. package/dist/xml/xml-read.d.ts +98 -9
  183. package/dist/xml/xml-read.js +70 -0
  184. package/dist/xml/xml.d.ts +74 -10
  185. package/dist/xml/xml.js +39 -7
  186. package/package.json +59 -36
@@ -12,12 +12,12 @@ import type { StyleRegistry } from './styles.ts';
12
12
  import { type FlushedSheet } from './write.ts';
13
13
  /** Calculation settings applied to the streamed workbook. Mirrors the {@link Workbook} flags. */
14
14
  export interface CalcProperties {
15
- /** Ask the consumer to recalculate every formula on open the OOXML `fullCalcOnLoad` flag. */
15
+ /** Ask the consumer to recalculate every formula on open: the OOXML `fullCalcOnLoad` flag. */
16
16
  fullCalcOnLoad?: boolean;
17
17
  }
18
18
  type SinkOptions = {
19
19
  /**
20
- * Write the package to a caller-owned {@link Writable} sink an outbound upload, a cloud-SDK
20
+ * Write the package to a caller-owned {@link Writable} sink: an outbound upload, a cloud-SDK
21
21
  * stream, any destination the caller controls. {@link WorkbookStreamWriter.commit} pushes every
22
22
  * chunk into it and settles only once the sink has finished (or rejects if it errors), so a
23
23
  * caller can deterministically sequence work after the upload completes. Mutually exclusive
@@ -41,7 +41,7 @@ type SinkOptions = {
41
41
  /** Options fixed at construction that shape the whole streamed package. */
42
42
  export type WorkbookStreamWriterOptions = SinkOptions & {
43
43
  /**
44
- * Pool plain string cell values into a shared-strings table rather than storing each inline the
44
+ * Pool plain string cell values into a shared-strings table rather than storing each inline: the
45
45
  * same {@link WriteOptions.useSharedStrings} the buffered writer exposes. Off by default.
46
46
  */
47
47
  readonly useSharedStrings?: boolean;
@@ -58,12 +58,12 @@ export declare class StreamedRow {
58
58
  /** The cells this row materialised, for styling before it is committed. */
59
59
  get cells(): readonly Cell[];
60
60
  /** Finalise the row: an eager writer serialises it now and releases its cells; otherwise a no-op.
61
- * Committing twice is harmless the second call does nothing rather than re-emitting the row. */
61
+ * Committing twice is harmless: the second call does nothing rather than re-emitting the row. */
62
62
  commit(): void;
63
63
  }
64
64
  /**
65
65
  * A worksheet being written incrementally. Append rows with {@link addRow}/{@link addRows}, style
66
- * cells through {@link getCell}, then {@link commit} to freeze it after which any further mutation
66
+ * cells through {@link getCell}, then {@link commit} to freeze it, after which any further mutation
67
67
  * is rejected with a legible error rather than silently accepted or crashing.
68
68
  */
69
69
  export declare class WorksheetStreamWriter {
@@ -71,7 +71,7 @@ export declare class WorksheetStreamWriter {
71
71
  constructor(sheet: Worksheet, eager: boolean, styles: StyleRegistry);
72
72
  /** The sheet's name. */
73
73
  get name(): string;
74
- /** The number of rows written so far spans gaps and formatted-only rows, like the model, and
74
+ /** The number of rows written so far. Spans gaps and formatted-only rows, like the model, and
75
75
  * survives the eviction of eagerly-flushed rows. */
76
76
  get rowCount(): number;
77
77
  /** Append one row of values after the last used row; the cells are returned for styling. */
@@ -83,32 +83,39 @@ export declare class WorksheetStreamWriter {
83
83
  * {@link StreamedRow.commit}; the row's `<row>` XML is retained (interned into the workbook's live
84
84
  * style registry so its ids stay valid) and the cell graph is dropped, bounding peak memory.
85
85
  *
86
- * @throws {AuthoringError} if the row carries a shared-formula cell a finished row cannot join the
86
+ * @throws {AuthoringError} if the row carries a shared-formula cell: a finished row cannot join the
87
87
  * whole-sheet formula planning, so shared formulas must be authored through {@link getCell}.
88
88
  */
89
89
  flushRow(number: number, cells: readonly Cell[]): void;
90
90
  flushedSheet(): FlushedSheet | undefined;
91
- /** Address a cell by its A1 reference to read or style it before the sheet is committed. */
91
+ /**
92
+ * Address a cell by its A1 reference to read or style it before the sheet is committed. The row it
93
+ * names must still be live: a row whose {@link StreamedRow.commit} has run is finished.
94
+ *
95
+ * @throws {AuthoringError} if the reference names an already-committed row. That row's `<row>` is
96
+ * rendered and its cells are released, so the cell this would materialise could only be written
97
+ * as a second row carrying the same number.
98
+ */
92
99
  getCell(reference: string): Cell;
93
100
  /**
94
101
  * Attach a data validation to a range before the sheet is committed. Delegates to the model, so the
95
- * streamed package emits the `<dataValidations>` block in its CT_Worksheet position before
96
- * `<hyperlinks>` because both writers share one worksheet serializer.
102
+ * streamed package emits the `<dataValidations>` block in its CT_Worksheet position, before
103
+ * `<hyperlinks>`, because both writers share one worksheet serializer.
97
104
  */
98
105
  addDataValidation(sqref: string, rule: DataValidation, options?: {
99
106
  extended?: boolean;
100
107
  }): void;
101
108
  /**
102
109
  * Attach a conditional formatting to a range before the sheet is committed. Like every other block,
103
- * it lands in its schema-mandated slot after `<mergeCells>`, before `<dataValidations>` and
104
- * `<hyperlinks>` since the streamed sheet is serialized through the same path as a buffered write.
110
+ * it lands in its schema-mandated slot, after `<mergeCells>` and before `<dataValidations>` and
111
+ * `<hyperlinks>`, since the streamed sheet is serialized through the same path as a buffered write.
105
112
  */
106
113
  addConditionalFormatting(formatting: ConditionalFormatting): void;
107
114
  /**
108
115
  * Anchor a workbook image (the id from {@link WorkbookStreamWriter.addImage}) to this sheet,
109
116
  * spanning the rectangle from the top-left grid point `tl` to the bottom-right `br`. The streamed
110
117
  * package emits the drawing part, its media relationship, and the sheet's `<drawing>` reference
111
- * exactly as a buffered write does both writers share `buildPackageParts`.
118
+ * exactly as a buffered write does: both writers share `buildPackageParts`.
112
119
  */
113
120
  addImage(imageId: number, anchor: {
114
121
  readonly tl: AnchorPoint;
@@ -116,7 +123,7 @@ export declare class WorksheetStreamWriter {
116
123
  }): void;
117
124
  /**
118
125
  * Apply the sheet's autofilter before it is committed; mirrors {@link Worksheet.autoFilter}. The
119
- * streamed package emits `<autoFilter>` in its CT_Worksheet slot after `<sheetProtection>` and
126
+ * streamed package emits `<autoFilter>` in its CT_Worksheet slot, after `<sheetProtection>`, and
120
127
  * contributes the hidden `_FilterDatabase` defined name, exactly as a buffered write does.
121
128
  */
122
129
  set autoFilter(filter: string | AutoFilter | undefined);
@@ -147,8 +154,8 @@ export declare class WorkbookStreamWriter {
147
154
  /** Document-level metadata written to the package's core properties. */
148
155
  get properties(): Workbook['properties'];
149
156
  /**
150
- * The output stream carrying the package bytes. A caller drives it with Node's standard idiom
151
- * `writer.stream.pipe(out)` which composes because `pipe` returns its destination. The stream is
157
+ * The output stream carrying the package bytes. A caller drives it with Node's standard idiom,
158
+ * `writer.stream.pipe(out)`, which composes because `pipe` returns its destination. The stream is
152
159
  * created lazily on first access so a caller handing the writer its own sink is still free to
153
160
  * ignore this one.
154
161
  */
@@ -1,7 +1,8 @@
1
1
  import { createWriteStream } from 'node:fs';
2
2
  import { PassThrough } from 'node:stream';
3
3
  import { Zip, ZipDeflate } from 'fflate';
4
- import { encodeAddress } from '../../core/address.js';
4
+ import { concat } from '../../bytes.js';
5
+ import { encodeAddress, tryDecodeCellRef } from '../../core/address.js';
5
6
  import { INTERNAL } from '../../core/internal.js';
6
7
  import { isSharedFormulaValue } from '../../core/value.js';
7
8
  import { Workbook } from '../../core/workbook.js';
@@ -36,6 +37,7 @@ export class WorksheetStreamWriter {
36
37
  #lastRow = 0;
37
38
  #columnDefaults;
38
39
  #flushedRows = [];
40
+ #flushedNumbers = new Set();
39
41
  #extent = new Extent();
40
42
  #maxRowOutlineLevel = 0;
41
43
  constructor(sheet, eager, styles) {
@@ -97,6 +99,7 @@ export class WorksheetStreamWriter {
97
99
  this.#flushedRows.push({ number, xml });
98
100
  this.#extent.add(number, minCol, maxCol);
99
101
  }
102
+ this.#flushedNumbers.add(number);
100
103
  this.#sheet[INTERNAL].evictRow(number);
101
104
  }
102
105
  flushedSheet() {
@@ -110,6 +113,12 @@ export class WorksheetStreamWriter {
110
113
  }
111
114
  getCell(reference) {
112
115
  this.#assertOpen();
116
+ const target = tryDecodeCellRef(reference);
117
+ 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 ` +
119
+ 'already committed: its bytes are written and its cells released, so this value could only ' +
120
+ 'be emitted as a second row with that number. Style a row before committing it.');
121
+ }
113
122
  return this.#sheet.getCell(reference);
114
123
  }
115
124
  addDataValidation(sqref, rule, options = {}) {
@@ -146,7 +155,7 @@ export class WorksheetStreamWriter {
146
155
  }
147
156
  #assertOpen() {
148
157
  if (this.#committed) {
149
- throw new AuthoringError(`worksheet "${this.#sheet.name}" is already committed its rows are finalised and no more can be added`);
158
+ throw new AuthoringError(`worksheet "${this.#sheet.name}" is already committed: its rows are finalised and no more can be added`);
150
159
  }
151
160
  }
152
161
  }
@@ -179,13 +188,13 @@ export class WorkbookStreamWriter {
179
188
  }
180
189
  addImage(options) {
181
190
  if (this.#committed) {
182
- throw new AuthoringError('the workbook is already committed no more images can be registered');
191
+ throw new AuthoringError('the workbook is already committed: no more images can be registered');
183
192
  }
184
193
  return this.#workbook.addImage(options);
185
194
  }
186
195
  addWorksheet(name, options = {}) {
187
196
  if (this.#committed) {
188
- throw new AuthoringError('the workbook is already committed no more worksheets can be added');
197
+ throw new AuthoringError('the workbook is already committed: no more worksheets can be added');
189
198
  }
190
199
  const sheet = new WorksheetStreamWriter(this.#workbook.addWorksheet(name, options), this.#eager, this.#styles);
191
200
  this.#sheets.push(sheet);
@@ -252,13 +261,3 @@ function streamZipPackage(parts, onChunk) {
252
261
  zip.end();
253
262
  });
254
263
  }
255
- function concat(chunks) {
256
- const total = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
257
- const out = new Uint8Array(total);
258
- let offset = 0;
259
- for (const chunk of chunks) {
260
- out.set(chunk, offset);
261
- offset += chunk.length;
262
- }
263
- return out;
264
- }
@@ -16,13 +16,14 @@ export interface WriteOptions {
16
16
  /**
17
17
  * {@link WriteOptions} plus the streaming writer's internal wiring, so a buffered caller's options
18
18
  * object can never carry fields meant only for {@link WorkbookStreamWriter}'s own use. Not exported
19
- * from the public barrel {@link buildPackageParts} is the only consumer outside this module.
19
+ * from the public barrel: {@link WorkbookStreamWriter} is the only caller that fills these fields,
20
+ * and it reaches them through {@link buildPackageParts}.
20
21
  */
21
22
  export interface InternalWriteOptions extends WriteOptions {
22
23
  /**
23
24
  * The style registry to intern into, in place of a freshly-seeded one. The streaming writer
24
25
  * serialises each committed row eagerly (freeing its cells), so those rows' style ids must be
25
- * assigned by the very same registry that later emits `xl/styles.xml` otherwise the ids in the
26
+ * assigned by the very same registry that later emits `xl/styles.xml`; otherwise the ids in the
26
27
  * pre-rendered rows would not match the styles part. When omitted the buffered path seeds its own,
27
28
  * so its output is unchanged.
28
29
  */
@@ -48,19 +49,19 @@ export declare function writeXlsx(workbook: Workbook, options?: WriteOptions): U
48
49
  /**
49
50
  * Serialise a workbook into an `.xlsx` package, deflating off the calling thread.
50
51
  *
51
- * Produces the same package {@link writeXlsx} does byte for byte, entry timestamps included and
52
+ * Produces the same package {@link writeXlsx} does, byte for byte and entry timestamps included, and
52
53
  * exists for one reason: DEFLATE dominates the cost of writing a large workbook, and {@link writeXlsx}
53
54
  * spends all of it on the caller's thread. Here `fflate` deflates each part in a worker, so the event
54
55
  * loop keeps turning (stalls drop from the whole write to tens of milliseconds) and parts compress in
55
56
  * parallel, which on a multi-sheet workbook also finishes sooner. On a single-sheet workbook there is
56
57
  * only one part to deflate, so expect responsiveness rather than speed.
57
58
  *
58
- * Building the parts still happens on the calling thread only compression moves. That is why there
59
+ * Building the parts still happens on the calling thread; only compression moves. That is why there
59
60
  * is no `readXlsxAsync` mirroring this: reading is dominated by XML parsing and model building, which
60
61
  * no worker can take, and the reader's zip-bomb ceiling is enforced by counting output between
61
62
  * synchronous input slices. See ADR-0024.
62
63
  *
63
- * @throws {AuthoringError} as a rejection under the same conditions as {@link writeXlsx};
64
+ * @throws {AuthoringError}, as a rejection, under the same conditions as {@link writeXlsx};
64
65
  * the part-building it shares happens before any worker is involved. A failure raised by the zip
65
66
  * layer itself (including an environment that cannot spawn a worker) propagates unwrapped, exactly
66
67
  * as it does from {@link writeXlsx}.
@@ -75,8 +76,8 @@ export declare function writeXlsxAsync(workbook: Workbook, options?: WriteOption
75
76
  export declare function createStyleRegistry(workbook: Workbook): StyleRegistry;
76
77
  /**
77
78
  * Assemble a workbook into the map of OPC package parts (part name → bytes) that make up an `.xlsx`,
78
- * short of zipping them. This is the whole serialisation content types, relationships, workbook,
79
- * per-sheet XML, styles, theme, media, tables, and props factored out of {@link writeXlsx} so the
79
+ * short of zipping them. This is the whole serialisation (content types, relationships, workbook,
80
+ * per-sheet XML, styles, theme, media, tables, and props) factored out of {@link writeXlsx} so the
80
81
  * streaming writer can drive the identical parts through a streamed zip container rather than
81
82
  * `zipSync`. Neither writer duplicates a byte of serialisation.
82
83
  *
@@ -1,5 +1,4 @@
1
1
  import { strToU8, zip, zipSync } from 'fflate';
2
- import { DEFAULT_THEME_XML } from '../../core/theme.js';
3
2
  import { AuthoringError } from '../../errors.js';
4
3
  import { THEME_PART_PATH } from '../opc/part-paths.js';
5
4
  import { relsPartXml } from '../opc/rels.js';
@@ -13,6 +12,7 @@ import { REL } from './relationships.js';
13
12
  import { SharedStringTable } from './shared-strings.js';
14
13
  import { StyleRegistry } from './styles.js';
15
14
  import { tableXml } from './tables.js';
15
+ import { applyThemeOverrides, DEFAULT_THEME_XML } from './theme-xml.js';
16
16
  import { personsXml, threadedCommentsXml } from './threaded-comments.js';
17
17
  import { appPropsXml, contentTypesXml, corePropsXml, FIXED_WORKBOOK_REL_COUNT, rootRelsXml, workbookRelsXml, workbookXml, } from './workbook-xml.js';
18
18
  import { worksheetRelsXml, worksheetXml, } from './worksheet-xml.js';
@@ -66,7 +66,7 @@ function resolveSheetReferences(plan) {
66
66
  export function buildPackageParts(workbook, options = {}) {
67
67
  const sheets = workbook.worksheets;
68
68
  if (sheets.length === 0) {
69
- throw new AuthoringError('cannot write a workbook with no worksheets a zero-sheet package is corrupt to Excel');
69
+ throw new AuthoringError('cannot write a workbook with no worksheets: a zero-sheet package is corrupt to Excel');
70
70
  }
71
71
  const sharedStrings = options.useSharedStrings ? new SharedStringTable() : null;
72
72
  const media = planMedia(workbook, sheets);
@@ -184,7 +184,10 @@ export function buildPackageParts(workbook, options = {}) {
184
184
  'xl/styles.xml': strToU8(styles.toXml()),
185
185
  };
186
186
  if (!preserved.themeEmitted) {
187
- files[THEME_PART_PATH] = strToU8(workbook.authoredThemeXml() ?? DEFAULT_THEME_XML);
187
+ const overrides = workbook.themeOverrides;
188
+ files[THEME_PART_PATH] = strToU8(overrides === undefined
189
+ ? DEFAULT_THEME_XML
190
+ : applyThemeOverrides(DEFAULT_THEME_XML, overrides));
188
191
  }
189
192
  if (hasSharedStrings) {
190
193
  files['xl/sharedStrings.xml'] = strToU8(sharedStrings.toXml());
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Wrap an x14 extension body in the `<ext>` envelope every worksheet- and workbook-level x14
3
3
  * extension shares: a `uri` scoping the extension to its feature (a fixed GUID from
4
- * {@link namespaces.ts}) plus the `xmlns:x14` binding the extension namespace on the element itself
4
+ * {@link namespaces.ts}) plus the `xmlns:x14` binding the extension namespace on the element itself.
5
5
  * Excel declares it per `<ext>`, never at the part root. Callers needing an `<extLst>` wrapper add it
6
6
  * around the result; the producers the worksheet serialiser gathers return this bare so several exts
7
7
  * compose under a single shared `<extLst>`.
@@ -0,0 +1,2 @@
1
+ /** The SHA-512 digest of `message`, as its 64 bytes. */
2
+ export declare function sha512(message: Uint8Array): Uint8Array;
package/dist/sha512.js ADDED
@@ -0,0 +1,146 @@
1
+ const BLOCK_BYTES = 128;
2
+ const DIGEST_BYTES = 64;
3
+ const K = new Uint32Array([
4
+ 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc,
5
+ 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118,
6
+ 0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe, 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2,
7
+ 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694,
8
+ 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65,
9
+ 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5,
10
+ 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4,
11
+ 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70,
12
+ 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df,
13
+ 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b,
14
+ 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30,
15
+ 0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910, 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8,
16
+ 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8,
17
+ 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3,
18
+ 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec,
19
+ 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b,
20
+ 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178,
21
+ 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b,
22
+ 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c,
23
+ 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817,
24
+ ]);
25
+ const INITIAL = new Uint32Array([
26
+ 0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1,
27
+ 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179,
28
+ ]);
29
+ function word(words, index) {
30
+ return words[index] ?? 0;
31
+ }
32
+ export function sha512(message) {
33
+ const blocks = Math.ceil((message.length + 17) / BLOCK_BYTES);
34
+ const padded = new Uint8Array(blocks * BLOCK_BYTES);
35
+ padded.set(message);
36
+ padded[message.length] = 0x80;
37
+ const view = new DataView(padded.buffer);
38
+ const bits = message.length * 8;
39
+ view.setUint32(padded.length - 8, Math.floor(bits / 0x1_0000_0000), false);
40
+ view.setUint32(padded.length - 4, bits >>> 0, false);
41
+ const h = Uint32Array.from(INITIAL);
42
+ const schedule = new Uint32Array(160);
43
+ for (let block = 0; block < blocks; block++)
44
+ compress(h, view, block * BLOCK_BYTES, schedule);
45
+ const digest = new Uint8Array(DIGEST_BYTES);
46
+ const out = new DataView(digest.buffer);
47
+ for (let i = 0; i < 16; i++)
48
+ out.setUint32(i * 4, word(h, i), false);
49
+ return digest;
50
+ }
51
+ function compress(h, input, offset, schedule) {
52
+ for (let t = 0; t < 16; t++) {
53
+ schedule[t * 2] = input.getUint32(offset + t * 8, false);
54
+ schedule[t * 2 + 1] = input.getUint32(offset + t * 8 + 4, false);
55
+ }
56
+ for (let t = 16; t < 80; t++) {
57
+ const xHi = word(schedule, (t - 15) * 2);
58
+ const xLo = word(schedule, (t - 15) * 2 + 1);
59
+ const s0Hi = ((xHi >>> 1) | (xLo << 31)) ^ ((xHi >>> 8) | (xLo << 24)) ^ (xHi >>> 7);
60
+ const s0Lo = ((xLo >>> 1) | (xHi << 31)) ^ ((xLo >>> 8) | (xHi << 24)) ^ ((xLo >>> 7) | (xHi << 25));
61
+ const yHi = word(schedule, (t - 2) * 2);
62
+ const yLo = word(schedule, (t - 2) * 2 + 1);
63
+ const s1Hi = ((yHi >>> 19) | (yLo << 13)) ^ ((yLo >>> 29) | (yHi << 3)) ^ (yHi >>> 6);
64
+ const s1Lo = ((yLo >>> 19) | (yHi << 13)) ^ ((yHi >>> 29) | (yLo << 3)) ^ ((yLo >>> 6) | (yHi << 26));
65
+ const lo = (s1Lo >>> 0) +
66
+ word(schedule, (t - 7) * 2 + 1) +
67
+ (s0Lo >>> 0) +
68
+ word(schedule, (t - 16) * 2 + 1);
69
+ const hi = (s1Hi >>> 0) +
70
+ word(schedule, (t - 7) * 2) +
71
+ (s0Hi >>> 0) +
72
+ word(schedule, (t - 16) * 2) +
73
+ Math.floor(lo / 0x1_0000_0000);
74
+ schedule[t * 2] = hi >>> 0;
75
+ schedule[t * 2 + 1] = lo >>> 0;
76
+ }
77
+ let aHi = word(h, 0);
78
+ let aLo = word(h, 1);
79
+ let bHi = word(h, 2);
80
+ let bLo = word(h, 3);
81
+ let cHi = word(h, 4);
82
+ let cLo = word(h, 5);
83
+ let dHi = word(h, 6);
84
+ let dLo = word(h, 7);
85
+ let eHi = word(h, 8);
86
+ let eLo = word(h, 9);
87
+ let fHi = word(h, 10);
88
+ let fLo = word(h, 11);
89
+ let gHi = word(h, 12);
90
+ let gLo = word(h, 13);
91
+ let hHi = word(h, 14);
92
+ let hLo = word(h, 15);
93
+ for (let t = 0; t < 80; t++) {
94
+ const bigS1Hi = ((eHi >>> 14) | (eLo << 18)) ^ ((eHi >>> 18) | (eLo << 14)) ^ ((eLo >>> 9) | (eHi << 23));
95
+ const bigS1Lo = ((eLo >>> 14) | (eHi << 18)) ^ ((eLo >>> 18) | (eHi << 14)) ^ ((eHi >>> 9) | (eLo << 23));
96
+ const chHi = (eHi & fHi) ^ (~eHi & gHi);
97
+ const chLo = (eLo & fLo) ^ (~eLo & gLo);
98
+ const t1Lo = hLo + (bigS1Lo >>> 0) + (chLo >>> 0) + word(K, t * 2 + 1) + word(schedule, t * 2 + 1);
99
+ const t1Hi = (hHi +
100
+ (bigS1Hi >>> 0) +
101
+ (chHi >>> 0) +
102
+ word(K, t * 2) +
103
+ word(schedule, t * 2) +
104
+ Math.floor(t1Lo / 0x1_0000_0000)) >>>
105
+ 0;
106
+ const t1LoWrapped = t1Lo >>> 0;
107
+ const bigS0Hi = ((aHi >>> 28) | (aLo << 4)) ^ ((aLo >>> 2) | (aHi << 30)) ^ ((aLo >>> 7) | (aHi << 25));
108
+ const bigS0Lo = ((aLo >>> 28) | (aHi << 4)) ^ ((aHi >>> 2) | (aLo << 30)) ^ ((aHi >>> 7) | (aLo << 25));
109
+ const majHi = (aHi & bHi) ^ (aHi & cHi) ^ (bHi & cHi);
110
+ const majLo = (aLo & bLo) ^ (aLo & cLo) ^ (bLo & cLo);
111
+ const t2Lo = (bigS0Lo >>> 0) + (majLo >>> 0);
112
+ const t2Hi = ((bigS0Hi >>> 0) + (majHi >>> 0) + Math.floor(t2Lo / 0x1_0000_0000)) >>> 0;
113
+ const t2LoWrapped = t2Lo >>> 0;
114
+ hHi = gHi;
115
+ hLo = gLo;
116
+ gHi = fHi;
117
+ gLo = fLo;
118
+ fHi = eHi;
119
+ fLo = eLo;
120
+ const nextELo = dLo + t1LoWrapped;
121
+ eHi = (dHi + t1Hi + Math.floor(nextELo / 0x1_0000_0000)) >>> 0;
122
+ eLo = nextELo >>> 0;
123
+ dHi = cHi;
124
+ dLo = cLo;
125
+ cHi = bHi;
126
+ cLo = bLo;
127
+ bHi = aHi;
128
+ bLo = aLo;
129
+ const nextALo = t1LoWrapped + t2LoWrapped;
130
+ aHi = (t1Hi + t2Hi + Math.floor(nextALo / 0x1_0000_0000)) >>> 0;
131
+ aLo = nextALo >>> 0;
132
+ }
133
+ addInto(h, 0, aHi, aLo);
134
+ addInto(h, 2, bHi, bLo);
135
+ addInto(h, 4, cHi, cLo);
136
+ addInto(h, 6, dHi, dLo);
137
+ addInto(h, 8, eHi, eLo);
138
+ addInto(h, 10, fHi, fLo);
139
+ addInto(h, 12, gHi, gLo);
140
+ addInto(h, 14, hHi, hLo);
141
+ }
142
+ function addInto(h, index, hi, lo) {
143
+ const sumLo = word(h, index + 1) + lo;
144
+ h[index] = (word(h, index) + hi + Math.floor(sumLo / 0x1_0000_0000)) >>> 0;
145
+ h[index + 1] = sumLo >>> 0;
146
+ }
@@ -0,0 +1,11 @@
1
+ export { concat } from '../bytes.ts';
2
+ /** Read a little-endian `uint16`. @throws {VbaParseError} if the two bytes are not both in `buf`. */
3
+ export declare function readU16(buf: Uint8Array, at: number): number;
4
+ /** Read a little-endian `uint32`. @throws {VbaParseError} if the four bytes are not all in `buf`. */
5
+ export declare function readU32(buf: Uint8Array, at: number): number;
6
+ /**
7
+ * Decode UTF-16LE code units: the encoding [MS-CFB] uses for directory-entry names and [MS-OVBA] for
8
+ * every "Unicode" name field. A trailing odd byte is dropped: these fields are length-prefixed by the
9
+ * producer and a half code unit carries nothing to decode.
10
+ */
11
+ export declare function decodeUtf16le(bytes: Uint8Array): string;
@@ -0,0 +1,29 @@
1
+ export { concat } from '../bytes.js';
2
+ import { VbaParseError } from './errors.js';
3
+ function truncated(at, need, length) {
4
+ return new VbaParseError(`read of ${need} bytes at offset ${at} runs past the end of a ${length}-byte buffer`);
5
+ }
6
+ export function readU16(buf, at) {
7
+ const b0 = buf[at];
8
+ const b1 = buf[at + 1];
9
+ if (b0 === undefined || b1 === undefined)
10
+ throw truncated(at, 2, buf.length);
11
+ return b0 | (b1 << 8);
12
+ }
13
+ export function readU32(buf, at) {
14
+ const b0 = buf[at];
15
+ const b1 = buf[at + 1];
16
+ const b2 = buf[at + 2];
17
+ const b3 = buf[at + 3];
18
+ if (b0 === undefined || b1 === undefined || b2 === undefined || b3 === undefined) {
19
+ throw truncated(at, 4, buf.length);
20
+ }
21
+ return (b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)) >>> 0;
22
+ }
23
+ export function decodeUtf16le(bytes) {
24
+ let s = '';
25
+ for (let i = 0; i + 1 < bytes.length; i += 2) {
26
+ s += String.fromCharCode(readU16(bytes, i));
27
+ }
28
+ return s;
29
+ }
@@ -14,6 +14,6 @@ export type CfbNode = CfbStream | CfbStorage;
14
14
  * by linear scan and by tree navigation.
15
15
  *
16
16
  * @throws {VbaAuthorError} if any name is empty or exceeds 31 characters, sibling names collide, or the
17
- * project is so large it would need more than 109 FAT sectors (~7 MB far beyond any real project).
17
+ * project is so large it would need more than 109 FAT sectors (~7 MB, far beyond any real project).
18
18
  */
19
19
  export declare function writeCompoundFile(root: readonly CfbNode[]): Uint8Array;
package/dist/vba/cfb.d.ts CHANGED
@@ -8,7 +8,7 @@ export declare class CompoundFile {
8
8
  readStream(name: string): Uint8Array | undefined;
9
9
  /**
10
10
  * Reconstruct the container's top-level children as the writer's node shape, recursing into every
11
- * storage so a caller can swap one stream and re-emit the whole hierarchy with {@link writeCompoundFile}.
11
+ * storage, so a caller can swap one stream and re-emit the whole hierarchy with {@link writeCompoundFile}.
12
12
  * Walks the red-black sibling tree each storage navigates (not the linear directory scan), so any part
13
13
  * a host reaches is carried through. Cycle- and bounds-guarded like every other chain walk here.
14
14
  */
package/dist/vba/cfb.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { concat, decodeUtf16le, readU16, readU32 } from './bytes.js';
1
2
  import { VbaParseError } from './errors.js';
2
3
  const MAX_REGULAR_SECTOR = 0xfffffffa;
3
4
  const NOSTREAM = 0xffffffff;
@@ -10,7 +11,6 @@ const CFB_SIGNATURE_HI = 0xe11ab1a1;
10
11
  const DIR_ENTRY_SIZE = 128;
11
12
  export class CompoundFile {
12
13
  #buf;
13
- #view;
14
14
  #sectorSize;
15
15
  #miniSectorSize;
16
16
  #miniCutoff;
@@ -21,14 +21,13 @@ export class CompoundFile {
21
21
  #miniStream;
22
22
  constructor(buf) {
23
23
  this.#buf = buf;
24
- this.#view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
25
24
  if (buf.length < 512)
26
25
  throw new VbaParseError('compound file shorter than its 512-byte header');
27
- if (this.#u32(0) !== CFB_SIGNATURE_LO || this.#u32(4) !== CFB_SIGNATURE_HI) {
26
+ if (readU32(this.#buf, 0) !== CFB_SIGNATURE_LO || readU32(this.#buf, 4) !== CFB_SIGNATURE_HI) {
28
27
  throw new VbaParseError('not a compound file (bad OLE2 signature)');
29
28
  }
30
- const sectorShift = this.#u16(30);
31
- const miniSectorShift = this.#u16(32);
29
+ const sectorShift = readU16(this.#buf, 30);
30
+ const miniSectorShift = readU16(this.#buf, 32);
32
31
  if (sectorShift !== 9 && sectorShift !== 12) {
33
32
  throw new VbaParseError(`unsupported sector shift ${sectorShift}`);
34
33
  }
@@ -37,12 +36,12 @@ export class CompoundFile {
37
36
  this.#sectorSize = 1 << sectorShift;
38
37
  this.#miniSectorSize = 1 << miniSectorShift;
39
38
  this.#maxSector = Math.floor(buf.length / this.#sectorSize);
40
- const numFatSectors = this.#u32(44);
41
- const firstDirSector = this.#u32(48);
42
- this.#miniCutoff = this.#u32(56);
43
- const firstMiniFatSector = this.#u32(60);
44
- const firstDifatSector = this.#u32(68);
45
- const numDifatSectors = this.#u32(72);
39
+ const numFatSectors = readU32(this.#buf, 44);
40
+ const firstDirSector = readU32(this.#buf, 48);
41
+ this.#miniCutoff = readU32(this.#buf, 56);
42
+ const firstMiniFatSector = readU32(this.#buf, 60);
43
+ const firstDifatSector = readU32(this.#buf, 68);
44
+ const numDifatSectors = readU32(this.#buf, 72);
46
45
  const fatSectorIds = this.#readDifat(numFatSectors, firstDifatSector, numDifatSectors);
47
46
  this.#fat = this.#readFat(fatSectorIds);
48
47
  this.#miniFat = this.#readChainValues(firstMiniFatSector);
@@ -103,7 +102,7 @@ export class CompoundFile {
103
102
  #readDifat(numFatSectors, firstDifat, numDifat) {
104
103
  const ids = [];
105
104
  for (let i = 0; i < 109 && ids.length < numFatSectors; i++) {
106
- const v = this.#u32(76 + i * 4);
105
+ const v = readU32(this.#buf, 76 + i * 4);
107
106
  if (v >= MAX_REGULAR_SECTOR)
108
107
  break;
109
108
  ids.push(v);
@@ -117,11 +116,11 @@ export class CompoundFile {
117
116
  seen.add(sector);
118
117
  const base = this.#dataSectorOffset(sector);
119
118
  for (let i = 0; i < perSector; i++) {
120
- const v = this.#u32(base + i * 4);
119
+ const v = readU32(this.#buf, base + i * 4);
121
120
  if (v < MAX_REGULAR_SECTOR)
122
121
  ids.push(v);
123
122
  }
124
- sector = this.#u32(base + perSector * 4);
123
+ sector = readU32(this.#buf, base + perSector * 4);
125
124
  }
126
125
  return ids;
127
126
  }
@@ -131,7 +130,7 @@ export class CompoundFile {
131
130
  for (const sid of fatSectorIds) {
132
131
  const base = this.#dataSectorOffset(sid);
133
132
  for (let i = 0; i < perSector; i++)
134
- fat.push(this.#u32(base + i * 4));
133
+ fat.push(readU32(this.#buf, base + i * 4));
135
134
  }
136
135
  return fat;
137
136
  }
@@ -146,7 +145,7 @@ export class CompoundFile {
146
145
  seen.add(sector);
147
146
  const base = this.#dataSectorOffset(sector);
148
147
  for (let i = 0; i < perSector; i++)
149
- values.push(this.#u32(base + i * 4));
148
+ values.push(readU32(this.#buf, base + i * 4));
150
149
  sector = this.#nextInFat(sector);
151
150
  }
152
151
  return values;
@@ -256,39 +255,4 @@ export class CompoundFile {
256
255
  }
257
256
  return base;
258
257
  }
259
- #u16(at) {
260
- return this.#view.getUint16(at, true);
261
- }
262
- #u32(at) {
263
- return this.#view.getUint32(at, true);
264
- }
265
- }
266
- function readU16(buf, at) {
267
- return buf[at] | (buf[at + 1] << 8);
268
- }
269
- function readU32(buf, at) {
270
- return ((buf[at] |
271
- (buf[at + 1] << 8) |
272
- (buf[at + 2] << 16) |
273
- (buf[at + 3] << 24)) >>>
274
- 0);
275
- }
276
- function decodeUtf16le(bytes) {
277
- let s = '';
278
- for (let i = 0; i + 1 < bytes.length; i += 2) {
279
- s += String.fromCharCode(bytes[i] | (bytes[i + 1] << 8));
280
- }
281
- return s;
282
- }
283
- function concat(chunks) {
284
- let total = 0;
285
- for (const c of chunks)
286
- total += c.length;
287
- const out = new Uint8Array(total);
288
- let off = 0;
289
- for (const c of chunks) {
290
- out.set(c, off);
291
- off += c.length;
292
- }
293
- return out;
294
258
  }
@@ -3,7 +3,7 @@ export type Decoder = InstanceType<typeof TextDecoder>;
3
3
  export type Encoder = (text: string) => Uint8Array;
4
4
  /**
5
5
  * A `TextDecoder` for the given VBA project code page. Non-fatal (malformed bytes become U+FFFD rather
6
- * than throwing) because recovered source is for reading, not re-encoding a stray byte must not sink
6
+ * than throwing) because recovered source is for reading, not re-encoding: a stray byte must not sink
7
7
  * the whole extraction. An unknown code page falls back to windows-1252.
8
8
  */
9
9
  export declare function decoderForCodePage(codePage: number): Decoder;
@@ -12,7 +12,7 @@ export declare function decoderForCodePage(codePage: number): Decoder;
12
12
  * decoding every byte 0x00..0xFF through the matching decoder and inverting the map. This is exact for
13
13
  * the single-byte code pages a VBA project realistically declares. Under a multi-byte code page (Shift
14
14
  * JIS, GBK, …) the high bytes are lead bytes, not standalone characters, so only ASCII inverts and any
15
- * non-ASCII character is rejected fail-closed rather than emit a corrupt byte. A character the chosen
15
+ * non-ASCII character is rejected, failing closed rather than emitting a corrupt byte. A character the chosen
16
16
  * code page cannot represent throws {@link VbaAuthorError}.
17
17
  */
18
18
  export declare function encoderForCodePage(codePage: number): Encoder;