{"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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"}