@shbernal/ts-xlsx 1.3.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +20 -10
  2. package/dist/bytes.d.ts +24 -0
  3. package/dist/bytes.js +34 -0
  4. package/dist/core/address.d.ts +72 -3
  5. package/dist/core/address.js +50 -10
  6. package/dist/core/autofilter.d.ts +13 -2
  7. package/dist/core/autofilter.js +35 -10
  8. package/dist/core/cell.d.ts +17 -16
  9. package/dist/core/cell.js +3 -7
  10. package/dist/core/color-resolution.d.ts +8 -8
  11. package/dist/core/color-resolution.js +7 -8
  12. package/dist/core/column.d.ts +8 -9
  13. package/dist/core/column.js +2 -4
  14. package/dist/core/comment-thread.d.ts +32 -23
  15. package/dist/core/comment-thread.js +4 -4
  16. package/dist/core/conditional-formatting-overlay.d.ts +6 -1
  17. package/dist/core/conditional-formatting-overlay.js +11 -0
  18. package/dist/core/conditional-formatting.d.ts +48 -8
  19. package/dist/core/conditional-formatting.js +88 -0
  20. package/dist/core/data-validation-overlay.d.ts +12 -1
  21. package/dist/core/data-validation-overlay.js +21 -2
  22. package/dist/core/data-validation.d.ts +12 -5
  23. package/dist/core/data-validation.js +34 -0
  24. package/dist/core/date.d.ts +3 -3
  25. package/dist/core/formula.d.ts +6 -6
  26. package/dist/core/grid-edits.d.ts +16 -0
  27. package/dist/core/grid-edits.js +29 -11
  28. package/dist/core/grid-shift.d.ts +19 -0
  29. package/dist/core/grid-shift.js +8 -0
  30. package/dist/core/image.d.ts +39 -2
  31. package/dist/core/image.js +10 -0
  32. package/dist/core/internal.d.ts +9 -1
  33. package/dist/core/limits.d.ts +4 -4
  34. package/dist/core/merge.d.ts +17 -12
  35. package/dist/core/merge.js +36 -5
  36. package/dist/core/page-setup.d.ts +20 -6
  37. package/dist/core/page-setup.js +8 -1
  38. package/dist/core/pivot-table.d.ts +4 -4
  39. package/dist/core/pivot-table.js +29 -20
  40. package/dist/core/preserved.d.ts +3 -3
  41. package/dist/core/protection.d.ts +7 -4
  42. package/dist/core/protection.js +20 -14
  43. package/dist/core/range.d.ts +16 -14
  44. package/dist/core/range.js +5 -5
  45. package/dist/core/row-input.d.ts +9 -8
  46. package/dist/core/row-input.js +12 -10
  47. package/dist/core/row.d.ts +7 -8
  48. package/dist/core/row.js +2 -4
  49. package/dist/core/style.d.ts +41 -14
  50. package/dist/core/style.js +82 -67
  51. package/dist/core/table-style.d.ts +9 -9
  52. package/dist/core/table-style.js +1 -1
  53. package/dist/core/table.d.ts +53 -51
  54. package/dist/core/table.js +83 -41
  55. package/dist/core/theme.d.ts +16 -44
  56. package/dist/core/theme.js +6 -148
  57. package/dist/core/value.d.ts +7 -7
  58. package/dist/core/workbook-protection.d.ts +2 -2
  59. package/dist/core/workbook-styles.d.ts +26 -0
  60. package/dist/core/workbook-styles.js +48 -0
  61. package/dist/core/workbook-theme.d.ts +25 -0
  62. package/dist/core/workbook-theme.js +49 -0
  63. package/dist/core/workbook-vba.d.ts +17 -0
  64. package/dist/core/workbook-vba.js +79 -0
  65. package/dist/core/workbook.d.ts +133 -87
  66. package/dist/core/workbook.js +74 -133
  67. package/dist/core/worksheet-comments.d.ts +17 -0
  68. package/dist/core/worksheet-comments.js +70 -0
  69. package/dist/core/worksheet-model.d.ts +4 -4
  70. package/dist/core/worksheet-model.js +1 -0
  71. package/dist/core/worksheet-pictures.d.ts +29 -0
  72. package/dist/core/worksheet-pictures.js +42 -0
  73. package/dist/core/worksheet.d.ts +100 -79
  74. package/dist/core/worksheet.js +63 -142
  75. package/dist/customui/errors.d.ts +1 -1
  76. package/dist/customui/ribbon.d.ts +3 -3
  77. package/dist/customui/ribbon.js +24 -23
  78. package/dist/entries/core.d.ts +7 -7
  79. package/dist/entries/core.js +1 -1
  80. package/dist/entries/csv.d.ts +1 -1
  81. package/dist/entries/node-unavailable.d.ts +15 -0
  82. package/dist/entries/node-unavailable.js +20 -0
  83. package/dist/entries/node.d.ts +1 -0
  84. package/dist/entries/node.js +1 -0
  85. package/dist/entries/vba.d.ts +1 -1
  86. package/dist/entries/vba.js +1 -1
  87. package/dist/entries/xlsx.d.ts +1 -1
  88. package/dist/entries/xlsx.js +1 -1
  89. package/dist/errors.d.ts +12 -12
  90. package/dist/io/csv/read.js +2 -3
  91. package/dist/io/csv/write.d.ts +19 -4
  92. package/dist/io/csv/write.js +34 -4
  93. package/dist/io/opc/errors.d.ts +6 -6
  94. package/dist/io/opc/inflate.d.ts +1 -1
  95. package/dist/io/opc/inflate.js +2 -12
  96. package/dist/io/opc/namespaces.d.ts +1 -1
  97. package/dist/io/opc/read-opc.d.ts +19 -3
  98. package/dist/io/opc/read-opc.js +14 -8
  99. package/dist/io/opc/read-options.d.ts +2 -2
  100. package/dist/io/opc/rels.d.ts +11 -0
  101. package/dist/io/opc/rels.js +3 -2
  102. package/dist/io/opc/sniff-format.d.ts +2 -2
  103. package/dist/io/style/xf-style.d.ts +44 -7
  104. package/dist/io/style/xf-style.js +27 -0
  105. package/dist/io/xlsb/errors.d.ts +1 -1
  106. package/dist/io/xlsb/formula.d.ts +4 -4
  107. package/dist/io/xlsb/primitives.d.ts +8 -8
  108. package/dist/io/xlsb/ptg-functions.d.ts +1 -1
  109. package/dist/io/xlsb/read-styles.js +2 -20
  110. package/dist/io/xlsb/read.d.ts +2 -2
  111. package/dist/io/xlsx/cell-accumulator.d.ts +26 -9
  112. package/dist/io/xlsx/cell-accumulator.js +89 -46
  113. package/dist/io/xlsx/cell-value.d.ts +3 -3
  114. package/dist/io/xlsx/cell-value.js +9 -6
  115. package/dist/io/xlsx/color-xml.d.ts +1 -1
  116. package/dist/io/xlsx/color-xml.js +18 -23
  117. package/dist/io/xlsx/comments.d.ts +5 -5
  118. package/dist/io/xlsx/comments.js +17 -8
  119. package/dist/io/xlsx/conditional-formatting.d.ts +4 -4
  120. package/dist/io/xlsx/conditional-formatting.js +64 -58
  121. package/dist/io/xlsx/data-validation.d.ts +3 -3
  122. package/dist/io/xlsx/data-validation.js +35 -33
  123. package/dist/io/xlsx/errors.d.ts +3 -3
  124. package/dist/io/xlsx/hyperlinks.d.ts +4 -2
  125. package/dist/io/xlsx/hyperlinks.js +10 -18
  126. package/dist/io/xlsx/images.d.ts +1 -1
  127. package/dist/io/xlsx/images.js +25 -28
  128. package/dist/io/xlsx/namespaces.d.ts +2 -2
  129. package/dist/io/xlsx/package-plan.js +5 -2
  130. package/dist/io/xlsx/read-pivot.js +4 -13
  131. package/dist/io/xlsx/read-rows.d.ts +13 -13
  132. package/dist/io/xlsx/read-rows.js +19 -55
  133. package/dist/io/xlsx/read-shared-strings.js +7 -44
  134. package/dist/io/xlsx/read-styles.d.ts +3 -3
  135. package/dist/io/xlsx/read-styles.js +42 -66
  136. package/dist/io/xlsx/read-worksheet.js +73 -129
  137. package/dist/io/xlsx/read.d.ts +4 -6
  138. package/dist/io/xlsx/read.js +104 -142
  139. package/dist/io/xlsx/rich-runs.d.ts +41 -7
  140. package/dist/io/xlsx/rich-runs.js +94 -30
  141. package/dist/io/xlsx/rich-text.d.ts +1 -1
  142. package/dist/io/xlsx/shared-strings.d.ts +2 -2
  143. package/dist/io/xlsx/sheet-properties.d.ts +1 -1
  144. package/dist/io/xlsx/sheet-properties.js +14 -10
  145. package/dist/io/xlsx/styles.d.ts +9 -22
  146. package/dist/io/xlsx/styles.js +65 -89
  147. package/dist/io/xlsx/tables.d.ts +2 -2
  148. package/dist/io/xlsx/tables.js +20 -25
  149. package/dist/io/xlsx/theme-xml.d.ts +37 -0
  150. package/dist/io/xlsx/theme-xml.js +145 -0
  151. package/dist/io/xlsx/threaded-comments.d.ts +18 -27
  152. package/dist/io/xlsx/threaded-comments.js +26 -38
  153. package/dist/io/xlsx/workbook-xml.js +14 -8
  154. package/dist/io/xlsx/worksheet-xml.d.ts +2 -2
  155. package/dist/io/xlsx/worksheet-xml.js +40 -48
  156. package/dist/io/xlsx/write-stream.d.ts +23 -16
  157. package/dist/io/xlsx/write-stream.js +13 -14
  158. package/dist/io/xlsx/write.d.ts +8 -7
  159. package/dist/io/xlsx/write.js +6 -3
  160. package/dist/io/xlsx/x14-ext.d.ts +1 -1
  161. package/dist/sha512.d.ts +2 -0
  162. package/dist/sha512.js +146 -0
  163. package/dist/vba/bytes.d.ts +11 -0
  164. package/dist/vba/bytes.js +29 -0
  165. package/dist/vba/cfb-writer.d.ts +1 -1
  166. package/dist/vba/cfb.d.ts +1 -1
  167. package/dist/vba/cfb.js +15 -51
  168. package/dist/vba/codepage.d.ts +2 -2
  169. package/dist/vba/dir-records.d.ts +53 -0
  170. package/dist/vba/dir-records.js +28 -0
  171. package/dist/vba/errors.d.ts +2 -2
  172. package/dist/vba/index.d.ts +2 -2
  173. package/dist/vba/index.js +2 -2
  174. package/dist/vba/ms-ovba.d.ts +1 -1
  175. package/dist/vba/ms-ovba.js +1 -3
  176. package/dist/vba/project-editor.d.ts +9 -9
  177. package/dist/vba/project-editor.js +6 -44
  178. package/dist/vba/project.d.ts +5 -5
  179. package/dist/vba/project.js +3 -28
  180. package/dist/vba/vba-encoding.d.ts +1 -1
  181. package/dist/xml/errors.d.ts +1 -1
  182. package/dist/xml/xml-read.d.ts +98 -9
  183. package/dist/xml/xml-read.js +70 -0
  184. package/dist/xml/xml.d.ts +74 -10
  185. package/dist/xml/xml.js +39 -7
  186. package/package.json +59 -36
