@office-kit/xlsx 0.9.2 → 0.10.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 (61) hide show
  1. package/dist/{cell-style-CN9JXOwm.mjs → cell-style-DuvS_Lsg.mjs} +2 -2
  2. package/dist/cell-style-DuvS_Lsg.mjs.map +1 -0
  3. package/dist/{differential-YEjxNScf.mjs → differential-DvKU8fJt.mjs} +2 -2
  4. package/dist/{differential-YEjxNScf.mjs.map → differential-DvKU8fJt.mjs.map} +1 -1
  5. package/dist/drawing/drawing-xml.d.ts +7 -0
  6. package/dist/drawing/drawing.d.ts +25 -0
  7. package/dist/drawing-BxzLuryn.mjs.map +1 -1
  8. package/dist/io.mjs +2 -2
  9. package/dist/{load-BA30X4-M.mjs → load-CnPfhGo3.mjs} +54 -10
  10. package/dist/load-CnPfhGo3.mjs.map +1 -0
  11. package/dist/{manifest-CVL1_t0O.mjs → manifest-Cm9K7D62.mjs} +5 -5
  12. package/dist/{manifest-CVL1_t0O.mjs.map → manifest-Cm9K7D62.mjs.map} +1 -1
  13. package/dist/node.mjs +1 -1
  14. package/dist/packaging.mjs +1 -1
  15. package/dist/{parser-nk0Lqmcb.mjs → parser-D3SLaOFe.mjs} +27 -4
  16. package/dist/parser-D3SLaOFe.mjs.map +1 -0
  17. package/dist/{save-C9ChJfWM.mjs → save-2moCT3Af.mjs} +68 -34
  18. package/dist/save-2moCT3Af.mjs.map +1 -0
  19. package/dist/schema.mjs +1 -1
  20. package/dist/{serialize-55EnT30e.mjs → serialize-BC2Wu3bR.mjs} +2 -2
  21. package/dist/{serialize-55EnT30e.mjs.map → serialize-BC2Wu3bR.mjs.map} +1 -1
  22. package/dist/{serializer-DQzTTNOS.mjs → serializer-BtO2TrI7.mjs} +21 -9
  23. package/dist/serializer-BtO2TrI7.mjs.map +1 -0
  24. package/dist/streaming.mjs +9 -12
  25. package/dist/streaming.mjs.map +1 -1
  26. package/dist/styles/stylesheet.d.ts +8 -0
  27. package/dist/styles.mjs +3 -3
  28. package/dist/{stylesheet-writer-DOGKBGEk.mjs → stylesheet-writer-BrOw4jMY.mjs} +87 -41
  29. package/dist/stylesheet-writer-BrOw4jMY.mjs.map +1 -0
  30. package/dist/{table-BifavDVW.mjs → table-1P8we2gS.mjs} +2 -2
  31. package/dist/{table-BifavDVW.mjs.map → table-1P8we2gS.mjs.map} +1 -1
  32. package/dist/{tree-Bbs1C8Rc.mjs → tree-BX-sRRVp.mjs} +3 -1
  33. package/dist/tree-BX-sRRVp.mjs.map +1 -0
  34. package/dist/{utf8-D91g1XTG.mjs → utf8-Tn3nzyik.mjs} +2 -2
  35. package/dist/{utf8-D91g1XTG.mjs.map → utf8-Tn3nzyik.mjs.map} +1 -1
  36. package/dist/workbook/shared-strings.d.ts +15 -5
  37. package/dist/workbook/views.d.ts +7 -0
  38. package/dist/workbook/workbook.d.ts +15 -0
  39. package/dist/{workbook-T1Y3lstQ.mjs → workbook-CARZS94U.mjs} +39 -14
  40. package/dist/workbook-CARZS94U.mjs.map +1 -0
  41. package/dist/workbook.mjs +1 -1
  42. package/dist/workbook.mjs.map +1 -1
  43. package/dist/worksheet/hyperlinks.d.ts +5 -0
  44. package/dist/worksheet/reader.d.ts +7 -2
  45. package/dist/worksheet/worksheet.d.ts +4 -0
  46. package/dist/{worksheet-C6CWgvP5.mjs → worksheet-C5Ensf_j.mjs} +167 -135
  47. package/dist/worksheet-C5Ensf_j.mjs.map +1 -0
  48. package/dist/worksheet.mjs +2 -2
  49. package/dist/xml/parser.d.ts +20 -0
  50. package/dist/xml/serializer.d.ts +8 -0
  51. package/dist/xml.mjs +4 -4
  52. package/package.json +1 -1
  53. package/dist/cell-style-CN9JXOwm.mjs.map +0 -1
  54. package/dist/load-BA30X4-M.mjs.map +0 -1
  55. package/dist/parser-nk0Lqmcb.mjs.map +0 -1
  56. package/dist/save-C9ChJfWM.mjs.map +0 -1
  57. package/dist/serializer-DQzTTNOS.mjs.map +0 -1
  58. package/dist/stylesheet-writer-DOGKBGEk.mjs.map +0 -1
  59. package/dist/tree-Bbs1C8Rc.mjs.map +0 -1
  60. package/dist/workbook-T1Y3lstQ.mjs.map +0 -1
  61. package/dist/worksheet-C6CWgvP5.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"streaming.mjs","names":[],"sources":["../src/streaming/read-only.ts","../src/streaming/write-only.ts"],"sourcesContent":["// Streaming read-only workbook.\n//\n// `loadWorkbookStream` opens the zip + parses workbook.xml / sharedStrings.xml\n// / styles.xml metadata up front (small even on million-row archives), then\n// exposes a lazy `openWorksheet(name)` that SAX-iterates the sheet body via\n// `iterParse`. The iterator streams rows without materialising the full sheet\n// in memory.\n\nimport { type SharedStringsTable, parseSharedStringsXml } from '../workbook/shared-strings';\nimport { ARC_CONTENT_TYPES, ARC_ROOT_RELS, ARC_SHARED_STRINGS, ARC_STYLE, REL_NS, SHEET_MAIN_NS } from '../xml/namespaces';\nimport { findById, relsFromBytes } from '../packaging/relationships';\nimport { manifestFromBytes } from '../packaging/manifest';\nimport { OpenXmlSchemaError } from '../utils/exceptions';\nimport type { DecompressionLimits } from '../zip/decompression-guard';\nimport { type ZipArchive, openZip } from '../zip/reader';\nimport type { CellValue, ExcelErrorCode } from '../cell/cell';\nimport { ERROR_CODES } from '../utils/inference';\nimport { iterParse, type SaxEvent, type SaxInput } from '../xml/iterparse';\nimport { parseXml } from '../xml/parser';\nimport { findChild, findChildren } from '../xml/tree';\nimport type { XlsxSource } from '../io/source';\nimport { coordinateToTuple } from '../utils/coordinate';\nimport { type Stylesheet, makeStylesheet } from '../styles/stylesheet';\nimport { parseStylesheetXml } from '../styles/stylesheet-reader';\nimport { resolveRelTarget } from '../io/load';\n\nconst SHEET_TAG = `{${SHEET_MAIN_NS}}sheet`;\nconst SHEETS_TAG = `{${SHEET_MAIN_NS}}sheets`;\n\nexport interface IterRowsOptions {\n minRow?: number;\n maxRow?: number;\n minCol?: number;\n maxCol?: number;\n}\n\nexport interface ReadOnlyCell {\n readonly row: number;\n readonly col: number;\n readonly value: CellValue;\n readonly styleId: number;\n}\n\nexport interface ReadOnlyWorksheet {\n title: string;\n iterRows(opts?: IterRowsOptions): AsyncIterableIterator<ReadOnlyCell[]>;\n iterValues(opts?: IterRowsOptions): AsyncIterableIterator<CellValue[]>;\n}\n\nexport interface ReadOnlyWorkbook {\n sheetNames: string[];\n styles: Stylesheet;\n openWorksheet(name: string): ReadOnlyWorksheet;\n close(): Promise<void>;\n}\n\ninterface SheetEntry {\n name: string;\n rId: string;\n partPath: string;\n}\n\nconst parseSheetList = (workbookXml: Uint8Array, workbookPath: string, archive: ZipArchive): SheetEntry[] => {\n const root = parseXml(workbookXml);\n const sheetsEl = findChild(root, SHEETS_TAG);\n if (!sheetsEl) return [];\n const wbRelsPath = relsPathFor(workbookPath);\n const wbRels = archive.has(wbRelsPath) ? relsFromBytes(archive.read(wbRelsPath)) : { rels: [] };\n const out: SheetEntry[] = [];\n for (const sheet of findChildren(sheetsEl, SHEET_TAG)) {\n const name = sheet.attrs['name'];\n const rId = sheet.attrs[`{${REL_NS}}id`];\n if (!name || !rId) continue;\n const rel = findById(wbRels, rId);\n if (!rel) continue;\n const partPath = resolveRelTarget(workbookPath, rel.target);\n out.push({ name, rId, partPath });\n }\n return out;\n};\n\nconst relsPathFor = (partPath: string): string => {\n const i = partPath.lastIndexOf('/');\n if (i < 0) return `_rels/${partPath}.rels`;\n return `${partPath.slice(0, i)}/_rels/${partPath.slice(i + 1)}.rels`;\n};\n\nconst localName = (qname: string): string => {\n const i = qname.lastIndexOf('}');\n return i < 0 ? qname : qname.slice(i + 1);\n};\n\nconst decodeCellValue = (\n t: string,\n vText: string | undefined,\n inlineText: string | undefined,\n sst: ReadonlyArray<string>,\n): CellValue => {\n switch (t) {\n case 'n':\n return vText !== undefined && vText !== '' ? Number.parseFloat(vText) : null;\n case 's': {\n if (vText === undefined) return null;\n const idx = Number.parseInt(vText, 10);\n if (!Number.isInteger(idx) || idx < 0 || idx >= sst.length) return null;\n return sst[idx] ?? null;\n }\n case 'b':\n return vText === '1';\n case 'e': {\n if (!vText || !ERROR_CODES.has(vText)) return null;\n return { kind: 'error', code: vText as ExcelErrorCode };\n }\n case 'str':\n return vText ?? '';\n case 'inlineStr':\n return inlineText ?? '';\n default:\n return vText !== undefined && vText !== '' ? Number.parseFloat(vText) : null;\n }\n};\n\n/**\n * SAX-iterate `<sheetData>/<row>/<c>` events out of the worksheet bytes (or a\n * stream that yields them), yielding one `ReadOnlyCell[]` per row that matches\n * `opts`.\n */\nasync function* iterSheetRows(\n sheetInput: SaxInput,\n sst: ReadonlyArray<string>,\n opts: IterRowsOptions,\n): AsyncIterableIterator<ReadOnlyCell[]> {\n const minRow = opts.minRow ?? 1;\n const maxRow = opts.maxRow ?? Number.POSITIVE_INFINITY;\n const minCol = opts.minCol ?? 1;\n const maxCol = opts.maxCol ?? Number.POSITIVE_INFINITY;\n\n let inSheetData = false;\n let currentRow = -1;\n let currentRowAttrs: Record<string, string> | null = null;\n let currentCells: ReadOnlyCell[] = [];\n\n // Per-cell state. Reset when each <c> starts.\n let cellOpen = false;\n let cellRow = 0;\n let cellCol = 0;\n let cellType = 'n';\n let cellStyleId = 0;\n let inV = false;\n let vText = '';\n let inIs = false;\n let inIsT = false;\n let isText = '';\n\n for await (const ev of iterParse(sheetInput)) {\n const e = ev as SaxEvent;\n if (e.kind === 'start') {\n const local = localName(e.name);\n if (!inSheetData) {\n if (local === 'sheetData') inSheetData = true;\n continue;\n }\n switch (local) {\n case 'row': {\n currentRowAttrs = e.attrs;\n const rRaw = e.attrs['r'];\n currentRow = rRaw ? Number.parseInt(rRaw, 10) : currentRow + 1;\n currentCells = [];\n break;\n }\n case 'c': {\n if (currentRow < 0) break;\n // Skip cell-attr parsing entirely when the row is outside the\n // requested band — saves the parseInt + coordinateToTuple hit on\n // every cell of every excluded row.\n if (currentRow < minRow || currentRow > maxRow) break;\n cellOpen = true;\n cellType = e.attrs['t'] ?? 'n';\n const sRaw = e.attrs['s'];\n cellStyleId = sRaw ? Number.parseInt(sRaw, 10) || 0 : 0;\n const ref = e.attrs['r'];\n if (ref) {\n const tup = coordinateToTuple(ref);\n cellRow = tup.row;\n cellCol = tup.col;\n } else {\n cellRow = currentRow;\n cellCol = (currentCells[currentCells.length - 1]?.col ?? 0) + 1;\n }\n vText = '';\n isText = '';\n break;\n }\n case 'v':\n if (cellOpen) inV = true;\n break;\n case 'is':\n if (cellOpen) inIs = true;\n break;\n case 't':\n if (inIs) inIsT = true;\n break;\n default:\n break;\n }\n continue;\n }\n if (e.kind === 'text') {\n if (inV) vText += e.text;\n else if (inIsT) isText += e.text;\n continue;\n }\n // end\n const local = localName(e.name);\n if (!inSheetData) continue;\n switch (local) {\n case 'sheetData':\n inSheetData = false;\n return;\n case 'row': {\n if (currentRow >= minRow && currentRow <= maxRow && currentCells.length > 0) {\n yield currentCells;\n }\n // Once we've crossed maxRow there are no more rows to yield — every\n // subsequent <row> would just be parsed and dropped. Stop iterating\n // early. ECMA-376 emits rows in ascending order.\n if (currentRow > maxRow) {\n inSheetData = false;\n return;\n }\n currentRow = -1;\n currentRowAttrs = null;\n currentCells = [];\n break;\n }\n case 'c': {\n if (cellOpen && cellCol >= minCol && cellCol <= maxCol && cellRow >= minRow && cellRow <= maxRow) {\n const value = decodeCellValue(cellType, vText, isText, sst);\n currentCells.push({ row: cellRow, col: cellCol, value, styleId: cellStyleId });\n }\n cellOpen = false;\n break;\n }\n case 'v':\n inV = false;\n break;\n case 'is':\n inIs = false;\n break;\n case 't':\n if (inIs) inIsT = false;\n break;\n default:\n break;\n }\n }\n // Avoid unused-var lint when row attrs never touched.\n void currentRowAttrs;\n}\n\n/**\n * Build a sorted `[rowNum, byteOffset]` index for every `<row r=\"N\">`\n * occurrence in a worksheet's bytes. Pure byte-level scan (no SAX), cheap\n * relative to the per-cell SAX walk: ~50 ns per row on M-series Node 22.\n *\n * `sheetDataEnd` is the byte offset of `</sheetData>` so callers can clip the\n * slice that gets handed to saxes.\n */\nconst buildRowOffsetIndex = (\n bytes: Uint8Array,\n): { index: ReadonlyArray<{ row: number; offset: number }>; sheetDataEnd: number } => {\n const out: Array<{ row: number; offset: number }> = [];\n let sheetDataEnd = -1;\n let i = 0;\n while (i < bytes.length) {\n if (bytes[i] !== 0x3c /* '<' */) {\n i++;\n continue;\n }\n // Detect `</sheetData>` once — used to clip the slice fed to saxes.\n if (\n sheetDataEnd < 0 &&\n bytes[i + 1] === 0x2f /* '/' */ &&\n bytes[i + 2] === 0x73 /* 's' */ &&\n bytes[i + 3] === 0x68 /* 'h' */ &&\n bytes[i + 4] === 0x65 /* 'e' */ &&\n bytes[i + 5] === 0x65 /* 'e' */ &&\n bytes[i + 6] === 0x74 /* 't' */ &&\n bytes[i + 7] === 0x44 /* 'D' */ &&\n bytes[i + 8] === 0x61 /* 'a' */ &&\n bytes[i + 9] === 0x74 /* 't' */ &&\n bytes[i + 10] === 0x61 /* 'a' */ &&\n bytes[i + 11] === 0x3e /* '>' */\n ) {\n sheetDataEnd = i;\n break;\n }\n // Match `<row` followed by ASCII whitespace or '>'.\n if (\n bytes[i + 1] !== 0x72 /* 'r' */ ||\n bytes[i + 2] !== 0x6f /* 'o' */ ||\n bytes[i + 3] !== 0x77 /* 'w' */\n ) {\n i++;\n continue;\n }\n const next = bytes[i + 4];\n if (\n next !== 0x20 /* sp */ &&\n next !== 0x09 /* tab */ &&\n next !== 0x0a /* lf */ &&\n next !== 0x0d /* cr */ &&\n next !== 0x3e /* > */ &&\n next !== 0x2f /* / */\n ) {\n i++;\n continue;\n }\n // Walk to the closing '>'; the attrs region carries `r=\"N\"`.\n const start = i;\n let j = i + 4;\n while (j < bytes.length && bytes[j] !== 0x3e) j++;\n if (j >= bytes.length) break;\n const attrsBuf = bytes.subarray(start + 4, j);\n const attrs = new TextDecoder('ascii', { fatal: false }).decode(attrsBuf);\n const m = /\\sr=\"(\\d+)\"/.exec(attrs);\n if (m?.[1]) {\n const row = Number.parseInt(m[1], 10);\n if (Number.isInteger(row)) out.push({ row, offset: start });\n }\n i = j + 1;\n }\n if (sheetDataEnd < 0) sheetDataEnd = bytes.length;\n return { index: out, sheetDataEnd };\n};\n\n/**\n * Binary-search the row index for the first entry with `row >= target`. Returns\n * -1 when every recorded row is below the target.\n */\nconst firstRowAtOrAfter = (\n index: ReadonlyArray<{ row: number; offset: number }>,\n target: number,\n): number => {\n let lo = 0;\n let hi = index.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n const entry = index[mid];\n if (!entry || entry.row < target) lo = mid + 1;\n else hi = mid;\n }\n return lo < index.length ? lo : -1;\n};\n\n/**\n * Slice a worksheet's bytes to start at the row at index `idxPos` of the\n * row-offset index, wrapping the result with a synthetic `<sheetData>` envelope\n * so saxes parses it in the right namespace.\n */\nconst SHEET_DATA_OPEN = `<?xml version=\"1.0\" encoding=\"UTF-8\"?><sheetData xmlns=\"${SHEET_MAIN_NS}\">`;\nconst SHEET_DATA_CLOSE = `</sheetData>`;\nconst sliceFromRow = (\n bytes: Uint8Array,\n fromOffset: number,\n sheetDataEnd: number,\n): Uint8Array => {\n const prefix = new TextEncoder().encode(SHEET_DATA_OPEN);\n const suffix = new TextEncoder().encode(SHEET_DATA_CLOSE);\n const middle = bytes.subarray(fromOffset, sheetDataEnd);\n const out = new Uint8Array(prefix.length + middle.length + suffix.length);\n out.set(prefix, 0);\n out.set(middle, prefix.length);\n out.set(suffix, prefix.length + middle.length);\n return out;\n};\n\n/**\n * Factory: build a {@link ReadOnlyWorksheet} bound to a single worksheet part\n * inside an opened archive. SAX iteration runs lazily — `iterRows` re-reads the\n * part bytes each time so the caller can iterate the same sheet repeatedly\n * without keeping a buffered decoder around.\n *\n * For `iterRows({ minRow > 1 })`, a row-offset index is built lazily on first\n * use and cached; subsequent band queries jump straight to the byte offset of\n * the first matching row instead of SAX-walking the entire `<sheetData>`.\n */\nconst makeStreamingReadOnlyWorksheet = (\n title: string,\n archive: ZipArchive,\n partPath: string,\n sst: ReadonlyArray<string>,\n): ReadOnlyWorksheet => {\n // Lazy + cached. The index is small (~16 B per row); for 1M rows that's 16 MB\n // of working set, vs. the alternative of walking the sheet bytes through\n // saxes on every band query.\n let cached: ReturnType<typeof buildRowOffsetIndex> | undefined;\n const ensureIndex = (bytes: Uint8Array) => {\n if (!cached) cached = buildRowOffsetIndex(bytes);\n return cached;\n };\n\n const iterRows = (opts: IterRowsOptions = {}): AsyncIterableIterator<ReadOnlyCell[]> => {\n const minRow = opts.minRow ?? 1;\n if (minRow <= 1) {\n // Whole-sheet (or no-min) iter — feed the SAX parser directly off the\n // archive's streaming inflate path so the worksheet's inflated payload\n // is never fully resident. Peak memory for the walk drops to the\n // inflate window + SAX state instead of the entire `<sheetData>` body.\n return iterSheetRows(archive.readStream(partPath), sst, opts);\n }\n // Band query (minRow > 1): the row-offset index needs the full inflated\n // bytes so we can binary-search to the byte offset of the first matching\n // row. Materialise once and reuse via `ensureIndex`.\n const bytes = archive.read(partPath);\n const { index, sheetDataEnd } = ensureIndex(bytes);\n if (index.length === 0) return iterSheetRows(bytes, sst, opts);\n const pos = firstRowAtOrAfter(index, minRow);\n if (pos < 0) {\n // Every row is below minRow — nothing to yield.\n return (async function* () {})();\n }\n const target = index[pos];\n if (!target) return iterSheetRows(bytes, sst, opts);\n const sliced = sliceFromRow(bytes, target.offset, sheetDataEnd);\n return iterSheetRows(sliced, sst, opts);\n };\n const iterValues = async function* (opts: IterRowsOptions = {}): AsyncIterableIterator<CellValue[]> {\n for await (const row of iterRows(opts)) {\n yield row.map((c) => c.value);\n }\n };\n return { title, iterRows, iterValues };\n};\n\n/**\n * Factory: build a {@link ReadOnlyWorkbook} from an opened archive + pre-parsed\n * sheet list / styles / shared strings. Per the project-wide \"no classes\" rule\n * (CLAUDE.md), the workbook is a plain object closing over the archive handle.\n */\nconst makeStreamingReadOnlyWorkbook = (\n sheetNames: string[],\n styles: Stylesheet,\n archive: ZipArchive,\n entries: ReadonlyMap<string, SheetEntry>,\n sst: ReadonlyArray<string>,\n): ReadOnlyWorkbook => ({\n sheetNames,\n styles,\n openWorksheet(name) {\n const entry = entries.get(name);\n if (!entry) {\n throw new OpenXmlSchemaError(`loadWorkbookStream: no worksheet named \"${name}\"`);\n }\n return makeStreamingReadOnlyWorksheet(name, archive, entry.partPath, sst);\n },\n async close() {\n archive.close();\n },\n});\n\n/** Options for {@link loadWorkbookStream}. */\nexport interface LoadWorkbookStreamOptions {\n /**\n * Decompression-bomb safeguards applied while inflating zip entries.\n * Defaults to limits that fit any legitimate xlsx; pass `false` to disable\n * (only safe for fully trusted sources).\n */\n decompressionLimits?: DecompressionLimits | false;\n}\n\n/** Open an xlsx for read-only streaming access. */\nexport async function loadWorkbookStream(\n source: XlsxSource,\n opts: LoadWorkbookStreamOptions = {},\n): Promise<ReadOnlyWorkbook> {\n const archive = await openZip(\n source,\n opts.decompressionLimits === undefined ? {} : { decompressionLimits: opts.decompressionLimits },\n );\n if (!archive.has(ARC_CONTENT_TYPES)) {\n throw new OpenXmlSchemaError(`loadWorkbookStream: missing \"${ARC_CONTENT_TYPES}\"`);\n }\n // Manifest parse is intentionally cheap and discarded — we resolve sheets by\n // walking workbook.xml.rels directly.\n manifestFromBytes(archive.read(ARC_CONTENT_TYPES));\n\n if (!archive.has(ARC_ROOT_RELS)) {\n throw new OpenXmlSchemaError(`loadWorkbookStream: missing \"${ARC_ROOT_RELS}\"`);\n }\n const rootRels = relsFromBytes(archive.read(ARC_ROOT_RELS));\n const officeDocRel = rootRels.rels.find((r) => r.type === `${REL_NS}/officeDocument`);\n if (!officeDocRel) {\n throw new OpenXmlSchemaError(`loadWorkbookStream: no officeDocument relationship in root rels`);\n }\n const workbookPath = resolveRelTarget('', officeDocRel.target);\n if (!archive.has(workbookPath)) {\n throw new OpenXmlSchemaError(`loadWorkbookStream: workbook part \"${workbookPath}\" missing`);\n }\n const sheetEntries = parseSheetList(archive.read(workbookPath), workbookPath, archive);\n const entryMap = new Map<string, SheetEntry>();\n for (const e of sheetEntries) entryMap.set(e.name, e);\n\n let sst: SharedStringsTable = { entries: [], index: new Map() };\n if (archive.has(ARC_SHARED_STRINGS)) {\n sst = parseSharedStringsXml(archive.read(ARC_SHARED_STRINGS));\n }\n let styles: Stylesheet = makeStylesheet();\n if (archive.has(ARC_STYLE)) {\n styles = parseStylesheetXml(archive.read(ARC_STYLE));\n }\n\n return makeStreamingReadOnlyWorkbook(\n sheetEntries.map((e) => e.name),\n styles,\n archive,\n entryMap,\n sst.entries.map((e) => (typeof e === 'string' ? e : e.runs.map((r) => r.text).join(''))),\n );\n}\n","// Streaming write-only workbook.\n//\n// `createWriteOnlyWorkbook` lets callers append rows one at a time without\n// holding a full Workbook in memory. Each `appendRow` serialises the row\n// directly into a string buffer (no `Cell` objects, no Worksheet rows Map) —\n// when `close()` runs we glue the rows under a `<sheetData>` envelope and hand\n// the bytes to the streaming-deflate ZIP writer. The buffer keeps ~30 bytes per\n// cell of XML text instead of the ~200-byte V8 footprint a Cell + Map entry\n// costs, so the heap budget at 3M cells drops roughly an order of magnitude vs.\n// the previous setCell-based path.\n\nimport type { Cell, CellValue } from '../cell/cell';\nimport type { XlsxSink } from '../io/sink';\nimport { addDefault, addOverride, makeManifest, manifestToBytes } from '../packaging/manifest';\nimport { makeRelationships, relsToBytes } from '../packaging/relationships';\nimport {\n addBorder,\n addCellXf,\n addFill,\n addFont,\n addNumFmt,\n type CellXf,\n defaultCellXf,\n makeStylesheet,\n type Stylesheet,\n} from '../styles/stylesheet';\nimport { stylesheetToBytes } from '../styles/stylesheet-writer';\nimport type { Alignment } from '../styles/alignment';\nimport type { Border } from '../styles/borders';\nimport type { Fill } from '../styles/fills';\nimport type { Font } from '../styles/fonts';\nimport type { Protection } from '../styles/protection';\nimport { escapeXmlAttr } from '../utils/escape';\nimport { OpenXmlIoError } from '../utils/exceptions';\nimport { utf8ByteLength } from '../utils/utf8';\nimport { makeSharedStrings, sharedStringsToBytes } from '../workbook/shared-strings';\nimport { validateSheetTitle } from '../workbook/workbook';\nimport { serializeCell } from '../worksheet/writer';\nimport {\n ARC_CONTENT_TYPES,\n ARC_ROOT_RELS,\n ARC_SHARED_STRINGS,\n ARC_STYLE,\n ARC_WORKBOOK,\n ARC_WORKBOOK_RELS,\n PKG_REL_NS,\n REL_NS,\n SHARED_STRINGS_TYPE,\n SHEET_MAIN_NS,\n STYLES_TYPE,\n WORKSHEET_TYPE,\n XLSX_TYPE,\n} from '../xml/namespaces';\nimport { createZipWriter } from '../zip/writer';\n\nconst escapeAttr = escapeXmlAttr;\n\nexport interface WriteOnlyOptions {\n /** Reserved — currently ignored (the buffered backend doesn't honour it). */\n estimatedMaxRow?: number;\n}\n\nexport interface WriteOnlyStyle {\n font?: Font;\n fill?: Fill;\n border?: Border;\n alignment?: Alignment;\n numberFormat?: string;\n protection?: Protection;\n}\n\nexport type WriteOnlyRowItem = CellValue | { value: CellValue; style?: WriteOnlyStyle };\n\nexport interface WriteOnlyWorksheet {\n readonly title: string;\n appendRow(row: WriteOnlyRowItem[]): Promise<void>;\n setColumnWidth(col: number, width: number): void;\n close(): Promise<void>;\n}\n\nexport interface WriteOnlyWorkbook {\n /** Add a new worksheet. The previous worksheet must be `close()`d first. */\n addWorksheet(title: string): Promise<WriteOnlyWorksheet>;\n /** Finalise the archive: emits styles / sharedStrings / workbook / manifest / rels. */\n finalize(): Promise<void>;\n /**\n * Release the sink without finalising the archive. Call this from a\n * surrounding catch block when the producer pipeline throws before\n * `finalize()` — without it, streaming destinations (`toFile` /\n * `toWritable`) keep the file descriptor / Writable open and the partial\n * xlsx looks valid on disk.\n *\n * Idempotent. Subsequent `addWorksheet` / `finalize` calls throw.\n */\n abort(cause?: unknown): void;\n}\n\nconst validateTitle = (title: string, taken: Set<string>): void => {\n const reason = validateSheetTitle(title);\n if (reason) {\n throw new OpenXmlIoError(`Worksheet title \"${title}\": ${reason}`);\n }\n if (taken.has(title.toLowerCase())) {\n throw new OpenXmlIoError(`Worksheet title \"${title}\" is already in use`);\n }\n};\n\n/** Allocate a CellXf id for a style spec. Mirrors cell-style.ts but works directly on the pool. */\nconst allocateXfId = (ss: Stylesheet, style: WriteOnlyStyle): number => {\n // `xfId` is intentionally omitted: leaving it undefined skips the\n // cellStyleXfs bounds check and matches what Excel emits when there is no\n // parent style.\n let xf: CellXf = { fontId: 0, fillId: 0, borderId: 0, numFmtId: 0 };\n if (style.font !== undefined) {\n xf = { ...xf, fontId: addFont(ss, style.font), applyFont: true };\n }\n if (style.fill !== undefined) {\n xf = { ...xf, fillId: addFill(ss, style.fill), applyFill: true };\n }\n if (style.border !== undefined) {\n xf = { ...xf, borderId: addBorder(ss, style.border), applyBorder: true };\n }\n if (style.numberFormat !== undefined) {\n xf = { ...xf, numFmtId: addNumFmt(ss, style.numberFormat), applyNumberFormat: true };\n }\n if (style.alignment !== undefined) {\n xf = { ...xf, alignment: style.alignment, applyAlignment: true };\n }\n if (style.protection !== undefined) {\n xf = { ...xf, protection: style.protection, applyProtection: true };\n }\n return addCellXf(ss, xf);\n};\n\ninterface WorkbookState {\n styles: Stylesheet;\n sst: ReturnType<typeof makeSharedStrings>;\n /** Sheet emit metadata, in addWorksheet order. */\n sheets: Array<{\n title: string;\n sheetId: number;\n }>;\n /** True once finalize() has been called (further mutations throw). */\n finalised: boolean;\n /** True while a worksheet is open (the next addWorksheet must wait). */\n hasOpenWorksheet: boolean;\n /** ZIP writer the workbook + each open worksheet stream chunks through. */\n writer: import('../zip/writer').ZipWriter;\n}\n\n/**\n * Flush threshold for the worksheet's pending-row text buffer. Smaller values\n * minimise heap; larger values amortise the TextEncoder + push overhead. 64 KB\n * is a sweet spot — heap stays low and per-row JS work is dominated by the\n * actual XML construction, not flushing.\n */\nconst FLUSH_THRESHOLD_BYTES = 64 * 1024;\n\n/**\n * Factory: build a {@link WriteOnlyWorksheet} that closes over the shared\n * {@link WorkbookState}. Per the project-wide \"no classes\" rule (CLAUDE.md) the\n * worksheet is a plain object holding the row buffer + column-width map in\n * closure state.\n *\n * The worksheet streams its `<sheetData>` body chunk-by-chunk through the ZIP\n * writer's `addStreamingEntry` API, so the heap footprint stays at one ~64 KB\n * pending text buffer plus deflate scratch — no Cell objects, no all-rows\n * accumulation. The XML envelope (decl / worksheet open / cols / sheetData\n * open) flushes on the first `appendRow` (or `close()` if the sheet is empty);\n * column widths staged via `setColumnWidth` *must* land before the first row.\n */\nconst makeWriteOnlyWorksheet = (state: WorkbookState, title: string, sheetId: number): WriteOnlyWorksheet => {\n let nextRow = 1;\n let closed = false;\n let headerFlushed = false;\n const columnWidths = new Map<number, number>();\n const dummyCtx = { sharedStrings: state.sst, rels: makeRelationships() };\n const encoder = new TextEncoder();\n const stream = state.writer.addStreamingEntry(`xl/worksheets/sheet${sheetId}.xml`);\n let pendingText = '';\n let pendingBytes = 0;\n\n const writeText = (text: string): void => {\n pendingText += text;\n // UTF-8 byte length, computed without a full encode: scan once and add the\n // exact codepoint cost. `text.length` would undercount CJK by ~3× and let\n // the buffer balloon past FLUSH_THRESHOLD_BYTES on Japanese / Chinese\n // workloads.\n pendingBytes += utf8ByteLength(text);\n if (pendingBytes >= FLUSH_THRESHOLD_BYTES) {\n stream.write(encoder.encode(pendingText));\n pendingText = '';\n pendingBytes = 0;\n }\n };\n\n const flushHeader = (): void => {\n if (headerFlushed) return;\n headerFlushed = true;\n let header = '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>';\n header += `<worksheet xmlns=\"${SHEET_MAIN_NS}\" xmlns:r=\"${REL_NS}\">`;\n if (columnWidths.size > 0) {\n header += '<cols>';\n const sorted = [...columnWidths.entries()].sort((a, b) => a[0] - b[0]);\n for (const [col, width] of sorted) {\n header += `<col min=\"${col}\" max=\"${col}\" width=\"${width}\" customWidth=\"1\"/>`;\n }\n header += '</cols>';\n }\n header += '<sheetData>';\n writeText(header);\n };\n\n const appendRow = async (row: WriteOnlyRowItem[]): Promise<void> => {\n if (closed) throw new OpenXmlIoError('appendRow: worksheet already closed');\n flushHeader();\n const r = nextRow++;\n let xml = `<row r=\"${r}\">`;\n for (let i = 0; i < row.length; i++) {\n const item = row[i];\n if (item === undefined || item === null) continue;\n const col = i + 1;\n let value: CellValue;\n let style: WriteOnlyStyle | undefined;\n if (item !== null && typeof item === 'object' && 'value' in (item as object)) {\n const wrapped = item as { value: CellValue; style?: WriteOnlyStyle };\n value = wrapped.value;\n style = wrapped.style;\n } else {\n value = item as CellValue;\n }\n const styleId = style ? allocateXfId(state.styles, style) : 0;\n // Ephemeral cell-shaped object — discarded as soon as serializeCell\n // returns its `<c .../>` string. Keeps the heap footprint at the size of\n // the pending text buffer instead of a full Worksheet model.\n const cell: Cell = { row: r, col, value, styleId };\n xml += serializeCell(cell, dummyCtx);\n }\n xml += '</row>';\n writeText(xml);\n };\n\n const setColumnWidth = (col: number, width: number): void => {\n if (closed) throw new OpenXmlIoError('setColumnWidth: worksheet already closed');\n if (headerFlushed) {\n throw new OpenXmlIoError(\n 'setColumnWidth: must be called before the first appendRow — column widths are emitted as part of the worksheet header',\n );\n }\n columnWidths.set(col, width);\n };\n\n const close = async (): Promise<void> => {\n if (closed) return;\n closed = true;\n flushHeader();\n writeText('</sheetData></worksheet>');\n if (pendingText.length > 0) {\n stream.write(encoder.encode(pendingText));\n pendingText = '';\n pendingBytes = 0;\n }\n await stream.end();\n state.sheets.push({ title, sheetId });\n state.hasOpenWorksheet = false;\n };\n\n return { title, appendRow, setColumnWidth, close };\n};\n\n/**\n * Factory: build a {@link WriteOnlyWorkbook} from a sink. State lives in a\n * closure rather than on a class instance per the project-wide \"no classes\"\n * rule (CLAUDE.md).\n */\nconst makeWriteOnlyWorkbook = (sink: XlsxSink): WriteOnlyWorkbook => {\n const styles = makeStylesheet();\n // Reserve cellXfs[0] for the default (no apply* flags). Unstyled cells point\n // at this slot via styleId=0; user-styled cells start at index 1 so the\n // writer emits an `s=\"N\"` attribute for them.\n addCellXf(styles, defaultCellXf());\n // The ZIP writer is created up front: each addWorksheet opens a streaming\n // entry on it and flushes row chunks through the deflate stream as they\n // arrive. Sheets emit before styles / sst / workbook / rels / content-types\n // so the writer can serialise them in order.\n const state: WorkbookState = {\n styles,\n sst: makeSharedStrings(),\n sheets: [],\n finalised: false,\n hasOpenWorksheet: false,\n writer: createZipWriter(sink),\n };\n\n const addWorksheet = async (title: string): Promise<WriteOnlyWorksheet> => {\n if (state.finalised) {\n throw new OpenXmlIoError('addWorksheet: workbook already finalised');\n }\n if (state.hasOpenWorksheet) {\n throw new OpenXmlIoError(\n 'addWorksheet: previous worksheet still open — call close() before opening the next one',\n );\n }\n const taken = new Set(state.sheets.map((s) => s.title.toLowerCase()));\n validateTitle(title, taken);\n state.hasOpenWorksheet = true;\n const sheetId = state.sheets.length + 1;\n return makeWriteOnlyWorksheet(state, title, sheetId);\n };\n\n const finalize = async (): Promise<void> => {\n if (state.finalised) {\n throw new OpenXmlIoError('finalize: already finalised');\n }\n if (state.hasOpenWorksheet) {\n throw new OpenXmlIoError('finalize: a worksheet is still open — call close() before finalising');\n }\n state.finalised = true;\n const writer = state.writer;\n try {\n await finalizeImpl(state, writer);\n } catch (err) {\n // Release the sink so the half-emitted archive doesn't linger as a\n // valid-looking file on disk. abort() is idempotent.\n writer.abort(err);\n throw err;\n }\n };\n\n const abort = (cause?: unknown): void => {\n if (state.finalised) return;\n state.finalised = true;\n state.writer.abort(cause);\n };\n\n return { addWorksheet, finalize, abort };\n};\n\nasync function finalizeImpl(state: WorkbookState, writer: WorkbookState['writer']): Promise<void> {\n // 1. Worksheets — already streamed through writer.addStreamingEntry\n // during each WriteOnlyWorksheet's appendRow / close cycle.\n\n // 2. Stylesheet.\n await writer.addEntry(ARC_STYLE, stylesheetToBytes(state.styles));\n\n // 3. SharedStrings (only when non-empty).\n if (state.sst.entries.length > 0) {\n await writer.addEntry(ARC_SHARED_STRINGS, sharedStringsToBytes(state.sst));\n }\n\n // 4. workbook.xml.\n const workbookXml = serializeWorkbookXml(state.sheets);\n await writer.addEntry(ARC_WORKBOOK, new TextEncoder().encode(workbookXml));\n\n // 5. workbook.xml.rels.\n const wbRels = makeRelationships();\n state.sheets.forEach((s, i) => {\n wbRels.rels.push({\n id: `rId${i + 1}`,\n type: `${REL_NS}/worksheet`,\n target: `worksheets/sheet${s.sheetId}.xml`,\n });\n });\n if (state.sst.entries.length > 0) {\n wbRels.rels.push({\n id: `rId${wbRels.rels.length + 1}`,\n type: `${REL_NS}/sharedStrings`,\n target: 'sharedStrings.xml',\n });\n }\n wbRels.rels.push({\n id: `rId${wbRels.rels.length + 1}`,\n type: `${REL_NS}/styles`,\n target: 'styles.xml',\n });\n await writer.addEntry(ARC_WORKBOOK_RELS, relsToBytes(wbRels));\n\n // 6. root rels.\n const rootRels = makeRelationships();\n rootRels.rels.push({\n id: 'rId1',\n type: `${REL_NS}/officeDocument`,\n target: 'xl/workbook.xml',\n });\n await writer.addEntry(ARC_ROOT_RELS, relsToBytes(rootRels));\n void PKG_REL_NS; // imported for future docProps support\n\n // 7. [Content_Types].xml.\n const manifest = makeManifest();\n // Excel rejects packages whose [Content_Types].xml is missing the Default\n // entries for `rels` / `xml` — without them the package relationships file\n // can't be classified and Excel refuses to open.\n addDefault(manifest, 'rels', 'application/vnd.openxmlformats-package.relationships+xml');\n addDefault(manifest, 'xml', 'application/xml');\n addOverride(manifest, `/${ARC_WORKBOOK}`, XLSX_TYPE);\n for (const s of state.sheets) {\n addOverride(manifest, `/xl/worksheets/sheet${s.sheetId}.xml`, WORKSHEET_TYPE);\n }\n addOverride(manifest, `/${ARC_STYLE}`, STYLES_TYPE);\n if (state.sst.entries.length > 0) {\n addOverride(manifest, `/${ARC_SHARED_STRINGS}`, SHARED_STRINGS_TYPE);\n }\n await writer.addEntry(ARC_CONTENT_TYPES, manifestToBytes(manifest));\n\n await writer.finalize();\n}\n\nconst serializeWorkbookXml = (\n sheets: ReadonlyArray<{ title: string; sheetId: number }>,\n): string => {\n const parts: string[] = [\n '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>',\n `<workbook xmlns=\"${SHEET_MAIN_NS}\" xmlns:r=\"${REL_NS}\">`,\n '<sheets>',\n ];\n sheets.forEach((s, i) => {\n parts.push(\n `<sheet name=\"${escapeAttr(s.title)}\" sheetId=\"${s.sheetId}\" r:id=\"rId${i + 1}\"/>`,\n );\n });\n parts.push('</sheets></workbook>');\n return parts.join('');\n};\n\n/** Open a workbook for streaming write-only output. */\nexport async function createWriteOnlyWorkbook(\n sink: XlsxSink,\n _opts: WriteOnlyOptions = {},\n): Promise<WriteOnlyWorkbook> {\n // The streaming-deflate ZIP writer is constructed eagerly here: each\n // addWorksheet opens an entry on it and flushes row chunks through fflate's\n // `Zip` + `ZipDeflate` immediately, so peak memory stays at one pending row\n // buffer plus deflate scratch (no all-sheets accumulation).\n return makeWriteOnlyWorkbook(sink);\n}\n\n"],"mappings":"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"}
