@shbernal/ts-xlsx 1.3.1 → 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 (155) hide show
  1. package/dist/core/address.d.ts +49 -2
  2. package/dist/core/address.js +31 -3
  3. package/dist/core/autofilter.d.ts +2 -2
  4. package/dist/core/cell.d.ts +17 -16
  5. package/dist/core/cell.js +3 -7
  6. package/dist/core/color-resolution.d.ts +7 -7
  7. package/dist/core/column.d.ts +8 -9
  8. package/dist/core/column.js +2 -4
  9. package/dist/core/comment-thread.d.ts +32 -23
  10. package/dist/core/comment-thread.js +4 -4
  11. package/dist/core/conditional-formatting-overlay.d.ts +1 -1
  12. package/dist/core/conditional-formatting.d.ts +3 -3
  13. package/dist/core/data-validation-overlay.d.ts +1 -1
  14. package/dist/core/data-validation.d.ts +3 -3
  15. package/dist/core/date.d.ts +3 -3
  16. package/dist/core/formula.d.ts +6 -6
  17. package/dist/core/grid-edits.js +3 -3
  18. package/dist/core/image.d.ts +37 -2
  19. package/dist/core/image.js +6 -0
  20. package/dist/core/internal.d.ts +9 -1
  21. package/dist/core/limits.d.ts +4 -4
  22. package/dist/core/merge.d.ts +6 -12
  23. package/dist/core/merge.js +0 -3
  24. package/dist/core/page-setup.d.ts +4 -4
  25. package/dist/core/pivot-table.d.ts +2 -4
  26. package/dist/core/pivot-table.js +5 -5
  27. package/dist/core/preserved.d.ts +3 -3
  28. package/dist/core/protection.d.ts +4 -4
  29. package/dist/core/range.d.ts +16 -14
  30. package/dist/core/range.js +5 -5
  31. package/dist/core/row-input.d.ts +2 -8
  32. package/dist/core/row-input.js +2 -2
  33. package/dist/core/row.d.ts +7 -8
  34. package/dist/core/row.js +2 -4
  35. package/dist/core/style.d.ts +14 -14
  36. package/dist/core/table-style.d.ts +9 -9
  37. package/dist/core/table-style.js +1 -1
  38. package/dist/core/table.d.ts +30 -42
  39. package/dist/core/table.js +16 -10
  40. package/dist/core/theme.d.ts +13 -43
  41. package/dist/core/theme.js +2 -148
  42. package/dist/core/value.d.ts +7 -7
  43. package/dist/core/workbook-protection.d.ts +2 -2
  44. package/dist/core/workbook-styles.d.ts +26 -0
  45. package/dist/core/workbook-styles.js +48 -0
  46. package/dist/core/workbook-theme.d.ts +25 -0
  47. package/dist/core/workbook-theme.js +49 -0
  48. package/dist/core/workbook-vba.d.ts +17 -0
  49. package/dist/core/workbook-vba.js +79 -0
  50. package/dist/core/workbook.d.ts +131 -85
  51. package/dist/core/workbook.js +74 -133
  52. package/dist/core/worksheet-comments.d.ts +9 -0
  53. package/dist/core/worksheet-comments.js +52 -0
  54. package/dist/core/worksheet-model.d.ts +4 -4
  55. package/dist/core/worksheet-model.js +1 -0
  56. package/dist/core/worksheet-pictures.d.ts +29 -0
  57. package/dist/core/worksheet-pictures.js +42 -0
  58. package/dist/core/worksheet.d.ts +85 -77
  59. package/dist/core/worksheet.js +36 -95
  60. package/dist/customui/errors.d.ts +1 -1
  61. package/dist/customui/ribbon.d.ts +3 -3
  62. package/dist/entries/core.d.ts +4 -4
  63. package/dist/entries/core.js +1 -1
  64. package/dist/entries/vba.d.ts +1 -1
  65. package/dist/entries/vba.js +1 -1
  66. package/dist/entries/xlsx.d.ts +1 -0
  67. package/dist/entries/xlsx.js +1 -0
  68. package/dist/errors.d.ts +12 -12
  69. package/dist/io/csv/read.js +1 -2
  70. package/dist/io/csv/write.d.ts +8 -3
  71. package/dist/io/csv/write.js +17 -2
  72. package/dist/io/opc/errors.d.ts +6 -6
  73. package/dist/io/opc/inflate.d.ts +1 -1
  74. package/dist/io/opc/namespaces.d.ts +1 -1
  75. package/dist/io/opc/read-opc.d.ts +19 -3
  76. package/dist/io/opc/read-opc.js +14 -8
  77. package/dist/io/opc/read-options.d.ts +2 -2
  78. package/dist/io/opc/sniff-format.d.ts +2 -2
  79. package/dist/io/style/xf-style.d.ts +38 -6
  80. package/dist/io/style/xf-style.js +22 -0
  81. package/dist/io/xlsb/errors.d.ts +1 -1
  82. package/dist/io/xlsb/formula.d.ts +4 -4
  83. package/dist/io/xlsb/primitives.d.ts +8 -8
  84. package/dist/io/xlsb/ptg-functions.d.ts +1 -1
  85. package/dist/io/xlsb/read-styles.js +2 -20
  86. package/dist/io/xlsb/read.d.ts +2 -2
  87. package/dist/io/xlsx/cell-accumulator.d.ts +25 -6
  88. package/dist/io/xlsx/cell-accumulator.js +111 -30
  89. package/dist/io/xlsx/cell-value.d.ts +2 -2
  90. package/dist/io/xlsx/cell-value.js +3 -3
  91. package/dist/io/xlsx/color-xml.js +11 -17
  92. package/dist/io/xlsx/comments.d.ts +5 -5
  93. package/dist/io/xlsx/comments.js +13 -6
  94. package/dist/io/xlsx/conditional-formatting.d.ts +3 -3
  95. package/dist/io/xlsx/conditional-formatting.js +7 -16
  96. package/dist/io/xlsx/data-validation.d.ts +2 -2
  97. package/dist/io/xlsx/data-validation.js +1 -1
  98. package/dist/io/xlsx/errors.d.ts +3 -3
  99. package/dist/io/xlsx/hyperlinks.d.ts +4 -2
  100. package/dist/io/xlsx/hyperlinks.js +5 -5
  101. package/dist/io/xlsx/images.d.ts +1 -1
  102. package/dist/io/xlsx/images.js +8 -8
  103. package/dist/io/xlsx/namespaces.d.ts +2 -2
  104. package/dist/io/xlsx/package-plan.js +5 -2
  105. package/dist/io/xlsx/read-pivot.js +2 -5
  106. package/dist/io/xlsx/read-rows.d.ts +13 -13
  107. package/dist/io/xlsx/read-rows.js +19 -55
  108. package/dist/io/xlsx/read-shared-strings.js +7 -5
  109. package/dist/io/xlsx/read-styles.d.ts +3 -3
  110. package/dist/io/xlsx/read-styles.js +40 -65
  111. package/dist/io/xlsx/read-worksheet.js +61 -115
  112. package/dist/io/xlsx/read.d.ts +3 -5
  113. package/dist/io/xlsx/read.js +61 -69
  114. package/dist/io/xlsx/rich-runs.d.ts +1 -1
  115. package/dist/io/xlsx/rich-runs.js +1 -1
  116. package/dist/io/xlsx/rich-text.d.ts +1 -1
  117. package/dist/io/xlsx/shared-strings.d.ts +2 -2
  118. package/dist/io/xlsx/sheet-properties.js +2 -2
  119. package/dist/io/xlsx/styles.d.ts +8 -9
  120. package/dist/io/xlsx/styles.js +4 -3
  121. package/dist/io/xlsx/tables.d.ts +2 -2
  122. package/dist/io/xlsx/tables.js +8 -13
  123. package/dist/io/xlsx/theme-xml.d.ts +37 -0
  124. package/dist/io/xlsx/theme-xml.js +142 -0
  125. package/dist/io/xlsx/threaded-comments.d.ts +18 -27
  126. package/dist/io/xlsx/threaded-comments.js +9 -21
  127. package/dist/io/xlsx/worksheet-xml.d.ts +2 -2
  128. package/dist/io/xlsx/worksheet-xml.js +7 -12
  129. package/dist/io/xlsx/write-stream.d.ts +15 -15
  130. package/dist/io/xlsx/write-stream.js +3 -3
  131. package/dist/io/xlsx/write.d.ts +8 -7
  132. package/dist/io/xlsx/write.js +6 -3
  133. package/dist/io/xlsx/x14-ext.d.ts +1 -1
  134. package/dist/vba/bytes.d.ts +12 -0
  135. package/dist/vba/bytes.js +40 -0
  136. package/dist/vba/cfb-writer.d.ts +1 -1
  137. package/dist/vba/cfb.d.ts +1 -1
  138. package/dist/vba/cfb.js +15 -51
  139. package/dist/vba/codepage.d.ts +2 -2
  140. package/dist/vba/errors.d.ts +2 -2
  141. package/dist/vba/index.d.ts +2 -2
  142. package/dist/vba/index.js +2 -2
  143. package/dist/vba/ms-ovba.d.ts +1 -1
  144. package/dist/vba/ms-ovba.js +1 -3
  145. package/dist/vba/project-editor.d.ts +9 -9
  146. package/dist/vba/project-editor.js +2 -11
  147. package/dist/vba/project.d.ts +5 -5
  148. package/dist/vba/project.js +1 -10
  149. package/dist/vba/vba-encoding.d.ts +1 -1
  150. package/dist/xml/errors.d.ts +1 -1
  151. package/dist/xml/xml-read.d.ts +38 -9
  152. package/dist/xml/xml-read.js +23 -0
  153. package/dist/xml/xml.d.ts +31 -9
  154. package/dist/xml/xml.js +23 -3
  155. package/package.json +41 -35
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The number format applied to a `Date` cell that carries no explicit format of its own,
3
- * so the value renders and reads back as a date rather than a bare serial number.
3
+ * so the value renders, and reads back, as a date rather than a bare serial number.
4
4
  */
