@shbernal/ts-xlsx 3.0.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 (213) 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 +4 -3
  7. package/dist/core/autofilter.js +31 -30
  8. package/dist/core/axis-handle.d.ts +2 -0
  9. package/dist/core/axis-handle.js +5 -2
  10. package/dist/core/cell.d.ts +4 -3
  11. package/dist/core/cell.js +4 -6
  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 +1 -0
  16. package/dist/core/column.js +3 -0
  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 +9 -1
  21. package/dist/core/conditional-formatting.js +34 -18
  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 +6 -0
  27. package/dist/core/data-validation.js +15 -4
  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 +13 -9
  39. package/dist/core/image.js +7 -5
  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.d.ts +5 -4
  44. package/dist/core/merge.js +18 -19
  45. package/dist/core/pivot-table.js +63 -45
  46. package/dist/core/protection.js +2 -11
  47. package/dist/core/range.js +34 -30
  48. package/dist/core/row-input.js +2 -2
  49. package/dist/core/row.d.ts +1 -0
  50. package/dist/core/row.js +3 -0
  51. package/dist/core/style.d.ts +85 -70
  52. package/dist/core/style.js +92 -28
  53. package/dist/core/table-style.d.ts +1 -1
  54. package/dist/core/table-style.js +4 -4
  55. package/dist/core/table.d.ts +27 -9
  56. package/dist/core/table.js +51 -34
  57. package/dist/core/theme.js +6 -5
  58. package/dist/core/value.d.ts +9 -0
  59. package/dist/core/value.js +36 -34
  60. package/dist/core/workbook-media.d.ts +34 -0
  61. package/dist/core/workbook-media.js +44 -0
  62. package/dist/core/workbook-styles.d.ts +67 -1
  63. package/dist/core/workbook-vba.js +3 -2
  64. package/dist/core/workbook.d.ts +23 -2
  65. package/dist/core/workbook.js +24 -40
  66. package/dist/core/worksheet-comments.d.ts +2 -1
  67. package/dist/core/worksheet-comments.js +20 -13
  68. package/dist/core/worksheet-merges.d.ts +44 -0
  69. package/dist/core/worksheet-merges.js +59 -0
  70. package/dist/core/worksheet-model.d.ts +9 -1
  71. package/dist/core/worksheet-model.js +24 -9
  72. package/dist/core/worksheet.d.ts +55 -6
  73. package/dist/core/worksheet.js +36 -44
  74. package/dist/customui/ribbon.js +2 -1
  75. package/dist/entries/core.d.ts +18 -13
  76. package/dist/entries/core.js +12 -6
  77. package/dist/entries/xlsx.d.ts +1 -1
  78. package/dist/errors.d.ts +64 -0
  79. package/dist/errors.js +22 -0
  80. package/dist/hex.d.ts +8 -0
  81. package/dist/hex.js +3 -0
  82. package/dist/io/csv/delimiter.d.ts +9 -0
  83. package/dist/io/csv/delimiter.js +6 -0
  84. package/dist/io/csv/read.js +11 -4
  85. package/dist/io/csv/write.d.ts +9 -2
  86. package/dist/io/csv/write.js +9 -38
  87. package/dist/io/opc/inflate.js +14 -0
  88. package/dist/io/opc/namespaces.d.ts +16 -0
  89. package/dist/io/opc/namespaces.js +4 -0
  90. package/dist/io/opc/part-paths.d.ts +9 -0
  91. package/dist/io/opc/part-paths.js +1 -1
  92. package/dist/io/opc/read-opc.d.ts +54 -14
  93. package/dist/io/opc/read-opc.js +42 -20
  94. package/dist/io/opc/read-options.d.ts +5 -1
  95. package/dist/io/opc/sniff-format.d.ts +3 -3
  96. package/dist/io/opc/sniff-format.js +2 -2
  97. package/dist/io/style/xf-style.d.ts +23 -1
  98. package/dist/io/style/xf-style.js +8 -0
  99. package/dist/io/xlsb/formula.d.ts +2 -2
  100. package/dist/io/xlsb/formula.js +49 -34
  101. package/dist/io/xlsb/primitives.js +8 -14
  102. package/dist/io/xlsb/ptg-functions.d.ts +19 -10
  103. package/dist/io/xlsb/ptg-functions.js +385 -596
  104. package/dist/io/xlsb/read-styles.js +45 -81
  105. package/dist/io/xlsb/read-worksheet.d.ts +20 -1
  106. package/dist/io/xlsb/read-worksheet.js +73 -89
  107. package/dist/io/xlsb/read.d.ts +6 -3
  108. package/dist/io/xlsb/read.js +44 -23
  109. package/dist/io/xlsb/record-stream.d.ts +30 -0
  110. package/dist/io/xlsb/record-stream.js +20 -0
  111. package/dist/io/xlsb/record-types.d.ts +1 -5
  112. package/dist/io/xlsb/record-types.js +1 -5
  113. package/dist/io/xlsx/cell-accumulator.d.ts +8 -1
  114. package/dist/io/xlsx/cell-accumulator.js +32 -9
  115. package/dist/io/xlsx/cell-style-resolution.d.ts +25 -0
  116. package/dist/io/xlsx/cell-style-resolution.js +29 -0
  117. package/dist/io/xlsx/cell-value.d.ts +5 -3
  118. package/dist/io/xlsx/cell-value.js +13 -15
  119. package/dist/io/xlsx/color-xml.js +3 -2
  120. package/dist/io/xlsx/column-budget.d.ts +26 -0
  121. package/dist/io/xlsx/column-budget.js +22 -0
  122. package/dist/io/xlsx/comments.d.ts +6 -2
  123. package/dist/io/xlsx/comments.js +36 -32
  124. package/dist/io/xlsx/conditional-formatting.d.ts +6 -3
  125. package/dist/io/xlsx/conditional-formatting.js +30 -26
  126. package/dist/io/xlsx/data-validation.js +29 -27
  127. package/dist/io/xlsx/edit-vba.d.ts +7 -2
  128. package/dist/io/xlsx/edit-vba.js +14 -21
  129. package/dist/io/xlsx/font-xml.d.ts +7 -0
  130. package/dist/io/xlsx/font-xml.js +104 -0
  131. package/dist/io/xlsx/hyperlinks.d.ts +37 -5
  132. package/dist/io/xlsx/hyperlinks.js +22 -13
  133. package/dist/io/xlsx/images.js +11 -13
  134. package/dist/io/xlsx/namespaces.d.ts +16 -0
  135. package/dist/io/xlsx/namespaces.js +7 -0
  136. package/dist/io/xlsx/package-plan.d.ts +14 -2
  137. package/dist/io/xlsx/package-plan.js +39 -11
  138. package/dist/io/xlsx/pivot.js +8 -7
  139. package/dist/io/xlsx/read-parts.d.ts +59 -0
  140. package/dist/io/xlsx/read-parts.js +252 -0
  141. package/dist/io/xlsx/read-pivot.js +2 -1
  142. package/dist/io/xlsx/read-repair.d.ts +26 -0
  143. package/dist/io/xlsx/read-repair.js +39 -0
  144. package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +6 -3
  145. package/dist/io/xlsx/read-rows.d.ts +18 -5
  146. package/dist/io/xlsx/read-rows.js +45 -31
  147. package/dist/io/xlsx/read-shared-strings.js +3 -2
  148. package/dist/io/xlsx/read-styles.d.ts +1 -6
  149. package/dist/io/xlsx/read-styles.js +6 -71
  150. package/dist/io/xlsx/read-workbook-xml.d.ts +36 -0
  151. package/dist/io/xlsx/read-workbook-xml.js +170 -0
  152. package/dist/io/xlsx/read-worksheet.d.ts +2 -1
  153. package/dist/io/xlsx/read-worksheet.js +35 -164
  154. package/dist/io/xlsx/read.d.ts +5 -12
  155. package/dist/io/xlsx/read.js +73 -406
  156. package/dist/io/xlsx/rich-text.js +1 -1
  157. package/dist/io/xlsx/row-position.d.ts +18 -0
  158. package/dist/io/xlsx/row-position.js +11 -0
  159. package/dist/io/xlsx/row-xml.d.ts +128 -0
  160. package/dist/io/xlsx/row-xml.js +203 -0
  161. package/dist/io/xlsx/shared-formulas.js +2 -2
  162. package/dist/io/xlsx/sheet-properties.d.ts +10 -1
  163. package/dist/io/xlsx/sheet-properties.js +145 -9
  164. package/dist/io/xlsx/style-elements.d.ts +43 -0
  165. package/dist/io/xlsx/style-elements.js +156 -0
  166. package/dist/io/xlsx/styles.d.ts +12 -3
  167. package/dist/io/xlsx/styles.js +5 -207
  168. package/dist/io/xlsx/tables.js +10 -14
  169. package/dist/io/xlsx/theme-xml.d.ts +26 -4
  170. package/dist/io/xlsx/theme-xml.js +107 -69
  171. package/dist/io/xlsx/threaded-comments.d.ts +8 -10
  172. package/dist/io/xlsx/threaded-comments.js +2 -1
  173. package/dist/io/xlsx/workbook-xml.d.ts +54 -4
  174. package/dist/io/xlsx/workbook-xml.js +60 -34
  175. package/dist/io/xlsx/worksheet-xml.d.ts +23 -70
  176. package/dist/io/xlsx/worksheet-xml.js +48 -229
  177. package/dist/io/xlsx/write-stream.d.ts +65 -16
  178. package/dist/io/xlsx/write-stream.js +50 -20
  179. package/dist/io/xlsx/write.d.ts +1 -2
  180. package/dist/io/xlsx/write.js +114 -74
  181. package/dist/rel-type.d.ts +16 -0
  182. package/dist/rel-type.js +6 -0
  183. package/dist/token-set.d.ts +13 -0
  184. package/dist/token-set.js +4 -1
  185. package/dist/vba/bytes.d.ts +14 -5
  186. package/dist/vba/bytes.js +16 -6
  187. package/dist/vba/cfb-format.d.ts +8 -0
  188. package/dist/vba/cfb-format.js +1 -0
  189. package/dist/vba/cfb-writer.d.ts +9 -0
  190. package/dist/vba/cfb-writer.js +12 -9
  191. package/dist/vba/cfb.d.ts +13 -1
  192. package/dist/vba/cfb.js +73 -51
  193. package/dist/vba/codepage.js +2 -1
  194. package/dist/vba/errors.d.ts +7 -0
  195. package/dist/vba/ms-ovba.js +51 -16
  196. package/dist/vba/project-editor.js +35 -40
  197. package/dist/vba/project.d.ts +11 -1
  198. package/dist/vba/project.js +27 -8
  199. package/dist/vba/vba-encoding.js +4 -5
  200. package/dist/xml/xml-attrs.d.ts +37 -0
  201. package/dist/xml/xml-attrs.js +30 -0
  202. package/dist/xml/xml-chars.d.ts +40 -0
  203. package/dist/xml/xml-chars.js +12 -0
  204. package/dist/xml/xml-namespaces.d.ts +31 -0
  205. package/dist/xml/xml-namespaces.js +50 -0
  206. package/dist/xml/xml-read.d.ts +72 -10
  207. package/dist/xml/xml-read.js +94 -18
  208. package/dist/xml/xml-scan.d.ts +15 -37
  209. package/dist/xml/xml-scan.js +12 -40
  210. package/dist/xml/xml.d.ts +28 -27
  211. package/dist/xml/xml.js +24 -24
  212. package/package.json +12 -7
  213. /package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +0 -0
