@vectojs/numera-cli 0.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/CHANGELOG.md +18 -0
- package/LICENSE +21 -0
- package/README.md +52 -0
- package/dist/CliError.d.ts +7 -0
- package/dist/CliError.d.ts.map +1 -0
- package/dist/a1.d.ts +7 -0
- package/dist/a1.d.ts.map +1 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +269 -0
- package/dist/cli.mjs +248 -0
- package/dist/commands.d.ts +23 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.mjs +0 -0
- package/dist/workbookFiles.d.ts +12 -0
- package/dist/workbookFiles.d.ts.map +1 -0
- package/package.json +57 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d7e3400: Add safe local workbook inspect, get, and explicit-output set commands for
|
|
8
|
+
Numera JSON, CSV, and XLSX files.
|
|
9
|
+
|
|
10
|
+
All notable changes to this project are documented in this file.
|
|
11
|
+
|
|
12
|
+
## Unreleased
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Safe local `inspect`, `get`, and explicit-output `set` commands for Numera
|
|
17
|
+
JSON, CSV, and XLSX workbooks.
|
|
18
|
+
- Atomic same-directory output replacement and stable command diagnostics.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Xuepoo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# @vectojs/numera-cli
|
|
2
|
+
|
|
3
|
+
> Safe, local-first workbook automation for Numera.
|
|
4
|
+
|
|
5
|
+
`@vectojs/numera-cli` is the command-line boundary for inspecting and editing
|
|
6
|
+
offline Numera workbooks. It uses the published Core document model and guarded
|
|
7
|
+
XLSX adapter; it does not start a browser, render a UI, contact a service, or
|
|
8
|
+
perform an implicit in-place write.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install --global @vectojs/numera-cli
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Commands
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
numera inspect budget.xlsx --json
|
|
20
|
+
numera get budget.xlsx "Sheet 1" A1:C3 --json
|
|
21
|
+
numera set budget.xlsx "Sheet 1" B2 --number 42 --output budget-edited.xlsx
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`set` always requires a distinct output path. A successful command writes an
|
|
25
|
+
atomically replaced output file, leaving the input untouched.
|
|
26
|
+
|
|
27
|
+
All command output is JSON so shell scripts and future agent tools can consume
|
|
28
|
+
it without screen scraping. `get` accepts either one A1 address or a normalized
|
|
29
|
+
range such as `A1:C3`.
|
|
30
|
+
|
|
31
|
+
`set` accepts exactly one explicit value mode: `--text`, `--number`,
|
|
32
|
+
`--boolean true|false`, or `--formula`. Formula source gets a leading `=` when
|
|
33
|
+
needed; no input type is inferred from a text argument.
|
|
34
|
+
|
|
35
|
+
## Formats
|
|
36
|
+
|
|
37
|
+
- `.xlsx` reads and writes through `@vectojs/numera-xlsx`.
|
|
38
|
+
- `.numera.json` uses Numera Core’s versioned snapshot codec.
|
|
39
|
+
- `.csv` contains one selected worksheet and cannot represent workbook-wide
|
|
40
|
+
formatting or multiple sheets.
|
|
41
|
+
|
|
42
|
+
## Verify
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
bun install
|
|
46
|
+
bun run verify
|
|
47
|
+
npm pack --dry-run
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## License
|
|
51
|
+
|
|
52
|
+
[MIT](./LICENSE) © 2026 Xuepoo
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type CliErrorCode = "INVALID_ARGUMENT" | "INPUT_FORMAT_UNSUPPORTED" | "OUTPUT_FORMAT_UNSUPPORTED" | "SHEET_NOT_FOUND" | "RANGE_OUT_OF_BOUNDS" | "OUTPUT_EQUALS_INPUT";
|
|
2
|
+
/** Stable command diagnostic that is safe to display to automation callers. */
|
|
3
|
+
export declare class CliError extends Error {
|
|
4
|
+
readonly code: CliErrorCode;
|
|
5
|
+
constructor(code: CliErrorCode, message: string);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=CliError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CliError.d.ts","sourceRoot":"","sources":["../src/CliError.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GACpB,kBAAkB,GAClB,0BAA0B,GAC1B,2BAA2B,GAC3B,iBAAiB,GACjB,qBAAqB,GACrB,qBAAqB,CAAC;AAE1B,+EAA+E;AAC/E,qBAAa,QAAS,SAAQ,KAAK;IAE/B,QAAQ,CAAC,IAAI,EAAE,YAAY;IAD7B,YACW,IAAI,EAAE,YAAY,EAC3B,OAAO,EAAE,MAAM,EAIhB;CACF"}
|
package/dist/a1.d.ts
ADDED
package/dist/a1.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a1.d.ts","sourceRoot":"","sources":["../src/a1.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAItE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAK7E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAanD"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
interface ParsedSetValue {
|
|
3
|
+
raw: string;
|
|
4
|
+
output: string;
|
|
5
|
+
}
|
|
6
|
+
declare function run(arguments_: string[]): Promise<string>;
|
|
7
|
+
declare function parseSetValue(arguments_: string[]): ParsedSetValue;
|
|
8
|
+
export { parseSetValue, run };
|
|
9
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAMA,UAAU,cAAc;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,iBAAe,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BxD;AAED,iBAAS,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,cAAc,CA2B3D;AAiBD,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,CAAC"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/cli.ts
|
|
22
|
+
var cli_exports = {};
|
|
23
|
+
__export(cli_exports, {
|
|
24
|
+
parseSetValue: () => parseSetValue,
|
|
25
|
+
run: () => run
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(cli_exports);
|
|
28
|
+
|
|
29
|
+
// src/CliError.ts
|
|
30
|
+
var CliError = class extends Error {
|
|
31
|
+
constructor(code, message) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.code = code;
|
|
34
|
+
this.name = "CliError";
|
|
35
|
+
}
|
|
36
|
+
code;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// src/commands.ts
|
|
40
|
+
var import_numera_core3 = require("@vectojs/numera-core");
|
|
41
|
+
var import_node_path = require("path");
|
|
42
|
+
|
|
43
|
+
// src/a1.ts
|
|
44
|
+
var import_numera_core = require("@vectojs/numera-core");
|
|
45
|
+
function parseCellAddress(source) {
|
|
46
|
+
const cell = (0, import_numera_core.parseA1)(source.toUpperCase());
|
|
47
|
+
if (!cell)
|
|
48
|
+
throw new CliError("INVALID_ARGUMENT", `Invalid cell address: ${source}`);
|
|
49
|
+
return cell;
|
|
50
|
+
}
|
|
51
|
+
function parseCellRange(source) {
|
|
52
|
+
const normalized = source.toUpperCase();
|
|
53
|
+
const range = normalized.includes(":") ? (0, import_numera_core.parseRange)(normalized) : (() => {
|
|
54
|
+
const cell = (0, import_numera_core.parseA1)(normalized);
|
|
55
|
+
return cell ? { r1: cell.row, c1: cell.col, r2: cell.row, c2: cell.col } : null;
|
|
56
|
+
})();
|
|
57
|
+
if (!range)
|
|
58
|
+
throw new CliError("INVALID_ARGUMENT", `Invalid cell range: ${source}`);
|
|
59
|
+
return range;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/workbookFiles.ts
|
|
63
|
+
var import_numera_core2 = require("@vectojs/numera-core");
|
|
64
|
+
var import_numera_xlsx = require("@vectojs/numera-xlsx");
|
|
65
|
+
var import_promises = require("fs/promises");
|
|
66
|
+
function detectWorkbookFormat(path) {
|
|
67
|
+
const normalized = path.toLowerCase();
|
|
68
|
+
if (normalized.endsWith(".xlsx")) return "xlsx";
|
|
69
|
+
if (normalized.endsWith(".csv")) return "csv";
|
|
70
|
+
if (normalized.endsWith(".numera.json") || normalized.endsWith(".json"))
|
|
71
|
+
return "json";
|
|
72
|
+
throw new CliError(
|
|
73
|
+
"INPUT_FORMAT_UNSUPPORTED",
|
|
74
|
+
`Unsupported input format: ${path}`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
async function loadWorkbook(path) {
|
|
78
|
+
const format = detectWorkbookFormat(path);
|
|
79
|
+
const file = Bun.file(path);
|
|
80
|
+
if (!await file.exists())
|
|
81
|
+
throw new CliError(
|
|
82
|
+
"INVALID_ARGUMENT",
|
|
83
|
+
`Input file does not exist: ${path}`
|
|
84
|
+
);
|
|
85
|
+
if (format === "xlsx")
|
|
86
|
+
return {
|
|
87
|
+
format,
|
|
88
|
+
workbook: await (0, import_numera_xlsx.decodeXlsx)(new Uint8Array(await file.arrayBuffer()))
|
|
89
|
+
};
|
|
90
|
+
const source = await file.text();
|
|
91
|
+
if (format === "json") return { format, workbook: (0, import_numera_core2.parseWorkbookJson)(source) };
|
|
92
|
+
const workbook = new import_numera_core2.Workbook();
|
|
93
|
+
const sheet = workbook.activeSheet.model;
|
|
94
|
+
for (const write of (0, import_numera_core2.fromCsv)(
|
|
95
|
+
source,
|
|
96
|
+
{ row: 0, col: 0 },
|
|
97
|
+
{ rows: sheet.rows, cols: sheet.cols }
|
|
98
|
+
))
|
|
99
|
+
sheet.setCell(write.row, write.col, write.raw);
|
|
100
|
+
return { format, workbook };
|
|
101
|
+
}
|
|
102
|
+
async function serializeWorkbook(workbook, outputPath, sheetName) {
|
|
103
|
+
const format = detectOutputFormat(outputPath);
|
|
104
|
+
if (format === "xlsx") return (0, import_numera_xlsx.encodeXlsx)(workbook);
|
|
105
|
+
if (format === "json") return (0, import_numera_core2.toWorkbookJson)(workbook);
|
|
106
|
+
const sheet = sheetName ? findSheet(workbook, sheetName).model : workbook.activeSheet.model;
|
|
107
|
+
const used = sheet.getUsedRange();
|
|
108
|
+
const range = used ?? { r1: 0, c1: 0, r2: 0, c2: 0 };
|
|
109
|
+
return (0, import_numera_core2.toCsv)(sheet, range);
|
|
110
|
+
}
|
|
111
|
+
function findSheet(workbook, name) {
|
|
112
|
+
const sheet = workbook.sheets.find((candidate) => candidate.name === name);
|
|
113
|
+
if (!sheet) throw new CliError("SHEET_NOT_FOUND", `Sheet not found: ${name}`);
|
|
114
|
+
return sheet;
|
|
115
|
+
}
|
|
116
|
+
async function writeOutputAtomically(outputPath, content) {
|
|
117
|
+
const temporaryPath = `${outputPath}.numera-${crypto.randomUUID()}.tmp`;
|
|
118
|
+
try {
|
|
119
|
+
await Bun.write(temporaryPath, content);
|
|
120
|
+
await (0, import_promises.rename)(temporaryPath, outputPath);
|
|
121
|
+
} finally {
|
|
122
|
+
await (0, import_promises.rm)(temporaryPath, { force: true });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function detectOutputFormat(path) {
|
|
126
|
+
try {
|
|
127
|
+
return detectWorkbookFormat(path);
|
|
128
|
+
} catch {
|
|
129
|
+
throw new CliError(
|
|
130
|
+
"OUTPUT_FORMAT_UNSUPPORTED",
|
|
131
|
+
`Unsupported output format: ${path}`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// src/commands.ts
|
|
137
|
+
function inspectWorkbook(workbook, format) {
|
|
138
|
+
return {
|
|
139
|
+
format,
|
|
140
|
+
activeSheet: workbook.activeSheet.name,
|
|
141
|
+
sheets: workbook.sheets.map((sheet) => ({
|
|
142
|
+
name: sheet.name,
|
|
143
|
+
populatedCells: sheet.model.cellCount,
|
|
144
|
+
rows: sheet.model.rows,
|
|
145
|
+
cols: sheet.model.cols
|
|
146
|
+
}))
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function getRange(workbook, sheetName, sourceRange) {
|
|
150
|
+
const sheet = findSheet(workbook, sheetName).model;
|
|
151
|
+
const range = parseCellRange(sourceRange);
|
|
152
|
+
if (range.r2 >= sheet.rows || range.c2 >= sheet.cols)
|
|
153
|
+
throw new CliError(
|
|
154
|
+
"RANGE_OUT_OF_BOUNDS",
|
|
155
|
+
`Range is outside sheet bounds: ${sourceRange}`
|
|
156
|
+
);
|
|
157
|
+
const cells = [];
|
|
158
|
+
for (let row = range.r1; row <= range.r2; row++) {
|
|
159
|
+
for (let col = range.c1; col <= range.c2; col++) {
|
|
160
|
+
cells.push({
|
|
161
|
+
address: (0, import_numera_core3.toA1)({ row, col }),
|
|
162
|
+
raw: sheet.getRaw(row, col),
|
|
163
|
+
display: sheet.getDisplay(row, col)
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return { sheet: sheetName, range: sourceRange.toUpperCase(), cells };
|
|
168
|
+
}
|
|
169
|
+
async function setCellAndWrite(workbook, sourcePath, sheetName, address, raw, outputPath) {
|
|
170
|
+
if (samePath(sourcePath, outputPath))
|
|
171
|
+
throw new CliError(
|
|
172
|
+
"OUTPUT_EQUALS_INPUT",
|
|
173
|
+
"--output must differ from the input path"
|
|
174
|
+
);
|
|
175
|
+
const cell = parseCellAddress(address);
|
|
176
|
+
const sheet = findSheet(workbook, sheetName).model;
|
|
177
|
+
expandSheetToCell(sheet, cell.row, cell.col);
|
|
178
|
+
sheet.setCell(cell.row, cell.col, raw);
|
|
179
|
+
await writeOutputAtomically(
|
|
180
|
+
outputPath,
|
|
181
|
+
await serializeWorkbook(workbook, outputPath, sheetName)
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
function expandSheetToCell(sheet, row, col) {
|
|
185
|
+
if (row < sheet.rows && col < sheet.cols) return;
|
|
186
|
+
const snapshot = sheet.toSnapshot();
|
|
187
|
+
sheet.restoreSnapshot({
|
|
188
|
+
...snapshot,
|
|
189
|
+
rows: Math.max(sheet.rows, row + 1),
|
|
190
|
+
cols: Math.max(sheet.cols, col + 1)
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
function samePath(left, right) {
|
|
194
|
+
return (0, import_node_path.resolve)(left) === (0, import_node_path.resolve)(right);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// src/cli.ts
|
|
198
|
+
async function run(arguments_) {
|
|
199
|
+
const [command, path, sheetName, target, ...rest] = arguments_;
|
|
200
|
+
if (command === "inspect" && path) {
|
|
201
|
+
const loaded = await loadWorkbook(path);
|
|
202
|
+
return JSON.stringify(inspectWorkbook(loaded.workbook, loaded.format));
|
|
203
|
+
}
|
|
204
|
+
if (command === "get" && path && sheetName && target) {
|
|
205
|
+
const loaded = await loadWorkbook(path);
|
|
206
|
+
return JSON.stringify(getRange(loaded.workbook, sheetName, target));
|
|
207
|
+
}
|
|
208
|
+
if (command === "set" && path && sheetName && target) {
|
|
209
|
+
const value = parseSetValue(rest);
|
|
210
|
+
const loaded = await loadWorkbook(path);
|
|
211
|
+
await setCellAndWrite(
|
|
212
|
+
loaded.workbook,
|
|
213
|
+
path,
|
|
214
|
+
sheetName,
|
|
215
|
+
target,
|
|
216
|
+
value.raw,
|
|
217
|
+
value.output
|
|
218
|
+
);
|
|
219
|
+
return JSON.stringify({
|
|
220
|
+
output: value.output,
|
|
221
|
+
sheet: sheetName,
|
|
222
|
+
address: target.toUpperCase()
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
throw new CliError("INVALID_ARGUMENT", usage());
|
|
226
|
+
}
|
|
227
|
+
function parseSetValue(arguments_) {
|
|
228
|
+
let raw;
|
|
229
|
+
let output;
|
|
230
|
+
for (let index = 0; index < arguments_.length; index += 2) {
|
|
231
|
+
const flag = arguments_[index];
|
|
232
|
+
const value = arguments_[index + 1];
|
|
233
|
+
if (!value)
|
|
234
|
+
throw new CliError("INVALID_ARGUMENT", `Missing value for ${flag}`);
|
|
235
|
+
if (flag === "--text") raw = value;
|
|
236
|
+
else if (flag === "--number") {
|
|
237
|
+
if (!Number.isFinite(Number(value)))
|
|
238
|
+
throw new CliError("INVALID_ARGUMENT", `Invalid number: ${value}`);
|
|
239
|
+
raw = value;
|
|
240
|
+
} else if (flag === "--boolean") {
|
|
241
|
+
if (value !== "true" && value !== "false")
|
|
242
|
+
throw new CliError("INVALID_ARGUMENT", `Invalid boolean: ${value}`);
|
|
243
|
+
raw = value.toUpperCase();
|
|
244
|
+
} else if (flag === "--formula")
|
|
245
|
+
raw = value.startsWith("=") ? value : `=${value}`;
|
|
246
|
+
else if (flag === "--output") output = value;
|
|
247
|
+
else throw new CliError("INVALID_ARGUMENT", `Unknown option: ${flag}`);
|
|
248
|
+
}
|
|
249
|
+
if (raw === void 0)
|
|
250
|
+
throw new CliError("INVALID_ARGUMENT", "set requires one value flag");
|
|
251
|
+
if (!output)
|
|
252
|
+
throw new CliError("INVALID_ARGUMENT", "set requires --output <path>");
|
|
253
|
+
return { raw, output };
|
|
254
|
+
}
|
|
255
|
+
function usage() {
|
|
256
|
+
return "Usage: numera inspect <file> | get <file> <sheet> <range> | set <file> <sheet> <cell> (--text|--number|--boolean|--formula) <value> --output <file>";
|
|
257
|
+
}
|
|
258
|
+
void run(Bun.argv.slice(2)).then((output) => process.stdout.write(`${output}
|
|
259
|
+
`)).catch((error) => {
|
|
260
|
+
const diagnostic = error instanceof CliError ? `${error.code}: ${error.message}` : `UNEXPECTED: ${String(error)}`;
|
|
261
|
+
process.stderr.write(`${diagnostic}
|
|
262
|
+
`);
|
|
263
|
+
process.exitCode = 1;
|
|
264
|
+
});
|
|
265
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
266
|
+
0 && (module.exports = {
|
|
267
|
+
parseSetValue,
|
|
268
|
+
run
|
|
269
|
+
});
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/CliError.ts
|
|
4
|
+
var CliError = class extends Error {
|
|
5
|
+
constructor(code, message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.name = "CliError";
|
|
9
|
+
}
|
|
10
|
+
code;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/commands.ts
|
|
14
|
+
import { toA1 } from "@vectojs/numera-core";
|
|
15
|
+
import { resolve } from "path";
|
|
16
|
+
|
|
17
|
+
// src/a1.ts
|
|
18
|
+
import { parseA1, parseRange } from "@vectojs/numera-core";
|
|
19
|
+
function parseCellAddress(source) {
|
|
20
|
+
const cell = parseA1(source.toUpperCase());
|
|
21
|
+
if (!cell)
|
|
22
|
+
throw new CliError("INVALID_ARGUMENT", `Invalid cell address: ${source}`);
|
|
23
|
+
return cell;
|
|
24
|
+
}
|
|
25
|
+
function parseCellRange(source) {
|
|
26
|
+
const normalized = source.toUpperCase();
|
|
27
|
+
const range = normalized.includes(":") ? parseRange(normalized) : (() => {
|
|
28
|
+
const cell = parseA1(normalized);
|
|
29
|
+
return cell ? { r1: cell.row, c1: cell.col, r2: cell.row, c2: cell.col } : null;
|
|
30
|
+
})();
|
|
31
|
+
if (!range)
|
|
32
|
+
throw new CliError("INVALID_ARGUMENT", `Invalid cell range: ${source}`);
|
|
33
|
+
return range;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/workbookFiles.ts
|
|
37
|
+
import {
|
|
38
|
+
fromCsv,
|
|
39
|
+
parseWorkbookJson,
|
|
40
|
+
toCsv,
|
|
41
|
+
toWorkbookJson,
|
|
42
|
+
Workbook
|
|
43
|
+
} from "@vectojs/numera-core";
|
|
44
|
+
import { decodeXlsx, encodeXlsx } from "@vectojs/numera-xlsx";
|
|
45
|
+
import { rename, rm } from "fs/promises";
|
|
46
|
+
function detectWorkbookFormat(path) {
|
|
47
|
+
const normalized = path.toLowerCase();
|
|
48
|
+
if (normalized.endsWith(".xlsx")) return "xlsx";
|
|
49
|
+
if (normalized.endsWith(".csv")) return "csv";
|
|
50
|
+
if (normalized.endsWith(".numera.json") || normalized.endsWith(".json"))
|
|
51
|
+
return "json";
|
|
52
|
+
throw new CliError(
|
|
53
|
+
"INPUT_FORMAT_UNSUPPORTED",
|
|
54
|
+
`Unsupported input format: ${path}`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
async function loadWorkbook(path) {
|
|
58
|
+
const format = detectWorkbookFormat(path);
|
|
59
|
+
const file = Bun.file(path);
|
|
60
|
+
if (!await file.exists())
|
|
61
|
+
throw new CliError(
|
|
62
|
+
"INVALID_ARGUMENT",
|
|
63
|
+
`Input file does not exist: ${path}`
|
|
64
|
+
);
|
|
65
|
+
if (format === "xlsx")
|
|
66
|
+
return {
|
|
67
|
+
format,
|
|
68
|
+
workbook: await decodeXlsx(new Uint8Array(await file.arrayBuffer()))
|
|
69
|
+
};
|
|
70
|
+
const source = await file.text();
|
|
71
|
+
if (format === "json") return { format, workbook: parseWorkbookJson(source) };
|
|
72
|
+
const workbook = new Workbook();
|
|
73
|
+
const sheet = workbook.activeSheet.model;
|
|
74
|
+
for (const write of fromCsv(
|
|
75
|
+
source,
|
|
76
|
+
{ row: 0, col: 0 },
|
|
77
|
+
{ rows: sheet.rows, cols: sheet.cols }
|
|
78
|
+
))
|
|
79
|
+
sheet.setCell(write.row, write.col, write.raw);
|
|
80
|
+
return { format, workbook };
|
|
81
|
+
}
|
|
82
|
+
async function serializeWorkbook(workbook, outputPath, sheetName) {
|
|
83
|
+
const format = detectOutputFormat(outputPath);
|
|
84
|
+
if (format === "xlsx") return encodeXlsx(workbook);
|
|
85
|
+
if (format === "json") return toWorkbookJson(workbook);
|
|
86
|
+
const sheet = sheetName ? findSheet(workbook, sheetName).model : workbook.activeSheet.model;
|
|
87
|
+
const used = sheet.getUsedRange();
|
|
88
|
+
const range = used ?? { r1: 0, c1: 0, r2: 0, c2: 0 };
|
|
89
|
+
return toCsv(sheet, range);
|
|
90
|
+
}
|
|
91
|
+
function findSheet(workbook, name) {
|
|
92
|
+
const sheet = workbook.sheets.find((candidate) => candidate.name === name);
|
|
93
|
+
if (!sheet) throw new CliError("SHEET_NOT_FOUND", `Sheet not found: ${name}`);
|
|
94
|
+
return sheet;
|
|
95
|
+
}
|
|
96
|
+
async function writeOutputAtomically(outputPath, content) {
|
|
97
|
+
const temporaryPath = `${outputPath}.numera-${crypto.randomUUID()}.tmp`;
|
|
98
|
+
try {
|
|
99
|
+
await Bun.write(temporaryPath, content);
|
|
100
|
+
await rename(temporaryPath, outputPath);
|
|
101
|
+
} finally {
|
|
102
|
+
await rm(temporaryPath, { force: true });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function detectOutputFormat(path) {
|
|
106
|
+
try {
|
|
107
|
+
return detectWorkbookFormat(path);
|
|
108
|
+
} catch {
|
|
109
|
+
throw new CliError(
|
|
110
|
+
"OUTPUT_FORMAT_UNSUPPORTED",
|
|
111
|
+
`Unsupported output format: ${path}`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/commands.ts
|
|
117
|
+
function inspectWorkbook(workbook, format) {
|
|
118
|
+
return {
|
|
119
|
+
format,
|
|
120
|
+
activeSheet: workbook.activeSheet.name,
|
|
121
|
+
sheets: workbook.sheets.map((sheet) => ({
|
|
122
|
+
name: sheet.name,
|
|
123
|
+
populatedCells: sheet.model.cellCount,
|
|
124
|
+
rows: sheet.model.rows,
|
|
125
|
+
cols: sheet.model.cols
|
|
126
|
+
}))
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function getRange(workbook, sheetName, sourceRange) {
|
|
130
|
+
const sheet = findSheet(workbook, sheetName).model;
|
|
131
|
+
const range = parseCellRange(sourceRange);
|
|
132
|
+
if (range.r2 >= sheet.rows || range.c2 >= sheet.cols)
|
|
133
|
+
throw new CliError(
|
|
134
|
+
"RANGE_OUT_OF_BOUNDS",
|
|
135
|
+
`Range is outside sheet bounds: ${sourceRange}`
|
|
136
|
+
);
|
|
137
|
+
const cells = [];
|
|
138
|
+
for (let row = range.r1; row <= range.r2; row++) {
|
|
139
|
+
for (let col = range.c1; col <= range.c2; col++) {
|
|
140
|
+
cells.push({
|
|
141
|
+
address: toA1({ row, col }),
|
|
142
|
+
raw: sheet.getRaw(row, col),
|
|
143
|
+
display: sheet.getDisplay(row, col)
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return { sheet: sheetName, range: sourceRange.toUpperCase(), cells };
|
|
148
|
+
}
|
|
149
|
+
async function setCellAndWrite(workbook, sourcePath, sheetName, address, raw, outputPath) {
|
|
150
|
+
if (samePath(sourcePath, outputPath))
|
|
151
|
+
throw new CliError(
|
|
152
|
+
"OUTPUT_EQUALS_INPUT",
|
|
153
|
+
"--output must differ from the input path"
|
|
154
|
+
);
|
|
155
|
+
const cell = parseCellAddress(address);
|
|
156
|
+
const sheet = findSheet(workbook, sheetName).model;
|
|
157
|
+
expandSheetToCell(sheet, cell.row, cell.col);
|
|
158
|
+
sheet.setCell(cell.row, cell.col, raw);
|
|
159
|
+
await writeOutputAtomically(
|
|
160
|
+
outputPath,
|
|
161
|
+
await serializeWorkbook(workbook, outputPath, sheetName)
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
function expandSheetToCell(sheet, row, col) {
|
|
165
|
+
if (row < sheet.rows && col < sheet.cols) return;
|
|
166
|
+
const snapshot = sheet.toSnapshot();
|
|
167
|
+
sheet.restoreSnapshot({
|
|
168
|
+
...snapshot,
|
|
169
|
+
rows: Math.max(sheet.rows, row + 1),
|
|
170
|
+
cols: Math.max(sheet.cols, col + 1)
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
function samePath(left, right) {
|
|
174
|
+
return resolve(left) === resolve(right);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/cli.ts
|
|
178
|
+
async function run(arguments_) {
|
|
179
|
+
const [command, path, sheetName, target, ...rest] = arguments_;
|
|
180
|
+
if (command === "inspect" && path) {
|
|
181
|
+
const loaded = await loadWorkbook(path);
|
|
182
|
+
return JSON.stringify(inspectWorkbook(loaded.workbook, loaded.format));
|
|
183
|
+
}
|
|
184
|
+
if (command === "get" && path && sheetName && target) {
|
|
185
|
+
const loaded = await loadWorkbook(path);
|
|
186
|
+
return JSON.stringify(getRange(loaded.workbook, sheetName, target));
|
|
187
|
+
}
|
|
188
|
+
if (command === "set" && path && sheetName && target) {
|
|
189
|
+
const value = parseSetValue(rest);
|
|
190
|
+
const loaded = await loadWorkbook(path);
|
|
191
|
+
await setCellAndWrite(
|
|
192
|
+
loaded.workbook,
|
|
193
|
+
path,
|
|
194
|
+
sheetName,
|
|
195
|
+
target,
|
|
196
|
+
value.raw,
|
|
197
|
+
value.output
|
|
198
|
+
);
|
|
199
|
+
return JSON.stringify({
|
|
200
|
+
output: value.output,
|
|
201
|
+
sheet: sheetName,
|
|
202
|
+
address: target.toUpperCase()
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
throw new CliError("INVALID_ARGUMENT", usage());
|
|
206
|
+
}
|
|
207
|
+
function parseSetValue(arguments_) {
|
|
208
|
+
let raw;
|
|
209
|
+
let output;
|
|
210
|
+
for (let index = 0; index < arguments_.length; index += 2) {
|
|
211
|
+
const flag = arguments_[index];
|
|
212
|
+
const value = arguments_[index + 1];
|
|
213
|
+
if (!value)
|
|
214
|
+
throw new CliError("INVALID_ARGUMENT", `Missing value for ${flag}`);
|
|
215
|
+
if (flag === "--text") raw = value;
|
|
216
|
+
else if (flag === "--number") {
|
|
217
|
+
if (!Number.isFinite(Number(value)))
|
|
218
|
+
throw new CliError("INVALID_ARGUMENT", `Invalid number: ${value}`);
|
|
219
|
+
raw = value;
|
|
220
|
+
} else if (flag === "--boolean") {
|
|
221
|
+
if (value !== "true" && value !== "false")
|
|
222
|
+
throw new CliError("INVALID_ARGUMENT", `Invalid boolean: ${value}`);
|
|
223
|
+
raw = value.toUpperCase();
|
|
224
|
+
} else if (flag === "--formula")
|
|
225
|
+
raw = value.startsWith("=") ? value : `=${value}`;
|
|
226
|
+
else if (flag === "--output") output = value;
|
|
227
|
+
else throw new CliError("INVALID_ARGUMENT", `Unknown option: ${flag}`);
|
|
228
|
+
}
|
|
229
|
+
if (raw === void 0)
|
|
230
|
+
throw new CliError("INVALID_ARGUMENT", "set requires one value flag");
|
|
231
|
+
if (!output)
|
|
232
|
+
throw new CliError("INVALID_ARGUMENT", "set requires --output <path>");
|
|
233
|
+
return { raw, output };
|
|
234
|
+
}
|
|
235
|
+
function usage() {
|
|
236
|
+
return "Usage: numera inspect <file> | get <file> <sheet> <range> | set <file> <sheet> <cell> (--text|--number|--boolean|--formula) <value> --output <file>";
|
|
237
|
+
}
|
|
238
|
+
void run(Bun.argv.slice(2)).then((output) => process.stdout.write(`${output}
|
|
239
|
+
`)).catch((error) => {
|
|
240
|
+
const diagnostic = error instanceof CliError ? `${error.code}: ${error.message}` : `UNEXPECTED: ${String(error)}`;
|
|
241
|
+
process.stderr.write(`${diagnostic}
|
|
242
|
+
`);
|
|
243
|
+
process.exitCode = 1;
|
|
244
|
+
});
|
|
245
|
+
export {
|
|
246
|
+
parseSetValue,
|
|
247
|
+
run
|
|
248
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Workbook } from "@vectojs/numera-core";
|
|
2
|
+
export interface InspectResult {
|
|
3
|
+
format: string;
|
|
4
|
+
activeSheet: string;
|
|
5
|
+
sheets: Array<{
|
|
6
|
+
name: string;
|
|
7
|
+
populatedCells: number;
|
|
8
|
+
rows: number;
|
|
9
|
+
cols: number;
|
|
10
|
+
}>;
|
|
11
|
+
}
|
|
12
|
+
export declare function inspectWorkbook(workbook: Workbook, format: string): InspectResult;
|
|
13
|
+
export declare function getRange(workbook: Workbook, sheetName: string, sourceRange: string): {
|
|
14
|
+
sheet: string;
|
|
15
|
+
range: string;
|
|
16
|
+
cells: {
|
|
17
|
+
address: string;
|
|
18
|
+
raw: string;
|
|
19
|
+
display: string;
|
|
20
|
+
}[];
|
|
21
|
+
};
|
|
22
|
+
export declare function setCellAndWrite(workbook: Workbook, sourcePath: string, sheetName: string, address: string, raw: string, outputPath: string): Promise<void>;
|
|
23
|
+
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAW3D,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,cAAc,EAAE,MAAM,CAAC;QACvB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;CACJ;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM,GACb,aAAa,CAWf;AAED,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM;;;;;;;;EAoBpB;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAcf"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// src/index.ts
|
|
17
|
+
var index_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(index_exports);
|
package/dist/index.mjs
ADDED
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Workbook } from "@vectojs/numera-core";
|
|
2
|
+
export type WorkbookFileFormat = "csv" | "json" | "xlsx";
|
|
3
|
+
export interface LoadedWorkbook {
|
|
4
|
+
format: WorkbookFileFormat;
|
|
5
|
+
workbook: Workbook;
|
|
6
|
+
}
|
|
7
|
+
export declare function detectWorkbookFormat(path: string): WorkbookFileFormat;
|
|
8
|
+
export declare function loadWorkbook(path: string): Promise<LoadedWorkbook>;
|
|
9
|
+
export declare function serializeWorkbook(workbook: Workbook, outputPath: string, sheetName?: string): Promise<Uint8Array | string>;
|
|
10
|
+
export declare function findSheet(workbook: Workbook, name: string): import("@vectojs/numera-core").WorkbookSheet;
|
|
11
|
+
export declare function writeOutputAtomically(outputPath: string, content: Uint8Array | string): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=workbookFiles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workbookFiles.d.ts","sourceRoot":"","sources":["../src/workbookFiles.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,QAAQ,EACT,MAAM,sBAAsB,CAAC;AAM9B,MAAM,MAAM,kBAAkB,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,CAUrE;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAwBxE;AAED,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,GAAG,MAAM,CAAC,CAU9B;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,gDAIzD;AAED,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,UAAU,GAAG,MAAM,GAC3B,OAAO,CAAC,IAAI,CAAC,CAQf"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vectojs/numera-cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Safe local-first workbook automation CLI for the Numera family.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/vectojs/numera-cli.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/vectojs/numera-cli/issues"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/vectojs/numera-cli#readme",
|
|
17
|
+
"main": "./dist/index.js",
|
|
18
|
+
"module": "./dist/index.mjs",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"bin": {
|
|
21
|
+
"numera": "./dist/cli.js"
|
|
22
|
+
},
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/index.mjs",
|
|
27
|
+
"require": "./dist/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE",
|
|
34
|
+
"CHANGELOG.md"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsup src/index.ts src/cli.ts --format cjs,esm --clean && tsc -p tsconfig.build.json --emitDeclarationOnly --outDir dist",
|
|
38
|
+
"changeset": "changeset",
|
|
39
|
+
"changeset:version": "changeset version",
|
|
40
|
+
"test": "bun test",
|
|
41
|
+
"format:check": "prettier --check .",
|
|
42
|
+
"lint": "oxlint --deny-warnings src test",
|
|
43
|
+
"verify": "bun run format:check && bun run lint && bun test && bun run build"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@vectojs/numera-core": "0.3.1",
|
|
47
|
+
"@vectojs/numera-xlsx": "0.1.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@changesets/cli": "^2.31.0",
|
|
51
|
+
"@types/bun": "^1.3.0",
|
|
52
|
+
"oxlint": "^1.71.0",
|
|
53
|
+
"prettier": "^3.9.1",
|
|
54
|
+
"tsup": "^8.3.5",
|
|
55
|
+
"typescript": "^7.0.2"
|
|
56
|
+
}
|
|
57
|
+
}
|