xlvane 1.1.0 → 1.2.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 +5 -4
- package/dist/cli.js +226 -47
- package/dist/index.d.ts +5 -4
- package/dist/index.js +3 -3
- package/dist/{node-files-B0iqSJIJ.js → node-files-CDb6TyZb.js} +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +3 -3
- package/dist/{options-CwSp2pA4.js → options-Ba1iWD-Z.js} +316 -75
- package/dist/{rows-DKwlr0rg.js → rows-IWKIxHt9.js} +59 -15
- package/dist/{stream-write-BKppyMz0.js → stream-write-BGuUjlXo.js} +24 -9
- package/dist/{stream-write-GJXORYQ2.d.ts → stream-write-BVaFkgwO.d.ts} +9 -6
- package/docs/cli-json/mcp.schema.json +43 -0
- package/docs/cli-json.md +2 -1
- package/docs/errors.md +27 -0
- package/docs/exceljs-compat.md +1 -1
- package/llms-full.txt +90 -10
- package/package.json +1 -1
- package/skills/xlvane/SKILL.md +8 -3
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ xlvane help edit # usage and examples of one command
|
|
|
58
58
|
- `--json` prints one JSON document per command with a documented schema ([docs/cli-json.md](https://github.com/DevXord/xlvane/blob/main/docs/cli-json.md)). Exit codes: 0 success, 1 a check found something (`diff`, `validate`), 2 usage error, 3 input or file error.
|
|
59
59
|
- Colour (red errors, yellow warnings, green results, bold headings) is used only on a terminal and never carries information alone; `NO_COLOR` or `TERM=dumb` turn it off, `FORCE_COLOR=1` turns it on. Control characters from files, such as an escape character in a cell, are shown as `\x1b` and never reach the terminal.
|
|
60
60
|
- Tables and summaries go to separate streams: `xlvane read report.xlsx > out.txt` writes only the table; the row count and warnings notes go to stderr.
|
|
61
|
-
- `xlvane migrate` prepares a project for `@xlvane/compat
|
|
61
|
+
- `xlvane migrate` prepares a project for `@xlvane/compat`, and `xlvane mcp` starts the MCP server from `@xlvane/mcp` (a separate package; [its README](https://github.com/DevXord/xlvane/blob/main/packages/mcp/README.md)).
|
|
62
62
|
|
|
63
63
|
## Reading a workbook
|
|
64
64
|
|
|
@@ -385,10 +385,10 @@ Limits can be changed per call, for example `readWorkbook(bytes, { limits: { max
|
|
|
385
385
|
| `maxXmlDepth` | 128 | deeply nested XML |
|
|
386
386
|
| `maxXmlAttributes` | 256 | elements with huge numbers of attributes |
|
|
387
387
|
| `maxXmlNameLength` | 1 024 | huge element or attribute names |
|
|
388
|
-
| `maxXmlTextLength` | 8 Mi characters | huge text nodes
|
|
388
|
+
| `maxXmlTextLength` | 8 Mi characters | huge text nodes, attribute values, comments or processing instructions |
|
|
389
389
|
| `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) |
|
|
391
|
-
| `maxSharedStringChars` | 512 Mi characters | many cells pointing at one huge shared string (a few MB that become many GB when values are copied) |
|
|
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 |
|
|
392
392
|
|
|
393
393
|
## Package size
|
|
394
394
|
|
|
@@ -403,6 +403,7 @@ Limits can be changed per call, for example `readWorkbook(bytes, { limits: { max
|
|
|
403
403
|
|
|
404
404
|
## Links
|
|
405
405
|
|
|
406
|
+
- Website with the guide and an in-browser demo: [xlvane.github.io](https://xlvane.github.io)
|
|
406
407
|
- Changes in each version: [CHANGELOG.md](https://github.com/DevXord/xlvane/blob/main/CHANGELOG.md)
|
|
407
408
|
- Bugs and questions: [GitHub issues](https://github.com/DevXord/xlvane/issues)
|
|
408
409
|
- Reporting a vulnerability: [SECURITY.md](https://github.com/DevXord/xlvane/blob/main/SECURITY.md)
|
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 { On as XlvError, Vt as suggest, _ as isoDate, b as readWorkbook, dn as parseRange, g as inspect } from "./options-Ba1iWD-Z.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-IWKIxHt9.js";
|
|
4
|
+
import { i as readInput } from "./node-files-CDb6TyZb.js";
|
|
5
5
|
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
-
import { chmod, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
6
|
+
import { chmod, lstat, 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";
|
|
@@ -463,8 +463,15 @@ var Output = class {
|
|
|
463
463
|
this.color = color;
|
|
464
464
|
this.width = width;
|
|
465
465
|
}
|
|
466
|
+
/**
|
|
467
|
+
* Colour codes are written with {@link STYLE_MARK} in place of ESC, and an ESC already in the
|
|
468
|
+
* text (from a file) is escaped, so that the final filter ({@link terminalSafe}) can tell the
|
|
469
|
+
* CLI's own colour codes from the same bytes in a sheet name or cell (D-H01).
|
|
470
|
+
*/
|
|
466
471
|
style(format, text) {
|
|
467
|
-
|
|
472
|
+
if (!this.color || text === "") return text;
|
|
473
|
+
const safe = text.includes("\x1B") ? text.split("\x1B").join("\\x1b") : text;
|
|
474
|
+
return nodeUtil.styleText(format, safe, { validateStream: false }).split("\x1B").join(STYLE_MARK);
|
|
468
475
|
}
|
|
469
476
|
bold(text) {
|
|
470
477
|
return this.style("bold", text);
|
|
@@ -519,7 +526,8 @@ function oneLine(text) {
|
|
|
519
526
|
* which includes the 8-bit CSI U+009B) written as `\x1b`, and bidirectional formatting characters
|
|
520
527
|
* written as `\u202e`, so text from a file, such as a cell
|
|
521
528
|
* holding `_x001B_]0;title_x0007_`, cannot move the cursor, change the window title or hide
|
|
522
|
-
* output (docs/decisions.md D-R02). Used for all human-readable output; `--json`
|
|
529
|
+
* output (docs/decisions.md D-R02). Used for all human-readable output; `--json` writes the same
|
|
530
|
+
* characters as `\uXXXX` escapes instead (D-H01).
|
|
523
531
|
*/
|
|
524
532
|
function escapeControls(text) {
|
|
525
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")}`);
|
|
@@ -531,27 +539,40 @@ function escapeControls(text) {
|
|
|
531
539
|
* `\u202e` in human-readable output, like other control characters (D-R08).
|
|
532
540
|
*/
|
|
533
541
|
const BIDI = /[\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/g;
|
|
534
|
-
/**
|
|
535
|
-
|
|
542
|
+
/**
|
|
543
|
+
* Stands for ESC in the colour codes `Output` writes until {@link terminalSafe} turns it back:
|
|
544
|
+
* NUL and 16 random hexadecimal digits, new for each process, so text from a file cannot contain
|
|
545
|
+
* it by chance or design. Before 1.1.1 the filter kept every ESC sequence that looked like one of
|
|
546
|
+
* the CLI's colours, including ones from a sheet name (D-H01).
|
|
547
|
+
*/
|
|
548
|
+
const STYLE_MARK = `\u0000${Array.from(globalThis.crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(8)), (b) => b.toString(16).padStart(2, "0")).join("")}`;
|
|
549
|
+
/** The colour sequences `Output` writes (styleText: bold, dim, red, green, yellow, cyan and their resets), after the mark. */
|
|
550
|
+
const OWN_STYLE = /^\[(?:1|2|22|31|32|33|36|39)m/;
|
|
551
|
+
const MARKED_STYLE = new RegExp(`${STYLE_MARK}\\[[0-9;]*m`, "g");
|
|
536
552
|
/**
|
|
537
553
|
* Final filter for human-readable output: escapes every control character (see
|
|
538
|
-
* {@link escapeControls}) except line feeds
|
|
539
|
-
*
|
|
554
|
+
* {@link escapeControls}) except line feeds and tabs. When `keepStyles` is true, the colour codes
|
|
555
|
+
* that `Output` wrote (marked with {@link STYLE_MARK}) are turned into ESC sequences; any ESC in
|
|
556
|
+
* the text itself is escaped. Applied to everything the CLI prints for people.
|
|
540
557
|
*/
|
|
541
558
|
function terminalSafe(text, keepStyles) {
|
|
559
|
+
if (!text.includes(STYLE_MARK)) return controlsSafe(text);
|
|
560
|
+
const parts = text.split(STYLE_MARK);
|
|
561
|
+
let result = controlsSafe(parts[0] ?? "");
|
|
562
|
+
for (const part of parts.slice(1)) {
|
|
563
|
+
const own = OWN_STYLE.exec(part)?.[0];
|
|
564
|
+
if (own === void 0) result += controlsSafe(part);
|
|
565
|
+
else result += (keepStyles ? `\u001b${own}` : "") + controlsSafe(part.slice(own.length));
|
|
566
|
+
}
|
|
567
|
+
return result;
|
|
568
|
+
}
|
|
569
|
+
/** Escapes the control characters of text (see {@link escapeControls}) except line feeds and tabs. */
|
|
570
|
+
function controlsSafe(text) {
|
|
542
571
|
let result = "";
|
|
543
572
|
let start = 0;
|
|
544
573
|
for (let i = 0; i < text.length; i++) {
|
|
545
574
|
const code = text.charCodeAt(i);
|
|
546
575
|
if (!(code < 32 && code !== 10 && code !== 9 || code >= 127 && code <= 159 || code === 1564 || code === 8206 || code === 8207 || code >= 8234 && code <= 8238 || code >= 8294 && code <= 8297)) continue;
|
|
547
|
-
if (keepStyles && code === 27) {
|
|
548
|
-
OWN_STYLES.lastIndex = i;
|
|
549
|
-
const m = OWN_STYLES.exec(text);
|
|
550
|
-
if (m !== null) {
|
|
551
|
-
i += m[0].length - 1;
|
|
552
|
-
continue;
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
576
|
result += text.slice(start, i) + escapeControls(text[i]);
|
|
556
577
|
start = i + 1;
|
|
557
578
|
}
|
|
@@ -591,6 +612,7 @@ const segmenter = typeof Intl === "object" && typeof Intl.Segmenter === "functio
|
|
|
591
612
|
*/
|
|
592
613
|
function displayWidth(text) {
|
|
593
614
|
if (text.includes("\x1B")) text = text.replace(/\u001b\[[0-9;]*m/g, "");
|
|
615
|
+
if (text.includes(STYLE_MARK)) text = text.replace(MARKED_STYLE, "");
|
|
594
616
|
let width = 0;
|
|
595
617
|
for (let i = 0; i < text.length; i++) {
|
|
596
618
|
const code = text.charCodeAt(i);
|
|
@@ -913,7 +935,7 @@ function welcome(out, version, commands) {
|
|
|
913
935
|
"",
|
|
914
936
|
`${out.bold("Commands:")} ${commands.map((c) => c.name).join(", ")}`,
|
|
915
937
|
`${out.bold("More:")} xlvane help <command> · Docs: ${DOCS_URL}`,
|
|
916
|
-
`For AI agents and scripts: add --json to any command (${DOCS_IN_PACKAGE}/cli-json.md)`,
|
|
938
|
+
`For AI agents and scripts: add --json to any command (${DOCS_IN_PACKAGE}/cli-json.md), or run "xlvane mcp"`,
|
|
917
939
|
""
|
|
918
940
|
].join("\n");
|
|
919
941
|
}
|
|
@@ -1334,7 +1356,7 @@ const DEFAULT_LIMIT$1 = 50;
|
|
|
1334
1356
|
function showCell(c) {
|
|
1335
1357
|
if (c === null) return "(empty)";
|
|
1336
1358
|
const value = c.value === null ? "" : typeof c.value === "string" ? JSON.stringify(c.value) : String(c.value);
|
|
1337
|
-
return c.formula !== null ? `=${c.formula}${value !== "" ? ` (${value})` : ""}` : value === "" ? "(no value)" : value;
|
|
1359
|
+
return c.formula !== null ? `=${escapeControls(c.formula)}${value !== "" ? ` (${value})` : ""}` : value === "" ? "(no value)" : value;
|
|
1338
1360
|
}
|
|
1339
1361
|
function kindLabel(out, kind) {
|
|
1340
1362
|
const text = kind.padEnd(7);
|
|
@@ -1393,14 +1415,14 @@ const diffCommand = {
|
|
|
1393
1415
|
result: r,
|
|
1394
1416
|
exitCode: r.equal ? 0 : 1,
|
|
1395
1417
|
render: (out) => {
|
|
1396
|
-
if (r.equal) return `${out.green("No differences")} between ${a} and ${b}.`;
|
|
1418
|
+
if (r.equal) return `${out.green("No differences")} between ${escapeControls(a)} and ${escapeControls(b)}.`;
|
|
1397
1419
|
const s = r.summary;
|
|
1398
|
-
const lines = [`${out.bold(`${a} → ${b}`)}: ${plural(s.cellsChanged, "cell")} changed, ${String(s.cellsAdded)} added, ${String(s.cellsRemoved)} removed` + (s.sheetsAdded + s.sheetsRemoved > 0 ? `; sheets added: ${String(s.sheetsAdded)}, removed: ${String(s.sheetsRemoved)}` : "")];
|
|
1399
|
-
for (const name of r.sheetsAdded) lines.push(`${out.green("added sheet")} "${name}"`);
|
|
1400
|
-
for (const name of r.sheetsRemoved) lines.push(`${out.red("removed sheet")} "${name}"`);
|
|
1420
|
+
const lines = [`${out.bold(`${escapeControls(a)} → ${escapeControls(b)}`)}: ${plural(s.cellsChanged, "cell")} changed, ${String(s.cellsAdded)} added, ${String(s.cellsRemoved)} removed` + (s.sheetsAdded + s.sheetsRemoved > 0 ? `; sheets added: ${String(s.sheetsAdded)}, removed: ${String(s.sheetsRemoved)}` : "")];
|
|
1421
|
+
for (const name of r.sheetsAdded) lines.push(`${out.green("added sheet")} "${escapeControls(name)}"`);
|
|
1422
|
+
for (const name of r.sheetsRemoved) lines.push(`${out.red("removed sheet")} "${escapeControls(name)}"`);
|
|
1401
1423
|
for (const sheet of r.sheets) {
|
|
1402
1424
|
if (sheet.cells.length === 0 && !sheet.state) continue;
|
|
1403
|
-
lines.push("", out.bold(`Sheet "${sheet.name}"`));
|
|
1425
|
+
lines.push("", out.bold(`Sheet "${escapeControls(sheet.name)}"`));
|
|
1404
1426
|
if (sheet.state) lines.push(` ${out.yellow("changed")} visibility: ${sheet.state.before} → ${sheet.state.after}`);
|
|
1405
1427
|
for (const c of sheet.cells.slice(0, limit)) {
|
|
1406
1428
|
const style = c.kind === "changed" && showCell(c.before) === showCell(c.after) ? " (formatting)" : "";
|
|
@@ -1757,17 +1779,17 @@ const inspectCommand = {
|
|
|
1757
1779
|
}
|
|
1758
1780
|
};
|
|
1759
1781
|
function renderReport(out, file, r, verbose) {
|
|
1760
|
-
const lines = [`${out.bold(file)}: ${r.format}, ${plural(r.sheetCount, "sheet")}, date system ${r.dateSystem}${r.conformance === "strict" ? ", Strict conformance" : ""}`];
|
|
1782
|
+
const lines = [`${out.bold(escapeControls(file))}: ${r.format}, ${plural(r.sheetCount, "sheet")}, date system ${r.dateSystem}${r.conformance === "strict" ? ", Strict conformance" : ""}`];
|
|
1761
1783
|
if (r.hasMacros) lines.push("Contains macros (not run).");
|
|
1762
1784
|
if (r.externalLinkCount > 0) lines.push(`Links to ${plural(r.externalLinkCount, "other workbook")} (not followed).`);
|
|
1763
1785
|
const props = Object.entries(r.properties);
|
|
1764
1786
|
if (props.length > 0) {
|
|
1765
1787
|
const keyWidth = Math.max(...props.map(([k]) => k.length));
|
|
1766
|
-
lines.push(out.bold("Properties:"), ...props.map(([k, v]) => ` ${k.padEnd(keyWidth)} ${v}`));
|
|
1788
|
+
lines.push(out.bold("Properties:"), ...props.map(([k, v]) => ` ${k.padEnd(keyWidth)} ${escapeControls(String(v))}`));
|
|
1767
1789
|
}
|
|
1768
1790
|
const names = verbose ? r.definedNames : r.definedNames.filter((n) => !(n.hidden && n.name.startsWith("_xlnm.")));
|
|
1769
1791
|
if (names.length > 0) {
|
|
1770
|
-
const shown = names.slice(0, 5).map((n) => `${n.name}${n.sheet === null ? "" : ` (sheet "${n.sheet}")`}${n.hidden ? " (hidden)" : ""} =${n.refersTo}`);
|
|
1792
|
+
const shown = names.slice(0, 5).map((n) => `${escapeControls(n.name)}${n.sheet === null ? "" : ` (sheet "${escapeControls(n.sheet)}")`}${n.hidden ? " (hidden)" : ""} =${escapeControls(n.refersTo)}`);
|
|
1771
1793
|
const more = names.length > 5 || r.definedNamesTruncated ? `, … (${r.definedNamesTruncated ? "more than " : ""}${String(names.length)} in total; see --json)` : "";
|
|
1772
1794
|
lines.push(`${out.bold("Defined names:")} ${shown.join(", ")}${more}`);
|
|
1773
1795
|
}
|
|
@@ -1775,16 +1797,16 @@ function renderReport(out, file, r, verbose) {
|
|
|
1775
1797
|
lines.push("");
|
|
1776
1798
|
const state = s.state === "visible" ? "" : ` (${s.state})`;
|
|
1777
1799
|
if (s.kind !== "worksheet") {
|
|
1778
|
-
lines.push(`${out.bold(`Sheet "${s.name}"`)}${state}: ${s.kind}, no cells`);
|
|
1800
|
+
lines.push(`${out.bold(`Sheet "${escapeControls(s.name)}"`)}${state}: ${s.kind}, no cells`);
|
|
1779
1801
|
continue;
|
|
1780
1802
|
}
|
|
1781
1803
|
if (s.usedRange === null) {
|
|
1782
|
-
lines.push(`${out.bold(`Sheet "${s.name}"`)}${state}: empty`);
|
|
1804
|
+
lines.push(`${out.bold(`Sheet "${escapeControls(s.name)}"`)}${state}: empty`);
|
|
1783
1805
|
continue;
|
|
1784
1806
|
}
|
|
1785
|
-
lines.push(`${out.bold(`Sheet "${s.name}"`)}${state}: ${s.usedRange}, ${plural(s.rowCount, "row")} × ${plural(s.columnCount, "column")}, ${plural(s.cellCount, "cell")}, ${plural(s.formulaCount, "formula")}`);
|
|
1807
|
+
lines.push(`${out.bold(`Sheet "${escapeControls(s.name)}"`)}${state}: ${s.usedRange}, ${plural(s.rowCount, "row")} × ${plural(s.columnCount, "column")}, ${plural(s.cellCount, "cell")}, ${plural(s.formulaCount, "formula")}`);
|
|
1786
1808
|
lines.push(s.headerRow ? ` Header row: ${String(s.headerRow.row)} (${s.headerRow.confidence} confidence)` : " Header row: none detected");
|
|
1787
|
-
if (s.mergedRangeCount > 0) lines.push(` Merged: ${s.mergedRanges.slice(0, 10).join(", ")}${s.mergedRangeCount > 10 ? `, … (${String(s.mergedRangeCount)} in total)` : ""}`);
|
|
1809
|
+
if (s.mergedRangeCount > 0) lines.push(` Merged: ${escapeControls(s.mergedRanges.slice(0, 10).join(", "))}${s.mergedRangeCount > 10 ? `, … (${String(s.mergedRangeCount)} in total)` : ""}`);
|
|
1788
1810
|
if (s.columns.length > 0) {
|
|
1789
1811
|
lines.push(out.table([
|
|
1790
1812
|
"Column",
|
|
@@ -1799,29 +1821,98 @@ function renderReport(out, file, r, verbose) {
|
|
|
1799
1821
|
]), " "));
|
|
1800
1822
|
if (s.columnsTruncated) lines.push(` … ${plural(s.columnCount - s.columns.length, "more column")}; use --max-columns ${String(s.columnCount)} to see them`);
|
|
1801
1823
|
}
|
|
1802
|
-
if (s.formulas.length > 0) lines.push(` Formulas: ${s.formulas.slice(0, 3).map((f) => `${f.cell} =${f.formula ?? "(shared)"}`).join(", ")}`);
|
|
1824
|
+
if (s.formulas.length > 0) lines.push(` Formulas: ${s.formulas.slice(0, 3).map((f) => `${f.cell} =${escapeControls(f.formula ?? "(shared)")}`).join(", ")}`);
|
|
1803
1825
|
if (s.richTextCellCount > 0) lines.push(` Rich text: ${plural(s.richTextCellCount, "cell")} (${s.richTextCells.slice(0, 5).join(", ")}${s.richTextCellCount > 5 ? ", …" : ""})`);
|
|
1804
1826
|
if (s.styledColumns.length > 0) lines.push(` Column styles: ${s.styledColumns.slice(0, 10).join(", ")}${s.styledColumns.length > 10 ? ", …" : ""}`);
|
|
1805
1827
|
if (s.hyperlinkCount > 0) {
|
|
1806
|
-
const shown = s.hyperlinks.slice(0, 3).map((l) => `${l.ref} → ${l.target ?? ""}${l.location === null ? "" : `${l.target === null ? "" : "#"}${l.location}`}`);
|
|
1828
|
+
const shown = s.hyperlinks.slice(0, 3).map((l) => `${l.ref} → ${escapeControls(l.target ?? "")}${l.location === null ? "" : `${l.target === null ? "" : "#"}${escapeControls(l.location)}`}`);
|
|
1807
1829
|
lines.push(` Hyperlinks: ${plural(s.hyperlinkCount, "link")} (${shown.join(", ")}${s.hyperlinkCount > 3 ? ", …" : ""})`);
|
|
1808
1830
|
}
|
|
1809
1831
|
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 ? ", …" : ""})`);
|
|
1810
1832
|
if (s.noteCount > 0) lines.push(` Notes: ${plural(s.noteCount, "note")} (${s.notes.slice(0, 5).join(", ")}${s.noteCount > 5 ? ", …" : ""})`);
|
|
1811
|
-
if (s.tables.length > 0) lines.push(` Tables: ${s.tables.slice(0, 5).map((
|
|
1833
|
+
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 ? ", …" : ""}`);
|
|
1812
1834
|
if (s.dataValidationCount > 0 || s.conditionalFormatCount > 0) {
|
|
1813
1835
|
const parts = [s.dataValidationCount > 0 ? plural(s.dataValidationCount, "data validation") : null, s.conditionalFormatCount > 0 ? plural(s.conditionalFormatCount, "conditional format rule") : null];
|
|
1814
1836
|
lines.push(` Rules: ${parts.filter((p) => p !== null).join(", ")}`);
|
|
1815
1837
|
}
|
|
1816
1838
|
if (s.protected) lines.push(" Protected: yes");
|
|
1817
1839
|
if (s.printArea !== null || s.pageBreakCount > 0) {
|
|
1818
|
-
const parts = [s.printArea === null ? null : `print area ${s.printArea.join(", ")}`, s.pageBreakCount > 0 ? plural(s.pageBreakCount, "page break") : null];
|
|
1840
|
+
const parts = [s.printArea === null ? null : `print area ${escapeControls(s.printArea.join(", "))}`, s.pageBreakCount > 0 ? plural(s.pageBreakCount, "page break") : null];
|
|
1819
1841
|
lines.push(` Printing: ${parts.filter((p) => p !== null).join(", ")}`);
|
|
1820
1842
|
}
|
|
1821
1843
|
}
|
|
1822
1844
|
if (r.warnings.length > 0) lines.push("", ...warningsBlock(out, r.warnings));
|
|
1823
1845
|
return lines.join("\n");
|
|
1824
1846
|
}
|
|
1847
|
+
// src/cli/commands/mcp.ts
|
|
1848
|
+
/** The package that holds the MCP server; xlvane does not depend on it (docs/PLAN.md section 3). */
|
|
1849
|
+
const MCP_PACKAGE = "@xlvane/mcp";
|
|
1850
|
+
/**
|
|
1851
|
+
* Whether loading failed because @xlvane/mcp itself is missing. A missing dependency of an
|
|
1852
|
+
* installed @xlvane/mcp has the same error code but names that dependency (D-I04).
|
|
1853
|
+
*/
|
|
1854
|
+
function isModuleNotFound(error) {
|
|
1855
|
+
const code = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
|
|
1856
|
+
const message = error instanceof Error ? error.message : "";
|
|
1857
|
+
return (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") && (message.includes(`'@xlvane/mcp'`) || message.includes(`"@xlvane/mcp"`));
|
|
1858
|
+
}
|
|
1859
|
+
/** Options of the xlvane command line itself, which the server does not take. */
|
|
1860
|
+
const OWN_OPTIONS = /* @__PURE__ */ new Set([
|
|
1861
|
+
"--quiet",
|
|
1862
|
+
"-q",
|
|
1863
|
+
"--verbose"
|
|
1864
|
+
]);
|
|
1865
|
+
const mcpCommand = {
|
|
1866
|
+
name: "mcp",
|
|
1867
|
+
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> and --max-response-chars <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
|
+
usage: "[server options]",
|
|
1870
|
+
positionals: {
|
|
1871
|
+
min: 0,
|
|
1872
|
+
max: Number.POSITIVE_INFINITY,
|
|
1873
|
+
names: []
|
|
1874
|
+
},
|
|
1875
|
+
passThrough: true,
|
|
1876
|
+
options: {},
|
|
1877
|
+
examples: [{
|
|
1878
|
+
command: "xlvane mcp --root ./data",
|
|
1879
|
+
description: "Serve the files in ./data, read-only"
|
|
1880
|
+
}, {
|
|
1881
|
+
command: "xlvane mcp --root ./data --allow-write",
|
|
1882
|
+
description: "Also allow writing files there"
|
|
1883
|
+
}],
|
|
1884
|
+
related: ["inspect", "read"],
|
|
1885
|
+
async run(ctx) {
|
|
1886
|
+
if (ctx.json) throw usageError("\"xlvane mcp\" does not take --json: the server uses stdout for the protocol.", "Run \"xlvane mcp --root <dir>\" without --json; the server reports its own errors as MCP results.", {
|
|
1887
|
+
command: "mcp",
|
|
1888
|
+
option: "--json"
|
|
1889
|
+
});
|
|
1890
|
+
let mod;
|
|
1891
|
+
try {
|
|
1892
|
+
mod = await ctx.io.importModule(MCP_PACKAGE);
|
|
1893
|
+
} catch (error) {
|
|
1894
|
+
if (!isModuleNotFound(error)) throw error;
|
|
1895
|
+
throw new XlvError("XLV_E_NOT_INSTALLED", `The MCP server is in the separate package ${MCP_PACKAGE}, which is not installed.`, {
|
|
1896
|
+
hint: `Install it next to xlvane with "npm install ${MCP_PACKAGE}", then run "xlvane mcp" again.`,
|
|
1897
|
+
details: { package: MCP_PACKAGE },
|
|
1898
|
+
cause: error
|
|
1899
|
+
});
|
|
1900
|
+
}
|
|
1901
|
+
if (typeof mod.runCli !== "function") throw new XlvError("XLV_E_NOT_SUPPORTED", `The installed ${MCP_PACKAGE} does not contain the MCP server yet.`, {
|
|
1902
|
+
hint: `Update ${MCP_PACKAGE} to a version that provides the server.`,
|
|
1903
|
+
details: {
|
|
1904
|
+
package: MCP_PACKAGE,
|
|
1905
|
+
feature: "runCli"
|
|
1906
|
+
}
|
|
1907
|
+
});
|
|
1908
|
+
const code = await mod.runCli(ctx.argv.filter((a) => !OWN_OPTIONS.has(a)));
|
|
1909
|
+
return {
|
|
1910
|
+
result: { exitCode: code },
|
|
1911
|
+
exitCode: code === 0 || code === 1 || code === 2 ? code : 3,
|
|
1912
|
+
render: () => ""
|
|
1913
|
+
};
|
|
1914
|
+
}
|
|
1915
|
+
};
|
|
1825
1916
|
// src/cli/commands/migrate.ts
|
|
1826
1917
|
/** The dependency value that installs @xlvane/compat under the name exceljs (npm alias). */
|
|
1827
1918
|
const ALIAS = "npm:@xlvane/compat@^1";
|
|
@@ -1866,10 +1957,11 @@ async function sourceFiles(root) {
|
|
|
1866
1957
|
const path = join(dir, name);
|
|
1867
1958
|
let info;
|
|
1868
1959
|
try {
|
|
1869
|
-
info = await
|
|
1960
|
+
info = await lstat(path);
|
|
1870
1961
|
} catch {
|
|
1871
1962
|
continue;
|
|
1872
1963
|
}
|
|
1964
|
+
if (info.isSymbolicLink()) continue;
|
|
1873
1965
|
if (info.isDirectory()) await walk(path);
|
|
1874
1966
|
else if (info.isFile() && SOURCE.test(name) && info.size <= MAX_FILE_BYTES) files.push(path);
|
|
1875
1967
|
}
|
|
@@ -1898,6 +1990,75 @@ function scanSource(file, text) {
|
|
|
1898
1990
|
});
|
|
1899
1991
|
return findings;
|
|
1900
1992
|
}
|
|
1993
|
+
/**
|
|
1994
|
+
* Offsets `[start, end)` of the string value (with its quotes) at `path` in valid JSON text, the
|
|
1995
|
+
* one JSON.parse reads when keys repeat, or `null`. Keys are compared after decoding their
|
|
1996
|
+
* escapes. Values off the path are skipped without recursion.
|
|
1997
|
+
*/
|
|
1998
|
+
function stringValueSpan(text, path) {
|
|
1999
|
+
let i = 0;
|
|
2000
|
+
const space = () => {
|
|
2001
|
+
while (i < text.length && " \r\n".includes(text[i])) i++;
|
|
2002
|
+
};
|
|
2003
|
+
/** Skips a string starting at i (on its opening quote); returns its raw text. */
|
|
2004
|
+
const string = () => {
|
|
2005
|
+
const start = i++;
|
|
2006
|
+
while (text[i] !== "\"") i += text[i] === "\\" ? 2 : 1;
|
|
2007
|
+
i++;
|
|
2008
|
+
return text.slice(start, i);
|
|
2009
|
+
};
|
|
2010
|
+
/** Skips any value starting at i, iteratively. */
|
|
2011
|
+
const skip = () => {
|
|
2012
|
+
let depth = 0;
|
|
2013
|
+
do {
|
|
2014
|
+
const c = text[i];
|
|
2015
|
+
if (c === "\"") string();
|
|
2016
|
+
else {
|
|
2017
|
+
if (c === "{" || c === "[") depth++;
|
|
2018
|
+
else if (c === "}" || c === "]") depth--;
|
|
2019
|
+
i++;
|
|
2020
|
+
if (depth === 0 && c !== "{" && c !== "[") while (i < text.length && !",]} \r\n".includes(text[i])) i++;
|
|
2021
|
+
}
|
|
2022
|
+
} while (depth > 0 && i < text.length);
|
|
2023
|
+
};
|
|
2024
|
+
const value = (level) => {
|
|
2025
|
+
space();
|
|
2026
|
+
if (level === path.length) {
|
|
2027
|
+
if (text[i] !== "\"") {
|
|
2028
|
+
skip();
|
|
2029
|
+
return null;
|
|
2030
|
+
}
|
|
2031
|
+
const start = i;
|
|
2032
|
+
string();
|
|
2033
|
+
return [start, i];
|
|
2034
|
+
}
|
|
2035
|
+
if (text[i] !== "{") {
|
|
2036
|
+
skip();
|
|
2037
|
+
return null;
|
|
2038
|
+
}
|
|
2039
|
+
i++;
|
|
2040
|
+
let found = null;
|
|
2041
|
+
space();
|
|
2042
|
+
if (text[i] === "}") {
|
|
2043
|
+
i++;
|
|
2044
|
+
return null;
|
|
2045
|
+
}
|
|
2046
|
+
for (;;) {
|
|
2047
|
+
space();
|
|
2048
|
+
const key = JSON.parse(string());
|
|
2049
|
+
space();
|
|
2050
|
+
i++;
|
|
2051
|
+
if (key === path[level]) found = value(level + 1);
|
|
2052
|
+
else {
|
|
2053
|
+
space();
|
|
2054
|
+
skip();
|
|
2055
|
+
}
|
|
2056
|
+
space();
|
|
2057
|
+
if (text[i++] === "}") return found;
|
|
2058
|
+
}
|
|
2059
|
+
};
|
|
2060
|
+
return value(0);
|
|
2061
|
+
}
|
|
1901
2062
|
function jsonError(path, cause) {
|
|
1902
2063
|
return new XlvError("XLV_E_JSON_INVALID", `"${path}" is not a valid package.json.`, {
|
|
1903
2064
|
hint: "Fix the JSON syntax of package.json and run the command again.",
|
|
@@ -1945,11 +2106,14 @@ async function run$1(ctx) {
|
|
|
1945
2106
|
}
|
|
1946
2107
|
let write = null;
|
|
1947
2108
|
if (changes.length > 0 && flag(ctx.args, "force")) {
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
2109
|
+
const spans = changes.map((c) => stringValueSpan(text, [c.section, "exceljs"])).filter((s) => s !== null);
|
|
2110
|
+
let updated = "";
|
|
2111
|
+
let at = 0;
|
|
2112
|
+
for (const [start, end] of spans.sort((a, b) => a[0] - b[0])) {
|
|
2113
|
+
updated += `${text.slice(at, start)}"${ALIAS}"`;
|
|
2114
|
+
at = end;
|
|
1952
2115
|
}
|
|
2116
|
+
updated += text.slice(at);
|
|
1953
2117
|
write = await writeOutput(ctx, pkgPath, updated, {
|
|
1954
2118
|
force: true,
|
|
1955
2119
|
dryRun: flag(ctx.args, "dry-run")
|
|
@@ -2326,7 +2490,8 @@ const COMMANDS = [
|
|
|
2326
2490
|
};
|
|
2327
2491
|
}
|
|
2328
2492
|
},
|
|
2329
|
-
migrateCommand
|
|
2493
|
+
migrateCommand,
|
|
2494
|
+
mcpCommand
|
|
2330
2495
|
];
|
|
2331
2496
|
/** Exit codes (docs/cli-json.md). */
|
|
2332
2497
|
const EXIT = {
|
|
@@ -2350,14 +2515,14 @@ async function run(argv, io) {
|
|
|
2350
2515
|
const err = new Output(useColor(io.env, io.stderrIsTTY), io.stderrIsTTY ? width : 100);
|
|
2351
2516
|
const stdout = (text) => io.stdout(terminalSafe(text, out.color));
|
|
2352
2517
|
const stderr = (text) => io.stderr(terminalSafe(text, err.color));
|
|
2353
|
-
const toJson = (value) => `${io.stdoutIsTTY ? JSON.stringify(value, null, 2) : JSON.stringify(value)}\n`;
|
|
2518
|
+
const toJson = (value) => `${jsonSafe(io.stdoutIsTTY ? JSON.stringify(value, null, 2) : JSON.stringify(value))}\n`;
|
|
2354
2519
|
const json = optionArgs(argv).includes("--json");
|
|
2355
2520
|
const [first, ...rest] = argv;
|
|
2356
2521
|
let spec;
|
|
2357
2522
|
const state = { lastFile: null };
|
|
2358
2523
|
try {
|
|
2359
2524
|
if (first === void 0 || first === "--json" && rest.length === 0) {
|
|
2360
|
-
|
|
2525
|
+
stdout(welcome(out, io.version, COMMANDS));
|
|
2361
2526
|
return EXIT.ok;
|
|
2362
2527
|
}
|
|
2363
2528
|
if (first === "--version" || first === "-v") {
|
|
@@ -2367,7 +2532,7 @@ async function run(argv, io) {
|
|
|
2367
2532
|
if (first === "help" || first === "--help" || first === "-h") {
|
|
2368
2533
|
const topic = rest.find((a) => !a.startsWith("-"));
|
|
2369
2534
|
if (topic === void 0) {
|
|
2370
|
-
|
|
2535
|
+
stdout(welcome(out, io.version, COMMANDS));
|
|
2371
2536
|
return EXIT.ok;
|
|
2372
2537
|
}
|
|
2373
2538
|
stdout(commandHelp(out, findCommand(topic)));
|
|
@@ -2379,8 +2544,11 @@ async function run(argv, io) {
|
|
|
2379
2544
|
stdout(commandHelp(out, spec));
|
|
2380
2545
|
return EXIT.ok;
|
|
2381
2546
|
}
|
|
2382
|
-
const args =
|
|
2383
|
-
|
|
2547
|
+
const args = spec.passThrough ? {
|
|
2548
|
+
positionals: rest,
|
|
2549
|
+
values: {}
|
|
2550
|
+
} : parseCommandArgs(spec, rest);
|
|
2551
|
+
if (!spec.passThrough) checkPositionals(spec, args);
|
|
2384
2552
|
const quiet = args.values["quiet"] === true;
|
|
2385
2553
|
const ctx = {
|
|
2386
2554
|
io,
|
|
@@ -2510,6 +2678,7 @@ function suggestCommand(error, first, rest, spec) {
|
|
|
2510
2678
|
const at = rest.findIndex((a, i) => a === path && rest[i - 1] !== "-o" && rest[i - 1] !== "--output");
|
|
2511
2679
|
if (at >= 0) return commandLine([spec.name, ...rest.map((a, i) => i === at ? suggestions[0] : a)]);
|
|
2512
2680
|
}
|
|
2681
|
+
if (error.code === "XLV_E_NOT_INSTALLED" && typeof d["package"] === "string") return `npm install ${d["package"]}`;
|
|
2513
2682
|
return null;
|
|
2514
2683
|
}
|
|
2515
2684
|
/** Where the problem is, as one line; `null` when the message already says it all. */
|
|
@@ -2542,6 +2711,15 @@ function formatError(err, error, tryCommand, verbose = false) {
|
|
|
2542
2711
|
""
|
|
2543
2712
|
].join("\n");
|
|
2544
2713
|
}
|
|
2714
|
+
/**
|
|
2715
|
+
* JSON text with DEL, the C1 controls (which include the 8-bit CSI U+009B), the bidirectional
|
|
2716
|
+
* formatting characters and U+2028/U+2029 written as `\uXXXX` escapes. JSON.stringify already
|
|
2717
|
+
* escapes C0 controls; these characters can occur only inside strings, so the escaped text is
|
|
2718
|
+
* valid JSON with the same value, and a terminal showing it does not act on them (D-H01).
|
|
2719
|
+
*/
|
|
2720
|
+
function jsonSafe(json) {
|
|
2721
|
+
return json.replace(/[\u007f-\u009f\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069\u2028\u2029]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
2722
|
+
}
|
|
2545
2723
|
// src/cli/bin.ts
|
|
2546
2724
|
function version() {
|
|
2547
2725
|
try {
|
|
@@ -2563,6 +2741,7 @@ run(process.argv.slice(2), {
|
|
|
2563
2741
|
stdoutIsTTY: process.stdout.isTTY === true,
|
|
2564
2742
|
stderrIsTTY: process.stderr.isTTY === true,
|
|
2565
2743
|
columns: process.stdout.columns,
|
|
2744
|
+
importModule: (specifier) => import(specifier),
|
|
2566
2745
|
version: version()
|
|
2567
2746
|
}).then((code) => {
|
|
2568
2747
|
process.exitCode = code;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as InspectOptions, $n as verifyPassword, $t as DataValidationOperator, A as toJSON, An as Hyperlink, At as Workbook, B as FormulaResult, Bn as PrintErrors, Bt as CfValue, C as TableRow, Cn as ImageAnchorInput, Ct as Sheet, D as fromJSON, Dn as SheetImage, Dt as SheetState, E as WriteTableOptions, En as ImagePositionInput, Et as SheetProperties, F as CellInput, Fn as PageOrder, Ft as TableDefinitionColumn, G as SheetPropertiesOptions, Gn as SheetProtectionOptions, Gt as ConditionalFormatRule, H as NewSheet, Hn as PrintTitles, Ht as CfValueType, I as ColumnOptions, In as PageOrientation, It as TableInput, J as WriteWorkbookOptions, Jn as HashPasswordOptions, Jt as DataBarAxisPosition, K as SheetViewOptions, Kn as WorkbookProtection, Kt as ConditionalFormatRuleInput, L as CreateWorkbookOptions, Ln as PageSetup, Lt as TableStyleOptions, M as StandardSchemaV1, Mn as HeaderFooter, Mt as WorkbookView, N as AddSheetOptions, Nn as PageBreaks, Nt as TableColumnInput, O as readTable, On as WorkbookImage, Ot as SheetView, P as CalculationOptions, Pn as PageMargins, Pt as TableDefinition, Q as InspectDefinedName, Qn as legacyPasswordHash, Qt as DataValidationImeMode, R as ERROR_VALUES, Rn as PageSetupOptions, Rt as TotalsRowFunction, S as TableResult, Sn as ImageAnchor, St as RowInfo, T as ToJSONOptions, Tn as ImageOptions, Tt as SheetLayout, U as NewWorkbook, Un as SheetProtection, Ut as ConditionalFormat, V as FreezeOptions, Vn as PrintOptions, Vt as CfValueInput, W as RowOptions, Wn as SheetProtectionFlags, Wt as ConditionalFormatOperator, X as writeWorkbook, Xn as PasswordVerifier, Xt as DataValidation, Y as createWorkbook, Yn as PasswordHash, Yt as DataBarDirection, Z as InspectColumn, Zn as hashPassword, Zt as DataValidationErrorStyle, _ as JsonTable, _n as ReadingOrder, _r as DefinedNameOptions, _t as DateSystem, a as WriteWorkbookStreamOptions, an as BorderEdge, ar as XlvDetails, at as ReadOptions, b as TableColumnSpec, bn as VerticalAlignment, bt as FrozenPanes, c as OpenWorkbookStreamOptions, cn as CellStyle, cr as XlvErrorInit, ct as NoteBox, d as StreamRow, dn as FontScheme, dr as XlvWarning, dt as NoteMargins, en as DataValidationRule, er as ShaAlgorithm, et as InspectReport, f as StreamSheetInfo, fn as FontStyle, fr as XlvWarningCode, ft as Cell, g as readTableStream, gn as ProtectionStyle, gr as DefinedName, gt as Conformance, h as openWorkbookStream, hn as PatternType, hr as ReadLimits, ht as ColumnInfo, i as WriteTableStreamOptions, in as AlignmentStyle, ir as XlvDetailValue, it as inspect, j as writeTable, jn as HyperlinkTarget, jt as WorkbookFormat, k as readTableAsync, kn as imageFormat, kt as SplitPanes, l as ReadTableStreamOptions, ln as Color, lr as XlvErrorJSON, lt as NoteBoxInput, m as WorkbookStream, mn as HorizontalAlignment, mr as DEFAULT_READ_LIMITS, mt as CellValue, n as StreamRowInput, nn as IconSetType, nr as ERROR_CODES, nt as InspectTypeCounts, o as writeTableStream, on as BorderLineStyle, or as XlvError, ot as readWorkbook, p as TableStreamItem, pn as GradientFill, pr as isXlvError, pt as CellType, q as WorkbookViewOptions, qn as WorkbookProtectionOptions, qt as ConditionalFormatType, r as StreamSheetSpec, rn as TimePeriod, rr as WARNING_CODES, rt as InspectValue, s as writeWorkbookStream, sn as BorderStyle, sr as XlvErrorCode, st as Note, t as StreamDefinedName, tn as DataValidationType, tr as WorkbookProperties, tt as InspectSheet, u as STREAM_LIMIT_DEFAULTS, un as FillStyle, ur as XlvLocation, ut as NoteInput, v as ReadTableOptions, vn as UnderlineStyle, vt as Formula, w as TableValue, wn as ImageFormat, wt as SheetKind, x as TableIssue, xn as AnchorMarker, xt as RichTextRun, y as TableColumn, yn as VerticalAlignRun, yt as FormulaKind, z as ErrorValue, zn as PrintCellComments, zt as CfIcon } from "./stream-write-
|
|
1
|
+
import { $ as InspectOptions, $n as verifyPassword, $t as DataValidationOperator, A as toJSON, An as Hyperlink, At as Workbook, B as FormulaResult, Bn as PrintErrors, Bt as CfValue, C as TableRow, Cn as ImageAnchorInput, Ct as Sheet, D as fromJSON, Dn as SheetImage, Dt as SheetState, E as WriteTableOptions, En as ImagePositionInput, Et as SheetProperties, F as CellInput, Fn as PageOrder, Ft as TableDefinitionColumn, G as SheetPropertiesOptions, Gn as SheetProtectionOptions, Gt as ConditionalFormatRule, H as NewSheet, Hn as PrintTitles, Ht as CfValueType, I as ColumnOptions, In as PageOrientation, It as TableInput, J as WriteWorkbookOptions, Jn as HashPasswordOptions, Jt as DataBarAxisPosition, K as SheetViewOptions, Kn as WorkbookProtection, Kt as ConditionalFormatRuleInput, L as CreateWorkbookOptions, Ln as PageSetup, Lt as TableStyleOptions, M as StandardSchemaV1, Mn as HeaderFooter, Mt as WorkbookView, N as AddSheetOptions, Nn as PageBreaks, Nt as TableColumnInput, O as readTable, On as WorkbookImage, Ot as SheetView, P as CalculationOptions, Pn as PageMargins, Pt as TableDefinition, Q as InspectDefinedName, Qn as legacyPasswordHash, Qt as DataValidationImeMode, R as ERROR_VALUES, Rn as PageSetupOptions, Rt as TotalsRowFunction, S as TableResult, Sn as ImageAnchor, St as RowInfo, T as ToJSONOptions, Tn as ImageOptions, Tt as SheetLayout, U as NewWorkbook, Un as SheetProtection, Ut as ConditionalFormat, V as FreezeOptions, Vn as PrintOptions, Vt as CfValueInput, W as RowOptions, Wn as SheetProtectionFlags, Wt as ConditionalFormatOperator, X as writeWorkbook, Xn as PasswordVerifier, Xt as DataValidation, Y as createWorkbook, Yn as PasswordHash, Yt as DataBarDirection, Z as InspectColumn, Zn as hashPassword, Zt as DataValidationErrorStyle, _ as JsonTable, _n as ReadingOrder, _r as DefinedNameOptions, _t as DateSystem, a as WriteWorkbookStreamOptions, an as BorderEdge, ar as XlvDetails, at as ReadOptions, b as TableColumnSpec, bn as VerticalAlignment, bt as FrozenPanes, c as OpenWorkbookStreamOptions, cn as CellStyle, cr as XlvErrorInit, ct as NoteBox, d as StreamRow, dn as FontScheme, dr as XlvWarning, dt as NoteMargins, en as DataValidationRule, er as ShaAlgorithm, et as InspectReport, f as StreamSheetInfo, fn as FontStyle, fr as XlvWarningCode, ft as Cell, g as readTableStream, gn as ProtectionStyle, gr as DefinedName, gt as Conformance, h as openWorkbookStream, hn as PatternType, hr as ReadLimits, ht as ColumnInfo, i as WriteTableStreamOptions, in as AlignmentStyle, ir as XlvDetailValue, it as inspect, j as writeTable, jn as HyperlinkTarget, jt as WorkbookFormat, k as readTableAsync, kn as imageFormat, kt as SplitPanes, l as ReadTableStreamOptions, ln as Color, lr as XlvErrorJSON, lt as NoteBoxInput, m as WorkbookStream, mn as HorizontalAlignment, mr as DEFAULT_READ_LIMITS, mt as CellValue, n as StreamRowInput, nn as IconSetType, nr as ERROR_CODES, nt as InspectTypeCounts, o as writeTableStream, on as BorderLineStyle, or as XlvError, ot as readWorkbook, p as TableStreamItem, pn as GradientFill, pr as isXlvError, pt as CellType, q as WorkbookViewOptions, qn as WorkbookProtectionOptions, qt as ConditionalFormatType, r as StreamSheetSpec, rn as TimePeriod, rr as WARNING_CODES, rt as InspectValue, s as writeWorkbookStream, sn as BorderStyle, sr as XlvErrorCode, st as Note, t as StreamDefinedName, tn as DataValidationType, tr as WorkbookProperties, tt as InspectSheet, u as STREAM_LIMIT_DEFAULTS, un as FillStyle, ur as XlvLocation, ut as NoteInput, v as ReadTableOptions, vn as UnderlineStyle, vt as Formula, w as TableValue, wn as ImageFormat, wt as SheetKind, x as TableIssue, xn as AnchorMarker, xt as RichTextRun, y as TableColumn, yn as VerticalAlignRun, yt as FormulaKind, z as ErrorValue, zn as PrintCellComments, zt as CfIcon } from "./stream-write-BVaFkgwO.js";
|
|
2
2
|
/**
|
|
3
3
|
* Options for {@link toCSV}.
|
|
4
4
|
*
|
|
@@ -21,9 +21,10 @@ interface ToCSVOptions {
|
|
|
21
21
|
/** Start with a UTF-8 byte order mark, which helps Excel detect UTF-8. Default `false`. */
|
|
22
22
|
readonly bom?: boolean;
|
|
23
23
|
/**
|
|
24
|
-
* `"escape"` (default): text cells that start with `=`, `+`, `-`, `@`,
|
|
25
|
-
*
|
|
26
|
-
* text instead of running them as formulas (OWASP CSV
|
|
24
|
+
* `"escape"` (default): text cells that start with `=`, `+`, `-`, `@`, their full-width or small
|
|
25
|
+
* forms, or a control character (tab, CR, LF, NUL, …) get a leading `'` and quotes, so
|
|
26
|
+
* spreadsheet applications show them as text instead of running them as formulas (OWASP CSV
|
|
27
|
+
* Injection). `"off"`: write them as is.
|
|
27
28
|
*/
|
|
28
29
|
readonly formulaInjection?: "escape" | "off";
|
|
29
30
|
/** Read limits for byte input; see `ReadLimits`. */
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Dn as
|
|
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 { Dn as WARNING_CODES, En as ERROR_CODES, Gt as hashPassword, Kt as legacyPasswordHash, On as XlvError, Qt as imageFormat, Rt as DEFAULT_READ_LIMITS, b as readWorkbook, c as NewSheet, f as createWorkbook, g as inspect, kn as isXlvError, l as NewWorkbook, m as writeWorkbook, qt as verifyPassword, s as ERROR_VALUES, z as shiftFormula } from "./options-Ba1iWD-Z.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-IWKIxHt9.js";
|
|
3
|
+
import { a as readTableStream, i as openWorkbookStream, n as writeWorkbookStream, r as STREAM_LIMIT_DEFAULTS, t as writeTableStream } from "./stream-write-BGuUjlXo.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 {
|
|
1
|
+
import { Bt as resolveLimits, On as XlvError } from "./options-Ba1iWD-Z.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. */
|
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-BVaFkgwO.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 {
|
|
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, On as XlvError, b as readWorkbook, g as inspect, m as writeWorkbook } from "./options-Ba1iWD-Z.js";
|
|
2
|
+
import { i as readInput, n as describePath, r as fileError, t as checkFile } from "./node-files-CDb6TyZb.js";
|
|
3
|
+
import { i as openWorkbookStream, n as writeWorkbookStream, r as STREAM_LIMIT_DEFAULTS } from "./stream-write-BGuUjlXo.js";
|
|
4
4
|
import { openAsBlob } from "node:fs";
|
|
5
5
|
import { open, unlink, writeFile } from "node:fs/promises";
|
|
6
6
|
// src/node.ts
|