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