1
+ {"version":3,"file":"streaming.mjs","names":[],"sources":["../src/streaming/read-only.ts","../src/streaming/write-only.ts"],"sourcesContent":["// Streaming read-only workbook.\n//\n// `loadWorkbookStream` opens the zip + parses workbook.xml / sharedStrings.xml\n// / styles.xml metadata up front (small even on million-row archives), then\n// exposes a lazy `openWorksheet(name)` that SAX-iterates the sheet body via\n// `iterParse`. The iterator streams rows without materialising the full sheet\n// in memory.\n\nimport { makeSharedStrings, parseSharedStringsXml, type SharedStringsTable } from '../workbook/shared-strings';\nimport { ARC_CONTENT_TYPES, ARC_ROOT_RELS, ARC_SHARED_STRINGS, ARC_STYLE, REL_NS, SHEET_MAIN_NS } from '../xml/namespaces';\nimport { findById, relsFromBytes } from '../packaging/relationships';\nimport { manifestFromBytes } from '../packaging/manifest';\nimport { OpenXmlSchemaError } from '../utils/exceptions';\nimport type { DecompressionLimits } from '../zip/decompression-guard';\nimport { type ZipArchive, openZip } from '../zip/reader';\nimport type { CellValue, ExcelErrorCode } from '../cell/cell';\nimport { ERROR_CODES } from '../utils/inference';\nimport { iterParse, type SaxEvent, type SaxInput } from '../xml/iterparse';\nimport { parseXml } from '../xml/parser';\nimport { findChild, findChildren } from '../xml/tree';\nimport type { XlsxSource } from '../io/source';\nimport { coordinateToTuple } from '../utils/coordinate';\nimport { type Stylesheet, makeStylesheet } from '../styles/stylesheet';\nimport { parseStylesheetXml } from '../styles/stylesheet-reader';\nimport { resolveRelTarget } from '../io/load';\n\nconst SHEET_TAG = `{${SHEET_MAIN_NS}}sheet`;\nconst SHEETS_TAG = `{${SHEET_MAIN_NS}}sheets`;\n\nexport interface IterRowsOptions {\n minRow?: number;\n maxRow?: number;\n minCol?: number;\n maxCol?: number;\n}\n\nexport interface ReadOnlyCell {\n readonly row: number;\n readonly col: number;\n readonly value: CellValue;\n readonly styleId: number;\n}\n\nexport interface ReadOnlyWorksheet {\n title: string;\n iterRows(opts?: IterRowsOptions): AsyncIterableIterator<ReadOnlyCell[]>;\n iterValues(opts?: IterRowsOptions): AsyncIterableIterator<CellValue[]>;\n}\n\nexport interface ReadOnlyWorkbook {\n sheetNames: string[];\n styles: Stylesheet;\n openWorksheet(name: string): ReadOnlyWorksheet;\n close(): Promise<void>;\n}\n\ninterface SheetEntry {\n name: string;\n rId: string;\n partPath: string;\n}\n\nconst parseSheetList = (workbookXml: Uint8Array, workbookPath: string, archive: ZipArchive): SheetEntry[] => {\n const root = parseXml(workbookXml);\n const sheetsEl = findChild(root, SHEETS_TAG);\n if (!sheetsEl) return [];\n const wbRelsPath = relsPathFor(workbookPath);\n const wbRels = archive.has(wbRelsPath) ? relsFromBytes(archive.read(wbRelsPath)) : { rels: [] };\n const out: SheetEntry[] = [];\n for (const sheet of findChildren(sheetsEl, SHEET_TAG)) {\n const name = sheet.attrs['name'];\n const rId = sheet.attrs[`{${REL_NS}}id`];\n if (!name || !rId) continue;\n const rel = findById(wbRels, rId);\n if (!rel) continue;\n const partPath = resolveRelTarget(workbookPath, rel.target);\n out.push({ name, rId, partPath });\n }\n return out;\n};\n\nconst relsPathFor = (partPath: string): string => {\n const i = partPath.lastIndexOf('/');\n if (i < 0) return `_rels/${partPath}.rels`;\n return `${partPath.slice(0, i)}/_rels/${partPath.slice(i + 1)}.rels`;\n};\n\nconst localName = (qname: string): string => {\n const i = qname.lastIndexOf('}');\n return i < 0 ? qname : qname.slice(i + 1);\n};\n\nconst decodeCellValue = (\n t: string,\n vText: string | undefined,\n inlineText: string | undefined,\n sst: ReadonlyArray<string>,\n): CellValue => {\n switch (t) {\n case 'n':\n return vText !== undefined && vText !== '' ? Number.parseFloat(vText) : null;\n case 's': {\n if (vText === undefined) return null;\n const idx = Number.parseInt(vText, 10);\n if (!Number.isInteger(idx) || idx < 0 || idx >= sst.length) return null;\n return sst[idx] ?? null;\n }\n case 'b':\n return vText === '1';\n case 'e': {\n if (!vText || !ERROR_CODES.has(vText)) return null;\n return { kind: 'error', code: vText as ExcelErrorCode };\n }\n case 'str':\n return vText ?? '';\n case 'inlineStr':\n return inlineText ?? '';\n default:\n return vText !== undefined && vText !== '' ? Number.parseFloat(vText) : null;\n }\n};\n\n/**\n * SAX-iterate `<sheetData>/<row>/<c>` events out of the worksheet bytes (or a\n * stream that yields them), yielding one `ReadOnlyCell[]` per row that matches\n * `opts`.\n */\nasync function* iterSheetRows(\n sheetInput: SaxInput,\n sst: ReadonlyArray<string>,\n opts: IterRowsOptions,\n): AsyncIterableIterator<ReadOnlyCell[]> {\n const minRow = opts.minRow ?? 1;\n const maxRow = opts.maxRow ?? Number.POSITIVE_INFINITY;\n const minCol = opts.minCol ?? 1;\n const maxCol = opts.maxCol ?? Number.POSITIVE_INFINITY;\n\n let inSheetData = false;\n let currentRow = -1;\n let currentRowAttrs: Record<string, string> | null = null;\n let currentCells: ReadOnlyCell[] = [];\n\n // Per-cell state. Reset when each <c> starts.\n let cellOpen = false;\n let cellRow = 0;\n let cellCol = 0;\n let cellType = 'n';\n let cellStyleId = 0;\n let inV = false;\n let vText = '';\n let inIs = false;\n let inIsT = false;\n let isText = '';\n\n for await (const ev of iterParse(sheetInput)) {\n const e = ev as SaxEvent;\n if (e.kind === 'start') {\n const local = localName(e.name);\n if (!inSheetData) {\n if (local === 'sheetData') inSheetData = true;\n continue;\n }\n switch (local) {\n case 'row': {\n currentRowAttrs = e.attrs;\n const rRaw = e.attrs['r'];\n currentRow = rRaw ? Number.parseInt(rRaw, 10) : currentRow + 1;\n currentCells = [];\n break;\n }\n case 'c': {\n if (currentRow < 0) break;\n // Skip cell-attr parsing entirely when the row is outside the\n // requested band — saves the parseInt + coordinateToTuple hit on\n // every cell of every excluded row.\n if (currentRow < minRow || currentRow > maxRow) break;\n cellOpen = true;\n cellType = e.attrs['t'] ?? 'n';\n const sRaw = e.attrs['s'];\n cellStyleId = sRaw ? Number.parseInt(sRaw, 10) || 0 : 0;\n const ref = e.attrs['r'];\n if (ref) {\n const tup = coordinateToTuple(ref);\n cellRow = tup.row;\n cellCol = tup.col;\n } else {\n cellRow = currentRow;\n cellCol = (currentCells[currentCells.length - 1]?.col ?? 0) + 1;\n }\n vText = '';\n isText = '';\n break;\n }\n case 'v':\n if (cellOpen) inV = true;\n break;\n case 'is':\n if (cellOpen) inIs = true;\n break;\n case 't':\n if (inIs) inIsT = true;\n break;\n default:\n break;\n }\n continue;\n }\n if (e.kind === 'text') {\n if (inV) vText += e.text;\n else if (inIsT) isText += e.text;\n continue;\n }\n // end\n const local = localName(e.name);\n if (!inSheetData) continue;\n switch (local) {\n case 'sheetData':\n inSheetData = false;\n return;\n case 'row': {\n if (currentRow >= minRow && currentRow <= maxRow && currentCells.length > 0) {\n yield currentCells;\n }\n // Once we've crossed maxRow there are no more rows to yield — every\n // subsequent <row> would just be parsed and dropped. Stop iterating\n // early. ECMA-376 emits rows in ascending order.\n if (currentRow > maxRow) {\n inSheetData = false;\n return;\n }\n currentRow = -1;\n currentRowAttrs = null;\n currentCells = [];\n break;\n }\n case 'c': {\n if (cellOpen && cellCol >= minCol && cellCol <= maxCol && cellRow >= minRow && cellRow <= maxRow) {\n const value = decodeCellValue(cellType, vText, isText, sst);\n currentCells.push({ row: cellRow, col: cellCol, value, styleId: cellStyleId });\n }\n cellOpen = false;\n break;\n }\n case 'v':\n inV = false;\n break;\n case 'is':\n inIs = false;\n break;\n case 't':\n if (inIs) inIsT = false;\n break;\n default:\n break;\n }\n }\n // Avoid unused-var lint when row attrs never touched.\n void currentRowAttrs;\n}\n\n/**\n * Build a sorted `[rowNum, byteOffset]` index for every `<row r=\"N\">`\n * occurrence in a worksheet's bytes. Pure byte-level scan (no SAX), cheap\n * relative to the per-cell SAX walk: ~50 ns per row on M-series Node 22.\n *\n * `sheetDataEnd` is the byte offset of `</sheetData>` so callers can clip the\n * slice that gets handed to saxes.\n */\nconst buildRowOffsetIndex = (\n bytes: Uint8Array,\n): { index: ReadonlyArray<{ row: number; offset: number }>; sheetDataEnd: number } => {\n const out: Array<{ row: number; offset: number }> = [];\n let sheetDataEnd = -1;\n let i = 0;\n while (i < bytes.length) {\n if (bytes[i] !== 0x3c /* '<' */) {\n i++;\n continue;\n }\n // Detect `</sheetData>` once — used to clip the slice fed to saxes.\n if (\n sheetDataEnd < 0 &&\n bytes[i + 1] === 0x2f /* '/' */ &&\n bytes[i + 2] === 0x73 /* 's' */ &&\n bytes[i + 3] === 0x68 /* 'h' */ &&\n bytes[i + 4] === 0x65 /* 'e' */ &&\n bytes[i + 5] === 0x65 /* 'e' */ &&\n bytes[i + 6] === 0x74 /* 't' */ &&\n bytes[i + 7] === 0x44 /* 'D' */ &&\n bytes[i + 8] === 0x61 /* 'a' */ &&\n bytes[i + 9] === 0x74 /* 't' */ &&\n bytes[i + 10] === 0x61 /* 'a' */ &&\n bytes[i + 11] === 0x3e /* '>' */\n ) {\n sheetDataEnd = i;\n break;\n }\n // Match `<row` followed by ASCII whitespace or '>'.\n if (\n bytes[i + 1] !== 0x72 /* 'r' */ ||\n bytes[i + 2] !== 0x6f /* 'o' */ ||\n bytes[i + 3] !== 0x77 /* 'w' */\n ) {\n i++;\n continue;\n }\n const next = bytes[i + 4];\n if (\n next !== 0x20 /* sp */ &&\n next !== 0x09 /* tab */ &&\n next !== 0x0a /* lf */ &&\n next !== 0x0d /* cr */ &&\n next !== 0x3e /* > */ &&\n next !== 0x2f /* / */\n ) {\n i++;\n continue;\n }\n // Walk to the closing '>'; the attrs region carries `r=\"N\"`.\n const start = i;\n let j = i + 4;\n while (j < bytes.length && bytes[j] !== 0x3e) j++;\n if (j >= bytes.length) break;\n const attrsBuf = bytes.subarray(start + 4, j);\n const attrs = new TextDecoder('ascii', { fatal: false }).decode(attrsBuf);\n const m = /\\sr=\"(\\d+)\"/.exec(attrs);\n if (m?.[1]) {\n const row = Number.parseInt(m[1], 10);\n if (Number.isInteger(row)) out.push({ row, offset: start });\n }\n i = j + 1;\n }\n if (sheetDataEnd < 0) sheetDataEnd = bytes.length;\n return { index: out, sheetDataEnd };\n};\n\n/**\n * Binary-search the row index for the first entry with `row >= target`. Returns\n * -1 when every recorded row is below the target.\n */\nconst firstRowAtOrAfter = (\n index: ReadonlyArray<{ row: number; offset: number }>,\n target: number,\n): number => {\n let lo = 0;\n let hi = index.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n const entry = index[mid];\n if (!entry || entry.row < target) lo = mid + 1;\n else hi = mid;\n }\n return lo < index.length ? lo : -1;\n};\n\n/**\n * Slice a worksheet's bytes to start at the row at index `idxPos` of the\n * row-offset index, wrapping the result with a synthetic `<sheetData>` envelope\n * so saxes parses it in the right namespace.\n */\nconst SHEET_DATA_OPEN = `<?xml version=\"1.0\" encoding=\"UTF-8\"?><sheetData xmlns=\"${SHEET_MAIN_NS}\">`;\nconst SHEET_DATA_CLOSE = `</sheetData>`;\nconst sliceFromRow = (\n bytes: Uint8Array,\n fromOffset: number,\n sheetDataEnd: number,\n): Uint8Array => {\n const prefix = new TextEncoder().encode(SHEET_DATA_OPEN);\n const suffix = new TextEncoder().encode(SHEET_DATA_CLOSE);\n const middle = bytes.subarray(fromOffset, sheetDataEnd);\n const out = new Uint8Array(prefix.length + middle.length + suffix.length);\n out.set(prefix, 0);\n out.set(middle, prefix.length);\n out.set(suffix, prefix.length + middle.length);\n return out;\n};\n\n/**\n * Factory: build a {@link ReadOnlyWorksheet} bound to a single worksheet part\n * inside an opened archive. SAX iteration runs lazily — `iterRows` re-reads the\n * part bytes each time so the caller can iterate the same sheet repeatedly\n * without keeping a buffered decoder around.\n *\n * For `iterRows({ minRow > 1 })`, a row-offset index is built lazily on first\n * use and cached; subsequent band queries jump straight to the byte offset of\n * the first matching row instead of SAX-walking the entire `<sheetData>`.\n */\nconst makeStreamingReadOnlyWorksheet = (\n title: string,\n archive: ZipArchive,\n partPath: string,\n sst: ReadonlyArray<string>,\n): ReadOnlyWorksheet => {\n // Lazy + cached. The index is small (~16 B per row); for 1M rows that's 16 MB\n // of working set, vs. the alternative of walking the sheet bytes through\n // saxes on every band query.\n let cached: ReturnType<typeof buildRowOffsetIndex> | undefined;\n const ensureIndex = (bytes: Uint8Array) => {\n if (!cached) cached = buildRowOffsetIndex(bytes);\n return cached;\n };\n\n const iterRows = (opts: IterRowsOptions = {}): AsyncIterableIterator<ReadOnlyCell[]> => {\n const minRow = opts.minRow ?? 1;\n if (minRow <= 1) {\n // Whole-sheet (or no-min) iter — feed the SAX parser directly off the\n // archive's streaming inflate path so the worksheet's inflated payload\n // is never fully resident. Peak memory for the walk drops to the\n // inflate window + SAX state instead of the entire `<sheetData>` body.\n return iterSheetRows(archive.readStream(partPath), sst, opts);\n }\n // Band query (minRow > 1): the row-offset index needs the full inflated\n // bytes so we can binary-search to the byte offset of the first matching\n // row. Materialise once and reuse via `ensureIndex`.\n const bytes = archive.read(partPath);\n const { index, sheetDataEnd } = ensureIndex(bytes);\n if (index.length === 0) return iterSheetRows(bytes, sst, opts);\n const pos = firstRowAtOrAfter(index, minRow);\n if (pos < 0) {\n // Every row is below minRow — nothing to yield.\n return (async function* () {})();\n }\n const target = index[pos];\n if (!target) return iterSheetRows(bytes, sst, opts);\n const sliced = sliceFromRow(bytes, target.offset, sheetDataEnd);\n return iterSheetRows(sliced, sst, opts);\n };\n const iterValues = async function* (opts: IterRowsOptions = {}): AsyncIterableIterator<CellValue[]> {\n for await (const row of iterRows(opts)) {\n yield row.map((c) => c.value);\n }\n };\n return { title, iterRows, iterValues };\n};\n\n/**\n * Factory: build a {@link ReadOnlyWorkbook} from an opened archive + pre-parsed\n * sheet list / styles / shared strings. Per the project-wide \"no classes\" rule\n * (CLAUDE.md), the workbook is a plain object closing over the archive handle.\n */\nconst makeStreamingReadOnlyWorkbook = (\n sheetNames: string[],\n styles: Stylesheet,\n archive: ZipArchive,\n entries: ReadonlyMap<string, SheetEntry>,\n sst: ReadonlyArray<string>,\n): ReadOnlyWorkbook => ({\n sheetNames,\n styles,\n openWorksheet(name) {\n const entry = entries.get(name);\n if (!entry) {\n throw new OpenXmlSchemaError(`loadWorkbookStream: no worksheet named \"${name}\"`);\n }\n return makeStreamingReadOnlyWorksheet(name, archive, entry.partPath, sst);\n },\n async close() {\n archive.close();\n },\n});\n\n/** Options for {@link loadWorkbookStream}. */\nexport interface LoadWorkbookStreamOptions {\n /**\n * Decompression-bomb safeguards applied while inflating zip entries.\n * Defaults to limits that fit any legitimate xlsx; pass `false` to disable\n * (only safe for fully trusted sources).\n */\n decompressionLimits?: DecompressionLimits | false;\n}\n\n/** Open an xlsx for read-only streaming access. */\nexport async function loadWorkbookStream(\n source: XlsxSource,\n opts: LoadWorkbookStreamOptions = {},\n): Promise<ReadOnlyWorkbook> {\n const archive = await openZip(\n source,\n opts.decompressionLimits === undefined ? {} : { decompressionLimits: opts.decompressionLimits },\n );\n if (!archive.has(ARC_CONTENT_TYPES)) {\n throw new OpenXmlSchemaError(`loadWorkbookStream: missing \"${ARC_CONTENT_TYPES}\"`);\n }\n // Manifest parse is intentionally cheap and discarded — we resolve sheets by\n // walking workbook.xml.rels directly.\n manifestFromBytes(archive.read(ARC_CONTENT_TYPES));\n\n if (!archive.has(ARC_ROOT_RELS)) {\n throw new OpenXmlSchemaError(`loadWorkbookStream: missing \"${ARC_ROOT_RELS}\"`);\n }\n const rootRels = relsFromBytes(archive.read(ARC_ROOT_RELS));\n const officeDocRel = rootRels.rels.find((r) => r.type === `${REL_NS}/officeDocument`);\n if (!officeDocRel) {\n throw new OpenXmlSchemaError(`loadWorkbookStream: no officeDocument relationship in root rels`);\n }\n const workbookPath = resolveRelTarget('', officeDocRel.target);\n if (!archive.has(workbookPath)) {\n throw new OpenXmlSchemaError(`loadWorkbookStream: workbook part \"${workbookPath}\" missing`);\n }\n const sheetEntries = parseSheetList(archive.read(workbookPath), workbookPath, archive);\n const entryMap = new Map<string, SheetEntry>();\n for (const e of sheetEntries) entryMap.set(e.name, e);\n\n let sst: SharedStringsTable = makeSharedStrings();\n if (archive.has(ARC_SHARED_STRINGS)) {\n sst = parseSharedStringsXml(archive.read(ARC_SHARED_STRINGS));\n }\n let styles: Stylesheet = makeStylesheet();\n if (archive.has(ARC_STYLE)) {\n styles = parseStylesheetXml(archive.read(ARC_STYLE));\n }\n\n return makeStreamingReadOnlyWorkbook(\n sheetEntries.map((e) => e.name),\n styles,\n archive,\n entryMap,\n sst.entries.map((e) => (typeof e === 'string' ? e : e.runs.map((r) => r.text).join(''))),\n );\n}\n","// Streaming write-only workbook.\n//\n// `createWriteOnlyWorkbook` lets callers append rows one at a time without\n// holding a full Workbook in memory. Each `appendRow` serialises the row\n// directly into a string buffer (no `Cell` objects, no Worksheet rows Map) —\n// when `close()` runs we glue the rows under a `<sheetData>` envelope and hand\n// the bytes to the streaming-deflate ZIP writer. The buffer keeps ~30 bytes per\n// cell of XML text instead of the ~200-byte V8 footprint a Cell + Map entry\n// costs, so the heap budget at 3M cells drops roughly an order of magnitude vs.\n// the previous setCell-based path.\n\nimport type { Cell, CellValue } from '../cell/cell';\nimport type { XlsxSink } from '../io/sink';\nimport { addDefault, addOverride, makeManifest, manifestToBytes } from '../packaging/manifest';\nimport { makeRelationships, relsToBytes } from '../packaging/relationships';\nimport {\n addBorder,\n addCellXf,\n addFill,\n addFont,\n addNumFmt,\n type CellXf,\n defaultCellXf,\n makeStylesheet,\n type Stylesheet,\n} from '../styles/stylesheet';\nimport { stylesheetToBytes } from '../styles/stylesheet-writer';\nimport type { Alignment } from '../styles/alignment';\nimport type { Border } from '../styles/borders';\nimport type { Fill } from '../styles/fills';\nimport type { Font } from '../styles/fonts';\nimport type { Protection } from '../styles/protection';\nimport { escapeXmlAttr } from '../utils/escape';\nimport { OpenXmlIoError } from '../utils/exceptions';\nimport { utf8ByteLength } from '../utils/utf8';\nimport { makeSharedStrings, sharedStringsToBytes } from '../workbook/shared-strings';\nimport { validateSheetTitle } from '../workbook/workbook';\nimport { serializeCell } from '../worksheet/writer';\nimport {\n ARC_CONTENT_TYPES,\n ARC_ROOT_RELS,\n ARC_SHARED_STRINGS,\n ARC_STYLE,\n ARC_WORKBOOK,\n ARC_WORKBOOK_RELS,\n PKG_REL_NS,\n REL_NS,\n SHARED_STRINGS_TYPE,\n SHEET_MAIN_NS,\n STYLES_TYPE,\n WORKSHEET_TYPE,\n XLSX_TYPE,\n} from '../xml/namespaces';\nimport { createZipWriter } from '../zip/writer';\n\nconst escapeAttr = escapeXmlAttr;\n\nexport interface WriteOnlyOptions {\n /** Reserved — currently ignored (the buffered backend doesn't honour it). */\n estimatedMaxRow?: number;\n}\n\nexport interface WriteOnlyStyle {\n font?: Font;\n fill?: Fill;\n border?: Border;\n alignment?: Alignment;\n numberFormat?: string;\n protection?: Protection;\n}\n\nexport type WriteOnlyRowItem = CellValue | { value: CellValue; style?: WriteOnlyStyle };\n\nexport interface WriteOnlyWorksheet {\n readonly title: string;\n appendRow(row: WriteOnlyRowItem[]): Promise<void>;\n setColumnWidth(col: number, width: number): void;\n close(): Promise<void>;\n}\n\nexport interface WriteOnlyWorkbook {\n /** Add a new worksheet. The previous worksheet must be `close()`d first. */\n addWorksheet(title: string): Promise<WriteOnlyWorksheet>;\n /** Finalise the archive: emits styles / sharedStrings / workbook / manifest / rels. */\n finalize(): Promise<void>;\n /**\n * Release the sink without finalising the archive. Call this from a\n * surrounding catch block when the producer pipeline throws before\n * `finalize()` — without it, streaming destinations (`toFile` /\n * `toWritable`) keep the file descriptor / Writable open and the partial\n * xlsx looks valid on disk.\n *\n * Idempotent. Subsequent `addWorksheet` / `finalize` calls throw.\n */\n abort(cause?: unknown): void;\n}\n\nconst validateTitle = (title: string, taken: Set<string>): void => {\n const reason = validateSheetTitle(title);\n if (reason) {\n throw new OpenXmlIoError(`Worksheet title \"${title}\": ${reason}`);\n }\n if (taken.has(title.toLowerCase())) {\n throw new OpenXmlIoError(`Worksheet title \"${title}\" is already in use`);\n }\n};\n\n/** Allocate a CellXf id for a style spec. Mirrors cell-style.ts but works directly on the pool. */\nconst allocateXfId = (ss: Stylesheet, style: WriteOnlyStyle): number => {\n // `xfId` is intentionally omitted: leaving it undefined skips the\n // cellStyleXfs bounds check and matches what Excel emits when there is no\n // parent style.\n let xf: CellXf = { fontId: 0, fillId: 0, borderId: 0, numFmtId: 0 };\n if (style.font !== undefined) {\n xf = { ...xf, fontId: addFont(ss, style.font), applyFont: true };\n }\n if (style.fill !== undefined) {\n xf = { ...xf, fillId: addFill(ss, style.fill), applyFill: true };\n }\n if (style.border !== undefined) {\n xf = { ...xf, borderId: addBorder(ss, style.border), applyBorder: true };\n }\n if (style.numberFormat !== undefined) {\n xf = { ...xf, numFmtId: addNumFmt(ss, style.numberFormat), applyNumberFormat: true };\n }\n if (style.alignment !== undefined) {\n xf = { ...xf, alignment: style.alignment, applyAlignment: true };\n }\n if (style.protection !== undefined) {\n xf = { ...xf, protection: style.protection, applyProtection: true };\n }\n return addCellXf(ss, xf);\n};\n\ninterface WorkbookState {\n styles: Stylesheet;\n sst: ReturnType<typeof makeSharedStrings>;\n /** Sheet emit metadata, in addWorksheet order. */\n sheets: Array<{\n title: string;\n sheetId: number;\n }>;\n /** True once finalize() has been called (further mutations throw). */\n finalised: boolean;\n /** True while a worksheet is open (the next addWorksheet must wait). */\n hasOpenWorksheet: boolean;\n /** ZIP writer the workbook + each open worksheet stream chunks through. */\n writer: import('../zip/writer').ZipWriter;\n}\n\n/**\n * Flush threshold for the worksheet's pending-row text buffer. Smaller values\n * minimise heap; larger values amortise the TextEncoder + push overhead. 64 KB\n * is a sweet spot — heap stays low and per-row JS work is dominated by the\n * actual XML construction, not flushing.\n */\nconst FLUSH_THRESHOLD_BYTES = 64 * 1024;\n\n/**\n * Factory: build a {@link WriteOnlyWorksheet} that closes over the shared\n * {@link WorkbookState}. Per the project-wide \"no classes\" rule (CLAUDE.md) the\n * worksheet is a plain object holding the row buffer + column-width map in\n * closure state.\n *\n * The worksheet streams its `<sheetData>` body chunk-by-chunk through the ZIP\n * writer's `addStreamingEntry` API, so the heap footprint stays at one ~64 KB\n * pending text buffer plus deflate scratch — no Cell objects, no all-rows\n * accumulation. The XML envelope (decl / worksheet open / cols / sheetData\n * open) flushes on the first `appendRow` (or `close()` if the sheet is empty);\n * column widths staged via `setColumnWidth` *must* land before the first row.\n */\nconst makeWriteOnlyWorksheet = (state: WorkbookState, title: string, sheetId: number): WriteOnlyWorksheet => {\n let nextRow = 1;\n let closed = false;\n let headerFlushed = false;\n const columnWidths = new Map<number, number>();\n const dummyCtx = { sharedStrings: state.sst, rels: makeRelationships() };\n const encoder = new TextEncoder();\n const stream = state.writer.addStreamingEntry(`xl/worksheets/sheet${sheetId}.xml`);\n let pendingText = '';\n let pendingBytes = 0;\n\n const writeText = (text: string): void => {\n pendingText += text;\n // UTF-8 byte length, computed without a full encode: scan once and add the\n // exact codepoint cost. `text.length` would undercount CJK by ~3× and let\n // the buffer balloon past FLUSH_THRESHOLD_BYTES on Japanese / Chinese\n // workloads.\n pendingBytes += utf8ByteLength(text);\n if (pendingBytes >= FLUSH_THRESHOLD_BYTES) {\n stream.write(encoder.encode(pendingText));\n pendingText = '';\n pendingBytes = 0;\n }\n };\n\n const flushHeader = (): void => {\n if (headerFlushed) return;\n headerFlushed = true;\n let header = '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>';\n header += `<worksheet xmlns=\"${SHEET_MAIN_NS}\" xmlns:r=\"${REL_NS}\">`;\n if (columnWidths.size > 0) {\n header += '<cols>';\n const sorted = [...columnWidths.entries()].sort((a, b) => a[0] - b[0]);\n for (const [col, width] of sorted) {\n header += `<col min=\"${col}\" max=\"${col}\" width=\"${width}\" customWidth=\"1\"/>`;\n }\n header += '</cols>';\n }\n header += '<sheetData>';\n writeText(header);\n };\n\n const appendRow = async (row: WriteOnlyRowItem[]): Promise<void> => {\n if (closed) throw new OpenXmlIoError('appendRow: worksheet already closed');\n flushHeader();\n const r = nextRow++;\n let xml = `<row r=\"${r}\">`;\n for (let i = 0; i < row.length; i++) {\n const item = row[i];\n if (item === undefined || item === null) continue;\n const col = i + 1;\n let value: CellValue;\n let style: WriteOnlyStyle | undefined;\n if (item !== null && typeof item === 'object' && 'value' in (item as object)) {\n const wrapped = item as { value: CellValue; style?: WriteOnlyStyle };\n value = wrapped.value;\n style = wrapped.style;\n } else {\n value = item as CellValue;\n }\n const styleId = style ? allocateXfId(state.styles, style) : 0;\n // Ephemeral cell-shaped object — discarded as soon as serializeCell\n // returns its `<c .../>` string. Keeps the heap footprint at the size of\n // the pending text buffer instead of a full Worksheet model.\n const cell: Cell = { row: r, col, value, styleId };\n xml += serializeCell(cell, dummyCtx);\n }\n xml += '</row>';\n writeText(xml);\n };\n\n const setColumnWidth = (col: number, width: number): void => {\n if (closed) throw new OpenXmlIoError('setColumnWidth: worksheet already closed');\n if (headerFlushed) {\n throw new OpenXmlIoError(\n 'setColumnWidth: must be called before the first appendRow — column widths are emitted as part of the worksheet header',\n );\n }\n columnWidths.set(col, width);\n };\n\n const close = async (): Promise<void> => {\n if (closed) return;\n closed = true;\n flushHeader();\n writeText('</sheetData></worksheet>');\n if (pendingText.length > 0) {\n stream.write(encoder.encode(pendingText));\n pendingText = '';\n pendingBytes = 0;\n }\n await stream.end();\n state.sheets.push({ title, sheetId });\n state.hasOpenWorksheet = false;\n };\n\n return { title, appendRow, setColumnWidth, close };\n};\n\n/**\n * Factory: build a {@link WriteOnlyWorkbook} from a sink. State lives in a\n * closure rather than on a class instance per the project-wide \"no classes\"\n * rule (CLAUDE.md).\n */\nconst makeWriteOnlyWorkbook = (sink: XlsxSink): WriteOnlyWorkbook => {\n const styles = makeStylesheet();\n // Reserve cellXfs[0] for the default (no apply* flags). Unstyled cells point\n // at this slot via styleId=0; user-styled cells start at index 1 so the\n // writer emits an `s=\"N\"` attribute for them.\n addCellXf(styles, defaultCellXf());\n // The ZIP writer is created up front: each addWorksheet opens a streaming\n // entry on it and flushes row chunks through the deflate stream as they\n // arrive. Sheets emit before styles / sst / workbook / rels / content-types\n // so the writer can serialise them in order.\n const state: WorkbookState = {\n styles,\n sst: makeSharedStrings(),\n sheets: [],\n finalised: false,\n hasOpenWorksheet: false,\n writer: createZipWriter(sink),\n };\n\n const addWorksheet = async (title: string): Promise<WriteOnlyWorksheet> => {\n if (state.finalised) {\n throw new OpenXmlIoError('addWorksheet: workbook already finalised');\n }\n if (state.hasOpenWorksheet) {\n throw new OpenXmlIoError(\n 'addWorksheet: previous worksheet still open — call close() before opening the next one',\n );\n }\n const taken = new Set(state.sheets.map((s) => s.title.toLowerCase()));\n validateTitle(title, taken);\n state.hasOpenWorksheet = true;\n const sheetId = state.sheets.length + 1;\n return makeWriteOnlyWorksheet(state, title, sheetId);\n };\n\n const finalize = async (): Promise<void> => {\n if (state.finalised) {\n throw new OpenXmlIoError('finalize: already finalised');\n }\n if (state.hasOpenWorksheet) {\n throw new OpenXmlIoError('finalize: a worksheet is still open — call close() before finalising');\n }\n state.finalised = true;\n const writer = state.writer;\n try {\n await finalizeImpl(state, writer);\n } catch (err) {\n // Release the sink so the half-emitted archive doesn't linger as a\n // valid-looking file on disk. abort() is idempotent.\n writer.abort(err);\n throw err;\n }\n };\n\n const abort = (cause?: unknown): void => {\n if (state.finalised) return;\n state.finalised = true;\n state.writer.abort(cause);\n };\n\n return { addWorksheet, finalize, abort };\n};\n\nasync function finalizeImpl(state: WorkbookState, writer: WorkbookState['writer']): Promise<void> {\n // 1. Worksheets — already streamed through writer.addStreamingEntry\n // during each WriteOnlyWorksheet's appendRow / close cycle.\n\n // 2. Stylesheet.\n await writer.addEntry(ARC_STYLE, stylesheetToBytes(state.styles));\n\n // 3. SharedStrings (only when non-empty).\n if (state.sst.entries.length > 0) {\n await writer.addEntry(ARC_SHARED_STRINGS, sharedStringsToBytes(state.sst));\n }\n\n // 4. workbook.xml.\n const workbookXml = serializeWorkbookXml(state.sheets);\n await writer.addEntry(ARC_WORKBOOK, new TextEncoder().encode(workbookXml));\n\n // 5. workbook.xml.rels.\n const wbRels = makeRelationships();\n state.sheets.forEach((s, i) => {\n wbRels.rels.push({\n id: `rId${i + 1}`,\n type: `${REL_NS}/worksheet`,\n target: `worksheets/sheet${s.sheetId}.xml`,\n });\n });\n if (state.sst.entries.length > 0) {\n wbRels.rels.push({\n id: `rId${wbRels.rels.length + 1}`,\n type: `${REL_NS}/sharedStrings`,\n target: 'sharedStrings.xml',\n });\n }\n wbRels.rels.push({\n id: `rId${wbRels.rels.length + 1}`,\n type: `${REL_NS}/styles`,\n target: 'styles.xml',\n });\n await writer.addEntry(ARC_WORKBOOK_RELS, relsToBytes(wbRels));\n\n // 6. root rels.\n const rootRels = makeRelationships();\n rootRels.rels.push({\n id: 'rId1',\n type: `${REL_NS}/officeDocument`,\n target: 'xl/workbook.xml',\n });\n await writer.addEntry(ARC_ROOT_RELS, relsToBytes(rootRels));\n void PKG_REL_NS; // imported for future docProps support\n\n // 7. [Content_Types].xml.\n const manifest = makeManifest();\n // Excel rejects packages whose [Content_Types].xml is missing the Default\n // entries for `rels` / `xml` — without them the package relationships file\n // can't be classified and Excel refuses to open.\n addDefault(manifest, 'rels', 'application/vnd.openxmlformats-package.relationships+xml');\n addDefault(manifest, 'xml', 'application/xml');\n addOverride(manifest, `/${ARC_WORKBOOK}`, XLSX_TYPE);\n for (const s of state.sheets) {\n addOverride(manifest, `/xl/worksheets/sheet${s.sheetId}.xml`, WORKSHEET_TYPE);\n }\n addOverride(manifest, `/${ARC_STYLE}`, STYLES_TYPE);\n if (state.sst.entries.length > 0) {\n addOverride(manifest, `/${ARC_SHARED_STRINGS}`, SHARED_STRINGS_TYPE);\n }\n await writer.addEntry(ARC_CONTENT_TYPES, manifestToBytes(manifest));\n\n await writer.finalize();\n}\n\nconst serializeWorkbookXml = (\n sheets: ReadonlyArray<{ title: string; sheetId: number }>,\n): string => {\n const parts: string[] = [\n '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>',\n `<workbook xmlns=\"${SHEET_MAIN_NS}\" xmlns:r=\"${REL_NS}\">`,\n '<sheets>',\n ];\n sheets.forEach((s, i) => {\n parts.push(\n `<sheet name=\"${escapeAttr(s.title)}\" sheetId=\"${s.sheetId}\" r:id=\"rId${i + 1}\"/>`,\n );\n });\n parts.push('</sheets></workbook>');\n return parts.join('');\n};\n\n/** Open a workbook for streaming write-only output. */\nexport async function createWriteOnlyWorkbook(\n sink: XlsxSink,\n _opts: WriteOnlyOptions = {},\n): Promise<WriteOnlyWorkbook> {\n // The streaming-deflate ZIP writer is constructed eagerly here: each\n // addWorksheet opens an entry on it and flushes row chunks through fflate's\n // `Zip` + `ZipDeflate` immediately, so peak memory stays at one pending row\n // buffer plus deflate scratch (no all-sheets accumulation).\n return makeWriteOnlyWorkbook(sink);\n}\n\n"],"mappings":";;;;;;;;;;;;;;;AA0BA,MAAM,YAAY,IAAI,cAAc;AACpC,MAAM,aAAa,IAAI,cAAc;AAmCrC,MAAM,kBAAkB,aAAyB,cAAsB,YAAsC;CAE3G,MAAM,WAAW,UADJ,SAAS,WACQ,GAAG,UAAU;CAC3C,IAAI,CAAC,UAAU,OAAO,CAAC;CACvB,MAAM,aAAa,YAAY,YAAY;CAC3C,MAAM,SAAS,QAAQ,IAAI,UAAU,IAAI,cAAc,QAAQ,KAAK,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE;CAC9F,MAAM,MAAoB,CAAC;CAC3B,KAAK,MAAM,SAAS,aAAa,UAAU,SAAS,GAAG;EACrD,MAAM,OAAO,MAAM,MAAM;EACzB,MAAM,MAAM,MAAM,MAAM,IAAI,OAAO;EACnC,IAAI,CAAC,QAAQ,CAAC,KAAK;EACnB,MAAM,MAAM,SAAS,QAAQ,GAAG;EAChC,IAAI,CAAC,KAAK;EACV,MAAM,WAAW,iBAAiB,cAAc,IAAI,MAAM;EAC1D,IAAI,KAAK;GAAE;GAAM;GAAK;EAAS,CAAC;CAClC;CACA,OAAO;AACT;AAEA,MAAM,eAAe,aAA6B;CAChD,MAAM,IAAI,SAAS,YAAY,GAAG;CAClC,IAAI,IAAI,GAAG,OAAO,SAAS,SAAS;CACpC,OAAO,GAAG,SAAS,MAAM,GAAG,CAAC,EAAE,SAAS,SAAS,MAAM,IAAI,CAAC,EAAE;AAChE;AAEA,MAAM,aAAa,UAA0B;CAC3C,MAAM,IAAI,MAAM,YAAY,GAAG;CAC/B,OAAO,IAAI,IAAI,QAAQ,MAAM,MAAM,IAAI,CAAC;AAC1C;AAEA,MAAM,mBACJ,GACA,OACA,YACA,QACc;CACd,QAAQ,GAAR;EACE,KAAK,KACH,OAAO,UAAU,KAAA,KAAa,UAAU,KAAK,OAAO,WAAW,KAAK,IAAI;EAC1E,KAAK,KAAK;GACR,IAAI,UAAU,KAAA,GAAW,OAAO;GAChC,MAAM,MAAM,OAAO,SAAS,OAAO,EAAE;GACrC,IAAI,CAAC,OAAO,UAAU,GAAG,KAAK,MAAM,KAAK,OAAO,IAAI,QAAQ,OAAO;GACnE,OAAO,IAAI,QAAQ;EACrB;EACA,KAAK,KACH,OAAO,UAAU;EACnB,KAAK;GACH,IAAI,CAAC,SAAS,CAAC,YAAY,IAAI,KAAK,GAAG,OAAO;GAC9C,OAAO;IAAE,MAAM;IAAS,MAAM;GAAwB;EAExD,KAAK,OACH,OAAO,SAAS;EAClB,KAAK,aACH,OAAO,cAAc;EACvB,SACE,OAAO,UAAU,KAAA,KAAa,UAAU,KAAK,OAAO,WAAW,KAAK,IAAI;CAC5E;AACF;;;;;;AAOA,gBAAgB,cACd,YACA,KACA,MACuC;CACvC,MAAM,SAAS,KAAK,UAAU;CAC9B,MAAM,SAAS,KAAK,UAAU,OAAO;CACrC,MAAM,SAAS,KAAK,UAAU;CAC9B,MAAM,SAAS,KAAK,UAAU,OAAO;CAErC,IAAI,cAAc;CAClB,IAAI,aAAa;CAEjB,IAAI,eAA+B,CAAC;CAGpC,IAAI,WAAW;CACf,IAAI,UAAU;CACd,IAAI,UAAU;CACd,IAAI,WAAW;CACf,IAAI,cAAc;CAClB,IAAI,MAAM;CACV,IAAI,QAAQ;CACZ,IAAI,OAAO;CACX,IAAI,QAAQ;CACZ,IAAI,SAAS;CAEb,WAAW,MAAM,MAAM,UAAU,UAAU,GAAG;EAC5C,MAAM,IAAI;EACV,IAAI,EAAE,SAAS,SAAS;GACtB,MAAM,QAAQ,UAAU,EAAE,IAAI;GAC9B,IAAI,CAAC,aAAa;IAChB,IAAI,UAAU,aAAa,cAAc;IACzC;GACF;GACA,QAAQ,OAAR;IACE,KAAK,OAAO;KACV,EAAoB;KACpB,MAAM,OAAO,EAAE,MAAM;KACrB,aAAa,OAAO,OAAO,SAAS,MAAM,EAAE,IAAI,aAAa;KAC7D,eAAe,CAAC;KAChB;IACF;IACA,KAAK,KAAK;KACR,IAAI,aAAa,GAAG;KAIpB,IAAI,aAAa,UAAU,aAAa,QAAQ;KAChD,WAAW;KACX,WAAW,EAAE,MAAM,QAAQ;KAC3B,MAAM,OAAO,EAAE,MAAM;KACrB,cAAc,OAAO,OAAO,SAAS,MAAM,EAAE,KAAK,IAAI;KACtD,MAAM,MAAM,EAAE,MAAM;KACpB,IAAI,KAAK;MACP,MAAM,MAAM,kBAAkB,GAAG;MACjC,UAAU,IAAI;MACd,UAAU,IAAI;KAChB,OAAO;MACL,UAAU;MACV,WAAW,aAAa,aAAa,SAAS,EAAE,EAAE,OAAO,KAAK;KAChE;KACA,QAAQ;KACR,SAAS;KACT;IACF;IACA,KAAK;KACH,IAAI,UAAU,MAAM;KACpB;IACF,KAAK;KACH,IAAI,UAAU,OAAO;KACrB;IACF,KAAK;KACH,IAAI,MAAM,QAAQ;KAClB;IACF,SACE;GACJ;GACA;EACF;EACA,IAAI,EAAE,SAAS,QAAQ;GACrB,IAAI,KAAK,SAAS,EAAE;QACf,IAAI,OAAO,UAAU,EAAE;GAC5B;EACF;EAEA,MAAM,QAAQ,UAAU,EAAE,IAAI;EAC9B,IAAI,CAAC,aAAa;EAClB,QAAQ,OAAR;GACE,KAAK;IACH,cAAc;IACd;GACF,KAAK;IACH,IAAI,cAAc,UAAU,cAAc,UAAU,aAAa,SAAS,GACxE,MAAM;IAKR,IAAI,aAAa,QAAQ;KACvB,cAAc;KACd;IACF;IACA,aAAa;IAEb,eAAe,CAAC;IAChB;GAEF,KAAK;IACH,IAAI,YAAY,WAAW,UAAU,WAAW,UAAU,WAAW,UAAU,WAAW,QAAQ;KAChG,MAAM,QAAQ,gBAAgB,UAAU,OAAO,QAAQ,GAAG;KAC1D,aAAa,KAAK;MAAE,KAAK;MAAS,KAAK;MAAS;MAAO,SAAS;KAAY,CAAC;IAC/E;IACA,WAAW;IACX;GAEF,KAAK;IACH,MAAM;IACN;GACF,KAAK;IACH,OAAO;IACP;GACF,KAAK;IACH,IAAI,MAAM,QAAQ;IAClB;GACF,SACE;EACJ;CACF;AAGF;;;;;;;;;AAUA,MAAM,uBACJ,UACoF;CACpF,MAAM,MAA8C,CAAC;CACrD,IAAI,eAAe;CACnB,IAAI,IAAI;CACR,OAAO,IAAI,MAAM,QAAQ;EACvB,IAAI,MAAM,OAAO,IAAgB;GAC/B;GACA;EACF;EAEA,IACE,eAAe,KACf,MAAM,IAAI,OAAO,MACjB,MAAM,IAAI,OAAO,OACjB,MAAM,IAAI,OAAO,OACjB,MAAM,IAAI,OAAO,OACjB,MAAM,IAAI,OAAO,OACjB,MAAM,IAAI,OAAO,OACjB,MAAM,IAAI,OAAO,MACjB,MAAM,IAAI,OAAO,MACjB,MAAM,IAAI,OAAO,OACjB,MAAM,IAAI,QAAQ,MAClB,MAAM,IAAI,QAAQ,IAClB;GACA,eAAe;GACf;EACF;EAEA,IACE,MAAM,IAAI,OAAO,OACjB,MAAM,IAAI,OAAO,OACjB,MAAM,IAAI,OAAO,KACjB;GACA;GACA;EACF;EACA,MAAM,OAAO,MAAM,IAAI;EACvB,IACE,SAAS,MACT,SAAS,KACT,SAAS,MACT,SAAS,MACT,SAAS,MACT,SAAS,IACT;GACA;GACA;EACF;EAEA,MAAM,QAAQ;EACd,IAAI,IAAI,IAAI;EACZ,OAAO,IAAI,MAAM,UAAU,MAAM,OAAO,IAAM;EAC9C,IAAI,KAAK,MAAM,QAAQ;EACvB,MAAM,WAAW,MAAM,SAAS,QAAQ,GAAG,CAAC;EAC5C,MAAM,QAAQ,IAAI,YAAY,SAAS,EAAE,OAAO,MAAM,CAAC,CAAC,CAAC,OAAO,QAAQ;EACxE,MAAM,IAAI,cAAc,KAAK,KAAK;EAClC,IAAI,IAAI,IAAI;GACV,MAAM,MAAM,OAAO,SAAS,EAAE,IAAI,EAAE;GACpC,IAAI,OAAO,UAAU,GAAG,GAAG,IAAI,KAAK;IAAE;IAAK,QAAQ;GAAM,CAAC;EAC5D;EACA,IAAI,IAAI;CACV;CACA,IAAI,eAAe,GAAG,eAAe,MAAM;CAC3C,OAAO;EAAE,OAAO;EAAK;CAAa;AACpC;;;;;AAMA,MAAM,qBACJ,OACA,WACW;CACX,IAAI,KAAK;CACT,IAAI,KAAK,MAAM;CACf,OAAO,KAAK,IAAI;EACd,MAAM,MAAO,KAAK,OAAQ;EAC1B,MAAM,QAAQ,MAAM;EACpB,IAAI,CAAC,SAAS,MAAM,MAAM,QAAQ,KAAK,MAAM;OACxC,KAAK;CACZ;CACA,OAAO,KAAK,MAAM,SAAS,KAAK;AAClC;;;;;;AAOA,MAAM,kBAAkB,2DAA2D,cAAc;AACjG,MAAM,mBAAmB;AACzB,MAAM,gBACJ,OACA,YACA,iBACe;CACf,MAAM,SAAS,IAAI,YAAY,CAAC,CAAC,OAAO,eAAe;CACvD,MAAM,SAAS,IAAI,YAAY,CAAC,CAAC,OAAO,gBAAgB;CACxD,MAAM,SAAS,MAAM,SAAS,YAAY,YAAY;CACtD,MAAM,MAAM,IAAI,WAAW,OAAO,SAAS,OAAO,SAAS,OAAO,MAAM;CACxE,IAAI,IAAI,QAAQ,CAAC;CACjB,IAAI,IAAI,QAAQ,OAAO,MAAM;CAC7B,IAAI,IAAI,QAAQ,OAAO,SAAS,OAAO,MAAM;CAC7C,OAAO;AACT;;;;;;;;;;;AAYA,MAAM,kCACJ,OACA,SACA,UACA,QACsB;CAItB,IAAI;CACJ,MAAM,eAAe,UAAsB;EACzC,IAAI,CAAC,QAAQ,SAAS,oBAAoB,KAAK;EAC/C,OAAO;CACT;CAEA,MAAM,YAAY,OAAwB,CAAC,MAA6C;EACtF,MAAM,SAAS,KAAK,UAAU;EAC9B,IAAI,UAAU,GAKZ,OAAO,cAAc,QAAQ,WAAW,QAAQ,GAAG,KAAK,IAAI;EAK9D,MAAM,QAAQ,QAAQ,KAAK,QAAQ;EACnC,MAAM,EAAE,OAAO,iBAAiB,YAAY,KAAK;EACjD,IAAI,MAAM,WAAW,GAAG,OAAO,cAAc,OAAO,KAAK,IAAI;EAC7D,MAAM,MAAM,kBAAkB,OAAO,MAAM;EAC3C,IAAI,MAAM,GAER,QAAQ,mBAAmB,CAAC,EAAA,CAAG;EAEjC,MAAM,SAAS,MAAM;EACrB,IAAI,CAAC,QAAQ,OAAO,cAAc,OAAO,KAAK,IAAI;EAElD,OAAO,cADQ,aAAa,OAAO,OAAO,QAAQ,YACxB,GAAG,KAAK,IAAI;CACxC;CACA,MAAM,aAAa,iBAAiB,OAAwB,CAAC,GAAuC;EAClG,WAAW,MAAM,OAAO,SAAS,IAAI,GACnC,MAAM,IAAI,KAAK,MAAM,EAAE,KAAK;CAEhC;CACA,OAAO;EAAE;EAAO;EAAU;CAAW;AACvC;;;;;;AAOA,MAAM,iCACJ,YACA,QACA,SACA,SACA,SACsB;CACtB;CACA;CACA,cAAc,MAAM;EAClB,MAAM,QAAQ,QAAQ,IAAI,IAAI;EAC9B,IAAI,CAAC,OACH,MAAM,IAAI,mBAAmB,2CAA2C,KAAK,EAAE;EAEjF,OAAO,+BAA+B,MAAM,SAAS,MAAM,UAAU,GAAG;CAC1E;CACA,MAAM,QAAQ;EACZ,QAAQ,MAAM;CAChB;AACF;;AAaA,eAAsB,mBACpB,QACA,OAAkC,CAAC,GACR;CAC3B,MAAM,UAAU,MAAM,QACpB,QACA,KAAK,wBAAwB,KAAA,IAAY,CAAC,IAAI,EAAE,qBAAqB,KAAK,oBAAoB,CAChG;CACA,IAAI,CAAC,QAAQ,IAAA,qBAAqB,GAChC,MAAM,IAAI,mBAAmB,gCAAgC,kBAAkB,EAAE;CAInF,kBAAkB,QAAQ,KAAK,iBAAiB,CAAC;CAEjD,IAAI,CAAC,QAAQ,IAAI,aAAa,GAC5B,MAAM,IAAI,mBAAmB,gCAAgC,cAAc,EAAE;CAG/E,MAAM,eADW,cAAc,QAAQ,KAAK,aAAa,CAC7B,CAAC,CAAC,KAAK,MAAM,MAAM,EAAE,SAAS,GAAG,OAAO,gBAAgB;CACpF,IAAI,CAAC,cACH,MAAM,IAAI,mBAAmB,iEAAiE;CAEhG,MAAM,eAAe,iBAAiB,IAAI,aAAa,MAAM;CAC7D,IAAI,CAAC,QAAQ,IAAI,YAAY,GAC3B,MAAM,IAAI,mBAAmB,sCAAsC,aAAa,UAAU;CAE5F,MAAM,eAAe,eAAe,QAAQ,KAAK,YAAY,GAAG,cAAc,OAAO;CACrF,MAAM,2BAAW,IAAI,IAAwB;CAC7C,KAAK,MAAM,KAAK,cAAc,SAAS,IAAI,EAAE,MAAM,CAAC;CAEpD,IAAI,MAA0B,kBAAkB;CAChD,IAAI,QAAQ,IAAI,kBAAkB,GAChC,MAAM,sBAAsB,QAAQ,KAAK,kBAAkB,CAAC;CAE9D,IAAI,SAAqB,eAAe;CACxC,IAAI,QAAQ,IAAI,SAAS,GACvB,SAAS,mBAAmB,QAAQ,KAAK,SAAS,CAAC;CAGrD,OAAO,8BACL,aAAa,KAAK,MAAM,EAAE,IAAI,GAC9B,QACA,SACA,UACA,IAAI,QAAQ,KAAK,MAAO,OAAO,MAAM,WAAW,IAAI,EAAE,KAAK,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE,CAAE,CACzF;AACF;;;AChdA,MAAM,aAAa;AA0CnB,MAAM,iBAAiB,OAAe,UAA6B;CACjE,MAAM,SAAS,mBAAmB,KAAK;CACvC,IAAI,QACF,MAAM,IAAI,eAAe,oBAAoB,MAAM,KAAK,QAAQ;CAElE,IAAI,MAAM,IAAI,MAAM,YAAY,CAAC,GAC/B,MAAM,IAAI,eAAe,oBAAoB,MAAM,oBAAoB;AAE3E;;AAGA,MAAM,gBAAgB,IAAgB,UAAkC;CAItE,IAAI,KAAa;EAAE,QAAQ;EAAG,QAAQ;EAAG,UAAU;EAAG,UAAU;CAAE;CAClE,IAAI,MAAM,SAAS,KAAA,GACjB,KAAK;EAAE,GAAG;EAAI,QAAQ,QAAQ,IAAI,MAAM,IAAI;EAAG,WAAW;CAAK;CAEjE,IAAI,MAAM,SAAS,KAAA,GACjB,KAAK;EAAE,GAAG;EAAI,QAAQ,QAAQ,IAAI,MAAM,IAAI;EAAG,WAAW;CAAK;CAEjE,IAAI,MAAM,WAAW,KAAA,GACnB,KAAK;EAAE,GAAG;EAAI,UAAU,UAAU,IAAI,MAAM,MAAM;EAAG,aAAa;CAAK;CAEzE,IAAI,MAAM,iBAAiB,KAAA,GACzB,KAAK;EAAE,GAAG;EAAI,UAAU,UAAU,IAAI,MAAM,YAAY;EAAG,mBAAmB;CAAK;CAErF,IAAI,MAAM,cAAc,KAAA,GACtB,KAAK;EAAE,GAAG;EAAI,WAAW,MAAM;EAAW,gBAAgB;CAAK;CAEjE,IAAI,MAAM,eAAe,KAAA,GACvB,KAAK;EAAE,GAAG;EAAI,YAAY,MAAM;EAAY,iBAAiB;CAAK;CAEpE,OAAO,UAAU,IAAI,EAAE;AACzB;;;;;;;AAwBA,MAAM,wBAAwB,KAAK;;;;;;;;;;;;;;AAenC,MAAM,0BAA0B,OAAsB,OAAe,YAAwC;CAC3G,IAAI,UAAU;CACd,IAAI,SAAS;CACb,IAAI,gBAAgB;CACpB,MAAM,+BAAe,IAAI,IAAoB;CAC7C,MAAM,WAAW;EAAE,eAAe,MAAM;EAAK,MAAM,kBAAkB;CAAE;CACvE,MAAM,UAAU,IAAI,YAAY;CAChC,MAAM,SAAS,MAAM,OAAO,kBAAkB,sBAAsB,QAAQ,KAAK;CACjF,IAAI,cAAc;CAClB,IAAI,eAAe;CAEnB,MAAM,aAAa,SAAuB;EACxC,eAAe;EAKf,gBAAgB,eAAe,IAAI;EACnC,IAAI,gBAAgB,uBAAuB;GACzC,OAAO,MAAM,QAAQ,OAAO,WAAW,CAAC;GACxC,cAAc;GACd,eAAe;EACjB;CACF;CAEA,MAAM,oBAA0B;EAC9B,IAAI,eAAe;EACnB,gBAAgB;EAChB,IAAI,SAAS;EACb,UAAU,qBAAqB,cAAc,aAAa,OAAO;EACjE,IAAI,aAAa,OAAO,GAAG;GACzB,UAAU;GACV,MAAM,SAAS,CAAC,GAAG,aAAa,QAAQ,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE;GACrE,KAAK,MAAM,CAAC,KAAK,UAAU,QACzB,UAAU,aAAa,IAAI,SAAS,IAAI,WAAW,MAAM;GAE3D,UAAU;EACZ;EACA,UAAU;EACV,UAAU,MAAM;CAClB;CAEA,MAAM,YAAY,OAAO,QAA2C;EAClE,IAAI,QAAQ,MAAM,IAAI,eAAe,qCAAqC;EAC1E,YAAY;EACZ,MAAM,IAAI;EACV,IAAI,MAAM,WAAW,EAAE;EACvB,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;GACnC,MAAM,OAAO,IAAI;GACjB,IAAI,SAAS,KAAA,KAAa,SAAS,MAAM;GACzC,MAAM,MAAM,IAAI;GAChB,IAAI;GACJ,IAAI;GACJ,IAAI,SAAS,QAAQ,OAAO,SAAS,YAAY,WAAY,MAAiB;IAC5E,MAAM,UAAU;IAChB,QAAQ,QAAQ;IAChB,QAAQ,QAAQ;GAClB,OACE,QAAQ;GAEV,MAAM,UAAU,QAAQ,aAAa,MAAM,QAAQ,KAAK,IAAI;GAK5D,OAAO,cAAc;IADA,KAAK;IAAG;IAAK;IAAO;GACjB,GAAG,QAAQ;EACrC;EACA,OAAO;EACP,UAAU,GAAG;CACf;CAEA,MAAM,kBAAkB,KAAa,UAAwB;EAC3D,IAAI,QAAQ,MAAM,IAAI,eAAe,0CAA0C;EAC/E,IAAI,eACF,MAAM,IAAI,eACR,uHACF;EAEF,aAAa,IAAI,KAAK,KAAK;CAC7B;CAEA,MAAM,QAAQ,YAA2B;EACvC,IAAI,QAAQ;EACZ,SAAS;EACT,YAAY;EACZ,UAAU,0BAA0B;EACpC,IAAI,YAAY,SAAS,GAAG;GAC1B,OAAO,MAAM,QAAQ,OAAO,WAAW,CAAC;GACxC,cAAc;GACd,eAAe;EACjB;EACA,MAAM,OAAO,IAAI;EACjB,MAAM,OAAO,KAAK;GAAE;GAAO;EAAQ,CAAC;EACpC,MAAM,mBAAmB;CAC3B;CAEA,OAAO;EAAE;EAAO;EAAW;EAAgB;CAAM;AACnD;;;;;;AAOA,MAAM,yBAAyB,SAAsC;CACnE,MAAM,SAAS,eAAe;CAI9B,UAAU,QAAQ,cAAc,CAAC;CAKjC,MAAM,QAAuB;EAC3B;EACA,KAAK,kBAAkB;EACvB,QAAQ,CAAC;EACT,WAAW;EACX,kBAAkB;EAClB,QAAQ,gBAAgB,IAAI;CAC9B;CAEA,MAAM,eAAe,OAAO,UAA+C;EACzE,IAAI,MAAM,WACR,MAAM,IAAI,eAAe,0CAA0C;EAErE,IAAI,MAAM,kBACR,MAAM,IAAI,eACR,wFACF;EAEF,MAAM,QAAQ,IAAI,IAAI,MAAM,OAAO,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC,CAAC;EACpE,cAAc,OAAO,KAAK;EAC1B,MAAM,mBAAmB;EACzB,MAAM,UAAU,MAAM,OAAO,SAAS;EACtC,OAAO,uBAAuB,OAAO,OAAO,OAAO;CACrD;CAEA,MAAM,WAAW,YAA2B;EAC1C,IAAI,MAAM,WACR,MAAM,IAAI,eAAe,6BAA6B;EAExD,IAAI,MAAM,kBACR,MAAM,IAAI,eAAe,sEAAsE;EAEjG,MAAM,YAAY;EAClB,MAAM,SAAS,MAAM;EACrB,IAAI;GACF,MAAM,aAAa,OAAO,MAAM;EAClC,SAAS,KAAK;GAGZ,OAAO,MAAM,GAAG;GAChB,MAAM;EACR;CACF;CAEA,MAAM,SAAS,UAA0B;EACvC,IAAI,MAAM,WAAW;EACrB,MAAM,YAAY;EAClB,MAAM,OAAO,MAAM,KAAK;CAC1B;CAEA,OAAO;EAAE;EAAc;EAAU;CAAM;AACzC;AAEA,eAAe,aAAa,OAAsB,QAAgD;CAKhG,MAAM,OAAO,SAAS,WAAW,kBAAkB,MAAM,MAAM,CAAC;CAGhE,IAAI,MAAM,IAAI,QAAQ,SAAS,GAC7B,MAAM,OAAO,SAAS,oBAAoB,qBAAqB,MAAM,GAAG,CAAC;CAI3E,MAAM,cAAc,qBAAqB,MAAM,MAAM;CACrD,MAAM,OAAO,SAAS,cAAc,IAAI,YAAY,CAAC,CAAC,OAAO,WAAW,CAAC;CAGzE,MAAM,SAAS,kBAAkB;CACjC,MAAM,OAAO,SAAS,GAAG,MAAM;EAC7B,OAAO,KAAK,KAAK;GACf,IAAI,MAAM,IAAI;GACd,MAAM,GAAG,OAAO;GAChB,QAAQ,mBAAmB,EAAE,QAAQ;EACvC,CAAC;CACH,CAAC;CACD,IAAI,MAAM,IAAI,QAAQ,SAAS,GAC7B,OAAO,KAAK,KAAK;EACf,IAAI,MAAM,OAAO,KAAK,SAAS;EAC/B,MAAM,GAAG,OAAO;EAChB,QAAQ;CACV,CAAC;CAEH,OAAO,KAAK,KAAK;EACf,IAAI,MAAM,OAAO,KAAK,SAAS;EAC/B,MAAM,GAAG,OAAO;EAChB,QAAQ;CACV,CAAC;CACD,MAAM,OAAO,SAAS,mBAAmB,YAAY,MAAM,CAAC;CAG5D,MAAM,WAAW,kBAAkB;CACnC,SAAS,KAAK,KAAK;EACjB,IAAI;EACJ,MAAM,GAAG,OAAO;EAChB,QAAQ;CACV,CAAC;CACD,MAAM,OAAO,SAAS,eAAe,YAAY,QAAQ,CAAC;CAI1D,MAAM,WAAW,aAAa;CAI9B,WAAW,UAAU,QAAQ,0DAA0D;CACvF,WAAW,UAAU,OAAO,iBAAiB;CAC7C,YAAY,UAAU,IAAI,gBAAgB,SAAS;CACnD,KAAK,MAAM,KAAK,MAAM,QACpB,YAAY,UAAU,uBAAuB,EAAE,QAAQ,OAAO,cAAc;CAE9E,YAAY,UAAU,IAAI,aAAa,WAAW;CAClD,IAAI,MAAM,IAAI,QAAQ,SAAS,GAC7B,YAAY,UAAU,IAAI,sBAAsB,mBAAmB;CAErE,MAAM,OAAO,SAAS,mBAAmB,gBAAgB,QAAQ,CAAC;CAElE,MAAM,OAAO,SAAS;AACxB;AAEA,MAAM,wBACJ,WACW;CACX,MAAM,QAAkB;EACtB;EACA,oBAAoB,cAAc,aAAa,OAAO;EACtD;CACF;CACA,OAAO,SAAS,GAAG,MAAM;EACvB,MAAM,KACJ,gBAAgB,WAAW,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,aAAa,IAAI,EAAE,IAChF;CACF,CAAC;CACD,MAAM,KAAK,sBAAsB;CACjC,OAAO,MAAM,KAAK,EAAE;AACtB;;AAGA,eAAsB,wBACpB,MACA,QAA0B,CAAC,GACC;CAK5B,OAAO,sBAAsB,IAAI;AACnC"}
@@ -39,6 +39,14 @@ export interface Stylesheet {
39
39
  cellStyleXfs: CellXf[];
40
40
  /** Named styles (Excel's "Cell Styles" gallery; populated by addNamedStyle). */
41
41
  namedStyles?: Array<import('./named-styles').StylesheetNamedStyle>;
42
+ /**
43
+ * The tail of `<styleSheet>` this model doesn't cover — `<tableStyles>`,
44
+ * `<colors>` and `<extLst>` (which is where Excel keeps its x14/x15 slicer
45
+ * and timeline styles). Captured verbatim in document order so re-saving a
46
+ * loaded workbook keeps them; CT_Stylesheet puts all three after `<dxfs>`,
47
+ * so the writer appends them there.
48
+ */
49
+ stylesXmlTail?: Array<import('../xml/tree').XmlNode>;
42
50
  _fontIdByKey: Map<string, number>;
43
51
  _fillIdByKey: Map<string, number>;
44
52
  _borderIdByKey: Map<string, number>;
package/dist/styles.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { an as hexToHsl, cn as luminance, dn as normaliseRgb, en as adjustLightness, fn as pickReadableTextColor, hn as rotateHue, in as darken, ln as makeColor, mn as rgbColor, nn as colorToHex, on as hslToHex, pn as resolveIndexedColor, rn as contrastRatio, sn as lighten, tn as adjustSaturation, un as mixColors } from "./worksheet-C6CWgvP5.mjs";
2
- import { $ as ensureBuiltinStyle, A as setCellFill, At as makeNumberFormat, B as setRangeBackgroundColor, Bt as makeGradientFill, C as setCellAsCurrency, Ct as FORMAT_TEXT, D as setCellBackgroundColor, Dt as isBuiltinFormat, E as setCellAsPercent, Et as classifyDateFormat, F as setFontColor, Ft as fontToCss, G as setRangeStyle, Gt as makeBorder, H as setRangeFont, Ht as makePatternFill, I as setFontName, It as makeFont, J as setUnderline, K as setRangeWrapText, Kt as makeSide, L as setFontSize, M as setCellNumberFormat, N as setCellProtection, O as setCellBorder, Ot as isDateFormat, P as setCellStyle, Q as addNamedStyle, R as setItalic, Rt as fillToCss, S as setCellAlignment, St as FORMAT_PERCENTAGE_00, T as setCellAsNumber, Tt as builtinFormatId, U as setRangeNumberFormat, V as setRangeBorderBox, Vt as makeGradientStop, W as setRangeProtection, Wt as borderToCss, X as makeProtection, Xt as makeAlignment, Y as wrapCellText, Yt as alignmentToCss, Z as BUILTIN_NAMED_STYLES, _ as getCellNumberFormat, _t as FORMAT_DATE_YYYYMMDD2, a as cellStyleToCss, at as addNumFmt, b as rotateCellText, bt as FORMAT_NUMBER_00, c as clearCellStyle, ct as listCellStyleXfs, d as copyCellStyle, dt as listFonts, et as addBorder, f as formatAsHeader, ft as makeStylesheet, g as getCellFont, gt as FORMAT_DATE_TIMEDELTA, h as getCellFill, ht as FORMAT_DATE_DATETIME, i as applyNamedStyle, it as addFont, j as setCellFont, jt as DEFAULT_FONT, k as setCellBorderAll, kt as isTimedeltaFormat, l as clearRangeStyle, lt as listCellXfs, m as getCellBorder, mt as BUILTIN_FORMATS_MAX_SIZE, n as alignCellVertical, nt as addCellXf, o as centerCell, ot as defaultCellXf, p as getCellAlignment, pt as BUILTIN_FORMATS, q as setStrikethrough, r as applyBuiltinStyle, rt as addFill, s as clearCellBackground, st as listBorders, t as alignCellHorizontal, tt as addCellStyleXf, u as cloneCellStyle, ut as listFills, v as getCellProtection, vt as FORMAT_GENERAL, w as setCellAsDate, wt as builtinFormatCode, x as setBold, xt as FORMAT_PERCENTAGE, y as indentCell, yt as FORMAT_NUMBER, z as setRangeAlignment, zt as makeFill } from "./cell-style-CN9JXOwm.mjs";
3
- import { r as makeDifferentialStyle, t as addDxf } from "./differential-YEjxNScf.mjs";
1
+ import { An as rotateHue, Cn as luminance, Dn as pickReadableTextColor, En as normaliseRgb, On as resolveIndexedColor, Sn as lighten, Tn as mixColors, _n as colorToHex, bn as hexToHsl, gn as adjustSaturation, hn as adjustLightness, kn as rgbColor, vn as contrastRatio, wn as makeColor, xn as hslToHex, yn as darken } from "./worksheet-C5Ensf_j.mjs";
2
+ import { $ as ensureBuiltinStyle, A as setCellFill, At as makeNumberFormat, B as setRangeBackgroundColor, Bt as makeGradientFill, C as setCellAsCurrency, Ct as FORMAT_TEXT, D as setCellBackgroundColor, Dt as isBuiltinFormat, E as setCellAsPercent, Et as classifyDateFormat, F as setFontColor, Ft as fontToCss, G as setRangeStyle, Gt as makeBorder, H as setRangeFont, Ht as makePatternFill, I as setFontName, It as makeFont, J as setUnderline, K as setRangeWrapText, Kt as makeSide, L as setFontSize, M as setCellNumberFormat, N as setCellProtection, O as setCellBorder, Ot as isDateFormat, P as setCellStyle, Q as addNamedStyle, R as setItalic, Rt as fillToCss, S as setCellAlignment, St as FORMAT_PERCENTAGE_00, T as setCellAsNumber, Tt as builtinFormatId, U as setRangeNumberFormat, V as setRangeBorderBox, Vt as makeGradientStop, W as setRangeProtection, Wt as borderToCss, X as makeProtection, Xt as makeAlignment, Y as wrapCellText, Yt as alignmentToCss, Z as BUILTIN_NAMED_STYLES, _ as getCellNumberFormat, _t as FORMAT_DATE_YYYYMMDD2, a as cellStyleToCss, at as addNumFmt, b as rotateCellText, bt as FORMAT_NUMBER_00, c as clearCellStyle, ct as listCellStyleXfs, d as copyCellStyle, dt as listFonts, et as addBorder, f as formatAsHeader, ft as makeStylesheet, g as getCellFont, gt as FORMAT_DATE_TIMEDELTA, h as getCellFill, ht as FORMAT_DATE_DATETIME, i as applyNamedStyle, it as addFont, j as setCellFont, jt as DEFAULT_FONT, k as setCellBorderAll, kt as isTimedeltaFormat, l as clearRangeStyle, lt as listCellXfs, m as getCellBorder, mt as BUILTIN_FORMATS_MAX_SIZE, n as alignCellVertical, nt as addCellXf, o as centerCell, ot as defaultCellXf, p as getCellAlignment, pt as BUILTIN_FORMATS, q as setStrikethrough, r as applyBuiltinStyle, rt as addFill, s as clearCellBackground, st as listBorders, t as alignCellHorizontal, tt as addCellStyleXf, u as cloneCellStyle, ut as listFills, v as getCellProtection, vt as FORMAT_GENERAL, w as setCellAsDate, wt as builtinFormatCode, x as setBold, xt as FORMAT_PERCENTAGE, y as indentCell, yt as FORMAT_NUMBER, z as setRangeAlignment, zt as makeFill } from "./cell-style-DuvS_Lsg.mjs";
3
+ import { r as makeDifferentialStyle, t as addDxf } from "./differential-DvKU8fJt.mjs";
4
4
  export { BUILTIN_FORMATS, BUILTIN_FORMATS_MAX_SIZE, BUILTIN_NAMED_STYLES, DEFAULT_FONT, FORMAT_DATE_DATETIME, FORMAT_DATE_TIMEDELTA, FORMAT_DATE_YYYYMMDD2, FORMAT_GENERAL, FORMAT_NUMBER, FORMAT_NUMBER_00, FORMAT_PERCENTAGE, FORMAT_PERCENTAGE_00, FORMAT_TEXT, addBorder, addCellStyleXf, addCellXf, addDxf, addFill, addFont, addNamedStyle, addNumFmt, adjustLightness, adjustSaturation, alignCellHorizontal, alignCellVertical, alignmentToCss, applyBuiltinStyle, applyNamedStyle, borderToCss, builtinFormatCode, builtinFormatId, cellStyleToCss, centerCell, classifyDateFormat, clearCellBackground, clearCellStyle, clearRangeStyle, cloneCellStyle, colorToHex, contrastRatio, copyCellStyle, darken, defaultCellXf, ensureBuiltinStyle, fillToCss, fontToCss, formatAsHeader, getCellAlignment, getCellBorder, getCellFill, getCellFont, getCellNumberFormat, getCellProtection, hexToHsl, hslToHex, indentCell, isBuiltinFormat, isDateFormat, isTimedeltaFormat, lighten, listBorders, listCellStyleXfs, listCellXfs, listFills, listFonts, luminance, makeAlignment, makeBorder, makeColor, makeDifferentialStyle, makeFill, makeFont, makeGradientFill, makeGradientStop, makeNumberFormat, makePatternFill, makeProtection, makeSide, makeStylesheet, mixColors, normaliseRgb, pickReadableTextColor, resolveIndexedColor, rgbColor, rotateCellText, rotateHue, setBold, setCellAlignment, setCellAsCurrency, setCellAsDate, setCellAsNumber, setCellAsPercent, setCellBackgroundColor, setCellBorder, setCellBorderAll, setCellFill, setCellFont, setCellNumberFormat, setCellProtection, setCellStyle, setFontColor, setFontName, setFontSize, setItalic, setRangeAlignment, setRangeBackgroundColor, setRangeBorderBox, setRangeFont, setRangeNumberFormat, setRangeProtection, setRangeStyle, setRangeWrapText, setStrikethrough, setUnderline, wrapCellText };
@@ -7,17 +7,17 @@ import { t as makeChartsheet } from "./chartsheet-C3-tqkPy.mjs";
7
7
  import { o as makeDrawing } from "./drawing-BxzLuryn.mjs";
8
8
  import { n as VALUED_COLOR_MOD_KINDS, r as VALUELESS_COLOR_MOD_KINDS, t as SCHEME_COLOR_NAMES } from "./colors-ovWAwnZI.mjs";
9
9
  import { n as escapeXmlAttr$1, r as escapeXmlText$1, t as escapeCellString } from "./escape-DFTE7ZJc.mjs";
10
- import { H as DRAWING_NS, I as CX_NS, T as CHART_NS, a as findChildren, bt as X14_NS, dt as SHEET_DRAWING_NS, ft as SHEET_MAIN_NS, i as findChild, jt as qname, lt as REL_NS, n as el, q as MARKUP_COMPAT_NS, w as CHART_DRAWING_NS } from "./tree-Bbs1C8Rc.mjs";
11
- import { t as parseXml } from "./parser-nk0Lqmcb.mjs";
12
- import { n as toTree, r as defineSchema, t as fromTree } from "./serialize-55EnT30e.mjs";
13
- import { t as serializeXml } from "./serializer-DQzTTNOS.mjs";
14
- import { Q as findById } from "./manifest-CVL1_t0O.mjs";
15
- import { $t as makeRowDimension, Cn as parseRange, Dn as rangeToString, Qt as makeColumnDimension, Sn as parseMultiCellRange, Xt as makeSheetView, Zt as makeHyperlink, at as makeWorksheet, bt as setCell, ln as makeColor, qt as makeLegacyComment, xn as multiCellRangeToString } from "./worksheet-C6CWgvP5.mjs";
16
- import { a as makeCfRule, i as makeDataValidation, n as makeTableColumn, o as makeConditionalFormatting, r as makeTableDefinition } from "./table-BifavDVW.mjs";
10
+ import { H as DRAWING_NS, I as CX_NS, T as CHART_NS, a as findChildren, bt as X14_NS, dt as SHEET_DRAWING_NS, ft as SHEET_MAIN_NS, i as findChild, jt as qname, lt as REL_NS, n as el, q as MARKUP_COMPAT_NS, w as CHART_DRAWING_NS } from "./tree-BX-sRRVp.mjs";
11
+ import { t as parseXml } from "./parser-D3SLaOFe.mjs";
12
+ import { n as toTree, r as defineSchema, t as fromTree } from "./serialize-BC2Wu3bR.mjs";
13
+ import { t as serializeXml } from "./serializer-BtO2TrI7.mjs";
14
+ import { Q as findById } from "./manifest-Cm9K7D62.mjs";
15
+ import { $t as makeRowDimension, Jt as makeLegacyComment, Qt as makeColumnDimension, Zt as makeSheetView, an as multiCellRangeToString, at as makeWorksheet, bt as setCell, dn as rangeToString, on as parseMultiCellRange, sn as parseRange, wn as makeColor } from "./worksheet-C5Ensf_j.mjs";
16
+ import { Jt as VERTICAL_ALIGNMENTS, Lt as PATTERN_TYPES, Mt as FONT_SCHEMES, Nt as UNDERLINE_STYLES, Pt as VERT_ALIGNS, Ut as SIDE_STYLES, qt as HORIZONTAL_ALIGNMENTS } from "./cell-style-DuvS_Lsg.mjs";
17
+ import { C as addSharedRichText, O as parseRichString, w as addSharedString } from "./workbook-CARZS94U.mjs";
18
+ import { a as makeCfRule, i as makeDataValidation, n as makeTableColumn, o as makeConditionalFormatting, r as makeTableDefinition } from "./table-1P8we2gS.mjs";
17
19
  import { i as durationToExcel, r as dateToExcel } from "./datetime-B2ySVlXt.mjs";
18
- import { C as addSharedString, O as serializeRichTextRuns } from "./workbook-T1Y3lstQ.mjs";
19
- import { Jt as VERTICAL_ALIGNMENTS, Lt as PATTERN_TYPES, Mt as FONT_SCHEMES, Nt as UNDERLINE_STYLES, Pt as VERT_ALIGNS, Ut as SIDE_STYLES, qt as HORIZONTAL_ALIGNMENTS } from "./cell-style-CN9JXOwm.mjs";
20
- import { n as getDxfs } from "./differential-YEjxNScf.mjs";
20
+ import { n as getDxfs } from "./differential-DvKU8fJt.mjs";
21
21
  //#region src/drawing/dml/effect.ts
22
22
  const PRESET_SHADOW_NAMES = [
23
23
  "shdw1",
@@ -4976,7 +4976,6 @@ const C_TAG = `{${SHEET_MAIN_NS}}c`;
4976
4976
  const V_TAG = `{${SHEET_MAIN_NS}}v`;
4977
4977
  const F_TAG = `{${SHEET_MAIN_NS}}f`;
4978
4978
  const IS_TAG = `{${SHEET_MAIN_NS}}is`;
4979
- const T_TAG$1 = `{${SHEET_MAIN_NS}}t`;
4980
4979
  const MERGE_CELLS_TAG = `{${SHEET_MAIN_NS}}mergeCells`;
4981
4980
  const MERGE_CELL_TAG = `{${SHEET_MAIN_NS}}mergeCell`;
4982
4981
  const SHEET_VIEWS_TAG$1 = `{${SHEET_MAIN_NS}}sheetViews`;
@@ -6081,7 +6080,7 @@ const readCell = (ws, cNode, coord, ctx, sharedFormulas) => {
6081
6080
  const isNode = findChild(cNode, IS_TAG);
6082
6081
  if (fNode) {
6083
6082
  const cell = setCell(ws, coord.row, coord.col, null, styleId);
6084
- const cachedRaw = vNode?.text;
6083
+ const cachedRaw = vNode === void 0 ? void 0 : vNode.text ?? "";
6085
6084
  const cached = decodeCachedValue(cachedRaw, t);
6086
6085
  handleFormula(cell, fNode, coord, cached, sharedFormulas);
6087
6086
  return;
@@ -6097,7 +6096,10 @@ const readCell = (ws, cNode, coord, ctx, sharedFormulas) => {
6097
6096
  if (!Number.isInteger(idx) || idx < 0) throw new OpenXmlSchemaError(`worksheet: <c t="s"><v>${vNode.text}</v> is not a valid index`);
6098
6097
  const sst = ctx.sharedStrings[idx];
6099
6098
  if (sst === void 0) throw new OpenXmlSchemaError(`worksheet: shared-string index ${idx} out of range [0, ${ctx.sharedStrings.length})`);
6100
- value = sst;
6099
+ value = typeof sst === "string" ? sst : {
6100
+ kind: "rich-text",
6101
+ runs: sst.runs
6102
+ };
6101
6103
  break;
6102
6104
  }
6103
6105
  case "b":
@@ -6127,23 +6129,25 @@ const parseStyleId = (raw) => {
6127
6129
  if (!Number.isInteger(n) || n < 0) throw new OpenXmlSchemaError(`worksheet: <c s="${raw}"> is not a non-negative integer`);
6128
6130
  return n;
6129
6131
  };
6130
- /** Inline-string body — concatenates `<is>/<t>` text runs. Rich runs lose formatting in stage-1. */
6132
+ /**
6133
+ * Inline-string body (`<c t="inlineStr"><is>…</is></c>`). `<is>` is a CT_Rst —
6134
+ * the same shape as an `<si>` in sharedStrings.xml — so it goes through the
6135
+ * same parser, and a body built from `<r>` runs keeps its per-run formatting
6136
+ * instead of collapsing to the concatenated text.
6137
+ */
6131
6138
  const readInlineString = (isNode) => {
6132
6139
  if (!isNode) return "";
6133
- const direct = findChild(isNode, T_TAG$1);
6134
- if (direct) return direct.text ?? "";
6135
- let out = "";
6136
- for (const child of isNode.children) {
6137
- const t = findChild(child, T_TAG$1);
6138
- if (t?.text) out += t.text;
6139
- }
6140
- return out;
6140
+ const entry = parseRichString(isNode);
6141
+ return typeof entry === "string" ? entry : {
6142
+ kind: "rich-text",
6143
+ runs: entry.runs
6144
+ };
6141
6145
  };
6142
6146
  const decodeCachedValue = (raw, t) => {
6143
- if (raw === void 0 || raw === "") return void 0;
6147
+ if (raw === void 0) return void 0;
6144
6148
  switch (t) {
6145
- case "n": return Number.parseFloat(raw);
6146
- case "b": return raw === "1";
6149
+ case "n": return raw === "" ? void 0 : Number.parseFloat(raw);
6150
+ case "b": return raw === "" ? void 0 : raw === "1";
6147
6151
  case "str": return raw;
6148
6152
  case "e": return raw;
6149
6153
  case "s": return raw;
@@ -6422,7 +6426,7 @@ const parseHyperlink = (node, rels) => {
6422
6426
  if (node.attrs["location"]) opts.location = node.attrs["location"];
6423
6427
  if (node.attrs["tooltip"]) opts.tooltip = node.attrs["tooltip"];
6424
6428
  if (node.attrs["display"]) opts.display = node.attrs["display"];
6425
- return makeHyperlink(opts);
6429
+ return opts;
6426
6430
  };
6427
6431
  const maybeRecordRowDimension = (ws, node, rowIdx) => {
6428
6432
  const opts = {};
@@ -6606,15 +6610,12 @@ const serializeCell = (cell, ctx) => {
6606
6610
  const ref = getCoordinate(cell);
6607
6611
  const styleAttr = cell.styleId === 0 ? "" : ` s="${cell.styleId}"`;
6608
6612
  const value = cell.value;
6609
- if (value === null) {
6610
- if (cell.styleId === 0) return "";
6611
- return `<c r="${ref}"${styleAttr}/>`;
6612
- }
6613
+ if (value === null) return `<c r="${ref}"${styleAttr}/>`;
6613
6614
  if (typeof value === "object" && value !== null && value.kind === "formula") return serializeFormulaCell(ref, styleAttr, value);
6614
6615
  if (typeof value === "object" && value !== null && value.kind === "error") return `<c r="${ref}"${styleAttr} t="e"><v>${value.code}</v></c>`;
6615
6616
  if (typeof value === "object" && value !== null && value.kind === "rich-text") {
6616
6617
  const runs = value.runs;
6617
- return `<c r="${ref}"${styleAttr} t="inlineStr"><is>${serializeRichTextRuns(runs)}</is></c>`;
6618
+ return `<c r="${ref}"${styleAttr} t="s"><v>${addSharedRichText(ctx.sharedStrings, runs)}</v></c>`;
6618
6619
  }
6619
6620
  if (typeof value === "number") {
6620
6621
  if (!Number.isFinite(value)) throw new OpenXmlSchemaError(`worksheet: cannot serialise non-finite number at ${ref}`);
@@ -6661,7 +6662,8 @@ const serializeFormulaCell = (ref, styleAttr, f) => {
6661
6662
  vEl = `<v>${cached ? "1" : "0"}</v>`;
6662
6663
  } else {
6663
6664
  valueAttr = " t=\"str\"";
6664
- vEl = `<v>${escapeXmlText(escapeCellString(cached))}</v>`;
6665
+ const text = escapeXmlText(escapeCellString(cached));
6666
+ vEl = text.length > 0 ? `<v>${text}</v>` : "<v/>";
6665
6667
  }
6666
6668
  return `<c r="${ref}"${styleAttr}${valueAttr}>${fEl}${vEl}</c>`;
6667
6669
  };
@@ -7770,7 +7772,8 @@ const parseAnchor = (node) => {
7770
7772
  content: {
7771
7773
  kind: "unsupported",
7772
7774
  rawTag: child.name
7773
- }
7775
+ },
7776
+ raw: node
7774
7777
  };
7775
7778
  }
7776
7779
  return {
@@ -7778,7 +7781,8 @@ const parseAnchor = (node) => {
7778
7781
  content: {
7779
7782
  kind: "unsupported",
7780
7783
  rawTag: ""
7781
- }
7784
+ },
7785
+ raw: node
7782
7786
  };
7783
7787
  };
7784
7788
  /** Parse a `xl/drawings/drawingN.xml` payload into a Drawing object. */
@@ -7786,12 +7790,46 @@ function parseDrawingXml(bytes) {
7786
7790
  const root = parseXml(bytes);
7787
7791
  if (root.name !== WS_DRAWING_TAG) throw new OpenXmlSchemaError(`parseDrawingXml: root is "${root.name}", expected wsDr`);
7788
7792
  const items = [];
7793
+ const rawChildren = [];
7789
7794
  for (const child of root.children) {
7790
- if (child.name !== ABSOLUTE_ANCHOR_TAG && child.name !== ONE_CELL_ANCHOR_TAG && child.name !== TWO_CELL_ANCHOR_TAG) continue;
7791
- const item = parseAnchor(child);
7792
- if (item) items.push(item);
7795
+ if (child.name === ABSOLUTE_ANCHOR_TAG || child.name === ONE_CELL_ANCHOR_TAG || child.name === TWO_CELL_ANCHOR_TAG) {
7796
+ const item = parseAnchor(child);
7797
+ if (item) items.push(item);
7798
+ continue;
7799
+ }
7800
+ rawChildren.push({
7801
+ beforeItem: items.length,
7802
+ node: child
7803
+ });
7793
7804
  }
7794
- return makeDrawing(items);
7805
+ const drawing = makeDrawing(items);
7806
+ if (rawChildren.length > 0) drawing.rawChildren = rawChildren;
7807
+ return drawing;
7808
+ }
7809
+ const WS_DR_PREFIXES = {
7810
+ [SHEET_DRAWING_NS]: "xdr",
7811
+ [DRAWING_NS]: "a"
7812
+ };
7813
+ /** Re-emit a captured node inline, verbatim. */
7814
+ const serializeRawNode = (node) => new TextDecoder().decode(serializeXml(node, {
7815
+ xmlDeclaration: false,
7816
+ inScopePrefixes: WS_DR_PREFIXES
7817
+ }));
7818
+ /**
7819
+ * Every relationship id referenced inside the drawing's verbatim nodes —
7820
+ * `r:embed` on a picture nested in a group, `r:id` on a shape's hyperlink, and
7821
+ * whatever else sits under an `<mc:AlternateContent>` wrapper. The loader uses
7822
+ * this to carry the matching rels (and their target parts) across a re-save.
7823
+ */
7824
+ function collectRawRelIds(drawing) {
7825
+ const ids = /* @__PURE__ */ new Set();
7826
+ const walk = (node) => {
7827
+ for (const [name, value] of Object.entries(node.attrs)) if (name.startsWith(`{${REL_NS}}`) && value.length > 0) ids.add(value);
7828
+ for (const child of node.children) walk(child);
7829
+ };
7830
+ for (const item of drawing.items) if (item.raw) walk(item.raw);
7831
+ for (const rc of drawing.rawChildren ?? []) walk(rc.node);
7832
+ return ids;
7795
7833
  }
7796
7834
  const serializeMarker = (tag, m) => `<xdr:${tag}><xdr:col>${m.col}</xdr:col><xdr:colOff>${m.colOff}</xdr:colOff><xdr:row>${m.row}</xdr:row><xdr:rowOff>${m.rowOff}</xdr:rowOff></xdr:${tag}>`;
7797
7835
  const serializePictureFrame = (picture, anchorIdx) => {
@@ -7831,6 +7869,7 @@ const serializeChartGraphicFrame = (chart, anchorIdx, ext) => {
7831
7869
  ].join("");
7832
7870
  };
7833
7871
  const serializeAnchor = (item, idx) => {
7872
+ if (item.raw) return serializeRawNode(item.raw);
7834
7873
  const a = item.anchor;
7835
7874
  let body = "";
7836
7875
  let chartExt = {
@@ -7858,10 +7897,16 @@ function drawingToBytes(drawing) {
7858
7897
  }
7859
7898
  function serializeDrawing(drawing) {
7860
7899
  const parts = [XML_HEADER$2, `<xdr:wsDr xmlns:xdr="${SHEET_DRAWING_NS}" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">`];
7900
+ const rawChildren = drawing.rawChildren ?? [];
7901
+ const emitRawBefore = (index) => {
7902
+ for (const rc of rawChildren) if (rc.beforeItem === index) parts.push(serializeRawNode(rc.node));
7903
+ };
7861
7904
  for (let i = 0; i < drawing.items.length; i++) {
7905
+ emitRawBefore(i);
7862
7906
  const item = drawing.items[i];
7863
7907
  if (item) parts.push(serializeAnchor(item, i));
7864
7908
  }
7909
+ emitRawBefore(drawing.items.length);
7865
7910
  parts.push("</xdr:wsDr>");
7866
7911
  return parts.join("");
7867
7912
  }
@@ -8623,7 +8668,7 @@ function buildStylesheetTree(ss) {
8623
8668
  root.children.push(cellStylesEl);
8624
8669
  }
8625
8670
  const dxfs = getDxfs(ss);
8626
- if (dxfs.length > 0) {
8671
+ {
8627
8672
  const dxfsEl = el(DXFS_TAG, { count: String(dxfs.length) });
8628
8673
  for (const dxf of dxfs) {
8629
8674
  const dxfEl = el(DXF_TAG);
@@ -8637,6 +8682,7 @@ function buildStylesheetTree(ss) {
8637
8682
  }
8638
8683
  root.children.push(dxfsEl);
8639
8684
  }
8685
+ if (ss.stylesXmlTail) for (const node of ss.stylesXmlTail) root.children.push(node);
8640
8686
  return root;
8641
8687
  }
8642
8688
  const cellXfToTree = (xf) => {
@@ -8661,6 +8707,6 @@ const cellXfToTree = (xf) => {
8661
8707
  return node;
8662
8708
  };
8663
8709
  //#endregion
8664
- export { isChartExBytes as C, parseChartXml as D, findUserShapesRId as E, chartExToBytes as S, chartToBytes as T, serializeCell as _, parseCommentsXml as a, parseUserShapesXml as b, NumberFormatSchema as c, BorderSchema as d, AlignmentSchema as f, parseChartsheetXml as g, chartsheetToBytes as h, commentsToBytes as i, FontSchema as l, parseDrawingXml as m, parseTableXml as n, placeholderVmlDrawing as o, drawingToBytes as p, tableToBytes as r, ProtectionSchema as s, stylesheetToBytes as t, fillFromTree as u, worksheetToBytes as v, parseChartExXml as w, userShapesToBytes as x, parseWorksheetXml as y };
8710
+ export { chartExToBytes as C, findUserShapesRId as D, chartToBytes as E, parseChartXml as O, userShapesToBytes as S, parseChartExXml as T, parseChartsheetXml as _, parseCommentsXml as a, parseWorksheetXml as b, NumberFormatSchema as c, BorderSchema as d, AlignmentSchema as f, chartsheetToBytes as g, parseDrawingXml as h, commentsToBytes as i, FontSchema as l, drawingToBytes as m, parseTableXml as n, placeholderVmlDrawing as o, collectRawRelIds as p, tableToBytes as r, ProtectionSchema as s, stylesheetToBytes as t, fillFromTree as u, serializeCell as v, isChartExBytes as w, parseUserShapesXml as x, worksheetToBytes as y };
8665
8711
 
8666
- //# sourceMappingURL=stylesheet-writer-DOGKBGEk.mjs.map
8712
+ //# sourceMappingURL=stylesheet-writer-BrOw4jMY.mjs.map