@shbernal/ts-xlsx 1.0.2 → 1.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 (114) hide show
  1. package/README.md +6 -0
  2. package/dist/core/autofilter.js +2 -2
  3. package/dist/core/cell.d.ts +10 -0
  4. package/dist/core/cell.js +16 -4
  5. package/dist/core/color-resolution.js +1 -1
  6. package/dist/core/column.d.ts +9 -1
  7. package/dist/core/column.js +11 -3
  8. package/dist/core/conditional-formatting-overlay.js +1 -1
  9. package/dist/core/data-validation-overlay.js +2 -2
  10. package/dist/core/formula.js +2 -2
  11. package/dist/core/grid-edits.js +5 -5
  12. package/dist/core/limits.d.ts +30 -0
  13. package/dist/core/limits.js +47 -0
  14. package/dist/core/merge.js +1 -1
  15. package/dist/core/pivot-table.js +7 -3
  16. package/dist/core/range.js +8 -4
  17. package/dist/core/row.d.ts +9 -1
  18. package/dist/core/row.js +11 -3
  19. package/dist/core/table-style.d.ts +2 -2
  20. package/dist/core/table-style.js +2 -2
  21. package/dist/core/table.js +2 -2
  22. package/dist/core/text-metrics.d.ts +20 -0
  23. package/dist/core/text-metrics.js +50 -0
  24. package/dist/core/theme.d.ts +1 -1
  25. package/dist/core/theme.js +1 -1
  26. package/dist/core/value.d.ts +50 -10
  27. package/dist/core/value.js +76 -0
  28. package/dist/core/workbook-protection.d.ts +1 -1
  29. package/dist/core/workbook.d.ts +24 -11
  30. package/dist/core/workbook.js +45 -22
  31. package/dist/core/worksheet-model.d.ts +1 -1
  32. package/dist/core/worksheet-model.js +5 -5
  33. package/dist/core/worksheet.d.ts +24 -2
  34. package/dist/core/worksheet.js +49 -21
  35. package/dist/customui/errors.js +1 -1
  36. package/dist/customui/index.js +2 -2
  37. package/dist/customui/ribbon.d.ts +1 -1
  38. package/dist/customui/ribbon.js +3 -3
  39. package/dist/entries/core.d.ts +3 -1
  40. package/dist/entries/core.js +16 -14
  41. package/dist/entries/csv.js +2 -2
  42. package/dist/entries/customui.js +1 -1
  43. package/dist/entries/errors.js +7 -7
  44. package/dist/entries/vba.js +2 -2
  45. package/dist/entries/xlsb.js +1 -1
  46. package/dist/entries/xlsx.js +5 -5
  47. package/dist/index.js +7 -7
  48. package/dist/io/csv/read.js +2 -2
  49. package/dist/io/csv/write.js +12 -24
  50. package/dist/io/opc/errors.js +1 -1
  51. package/dist/io/opc/inflate.d.ts +1 -1
  52. package/dist/io/opc/inflate.js +2 -2
  53. package/dist/io/opc/read-opc.js +2 -2
  54. package/dist/io/opc/rels.js +2 -2
  55. package/dist/io/opc/sniff-format.js +2 -2
  56. package/dist/io/style/xf-style.js +2 -2
  57. package/dist/io/xlsb/errors.js +1 -1
  58. package/dist/io/xlsb/formula.js +4 -4
  59. package/dist/io/xlsb/primitives.js +2 -2
  60. package/dist/io/xlsb/read-shared-strings.js +3 -3
  61. package/dist/io/xlsb/read-styles.js +5 -5
  62. package/dist/io/xlsb/read-worksheet.js +9 -9
  63. package/dist/io/xlsb/read.d.ts +1 -1
  64. package/dist/io/xlsb/read.js +15 -15
  65. package/dist/io/xlsb/record-stream.js +1 -1
  66. package/dist/io/xlsx/cell-accumulator.js +6 -6
  67. package/dist/io/xlsx/cell-value.js +4 -4
  68. package/dist/io/xlsx/comments.js +4 -4
  69. package/dist/io/xlsx/conditional-formatting.js +5 -5
  70. package/dist/io/xlsx/data-validation.js +4 -4
  71. package/dist/io/xlsx/edit-vba.js +4 -4
  72. package/dist/io/xlsx/errors.js +1 -1
  73. package/dist/io/xlsx/hyperlinks.js +4 -4
  74. package/dist/io/xlsx/images.js +6 -6
  75. package/dist/io/xlsx/package-plan.js +3 -3
  76. package/dist/io/xlsx/pivot-read.js +2 -2
  77. package/dist/io/xlsx/pivot.js +4 -4
  78. package/dist/io/xlsx/read-rows.d.ts +3 -3
  79. package/dist/io/xlsx/read-rows.js +12 -12
  80. package/dist/io/xlsx/read-styles.js +4 -4
  81. package/dist/io/xlsx/read-worksheet.js +8 -8
  82. package/dist/io/xlsx/read.d.ts +1 -1
  83. package/dist/io/xlsx/read.js +28 -28
  84. package/dist/io/xlsx/relationships.d.ts +6 -6
  85. package/dist/io/xlsx/relationships.js +2 -2
  86. package/dist/io/xlsx/rich-runs.js +1 -1
  87. package/dist/io/xlsx/rich-text.js +2 -2
  88. package/dist/io/xlsx/shared-formulas.js +3 -3
  89. package/dist/io/xlsx/shared-strings-read.js +2 -2
  90. package/dist/io/xlsx/shared-strings.js +3 -3
  91. package/dist/io/xlsx/sheet-properties.js +4 -4
  92. package/dist/io/xlsx/styles.js +6 -6
  93. package/dist/io/xlsx/tables.js +5 -5
  94. package/dist/io/xlsx/threaded-comments.js +5 -5
  95. package/dist/io/xlsx/workbook-xml.js +9 -9
  96. package/dist/io/xlsx/worksheet-xml.js +18 -18
  97. package/dist/io/xlsx/write-stream.d.ts +1 -1
  98. package/dist/io/xlsx/write-stream.js +7 -7
  99. package/dist/io/xlsx/write.d.ts +3 -3
  100. package/dist/io/xlsx/write.js +20 -20
  101. package/dist/io/xlsx/x14-ext.js +1 -1
  102. package/dist/vba/cfb-writer.js +1 -1
  103. package/dist/vba/cfb.js +1 -1
  104. package/dist/vba/codepage.js +1 -1
  105. package/dist/vba/errors.js +1 -1
  106. package/dist/vba/index.js +5 -5
  107. package/dist/vba/ms-ovba.js +1 -1
  108. package/dist/vba/project-editor.js +7 -7
  109. package/dist/vba/project.js +4 -4
  110. package/dist/vba/vba-encoding.js +1 -1
  111. package/dist/xml/errors.js +1 -1
  112. package/dist/xml/xml-read.js +1 -1
  113. package/dist/xml/xml.js +1 -1
  114. package/package.json +4 -3