@@ -12,7 +12,7 @@ import type { PreservedWorksheetReference } from './preserved.ts';
12
12
  import { type SheetProtection, type SheetProtectionOptions } from './protection.ts';
13
13
  import { Range } from './range.ts';
14
14
  import { Row } from './row.ts';
15
- import type { CellStyle, Color, Fill } from './style.ts';
15
+ import type { CellContent, CellStyle, Color, Fill } from './style.ts';
16
16
  import { Table, type TableOptions } from './table.ts';
17
17
  import type { CellValue } from './value.ts';
18
18
  /**
@@ -107,8 +107,13 @@ export interface RowProperties {
107
107
  /** Background fill applied to the row's cells that carry no fill of their own. */
108
108
  fill?: Fill;
109
109
  }
110
- /** One materialised cell in a {@link WorksheetModel}: its position, value, and per-cell style facets. */
111
- export interface CellModel extends CellStyle {
110
+ /**
111
+ * One materialised cell in a {@link WorksheetModel}: its position, value, note, and every facet of its
112
+ * formatting. Extends {@link CellContent} rather than {@link CellStyle} so the quote-prefix flag and
113
+ * the named-style link travel with a model round-trip: they are written and read back like any other
114
+ * facet, and leaving them off the tuple is what made a `dst.model = src.model` drop them.
115
+ */
116
+ export interface CellModel extends CellContent {
112
117
  readonly row: number;
113
118
  readonly col: number;
114
119
  value: CellValue;
@@ -177,6 +182,17 @@ export declare class Worksheet {
177
182
  * never fabricates one.
178
183
  */
179
184
  tabColor: Color | undefined;
185
+ /**
186
+ * The sheet's VBA identity (`<sheetPr codeName>`), the name a macro means by `Sheet1`. Undefined
187
+ * for a sheet in a workbook with no VBA project, which is what Excel writes for one.
188
+ *
189
+ * Preserved rather than modeled: nothing here reads it, but a `.xlsm` whose sheet code names are
190
+ * dropped on a round trip has had the binding between its macros and its sheets cut. Deliberately
191
+ * absent from {@link WorksheetModel}, which is the copy shape: a code name identifies *this* sheet
192
+ * to the workbook's VBA project, so copying it onto a second sheet would give the project two
193
+ * sheets answering to one name.
194
+ */
195
+ codeName?: string;
180
196
  /** Sheet-level format defaults. Mutate in place: `sheet.properties.defaultRowHeight = 20`. */
181
197
  readonly properties: WorksheetProperties;
182
198
  /**
@@ -267,6 +283,7 @@ export declare class Worksheet {
267
283
  *
268
284
  * @throws {SyntaxError} if the reference is unparseable, names another worksheet, or leaves an
269
285
  * axis unbounded.
286
+ * @throws {AuthoringError} if the numeric form is called with fewer than four corners.
270
287
  * @throws {RangeError} if a numeric corner is not a positive integer within the sheet's bounds.
271
288
  */
272
289
  getRange(reference: string): Range;
@@ -512,6 +529,9 @@ export declare class Worksheet {
512
529
  * shift with the rows they cover.
513
530
  *
514
531
  * @throws {RangeError} if `start` is not a positive integer or `count` is negative.
532
+ * @throws {RangeError} if an inserted row would land past the last row of the grid. The sheet is
533
+ * left untouched, so this is a refused edit rather than half of one: a region pushed off the edge
534
+ * clamps and absorbs the loss, but content pushed off it is what Excel refuses outright.
515
535
  */
516
536
  spliceRows(start: number, count: number, ...inserts: RowInput[]): void;
517
537
  /**
@@ -577,6 +597,10 @@ export declare class Worksheet {
577
597
  * indexed by row (index 0 → row 1); an empty array inserts a blank column.
578
598
  *
579
599
  * @throws {RangeError} if `start` is not a positive integer or `count` is negative.
600
+ * @throws {RangeError} if an inserted column would land past the last column, or one of its values
601
+ * past the last row. The sheet is left untouched, so this is a refused edit rather than half of
602
+ * one: a region pushed off the edge clamps and absorbs the loss, but content pushed off it is
603
+ * what Excel refuses outright, and {@link addColumn} refuses the same argument identically.
580
604
  */
581
605
  spliceColumns(start: number, count: number, ...inserts: CellValue[][]): void;
582
606
  /**
@@ -584,7 +608,8 @@ export declare class Worksheet {
584
608
  * by one. `values` is an array of values indexed by row (index 0 → row 1), like
585
609
  * {@link addColumn}. Shorthand for {@link spliceColumns}`(pos, 0, values)`.
586
610
  *
587
- * @throws {RangeError} if `pos` is not a positive integer.
611
+ * @throws {RangeError} if `pos` is not a positive integer, or if the column would land past the
612
+ * last column or one of its values past the last row; see {@link spliceColumns}.
588
613
  */
589
614
  insertColumn(pos: number, values: CellValue[]): void;
590
615
  /**
@@ -693,18 +718,42 @@ export interface WorksheetInternals {
693
718
  */
694
719
  cellAt(row: number, col: number): Cell;
695
720
  /**
696
- * The store behind a {@link Row} or {@link Column} handle. These six exist because the handles are
721
+ * The cell materialised at a position, or `undefined` when nothing has been written there.
722
+ *
723
+ * Merge-blind and non-materialising, which is what separates it from `Worksheet.getCell`: that
724
+ * resolves a covered address to its merge master, so `hasCell(row, col)` followed by a `getCell` of
725
+ * the same position can hand back a *different* cell. A caller walking a rectangle got the master
726
+ * once per covered position and never saw the covered cells at all, which made a `clearStyle` over a
727
+ * merge clear the master repeatedly and the covered cells not once. A caller that wants merge
728
+ * resolution keeps `getCell`; a caller enumerating what is actually stored wants this.
729
+ */
730
+ peekCell(row: number, col: number): Cell | undefined;
731
+ /**
732
+ * {@link cellAt} with merge resolution: the cell a write to this position lands on, materialised.
733
+ *
734
+ * What {@link Worksheet.getCell} does, minus the address. `getCell` is the public spelling and takes
735
+ * an A1 reference, so a caller already holding a row and a column had to encode one and have it
736
+ * decoded straight back. That round-trip costs about three times a positional read, and a
737
+ * {@link Range} write performs one per cell: styling a 100x100 block spent 10,000 encodes and 10,000
738
+ * regex decodes on positions it already had.
739
+ */
740
+ masterAt(row: number, col: number): Cell;
741
+ /**
742
+ * The store behind a {@link Row} or {@link Column} handle. These exist because the handles are
697
743
  * views rather than records: they hold a sheet and a position, and every read and write goes
698
744
  * through here to the one authoritative map. `…PropertiesOf` never fabricates, so reading a row
699
745
  * cannot extend the used range; `ensure…` is what a write calls, so the record appears exactly
700
- * when a value is set.
746
+ * when a value is set; `drop…` is what clearing the last field calls, so the record disappears
747
+ * exactly when the line stops being formatted, which is what keeps the used range honest.
701
748
  */
702
749
  rowPropertiesOf(number: number): RowProperties | undefined;
703
750
  ensureRowProperties(number: number): RowProperties;
751
+ dropRowProperties(number: number): void;
704
752
  /** The row's materialised cells in ascending column order. */
705
753
  rowCells(number: number): Cell[];
706
754
  columnPropertiesOf(index: number): ColumnProperties | undefined;
707
755
  ensureColumnProperties(index: number): ColumnProperties;
756
+ dropColumnProperties(index: number): void;
708
757
  /** The column's materialised cells in ascending row order. */
709
758
  columnCells(index: number): Cell[];
710
759
  }
@@ -1,6 +1,6 @@
1
1
  import { AuthoringError } from '../errors.js';
2
2
  import { tokenSet } from '../token-set.js';
3
- import { decodeCellRef, decodeRange, encodeAddress, tryDecodeCellRef } from './address.js';
3
+ import { assertAxisInBounds, decodeCellRef, encodeAddress, tryDecodeCellRef } from './address.js';
4
4
  import { canonicalizeAutoFilter } from './autofilter.js';
5
5
  import { applyCellStyle, Cell, copyCellContent } from './cell.js';
6
6
  import { Column } from './column.js';
@@ -8,7 +8,6 @@ import { ConditionalFormattingOverlay } from './conditional-formatting-overlay.j
8
8
  import { DataValidationOverlay } from './data-validation-overlay.js';
9
9
  import { GridEdits } from './grid-edits.js';
10
10
  import { INTERNAL } from './internal.js';
11
- import { MergeIndex } from './merge-index.js';
12
11
  import { clearCoveredValues } from './merge.js';
13
12
  import { PivotTable } from './pivot-table.js';
14
13
  import { deriveCredential, } from './protection.js';
@@ -18,6 +17,7 @@ import { Row } from './row.js';
18
17
  import { Table } from './table.js';
19
18
  import { UsedExtent } from './used-extent.js';
20
19
  import { WorksheetComments } from './worksheet-comments.js';
20
+ import { WorksheetMerges } from './worksheet-merges.js';
21
21
  import { WORKSHEET_MODEL_FACETS } from './worksheet-model.js';
22
22
  import { WorksheetPictures } from './worksheet-pictures.js';
23
23
  export const isVisibility = tokenSet({ visible: true, hidden: true, veryHidden: true });
@@ -26,6 +26,7 @@ export class Worksheet {
26
26
  id;
27
27
  state;
28
28
  tabColor;
29
+ codeName;
29
30
  properties = {};
30
31
  outline = {};
31
32
  view = {};
@@ -42,37 +43,33 @@ export class Worksheet {
42
43
  #pivotTables = [];
43
44
  #loadedPivotTables = [];
44
45
  #comments = new WorksheetComments(() => this.name);
45
- #merges = [];
46
+ #merges = new WorksheetMerges();
46
47
  #images = new WorksheetPictures({
47
48
  columnWidth: (col) => this.#columns.get(col + 1)?.width ?? this.properties.defaultColWidth,
48
49
  rowHeight: (row) => this.#rowProperties.get(row + 1)?.height ?? this.properties.defaultRowHeight,
49
50
  });
50
51
  #preservedReferences = [];
51
- #mergeRects = [];
52
52
  #dataValidations = new DataValidationOverlay();
53
53
  #conditionalFormattings = new ConditionalFormattingOverlay();
54
54
  #protection;
55
55
  #autoFilter;
56
56
  #edits;
57
57
  #extent;
58
- #mergeIndex;
59
58
  constructor(name, id, state = 'visible') {
60
59
  this.name = name;
61
60
  this.id = id;
62
61
  this.state = state;
63
- this.#mergeIndex = new MergeIndex(this.#mergeRects);
64
62
  this.#extent = new UsedExtent({
65
63
  rows: this.#rows,
66
64
  rowProperties: this.#rowProperties,
67
65
  columns: this.#columns,
68
- mergeRects: this.#mergeRects,
66
+ mergeRects: this.#merges.rects,
69
67
  });
70
68
  this.#edits = new GridEdits({
71
69
  rows: this.#rows,
72
70
  rowProperties: this.#rowProperties,
73
71
  columns: this.#columns,
74
72
  merges: this.#merges,
75
- mergeRects: this.#mergeRects,
76
73
  tables: this.#tables,
77
74
  images: this.#images.anchors,
78
75
  dataValidations: this.#dataValidations,
@@ -88,8 +85,7 @@ export class Worksheet {
88
85
  }
89
86
  getCell(reference) {
90
87
  const { col, row } = decodeCellRef(reference);
91
- const master = this.#mergeIndex.masterOf(row, col);
92
- return this.#cellAt(master.row, master.col);
88
+ return this[INTERNAL].masterAt(row, col);
93
89
  }
94
90
  hasCell(row, col) {
95
91
  return this.#rows.get(row)?.has(col) ?? false;
@@ -103,7 +99,11 @@ export class Worksheet {
103
99
  getRange(referenceOrTop, left, bottom, right) {
104
100
  if (typeof referenceOrTop === 'string')
105
101
  return rangeFrom(this, referenceOrTop);
106
- return new Range(this, referenceOrTop, left ?? 0, bottom ?? 0, right ?? 0);
102
+ const corners = [left, bottom, right].filter((corner) => corner !== undefined).length + 1;
103
+ if (left === undefined || bottom === undefined || right === undefined) {
104
+ throw new AuthoringError(`getRange(top, left, bottom, right) needs all four corners; got ${corners}`);
105
+ }
106
+ return new Range(this, referenceOrTop, left, bottom, right);
107
107
  }
108
108
  get rowCount() {
109
109
  return this.#extent.lastRow;
@@ -209,21 +209,14 @@ export class Worksheet {
209
209
  return this.#preservedReferences;
210
210
  }
211
211
  mergeCells(range) {
212
- const { top, left, bottom, right } = decodeRange(range);
213
- if (top !== undefined && left !== undefined && bottom !== undefined && right !== undefined) {
214
- const rect = { top, left, bottom, right };
215
- if (this.#mergeIndex.overlapping(rect) !== undefined) {
216
- throw new AuthoringError(`merged range "${range}" overlaps an existing merged region`);
217
- }
218
- this.#mergeRects.push(rect);
219
- this.#mergeIndex.note(rect);
212
+ const rect = this.#merges.add(range);
213
+ if (rect !== undefined) {
220
214
  this.#extent.noteMerge(rect);
221
215
  clearCoveredValues(this.#rows, rect);
222
216
  }
223
- this.#merges.push(range);
224
217
  }
225
218
  get merges() {
226
- return this.#merges;
219
+ return this.#merges.ranges;
227
220
  }
228
221
  get autoFilter() {
229
222
  return this.#autoFilter;
@@ -232,20 +225,10 @@ export class Worksheet {
232
225
  this.#autoFilter = filter === undefined ? undefined : canonicalizeAutoFilter(filter);
233
226
  }
234
227
  unmergeCells(range) {
235
- const index = this.#merges.indexOf(range);
236
- if (index === -1)
237
- return false;
238
- this.#merges.splice(index, 1);
239
- const { top, left, bottom, right } = decodeRange(range);
240
- if (top !== undefined && left !== undefined && bottom !== undefined && right !== undefined) {
241
- const rectIndex = this.#mergeRects.findIndex((r) => r.top === top && r.left === left && r.bottom === bottom && r.right === right);
242
- if (rectIndex !== -1) {
243
- this.#mergeRects.splice(rectIndex, 1);
244
- this.#mergeIndex.invalidate();
245
- this.#extent.invalidate();
246
- }
247
- }
248
- return true;
228
+ const { existed, rectsChanged } = this.#merges.remove(range);
229
+ if (rectsChanged)
230
+ this.#extent.invalidate();
231
+ return existed;
249
232
  }
250
233
  addDataValidation(sqref, rule, options = {}) {
251
234
  this.#dataValidations.add(sqref, rule, options);
@@ -267,7 +250,7 @@ export class Worksheet {
267
250
  assertStartAndCount('splice', 'row', start, count);
268
251
  const inserted = inserts.map((values, i) => buildRowCells(start + i, values, this.#columns));
269
252
  this.#edits.spliceRows(start, count, inserted);
270
- this.#mergeIndex.invalidate();
253
+ this.#merges.invalidate();
271
254
  this.#extent.invalidate();
272
255
  }
273
256
  insertRow(pos, values) {
@@ -331,7 +314,7 @@ export class Worksheet {
331
314
  if (insert) {
332
315
  const copies = Array.from({ length: count }, () => snapshot(start));
333
316
  this.#edits.spliceRows(start + 1, 0, copies);
334
- this.#mergeIndex.invalidate();
317
+ this.#merges.invalidate();
335
318
  }
336
319
  else {
337
320
  for (let i = 1; i <= count; i++)
@@ -344,7 +327,7 @@ export class Worksheet {
344
327
  spliceColumns(start, count, ...inserts) {
345
328
  assertStartAndCount('splice', 'column', start, count);
346
329
  this.#edits.spliceColumns(start, count, inserts);
347
- this.#mergeIndex.invalidate();
330
+ this.#merges.invalidate();
348
331
  this.#extent.invalidate();
349
332
  }
350
333
  insertColumn(pos, values) {
@@ -374,12 +357,10 @@ export class Worksheet {
374
357
  this.#rows.clear();
375
358
  this.#columns.clear();
376
359
  this.#rowProperties.clear();
377
- this.#merges.length = 0;
378
- this.#mergeRects.length = 0;
360
+ this.#merges.clear();
379
361
  this.#dataValidations.clear();
380
362
  this.#conditionalFormattings.clear();
381
363
  this.#tables.length = 0;
382
- this.#mergeIndex.invalidate();
383
364
  this.#extent.reset();
384
365
  }
385
366
  set model(model) {
@@ -436,6 +417,11 @@ export class Worksheet {
436
417
  this.#protection = protection;
437
418
  },
438
419
  cellAt: (row, col) => this.#cellAt(row, col),
420
+ masterAt: (row, col) => {
421
+ const master = this.#merges.masterOf(row, col);
422
+ return this.#cellAt(master.row, master.col);
423
+ },
424
+ peekCell: (row, col) => this.#rows.get(row)?.get(col),
439
425
  rowPropertiesOf: (number) => this.#rowProperties.get(number),
440
426
  ensureRowProperties: (number) => {
441
427
  let properties = this.#rowProperties.get(number);
@@ -446,6 +432,10 @@ export class Worksheet {
446
432
  this.#extent.noteDeclaredRow(number);
447
433
  return properties;
448
434
  },
435
+ dropRowProperties: (number) => {
436
+ if (this.#rowProperties.delete(number))
437
+ this.#extent.invalidate();
438
+ },
449
439
  rowCells: (number) => {
450
440
  const cols = this.#rows.get(number);
451
441
  return cols ? [...cols].sort(([a], [b]) => a - b).map(([, cell]) => cell) : [];
@@ -460,6 +450,10 @@ export class Worksheet {
460
450
  this.#extent.noteDeclaredColumn(index);
461
451
  return properties;
462
452
  },
453
+ dropColumnProperties: (index) => {
454
+ if (this.#columns.delete(index))
455
+ this.#extent.invalidate();
456
+ },
463
457
  columnCells: (index) => {
464
458
  const cells = [];
465
459
  for (const number of [...this.#rows.keys()].sort((a, b) => a - b)) {
@@ -472,9 +466,7 @@ export class Worksheet {
472
466
  };
473
467
  }
474
468
  function assertStartAndCount(verb, axis, start, count) {
475
- if (!Number.isInteger(start) || start < 1) {
476
- throw new RangeError(`${verb} start ${start} is out of bounds: ${axis}s start at 1`);
477
- }
469
+ assertAxisInBounds(axis, start);
478
470
  if (!Number.isInteger(count) || count < 0) {
479
471
  throw new RangeError(`${verb} count ${count} is invalid: it must be a non-negative integer`);
480
472
  }
@@ -1,4 +1,5 @@
1
1
  import { strFromU8 } from 'fflate';
2
+ import { isRelType } from '../rel-type.js';
2
3
  import { tokenSet } from '../token-set.js';
3
4
  import { boolStrict, localName, xmlEvents } from '../xml/xml-scan.js';
4
5
  import { CustomUiParseError } from './errors.js';
@@ -7,7 +8,7 @@ export const CUSTOMUI_2009_NAMESPACE = 'http://schemas.microsoft.com/office/2009
7
8
  export const CUSTOMUI_2007_REL_TYPE = 'http://schemas.microsoft.com/office/2006/relationships/ui/extensibility';
8
9
  export const CUSTOMUI_2010_REL_TYPE = 'http://schemas.microsoft.com/office/2007/relationships/ui/extensibility';
9
10
  export function isCustomUiRelType(type) {
10
- return type.endsWith('/ui/extensibility');
11
+ return isRelType(type, 'ui/extensibility');
11
12
  }
12
13
  const MAX_DEPTH = 256;
13
14
  const isKnownControlKind = tokenSet({
@@ -1,25 +1,30 @@
1
- export { type CellAddress, columnToNumber, decodeAddress, decodeRange, encodeAddress, type GridRect, MAX_COLUMN, MAX_ROW, numberToColumn, type RangeAddress, } from '../core/address.ts';
2
- export type { AutoFilter, CustomFilter, CustomFilterOperator, CustomFilterPredicate, FilterColumn, FilterCriteria, ValuesFilter, } from '../core/autofilter.ts';
1
+ export { type CellAddress, type CellPosition, columnToNumber, decodeAddress, decodeRange, encodeAddress, type GridRect, MAX_COLUMN, MAX_ROW, numberToColumn, type RangeAddress, } from '../core/address.ts';
2
+ export { type AutoFilter, type CustomFilter, type CustomFilterOperator, type CustomFilterPredicate, type FilterColumn, type FilterCriteria, isCustomFilterOperator, type ValuesFilter, } from '../core/autofilter.ts';
3
3
  export { Cell } from '../core/cell.ts';
4
4
  export { applyTint, type ColorResolutionContext, DEFAULT_INDEXED_COLORS, resolveColor, SYSTEM_INDEXED_COLORS, } from '../core/color-resolution.ts';
5
+ export { AxisHandle } from '../core/axis-handle.ts';
5
6
  export { Column } from '../core/column.ts';
7
+ export type { DateEpoch } from '../core/date.ts';
6
8
  export type { Comment, CommentThread, Mention, MentionRef, Person } from '../core/comment-thread.ts';
7
- export type { CfTimePeriod, CfValueObject, CfValueObjectType, ConditionalFormatting, ConditionalFormattingOperator, ConditionalFormattingRule, ConditionalFormattingType, IconSetType, } from '../core/conditional-formatting.ts';
8
- export type { DataValidation, DataValidationEntry, DataValidationErrorStyle, DataValidationOperator, DataValidationType, } from '../core/data-validation.ts';
9
- export { type AnchoredImage, type AnchorPoint, type Extent, type ImageAnchor, type ImageEditAs, isOneCellAnchor, type OneCellAnchor, type PortableImage, PX_TO_EMU, type TwoCellAnchor, type WorkbookImage, type WorksheetImages, } from '../core/image.ts';
10
- export { MAX_COLUMN_WIDTH, MAX_ROW_HEIGHT } from '../core/limits.ts';
11
- export type { HeaderFooter, PageBreak, PageMargins, PageOrder, PageOrientation, PageSetup, PrintOptions, } from '../core/page-setup.ts';
12
- export { type ParsedPivotField, type ParsedPivotSource, type ParsedPivotTable, type PivotCacheField, type PivotItem, type PivotMetric, type PivotNumericSummary, type PivotRecordCell, type PivotSourceKind, PivotTable, type PivotTableOptions, } from '../core/pivot-table.ts';
9
+ export { type CfTimePeriod, type CfValueObject, type CfValueObjectType, type ConditionalFormatting, type ConditionalFormattingOperator, type ConditionalFormattingRule, type ConditionalFormattingType, type IconSetType, isCfTimePeriod, isCfValueObjectType, isConditionalFormattingOperator, isConditionalFormattingType, isIconSetType, } from '../core/conditional-formatting.ts';
10
+ export { type DataValidation, type DataValidationEntry, type DataValidationErrorStyle, type DataValidationOperator, type DataValidationType, isDataValidationErrorStyle, isDataValidationOperator, isDataValidationType, } from '../core/data-validation.ts';
11
+ export type { AxisSplice } from '../core/grid-shift.ts';
12
+ export { type AnchoredImage, type AnchorPoint, type Extent, type ImageAnchor, type ImageEditAs, isImageEditAs, isOneCellAnchor, type OneCellAnchor, type PortableImage, PX_TO_EMU, type TwoCellAnchor, type WorkbookImage, type WorksheetImages, } from '../core/image.ts';
13
+ export type { ReadPackageOptions } from '../io/opc/read-options.ts';
14
+ export { INVALID_SHEET_NAME_CHARS, MAX_COLUMN_WIDTH, MAX_ROW_HEIGHT, MAX_SHEET_NAME_LENGTH, MAX_TABLE_NAME_LENGTH, TABLE_NAME_PATTERN, } from '../core/limits.ts';
15
+ export { type HeaderFooter, isPageOrder, isPageOrientation, type PageBreak, type PageMargins, type PageOrder, type PageOrientation, type PageSetup, type PrintOptions, } from '../core/page-setup.ts';
16
+ export { type ParsedPivotField, type ParsedPivotSource, type ParsedPivotTable, type PivotCacheField, type PivotItem, type PivotMetric, type PivotNumericSummary, isDeclarablePivotSourceKind, type PivotRecordCell, type PivotSourceKind, PivotTable, type PivotTableOptions, } from '../core/pivot-table.ts';
13
17
  export type { PreservedPart, PreservedRelationship, PreservedRootReference, PreservedWorksheetReference, } from '../core/preserved.ts';
14
18
  export type { SheetProtection, SheetProtectionCredential, SheetProtectionFlags, SheetProtectionOptions, } from '../core/protection.ts';
15
19
  export { Range } from '../core/range.ts';
16
20
  export { Row } from '../core/row.ts';
17
- export type { Alignment, Border, BorderEdge, BorderStyle, CellStyle, Color, DifferentialStyle, Fill, FillPatternType, Font, FontScheme, FontVerticalAlignment, GradientFill, GradientStop, HorizontalAlignment, NamedCellStyle, PatternFill, Protection, TableStyleNamespace, TableStyleTable, UnderlineStyle, VerticalAlignment, } from '../core/style.ts';
18
- export { Table, type TableColumn, type TableColumnStyle, type TableOptions, type TableRegion, type TableStyleInfo, } from '../core/table.ts';
21
+ export { type Alignment, type Border, type BorderEdge, type BorderStyle, type CellContent, type CellStyle, type Color, type Fill, type FillPatternType, type Font, type FontScheme, type FontVerticalAlignment, type GradientFill, type GradientStop, type HorizontalAlignment, isBorderStyle, isFillPatternType, isFontScheme, isFontVerticalAlignment, isHorizontalAlignment, isNamedUnderlineStyle, isVerticalAlignment, type PatternFill, type Protection, type UnderlineStyle, type VerticalAlignment, } from '../core/style.ts';
22
+ export { Table, type TableColumn, type TableColumnStyle, isTotalsRowFunction, type TableGrid, type TableOptions, type TableRegion, type TableStyleInfo, type TotalsRowFunction, } from '../core/table.ts';
23
+ export type { DifferentialStyle, NamedCellStyle, TableStyleNamespace, TableStyleTable, } from '../core/workbook-styles.ts';
19
24
  export { isTableStyleElementType, STRIPE_ELEMENT_TYPES, TABLE_STYLE_ELEMENT_TYPES, type TableStyle, type TableStyleElement, type TableStyleElementType, } from '../core/table-style.ts';
20
25
  export { estimateWrappedLines } from '../core/text-metrics.ts';
21
- export { DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, THEME_COLOR_SLOTS, type ThemeColorScheme, type ThemeColorSlot, type ThemeFontScheme, type ThemeOverrides, } from '../core/theme.ts';
26
+ export { DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, isThemeColorSlot, THEME_COLOR_SLOTS, type ThemeColorScheme, type ThemeColorSlot, type ThemeFontScheme, type ThemeOverrides, } from '../core/theme.ts';
22
27
  export { type CellValue, cellValueToText, coerceCellValue, type DataTableFormulaValue, detectValueType, ERROR_CODES, type ErrorCode, type ErrorValue, type FormulaResult, type FormulaValue, type HyperlinkValue, isDataTableFormulaValue, isErrorCode, isErrorValue, isFormulaValue, isHyperlinkValue, isRichTextValue, isSharedFormulaValue, type RichTextRun, type RichTextValue, richTextToPlain, type SharedFormulaValue, ValueType, } from '../core/value.ts';
23
- export { type AddImageOptions, type AddWorksheetOptions, DEFAULT_WORKBOOK_VIEW, type DefinedName, type PreservedWorkbookReference, Workbook, type WorkbookProperties, type WorkbookView, } from '../core/workbook.ts';
28
+ export { type AddImageOptions, type AddWorksheetOptions, DEFAULT_WORKBOOK_VIEW, type DefinedName, type PreservedTheme, type PreservedWorkbookReference, Workbook, type WorkbookProperties, type WorkbookView, } from '../core/workbook.ts';
24
29
  export type { WorkbookProtection, WorkbookProtectionCredentialAttr, } from '../core/workbook-protection.ts';
25
- export { type CellModel, type ColumnProperties, type OutlineProperties, type RowInput, type RowProperties, type SheetView, type Visibility, Worksheet, type WorksheetModel, type WorksheetProperties, type WorksheetState, } from '../core/worksheet.ts';
30
+ export { type CellModel, type ColumnProperties, type OutlineProperties, type RowInput, type RowProperties, type SheetView, isVisibility, type Visibility, Worksheet, type WorksheetModel, type WorksheetProperties, type WorksheetState, } from '../core/worksheet.ts';
@@ -1,16 +1,22 @@
1
1
  export { columnToNumber, decodeAddress, decodeRange, encodeAddress, MAX_COLUMN, MAX_ROW, numberToColumn, } from '../core/address.js';
2
+ export { isCustomFilterOperator, } from '../core/autofilter.js';
2
3
  export { Cell } from '../core/cell.js';
3
4
  export { applyTint, DEFAULT_INDEXED_COLORS, resolveColor, SYSTEM_INDEXED_COLORS, } from '../core/color-resolution.js';
5
+ export { AxisHandle } from '../core/axis-handle.js';
4
6
  export { Column } from '../core/column.js';
5
- export { isOneCellAnchor, PX_TO_EMU, } from '../core/image.js';
6
- export { MAX_COLUMN_WIDTH, MAX_ROW_HEIGHT } from '../core/limits.js';
7
- export { PivotTable, } from '../core/pivot-table.js';
7
+ export { isCfTimePeriod, isCfValueObjectType, isConditionalFormattingOperator, isConditionalFormattingType, isIconSetType, } from '../core/conditional-formatting.js';
8
+ export { isDataValidationErrorStyle, isDataValidationOperator, isDataValidationType, } from '../core/data-validation.js';
9
+ export { isImageEditAs, isOneCellAnchor, PX_TO_EMU, } from '../core/image.js';
10
+ export { INVALID_SHEET_NAME_CHARS, MAX_COLUMN_WIDTH, MAX_ROW_HEIGHT, MAX_SHEET_NAME_LENGTH, MAX_TABLE_NAME_LENGTH, TABLE_NAME_PATTERN, } from '../core/limits.js';
11
+ export { isPageOrder, isPageOrientation, } from '../core/page-setup.js';
12
+ export { isDeclarablePivotSourceKind, PivotTable, } from '../core/pivot-table.js';
8
13
  export { Range } from '../core/range.js';
9
14
  export { Row } from '../core/row.js';
10
- export { Table, } from '../core/table.js';
15
+ export { isBorderStyle, isFillPatternType, isFontScheme, isFontVerticalAlignment, isHorizontalAlignment, isNamedUnderlineStyle, isVerticalAlignment, } from '../core/style.js';
16
+ export { Table, isTotalsRowFunction, } from '../core/table.js';
11
17
  export { isTableStyleElementType, STRIPE_ELEMENT_TYPES, TABLE_STYLE_ELEMENT_TYPES, } from '../core/table-style.js';
12
18
  export { estimateWrappedLines } from '../core/text-metrics.js';
13
- export { DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, THEME_COLOR_SLOTS, } from '../core/theme.js';
19
+ export { DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, isThemeColorSlot, THEME_COLOR_SLOTS, } from '../core/theme.js';
14
20
  export { cellValueToText, coerceCellValue, detectValueType, ERROR_CODES, isDataTableFormulaValue, isErrorCode, isErrorValue, isFormulaValue, isHyperlinkValue, isRichTextValue, isSharedFormulaValue, richTextToPlain, ValueType, } from '../core/value.js';
15
21
  export { DEFAULT_WORKBOOK_VIEW, Workbook, } from '../core/workbook.js';
16
- export { Worksheet, } from '../core/worksheet.js';
22
+ export { isVisibility, Worksheet, } from '../core/worksheet.js';
@@ -1,5 +1,5 @@
1
1
  export { editXlsxVbaAddReference, editXlsxVbaRemoveModule } from '../io/xlsx/edit-vba.ts';
2
- export { type ReadXlsxOptions, readXlsx } from '../io/xlsx/read.ts';
2
+ export { readXlsx } from '../io/xlsx/read.ts';
3
3
  export { type ReadSheetRowsOptions, readSheetRows, readWorkbookStream, } from '../io/xlsx/read-rows.ts';
4
4
  export { DEFAULT_THEME_XML, parseThemeColorScheme, parseThemeFontScheme, } from '../io/xlsx/theme-xml.ts';
5
5
  export { type WriteOptions, writeXlsx, writeXlsxAsync } from '../io/xlsx/write.ts';
package/dist/errors.d.ts CHANGED
@@ -77,3 +77,67 @@ export declare class InternalError extends XlsxError {
77
77
  readonly code = "internal";
78
78
  constructor(message?: string, options?: ErrorOptions);
79
79
  }
80
+ /**
81
+ * A name as it should appear inside an error message: quoted, and unambiguous whatever it contains.
82
+ *
83
+ * Almost every message this library throws names something the caller or the file chose - a sheet, a
84
+ * table, a defined name, a module stream, a part path - and the tree had grown three ways of setting
85
+ * that name off from the prose, split by directory rather than by intent: `"..."`, `'...'`, and
86
+ * `JSON.stringify`. Only the third survives a name that itself contains a quote, which is exactly the
87
+ * class of name a spreadsheet permits: a sheet called `Q1 "draft"` renders under either literal
88
+ * spelling as a message whose reader cannot tell where the name ends. It also renders a name
89
+ * containing a newline or a zero-width character as one that looks identical to a name that does not,
90
+ * which for a library whose input is untrusted is the difference between a diagnostic and a decoy.
91
+ *
92
+ * So: one spelling, and the one that escapes. `JSON.stringify` is the whole implementation - the
93
+ * point is not the algorithm but that every throw site reaches the same one.
94
+ *
95
+ * Not exported from the `/errors` entry: this is how messages are written, not part of the taxonomy
96
+ * a caller catches.
97
+ */
98
+ export declare function quoted(name: string): string;
99
+ /**
100
+ * The one message for "that token is not in the enumeration", owned here because both sides of a
101
+ * layering boundary throw it.
102
+ *
103
+ * `checkedToken` in `src/xml/xml.ts` is the general form, and `core/` may not import a serialisation
104
+ * to reach it. The response to that constraint had been to transcribe the sentence into
105
+ * `core/table-style.ts` character for character, which is a copy that no gate can see is a copy. This
106
+ * module is the one place below both of them, so the template lives here and neither side owns it.
107
+ */
108
+ export declare function invalidToken(kind: string, value: string): AuthoringError;
109
+ /**
110
+ * Refuse a number OOXML cannot spell. Every numeric attribute in the format is `xsd:double`,
111
+ * `xsd:unsignedInt` or a bounded flavour of one, and none of those lexical spaces has a form for a
112
+ * NaN or an infinity, so a value that reaches the file as `NaN` is a package Excel reports as
113
+ * damaged. Neither has the formula grammar, which is why the guard is here rather than in the XML
114
+ * serialiser: the two spellings of a number this library writes -- an attribute's and a formula
115
+ * literal's -- sit on opposite sides of the `core`/`xml` boundary and refuse the same values.
116
+ *
117
+ * Exported for the callers that must refuse before they write. A number can be unwritable and still
118
+ * be read on the way to the bytes: compared, summed, walked. A comparison against `NaN` or an
119
+ * infinity silently takes the wrong branch long before the value would have been serialised.
120
+ *
121
+ * @throws {AuthoringError} naming the value.
122
+ */
123
+ export declare function assertWritableNumber(value: number): void;
124
+ /** `U+0001`-style spelling of a code point, for an escape body or an error message. */
125
+ export declare function codePointHex(codePoint: number): string;
126
+ /**
127
+ * The error for a string carrying a character the target format cannot encode, or `undefined` when
128
+ * it carries none. Naming the code point and its offset is the whole value of the message: these
129
+ * strings arrive from a database column or a CSV field, not from a literal the author can see.
130
+ *
131
+ * Two formats refuse characters for unrelated reasons -- XML 1.0's `Char` production, and UTF-8's
132
+ * inability to encode a lone surrogate -- and each had grown its own copy of the rendering, which
133
+ * had already drifted: the CSV one omitted the zero padding, invisibly, because a surrogate is
134
+ * always four digits and nothing narrower had been added yet. The *patterns* stay beside their
135
+ * consumers, which is where they are understood; only the spelling and the message skeleton are
136
+ * shared, which is where they were wrong.
137
+ *
138
+ * `pattern` must not carry the `g` flag: a stateful `lastIndex` would make the same string answer
139
+ * differently on a second call.
140
+ *
141
+ * @param why the clause explaining the refusal, appended after the offset.
142
+ */
143
+ export declare function unrepresentable(text: string, pattern: RegExp, why: string): AuthoringError | undefined;
package/dist/errors.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { hex } from './hex.js';
1
2
  export class XlsxError extends Error {
2
3
  }
3
4
  export class AuthoringError extends XlsxError {
@@ -13,3 +14,24 @@ export class InternalError extends XlsxError {
13
14
  super(message === undefined ? REPORT_NOTICE : `${message}\n\n${REPORT_NOTICE}`, options);
14
15
  }
15
16
  }
17
+ export function quoted(name) {
18
+ return JSON.stringify(name);
19
+ }
20
+ export function invalidToken(kind, value) {
21
+ return new AuthoringError(`Invalid ${kind} ${quoted(value)}: not a value the OOXML enumeration allows`);
22
+ }
23
+ export function assertWritableNumber(value) {
24
+ if (Number.isFinite(value))
25
+ return;
26
+ throw new AuthoringError(`cannot write a non-finite number (${value}): it has no OOXML representation`);
27
+ }
28
+ export function codePointHex(codePoint) {
29
+ return hex(codePoint, 4);
30
+ }
31
+ export function unrepresentable(text, pattern, why) {
32
+ const found = pattern.exec(text);
33
+ if (found === null)
34
+ return undefined;
35
+ const codePoint = text.codePointAt(found.index);
36
+ return new AuthoringError(`cannot write U+${codePointHex(codePoint)} at offset ${found.index}: ${why}`);
37
+ }
package/dist/hex.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `value` in uppercase hexadecimal, zero-padded to at least `digits`.
3
+ *
4
+ * Uppercase because five of the six sites this replaced were, because OOXML writes `ARGB` values that
5
+ * way, and because a hex digit read by a human beside a decimal number is easier to tell apart when
6
+ * it is not lowercase. A value wider than `digits` is not truncated: padding is a minimum.
7
+ */
8
+ export declare function hex(value: number, digits: number): string;
package/dist/hex.js ADDED
@@ -0,0 +1,3 @@
1
+ export function hex(value, digits) {
2
+ return value.toString(16).toUpperCase().padStart(digits, '0');
3
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Refuse a delimiter the codec cannot honour.
3
+ *
4
+ * A `RangeError` rather than an `AuthoringError`: this is a single scalar out of range, which
5
+ * `src/errors.ts` reserves for the native types on the grounds that they exist for exactly that.
6
+ *
7
+ * @throws {RangeError} if the delimiter is not exactly one character.
8
+ */
9
+ export declare function assertDelimiter(delimiter: string): void;
@@ -0,0 +1,6 @@
1
+ import { quoted } from '../../errors.js';
2
+ export function assertDelimiter(delimiter) {
3
+ if (delimiter.length !== 1) {
4
+ throw new RangeError(`CSV delimiter must be a single character, got ${quoted(delimiter)}`);
5
+ }
6
+ }
@@ -1,10 +1,9 @@
1
1
  import { Workbook } from '../../core/workbook.js';
2
+ import { assertDelimiter } from './delimiter.js';
2
3
  export function readCsv(input, options = {}) {
3
4
  const text = stripBom(typeof input === 'string' ? input : new TextDecoder('utf-8', { ignoreBOM: true }).decode(input));
4
5
  const delimiter = options.delimiter ?? ',';
5
- if (delimiter.length !== 1) {
6
- throw new RangeError(`CSV delimiter must be a single character, got ${JSON.stringify(delimiter)}`);
7
- }
6
+ assertDelimiter(delimiter);
8
7
  let rows = parseCsvRows(text, delimiter);
9
8
  if (options.headers)
10
9
  rows = rows.slice(1);
@@ -52,7 +51,15 @@ function parseCsvRows(text, delimiter) {
52
51
  row = [];
53
52
  field = '';
54
53
  }
55
- else if (ch !== '\r') {
54
+ else if (ch === '\r') {
55
+ if (text[i + 1] !== '\n') {
56
+ row.push(field);
57
+ rows.push(row);
58
+ row = [];
59
+ field = '';
60
+ }
61
+ }
62
+ else {
56
63
  field += ch;
57
64
  }
58
65
  }
@@ -18,8 +18,15 @@ export interface CsvWriteOptions {
18
18
  readonly delimiter?: string;
19
19
  /** Line separator between rows; defaults to `"\n"`. */
20
20
  readonly rowDelimiter?: string;
21
- /** A token format (e.g. `"MM/DD/YYYY"`) for Date cells; without it a Date renders as a full
22
- * ISO-8601 timestamp. */
21
+ /**
22
+ * An Excel number-format code (e.g. `"yyyy-mm-dd"`, `"d mmm yy hh:mm"`) for Date cells; without it
23
+ * a Date renders as a full ISO-8601 timestamp.
24
+ *
25
+ * The same vocabulary as {@link Cell.numFmt}, so `writeCsv(wb, {dateFormat: cell.numFmt})` renders
26
+ * what the cell would show. It used to be a moment.js-style token set, which is case-sensitive and
27
+ * spells the month `MM` and the minute `mm`: passing this library's own format codes to it produced
28
+ * `2024-45-dd` with no throw and no warning. See ADR 0041.
29
+ */
23
30
  readonly dateFormat?: string;
24
31
  /** Render Date cells in UTC rather than the runner's local time. */
25
32
  readonly dateUTC?: boolean;