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.
@@ -20598,8 +20598,9 @@ var AreaIndex = class {
20598
20598
  wide = /* @__PURE__ */ new Map();
20599
20599
  add(area, node) {
20600
20600
  const { start, end } = area.range;
20601
- if (start.row === end.row && start.col === end.col) {
20602
- push2(inner(this.cells, area.sheet), cellKey(start.row, start.col), node);
20601
+ if (start.row === end.row && end.col - start.col + 1 <= BUCKET_WIDTH) {
20602
+ const cells3 = inner(this.cells, area.sheet);
20603
+ for (let col = start.col; col <= end.col; col++) push2(cells3, cellKey(start.row, col), node);
20603
20604
  return;
20604
20605
  }
20605
20606
  const entry = { range: area.range, node };
@@ -20825,7 +20826,8 @@ function writeFootprint(sheet, footprint, m) {
20825
20826
  }
20826
20827
  if (cell) {
20827
20828
  cell.value = value;
20828
- cell.spillAnchor = { row, col };
20829
+ if (!isSpilledCell(cell) || cell.spillAnchor.row !== row || cell.spillAnchor.col !== col)
20830
+ cell.spillAnchor = { row, col };
20829
20831
  } else {
20830
20832
  const spilled = { value, spillAnchor: { row, col } };
20831
20833
  putCell(sheet, r, c2, spilled);
@@ -20872,6 +20874,7 @@ var EngineHost = class {
20872
20874
  columnIndex;
20873
20875
  reverseIndex;
20874
20876
  footprintIndex;
20877
+ fullOrder;
20875
20878
  /** Range reads cached for one evaluation pass (see `readBlock`). */
20876
20879
  blockCache = /* @__PURE__ */ new Map();
20877
20880
  /** Counts reads that hit a formula still being evaluated (a circular reference). */
@@ -21044,6 +21047,7 @@ var EngineHost = class {
21044
21047
  if (this.footprints.delete(node)) this.footprintIndex = void 0;
21045
21048
  }
21046
21049
  graphChanged() {
21050
+ this.fullOrder = void 0;
21047
21051
  this.columnIndex = void 0;
21048
21052
  this.reverseIndex = void 0;
21049
21053
  this.footprintIndex = void 0;
@@ -21074,52 +21078,6 @@ var EngineHost = class {
21074
21078
  }
21075
21079
  };
21076
21080
 
21077
- // src/xlsx/formula/engine-types.ts
21078
- var CIRCULAR_REFERENCE_WARNING = "The workbook contains circular references; the cells involved show 0.";
21079
-
21080
- // src/xlsx/formula/engine-util.ts
21081
- function finalize(value, host) {
21082
- let result2;
21083
- if (value instanceof RefValue) {
21084
- if (value.areas.length !== 1) return ERR.VALUE;
21085
- if (value.isCell()) {
21086
- const area = value.areas[0];
21087
- result2 = host.readCell(area.sheet, area.range.start.row, area.range.start.col);
21088
- } else result2 = toMatrix(host, value);
21089
- } else if (value instanceof LambdaValue) return ERR.CALC;
21090
- else result2 = value;
21091
- if (result2 instanceof Matrix) {
21092
- if (result2.rows === 1 && result2.cols === 1) result2 = result2.get(0, 0);
21093
- else return result2.block().mapValues(clean);
21094
- }
21095
- return clean(result2);
21096
- }
21097
- function clean(value) {
21098
- if (value === null) return 0;
21099
- if (typeof value === "number") {
21100
- if (!Number.isFinite(value)) return ERR.NUM;
21101
- return value === 0 ? 0 : value;
21102
- }
21103
- return value;
21104
- }
21105
- var rangeHas = (range, row, col) => row >= range.start.row && row <= range.end.row && col >= range.start.col && col <= range.end.col;
21106
- function sameRange(a, b) {
21107
- if (!a || !b) return a === b || !a && !b;
21108
- 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;
21109
- }
21110
- function boundingBox(ranges) {
21111
- const first4 = ranges[0];
21112
- const start = { ...first4.start };
21113
- const end = { ...first4.end };
21114
- for (const r of ranges) {
21115
- start.row = Math.min(start.row, r.start.row);
21116
- start.col = Math.min(start.col, r.start.col);
21117
- end.row = Math.max(end.row, r.end.row);
21118
- end.col = Math.max(end.col, r.end.col);
21119
- }
21120
- return { start, end };
21121
- }
21122
-
21123
21081
  // src/xlsx/formula/scc.ts
21124
21082
  function stronglyConnected(nodes, successors) {
21125
21083
  const index2 = /* @__PURE__ */ new Map();
@@ -21172,6 +21130,63 @@ function stronglyConnected(nodes, successors) {
21172
21130
  return out;
21173
21131
  }
21174
21132
 
21133
+ // src/xlsx/formula/engine-order.ts
21134
+ function formulaOrder(nodes, successors) {
21135
+ const ranks = /* @__PURE__ */ new Map();
21136
+ const components = stronglyConnected(nodes, successors).map((members, rank4) => {
21137
+ for (const member of members) ranks.set(member, rank4);
21138
+ const first4 = members[0];
21139
+ return { nodes: members, cyclic: members.length > 1 || successors(first4).includes(first4) };
21140
+ });
21141
+ return { components, ranks };
21142
+ }
21143
+
21144
+ // src/xlsx/formula/engine-types.ts
21145
+ var CIRCULAR_REFERENCE_WARNING = "The workbook contains circular references; the cells involved show 0.";
21146
+
21147
+ // src/xlsx/formula/engine-util.ts
21148
+ function finalize(value, host) {
21149
+ let result2;
21150
+ if (value instanceof RefValue) {
21151
+ if (value.areas.length !== 1) return ERR.VALUE;
21152
+ if (value.isCell()) {
21153
+ const area = value.areas[0];
21154
+ result2 = host.readCell(area.sheet, area.range.start.row, area.range.start.col);
21155
+ } else result2 = toMatrix(host, value);
21156
+ } else if (value instanceof LambdaValue) return ERR.CALC;
21157
+ else result2 = value;
21158
+ if (result2 instanceof Matrix) {
21159
+ if (result2.rows === 1 && result2.cols === 1) result2 = result2.get(0, 0);
21160
+ else return result2.block().mapValues(clean);
21161
+ }
21162
+ return clean(result2);
21163
+ }
21164
+ function clean(value) {
21165
+ if (value === null) return 0;
21166
+ if (typeof value === "number") {
21167
+ if (!Number.isFinite(value)) return ERR.NUM;
21168
+ return value === 0 ? 0 : value;
21169
+ }
21170
+ return value;
21171
+ }
21172
+ var rangeHas = (range, row, col) => row >= range.start.row && row <= range.end.row && col >= range.start.col && col <= range.end.col;
21173
+ function sameRange(a, b) {
21174
+ if (!a || !b) return a === b || !a && !b;
21175
+ 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;
21176
+ }
21177
+ function boundingBox(ranges) {
21178
+ const first4 = ranges[0];
21179
+ const start = { ...first4.start };
21180
+ const end = { ...first4.end };
21181
+ for (const r of ranges) {
21182
+ start.row = Math.min(start.row, r.start.row);
21183
+ start.col = Math.min(start.col, r.start.col);
21184
+ end.row = Math.max(end.row, r.end.row);
21185
+ end.col = Math.max(end.col, r.end.col);
21186
+ }
21187
+ return { start, end };
21188
+ }
21189
+
21175
21190
  // src/xlsx/formula/store.ts
21176
21191
  function storeResult(sheet, node, cell, result2, claims) {
21177
21192
  const { row, col } = node;
@@ -21191,9 +21206,9 @@ function storeResult(sheet, node, cell, result2, claims) {
21191
21206
  return;
21192
21207
  }
21193
21208
  const footprint = footprintFor(row, col, result2);
21194
- if (node.spill) clearFootprint(sheet, node.spill, row, col);
21195
- delete node.spill;
21196
21209
  if (!footprint || !footprintFree(sheet, footprint, row, col, claims)) {
21210
+ if (node.spill) clearFootprint(sheet, node.spill, row, col);
21211
+ delete node.spill;
21197
21212
  node.blockedSpill = footprint ?? {
21198
21213
  start: { row, col },
21199
21214
  end: {
@@ -21204,6 +21219,7 @@ function storeResult(sheet, node, cell, result2, claims) {
21204
21219
  cell.value = ERR.SPILL;
21205
21220
  return;
21206
21221
  }
21222
+ if (node.spill && !sameRange(node.spill, footprint)) clearFootprint(sheet, node.spill, row, col);
21207
21223
  delete node.blockedSpill;
21208
21224
  writeFootprint(sheet, footprint, result2);
21209
21225
  node.spill = footprint;
@@ -21226,7 +21242,7 @@ var EngineCore = class extends EngineHost {
21226
21242
  const dirty = everything && round2 === 0 ? current : this.closure(current);
21227
21243
  this.footprintChanges = [];
21228
21244
  this.evaluatedAt = /* @__PURE__ */ new Map();
21229
- this.evaluateSet(dirty);
21245
+ this.evaluateSet(dirty, everything && round2 === 0);
21230
21246
  if (this.footprintChanges.length === 0) break;
21231
21247
  const next = /* @__PURE__ */ new Set();
21232
21248
  const reverse2 = this.reverse();
@@ -21268,7 +21284,7 @@ var EngineCore = class extends EngineHost {
21268
21284
  }
21269
21285
  return out;
21270
21286
  }
21271
- evaluateSet(dirty) {
21287
+ evaluateSet(dirty, full) {
21272
21288
  this.boundsCache.clear();
21273
21289
  this.blockCache.clear();
21274
21290
  const columns = this.columns();
@@ -21278,24 +21294,25 @@ var EngineCore = class extends EngineHost {
21278
21294
  let out = succCache.get(node);
21279
21295
  if (out) return out;
21280
21296
  const found = [];
21297
+ const anchors = /* @__PURE__ */ new Set();
21281
21298
  for (const dep of node.deps) {
21282
21299
  columns.nodesIn(dep, found);
21283
- const anchors = [];
21284
21300
  footprints.dependents(dep.sheet, dep.range, anchors);
21285
- for (const anchor of anchors) if (anchor.spill ?? anchor.arrayRange) found.push(anchor);
21286
21301
  }
21287
- out = [...new Set(found)].filter((n) => dirty.has(n));
21302
+ for (const anchor of anchors) if (anchor.spill ?? anchor.arrayRange) found.push(anchor);
21303
+ out = found.length ? [...new Set(found)].filter((n) => dirty.has(n)) : [];
21288
21304
  succCache.set(node, out);
21289
21305
  return out;
21290
21306
  };
21291
- const order = stronglyConnected(dirty, successors);
21307
+ const order = full && this.fullOrder || formulaOrder(dirty, successors);
21308
+ if (full) this.fullOrder = order;
21292
21309
  this.pending = new Set(dirty);
21293
21310
  this.dynamicCycleHits = /* @__PURE__ */ new Set();
21294
21311
  try {
21295
- for (const component of order) {
21296
- const first4 = component[0];
21297
- if (component.length > 1 || successors(first4).includes(first4)) {
21298
- for (const node of component) this.markCycle(node);
21312
+ for (const component of order.components) {
21313
+ const first4 = component.nodes[0];
21314
+ if (component.cyclic) {
21315
+ for (const node of component.nodes) this.markCycle(node);
21299
21316
  } else if (this.pending.has(first4)) {
21300
21317
  this.computeFrom(first4);
21301
21318
  }
@@ -21374,6 +21391,7 @@ var EngineCore = class extends EngineHost {
21374
21391
  return !!other && other !== node && !!other.spill && rangeHas(other.spill, r, c2);
21375
21392
  });
21376
21393
  if (!sameRange(before, node.spill) || !sameRange(blockedBefore, node.blockedSpill)) {
21394
+ this.updateOrder(node, before, blockedBefore);
21377
21395
  this.footprintIndex = void 0;
21378
21396
  this.blockCache.clear();
21379
21397
  }
@@ -21393,10 +21411,30 @@ var EngineCore = class extends EngineHost {
21393
21411
  }
21394
21412
  }
21395
21413
  }
21414
+ /** New spill edges may reuse the order only when every reader already follows its anchor. */
21415
+ updateOrder(node, before, blockedBefore) {
21416
+ const order = this.fullOrder;
21417
+ if (!order) return;
21418
+ if (before || blockedBefore || !node.spill || node.blockedSpill) {
21419
+ this.fullOrder = void 0;
21420
+ return;
21421
+ }
21422
+ const rank4 = order.ranks.get(node);
21423
+ if (rank4 === void 0) {
21424
+ this.fullOrder = void 0;
21425
+ return;
21426
+ }
21427
+ const readers = /* @__PURE__ */ new Set();
21428
+ this.reverse().dependents(node.sheet, node.spill, readers);
21429
+ for (const reader of readers) {
21430
+ if ((order.ranks.get(reader) ?? -1) <= rank4) {
21431
+ this.fullOrder = void 0;
21432
+ return;
21433
+ }
21434
+ }
21435
+ }
21396
21436
  frameFor(sheet, row, col, root, legacy = false) {
21397
- const frame = { host: this, sheet, row, col, scope: void 0, depth: 0 };
21398
- if (legacy) Object.assign(frame, { legacy: true });
21399
- return root ? { ...frame, root } : frame;
21437
+ return { host: this, sheet, row, col, scope: void 0, depth: 0, root, legacy };
21400
21438
  }
21401
21439
  evaluateFormula(node) {
21402
21440
  if (!node.ast) return node.parseError ?? ERR.NAME;
@@ -35675,7 +35713,7 @@ function setCell(shape2, name, value, formula) {
35675
35713
  if (formula) node.setAttribute("F", formula);
35676
35714
  else if (attribute2(node, "F") !== "No Formula") node.removeAttribute("F");
35677
35715
  }
35678
- function admitted(root, shapeId, masterMovePins = /* @__PURE__ */ new Set()) {
35716
+ function admitted(root, shapeId, masterMovePins = /* @__PURE__ */ new Set(), masterDimensions = /* @__PURE__ */ new Map()) {
35679
35717
  const containers = children4(root, "Shapes");
35680
35718
  if (containers.length !== 1)
35681
35719
  fail2("UNSUPPORTED_GEOMETRY_EDIT", "One local Shapes container is required.");
@@ -35704,12 +35742,17 @@ function admitted(root, shapeId, masterMovePins = /* @__PURE__ */ new Set()) {
35704
35742
  "UNSUPPORTED_GEOMETRY_EDIT",
35705
35743
  "Only local 2D shapes are admitted; line routing and glue are unsupported."
35706
35744
  );
35707
- if (!(numeric4(local.get("Width")) > 0) || !(numeric4(local.get("Height")) > 0))
35708
- fail2("UNSUPPORTED_GEOMETRY_EDIT", "Positive local Width and Height caches are required.");
35745
+ const proven = masterDimensions.get(shape2);
35746
+ if (!(numeric4(local.get("Width"), proven?.width) > 0) || !(numeric4(local.get("Height"), proven?.height) > 0))
35747
+ fail2("UNSUPPORTED_GEOMETRY_EDIT", "Positive proven Width and Height caches are required.");
35709
35748
  return shape2;
35710
35749
  }
35711
- function protectedShape(shape2, document) {
35750
+ function protectedShape(shape2, document, masterMovePins = /* @__PURE__ */ new Set()) {
35712
35751
  const local = cells(shape2);
35752
+ const certifiedMove = shape2.hasAttribute("Master") && ["PinX", "PinY"].every((name) => {
35753
+ const node = local.get(name);
35754
+ return node !== void 0 && masterMovePins.has(node);
35755
+ });
35713
35756
  const styles = /* @__PURE__ */ new Map();
35714
35757
  for (const container of children4(document, "StyleSheets"))
35715
35758
  for (const node of children4(container, "StyleSheet")) {
@@ -35717,9 +35760,13 @@ function protectedShape(shape2, document) {
35717
35760
  if (!id || styles.has(id)) fail2("EDIT_PROTECTED_CELL", "Ambiguous protection style IDs.");
35718
35761
  styles.set(id, node);
35719
35762
  }
35720
- const inactive = (cell) => {
35763
+ const validateProtection = (cell) => {
35721
35764
  try {
35722
- if (cell.hasAttribute("E") || visioFormulaCachedValue(attribute2(cell, "V") ?? "", attribute2(cell, "U")).unit !== "scalar" || numeric4(cell) !== 0)
35765
+ const cached3 = numeric4(cell);
35766
+ const operationIndependent = certifiedMove && ["LockWidth", "LockHeight", "LockAspect", "LockDelete"].includes(
35767
+ attribute2(cell, "N") ?? ""
35768
+ );
35769
+ if (cell.hasAttribute("E") || visioFormulaCachedValue(attribute2(cell, "V") ?? "", attribute2(cell, "U")).unit !== "scalar" || !(cached3 === 0 || operationIndependent && cached3 === 1))
35723
35770
  fail2("EDIT_PROTECTED_CELL", "Inherited protection is active or invalid.");
35724
35771
  const source2 = executableCellFormula(attribute2(cell, "F"));
35725
35772
  if (!source2) return;
@@ -35730,12 +35777,12 @@ function protectedShape(shape2, document) {
35730
35777
  source2,
35731
35778
  () => fail2("EDIT_PROTECTED_CELL", "Inherited protection has dependencies.")
35732
35779
  );
35733
- if (result2.unit !== "scalar" || result2.value !== 0)
35734
- fail2("EDIT_PROTECTED_CELL", "Inherited protection formula is active or stale.");
35780
+ if (result2.unit !== "scalar" || result2.value !== cached3)
35781
+ fail2("EDIT_PROTECTED_CELL", "Inherited protection formula cache is stale.");
35735
35782
  } catch (error) {
35736
35783
  fail2(
35737
35784
  "EDIT_PROTECTED_CELL",
35738
- `Inherited protection cannot be proven inactive: ${error instanceof Error ? error.message : "invalid cache"}`
35785
+ `Inherited protection cannot be proven safe for this operation: ${error instanceof Error ? error.message : "invalid cache"}`
35739
35786
  );
35740
35787
  }
35741
35788
  };
@@ -35747,16 +35794,18 @@ function protectedShape(shape2, document) {
35747
35794
  if (!style) fail2("EDIT_PROTECTED_CELL", "Protection style ancestry cannot be resolved.");
35748
35795
  const cell = cells(style).get(lock), parent = attribute2(style, category);
35749
35796
  if (cell && attribute2(cell, "F") !== "Inh") {
35750
- inactive(cell);
35797
+ validateProtection(cell);
35751
35798
  return cell;
35752
35799
  }
35753
35800
  if (cell) {
35754
- inactive(cell);
35801
+ validateProtection(cell);
35755
35802
  if (parent === void 0 || parent === id)
35756
35803
  fail2("EDIT_PROTECTED_CELL", "Inherited protection has no provable parent.");
35757
35804
  const inherited = resolve(parent, category, lock, seen);
35758
35805
  if (!inherited)
35759
- fail2("EDIT_PROTECTED_CELL", "Inherited protection has no explicit inactive ancestor.");
35806
+ fail2("EDIT_PROTECTED_CELL", "Inherited protection has no explicit provable ancestor.");
35807
+ if (numeric4(cell) !== numeric4(inherited))
35808
+ fail2("EDIT_PROTECTED_CELL", "Delegated protection cache disagrees with its ancestor.");
35760
35809
  return inherited;
35761
35810
  }
35762
35811
  return parent !== void 0 && parent !== id ? resolve(parent, category, lock, seen) : void 0;
@@ -35833,6 +35882,130 @@ function resizeGeometry(shape2, roots, edit, check3) {
35833
35882
  }
35834
35883
  }
35835
35884
 
35885
+ // src/visio/edit-master-move.ts
35886
+ var protection = [
35887
+ "LockMoveX",
35888
+ "LockMoveY",
35889
+ "LockWidth",
35890
+ "LockHeight",
35891
+ "LockAspect",
35892
+ "LockDelete"
35893
+ ];
35894
+ var dimensions2 = ["PinX", "PinY", "Width", "Height", "LocPinX", "LocPinY"];
35895
+ var emptyMasterMoveProof = () => ({
35896
+ pins: /* @__PURE__ */ new Set(),
35897
+ dimensions: /* @__PURE__ */ new Map()
35898
+ });
35899
+ function prepareMasterMovePins(roots, commands, bindings, check3) {
35900
+ const result2 = /* @__PURE__ */ new Set();
35901
+ const provenDimensions = /* @__PURE__ */ new Map();
35902
+ let work = 1e5;
35903
+ const charge = (amount = 1) => {
35904
+ check3();
35905
+ work -= amount;
35906
+ if (work < 0)
35907
+ fail2("EDIT_UNSUPPORTED_PACKAGE_DEPENDENCY", "Master move preparation budget exceeded.");
35908
+ };
35909
+ const analyze2 = (formula) => {
35910
+ charge(formula.length);
35911
+ return analyzeVisioFormula(formula);
35912
+ };
35913
+ for (const command2 of commands) {
35914
+ charge();
35915
+ const root = roots.get(command2.pageId);
35916
+ const instance = children4(children4(root, "Shapes")[0], "Shape").find(
35917
+ (shape2) => attribute2(shape2, "ID") === command2.shapeId
35918
+ );
35919
+ if (!instance?.hasAttribute("Master")) continue;
35920
+ if (command2.type !== "move-shape")
35921
+ fail2("UNSUPPORTED_GEOMETRY_EDIT", "Master-linked resizing and deletion remain unsupported.");
35922
+ const binding = bindings.find((candidate) => candidate.instance === instance);
35923
+ if (!binding) fail2("UNSUPPORTED_GEOMETRY_EDIT", "A unique effective master root is required.");
35924
+ for (const node of [instance, binding.template]) {
35925
+ charge();
35926
+ if (attribute2(node, "Type") !== "Shape" || children4(node, "Shapes").length || children4(node, "ForeignData").length || children4(node, "Rel").length || ["1", "true"].includes(attribute2(node, "Del") ?? ""))
35927
+ fail2("UNSUPPORTED_GEOMETRY_EDIT", "Master moves require top-level local 2D shape roots.");
35928
+ }
35929
+ for (const name of ["BeginX", "BeginY", "EndX", "EndY"])
35930
+ if (binding.cells.has(name.toLowerCase()))
35931
+ fail2("UNSUPPORTED_GEOMETRY_EDIT", "Master lines and endpoint routing remain unsupported.");
35932
+ const oneD = binding.cells.get("oned")?.node;
35933
+ charge();
35934
+ if (oneD && (numeric4(oneD) !== 0 || visioFormulaCachedValue(attribute2(oneD, "V") ?? "", attribute2(oneD, "U")).unit !== "scalar" || executableCellFormula(attribute2(oneD, "F"))))
35935
+ fail2("UNSUPPORTED_GEOMETRY_EDIT", "A static effective 2D master is required.");
35936
+ const local = cells(instance);
35937
+ for (const name of dimensions2) {
35938
+ const effective = binding.cells.get(name.toLowerCase())?.node;
35939
+ for (const node of /* @__PURE__ */ new Set([effective, ...local.has(name) ? [local.get(name)] : []])) {
35940
+ charge();
35941
+ numeric4(node);
35942
+ if (visioFormulaCachedValue("0", attribute2(node, "U") ?? "DL").unit !== "length")
35943
+ fail2("EDIT_FORMULA_UNIT", "Master transform caches require length units.");
35944
+ }
35945
+ }
35946
+ const dimension = (name) => numeric4(local.get(name) ?? binding.cells.get(name.toLowerCase())?.node);
35947
+ const width = dimension("Width"), height = dimension("Height");
35948
+ if (width <= 0 || height <= 0)
35949
+ fail2("UNSUPPORTED_GEOMETRY_EDIT", "Positive proven effective dimensions are required.");
35950
+ provenDimensions.set(instance, { width, height });
35951
+ for (const name of protection) {
35952
+ charge();
35953
+ const node = binding.cells.get(name.toLowerCase())?.node;
35954
+ const cached3 = node ? numeric4(node) : NaN;
35955
+ const moving = name === "LockMoveX" || name === "LockMoveY";
35956
+ if (!node || visioFormulaCachedValue(attribute2(node, "V") ?? "", attribute2(node, "U")).unit !== "scalar" || ![0, 1].includes(cached3) || moving && cached3 !== 0)
35957
+ fail2(
35958
+ "EDIT_PROTECTED_CELL",
35959
+ "Effective master protection must be proven boolean with inactive movement locks."
35960
+ );
35961
+ const formula = executableCellFormula(attribute2(node, "F"));
35962
+ if (formula) {
35963
+ const analysis = analyze2(formula);
35964
+ if (analysis.dynamic || analysis.references.length || analysis.unsupportedFunctions.length)
35965
+ fail2("EDIT_PROTECTED_CELL", "Effective master protection dependencies are unsupported.");
35966
+ const value = evaluateVisioFormula(
35967
+ formula,
35968
+ () => fail2("EDIT_PROTECTED_CELL", "Unexpected protection dependency."),
35969
+ { onStep: () => charge() }
35970
+ );
35971
+ if (value.unit !== "scalar" || value.value !== cached3)
35972
+ fail2("EDIT_PROTECTED_CELL", "Effective master protection cache is stale.");
35973
+ }
35974
+ }
35975
+ for (const name of ["PinX", "PinY"]) {
35976
+ charge();
35977
+ const node = local.get(name);
35978
+ if (!node)
35979
+ fail2("EDIT_PROTECTED_CELL", "The rotation pin must be an explicit local override.");
35980
+ if (binding.cells.get(name.toLowerCase())?.node !== node)
35981
+ fail2("EDIT_PROTECTED_CELL", "The rotation pin must be an explicit local override.");
35982
+ charge(executableCellFormula(attribute2(node, "F"))?.length ?? 0);
35983
+ editableCell(node);
35984
+ const inherited = binding.inheritedCells.get(name.toLowerCase())?.node;
35985
+ numeric4(inherited);
35986
+ if (visioFormulaCachedValue("0", attribute2(inherited, "U") ?? "DL").unit !== "length")
35987
+ fail2("EDIT_FORMULA_UNIT", "Inherited rotation pins require length units.");
35988
+ const formula = executableCellFormula(attribute2(inherited, "F"));
35989
+ if (formula) {
35990
+ const analysis = analyze2(formula);
35991
+ if (analysis.guarded || analysis.dynamic || analysis.unsupportedFunctions.length)
35992
+ fail2("EDIT_PROTECTED_CELL", "Protected or redirected master pins cannot be overridden.");
35993
+ }
35994
+ result2.add(node);
35995
+ }
35996
+ }
35997
+ if (result2.size) {
35998
+ const indexed = indexCells(roots, { check: check3 });
35999
+ const pins = new Set(
36000
+ [...indexed.values()].filter((cell) => cell.node && result2.has(cell.node)).map(key)
36001
+ );
36002
+ for (const cell of indexed.values())
36003
+ if (charge(), cell.dependencies.some((dependency) => pins.has(dependency)))
36004
+ fail2("EDIT_UNSUPPORTED_DEPENDENCY", "A page cache depends on a master rotation pin.");
36005
+ }
36006
+ return { pins: result2, dimensions: provenDimensions };
36007
+ }
36008
+
35836
36009
  // src/visio/edit-geometry.ts
35837
36010
  function createRectangle(root, edit) {
35838
36011
  const doc = root.ownerDocument;
@@ -35895,7 +36068,8 @@ function createRectangle(root, edit) {
35895
36068
  container.appendChild(shape2);
35896
36069
  return shape2;
35897
36070
  }
35898
- function applyGeometryEdit(roots, document, edit, check3, masterMovePins = /* @__PURE__ */ new Set()) {
36071
+ function applyGeometryEdit(roots, document, edit, check3, masterMoveProof = emptyMasterMoveProof()) {
36072
+ const masterMovePins = masterMoveProof.pins, masterDimensions = masterMoveProof.dimensions;
35899
36073
  check3();
35900
36074
  const root = roots.get(edit.pageId);
35901
36075
  if (!root) fail2("EDIT_TARGET_NOT_FOUND", "Page does not exist.");
@@ -35910,9 +36084,10 @@ function applyGeometryEdit(roots, document, edit, check3, masterMovePins = /* @_
35910
36084
  const shape2 = admitted(
35911
36085
  root,
35912
36086
  edit.shapeId,
35913
- edit.type === "move-shape" ? masterMovePins : /* @__PURE__ */ new Set()
36087
+ edit.type === "move-shape" ? masterMovePins : /* @__PURE__ */ new Set(),
36088
+ edit.type === "move-shape" ? masterDimensions : /* @__PURE__ */ new Map()
35914
36089
  );
35915
- protectedShape(shape2, document);
36090
+ protectedShape(shape2, document, edit.type === "move-shape" ? masterMovePins : /* @__PURE__ */ new Set());
35916
36091
  if (edit.type !== "delete-shape") {
35917
36092
  for (const connections2 of children4(root, "Connects"))
35918
36093
  for (const connection of children4(connections2, "Connect"))
@@ -35934,9 +36109,10 @@ function applyGeometryEdit(roots, document, edit, check3, masterMovePins = /* @_
35934
36109
  return [edit.pageId];
35935
36110
  }
35936
36111
  if (edit.type === "move-shape") {
36112
+ const proven = masterDimensions.get(shape2);
35937
36113
  expected = {
35938
- width: numeric4(local.get("Width")),
35939
- height: numeric4(local.get("Height")),
36114
+ width: numeric4(local.get("Width"), proven?.width),
36115
+ height: numeric4(local.get("Height"), proven?.height),
35940
36116
  x: edit.x,
35941
36117
  y: edit.y
35942
36118
  };
@@ -35944,10 +36120,7 @@ function applyGeometryEdit(roots, document, edit, check3, masterMovePins = /* @_
35944
36120
  ["PinX", edit.x, "LockMoveX"],
35945
36121
  ["PinY", edit.y, "LockMoveY"]
35946
36122
  ]) {
35947
- if (numeric4(
35948
- local.get(name),
35949
- name === "PinX" ? numeric4(local.get("Width")) / 2 : numeric4(local.get("Height")) / 2
35950
- ) === value)
36123
+ if (numeric4(local.get(name), name === "PinX" ? expected.width / 2 : expected.height / 2) === value)
35951
36124
  continue;
35952
36125
  unlocked(lock);
35953
36126
  editableCell(local.get(name));
@@ -35990,10 +36163,14 @@ function applyGeometryEdit(roots, document, edit, check3, masterMovePins = /* @_
35990
36163
  }
35991
36164
  if (!changed.length) return [];
35992
36165
  const affectedPages = recalculateVisioCells(roots, changed, { check: check3, masterMovePins });
35993
- const result2 = cells(
35994
- admitted(root, edit.shapeId, edit.type === "move-shape" ? masterMovePins : /* @__PURE__ */ new Set())
36166
+ const resultShape = admitted(
36167
+ root,
36168
+ edit.shapeId,
36169
+ edit.type === "move-shape" ? masterMovePins : /* @__PURE__ */ new Set(),
36170
+ edit.type === "move-shape" ? masterDimensions : /* @__PURE__ */ new Map()
35995
36171
  );
35996
- 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))
36172
+ const result2 = cells(resultShape), provenResult = masterDimensions.get(resultShape);
36173
+ 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))
35997
36174
  fail2(
35998
36175
  "EDIT_UNSUPPORTED_DEPENDENCY",
35999
36176
  "Dependent formulas would violate the requested geometry or fixed rotation pin."
@@ -36040,117 +36217,29 @@ function analyzeVisioMasterFormula(source2, options) {
36040
36217
  return { ...analysis, references: [...references.values()], dynamic: dynamic2, readsText, readsTheme };
36041
36218
  }
36042
36219
 
36043
- // src/visio/edit-master-move.ts
36044
- var protection = [
36045
- "LockMoveX",
36046
- "LockMoveY",
36047
- "LockWidth",
36048
- "LockHeight",
36049
- "LockAspect",
36050
- "LockDelete"
36051
- ];
36052
- var dimensions2 = ["PinX", "PinY", "Width", "Height", "LocPinX", "LocPinY"];
36053
- function prepareMasterMovePins(roots, commands, bindings, check3) {
36054
- const result2 = /* @__PURE__ */ new Set();
36055
- let work = 1e5;
36056
- const charge = (amount = 1) => {
36057
- check3();
36058
- work -= amount;
36059
- if (work < 0)
36060
- fail2("EDIT_UNSUPPORTED_PACKAGE_DEPENDENCY", "Master move preparation budget exceeded.");
36061
- };
36062
- const analyze2 = (formula) => {
36063
- charge(formula.length);
36064
- return analyzeVisioFormula(formula);
36065
- };
36066
- for (const command2 of commands) {
36067
- charge();
36068
- const root = roots.get(command2.pageId);
36069
- const instance = children4(children4(root, "Shapes")[0], "Shape").find(
36070
- (shape2) => attribute2(shape2, "ID") === command2.shapeId
36071
- );
36072
- if (!instance?.hasAttribute("Master")) continue;
36073
- if (command2.type !== "move-shape")
36074
- fail2("UNSUPPORTED_GEOMETRY_EDIT", "Master-linked resizing and deletion remain unsupported.");
36075
- const binding = bindings.find((candidate) => candidate.instance === instance);
36076
- if (!binding) fail2("UNSUPPORTED_GEOMETRY_EDIT", "A unique effective master root is required.");
36077
- for (const node of [instance, binding.template]) {
36078
- charge();
36079
- if (attribute2(node, "Type") !== "Shape" || children4(node, "Shapes").length || children4(node, "ForeignData").length || children4(node, "Rel").length || ["1", "true"].includes(attribute2(node, "Del") ?? ""))
36080
- fail2("UNSUPPORTED_GEOMETRY_EDIT", "Master moves require top-level local 2D shape roots.");
36081
- }
36082
- for (const name of ["BeginX", "BeginY", "EndX", "EndY"])
36083
- if (binding.cells.has(name.toLowerCase()))
36084
- fail2("UNSUPPORTED_GEOMETRY_EDIT", "Master lines and endpoint routing remain unsupported.");
36085
- const oneD = binding.cells.get("oned")?.node;
36086
- charge();
36087
- if (oneD && (numeric4(oneD) !== 0 || visioFormulaCachedValue(attribute2(oneD, "V") ?? "", attribute2(oneD, "U")).unit !== "scalar" || executableCellFormula(attribute2(oneD, "F"))))
36088
- fail2("UNSUPPORTED_GEOMETRY_EDIT", "A static effective 2D master is required.");
36089
- const local = cells(instance);
36090
- for (const name of dimensions2) {
36091
- charge();
36092
- const node = local.get(name);
36093
- numeric4(node);
36094
- if (visioFormulaCachedValue("0", attribute2(node, "U") ?? "DL").unit !== "length")
36095
- fail2("EDIT_FORMULA_UNIT", "Master transform caches require length units.");
36096
- }
36097
- if (numeric4(local.get("Width")) <= 0 || numeric4(local.get("Height")) <= 0)
36098
- fail2("UNSUPPORTED_GEOMETRY_EDIT", "Positive explicit local dimensions are required.");
36099
- for (const name of protection) {
36100
- charge();
36101
- const node = binding.cells.get(name.toLowerCase())?.node;
36102
- if (!node || visioFormulaCachedValue(attribute2(node, "V") ?? "", attribute2(node, "U")).unit !== "scalar" || numeric4(node) !== 0)
36103
- fail2(
36104
- "EDIT_PROTECTED_CELL",
36105
- "Effective master protection must be explicitly proven inactive."
36106
- );
36107
- const formula = executableCellFormula(attribute2(node, "F"));
36108
- if (formula) {
36109
- const analysis = analyze2(formula);
36110
- if (analysis.dynamic || analysis.references.length || analysis.unsupportedFunctions.length)
36111
- fail2("EDIT_PROTECTED_CELL", "Effective master protection dependencies are unsupported.");
36112
- const value = evaluateVisioFormula(
36113
- formula,
36114
- () => fail2("EDIT_PROTECTED_CELL", "Unexpected protection dependency."),
36115
- { onStep: () => charge() }
36116
- );
36117
- if (value.unit !== "scalar" || value.value !== 0)
36118
- fail2("EDIT_PROTECTED_CELL", "Effective master protection is active or stale.");
36119
- }
36120
- }
36121
- for (const name of ["PinX", "PinY"]) {
36122
- charge();
36123
- const node = local.get(name);
36124
- if (binding.cells.get(name.toLowerCase())?.node !== node)
36125
- fail2("EDIT_PROTECTED_CELL", "The rotation pin must be an explicit local override.");
36126
- charge(executableCellFormula(attribute2(node, "F"))?.length ?? 0);
36127
- editableCell(node);
36128
- const inherited = binding.inheritedCells.get(name.toLowerCase())?.node;
36129
- numeric4(inherited);
36130
- if (visioFormulaCachedValue("0", attribute2(inherited, "U") ?? "DL").unit !== "length")
36131
- fail2("EDIT_FORMULA_UNIT", "Inherited rotation pins require length units.");
36132
- const formula = executableCellFormula(attribute2(inherited, "F"));
36133
- if (formula) {
36134
- const analysis = analyze2(formula);
36135
- if (analysis.guarded || analysis.dynamic || analysis.unsupportedFunctions.length)
36136
- fail2("EDIT_PROTECTED_CELL", "Protected or redirected master pins cannot be overridden.");
36137
- }
36138
- result2.add(node);
36139
- }
36140
- }
36141
- if (result2.size) {
36142
- const indexed = indexCells(roots, { check: check3 });
36143
- const pins = new Set(
36144
- [...indexed.values()].filter((cell) => cell.node && result2.has(cell.node)).map(key)
36220
+ // src/visio/edit-master-index.ts
36221
+ function inheritedProtection(node, ancestor) {
36222
+ try {
36223
+ const boolean2 = (cell) => {
36224
+ if (!cell || cell.hasAttribute("E"))
36225
+ fail2("EDIT_PROTECTED_CELL", "Inherited protection has an unresolved or error cache.");
36226
+ const value = visioFormulaCachedValue(attribute2(cell, "V") ?? "", attribute2(cell, "U"));
36227
+ if (value.unit !== "scalar" || ![0, 1].includes(value.value))
36228
+ fail2("EDIT_PROTECTED_CELL", "Inherited protection requires scalar boolean caches.");
36229
+ return value.value;
36230
+ };
36231
+ if (boolean2(node) !== boolean2(ancestor))
36232
+ fail2(
36233
+ "EDIT_PROTECTED_CELL",
36234
+ "Delegated master style protection cache disagrees with its ancestor."
36235
+ );
36236
+ } catch (error) {
36237
+ fail2(
36238
+ "EDIT_PROTECTED_CELL",
36239
+ `Inherited master style protection cannot be proven: ${error instanceof Error ? error.message : "invalid cache"}`
36145
36240
  );
36146
- for (const cell of indexed.values())
36147
- if (charge(), cell.dependencies.some((dependency) => pins.has(dependency)))
36148
- fail2("EDIT_UNSUPPORTED_DEPENDENCY", "A page cache depends on a master rotation pin.");
36149
36241
  }
36150
- return result2;
36151
36242
  }
36152
-
36153
- // src/visio/edit-master-index.ts
36154
36243
  function masterCells(sheet, charge = () => {
36155
36244
  }) {
36156
36245
  const result2 = /* @__PURE__ */ new Map();
@@ -36208,7 +36297,7 @@ function assertMasterMarkup(root, known, check3) {
36208
36297
  }
36209
36298
  }
36210
36299
  function masterStyleCells(document, template, instance, check3, charge = () => {
36211
- }) {
36300
+ }, verifyMovementProtection = false) {
36212
36301
  const styles = /* @__PURE__ */ new Map();
36213
36302
  for (const container of children4(document, "StyleSheets"))
36214
36303
  for (const style of children4(container, "StyleSheet")) {
@@ -36233,6 +36322,10 @@ function masterStyleCells(document, template, instance, check3, charge = () => {
36233
36322
  for (const [name, node] of own) {
36234
36323
  charge();
36235
36324
  if (attribute2(node, "F") !== "Inh") result3.set(name, node);
36325
+ else if (verifyMovementProtection && /^lock(move[xy]|width|height|aspect|delete)$/.test(name)) {
36326
+ charge();
36327
+ inheritedProtection(node, result3.get(name));
36328
+ }
36236
36329
  }
36237
36330
  return result3;
36238
36331
  };
@@ -36304,12 +36397,20 @@ async function assertVisioMasterIndependence(pkg, roots, commands, check3) {
36304
36397
  const instances = [...roots].flatMap(
36305
36398
  ([pageId, root]) => masterShapes(root).filter((shape2) => shape2.hasAttribute("Master")).map((instance) => ({ pageId, instance }))
36306
36399
  );
36307
- if (!instances.length) return /* @__PURE__ */ new Set();
36400
+ if (!instances.length) return emptyMasterMoveProof();
36308
36401
  const documentPart = await related(pkg, "", "document");
36309
36402
  if (!documentPart) problem("Missing master document relationship.");
36310
36403
  const document = await visioXml(pkg, documentPart, "VisioDocument");
36311
36404
  const mastersPart = await related(pkg, documentPart, "masters", false);
36312
- if (!mastersPart) problem("Active master definitions are missing.");
36405
+ if (!mastersPart) {
36406
+ if (commands.some(
36407
+ (command2) => instances.some(
36408
+ (item) => item.pageId === command2.pageId && attribute2(item.instance, "ID") === command2.shapeId
36409
+ )
36410
+ ))
36411
+ fail2("UNSUPPORTED_GEOMETRY_EDIT", "Active master definitions are missing.");
36412
+ problem("Active master definitions are missing.");
36413
+ }
36313
36414
  const definitions = /* @__PURE__ */ new Map();
36314
36415
  for (const node of children4(await visioXml(pkg, mastersPart, "Masters"), "Master")) {
36315
36416
  const id = attribute2(node, "ID");
@@ -36324,6 +36425,12 @@ async function assertVisioMasterIndependence(pkg, roots, commands, check3) {
36324
36425
  if (--buildWork < 0) problem("Master effective-cell construction budget exceeded.");
36325
36426
  };
36326
36427
  const pageBindings = /* @__PURE__ */ new Map();
36428
+ const movingTargets = /* @__PURE__ */ new Set();
36429
+ for (const command2 of commands) {
36430
+ charge();
36431
+ if (command2.type === "move-shape")
36432
+ movingTargets.add(JSON.stringify([command2.pageId, command2.shapeId]));
36433
+ }
36327
36434
  const pageShapes = /* @__PURE__ */ new Map();
36328
36435
  for (const [pageId, root] of roots)
36329
36436
  for (const shape2 of masterShapes(root)) {
@@ -36383,7 +36490,15 @@ async function assertVisioMasterIndependence(pkg, roots, commands, check3) {
36383
36490
  const templateId = attribute2(template, "ID");
36384
36491
  if (!templateId || context.has(templateId)) problem("Master-local sheet IDs are ambiguous.");
36385
36492
  const local = byTemplate.get(templateId), cells3 = /* @__PURE__ */ new Map();
36386
- for (const [name, node] of masterStyleCells(document, template, local, check3, charge))
36493
+ const movedInstance = local === instance && movingTargets.has(JSON.stringify([pageId, attribute2(local, "ID")]));
36494
+ for (const [name, node] of masterStyleCells(
36495
+ document,
36496
+ template,
36497
+ local,
36498
+ check3,
36499
+ charge,
36500
+ movedInstance
36501
+ ))
36387
36502
  cells3.set(name, { node, kind: "style" });
36388
36503
  overlay(cells3, masterCells(template, charge), "template", charge);
36389
36504
  const inheritedCells = new Map(cells3);
@@ -36513,7 +36628,7 @@ async function assertVisioMasterIndependence(pkg, roots, commands, check3) {
36513
36628
  for (const binding of bindings)
36514
36629
  for (const name of binding.cells.keys()) {
36515
36630
  const source2 = binding.cells.get(name);
36516
- if (["pinx", "piny"].includes(name) && source2?.kind === "instance" && movePins.has(source2.node))
36631
+ if (["pinx", "piny"].includes(name) && source2?.kind === "instance" && movePins.pins.has(source2.node))
36517
36632
  continue;
36518
36633
  inspect2(binding, name);
36519
36634
  }
@@ -36637,7 +36752,7 @@ async function assertGeometryPackageScope(pkg, pagePaths, commands, check3, root
36637
36752
  });
36638
36753
  }
36639
36754
  }
36640
- let movePins = /* @__PURE__ */ new Set();
36755
+ let movePins = emptyMasterMoveProof();
36641
36756
  if (hasMasterInstances) {
36642
36757
  for (const command2 of commands) {
36643
36758
  const root = roots.get(command2.pageId);
@@ -36647,10 +36762,10 @@ async function assertGeometryPackageScope(pkg, pagePaths, commands, check3, root
36647
36762
  );
36648
36763
  if (target?.hasAttribute("MasterShape") && !target.hasAttribute("Master"))
36649
36764
  fail2("UNSUPPORTED_GEOMETRY_EDIT", "Editing master-linked shapes is unsupported.");
36650
- if (target?.hasAttribute("Master") && (command2.type !== "move-shape" || !["PinX", "PinY", "Width", "Height", "LocPinX", "LocPinY"].every(
36765
+ if (target?.hasAttribute("Master") && (command2.type !== "move-shape" || !["PinX", "PinY"].every(
36651
36766
  (name) => children4(target, "Cell").some((node) => attribute2(node, "N") === name)
36652
36767
  )))
36653
- fail2("UNSUPPORTED_GEOMETRY_EDIT", "Master moves require complete local transform caches.");
36768
+ fail2("UNSUPPORTED_GEOMETRY_EDIT", "Master moves require explicit local rotation pins.");
36654
36769
  }
36655
36770
  movePins = await assertVisioMasterIndependence(pkg, roots, commands, check3);
36656
36771
  }
@@ -36747,7 +36862,7 @@ async function editVsdx(input, edits, options = {}) {
36747
36862
  const roots = /* @__PURE__ */ new Map();
36748
36863
  const geometry = commands.some((command2) => command2.type !== "replace-plain-text");
36749
36864
  let document;
36750
- let masterMovePins = /* @__PURE__ */ new Set();
36865
+ let masterMovePins = emptyMasterMoveProof();
36751
36866
  if (geometry) {
36752
36867
  for (const [pageId, path2] of pages) {
36753
36868
  const sourceRoot = await visioXml(pkg, path2, "PageContents");