@office-kit/xlsx 0.16.0 → 0.17.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.
Files changed (62) hide show
  1. package/README.md +12 -1
  2. package/dist/cell/cell.d.ts +12 -19
  3. package/dist/{cell-Df3cKRIl.mjs → cell-B5OUUdfI.mjs} +13 -7
  4. package/dist/cell-B5OUUdfI.mjs.map +1 -0
  5. package/dist/{cell-style-CNsET6WU.mjs → cell-style-DjKKPWDL.mjs} +2 -2
  6. package/dist/{cell-style-CNsET6WU.mjs.map → cell-style-DjKKPWDL.mjs.map} +1 -1
  7. package/dist/cell.mjs +1 -1
  8. package/dist/{coordinate-Bt5Gni2I.mjs → coordinate-CxC88uBk.mjs} +31 -2
  9. package/dist/coordinate-CxC88uBk.mjs.map +1 -0
  10. package/dist/{defined-names-Ctu3F6ls.mjs → defined-names-Cmp8rbvI.mjs} +2 -2
  11. package/dist/{defined-names-Ctu3F6ls.mjs.map → defined-names-Cmp8rbvI.mjs.map} +1 -1
  12. package/dist/{differential-BPKa7wXi.mjs → differential-7KGzbliB.mjs} +2 -2
  13. package/dist/{differential-BPKa7wXi.mjs.map → differential-7KGzbliB.mjs.map} +1 -1
  14. package/dist/{drawing-ZJ3h4VHD.mjs → drawing-Cv6UtInY.mjs} +2 -2
  15. package/dist/{drawing-ZJ3h4VHD.mjs.map → drawing-Cv6UtInY.mjs.map} +1 -1
  16. package/dist/drawing.mjs +1 -1
  17. package/dist/{escape-rnWSPpWY.mjs → escape-rA5UpibT.mjs} +26 -3
  18. package/dist/escape-rA5UpibT.mjs.map +1 -0
  19. package/dist/io/load.d.ts +28 -0
  20. package/dist/io.mjs +2 -2
  21. package/dist/{load-B1sK-LjT.mjs → load-DIdQ_zum.mjs} +66 -39
  22. package/dist/{load-B1sK-LjT.mjs.map → load-DIdQ_zum.mjs.map} +1 -1
  23. package/dist/{manifest-pLmx7KYh.mjs → manifest-BHUYpjUG.mjs} +3 -3
  24. package/dist/{manifest-pLmx7KYh.mjs.map → manifest-BHUYpjUG.mjs.map} +1 -1
  25. package/dist/node.mjs +1 -1
  26. package/dist/packaging.mjs +1 -1
  27. package/dist/{reader-DHMxLBQV.mjs → reader-pC7bxYAs.mjs} +30 -7
  28. package/dist/reader-pC7bxYAs.mjs.map +1 -0
  29. package/dist/{save-DmdWwxmw.mjs → save-ax11Gsw0.mjs} +7 -7
  30. package/dist/{save-DmdWwxmw.mjs.map → save-ax11Gsw0.mjs.map} +1 -1
  31. package/dist/schema.mjs +1 -1
  32. package/dist/{serialize-BC2Wu3bR.mjs → serialize-Dmb6Vtt9.mjs} +23 -6
  33. package/dist/serialize-Dmb6Vtt9.mjs.map +1 -0
  34. package/dist/{serializer-C6nm0mRJ.mjs → serializer-dhv07Z6F.mjs} +2 -2
  35. package/dist/{serializer-C6nm0mRJ.mjs.map → serializer-dhv07Z6F.mjs.map} +1 -1
  36. package/dist/streaming.mjs +103 -68
  37. package/dist/streaming.mjs.map +1 -1
  38. package/dist/styles.mjs +3 -3
  39. package/dist/{stylesheet-writer-oXSuGnJy.mjs → stylesheet-writer-BBYjNSMA.mjs} +184 -113
  40. package/dist/stylesheet-writer-BBYjNSMA.mjs.map +1 -0
  41. package/dist/{table-BrP0RwUd.mjs → table-BFi2WmgG.mjs} +2 -2
  42. package/dist/{table-BrP0RwUd.mjs.map → table-BFi2WmgG.mjs.map} +1 -1
  43. package/dist/utils/cell-number.d.ts +10 -0
  44. package/dist/utils/coordinate.d.ts +11 -0
  45. package/dist/utils/escape.d.ts +17 -0
  46. package/dist/utils/xsd-boolean.d.ts +9 -0
  47. package/dist/utils.mjs +2 -2
  48. package/dist/{workbook-DOTFzCpz.mjs → workbook-CMel6UcI.mjs} +7 -7
  49. package/dist/{workbook-DOTFzCpz.mjs.map → workbook-CMel6UcI.mjs.map} +1 -1
  50. package/dist/workbook.mjs +2 -2
  51. package/dist/{worksheet-B2NOQ6dM.mjs → worksheet-Cxn2c-jz.mjs} +3 -3
  52. package/dist/{worksheet-B2NOQ6dM.mjs.map → worksheet-Cxn2c-jz.mjs.map} +1 -1
  53. package/dist/worksheet.mjs +2 -2
  54. package/dist/xml.mjs +2 -2
  55. package/dist/zip.mjs +1 -1
  56. package/package.json +1 -1
  57. package/dist/cell-Df3cKRIl.mjs.map +0 -1
  58. package/dist/coordinate-Bt5Gni2I.mjs.map +0 -1
  59. package/dist/escape-rnWSPpWY.mjs.map +0 -1
  60. package/dist/reader-DHMxLBQV.mjs.map +0 -1
  61. package/dist/serialize-BC2Wu3bR.mjs.map +0 -1
  62. package/dist/stylesheet-writer-oXSuGnJy.mjs.map +0 -1