@@ -38,23 +38,44 @@ const ERROR_SET = new Set(ERROR_CODES);
38
38
  function hasKey(value, key) {
39
39
  return typeof value === 'object' && value !== null && key in value;
40
40
  }
41
+ /**
42
+ * Whether a value is an in-cell error ({@link ErrorValue}). The narrowing counterpart of
43
+ * `detectValueType(value) === ValueType.Error`: use this one when the branch goes on to read
44
+ * `.error`, and {@link detectValueType} when it dispatches over all nine kinds at once.
45
+ */
41
46
  export function isErrorValue(value) {
42
47
  return hasKey(value, 'error');
43
48
  }
49
+ /**
50
+ * Whether a value is a cell's own formula ({@link FormulaValue}) — a master, or a formula
51
+ * belonging to no shared group. A shared-formula clone is **not** one of these; see
52
+ * {@link isSharedFormulaValue}. Both report as `ValueType.Formula`, so a caller that means "any
53
+ * formula-shaped cell" wants {@link detectValueType}, not this.
54
+ */
44
55
  export function isFormulaValue(value) {
45
56
  // A shared-formula clone resolved on read carries both its master address (`sharedFormula`) and the
46
57
  // translated `formula`; it is a SharedFormulaValue, so exclude it here to keep the two kinds distinct.
47
58
  return hasKey(value, 'formula') && !('sharedFormula' in value);
48
59
  }
60
+ /** Whether a value is a clone participating in a shared formula ({@link SharedFormulaValue}). */
49
61
  export function isSharedFormulaValue(value) {
50
62
  return hasKey(value, 'sharedFormula');
51
63
  }
64
+ /** Whether a value is a What-If-Analysis data-table formula ({@link DataTableFormulaValue}). */
52
65
  export function isDataTableFormulaValue(value) {
53
66
  return hasKey(value, 'shareType') && value.shareType === 'dataTable';
54
67
  }
68
+ /**
69
+ * Whether a value is composed of formatted runs ({@link RichTextValue}). This is the test to
70
+ * make before {@link richTextToPlain}, which accepts nothing else.
71
+ */
55
72
  export function isRichTextValue(value) {
56
73
  return hasKey(value, 'richText');
57
74
  }
75
+ /**
76
+ * Whether a value is a hyperlink ({@link HyperlinkValue}). Note that its `text` is itself either
77
+ * a string or a {@link RichTextValue}, so reading the label out means one more narrowing.
78
+ */
58
79
  export function isHyperlinkValue(value) {
59
80
  return hasKey(value, 'hyperlink');
60
81
  }
