xlvane 1.2.2 → 1.3.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.
- package/README.md +37 -6
- package/dist/cli.js +125 -45
- package/dist/index.d.ts +33 -11
- package/dist/index.js +3 -3
- package/dist/{node-files-CDb6TyZb.js → node-files-SVg17pky.js} +2 -2
- package/dist/node.d.ts +1 -1
- package/dist/node.js +3 -3
- package/dist/{options-Ba1iWD-Z.js → options-Cxt-LJE4.js} +8107 -6923
- package/dist/{rows-IWKIxHt9.js → rows-DltZ-hiW.js} +1116 -396
- package/dist/{stream-write-BGuUjlXo.js → stream-write-D573ewpr.js} +112 -16
- package/dist/{stream-write-BVaFkgwO.d.ts → stream-write-XnCyLdOy.d.ts} +354 -8
- package/docs/cli-json/inspect.schema.json +76 -0
- package/docs/errors.md +33 -12
- package/docs/exceljs-compat.md +32 -12
- package/docs/inspect-json.md +4 -0
- package/docs/migrate-from-exceljs.md +131 -0
- package/docs/xlsx-cves.md +88 -0
- package/llms-full.txt +422 -32
- package/package.json +1 -1
- package/skills/xlvane/SKILL.md +14 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ From a shell: `npx xlvane inspect orders.xlsx`, then `npx xlvane read orders.xls
|
|
|
26
26
|
## Contents
|
|
27
27
|
|
|
28
28
|
- [Command line in 5 minutes](#command-line-in-5-minutes)
|
|
29
|
-
- [Reading a workbook](#reading-a-workbook) · [Writing a workbook](#writing-a-workbook) (rich text, hyperlinks, notes, images, printing, [data validation and conditional formatting](#data-validation-and-conditional-formatting), [tables](#tables))
|
|
29
|
+
- [Reading a workbook](#reading-a-workbook) · [Writing a workbook](#writing-a-workbook) (rich text, hyperlinks, notes, images, [charts](#charts), printing, [data validation and conditional formatting](#data-validation-and-conditional-formatting), [tables](#tables))
|
|
30
30
|
- [Tasks in one call](#tasks-in-one-call): tables of rows, CSV, JSON, editing files, diff · [inserting and deleting rows](#inserting-and-deleting-rows)
|
|
31
31
|
- [Large files: streaming](#large-files-streaming)
|
|
32
32
|
- [Inspecting a workbook](#inspecting-a-workbook) · [Errors and warnings](#errors-and-warnings) · [Untrusted input and limits](#untrusted-input-and-limits)
|
|
@@ -172,10 +172,41 @@ for (const picture of read.getSheet('Report').images) console.log(picture.anchor
|
|
|
172
172
|
```
|
|
173
173
|
|
|
174
174
|
- **Adding:** `workbook.addImage(bytes)` takes PNG, JPEG or GIF bytes (recognised from the first bytes; other formats throw `XLV_E_INVALID_IMAGE`) and returns a number; the same image placed many times is stored once. `sheet.addImage(image, anchor, { name?, description?, hyperlink? })` places it over a range (`"E1:G4"`), between two corners (`{ from, to, editAs }`, each a cell or a 0-based `{ col, row }` with fractions) or at a corner with a size in pixels (`{ from, size }`). `sheet.background(image)` sets the sheet background. Pixels are converted at 96 per inch (9525 EMU); fractional positions use the sheet's column widths and row heights.
|
|
175
|
-
- **Reading:** `sheet.images` lists the pictures (`anchor` in EMU with the cell of each corner, `image` with `format`, `contentType` and `bytes`, `description`, `hyperlink`), `sheet.backgroundImage` the background, and `workbook.images` every image once. Charts
|
|
175
|
+
- **Reading:** `sheet.images` lists the pictures (`anchor` in EMU with the cell of each corner, `image` with `format`, `contentType` and `bytes`, `description`, `hyperlink`), `sheet.backgroundImage` the background, and `workbook.images` every image once. Charts are read into `sheet.charts` (see [Charts](#charts)); shapes and other drawing objects are not read: `XLV_W_DRAWING_CONTENT_NOT_READ` reports them, and `editCells` keeps them unchanged.
|
|
176
176
|
- **Rows:** `insertRows` and `deleteRows` move pictures with their rows. A deletion that would cut a picture, and a drawing with charts or shapes, are refused with `XLV_E_NOT_SUPPORTED`.
|
|
177
177
|
- **Streaming:** `writeWorkbookStream({ images: [png], sheets: [{ images: [{ image: 0, anchor: 'B2:D6' }], background: 0, rows }] })`.
|
|
178
178
|
|
|
179
|
+
### Charts
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
import { createWorkbook, readWorkbook, writeWorkbook } from 'xlvane';
|
|
183
|
+
|
|
184
|
+
const workbook = createWorkbook();
|
|
185
|
+
const sales = workbook.addSheet('Sales').set('A1', 'Month').set('B1', 'North').set('C1', 'South');
|
|
186
|
+
['Jan', 'Feb', 'Mar'].forEach((month, i) => sales.set(`A${String(i + 2)}`, month).set(`B${String(i + 2)}`, 10 + i).set(`C${String(i + 2)}`, 20 - i));
|
|
187
|
+
sales.addChart({
|
|
188
|
+
type: 'column',
|
|
189
|
+
anchor: 'E2:L18',
|
|
190
|
+
title: 'Sales by month',
|
|
191
|
+
series: [
|
|
192
|
+
{ name: 'Sales!B1', categories: 'Sales!A2:A4', values: 'Sales!B2:B4' },
|
|
193
|
+
{ name: 'Sales!C1', categories: 'Sales!A2:A4', values: 'Sales!C2:C4', color: 'C00000' },
|
|
194
|
+
],
|
|
195
|
+
legend: 'bottom',
|
|
196
|
+
axes: { y: { title: 'Units', min: 0 } },
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const chart = readWorkbook(writeWorkbook(workbook)).getSheet('Sales').charts[0];
|
|
200
|
+
console.log(chart?.types, chart?.title, chart?.series.map((s) => s.values)); // ['column'] { text: 'Sales by month' } ['Sales!$B$2:$B$4', 'Sales!$C$2:$C$4']
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
- **Adding:** `sheet.addChart({ type, anchor, series, title?, legend?, stacked?, axes?, name?, description? })` with `type` `column`, `bar`, `line`, `pie`, `scatter` or `area`; `anchor` as for pictures. Each series has `values` and optional `categories` (for scatter: the y and x values), each a sheet-qualified range of one row or one column such as `'Sales Data'!B2:B13`, an optional `name` (literal text, or a one-cell reference such as `Sales!B1`) and an optional `color` (`RRGGBB`). `stacked` is `true` or `'percent'` (not for pie and scatter); `legend` is `'right'` (default), `'bottom'`, `'top'`, `'left'` or `false`; `axes.x.title`, `axes.y.title`, `axes.y.min`, `axes.y.max`. A chart has 1 to 255 series; a pie exactly one, with at most 1 000 values. The sheets named in the ranges must exist when the workbook is written. Errors: `XLV_E_INVALID_CHART`.
|
|
204
|
+
- **What is written:** a chart part per chart with the series colours from a fixed palette (the accent colours of Excel's Office theme), since xlvane writes no theme part, and with the current cell values as caches, as Excel writes them. Combination charts, secondary axes, trendlines, data labels and chart sheets are not written.
|
|
205
|
+
- **Reading:** `sheet.charts` lists each chart's `name`, `description`, `anchor`, `types` (the chart groups in order, for example `['column', 'line']` for a combination chart; other chart types as `other:` and the element name), `stacked`, `title` (`{ text }`, `{ ref }` or `null`), `legend`, `axes` (`title`, `min`, `max`) and `series` (`type`, `name`, `categories`, `values` as reference text). References are never evaluated and cached values are not read. Charts on chart sheets are not read. `openWorkbookStream` does not report pictures or charts.
|
|
206
|
+
- **Streaming:** `writeWorkbookStream({ sheets: [{ name: 'Sales', rows, charts: [{ type: 'line', anchor: 'E2:L18', series: [...] }] }] })`. The streaming writer keeps no cell values, so its charts have no caches. LibreOffice draws such charts from the cells; whether Excel does is not verified.
|
|
207
|
+
- **Rows:** `insertRows` and `deleteRows` refuse sheets with charts, and workbooks whose charts may refer to the sheet, with `XLV_E_NOT_SUPPORTED`.
|
|
208
|
+
- **Checked with:** a copy of the ECMA-376 chart schema and LibreOffice 24.2.7.2, not with Excel. In the repository, `node scripts/chart-samples.mjs <directory>` (after `npm run build`) writes one sample file per type and variant.
|
|
209
|
+
|
|
179
210
|
### Sheet rules and printing
|
|
180
211
|
|
|
181
212
|
```ts
|
|
@@ -330,7 +361,7 @@ const stream = writeWorkbookStream({ sheets: [{ name: 'Data', rows: rows() }] })
|
|
|
330
361
|
- Memory stays constant: the test suite reads and writes 50 000 and 400 000 rows and checks that the retained heap does not grow. It measured under 1 MB in both cases.
|
|
331
362
|
- Streamed text is stored in the cells (inline strings) by default. Pass `sharedStrings: true` for a shared string table, which is smaller when values repeat but kept in memory until the end.
|
|
332
363
|
- Rows come in file order. A shared formula whose master cell comes later in the file cannot be translated while streaming.
|
|
333
|
-
- `maxCells` does not apply when streaming; single parts may decompress to 4 GiB (`STREAM_LIMIT_DEFAULTS`). All other limits apply.
|
|
364
|
+
- `maxCells` does not apply when streaming, except that one `<row>` element may hold at most 16 384 cells; single parts may decompress to 4 GiB (`STREAM_LIMIT_DEFAULTS`). All other limits apply.
|
|
334
365
|
- `readTableStream` differs from `readTable` in what it can see in advance: it detects the header row among the first rows it reads and fixes the columns there (the columns of the header row, or of the first rows without one), so a column that first appears further down is not part of the objects. `readTable` takes the columns of the whole sheet. For files that fit in memory, `readTable` gives the complete table.
|
|
335
366
|
|
|
336
367
|
## Benchmark
|
|
@@ -387,8 +418,8 @@ Limits can be changed per call, for example `readWorkbook(bytes, { limits: { max
|
|
|
387
418
|
| `maxXmlNameLength` | 1 024 | huge element or attribute names |
|
|
388
419
|
| `maxXmlTextLength` | 8 Mi characters | huge text nodes, attribute values, comments or processing instructions |
|
|
389
420
|
| `maxXmlTreeElements` | 1 000 000 | huge workbook, styles or relationship parts (elements plus attributes) |
|
|
390
|
-
| `maxCells` | 5 000 000 | workbooks too large to hold in memory (measured: about 180 bytes per numeric cell, Node.js 26); also bounds the output of `toCSV`, `readTable`, `toJSON` and `readTableStream` to `max(maxCells, 16 × cells read)` positions |
|
|
391
|
-
| `maxSharedStringChars` | 512 Mi characters | many cells pointing at one huge shared string (a few MB that become many GB when values are copied); counted separately, the same for cells sharing one huge formula |
|
|
421
|
+
| `maxCells` | 5 000 000 | workbooks too large to hold in memory (measured: about 180 bytes per numeric cell, Node.js 26); also bounds the output of `toCSV`, `readTable`, `toJSON` and `readTableStream` to `max(maxCells, 16 × cells read)` positions, and of `fromCSV`, `fromJSON` and `writeTable` to `max(maxCells, 16 × cells written)`; for `editCells` and `appendRows`, the cells of the workbook plus the new cells, and at most 1 000 000 cells written per call unless `maxCells` is given; for `insertRows` with `style`, the copied cell formats plus the cells of the workbook |
|
|
422
|
+
| `maxSharedStringChars` | 512 Mi characters | many cells pointing at one huge shared string (a few MB that become many GB when values are copied); counted separately, the same for cells sharing one huge formula and for header names repeated in every row object of `readTable` and `toJSON` |
|
|
392
423
|
|
|
393
424
|
## Package size
|
|
394
425
|
|
|
@@ -398,7 +429,7 @@ Limits can be changed per call, for example `readWorkbook(bytes, { limits: { max
|
|
|
398
429
|
|
|
399
430
|
- xlvane does not calculate formulas. It reads and writes formula text and the cached results stored in the file; edited files ask spreadsheet applications to recalculate when they are opened.
|
|
400
431
|
- Password-protected (encrypted) workbooks and legacy `.xls` files are rejected with `XLV_E_UNSUPPORTED_CONTAINER`. A sheet protection password is not encryption and is supported.
|
|
401
|
-
-
|
|
432
|
+
- Shapes, pivot tables, slicers and form controls are not modelled: reading skips them (with a warning for drawings), `editCells` keeps them unchanged, and row operations refuse sheets where they would have to move. Charts are read and written for six types (see [Charts](#charts)); row operations refuse them too.
|
|
402
433
|
- The command line reads files, not standard input.
|
|
403
434
|
|
|
404
435
|
## Links
|
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { a as appendRows, c as fromCSV, d as fromJSON, f as pickSheet, h as toJSON, l as inferValue, n as insertRows, o as editCells, p as readTable, s as diff, t as deleteRows, u as toCSV } from "./rows-
|
|
4
|
-
import { i as readInput } from "./node-files-
|
|
2
|
+
import { Vt as suggest, _ as inspect, v as isoDate, x as readWorkbook, yn as parseRange, zn as XlvError } from "./options-Cxt-LJE4.js";
|
|
3
|
+
import { a as appendRows, c as fromCSV, d as fromJSON, f as pickSheet, h as toJSON, l as inferValue, n as insertRows, o as editCells, p as readTable, s as diff, t as deleteRows, u as toCSV } from "./rows-DltZ-hiW.js";
|
|
4
|
+
import { i as readInput } from "./node-files-SVg17pky.js";
|
|
5
5
|
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
-
import {
|
|
6
|
+
import { lstat, open, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
7
7
|
import process from "node:process";
|
|
8
8
|
import { createRequire } from "node:module";
|
|
9
9
|
import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
@@ -523,14 +523,15 @@ function oneLine(text) {
|
|
|
523
523
|
}
|
|
524
524
|
/**
|
|
525
525
|
* Control characters that a terminal would act on (C0 except tab and line feed, DEL, and C1,
|
|
526
|
-
* which includes the 8-bit CSI U+009B) written as `\x1b`,
|
|
527
|
-
* written as `\u202e`, so
|
|
526
|
+
* which includes the 8-bit CSI U+009B) written as `\x1b`, bidirectional formatting characters
|
|
527
|
+
* written as `\u202e`, and line feeds written as `⏎` (so a name from a file cannot add lines of its
|
|
528
|
+
* own to the output, D-K04), so text from a file, such as a cell
|
|
528
529
|
* holding `_x001B_]0;title_x0007_`, cannot move the cursor, change the window title or hide
|
|
529
530
|
* output (docs/decisions.md D-R02). Used for all human-readable output; `--json` writes the same
|
|
530
531
|
* characters as `\uXXXX` escapes instead (D-H01).
|
|
531
532
|
*/
|
|
532
533
|
function escapeControls(text) {
|
|
533
|
-
return text.replace(/[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g, (c) => `\\x${c.charCodeAt(0).toString(16).padStart(2, "0")}`).replace(BIDI, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
534
|
+
return text.replace(/\n/g, "⏎").replace(/[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g, (c) => `\\x${c.charCodeAt(0).toString(16).padStart(2, "0")}`).replace(BIDI, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
534
535
|
}
|
|
535
536
|
/**
|
|
536
537
|
* The characters with the Unicode property Bidi_Control (checked against `/\p{Bidi_Control}/u` in
|
|
@@ -649,6 +650,18 @@ function quoteArg(arg) {
|
|
|
649
650
|
if (/^[\w@%+=:,./-]+$/.test(arg)) return arg;
|
|
650
651
|
return `'${arg.replace(/'/g, `'\\''`)}'`;
|
|
651
652
|
}
|
|
653
|
+
/**
|
|
654
|
+
* `args` with `options` added before the first "--", after which every argument is positional
|
|
655
|
+
* (or at the end when there is none).
|
|
656
|
+
*/
|
|
657
|
+
function withOptions(args, ...options) {
|
|
658
|
+
const at = args.indexOf("--");
|
|
659
|
+
return at === -1 ? [...args, ...options] : [
|
|
660
|
+
...args.slice(0, at),
|
|
661
|
+
...options,
|
|
662
|
+
...args.slice(at)
|
|
663
|
+
];
|
|
664
|
+
}
|
|
652
665
|
/** A command line as text. */
|
|
653
666
|
function commandLine(args) {
|
|
654
667
|
return ["xlvane", ...args].map(quoteArg).join(" ");
|
|
@@ -805,8 +818,8 @@ function withPath(error, path) {
|
|
|
805
818
|
}
|
|
806
819
|
/**
|
|
807
820
|
* For a missing file, suggests a file with a similar name in the same directory: only a name the
|
|
808
|
-
* command can read (`accept`), and never a file that another argument of the
|
|
809
|
-
* names, such as the other side of a diff (D-R08).
|
|
821
|
+
* command can read (`accept`), and never the path itself or a file that another argument of the
|
|
822
|
+
* command already names, such as the other side of a diff (D-R08, D-K04).
|
|
810
823
|
*/
|
|
811
824
|
async function withFileHint(ctx, path, error, accept) {
|
|
812
825
|
if (!(error instanceof XlvError) || error.details["code"] !== "ENOENT") return error;
|
|
@@ -817,6 +830,7 @@ async function withFileHint(ctx, path, error, accept) {
|
|
|
817
830
|
return error;
|
|
818
831
|
}
|
|
819
832
|
const others = new Set(ctx.argv.filter((a) => a !== path && !a.startsWith("-")).map((a) => resolvePath(ctx, a)));
|
|
833
|
+
others.add(resolvePath(ctx, path));
|
|
820
834
|
const candidates = names.filter((n) => accept(n) && !others.has(resolvePath(ctx, join(dirname(path), n))));
|
|
821
835
|
const close = suggest(basename(path), candidates, 1);
|
|
822
836
|
if (close.length === 0) return error;
|
|
@@ -844,16 +858,26 @@ async function writeOutput(ctx, path, data, options) {
|
|
|
844
858
|
let exists = false;
|
|
845
859
|
let mode = null;
|
|
846
860
|
try {
|
|
847
|
-
const
|
|
861
|
+
const link = await lstat(target);
|
|
848
862
|
exists = true;
|
|
849
|
-
|
|
850
|
-
if (
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
863
|
+
const info = link.isSymbolicLink() ? await stat(target).catch(() => null) : link;
|
|
864
|
+
if (info !== null) {
|
|
865
|
+
mode = info.mode & 511;
|
|
866
|
+
if (info.isDirectory()) throw new XlvError("XLV_E_FILE_WRITE", `"${path}" is a directory.`, {
|
|
867
|
+
hint: "Give a file name for the output.",
|
|
868
|
+
details: {
|
|
869
|
+
path,
|
|
870
|
+
code: "EISDIR"
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
if (!info.isFile()) throw new XlvError("XLV_E_FILE_WRITE", `"${path}" is not a regular file.`, {
|
|
874
|
+
hint: "Give the name of a regular file for the output.",
|
|
875
|
+
details: {
|
|
876
|
+
path,
|
|
877
|
+
code: "ENOTREG"
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
}
|
|
857
881
|
} catch (error) {
|
|
858
882
|
if (error instanceof XlvError) throw error;
|
|
859
883
|
}
|
|
@@ -869,13 +893,21 @@ async function writeOutput(ctx, path, data, options) {
|
|
|
869
893
|
};
|
|
870
894
|
try {
|
|
871
895
|
if (exists) {
|
|
872
|
-
const
|
|
896
|
+
const random = Array.from(globalThis.crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(8)), (b) => b.toString(16).padStart(2, "0")).join("");
|
|
897
|
+
const temp = join(dirname(target), `.${shortName(basename(target))}.xlvane-${random}.tmp`);
|
|
898
|
+
let created = false;
|
|
873
899
|
try {
|
|
874
|
-
await
|
|
875
|
-
|
|
900
|
+
const handle = await open(temp, "wx", mode === null ? void 0 : 384);
|
|
901
|
+
created = true;
|
|
902
|
+
try {
|
|
903
|
+
await handle.writeFile(bytes);
|
|
904
|
+
if (mode !== null) await handle.chmod(mode);
|
|
905
|
+
} finally {
|
|
906
|
+
await handle.close();
|
|
907
|
+
}
|
|
876
908
|
await rename(temp, target);
|
|
877
909
|
} catch (error) {
|
|
878
|
-
await rm(temp, { force: true });
|
|
910
|
+
if (created) await rm(temp, { force: true });
|
|
879
911
|
throw error;
|
|
880
912
|
}
|
|
881
913
|
} else await writeFile(target, bytes, { flag: "wx" });
|
|
@@ -902,6 +934,21 @@ async function writeOutput(ctx, path, data, options) {
|
|
|
902
934
|
replaced: exists
|
|
903
935
|
};
|
|
904
936
|
}
|
|
937
|
+
/**
|
|
938
|
+
* The start of a file name, at most 100 bytes of UTF-8 and never cut inside a character, for the
|
|
939
|
+
* name of a temporary file next to it (file systems limit names in bytes, D-K03).
|
|
940
|
+
*/
|
|
941
|
+
function shortName(name) {
|
|
942
|
+
let bytes = 0;
|
|
943
|
+
let end = 0;
|
|
944
|
+
for (const ch of name) {
|
|
945
|
+
const size = new TextEncoder().encode(ch).length;
|
|
946
|
+
if (bytes + size > 100) break;
|
|
947
|
+
bytes += size;
|
|
948
|
+
end += ch.length;
|
|
949
|
+
}
|
|
950
|
+
return name.slice(0, end);
|
|
951
|
+
}
|
|
905
952
|
/** The line every writing command ends with; green when something was written. */
|
|
906
953
|
function wroteLine(w, out) {
|
|
907
954
|
if (!w.written) {
|
|
@@ -1068,6 +1115,23 @@ function formatOf(command, path, role) {
|
|
|
1068
1115
|
[role]: path
|
|
1069
1116
|
});
|
|
1070
1117
|
}
|
|
1118
|
+
/**
|
|
1119
|
+
* The rows as indented JSON text. Every row repeats the header names, so a small file can ask for
|
|
1120
|
+
* more text than a JavaScript string can hold; that ends as a limit error, not an internal one
|
|
1121
|
+
* (D-J01).
|
|
1122
|
+
*/
|
|
1123
|
+
function jsonText(rows) {
|
|
1124
|
+
try {
|
|
1125
|
+
return `${JSON.stringify(rows, null, 2)}\n`;
|
|
1126
|
+
} catch (error) {
|
|
1127
|
+
if (!(error instanceof RangeError)) throw error;
|
|
1128
|
+
throw new XlvError("XLV_E_LIMIT_EXCEEDED", "The JSON text would be longer than a JavaScript string can be.", {
|
|
1129
|
+
hint: "Convert part of the sheet with --range, or convert to CSV, which does not repeat the header in every row.",
|
|
1130
|
+
details: { limit: "maxSharedStringChars" },
|
|
1131
|
+
cause: error
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1071
1135
|
/** The delimiter option: one character, or "tab". */
|
|
1072
1136
|
function delimiterOf(ctx, command, format) {
|
|
1073
1137
|
const d = str(ctx.args, "delimiter");
|
|
@@ -1091,12 +1155,7 @@ function delimiterNote(ctx, command, text, input) {
|
|
|
1091
1155
|
const other = first.includes(";") ? ";" : first.includes(" ") ? "tab" : null;
|
|
1092
1156
|
if (other === null) return;
|
|
1093
1157
|
const args = ctx.argv.filter((a) => a !== "--json");
|
|
1094
|
-
ctx.note(`note: the first line of ${input} has no "," but has ${other === "tab" ? "tabs" : "\";\""}; every line became one column. Try: ${commandLine([
|
|
1095
|
-
command,
|
|
1096
|
-
...args,
|
|
1097
|
-
"--delimiter",
|
|
1098
|
-
other
|
|
1099
|
-
])}`);
|
|
1158
|
+
ctx.note(`note: the first line of ${input} has no "," but has ${other === "tab" ? "tabs" : "\";\""}; every line became one column. Try: ${commandLine(withOptions([command, ...args], "--delimiter", other))}`);
|
|
1100
1159
|
}
|
|
1101
1160
|
/** What to do with a JSON file that holds something other than an array of rows. */
|
|
1102
1161
|
/**
|
|
@@ -1253,10 +1312,10 @@ const convertCommand = {
|
|
|
1253
1312
|
...sheet !== void 0 ? { sheet } : {},
|
|
1254
1313
|
...range !== void 0 ? { range } : {}
|
|
1255
1314
|
};
|
|
1256
|
-
if (to === "json") data =
|
|
1315
|
+
if (to === "json") data = jsonText(toJSON(workbook, {
|
|
1257
1316
|
...where,
|
|
1258
1317
|
header: headerOption(ctx.args, "convert")
|
|
1259
|
-
}).rows
|
|
1318
|
+
}).rows);
|
|
1260
1319
|
else data = toCSV(workbook, {
|
|
1261
1320
|
...where,
|
|
1262
1321
|
delimiter: delimiterOf(ctx, "convert", to),
|
|
@@ -1682,10 +1741,10 @@ const editCommand = {
|
|
|
1682
1741
|
render: (out) => {
|
|
1683
1742
|
if (ctx.quiet && w.written) return "";
|
|
1684
1743
|
const done = [
|
|
1685
|
-
insert !== null ? `inserted ${plural(insert.count, "row")} before row ${String(insert.at)} of "${sheetOption ?? ""}"` : "",
|
|
1686
|
-
remove !== null ? `deleted ${remove.count === 1 ? `row ${String(remove.at)}` : `rows ${String(remove.at)}–${String(remove.at + remove.count - 1)}`} of "${sheetOption ?? ""}"` : "",
|
|
1744
|
+
insert !== null ? `inserted ${plural(insert.count, "row")} before row ${String(insert.at)} of "${escapeControls(sheetOption ?? "")}"` : "",
|
|
1745
|
+
remove !== null ? `deleted ${remove.count === 1 ? `row ${String(remove.at)}` : `rows ${String(remove.at)}–${String(remove.at + remove.count - 1)}`} of "${escapeControls(sheetOption ?? "")}"` : "",
|
|
1687
1746
|
parsed.length > 0 ? `set ${plural(parsed.length, "cell")}` : "",
|
|
1688
|
-
rows.length > 0 ? `appended ${plural(rows.length, "row")} to "${appendSheet ?? ""}"` : ""
|
|
1747
|
+
rows.length > 0 ? `appended ${plural(rows.length, "row")} to "${escapeControls(appendSheet ?? "")}"` : ""
|
|
1689
1748
|
].filter((p) => p !== "").join(", ");
|
|
1690
1749
|
return [
|
|
1691
1750
|
`${done.charAt(0).toUpperCase()}${done.slice(1)}.`,
|
|
@@ -1829,6 +1888,10 @@ function renderReport(out, file, r, verbose) {
|
|
|
1829
1888
|
lines.push(` Hyperlinks: ${plural(s.hyperlinkCount, "link")} (${shown.join(", ")}${s.hyperlinkCount > 3 ? ", …" : ""})`);
|
|
1830
1889
|
}
|
|
1831
1890
|
if (s.imageCount > 0) lines.push(` Images: ${plural(s.imageCount, "picture")} (${s.images.slice(0, 5).map((i) => `${i.cell ?? "absolute"} ${i.format ?? "other"}`).join(", ")}${s.imageCount > 5 ? ", …" : ""})`);
|
|
1891
|
+
if (s.chartCount > 0) {
|
|
1892
|
+
const shown = s.charts.slice(0, 5).map((c) => `${c.cell ?? "absolute"} ${escapeControls(c.types.join("+") || "empty")}${c.typesTruncated ? "+…" : ""}${c.title === null ? "" : "text" in c.title ? ` "${escapeControls(c.title.text)}"` : ` =${escapeControls(c.title.ref)}`}`);
|
|
1893
|
+
lines.push(` Charts: ${plural(s.chartCount, "chart")} (${shown.join(", ")}${s.chartCount > 5 ? ", …" : ""})`);
|
|
1894
|
+
}
|
|
1832
1895
|
if (s.noteCount > 0) lines.push(` Notes: ${plural(s.noteCount, "note")} (${s.notes.slice(0, 5).join(", ")}${s.noteCount > 5 ? ", …" : ""})`);
|
|
1833
1896
|
if (s.tables.length > 0) lines.push(` Tables: ${s.tables.slice(0, 5).map((table) => `${escapeControls(table.name)} ${escapeControls(table.ref)}`).join(", ")}${s.tables.length > 5 ? ", …" : ""}`);
|
|
1834
1897
|
if (s.dataValidationCount > 0 || s.conditionalFormatCount > 0) {
|
|
@@ -1865,7 +1928,7 @@ const OWN_OPTIONS = /* @__PURE__ */ new Set([
|
|
|
1865
1928
|
const mcpCommand = {
|
|
1866
1929
|
name: "mcp",
|
|
1867
1930
|
summary: "Start the MCP server for AI agents",
|
|
1868
|
-
description: "Starts the Model Context Protocol server from the separate package @xlvane/mcp over stdin and stdout, so that an AI agent can inspect, read, convert and compare spreadsheets through tools instead of code. The arguments after \"mcp\" are passed to the server: --root <dir> (required, repeatable) names a directory the tools may use, --allow-write adds the tools that write files, and --max-file-size <bytes>, --max-rows <n
|
|
1931
|
+
description: "Starts the Model Context Protocol server from the separate package @xlvane/mcp over stdin and stdout, so that an AI agent can inspect, read, convert and compare spreadsheets through tools instead of code. The arguments after \"mcp\" are passed to the server: --root <dir> (required, repeatable) names a directory the tools may use, --allow-write adds the tools that write files, and --max-file-size <bytes>, --max-rows <n>, --max-response-chars <n> and --max-cells <n> set its limits. --json is not available, because stdout carries the protocol. If @xlvane/mcp is not installed, the command prints how to install it. The exit code is the server's: 0 when the client closed the connection, 2 for wrong arguments, 3 for a directory that cannot be used.",
|
|
1869
1932
|
usage: "[server options]",
|
|
1870
1933
|
positionals: {
|
|
1871
1934
|
min: 0,
|
|
@@ -2066,9 +2129,23 @@ function jsonError(path, cause) {
|
|
|
2066
2129
|
cause
|
|
2067
2130
|
});
|
|
2068
2131
|
}
|
|
2132
|
+
async function isLink(path) {
|
|
2133
|
+
try {
|
|
2134
|
+
return (await lstat(path)).isSymbolicLink();
|
|
2135
|
+
} catch {
|
|
2136
|
+
return false;
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2069
2139
|
async function run$1(ctx) {
|
|
2070
2140
|
const dir = ctx.args.positionals[0] ?? ".";
|
|
2071
2141
|
const pkgPath = join(dir, "package.json");
|
|
2142
|
+
if (await isLink(resolvePath(ctx, pkgPath))) throw new XlvError("XLV_E_FILE_READ", `"${pkgPath}" is a symbolic link; migrate does not follow links.`, {
|
|
2143
|
+
hint: "Run migrate in the directory that holds the real package.json.",
|
|
2144
|
+
details: {
|
|
2145
|
+
path: pkgPath,
|
|
2146
|
+
code: "ELOOP"
|
|
2147
|
+
}
|
|
2148
|
+
});
|
|
2072
2149
|
const text = await readText(ctx, pkgPath);
|
|
2073
2150
|
let pkg;
|
|
2074
2151
|
try {
|
|
@@ -2138,17 +2215,22 @@ async function run$1(ctx) {
|
|
|
2138
2215
|
else if (alreadyMigrated) lines.push(`${pkgPath} already uses ${ALIAS} for "exceljs".`);
|
|
2139
2216
|
else {
|
|
2140
2217
|
lines.push(`${out.bold("Change in")} ${pkgPath}:`);
|
|
2141
|
-
for (const c of changes) lines.push(` ${c.section}: ${out.red(`- "exceljs": "${c.from}"`)}`, ` ${" ".repeat(c.section.length)} ${out.green(`+ "exceljs": "${c.to}"`)}`);
|
|
2218
|
+
for (const c of changes) lines.push(` ${c.section}: ${out.red(`- "exceljs": "${escapeControls(c.from)}"`)}`, ` ${" ".repeat(c.section.length)} ${out.green(`+ "exceljs": "${escapeControls(c.to)}"`)}`);
|
|
2142
2219
|
}
|
|
2143
|
-
lines.push(`Node.js: require('exceljs') of @xlvane/compat needs Node.js 22.12 or later (package.json engines.node: ${typeof engines === "string" ? `"${engines}"` : "not set"}).`);
|
|
2220
|
+
lines.push(`Node.js: require('exceljs') of @xlvane/compat needs Node.js 22.12 or later (package.json engines.node: ${typeof engines === "string" ? `"${escapeControls(engines)}"` : "not set"}).`);
|
|
2144
2221
|
lines.push("", `Scanned ${plural(files.length, "source file")}${limited ? ` (stopped at ${String(MAX_FILES)})` : ""} for exceljs API that @xlvane/compat does not fully support:`);
|
|
2145
2222
|
if (findings.length === 0) lines.push(" none found.");
|
|
2146
|
-
for (const f of findings.slice(0, 200)) lines.push(` ${f.status === "partly" ? out.yellow("partly ") : out.red("not supported")} ${f.file}:${String(f.line)}:${String(f.column)} ${f.api}`);
|
|
2223
|
+
for (const f of findings.slice(0, 200)) lines.push(` ${f.status === "partly" ? out.yellow("partly ") : out.red("not supported")} ${escapeControls(f.file)}:${String(f.line)}:${String(f.column)} ${escapeControls(f.api)}`);
|
|
2147
2224
|
if (findings.length > 200) lines.push(out.dim(` … ${String(findings.length - 200)} more; use --json for all`));
|
|
2148
2225
|
lines.push(" Details: docs/exceljs-compat.md. Unsupported calls throw XLV_E_NOT_SUPPORTED at run time.");
|
|
2149
2226
|
lines.push("");
|
|
2150
2227
|
if (write) lines.push(wroteLine(write));
|
|
2151
|
-
else if (changes.length > 0) lines.push(`Nothing was written. Run "${commandLine([
|
|
2228
|
+
else if (changes.length > 0) lines.push(`Nothing was written. Run "${commandLine(dir.startsWith("-") ? [
|
|
2229
|
+
"migrate",
|
|
2230
|
+
"--force",
|
|
2231
|
+
"--",
|
|
2232
|
+
dir
|
|
2233
|
+
] : [
|
|
2152
2234
|
"migrate",
|
|
2153
2235
|
...dir === "." ? [] : [dir],
|
|
2154
2236
|
"--force"
|
|
@@ -2300,7 +2382,7 @@ const readCommand = {
|
|
|
2300
2382
|
result,
|
|
2301
2383
|
exitCode: 0,
|
|
2302
2384
|
render: (out) => {
|
|
2303
|
-
if (table.columns.length === 0) return `Sheet "${table.sheet}" has no data${table.range ? ` in ${table.range}` : ""}.`;
|
|
2385
|
+
if (table.columns.length === 0) return `Sheet "${escapeControls(table.sheet)}" has no data${table.range ? ` in ${table.range}` : ""}.`;
|
|
2304
2386
|
const numeric = table.columns.map((c) => shown.length > 0 && shown.every((row) => row[c.key] === null || typeof row[c.key] === "number") ? "right" : "left");
|
|
2305
2387
|
return out.table(table.columns.map((c) => c.header ?? c.key), shown.map((row) => table.columns.map((c) => showValue(row[c.key]))), "", numeric);
|
|
2306
2388
|
}
|
|
@@ -2410,6 +2492,8 @@ const COMMANDS = [
|
|
|
2410
2492
|
const problems = [];
|
|
2411
2493
|
let sheetName = null;
|
|
2412
2494
|
let rowsChecked = 0;
|
|
2495
|
+
const named = str(ctx.args, "sheet");
|
|
2496
|
+
if (named !== void 0) workbook.getSheet(named);
|
|
2413
2497
|
if (rules.length > 0) {
|
|
2414
2498
|
const sheet = str(ctx.args, "sheet");
|
|
2415
2499
|
const table = readTable(workbook, {
|
|
@@ -2481,7 +2565,7 @@ const COMMANDS = [
|
|
|
2481
2565
|
lines.push("");
|
|
2482
2566
|
}
|
|
2483
2567
|
if (warnings.length > 0) lines.push(...warningsBlock(out, warnings), "");
|
|
2484
|
-
const checked = sheetName !== null ? `, ${plural(rowsChecked, "row")} of sheet "${sheetName}" checked against ${plural(rules.length, "column rule")}` : "";
|
|
2568
|
+
const checked = sheetName !== null ? `, ${plural(rowsChecked, "row")} of sheet "${escapeControls(sheetName)}" checked against ${plural(rules.length, "column rule")}` : "";
|
|
2485
2569
|
if (ok && warnings.length > 0) lines.push(`${out.yellow(`OK with ${plural(warnings.length, "warning")}`)}: ${file}${checked}. Use --strict to fail on warnings.`);
|
|
2486
2570
|
else if (ok) lines.push(`${out.green("OK")}: ${file}${checked}.`);
|
|
2487
2571
|
else lines.push(`${out.red("Failed")}: ${file}${checked}${strict && problems.length === 0 ? " (warnings count with --strict)" : ""}.`);
|
|
@@ -2658,11 +2742,7 @@ function suggestCommand(error, first, rest, spec) {
|
|
|
2658
2742
|
}
|
|
2659
2743
|
}
|
|
2660
2744
|
if (spec === void 0) return null;
|
|
2661
|
-
if (error.code === "XLV_E_FILE_EXISTS") return commandLine([
|
|
2662
|
-
spec.name,
|
|
2663
|
-
...rest,
|
|
2664
|
-
"--force"
|
|
2665
|
-
]);
|
|
2745
|
+
if (error.code === "XLV_E_FILE_EXISTS") return commandLine(withOptions([spec.name, ...rest], "--force"));
|
|
2666
2746
|
if (error.code === "XLV_E_SHEET_NOT_FOUND" && Array.isArray(d["available"]) && typeof error.location.sheet === "string") {
|
|
2667
2747
|
const close = suggest(error.location.sheet, d["available"], 1);
|
|
2668
2748
|
const fixed = close.length > 0 ? withSheetName(rest, error.location.sheet, close[0]) : null;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as InspectOptions, $n as
|
|
1
|
+
import { $ as InspectOptions, $n as SheetProtection, $t as DataValidationOperator, A as toJSON, An as SheetChart, At as Workbook, B as FormulaResult, Bn as Hyperlink, Bt as CfValue, C as TableRow, Cn as ChartInput, Cr as isXlvError, Ct as Sheet, D as fromJSON, Dn as ChartSeriesInput, Dr as DefinedNameOptions, Dt as SheetState, E as WriteTableOptions, En as ChartSeries, Er as DefinedName, Et as SheetProperties, F as CellInput, Fn as ImageOptions, Ft as TableDefinitionColumn, G as SheetPropertiesOptions, Gn as PageOrder, Gt as ConditionalFormatRule, H as NewSheet, Hn as HeaderFooter, Ht as CfValueType, I as ColumnOptions, In as ImagePositionInput, It as TableInput, J as WriteWorkbookOptions, Jn as PageSetupOptions, Jt as DataBarAxisPosition, K as SheetViewOptions, Kn as PageOrientation, Kt as ConditionalFormatRuleInput, L as CreateWorkbookOptions, Ln as SheetImage, Lt as TableStyleOptions, M as StandardSchemaV1, Mn as ImageAnchor, Mt as WorkbookView, N as AddSheetOptions, Nn as ImageAnchorInput, Nt as TableColumnInput, O as readTable, On as ChartText, Ot as SheetView, P as CalculationOptions, Pn as ImageFormat, Pt as TableDefinition, Q as InspectDefinedName, Qn as PrintTitles, Qt as DataValidationImeMode, R as ERROR_VALUES, Rn as WorkbookImage, Rt as TotalsRowFunction, S as TableResult, Sn as ChartAxisInput, Sr as XlvWarningCode, St as RowInfo, T as ToJSONOptions, Tn as ChartLegendPosition, Tr as ReadLimits, Tt as SheetLayout, U as NewWorkbook, Un as PageBreaks, Ut as ConditionalFormat, V as FreezeOptions, Vn as HyperlinkTarget, Vt as CfValueInput, W as RowOptions, Wn as PageMargins, Wt as ConditionalFormatOperator, X as writeWorkbook, Xn as PrintErrors, Xt as DataValidation, Y as createWorkbook, Yn as PrintCellComments, Yt as DataBarDirection, Z as InspectColumn, Zn as PrintOptions, Zt as DataValidationErrorStyle, _ as JsonTable, _n as ReadingOrder, _r as XlvErrorCode, _t as DateSystem, a as WriteWorkbookStreamOptions, an as BorderEdge, ar as PasswordHash, at as ReadOptions, b as TableColumnSpec, bn as VerticalAlignment, br as XlvLocation, bt as FrozenPanes, c as OpenWorkbookStreamOptions, cn as CellStyle, cr as legacyPasswordHash, ct as NoteBox, d as StreamRow, dn as FontScheme, dr as WorkbookProperties, dt as NoteMargins, en as DataValidationRule, er as SheetProtectionFlags, et as InspectReport, f as StreamSheetInfo, fn as FontStyle, fr as ERROR_CODES, ft as Cell, g as readTableStream, gn as ProtectionStyle, gr as XlvError, gt as Conformance, h as openWorkbookStream, hn as PatternType, hr as XlvDetails, ht as ColumnInfo, i as WriteTableStreamOptions, in as AlignmentStyle, ir as HashPasswordOptions, it as inspect, j as writeTable, jn as AnchorMarker, jt as WorkbookFormat, k as readTableAsync, kn as ChartType, kt as SplitPanes, l as ReadTableStreamOptions, ln as Color, lr as verifyPassword, lt as NoteBoxInput, m as WorkbookStream, mn as HorizontalAlignment, mr as XlvDetailValue, mt as CellValue, n as StreamRowInput, nn as IconSetType, nr as WorkbookProtection, nt as InspectTypeCounts, o as writeTableStream, on as BorderLineStyle, or as PasswordVerifier, ot as readWorkbook, p as TableStreamItem, pn as GradientFill, pr as WARNING_CODES, pt as CellType, q as WorkbookViewOptions, qn as PageSetup, qt as ConditionalFormatType, r as StreamSheetSpec, rn as TimePeriod, rr as WorkbookProtectionOptions, rt as InspectValue, s as writeWorkbookStream, sn as BorderStyle, sr as hashPassword, st as Note, t as StreamDefinedName, tn as DataValidationType, tr as SheetProtectionOptions, tt as InspectSheet, u as STREAM_LIMIT_DEFAULTS, un as FillStyle, ur as ShaAlgorithm, ut as NoteInput, v as ReadTableOptions, vn as UnderlineStyle, vr as XlvErrorInit, vt as Formula, w as TableValue, wn as ChartKind, wr as DEFAULT_READ_LIMITS, wt as SheetKind, x as TableIssue, xn as ChartAxis, xr as XlvWarning, xt as RichTextRun, y as TableColumn, yn as VerticalAlignRun, yr as XlvErrorJSON, yt as FormulaKind, z as ErrorValue, zn as imageFormat, zt as CfIcon } from "./stream-write-XnCyLdOy.js";
|
|
2
2
|
/**
|
|
3
3
|
* Options for {@link toCSV}.
|
|
4
4
|
*
|
|
@@ -14,7 +14,11 @@ interface ToCSVOptions {
|
|
|
14
14
|
readonly sheet?: string;
|
|
15
15
|
/** Only this range, for example `"A1:D100"`. Default: from A1 to the last used row and column. */
|
|
16
16
|
readonly range?: string;
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Field separator: one character other than `"`, CR or LF. Default `","`. While formulas are
|
|
19
|
+
* escaped (`formulaInjection: "escape"`), `=`, `+`, `-`, `@` and their full-width and small forms
|
|
20
|
+
* are refused, because a line whose first field is empty would start with them.
|
|
21
|
+
*/
|
|
18
22
|
readonly delimiter?: string;
|
|
19
23
|
/** Line ending. Default `"\r\n"` (RFC 4180). */
|
|
20
24
|
readonly newline?: "\r\n" | "\n";
|
|
@@ -22,7 +26,8 @@ interface ToCSVOptions {
|
|
|
22
26
|
readonly bom?: boolean;
|
|
23
27
|
/**
|
|
24
28
|
* `"escape"` (default): text cells that start with `=`, `+`, `-`, `@`, their full-width or small
|
|
25
|
-
* forms,
|
|
29
|
+
* forms, a control character (tab, CR, LF, NUL, …) or a byte order mark (U+FEFF, U+FFFE, which
|
|
30
|
+
* spreadsheet applications drop at the start of a file) get a leading `'` and quotes, so
|
|
26
31
|
* spreadsheet applications show them as text instead of running them as formulas (OWASP CSV
|
|
27
32
|
* Injection). `"off"`: write them as is.
|
|
28
33
|
*/
|
|
@@ -33,8 +38,9 @@ interface ToCSVOptions {
|
|
|
33
38
|
readonly onWarning?: (warning: XlvWarning) => void;
|
|
34
39
|
}
|
|
35
40
|
/**
|
|
36
|
-
* Converts a sheet to CSV text. Fields that contain the delimiter, a quote, CR or
|
|
37
|
-
*
|
|
41
|
+
* Converts a sheet to CSV text. Fields that contain the delimiter, a quote, CR, LF, or one of
|
|
42
|
+
* the common separators `,`, `;`, tab and `|` (whatever the delimiter, CSV-14) are enclosed in
|
|
43
|
+
* quotes, and quotes are doubled (RFC 4180). Numbers are written in JavaScript's
|
|
38
44
|
* shortest form, booleans as `TRUE`/`FALSE`, dates as ISO 8601, error cells as their code and
|
|
39
45
|
* formula cells as their cached value. By default, text that a spreadsheet would run as a
|
|
40
46
|
* formula is escaped (`formulaInjection: "escape"`).
|
|
@@ -266,7 +272,12 @@ interface CellEdit {
|
|
|
266
272
|
* ```
|
|
267
273
|
*/
|
|
268
274
|
interface EditOptions {
|
|
269
|
-
/**
|
|
275
|
+
/**
|
|
276
|
+
* Read limits for the input file; see `ReadLimits`. For `editCells` and `appendRows`,
|
|
277
|
+
* `maxCells` also bounds the edit: the cells of the edited sheets plus the new cells must not
|
|
278
|
+
* exceed it, and one call writes at most 1 000 000 cells unless `maxCells` is given, in which
|
|
279
|
+
* case it writes at most `maxCells` (`XLV_E_LIMIT_EXCEEDED`, `details.limit` `"maxCells"`).
|
|
280
|
+
*/
|
|
270
281
|
readonly limits?: Partial<ReadLimits>;
|
|
271
282
|
}
|
|
272
283
|
/**
|
|
@@ -329,10 +340,14 @@ export declare function editCells(input: Uint8Array | ArrayBuffer, edits: readon
|
|
|
329
340
|
* Appends rows below the last used row of a sheet (the rows of merged ranges count as used) in
|
|
330
341
|
* an existing XLSX file, editing the file surgically like {@link editCells}. Object rows are
|
|
331
342
|
* placed under the matching header columns (unknown keys throw `XLV_E_INVALID_ROWS` with
|
|
332
|
-
* suggestions)
|
|
333
|
-
*
|
|
343
|
+
* suggestions). Array rows start at the first used column, or, when the new rows extend a table
|
|
344
|
+
* (one that reaches the last used row), at that table's first column; when several such tables
|
|
345
|
+
* start in different columns the call is refused with `XLV_E_INVALID_ARGUMENT` (pass object rows
|
|
346
|
+
* instead). A table whose last row is the sheet's last used row grows to include the new rows
|
|
347
|
+
* (its range and filter); if that table has a totals row the call is refused with
|
|
334
348
|
* `XLV_E_NOT_SUPPORTED` and a hint to insert the rows above the totals row with `insertRows`.
|
|
335
|
-
* Calculated columns are not filled in the new rows.
|
|
349
|
+
* Calculated columns are not filled in the new rows. One call writes at most 1 000 000 cells
|
|
350
|
+
* unless `limits.maxCells` is given (see `EditOptions`).
|
|
336
351
|
*
|
|
337
352
|
* @example
|
|
338
353
|
* ```ts
|
|
@@ -343,6 +358,9 @@ export declare function editCells(input: Uint8Array | ArrayBuffer, edits: readon
|
|
|
343
358
|
* { Customer: 'Alice', Total: 120.5 },
|
|
344
359
|
* { Customer: 'Bob', Total: 80 },
|
|
345
360
|
* ]);
|
|
361
|
+
* // Array rows that extend a table start at the table's first column.
|
|
362
|
+
* const { bytes: more } = appendRows(updated, 'Orders', [['Carol', 64]]);
|
|
363
|
+
* console.log(more.length);
|
|
346
364
|
* ```
|
|
347
365
|
*/
|
|
348
366
|
export declare function appendRows(input: Uint8Array | ArrayBuffer, sheet: string, rows: readonly (readonly CellInput[])[] | readonly Readonly<Record<string, CellInput>>[], options?: AppendRowsOptions): EditResult;
|
|
@@ -367,6 +385,8 @@ interface InsertRowsOptions extends EditOptions {
|
|
|
367
385
|
/**
|
|
368
386
|
* Formatting of the new rows: `'above'` copies the row format, height and cell formats of the row
|
|
369
387
|
* above, `'below'` those of the row that is pushed down, `'none'` (default) leaves them empty.
|
|
388
|
+
* The copied cell formats are cells that count against `limits.maxCells` with the cells of the
|
|
389
|
+
* workbook (`XLV_E_LIMIT_EXCEEDED` above it).
|
|
370
390
|
*/
|
|
371
391
|
readonly style?: "above" | "below" | "none";
|
|
372
392
|
}
|
|
@@ -403,7 +423,9 @@ interface DeleteRowsOptions extends EditOptions {
|
|
|
403
423
|
* custom sheet views, other extended content that refers to cells (sparklines, slicers) and
|
|
404
424
|
* other related parts xlvane does not know; charts or pivot tables anywhere in the workbook that
|
|
405
425
|
* may refer to the sheet; data tables; an array formula the change would split; and formulas it
|
|
406
|
-
* cannot rewrite safely (docs/facts/ref-adjust.md).
|
|
426
|
+
* cannot rewrite safely (docs/facts/ref-adjust.md). Rows formatted with `style` add a formatted
|
|
427
|
+
* cell for each formatted cell of the row they copy; when the workbook would then hold more cells
|
|
428
|
+
* than `limits.maxCells`, the call throws `XLV_E_LIMIT_EXCEEDED` before building them.
|
|
407
429
|
*
|
|
408
430
|
* @example
|
|
409
431
|
* ```ts
|
|
@@ -517,4 +539,4 @@ export declare function adjustRange(sqref: string, change: ReferenceChange): str
|
|
|
517
539
|
* ```
|
|
518
540
|
*/
|
|
519
541
|
export declare function shiftFormula(text: string, dRow: number, dCol: number): string | null;
|
|
520
|
-
export { type AddSheetOptions, type AlignmentStyle, type AnchorMarker, type AppendRowsOptions, type BorderEdge, type BorderLineStyle, type BorderStyle, type CalculationOptions, type Cell, type CellChange, type CellEdit, type CellInput, type CellStyle, type CellType, type CellValue, type CfIcon, type CfValue, type CfValueInput, type CfValueType, type Color, type ColumnInfo, type ColumnOptions, type ConditionalFormat, type ConditionalFormatOperator, type ConditionalFormatRule, type ConditionalFormatRuleInput, type ConditionalFormatType, type Conformance, type CreateWorkbookOptions, DEFAULT_READ_LIMITS, type DataBarAxisPosition, type DataBarDirection, type DataValidation, type DataValidationErrorStyle, type DataValidationImeMode, type DataValidationOperator, type DataValidationRule, type DataValidationType, type DateSystem, type DefinedName, type DefinedNameOptions, type DeleteRowsOptions, type DiffCell, type DiffOptions, ERROR_CODES, ERROR_VALUES, type EditOptions, type EditResult, type ErrorValue, type FillStyle, type FontScheme, type FontStyle, type Formula, type FormulaKind, type FormulaResult, type FreezeOptions, type FromCSVOptions, type FrozenPanes, type GradientFill, type HashPasswordOptions, type HeaderFooter, type HorizontalAlignment, type Hyperlink, type HyperlinkTarget, type IconSetType, type ImageAnchor, type ImageAnchorInput, type ImageFormat, type ImageOptions, type ImagePositionInput, type InsertRowsOptions, type InspectColumn, type InspectDefinedName, type InspectOptions, type InspectReport, type InspectSheet, type InspectTypeCounts, type InspectValue, type JsonTable, NewSheet, NewWorkbook, type Note, type NoteBox, type NoteBoxInput, type NoteInput, type NoteMargins, type OpenWorkbookStreamOptions, type PageBreaks, type PageMargins, type PageOrder, type PageOrientation, type PageSetup, type PageSetupOptions, type PasswordHash, type PasswordVerifier, type PatternType, type PrintCellComments, type PrintErrors, type PrintOptions, type PrintTitles, type ProtectionStyle, type ReadLimits, type ReadOptions, type ReadTableOptions, type ReadTableStreamOptions, type ReadingOrder, type ReferenceChange, type RichTextRun, type RowInfo, type RowOptions, STREAM_LIMIT_DEFAULTS, type ShaAlgorithm, type Sheet, type SheetDiff, type SheetImage, type SheetKind, type SheetLayout, type SheetProperties, type SheetPropertiesOptions, type SheetProtection, type SheetProtectionFlags, type SheetProtectionOptions, type SheetState, type SheetView, type SheetViewOptions, type SplitPanes, type StandardSchemaV1, type StreamDefinedName, type StreamRow, type StreamRowInput, type StreamSheetInfo, type StreamSheetSpec, type TableColumn, type TableColumnInput, type TableColumnSpec, type TableDefinition, type TableDefinitionColumn, type TableInput, type TableIssue, type TableResult, type TableRow, type TableStreamItem, type TableStyleOptions, type TableValue, type TimePeriod, type ToCSVOptions, type ToJSONOptions, type TotalsRowFunction, type UnderlineStyle, type VerticalAlignRun, type VerticalAlignment, WARNING_CODES, type Workbook, type WorkbookDiff, type WorkbookFormat, type WorkbookImage, type WorkbookProperties, type WorkbookProtection, type WorkbookProtectionOptions, type WorkbookStream, type WorkbookView, type WorkbookViewOptions, type WriteTableOptions, type WriteTableStreamOptions, type WriteWorkbookOptions, type WriteWorkbookStreamOptions, type XlvDetailValue, type XlvDetails, XlvError, type XlvErrorCode, type XlvErrorInit, type XlvErrorJSON, type XlvLocation, type XlvWarning, type XlvWarningCode, createWorkbook, fromJSON, hashPassword, imageFormat, inspect, isXlvError, legacyPasswordHash, openWorkbookStream, readTable, readTableAsync, readTableStream, readWorkbook, toJSON, verifyPassword, writeTable, writeTableStream, writeWorkbook, writeWorkbookStream };
|
|
542
|
+
export { type AddSheetOptions, type AlignmentStyle, type AnchorMarker, type AppendRowsOptions, type BorderEdge, type BorderLineStyle, type BorderStyle, type CalculationOptions, type Cell, type CellChange, type CellEdit, type CellInput, type CellStyle, type CellType, type CellValue, type CfIcon, type CfValue, type CfValueInput, type CfValueType, type ChartAxis, type ChartAxisInput, type ChartInput, type ChartKind, type ChartLegendPosition, type ChartSeries, type ChartSeriesInput, type ChartText, type ChartType, type Color, type ColumnInfo, type ColumnOptions, type ConditionalFormat, type ConditionalFormatOperator, type ConditionalFormatRule, type ConditionalFormatRuleInput, type ConditionalFormatType, type Conformance, type CreateWorkbookOptions, DEFAULT_READ_LIMITS, type DataBarAxisPosition, type DataBarDirection, type DataValidation, type DataValidationErrorStyle, type DataValidationImeMode, type DataValidationOperator, type DataValidationRule, type DataValidationType, type DateSystem, type DefinedName, type DefinedNameOptions, type DeleteRowsOptions, type DiffCell, type DiffOptions, ERROR_CODES, ERROR_VALUES, type EditOptions, type EditResult, type ErrorValue, type FillStyle, type FontScheme, type FontStyle, type Formula, type FormulaKind, type FormulaResult, type FreezeOptions, type FromCSVOptions, type FrozenPanes, type GradientFill, type HashPasswordOptions, type HeaderFooter, type HorizontalAlignment, type Hyperlink, type HyperlinkTarget, type IconSetType, type ImageAnchor, type ImageAnchorInput, type ImageFormat, type ImageOptions, type ImagePositionInput, type InsertRowsOptions, type InspectColumn, type InspectDefinedName, type InspectOptions, type InspectReport, type InspectSheet, type InspectTypeCounts, type InspectValue, type JsonTable, NewSheet, NewWorkbook, type Note, type NoteBox, type NoteBoxInput, type NoteInput, type NoteMargins, type OpenWorkbookStreamOptions, type PageBreaks, type PageMargins, type PageOrder, type PageOrientation, type PageSetup, type PageSetupOptions, type PasswordHash, type PasswordVerifier, type PatternType, type PrintCellComments, type PrintErrors, type PrintOptions, type PrintTitles, type ProtectionStyle, type ReadLimits, type ReadOptions, type ReadTableOptions, type ReadTableStreamOptions, type ReadingOrder, type ReferenceChange, type RichTextRun, type RowInfo, type RowOptions, STREAM_LIMIT_DEFAULTS, type ShaAlgorithm, type Sheet, type SheetChart, type SheetDiff, type SheetImage, type SheetKind, type SheetLayout, type SheetProperties, type SheetPropertiesOptions, type SheetProtection, type SheetProtectionFlags, type SheetProtectionOptions, type SheetState, type SheetView, type SheetViewOptions, type SplitPanes, type StandardSchemaV1, type StreamDefinedName, type StreamRow, type StreamRowInput, type StreamSheetInfo, type StreamSheetSpec, type TableColumn, type TableColumnInput, type TableColumnSpec, type TableDefinition, type TableDefinitionColumn, type TableInput, type TableIssue, type TableResult, type TableRow, type TableStreamItem, type TableStyleOptions, type TableValue, type TimePeriod, type ToCSVOptions, type ToJSONOptions, type TotalsRowFunction, type UnderlineStyle, type VerticalAlignRun, type VerticalAlignment, WARNING_CODES, type Workbook, type WorkbookDiff, type WorkbookFormat, type WorkbookImage, type WorkbookProperties, type WorkbookProtection, type WorkbookProtectionOptions, type WorkbookStream, type WorkbookView, type WorkbookViewOptions, type WriteTableOptions, type WriteTableStreamOptions, type WriteWorkbookOptions, type WriteWorkbookStreamOptions, type XlvDetailValue, type XlvDetails, XlvError, type XlvErrorCode, type XlvErrorInit, type XlvErrorJSON, type XlvLocation, type XlvWarning, type XlvWarningCode, createWorkbook, fromJSON, hashPassword, imageFormat, inspect, isXlvError, legacyPasswordHash, openWorkbookStream, readTable, readTableAsync, readTableStream, readWorkbook, toJSON, verifyPassword, writeTable, writeTableStream, writeWorkbook, writeWorkbookStream };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as appendRows, c as fromCSV, d as fromJSON, g as writeTable, h as toJSON, i as adjustRange, m as readTableAsync, n as insertRows, o as editCells, p as readTable, r as adjustFormula, s as diff, t as deleteRows, u as toCSV } from "./rows-
|
|
3
|
-
import { a as readTableStream, i as openWorkbookStream, n as writeWorkbookStream, r as STREAM_LIMIT_DEFAULTS, t as writeTableStream } from "./stream-write-
|
|
1
|
+
import { Bn as isXlvError, Gt as hashPassword, Kt as legacyPasswordHash, Ln as ERROR_CODES, Lt as DEFAULT_READ_LIMITS, Qt as imageFormat, Rn as WARNING_CODES, _ as inspect, c as NewSheet, dn as shiftFormula, f as createWorkbook, l as NewWorkbook, m as writeWorkbook, qt as verifyPassword, s as ERROR_VALUES, x as readWorkbook, zn as XlvError } from "./options-Cxt-LJE4.js";
|
|
2
|
+
import { a as appendRows, c as fromCSV, d as fromJSON, g as writeTable, h as toJSON, i as adjustRange, m as readTableAsync, n as insertRows, o as editCells, p as readTable, r as adjustFormula, s as diff, t as deleteRows, u as toCSV } from "./rows-DltZ-hiW.js";
|
|
3
|
+
import { a as readTableStream, i as openWorkbookStream, n as writeWorkbookStream, r as STREAM_LIMIT_DEFAULTS, t as writeTableStream } from "./stream-write-D573ewpr.js";
|
|
4
4
|
export { DEFAULT_READ_LIMITS, ERROR_CODES, ERROR_VALUES, NewSheet, NewWorkbook, STREAM_LIMIT_DEFAULTS, WARNING_CODES, XlvError, adjustFormula, adjustRange, appendRows, createWorkbook, deleteRows, diff, editCells, fromCSV, fromJSON, hashPassword, imageFormat, insertRows, inspect, isXlvError, legacyPasswordHash, openWorkbookStream, readTable, readTableAsync, readTableStream, readWorkbook, shiftFormula, toCSV, toJSON, verifyPassword, writeTable, writeTableStream, writeWorkbook, writeWorkbookStream };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Bt as resolveLimits,
|
|
1
|
+
import { Bt as resolveLimits, zn as XlvError } from "./options-Cxt-LJE4.js";
|
|
2
2
|
import { readFile, stat } from "node:fs/promises";
|
|
3
3
|
// src/util/node-files.ts
|
|
4
4
|
/** The path as shown in messages. */
|
|
@@ -12,7 +12,7 @@ async function checkFile(path, limits) {
|
|
|
12
12
|
const info = await stat(path);
|
|
13
13
|
if (!info.isFile()) throw new XlvError("XLV_E_FILE_READ", `"${shown}" is not a file.`, {
|
|
14
14
|
location: {},
|
|
15
|
-
hint: "Pass the path of an .xlsx file, not a directory.",
|
|
15
|
+
hint: info.isDirectory() ? "Pass the path of an .xlsx file, not a directory." : "Pass the path of an .xlsx file, not a pipe, device or other special file.",
|
|
16
16
|
details: { path: shown }
|
|
17
17
|
});
|
|
18
18
|
size = info.size;
|
package/dist/node.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as InspectOptions, At as Workbook, J as WriteWorkbookOptions, U as NewWorkbook, a as WriteWorkbookStreamOptions, at as ReadOptions, c as OpenWorkbookStreamOptions, et as InspectReport, m as WorkbookStream } from "./stream-write-
|
|
1
|
+
import { $ as InspectOptions, At as Workbook, J as WriteWorkbookOptions, U as NewWorkbook, a as WriteWorkbookStreamOptions, at as ReadOptions, c as OpenWorkbookStreamOptions, et as InspectReport, m as WorkbookStream } from "./stream-write-XnCyLdOy.js";
|
|
2
2
|
/**
|
|
3
3
|
* Reads an XLSX file from disk. Node.js only; in other runtimes pass the bytes to
|
|
4
4
|
* `readWorkbook` from `xlvane`. The file size is checked against `limits.maxInputBytes`
|
package/dist/node.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Bt as resolveLimits,
|
|
2
|
-
import { i as readInput, n as describePath, r as fileError, t as checkFile } from "./node-files-
|
|
3
|
-
import { i as openWorkbookStream, n as writeWorkbookStream, r as STREAM_LIMIT_DEFAULTS } from "./stream-write-
|
|
1
|
+
import { Bt as resolveLimits, _ as inspect, m as writeWorkbook, x as readWorkbook, zn as XlvError } from "./options-Cxt-LJE4.js";
|
|
2
|
+
import { i as readInput, n as describePath, r as fileError, t as checkFile } from "./node-files-SVg17pky.js";
|
|
3
|
+
import { i as openWorkbookStream, n as writeWorkbookStream, r as STREAM_LIMIT_DEFAULTS } from "./stream-write-D573ewpr.js";
|
|
4
4
|
import { openAsBlob } from "node:fs";
|
|
5
5
|
import { open, unlink, writeFile } from "node:fs/promises";
|
|
6
6
|
// src/node.ts
|