@devmm/puredocs-excel 1.0.6 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -196,6 +196,124 @@ function listEntries(entries, prefix) {
196
196
  }
197
197
  return results.sort();
198
198
  }
199
+
200
+ // src/io/zip-container.ts
201
+ var LOCAL_HEADER = 67324752;
202
+ var CENTRAL_HEADER = 33639248;
203
+ var END_OF_CENTRAL_DIR = 101010256;
204
+ var DOS_DATE = 33;
205
+ var DOS_TIME = 0;
206
+ var MAX_ENTRIES = 65535;
207
+ var MAX_BYTES = 4294967295;
208
+ var CRC_TABLE = (() => {
209
+ const table = new Int32Array(256);
210
+ for (let i = 0; i < 256; i++) {
211
+ let c = i;
212
+ for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
213
+ table[i] = c;
214
+ }
215
+ return table;
216
+ })();
217
+ function crc32(data) {
218
+ let c = -1;
219
+ for (let i = 0; i < data.length; i++) {
220
+ c = c >>> 8 ^ CRC_TABLE[(c ^ data[i]) & 255];
221
+ }
222
+ return (c ^ -1) >>> 0;
223
+ }
224
+ function exceedsZipLimits(parts) {
225
+ if (parts.length > MAX_ENTRIES) return true;
226
+ let total = 0;
227
+ for (const p of parts) {
228
+ total += 30 + utf8Length(p.name) * 2 + 46 + p.deflated.length;
229
+ if (total > MAX_BYTES || p.rawSize > MAX_BYTES) return true;
230
+ }
231
+ return false;
232
+ }
233
+ function buildZipContainer(parts) {
234
+ const names = parts.map((p) => encodeName(p.name));
235
+ let localSize = 0;
236
+ let centralSize = 0;
237
+ for (let i = 0; i < parts.length; i++) {
238
+ localSize += 30 + names[i].bytes.length + parts[i].deflated.length;
239
+ centralSize += 46 + names[i].bytes.length;
240
+ }
241
+ const out = new Uint8Array(localSize + centralSize + 22);
242
+ const view = new DataView(out.buffer);
243
+ const offsets = [];
244
+ let at = 0;
245
+ for (let i = 0; i < parts.length; i++) {
246
+ const part = parts[i];
247
+ const name = names[i];
248
+ offsets.push(at);
249
+ view.setUint32(at, LOCAL_HEADER, true);
250
+ view.setUint16(at + 4, 20, true);
251
+ view.setUint16(at + 6, name.utf8Flag, true);
252
+ view.setUint16(at + 8, 8, true);
253
+ view.setUint16(at + 10, DOS_TIME, true);
254
+ view.setUint16(at + 12, DOS_DATE, true);
255
+ view.setUint32(at + 14, part.crc, true);
256
+ view.setUint32(at + 18, part.deflated.length, true);
257
+ view.setUint32(at + 22, part.rawSize, true);
258
+ view.setUint16(at + 26, name.bytes.length, true);
259
+ view.setUint16(at + 28, 0, true);
260
+ at += 30;
261
+ out.set(name.bytes, at);
262
+ at += name.bytes.length;
263
+ out.set(part.deflated, at);
264
+ at += part.deflated.length;
265
+ }
266
+ const centralStart = at;
267
+ for (let i = 0; i < parts.length; i++) {
268
+ const part = parts[i];
269
+ const name = names[i];
270
+ view.setUint32(at, CENTRAL_HEADER, true);
271
+ view.setUint16(at + 4, 20, true);
272
+ view.setUint16(at + 6, 20, true);
273
+ view.setUint16(at + 8, name.utf8Flag, true);
274
+ view.setUint16(at + 10, 8, true);
275
+ view.setUint16(at + 12, DOS_TIME, true);
276
+ view.setUint16(at + 14, DOS_DATE, true);
277
+ view.setUint32(at + 16, part.crc, true);
278
+ view.setUint32(at + 20, part.deflated.length, true);
279
+ view.setUint32(at + 24, part.rawSize, true);
280
+ view.setUint16(at + 28, name.bytes.length, true);
281
+ view.setUint16(at + 30, 0, true);
282
+ view.setUint16(at + 32, 0, true);
283
+ view.setUint16(at + 34, 0, true);
284
+ view.setUint16(at + 36, 0, true);
285
+ view.setUint32(at + 38, 0, true);
286
+ view.setUint32(at + 42, offsets[i], true);
287
+ at += 46;
288
+ out.set(name.bytes, at);
289
+ at += name.bytes.length;
290
+ }
291
+ view.setUint32(at, END_OF_CENTRAL_DIR, true);
292
+ view.setUint16(at + 4, 0, true);
293
+ view.setUint16(at + 6, 0, true);
294
+ view.setUint16(at + 8, parts.length, true);
295
+ view.setUint16(at + 10, parts.length, true);
296
+ view.setUint32(at + 12, at - centralStart, true);
297
+ view.setUint32(at + 16, centralStart, true);
298
+ view.setUint16(at + 20, 0, true);
299
+ return out;
300
+ }
301
+ function encodeName(name) {
302
+ const bytes = new TextEncoder().encode(name);
303
+ let ascii = true;
304
+ for (let i = 0; i < bytes.length; i++) {
305
+ if (bytes[i] > 127) {
306
+ ascii = false;
307
+ break;
308
+ }
309
+ }
310
+ return { bytes, utf8Flag: ascii ? 0 : 2048 };
311
+ }
312
+ function utf8Length(name) {
313
+ return new TextEncoder().encode(name).length;
314
+ }
315
+
316
+ // src/io/zip-writer.ts
199
317
  function zipXlsx(entries) {
200
318
  const zippable = {};
201
319
  for (const [path, content] of entries) {
@@ -210,6 +328,95 @@ function zipXlsx(entries) {
210
328
  );
211
329
  }
212
330
  }
331
+ var ASYNC_MIN_BYTES = 512 * 1024;
332
+ function createDeflateCache() {
333
+ return /* @__PURE__ */ new Map();
334
+ }
335
+ function compress(name, source, bytes) {
336
+ return {
337
+ name,
338
+ source,
339
+ deflated: fflate.deflateSync(bytes, { level: 6 }),
340
+ crc: crc32(bytes),
341
+ rawSize: bytes.length
342
+ };
343
+ }
344
+ function zipXlsxCached(entries, cache) {
345
+ const parts = collectParts(entries, cache);
346
+ pruneCache(entries, cache);
347
+ return exceedsZipLimits(parts) ? zipXlsx(entries) : buildZipContainer(parts);
348
+ }
349
+ async function zipXlsxCachedAsync(entries, cache) {
350
+ const order = [];
351
+ const ready = /* @__PURE__ */ new Map();
352
+ const jobs = [];
353
+ const todo = [];
354
+ let todoBytes = 0;
355
+ for (const [path, content] of entries) {
356
+ order.push(path);
357
+ const cached = cache.get(path);
358
+ if (cached && cached.source === content) {
359
+ ready.set(path, cached);
360
+ continue;
361
+ }
362
+ const bytes = typeof content === "string" ? encodeUtf8(content) : content;
363
+ todo.push({ path, content, bytes });
364
+ todoBytes += bytes.length;
365
+ }
366
+ if (todoBytes <= ASYNC_MIN_BYTES) {
367
+ for (const { path, content, bytes } of todo) {
368
+ const compressed = compress(path, content, bytes);
369
+ cache.set(path, compressed);
370
+ ready.set(path, compressed);
371
+ }
372
+ } else {
373
+ for (const { path, content, bytes } of todo) {
374
+ jobs.push(deflateAsync(bytes).then((deflated) => ({
375
+ name: path,
376
+ source: content,
377
+ deflated,
378
+ crc: crc32(bytes),
379
+ rawSize: bytes.length
380
+ })));
381
+ }
382
+ }
383
+ for (const part of await Promise.all(jobs)) {
384
+ cache.set(part.name, part);
385
+ ready.set(part.name, part);
386
+ }
387
+ pruneCache(entries, cache);
388
+ const parts = order.map((path) => ready.get(path));
389
+ return exceedsZipLimits(parts) ? zipXlsx(entries) : buildZipContainer(parts);
390
+ }
391
+ function deflateAsync(bytes) {
392
+ return new Promise((resolve, reject) => {
393
+ fflate.deflate(bytes, { level: 6 }, (err, data) => {
394
+ if (err) reject(new Error(`Failed to create xlsx: ${err.message}`));
395
+ else resolve(data);
396
+ });
397
+ });
398
+ }
399
+ function collectParts(entries, cache) {
400
+ const parts = [];
401
+ for (const [path, content] of entries) {
402
+ const cached = cache.get(path);
403
+ if (cached && cached.source === content) {
404
+ parts.push(cached);
405
+ continue;
406
+ }
407
+ const bytes = typeof content === "string" ? encodeUtf8(content) : content;
408
+ const part = compress(path, content, bytes);
409
+ cache.set(path, part);
410
+ parts.push(part);
411
+ }
412
+ return parts;
413
+ }
414
+ function pruneCache(entries, cache) {
415
+ if (cache.size === entries.size) return;
416
+ for (const path of [...cache.keys()]) {
417
+ if (!entries.has(path)) cache.delete(path);
418
+ }
419
+ }
213
420
  function setXmlEntry(entries, path, xml2) {
214
421
  entries.set(path, xml2);
215
422
  }
@@ -302,7 +509,8 @@ var REL_TYPES = {
302
509
  drawing: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
303
510
  hyperlink: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
304
511
  comments: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments",
305
- vmlDrawing: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing"
512
+ vmlDrawing: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing",
513
+ calcChain: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/calcChain"
306
514
  };
307
515
  var CONTENT_TYPES = {
308
516
  workbook: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
@@ -314,7 +522,8 @@ var CONTENT_TYPES = {
314
522
  relationships: "application/vnd.openxmlformats-package.relationships+xml",
315
523
  coreProperties: "application/vnd.openxmlformats-package.core-properties+xml",
316
524
  comments: "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml",
317
- vmlDrawing: "application/vnd.openxmlformats-officedocument.vmlDrawing"
525
+ vmlDrawing: "application/vnd.openxmlformats-officedocument.vmlDrawing",
526
+ calcChain: "application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml"
318
527
  };
319
528
 
320
529
  // src/io/ooxml-templates.ts
@@ -347,18 +556,20 @@ var ROOT_RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
347
556
  <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
348
557
  <Relationship Id="rId1" Type="${REL_TYPES.officeDocument}" Target="xl/workbook.xml"/>
349
558
  </Relationships>`;
350
- function buildWorkbookRels(sheetCount) {
559
+ function buildWorkbookRels(sheetCount, includeCalcChain = false) {
351
560
  const sheetRels = Array.from(
352
561
  { length: sheetCount },
353
562
  (_, i) => ` <Relationship Id="rId${i + 1}" Type="${REL_TYPES.worksheet}" Target="worksheets/sheet${i + 1}.xml"/>`
354
563
  ).join("\n");
355
564
  const stylesId = sheetCount + 1;
356
565
  const sharedId = sheetCount + 2;
566
+ const calcChainRel = includeCalcChain ? `
567
+ <Relationship Id="rId${sheetCount + 3}" Type="${REL_TYPES.calcChain}" Target="calcChain.xml"/>` : "";
357
568
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
358
569
  <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
359
570
  ${sheetRels}
360
571
  <Relationship Id="rId${stylesId}" Type="${REL_TYPES.styles}" Target="styles.xml"/>
361
- <Relationship Id="rId${sharedId}" Type="${REL_TYPES.sharedStrings}" Target="sharedStrings.xml"/>
572
+ <Relationship Id="rId${sharedId}" Type="${REL_TYPES.sharedStrings}" Target="sharedStrings.xml"/>${calcChainRel}
362
573
  </Relationships>`;
363
574
  }
