@shbernal/ts-xlsx 2.1.0 → 3.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 (100) hide show
  1. package/dist/core/autofilter.d.ts +1 -1
  2. package/dist/core/autofilter.js +3 -5
  3. package/dist/core/axis-handle.d.ts +8 -0
  4. package/dist/core/axis-handle.js +14 -0
  5. package/dist/core/cell.d.ts +24 -0
  6. package/dist/core/cell.js +9 -1
  7. package/dist/core/column.d.ts +4 -1
  8. package/dist/core/column.js +31 -35
  9. package/dist/core/conditional-formatting.d.ts +5 -5
  10. package/dist/core/conditional-formatting.js +11 -25
  11. package/dist/core/data-validation.d.ts +3 -3
  12. package/dist/core/data-validation.js +7 -15
  13. package/dist/core/image.d.ts +1 -1
  14. package/dist/core/image.js +2 -4
  15. package/dist/core/merge-index.d.ts +31 -0
  16. package/dist/core/merge-index.js +60 -0
  17. package/dist/core/merge.d.ts +0 -10
  18. package/dist/core/merge.js +0 -8
  19. package/dist/core/page-setup.d.ts +72 -2
  20. package/dist/core/page-setup.js +34 -8
  21. package/dist/core/pivot-table.d.ts +1 -1
  22. package/dist/core/pivot-table.js +4 -8
  23. package/dist/core/row.d.ts +4 -1
  24. package/dist/core/row.js +19 -23
  25. package/dist/core/style.d.ts +51 -7
  26. package/dist/core/style.js +29 -34
  27. package/dist/core/table.d.ts +1 -1
  28. package/dist/core/table.js +3 -5
  29. package/dist/core/used-extent.d.ts +44 -0
  30. package/dist/core/used-extent.js +102 -0
  31. package/dist/core/worksheet.d.ts +17 -6
  32. package/dist/core/worksheet.js +48 -42
  33. package/dist/customui/ribbon.js +4 -6
  34. package/dist/io/opc/inflate.js +1 -1
  35. package/dist/io/opc/part-paths.d.ts +1 -0
  36. package/dist/io/opc/part-paths.js +15 -0
  37. package/dist/io/opc/read-opc.d.ts +22 -3
  38. package/dist/io/opc/read-opc.js +11 -39
  39. package/dist/io/opc/rels.d.ts +1 -6
  40. package/dist/io/opc/rels.js +1 -4
  41. package/dist/io/style/xf-style.d.ts +26 -2
  42. package/dist/io/style/xf-style.js +6 -0
  43. package/dist/io/xlsb/read-styles.js +6 -3
  44. package/dist/io/xlsb/read.d.ts +1 -1
  45. package/dist/io/xlsb/read.js +5 -8
  46. package/dist/io/xlsx/cell-accumulator.d.ts +1 -1
  47. package/dist/io/xlsx/cell-accumulator.js +1 -1
  48. package/dist/io/xlsx/cell-value.js +1 -1
  49. package/dist/io/xlsx/color-xml.js +1 -1
  50. package/dist/io/xlsx/comments.js +2 -1
  51. package/dist/io/xlsx/conditional-formatting.d.ts +3 -2
  52. package/dist/io/xlsx/conditional-formatting.js +22 -21
  53. package/dist/io/xlsx/data-validation.d.ts +9 -7
  54. package/dist/io/xlsx/data-validation.js +10 -9
  55. package/dist/io/xlsx/edit-vba.js +4 -11
  56. package/dist/io/xlsx/hyperlinks.d.ts +4 -2
  57. package/dist/io/xlsx/hyperlinks.js +20 -17
  58. package/dist/io/xlsx/images.js +15 -14
  59. package/dist/io/xlsx/package-plan.d.ts +9 -1
  60. package/dist/io/xlsx/package-plan.js +29 -16
  61. package/dist/io/xlsx/part-names.d.ts +41 -0
  62. package/dist/io/xlsx/part-names.js +46 -0
  63. package/dist/io/xlsx/read-pivot.js +2 -1
  64. package/dist/io/xlsx/read-rows.js +12 -13
  65. package/dist/io/xlsx/read-shared-strings.js +2 -1
  66. package/dist/io/xlsx/read-styles.d.ts +1 -1
  67. package/dist/io/xlsx/read-styles.js +63 -42
  68. package/dist/io/xlsx/read-worksheet.d.ts +10 -1
  69. package/dist/io/xlsx/read-worksheet.js +43 -56
  70. package/dist/io/xlsx/read.d.ts +2 -3
  71. package/dist/io/xlsx/read.js +81 -73
  72. package/dist/io/xlsx/rich-runs.d.ts +1 -1
  73. package/dist/io/xlsx/rich-runs.js +2 -1
  74. package/dist/io/xlsx/sheet-properties.js +21 -28
  75. package/dist/io/xlsx/styles.js +23 -20
  76. package/dist/io/xlsx/tables.js +2 -1
  77. package/dist/io/xlsx/theme-xml.js +1 -1
  78. package/dist/io/xlsx/threaded-comments.js +2 -1
  79. package/dist/io/xlsx/workbook-xml.js +29 -28
  80. package/dist/io/xlsx/worksheet-xml.d.ts +1 -1
  81. package/dist/io/xlsx/worksheet-xml.js +14 -16
  82. package/dist/io/xlsx/write-stream.d.ts +6 -0
  83. package/dist/io/xlsx/write-stream.js +30 -22
  84. package/dist/io/xlsx/write.d.ts +5 -0
  85. package/dist/io/xlsx/write.js +155 -131
  86. package/dist/token-set.d.ts +15 -0
  87. package/dist/token-set.js +4 -0
  88. package/dist/vba/cfb-format.d.ts +24 -0
  89. package/dist/vba/cfb-format.js +12 -0
  90. package/dist/vba/cfb-writer.js +17 -17
  91. package/dist/vba/cfb.js +1 -7
  92. package/dist/vba/ms-ovba.js +32 -11
  93. package/dist/vba/project.js +13 -13
  94. package/dist/vba/vba-encoding.js +1 -1
  95. package/dist/xml/xml-read.d.ts +59 -74
  96. package/dist/xml/xml-read.js +83 -176
  97. package/dist/xml/xml-scan.d.ts +100 -0
  98. package/dist/xml/xml-scan.js +208 -0
  99. package/dist/xml/xml.d.ts +1 -1
  100. package/package.json +2 -3