@@ -1,42 +1,20 @@
1
1
  import { i as OpenXmlIoError, o as OpenXmlSchemaError } from "./exceptions-D-CFwxgm.mjs";
2
- import { s as coordinateToTuple } from "./coordinate-Bt5Gni2I.mjs";
2
+ import { _ as rowNumberFromAttr, c as derivedRowNumber, s as coordinateToTuple } from "./coordinate-CxC88uBk.mjs";
3
3
  import { t as ERROR_CODES } from "./inference-BMvv9cUY.mjs";
4
- import { i as unescapeCellString, n as escapeXmlAttr } from "./escape-rnWSPpWY.mjs";
5
- import { _ as ARC_WORKBOOK_RELS, a as findChildren, c as ARC_CONTENT_TYPES, f as ARC_ROOT_RELS, ft as SHEET_MAIN_NS, g as ARC_WORKBOOK, i as findChild, lt as REL_NS, m as ARC_STYLE, p as ARC_SHARED_STRINGS, pt as STYLES_TYPE, ut as SHARED_STRINGS_TYPE, wt as XLSX_TYPE, yt as WORKSHEET_TYPE } from "./tree-BX-sRRVp.mjs";
6
- import { t as stylesheetToBytes, v as serializeCell } from "./stylesheet-writer-oXSuGnJy.mjs";
4
+ import { a as unescapeCellString, n as escapeXmlAttr } from "./escape-rA5UpibT.mjs";
5
+ import { _ as ARC_WORKBOOK_RELS, c as ARC_CONTENT_TYPES, f as ARC_ROOT_RELS, ft as SHEET_MAIN_NS, g as ARC_WORKBOOK, lt as REL_NS, m as ARC_STYLE, p as ARC_SHARED_STRINGS, pt as STYLES_TYPE, ut as SHARED_STRINGS_TYPE, wt as XLSX_TYPE, yt as WORKSHEET_TYPE } from "./tree-BX-sRRVp.mjs";
6
+ import { t as stylesheetToBytes, v as serializeCell, x as parseCellNumber } from "./stylesheet-writer-BBYjNSMA.mjs";
7
7
  import { t as parseXml } from "./parser-By6RWZVW.mjs";
