ooxml-core 1.2.0 → 1.2.1

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.
Files changed (82) hide show
  1. package/dist/{PptxHandler-SJTINPU4.mjs → PptxHandler-VQQS4VS6.mjs} +2 -2
  2. package/dist/automation/index.cjs +1059 -328
  3. package/dist/automation/index.mjs +1059 -328
  4. package/dist/chart/bar-cluster-geometry.d.ts +6 -0
  5. package/dist/chart/bar-cluster-geometry.d.ts.map +1 -1
  6. package/dist/chart/index.cjs +5 -2
  7. package/dist/chart/index.mjs +5 -3
  8. package/dist/chart/render/chart-spacing.d.ts.map +1 -1
  9. package/dist/{chunk-TEOAOAIF.mjs → chunk-5U27IVB7.mjs} +3 -3
  10. package/dist/{chunk-VYTA2OUN.mjs → chunk-5VLNF4OX.mjs} +1 -1
  11. package/dist/{chunk-CQQAPUAG.mjs → chunk-V2BIGIH4.mjs} +11 -9
  12. package/dist/{chunk-FI4RSJ24.mjs → chunk-XBNSBDNT.mjs} +1 -1
  13. package/dist/{chunk-XCZ7U3GZ.mjs → chunk-XSFHGZFH.mjs} +11 -9
  14. package/dist/docx/index.cjs +4 -2
  15. package/dist/docx/index.mjs +4 -2
  16. package/dist/docx/layout/index.cjs +4 -2
  17. package/dist/docx/layout/index.mjs +4 -2
  18. package/dist/docx/ui/index.cjs +4 -2
  19. package/dist/docx/ui/index.mjs +4 -2
  20. package/dist/index.cjs +5 -2
  21. package/dist/index.mjs +5 -2
  22. package/dist/pptx/automation/index.cjs +27 -17
  23. package/dist/pptx/automation/index.mjs +5 -5
  24. package/dist/pptx/chunks/index-EZJZ7USt.d.ts.map +1 -1
  25. package/dist/pptx/cli/index.cjs +27 -17
  26. package/dist/pptx/cli/index.mjs +4 -4
  27. package/dist/pptx/converter/index.mjs +2 -2
  28. package/dist/pptx/index.cjs +27 -17
  29. package/dist/pptx/index.mjs +5 -5
  30. package/dist/teams/index.cjs +157 -82
  31. package/dist/teams/index.mjs +157 -82
  32. package/dist/xlsx/edit/context.d.ts +3 -1
  33. package/dist/xlsx/edit/context.d.ts.map +1 -1
  34. package/dist/xlsx/edit/history.d.ts +18 -0
  35. package/dist/xlsx/edit/history.d.ts.map +1 -1
  36. package/dist/xlsx/edit/session-calc.d.ts +30 -8
  37. package/dist/xlsx/edit/session-calc.d.ts.map +1 -1
  38. package/dist/xlsx/edit/session.d.ts.map +1 -1
  39. package/dist/xlsx/edit/sheets.d.ts.map +1 -1
  40. package/dist/xlsx/edit/types.d.ts +23 -3
  41. package/dist/xlsx/edit/types.d.ts.map +1 -1
  42. package/dist/xlsx/formula/engine-core.d.ts.map +1 -1
  43. package/dist/xlsx/formula/engine-host.d.ts +6 -2
  44. package/dist/xlsx/formula/engine-host.d.ts.map +1 -1
  45. package/dist/xlsx/formula/engine-prepare.d.ts +33 -0
  46. package/dist/xlsx/formula/engine-prepare.d.ts.map +1 -0
  47. package/dist/xlsx/formula/engine-structure.d.ts +40 -0
  48. package/dist/xlsx/formula/engine-structure.d.ts.map +1 -0
  49. package/dist/xlsx/formula/engine-types.d.ts +26 -0
  50. package/dist/xlsx/formula/engine-types.d.ts.map +1 -1
  51. package/dist/xlsx/formula/engine.d.ts.map +1 -1
  52. package/dist/xlsx/formula/formula-shape.d.ts +28 -0
  53. package/dist/xlsx/formula/formula-shape.d.ts.map +1 -0
  54. package/dist/xlsx/formula/graph-index.d.ts +3 -1
  55. package/dist/xlsx/formula/graph-index.d.ts.map +1 -1
  56. package/dist/xlsx/formula/graph.d.ts +5 -0
  57. package/dist/xlsx/formula/graph.d.ts.map +1 -1
  58. package/dist/xlsx/formula/lexemes.d.ts.map +1 -1
  59. package/dist/xlsx/formula/parse-cache.d.ts +9 -0
  60. package/dist/xlsx/formula/parse-cache.d.ts.map +1 -0
  61. package/dist/xlsx/formula/parser.d.ts +3 -0
  62. package/dist/xlsx/formula/parser.d.ts.map +1 -1
  63. package/dist/xlsx/formula/ref-text.d.ts +14 -0
  64. package/dist/xlsx/formula/ref-text.d.ts.map +1 -0
  65. package/dist/xlsx/formula/references.d.ts.map +1 -1
  66. package/dist/xlsx/formula/shift-formula.d.ts +19 -0
  67. package/dist/xlsx/formula/shift-formula.d.ts.map +1 -0
  68. package/dist/xlsx/formula/shift-graph.d.ts +42 -0
  69. package/dist/xlsx/formula/shift-graph.d.ts.map +1 -0
  70. package/dist/xlsx/formula/transform.d.ts +2 -23
  71. package/dist/xlsx/formula/transform.d.ts.map +1 -1
  72. package/dist/xlsx/index.cjs +1069 -329
  73. package/dist/xlsx/index.mjs +1069 -330
  74. package/dist/xlsx/layout/index.d.ts +1 -1
  75. package/dist/xlsx/layout/index.d.ts.map +1 -1
  76. package/dist/xlsx/layout/sheet-queries.d.ts +7 -0
  77. package/dist/xlsx/layout/sheet-queries.d.ts.map +1 -1
  78. package/dist/xlsx/load/index.cjs +152 -90
  79. package/dist/xlsx/load/index.mjs +152 -90
  80. package/dist/xlsx/ui/index.cjs +1071 -340
  81. package/dist/xlsx/ui/index.mjs +1071 -340
  82. package/package.json +1 -1
@@ -8618,9 +8618,9 @@ function rejectUnsafeRunSegmentation(paragraph2, base, oldRuns) {
8618
8618
  (run2) => !run2.image && !run2.equation && (run2.restoredRunPropertiesXml || run2.sourceRunPropertiesXml)
8619
8619
  ))
8620
8620
  return;
8621
- const sameText = paragraph2.runs.map((run2) => run2.text).join("") === base.runs.map((run2) => run2.text).join("");
8621
+ const sameText2 = paragraph2.runs.map((run2) => run2.text).join("") === base.runs.map((run2) => run2.text).join("");
8622
8622
  const sameBoundaries = paragraph2.runs.length === base.runs.length && paragraph2.runs.every((run2, index2) => run2.text === base.runs[index2]?.text);
8623
- if (sameBoundaries || !sameText && paragraph2.runs.length === base.runs.length) return;
8623
+ if (sameBoundaries || !sameText2 && paragraph2.runs.length === base.runs.length) return;
8624
8624
  throw new Error(
8625
8625
  `Cannot edit paragraph ${paragraph2.id}: changing run boundaries could drop unsupported run properties. The original DOCX package remains unchanged.`
8626
8626
  );
@@ -12492,7 +12492,7 @@ var NAME_RE = /^[A-Za-z_\\\u00A1-\uFFFF][A-Za-z0-9_.?\\\u00A1-\uFFFF]*/;
12492
12492
  var CELL_RE = /^(\$?)([A-Za-z]{1,3})(\$?)(\d{1,7})/;
12493
12493
  var COLS_RE = /^(\$?)([A-Za-z]{1,3}):(\$?)([A-Za-z]{1,3})/;
12494
12494
  var ROWS_RE = /^(\$?)(\d{1,7}):(\$?)(\d{1,7})/;