5
5
  export declare const DEFAULT_DATE_NUMFMT = "yyyy-mm-dd";
6
6
  /**
@@ -17,8 +17,8 @@ export declare function dateToSerial(date: Date): number;
17
17
  export declare function serialToDate(serial: number): Date;
18
18
  /**
19
19
  * Whether a number-format code renders its value as a date or time. A format is a date
20
- * format when, once its non-formatting sections are removed bracketed color/locale/
21
- * condition directives, quoted literals, and escaped characters any of the date/time
20
+ * format when, once its non-formatting sections are removed (bracketed color/locale/
21
+ * condition directives, quoted literals, and escaped characters) any of the date/time
22
22
  * placeholder letters (`y m d h s`) remain. So `"$"#,##0.00` and `0.00%` are not dates
23
23
  * while `yyyy-mm-dd`, `dd/mm/yyyy`, and `[$-409]mmmm d, yyyy` are.
24
24
  */
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * Quote a sheet name for use in a reference exactly when Excel would: a name that is not a plain
3
- * identifier or that would read as a cell address is wrapped in single quotes with its internal
3
+ * identifier, or that would read as a cell address, is wrapped in single quotes with its internal
4
4
  * quotes doubled, and a simple name is left bare. Shared by everything that *builds* a qualified
5
5
  * reference: the `_FilterDatabase` name the writer derives from an autofilter, and the `.xlsb`
