@shbernal/ts-xlsx 3.2.1 → 3.2.2-next.22.g076b1ec

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.
package/README.md CHANGED
@@ -7,25 +7,25 @@
7
7
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
8
8
 
9
9
  A TypeScript-first library for reading and writing spreadsheet documents
10
- (`.xlsx` / OOXML, and CSV) — synchronous, `Uint8Array`-native, and dependency-lean.
10
+ (`.xlsx` / OOXML, and CSV). It is synchronous, `Uint8Array`-native, and dependency-lean.
11
11
 
12
12
  **[Documentation, guide and a browser playground](https://shbernal.github.io/ts-xlsx/)**
13
13
 
14
- > **Status — 1.0.0, the first release.**
14
+ > **Status: stable and published on npm.** The [changelog](CHANGELOG.md) has the current version.
15
15
  > `ts-xlsx` began as a hard fork of [ExcelJS](https://github.com/exceljs/exceljs)
16
16
  > and has been rebuilt from the ground up into a modern, strict-TypeScript library.
17
- > **It carries no backwards-compatibility guarantee with ExcelJS** — the API below is
17
+ > **It carries no backwards-compatibility guarantee with ExcelJS**: the API below is
18
18
  > its own, not a drop-in. From 1.0.0 onward it follows
19
19
  > [SemVer](https://semver.org/) against _itself_; see the [changelog](CHANGELOG.md).
20
- > See [`CLAUDE.md`](CLAUDE.md) for the goals and [`docs/architecture.md`](docs/architecture.md)
20
+ > See [`AGENTS.md`](AGENTS.md) for the goals and [`docs/architecture.md`](docs/architecture.md)
21
21
  > for the design, and [migrating from ExcelJS](docs/migrating-from-exceljs.md) if you are coming across.
22
22
 
23
23
  ## Why it exists
24
24
 
25
- Upstream ExcelJS is effectively unmaintained — no release since 2023, a backlog of
26
- hundreds of issues and PRs — while still serving tens of millions of downloads a month.
25
+ Upstream ExcelJS is effectively unmaintained (no release since 2023, a backlog of
26
+ hundreds of issues and PRs), while still serving tens of millions of downloads a month.
27
27
  `ts-xlsx` extracts the accumulated value from that backlog (as a permanent regression
28
- corpus) and discards the debt: no untyped surfaces, no callback APIs, a single small
28
+ corpus) and discards the debt: no untyped APIs, no callback APIs, a single small
29
29
  runtime dependency, and every behavior pinned by a test. This is not a compatibility
30
30
  shim. It is a different, better library that happens to share ancestry.
31
31
 
@@ -35,9 +35,9 @@ What that buys you today:
35
35
  `exactOptionalPropertyTypes`. The published `.d.ts` _is_ the documentation, and the
36
36
  [API reference](docs/api/README.md) is generated straight from it.
37
37
  - **Synchronous, buffer-native I/O.** `readXlsx`/`writeXlsx` take and return a
38
- `Uint8Array` — no streams to await for the buffered path, no Node `Buffer` assumption,
38
+ `Uint8Array`. There are no streams to await for the buffered path, no Node `Buffer` assumption,
39
39
  so the same code runs in Node and the browser.
40
- - **Streaming when you need it.** Bounded-memory row streaming both ways — read a large
40
+ - **Streaming when you need it.** Bounded-memory row streaming both ways: read a large
41
41
  workbook without materializing it, or write one incrementally to a Node stream so peak
42
42
  memory stays flat.
43
43
  - **One runtime dependency** ([`fflate`](https://github.com/101arrowz/fflate) for zip),
@@ -77,7 +77,7 @@ const people = reopened.getWorksheet('People');
77
77
  console.log(people?.getCell('A2').value); // 'Ada'
78
78
  ```
79
79
 
80
- Persisting to disk is your platform's job, not the library's — `writeXlsx` hands you the
80
+ Persisting to disk is your platform's job, not the library's. `writeXlsx` hands you the
81
81
  bytes:
82
82
 
83
83
  ```ts
@@ -89,17 +89,17 @@ const wb2 = readXlsx(readFileSync('people.xlsx')); // a Buffer is a Uint8Array
89
89
 
90
90
  ## Core model
91
91
 
92
- - **`Workbook`** — the document. `addWorksheet(name)`, `getWorksheet(nameOrId)`,
92
+ - **`Workbook`**: the document. `addWorksheet(name)`, `getWorksheet(nameOrId)`,
93
93
  `worksheets`, defined names, images, and workbook-level properties.
94
- - **`Worksheet`** — a sheet. `getCell('B3')`, `addRow(values)`, `addTable(...)`,
94
+ - **`Worksheet`**: a sheet. `getCell('B3')`, `addRow(values)`, `addTable(...)`,
95
95
  `mergeCells('A1:B2')`, `getRow(n)` / `getColumn(n)`, `rows()` / `columns()`, page setup,
96
96
  and print options.
97
- - **`Row`** / **`Column`** — one line of the grid. Formatting is flat (`row.height = 20`,
97
+ - **`Row`** / **`Column`**: one line of the grid. Formatting is flat (`row.height = 20`,
98
98
  `column.width = 12`, `column.key = 'name'`), and cells are reachable from it:
99
99
  `row.getCell('B')`, `row.cells`, `row.values`.
100
- - **`Cell`** — one cell. `cell.value` is the whole story: a `number`, `string`,
100
+ - **`Cell`**: one cell. `cell.value` is the whole story: a `number`, `string`,
101
101
  `boolean`, `Date`, `null` (empty), a formula (`{formula, result}`), rich text, or an
102
- error — all precisely typed as [`CellValue`](docs/api/cell-values.md). A hyperlink is not
102
+ error, all precisely typed as [`CellValue`](docs/api/cell-values.md). A hyperlink is not
103
103
  a value: it sits on the sheet, over any cell, through
104
104
  `sheet.addHyperlink({ref: 'B2', target: 'https://example.com'})`.
105
105
 
@@ -115,16 +115,16 @@ for (const row of sheet.rows()) {
115
115
  ```
116
116
 
117
117
  `Row` and `Column` are _handles_, not snapshots: they read and write straight through to the
118
- sheet, so two handles on the same line always agree, and reading one creates nothing — asking
118
+ sheet, so two handles on the same line always agree, and reading one creates nothing. Asking
119
119
  about row 500 costs nothing and does not extend the used range. Position is fixed, exactly as a
120
120
  `Cell`'s is: after a splice, `getRow(3)` still means row 3, now holding whatever moved there.
121
121
 
122
122
  Addresses are honest: an axis a reference doesn't mention is `undefined`, never a
123
- sentinel — see [`decodeAddress`](docs/api/addresses-ranges.md).
123
+ sentinel. See [`decodeAddress`](docs/api/addresses-ranges.md).
124
124
 
125
125
  Charts, vector shapes, slicers, and legacy form controls are **round-trip-only**: a
126
126
  workbook that has them keeps them byte-faithfully through a load/edit/save, but there is
127
- no API to author a new one — see [`docs/api/preserved.md`](docs/api/preserved.md) and
127
+ no API to author a new one. See [`docs/api/preserved.md`](docs/api/preserved.md) and
128
128
  [ADR-0014](docs/decisions/0014-charts-shapes-slicers-are-round-trip-only-for-1-0.md).
129
129
 
130
130
  ## Reading, writing, streaming, CSV
@@ -140,10 +140,10 @@ import {
140
140
  writeCsvText, // CSV as a string
141
141
  } from '@shbernal/ts-xlsx';
142
142
  // The streaming writer opens files and pipes Node streams, so it is the one Node-only face
143
- // and lives behind its own subpath — which is what keeps everything above browser-safe:
143
+ // and lives behind its own subpath. That keeps everything above browser-safe:
144
144
  import {WorkbookStreamWriter} from '@shbernal/ts-xlsx/node';
145
145
 
146
- // Bounded-memory extraction — the whole workbook is never materialized:
146
+ // Bounded-memory extraction. The whole workbook is never materialized:
147
147
  for (const row of readSheetRows(bytes, {sheet: 'People'})) {
148
148
  console.log(
149
149
  row.number,
@@ -151,7 +151,7 @@ for (const row of readSheetRows(bytes, {sheet: 'People'})) {
151
151
  );
152
152
  }
153
153
 
154
- // Bounded-memory generation — commit each row to serialize and free it as you go:
154
+ // Bounded-memory generation. Commit each row to serialize and free it as you go:
155
155
  const writer = new WorkbookStreamWriter();
156
156
  const out = writer.addWorksheet('Big');
157
157
  for (let i = 1; i <= 1_000_000; i++) out.addRow([i, i * i]).commit();
@@ -168,7 +168,7 @@ never assembled as one object, which is what passing a sink was for.
168
168
 
169
169
  ## Writing without blocking the event loop
170
170
 
171
- `writeXlsx` spends the whole cost of DEFLATE on the calling thread — seconds, for a large
171
+ `writeXlsx` spends the whole cost of DEFLATE on the calling thread: seconds, for a large
172
172
  workbook, during which a server answers nothing. `writeXlsxAsync` produces the same package
173
173
  with the compression handed to worker threads:
174
174
 
@@ -185,12 +185,12 @@ always and speed only when there is more than one substantial part.
185
185
 
186
186
  There is deliberately **no `readXlsxAsync`**. Reading is dominated by XML parsing and model
187
187
  building, which no worker can take off the calling thread, so it would advertise a
188
- non-blocking read and then block for most of its duration — and the reader's zip-bomb ceiling
189
- is enforced by counting output between synchronous input slices, a guarantee that weakens the
188
+ non-blocking read and then block for most of its duration. The reader also enforces its zip-bomb
189
+ ceiling by counting output between synchronous input slices, a guarantee that weakens the
190
190
  moment inflation moves to a worker. If you need a non-blocking read, run the whole read in a
191
191
  worker. See [ADR-0024](docs/decisions/0024-async-is-one-writer-not-a-mirrored-pair.md).
192
192
 
193
- The reader decodes untrusted input defensively — entities are decoded but never
193
+ The reader decodes untrusted input defensively. Entities are decoded but never
194
194
  expanded, and inflation is bounded by a running output counter rather than any declared
195
195
  size, so a malformed or hostile package can't exhaust memory.
196
196
 
@@ -199,7 +199,7 @@ size, so a malformed or hostile package can't exhaust memory.
199
199
  The bare package name gives you everything, and with a bundler that is the right default:
200
200
  `"sideEffects": false` is declared, so anything you don't reference is dropped. The subpaths
201
201
  are for when you'd rather the module graph itself said which half of the library you depend
202
- on — a Lambda with no bundler, a service that only classifies failures:
202
+ on, as in a Lambda with no bundler or a service that only classifies failures:
203
203
 
204
204
  | Import from | You get | It loads |
205
205
  | ---------------------------- | ------------------------------------------------------------------------------ | -------- |
@@ -219,18 +219,18 @@ no Node built-in at all, which is what makes them bundle for a browser with noth
219
219
  configure ([ADR-0040](docs/decisions/0040-the-browser-boundary-is-an-entry-point.md)).
220
220
 
221
221
  Every error class lives in `/errors` and nowhere else, because a container-level failure
222
- belongs to no single codec — `readXlsx` and `readXlsb` both raise `UnsupportedFormatError`.
222
+ belongs to no single codec: `readXlsx` and `readXlsb` both raise `UnsupportedFormatError`.
223
223
  Catching and classifying therefore costs 3 KB, not a parser.
224
224
 
225
225
  `/xlsx` is barely cheaper than the whole package, and that is honest rather than a defect:
226
226
  `readXlsx` sniffs the bytes and hands a binary package to the BIFF12 reader, so the `.xlsb`
227
227
  codec is not optional on that path. The numbers above are the static-import closure of each
228
- entry, measured by `pnpm run size` — a bundler can only shrink them further.
228
+ entry, measured by `pnpm run size`. A bundler can only shrink them further.
229
229
 
230
230
  ## API reference
231
231
 
232
- The full reference is generated from the public types — it cannot drift from what the
233
- compiler accepts — and lives in **[`docs/api/`](docs/api/README.md)**. Regenerate it with:
232
+ The full reference is generated from the public types, so it cannot drift from what the
233
+ compiler accepts. It lives in **[`docs/api/`](docs/api/README.md)**. Regenerate it with:
234
234
 
235
235
  ```shell
236
236
  pnpm run docs
@@ -246,7 +246,7 @@ shapes are deliberately different (synchronous `Uint8Array` I/O instead of async
246
246
  ## Hit a bug? There is a skill for that
247
247
 
248
248
  Most code that uses this library is written by an agent, and an agent that hits a library
249
- defect will usually route around it silently — so the defect is never reported and never
249
+ defect will usually route around it silently, so the defect is never reported and never
250
250
  fixed. `ts-xlsx-upstream` is a skill that turns that moment into a filed issue with a
251
251
  minimal reproduction, which is what the maintainers convert into a permanent regression
252
252
  test. It ships inside the package, so it is already on disk:
@@ -262,8 +262,8 @@ copies: they are derived, `npx skills experimental_install` rebuilds them from t
262
262
  `npx skills update` overwrites them in place.
263
263
 
264
264
  It covers triage (is this bug ours or your file's?), reducing a failure to a script that
265
- builds its own input, and — because spreadsheets carry real data — never uploading a
266
- workbook to a public tracker. Once the reproduction stands on its own, it files without
265
+ builds its own input, and never uploading a workbook to a public tracker, because
266
+ spreadsheets carry real data. Once the reproduction stands on its own, it files without
267
267
  interrupting you, and tells you the issue number afterwards.
268
268
 
269
269
  You do not need it to report something: <https://github.com/shbernal/ts-xlsx/issues> is
@@ -273,10 +273,10 @@ open, and errors the library knows are its own fault print that link themselves.
273
273
 
274
274
  This project is optimized to be built and maintained largely by autonomous agents, with
275
275
  a machine-checkable safety net as the primary guarantor of correctness. The rules that
276
- govern every change are in [`CLAUDE.md`](CLAUDE.md); the design and working agreements are
276
+ govern every change are in [`AGENTS.md`](AGENTS.md); the design and working agreements are
277
277
  in [`docs/architecture.md`](docs/architecture.md); notable decisions are recorded under
278
278
  [`docs/decisions/`](docs/decisions/).
279
279
 
280
280
  ## License
281
281
 
282
- [MIT](LICENSE) — inherited from ExcelJS and retained.
282
+ [MIT](LICENSE), inherited from ExcelJS and retained.
@@ -50,7 +50,7 @@ export declare const isCustomFilterOperator: (value: string) => value is CustomF
50
50
  * is the common case (dropdowns, no criteria); an {@link AutoFilter} object carries per-column
51
51
  * criteria too. Throws when the range is not a bounded rectangle, or a column's `colId` falls
52
52
  * outside it, or a custom filter does not carry one or two predicates: a filter authored wrong is
53
- * a bug to surface, not to silently repair. (The reader sanitises hostile input before it reaches
53
+ * a bug to report, not to silently repair. (The reader sanitises hostile input before it reaches
54
54
  * here, so load never trips these guards.)
55
55
  */
56
56
  export declare function canonicalizeAutoFilter(input: string | AutoFilter): AutoFilter;
@@ -14,7 +14,7 @@ export declare class DataValidationOverlay {
14
14
  * round-trip writes it back there instead of silently corrupting the cross-sheet reference.
15
15
  *
16
16
  * @throws {AuthoringError} when `sqref` names no area at all. A rule attached to nothing covers no
17
- * cell and is written back as the same unreadable text, so it is a mistake worth surfacing at the
17
+ * cell and is written back as the same unreadable text, so it is a mistake worth reporting at the
18
18
  * call. The reader does not reach this: it drops such an entry at its own boundary, where a foreign
19
19
  * file's malformed attribute is supposed to be dropped.
20
20
  */
@@ -14,17 +14,21 @@ import { type NamedCellStyle, type TableStyleTable } from './workbook-styles.ts'
14
14
  import { type DeclaredThemeSchemes } from './workbook-theme.ts';
15
15
  import { type Visibility, Worksheet, type WorksheetState } from './worksheet.ts';
16
16
  /**
17
- * A workbook-level reference to package content the model does not model: a pivot cache
18
- * (`pivotCacheDefinition`) or a slicer cache (`slicerCache`), preserved verbatim across a round-trip
19
- * instead of being dropped. `relType` is the workbook relationship Type URI to re-emit; `entryPath`
20
- * is the part it points at; `parts` is the transitive closure that reference reaches (the entry
21
- * included). `pivotCacheId` carries the `<pivotCache cacheId>` a pivot cache is registered under in
22
- * the workbook's `<pivotCaches>`, so the wiring a pivot table resolves its cache through is re-emitted
23
- * too; it is absent for a slicer cache, which the workbook lists in an extension block instead.
17
+ * A workbook-level reference to package content the model does not interpret: a pivot cache
18
+ * (`pivotCacheDefinition`), a slicer cache (`slicerCache`) or an external link. The reader keeps it
19
+ * verbatim so a round-trip re-emits it instead of dropping it.
20
+ *
21
+ * `relType` is the workbook relationship Type URI to re-emit, `entryPath` is the part it points at,
22
+ * and `parts` is the transitive closure that reference reaches, the entry included.
23
+ *
24
+ * `pivotCacheId` is the `<pivotCache cacheId>` a pivot cache is registered under in the workbook's
25
+ * `<pivotCaches>`. A pivot table finds its cache through that id, so the writer re-emits it too. A
26
+ * slicer cache has none, because the workbook lists slicer caches in an extension block instead.
27
+ *
24
28
  * `externalReferenceIndex` is the 0-based position of an `externalLink` within the workbook's
25
- * `<externalReferences>`, the `[n]` a formula or defined name resolves an external cell through, so
26
- * the block is re-emitted in the original order and every `[n]` still points at the same linked
27
- * workbook; it is absent for a pivot/slicer cache.
29
+ * `<externalReferences>`: the `[n]` through which a formula or defined name reaches an external
30
+ * cell. The writer re-emits the block in its original order, so every `[n]` still points at the same
31
+ * linked workbook. A pivot or slicer cache has none.
28
32
  */
29
33
  export interface PreservedWorkbookReference {
30
34
  readonly relType: string;
@@ -113,7 +117,7 @@ export interface WorkbookProperties {
113
117
  company?: string;
114
118
  }
115
119
  /**
116
- * A named reference in the workbook, one of the entries Excel surfaces in its Name Manager. A name
120
+ * A named reference in the workbook, one of the entries Excel lists in its Name Manager. A name
117
121
  * maps an identifier to a formula (`refersTo`), most often a cell range like `Sheet1!$A$1:$B$2` but
118
122
  * possibly any formula. A name is global to the workbook unless it names a sheet in {@link scope},
119
123
  * which restricts it to that sheet and lets another sheet reuse the same name independently.
@@ -369,8 +373,8 @@ export declare class Workbook {
369
373
  * read from a package carrying no styles part: nothing was declared, and the library does not
370
374
  * fabricate a declaration on the file's behalf.
371
375
  *
372
- * This is the *round-trip* surface. {@link defaultFont} is what the workbook actually renders in,
373
- * which is this once anything has been authored over it.
376
+ * This is the value a round-trip preserves. {@link defaultFont} is what the workbook actually
377
+ * renders in, which is this once anything has been authored over it.
374
378
  */
375
379
  get declaredDefaultFont(): Font | undefined;
376
380
  /**
@@ -496,14 +500,14 @@ export declare class Workbook {
496
500
  /**
497
501
  * Show `images` on `sheet`, a worksheet of this workbook, registering each picture's bytes here
498
502
  * and re-anchoring it against the id they land on. The counterpart to {@link exportImages}, and
499
- * the affordance that lets a picture cross workbooks at all: an {@link AnchoredImage}'s `imageId`
500
- * indexes one workbook's registry and means nothing in the next, so a raw anchor moved between
501
- * workbooks points at media that does not exist there.
503
+ * the only way a picture can cross workbooks: an {@link AnchoredImage}'s `imageId` indexes one
504
+ * workbook's registry and means nothing in the next, so a raw anchor moved between workbooks
505
+ * points at media that does not exist there.
502
506
  *
503
507
  * The sheet's existing pictures are replaced, not appended to, so `importImages` is a transfer
504
- * rather than an accumulation: the same direction {@link Worksheet.model} assignment goes, and
505
- * what makes re-importing a sheet's own export leave it unchanged. An import whose `background` is
506
- * absent clears the destination's background for the same reason.
508
+ * rather than an accumulation. {@link Worksheet.model} assignment works the same way, and it is why
509
+ * re-importing a sheet's own export leaves the sheet unchanged. For the same reason, an import with
510
+ * no `background` clears the destination's background.
507
511
  *
508
512
  * Registration is content-addressed: a picture whose bytes are already here is re-used at its
509
513
  * existing id rather than stored twice, so importing the same sheet repeatedly, or twenty sheets
@@ -532,7 +536,7 @@ export declare class Workbook {
532
536
  * {@link getWorksheet}, for a caller who knows the sheet is there: the miss throws instead of
533
537
  * returning `undefined`, and the message names every sheet the workbook does have.
534
538
  *
535
- * The partial lookup is the right primitive for asking *whether* a sheet exists, and the wrong
539
+ * The partial lookup is the right call for asking *whether* a sheet exists, and the wrong
536
540
  * one for reaching a sheet a template is expected to carry: `undefined` flows on into a `?.`
537
541
  * chain and fails several steps later with nothing left to say about which name was missing.
538
542
  * That listing is the whole point: a lookup miss is a typo, a stale template or a renamed tab,
@@ -342,7 +342,7 @@ export declare class Worksheet {
342
342
  /**
343
343
  * The rows to serialise, as handles, in ascending row order: the union of rows holding cells and
344
344
  * rows holding only metadata (a hidden or grouped row need carry no data). Mirrors how OOXML
345
- * serialises (`<row>` wrapping `<c>`) and is the writer's row surface.
345
+ * serialises (`<row>` wrapping `<c>`) and is what the writer iterates over.
346
346
  *
347
347
  * A handle yields its cells only when asked, so a pass that reads nothing but row attributes
348
348
  * never assembles a cell array it will not look at.
@@ -453,7 +453,7 @@ export declare class Worksheet {
453
453
  }, properties?: PictureProperties): void;
454
454
  /**
455
455
  * Anchor an image with a pre-built model anchor in the model's own units (EMUs). This is the
456
- * low-level primitive {@link addImage} builds on and the reader uses to re-pin an image parsed from
456
+ * low-level call {@link addImage} builds on and the reader uses to re-pin an image parsed from
457
457
  * a drawing part without a lossy pixel round-trip.
458
458
  */
459
459
  addImageAnchor(imageId: number, anchor: ImageAnchor, properties?: PictureProperties): void;
@@ -11,7 +11,7 @@ export declare const CUSTOMUI_2010_REL_TYPE = "http://schemas.microsoft.com/offi
11
11
  export declare function isCustomUiRelType(type: string): boolean;
12
12
  /**
13
13
  * A control element inside a ribbon group. `kind` is the element's local name, narrowed to the closed
14
- * set of RibbonX control elements ({@link RibbonControlKind}); an element outside that set is surfaced
14
+ * set of RibbonX control elements ({@link RibbonControlKind}); an element outside that set is kept
15
15
  * as `unknown` rather than dropped. The three identity attributes (`id` a document-defined control,
16
16
  * `idQ` a qualified id, `idMso` a built-in control) and the two most-consulted display/behaviour
17
17
  * attributes (`label`, `onAction`) are lifted out as typed conveniences; every attribute the element
@@ -5,8 +5,8 @@ import type { Workbook } from '../../core/workbook.ts';
5
5
  * meaning the same bytes.
6
6
  *
7
7
  * Named here rather than taken from Node's `BufferEncoding`, which is what this option used to be:
8
- * that type is part of `@types/node`, so it made a browser consumer's public API surface depend on
9
- * Node's types, and it offered `base64` and `hex` as if they were output encodings for a text
8
+ * that type is part of `@types/node`, so it made a browser consumer's public API depend on Node's
9
+ * types, and it offered `base64` and `hex` as if they were output encodings for a text
10
10
  * format. The list is what a CSV consumer actually asks for.
11
11
  */
12
12
  export type CsvEncoding = 'ascii' | 'latin1' | 'ucs-2' | 'ucs2' | 'utf-8' | 'utf-16le' | 'utf16le' | 'utf8';
@@ -28,7 +28,7 @@ export interface StyleTable {
28
28
  readonly namedStyles: ReadonlyArray<NamedCellStyle>;
29
29
  /**
30
30
  * Font id 0: the workbook's declared default font, the face every cell naming no font renders in.
31
- * Surfaced separately from the fonts it was flattened onto because it is workbook-level state, not a
31
+ * Kept separately from the fonts it was flattened onto because it is workbook-level state, not a
32
32
  * cell format: a re-write must emit *this* face as font 0 rather than an assumed Calibri, or every
33
33
  * empty cell changes face and every character-unit column width changes meaning. Absent when the
34
34
  * file declares no font table.
@@ -38,7 +38,7 @@ export declare function collectNotes(cells: Iterable<Cell>): CommentCell[];
38
38
  *
39
39
  * Authors are laid out the way Excel lays them out: one synthetic `tc={headId}` entry per threaded
40
40
  * conversation first, then a single anonymous author shared by every note (the model carries no note
41
- * author). Each comment points at its own author by index, and a fallback additionally carries the
41
+ * author). Each comment points at its own author by index, and a fallback also carries the
42
42
  * `xr:uid` naming its thread: the pair that keeps Excel treating the cell as threaded.
43
43
  */
44
44
  export declare function commentsXml(comments: readonly CommentCell[]): string;
@@ -7,7 +7,7 @@
7
7
  * Only `procedural` and `class` modules can be removed this way. Removing a `document` module (e.g.
8
8
  * `ThisWorkbook`) or a `designer` module (a UserForm) would leave the host referencing code that no
9
9
  * longer exists, since their names are tied to a worksheet/workbook `codeName` or a designer storage
10
- * this project-level primitive has no visibility into. Editing such a module's code-behind is a job for
10
+ * this project-level edit has no visibility into. Editing such a module's code-behind is a job for
11
11
  * the offline `tools/vba-compiler` (in-place mode), which drives the real host.
12
12
  *
13
13
  * @throws {VbaParseError} if `bin` is not a parseable VBA project (validated before any edit).
@@ -122,7 +122,7 @@ export declare function tagRanges(source: string): Generator<TagRange>;
122
122
  * {@link XmlParseError}.
123
123
  */
124
124
  export declare function elementRanges(source: string, local: string): Generator<ElementRange>;
125
- /** An element start surfaced by {@link openElements}: its qualified `name`, the namespace-stripped
125
+ /** An element start yielded by {@link openElements}: its qualified `name`, the namespace-stripped
126
126
  * `local` name the filter matched on, and its already-decoded `attrs`. */
127
127
  export interface OpenElement {
128
128
  readonly name: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shbernal/ts-xlsx",
3
- "version": "3.2.1",
3
+ "version": "3.2.2-next.22.g076b1ec",
4
4
  "description": "A TypeScript-first library for reading and writing xlsx (OOXML) spreadsheets.",
5
5
  "keywords": [
6
6
  "csv",
@@ -88,7 +88,6 @@
88
88
  "site:preview": "vitepress preview www",
89
89
  "docs": "node scripts/gen-docs.ts",
90
90
  "docs:check": "node scripts/gen-docs.ts && git add --intent-to-add -- docs/api && git diff --exit-code -- docs/api",
91
- "constitution:check": "node scripts/check-constitution.ts",
92
91
  "layering:check": "node scripts/check-layering.ts",
93
92
  "corpus-blind:check": "node scripts/check-corpus-blind.ts",
94
93
  "entries:check": "node scripts/check-entries.ts",
@@ -125,18 +124,18 @@
125
124
  "fflate": "^0.8.3"
126
125
  },
127
126
  "devDependencies": {
128
- "@types/node": "^24.13.3",
127
+ "@types/node": "^24.19.2",
129
128
  "charcheck": "^0.4.0",
130
- "jszip": "^3.10.1",
131
- "lefthook": "^2.1.12",
129
+ "jszip": "^3.10.2",
130
+ "lefthook": "^2.2.1",
132
131
  "ooxml-validate": "^0.0.3",
133
- "oxfmt": "^0.67.0",
134
- "oxlint": "^1.82.0",
135
- "oxlint-tsgolint": "7.0.2001",
136
- "shiki": "^4.4.3",
132
+ "oxfmt": "^0.72.0",
133
+ "oxlint": "^1.87.0",
134
+ "oxlint-tsgolint": "7.0.2003",
135
+ "shiki": "^4.5.0",
137
136
  "typescript": "^7.0.2",
138
137
  "vitepress": "^1.6.4",
139
- "vue": "^3.5.42"
138
+ "vue": "^3.5.43"
140
139
  },
141
140
  "engines": {
142
141
  "node": ">=24"
@@ -17,7 +17,7 @@ builds its own input.** Everything below is in service of that.
17
17
 
18
18
  ## 1. Decide whether it is actually ours
19
19
 
20
- Catch the error and read `code` — the taxonomy is deliberately coarse and each answer
20
+ Catch the error and read `code`. The taxonomy is deliberately coarse, and each answer
21
21
  means something different about whose bug it is.
22
22
 
23
23
  | `error.code` | Whose bug | Report it? |
@@ -45,14 +45,14 @@ node -v # runtime
45
45
  ```
46
46
 
47
47
  Also capture, verbatim: the error `name`, its `code`, the full message, and the stack.
48
- Do not paraphrase the message — the throw site is often identifiable from its exact text.
48
+ Do not paraphrase the message: the throw site is often identifiable from its exact text.
49
49
 
50
50
  ## 3. Build a minimal reproduction
51
51
 
52
52
  **Never attach or paste the user's workbook.** Spreadsheets in a real project carry
53
53
  salaries, customer lists, and unreleased numbers. Treat every file in this repo as
54
54
  confidential unless the user tells you otherwise, and never upload one to a public
55
- tracker — including "just a screenshot of the sheet".
55
+ tracker, including "just a screenshot of the sheet".
56
56
 
57
57
  Instead, write a self-contained script that _constructs_ its input and fails:
58
58
 
@@ -80,7 +80,7 @@ from that description; they can never unsee an attachment.
80
80
 
81
81
  If you need a file attached, build one: reproduce the _structure_ you suspect in a workbook
82
82
  you generate, with invented values. A synthesized file is always safe to attach. A redacted
83
- one is not yours to judge — redaction fails quietly, and a public tracker is permanent.
83
+ one is not yours to judge: redaction fails quietly, and a public tracker is permanent.
84
84
 
85
85
  ## 4. Check it is not already filed
86
86
 
@@ -167,7 +167,7 @@ this project and leave a comment pointing at the issue, so the stopgap is remova
167
167
  the fix ships:
168
168
 
169
169
  ```ts
170
- // Workaround for ts-xlsx#<N> — remove once fixed upstream.
170
+ // Workaround for ts-xlsx#<N>: remove once fixed upstream.
171
171
  ```
172
172
 
173
173
  ## If `gh` is unavailable