8
- import { Q as findById, a as makeManifest, n as addOverride, nt as relsFromBytes, o as manifestFromBytes, rt as relsToBytes, s as manifestToBytes, t as addDefault, tt as makeRelationships } from "./manifest-pLmx7KYh.mjs";
9
- import { ct as buildXfPatch, ht as makeStylesheet, it as addCellXf, lt as defaultCellXf } from "./cell-style-CNsET6WU.mjs";
10
- import { A as serializeRichString, D as makeSharedStrings, S as validateSheetTitle, k as parseSharedStringsXml } from "./workbook-DOTFzCpz.mjs";
11
- import { i as parseStylesheetXml, n as parseDate1904, r as resolveRelTarget } from "./load-B1sK-LjT.mjs";
12
- import { t as openZip } from "./reader-DHMxLBQV.mjs";
8
+ import { r as parseXsdBoolean } from "./serialize-Dmb6Vtt9.mjs";
9
+ import { $ as findByType, Q as findById, a as makeManifest, n as addOverride, nt as relsFromBytes, o as manifestFromBytes, rt as relsToBytes, s as manifestToBytes, t as addDefault, tt as makeRelationships } from "./manifest-BHUYpjUG.mjs";
10
+ import { ct as buildXfPatch, ht as makeStylesheet, it as addCellXf, lt as defaultCellXf } from "./cell-style-DjKKPWDL.mjs";
11
+ import { A as serializeRichString, D as makeSharedStrings, S as validateSheetTitle, k as parseSharedStringsXml } from "./workbook-CMel6UcI.mjs";
12
+ import { a as parseDate1904, c as resolveRelTarget, l as parseStylesheetXml, n as SHARED_STRINGS_PART, o as parseSheetEntries, r as STYLES_PART, s as readOptionalWorkbookPart, t as OFFICE_DOC_REL_TYPE } from "./load-DIdQ_zum.mjs";
13
+ import { t as openZip } from "./reader-pC7bxYAs.mjs";
13
14
  import { t as utf8ByteLength } from "./utf8-OAkCDG5g.mjs";
14
15
  import { t as createZipWriter } from "./writer-C38RBZCy.mjs";
15
16
  import { t as iterParse } from "./iterparse-DZE0zy2M.mjs";
16
17
  //#region src/streaming/read-only.ts