364
575
  function buildWorkbookXml(sheets, definedNames = []) {
@@ -963,8 +1174,12 @@ var ExcelBorderEdge = class _ExcelBorderEdge {
963
1174
  cacheKey() {
964
1175
  return `${this.style}:${this.color?.toString() ?? "null"}`;
965
1176
  }
1177
+ /** An independent copy. ExcelColor is immutable, so it is shared. */
1178
+ clone() {
1179
+ return new _ExcelBorderEdge(this.style, this.color);
1180
+ }
966
1181
  };
967
- var _ExcelBorder = class _ExcelBorder {
1182
+ var ExcelBorder = class _ExcelBorder {
968
1183
  constructor() {
969
1184
  this.left = new ExcelBorderEdge();
970
1185
  this.right = new ExcelBorderEdge();
@@ -1004,6 +1219,33 @@ var _ExcelBorder = class _ExcelBorder {
1004
1219
  b.right = new ExcelBorderEdge(style, color);
1005
1220
  return b;
1006
1221
  }
1222
+ /**
1223
+ * A border with no edges set.
1224
+ *
1225
+ * A fresh instance per access, not a shared singleton. `ExcelBorder` is mutable
1226
+ * (`border.left.style = …` is the normal way to use it), so a shared instance
1227
+ * handed to two styles let an edit through one of them reach the other — and,
1228
+ * once anything had assigned `none` and then modified it, every later
1229
+ * `ExcelBorder.none` came back already carrying that edge.
1230
+ */
1231
+ static get none() {
1232
+ return new _ExcelBorder();
1233
+ }
1234
+ /**
1235
+ * A deep copy: the edges are new objects, so mutating the copy cannot reach the
1236
+ * original. {@link CellStyle.clone} relies on this.
1237
+ */
1238
+ clone() {
1239
+ const b = new _ExcelBorder();
1240
+ b.left = this.left.clone();
1241
+ b.right = this.right.clone();
1242
+ b.top = this.top.clone();
1243
+ b.bottom = this.bottom.clone();
1244
+ b.diagonal = this.diagonal.clone();
1245
+ b.diagonalDown = this.diagonalDown;
1246
+ b.diagonalUp = this.diagonalUp;
1247
+ return b;
1248
+ }
1007
1249
  // ── Serialisation ─────────────────────────────────────────────────────────
1008
1250
  toXml() {
1009
1251
  const dd = this.diagonalDown ? ' diagonalDown="1"' : "";
@@ -1049,8 +1291,6 @@ var _ExcelBorder = class _ExcelBorder {
1049
1291
  ].join("|");
1050
1292
  }
1051
1293
  };
1052
- _ExcelBorder.none = new _ExcelBorder();
1053
- var ExcelBorder = _ExcelBorder;
1054
1294
 
1055
1295
  // src/style/excel-alignment.ts
1056
1296
  var ExcelHorizontalAlignment = /* @__PURE__ */ ((ExcelHorizontalAlignment3) => {
@@ -1343,7 +1583,7 @@ var _CellStyle = class _CellStyle {
1343
1583
  const s = new _CellStyle();
1344
1584
  if (this.font) s.font = this.font.clone();
1345
1585
  s.fill = this.fill;
1346
- s.border = this.border;
1586
+ s.border = this.border?.clone();
1347
1587
  s.alignment = this.alignment ? Object.assign(new ExcelAlignment(), this.alignment) : void 0;
1348
1588
  s.numberFormat = this.numberFormat;
1349
1589
  s.isLocked = this.isLocked;
@@ -1366,8 +1606,46 @@ ensureAlignment_fn = function() {
1366
1606
  };
1367
1607
  var CellStyle = _CellStyle;
1368
1608
 
1609
+ // src/model/oa-date.ts
1610
+ var OA_EPOCH_MS = Date.UTC(1899, 11, 30, 0, 0, 0, 0);
1611
+ var MS_PER_DAY = 864e5;
1612
+ function toOADate(date) {
1613
+ const utcMs = Date.UTC(
1614
+ date.getFullYear(),
1615
+ date.getMonth(),
1616
+ date.getDate(),
1617
+ date.getHours(),
1618
+ date.getMinutes(),
1619
+ date.getSeconds(),
1620
+ date.getMilliseconds()
1621
+ );
1622
+ return (utcMs - OA_EPOCH_MS) / MS_PER_DAY;
1623
+ }
1624
+ function fromOADate(oaDate) {
1625
+ const ms = OA_EPOCH_MS + oaDate * MS_PER_DAY;
1626
+ const d = new Date(ms);
1627
+ return new Date(
1628
+ d.getUTCFullYear(),
1629
+ d.getUTCMonth(),
1630
+ d.getUTCDate(),
1631
+ d.getUTCHours(),
1632
+ d.getUTCMinutes(),
1633
+ d.getUTCSeconds(),
1634
+ d.getUTCMilliseconds()
1635
+ );
1636
+ }
1637
+ var DATE_FORMAT_IDS = /* @__PURE__ */ new Set([14, 15, 16, 17, 18, 19, 20, 21, 22, 45, 46, 47]);
1638
+ function isDateFormatId(formatId) {
1639
+ return DATE_FORMAT_IDS.has(formatId);
1640
+ }
1641
+ function isDateFormatCode(formatCode) {
1642
+ const lower = formatCode.toLowerCase();
1643
+ return lower.includes("y") || lower.includes("d") || lower.includes("m") && !lower.includes("mm:") || // m = month unless part of mm:ss
1644
+ lower.includes("h") || lower.includes("am") || lower.includes("pm");
1645
+ }
1646
+
1369
1647
  // src/style/style-manager.ts
1370
- var _StyleManager_instances, initDefaults_fn, getOrCreateFont_fn, getOrCreateFill_fn, getOrCreateBorder_fn, getOrCreateNumFmt_fn, cellFormatKey_fn, cfToXml_fn;
1648
+ var _numFmtMapCache, _dateStyleMemo, _StyleManager_instances, numFmtMap_fn, invalidateFormatCaches_fn, initDefaults_fn, getOrCreateFont_fn, getOrCreateFill_fn, getOrCreateBorder_fn, getOrCreateNumFmt_fn, cellFormatKey_fn, cfToXml_fn;
1371
1649
  var StyleManager = class {
1372
1650
  constructor() {
1373
1651
  __privateAdd(this, _StyleManager_instances);
@@ -1386,6 +1664,21 @@ var StyleManager = class {
1386
1664
  this.cellXfCache = /* @__PURE__ */ new Map();
1387
1665
  this.nextCustomNumFmtId = 164;
1388
1666
  this.isDirty = false;
1667
+ /**
1668
+ * `numFmtId` → format code for the custom (≥164) formats, materialised once.
1669
+ * Rebuilding it per lookup made every numeric cell read O(custom formats),
1670
+ * and reading a cell's value has to consult it to tell dates from numbers.
1671
+ * Invalidated whenever `numFmts` changes — see {@link #invalidateFormatCaches}.
1672
+ */
1673
+ __privateAdd(this, _numFmtMapCache);
1674
+ /**
1675
+ * cellXf index → does its number format render a date? Memoised because
1676
+ * {@link Cell.getValue} asks this for every numeric cell, and answering it
1677
+ * from scratch reconstructs a whole CellStyle. Entries stay valid because
1678
+ * `cellXfs` and `numFmts` are append-only: an existing index never changes
1679
+ * meaning.
1680
+ */
1681
+ __privateAdd(this, _dateStyleMemo, /* @__PURE__ */ new Map());
1389
1682
  __privateMethod(this, _StyleManager_instances, initDefaults_fn).call(this);
1390
1683
  }
1391
1684
  // ── Public API ─────────────────────────────────────────────────────────────
@@ -1437,10 +1730,7 @@ var StyleManager = class {
1437
1730
  const borderEntry = this.borders[cf.borderId];
1438
1731
  if (borderEntry && cf.applyBorder) s.border = borderEntry.border;
1439
1732
  if (cf.applyNumFmt) {
1440
- const customMap = new Map(
1441
- this.numFmts.map((n) => [n.id, n.code])
1442
- );
1443
- s.numberFormat = ExcelNumberFormat.fromFormatId(cf.numFmtId, customMap);
1733
+ s.numberFormat = ExcelNumberFormat.fromFormatId(cf.numFmtId, __privateMethod(this, _StyleManager_instances, numFmtMap_fn).call(this));
1444
1734
  }
1445
1735
  if (cf.alignment && cf.applyAlign) s.alignment = cf.alignment;
1446
1736
  if (cf.applyProt) {
@@ -1449,6 +1739,29 @@ var StyleManager = class {
1449
1739
  }
1450
1740
  return s;
1451
1741
  }
1742
+ /**
1743
+ * Whether the cellXf at `styleIndex` formats its value as a date. This is the
1744
+ * one style question the value-reading path has to ask, so it is answered
1745
+ * without reconstructing a CellStyle: the result is derived from the stored
1746
+ * format id/code and memoised per index.
1747
+ *
1748
+ * Equivalent to `getCellStyle(i).numberFormat` being date-like, by
1749
+ * construction — both read the same `applyNumFmt`/`numFmtId` pair through
1750
+ * {@link ExcelNumberFormat.fromFormatId}.
1751
+ */
1752
+ isDateStyle(styleIndex) {
1753
+ const memo = __privateGet(this, _dateStyleMemo).get(styleIndex);
1754
+ if (memo !== void 0) return memo;
1755
+ const cf = this.cellXfs[styleIndex];
1756
+ if (!cf) return false;
1757
+ let isDate = false;
1758
+ if (cf.applyNumFmt) {
1759
+ const fmt = ExcelNumberFormat.fromFormatId(cf.numFmtId, __privateMethod(this, _StyleManager_instances, numFmtMap_fn).call(this));
1760
+ isDate = isDateFormatId(fmt.formatId) || isDateFormatCode(fmt.formatCode);
1761
+ }
1762
+ __privateGet(this, _dateStyleMemo).set(styleIndex, isDate);
1763
+ return isDate;
1764
+ }
1452
1765
  // ── XML serialisation ──────────────────────────────────────────────────────
1453
1766
  /** Builds the complete xl/styles.xml string. */
1454
1767
  buildStylesXml() {
@@ -1478,6 +1791,7 @@ ${cellXfsXml}
1478
1791
  this.numFmtCache.clear();
1479
1792
  this.cellXfCache.clear();
1480
1793
  this.nextCustomNumFmtId = 164;
1794
+ __privateMethod(this, _StyleManager_instances, invalidateFormatCaches_fn).call(this);
1481
1795
  const root = parseXml(stylesXml);
1482
1796
  for (const el of getElementsByTagName(root, "numFmt")) {
1483
1797
  const id = parseInt(getAttr(el, "numFmtId") ?? "0", 10);
@@ -1536,9 +1850,23 @@ ${cellXfsXml}
1536
1850
  }
1537
1851
  if (this.fonts.length === 0) __privateMethod(this, _StyleManager_instances, initDefaults_fn).call(this);
1538
1852
  if (this.cellXfs.length === 0) __privateMethod(this, _StyleManager_instances, initDefaults_fn).call(this);
1853
+ __privateMethod(this, _StyleManager_instances, invalidateFormatCaches_fn).call(this);
1539
1854
  }
1540
1855
  };
1856
+ _numFmtMapCache = new WeakMap();
1857
+ _dateStyleMemo = new WeakMap();
1541
1858
  _StyleManager_instances = new WeakSet();
1859
+ numFmtMap_fn = function() {
1860
+ if (!__privateGet(this, _numFmtMapCache)) {
1861
+ __privateSet(this, _numFmtMapCache, new Map(this.numFmts.map((n) => [n.id, n.code])));
1862
+ }
1863
+ return __privateGet(this, _numFmtMapCache);
1864
+ };
1865
+ /** Drops the derived format caches after `numFmts` or `cellXfs` changed. */
1866
+ invalidateFormatCaches_fn = function() {
1867
+ __privateSet(this, _numFmtMapCache, void 0);
1868
+ __privateGet(this, _dateStyleMemo).clear();
1869
+ };
1542
1870
  // ── Private helpers ────────────────────────────────────────────────────────
1543
1871
  initDefaults_fn = function() {
1544
1872
  const defaultFont = new ExcelFont({ name: "Calibri", size: 11 });
@@ -1611,6 +1939,7 @@ getOrCreateNumFmt_fn = function(fmt) {
1611
1939
  const id = this.nextCustomNumFmtId++;
1612
1940
  this.numFmts.push({ id, code: fmt.formatCode });
1613
1941
  this.numFmtCache.set(fmt.formatCode, id);
1942
+ __privateMethod(this, _StyleManager_instances, invalidateFormatCaches_fn).call(this);
1614
1943
  this.isDirty = true;
1615
1944
  return id;
1616
1945
  };
@@ -2023,56 +2352,54 @@ function parseRangeRef(rangeReference) {
2023
2352
  };
2024
2353
  }
2025
2354
 
2026
- // src/model/oa-date.ts
2027
- var OA_EPOCH_MS = Date.UTC(1899, 11, 30, 0, 0, 0, 0);
2028
- var MS_PER_DAY = 864e5;
2029
- function toOADate(date) {
2030
- const utcMs = Date.UTC(
2031
- date.getFullYear(),
2032
- date.getMonth(),
2033
- date.getDate(),
2034
- date.getHours(),
2035
- date.getMinutes(),
2036
- date.getSeconds(),
2037
- date.getMilliseconds()
2038
- );
2039
- return (utcMs - OA_EPOCH_MS) / MS_PER_DAY;
2040
- }
2041
- function fromOADate(oaDate) {
2042
- const ms = OA_EPOCH_MS + oaDate * MS_PER_DAY;
2043
- const d = new Date(ms);
2044
- return new Date(
2045
- d.getUTCFullYear(),
2046
- d.getUTCMonth(),
2047
- d.getUTCDate(),
2048
- d.getUTCHours(),
2049
- d.getUTCMinutes(),
2050
- d.getUTCSeconds(),
2051
- d.getUTCMilliseconds()
2052
- );
2053
- }
2054
- var DATE_FORMAT_IDS = /* @__PURE__ */ new Set([14, 15, 16, 17, 18, 19, 20, 21, 22, 45, 46, 47]);
2055
- function isDateFormatId(formatId) {
2056
- return DATE_FORMAT_IDS.has(formatId);
2355
+ // src/model/cell.ts
2356
+ function readCellValue(data, sharedStrings, styles) {
2357
+ const raw = data.rawValue;
2358
+ if (raw === void 0 || raw === "") return null;
2359
+ switch (data.dataType) {
2360
+ case "s": {
2361
+ const idx = parseInt(raw, 10);
2362
+ return sharedStrings.tryGetString(idx) ?? raw;
2363
+ }
2364
+ case "b":
2365
+ return raw === "1";
2366
+ case "str":
2367
+ return raw;
2368
+ default: {
2369
+ const num = parseFloat(raw);
2370
+ if (isNaN(num)) return raw;
2371
+ if (styles.isDateStyle(data.styleIndex)) {
2372
+ try {
2373
+ return fromOADate(num);
2374
+ } catch {
2375
+ return num;
2376
+ }
2377
+ }
2378
+ return num;
2379
+ }
2380
+ }
2057
2381
  }
2058
- function isDateFormatCode(formatCode) {
2059
- const lower = formatCode.toLowerCase();
2060
- return lower.includes("y") || lower.includes("d") || lower.includes("m") && !lower.includes("mm:") || // m = month unless part of mm:ss
2061
- lower.includes("h") || lower.includes("am") || lower.includes("pm");
2382
+ function buildCellXml(data) {
2383
+ const style = data.styleIndex > 0 ? ` s="${data.styleIndex}"` : "";
2384
+ const formulaXml = data.formula ? data.arrayRef ? `<f t="array" ref="${data.arrayRef}" aca="1" ca="1">${escXml(data.formula)}</f>` : `<f>${escXml(data.formula)}</f>` : "";
2385
+ const valueXml = data.rawValue !== void 0 && data.rawValue !== "" ? `<v>${escXml(data.rawValue)}</v>` : "";
2386
+ const type = data.dataType && (formulaXml || valueXml) ? ` t="${data.dataType}"` : "";
2387
+ if (!formulaXml && !valueXml && data.styleIndex === 0) return "";
2388
+ return `<c r="${data.reference}"${style}${type}>${formulaXml}${valueXml}</c>`;
2062
2389
  }
2063
-
2064
- // src/model/cell.ts
2065
- var _data, _sharedStrings, _styles, _Cell_instances, isDateStyle_fn;
2390
+ var _data, _sharedStrings, _styles, _version, _Cell_instances, touch_fn;
2066
2391
  var Cell = class {
2067
2392
  /** @internal */
2068
- constructor(data, sharedStrings, styles) {
2393
+ constructor(data, sharedStrings, styles, version) {
2069
2394
  __privateAdd(this, _Cell_instances);
2070
2395
  __privateAdd(this, _data);
2071
2396
  __privateAdd(this, _sharedStrings);
2072
2397
  __privateAdd(this, _styles);
2398
+ __privateAdd(this, _version);
2073
2399
  __privateSet(this, _data, data);
2074
2400
  __privateSet(this, _sharedStrings, sharedStrings);
2075
2401
  __privateSet(this, _styles, styles);
2402
+ __privateSet(this, _version, version);
2076
2403
  }
2077
2404
  // ── Identity ───────────────────────────────────────────────────────────────
2078
2405
  /** Cell reference string e.g. "A1". */
@@ -2088,6 +2415,7 @@ var Cell = class {
2088
2415
  return parseCellRef(__privateGet(this, _data).reference).column;
2089
2416
  }
2090
2417
  setValue(value) {
2418
+ __privateMethod(this, _Cell_instances, touch_fn).call(this);
2091
2419
  __privateGet(this, _data).formula = void 0;
2092
2420
  if (value === null || value === void 0) {
2093
2421
  __privateGet(this, _data).dataType = void 0;
@@ -2125,29 +2453,7 @@ var Cell = class {
2125
2453
  * Date detection is based on the cell's number format.
2126
2454
  */
2127
2455
  getValue() {
2128
- if (__privateGet(this, _data).rawValue === void 0 || __privateGet(this, _data).rawValue === "") return null;
2129
- switch (__privateGet(this, _data).dataType) {
2130
- case "s": {
2131
- const idx = parseInt(__privateGet(this, _data).rawValue, 10);
2132
- return __privateGet(this, _sharedStrings).tryGetString(idx) ?? __privateGet(this, _data).rawValue;
2133
- }
2134
- case "b":
2135
- return __privateGet(this, _data).rawValue === "1";
2136
- case "str":
2137
- return __privateGet(this, _data).rawValue;
2138
- default: {
2139
- const num = parseFloat(__privateGet(this, _data).rawValue);
2140
- if (isNaN(num)) return __privateGet(this, _data).rawValue;
2141
- if (__privateMethod(this, _Cell_instances, isDateStyle_fn).call(this)) {
2142
- try {
2143
- return fromOADate(num);
2144
- } catch {
2145
- return num;
2146
- }
2147
- }
2148
- return num;
2149
- }
2150
- }
2456
+ return readCellValue(__privateGet(this, _data), __privateGet(this, _sharedStrings), __privateGet(this, _styles));
2151
2457
  }
2152
2458
  /**
2153
2459
  * Returns the display text of the cell value (always a string).
@@ -2185,6 +2491,7 @@ var Cell = class {
2185
2491
  */
2186
2492
  setFormula(formula) {
2187
2493
  if (!formula) throw new Error("Formula cannot be empty.");
2494
+ __privateMethod(this, _Cell_instances, touch_fn).call(this);
2188
2495
  __privateGet(this, _data).formula = formula.startsWith("=") ? formula.slice(1) : formula;
2189
2496
  __privateGet(this, _data).rawValue = void 0;
2190
2497
  __privateGet(this, _data).dataType = void 0;
@@ -2197,6 +2504,7 @@ var Cell = class {
2197
2504
  * plain scalar formula. Serialises as `<f t="array" ref="…">`.
2198
2505
  */
2199
2506
  setArrayRef(spillRef) {
2507
+ __privateMethod(this, _Cell_instances, touch_fn).call(this);
2200
2508
  __privateGet(this, _data).arrayRef = spillRef ?? void 0;
2201
2509
  return this;
2202
2510
  }
@@ -2214,6 +2522,7 @@ var Cell = class {
2214
2522
  * added to the shared-string table.
2215
2523
  */
2216
2524
  setComputedValue(value) {
2525
+ __privateMethod(this, _Cell_instances, touch_fn).call(this);
2217
2526
  if (value === null || value === void 0) {
2218
2527
  __privateGet(this, _data).dataType = void 0;
2219
2528
  __privateGet(this, _data).rawValue = void 0;
@@ -2253,6 +2562,7 @@ var Cell = class {
2253
2562
  }
2254
2563
  /** Replaces the entire cell style. */
2255
2564
  set style(value) {
2565
+ __privateMethod(this, _Cell_instances, touch_fn).call(this);
2256
2566
  __privateGet(this, _data).styleIndex = __privateGet(this, _styles).getOrCreateCellFormatIndex(value);
2257
2567
  }
2258
2568
  /**
@@ -2263,6 +2573,7 @@ var Cell = class {
2263
2573
  * cell.applyStyle(s => s.setBold(true).setFontColor(ExcelColor.red))
2264
2574
  */
2265
2575
  applyStyle(action) {
2576
+ __privateMethod(this, _Cell_instances, touch_fn).call(this);
2266
2577
  const s = __privateGet(this, _styles).getCellStyle(__privateGet(this, _data).styleIndex).clone();
2267
2578
  action(s);
2268
2579
  __privateGet(this, _data).styleIndex = __privateGet(this, _styles).getOrCreateCellFormatIndex(s);
@@ -2305,6 +2616,7 @@ var Cell = class {
2305
2616
  // ── Clear ──────────────────────────────────────────────────────────────────
2306
2617
  /** Clears the cell value and formula, preserving style. */
2307
2618
  clear() {
2619
+ __privateMethod(this, _Cell_instances, touch_fn).call(this);
2308
2620
  __privateGet(this, _data).rawValue = void 0;
2309
2621
  __privateGet(this, _data).dataType = void 0;
2310
2622
  __privateGet(this, _data).formula = void 0;
@@ -2312,19 +2624,14 @@ var Cell = class {
2312
2624
  }
2313
2625
  /** Clears the cell value, formula, AND style. */
2314
2626
  clearAll() {
2627
+ __privateMethod(this, _Cell_instances, touch_fn).call(this);
2315
2628
  this.clear();
2316
2629
  __privateGet(this, _data).styleIndex = 0;
2317
2630
  }
2318
2631
  // ── XML serialisation ──────────────────────────────────────────────────────
2319
2632
  /** @internal Builds the <c> XML element string for this cell. */
2320
2633
  toXml() {
2321
- const ref = __privateGet(this, _data).reference;
2322
- const style = __privateGet(this, _data).styleIndex > 0 ? ` s="${__privateGet(this, _data).styleIndex}"` : "";
2323
- const formulaXml = __privateGet(this, _data).formula ? __privateGet(this, _data).arrayRef ? `<f t="array" ref="${__privateGet(this, _data).arrayRef}" aca="1" ca="1">${escXml(__privateGet(this, _data).formula)}</f>` : `<f>${escXml(__privateGet(this, _data).formula)}</f>` : "";
2324
- const valueXml = __privateGet(this, _data).rawValue !== void 0 && __privateGet(this, _data).rawValue !== "" ? `<v>${escXml(__privateGet(this, _data).rawValue)}</v>` : "";
2325
- const type = __privateGet(this, _data).dataType && (formulaXml || valueXml) ? ` t="${__privateGet(this, _data).dataType}"` : "";
2326
- if (!formulaXml && !valueXml && __privateGet(this, _data).styleIndex === 0) return "";
2327
- return `<c r="${ref}"${style}${type}>${formulaXml}${valueXml}</c>`;
2634
+ return buildCellXml(__privateGet(this, _data));
2328
2635
  }
2329
2636
  /** @internal Parses a <c> XML element into CellData. */
2330
2637
  static fromXmlElement(el, ref) {
@@ -2333,29 +2640,35 @@ var Cell = class {
2333
2640
  let rawValue;
2334
2641
  let formula;
2335
2642
  let arrayRef;
2643
+ let sharedIndex;
2336
2644
  for (const child of el.children) {
2337
2645
  const tag = child.tagName.replace(/^.*:/, "");
2338
2646
  if (tag === "v") rawValue = child.textContent;
2339
2647
  if (tag === "f") {
2340
2648
  formula = child.textContent;
2341
- if (child.attributes && child.attributes["t"] === "array" && child.attributes["ref"]) {
2342
- arrayRef = child.attributes["ref"];
2649
+ const attrs = child.attributes;
2650
+ const type = attrs?.["t"];
2651
+ if (type === "array" && attrs?.["ref"]) {
2652
+ arrayRef = attrs["ref"];
2653
+ }
2654
+ if (type === "shared" && attrs?.["si"] !== void 0) {
2655
+ const si = parseInt(attrs["si"], 10);
2656
+ if (!Number.isNaN(si)) sharedIndex = si;
2343
2657
  }
2344
2658
  }
2345
2659
  }
2346
- return { reference: ref, dataType, rawValue, formula, arrayRef, styleIndex };
2660
+ if (formula === "") formula = void 0;
2661
+ return { reference: ref, dataType, rawValue, formula, arrayRef, styleIndex, sharedIndex };
2347
2662
  }
2348
2663
  };
2349
2664
  _data = new WeakMap();
2350
2665
  _sharedStrings = new WeakMap();
2351
2666
  _styles = new WeakMap();
2667
+ _version = new WeakMap();
2352
2668
  _Cell_instances = new WeakSet();
2353
- // ── Private helpers ────────────────────────────────────────────────────────
2354
- isDateStyle_fn = function() {
2355
- const style = __privateGet(this, _styles).getCellStyle(__privateGet(this, _data).styleIndex);
2356
- if (!style.numberFormat) return false;
2357
- if (isDateFormatId(style.numberFormat.formatId)) return true;
2358
- return isDateFormatCode(style.numberFormat.formatCode);
2669
+ /** Records that this cell's stored data changed. */
2670
+ touch_fn = function() {
2671
+ if (__privateGet(this, _version)) __privateGet(this, _version).v++;
2359
2672
  };
2360
2673
  function escXml(s) {
2361
2674
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -2622,6 +2935,219 @@ function shiftFormulaRefs(formula, shift, formulaSheet) {
2622
2935
  return { text: out + formula.slice(flushed), changed: true, hasRefError };
2623
2936
  }
2624
2937
 
2938
+ // src/model/formula-ref-translate.ts
2939
+ function isWordChar2(code) {
2940
+ return code >= 65 && code <= 90 || // A-Z
2941
+ code >= 97 && code <= 122 || // a-z
2942
+ code >= 48 && code <= 57 || // 0-9
2943
+ code === 95 || // _
2944
+ code === 46 || // .
2945
+ code === 36;
2946
+ }
2947
+ function parseCellComponent2(word) {
2948
+ const m = /^(\$?)([A-Za-z]{1,3})(\$?)(\d+)$/.exec(word);
2949
+ if (!m) return null;
2950
+ const col = columnNumber(m[2]);
2951
+ const row = parseInt(m[4], 10);
2952
+ if (col > EXCEL_MAX_COLUMNS || row < 1 || row > EXCEL_MAX_ROWS) return null;
2953
+ return { colAbs: m[1] === "$", col, rowAbs: m[3] === "$", row };
2954
+ }
2955
+ function parseColComponent2(word) {
2956
+ const m = /^(\$?)([A-Za-z]{1,3})$/.exec(word);
2957
+ if (!m) return null;
2958
+ const col = columnNumber(m[2]);
2959
+ return col > EXCEL_MAX_COLUMNS ? null : { abs: m[1] === "$", col };
2960
+ }
2961
+ function parseRowComponent2(word) {
2962
+ const m = /^(\$?)(\d+)$/.exec(word);
2963
+ if (!m) return null;
2964
+ const row = parseInt(m[2], 10);
2965
+ return row < 1 || row > EXCEL_MAX_ROWS ? null : { abs: m[1] === "$", row };
2966
+ }
2967
+ function move(index, delta, abs, max) {
2968
+ if (abs || delta === 0) return index;
2969
+ const moved = index + delta;
2970
+ return moved < 1 || moved > max ? null : moved;
2971
+ }
2972
+ function translateFormulaRefs(formula, dRow, dCol) {
2973
+ if (dRow === 0 && dCol === 0) return { text: formula, hasRefError: false };
2974
+ const n = formula.length;
2975
+ let out = "";
2976
+ let flushed = 0;
2977
+ let hasRefError = false;
2978
+ let i = 0;
2979
+ const replace = (start, text) => {
2980
+ if (text.length === i - start && formula.startsWith(text, start)) return;
2981
+ out += formula.slice(flushed, start) + text;
2982
+ flushed = i;
2983
+ };
2984
+ const readWord = () => {
2985
+ let j = i;
2986
+ while (j < n && isWordChar2(formula.charCodeAt(j))) j++;
2987
+ const w = formula.slice(i, j);
2988
+ i = j;
2989
+ return w;
2990
+ };
2991
+ const readQuoted = () => {
2992
+ let j = i + 1;
2993
+ while (j < n) {
2994
+ if (formula[j] === "'") {
2995
+ if (formula[j + 1] === "'") {
2996
+ j += 2;
2997
+ continue;
2998
+ }
2999
+ j++;
3000
+ break;
3001
+ }
3002
+ j++;
3003
+ }
3004
+ const q = formula.slice(i, j);
3005
+ i = j;
3006
+ return q;
3007
+ };
3008
+ const emitCell2 = (c, row, col) => `${c.colAbs ? "$" : ""}${columnLetter(col)}${c.rowAbs ? "$" : ""}${row}`;
3009
+ const consumeRef = (refStart, rewrite = true) => {
3010
+ const first = readWord();
3011
+ const firstCell = parseCellComponent2(first);
3012
+ const firstCol = firstCell ? null : parseColComponent2(first);
3013
+ const firstRow = firstCell || firstCol ? null : parseRowComponent2(first);
3014
+ if (!firstCell && !firstCol && !firstRow) return;
3015
+ let secondCell = null;
3016
+ let secondCol = null;
3017
+ let secondRow = null;
3018
+ let isRange = false;
3019
+ if (formula[i] === ":" && i + 1 < n && isWordChar2(formula.charCodeAt(i + 1))) {
3020
+ const save = i;
3021
+ i++;
3022
+ const w = readWord();
3023
+ if (firstCell) secondCell = parseCellComponent2(w);
3024
+ else if (firstCol) secondCol = parseColComponent2(w);
3025
+ else if (firstRow) secondRow = parseRowComponent2(w);
3026
+ if (secondCell || secondCol || secondRow) isRange = true;
3027
+ else i = save;
3028
+ }
3029
+ if (!isRange && formula[i] === "(") return;
3030
+ if (!rewrite) {
3031
+ if (formula[i] === "#") i++;
3032
+ return;
3033
+ }
3034
+ const dead = () => {
3035
+ if (formula[i] === "#") i++;
3036
+ replace(refStart, "#REF!");
3037
+ hasRefError = true;
3038
+ };
3039
+ if (isRange) {
3040
+ if (firstCell && secondCell) {
3041
+ const r1 = move(firstCell.row, dRow, firstCell.rowAbs, EXCEL_MAX_ROWS);
3042
+ const c1 = move(firstCell.col, dCol, firstCell.colAbs, EXCEL_MAX_COLUMNS);
3043
+ const r2 = move(secondCell.row, dRow, secondCell.rowAbs, EXCEL_MAX_ROWS);
3044
+ const c2 = move(secondCell.col, dCol, secondCell.colAbs, EXCEL_MAX_COLUMNS);
3045
+ if (r1 === null || c1 === null || r2 === null || c2 === null) {
3046
+ dead();
3047
+ return;
3048
+ }
3049
+ replace(refStart, `${emitCell2(firstCell, r1, c1)}:${emitCell2(secondCell, r2, c2)}`);
3050
+ return;
3051
+ }
3052
+ if (firstCol && secondCol) {
3053
+ const c1 = move(firstCol.col, dCol, firstCol.abs, EXCEL_MAX_COLUMNS);
3054
+ const c2 = move(secondCol.col, dCol, secondCol.abs, EXCEL_MAX_COLUMNS);
3055
+ if (c1 === null || c2 === null) {
3056
+ dead();
3057
+ return;
3058
+ }
3059
+ replace(refStart, `${firstCol.abs ? "$" : ""}${columnLetter(c1)}:${secondCol.abs ? "$" : ""}${columnLetter(c2)}`);
3060
+ return;
3061
+ }
3062
+ if (firstRow && secondRow) {
3063
+ const r1 = move(firstRow.row, dRow, firstRow.abs, EXCEL_MAX_ROWS);
3064
+ const r2 = move(secondRow.row, dRow, secondRow.abs, EXCEL_MAX_ROWS);
3065
+ if (r1 === null || r2 === null) {
3066
+ dead();
3067
+ return;
3068
+ }
3069
+ replace(refStart, `${firstRow.abs ? "$" : ""}${r1}:${secondRow.abs ? "$" : ""}${r2}`);
3070
+ return;
3071
+ }
3072
+ return;
3073
+ }
3074
+ if (!firstCell) return;
3075
+ const row = move(firstCell.row, dRow, firstCell.rowAbs, EXCEL_MAX_ROWS);
3076
+ const col = move(firstCell.col, dCol, firstCell.colAbs, EXCEL_MAX_COLUMNS);
3077
+ if (row === null || col === null) {
3078
+ dead();
3079
+ return;
3080
+ }
3081
+ replace(refStart, emitCell2(firstCell, row, col));
3082
+ };
3083
+ while (i < n) {
3084
+ const ch = formula[i];
3085
+ if (ch === '"') {
3086
+ let j = i + 1;
3087
+ while (j < n) {
3088
+ if (formula[j] === '"') {
3089
+ if (formula[j + 1] === '"') {
3090
+ j += 2;
3091
+ continue;
3092
+ }
3093
+ j++;
3094
+ break;
3095
+ }
3096
+ j++;
3097
+ }
3098
+ i = j;
3099
+ continue;
3100
+ }
3101
+ if (ch === "'") {
3102
+ readQuoted();
3103
+ if (formula[i] === "!") {
3104
+ i++;
3105
+ consumeRef(i);
3106
+ }
3107
+ continue;
3108
+ }
3109
+ if (isWordChar2(formula.charCodeAt(i))) {
3110
+ const start = i;
3111
+ readWord();
3112
+ if (formula[i] === "!") {
3113
+ i++;
3114
+ consumeRef(i);
3115
+ continue;
3116
+ }
3117
+ if (formula[i] === ":") {
3118
+ const save = i;
3119
+ i++;
3120
+ const w2 = i < n && isWordChar2(formula.charCodeAt(i)) ? readWord() : "";
3121
+ if (w2 && formula[i] === "!") {
3122
+ i++;
3123
+ consumeRef(i, false);
3124
+ continue;
3125
+ }
3126
+ i = save;
3127
+ }
3128
+ if (formula[i] === "[") {
3129
+ let depth = 0;
3130
+ let j = i;
3131
+ while (j < n) {
3132
+ if (formula[j] === "[") depth++;
3133
+ else if (formula[j] === "]" && --depth === 0) {
3134
+ j++;
3135
+ break;
3136
+ }
3137
+ j++;
3138
+ }
3139
+ i = j;
3140
+ continue;
3141
+ }
3142
+ i = start;
3143
+ consumeRef(start);
3144
+ continue;
3145
+ }
3146
+ i++;
3147
+ }
3148
+ return { text: out + formula.slice(flushed), hasRefError };
3149
+ }
3150
+
2625
3151
  // src/model/range.ts
2626
3152
  var _sheet, _startRow, _startColumn, _endRow, _endColumn, _address, _Range_instances, forEach_fn, forEachExisting_fn;
2627
3153
  var Range = class {
@@ -2852,7 +3378,7 @@ var PROTECTION_ATTRS = [
2852
3378
  ["allowEditObjects", "objects"],
2853
3379
  ["allowEditScenarios", "scenarios"]
2854
3380
  ];
2855
- var _sharedStrings2, _styles2, _name, _rows, _cols, _mergeCells, _pane, _autoFilter, _dataValidations, _visibility, _drawings, _protection, _protectionCredentials, _hyperlinks, _comments, _commentsDirty, _pendingHyperlinkRels, _preservedTail, _preservedRels, _Worksheet_instances, takeCellData_fn, takeCellDataAt_fn, deleteCellData_fn, applyStructural_fn, shiftRowsAndCells_fn, shiftRows_fn, shiftColumns_fn, maxPopulatedRow_fn, shiftSpillRanges_fn, shiftSpillRange_fn, rewriteFormulas_fn, shiftMerges_fn, shiftAutoFilter_fn, shiftValidations_fn, shiftHyperlinks_fn, shiftComments_fn, shiftColDefs_fn, relIdBase_fn, activeTail_fn, buildTailXml_fn, buildSheetProtectionXml_fn, buildHyperlinksXml_fn, buildColsXml_fn, buildSheetDataXml_fn, buildMergeCellsXml_fn, buildDataValidationsXml_fn, buildSheetViewXml_fn, splitColSpan_fn, insertColDef_fn, findColDefIndex_fn, findColDef_fn, pruneColDefs_fn, getOrCreateRow_fn;
3381
+ var _sharedStrings2, _styles2, _name, _rows, _cols, _mergeCells, _pane, _autoFilter, _dataValidations, _visibility, _drawings, _protection, _protectionCredentials, _hyperlinks, _comments, _commentsDirty, _pendingHyperlinkRels, _preservedTail, _preservedRels, _version2, _usedBoundsMemo, _xmlMemo, _Worksheet_instances, touch_fn2, findCellData_fn, computeUsedBounds_fn, takeCellData_fn, takeCellDataAt_fn, deleteCellData_fn, applyStructural_fn, shiftRowsAndCells_fn, shiftRows_fn, shiftColumns_fn, maxPopulatedRow_fn, shiftSpillRanges_fn, shiftSpillRange_fn, rewriteFormulas_fn, shiftMerges_fn, shiftAutoFilter_fn, shiftValidations_fn, shiftHyperlinks_fn, shiftComments_fn, shiftColDefs_fn, relIdBase_fn, activeTail_fn, buildTailXml_fn, buildSheetProtectionXml_fn, buildHyperlinksXml_fn, resolveSharedFormulas_fn, buildColsXml_fn, buildSheetDataXml_fn, buildMergeCellsXml_fn, buildDataValidationsXml_fn, buildSheetViewXml_fn, splitColSpan_fn, insertColDef_fn, findColDefIndex_fn, findColDef_fn, pruneColDefs_fn, getOrCreateRow_fn;
2856
3382
  var Worksheet = class {
2857
3383
  /** @internal */
2858
3384
  constructor(name, sharedStrings, styles) {
@@ -2894,10 +3420,26 @@ var Worksheet = class {
2894
3420
  // comment anchors survive open→save. See io/preserved-parts.ts.
2895
3421
  __privateAdd(this, _preservedTail, []);
2896
3422
  __privateAdd(this, _preservedRels, []);
3423
+ /**
3424
+ * Mutation counter for this sheet, shared with every {@link Cell} view it
3425
+ * hands out (a Cell writes directly into the stored CellData). Everything
3426
+ * derived from the sheet's contents — the used-range memo, the cached
3427
+ * worksheet XML — is keyed on it, so every mutator must bump it. The test
3428
+ * `worksheet-version.test.ts` walks the public surface to keep that true.
3429
+ */
3430
+ __privateAdd(this, _version2, { v: 0 });
3431
+ /** Memoised {@link usedBounds}, valid while `#version.v` is unchanged. */
3432
+ __privateAdd(this, _usedBoundsMemo);
3433
+ /** Memoised {@link buildXml} output, valid while `#version.v` is unchanged. */
3434
+ __privateAdd(this, _xmlMemo);
2897
3435
  __privateSet(this, _name, name);
2898
3436
  __privateSet(this, _sharedStrings2, sharedStrings);
2899
3437
  __privateSet(this, _styles2, styles);
2900
3438
  }
3439
+ /** @internal Current mutation count — see {@link #version}. */
3440
+ get version() {
3441
+ return __privateGet(this, _version2).v;
3442
+ }
2901
3443
  // ── Identity ───────────────────────────────────────────────────────────────
2902
3444
  get name() {
2903
3445
  return __privateGet(this, _name);
@@ -2919,10 +3461,11 @@ var Worksheet = class {
2919
3461
  const rowData = __privateMethod(this, _Worksheet_instances, getOrCreateRow_fn).call(this, row);
2920
3462
  let cellData = rowData.cells.get(ref);
2921
3463
  if (!cellData) {
3464
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
2922
3465
  cellData = { reference: ref, styleIndex: 0 };
2923
3466
  rowData.cells.set(ref, cellData);
2924
3467
  }
2925
- return new Cell(cellData, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2));
3468
+ return new Cell(cellData, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2), __privateGet(this, _version2));
2926
3469
  }
2927
3470
  tryGetCell(refOrRow, column) {
2928
3471
  let rowData;
@@ -2938,7 +3481,7 @@ var Worksheet = class {
2938
3481
  }
2939
3482
  const cellData = rowData.cells.get(ref);
2940
3483
  if (!cellData) return null;
2941
- return new Cell(cellData, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2));
3484
+ return new Cell(cellData, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2), __privateGet(this, _version2));
2942
3485
  }
2943
3486
  cell(refOrRow, column) {
2944
3487
  return typeof refOrRow === "string" ? this.getCell(refOrRow) : this.getCell(refOrRow, column);
@@ -2950,15 +3493,28 @@ var Worksheet = class {
2950
3493
  }
2951
3494
  /** Gets the raw value of a cell (for formula evaluation). */
2952
3495
  getCellValue(reference) {
2953
- return this.tryGetCell(reference)?.getValue() ?? null;
3496
+ const cd = __privateMethod(this, _Worksheet_instances, findCellData_fn).call(this, reference);
3497
+ return cd ? readCellValue(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2)) : null;
3498
+ }
3499
+ /**
3500
+ * Value of a cell addressed by 1-based row/column. The reference-string
3501
+ * overload has to build (and the callee re-parse) a string per cell; a range
3502
+ * read walking a rectangle already knows the coordinates, and on a real
3503
+ * workbook that is tens of millions of strings per recalculation.
3504
+ */
3505
+ getCellValueAt(row, column) {
3506
+ const rowData = __privateGet(this, _rows).get(row);
3507
+ if (!rowData) return null;
3508
+ const cd = rowData.cells.get(cellRefFromRowCol(row, column));
3509
+ return cd ? readCellValue(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2)) : null;
2954
3510
  }
2955
3511
  /** Formula text (without leading '=') of a cell, or null if it has none. */
2956
3512
  getCellFormula(reference) {
2957
- return this.tryGetCell(reference)?.getFormula() ?? null;
3513
+ return __privateMethod(this, _Worksheet_instances, findCellData_fn).call(this, reference)?.formula ?? null;
2958
3514
  }
2959
3515
  /** Spill range (e.g. "A1:A3") of a dynamic-array anchor, or null. Supports A1#. */
2960
3516
  getSpillRange(reference) {
2961
- return this.tryGetCell(reference)?.getArrayRef() ?? null;
3517
+ return __privateMethod(this, _Worksheet_instances, findCellData_fn).call(this, reference)?.arrayRef ?? null;
2962
3518
  }
2963
3519
  /**
2964
3520
  * Iterates the populated rows of the sheet in ascending row order, yielding
@@ -2974,7 +3530,7 @@ var Worksheet = class {
2974
3530
  *rows() {
2975
3531
  const populated = [...__privateGet(this, _rows).values()].filter((row) => row.cells.size > 0).sort((a, b) => a.rowIndex - b.rowIndex);
2976
3532
  for (const row of populated) {
2977
- const cells = [...row.cells.values()].map((cd) => ({ cd, col: columnFromRef(cd.reference, refSplit(cd.reference)) })).sort((a, b) => a.col - b.col).map(({ cd }) => new Cell(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2)));
3533
+ const cells = [...row.cells.values()].map((cd) => ({ cd, col: columnFromRef(cd.reference, refSplit(cd.reference)) })).sort((a, b) => a.col - b.col).map(({ cd }) => new Cell(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2), __privateGet(this, _version2)));
2978
3534
  yield { rowIndex: row.rowIndex, cells };
2979
3535
  }
2980
3536
  }
@@ -2999,7 +3555,7 @@ var Worksheet = class {
2999
3555
  for (const cd of row.cells.values()) {
3000
3556
  const col = columnFromRef(cd.reference, refSplit(cd.reference));
3001
3557
  if (col < startColumn || col > endColumn) continue;
3002
- action(new Cell(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2)), row.rowIndex, col);
3558
+ action(new Cell(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2), __privateGet(this, _version2)), row.rowIndex, col);
3003
3559
  }
3004
3560
  };
3005
3561
  if (endRow - startRow + 1 <= __privateGet(this, _rows).size) {
@@ -3018,6 +3574,7 @@ var Worksheet = class {
3018
3574
  * the public {@link moveCell} has to do. Used by bulk range moves.
3019
3575
  */
3020
3576
  moveCellRC(fromRow, fromColumn, toRow, toColumn) {
3577
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3021
3578
  if (fromRow === toRow && fromColumn === toColumn) return;
3022
3579
  const fromRef = cellRefFromRowCol(fromRow, fromColumn);
3023
3580
  const toRef = cellRefFromRowCol(toRow, toColumn);
@@ -3039,21 +3596,16 @@ var Worksheet = class {
3039
3596
  * cheaper when a dense shape is not actually required.
3040
3597
  */
3041
3598
  get usedBounds() {
3042
- let rowCount = 0;
3043
- let columnCount = 0;
3044
- for (const row of __privateGet(this, _rows).values()) {
3045
- if (row.cells.size === 0) continue;
3046
- if (row.rowIndex > rowCount) rowCount = row.rowIndex;
3047
- for (const ref of row.cells.keys()) {
3048
- const col = columnFromRef(ref, refSplit(ref));
3049
- if (col > columnCount) columnCount = col;
3050
- }
3051
- }
3052
- return { rowCount, columnCount };
3599
+ const memo = __privateGet(this, _usedBoundsMemo);
3600
+ if (memo && memo.at === __privateGet(this, _version2).v) return memo.value;
3601
+ const value = __privateMethod(this, _Worksheet_instances, computeUsedBounds_fn).call(this);
3602
+ __privateSet(this, _usedBoundsMemo, { at: __privateGet(this, _version2).v, value });
3603
+ return value;
3053
3604
  }
3054
3605
  // ── Layout ─────────────────────────────────────────────────────────────────
3055
3606
  /** Sets the width of a column (1-based index). */
3056
3607
  setColumnWidth(columnIndex, width) {
3608
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3057
3609
  if (columnIndex < 1) throw new RangeError("Column index must be >= 1.");
3058
3610
  if (width <= 0) throw new RangeError("Width must be > 0.");
3059
3611
  const def = __privateMethod(this, _Worksheet_instances, splitColSpan_fn).call(this, columnIndex) ?? __privateMethod(this, _Worksheet_instances, insertColDef_fn).call(this, { min: columnIndex, max: columnIndex, customWidth: false });
@@ -3066,6 +3618,7 @@ var Worksheet = class {
3066
3618
  * {@link getColumnWidth} returns `undefined`. A hidden column stays hidden.
3067
3619
  */
3068
3620
  resetColumnWidth(columnIndex) {
3621
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3069
3622
  if (columnIndex < 1) throw new RangeError("Column index must be >= 1.");
3070
3623
  const def = __privateMethod(this, _Worksheet_instances, splitColSpan_fn).call(this, columnIndex);
3071
3624
  if (!def) return;
@@ -3078,6 +3631,7 @@ var Worksheet = class {
3078
3631
  * file as `<col hidden="1">` and keep any explicit width.
3079
3632
  */
3080
3633
  setColumnHidden(columnIndex, hidden = true) {
3634
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3081
3635
  if (columnIndex < 1) throw new RangeError("Column index must be >= 1.");
3082
3636
  const def = __privateMethod(this, _Worksheet_instances, splitColSpan_fn).call(this, columnIndex);
3083
3637
  if (def) {
@@ -3104,6 +3658,7 @@ var Worksheet = class {
3104
3658
  }
3105
3659
  /** Auto-fits column width based on content (approximate). */
3106
3660
  autoFitColumn(columnIndex) {
3661
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3107
3662
  this.autoFitColumns(columnIndex, columnIndex);
3108
3663
  }
3109
3664
  /**
@@ -3111,6 +3666,7 @@ var Worksheet = class {
3111
3666
  * populated cells, instead of re-scanning the sheet once per column.
3112
3667
  */
3113
3668
  autoFitColumns(firstColumn, lastColumn) {
3669
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3114
3670
  if (firstColumn < 1) throw new RangeError("Column index must be >= 1.");
3115
3671
  if (lastColumn < firstColumn) return;
3116
3672
  const maxLen = /* @__PURE__ */ new Map();
@@ -3125,6 +3681,7 @@ var Worksheet = class {
3125
3681
  }
3126
3682
  /** Sets the height of a row (1-based index). */
3127
3683
  setRowHeight(rowIndex, height) {
3684
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3128
3685
  if (rowIndex < 1) throw new RangeError("Row index must be >= 1.");
3129
3686
  if (height <= 0) throw new RangeError("Height must be > 0.");
3130
3687
  const row = __privateMethod(this, _Worksheet_instances, getOrCreateRow_fn).call(this, rowIndex);
@@ -3145,6 +3702,7 @@ var Worksheet = class {
3145
3702
  * {@link getRowHeight} returns `undefined`. A hidden row stays hidden.
3146
3703
  */
3147
3704
  resetRowHeight(rowIndex) {
3705
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3148
3706
  if (rowIndex < 1) throw new RangeError("Row index must be >= 1.");
3149
3707
  const row = __privateGet(this, _rows).get(rowIndex);
3150
3708
  if (!row) return;
@@ -3154,6 +3712,7 @@ var Worksheet = class {
3154
3712
  }
3155
3713
  /** Shows or hides a row (1-based index). Hidden rows affect SUBTOTAL(101-111). */
3156
3714
  setRowHidden(rowIndex, hidden = true) {
3715
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3157
3716
  if (rowIndex < 1) throw new RangeError("Row index must be >= 1.");
3158
3717
  __privateMethod(this, _Worksheet_instances, getOrCreateRow_fn).call(this, rowIndex).hidden = hidden;
3159
3718
  }
@@ -3204,6 +3763,7 @@ var Worksheet = class {
3204
3763
  * ws.insertRows(3, 2); // pushes row 3 and below down by two rows
3205
3764
  */
3206
3765
  insertRows(at, count = 1) {
3766
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3207
3767
  return __privateMethod(this, _Worksheet_instances, applyStructural_fn).call(this, "row", "insert", at, count);
3208
3768
  }
3209
3769
  /**
@@ -3213,6 +3773,7 @@ var Worksheet = class {
3213
3773
  * for the shifting rules and known limitations.
3214
3774
  */
3215
3775
  deleteRows(at, count = 1) {
3776
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3216
3777
  return __privateMethod(this, _Worksheet_instances, applyStructural_fn).call(this, "row", "delete", at, count);
3217
3778
  }
3218
3779
  /**
@@ -3221,6 +3782,7 @@ var Worksheet = class {
3221
3782
  * shift with the columns.
3222
3783
  */
3223
3784
  insertColumns(at, count = 1) {
3785
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3224
3786
  return __privateMethod(this, _Worksheet_instances, applyStructural_fn).call(this, "col", "insert", at, count);
3225
3787
  }
3226
3788
  /**
@@ -3228,6 +3790,7 @@ var Worksheet = class {
3228
3790
  * Column-level counterpart to {@link deleteRows}.
3229
3791
  */
3230
3792
  deleteColumns(at, count = 1) {
3793
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3231
3794
  return __privateMethod(this, _Worksheet_instances, applyStructural_fn).call(this, "col", "delete", at, count);
3232
3795
  }
3233
3796
  /**
@@ -3236,6 +3799,7 @@ var Worksheet = class {
3236
3799
  * like `'Data'!A5` stay correct. Cell positions here do not change.
3237
3800
  */
3238
3801
  applyExternalShift(shift) {
3802
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3239
3803
  return { refErrors: __privateMethod(this, _Worksheet_instances, rewriteFormulas_fn).call(this, shift) };
3240
3804
  }
3241
3805
  /**
@@ -3248,6 +3812,7 @@ var Worksheet = class {
3248
3812
  * ws.moveCell('A1', 'C5');
3249
3813
  */
3250
3814
  moveCell(from, to) {
3815
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3251
3816
  const fromRef = normalizeRef(from);
3252
3817
  const toRef = normalizeRef(to);
3253
3818
  if (fromRef === toRef) return;
@@ -3279,6 +3844,7 @@ var Worksheet = class {
3279
3844
  * ws.copyCell('A1', 'C5');
3280
3845
  */
3281
3846
  copyCell(from, to) {
3847
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3282
3848
  const fromRef = normalizeRef(from);
3283
3849
  const toRef = normalizeRef(to);
3284
3850
  if (fromRef === toRef) return;
@@ -3294,11 +3860,13 @@ var Worksheet = class {
3294
3860
  }
3295
3861
  /** Merges cells in the given range (e.g. "A1:B2"). */
3296
3862
  mergeCells(rangeReference) {
3863
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3297
3864
  if (!rangeReference) throw new Error("Range reference cannot be empty.");
3298
3865
  __privateGet(this, _mergeCells).push({ ref: rangeReference });
3299
3866
  }
3300
3867
  /** Removes a merge for the given range reference. */
3301
3868
  unmergeCells(rangeReference) {
3869
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3302
3870
  __privateSet(this, _mergeCells, __privateGet(this, _mergeCells).filter((m) => m.ref !== rangeReference));
3303
3871
  }
3304
3872
  /**
@@ -3315,12 +3883,14 @@ var Worksheet = class {
3315
3883
  * @param column Number of columns to freeze (0 = none)
3316
3884
  */
3317
3885
  freezePanes(row, column) {
3886
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3318
3887
  if (row < 0) throw new RangeError("Row must be >= 0.");
3319
3888
  if (column < 0) throw new RangeError("Column must be >= 0.");
3320
3889
  __privateSet(this, _pane, row === 0 && column === 0 ? void 0 : { row, column });
3321
3890
  }
3322
3891
  /** Sets auto-filter on the given range. */
3323
3892
  setAutoFilter(rangeReference) {
3893
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3324
3894
  __privateSet(this, _autoFilter, { ref: rangeReference });
3325
3895
  }
3326
3896
  /**
@@ -3329,10 +3899,12 @@ var Worksheet = class {
3329
3899
  * @param listFormula Formula for the dropdown list (e.g. "'Sheet2'!$A$1:$A$10")
3330
3900
  */
3331
3901
  addDropdownValidation(cellRange, listFormula) {
3902
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3332
3903
  __privateGet(this, _dataValidations).push({ sqref: cellRange, formula: listFormula });
3333
3904
  }
3334
3905
  /** Sets the visibility state of this worksheet. */
3335
3906
  setVisibility(state) {
3907
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3336
3908
  __privateSet(this, _visibility, state);
3337
3909
  }
3338
3910
  get visibility() {
@@ -3354,10 +3926,12 @@ var Worksheet = class {
3354
3926
  * ws.protect({ allowSort: true, allowAutoFilter: true });
3355
3927
  */
3356
3928
  protect(options) {
3929
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3357
3930
  __privateSet(this, _protection, { ...PROTECTION_DEFAULTS, ...options });
3358
3931
  }
3359
3932
  /** Turns off sheet protection, discarding any password read from a file. */
3360
3933
  unprotect() {
3934
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3361
3935
  __privateSet(this, _protection, void 0);
3362
3936
  __privateSet(this, _protectionCredentials, "");
3363
3937
  }
@@ -3388,6 +3962,7 @@ var Worksheet = class {
3388
3962
  if (!link.target && !link.location) {
3389
3963
  throw new Error("A hyperlink needs a target (external) or a location (in-workbook).");
3390
3964
  }
3965
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3391
3966
  const normalized = normalizeRangeRef(ref);
3392
3967
  __privateGet(this, _hyperlinks).set(normalized, { ref: normalized, ...link });
3393
3968
  }
@@ -3397,6 +3972,7 @@ var Worksheet = class {
3397
3972
  }
3398
3973
  /** Removes the hyperlink on this ref. Returns true if one was there. */
3399
3974
  removeHyperlink(ref) {
3975
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3400
3976
  return __privateGet(this, _hyperlinks).delete(normalizeRangeRef(ref));
3401
3977
  }
3402
3978
  /** Every hyperlink on the sheet, including ones read from a file. */
@@ -3417,6 +3993,7 @@ var Worksheet = class {
3417
3993
  * ws.setComment('B4', 'Check this figure', { author: 'Bill' });
3418
3994
  */
3419
3995
  setComment(ref, text, options) {
3996
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3420
3997
  const normalized = normalizeRef(ref);
3421
3998
  __privateGet(this, _comments).set(normalized, {
3422
3999
  ref: normalized,
@@ -3431,6 +4008,7 @@ var Worksheet = class {
3431
4008
  }
3432
4009
  /** Removes the comment on a cell. Returns true if one was there. */
3433
4010
  removeComment(ref) {
4011
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3434
4012
  const removed = __privateGet(this, _comments).delete(normalizeRef(ref));
3435
4013
  if (removed) __privateSet(this, _commentsDirty, true);
3436
4014
  return removed;
@@ -3458,6 +4036,7 @@ var Worksheet = class {
3458
4036
  * save pipeline turns registered providers into valid OOXML drawing/chart parts.
3459
4037
  */
3460
4038
  addDrawingProvider(provider) {
4039
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3461
4040
  __privateGet(this, _drawings).push(provider);
3462
4041
  }
3463
4042
  /**
@@ -3469,6 +4048,7 @@ var Worksheet = class {
3469
4048
  * deliberately drop a chart that came from the source file.
3470
4049
  */
3471
4050
  clearDrawings() {
4051
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3472
4052
  __privateSet(this, _drawings, []);
3473
4053
  __privateSet(this, _preservedTail, __privateGet(this, _preservedTail).filter((t) => t.tag !== "drawing"));
3474
4054
  }
@@ -3535,6 +4115,7 @@ var Worksheet = class {
3535
4115
  }
3536
4116
  /** @internal Captures round-trip state from the source package. */
3537
4117
  loadPreservedState(worksheetXml, relationships) {
4118
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3538
4119
  __privateSet(this, _preservedRels, relationships.filter((r) => r.type !== REL_TYPES.hyperlink));
3539
4120
  const known = new Set(__privateGet(this, _preservedRels).map((r) => r.id));
3540
4121
  __privateSet(this, _preservedTail, parseTailElements(worksheetXml).filter((t) => known.has(t.relId)));
@@ -3575,6 +4156,8 @@ var Worksheet = class {
3575
4156
  // ── XML serialisation ──────────────────────────────────────────────────────
3576
4157
  /** Builds the xl/worksheets/sheetN.xml string. */
3577
4158
  buildXml() {
4159
+ const memo = __privateGet(this, _xmlMemo);
4160
+ if (memo && memo.at === __privateGet(this, _version2).v) return memo.xml;
3578
4161
  const rels = this.generatedRels();
3579
4162
  const sheetViewXml = __privateMethod(this, _Worksheet_instances, buildSheetViewXml_fn).call(this);
3580
4163
  const colsXml = __privateMethod(this, _Worksheet_instances, buildColsXml_fn).call(this);
@@ -3585,15 +4168,18 @@ var Worksheet = class {
3585
4168
  const validXml = __privateMethod(this, _Worksheet_instances, buildDataValidationsXml_fn).call(this);
3586
4169
  const linksXml = __privateMethod(this, _Worksheet_instances, buildHyperlinksXml_fn).call(this, rels.hyperlinks);
3587
4170
  const tailXml = __privateMethod(this, _Worksheet_instances, buildTailXml_fn).call(this, rels);
3588
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
4171
+ const xml2 = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3589
4172
  <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
3590
4173
  xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
3591
4174
  <sheetViews>${sheetViewXml}</sheetViews>
3592
4175
  ${colsXml}${sheetDataXml}${protectionXml}${filterXml}${mergeXml}${validXml}${linksXml}${tailXml}
3593
4176
  </worksheet>`;
4177
+ __privateSet(this, _xmlMemo, { at: __privateGet(this, _version2).v, xml: xml2 });
4178
+ return xml2;
3594
4179
  }
3595
4180
  /** Parses xl/worksheets/sheetN.xml and loads cells into memory. */
3596
4181
  loadFromXml(xml2) {
4182
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3597
4183
  __privateGet(this, _rows).clear();
3598
4184
  __privateSet(this, _cols, []);
3599
4185
  __privateSet(this, _mergeCells, []);
@@ -3615,6 +4201,8 @@ ${colsXml}${sheetDataXml}${protectionXml}${filterXml}${mergeXml}${validXml}${lin
3615
4201
  hidden: getAttr(colEl, "hidden") === "1"
3616
4202
  });
3617
4203
  }
4204
+ const sharedMasters = /* @__PURE__ */ new Map();
4205
+ const sharedInstances = [];
3618
4206
  for (const rowEl of getElementsByTagName(root, "row")) {
3619
4207
  const rowIndex = parseInt(getAttr(rowEl, "r") ?? "0", 10);
3620
4208
  if (rowIndex < 1) continue;
@@ -3635,9 +4223,17 @@ ${colsXml}${sheetDataXml}${protectionXml}${filterXml}${mergeXml}${validXml}${lin
3635
4223
  ref.toUpperCase()
3636
4224
  );
3637
4225
  rowData.cells.set(ref.toUpperCase(), cellData);
4226
+ if (cellData.sharedIndex !== void 0) {
4227
+ if (cellData.formula !== void 0) {
4228
+ sharedMasters.set(cellData.sharedIndex, { formula: cellData.formula, ref: cellData.reference });
4229
+ } else {
4230
+ sharedInstances.push(cellData);
4231
+ }
4232
+ }
3638
4233
  }
3639
4234
  __privateGet(this, _rows).set(rowIndex, rowData);
3640
4235
  }
4236
+ __privateMethod(this, _Worksheet_instances, resolveSharedFormulas_fn).call(this, sharedMasters, sharedInstances);
3641
4237
  for (const mcEl of getElementsByTagName(root, "mergeCell")) {
3642
4238
  const ref = getAttr(mcEl, "ref");
3643
4239
  if (ref) __privateGet(this, _mergeCells).push({ ref });
@@ -3693,6 +4289,7 @@ ${colsXml}${sheetDataXml}${protectionXml}${filterXml}${mergeXml}${validXml}${lin
3693
4289
  }
3694
4290
  /** @internal Parses an `xl/comments*.xml` part into this sheet's comments. */
3695
4291
  loadCommentsXml(xml2) {
4292
+ __privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
3696
4293
  const root = parseXml(xml2);
3697
4294
  const authors = getElementsByTagName(root, "author").map((a) => a.textContent);
3698
4295
  for (const cEl of getElementsByTagName(root, "comment")) {
@@ -3752,7 +4349,47 @@ _commentsDirty = new WeakMap();
3752
4349
  _pendingHyperlinkRels = new WeakMap();
3753
4350
  _preservedTail = new WeakMap();
3754
4351
  _preservedRels = new WeakMap();
4352
+ _version2 = new WeakMap();
4353
+ _usedBoundsMemo = new WeakMap();
4354
+ _xmlMemo = new WeakMap();
3755
4355
  _Worksheet_instances = new WeakSet();
4356
+ /** Records a change to this sheet's contents or layout. */
4357
+ touch_fn2 = function() {
4358
+ __privateGet(this, _version2).v++;
4359
+ };
4360
+ /**
4361
+ * The stored record for a cell, without creating it — the read paths below
4362
+ * work on this directly instead of wrapping it in a {@link Cell}. Formula
4363
+ * evaluation reads far more cells than it writes, and one wrapper per read is
4364
+ * pure garbage.
4365
+ */
4366
+ findCellData_fn = function(reference) {
4367
+ const ref = normalizeRefFast(reference);
4368
+ return __privateGet(this, _rows).get(parseCellRef(ref).row)?.cells.get(ref);
4369
+ };
4370
+ /**
4371
+ * The extent, computed without touching the memo.
4372
+ *
4373
+ * Internal callers must use this. A mutator bumps the version once, at its
4374
+ * start, and then does its work; if it read the memoised getter part-way
4375
+ * through, the half-finished extent would be cached against the *new* version
4376
+ * and served as current for the rest of that version's life. That is precisely
4377
+ * how `insertColumns` came to report the extent it had before the insert —
4378
+ * caught by the round-trip fuzz test, not by any of the hand-written ones.
4379
+ */
4380
+ computeUsedBounds_fn = function() {
4381
+ let rowCount = 0;
4382
+ let columnCount = 0;
4383
+ for (const row of __privateGet(this, _rows).values()) {
4384
+ if (row.cells.size === 0) continue;
4385
+ if (row.rowIndex > rowCount) rowCount = row.rowIndex;
4386
+ for (const ref of row.cells.keys()) {
4387
+ const col = columnFromRef(ref, refSplit(ref));
4388
+ if (col > columnCount) columnCount = col;
4389
+ }
4390
+ }
4391
+ return { rowCount, columnCount };
4392
+ };
3756
4393
  /** Removes and returns a cell's data record, pruning an emptied bare row. */
3757
4394
  takeCellData_fn = function(ref) {
3758
4395
  return __privateMethod(this, _Worksheet_instances, takeCellDataAt_fn).call(this, parseCellRef(ref).row, ref);
@@ -3778,7 +4415,7 @@ applyStructural_fn = function(dim, kind, at, count) {
3778
4415
  const shift = { dim, kind, at, count, sheetName: __privateGet(this, _name) };
3779
4416
  if (kind === "insert") {
3780
4417
  const limit = dim === "row" ? EXCEL_MAX_ROWS : EXCEL_MAX_COLUMNS;
3781
- const used = dim === "row" ? __privateMethod(this, _Worksheet_instances, maxPopulatedRow_fn).call(this) : this.usedBounds.columnCount;
4418
+ const used = dim === "row" ? __privateMethod(this, _Worksheet_instances, maxPopulatedRow_fn).call(this) : __privateMethod(this, _Worksheet_instances, computeUsedBounds_fn).call(this).columnCount;
3782
4419
  if (used >= at && used + count > limit) {
3783
4420
  throw new RangeError(`Insert would push populated cells past the sheet limit of ${limit}.`);
3784
4421
  }
@@ -3994,6 +4631,34 @@ buildHyperlinksXml_fn = function(relIds) {
3994
4631
  }).join("");
3995
4632
  return `<hyperlinks>${inner}</hyperlinks>`;
3996
4633
  };
4634
+ /**
4635
+ * Materialises the formula of every shared-formula instance cell.
4636
+ *
4637
+ * Excel writes a fill-down once: the group's master carries the text, the rest
4638
+ * carry `<f t="shared" si="n"/>` and are understood to hold the master's
4639
+ * formula with its relative references moved by the offset between the cells.
4640
+ * Read literally — the way this used to be read — an instance cell has no
4641
+ * formula at all, and the next save turns it into the constant that happened to
4642
+ * be cached in `<v>`. On a real workbook that is tens of thousands of formulas
4643
+ * quietly replaced by numbers.
4644
+ *
4645
+ * Each cell ends up owning its own text, so nothing downstream (evaluation,
4646
+ * structural edits, serialisation) needs to know the group ever existed.
4647
+ */
4648
+ resolveSharedFormulas_fn = function(masters, instances) {
4649
+ if (instances.length === 0) return;
4650
+ for (const cd of instances) {
4651
+ const master = masters.get(cd.sharedIndex);
4652
+ if (!master) continue;
4653
+ const from = parseCellRef(master.ref);
4654
+ const to = parseCellRef(cd.reference);
4655
+ cd.formula = translateFormulaRefs(
4656
+ master.formula,
4657
+ to.row - from.row,
4658
+ to.column - from.column
4659
+ ).text;
4660
+ }
4661
+ };
3997
4662
  // ── Private XML builders ───────────────────────────────────────────────────
3998
4663
  buildColsXml_fn = function() {
3999
4664
  if (__privateGet(this, _cols).length === 0) return "";
@@ -4011,7 +4676,7 @@ buildSheetDataXml_fn = function() {
4011
4676
  const ht = row.height !== void 0 && row.customHeight ? ` ht="${row.height}" customHeight="1"` : "";
4012
4677
  const hidden = row.hidden ? ' hidden="1"' : "";
4013
4678
  const sortedCells = [...row.cells.values()].map((cd) => ({ cd, col: columnFromRef(cd.reference, refSplit(cd.reference)) })).sort((a, b) => a.col - b.col);
4014
- const cellsXml = sortedCells.map(({ cd }) => new Cell(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2)).toXml()).filter(Boolean).join("");
4679
+ const cellsXml = sortedCells.map(({ cd }) => buildCellXml(cd)).filter(Boolean).join("");
4015
4680
  if (!cellsXml && !ht && !hidden) return "";
4016
4681
  return `<row r="${row.rowIndex}"${ht}${hidden}>${cellsXml}</row>`;
4017
4682
  }).filter(Boolean).join("");
@@ -4225,7 +4890,7 @@ indexOf_fn = function(name, scope) {
4225
4890
  };
4226
4891
 
4227
4892
  // src/model/workbook.ts
4228
- var _sharedStrings3, _styles3, _sheets, _definedNames, _closed, _preservedParts, _preservedContentTypes, _orphanedTargets, _Workbook_instances, structuralEdit_fn, assertOpen_fn, seedPreservedParts_fn, preservedContentTypeDeclarations_fn, buildZipEntries_fn;
4893
+ var _sharedStrings3, _styles3, _sheets, _definedNames, _closed, _calcChain, _calcChainFingerprint, _deflateCache, _preservedParts, _preservedContentTypes, _orphanedTargets, _Workbook_instances, structuralEdit_fn, sheetFingerprint_fn, calcChainStillValid_fn, assertOpen_fn, seedPreservedParts_fn, preservedContentTypeDeclarations_fn, buildZipEntries_fn;
4229
4894
  var _Workbook = class _Workbook {
4230
4895
  constructor(sharedStrings, styles) {
4231
4896
  __privateAdd(this, _Workbook_instances);
@@ -4234,6 +4899,24 @@ var _Workbook = class _Workbook {
4234
4899
  __privateAdd(this, _sheets, []);
4235
4900
  __privateAdd(this, _definedNames, new DefinedNameManager());
4236
4901
  __privateAdd(this, _closed, false);
4902
+ /**
4903
+ * The calculation order Excel stored in `xl/calcChain.xml`, in file order, or
4904
+ * undefined when the source had no such part. Read-only to consumers: it is a
4905
+ * record of what the source file said, not a live view of the model.
4906
+ */
4907
+ __privateAdd(this, _calcChain);
4908
+ /**
4909
+ * Fingerprint of the sheet set taken right after the calc chain was parsed.
4910
+ * The chain is only written back out when this still matches — see
4911
+ * {@link #calcChainStillValid}.
4912
+ */
4913
+ __privateAdd(this, _calcChainFingerprint);
4914
+ /**
4915
+ * Compressed form of each package part from the last save. Compression is where
4916
+ * a save spends its time, and an autosave changes one part, so the rest are
4917
+ * reused verbatim. Keyed on content, so a part rebuilt identically still hits.
4918
+ */
4919
+ __privateAdd(this, _deflateCache, createDeflateCache());
4237
4920
  /** Parts carried over verbatim from the file this workbook was opened from. */
4238
4921
  __privateAdd(this, _preservedParts, /* @__PURE__ */ new Map());
4239
4922
  __privateAdd(this, _preservedContentTypes);
@@ -4258,6 +4941,7 @@ var _Workbook = class _Workbook {
4258
4941
  * Mirrors Workbook.Open(stream) in C#.
4259
4942
  */
4260
4943
  static fromBuffer(buffer) {
4944
+ var _a;
4261
4945
  const entries = unzipXlsx(buffer);
4262
4946
  const ss = new SharedStringManager();
4263
4947
  const sm = new StyleManager();
@@ -4294,6 +4978,11 @@ var _Workbook = class _Workbook {
4294
4978
  }
4295
4979
  __privateGet(wb, _sheets).push({ worksheet: ws, sheetId, relId });
4296
4980
  }
4981
+ const calcChainXml = readXmlEntry(entries, CALC_CHAIN_PATH);
4982
+ if (calcChainXml) {
4983
+ __privateSet(wb, _calcChain, parseCalcChain(calcChainXml, __privateGet(wb, _sheets)));
4984
+ __privateSet(wb, _calcChainFingerprint, __privateMethod(_a = wb, _Workbook_instances, sheetFingerprint_fn).call(_a));
4985
+ }
4297
4986
  __privateSet(wb, _preservedParts, collectPreservedParts(entries));
4298
4987
  const ctXml = readXmlEntry(entries, "[Content_Types].xml");
4299
4988
  if (ctXml) __privateSet(wb, _preservedContentTypes, parseContentTypes(ctXml));
@@ -4340,8 +5029,8 @@ var _Workbook = class _Workbook {
4340
5029
  if (__privateGet(this, _sheets).some((e) => e.worksheet.name === name)) {
4341
5030
  throw new Error(`Worksheet "${name}" already exists.`);
4342
5031
  }
4343
- const sheetId = __privateGet(this, _sheets).length + 1;
4344
- const relId = `rId${sheetId}`;
5032
+ const sheetId = __privateGet(this, _sheets).reduce((max, e) => Math.max(max, e.sheetId), 0) + 1;
5033
+ const relId = `rId${__privateGet(this, _sheets).length + 1}`;
4345
5034
  const ws = new Worksheet(name, __privateGet(this, _sharedStrings3), __privateGet(this, _styles3));
4346
5035
  __privateGet(this, _sheets).push({ worksheet: ws, sheetId, relId });
4347
5036
  return ws;
@@ -4450,7 +5139,21 @@ var _Workbook = class _Workbook {
4450
5139
  saveAsBuffer() {
4451
5140
  __privateMethod(this, _Workbook_instances, assertOpen_fn).call(this);
4452
5141
  const entries = __privateMethod(this, _Workbook_instances, buildZipEntries_fn).call(this);
4453
- return zipXlsx(entries);
5142
+ return zipXlsxCached(entries, __privateGet(this, _deflateCache));
5143
+ }
5144
+ /**
5145
+ * Same output as {@link saveAsBuffer}, without blocking the calling thread on
5146
+ * compression — for an editor that autosaves, this is the difference between a
5147
+ * visible stall on every save and none.
5148
+ *
5149
+ * The XML for sheets that have not changed since the last save is reused, so a
5150
+ * save after a one-cell edit re-serialises one sheet rather than all of them.
5151
+ * That reuse applies to the synchronous path too; only the compression differs.
5152
+ */
5153
+ async saveAsBufferAsync() {
5154
+ __privateMethod(this, _Workbook_instances, assertOpen_fn).call(this);
5155
+ const entries = __privateMethod(this, _Workbook_instances, buildZipEntries_fn).call(this);
5156
+ return zipXlsxCachedAsync(entries, __privateGet(this, _deflateCache));
4454
5157
  }
4455
5158
  /**
4456
5159
  * Saves the workbook to a file (Node.js only).
@@ -4458,7 +5161,7 @@ var _Workbook = class _Workbook {
4458
5161
  */
4459
5162
  async saveToFile(filePath) {
4460
5163
  __privateMethod(this, _Workbook_instances, assertOpen_fn).call(this);
4461
- const buffer = this.saveAsBuffer();
5164
+ const buffer = await this.saveAsBufferAsync();
4462
5165
  const fs = await import('fs/promises');
4463
5166
  const path = await import('path');
4464
5167
  const dir = path.dirname(filePath);
@@ -4474,12 +5177,29 @@ var _Workbook = class _Workbook {
4474
5177
  [Symbol.dispose]() {
4475
5178
  this.close();
4476
5179
  }
5180
+ // ── Calculation order ──────────────────────────────────────────────────────
5181
+ /**
5182
+ * The calculation order Excel recorded for this workbook, oldest-first as
5183
+ * stored in `xl/calcChain.xml`, or an empty array when the source file had
5184
+ * none (a freshly created workbook, or one saved by a tool that omits it).
5185
+ *
5186
+ * Excel has already done the topological sort that produced this order, so
5187
+ * evaluating in it avoids repeating that work. Treat it as a hint, not a
5188
+ * guarantee: it reflects the file as opened, and edits made since are not
5189
+ * reflected here.
5190
+ */
5191
+ get calcChain() {
5192
+ return __privateGet(this, _calcChain) ?? EMPTY_CALC_CHAIN;
5193
+ }
4477
5194
  };
4478
5195
  _sharedStrings3 = new WeakMap();
4479
5196
  _styles3 = new WeakMap();
4480
5197
  _sheets = new WeakMap();
4481
5198
  _definedNames = new WeakMap();
4482
5199
  _closed = new WeakMap();
5200
+ _calcChain = new WeakMap();
5201
+ _calcChainFingerprint = new WeakMap();
5202
+ _deflateCache = new WeakMap();
4483
5203
  _preservedParts = new WeakMap();
4484
5204
  _preservedContentTypes = new WeakMap();
4485
5205
  _orphanedTargets = new WeakMap();
@@ -4501,6 +5221,28 @@ structuralEdit_fn = function(sheetName, dim, kind, at, count) {
4501
5221
  }
4502
5222
  return { refErrors };
4503
5223
  };
5224
+ /**
5225
+ * Identity of the sheet set: names, durable ids and per-sheet mutation counts.
5226
+ * The calc chain is a statement about exactly this set — which cells hold
5227
+ * formulas, on which sheet, under which id — so any change to it invalidates
5228
+ * the chain wholesale.
5229
+ */
5230
+ sheetFingerprint_fn = function() {
5231
+ return __privateGet(this, _sheets).map((e) => `${e.sheetId}:${e.worksheet.name}:${e.worksheet.version}`).join("|");
5232
+ };
5233
+ /**
5234
+ * Whether the parsed calc chain may be written back out.
5235
+ *
5236
+ * Deliberately conservative: any edit at all (a value, a formula, a sheet
5237
+ * added or removed) and the chain is dropped instead of patched. An absent
5238
+ * calcChain is a documented no-op — Excel rebuilds it on open — whereas a
5239
+ * chain that disagrees with the sheets is the failure mode this whole change
5240
+ * exists to remove. Rebuilding a correct chain would mean redoing Excel's
5241
+ * dependency analysis, which belongs in the formula package, not here.
5242
+ */
5243
+ calcChainStillValid_fn = function() {
5244
+ return __privateGet(this, _calcChain) !== void 0 && __privateGet(this, _calcChain).length > 0 && __privateGet(this, _calcChainFingerprint) === __privateMethod(this, _Workbook_instances, sheetFingerprint_fn).call(this);
5245
+ };
4504
5246
  // ── Private helpers ────────────────────────────────────────────────────────
4505
5247
  assertOpen_fn = function() {
4506
5248
  if (__privateGet(this, _closed)) throw new Error("Workbook has been closed.");
@@ -4561,7 +5303,7 @@ buildZipEntries_fn = function() {
4561
5303
  entries.set("_rels/.rels", ROOT_RELS);
4562
5304
  const sheetMetas = __privateGet(this, _sheets).map((e, i) => ({
4563
5305
  name: e.worksheet.name,
4564
- sheetId: i + 1,
5306
+ sheetId: e.sheetId,
4565
5307
  relationshipId: `rId${i + 1}`,
4566
5308
  state: e.worksheet.visibility !== "visible" ? e.worksheet.visibility : void 0
4567
5309
  }));
@@ -4574,7 +5316,15 @@ buildZipEntries_fn = function() {
4574
5316
  return { name: dn.name, refersTo: dn.refersTo, localSheetId };
4575
5317
  });
4576
5318
  entries.set("xl/workbook.xml", buildWorkbookXml(sheetMetas, definedNameMetas));
4577
- entries.set("xl/_rels/workbook.xml.rels", buildWorkbookRels(sheetCount));
5319
+ const keepCalcChain = __privateMethod(this, _Workbook_instances, calcChainStillValid_fn).call(this);
5320
+ if (keepCalcChain) {
5321
+ const idBySheet = new Map(__privateGet(this, _sheets).map((e) => [e.worksheet.name, e.sheetId]));
5322
+ entries.set(CALC_CHAIN_PATH, buildCalcChainXml(__privateGet(this, _calcChain), idBySheet));
5323
+ } else {
5324
+ entries.delete(CALC_CHAIN_PATH);
5325
+ preservedPaths.delete(CALC_CHAIN_PATH);
5326
+ }
5327
+ entries.set("xl/_rels/workbook.xml.rels", buildWorkbookRels(sheetCount, keepCalcChain));
4578
5328
  entries.set("xl/styles.xml", __privateGet(this, _styles3).buildStylesXml());
4579
5329
  entries.set("xl/sharedStrings.xml", __privateGet(this, _sharedStrings3).buildXml());
4580
5330
  for (let i = 0; i < __privateGet(this, _sheets).length; i++) {
@@ -4582,6 +5332,9 @@ buildZipEntries_fn = function() {
4582
5332
  }
4583
5333
  const extraOverrides = [];
4584
5334
  const extraDefaults = [];
5335
+ if (keepCalcChain) {
5336
+ extraOverrides.push({ partName: `/${CALC_CHAIN_PATH}`, contentType: CONTENT_TYPES.calcChain });
5337
+ }
4585
5338
  let drawingCounter = 0;
4586
5339
  let partCounter = 0;
4587
5340
  let commentsCounter = 0;
@@ -4657,6 +5410,42 @@ buildZipEntries_fn = function() {
4657
5410
  return entries;
4658
5411
  };
4659
5412
  var Workbook = _Workbook;
5413
+ var CALC_CHAIN_PATH = "xl/calcChain.xml";
5414
+ var EMPTY_CALC_CHAIN = [];
5415
+ function parseCalcChain(xml2, sheets) {
5416
+ const nameById = new Map(sheets.map((e) => [e.sheetId, e.worksheet.name]));
5417
+ const out = [];
5418
+ let currentId;
5419
+ try {
5420
+ const root = parseXml(xml2);
5421
+ for (const el of getElementsByTagName(root, "c")) {
5422
+ const ref = getAttr(el, "r");
5423
+ if (!ref) continue;
5424
+ const iAttr = getAttr(el, "i");
5425
+ if (iAttr !== void 0) {
5426
+ const parsed = parseInt(iAttr, 10);
5427
+ if (!Number.isNaN(parsed)) currentId = parsed;
5428
+ }
5429
+ if (currentId === void 0) continue;
5430
+ const sheet = nameById.get(currentId);
5431
+ if (sheet === void 0) continue;
5432
+ out.push({ sheet, ref: ref.toUpperCase() });
5433
+ }
5434
+ } catch {
5435
+ return [];
5436
+ }
5437
+ return out;
5438
+ }
5439
+ function buildCalcChainXml(chain, idBySheet) {
5440
+ const cells = [];
5441
+ for (const { sheet, ref } of chain) {
5442
+ const id = idBySheet.get(sheet);
5443
+ if (id === void 0) continue;
5444
+ cells.push(`<c r="${ref}" i="${id}"/>`);
5445
+ }
5446
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
5447
+ <calcChain xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">${cells.join("")}</calcChain>`;
5448
+ }
4660
5449
  function resolveWorksheetPath(relsXml, relId) {
4661
5450
  if (!relsXml) return void 0;
4662
5451
  try {