12495
- var NUMBER_RE = /^(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/;
12495
+ var NUMBER_RE = /^(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?/;
12496
12496
  var sticky = (re) => new RegExp(re.source.replace(/^\^/, ""), "y");
12497
12497
  var CELL_AT = sticky(CELL_RE);
12498
12498
  var COLS_AT = sticky(COLS_RE);
@@ -12839,7 +12839,7 @@ function readName(source2, at2, start, prefix) {
12839
12839
  }
12840
12840
  var joinTokens = (tokens3) => tokens3.map((t) => t.text).join("");
12841
12841
 
12842
- // xlsx/formula/transform.ts
12842
+ // xlsx/formula/ref-text.ts
12843
12843
  function safeTokens(formula) {
12844
12844
  try {
12845
12845
  return tokenize(formula);
@@ -12858,50 +12858,26 @@ function formatRefSpec(spec2) {
12858
12858
  if (spec2.kind === "cell") return a2;
12859
12859
  return `${a2}:${cornerText(spec2.end, spec2.kind)}`;
12860
12860
  }
12861
- function rewrite(token, ref) {
12861
+ function rewriteRef(token, ref) {
12862
12862
  const prefix = token.prefix?.text ?? "";
12863
12863
  if (!ref) return `${prefix}#REF!`;
12864
12864
  return `${prefix}${formatRefSpec(ref)}${token.spill ? "#" : ""}`;
12865
12865
  }
12866
12866
  var sameSpec = (a2, b) => a2.kind === b.kind && a2.start.row === b.start.row && a2.start.col === b.start.col && a2.end.row === b.end.row && a2.end.col === b.end.col;
12867
- function translateFormula(formula, dRow, dCol) {
12868
- const tokens3 = safeTokens(formula);
12869
- if (!tokens3) return formula;
12870
- for (const token of tokens3) {
12871
- if (token.kind !== "ref" || !token.ref) continue;
12872
- const spec2 = token.ref;
12873
- const move = (c2) => {
12874
- const row2 = spec2.kind === "cols" || c2.rowAbs ? c2.row : c2.row + dRow;
12875
- const col = spec2.kind === "rows" || c2.colAbs ? c2.col : c2.col + dCol;
12876
- if (row2 < 0 || row2 > MAX_ROW || col < 0 || col > MAX_COL) return void 0;
12877
- return { ...c2, row: row2, col };
12878
- };
12879
- const start = move(spec2.start);
12880
- const end = move(spec2.end);
12881
- const next = start && end ? { kind: spec2.kind, start, end } : void 0;
12882
- if (next && sameSpec(next, spec2)) continue;
12883
- token.text = rewrite(token, next);
12884
- }
12885
- return joinTokens(tokens3);
12867
+ var needsQuote = (name) => !/^[A-Za-z_¡-￿][A-Za-z0-9_.¡-￿]*$/.test(name) || /^[A-Za-z]{1,3}\d+$/.test(name) || /^(?:R\d*C?\d*|C\d*)$/i.test(name) || /^(?:TRUE|FALSE)$/i.test(name);
12868
+ function prefixText(prefix) {
12869
+ const sheets = prefix.sheet2 !== void 0 ? `${prefix.sheet}:${prefix.sheet2}` : prefix.sheet;
12870
+ const book = prefix.book !== void 0 ? `[${prefix.book}]` : "";
12871
+ const quote2 = needsQuote(prefix.sheet) || prefix.sheet2 !== void 0 && needsQuote(prefix.sheet2);
12872
+ return quote2 ? `'${(book + sheets).replace(/'/g, "''")}'!` : `${book}${sheets}!`;
12886
12873
  }
12887
- var sheetMatches = (prefix, formulaSheet, sheet) => {
12888
- if (prefix?.book !== void 0) return false;
12889
- const name = prefix ? prefix.sheet : formulaSheet;
12890
- return name.toLowerCase() === sheet.toLowerCase();
12891
- };
12892
- function shiftInterval(a2, b, at2, count2, max) {
12893
- if (count2 > 0) {
12894
- const na2 = a2 >= at2 ? a2 + count2 : a2;
12895
- const nb2 = b >= at2 ? b + count2 : b;
12896
- if (nb2 > max) return na2 > max ? void 0 : [na2, max];
12897
- return [na2, nb2];
12898
- }
12899
- const n = -count2;
12900
- const last = at2 + n - 1;
12901
- if (a2 >= at2 && b <= last) return void 0;
12902
- const na = a2 < at2 ? a2 : a2 <= last ? at2 : a2 - n;
12903
- const nb = b < at2 ? b : b <= last ? at2 - 1 : b - n;
12904
- return [na, nb];
12874
+
12875
+ // xlsx/formula/shift-formula.ts
12876
+ var templates = /* @__PURE__ */ new Map();
12877
+ var MAX_TEMPLATES = 2e5;
12878
+ function remember2(text2, template) {
12879
+ if (templates.size >= MAX_TEMPLATES) templates.clear();
12880
+ templates.set(text2, template);
12905
12881
  }
12906
12882
  function mergeSpacedAreas(tokens3) {
12907
12883
  const out = [];
@@ -12928,63 +12904,145 @@ function mergeSpacedAreas(tokens3) {
12928
12904
  }
12929
12905
  return out;
12930
12906
  }
12931
- function shiftFormula(formula, formulaSheet, spec2) {
12932
- if (spec2.count === 0) return formula;
12907
+ function templateOf(formula) {
12908
+ const cached3 = templates.get(formula);
12909
+ if (cached3 !== void 0) return cached3;
12933
12910
  const raw2 = safeTokens(formula);
12934
- if (!raw2) return formula;
12935
- const tokens3 = mergeSpacedAreas(raw2);
12936
- const isRow = spec2.axis === "row";
12937
- for (const token of tokens3) {
12938
- if (token.kind !== "ref" || !token.ref || !sheetMatches(token.prefix, formulaSheet, spec2.sheet))
12939
- continue;
12940
- const ref = token.ref;
12941
- if (isRow && ref.kind === "cols" || !isRow && ref.kind === "rows") continue;
12942
- const startFirst = isRow ? ref.start.row <= ref.end.row : ref.start.col <= ref.end.col;
12943
- const lo = startFirst ? ref.start : ref.end;
12944
- const hi = startFirst ? ref.end : ref.start;
12945
- const shifted2 = shiftInterval(
12946
- isRow ? lo.row : lo.col,
12947
- isRow ? hi.row : hi.col,
12948
- spec2.at,
12949
- spec2.count,
12950
- isRow ? MAX_ROW : MAX_COL
12951
- );
12952
- let next;
12953
- if (shifted2) {
12954
- const [a2, b] = shifted2;
12955
- const nlo = isRow ? { ...lo, row: a2 } : { ...lo, col: a2 };
12956
- const nhi = isRow ? { ...hi, row: b } : { ...hi, col: b };
12957
- next = { kind: ref.kind, start: startFirst ? nlo : nhi, end: startFirst ? nhi : nlo };
12958
- if (ref.kind === "cell") next.end = next.start;
12911
+ let template = null;
12912
+ if (raw2) {
12913
+ template = [];
12914
+ let literal2 = "";
12915
+ for (const token of mergeSpacedAreas(raw2)) {
12916
+ if (token.kind !== "ref" && !(token.kind === "name" && token.prefix)) {
12917
+ literal2 += token.text;
12918
+ continue;
12919
+ }
12920
+ if (literal2) template.push(literal2);
12921
+ literal2 = "";
12922
+ const part = { kind: token.kind, text: token.text };
12923
+ if (token.prefix) {
12924
+ part.prefix = token.prefix;
12925
+ if (token.prefix.book === void 0) part.sheet = token.prefix.sheet.toLowerCase();
12926
+ }
12927
+ if (token.ref) part.ref = token.ref;
12928
+ if (token.spill) part.spill = true;
12929
+ template.push(part);
12959
12930
  }
12960
- if (next && sameSpec(next, ref)) continue;
12961
- token.text = rewrite(token, next);
12931
+ if (literal2) template.push(literal2);
12962
12932
  }
12963
- return joinTokens(tokens3);
12933
+ remember2(formula, template);
12934
+ return template;
12964
12935
  }
12965
- var needsQuote = (name) => !/^[A-Za-z_\u00A1-\uFFFF][A-Za-z0-9_.\u00A1-\uFFFF]*$/.test(name) || /^[A-Za-z]{1,3}\d+$/.test(name) || /^(?:R\d*C?\d*|C\d*)$/i.test(name) || /^(?:TRUE|FALSE)$/i.test(name);
12966
- function prefixText(prefix) {
12967
- const sheets = prefix.sheet2 !== void 0 ? `${prefix.sheet}:${prefix.sheet2}` : prefix.sheet;
12968
- const book = prefix.book !== void 0 ? `[${prefix.book}]` : "";
12969
- const quote2 = needsQuote(prefix.sheet) || prefix.sheet2 !== void 0 && needsQuote(prefix.sheet2);
12970
- return quote2 ? `'${(book + sheets).replace(/'/g, "''")}'!` : `${book}${sheets}!`;
12936
+ function rewriteTemplate(formula, rewrite2) {
12937
+ const template = templateOf(formula);
12938
+ if (!template) return formula;
12939
+ let next;
12940
+ for (let i2 = 0; i2 < template.length; i2++) {
12941
+ const part = template[i2];
12942
+ if (typeof part === "string") continue;
12943
+ const changed = rewrite2(part);
12944
+ if (!changed) continue;
12945
+ next ??= template.slice();
12946
+ next[i2] = changed;
12947
+ }
12948
+ if (!next) return formula;
12949
+ const text2 = next.map((part) => typeof part === "string" ? part : part.text).join("");
12950
+ remember2(text2, next);
12951
+ return text2;
12952
+ }
12953
+ function shiftInterval(a2, b, at2, count2, max) {
12954
+ if (count2 > 0) {
12955
+ const na2 = a2 >= at2 ? a2 + count2 : a2;
12956
+ const nb2 = b >= at2 ? b + count2 : b;
12957
+ if (nb2 > max) return na2 > max ? void 0 : [na2, max];
12958
+ return [na2, nb2];
12959
+ }
12960
+ const n = -count2;
12961
+ const last = at2 + n - 1;
12962
+ if (a2 >= at2 && b <= last) return void 0;
12963
+ const na = a2 < at2 ? a2 : a2 <= last ? at2 : a2 - n;
12964
+ const nb = b < at2 ? b : b <= last ? at2 - 1 : b - n;
12965
+ return [na, nb];
12966
+ }
12967
+ function shiftRef(ref, spec2) {
12968
+ const isRow = spec2.axis === "row";
12969
+ if (isRow && ref.kind === "cols" || !isRow && ref.kind === "rows") return ref;
12970
+ const startFirst = isRow ? ref.start.row <= ref.end.row : ref.start.col <= ref.end.col;
12971
+ const lo = startFirst ? ref.start : ref.end;
12972
+ const hi = startFirst ? ref.end : ref.start;
12973
+ const shifted2 = shiftInterval(
12974
+ isRow ? lo.row : lo.col,
12975
+ isRow ? hi.row : hi.col,
12976
+ spec2.at,
12977
+ spec2.count,
12978
+ isRow ? MAX_ROW : MAX_COL
12979
+ );
12980
+ if (!shifted2) return void 0;
12981
+ const [a2, b] = shifted2;
12982
+ const nlo = isRow ? { ...lo, row: a2 } : { ...lo, col: a2 };
12983
+ const nhi = isRow ? { ...hi, row: b } : { ...hi, col: b };
12984
+ const next = {
12985
+ kind: ref.kind,
12986
+ start: startFirst ? nlo : nhi,
12987
+ end: startFirst ? nhi : nlo
12988
+ };
12989
+ if (ref.kind === "cell") next.end = next.start;
12990
+ return sameSpec(next, ref) ? ref : next;
12991
+ }
12992
+ function shiftFormula(formula, formulaSheet, spec2) {
12993
+ if (spec2.count === 0) return formula;
12994
+ const sheet = spec2.sheet.toLowerCase();
12995
+ const own = formulaSheet.toLowerCase() === sheet;
12996
+ return rewriteTemplate(formula, (part) => {
12997
+ if (part.kind !== "ref" || !part.ref || (part.prefix ? part.sheet !== sheet : !own))
12998
+ return void 0;
12999
+ const next = shiftRef(part.ref, spec2);
13000
+ if (next === part.ref) return void 0;
13001
+ const out = { kind: "ref", text: rewriteRef(part, next) };
13002
+ if (part.prefix) out.prefix = part.prefix;
13003
+ if (part.sheet !== void 0) out.sheet = part.sheet;
13004
+ if (next) out.ref = next;
13005
+ if (part.spill) out.spill = true;
13006
+ return out;
13007
+ });
12971
13008
  }
12972
13009
  function renameSheetInFormula(formula, oldName, newName) {
12973
- const tokens3 = safeTokens(formula);
12974
- if (!tokens3) return formula;
12975
13010
  const lower2 = oldName.toLowerCase();
12976
- for (const token of tokens3) {
12977
- const prefix = token.prefix;
12978
- if (!prefix || prefix.book !== void 0) continue;
12979
- const first3 = prefix.sheet.toLowerCase() === lower2;
13011
+ return rewriteTemplate(formula, (part) => {
13012
+ const prefix = part.prefix;
13013
+ if (!prefix || part.sheet === void 0) return void 0;
13014
+ const first3 = part.sheet === lower2;
12980
13015
  const second = prefix.sheet2?.toLowerCase() === lower2;
12981
- if (!first3 && !second) continue;
13016
+ if (!first3 && !second) return void 0;
12982
13017
  const renamed = { ...prefix, sheet: first3 ? newName : prefix.sheet };
12983
13018
  if (prefix.sheet2 !== void 0) renamed.sheet2 = second ? newName : prefix.sheet2;
12984
13019
  const text2 = prefixText(renamed);
12985
- token.text = text2 + token.text.slice(prefix.text.length);
12986
13020
  renamed.text = text2;
12987
- token.prefix = renamed;
13021
+ const out = { ...part, text: text2 + part.text.slice(prefix.text.length) };
13022
+ out.prefix = renamed;
13023
+ out.sheet = renamed.sheet.toLowerCase();
13024
+ return out;
13025
+ });
13026
+ }
13027
+
13028
+ // xlsx/formula/transform.ts
13029
+ function translateFormula(formula, dRow, dCol) {
13030
+ const tokens3 = safeTokens(formula);
13031
+ if (!tokens3) return formula;
13032
+ for (const token of tokens3) {
13033
+ if (token.kind !== "ref" || !token.ref) continue;
13034
+ const spec2 = token.ref;
13035
+ const move = (c2) => {
13036
+ const row2 = spec2.kind === "cols" || c2.rowAbs ? c2.row : c2.row + dRow;
13037
+ const col = spec2.kind === "rows" || c2.colAbs ? c2.col : c2.col + dCol;
13038
+ if (row2 < 0 || row2 > MAX_ROW || col < 0 || col > MAX_COL) return void 0;
13039
+ return { ...c2, row: row2, col };
13040
+ };
13041
+ const start = move(spec2.start);
13042
+ const end = move(spec2.end);
13043
+ const next = start && end ? { kind: spec2.kind, start, end } : void 0;
13044
+ if (next && sameSpec(next, spec2)) continue;
13045
+ token.text = rewriteRef(token, next);
12988
13046
  }
12989
13047
  return joinTokens(tokens3);
12990
13048
  }
@@ -13073,7 +13131,7 @@ var FUTURE_FUNCTIONS = new Set(
13073
13131
  "AGGREGATE BETA.DIST BETA.INV BINOM.DIST BINOM.INV CEILING.PRECISE CHISQ.DIST CHISQ.DIST.RT CHISQ.INV CHISQ.INV.RT CHISQ.TEST CONFIDENCE.NORM CONFIDENCE.T COVARIANCE.P COVARIANCE.S ERF.PRECISE ERFC.PRECISE EXPON.DIST F.DIST F.DIST.RT F.INV F.INV.RT F.TEST FLOOR.PRECISE GAMMA.DIST GAMMA.INV GAMMALN.PRECISE HYPGEOM.DIST LOGNORM.DIST LOGNORM.INV MODE.MULT MODE.SNGL NEGBINOM.DIST NETWORKDAYS.INTL NORM.DIST NORM.INV NORM.S.DIST NORM.S.INV PERCENTILE.EXC PERCENTILE.INC PERCENTRANK.EXC PERCENTRANK.INC POISSON.DIST QUARTILE.EXC QUARTILE.INC RANK.AVG RANK.EQ STDEV.P STDEV.S T.DIST T.DIST.2T T.DIST.RT T.INV T.INV.2T T.TEST VAR.P VAR.S WEIBULL.DIST WORKDAY.INTL ACOT ACOTH ARABIC BASE BINOM.DIST.RANGE BITAND BITLSHIFT BITOR BITRSHIFT BITXOR CEILING.MATH COMBINA COT COTH CSC CSCH DAYS DECIMAL ENCODEURL FILTERXML FLOOR.MATH FORMULATEXT GAMMA GAUSS IFNA IMCOSH IMCOT IMCSC IMCSCH IMSEC IMSECH IMSINH IMTAN ISFORMULA ISOWEEKNUM MUNIT NUMBERVALUE PDURATION PERMUTATIONA PHI RRI SEC SECH SHEET SHEETS SKEW.P UNICHAR UNICODE WEBSERVICE XOR CONCAT FORECAST.ETS FORECAST.ETS.CONFINT FORECAST.ETS.SEASONALITY FORECAST.ETS.STAT FORECAST.LINEAR IFS MAXIFS MINIFS SWITCH TEXTJOIN XLOOKUP XMATCH FILTER SORT SORTBY UNIQUE SEQUENCE RANDARRAY LET LAMBDA TEXTBEFORE TEXTAFTER TEXTSPLIT VSTACK HSTACK TAKE DROP CHOOSEROWS CHOOSECOLS TOCOL TOROW WRAPROWS WRAPCOLS EXPAND ARRAYTOTEXT VALUETOTEXT ISOMITTED BYROW BYCOL MAP REDUCE SCAN MAKEARRAY IMAGE GROUPBY PIVOTBY PERCENTOF REGEXTEST REGEXEXTRACT REGEXREPLACE TRIMRANGE ANCHORARRAY SINGLE STOCKHISTORY FIELDVALUE".split(" ")
13074
13132
  );
13075
13133
  var WORKSHEET_PREFIXED = /* @__PURE__ */ new Set(["FILTER", "SORT"]);
13076
- function rewrite2(formula, visit2) {
13134
+ function rewrite(formula, visit2) {
13077
13135
  const out = [];
13078
13136
  let i2 = 0;
13079
13137
  while (i2 < formula.length) {
@@ -13116,7 +13174,7 @@ function rewrite2(formula, visit2) {
13116
13174
  }
13117
13175
  function stripFuturePrefixes(formula) {
13118
13176
  if (!formula.includes("_xl")) return formula;
13119
- const plain2 = rewrite2(formula, (token, next, out) => {
13177
+ const plain2 = rewrite(formula, (token, next, out) => {
13120
13178
  if (!next.startsWith("(")) return false;
13121
13179
  const stripped = token.replace(/^(_xlfn\.)?(_xlws\.)?/i, "");
13122
13180
  if (stripped === token) return false;
@@ -13126,7 +13184,7 @@ function stripFuturePrefixes(formula) {
13126
13184
  return fromStoredSyntax(plain2);
13127
13185
  }
13128
13186
  function addFuturePrefixes(formula) {
13129
- return rewrite2(toStoredSyntax(formula), (token, next, out) => {
13187
+ return rewrite(toStoredSyntax(formula), (token, next, out) => {
13130
13188
  if (!next.startsWith("(")) return false;
13131
13189
  const upper2 = token.toUpperCase();
13132
13190
  if (!FUTURE_FUNCTIONS.has(upper2)) return false;
@@ -13779,13 +13837,13 @@ function dataBarLengths(rule2, node) {
13779
13837
  maxLength: rule2.maxLength ?? numAttr(bar, "maxLength") ?? 90
13780
13838
  };
13781
13839
  }
13782
- function rewriteConditionalExtension(rule2, rewrite3) {
13840
+ function rewriteConditionalExtension(rule2, rewrite2) {
13783
13841
  if (!rule2.extensionXml) return;
13784
13842
  const doc = parseXml(rule2.extensionXml);
13785
13843
  let changed = false;
13786
13844
  for (const formula of descendants3(doc.documentElement, "f", XM)) {
13787
13845
  const before = formula.textContent ?? "";
13788
- const next = rewrite3(before);
13846
+ const next = rewrite2(before);
13789
13847
  if (before !== next) {
13790
13848
  formula.textContent = next;
13791
13849
  changed = true;
@@ -17018,7 +17076,9 @@ var PREC_INTERSECT = 9;
17018
17076
  var PREC_RANGE = 10;
17019
17077
  var OPERAND_START = /* @__PURE__ */ new Set(["ref", "name", "func", "open", "structured"]);
17020
17078
  function parseFormula(formula) {
17021
- const all2 = tokenize(formula);
17079
+ return parseTokens(tokenize(formula));
17080
+ }
17081
+ function parseTokens(all2) {
17022
17082
  const tokens3 = [];
17023
17083
  const wsBefore = [];
17024
17084
  let sawWs = false;
@@ -17359,6 +17419,222 @@ function localDateToSerial(date, date1904 = date1904Mode) {
17359
17419
  return base + (utc - dayStart) / DAY_MS2;
17360
17420
  }
17361
17421
 
17422
+ // xlsx/formula/area-index.ts
17423
+ var BUCKET_WIDTH = 64;
17424
+ var CHUNK_SHIFT = 6;
17425
+ var CHUNKS = 64;
17426
+ function push(map, key2, value) {
17427
+ const list = map.get(key2);
17428
+ if (list) list.push(value);
17429
+ else map.set(key2, [value]);
17430
+ }
17431
+ function inner(map, sheet) {
17432
+ let found = map.get(sheet);
17433
+ if (!found) map.set(sheet, found = /* @__PURE__ */ new Map());
17434
+ return found;
17435
+ }
17436
+ var AreaIndex = class {
17437
+ cells = /* @__PURE__ */ new Map();
17438
+ chunked = /* @__PURE__ */ new Map();
17439
+ tall = /* @__PURE__ */ new Map();
17440
+ wide = /* @__PURE__ */ new Map();
17441
+ add(area, node) {
17442
+ const { start, end } = area.range;
17443
+ if (start.row === end.row && end.col - start.col + 1 <= BUCKET_WIDTH) {
17444
+ const cells3 = inner(this.cells, area.sheet);
17445
+ for (let col = start.col; col <= end.col; col++) push(cells3, cellKey(start.row, col), node);
17446
+ return;
17447
+ }
17448
+ const entry3 = { range: area.range, node };
17449
+ if (end.col - start.col + 1 > BUCKET_WIDTH) {
17450
+ const list = this.wide.get(area.sheet);
17451
+ if (list) list.push(entry3);
17452
+ else this.wide.set(area.sheet, [entry3]);
17453
+ return;
17454
+ }
17455
+ const first3 = start.row >> CHUNK_SHIFT;
17456
+ const last = end.row >> CHUNK_SHIFT;
17457
+ if (last - first3 + 1 > CHUNKS) {
17458
+ const cols2 = inner(this.tall, area.sheet);
17459
+ for (let c2 = start.col; c2 <= end.col; c2++) push(cols2, c2, entry3);
17460
+ return;
17461
+ }
17462
+ const cols = inner(this.chunked, area.sheet);
17463
+ for (let c2 = start.col; c2 <= end.col; c2++) {
17464
+ let chunks = cols.get(c2);
17465
+ if (!chunks) cols.set(c2, chunks = /* @__PURE__ */ new Map());
17466
+ for (let k = first3; k <= last; k++) push(chunks, k, entry3);
17467
+ }
17468
+ }
17469
+ /** Items whose areas intersect `range` on `sheet` (each once). */
17470
+ dependents(sheet, range, out) {
17471
+ const found = out instanceof Set ? out : /* @__PURE__ */ new Set();
17472
+ this.collect(sheet, range, found);
17473
+ if (Array.isArray(out)) for (const node of found) out.push(node);
17474
+ }
17475
+ collect(sheet, range, out) {
17476
+ const { start, end } = range;
17477
+ const check3 = (list) => {
17478
+ if (list) {
17479
+ for (const entry3 of list) if (rangesIntersect(entry3.range, range)) out.add(entry3.node);
17480
+ }
17481
+ };
17482
+ const cells3 = this.cells.get(sheet);
17483
+ if (cells3) {
17484
+ const size = (end.row - start.row + 1) * (end.col - start.col + 1);
17485
+ if (size <= 4096) {
17486
+ for (let r = start.row; r <= end.row; r++) {
17487
+ for (let c2 = start.col; c2 <= end.col; c2++) {
17488
+ const list = cells3.get(cellKey(r, c2));
17489
+ if (list) for (const node of list) out.add(node);
17490
+ }
17491
+ }
17492
+ } else {
17493
+ for (const [key2, list] of cells3) {
17494
+ const { row: row2, col } = keyToAddress(key2);
17495
+ if (row2 >= start.row && row2 <= end.row && col >= start.col && col <= end.col)
17496
+ for (const node of list) out.add(node);
17497
+ }
17498
+ }
17499
+ }
17500
+ const columns = (map, visit2) => {
17501
+ if (!map) return;
17502
+ if (end.col - start.col + 1 <= map.size) {
17503
+ for (let c2 = start.col; c2 <= end.col; c2++) if (map.has(c2)) visit2(c2);
17504
+ } else for (const c2 of map.keys()) if (c2 >= start.col && c2 <= end.col) visit2(c2);
17505
+ };
17506
+ const chunked = this.chunked.get(sheet);
17507
+ const first3 = start.row >> CHUNK_SHIFT;
17508
+ const last = end.row >> CHUNK_SHIFT;
17509
+ columns(chunked, (c2) => {
17510
+ const chunks = chunked?.get(c2);
17511
+ if (last - first3 + 1 <= chunks.size) {
17512
+ for (let k = first3; k <= last; k++) check3(chunks.get(k));
17513
+ } else for (const [k, list] of chunks) if (k >= first3 && k <= last) check3(list);
17514
+ });
17515
+ const tall = this.tall.get(sheet);
17516
+ columns(tall, (c2) => check3(tall?.get(c2)));
17517
+ check3(this.wide.get(sheet));
17518
+ }
17519
+ };
17520
+
17521
+ // xlsx/formula/graph-index.ts
17522
+ var ColumnIndex = class {
17523
+ sheets = /* @__PURE__ */ new Map();
17524
+ constructor(nodes) {
17525
+ for (const node of nodes) {
17526
+ let cols = this.sheets.get(node.sheet);
17527
+ if (!cols) this.sheets.set(node.sheet, cols = /* @__PURE__ */ new Map());
17528
+ let list = cols.get(node.col);
17529
+ if (!list) cols.set(node.col, list = []);
17530
+ list.push(node);
17531
+ }
17532
+ for (const cols of this.sheets.values())
17533
+ for (const list of cols.values()) list.sort((a2, b) => a2.row - b.row);
17534
+ }
17535
+ nodesIn(area, out) {
17536
+ const cols = this.sheets.get(area.sheet);
17537
+ if (!cols) return;
17538
+ const { start, end } = area.range;
17539
+ const width = end.col - start.col + 1;
17540
+ const scan3 = (list) => {
17541
+ let lo = 0;
17542
+ let hi = list.length;
17543
+ while (lo < hi) {
17544
+ const mid = lo + hi >> 1;
17545
+ if (list[mid].row < start.row) lo = mid + 1;
17546
+ else hi = mid;
17547
+ }
17548
+ for (let i2 = lo; i2 < list.length; i2++) {
17549
+ const node = list[i2];
17550
+ if (node.row > end.row) break;
17551
+ out.push(node);
17552
+ }
17553
+ };
17554
+ if (width <= cols.size) {
17555
+ for (let c2 = start.col; c2 <= end.col; c2++) {
17556
+ const list = cols.get(c2);
17557
+ if (list) scan3(list);
17558
+ }
17559
+ } else {
17560
+ for (const [c2, list] of cols) if (c2 >= start.col && c2 <= end.col) scan3(list);
17561
+ }
17562
+ }
17563
+ };
17564
+ var ReverseIndex = class extends AreaIndex {
17565
+ constructor(nodes = []) {
17566
+ super();
17567
+ for (const node of nodes) this.addNode(node);
17568
+ }
17569
+ /** Indexes one more formula's precedents. */
17570
+ addNode(node) {
17571
+ for (const dep of node.deps) this.add(dep, node);
17572
+ for (const dep of node.dynamicDeps) this.add(dep, node);
17573
+ }
17574
+ };
17575
+
17576
+ // xlsx/formula/engine-prepare.ts
17577
+ var CHECK_EVERY = 32;
17578
+ var GraphPreparation = class {
17579
+ constructor(workbook, create) {
17580
+ this.workbook = workbook;
17581
+ this.create = create;
17582
+ }
17583
+ workbook;
17584
+ create;
17585
+ reverse = new ReverseIndex();
17586
+ formulas = { dynamic: [], unvalued: [] };
17587
+ sheet = 0;
17588
+ rows;
17589
+ row = 0;
17590
+ cells;
17591
+ finished = false;
17592
+ /**
17593
+ * Adds formulas to the graph until `timeRemaining` (milliseconds, as an idle deadline reports
17594
+ * them) reaches zero, or until every formula is in. Returns true when the graph is complete.
17595
+ */
17596
+ run(timeRemaining) {
17597
+ let visited = 0;
17598
+ while (!this.finished) {
17599
+ const cells3 = this.cells ?? this.nextRow();
17600
+ if (!cells3) continue;
17601
+ const next = cells3.next();
17602
+ if (next.done) {
17603
+ this.cells = void 0;
17604
+ continue;
17605
+ }
17606
+ const [col, cell] = next.value;
17607
+ if (cell.formula) this.add(this.create(this.sheet, this.row, col, cell), cell);
17608
+ if (timeRemaining && ++visited % CHECK_EVERY === 0 && timeRemaining() <= 0) break;
17609
+ }
17610
+ return this.finished;
17611
+ }
17612
+ add(node, cell) {
17613
+ this.reverse.addNode(node);
17614
+ if (!node.legacy && !node.arrayRange) this.formulas.dynamic.push(node);
17615
+ if (cell.value === null && !node.keepCached) this.formulas.unvalued.push(node);
17616
+ }
17617
+ /** Moves to the next stored row (and sheet), or marks the walk finished. */
17618
+ nextRow() {
17619
+ for (; ; ) {
17620
+ const sheet = this.workbook.sheets[this.sheet];
17621
+ if (!sheet) {
17622
+ this.finished = true;
17623
+ return void 0;
17624
+ }
17625
+ this.rows ??= sheet.rows.entries();
17626
+ const next = this.rows.next();
17627
+ if (!next.done) {
17628
+ this.row = next.value[0];
17629
+ this.cells = next.value[1].entries();
17630
+ return this.cells;
17631
+ }
17632
+ this.rows = void 0;
17633
+ this.sheet++;
17634
+ }
17635
+ }
17636
+ };
17637
+
17362
17638
  // xlsx/formula/text-number.ts
17363
17639
  var MONTHS2 = [
17364
17640
  "jan",
@@ -20493,8 +20769,10 @@ var FINANCIAL_FUNCTIONS = [
20493
20769
 
20494
20770
  // xlsx/formula/references.ts
20495
20771
  function sheetIndex(host, name) {
20772
+ const sheets = host.workbook.sheets;
20773
+ for (let i2 = 0; i2 < sheets.length; i2++) if (sheets[i2]?.name === name) return i2;
20496
20774
  const lower2 = name.toLowerCase();
20497
- return host.workbook.sheets.findIndex((sheet) => sheet.name.toLowerCase() === lower2);
20775
+ return sheets.findIndex((sheet) => sheet.name.toLowerCase() === lower2);
20498
20776
  }
20499
20777
  function prefixSheets(host, prefix, current) {
20500
20778
  if (!prefix) return [current];
@@ -22108,9 +22386,9 @@ function readBlock(ctx, area, rows, cols) {
22108
22386
  if (host.readBlock) return host.readBlock(area.sheet, range);
22109
22387
  return Matrix.build(rows, cols, (r, c2) => ctx.readCell(area.sheet, row2 + r, col + c2));
22110
22388
  }
22111
- function shapedMatrices(ctx, values2, sameShape) {
22389
+ function shapedMatrices(ctx, values2, sameShape2) {
22112
22390
  let { rows, cols } = shapeOf(values2[0] ?? null);
22113
- if (sameShape) {
22391
+ if (sameShape2) {
22114
22392
  for (const value of values2) {
22115
22393
  const shape2 = shapeOf(value);
22116
22394
  if (shape2.rows !== rows || shape2.cols !== cols) fail(ERR.VALUE);
@@ -22136,12 +22414,12 @@ function shapedMatrices(ctx, values2, sameShape) {
22136
22414
  });
22137
22415
  return { matrices, total: total2, block: rows * cols };
22138
22416
  }
22139
- function matchingCells(ctx, pairs, target, sameShape) {
22417
+ function matchingCells(ctx, pairs, target, sameShape2) {
22140
22418
  const ranges = pairs.map((p) => p.range);
22141
22419
  const { matrices, total: total2, block } = shapedMatrices(
22142
22420
  ctx,
22143
22421
  target === void 0 ? ranges : [...ranges, target],
22144
- sameShape
22422
+ sameShape2
22145
22423
  );
22146
22424
  const tests = pairs.map((p) => makeCriteria(scalar(p.criteria)));
22147
22425
  const values2 = matrices[matrices.length - 1];
@@ -22180,7 +22458,7 @@ function indexedCandidates(pairs, matrices) {
22180
22458
  }
22181
22459
  return void 0;
22182
22460
  }
22183
- var matchingValues = (ctx, pairs, target, sameShape) => matchingCells(ctx, pairs, target, sameShape).values;
22461
+ var matchingValues = (ctx, pairs, target, sameShape2) => matchingCells(ctx, pairs, target, sameShape2).values;
22184
22462
  function criteriaPairs(args, from) {
22185
22463
  if ((args.length - from) % 2 !== 0) fail(ERR.VALUE);
22186
22464
  const pairs = [];
@@ -26074,7 +26352,7 @@ var REFERENCE_ARGUMENT = /* @__PURE__ */ new Set([
26074
26352
  "AREAS",
26075
26353
  "ISREF"
26076
26354
  ]);
26077
- var push = (c2, area) => {
26355
+ var push2 = (c2, area) => {
26078
26356
  (c2.dynamic > 0 ? c2.dynamicDeps : c2.deps).push(area);
26079
26357
  };
26080
26358
  function spans(areas) {
@@ -26100,7 +26378,7 @@ function visit(node, c2) {
26100
26378
  if (!sheets) return;
26101
26379
  const range = specRange(node.ref);
26102
26380
  const target = node.spill ? { start: range.start, end: { ...range.start } } : range;
26103
- for (const sheet of sheets) push(c2, { sheet, range: target });
26381
+ for (const sheet of sheets) push2(c2, { sheet, range: target });
26104
26382
  return;
26105
26383
  }
26106
26384
  case "name": {
@@ -26174,7 +26452,7 @@ function visitBinary(node, c2) {
26174
26452
  const list = c2.dynamic > 0 ? c2.dynamicDeps : c2.deps;
26175
26453
  const l = list[list.length - 2];
26176
26454
  const r = list[list.length - 1];
26177
- if (l && r && l.sheet === r.sheet) push(c2, spans([l, r])[0]);
26455
+ if (l && r && l.sheet === r.sheet) push2(c2, spans([l, r])[0]);
26178
26456
  continue;
26179
26457
  }
26180
26458
  const mentioned = [...c2.deps.slice(staticStart), ...c2.dynamicDeps.slice(dynamicStart)];
@@ -26190,7 +26468,7 @@ function addStructured(ref, c2) {
26190
26468
  sheet: c2.sheet,
26191
26469
  row: c2.row,
26192
26470
  col: c2.col});
26193
- if (value instanceof RefValue) for (const area of value.areas) push(c2, area);
26471
+ if (value instanceof RefValue) for (const area of value.areas) push2(c2, area);
26194
26472
  }
26195
26473
  function analyze(ast, host, sheet, row2, col) {
26196
26474
  const c2 = {
@@ -26214,155 +26492,191 @@ function analyze(ast, host, sheet, row2, col) {
26214
26492
  };
26215
26493
  }
26216
26494
 
26217
- // xlsx/formula/area-index.ts
26218
- var BUCKET_WIDTH = 64;
26219
- var CHUNK_SHIFT = 6;
26220
- var CHUNKS = 64;
26221
- function push2(map, key2, value) {
26222
- const list = map.get(key2);
26223
- if (list) list.push(value);
26224
- else map.set(key2, [value]);
26495
+ // xlsx/formula/formula-shape.ts
26496
+ var isDigit4 = (code) => code >= 48 && code <= 57;
26497
+ function formulaShape(formula) {
26498
+ const starts = [];
26499
+ const ends = [];
26500
+ let hash = 2166136261;
26501
+ for (let i2 = 0; i2 < formula.length; i2++) {
26502
+ let code = formula.charCodeAt(i2);
26503
+ if (isDigit4(code)) {
26504
+ let j = i2 + 1;
26505
+ while (j < formula.length && isDigit4(formula.charCodeAt(j))) j++;
26506
+ starts.push(i2);
26507
+ ends.push(j);
26508
+ i2 = j - 1;
26509
+ code = 0;
26510
+ }
26511
+ hash = Math.imul(hash ^ code, 16777619);
26512
+ }
26513
+ return { hash: hash >>> 0, starts, ends };
26225
26514
  }
26226
- function inner(map, sheet) {
26227
- let found = map.get(sheet);
26228
- if (!found) map.set(sheet, found = /* @__PURE__ */ new Map());
26229
- return found;
26515
+ function sameText(a2, from, b, at2, length) {
26516
+ for (let k = 0; k < length; k++)
26517
+ if (a2.charCodeAt(from + k) !== b.charCodeAt(at2 + k)) return false;
26518
+ return true;
26230
26519
  }
26231
- var AreaIndex = class {
26232
- cells = /* @__PURE__ */ new Map();
26233
- chunked = /* @__PURE__ */ new Map();
26234
- tall = /* @__PURE__ */ new Map();
26235
- wide = /* @__PURE__ */ new Map();
26236
- add(area, node) {
26237
- const { start, end } = area.range;
26238
- if (start.row === end.row && end.col - start.col + 1 <= BUCKET_WIDTH) {
26239
- const cells3 = inner(this.cells, area.sheet);
26240
- for (let col = start.col; col <= end.col; col++) push2(cells3, cellKey(start.row, col), node);
26241
- return;
26520
+ function sameShape(a2, as, b, bs) {
26521
+ const runs = as.starts.length;
26522
+ if (runs !== bs.starts.length) return false;
26523
+ let fromA = 0;
26524
+ let fromB = 0;
26525
+ for (let i2 = 0; i2 <= runs; i2++) {
26526
+ const toA = i2 < runs ? as.starts[i2] : a2.length;
26527
+ const toB = i2 < runs ? bs.starts[i2] : b.length;
26528
+ if (toA - fromA !== toB - fromB || !sameText(a2, fromA, b, fromB, toA - fromA)) return false;
26529
+ fromA = as.ends[i2] ?? 0;
26530
+ fromB = bs.ends[i2] ?? 0;
26531
+ }
26532
+ return true;
26533
+ }
26534
+ function makeTemplate(formula, shape2) {
26535
+ let tokens3;
26536
+ let ast;
26537
+ try {
26538
+ tokens3 = tokenize(formula);
26539
+ ast = parseTokens(tokens3);
26540
+ } catch (e) {
26541
+ if (e instanceof RangeError) return new FormulaError(e.message, -1, "#VALUE!");
26542
+ if (e instanceof FormulaError) return e;
26543
+ throw e;
26544
+ }
26545
+ const { starts } = shape2;
26546
+ const slotted = starts.map(() => false);
26547
+ const refs = /* @__PURE__ */ new Map();
26548
+ let run2 = 0;
26549
+ for (const token of tokens3) {
26550
+ const end = token.start + token.text.length;
26551
+ const first3 = run2;
26552
+ while (run2 < starts.length && starts[run2] < end) run2++;
26553
+ const ref = token.kind === "ref" ? token.ref : void 0;
26554
+ if (!ref || ref.kind === "cols") continue;
26555
+ const count2 = ref.kind === "cell" ? 1 : 2;
26556
+ if (run2 - first3 < count2) continue;
26557
+ const at2 = run2 - count2;
26558
+ const indexes2 = count2 === 1 ? [at2] : [at2, at2 + 1];
26559
+ if (indexes2.some((i2) => formula.charCodeAt(starts[i2]) === 48)) continue;
26560
+ for (const i2 of indexes2) slotted[i2] = true;
26561
+ refs.set(ref, count2 === 1 ? { start: at2 } : { start: at2, end: at2 + 1 });
26562
+ }
26563
+ return { formula, shape: shape2, ast, slotted, refs };
26564
+ }
26565
+ function rowsFor(t, formula, shape2) {
26566
+ if (!sameShape(t.formula, t.shape, formula, shape2)) return void 0;
26567
+ const rows = [];
26568
+ for (let i2 = 0; i2 < shape2.starts.length; i2++) {
26569
+ const start = shape2.starts[i2];
26570
+ const length = shape2.ends[i2] - start;
26571
+ if (!t.slotted[i2]) {
26572
+ const own = t.shape.starts[i2];
26573
+ if (t.shape.ends[i2] - own !== length) return void 0;
26574
+ if (!sameText(t.formula, own, formula, start, length)) return void 0;
26575
+ rows.push(-1);
26576
+ continue;
26242
26577
  }
26243
- const entry3 = { range: area.range, node };
26244
- if (end.col - start.col + 1 > BUCKET_WIDTH) {
26245
- const list = this.wide.get(area.sheet);
26246
- if (list) list.push(entry3);
26247
- else this.wide.set(area.sheet, [entry3]);
26248
- return;
26578
+ if (length > 7 || formula.charCodeAt(start) === 48) return void 0;
26579
+ let row2 = 0;
26580
+ for (let k = start; k < start + length; k++) row2 = row2 * 10 + formula.charCodeAt(k) - 48;
26581
+ if (row2 < 1 || row2 > MAX_ROW + 1) return void 0;
26582
+ rows.push(row2 - 1);
26583
+ }
26584
+ return rows;
26585
+ }
26586
+ function shiftSpec(spec2, slots, rows) {
26587
+ const startRow = rows[slots.start];
26588
+ const endRow = slots.end === void 0 ? startRow : rows[slots.end];
26589
+ if (startRow === spec2.start.row && endRow === spec2.end.row) return spec2;
26590
+ const { start: from, end: to } = spec2;
26591
+ const start = { row: startRow, col: from.col, rowAbs: from.rowAbs, colAbs: from.colAbs };
26592
+ if (slots.end === void 0) return { kind: spec2.kind, start, end: start };
26593
+ const end = { row: endRow, col: to.col, rowAbs: to.rowAbs, colAbs: to.colAbs };
26594
+ return { kind: spec2.kind, start, end };
26595
+ }
26596
+ function copyTree(node, t, rows) {
26597
+ switch (node.type) {
26598
+ case "ref": {
26599
+ const slots = node.ref ? t.refs.get(node.ref) : void 0;
26600
+ if (!slots || !node.ref) return node;
26601
+ const ref = shiftSpec(node.ref, slots, rows);
26602
+ if (ref === node.ref) return node;
26603
+ const copy2 = { type: "ref" };
26604
+ if (node.prefix) copy2.prefix = node.prefix;
26605
+ copy2.ref = ref;
26606
+ if (node.spill) copy2.spill = true;
26607
+ return copy2;
26249
26608
  }
26250
- const first3 = start.row >> CHUNK_SHIFT;
26251
- const last = end.row >> CHUNK_SHIFT;
26252
- if (last - first3 + 1 > CHUNKS) {
26253
- const cols2 = inner(this.tall, area.sheet);
26254
- for (let c2 = start.col; c2 <= end.col; c2++) push2(cols2, c2, entry3);
26255
- return;
26609
+ case "unary":
26610
+ case "percent": {
26611
+ const operand4 = copyTree(node.operand, t, rows);
26612
+ if (operand4 === node.operand) return node;
26613
+ return node.type === "unary" ? { type: "unary", op: node.op, operand: operand4 } : { type: "percent", operand: operand4 };
26256
26614
  }
26257
- const cols = inner(this.chunked, area.sheet);
26258
- for (let c2 = start.col; c2 <= end.col; c2++) {
26259
- let chunks = cols.get(c2);
26260
- if (!chunks) cols.set(c2, chunks = /* @__PURE__ */ new Map());
26261
- for (let k = first3; k <= last; k++) push2(chunks, k, entry3);
26615
+ case "binary": {
26616
+ const left2 = copyTree(node.left, t, rows);
26617
+ const right2 = copyTree(node.right, t, rows);
26618
+ if (left2 === node.left && right2 === node.right) return node;
26619
+ return { type: "binary", op: node.op, left: left2, right: right2 };
26262
26620
  }
26263
- }
26264
- /** Items whose areas intersect `range` on `sheet` (each once). */
26265
- dependents(sheet, range, out) {
26266
- const found = out instanceof Set ? out : /* @__PURE__ */ new Set();
26267
- this.collect(sheet, range, found);
26268
- if (Array.isArray(out)) for (const node of found) out.push(node);
26269
- }
26270
- collect(sheet, range, out) {
26271
- const { start, end } = range;
26272
- const check3 = (list) => {
26273
- if (list) {
26274
- for (const entry3 of list) if (rangesIntersect(entry3.range, range)) out.add(entry3.node);
26275
- }
26276
- };
26277
- const cells3 = this.cells.get(sheet);
26278
- if (cells3) {
26279
- const size = (end.row - start.row + 1) * (end.col - start.col + 1);
26280
- if (size <= 4096) {
26281
- for (let r = start.row; r <= end.row; r++) {
26282
- for (let c2 = start.col; c2 <= end.col; c2++) {
26283
- const list = cells3.get(cellKey(r, c2));
26284
- if (list) for (const node of list) out.add(node);
26285
- }
26286
- }
26287
- } else {
26288
- for (const [key2, list] of cells3) {
26289
- const { row: row2, col } = keyToAddress(key2);
26290
- if (row2 >= start.row && row2 <= end.row && col >= start.col && col <= end.col)
26291
- for (const node of list) out.add(node);
26292
- }
26293
- }
26621
+ case "call": {
26622
+ const args = copyAll(node.args, t, rows);
26623
+ if (args === node.args) return node;
26624
+ return { type: "call", name: node.name, rawName: node.rawName, args };
26294
26625
  }
26295
- const columns = (map, visit2) => {
26296
- if (!map) return;
26297
- if (end.col - start.col + 1 <= map.size) {
26298
- for (let c2 = start.col; c2 <= end.col; c2++) if (map.has(c2)) visit2(c2);
26299
- } else for (const c2 of map.keys()) if (c2 >= start.col && c2 <= end.col) visit2(c2);
26300
- };
26301
- const chunked = this.chunked.get(sheet);
26302
- const first3 = start.row >> CHUNK_SHIFT;
26303
- const last = end.row >> CHUNK_SHIFT;
26304
- columns(chunked, (c2) => {
26305
- const chunks = chunked?.get(c2);
26306
- if (last - first3 + 1 <= chunks.size) {
26307
- for (let k = first3; k <= last; k++) check3(chunks.get(k));
26308
- } else for (const [k, list] of chunks) if (k >= first3 && k <= last) check3(list);
26309
- });
26310
- const tall = this.tall.get(sheet);
26311
- columns(tall, (c2) => check3(tall?.get(c2)));
26312
- check3(this.wide.get(sheet));
26313
- }
26314
- };
26315
-
26316
- // xlsx/formula/graph-index.ts
26317
- var ColumnIndex = class {
26318
- sheets = /* @__PURE__ */ new Map();
26319
- constructor(nodes) {
26320
- for (const node of nodes) {
26321
- let cols = this.sheets.get(node.sheet);
26322
- if (!cols) this.sheets.set(node.sheet, cols = /* @__PURE__ */ new Map());
26323
- let list = cols.get(node.col);
26324
- if (!list) cols.set(node.col, list = []);
26325
- list.push(node);
26626
+ case "invoke": {
26627
+ const callee = copyTree(node.callee, t, rows);
26628
+ const args = copyAll(node.args, t, rows);
26629
+ if (callee === node.callee && args === node.args) return node;
26630
+ return { type: "invoke", callee, args };
26326
26631
  }
26327
- for (const cols of this.sheets.values())
26328
- for (const list of cols.values()) list.sort((a2, b) => a2.row - b.row);
26632
+ default:
26633
+ return node;
26329
26634
  }
26330
- nodesIn(area, out) {
26331
- const cols = this.sheets.get(area.sheet);
26332
- if (!cols) return;
26333
- const { start, end } = area.range;
26334
- const width = end.col - start.col + 1;
26335
- const scan3 = (list) => {
26336
- let lo = 0;
26337
- let hi = list.length;
26338
- while (lo < hi) {
26339
- const mid = lo + hi >> 1;
26340
- if (list[mid].row < start.row) lo = mid + 1;
26341
- else hi = mid;
26342
- }
26343
- for (let i2 = lo; i2 < list.length; i2++) {
26344
- const node = list[i2];
26345
- if (node.row > end.row) break;
26346
- out.push(node);
26347
- }
26348
- };
26349
- if (width <= cols.size) {
26350
- for (let c2 = start.col; c2 <= end.col; c2++) {
26351
- const list = cols.get(c2);
26352
- if (list) scan3(list);
26353
- }
26354
- } else {
26355
- for (const [c2, list] of cols) if (c2 >= start.col && c2 <= end.col) scan3(list);
26356
- }
26635
+ }
26636
+ function copyAll(nodes, t, rows) {
26637
+ let out;
26638
+ for (let i2 = 0; i2 < nodes.length; i2++) {
26639
+ const node = nodes[i2];
26640
+ const copy2 = copyTree(node, t, rows);
26641
+ if (copy2 !== node && !out) out = nodes.slice(0, i2);
26642
+ out?.push(copy2);
26643
+ }
26644
+ return out ?? nodes;
26645
+ }
26646
+ function fromTemplate(t, formula, shape2) {
26647
+ const rows = rowsFor(t, formula, shape2);
26648
+ return rows ? copyTree(t.ast, t, rows) : void 0;
26649
+ }
26650
+
26651
+ // xlsx/formula/parse-cache.ts
26652
+ var MAX_TEMPLATE_LENGTH = 2048;
26653
+ var MAX_ENTRIES = 1e5;
26654
+ var MAX_TEMPLATES2 = 1e4;
26655
+ var NO_RUNS = { hash: 0, starts: [], ends: [] };
26656
+ var treeOf = (made) => "ast" in made ? made.ast : made;
26657
+ var ParseCache = class {
26658
+ byText = /* @__PURE__ */ new Map();
26659
+ templates = /* @__PURE__ */ new Map();
26660
+ parse(formula) {
26661
+ let ast = this.byText.get(formula);
26662
+ if (ast) return ast;
26663
+ ast = this.parseNew(formula);
26664
+ if (this.byText.size > MAX_ENTRIES) this.byText.clear();
26665
+ this.byText.set(formula, ast);
26666
+ return ast;
26357
26667
  }
26358
- };
26359
- var ReverseIndex = class extends AreaIndex {
26360
- constructor(nodes) {
26361
- super();
26362
- for (const node of nodes) {
26363
- for (const dep of node.deps) this.add(dep, node);
26364
- for (const dep of node.dynamicDeps) this.add(dep, node);
26668
+ parseNew(formula) {
26669
+ if (formula.length > MAX_TEMPLATE_LENGTH) return treeOf(makeTemplate(formula, NO_RUNS));
26670
+ const shape2 = formulaShape(formula);
26671
+ const template = this.templates.get(shape2.hash);
26672
+ const reused = template && fromTemplate(template, formula, shape2);
26673
+ if (reused) return reused;
26674
+ const made = makeTemplate(formula, shape2);
26675
+ if (!template && "ast" in made) {
26676
+ if (this.templates.size > MAX_TEMPLATES2) this.templates.clear();
26677
+ this.templates.set(shape2.hash, made);
26365
26678
  }
26679
+ return treeOf(made);
26366
26680
  }
26367
26681
  };
26368
26682
 
@@ -26502,7 +26816,7 @@ var EngineHost = class {
26502
26816
  options;
26503
26817
  nodes = /* @__PURE__ */ new Map();
26504
26818
  footprints = /* @__PURE__ */ new Set();
26505
- astCache = /* @__PURE__ */ new Map();
26819
+ parsed = new ParseCache();
26506
26820
  boundsCache = /* @__PURE__ */ new Map();
26507
26821
  inProgress = /* @__PURE__ */ new Set();
26508
26822
  cycles = /* @__PURE__ */ new Set();
@@ -26526,6 +26840,8 @@ var EngineHost = class {
26526
26840
  evaluations = 0;
26527
26841
  built = false;
26528
26842
  needsFull = false;
26843
+ /** No recalculation has run since the workbook was opened: stored values may stand. */
26844
+ fresh = true;
26529
26845
  // ---- EvalHost ----
26530
26846
  readCell(sheet, row2, col) {
26531
26847
  const cell = this.workbook.sheets[sheet]?.rows.get(row2)?.get(col);
@@ -26614,19 +26930,7 @@ var EngineHost = class {
26614
26930
  return this.workbook.sheets[sheet]?.rows.get(row2)?.get(col)?.formula;
26615
26931
  }
26616
26932
  parse(formula) {
26617
- let ast = this.astCache.get(formula);
26618
- if (!ast) {
26619
- try {
26620
- ast = parseFormula(formula);
26621
- } catch (e) {
26622
- if (e instanceof RangeError) ast = new FormulaError(e.message, -1, "#VALUE!");
26623
- else if (!(e instanceof FormulaError)) throw e;
26624
- else ast = e;
26625
- }
26626
- if (this.astCache.size > 1e5) this.astCache.clear();
26627
- this.astCache.set(formula, ast);
26628
- }
26629
- return ast;
26933
+ return this.parsed.parse(formula);
26630
26934
  }
26631
26935
  now() {
26632
26936
  return this.options.now ? this.options.now() : /* @__PURE__ */ new Date();
@@ -26635,20 +26939,6 @@ var EngineHost = class {
26635
26939
  return this.options.random ? this.options.random() : Math.random();
26636
26940
  }
26637
26941
  // ---- graph ----
26638
- build() {
26639
- this.nodes.clear();
26640
- this.footprints.clear();
26641
- this.footprintIndex = void 0;
26642
- this.workbook.sheets.forEach((sheet, s) => {
26643
- for (const [row2, cells3] of sheet.rows) {
26644
- for (const [col, cell] of cells3) {
26645
- if (cell.formula) this.createNode(s, row2, col, cell, true);
26646
- }
26647
- }
26648
- });
26649
- this.built = true;
26650
- this.graphChanged();
26651
- }
26652
26942
  createNode(sheet, row2, col, cell, initial) {
26653
26943
  const formula = cell.formula ?? "";
26654
26944
  const parsed = this.parse(formula);
@@ -26677,6 +26967,14 @@ var EngineHost = class {
26677
26967
  map.set(cellKey(row2, col), node);
26678
26968
  return node;
26679
26969
  }
26970
+ /** Parses a node's text again after a structural edit rewrote it (see `FormulaNode.stale`). */
26971
+ refreshAst(node) {
26972
+ const parsed = this.parse(node.formula);
26973
+ delete node.stale;
26974
+ delete node.parseError;
26975
+ node.ast = parsed instanceof FormulaError ? void 0 : parsed;
26976
+ if (parsed instanceof FormulaError && parsed.code) node.parseError = err(parsed.code);
26977
+ }
26680
26978
  removeNode(node, changed) {
26681
26979
  const sheet = this.workbook.sheets[node.sheet];
26682
26980
  if (node.spill && sheet) {
@@ -27107,6 +27405,7 @@ var EngineCore = class extends EngineHost {
27107
27405
  return { host: this, sheet, row: row2, col, scope: void 0, depth: 0, root, legacy };
27108
27406
  }
27109
27407
  evaluateFormula(node) {
27408
+ if (node.stale) this.refreshAst(node);
27110
27409
  if (!node.ast) return node.parseError ?? ERR.NAME;
27111
27410
  const ast = node.ast;
27112
27411
  const frame = this.frameFor(node.sheet, node.row, node.col, ast, node.legacy);
@@ -27124,20 +27423,363 @@ var EngineCore = class extends EngineHost {
27124
27423
  }
27125
27424
  };
27126
27425
 
27426
+ // xlsx/formula/shift-graph.ts
27427
+ var POSITIONAL = /* @__PURE__ */ new Set(["ROW", "COLUMN"]);
27428
+ var DESCRIPTIVE = /* @__PURE__ */ new Set(["FORMULATEXT", "CELL", "SHEET"]);
27429
+ var traitsCache = /* @__PURE__ */ new WeakMap();
27430
+ function astTraits(ast) {
27431
+ const cached3 = traitsCache.get(ast);
27432
+ if (cached3) return cached3;
27433
+ const sheets = /* @__PURE__ */ new Set();
27434
+ const traits = { complex: false, opaque: false, positional: false, descriptive: false, sheets };
27435
+ const stack2 = [ast];
27436
+ while (stack2.length) {
27437
+ const node = stack2.pop();
27438
+ switch (node.type) {
27439
+ case "ref":
27440
+ if (node.prefix) {
27441
+ sheets.add(node.prefix.sheet.toLowerCase());
27442
+ if (node.prefix.sheet2 !== void 0) {
27443
+ sheets.add(node.prefix.sheet2.toLowerCase());
27444
+ traits.complex = true;
27445
+ }
27446
+ }
27447
+ break;
27448
+ case "name":
27449
+ if (node.prefix) sheets.add(node.prefix.sheet.toLowerCase());
27450
+ traits.complex = true;
27451
+ break;
27452
+ case "structured":
27453
+ traits.complex = true;
27454
+ break;
27455
+ case "unary":
27456
+ case "percent":
27457
+ stack2.push(node.operand);
27458
+ break;
27459
+ case "binary":
27460
+ stack2.push(node.left, node.right);
27461
+ break;
27462
+ case "call":
27463
+ if (POSITIONAL.has(node.name)) traits.positional = true;
27464
+ if (DESCRIPTIVE.has(node.name)) traits.descriptive = true;
27465
+ if (!getFunction(node.name)) traits.complex = traits.opaque = true;
27466
+ stack2.push(...node.args);
27467
+ break;
27468
+ case "invoke":
27469
+ stack2.push(node.callee, ...node.args);
27470
+ break;
27471
+ }
27472
+ }
27473
+ traitsCache.set(ast, traits);
27474
+ return traits;
27475
+ }
27476
+ function intervalDelta(a2, b, shift2) {
27477
+ const { at: at2, count: count2 } = shift2;
27478
+ if (count2 > 0) {
27479
+ if (b < at2) return 0;
27480
+ if (a2 < at2) return void 0;
27481
+ return b + count2 > (shift2.axis === "row" ? MAX_ROW : MAX_COL) ? void 0 : count2;
27482
+ }
27483
+ const last = at2 - count2 - 1;
27484
+ if (b < at2) return 0;
27485
+ if (a2 > last) return count2;
27486
+ return void 0;
27487
+ }
27488
+ function areaDelta(area, sheet, shift2) {
27489
+ if (area.sheet !== sheet) return 0;
27490
+ const { start, end } = area.range;
27491
+ return shift2.axis === "row" ? intervalDelta(start.row, end.row, shift2) : intervalDelta(start.col, end.col, shift2);
27492
+ }
27493
+ function movedRange(range, axis, delta) {
27494
+ if (delta === 0) return range;
27495
+ return axis === "row" ? {
27496
+ start: { row: range.start.row + delta, col: range.start.col },
27497
+ end: { row: range.end.row + delta, col: range.end.col }
27498
+ } : {
27499
+ start: { row: range.start.row, col: range.start.col + delta },
27500
+ end: { row: range.end.row, col: range.end.col + delta }
27501
+ };
27502
+ }
27503
+ function reachesEdit(areas, sheet, shift2) {
27504
+ for (const area of areas) {
27505
+ if (area.sheet !== sheet) continue;
27506
+ if ((shift2.axis === "row" ? area.range.end.row : area.range.end.col) >= shift2.at) return true;
27507
+ }
27508
+ return false;
27509
+ }
27510
+ function intersectionMoves(area, delta, pos, nodeDelta, axis) {
27511
+ if (delta === nodeDelta) return false;
27512
+ const { start, end } = area.range;
27513
+ const a2 = axis === "row" ? start.row : start.col;
27514
+ const b = axis === "row" ? end.row : end.col;
27515
+ if (a2 === b) return false;
27516
+ return pos >= a2 && pos <= b || pos + nodeDelta >= a2 + delta && pos + nodeDelta <= b + delta;
27517
+ }
27518
+
27519
+ // xlsx/formula/engine-structure.ts
27520
+ var sameAreas = (a2, b) => a2.length === b.length && a2.every((x, i2) => {
27521
+ const y = b[i2];
27522
+ return x.sheet === y.sheet && sameRange(x.range, y.range);
27523
+ });
27524
+ var EngineStructure = class extends EngineCore {
27525
+ /** Formulas a structural edit left to recalculate (with their dependents) on the next pass. */
27526
+ structuralSeeds = /* @__PURE__ */ new Set();
27527
+ /** Whether the graph describes the workbook as it was before the edit just reported. */
27528
+ graphReady() {
27529
+ return this.built && !this.needsFull;
27530
+ }
27531
+ /** Parses a node's current text again and recomputes its precedents. */
27532
+ reanalyze(node) {
27533
+ const parsed = this.parse(node.formula);
27534
+ delete node.stale;
27535
+ delete node.parseError;
27536
+ if (parsed instanceof FormulaError) {
27537
+ node.ast = void 0;
27538
+ if (parsed.code) node.parseError = err(parsed.code);
27539
+ node.deps = [];
27540
+ node.dynamicDeps = [];
27541
+ return;
27542
+ }
27543
+ node.ast = parsed;
27544
+ const found = analyze(parsed, this, node.sheet, node.row, node.col);
27545
+ node.deps = found.deps;
27546
+ node.dynamicDeps = found.dynamicDeps;
27547
+ node.volatile = found.volatile;
27548
+ node.unsupported = found.unsupported;
27549
+ }
27550
+ /**
27551
+ * Moves the graph for rows or columns inserted or deleted on `sheet`, after the workbook was
27552
+ * changed (cells moved, formulas rewritten). Spills on the sheet, array formulas the edit cuts
27553
+ * and anything unexpected fall back to a rebuild on the next recalculation.
27554
+ */
27555
+ shiftCells(sheet, shift2) {
27556
+ if (!this.graphReady() || !this.shiftable(sheet, shift2)) {
27557
+ this.invalidate();
27558
+ return;
27559
+ }
27560
+ const ws = this.workbook.sheets[sheet];
27561
+ const own = this.nodes.get(sheet);
27562
+ if (!ws) return;
27563
+ const moved = /* @__PURE__ */ new Map();
27564
+ if (own) {
27565
+ const next = /* @__PURE__ */ new Map();
27566
+ for (const node of own.values()) {
27567
+ const pos = shift2.axis === "row" ? node.row : node.col;
27568
+ const delta = intervalDelta(pos, pos, shift2);
27569
+ if (delta === void 0) {
27570
+ this.dropNode(node);
27571
+ continue;
27572
+ }
27573
+ if (delta !== 0) {
27574
+ moved.set(node, delta);
27575
+ if (shift2.axis === "row") node.row += delta;
27576
+ else node.col += delta;
27577
+ }
27578
+ next.set(cellKey(node.row, node.col), node);
27579
+ }
27580
+ this.nodes.set(sheet, next);
27581
+ }
27582
+ for (const map of this.nodes.values())
27583
+ for (const node of map.values()) {
27584
+ if (!this.shiftNode(node, sheet, shift2, moved.get(node) ?? 0)) {
27585
+ this.invalidate();
27586
+ return;
27587
+ }
27588
+ }
27589
+ this.boundsCache.clear();
27590
+ this.graphChanged();
27591
+ }
27592
+ /** No spill or stale spilled value on the sheet, and no array formula the edit cuts. */
27593
+ shiftable(sheet, shift2) {
27594
+ for (const node of this.footprints) {
27595
+ if (node.sheet !== sheet) continue;
27596
+ if (node.spill || node.blockedSpill) return false;
27597
+ if (node.arrayRange && areaDelta({ sheet, range: node.arrayRange }, sheet, shift2) === void 0)
27598
+ return false;
27599
+ }
27600
+ const ws = this.workbook.sheets[sheet];
27601
+ let spilled = false;
27602
+ ws?.rows.forEach((cells3) => {
27603
+ if (!spilled) cells3.forEach((cell) => spilled ||= isSpilledCell(cell));
27604
+ });
27605
+ return !spilled;
27606
+ }
27607
+ dropNode(node) {
27608
+ this.structuralSeeds.delete(node);
27609
+ this.cycles.delete(node);
27610
+ this.footprints.delete(node);
27611
+ }
27612
+ /** Updates one formula for the edit; false when the graph cannot follow it. */
27613
+ shiftNode(node, sheet, shift2, nodeDelta) {
27614
+ const cell = this.workbook.sheets[node.sheet]?.rows.get(node.row)?.get(node.col);
27615
+ if (cell?.formula === void 0) return false;
27616
+ if (node.arrayRange) {
27617
+ const delta = areaDelta({ sheet: node.sheet, range: node.arrayRange }, sheet, shift2);
27618
+ if (delta === void 0) return false;
27619
+ node.arrayRange = movedRange(node.arrayRange, shift2.axis, delta);
27620
+ if (!cell.arrayRange || !sameRange(node.arrayRange, normalizeRange(cell.arrayRange)))
27621
+ return false;
27622
+ }
27623
+ const changed = cell.formula !== node.formula;
27624
+ const traits = node.ast && astTraits(node.ast);
27625
+ const reached = reachesEdit(node.deps, sheet, shift2) || reachesEdit(node.dynamicDeps, sheet, shift2);
27626
+ if (!changed && !reached && nodeDelta === 0 && !traits?.descriptive && !traits?.opaque)
27627
+ return true;
27628
+ if (traits?.complex) {
27629
+ node.formula = cell.formula;
27630
+ this.reanalyze(node);
27631
+ this.structuralSeeds.add(node);
27632
+ return true;
27633
+ }
27634
+ const pos = shift2.axis === "row" ? node.row - nodeDelta : node.col - nodeDelta;
27635
+ const moves = { seed: !!traits?.descriptive, moved: false, cut: false };
27636
+ const deps = this.moveAreas(node.deps, sheet, shift2, pos, nodeDelta, moves);
27637
+ const dynamicDeps = this.moveAreas(node.dynamicDeps, sheet, shift2, pos, nodeDelta, moves);
27638
+ const { cut, moved: anyMoved } = moves;
27639
+ let seed = moves.seed;
27640
+ if (cut || changed !== anyMoved) {
27641
+ node.formula = cell.formula;
27642
+ this.reanalyze(node);
27643
+ this.structuralSeeds.add(node);
27644
+ return true;
27645
+ }
27646
+ if (traits?.positional && (anyMoved || nodeDelta !== 0)) seed = true;
27647
+ node.deps = deps;
27648
+ node.dynamicDeps = dynamicDeps;
27649
+ if (changed) {
27650
+ node.formula = cell.formula;
27651
+ node.stale = true;
27652
+ }
27653
+ if (seed) this.structuralSeeds.add(node);
27654
+ return true;
27655
+ }
27656
+ /**
27657
+ * Areas moved for the edit (the same list when none moves). Records in `moves` whether one
27658
+ * moved, one was cut (deleted, widened, pushed off the grid) and whether implicit intersection
27659
+ * with one can now pick another cell.
27660
+ */
27661
+ moveAreas(list, sheet, shift2, pos, nodeDelta, moves) {
27662
+ let out;
27663
+ for (let i2 = 0; i2 < list.length; i2++) {
27664
+ const area = list[i2];
27665
+ const delta = areaDelta(area, sheet, shift2);
27666
+ if (delta === void 0) {
27667
+ moves.cut = true;
27668
+ continue;
27669
+ }
27670
+ if (intersectionMoves(area, delta, pos, nodeDelta, shift2.axis)) moves.seed = true;
27671
+ if (delta === 0) continue;
27672
+ moves.moved = true;
27673
+ out ??= list.slice();
27674
+ out[i2] = { sheet: area.sheet, range: movedRange(area.range, shift2.axis, delta) };
27675
+ }
27676
+ return out ?? list;
27677
+ }
27678
+ /**
27679
+ * Follows a sheet rename (`from` to `to`, formulas already rewritten): precedents are kept by
27680
+ * sheet index, so only rewritten formulas need their text parsed again, and only formulas that
27681
+ * name the new sheet without having been rewritten (previously unresolved), describe sheets
27682
+ * (CELL, SHEET, FORMULATEXT) or read defined names whose areas changed are recalculated.
27683
+ */
27684
+ renameSheet(from, to) {
27685
+ if (!this.graphReady()) {
27686
+ this.invalidate();
27687
+ return;
27688
+ }
27689
+ const target = to.toLowerCase();
27690
+ let depsChanged = false;
27691
+ for (const map of this.nodes.values())
27692
+ for (const node of map.values()) {
27693
+ const cell = this.workbook.sheets[node.sheet]?.rows.get(node.row)?.get(node.col);
27694
+ if (cell?.formula === void 0) {
27695
+ this.invalidate();
27696
+ return;
27697
+ }
27698
+ const changed = cell.formula !== node.formula;
27699
+ const traits = node.ast && astTraits(node.ast);
27700
+ node.formula = cell.formula;
27701
+ if (traits?.complex || !changed && traits?.sheets.has(target)) {
27702
+ const deps = node.deps;
27703
+ const dynamicDeps = node.dynamicDeps;
27704
+ this.reanalyze(node);
27705
+ const same5 = sameAreas(deps, node.deps) && sameAreas(dynamicDeps, node.dynamicDeps);
27706
+ if (!same5) depsChanged = true;
27707
+ if (!same5 || !traits?.complex) this.structuralSeeds.add(node);
27708
+ } else if (changed) node.stale = true;
27709
+ if (traits?.descriptive || traits?.opaque) this.structuralSeeds.add(node);
27710
+ }
27711
+ if (depsChanged) this.graphChanged();
27712
+ }
27713
+ /** Takes the formulas queued by structural edits. */
27714
+ takeStructuralSeeds(into) {
27715
+ for (const node of this.structuralSeeds)
27716
+ if (this.nodes.get(node.sheet)?.get(cellKey(node.row, node.col)) === node) into.add(node);
27717
+ this.structuralSeeds.clear();
27718
+ }
27719
+ };
27720
+
27127
27721
  // xlsx/formula/engine.ts
27128
- var Engine = class extends EngineCore {
27722
+ var Engine = class extends EngineStructure {
27723
+ preparation;
27724
+ /** The prepared formulas the first recalculation checks (see `trustsStoredValues`). */
27725
+ prepared;
27726
+ // ---- the graph ----
27727
+ prepare(timeRemaining) {
27728
+ if (this.built || this.needsFull) return true;
27729
+ this.preparation ??= this.startPreparation();
27730
+ if (!this.preparation.run(timeRemaining)) return false;
27731
+ this.finishPreparation(this.preparation);
27732
+ return true;
27733
+ }
27734
+ discardPreparation() {
27735
+ if (!this.fresh || this.needsFull) return;
27736
+ this.preparation = void 0;
27737
+ this.prepared = void 0;
27738
+ this.structuralSeeds.clear();
27739
+ this.nodes.clear();
27740
+ this.footprints.clear();
27741
+ this.graphChanged();
27742
+ this.built = false;
27743
+ }
27744
+ /** Completes the graph, continuing a preparation that is under way. */
27745
+ build() {
27746
+ const preparation = this.preparation ?? this.startPreparation();
27747
+ preparation.run();
27748
+ this.finishPreparation(preparation);
27749
+ }
27750
+ startPreparation() {
27751
+ this.nodes.clear();
27752
+ this.footprints.clear();
27753
+ this.footprintIndex = void 0;
27754
+ this.prepared = void 0;
27755
+ return new GraphPreparation(
27756
+ this.workbook,
27757
+ (s, row2, col, cell) => this.createNode(s, row2, col, cell, true)
27758
+ );
27759
+ }
27760
+ finishPreparation(preparation) {
27761
+ this.preparation = void 0;
27762
+ this.built = true;
27763
+ this.graphChanged();
27764
+ this.reverseIndex = preparation.reverse;
27765
+ this.prepared = preparation.formulas;
27766
+ }
27129
27767
  // ---- recalculation ----
27130
27768
  recalculateAll() {
27769
+ this.fresh = false;
27770
+ this.prepared = void 0;
27131
27771
  if (this.needsFull) this.releaseAllSpills();
27132
27772
  if (!this.built || this.needsFull) this.build();
27133
27773
  this.needsFull = false;
27774
+ this.structuralSeeds.clear();
27134
27775
  this.cycles = /* @__PURE__ */ new Set();
27135
27776
  this.run(new Set(this.allNodes()), true);
27136
27777
  }
27137
27778
  recalculateFrom(changes) {
27138
27779
  const seeds = /* @__PURE__ */ new Set();
27139
- if (!this.built && !this.needsFull) {
27140
- this.build();
27780
+ if (this.fresh && !this.needsFull) {
27781
+ this.fresh = false;
27782
+ if (!this.built) this.build();
27141
27783
  if (!this.trustsStoredValues(changes, seeds)) {
27142
27784
  this.recalculateAll();
27143
27785
  return;
@@ -27181,6 +27823,7 @@ var Engine = class extends EngineCore {
27181
27823
  }
27182
27824
  }
27183
27825
  if (structural) this.graphChanged();
27826
+ this.takeStructuralSeeds(seeds);
27184
27827
  const reverse2 = this.reverse();
27185
27828
  for (const area of changed) reverse2.dependents(area.sheet, area.range, seeds);
27186
27829
  for (const node of this.allNodes()) if (node.volatile) seeds.add(node);
@@ -27192,13 +27835,18 @@ var Engine = class extends EngineCore {
27192
27835
  * without a value into `missing`.
27193
27836
  */
27194
27837
  trustsStoredValues(changes, missing) {
27838
+ const prepared = this.prepared;
27839
+ this.prepared = void 0;
27840
+ if (!prepared) return false;
27195
27841
  const changed = new Set(changes.map((c2) => `${c2.sheet}:${cellKey(c2.row, c2.col)}`));
27196
- for (const node of this.allNodes()) {
27197
- const legacyOrArray = node.legacy || node.arrayRange;
27198
- if (!legacyOrArray && !changed.has(`${node.sheet}:${cellKey(node.row, node.col)}`))
27842
+ const current = (node) => this.nodes.get(node.sheet)?.get(cellKey(node.row, node.col)) === node;
27843
+ for (const node of prepared.dynamic) {
27844
+ if (current(node) && !changed.has(`${node.sheet}:${cellKey(node.row, node.col)}`))
27199
27845
  return false;
27846
+ }
27847
+ for (const node of prepared.unvalued) {
27200
27848
  const cell = this.workbook.sheets[node.sheet]?.rows.get(node.row)?.get(node.col);
27201
- if (cell && cell.value === null && !node.keepCached) missing.add(node);
27849
+ if (current(node) && cell && cell.value === null) missing.add(node);
27202
27850
  }
27203
27851
  return true;
27204
27852
  }
@@ -27212,7 +27860,10 @@ var Engine = class extends EngineCore {
27212
27860
  return !!anchor?.spill && rangeHas(anchor.spill, row2, col);
27213
27861
  }
27214
27862
  invalidate() {
27863
+ this.structuralSeeds.clear();
27215
27864
  this.releaseAllSpills();
27865
+ this.preparation = void 0;
27866
+ this.prepared = void 0;
27216
27867
  this.nodes.clear();
27217
27868
  this.footprints.clear();
27218
27869
  this.graphChanged();
@@ -30143,13 +30794,13 @@ function linearTrend(values2) {
30143
30794
  const slope = den ? num3 / den : 0;
30144
30795
  return (k) => Number((meanY + slope * (k - meanX)).toPrecision(15));
30145
30796
  }
30146
- var isDigit4 = (ch) => ch !== void 0 && ch >= "0" && ch <= "9";
30797
+ var isDigit5 = (ch) => ch !== void 0 && ch >= "0" && ch <= "9";
30147
30798
  function matchTextNumber(text2) {
30148
30799
  let end = text2.length;
30149
- while (end > 0 && !isDigit4(text2[end - 1])) end--;
30800
+ while (end > 0 && !isDigit5(text2[end - 1])) end--;
30150
30801
  if (end === 0) return null;
30151
30802
  let start = end;
30152
- while (start > 0 && isDigit4(text2[start - 1])) start--;
30803
+ while (start > 0 && isDigit5(text2[start - 1])) start--;
30153
30804
  return [text2, text2.slice(0, start), text2.slice(start, end), text2.slice(end)];
30154
30805
  }
30155
30806
  var QUARTER = /^(q|qtr|quarter)(\s?)([1-4])$/i;
@@ -30332,10 +30983,10 @@ function removeValidationArea(rules, area) {
30332
30983
  }
30333
30984
 
30334
30985
  // xlsx/edit/conditional-formulas.ts
30335
- function rewriteConditionalRule(rule2, rewrite3) {
30986
+ function rewriteConditionalRule(rule2, rewrite2) {
30336
30987
  const safe2 = (formula) => {
30337
30988
  try {
30338
- return rewrite3(formula);
30989
+ return rewrite2(formula);
30339
30990
  } catch {
30340
30991
  return formula;
30341
30992
  }
@@ -30854,7 +31505,7 @@ function shiftPreservedXml(sheet, shift2, moveRange, banded) {
30854
31505
  (xml) => xml.includes("sqref")
30855
31506
  );
30856
31507
  }
30857
- function rewritePreservedFormulas(sheet, rewrite3) {
31508
+ function rewritePreservedFormulas(sheet, rewrite2) {
30858
31509
  editEntries(
30859
31510
  sheet,
30860
31511
  "extLst",
@@ -30864,7 +31515,7 @@ function rewritePreservedFormulas(sheet, rewrite3) {
30864
31515
  const text2 = f6.textContent ?? "";
30865
31516
  let next = text2;
30866
31517
  try {
30867
- next = rewrite3(text2, sheet.name);
31518
+ next = rewrite2(text2, sheet.name);
30868
31519
  } catch {
30869
31520
  next = text2;
30870
31521
  }
@@ -30879,46 +31530,46 @@ function rewritePreservedFormulas(sheet, rewrite3) {
30879
31530
  }
30880
31531
 
30881
31532
  // xlsx/edit/shift-formulas.ts
30882
- function safe(rewrite3, formula, formulaSheet) {
31533
+ function safe(rewrite2, formula, formulaSheet) {
30883
31534
  try {
30884
- return rewrite3(formula, formulaSheet);
31535
+ return rewrite2(formula, formulaSheet);
30885
31536
  } catch {
30886
31537
  return formula;
30887
31538
  }
30888
31539
  }
30889
- function rewriteFormulas(workbook, rewrite3) {
30890
- for (const sheet of workbook.sheets) rewriteSheetFormulas(sheet, rewrite3);
31540
+ function rewriteFormulas(workbook, rewrite2) {
31541
+ for (const sheet of workbook.sheets) rewriteSheetFormulas(sheet, rewrite2);
30891
31542
  for (const definedName of workbook.definedNames) {
30892
31543
  const local = definedName.localSheet === void 0 ? void 0 : workbook.sheets[definedName.localSheet];
30893
- definedName.formula = safe(rewrite3, definedName.formula, local?.name ?? "");
31544
+ definedName.formula = safe(rewrite2, definedName.formula, local?.name ?? "");
30894
31545
  }
30895
31546
  }
30896
- function rewriteSheetFormulas(sheet, rewrite3) {
30897
- rewritePreservedFormulas(sheet, rewrite3);
31547
+ function rewriteSheetFormulas(sheet, rewrite2) {
31548
+ rewritePreservedFormulas(sheet, rewrite2);
30898
31549
  {
30899
31550
  const name = sheet.name;
30900
31551
  for (const table2 of sheet.tables)
30901
31552
  for (const column of table2.columns)
30902
31553
  if (column.calculatedColumnFormula !== void 0)
30903
- column.calculatedColumnFormula = safe(rewrite3, column.calculatedColumnFormula, name);
31554
+ column.calculatedColumnFormula = safe(rewrite2, column.calculatedColumnFormula, name);
30904
31555
  for (const cells3 of sheet.rows.values())
30905
31556
  for (const cell of cells3.values())
30906
- if (cell.formula !== void 0) cell.formula = safe(rewrite3, cell.formula, name);
31557
+ if (cell.formula !== void 0) cell.formula = safe(rewrite2, cell.formula, name);
30907
31558
  for (const cf of sheet.conditionalFormats)
30908
- for (const rule2 of cf.rules) rewriteConditionalRule(rule2, (f6) => rewrite3(f6, name));
31559
+ for (const rule2 of cf.rules) rewriteConditionalRule(rule2, (f6) => rewrite2(f6, name));
30909
31560
  for (const dv of sheet.dataValidations) {
30910
- if (dv.formula1 !== void 0) dv.formula1 = safe(rewrite3, dv.formula1, name);
30911
- if (dv.formula2 !== void 0) dv.formula2 = safe(rewrite3, dv.formula2, name);
31561
+ if (dv.formula1 !== void 0) dv.formula1 = safe(rewrite2, dv.formula1, name);
31562
+ if (dv.formula2 !== void 0) dv.formula2 = safe(rewrite2, dv.formula2, name);
30912
31563
  }
30913
31564
  for (const link of sheet.hyperlinks)
30914
31565
  if (link.location && /![A-Za-z$]/.test(link.location))
30915
- link.location = safe(rewrite3, link.location, name);
31566
+ link.location = safe(rewrite2, link.location, name);
30916
31567
  for (const drawing of sheet.drawings) {
30917
31568
  if (drawing.kind !== "chart") continue;
30918
31569
  for (const series of drawing.series) {
30919
- if (series.nameRef) series.nameRef = safe(rewrite3, series.nameRef, name);
30920
- if (series.categoriesRef) series.categoriesRef = safe(rewrite3, series.categoriesRef, name);
30921
- if (series.valuesRef) series.valuesRef = safe(rewrite3, series.valuesRef, name);
31570
+ if (series.nameRef) series.nameRef = safe(rewrite2, series.nameRef, name);
31571
+ if (series.categoriesRef) series.categoriesRef = safe(rewrite2, series.categoriesRef, name);
31572
+ if (series.valuesRef) series.valuesRef = safe(rewrite2, series.valuesRef, name);
30922
31573
  }
30923
31574
  }
30924
31575
  }
@@ -31105,7 +31756,7 @@ function shiftAxis(ctx, s, axis, at2, count2, insert) {
31105
31756
  applyAxisShift(ctx, sheet, shift2);
31106
31757
  if (insert) inheritFormats(sheet, axis, at2, count2);
31107
31758
  },
31108
- { sheet: s, structural: true }
31759
+ { sheet: s, structural: true, calc: { kind: "shift", sheet: s, shift: shift2 } }
31109
31760
  );
31110
31761
  }
31111
31762
  var insertRows = (ctx, s, at2, count2) => shiftAxis(ctx, s, "row", at2, count2, true);
@@ -32919,6 +33570,14 @@ function setCalcMode(ctx, mode) {
32919
33570
 
32920
33571
  // xlsx/edit/session-calc.ts
32921
33572
  var isStructural = (step) => step.structural || step.entries.some((entry3) => entry3.before.kind !== "cells");
33573
+ function hintFor(step, mode) {
33574
+ const hint = step.calc;
33575
+ if (!hint) return void 0;
33576
+ if (hint.kind === "rename")
33577
+ return mode === "undo" ? { kind: "rename", from: hint.to, to: hint.from } : hint;
33578
+ return mode === "undo" ? void 0 : hint;
33579
+ }
33580
+ var settingsOf = (workbook) => `${workbook.iterate ? `${workbook.iterate.count}:${workbook.iterate.delta}` : ""}|${workbook.date1904}`;
32922
33581
  var SessionCalculator = class {
32923
33582
  constructor(workbook, calc, enabled2) {
32924
33583
  this.workbook = workbook;
@@ -32928,43 +33587,113 @@ var SessionCalculator = class {
32928
33587
  workbook;
32929
33588
  calc;
32930
33589
  enabled;
32931
- /** Structural edits made in manual mode leave the dependency graph stale until a full pass. */
33590
+ /** Structural edits the engine could not follow leave the dependency graph stale (manual mode). */
32932
33591
  staleGraph = false;
33592
+ /** Cells changed in manual mode since the last calculation. */
33593
+ dirty = [];
33594
+ /** The settings at the last calculation; undefined before the first. */
33595
+ calculated;
32933
33596
  /** Recalculates after an applied, undone or redone step (failures become warnings). */
32934
- afterStep(step) {
32935
- if (!this.enabled) return;
33597
+ afterStep(step, mode = "apply") {
33598
+ if (!this.enabled) {
33599
+ this.calc.discardPreparation();
33600
+ return;
33601
+ }
33602
+ const structural = isStructural(step);
33603
+ const hint = structural ? hintFor(step, mode) : void 0;
32936
33604
  if (calcModeOf(this.workbook) === "manual") {
32937
- if (isStructural(step)) this.staleGraph = true;
33605
+ if (hint && !this.staleGraph && !this.dirty.length) {
33606
+ this.guard(() => this.follow(hint));
33607
+ return;
33608
+ }
33609
+ this.calc.discardPreparation();
33610
+ if (structural) this.staleGraph = true;
33611
+ else this.dirty.push(...touchedCells(step));
32938
33612
  return;
32939
33613
  }
32940
- this.recalc(isStructural(step), () => touchedCells(step));
33614
+ if (hint && !this.staleGraph) {
33615
+ this.guard(() => {
33616
+ this.follow(hint);
33617
+ this.calc.recalculateFrom(touchedCells(step));
33618
+ this.calculated = settingsOf(this.workbook);
33619
+ });
33620
+ return;
33621
+ }
33622
+ this.recalc(structural, () => touchedCells(step));
32941
33623
  }
32942
33624
  /** Recalculates after a change applied outside the history (a collaborator's edit). */
32943
33625
  afterExternal(structural, cells3) {
32944
- if (!this.enabled) return;
32945
- if (calcModeOf(this.workbook) === "manual") {
33626
+ if (!this.enabled || calcModeOf(this.workbook) === "manual") {
33627
+ this.calc.discardPreparation();
33628
+ if (!this.enabled) return;
32946
33629
  if (structural) this.staleGraph = true;
33630
+ else this.dirty.push(...cells3);
32947
33631
  return;
32948
33632
  }
32949
33633
  this.recalc(structural, () => cells3);
32950
33634
  }
32951
- recalc(structural, cells3) {
33635
+ follow(hint) {
33636
+ if (hint.kind === "shift") this.calc.shiftCells(hint.sheet, hint.shift);
33637
+ else this.calc.renameSheet(hint.from, hint.to);
33638
+ }
33639
+ guard(run2) {
32952
33640
  try {
33641
+ run2();
33642
+ } catch (error) {
33643
+ const message = `Recalculation failed: ${error instanceof Error ? error.message : String(error)}`;
33644
+ if (!this.workbook.warnings.includes(message)) this.workbook.warnings.push(message);
33645
+ }
33646
+ }
33647
+ /** Builds the dependency graph ahead of the first edit (see `EditSession.prepareCalculation`). */
33648
+ prepare(timeRemaining) {
33649
+ if (!this.enabled || calcModeOf(this.workbook) === "manual" || this.staleGraph) return true;
33650
+ try {
33651
+ return this.calc.prepare(timeRemaining);
33652
+ } catch {
33653
+ this.calc.discardPreparation();
33654
+ return true;
33655
+ }
33656
+ }
33657
+ /**
33658
+ * Before a structural edit the graph can follow in place: finishes a graph being prepared (or
33659
+ * builds it) while it still matches the workbook, so the edit moves it instead of rebuilding.
33660
+ */
33661
+ beforeStructural() {
33662
+ if (!this.enabled || calcModeOf(this.workbook) === "manual" || this.staleGraph) return;
33663
+ this.prepare();
33664
+ }
33665
+ recalc(structural, cells3) {
33666
+ this.guard(() => {
32953
33667
  if (this.staleGraph || structural) this.full();
32954
33668
  else {
32955
33669
  const changed = cells3();
32956
33670
  if (changed.length) this.calc.recalculateFrom(changed);
33671
+ this.calculated = settingsOf(this.workbook);
32957
33672
  }
32958
- } catch (error) {
32959
- const message = `Recalculation failed: ${error instanceof Error ? error.message : String(error)}`;
32960
- if (!this.workbook.warnings.includes(message)) this.workbook.warnings.push(message);
32961
- }
33673
+ });
32962
33674
  }
32963
- /** Rebuilds the dependency graph and recalculates every formula (F9). */
33675
+ /** Rebuilds the dependency graph and recalculates every formula (Ctrl+Alt+F9). */
32964
33676
  full() {
32965
33677
  this.staleGraph = false;
33678
+ this.dirty = [];
32966
33679
  this.calc.invalidate();
32967
33680
  this.calc.recalculateAll();
33681
+ this.calculated = settingsOf(this.workbook);
33682
+ }
33683
+ /**
33684
+ * Calculate Now (F9): the cells changed since the last calculation, the formulas structural
33685
+ * edits queued, their dependents and the volatile functions. Everything when `full`, before
33686
+ * the session's first calculation, after the iteration settings changed or while the graph
33687
+ * is stale.
33688
+ */
33689
+ now(full) {
33690
+ if (full || this.staleGraph || this.calculated !== settingsOf(this.workbook)) {
33691
+ this.full();
33692
+ return;
33693
+ }
33694
+ const changed = this.dirty;
33695
+ this.dirty = [];
33696
+ this.calc.recalculateFrom(changed);
32968
33697
  }
32969
33698
  /**
32970
33699
  * Recalculates one sheet's formulas and their dependents (Shift+F9); after structural edits
@@ -36768,12 +37497,12 @@ function renameSheet(ctx, index2, name) {
36768
37497
  ctx.run(
36769
37498
  "Rename sheet",
36770
37499
  "sheets",
36771
- [{ kind: "workbook" }],
37500
+ [{ kind: "refs" }],
36772
37501
  () => {
36773
37502
  rewriteFormulas(workbook, (formula) => renameSheetInFormula(formula, old, name));
36774
37503
  sheet.name = name;
36775
37504
  },
36776
- { sheet: index2, structural: true }
37505
+ { sheet: index2, structural: true, calc: { kind: "rename", from: old, to: name } }
36777
37506
  );
36778
37507
  }
36779
37508
  function moveSheet(ctx, from, to) {
@@ -37149,13 +37878,12 @@ function createEditSession(workbook, options = {}) {
37149
37878
  for (const listener of [...listeners]) listener(change);
37150
37879
  };
37151
37880
  const calculator = new SessionCalculator(workbook, calc, autoRecalc);
37152
- const recalc = (step) => calculator.afterStep(step);
37153
- const begin = (label2, kind, info) => ({
37154
- step: { label: label2, entries: [], structural: info.structural ?? false },
37155
- kind,
37156
- sheet: info.sheet,
37157
- ranges: [...info.ranges ?? []]
37158
- });
37881
+ const recalc = (step, mode = "apply") => calculator.afterStep(step, mode);
37882
+ const begin = (label2, kind, info) => {
37883
+ const step = { label: label2, entries: [], structural: info.structural ?? false };
37884
+ if (info.calc) step.calc = info.calc;
37885
+ return { step, kind, sheet: info.sheet, ranges: [...info.ranges ?? []] };
37886
+ };
37159
37887
  const finish = (current) => {
37160
37888
  if (!current.step.entries.length) return;
37161
37889
  history.push(current.step);
@@ -37180,10 +37908,12 @@ function createEditSession(workbook, options = {}) {
37180
37908
  const outer = !open;
37181
37909
  const current = open ?? begin(label2, kind, info);
37182
37910
  if (!outer) {
37911
+ delete current.step.calc;
37183
37912
  if (info.structural) current.step.structural = true;
37184
37913
  if (current.sheet === void 0 && info.sheet !== void 0) current.sheet = info.sheet;
37185
37914
  current.ranges.push(...info.ranges ?? []);
37186
37915
  }
37916
+ if (outer && info.calc) calculator.beforeStructural();
37187
37917
  open = current;
37188
37918
  const mark = current.step.entries.length;
37189
37919
  const befores = scopes.map((scope) => captureScope(workbook, scope));
@@ -37231,7 +37961,7 @@ function createEditSession(workbook, options = {}) {
37231
37961
  if (open) throw new Error(`Cannot ${kind} inside a batch`);
37232
37962
  const step = kind === "undo" ? history.undo(workbook) : history.redo(workbook);
37233
37963
  if (!step) return false;
37234
- recalc(step);
37964
+ recalc(step, kind);
37235
37965
  emit({ kind, label: step.label, structural: step.structural });
37236
37966
  return true;
37237
37967
  };
@@ -37310,10 +38040,11 @@ function createEditSession(workbook, options = {}) {
37310
38040
  setDocumentProperties: (patch) => setDocumentProperties(ctx, patch),
37311
38041
  setAutoRecalc: (enabled2) => setCalcMode(ctx, enabled2 ? "auto" : "manual"),
37312
38042
  autoRecalc: () => autoRecalc && calcModeOf(workbook) === "auto",
37313
- calculateNow() {
37314
- calculator.full();
38043
+ calculateNow(options2) {
38044
+ calculator.now(options2?.full ?? false);
37315
38045
  emit({ kind: "cells", label: "Calculate now", structural: false });
37316
38046
  },
38047
+ prepareCalculation: (opts) => calculator.prepare(opts?.timeRemaining),
37317
38048
  calculateSheet(s) {
37318
38049
  calculator.sheet(s);
37319
38050
  emit({ kind: "cells", label: "Calculate sheet", sheet: s, structural: false });
@@ -45901,11 +46632,11 @@ async function assertVisioMasterIndependence(pkg, roots, commands, check3) {
45901
46632
  }
45902
46633
  const tops = children3(children3(root, "Shapes")[0], "Shape");
45903
46634
  if (tops.length !== 1) problem("Only a uniquely rooted master can prove independence.");
45904
- const templates = masterShapes(root), context = /* @__PURE__ */ new Map();
46635
+ const templates2 = masterShapes(root), context = /* @__PURE__ */ new Map();
45905
46636
  assertMasterMarkup(
45906
46637
  root,
45907
46638
  new Set(
45908
- templates.flatMap((shape2) => {
46639
+ templates2.flatMap((shape2) => {
45909
46640
  charge();
45910
46641
  return [...masterCells(shape2, charge).values()];
45911
46642
  })
@@ -45927,7 +46658,7 @@ async function assertVisioMasterIndependence(pkg, roots, commands, check3) {
45927
46658
  if (byTemplate.has(id)) problem("MasterShape mappings are ambiguous.");
45928
46659
  byTemplate.set(id, local);
45929
46660
  }
45930
- for (const template of templates) {
46661
+ for (const template of templates2) {
45931
46662
  charge();
45932
46663
  if (bindings.length >= 1e4) problem("Master effective binding limit exceeded.");
45933
46664
  check3();