6
6
  * reader's Ptg decoder, which has only a sheet index to work from and must spell the prefix itself.
7
7
  *
8
8
  * `last` names the far end of a 3-D span (`Data:More!A1`). A span is quoted as a whole or not at all,
9
- * because the quotes delimit the sheet *reference* rather than either name so one awkward endpoint
9
+ * because the quotes delimit the sheet *reference* rather than either name, so one awkward endpoint
10
10
  * puts both inside the quotes.
11
11
  */
12
12
  export declare function quoteSheetName(name: string, last?: string): string;
@@ -14,7 +14,7 @@ export declare function quoteSheetName(name: string, last?: string): string;
14
14
  * Prefix every modern function called by its plain name with `_xlfn.` so Excel accepts the stored
15
15
  * formula. Names already prefixed are left alone (never doubled), unknown/legacy functions pass
16
16
  * through untouched, and opaque regions (string literals, sheet names, structured references) are
17
- * preserved verbatim. No other rewriting occurs in particular no `@` implicit-intersection operator
17
+ * preserved verbatim. No other rewriting occurs: in particular no `@` implicit-intersection operator
18
18
  * is ever introduced.
19
19
  */
20
20
  export declare function mangleFunctions(formula: string): string;
@@ -25,8 +25,8 @@ export declare function mangleFunctions(formula: string): string;
25
25
  */