@@ -1,3 +1,4 @@
1
+ import type { AssertNever } from './internal.ts';
1
2
  /**
2
3
  * Paper orientation, as `<pageSetup orientation>` carries it.
3
4
  *
@@ -7,11 +8,11 @@
7
8
  */
8
9
  export type PageOrientation = 'portrait' | 'landscape';
9
10
  /** Narrow a raw `<pageSetup orientation>` token to a known {@link PageOrientation}. */
10
- export declare function isPageOrientation(value: string): value is PageOrientation;
11
+ export declare const isPageOrientation: (value: string) => value is PageOrientation;
11
12
  /** The order pages are numbered and printed in across a sheet wider and taller than one page. */
12
13
  export type PageOrder = 'downThenOver' | 'overThenDown';
13
14
  /** Narrow a raw `<pageSetup pageOrder>` token to a known {@link PageOrder}. */
14
- export declare function isPageOrder(value: string): value is PageOrder;
15
+ export declare const isPageOrder: (value: string) => value is PageOrder;
15
16
  /**
16
17
  * Print-scaling and orientation settings. These map onto two OOXML elements: `fitToPage` is the
17
18
  * `<pageSetUpPr>` flag (a `<sheetPr>` child) that switches Excel from fixed-zoom to fit-to-page
@@ -47,6 +48,61 @@ export interface PageSetup {
47
48
  */
48
49
  printerSettings?: Uint8Array;
49
50
  }
51
+ /**
52
+ * How one `<pageSetup>` attribute encodes: which model key it is and what kind of value it carries.
53
+ *
54
+ * Format-blind on purpose, the same way {@link AlignmentFacet} is: `PageSetup` is a core type and
55
+ * the layering gate forbids core importing a serialisation, so the table states what an attribute
56
+ * *is* and each codec supplies the reading and the writing off the `kind`. The OOXML attribute name
57
+ * is the model key throughout, so it is not restated.
58
+ */
59
+ export type PageSetupFacet = {
60
+ readonly key: 'paperSize' | 'scale' | 'fitToWidth' | 'fitToHeight';
61
+ /** A non-negative integer: a page count, a percentage, or a paper-size id. */
62
+ readonly kind: 'count';
63
+ } | {
64
+ readonly key: 'pageOrder' | 'orientation';
65
+ readonly kind: 'token';
66
+ /** The enumeration guard, and what to call it in the error when a value fails it. */
67
+ readonly isValid: (value: string) => boolean;
68
+ readonly label: string;
69
+ };
70
+ /**
71
+ * The six `<pageSetup>` attributes, declared once, in CT_PageSetup order. Both directions key off
72
+ * this list, so an attribute written but not read (it survives a re-write and vanishes on load) or
73
+ * read but not written is a compile error rather than something a reviewer has to notice.
74
+ */
75
+ export declare const PAGE_SETUP_FACETS: readonly [{
76
+ readonly key: 'paperSize';
77
+ readonly kind: 'count';
78
+ }, {
79
+ readonly key: 'scale';
80
+ readonly kind: 'count';
81
+ }, {
82
+ readonly key: 'fitToWidth';
83
+ readonly kind: 'count';
84
+ }, {
85
+ readonly key: 'fitToHeight';
86
+ readonly kind: 'count';
87
+ }, {
88
+ readonly key: 'pageOrder';
89
+ readonly kind: 'token';
90
+ readonly isValid: (value: string) => value is PageOrder;
91
+ readonly label: 'page order';
92
+ }, {
93
+ readonly key: 'orientation';
94
+ readonly kind: 'token';
95
+ readonly isValid: (value: string) => value is PageOrientation;
96
+ readonly label: 'page orientation';
97
+ }];
98
+ /**
99
+ * Compile-time proof that {@link PAGE_SETUP_FACETS} covers every `<pageSetup>` attribute.
100
+ *
101
+ * `fitToPage` and `printerSettings` are excluded because neither is one: `fitToPage` is a
102
+ * `<sheetPr>` child's flag and `printerSettings` is the blob behind an `r:id`, so both are written
103
+ * and read somewhere else entirely and a table entry for them would describe nothing.
104
+ */
105
+ export type EveryPageSetupFacetIsDeclared = AssertNever<Exclude<keyof PageSetup, 'fitToPage' | 'printerSettings' | (typeof PAGE_SETUP_FACETS)[number]['key']>>;
50
106
  /**
51
107
  * Print-toggle flags from the `<printOptions>` element. Each maps to a boolean OOXML attribute that
52
108
  * defaults false, except `gridLinesSet`, which defaults true and gates whether `gridLines` is
@@ -65,6 +121,11 @@ export interface PrintOptions {
65
121
  /** Whether the `gridLines` flag is authoritative; when `false`, Excel ignores `gridLines`. */
