@devmm/puredocs-excel-formula 1.0.5 → 1.0.7

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/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { toOADate, fromOADate, columnLetter, parseCellRef, cellRefFromRowCol } from '@devmm/puredocs-excel';
1
+ import { toOADate, fromOADate, columnLetter, parseCellRef, cellRefFromRowCol, shiftIndex, shiftFormulaRefs, shiftSpan, EXCEL_MAX_ROWS, EXCEL_MAX_COLUMNS, columnNumber } from '@devmm/puredocs-excel';
2
2
 
3
3
  var __typeError = (msg) => {
4
4
  throw TypeError(msg);
@@ -802,6 +802,19 @@ var SheetRangeReferenceNode = class extends FormulaNode {
802
802
  return ctx.getSheetRangeValues(this.sheetName, this.startRef, this.endRef);
803
803
  }
804
804
  };
805
+ var FullRangeReferenceNode = class extends FormulaNode {
806
+ constructor(startCol, endCol, startRow, endRow, sheetName) {
807
+ super();
808
+ this.startCol = startCol;
809
+ this.endCol = endCol;
810
+ this.startRow = startRow;
811
+ this.endRow = endRow;
812
+ this.sheetName = sheetName;
813
+ }
814
+ evaluate(ctx) {
815
+ return this.sheetName === void 0 ? ctx.getFullRangeValues(this.startCol, this.endCol, this.startRow, this.endRow) : ctx.getSheetFullRangeValues(this.sheetName, this.startCol, this.endCol, this.startRow, this.endRow);
816
+ }
817
+ };
805
818
  var NamedRangeNode = class extends FormulaNode {
806
819
  constructor(name) {
807
820
  super();
@@ -1021,7 +1034,7 @@ var CallNode = class extends FormulaNode {
1021
1034
  };
1022
1035
 
1023
1036
  // src/formula-parser.ts
1024
- var _tokens, _pos2, _FormulaParser_instances, current_get, advance_fn, expect_fn, parseComparison_fn, parseConcatenation_fn, parseAddSub_fn, parseMulDiv_fn, parseUnary_fn, parsePower_fn, parsePercent_fn, parsePostfix_fn, parsePrimary_fn, parseFunction_fn, parseArgList_fn;
1037
+ var _tokens, _pos2, _FormulaParser_instances, current_get, advance_fn, expect_fn, parseComparison_fn, parseConcatenation_fn, parseAddSub_fn, parseMulDiv_fn, parseUnary_fn, parsePower_fn, parsePercent_fn, parsePostfix_fn, parsePrimary_fn, tryParseFullRange_fn, parseFunction_fn, parseArgList_fn;
1025
1038
  var FormulaParser = class {
1026
1039
  constructor(tokens) {
1027
1040
  __privateAdd(this, _FormulaParser_instances);
@@ -1145,6 +1158,10 @@ parsePrimary_fn = function() {
1145
1158
  switch (token.type) {
1146
1159
  case 0 /* Number */: {
1147
1160
  __privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
1161
+ if (__privateGet(this, _FormulaParser_instances, current_get).type === 4 /* Colon */) {
1162
+ const full = __privateMethod(this, _FormulaParser_instances, tryParseFullRange_fn).call(this, token.value);
1163
+ if (full) return full;
1164
+ }
1148
1165
  const n = parseFloat(token.value);
1149
1166
  if (isNaN(n)) throw new FormulaException(`Invalid number: ${token.value}`);
1150
1167
  return new NumberNode(n);
@@ -1173,15 +1190,22 @@ parsePrimary_fn = function() {
1173
1190
  const sheetName = parts[0];
1174
1191
  const cellRef = parts[1] ?? "";
1175
1192
  if (__privateGet(this, _FormulaParser_instances, current_get).type === 4 /* Colon */) {
1193
+ const full = __privateMethod(this, _FormulaParser_instances, tryParseFullRange_fn).call(this, cellRef, sheetName);
1194
+ if (full) return full;
1176
1195
  __privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
1177
1196
  const end = __privateMethod(this, _FormulaParser_instances, expect_fn).call(this, 3 /* CellReference */);
1178
1197
  return new SheetRangeReferenceNode(sheetName, cellRef, end.value);
1179
1198
  }
1180
1199
  return new SheetCellReferenceNode(sheetName, cellRef);
1181
1200
  }
1182
- case 23 /* NamedRange */:
1201
+ case 23 /* NamedRange */: {
1183
1202
  __privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
1203
+ if (__privateGet(this, _FormulaParser_instances, current_get).type === 4 /* Colon */) {
1204
+ const full = __privateMethod(this, _FormulaParser_instances, tryParseFullRange_fn).call(this, token.value);
1205
+ if (full) return full;
1206
+ }
1184
1207
  return new NamedRangeNode(token.value);
1208
+ }
1185
1209
  case 24 /* SpillReference */:
1186
1210
  __privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
1187
1211
  return new SpillReferenceNode(token.value);
@@ -1199,6 +1223,28 @@ parsePrimary_fn = function() {
1199
1223
  );
1200
1224
  }
1201
1225
  };
1226
+ /**
1227
+ * Completes a whole-column or whole-row reference whose first half has already
1228
+ * been consumed and whose ':' is the current token. Returns null — consuming
1229
+ * nothing — when the two halves are not a matching pair of columns or rows, so
1230
+ * the caller can fall back to its normal interpretation.
1231
+ *
1232
+ * Both halves must be the same kind: `A:A` and `1:5` are ranges, `A:1` is not
1233
+ * a reference at all in Excel and must stay an error rather than becoming a
1234
+ * silently different region.
1235
+ */
1236
+ tryParseFullRange_fn = function(startText, sheetName) {
1237
+ const endToken = __privateGet(this, _tokens)[__privateGet(this, _pos2) + 1];
1238
+ if (!endToken) return null;
1239
+ const start = classifyBound(startText);
1240
+ const end = classifyBound(endToken.value);
1241
+ if (!start || !end || start.kind !== end.kind) return null;
1242
+ __privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
1243
+ __privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
1244
+ const lo = Math.min(start.index, end.index);
1245
+ const hi = Math.max(start.index, end.index);
1246
+ return start.kind === "col" ? new FullRangeReferenceNode(lo, hi, null, null, sheetName) : new FullRangeReferenceNode(null, null, lo, hi, sheetName);
1247
+ };
1202
1248
  parseFunction_fn = function() {
1203
1249
  const nameToken = __privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
1204
1250
  __privateMethod(this, _FormulaParser_instances, expect_fn).call(this, 6 /* LeftParen */);
@@ -1225,6 +1271,18 @@ parseArgList_fn = function() {
1225
1271
  __privateMethod(this, _FormulaParser_instances, expect_fn).call(this, 7 /* RightParen */);
1226
1272
  return args;
1227
1273
  };
1274
+ function classifyBound(text) {
1275
+ const bare = text.replace(/\$/g, "");
1276
+ if (/^\d+$/.test(bare)) {
1277
+ const row2 = parseInt(bare, 10);
1278
+ return row2 >= 1 && row2 <= EXCEL_MAX_ROWS ? { kind: "row", index: row2 } : null;
1279
+ }
1280
+ if (/^[A-Za-z]{1,3}$/.test(bare)) {
1281
+ const col = columnNumber(bare.toUpperCase());
1282
+ return col >= 1 && col <= EXCEL_MAX_COLUMNS ? { kind: "col", index: col } : null;
1283
+ }
1284
+ return null;
1285
+ }
1228
1286
  function comparisonOp(type) {
1229
1287
  switch (type) {
1230
1288
  case 16 /* Equal */:
@@ -1244,6 +1302,137 @@ function comparisonOp(type) {
1244
1302
  }
1245
1303
  }
1246
1304
 
1305
+ // src/criteria-index.ts
1306
+ var EPSILON = 1e-10;
1307
+ var indexes = /* @__PURE__ */ new WeakMap();
1308
+ function build(arr) {
1309
+ const byText = /* @__PURE__ */ new Map();
1310
+ const byNumber = /* @__PURE__ */ new Map();
1311
+ const classes = /* @__PURE__ */ new Map();
1312
+ const addToClass = (kind, v, i2) => {
1313
+ const group = classes.get(kind);
1314
+ if (group) group.positions.push(i2);
1315
+ else classes.set(kind, { positions: [i2], representative: v });
1316
+ };
1317
+ let i = 0;
1318
+ for (const v of arr.values()) {
1319
+ const textKey = v.asText().toUpperCase();
1320
+ const atText = byText.get(textKey);
1321
+ if (atText) atText.push(i);
1322
+ else byText.set(textKey, [i]);
1323
+ if (v.isNumber) {
1324
+ const n = v.numberValue;
1325
+ const atNum = byNumber.get(n);
1326
+ if (atNum) atNum.push(i);
1327
+ else byNumber.set(n, [i]);
1328
+ } else {
1329
+ addToClass(v.kind, v, i);
1330
+ }
1331
+ i++;
1332
+ }
1333
+ const sortedNumbers = Float64Array.from(byNumber.keys()).sort();
1334
+ const cumulative = new Int32Array(sortedNumbers.length + 1);
1335
+ for (let k = 0; k < sortedNumbers.length; k++) {
1336
+ cumulative[k + 1] = cumulative[k] + byNumber.get(sortedNumbers[k]).length;
1337
+ }
1338
+ return { byText, byNumber, sortedNumbers, cumulative, classes: [...classes.values()] };
1339
+ }
1340
+ function indexOf(arr) {
1341
+ let idx = indexes.get(arr);
1342
+ if (!idx) {
1343
+ idx = build(arr);
1344
+ indexes.set(arr, idx);
1345
+ }
1346
+ return idx;
1347
+ }
1348
+ function lowerBound(sorted, target) {
1349
+ let lo = 0, hi = sorted.length;
1350
+ while (lo < hi) {
1351
+ const mid2 = lo + hi >> 1;
1352
+ if (sorted[mid2] < target) lo = mid2 + 1;
1353
+ else hi = mid2;
1354
+ }
1355
+ return lo;
1356
+ }
1357
+ function upperBound(sorted, target) {
1358
+ let lo = 0, hi = sorted.length;
1359
+ while (lo < hi) {
1360
+ const mid2 = lo + hi >> 1;
1361
+ if (sorted[mid2] <= target) lo = mid2 + 1;
1362
+ else hi = mid2;
1363
+ }
1364
+ return lo;
1365
+ }
1366
+ function matchingPositions(criteriaRange, criteria, minSize) {
1367
+ if (!criteriaRange.isArray) return null;
1368
+ const arr = criteriaRange.arrayVal;
1369
+ if (arr.length < minSize) return null;
1370
+ if (criteria.kind === "equalsText") {
1371
+ return indexOf(arr).byText.get(criteria.text) ?? EMPTY;
1372
+ }
1373
+ if (criteria.kind === "equalsNumber") {
1374
+ const target = criteria.number;
1375
+ const idx = indexOf(arr);
1376
+ const exact2 = idx.byNumber.get(target);
1377
+ let near;
1378
+ const { sortedNumbers } = idx;
1379
+ for (let k = lowerBound(sortedNumbers, target - EPSILON); k < sortedNumbers.length; k++) {
1380
+ const value2 = sortedNumbers[k];
1381
+ if (value2 > target + EPSILON) break;
1382
+ if (value2 === target) continue;
1383
+ if (!criteria.test(FormulaValue.number(value2))) continue;
1384
+ (near ?? (near = [])).push(...idx.byNumber.get(value2));
1385
+ }
1386
+ const blanks = criteria.test(FormulaValue.blank) ? blankPositions(idx) : void 0;
1387
+ if (!near && !blanks?.length) return exact2 ?? EMPTY;
1388
+ const merged = [...exact2 ?? EMPTY, ...near ?? EMPTY, ...blanks ?? EMPTY];
1389
+ merged.sort((a, b) => a - b);
1390
+ return merged;
1391
+ }
1392
+ if (criteria.kind === "compareNumber") {
1393
+ return comparePositions(indexOf(arr), criteria, arr.length);
1394
+ }
1395
+ return null;
1396
+ }
1397
+ function comparePositions(idx, criteria, length) {
1398
+ const target = criteria.number;
1399
+ const { sortedNumbers } = idx;
1400
+ let from = 0;
1401
+ let to = sortedNumbers.length;
1402
+ switch (criteria.op) {
1403
+ case ">":
1404
+ from = upperBound(sortedNumbers, target);
1405
+ break;
1406
+ case ">=":
1407
+ from = lowerBound(sortedNumbers, target);
1408
+ break;
1409
+ case "<":
1410
+ to = lowerBound(sortedNumbers, target);
1411
+ break;
1412
+ default:
1413
+ to = upperBound(sortedNumbers, target);
1414
+ break;
1415
+ }
1416
+ const budget = length >> 1;
1417
+ const matchedClasses = idx.classes.filter((g) => criteria.test(g.representative));
1418
+ let total = idx.cumulative[to] - idx.cumulative[from];
1419
+ for (const group of matchedClasses) total += group.positions.length;
1420
+ if (total > budget) return null;
1421
+ const positions = [];
1422
+ for (let k = from; k < to; k++) {
1423
+ for (const i of idx.byNumber.get(sortedNumbers[k])) positions.push(i);
1424
+ }
1425
+ for (const group of matchedClasses) {
1426
+ for (const i of group.positions) positions.push(i);
1427
+ }
1428
+ positions.sort((a, b) => a - b);
1429
+ return positions;
1430
+ }
1431
+ function blankPositions(idx) {
1432
+ return idx.classes.find((g) => g.representative.isBlank)?.positions;
1433
+ }
1434
+ var EMPTY = [];
1435
+
1247
1436
  // src/functions/math-functions.ts
1248
1437
  function registerMath(r) {
1249
1438
  r.register("SUM", sum, 1);
@@ -1305,34 +1494,72 @@ function valueAt(v, i) {
1305
1494
  function lengthOf(v) {
1306
1495
  return v.isArray ? v.arrayVal.length : 1;
1307
1496
  }
1308
- function computeIfsMask(a, c, startIdx, len2) {
1309
- const mask = new Uint8Array(len2).fill(1);
1497
+ var INDEX_MIN_RANGE = 32;
1498
+ function resolveIfsSelection(a, c, startIdx, len2) {
1499
+ const ranges = [];
1500
+ const criteria = [];
1501
+ let seed;
1502
+ let seedPair = -1;
1310
1503
  for (let p = startIdx; p + 1 < a.length; p += 2) {
1311
1504
  const cr = a[p].evaluate(c);
1312
1505
  if (cr.isError) return { ok: false, error: cr };
1313
1506
  const crit = a[p + 1].evaluate(c);
1314
1507
  if (crit.isError) return { ok: false, error: crit };
1315
- const critText = crit.asText();
1508
+ const compiled = FormulaHelper.compileCriteria(crit.asText());
1509
+ ranges.push(cr);
1510
+ criteria.push(compiled);
1511
+ if (seed === void 0) {
1512
+ const positions = matchingPositions(cr, compiled, INDEX_MIN_RANGE);
1513
+ if (positions) {
1514
+ seed = positions;
1515
+ seedPair = ranges.length - 1;
1516
+ }
1517
+ }
1518
+ }
1519
+ if (seed !== void 0) {
1520
+ let candidates = [];
1521
+ for (const i of seed) if (i < len2) candidates.push(i);
1522
+ for (let k = 0; k < ranges.length && candidates.length > 0; k++) {
1523
+ if (k === seedPair) continue;
1524
+ const range = ranges[k];
1525
+ const test = criteria[k].test;
1526
+ const next = [];
1527
+ for (const i of candidates) if (test(valueAt(range, i))) next.push(i);
1528
+ candidates = next;
1529
+ }
1530
+ return { ok: true, positions: candidates };
1531
+ }
1532
+ const mask = new Uint8Array(len2).fill(1);
1533
+ for (let k = 0; k < ranges.length; k++) {
1534
+ const range = ranges[k];
1535
+ const test = criteria[k].test;
1316
1536
  for (let i = 0; i < len2; i++) {
1317
1537
  if (!mask[i]) continue;
1318
- if (!FormulaHelper.matchesCriteria(valueAt(cr, i), critText)) mask[i] = 0;
1538
+ if (!test(valueAt(range, i))) mask[i] = 0;
1319
1539
  }
1320
1540
  }
1321
1541
  return { ok: true, mask };
1322
1542
  }
1543
+ function forEachSelected(sel, len2, visit) {
1544
+ if ("positions" in sel) {
1545
+ for (const i of sel.positions) visit(i);
1546
+ return;
1547
+ }
1548
+ const { mask } = sel;
1549
+ for (let i = 0; i < len2; i++) if (mask[i]) visit(i);
1550
+ }
1323
1551
  function sumIfs(a, c) {
1324
1552
  if (a.length < 3 || a.length % 2 === 0) return FormulaValue.errorValue;
1325
1553
  const sumRange = a[0].evaluate(c);
1326
1554
  if (sumRange.isError) return sumRange;
1327
1555
  const len2 = lengthOf(sumRange);
1328
- const m = computeIfsMask(a, c, 1, len2);
1329
- if (!m.ok) return m.error;
1556
+ const sel = resolveIfsSelection(a, c, 1, len2);
1557
+ if (!sel.ok) return sel.error;
1330
1558
  let total = 0;
1331
- for (let i = 0; i < len2; i++) {
1332
- if (!m.mask[i]) continue;
1559
+ forEachSelected(sel, len2, (i) => {
1333
1560
  const r = valueAt(sumRange, i).tryAsDouble();
1334
1561
  if (r.ok) total += r.value;
1335
- }
1562
+ });
1336
1563
  return FormulaValue.number(total);
1337
1564
  }
1338
1565
  function countIfs(a, c) {
@@ -1340,10 +1567,12 @@ function countIfs(a, c) {
1340
1567
  const first = a[0].evaluate(c);
1341
1568
  if (first.isError) return first;
1342
1569
  const len2 = lengthOf(first);
1343
- const m = computeIfsMask(a, c, 0, len2);
1344
- if (!m.ok) return m.error;
1570
+ const sel = resolveIfsSelection(a, c, 0, len2);
1571
+ if (!sel.ok) return sel.error;
1345
1572
  let n = 0;
1346
- for (let i = 0; i < len2; i++) if (m.mask[i]) n++;
1573
+ forEachSelected(sel, len2, () => {
1574
+ n++;
1575
+ });
1347
1576
  return FormulaValue.number(n);
1348
1577
  }
1349
1578
  function averageIfs(a, c) {
@@ -1351,17 +1580,16 @@ function averageIfs(a, c) {
1351
1580
  const avgRange = a[0].evaluate(c);
1352
1581
  if (avgRange.isError) return avgRange;
1353
1582
  const len2 = lengthOf(avgRange);
1354
- const m = computeIfsMask(a, c, 1, len2);
1355
- if (!m.ok) return m.error;
1583
+ const sel = resolveIfsSelection(a, c, 1, len2);
1584
+ if (!sel.ok) return sel.error;
1356
1585
  let total = 0, cnt = 0;
1357
- for (let i = 0; i < len2; i++) {
1358
- if (!m.mask[i]) continue;
1586
+ forEachSelected(sel, len2, (i) => {
1359
1587
  const r = valueAt(avgRange, i).tryAsDouble();
1360
1588
  if (r.ok) {
1361
1589
  total += r.value;
1362
1590
  cnt++;
1363
1591
  }
1364
- }
1592
+ });
1365
1593
  return cnt === 0 ? FormulaValue.errorDiv0 : FormulaValue.number(total / cnt);
1366
1594
  }
1367
1595
  function sumSq(a, c) {
@@ -4185,39 +4413,43 @@ var FormulaHelper;
4185
4413
  return result;
4186
4414
  }
4187
4415
  FormulaHelper2.flattenArgs = flattenArgs;
4188
- function matchesCriteria(value2, criteria) {
4189
- criteria = criteria.trim();
4190
- const opMatch = /^(>=|<=|<>|>|<|=)(.*)$/.exec(criteria);
4416
+ function compileCriteria(criteria) {
4417
+ const trimmed = criteria.trim();
4418
+ const opMatch = /^(>=|<=|<>|>|<|=)(.*)$/.exec(trimmed);
4191
4419
  if (opMatch) {
4192
4420
  const op = opMatch[1];
4193
4421
  const rhs = opMatch[2];
4194
4422
  const rhsVal = parseFloat(rhs);
4195
4423
  const rhsNum = isNaN(rhsVal) ? null : rhsVal;
4196
- const cmp = rhsNum !== null ? FormulaValue.compare(value2, FormulaValue.number(rhsNum)) : FormulaValue.compare(value2, FormulaValue.text(rhs));
4197
- switch (op) {
4198
- case ">":
4199
- return cmp > 0;
4200
- case ">=":
4201
- return cmp >= 0;
4202
- case "<":
4203
- return cmp < 0;
4204
- case "<=":
4205
- return cmp <= 0;
4206
- case "<>":
4207
- return !FormulaValue.areEqual(value2, rhsNum !== null ? FormulaValue.number(rhsNum) : FormulaValue.text(rhs));
4208
- case "=":
4209
- return FormulaValue.areEqual(value2, rhsNum !== null ? FormulaValue.number(rhsNum) : FormulaValue.text(rhs));
4424
+ const rhsValue = rhsNum !== null ? FormulaValue.number(rhsNum) : FormulaValue.text(rhs);
4425
+ if (op === "=") {
4426
+ return rhsNum !== null ? numberEquality(rhsNum) : textEquality(rhs);
4210
4427
  }
4428
+ if (op === "<>") {
4429
+ return { kind: "other", test: (v) => !FormulaValue.areEqual(v, rhsValue) };
4430
+ }
4431
+ const wanted = op === ">" ? (c) => c > 0 : op === ">=" ? (c) => c >= 0 : op === "<" ? (c) => c < 0 : (c) => c <= 0;
4432
+ const test = (v) => wanted(FormulaValue.compare(v, rhsValue));
4433
+ return rhsNum !== null ? { kind: "compareNumber", number: rhsNum, op, test } : { kind: "other", test };
4211
4434
  }
4212
- if (criteria.includes("*") || criteria.includes("?")) {
4213
- const pattern = wildcardToRegex(criteria);
4214
- return pattern.test(value2.asText());
4215
- }
4216
- const num5 = parseFloat(criteria);
4217
- if (!isNaN(num5)) {
4218
- return FormulaValue.areEqual(value2, FormulaValue.number(num5));
4435
+ if (trimmed.includes("*") || trimmed.includes("?")) {
4436
+ const pattern = wildcardToRegex(trimmed);
4437
+ return { kind: "other", test: (v) => pattern.test(v.asText()) };
4219
4438
  }
4220
- return value2.asText().toUpperCase() === criteria.toUpperCase();
4439
+ const num5 = parseFloat(trimmed);
4440
+ return isNaN(num5) ? textEquality(trimmed) : numberEquality(num5);
4441
+ }
4442
+ FormulaHelper2.compileCriteria = compileCriteria;
4443
+ function numberEquality(n) {
4444
+ const target = FormulaValue.number(n);
4445
+ return { kind: "equalsNumber", number: n, test: (v) => FormulaValue.areEqual(v, target) };
4446
+ }
4447
+ function textEquality(s) {
4448
+ const upper2 = s.toUpperCase();
4449
+ return { kind: "equalsText", text: upper2, test: (v) => v.asText().toUpperCase() === upper2 };
4450
+ }
4451
+ function matchesCriteria(value2, criteria) {
4452
+ return compileCriteria(criteria).test(value2);
4221
4453
  }
4222
4454
  FormulaHelper2.matchesCriteria = matchesCriteria;
4223
4455
  function wildcardToRegex(pattern) {
@@ -4227,7 +4459,7 @@ var FormulaHelper;
4227
4459
  })(FormulaHelper || (FormulaHelper = {}));
4228
4460
 
4229
4461
  // src/formula-context.ts
4230
- var _sheet, _functions2, _evaluating, _scopes, _FormulaContext_instances, lookupScope_fn;
4462
+ var _sheet, _functions2, _evaluating, _scopes, _FormulaContext_instances, lookupScope_fn, forSheet_fn;
4231
4463
  var _FormulaContext = class _FormulaContext {
4232
4464
  constructor(sheet, formulaRow = 0, formulaCol = 0, evaluating, registry) {
4233
4465
  __privateAdd(this, _FormulaContext_instances);
@@ -4272,24 +4504,70 @@ var _FormulaContext = class _FormulaContext {
4272
4504
  getRangeValues(startRef, endRef) {
4273
4505
  const s = parseCellRef(startRef);
4274
4506
  const e = parseCellRef(endRef);
4275
- const rows2 = e.row - s.row + 1;
4276
- const cols = e.column - s.column + 1;
4507
+ return this.getRangeValuesByIndex(s.row, s.column, e.row, e.column);
4508
+ }
4509
+ /**
4510
+ * Resolves a rectangle already known in coordinates. Split out from
4511
+ * {@link getRangeValues} because whole-column references and the range memo
4512
+ * both work in numbers, and formatting them back into `A1` strings only to
4513
+ * re-parse them is measurable at range scale.
4514
+ */
4515
+ getRangeValuesByIndex(startRow, startCol, endRow, endCol) {
4516
+ const rows2 = endRow - startRow + 1;
4517
+ const cols = endCol - startCol + 1;
4518
+ const cache = rows2 * cols > 1 ? this.rangeCache : void 0;
4519
+ const sheetKey = cache ? __privateGet(this, _sheet).name.toUpperCase() : "";
4520
+ if (cache) {
4521
+ const hit = cache.get(sheetKey, startRow, startCol, endRow, endCol);
4522
+ if (hit !== void 0) return hit;
4523
+ }
4277
4524
  const arr = new ArrayValue(rows2, cols);
4278
- for (let r = 0; r < rows2; r++) {
4279
- for (let c = 0; c < cols; c++) {
4280
- const ref = cellRefFromRowCol(s.row + r, s.column + c);
4281
- arr.set(r, c, this.getCellValue(ref));
4525
+ const at = __privateGet(this, _sheet).getCellValueAt;
4526
+ if (at) {
4527
+ for (let r = 0; r < rows2; r++) {
4528
+ for (let c = 0; c < cols; c++) {
4529
+ arr.set(r, c, FormulaValue.fromObjectSync(at.call(__privateGet(this, _sheet), startRow + r, startCol + c)));
4530
+ }
4282
4531
  }
4532
+ } else {
4533
+ for (let r = 0; r < rows2; r++) {
4534
+ for (let c = 0; c < cols; c++) {
4535
+ const ref = cellRefFromRowCol(startRow + r, startCol + c);
4536
+ arr.set(r, c, this.getCellValue(ref));
4537
+ }
4538
+ }
4539
+ }
4540
+ const value2 = FormulaValue.array(arr);
4541
+ if (cache && rows2 > 0 && cols > 0) {
4542
+ cache.set(sheetKey, startRow, startCol, endRow, endCol, value2, rows2 * cols);
4283
4543
  }
4284
- return FormulaValue.array(arr);
4544
+ return value2;
4545
+ }
4546
+ /**
4547
+ * Resolves a whole-column (`A:A`) or whole-row (`1:1`) reference, bounded by
4548
+ * the sheet's used range the way Excel does. Without a bound this would
4549
+ * materialise a million-cell array for a sheet with twelve rows in it.
4550
+ *
4551
+ * A sheet that cannot report its extent yields `#REF!` rather than a guess:
4552
+ * silently reading nothing would make SUM(A:A) return 0 on a full column.
4553
+ */
4554
+ getFullRangeValues(startCol, endCol, startRow, endRow) {
4555
+ const bounds = __privateGet(this, _sheet).usedBounds;
4556
+ if (!bounds) return FormulaValue.errorRef;
4557
+ if (bounds.rowCount === 0 || bounds.columnCount === 0) return FormulaValue.blank;
4558
+ const r0 = startRow ?? 1;
4559
+ const r1 = endRow ?? bounds.rowCount;
4560
+ const c0 = startCol ?? 1;
4561
+ const c1 = endCol ?? bounds.columnCount;
4562
+ const lastRow = Math.min(r1, bounds.rowCount);
4563
+ const lastCol = Math.min(c1, bounds.columnCount);
4564
+ if (lastRow < r0 || lastCol < c0) return FormulaValue.blank;
4565
+ return this.getRangeValuesByIndex(r0, c0, lastRow, lastCol);
4285
4566
  }
4286
4567
  getSheetCellValue(sheetName, cellReference) {
4287
4568
  if (!this.workbook) return FormulaValue.errorRef;
4288
4569
  try {
4289
- const sheet = this.workbook.getWorksheet(sheetName);
4290
- const ctx = new _FormulaContext(sheet, this.formulaRow, this.formulaCol, __privateGet(this, _evaluating), __privateGet(this, _functions2));
4291
- ctx.workbook = this.workbook;
4292
- return ctx.getCellValue(cellReference);
4570
+ return __privateMethod(this, _FormulaContext_instances, forSheet_fn).call(this, sheetName).getCellValue(cellReference);
4293
4571
  } catch {
4294
4572
  return FormulaValue.errorRef;
4295
4573
  }
@@ -4297,10 +4575,16 @@ var _FormulaContext = class _FormulaContext {
4297
4575
  getSheetRangeValues(sheetName, startRef, endRef) {
4298
4576
  if (!this.workbook) return FormulaValue.errorRef;
4299
4577
  try {
4300
- const sheet = this.workbook.getWorksheet(sheetName);
4301
- const ctx = new _FormulaContext(sheet, this.formulaRow, this.formulaCol, __privateGet(this, _evaluating), __privateGet(this, _functions2));
4302
- ctx.workbook = this.workbook;
4303
- return ctx.getRangeValues(startRef, endRef);
4578
+ return __privateMethod(this, _FormulaContext_instances, forSheet_fn).call(this, sheetName).getRangeValues(startRef, endRef);
4579
+ } catch {
4580
+ return FormulaValue.errorRef;
4581
+ }
4582
+ }
4583
+ /** Whole-column/whole-row reference qualified by a sheet name. */
4584
+ getSheetFullRangeValues(sheetName, startCol, endCol, startRow, endRow) {
4585
+ if (!this.workbook) return FormulaValue.errorRef;
4586
+ try {
4587
+ return __privateMethod(this, _FormulaContext_instances, forSheet_fn).call(this, sheetName).getFullRangeValues(startCol, endCol, startRow, endRow);
4304
4588
  } catch {
4305
4589
  return FormulaValue.errorRef;
4306
4590
  }
@@ -4355,6 +4639,21 @@ lookupScope_fn = function(name) {
4355
4639
  }
4356
4640
  return void 0;
4357
4641
  };
4642
+ /**
4643
+ * A sibling context bound to another sheet, sharing this one's circular-
4644
+ * reference guard and range memo. These contexts only read values — they
4645
+ * never evaluate an AST — so the name resolver and recursion budget are
4646
+ * deliberately not carried over; passing them would suggest a scoping
4647
+ * relationship that does not exist. The memo, on the other hand, must be
4648
+ * carried: cross-sheet ranges are the most expensive reads in a workbook.
4649
+ */
4650
+ forSheet_fn = function(sheetName) {
4651
+ const sheet = this.workbook.getWorksheet(sheetName);
4652
+ const ctx = new _FormulaContext(sheet, this.formulaRow, this.formulaCol, __privateGet(this, _evaluating), __privateGet(this, _functions2));
4653
+ ctx.workbook = this.workbook;
4654
+ ctx.rangeCache = this.rangeCache;
4655
+ return ctx;
4656
+ };
4358
4657
  var FormulaContext = _FormulaContext;
4359
4658
 
4360
4659
  // src/lru-cache.ts
@@ -4455,12 +4754,15 @@ function parseFormula(formula) {
4455
4754
  const stripped = formula.startsWith("=") ? formula.slice(1) : formula;
4456
4755
  const cached = astCache.get(stripped);
4457
4756
  if (cached !== void 0) return cached;
4458
- const lexer = new FormulaLexer(stripped);
4459
- const tokens = lexer.tokenize();
4460
- const ast = new FormulaParser(tokens).parse();
4757
+ const ast = parseFormulaUncached(stripped);
4461
4758
  astCache.set(stripped, ast);
4462
4759
  return ast;
4463
4760
  }
4761
+ function parseFormulaUncached(formula) {
4762
+ const stripped = formula.startsWith("=") ? formula.slice(1) : formula;
4763
+ const tokens = new FormulaLexer(stripped).tokenize();
4764
+ return new FormulaParser(tokens).parse();
4765
+ }
4464
4766
  var DEFAULT_LAMBDA_DEPTH = 1024;
4465
4767
  function buildContext(sheet, opts) {
4466
4768
  const evaluating = opts.evaluating ?? /* @__PURE__ */ new Set();
@@ -4471,6 +4773,7 @@ function buildContext(sheet, opts) {
4471
4773
  evaluating
4472
4774
  );
4473
4775
  ctx.recursionGuard = opts.recursionGuard ?? { depth: 0, max: DEFAULT_LAMBDA_DEPTH };
4776
+ ctx.rangeCache = opts.rangeCache;
4474
4777
  const wb = opts.workbook;
4475
4778
  if (wb) {
4476
4779
  ctx.workbook = wb;
@@ -4484,7 +4787,7 @@ function buildContext(sheet, opts) {
4484
4787
  return ctx;
4485
4788
  }
4486
4789
  function resolveName(name, sheet, workbook, evaluating, resolving, resolved, recursionGuard) {
4487
- const key = `${sheet.name}\0${name.toUpperCase()}`;
4790
+ const key = `${sheet.name} ${name.toUpperCase()}`;
4488
4791
  const cached = resolved.get(key);
4489
4792
  if (cached !== void 0) return cached;
4490
4793
  if (resolving.has(key)) return FormulaValue.errorRef;
@@ -4512,11 +4815,18 @@ function resolveName(name, sheet, workbook, evaluating, resolving, resolved, rec
4512
4815
  function evaluateFormula(formula, sheet, options) {
4513
4816
  if (!formula) return FormulaValue.blank;
4514
4817
  try {
4515
- const ast = parseFormula(formula);
4818
+ return evaluateAst(parseFormula(formula), sheet, options);
4819
+ } catch {
4820
+ return FormulaValue.errorValue;
4821
+ }
4822
+ }
4823
+ function evaluateAst(ast, sheet, options) {
4824
+ try {
4516
4825
  const ctx = buildContext(sheet, {
4517
4826
  workbook: options?.workbook,
4518
4827
  formulaRow: options?.formulaRow,
4519
- formulaCol: options?.formulaCol
4828
+ formulaCol: options?.formulaCol,
4829
+ rangeCache: options?.rangeCache
4520
4830
  });
4521
4831
  return ast.evaluate(ctx);
4522
4832
  } catch {
@@ -4537,6 +4847,149 @@ function getAstCacheStats() {
4537
4847
  function setAstCacheCapacity(capacity) {
4538
4848
  astCache.resize(capacity);
4539
4849
  }
4850
+
4851
+ // src/range-cache.ts
4852
+ var TILE_ROWS = 64;
4853
+ var TILE_COLS = 64;
4854
+ var MAX_TILES_PER_RANGE = 32;
4855
+ var DEFAULT_MAX_CELLS = 2e6;
4856
+ var _entries, _tiles, _wide, _placements, _sizes, _cells, _maxCells, _RangeCache_static, contains_fn, _RangeCache_instances, drop_fn, fileIn_fn;
4857
+ var _RangeCache = class _RangeCache {
4858
+ constructor(maxCells = DEFAULT_MAX_CELLS) {
4859
+ __privateAdd(this, _RangeCache_instances);
4860
+ /** `SHEET r0 c0 r1 c1` → the memoised array value. Insertion-ordered (FIFO eviction). */
4861
+ __privateAdd(this, _entries, /* @__PURE__ */ new Map());
4862
+ /** `sheet tr tc` → entry keys whose rectangle overlaps that tile. */
4863
+ __privateAdd(this, _tiles, /* @__PURE__ */ new Map());
4864
+ /** sheet → entry keys whose rectangle is too broad to tile. */
4865
+ __privateAdd(this, _wide, /* @__PURE__ */ new Map());
4866
+ /** entry key → buckets it was filed under, for cheap removal. */
4867
+ __privateAdd(this, _placements, /* @__PURE__ */ new Map());
4868
+ /** entry key → its area, so eviction can give the budget back. */
4869
+ __privateAdd(this, _sizes, /* @__PURE__ */ new Map());
4870
+ /** Number of cells across all cached rectangles, kept under {@link #maxCells}. */
4871
+ __privateAdd(this, _cells, 0);
4872
+ __privateAdd(this, _maxCells);
4873
+ this.hits = 0;
4874
+ this.misses = 0;
4875
+ __privateSet(this, _maxCells, maxCells);
4876
+ }
4877
+ get size() {
4878
+ return __privateGet(this, _entries).size;
4879
+ }
4880
+ get cachedCells() {
4881
+ return __privateGet(this, _cells);
4882
+ }
4883
+ static key(sheet, r0, c0, r1, c1) {
4884
+ return `${sheet} ${r0} ${c0} ${r1} ${c1}`;
4885
+ }
4886
+ get(sheet, r0, c0, r1, c1) {
4887
+ const hit = __privateGet(this, _entries).get(_RangeCache.key(sheet, r0, c0, r1, c1));
4888
+ if (hit === void 0) this.misses++;
4889
+ else this.hits++;
4890
+ return hit;
4891
+ }
4892
+ /** Memoises a rectangle's value. `cells` is its area, for the budget. */
4893
+ set(sheet, r0, c0, r1, c1, value2, cells) {
4894
+ if (cells <= 0 || cells > __privateGet(this, _maxCells)) return;
4895
+ const key = _RangeCache.key(sheet, r0, c0, r1, c1);
4896
+ if (__privateGet(this, _entries).has(key)) __privateMethod(this, _RangeCache_instances, drop_fn).call(this, key);
4897
+ __privateGet(this, _entries).set(key, value2);
4898
+ __privateSet(this, _cells, __privateGet(this, _cells) + cells);
4899
+ const buckets = [];
4900
+ const tileRow0 = r0 / TILE_ROWS | 0, tileRow1 = r1 / TILE_ROWS | 0;
4901
+ const tileCol0 = c0 / TILE_COLS | 0, tileCol1 = c1 / TILE_COLS | 0;
4902
+ const tileCount = (tileRow1 - tileRow0 + 1) * (tileCol1 - tileCol0 + 1);
4903
+ if (tileCount > MAX_TILES_PER_RANGE) {
4904
+ __privateMethod(this, _RangeCache_instances, fileIn_fn).call(this, __privateGet(this, _wide), sheet, key, buckets, `w ${sheet}`);
4905
+ } else {
4906
+ for (let tr = tileRow0; tr <= tileRow1; tr++) {
4907
+ for (let tc = tileCol0; tc <= tileCol1; tc++) {
4908
+ const bucket = `${sheet} ${tr} ${tc}`;
4909
+ __privateMethod(this, _RangeCache_instances, fileIn_fn).call(this, __privateGet(this, _tiles), bucket, key, buckets, `t ${bucket}`);
4910
+ }
4911
+ }
4912
+ }
4913
+ __privateGet(this, _placements).set(key, buckets);
4914
+ __privateGet(this, _sizes).set(key, cells);
4915
+ if (__privateGet(this, _cells) > __privateGet(this, _maxCells)) {
4916
+ for (const oldest of __privateGet(this, _entries).keys()) {
4917
+ if (__privateGet(this, _cells) <= __privateGet(this, _maxCells)) break;
4918
+ if (oldest === key) continue;
4919
+ __privateMethod(this, _RangeCache_instances, drop_fn).call(this, oldest);
4920
+ }
4921
+ }
4922
+ }
4923
+ /** Drops every cached rectangle containing the given cell. */
4924
+ invalidateCell(sheet, row2, col) {
4925
+ var _a, _b;
4926
+ const tile = __privateGet(this, _tiles).get(`${sheet} ${row2 / TILE_ROWS | 0} ${col / TILE_COLS | 0}`);
4927
+ if (tile) {
4928
+ for (const key of [...tile]) {
4929
+ if (__privateMethod(_a = _RangeCache, _RangeCache_static, contains_fn).call(_a, key, sheet, row2, col)) __privateMethod(this, _RangeCache_instances, drop_fn).call(this, key);
4930
+ }
4931
+ }
4932
+ const wide = __privateGet(this, _wide).get(sheet);
4933
+ if (wide) {
4934
+ for (const key of [...wide]) {
4935
+ if (__privateMethod(_b = _RangeCache, _RangeCache_static, contains_fn).call(_b, key, sheet, row2, col)) __privateMethod(this, _RangeCache_instances, drop_fn).call(this, key);
4936
+ }
4937
+ }
4938
+ }
4939
+ clear() {
4940
+ __privateGet(this, _entries).clear();
4941
+ __privateGet(this, _tiles).clear();
4942
+ __privateGet(this, _wide).clear();
4943
+ __privateGet(this, _placements).clear();
4944
+ __privateGet(this, _sizes).clear();
4945
+ __privateSet(this, _cells, 0);
4946
+ }
4947
+ };
4948
+ _entries = new WeakMap();
4949
+ _tiles = new WeakMap();
4950
+ _wide = new WeakMap();
4951
+ _placements = new WeakMap();
4952
+ _sizes = new WeakMap();
4953
+ _cells = new WeakMap();
4954
+ _maxCells = new WeakMap();
4955
+ _RangeCache_static = new WeakSet();
4956
+ contains_fn = function(key, sheet, row2, col) {
4957
+ const parts = key.split(" ");
4958
+ const n = parts.length;
4959
+ const c1 = +parts[n - 1], r1 = +parts[n - 2], c0 = +parts[n - 3], r0 = +parts[n - 4];
4960
+ if (parts.slice(0, n - 4).join(" ") !== sheet) return false;
4961
+ return row2 >= r0 && row2 <= r1 && col >= c0 && col <= c1;
4962
+ };
4963
+ _RangeCache_instances = new WeakSet();
4964
+ drop_fn = function(key) {
4965
+ if (!__privateGet(this, _entries).delete(key)) return;
4966
+ __privateSet(this, _cells, __privateGet(this, _cells) - (__privateGet(this, _sizes).get(key) ?? 0));
4967
+ __privateGet(this, _sizes).delete(key);
4968
+ const buckets = __privateGet(this, _placements).get(key);
4969
+ if (buckets) {
4970
+ for (const bucket of buckets) {
4971
+ const store = bucket.charCodeAt(0) === 119 ? __privateGet(this, _wide) : __privateGet(this, _tiles);
4972
+ const bucketKey = bucket.slice(2);
4973
+ const set = store.get(bucketKey);
4974
+ if (!set) continue;
4975
+ set.delete(key);
4976
+ if (set.size === 0) store.delete(bucketKey);
4977
+ }
4978
+ __privateGet(this, _placements).delete(key);
4979
+ }
4980
+ };
4981
+ fileIn_fn = function(store, bucketKey, entryKey, buckets, bucketId) {
4982
+ let set = store.get(bucketKey);
4983
+ if (!set) {
4984
+ set = /* @__PURE__ */ new Set();
4985
+ store.set(bucketKey, set);
4986
+ }
4987
+ if (set.has(entryKey)) return;
4988
+ set.add(entryKey);
4989
+ buckets.push(bucketId);
4990
+ };
4991
+ __privateAdd(_RangeCache, _RangeCache_static);
4992
+ var RangeCache = _RangeCache;
4540
4993
  function extractReferences(ast, formulaSheet, registry = FunctionRegistry.default, resolveName2) {
4541
4994
  const refs = [];
4542
4995
  let volatile = false;
@@ -4565,6 +5018,14 @@ function extractReferences(ast, formulaSheet, registry = FunctionRegistry.defaul
4565
5018
  refs.push(cell(node.sheetName, node.cellReference));
4566
5019
  } else if (node instanceof SheetRangeReferenceNode) {
4567
5020
  refs.push(range(node.sheetName, node.startRef, node.endRef));
5021
+ } else if (node instanceof FullRangeReferenceNode) {
5022
+ refs.push({
5023
+ sheet: (node.sheetName ?? formulaSheet).toUpperCase(),
5024
+ startRow: node.startRow ?? 1,
5025
+ endRow: node.endRow ?? EXCEL_MAX_ROWS,
5026
+ startCol: node.startCol ?? 1,
5027
+ endCol: node.endCol ?? EXCEL_MAX_COLUMNS
5028
+ });
4568
5029
  } else if (node instanceof NamedRangeNode) {
4569
5030
  expandName(node.name);
4570
5031
  } else if (node instanceof SpillReferenceNode) {
@@ -4600,10 +5061,149 @@ function refContains(ref, sheet, row2, col) {
4600
5061
  return ref.sheet === sheet && row2 >= ref.startRow && row2 <= ref.endRow && col >= ref.startCol && col <= ref.endCol;
4601
5062
  }
4602
5063
 
5064
+ // src/dependency-index.ts
5065
+ var TILE_ROWS2 = 64;
5066
+ var TILE_COLS2 = 64;
5067
+ var MAX_TILES_PER_REF = 32;
5068
+ var MAX_EXACT_SPAN = 16;
5069
+ var MAX_BUCKETS_PER_REF = 64;
5070
+ var tileOfRow = (row2) => row2 / TILE_ROWS2 | 0;
5071
+ var tileOfCol = (col) => col / TILE_COLS2 | 0;
5072
+ var _buckets, _placements2, _DependencyIndex_instances, visit_fn, file_fn;
5073
+ var DependencyIndex = class {
5074
+ constructor() {
5075
+ __privateAdd(this, _DependencyIndex_instances);
5076
+ /**
5077
+ * All buckets in one map, keyed by a discriminating prefix:
5078
+ *
5079
+ * `t s tr tc` rectangle compact in both dimensions
5080
+ * `c s col tr` narrow columns, row tiles (the common case)
5081
+ * `C s col` narrow columns, every row (`A:A`)
5082
+ * `r s row tc` narrow rows, column tiles
5083
+ * `R s row` narrow rows, every column (`1:1`)
5084
+ * `w s` broad in both dimensions
5085
+ */
5086
+ __privateAdd(this, _buckets, /* @__PURE__ */ new Map());
5087
+ /** formula key → the buckets it was filed under, for cheap removal. */
5088
+ __privateAdd(this, _placements2, /* @__PURE__ */ new Map());
5089
+ }
5090
+ /** Registers (or re-registers) a formula's precedent rectangles. */
5091
+ add(formulaKey, refs) {
5092
+ this.remove(formulaKey);
5093
+ if (refs.length === 0) return;
5094
+ const buckets = [];
5095
+ for (const ref of refs) {
5096
+ const rowSpan = ref.endRow - ref.startRow + 1;
5097
+ const colSpan = ref.endCol - ref.startCol + 1;
5098
+ const rowTile0 = tileOfRow(ref.startRow), rowTile1 = tileOfRow(ref.endRow);
5099
+ const colTile0 = tileOfCol(ref.startCol), colTile1 = tileOfCol(ref.endCol);
5100
+ const rowTiles = rowTile1 - rowTile0 + 1;
5101
+ const colTiles = colTile1 - colTile0 + 1;
5102
+ if (colSpan <= MAX_EXACT_SPAN && colSpan <= rowSpan) {
5103
+ if (colSpan * rowTiles <= MAX_BUCKETS_PER_REF && rowTiles <= MAX_TILES_PER_REF) {
5104
+ for (let col = ref.startCol; col <= ref.endCol; col++) {
5105
+ for (let tr = rowTile0; tr <= rowTile1; tr++) {
5106
+ __privateMethod(this, _DependencyIndex_instances, file_fn).call(this, `c ${ref.sheet} ${col} ${tr}`, formulaKey, buckets);
5107
+ }
5108
+ }
5109
+ } else {
5110
+ for (let col = ref.startCol; col <= ref.endCol; col++) {
5111
+ __privateMethod(this, _DependencyIndex_instances, file_fn).call(this, `C ${ref.sheet} ${col}`, formulaKey, buckets);
5112
+ }
5113
+ }
5114
+ continue;
5115
+ }
5116
+ if (rowSpan <= MAX_EXACT_SPAN) {
5117
+ if (rowSpan * colTiles <= MAX_BUCKETS_PER_REF && colTiles <= MAX_TILES_PER_REF) {
5118
+ for (let row2 = ref.startRow; row2 <= ref.endRow; row2++) {
5119
+ for (let tc = colTile0; tc <= colTile1; tc++) {
5120
+ __privateMethod(this, _DependencyIndex_instances, file_fn).call(this, `r ${ref.sheet} ${row2} ${tc}`, formulaKey, buckets);
5121
+ }
5122
+ }
5123
+ } else {
5124
+ for (let row2 = ref.startRow; row2 <= ref.endRow; row2++) {
5125
+ __privateMethod(this, _DependencyIndex_instances, file_fn).call(this, `R ${ref.sheet} ${row2}`, formulaKey, buckets);
5126
+ }
5127
+ }
5128
+ continue;
5129
+ }
5130
+ if (rowTiles * colTiles <= MAX_TILES_PER_REF) {
5131
+ for (let tr = rowTile0; tr <= rowTile1; tr++) {
5132
+ for (let tc = colTile0; tc <= colTile1; tc++) {
5133
+ __privateMethod(this, _DependencyIndex_instances, file_fn).call(this, `t ${ref.sheet} ${tr} ${tc}`, formulaKey, buckets);
5134
+ }
5135
+ }
5136
+ continue;
5137
+ }
5138
+ __privateMethod(this, _DependencyIndex_instances, file_fn).call(this, `w ${ref.sheet}`, formulaKey, buckets);
5139
+ }
5140
+ __privateGet(this, _placements2).set(formulaKey, buckets);
5141
+ }
5142
+ /** Forgets a formula. */
5143
+ remove(formulaKey) {
5144
+ const buckets = __privateGet(this, _placements2).get(formulaKey);
5145
+ if (!buckets) return;
5146
+ for (const bucket of buckets) {
5147
+ const set = __privateGet(this, _buckets).get(bucket);
5148
+ if (!set) continue;
5149
+ set.delete(formulaKey);
5150
+ if (set.size === 0) __privateGet(this, _buckets).delete(bucket);
5151
+ }
5152
+ __privateGet(this, _placements2).delete(formulaKey);
5153
+ }
5154
+ /** Drops every registration. */
5155
+ clear() {
5156
+ __privateGet(this, _buckets).clear();
5157
+ __privateGet(this, _placements2).clear();
5158
+ }
5159
+ /**
5160
+ * Calls `visit` with the key of each formula that *may* read the given cell.
5161
+ * Callers still confirm with {@link refContains}: buckets are coarser than
5162
+ * rectangles in the broad dimension, so this over-approximates.
5163
+ *
5164
+ * A fixed six lookups, whatever the workbook contains.
5165
+ */
5166
+ candidates(sheet, row2, col, visit) {
5167
+ const tr = tileOfRow(row2);
5168
+ const tc = tileOfCol(col);
5169
+ __privateMethod(this, _DependencyIndex_instances, visit_fn).call(this, `c ${sheet} ${col} ${tr}`, visit);
5170
+ __privateMethod(this, _DependencyIndex_instances, visit_fn).call(this, `C ${sheet} ${col}`, visit);
5171
+ __privateMethod(this, _DependencyIndex_instances, visit_fn).call(this, `r ${sheet} ${row2} ${tc}`, visit);
5172
+ __privateMethod(this, _DependencyIndex_instances, visit_fn).call(this, `R ${sheet} ${row2}`, visit);
5173
+ __privateMethod(this, _DependencyIndex_instances, visit_fn).call(this, `t ${sheet} ${tr} ${tc}`, visit);
5174
+ __privateMethod(this, _DependencyIndex_instances, visit_fn).call(this, `w ${sheet}`, visit);
5175
+ }
5176
+ };
5177
+ _buckets = new WeakMap();
5178
+ _placements2 = new WeakMap();
5179
+ _DependencyIndex_instances = new WeakSet();
5180
+ visit_fn = function(bucket, visit) {
5181
+ const set = __privateGet(this, _buckets).get(bucket);
5182
+ if (set) for (const key of set) visit(key);
5183
+ };
5184
+ file_fn = function(bucket, formulaKey, buckets) {
5185
+ let set = __privateGet(this, _buckets).get(bucket);
5186
+ if (!set) {
5187
+ set = /* @__PURE__ */ new Set();
5188
+ __privateGet(this, _buckets).set(bucket, set);
5189
+ }
5190
+ if (set.has(formulaKey)) return;
5191
+ set.add(formulaKey);
5192
+ buckets.push(bucket);
5193
+ };
5194
+
4603
5195
  // src/recalc-engine.ts
4604
5196
  function createWorkbookRecalcModel(workbook) {
4605
5197
  return {
4606
5198
  getCellValue: (sheet, ref) => workbook.getWorksheet(sheet).getCellValue(ref),
5199
+ // Falls back to the reference-string read rather than reporting "no value":
5200
+ // an older core without getCellValueAt must still return the real value,
5201
+ // because a silent undefined here reads as a blank cell.
5202
+ getCellValueAt: (sheet, row2, column2) => {
5203
+ const ws = workbook.getWorksheet(sheet);
5204
+ return ws.getCellValueAt ? ws.getCellValueAt(row2, column2) : ws.getCellValue(cellRefFromRowCol(row2, column2));
5205
+ },
5206
+ getUsedBounds: (sheet) => workbook.getWorksheet(sheet).usedBounds,
4607
5207
  setCellValue: (sheet, ref, value2) => {
4608
5208
  workbook.getWorksheet(sheet).getCell(ref).setComputedValue(value2);
4609
5209
  },
@@ -4632,7 +5232,7 @@ function parseKey(key) {
4632
5232
  const { row: row2, column: column2 } = parseCellRef(ref);
4633
5233
  return { sheet, ref, row: row2, col: column2 };
4634
5234
  }
4635
- var _model, _registry, _formulas, _sheetNames, _spills, _spillOwner, _RecalcEngine_instances, rememberSheet_fn, recalcFrom_fn, dependentClosure_fn, directDependents_fn, evaluatePass_fn, writeResult_fn, isOccupied_fn, clearSpill_fn, evaluateFormulaValue_fn, sheetLike_fn, buildWorkbookLike_fn;
5235
+ var _model, _registry, _formulas, _sheetNames, _spills, _spillOwner, _dependents, _volatiles, _sheetLikes, _workbookLike, _rangeCache, _RecalcEngine_instances, writeCell_fn, register_fn, unregister_fn, rememberSheet_fn, recalcInOrder_fn, applyShift_fn, shiftPrecedents_fn, shiftSpillBookkeeping_fn, recalcFrom_fn, runFixpoint_fn, addVolatiles_fn, dependentClosure_fn, forEachDirectDependent_fn, evaluatePass_fn, writeResult_fn, isOccupied_fn, clearSpill_fn, evaluateFormulaValue_fn, sheetLike_fn, buildSheetLike_fn, buildWorkbookLike_fn;
4636
5236
  var RecalcEngine = class {
4637
5237
  constructor(model, registry = FunctionRegistry.default) {
4638
5238
  __privateAdd(this, _RecalcEngine_instances);
@@ -4645,6 +5245,27 @@ var RecalcEngine = class {
4645
5245
  __privateAdd(this, _spills, /* @__PURE__ */ new Map());
4646
5246
  /** spilled cellKey → the anchor cellKey that owns it. */
4647
5247
  __privateAdd(this, _spillOwner, /* @__PURE__ */ new Map());
5248
+ /**
5249
+ * Reverse dependency index, so finding the formulas that read a cell does not
5250
+ * mean testing every registered formula.
5251
+ */
5252
+ __privateAdd(this, _dependents, new DependencyIndex());
5253
+ /**
5254
+ * Keys of the volatile formulas. Maintained incrementally: scanning every
5255
+ * formula for volatility on each edit is O(F) per keystroke on its own.
5256
+ */
5257
+ __privateAdd(this, _volatiles, /* @__PURE__ */ new Set());
5258
+ /** Memoised per-sheet adapters — these are stateless w.r.t. the formula. */
5259
+ __privateAdd(this, _sheetLikes, /* @__PURE__ */ new Map());
5260
+ __privateAdd(this, _workbookLike);
5261
+ /**
5262
+ * Memo for range reads. The engine owns it because it owns the writes: every
5263
+ * value written goes through {@link #writeCell}, which drops the cached
5264
+ * rectangles containing that cell. Nothing else may write to the model behind
5265
+ * the engine's back without calling {@link onCellChanged} — that was already
5266
+ * true for dependency tracking, and now also keeps this memo honest.
5267
+ */
5268
+ __privateAdd(this, _rangeCache, new RangeCache());
4648
5269
  __privateSet(this, _model, model);
4649
5270
  __privateSet(this, _registry, registry);
4650
5271
  }
@@ -4660,7 +5281,7 @@ var RecalcEngine = class {
4660
5281
  const s = normSheet(sheet);
4661
5282
  const r = normRef(ref);
4662
5283
  const { row: row2, column: column2 } = parseCellRef(r);
4663
- const ast = parseFormula(text);
5284
+ const ast = parseFormulaUncached(text);
4664
5285
  const { refs, volatile } = extractReferences(
4665
5286
  ast,
4666
5287
  s,
@@ -4668,18 +5289,76 @@ var RecalcEngine = class {
4668
5289
  __privateGet(this, _model).getDefinedName ? (n, sh) => __privateGet(this, _model).getDefinedName(n, sh) : void 0
4669
5290
  );
4670
5291
  __privateMethod(this, _RecalcEngine_instances, rememberSheet_fn).call(this, sheet);
4671
- __privateGet(this, _formulas).set(keyOf(s, r), {
5292
+ const key = keyOf(s, r);
5293
+ __privateMethod(this, _RecalcEngine_instances, register_fn).call(this, {
4672
5294
  sheet: s,
4673
5295
  sheetName: sheet,
4674
5296
  ref: r,
5297
+ key,
4675
5298
  row: row2,
4676
5299
  col: column2,
4677
5300
  formula: text,
5301
+ ast,
4678
5302
  refs,
4679
5303
  volatile
4680
5304
  });
4681
5305
  __privateGet(this, _model).setCellFormula?.(sheet, r, text);
4682
- return __privateMethod(this, _RecalcEngine_instances, recalcFrom_fn).call(this, [{ sheet: s, row: row2, col: column2 }], keyOf(s, r));
5306
+ __privateGet(this, _rangeCache).invalidateCell(s, row2, column2);
5307
+ return __privateMethod(this, _RecalcEngine_instances, recalcFrom_fn).call(this, [{ sheet: s, row: row2, col: column2 }], key);
5308
+ }
5309
+ /**
5310
+ * Indexes many formulas **without evaluating anything**.
5311
+ *
5312
+ * This is the path for loading a workbook. `setCellFormula` is built for an
5313
+ * edit: it recalculates the new cell and everything downstream of it, which is
5314
+ * right for one keystroke and quadratic for a file — registering N formulas one
5315
+ * by one evaluates the whole workbook N times. Here the formulas are only
5316
+ * parsed and filed into the dependency index; call {@link recalcAll} once
5317
+ * afterwards, or nothing at all if the cached values from the file will do.
5318
+ *
5319
+ * The model is deliberately **not** written to. These formulas are assumed to
5320
+ * be the ones the model already holds (they came from the file), and persisting
5321
+ * them again would clear each cell's cached value — leaving every formula cell
5322
+ * blank until a full recalculation had run. Use `setCellFormula` for formulas
5323
+ * the user is actually introducing.
5324
+ *
5325
+ * A formula that fails to parse is reported rather than thrown: one unsupported
5326
+ * construct in a large workbook should not abort the load.
5327
+ */
5328
+ registerBulk(entries) {
5329
+ const resolveName2 = __privateGet(this, _model).getDefinedName ? (n, sh) => __privateGet(this, _model).getDefinedName(n, sh) : void 0;
5330
+ let registered = 0;
5331
+ const failed = [];
5332
+ for (const entry of entries) {
5333
+ const { sheet, ref, formula } = entry;
5334
+ if (!formula) continue;
5335
+ const text = formula.startsWith("=") ? formula.slice(1) : formula;
5336
+ const s = normSheet(sheet);
5337
+ const r = normRef(ref);
5338
+ try {
5339
+ const { row: row2, column: column2 } = parseCellRef(r);
5340
+ const ast = parseFormulaUncached(text);
5341
+ const { refs, volatile } = extractReferences(ast, s, __privateGet(this, _registry), resolveName2);
5342
+ __privateMethod(this, _RecalcEngine_instances, rememberSheet_fn).call(this, sheet);
5343
+ __privateMethod(this, _RecalcEngine_instances, register_fn).call(this, {
5344
+ sheet: s,
5345
+ sheetName: sheet,
5346
+ ref: r,
5347
+ key: keyOf(s, r),
5348
+ row: row2,
5349
+ col: column2,
5350
+ formula: text,
5351
+ ast,
5352
+ refs,
5353
+ volatile
5354
+ });
5355
+ registered++;
5356
+ } catch (err) {
5357
+ failed.push({ sheet, ref, error: err instanceof Error ? err.message : String(err) });
5358
+ }
5359
+ }
5360
+ if (registered > 0) __privateGet(this, _rangeCache).clear();
5361
+ return { registered, failed };
4683
5362
  }
4684
5363
  /**
4685
5364
  * Removes a formula from a cell and recalculates its dependents (which may now
@@ -4688,10 +5367,12 @@ var RecalcEngine = class {
4688
5367
  clearCellFormula(sheet, ref) {
4689
5368
  const s = normSheet(sheet);
4690
5369
  const r = normRef(ref);
4691
- const existed = __privateGet(this, _formulas).delete(keyOf(s, r));
4692
- if (!existed) return [];
5370
+ const key = keyOf(s, r);
5371
+ if (!__privateGet(this, _formulas).has(key)) return [];
5372
+ __privateMethod(this, _RecalcEngine_instances, unregister_fn).call(this, key);
4693
5373
  __privateGet(this, _model).clearCell?.(sheet, r);
4694
5374
  const { row: row2, column: column2 } = parseCellRef(r);
5375
+ __privateGet(this, _rangeCache).invalidateCell(s, row2, column2);
4695
5376
  return __privateMethod(this, _RecalcEngine_instances, recalcFrom_fn).call(this, [{ sheet: s, row: row2, col: column2 }]);
4696
5377
  }
4697
5378
  /**
@@ -4702,8 +5383,8 @@ var RecalcEngine = class {
4702
5383
  __privateMethod(this, _RecalcEngine_instances, rememberSheet_fn).call(this, sheet);
4703
5384
  const s = normSheet(sheet);
4704
5385
  const r = normRef(ref);
4705
- __privateGet(this, _model).setCellValue(sheet, r, value2);
4706
5386
  const { row: row2, column: column2 } = parseCellRef(r);
5387
+ __privateMethod(this, _RecalcEngine_instances, writeCell_fn).call(this, sheet, r, value2, row2, column2);
4707
5388
  return __privateMethod(this, _RecalcEngine_instances, recalcFrom_fn).call(this, [{ sheet: s, row: row2, col: column2 }]);
4708
5389
  }
4709
5390
  /**
@@ -4715,16 +5396,86 @@ var RecalcEngine = class {
4715
5396
  const s = normSheet(sheet);
4716
5397
  const r = normRef(ref);
4717
5398
  const { row: row2, column: column2 } = parseCellRef(r);
5399
+ __privateGet(this, _rangeCache).invalidateCell(s, row2, column2);
4718
5400
  return __privateMethod(this, _RecalcEngine_instances, recalcFrom_fn).call(this, [{ sheet: s, row: row2, col: column2 }]);
4719
5401
  }
4720
- /** Recalculates every registered formula in dependency order. */
4721
- recalcAll() {
4722
- return __privateMethod(this, _RecalcEngine_instances, evaluatePass_fn).call(this, [...__privateGet(this, _formulas).values()]).results;
5402
+ /**
5403
+ * Recalculates every registered formula.
5404
+ *
5405
+ * By default the order is derived here, with a topological sort over the
5406
+ * dependency index. `order` — typically `workbook.calcChain` — supplies one
5407
+ * instead, so the sort is not needed.
5408
+ *
5409
+ * **A supplied order is verified, not trusted.** Evaluating in a stale order
5410
+ * makes a formula read a precedent that has not been computed yet, and the
5411
+ * result is a plausible wrong number with nothing to distinguish it from a
5412
+ * right one. In a calculation engine that is the worst failure available, so
5413
+ * this checks as it goes: after each formula is written, the dependency index
5414
+ * says who reads it, and any reader that was already evaluated is an inversion.
5415
+ * On the first inversion the ordered attempt is abandoned and the whole
5416
+ * recalculation is redone through the sorted path, whose result is returned;
5417
+ * `onOrderConflict`, if given, is told which pair conflicted.
5418
+ *
5419
+ * The verification costs about what the sort it replaces costs, so `order` is
5420
+ * worth roughly nothing now (measured at ~9% of a full recalculation on a
5421
+ * 2 000-formula workbook, after the dependency index stopped being the
5422
+ * bottleneck). It is kept because reading an order Excel already computed is a
5423
+ * reasonable thing to want; prefer plain `recalcAll()` unless you have measured
5424
+ * a reason not to.
5425
+ *
5426
+ * Registered formulas the order does not mention are evaluated afterwards
5427
+ * through the sorted path, and a dynamic array that spills still triggers the
5428
+ * usual fixpoint, so neither silently misses cells.
5429
+ */
5430
+ recalcAll(options) {
5431
+ const order = options?.order;
5432
+ if (!order || order.length === 0) {
5433
+ return __privateMethod(this, _RecalcEngine_instances, evaluatePass_fn).call(this, [...__privateGet(this, _formulas).values()]).results;
5434
+ }
5435
+ const attempt = __privateMethod(this, _RecalcEngine_instances, recalcInOrder_fn).call(this, order);
5436
+ if (attempt.conflict) {
5437
+ options?.onOrderConflict?.(attempt.conflict);
5438
+ return __privateMethod(this, _RecalcEngine_instances, evaluatePass_fn).call(this, [...__privateGet(this, _formulas).values()]).results;
5439
+ }
5440
+ return attempt.results;
4723
5441
  }
4724
5442
  /** The precedents recorded for a formula cell (mainly for tests/inspection). */
4725
5443
  getPrecedents(sheet, ref) {
4726
5444
  return __privateGet(this, _formulas).get(keyOf(sheet, ref))?.refs ?? [];
4727
5445
  }
5446
+ // ── Structural edits ─────────────────────────────────────────────────────────
5447
+ /**
5448
+ * Tells the engine rows were inserted on a sheet. Call AFTER the
5449
+ * corresponding `Worksheet.insertRows` (the model must already reflect the
5450
+ * edit). The dependency graph is translated in place — registrations,
5451
+ * formula text and spill bookkeeping all move — and only volatile formulas
5452
+ * are recomputed, because a pure translation changes no values.
5453
+ *
5454
+ * @example
5455
+ * ws.insertRows(3, 2);
5456
+ * engine.onRowsInserted('S1', 3, 2);
5457
+ */
5458
+ onRowsInserted(sheet, at, count2 = 1) {
5459
+ return __privateMethod(this, _RecalcEngine_instances, applyShift_fn).call(this, { dim: "row", kind: "insert", at, count: count2, sheetName: sheet });
5460
+ }
5461
+ /**
5462
+ * Tells the engine rows were deleted on a sheet. Call AFTER the
5463
+ * corresponding `Worksheet.deleteRows`. Registrations inside the deleted
5464
+ * band are dropped; the rest of the graph is translated, and only formulas
5465
+ * whose precedents touched the deleted band (plus their dependents and
5466
+ * volatiles) are recomputed.
5467
+ */
5468
+ onRowsDeleted(sheet, at, count2 = 1) {
5469
+ return __privateMethod(this, _RecalcEngine_instances, applyShift_fn).call(this, { dim: "row", kind: "delete", at, count: count2, sheetName: sheet });
5470
+ }
5471
+ /** Column counterpart to {@link onRowsInserted}. */
5472
+ onColumnsInserted(sheet, at, count2 = 1) {
5473
+ return __privateMethod(this, _RecalcEngine_instances, applyShift_fn).call(this, { dim: "col", kind: "insert", at, count: count2, sheetName: sheet });
5474
+ }
5475
+ /** Column counterpart to {@link onRowsDeleted}. */
5476
+ onColumnsDeleted(sheet, at, count2 = 1) {
5477
+ return __privateMethod(this, _RecalcEngine_instances, applyShift_fn).call(this, { dim: "col", kind: "delete", at, count: count2, sheetName: sheet });
5478
+ }
4728
5479
  };
4729
5480
  _model = new WeakMap();
4730
5481
  _registry = new WeakMap();
@@ -4732,10 +5483,212 @@ _formulas = new WeakMap();
4732
5483
  _sheetNames = new WeakMap();
4733
5484
  _spills = new WeakMap();
4734
5485
  _spillOwner = new WeakMap();
5486
+ _dependents = new WeakMap();
5487
+ _volatiles = new WeakMap();
5488
+ _sheetLikes = new WeakMap();
5489
+ _workbookLike = new WeakMap();
5490
+ _rangeCache = new WeakMap();
4735
5491
  _RecalcEngine_instances = new WeakSet();
5492
+ /**
5493
+ * The single write path into the model. Writing a value invalidates the
5494
+ * cached rectangles that contain the cell, so a formula evaluated later in the
5495
+ * same pass cannot read a pre-write copy of a range. Coordinates are passed in
5496
+ * where the caller already knows them, to avoid re-parsing the reference.
5497
+ */
5498
+ writeCell_fn = function(sheetName, ref, value2, row2, col) {
5499
+ __privateGet(this, _model).setCellValue(sheetName, ref, value2);
5500
+ if (row2 === void 0 || col === void 0) {
5501
+ const parsed = parseCellRef(ref);
5502
+ row2 = parsed.row;
5503
+ col = parsed.column;
5504
+ }
5505
+ __privateGet(this, _rangeCache).invalidateCell(normSheet(sheetName), row2, col);
5506
+ };
5507
+ /** Registers an entry in the map, the reverse index and the volatile set. */
5508
+ register_fn = function(entry) {
5509
+ __privateGet(this, _formulas).set(entry.key, entry);
5510
+ __privateGet(this, _dependents).add(entry.key, entry.refs);
5511
+ if (entry.volatile) __privateGet(this, _volatiles).add(entry.key);
5512
+ else __privateGet(this, _volatiles).delete(entry.key);
5513
+ };
5514
+ unregister_fn = function(key) {
5515
+ __privateGet(this, _formulas).delete(key);
5516
+ __privateGet(this, _dependents).remove(key);
5517
+ __privateGet(this, _volatiles).delete(key);
5518
+ };
4736
5519
  rememberSheet_fn = function(name) {
4737
5520
  __privateGet(this, _sheetNames).set(normSheet(name), name);
4738
5521
  };
5522
+ /**
5523
+ * One pass in a caller-supplied order, watching for inversions.
5524
+ *
5525
+ * The check is the mirror image of the topological sort: instead of asking
5526
+ * "which of my precedents come first?", it asks, of each formula just
5527
+ * evaluated, "has anything that reads me already run?". The dependency index
5528
+ * answers that directly, and one positive answer is enough to know the order
5529
+ * cannot be trusted.
5530
+ */
5531
+ recalcInOrder_fn = function(order) {
5532
+ const results = [];
5533
+ const changed = [];
5534
+ const done = /* @__PURE__ */ new Set();
5535
+ let spilled = false;
5536
+ for (const { sheet, ref } of order) {
5537
+ const key = keyOf(sheet, ref);
5538
+ if (done.has(key)) continue;
5539
+ const entry = __privateGet(this, _formulas).get(key);
5540
+ if (!entry) continue;
5541
+ done.add(key);
5542
+ const written = __privateMethod(this, _RecalcEngine_instances, writeResult_fn).call(this, entry);
5543
+ results.push(written.result);
5544
+ changed.push(...written.changed);
5545
+ if (written.result.spill) spilled = true;
5546
+ let conflict;
5547
+ __privateMethod(this, _RecalcEngine_instances, forEachDirectDependent_fn).call(this, { sheet: entry.sheet, row: entry.row, col: entry.col }, (dep) => {
5548
+ if (conflict || dep.key === entry.key) return;
5549
+ if (done.has(dep.key)) conflict = { dependent: dep.key, precedent: entry.key };
5550
+ });
5551
+ if (conflict) return { results: [], conflict };
5552
+ }
5553
+ const rest = [...__privateGet(this, _formulas).values()].filter((e) => !done.has(e.key));
5554
+ if (rest.length > 0) {
5555
+ const pass = __privateMethod(this, _RecalcEngine_instances, evaluatePass_fn).call(this, rest);
5556
+ results.push(...pass.results);
5557
+ changed.push(...pass.changed);
5558
+ }
5559
+ if (spilled) {
5560
+ const evaluated = new Set(done);
5561
+ results.push(...__privateMethod(this, _RecalcEngine_instances, runFixpoint_fn).call(this, __privateMethod(this, _RecalcEngine_instances, dependentClosure_fn).call(this, changed, evaluated), evaluated));
5562
+ }
5563
+ return { results };
5564
+ };
5565
+ applyShift_fn = function(shift) {
5566
+ const editedSheet = normSheet(shift.sheetName);
5567
+ const isRowShift = shift.dim === "row";
5568
+ const bandEnd = shift.at + shift.count - 1;
5569
+ const rekeyed = [];
5570
+ const needsRecalc = /* @__PURE__ */ new Set();
5571
+ for (const e of __privateGet(this, _formulas).values()) {
5572
+ let row2 = e.row;
5573
+ let col = e.col;
5574
+ if (e.sheet === editedSheet) {
5575
+ const shifted = shiftIndex(isRowShift ? row2 : col, shift);
5576
+ if (shifted === null) continue;
5577
+ if (isRowShift) row2 = shifted;
5578
+ else col = shifted;
5579
+ }
5580
+ const touchedBand = shift.kind === "delete" && e.refs.some((r) => r.sheet === editedSheet && (isRowShift ? r.endRow >= shift.at && r.startRow <= bandEnd : r.endCol >= shift.at && r.startCol <= bandEnd));
5581
+ const rewritten = shiftFormulaRefs(e.formula, shift, e.sheetName);
5582
+ const moved = row2 !== e.row || col !== e.col;
5583
+ const ref = moved ? cellRefFromRowCol(row2, col) : e.ref;
5584
+ const key = moved ? keyOf(e.sheet, ref) : e.key;
5585
+ rekeyed.push({
5586
+ ...e,
5587
+ ref,
5588
+ key,
5589
+ row: row2,
5590
+ col,
5591
+ formula: rewritten.changed ? rewritten.text : e.formula,
5592
+ // A rewritten formula's cached AST no longer matches its text; it is
5593
+ // re-parsed lazily, and only if the cell is actually evaluated again.
5594
+ ast: rewritten.changed ? void 0 : e.ast,
5595
+ refs: __privateMethod(this, _RecalcEngine_instances, shiftPrecedents_fn).call(this, e.refs, shift, editedSheet)
5596
+ // Volatility is a property of which *functions* a formula calls, and
5597
+ // rewriting references cannot change that.
5598
+ });
5599
+ if (touchedBand || rewritten.hasRefError) needsRecalc.add(key);
5600
+ }
5601
+ __privateSet(this, _formulas, /* @__PURE__ */ new Map());
5602
+ __privateGet(this, _dependents).clear();
5603
+ __privateGet(this, _volatiles).clear();
5604
+ for (const e of rekeyed) __privateMethod(this, _RecalcEngine_instances, register_fn).call(this, e);
5605
+ __privateMethod(this, _RecalcEngine_instances, shiftSpillBookkeeping_fn).call(this, shift, editedSheet);
5606
+ __privateGet(this, _rangeCache).clear();
5607
+ const pending = /* @__PURE__ */ new Map();
5608
+ for (const k of needsRecalc) {
5609
+ const e = __privateGet(this, _formulas).get(k);
5610
+ if (e) pending.set(k, e);
5611
+ }
5612
+ for (const e of __privateGet(this, _formulas).values()) {
5613
+ if (e.volatile) pending.set(e.key, e);
5614
+ }
5615
+ return __privateMethod(this, _RecalcEngine_instances, runFixpoint_fn).call(this, pending, /* @__PURE__ */ new Set());
5616
+ };
5617
+ /**
5618
+ * Translates precedent rectangles across a structural edit.
5619
+ *
5620
+ * This is arithmetic rather than a re-parse of the rewritten formula, and the
5621
+ * two are equivalent by construction: `shiftFormulaRefs` derives the new
5622
+ * reference text by calling `shiftSpan`/`shiftIndex` on exactly these numbers,
5623
+ * so applying the same functions to the stored rectangle yields the same
5624
+ * region. A rectangle that is entirely deleted disappears, matching the
5625
+ * `#REF!` the text rewrite produces.
5626
+ */
5627
+ shiftPrecedents_fn = function(refs, shift, editedSheet) {
5628
+ const out = [];
5629
+ for (const r of refs) {
5630
+ if (r.sheet !== editedSheet) {
5631
+ out.push(r);
5632
+ continue;
5633
+ }
5634
+ if (shift.dim === "row") {
5635
+ const span = shiftSpan(r.startRow, r.endRow, shift);
5636
+ if (!span) continue;
5637
+ out.push({
5638
+ sheet: r.sheet,
5639
+ startRow: span.start,
5640
+ endRow: Math.min(span.end, EXCEL_MAX_ROWS),
5641
+ startCol: r.startCol,
5642
+ endCol: r.endCol
5643
+ });
5644
+ } else {
5645
+ const span = shiftSpan(r.startCol, r.endCol, shift);
5646
+ if (!span) continue;
5647
+ out.push({
5648
+ sheet: r.sheet,
5649
+ startRow: r.startRow,
5650
+ endRow: r.endRow,
5651
+ startCol: span.start,
5652
+ endCol: Math.min(span.end, EXCEL_MAX_COLUMNS)
5653
+ });
5654
+ }
5655
+ }
5656
+ return out;
5657
+ };
5658
+ /** Translates spill ownership across a structural edit on `editedSheet`. */
5659
+ shiftSpillBookkeeping_fn = function(shift, editedSheet) {
5660
+ const shiftCellKey = (key) => {
5661
+ const { sheet, row: row2, col } = parseKey(key);
5662
+ if (sheet !== editedSheet) return key;
5663
+ const r = shift.dim === "row" ? shiftIndex(row2, shift) : row2;
5664
+ const c = shift.dim === "col" ? shiftIndex(col, shift) : col;
5665
+ if (r === null || c === null) return null;
5666
+ return keyOf(sheet, cellRefFromRowCol(r, c));
5667
+ };
5668
+ const newSpills = /* @__PURE__ */ new Map();
5669
+ __privateGet(this, _spillOwner).clear();
5670
+ for (const [anchor, members] of __privateGet(this, _spills)) {
5671
+ const newAnchor = shiftCellKey(anchor);
5672
+ if (newAnchor === null) {
5673
+ for (const m of members) {
5674
+ const shifted = shiftCellKey(m);
5675
+ if (!shifted) continue;
5676
+ const cell = parseKey(shifted);
5677
+ __privateMethod(this, _RecalcEngine_instances, writeCell_fn).call(this, __privateGet(this, _sheetNames).get(cell.sheet) ?? cell.sheet, cell.ref, null, cell.row, cell.col);
5678
+ }
5679
+ continue;
5680
+ }
5681
+ const newMembers = /* @__PURE__ */ new Set();
5682
+ for (const m of members) {
5683
+ const shifted = shiftCellKey(m);
5684
+ if (!shifted) continue;
5685
+ newMembers.add(shifted);
5686
+ __privateGet(this, _spillOwner).set(shifted, newAnchor);
5687
+ }
5688
+ newSpills.set(newAnchor, newMembers);
5689
+ }
5690
+ __privateSet(this, _spills, newSpills);
5691
+ };
4739
5692
  // ── Internals ────────────────────────────────────────────────────────────────
4740
5693
  /**
4741
5694
  * Recomputes formulas affected by the given changed cells, to a fixpoint.
@@ -4751,13 +5704,17 @@ recalcFrom_fn = function(seeds, includeKey) {
4751
5704
  const results = [];
4752
5705
  const evaluated = /* @__PURE__ */ new Set();
4753
5706
  let pending = __privateMethod(this, _RecalcEngine_instances, dependentClosure_fn).call(this, seeds, evaluated);
4754
- for (const e of __privateGet(this, _formulas).values()) {
4755
- if (e.volatile) pending.set(keyOf(e.sheet, e.ref), e);
4756
- }
5707
+ __privateMethod(this, _RecalcEngine_instances, addVolatiles_fn).call(this, pending);
4757
5708
  if (includeKey) {
4758
5709
  const self = __privateGet(this, _formulas).get(includeKey);
4759
5710
  if (self) pending.set(includeKey, self);
4760
5711
  }
5712
+ results.push(...__privateMethod(this, _RecalcEngine_instances, runFixpoint_fn).call(this, pending, evaluated));
5713
+ return results;
5714
+ };
5715
+ /** Evaluates `pending` entries to a fixpoint, following spill-induced changes. */
5716
+ runFixpoint_fn = function(pending, evaluated) {
5717
+ const results = [];
4761
5718
  let guard = 0;
4762
5719
  const maxIterations = __privateGet(this, _formulas).size + 8;
4763
5720
  while (pending.size > 0 && guard++ <= maxIterations) {
@@ -4768,28 +5725,47 @@ recalcFrom_fn = function(seeds, includeKey) {
4768
5725
  }
4769
5726
  return results;
4770
5727
  };
4771
- /** Formula entries (not yet evaluated) whose precedents include one of the cells. */
5728
+ /** Adds every volatile formula to a pending set. */
5729
+ addVolatiles_fn = function(pending) {
5730
+ for (const key of __privateGet(this, _volatiles)) {
5731
+ const e = __privateGet(this, _formulas).get(key);
5732
+ if (e) pending.set(key, e);
5733
+ }
5734
+ };
5735
+ /**
5736
+ * Formula entries (not yet evaluated) whose precedents include one of the
5737
+ * cells, transitively. The queue is walked with a head index — Array.shift()
5738
+ * is O(n) per dequeue, which turns a long dependency chain quadratic.
5739
+ */
4772
5740
  dependentClosure_fn = function(cells, evaluated) {
4773
5741
  const out = /* @__PURE__ */ new Map();
4774
5742
  const queue = [...cells];
4775
- while (queue.length > 0) {
4776
- const cell = queue.shift();
4777
- for (const dep of __privateMethod(this, _RecalcEngine_instances, directDependents_fn).call(this, cell)) {
4778
- const k = keyOf(dep.sheet, dep.ref);
4779
- if (out.has(k) || evaluated.has(k)) continue;
4780
- out.set(k, dep);
5743
+ for (let head = 0; head < queue.length; head++) {
5744
+ const cell = queue[head];
5745
+ __privateMethod(this, _RecalcEngine_instances, forEachDirectDependent_fn).call(this, cell, (dep) => {
5746
+ if (out.has(dep.key) || evaluated.has(dep.key)) return;
5747
+ out.set(dep.key, dep);
4781
5748
  queue.push({ sheet: dep.sheet, row: dep.row, col: dep.col });
4782
- }
5749
+ });
4783
5750
  }
4784
5751
  return out;
4785
5752
  };
4786
- /** Formula entries whose precedents include the given cell. */
4787
- directDependents_fn = function(cell) {
4788
- const out = [];
4789
- for (const e of __privateGet(this, _formulas).values()) {
4790
- if (e.refs.some((ref) => refContains(ref, cell.sheet, cell.row, cell.col))) out.push(e);
4791
- }
4792
- return out;
5753
+ /**
5754
+ * Visits the formula entries whose precedents include the given cell, via the
5755
+ * reverse index. The index over-approximates (it buckets rectangles into
5756
+ * tiles), so each candidate is still confirmed against its real rectangles.
5757
+ */
5758
+ forEachDirectDependent_fn = function(cell, visit) {
5759
+ __privateGet(this, _dependents).candidates(cell.sheet, cell.row, cell.col, (key) => {
5760
+ const e = __privateGet(this, _formulas).get(key);
5761
+ if (!e) return;
5762
+ for (const ref of e.refs) {
5763
+ if (refContains(ref, cell.sheet, cell.row, cell.col)) {
5764
+ visit(e);
5765
+ return;
5766
+ }
5767
+ }
5768
+ });
4793
5769
  };
4794
5770
  /**
4795
5771
  * Topologically sorts the given entries and evaluates them, writing results
@@ -4798,32 +5774,32 @@ directDependents_fn = function(cell) {
4798
5774
  */
4799
5775
  evaluatePass_fn = function(entries) {
4800
5776
  const inSet = /* @__PURE__ */ new Map();
4801
- for (const e of entries) inSet.set(keyOf(e.sheet, e.ref), e);
5777
+ for (const e of entries) inSet.set(e.key, e);
4802
5778
  const indegree = /* @__PURE__ */ new Map();
4803
5779
  const dependents = /* @__PURE__ */ new Map();
4804
5780
  for (const k of inSet.keys()) {
4805
5781
  indegree.set(k, 0);
4806
5782
  dependents.set(k, []);
4807
5783
  }
4808
- for (const e of entries) {
4809
- const depKey = keyOf(e.sheet, e.ref);
4810
- for (const [pk, pe] of inSet) {
4811
- if (pk === depKey) continue;
4812
- if (e.refs.some((ref) => refContains(ref, pe.sheet, pe.row, pe.col))) {
4813
- dependents.get(pk).push(depKey);
4814
- indegree.set(depKey, indegree.get(depKey) + 1);
4815
- }
4816
- }
5784
+ for (const pe of entries) {
5785
+ const seen = /* @__PURE__ */ new Set();
5786
+ __privateMethod(this, _RecalcEngine_instances, forEachDirectDependent_fn).call(this, { sheet: pe.sheet, row: pe.row, col: pe.col }, (dep) => {
5787
+ if (dep.key === pe.key || !inSet.has(dep.key) || seen.has(dep.key)) return;
5788
+ seen.add(dep.key);
5789
+ dependents.get(pe.key).push(dep.key);
5790
+ indegree.set(dep.key, indegree.get(dep.key) + 1);
5791
+ });
4817
5792
  }
4818
5793
  const ready = [];
4819
5794
  for (const [k, d] of indegree) if (d === 0) ready.push(k);
4820
5795
  const order = [];
4821
- while (ready.length > 0) {
4822
- const k = ready.shift();
5796
+ for (let head = 0; head < ready.length; head++) {
5797
+ const k = ready[head];
4823
5798
  order.push(k);
4824
5799
  for (const d of dependents.get(k)) {
4825
- indegree.set(d, indegree.get(d) - 1);
4826
- if (indegree.get(d) === 0) ready.push(d);
5800
+ const left2 = indegree.get(d) - 1;
5801
+ indegree.set(d, left2);
5802
+ if (left2 === 0) ready.push(d);
4827
5803
  }
4828
5804
  }
4829
5805
  const results = [];
@@ -4837,9 +5813,9 @@ evaluatePass_fn = function(entries) {
4837
5813
  }
4838
5814
  for (const [k, e] of inSet) {
4839
5815
  if (emitted.has(k)) continue;
4840
- __privateMethod(this, _RecalcEngine_instances, clearSpill_fn).call(this, keyOf(e.sheet, e.ref), changed);
5816
+ __privateMethod(this, _RecalcEngine_instances, clearSpill_fn).call(this, e.key, changed);
4841
5817
  const value2 = FormulaValue.errorRef.toObject();
4842
- __privateGet(this, _model).setCellValue(e.sheetName, e.ref, value2);
5818
+ __privateMethod(this, _RecalcEngine_instances, writeCell_fn).call(this, e.sheetName, e.ref, value2, e.row, e.col);
4843
5819
  changed.push({ sheet: e.sheet, row: e.row, col: e.col });
4844
5820
  results.push({ sheet: e.sheetName, ref: e.ref, value: value2, circular: true });
4845
5821
  }
@@ -4852,7 +5828,7 @@ evaluatePass_fn = function(entries) {
4852
5828
  * an array shrinks, previously-spilled cells it no longer covers are cleared.
4853
5829
  */
4854
5830
  writeResult_fn = function(e) {
4855
- const anchorKey = keyOf(e.sheet, e.ref);
5831
+ const anchorKey = e.key;
4856
5832
  const fv2 = __privateMethod(this, _RecalcEngine_instances, evaluateFormulaValue_fn).call(this, e);
4857
5833
  const changed = [];
4858
5834
  const prevSpill = __privateGet(this, _spills).get(anchorKey) ?? /* @__PURE__ */ new Set();
@@ -4860,7 +5836,7 @@ writeResult_fn = function(e) {
4860
5836
  __privateMethod(this, _RecalcEngine_instances, clearSpill_fn).call(this, anchorKey, changed);
4861
5837
  __privateGet(this, _model).setCellArrayRef?.(e.sheetName, e.ref, null);
4862
5838
  const value2 = fv2.toObject();
4863
- __privateGet(this, _model).setCellValue(e.sheetName, e.ref, value2);
5839
+ __privateMethod(this, _RecalcEngine_instances, writeCell_fn).call(this, e.sheetName, e.ref, value2, e.row, e.col);
4864
5840
  changed.push({ sheet: e.sheet, row: e.row, col: e.col });
4865
5841
  return { result: { sheet: e.sheetName, ref: e.ref, value: value2 }, changed };
4866
5842
  }
@@ -4881,7 +5857,7 @@ writeResult_fn = function(e) {
4881
5857
  __privateMethod(this, _RecalcEngine_instances, clearSpill_fn).call(this, anchorKey, changed);
4882
5858
  __privateGet(this, _model).setCellArrayRef?.(e.sheetName, e.ref, null);
4883
5859
  const value2 = FormulaValue.error(9 /* Spill */).toObject();
4884
- __privateGet(this, _model).setCellValue(e.sheetName, e.ref, value2);
5860
+ __privateMethod(this, _RecalcEngine_instances, writeCell_fn).call(this, e.sheetName, e.ref, value2, e.row, e.col);
4885
5861
  changed.push({ sheet: e.sheet, row: e.row, col: e.col });
4886
5862
  return { result: { sheet: e.sheetName, ref: e.ref, value: value2 }, changed };
4887
5863
  }
@@ -4890,7 +5866,7 @@ writeResult_fn = function(e) {
4890
5866
  for (let dc = 0; dc < cols; dc++) {
4891
5867
  const r = e.row + dr, c = e.col + dc;
4892
5868
  const ref = cellRefFromRowCol(r, c);
4893
- __privateGet(this, _model).setCellValue(e.sheetName, ref, arr.get(dr, dc).toObject());
5869
+ __privateMethod(this, _RecalcEngine_instances, writeCell_fn).call(this, e.sheetName, ref, arr.get(dr, dc).toObject(), r, c);
4894
5870
  changed.push({ sheet: e.sheet, row: r, col: c });
4895
5871
  if (dr !== 0 || dc !== 0) {
4896
5872
  const cellKey2 = keyOf(e.sheet, ref);
@@ -4902,7 +5878,7 @@ writeResult_fn = function(e) {
4902
5878
  for (const oldKey of prevSpill) {
4903
5879
  if (newSpill.has(oldKey)) continue;
4904
5880
  const cell = parseKey(oldKey);
4905
- __privateGet(this, _model).setCellValue(__privateGet(this, _sheetNames).get(cell.sheet) ?? cell.sheet, cell.ref, null);
5881
+ __privateMethod(this, _RecalcEngine_instances, writeCell_fn).call(this, __privateGet(this, _sheetNames).get(cell.sheet) ?? cell.sheet, cell.ref, null, cell.row, cell.col);
4906
5882
  __privateGet(this, _spillOwner).delete(oldKey);
4907
5883
  changed.push({ sheet: cell.sheet, row: cell.row, col: cell.col });
4908
5884
  }
@@ -4929,33 +5905,60 @@ clearSpill_fn = function(anchorKey, changed) {
4929
5905
  if (!spill) return;
4930
5906
  for (const cellKey2 of spill) {
4931
5907
  const cell = parseKey(cellKey2);
4932
- __privateGet(this, _model).setCellValue(__privateGet(this, _sheetNames).get(cell.sheet) ?? cell.sheet, cell.ref, null);
5908
+ __privateMethod(this, _RecalcEngine_instances, writeCell_fn).call(this, __privateGet(this, _sheetNames).get(cell.sheet) ?? cell.sheet, cell.ref, null, cell.row, cell.col);
4933
5909
  __privateGet(this, _spillOwner).delete(cellKey2);
4934
5910
  changed.push({ sheet: cell.sheet, row: cell.row, col: cell.col });
4935
5911
  }
4936
5912
  __privateGet(this, _spills).delete(anchorKey);
4937
5913
  };
4938
5914
  evaluateFormulaValue_fn = function(e) {
4939
- const wb = __privateMethod(this, _RecalcEngine_instances, buildWorkbookLike_fn).call(this);
4940
- const ws = __privateMethod(this, _RecalcEngine_instances, sheetLike_fn).call(this, e.sheetName);
4941
- return evaluateFormula(e.formula, ws, {
4942
- workbook: wb,
5915
+ if (!e.ast) {
5916
+ try {
5917
+ e.ast = parseFormulaUncached(e.formula);
5918
+ } catch {
5919
+ return FormulaValue.errorValue;
5920
+ }
5921
+ }
5922
+ return evaluateAst(e.ast, __privateMethod(this, _RecalcEngine_instances, sheetLike_fn).call(this, e.sheetName), {
5923
+ workbook: __privateMethod(this, _RecalcEngine_instances, buildWorkbookLike_fn).call(this),
4943
5924
  formulaRow: e.row,
4944
- formulaCol: e.col
5925
+ formulaCol: e.col,
5926
+ rangeCache: __privateGet(this, _rangeCache)
4945
5927
  });
4946
5928
  };
5929
+ /**
5930
+ * Per-sheet evaluator adapter. Memoised: these hold no per-formula state, and
5931
+ * rebuilding one (with its four closures) for every formula evaluated made a
5932
+ * full recalc allocate several objects per cell.
5933
+ */
4947
5934
  sheetLike_fn = function(sheetName) {
5935
+ let cached = __privateGet(this, _sheetLikes).get(sheetName);
5936
+ if (!cached) {
5937
+ cached = __privateMethod(this, _RecalcEngine_instances, buildSheetLike_fn).call(this, sheetName);
5938
+ __privateGet(this, _sheetLikes).set(sheetName, cached);
5939
+ }
5940
+ return cached;
5941
+ };
5942
+ buildSheetLike_fn = function(sheetName) {
5943
+ const model = __privateGet(this, _model);
4948
5944
  return {
4949
5945
  name: sheetName,
4950
5946
  getCellValue: (ref) => __privateGet(this, _model).getCellValue(sheetName, ref),
5947
+ getCellValueAt: model.getCellValueAt ? (row2, column2) => model.getCellValueAt(sheetName, row2, column2) : void 0,
5948
+ // A getter, not a snapshot: the extent grows as formulas write results,
5949
+ // and a whole-column reference read later in the same pass must see it.
5950
+ get usedBounds() {
5951
+ return model.getUsedBounds?.(sheetName);
5952
+ },
4951
5953
  isRowHidden: __privateGet(this, _model).isRowHidden ? (row2) => __privateGet(this, _model).isRowHidden(sheetName, row2) : void 0,
4952
5954
  getCellFormula: __privateGet(this, _model).getCellFormula ? (ref) => __privateGet(this, _model).getCellFormula(sheetName, ref) : void 0,
4953
5955
  getSpillRange: __privateGet(this, _model).getSpillRange ? (ref) => __privateGet(this, _model).getSpillRange(sheetName, ref) : void 0
4954
5956
  };
4955
5957
  };
4956
5958
  buildWorkbookLike_fn = function() {
5959
+ if (__privateGet(this, _workbookLike)) return __privateGet(this, _workbookLike);
4957
5960
  const self = this;
4958
- return {
5961
+ return __privateSet(this, _workbookLike, {
4959
5962
  get worksheets() {
4960
5963
  return [...__privateGet(self, _sheetNames).values()].map((n) => {
4961
5964
  var _a;
@@ -4967,9 +5970,9 @@ buildWorkbookLike_fn = function() {
4967
5970
  return __privateMethod(_a = self, _RecalcEngine_instances, sheetLike_fn).call(_a, name);
4968
5971
  },
4969
5972
  getDefinedName: __privateGet(this, _model).getDefinedName ? (name, sheet) => __privateGet(this, _model).getDefinedName(name, sheet) : void 0
4970
- };
5973
+ });
4971
5974
  };
4972
5975
 
4973
- export { ArrayValue, BinaryOpNode, BinaryOperator, BlankNode, BooleanNode, CallNode, CellReferenceNode, ErrorNode, FormulaContext, FormulaError, FormulaException, FormulaHelper, FormulaLexer, FormulaNode, FormulaParser, FormulaValue, FunctionCallNode, FunctionRegistry, ImplicitIntersectionNode, LruCache, NamedRangeNode, NumberNode, RangeReferenceNode, RecalcEngine, SheetCellReferenceNode, SheetRangeReferenceNode, SpillReferenceNode, StringNode, TokenType, UnaryOpNode, UnaryOperator, clearAstCache, createWorkbookRecalcModel, evaluateFormula, extractReferences, getAstCacheStats, parseFormula, refContains, registerAggregate, registerArray, registerDate, registerFinance, registerInfo, registerLogical, registerLookup, registerMath, registerMeta, registerStatistical, registerText, setAstCacheCapacity };
5976
+ export { ArrayValue, BinaryOpNode, BinaryOperator, BlankNode, BooleanNode, CallNode, CellReferenceNode, ErrorNode, FormulaContext, FormulaError, FormulaException, FormulaHelper, FormulaLexer, FormulaNode, FormulaParser, FormulaValue, FullRangeReferenceNode, FunctionCallNode, FunctionRegistry, ImplicitIntersectionNode, LruCache, NamedRangeNode, NumberNode, RangeCache, RangeReferenceNode, RecalcEngine, SheetCellReferenceNode, SheetRangeReferenceNode, SpillReferenceNode, StringNode, TokenType, UnaryOpNode, UnaryOperator, clearAstCache, createWorkbookRecalcModel, evaluateAst, evaluateFormula, extractReferences, getAstCacheStats, parseFormula, parseFormulaUncached, refContains, registerAggregate, registerArray, registerDate, registerFinance, registerInfo, registerLogical, registerLookup, registerMath, registerMeta, registerStatistical, registerText, setAstCacheCapacity };
4974
5977
  //# sourceMappingURL=index.js.map
4975
5978
  //# sourceMappingURL=index.js.map