@shbernal/ts-xlsx 1.2.0 → 2.0.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 (161) hide show
  1. package/README.md +5 -0
  2. package/dist/core/address.d.ts +49 -2
  3. package/dist/core/address.js +31 -3
  4. package/dist/core/autofilter.d.ts +2 -2
  5. package/dist/core/cell.d.ts +17 -16
  6. package/dist/core/cell.js +3 -7
  7. package/dist/core/color-resolution.d.ts +7 -7
  8. package/dist/core/column.d.ts +8 -9
  9. package/dist/core/column.js +2 -4
  10. package/dist/core/comment-thread.d.ts +32 -23
  11. package/dist/core/comment-thread.js +4 -4
  12. package/dist/core/conditional-formatting-overlay.d.ts +1 -1
  13. package/dist/core/conditional-formatting.d.ts +3 -3
  14. package/dist/core/data-validation-overlay.d.ts +1 -1
  15. package/dist/core/data-validation.d.ts +3 -3
  16. package/dist/core/date.d.ts +3 -3
  17. package/dist/core/formula.d.ts +6 -6
  18. package/dist/core/grid-edits.js +3 -3
  19. package/dist/core/image.d.ts +37 -2
  20. package/dist/core/image.js +6 -0
  21. package/dist/core/internal.d.ts +9 -1
  22. package/dist/core/limits.d.ts +4 -4
  23. package/dist/core/merge.d.ts +6 -12
  24. package/dist/core/merge.js +0 -3
  25. package/dist/core/page-setup.d.ts +4 -4
  26. package/dist/core/pivot-table.d.ts +2 -4
  27. package/dist/core/pivot-table.js +5 -5
  28. package/dist/core/preserved.d.ts +3 -3
  29. package/dist/core/protection.d.ts +4 -4
  30. package/dist/core/range.d.ts +16 -14
  31. package/dist/core/range.js +5 -5
  32. package/dist/core/row-input.d.ts +2 -8
  33. package/dist/core/row-input.js +2 -2
  34. package/dist/core/row.d.ts +7 -8
  35. package/dist/core/row.js +2 -4
  36. package/dist/core/style.d.ts +14 -14
  37. package/dist/core/table-style.d.ts +9 -9
  38. package/dist/core/table-style.js +1 -1
  39. package/dist/core/table.d.ts +30 -42
  40. package/dist/core/table.js +16 -10
  41. package/dist/core/theme.d.ts +13 -43
  42. package/dist/core/theme.js +2 -148
  43. package/dist/core/value.d.ts +7 -7
  44. package/dist/core/workbook-protection.d.ts +2 -2
  45. package/dist/core/workbook-styles.d.ts +26 -0
  46. package/dist/core/workbook-styles.js +48 -0
  47. package/dist/core/workbook-theme.d.ts +25 -0
  48. package/dist/core/workbook-theme.js +49 -0
  49. package/dist/core/workbook-vba.d.ts +17 -0
  50. package/dist/core/workbook-vba.js +79 -0
  51. package/dist/core/workbook.d.ts +141 -83
  52. package/dist/core/workbook.js +74 -133
  53. package/dist/core/worksheet-comments.d.ts +9 -0
  54. package/dist/core/worksheet-comments.js +52 -0
  55. package/dist/core/worksheet-model.d.ts +4 -4
  56. package/dist/core/worksheet-model.js +1 -0
  57. package/dist/core/worksheet-pictures.d.ts +29 -0
  58. package/dist/core/worksheet-pictures.js +42 -0
  59. package/dist/core/worksheet.d.ts +94 -77
  60. package/dist/core/worksheet.js +36 -95
  61. package/dist/customui/errors.d.ts +1 -1
  62. package/dist/customui/ribbon.d.ts +3 -3
  63. package/dist/entries/core.d.ts +4 -4
  64. package/dist/entries/core.js +1 -1
  65. package/dist/entries/vba.d.ts +1 -1
  66. package/dist/entries/vba.js +1 -1
  67. package/dist/entries/xlsx.d.ts +1 -0
  68. package/dist/entries/xlsx.js +1 -0
  69. package/dist/errors.d.ts +12 -12
  70. package/dist/io/csv/read.js +1 -2
  71. package/dist/io/csv/write.d.ts +8 -3
  72. package/dist/io/csv/write.js +17 -2
  73. package/dist/io/opc/errors.d.ts +6 -6
  74. package/dist/io/opc/inflate.d.ts +1 -1
  75. package/dist/io/opc/namespaces.d.ts +1 -1
  76. package/dist/io/opc/read-opc.d.ts +19 -3
  77. package/dist/io/opc/read-opc.js +14 -8
  78. package/dist/io/opc/read-options.d.ts +2 -2
  79. package/dist/io/opc/sniff-format.d.ts +2 -2
  80. package/dist/io/opc/zip-mtime.d.ts +5 -0
  81. package/dist/io/opc/zip-mtime.js +1 -0
  82. package/dist/io/style/xf-style.d.ts +38 -6
  83. package/dist/io/style/xf-style.js +22 -0
  84. package/dist/io/xlsb/errors.d.ts +1 -1
  85. package/dist/io/xlsb/formula.d.ts +4 -4
  86. package/dist/io/xlsb/primitives.d.ts +8 -8
  87. package/dist/io/xlsb/ptg-functions.d.ts +1 -1
  88. package/dist/io/xlsb/read-styles.js +2 -20
  89. package/dist/io/xlsb/read.d.ts +2 -2
  90. package/dist/io/xlsx/cell-accumulator.d.ts +25 -6
  91. package/dist/io/xlsx/cell-accumulator.js +111 -30
  92. package/dist/io/xlsx/cell-value.d.ts +2 -2
  93. package/dist/io/xlsx/cell-value.js +3 -3
  94. package/dist/io/xlsx/color-xml.js +11 -17
  95. package/dist/io/xlsx/comments.d.ts +5 -5
  96. package/dist/io/xlsx/comments.js +13 -6
  97. package/dist/io/xlsx/conditional-formatting.d.ts +3 -3
  98. package/dist/io/xlsx/conditional-formatting.js +7 -16
  99. package/dist/io/xlsx/data-validation.d.ts +2 -2
  100. package/dist/io/xlsx/data-validation.js +1 -1
  101. package/dist/io/xlsx/edit-vba.js +2 -1
  102. package/dist/io/xlsx/errors.d.ts +3 -3
  103. package/dist/io/xlsx/hyperlinks.d.ts +4 -2
  104. package/dist/io/xlsx/hyperlinks.js +5 -5
  105. package/dist/io/xlsx/images.d.ts +1 -1
  106. package/dist/io/xlsx/images.js +8 -8
  107. package/dist/io/xlsx/namespaces.d.ts +2 -2
  108. package/dist/io/xlsx/package-plan.js +5 -2
  109. package/dist/io/xlsx/read-pivot.js +2 -5
  110. package/dist/io/xlsx/read-rows.d.ts +13 -13
  111. package/dist/io/xlsx/read-rows.js +19 -55
  112. package/dist/io/xlsx/read-shared-strings.js +7 -5
  113. package/dist/io/xlsx/read-styles.d.ts +3 -3
  114. package/dist/io/xlsx/read-styles.js +40 -65
  115. package/dist/io/xlsx/read-worksheet.js +67 -115
  116. package/dist/io/xlsx/read.d.ts +3 -5
  117. package/dist/io/xlsx/read.js +94 -71
  118. package/dist/io/xlsx/rich-runs.d.ts +1 -1
  119. package/dist/io/xlsx/rich-runs.js +1 -1
  120. package/dist/io/xlsx/rich-text.d.ts +1 -1
  121. package/dist/io/xlsx/shared-strings.d.ts +2 -2
  122. package/dist/io/xlsx/sheet-properties.js +5 -4
  123. package/dist/io/xlsx/styles.d.ts +8 -9
  124. package/dist/io/xlsx/styles.js +4 -3
  125. package/dist/io/xlsx/tables.d.ts +2 -2
  126. package/dist/io/xlsx/tables.js +8 -13
  127. package/dist/io/xlsx/theme-xml.d.ts +37 -0
  128. package/dist/io/xlsx/theme-xml.js +142 -0
  129. package/dist/io/xlsx/threaded-comments.d.ts +18 -27
  130. package/dist/io/xlsx/threaded-comments.js +9 -21
  131. package/dist/io/xlsx/workbook-xml.d.ts +1 -1
  132. package/dist/io/xlsx/workbook-xml.js +6 -2
  133. package/dist/io/xlsx/worksheet-xml.d.ts +2 -2
  134. package/dist/io/xlsx/worksheet-xml.js +7 -12
  135. package/dist/io/xlsx/write-stream.d.ts +15 -15
  136. package/dist/io/xlsx/write-stream.js +5 -3
  137. package/dist/io/xlsx/write.d.ts +12 -7
  138. package/dist/io/xlsx/write.js +10 -6
  139. package/dist/io/xlsx/x14-ext.d.ts +1 -1
  140. package/dist/vba/bytes.d.ts +12 -0
  141. package/dist/vba/bytes.js +40 -0
  142. package/dist/vba/cfb-writer.d.ts +1 -1
  143. package/dist/vba/cfb.d.ts +1 -1
  144. package/dist/vba/cfb.js +15 -51
  145. package/dist/vba/codepage.d.ts +2 -2
  146. package/dist/vba/errors.d.ts +2 -2
  147. package/dist/vba/index.d.ts +2 -2
  148. package/dist/vba/index.js +2 -2
  149. package/dist/vba/ms-ovba.d.ts +1 -1
  150. package/dist/vba/ms-ovba.js +1 -3
  151. package/dist/vba/project-editor.d.ts +9 -9
  152. package/dist/vba/project-editor.js +2 -11
  153. package/dist/vba/project.d.ts +5 -5
  154. package/dist/vba/project.js +1 -10
  155. package/dist/vba/vba-encoding.d.ts +1 -1
  156. package/dist/xml/errors.d.ts +1 -1
  157. package/dist/xml/xml-read.d.ts +38 -9
  158. package/dist/xml/xml-read.js +23 -0
  159. package/dist/xml/xml.d.ts +31 -9
  160. package/dist/xml/xml.js +23 -3
  161. package/package.json +41 -35