17
- const SHEET_TAG = `{${SHEET_MAIN_NS}}sheet`;
18
- const SHEETS_TAG = `{${SHEET_MAIN_NS}}sheets`;
19
- const parseSheetList = (root, workbookPath, archive) => {
20
- const sheetsEl = findChild(root, SHEETS_TAG);
21
- if (!sheetsEl) return [];
22
- const wbRelsPath = relsPathFor(workbookPath);
23
- const wbRels = archive.has(wbRelsPath) ? relsFromBytes(archive.read(wbRelsPath)) : { rels: [] };
24
- const out = [];
25
- for (const sheet of findChildren(sheetsEl, SHEET_TAG)) {
26
- const name = sheet.attrs["name"];
27
- const rId = sheet.attrs[`{${REL_NS}}id`];
28
- if (!name || !rId) continue;
29
- const rel = findById(wbRels, rId);
30
- if (!rel) continue;
31
- const partPath = resolveRelTarget(workbookPath, rel.target);
32
- out.push({
33
- name,
34
- rId,
35
- partPath
36
- });
37
- }
38
- return out;
39
- };
40
18
  const relsPathFor = (partPath) => {
41
19
  const i = partPath.lastIndexOf("/");
42
20
  if (i < 0) return `_rels/${partPath}.rels`;
@@ -46,16 +24,16 @@ const localName = (qname) => {
46
24
  const i = qname.lastIndexOf("}");
47
25
  return i < 0 ? qname : qname.slice(i + 1);
48
26
  };
49
- const decodeCellValue = (t, vText, inlineText, sst) => {
27
+ const decodeCellValue = (t, vText, inlineText, sst, sheet, col, row) => {
50
28
  switch (t) {
51
- case "n": return vText !== void 0 && vText !== "" ? Number.parseFloat(vText) : null;
29
+ case "n": return parseCellNumber(vText, sheet, col, row);
52
30
  case "s": {
53
31
  if (vText === void 0) return null;
54
32
  const idx = Number.parseInt(vText, 10);
55
33
  if (!Number.isInteger(idx) || idx < 0 || idx >= sst.length) return null;
56
34
  return sst[idx] ?? null;
57
35
  }
58
- case "b": return vText === "1";
36
+ case "b": return parseXsdBoolean(vText) ?? null;
59
37
  case "e":
60
38
  if (!vText || !ERROR_CODES.has(vText)) return null;
61
39
  return {
@@ -72,14 +50,28 @@ const decodeCellValue = (t, vText, inlineText, sst) => {
72
50
  * stream that yields them), yielding one `ReadOnlyCell[]` per row that matches
73
51
  * `opts`.
74
52
  */
75
- async function* iterSheetRows(sheetInput, sst, opts) {
53
+ async function* iterSheetRows(title, sheetInput, sst, opts) {
76
54
  const minRow = opts.minRow ?? 1;
77
55
  const maxRow = opts.maxRow ?? Number.POSITIVE_INFINITY;
78
56
  const minCol = opts.minCol ?? 1;
79
57
  const maxCol = opts.maxCol ?? Number.POSITIVE_INFINITY;
80
58
  let inSheetData = false;
81
59
  let currentRow = -1;
60
+ let nextRow = 1;
82
61
  let currentCells = [];
62
+ let nextCol = 1;
63
+ let pendingCells = [];
64
+ const settleRow = (row) => {
65
+ currentRow = row;
66
+ nextRow = Math.max(nextRow, row + 1);
67
+ if (row >= minRow && row <= maxRow) for (const cell of pendingCells) currentCells.push({
68
+ row,
69
+ col: cell.col,
70
+ value: decodeCellValue(cell.type, cell.text, cell.inline, sst, title, cell.col, row),
71
+ styleId: cell.styleId
72
+ });
73
+ pendingCells = [];
74
+ };
83
75
  let cellOpen = false;
84
76
  let cellRow = 0;
85
77
  let cellCol = 0;
@@ -101,28 +93,35 @@ async function* iterSheetRows(sheetInput, sst, opts) {
101
93
  }
102
94
  switch (local) {
103
95
  case "row": {
104
- e.attrs;
105
96
  const rRaw = e.attrs["r"];
106
- currentRow = rRaw ? Number.parseInt(rRaw, 10) : currentRow + 1;
97
+ if (rRaw === void 0) currentRow = 0;
98
+ else {
99
+ currentRow = rowNumberFromAttr(rRaw, "loadWorkbookStream");
100
+ nextRow = Math.max(nextRow, currentRow + 1);
101
+ }
107
102
  currentCells = [];
103
+ pendingCells = [];
104
+ nextCol = 1;
108
105
  break;
109
106
  }
110
107
  case "c": {
111
108
  if (currentRow < 0) break;
112
- if (currentRow < minRow || currentRow > maxRow) break;
109
+ const ref = e.attrs["r"];
110
+ if (currentRow === 0 && ref) settleRow(coordinateToTuple(ref).row);
111
+ if (currentRow !== 0 && (currentRow < minRow || currentRow > maxRow)) break;
113
112
  cellOpen = true;
114
113
  cellType = e.attrs["t"] ?? "n";
115
114
  const sRaw = e.attrs["s"];
116
115
  cellStyleId = sRaw ? Number.parseInt(sRaw, 10) || 0 : 0;
117
- const ref = e.attrs["r"];
118
116
  if (ref) {
119
117
  const tup = coordinateToTuple(ref);
120
118
  cellRow = tup.row;
121
119
  cellCol = tup.col;
122
120
  } else {
123
121
  cellRow = currentRow;
124
- cellCol = (currentCells[currentCells.length - 1]?.col ?? 0) + 1;
122
+ cellCol = nextCol;
125
123
  }
124
+ nextCol = cellCol + 1;
126
125
  vText = "";
127
126
  isText = "";
128
127
  break;
@@ -155,6 +154,7 @@ async function* iterSheetRows(sheetInput, sst, opts) {
155
154
  inSheetData = false;
156
155
  return;
157
156
  case "row":
157
+ if (currentRow === 0) settleRow(derivedRowNumber(nextRow, "loadWorkbookStream"));
158
158
  if (currentRow >= minRow && currentRow <= maxRow && currentCells.length > 0) yield currentCells;
159
159
  if (currentRow > maxRow) {
160
160
  inSheetData = false;
@@ -164,8 +164,15 @@ async function* iterSheetRows(sheetInput, sst, opts) {
164
164
  currentCells = [];
165
165
  break;
166
166
  case "c":
167
- if (cellOpen && cellCol >= minCol && cellCol <= maxCol && cellRow >= minRow && cellRow <= maxRow) {
168
- const value = decodeCellValue(cellType, vText, isText, sst);
167
+ if (cellOpen && cellRow === 0 && cellCol >= minCol && cellCol <= maxCol) pendingCells.push({
168
+ col: cellCol,
169
+ type: cellType,
170
+ text: vText,
171
+ inline: isText,
172
+ styleId: cellStyleId
173
+ });
174
+ else if (cellOpen && cellCol >= minCol && cellCol <= maxCol && cellRow >= minRow && cellRow <= maxRow) {
175
+ const value = decodeCellValue(cellType, vText, isText, sst, title, cellCol, cellRow);
169
176
  currentCells.push({
170
177
  row: cellRow,
171
178
  col: cellCol,
@@ -194,7 +201,9 @@ async function* iterSheetRows(sheetInput, sst, opts) {
194
201
  const isXmlSpace = (b) => b === 32 || b === 9 || b === 10 || b === 13;
195
202
  /**
196
203
  * Read the `r="N"` row number out of a `<row …>` attribute region, scanning
197
- * bytes directly. Returns -1 when the attribute is absent or malformed.
204
+ * bytes directly. Returns -1 when the attribute is absent, or holds anything
205
+ * `parseRowNumberAttr` rejects: the two accept the same shapes, so the index
206
+ * can never seek by a number the SAX walk reads differently.
198
207
  *
199
208
  * Decoding the region to a string and running a regex instead costs one
200
209
  * TextDecoder, one string and one match array per row, which on a million-row
@@ -204,9 +213,11 @@ const readRowAttr = (bytes, from, to) => {
204
213
  for (let p = from; p + 3 < to; p++) {
205
214
  if (!isXmlSpace(bytes[p])) continue;
206
215
  if (bytes[p + 1] !== 114 || bytes[p + 2] !== 61 || bytes[p + 3] !== 34) continue;
216
+ let q = p + 4;
217
+ while (q < to && isXmlSpace(bytes[q])) q++;
218
+ if (bytes[q] === 43) q++;
207
219
  let value = 0;
208
220
  let digits = 0;
209
- let q = p + 4;
210
221
  while (q < to) {
211
222
  const d = bytes[q];
212
223
  if (d === void 0 || d < 48 || d > 57) break;
@@ -214,7 +225,8 @@ const readRowAttr = (bytes, from, to) => {
214
225
  digits++;
215
226
  q++;
216
227
  }
217
- if (digits > 0 && q < to && bytes[q] === 34) return value;
228
+ while (q < to && isXmlSpace(bytes[q])) q++;
229
+ if (digits > 0 && value >= 1 && value <= 1048576 && q < to && bytes[q] === 34) return value;
218
230
  }
219
231
  return -1;
220
232
  };
@@ -277,9 +289,14 @@ const findSheetDataTagEnd = (bytes) => {
277
289
  * `sheetDataEnd` is the byte offset of `</sheetData>` so callers can clip the
278
290
  * region that gets handed to saxes. `sheetDataTagEnd` includes the original
279
291
  * namespace-bearing ancestor tags, reused by {@link replayFromRow}.
292
+ *
293
+ * `hasUnnumberedRow` reports a `<row>` this scan cannot number, which makes the
294
+ * whole sheet unseekable: a row without `@r` takes the row its first cell
295
+ * names, and reading cell refs is the SAX walk's job.
280
296
  */
281
297
  const buildRowOffsetIndex = (bytes) => {
282
298
  const out = [];
299
+ let hasUnnumberedRow = false;
283
300
  let sheetDataEnd = -1;
284
301
  let i = 0;
285
302
  while (i < bytes.length) {
@@ -309,13 +326,15 @@ const buildRowOffsetIndex = (bytes) => {
309
326
  row,
310
327
  offset: start
311
328
  });
329
+ else hasUnnumberedRow = true;
312
330
  i = j + 1;
313
331
  }
314
332
  if (sheetDataEnd < 0) sheetDataEnd = bytes.length;
315
333
  return {
316
334
  index: out,
317
335
  sheetDataEnd,
318
- sheetDataTagEnd: findSheetDataTagEnd(bytes)
336
+ sheetDataTagEnd: findSheetDataTagEnd(bytes),
337
+ hasUnnumberedRow
319
338
  };
320
339
  };
321
340
  /**
@@ -365,7 +384,9 @@ const replayFromRow = (bytes, sheetDataTagEnd, fromOffset, sheetDataEnd) => {
365
384
  * `iterRows({ minRow > 1 })` needs random access instead, so the first band
366
385
  * query inflates the part and indexes its row offsets, and holds both for the
367
386
  * life of the worksheet handle or until the workbook closes. Subsequent band
368
- * queries jump straight to the first matching row without inflating or scanning again.
387
+ * queries jump straight to the first matching row without inflating or scanning
388
+ * again. A sheet whose rows omit `@r` cannot be indexed by row number, so its
389
+ * band queries keep streaming and hold nothing.
369
390
  */
370
391
  const makeStreamingReadOnlyWorksheet = (title, archive, partPath, sst, indexes) => {
371
392
  const cacheKey = {};
@@ -373,9 +394,13 @@ const makeStreamingReadOnlyWorksheet = (title, archive, partPath, sst, indexes)
373
394
  let cached = indexes.byWorksheet.get(cacheKey);
374
395
  if (!cached) {
375
396
  const bytes = archive.read(partPath);
376
- cached = {
397
+ const { hasUnnumberedRow, index, sheetDataEnd, sheetDataTagEnd } = buildRowOffsetIndex(bytes);
398
+ cached = hasUnnumberedRow ? { seekable: false } : {
399
+ seekable: true,
377
400
  bytes,
378
- ...buildRowOffsetIndex(bytes)
401
+ index,
402
+ sheetDataEnd,
403
+ sheetDataTagEnd
379
404
  };
380
405
  indexes.byWorksheet.set(cacheKey, cached);
381
406
  }
@@ -383,14 +408,16 @@ const makeStreamingReadOnlyWorksheet = (title, archive, partPath, sst, indexes)
383
408
  };
384
409
  const iterRows = (opts = {}) => {
385
410
  const minRow = opts.minRow ?? 1;
386
- if (minRow <= 1) return iterSheetRows(archive.readStream(partPath), sst, opts);
387
- const { bytes, index, sheetDataEnd, sheetDataTagEnd } = ensureIndexed();
388
- if (index.length === 0 || sheetDataTagEnd < 0) return iterSheetRows(bytes, sst, opts);
411
+ if (minRow <= 1) return iterSheetRows(title, archive.readStream(partPath), sst, opts);
412
+ const seek = ensureIndexed();
413
+ if (!seek.seekable) return iterSheetRows(title, archive.readStream(partPath), sst, opts);
414
+ const { bytes, index, sheetDataEnd, sheetDataTagEnd } = seek;
415
+ if (index.length === 0 || sheetDataTagEnd < 0) return iterSheetRows(title, bytes, sst, opts);
389
416
  const pos = firstRowAtOrAfter(index, minRow);
390
417
  if (pos < 0) return (async function* () {})();
391
418
  const target = index[pos];
392
- if (!target) return iterSheetRows(bytes, sst, opts);
393
- return iterSheetRows(replayFromRow(bytes, sheetDataTagEnd, target.offset, sheetDataEnd), sst, opts);
419
+ if (!target) return iterSheetRows(title, bytes, sst, opts);
420
+ return iterSheetRows(title, replayFromRow(bytes, sheetDataTagEnd, target.offset, sheetDataEnd), sst, opts);
394
421
  };
395
422
  const iterValues = async function* (opts = {}) {
396
423
  for await (const row of iterRows(opts)) yield row.map((c) => c.value);
@@ -406,16 +433,16 @@ const makeStreamingReadOnlyWorksheet = (title, archive, partPath, sst, indexes)
406
433
  * sheet list / styles / shared strings. Per the project-wide "no classes" rule
407
434
  * (CLAUDE.md), the workbook is a plain object closing over the archive handle.
408
435
  */
409
- const makeStreamingReadOnlyWorkbook = (sheetNames, styles, date1904, archive, entries, sst) => {
436
+ const makeStreamingReadOnlyWorkbook = (sheetNames, styles, date1904, archive, partPathByName, sst) => {
410
437
  const indexes = { byWorksheet: /* @__PURE__ */ new WeakMap() };
411
438
  return {
412
439
  sheetNames,
413
440
  styles,
414
441
  date1904,
415
442
  openWorksheet(name) {
416
- const entry = entries.get(name);
417
- if (!entry) throw new OpenXmlSchemaError(`loadWorkbookStream: no worksheet named "${name}"`);
418
- return makeStreamingReadOnlyWorksheet(name, archive, entry.partPath, sst, indexes);
443
+ const partPath = partPathByName.get(name);
444
+ if (partPath === void 0) throw new OpenXmlSchemaError(`loadWorkbookStream: no worksheet named "${name}"`);
445
+ return makeStreamingReadOnlyWorksheet(name, archive, partPath, sst, indexes);
419
446
  },
420
447
  async close() {
421
448
  archive.close();
@@ -429,19 +456,27 @@ async function loadWorkbookStream(source, opts = {}) {
429
456
  if (!archive.has("[Content_Types].xml")) throw new OpenXmlSchemaError(`loadWorkbookStream: missing "${ARC_CONTENT_TYPES}"`);
430
457
  manifestFromBytes(archive.read(ARC_CONTENT_TYPES));
431
458
  if (!archive.has(ARC_ROOT_RELS)) throw new OpenXmlSchemaError(`loadWorkbookStream: missing "${ARC_ROOT_RELS}"`);
432
- const officeDocRel = relsFromBytes(archive.read(ARC_ROOT_RELS)).rels.find((r) => r.type === `${REL_NS}/officeDocument`);
459
+ const officeDocRel = findByType(relsFromBytes(archive.read(ARC_ROOT_RELS)), OFFICE_DOC_REL_TYPE);
433
460
  if (!officeDocRel) throw new OpenXmlSchemaError(`loadWorkbookStream: no officeDocument relationship in root rels`);
434
461
  const workbookPath = resolveRelTarget("", officeDocRel.target);
435
462
  if (!archive.has(workbookPath)) throw new OpenXmlSchemaError(`loadWorkbookStream: workbook part "${workbookPath}" missing`);
436
463
  const workbookRoot = parseXml(archive.read(workbookPath));
437
- const sheetEntries = parseSheetList(workbookRoot, workbookPath, archive);
438
- const entryMap = /* @__PURE__ */ new Map();
439
- for (const e of sheetEntries) entryMap.set(e.name, e);
440
- let sst = makeSharedStrings();
441
- if (archive.has(ARC_SHARED_STRINGS)) sst = parseSharedStringsXml(archive.read(ARC_SHARED_STRINGS));
442
- let styles = makeStylesheet();
443
- if (archive.has(ARC_STYLE)) styles = parseStylesheetXml(archive.read(ARC_STYLE));
444
- return makeStreamingReadOnlyWorkbook(sheetEntries.map((e) => e.name), styles, parseDate1904(workbookRoot), archive, entryMap, sst.entries.map((e) => typeof e === "string" ? e : e.runs.map((r) => r.text).join("")));
464
+ const declaredSheets = parseSheetEntries(workbookRoot);
465
+ const wbRelsPath = relsPathFor(workbookPath);
466
+ if (declaredSheets.length > 0 && !archive.has(wbRelsPath)) throw new OpenXmlSchemaError(`loadWorkbookStream: workbook has sheets but rels part "${wbRelsPath}" is missing`);
467
+ const wbRels = archive.has(wbRelsPath) ? relsFromBytes(archive.read(wbRelsPath)) : makeRelationships();
468
+ const partPathByName = /* @__PURE__ */ new Map();
469
+ for (const declared of declaredSheets) {
470
+ if (partPathByName.has(declared.name)) throw new OpenXmlSchemaError(`loadWorkbookStream: duplicate sheet name "${declared.name}"`);
471
+ const rel = findById(wbRels, declared.rId);
472
+ if (!rel) throw new OpenXmlSchemaError(`loadWorkbookStream: sheet "${declared.name}" rId "${declared.rId}" has no matching rels entry`);
473
+ partPathByName.set(declared.name, resolveRelTarget(workbookPath, rel.target));
474
+ }
475
+ const sstBytes = readOptionalWorkbookPart(archive, workbookPath, wbRels, SHARED_STRINGS_PART);
476
+ const sst = sstBytes === void 0 ? makeSharedStrings() : parseSharedStringsXml(sstBytes);
477
+ const stylesBytes = readOptionalWorkbookPart(archive, workbookPath, wbRels, STYLES_PART);
478
+ const styles = stylesBytes === void 0 ? makeStylesheet() : parseStylesheetXml(stylesBytes);
479
+ return makeStreamingReadOnlyWorkbook(declaredSheets.map((e) => e.name), styles, parseDate1904(workbookRoot), archive, partPathByName, sst.entries.map((e) => typeof e === "string" ? e : e.runs.map((r) => r.text).join("")));
445
480
  }
446
481
  //#endregion
447
482
  //#region src/streaming/string-table.ts