@@ -2,7 +2,7 @@
2
2
  * One outbound relationship of a {@link PreservedPart}: the id it carries inside its own rels part,
3
3
  * the relationship Type URI, and its target. An internal relationship's `targetPath` is the resolved
4
4
  * package path of the part it points at (the writer re-numbers and rewires it); an `external`
5
- * relationship's `targetPath` is the raw `Target` verbatim (a linked workbook's path or URL) it is
5
+ * relationship's `targetPath` is the raw `Target` verbatim (a linked workbook's path or URL). It is
6
6
  * outside the package, so it is emitted unchanged with `TargetMode="External"` and never remapped.
7
7
  * Preserving external relationships is what keeps an `externalLink` part's pointer to its source
8
8
  * workbook alive, so a round-trip does not orphan the `[n]` external references formulas resolve through.
@@ -26,7 +26,7 @@ export interface PreservedPart {
26
26
  readonly rels: readonly PreservedRelationship[];
27
27
  }
28
28
  /**
29
- * A worksheet-level reference to package content the model does not model preserved verbatim across
29
+ * A worksheet-level reference to package content the model does not model, preserved verbatim across
30
30
  * a round-trip instead of being silently dropped. `element` is the worksheet child that wires the
31
31
  * reference (`<drawing>` for a vector-shape drawing, `<legacyDrawingHF>` for a header/footer image),
32
32
  * or `undefined` when the sheet wires it by relationship alone (a pivot table or slicer Excel
@@ -43,7 +43,7 @@ export interface PreservedWorksheetReference {
43
43
  }
44
44
  /**
45
45
  * A package-root reference to content the model does not model, wired from the package's own
46
- * `_rels/.rels` rather than the workbook part's rels the ribbon-customisation parts
46
+ * `_rels/.rels` rather than the workbook part's rels: the ribbon-customisation parts
47
47
  * (`customUI/customUI14.xml`), custom document properties (`docProps/custom.xml`), a thumbnail, and
48
48
  * anything else hung off the root. The writer regenerates the root rels for the parts it models
49
49
  * (workbook, core/app properties), so these would be dropped unless captured here and re-declared.
@@ -33,7 +33,7 @@ export interface SheetProtectionOptions extends SheetProtectionFlags {
33
33
  }
34
34
  /**
35
35
  * A password-derived credential, in OOXML's agile form: the hash algorithm, the salted
36
- * iterated hash of the password, the salt, and the iteration count everything a consumer
36
+ * iterated hash of the password, the salt, and the iteration count: everything a consumer
37
37
  * needs to verify a supplied password without the password ever being stored.
38
38
  */
