amendsheet 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,3475 @@
1
+ 'use strict';
2
+
3
+ var fflate = require('fflate');
4
+
5
+ // src/lib/errors.ts
6
+ var XlsxError = class extends Error {
7
+ code;
8
+ /** Where in the document the failure was, as tightly as it was known. */
9
+ part;
10
+ sheet;
11
+ reference;
12
+ constructor(code, message, context) {
13
+ super(message, { cause: context.cause });
14
+ this.name = "XlsxError";
15
+ this.code = code;
16
+ this.part = context.part;
17
+ this.sheet = context.sheet;
18
+ this.reference = context.reference;
19
+ }
20
+ };
21
+ var LOCAL_SIG = 67324752;
22
+ var CENTRAL_SIG = 33639248;
23
+ var END_SIG = 101010256;
24
+ var ZIP64_EOCD_SIG = 101075792;
25
+ var ZIP64_LOCATOR_SIG = 117853008;
26
+ var ZIP64_EXTRA_ID = 1;
27
+ var U32_MAX = 4294967295;
28
+ var U16_MAX = 65535;
29
+ var notAZip = (message) => new XlsxError("not-a-zip", message, {});
30
+ function readU64(view, offset) {
31
+ const value = view.getBigUint64(offset, true);
32
+ if (value > BigInt(Number.MAX_SAFE_INTEGER)) {
33
+ throw notAZip("Archive declares a size beyond 9PB, which cannot be represented");
34
+ }
35
+ return Number(value);
36
+ }
37
+ function endOfCentralDirectory(view, length) {
38
+ for (let offset = length - 22; offset >= 0; offset--) {
39
+ if (view.getUint32(offset, true) === END_SIG) return offset;
40
+ }
41
+ throw notAZip("File has no end-of-central-directory record, so it is not a zip");
42
+ }
43
+ function directoryStart(view, end) {
44
+ const count = view.getUint16(end + 10, true);
45
+ const offset = view.getUint32(end + 16, true);
46
+ if (count !== U16_MAX && offset !== U32_MAX) return { count, offset };
47
+ const locator = end - 20;
48
+ if (locator < 0 || view.getUint32(locator, true) !== ZIP64_LOCATOR_SIG) {
49
+ if (offset !== U32_MAX) return { count, offset };
50
+ throw notAZip("Archive maxes out a directory field but has no ZIP64 locator");
51
+ }
52
+ const record = readU64(view, locator + 8);
53
+ if (record + 56 > view.byteLength || view.getUint32(record, true) !== ZIP64_EOCD_SIG) {
54
+ throw notAZip("ZIP64 end-of-central-directory record is missing or malformed");
55
+ }
56
+ return { count: readU64(view, record + 32), offset: readU64(view, record + 48) };
57
+ }
58
+ function readZip64Extra(view, start, length, name2) {
59
+ if (start + length > view.byteLength) {
60
+ throw notAZip(`Entry ${name2} declares a ZIP64 extra field that runs past the archive`);
61
+ }
62
+ let position = start;
63
+ const end = start + length;
64
+ while (position + 4 <= end) {
65
+ const id = view.getUint16(position, true);
66
+ const size = view.getUint16(position + 2, true);
67
+ if (id === ZIP64_EXTRA_ID) {
68
+ const valuesEnd = Math.min(position + 4 + size, end);
69
+ let cursor = position + 4;
70
+ return () => {
71
+ if (cursor + 8 > valuesEnd) {
72
+ throw notAZip(`Entry ${name2} ZIP64 extra field is too short for its overflowed fields`);
73
+ }
74
+ const value = readU64(view, cursor);
75
+ cursor += 8;
76
+ return value;
77
+ };
78
+ }
79
+ position += 4 + size;
80
+ }
81
+ throw notAZip(`Entry ${name2} needs a ZIP64 extra field but has none`);
82
+ }
83
+ function readZip(bytes) {
84
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
85
+ const end = endOfCentralDirectory(view, bytes.length);
86
+ const { count, offset: centralOffset } = directoryStart(view, end);
87
+ const entries = [];
88
+ let offset = centralOffset;
89
+ for (let index = 0; index < count; index++) {
90
+ if (offset + 46 > view.byteLength || view.getUint32(offset, true) !== CENTRAL_SIG) {
91
+ throw notAZip("Central directory is malformed");
92
+ }
93
+ const method = view.getUint16(offset + 10, true);
94
+ const crc = view.getUint32(offset + 16, true);
95
+ let compressedSize = view.getUint32(offset + 20, true);
96
+ let uncompressedSize = view.getUint32(offset + 24, true);
97
+ const nameLength = view.getUint16(offset + 28, true);
98
+ const extraLength = view.getUint16(offset + 30, true);
99
+ const commentLength = view.getUint16(offset + 32, true);
100
+ let localOffset = view.getUint32(offset + 42, true);
101
+ const name2 = new TextDecoder().decode(bytes.subarray(offset + 46, offset + 46 + nameLength));
102
+ if (uncompressedSize === U32_MAX || compressedSize === U32_MAX || localOffset === U32_MAX) {
103
+ const next = readZip64Extra(view, offset + 46 + nameLength, extraLength, name2);
104
+ if (uncompressedSize === U32_MAX) uncompressedSize = next();
105
+ if (compressedSize === U32_MAX) compressedSize = next();
106
+ if (localOffset === U32_MAX) localOffset = next();
107
+ }
108
+ if (localOffset + 30 > view.byteLength || view.getUint32(localOffset, true) !== LOCAL_SIG) {
109
+ throw notAZip(`Local header for ${name2} is malformed`);
110
+ }
111
+ const localNameLength = view.getUint16(localOffset + 26, true);
112
+ const localExtraLength = view.getUint16(localOffset + 28, true);
113
+ const dataStart = localOffset + 30 + localNameLength + localExtraLength;
114
+ entries.push({
115
+ name: name2,
116
+ method,
117
+ crc,
118
+ uncompressedSize,
119
+ compressed: bytes.subarray(dataStart, dataStart + compressedSize)
120
+ });
121
+ offset += 46 + nameLength + extraLength + commentLength;
122
+ }
123
+ return entries;
124
+ }
125
+ var STORED = 0;
126
+ var DEFLATED = 8;
127
+ function inflate(entry) {
128
+ if (entry.method === STORED) return entry.compressed;
129
+ if (entry.method === DEFLATED) {
130
+ let out;
131
+ try {
132
+ out = new Uint8Array(entry.uncompressedSize);
133
+ } catch (cause) {
134
+ throw new XlsxError(
135
+ "part-too-large",
136
+ `Part ${entry.name} decompresses to ${entry.uncompressedSize} bytes, too large to hold in memory`,
137
+ { part: entry.name, cause }
138
+ );
139
+ }
140
+ try {
141
+ return fflate.inflateSync(entry.compressed, { out });
142
+ } catch (cause) {
143
+ throw new XlsxError("unreadable-part", `Part ${entry.name} could not be inflated`, {
144
+ part: entry.name,
145
+ cause
146
+ });
147
+ }
148
+ }
149
+ throw new XlsxError("unreadable-part", `Part ${entry.name} uses compression ${entry.method}`, {
150
+ part: entry.name
151
+ });
152
+ }
153
+ function crc32(bytes) {
154
+ let crc = 4294967295;
155
+ for (const byte of bytes) {
156
+ crc ^= byte;
157
+ for (let bit = 0; bit < 8; bit++) crc = crc >>> 1 ^ 3988292384 & -(crc & 1);
158
+ }
159
+ return (crc ^ 4294967295) >>> 0;
160
+ }
161
+ function deflate(name2, bytes) {
162
+ return {
163
+ name: name2,
164
+ method: DEFLATED,
165
+ crc: crc32(bytes),
166
+ compressed: fflate.deflateSync(bytes, { level: 6 }),
167
+ uncompressedSize: bytes.length
168
+ };
169
+ }
170
+ var DOS_TIME = 0;
171
+ var DOS_DATE = 33;
172
+ var VERSION = 20;
173
+ var VERSION_ZIP64 = 45;
174
+ var setU64 = (view, offset, value) => view.setBigUint64(offset, BigInt(value), true);
175
+ function writeZip(entries) {
176
+ const encoder2 = new TextEncoder();
177
+ let localTotal = 0;
178
+ const placed = [];
179
+ for (const entry of entries) {
180
+ const name2 = encoder2.encode(entry.name);
181
+ const sizeZip64 = entry.compressed.length >= U32_MAX || entry.uncompressedSize >= U32_MAX;
182
+ const localOffset = localTotal;
183
+ localTotal += 30 + name2.length + (sizeZip64 ? 20 : 0) + entry.compressed.length;
184
+ placed.push({ entry, name: name2, sizeZip64, localOffset, offsetZip64: false, centralExtra: 0 });
185
+ }
186
+ const centralStart = localTotal;
187
+ let centralTotal = 0;
188
+ const resolved = placed.map((item) => {
189
+ const offsetZip64 = item.localOffset >= U32_MAX;
190
+ const centralExtra = item.sizeZip64 || offsetZip64 ? 4 + (item.sizeZip64 ? 16 : 0) + (offsetZip64 ? 8 : 0) : 0;
191
+ centralTotal += 46 + item.name.length + centralExtra;
192
+ return { ...item, offsetZip64, centralExtra };
193
+ });
194
+ const eocdZip64 = entries.length >= U16_MAX || centralStart >= U32_MAX || centralTotal >= U32_MAX;
195
+ const out = new Uint8Array(localTotal + centralTotal + (eocdZip64 ? 76 : 0) + 22);
196
+ const view = new DataView(out.buffer);
197
+ let offset = 0;
198
+ for (const { entry, name: name2, sizeZip64 } of resolved) {
199
+ view.setUint32(offset, LOCAL_SIG, true);
200
+ view.setUint16(offset + 4, sizeZip64 ? VERSION_ZIP64 : VERSION, true);
201
+ view.setUint16(offset + 8, entry.method, true);
202
+ view.setUint16(offset + 10, DOS_TIME, true);
203
+ view.setUint16(offset + 12, DOS_DATE, true);
204
+ view.setUint32(offset + 14, entry.crc, true);
205
+ view.setUint32(offset + 18, sizeZip64 ? U32_MAX : entry.compressed.length, true);
206
+ view.setUint32(offset + 22, sizeZip64 ? U32_MAX : entry.uncompressedSize, true);
207
+ view.setUint16(offset + 26, name2.length, true);
208
+ view.setUint16(offset + 28, sizeZip64 ? 20 : 0, true);
209
+ offset += 30;
210
+ out.set(name2, offset);
211
+ offset += name2.length;
212
+ if (sizeZip64) {
213
+ view.setUint16(offset, ZIP64_EXTRA_ID, true);
214
+ view.setUint16(offset + 2, 16, true);
215
+ setU64(view, offset + 4, entry.uncompressedSize);
216
+ setU64(view, offset + 12, entry.compressed.length);
217
+ offset += 20;
218
+ }
219
+ out.set(entry.compressed, offset);
220
+ offset += entry.compressed.length;
221
+ }
222
+ for (const { entry, name: name2, sizeZip64, offsetZip64, localOffset, centralExtra } of resolved) {
223
+ view.setUint32(offset, CENTRAL_SIG, true);
224
+ view.setUint16(offset + 4, VERSION, true);
225
+ view.setUint16(offset + 6, sizeZip64 || offsetZip64 ? VERSION_ZIP64 : VERSION, true);
226
+ view.setUint16(offset + 10, entry.method, true);
227
+ view.setUint16(offset + 12, DOS_TIME, true);
228
+ view.setUint16(offset + 14, DOS_DATE, true);
229
+ view.setUint32(offset + 16, entry.crc, true);
230
+ view.setUint32(offset + 20, sizeZip64 ? U32_MAX : entry.compressed.length, true);
231
+ view.setUint32(offset + 24, sizeZip64 ? U32_MAX : entry.uncompressedSize, true);
232
+ view.setUint16(offset + 28, name2.length, true);
233
+ view.setUint16(offset + 30, centralExtra, true);
234
+ view.setUint32(offset + 42, offsetZip64 ? U32_MAX : localOffset, true);
235
+ offset += 46;
236
+ out.set(name2, offset);
237
+ offset += name2.length;
238
+ if (centralExtra > 0) {
239
+ view.setUint16(offset, ZIP64_EXTRA_ID, true);
240
+ view.setUint16(offset + 2, centralExtra - 4, true);
241
+ offset += 4;
242
+ if (sizeZip64) {
243
+ setU64(view, offset, entry.uncompressedSize);
244
+ setU64(view, offset + 8, entry.compressed.length);
245
+ offset += 16;
246
+ }
247
+ if (offsetZip64) {
248
+ setU64(view, offset, localOffset);
249
+ offset += 8;
250
+ }
251
+ }
252
+ }
253
+ if (eocdZip64) {
254
+ const record = offset;
255
+ view.setUint32(offset, ZIP64_EOCD_SIG, true);
256
+ setU64(view, offset + 4, 44);
257
+ view.setUint16(offset + 12, VERSION_ZIP64, true);
258
+ view.setUint16(offset + 14, VERSION_ZIP64, true);
259
+ setU64(view, offset + 24, entries.length);
260
+ setU64(view, offset + 32, entries.length);
261
+ setU64(view, offset + 40, centralTotal);
262
+ setU64(view, offset + 48, centralStart);
263
+ offset += 56;
264
+ view.setUint32(offset, ZIP64_LOCATOR_SIG, true);
265
+ setU64(view, offset + 8, record);
266
+ view.setUint32(offset + 16, 1, true);
267
+ offset += 20;
268
+ }
269
+ view.setUint32(offset, END_SIG, true);
270
+ view.setUint16(offset + 8, Math.min(entries.length, U16_MAX), true);
271
+ view.setUint16(offset + 10, Math.min(entries.length, U16_MAX), true);
272
+ view.setUint32(offset + 12, Math.min(centralTotal, U32_MAX), true);
273
+ view.setUint32(offset + 16, Math.min(centralStart, U32_MAX), true);
274
+ return out;
275
+ }
276
+
277
+ // src/lib/container.ts
278
+ var LazyParts = class {
279
+ constructor(raw) {
280
+ this.raw = raw;
281
+ }
282
+ raw;
283
+ cache = /* @__PURE__ */ new Map();
284
+ everything;
285
+ read(name2, entry) {
286
+ const cached = this.cache.get(name2);
287
+ if (cached !== void 0) return cached;
288
+ const bytes = inflate(entry);
289
+ this.cache.set(name2, bytes);
290
+ return bytes;
291
+ }
292
+ get(name2) {
293
+ const entry = this.raw.get(name2);
294
+ return entry === void 0 ? void 0 : this.read(name2, entry);
295
+ }
296
+ has(name2) {
297
+ return this.raw.has(name2);
298
+ }
299
+ get size() {
300
+ return this.raw.size;
301
+ }
302
+ keys() {
303
+ return this.raw.keys();
304
+ }
305
+ // Iterating values or pairs wants every part, so it inflates them all at once.
306
+ all() {
307
+ if (this.everything === void 0) {
308
+ this.everything = /* @__PURE__ */ new Map();
309
+ for (const [name2, entry] of this.raw) this.everything.set(name2, this.read(name2, entry));
310
+ }
311
+ return this.everything;
312
+ }
313
+ values() {
314
+ return this.all().values();
315
+ }
316
+ entries() {
317
+ return this.all().entries();
318
+ }
319
+ [Symbol.iterator]() {
320
+ return this.all()[Symbol.iterator]();
321
+ }
322
+ forEach(callback, thisArg) {
323
+ for (const [name2, bytes] of this.all()) callback.call(thisArg, bytes, name2, this);
324
+ }
325
+ };
326
+ var OLE2_MAGIC = [208, 207, 17, 224, 161, 177, 26, 225];
327
+ var startsWith = (bytes, magic) => magic.every((byte, index) => bytes[index] === byte);
328
+ function readContainer(bytes) {
329
+ if (startsWith(bytes, OLE2_MAGIC)) {
330
+ throw new XlsxError(
331
+ "not-a-zip",
332
+ "File is an OLE2 compound file, not an .xlsx. A password-protected workbook must be decrypted first, and a legacy .xls must be converted to .xlsx.",
333
+ {}
334
+ );
335
+ }
336
+ const entries = /* @__PURE__ */ new Map();
337
+ for (const entry of readZip(bytes)) {
338
+ if (entry.name.endsWith("/")) continue;
339
+ entries.set(entry.name, entry);
340
+ }
341
+ return {
342
+ parts: new LazyParts(entries),
343
+ write: (changes) => writeChanges(entries, changes)
344
+ };
345
+ }
346
+ function writeChanges(entries, changes) {
347
+ const out = [];
348
+ for (const [name2, entry] of entries) {
349
+ const change = changes.get(name2);
350
+ if (change === void 0) out.push(entry);
351
+ else if (change !== null) out.push(deflate(name2, change));
352
+ }
353
+ return writeZip(out);
354
+ }
355
+ function decodeXmlPart(bytes, path) {
356
+ const misencoded = bytes[0] === 255 || bytes[0] === 254 || bytes[0] === 0 || bytes[1] === 0;
357
+ if (misencoded) {
358
+ throw new XlsxError(
359
+ "unreadable-part",
360
+ `Part ${path} is not UTF-8 (it looks like UTF-16); every part must be UTF-8 XML`,
361
+ { part: path }
362
+ );
363
+ }
364
+ try {
365
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
366
+ } catch (cause) {
367
+ throw new XlsxError("unreadable-part", `Part ${path} is not valid utf-8`, { part: path, cause });
368
+ }
369
+ }
370
+
371
+ // src/lib/date.ts
372
+ var MILLISECONDS_PER_DAY = 864e5;
373
+ var EPOCH_1900 = Date.UTC(1899, 11, 31);
374
+ var EPOCH_1904 = Date.UTC(1904, 0, 1);
375
+ var PHANTOM_LEAP_DAY = 60;
376
+ var LAST_SERIAL = 2958465;
377
+ function serialToDate(serial, date1904, at = {}) {
378
+ if (!Number.isFinite(serial) || serial < 0 || serial > LAST_SERIAL) {
379
+ throw new XlsxError("invalid-content", `Serial ${serial} is not a date`, at);
380
+ }
381
+ const corrected = !date1904 && serial > PHANTOM_LEAP_DAY ? serial - 1 : serial;
382
+ const epoch = date1904 ? EPOCH_1904 : EPOCH_1900;
383
+ const asUtc = new Date(Math.round(epoch + corrected * MILLISECONDS_PER_DAY));
384
+ return new Date(
385
+ asUtc.getUTCFullYear(),
386
+ asUtc.getUTCMonth(),
387
+ asUtc.getUTCDate(),
388
+ asUtc.getUTCHours(),
389
+ asUtc.getUTCMinutes(),
390
+ asUtc.getUTCSeconds(),
391
+ asUtc.getUTCMilliseconds()
392
+ );
393
+ }
394
+ var ISO_DATE = /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?)?/;
395
+ function parseIsoDate(text) {
396
+ const match = ISO_DATE.exec(text);
397
+ if (match === null) return void 0;
398
+ const [, year, month, day, hours, minutes, seconds, fraction] = match;
399
+ const milliseconds = fraction === void 0 ? 0 : Math.round(Number(`0.${fraction}`) * 1e3);
400
+ const date = new Date(
401
+ Number(year),
402
+ Number(month) - 1,
403
+ Number(day),
404
+ Number(hours ?? 0),
405
+ Number(minutes ?? 0),
406
+ Number(seconds ?? 0),
407
+ milliseconds
408
+ );
409
+ if (date.getFullYear() !== Number(year) || date.getMonth() !== Number(month) - 1 || date.getHours() !== Number(hours ?? 0) || date.getMinutes() !== Number(minutes ?? 0) || date.getSeconds() !== Number(seconds ?? 0)) {
410
+ return void 0;
411
+ }
412
+ return date;
413
+ }
414
+ function dateToSerial(date, date1904, at = {}) {
415
+ const where = at.reference === void 0 ? "" : ` to cell ${at.reference}`;
416
+ if (Number.isNaN(date.getTime())) {
417
+ throw new XlsxError("unwritable-value", `Cannot write an invalid date${where}`, at);
418
+ }
419
+ const startOfDay = new Date(date.getFullYear(), date.getMonth(), date.getDate());
420
+ const atStartOfDay = date.getTime() === startOfDay.getTime();
421
+ const asUtc = Date.UTC(
422
+ date.getFullYear(),
423
+ date.getMonth(),
424
+ date.getDate(),
425
+ atStartOfDay ? 0 : date.getHours(),
426
+ atStartOfDay ? 0 : date.getMinutes(),
427
+ atStartOfDay ? 0 : date.getSeconds(),
428
+ atStartOfDay ? 0 : date.getMilliseconds()
429
+ );
430
+ const epoch = date1904 ? EPOCH_1904 : EPOCH_1900;
431
+ const days = (asUtc - epoch) / MILLISECONDS_PER_DAY;
432
+ if (days < 0) {
433
+ throw new XlsxError(
434
+ "unwritable-value",
435
+ `Cannot write ${date.toDateString()}${where}: it is before ${date1904 ? 1904 : 1900}, which this workbook cannot hold`,
436
+ at
437
+ );
438
+ }
439
+ const serial = date1904 ? days : days >= PHANTOM_LEAP_DAY ? days + 1 : days;
440
+ if (serial > LAST_SERIAL) {
441
+ throw new XlsxError(
442
+ "unwritable-value",
443
+ `Cannot write ${date.toDateString()}${where}: it is after 9999, which this workbook cannot hold`,
444
+ at
445
+ );
446
+ }
447
+ return serial;
448
+ }
449
+
450
+ // src/lib/reference.ts
451
+ var LAST_COLUMN = 16384;
452
+ var LAST_ROW = 1048576;
453
+ var LETTER_A = "A".charCodeAt(0);
454
+ var isLetter = (character) => character >= "A" && character <= "Z" || character >= "a" && character <= "z";
455
+ var isDigit = (character) => character >= "0" && character <= "9";
456
+ function columnToIndex(letters) {
457
+ if (letters === "" || !Array.from(letters).every(isLetter)) {
458
+ throw new XlsxError("bad-reference", `"${letters}" does not name a column`, {
459
+ reference: letters
460
+ });
461
+ }
462
+ let index = 0;
463
+ for (const letter of letters.toUpperCase()) {
464
+ index = index * 26 + (letter.charCodeAt(0) - LETTER_A + 1);
465
+ }
466
+ return index;
467
+ }
468
+ function indexToColumn(index) {
469
+ if (index < 1 || index > LAST_COLUMN) {
470
+ throw new XlsxError("bad-reference", `Column ${index} is outside the sheet`, {
471
+ reference: String(index)
472
+ });
473
+ }
474
+ let letters = "";
475
+ let remaining = index;
476
+ while (remaining > 0) {
477
+ const digit = (remaining - 1) % 26;
478
+ letters = String.fromCharCode(LETTER_A + digit) + letters;
479
+ remaining = (remaining - 1 - digit) / 26;
480
+ }
481
+ return letters;
482
+ }
483
+ function parseReference(reference) {
484
+ let index = 0;
485
+ if (reference.charAt(index) === "$") index++;
486
+ const lettersStart = index;
487
+ while (index < reference.length && isLetter(reference.charAt(index))) index++;
488
+ const letters = reference.slice(lettersStart, index);
489
+ if (reference.charAt(index) === "$") index++;
490
+ const digitsStart = index;
491
+ while (index < reference.length && isDigit(reference.charAt(index))) index++;
492
+ const digits = reference.slice(digitsStart, index);
493
+ if (letters === "" || digits === "" || index !== reference.length) {
494
+ throw new XlsxError("bad-reference", `"${reference}" is not a cell reference`, { reference });
495
+ }
496
+ return { row: Number(digits), column: columnToIndex(letters) };
497
+ }
498
+ function parseWritableReference(reference) {
499
+ const address = parseReference(reference);
500
+ const { row, column } = address;
501
+ if (row < 1 || row > LAST_ROW || column < 1 || column > LAST_COLUMN) {
502
+ throw new XlsxError("bad-reference", `"${reference}" is outside the sheet`, { reference });
503
+ }
504
+ return address;
505
+ }
506
+ function formatReference(address) {
507
+ return `${indexToColumn(address.column)}${address.row}`;
508
+ }
509
+ function parseFileReference(reference, context) {
510
+ try {
511
+ return parseReference(reference);
512
+ } catch {
513
+ throw new XlsxError(
514
+ "invalid-content",
515
+ `Cell reference "${reference}" in the file is not a valid reference`,
516
+ { ...context, reference }
517
+ );
518
+ }
519
+ }
520
+ function canonicalReference(address) {
521
+ if (address.column < 1 || address.column > LAST_COLUMN) return void 0;
522
+ return formatReference(address);
523
+ }
524
+
525
+ // src/lib/xml.ts
526
+ var parseError = (message) => new XlsxError("malformed-xml", message, {});
527
+ var ENTITY = /&(?:#x([0-9a-fA-F]+)|#(\d+)|([a-zA-Z][a-zA-Z0-9]*));/g;
528
+ var NAMED_ENTITIES = /* @__PURE__ */ new Map([
529
+ ["amp", "&"],
530
+ ["lt", "<"],
531
+ ["gt", ">"],
532
+ ["quot", '"'],
533
+ ["apos", "'"]
534
+ ]);
535
+ var HIGHEST_CODE_POINT = 1114111;
536
+ function fromCodePoint(code, reference) {
537
+ if (code > HIGHEST_CODE_POINT) {
538
+ throw parseError(`Character reference ${reference} is above the highest code point`);
539
+ }
540
+ return String.fromCodePoint(code);
541
+ }
542
+ function decodeEntities(text) {
543
+ if (!text.includes("&")) return text;
544
+ return text.replace(
545
+ ENTITY,
546
+ (match, hex, decimal, named) => {
547
+ if (hex !== void 0) return fromCodePoint(Number.parseInt(hex, 16), match);
548
+ if (decimal !== void 0) return fromCodePoint(Number(decimal), match);
549
+ if (named !== void 0) return NAMED_ENTITIES.get(named) ?? match;
550
+ return match;
551
+ }
552
+ );
553
+ }
554
+ var isWhitespace = (character) => character === " " || character === " " || character === "\n" || character === "\r";
555
+ function findTagEnd(source, start) {
556
+ let quote = null;
557
+ for (let index = start; index < source.length; index++) {
558
+ const character = source.charAt(index);
559
+ if (quote !== null) {
560
+ if (character === quote) quote = null;
561
+ } else if (character === '"' || character === "'") {
562
+ quote = character;
563
+ } else if (character === ">") {
564
+ return index;
565
+ }
566
+ }
567
+ return -1;
568
+ }
569
+ function parseAttributes(source, tagStart) {
570
+ const attributes = /* @__PURE__ */ new Map();
571
+ let index = 0;
572
+ while (index < source.length) {
573
+ while (index < source.length && isWhitespace(source.charAt(index))) index++;
574
+ if (index >= source.length) break;
575
+ const nameStart = index;
576
+ while (index < source.length && !isWhitespace(source.charAt(index)) && source.charAt(index) !== "=") {
577
+ index++;
578
+ }
579
+ const name2 = source.slice(nameStart, index);
580
+ if (name2.length === 0) break;
581
+ while (index < source.length && isWhitespace(source.charAt(index))) index++;
582
+ if (source.charAt(index) !== "=") {
583
+ throw parseError(`Attribute "${name2}" has no value, at offset ${tagStart}`);
584
+ }
585
+ index++;
586
+ while (index < source.length && isWhitespace(source.charAt(index))) index++;
587
+ const quote = source.charAt(index);
588
+ if (quote !== '"' && quote !== "'") {
589
+ throw parseError(`Attribute "${name2}" is not quoted, at offset ${tagStart}`);
590
+ }
591
+ index++;
592
+ const valueStart = index;
593
+ while (index < source.length && source.charAt(index) !== quote) index++;
594
+ attributes.set(name2, decodeEntities(source.slice(valueStart, index)));
595
+ index++;
596
+ }
597
+ return attributes;
598
+ }
599
+ var localNameOf = (name2) => {
600
+ const colon = name2.indexOf(":");
601
+ return colon === -1 ? name2 : name2.slice(colon + 1);
602
+ };
603
+ function interpretTag(inner, start, end) {
604
+ if (inner.startsWith("/")) {
605
+ const name3 = inner.slice(1).trim();
606
+ return { kind: "close", name: name3, localName: localNameOf(name3), start, end };
607
+ }
608
+ const selfClosing = inner.endsWith("/");
609
+ const body = selfClosing ? inner.slice(0, -1) : inner;
610
+ let nameEnd = 0;
611
+ while (nameEnd < body.length && !isWhitespace(body.charAt(nameEnd))) nameEnd++;
612
+ const name2 = body.slice(0, nameEnd);
613
+ return {
614
+ kind: "open",
615
+ name: name2,
616
+ localName: localNameOf(name2),
617
+ attributes: parseAttributes(body.slice(nameEnd), start),
618
+ selfClosing,
619
+ start,
620
+ end
621
+ };
622
+ }
623
+ function* readXml(source) {
624
+ let position = 0;
625
+ while (position < source.length) {
626
+ const tagStart = source.indexOf("<", position);
627
+ if (tagStart === -1) {
628
+ yield {
629
+ kind: "text",
630
+ text: decodeEntities(source.slice(position)),
631
+ start: position,
632
+ end: source.length
633
+ };
634
+ return;
635
+ }
636
+ if (tagStart > position) {
637
+ yield {
638
+ kind: "text",
639
+ text: decodeEntities(source.slice(position, tagStart)),
640
+ start: position,
641
+ end: tagStart
642
+ };
643
+ }
644
+ position = tagStart;
645
+ if (source.startsWith("<![CDATA[", position)) {
646
+ const end = source.indexOf("]]>", position);
647
+ if (end === -1) throw parseError(`Unterminated CDATA at offset ${position}`);
648
+ yield { kind: "text", text: source.slice(position + 9, end), start: position, end: end + 3 };
649
+ position = end + 3;
650
+ continue;
651
+ }
652
+ if (source.startsWith("<!--", position)) {
653
+ const end = source.indexOf("-->", position);
654
+ if (end === -1) throw parseError(`Unterminated comment at offset ${position}`);
655
+ position = end + 3;
656
+ continue;
657
+ }
658
+ if (source.startsWith("<?", position)) {
659
+ const end = source.indexOf("?>", position);
660
+ if (end === -1) throw parseError(`Unterminated processing instruction at offset ${position}`);
661
+ position = end + 2;
662
+ continue;
663
+ }
664
+ if (source.startsWith("<!", position)) {
665
+ const end = findTagEnd(source, position);
666
+ if (end === -1) throw parseError(`Unterminated declaration at offset ${position}`);
667
+ position = end + 1;
668
+ continue;
669
+ }
670
+ const tagEnd = findTagEnd(source, position);
671
+ if (tagEnd === -1) throw parseError(`Unclosed tag starting at offset ${position}`);
672
+ const inner = source.slice(position + 1, tagEnd);
673
+ position = tagEnd + 1;
674
+ yield interpretTag(inner, tagStart, position);
675
+ }
676
+ }
677
+ var LESS_THAN = 60;
678
+ var QUOTE = 34;
679
+ var APOSTROPHE = 39;
680
+ var GREATER_THAN = 62;
681
+ var utf8 = new TextDecoder("utf-8", { fatal: true });
682
+ function decodeSpan(bytes, start, end) {
683
+ for (let index = start; index < end; index++) {
684
+ const byte = bytes[index];
685
+ if (byte !== void 0 && byte >= 128) {
686
+ try {
687
+ return utf8.decode(bytes.subarray(start, end));
688
+ } catch (cause) {
689
+ throw new XlsxError("unreadable-part", `Not valid utf-8, at offset ${start}`, { cause });
690
+ }
691
+ }
692
+ }
693
+ let text = "";
694
+ for (let index = start; index < end; index += 8192) {
695
+ text += String.fromCharCode(...bytes.subarray(index, Math.min(end, index + 8192)));
696
+ }
697
+ return text;
698
+ }
699
+ function indexOfSequence(bytes, ascii, from) {
700
+ const first = ascii.charCodeAt(0);
701
+ for (let at = bytes.indexOf(first, from); at !== -1; at = bytes.indexOf(first, at + 1)) {
702
+ if (startsWithAscii(bytes, ascii, at)) return at;
703
+ }
704
+ return -1;
705
+ }
706
+ function startsWithAscii(bytes, ascii, at) {
707
+ for (let index = 0; index < ascii.length; index++) {
708
+ if (bytes[at + index] !== ascii.charCodeAt(index)) return false;
709
+ }
710
+ return true;
711
+ }
712
+ function findTagEndByte(bytes, start) {
713
+ let quote = 0;
714
+ for (let index = start; index < bytes.length; index++) {
715
+ const byte = bytes[index];
716
+ if (quote !== 0) {
717
+ if (byte === quote) quote = 0;
718
+ } else if (byte === QUOTE || byte === APOSTROPHE) {
719
+ quote = byte;
720
+ } else if (byte === GREATER_THAN) {
721
+ return index;
722
+ }
723
+ }
724
+ return -1;
725
+ }
726
+ function* readXmlBytes(source) {
727
+ let position = 0;
728
+ while (position < source.length) {
729
+ const tagStart = source.indexOf(LESS_THAN, position);
730
+ if (tagStart === -1) {
731
+ yield {
732
+ kind: "text",
733
+ text: decodeEntities(decodeSpan(source, position, source.length)),
734
+ start: position,
735
+ end: source.length
736
+ };
737
+ return;
738
+ }
739
+ if (tagStart > position) {
740
+ yield {
741
+ kind: "text",
742
+ text: decodeEntities(decodeSpan(source, position, tagStart)),
743
+ start: position,
744
+ end: tagStart
745
+ };
746
+ }
747
+ position = tagStart;
748
+ if (startsWithAscii(source, "<![CDATA[", position)) {
749
+ const end = indexOfSequence(source, "]]>", position);
750
+ if (end === -1) throw parseError(`Unterminated CDATA at offset ${position}`);
751
+ yield {
752
+ kind: "text",
753
+ text: decodeSpan(source, position + 9, end),
754
+ start: position,
755
+ end: end + 3
756
+ };
757
+ position = end + 3;
758
+ continue;
759
+ }
760
+ if (startsWithAscii(source, "<!--", position)) {
761
+ const end = indexOfSequence(source, "-->", position);
762
+ if (end === -1) throw parseError(`Unterminated comment at offset ${position}`);
763
+ position = end + 3;
764
+ continue;
765
+ }
766
+ if (startsWithAscii(source, "<?", position)) {
767
+ const end = indexOfSequence(source, "?>", position);
768
+ if (end === -1) throw parseError(`Unterminated processing instruction at offset ${position}`);
769
+ position = end + 2;
770
+ continue;
771
+ }
772
+ if (startsWithAscii(source, "<!", position)) {
773
+ const end = findTagEndByte(source, position);
774
+ if (end === -1) throw parseError(`Unterminated declaration at offset ${position}`);
775
+ position = end + 1;
776
+ continue;
777
+ }
778
+ const tagEnd = findTagEndByte(source, position);
779
+ if (tagEnd === -1) throw parseError(`Unclosed tag starting at offset ${position}`);
780
+ const inner = decodeSpan(source, position + 1, tagEnd);
781
+ position = tagEnd + 1;
782
+ yield interpretTag(inner, tagStart, position);
783
+ }
784
+ }
785
+ var name = (code) => `U+${code.toString(16).toUpperCase().padStart(4, "0")}`;
786
+ function findUnwritableCharacter(text) {
787
+ for (let index = 0; index < text.length; index++) {
788
+ const code = text.charCodeAt(index);
789
+ if (code < 32 && code !== 9 && code !== 10 && code !== 13) return name(code);
790
+ if (code === 65534 || code === 65535) return name(code);
791
+ if (code >= 55296 && code <= 56319) {
792
+ const next = text.charCodeAt(index + 1);
793
+ if (Number.isNaN(next) || next < 56320 || next > 57343) return name(code);
794
+ index++;
795
+ continue;
796
+ }
797
+ if (code >= 56320 && code <= 57343) return name(code);
798
+ }
799
+ return void 0;
800
+ }
801
+ var attributeIn = (name2) => new RegExp(`(\\s${name2}\\s*=\\s*)("[^"]*"|'[^']*')`);
802
+ function withAttribute(openTag, name2, value) {
803
+ const pattern = attributeIn(name2);
804
+ if (pattern.test(openTag)) {
805
+ return openTag.replace(pattern, (_, head, quoted) => {
806
+ const quote = quoted.charAt(0);
807
+ return `${head}${quote}${value}${quote}`;
808
+ });
809
+ }
810
+ return openTag.replace(/^<([^\s/>]+)/, `<$1 ${name2}="${value}"`);
811
+ }
812
+ function bumpAttribute(openTag, name2, by) {
813
+ return openTag.replace(
814
+ new RegExp(`(\\s${name2}\\s*=\\s*)(["'])(\\d+)\\2`),
815
+ (_, head, quote, digits) => `${head}${quote}${Number(digits) + by}${quote}`
816
+ );
817
+ }
818
+
819
+ // src/lib/patch.ts
820
+ var PROTECTION_PERMISSIONS = [
821
+ ["selectLockedCells", "selectLockedCells"],
822
+ ["selectUnlockedCells", "selectUnlockedCells"],
823
+ ["formatCells", "formatCells"],
824
+ ["formatColumns", "formatColumns"],
825
+ ["formatRows", "formatRows"],
826
+ ["insertColumns", "insertColumns"],
827
+ ["insertRows", "insertRows"],
828
+ ["insertHyperlinks", "insertHyperlinks"],
829
+ ["deleteColumns", "deleteColumns"],
830
+ ["deleteRows", "deleteRows"],
831
+ ["sort", "sort"],
832
+ ["autoFilter", "autoFilter"],
833
+ ["pivotTables", "pivotTables"]
834
+ ];
835
+ function sheetProtectionElement(protection, prefix) {
836
+ let attributes = ` sheet="1" objects="${protection.editObjects === true ? "0" : "1"}" scenarios="${protection.editScenarios === true ? "0" : "1"}"`;
837
+ for (const [key, attribute] of PROTECTION_PERMISSIONS) {
838
+ const permitted = protection[key];
839
+ if (permitted !== void 0) attributes += ` ${attribute}="${permitted ? "0" : "1"}"`;
840
+ }
841
+ return `<${prefix}sheetProtection${attributes}/>`;
842
+ }
843
+ var isPermitted = (value) => value === "0" || value === "false";
844
+ function readSheetProtection(bytes) {
845
+ for (const event of readXmlBytes(bytes)) {
846
+ if (event.kind !== "open" || event.localName !== "sheetProtection") continue;
847
+ if (!isProtected(event.attributes.get("sheet"))) return void 0;
848
+ const result = {};
849
+ for (const [key, attribute] of PROTECTION_PERMISSIONS) {
850
+ const value = event.attributes.get(attribute);
851
+ if (value !== void 0) result[key] = isPermitted(value);
852
+ }
853
+ const objects = event.attributes.get("objects");
854
+ if (objects !== void 0) result.editObjects = isPermitted(objects);
855
+ const scenarios = event.attributes.get("scenarios");
856
+ if (scenarios !== void 0) result.editScenarios = isPermitted(scenarios);
857
+ return result;
858
+ }
859
+ return void 0;
860
+ }
861
+ var isProtected = (value) => value === "1" || value === "true";
862
+ var escapeXml = (text) => text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\r/g, "&#13;");
863
+ function formulaOf(value, reference, at) {
864
+ if ("formula" in value && typeof value.formula === "string") return value.formula;
865
+ throw new XlsxError(
866
+ "unwritable-value",
867
+ `Cell ${reference} was given an object that names no string formula`,
868
+ { ...at, reference }
869
+ );
870
+ }
871
+ function checkWritable(reference, value, date1904, at = {}) {
872
+ if (value === null || typeof value === "boolean") return;
873
+ if (typeof value === "number") {
874
+ if (!Number.isFinite(value)) {
875
+ throw new XlsxError("unwritable-value", `Cell ${reference} cannot hold ${value}`, {
876
+ ...at,
877
+ reference
878
+ });
879
+ }
880
+ return;
881
+ }
882
+ if (value instanceof Date) {
883
+ dateToSerial(value, date1904, { ...at, reference });
884
+ return;
885
+ }
886
+ if (typeof value !== "string" && (typeof value !== "object" || value === null)) {
887
+ throw new XlsxError(
888
+ "unwritable-value",
889
+ `Cell ${reference} cannot hold a value of type ${typeof value}`,
890
+ { ...at, reference }
891
+ );
892
+ }
893
+ const unwritable = findUnwritableCharacter(
894
+ typeof value === "string" ? value : formulaOf(value, reference, at)
895
+ );
896
+ if (unwritable !== void 0) {
897
+ throw new XlsxError(
898
+ "unwritable-value",
899
+ `Cell ${reference} holds ${unwritable}, which cannot be written to xml`,
900
+ { ...at, reference }
901
+ );
902
+ }
903
+ }
904
+ function parseMergeRange(ref) {
905
+ const colon = ref.indexOf(":");
906
+ if (colon === -1) return void 0;
907
+ const start = parseReference(ref.slice(0, colon));
908
+ const end = parseReference(ref.slice(colon + 1));
909
+ const minRow = Math.min(start.row, end.row);
910
+ const minColumn = Math.min(start.column, end.column);
911
+ return {
912
+ anchor: formatReference({ row: minRow, column: minColumn }),
913
+ minRow,
914
+ maxRow: Math.max(start.row, end.row),
915
+ minColumn,
916
+ maxColumn: Math.max(start.column, end.column)
917
+ };
918
+ }
919
+ var canonicalMerge = (merge) => `${merge.anchor}:${formatReference({ row: merge.maxRow, column: merge.maxColumn })}`;
920
+ function mergeRangeReference(range, at = {}) {
921
+ const parsed = parseMergeRange(range);
922
+ if (parsed === void 0) {
923
+ throw new XlsxError("bad-reference", `"${range}" is not an A1:B2 style range to merge`, {
924
+ ...at,
925
+ reference: range
926
+ });
927
+ }
928
+ return canonicalMerge(parsed);
929
+ }
930
+ function indexSheet(bytes) {
931
+ const styles = /* @__PURE__ */ new Map();
932
+ const sharedFormulas = /* @__PURE__ */ new Map();
933
+ const shape = readShape(bytes);
934
+ for (const row of shape.rows) {
935
+ for (const cell of row.cells) {
936
+ if (cell.style === void 0 && cell.spillingFormula === void 0) continue;
937
+ const reference = canonicalReference({ row: row.row, column: cell.column });
938
+ if (reference === void 0) continue;
939
+ if (cell.style !== void 0) styles.set(reference, Number(cell.style));
940
+ if (cell.spillingFormula !== void 0) {
941
+ sharedFormulas.set(reference, cell.spillingFormula);
942
+ }
943
+ }
944
+ }
945
+ return { styles, sharedFormulas, merges: shape.merges };
946
+ }
947
+ function mergeAnchorFor(index, reference) {
948
+ const { row, column } = parseReference(reference);
949
+ for (const merge of index.merges) {
950
+ const inside = row >= merge.minRow && row <= merge.maxRow && column >= merge.minColumn && column <= merge.maxColumn;
951
+ if (inside && merge.anchor !== reference) return merge.anchor;
952
+ }
953
+ return void 0;
954
+ }
955
+ function sharedFormulaRefusal(reference, master, at = {}) {
956
+ const what = master.kind === "shared" ? `defines shared formula ${master.name}` : `holds the ${master.kind === "array" ? "array formula" : "data table"} covering ${master.name}`;
957
+ return new XlsxError(
958
+ "unwritable-value",
959
+ `Cell ${reference} ${what}; overwriting it would break the cells that depend on it`,
960
+ { ...at, reference }
961
+ );
962
+ }
963
+ function mergeRefusal(reference, anchor, at = {}) {
964
+ return new XlsxError(
965
+ "unwritable-value",
966
+ `Cell ${reference} is merged into ${anchor}; a value there would never show, so write ${anchor} instead`,
967
+ { ...at, reference }
968
+ );
969
+ }
970
+ function cellElement(reference, value, style, date1904, sharedStrings, prefix, at) {
971
+ checkWritable(reference, value, date1904, at);
972
+ const attributes = style === void 0 ? "" : ` s="${style}"`;
973
+ const c = `${prefix}c`;
974
+ const v = `${prefix}v`;
975
+ if (value === null) return `<${c} r="${reference}"${attributes}/>`;
976
+ if (typeof value === "object" && !(value instanceof Date)) {
977
+ const f = `${prefix}f`;
978
+ return `<${c} r="${reference}"${attributes}><${f}>${escapeXml(value.formula)}</${f}></${c}>`;
979
+ }
980
+ if (typeof value === "string") {
981
+ const shared = sharedStrings?.get(value);
982
+ if (shared !== void 0) {
983
+ return `<${c} r="${reference}"${attributes} t="s"><${v}>${shared}</${v}></${c}>`;
984
+ }
985
+ const space = value === value.trim() ? "" : ' xml:space="preserve"';
986
+ return `<${c} r="${reference}"${attributes} t="inlineStr"><${prefix}is><${prefix}t${space}>${escapeXml(value)}</${prefix}t></${prefix}is></${c}>`;
987
+ }
988
+ if (typeof value === "boolean") {
989
+ return `<${c} r="${reference}"${attributes} t="b"><${v}>${value ? 1 : 0}</${v}></${c}>`;
990
+ }
991
+ if (value instanceof Date) {
992
+ return `<${c} r="${reference}"${attributes}><${v}>${dateToSerial(value, date1904)}</${v}></${c}>`;
993
+ }
994
+ return `<${c} r="${reference}"${attributes}><${v}>${value}</${v}></${c}>`;
995
+ }
996
+ function readShape(bytes, at = {}) {
997
+ const rows = [];
998
+ const merges = [];
999
+ let dimension;
1000
+ let contentEnd = -1;
1001
+ let dataStart = -1;
1002
+ let dataEnd = -1;
1003
+ let selfClosing = false;
1004
+ let protection;
1005
+ let mergeOpenStart = -1;
1006
+ let mergeOpenEnd = -1;
1007
+ let mergeInsertAt = -1;
1008
+ let mergeSelfClosing = false;
1009
+ let mergeCount = 0;
1010
+ let colOpenStart = -1;
1011
+ let colOpenEnd = -1;
1012
+ let colInsertAt = -1;
1013
+ let colSelfClosing = false;
1014
+ const cols = [];
1015
+ let prefix = "";
1016
+ let lastRow = 0;
1017
+ let openDimension = -1;
1018
+ let currentRow;
1019
+ let currentCells = [];
1020
+ let cellStart = -1;
1021
+ let cellColumn = 0;
1022
+ let cellStyle;
1023
+ let master;
1024
+ let openCell = false;
1025
+ for (const event of readXmlBytes(bytes)) {
1026
+ if (event.kind === "open") {
1027
+ if (event.localName === "dimension") {
1028
+ const ref = event.attributes.get("ref");
1029
+ if (ref !== void 0) {
1030
+ dimension = { start: event.start, end: event.end, ref };
1031
+ openDimension = event.selfClosing ? -1 : event.start;
1032
+ }
1033
+ continue;
1034
+ }
1035
+ if (event.localName === "sheetData") {
1036
+ const colon = event.name.indexOf(":");
1037
+ prefix = colon === -1 ? "" : event.name.slice(0, colon + 1);
1038
+ dataStart = event.start;
1039
+ selfClosing = event.selfClosing;
1040
+ if (selfClosing) {
1041
+ dataEnd = event.end;
1042
+ contentEnd = event.end;
1043
+ }
1044
+ continue;
1045
+ }
1046
+ if (event.localName === "row") {
1047
+ const declared = event.attributes.get("r");
1048
+ lastRow = declared === void 0 ? lastRow + 1 : Number(declared);
1049
+ currentRow = { row: lastRow, start: event.start, openEnd: event.end };
1050
+ currentCells = [];
1051
+ cellColumn = 0;
1052
+ if (event.selfClosing) {
1053
+ rows.push({
1054
+ ...currentRow,
1055
+ contentEnd: event.end,
1056
+ end: event.end,
1057
+ selfClosing: true,
1058
+ cells: []
1059
+ });
1060
+ currentRow = void 0;
1061
+ }
1062
+ continue;
1063
+ }
1064
+ if (event.localName === "c") {
1065
+ const reference = event.attributes.get("r");
1066
+ cellColumn = reference === void 0 ? cellColumn + 1 : parseFileReference(reference, at).column;
1067
+ cellStyle = event.attributes.get("s");
1068
+ cellStart = event.start;
1069
+ master = void 0;
1070
+ openCell = !event.selfClosing;
1071
+ if (event.selfClosing) {
1072
+ currentCells.push({
1073
+ column: cellColumn,
1074
+ start: event.start,
1075
+ end: event.end,
1076
+ style: cellStyle,
1077
+ spillingFormula: void 0
1078
+ });
1079
+ }
1080
+ }
1081
+ if (event.localName === "cols") {
1082
+ colOpenStart = event.start;
1083
+ colOpenEnd = event.end;
1084
+ colSelfClosing = event.selfClosing;
1085
+ if (event.selfClosing) colInsertAt = event.end;
1086
+ continue;
1087
+ }
1088
+ if (event.localName === "col") {
1089
+ const min = Number(event.attributes.get("min"));
1090
+ const max = Number(event.attributes.get("max"));
1091
+ if (Number.isInteger(min) && Number.isInteger(max)) {
1092
+ cols.push({ start: event.start, end: event.end, min, max });
1093
+ }
1094
+ continue;
1095
+ }
1096
+ if (event.localName === "mergeCells") {
1097
+ mergeOpenStart = event.start;
1098
+ mergeOpenEnd = event.end;
1099
+ mergeSelfClosing = event.selfClosing;
1100
+ if (event.selfClosing) mergeInsertAt = event.end;
1101
+ continue;
1102
+ }
1103
+ if (event.localName === "mergeCell") {
1104
+ mergeCount++;
1105
+ const ref = event.attributes.get("ref");
1106
+ const merge = ref === void 0 ? void 0 : parseMergeRange(ref);
1107
+ if (merge !== void 0) merges.push(merge);
1108
+ continue;
1109
+ }
1110
+ if (event.localName === "sheetProtection") {
1111
+ protection = { start: event.start, end: event.end };
1112
+ continue;
1113
+ }
1114
+ if (event.localName === "f") {
1115
+ const kind = event.attributes.get("t");
1116
+ const covers = event.attributes.get("ref");
1117
+ if (covers !== void 0) {
1118
+ if (kind === "shared") {
1119
+ const si = event.attributes.get("si");
1120
+ if (si !== void 0) master = { kind: "shared", name: si };
1121
+ } else if (kind === "array" || kind === "dataTable") {
1122
+ master = { kind, name: covers };
1123
+ }
1124
+ }
1125
+ }
1126
+ continue;
1127
+ }
1128
+ if (event.kind !== "close") continue;
1129
+ if (event.localName === "c" && openCell) {
1130
+ currentCells.push({
1131
+ column: cellColumn,
1132
+ start: cellStart,
1133
+ end: event.end,
1134
+ style: cellStyle,
1135
+ spillingFormula: master
1136
+ });
1137
+ openCell = false;
1138
+ continue;
1139
+ }
1140
+ if (event.localName === "row" && currentRow !== void 0) {
1141
+ rows.push({
1142
+ ...currentRow,
1143
+ contentEnd: event.start,
1144
+ end: event.end,
1145
+ selfClosing: false,
1146
+ cells: currentCells
1147
+ });
1148
+ currentRow = void 0;
1149
+ continue;
1150
+ }
1151
+ if (event.localName === "dimension" && openDimension !== -1 && dimension !== void 0) {
1152
+ dimension = { start: openDimension, end: event.end, ref: dimension.ref };
1153
+ openDimension = -1;
1154
+ continue;
1155
+ }
1156
+ if (event.localName === "sheetData") {
1157
+ contentEnd = event.start;
1158
+ dataEnd = event.end;
1159
+ }
1160
+ if (event.localName === "mergeCells") mergeInsertAt = event.start;
1161
+ if (event.localName === "cols") colInsertAt = event.start;
1162
+ }
1163
+ if (dataStart === -1)
1164
+ throw new XlsxError("malformed-xml", "Sheet has no sheetData element to write into", at);
1165
+ return {
1166
+ prefix,
1167
+ dimension,
1168
+ rows,
1169
+ merges,
1170
+ contentEnd,
1171
+ selfClosing,
1172
+ dataStart,
1173
+ dataEnd,
1174
+ protection,
1175
+ mergeContainer: mergeOpenStart === -1 ? void 0 : {
1176
+ openStart: mergeOpenStart,
1177
+ openEnd: mergeOpenEnd,
1178
+ insertAt: mergeInsertAt,
1179
+ selfClosing: mergeSelfClosing,
1180
+ count: mergeCount
1181
+ },
1182
+ colContainer: colOpenStart === -1 ? void 0 : {
1183
+ openStart: colOpenStart,
1184
+ openEnd: colOpenEnd,
1185
+ insertAt: colInsertAt,
1186
+ selfClosing: colSelfClosing
1187
+ },
1188
+ cols
1189
+ };
1190
+ }
1191
+ function patchSheet(bytes, edits, date1904, sharedStrings, styleOverrides, at = {}, sheet = {}) {
1192
+ const { protection, merges } = sheet;
1193
+ const restyleRefs = styleOverrides === void 0 ? [] : [...styleOverrides.keys()].filter((ref) => !edits.has(ref));
1194
+ const newMerges = merges ?? [];
1195
+ const rowHeights = sheet.rowHeights ?? /* @__PURE__ */ new Map();
1196
+ const columnWidths = sheet.columnWidths ?? /* @__PURE__ */ new Map();
1197
+ if (edits.size === 0 && restyleRefs.length === 0 && protection === void 0 && newMerges.length === 0 && rowHeights.size === 0 && columnWidths.size === 0) {
1198
+ return bytes;
1199
+ }
1200
+ const heightAttributes = (row) => {
1201
+ const height = rowHeights.get(row);
1202
+ return height === void 0 ? "" : ` ht="${height}" customHeight="1"`;
1203
+ };
1204
+ const operations = [
1205
+ ...[...edits].map(([given, value]) => ({ given, value, restyle: false })),
1206
+ ...restyleRefs.map((given) => ({
1207
+ given,
1208
+ value: null,
1209
+ restyle: true
1210
+ }))
1211
+ ];
1212
+ const shape = readShape(bytes, at);
1213
+ const splices = [];
1214
+ const newRows = /* @__PURE__ */ new Map();
1215
+ const filledRows = /* @__PURE__ */ new Map();
1216
+ const styleFor = (reference, current) => {
1217
+ const override = styleOverrides?.get(reference);
1218
+ return override === void 0 ? current : String(override);
1219
+ };
1220
+ const rowsByNumber = /* @__PURE__ */ new Map();
1221
+ for (const candidate of shape.rows) rowsByNumber.set(candidate.row, candidate);
1222
+ const cellIndexes = /* @__PURE__ */ new Map();
1223
+ const cellsOf = (span) => {
1224
+ const known = cellIndexes.get(span);
1225
+ if (known !== void 0) return known;
1226
+ const built = /* @__PURE__ */ new Map();
1227
+ for (const candidate of span.cells) built.set(candidate.column, candidate);
1228
+ cellIndexes.set(span, built);
1229
+ return built;
1230
+ };
1231
+ for (const { given, value, restyle } of operations) {
1232
+ const address = parseReference(given);
1233
+ const { row, column } = address;
1234
+ const reference = formatReference(address);
1235
+ const existingRow = rowsByNumber.get(row);
1236
+ if (existingRow === void 0) {
1237
+ const pending = newRows.get(row) ?? [];
1238
+ pending.push(
1239
+ cellElement(
1240
+ reference,
1241
+ value,
1242
+ styleFor(reference, void 0),
1243
+ date1904,
1244
+ sharedStrings,
1245
+ shape.prefix,
1246
+ at
1247
+ )
1248
+ );
1249
+ newRows.set(row, pending);
1250
+ continue;
1251
+ }
1252
+ const existingCell = cellsOf(existingRow).get(column);
1253
+ if (existingCell !== void 0 && restyle) {
1254
+ const override = styleOverrides?.get(reference);
1255
+ if (override !== void 0) {
1256
+ const element = decoder.decode(bytes.subarray(existingCell.start, existingCell.end));
1257
+ const openEnd = element.indexOf(">") + 1;
1258
+ splices.push({
1259
+ start: existingCell.start,
1260
+ end: existingCell.end,
1261
+ text: withAttribute(element.slice(0, openEnd), "s", override) + element.slice(openEnd),
1262
+ order: column
1263
+ });
1264
+ }
1265
+ continue;
1266
+ }
1267
+ if (existingCell?.spillingFormula !== void 0) {
1268
+ throw sharedFormulaRefusal(reference, existingCell.spillingFormula, at);
1269
+ }
1270
+ if (existingCell !== void 0) {
1271
+ splices.push({
1272
+ start: existingCell.start,
1273
+ end: existingCell.end,
1274
+ text: cellElement(
1275
+ reference,
1276
+ value,
1277
+ styleFor(reference, existingCell.style),
1278
+ date1904,
1279
+ sharedStrings,
1280
+ shape.prefix,
1281
+ at
1282
+ ),
1283
+ order: column
1284
+ });
1285
+ continue;
1286
+ }
1287
+ const cell = cellElement(
1288
+ reference,
1289
+ value,
1290
+ styleFor(reference, void 0),
1291
+ date1904,
1292
+ sharedStrings,
1293
+ shape.prefix,
1294
+ at
1295
+ );
1296
+ if (existingRow.selfClosing) {
1297
+ const pending = filledRows.get(existingRow) ?? [];
1298
+ pending.push({ column, cell });
1299
+ filledRows.set(existingRow, pending);
1300
+ continue;
1301
+ }
1302
+ const next = existingRow.cells.find((candidate) => candidate.column > column);
1303
+ const insertAt = next === void 0 ? existingRow.contentEnd : next.start;
1304
+ splices.push({ start: insertAt, end: insertAt, text: cell, order: column });
1305
+ }
1306
+ for (const [row, cells] of filledRows) {
1307
+ const ordered = [...cells].sort((left, right) => left.column - right.column).map((entry) => entry.cell).join("");
1308
+ const height = rowHeights.get(row.row);
1309
+ const openTag = decoder.decode(bytes.subarray(row.start, row.end));
1310
+ const opened = height === void 0 ? openTag : withAttribute(withAttribute(openTag, "ht", height), "customHeight", 1);
1311
+ splices.push({
1312
+ start: row.start,
1313
+ end: row.end,
1314
+ text: `${opened.slice(0, -2)}>${ordered}</${shape.prefix}row>`,
1315
+ order: row.row
1316
+ });
1317
+ }
1318
+ for (const row of rowHeights.keys()) {
1319
+ if (!rowsByNumber.has(row) && !newRows.has(row)) newRows.set(row, []);
1320
+ }
1321
+ for (const row of rowHeights.keys()) {
1322
+ const span = rowsByNumber.get(row);
1323
+ const height = rowHeights.get(row);
1324
+ if (span === void 0 || height === void 0 || filledRows.has(span)) continue;
1325
+ const openTag = decoder.decode(bytes.subarray(span.start, span.openEnd));
1326
+ splices.push({
1327
+ start: span.start,
1328
+ end: span.openEnd,
1329
+ text: withAttribute(withAttribute(openTag, "ht", height), "customHeight", 1),
1330
+ order: span.row
1331
+ });
1332
+ }
1333
+ const buildRow = (row, cells) => {
1334
+ const ordered = cells.map((text) => ({ text, column: parseReference(cellReferenceOf(text)).column })).sort((a, b) => a.column - b.column).map((entry) => entry.text).join("");
1335
+ return `<${shape.prefix}row r="${row}"${heightAttributes(row)}>${ordered}</${shape.prefix}row>`;
1336
+ };
1337
+ if (shape.selfClosing && newRows.size > 0) {
1338
+ const body = [...newRows].sort(([left], [right]) => left - right).map(([row, cells]) => buildRow(row, cells)).join("");
1339
+ splices.push({
1340
+ start: shape.dataStart,
1341
+ end: shape.dataEnd,
1342
+ text: `<${shape.prefix}sheetData>${body}</${shape.prefix}sheetData>`,
1343
+ order: 0
1344
+ });
1345
+ } else {
1346
+ let cursor = 0;
1347
+ let next = shape.rows[cursor];
1348
+ for (const [row, cells] of [...newRows].sort(([left], [right]) => left - right)) {
1349
+ while (next !== void 0 && next.row <= row) {
1350
+ cursor++;
1351
+ next = shape.rows[cursor];
1352
+ }
1353
+ const offset = next === void 0 ? shape.contentEnd : next.start;
1354
+ splices.push({ start: offset, end: offset, text: buildRow(row, cells), order: row });
1355
+ }
1356
+ }
1357
+ const widened = widenDimension(shape.dimension, [...edits.keys()]);
1358
+ if (widened !== void 0 && shape.dimension !== void 0) {
1359
+ splices.push({
1360
+ start: shape.dimension.start,
1361
+ end: shape.dimension.end,
1362
+ text: `<${shape.prefix}dimension ref="${widened}"/>`,
1363
+ order: -1
1364
+ });
1365
+ }
1366
+ if (protection === "remove") {
1367
+ if (shape.protection !== void 0) {
1368
+ splices.push({
1369
+ start: shape.protection.start,
1370
+ end: shape.protection.end,
1371
+ text: "",
1372
+ order: -1
1373
+ });
1374
+ }
1375
+ } else if (protection !== void 0) {
1376
+ const span = shape.protection ?? { start: shape.dataEnd, end: shape.dataEnd };
1377
+ splices.push({
1378
+ start: span.start,
1379
+ end: span.end,
1380
+ text: sheetProtectionElement(protection, shape.prefix),
1381
+ order: -1
1382
+ });
1383
+ }
1384
+ if (newMerges.length > 0) {
1385
+ const seen = new Set(shape.merges.map(canonicalMerge));
1386
+ const fresh = [];
1387
+ for (const range of newMerges) {
1388
+ if (seen.has(range)) continue;
1389
+ seen.add(range);
1390
+ fresh.push(range);
1391
+ }
1392
+ if (fresh.length > 0) {
1393
+ const children = fresh.map((ref) => `<${shape.prefix}mergeCell ref="${ref}"/>`).join("");
1394
+ const container = shape.mergeContainer;
1395
+ if (container === void 0) {
1396
+ const anchor = shape.protection?.end ?? shape.dataEnd;
1397
+ splices.push({
1398
+ start: anchor,
1399
+ end: anchor,
1400
+ text: `<${shape.prefix}mergeCells count="${fresh.length}">${children}</${shape.prefix}mergeCells>`,
1401
+ order: 0
1402
+ });
1403
+ } else {
1404
+ const openTag = decoder.decode(bytes.subarray(container.openStart, container.openEnd));
1405
+ const counted = withAttribute(openTag, "count", container.count + fresh.length);
1406
+ if (container.selfClosing) {
1407
+ splices.push({
1408
+ start: container.openStart,
1409
+ end: container.openEnd,
1410
+ text: `${counted.slice(0, -2)}>${children}</${shape.prefix}mergeCells>`,
1411
+ order: -1
1412
+ });
1413
+ } else {
1414
+ splices.push({
1415
+ start: container.openStart,
1416
+ end: container.openEnd,
1417
+ text: counted,
1418
+ order: -1
1419
+ });
1420
+ splices.push({
1421
+ start: container.insertAt,
1422
+ end: container.insertAt,
1423
+ text: children,
1424
+ order: 0
1425
+ });
1426
+ }
1427
+ }
1428
+ }
1429
+ }
1430
+ if (columnWidths.size > 0) {
1431
+ const colElement = (min, max, width) => `<${shape.prefix}col min="${min}" max="${max}" width="${width}" customWidth="1"/>`;
1432
+ const colWith = (element, min, max, width) => {
1433
+ const ranged = withAttribute(withAttribute(element, "min", min), "max", max);
1434
+ return width === void 0 ? ranged : withAttribute(withAttribute(ranged, "width", width), "customWidth", 1);
1435
+ };
1436
+ const covering = (column) => shape.cols.find((c) => c.min <= column && column <= c.max);
1437
+ const bySpan = /* @__PURE__ */ new Map();
1438
+ const appends = /* @__PURE__ */ new Map();
1439
+ for (const [column, width] of columnWidths) {
1440
+ const span = covering(column);
1441
+ if (span === void 0) {
1442
+ appends.set(column, width);
1443
+ continue;
1444
+ }
1445
+ const grouped = bySpan.get(span) ?? /* @__PURE__ */ new Map();
1446
+ grouped.set(column, width);
1447
+ bySpan.set(span, grouped);
1448
+ }
1449
+ for (const [span, widths] of bySpan) {
1450
+ const element = decoder.decode(bytes.subarray(span.start, span.end));
1451
+ const segments = [];
1452
+ let cursor = span.min;
1453
+ for (const [column, width] of [...widths].sort(([a], [b]) => a - b)) {
1454
+ if (column > cursor) segments.push(colWith(element, cursor, column - 1));
1455
+ segments.push(colWith(element, column, column, width));
1456
+ cursor = column + 1;
1457
+ }
1458
+ if (cursor <= span.max) segments.push(colWith(element, cursor, span.max));
1459
+ splices.push({ start: span.start, end: span.end, text: segments.join(""), order: span.min });
1460
+ }
1461
+ if (appends.size > 0) {
1462
+ const added = [...appends].sort(([a], [b]) => a - b).map(([column, width]) => colElement(column, column, width)).join("");
1463
+ const container = shape.colContainer;
1464
+ if (container === void 0) {
1465
+ splices.push({
1466
+ start: shape.dataStart,
1467
+ end: shape.dataStart,
1468
+ text: `<${shape.prefix}cols>${added}</${shape.prefix}cols>`,
1469
+ order: -2
1470
+ });
1471
+ } else if (container.selfClosing) {
1472
+ const openTag = decoder.decode(bytes.subarray(container.openStart, container.openEnd));
1473
+ splices.push({
1474
+ start: container.openStart,
1475
+ end: container.openEnd,
1476
+ text: `${openTag.slice(0, -2)}>${added}</${shape.prefix}cols>`,
1477
+ order: -1
1478
+ });
1479
+ } else {
1480
+ splices.push({ start: container.insertAt, end: container.insertAt, text: added, order: 1 });
1481
+ }
1482
+ }
1483
+ }
1484
+ return applySplices(bytes, splices);
1485
+ }
1486
+ function widenDimension(dimension, references) {
1487
+ if (dimension === void 0) return void 0;
1488
+ const bounds = dimension.ref.split(":");
1489
+ const from = bounds[0] ?? "";
1490
+ if (from === "") return void 0;
1491
+ try {
1492
+ const topLeft = parseReference(from);
1493
+ const bottomRight = parseReference(bounds[1] ?? from);
1494
+ let { row: lastRow, column: lastColumn } = bottomRight;
1495
+ let { row: firstRow, column: firstColumn } = topLeft;
1496
+ for (const reference of references) {
1497
+ const { row, column } = parseReference(reference);
1498
+ firstRow = Math.min(firstRow, row);
1499
+ firstColumn = Math.min(firstColumn, column);
1500
+ lastRow = Math.max(lastRow, row);
1501
+ lastColumn = Math.max(lastColumn, column);
1502
+ }
1503
+ const grown = firstRow !== topLeft.row || firstColumn !== topLeft.column || lastRow !== bottomRight.row || lastColumn !== bottomRight.column;
1504
+ if (!grown) return void 0;
1505
+ const start = formatReference({ row: firstRow, column: firstColumn });
1506
+ const end = formatReference({ row: lastRow, column: lastColumn });
1507
+ return `${start}:${end}`;
1508
+ } catch {
1509
+ return void 0;
1510
+ }
1511
+ }
1512
+ function cellReferenceOf(element) {
1513
+ const start = element.indexOf('"') + 1;
1514
+ return element.slice(start, element.indexOf('"', start));
1515
+ }
1516
+ var decoder = new TextDecoder();
1517
+ var encoder = new TextEncoder();
1518
+ function applySplices(bytes, splices) {
1519
+ const ordered = [...splices].sort((a, b) => a.start - b.start || a.order - b.order);
1520
+ const pieces = [];
1521
+ let cursor = 0;
1522
+ for (const splice of ordered) {
1523
+ pieces.push(bytes.subarray(cursor, splice.start));
1524
+ pieces.push(encoder.encode(splice.text));
1525
+ cursor = splice.end;
1526
+ }
1527
+ pieces.push(bytes.subarray(cursor));
1528
+ let length = 0;
1529
+ for (const piece of pieces) length += piece.length;
1530
+ const out = new Uint8Array(length);
1531
+ let offset = 0;
1532
+ for (const piece of pieces) {
1533
+ out.set(piece, offset);
1534
+ offset += piece.length;
1535
+ }
1536
+ return out;
1537
+ }
1538
+
1539
+ // src/lib/relationships.ts
1540
+ function readRelationships(xml, part) {
1541
+ const relationships = /* @__PURE__ */ new Map();
1542
+ for (const event of readXml(xml)) {
1543
+ if (event.kind !== "open" || event.localName !== "Relationship") continue;
1544
+ const id = event.attributes.get("Id");
1545
+ if (id === void 0)
1546
+ throw new XlsxError("malformed-xml", "Relationship is missing Id", { part });
1547
+ const target = event.attributes.get("Target");
1548
+ if (target === void 0)
1549
+ throw new XlsxError("malformed-xml", `Relationship ${id} is missing Target`, { part });
1550
+ relationships.set(id, {
1551
+ id,
1552
+ type: event.attributes.get("Type") ?? "",
1553
+ target,
1554
+ external: event.attributes.get("TargetMode") === "External"
1555
+ });
1556
+ }
1557
+ return relationships;
1558
+ }
1559
+ function resolveTarget(ownerPath, target) {
1560
+ if (target.startsWith("/")) return target.slice(1);
1561
+ const ownerFolder = ownerPath.split("/").slice(0, -1);
1562
+ const segments = ownerPath === "" ? [] : ownerFolder;
1563
+ for (const segment of target.split("/")) {
1564
+ if (segment === "." || segment === "") continue;
1565
+ if (segment === "..") segments.pop();
1566
+ else segments.push(segment);
1567
+ }
1568
+ return segments.join("/");
1569
+ }
1570
+
1571
+ // src/lib/sheet.ts
1572
+ function* readSheet(bytes, sharedStrings, at = {}) {
1573
+ let row = 0;
1574
+ let column = 0;
1575
+ let type = "";
1576
+ let styleIndex;
1577
+ let reference;
1578
+ let rawValue = null;
1579
+ let formula = null;
1580
+ let sharedIndex;
1581
+ let ownsSharedRange = false;
1582
+ let inlineText = null;
1583
+ let inValue = false;
1584
+ let inFormula = false;
1585
+ let inInlineText = false;
1586
+ let inPhonetic = false;
1587
+ const finishCell = () => {
1588
+ const address = reference === void 0 ? { row, column } : parseFileReference(reference, at);
1589
+ const written = reference ?? formatReference(address);
1590
+ column = address.column;
1591
+ return {
1592
+ address,
1593
+ reference: written,
1594
+ value: toValue(type, rawValue, inlineText, sharedStrings, written, at),
1595
+ ...formula === null ? {} : { formula: formula.join("") },
1596
+ ...sharedIndex === void 0 ? {} : { sharedIndex, ownsSharedRange },
1597
+ ...styleIndex === void 0 ? {} : { styleIndex }
1598
+ };
1599
+ };
1600
+ for (const event of readXmlBytes(bytes)) {
1601
+ if (event.kind === "open") {
1602
+ switch (event.localName) {
1603
+ case "row": {
1604
+ const declared = event.attributes.get("r");
1605
+ row = declared === void 0 ? row + 1 : Number(declared);
1606
+ column = 0;
1607
+ break;
1608
+ }
1609
+ case "c": {
1610
+ reference = event.attributes.get("r");
1611
+ type = event.attributes.get("t") ?? "n";
1612
+ const style = event.attributes.get("s");
1613
+ styleIndex = style === void 0 ? void 0 : Number(style);
1614
+ rawValue = null;
1615
+ formula = null;
1616
+ sharedIndex = void 0;
1617
+ ownsSharedRange = false;
1618
+ inlineText = null;
1619
+ inPhonetic = false;
1620
+ column++;
1621
+ if (event.selfClosing) yield finishCell();
1622
+ break;
1623
+ }
1624
+ case "v":
1625
+ rawValue = [];
1626
+ inValue = !event.selfClosing;
1627
+ break;
1628
+ case "f":
1629
+ formula = [];
1630
+ inFormula = !event.selfClosing;
1631
+ if (event.attributes.get("t") === "shared") {
1632
+ sharedIndex = event.attributes.get("si");
1633
+ ownsSharedRange = event.attributes.get("ref") !== void 0;
1634
+ }
1635
+ break;
1636
+ case "rPh":
1637
+ inPhonetic = true;
1638
+ break;
1639
+ case "t":
1640
+ inlineText ??= [];
1641
+ inInlineText = !event.selfClosing && !inPhonetic;
1642
+ break;
1643
+ }
1644
+ continue;
1645
+ }
1646
+ if (event.kind === "text") {
1647
+ if (inValue && rawValue !== null) rawValue.push(event.text);
1648
+ else if (inFormula && formula !== null) formula.push(event.text);
1649
+ else if (inInlineText && inlineText !== null) inlineText.push(event.text);
1650
+ continue;
1651
+ }
1652
+ switch (event.localName) {
1653
+ case "v":
1654
+ inValue = false;
1655
+ break;
1656
+ case "f":
1657
+ inFormula = false;
1658
+ break;
1659
+ case "t":
1660
+ inInlineText = false;
1661
+ break;
1662
+ case "rPh":
1663
+ inPhonetic = false;
1664
+ break;
1665
+ case "c":
1666
+ yield finishCell();
1667
+ break;
1668
+ }
1669
+ }
1670
+ }
1671
+ function toValue(type, rawValue, inlineText, sharedStrings, reference, at) {
1672
+ if (type === "inlineStr") {
1673
+ return { kind: "text", value: inlineText === null ? "" : inlineText.join("") };
1674
+ }
1675
+ if (rawValue === null) return { kind: "empty" };
1676
+ const raw = rawValue.join("");
1677
+ switch (type) {
1678
+ case "s": {
1679
+ const value = sharedStrings[Number(raw)];
1680
+ if (value === void 0) {
1681
+ throw new XlsxError(
1682
+ "invalid-content",
1683
+ `Cell ${reference} references shared string ${raw}, which the table does not hold`,
1684
+ { ...at, reference }
1685
+ );
1686
+ }
1687
+ return { kind: "text", value };
1688
+ }
1689
+ case "str":
1690
+ return { kind: "text", value: raw };
1691
+ case "d":
1692
+ return { kind: "date", value: raw };
1693
+ case "b":
1694
+ return { kind: "boolean", value: raw === "1" };
1695
+ case "e":
1696
+ return { kind: "error", value: raw };
1697
+ default: {
1698
+ if (raw === "") return { kind: "empty" };
1699
+ const value = Number(raw);
1700
+ if (Number.isNaN(value)) {
1701
+ throw new XlsxError(
1702
+ "invalid-content",
1703
+ `Cell ${reference} holds "${raw}", which is not a number`,
1704
+ { ...at, reference }
1705
+ );
1706
+ }
1707
+ return { kind: "number", value };
1708
+ }
1709
+ }
1710
+ }
1711
+
1712
+ // src/lib/shared-strings.ts
1713
+ function readEntries(xml) {
1714
+ const entries = [];
1715
+ let current = null;
1716
+ let plain = true;
1717
+ let inPhonetic = false;
1718
+ let inText = false;
1719
+ for (const event of readXml(xml)) {
1720
+ if (event.kind === "open") {
1721
+ if (event.localName === "si") {
1722
+ current = [];
1723
+ plain = true;
1724
+ } else if (event.localName === "rPh") {
1725
+ inPhonetic = true;
1726
+ plain = false;
1727
+ } else if (event.localName === "r") plain = false;
1728
+ else if (event.localName === "t" && !event.selfClosing) inText = !inPhonetic;
1729
+ continue;
1730
+ }
1731
+ if (event.kind === "text") {
1732
+ if (inText && current !== null) current.push(event.text);
1733
+ continue;
1734
+ }
1735
+ if (event.localName === "t") inText = false;
1736
+ else if (event.localName === "rPh") inPhonetic = false;
1737
+ else if (event.localName === "si" && current !== null) {
1738
+ entries.push({ text: current.join(""), plain });
1739
+ current = null;
1740
+ }
1741
+ }
1742
+ return entries;
1743
+ }
1744
+ function readSharedStrings(xml) {
1745
+ return readEntries(xml).map((entry) => entry.text);
1746
+ }
1747
+ var escapeXml2 = (text) => text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\r/g, "&#13;");
1748
+ function entryFor(value, prefix) {
1749
+ const unwritable = findUnwritableCharacter(value);
1750
+ if (unwritable !== void 0) {
1751
+ throw new XlsxError(
1752
+ "unwritable-value",
1753
+ `A string holds ${unwritable}, which cannot be written to xml`,
1754
+ { part: "xl/sharedStrings.xml" }
1755
+ );
1756
+ }
1757
+ const needsPreserve = value !== value.trim();
1758
+ const attributes = needsPreserve ? ' xml:space="preserve"' : "";
1759
+ return `<${prefix}si><${prefix}t${attributes}>${escapeXml2(value)}</${prefix}t></${prefix}si>`;
1760
+ }
1761
+ function appendSharedStrings(xml, strings) {
1762
+ const existing = readEntries(xml);
1763
+ const indexes = /* @__PURE__ */ new Map();
1764
+ for (const [index, entry] of existing.entries()) {
1765
+ if (entry.plain && !indexes.has(entry.text)) indexes.set(entry.text, index);
1766
+ }
1767
+ let openTag = "";
1768
+ let insertAt = -1;
1769
+ let closeLength = 0;
1770
+ let prefix = "";
1771
+ for (const event of readXml(xml)) {
1772
+ if (event.kind === "open" && event.localName === "sst") {
1773
+ openTag = xml.slice(event.start, event.end);
1774
+ const colon = event.name.indexOf(":");
1775
+ prefix = colon === -1 ? "" : event.name.slice(0, colon + 1);
1776
+ if (event.selfClosing) {
1777
+ insertAt = event.end;
1778
+ closeLength = event.end - event.start;
1779
+ }
1780
+ continue;
1781
+ }
1782
+ if (event.kind === "close" && event.localName === "sst") {
1783
+ insertAt = event.start;
1784
+ closeLength = 0;
1785
+ }
1786
+ }
1787
+ const additions = [];
1788
+ const requested = /* @__PURE__ */ new Map();
1789
+ for (const value of strings) {
1790
+ const known = indexes.get(value);
1791
+ if (known !== void 0) {
1792
+ requested.set(value, known);
1793
+ continue;
1794
+ }
1795
+ const index = existing.length + additions.length;
1796
+ indexes.set(value, index);
1797
+ requested.set(value, index);
1798
+ additions.push(entryFor(value, prefix));
1799
+ }
1800
+ if (additions.length === 0) return { xml, indexes: requested };
1801
+ if (insertAt === -1)
1802
+ throw new XlsxError("malformed-xml", "Shared string table has no sst element", {
1803
+ part: "xl/sharedStrings.xml"
1804
+ });
1805
+ const updatedTag = bumpAttribute(
1806
+ bumpAttribute(openTag, "uniqueCount", additions.length),
1807
+ "count",
1808
+ additions.length
1809
+ );
1810
+ const body = additions.join("");
1811
+ if (closeLength > 0) {
1812
+ const opened = `${updatedTag.slice(0, -2)}>`;
1813
+ return {
1814
+ xml: `${xml.slice(0, insertAt - closeLength)}${opened}${body}</${prefix}sst>${xml.slice(insertAt)}`,
1815
+ indexes: requested
1816
+ };
1817
+ }
1818
+ const head = xml.slice(0, insertAt).replace(openTag, updatedTag);
1819
+ return { xml: `${head}${body}${xml.slice(insertAt)}`, indexes: requested };
1820
+ }
1821
+
1822
+ // src/lib/tables.ts
1823
+ function extendTables(sheetBytes, sheetPath, container, written) {
1824
+ const paths = tablePartPaths(sheetBytes, sheetPath, container);
1825
+ if (paths.length === 0) return [];
1826
+ const cells = new Set(written);
1827
+ const tables = paths.map((path) => decodeTable(container, path)).filter((table) => table !== void 0);
1828
+ const ranges = tables.map((table) => table.range);
1829
+ const extensions = [];
1830
+ for (const table of tables) {
1831
+ const grown = grow(table, cells, ranges);
1832
+ if (grown !== void 0) extensions.push({ path: table.path, xml: grown });
1833
+ }
1834
+ return extensions;
1835
+ }
1836
+ function tablePartPaths(sheetBytes, sheetPath, container) {
1837
+ const ids = [];
1838
+ for (const event of readXmlBytes(sheetBytes)) {
1839
+ if (event.kind !== "open" || event.localName !== "tablePart") continue;
1840
+ const id = relationshipId(event.attributes);
1841
+ if (id !== void 0) ids.push(id);
1842
+ }
1843
+ if (ids.length === 0) return [];
1844
+ const relsPath = sheetPath.replace(/([^/]+)$/, "_rels/$1.rels");
1845
+ const relsBytes = container.parts.get(relsPath);
1846
+ if (relsBytes === void 0) return [];
1847
+ const relationships = readRelationships(decode(relsBytes), relsPath);
1848
+ const paths = [];
1849
+ for (const id of ids) {
1850
+ const target = relationships.get(id)?.target;
1851
+ if (target !== void 0) paths.push(resolveTarget(sheetPath, target));
1852
+ }
1853
+ return paths;
1854
+ }
1855
+ var decode = (bytes) => new TextDecoder("utf-8", { fatal: true }).decode(bytes);
1856
+ function decodeTable(container, path) {
1857
+ const bytes = container.parts.get(path);
1858
+ if (bytes === void 0) return void 0;
1859
+ let xml;
1860
+ try {
1861
+ xml = decode(bytes);
1862
+ } catch {
1863
+ return void 0;
1864
+ }
1865
+ let range;
1866
+ let hasTotalsRow = false;
1867
+ const columnNames = [];
1868
+ const columnIds = [];
1869
+ for (const event of readXml(xml)) {
1870
+ if (event.kind !== "open") continue;
1871
+ if (event.localName === "table") {
1872
+ const ref = event.attributes.get("ref");
1873
+ range = ref === void 0 ? void 0 : parseRange(ref);
1874
+ if (range === void 0) return void 0;
1875
+ const totals = event.attributes.get("totalsRowCount");
1876
+ hasTotalsRow = totals !== void 0 && totals !== "0";
1877
+ }
1878
+ if (event.localName === "tableColumn") {
1879
+ const name2 = event.attributes.get("name");
1880
+ if (name2 !== void 0) columnNames.push(name2);
1881
+ const id = Number(event.attributes.get("id"));
1882
+ if (Number.isInteger(id)) columnIds.push(id);
1883
+ }
1884
+ }
1885
+ if (range === void 0) return void 0;
1886
+ return { path, xml, range, hasTotalsRow, columnNames, columnIds };
1887
+ }
1888
+ function parseRange(ref) {
1889
+ const colon = ref.indexOf(":");
1890
+ if (colon === -1) return void 0;
1891
+ try {
1892
+ const start = parseReference(ref.slice(0, colon));
1893
+ const end = parseReference(ref.slice(colon + 1));
1894
+ return {
1895
+ minRow: Math.min(start.row, end.row),
1896
+ maxRow: Math.max(start.row, end.row),
1897
+ minColumn: Math.min(start.column, end.column),
1898
+ maxColumn: Math.max(start.column, end.column)
1899
+ };
1900
+ } catch {
1901
+ return void 0;
1902
+ }
1903
+ }
1904
+ function grow(table, written, all) {
1905
+ if (table.hasTotalsRow) return void 0;
1906
+ const { range } = table;
1907
+ const others = all.filter((candidate) => candidate !== range);
1908
+ const rowWritten = (row) => spanWritten(written, row, row, range.minColumn, range.maxColumn);
1909
+ const columnHasWrite = (column) => spanWritten(written, range.minRow, range.maxRow, column, column);
1910
+ const overlaps = (r) => others.some(
1911
+ (other) => r.minRow <= other.maxRow && r.maxRow >= other.minRow && r.minColumn <= other.maxColumn && r.maxColumn >= other.minColumn
1912
+ );
1913
+ const downTo = (row) => ({ ...range, maxRow: row });
1914
+ const rightTo = (column) => ({ ...range, maxColumn: column });
1915
+ let maxRow = range.maxRow;
1916
+ while (rowWritten(maxRow + 1) && !overlaps(downTo(maxRow + 1))) maxRow++;
1917
+ let maxColumn = range.maxColumn;
1918
+ while (columnHasWrite(maxColumn + 1) && !overlaps(rightTo(maxColumn + 1))) maxColumn++;
1919
+ if (maxRow === range.maxRow && maxColumn === range.maxColumn) return void 0;
1920
+ const oldRef = refOf(range);
1921
+ const newRef = refOf({ ...range, maxRow, maxColumn });
1922
+ let xml = rewriteRefs(table.xml, oldRef, newRef);
1923
+ if (maxColumn > range.maxColumn) {
1924
+ xml = addColumns(xml, maxColumn - range.maxColumn, table.columnIds, table.columnNames);
1925
+ }
1926
+ return xml;
1927
+ }
1928
+ var refOf = (range) => `${formatReference({ row: range.minRow, column: range.minColumn })}:${formatReference({ row: range.maxRow, column: range.maxColumn })}`;
1929
+ function spanWritten(written, minRow, maxRow, minColumn, maxColumn) {
1930
+ for (let row = minRow; row <= maxRow; row++) {
1931
+ for (let column = minColumn; column <= maxColumn; column++) {
1932
+ if (written.has(formatReference({ row, column }))) return true;
1933
+ }
1934
+ }
1935
+ return false;
1936
+ }
1937
+ function freshColumnName(taken) {
1938
+ let n = 1;
1939
+ while (taken.has(`column${n}`)) n++;
1940
+ return `Column${n}`;
1941
+ }
1942
+ function addColumns(xml, count, ids, names) {
1943
+ const taken = new Set(names.map((name2) => name2.toLowerCase()));
1944
+ let nextId = Math.max(0, ...ids) + 1;
1945
+ const added = [];
1946
+ for (let index = 0; index < count; index++) {
1947
+ const name2 = freshColumnName(taken);
1948
+ taken.add(name2.toLowerCase());
1949
+ added.push(`<tableColumn id="${nextId++}" name="${name2}"/>`);
1950
+ }
1951
+ const elements = added.join("");
1952
+ let prefix = "";
1953
+ let insertAt = -1;
1954
+ let countStart = -1;
1955
+ let countEnd = -1;
1956
+ for (const event of readXml(xml)) {
1957
+ if (event.kind === "open" && event.localName === "tableColumns") {
1958
+ const colon = event.name.indexOf(":");
1959
+ prefix = colon === -1 ? "" : event.name.slice(0, colon + 1);
1960
+ countStart = event.start;
1961
+ countEnd = event.end;
1962
+ }
1963
+ if (event.kind === "close" && event.localName === "tableColumns") insertAt = event.start;
1964
+ }
1965
+ if (insertAt === -1 || countStart === -1) return xml;
1966
+ const openTag = withAttribute(xml.slice(countStart, countEnd), "count", names.length + count);
1967
+ const prefixed = elements.replace(/<tableColumn /g, `<${prefix}tableColumn `);
1968
+ return xml.slice(0, countStart) + openTag + xml.slice(countEnd, insertAt) + prefixed + xml.slice(insertAt);
1969
+ }
1970
+ function rewriteRefs(xml, oldRef, newRef) {
1971
+ let out = "";
1972
+ let position = 0;
1973
+ for (const event of readXml(xml)) {
1974
+ if (event.kind !== "open") continue;
1975
+ if (event.localName !== "table" && event.localName !== "autoFilter") continue;
1976
+ const tag = xml.slice(event.start, event.end);
1977
+ const replaced = tag.replace(
1978
+ /(\sref\s*=\s*)("[^"]*"|'[^']*')/,
1979
+ (whole, head, quoted) => {
1980
+ const value = quoted.slice(1, -1);
1981
+ if (value !== oldRef) return whole;
1982
+ const quote = quoted.charAt(0);
1983
+ return `${head}${quote}${newRef}${quote}`;
1984
+ }
1985
+ );
1986
+ out += xml.slice(position, event.start) + replaced;
1987
+ position = event.end;
1988
+ }
1989
+ return out + xml.slice(position);
1990
+ }
1991
+ function relationshipId(attributes) {
1992
+ const direct = attributes.get("r:id");
1993
+ if (direct !== void 0) return direct;
1994
+ for (const [name2, value] of attributes) {
1995
+ if (name2 === "id" || name2.endsWith(":id")) return value;
1996
+ }
1997
+ return void 0;
1998
+ }
1999
+
2000
+ // src/lib/styles.ts
2001
+ var BUILT_IN_FORMATS = /* @__PURE__ */ new Map([
2002
+ [0, "General"],
2003
+ [1, "0"],
2004
+ [2, "0.00"],
2005
+ [3, "#,##0"],
2006
+ [4, "#,##0.00"],
2007
+ [9, "0%"],
2008
+ [10, "0.00%"],
2009
+ [11, "0.00E+00"],
2010
+ [12, "# ?/?"],
2011
+ [13, "# ??/??"],
2012
+ [14, "mm-dd-yy"],
2013
+ [15, "d-mmm-yy"],
2014
+ [16, "d-mmm"],
2015
+ [17, "mmm-yy"],
2016
+ [18, "h:mm AM/PM"],
2017
+ [19, "h:mm:ss AM/PM"],
2018
+ [20, "h:mm"],
2019
+ [21, "h:mm:ss"],
2020
+ [22, "m/d/yy h:mm"],
2021
+ [37, "#,##0 ;(#,##0)"],
2022
+ [38, "#,##0 ;[Red](#,##0)"],
2023
+ [39, "#,##0.00;(#,##0.00)"],
2024
+ [40, "#,##0.00;[Red](#,##0.00)"],
2025
+ [45, "mm:ss"],
2026
+ [46, "[h]:mm:ss"],
2027
+ [47, "mmss.0"],
2028
+ [48, "##0.0E+0"],
2029
+ [49, "@"]
2030
+ ]);
2031
+ function builtInFormatId(code) {
2032
+ for (const [id, builtIn] of BUILT_IN_FORMATS) {
2033
+ if (builtIn === code) return id;
2034
+ }
2035
+ return void 0;
2036
+ }
2037
+ function readStyles(xml) {
2038
+ const numberFormats = /* @__PURE__ */ new Map();
2039
+ const cellFormats = [];
2040
+ let inCellFormats = false;
2041
+ let inNumberFormats = false;
2042
+ for (const event of readXml(xml)) {
2043
+ if (event.kind === "close") {
2044
+ if (event.localName === "cellXfs") inCellFormats = false;
2045
+ if (event.localName === "numFmts") inNumberFormats = false;
2046
+ continue;
2047
+ }
2048
+ if (event.kind !== "open") continue;
2049
+ if (event.localName === "numFmts") {
2050
+ inNumberFormats = !event.selfClosing;
2051
+ continue;
2052
+ }
2053
+ if (event.localName === "numFmt" && inNumberFormats) {
2054
+ const id = Number(event.attributes.get("numFmtId"));
2055
+ const code = event.attributes.get("formatCode");
2056
+ if (!Number.isNaN(id) && code !== void 0) numberFormats.set(id, code);
2057
+ continue;
2058
+ }
2059
+ if (event.localName === "cellXfs") {
2060
+ inCellFormats = !event.selfClosing;
2061
+ continue;
2062
+ }
2063
+ if (event.localName === "xf" && inCellFormats) {
2064
+ cellFormats.push(Number(event.attributes.get("numFmtId") ?? 0));
2065
+ }
2066
+ }
2067
+ return { numberFormats, cellFormats };
2068
+ }
2069
+ function numberFormatOf(styles, styleIndex) {
2070
+ if (styleIndex === void 0) return void 0;
2071
+ const formatId = styles.cellFormats[styleIndex];
2072
+ if (formatId === void 0) return void 0;
2073
+ return styles.numberFormats.get(formatId) ?? BUILT_IN_FORMATS.get(formatId);
2074
+ }
2075
+ function numericSections(code) {
2076
+ let stripped = "";
2077
+ let index = 0;
2078
+ let section = 1;
2079
+ while (index < code.length) {
2080
+ const character = code.charAt(index);
2081
+ if (character === ";") {
2082
+ section++;
2083
+ if (section > 3) break;
2084
+ stripped += ";";
2085
+ index++;
2086
+ continue;
2087
+ }
2088
+ if (character === '"') {
2089
+ index++;
2090
+ while (index < code.length && code.charAt(index) !== '"') index++;
2091
+ index++;
2092
+ continue;
2093
+ }
2094
+ if (character === "\\" || character === "*" || character === "_") {
2095
+ index += 2;
2096
+ continue;
2097
+ }
2098
+ if (character === "[") {
2099
+ const end = code.indexOf("]", index);
2100
+ const inside = end === -1 ? "" : code.slice(index + 1, end);
2101
+ if (/^[hms]+$/i.test(inside)) stripped += inside;
2102
+ index = end === -1 ? code.length : end + 1;
2103
+ continue;
2104
+ }
2105
+ stripped += character;
2106
+ index++;
2107
+ }
2108
+ return stripped;
2109
+ }
2110
+ var DATE_TOKEN = /[ymdhs]/i;
2111
+ var ELAPSED_TIME = /\[[hms]+\]/i;
2112
+ function isDateFormatCode(code) {
2113
+ if (code === void 0 || code === "General") return false;
2114
+ if (ELAPSED_TIME.test(code)) return false;
2115
+ return DATE_TOKEN.test(numericSections(code));
2116
+ }
2117
+ function isDateFormat(styles, styleIndex) {
2118
+ return isDateFormatCode(numberFormatOf(styles, styleIndex));
2119
+ }
2120
+
2121
+ // src/lib/styles-writer.ts
2122
+ var SHORT_DATE_FORMAT_ID = 14;
2123
+ var FIRST_CUSTOM_FORMAT_ID = 164;
2124
+ var escapeXml3 = (text) => text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
2125
+ var prefixOf = (name2) => {
2126
+ const colon = name2.indexOf(":");
2127
+ return colon === -1 ? "" : name2.slice(0, colon + 1);
2128
+ };
2129
+ var withNamespacePrefix = (fragment, prefix) => prefix === "" ? fragment : fragment.replace(/<(\/?)([A-Za-z])/g, `<$1${prefix}$2`);
2130
+ function readTable(xml, container, child) {
2131
+ let openTag = "";
2132
+ let openStart = -1;
2133
+ let openEnd = -1;
2134
+ let insertAt = -1;
2135
+ let selfClosing = false;
2136
+ let inside = false;
2137
+ let prefix = "";
2138
+ let openElement = -1;
2139
+ const elements = [];
2140
+ for (const event of readXml(xml)) {
2141
+ if (event.kind === "open" && event.localName === container) {
2142
+ prefix = prefixOf(event.name);
2143
+ openTag = xml.slice(event.start, event.end);
2144
+ openStart = event.start;
2145
+ openEnd = event.end;
2146
+ selfClosing = event.selfClosing;
2147
+ inside = !event.selfClosing;
2148
+ if (selfClosing) insertAt = event.end;
2149
+ continue;
2150
+ }
2151
+ if (event.kind === "close" && event.localName === container) {
2152
+ insertAt = event.start;
2153
+ inside = false;
2154
+ continue;
2155
+ }
2156
+ if (inside && event.kind === "open" && event.localName === child) {
2157
+ if (event.selfClosing) elements.push(xml.slice(event.start, event.end));
2158
+ else openElement = event.start;
2159
+ continue;
2160
+ }
2161
+ if (inside && event.kind === "close" && event.localName === child && openElement !== -1) {
2162
+ elements.push(xml.slice(openElement, event.end));
2163
+ openElement = -1;
2164
+ }
2165
+ }
2166
+ if (openStart === -1) return void 0;
2167
+ return { elements, prefix, openTag, openStart, openEnd, insertAt, selfClosing };
2168
+ }
2169
+ function tableInsertPoint(xml) {
2170
+ let cellXfsStart = -1;
2171
+ let closeStart = -1;
2172
+ let prefix = "";
2173
+ for (const event of readXml(xml)) {
2174
+ if (event.kind === "open" && event.localName === "cellXfs" && cellXfsStart === -1) {
2175
+ cellXfsStart = event.start;
2176
+ }
2177
+ if (event.kind === "close" && event.localName === "styleSheet") {
2178
+ closeStart = event.start;
2179
+ prefix = prefixOf(event.name);
2180
+ }
2181
+ }
2182
+ if (closeStart === -1) {
2183
+ throw new XlsxError("malformed-xml", "Style table has no styleSheet element", {
2184
+ part: "xl/styles.xml"
2185
+ });
2186
+ }
2187
+ return { position: cellXfsStart === -1 ? closeStart : cellXfsStart, prefix };
2188
+ }
2189
+ function ensureInTable(xml, container, child, element) {
2190
+ const table = readTable(xml, container, child);
2191
+ if (table === void 0) {
2192
+ const { position, prefix } = tableInsertPoint(xml);
2193
+ const prefixed2 = element.replace(/^<[^\s/>]+/, `<${prefix}${child}`);
2194
+ const created = `<${prefix}${container} count="1">${prefixed2}</${prefix}${container}>`;
2195
+ return { xml: xml.slice(0, position) + created + xml.slice(position), id: 0 };
2196
+ }
2197
+ const prefixed = element.replace(/^<[^\s/>]+/, `<${table.prefix}${child}`);
2198
+ const existing = table.elements.indexOf(prefixed);
2199
+ if (existing !== -1) return { xml, id: existing };
2200
+ const id = table.elements.length;
2201
+ const openTag = withAttribute(table.openTag, "count", id + 1);
2202
+ if (table.selfClosing) {
2203
+ const opened = `${openTag.slice(0, -2)}>`;
2204
+ return {
2205
+ xml: xml.slice(0, table.openStart) + `${opened}${prefixed}</${table.prefix}${container}>` + xml.slice(table.openEnd),
2206
+ id
2207
+ };
2208
+ }
2209
+ const head = xml.slice(0, table.openStart) + openTag;
2210
+ const body = xml.slice(table.openEnd, table.insertAt);
2211
+ return { xml: `${head}${body}${prefixed}${xml.slice(table.insertAt)}`, id };
2212
+ }
2213
+ var DEFAULT_XF = '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>';
2214
+ var OVERRIDE_FLAGS = [
2215
+ ["numFmtId", "applyNumberFormat"],
2216
+ ["fontId", "applyFont"],
2217
+ ["fillId", "applyFill"],
2218
+ ["borderId", "applyBorder"]
2219
+ ];
2220
+ function withApplyFlag(openTag, flag) {
2221
+ const pattern = new RegExp(`${flag}=("|')(?:\\d+|true|false)\\1`);
2222
+ if (pattern.test(openTag)) return openTag.replace(pattern, `${flag}="1"`);
2223
+ return openTag.replace(/\/?>$/, (tag) => tag === "/>" ? ` ${flag}="1"/>` : ` ${flag}="1">`);
2224
+ }
2225
+ function withOverrides(element, overrides) {
2226
+ const openTagEnd = element.indexOf(">") + 1;
2227
+ let openTag = element.slice(0, openTagEnd);
2228
+ const rest = element.slice(openTagEnd);
2229
+ for (const [attribute, flag] of OVERRIDE_FLAGS) {
2230
+ const value = overrides[attribute];
2231
+ if (value !== void 0) openTag = withApplyFlag(withAttribute(openTag, attribute, value), flag);
2232
+ }
2233
+ return `${openTag}${rest}`;
2234
+ }
2235
+ function ensureDateStyle(stylesXml, basedOn) {
2236
+ const parsed = readStyles(stylesXml);
2237
+ if (basedOn !== void 0 && isDateFormat(parsed, basedOn)) {
2238
+ return { xml: stylesXml, index: basedOn };
2239
+ }
2240
+ if (basedOn === void 0) {
2241
+ for (let index = 0; index < parsed.cellFormats.length; index++) {
2242
+ if (isDateFormat(parsed, index) && isPlainFormat(stylesXml, index)) {
2243
+ return { xml: stylesXml, index };
2244
+ }
2245
+ }
2246
+ }
2247
+ return applyCellFormat(stylesXml, basedOn, { numFmtId: SHORT_DATE_FORMAT_ID });
2248
+ }
2249
+ function applyCellFormat(stylesXml, basedOn, overrides) {
2250
+ const formats = readTable(stylesXml, "cellXfs", "xf");
2251
+ const base = basedOn === void 0 ? DEFAULT_XF : formats?.elements[basedOn] ?? DEFAULT_XF;
2252
+ const wanted = withOverrides(base, overrides);
2253
+ const { xml, id } = ensureInTable(stylesXml, "cellXfs", "xf", wanted);
2254
+ return { xml, index: id };
2255
+ }
2256
+ var REPORTED_UNDERLINES = /* @__PURE__ */ new Set([
2257
+ "double",
2258
+ "singleAccounting",
2259
+ "doubleAccounting"
2260
+ ]);
2261
+ var toUnderline = (value) => {
2262
+ for (const known of REPORTED_UNDERLINES) if (known === value) return known;
2263
+ return void 0;
2264
+ };
2265
+ var VERTICAL_ALIGNS = /* @__PURE__ */ new Set([
2266
+ "baseline",
2267
+ "superscript",
2268
+ "subscript"
2269
+ ]);
2270
+ var toVertAlign = (value) => {
2271
+ for (const known of VERTICAL_ALIGNS) if (known === value) return known;
2272
+ return void 0;
2273
+ };
2274
+ var HEX_COLOR = /^[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/;
2275
+ function normalizeColor(color) {
2276
+ const hex = color.startsWith("#") ? color.slice(1) : color;
2277
+ if (!HEX_COLOR.test(hex)) {
2278
+ throw new XlsxError("unwritable-value", `Colour "${color}" is not a 6 or 8 digit hex value`, {});
2279
+ }
2280
+ return (hex.length === 6 ? `FF${hex}` : hex).toUpperCase();
2281
+ }
2282
+ function parseColor(attributes) {
2283
+ const rgb = attributes.get("rgb");
2284
+ if (rgb !== void 0 && HEX_COLOR.test(rgb)) return rgb;
2285
+ const themeText = attributes.get("theme");
2286
+ if (themeText !== void 0) {
2287
+ const theme = Number(themeText);
2288
+ if (Number.isInteger(theme) && theme >= 0) {
2289
+ const tint = Number(attributes.get("tint"));
2290
+ return Number.isNaN(tint) ? { theme } : { theme, tint };
2291
+ }
2292
+ }
2293
+ const indexedText = attributes.get("indexed");
2294
+ if (indexedText !== void 0) {
2295
+ const indexed = Number(indexedText);
2296
+ if (Number.isInteger(indexed) && indexed >= 0) return { indexed };
2297
+ }
2298
+ return void 0;
2299
+ }
2300
+ function colorAttributes(color) {
2301
+ if (typeof color === "string") return ` rgb="${normalizeColor(color)}"`;
2302
+ if ("theme" in color) {
2303
+ if (!Number.isInteger(color.theme) || color.theme < 0) {
2304
+ throw new XlsxError(
2305
+ "unwritable-value",
2306
+ `Colour theme "${color.theme}" is not a non-negative integer`,
2307
+ {}
2308
+ );
2309
+ }
2310
+ if (color.tint === void 0) return ` theme="${color.theme}"`;
2311
+ if (!Number.isFinite(color.tint) || color.tint < -1 || color.tint > 1) {
2312
+ throw new XlsxError(
2313
+ "unwritable-value",
2314
+ `Colour tint "${color.tint}" is not between -1 and 1`,
2315
+ {}
2316
+ );
2317
+ }
2318
+ return ` theme="${color.theme}" tint="${color.tint}"`;
2319
+ }
2320
+ if (!Number.isInteger(color.indexed) || color.indexed < 0) {
2321
+ throw new XlsxError(
2322
+ "unwritable-value",
2323
+ `Colour index "${color.indexed}" is not a non-negative integer`,
2324
+ {}
2325
+ );
2326
+ }
2327
+ return ` indexed="${color.indexed}"`;
2328
+ }
2329
+ var flagOn = (value) => value !== "0" && value !== "false";
2330
+ function parseFont(element) {
2331
+ const font = {};
2332
+ for (const event of readXml(element)) {
2333
+ if (event.kind !== "open") continue;
2334
+ switch (event.localName) {
2335
+ case "b":
2336
+ font.bold = flagOn(event.attributes.get("val"));
2337
+ break;
2338
+ case "i":
2339
+ font.italic = flagOn(event.attributes.get("val"));
2340
+ break;
2341
+ case "strike":
2342
+ font.strike = flagOn(event.attributes.get("val"));
2343
+ break;
2344
+ case "u": {
2345
+ const val = event.attributes.get("val");
2346
+ if (val === "none") break;
2347
+ font.underline = toUnderline(val) ?? true;
2348
+ break;
2349
+ }
2350
+ case "vertAlign": {
2351
+ const verticalAlign = toVertAlign(event.attributes.get("val"));
2352
+ if (verticalAlign !== void 0) font.verticalAlign = verticalAlign;
2353
+ break;
2354
+ }
2355
+ case "sz": {
2356
+ const size = Number(event.attributes.get("val"));
2357
+ if (!Number.isNaN(size)) font.size = size;
2358
+ break;
2359
+ }
2360
+ case "color": {
2361
+ const color = parseColor(event.attributes);
2362
+ if (color !== void 0) font.color = color;
2363
+ break;
2364
+ }
2365
+ case "name": {
2366
+ const name2 = event.attributes.get("val");
2367
+ if (name2 !== void 0) font.name = name2;
2368
+ break;
2369
+ }
2370
+ }
2371
+ }
2372
+ return font;
2373
+ }
2374
+ function buildFontElement(font) {
2375
+ let inner = "";
2376
+ if (font.bold === true) inner += "<b/>";
2377
+ if (font.italic === true) inner += "<i/>";
2378
+ if (font.strike === true) inner += "<strike/>";
2379
+ if (font.underline === true || font.underline === "single") inner += "<u/>";
2380
+ else if (typeof font.underline === "string") inner += `<u val="${font.underline}"/>`;
2381
+ if (font.verticalAlign !== void 0) inner += `<vertAlign val="${font.verticalAlign}"/>`;
2382
+ if (font.size !== void 0) {
2383
+ if (!Number.isFinite(font.size) || font.size <= 0) {
2384
+ throw new XlsxError("unwritable-value", `Font size ${font.size} is not a positive number`, {
2385
+ part: "xl/styles.xml"
2386
+ });
2387
+ }
2388
+ inner += `<sz val="${font.size}"/>`;
2389
+ }
2390
+ if (font.color !== void 0) inner += `<color${colorAttributes(font.color)}/>`;
2391
+ if (font.name !== void 0) inner += `<name val="${escapeXml3(font.name)}"/>`;
2392
+ return `<font>${inner}</font>`;
2393
+ }
2394
+ var numericAttributePatterns = /* @__PURE__ */ new Map();
2395
+ var numericAttributePattern = (attribute) => {
2396
+ const cached = numericAttributePatterns.get(attribute);
2397
+ if (cached !== void 0) return cached;
2398
+ const pattern = new RegExp(`\\b${attribute}\\s*=\\s*["'](\\d+)["']`);
2399
+ numericAttributePatterns.set(attribute, pattern);
2400
+ return pattern;
2401
+ };
2402
+ var attrId = (element, attribute) => {
2403
+ const match = element.match(numericAttributePattern(attribute));
2404
+ return match?.[1] === void 0 ? 0 : Number(match[1]);
2405
+ };
2406
+ function idOf(stylesXml, basedOn, attribute) {
2407
+ if (basedOn === void 0) return 0;
2408
+ const element = readTable(stylesXml, "cellXfs", "xf")?.elements[basedOn];
2409
+ return element === void 0 ? 0 : attrId(element, attribute);
2410
+ }
2411
+ function isPlainFormat(stylesXml, index) {
2412
+ const element = readTable(stylesXml, "cellXfs", "xf")?.elements[index];
2413
+ if (element === void 0) return true;
2414
+ const idIsZero = (attribute) => attrId(element, attribute) === 0;
2415
+ return idIsZero("fontId") && idIsZero("fillId") && idIsZero("borderId") && !/<[a-z0-9]*:?alignment[\s/>]/i.test(element) && !/<[a-z0-9]*:?protection[\s/>]/i.test(element);
2416
+ }
2417
+ function withReservedTable(stylesXml, container, child, reservedInner, reservedCount) {
2418
+ const table = readTable(stylesXml, container, child);
2419
+ if (table !== void 0 && table.elements.length > 0) return stylesXml;
2420
+ if (table === void 0) {
2421
+ const { position, prefix: prefix2 } = tableInsertPoint(stylesXml);
2422
+ const block2 = `<${prefix2}${container} count="${reservedCount}">${reservedInner(prefix2)}</${prefix2}${container}>`;
2423
+ return stylesXml.slice(0, position) + block2 + stylesXml.slice(position);
2424
+ }
2425
+ const { prefix } = table;
2426
+ const block = `<${prefix}${container} count="${reservedCount}">${reservedInner(prefix)}</${prefix}${container}>`;
2427
+ const end = table.selfClosing ? table.openEnd : table.insertAt + `</${prefix}${container}>`.length;
2428
+ return stylesXml.slice(0, table.openStart) + block + stylesXml.slice(end);
2429
+ }
2430
+ function withReservedFont(stylesXml) {
2431
+ return withReservedTable(stylesXml, "fonts", "font", (prefix) => `<${prefix}font/>`, 1);
2432
+ }
2433
+ function ensureFontStyle(stylesXml, basedOn, font) {
2434
+ const seeded = withReservedFont(stylesXml);
2435
+ const fonts = readTable(seeded, "fonts", "font");
2436
+ const current = parseFont(fonts?.elements[idOf(seeded, basedOn, "fontId")] ?? "");
2437
+ const merged = {
2438
+ bold: font.bold ?? current.bold,
2439
+ italic: font.italic ?? current.italic,
2440
+ strike: font.strike ?? current.strike,
2441
+ underline: font.underline ?? current.underline,
2442
+ verticalAlign: font.verticalAlign ?? current.verticalAlign,
2443
+ size: font.size ?? current.size,
2444
+ color: font.color ?? current.color,
2445
+ name: font.name ?? current.name
2446
+ };
2447
+ const element = withNamespacePrefix(buildFontElement(merged), tablePrefix(seeded));
2448
+ const { xml, id } = ensureInTable(seeded, "fonts", "font", element);
2449
+ return applyCellFormat(xml, basedOn, { fontId: id });
2450
+ }
2451
+ var RESERVED_FILLS = '<fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill>';
2452
+ function withReservedFills(stylesXml) {
2453
+ return withReservedTable(
2454
+ stylesXml,
2455
+ "fills",
2456
+ "fill",
2457
+ (prefix) => RESERVED_FILLS.replace(/<(\/?)(fill|patternFill)/g, `<$1${prefix}$2`),
2458
+ 2
2459
+ );
2460
+ }
2461
+ function buildFillElement(fill) {
2462
+ const fg = `<fgColor${colorAttributes(fill.color)}/>`;
2463
+ if (fill.type === "solid") {
2464
+ return `<fill><patternFill patternType="solid">${fg}<bgColor indexed="64"/></patternFill></fill>`;
2465
+ }
2466
+ const bg = fill.background === void 0 ? '<bgColor indexed="64"/>' : `<bgColor${colorAttributes(fill.background)}/>`;
2467
+ return `<fill><patternFill patternType="${fill.pattern}">${fg}${bg}</patternFill></fill>`;
2468
+ }
2469
+ function ensureFillStyle(stylesXml, basedOn, fill) {
2470
+ const seeded = withReservedFills(stylesXml);
2471
+ const element = withNamespacePrefix(buildFillElement(fill), tablePrefix(seeded));
2472
+ const { xml, id } = ensureInTable(seeded, "fills", "fill", element);
2473
+ return applyCellFormat(xml, basedOn, { fillId: id });
2474
+ }
2475
+ var SIDE_NAMES = ["left", "right", "top", "bottom"];
2476
+ var RESERVED_BORDER = "<border><left/><right/><top/><bottom/><diagonal/></border>";
2477
+ function withReservedBorder(stylesXml) {
2478
+ return withReservedTable(
2479
+ stylesXml,
2480
+ "borders",
2481
+ "border",
2482
+ (prefix) => RESERVED_BORDER.replace(/<(\/?)(border|left|right|top|bottom|diagonal)/g, `<$1${prefix}$2`),
2483
+ 1
2484
+ );
2485
+ }
2486
+ function parseBorder(element) {
2487
+ const sides = {};
2488
+ let inSide;
2489
+ for (const event of readXml(element)) {
2490
+ if (event.kind === "close") {
2491
+ if (inSide !== void 0 && event.localName === inSide) inSide = void 0;
2492
+ continue;
2493
+ }
2494
+ if (event.kind !== "open") continue;
2495
+ if (event.localName === "left" || event.localName === "right") inSide = event.localName;
2496
+ else if (event.localName === "top" || event.localName === "bottom") inSide = event.localName;
2497
+ else {
2498
+ if (event.localName === "color" && inSide !== void 0) {
2499
+ const color = parseColor(event.attributes);
2500
+ const style2 = sides[inSide]?.style;
2501
+ if (color !== void 0 && style2 !== void 0) sides[inSide] = { style: style2, color };
2502
+ }
2503
+ continue;
2504
+ }
2505
+ const style = event.attributes.get("style");
2506
+ if (style !== void 0) sides[inSide] = { style };
2507
+ if (event.selfClosing) inSide = void 0;
2508
+ }
2509
+ return sides;
2510
+ }
2511
+ var borderSidesAt = (stylesXml, id) => {
2512
+ const element = readTable(stylesXml, "borders", "border")?.elements[id];
2513
+ return element === void 0 ? {} : parseBorder(element);
2514
+ };
2515
+ var buildSide = (name2, side) => {
2516
+ if (side === void 0) return `<${name2}/>`;
2517
+ if (side.color === void 0) return `<${name2} style="${side.style}"/>`;
2518
+ return `<${name2} style="${side.style}"><color${colorAttributes(side.color)}/></${name2}>`;
2519
+ };
2520
+ var buildBorderElement = (sides) => `<border>${buildSide("left", sides.left)}${buildSide("right", sides.right)}${buildSide("top", sides.top)}${buildSide("bottom", sides.bottom)}<diagonal/></border>`;
2521
+ function ensureBorderStyle(stylesXml, basedOn, border) {
2522
+ const seeded = withReservedBorder(stylesXml);
2523
+ const current = borderSidesAt(seeded, idOf(seeded, basedOn, "borderId"));
2524
+ const merged = {};
2525
+ for (const name2 of SIDE_NAMES) {
2526
+ const side = border[name2] ?? border.all ?? current[name2];
2527
+ if (side !== void 0) merged[name2] = side;
2528
+ }
2529
+ const element = withNamespacePrefix(buildBorderElement(merged), tablePrefix(seeded));
2530
+ const { xml, id } = ensureInTable(seeded, "borders", "border", element);
2531
+ return applyCellFormat(xml, basedOn, { borderId: id });
2532
+ }
2533
+ var numberAttribute = (value) => {
2534
+ if (value === void 0) return void 0;
2535
+ const parsed = Number(value);
2536
+ return Number.isNaN(parsed) ? void 0 : parsed;
2537
+ };
2538
+ function parseAlignment(xf) {
2539
+ const out = {};
2540
+ for (const event of readXml(xf)) {
2541
+ if (event.kind !== "open" || event.localName !== "alignment") continue;
2542
+ const horizontal = event.attributes.get("horizontal");
2543
+ if (horizontal !== void 0) out.horizontal = horizontal;
2544
+ const vertical = event.attributes.get("vertical");
2545
+ if (vertical !== void 0) out.vertical = vertical;
2546
+ const wrapText = event.attributes.get("wrapText");
2547
+ if (wrapText !== void 0) out.wrapText = flagOn(wrapText);
2548
+ const rotation = numberAttribute(event.attributes.get("textRotation"));
2549
+ if (rotation !== void 0) out.textRotation = rotation;
2550
+ const indent = numberAttribute(event.attributes.get("indent"));
2551
+ if (indent !== void 0) out.indent = indent;
2552
+ }
2553
+ return out;
2554
+ }
2555
+ function buildAlignmentElement(alignment, prefix) {
2556
+ let attributes = "";
2557
+ if (alignment.horizontal !== void 0) attributes += ` horizontal="${alignment.horizontal}"`;
2558
+ if (alignment.vertical !== void 0) attributes += ` vertical="${alignment.vertical}"`;
2559
+ if (alignment.wrapText === true) attributes += ' wrapText="1"';
2560
+ if (alignment.textRotation !== void 0)
2561
+ attributes += ` textRotation="${alignment.textRotation}"`;
2562
+ if (alignment.indent !== void 0) attributes += ` indent="${alignment.indent}"`;
2563
+ return `<${prefix}alignment${attributes}/>`;
2564
+ }
2565
+ var ALIGNMENT_CHILD = /<(?:[A-Za-z0-9]+:)?alignment\b(?:[^>]*\/>|[\s\S]*?<\/(?:[A-Za-z0-9]+:)?alignment>)/;
2566
+ function withAlignmentChild(xf, alignment, prefix) {
2567
+ const close = xf.indexOf(">");
2568
+ const selfClosing = xf.charAt(close - 1) === "/";
2569
+ const openTag = withApplyFlag(
2570
+ `${xf.slice(0, selfClosing ? close - 1 : close)}>`,
2571
+ "applyAlignment"
2572
+ );
2573
+ const body = selfClosing ? "" : xf.slice(close + 1, xf.lastIndexOf("</"));
2574
+ return `${openTag}${alignment}${body.replace(ALIGNMENT_CHILD, "")}</${prefix}xf>`;
2575
+ }
2576
+ function validateAlignment(alignment) {
2577
+ const { textRotation, indent } = alignment;
2578
+ if (textRotation !== void 0 && (!Number.isInteger(textRotation) || textRotation < 0 || textRotation > 180 && textRotation !== 255)) {
2579
+ throw new XlsxError("unwritable-value", `Text rotation ${textRotation} is not 0\u2013180 or 255`, {
2580
+ part: "xl/styles.xml"
2581
+ });
2582
+ }
2583
+ if (indent !== void 0 && (!Number.isInteger(indent) || indent < 0)) {
2584
+ throw new XlsxError("unwritable-value", `Indent ${indent} is not a whole number of steps`, {
2585
+ part: "xl/styles.xml"
2586
+ });
2587
+ }
2588
+ }
2589
+ function ensureAlignmentStyle(stylesXml, basedOn, alignment) {
2590
+ validateAlignment(alignment);
2591
+ const prefix = tablePrefix(stylesXml);
2592
+ const base = basedOn === void 0 ? DEFAULT_XF : readTable(stylesXml, "cellXfs", "xf")?.elements[basedOn] ?? DEFAULT_XF;
2593
+ const current = parseAlignment(base);
2594
+ const merged = {
2595
+ horizontal: alignment.horizontal ?? current.horizontal,
2596
+ vertical: alignment.vertical ?? current.vertical,
2597
+ wrapText: alignment.wrapText ?? current.wrapText,
2598
+ textRotation: alignment.textRotation ?? current.textRotation,
2599
+ indent: alignment.indent ?? current.indent
2600
+ };
2601
+ const wanted = withAlignmentChild(base, buildAlignmentElement(merged, prefix), prefix);
2602
+ const { xml, id } = ensureInTable(stylesXml, "cellXfs", "xf", wanted);
2603
+ return { xml, index: id };
2604
+ }
2605
+ function parseProtection(xf) {
2606
+ const out = {};
2607
+ for (const event of readXml(xf)) {
2608
+ if (event.kind !== "open" || event.localName !== "protection") continue;
2609
+ const locked = event.attributes.get("locked");
2610
+ if (locked !== void 0) out.locked = flagOn(locked);
2611
+ const hidden = event.attributes.get("hidden");
2612
+ if (hidden !== void 0) out.hidden = flagOn(hidden);
2613
+ }
2614
+ return out;
2615
+ }
2616
+ function buildProtectionElement(protection, prefix) {
2617
+ let attributes = "";
2618
+ if (protection.locked !== void 0) attributes += ` locked="${protection.locked ? "1" : "0"}"`;
2619
+ if (protection.hidden !== void 0) attributes += ` hidden="${protection.hidden ? "1" : "0"}"`;
2620
+ return `<${prefix}protection${attributes}/>`;
2621
+ }
2622
+ var PROTECTION_CHILD = /<(?:[A-Za-z0-9]+:)?protection\b[^>]*\/>/;
2623
+ function withProtectionChild(xf, protection, prefix) {
2624
+ const close = xf.indexOf(">");
2625
+ const selfClosing = xf.charAt(close - 1) === "/";
2626
+ const openTag = withApplyFlag(
2627
+ `${xf.slice(0, selfClosing ? close - 1 : close)}>`,
2628
+ "applyProtection"
2629
+ );
2630
+ const body = (selfClosing ? "" : xf.slice(close + 1, xf.lastIndexOf("</"))).replace(
2631
+ PROTECTION_CHILD,
2632
+ ""
2633
+ );
2634
+ const extLst = body.match(/<(?:[A-Za-z0-9]+:)?extLst\b/);
2635
+ const at = extLst?.index ?? body.length;
2636
+ return `${openTag}${body.slice(0, at)}${protection}${body.slice(at)}</${prefix}xf>`;
2637
+ }
2638
+ function ensureProtectionStyle(stylesXml, basedOn, protection) {
2639
+ const prefix = tablePrefix(stylesXml);
2640
+ const base = basedOn === void 0 ? DEFAULT_XF : readTable(stylesXml, "cellXfs", "xf")?.elements[basedOn] ?? DEFAULT_XF;
2641
+ const current = parseProtection(base);
2642
+ const merged = {
2643
+ locked: protection.locked ?? current.locked,
2644
+ hidden: protection.hidden ?? current.hidden
2645
+ };
2646
+ const wanted = withProtectionChild(base, buildProtectionElement(merged, prefix), prefix);
2647
+ const { xml, id } = ensureInTable(stylesXml, "cellXfs", "xf", wanted);
2648
+ return { xml, index: id };
2649
+ }
2650
+ function tablePrefix(xml) {
2651
+ for (const event of readXml(xml)) {
2652
+ if (event.kind !== "open") continue;
2653
+ if (event.localName !== "numFmts" && event.localName !== "styleSheet") continue;
2654
+ const colon = event.name.indexOf(":");
2655
+ return colon === -1 ? "" : event.name.slice(0, colon + 1);
2656
+ }
2657
+ return "";
2658
+ }
2659
+ function usedFormatIds(xml) {
2660
+ const used = /* @__PURE__ */ new Set();
2661
+ for (const event of readXml(xml)) {
2662
+ if (event.kind !== "open") continue;
2663
+ const declared = event.attributes.get("numFmtId");
2664
+ if (declared === void 0) continue;
2665
+ const id = Number(declared);
2666
+ if (!Number.isNaN(id)) used.add(id);
2667
+ }
2668
+ return used;
2669
+ }
2670
+ function withNumberFormat(xml, id, code, prefix) {
2671
+ const element = `<${prefix}numFmt numFmtId="${id}" formatCode="${escapeXml3(code)}"/>`;
2672
+ let openStart = -1;
2673
+ let openEnd = -1;
2674
+ let children = 0;
2675
+ for (const event of readXml(xml)) {
2676
+ if (event.kind === "open" && event.localName === "numFmts" && event.selfClosing) {
2677
+ const opened = `${withAttribute(xml.slice(event.start, event.end - 2), "count", 1)}>`;
2678
+ return `${xml.slice(0, event.start)}${opened}${element}</${prefix}numFmts>${xml.slice(event.end)}`;
2679
+ }
2680
+ if (event.kind === "open" && event.localName === "numFmts") {
2681
+ openStart = event.start;
2682
+ openEnd = event.end;
2683
+ continue;
2684
+ }
2685
+ if (openStart !== -1 && event.kind === "open" && event.localName === "numFmt") {
2686
+ children++;
2687
+ continue;
2688
+ }
2689
+ if (event.kind === "close" && event.localName === "numFmts" && openStart !== -1) {
2690
+ const head = xml.slice(0, openStart) + withAttribute(xml.slice(openStart, openEnd), "count", children + 1) + xml.slice(openEnd, event.start);
2691
+ return head + element + xml.slice(event.start);
2692
+ }
2693
+ }
2694
+ for (const event of readXml(xml)) {
2695
+ if (event.kind !== "open" || event.localName !== "styleSheet") continue;
2696
+ const table = `<${prefix}numFmts count="1">${element}</${prefix}numFmts>`;
2697
+ return xml.slice(0, event.end) + table + xml.slice(event.end);
2698
+ }
2699
+ throw new XlsxError("malformed-xml", "Style table has no styleSheet element", {
2700
+ part: "xl/styles.xml"
2701
+ });
2702
+ }
2703
+ function ensureNumberFormat(stylesXml, basedOn, formatCode) {
2704
+ const unwritable = findUnwritableCharacter(formatCode);
2705
+ if (unwritable !== void 0) {
2706
+ throw new XlsxError(
2707
+ "unwritable-value",
2708
+ `Number format holds ${unwritable}, which cannot be written to xml`,
2709
+ { part: "xl/styles.xml" }
2710
+ );
2711
+ }
2712
+ const parsed = readStyles(stylesXml);
2713
+ if (basedOn !== void 0 && numberFormatOf(parsed, basedOn) === formatCode) {
2714
+ return { xml: stylesXml, index: basedOn };
2715
+ }
2716
+ if (basedOn === void 0) {
2717
+ for (let index = 0; index < parsed.cellFormats.length; index++) {
2718
+ if (numberFormatOf(parsed, index) === formatCode && isPlainFormat(stylesXml, index)) {
2719
+ return { xml: stylesXml, index };
2720
+ }
2721
+ }
2722
+ }
2723
+ let formatId = builtInFormatId(formatCode);
2724
+ let withFormat = stylesXml;
2725
+ const prefix = tablePrefix(stylesXml);
2726
+ if (formatId === void 0) {
2727
+ for (const [id, code] of parsed.numberFormats) {
2728
+ if (code === formatCode) formatId = id;
2729
+ }
2730
+ }
2731
+ if (formatId === void 0) {
2732
+ const used = usedFormatIds(stylesXml);
2733
+ formatId = FIRST_CUSTOM_FORMAT_ID;
2734
+ while (used.has(formatId)) formatId++;
2735
+ withFormat = withNumberFormat(stylesXml, formatId, formatCode, prefix);
2736
+ }
2737
+ return applyCellFormat(withFormat, basedOn, { numFmtId: formatId });
2738
+ }
2739
+ var PATTERN_STYLES = /* @__PURE__ */ new Set([
2740
+ "gray125",
2741
+ "gray0625",
2742
+ "mediumGray",
2743
+ "darkGray",
2744
+ "lightGray",
2745
+ "darkHorizontal",
2746
+ "darkVertical",
2747
+ "darkDown",
2748
+ "darkUp",
2749
+ "darkGrid",
2750
+ "darkTrellis",
2751
+ "lightHorizontal",
2752
+ "lightVertical",
2753
+ "lightDown",
2754
+ "lightUp",
2755
+ "lightGrid",
2756
+ "lightTrellis"
2757
+ ]);
2758
+ var toPatternStyle = (value) => {
2759
+ for (const known of PATTERN_STYLES) if (known === value) return known;
2760
+ return void 0;
2761
+ };
2762
+ function parseFill(element) {
2763
+ let patternType;
2764
+ let foreground;
2765
+ let background;
2766
+ for (const event of readXml(element)) {
2767
+ if (event.kind !== "open") continue;
2768
+ if (event.localName === "patternFill") patternType = event.attributes.get("patternType");
2769
+ if (event.localName === "fgColor") foreground = parseColor(event.attributes) ?? foreground;
2770
+ if (event.localName === "bgColor") {
2771
+ const color = parseColor(event.attributes);
2772
+ if (color !== void 0 && !(typeof color === "object" && "indexed" in color))
2773
+ background = color;
2774
+ }
2775
+ }
2776
+ if (foreground === void 0) return void 0;
2777
+ if (patternType === "solid") return { type: "solid", color: foreground };
2778
+ const pattern = toPatternStyle(patternType);
2779
+ if (pattern === void 0) return void 0;
2780
+ return background === void 0 ? { type: "pattern", pattern, color: foreground } : { type: "pattern", pattern, color: foreground, background };
2781
+ }
2782
+ var BORDER_STYLES = /* @__PURE__ */ new Set([
2783
+ "thin",
2784
+ "medium",
2785
+ "thick",
2786
+ "dashed",
2787
+ "dotted",
2788
+ "double",
2789
+ "hair",
2790
+ "mediumDashed",
2791
+ "dashDot",
2792
+ "mediumDashDot",
2793
+ "dashDotDot",
2794
+ "mediumDashDotDot",
2795
+ "slantDashDot"
2796
+ ]);
2797
+ function toBorderStyle(value) {
2798
+ for (const known of BORDER_STYLES) if (known === value) return known;
2799
+ return void 0;
2800
+ }
2801
+ function fontFrom(xf, fonts) {
2802
+ const id = attrId(xf, "fontId");
2803
+ const element = fonts[id];
2804
+ if (id === 0 || element === void 0) return void 0;
2805
+ const font = parseFont(element);
2806
+ return Object.keys(font).length === 0 ? void 0 : font;
2807
+ }
2808
+ var fillFrom = (xf, fills) => {
2809
+ const element = fills[attrId(xf, "fillId")];
2810
+ return element === void 0 ? void 0 : parseFill(element);
2811
+ };
2812
+ function borderFrom(xf, borders) {
2813
+ const element = borders[attrId(xf, "borderId")];
2814
+ if (element === void 0) return void 0;
2815
+ const sides = parseBorder(element);
2816
+ const border = {};
2817
+ for (const name2 of SIDE_NAMES) {
2818
+ const side = sides[name2];
2819
+ const style = side === void 0 ? void 0 : toBorderStyle(side.style);
2820
+ if (side === void 0 || style === void 0) continue;
2821
+ border[name2] = side.color === void 0 ? { style } : { style, color: side.color };
2822
+ }
2823
+ return Object.keys(border).length === 0 ? void 0 : border;
2824
+ }
2825
+ var HORIZONTAL_ALIGNMENTS = /* @__PURE__ */ new Set([
2826
+ "general",
2827
+ "left",
2828
+ "center",
2829
+ "right",
2830
+ "fill",
2831
+ "justify",
2832
+ "centerContinuous",
2833
+ "distributed"
2834
+ ]);
2835
+ var VERTICAL_ALIGNMENTS = /* @__PURE__ */ new Set([
2836
+ "top",
2837
+ "center",
2838
+ "bottom",
2839
+ "justify",
2840
+ "distributed"
2841
+ ]);
2842
+ var toHorizontal = (value) => {
2843
+ for (const known of HORIZONTAL_ALIGNMENTS) if (known === value) return known;
2844
+ return void 0;
2845
+ };
2846
+ var toVertical = (value) => {
2847
+ for (const known of VERTICAL_ALIGNMENTS) if (known === value) return known;
2848
+ return void 0;
2849
+ };
2850
+ function alignmentFrom(xf) {
2851
+ const parsed = parseAlignment(xf);
2852
+ const horizontal = toHorizontal(parsed.horizontal);
2853
+ const vertical = toVertical(parsed.vertical);
2854
+ const alignment = {};
2855
+ if (horizontal !== void 0) alignment.horizontal = horizontal;
2856
+ if (vertical !== void 0) alignment.vertical = vertical;
2857
+ if (parsed.wrapText !== void 0) alignment.wrapText = parsed.wrapText;
2858
+ if (parsed.textRotation !== void 0) alignment.textRotation = parsed.textRotation;
2859
+ if (parsed.indent !== void 0) alignment.indent = parsed.indent;
2860
+ return Object.keys(alignment).length === 0 ? void 0 : alignment;
2861
+ }
2862
+ function protectionFrom(xf) {
2863
+ const parsed = parseProtection(xf);
2864
+ return Object.keys(parsed).length === 0 ? void 0 : parsed;
2865
+ }
2866
+ function readFormatting(stylesXml) {
2867
+ const xfs = readTable(stylesXml, "cellXfs", "xf")?.elements ?? [];
2868
+ const fonts = readTable(stylesXml, "fonts", "font")?.elements ?? [];
2869
+ const fills = readTable(stylesXml, "fills", "fill")?.elements ?? [];
2870
+ const borders = readTable(stylesXml, "borders", "border")?.elements ?? [];
2871
+ return xfs.map((xf) => {
2872
+ const font = fontFrom(xf, fonts);
2873
+ const fill = fillFrom(xf, fills);
2874
+ const border = borderFrom(xf, borders);
2875
+ const alignment = alignmentFrom(xf);
2876
+ const protection = protectionFrom(xf);
2877
+ return {
2878
+ ...font === void 0 ? {} : { font },
2879
+ ...fill === void 0 ? {} : { fill },
2880
+ ...border === void 0 ? {} : { border },
2881
+ ...alignment === void 0 ? {} : { alignment },
2882
+ ...protection === void 0 ? {} : { protection }
2883
+ };
2884
+ });
2885
+ }
2886
+
2887
+ // src/lib/workbook.ts
2888
+ var OFFICE_DOCUMENT = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
2889
+ var WORKSHEET = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet";
2890
+ var ROOT_RELATIONSHIPS = "_rels/.rels";
2891
+ function partText(container, path) {
2892
+ const bytes = container.parts.get(path);
2893
+ if (bytes === void 0)
2894
+ throw new XlsxError("missing-part", `Missing part ${path}`, { part: path });
2895
+ return decodeXmlPart(bytes, path);
2896
+ }
2897
+ function findWorkbookPath(container) {
2898
+ if (!container.parts.has(ROOT_RELATIONSHIPS)) {
2899
+ throw new XlsxError("missing-part", `Missing part ${ROOT_RELATIONSHIPS}`, {
2900
+ part: ROOT_RELATIONSHIPS
2901
+ });
2902
+ }
2903
+ for (const relationship of readRelationships(
2904
+ partText(container, ROOT_RELATIONSHIPS),
2905
+ ROOT_RELATIONSHIPS
2906
+ ).values()) {
2907
+ if (relationship.type === OFFICE_DOCUMENT) return resolveTarget("", relationship.target);
2908
+ }
2909
+ throw new XlsxError("missing-part", "Package declares no workbook part", {
2910
+ part: ROOT_RELATIONSHIPS
2911
+ });
2912
+ }
2913
+ function toSheetState(value) {
2914
+ if (value === "hidden") return "hidden";
2915
+ if (value === "veryHidden") return "veryHidden";
2916
+ return "visible";
2917
+ }
2918
+ var isTrue = (value) => value === "1" || value === "true";
2919
+ function relationshipId2(attributes) {
2920
+ const direct = attributes.get("r:id");
2921
+ if (direct !== void 0) return direct;
2922
+ for (const [name2, value] of attributes) {
2923
+ if (name2 === "id" || name2.endsWith(":id")) return value;
2924
+ }
2925
+ return void 0;
2926
+ }
2927
+ function readWorkbookPart(bytes) {
2928
+ const container = readContainer(bytes);
2929
+ const workbookPath = findWorkbookPath(container);
2930
+ const workbookXml = partText(container, workbookPath);
2931
+ const relationshipsPath = workbookPath.replace(/([^/]+)$/, "_rels/$1.rels");
2932
+ const relationships = container.parts.has(relationshipsPath) ? readRelationships(partText(container, relationshipsPath), relationshipsPath) : /* @__PURE__ */ new Map();
2933
+ const sheets = [];
2934
+ let date1904 = false;
2935
+ for (const event of readXml(workbookXml)) {
2936
+ if (event.kind !== "open") continue;
2937
+ if (event.localName === "workbookPr") {
2938
+ date1904 = isTrue(event.attributes.get("date1904"));
2939
+ continue;
2940
+ }
2941
+ if (event.localName !== "sheet") continue;
2942
+ const id = relationshipId2(event.attributes);
2943
+ const relationship = id === void 0 ? void 0 : relationships.get(id);
2944
+ if (relationship === void 0 || relationship.external) continue;
2945
+ if (relationship.type !== WORKSHEET) continue;
2946
+ sheets.push({
2947
+ name: event.attributes.get("name") ?? "",
2948
+ sheetId: event.attributes.get("sheetId") ?? "",
2949
+ path: resolveTarget(workbookPath, relationship.target),
2950
+ state: toSheetState(event.attributes.get("state"))
2951
+ });
2952
+ }
2953
+ return { path: workbookPath, relationshipsPath, sheets, date1904, container };
2954
+ }
2955
+
2956
+ // src/lib/document.ts
2957
+ var EMPTY_STYLES = { numberFormats: /* @__PURE__ */ new Map(), cellFormats: [] };
2958
+ var EMPTY_EDITS = /* @__PURE__ */ new Map();
2959
+ var CALCULATION_CHAIN = "xl/calcChain.xml";
2960
+ var CONTENT_TYPES = "[Content_Types].xml";
2961
+ var AFTER_CALC_PR = /* @__PURE__ */ new Set([
2962
+ "oleSize",
2963
+ "customWorkbookViews",
2964
+ "pivotCaches",
2965
+ "smartTagPr",
2966
+ "smartTagTypes",
2967
+ "webPublishing",
2968
+ "fileRecoveryPr",
2969
+ "webPublishObjects",
2970
+ "extLst"
2971
+ ]);
2972
+ function withRecalculation(xml) {
2973
+ for (const event of readXml(xml)) {
2974
+ if (event.kind !== "open" || event.localName !== "calcPr") continue;
2975
+ const tag = xml.slice(event.start, event.end);
2976
+ if (tag.includes("fullCalcOnLoad=")) {
2977
+ return xml.slice(0, event.start) + // Either quote character is legal, and matching only double quotes
2978
+ // left the check passing while the rewrite did nothing.
2979
+ tag.replace(/fullCalcOnLoad=("|')[^"']*\1/, 'fullCalcOnLoad="1"') + xml.slice(event.end);
2980
+ }
2981
+ const opened = tag.replace(
2982
+ /\/?>$/,
2983
+ (end) => end === "/>" ? ' fullCalcOnLoad="1"/>' : ' fullCalcOnLoad="1">'
2984
+ );
2985
+ return xml.slice(0, event.start) + opened + xml.slice(event.end);
2986
+ }
2987
+ let depth = 0;
2988
+ let insertAt = -1;
2989
+ for (const event of readXml(xml)) {
2990
+ if (event.kind === "open") {
2991
+ if (depth === 1 && insertAt === -1 && AFTER_CALC_PR.has(event.localName)) {
2992
+ insertAt = event.start;
2993
+ }
2994
+ if (!event.selfClosing) depth++;
2995
+ continue;
2996
+ }
2997
+ if (event.kind !== "close") continue;
2998
+ depth--;
2999
+ if (event.localName !== "workbook" || depth !== 0) continue;
3000
+ const colon = event.name.indexOf(":");
3001
+ const prefix = colon === -1 ? "" : event.name.slice(0, colon + 1);
3002
+ const element = `<${prefix}calcPr fullCalcOnLoad="1"/>`;
3003
+ const at = insertAt === -1 ? event.start : insertAt;
3004
+ return xml.slice(0, at) + element + xml.slice(at);
3005
+ }
3006
+ return xml;
3007
+ }
3008
+ function withoutRelationship(xml, ownerPath, part) {
3009
+ for (const event of readXml(xml)) {
3010
+ if (event.kind !== "open" || event.localName !== "Relationship") continue;
3011
+ if (event.attributes.get("TargetMode") === "External") continue;
3012
+ const target = event.attributes.get("Target");
3013
+ if (target === void 0 || resolveTarget(ownerPath, target) !== part) continue;
3014
+ return xml.slice(0, event.start) + xml.slice(event.end);
3015
+ }
3016
+ return xml;
3017
+ }
3018
+ function withoutOverride(xml, part) {
3019
+ for (const event of readXml(xml)) {
3020
+ if (event.kind !== "open" || event.localName !== "Override") continue;
3021
+ if (event.attributes.get("PartName") !== `/${part}`) continue;
3022
+ return xml.slice(0, event.start) + xml.slice(event.end);
3023
+ }
3024
+ return xml;
3025
+ }
3026
+ function partText2(container, path) {
3027
+ const bytes = container.parts.get(path);
3028
+ if (bytes === void 0) return void 0;
3029
+ return decodeXmlPart(bytes, path);
3030
+ }
3031
+ function toCellValue(raw, styles, date1904) {
3032
+ const value = raw.value;
3033
+ if (value.kind === "date") {
3034
+ const parsed = parseIsoDate(value.value);
3035
+ if (parsed === void 0) return { kind: "text", value: value.value };
3036
+ return { kind: "date", value: parsed, serial: dateToSerial(parsed, date1904) };
3037
+ }
3038
+ if (value.kind === "number" && isDateFormat(styles, raw.styleIndex)) {
3039
+ const serial = value.value;
3040
+ if (serial >= 0 && serial <= LAST_SERIAL) {
3041
+ return { kind: "date", value: serialToDate(serial, date1904), serial };
3042
+ }
3043
+ }
3044
+ return value;
3045
+ }
3046
+ function toFormula(raw, masters) {
3047
+ if (raw.formula === void 0) return void 0;
3048
+ if (raw.sharedIndex === void 0 || raw.ownsSharedRange === true) {
3049
+ return { kind: "expression", expression: raw.formula };
3050
+ }
3051
+ const master = masters?.get(raw.sharedIndex);
3052
+ return master === void 0 ? { kind: "shared" } : { kind: "shared", master };
3053
+ }
3054
+ function toCell(raw, styles, formatting, date1904, masters) {
3055
+ const numberFormat = numberFormatOf(styles, raw.styleIndex);
3056
+ const value = toCellValue(raw, styles, date1904);
3057
+ const formula = toFormula(raw, masters);
3058
+ return {
3059
+ address: raw.address,
3060
+ // Not the file's spelling: $A$1 and a1 are the same cell, and a caller that
3061
+ // cross-references this against set() or formatReference needs one answer.
3062
+ reference: canonicalReference(raw.address) ?? raw.reference,
3063
+ value,
3064
+ ...formula === void 0 ? {} : { formula },
3065
+ ...numberFormat === void 0 ? {} : { numberFormat },
3066
+ ...formatting
3067
+ };
3068
+ }
3069
+ function readWorkbook(bytes) {
3070
+ const part = readWorkbookPart(bytes);
3071
+ const { container, date1904 } = part;
3072
+ const stylesXml = partText2(container, "xl/styles.xml");
3073
+ const styles = stylesXml === void 0 ? EMPTY_STYLES : readStyles(stylesXml);
3074
+ const stringsXml = partText2(container, "xl/sharedStrings.xml");
3075
+ const sharedStrings = stringsXml === void 0 ? [] : readSharedStrings(stringsXml);
3076
+ const edits = /* @__PURE__ */ new Map();
3077
+ const styleOverrides = /* @__PURE__ */ new Map();
3078
+ const sheetProtections = /* @__PURE__ */ new Map();
3079
+ const sheetMerges = /* @__PURE__ */ new Map();
3080
+ const sheetRowHeights = /* @__PURE__ */ new Map();
3081
+ const sheetColumnWidths = /* @__PURE__ */ new Map();
3082
+ let workingStyles = stylesXml;
3083
+ let parsedStyles = styles;
3084
+ let parsedFrom = stylesXml;
3085
+ const stylesNow = () => {
3086
+ if (workingStyles !== void 0 && workingStyles !== parsedFrom) {
3087
+ parsedStyles = readStyles(workingStyles);
3088
+ parsedFrom = workingStyles;
3089
+ }
3090
+ return parsedStyles;
3091
+ };
3092
+ let parsedFormatting;
3093
+ let formattingFrom;
3094
+ const formattingFor = (styleIndex) => {
3095
+ if (styleIndex === void 0 || workingStyles === void 0) return {};
3096
+ if (parsedFormatting === void 0 || workingStyles !== formattingFrom) {
3097
+ parsedFormatting = readFormatting(workingStyles);
3098
+ formattingFrom = workingStyles;
3099
+ }
3100
+ return parsedFormatting[styleIndex] ?? {};
3101
+ };
3102
+ const sheets = part.sheets.map((reference) => {
3103
+ const sheetBytes = container.parts.get(reference.path);
3104
+ const at = { sheet: reference.name, part: reference.path };
3105
+ const patched = () => {
3106
+ if (sheetBytes === void 0) return void 0;
3107
+ const pending = edits.get(reference.path);
3108
+ const overrides = styleOverrides.get(reference.path);
3109
+ const protection = sheetProtections.get(reference.path);
3110
+ const merges = sheetMerges.get(reference.path);
3111
+ const rowHeights = sheetRowHeights.get(reference.path);
3112
+ const columnWidths = sheetColumnWidths.get(reference.path);
3113
+ if (pending === void 0 && overrides === void 0 && protection === void 0 && merges === void 0 && rowHeights === void 0 && columnWidths === void 0) {
3114
+ return sheetBytes;
3115
+ }
3116
+ return patchSheet(sheetBytes, pending ?? EMPTY_EDITS, date1904, void 0, overrides, at, {
3117
+ protection,
3118
+ merges,
3119
+ rowHeights,
3120
+ columnWidths
3121
+ });
3122
+ };
3123
+ function* readCells(source) {
3124
+ const bytes2 = source ?? patched();
3125
+ if (bytes2 === void 0) return;
3126
+ const masters = /* @__PURE__ */ new Map();
3127
+ for (const raw of readSheet(bytes2, sharedStrings, at)) {
3128
+ if (raw.ownsSharedRange === true && raw.sharedIndex !== void 0) {
3129
+ masters.set(raw.sharedIndex, canonicalReference(raw.address) ?? raw.reference);
3130
+ }
3131
+ yield toCell(raw, stylesNow(), formattingFor(raw.styleIndex), date1904, masters);
3132
+ }
3133
+ }
3134
+ let index;
3135
+ const indexed = () => {
3136
+ if (sheetBytes === void 0) return void 0;
3137
+ index ??= indexSheet(sheetBytes);
3138
+ return index;
3139
+ };
3140
+ const styleAt = (canonical) => styleOverrides.get(reference.path)?.get(canonical) ?? indexed()?.styles.get(canonical);
3141
+ const predict = (canonical, value, styleIndex) => {
3142
+ const address = parseReference(canonical);
3143
+ const style = styleIndex === void 0 ? {} : { styleIndex };
3144
+ const raw = { address, reference: canonical, ...style };
3145
+ const formatting = formattingFor(styleIndex);
3146
+ if (value === null) {
3147
+ return toCell({ ...raw, value: { kind: "empty" } }, stylesNow(), formatting, date1904);
3148
+ }
3149
+ if (typeof value === "number") {
3150
+ return toCell(
3151
+ { ...raw, value: { kind: "number", value } },
3152
+ stylesNow(),
3153
+ formatting,
3154
+ date1904
3155
+ );
3156
+ }
3157
+ if (typeof value === "boolean") {
3158
+ return toCell(
3159
+ { ...raw, value: { kind: "boolean", value } },
3160
+ stylesNow(),
3161
+ formatting,
3162
+ date1904
3163
+ );
3164
+ }
3165
+ if (typeof value === "string") {
3166
+ return toCell({ ...raw, value: { kind: "text", value } }, stylesNow(), formatting, date1904);
3167
+ }
3168
+ if (value instanceof Date) {
3169
+ const serial = dateToSerial(value, date1904);
3170
+ return toCell(
3171
+ { ...raw, value: { kind: "number", value: serial } },
3172
+ stylesNow(),
3173
+ formatting,
3174
+ date1904
3175
+ );
3176
+ }
3177
+ const formula = value.formula;
3178
+ return toCell(
3179
+ { ...raw, value: { kind: "empty" }, formula },
3180
+ stylesNow(),
3181
+ formatting,
3182
+ date1904
3183
+ );
3184
+ };
3185
+ let byReference;
3186
+ const overlay = /* @__PURE__ */ new Map();
3187
+ const findCell = (canonical) => {
3188
+ const edited = overlay.get(canonical);
3189
+ if (edited !== void 0) return edited;
3190
+ if (byReference === void 0) {
3191
+ byReference = /* @__PURE__ */ new Map();
3192
+ for (const found of readCells(sheetBytes)) {
3193
+ const where = canonicalReference(found.address);
3194
+ if (where !== void 0) byReference.set(where, found);
3195
+ }
3196
+ }
3197
+ return byReference.get(canonical);
3198
+ };
3199
+ const restyled = (base, styleIndex) => {
3200
+ const styles2 = stylesNow();
3201
+ const numberFormat = numberFormatOf(styles2, styleIndex);
3202
+ const dateFormat = isDateFormat(styles2, styleIndex);
3203
+ let value = base.value;
3204
+ if (value.kind === "number" && dateFormat && value.value >= 0 && value.value <= LAST_SERIAL) {
3205
+ value = { kind: "date", value: serialToDate(value.value, date1904), serial: value.value };
3206
+ } else if (value.kind === "date" && !dateFormat) {
3207
+ value = { kind: "number", value: value.serial };
3208
+ }
3209
+ return {
3210
+ address: base.address,
3211
+ reference: base.reference,
3212
+ value,
3213
+ ...base.formula === void 0 ? {} : { formula: base.formula },
3214
+ ...numberFormat === void 0 ? {} : { numberFormat },
3215
+ ...formattingFor(styleIndex)
3216
+ };
3217
+ };
3218
+ const resolveStyle = (current, value, options, canonical) => {
3219
+ if (workingStyles === void 0) {
3220
+ if (Object.values(options ?? {}).some((asked) => asked !== void 0)) {
3221
+ throw new XlsxError(
3222
+ "missing-part",
3223
+ `Cannot format ${canonical}: the package has no style table`,
3224
+ { part: "xl/styles.xml", reference: canonical }
3225
+ );
3226
+ }
3227
+ return void 0;
3228
+ }
3229
+ let xml = workingStyles;
3230
+ let base = current;
3231
+ let applied;
3232
+ const step = (next) => {
3233
+ xml = next.xml;
3234
+ base = next.index;
3235
+ applied = next;
3236
+ };
3237
+ if (options?.numberFormat !== void 0)
3238
+ step(ensureNumberFormat(xml, base, options.numberFormat));
3239
+ else if (value instanceof Date) step(ensureDateStyle(xml, base));
3240
+ if (options?.font !== void 0) step(ensureFontStyle(xml, base, options.font));
3241
+ if (options?.fill !== void 0) step(ensureFillStyle(xml, base, options.fill));
3242
+ if (options?.border !== void 0) step(ensureBorderStyle(xml, base, options.border));
3243
+ if (options?.alignment !== void 0) step(ensureAlignmentStyle(xml, base, options.alignment));
3244
+ if (options?.protection !== void 0)
3245
+ step(ensureProtectionStyle(xml, base, options.protection));
3246
+ return applied;
3247
+ };
3248
+ const commitStyle = (canonical, current, applied) => {
3249
+ if (applied === void 0) return current;
3250
+ workingStyles = applied.xml;
3251
+ if (applied.index !== current) {
3252
+ const overrides = styleOverrides.get(reference.path) ?? /* @__PURE__ */ new Map();
3253
+ overrides.set(canonical, applied.index);
3254
+ styleOverrides.set(reference.path, overrides);
3255
+ }
3256
+ return applied.index;
3257
+ };
3258
+ const absent = (canonical, verb) => new XlsxError(
3259
+ "missing-part",
3260
+ `Sheet ${reference.name} is not in the package, so ${canonical} cannot be ${verb}`,
3261
+ { ...at, reference: canonical }
3262
+ );
3263
+ return {
3264
+ name: reference.name,
3265
+ state: reference.state,
3266
+ sheetId: reference.sheetId,
3267
+ get protection() {
3268
+ const pending = sheetProtections.get(reference.path);
3269
+ if (pending === "remove") return void 0;
3270
+ if (pending !== void 0) return pending;
3271
+ return sheetBytes === void 0 ? void 0 : readSheetProtection(sheetBytes);
3272
+ },
3273
+ cells: () => readCells(),
3274
+ cell(cellReference) {
3275
+ const wanted = canonicalReference(parseReference(cellReference));
3276
+ if (wanted === void 0) return void 0;
3277
+ return findCell(wanted);
3278
+ },
3279
+ set(cellReference, value, options) {
3280
+ const canonical = formatReference(parseWritableReference(cellReference));
3281
+ if (sheetBytes === void 0) throw absent(canonical, "written");
3282
+ checkWritable(canonical, value, date1904, at);
3283
+ const index2 = indexed();
3284
+ if (index2 !== void 0) {
3285
+ const si = index2.sharedFormulas.get(canonical);
3286
+ if (si !== void 0) throw sharedFormulaRefusal(canonical, si, at);
3287
+ const anchor = mergeAnchorFor(index2, canonical);
3288
+ if (anchor !== void 0) throw mergeRefusal(canonical, anchor, at);
3289
+ }
3290
+ const current = styleAt(canonical);
3291
+ const applied = resolveStyle(current, value, options, canonical);
3292
+ const pending = edits.get(reference.path) ?? /* @__PURE__ */ new Map();
3293
+ pending.set(canonical, value);
3294
+ edits.set(reference.path, pending);
3295
+ overlay.set(canonical, predict(canonical, value, commitStyle(canonical, current, applied)));
3296
+ },
3297
+ format(cellReference, options) {
3298
+ const canonical = formatReference(parseWritableReference(cellReference));
3299
+ if (sheetBytes === void 0) throw absent(canonical, "formatted");
3300
+ const current = styleAt(canonical);
3301
+ const applied = resolveStyle(current, void 0, options, canonical);
3302
+ if (applied === void 0) return;
3303
+ const existing = findCell(canonical);
3304
+ const resolved = commitStyle(canonical, current, applied);
3305
+ overlay.set(
3306
+ canonical,
3307
+ existing === void 0 ? predict(canonical, null, resolved) : restyled(existing, resolved)
3308
+ );
3309
+ },
3310
+ protect(options = {}) {
3311
+ if (sheetBytes === void 0) {
3312
+ throw new XlsxError(
3313
+ "missing-part",
3314
+ `Sheet ${reference.name} is not in the package, so it cannot be protected`,
3315
+ at
3316
+ );
3317
+ }
3318
+ sheetProtections.set(reference.path, options);
3319
+ },
3320
+ unprotect() {
3321
+ sheetProtections.set(reference.path, "remove");
3322
+ },
3323
+ merge(range) {
3324
+ if (sheetBytes === void 0) {
3325
+ throw new XlsxError(
3326
+ "missing-part",
3327
+ `Sheet ${reference.name} is not in the package, so ${range} cannot be merged`,
3328
+ { ...at, reference: range }
3329
+ );
3330
+ }
3331
+ const canonical = mergeRangeReference(range, at);
3332
+ const pending = sheetMerges.get(reference.path) ?? [];
3333
+ pending.push(canonical);
3334
+ sheetMerges.set(reference.path, pending);
3335
+ },
3336
+ setRowHeight(row, height) {
3337
+ if (sheetBytes === void 0) {
3338
+ throw new XlsxError(
3339
+ "missing-part",
3340
+ `Sheet ${reference.name} is not in the package, so row ${row} cannot be sized`,
3341
+ at
3342
+ );
3343
+ }
3344
+ if (!Number.isInteger(row) || row < 1) {
3345
+ throw new XlsxError("bad-reference", `Row ${row} is not a row number`, { ...at });
3346
+ }
3347
+ if (row > LAST_ROW) {
3348
+ throw new XlsxError("bad-reference", `Row ${row} is outside the sheet`, { ...at });
3349
+ }
3350
+ if (!Number.isFinite(height) || height < 0) {
3351
+ throw new XlsxError("unwritable-value", `Row height ${height} is not zero or more`, {
3352
+ ...at
3353
+ });
3354
+ }
3355
+ const heights = sheetRowHeights.get(reference.path) ?? /* @__PURE__ */ new Map();
3356
+ heights.set(row, height);
3357
+ sheetRowHeights.set(reference.path, heights);
3358
+ },
3359
+ setColumnWidth(column, width) {
3360
+ if (sheetBytes === void 0) {
3361
+ throw new XlsxError(
3362
+ "missing-part",
3363
+ `Sheet ${reference.name} is not in the package, so column ${column} cannot be sized`,
3364
+ { ...at, reference: column }
3365
+ );
3366
+ }
3367
+ const index2 = columnToIndex(column);
3368
+ if (index2 > LAST_COLUMN) {
3369
+ throw new XlsxError("bad-reference", `Column ${column} is outside the sheet`, {
3370
+ ...at,
3371
+ reference: column
3372
+ });
3373
+ }
3374
+ if (!Number.isFinite(width) || width < 0) {
3375
+ throw new XlsxError("unwritable-value", `Column width ${width} is not zero or more`, {
3376
+ ...at,
3377
+ reference: column
3378
+ });
3379
+ }
3380
+ const widths = sheetColumnWidths.get(reference.path) ?? /* @__PURE__ */ new Map();
3381
+ widths.set(index2, width);
3382
+ sheetColumnWidths.set(reference.path, widths);
3383
+ }
3384
+ };
3385
+ });
3386
+ const toBytes = () => {
3387
+ const changes = /* @__PURE__ */ new Map();
3388
+ if (edits.size === 0 && styleOverrides.size === 0 && sheetProtections.size === 0 && sheetMerges.size === 0 && sheetRowHeights.size === 0 && sheetColumnWidths.size === 0) {
3389
+ return container.write(changes);
3390
+ }
3391
+ const encoder2 = new TextEncoder();
3392
+ if (container.parts.has(CALCULATION_CHAIN)) {
3393
+ changes.set(CALCULATION_CHAIN, null);
3394
+ const types = partText2(container, CONTENT_TYPES);
3395
+ if (types !== void 0) {
3396
+ changes.set(CONTENT_TYPES, encoder2.encode(withoutOverride(types, CALCULATION_CHAIN)));
3397
+ }
3398
+ const rels = partText2(container, part.relationshipsPath);
3399
+ if (rels !== void 0) {
3400
+ changes.set(
3401
+ part.relationshipsPath,
3402
+ encoder2.encode(withoutRelationship(rels, part.path, CALCULATION_CHAIN))
3403
+ );
3404
+ }
3405
+ }
3406
+ if (workingStyles !== void 0 && workingStyles !== stylesXml) {
3407
+ changes.set("xl/styles.xml", encoder2.encode(workingStyles));
3408
+ }
3409
+ let indexes;
3410
+ if (stringsXml !== void 0) {
3411
+ const written = [];
3412
+ for (const pending of edits.values()) {
3413
+ for (const value of pending.values()) {
3414
+ if (typeof value === "string") written.push(value);
3415
+ }
3416
+ }
3417
+ if (written.length > 0) {
3418
+ const appended = appendSharedStrings(stringsXml, written);
3419
+ changes.set("xl/sharedStrings.xml", encoder2.encode(appended.xml));
3420
+ indexes = appended.indexes;
3421
+ }
3422
+ }
3423
+ for (const path of /* @__PURE__ */ new Set([
3424
+ ...edits.keys(),
3425
+ ...styleOverrides.keys(),
3426
+ ...sheetProtections.keys(),
3427
+ ...sheetMerges.keys(),
3428
+ ...sheetRowHeights.keys(),
3429
+ ...sheetColumnWidths.keys()
3430
+ ])) {
3431
+ const bytes2 = container.parts.get(path);
3432
+ if (bytes2 === void 0) continue;
3433
+ const pending = edits.get(path) ?? EMPTY_EDITS;
3434
+ const at = {
3435
+ sheet: part.sheets.find((s) => s.path === path)?.name,
3436
+ part: path
3437
+ };
3438
+ changes.set(
3439
+ path,
3440
+ patchSheet(bytes2, pending, date1904, indexes, styleOverrides.get(path), at, {
3441
+ protection: sheetProtections.get(path),
3442
+ merges: sheetMerges.get(path),
3443
+ rowHeights: sheetRowHeights.get(path),
3444
+ columnWidths: sheetColumnWidths.get(path)
3445
+ })
3446
+ );
3447
+ for (const extension of extendTables(bytes2, path, container, pending.keys())) {
3448
+ changes.set(extension.path, encoder2.encode(extension.xml));
3449
+ }
3450
+ }
3451
+ const wroteFormula = [...edits.values()].some(
3452
+ (pending) => [...pending.values()].some(
3453
+ (value) => typeof value === "object" && value !== null && !(value instanceof Date)
3454
+ )
3455
+ );
3456
+ if (wroteFormula) {
3457
+ const book = partText2(container, part.path);
3458
+ if (book !== void 0) changes.set(part.path, encoder2.encode(withRecalculation(book)));
3459
+ }
3460
+ return container.write(changes);
3461
+ };
3462
+ return {
3463
+ sheets,
3464
+ sheet: (name2) => sheets.find((candidate) => candidate.name === name2),
3465
+ epoch: date1904 ? 1904 : 1900,
3466
+ toBytes
3467
+ };
3468
+ }
3469
+
3470
+ exports.XlsxError = XlsxError;
3471
+ exports.columnToIndex = columnToIndex;
3472
+ exports.formatReference = formatReference;
3473
+ exports.indexToColumn = indexToColumn;
3474
+ exports.parseReference = parseReference;
3475
+ exports.readWorkbook = readWorkbook;