ooxml-core 0.11.0 → 0.12.0

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.
@@ -20592,8 +20592,9 @@ var AreaIndex = class {
20592
20592
  wide = /* @__PURE__ */ new Map();
20593
20593
  add(area, node) {
20594
20594
  const { start, end } = area.range;
20595
- if (start.row === end.row && start.col === end.col) {
20596
- push2(inner(this.cells, area.sheet), cellKey(start.row, start.col), node);
20595
+ if (start.row === end.row && end.col - start.col + 1 <= BUCKET_WIDTH) {
20596
+ const cells3 = inner(this.cells, area.sheet);
20597
+ for (let col = start.col; col <= end.col; col++) push2(cells3, cellKey(start.row, col), node);
20597
20598
  return;
20598
20599
  }
20599
20600
  const entry = { range: area.range, node };
@@ -20819,7 +20820,8 @@ function writeFootprint(sheet, footprint, m) {
20819
20820
  }
20820
20821
  if (cell) {
20821
20822
  cell.value = value;
20822
- cell.spillAnchor = { row, col };
20823
+ if (!isSpilledCell(cell) || cell.spillAnchor.row !== row || cell.spillAnchor.col !== col)
20824
+ cell.spillAnchor = { row, col };
20823
20825
  } else {
20824
20826
  const spilled = { value, spillAnchor: { row, col } };
20825
20827
  putCell(sheet, r, c2, spilled);
@@ -20866,6 +20868,7 @@ var EngineHost = class {
20866
20868
  columnIndex;
20867
20869
  reverseIndex;
20868
20870
  footprintIndex;
20871
+ fullOrder;
20869
20872
  /** Range reads cached for one evaluation pass (see `readBlock`). */
20870
20873
  blockCache = /* @__PURE__ */ new Map();
20871
20874
  /** Counts reads that hit a formula still being evaluated (a circular reference). */
@@ -21038,6 +21041,7 @@ var EngineHost = class {
21038
21041
  if (this.footprints.delete(node)) this.footprintIndex = void 0;
21039
21042
  }
21040
21043
  graphChanged() {
21044
+ this.fullOrder = void 0;
21041
21045
  this.columnIndex = void 0;
21042
21046
  this.reverseIndex = void 0;
21043
21047
  this.footprintIndex = void 0;
@@ -21068,52 +21072,6 @@ var EngineHost = class {
21068
21072
  }
21069
21073
  };
21070
21074
 
21071
- // src/xlsx/formula/engine-types.ts
21072
- var CIRCULAR_REFERENCE_WARNING = "The workbook contains circular references; the cells involved show 0.";
21073
-
21074
- // src/xlsx/formula/engine-util.ts
21075
- function finalize(value, host) {
21076
- let result2;
21077
- if (value instanceof RefValue) {
21078
- if (value.areas.length !== 1) return ERR.VALUE;
21079
- if (value.isCell()) {
21080
- const area = value.areas[0];
21081
- result2 = host.readCell(area.sheet, area.range.start.row, area.range.start.col);
21082
- } else result2 = toMatrix(host, value);
21083
- } else if (value instanceof LambdaValue) return ERR.CALC;
21084
- else result2 = value;
21085
- if (result2 instanceof Matrix) {
21086
- if (result2.rows === 1 && result2.cols === 1) result2 = result2.get(0, 0);
21087
- else return result2.block().mapValues(clean);
21088
- }
21089
- return clean(result2);
21090
- }
21091
- function clean(value) {
21092
- if (value === null) return 0;
21093
- if (typeof value === "number") {
21094
- if (!Number.isFinite(value)) return ERR.NUM;
21095
- return value === 0 ? 0 : value;
21096
- }
21097
- return value;
21098
- }
21099
- var rangeHas = (range, row, col) => row >= range.start.row && row <= range.end.row && col >= range.start.col && col <= range.end.col;
21100
- function sameRange(a, b) {
21101
- if (!a || !b) return a === b || !a && !b;
21102
- return a.start.row === b.start.row && a.start.col === b.start.col && a.end.row === b.end.row && a.end.col === b.end.col;
21103
- }
21104
- function boundingBox(ranges) {
21105
- const first4 = ranges[0];
21106
- const start = { ...first4.start };
21107
- const end = { ...first4.end };
21108
- for (const r of ranges) {
21109
- start.row = Math.min(start.row, r.start.row);
21110
- start.col = Math.min(start.col, r.start.col);
21111
- end.row = Math.max(end.row, r.end.row);
21112
- end.col = Math.max(end.col, r.end.col);
21113
- }
21114
- return { start, end };
21115
- }
21116
-
21117
21075
  // src/xlsx/formula/scc.ts
21118
21076
  function stronglyConnected(nodes, successors) {
21119
21077
  const index2 = /* @__PURE__ */ new Map();
@@ -21166,6 +21124,63 @@ function stronglyConnected(nodes, successors) {
21166
21124
  return out;
21167
21125
  }
21168
21126
 
21127
+ // src/xlsx/formula/engine-order.ts
21128
+ function formulaOrder(nodes, successors) {
21129
+ const ranks = /* @__PURE__ */ new Map();
21130
+ const components = stronglyConnected(nodes, successors).map((members, rank4) => {
21131
+ for (const member of members) ranks.set(member, rank4);
21132
+ const first4 = members[0];
21133
+ return { nodes: members, cyclic: members.length > 1 || successors(first4).includes(first4) };
21134
+ });
21135
+ return { components, ranks };
21136
+ }
21137
+
21138
+ // src/xlsx/formula/engine-types.ts
21139
+ var CIRCULAR_REFERENCE_WARNING = "The workbook contains circular references; the cells involved show 0.";
21140
+
21141
+ // src/xlsx/formula/engine-util.ts
21142
+ function finalize(value, host) {
21143
+ let result2;
21144
+ if (value instanceof RefValue) {
21145
+ if (value.areas.length !== 1) return ERR.VALUE;
21146
+ if (value.isCell()) {
21147
+ const area = value.areas[0];
21148
+ result2 = host.readCell(area.sheet, area.range.start.row, area.range.start.col);
21149
+ } else result2 = toMatrix(host, value);
21150
+ } else if (value instanceof LambdaValue) return ERR.CALC;
21151
+ else result2 = value;
21152
+ if (result2 instanceof Matrix) {
21153
+ if (result2.rows === 1 && result2.cols === 1) result2 = result2.get(0, 0);
21154
+ else return result2.block().mapValues(clean);
21155
+ }
21156
+ return clean(result2);
21157
+ }
21158
+ function clean(value) {
21159
+ if (value === null) return 0;
21160
+ if (typeof value === "number") {
21161
+ if (!Number.isFinite(value)) return ERR.NUM;
21162
+ return value === 0 ? 0 : value;
21163
+ }
21164
+ return value;
21165
+ }
21166
+ var rangeHas = (range, row, col) => row >= range.start.row && row <= range.end.row && col >= range.start.col && col <= range.end.col;
21167
+ function sameRange(a, b) {
21168
+ if (!a || !b) return a === b || !a && !b;
21169
+ return a.start.row === b.start.row && a.start.col === b.start.col && a.end.row === b.end.row && a.end.col === b.end.col;
21170
+ }
21171
+ function boundingBox(ranges) {
21172
+ const first4 = ranges[0];
21173
+ const start = { ...first4.start };
21174
+ const end = { ...first4.end };
21175
+ for (const r of ranges) {
21176
+ start.row = Math.min(start.row, r.start.row);
21177
+ start.col = Math.min(start.col, r.start.col);
21178
+ end.row = Math.max(end.row, r.end.row);
21179
+ end.col = Math.max(end.col, r.end.col);
21180
+ }
21181
+ return { start, end };
21182
+ }
21183
+
21169
21184
  // src/xlsx/formula/store.ts
21170
21185
  function storeResult(sheet, node, cell, result2, claims) {
21171
21186
  const { row, col } = node;
@@ -21185,9 +21200,9 @@ function storeResult(sheet, node, cell, result2, claims) {
21185
21200
  return;
21186
21201
  }
21187
21202
  const footprint = footprintFor(row, col, result2);
21188
- if (node.spill) clearFootprint(sheet, node.spill, row, col);
21189
- delete node.spill;
21190
21203
  if (!footprint || !footprintFree(sheet, footprint, row, col, claims)) {
21204
+ if (node.spill) clearFootprint(sheet, node.spill, row, col);
21205
+ delete node.spill;
21191
21206
  node.blockedSpill = footprint ?? {
21192
21207
  start: { row, col },
21193
21208
  end: {
@@ -21198,6 +21213,7 @@ function storeResult(sheet, node, cell, result2, claims) {
21198
21213
  cell.value = ERR.SPILL;
21199
21214
  return;
21200
21215
  }
21216
+ if (node.spill && !sameRange(node.spill, footprint)) clearFootprint(sheet, node.spill, row, col);
21201
21217
  delete node.blockedSpill;
21202
21218
  writeFootprint(sheet, footprint, result2);
21203
21219
  node.spill = footprint;
@@ -21220,7 +21236,7 @@ var EngineCore = class extends EngineHost {
21220
21236
  const dirty = everything && round2 === 0 ? current : this.closure(current);
21221
21237
  this.footprintChanges = [];
21222
21238
  this.evaluatedAt = /* @__PURE__ */ new Map();
21223
- this.evaluateSet(dirty);
21239
+ this.evaluateSet(dirty, everything && round2 === 0);
21224
21240
  if (this.footprintChanges.length === 0) break;
21225
21241
  const next = /* @__PURE__ */ new Set();
21226
21242
  const reverse2 = this.reverse();
@@ -21262,7 +21278,7 @@ var EngineCore = class extends EngineHost {
21262
21278
  }
21263
21279
  return out;
21264
21280
  }
21265
- evaluateSet(dirty) {
21281
+ evaluateSet(dirty, full) {
21266
21282
  this.boundsCache.clear();
21267
21283
  this.blockCache.clear();
21268
21284
  const columns = this.columns();
@@ -21272,24 +21288,25 @@ var EngineCore = class extends EngineHost {
21272
21288
  let out = succCache.get(node);
21273
21289
  if (out) return out;
21274
21290
  const found = [];
21291
+ const anchors = /* @__PURE__ */ new Set();
21275
21292
  for (const dep of node.deps) {
21276
21293
  columns.nodesIn(dep, found);
21277
- const anchors = [];
21278
21294
  footprints.dependents(dep.sheet, dep.range, anchors);
21279
- for (const anchor of anchors) if (anchor.spill ?? anchor.arrayRange) found.push(anchor);
21280
21295
  }
21281
- out = [...new Set(found)].filter((n) => dirty.has(n));
21296
+ for (const anchor of anchors) if (anchor.spill ?? anchor.arrayRange) found.push(anchor);
21297
+ out = found.length ? [...new Set(found)].filter((n) => dirty.has(n)) : [];
21282
21298
  succCache.set(node, out);
21283
21299
  return out;
21284
21300
  };
21285
- const order = stronglyConnected(dirty, successors);
21301
+ const order = full && this.fullOrder || formulaOrder(dirty, successors);
21302
+ if (full) this.fullOrder = order;
21286
21303
  this.pending = new Set(dirty);
21287
21304
  this.dynamicCycleHits = /* @__PURE__ */ new Set();
21288
21305
  try {
21289
- for (const component of order) {
21290
- const first4 = component[0];
21291
- if (component.length > 1 || successors(first4).includes(first4)) {
21292
- for (const node of component) this.markCycle(node);
21306
+ for (const component of order.components) {
21307
+ const first4 = component.nodes[0];
21308
+ if (component.cyclic) {
21309
+ for (const node of component.nodes) this.markCycle(node);
21293
21310
  } else if (this.pending.has(first4)) {
21294
21311
  this.computeFrom(first4);
21295
21312
  }
@@ -21368,6 +21385,7 @@ var EngineCore = class extends EngineHost {
21368
21385
  return !!other && other !== node && !!other.spill && rangeHas(other.spill, r, c2);
21369
21386
  });
21370
21387
  if (!sameRange(before, node.spill) || !sameRange(blockedBefore, node.blockedSpill)) {
21388
+ this.updateOrder(node, before, blockedBefore);
21371
21389
  this.footprintIndex = void 0;
21372
21390
  this.blockCache.clear();
21373
21391
  }
@@ -21387,10 +21405,30 @@ var EngineCore = class extends EngineHost {
21387
21405
  }
21388
21406
  }
21389
21407
  }
21408
+ /** New spill edges may reuse the order only when every reader already follows its anchor. */
21409
+ updateOrder(node, before, blockedBefore) {
21410
+ const order = this.fullOrder;
21411
+ if (!order) return;
21412
+ if (before || blockedBefore || !node.spill || node.blockedSpill) {
21413
+ this.fullOrder = void 0;
21414
+ return;
21415
+ }
21416
+ const rank4 = order.ranks.get(node);
21417
+ if (rank4 === void 0) {
21418
+ this.fullOrder = void 0;
21419
+ return;
21420
+ }
21421
+ const readers = /* @__PURE__ */ new Set();
21422
+ this.reverse().dependents(node.sheet, node.spill, readers);
21423
+ for (const reader of readers) {
21424
+ if ((order.ranks.get(reader) ?? -1) <= rank4) {
21425
+ this.fullOrder = void 0;
21426
+ return;
21427
+ }
21428
+ }
21429
+ }
21390
21430
  frameFor(sheet, row, col, root, legacy = false) {
21391
- const frame = { host: this, sheet, row, col, scope: void 0, depth: 0 };
21392
- if (legacy) Object.assign(frame, { legacy: true });
21393
- return root ? { ...frame, root } : frame;
21431
+ return { host: this, sheet, row, col, scope: void 0, depth: 0, root, legacy };
21394
21432
  }
21395
21433
  evaluateFormula(node) {
21396
21434
  if (!node.ast) return node.parseError ?? ERR.NAME;
@@ -35669,7 +35707,7 @@ function setCell(shape2, name, value, formula) {
35669
35707
  if (formula) node.setAttribute("F", formula);
35670
35708
  else if (attribute2(node, "F") !== "No Formula") node.removeAttribute("F");
35671
35709
  }
35672
- function admitted(root, shapeId, masterMovePins = /* @__PURE__ */ new Set()) {
35710
+ function admitted(root, shapeId, masterMovePins = /* @__PURE__ */ new Set(), masterDimensions = /* @__PURE__ */ new Map()) {
35673
35711
  const containers = children4(root, "Shapes");
35674
35712
  if (containers.length !== 1)
35675
35713
  fail2("UNSUPPORTED_GEOMETRY_EDIT", "One local Shapes container is required.");
@@ -35698,12 +35736,17 @@ function admitted(root, shapeId, masterMovePins = /* @__PURE__ */ new Set()) {
35698
35736
  "UNSUPPORTED_GEOMETRY_EDIT",
35699
35737
  "Only local 2D shapes are admitted; line routing and glue are unsupported."
35700
35738
  );
35701
- if (!(numeric4(local.get("Width")) > 0) || !(numeric4(local.get("Height")) > 0))
35702
- fail2("UNSUPPORTED_GEOMETRY_EDIT", "Positive local Width and Height caches are required.");
35739
+ const proven = masterDimensions.get(shape2);
35740
+ if (!(numeric4(local.get("Width"), proven?.width) > 0) || !(numeric4(local.get("Height"), proven?.height) > 0))
35741
+ fail2("UNSUPPORTED_GEOMETRY_EDIT", "Positive proven Width and Height caches are required.");
35703
35742
  return shape2;
35704
35743
  }
35705
- function protectedShape(shape2, document) {
35744
+ function protectedShape(shape2, document, masterMovePins = /* @__PURE__ */ new Set()) {
35706
35745
  const local = cells(shape2);
35746
+ const certifiedMove = shape2.hasAttribute("Master") && ["PinX", "PinY"].every((name) => {
35747
+ const node = local.get(name);
35748
+ return node !== void 0 && masterMovePins.has(node);
35749
+ });
35707
35750
  const styles = /* @__PURE__ */ new Map();
35708
35751
  for (const container of children4(document, "StyleSheets"))
35709
35752
  for (const node of children4(container, "StyleSheet")) {
@@ -35711,9 +35754,13 @@ function protectedShape(shape2, document) {
35711
35754
  if (!id || styles.has(id)) fail2("EDIT_PROTECTED_CELL", "Ambiguous protection style IDs.");
35712
35755
  styles.set(id, node);
35713
35756
  }
35714
- const inactive = (cell) => {
35757
+ const validateProtection = (cell) => {
35715
35758
  try {
35716
- if (cell.hasAttribute("E") || visioFormulaCachedValue(attribute2(cell, "V") ?? "", attribute2(cell, "U")).unit !== "scalar" || numeric4(cell) !== 0)
35759
+ const cached3 = numeric4(cell);
35760
+ const operationIndependent = certifiedMove && ["LockWidth", "LockHeight", "LockAspect", "LockDelete"].includes(
35761
+ attribute2(cell, "N") ?? ""
35762
+ );
35763
+ if (cell.hasAttribute("E") || visioFormulaCachedValue(attribute2(cell, "V") ?? "", attribute2(cell, "U")).unit !== "scalar" || !(cached3 === 0 || operationIndependent && cached3 === 1))
35717
35764
  fail2("EDIT_PROTECTED_CELL", "Inherited protection is active or invalid.");
35718
35765
  const source2 = executableCellFormula(attribute2(cell, "F"));
35719
35766
  if (!source2) return;
@@ -35724,12 +35771,12 @@ function protectedShape(shape2, document) {
35724
35771
  source2,
35725
35772
  () => fail2("EDIT_PROTECTED_CELL", "Inherited protection has dependencies.")
35726
35773
  );
35727
- if (result2.unit !== "scalar" || result2.value !== 0)
35728
- fail2("EDIT_PROTECTED_CELL", "Inherited protection formula is active or stale.");
35774
+ if (result2.unit !== "scalar" || result2.value !== cached3)
35775
+ fail2("EDIT_PROTECTED_CELL", "Inherited protection formula cache is stale.");
35729
35776
  } catch (error) {
35730
35777
  fail2(
35731
35778
  "EDIT_PROTECTED_CELL",
35732
- `Inherited protection cannot be proven inactive: ${error instanceof Error ? error.message : "invalid cache"}`
35779
+ `Inherited protection cannot be proven safe for this operation: ${error instanceof Error ? error.message : "invalid cache"}`
35733
35780
  );
35734
35781
  }
35735
35782
  };
@@ -35741,16 +35788,18 @@ function protectedShape(shape2, document) {
35741
35788
  if (!style) fail2("EDIT_PROTECTED_CELL", "Protection style ancestry cannot be resolved.");
35742
35789
  const cell = cells(style).get(lock), parent = attribute2(style, category);
35743
35790
  if (cell && attribute2(cell, "F") !== "Inh") {
35744
- inactive(cell);
35791
+ validateProtection(cell);
35745
35792
  return cell;
35746
35793
  }
35747
35794
  if (cell) {
35748
- inactive(cell);
35795
+ validateProtection(cell);
35749
35796
  if (parent === void 0 || parent === id)
35750
35797
  fail2("EDIT_PROTECTED_CELL", "Inherited protection has no provable parent.");
35751
35798
  const inherited = resolve(parent, category, lock, seen);
35752
35799
  if (!inherited)
35753
- fail2("EDIT_PROTECTED_CELL", "Inherited protection has no explicit inactive ancestor.");
35800
+ fail2("EDIT_PROTECTED_CELL", "Inherited protection has no explicit provable ancestor.");
35801
+ if (numeric4(cell) !== numeric4(inherited))
35802
+ fail2("EDIT_PROTECTED_CELL", "Delegated protection cache disagrees with its ancestor.");
35754
35803
  return inherited;
35755
35804
  }
35756
35805
  return parent !== void 0 && parent !== id ? resolve(parent, category, lock, seen) : void 0;
@@ -35827,6 +35876,130 @@ function resizeGeometry(shape2, roots, edit, check3) {
35827
35876
  }
35828
35877
  }
35829
35878
 
35879
+ // src/visio/edit-master-move.ts
35880
+ var protection = [
35881
+ "LockMoveX",
35882
+ "LockMoveY",
35883
+ "LockWidth",
35884
+ "LockHeight",
35885
+ "LockAspect",
35886
+ "LockDelete"
35887
+ ];
35888
+ var dimensions2 = ["PinX", "PinY", "Width", "Height", "LocPinX", "LocPinY"];
35889
+ var emptyMasterMoveProof = () => ({
35890
+ pins: /* @__PURE__ */ new Set(),
35891
+ dimensions: /* @__PURE__ */ new Map()
35892
+ });
35893
+ function prepareMasterMovePins(roots, commands, bindings, check3) {
35894
+ const result2 = /* @__PURE__ */ new Set();
35895
+ const provenDimensions = /* @__PURE__ */ new Map();
35896
+ let work = 1e5;
35897
+ const charge = (amount = 1) => {
35898
+ check3();
35899
+ work -= amount;
35900
+ if (work < 0)
35901
+ fail2("EDIT_UNSUPPORTED_PACKAGE_DEPENDENCY", "Master move preparation budget exceeded.");
35902
+ };
35903
+ const analyze2 = (formula) => {
35904
+ charge(formula.length);
35905
+ return analyzeVisioFormula(formula);
35906
+ };
35907
+ for (const command2 of commands) {
35908
+ charge();
35909
+ const root = roots.get(command2.pageId);
35910
+ const instance = children4(children4(root, "Shapes")[0], "Shape").find(
35911
+ (shape2) => attribute2(shape2, "ID") === command2.shapeId
35912
+ );
35913
+ if (!instance?.hasAttribute("Master")) continue;
35914
+ if (command2.type !== "move-shape")
35915
+ fail2("UNSUPPORTED_GEOMETRY_EDIT", "Master-linked resizing and deletion remain unsupported.");
35916
+ const binding = bindings.find((candidate) => candidate.instance === instance);
35917
+ if (!binding) fail2("UNSUPPORTED_GEOMETRY_EDIT", "A unique effective master root is required.");
35918
+ for (const node of [instance, binding.template]) {
35919
+ charge();
35920
+ if (attribute2(node, "Type") !== "Shape" || children4(node, "Shapes").length || children4(node, "ForeignData").length || children4(node, "Rel").length || ["1", "true"].includes(attribute2(node, "Del") ?? ""))
35921
+ fail2("UNSUPPORTED_GEOMETRY_EDIT", "Master moves require top-level local 2D shape roots.");
35922
+ }
35923
+ for (const name of ["BeginX", "BeginY", "EndX", "EndY"])
35924
+ if (binding.cells.has(name.toLowerCase()))
35925
+ fail2("UNSUPPORTED_GEOMETRY_EDIT", "Master lines and endpoint routing remain unsupported.");
35926
+ const oneD = binding.cells.get("oned")?.node;
35927
+ charge();
35928
+ if (oneD && (numeric4(oneD) !== 0 || visioFormulaCachedValue(attribute2(oneD, "V") ?? "", attribute2(oneD, "U")).unit !== "scalar" || executableCellFormula(attribute2(oneD, "F"))))
35929
+ fail2("UNSUPPORTED_GEOMETRY_EDIT", "A static effective 2D master is required.");
35930
+ const local = cells(instance);
35931
+ for (const name of dimensions2) {
35932
+ const effective = binding.cells.get(name.toLowerCase())?.node;
35933
+ for (const node of /* @__PURE__ */ new Set([effective, ...local.has(name) ? [local.get(name)] : []])) {
35934
+ charge();
35935
+ numeric4(node);
35936
+ if (visioFormulaCachedValue("0", attribute2(node, "U") ?? "DL").unit !== "length")
35937
+ fail2("EDIT_FORMULA_UNIT", "Master transform caches require length units.");
35938
+ }
35939
+ }
35940
+ const dimension = (name) => numeric4(local.get(name) ?? binding.cells.get(name.toLowerCase())?.node);
35941
+ const width = dimension("Width"), height = dimension("Height");
35942
+ if (width <= 0 || height <= 0)
35943
+ fail2("UNSUPPORTED_GEOMETRY_EDIT", "Positive proven effective dimensions are required.");
35944
+ provenDimensions.set(instance, { width, height });
35945
+ for (const name of protection) {
35946
+ charge();
35947
+ const node = binding.cells.get(name.toLowerCase())?.node;
35948
+ const cached3 = node ? numeric4(node) : NaN;
35949
+ const moving = name === "LockMoveX" || name === "LockMoveY";
35950
+ if (!node || visioFormulaCachedValue(attribute2(node, "V") ?? "", attribute2(node, "U")).unit !== "scalar" || ![0, 1].includes(cached3) || moving && cached3 !== 0)
35951
+ fail2(
35952
+ "EDIT_PROTECTED_CELL",
35953
+ "Effective master protection must be proven boolean with inactive movement locks."
35954
+ );
35955
+ const formula = executableCellFormula(attribute2(node, "F"));
35956
+ if (formula) {
35957
+ const analysis = analyze2(formula);
35958
+ if (analysis.dynamic || analysis.references.length || analysis.unsupportedFunctions.length)
35959
+ fail2("EDIT_PROTECTED_CELL", "Effective master protection dependencies are unsupported.");
35960
+ const value = evaluateVisioFormula(
35961
+ formula,
35962
+ () => fail2("EDIT_PROTECTED_CELL", "Unexpected protection dependency."),
35963
+ { onStep: () => charge() }
35964
+ );
35965
+ if (value.unit !== "scalar" || value.value !== cached3)
35966
+ fail2("EDIT_PROTECTED_CELL", "Effective master protection cache is stale.");
35967
+ }
35968
+ }
35969
+ for (const name of ["PinX", "PinY"]) {
35970
+ charge();
35971
+ const node = local.get(name);
35972
+ if (!node)
35973
+ fail2("EDIT_PROTECTED_CELL", "The rotation pin must be an explicit local override.");
35974
+ if (binding.cells.get(name.toLowerCase())?.node !== node)
35975
+ fail2("EDIT_PROTECTED_CELL", "The rotation pin must be an explicit local override.");
35976
+ charge(executableCellFormula(attribute2(node, "F"))?.length ?? 0);
35977
+ editableCell(node);
35978
+ const inherited = binding.inheritedCells.get(name.toLowerCase())?.node;
35979
+ numeric4(inherited);
35980
+ if (visioFormulaCachedValue("0", attribute2(inherited, "U") ?? "DL").unit !== "length")
35981
+ fail2("EDIT_FORMULA_UNIT", "Inherited rotation pins require length units.");
35982
+ const formula = executableCellFormula(attribute2(inherited, "F"));
35983
+ if (formula) {
35984
+ const analysis = analyze2(formula);
35985
+ if (analysis.guarded || analysis.dynamic || analysis.unsupportedFunctions.length)
35986
+ fail2("EDIT_PROTECTED_CELL", "Protected or redirected master pins cannot be overridden.");
35987
+ }
35988
+ result2.add(node);
35989
+ }
35990
+ }
35991
+ if (result2.size) {
35992
+ const indexed = indexCells(roots, { check: check3 });
35993
+ const pins = new Set(
35994
+ [...indexed.values()].filter((cell) => cell.node && result2.has(cell.node)).map(key)
35995
+ );
35996
+ for (const cell of indexed.values())
35997
+ if (charge(), cell.dependencies.some((dependency) => pins.has(dependency)))
35998
+ fail2("EDIT_UNSUPPORTED_DEPENDENCY", "A page cache depends on a master rotation pin.");
35999
+ }
36000
+ return { pins: result2, dimensions: provenDimensions };
36001
+ }
36002
+
35830
36003
  // src/visio/edit-geometry.ts
35831
36004
  function createRectangle(root, edit) {
35832
36005
  const doc = root.ownerDocument;
@@ -35889,7 +36062,8 @@ function createRectangle(root, edit) {
35889
36062
  container.appendChild(shape2);
35890
36063
  return shape2;
35891
36064
  }
35892
- function applyGeometryEdit(roots, document, edit, check3, masterMovePins = /* @__PURE__ */ new Set()) {
36065
+ function applyGeometryEdit(roots, document, edit, check3, masterMoveProof = emptyMasterMoveProof()) {
36066
+ const masterMovePins = masterMoveProof.pins, masterDimensions = masterMoveProof.dimensions;
35893
36067
  check3();
35894
36068
  const root = roots.get(edit.pageId);
35895
36069
  if (!root) fail2("EDIT_TARGET_NOT_FOUND", "Page does not exist.");
@@ -35904,9 +36078,10 @@ function applyGeometryEdit(roots, document, edit, check3, masterMovePins = /* @_
35904
36078
  const shape2 = admitted(
35905
36079
  root,
35906
36080
  edit.shapeId,
35907
- edit.type === "move-shape" ? masterMovePins : /* @__PURE__ */ new Set()
36081
+ edit.type === "move-shape" ? masterMovePins : /* @__PURE__ */ new Set(),
36082
+ edit.type === "move-shape" ? masterDimensions : /* @__PURE__ */ new Map()
35908
36083
  );
35909
- protectedShape(shape2, document);
36084
+ protectedShape(shape2, document, edit.type === "move-shape" ? masterMovePins : /* @__PURE__ */ new Set());
35910
36085
  if (edit.type !== "delete-shape") {
35911
36086
  for (const connections2 of children4(root, "Connects"))
35912
36087
  for (const connection of children4(connections2, "Connect"))
@@ -35928,9 +36103,10 @@ function applyGeometryEdit(roots, document, edit, check3, masterMovePins = /* @_
35928
36103
  return [edit.pageId];
35929
36104
  }
35930
36105
  if (edit.type === "move-shape") {
36106
+ const proven = masterDimensions.get(shape2);
35931
36107
  expected = {
35932
- width: numeric4(local.get("Width")),
35933
- height: numeric4(local.get("Height")),
36108
+ width: numeric4(local.get("Width"), proven?.width),
36109
+ height: numeric4(local.get("Height"), proven?.height),
35934
36110
  x: edit.x,
35935
36111
  y: edit.y
35936
36112
  };
@@ -35938,10 +36114,7 @@ function applyGeometryEdit(roots, document, edit, check3, masterMovePins = /* @_
35938
36114
  ["PinX", edit.x, "LockMoveX"],
35939
36115
  ["PinY", edit.y, "LockMoveY"]
35940
36116
  ]) {
35941
- if (numeric4(
35942
- local.get(name),
35943
- name === "PinX" ? numeric4(local.get("Width")) / 2 : numeric4(local.get("Height")) / 2
35944
- ) === value)
36117
+ if (numeric4(local.get(name), name === "PinX" ? expected.width / 2 : expected.height / 2) === value)
35945
36118
  continue;
35946
36119
  unlocked(lock);
35947
36120
  editableCell(local.get(name));
@@ -35984,10 +36157,14 @@ function applyGeometryEdit(roots, document, edit, check3, masterMovePins = /* @_
35984
36157
  }
35985
36158
  if (!changed.length) return [];
35986
36159
  const affectedPages = recalculateVisioCells(roots, changed, { check: check3, masterMovePins });
35987
- const result2 = cells(
35988
- admitted(root, edit.shapeId, edit.type === "move-shape" ? masterMovePins : /* @__PURE__ */ new Set())
36160
+ const resultShape = admitted(
36161
+ root,
36162
+ edit.shapeId,
36163
+ edit.type === "move-shape" ? masterMovePins : /* @__PURE__ */ new Set(),
36164
+ edit.type === "move-shape" ? masterDimensions : /* @__PURE__ */ new Map()
35989
36165
  );
35990
- if (expected && (numeric4(result2.get("Width")) !== expected.width || numeric4(result2.get("Height")) !== expected.height || numeric4(result2.get("PinX"), expected.width / 2) !== expected.x || numeric4(result2.get("PinY"), expected.height / 2) !== expected.y))
36166
+ const result2 = cells(resultShape), provenResult = masterDimensions.get(resultShape);
36167
+ if (expected && (numeric4(result2.get("Width"), provenResult?.width) !== expected.width || numeric4(result2.get("Height"), provenResult?.height) !== expected.height || numeric4(result2.get("PinX"), expected.width / 2) !== expected.x || numeric4(result2.get("PinY"), expected.height / 2) !== expected.y))
35991
36168
  fail2(
35992
36169
  "EDIT_UNSUPPORTED_DEPENDENCY",
35993
36170
  "Dependent formulas would violate the requested geometry or fixed rotation pin."
@@ -36034,117 +36211,29 @@ function analyzeVisioMasterFormula(source2, options) {
36034
36211
  return { ...analysis, references: [...references.values()], dynamic: dynamic2, readsText, readsTheme };
36035
36212
  }
36036
36213
 
36037
- // src/visio/edit-master-move.ts
36038
- var protection = [
36039
- "LockMoveX",
36040
- "LockMoveY",
36041
- "LockWidth",
36042
- "LockHeight",
36043
- "LockAspect",
36044
- "LockDelete"
36045
- ];
36046
- var dimensions2 = ["PinX", "PinY", "Width", "Height", "LocPinX", "LocPinY"];
36047
- function prepareMasterMovePins(roots, commands, bindings, check3) {
36048
- const result2 = /* @__PURE__ */ new Set();
36049
- let work = 1e5;
36050
- const charge = (amount = 1) => {
36051
- check3();
36052
- work -= amount;
36053
- if (work < 0)
36054
- fail2("EDIT_UNSUPPORTED_PACKAGE_DEPENDENCY", "Master move preparation budget exceeded.");
36055
- };
36056
- const analyze2 = (formula) => {
36057
- charge(formula.length);
36058
- return analyzeVisioFormula(formula);
36059
- };
36060
- for (const command2 of commands) {
36061
- charge();
36062
- const root = roots.get(command2.pageId);
36063
- const instance = children4(children4(root, "Shapes")[0], "Shape").find(
36064
- (shape2) => attribute2(shape2, "ID") === command2.shapeId
36065
- );
36066
- if (!instance?.hasAttribute("Master")) continue;
36067
- if (command2.type !== "move-shape")
36068
- fail2("UNSUPPORTED_GEOMETRY_EDIT", "Master-linked resizing and deletion remain unsupported.");
36069
- const binding = bindings.find((candidate) => candidate.instance === instance);
36070
- if (!binding) fail2("UNSUPPORTED_GEOMETRY_EDIT", "A unique effective master root is required.");
36071
- for (const node of [instance, binding.template]) {
36072
- charge();
36073
- if (attribute2(node, "Type") !== "Shape" || children4(node, "Shapes").length || children4(node, "ForeignData").length || children4(node, "Rel").length || ["1", "true"].includes(attribute2(node, "Del") ?? ""))
36074
- fail2("UNSUPPORTED_GEOMETRY_EDIT", "Master moves require top-level local 2D shape roots.");
36075
- }
36076
- for (const name of ["BeginX", "BeginY", "EndX", "EndY"])
36077
- if (binding.cells.has(name.toLowerCase()))
36078
- fail2("UNSUPPORTED_GEOMETRY_EDIT", "Master lines and endpoint routing remain unsupported.");
36079
- const oneD = binding.cells.get("oned")?.node;
36080
- charge();
36081
- if (oneD && (numeric4(oneD) !== 0 || visioFormulaCachedValue(attribute2(oneD, "V") ?? "", attribute2(oneD, "U")).unit !== "scalar" || executableCellFormula(attribute2(oneD, "F"))))
36082
- fail2("UNSUPPORTED_GEOMETRY_EDIT", "A static effective 2D master is required.");
36083
- const local = cells(instance);
36084
- for (const name of dimensions2) {
36085
- charge();
36086
- const node = local.get(name);
36087
- numeric4(node);
36088
- if (visioFormulaCachedValue("0", attribute2(node, "U") ?? "DL").unit !== "length")
36089
- fail2("EDIT_FORMULA_UNIT", "Master transform caches require length units.");
36090
- }
36091
- if (numeric4(local.get("Width")) <= 0 || numeric4(local.get("Height")) <= 0)
36092
- fail2("UNSUPPORTED_GEOMETRY_EDIT", "Positive explicit local dimensions are required.");
36093
- for (const name of protection) {
36094
- charge();
36095
- const node = binding.cells.get(name.toLowerCase())?.node;
36096
- if (!node || visioFormulaCachedValue(attribute2(node, "V") ?? "", attribute2(node, "U")).unit !== "scalar" || numeric4(node) !== 0)
36097
- fail2(
36098
- "EDIT_PROTECTED_CELL",
36099
- "Effective master protection must be explicitly proven inactive."
36100
- );
36101
- const formula = executableCellFormula(attribute2(node, "F"));
36102
- if (formula) {
36103
- const analysis = analyze2(formula);
36104
- if (analysis.dynamic || analysis.references.length || analysis.unsupportedFunctions.length)
36105
- fail2("EDIT_PROTECTED_CELL", "Effective master protection dependencies are unsupported.");
36106
- const value = evaluateVisioFormula(
36107
- formula,
36108
- () => fail2("EDIT_PROTECTED_CELL", "Unexpected protection dependency."),
36109
- { onStep: () => charge() }
36110
- );
36111
- if (value.unit !== "scalar" || value.value !== 0)
36112
- fail2("EDIT_PROTECTED_CELL", "Effective master protection is active or stale.");
36113
- }
36114
- }
36115
- for (const name of ["PinX", "PinY"]) {
36116
- charge();
36117
- const node = local.get(name);
36118
- if (binding.cells.get(name.toLowerCase())?.node !== node)
36119
- fail2("EDIT_PROTECTED_CELL", "The rotation pin must be an explicit local override.");
36120
- charge(executableCellFormula(attribute2(node, "F"))?.length ?? 0);
36121
- editableCell(node);
36122
- const inherited = binding.inheritedCells.get(name.toLowerCase())?.node;
36123
- numeric4(inherited);
36124
- if (visioFormulaCachedValue("0", attribute2(inherited, "U") ?? "DL").unit !== "length")
36125
- fail2("EDIT_FORMULA_UNIT", "Inherited rotation pins require length units.");
36126
- const formula = executableCellFormula(attribute2(inherited, "F"));
36127
- if (formula) {
36128
- const analysis = analyze2(formula);
36129
- if (analysis.guarded || analysis.dynamic || analysis.unsupportedFunctions.length)
36130
- fail2("EDIT_PROTECTED_CELL", "Protected or redirected master pins cannot be overridden.");
36131
- }
36132
- result2.add(node);
36133
- }
36134
- }
36135
- if (result2.size) {
36136
- const indexed = indexCells(roots, { check: check3 });
36137
- const pins = new Set(
36138
- [...indexed.values()].filter((cell) => cell.node && result2.has(cell.node)).map(key)
36214
+ // src/visio/edit-master-index.ts
36215
+ function inheritedProtection(node, ancestor) {
36216
+ try {
36217
+ const boolean2 = (cell) => {
36218
+ if (!cell || cell.hasAttribute("E"))
36219
+ fail2("EDIT_PROTECTED_CELL", "Inherited protection has an unresolved or error cache.");
36220
+ const value = visioFormulaCachedValue(attribute2(cell, "V") ?? "", attribute2(cell, "U"));
36221
+ if (value.unit !== "scalar" || ![0, 1].includes(value.value))
36222
+ fail2("EDIT_PROTECTED_CELL", "Inherited protection requires scalar boolean caches.");
36223
+ return value.value;
36224
+ };
36225
+ if (boolean2(node) !== boolean2(ancestor))
36226
+ fail2(
36227
+ "EDIT_PROTECTED_CELL",
36228
+ "Delegated master style protection cache disagrees with its ancestor."
36229
+ );
36230
+ } catch (error) {
36231
+ fail2(
36232
+ "EDIT_PROTECTED_CELL",
36233
+ `Inherited master style protection cannot be proven: ${error instanceof Error ? error.message : "invalid cache"}`
36139
36234
  );
36140
- for (const cell of indexed.values())
36141
- if (charge(), cell.dependencies.some((dependency) => pins.has(dependency)))
36142
- fail2("EDIT_UNSUPPORTED_DEPENDENCY", "A page cache depends on a master rotation pin.");
36143
36235
  }
36144
- return result2;
36145
36236
  }
36146
-
36147
- // src/visio/edit-master-index.ts
36148
36237
  function masterCells(sheet, charge = () => {
36149
36238
  }) {
36150
36239
  const result2 = /* @__PURE__ */ new Map();
@@ -36202,7 +36291,7 @@ function assertMasterMarkup(root, known, check3) {
36202
36291
  }
36203
36292
  }
36204
36293
  function masterStyleCells(document, template, instance, check3, charge = () => {
36205
- }) {
36294
+ }, verifyMovementProtection = false) {
36206
36295
  const styles = /* @__PURE__ */ new Map();
36207
36296
  for (const container of children4(document, "StyleSheets"))
36208
36297
  for (const style of children4(container, "StyleSheet")) {
@@ -36227,6 +36316,10 @@ function masterStyleCells(document, template, instance, check3, charge = () => {
36227
36316
  for (const [name, node] of own) {
36228
36317
  charge();
36229
36318
  if (attribute2(node, "F") !== "Inh") result3.set(name, node);
36319
+ else if (verifyMovementProtection && /^lock(move[xy]|width|height|aspect|delete)$/.test(name)) {
36320
+ charge();
36321
+ inheritedProtection(node, result3.get(name));
36322
+ }
36230
36323
  }
36231
36324
  return result3;
36232
36325
  };
@@ -36298,12 +36391,20 @@ async function assertVisioMasterIndependence(pkg, roots, commands, check3) {
36298
36391
  const instances = [...roots].flatMap(
36299
36392
  ([pageId, root]) => masterShapes(root).filter((shape2) => shape2.hasAttribute("Master")).map((instance) => ({ pageId, instance }))
36300
36393
  );
36301
- if (!instances.length) return /* @__PURE__ */ new Set();
36394
+ if (!instances.length) return emptyMasterMoveProof();
36302
36395
  const documentPart = await related(pkg, "", "document");
36303
36396
  if (!documentPart) problem("Missing master document relationship.");
36304
36397
  const document = await visioXml(pkg, documentPart, "VisioDocument");
36305
36398
  const mastersPart = await related(pkg, documentPart, "masters", false);
36306
- if (!mastersPart) problem("Active master definitions are missing.");
36399
+ if (!mastersPart) {
36400
+ if (commands.some(
36401
+ (command2) => instances.some(
36402
+ (item) => item.pageId === command2.pageId && attribute2(item.instance, "ID") === command2.shapeId
36403
+ )
36404
+ ))
36405
+ fail2("UNSUPPORTED_GEOMETRY_EDIT", "Active master definitions are missing.");
36406
+ problem("Active master definitions are missing.");
36407
+ }
36307
36408
  const definitions = /* @__PURE__ */ new Map();
36308
36409
  for (const node of children4(await visioXml(pkg, mastersPart, "Masters"), "Master")) {
36309
36410
  const id = attribute2(node, "ID");
@@ -36318,6 +36419,12 @@ async function assertVisioMasterIndependence(pkg, roots, commands, check3) {
36318
36419
  if (--buildWork < 0) problem("Master effective-cell construction budget exceeded.");
36319
36420
  };
36320
36421
  const pageBindings = /* @__PURE__ */ new Map();
36422
+ const movingTargets = /* @__PURE__ */ new Set();
36423
+ for (const command2 of commands) {
36424
+ charge();
36425
+ if (command2.type === "move-shape")
36426
+ movingTargets.add(JSON.stringify([command2.pageId, command2.shapeId]));
36427
+ }
36321
36428
  const pageShapes = /* @__PURE__ */ new Map();
36322
36429
  for (const [pageId, root] of roots)
36323
36430
  for (const shape2 of masterShapes(root)) {
@@ -36377,7 +36484,15 @@ async function assertVisioMasterIndependence(pkg, roots, commands, check3) {
36377
36484
  const templateId = attribute2(template, "ID");
36378
36485
  if (!templateId || context.has(templateId)) problem("Master-local sheet IDs are ambiguous.");
36379
36486
  const local = byTemplate.get(templateId), cells3 = /* @__PURE__ */ new Map();
36380
- for (const [name, node] of masterStyleCells(document, template, local, check3, charge))
36487
+ const movedInstance = local === instance && movingTargets.has(JSON.stringify([pageId, attribute2(local, "ID")]));
36488
+ for (const [name, node] of masterStyleCells(
36489
+ document,
36490
+ template,
36491
+ local,
36492
+ check3,
36493
+ charge,
36494
+ movedInstance
36495
+ ))
36381
36496
  cells3.set(name, { node, kind: "style" });
36382
36497
  overlay(cells3, masterCells(template, charge), "template", charge);
36383
36498
  const inheritedCells = new Map(cells3);
@@ -36507,7 +36622,7 @@ async function assertVisioMasterIndependence(pkg, roots, commands, check3) {
36507
36622
  for (const binding of bindings)
36508
36623
  for (const name of binding.cells.keys()) {
36509
36624
  const source2 = binding.cells.get(name);
36510
- if (["pinx", "piny"].includes(name) && source2?.kind === "instance" && movePins.has(source2.node))
36625
+ if (["pinx", "piny"].includes(name) && source2?.kind === "instance" && movePins.pins.has(source2.node))
36511
36626
  continue;
36512
36627
  inspect2(binding, name);
36513
36628
  }
@@ -36631,7 +36746,7 @@ async function assertGeometryPackageScope(pkg, pagePaths, commands, check3, root
36631
36746
  });
36632
36747
  }
36633
36748
  }
36634
- let movePins = /* @__PURE__ */ new Set();
36749
+ let movePins = emptyMasterMoveProof();
36635
36750
  if (hasMasterInstances) {
36636
36751
  for (const command2 of commands) {
36637
36752
  const root = roots.get(command2.pageId);
@@ -36641,10 +36756,10 @@ async function assertGeometryPackageScope(pkg, pagePaths, commands, check3, root
36641
36756
  );
36642
36757
  if (target?.hasAttribute("MasterShape") && !target.hasAttribute("Master"))
36643
36758
  fail2("UNSUPPORTED_GEOMETRY_EDIT", "Editing master-linked shapes is unsupported.");
36644
- if (target?.hasAttribute("Master") && (command2.type !== "move-shape" || !["PinX", "PinY", "Width", "Height", "LocPinX", "LocPinY"].every(
36759
+ if (target?.hasAttribute("Master") && (command2.type !== "move-shape" || !["PinX", "PinY"].every(
36645
36760
  (name) => children4(target, "Cell").some((node) => attribute2(node, "N") === name)
36646
36761
  )))
36647
- fail2("UNSUPPORTED_GEOMETRY_EDIT", "Master moves require complete local transform caches.");
36762
+ fail2("UNSUPPORTED_GEOMETRY_EDIT", "Master moves require explicit local rotation pins.");
36648
36763
  }
36649
36764
  movePins = await assertVisioMasterIndependence(pkg, roots, commands, check3);
36650
36765
  }
@@ -36741,7 +36856,7 @@ async function editVsdx(input, edits, options = {}) {
36741
36856
  const roots = /* @__PURE__ */ new Map();
36742
36857
  const geometry = commands.some((command2) => command2.type !== "replace-plain-text");
36743
36858
  let document;
36744
- let masterMovePins = /* @__PURE__ */ new Set();
36859
+ let masterMovePins = emptyMasterMoveProof();
36745
36860
  if (geometry) {
36746
36861
  for (const [pageId, path2] of pages) {
36747
36862
  const sourceRoot = await visioXml(pkg, path2, "PageContents");