39
39
  export interface SheetProtectionCredential {
@@ -50,8 +50,8 @@ export interface SheetProtection {
50
50
  /**
51
51
  * The OOXML encoding table for the protection flags: each `<sheetProtection>` attribute paired
52
52
  * with whether that operation is *forbidden by default* once a sheet is protected. Both directions
53
- * key off this one list the writer turns an author allow-flag into an attribute (omitting values
54
- * equal to the default), the reader turns an attribute back into an allow-flag so serialization
53
+ * key off this one list. The writer turns an author allow-flag into an attribute (omitting values
54
+ * equal to the default) and the reader turns an attribute back into an allow-flag, so serialization
55
55
  * and deserialization can never fall out of step. Most editing operations default to forbidden
56
56
  * under protection; selecting cells and the object/scenario operations default to permitted.
57
57
  */
@@ -61,7 +61,10 @@ export declare const SHEET_PROTECTION_FLAGS: readonly {
61
61
  }[];
62
62
  /**
63
63
  * Derive a fresh {@link SheetProtectionCredential} for a password. Each call generates a new
64
- * random salt, so protecting two sheets with the same password yields different credentials
64
+ * random salt, so protecting two sheets with the same password yields different credentials:
65
65
  * the salt is real randomness, not a stub.
66
+ *
67
+ * The spin loop is the cost of the scheme rather than of this implementation: Excel's default
68
+ * 100000 iterations is 100000 chained SHA-512 digests, and no shortcut through them exists.
66
69
  */
67
70
  export declare function deriveCredential(password: string, spinCount?: number): SheetProtectionCredential;
@@ -1,4 +1,5 @@
1
- import { createHash, randomBytes } from 'node:crypto';
1
+ import { concat, toBase64 } from '../bytes.js';
2
+ import { sha512 } from '../sha512.js';
2
3
  export const SHEET_PROTECTION_FLAGS = [
3
4
  { key: 'formatCells', defaultForbidden: true },
4
5
  { key: 'formatColumns', defaultForbidden: true },
@@ -17,26 +18,31 @@ export const SHEET_PROTECTION_FLAGS = [
17
18
  { key: 'selectUnlockedCells', defaultForbidden: false },
18
19
  ];
19
20
  const ALGORITHM_NAME = 'SHA-512';
20
- const HASH = 'sha512';
21
21
  const DEFAULT_SPIN_COUNT = 100000;
22
22
  const SALT_BYTES = 16;
23
23
  export function deriveCredential(password, spinCount = DEFAULT_SPIN_COUNT) {
24
- const salt = randomBytes(SALT_BYTES);
25
- const secret = Buffer.from(password, 'utf16le');
26
- let hash = createHash(HASH)
27
- .update(Buffer.concat([salt, secret]))
28
- .digest();
29
- const iteration = Buffer.alloc(4);
24
+ const salt = crypto.getRandomValues(new Uint8Array(SALT_BYTES));
25
+ let hash = sha512(concat([salt, utf16le(password)]));
26
+ const spun = new Uint8Array(hash.length + 4);
27
+ const counter = new DataView(spun.buffer, hash.length, 4);
30
28
  for (let i = 0; i < spinCount; i++) {
31
- iteration.writeUInt32LE(i, 0);
32
- hash = createHash(HASH)
33
- .update(Buffer.concat([hash, iteration]))
34
- .digest();
29
+ spun.set(hash, 0);
30
+ counter.setUint32(0, i, true);
31
+ hash = sha512(spun);
35
32
  }
36
33
  return {
37
34
  algorithmName: ALGORITHM_NAME,
38
- hashValue: hash.toString('base64'),
39
- saltValue: salt.toString('base64'),
35
+ hashValue: toBase64(hash),
36
+ saltValue: toBase64(salt),
40
37
  spinCount,
41
38
  };
42
39
  }
40
+ function utf16le(text) {
41
+ const bytes = new Uint8Array(text.length * 2);
42
+ for (let i = 0; i < text.length; i++) {
43
+ const unit = text.charCodeAt(i);
44
+ bytes[i * 2] = unit & 0xff;
45
+ bytes[i * 2 + 1] = unit >>> 8;
46
+ }
47
+ return bytes;
48
+ }
@@ -1,18 +1,20 @@
1
+ import { type GridRect } from './address.ts';
1
2
  import { type Cell } from './cell.ts';
2
3
  import { type Alignment, type Border, type CellStyle, type Fill, type Font, type Protection } from './style.ts';
3
4
  import type { Worksheet } from './worksheet.ts';
4
5
  /**
5
6
  * A rectangular block of a worksheet's cells: `sheet.getRange('B2:D5')`.
6
7
  *
7
- * Cheap and stateless constructing one creates no cells and does not extend the used range.
8
+ * Cheap and stateless: constructing one creates no cells and does not extend the used range.
8
9
  * {@link addresses} walks the block without materialising anything; {@link cells} reports only what
9
10
  * already exists.
10
11
  *
11
12
  * Bounds are **inclusive first/last**, never start-and-count. That is the convention every
12
13
  * range-shaped accessor in this library follows, so the three axes cannot disagree about what a
13
- * pair of numbers means.
14
+ * pair of numbers means; the handle is declared to satisfy {@link GridRect}, which is that
15
+ * convention written down once.
14
16
  */
15
- export declare class Range {
17
+ export declare class Range implements GridRect {
16
18
  #private;
17
19
  /** 1-based row of the block's top edge. Fixed for this handle's lifetime. */
18
20
  readonly top: number;
@@ -23,7 +25,7 @@ export declare class Range {
23
25
  /** 1-based column of the block's right edge, inclusive. */
24
26
  readonly right: number;
25
27
  /**
26
- * Build a handle from inclusive corners, in any order `(5, 4, 2, 2)` and `(2, 2, 5, 4)` name the
28
+ * Build a handle from inclusive corners, in any order: `(5, 4, 2, 2)` and `(2, 2, 5, 4)` name the
27
29
  * same block, exactly as `D5:B2` and `B2:D5` do. Prefer {@link Worksheet.getRange}.
28
30
  *
29
31
  * @throws {RangeError} if a corner is not a positive integer or falls outside the sheet's bounds.
@@ -31,30 +33,30 @@ export declare class Range {
31
33
  constructor(sheet: Worksheet, top: number, left: number, bottom: number, right: number);
32
34
  /** The worksheet this block belongs to. */
33
35
  get sheet(): Worksheet;
34
- /** Canonical `tl:br` A1 form `"B2:D5"`. A one-cell block still reads as `"B2:B2"`. */
36
+ /** Canonical `tl:br` A1 form, `"B2:D5"`. A one-cell block still reads as `"B2:B2"`. */
35
37
  get address(): string;
36
38
  /** Rows spanned, inclusive of both edges. */
37
39
  get rowCount(): number;
38
40
  /** Columns spanned, inclusive of both edges. */
39
41
  get columnCount(): number;
40
- /** Cells the block covers `rowCount * columnCount`, whether or not they exist yet. */
42
+ /** Cells the block covers: `rowCount * columnCount`, whether or not they exist yet. */
41
43
  get cellCount(): number;
42
44
  /** Whether a 1-based position falls inside the block. */
43
45
  contains(row: number, col: number): boolean;
44
46
  /**
45
47
  * Every address the block covers, row-major (`B2`, `C2`, `D2`, `B3`, …). Materialises nothing, so
46
- * this is the cheap way to walk a large block and, being a generator, it can be abandoned
48
+ * this is the cheap way to walk a large block, and, being a generator, it can be abandoned
47
49
  * part-way without having built the whole list.
48
50
  */
49
51
  addresses(): IterableIterator<string>;
50
52
  /**
51
53
  * The block's **materialised** cells, row-major. Sparse: a position nothing has ever written to is
52
54
  * simply absent, which is what distinguishes "never written" from a cell holding `null`. Reading
53
- * this creates nothing mirroring {@link Column.cells}.
55
+ * this creates nothing, mirroring {@link Column.cells}.
54
56
  */
55
57
  get cells(): readonly Cell[];
56
58
  /**
57
- * The block's style, facet by facet the counterpart of {@link Cell.style} over a rectangle, with
59
+ * The block's style, facet by facet: the counterpart of {@link Cell.style} over a rectangle, with
58
60
  * the same semantics in both directions.
59
61
  *
60
62
  * **Reading** reports a facet only when *every* position in the block carries a structurally
@@ -62,13 +64,13 @@ export declare class Range {
62
64
  * through this handle therefore reads back what was written; a block whose cells disagree says so
63
65
  * rather than picking a corner's answer and passing it off as the whole.
64
66
  *
65
- * **Writing** lays each facet the payload names onto every cell, leaving facets it omits untouched
66
- * exactly what `cell.style = {...}` does, so this composes with prior styling instead of clearing
67
- * it. Use {@link clearStyle} first for a wholesale replace.
67
+ * **Writing** lays each facet the payload names onto every cell, leaving facets it omits
68
+ * untouched, exactly what `cell.style = {...}` does, so this composes with prior styling instead
69
+ * of clearing it. Use {@link clearStyle} first for a wholesale replace.
68
70
  *
69
71
  * Writing **materialises** every position in the block, because a styled-but-valueless cell is the
70
72
  * only way an empty cell renders with a fill: skipping the holes would leave gaps in a header band.
71
- * The cost is bounded by construction a range is always a bounded rectangle, and whole-axis
73
+ * The cost is bounded by construction: a range is always a bounded rectangle, and whole-axis
72
74
  * styling belongs to {@link Worksheet.getColumn}/{@link Worksheet.getRow} instead. {@link cellCount}
73
75
  * is the exact number of cells a write will create.
74
76
  */
@@ -77,7 +79,7 @@ export declare class Range {
77
79
  /**
78
80
  * Strip every style facet from every cell in the block, leaving values untouched. Assigning
79
81
  * {@link style} composes, so this is how a wholesale replace is said: `clearStyle()` then assign.
80
- * Materialises nothing a cell that does not exist carries no style to clear.
82
+ * Materialises nothing: a cell that does not exist carries no style to clear.
81
83
  */
82
84
  clearStyle(): void;
83
85
  /** Fill applied to every cell in the block; `undefined` when they do not all agree. */
@@ -154,22 +154,22 @@ function facetKey(value) {
154
154
  }
155
155
  function checkBound(axis, value, max) {
156
156
  if (!Number.isInteger(value) || value < 1) {
157
- throw new RangeError(`${axis} ${value} is out of bounds ${axis}s start at 1`);
157
+ throw new RangeError(`${axis} ${value} is out of bounds: ${axis}s start at 1`);
158
158
  }
159
159
  if (value > max) {
160
- throw new RangeError(`${axis} ${value} is out of bounds the sheet ends at ${max}`);
160
+ throw new RangeError(`${axis} ${value} is out of bounds: the sheet ends at ${max}`);
161
161
  }
162
162
  }
163
163
  export function rangeFrom(sheet, reference) {
164
164
  const { top, left, bottom, right, sheetName, dimensions } = decodeRange(reference);
165
165
  if (sheetName !== undefined && sheetName.toLowerCase() !== sheet.name.toLowerCase()) {
166
- throw new SyntaxError(`"${reference}" names worksheet "${sheetName}", not "${sheet.name}" a range belongs to the sheet it came from`);
166
+ throw new SyntaxError(`"${reference}" names worksheet "${sheetName}", not "${sheet.name}": a range belongs to the sheet it came from`);
167
167
  }
168
168
  if (top === undefined || bottom === undefined) {
169
- throw new SyntaxError(`"${reference}" spans whole columns (${dimensions}) style them through getColumn(n), which says the same thing in one attribute instead of ${MAX_ROW} cells`);
169
+ throw new SyntaxError(`"${reference}" spans whole columns (${dimensions}): style them through getColumn(n), which says the same thing in one attribute instead of ${MAX_ROW} cells`);
170
170
  }
171
171
  if (left === undefined || right === undefined) {
172
- throw new SyntaxError(`"${reference}" spans whole rows (${dimensions}) style them through getRow(n), which says the same thing in one attribute instead of ${MAX_COLUMN} cells`);
172
+ throw new SyntaxError(`"${reference}" spans whole rows (${dimensions}): style them through getRow(n), which says the same thing in one attribute instead of ${MAX_COLUMN} cells`);
173
173
  }
174
174
  return new Range(sheet, top, left, bottom, right);
175
175
  }
@@ -1,14 +1,21 @@
1
1
  import { Cell } from './cell.ts';
2
2
  import type { CellValue } from './value.ts';
3
3
  import type { ColumnProperties, RowInput } from './worksheet.ts';
4
+ /**
5
+ * Resolve a positional array of values to the (1-based index, value) placements it names: the first
6
+ * value lands at 1, and a hole or an explicit `undefined` places nothing, leaving that line
7
+ * untouched. The index is a column on the row axis and a row on the column axis; the reading is the
8
+ * same either way.
9
+ */
10
+ export declare function positionalPlacements(values: readonly (CellValue | undefined)[]): Array<[number, CellValue]>;
4
11
  /**
5
12
  * Resolve a `RowInput` to the (1-based column, value) placements it names. A positional array maps
6
13
  * each value to its column from A, skipping a hole or an explicit `undefined` so that column is left
7
14
  * untouched; a keyed object maps each value under the column carrying the matching key.
8
15
  *
9
16
  * `Array.isArray`, not `instanceof Array`: a row built in another realm (a vm context, a browser
10
- * iframe) is still an array but fails the identity check, and would then be walked as a keyed object —
11
- * placing nothing.
17
+ * iframe) is still an array but fails the identity check, and would then be walked as a keyed
18
+ * object, placing nothing.
12
19
  */
13
20
  export declare function rowPlacements(values: RowInput, columns: ReadonlyMap<number, ColumnProperties>): Array<[number, CellValue]>;
14
21
  /**
@@ -16,9 +23,3 @@ export declare function rowPlacements(values: RowInput, columns: ReadonlyMap<num
16
23
  * keyed by column. The grid-edit machinery then splices this map into place.
17
24
  */
18
25
  export declare function buildRowCells(number: number, values: RowInput, columns: ReadonlyMap<number, ColumnProperties>): Map<number, Cell>;
19
- /**
20
- * The 1-based index of the column carrying `key` (see {@link ColumnProperties.key}).
21
- *
22
- * @throws {AuthoringError} if no column declares that key.
23
- */
24
- export declare function columnIndexByKey(columns: ReadonlyMap<number, ColumnProperties>, key: string): number;
@@ -1,14 +1,16 @@
1
1
  import { AuthoringError } from '../errors.js';
2
2
  import { Cell } from './cell.js';
3
+ export function positionalPlacements(values) {
4
+ const placements = [];
5
+ values.forEach((value, index) => {
6
+ if (value !== undefined)
7
+ placements.push([index + 1, value]);
8
+ });
9
+ return placements;
10
+ }
3
11
  export function rowPlacements(values, columns) {
4
- if (Array.isArray(values)) {
5
- const placements = [];
6
- values.forEach((value, index) => {
7
- if (value !== undefined)
8
- placements.push([index + 1, value]);
9
- });
10
- return placements;
11
- }
12
+ if (Array.isArray(values))
13
+ return positionalPlacements(values);
12
14
  return Object.entries(values).map(([key, value]) => [columnIndexByKey(columns, key), value]);
13
15
  }
14
16
  export function buildRowCells(number, values, columns) {
@@ -20,10 +22,10 @@ export function buildRowCells(number, values, columns) {
20
22
  }
21
23
  return row;
22
24
  }
23
- export function columnIndexByKey(columns, key) {
25
+ function columnIndexByKey(columns, key) {
24
26
  for (const [index, properties] of columns) {
25
27
  if (properties.key === key)
26
28
  return index;
27
29
  }
28
- throw new AuthoringError(`no column is keyed ${JSON.stringify(key)} set getColumn(n).key first`);
30
+ throw new AuthoringError(`no column is keyed ${JSON.stringify(key)}: set getColumn(n).key first`);
29
31
  }
@@ -1,4 +1,5 @@
1
1
  import type { Cell } from './cell.ts';
2
+ import { type AssertNever } from './internal.ts';
2
3
  import type { Fill } from './style.ts';
3
4
  import type { CellValue } from './value.ts';
4
5
  import type { RowProperties, Worksheet } from './worksheet.ts';
@@ -6,10 +7,10 @@ export declare class Row {
6
7
  #private;
7
8
  /** 1-based row number. Fixed for this handle's lifetime. */
8
9
  readonly number: number;
9
- /** @throws {RangeError} if the number is not a positive integer. */
10
+ /** @throws {RangeError} unless the number is an integer within Excel's row grid (1..1048576). */
10
11
  constructor(sheet: Worksheet, number: number);
11
12
  /**
12
- * The row's format record if it has one, else `undefined` a read that never fabricates, so a
13
+ * The row's format record if it has one, else `undefined`: a read that never fabricates, so a
13
14
  * serializer can ask every row it visits whether there are attributes to emit without giving each
14
15
  * one an empty record. Read-only on purpose: {@link height} and its siblings are how a row is
15
16
  * formatted, and they create the record on first write.
@@ -21,7 +22,7 @@ export declare class Row {
21
22
  * Not bounded here, deliberately: {@link MAX_ROW_HEIGHT} is what Excel accepts *being set*, but
22
23
  * the schema puts no ceiling on `ht` and this setter is also how the reader loads a foreign
23
24
  * file, so refusing a taller row would mean refusing a file Excel opens clean. Check against the
24
- * constant when authoring Excel silently clamps a taller row on read, so a height above it is
25
+ * constant when authoring: Excel silently clamps a taller row on read, so a height above it is
25
26
  * one you state and do not get.
26
27
  */
27
28
  get height(): number | undefined;
@@ -51,7 +52,7 @@ export declare class Row {
51
52
  getCell(column: number | string): Cell;
52
53
  /**
53
54
  * The row's materialised cells in ascending column order. Sparse: a column never written to has
54
- * no cell here, and the array is a fresh snapshot of *which* cells exist the cells themselves
55
+ * no cell here, and the array is a fresh snapshot of *which* cells exist; the cells themselves
55
56
  * are the live ones.
56
57
  */
57
58
  get cells(): readonly Cell[];
@@ -60,16 +61,15 @@ export declare class Row {
60
61
  * a column with no cell is a hole, which is what distinguishes "never written" from a cell
61
62
  * holding `null`.
62
63
  *
63
- * Assigning places each value it names and leaves every other column untouched a hole or an
64
+ * Assigning places each value it names and leaves every other column untouched: a hole or an
64
65
  * explicit `undefined` skips that column, and a shorter array does not clear the tail. These are
65
66
  * {@link Worksheet.addRow}'s rules, deliberately: `values` is that same row shape addressed by
66
- * number rather than appended. To *replace* a row, including clearing what it held, splice it
67
+ * number rather than appended. To *replace* a row, including clearing what it held, splice it:
67
68
  * `sheet.spliceRows(n, 1, values)`.
68
69
  */
69
70
  get values(): (CellValue | undefined)[];
70
71
  set values(values: (CellValue | undefined)[]);
71
72
  }
72
- type AssertNever<T extends never> = T;
73
73
  /**
74
74
  * Compile-time proof that {@link Row} mirrors every {@link RowProperties} field. A field added to
75
75
  * the record without an accessor here resolves this to that field's name, which does not satisfy
@@ -78,4 +78,3 @@ type AssertNever<T extends never> = T;
78
78
  * would simply never mention it.
79
79
  */
80
80
  export type EveryRowPropertyIsMirrored = AssertNever<Exclude<keyof RowProperties, keyof Row & keyof RowProperties>>;
81
- export {};
package/dist/core/row.js CHANGED
@@ -1,12 +1,10 @@
1
- import { columnToNumber, encodeAddress } from './address.js';
1
+ import { assertRowInBounds, columnToNumber, encodeAddress } from './address.js';
2
2
  import { INTERNAL } from './internal.js';
3
3
  export class Row {
4
4
  #sheet;
5
5
  number;
6
6
  constructor(sheet, number) {
7
- if (!Number.isInteger(number) || number < 1) {
8
- throw new RangeError(`row ${number} is out of bounds — rows start at 1`);
9
- }
7
+ assertRowInBounds(number);
10
8
  this.#sheet = sheet;
11
9
  this.number = number;
12
10
  }
@@ -14,6 +14,26 @@ export interface Color {
14
14
  * automatic placeholder `indexed="64"`; the visible colour lives on `fgColor`. */
15
15
  readonly indexed?: number;
16
16
  }
17
+ /**
18
+ * Parse a colour written in any of the shapes the API accepts into the bare 8-hex ARGB OOXML wants,
19
+ * or `undefined` if it is not one of them.
20
+ *
21
+ * Two conveniences are accepted, and nothing else: a leading `#` is a CSS habit and is stripped
22
+ * (`'#FFBFBFBF'` → `'FFBFBFBF'`), and a 6-hex RGB is promoted with a fully-opaque alpha (`'00FF00'` →
23
+ * `'FF00FF00'`), the common case of a colour written without its alpha channel. Casing is preserved,
24
+ * so a foreign file's lowercase value round-trips as it arrived.
25
+ *
26
+ * This states the grammar once for both directions. What a malformed value *means* differs by
27
+ * direction and is decided by the caller: on read it is foreign data and resolves to nothing, on
28
+ * write it is a caller's bug and throws (see `normalizeArgb` in `io/xlsx/color-xml.ts`). Neither can
29
+ * be a silently half-parsed value, because Excel does not report a malformed `rgb` at all; it
30
+ * renders flat black.
31
+ *
32
+ * `normalizeThemeColor` in `core/theme.ts` asks a similar question and stays separate: a theme
33
+ * slot is `<a:srgbClr val>`, which DrawingML gives no alpha channel, so the two differ in exactly the
34
+ * thing this one exists to add.
35
+ */
36
+ export declare function parseArgb(value: string): string | undefined;
17
37
  /**
18
38
  * Fill pattern kinds, as OOXML's `ST_PatternType` enumerates them. `none` is the
19
39
  * absence of a fill; `solid` paints the whole cell with the foreground colour (the
@@ -24,7 +44,7 @@ export type FillPatternType = 'none' | 'solid' | 'gray125' | 'darkGray' | 'mediu
24
44
  export declare function isFillPatternType(value: string): value is FillPatternType;
25
45
  /**
26
46
  * A pattern fill. For a `solid` fill the visible colour is the pattern *foreground*
27
- * (`fgColor`) OOXML's counter-intuitive rule while `bgColor` is the automatic
47
+ * (`fgColor`), OOXML's counter-intuitive rule, while `bgColor` is the automatic
28
48
  * indexed placeholder.
29
49
  */
30
50
  export interface PatternFill {
@@ -89,13 +109,13 @@ export interface Border {
89
109
  export type FontVerticalAlignment = 'superscript' | 'subscript';
90
110
  /** Narrow a raw `<vertAlign val>` token to a known {@link FontVerticalAlignment}. */
91
111
  export declare function isFontVerticalAlignment(value: string): value is FontVerticalAlignment;
92
- /** The theme-font role a `<scheme val>` names `"minor"`/`"major"` bind the font to whichever
112
+ /** The theme-font role a `<scheme val>` names: `"minor"`/`"major"` bind the font to whichever
93
113
  * face the workbook theme assigns that role, `"none"` leaves it a literal, unbound face. */
94
114
  export type FontScheme = 'minor' | 'major' | 'none';
95
115
  /** Narrow a raw `<scheme val>` token to a known {@link FontScheme}. */
96
116
  export declare function isFontScheme(value: string): value is FontScheme;
97
117
  /** A font, as it applies to a cell or a single rich-text run. Every facet is optional and
98
- * independent, like {@link Border}/{@link Alignment}/{@link Protection} a font sets only the
118
+ * independent, like {@link Border}/{@link Alignment}/{@link Protection}: a font sets only the
99
119
  * facets it overrides (Excel's own default font backs the rest), so no consumer ever holds every
100
120
  * field populated at once. */
101
121
  export interface Font {
@@ -125,7 +145,7 @@ export type VerticalAlignment = 'top' | 'center' | 'bottom' | 'justify' | 'distr
125
145
  export declare function isVerticalAlignment(value: string): value is VerticalAlignment;
126
146
  /**
127
147
  * A cell's alignment. Every facet is optional and independent; an absent facet means the cell
128
- * takes Excel's default for it. The boolean flags default to off a cell that never enabled
148
+ * takes Excel's default for it. The boolean flags default to off, so a cell that never enabled
129
149
  * `wrapText`/`shrinkToFit` must never read back with them on. `textRotation` is in degrees
130
150
  * (0–180, where 91–180 encodes -1° to -90°); `indent` is a non-negative indent level.
131
151
  */
@@ -139,7 +159,7 @@ export interface Alignment {
139
159
  readonly readingOrder?: number;
140
160
  }
141
161
  /**
142
- * A cell's protection state, enforced only when the worksheet itself is protected the flags
162
+ * A cell's protection state, enforced only when the worksheet itself is protected. The flags
143
163
  * do nothing on an unprotected sheet. `locked` defaults to TRUE in OOXML (every cell is locked
144
164
  * unless told otherwise), so the meaningful, information-carrying state is an explicitly
145
165
  * *unlocked* cell (`locked: false`); marking a cell locked merely restates the default and
@@ -151,14 +171,14 @@ export interface Protection {
151
171
  readonly hidden?: boolean;
152
172
  }
153
173
  /**
154
- * The six direct-format facets a cell can carry its fill, number format, font, border, alignment,
174
+ * The six direct-format facets a cell can carry: its fill, number format, font, border, alignment,
155
175
  * and protection. Every facet is optional and independent: a cell sets only the facets it overrides
156
176
  * and inherits the rest. This one tuple is the unit of style throughout the library, so the
157
- * interfaces that carry a cell's formatting compose it rather than re-listing the fields a column,
177
+ * interfaces that carry a cell's formatting compose it rather than re-listing the fields: a column,
158
178
  * table column, or named style whose facets *default* the cells that leave them unset (see
159
179
  * {@link ColumnProperties}, {@link NamedCellStyle}), and a cell's own resolved format. Because they
160
180
  * share this type, "add a facet" is a single edit here and the compiler enforces that no read/write
161
- * path silently drops one the round-trip symmetry the merge-loss contract depends on.
181
+ * path silently drops one, the round-trip symmetry the merge-loss contract depends on.
162
182
  */
163
183
  export interface CellStyle {
164
184
  fill?: Fill | undefined;
@@ -171,15 +191,22 @@ export interface CellStyle {
171
191
  /** The names of the {@link CellStyle} facets, for helpers that copy the tuple facet-by-facet. */
172
192
  export declare const CELL_STYLE_FACETS: (keyof CellStyle)[];
173
193
  /**
174
- * Copy each present facet of `source` onto `target`, leaving facets `source` omits untouched the
194
+ * Copy each present facet of `source` onto `target`, leaving facets `source` omits untouched: the
175
195
  * plain-record counterpart to a cell's `applyCellStyle`, for the {@link CellStyle}-shaped targets a
176
196
  * `Cell`'s setters don't reach (a column's cell-defaults, a named style being assembled on read).
177
197
  * Driven by {@link CELL_STYLE_FACETS}, so a facet added to the tuple reaches these paths the moment
178
- * it joins the same single-point-of-change the cell path gets.
198
+ * it joins, the same single-point-of-change the cell path gets.
179
199
  */
180
200
  export declare function assignStyleFacets(target: CellStyle, source: Readonly<CellStyle>): void;
181
201
  /**
182
- * A named cell style the OOXML `cellStyleXfs`/`cellStyles` layer. A spreadsheet applies a built-in
202
+ * The {@link CellStyle} facets of `source` as a plain tuple of their own, for a source that carries
203
+ * more than the facets (a column's properties also hold width, hidden and outline state). The
204
+ * projection counterpart to {@link assignStyleFacets}, driven by the same list, so a facet added to
205
+ * the tuple reaches a `<col>` style without anyone remembering to widen a literal.
206
+ */
207
+ export declare function pickStyleFacets(source: Readonly<CellStyle>): CellStyle;
208
+ /**
209
+ * A named cell style: the OOXML `cellStyleXfs`/`cellStyles` layer. A spreadsheet applies a built-in
183
210
  * or custom style (e.g. "Normal", "Accent1") whose visual facets live in this shared, named layer
184
211
  * rather than on each cell's direct format; a cell links to it and inherits any facet the direct
185
212
  * format leaves unset. The facets are a cell's own (see {@link CellStyle}); `name` is the style's
@@ -192,7 +219,7 @@ export type NamedCellStyle = Readonly<CellStyle> & {
192
219
  /**
193
220
  * A differential style (OOXML CT_Dxf): formatting laid *over* whatever a cell already carries. Only
194
221
  * the facets present override; the rest of the cell's own style shows through. It carries the subset
195
- * of the cell-style facets (see {@link CellStyle}) a `<dxf>` can express font, number format, fill,
222
+ * of the cell-style facets (see {@link CellStyle}) a `<dxf>` can express: font, number format, fill,
196
223
  * and border.
197
224
  *
198
225
  * Differential styles live in one workbook-level table (`<dxfs>`) that several features index into:
@@ -214,7 +241,7 @@ export type DifferentialStyle = Pick<CellStyle, 'font' | 'numFmt' | 'fill' | 'bo
214
241
  * Each entry of {@link styles} is one `<tableStyle>…</tableStyle>` fragment kept verbatim, for the
215
242
  * same reason a `<dxf>` is: a `tableStyleElement`'s `dxfId` indexes the differential-style table,
216
243
  * which the writer re-emits **at its original indices**, so the references stay valid without
217
- * reparsing anything. That index-stability is load-bearing renumbering the dxf table would
244
+ * reparsing anything. That index-stability is load-bearing: renumbering the dxf table would
218
245
  * silently re-point every preserved table style at a different format.
219
246
  *
220
247
  * The two default names are ordinary strings, not fragments: they are re-escaped on write, so they
@@ -230,7 +257,7 @@ export interface TableStyleTable {
230
257
  *
231
258
  * Carrying a fragment verbatim carries its *prefixes* too. Excel stamps a revision id
232
259
  * (`xr9:uid="{…}"`) on every `<tableStyle>` it writes, so a fragment re-emitted under a
233
- * `<styleSheet>` that declares only the default namespace is not namespace-well-formed no
260
+ * `<styleSheet>` that declares only the default namespace is not namespace-well-formed, and no
234
261
  * consumer can parse the part at all, which is a far louder failure than the dropped table style
235
262
  * this preservation exists to prevent. The writer re-declares each prefix on `<styleSheet>` and
236
263
  * re-states the ignorable ones, exactly as the source did.