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
@@ -8624,9 +8624,9 @@ function rejectUnsafeRunSegmentation(paragraph2, base, oldRuns) {
8624
8624
  (run2) => !run2.image && !run2.equation && (run2.restoredRunPropertiesXml || run2.sourceRunPropertiesXml)
8625
8625
  ))
8626
8626
  return;
8627
- const sameText = paragraph2.runs.map((run2) => run2.text).join("") === base.runs.map((run2) => run2.text).join("");
8627
+ const sameText2 = paragraph2.runs.map((run2) => run2.text).join("") === base.runs.map((run2) => run2.text).join("");
8628
8628
  const sameBoundaries = paragraph2.runs.length === base.runs.length && paragraph2.runs.every((run2, index2) => run2.text === base.runs[index2]?.text);
8629
- if (sameBoundaries || !sameText && paragraph2.runs.length === base.runs.length) return;
8629
+ if (sameBoundaries || !sameText2 && paragraph2.runs.length === base.runs.length) return;
8630
8630
  throw new Error(
8631
8631
  `Cannot edit paragraph ${paragraph2.id}: changing run boundaries could drop unsupported run properties. The original DOCX package remains unchanged.`
8632
8632
  );
@@ -12498,7 +12498,7 @@ var NAME_RE = /^[A-Za-z_\\\u00A1-\uFFFF][A-Za-z0-9_.?\\\u00A1-\uFFFF]*/;
12498
12498
  var CELL_RE = /^(\$?)([A-Za-z]{1,3})(\$?)(\d{1,7})/;
12499
12499
  var COLS_RE = /^(\$?)([A-Za-z]{1,3}):(\$?)([A-Za-z]{1,3})/;
12500
12500
  var ROWS_RE = /^(\$?)(\d{1,7}):(\$?)(\d{1,7})/;