66
122
  gridLinesSet?: boolean;
67
123
  }
124
+ /** The `<printOptions>` flags, in CT_PrintOptions attribute order. Each is a plain OOXML boolean, so
125
+ * the list is the whole of what either direction needs to know. */
126
+ export declare const PRINT_OPTION_FLAGS: readonly ["horizontalCentered", "verticalCentered", "headings", "gridLines", "gridLinesSet"];
127
+ /** Compile-time proof that {@link PRINT_OPTION_FLAGS} covers every {@link PrintOptions} flag. */
128
+ export type EveryPrintOptionFlagIsDeclared = AssertNever<Exclude<keyof PrintOptions, (typeof PRINT_OPTION_FLAGS)[number]>>;
68
129
  /**
69
130
  * A manual page break (`<brk>`). For a row break, `id` is the row the layout splits *before*; for a
70
131
  * column break it is the column. `max` bounds the break's extent across the other axis (Excel writes
@@ -92,6 +153,10 @@ export interface PageMargins {
92
153
  header?: number;
93
154
  footer?: number;
94
155
  }
156
+ /** The `<pageMargins>` sides, in the order CT_PageMargins declares them. */
157
+ export declare const MARGIN_SIDES: readonly ["left", "right", "top", "bottom", "header", "footer"];
158
+ /** Compile-time proof that {@link MARGIN_SIDES} covers every {@link PageMargins} side. */
159
+ export type EveryMarginSideIsDeclared = AssertNever<Exclude<keyof PageMargins, (typeof MARGIN_SIDES)[number]>>;
95
160
  /**
96
161
  * Page header/footer text, one string per page class. Excel only honours the even- and
97
162
  * first-page variants when the writer also sets the gating flags (`differentOddEven`,
@@ -106,3 +171,8 @@ export interface HeaderFooter {
106
171
  firstHeader?: string;
107
172
  firstFooter?: string;
108
173
  }
174
+ /** The `<headerFooter>` children, in CT_HeaderFooter child order. The element name is the model key
175
+ * throughout, so one list serves the reader's capture, the reader's commit, and the writer. */
176
+ export declare const HEADER_FOOTER_ELEMENTS: readonly ["oddHeader", "oddFooter", "evenHeader", "evenFooter", "firstHeader", "firstFooter"];
177
+ /** Compile-time proof that {@link HEADER_FOOTER_ELEMENTS} covers every {@link HeaderFooter} slot. */
178
+ export type EveryHeaderFooterElementIsDeclared = AssertNever<Exclude<keyof HeaderFooter, (typeof HEADER_FOOTER_ELEMENTS)[number]>>;
@@ -1,8 +1,34 @@
1
- const PAGE_ORIENTATIONS = { portrait: true, landscape: true };
2
- export function isPageOrientation(value) {
3
- return Object.hasOwn(PAGE_ORIENTATIONS, value);
4
- }
5
- const PAGE_ORDERS = { downThenOver: true, overThenDown: true };
6
- export function isPageOrder(value) {
7
- return Object.hasOwn(PAGE_ORDERS, value);
8
- }
1
+ import { tokenSet } from '../token-set.js';
2
+ export const isPageOrientation = tokenSet({ portrait: true, landscape: true });
3
+ export const isPageOrder = tokenSet({ downThenOver: true, overThenDown: true });
4
+ export const PAGE_SETUP_FACETS = [
5
+ { key: 'paperSize', kind: 'count' },
6
+ { key: 'scale', kind: 'count' },
7
+ { key: 'fitToWidth', kind: 'count' },
8
+ { key: 'fitToHeight', kind: 'count' },
9
+ { key: 'pageOrder', kind: 'token', isValid: isPageOrder, label: 'page order' },
10
+ { key: 'orientation', kind: 'token', isValid: isPageOrientation, label: 'page orientation' },
11
+ ];
12
+ export const PRINT_OPTION_FLAGS = [
13
+ 'horizontalCentered',
14
+ 'verticalCentered',
15
+ 'headings',
16
+ 'gridLines',
17
+ 'gridLinesSet',
18
+ ];
19
+ export const MARGIN_SIDES = [
20
+ 'left',
21
+ 'right',
22
+ 'top',
23
+ 'bottom',
24
+ 'header',
25
+ 'footer',
26
+ ];
27
+ export const HEADER_FOOTER_ELEMENTS = [
28
+ 'oddHeader',
29
+ 'oddFooter',
30
+ 'evenHeader',
31
+ 'evenFooter',
32
+ 'firstHeader',
33
+ 'firstFooter',
34
+ ];
@@ -18,7 +18,7 @@ export interface ParsedPivotField {
18
18
  * `unknown` covers a `type` the file declares that is none of these. */
19
19
  export type PivotSourceKind = 'worksheet' | 'external' | 'consolidation' | 'scenario' | 'unknown';
20
20
  /** Narrow a raw `<cacheSource type>` token to a {@link PivotSourceKind} a file may declare. */
21
- export declare function isDeclarablePivotSourceKind(value: string): value is Exclude<PivotSourceKind, 'unknown'>;
21
+ export declare const isDeclarablePivotSourceKind: (value: string) => value is "consolidation" | "external" | "scenario" | "worksheet";
22
22
  /** Where a pivot cache draws its rows from. {@link kind} names the source type; {@link sheet} and
23
23
  * {@link ref} locate the range only when it is `worksheet` and are empty strings otherwise, so a
24
24
  * consumer can tell a genuinely non-worksheet source apart from a worksheet source that failed to
@@ -1,4 +1,5 @@
1
1
  import { AuthoringError, InternalError } from '../errors.js';
2
+ import { tokenSet } from '../token-set.js';
2
3
  import { encodeAddress } from './address.js';
3
4
  import { isErrorValue, isFormulaValue, isHyperlinkValue, isRichTextValue, isSharedFormulaValue, richTextToPlain, } from './value.js';
4
5
  const PIVOT_METRICS = {
@@ -14,23 +15,18 @@ const PIVOT_METRICS = {
14
15
  var: true,
15
16
  varp: true,
16
17
  };
17
- function isPivotMetric(value) {
18
- return Object.hasOwn(PIVOT_METRICS, value);
19
- }
18
+ const isPivotMetric = tokenSet(PIVOT_METRICS);
20
19
  export function pivotMetricFromSubtotal(subtotal) {
21
20
  if (subtotal === undefined)
22
21
  return 'sum';
23
22
  return isPivotMetric(subtotal) ? subtotal : 'sum';
24
23
  }
25
- const DECLARABLE_PIVOT_SOURCE_KINDS = {
24
+ export const isDeclarablePivotSourceKind = tokenSet({
26
25
  worksheet: true,
27
26
  external: true,
28
27
  consolidation: true,
29
28
  scenario: true,
30
- };
31
- export function isDeclarablePivotSourceKind(value) {
32
- return Object.hasOwn(DECLARABLE_PIVOT_SOURCE_KINDS, value);
33
- }
29
+ });
34
30
  const BLANK = { kind: 'blank' };
35
31
  export class PivotTable {
36
32
  metric;
@@ -1,9 +1,10 @@
1
+ import { AxisHandle } from './axis-handle.ts';
1
2
  import type { Cell } from './cell.ts';
2
3
  import { type AssertNever } from './internal.ts';
3
4
  import type { Fill } from './style.ts';
4
5
  import type { CellValue } from './value.ts';
5
6
  import type { RowProperties, Worksheet } from './worksheet.ts';
6
- export declare class Row {
7
+ export declare class Row extends AxisHandle<RowProperties> {
7
8
  #private;
8
9
  /** 1-based row number. Fixed for this handle's lifetime. */
9
10
  readonly number: number;
@@ -16,6 +17,8 @@ export declare class Row {
16
17
  * formatted, and they create the record on first write.
17
18
  */
18
19
  get properties(): Readonly<RowProperties> | undefined;
20
+ protected propertiesOf(): RowProperties | undefined;
21
+ protected ensureProperties(): RowProperties;
19
22
  /**
20
23
  * Row height in points; `undefined` leaves the sheet default in force.
21
24
  *
package/dist/core/row.js CHANGED
@@ -1,45 +1,53 @@
1
1
  import { assertRowInBounds, columnToNumber, encodeAddress } from './address.js';
2
+ import { AxisHandle } from './axis-handle.js';
2
3
  import { INTERNAL } from './internal.js';
3
- export class Row {
4
+ export class Row extends AxisHandle {
4
5
  #sheet;
5
6
  number;
6
7
  constructor(sheet, number) {
8
+ super();
7
9
  assertRowInBounds(number);
8
10
  this.#sheet = sheet;
9
11
  this.number = number;
10
12
  }
11
13
  get properties() {
14
+ return this.propertiesOf();
15
+ }
16
+ propertiesOf() {
12
17
  return this.#sheet[INTERNAL].rowPropertiesOf(this.number);
13
18
  }
19
+ ensureProperties() {
20
+ return this.#sheet[INTERNAL].ensureRowProperties(this.number);
21
+ }
14
22
  get height() {
15
- return this.#read('height');
23
+ return this.read('height');
16
24
  }
17
25
  set height(height) {
18
- this.#write('height', height);
26
+ this.write('height', height);
19
27
  }
20
28
  get hidden() {
21
- return this.#read('hidden');
29
+ return this.read('hidden');
22
30
  }
23
31
  set hidden(hidden) {
24
- this.#write('hidden', hidden);
32
+ this.write('hidden', hidden);
25
33
  }
26
34
  get outlineLevel() {
27
- return this.#read('outlineLevel');
35
+ return this.read('outlineLevel');
28
36
  }
29
37
  set outlineLevel(outlineLevel) {
30
- this.#write('outlineLevel', outlineLevel);
38
+ this.write('outlineLevel', outlineLevel);
31
39
  }
32
40
  get collapsed() {
33
- return this.#read('collapsed');
41
+ return this.read('collapsed');
34
42
  }
35
43
  set collapsed(collapsed) {
36
- this.#write('collapsed', collapsed);
44
+ this.write('collapsed', collapsed);
37
45
  }
38
46
  get fill() {
39
- return this.#read('fill');
47
+ return this.read('fill');
40
48
  }
41
49
  set fill(fill) {
42
- this.#write('fill', fill);
50
+ this.write('fill', fill);
43
51
  }
44
52
  getCell(column) {
45
53
  const index = typeof column === 'number' ? column : columnToNumber(column);
@@ -60,16 +68,4 @@ export class Row {
60
68
  this.getCell(index + 1).value = value;
61
69
  });
62
70
  }
63
- #read(key) {
64
- return this.#sheet[INTERNAL].rowPropertiesOf(this.number)?.[key];
65
- }
66
- #write(key, value) {
67
- if (value === undefined) {
68
- const properties = this.#sheet[INTERNAL].rowPropertiesOf(this.number);
69
- if (properties !== undefined)
70
- delete properties[key];
71
- return;
72
- }
73
- this.#sheet[INTERNAL].ensureRowProperties(this.number)[key] = value;
74
- }
75
71
  }
@@ -1,7 +1,8 @@
1
+ import type { AssertNever } from './internal.ts';
1
2
  /** Underline can be a plain flag or one of Excel's named underline styles. */
2
3
  export type UnderlineStyle = boolean | 'none' | 'single' | 'double' | 'singleAccounting' | 'doubleAccounting';
3
4
  /** Narrow a raw `<u val>` token to a named {@link UnderlineStyle} (the non-boolean members). */
4
- export declare function isNamedUnderlineStyle(value: string): value is Exclude<UnderlineStyle, boolean>;
5
+ export declare const isNamedUnderlineStyle: (value: string) => value is "double" | "doubleAccounting" | "none" | "single" | "singleAccounting";
5
6
  /** A colour, expressed as an ARGB hex string (`"FF0000FF"`) or an indexed theme colour. */
6
7
  export interface Color {
7
8
  /** 8-digit ARGB hex, uppercase, no leading `#`. */
@@ -41,7 +42,7 @@ export declare function parseArgb(value: string): string | undefined;
41
42
  */
42
43
  export type FillPatternType = 'none' | 'solid' | 'gray125' | 'darkGray' | 'mediumGray' | 'lightGray' | 'gray0625' | 'darkHorizontal' | 'darkVertical' | 'darkDown' | 'darkUp' | 'darkGrid' | 'darkTrellis' | 'lightHorizontal' | 'lightVertical' | 'lightDown' | 'lightUp' | 'lightGrid' | 'lightTrellis';
43
44
  /** Narrow a raw `<patternFill patternType>` token to a known {@link FillPatternType}. */
44
- export declare function isFillPatternType(value: string): value is FillPatternType;
45
+ export declare const isFillPatternType: (value: string) => value is FillPatternType;
45
46
  /**
46
47
  * A pattern fill. For a `solid` fill the visible colour is the pattern *foreground*
47
48
  * (`fgColor`), OOXML's counter-intuitive rule, while `bgColor` is the automatic
@@ -84,7 +85,7 @@ export type Fill = PatternFill | GradientFill;
84
85
  */
85
86
  export type BorderStyle = 'thin' | 'medium' | 'thick' | 'dashed' | 'dotted' | 'double' | 'hair' | 'mediumDashed' | 'dashDot' | 'mediumDashDot' | 'dashDotDot' | 'mediumDashDotDot' | 'slantDashDot';
86
87
  /** Narrow a raw border-edge `style` attribute to a known {@link BorderStyle}. */
87
- export declare function isBorderStyle(value: string): value is BorderStyle;
88
+ export declare const isBorderStyle: (value: string) => value is BorderStyle;
88
89
  /** One edge of a cell border: its line style, and optionally the line colour. */
89
90
  export interface BorderEdge {
90
91
  readonly style: BorderStyle;
@@ -108,12 +109,12 @@ export interface Border {
108
109
  /** Vertical alignment of a font relative to the baseline (super/subscript). */
109
110
  export type FontVerticalAlignment = 'superscript' | 'subscript';
110
111
  /** Narrow a raw `<vertAlign val>` token to a known {@link FontVerticalAlignment}. */
111
- export declare function isFontVerticalAlignment(value: string): value is FontVerticalAlignment;
112
+ export declare const isFontVerticalAlignment: (value: string) => value is FontVerticalAlignment;
112
113
  /** The theme-font role a `<scheme val>` names: `"minor"`/`"major"` bind the font to whichever
113
114
  * face the workbook theme assigns that role, `"none"` leaves it a literal, unbound face. */
114
115
  export type FontScheme = 'minor' | 'major' | 'none';
115
116
  /** Narrow a raw `<scheme val>` token to a known {@link FontScheme}. */
116
- export declare function isFontScheme(value: string): value is FontScheme;
117
+ export declare const isFontScheme: (value: string) => value is FontScheme;
117
118
  /** A font, as it applies to a cell or a single rich-text run. Every facet is optional and
118
119
  * independent, like {@link Border}/{@link Alignment}/{@link Protection}: a font sets only the
119
120
  * facets it overrides (Excel's own default font backs the rest), so no consumer ever holds every
@@ -137,12 +138,12 @@ export interface Font {
137
138
  * back as no explicit horizontal alignment. */
138
139
  export type HorizontalAlignment = 'general' | 'left' | 'center' | 'right' | 'fill' | 'justify' | 'centerContinuous' | 'distributed';
139
140
  /** Narrow a raw `<alignment horizontal>` token to a known {@link HorizontalAlignment}. */
140
- export declare function isHorizontalAlignment(value: string): value is HorizontalAlignment;
141
+ export declare const isHorizontalAlignment: (value: string) => value is HorizontalAlignment;
141
142
  /** How a cell's content sits vertically within its bounds, as OOXML's `ST_VerticalAlignment`
142
143
  * enumerates it. */
143
144
  export type VerticalAlignment = 'top' | 'center' | 'bottom' | 'justify' | 'distributed';
144
145
  /** Narrow a raw `<alignment vertical>` token to a known {@link VerticalAlignment}. */
145
- export declare function isVerticalAlignment(value: string): value is VerticalAlignment;
146
+ export declare const isVerticalAlignment: (value: string) => value is VerticalAlignment;
146
147
  /**
147
148
  * A cell's alignment. Every facet is optional and independent; an absent facet means the cell
148
149
  * takes Excel's default for it. The boolean flags default to off, so a cell that never enabled
@@ -158,6 +159,49 @@ export interface Alignment {
158
159
  readonly shrinkToFit?: boolean;
159
160
  readonly readingOrder?: number;
160
161
  }
162
+ /**
163
+ * How one `<alignment>` facet encodes: which model key it is, what kind of value it carries, and the
164
+ * value that means "default", which the writer omits and the reader drops.
165
+ *
166
+ * Format-blind on purpose. `Alignment` is a core type and the layering gate forbids core importing a
167
+ * serialisation, so the table states what a facet *is* and each codec supplies the reading and the
168
+ * writing off the `kind`. That is where {@link SHEET_PROTECTION_FLAGS} sits and how it is consumed,
169
+ * and it is the shape the BIFF12 codec would want if alignment ever reaches it.
170
+ *
171
+ * The OOXML attribute name is the model key for all seven facets, so it is not restated here: a
172
+ * second list that is always identical is a second list to keep in step. A future facet whose
173
+ * attribute differs from its key is the one that would have to add the field.
174
+ */
175
+ export type AlignmentFacet = {
176
+ readonly key: 'horizontal' | 'vertical';
177
+ readonly kind: 'token';
178
+ /** The enumeration guard, and what to call it in the error when a value fails it. */
179
+ readonly isValid: (value: string) => boolean;
180
+ readonly label: string;
181
+ /** The token that means the OOXML default, expressed by omitting the attribute. */
182
+ readonly omit?: string;
183
+ } | {
184
+ readonly key: 'textRotation' | 'indent' | 'readingOrder';
185
+ readonly kind: 'number';
186
+ } | {
187
+ readonly key: 'wrapText' | 'shrinkToFit';
188
+ readonly kind: 'flag';
189
+ };
190
+ /**
191
+ * The seven `<alignment>` facets, declared once. Both directions key off this list, so a facet
192
+ * written but not read (it survives a re-write and vanishes on load) or read but not written (the
193
+ * reverse) is no longer something a reviewer has to notice: {@link EveryAlignmentFacetIsDeclared}
194
+ * makes a facet added to {@link Alignment} and forgotten here a compile error.
195
+ *
196
+ * In ECMA-376 CT_CellAlignment order, which is the order the writer emits.
197
+ */
198
+ export declare const ALIGNMENT_FACETS: readonly AlignmentFacet[];
199
+ /**
200
+ * Compile-time proof that {@link ALIGNMENT_FACETS} covers every {@link Alignment} facet. A facet
201
+ * added to the type without a table entry resolves this to that facet's name, which does not satisfy
202
+ * `never`, so the error names what is missing.
203
+ */
204
+ export type EveryAlignmentFacetIsDeclared = AssertNever<Exclude<keyof Alignment, (typeof ALIGNMENT_FACETS)[number]['key']>>;
161
205
  /**
162
206
  * A cell's protection state, enforced only when the worksheet itself is protected. The flags
163
207
  * do nothing on an unprotected sheet. `locked` defaults to TRUE in OOXML (every cell is locked
@@ -1,19 +1,17 @@
1
- const NAMED_UNDERLINE_STYLES = {
1
+ import { tokenSet } from '../token-set.js';
2
+ export const isNamedUnderlineStyle = tokenSet({
2
3
  none: true,
3
4
  single: true,
4
5
  double: true,
5
6
  singleAccounting: true,
6
7
  doubleAccounting: true,
7
- };
8
- export function isNamedUnderlineStyle(value) {
9
- return Object.hasOwn(NAMED_UNDERLINE_STYLES, value);
10
- }
8
+ });
11
9
  export function parseArgb(value) {
12
10
  const hex = value.startsWith('#') ? value.slice(1) : value;
13
11
  const argb = hex.length === 6 ? `FF${hex}` : hex;
14
12
  return /^[0-9a-fA-F]{8}$/.test(argb) ? argb : undefined;
15
13
  }
16
- const FILL_PATTERN_TYPES = {
14
+ export const isFillPatternType = tokenSet({
17
15
  none: true,
18
16
  solid: true,
19
17
  gray125: true,
@@ -33,11 +31,8 @@ const FILL_PATTERN_TYPES = {
33
31
  lightUp: true,
34
32
  lightGrid: true,
35
33
  lightTrellis: true,
36
- };
37
- export function isFillPatternType(value) {
38
- return Object.hasOwn(FILL_PATTERN_TYPES, value);
39
- }
40
- const BORDER_STYLES = {
34
+ });
35
+ export const isBorderStyle = tokenSet({
41
36
  thin: true,
42
37
  medium: true,
43
38
  thick: true,
@@ -51,22 +46,13 @@ const BORDER_STYLES = {
51
46
  dashDotDot: true,
52
47
  mediumDashDotDot: true,
53
48
  slantDashDot: true,
54
- };
55
- export function isBorderStyle(value) {
56
- return Object.hasOwn(BORDER_STYLES, value);
57
- }
58
- const FONT_VERTICAL_ALIGNMENTS = {
49
+ });
50
+ export const isFontVerticalAlignment = tokenSet({
59
51
  superscript: true,
60
52
  subscript: true,
61
- };
62
- export function isFontVerticalAlignment(value) {
63
- return Object.hasOwn(FONT_VERTICAL_ALIGNMENTS, value);
64
- }
65
- const FONT_SCHEMES = { minor: true, major: true, none: true };
66
- export function isFontScheme(value) {
67
- return Object.hasOwn(FONT_SCHEMES, value);
68
- }
69
- const HORIZONTAL_ALIGNMENTS = {
53
+ });
54
+ export const isFontScheme = tokenSet({ minor: true, major: true, none: true });
55
+ export const isHorizontalAlignment = tokenSet({
70
56
  general: true,
71
57
  left: true,
72
58
  center: true,
@@ -75,20 +61,29 @@ const HORIZONTAL_ALIGNMENTS = {
75
61
  justify: true,
76
62
  centerContinuous: true,
77
63
  distributed: true,
78
- };
79
- export function isHorizontalAlignment(value) {
80
- return Object.hasOwn(HORIZONTAL_ALIGNMENTS, value);
81
- }
82
- const VERTICAL_ALIGNMENTS = {
64
+ });
65
+ export const isVerticalAlignment = tokenSet({
83
66
  top: true,
84
67
  center: true,
85
68
  bottom: true,
86
69
  justify: true,
87
70
  distributed: true,
88
- };
89
- export function isVerticalAlignment(value) {
90
- return Object.hasOwn(VERTICAL_ALIGNMENTS, value);
91
- }
71
+ });
72
+ export const ALIGNMENT_FACETS = [
73
+ {
74
+ key: 'horizontal',
75
+ kind: 'token',
76
+ isValid: isHorizontalAlignment,
77
+ label: 'horizontal alignment',
78
+ omit: 'general',
79
+ },
80
+ { key: 'vertical', kind: 'token', isValid: isVerticalAlignment, label: 'vertical alignment' },
81
+ { key: 'textRotation', kind: 'number' },
82
+ { key: 'wrapText', kind: 'flag' },
83
+ { key: 'indent', kind: 'number' },
84
+ { key: 'shrinkToFit', kind: 'flag' },
85
+ { key: 'readingOrder', kind: 'number' },
86
+ ];
92
87
  const CELL_STYLE_FACET_KEYS = {
93
88
  fill: true,
94
89
  numFmt: true,
@@ -75,7 +75,7 @@ export declare const TOTALS_ROW_SUBTOTAL_CODE: Readonly<Partial<Record<TotalsRow
75
75
  */
76
76
  export type TotalsRowFunction = 'average' | 'countNums' | 'count' | 'max' | 'min' | 'stdDev' | 'sum' | 'var' | 'custom' | 'none';
77
77
  /** Narrow a raw `totalsRowFunction` attribute to a known {@link TotalsRowFunction}. */
78
- export declare function isTotalsRowFunction(value: string): value is TotalsRowFunction;
78
+ export declare const isTotalsRowFunction: (value: string) => value is TotalsRowFunction;
79
79
  /** One column of a table: a header name and its optional totals-row behaviour. */
80
80
  export interface TableColumn {
81
81
  /** The column's header/display name. Must be unique within the table (case-insensitively):
@@ -1,4 +1,5 @@
1
1
  import { AuthoringError } from '../errors.js';
2
+ import { tokenSet } from '../token-set.js';
2
3
  import { decodeCellRef, encodeAddress } from './address.js';
3
4
  import { isDeletedSpan, shiftIndex } from './grid-shift.js';
4
5
  function cloneStyleInfo(style) {
@@ -25,7 +26,7 @@ export const TOTALS_ROW_SUBTOTAL_CODE = {
25
26
  sum: 109,
26
27
  var: 110,
27
28
  };
28
- const TOTALS_ROW_FUNCTIONS = {
29
+ export const isTotalsRowFunction = tokenSet({
29
30
  average: true,
30
31
  countNums: true,
31
32
  count: true,
@@ -36,10 +37,7 @@ const TOTALS_ROW_FUNCTIONS = {
36
37
  var: true,
37
38
  custom: true,
38
39
  none: true,
39
- };
40
- export function isTotalsRowFunction(value) {
41
- return Object.hasOwn(TOTALS_ROW_FUNCTIONS, value);
42
- }
40
+ });
43
41
  const IDENTIFIER = /^[\p{L}\\_][\p{L}\p{N}._]*$/u;
44
42
  function disambiguateColumnNames(columns) {
45
43
  const seen = new Set();
@@ -0,0 +1,44 @@
1
+ import { type Cell } from './cell.ts';
2
+ import type { MergeRect } from './merge.ts';
3
+ /**
4
+ * The sheet storage an extent reads. Held by reference and never reassigned, so the extent always
5
+ * sees the live grid; the value types are irrelevant to it, only which keys are present.
6
+ */
7
+ export interface ExtentStorage {
8
+ readonly rows: ReadonlyMap<number, ReadonlyMap<number, Cell>>;
9
+ readonly rowProperties: ReadonlyMap<number, unknown>;
10
+ readonly columns: ReadonlyMap<number, unknown>;
11
+ readonly mergeRects: readonly MergeRect[];
12
+ }
13
+ /**
14
+ * The last used row and column of a sheet, tracked incrementally. The sheet reports every edit that
15
+ * can move the grid's outer keys; this turns those reports into the two accessors behind
16
+ * `Worksheet.rowCount` and `Worksheet.columnCount`.
17
+ */
18
+ export declare class UsedExtent {
19
+ #private;
20
+ constructor(storage: ExtentStorage);
21
+ /** Note a cell materialised at `(row, col)`. Whether it goes on to carry anything is not this
22
+ * class's business: the bound is on where a used cell *could* be, and the read confirms it. */
23
+ noteCell(row: number, col: number): void;
24
+ /** Note a row that bounds the used range by declaration: one given properties of its own. */
25
+ noteDeclaredRow(row: number): void;
26
+ /** Note a column that bounds the used range by declaration: one given properties of its own. */
27
+ noteDeclaredColumn(col: number): void;
28
+ /** Note a merged region, which occupies its whole rectangle and so bounds both axes. */
29
+ noteMerge(rect: MergeRect): void;
30
+ /**
31
+ * Report an edit that can pull the grid's outer keys inward: an eviction, a splice, an unmerge, a
32
+ * row replaced wholesale. The bounds are recomputed once, on the next read, rather than adjusted
33
+ * here, because finding the *new* maximum after a deletion is the same scan as finding it from
34
+ * scratch. Appending never invalidates, which is the whole point.
35
+ */
36
+ invalidate(): void;
37
+ /** Report that the grid has been emptied, which puts the bounds at a known zero rather than
38
+ * stale: nothing is left to scan. */
39
+ reset(): void;
40
+ /** The 1-based index of the last row carrying anything, or 0 when the sheet holds nothing. */
41
+ get lastRow(): number;
42
+ /** The 1-based index of the last column carrying anything, or 0 when the sheet holds nothing. */
43
+ get lastColumn(): number;
44
+ }