26
26
  export declare function unmangleFunctions(formula: string): string;
27
27
  /**
28
- * Prefix every LET/LAMBDA parameter identifier with `_xlpm.` at its declaration and at each
29
- * reference within the binding call's parentheses so Excel accepts the stored formula. The prefix
28
+ * Prefix every LET/LAMBDA parameter identifier with `_xlpm.`, at its declaration and at each
29
+ * reference within the binding call's parentheses, so Excel accepts the stored formula. The prefix
30
30
  * is lexically scoped: a name is only rewritten inside the call that binds it, opaque regions are
31
31
  * copied verbatim, and a lambda-valued parameter used as a call (`f(…)`) is prefixed too. Formulas
32
32
  * with no LET/LAMBDA pass through unchanged.
@@ -34,7 +34,7 @@ export declare function unmangleFunctions(formula: string): string;
34
34
  export declare function mangleParams(formula: string): string;
35
35
  /**
36
36
  * Mangle a model formula into its on-disk form: LET/LAMBDA parameter names first (`_xlpm.`), then the
37
- * modern-function prefix (`_xlfn.`). Ordering matters parameter mangling reads the plain LET/LAMBDA
37
+ * modern-function prefix (`_xlfn.`). Ordering matters: parameter mangling reads the plain LET/LAMBDA
38
38
  * names before the function pass qualifies them. The inverse for both prefixes is unmangleFunctions.
39
39
  */
40
40
  export declare function mangleFormula(formula: string): string;
@@ -1,4 +1,4 @@
1
- import { decodeAddress, decodeRange, encodeAddress } from './address.js';
1
+ import { decodeRange, encodeAddress, tryDecodeCellRef } from './address.js';
2
2
  import { Cell, copyCellContent } from './cell.js';
3
3
  import { replaceContents } from './containers.js';
4
4
  import { isOneCellAnchor } from './image.js';