12501
- var NUMBER_RE = /^(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/;
12501
+ var NUMBER_RE = /^(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?/;
12502
12502
  var sticky = (re) => new RegExp(re.source.replace(/^\^/, ""), "y");
12503
12503
  var CELL_AT = sticky(CELL_RE);
12504
12504
  var COLS_AT = sticky(COLS_RE);
@@ -12845,7 +12845,7 @@ function readName(source2, at2, start, prefix) {
12845
12845
  }
12846
12846
  var joinTokens = (tokens3) => tokens3.map((t) => t.text).join("");
12847
12847
 
12848
- // xlsx/formula/transform.ts
12848
+ // xlsx/formula/ref-text.ts
12849
12849
  function safeTokens(formula) {
12850
12850
  try {
12851
12851
  return tokenize(formula);
@@ -12864,50 +12864,26 @@ function formatRefSpec(spec2) {
12864
12864
  if (spec2.kind === "cell") return a2;
12865
12865
  return `${a2}:${cornerText(spec2.end, spec2.kind)}`;
12866
12866
  }
12867
- function rewrite(token, ref) {
12867
+ function rewriteRef(token, ref) {
12868
12868
  const prefix = token.prefix?.text ?? "";
12869
12869
  if (!ref) return `${prefix}#REF!`;
12870
12870
  return `${prefix}${formatRefSpec(ref)}${token.spill ? "#" : ""}`;
12871
12871
  }
12872
12872
  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;
12873
- function translateFormula(formula, dRow, dCol) {
12874
- const tokens3 = safeTokens(formula);
12875
- if (!tokens3) return formula;
12876
- for (const token of tokens3) {
12877
- if (token.kind !== "ref" || !token.ref) continue;
12878
- const spec2 = token.ref;
12879
- const move = (c2) => {
12880
- const row2 = spec2.kind === "cols" || c2.rowAbs ? c2.row : c2.row + dRow;
12881
- const col = spec2.kind === "rows" || c2.colAbs ? c2.col : c2.col + dCol;
12882
- if (row2 < 0 || row2 > MAX_ROW || col < 0 || col > MAX_COL) return void 0;
12883
- return { ...c2, row: row2, col };
12884
- };
12885
- const start = move(spec2.start);
12886
- const end = move(spec2.end);
12887
- const next = start && end ? { kind: spec2.kind, start, end } : void 0;
12888
- if (next && sameSpec(next, spec2)) continue;
12889
- token.text = rewrite(token, next);
12890
- }
12891
- return joinTokens(tokens3);
12873
+ 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);
12874
+ function prefixText(prefix) {
12875
+ const sheets = prefix.sheet2 !== void 0 ? `${prefix.sheet}:${prefix.sheet2}` : prefix.sheet;
12876
+ const book = prefix.book !== void 0 ? `[${prefix.book}]` : "";
12877
+ const quote2 = needsQuote(prefix.sheet) || prefix.sheet2 !== void 0 && needsQuote(prefix.sheet2);
12878
+ return quote2 ? `'${(book + sheets).replace(/'/g, "''")}'!` : `${book}${sheets}!`;
12892
12879
  }
12893
- var sheetMatches = (prefix, formulaSheet, sheet) => {
12894
- if (prefix?.book !== void 0) return false;
12895
- const name = prefix ? prefix.sheet : formulaSheet;
12896
- return name.toLowerCase() === sheet.toLowerCase();
12897
- };
12898
- function shiftInterval(a2, b, at2, count2, max) {
12899
- if (count2 > 0) {
12900
- const na2 = a2 >= at2 ? a2 + count2 : a2;
12901
- const nb2 = b >= at2 ? b + count2 : b;
12902
- if (nb2 > max) return na2 > max ? void 0 : [na2, max];
12903
- return [na2, nb2];
12904
- }
12905
- const n = -count2;
12906
- const last = at2 + n - 1;
12907
- if (a2 >= at2 && b <= last) return void 0;
12908
- const na = a2 < at2 ? a2 : a2 <= last ? at2 : a2 - n;
12909
- const nb = b < at2 ? b : b <= last ? at2 - 1 : b - n;
12910
- return [na, nb];
12880
+
12881
+ // xlsx/formula/shift-formula.ts
12882
+ var templates = /* @__PURE__ */ new Map();
12883
+ var MAX_TEMPLATES = 2e5;
12884
+ function remember2(text2, template) {
12885
+ if (templates.size >= MAX_TEMPLATES) templates.clear();
12886
+ templates.set(text2, template);
12911
12887
  }
12912
12888
  function mergeSpacedAreas(tokens3) {
12913
12889
  const out = [];
@@ -12934,63 +12910,145 @@ function mergeSpacedAreas(tokens3) {
12934
12910
  }
12935
12911
  return out;
12936
12912
  }
12937
- function shiftFormula(formula, formulaSheet, spec2) {
12938
- if (spec2.count === 0) return formula;
12913
+ function templateOf(formula) {
12914
+ const cached3 = templates.get(formula);
12915
+ if (cached3 !== void 0) return cached3;
12939
12916
  const raw2 = safeTokens(formula);
12940
- if (!raw2) return formula;
12941
- const tokens3 = mergeSpacedAreas(raw2);
12942
- const isRow = spec2.axis === "row";
12943
- for (const token of tokens3) {
12944
- if (token.kind !== "ref" || !token.ref || !sheetMatches(token.prefix, formulaSheet, spec2.sheet))
12945
- continue;
12946
- const ref = token.ref;
12947
- if (isRow && ref.kind === "cols" || !isRow && ref.kind === "rows") continue;
12948
- const startFirst = isRow ? ref.start.row <= ref.end.row : ref.start.col <= ref.end.col;
12949
- const lo = startFirst ? ref.start : ref.end;
12950
- const hi = startFirst ? ref.end : ref.start;
12951
- const shifted2 = shiftInterval(
12952
- isRow ? lo.row : lo.col,
12953
- isRow ? hi.row : hi.col,
12954
- spec2.at,
12955
- spec2.count,
12956
- isRow ? MAX_ROW : MAX_COL
12957
- );
12958
- let next;
12959
- if (shifted2) {
12960
- const [a2, b] = shifted2;
12961
- const nlo = isRow ? { ...lo, row: a2 } : { ...lo, col: a2 };
12962
- const nhi = isRow ? { ...hi, row: b } : { ...hi, col: b };
12963
- next = { kind: ref.kind, start: startFirst ? nlo : nhi, end: startFirst ? nhi : nlo };
12964
- if (ref.kind === "cell") next.end = next.start;
12917
+ let template = null;
12918
+ if (raw2) {
12919
+ template = [];
12920
+ let literal2 = "";
12921
+ for (const token of mergeSpacedAreas(raw2)) {
12922
+ if (token.kind !== "ref" && !(token.kind === "name" && token.prefix)) {
12923
+ literal2 += token.text;
12924
+ continue;
12925
+ }
12926
+ if (literal2) template.push(literal2);
12927
+ literal2 = "";
12928
+ const part = { kind: token.kind, text: token.text };
12929
+ if (token.prefix) {
12930
+ part.prefix = token.prefix;
12931
+ if (token.prefix.book === void 0) part.sheet = token.prefix.sheet.toLowerCase();
12932
+ }
12933
+ if (token.ref) part.ref = token.ref;
12934
+ if (token.spill) part.spill = true;
12935
+ template.push(part);
12965
12936
  }
12966
- if (next && sameSpec(next, ref)) continue;
12967
- token.text = rewrite(token, next);
12937
+ if (literal2) template.push(literal2);
12968
12938
  }
12969
- return joinTokens(tokens3);
12939
+ remember2(formula, template);
12940
+ return template;
12970
12941
  }
12971
- 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);
12972
- function prefixText(prefix) {
12973
- const sheets = prefix.sheet2 !== void 0 ? `${prefix.sheet}:${prefix.sheet2}` : prefix.sheet;
12974
- const book = prefix.book !== void 0 ? `[${prefix.book}]` : "";
12975
- const quote2 = needsQuote(prefix.sheet) || prefix.sheet2 !== void 0 && needsQuote(prefix.sheet2);
12976
- return quote2 ? `'${(book + sheets).replace(/'/g, "''")}'!` : `${book}${sheets}!`;
12942
+ function rewriteTemplate(formula, rewrite2) {
12943
+ const template = templateOf(formula);
12944
+ if (!template) return formula;
12945
+ let next;
12946
+ for (let i2 = 0; i2 < template.length; i2++) {
12947
+ const part = template[i2];
12948
+ if (typeof part === "string") continue;
12949
+ const changed = rewrite2(part);
12950
+ if (!changed) continue;
12951
+ next ??= template.slice();
12952
+ next[i2] = changed;
12953
+ }
12954
+ if (!next) return formula;
12955
+ const text2 = next.map((part) => typeof part === "string" ? part : part.text).join("");
12956
+ remember2(text2, next);
12957
+ return text2;
12958
+ }
12959
+ function shiftInterval(a2, b, at2, count2, max) {
12960
+ if (count2 > 0) {
12961
+ const na2 = a2 >= at2 ? a2 + count2 : a2;
12962
+ const nb2 = b >= at2 ? b + count2 : b;
12963
+ if (nb2 > max) return na2 > max ? void 0 : [na2, max];
12964
+ return [na2, nb2];
12965
+ }
12966
+ const n = -count2;
12967
+ const last = at2 + n - 1;
12968
+ if (a2 >= at2 && b <= last) return void 0;
12969
+ const na = a2 < at2 ? a2 : a2 <= last ? at2 : a2 - n;
12970
+ const nb = b < at2 ? b : b <= last ? at2 - 1 : b - n;
12971
+ return [na, nb];
12972
+ }
12973
+ function shiftRef(ref, spec2) {
12974
+ const isRow = spec2.axis === "row";
12975
+ if (isRow && ref.kind === "cols" || !isRow && ref.kind === "rows") return ref;
12976
+ const startFirst = isRow ? ref.start.row <= ref.end.row : ref.start.col <= ref.end.col;
12977
+ const lo = startFirst ? ref.start : ref.end;
12978
+ const hi = startFirst ? ref.end : ref.start;
12979
+ const shifted2 = shiftInterval(
12980
+ isRow ? lo.row : lo.col,
12981
+ isRow ? hi.row : hi.col,
12982
+ spec2.at,
12983
+ spec2.count,
12984
+ isRow ? MAX_ROW : MAX_COL
12985
+ );
12986
+ if (!shifted2) return void 0;
12987
+ const [a2, b] = shifted2;
12988
+ const nlo = isRow ? { ...lo, row: a2 } : { ...lo, col: a2 };
12989
+ const nhi = isRow ? { ...hi, row: b } : { ...hi, col: b };
12990
+ const next = {
12991
+ kind: ref.kind,
12992
+ start: startFirst ? nlo : nhi,
12993
+ end: startFirst ? nhi : nlo
12994
+ };
12995
+ if (ref.kind === "cell") next.end = next.start;
12996
+ return sameSpec(next, ref) ? ref : next;
12997
+ }
12998
+ function shiftFormula(formula, formulaSheet, spec2) {
12999
+ if (spec2.count === 0) return formula;
13000
+ const sheet = spec2.sheet.toLowerCase();
13001
+ const own = formulaSheet.toLowerCase() === sheet;
13002
+ return rewriteTemplate(formula, (part) => {
13003
+ if (part.kind !== "ref" || !part.ref || (part.prefix ? part.sheet !== sheet : !own))
13004
+ return void 0;
13005
+ const next = shiftRef(part.ref, spec2);
13006
+ if (next === part.ref) return void 0;
13007
+ const out = { kind: "ref", text: rewriteRef(part, next) };
13008
+ if (part.prefix) out.prefix = part.prefix;
13009
+ if (part.sheet !== void 0) out.sheet = part.sheet;
13010
+ if (next) out.ref = next;
13011
+ if (part.spill) out.spill = true;
13012
+ return out;
13013
+ });
12977
13014
  }
12978
13015
  function renameSheetInFormula(formula, oldName, newName) {
12979
- const tokens3 = safeTokens(formula);
12980
- if (!tokens3) return formula;
12981
13016
  const lower2 = oldName.toLowerCase();
12982
- for (const token of tokens3) {
12983
- const prefix = token.prefix;
12984
- if (!prefix || prefix.book !== void 0) continue;
12985
- const first3 = prefix.sheet.toLowerCase() === lower2;
13017
+ return rewriteTemplate(formula, (part) => {
13018
+ const prefix = part.prefix;
13019
+ if (!prefix || part.sheet === void 0) return void 0;
13020
+ const first3 = part.sheet === lower2;
12986
13021
  const second = prefix.sheet2?.toLowerCase() === lower2;
12987
- if (!first3 && !second) continue;
13022
+ if (!first3 && !second) return void 0;
12988
13023
  const renamed = { ...prefix, sheet: first3 ? newName : prefix.sheet };
12989
13024
  if (prefix.sheet2 !== void 0) renamed.sheet2 = second ? newName : prefix.sheet2;
12990
13025
  const text2 = prefixText(renamed);
12991
- token.text = text2 + token.text.slice(prefix.text.length);
12992
13026
  renamed.text = text2;
12993
- token.prefix = renamed;
13027
+ const out = { ...part, text: text2 + part.text.slice(prefix.text.length) };
13028
+ out.prefix = renamed;
13029
+ out.sheet = renamed.sheet.toLowerCase();
13030
+ return out;
13031
+ });
13032
+ }
13033
+
13034
+ // xlsx/formula/transform.ts
13035
+ function translateFormula(formula, dRow, dCol) {
13036
+ const tokens3 = safeTokens(formula);
13037
+ if (!tokens3) return formula;
13038
+ for (const token of tokens3) {
13039
+ if (token.kind !== "ref" || !token.ref) continue;
13040
+ const spec2 = token.ref;
13041
+ const move = (c2) => {
13042
+ const row2 = spec2.kind === "cols" || c2.rowAbs ? c2.row : c2.row + dRow;
13043
+ const col = spec2.kind === "rows" || c2.colAbs ? c2.col : c2.col + dCol;
13044
+ if (row2 < 0 || row2 > MAX_ROW || col < 0 || col > MAX_COL) return void 0;
13045
+ return { ...c2, row: row2, col };
13046
+ };
13047
+ const start = move(spec2.start);
13048
+ const end = move(spec2.end);
13049
+ const next = start && end ? { kind: spec2.kind, start, end } : void 0;
13050
+ if (next && sameSpec(next, spec2)) continue;
13051
+ token.text = rewriteRef(token, next);
12994
13052
  }
12995
13053
  return joinTokens(tokens3);
12996
13054
  }
@@ -13079,7 +13137,7 @@ var FUTURE_FUNCTIONS = new Set(
13079
13137
  "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(" ")
13080
13138
  );
13081
13139
  var WORKSHEET_PREFIXED = /* @__PURE__ */ new Set(["FILTER", "SORT"]);
13082
- function rewrite2(formula, visit2) {
13140
+ function rewrite(formula, visit2) {
13083
13141
  const out = [];
13084
13142
  let i2 = 0;
13085
13143
  while (i2 < formula.length) {
@@ -13122,7 +13180,7 @@ function rewrite2(formula, visit2) {
13122
13180
  }
13123
13181
  function stripFuturePrefixes(formula) {
13124
13182
  if (!formula.includes("_xl")) return formula;
13125
- const plain2 = rewrite2(formula, (token, next, out) => {
13183
+ const plain2 = rewrite(formula, (token, next, out) => {
13126
13184
  if (!next.startsWith("(")) return false;
13127
13185
  const stripped = token.replace(/^(_xlfn\.)?(_xlws\.)?/i, "");
13128
13186
  if (stripped === token) return false;
@@ -13132,7 +13190,7 @@ function stripFuturePrefixes(formula) {
13132
13190
  return fromStoredSyntax(plain2);
13133
13191
  }
13134
13192
  function addFuturePrefixes(formula) {
13135
- return rewrite2(toStoredSyntax(formula), (token, next, out) => {
13193
+ return rewrite(toStoredSyntax(formula), (token, next, out) => {
13136
13194
  if (!next.startsWith("(")) return false;
13137
13195
  const upper2 = token.toUpperCase();
13138
13196
  if (!FUTURE_FUNCTIONS.has(upper2)) return false;
@@ -13785,13 +13843,13 @@ function dataBarLengths(rule2, node) {
13785
13843
  maxLength: rule2.maxLength ?? numAttr(bar, "maxLength") ?? 90
13786
13844
  };
13787
13845
  }
13788
- function rewriteConditionalExtension(rule2, rewrite3) {
13846
+ function rewriteConditionalExtension(rule2, rewrite2) {
13789
13847
  if (!rule2.extensionXml) return;
13790
13848
  const doc = parseXml(rule2.extensionXml);
13791
13849
  let changed = false;
13792
13850
  for (const formula of descendants3(doc.documentElement, "f", XM)) {
13793
13851
  const before = formula.textContent ?? "";
13794
- const next = rewrite3(before);
13852
+ const next = rewrite2(before);
13795
13853
  if (before !== next) {
13796
13854
  formula.textContent = next;
13797
13855
  changed = true;
@@ -17024,7 +17082,9 @@ var PREC_INTERSECT = 9;
17024
17082
  var PREC_RANGE = 10;
17025
17083
  var OPERAND_START = /* @__PURE__ */ new Set(["ref", "name", "func", "open", "structured"]);
17026
17084
  function parseFormula(formula) {
17027
- const all2 = tokenize(formula);
17085
+ return parseTokens(tokenize(formula));
17086
+ }
17087
+ function parseTokens(all2) {
17028
17088
  const tokens3 = [];
17029
17089
  const wsBefore = [];
17030
17090
  let sawWs = false;
@@ -17365,6 +17425,222 @@ function localDateToSerial(date, date1904 = date1904Mode) {
17365
17425
  return base + (utc - dayStart) / DAY_MS2;
17366
17426
  }
17367
17427
 
17428
+ // xlsx/formula/area-index.ts
17429
+ var BUCKET_WIDTH = 64;
17430
+ var CHUNK_SHIFT = 6;
17431
+ var CHUNKS = 64;
17432
+ function push(map, key2, value) {
17433
+ const list = map.get(key2);
17434
+ if (list) list.push(value);
17435
+ else map.set(key2, [value]);
17436
+ }
17437
+ function inner(map, sheet) {
17438
+ let found = map.get(sheet);
17439
+ if (!found) map.set(sheet, found = /* @__PURE__ */ new Map());
17440
+ return found;
17441
+ }
17442
+ var AreaIndex = class {
17443
+ cells = /* @__PURE__ */ new Map();
17444
+ chunked = /* @__PURE__ */ new Map();
17445
+ tall = /* @__PURE__ */ new Map();
17446
+ wide = /* @__PURE__ */ new Map();
17447
+ add(area, node) {
17448
+ const { start, end } = area.range;
17449
+ if (start.row === end.row && end.col - start.col + 1 <= BUCKET_WIDTH) {
17450
+ const cells3 = inner(this.cells, area.sheet);
17451
+ for (let col = start.col; col <= end.col; col++) push(cells3, cellKey(start.row, col), node);
17452
+ return;
17453
+ }
17454
+ const entry3 = { range: area.range, node };
17455
+ if (end.col - start.col + 1 > BUCKET_WIDTH) {
17456
+ const list = this.wide.get(area.sheet);
17457
+ if (list) list.push(entry3);
17458
+ else this.wide.set(area.sheet, [entry3]);
17459
+ return;
17460
+ }
17461
+ const first3 = start.row >> CHUNK_SHIFT;
17462
+ const last = end.row >> CHUNK_SHIFT;
17463
+ if (last - first3 + 1 > CHUNKS) {
17464
+ const cols2 = inner(this.tall, area.sheet);
17465
+ for (let c2 = start.col; c2 <= end.col; c2++) push(cols2, c2, entry3);
17466
+ return;
17467
+ }
17468
+ const cols = inner(this.chunked, area.sheet);
17469
+ for (let c2 = start.col; c2 <= end.col; c2++) {
17470
+ let chunks = cols.get(c2);
17471
+ if (!chunks) cols.set(c2, chunks = /* @__PURE__ */ new Map());
17472
+ for (let k = first3; k <= last; k++) push(chunks, k, entry3);
17473
+ }
17474
+ }
17475
+ /** Items whose areas intersect `range` on `sheet` (each once). */
17476
+ dependents(sheet, range, out) {
17477
+ const found = out instanceof Set ? out : /* @__PURE__ */ new Set();
17478
+ this.collect(sheet, range, found);
17479
+ if (Array.isArray(out)) for (const node of found) out.push(node);
17480
+ }
17481
+ collect(sheet, range, out) {
17482
+ const { start, end } = range;
17483
+ const check3 = (list) => {
17484
+ if (list) {
17485
+ for (const entry3 of list) if (rangesIntersect(entry3.range, range)) out.add(entry3.node);
17486
+ }
17487
+ };
17488
+ const cells3 = this.cells.get(sheet);
17489
+ if (cells3) {
17490
+ const size = (end.row - start.row + 1) * (end.col - start.col + 1);
17491
+ if (size <= 4096) {
17492
+ for (let r = start.row; r <= end.row; r++) {
17493
+ for (let c2 = start.col; c2 <= end.col; c2++) {
17494
+ const list = cells3.get(cellKey(r, c2));
17495
+ if (list) for (const node of list) out.add(node);
17496
+ }
17497
+ }
17498
+ } else {
17499
+ for (const [key2, list] of cells3) {
17500
+ const { row: row2, col } = keyToAddress(key2);
17501
+ if (row2 >= start.row && row2 <= end.row && col >= start.col && col <= end.col)
17502
+ for (const node of list) out.add(node);
17503
+ }
17504
+ }
17505
+ }
17506
+ const columns = (map, visit2) => {
17507
+ if (!map) return;
17508
+ if (end.col - start.col + 1 <= map.size) {
17509
+ for (let c2 = start.col; c2 <= end.col; c2++) if (map.has(c2)) visit2(c2);
17510
+ } else for (const c2 of map.keys()) if (c2 >= start.col && c2 <= end.col) visit2(c2);
17511
+ };
17512
+ const chunked = this.chunked.get(sheet);
17513
+ const first3 = start.row >> CHUNK_SHIFT;
17514
+ const last = end.row >> CHUNK_SHIFT;
17515
+ columns(chunked, (c2) => {
17516
+ const chunks = chunked?.get(c2);
17517
+ if (last - first3 + 1 <= chunks.size) {
17518
+ for (let k = first3; k <= last; k++) check3(chunks.get(k));
17519
+ } else for (const [k, list] of chunks) if (k >= first3 && k <= last) check3(list);
17520
+ });
17521
+ const tall = this.tall.get(sheet);
17522
+ columns(tall, (c2) => check3(tall?.get(c2)));
17523
+ check3(this.wide.get(sheet));
17524
+ }
17525
+ };
17526
+
17527
+ // xlsx/formula/graph-index.ts
17528
+ var ColumnIndex = class {
17529
+ sheets = /* @__PURE__ */ new Map();
17530
+ constructor(nodes) {
17531
+ for (const node of nodes) {
17532
+ let cols = this.sheets.get(node.sheet);
17533
+ if (!cols) this.sheets.set(node.sheet, cols = /* @__PURE__ */ new Map());
17534
+ let list = cols.get(node.col);
17535
+ if (!list) cols.set(node.col, list = []);
17536
+ list.push(node);
17537
+ }
17538
+ for (const cols of this.sheets.values())
17539
+ for (const list of cols.values()) list.sort((a2, b) => a2.row - b.row);
17540
+ }
17541
+ nodesIn(area, out) {
17542
+ const cols = this.sheets.get(area.sheet);
17543
+ if (!cols) return;
17544
+ const { start, end } = area.range;
17545
+ const width = end.col - start.col + 1;
17546
+ const scan3 = (list) => {
17547
+ let lo = 0;
17548
+ let hi = list.length;
17549
+ while (lo < hi) {
17550
+ const mid = lo + hi >> 1;
17551
+ if (list[mid].row < start.row) lo = mid + 1;
17552
+ else hi = mid;
17553
+ }
17554
+ for (let i2 = lo; i2 < list.length; i2++) {
17555
+ const node = list[i2];
17556
+ if (node.row > end.row) break;
17557
+ out.push(node);
17558
+ }
17559
+ };
17560
+ if (width <= cols.size) {
17561
+ for (let c2 = start.col; c2 <= end.col; c2++) {
17562
+ const list = cols.get(c2);
17563
+ if (list) scan3(list);
17564
+ }
17565
+ } else {
17566
+ for (const [c2, list] of cols) if (c2 >= start.col && c2 <= end.col) scan3(list);
17567
+ }
17568
+ }
17569
+ };
17570
+ var ReverseIndex = class extends AreaIndex {
17571
+ constructor(nodes = []) {
17572
+ super();
17573
+ for (const node of nodes) this.addNode(node);
17574
+ }
17575
+ /** Indexes one more formula's precedents. */
17576
+ addNode(node) {
17577
+ for (const dep of node.deps) this.add(dep, node);
17578
+ for (const dep of node.dynamicDeps) this.add(dep, node);
17579
+ }
17580
+ };
17581
+
17582
+ // xlsx/formula/engine-prepare.ts
17583
+ var CHECK_EVERY = 32;
17584
+ var GraphPreparation = class {
17585
+ constructor(workbook, create) {
17586
+ this.workbook = workbook;
17587
+ this.create = create;
17588
+ }
17589
+ workbook;
17590
+ create;
17591
+ reverse = new ReverseIndex();
17592
+ formulas = { dynamic: [], unvalued: [] };
17593
+ sheet = 0;
17594
+ rows;
17595
+ row = 0;
17596
+ cells;
17597
+ finished = false;
17598
+ /**
17599
+ * Adds formulas to the graph until `timeRemaining` (milliseconds, as an idle deadline reports
17600
+ * them) reaches zero, or until every formula is in. Returns true when the graph is complete.
17601
+ */
17602
+ run(timeRemaining) {
17603
+ let visited = 0;
17604
+ while (!this.finished) {
17605
+ const cells3 = this.cells ?? this.nextRow();
17606
+ if (!cells3) continue;
17607
+ const next = cells3.next();
17608
+ if (next.done) {
17609
+ this.cells = void 0;
17610
+ continue;
17611
+ }
17612
+ const [col, cell] = next.value;
17613
+ if (cell.formula) this.add(this.create(this.sheet, this.row, col, cell), cell);
17614
+ if (timeRemaining && ++visited % CHECK_EVERY === 0 && timeRemaining() <= 0) break;
17615
+ }
17616
+ return this.finished;
17617
+ }
17618
+ add(node, cell) {
17619
+ this.reverse.addNode(node);
17620
+ if (!node.legacy && !node.arrayRange) this.formulas.dynamic.push(node);
17621
+ if (cell.value === null && !node.keepCached) this.formulas.unvalued.push(node);
17622
+ }
17623
+ /** Moves to the next stored row (and sheet), or marks the walk finished. */
17624
+ nextRow() {
17625
+ for (; ; ) {
17626
+ const sheet = this.workbook.sheets[this.sheet];
17627
+ if (!sheet) {
17628
+ this.finished = true;
17629
+ return void 0;
17630
+ }
17631
+ this.rows ??= sheet.rows.entries();
17632
+ const next = this.rows.next();
17633
+ if (!next.done) {
17634
+ this.row = next.value[0];
17635
+ this.cells = next.value[1].entries();
17636
+ return this.cells;
17637
+ }
17638
+ this.rows = void 0;
17639
+ this.sheet++;
17640
+ }
17641
+ }
17642
+ };
17643
+
17368
17644
  // xlsx/formula/text-number.ts
17369
17645
  var MONTHS2 = [
17370
17646
  "jan",
@@ -20499,8 +20775,10 @@ var FINANCIAL_FUNCTIONS = [
20499
20775
 
20500
20776
  // xlsx/formula/references.ts
20501
20777
  function sheetIndex(host, name) {
20778
+ const sheets = host.workbook.sheets;
20779
+ for (let i2 = 0; i2 < sheets.length; i2++) if (sheets[i2]?.name === name) return i2;
20502
20780
  const lower2 = name.toLowerCase();
20503
- return host.workbook.sheets.findIndex((sheet) => sheet.name.toLowerCase() === lower2);
20781
+ return sheets.findIndex((sheet) => sheet.name.toLowerCase() === lower2);
20504
20782
  }
20505
20783
  function prefixSheets(host, prefix, current) {
20506
20784
  if (!prefix) return [current];
@@ -22114,9 +22392,9 @@ function readBlock(ctx, area, rows, cols) {
22114
22392
  if (host.readBlock) return host.readBlock(area.sheet, range);
22115
22393
  return Matrix.build(rows, cols, (r, c2) => ctx.readCell(area.sheet, row2 + r, col + c2));
22116
22394
  }
22117
- function shapedMatrices(ctx, values2, sameShape) {
22395
+ function shapedMatrices(ctx, values2, sameShape2) {
22118
22396
  let { rows, cols } = shapeOf(values2[0] ?? null);
22119
- if (sameShape) {
22397
+ if (sameShape2) {
22120
22398
  for (const value of values2) {
22121
22399
  const shape2 = shapeOf(value);
22122
22400
  if (shape2.rows !== rows || shape2.cols !== cols) fail(ERR.VALUE);
@@ -22142,12 +22420,12 @@ function shapedMatrices(ctx, values2, sameShape) {
22142
22420
  });
22143
22421
  return { matrices, total: total2, block: rows * cols };
22144
22422
  }
22145
- function matchingCells(ctx, pairs, target, sameShape) {
22423
+ function matchingCells(ctx, pairs, target, sameShape2) {
22146
22424
  const ranges = pairs.map((p) => p.range);
22147
22425
  const { matrices, total: total2, block } = shapedMatrices(
22148
22426
  ctx,
22149
22427
  target === void 0 ? ranges : [...ranges, target],
22150
- sameShape
22428
+ sameShape2
22151
22429
  );
22152
22430
  const tests = pairs.map((p) => makeCriteria(scalar(p.criteria)));
22153
22431
  const values2 = matrices[matrices.length - 1];
@@ -22186,7 +22464,7 @@ function indexedCandidates(pairs, matrices) {
22186
22464
  }
22187
22465
  return void 0;
22188
22466
  }
22189
- var matchingValues = (ctx, pairs, target, sameShape) => matchingCells(ctx, pairs, target, sameShape).values;
22467
+ var matchingValues = (ctx, pairs, target, sameShape2) => matchingCells(ctx, pairs, target, sameShape2).values;
22190
22468
  function criteriaPairs(args, from) {
22191
22469
  if ((args.length - from) % 2 !== 0) fail(ERR.VALUE);
22192
22470
  const pairs = [];
@@ -26080,7 +26358,7 @@ var REFERENCE_ARGUMENT = /* @__PURE__ */ new Set([
26080
26358
  "AREAS",
26081
26359
  "ISREF"
26082
26360
  ]);
26083
- var push = (c2, area) => {
26361
+ var push2 = (c2, area) => {
26084
26362
  (c2.dynamic > 0 ? c2.dynamicDeps : c2.deps).push(area);
26085
26363
  };
26086
26364
  function spans(areas) {
@@ -26106,7 +26384,7 @@ function visit(node, c2) {
26106
26384
  if (!sheets) return;
26107
26385
  const range = specRange(node.ref);
26108
26386
  const target = node.spill ? { start: range.start, end: { ...range.start } } : range;
26109
- for (const sheet of sheets) push(c2, { sheet, range: target });
26387
+ for (const sheet of sheets) push2(c2, { sheet, range: target });
26110
26388
  return;
26111
26389
  }
26112
26390
  case "name": {
@@ -26180,7 +26458,7 @@ function visitBinary(node, c2) {
26180
26458
  const list = c2.dynamic > 0 ? c2.dynamicDeps : c2.deps;
26181
26459
  const l = list[list.length - 2];
26182
26460
  const r = list[list.length - 1];
26183
- if (l && r && l.sheet === r.sheet) push(c2, spans([l, r])[0]);
26461
+ if (l && r && l.sheet === r.sheet) push2(c2, spans([l, r])[0]);
26184
26462
  continue;
26185
26463
  }
26186
26464
  const mentioned = [...c2.deps.slice(staticStart), ...c2.dynamicDeps.slice(dynamicStart)];
@@ -26196,7 +26474,7 @@ function addStructured(ref, c2) {
26196
26474
  sheet: c2.sheet,
26197
26475
  row: c2.row,
26198
26476
  col: c2.col});
26199
- if (value instanceof RefValue) for (const area of value.areas) push(c2, area);
26477
+ if (value instanceof RefValue) for (const area of value.areas) push2(c2, area);
26200
26478
  }
26201
26479
  function analyze(ast, host, sheet, row2, col) {
26202
26480
  const c2 = {
@@ -26220,155 +26498,191 @@ function analyze(ast, host, sheet, row2, col) {
26220
26498
  };
26221
26499
  }
26222
26500
 
26223
- // xlsx/formula/area-index.ts
26224
- var BUCKET_WIDTH = 64;
26225
- var CHUNK_SHIFT = 6;
26226
- var CHUNKS = 64;
26227
- function push2(map, key2, value) {
26228
- const list = map.get(key2);
26229
- if (list) list.push(value);
26230
- else map.set(key2, [value]);
26501
+ // xlsx/formula/formula-shape.ts
26502
+ var isDigit4 = (code) => code >= 48 && code <= 57;
26503
+ function formulaShape(formula) {
26504
+ const starts = [];
26505
+ const ends = [];
26506
+ let hash = 2166136261;
26507
+ for (let i2 = 0; i2 < formula.length; i2++) {
26508
+ let code = formula.charCodeAt(i2);
26509
+ if (isDigit4(code)) {
26510
+ let j = i2 + 1;
26511
+ while (j < formula.length && isDigit4(formula.charCodeAt(j))) j++;
26512
+ starts.push(i2);
26513
+ ends.push(j);
26514
+ i2 = j - 1;
26515
+ code = 0;
26516
+ }
26517
+ hash = Math.imul(hash ^ code, 16777619);
26518
+ }
26519
+ return { hash: hash >>> 0, starts, ends };
26231
26520
  }
26232
- function inner(map, sheet) {
26233
- let found = map.get(sheet);
26234
- if (!found) map.set(sheet, found = /* @__PURE__ */ new Map());
26235
- return found;
26521
+ function sameText(a2, from, b, at2, length) {
26522
+ for (let k = 0; k < length; k++)
26523
+ if (a2.charCodeAt(from + k) !== b.charCodeAt(at2 + k)) return false;
26524
+ return true;
26236
26525
  }
26237
- var AreaIndex = class {
26238
- cells = /* @__PURE__ */ new Map();
26239
- chunked = /* @__PURE__ */ new Map();
26240
- tall = /* @__PURE__ */ new Map();
26241
- wide = /* @__PURE__ */ new Map();
26242
- add(area, node) {
26243
- const { start, end } = area.range;
26244
- if (start.row === end.row && end.col - start.col + 1 <= BUCKET_WIDTH) {
26245
- const cells3 = inner(this.cells, area.sheet);
26246
- for (let col = start.col; col <= end.col; col++) push2(cells3, cellKey(start.row, col), node);
26247
- return;
26526
+ function sameShape(a2, as, b, bs) {
26527
+ const runs = as.starts.length;
26528
+ if (runs !== bs.starts.length) return false;
26529
+ let fromA = 0;
26530
+ let fromB = 0;
26531
+ for (let i2 = 0; i2 <= runs; i2++) {
26532
+ const toA = i2 < runs ? as.starts[i2] : a2.length;
26533
+ const toB = i2 < runs ? bs.starts[i2] : b.length;
26534
+ if (toA - fromA !== toB - fromB || !sameText(a2, fromA, b, fromB, toA - fromA)) return false;
26535
+ fromA = as.ends[i2] ?? 0;
26536
+ fromB = bs.ends[i2] ?? 0;
26537
+ }
26538
+ return true;
26539
+ }
26540
+ function makeTemplate(formula, shape2) {
26541
+ let tokens3;
26542
+ let ast;
26543
+ try {
26544
+ tokens3 = tokenize(formula);
26545
+ ast = parseTokens(tokens3);
26546
+ } catch (e) {
26547
+ if (e instanceof RangeError) return new FormulaError(e.message, -1, "#VALUE!");
26548
+ if (e instanceof FormulaError) return e;
26549
+ throw e;
26550
+ }
26551
+ const { starts } = shape2;
26552
+ const slotted = starts.map(() => false);
26553
+ const refs = /* @__PURE__ */ new Map();
26554
+ let run2 = 0;
26555
+ for (const token of tokens3) {
26556
+ const end = token.start + token.text.length;
26557
+ const first3 = run2;
26558
+ while (run2 < starts.length && starts[run2] < end) run2++;
26559
+ const ref = token.kind === "ref" ? token.ref : void 0;
26560
+ if (!ref || ref.kind === "cols") continue;
26561
+ const count2 = ref.kind === "cell" ? 1 : 2;
26562
+ if (run2 - first3 < count2) continue;
26563
+ const at2 = run2 - count2;
26564
+ const indexes2 = count2 === 1 ? [at2] : [at2, at2 + 1];
26565
+ if (indexes2.some((i2) => formula.charCodeAt(starts[i2]) === 48)) continue;
26566
+ for (const i2 of indexes2) slotted[i2] = true;
26567
+ refs.set(ref, count2 === 1 ? { start: at2 } : { start: at2, end: at2 + 1 });
26568
+ }
26569
+ return { formula, shape: shape2, ast, slotted, refs };
26570
+ }
26571
+ function rowsFor(t, formula, shape2) {
26572
+ if (!sameShape(t.formula, t.shape, formula, shape2)) return void 0;
26573
+ const rows = [];
26574
+ for (let i2 = 0; i2 < shape2.starts.length; i2++) {
26575
+ const start = shape2.starts[i2];
26576
+ const length = shape2.ends[i2] - start;
26577
+ if (!t.slotted[i2]) {
26578
+ const own = t.shape.starts[i2];
26579
+ if (t.shape.ends[i2] - own !== length) return void 0;
26580
+ if (!sameText(t.formula, own, formula, start, length)) return void 0;
26581
+ rows.push(-1);
26582
+ continue;
26248
26583
  }
26249
- const entry3 = { range: area.range, node };
26250
- if (end.col - start.col + 1 > BUCKET_WIDTH) {
26251
- const list = this.wide.get(area.sheet);
26252
- if (list) list.push(entry3);
26253
- else this.wide.set(area.sheet, [entry3]);
26254
- return;
26584
+ if (length > 7 || formula.charCodeAt(start) === 48) return void 0;
26585
+ let row2 = 0;
26586
+ for (let k = start; k < start + length; k++) row2 = row2 * 10 + formula.charCodeAt(k) - 48;
26587
+ if (row2 < 1 || row2 > MAX_ROW + 1) return void 0;
26588
+ rows.push(row2 - 1);
26589
+ }
26590
+ return rows;
26591
+ }
26592
+ function shiftSpec(spec2, slots, rows) {
26593
+ const startRow = rows[slots.start];
26594
+ const endRow = slots.end === void 0 ? startRow : rows[slots.end];
26595
+ if (startRow === spec2.start.row && endRow === spec2.end.row) return spec2;
26596
+ const { start: from, end: to } = spec2;
26597
+ const start = { row: startRow, col: from.col, rowAbs: from.rowAbs, colAbs: from.colAbs };
26598
+ if (slots.end === void 0) return { kind: spec2.kind, start, end: start };
26599
+ const end = { row: endRow, col: to.col, rowAbs: to.rowAbs, colAbs: to.colAbs };
26600
+ return { kind: spec2.kind, start, end };
26601
+ }
26602
+ function copyTree(node, t, rows) {
26603
+ switch (node.type) {
26604
+ case "ref": {
26605
+ const slots = node.ref ? t.refs.get(node.ref) : void 0;
26606
+ if (!slots || !node.ref) return node;
26607
+ const ref = shiftSpec(node.ref, slots, rows);
26608
+ if (ref === node.ref) return node;
26609
+ const copy2 = { type: "ref" };
26610
+ if (node.prefix) copy2.prefix = node.prefix;
26611
+ copy2.ref = ref;
26612
+ if (node.spill) copy2.spill = true;
26613
+ return copy2;
26255
26614
  }
26256
- const first3 = start.row >> CHUNK_SHIFT;
26257
- const last = end.row >> CHUNK_SHIFT;
26258
- if (last - first3 + 1 > CHUNKS) {
26259
- const cols2 = inner(this.tall, area.sheet);
26260
- for (let c2 = start.col; c2 <= end.col; c2++) push2(cols2, c2, entry3);
26261
- return;
26615
+ case "unary":
26616
+ case "percent": {
26617
+ const operand4 = copyTree(node.operand, t, rows);
26618
+ if (operand4 === node.operand) return node;
26619
+ return node.type === "unary" ? { type: "unary", op: node.op, operand: operand4 } : { type: "percent", operand: operand4 };
26262
26620
  }
26263
- const cols = inner(this.chunked, area.sheet);
26264
- for (let c2 = start.col; c2 <= end.col; c2++) {
26265
- let chunks = cols.get(c2);
26266
- if (!chunks) cols.set(c2, chunks = /* @__PURE__ */ new Map());
26267
- for (let k = first3; k <= last; k++) push2(chunks, k, entry3);
26621
+ case "binary": {
26622
+ const left2 = copyTree(node.left, t, rows);
26623
+ const right2 = copyTree(node.right, t, rows);
26624
+ if (left2 === node.left && right2 === node.right) return node;
26625
+ return { type: "binary", op: node.op, left: left2, right: right2 };
26268
26626
  }
26269
- }
26270
- /** Items whose areas intersect `range` on `sheet` (each once). */
26271
- dependents(sheet, range, out) {
26272
- const found = out instanceof Set ? out : /* @__PURE__ */ new Set();
26273
- this.collect(sheet, range, found);
26274
- if (Array.isArray(out)) for (const node of found) out.push(node);
26275
- }
26276
- collect(sheet, range, out) {
26277
- const { start, end } = range;
26278
- const check3 = (list) => {
26279
- if (list) {
26280
- for (const entry3 of list) if (rangesIntersect(entry3.range, range)) out.add(entry3.node);
26281
- }
26282
- };
26283
- const cells3 = this.cells.get(sheet);
26284
- if (cells3) {
26285
- const size = (end.row - start.row + 1) * (end.col - start.col + 1);
26286
- if (size <= 4096) {
26287
- for (let r = start.row; r <= end.row; r++) {
26288
- for (let c2 = start.col; c2 <= end.col; c2++) {
26289
- const list = cells3.get(cellKey(r, c2));
26290
- if (list) for (const node of list) out.add(node);
26291
- }
26292
- }
26293
- } else {
26294
- for (const [key2, list] of cells3) {
26295
- const { row: row2, col } = keyToAddress(key2);
26296
- if (row2 >= start.row && row2 <= end.row && col >= start.col && col <= end.col)
26297
- for (const node of list) out.add(node);
26298
- }
26299
- }
26627
+ case "call": {
26628
+ const args = copyAll(node.args, t, rows);
26629
+ if (args === node.args) return node;
26630
+ return { type: "call", name: node.name, rawName: node.rawName, args };
26300
26631
  }
26301
- const columns = (map, visit2) => {
26302
- if (!map) return;
26303
- if (end.col - start.col + 1 <= map.size) {
26304
- for (let c2 = start.col; c2 <= end.col; c2++) if (map.has(c2)) visit2(c2);
26305
- } else for (const c2 of map.keys()) if (c2 >= start.col && c2 <= end.col) visit2(c2);
26306
- };
26307
- const chunked = this.chunked.get(sheet);
26308
- const first3 = start.row >> CHUNK_SHIFT;
26309
- const last = end.row >> CHUNK_SHIFT;
26310
- columns(chunked, (c2) => {
26311
- const chunks = chunked?.get(c2);
26312
- if (last - first3 + 1 <= chunks.size) {
26313
- for (let k = first3; k <= last; k++) check3(chunks.get(k));
26314
- } else for (const [k, list] of chunks) if (k >= first3 && k <= last) check3(list);
26315
- });
26316
- const tall = this.tall.get(sheet);
26317
- columns(tall, (c2) => check3(tall?.get(c2)));
26318
- check3(this.wide.get(sheet));
26319
- }
26320
- };
26321
-
26322
- // xlsx/formula/graph-index.ts
26323
- var ColumnIndex = class {
26324
- sheets = /* @__PURE__ */ new Map();
26325
- constructor(nodes) {
26326
- for (const node of nodes) {
26327
- let cols = this.sheets.get(node.sheet);
26328
- if (!cols) this.sheets.set(node.sheet, cols = /* @__PURE__ */ new Map());
26329
- let list = cols.get(node.col);
26330
- if (!list) cols.set(node.col, list = []);
26331
- list.push(node);
26632
+ case "invoke": {
26633
+ const callee = copyTree(node.callee, t, rows);
26634
+ const args = copyAll(node.args, t, rows);
26635
+ if (callee === node.callee && args === node.args) return node;
26636
+ return { type: "invoke", callee, args };
26332
26637
  }
26333
- for (const cols of this.sheets.values())
26334
- for (const list of cols.values()) list.sort((a2, b) => a2.row - b.row);
26638
+ default:
26639
+ return node;
26335
26640
  }
26336
- nodesIn(area, out) {
26337
- const cols = this.sheets.get(area.sheet);
26338
- if (!cols) return;
26339
- const { start, end } = area.range;
26340
- const width = end.col - start.col + 1;
26341
- const scan3 = (list) => {
26342
- let lo = 0;
26343
- let hi = list.length;
26344
- while (lo < hi) {
26345
- const mid = lo + hi >> 1;
26346
- if (list[mid].row < start.row) lo = mid + 1;
26347
- else hi = mid;
26348
- }
26349
- for (let i2 = lo; i2 < list.length; i2++) {
26350
- const node = list[i2];
26351
- if (node.row > end.row) break;
26352
- out.push(node);
26353
- }
26354
- };
26355
- if (width <= cols.size) {
26356
- for (let c2 = start.col; c2 <= end.col; c2++) {
26357
- const list = cols.get(c2);
26358
- if (list) scan3(list);
26359
- }
26360
- } else {
26361
- for (const [c2, list] of cols) if (c2 >= start.col && c2 <= end.col) scan3(list);
26362
- }
26641
+ }
26642
+ function copyAll(nodes, t, rows) {
26643
+ let out;
26644
+ for (let i2 = 0; i2 < nodes.length; i2++) {
26645
+ const node = nodes[i2];
26646
+ const copy2 = copyTree(node, t, rows);
26647
+ if (copy2 !== node && !out) out = nodes.slice(0, i2);
26648
+ out?.push(copy2);
26649
+ }
26650
+ return out ?? nodes;
26651
+ }
26652
+ function fromTemplate(t, formula, shape2) {
26653
+ const rows = rowsFor(t, formula, shape2);
26654
+ return rows ? copyTree(t.ast, t, rows) : void 0;
26655
+ }
26656
+
26657
+ // xlsx/formula/parse-cache.ts
26658
+ var MAX_TEMPLATE_LENGTH = 2048;
26659
+ var MAX_ENTRIES = 1e5;
26660
+ var MAX_TEMPLATES2 = 1e4;
26661
+ var NO_RUNS = { hash: 0, starts: [], ends: [] };
26662
+ var treeOf = (made) => "ast" in made ? made.ast : made;
26663
+ var ParseCache = class {
26664
+ byText = /* @__PURE__ */ new Map();
26665
+ templates = /* @__PURE__ */ new Map();
26666
+ parse(formula) {
26667
+ let ast = this.byText.get(formula);
26668
+ if (ast) return ast;
26669
+ ast = this.parseNew(formula);
26670
+ if (this.byText.size > MAX_ENTRIES) this.byText.clear();
26671
+ this.byText.set(formula, ast);
26672
+ return ast;
26363
26673
  }
26364
- };
26365
- var ReverseIndex = class extends AreaIndex {
26366
- constructor(nodes) {
26367
- super();
26368
- for (const node of nodes) {
26369
- for (const dep of node.deps) this.add(dep, node);
26370
- for (const dep of node.dynamicDeps) this.add(dep, node);
26674
+ parseNew(formula) {
26675
+ if (formula.length > MAX_TEMPLATE_LENGTH) return treeOf(makeTemplate(formula, NO_RUNS));
26676
+ const shape2 = formulaShape(formula);
26677
+ const template = this.templates.get(shape2.hash);
26678
+ const reused = template && fromTemplate(template, formula, shape2);
26679
+ if (reused) return reused;
26680
+ const made = makeTemplate(formula, shape2);
26681
+ if (!template && "ast" in made) {
26682
+ if (this.templates.size > MAX_TEMPLATES2) this.templates.clear();
26683
+ this.templates.set(shape2.hash, made);
26371
26684
  }
26685
+ return treeOf(made);
26372
26686
  }
26373
26687
  };
26374
26688
 
@@ -26508,7 +26822,7 @@ var EngineHost = class {
26508
26822
  options;
26509
26823
  nodes = /* @__PURE__ */ new Map();
26510
26824
  footprints = /* @__PURE__ */ new Set();
26511
- astCache = /* @__PURE__ */ new Map();
26825
+ parsed = new ParseCache();
26512
26826
  boundsCache = /* @__PURE__ */ new Map();
26513
26827
  inProgress = /* @__PURE__ */ new Set();
26514
26828
  cycles = /* @__PURE__ */ new Set();
@@ -26532,6 +26846,8 @@ var EngineHost = class {
26532
26846
  evaluations = 0;
26533
26847
  built = false;
26534
26848
  needsFull = false;
26849
+ /** No recalculation has run since the workbook was opened: stored values may stand. */
26850
+ fresh = true;
26535
26851
  // ---- EvalHost ----
26536
26852
  readCell(sheet, row2, col) {
26537
26853
  const cell = this.workbook.sheets[sheet]?.rows.get(row2)?.get(col);
@@ -26620,19 +26936,7 @@ var EngineHost = class {
26620
26936
  return this.workbook.sheets[sheet]?.rows.get(row2)?.get(col)?.formula;
26621
26937
  }
26622
26938
  parse(formula) {
26623
- let ast = this.astCache.get(formula);
26624
- if (!ast) {
26625
- try {
26626
- ast = parseFormula(formula);
26627
- } catch (e) {
26628
- if (e instanceof RangeError) ast = new FormulaError(e.message, -1, "#VALUE!");
26629
- else if (!(e instanceof FormulaError)) throw e;
26630
- else ast = e;
26631
- }
26632
- if (this.astCache.size > 1e5) this.astCache.clear();
26633
- this.astCache.set(formula, ast);
26634
- }
26635
- return ast;
26939
+ return this.parsed.parse(formula);
26636
26940
  }
26637
26941
  now() {
26638
26942
  return this.options.now ? this.options.now() : /* @__PURE__ */ new Date();
@@ -26641,20 +26945,6 @@ var EngineHost = class {
26641
26945
  return this.options.random ? this.options.random() : Math.random();
26642
26946
  }
26643
26947
  // ---- graph ----
26644
- build() {
26645
- this.nodes.clear();
26646
- this.footprints.clear();
26647
- this.footprintIndex = void 0;
26648
- this.workbook.sheets.forEach((sheet, s) => {
26649
- for (const [row2, cells3] of sheet.rows) {
26650
- for (const [col, cell] of cells3) {
26651
- if (cell.formula) this.createNode(s, row2, col, cell, true);
26652
- }
26653
- }
26654
- });
26655
- this.built = true;
26656
- this.graphChanged();
26657
- }
26658
26948
  createNode(sheet, row2, col, cell, initial) {
26659
26949
  const formula = cell.formula ?? "";
26660
26950
  const parsed = this.parse(formula);
@@ -26683,6 +26973,14 @@ var EngineHost = class {
26683
26973
  map.set(cellKey(row2, col), node);
26684
26974
  return node;
26685
26975
  }
26976
+ /** Parses a node's text again after a structural edit rewrote it (see `FormulaNode.stale`). */
26977
+ refreshAst(node) {
26978
+ const parsed = this.parse(node.formula);
26979
+ delete node.stale;
26980
+ delete node.parseError;
26981
+ node.ast = parsed instanceof FormulaError ? void 0 : parsed;
26982
+ if (parsed instanceof FormulaError && parsed.code) node.parseError = err(parsed.code);
26983
+ }
26686
26984
  removeNode(node, changed) {
26687
26985
  const sheet = this.workbook.sheets[node.sheet];
26688
26986
  if (node.spill && sheet) {
@@ -27113,6 +27411,7 @@ var EngineCore = class extends EngineHost {
27113
27411
  return { host: this, sheet, row: row2, col, scope: void 0, depth: 0, root, legacy };
27114
27412
  }
27115
27413
  evaluateFormula(node) {
27414
+ if (node.stale) this.refreshAst(node);
27116
27415
  if (!node.ast) return node.parseError ?? ERR.NAME;
27117
27416
  const ast = node.ast;
27118
27417
  const frame = this.frameFor(node.sheet, node.row, node.col, ast, node.legacy);
@@ -27130,20 +27429,363 @@ var EngineCore = class extends EngineHost {
27130
27429
  }
27131
27430
  };
27132
27431
 
27432
+ // xlsx/formula/shift-graph.ts
27433
+ var POSITIONAL = /* @__PURE__ */ new Set(["ROW", "COLUMN"]);
27434
+ var DESCRIPTIVE = /* @__PURE__ */ new Set(["FORMULATEXT", "CELL", "SHEET"]);
27435
+ var traitsCache = /* @__PURE__ */ new WeakMap();
27436
+ function astTraits(ast) {
27437
+ const cached3 = traitsCache.get(ast);
27438
+ if (cached3) return cached3;
27439
+ const sheets = /* @__PURE__ */ new Set();
27440
+ const traits = { complex: false, opaque: false, positional: false, descriptive: false, sheets };
27441
+ const stack2 = [ast];
27442
+ while (stack2.length) {
27443
+ const node = stack2.pop();
27444
+ switch (node.type) {
27445
+ case "ref":
27446
+ if (node.prefix) {
27447
+ sheets.add(node.prefix.sheet.toLowerCase());
27448
+ if (node.prefix.sheet2 !== void 0) {
27449
+ sheets.add(node.prefix.sheet2.toLowerCase());
27450
+ traits.complex = true;
27451
+ }
27452
+ }
27453
+ break;
27454
+ case "name":
27455
+ if (node.prefix) sheets.add(node.prefix.sheet.toLowerCase());
27456
+ traits.complex = true;
27457
+ break;
27458
+ case "structured":
27459
+ traits.complex = true;
27460
+ break;
27461
+ case "unary":
27462
+ case "percent":
27463
+ stack2.push(node.operand);
27464
+ break;
27465
+ case "binary":
27466
+ stack2.push(node.left, node.right);
27467
+ break;
27468
+ case "call":
27469
+ if (POSITIONAL.has(node.name)) traits.positional = true;
27470
+ if (DESCRIPTIVE.has(node.name)) traits.descriptive = true;
27471
+ if (!getFunction(node.name)) traits.complex = traits.opaque = true;
27472
+ stack2.push(...node.args);
27473
+ break;
27474
+ case "invoke":
27475
+ stack2.push(node.callee, ...node.args);
27476
+ break;
27477
+ }
27478
+ }
27479
+ traitsCache.set(ast, traits);
27480
+ return traits;
27481
+ }
27482
+ function intervalDelta(a2, b, shift2) {
27483
+ const { at: at2, count: count2 } = shift2;
27484
+ if (count2 > 0) {
27485
+ if (b < at2) return 0;
27486
+ if (a2 < at2) return void 0;
27487
+ return b + count2 > (shift2.axis === "row" ? MAX_ROW : MAX_COL) ? void 0 : count2;
27488
+ }
27489
+ const last = at2 - count2 - 1;
27490
+ if (b < at2) return 0;
27491
+ if (a2 > last) return count2;
27492
+ return void 0;
27493
+ }
27494
+ function areaDelta(area, sheet, shift2) {
27495
+ if (area.sheet !== sheet) return 0;
27496
+ const { start, end } = area.range;
27497
+ return shift2.axis === "row" ? intervalDelta(start.row, end.row, shift2) : intervalDelta(start.col, end.col, shift2);
27498
+ }
27499
+ function movedRange(range, axis, delta) {
27500
+ if (delta === 0) return range;
27501
+ return axis === "row" ? {
27502
+ start: { row: range.start.row + delta, col: range.start.col },
27503
+ end: { row: range.end.row + delta, col: range.end.col }
27504
+ } : {
27505
+ start: { row: range.start.row, col: range.start.col + delta },
27506
+ end: { row: range.end.row, col: range.end.col + delta }
27507
+ };
27508
+ }
27509
+ function reachesEdit(areas, sheet, shift2) {
27510
+ for (const area of areas) {
27511
+ if (area.sheet !== sheet) continue;
27512
+ if ((shift2.axis === "row" ? area.range.end.row : area.range.end.col) >= shift2.at) return true;
27513
+ }
27514
+ return false;
27515
+ }
27516
+ function intersectionMoves(area, delta, pos, nodeDelta, axis) {
27517
+ if (delta === nodeDelta) return false;
27518
+ const { start, end } = area.range;
27519
+ const a2 = axis === "row" ? start.row : start.col;
27520
+ const b = axis === "row" ? end.row : end.col;
27521
+ if (a2 === b) return false;
27522
+ return pos >= a2 && pos <= b || pos + nodeDelta >= a2 + delta && pos + nodeDelta <= b + delta;
27523
+ }
27524
+
27525
+ // xlsx/formula/engine-structure.ts
27526
+ var sameAreas = (a2, b) => a2.length === b.length && a2.every((x, i2) => {
27527
+ const y = b[i2];
27528
+ return x.sheet === y.sheet && sameRange(x.range, y.range);
27529
+ });
27530
+ var EngineStructure = class extends EngineCore {
27531
+ /** Formulas a structural edit left to recalculate (with their dependents) on the next pass. */
27532
+ structuralSeeds = /* @__PURE__ */ new Set();
27533
+ /** Whether the graph describes the workbook as it was before the edit just reported. */
27534
+ graphReady() {
27535
+ return this.built && !this.needsFull;
27536
+ }
27537
+ /** Parses a node's current text again and recomputes its precedents. */
27538
+ reanalyze(node) {
27539
+ const parsed = this.parse(node.formula);
27540
+ delete node.stale;
27541
+ delete node.parseError;
27542
+ if (parsed instanceof FormulaError) {
27543
+ node.ast = void 0;
27544
+ if (parsed.code) node.parseError = err(parsed.code);
27545
+ node.deps = [];
27546
+ node.dynamicDeps = [];
27547
+ return;
27548
+ }
27549
+ node.ast = parsed;
27550
+ const found = analyze(parsed, this, node.sheet, node.row, node.col);
27551
+ node.deps = found.deps;
27552
+ node.dynamicDeps = found.dynamicDeps;
27553
+ node.volatile = found.volatile;
27554
+ node.unsupported = found.unsupported;
27555
+ }
27556
+ /**
27557
+ * Moves the graph for rows or columns inserted or deleted on `sheet`, after the workbook was
27558
+ * changed (cells moved, formulas rewritten). Spills on the sheet, array formulas the edit cuts
27559
+ * and anything unexpected fall back to a rebuild on the next recalculation.
27560
+ */
27561
+ shiftCells(sheet, shift2) {
27562
+ if (!this.graphReady() || !this.shiftable(sheet, shift2)) {
27563
+ this.invalidate();
27564
+ return;
27565
+ }
27566
+ const ws = this.workbook.sheets[sheet];
27567
+ const own = this.nodes.get(sheet);
27568
+ if (!ws) return;
27569
+ const moved = /* @__PURE__ */ new Map();
27570
+ if (own) {
27571
+ const next = /* @__PURE__ */ new Map();
27572
+ for (const node of own.values()) {
27573
+ const pos = shift2.axis === "row" ? node.row : node.col;
27574
+ const delta = intervalDelta(pos, pos, shift2);
27575
+ if (delta === void 0) {
27576
+ this.dropNode(node);
27577
+ continue;
27578
+ }
27579
+ if (delta !== 0) {
27580
+ moved.set(node, delta);
27581
+ if (shift2.axis === "row") node.row += delta;
27582
+ else node.col += delta;
27583
+ }
27584
+ next.set(cellKey(node.row, node.col), node);
27585
+ }
27586
+ this.nodes.set(sheet, next);
27587
+ }
27588
+ for (const map of this.nodes.values())
27589
+ for (const node of map.values()) {
27590
+ if (!this.shiftNode(node, sheet, shift2, moved.get(node) ?? 0)) {
27591
+ this.invalidate();
27592
+ return;
27593
+ }
27594
+ }
27595
+ this.boundsCache.clear();
27596
+ this.graphChanged();
27597
+ }
27598
+ /** No spill or stale spilled value on the sheet, and no array formula the edit cuts. */
27599
+ shiftable(sheet, shift2) {
27600
+ for (const node of this.footprints) {
27601
+ if (node.sheet !== sheet) continue;
27602
+ if (node.spill || node.blockedSpill) return false;
27603
+ if (node.arrayRange && areaDelta({ sheet, range: node.arrayRange }, sheet, shift2) === void 0)
27604
+ return false;
27605
+ }
27606
+ const ws = this.workbook.sheets[sheet];
27607
+ let spilled = false;
27608
+ ws?.rows.forEach((cells3) => {
27609
+ if (!spilled) cells3.forEach((cell) => spilled ||= isSpilledCell(cell));
27610
+ });
27611
+ return !spilled;
27612
+ }
27613
+ dropNode(node) {
27614
+ this.structuralSeeds.delete(node);
27615
+ this.cycles.delete(node);
27616
+ this.footprints.delete(node);
27617
+ }
27618
+ /** Updates one formula for the edit; false when the graph cannot follow it. */
27619
+ shiftNode(node, sheet, shift2, nodeDelta) {
27620
+ const cell = this.workbook.sheets[node.sheet]?.rows.get(node.row)?.get(node.col);
27621
+ if (cell?.formula === void 0) return false;
27622
+ if (node.arrayRange) {
27623
+ const delta = areaDelta({ sheet: node.sheet, range: node.arrayRange }, sheet, shift2);
27624
+ if (delta === void 0) return false;
27625
+ node.arrayRange = movedRange(node.arrayRange, shift2.axis, delta);
27626
+ if (!cell.arrayRange || !sameRange(node.arrayRange, normalizeRange(cell.arrayRange)))
27627
+ return false;
27628
+ }
27629
+ const changed = cell.formula !== node.formula;
27630
+ const traits = node.ast && astTraits(node.ast);
27631
+ const reached = reachesEdit(node.deps, sheet, shift2) || reachesEdit(node.dynamicDeps, sheet, shift2);
27632
+ if (!changed && !reached && nodeDelta === 0 && !traits?.descriptive && !traits?.opaque)
27633
+ return true;
27634
+ if (traits?.complex) {
27635
+ node.formula = cell.formula;
27636
+ this.reanalyze(node);
27637
+ this.structuralSeeds.add(node);
27638
+ return true;
27639
+ }
27640
+ const pos = shift2.axis === "row" ? node.row - nodeDelta : node.col - nodeDelta;
27641
+ const moves = { seed: !!traits?.descriptive, moved: false, cut: false };
27642
+ const deps = this.moveAreas(node.deps, sheet, shift2, pos, nodeDelta, moves);
27643
+ const dynamicDeps = this.moveAreas(node.dynamicDeps, sheet, shift2, pos, nodeDelta, moves);
27644
+ const { cut, moved: anyMoved } = moves;
27645
+ let seed = moves.seed;
27646
+ if (cut || changed !== anyMoved) {
27647
+ node.formula = cell.formula;
27648
+ this.reanalyze(node);
27649
+ this.structuralSeeds.add(node);
27650
+ return true;
27651
+ }
27652
+ if (traits?.positional && (anyMoved || nodeDelta !== 0)) seed = true;
27653
+ node.deps = deps;
27654
+ node.dynamicDeps = dynamicDeps;
27655
+ if (changed) {
27656
+ node.formula = cell.formula;
27657
+ node.stale = true;
27658
+ }
27659
+ if (seed) this.structuralSeeds.add(node);
27660
+ return true;
27661
+ }
27662
+ /**
27663
+ * Areas moved for the edit (the same list when none moves). Records in `moves` whether one
27664
+ * moved, one was cut (deleted, widened, pushed off the grid) and whether implicit intersection
27665
+ * with one can now pick another cell.
27666
+ */
27667
+ moveAreas(list, sheet, shift2, pos, nodeDelta, moves) {
27668
+ let out;
27669
+ for (let i2 = 0; i2 < list.length; i2++) {
27670
+ const area = list[i2];
27671
+ const delta = areaDelta(area, sheet, shift2);
27672
+ if (delta === void 0) {
27673
+ moves.cut = true;
27674
+ continue;
27675
+ }
27676
+ if (intersectionMoves(area, delta, pos, nodeDelta, shift2.axis)) moves.seed = true;
27677
+ if (delta === 0) continue;
27678
+ moves.moved = true;
27679
+ out ??= list.slice();
27680
+ out[i2] = { sheet: area.sheet, range: movedRange(area.range, shift2.axis, delta) };
27681
+ }
27682
+ return out ?? list;
27683
+ }
27684
+ /**
27685
+ * Follows a sheet rename (`from` to `to`, formulas already rewritten): precedents are kept by
27686
+ * sheet index, so only rewritten formulas need their text parsed again, and only formulas that
27687
+ * name the new sheet without having been rewritten (previously unresolved), describe sheets
27688
+ * (CELL, SHEET, FORMULATEXT) or read defined names whose areas changed are recalculated.
27689
+ */
27690
+ renameSheet(from, to) {
27691
+ if (!this.graphReady()) {
27692
+ this.invalidate();
27693
+ return;
27694
+ }
27695
+ const target = to.toLowerCase();
27696
+ let depsChanged = false;
27697
+ for (const map of this.nodes.values())
27698
+ for (const node of map.values()) {
27699
+ const cell = this.workbook.sheets[node.sheet]?.rows.get(node.row)?.get(node.col);
27700
+ if (cell?.formula === void 0) {
27701
+ this.invalidate();
27702
+ return;
27703
+ }
27704
+ const changed = cell.formula !== node.formula;
27705
+ const traits = node.ast && astTraits(node.ast);
27706
+ node.formula = cell.formula;
27707
+ if (traits?.complex || !changed && traits?.sheets.has(target)) {
27708
+ const deps = node.deps;
27709
+ const dynamicDeps = node.dynamicDeps;
27710
+ this.reanalyze(node);
27711
+ const same5 = sameAreas(deps, node.deps) && sameAreas(dynamicDeps, node.dynamicDeps);
27712
+ if (!same5) depsChanged = true;
27713
+ if (!same5 || !traits?.complex) this.structuralSeeds.add(node);
27714
+ } else if (changed) node.stale = true;
27715
+ if (traits?.descriptive || traits?.opaque) this.structuralSeeds.add(node);
27716
+ }
27717
+ if (depsChanged) this.graphChanged();
27718
+ }
27719
+ /** Takes the formulas queued by structural edits. */
27720
+ takeStructuralSeeds(into) {
27721
+ for (const node of this.structuralSeeds)
27722
+ if (this.nodes.get(node.sheet)?.get(cellKey(node.row, node.col)) === node) into.add(node);
27723
+ this.structuralSeeds.clear();
27724
+ }
27725
+ };
27726
+
27133
27727
  // xlsx/formula/engine.ts
27134
- var Engine = class extends EngineCore {
27728
+ var Engine = class extends EngineStructure {
27729
+ preparation;
27730
+ /** The prepared formulas the first recalculation checks (see `trustsStoredValues`). */
27731
+ prepared;
27732
+ // ---- the graph ----
27733
+ prepare(timeRemaining) {
27734
+ if (this.built || this.needsFull) return true;
27735
+ this.preparation ??= this.startPreparation();
27736
+ if (!this.preparation.run(timeRemaining)) return false;
27737
+ this.finishPreparation(this.preparation);
27738
+ return true;
27739
+ }
27740
+ discardPreparation() {
27741
+ if (!this.fresh || this.needsFull) return;
27742
+ this.preparation = void 0;
27743
+ this.prepared = void 0;
27744
+ this.structuralSeeds.clear();
27745
+ this.nodes.clear();
27746
+ this.footprints.clear();
27747
+ this.graphChanged();
27748
+ this.built = false;
27749
+ }
27750
+ /** Completes the graph, continuing a preparation that is under way. */
27751
+ build() {
27752
+ const preparation = this.preparation ?? this.startPreparation();
27753
+ preparation.run();
27754
+ this.finishPreparation(preparation);
27755
+ }
27756
+ startPreparation() {
27757
+ this.nodes.clear();
27758
+ this.footprints.clear();
27759
+ this.footprintIndex = void 0;
27760
+ this.prepared = void 0;
27761
+ return new GraphPreparation(
27762
+ this.workbook,
27763
+ (s, row2, col, cell) => this.createNode(s, row2, col, cell, true)
27764
+ );
27765
+ }
27766
+ finishPreparation(preparation) {
27767
+ this.preparation = void 0;
27768
+ this.built = true;
27769
+ this.graphChanged();
27770
+ this.reverseIndex = preparation.reverse;
27771
+ this.prepared = preparation.formulas;
27772
+ }
27135
27773
  // ---- recalculation ----
27136
27774
  recalculateAll() {
27775
+ this.fresh = false;
27776
+ this.prepared = void 0;
27137
27777
  if (this.needsFull) this.releaseAllSpills();
27138
27778
  if (!this.built || this.needsFull) this.build();
27139
27779
  this.needsFull = false;
27780
+ this.structuralSeeds.clear();
27140
27781
  this.cycles = /* @__PURE__ */ new Set();
27141
27782
  this.run(new Set(this.allNodes()), true);
27142
27783
  }
27143
27784
  recalculateFrom(changes) {
27144
27785
  const seeds = /* @__PURE__ */ new Set();
27145
- if (!this.built && !this.needsFull) {
27146
- this.build();
27786
+ if (this.fresh && !this.needsFull) {
27787
+ this.fresh = false;
27788
+ if (!this.built) this.build();
27147
27789
  if (!this.trustsStoredValues(changes, seeds)) {
27148
27790
  this.recalculateAll();
27149
27791
  return;
@@ -27187,6 +27829,7 @@ var Engine = class extends EngineCore {
27187
27829
  }
27188
27830
  }
27189
27831
  if (structural) this.graphChanged();
27832
+ this.takeStructuralSeeds(seeds);
27190
27833
  const reverse2 = this.reverse();
27191
27834
  for (const area of changed) reverse2.dependents(area.sheet, area.range, seeds);
27192
27835
  for (const node of this.allNodes()) if (node.volatile) seeds.add(node);
@@ -27198,13 +27841,18 @@ var Engine = class extends EngineCore {
27198
27841
  * without a value into `missing`.
27199
27842
  */
27200
27843
  trustsStoredValues(changes, missing) {
27844
+ const prepared = this.prepared;
27845
+ this.prepared = void 0;
27846
+ if (!prepared) return false;
27201
27847
  const changed = new Set(changes.map((c2) => `${c2.sheet}:${cellKey(c2.row, c2.col)}`));
27202
- for (const node of this.allNodes()) {
27203
- const legacyOrArray = node.legacy || node.arrayRange;
27204
- if (!legacyOrArray && !changed.has(`${node.sheet}:${cellKey(node.row, node.col)}`))
27848
+ const current = (node) => this.nodes.get(node.sheet)?.get(cellKey(node.row, node.col)) === node;
27849
+ for (const node of prepared.dynamic) {
27850
+ if (current(node) && !changed.has(`${node.sheet}:${cellKey(node.row, node.col)}`))
27205
27851
  return false;
27852
+ }
27853
+ for (const node of prepared.unvalued) {
27206
27854
  const cell = this.workbook.sheets[node.sheet]?.rows.get(node.row)?.get(node.col);
27207
- if (cell && cell.value === null && !node.keepCached) missing.add(node);
27855
+ if (current(node) && cell && cell.value === null) missing.add(node);
27208
27856
  }
27209
27857
  return true;
27210
27858
  }
@@ -27218,7 +27866,10 @@ var Engine = class extends EngineCore {
27218
27866
  return !!anchor?.spill && rangeHas(anchor.spill, row2, col);
27219
27867
  }
27220
27868
  invalidate() {
27869
+ this.structuralSeeds.clear();
27221
27870
  this.releaseAllSpills();
27871
+ this.preparation = void 0;
27872
+ this.prepared = void 0;
27222
27873
  this.nodes.clear();
27223
27874
  this.footprints.clear();
27224
27875
  this.graphChanged();
@@ -30149,13 +30800,13 @@ function linearTrend(values2) {
30149
30800
  const slope = den ? num3 / den : 0;
30150
30801
  return (k) => Number((meanY + slope * (k - meanX)).toPrecision(15));
30151
30802
  }
30152
- var isDigit4 = (ch) => ch !== void 0 && ch >= "0" && ch <= "9";
30803
+ var isDigit5 = (ch) => ch !== void 0 && ch >= "0" && ch <= "9";
30153
30804
  function matchTextNumber(text2) {
30154
30805
  let end = text2.length;
30155
- while (end > 0 && !isDigit4(text2[end - 1])) end--;
30806
+ while (end > 0 && !isDigit5(text2[end - 1])) end--;
30156
30807
  if (end === 0) return null;
30157
30808
  let start = end;
30158
- while (start > 0 && isDigit4(text2[start - 1])) start--;
30809
+ while (start > 0 && isDigit5(text2[start - 1])) start--;
30159
30810
  return [text2, text2.slice(0, start), text2.slice(start, end), text2.slice(end)];
30160
30811
  }
30161
30812
  var QUARTER = /^(q|qtr|quarter)(\s?)([1-4])$/i;
@@ -30338,10 +30989,10 @@ function removeValidationArea(rules, area) {
30338
30989
  }
30339
30990
 
30340
30991
  // xlsx/edit/conditional-formulas.ts
30341
- function rewriteConditionalRule(rule2, rewrite3) {
30992
+ function rewriteConditionalRule(rule2, rewrite2) {
30342
30993
  const safe2 = (formula) => {
30343
30994
  try {
30344
- return rewrite3(formula);
30995
+ return rewrite2(formula);
30345
30996
  } catch {
30346
30997
  return formula;
30347
30998
  }
@@ -30860,7 +31511,7 @@ function shiftPreservedXml(sheet, shift2, moveRange, banded) {
30860
31511
  (xml) => xml.includes("sqref")
30861
31512
  );
30862
31513
  }
30863
- function rewritePreservedFormulas(sheet, rewrite3) {
31514
+ function rewritePreservedFormulas(sheet, rewrite2) {
30864
31515
  editEntries(
30865
31516
  sheet,
30866
31517
  "extLst",
@@ -30870,7 +31521,7 @@ function rewritePreservedFormulas(sheet, rewrite3) {
30870
31521
  const text2 = f6.textContent ?? "";
30871
31522
  let next = text2;
30872
31523
  try {
30873
- next = rewrite3(text2, sheet.name);
31524
+ next = rewrite2(text2, sheet.name);
30874
31525
  } catch {
30875
31526
  next = text2;
30876
31527
  }
@@ -30885,46 +31536,46 @@ function rewritePreservedFormulas(sheet, rewrite3) {
30885
31536
  }
30886
31537
 
30887
31538
  // xlsx/edit/shift-formulas.ts
30888
- function safe(rewrite3, formula, formulaSheet) {
31539
+ function safe(rewrite2, formula, formulaSheet) {
30889
31540
  try {
30890
- return rewrite3(formula, formulaSheet);
31541
+ return rewrite2(formula, formulaSheet);
30891
31542
  } catch {
30892
31543
  return formula;
30893
31544
  }
30894
31545
  }
30895
- function rewriteFormulas(workbook, rewrite3) {
30896
- for (const sheet of workbook.sheets) rewriteSheetFormulas(sheet, rewrite3);
31546
+ function rewriteFormulas(workbook, rewrite2) {
31547
+ for (const sheet of workbook.sheets) rewriteSheetFormulas(sheet, rewrite2);
30897
31548
  for (const definedName of workbook.definedNames) {
30898
31549
  const local = definedName.localSheet === void 0 ? void 0 : workbook.sheets[definedName.localSheet];
30899
- definedName.formula = safe(rewrite3, definedName.formula, local?.name ?? "");
31550
+ definedName.formula = safe(rewrite2, definedName.formula, local?.name ?? "");
30900
31551
  }
30901
31552
  }
30902
- function rewriteSheetFormulas(sheet, rewrite3) {
30903
- rewritePreservedFormulas(sheet, rewrite3);
31553
+ function rewriteSheetFormulas(sheet, rewrite2) {
31554
+ rewritePreservedFormulas(sheet, rewrite2);
30904
31555
  {
30905
31556
  const name = sheet.name;
30906
31557
  for (const table2 of sheet.tables)
30907
31558
  for (const column of table2.columns)
30908
31559
  if (column.calculatedColumnFormula !== void 0)
30909
- column.calculatedColumnFormula = safe(rewrite3, column.calculatedColumnFormula, name);
31560
+ column.calculatedColumnFormula = safe(rewrite2, column.calculatedColumnFormula, name);
30910
31561
  for (const cells3 of sheet.rows.values())
30911
31562
  for (const cell of cells3.values())
30912
- if (cell.formula !== void 0) cell.formula = safe(rewrite3, cell.formula, name);
31563
+ if (cell.formula !== void 0) cell.formula = safe(rewrite2, cell.formula, name);
30913
31564
  for (const cf of sheet.conditionalFormats)
30914
- for (const rule2 of cf.rules) rewriteConditionalRule(rule2, (f6) => rewrite3(f6, name));
31565
+ for (const rule2 of cf.rules) rewriteConditionalRule(rule2, (f6) => rewrite2(f6, name));
30915
31566
  for (const dv of sheet.dataValidations) {
30916
- if (dv.formula1 !== void 0) dv.formula1 = safe(rewrite3, dv.formula1, name);
30917
- if (dv.formula2 !== void 0) dv.formula2 = safe(rewrite3, dv.formula2, name);
31567
+ if (dv.formula1 !== void 0) dv.formula1 = safe(rewrite2, dv.formula1, name);
31568
+ if (dv.formula2 !== void 0) dv.formula2 = safe(rewrite2, dv.formula2, name);
30918
31569
  }
30919
31570
  for (const link of sheet.hyperlinks)
30920
31571
  if (link.location && /![A-Za-z$]/.test(link.location))
30921
- link.location = safe(rewrite3, link.location, name);
31572
+ link.location = safe(rewrite2, link.location, name);
30922
31573
  for (const drawing of sheet.drawings) {
30923
31574
  if (drawing.kind !== "chart") continue;
30924
31575
  for (const series of drawing.series) {
30925
- if (series.nameRef) series.nameRef = safe(rewrite3, series.nameRef, name);
30926
- if (series.categoriesRef) series.categoriesRef = safe(rewrite3, series.categoriesRef, name);
30927
- if (series.valuesRef) series.valuesRef = safe(rewrite3, series.valuesRef, name);
31576
+ if (series.nameRef) series.nameRef = safe(rewrite2, series.nameRef, name);
31577
+ if (series.categoriesRef) series.categoriesRef = safe(rewrite2, series.categoriesRef, name);
31578
+ if (series.valuesRef) series.valuesRef = safe(rewrite2, series.valuesRef, name);
30928
31579
  }
30929
31580
  }
30930
31581
  }
@@ -31111,7 +31762,7 @@ function shiftAxis(ctx, s, axis, at2, count2, insert) {
31111
31762
  applyAxisShift(ctx, sheet, shift2);
31112
31763
  if (insert) inheritFormats(sheet, axis, at2, count2);
31113
31764
  },
31114
- { sheet: s, structural: true }
31765
+ { sheet: s, structural: true, calc: { kind: "shift", sheet: s, shift: shift2 } }
31115
31766
  );
31116
31767
  }
31117
31768
  var insertRows = (ctx, s, at2, count2) => shiftAxis(ctx, s, "row", at2, count2, true);
@@ -32925,6 +33576,14 @@ function setCalcMode(ctx, mode) {
32925
33576
 
32926
33577
  // xlsx/edit/session-calc.ts
32927
33578
  var isStructural = (step) => step.structural || step.entries.some((entry3) => entry3.before.kind !== "cells");
33579
+ function hintFor(step, mode) {
33580
+ const hint = step.calc;
33581
+ if (!hint) return void 0;
33582
+ if (hint.kind === "rename")
33583
+ return mode === "undo" ? { kind: "rename", from: hint.to, to: hint.from } : hint;
33584
+ return mode === "undo" ? void 0 : hint;
33585
+ }
33586
+ var settingsOf = (workbook) => `${workbook.iterate ? `${workbook.iterate.count}:${workbook.iterate.delta}` : ""}|${workbook.date1904}`;
32928
33587
  var SessionCalculator = class {
32929
33588
  constructor(workbook, calc, enabled2) {
32930
33589
  this.workbook = workbook;
@@ -32934,43 +33593,113 @@ var SessionCalculator = class {
32934
33593
  workbook;
32935
33594
  calc;
32936
33595
  enabled;
32937
- /** Structural edits made in manual mode leave the dependency graph stale until a full pass. */
33596
+ /** Structural edits the engine could not follow leave the dependency graph stale (manual mode). */
32938
33597
  staleGraph = false;
33598
+ /** Cells changed in manual mode since the last calculation. */
33599
+ dirty = [];
33600
+ /** The settings at the last calculation; undefined before the first. */
33601
+ calculated;
32939
33602
  /** Recalculates after an applied, undone or redone step (failures become warnings). */
32940
- afterStep(step) {
32941
- if (!this.enabled) return;
33603
+ afterStep(step, mode = "apply") {
33604
+ if (!this.enabled) {
33605
+ this.calc.discardPreparation();
33606
+ return;
33607
+ }
33608
+ const structural = isStructural(step);
33609
+ const hint = structural ? hintFor(step, mode) : void 0;
32942
33610
  if (calcModeOf(this.workbook) === "manual") {
32943
- if (isStructural(step)) this.staleGraph = true;
33611
+ if (hint && !this.staleGraph && !this.dirty.length) {
33612
+ this.guard(() => this.follow(hint));
33613
+ return;
33614
+ }
33615
+ this.calc.discardPreparation();
33616
+ if (structural) this.staleGraph = true;
33617
+ else this.dirty.push(...touchedCells(step));
32944
33618
  return;
32945
33619
  }
32946
- this.recalc(isStructural(step), () => touchedCells(step));
33620
+ if (hint && !this.staleGraph) {
33621
+ this.guard(() => {
33622
+ this.follow(hint);
33623
+ this.calc.recalculateFrom(touchedCells(step));
33624
+ this.calculated = settingsOf(this.workbook);
33625
+ });
33626
+ return;
33627
+ }
33628
+ this.recalc(structural, () => touchedCells(step));
32947
33629
  }
32948
33630
  /** Recalculates after a change applied outside the history (a collaborator's edit). */
32949
33631
  afterExternal(structural, cells3) {
32950
- if (!this.enabled) return;
32951
- if (calcModeOf(this.workbook) === "manual") {
33632
+ if (!this.enabled || calcModeOf(this.workbook) === "manual") {
33633
+ this.calc.discardPreparation();
33634
+ if (!this.enabled) return;
32952
33635
  if (structural) this.staleGraph = true;
33636
+ else this.dirty.push(...cells3);
32953
33637
  return;
32954
33638
  }
32955
33639
  this.recalc(structural, () => cells3);
32956
33640
  }
32957
- recalc(structural, cells3) {
33641
+ follow(hint) {
33642
+ if (hint.kind === "shift") this.calc.shiftCells(hint.sheet, hint.shift);
33643
+ else this.calc.renameSheet(hint.from, hint.to);
33644
+ }
33645
+ guard(run2) {
32958
33646
  try {
33647
+ run2();
33648
+ } catch (error) {
33649
+ const message = `Recalculation failed: ${error instanceof Error ? error.message : String(error)}`;
33650
+ if (!this.workbook.warnings.includes(message)) this.workbook.warnings.push(message);
33651
+ }
33652
+ }
33653
+ /** Builds the dependency graph ahead of the first edit (see `EditSession.prepareCalculation`). */
33654
+ prepare(timeRemaining) {
33655
+ if (!this.enabled || calcModeOf(this.workbook) === "manual" || this.staleGraph) return true;
33656
+ try {
33657
+ return this.calc.prepare(timeRemaining);
33658
+ } catch {
33659
+ this.calc.discardPreparation();
33660
+ return true;
33661
+ }
33662
+ }
33663
+ /**
33664
+ * Before a structural edit the graph can follow in place: finishes a graph being prepared (or
33665
+ * builds it) while it still matches the workbook, so the edit moves it instead of rebuilding.
33666
+ */
33667
+ beforeStructural() {
33668
+ if (!this.enabled || calcModeOf(this.workbook) === "manual" || this.staleGraph) return;
33669
+ this.prepare();
33670
+ }
33671
+ recalc(structural, cells3) {
33672
+ this.guard(() => {
32959
33673
  if (this.staleGraph || structural) this.full();
32960
33674
  else {
32961
33675
  const changed = cells3();
32962
33676
  if (changed.length) this.calc.recalculateFrom(changed);
33677
+ this.calculated = settingsOf(this.workbook);
32963
33678
  }
32964
- } catch (error) {
32965
- const message = `Recalculation failed: ${error instanceof Error ? error.message : String(error)}`;
32966
- if (!this.workbook.warnings.includes(message)) this.workbook.warnings.push(message);
32967
- }
33679
+ });
32968
33680
  }
32969
- /** Rebuilds the dependency graph and recalculates every formula (F9). */
33681
+ /** Rebuilds the dependency graph and recalculates every formula (Ctrl+Alt+F9). */
32970
33682
  full() {
32971
33683
  this.staleGraph = false;
33684
+ this.dirty = [];
32972
33685
  this.calc.invalidate();
32973
33686
  this.calc.recalculateAll();
33687
+ this.calculated = settingsOf(this.workbook);
33688
+ }
33689
+ /**
33690
+ * Calculate Now (F9): the cells changed since the last calculation, the formulas structural
33691
+ * edits queued, their dependents and the volatile functions. Everything when `full`, before
33692
+ * the session's first calculation, after the iteration settings changed or while the graph
33693
+ * is stale.
33694
+ */
33695
+ now(full) {
33696
+ if (full || this.staleGraph || this.calculated !== settingsOf(this.workbook)) {
33697
+ this.full();
33698
+ return;
33699
+ }
33700
+ const changed = this.dirty;
33701
+ this.dirty = [];
33702
+ this.calc.recalculateFrom(changed);
32974
33703
  }
32975
33704
  /**
32976
33705
  * Recalculates one sheet's formulas and their dependents (Shift+F9); after structural edits
@@ -36774,12 +37503,12 @@ function renameSheet(ctx, index2, name) {
36774
37503
  ctx.run(
36775
37504
  "Rename sheet",
36776
37505
  "sheets",
36777
- [{ kind: "workbook" }],
37506
+ [{ kind: "refs" }],
36778
37507
  () => {
36779
37508
  rewriteFormulas(workbook, (formula) => renameSheetInFormula(formula, old, name));
36780
37509
  sheet.name = name;
36781
37510
  },
36782
- { sheet: index2, structural: true }
37511
+ { sheet: index2, structural: true, calc: { kind: "rename", from: old, to: name } }
36783
37512
  );
36784
37513
  }
36785
37514
  function moveSheet(ctx, from, to) {
@@ -37155,13 +37884,12 @@ function createEditSession(workbook, options = {}) {
37155
37884
  for (const listener of [...listeners]) listener(change);
37156
37885
  };
37157
37886
  const calculator = new SessionCalculator(workbook, calc, autoRecalc);
37158
- const recalc = (step) => calculator.afterStep(step);
37159
- const begin = (label2, kind, info) => ({
37160
- step: { label: label2, entries: [], structural: info.structural ?? false },
37161
- kind,
37162
- sheet: info.sheet,
37163
- ranges: [...info.ranges ?? []]
37164
- });
37887
+ const recalc = (step, mode = "apply") => calculator.afterStep(step, mode);
37888
+ const begin = (label2, kind, info) => {
37889
+ const step = { label: label2, entries: [], structural: info.structural ?? false };
37890
+ if (info.calc) step.calc = info.calc;
37891
+ return { step, kind, sheet: info.sheet, ranges: [...info.ranges ?? []] };
37892
+ };
37165
37893
  const finish = (current) => {
37166
37894
  if (!current.step.entries.length) return;
37167
37895
  history.push(current.step);
@@ -37186,10 +37914,12 @@ function createEditSession(workbook, options = {}) {
37186
37914
  const outer = !open;
37187
37915
  const current = open ?? begin(label2, kind, info);
37188
37916
  if (!outer) {
37917
+ delete current.step.calc;
37189
37918
  if (info.structural) current.step.structural = true;
37190
37919
  if (current.sheet === void 0 && info.sheet !== void 0) current.sheet = info.sheet;
37191
37920
  current.ranges.push(...info.ranges ?? []);
37192
37921
  }
37922
+ if (outer && info.calc) calculator.beforeStructural();
37193
37923
  open = current;
37194
37924
  const mark = current.step.entries.length;
37195
37925
  const befores = scopes.map((scope) => captureScope(workbook, scope));
@@ -37237,7 +37967,7 @@ function createEditSession(workbook, options = {}) {
37237
37967
  if (open) throw new Error(`Cannot ${kind} inside a batch`);
37238
37968
  const step = kind === "undo" ? history.undo(workbook) : history.redo(workbook);
37239
37969
  if (!step) return false;
37240
- recalc(step);
37970
+ recalc(step, kind);
37241
37971
  emit({ kind, label: step.label, structural: step.structural });
37242
37972
  return true;
37243
37973
  };
@@ -37316,10 +38046,11 @@ function createEditSession(workbook, options = {}) {
37316
38046
  setDocumentProperties: (patch) => setDocumentProperties(ctx, patch),
37317
38047
  setAutoRecalc: (enabled2) => setCalcMode(ctx, enabled2 ? "auto" : "manual"),
37318
38048
  autoRecalc: () => autoRecalc && calcModeOf(workbook) === "auto",
37319
- calculateNow() {
37320
- calculator.full();
38049
+ calculateNow(options2) {
38050
+ calculator.now(options2?.full ?? false);
37321
38051
  emit({ kind: "cells", label: "Calculate now", structural: false });
37322
38052
  },
38053
+ prepareCalculation: (opts) => calculator.prepare(opts?.timeRemaining),
37323
38054
  calculateSheet(s) {
37324
38055
  calculator.sheet(s);
37325
38056
  emit({ kind: "cells", label: "Calculate sheet", sheet: s, structural: false });
@@ -45907,11 +46638,11 @@ async function assertVisioMasterIndependence(pkg, roots, commands, check3) {
45907
46638
  }
45908
46639
  const tops = children3(children3(root, "Shapes")[0], "Shape");
45909
46640
  if (tops.length !== 1) problem("Only a uniquely rooted master can prove independence.");
45910
- const templates = masterShapes(root), context = /* @__PURE__ */ new Map();
46641
+ const templates2 = masterShapes(root), context = /* @__PURE__ */ new Map();
45911
46642
  assertMasterMarkup(
45912
46643
  root,
45913
46644
  new Set(
45914
- templates.flatMap((shape2) => {
46645
+ templates2.flatMap((shape2) => {
45915
46646
  charge();
45916
46647
  return [...masterCells(shape2, charge).values()];
45917
46648
  })
@@ -45933,7 +46664,7 @@ async function assertVisioMasterIndependence(pkg, roots, commands, check3) {
45933
46664
  if (byTemplate.has(id)) problem("MasterShape mappings are ambiguous.");
45934
46665
  byTemplate.set(id, local);
45935
46666
  }
45936
- for (const template of templates) {
46667
+ for (const template of templates2) {
45937
46668
  charge();
45938
46669
  if (bindings.length >= 1e4) problem("Master effective binding limit exceeded.");
45939
46670
  check3();