package/README.md CHANGED
@@ -236,6 +236,11 @@ npx skills add ./node_modules/@shbernal/ts-xlsx # offline, matches your inst
236
236
  npx skills add shbernal/ts-xlsx # or straight from the repo
237
237
  ```
238
238
 
239
+ That writes the skill under `.agents/skills/`, links it into whichever agent directories it
240
+ finds, and records what it did in `skills-lock.json`. Track the lock file and ignore the
241
+ copies: they are derived, `npx skills experimental_install` rebuilds them from the lock, and
242
+ `npx skills update` overwrites them in place.
243
+
239
244
  It covers triage (is this bug ours or your file's?), reducing a failure to a script that
240
245
  builds its own input, and — because spreadsheets carry real data — never uploading a
241
246
  workbook to a public tracker. Once the reproduction stands on its own, it files without
@@ -2,9 +2,33 @@
2
2
  export declare const MAX_COLUMN = 16384;
3
3
  /** Excel's row bound: 1 through 1048576. The other axis of {@link MAX_COLUMN}. */
4
4
  export declare const MAX_ROW = 1048576;
5
+ /** @throws {RangeError} unless `n` is an integer in `1..MAX_COLUMN`. */
6
+ export declare function assertColumnInBounds(n: number): void;
7
+ /** @throws {RangeError} unless `n` is an integer in `1..MAX_ROW`. */
8
+ export declare function assertRowInBounds(n: number): void;
9
+ /**
10
+ * A rectangular block of the grid, as **inclusive** 1-based bounds on both axes.
11
+ *
12
+ * One declaration because inclusive-first/last is the convention every range-shaped thing in this
13
+ * library follows, and three copies of a convention are three places it can drift. A merged region,
14
+ * a table's extent and a {@link Range} handle are all this shape; what differs between them is what
15
+ * the rectangle *means*, which is what their own names carry.
16
+ */
17
+ export interface GridRect {
18
+ /** 1-based row of the top edge. */
19
+ readonly top: number;
20
+ /** 1-based column of the left edge. */
21
+ readonly left: number;
22
+ /** 1-based row of the bottom edge, inclusive. */
23
+ readonly bottom: number;
24
+ /** 1-based column of the right edge, inclusive. */
25
+ readonly right: number;
26
+ }
27
+ /** Whether two grid rectangles share at least one cell. */
28
+ export declare function rectsOverlap(a: GridRect, b: GridRect): boolean;
5
29
  /** A decoded single-cell reference. An axis the reference omits is `undefined`. */
6
30
  export interface CellAddress {
7
- /** Canonical A1 form with `$` anchors stripped e.g. `"B2"`, `"1"`, `"A"`. */
31
+ /** Canonical A1 form with `$` anchors stripped: e.g. `"B2"`, `"1"`, `"A"`. */
8
32
  readonly address: string;
9
33
  /** 1-based column, or `undefined` for a row-only reference (`$1`). */
10
34
  readonly col: number | undefined;
@@ -25,7 +49,7 @@ export interface RangeAddress {
25
49
  readonly sheetName?: string;
26
50
  readonly tl: CellAddress;
27
51
  readonly br: CellAddress;
28
- /** Canonical `tl:br` form `"A1:B2"`, `"1:1"` (rows), `"A:A"` (columns). */
52
+ /** Canonical `tl:br` form: `"A1:B2"`, `"1:1"` (rows), `"A:A"` (columns). */
29
53
  readonly dimensions: string;
30
54
  }
31
55
  /** Convert a 1-based column number to its letters (`1 → "A"`, `27 → "AA"`). */
@@ -39,6 +63,29 @@ export declare function columnToNumber(letters: string): number;
39
63
  * @throws {SyntaxError} if the reference mentions neither a column nor a row.
40
64
  */
41
65
  export declare function decodeAddress(reference: string): CellAddress;
66
+ /**
67
+ * A reference that names one cell, both axes present. The narrowing of {@link CellAddress} that
68
+ * most callers actually want: `decodeAddress` is deliberately three-shaped because a bare row
69
+ * (`$1`) and a bare column (`$A`) are legitimate references, but a cell is where a value lives, and
70
+ * every caller that needs one was re-deriving that invariant by hand.
71
+ */
72
+ export interface CellPosition {
73
+ readonly col: number;
74
+ readonly row: number;
75
+ }
76
+ /**
77
+ * Decode a reference that must name a single cell. Anchoring `$` signs are accepted and dropped.
78
+ *
79
+ * @throws {SyntaxError} if the reference is unparseable, or parses but omits an axis (`"A"`, `"1"`).
80
+ */
81
+ export declare function decodeCellRef(reference: string): CellPosition;
82
+ /**
83
+ * {@link decodeCellRef} for a reference that came out of a file rather than out of a caller:
84
+ * `undefined` for anything that does not name one cell, whether it is a range, a bare row or
85
+ * column, or outright garbage. A foreign producer writes all four, and none of them is worth
86
+ * throwing over when the reading code's answer is simply "then there is nothing here".
87
+ */
88
+ export declare function tryDecodeCellRef(reference: string): CellPosition | undefined;
42
89
  /**
43
90
  * Decode a range reference (`A1:B2`, `$1:$1`, `Sheet1!$A:$A`) into its corners and
44
91
  * canonical dimensions. A single reference collapses to a degenerate range whose
@@ -1,10 +1,23 @@
1
1
  export const MAX_COLUMN = 16384;
2
2
  export const MAX_ROW = 1048576;
3
+ export function assertColumnInBounds(n) {
4
+ if (!Number.isInteger(n) || n < 1 || n > MAX_COLUMN) {
5
+ throw new RangeError(`column ${n} is out of bounds: Excel supports 1..${MAX_COLUMN}`);
6
+ }
7
+ }
8
+ export function assertRowInBounds(n) {
9
+ if (!Number.isInteger(n) || n < 1 || n > MAX_ROW) {
10
+ throw new RangeError(`row ${n} is out of bounds: Excel supports 1..${MAX_ROW}`);
11
+ }
12
+ }
13
+ export function rectsOverlap(a, b) {
14
+ return a.left <= b.right && b.left <= a.right && a.top <= b.bottom && b.top <= a.bottom;
15
+ }
3
16
  const SINGLE_REF = /^\$?([A-Z]*)\$?(\d*)$/;
4
17
  const SHEET_PREFIX = /^(?:(?:'((?:[^']|'')*)')|([^'!]+))!(.*)$/;
5
18
  export function numberToColumn(n) {
6
19
  if (!Number.isInteger(n) || n < 1 || n > MAX_COLUMN) {
7
- throw new RangeError(`column ${n} is out of bounds Excel supports 1..${MAX_COLUMN}`);
20
+ throw new RangeError(`column ${n} is out of bounds: Excel supports 1..${MAX_COLUMN}`);
8
21
  }
9
22
  let letters = '';
10
23
  let remaining = n;
@@ -28,7 +41,7 @@ export function columnToNumber(letters) {
28
41
  n = n * 26 + (code - 64);
29
42
  }
30
43
  if (n > MAX_COLUMN) {
31
- throw new RangeError(`column "${letters}" is out of bounds Excel supports up to ${MAX_COLUMN} (XFD)`);
44
+ throw new RangeError(`column "${letters}" is out of bounds: Excel supports up to ${MAX_COLUMN} (XFD)`);
32
45
  }
33
46
  return n;
34
47
  }
@@ -53,6 +66,21 @@ export function decodeAddress(reference) {
53
66
  const row = digits.length > 0 ? Number.parseInt(digits, 10) : undefined;
54
67
  return { address: `${letters}${digits}`, col, row };
55
68
  }
69
+ export function decodeCellRef(reference) {
70
+ const { col, row } = decodeAddress(reference);
71
+ if (col === undefined || row === undefined) {
72
+ throw new SyntaxError(`"${reference}" is not a single-cell reference: it omits a column or row`);
73
+ }
74
+ return { col, row };
75
+ }
76
+ export function tryDecodeCellRef(reference) {
77
+ try {
78
+ return decodeCellRef(reference);
79
+ }
80
+ catch {
81
+ return undefined;
82
+ }
83
+ }
56
84
  export function decodeRange(reference) {
57
85
  const prefix = SHEET_PREFIX.exec(reference);
58
86
  let sheetName;
@@ -86,7 +114,7 @@ export function decodeRange(reference) {
86
114
  }
87
115
  export function encodeAddress(col, row) {
88
116
  if (!Number.isInteger(row) || row < 1) {
89
- throw new RangeError(`row ${row} is out of bounds rows start at 1`);
117
+ throw new RangeError(`row ${row} is out of bounds: rows start at 1`);
90
118
  }
91
119
  return `${numberToColumn(col)}${row}`;
92
120
  }
@@ -18,7 +18,7 @@ export interface FilterColumn {
18
18
  /** The two criteria kinds this library models: a discrete value set, or operator predicates. */
19
19
  export type FilterCriteria = ValuesFilter | CustomFilter;
20
20
  /**
21
- * A column narrowed to a discrete set of allowed values the checkbox list in Excel's dropdown.
21
+ * A column narrowed to a discrete set of allowed values: the checkbox list in Excel's dropdown.
22
22
  * A row survives when its cell in this column matches one of {@link values} (or is blank, when
23
23
  * {@link blank} is set).
24
24
  */
@@ -48,7 +48,7 @@ export declare function isCustomFilterOperator(value: string): value is CustomFi
48
48
  * Validate and normalise a settable autofilter into its canonical stored form. A bare range string
49
49
  * is the common case (dropdowns, no criteria); an {@link AutoFilter} object carries per-column
50
50
  * criteria too. Throws when the range is not a bounded rectangle, or a column's `colId` falls
51
- * outside it, or a custom filter does not carry one or two predicates a filter authored wrong is
51
+ * outside it, or a custom filter does not carry one or two predicates: a filter authored wrong is
52
52
  * a bug to surface, not to silently repair. (The reader sanitises hostile input before it reaches
53
53
  * here, so load never trips these guards.)
54
54
  */
@@ -3,8 +3,8 @@ import { type Alignment, type Border, type CellStyle, type Fill, type Font, type
3
3
  import { type CellValue, type RichTextRun, type ValueType } from './value.ts';
4
4
  import type { CellModel } from './worksheet.ts';
5
5
  /**
6
- * A single cell owns its value and every style facet outright. Each facet below fill, number format,
7
- * font, border, alignment, protection, quote-prefix, and note is held in the cell's own field and
6
+ * A single cell owns its value and every style facet outright. Each facet below (fill, number format,
7
+ * font, border, alignment, protection, quote-prefix, and note) is held in the cell's own field and
8
8
  * *replaced* (never mutated in place) by its setter, so a facet set on one cell never aliases or bleeds
9
9
  * onto its row, column, or sheet siblings. Each facet's own doc covers only what is specific to it.
10
10
  */
@@ -14,6 +14,7 @@ export declare class Cell {
14
14
  readonly row: number;
15
15
  /** 1-based column index. */
16
16
  readonly col: number;
17
+ /** @throws {RangeError} unless both axes are integers within Excel's grid. */
17
18
  constructor(row: number, col: number);
18
19
  /** Canonical A1 address of this cell (`"B3"`). */
19
20
  get address(): string;
@@ -23,12 +24,12 @@ export declare class Cell {
23
24
  /** The observable {@link ValueType} of the current value. */
24
25
  get type(): ValueType;
25
26
  /**
26
- * The cell's value as plain text ({@link cellValueToText}), `""` when it is empty so a reader
27
+ * The cell's value as plain text ({@link cellValueToText}), `""` when it is empty, so a reader
27
28
  * that only wants strings never has to narrow the value union itself.
28
29
  *
29
30
  * Read-only, because text is a *rendering* of the value and not a second place to store one:
30
31
  * writing `"3"` here could only mean the string `"3"`, which is exactly `value = '3'` and reads
31
- * nothing like it. The number format is not applied either the style is not the cell's value,
32
+ * nothing like it. The number format is not applied either: the style is not the cell's value,
32
33
  * so a currency cell's text carries no currency sign.
33
34
  */
34
35
  get text(): string;
@@ -39,27 +40,27 @@ export declare class Cell {
39
40
  *
40
41
  * This exists because a run's format element does **not** inherit anything. A `<rPr>` is a
41
42
  * *complete* character format, and any facet it omits falls back to the workbook default font
42
- * ({@link Workbook.defaultFont}) not to the cell's. Verified against Excel: a cell set to
43
+ * ({@link Workbook.defaultFont}), not to the cell's. Verified against Excel: a cell set to
43
44
  * Courier New 16 whose first run carries only `<b/>` renders that run in the workbook default face
44
45
  * at the default size, bold, while the rest of the cell renders Courier New 16. So a run authored
45
46
  * as `{bold: true}` beside a styled cell silently loses the face, which is the format's rule rather
46
- * than a bug and the reason this is a helper rather than a change to how runs are written.
47
+ * than a bug, and the reason this is a helper rather than a change to how runs are written.
47
48
  *
48
49
  * Composition is per facet: a facet the run names wins, one it omits comes from the cell. Assigning
49
50
  * `value` directly stays the bare path, for a caller who wants a run that deliberately falls back
50
51
  * to the workbook default.
51
52
  *
52
- * A cell that names no font of its own needs no composition an omitted facet already falls back
53
- * to the workbook default, which is exactly what such a cell renders in so the runs pass through
53
+ * A cell that names no font of its own needs no composition: an omitted facet already falls back
54
+ * to the workbook default, which is exactly what such a cell renders in, so the runs pass through
54
55
  * unchanged.
55
56
  */
56
57
  setRichText(runs: readonly RichTextRun[]): void;
57
58
  /**
58
- * The cell's full style fill, number format, font, border, alignment, and protection as one
59
+ * The cell's full style (fill, number format, font, border, alignment, and protection) as one
59
60
  * {@link CellStyle}, for restyling a cell wholesale without importing {@link applyCellStyle}
60
61
  * separately (mirrors {@link Worksheet.model}'s getter/setter pair for the whole sheet). The
61
62
  * getter carries only the facets this cell has set (the same shape {@link cellToModel} emits);
62
- * the setter lays each facet `style` carries onto this cell like every per-facet setter, it
63
+ * the setter lays each facet `style` carries onto this cell and, like every per-facet setter, it
63
64
  * replaces that facet outright but leaves a facet `style` omits untouched, so `cell.style = {...}`
64
65
  * composes with prior per-facet sets rather than clearing them wholesale.
65
66
  */
@@ -71,35 +72,35 @@ export declare class Cell {
71
72
  /**
72
73
  * The cell's number-format code (`"0.00%"`, a custom accounting format, …), or
73
74
  * `undefined` for the General format. Stored verbatim: the invariant form Excel
74
- * persists `.` decimal, `,` grouping, `/` date separator is neither localized
75
+ * persists (`.` decimal, `,` grouping, `/` date separator) is neither localized
75
76
  * nor rewritten, so the code round-trips character-for-character. A cell that also carries
76
77
  * a column-level format keeps both, so overriding one facet never drops the other.
77
78
  */
78
79
  get numFmt(): string | undefined;
79
80
  set numFmt(numFmt: string | undefined);
80
81
  /**
81
- * The cell's font bold/italic/underline, size, colour, typeface as a partial set
82
+ * The cell's font (bold/italic/underline, size, colour, typeface) as a partial set
82
83
  * of the facets that differ from the default (only the facets actually set are carried,
83
84
  * exactly as OOXML stores them). `undefined` means the cell uses the workbook default font.
84
85
  */
85
86
  get font(): Font | undefined;
86
87
  set font(font: Font | undefined);
87
88
  /**
88
- * The cell's border the line style and colour of each side or `undefined` when the
89
+ * The cell's border (the line style and colour of each side), or `undefined` when the
89
90
  * cell has none. An absent edge within a border means that side is unbordered, so reading
90
91
  * a cell never fabricates a border it does not have.
91
92
  */
92
93
  get border(): Border | undefined;
93
94
  set border(border: Border | undefined);
94
95
  /**
95
- * The cell's alignment how its content sits within the cell, plus the wrap/shrink flags
96
+ * The cell's alignment (how its content sits within the cell, plus the wrap/shrink flags),
96
97
  * or `undefined` when it uses the defaults. The boolean flags are off unless explicitly set,
97
98
  * so a cell that never enabled wrapping never reads back wrapped.
98
99
  */
99
100
  get alignment(): Alignment | undefined;
100
101
  set alignment(alignment: Alignment | undefined);
101
102
  /**
102
- * The cell's protection its locked/hidden flags, enforced only once the sheet is protected
103
+ * The cell's protection (its locked/hidden flags, enforced only once the sheet is protected),
103
104
  * or `undefined` when the cell carries neither. `locked` defaults to on in OOXML, so a cell
104
105
  * that never touched protection is implicitly locked and reads back as `undefined`, not as
105
106
  * `{locked: true}`; the flag only becomes explicit when a cell is unlocked.
@@ -110,7 +111,7 @@ export declare class Cell {
110
111
  * The quote-prefix flag: when set, a spreadsheet stores the cell's content as literal text even
111
112
  * when it looks like a formula or number, and shows a leading apostrophe in the formula bar without
112
113
  * that apostrophe being part of the stored value. `undefined` (or `false`) when unset. It is a
113
- * cell-format flag an attribute on the cell's `xf` record so it composes independently of the
114
+ * cell-format flag, an attribute on the cell's `xf` record, so it composes independently of the
114
115
  * value.
115
116
  */
116
117
  get quotePrefix(): boolean | undefined;
package/dist/core/cell.js CHANGED
@@ -1,4 +1,4 @@
1
- import { encodeAddress } from './address.js';
1
+ import { assertColumnInBounds, assertRowInBounds, encodeAddress } from './address.js';
2
2
  import { NAMED_STYLE_ID } from './internal.js';
3
3
  import { assignStyleFacets, } from './style.js';
4
4
  import { cellValueToText, coerceCellValue, detectValueType, } from './value.js';
@@ -16,12 +16,8 @@ export class Cell {
16
16
  #namedStyleId;
17
17
  #note;
18
18
  constructor(row, col) {
19
- if (!Number.isInteger(row) || row < 1) {
20
- throw new RangeError(`cell row ${row} is out of bounds — rows start at 1`);
21
- }
22
- if (!Number.isInteger(col) || col < 1) {
23
- throw new RangeError(`cell column ${col} is out of bounds — columns start at 1`);
24
- }
19
+ assertRowInBounds(row);
20
+ assertColumnInBounds(col);
25
21
  this.row = row;
26
22
  this.col = col;
27
23
  }
@@ -1,21 +1,21 @@
1
1
  import type { Color } from './style.ts';
2
2
  import { type ThemeColorScheme } from './theme.ts';
3
3
  /**
4
- * The built-in indexed colour palette (ECMA-376 §18.8.27), by index. Entries 0–7 duplicate 8–15
4
+ * The built-in indexed colour palette (ECMA-376 §18.8.27), by index. Entries 0–7 duplicate 8–15, a
5
5
  * redundancy the spec preserves for backwards compatibility with the legacy formats this palette came
6
- * from and the table is only 64 long: indices 64 and 65 are the *system* foreground and background,
6
+ * from, and the table is only 64 long: indices 64 and 65 are the *system* foreground and background,
7
7
  * which name whatever the operating system's window colours are and therefore have no fixed value at
8
8
  * all (see {@link SYSTEM_INDEXED_COLORS}).
9
9
  *
10
- * The spec writes each entry with a leading `00`. That byte is not an alpha channel a palette of
11
- * fully transparent colours would be absurd it is an artefact of the 32-bit colour records these
10
+ * The spec writes each entry with a leading `00`. That byte is not an alpha channel, since a palette
11
+ * of fully transparent colours would be absurd. It is an artefact of the 32-bit colour records these
12
12
  * values were lifted from, which is why {@link resolveColor} returns them fully opaque.
13
13
  */
14
14
  export declare const DEFAULT_INDEXED_COLORS: readonly string[];
15
15
  /**
16
16
  * The two indices that are not colours: 64 is the system foreground and 65 the system background.
17
17
  * They resolve to whatever the viewing system's window colours are, so this library reports them
18
- * unresolved rather than inventing black and white a caller that wants to paint them must decide
18
+ * unresolved rather than inventing black and white: a caller that wants to paint them must decide
19
19
  * for itself what "automatic" means in its context. `indexed="64"` in particular is extremely common:
20
20
  * it is the placeholder every solid fill Excel writes carries as its background colour.
21
21
  */
@@ -27,14 +27,14 @@ export interface ColorResolutionContext {
27
27
  /**
28
28
  * The workbook's custom indexed palette, by index, each entry an ARGB string. Empty or absent means
29
29
  * the workbook rides {@link DEFAULT_INDEXED_COLORS}. A custom palette replaces the built-in one
30
- * wholesale that is what `<indexedColors>` means so a short custom palette leaves the indices
30
+ * wholesale, which is what `<indexedColors>` means, so a short custom palette leaves the indices
31
31
  * past its end unresolved rather than falling through to the built-in entry.
32
32
  */
33
33
  readonly indexed?: readonly string[] | undefined;
34
34
  }
35
35
  /**
36
36
  * Resolve a colour reference to a concrete 8-hex ARGB string, or `undefined` when it cannot be
37
- * resolved an `auto` colour, a system indexed colour, a theme slot the workbook's scheme does not
37
+ * resolved: an `auto` colour, a system indexed colour, a theme slot the workbook's scheme does not
38
38
  * declare, or an index past the end of a custom palette.
39
39
  *
40
40
  * Precedence follows what the encodings mean: an explicit `argb` is already concrete and wins; then
@@ -1,4 +1,5 @@
1
1
  import type { Cell } from './cell.ts';
2
+ import { type AssertNever } from './internal.ts';
2
3
  import type { Alignment, Border, Fill, Font, Protection } from './style.ts';
3
4
  import type { CellValue } from './value.ts';
4
5
  import type { ColumnProperties, Worksheet } from './worksheet.ts';
@@ -6,12 +7,12 @@ export declare class Column {
6
7
  #private;
7
8
  /** 1-based column index. Fixed for this handle's lifetime. */
8
9
  readonly index: number;
9
- /** @throws {RangeError} if the index is not a positive integer. */
10
+ /** @throws {RangeError} unless the index is an integer within Excel's column grid (1..16384). */
10
11
  constructor(sheet: Worksheet, index: number);
11
- /** The column's letters (`"B"`) the spreadsheet-facing name for {@link index}. */
12
+ /** The column's letters (`"B"`): the spreadsheet-facing name for {@link index}. */
12
13
  get letter(): string;
13
14
  /**
14
- * The column's format record if it has one, else `undefined` a read that never fabricates, so a
15
+ * The column's format record if it has one, else `undefined`: a read that never fabricates, so a
15
16
  * serializer can ask every column it visits whether there are attributes to emit without giving
16
17
  * each one an empty record. Read-only on purpose: {@link width} and its siblings are how a column
17
18
  * is formatted, and they create the record on first write.
@@ -19,12 +20,12 @@ export declare class Column {
19
20
  get properties(): Readonly<ColumnProperties> | undefined;
20
21
  /**
21
22
  * Stable key naming this column so a keyed-object row (see {@link Worksheet.addRow}) can place a
22
- * value under it by name rather than position. In-memory only never serialized to OOXML.
23
+ * value under it by name rather than position. In-memory only: never serialized to OOXML.
23
24
  */
24
25
  get key(): string | undefined;
25
26
  set key(key: string | undefined);
26
27
  /**
27
- * Column width in character units digits of the workbook default font's maximum digit width,
28
+ * Column width in character units: digits of the workbook default font's maximum digit width,
28
29
  * so what one unit measures moves with that font. `undefined` leaves the sheet default in force.
29
30
  *
30
31
  * Not bounded here, for the same reason {@link Row.height} is not, and for a stronger one:
@@ -79,17 +80,15 @@ export declare class Column {
79
80
  * cell holding `null`.
80
81
  *
81
82
  * Assigning places each value it names and leaves every other row untouched, mirroring
82
- * {@link Row.values} a hole or an explicit `undefined` skips that row, and a shorter array does
83
+ * {@link Row.values}: a hole or an explicit `undefined` skips that row, and a shorter array does
83
84
  * not clear the tail.
84
85
  */
85
86
  get values(): (CellValue | undefined)[];
86
87
  set values(values: (CellValue | undefined)[]);
87
88
  }
88
- type AssertNever<T extends never> = T;
89
89
  /**
90
- * Compile-time proof that {@link Column} mirrors every {@link ColumnProperties} field including
90
+ * Compile-time proof that {@link Column} mirrors every {@link ColumnProperties} field, including
91
91
  * the six inherited `CellStyle` facets, so a seventh facet reaches this handle the moment it joins
92
92
  * the tuple. See the counterpart on `Row` for why the mirror needs proving rather than reviewing.
93
93
  */
94
94
  export type EveryColumnPropertyIsMirrored = AssertNever<Exclude<keyof ColumnProperties, keyof Column & keyof ColumnProperties>>;
95
- export {};
@@ -1,12 +1,10 @@
1
- import { encodeAddress, numberToColumn } from './address.js';
1
+ import { assertColumnInBounds, encodeAddress, numberToColumn } from './address.js';
2
2
  import { INTERNAL } from './internal.js';
3
3
  export class Column {
4
4
  #sheet;
5
5
  index;
6
6
  constructor(sheet, index) {
7
- if (!Number.isInteger(index) || index < 1) {
8
- throw new RangeError(`column ${index} is out of bounds — columns start at 1`);
9
- }
7
+ assertColumnInBounds(index);
10
8
  this.#sheet = sheet;
11
9
  this.index = index;
12
10
  }
@@ -1,47 +1,56 @@
1
1
  /**
2
- * A registered identity a threaded comment can point at an author, or someone `@mentioned` in a
2
+ * A registered identity a threaded comment can point at: an author, or someone `@mentioned` in a
3
3
  * message. One `<person>` of the workbook's `xl/persons/person.xml` registry.
4
4
  *
5
5
  * A single human legitimately has **several** entries: Excel registers a mentioned identity separately
6
6
  * from that person's authoring identity, with the same {@link displayName} and {@link userId} but a
7
- * different {@link id} and a different {@link providerId}. The id is therefore the only identity
7
+ * different {@link id} and a different {@link providerId}. The id is therefore the only identity;
8
8
  * see {@link Workbook.getPerson}.
9
9
  */
10
10
  export interface Person {
11
11
  /** Brace-wrapped GUID this identity is referenced by. The only field that identifies it. */
12
12
  readonly id: string;
13
- /** The name a spreadsheet app shows not unique, and not an identity. */
13
+ /** The name a spreadsheet app shows. Not unique, and not an identity. */
14
14
  readonly displayName: string;
15
15
  /** Identity-provider handle, `S::<email>::<tenant-guid>` for an AzureAD account. */
16
16
  readonly userId?: string;
17
- /** The provider that registered this entry `AD` for a directory account, `PeoplePicker` for an
17
+ /** The provider that registered this entry: `AD` for a directory account, `PeoplePicker` for an
18
18
  * identity interned by being mentioned. */
19
19
  readonly providerId?: string;
20
20
  }
21
21
  /**
22
- * An `@mention` inside a message: who was named, and the run of {@link Comment.text} that renders as the
23
- * mention chip.
22
+ * An `@mention` as the file spells it: who was named, and the run of {@link Comment.text} that
23
+ * renders as the mention chip.
24
24
  *
25
- * The offsets are only meaningful against that exact text shift either and a spreadsheet app
25
+ * The offsets are only meaningful against that exact text: shift either and a spreadsheet app
26
26
  * highlights the wrong words.
27
+ *
28
+ * This is the wire shape, shared with the codec that reads it. {@link Mention} is this plus the
29
+ * identity we resolved the id to, which is the one thing the file does not carry.
27
30
  */
28
- export interface Mention {
29
- /**
30
- * The mentioned identity, resolved through the workbook registry. Absent when the file names an id
31
- * the registry does not hold (a mention left dangling by a foreign generator); {@link personId}
32
- * still says who was meant.
33
- */
34
- readonly person?: Person;
31
+ export interface MentionRef {
35
32
  /** The mentioned {@link Person.id} exactly as written, so a dangling mention stays diagnosable. */
36
33
  readonly personId: string;
37
34
  /** Excel's own id for this mention, preserved so re-emitting it does not invent a new one. */
38
35
  readonly mentionId?: string;
39
- /** 0-based character offset into {@link Comment.text} where the mention starts. */
36
+ /**
37
+ * 0-based character offset into {@link Comment.text} where the mention starts. Verified against
38
+ * desktop Excel by rendering: the chip covers exactly `[startIndex, startIndex + length)`.
39
+ */
40
40
  readonly startIndex: number;
41
41
  /** Length of the mention in characters, **counting the leading `@`** (`@Grace Hopper` is 13). */
42
42
  readonly length: number;
43
43
  }
44
- /** One message of a {@link CommentThread} what a single person wrote, once. */
44
+ /** A {@link MentionRef} with its identity resolved against the workbook's person registry. */
45
+ export interface Mention extends MentionRef {
46
+ /**
47
+ * The mentioned identity, resolved through the workbook registry. Absent when the file names an id
48
+ * the registry does not hold (a mention left dangling by a foreign generator); {@link personId}
49
+ * still says who was meant.
50
+ */
51
+ readonly person?: Person;
52
+ }
53
+ /** One message of a {@link CommentThread}: what a single person wrote, once. */
45
54
  export interface Comment {
46
55
  /** Brace-wrapped GUID identifying this message, preserved verbatim from the file. */
47
56
  readonly id: string;
@@ -54,7 +63,7 @@ export interface Comment {
54
63
  readonly personId?: string;
55
64
  /**
56
65
  * When it was written, verbatim. Excel writes local wall-clock with fractional seconds and no
57
- * timezone (`2026-07-24T10:56:41.72`), which is not a round-trippable instant keeping the string
66
+ * timezone (`2026-07-24T10:56:41.72`), which is not a round-trippable instant. Keeping the string
58
67
  * spares the reader from inventing a zone the file never stated.
59
68
  */
60
69
  readonly date?: string;
@@ -67,7 +76,7 @@ export interface Comment {
67
76
  * The one spelling every threaded-comment identifier must take on the wire: brace-wrapped, upper-case hex.
68
77
  *
69
78
  * Verified against the OOXML schema, which pins `person/@id`, a message's `id`/`personId`/`parentId`, and a
70
- * mention's `mentionpersonId`/`mentionId` to exactly `\{[0-9A-F]{8}-…\}` a bare GUID and a lower-case one
79
+ * mention's `mentionpersonId`/`mentionId` to exactly `\{[0-9A-F]{8}-…\}`, so a bare GUID and a lower-case one
71
80
  * are each rejected outright. So this normalises rather than merely checking: a caller passing
72
81
  * `crypto.randomUUID()` gets a valid file instead of one Excel offers to repair.
73
82
  *
@@ -82,7 +91,7 @@ export declare function commentThreadGuid(value: string, what: string): string;
82
91
  * `startIndex` and `length` are `xsd:unsignedInt`, so `4294967295` validates and `4294967296` is rejected
83
92
  * as "not a valid 'UInt32' value".
84
93
  *
85
- * Wildly beyond any real message, and that is the point the ceiling exists so a value from a hostile
94
+ * Wildly beyond any real message, and that is the point: the ceiling exists so a value from a hostile
86
95
  * part can never reach the serialiser. JavaScript spells a large enough number in exponent form
87
96
  * (`String(1e21)` is `"1e+21"`), which is not a numeric literal any schema accepts, and one invalid
88
97
  * attribute is enough for Excel to offer to repair the whole conversation away.
@@ -91,8 +100,8 @@ export declare const MENTION_OFFSET_MAX = 4294967295;
91
100
  /**
92
101
  * A mention offset as the wire accepts it: a whole number within {@link MENTION_OFFSET_MAX}.
93
102
  *
94
- * The authoring path alone throws. A file's own mentions are read leniently one carrying an unusable
95
- * offset is dropped, keeping the message text and losing only the chip because a foreign generator's
103
+ * The authoring path alone throws. A file's own mentions are read leniently: one carrying an unusable
104
+ * offset is dropped, keeping the message text and losing only the chip, because a foreign generator's
96
105
  * arithmetic is not something a caller can fix, whereas their own is.
97
106
  *
98
107
  * @throws {SyntaxError} if the value is negative, fractional, or beyond the wire's ceiling.
@@ -101,8 +110,8 @@ export declare function commentThreadOffset(value: number, what: string): number
101
110
  /** A conversation anchored to one cell: what was asked, every reply, and whether it was resolved. */
102
111
  export interface CommentThread {
103
112
  /**
104
- * A1 reference of the single cell the conversation hangs off, canonicalised no `$` anchors, always
105
- * a column and a row so two anchors compare as plain strings and a writer can resolve it without
113
+ * A1 reference of the single cell the conversation hangs off, canonicalised with no `$` anchors and
114
+ * always a column and a row, so two anchors compare as plain strings and a writer can resolve it without
106
115
  * re-validating it.
107
116
  */
108
117
  readonly ref: string;
@@ -2,16 +2,16 @@ const GUID = /^\{?([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a
2
2
  export function commentThreadGuid(value, what) {
3
3
  const match = GUID.exec(value.trim());
4
4
  if (match === null) {
5
- throw new SyntaxError(`${what} must be a GUID Excel writes threaded-comment ids as ` +
6
- `"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}" but got "${value}"`);
5
+ throw new SyntaxError(`${what} must be a GUID: Excel writes threaded-comment ids as ` +
6
+ `"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}", but got "${value}"`);
7
7
  }
8
8
  return `{${match.slice(1).join('-').toUpperCase()}}`;
9
9
  }
10
10
  export const MENTION_OFFSET_MAX = 0xffff_ffff;
11
11
  export function commentThreadOffset(value, what) {
12
12
  if (!Number.isInteger(value) || value < 0 || value > MENTION_OFFSET_MAX) {
13
- throw new SyntaxError(`${what} must be a whole number between 0 and ${MENTION_OFFSET_MAX} a mention's span is ` +
14
- `written as an unsigned 32-bit integer but got ${value}`);
13
+ throw new SyntaxError(`${what} must be a whole number between 0 and ${MENTION_OFFSET_MAX}: a mention's span is ` +
14
+ `written as an unsigned 32-bit integer, but got ${value}`);
15
15
  }
