@lotics/cli 0.143.0 → 0.143.3
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/AGENTS.md +6 -15
- package/README.md +9 -13
- package/dist/render_page.js +215 -63
- package/dist/src/cli.js +240 -70
- package/dist/src/client.d.ts +4 -0
- package/docs/building_an_app.md +18 -6
- package/docs/cli_reference.md +13 -13
- package/docs/knowledge_docs.md +4 -8
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
# @lotics/cli — agent index
|
|
2
2
|
|
|
3
3
|
The model an agent needs before driving this CLI: which surface answers which question, what the
|
|
4
|
-
conventions are, and where the traps are.
|
|
5
|
-
renders § COMMANDS from the same table the CLI dispatches on, so it is the one inventory that cannot
|
|
6
|
-
fall behind the binary you have.
|
|
4
|
+
conventions are, and where the traps are.
|
|
7
5
|
|
|
8
6
|
| Read | For |
|
|
9
7
|
|---|---|
|
|
@@ -51,18 +49,15 @@ whether something exists, read `lotics --help` § COMMANDS — the whole section
|
|
|
51
49
|
`workspace doctor` on findings. Scripts can gate on them.
|
|
52
50
|
- **Text output is the default and is built for reading**; reach for `--json` only when a field is
|
|
53
51
|
needed programmatically.
|
|
54
|
-
- **`lotics report '<json>'` is the channel for what nothing else records.**
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
when the platform is genuinely missing something, when a success was wrong, or when an error did
|
|
52
|
+
- **`lotics report '<json>'` is the channel for what nothing else records.** Reach for it
|
|
53
|
+
when the platform is genuinely missing something (a capability that does not exist — no
|
|
54
|
+
command ran), when a success was wrong (exited 0, wrong effect), or when an error did
|
|
58
55
|
not name the remedy — not for your own mistakes, which the logs already show.
|
|
59
56
|
**It is a frame, not a paragraph** — `{goal, actual, expected?, tried?, wanted?}`, because a log
|
|
60
57
|
reconstructs what you RAN and never what you WANTED, and that gap is the report. `goal` and
|
|
61
|
-
`actual` are required; there is no severity or category to pick
|
|
62
|
-
off the corpus, and a taxonomy in a released binary cannot be corrected without a release). The
|
|
58
|
+
`actual` are required; there is no severity or category to pick. The
|
|
63
59
|
session's commands attach themselves — do not retype them. Run it bare for the full prompt; a
|
|
64
|
-
long one rides `@file.json` or an explicit `-` for stdin (bare NEVER reads stdin
|
|
65
|
-
inherited pipe cannot wedge the one command meant to unblock you).
|
|
60
|
+
long one rides `@file.json` or an explicit `-` for stdin (bare NEVER reads stdin).
|
|
66
61
|
- **`LOTICS_TELEMETRY=1` correlates a whole session** so the authoring loop's rough edges can be
|
|
67
62
|
found and fixed. Off by default; set it in the shell profile, not per command (each invocation is
|
|
68
63
|
its own process). It sends no arguments, no file contents, and no record data — see README
|
|
@@ -93,7 +88,3 @@ whether something exists, read `lotics --help` § COMMANDS — the whole section
|
|
|
93
88
|
`src/agents/<alias>.md` and is pushed by `app agent set`.
|
|
94
89
|
- **OAuth connections.** Attaching a connected account is web-only; the CLI can list them.
|
|
95
90
|
- **Anything needing a browser.** `app dev` and `file preview` shell out to a local Chrome.
|
|
96
|
-
|
|
97
|
-
Everything above is a *known* boundary. When you hit one that is NOT listed here — a verb that
|
|
98
|
-
should exist and does not — that is the gap worth reporting, and `lotics report` is how it reaches
|
|
99
|
-
us. Working around it silently is how it stays missing.
|
package/README.md
CHANGED
|
@@ -23,9 +23,12 @@ time, so a doc or a whole package added upstream shows up without upgrading this
|
|
|
23
23
|
Per-command contracts, flags, exit codes and gotchas are in
|
|
24
24
|
[docs/cli_reference.md](./docs/cli_reference.md); `lotics --help` is the always-current verb list.
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
The platform's primary surfaces have dedicated usage guides that ship inside this
|
|
27
27
|
package (reachable at `node_modules/@lotics/cli/docs/*.md` once installed):
|
|
28
28
|
|
|
29
|
+
- [`docs/building_an_app.md`](docs/building_an_app.md) — building a custom-code app end to end:
|
|
30
|
+
the sequence the steps go in (clarify → model → types → queries → workflows → screens → ship)
|
|
31
|
+
and the deploy-free inner loop. Read it once before starting an app.
|
|
29
32
|
- [`docs/document_templates.md`](docs/document_templates.md) — generate finished documents
|
|
30
33
|
(PDF, Excel, Word, email) by filling reusable templates: the five template types, the
|
|
31
34
|
create → generate → chain lifecycle, and the marker capabilities.
|
|
@@ -33,8 +36,6 @@ package (reachable at `node_modules/@lotics/cli/docs/*.md` once installed):
|
|
|
33
36
|
workspace facts an agent can't guess, the access-vs-activation model, and the
|
|
34
37
|
catalog-then-stage retrieval model agents use to pull only the lines they need.
|
|
35
38
|
|
|
36
|
-
Both point to `lotics tools <name>` for exact input schemas.
|
|
37
|
-
|
|
38
39
|
## Install
|
|
39
40
|
|
|
40
41
|
```bash
|
|
@@ -118,7 +119,7 @@ Every request identifies the CLI (`user-agent: lotics-cli/<version> node/<v> <pl
|
|
|
118
119
|
- Requests carry a session id shared by every command in the sitting — under an agent harness it adopts the harness's own session id, otherwise it rolls over after 30 minutes idle (`~/.lotics/session.json`).
|
|
119
120
|
- Each invocation appends one record to `~/.lotics/telemetry.ndjson` — the command, its exit code, how long it took, and, on a failure, the message that was printed. Batches are sent on a later run; a failed send is retried, never dropped silently.
|
|
120
121
|
|
|
121
|
-
Arguments contribute a **hash** and a **shape** (`records[].data.name:string`) and nothing else — no values, no file contents, no record data. The hash is the point: two failures in a row with the same hash mean the error message did not tell you enough to fix the call
|
|
122
|
+
Arguments contribute a **hash** and a **shape** (`records[].data.name:string`) and nothing else — no values, no file contents, no record data. The hash is the point: two failures in a row with the same hash mean the error message did not tell you enough to fix the call.
|
|
122
123
|
|
|
123
124
|
Unset, nothing is stored, nothing is sent, and no spool file is created.
|
|
124
125
|
|
|
@@ -148,14 +149,10 @@ reconstruct what you *wanted*, and the gap between the two is the report.
|
|
|
148
149
|
| `tried` | | what you already tried, and what it said |
|
|
149
150
|
| `wanted` | | what would have unblocked you |
|
|
150
151
|
|
|
151
|
-
There is no severity or category to pick.
|
|
152
|
-
were trying to do is not recoverable from anywhere else.
|
|
152
|
+
There is no severity or category to pick.
|
|
153
153
|
|
|
154
|
-
Reading from stdin needs an explicit `-`.
|
|
155
|
-
|
|
156
|
-
writer from a descriptor a supervisor left open, so guessing either hangs forever or drops a
|
|
157
|
-
slow writer's report. With `-` the bare form always prints the frame and a piped one waits as
|
|
158
|
-
long as it takes.
|
|
154
|
+
Reading from stdin needs an explicit `-`. With `-` the bare form always prints the frame and a
|
|
155
|
+
piped one waits as long as it takes.
|
|
159
156
|
|
|
160
157
|
If `LOTICS_TELEMETRY=1` is set, the commands from your session attach themselves — don't retype
|
|
161
158
|
them. It runs whether or not telemetry is on (invoking it is the consent that passive recording
|
|
@@ -163,7 +160,7 @@ needs an opt-in for), posts immediately, and tells you if it did not land.
|
|
|
163
160
|
|
|
164
161
|
Do not paste records, file contents, or credentials.
|
|
165
162
|
|
|
166
|
-
In an app project, `lotics app *` commands derive the credential from the directory when nothing explicit chose one: the manifest names the app's workspace, and when exactly one saved profile owns that workspace, that profile is used — the machine-wide default
|
|
163
|
+
In an app project, `lotics app *` commands derive the credential from the directory when nothing explicit chose one: the manifest names the app's workspace, and when exactly one saved profile owns that workspace, that profile is used — the machine-wide default is never consulted. An explicit flag, env var, or directory pin still wins, and an org whose profile remembers a different workspace simply falls through to the announced default.
|
|
167
164
|
|
|
168
165
|
## Workspaces
|
|
169
166
|
|
|
@@ -313,7 +310,6 @@ cat input.json | lotics app agent run app_abc recognize # inputs via stdi
|
|
|
313
310
|
lotics app agent run app_abc recognize --json # full run summary to stdout
|
|
314
311
|
lotics app agent run app_abc recognize --session cli-123 '{}' # continue an existing thread
|
|
315
312
|
|
|
316
|
-
# Dev-link @lotics/ui to packages/ui/src for live HMR (Vite alias; deploy bundles it)
|
|
317
313
|
# Dev-link @lotics/ui to a monorepo checkout for ONE command — nothing is written to disk
|
|
318
314
|
LOTICS_UI_SRC=/abs/monorepo/packages/ui/src lotics app dev
|
|
319
315
|
LOTICS_UI_SRC=/abs/monorepo/packages/ui/src lotics app deploy -m "..." # warns: bundles YOUR kit copy
|
package/dist/render_page.js
CHANGED
|
@@ -4563,7 +4563,7 @@ ${e.toString()}`);
|
|
|
4563
4563
|
}
|
|
4564
4564
|
};
|
|
4565
4565
|
var type2Number = { "boolean": 3, "string": 2, "number": 1 };
|
|
4566
|
-
var
|
|
4566
|
+
var Infix2 = {
|
|
4567
4567
|
compareOp: (value1, infix, value2, isArray1, isArray2) => {
|
|
4568
4568
|
if (value1 == null) value1 = 0;
|
|
4569
4569
|
if (value2 == null) value2 = 0;
|
|
@@ -4654,7 +4654,7 @@ ${e.toString()}`);
|
|
|
4654
4654
|
module2.exports = {
|
|
4655
4655
|
Prefix,
|
|
4656
4656
|
Postfix,
|
|
4657
|
-
Infix,
|
|
4657
|
+
Infix: Infix2,
|
|
4658
4658
|
Operators: {
|
|
4659
4659
|
compareOp: ["<", ">", "=", "<>", "<=", ">="],
|
|
4660
4660
|
concatOp: ["&"],
|
|
@@ -4669,7 +4669,7 @@ ${e.toString()}`);
|
|
|
4669
4669
|
"../../node_modules/fast-formula-parser/formulas/functions/math.js"(exports2, module2) {
|
|
4670
4670
|
var FormulaError = require_error();
|
|
4671
4671
|
var { FormulaHelpers, Types, Factorials, Criteria } = require_helpers();
|
|
4672
|
-
var { Infix } = require_operators();
|
|
4672
|
+
var { Infix: Infix2 } = require_operators();
|
|
4673
4673
|
var H = FormulaHelpers;
|
|
4674
4674
|
var f = [];
|
|
4675
4675
|
var fd2 = [];
|
|
@@ -5192,7 +5192,7 @@ ${e.toString()}`);
|
|
|
5192
5192
|
if (criteria.match === criteria.value.test(value2)) {
|
|
5193
5193
|
sum += valueToAdd;
|
|
5194
5194
|
}
|
|
5195
|
-
} else if (
|
|
5195
|
+
} else if (Infix2.compareOp(value2, criteria.op, criteria.value, Array.isArray(value2), isCriteriaArray)) {
|
|
5196
5196
|
sum += valueToAdd;
|
|
5197
5197
|
}
|
|
5198
5198
|
});
|
|
@@ -11472,7 +11472,7 @@ ${e.toString()}`);
|
|
|
11472
11472
|
"../../node_modules/fast-formula-parser/formulas/functions/statistical.js"(exports2, module2) {
|
|
11473
11473
|
var FormulaError = require_error();
|
|
11474
11474
|
var { FormulaHelpers, Types, Criteria, Address } = require_helpers();
|
|
11475
|
-
var { Infix } = require_operators();
|
|
11475
|
+
var { Infix: Infix2 } = require_operators();
|
|
11476
11476
|
var H = FormulaHelpers;
|
|
11477
11477
|
var { DistributionFunctions } = require_distribution();
|
|
11478
11478
|
var StatisticalFunctions = {
|
|
@@ -11534,7 +11534,7 @@ ${e.toString()}`);
|
|
|
11534
11534
|
sum += valueToAdd;
|
|
11535
11535
|
cnt++;
|
|
11536
11536
|
}
|
|
11537
|
-
} else if (
|
|
11537
|
+
} else if (Infix2.compareOp(value2, criteria.op, criteria.value, Array.isArray(value2), isCriteriaArray)) {
|
|
11538
11538
|
sum += valueToAdd;
|
|
11539
11539
|
cnt++;
|
|
11540
11540
|
}
|
|
@@ -11573,7 +11573,7 @@ ${e.toString()}`);
|
|
|
11573
11573
|
if (criteria.op === "wc") {
|
|
11574
11574
|
if (criteria.match === criteria.value.test(value2))
|
|
11575
11575
|
cnt++;
|
|
11576
|
-
} else if (
|
|
11576
|
+
} else if (Infix2.compareOp(value2, criteria.op, criteria.value, Array.isArray(value2), isCriteriaArray)) {
|
|
11577
11577
|
cnt++;
|
|
11578
11578
|
}
|
|
11579
11579
|
});
|
|
@@ -21126,7 +21126,7 @@ ${e.toString()}`);
|
|
|
21126
21126
|
"../../node_modules/fast-formula-parser/grammar/utils.js"(exports2, module2) {
|
|
21127
21127
|
var FormulaError = require_error();
|
|
21128
21128
|
var { Address } = require_helpers();
|
|
21129
|
-
var { Prefix, Postfix, Infix, Operators } = require_operators();
|
|
21129
|
+
var { Prefix, Postfix, Infix: Infix2, Operators } = require_operators();
|
|
21130
21130
|
var Collection = require_collection();
|
|
21131
21131
|
var MAX_ROW = 1048576;
|
|
21132
21132
|
var MAX_COLUMN = 16384;
|
|
@@ -21250,11 +21250,11 @@ ${e.toString()}`);
|
|
|
21250
21250
|
if (this.isFormulaError(val2))
|
|
21251
21251
|
return val2;
|
|
21252
21252
|
if (Operators.compareOp.includes(infix))
|
|
21253
|
-
return
|
|
21253
|
+
return Infix2.compareOp(val1, infix, val2, isArray1, isArray2);
|
|
21254
21254
|
else if (Operators.concatOp.includes(infix))
|
|
21255
|
-
return
|
|
21255
|
+
return Infix2.concatOp(val1, infix, val2, isArray1, isArray2);
|
|
21256
21256
|
else if (Operators.mathOp.includes(infix))
|
|
21257
|
-
return
|
|
21257
|
+
return Infix2.mathOp(val1, infix, val2, isArray1, isArray2);
|
|
21258
21258
|
else
|
|
21259
21259
|
throw new Error(`Unrecognized infix: ${infix}`);
|
|
21260
21260
|
}
|
|
@@ -21806,7 +21806,7 @@ ${e.toString()}`);
|
|
|
21806
21806
|
"../../node_modules/fast-formula-parser/grammar/dependency/utils.js"(exports2, module2) {
|
|
21807
21807
|
var FormulaError = require_error();
|
|
21808
21808
|
var { FormulaHelpers, Types, Address } = require_helpers();
|
|
21809
|
-
var { Prefix, Postfix, Infix, Operators } = require_operators();
|
|
21809
|
+
var { Prefix, Postfix, Infix: Infix2, Operators } = require_operators();
|
|
21810
21810
|
var Collection = require_collection();
|
|
21811
21811
|
var MAX_ROW = 1048576;
|
|
21812
21812
|
var MAX_COLUMN = 16384;
|
|
@@ -53990,14 +53990,14 @@ ${e.toString()}`);
|
|
|
53990
53990
|
if (!range) return void 0;
|
|
53991
53991
|
const header = [];
|
|
53992
53992
|
for (let col = range.startCol; col <= range.endCol; col++) {
|
|
53993
|
-
const cell = sheet.
|
|
53993
|
+
const cell = sheet.getCellAt(range.startRow, col);
|
|
53994
53994
|
header.push(formatHeader(cell?.value));
|
|
53995
53995
|
}
|
|
53996
53996
|
const records = [];
|
|
53997
53997
|
for (let row = range.startRow + 1; row <= range.endRow; row++) {
|
|
53998
53998
|
const rec = [];
|
|
53999
53999
|
for (let col = range.startCol; col <= range.endCol; col++) {
|
|
54000
|
-
const cell = sheet.
|
|
54000
|
+
const cell = sheet.getCellAt(row, col);
|
|
54001
54001
|
rec.push(coerceValue(cell?.value));
|
|
54002
54002
|
}
|
|
54003
54003
|
records.push(rec);
|
|
@@ -54041,23 +54041,70 @@ ${e.toString()}`);
|
|
|
54041
54041
|
get size() {
|
|
54042
54042
|
return this.store.size;
|
|
54043
54043
|
}
|
|
54044
|
+
/**
|
|
54045
|
+
* The populated extent of this map, memoized until a mutation invalidates it.
|
|
54046
|
+
*
|
|
54047
|
+
* It lives HERE rather than on `SheetModel` because it is a pure function of
|
|
54048
|
+
* the cell set: a cache held next to the data cannot outlive it, so replacing
|
|
54049
|
+
* a sheet's map can never serve the previous map's extent. Every mutator
|
|
54050
|
+
* below drops it, including the packed pair — those bypass `set`/`delete`
|
|
54051
|
+
* entirely and are what the row/column commands use.
|
|
54052
|
+
*
|
|
54053
|
+
* Walking PACKED keys is the other half. `keys()` unpacks each key into an A1
|
|
54054
|
+
* string, which the caller then reparsed with a regex, so the old form packed,
|
|
54055
|
+
* unpacked and reparsed the whole sheet on every call — and it is called once
|
|
54056
|
+
* per oversized range during a recompute.
|
|
54057
|
+
*/
|
|
54058
|
+
usedRange() {
|
|
54059
|
+
if (!this.usedRangeCache) {
|
|
54060
|
+
let minRow2 = Infinity;
|
|
54061
|
+
let maxRow2 = 0;
|
|
54062
|
+
let minCol2 = Infinity;
|
|
54063
|
+
let maxCol2 = 0;
|
|
54064
|
+
for (const packed of this.store.keys()) {
|
|
54065
|
+
const { row, col } = unpackRowCol(packed);
|
|
54066
|
+
if (row < minRow2) minRow2 = row;
|
|
54067
|
+
if (row > maxRow2) maxRow2 = row;
|
|
54068
|
+
if (col < minCol2) minCol2 = col;
|
|
54069
|
+
if (col > maxCol2) maxCol2 = col;
|
|
54070
|
+
}
|
|
54071
|
+
this.usedRangeCache = maxRow2 === 0 ? { minRow: 1, maxRow: 1, minCol: 1, maxCol: 1 } : { minRow: minRow2, maxRow: maxRow2, minCol: minCol2, maxCol: maxCol2 };
|
|
54072
|
+
}
|
|
54073
|
+
const { minRow, maxRow, minCol, maxCol } = this.usedRangeCache;
|
|
54074
|
+
return { minRow, maxRow, minCol, maxCol };
|
|
54075
|
+
}
|
|
54044
54076
|
get(ref2) {
|
|
54045
54077
|
return this.store.get(packRef(ref2));
|
|
54046
54078
|
}
|
|
54079
|
+
/**
|
|
54080
|
+
* Read by coordinate, skipping the A1 round trip.
|
|
54081
|
+
*
|
|
54082
|
+
* `get(ref)` costs a number→string→regex→number conversion per call, which
|
|
54083
|
+
* is invisible until a caller reads a range cell by cell: on one real
|
|
54084
|
+
* workbook `refToRowCol` alone was 39% of a full recompute. A caller already
|
|
54085
|
+
* holding (row, col) — the range resolver, any grid walk — should use this.
|
|
54086
|
+
*/
|
|
54087
|
+
getAt(row, col) {
|
|
54088
|
+
return this.store.get(packRowCol(row, col));
|
|
54089
|
+
}
|
|
54047
54090
|
set(ref2, cell) {
|
|
54048
54091
|
const key = packRef(ref2);
|
|
54049
54092
|
if (key < 0) return this;
|
|
54050
54093
|
this.store.set(key, cell);
|
|
54094
|
+
this.usedRangeCache = void 0;
|
|
54051
54095
|
return this;
|
|
54052
54096
|
}
|
|
54053
54097
|
has(ref2) {
|
|
54054
54098
|
return this.store.has(packRef(ref2));
|
|
54055
54099
|
}
|
|
54056
54100
|
delete(ref2) {
|
|
54057
|
-
|
|
54101
|
+
const deleted = this.store.delete(packRef(ref2));
|
|
54102
|
+
if (deleted) this.usedRangeCache = void 0;
|
|
54103
|
+
return deleted;
|
|
54058
54104
|
}
|
|
54059
54105
|
clear() {
|
|
54060
54106
|
this.store.clear();
|
|
54107
|
+
this.usedRangeCache = void 0;
|
|
54061
54108
|
}
|
|
54062
54109
|
*keys() {
|
|
54063
54110
|
for (const k of this.store.keys()) yield unpackRef(k);
|
|
@@ -54085,10 +54132,13 @@ ${e.toString()}`);
|
|
|
54085
54132
|
/** Direct packed-key set, for the same hot loops. */
|
|
54086
54133
|
setPacked(packed, cell) {
|
|
54087
54134
|
this.store.set(packed, cell);
|
|
54135
|
+
this.usedRangeCache = void 0;
|
|
54088
54136
|
}
|
|
54089
54137
|
/** Direct packed-key delete. */
|
|
54090
54138
|
deletePacked(packed) {
|
|
54091
|
-
|
|
54139
|
+
const deleted = this.store.delete(packed);
|
|
54140
|
+
if (deleted) this.usedRangeCache = void 0;
|
|
54141
|
+
return deleted;
|
|
54092
54142
|
}
|
|
54093
54143
|
};
|
|
54094
54144
|
var StyleRegistry = class {
|
|
@@ -54271,6 +54321,10 @@ ${e.toString()}`);
|
|
|
54271
54321
|
getCell(ref2) {
|
|
54272
54322
|
return this.cells.get(ref2);
|
|
54273
54323
|
}
|
|
54324
|
+
/** Read by coordinate, skipping the A1 round trip — see `CellMap.getAt`. */
|
|
54325
|
+
getCellAt(row, col) {
|
|
54326
|
+
return this.cells.getAt(row, col);
|
|
54327
|
+
}
|
|
54274
54328
|
/**
|
|
54275
54329
|
* Set cell value with optional inline style. Interns style via the
|
|
54276
54330
|
* StyleRegistry when available. When `style` is supplied and matches the
|
|
@@ -54365,22 +54419,12 @@ ${e.toString()}`);
|
|
|
54365
54419
|
this.set(ref2, label, style);
|
|
54366
54420
|
});
|
|
54367
54421
|
}
|
|
54368
|
-
/**
|
|
54422
|
+
/**
|
|
54423
|
+
* The populated extent of the sheet. Derived from — and cached by — the cell
|
|
54424
|
+
* map, so it cannot outlive the data it describes.
|
|
54425
|
+
*/
|
|
54369
54426
|
getUsedRange() {
|
|
54370
|
-
|
|
54371
|
-
let maxRow = 0;
|
|
54372
|
-
let minCol = Infinity;
|
|
54373
|
-
let maxCol = 0;
|
|
54374
|
-
for (const ref2 of this.cells.keys()) {
|
|
54375
|
-
const rc = refToRowCol(ref2);
|
|
54376
|
-
if (!rc) continue;
|
|
54377
|
-
if (rc.row < minRow) minRow = rc.row;
|
|
54378
|
-
if (rc.row > maxRow) maxRow = rc.row;
|
|
54379
|
-
if (rc.col < minCol) minCol = rc.col;
|
|
54380
|
-
if (rc.col > maxCol) maxCol = rc.col;
|
|
54381
|
-
}
|
|
54382
|
-
if (maxRow === 0) return { minRow: 1, maxRow: 1, minCol: 1, maxCol: 1 };
|
|
54383
|
-
return { minRow, maxRow, minCol, maxCol };
|
|
54427
|
+
return this.cells.usedRange();
|
|
54384
54428
|
}
|
|
54385
54429
|
};
|
|
54386
54430
|
var WorkbookModel = class {
|
|
@@ -54627,7 +54671,7 @@ ${e.toString()}`);
|
|
|
54627
54671
|
let visible = true;
|
|
54628
54672
|
for (const filterCol of activeFilters) {
|
|
54629
54673
|
const col = startCol + filterCol.colIndex;
|
|
54630
|
-
const cell = sheet.
|
|
54674
|
+
const cell = sheet.getCellAt(r, col);
|
|
54631
54675
|
const cellText = cell?.displayValue ?? "";
|
|
54632
54676
|
if (filterCol.filterValues && filterCol.filterValues.length > 0) {
|
|
54633
54677
|
if (!filterCol.filterValues.includes(cellText)) {
|
|
@@ -61857,6 +61901,28 @@ ${e.toString()}`);
|
|
|
61857
61901
|
const bang = key.indexOf("!");
|
|
61858
61902
|
return { sheet: key.slice(0, bang), ref: key.slice(bang + 1) };
|
|
61859
61903
|
}
|
|
61904
|
+
var COARSE_COL_SUFFIX = ":*";
|
|
61905
|
+
var COARSE_ROW_PREFIX = "*:";
|
|
61906
|
+
function columnKey(sheet, column) {
|
|
61907
|
+
return `${sheet}!${column}${COARSE_COL_SUFFIX}`;
|
|
61908
|
+
}
|
|
61909
|
+
function rowKey(sheet, row) {
|
|
61910
|
+
return `${sheet}!${COARSE_ROW_PREFIX}${row}`;
|
|
61911
|
+
}
|
|
61912
|
+
function isCoarseKey(key) {
|
|
61913
|
+
return key.endsWith(COARSE_COL_SUFFIX) || key.includes(`!${COARSE_ROW_PREFIX}`);
|
|
61914
|
+
}
|
|
61915
|
+
var A1 = /^([A-Z]+)(\d+)$/;
|
|
61916
|
+
function coarseKeysFor(cellKeyStr) {
|
|
61917
|
+
const bang = cellKeyStr.indexOf("!");
|
|
61918
|
+
if (bang < 0) return [];
|
|
61919
|
+
const sheet = cellKeyStr.slice(0, bang);
|
|
61920
|
+
const ref2 = cellKeyStr.slice(bang + 1);
|
|
61921
|
+
const m = A1.exec(ref2);
|
|
61922
|
+
if (!m) return [];
|
|
61923
|
+
return [columnKey(sheet, m[1]), rowKey(sheet, Number(m[2]))];
|
|
61924
|
+
}
|
|
61925
|
+
var EMPTY = /* @__PURE__ */ new Set();
|
|
61860
61926
|
var DependencyGraph = class {
|
|
61861
61927
|
constructor() {
|
|
61862
61928
|
// Forward edges: cell → cells it references (precedents)
|
|
@@ -61891,19 +61957,35 @@ ${e.toString()}`);
|
|
|
61891
61957
|
}
|
|
61892
61958
|
/** Get all cells that directly depend on the given cell. */
|
|
61893
61959
|
getDirectDependents(cell) {
|
|
61894
|
-
return Array.from(this.
|
|
61960
|
+
return Array.from(this.dependentsOf(cell));
|
|
61961
|
+
}
|
|
61962
|
+
/**
|
|
61963
|
+
* Everything that references this cell — directly, or through the column or
|
|
61964
|
+
* row node that contains it. The single place `dependents` is read, so a
|
|
61965
|
+
* whole-column reference cannot be forgotten by one traversal and honoured by
|
|
61966
|
+
* another.
|
|
61967
|
+
*/
|
|
61968
|
+
dependentsOf(cell) {
|
|
61969
|
+
const direct = this.dependents.get(cell);
|
|
61970
|
+
if (isCoarseKey(cell)) return direct ?? EMPTY;
|
|
61971
|
+
const coarse = coarseKeysFor(cell);
|
|
61972
|
+
let widened;
|
|
61973
|
+
for (const key of coarse) {
|
|
61974
|
+
const via = this.dependents.get(key);
|
|
61975
|
+
if (!via || via.size === 0) continue;
|
|
61976
|
+
widened ?? (widened = new Set(direct));
|
|
61977
|
+
for (const d of via) widened.add(d);
|
|
61978
|
+
}
|
|
61979
|
+
return widened ?? direct ?? EMPTY;
|
|
61895
61980
|
}
|
|
61896
61981
|
/** Get all cells that need recalculation when the given cells change, in topological order. */
|
|
61897
61982
|
getRecalcOrder(changed) {
|
|
61898
|
-
const changedSet = new Set(changed);
|
|
61899
61983
|
const affected = /* @__PURE__ */ new Set();
|
|
61900
61984
|
const queue = [...changed];
|
|
61901
61985
|
let head = 0;
|
|
61902
61986
|
while (head < queue.length) {
|
|
61903
61987
|
const cell = queue[head++];
|
|
61904
|
-
const
|
|
61905
|
-
if (!deps) continue;
|
|
61906
|
-
for (const dep of deps) {
|
|
61988
|
+
for (const dep of this.dependentsOf(cell)) {
|
|
61907
61989
|
if (!affected.has(dep)) {
|
|
61908
61990
|
affected.add(dep);
|
|
61909
61991
|
queue.push(dep);
|
|
@@ -61912,15 +61994,11 @@ ${e.toString()}`);
|
|
|
61912
61994
|
}
|
|
61913
61995
|
if (affected.size === 0) return [];
|
|
61914
61996
|
const inDegree = /* @__PURE__ */ new Map();
|
|
61997
|
+
for (const cell of affected) inDegree.set(cell, 0);
|
|
61915
61998
|
for (const cell of affected) {
|
|
61916
|
-
|
|
61917
|
-
|
|
61918
|
-
if (prec) {
|
|
61919
|
-
for (const p of prec) {
|
|
61920
|
-
if (affected.has(p)) count++;
|
|
61921
|
-
}
|
|
61999
|
+
for (const dep of this.dependentsOf(cell)) {
|
|
62000
|
+
if (affected.has(dep)) inDegree.set(dep, (inDegree.get(dep) ?? 0) + 1);
|
|
61922
62001
|
}
|
|
61923
|
-
inDegree.set(cell, count);
|
|
61924
62002
|
}
|
|
61925
62003
|
const result = [];
|
|
61926
62004
|
const ready = [];
|
|
@@ -61931,9 +62009,7 @@ ${e.toString()}`);
|
|
|
61931
62009
|
while (readyHead < ready.length) {
|
|
61932
62010
|
const cell = ready[readyHead++];
|
|
61933
62011
|
result.push(cell);
|
|
61934
|
-
const
|
|
61935
|
-
if (!deps) continue;
|
|
61936
|
-
for (const dep of deps) {
|
|
62012
|
+
for (const dep of this.dependentsOf(cell)) {
|
|
61937
62013
|
if (!affected.has(dep)) continue;
|
|
61938
62014
|
const newDeg = (inDegree.get(dep) ?? 1) - 1;
|
|
61939
62015
|
inDegree.set(dep, newDeg);
|
|
@@ -61954,12 +62030,13 @@ ${e.toString()}`);
|
|
|
61954
62030
|
const stack = new Array();
|
|
61955
62031
|
const prec = this.precedents.get(cell);
|
|
61956
62032
|
if (!prec) return null;
|
|
62033
|
+
const originCoarse = new Set(coarseKeysFor(cell));
|
|
61957
62034
|
for (const p of prec) {
|
|
61958
62035
|
stack.push({ cell: p, path: [cell, p] });
|
|
61959
62036
|
}
|
|
61960
62037
|
while (stack.length > 0) {
|
|
61961
62038
|
const { cell: current, path } = stack.pop();
|
|
61962
|
-
if (current === cell) return path;
|
|
62039
|
+
if (current === cell || originCoarse.has(current)) return path;
|
|
61963
62040
|
if (visited.has(current)) continue;
|
|
61964
62041
|
visited.add(current);
|
|
61965
62042
|
const nextPrec = this.precedents.get(current);
|
|
@@ -62072,6 +62149,20 @@ ${e.toString()}`);
|
|
|
62072
62149
|
};
|
|
62073
62150
|
}
|
|
62074
62151
|
|
|
62152
|
+
// ../xlsx/src/blank_comparison.ts
|
|
62153
|
+
var import_operators = __toESM(require_operators(), 1);
|
|
62154
|
+
var patched = false;
|
|
62155
|
+
function applyExcelBlankSemantics() {
|
|
62156
|
+
if (patched) return;
|
|
62157
|
+
patched = true;
|
|
62158
|
+
const original = import_operators.Infix.compareOp;
|
|
62159
|
+
import_operators.Infix.compareOp = (value1, infix, value2, isArray1, isArray2) => {
|
|
62160
|
+
if (value1 == null && typeof value2 === "string") value1 = "";
|
|
62161
|
+
if (value2 == null && typeof value1 === "string") value2 = "";
|
|
62162
|
+
return original(value1, infix, value2, isArray1, isArray2);
|
|
62163
|
+
};
|
|
62164
|
+
}
|
|
62165
|
+
|
|
62075
62166
|
// ../xlsx/src/formula_engine.ts
|
|
62076
62167
|
var unwrapArg = (a) => a !== null && typeof a === "object" && "value" in a ? a.value : a;
|
|
62077
62168
|
function adaptFormulajs(fn) {
|
|
@@ -62114,9 +62205,24 @@ ${e.toString()}`);
|
|
|
62114
62205
|
bridgeCtxCache = withCtx;
|
|
62115
62206
|
return withCtx;
|
|
62116
62207
|
}
|
|
62208
|
+
var MAX_EXPANDED_CELLS = 1e4;
|
|
62117
62209
|
var _FormulaEngine = class _FormulaEngine {
|
|
62118
62210
|
constructor(workbook) {
|
|
62119
62211
|
this.graph = new DependencyGraph();
|
|
62212
|
+
/**
|
|
62213
|
+
* Formula cells (by key) whose text the parser could not read AT ALL, found
|
|
62214
|
+
* while building the graph from a loaded file.
|
|
62215
|
+
*
|
|
62216
|
+
* They are held apart from every other failure because the honest response
|
|
62217
|
+
* differs: a formula that evaluates TO an error has been understood, so
|
|
62218
|
+
* writing `#DIV/0!` reports a real answer, while one we cannot parse has not
|
|
62219
|
+
* been — and overwriting the value its author computed would replace a
|
|
62220
|
+
* correct number with a guess. These keep their cached value and are named in
|
|
62221
|
+
* the report instead. The engine's own rule, one level down: degrade what you
|
|
62222
|
+
* cannot draw, refuse what you cannot read.
|
|
62223
|
+
*/
|
|
62224
|
+
this.unreadableFormulas = /* @__PURE__ */ new Set();
|
|
62225
|
+
applyExcelBlankSemantics();
|
|
62120
62226
|
this.workbook = workbook;
|
|
62121
62227
|
this.parser = new import_fast_formula_parser.default({
|
|
62122
62228
|
onCell: (ref2) => this.resolveCell(ref2),
|
|
@@ -62137,11 +62243,13 @@ ${e.toString()}`);
|
|
|
62137
62243
|
/** Build dependency graph from all formula cells in the workbook. */
|
|
62138
62244
|
buildGraph() {
|
|
62139
62245
|
this.graph.clear();
|
|
62246
|
+
this.unreadableFormulas.clear();
|
|
62140
62247
|
for (const sheet of this.workbook.sheets) {
|
|
62141
62248
|
for (const [ref2, cell] of sheet.cells) {
|
|
62142
62249
|
if (!cell.formula) continue;
|
|
62143
62250
|
const key = cellKey({ sheet: sheet.name, ref: ref2 });
|
|
62144
|
-
const deps = this.
|
|
62251
|
+
const { deps, readable } = this.analyzeFormula(cell.formula, sheet.name);
|
|
62252
|
+
if (!readable) this.unreadableFormulas.add(key);
|
|
62145
62253
|
this.graph.setDependencies(key, deps);
|
|
62146
62254
|
}
|
|
62147
62255
|
}
|
|
@@ -62197,8 +62305,13 @@ ${e.toString()}`);
|
|
|
62197
62305
|
onCellChanged(sheetName, ref2, formula) {
|
|
62198
62306
|
const key = cellKey({ sheet: sheetName, ref: ref2 });
|
|
62199
62307
|
if (formula) {
|
|
62200
|
-
const deps = this.
|
|
62308
|
+
const { deps, readable } = this.analyzeFormula(formula, sheetName);
|
|
62201
62309
|
this.graph.setDependencies(key, deps);
|
|
62310
|
+
this.unreadableFormulas.delete(key);
|
|
62311
|
+
if (!readable) {
|
|
62312
|
+
this.setCellError(key, "#ERROR!");
|
|
62313
|
+
return;
|
|
62314
|
+
}
|
|
62202
62315
|
const cycle = this.graph.detectCycle(key);
|
|
62203
62316
|
if (cycle) {
|
|
62204
62317
|
this.setCellError(key, "#CIRC!");
|
|
@@ -62207,6 +62320,7 @@ ${e.toString()}`);
|
|
|
62207
62320
|
this.evaluateCell(key);
|
|
62208
62321
|
} else {
|
|
62209
62322
|
this.graph.removeCell(key);
|
|
62323
|
+
this.unreadableFormulas.delete(key);
|
|
62210
62324
|
}
|
|
62211
62325
|
const order = this.graph.getRecalcOrder([key]);
|
|
62212
62326
|
for (const depKey of order) {
|
|
@@ -62223,6 +62337,7 @@ ${e.toString()}`);
|
|
|
62223
62337
|
// ===========================================================================
|
|
62224
62338
|
/** Evaluate a single formula cell and update its value. */
|
|
62225
62339
|
evaluateCell(key) {
|
|
62340
|
+
if (this.unreadableFormulas.has(key)) return;
|
|
62226
62341
|
const { sheet: sheetName, ref: ref2 } = parseCellKey(key);
|
|
62227
62342
|
const sheetIndex = this.workbook.sheets.findIndex((s) => s.name === sheetName);
|
|
62228
62343
|
if (sheetIndex < 0) return;
|
|
@@ -62271,26 +62386,60 @@ ${e.toString()}`);
|
|
|
62271
62386
|
// ===========================================================================
|
|
62272
62387
|
/** Extract cell/range references from a formula string. Returns empty array for unparseable formulas. */
|
|
62273
62388
|
extractDependencies(formula, currentSheet) {
|
|
62274
|
-
|
|
62275
|
-
|
|
62276
|
-
|
|
62389
|
+
return this.analyzeFormula(formula, currentSheet).deps;
|
|
62390
|
+
}
|
|
62391
|
+
/**
|
|
62392
|
+
* Dependencies plus whether the parser could read the formula at all.
|
|
62393
|
+
*
|
|
62394
|
+
* `fast-formula-parser` reports a formula it cannot LEX by throwing, the same
|
|
62395
|
+
* channel it uses for `#VALUE!`, so the lexer failure of a single cell used to
|
|
62396
|
+
* escape `buildGraph` and abort the recompute for the entire workbook —
|
|
62397
|
+
* observed on a real workbook whose `VLOOKUP` pointed at another file
|
|
62398
|
+
* (`'[1]DATA'!B:C`, an external-workbook reference the lexer has no rule for).
|
|
62399
|
+
* Anything the grammar does not cover lands here the same way, so this catches
|
|
62400
|
+
* the class rather than that one syntax.
|
|
62401
|
+
*/
|
|
62402
|
+
analyzeFormula(formula, currentSheet) {
|
|
62403
|
+
let deps;
|
|
62404
|
+
try {
|
|
62405
|
+
const sheet = this.workbook.sheets.find((s) => s.name === currentSheet);
|
|
62406
|
+
const expanded = sheet ? expandStructuredRefs(formula, sheet.tables, 1) : formula;
|
|
62407
|
+
deps = this.depParser.parse(expanded, { sheet: currentSheet, row: 1, col: 1 });
|
|
62408
|
+
} catch {
|
|
62409
|
+
return { deps: [], readable: false };
|
|
62410
|
+
}
|
|
62277
62411
|
const refs = [];
|
|
62278
62412
|
for (const dep of deps) {
|
|
62279
62413
|
const depSheet = dep.sheet ?? currentSheet;
|
|
62280
62414
|
if (dep.from && dep.to) {
|
|
62281
|
-
const
|
|
62282
|
-
|
|
62283
|
-
|
|
62284
|
-
|
|
62415
|
+
const rows = dep.to.row - dep.from.row + 1;
|
|
62416
|
+
const cols = dep.to.col - dep.from.col + 1;
|
|
62417
|
+
if (rows * cols > MAX_EXPANDED_CELLS) {
|
|
62418
|
+
if (cols <= rows) {
|
|
62419
|
+
for (let c = dep.from.col; c <= dep.to.col; c++) {
|
|
62420
|
+
refs.push(columnKey(depSheet, colNumToLetters(c)));
|
|
62421
|
+
}
|
|
62422
|
+
} else {
|
|
62423
|
+
for (let r = dep.from.row; r <= dep.to.row; r++) {
|
|
62424
|
+
refs.push(rowKey(depSheet, r));
|
|
62425
|
+
}
|
|
62426
|
+
}
|
|
62427
|
+
continue;
|
|
62428
|
+
}
|
|
62429
|
+
for (let r = dep.from.row; r <= dep.to.row; r++) {
|
|
62430
|
+
for (let c = dep.from.col; c <= dep.to.col; c++) {
|
|
62285
62431
|
refs.push(cellKey({ sheet: depSheet, ref: rowColToRef(r, c) }));
|
|
62286
|
-
count++;
|
|
62287
62432
|
}
|
|
62288
62433
|
}
|
|
62289
62434
|
} else if (dep.row != null && dep.col != null) {
|
|
62290
62435
|
refs.push(cellKey({ sheet: depSheet, ref: rowColToRef(dep.row, dep.col) }));
|
|
62291
62436
|
}
|
|
62292
62437
|
}
|
|
62293
|
-
return refs;
|
|
62438
|
+
return { deps: refs, readable: true };
|
|
62439
|
+
}
|
|
62440
|
+
/** Formula cells the parser could not read, by key. Read by the recompute report. */
|
|
62441
|
+
unreadable() {
|
|
62442
|
+
return this.unreadableFormulas;
|
|
62294
62443
|
}
|
|
62295
62444
|
// ===========================================================================
|
|
62296
62445
|
// Cell value resolution callbacks
|
|
@@ -62320,8 +62469,7 @@ ${e.toString()}`);
|
|
|
62320
62469
|
for (let r = ref2.from.row; r <= lastRow; r++) {
|
|
62321
62470
|
const row = [];
|
|
62322
62471
|
for (let c = ref2.from.col; c <= lastCol; c++) {
|
|
62323
|
-
|
|
62324
|
-
row.push(cell?.value ?? null);
|
|
62472
|
+
row.push(sheet.getCellAt(r, c)?.value ?? null);
|
|
62325
62473
|
}
|
|
62326
62474
|
result.push(row);
|
|
62327
62475
|
}
|
|
@@ -62411,15 +62559,19 @@ ${e.toString()}`);
|
|
|
62411
62559
|
const engine = new FormulaEngine(workbook);
|
|
62412
62560
|
engine.buildGraph();
|
|
62413
62561
|
engine.evaluateAll();
|
|
62414
|
-
return collectRecomputeReport(workbook);
|
|
62562
|
+
return collectRecomputeReport(workbook, engine.unreadable());
|
|
62415
62563
|
}
|
|
62416
|
-
function collectRecomputeReport(workbook) {
|
|
62564
|
+
function collectRecomputeReport(workbook, unreadable) {
|
|
62417
62565
|
let total = 0;
|
|
62418
62566
|
const unevaluated = [];
|
|
62419
62567
|
for (const sheet of workbook.sheets) {
|
|
62420
62568
|
for (const [ref2, cell] of sheet.cells) {
|
|
62421
62569
|
if (!cell.formula) continue;
|
|
62422
62570
|
total++;
|
|
62571
|
+
if (unreadable.has(cellKey({ sheet: sheet.name, ref: ref2 }))) {
|
|
62572
|
+
unevaluated.push({ sheet: sheet.name, ref: ref2, formula: cell.formula, error: "#UNREADABLE!" });
|
|
62573
|
+
continue;
|
|
62574
|
+
}
|
|
62423
62575
|
if (cell.value === null) {
|
|
62424
62576
|
unevaluated.push({ sheet: sheet.name, ref: ref2, formula: cell.formula, error: cell.error ?? "#ERROR!" });
|
|
62425
62577
|
}
|