@@ -66,6 +87,54 @@ export function isHyperlinkValue(value) {
66
87
  export function richTextToPlain(value) {
67
88
  return value.richText.map((run) => run.text).join('');
68
89
  }
90
+ /**
91
+ * The plain text of any cell value — total over {@link CellValue}, so a caller reading a sheet
92
+ * whose cells it did not write never has to switch on the union itself.
93
+ *
94
+ * This is the *value's* text, not the cell's *display* text: a number renders as JavaScript
95
+ * renders it, with no number format applied (`0.1 + 0.2` is `"0.30000000000000004"`, a currency
96
+ * cell has no currency sign), because the format lives on the style and this function is given
97
+ * only the value. What each kind yields:
98
+ *
99
+ * - the empty cell (`null`) and an invalid `Date` → `""`, the two ways a cell has no text
100
+ * - a boolean → `"TRUE"` / `"FALSE"`, Excel's own literals rather than JavaScript's
101
+ * - a `Date` → a full ISO-8601 timestamp
102
+ * - an error → its literal, e.g. `"#REF!"` — the same string the grid shows
103
+ * - rich text → every run concatenated ({@link richTextToPlain})
104
+ * - a hyperlink → its label, never its destination
105
+ * - any of the three formula kinds → the text of the *cached result*, and `""` when the cell
106
+ * carries no cached result: the formula source is not text the sheet ever displayed
107
+ */
108
+ export function cellValueToText(value) {
109
+ if (value === null)
110
+ return '';
111
+ switch (typeof value) {
112
+ case 'number':
113
+ return String(value);
114
+ case 'string':
115
+ return value;
116
+ case 'boolean':
117
+ return value ? 'TRUE' : 'FALSE';
118
+ default:
119
+ break;
120
+ }
121
+ if (value instanceof Date)
122
+ return Number.isNaN(value.getTime()) ? '' : value.toISOString();
123
+ // Same precedence as detectValueType, and for the same reason: the outer shape wins, so a
124
+ // hyperlink whose label is rich text renders as a hyperlink's label, not as rich text.
125
+ if (isHyperlinkValue(value)) {
126
+ return typeof value.text === 'string' ? value.text : richTextToPlain(value.text);
127
+ }
128
+ // Every formula kind carries a `result` of the same optional shape; one recursion renders it.
129
+ if (isFormulaValue(value) || isSharedFormulaValue(value) || isDataTableFormulaValue(value)) {
130
+ return value.result === undefined ? '' : cellValueToText(value.result);
131
+ }
132
+ if (isRichTextValue(value))
133
+ return richTextToPlain(value);
134
+ if (isErrorValue(value))
135
+ return value.error;
136
+ return unsupportedValue(value);
137
+ }
69
138
  /**
70
139
  * Classify a value into its observable {@link ValueType}. This is total over
71
140
  * {@link CellValue}: every legal value has exactly one type. A `Date` is a date even
@@ -98,6 +167,13 @@ export function detectValueType(value) {
98
167
  return ValueType.RichText;
99
168
  if (isErrorValue(value))
100
169
  return ValueType.Error;
170
+ return unsupportedValue(value);
171
+ }
172
+ /**
173
+ * The verdict "this is not a cell value at all", raised from one place so that every function
174
+ * total over {@link CellValue} rejects the same inputs with the same message.
175
+ */
176
+ function unsupportedValue(value) {
101
177
  throw new TypeError(`unsupported cell value: ${describe(value)}`);
102
178
  }
103
179
  /** Whether a string is one of Excel's canonical error literals. */
@@ -6,7 +6,7 @@
6
6
  * revisions guard (`revisions*`). The reader accepts only these names, so a hostile or unknown
7
7
  * attribute is never echoed back into the output.
8
8
  */
9
- export declare const WORKBOOK_PROTECTION_CREDENTIAL_ATTRS: readonly ["workbookPassword", "workbookAlgorithmName", "workbookHashValue", "workbookSaltValue", "workbookSpinCount", "revisionsPassword", "revisionsAlgorithmName", "revisionsHashValue", "revisionsSaltValue", "revisionsSpinCount"];
9
+ export declare const WORKBOOK_PROTECTION_CREDENTIAL_ATTRS: readonly ['workbookPassword', 'workbookAlgorithmName', 'workbookHashValue', 'workbookSaltValue', 'workbookSpinCount', 'revisionsPassword', 'revisionsAlgorithmName', 'revisionsHashValue', 'revisionsSaltValue', 'revisionsSpinCount'];
10
10
  /** One of the attribute names {@link WORKBOOK_PROTECTION_CREDENTIAL_ATTRS} enumerates. */
11
11
  export type WorkbookProtectionCredentialAttr = (typeof WORKBOOK_PROTECTION_CREDENTIAL_ATTRS)[number];
12
12
  /**
@@ -173,7 +173,7 @@ export declare class Workbook {
173
173
  * returned objects changes nothing on write; the original `customUI` XML is re-emitted byte-for-byte
174
174
  * regardless. Parsed lazily on first access and memoised.
175
175
  *
176
- * @throws {@link CustomUiParseError} if a `customUI` part is present but its XML is malformed.
176
+ * @throws {CustomUiParseError} if a `customUI` part is present but its XML is malformed.
177
177
  */
178
178
  get customUI(): readonly CustomUiDocument[];
179
179
  /**
@@ -182,7 +182,7 @@ export declare class Workbook {
182
182
  * verbatim — mutating the returned object changes nothing on write; the original macro blob is
183
183
  * re-emitted byte-for-byte regardless. Parsed lazily on first access and memoised.
184
184
  *
185
- * @throws {@link VbaParseError} if a macro project is present but its `vbaProject.bin` is malformed.
185
+ * @throws {VbaParseError} if a macro project is present but its `vbaProject.bin` is malformed.
186
186
  */
187
187
  get vbaProject(): VbaProject | undefined;
188
188
  /**
@@ -236,9 +236,9 @@ export declare class Workbook {
236
236
  * To author or edit module *source* (which needs real compiled p-code), use the offline
237
237
  * `tools/vba-compiler`, then attach its output via {@link vbaProjectBytes}.
238
238
  *
239
- * @throws {@link VbaAuthorError} if the workbook has no macro project, or `name` is not in the project,
239
+ * @throws {VbaAuthorError} if the workbook has no macro project, or `name` is not in the project,
240
240
  * or names a `document`/`designer` module.
241
- * @throws {@link VbaParseError} if the attached `vbaProject.bin` is malformed.
241
+ * @throws {VbaParseError} if the attached `vbaProject.bin` is malformed.
242
242
  */
243
243
  removeVbaModule(name: string): void;
244
244
  /**
@@ -247,9 +247,9 @@ export declare class Workbook {
247
247
  * {@link addVbaReference}). Replacing the project also drops a stale signature, as
248
248
  * {@link vbaProjectBytes} does.
249
249
  *
250
- * @throws {@link VbaAuthorError} if the workbook has no macro project, or any field of `ref` is invalid
250
+ * @throws {VbaAuthorError} if the workbook has no macro project, or any field of `ref` is invalid
251
251
  * (see {@link VbaLibraryReference}).
252
- * @throws {@link VbaParseError} if the attached `vbaProject.bin` is malformed.
252
+ * @throws {VbaParseError} if the attached `vbaProject.bin` is malformed.
253
253
  */
254
254
  addVbaReference(ref: VbaLibraryReference): void;
255
255
  /** The preserved differential-style (`<dxfs>`) fragments, in index order. */
@@ -283,7 +283,7 @@ export declare class Workbook {
283
283
  * Registering a name a source file already defined **overrides** that definition rather than adding
284
284
  * a second one beside it.
285
285
  *
286
- * @throws {@link AuthoringError} if the name is empty, or an element carries a `size` outside the four stripe
286
+ * @throws {AuthoringError} if the name is empty, or an element carries a `size` outside the four stripe
287
287
  * types, or a `size` is not a positive integer — see {@link checkTableStyle} for why those are
288
288
  * refused here rather than silently dropped.
289
289
  */
@@ -310,7 +310,7 @@ export declare class Workbook {
310
310
  * unauthored keeps the source's own encoding, including the `<a:sysClr>` form Excel uses for
311
311
  * `dk1`/`lt1` so they follow the viewer's window colours.
312
312
  *
313
- * @throws {@link AuthoringError} if a colour is not 6 or 8 hexadecimal digits.
313
+ * @throws {AuthoringError} if a colour is not 6 or 8 hexadecimal digits.
314
314
  */
315
315
  setTheme(overrides: ThemeOverrides): void;
316
316
  /**
@@ -345,7 +345,7 @@ export declare class Workbook {
345
345
  * follows {@link themeFonts}'s minor face, so `setTheme({fonts: {minor}})` already reaches every
346
346
  * unstyled cell and needs no second call here. See {@link defaultFont} for the full chain.
347
347
  *
348
- * @throws {@link AuthoringError} if `size` is not a positive finite number, or `name` is empty — both
348
+ * @throws {AuthoringError} if `size` is not a positive finite number, or `name` is empty — both
349
349
  * produce a styles part Excel renders from some other font without ever reporting why.
350
350
  */
351
351
  setDefaultFont(font: Font): void;
@@ -431,19 +431,32 @@ export declare class Workbook {
431
431
  /**
432
432
  * Register a defined name on the workbook.
433
433
  *
434
- * @throws {@link AuthoringError} if the name is empty, or if a {@link DefinedName.scope} is given that names no
434
+ * @throws {AuthoringError} if the name is empty, or if a {@link DefinedName.scope} is given that names no
435
435
  * existing worksheet — a scoped name must target a sheet that is already part of the workbook.
436
436
  */
437
437
  defineName(definedName: DefinedName): void;
438
438
  /**
439
439
  * Create a worksheet and append it to the workbook.
440
440
  *
441
- * @throws {@link AuthoringError} if the name is empty, too long, contains a forbidden character,
441
+ * @throws {AuthoringError} if the name is empty, too long, contains a forbidden character,
442
442
  * or collides (case-insensitively) with an existing sheet.
443
443
  */
444
444
  addWorksheet(name: string, options?: AddWorksheetOptions): Worksheet;
445
445
  /** Look up a worksheet by name (case-insensitive) or by numeric id. */
446
446
  getWorksheet(nameOrId: string | number): Worksheet | undefined;
447
+ /**
448
+ * {@link getWorksheet}, for a caller who knows the sheet is there — the miss throws instead of
449
+ * returning `undefined`, and the message names every sheet the workbook does have.
450
+ *
451
+ * The partial lookup is the right primitive for asking *whether* a sheet exists, and the wrong
452
+ * one for reaching a sheet a template is expected to carry: `undefined` flows on into a `?.`
453
+ * chain and fails several steps later with nothing left to say about which name was missing.
454
+ * That listing is the whole point — a lookup miss is a typo, a stale template or a renamed tab,
455
+ * and all three are answered by seeing the real names.
456
+ *
457
+ * @throws {AuthoringError} if no worksheet has that name (case-insensitive) or numeric id.
458
+ */
459
+ requireWorksheet(nameOrId: string | number): Worksheet;
447
460
  /**
448
461
  * The codec's channel into this workbook — see `core/internal.ts` for why these are not public
449
462
  * methods. Declared last so every private field it closes over is already in scope.
@@ -4,17 +4,17 @@
4
4
  // Excel's rules — names are unique case-insensitively, bounded in length, and free
5
5
  // of the characters Excel forbids — so an invalid book cannot be constructed in the
6
6
  // first place, rather than failing only at write time.
7
- import { isCustomUiRelType, parseCustomUi } from "../customui/index.js";
8
- import { AuthoringError } from "../errors.js";
9
- import { addVbaReference, parseVbaProject, removeVbaModule, VBA_PROJECT_CONTENT_TYPE, VBA_PROJECT_PART_PATH, VBA_PROJECT_REL_TYPE, VbaAuthorError, vbaProjectSignatureKind, } from "../vba/index.js";
10
- import { resolveColor } from "./color-resolution.js";
11
- import { commentThreadGuid } from "./comment-thread.js";
12
- import { replaceContents } from "./containers.js";
13
- import { normalizeImageExtension } from "./image.js";
14
- import { INTERNAL } from "./internal.js";
15
- import { checkTableStyle } from "./table-style.js";
16
- import { applyThemeOverrides, DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, DEFAULT_THEME_XML, OFFICE_BODY_FACE, parseThemeColorScheme, parseThemeFontScheme, THEME_COLOR_SLOTS, } from "./theme.js";
17
- import { Worksheet } from "./worksheet.js";
7
+ import { isCustomUiRelType, parseCustomUi } from '../customui/index.js';
8
+ import { AuthoringError } from '../errors.js';
9
+ import { addVbaReference, parseVbaProject, removeVbaModule, VBA_PROJECT_CONTENT_TYPE, VBA_PROJECT_PART_PATH, VBA_PROJECT_REL_TYPE, VbaAuthorError, vbaProjectSignatureKind, } from '../vba/index.js';
10
+ import { resolveColor } from './color-resolution.js';
11
+ import { commentThreadGuid } from './comment-thread.js';
12
+ import { replaceContents } from './containers.js';
13
+ import { normalizeImageExtension } from './image.js';
14
+ import { INTERNAL } from './internal.js';
15
+ import { checkTableStyle } from './table-style.js';
16
+ import { applyThemeOverrides, DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, DEFAULT_THEME_XML, OFFICE_BODY_FACE, parseThemeColorScheme, parseThemeFontScheme, THEME_COLOR_SLOTS, } from './theme.js';
17
+ import { Worksheet } from './worksheet.js';
18
18
  /**
19
19
  * The window geometry a workbook starts from — the values desktop Excel writes for its own default
20
20
  * window.
@@ -144,7 +144,7 @@ export class Workbook {
144
144
  * returned objects changes nothing on write; the original `customUI` XML is re-emitted byte-for-byte
145
145
  * regardless. Parsed lazily on first access and memoised.
146
146
  *
147
- * @throws {@link CustomUiParseError} if a `customUI` part is present but its XML is malformed.
147
+ * @throws {CustomUiParseError} if a `customUI` part is present but its XML is malformed.
148
148
  */
149
149
  get customUI() {
150
150
  if (!this.#customUiParsed) {
@@ -168,7 +168,7 @@ export class Workbook {
168
168
  * verbatim — mutating the returned object changes nothing on write; the original macro blob is
169
169
  * re-emitted byte-for-byte regardless. Parsed lazily on first access and memoised.
170
170
  *
171
- * @throws {@link VbaParseError} if a macro project is present but its `vbaProject.bin` is malformed.
171
+ * @throws {VbaParseError} if a macro project is present but its `vbaProject.bin` is malformed.
172
172
  */
173
173
  get vbaProject() {
174
174
  if (!this.#vbaParsed) {
@@ -280,9 +280,9 @@ export class Workbook {
280
280
  * To author or edit module *source* (which needs real compiled p-code), use the offline
281
281
  * `tools/vba-compiler`, then attach its output via {@link vbaProjectBytes}.
282
282
  *
283
- * @throws {@link VbaAuthorError} if the workbook has no macro project, or `name` is not in the project,
283
+ * @throws {VbaAuthorError} if the workbook has no macro project, or `name` is not in the project,
284
284
  * or names a `document`/`designer` module.
285
- * @throws {@link VbaParseError} if the attached `vbaProject.bin` is malformed.
285
+ * @throws {VbaParseError} if the attached `vbaProject.bin` is malformed.
286
286
  */
287
287
  removeVbaModule(name) {
288
288
  const bytes = this.vbaProjectBytes;
@@ -297,9 +297,9 @@ export class Workbook {
297
297
  * {@link addVbaReference}). Replacing the project also drops a stale signature, as
298
298
  * {@link vbaProjectBytes} does.
299
299
  *
300
- * @throws {@link VbaAuthorError} if the workbook has no macro project, or any field of `ref` is invalid
300
+ * @throws {VbaAuthorError} if the workbook has no macro project, or any field of `ref` is invalid
301
301
  * (see {@link VbaLibraryReference}).
302
- * @throws {@link VbaParseError} if the attached `vbaProject.bin` is malformed.
302
+ * @throws {VbaParseError} if the attached `vbaProject.bin` is malformed.
303
303
  */
304
304
  addVbaReference(ref) {
305
305
  const bytes = this.vbaProjectBytes;
@@ -357,7 +357,7 @@ export class Workbook {
357
357
  * Registering a name a source file already defined **overrides** that definition rather than adding
358
358
  * a second one beside it.
359
359
  *
360
- * @throws {@link AuthoringError} if the name is empty, or an element carries a `size` outside the four stripe
360
+ * @throws {AuthoringError} if the name is empty, or an element carries a `size` outside the four stripe
361
361
  * types, or a `size` is not a positive integer — see {@link checkTableStyle} for why those are
362
362
  * refused here rather than silently dropped.
363
363
  */
@@ -400,7 +400,7 @@ export class Workbook {
400
400
  * unauthored keeps the source's own encoding, including the `<a:sysClr>` form Excel uses for
401
401
  * `dk1`/`lt1` so they follow the viewer's window colours.
402
402
  *
403
- * @throws {@link AuthoringError} if a colour is not 6 or 8 hexadecimal digits.
403
+ * @throws {AuthoringError} if a colour is not 6 or 8 hexadecimal digits.
404
404
  */
405
405
  setTheme(overrides) {
406
406
  // Validated eagerly, by running the generation the writer will later run: a colour rejected at
@@ -468,7 +468,7 @@ export class Workbook {
468
468
  * follows {@link themeFonts}'s minor face, so `setTheme({fonts: {minor}})` already reaches every
469
469
  * unstyled cell and needs no second call here. See {@link defaultFont} for the full chain.
470
470
  *
471
- * @throws {@link AuthoringError} if `size` is not a positive finite number, or `name` is empty — both
471
+ * @throws {AuthoringError} if `size` is not a positive finite number, or `name` is empty — both
472
472
  * produce a styles part Excel renders from some other font without ever reporting why.
473
473
  */
474
474
  setDefaultFont(font) {
@@ -641,7 +641,7 @@ export class Workbook {
641
641
  /**
642
642
  * Register a defined name on the workbook.
643
643
  *
644
- * @throws {@link AuthoringError} if the name is empty, or if a {@link DefinedName.scope} is given that names no
644
+ * @throws {AuthoringError} if the name is empty, or if a {@link DefinedName.scope} is given that names no
645
645
  * existing worksheet — a scoped name must target a sheet that is already part of the workbook.
646
646
  */
647
647
  defineName(definedName) {
@@ -656,7 +656,7 @@ export class Workbook {
656
656
  /**
657
657
  * Create a worksheet and append it to the workbook.
658
658
  *
659
- * @throws {@link AuthoringError} if the name is empty, too long, contains a forbidden character,
659
+ * @throws {AuthoringError} if the name is empty, too long, contains a forbidden character,
660
660
  * or collides (case-insensitively) with an existing sheet.
661
661
  */
662
662
  addWorksheet(name, options = {}) {
@@ -673,6 +673,29 @@ export class Workbook {
673
673
  const target = nameOrId.toLowerCase();
674
674
  return this.#worksheets.find((sheet) => sheet.name.toLowerCase() === target);
675
675
  }
676
+ /**
677
+ * {@link getWorksheet}, for a caller who knows the sheet is there — the miss throws instead of
678
+ * returning `undefined`, and the message names every sheet the workbook does have.
679
+ *
680
+ * The partial lookup is the right primitive for asking *whether* a sheet exists, and the wrong
681
+ * one for reaching a sheet a template is expected to carry: `undefined` flows on into a `?.`
682
+ * chain and fails several steps later with nothing left to say about which name was missing.
683
+ * That listing is the whole point — a lookup miss is a typo, a stale template or a renamed tab,
684
+ * and all three are answered by seeing the real names.
685
+ *
686
+ * @throws {AuthoringError} if no worksheet has that name (case-insensitive) or numeric id.
687
+ */
688
+ requireWorksheet(nameOrId) {
689
+ const sheet = this.getWorksheet(nameOrId);
690
+ if (sheet !== undefined)
691
+ return sheet;
692
+ const wanted = typeof nameOrId === 'number' ? `id ${nameOrId}` : JSON.stringify(nameOrId);
693
+ if (this.#worksheets.length === 0) {
694
+ throw new AuthoringError(`no worksheet ${wanted}: this workbook has no worksheets`);
695
+ }
696
+ const have = this.#worksheets.map((sheet) => JSON.stringify(sheet.name)).join(', ');
697
+ throw new AuthoringError(`no worksheet ${wanted}; this workbook has ${have}`);
698
+ }
676
699
  #assertValidSheetName(name) {
677
700
  if (name.length === 0) {
678
701
  throw new AuthoringError('worksheet name cannot be empty');
@@ -22,7 +22,7 @@ interface ModelFacet<K extends keyof WorksheetModel = keyof WorksheetModel> {
22
22
  * load-bearing: cells are placed at their exact positions before any merge exists, so a covered
23
23
  * cell's value lands where the model says instead of being routed to a region master mid-load.
24
24
  */
25
- export declare const WORKSHEET_MODEL_FACETS: (ModelFacet<"state"> | ModelFacet<"tabColor"> | ModelFacet<"properties"> | ModelFacet<"outline"> | ModelFacet<"pageSetup"> | ModelFacet<"printOptions"> | ModelFacet<"pageMargins"> | ModelFacet<"headerFooter"> | ModelFacet<"rowBreaks"> | ModelFacet<"columnBreaks"> | ModelFacet<"columns"> | ModelFacet<"rows"> | ModelFacet<"cells"> | ModelFacet<"merges"> | ModelFacet<"dataValidations"> | ModelFacet<"conditionalFormattings"> | ModelFacet<"tables"> | ModelFacet<"autoFilter"> | ModelFacet<"protection">)[];
25
+ export declare const WORKSHEET_MODEL_FACETS: (ModelFacet<"autoFilter"> | ModelFacet<"cells"> | ModelFacet<"columnBreaks"> | ModelFacet<"columns"> | ModelFacet<"conditionalFormattings"> | ModelFacet<"dataValidations"> | ModelFacet<"headerFooter"> | ModelFacet<"merges"> | ModelFacet<"outline"> | ModelFacet<"pageMargins"> | ModelFacet<"pageSetup"> | ModelFacet<"printOptions"> | ModelFacet<"properties"> | ModelFacet<"protection"> | ModelFacet<"rowBreaks"> | ModelFacet<"rows"> | ModelFacet<"state"> | ModelFacet<"tabColor"> | ModelFacet<"tables">)[];
26
26
  type AssertNever<T extends never> = T;
27
27
  /**
28
28
  * Compile-time proof that {@link WORKSHEET_MODEL_FACETS} covers every {@link WorksheetModel} field.
@@ -6,11 +6,11 @@
6
6
  // model contract exists to prevent. Here each field declares both directions in one place, and the
7
7
  // registry is proved exhaustive over `keyof WorksheetModel` at compile time, so adding a field
8
8
  // without wiring it is an error naming the field rather than a review catch.
9
- import { cellToModel, copyCellContent } from "./cell.js";
10
- import { cloneConditionalFormatting } from "./conditional-formatting.js";
11
- import { overwrite, replaceContents } from "./containers.js";
12
- import { cloneDataValidation } from "./data-validation.js";
13
- import { INTERNAL } from "./internal.js";
9
+ import { cellToModel, copyCellContent } from './cell.js';
10
+ import { cloneConditionalFormatting } from './conditional-formatting.js';
11
+ import { overwrite, replaceContents } from './containers.js';
12
+ import { cloneDataValidation } from './data-validation.js';
13
+ import { INTERNAL } from './internal.js';
14
14
  function facet(key, read, write) {
15
15
  return { key, read, write: (sheet, model) => write(sheet, model[key]) };
16
16
  }
@@ -259,6 +259,28 @@ export declare class Worksheet {
259
259
  * other axis: a value in column E makes this 5 even if columns B–D are empty.
260
260
  */
261
261
  get columnCount(): number;
262
+ /**
263
+ * The sheet's used range as one handle — `A1` through the last row and column that carry
264
+ * anything — or `undefined` when there is no rectangle to name.
265
+ *
266
+ * This is {@link rowCount} and {@link columnCount} said once, so a caller stops reassembling
267
+ * `A1:${numberToColumn(sheet.columnCount)}${sheet.rowCount}` by hand. That is what an
268
+ * {@link autoFilter} covering the whole sheet wants — `sheet.autoFilter = sheet.usedRange.address`
269
+ * — and Excel writes exactly that ref for a filter it applies itself. A header-only ref filters
270
+ * nothing, which is the bug this exists to make hard to write.
271
+ *
272
+ * It inherits both counts' definition of *used*, so it spans gaps (a value in `E5` and nothing
273
+ * else still gives `A1:E5`) and includes a line carrying only its own formatting — a set column
274
+ * width, an outline level, a merge reaching past the last value. `undefined` therefore means
275
+ * strictly "no rectangle": an empty sheet, or one carrying only row formatting and no columns at
276
+ * all (or the reverse), where an axis has no extent to bound the other against.
277
+ *
278
+ * Not the same thing as the `<dimension>` a written package records. That is the *tight* box —
279
+ * top-left at the first used cell, formatting-only rows excluded — because Excel writes it to
280
+ * describe where the data is, not what the grid spans. This handle is anchored at `A1`, because
281
+ * a caller asking for the used range means the block to read, style or filter.
282
+ */
283
+ get usedRange(): Range | undefined;
262
284
  /** The columns carrying format properties, as handles, in ascending index order. */
263
285
  columns(): IterableIterator<Column>;
264
286
  /**
@@ -275,7 +297,7 @@ export declare class Worksheet {
275
297
  * name, at least one column, at least one row) are enforced here; conflicts with the
276
298
  * rest of the sheet (e.g. an overlapping merge) are the writer's concern.
277
299
  *
278
- * @throws {@link AuthoringError} if the name, columns, or geometry are invalid.
300
+ * @throws {AuthoringError} if the name, columns, or geometry are invalid.
279
301
  */
280
302
  addTable(options: TableOptions): Table;
281
303
  /** The tables defined on this sheet, in definition order. */
@@ -288,7 +310,7 @@ export declare class Worksheet {
288
310
  * read once, now, so the pivot is a snapshot: later edits to the source do not change it. The
289
311
  * supported shape (one summed value field, at least one row and column field) is enforced here.
290
312
  *
291
- * @throws {@link AuthoringError} if the metric, fields, or source shape are unsupported.
313
+ * @throws {AuthoringError} if the metric, fields, or source shape are unsupported.
292
314
  */
293
315
  addPivotTable(options: PivotTableOptions): PivotTable;
294
316
  /** The pivot tables hosted on this sheet, in definition order. */
@@ -5,25 +5,25 @@
5
5
  // row metadata (widths, heights, visibility, outline grouping) are stored apart from
6
6
  // the cell grid, because a column or row can carry formatting while holding no cells.
7
7
  // Merges and views layer on in later slices.
8
- import { AuthoringError } from "../errors.js";
9
- import { decodeAddress, decodeRange, encodeAddress } from "./address.js";
10
- import { canonicalizeAutoFilter } from "./autofilter.js";
11
- import { applyCellStyle, Cell, copyCellContent } from "./cell.js";
12
- import { Column } from "./column.js";
13
- import { commentThreadGuid, commentThreadOffset } from "./comment-thread.js";
14
- import { ConditionalFormattingOverlay } from "./conditional-formatting-overlay.js";
15
- import { replaceContents } from "./containers.js";
16
- import { DataValidationOverlay } from "./data-validation-overlay.js";
17
- import { GridEdits } from "./grid-edits.js";
18
- import { PX_TO_EMU, resolveAnchorPoint, } from "./image.js";
19
- import { INTERNAL } from "./internal.js";
20
- import { rectsOverlap } from "./merge.js";
21
- import { PivotTable } from "./pivot-table.js";
22
- import { deriveCredential, } from "./protection.js";
23
- import { Range, rangeFrom } from "./range.js";
24
- import { Row } from "./row.js";
25
- import { Table, TOTALS_ROW_SUBTOTAL_CODE } from "./table.js";
26
- import { WORKSHEET_MODEL_FACETS } from "./worksheet-model.js";
8
+ import { AuthoringError } from '../errors.js';
9
+ import { decodeAddress, decodeRange, encodeAddress } from './address.js';
10
+ import { canonicalizeAutoFilter } from './autofilter.js';
11
+ import { applyCellStyle, Cell, copyCellContent } from './cell.js';
12
+ import { Column } from './column.js';
13
+ import { commentThreadGuid, commentThreadOffset } from './comment-thread.js';
14
+ import { ConditionalFormattingOverlay } from './conditional-formatting-overlay.js';
15
+ import { replaceContents } from './containers.js';
16
+ import { DataValidationOverlay } from './data-validation-overlay.js';
17
+ import { GridEdits } from './grid-edits.js';
18
+ import { PX_TO_EMU, resolveAnchorPoint, } from './image.js';
19
+ import { INTERNAL } from './internal.js';
20
+ import { rectsOverlap } from './merge.js';
21
+ import { PivotTable } from './pivot-table.js';
22
+ import { deriveCredential, } from './protection.js';
23
+ import { Range, rangeFrom } from './range.js';
24
+ import { Row } from './row.js';
25
+ import { Table, TOTALS_ROW_SUBTOTAL_CODE } from './table.js';
26
+ import { WORKSHEET_MODEL_FACETS } from './worksheet-model.js';
27
27
  export class Worksheet {
28
28
  name;
29
29
  /** 1-based workbook-assigned id, stable for the sheet's lifetime. */
@@ -260,6 +260,34 @@ export class Worksheet {
260
260
  }
261
261
  return last;
262
262
  }
263
+ /**
264
+ * The sheet's used range as one handle — `A1` through the last row and column that carry
265
+ * anything — or `undefined` when there is no rectangle to name.
266
+ *
267
+ * This is {@link rowCount} and {@link columnCount} said once, so a caller stops reassembling
268
+ * `A1:${numberToColumn(sheet.columnCount)}${sheet.rowCount}` by hand. That is what an
269
+ * {@link autoFilter} covering the whole sheet wants — `sheet.autoFilter = sheet.usedRange.address`
270
+ * — and Excel writes exactly that ref for a filter it applies itself. A header-only ref filters
271
+ * nothing, which is the bug this exists to make hard to write.
272
+ *
273
+ * It inherits both counts' definition of *used*, so it spans gaps (a value in `E5` and nothing
274
+ * else still gives `A1:E5`) and includes a line carrying only its own formatting — a set column
275
+ * width, an outline level, a merge reaching past the last value. `undefined` therefore means
276
+ * strictly "no rectangle": an empty sheet, or one carrying only row formatting and no columns at
277
+ * all (or the reverse), where an axis has no extent to bound the other against.
278
+ *
279
+ * Not the same thing as the `<dimension>` a written package records. That is the *tight* box —
280
+ * top-left at the first used cell, formatting-only rows excluded — because Excel writes it to
281
+ * describe where the data is, not what the grid spans. This handle is anchored at `A1`, because
282
+ * a caller asking for the used range means the block to read, style or filter.
283
+ */
284
+ get usedRange() {
285
+ const bottom = this.rowCount;
286
+ const right = this.columnCount;
287
+ if (bottom === 0 || right === 0)
288
+ return undefined;
289
+ return new Range(this, 1, 1, bottom, right);
290
+ }
263
291
  /** The columns carrying format properties, as handles, in ascending index order. */
264
292
  *columns() {
265
293
  for (const index of [...this.#columns.keys()].sort((a, b) => a - b)) {
@@ -285,7 +313,7 @@ export class Worksheet {
285
313
  * name, at least one column, at least one row) are enforced here; conflicts with the
286
314
  * rest of the sheet (e.g. an overlapping merge) are the writer's concern.
287
315
  *
288
- * @throws {@link AuthoringError} if the name, columns, or geometry are invalid.
316
+ * @throws {AuthoringError} if the name, columns, or geometry are invalid.
289
317
  */
290
318
  addTable(options) {
291
319
  const table = new Table(options, (row, col, value, style) => {
@@ -372,7 +400,7 @@ export class Worksheet {
372
400
  * read once, now, so the pivot is a snapshot: later edits to the source do not change it. The
373
401
  * supported shape (one summed value field, at least one row and column field) is enforced here.
374
402
  *
375
- * @throws {@link AuthoringError} if the metric, fields, or source shape are unsupported.
403
+ * @throws {AuthoringError} if the metric, fields, or source shape are unsupported.
376
404
  */
377
405
  addPivotTable(options) {
378
406
  const pivot = new PivotTable(options);
@@ -1,4 +1,4 @@
1
- import { XlsxError } from "../errors.js";
1
+ import { XlsxError } from '../errors.js';
2
2
  /**
3
3
  * Thrown when a `customUI` ribbon-customisation part (`customUI/customUI.xml` or
4
4
  * `customUI/customUI14.xml`) is present but cannot be parsed into a {@link CustomUiDocument} — malformed
@@ -3,5 +3,5 @@
3
3
  // Those parts are preserved byte-for-byte on round-trip (see `src/core/preserved.ts`); this is a
4
4
  // projection over them, the same posture as the VBA read view (`src/vba`). Authoring/editing the ribbon
5
5
  // is out of scope — round-trip fidelity already comes from verbatim preservation.
6
- export { CustomUiParseError } from "./errors.js";
7
- export { CUSTOMUI_2006_NAMESPACE, CUSTOMUI_2007_REL_TYPE, CUSTOMUI_2009_NAMESPACE, CUSTOMUI_2010_REL_TYPE, isCustomUiRelType, parseCustomUi, } from "./ribbon.js";
6
+ export { CustomUiParseError } from './errors.js';
7
+ export { CUSTOMUI_2006_NAMESPACE, CUSTOMUI_2007_REL_TYPE, CUSTOMUI_2009_NAMESPACE, CUSTOMUI_2010_REL_TYPE, isCustomUiRelType, parseCustomUi, } from './ribbon.js';
@@ -80,7 +80,7 @@ export interface CustomUiDocument {
80
80
  /**
81
81
  * Parse a `customUI` part (raw UTF-8 bytes or its decoded text) into a {@link CustomUiDocument}.
82
82
  *
83
- * @throws {@link CustomUiParseError} if the XML is malformed, the root is not a `<customUI>` element in
83
+ * @throws {CustomUiParseError} if the XML is malformed, the root is not a `<customUI>` element in
84
84
  * a recognised namespace, or the tree nests beyond {@link MAX_DEPTH}.
85
85
  */
86
86
  export declare function parseCustomUi(input: string | Uint8Array): CustomUiDocument;
@@ -16,8 +16,8 @@
16
16
  // with {@link CustomUiParseError} on any malformed or unrecognised structure rather than returning a
17
17
  // half-built tree.
18
18
  import { strFromU8 } from 'fflate';
19
- import { boolStrict, localName, xmlEvents } from "../xml/xml-read.js";
20
- import { CustomUiParseError } from "./errors.js";
19
+ import { boolStrict, localName, xmlEvents } from '../xml/xml-read.js';
20
+ import { CustomUiParseError } from './errors.js';
21
21
  /** The `customUI` root namespaces, one per {@link RibbonDialect}. */
22
22
  export const CUSTOMUI_2006_NAMESPACE = 'http://schemas.microsoft.com/office/2006/01/customui';
23
23
  export const CUSTOMUI_2009_NAMESPACE = 'http://schemas.microsoft.com/office/2009/07/customui';
@@ -59,7 +59,7 @@ const KNOWN_KINDS = new Set([
59
59
  /**
60
60
  * Parse a `customUI` part (raw UTF-8 bytes or its decoded text) into a {@link CustomUiDocument}.
61
61
  *
62
- * @throws {@link CustomUiParseError} if the XML is malformed, the root is not a `<customUI>` element in
62
+ * @throws {CustomUiParseError} if the XML is malformed, the root is not a `<customUI>` element in
63
63
  * a recognised namespace, or the tree nests beyond {@link MAX_DEPTH}.
64
64
  */
65
65
  export function parseCustomUi(input) {
@@ -7,6 +7,7 @@ export type { Comment, CommentThread, Mention, Person } from '../core/comment-th
7
7
  export type { CfValueObject, ConditionalFormatting, ConditionalFormattingRule, } from '../core/conditional-formatting.ts';
8
8
  export type { DataValidation, DataValidationEntry, DataValidationErrorStyle, DataValidationOperator, DataValidationType, } from '../core/data-validation.ts';
9
9
  export { type AnchoredImage, type AnchorPoint, type Extent, type ImageAnchor, type ImageEditAs, isOneCellAnchor, type OneCellAnchor, PX_TO_EMU, type TwoCellAnchor, type WorkbookImage, } from '../core/image.ts';
10
+ export { MAX_COLUMN_WIDTH, MAX_ROW_HEIGHT } from '../core/limits.ts';
10
11
  export type { HeaderFooter, PageBreak, PageMargins, PageSetup, PrintOptions, } from '../core/page-setup.ts';
11
12
  export { type ParsedPivotField, type ParsedPivotSource, type ParsedPivotTable, type PivotCacheField, type PivotItem, type PivotMetric, type PivotNumericSummary, type PivotRecordCell, type PivotSourceKind, PivotTable, type PivotTableOptions, } from '../core/pivot-table.ts';
12
13
  export type { PreservedPart, PreservedRelationship, PreservedRootReference, PreservedWorksheetReference, } from '../core/preserved.ts';
@@ -16,8 +17,9 @@ export { Row } from '../core/row.ts';
16
17
  export type { Alignment, Border, BorderEdge, BorderStyle, CellStyle, Color, DifferentialStyle, Fill, FillPatternType, Font, FontScheme, FontVerticalAlignment, GradientFill, GradientStop, HorizontalAlignment, NamedCellStyle, PatternFill, Protection, TableStyleNamespace, TableStyleTable, UnderlineStyle, VerticalAlignment, } from '../core/style.ts';
17
18
  export { Table, type TableColumn, type TableColumnStyle, type TableOptions, type TableRegion, type TableStyleInfo, } from '../core/table.ts';
18
19
  export { isTableStyleElementType, STRIPE_ELEMENT_TYPES, TABLE_STYLE_ELEMENT_TYPES, type TableStyle, type TableStyleElement, type TableStyleElementType, } from '../core/table-style.ts';
20
+ export { estimateWrappedLines } from '../core/text-metrics.ts';
19
21
  export { DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, parseThemeColorScheme, THEME_COLOR_SLOTS, type ThemeColorScheme, type ThemeColorSlot, type ThemeFontScheme, type ThemeOverrides, } from '../core/theme.ts';
20
- export { type CellValue, coerceCellValue, type DataTableFormulaValue, detectValueType, ERROR_CODES, type ErrorCode, type ErrorValue, type FormulaResult, type FormulaValue, type HyperlinkValue, isErrorCode, type RichTextRun, type RichTextValue, richTextToPlain, type SharedFormulaValue, ValueType, } from '../core/value.ts';
22
+ export { type CellValue, cellValueToText, coerceCellValue, type DataTableFormulaValue, detectValueType, ERROR_CODES, type ErrorCode, type ErrorValue, type FormulaResult, type FormulaValue, type HyperlinkValue, isDataTableFormulaValue, isErrorCode, isErrorValue, isFormulaValue, isHyperlinkValue, isRichTextValue, isSharedFormulaValue, type RichTextRun, type RichTextValue, richTextToPlain, type SharedFormulaValue, ValueType, } from '../core/value.ts';
21
23
  export { type AddImageOptions, type AddWorksheetOptions, DEFAULT_WORKBOOK_VIEW, type DefinedName, type PreservedWorkbookReference, Workbook, type WorkbookProperties, type WorkbookView, } from '../core/workbook.ts';
22
24
  export type { WorkbookProtection, WorkbookProtectionCredentialAttr, } from '../core/workbook-protection.ts';
23
25
  export { type CellModel, type ColumnProperties, type OutlineProperties, type RowInput, type RowProperties, type SheetView, Worksheet, type WorksheetModel, type WorksheetProperties, type WorksheetState, } from '../core/worksheet.ts';