16
16
  return value;
17
17
  }
@@ -2,7 +2,7 @@ import { type ConditionalFormatting } from './conditional-formatting.ts';
2
2
  export declare class ConditionalFormattingOverlay {
3
3
  #private;
4
4
  /**
5
- * Attach a conditional formatting to a target range. `formatting.ref` is an OOXML `sqref` one
5
+ * Attach a conditional formatting to a target range. `formatting.ref` is an OOXML `sqref`: one
6
6
  * range (`"A1:A10"`), a whole column, or several space-separated areas (`"A1:C1 A3:C3"`) sharing one
7
7
  * rule set. The block is stored once against the range, defensively copied so the getter never hands
8
8
  * back a reference into the caller's object.
@@ -1,6 +1,6 @@
1
1
  import type { Color, DifferentialStyle } from './style.ts';
2
2
  /**
3
- * One anchor of a colour-scale, data-bar, or icon-set scale a "conditional format value object".
3
+ * One anchor of a colour-scale, data-bar, or icon-set scale: a "conditional format value object".
4
4
  * `type` names how `value` is read: a literal `num`, a `percent`/`percentile` of the range, a
5
5
  * `formula`, or the range's own `min`/`max` (which carry no value).
6
6
  */
@@ -15,7 +15,7 @@ export interface CfValueObject {
15
15
  */
16
16
  export interface ConditionalFormattingRule {
17
17
  type: string;
18
- /** Evaluation precedence; lower wins. Excel requires one the writer supplies it when absent. */
18
+ /** Evaluation precedence; lower wins. Excel requires one, so the writer supplies it when absent. */
19
19
  priority?: number;
20
20
  /** Halt evaluation of lower-priority rules on any cell this rule matches. */
21
21
  stopIfTrue?: boolean;
@@ -58,7 +58,7 @@ export interface ConditionalFormattingRule {
58
58
  /** timePeriod window (`today`, `lastWeek`, …). */
59
59
  timePeriod?: string;
60
60
  }
61
- /** A set of rules bound to the range(s) they cover. `ref` is an OOXML `sqref` one or more
61
+ /** A set of rules bound to the range(s) they cover. `ref` is an OOXML `sqref`: one or more
62
62
  * space-separated areas (`"A1:C1 A3:C3 A5:C5"`), the shape Excel writes when one rule is applied to
63
63
  * several non-contiguous selections at once. */
64
64
  export interface ConditionalFormatting {
@@ -8,7 +8,7 @@ export declare class DataValidationOverlay {
8
8
  * through {@link at}.
9
9
  *
10
10
  * Pass `{extended: true}` to mark a rule that belongs in the 2009 extension form
11
- * (`<x14:dataValidation>`) the carrier Excel uses for a list source on another sheet and other
11
+ * (`<x14:dataValidation>`), the carrier Excel uses for a list source on another sheet and other
12
12
  * shapes the standard element cannot express. The reader sets it for a rule found in that form so a
13
13
  * round-trip writes it back there instead of silently corrupting the cross-sheet reference.
14
14
  */
@@ -6,7 +6,7 @@ export type DataValidationType = 'list' | 'whole' | 'decimal' | 'date' | 'time'
6
6
  export type DataValidationOperator = 'between' | 'notBetween' | 'equal' | 'notEqual' | 'greaterThan' | 'lessThan' | 'greaterThanOrEqual' | 'lessThanOrEqual';
7
7
  /** How Excel reacts to input that fails the rule. */
8
8
  export type DataValidationErrorStyle = 'stop' | 'warning' | 'information';
9
- /** One validation rule. `formulae` holds the operand(s) `formula1` then optional `formula2`: a
9
+ /** One validation rule. `formulae` holds the operand(s), `formula1` then optional `formula2`: a
10
10
  * numeric literal is stored as a number, while a cell reference, defined name, or list source keeps
11
11
  * its verbatim string. */
12
12
  export interface DataValidation {
@@ -22,9 +22,9 @@ export interface DataValidation {
22
22
  prompt?: string;
23
23
  promptTitle?: string;
24
24
  }
25
- /** A validation bound to the range(s) it covers. `sqref` is an OOXML `sqref` one or more
25
+ /** A validation bound to the range(s) it covers. `sqref` is an OOXML `sqref`: one or more
26
26
  * space-separated ranges. `extended` marks a rule stored in the 2009 extension form
27
- * (`<x14:dataValidation>` inside the worksheet `<extLst>`) Excel's carrier for validations a
27
+ * (`<x14:dataValidation>` inside the worksheet `<extLst>`), Excel's carrier for validations a
28
28
  * legacy `<dataValidation>` cannot express, such as a list source on another sheet. The flag is how
29
29
  * a rule read from that form remembers to be written back to it, rather than downgraded to the
30
30
  * standard element (which would corrupt a cross-sheet reference). */