@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/dist/src/cli.js
CHANGED
|
@@ -16769,7 +16769,7 @@ var require_operators = __commonJS({
|
|
|
16769
16769
|
}
|
|
16770
16770
|
};
|
|
16771
16771
|
var type2Number = { "boolean": 3, "string": 2, "number": 1 };
|
|
16772
|
-
var
|
|
16772
|
+
var Infix2 = {
|
|
16773
16773
|
compareOp: (value1, infix, value2, isArray1, isArray22) => {
|
|
16774
16774
|
if (value1 == null) value1 = 0;
|
|
16775
16775
|
if (value2 == null) value2 = 0;
|
|
@@ -16860,7 +16860,7 @@ var require_operators = __commonJS({
|
|
|
16860
16860
|
module2.exports = {
|
|
16861
16861
|
Prefix,
|
|
16862
16862
|
Postfix,
|
|
16863
|
-
Infix,
|
|
16863
|
+
Infix: Infix2,
|
|
16864
16864
|
Operators: {
|
|
16865
16865
|
compareOp: ["<", ">", "=", "<>", "<=", ">="],
|
|
16866
16866
|
concatOp: ["&"],
|
|
@@ -16875,7 +16875,7 @@ var require_math = __commonJS({
|
|
|
16875
16875
|
"../../node_modules/fast-formula-parser/formulas/functions/math.js"(exports2, module2) {
|
|
16876
16876
|
var FormulaError = require_error();
|
|
16877
16877
|
var { FormulaHelpers, Types, Factorials, Criteria } = require_helpers();
|
|
16878
|
-
var { Infix } = require_operators();
|
|
16878
|
+
var { Infix: Infix2 } = require_operators();
|
|
16879
16879
|
var H = FormulaHelpers;
|
|
16880
16880
|
var f = [];
|
|
16881
16881
|
var fd2 = [];
|
|
@@ -17398,7 +17398,7 @@ var require_math = __commonJS({
|
|
|
17398
17398
|
if (criteria.match === criteria.value.test(value2)) {
|
|
17399
17399
|
sum2 += valueToAdd;
|
|
17400
17400
|
}
|
|
17401
|
-
} else if (
|
|
17401
|
+
} else if (Infix2.compareOp(value2, criteria.op, criteria.value, Array.isArray(value2), isCriteriaArray)) {
|
|
17402
17402
|
sum2 += valueToAdd;
|
|
17403
17403
|
}
|
|
17404
17404
|
});
|
|
@@ -23678,7 +23678,7 @@ var require_statistical = __commonJS({
|
|
|
23678
23678
|
"../../node_modules/fast-formula-parser/formulas/functions/statistical.js"(exports2, module2) {
|
|
23679
23679
|
var FormulaError = require_error();
|
|
23680
23680
|
var { FormulaHelpers, Types, Criteria, Address } = require_helpers();
|
|
23681
|
-
var { Infix } = require_operators();
|
|
23681
|
+
var { Infix: Infix2 } = require_operators();
|
|
23682
23682
|
var H = FormulaHelpers;
|
|
23683
23683
|
var { DistributionFunctions } = require_distribution();
|
|
23684
23684
|
var StatisticalFunctions = {
|
|
@@ -23740,7 +23740,7 @@ var require_statistical = __commonJS({
|
|
|
23740
23740
|
sum2 += valueToAdd;
|
|
23741
23741
|
cnt++;
|
|
23742
23742
|
}
|
|
23743
|
-
} else if (
|
|
23743
|
+
} else if (Infix2.compareOp(value2, criteria.op, criteria.value, Array.isArray(value2), isCriteriaArray)) {
|
|
23744
23744
|
sum2 += valueToAdd;
|
|
23745
23745
|
cnt++;
|
|
23746
23746
|
}
|
|
@@ -23779,7 +23779,7 @@ var require_statistical = __commonJS({
|
|
|
23779
23779
|
if (criteria.op === "wc") {
|
|
23780
23780
|
if (criteria.match === criteria.value.test(value2))
|
|
23781
23781
|
cnt++;
|
|
23782
|
-
} else if (
|
|
23782
|
+
} else if (Infix2.compareOp(value2, criteria.op, criteria.value, Array.isArray(value2), isCriteriaArray)) {
|
|
23783
23783
|
cnt++;
|
|
23784
23784
|
}
|
|
23785
23785
|
});
|
|
@@ -33332,7 +33332,7 @@ var require_utils2 = __commonJS({
|
|
|
33332
33332
|
"../../node_modules/fast-formula-parser/grammar/utils.js"(exports2, module2) {
|
|
33333
33333
|
var FormulaError = require_error();
|
|
33334
33334
|
var { Address } = require_helpers();
|
|
33335
|
-
var { Prefix, Postfix, Infix, Operators } = require_operators();
|
|
33335
|
+
var { Prefix, Postfix, Infix: Infix2, Operators } = require_operators();
|
|
33336
33336
|
var Collection = require_collection();
|
|
33337
33337
|
var MAX_ROW2 = 1048576;
|
|
33338
33338
|
var MAX_COLUMN2 = 16384;
|
|
@@ -33456,11 +33456,11 @@ var require_utils2 = __commonJS({
|
|
|
33456
33456
|
if (this.isFormulaError(val2))
|
|
33457
33457
|
return val2;
|
|
33458
33458
|
if (Operators.compareOp.includes(infix))
|
|
33459
|
-
return
|
|
33459
|
+
return Infix2.compareOp(val1, infix, val2, isArray1, isArray22);
|
|
33460
33460
|
else if (Operators.concatOp.includes(infix))
|
|
33461
|
-
return
|
|
33461
|
+
return Infix2.concatOp(val1, infix, val2, isArray1, isArray22);
|
|
33462
33462
|
else if (Operators.mathOp.includes(infix))
|
|
33463
|
-
return
|
|
33463
|
+
return Infix2.mathOp(val1, infix, val2, isArray1, isArray22);
|
|
33464
33464
|
else
|
|
33465
33465
|
throw new Error(`Unrecognized infix: ${infix}`);
|
|
33466
33466
|
}
|
|
@@ -34012,7 +34012,7 @@ var require_utils3 = __commonJS({
|
|
|
34012
34012
|
"../../node_modules/fast-formula-parser/grammar/dependency/utils.js"(exports2, module2) {
|
|
34013
34013
|
var FormulaError = require_error();
|
|
34014
34014
|
var { FormulaHelpers, Types, Address } = require_helpers();
|
|
34015
|
-
var { Prefix, Postfix, Infix, Operators } = require_operators();
|
|
34015
|
+
var { Prefix, Postfix, Infix: Infix2, Operators } = require_operators();
|
|
34016
34016
|
var Collection = require_collection();
|
|
34017
34017
|
var MAX_ROW2 = 1048576;
|
|
34018
34018
|
var MAX_COLUMN2 = 16384;
|
|
@@ -45731,8 +45731,8 @@ function renderDocIndex(areas) {
|
|
|
45731
45731
|
lines.push(`
|
|
45732
45732
|
${a.pkg} ${a.version}`);
|
|
45733
45733
|
}
|
|
45734
|
-
const name2 = a.absPath.endsWith("AGENTS.md") ? `${a.area} \u2190 start here` : a.area;
|
|
45735
|
-
lines.push(` ${name2.padEnd(24)}${a.title}`);
|
|
45734
|
+
const name2 = a.absPath.endsWith("AGENTS.md") ? `${a.area} \u2190 start here` : a.pkg === "@lotics/cli" && a.area === "building_an_app" ? `${a.area} \u2190 read once, first` : a.area;
|
|
45735
|
+
lines.push(` ${name2.length >= 24 ? `${name2} ` : name2.padEnd(24)}${a.title}`);
|
|
45736
45736
|
}
|
|
45737
45737
|
lines.push("\n lotics docs <area> print one, e.g. `lotics docs ai`");
|
|
45738
45738
|
lines.push(" lotics docs <pkg>/<area> when two packages share a name");
|
|
@@ -46734,7 +46734,9 @@ router and render one screen directly.
|
|
|
46734
46734
|
|
|
46735
46735
|
Run \`lotics docs\` for the SDK reference \u2014 it lists what the packages installed
|
|
46736
46736
|
here actually ship, at the versions installed, so it can never point at a page
|
|
46737
|
-
that moved.
|
|
46737
|
+
that moved. \`lotics docs building_an_app\` is the sequence, read once before
|
|
46738
|
+
building; the area docs (\`lotics docs ai\`, \`lotics docs queries\`,
|
|
46739
|
+
\`lotics docs ui\`) are its detail.
|
|
46738
46740
|
`
|
|
46739
46741
|
}
|
|
46740
46742
|
];
|
|
@@ -72889,7 +72891,8 @@ async function appPull(client, args) {
|
|
|
72889
72891
|
console.error(`
|
|
72890
72892
|
Ready. Next steps:`);
|
|
72891
72893
|
console.error(` cd ${path9.relative(process.cwd(), targetPath) || "."}`);
|
|
72892
|
-
console.error(` #
|
|
72894
|
+
console.error(` lotics docs # the contracts, at the versions installed here`);
|
|
72895
|
+
console.error(` # edit src/App.tsx, then:`);
|
|
72893
72896
|
console.error(` lotics app deploy`);
|
|
72894
72897
|
}
|
|
72895
72898
|
function readAppSourceText(projectDir) {
|
|
@@ -73018,6 +73021,7 @@ async function appDeploy(client, args) {
|
|
|
73018
73021
|
}
|
|
73019
73022
|
if (liveAfter) {
|
|
73020
73023
|
warnIfUnbranded(liveAfter);
|
|
73024
|
+
warnIfUndescribed(liveAfter);
|
|
73021
73025
|
warnIfUnboundAliases(liveAfter, called);
|
|
73022
73026
|
warnIfInertAgentTools(liveAfter);
|
|
73023
73027
|
}
|
|
@@ -73056,6 +73060,15 @@ function warnIfUnbranded(app) {
|
|
|
73056
73060
|
Find an icon: lotics run search_app_icons '{"query":"<word>"}'`
|
|
73057
73061
|
);
|
|
73058
73062
|
}
|
|
73063
|
+
function warnIfUndescribed(app) {
|
|
73064
|
+
if (app.description && app.description.trim() !== "") return;
|
|
73065
|
+
console.error(
|
|
73066
|
+
`
|
|
73067
|
+
\u26A0 This app has no description \u2014 the capability catalog its chat agent reads every turn
|
|
73068
|
+
lists the aliases and says nothing about what to do with them.
|
|
73069
|
+
Set it: lotics run update_app '{"app_id":"${app.id}","description":"<what the app is for, and the standing job it does>"}'`
|
|
73070
|
+
);
|
|
73071
|
+
}
|
|
73059
73072
|
async function readLiveQueries(client, appId) {
|
|
73060
73073
|
try {
|
|
73061
73074
|
const app = await client.getApp(appId);
|
|
@@ -73163,6 +73176,7 @@ async function appCheck(client, args = {}) {
|
|
|
73163
73176
|
warnIfInertAgentTools(app);
|
|
73164
73177
|
warnIfProjectFootguns(projectDir, sourceText);
|
|
73165
73178
|
warnIfUnbranded(app);
|
|
73179
|
+
warnIfUndescribed(app);
|
|
73166
73180
|
if (!nothingPending(pending)) {
|
|
73167
73181
|
process.exit(1);
|
|
73168
73182
|
}
|
|
@@ -84774,14 +84788,14 @@ function readSourceData(workbook, cache) {
|
|
|
84774
84788
|
if (!range2) return void 0;
|
|
84775
84789
|
const header = [];
|
|
84776
84790
|
for (let col = range2.startCol; col <= range2.endCol; col++) {
|
|
84777
|
-
const cell = sheet.
|
|
84791
|
+
const cell = sheet.getCellAt(range2.startRow, col);
|
|
84778
84792
|
header.push(formatHeader(cell?.value));
|
|
84779
84793
|
}
|
|
84780
84794
|
const records = [];
|
|
84781
84795
|
for (let row = range2.startRow + 1; row <= range2.endRow; row++) {
|
|
84782
84796
|
const rec = [];
|
|
84783
84797
|
for (let col = range2.startCol; col <= range2.endCol; col++) {
|
|
84784
|
-
const cell = sheet.
|
|
84798
|
+
const cell = sheet.getCellAt(row, col);
|
|
84785
84799
|
rec.push(coerceValue(cell?.value));
|
|
84786
84800
|
}
|
|
84787
84801
|
records.push(rec);
|
|
@@ -86325,23 +86339,70 @@ var CellMap = class {
|
|
|
86325
86339
|
get size() {
|
|
86326
86340
|
return this.store.size;
|
|
86327
86341
|
}
|
|
86342
|
+
/**
|
|
86343
|
+
* The populated extent of this map, memoized until a mutation invalidates it.
|
|
86344
|
+
*
|
|
86345
|
+
* It lives HERE rather than on `SheetModel` because it is a pure function of
|
|
86346
|
+
* the cell set: a cache held next to the data cannot outlive it, so replacing
|
|
86347
|
+
* a sheet's map can never serve the previous map's extent. Every mutator
|
|
86348
|
+
* below drops it, including the packed pair — those bypass `set`/`delete`
|
|
86349
|
+
* entirely and are what the row/column commands use.
|
|
86350
|
+
*
|
|
86351
|
+
* Walking PACKED keys is the other half. `keys()` unpacks each key into an A1
|
|
86352
|
+
* string, which the caller then reparsed with a regex, so the old form packed,
|
|
86353
|
+
* unpacked and reparsed the whole sheet on every call — and it is called once
|
|
86354
|
+
* per oversized range during a recompute.
|
|
86355
|
+
*/
|
|
86356
|
+
usedRange() {
|
|
86357
|
+
if (!this.usedRangeCache) {
|
|
86358
|
+
let minRow2 = Infinity;
|
|
86359
|
+
let maxRow2 = 0;
|
|
86360
|
+
let minCol2 = Infinity;
|
|
86361
|
+
let maxCol2 = 0;
|
|
86362
|
+
for (const packed of this.store.keys()) {
|
|
86363
|
+
const { row, col } = unpackRowCol(packed);
|
|
86364
|
+
if (row < minRow2) minRow2 = row;
|
|
86365
|
+
if (row > maxRow2) maxRow2 = row;
|
|
86366
|
+
if (col < minCol2) minCol2 = col;
|
|
86367
|
+
if (col > maxCol2) maxCol2 = col;
|
|
86368
|
+
}
|
|
86369
|
+
this.usedRangeCache = maxRow2 === 0 ? { minRow: 1, maxRow: 1, minCol: 1, maxCol: 1 } : { minRow: minRow2, maxRow: maxRow2, minCol: minCol2, maxCol: maxCol2 };
|
|
86370
|
+
}
|
|
86371
|
+
const { minRow, maxRow, minCol, maxCol } = this.usedRangeCache;
|
|
86372
|
+
return { minRow, maxRow, minCol, maxCol };
|
|
86373
|
+
}
|
|
86328
86374
|
get(ref2) {
|
|
86329
86375
|
return this.store.get(packRef(ref2));
|
|
86330
86376
|
}
|
|
86377
|
+
/**
|
|
86378
|
+
* Read by coordinate, skipping the A1 round trip.
|
|
86379
|
+
*
|
|
86380
|
+
* `get(ref)` costs a number→string→regex→number conversion per call, which
|
|
86381
|
+
* is invisible until a caller reads a range cell by cell: on one real
|
|
86382
|
+
* workbook `refToRowCol` alone was 39% of a full recompute. A caller already
|
|
86383
|
+
* holding (row, col) — the range resolver, any grid walk — should use this.
|
|
86384
|
+
*/
|
|
86385
|
+
getAt(row, col) {
|
|
86386
|
+
return this.store.get(packRowCol(row, col));
|
|
86387
|
+
}
|
|
86331
86388
|
set(ref2, cell) {
|
|
86332
86389
|
const key = packRef(ref2);
|
|
86333
86390
|
if (key < 0) return this;
|
|
86334
86391
|
this.store.set(key, cell);
|
|
86392
|
+
this.usedRangeCache = void 0;
|
|
86335
86393
|
return this;
|
|
86336
86394
|
}
|
|
86337
86395
|
has(ref2) {
|
|
86338
86396
|
return this.store.has(packRef(ref2));
|
|
86339
86397
|
}
|
|
86340
86398
|
delete(ref2) {
|
|
86341
|
-
|
|
86399
|
+
const deleted = this.store.delete(packRef(ref2));
|
|
86400
|
+
if (deleted) this.usedRangeCache = void 0;
|
|
86401
|
+
return deleted;
|
|
86342
86402
|
}
|
|
86343
86403
|
clear() {
|
|
86344
86404
|
this.store.clear();
|
|
86405
|
+
this.usedRangeCache = void 0;
|
|
86345
86406
|
}
|
|
86346
86407
|
*keys() {
|
|
86347
86408
|
for (const k of this.store.keys()) yield unpackRef(k);
|
|
@@ -86369,10 +86430,13 @@ var CellMap = class {
|
|
|
86369
86430
|
/** Direct packed-key set, for the same hot loops. */
|
|
86370
86431
|
setPacked(packed, cell) {
|
|
86371
86432
|
this.store.set(packed, cell);
|
|
86433
|
+
this.usedRangeCache = void 0;
|
|
86372
86434
|
}
|
|
86373
86435
|
/** Direct packed-key delete. */
|
|
86374
86436
|
deletePacked(packed) {
|
|
86375
|
-
|
|
86437
|
+
const deleted = this.store.delete(packed);
|
|
86438
|
+
if (deleted) this.usedRangeCache = void 0;
|
|
86439
|
+
return deleted;
|
|
86376
86440
|
}
|
|
86377
86441
|
};
|
|
86378
86442
|
var StyleRegistry = class {
|
|
@@ -86555,6 +86619,10 @@ var SheetModel = class {
|
|
|
86555
86619
|
getCell(ref2) {
|
|
86556
86620
|
return this.cells.get(ref2);
|
|
86557
86621
|
}
|
|
86622
|
+
/** Read by coordinate, skipping the A1 round trip — see `CellMap.getAt`. */
|
|
86623
|
+
getCellAt(row, col) {
|
|
86624
|
+
return this.cells.getAt(row, col);
|
|
86625
|
+
}
|
|
86558
86626
|
/**
|
|
86559
86627
|
* Set cell value with optional inline style. Interns style via the
|
|
86560
86628
|
* StyleRegistry when available. When `style` is supplied and matches the
|
|
@@ -86649,22 +86717,12 @@ var SheetModel = class {
|
|
|
86649
86717
|
this.set(ref2, label, style);
|
|
86650
86718
|
});
|
|
86651
86719
|
}
|
|
86652
|
-
/**
|
|
86720
|
+
/**
|
|
86721
|
+
* The populated extent of the sheet. Derived from — and cached by — the cell
|
|
86722
|
+
* map, so it cannot outlive the data it describes.
|
|
86723
|
+
*/
|
|
86653
86724
|
getUsedRange() {
|
|
86654
|
-
|
|
86655
|
-
let maxRow = 0;
|
|
86656
|
-
let minCol = Infinity;
|
|
86657
|
-
let maxCol = 0;
|
|
86658
|
-
for (const ref2 of this.cells.keys()) {
|
|
86659
|
-
const rc = refToRowCol(ref2);
|
|
86660
|
-
if (!rc) continue;
|
|
86661
|
-
if (rc.row < minRow) minRow = rc.row;
|
|
86662
|
-
if (rc.row > maxRow) maxRow = rc.row;
|
|
86663
|
-
if (rc.col < minCol) minCol = rc.col;
|
|
86664
|
-
if (rc.col > maxCol) maxCol = rc.col;
|
|
86665
|
-
}
|
|
86666
|
-
if (maxRow === 0) return { minRow: 1, maxRow: 1, minCol: 1, maxCol: 1 };
|
|
86667
|
-
return { minRow, maxRow, minCol, maxCol };
|
|
86725
|
+
return this.cells.usedRange();
|
|
86668
86726
|
}
|
|
86669
86727
|
};
|
|
86670
86728
|
var WorkbookModel = class {
|
|
@@ -86911,7 +86969,7 @@ function applyAutoFilterHiddenRows(sheet) {
|
|
|
86911
86969
|
let visible = true;
|
|
86912
86970
|
for (const filterCol of activeFilters) {
|
|
86913
86971
|
const col = startCol + filterCol.colIndex;
|
|
86914
|
-
const cell = sheet.
|
|
86972
|
+
const cell = sheet.getCellAt(r, col);
|
|
86915
86973
|
const cellText = cell?.displayValue ?? "";
|
|
86916
86974
|
if (filterCol.filterValues && filterCol.filterValues.length > 0) {
|
|
86917
86975
|
if (!filterCol.filterValues.includes(cellText)) {
|
|
@@ -94141,6 +94199,28 @@ function parseCellKey(key) {
|
|
|
94141
94199
|
const bang = key.indexOf("!");
|
|
94142
94200
|
return { sheet: key.slice(0, bang), ref: key.slice(bang + 1) };
|
|
94143
94201
|
}
|
|
94202
|
+
var COARSE_COL_SUFFIX = ":*";
|
|
94203
|
+
var COARSE_ROW_PREFIX = "*:";
|
|
94204
|
+
function columnKey(sheet, column) {
|
|
94205
|
+
return `${sheet}!${column}${COARSE_COL_SUFFIX}`;
|
|
94206
|
+
}
|
|
94207
|
+
function rowKey(sheet, row) {
|
|
94208
|
+
return `${sheet}!${COARSE_ROW_PREFIX}${row}`;
|
|
94209
|
+
}
|
|
94210
|
+
function isCoarseKey(key) {
|
|
94211
|
+
return key.endsWith(COARSE_COL_SUFFIX) || key.includes(`!${COARSE_ROW_PREFIX}`);
|
|
94212
|
+
}
|
|
94213
|
+
var A1 = /^([A-Z]+)(\d+)$/;
|
|
94214
|
+
function coarseKeysFor(cellKeyStr) {
|
|
94215
|
+
const bang = cellKeyStr.indexOf("!");
|
|
94216
|
+
if (bang < 0) return [];
|
|
94217
|
+
const sheet = cellKeyStr.slice(0, bang);
|
|
94218
|
+
const ref2 = cellKeyStr.slice(bang + 1);
|
|
94219
|
+
const m = A1.exec(ref2);
|
|
94220
|
+
if (!m) return [];
|
|
94221
|
+
return [columnKey(sheet, m[1]), rowKey(sheet, Number(m[2]))];
|
|
94222
|
+
}
|
|
94223
|
+
var EMPTY = /* @__PURE__ */ new Set();
|
|
94144
94224
|
var DependencyGraph = class {
|
|
94145
94225
|
constructor() {
|
|
94146
94226
|
// Forward edges: cell → cells it references (precedents)
|
|
@@ -94175,19 +94255,35 @@ var DependencyGraph = class {
|
|
|
94175
94255
|
}
|
|
94176
94256
|
/** Get all cells that directly depend on the given cell. */
|
|
94177
94257
|
getDirectDependents(cell) {
|
|
94178
|
-
return Array.from(this.
|
|
94258
|
+
return Array.from(this.dependentsOf(cell));
|
|
94259
|
+
}
|
|
94260
|
+
/**
|
|
94261
|
+
* Everything that references this cell — directly, or through the column or
|
|
94262
|
+
* row node that contains it. The single place `dependents` is read, so a
|
|
94263
|
+
* whole-column reference cannot be forgotten by one traversal and honoured by
|
|
94264
|
+
* another.
|
|
94265
|
+
*/
|
|
94266
|
+
dependentsOf(cell) {
|
|
94267
|
+
const direct = this.dependents.get(cell);
|
|
94268
|
+
if (isCoarseKey(cell)) return direct ?? EMPTY;
|
|
94269
|
+
const coarse = coarseKeysFor(cell);
|
|
94270
|
+
let widened;
|
|
94271
|
+
for (const key of coarse) {
|
|
94272
|
+
const via = this.dependents.get(key);
|
|
94273
|
+
if (!via || via.size === 0) continue;
|
|
94274
|
+
widened ??= new Set(direct);
|
|
94275
|
+
for (const d of via) widened.add(d);
|
|
94276
|
+
}
|
|
94277
|
+
return widened ?? direct ?? EMPTY;
|
|
94179
94278
|
}
|
|
94180
94279
|
/** Get all cells that need recalculation when the given cells change, in topological order. */
|
|
94181
94280
|
getRecalcOrder(changed) {
|
|
94182
|
-
const changedSet = new Set(changed);
|
|
94183
94281
|
const affected = /* @__PURE__ */ new Set();
|
|
94184
94282
|
const queue = [...changed];
|
|
94185
94283
|
let head = 0;
|
|
94186
94284
|
while (head < queue.length) {
|
|
94187
94285
|
const cell = queue[head++];
|
|
94188
|
-
const
|
|
94189
|
-
if (!deps) continue;
|
|
94190
|
-
for (const dep of deps) {
|
|
94286
|
+
for (const dep of this.dependentsOf(cell)) {
|
|
94191
94287
|
if (!affected.has(dep)) {
|
|
94192
94288
|
affected.add(dep);
|
|
94193
94289
|
queue.push(dep);
|
|
@@ -94196,15 +94292,11 @@ var DependencyGraph = class {
|
|
|
94196
94292
|
}
|
|
94197
94293
|
if (affected.size === 0) return [];
|
|
94198
94294
|
const inDegree = /* @__PURE__ */ new Map();
|
|
94295
|
+
for (const cell of affected) inDegree.set(cell, 0);
|
|
94199
94296
|
for (const cell of affected) {
|
|
94200
|
-
|
|
94201
|
-
|
|
94202
|
-
if (prec) {
|
|
94203
|
-
for (const p of prec) {
|
|
94204
|
-
if (affected.has(p)) count++;
|
|
94205
|
-
}
|
|
94297
|
+
for (const dep of this.dependentsOf(cell)) {
|
|
94298
|
+
if (affected.has(dep)) inDegree.set(dep, (inDegree.get(dep) ?? 0) + 1);
|
|
94206
94299
|
}
|
|
94207
|
-
inDegree.set(cell, count);
|
|
94208
94300
|
}
|
|
94209
94301
|
const result = [];
|
|
94210
94302
|
const ready = [];
|
|
@@ -94215,9 +94307,7 @@ var DependencyGraph = class {
|
|
|
94215
94307
|
while (readyHead < ready.length) {
|
|
94216
94308
|
const cell = ready[readyHead++];
|
|
94217
94309
|
result.push(cell);
|
|
94218
|
-
const
|
|
94219
|
-
if (!deps) continue;
|
|
94220
|
-
for (const dep of deps) {
|
|
94310
|
+
for (const dep of this.dependentsOf(cell)) {
|
|
94221
94311
|
if (!affected.has(dep)) continue;
|
|
94222
94312
|
const newDeg = (inDegree.get(dep) ?? 1) - 1;
|
|
94223
94313
|
inDegree.set(dep, newDeg);
|
|
@@ -94238,12 +94328,13 @@ var DependencyGraph = class {
|
|
|
94238
94328
|
const stack = new Array();
|
|
94239
94329
|
const prec = this.precedents.get(cell);
|
|
94240
94330
|
if (!prec) return null;
|
|
94331
|
+
const originCoarse = new Set(coarseKeysFor(cell));
|
|
94241
94332
|
for (const p of prec) {
|
|
94242
94333
|
stack.push({ cell: p, path: [cell, p] });
|
|
94243
94334
|
}
|
|
94244
94335
|
while (stack.length > 0) {
|
|
94245
94336
|
const { cell: current, path: path12 } = stack.pop();
|
|
94246
|
-
if (current === cell) return path12;
|
|
94337
|
+
if (current === cell || originCoarse.has(current)) return path12;
|
|
94247
94338
|
if (visited.has(current)) continue;
|
|
94248
94339
|
visited.add(current);
|
|
94249
94340
|
const nextPrec = this.precedents.get(current);
|
|
@@ -94356,6 +94447,20 @@ function parseTableRange(ref2) {
|
|
|
94356
94447
|
};
|
|
94357
94448
|
}
|
|
94358
94449
|
|
|
94450
|
+
// ../xlsx/src/blank_comparison.ts
|
|
94451
|
+
var import_operators = __toESM(require_operators(), 1);
|
|
94452
|
+
var patched = false;
|
|
94453
|
+
function applyExcelBlankSemantics() {
|
|
94454
|
+
if (patched) return;
|
|
94455
|
+
patched = true;
|
|
94456
|
+
const original = import_operators.Infix.compareOp;
|
|
94457
|
+
import_operators.Infix.compareOp = (value1, infix, value2, isArray1, isArray22) => {
|
|
94458
|
+
if (value1 == null && typeof value2 === "string") value1 = "";
|
|
94459
|
+
if (value2 == null && typeof value1 === "string") value2 = "";
|
|
94460
|
+
return original(value1, infix, value2, isArray1, isArray22);
|
|
94461
|
+
};
|
|
94462
|
+
}
|
|
94463
|
+
|
|
94359
94464
|
// ../xlsx/src/formula_engine.ts
|
|
94360
94465
|
var unwrapArg = (a) => a !== null && typeof a === "object" && "value" in a ? a.value : a;
|
|
94361
94466
|
function adaptFormulajs(fn) {
|
|
@@ -94398,9 +94503,24 @@ function formulajsBridgeWithContext() {
|
|
|
94398
94503
|
bridgeCtxCache = withCtx;
|
|
94399
94504
|
return withCtx;
|
|
94400
94505
|
}
|
|
94506
|
+
var MAX_EXPANDED_CELLS = 1e4;
|
|
94401
94507
|
var FormulaEngine = class _FormulaEngine {
|
|
94402
94508
|
constructor(workbook) {
|
|
94403
94509
|
this.graph = new DependencyGraph();
|
|
94510
|
+
/**
|
|
94511
|
+
* Formula cells (by key) whose text the parser could not read AT ALL, found
|
|
94512
|
+
* while building the graph from a loaded file.
|
|
94513
|
+
*
|
|
94514
|
+
* They are held apart from every other failure because the honest response
|
|
94515
|
+
* differs: a formula that evaluates TO an error has been understood, so
|
|
94516
|
+
* writing `#DIV/0!` reports a real answer, while one we cannot parse has not
|
|
94517
|
+
* been — and overwriting the value its author computed would replace a
|
|
94518
|
+
* correct number with a guess. These keep their cached value and are named in
|
|
94519
|
+
* the report instead. The engine's own rule, one level down: degrade what you
|
|
94520
|
+
* cannot draw, refuse what you cannot read.
|
|
94521
|
+
*/
|
|
94522
|
+
this.unreadableFormulas = /* @__PURE__ */ new Set();
|
|
94523
|
+
applyExcelBlankSemantics();
|
|
94404
94524
|
this.workbook = workbook;
|
|
94405
94525
|
this.parser = new import_fast_formula_parser.default({
|
|
94406
94526
|
onCell: (ref2) => this.resolveCell(ref2),
|
|
@@ -94421,11 +94541,13 @@ var FormulaEngine = class _FormulaEngine {
|
|
|
94421
94541
|
/** Build dependency graph from all formula cells in the workbook. */
|
|
94422
94542
|
buildGraph() {
|
|
94423
94543
|
this.graph.clear();
|
|
94544
|
+
this.unreadableFormulas.clear();
|
|
94424
94545
|
for (const sheet of this.workbook.sheets) {
|
|
94425
94546
|
for (const [ref2, cell] of sheet.cells) {
|
|
94426
94547
|
if (!cell.formula) continue;
|
|
94427
94548
|
const key = cellKey({ sheet: sheet.name, ref: ref2 });
|
|
94428
|
-
const deps = this.
|
|
94549
|
+
const { deps, readable } = this.analyzeFormula(cell.formula, sheet.name);
|
|
94550
|
+
if (!readable) this.unreadableFormulas.add(key);
|
|
94429
94551
|
this.graph.setDependencies(key, deps);
|
|
94430
94552
|
}
|
|
94431
94553
|
}
|
|
@@ -94481,8 +94603,13 @@ var FormulaEngine = class _FormulaEngine {
|
|
|
94481
94603
|
onCellChanged(sheetName, ref2, formula) {
|
|
94482
94604
|
const key = cellKey({ sheet: sheetName, ref: ref2 });
|
|
94483
94605
|
if (formula) {
|
|
94484
|
-
const deps = this.
|
|
94606
|
+
const { deps, readable } = this.analyzeFormula(formula, sheetName);
|
|
94485
94607
|
this.graph.setDependencies(key, deps);
|
|
94608
|
+
this.unreadableFormulas.delete(key);
|
|
94609
|
+
if (!readable) {
|
|
94610
|
+
this.setCellError(key, "#ERROR!");
|
|
94611
|
+
return;
|
|
94612
|
+
}
|
|
94486
94613
|
const cycle = this.graph.detectCycle(key);
|
|
94487
94614
|
if (cycle) {
|
|
94488
94615
|
this.setCellError(key, "#CIRC!");
|
|
@@ -94491,6 +94618,7 @@ var FormulaEngine = class _FormulaEngine {
|
|
|
94491
94618
|
this.evaluateCell(key);
|
|
94492
94619
|
} else {
|
|
94493
94620
|
this.graph.removeCell(key);
|
|
94621
|
+
this.unreadableFormulas.delete(key);
|
|
94494
94622
|
}
|
|
94495
94623
|
const order = this.graph.getRecalcOrder([key]);
|
|
94496
94624
|
for (const depKey of order) {
|
|
@@ -94507,6 +94635,7 @@ var FormulaEngine = class _FormulaEngine {
|
|
|
94507
94635
|
// ===========================================================================
|
|
94508
94636
|
/** Evaluate a single formula cell and update its value. */
|
|
94509
94637
|
evaluateCell(key) {
|
|
94638
|
+
if (this.unreadableFormulas.has(key)) return;
|
|
94510
94639
|
const { sheet: sheetName, ref: ref2 } = parseCellKey(key);
|
|
94511
94640
|
const sheetIndex = this.workbook.sheets.findIndex((s) => s.name === sheetName);
|
|
94512
94641
|
if (sheetIndex < 0) return;
|
|
@@ -94555,26 +94684,60 @@ var FormulaEngine = class _FormulaEngine {
|
|
|
94555
94684
|
// ===========================================================================
|
|
94556
94685
|
/** Extract cell/range references from a formula string. Returns empty array for unparseable formulas. */
|
|
94557
94686
|
extractDependencies(formula, currentSheet) {
|
|
94558
|
-
|
|
94559
|
-
|
|
94560
|
-
|
|
94687
|
+
return this.analyzeFormula(formula, currentSheet).deps;
|
|
94688
|
+
}
|
|
94689
|
+
/**
|
|
94690
|
+
* Dependencies plus whether the parser could read the formula at all.
|
|
94691
|
+
*
|
|
94692
|
+
* `fast-formula-parser` reports a formula it cannot LEX by throwing, the same
|
|
94693
|
+
* channel it uses for `#VALUE!`, so the lexer failure of a single cell used to
|
|
94694
|
+
* escape `buildGraph` and abort the recompute for the entire workbook —
|
|
94695
|
+
* observed on a real workbook whose `VLOOKUP` pointed at another file
|
|
94696
|
+
* (`'[1]DATA'!B:C`, an external-workbook reference the lexer has no rule for).
|
|
94697
|
+
* Anything the grammar does not cover lands here the same way, so this catches
|
|
94698
|
+
* the class rather than that one syntax.
|
|
94699
|
+
*/
|
|
94700
|
+
analyzeFormula(formula, currentSheet) {
|
|
94701
|
+
let deps;
|
|
94702
|
+
try {
|
|
94703
|
+
const sheet = this.workbook.sheets.find((s) => s.name === currentSheet);
|
|
94704
|
+
const expanded = sheet ? expandStructuredRefs(formula, sheet.tables, 1) : formula;
|
|
94705
|
+
deps = this.depParser.parse(expanded, { sheet: currentSheet, row: 1, col: 1 });
|
|
94706
|
+
} catch {
|
|
94707
|
+
return { deps: [], readable: false };
|
|
94708
|
+
}
|
|
94561
94709
|
const refs = [];
|
|
94562
94710
|
for (const dep of deps) {
|
|
94563
94711
|
const depSheet = dep.sheet ?? currentSheet;
|
|
94564
94712
|
if (dep.from && dep.to) {
|
|
94565
|
-
const
|
|
94566
|
-
|
|
94567
|
-
|
|
94568
|
-
|
|
94713
|
+
const rows = dep.to.row - dep.from.row + 1;
|
|
94714
|
+
const cols = dep.to.col - dep.from.col + 1;
|
|
94715
|
+
if (rows * cols > MAX_EXPANDED_CELLS) {
|
|
94716
|
+
if (cols <= rows) {
|
|
94717
|
+
for (let c = dep.from.col; c <= dep.to.col; c++) {
|
|
94718
|
+
refs.push(columnKey(depSheet, colNumToLetters(c)));
|
|
94719
|
+
}
|
|
94720
|
+
} else {
|
|
94721
|
+
for (let r = dep.from.row; r <= dep.to.row; r++) {
|
|
94722
|
+
refs.push(rowKey(depSheet, r));
|
|
94723
|
+
}
|
|
94724
|
+
}
|
|
94725
|
+
continue;
|
|
94726
|
+
}
|
|
94727
|
+
for (let r = dep.from.row; r <= dep.to.row; r++) {
|
|
94728
|
+
for (let c = dep.from.col; c <= dep.to.col; c++) {
|
|
94569
94729
|
refs.push(cellKey({ sheet: depSheet, ref: rowColToRef(r, c) }));
|
|
94570
|
-
count++;
|
|
94571
94730
|
}
|
|
94572
94731
|
}
|
|
94573
94732
|
} else if (dep.row != null && dep.col != null) {
|
|
94574
94733
|
refs.push(cellKey({ sheet: depSheet, ref: rowColToRef(dep.row, dep.col) }));
|
|
94575
94734
|
}
|
|
94576
94735
|
}
|
|
94577
|
-
return refs;
|
|
94736
|
+
return { deps: refs, readable: true };
|
|
94737
|
+
}
|
|
94738
|
+
/** Formula cells the parser could not read, by key. Read by the recompute report. */
|
|
94739
|
+
unreadable() {
|
|
94740
|
+
return this.unreadableFormulas;
|
|
94578
94741
|
}
|
|
94579
94742
|
// ===========================================================================
|
|
94580
94743
|
// Cell value resolution callbacks
|
|
@@ -94611,8 +94774,7 @@ var FormulaEngine = class _FormulaEngine {
|
|
|
94611
94774
|
for (let r = ref2.from.row; r <= lastRow; r++) {
|
|
94612
94775
|
const row = [];
|
|
94613
94776
|
for (let c = ref2.from.col; c <= lastCol; c++) {
|
|
94614
|
-
|
|
94615
|
-
row.push(cell?.value ?? null);
|
|
94777
|
+
row.push(sheet.getCellAt(r, c)?.value ?? null);
|
|
94616
94778
|
}
|
|
94617
94779
|
result.push(row);
|
|
94618
94780
|
}
|
|
@@ -94696,15 +94858,19 @@ function recomputeAll(workbook) {
|
|
|
94696
94858
|
const engine = new FormulaEngine(workbook);
|
|
94697
94859
|
engine.buildGraph();
|
|
94698
94860
|
engine.evaluateAll();
|
|
94699
|
-
return collectRecomputeReport(workbook);
|
|
94861
|
+
return collectRecomputeReport(workbook, engine.unreadable());
|
|
94700
94862
|
}
|
|
94701
|
-
function collectRecomputeReport(workbook) {
|
|
94863
|
+
function collectRecomputeReport(workbook, unreadable) {
|
|
94702
94864
|
let total = 0;
|
|
94703
94865
|
const unevaluated = [];
|
|
94704
94866
|
for (const sheet of workbook.sheets) {
|
|
94705
94867
|
for (const [ref2, cell] of sheet.cells) {
|
|
94706
94868
|
if (!cell.formula) continue;
|
|
94707
94869
|
total++;
|
|
94870
|
+
if (unreadable.has(cellKey({ sheet: sheet.name, ref: ref2 }))) {
|
|
94871
|
+
unevaluated.push({ sheet: sheet.name, ref: ref2, formula: cell.formula, error: "#UNREADABLE!" });
|
|
94872
|
+
continue;
|
|
94873
|
+
}
|
|
94708
94874
|
if (cell.value === null) {
|
|
94709
94875
|
unevaluated.push({ sheet: sheet.name, ref: ref2, formula: cell.formula, error: cell.error ?? "#ERROR!" });
|
|
94710
94876
|
}
|
|
@@ -95642,9 +95808,9 @@ function applySheetCells(workbook, sheetIndex, def) {
|
|
|
95642
95808
|
}
|
|
95643
95809
|
}
|
|
95644
95810
|
if (def.rowHeights) {
|
|
95645
|
-
for (const [
|
|
95646
|
-
const row = Number(
|
|
95647
|
-
if (!Number.isInteger(row) || row < 1) fail2(`Invalid row in sheet "${def.name}".rowHeights: ${
|
|
95811
|
+
for (const [rowKey2, height] of Object.entries(def.rowHeights)) {
|
|
95812
|
+
const row = Number(rowKey2);
|
|
95813
|
+
if (!Number.isInteger(row) || row < 1) fail2(`Invalid row in sheet "${def.name}".rowHeights: ${rowKey2}`);
|
|
95648
95814
|
sheet.rowHeights.set(row, height);
|
|
95649
95815
|
}
|
|
95650
95816
|
}
|
|
@@ -102076,6 +102242,10 @@ USAGE
|
|
|
102076
102242
|
Tools are grouped by category (tables, records, views, etc.).
|
|
102077
102243
|
Query tools return IDs used as arguments to other tools.
|
|
102078
102244
|
|
|
102245
|
+
Building an APP? Read \`lotics docs building_an_app\` first \u2014 the sequence the
|
|
102246
|
+
\`lotics app *\` commands below go in \u2014 then \`lotics docs\` for the area
|
|
102247
|
+
references installed beside the project.
|
|
102248
|
+
|
|
102079
102249
|
COMMANDS
|
|
102080
102250
|
${renderCommandsHelp()}
|
|
102081
102251
|
|
package/dist/src/client.d.ts
CHANGED
|
@@ -224,6 +224,10 @@ export declare class LoticsClient {
|
|
|
224
224
|
getApp(app_id: string): Promise<{
|
|
225
225
|
id: string;
|
|
226
226
|
name: string;
|
|
227
|
+
/** What the app is for. Heads the capability catalog the member's chat agent
|
|
228
|
+
* reads every turn the app is open, so it is where a standing process is
|
|
229
|
+
* stated. Null when unset. */
|
|
230
|
+
description?: string | null;
|
|
227
231
|
workspace_id: string;
|
|
228
232
|
current_version_id: string | null;
|
|
229
233
|
/** Launcher icon — a Lucide name, or an image ref. Null when unset. */
|