@openjsxl/core 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.js ADDED
@@ -0,0 +1,1262 @@
1
+ // src/errors.ts
2
+ var XlsxError = class extends Error {
3
+ /** Machine-readable discriminant; branch on this rather than the message. */
4
+ code;
5
+ constructor(code, message, options) {
6
+ super(message, options);
7
+ this.name = "XlsxError";
8
+ this.code = code;
9
+ }
10
+ };
11
+
12
+ // src/ooxml/a1.ts
13
+ var CODE_UPPER_A = 65;
14
+ var CODE_UPPER_Z = 90;
15
+ var CODE_LOWER_A = 97;
16
+ var CODE_LOWER_Z = 122;
17
+ function columnToIndex(letters) {
18
+ if (letters.length === 0) throw new Error("empty column reference");
19
+ let index = 0;
20
+ for (let i = 0; i < letters.length; i++) {
21
+ const code = letters.charCodeAt(i);
22
+ let value = 0;
23
+ if (code >= CODE_UPPER_A && code <= CODE_UPPER_Z) value = code - CODE_UPPER_A + 1;
24
+ else if (code >= CODE_LOWER_A && code <= CODE_LOWER_Z) value = code - CODE_LOWER_A + 1;
25
+ else throw new Error(`invalid column reference: ${letters}`);
26
+ index = index * 26 + value;
27
+ if (index > Number.MAX_SAFE_INTEGER)
28
+ throw new Error(`column reference too large: ${letters}`);
29
+ }
30
+ return index;
31
+ }
32
+ function indexToColumn(index) {
33
+ if (!Number.isInteger(index) || index < 1) throw new Error(`invalid column index: ${index}`);
34
+ let remaining = index;
35
+ let letters = "";
36
+ while (remaining > 0) {
37
+ const digit = (remaining - 1) % 26;
38
+ letters = String.fromCharCode(CODE_UPPER_A + digit) + letters;
39
+ remaining = Math.floor((remaining - 1) / 26);
40
+ }
41
+ return letters;
42
+ }
43
+ var A1_PATTERN = /^([A-Za-z]+)([1-9][0-9]*)$/;
44
+ function parseRef(ref) {
45
+ const match = A1_PATTERN.exec(ref);
46
+ if (match === null) throw new Error(`invalid A1 reference: ${ref}`);
47
+ return {
48
+ col: columnToIndex(match[1]),
49
+ row: Number.parseInt(match[2], 10)
50
+ };
51
+ }
52
+ function formatRef(ref) {
53
+ if (!Number.isInteger(ref.row) || ref.row < 1) throw new Error(`invalid row index: ${ref.row}`);
54
+ return `${indexToColumn(ref.col)}${ref.row}`;
55
+ }
56
+
57
+ // src/ooxml/dates.ts
58
+ var MS_PER_DAY = 864e5;
59
+ var EPOCH_1900_UTC = Date.UTC(1899, 11, 30);
60
+ var EPOCH_1904_UTC = Date.UTC(1904, 0, 1);
61
+ function serialToDate(serial, date1904 = false) {
62
+ const epoch = date1904 ? EPOCH_1904_UTC : EPOCH_1900_UTC;
63
+ return new Date(epoch + Math.round(serial * MS_PER_DAY));
64
+ }
65
+
66
+ // src/ooxml/cell.ts
67
+ function decodeCell(raw, ctx) {
68
+ const { ref, value } = raw;
69
+ switch (raw.type) {
70
+ case "s": {
71
+ const index = Number.parseInt(value ?? "", 10);
72
+ const resolved = Number.isInteger(index) ? ctx.sharedStrings[index] : void 0;
73
+ return resolved === void 0 ? { ref, type: "empty", value: null } : { ref, type: "string", value: resolved };
74
+ }
75
+ case "inlineStr":
76
+ case "str":
77
+ return value === void 0 ? { ref, type: "empty", value: null } : { ref, type: "string", value };
78
+ case "b":
79
+ return value === void 0 ? { ref, type: "empty", value: null } : { ref, type: "boolean", value: value === "1" };
80
+ case "e":
81
+ return value === void 0 ? { ref, type: "empty", value: null } : { ref, type: "error", value };
82
+ default: {
83
+ if (value === void 0 || value === "") return { ref, type: "empty", value: null };
84
+ const num = Number(value);
85
+ if (!Number.isFinite(num)) return { ref, type: "empty", value: null };
86
+ if (ctx.styles?.isDateStyle(raw.style)) {
87
+ return { ref, type: "date", value: serialToDate(num, ctx.date1904 ?? false) };
88
+ }
89
+ return { ref, type: "number", value: num };
90
+ }
91
+ }
92
+ }
93
+
94
+ // src/utils/chars.ts
95
+ function isWhitespace(ch) {
96
+ return ch === " " || ch === " " || ch === "\n" || ch === "\r";
97
+ }
98
+
99
+ // src/utils/xml-names.ts
100
+ function localName(name) {
101
+ const colon = name.indexOf(":");
102
+ return colon === -1 ? name : name.slice(colon + 1);
103
+ }
104
+ function relationshipId(attrs) {
105
+ if (attrs["r:id"] !== void 0) return attrs["r:id"];
106
+ for (const key of Object.keys(attrs)) {
107
+ if (localName(key) === "id") return attrs[key];
108
+ }
109
+ return void 0;
110
+ }
111
+
112
+ // src/xml/entities.ts
113
+ var ENTITY_PATTERN = /&(#x[0-9a-fA-F]+|#[0-9]+|amp|lt|gt|quot|apos);/g;
114
+ function decodeXmlEntities(input) {
115
+ if (!input.includes("&")) return input;
116
+ return input.replace(ENTITY_PATTERN, (match, body) => {
117
+ switch (body) {
118
+ case "amp":
119
+ return "&";
120
+ case "lt":
121
+ return "<";
122
+ case "gt":
123
+ return ">";
124
+ case "quot":
125
+ return '"';
126
+ case "apos":
127
+ return "'";
128
+ default: {
129
+ const code = body[1] === "x" ? Number.parseInt(body.slice(2), 16) : Number.parseInt(body.slice(1), 10);
130
+ const isScalar = code >= 0 && code <= 1114111 && !(code >= 55296 && code <= 57343);
131
+ return isScalar ? String.fromCodePoint(code) : match;
132
+ }
133
+ }
134
+ });
135
+ }
136
+
137
+ // src/xml/tokenizer.ts
138
+ function* tokenize(xml) {
139
+ const len = xml.length;
140
+ let i = xml.charCodeAt(0) === 65279 ? 1 : 0;
141
+ while (i < len) {
142
+ const lt = xml.indexOf("<", i);
143
+ if (lt === -1) {
144
+ const text = xml.slice(i);
145
+ if (text.length > 0) yield { kind: "text", value: decodeXmlEntities(text) };
146
+ break;
147
+ }
148
+ if (lt > i) {
149
+ yield { kind: "text", value: decodeXmlEntities(xml.slice(i, lt)) };
150
+ }
151
+ const next = xml[lt + 1];
152
+ if (next === "?") {
153
+ const end = xml.indexOf("?>", lt + 2);
154
+ i = end === -1 ? len : end + 2;
155
+ continue;
156
+ }
157
+ if (next === "!") {
158
+ if (xml.startsWith("<!--", lt)) {
159
+ const end2 = xml.indexOf("-->", lt + 4);
160
+ i = end2 === -1 ? len : end2 + 3;
161
+ continue;
162
+ }
163
+ if (xml.startsWith("<![CDATA[", lt)) {
164
+ const end2 = xml.indexOf("]]>", lt + 9);
165
+ const content = xml.slice(lt + 9, end2 === -1 ? len : end2);
166
+ if (content.length > 0) yield { kind: "text", value: content };
167
+ i = end2 === -1 ? len : end2 + 3;
168
+ continue;
169
+ }
170
+ const end = xml.indexOf(">", lt + 2);
171
+ i = end === -1 ? len : end + 1;
172
+ continue;
173
+ }
174
+ if (next === "/") {
175
+ const end = xml.indexOf(">", lt + 2);
176
+ const name2 = xml.slice(lt + 2, end === -1 ? len : end).trim();
177
+ yield { kind: "close", name: name2 };
178
+ i = end === -1 ? len : end + 1;
179
+ continue;
180
+ }
181
+ if (next === void 0 || next === ">" || next === "=" || isWhitespace(next)) {
182
+ yield { kind: "text", value: "<" };
183
+ i = lt + 1;
184
+ continue;
185
+ }
186
+ let j = lt + 1;
187
+ const nameStart = j;
188
+ while (j < len) {
189
+ const ch = xml[j];
190
+ if (isWhitespace(ch) || ch === ">" || ch === "/") break;
191
+ j++;
192
+ }
193
+ const name = xml.slice(nameStart, j);
194
+ const attrs = {};
195
+ let selfClosing = false;
196
+ while (j < len) {
197
+ while (j < len && isWhitespace(xml[j])) j++;
198
+ if (j >= len) break;
199
+ const ch = xml[j];
200
+ if (ch === ">") {
201
+ j++;
202
+ break;
203
+ }
204
+ if (ch === "/") {
205
+ selfClosing = true;
206
+ j++;
207
+ continue;
208
+ }
209
+ const attrNameStart = j;
210
+ while (j < len) {
211
+ const c = xml[j];
212
+ if (c === "=" || c === ">" || c === "/" || isWhitespace(c)) break;
213
+ j++;
214
+ }
215
+ const attrName = xml.slice(attrNameStart, j);
216
+ while (j < len && isWhitespace(xml[j])) j++;
217
+ if (xml[j] === "=") {
218
+ j++;
219
+ while (j < len && isWhitespace(xml[j])) j++;
220
+ const quote = xml[j];
221
+ if (quote === '"' || quote === "'") {
222
+ j++;
223
+ const close = xml.indexOf(quote, j);
224
+ const end = close === -1 ? len : close;
225
+ if (attrName.length > 0) attrs[attrName] = decodeXmlEntities(xml.slice(j, end));
226
+ j = close === -1 ? len : close + 1;
227
+ } else {
228
+ const valStart = j;
229
+ while (j < len && !isWhitespace(xml[j]) && xml[j] !== ">" && xml[j] !== "/") j++;
230
+ if (attrName.length > 0)
231
+ attrs[attrName] = decodeXmlEntities(xml.slice(valStart, j));
232
+ }
233
+ } else if (attrName.length > 0) {
234
+ attrs[attrName] = "";
235
+ }
236
+ }
237
+ yield { kind: "open", name, attrs, selfClosing };
238
+ i = j;
239
+ }
240
+ }
241
+
242
+ // src/xml/stream.ts
243
+ var CDATA_OPEN = "<![CDATA[";
244
+ var COMMENT_OPEN = "<!--";
245
+ var MAX_ENTITY_TAIL = 16;
246
+ function isPartialMarkerOf(buf, at, marker) {
247
+ const tail = buf.length - at;
248
+ return tail < marker.length && marker.startsWith(buf.slice(at));
249
+ }
250
+ function safeBoundary(buf) {
251
+ const len = buf.length;
252
+ let i = 0;
253
+ let safe = 0;
254
+ while (i < len) {
255
+ const lt = buf.indexOf("<", i);
256
+ if (lt === -1) {
257
+ const amp = buf.lastIndexOf("&");
258
+ const splitEntity = amp >= i && buf.indexOf(";", amp) === -1 && len - amp <= MAX_ENTITY_TAIL;
259
+ return splitEntity ? amp : len;
260
+ }
261
+ if (isPartialMarkerOf(buf, lt, COMMENT_OPEN) || isPartialMarkerOf(buf, lt, CDATA_OPEN)) {
262
+ return lt;
263
+ }
264
+ let end;
265
+ if (buf.startsWith(COMMENT_OPEN, lt)) {
266
+ end = buf.indexOf("-->", lt + COMMENT_OPEN.length);
267
+ if (end === -1) return lt;
268
+ i = end + 3;
269
+ } else if (buf.startsWith(CDATA_OPEN, lt)) {
270
+ end = buf.indexOf("]]>", lt + CDATA_OPEN.length);
271
+ if (end === -1) return lt;
272
+ i = end + 3;
273
+ } else if (buf[lt + 1] === "?") {
274
+ end = buf.indexOf("?>", lt + 2);
275
+ if (end === -1) return lt;
276
+ i = end + 2;
277
+ } else {
278
+ let k = lt + 1;
279
+ let quote = "";
280
+ let close = -1;
281
+ while (k < len) {
282
+ const ch = buf[k];
283
+ if (quote !== "") {
284
+ if (ch === quote) quote = "";
285
+ } else if (ch === '"' || ch === "'") {
286
+ quote = ch;
287
+ } else if (ch === ">") {
288
+ close = k;
289
+ break;
290
+ }
291
+ k++;
292
+ }
293
+ if (close === -1) return lt;
294
+ i = close + 1;
295
+ }
296
+ safe = i;
297
+ }
298
+ return safe;
299
+ }
300
+ function createXmlStream() {
301
+ let buffer = "";
302
+ return {
303
+ push(text) {
304
+ buffer += text;
305
+ const cut = safeBoundary(buffer);
306
+ if (cut === 0) return [];
307
+ const tokens = [...tokenize(buffer.slice(0, cut))];
308
+ buffer = buffer.slice(cut);
309
+ return tokens;
310
+ },
311
+ flush() {
312
+ if (buffer === "") return [];
313
+ const tokens = [...tokenize(buffer)];
314
+ buffer = "";
315
+ return tokens;
316
+ }
317
+ };
318
+ }
319
+
320
+ // src/ooxml/rels.ts
321
+ function parseRels(xml) {
322
+ const rels = /* @__PURE__ */ new Map();
323
+ for (const token of tokenize(xml)) {
324
+ if (token.kind !== "open" || localName(token.name) !== "Relationship") continue;
325
+ const id = token.attrs.Id;
326
+ const target = token.attrs.Target;
327
+ if (id === void 0 || target === void 0) continue;
328
+ rels.set(id, {
329
+ id,
330
+ type: token.attrs.Type ?? "",
331
+ target,
332
+ targetMode: token.attrs.TargetMode === "External" ? "External" : "Internal"
333
+ });
334
+ }
335
+ return rels;
336
+ }
337
+ function resolveTarget(baseDir, target) {
338
+ const segments = target.startsWith("/") || baseDir === "" ? [] : baseDir.split("/");
339
+ for (const part of target.split("/")) {
340
+ if (part === "" || part === ".") continue;
341
+ if (part === "..") segments.pop();
342
+ else segments.push(part);
343
+ }
344
+ return segments.join("/");
345
+ }
346
+
347
+ // src/ooxml/shared-strings.ts
348
+ function parseSharedStrings(xml) {
349
+ const strings = [];
350
+ let inItem = false;
351
+ let current = "";
352
+ let textDepth = 0;
353
+ let phoneticDepth = 0;
354
+ for (const token of tokenize(xml)) {
355
+ if (token.kind === "open") {
356
+ const name = localName(token.name);
357
+ if (name === "si") {
358
+ if (inItem) {
359
+ strings.push(current);
360
+ inItem = false;
361
+ }
362
+ if (token.selfClosing) {
363
+ strings.push("");
364
+ } else {
365
+ inItem = true;
366
+ current = "";
367
+ textDepth = 0;
368
+ phoneticDepth = 0;
369
+ }
370
+ } else if (inItem && !token.selfClosing) {
371
+ if (name === "t") textDepth++;
372
+ else if (name === "rPh" || name === "phoneticPr") phoneticDepth++;
373
+ }
374
+ } else if (token.kind === "text") {
375
+ if (inItem && textDepth > 0 && phoneticDepth === 0) current += token.value;
376
+ } else {
377
+ const name = localName(token.name);
378
+ if (!inItem) continue;
379
+ if (name === "t") {
380
+ if (textDepth > 0) textDepth--;
381
+ } else if (name === "rPh" || name === "phoneticPr") {
382
+ if (phoneticDepth > 0) phoneticDepth--;
383
+ } else if (name === "si") {
384
+ strings.push(current);
385
+ inItem = false;
386
+ }
387
+ }
388
+ }
389
+ return strings;
390
+ }
391
+
392
+ // src/ooxml/styles.ts
393
+ var BUILTIN_FORMATS = {
394
+ 0: "General",
395
+ 1: "0",
396
+ 2: "0.00",
397
+ 3: "#,##0",
398
+ 4: "#,##0.00",
399
+ 5: '"$"#,##0_);("$"#,##0)',
400
+ 6: '"$"#,##0_);[Red]("$"#,##0)',
401
+ 7: '"$"#,##0.00_);("$"#,##0.00)',
402
+ 8: '"$"#,##0.00_);[Red]("$"#,##0.00)',
403
+ 9: "0%",
404
+ 10: "0.00%",
405
+ 11: "0.00E+00",
406
+ 12: "# ?/?",
407
+ 13: "# ??/??",
408
+ 14: "mm-dd-yy",
409
+ 15: "d-mmm-yy",
410
+ 16: "d-mmm",
411
+ 17: "mmm-yy",
412
+ 18: "h:mm AM/PM",
413
+ 19: "h:mm:ss AM/PM",
414
+ 20: "h:mm",
415
+ 21: "h:mm:ss",
416
+ 22: "m/d/yy h:mm",
417
+ 37: "#,##0_);(#,##0)",
418
+ 38: "#,##0_);[Red](#,##0)",
419
+ 39: "#,##0.00_);(#,##0.00)",
420
+ 40: "#,##0.00_);[Red](#,##0.00)",
421
+ 45: "mm:ss",
422
+ 46: "[h]:mm:ss",
423
+ 47: "mmss.0",
424
+ 48: "##0.0E+0",
425
+ 49: "@"
426
+ };
427
+ function isBuiltinDateId(id) {
428
+ return id >= 14 && id <= 22 || id >= 27 && id <= 36 || id >= 45 && id <= 47 || id >= 50 && id <= 58;
429
+ }
430
+ var ELAPSED_TIME = /\[(?:h+|m+|s+)\]/i;
431
+ var NON_TOKEN = /\[[^\]]*\]|"[^"]*"|\\./g;
432
+ var DATE_TOKEN = /[dmyhs]/i;
433
+ function isDateFormatCode(formatCode) {
434
+ if (ELAPSED_TIME.test(formatCode)) return true;
435
+ return DATE_TOKEN.test(formatCode.replace(NON_TOKEN, ""));
436
+ }
437
+ function parseStyles(xml) {
438
+ const customFormats = /* @__PURE__ */ new Map();
439
+ const cellFormatIds = [];
440
+ let inNumFmts = false;
441
+ let inCellXfs = false;
442
+ for (const token of tokenize(xml)) {
443
+ if (token.kind === "text") continue;
444
+ const name = localName(token.name);
445
+ if (token.kind === "open") {
446
+ if (name === "numFmts") {
447
+ if (!token.selfClosing) inNumFmts = true;
448
+ } else if (name === "cellXfs") {
449
+ if (!token.selfClosing) inCellXfs = true;
450
+ } else if (name === "numFmt" && inNumFmts) {
451
+ const id = Number(token.attrs.numFmtId);
452
+ const code = token.attrs.formatCode;
453
+ if (Number.isInteger(id) && code !== void 0) customFormats.set(id, code);
454
+ } else if (name === "xf" && inCellXfs) {
455
+ const id = Number(token.attrs.numFmtId ?? "0");
456
+ cellFormatIds.push(Number.isInteger(id) ? id : 0);
457
+ }
458
+ } else if (token.kind === "close") {
459
+ if (name === "numFmts") inNumFmts = false;
460
+ else if (name === "cellXfs") inCellXfs = false;
461
+ }
462
+ }
463
+ function isDateStyle(styleIndex) {
464
+ const numFmtId = cellFormatIds[styleIndex ?? 0];
465
+ if (numFmtId === void 0) return false;
466
+ const custom = customFormats.get(numFmtId);
467
+ return custom !== void 0 ? isDateFormatCode(custom) : isBuiltinDateId(numFmtId);
468
+ }
469
+ function formatCode(styleIndex) {
470
+ const numFmtId = cellFormatIds[styleIndex ?? 0];
471
+ if (numFmtId === void 0) return void 0;
472
+ return customFormats.get(numFmtId) ?? BUILTIN_FORMATS[numFmtId];
473
+ }
474
+ return { isDateStyle, formatCode };
475
+ }
476
+
477
+ // src/ooxml/workbook.ts
478
+ function parseWorkbook(xml) {
479
+ const sheets = [];
480
+ let date1904 = false;
481
+ for (const token of tokenize(xml)) {
482
+ if (token.kind !== "open") continue;
483
+ const tag = localName(token.name);
484
+ if (tag === "workbookPr") {
485
+ const flag = token.attrs.date1904;
486
+ if (flag === "1" || flag === "true") date1904 = true;
487
+ } else if (tag === "sheet") {
488
+ const name = token.attrs.name;
489
+ const rid = relationshipId(token.attrs);
490
+ if (name === void 0 || rid === void 0) continue;
491
+ const state = token.attrs.state;
492
+ sheets.push({ name, rid, visible: state !== "hidden" && state !== "veryHidden" });
493
+ }
494
+ }
495
+ return { sheets, date1904 };
496
+ }
497
+
498
+ // src/zip/inflate.ts
499
+ async function inflateRaw(data, maxBytes = Number.POSITIVE_INFINITY) {
500
+ const blob = new Blob([data]);
501
+ const reader = blob.stream().pipeThrough(new DecompressionStream("deflate-raw")).getReader();
502
+ const chunks = [];
503
+ let total = 0;
504
+ for (; ; ) {
505
+ const { done, value } = await reader.read();
506
+ if (done) break;
507
+ total += value.byteLength;
508
+ if (total > maxBytes) {
509
+ await reader.cancel();
510
+ throw new Error(`inflated output exceeds the expected ${maxBytes} bytes`);
511
+ }
512
+ chunks.push(value);
513
+ }
514
+ const out = new Uint8Array(total);
515
+ let offset = 0;
516
+ for (const chunk of chunks) {
517
+ out.set(chunk, offset);
518
+ offset += chunk.byteLength;
519
+ }
520
+ return out;
521
+ }
522
+ async function* inflateRawStream(data, maxBytes = Number.POSITIVE_INFINITY) {
523
+ const blob = new Blob([data]);
524
+ const reader = blob.stream().pipeThrough(new DecompressionStream("deflate-raw")).getReader();
525
+ let total = 0;
526
+ try {
527
+ for (; ; ) {
528
+ const { done, value } = await reader.read();
529
+ if (done) break;
530
+ total += value.byteLength;
531
+ if (total > maxBytes) {
532
+ throw new Error(`inflated output exceeds the expected ${maxBytes} bytes`);
533
+ }
534
+ yield value;
535
+ }
536
+ } finally {
537
+ await reader.cancel().catch(() => {
538
+ });
539
+ }
540
+ }
541
+
542
+ // src/zip/central-directory.ts
543
+ var SIG_EOCD = 101010256;
544
+ var SIG_CENTRAL = 33639248;
545
+ var SIG_LOCAL = 67324752;
546
+ var EOCD_MIN_SIZE = 22;
547
+ var MAX_COMMENT = 65535;
548
+ var ZIP64_SENTINEL = 4294967295;
549
+ function findEocd(view, len) {
550
+ const earliest = Math.max(0, len - EOCD_MIN_SIZE - MAX_COMMENT);
551
+ for (let pos = len - EOCD_MIN_SIZE; pos >= earliest; pos--) {
552
+ if (view.getUint32(pos, true) !== SIG_EOCD) continue;
553
+ const commentLen = view.getUint16(pos + 20, true);
554
+ if (pos + EOCD_MIN_SIZE + commentLen === len) return pos;
555
+ }
556
+ return -1;
557
+ }
558
+ function openZip(bytes, options) {
559
+ const len = bytes.byteLength;
560
+ const view = new DataView(bytes.buffer, bytes.byteOffset, len);
561
+ const maxPartBytes = options?.maxPartBytes;
562
+ const eocd = findEocd(view, len);
563
+ if (eocd === -1) {
564
+ throw new XlsxError(
565
+ "not-a-zip",
566
+ "not a zip archive: end-of-central-directory record not found"
567
+ );
568
+ }
569
+ const entryCount = view.getUint16(eocd + 10, true);
570
+ const cdOffset = view.getUint32(eocd + 16, true);
571
+ if (cdOffset === ZIP64_SENTINEL)
572
+ throw new XlsxError("unsupported", "ZIP64 archives are not supported");
573
+ if (cdOffset > len) {
574
+ throw new XlsxError("corrupt-zip", "corrupt zip: central directory offset past end of file");
575
+ }
576
+ const decoder2 = new TextDecoder();
577
+ const entries = /* @__PURE__ */ new Map();
578
+ let pos = cdOffset;
579
+ for (let i = 0; i < entryCount; i++) {
580
+ if (pos + 46 > len || view.getUint32(pos, true) !== SIG_CENTRAL) {
581
+ throw new XlsxError(
582
+ "corrupt-zip",
583
+ `corrupt zip: bad central directory header at offset ${pos}`
584
+ );
585
+ }
586
+ const method = view.getUint16(pos + 10, true);
587
+ const compressedSize = view.getUint32(pos + 20, true);
588
+ const uncompressedSize = view.getUint32(pos + 24, true);
589
+ const nameLen = view.getUint16(pos + 28, true);
590
+ const extraLen = view.getUint16(pos + 30, true);
591
+ const commentLen = view.getUint16(pos + 32, true);
592
+ const localHeaderOffset = view.getUint32(pos + 42, true);
593
+ if (compressedSize === ZIP64_SENTINEL || uncompressedSize === ZIP64_SENTINEL || localHeaderOffset === ZIP64_SENTINEL) {
594
+ throw new XlsxError("unsupported", "ZIP64 archives are not supported");
595
+ }
596
+ const name = decoder2.decode(bytes.subarray(pos + 46, pos + 46 + nameLen));
597
+ pos += 46 + nameLen + extraLen + commentLen;
598
+ if (name.endsWith("/")) continue;
599
+ if (entries.has(name)) {
600
+ throw new XlsxError("corrupt-zip", `corrupt zip: duplicate entry name ${name}`);
601
+ }
602
+ entries.set(name, { name, method, compressedSize, uncompressedSize, localHeaderOffset });
603
+ }
604
+ function locate(name) {
605
+ const entry = entries.get(name);
606
+ if (entry === void 0) throw new XlsxError("missing-part", `zip entry not found: ${name}`);
607
+ if (maxPartBytes !== void 0 && entry.uncompressedSize > maxPartBytes) {
608
+ throw new XlsxError(
609
+ "part-too-large",
610
+ `zip part ${name} declares ${entry.uncompressedSize} bytes, over the ${maxPartBytes}-byte limit`
611
+ );
612
+ }
613
+ const header = entry.localHeaderOffset;
614
+ if (header + 30 > len || view.getUint32(header, true) !== SIG_LOCAL) {
615
+ throw new XlsxError("corrupt-zip", `corrupt zip: bad local header for ${name}`);
616
+ }
617
+ const nameLen = view.getUint16(header + 26, true);
618
+ const extraLen = view.getUint16(header + 28, true);
619
+ const dataStart = header + 30 + nameLen + extraLen;
620
+ if (dataStart + entry.compressedSize > len) {
621
+ throw new XlsxError(
622
+ "corrupt-zip",
623
+ `corrupt zip: entry data for ${name} runs past end of file`
624
+ );
625
+ }
626
+ return { entry, payload: bytes.subarray(dataStart, dataStart + entry.compressedSize) };
627
+ }
628
+ async function read(name) {
629
+ const { entry, payload } = locate(name);
630
+ if (entry.method === 0) return payload;
631
+ if (entry.method === 8) {
632
+ if (entry.compressedSize === 0) return new Uint8Array(0);
633
+ try {
634
+ return await inflateRaw(payload, entry.uncompressedSize);
635
+ } catch (cause) {
636
+ throw new XlsxError("corrupt-zip", `corrupt zip: failed to inflate ${name}`, {
637
+ cause
638
+ });
639
+ }
640
+ }
641
+ throw new XlsxError(
642
+ "unsupported",
643
+ `unsupported zip compression method ${entry.method} for ${name}`
644
+ );
645
+ }
646
+ async function* readStream(name) {
647
+ const { entry, payload } = locate(name);
648
+ if (entry.method === 0) {
649
+ if (payload.byteLength > 0) yield payload;
650
+ return;
651
+ }
652
+ if (entry.method === 8) {
653
+ if (entry.compressedSize === 0) return;
654
+ try {
655
+ yield* inflateRawStream(payload, entry.uncompressedSize);
656
+ } catch (cause) {
657
+ throw new XlsxError("corrupt-zip", `corrupt zip: failed to inflate ${name}`, {
658
+ cause
659
+ });
660
+ }
661
+ return;
662
+ }
663
+ throw new XlsxError(
664
+ "unsupported",
665
+ `unsupported zip compression method ${entry.method} for ${name}`
666
+ );
667
+ }
668
+ return {
669
+ entries,
670
+ has: (name) => entries.has(name),
671
+ read,
672
+ readStream
673
+ };
674
+ }
675
+
676
+ // src/reader/worksheet.ts
677
+ function safeColumn(ref) {
678
+ try {
679
+ return parseRef(ref).col;
680
+ } catch {
681
+ return void 0;
682
+ }
683
+ }
684
+ function columnStyleFromToken(attrs) {
685
+ if (attrs.style === void 0) return void 0;
686
+ const style = Number(attrs.style);
687
+ const min = Number(attrs.min);
688
+ const max = Number(attrs.max);
689
+ if (!Number.isInteger(style) || !Number.isInteger(min) || !Number.isInteger(max))
690
+ return void 0;
691
+ return { min, max, style };
692
+ }
693
+ function rowDefaultStyleFromToken(attrs) {
694
+ if (attrs.customFormat !== "1" && attrs.customFormat !== "true") return void 0;
695
+ if (attrs.s === void 0) return void 0;
696
+ const s = Number(attrs.s);
697
+ return Number.isInteger(s) ? s : void 0;
698
+ }
699
+ function effectiveStyle(ownS, rowDefault, col, columns) {
700
+ if (ownS !== void 0) {
701
+ const s = Number(ownS);
702
+ return Number.isInteger(s) ? s : void 0;
703
+ }
704
+ if (rowDefault !== void 0) return rowDefault;
705
+ if (col !== void 0) {
706
+ const range = columns.find((c) => col >= c.min && col <= c.max);
707
+ if (range !== void 0) return range.style;
708
+ }
709
+ return void 0;
710
+ }
711
+ function createRowAssembler(ctx) {
712
+ let inSheetData = false;
713
+ let inRow = false;
714
+ let lastRow = 0;
715
+ let rowIndex = 0;
716
+ let cells = [];
717
+ let lastCol = 0;
718
+ const columns = [];
719
+ let rowDefault;
720
+ let inCell = false;
721
+ let cellRef = "";
722
+ let cellType;
723
+ let cellStyle;
724
+ let cellIsInline = false;
725
+ let cellValue = "";
726
+ let hasValue = false;
727
+ let inValue = false;
728
+ let inInline = false;
729
+ let textDepth = 0;
730
+ let phoneticDepth = 0;
731
+ const flushCell = () => {
732
+ if (!inCell) return;
733
+ cells.push(
734
+ decodeCell(
735
+ {
736
+ ref: cellRef,
737
+ type: cellType,
738
+ value: hasValue ? cellValue : void 0,
739
+ style: cellStyle
740
+ },
741
+ ctx
742
+ )
743
+ );
744
+ inCell = false;
745
+ };
746
+ function push(token) {
747
+ const out = [];
748
+ if (token.kind === "open") {
749
+ const name2 = localName(token.name);
750
+ if (name2 === "sheetData") {
751
+ if (!token.selfClosing) inSheetData = true;
752
+ return out;
753
+ }
754
+ if (name2 === "col") {
755
+ const cs = columnStyleFromToken(token.attrs);
756
+ if (cs !== void 0) columns.push(cs);
757
+ return out;
758
+ }
759
+ if (!inSheetData) return out;
760
+ if (name2 === "row") {
761
+ flushCell();
762
+ if (inRow) out.push({ index: rowIndex, cells });
763
+ const r = token.attrs.r;
764
+ const parsed = r !== void 0 ? Number.parseInt(r, 10) : Number.NaN;
765
+ rowIndex = Number.isInteger(parsed) && parsed > 0 ? parsed : lastRow + 1;
766
+ lastRow = rowIndex;
767
+ rowDefault = rowDefaultStyleFromToken(token.attrs);
768
+ cells = [];
769
+ lastCol = 0;
770
+ if (token.selfClosing) {
771
+ out.push({ index: rowIndex, cells });
772
+ inRow = false;
773
+ } else {
774
+ inRow = true;
775
+ }
776
+ return out;
777
+ }
778
+ if (!inRow) return out;
779
+ if (name2 === "c") {
780
+ flushCell();
781
+ const r = token.attrs.r;
782
+ let col;
783
+ if (r !== void 0) {
784
+ cellRef = r;
785
+ col = safeColumn(r);
786
+ if (col !== void 0) lastCol = col;
787
+ } else {
788
+ lastCol += 1;
789
+ col = lastCol;
790
+ cellRef = formatRef({ col: lastCol, row: rowIndex });
791
+ }
792
+ cellType = token.attrs.t;
793
+ cellStyle = effectiveStyle(token.attrs.s, rowDefault, col, columns);
794
+ cellIsInline = cellType === "inlineStr";
795
+ cellValue = "";
796
+ hasValue = false;
797
+ inValue = false;
798
+ inInline = false;
799
+ textDepth = 0;
800
+ phoneticDepth = 0;
801
+ if (token.selfClosing) {
802
+ cells.push(
803
+ decodeCell(
804
+ { ref: cellRef, type: cellType, value: void 0, style: cellStyle },
805
+ ctx
806
+ )
807
+ );
808
+ } else {
809
+ inCell = true;
810
+ }
811
+ return out;
812
+ }
813
+ if (!inCell) return out;
814
+ if (cellIsInline) {
815
+ if (name2 === "is") {
816
+ hasValue = true;
817
+ if (!token.selfClosing) inInline = true;
818
+ } else if (name2 === "t") {
819
+ if (inInline && !token.selfClosing) textDepth++;
820
+ } else if (name2 === "rPh" || name2 === "phoneticPr") {
821
+ if (inInline && !token.selfClosing) phoneticDepth++;
822
+ }
823
+ } else if (name2 === "v") {
824
+ hasValue = true;
825
+ if (!token.selfClosing) inValue = true;
826
+ }
827
+ return out;
828
+ }
829
+ if (token.kind === "text") {
830
+ const collect = cellIsInline ? inInline && textDepth > 0 && phoneticDepth === 0 : inValue;
831
+ if (inCell && collect) {
832
+ cellValue += token.value;
833
+ hasValue = true;
834
+ }
835
+ return out;
836
+ }
837
+ const name = localName(token.name);
838
+ if (name === "sheetData") {
839
+ flushCell();
840
+ if (inRow) {
841
+ out.push({ index: rowIndex, cells });
842
+ inRow = false;
843
+ }
844
+ inSheetData = false;
845
+ return out;
846
+ }
847
+ if (name === "row") {
848
+ if (inRow) {
849
+ flushCell();
850
+ out.push({ index: rowIndex, cells });
851
+ inRow = false;
852
+ }
853
+ return out;
854
+ }
855
+ if (!inCell) return out;
856
+ if (name === "c") flushCell();
857
+ else if (name === "v") inValue = false;
858
+ else if (name === "is") inInline = false;
859
+ else if (name === "t") {
860
+ if (textDepth > 0) textDepth--;
861
+ } else if (name === "rPh" || name === "phoneticPr") {
862
+ if (phoneticDepth > 0) phoneticDepth--;
863
+ }
864
+ return out;
865
+ }
866
+ function flush() {
867
+ flushCell();
868
+ if (inRow) {
869
+ inRow = false;
870
+ return [{ index: rowIndex, cells }];
871
+ }
872
+ return [];
873
+ }
874
+ return { push, flush };
875
+ }
876
+ function parseComments(xml) {
877
+ const authors = [];
878
+ const comments = [];
879
+ let inAuthors = false;
880
+ let authorText;
881
+ let current;
882
+ let inText = false;
883
+ let tDepth = 0;
884
+ let text = "";
885
+ for (const token of tokenize(xml)) {
886
+ if (token.kind === "open") {
887
+ const name = localName(token.name);
888
+ if (name === "authors") {
889
+ if (!token.selfClosing) inAuthors = true;
890
+ } else if (name === "author" && inAuthors) {
891
+ if (token.selfClosing) authors.push("");
892
+ else authorText = "";
893
+ } else if (name === "comment") {
894
+ const ref = token.attrs.ref;
895
+ const rawId = token.attrs.authorId;
896
+ const authorId = rawId !== void 0 && rawId !== "" && Number.isInteger(Number(rawId)) ? Number(rawId) : -1;
897
+ current = ref !== void 0 && ref !== "" ? { ref, authorId } : void 0;
898
+ text = "";
899
+ inText = false;
900
+ tDepth = 0;
901
+ } else if (name === "text" && current !== void 0) {
902
+ if (!token.selfClosing) inText = true;
903
+ } else if (name === "t" && inText) {
904
+ if (!token.selfClosing) tDepth++;
905
+ }
906
+ } else if (token.kind === "text") {
907
+ if (authorText !== void 0) authorText += token.value;
908
+ else if (inText && tDepth > 0) text += token.value;
909
+ } else {
910
+ const name = localName(token.name);
911
+ if (name === "authors") inAuthors = false;
912
+ else if (name === "author" && authorText !== void 0) {
913
+ authors.push(authorText);
914
+ authorText = void 0;
915
+ } else if (name === "t") {
916
+ if (tDepth > 0) tDepth--;
917
+ } else if (name === "text") inText = false;
918
+ else if (name === "comment" && current !== void 0) {
919
+ const author = authors[current.authorId];
920
+ comments.push({
921
+ ref: current.ref,
922
+ ...author !== void 0 && author !== "" ? { author } : {},
923
+ text
924
+ });
925
+ current = void 0;
926
+ }
927
+ }
928
+ }
929
+ return comments;
930
+ }
931
+ function parseDimension(xml) {
932
+ for (const token of tokenize(xml)) {
933
+ if (token.kind === "open" && localName(token.name) === "dimension") {
934
+ const ref = token.attrs.ref;
935
+ if (ref !== void 0 && ref !== "") return ref;
936
+ }
937
+ }
938
+ return void 0;
939
+ }
940
+ function parseCellStyles(xml) {
941
+ const styles = /* @__PURE__ */ new Map();
942
+ const columns = [];
943
+ let rowDefault;
944
+ let lastRow = 0;
945
+ let rowIndex = 0;
946
+ let lastCol = 0;
947
+ for (const token of tokenize(xml)) {
948
+ if (token.kind !== "open") continue;
949
+ const name = localName(token.name);
950
+ if (name === "col") {
951
+ const cs = columnStyleFromToken(token.attrs);
952
+ if (cs !== void 0) columns.push(cs);
953
+ continue;
954
+ }
955
+ if (name === "row") {
956
+ const r2 = token.attrs.r;
957
+ const parsed = r2 !== void 0 ? Number.parseInt(r2, 10) : Number.NaN;
958
+ rowIndex = Number.isInteger(parsed) && parsed > 0 ? parsed : lastRow + 1;
959
+ lastRow = rowIndex;
960
+ rowDefault = rowDefaultStyleFromToken(token.attrs);
961
+ lastCol = 0;
962
+ continue;
963
+ }
964
+ if (name !== "c") continue;
965
+ const r = token.attrs.r;
966
+ let ref;
967
+ let col;
968
+ if (r !== void 0) {
969
+ ref = r;
970
+ col = safeColumn(r);
971
+ if (col !== void 0) lastCol = col;
972
+ } else {
973
+ lastCol += 1;
974
+ col = lastCol;
975
+ ref = formatRef({ col: lastCol, row: rowIndex });
976
+ }
977
+ const index = effectiveStyle(token.attrs.s, rowDefault, col, columns);
978
+ if (index !== void 0) styles.set(ref, index);
979
+ }
980
+ return styles;
981
+ }
982
+ function* readRows(xml, ctx) {
983
+ const assembler = createRowAssembler(ctx);
984
+ for (const token of tokenize(xml)) yield* assembler.push(token);
985
+ yield* assembler.flush();
986
+ }
987
+ function parseMergedCells(xml) {
988
+ const ranges = [];
989
+ for (const token of tokenize(xml)) {
990
+ if (token.kind === "open" && localName(token.name) === "mergeCell") {
991
+ const ref = token.attrs.ref;
992
+ if (ref !== void 0 && ref !== "") ranges.push(ref);
993
+ }
994
+ }
995
+ return ranges;
996
+ }
997
+ function parseHyperlinks(xml, rels) {
998
+ const links = [];
999
+ for (const token of tokenize(xml)) {
1000
+ if (token.kind !== "open" || localName(token.name) !== "hyperlink") continue;
1001
+ const ref = token.attrs.ref;
1002
+ if (ref === void 0 || ref === "") continue;
1003
+ const rid = relationshipId(token.attrs);
1004
+ const target = rid !== void 0 ? rels?.get(rid)?.target : void 0;
1005
+ const location = token.attrs.location;
1006
+ const tooltip = token.attrs.tooltip;
1007
+ const display = token.attrs.display;
1008
+ links.push({
1009
+ ref,
1010
+ ...target !== void 0 ? { target } : {},
1011
+ ...location !== void 0 && location !== "" ? { location } : {},
1012
+ ...tooltip !== void 0 ? { tooltip } : {},
1013
+ ...display !== void 0 ? { display } : {}
1014
+ });
1015
+ }
1016
+ return links;
1017
+ }
1018
+ async function* streamRows(chunks, ctx) {
1019
+ const assembler = createRowAssembler(ctx);
1020
+ const xml = createXmlStream();
1021
+ const decoder2 = new TextDecoder();
1022
+ for await (const bytes of chunks) {
1023
+ const text = decoder2.decode(bytes, { stream: true });
1024
+ if (text === "") continue;
1025
+ for (const token of xml.push(text)) yield* assembler.push(token);
1026
+ }
1027
+ const tail = decoder2.decode();
1028
+ if (tail !== "") for (const token of xml.push(tail)) yield* assembler.push(token);
1029
+ for (const token of xml.flush()) yield* assembler.push(token);
1030
+ yield* assembler.flush();
1031
+ }
1032
+
1033
+ // src/reader/workbook.ts
1034
+ var decoder = new TextDecoder();
1035
+ var REL_OFFICE_DOCUMENT = "/officeDocument";
1036
+ var REL_SHARED_STRINGS = "/sharedStrings";
1037
+ var REL_STYLES = "/styles";
1038
+ var REL_COMMENTS = "/comments";
1039
+ function directoryOf(path) {
1040
+ const slash = path.lastIndexOf("/");
1041
+ return slash === -1 ? "" : path.slice(0, slash);
1042
+ }
1043
+ function relsPathFor(path) {
1044
+ const slash = path.lastIndexOf("/");
1045
+ const dir = slash === -1 ? "" : path.slice(0, slash);
1046
+ const file = slash === -1 ? path : path.slice(slash + 1);
1047
+ return dir === "" ? `_rels/${file}.rels` : `${dir}/_rels/${file}.rels`;
1048
+ }
1049
+ async function readText(zip, path) {
1050
+ if (!zip.has(path))
1051
+ throw new XlsxError("missing-part", `xlsx is missing a required part: ${path}`);
1052
+ return decoder.decode(await zip.read(path));
1053
+ }
1054
+ var Worksheet = class {
1055
+ /** Sheet name as shown on Excel's tab. */
1056
+ name;
1057
+ #info;
1058
+ #xml;
1059
+ #context;
1060
+ #rels;
1061
+ #commentsXml;
1062
+ #cells;
1063
+ #merged;
1064
+ #hyperlinks;
1065
+ #cellStyles;
1066
+ #dimension;
1067
+ #dimensionRead = false;
1068
+ #comments;
1069
+ constructor(info, xml, context, rels, commentsXml) {
1070
+ this.name = info.name;
1071
+ this.#info = info;
1072
+ this.#xml = xml;
1073
+ this.#context = context;
1074
+ this.#rels = rels;
1075
+ this.#commentsXml = commentsXml;
1076
+ }
1077
+ /** Workbook-relative part path, e.g. `xl/worksheets/sheet1.xml`. */
1078
+ get path() {
1079
+ return this.#info.path;
1080
+ }
1081
+ /** false for hidden or very-hidden sheets. */
1082
+ get visible() {
1083
+ return this.#info.visible;
1084
+ }
1085
+ /**
1086
+ * Merged-cell ranges in A1 notation (e.g. `['A1:B1', 'A2:A4']`), in document order. Only the
1087
+ * top-left cell of a merge holds a value; the rest read as `empty`. Empty when none.
1088
+ */
1089
+ get mergedCells() {
1090
+ if (this.#merged === void 0) this.#merged = parseMergedCells(this.#xml);
1091
+ return this.#merged;
1092
+ }
1093
+ /**
1094
+ * Hyperlinks declared on this sheet, in document order. Each carries the covered `ref` and,
1095
+ * where present, a resolved external `target`, an in-workbook `location`, a `tooltip`, and a
1096
+ * `display` override. Empty when none.
1097
+ */
1098
+ get hyperlinks() {
1099
+ if (this.#hyperlinks === void 0) {
1100
+ this.#hyperlinks = parseHyperlinks(this.#xml, this.#rels);
1101
+ }
1102
+ return this.#hyperlinks;
1103
+ }
1104
+ /**
1105
+ * The number-format code applied to the cell at `ref` — a custom code like `"yyyy-mm-dd"`
1106
+ * or `"0.00%"`, or a built-in one. `undefined` when the workbook has no style table or the
1107
+ * id has no portable code. An unstyled or absent cell resolves to the default format (style
1108
+ * 0, usually `"General"`), mirroring how date detection defaults.
1109
+ */
1110
+ numberFormat(ref) {
1111
+ return this.#context.styles?.formatCode(this.#cellStyleMap().get(ref));
1112
+ }
1113
+ /**
1114
+ * The sheet's declared used range in A1 notation (e.g. `"A1:E10"`, or a single cell), from
1115
+ * the worksheet's `<dimension>`. `undefined` when the producer omits it — it is an optional
1116
+ * hint, not authoritative, so treat a present value as advisory.
1117
+ */
1118
+ get dimension() {
1119
+ if (!this.#dimensionRead) {
1120
+ this.#dimension = parseDimension(this.#xml);
1121
+ this.#dimensionRead = true;
1122
+ }
1123
+ return this.#dimension;
1124
+ }
1125
+ /**
1126
+ * The comments anchored to cells on this sheet, in document order — each with its `ref`,
1127
+ * resolved `author`, and plain `text`. Empty when the sheet has no comments part.
1128
+ */
1129
+ get comments() {
1130
+ if (this.#comments === void 0) {
1131
+ this.#comments = this.#commentsXml === void 0 ? [] : parseComments(this.#commentsXml);
1132
+ }
1133
+ return this.#comments;
1134
+ }
1135
+ #cellStyleMap() {
1136
+ if (this.#cellStyles === void 0) this.#cellStyles = parseCellStyles(this.#xml);
1137
+ return this.#cellStyles;
1138
+ }
1139
+ /** The cell at an A1 reference. Absent cells read as `empty` (Excel treats them blank). */
1140
+ cell(ref) {
1141
+ return this.#index().get(ref) ?? { ref, type: "empty", value: null };
1142
+ }
1143
+ /** Stream the populated rows in document order. Sparse: empty rows/cells are absent. */
1144
+ async *rows() {
1145
+ for (const row of readRows(this.#xml, this.#context)) {
1146
+ yield row;
1147
+ }
1148
+ }
1149
+ #index() {
1150
+ if (this.#cells === void 0) {
1151
+ const cells = /* @__PURE__ */ new Map();
1152
+ for (const row of readRows(this.#xml, this.#context)) {
1153
+ for (const cell of row.cells) cells.set(cell.ref, cell);
1154
+ }
1155
+ this.#cells = cells;
1156
+ }
1157
+ return this.#cells;
1158
+ }
1159
+ };
1160
+ var Workbook = class {
1161
+ /** Sheets in tab order. */
1162
+ sheets;
1163
+ #byName;
1164
+ constructor(sheets, byName) {
1165
+ this.sheets = sheets;
1166
+ this.#byName = byName;
1167
+ }
1168
+ /** The worksheet with this tab name. Throws if there is none. */
1169
+ sheet(name) {
1170
+ const worksheet = this.#byName.get(name);
1171
+ if (worksheet === void 0) {
1172
+ const available = this.sheets.map((s) => s.name).join(", ");
1173
+ throw new XlsxError(
1174
+ "no-such-sheet",
1175
+ `no sheet named ${JSON.stringify(name)}; available: ${available}`
1176
+ );
1177
+ }
1178
+ return worksheet;
1179
+ }
1180
+ };
1181
+ async function loadWorkbook(source, options) {
1182
+ const bytes = source instanceof Uint8Array ? source : new Uint8Array(source);
1183
+ const zip = openZip(bytes, options);
1184
+ const packageRels = parseRels(await readText(zip, "_rels/.rels"));
1185
+ const office = [...packageRels.values()].find((r) => r.type.endsWith(REL_OFFICE_DOCUMENT));
1186
+ if (office === void 0) {
1187
+ throw new XlsxError("not-xlsx", "not an xlsx: no officeDocument relationship");
1188
+ }
1189
+ const workbookPath = resolveTarget("", office.target);
1190
+ const workbookDir = directoryOf(workbookPath);
1191
+ const { sheets: workbookSheets, date1904 } = parseWorkbook(await readText(zip, workbookPath));
1192
+ const workbookRels = parseRels(await readText(zip, relsPathFor(workbookPath)));
1193
+ let sharedStrings = [];
1194
+ const sst = [...workbookRels.values()].find((r) => r.type.endsWith(REL_SHARED_STRINGS));
1195
+ if (sst !== void 0 && sst.targetMode !== "External") {
1196
+ const sstPath = resolveTarget(workbookDir, sst.target);
1197
+ if (zip.has(sstPath)) {
1198
+ sharedStrings = parseSharedStrings(decoder.decode(await zip.read(sstPath)));
1199
+ }
1200
+ }
1201
+ let styles;
1202
+ const stylesRel = [...workbookRels.values()].find((r) => r.type.endsWith(REL_STYLES));
1203
+ if (stylesRel !== void 0 && stylesRel.targetMode !== "External") {
1204
+ const stylesPath = resolveTarget(workbookDir, stylesRel.target);
1205
+ if (zip.has(stylesPath)) {
1206
+ styles = parseStyles(decoder.decode(await zip.read(stylesPath)));
1207
+ }
1208
+ }
1209
+ const context = styles !== void 0 ? { sharedStrings, date1904, styles } : { sharedStrings, date1904 };
1210
+ const sheets = [];
1211
+ for (const entry of workbookSheets) {
1212
+ const rel = workbookRels.get(entry.rid);
1213
+ if (rel === void 0 || rel.targetMode === "External") continue;
1214
+ const path = resolveTarget(workbookDir, rel.target);
1215
+ if (!zip.has(path)) continue;
1216
+ sheets.push({ info: { name: entry.name, path, visible: entry.visible }, path });
1217
+ }
1218
+ return { zip, context, sheets };
1219
+ }
1220
+ async function openXlsx(source, options) {
1221
+ const { zip, context, sheets } = await loadWorkbook(source, options);
1222
+ const infos = [];
1223
+ const byName = /* @__PURE__ */ new Map();
1224
+ for (const { info, path } of sheets) {
1225
+ const xml = decoder.decode(await zip.read(path));
1226
+ const relsPath = relsPathFor(path);
1227
+ const rels = zip.has(relsPath) ? parseRels(decoder.decode(await zip.read(relsPath))) : void 0;
1228
+ let commentsXml;
1229
+ const commentsRel = rels && [...rels.values()].find((r) => r.type.endsWith(REL_COMMENTS));
1230
+ if (commentsRel !== void 0 && commentsRel.targetMode !== "External") {
1231
+ const commentsPath = resolveTarget(directoryOf(path), commentsRel.target);
1232
+ if (zip.has(commentsPath)) commentsXml = decoder.decode(await zip.read(commentsPath));
1233
+ }
1234
+ infos.push(info);
1235
+ if (!byName.has(info.name)) {
1236
+ byName.set(info.name, new Worksheet(info, xml, context, rels, commentsXml));
1237
+ }
1238
+ }
1239
+ return new Workbook(infos, byName);
1240
+ }
1241
+ async function* streamSheetRows(source, sheetName, options) {
1242
+ const { zip, context, sheets } = await loadWorkbook(source, options);
1243
+ const first = sheets[0];
1244
+ if (first === void 0) throw new XlsxError("not-xlsx", "xlsx has no readable worksheets");
1245
+ let path = first.path;
1246
+ if (sheetName !== void 0) {
1247
+ const match = sheets.find((s) => s.info.name === sheetName);
1248
+ if (match === void 0) {
1249
+ const available = sheets.map((s) => s.info.name).join(", ");
1250
+ throw new XlsxError(
1251
+ "no-such-sheet",
1252
+ `no sheet named ${JSON.stringify(sheetName)}; available: ${available}`
1253
+ );
1254
+ }
1255
+ path = match.path;
1256
+ }
1257
+ yield* streamRows(zip.readStream(path), context);
1258
+ }
1259
+
1260
+ export { Workbook, Worksheet, XlsxError, columnToIndex, formatRef, indexToColumn, openXlsx, parseRef, serialToDate, streamSheetRows };
1261
+ //# sourceMappingURL=index.js.map
1262
+ //# sourceMappingURL=index.js.map