@@ -95,8 +95,8 @@ export class GridEdits {
95
95
  const value = cell.value;
96
96
  if (!isSharedFormulaValue(value))
97
97
  continue;
98
- const master = decodeAddress(value.sharedFormula);
99
- if (master.col === undefined || master.row === undefined)
98
+ const master = tryDecodeCellRef(value.sharedFormula);
99
+ if (master === undefined)
100
100
  continue;
101
101
  const anchored = axis === 'row'
102
102
  ? encodeAddress(master.col, shiftIndex(master.row, start, count, delta))
@@ -17,7 +17,7 @@ export declare const PX_TO_EMU = 9525;
17
17
  * them; `oneCell` moves but keeps its size; `absolute` is pinned to the page and does neither. Excel
18
18
  * defaults to `oneCell` when the attribute is omitted. */
19
19
  export type ImageEditAs = 'oneCell' | 'twoCell' | 'absolute';
20
- /** A fixed image size in EMUs the extent of a one-cell anchor, which pixel dimensions convert into
20
+ /** A fixed image size in EMUs: the extent of a one-cell anchor, which pixel dimensions convert into
21
21
  * via {@link PX_TO_EMU}. */
22
22
  export interface Extent {
23
23
  readonly cx: number;
@@ -63,7 +63,7 @@ export interface AnchoredImage {
63
63
  }
64
64
  /** A picture's bytes and its file kind, as held in the workbook's media registry. */
65
65
  export interface WorkbookImage {
66
- /** Lower-case file extension without a dot `"png"`, `"jpeg"`, `"gif"`. Drives the media part's
66
+ /** Lower-case file extension without a dot: `"png"`, `"jpeg"`, `"gif"`. Drives the media part's
67
67
  * name and content type. */
68
68
  readonly extension: string;
69
69
  readonly data: Uint8Array;
@@ -74,3 +74,38 @@ export interface WorkbookImage {
74
74
  * leading run of alphanumerics; a missing or all-punctuation hint falls back to sniffing the bytes'
75
75
  * magic number, so the package is always well-formed. */
76
76
  export declare function normalizeImageExtension(extension: string | undefined, data: Uint8Array): string;
77
+ /**
78
+ * An anchored image in workbook-independent form: the picture's own bytes rather than a media id
79
+ * into one particular workbook's registry.
80
+ *
81
+ * An {@link AnchoredImage} means nothing away from that registry: its `imageId` is an index, and
82
+ * the same index names a different picture (or none) in the next workbook. Attaching the picture
83
+ * itself is what lets an anchor cross that boundary, which is why the transfer form carries bytes
84
+ * where the stored form carries an id.
85
+ */
86
+ export interface PortableImage {
87
+ readonly image: WorkbookImage;
88
+ readonly anchor: ImageAnchor;
89
+ }
90
+ /**
91
+ * Every picture a worksheet shows, in the workbook-independent form of {@link PortableImage}: the
92
+ * images anchored to the grid, in the order they were added, and the background tiled behind it.
93
+ * {@link Workbook.exportImages} produces one, {@link Workbook.importImages} applies one.
94
+ */
95
+ export interface WorksheetImages {
96
+ readonly anchored: readonly PortableImage[];
97
+ readonly background: WorkbookImage | undefined;
98
+ }
99
+ /**
100
+ * The id under which `image` is already registered in `media`, or `undefined` if it is not.
101
+ *
102
+ * Content-addressed rather than reference-addressed: two byte-identical pictures are one picture,
103
+ * however they reached the registry. This is what keeps repeated imports from growing the media
104
+ * list without bound (the same logo carried onto twenty sheets registers once), and it is why the
105
+ * comparison is over bytes rather than object identity, which a picture arriving from another
106
+ * workbook would never satisfy.
107
+ *
108
+ * The length check comes first and short-circuits, so pictures of different sizes never reach the
109
+ * byte loop; only same-extension, same-length candidates are compared in full.
110
+ */
111
+ export declare function findRegisteredImage(media: readonly WorkbookImage[], image: WorkbookImage): number | undefined;
@@ -38,3 +38,9 @@ export function normalizeImageExtension(extension, data) {
38
38
  }
39
39
  return sniffImageExtension(data);
40
40
  }
41
+ export function findRegisteredImage(media, image) {
42
+ const index = media.findIndex((held) => held.extension === image.extension &&
43
+ held.data.length === image.data.length &&
44
+ held.data.every((byte, i) => byte === image.data[i]));
45
+ return index === -1 ? undefined : index;
46
+ }
@@ -4,8 +4,16 @@
4
4
  */
5
5
  export declare const INTERNAL: unique symbol;
6
6
  /**
7
- * Keys a `Cell`'s link to a named cell style its OOXML `xfId`. A single hidden property rather than
7
+ * Keys a `Cell`'s link to a named cell style, its OOXML `xfId`. A single hidden property rather than
8
8
  * a channel object, so it stays a prototype accessor: cells are the one model class allocated in the
9
9
  * millions, and a per-instance internals object would be a real cost for state most cells never carry.
10
10
  */
11
11
  export declare const NAMED_STYLE_ID: unique symbol;
12
+ /**
13
+ * The exhaustiveness proof the mirror types are built on: instantiate it with a `keyof` difference
14
+ * that should be empty. An unmirrored field leaves that difference non-`never`, which does not
15
+ * satisfy the constraint, so the build fails naming the field that was missed: a type error at the
16
+ * declaration rather than a silent hole discovered by a round trip. `Row`, `Column` and
17
+ * `WORKSHEET_MODEL_FACETS` each carry one of these proofs; this is the mechanism all three use.
18
+ */
19
+ export type AssertNever<T extends never> = T;
@@ -4,8 +4,8 @@
4
4
  * shown.
5
5
  *
6
6
  * A file may state more, and stating more loses the value rather than the file. Excel opens such a
7
- * package without complaint and silently clamps the row to 409.6, a tick *above* what it lets
8
- * you assign, being 8192 twentieths of a point and so the width of the field it is read into — and
7
+ * package without complaint and silently clamps the row to 409.6, a tick *above* what it lets you
8
+ * assign, being 8192 twentieths of a point and so the width of the field it is read into, then
9
9
  * writes 409.6 back on its next save. Check against this constant to keep a stated height from
10
10
  * quietly becoming a different one.
11
11
  */
@@ -16,12 +16,12 @@ export declare const MAX_ROW_HEIGHT = 409.5;
16
16
  *
17
17
  * It is also the weaker of the two ceilings: it binds assignment only, and not a file at all.
18
18
  * Excel honours a `width` of 1000 read from a package, renders the column at it, and round-trips
19
- * it verbatim through its own save where an over-limit row height is clamped away. So a width
19
+ * it verbatim through its own save, where an over-limit row height is clamped away. So a width
20
20
  * above this is a column no Excel user could have produced by dragging, not a value at risk.
21
21
  *
22
22
  * Character units, not points or pixels: a width is a count of digits of the default font's
23
23
  * *maximum digit width*, which is why there is no companion `DEFAULT_COLUMN_WIDTH` constant here.
24
- * The width a column takes when it states none is a function of that font the familiar 8.43 holds
24
+ * The width a column takes when it states none is a function of that font: the familiar 8.43 holds
25
25
  * for Calibri 11 and not for a workbook whose normal style says otherwise (Excel reports 8.09 for
26
26
  * Aptos Narrow 11). `sheet.properties.defaultColWidth` is what a file declares, and
27
27
  * docs/knowledge/specs/default-font-must-not-be-assumed-for-column-widths.md is why assuming a
@@ -1,17 +1,11 @@
1
+ import { type GridRect } from './address.ts';
1
2
  import type { Cell } from './cell.ts';
2
- /** A merged region as inclusive 1-based grid bounds. */
3
- export interface MergeRect {
4
- readonly top: number;
5
- readonly left: number;
6
- readonly bottom: number;
7
- readonly right: number;
8
- }
9
- /** Whether two inclusive grid rectangles share at least one cell. */
10
- export declare function rectsOverlap(a: MergeRect, b: MergeRect): boolean;
3
+ /** A merged region, as the {@link GridRect} every range-shaped thing in the library is. */
4
+ export type MergeRect = GridRect;
11
5
  /**
12
6
  * Resolve a position to the master (top-left) of the merged region covering it, or to itself when no
13
7
  * region does. First covering region wins; `Worksheet.mergeCells` rejects overlaps, so at most one
14
- * region ever applies. Only fully-bounded rects participate an unbounded whole-row/column merge
8
+ * region ever applies. Only fully-bounded rects participate: an unbounded whole-row/column merge
15
9
  * carries no rect and so resolves nothing.
16
10
  */
17
11
  export declare function masterOf(rects: readonly MergeRect[], row: number, col: number): {
@@ -20,12 +14,12 @@ export declare function masterOf(rects: readonly MergeRect[], row: number, col:
20
14
  };
21
15
  /**
22
16
  * Drop any value already sitting in a merge's covered non-anchor cells, keeping only the top-left
23
- * anchor the collapse Excel performs on merge. A leftover covered value would serialise as a
17
+ * anchor, the collapse Excel performs on merge. A leftover covered value would serialise as a
24
18
  * populated `<c>` under the range's `<mergeCell>` ref, the geometry that trips Excel's repair prompt.
25
19
  * Styles are untouched: a border spanning the merged region rides the covered cells.
26
20
  */
27
21
  export declare function clearCoveredValues(rows: Map<number, Map<number, Cell>>, rect: MergeRect): void;
28
22
  /** Decode an OOXML `sqref` (one or more space-separated ranges) into containment rectangles. A whole
29
23
  * column or row leaves one axis unbounded, so its missing edges open to `Infinity` rather than
30
- * clamping a cell anywhere down the column still resolves inside it. */
24
+ * clamping: a cell anywhere down the column still resolves inside it. */
31
25
  export declare function decodeSqrefRects(sqref: string): MergeRect[];
@@ -1,7 +1,4 @@
1
1
  import { decodeRange } from './address.js';
2
- export function rectsOverlap(a, b) {
3
- return a.left <= b.right && b.left <= a.right && a.top <= b.bottom && b.top <= a.bottom;
4
- }
5
2
  export function masterOf(rects, row, col) {
6
3
  for (const rect of rects) {
7
4
  if (row >= rect.top && row <= rect.bottom && col >= rect.left && col <= rect.right) {
@@ -3,7 +3,7 @@
3
3
  * `<pageSetUpPr>` flag (a `<sheetPr>` child) that switches Excel from fixed-zoom to fit-to-page
4
4
  * scaling, while the rest are `<pageSetup>` attributes. Excel honours `scale` only when `fitToPage`
5
5
  * is off and the `fitToWidth`/`fitToHeight` page counts only when it is on, but the model carries
6
- * whatever the author set an unset field is omitted so a round-trip never fabricates one. An
6
+ * whatever the author set: an unset field is omitted so a round-trip never fabricates one. An
7
7
  * empty object emits neither element.
8
8
  */
9
9
  export interface PageSetup {
@@ -21,7 +21,7 @@ export interface PageSetup {
21
21
  pageOrder?: 'downThenOver' | 'overThenDown';
22
22
  /**
23
23
  * Paper size as Excel's 1-based enumeration index (e.g. `9` = A4, `1` = US Letter). Carried as an
24
- * opaque integer the model does not map it to physical dimensions, only preserves whatever the
24
+ * opaque integer: the model does not map it to physical dimensions, only preserves whatever the
25
25
  * author or source file set.
26
26
  */
27
27
  paperSize?: number;
@@ -35,7 +35,7 @@ export interface PageSetup {
35
35
  }
36
36
  /**
37
37
  * Print-toggle flags from the `<printOptions>` element. Each maps to a boolean OOXML attribute that
38
- * defaults false except `gridLinesSet`, which defaults true and gates whether `gridLines` is
38
+ * defaults false, except `gridLinesSet`, which defaults true and gates whether `gridLines` is
39
39
  * honoured. The model stores only what the source or caller set, so an unset flag is omitted and a
40
40
  * round-trip never fabricates one; an empty object emits no element at all.
41
41
  */
@@ -54,7 +54,7 @@ export interface PrintOptions {
54
54
  /**
55
55
  * A manual page break (`<brk>`). For a row break, `id` is the row the layout splits *before*; for a
56
56
  * column break it is the column. `max` bounds the break's extent across the other axis (Excel writes
57
- * the last row/column index) and `man` marks it author-set rather than automatic the model preserves
57
+ * the last row/column index) and `man` marks it author-set rather than automatic. The model preserves
58
58
  * whatever the source carried so a round-trip reproduces the break's span exactly.
59
59
  */
60
60
  export interface PageBreak {
@@ -3,12 +3,10 @@ import type { Worksheet } from './worksheet.ts';
3
3
  * names verbatim, so a metric doubles as its `<dataField subtotal="…">` value. Excel performs the
4
4
  * aggregation itself on refresh; the writer only records which function to apply. */
5
5
  export type PivotMetric = 'sum' | 'count' | 'countNums' | 'average' | 'max' | 'min' | 'product' | 'stdDev' | 'stdDevp' | 'var' | 'varp';
6
- /** Narrow a raw `subtotal` attribute (or any string) to a known {@link PivotMetric}. */
7
- export declare function isPivotMetric(value: string): value is PivotMetric;
8
6
  /** Map an OOXML `<dataField subtotal="…">` value back to its metric. The attribute is absent for
9
7
  * `sum` (Excel's implicit default), so `undefined` reads as `sum`; an unrecognised value also reads
10
- * as `sum` rather than throwing, because reconstructing an existing file is a lenient operation
11
- * the strict rejection of unknown metrics belongs on the authoring path, not the read path. */
8
+ * as `sum` rather than throwing, because reconstructing an existing file is a lenient operation.
9
+ * The strict rejection of unknown metrics belongs on the authoring path, not the read path. */
12
10
  export declare function pivotMetricFromSubtotal(subtotal: string | undefined): PivotMetric;
13
11
  /** One field in a loaded pivot's cache catalogue, in declared order; the pivot refers to it by index. */
14
12
  export interface ParsedPivotField {
@@ -14,7 +14,7 @@ const PIVOT_METRICS = new Set([
14
14
  'var',
15
15
  'varp',
16
16
  ]);
17
- export function isPivotMetric(value) {
17
+ function isPivotMetric(value) {
18
18
  return PIVOT_METRICS.has(value);
19
19
  }
20
20
  export function pivotMetricFromSubtotal(subtotal) {
@@ -35,7 +35,7 @@ export class PivotTable {
35
35
  constructor(options) {
36
36
  const metric = options.metric ?? 'sum';
37
37
  if (!PIVOT_METRICS.has(metric)) {
38
- throw new AuthoringError(`unsupported pivot metric "${metric}" expected one of ${[...PIVOT_METRICS].join(', ')}`);
38
+ throw new AuthoringError(`unsupported pivot metric "${metric}": expected one of ${[...PIVOT_METRICS].join(', ')}`);
39
39
  }
40
40
  this.metric = metric;
41
41
  const source = options.source;
@@ -110,7 +110,7 @@ export class PivotTable {
110
110
  records.push(fields.map((_field, fieldIndex) => {
111
111
  const scalar = scalarsForField(columnScalars, fieldIndex)[row];
112
112
  if (scalar === undefined) {
113
- throw new InternalError(`pivot record row ${row} is out of range for field ${fieldIndex} every column was ` +
113
+ throw new InternalError(`pivot record row ${row} is out of range for field ${fieldIndex}: every column was ` +
114
114
  'scanned for the same dataRowCount above, so this index is always in range');
115
115
  }
116
116
  const catalogue = catalogues[fieldIndex];
@@ -127,7 +127,7 @@ export class PivotTable {
127
127
  get valueFieldName() {
128
128
  const field = this.cacheFields[this.valueField];
129
129
  if (field === undefined) {
130
- throw new InternalError(`pivot valueField index ${this.valueField} is out of range resolve() validated it against ` +
130
+ throw new InternalError(`pivot valueField index ${this.valueField} is out of range: resolve() validated it against ` +
131
131
  'the same fields array cacheFields was built from');
132
132
  }
133
133
  return field.name;
@@ -136,7 +136,7 @@ export class PivotTable {
136
136
  function scalarsForField(columnScalars, fieldIndex) {
137
137
  const scalars = columnScalars[fieldIndex];
138
138
  if (scalars === undefined) {
139
- throw new InternalError(`pivot field index ${fieldIndex} is out of range for columnScalars it was built from the same fields array`);
139
+ throw new InternalError(`pivot field index ${fieldIndex} is out of range for columnScalars: it was built from the same fields array`);
140
140
  }
141
141
  return scalars;
142
142
  }
@@ -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,7 @@ 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
66
  */
67
67
  export declare function deriveCredential(password: string, spinCount?: number): SheetProtectionCredential;
@@ -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
  }
@@ -7,8 +7,8 @@ import type { ColumnProperties, RowInput } from './worksheet.ts';
7
7
  * untouched; a keyed object maps each value under the column carrying the matching key.
8
8
  *
9
9
  * `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.
10
+ * iframe) is still an array but fails the identity check, and would then be walked as a keyed
11
+ * object, placing nothing.
12
12
  */
13
13
  export declare function rowPlacements(values: RowInput, columns: ReadonlyMap<number, ColumnProperties>): Array<[number, CellValue]>;
14
14
  /**
@@ -16,9 +16,3 @@ export declare function rowPlacements(values: RowInput, columns: ReadonlyMap<num
16
16
  * keyed by column. The grid-edit machinery then splices this map into place.
17
17
  */
18
18
  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;
@@ -20,10 +20,10 @@ export function buildRowCells(number, values, columns) {
20
20
  }
21
21
  return row;
22
22
  }
23
- export function columnIndexByKey(columns, key) {
23
+ function columnIndexByKey(columns, key) {
24
24
  for (const [index, properties] of columns) {
25
25
  if (properties.key === key)
26
26
  return index;
27
27
  }
28
- throw new AuthoringError(`no column is keyed ${JSON.stringify(key)} set getColumn(n).key first`);
28
+ throw new AuthoringError(`no column is keyed ${JSON.stringify(key)}: set getColumn(n).key first`);
29
29
  }
@@ -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
  }