@office-kit/xlsx 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/io/save.d.ts +15 -3
- package/dist/io.mjs +1 -1
- package/dist/node.mjs +1 -1
- package/dist/{save-a0InIbtJ.mjs → save-CjarR7Rp.mjs} +4 -4
- package/dist/save-CjarR7Rp.mjs.map +1 -0
- package/dist/streaming/write-only.d.ts +11 -1
- package/dist/streaming.mjs +5 -5
- package/dist/streaming.mjs.map +1 -1
- package/dist/{writer-IyFccCRJ.mjs → writer-Z7cF_CBk.mjs} +60 -8
- package/dist/writer-Z7cF_CBk.mjs.map +1 -0
- package/dist/zip/index.d.ts +1 -1
- package/dist/zip/writer.d.ts +20 -1
- package/dist/zip.mjs +1 -1
- package/package.json +1 -1
- package/dist/save-a0InIbtJ.mjs.map +0 -1
- package/dist/writer-IyFccCRJ.mjs.map +0 -1
package/dist/io/save.d.ts
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import type { XlsxSink } from '../io/sink.js';
|
|
2
2
|
import { type Workbook } from '../workbook/workbook.js';
|
|
3
|
+
import { type CompressionLevel } from '../zip/writer.js';
|
|
3
4
|
export interface SaveOptions {
|
|
4
|
-
/**
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Last-modified timestamp for every ZIP entry. Left unset, fflate stamps the
|
|
7
|
+
* wall clock into each local header and central-directory record, so saving
|
|
8
|
+
* the same workbook twice produces different bytes. Pin it and the output is
|
|
9
|
+
* byte-identical for identical input on any machine, which is what
|
|
10
|
+
* golden-file tests and content-addressed caches need.
|
|
11
|
+
*
|
|
12
|
+
* Recorded as the date's UTC wall time, to ZIP's two-second resolution; the
|
|
13
|
+
* year has to fall in 1980-2099, the range supported by the ZIP backend.
|
|
14
|
+
*/
|
|
15
|
+
mtime?: Date;
|
|
16
|
+
/** Deflate level, 0 (no compression) to 9 (smallest). Defaults to fflate's own 6. */
|
|
17
|
+
compressionLevel?: CompressionLevel;
|
|
6
18
|
}
|
|
7
19
|
/** Convenience: serialise a Workbook to an in-memory `Uint8Array` xlsx.
|
|
8
20
|
*
|
|
@@ -11,4 +23,4 @@ export interface SaveOptions {
|
|
|
11
23
|
*/
|
|
12
24
|
export declare function workbookToBytes(wb: Workbook, opts?: SaveOptions): Promise<Uint8Array>;
|
|
13
25
|
/** Save a workbook through the given sink. Returns once `finalize()` resolves. */
|
|
14
|
-
export declare function saveWorkbook(wb: Workbook, sink: XlsxSink,
|
|
26
|
+
export declare function saveWorkbook(wb: Workbook, sink: XlsxSink, opts?: SaveOptions): Promise<void>;
|
package/dist/io.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as OpenXmlIoError } from "./exceptions-D-CFwxgm.mjs";
|
|
2
2
|
import { t as loadWorkbook } from "./load-CJdGh50R.mjs";
|
|
3
|
-
import { n as workbookToBytes, t as saveWorkbook } from "./save-
|
|
3
|
+
import { n as workbookToBytes, t as saveWorkbook } from "./save-CjarR7Rp.mjs";
|
|
4
4
|
//#region src/io/browser.ts
|
|
5
5
|
/**
|
|
6
6
|
* Wrap a Blob (or File, since File extends Blob) as an XlsxSource. The
|
package/dist/node.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as OpenXmlIoError } from "./exceptions-D-CFwxgm.mjs";
|
|
2
|
-
import { t as saveWorkbook } from "./save-
|
|
2
|
+
import { t as saveWorkbook } from "./save-CjarR7Rp.mjs";
|
|
3
3
|
import { createReadStream, createWriteStream, readFileSync } from "node:fs";
|
|
4
4
|
import { readFile, unlink } from "node:fs/promises";
|
|
5
5
|
import { once } from "node:events";
|
|
@@ -7,7 +7,7 @@ import { C as chartExToBytes, E as chartToBytes, S as userShapesToBytes, g as ch
|
|
|
7
7
|
import { t as serializeXml } from "./serializer-C6nm0mRJ.mjs";
|
|
8
8
|
import { V as corePropsToBytes, a as makeManifest, l as extendedPropsToBytes, n as addOverride, rt as relsToBytes, s as manifestToBytes, t as addDefault, tt as makeRelationships, v as customPropsToBytes } from "./manifest-pLmx7KYh.mjs";
|
|
9
9
|
import { D as makeSharedStrings, S as validateSheetTitle, j as sharedStringsToBytes } from "./workbook-B15-T4cs.mjs";
|
|
10
|
-
import { t as createZipWriter } from "./writer-
|
|
10
|
+
import { t as createZipWriter } from "./writer-Z7cF_CBk.mjs";
|
|
11
11
|
//#region src/io/save.ts
|
|
12
12
|
const CORE_PROPS_TYPE = "application/vnd.openxmlformats-package.core-properties+xml";
|
|
13
13
|
const EXT_PROPS_TYPE = "application/vnd.openxmlformats-officedocument.extended-properties+xml";
|
|
@@ -105,9 +105,9 @@ const validateSheetTitles = (wb) => {
|
|
|
105
105
|
}
|
|
106
106
|
};
|
|
107
107
|
/** Save a workbook through the given sink. Returns once `finalize()` resolves. */
|
|
108
|
-
async function saveWorkbook(wb, sink,
|
|
108
|
+
async function saveWorkbook(wb, sink, opts = {}) {
|
|
109
109
|
validateSheetTitles(wb);
|
|
110
|
-
const writer = createZipWriter(sink);
|
|
110
|
+
const writer = createZipWriter(sink, opts);
|
|
111
111
|
try {
|
|
112
112
|
await saveWorkbookImpl(wb, writer);
|
|
113
113
|
} catch (err) {
|
|
@@ -792,4 +792,4 @@ function serializeChildNode(node, inScopePrefixes) {
|
|
|
792
792
|
//#endregion
|
|
793
793
|
export { workbookToBytes as n, saveWorkbook as t };
|
|
794
794
|
|
|
795
|
-
//# sourceMappingURL=save-
|
|
795
|
+
//# sourceMappingURL=save-CjarR7Rp.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"save-CjarR7Rp.mjs","names":["serializeXmlNode"],"sources":["../src/io/save.ts"],"sourcesContent":["// Public `saveWorkbook` entry point.\n//\n// **Stage 1 minimum**: emits the bare set of parts a Workbook needs to\n// round-trip through `loadWorkbook`:\n//\n// [Content_Types].xml\n// _rels/.rels\n// xl/workbook.xml\n// xl/_rels/workbook.xml.rels\n// xl/worksheets/sheetN.xml ...\n// xl/styles.xml\n// xl/sharedStrings.xml (only when sst is non-empty)\n//\n// docProps / theme / VBA / drawings / charts are reserved for later iterations\n// — load tolerates their absence.\n\nimport { escapeXmlAttr, escapeXmlText } from '../utils/escape.js';\nimport { chartToBytes } from '../chart/chart-xml.js';\nimport { chartExToBytes } from '../chart/cx/chartex-xml.js';\nimport { userShapesToBytes } from '../chart/user-shapes-xml.js';\nimport { chartsheetToBytes } from '../chartsheet/chartsheet-xml.js';\nimport type { Drawing, DrawingItem } from '../drawing/drawing.js';\nimport { drawingToBytes } from '../drawing/drawing-xml.js';\nimport { IMAGE_FORMAT_EXTENSION, IMAGE_FORMAT_MIME, type XlsxImageFormat } from '../drawing/image.js';\nimport type { XlsxSink } from '../io/sink.js';\nimport { OpenXmlIoError, OpenXmlSchemaError } from '../utils/exceptions.js';\nimport { normalizeFormulaText } from '../utils/formula-text.js';\nimport { corePropsToBytes } from '../packaging/core.js';\nimport { customPropsToBytes } from '../packaging/custom.js';\nimport { extendedPropsToBytes } from '../packaging/extended.js';\nimport { addDefault, addOverride, makeManifest, manifestToBytes } from '../packaging/manifest.js';\nimport { makeRelationships, type Relationships, relsToBytes } from '../packaging/relationships.js';\nimport { stylesheetToBytes } from '../styles/stylesheet-writer.js';\nimport { makeSharedStrings, sharedStringsToBytes } from '../workbook/shared-strings.js';\nimport { type Workbook, validateSheetTitle } from '../workbook/workbook.js';\nimport type { LegacyComment } from '../worksheet/comments.js';\nimport { commentsToBytes, placeholderVmlDrawing } from '../worksheet/comments-xml.js';\nimport type { TableDefinition } from '../worksheet/table.js';\nimport { tableToBytes } from '../worksheet/table-xml.js';\nimport { worksheetToBytes } from '../worksheet/writer.js';\nimport { serializeXml as serializeXmlNode } from '../xml/serializer.js';\nimport {\n ARC_APP,\n ARC_CONTENT_TYPES,\n ARC_CORE,\n ARC_CUSTOM,\n ARC_ROOT_RELS,\n ARC_SHARED_STRINGS,\n ARC_STYLE,\n ARC_THEME,\n ARC_WORKBOOK,\n ARC_WORKBOOK_RELS,\n CHARTEX_TYPE,\n CPROPS_TYPE,\n parseQName,\n PKG_REL_NS,\n REL_NS,\n SHARED_STRINGS_TYPE,\n SHEET_MAIN_NS,\n STYLES_TYPE,\n THEME_TYPE,\n WORKSHEET_TYPE,\n XLSX_TYPE,\n} from '../xml/namespaces.js';\nimport { type CompressionLevel, createZipWriter } from '../zip/writer.js';\n\nconst CORE_PROPS_TYPE = 'application/vnd.openxmlformats-package.core-properties+xml';\nconst EXT_PROPS_TYPE = 'application/vnd.openxmlformats-officedocument.extended-properties+xml';\nconst CORE_PROPS_REL = `${PKG_REL_NS}/metadata/core-properties`;\nconst EXT_PROPS_REL = `${REL_NS}/extended-properties`;\nconst CUSTOM_PROPS_REL = `${REL_NS}/custom-properties`;\nconst THEME_REL = `${REL_NS}/theme`;\nconst TABLE_REL = `${REL_NS}/table`;\nconst TABLE_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml';\nconst COMMENTS_REL = `${REL_NS}/comments`;\nconst VML_DRAWING_REL = `${REL_NS}/vmlDrawing`;\nconst COMMENTS_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml';\nconst VML_DRAWING_TYPE = 'application/vnd.openxmlformats-officedocument.vmlDrawing';\nconst DRAWING_REL = `${REL_NS}/drawing`;\nconst DRAWING_TYPE = 'application/vnd.openxmlformats-officedocument.drawing+xml';\nconst CHART_REL = `${REL_NS}/chart`;\nconst CHARTEX_REL = 'http://schemas.microsoft.com/office/2014/relationships/chartEx';\nconst CHART_TYPE = 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml';\nconst IMAGE_REL = `${REL_NS}/image`;\nconst CHARTSHEET_REL = `${REL_NS}/chartsheet`;\nconst CHARTSHEET_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml';\nconst CHART_USER_SHAPES_REL = `${REL_NS}/chartUserShapes`;\n\nexport interface SaveOptions {\n /**\n * Last-modified timestamp for every ZIP entry. Left unset, fflate stamps the\n * wall clock into each local header and central-directory record, so saving\n * the same workbook twice produces different bytes. Pin it and the output is\n * byte-identical for identical input on any machine, which is what\n * golden-file tests and content-addressed caches need.\n *\n * Recorded as the date's UTC wall time, to ZIP's two-second resolution; the\n * year has to fall in 1980-2099, the range supported by the ZIP backend.\n */\n mtime?: Date;\n /** Deflate level, 0 (no compression) to 9 (smallest). Defaults to fflate's own 6. */\n compressionLevel?: CompressionLevel;\n}\n\n/** Convenience: serialise a Workbook to an in-memory `Uint8Array` xlsx.\n *\n * Browser-safe: this path uses an in-memory `Uint8Array` sink and never\n * touches Node's `Buffer` global, so it works unchanged in browser bundles.\n */\nexport async function workbookToBytes(wb: Workbook, opts: SaveOptions = {}): Promise<Uint8Array> {\n const sink = toUint8ArraySink();\n await saveWorkbook(wb, sink, opts);\n return sink.result();\n}\n\nconst toUint8ArraySink = (): XlsxSink & { result(): Uint8Array } => {\n const chunks: Uint8Array[] = [];\n let finalised: Uint8Array | undefined;\n const finalise = (): Uint8Array => {\n if (finalised !== undefined) return finalised;\n let total = 0;\n for (const c of chunks) total += c.byteLength;\n const out = new Uint8Array(total);\n let off = 0;\n for (const c of chunks) {\n out.set(c, off);\n off += c.byteLength;\n }\n finalised = out;\n chunks.length = 0;\n return out;\n };\n return {\n toBytes() {\n return {\n write(chunk: Uint8Array): void {\n if (finalised !== undefined) {\n throw new OpenXmlIoError('workbookToBytes sink: write after finish');\n }\n if (!(chunk instanceof Uint8Array)) {\n throw new OpenXmlIoError('workbookToBytes sink: chunk is not a Uint8Array');\n }\n chunks.push(chunk);\n },\n async finish(): Promise<Uint8Array> {\n return finalise();\n },\n abort(): void {\n if (finalised !== undefined) return;\n finalised = new Uint8Array(0);\n chunks.length = 0;\n },\n };\n },\n result(): Uint8Array {\n return finalise();\n },\n };\n};\n\n/**\n * Validate every sheet title against Excel's character + length rules and\n * confirm titles are unique within the workbook. Catches bad state introduced\n * via direct mutation (`ws.title = ...`) that bypassed the public mutators.\n *\n * Without this gate, a workbook with an invalid sheet name would round-trip to\n * an xlsx Excel rejects with a generic \"file is corrupt\" dialog on open —\n * making it hard to diagnose. Raising at save time keeps the bad state\n * recoverable: callers can `renameSheet` and retry.\n */\nconst validateSheetTitles = (wb: Workbook): void => {\n const seen = new Map<string, number>();\n for (let i = 0; i < wb.sheets.length; i++) {\n const ref = wb.sheets[i];\n if (!ref) continue;\n const title = ref.sheet.title;\n const reason = validateSheetTitle(title);\n if (reason) {\n throw new OpenXmlSchemaError(`saveWorkbook: sheet[${i}] title \"${title}\": ${reason}`);\n }\n const lower = title.toLowerCase();\n const prior = seen.get(lower);\n if (prior !== undefined) {\n throw new OpenXmlSchemaError(\n `saveWorkbook: sheet[${i}] title \"${title}\" collides with sheet[${prior}]` +\n ` (Excel treats sheet names case-insensitively for uniqueness).`,\n );\n }\n seen.set(lower, i);\n }\n};\n\n/** Save a workbook through the given sink. Returns once `finalize()` resolves. */\nexport async function saveWorkbook(wb: Workbook, sink: XlsxSink, opts: SaveOptions = {}): Promise<void> {\n validateSheetTitles(wb);\n const writer = createZipWriter(sink, opts);\n try {\n await saveWorkbookImpl(wb, writer);\n } catch (err) {\n // Release the sink so streaming destinations (`toFile` / `toWritable`)\n // don't leave a half-written file looking valid. abort() is idempotent so\n // a successful finalize() above is a no-op here.\n writer.abort(err);\n throw err;\n }\n}\n\nasync function saveWorkbookImpl(wb: Workbook, writer: ReturnType<typeof createZipWriter>): Promise<void> {\n // ---- 1. assemble the per-sheet rels + serialise each worksheet ----------\n const sst = makeSharedStrings();\n interface SheetEmit {\n id: string;\n target: string;\n bytes: Uint8Array;\n /** OOXML relationship type used in workbook.xml.rels. */\n relType: string;\n /** ZIP archive path the bytes are written to. */\n archivePath: string;\n /** Override content type for [Content_Types].xml. */\n contentType: string;\n /** Per-sheet rels — populated only if the sheet has hyperlinks / drawings / etc. */\n rels?: Relationships;\n }\n const sheetEmits: SheetEmit[] = [];\n // Workbook-global table counter so xl/tables/tableN.xml ids stay unique.\n const tableEmits: Array<{ id: number; bytes: Uint8Array }> = [];\n let nextTableId = 1;\n // Same counter pattern for comments parts. The comments part and the VML\n // drawing share their N because Excel always emits them paired.\n interface CommentEmit {\n id: number;\n commentsBytes: Uint8Array;\n vmlBytes: Uint8Array;\n }\n const commentEmits: CommentEmit[] = [];\n // Passthrough carries form-control / header-footer VML under the same\n // xl/drawings/vmlDrawingN.vml naming, so start past whatever is already\n // taken — the zip writer rejects duplicate entries.\n let nextCommentsId = nextFreeIndex(wb, /^xl\\/drawings\\/vmlDrawing(\\d+)\\.vml$/);\n // Drawings: workbook-global drawingN counter for xl/drawings/drawingN.xml +\n // matching drawing-rels file (when chart items are present).\n interface DrawingEmit {\n id: number;\n bytes: Uint8Array;\n /** Drawing rels — when non-empty we also emit the rels file. */\n rels?: Relationships;\n }\n const drawingEmits: DrawingEmit[] = [];\n let nextDrawingId = 1;\n // Chart parts share a workbook-global counter so xl/charts/chartN.xml ids\n // stay unique.\n const chartEmits: Array<{\n id: number;\n bytes: Uint8Array;\n isCx: boolean;\n /** Per-chart rels file (only emitted when chart.userShapes is set). */\n rels?: Relationships;\n }> = [];\n let nextChartId = 1;\n // Workbook-global counter for chartDrawing parts. Each chart with userShapes\n // set produces one xl/drawings/chartDrawingN.xml entry.\n const userShapeEmits: Array<{ id: number; bytes: Uint8Array }> = [];\n let nextUserShapesId = 1;\n // Workbook-global counter for image media parts. Excel uses\n // xl/media/imageN.{ext} where N is shared across the package.\n interface ImageEmit {\n id: number;\n /** File extension (without the dot) — also drives the manifest Default. */\n ext: string;\n bytes: Uint8Array;\n }\n const imageEmits: ImageEmit[] = [];\n const imageExts = new Set<string>();\n // Same collision guard as for VML: images referenced by passthrough parts\n // keep their original xl/media/imageN.* names.\n let nextImageId = nextFreeIndex(wb, /^xl\\/media\\/image(\\d+)\\./);\n // Track separate counters so worksheet / chartsheet IDs do not collide (Excel\n // uses xl/worksheets/sheetN.xml and xl/chartsheets/sheetM.xml independently\n // of one another).\n let nextWorksheetId = 1;\n let nextChartsheetId = 1;\n // Pre-claim every original rId so freshly allocated ones (sheets without a\n // captured rId, plus modeled non-sheet rels) avoid collision with captured\n // workbookRelsExtras and the modeled non-sheet original ids.\n const claimedRIds = new Set<string>();\n for (const ref of wb.sheets) {\n if (ref.rId !== undefined) claimedRIds.add(ref.rId);\n }\n if (wb.workbookRelOriginalIds) {\n for (const v of Object.values(wb.workbookRelOriginalIds)) {\n if (typeof v === 'string') claimedRIds.add(v);\n }\n }\n if (wb.workbookRelsExtras) {\n for (const e of wb.workbookRelsExtras) claimedRIds.add(e.id);\n }\n let nextRIdCursor = 1;\n const allocateRId = (): string => {\n let id = `rId${nextRIdCursor}`;\n while (claimedRIds.has(id)) {\n nextRIdCursor++;\n id = `rId${nextRIdCursor}`;\n }\n claimedRIds.add(id);\n nextRIdCursor++;\n return id;\n };\n wb.sheets.forEach((ref, _i) => {\n const isChartsheet = ref.kind === 'chartsheet';\n const target = isChartsheet ? `chartsheets/sheet${nextChartsheetId}.xml` : `worksheets/sheet${nextWorksheetId}.xml`;\n const archivePath = isChartsheet\n ? `xl/chartsheets/sheet${nextChartsheetId}.xml`\n : `xl/worksheets/sheet${nextWorksheetId}.xml`;\n if (isChartsheet) nextChartsheetId++;\n else nextWorksheetId++;\n const sheetRels = makeRelationships();\n // Pre-claim every captured relsExtras rId so freshly allocated modeled rels\n // never clash. The extras themselves are appended to sheetRels at the end\n // so any rId already used inside the worksheet body (drawing / hyperlink\n // refs) keeps pointing at our newly emitted rel.\n const sheetRelsClaimed = new Set<string>();\n const sheetRelsExtras = ref.kind === 'worksheet' ? (ref.sheet.relsExtras ?? []) : [];\n for (const e of sheetRelsExtras) sheetRelsClaimed.add(e.id);\n let sheetRIdCursor = 1;\n const allocateSheetRId = (): string => {\n let id = `rId${sheetRIdCursor}`;\n while (sheetRelsClaimed.has(id) || sheetRels.rels.some((r) => r.id === id)) {\n sheetRIdCursor++;\n id = `rId${sheetRIdCursor}`;\n }\n sheetRIdCursor++;\n return id;\n };\n const registerTable = (table: TableDefinition): { rId: string } => {\n const tableId = nextTableId++;\n const rId = allocateSheetRId();\n sheetRels.rels.push({\n id: rId,\n type: TABLE_REL,\n target: `../tables/table${tableId}.xml`,\n });\n // Emit the table part with its workbook-global id baked in.\n const xmlTable: TableDefinition = { ...table, id: tableId };\n tableEmits.push({ id: tableId, bytes: tableToBytes(xmlTable) });\n return { rId };\n };\n const registerComments = (comments: ReadonlyArray<LegacyComment>): { vmlRelId: string } => {\n const id = nextCommentsId++;\n const commentsRelId = allocateSheetRId();\n sheetRels.rels.push({\n id: commentsRelId,\n type: COMMENTS_REL,\n target: `../comments${id}.xml`,\n });\n const vmlRelId = allocateSheetRId();\n sheetRels.rels.push({\n id: vmlRelId,\n type: VML_DRAWING_REL,\n target: `../drawings/vmlDrawing${id}.vml`,\n });\n commentEmits.push({\n id,\n commentsBytes: commentsToBytes(comments),\n vmlBytes: placeholderVmlDrawing(comments),\n });\n return { vmlRelId };\n };\n const registerDrawing = (drawing: Drawing): { rId: string } => {\n const id = nextDrawingId++;\n const rId = allocateSheetRId();\n sheetRels.rels.push({\n id: rId,\n type: DRAWING_REL,\n target: `../drawings/drawing${id}.xml`,\n });\n // Walk drawing items: for each chart with a payload, allocate a\n // workbook-global chartN id and a per-drawing rId. Emit the chart part +\n // collect a drawing-rels entry so drawing.xml's <c:chart r:id> resolves.\n const drawingRels = makeRelationships();\n // Rels the drawing's verbatim nodes reference keep their original ids —\n // those ids are baked into XML we re-emit untouched — so the allocator\n // below has to step over them.\n const usedDrawingRIds = new Set<string>();\n for (const rel of drawing.rawRels?.rels ?? []) {\n drawingRels.rels.push(rel);\n usedDrawingRIds.add(rel.id);\n }\n let nextDrawingRId = 1;\n const allocateDrawingRId = (): string => {\n let candidate = `rId${nextDrawingRId++}`;\n while (usedDrawingRIds.has(candidate)) candidate = `rId${nextDrawingRId++}`;\n usedDrawingRIds.add(candidate);\n return candidate;\n };\n const itemsForXml: DrawingItem[] = [];\n for (const item of drawing.items) {\n if (item.content.kind === 'chart' && (item.content.chart.space || item.content.chart.cxSpace)) {\n const chartId = nextChartId++;\n const chartRId = allocateDrawingRId();\n // chartex parts use a different relationship Type than the legacy\n // ECMA-376 charts. Excel rejects the workbook when the drawing rels\n // claim a `relationships/chart` target that actually contains a\n // `cx:chartSpace` root.\n const isCxChart = item.content.chart.cxSpace !== undefined;\n drawingRels.rels.push({\n id: chartRId,\n type: isCxChart ? CHARTEX_REL : CHART_REL,\n target: `../charts/chart${chartId}.xml`,\n });\n if (item.content.chart.cxSpace) {\n chartEmits.push({\n id: chartId,\n bytes: chartExToBytes(item.content.chart.cxSpace),\n isCx: true,\n });\n } else if (item.content.chart.space) {\n const space = item.content.chart.space;\n // If the chart carries user shapes, allocate the chartDrawingN part\n // + a per-chart rels file referencing it, then bake the resulting\n // r:id into the chart's <c:userShapes> element.\n let userShapesRId: string | undefined;\n let chartRelsFile: Relationships | undefined;\n if (space.userShapes && space.userShapes.shapes.length > 0) {\n const userShapesId = nextUserShapesId++;\n userShapesRId = 'rId1';\n chartRelsFile = makeRelationships();\n chartRelsFile.rels.push({\n id: userShapesRId,\n type: CHART_USER_SHAPES_REL,\n target: `../drawings/chartDrawing${userShapesId}.xml`,\n });\n userShapeEmits.push({\n id: userShapesId,\n bytes: userShapesToBytes(space.userShapes),\n });\n }\n const chartEmit: typeof chartEmits[number] = {\n id: chartId,\n bytes: chartToBytes(space, userShapesRId !== undefined ? { userShapesRId } : {}),\n isCx: false,\n };\n if (chartRelsFile) chartEmit.rels = chartRelsFile;\n chartEmits.push(chartEmit);\n }\n itemsForXml.push({\n anchor: item.anchor,\n content: {\n kind: 'chart',\n chart: {\n rId: chartRId,\n ...(item.content.chart.cxSpace ? { isCx: true } : {}),\n },\n },\n });\n } else if (item.content.kind === 'picture' && item.content.picture.image) {\n const img = item.content.picture.image;\n const ext = IMAGE_FORMAT_EXTENSION[img.format];\n const imageId = nextImageId++;\n const picRId = allocateDrawingRId();\n drawingRels.rels.push({\n id: picRId,\n type: IMAGE_REL,\n target: `../media/image${imageId}.${ext}`,\n });\n imageEmits.push({ id: imageId, ext, bytes: img.bytes });\n imageExts.add(ext);\n itemsForXml.push({\n anchor: item.anchor,\n content: {\n kind: 'picture',\n picture: {\n rId: picRId,\n ...(item.content.picture.name !== undefined ? { name: item.content.picture.name } : {}),\n ...(item.content.picture.descr !== undefined ? { descr: item.content.picture.descr } : {}),\n ...(item.content.picture.hidden !== undefined ? { hidden: item.content.picture.hidden } : {}),\n ...(item.content.picture.spPr ? { spPr: item.content.picture.spPr } : {}),\n },\n },\n });\n } else {\n itemsForXml.push(item);\n }\n }\n const emit: DrawingEmit = {\n id,\n bytes: drawingToBytes({\n items: itemsForXml,\n ...(drawing.rawChildren ? { rawChildren: drawing.rawChildren } : {}),\n }),\n };\n if (drawingRels.rels.length > 0) emit.rels = drawingRels;\n drawingEmits.push(emit);\n return { rId };\n };\n let bytes: Uint8Array;\n if (ref.kind === 'worksheet') {\n bytes = worksheetToBytes(ref.sheet, {\n sharedStrings: sst,\n styles: wb.styles,\n date1904: wb.date1904,\n rels: sheetRels,\n registerTable,\n registerComments,\n registerDrawing,\n });\n } else {\n // Chartsheet: register the drawing (if any), then emit the chartsheet\n // part with the resulting r:id baked in.\n let drawingRId: string | undefined;\n if (ref.sheet.drawing) drawingRId = registerDrawing(ref.sheet.drawing).rId;\n bytes = chartsheetToBytes(ref.sheet, drawingRId !== undefined ? { drawingRId } : {});\n }\n // Append captured per-sheet rels passthrough (pivotTable / queryTable /\n // printerSettings / oleObject / customProperty / threadedComment …)\n // verbatim. Their original rIds were pre-claimed so the modeled allocations\n // above never collide with them.\n for (const e of sheetRelsExtras) {\n sheetRels.rels.push({ id: e.id, type: e.type, target: e.target });\n }\n const sheetRId = ref.rId ?? allocateRId();\n const emit: SheetEmit = {\n id: sheetRId,\n target,\n bytes,\n relType: isChartsheet ? CHARTSHEET_REL : `${REL_NS}/worksheet`,\n archivePath,\n contentType: isChartsheet ? CHARTSHEET_TYPE : WORKSHEET_TYPE,\n };\n if (sheetRels.rels.length > 0) emit.rels = sheetRels;\n sheetEmits.push(emit);\n });\n\n // ---- 2. workbook rels -- sheets first, then sst (if any), then styles, then\n // theme / vbaProject, then any captured workbookRelsExtras (e.g.\n // pivotCacheDefinition rels referenced by `<pivotCaches>`). Modeled non-sheet\n // rels prefer the rId captured at load time so any captured extras XML using\n // that Id still resolves after the round-trip.\n const wbRels = makeRelationships();\n wbRels.rels = sheetEmits.map((e) => ({\n id: e.id,\n type: e.relType,\n target: e.target,\n }));\n const orig = wb.workbookRelOriginalIds;\n if (sst.entries.length > 0) {\n wbRels.rels.push({\n id: orig?.sharedStrings ?? allocateRId(),\n type: `${REL_NS}/sharedStrings`,\n target: 'sharedStrings.xml',\n });\n }\n wbRels.rels.push({\n id: orig?.styles ?? allocateRId(),\n type: `${REL_NS}/styles`,\n target: 'styles.xml',\n });\n if (wb.themeXml) {\n wbRels.rels.push({\n id: orig?.theme ?? allocateRId(),\n type: THEME_REL,\n target: 'theme/theme1.xml',\n });\n }\n if (wb.vbaProject) {\n wbRels.rels.push({\n id: orig?.vbaProject ?? allocateRId(),\n type: `${REL_NS}/vbaProject`,\n target: 'vbaProject.bin',\n });\n }\n if (wb.workbookRelsExtras) {\n for (const e of wb.workbookRelsExtras) {\n wbRels.rels.push({ id: e.id, type: e.type, target: e.target });\n }\n }\n\n // ---- 3. workbook.xml ----------------------------------------------------\n const workbookXml = serializeWorkbookXml(\n wb,\n sheetEmits.map((e) => e.id),\n );\n await writer.addEntry(ARC_WORKBOOK, new TextEncoder().encode(workbookXml));\n await writer.addEntry(ARC_WORKBOOK_RELS, relsToBytes(wbRels));\n\n // ---- 4. each worksheet / chartsheet (and its rels file when present) --\n for (const e of sheetEmits) {\n await writer.addEntry(e.archivePath, e.bytes);\n if (e.rels) {\n // The rels file sits alongside its part: `xl/<dir>/_rels/<file>.rels`.\n const slash = e.archivePath.lastIndexOf('/');\n const dir = e.archivePath.slice(0, slash);\n const file = e.archivePath.slice(slash + 1);\n await writer.addEntry(`${dir}/_rels/${file}.rels`, relsToBytes(e.rels));\n }\n }\n\n // ---- 4b. table parts ----------------------------------------------------\n for (const t of tableEmits) {\n await writer.addEntry(`xl/tables/table${t.id}.xml`, t.bytes);\n }\n\n // ---- 4c. comments parts + matching VML drawings ------------------------\n for (const c of commentEmits) {\n await writer.addEntry(`xl/comments${c.id}.xml`, c.commentsBytes);\n await writer.addEntry(`xl/drawings/vmlDrawing${c.id}.vml`, c.vmlBytes);\n }\n\n // ---- 4d. drawings + their rels (when charts are embedded) -------------\n for (const d of drawingEmits) {\n await writer.addEntry(`xl/drawings/drawing${d.id}.xml`, d.bytes);\n if (d.rels) {\n await writer.addEntry(`xl/drawings/_rels/drawing${d.id}.xml.rels`, relsToBytes(d.rels));\n }\n }\n\n // ---- 4e. chart parts (and per-chart rels when user shapes attach) -----\n for (const c of chartEmits) {\n await writer.addEntry(`xl/charts/chart${c.id}.xml`, c.bytes);\n if (c.rels) {\n await writer.addEntry(`xl/charts/_rels/chart${c.id}.xml.rels`, relsToBytes(c.rels));\n }\n }\n\n // ---- 4f. user-shape drawings (xl/drawings/chartDrawingN.xml) ----------\n for (const us of userShapeEmits) {\n await writer.addEntry(`xl/drawings/chartDrawing${us.id}.xml`, us.bytes);\n }\n\n // ---- 4g. embedded images (xl/media/imageN.{ext}) ----------------------\n for (const img of imageEmits) {\n await writer.addEntry(`xl/media/image${img.id}.${img.ext}`, img.bytes);\n }\n\n // ---- 5. styles.xml + sharedStrings.xml (if any) -------------------------\n await writer.addEntry(ARC_STYLE, stylesheetToBytes(wb.styles));\n if (sst.entries.length > 0) {\n await writer.addEntry(ARC_SHARED_STRINGS, sharedStringsToBytes(sst));\n }\n\n // ---- 5b. theme1.xml (passthrough) — only when wb carries one. The theme rel\n // was already added to wbRels above so workbook.xml.rels references it.\n if (wb.themeXml) await writer.addEntry(ARC_THEME, wb.themeXml);\n\n // ---- 5c. docProps/{core,app,custom}.xml (when present) ------------------\n if (wb.properties) await writer.addEntry(ARC_CORE, corePropsToBytes(wb.properties));\n if (wb.appProperties) await writer.addEntry(ARC_APP, extendedPropsToBytes(wb.appProperties));\n if (wb.customProperties) await writer.addEntry(ARC_CUSTOM, customPropsToBytes(wb.customProperties));\n\n // ---- 5d. VBA project + signature (when present) -------------------------\n if (wb.vbaProject) await writer.addEntry('xl/vbaProject.bin', wb.vbaProject);\n if (wb.vbaSignature) await writer.addEntry('xl/vbaProjectSignature.bin', wb.vbaSignature);\n\n // ---- 5e. Pass-through bytes (pivot / activeX / customXml / etc.) -------\n if (wb.passthrough) {\n for (const [path, bytes] of wb.passthrough) await writer.addEntry(path, bytes);\n }\n\n // ---- 6. root rels -------------------------------------------------------\n const rootRels: Relationships = { rels: [] };\n rootRels.rels.push({\n id: 'rId1',\n type: `${REL_NS}/officeDocument`,\n target: 'xl/workbook.xml',\n });\n if (wb.properties) {\n rootRels.rels.push({\n id: `rId${rootRels.rels.length + 1}`,\n type: CORE_PROPS_REL,\n target: 'docProps/core.xml',\n });\n }\n if (wb.appProperties) {\n rootRels.rels.push({\n id: `rId${rootRels.rels.length + 1}`,\n type: EXT_PROPS_REL,\n target: 'docProps/app.xml',\n });\n }\n if (wb.customProperties) {\n rootRels.rels.push({\n id: `rId${rootRels.rels.length + 1}`,\n type: CUSTOM_PROPS_REL,\n target: 'docProps/custom.xml',\n });\n }\n await writer.addEntry(ARC_ROOT_RELS, relsToBytes(rootRels));\n\n // ---- 7. [Content_Types].xml --------------------------------------------\n const manifest = makeManifest();\n addDefault(manifest, 'rels', 'application/vnd.openxmlformats-package.relationships+xml');\n addDefault(manifest, 'xml', 'application/xml');\n // Defaults carried over for passthrough parts go first: addDefault is\n // last-wins, so the writer's own registrations below (vml for comments, bin\n // for VBA, image formats) still take precedence on a shared extension.\n if (wb.passthroughDefaults) {\n for (const [ext, ct] of wb.passthroughDefaults) addDefault(manifest, ext, ct);\n }\n // VBA-bearing workbooks promote the workbook content type to xlsm.\n const workbookContentType = wb.vbaProject\n ? 'application/vnd.ms-excel.sheet.macroEnabled.main+xml'\n : XLSX_TYPE;\n addOverride(manifest, `/${ARC_WORKBOOK}`, workbookContentType);\n for (const e of sheetEmits) {\n addOverride(manifest, `/${e.archivePath}`, e.contentType);\n }\n addOverride(manifest, `/${ARC_STYLE}`, STYLES_TYPE);\n if (sst.entries.length > 0) {\n addOverride(manifest, `/${ARC_SHARED_STRINGS}`, SHARED_STRINGS_TYPE);\n }\n if (wb.themeXml) addOverride(manifest, `/${ARC_THEME}`, THEME_TYPE);\n for (const t of tableEmits) {\n addOverride(manifest, `/xl/tables/table${t.id}.xml`, TABLE_TYPE);\n }\n if (commentEmits.length > 0) {\n addDefault(manifest, 'vml', VML_DRAWING_TYPE);\n }\n for (const c of commentEmits) {\n addOverride(manifest, `/xl/comments${c.id}.xml`, COMMENTS_TYPE);\n }\n for (const d of drawingEmits) {\n addOverride(manifest, `/xl/drawings/drawing${d.id}.xml`, DRAWING_TYPE);\n }\n for (const c of chartEmits) {\n addOverride(manifest, `/xl/charts/chart${c.id}.xml`, c.isCx ? CHARTEX_TYPE : CHART_TYPE);\n }\n for (const us of userShapeEmits) {\n addOverride(manifest, `/xl/drawings/chartDrawing${us.id}.xml`, DRAWING_TYPE);\n }\n if (wb.vbaProject) {\n addDefault(manifest, 'bin', 'application/vnd.ms-office.vbaProject');\n }\n if (wb.passthrough) {\n for (const path of wb.passthrough.keys()) {\n const ct = wb.passthroughContentTypes?.get(path);\n if (ct !== undefined) addOverride(manifest, `/${path}`, ct);\n }\n }\n // Each unique image extension gets a Default entry (`<Default Extension=\"png\"\n // ContentType=\"image/png\"/>`).\n for (const ext of imageExts) {\n const fmt = (Object.entries(IMAGE_FORMAT_EXTENSION).find(([, e]) => e === ext) ?? [])[0] as\n | XlsxImageFormat\n | undefined;\n if (fmt) addDefault(manifest, ext, IMAGE_FORMAT_MIME[fmt]);\n }\n if (wb.properties) addOverride(manifest, `/${ARC_CORE}`, CORE_PROPS_TYPE);\n if (wb.appProperties) addOverride(manifest, `/${ARC_APP}`, EXT_PROPS_TYPE);\n if (wb.customProperties) addOverride(manifest, `/${ARC_CUSTOM}`, CPROPS_TYPE);\n await writer.addEntry(ARC_CONTENT_TYPES, manifestToBytes(manifest));\n\n // ---- 8. close ----------------------------------------------------------\n await writer.finalize();\n}\n\n/**\n * 1 + the highest N among passthrough paths matching `pattern` (whose first\n * capture group is N), so freshly numbered modeled parts never collide with a\n * carried-over one.\n */\nfunction nextFreeIndex(wb: Workbook, pattern: RegExp): number {\n let max = 0;\n if (wb.passthrough) {\n for (const path of wb.passthrough.keys()) {\n const m = pattern.exec(path);\n if (m?.[1] !== undefined) max = Math.max(max, Number.parseInt(m[1], 10));\n }\n }\n return max + 1;\n}\n\n/** Serialise the minimum `<workbook><sheets/></workbook>` Excel needs to load a sheet list. */\nfunction serializeWorkbookXml(wb: Workbook, sheetRIds: ReadonlyArray<string>): string {\n // Excel's markup-compatibility header: the extra prefixes it declares on the\n // root and the `mc:Ignorable` list naming them. Both are re-emitted exactly\n // as written, and the same prefix map is handed to every captured child so\n // `xr:revisionPtr` doesn't come back as `x16:revisionPtr`.\n const rootNs = wb.workbookXmlRoot?.namespaces ?? [];\n const nsDecls = rootNs.map((d) => ` xmlns:${d.prefix}=\"${escapeAttr(d.ns)}\"`).join('');\n const ignorable = wb.workbookXmlRoot?.ignorable;\n const ignorableAttr = ignorable !== undefined ? ` mc:Ignorable=\"${escapeAttr(ignorable)}\"` : '';\n const inScopePrefixes: Record<string, string> = { [SHEET_MAIN_NS]: '', [REL_NS]: 'r' };\n for (const d of rootNs) inScopePrefixes[d.ns] ??= d.prefix;\n const emitExtra = (node: import('../xml/tree.js').XmlNode): string =>\n serializeChildNode(node, inScopePrefixes);\n\n const parts: string[] = [\n '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>',\n `<workbook xmlns=\"${SHEET_MAIN_NS}\" xmlns:r=\"${REL_NS}\"${nsDecls}${ignorableAttr}>`,\n ];\n if (wb.fileVersion) {\n const fv = serializeFileVersion(wb.fileVersion);\n if (fv) parts.push(fv);\n }\n if (wb.fileSharing) {\n const fs = serializeFileSharing(wb.fileSharing);\n if (fs) parts.push(fs);\n }\n // Emit <workbookPr> from the typed model; fall back to a minimal {date1904:\n // true} synthesis so a fresh workbook (no load history) still round-trips\n // through Excel with the right epoch.\n const effectiveWp = effectiveWorkbookProperties(wb);\n if (effectiveWp) {\n const wp = serializeWorkbookProperties(effectiveWp);\n if (wp) parts.push(wp);\n }\n if (wb.workbookProtection) {\n const wp = serializeWorkbookProtection(wb.workbookProtection);\n if (wp) parts.push(wp);\n }\n // The unmodeled head — `mc:AlternateContent`, `xr:revisionPtr` — sits after\n // `<workbookPr>` in what Excel writes, and CT_Workbook has no slot for it,\n // so it goes here rather than ahead of the modeled elements.\n if (wb.workbookXmlExtras?.beforeSheets) {\n for (const node of wb.workbookXmlExtras.beforeSheets) parts.push(emitExtra(node));\n }\n if (wb.bookViews && wb.bookViews.length > 0) parts.push(serializeBookViews(wb.bookViews, inScopePrefixes));\n parts.push('<sheets>');\n wb.sheets.forEach((ref, i) => {\n const stateAttr = ref.state === 'visible' ? '' : ` state=\"${ref.state}\"`;\n const rId = sheetRIds[i] ?? `rId${i + 1}`;\n parts.push(`<sheet name=\"${escapeAttr(ref.sheet.title)}\" sheetId=\"${ref.sheetId}\"${stateAttr} r:id=\"${rId}\"/>`);\n });\n parts.push('</sheets>');\n // ECMA-376 §18.2.27 makes CT_Workbook an xsd:sequence, so the order below is\n // normative: functionGroups, externalReferences, definedNames, calcPr,\n // oleSize, customWorkbookViews, pivotCaches, smartTagPr, smartTagTypes,\n // webPublishing, fileRecoveryPr, webPublishObjects, extLst. Excel rejects a\n // workbook whose children are out of sequence. `afterSheets` is the\n // unmodeled tail (webPublishing / webPublishObjects / extLst /\n // mc:AlternateContent), so it goes last.\n if (wb.functionGroups) {\n const fg = serializeFunctionGroups(wb.functionGroups);\n if (fg) parts.push(fg);\n }\n if (wb.externalReferences && wb.externalReferences.length > 0) {\n const inner: string[] = ['<externalReferences>'];\n for (const er of wb.externalReferences) {\n inner.push(`<externalReference r:id=\"${escapeAttr(er.rId)}\"/>`);\n }\n inner.push('</externalReferences>');\n parts.push(inner.join(''));\n }\n if (wb.definedNames.length > 0) {\n parts.push('<definedNames>');\n for (const dn of wb.definedNames) {\n let attrs = ` name=\"${escapeAttr(dn.name)}\"`;\n if (dn.scope !== undefined) attrs += ` localSheetId=\"${dn.scope}\"`;\n if (dn.hidden) attrs += ' hidden=\"1\"';\n if (dn.comment !== undefined) attrs += ` comment=\"${escapeAttr(dn.comment)}\"`;\n parts.push(`<definedName${attrs}>${escapeText(normalizeFormulaText(dn.value))}</definedName>`);\n }\n parts.push('</definedNames>');\n }\n if (wb.calcProperties) {\n const cp = serializeCalcProperties(wb.calcProperties);\n if (cp) parts.push(cp);\n }\n // No default `<calcPr>` — Excel handles its absence by inserting one on save.\n // Hand-rolled `calcId` values (especially the modern 191029) made Excel\n // reject workbooks whose future-function formulas (LET / LAMBDA / FILTER…)\n // couldn't be evaluated against the declared engine.\n if (wb.oleSize !== undefined) {\n parts.push(`<oleSize ref=\"${escapeAttr(wb.oleSize)}\"/>`);\n }\n if (wb.customWorkbookViews && wb.customWorkbookViews.length > 0) {\n parts.push(serializeCustomWorkbookViews(wb.customWorkbookViews));\n }\n if (wb.pivotCaches && wb.pivotCaches.length > 0) {\n const inner: string[] = ['<pivotCaches>'];\n for (const pc of wb.pivotCaches) {\n inner.push(`<pivotCache cacheId=\"${pc.cacheId}\" r:id=\"${escapeAttr(pc.rId)}\"/>`);\n }\n inner.push('</pivotCaches>');\n parts.push(inner.join(''));\n }\n if (wb.smartTagPr) {\n const stp = serializeSmartTagPr(wb.smartTagPr);\n if (stp) parts.push(stp);\n }\n if (wb.smartTagTypes && wb.smartTagTypes.length > 0) {\n parts.push(serializeSmartTagTypes(wb.smartTagTypes));\n }\n if (wb.fileRecoveryPr) {\n const fp = serializeFileRecoveryPr(wb.fileRecoveryPr);\n if (fp) parts.push(fp);\n }\n if (wb.workbookXmlExtras?.afterSheets) {\n for (const node of wb.workbookXmlExtras.afterSheets) parts.push(emitExtra(node));\n }\n parts.push('</workbook>');\n return parts.join('');\n}\n\nfunction serializeCalcProperties(\n cp: import('../workbook/calc-properties.js').CalcProperties,\n): string | undefined {\n let attrs = '';\n if (cp.calcId !== undefined) attrs += ` calcId=\"${cp.calcId}\"`;\n if (cp.calcMode !== undefined) attrs += ` calcMode=\"${cp.calcMode}\"`;\n if (cp.fullCalcOnLoad !== undefined) attrs += ` fullCalcOnLoad=\"${cp.fullCalcOnLoad ? '1' : '0'}\"`;\n if (cp.refMode !== undefined) attrs += ` refMode=\"${cp.refMode}\"`;\n if (cp.iterate !== undefined) attrs += ` iterate=\"${cp.iterate ? '1' : '0'}\"`;\n if (cp.iterateCount !== undefined) attrs += ` iterateCount=\"${cp.iterateCount}\"`;\n if (cp.iterateDelta !== undefined) attrs += ` iterateDelta=\"${cp.iterateDelta}\"`;\n if (cp.fullPrecision !== undefined) attrs += ` fullPrecision=\"${cp.fullPrecision ? '1' : '0'}\"`;\n if (cp.calcCompleted !== undefined) attrs += ` calcCompleted=\"${cp.calcCompleted ? '1' : '0'}\"`;\n if (cp.calcOnSave !== undefined) attrs += ` calcOnSave=\"${cp.calcOnSave ? '1' : '0'}\"`;\n if (cp.concurrentCalc !== undefined) attrs += ` concurrentCalc=\"${cp.concurrentCalc ? '1' : '0'}\"`;\n if (cp.concurrentManualCount !== undefined) attrs += ` concurrentManualCount=\"${cp.concurrentManualCount}\"`;\n if (cp.forceFullCalc !== undefined) attrs += ` forceFullCalc=\"${cp.forceFullCalc ? '1' : '0'}\"`;\n if (attrs.length === 0) return undefined;\n return `<calcPr${attrs}/>`;\n}\n\nfunction serializeFunctionGroups(\n fg: import('../workbook/function-groups.js').FunctionGroups,\n): string | undefined {\n let attrs = '';\n if (fg.builtInGroupCount !== undefined) attrs += ` builtInGroupCount=\"${fg.builtInGroupCount}\"`;\n if (fg.groups.length === 0 && attrs.length === 0) return undefined;\n if (fg.groups.length === 0) return `<functionGroups${attrs}/>`;\n const inner: string[] = [`<functionGroups${attrs}>`];\n for (const g of fg.groups) inner.push(`<functionGroup name=\"${escapeAttr(g.name)}\"/>`);\n inner.push('</functionGroups>');\n return inner.join('');\n}\n\nfunction serializeSmartTagPr(\n stp: import('../workbook/smart-tags.js').SmartTagProperties,\n): string | undefined {\n let attrs = '';\n if (stp.embed !== undefined) attrs += ` embed=\"${stp.embed ? '1' : '0'}\"`;\n if (stp.show !== undefined) attrs += ` show=\"${stp.show}\"`;\n if (attrs.length === 0) return undefined;\n return `<smartTagPr${attrs}/>`;\n}\n\nfunction serializeSmartTagTypes(\n tags: ReadonlyArray<import('../workbook/smart-tags.js').SmartTagType>,\n): string {\n const inner: string[] = ['<smartTagTypes>'];\n for (const t of tags) {\n let attrs = '';\n if (t.namespaceUri !== undefined) attrs += ` namespaceUri=\"${escapeAttr(t.namespaceUri)}\"`;\n if (t.name !== undefined) attrs += ` name=\"${escapeAttr(t.name)}\"`;\n if (t.url !== undefined) attrs += ` url=\"${escapeAttr(t.url)}\"`;\n inner.push(`<smartTagType${attrs}/>`);\n }\n inner.push('</smartTagTypes>');\n return inner.join('');\n}\n\nfunction serializeFileRecoveryPr(\n fp: import('../workbook/file-recovery.js').FileRecoveryProperties,\n): string | undefined {\n let attrs = '';\n if (fp.autoRecover !== undefined) attrs += ` autoRecover=\"${fp.autoRecover ? '1' : '0'}\"`;\n if (fp.crashSave !== undefined) attrs += ` crashSave=\"${fp.crashSave ? '1' : '0'}\"`;\n if (fp.dataExtractLoad !== undefined) attrs += ` dataExtractLoad=\"${fp.dataExtractLoad ? '1' : '0'}\"`;\n if (fp.repairLoad !== undefined) attrs += ` repairLoad=\"${fp.repairLoad ? '1' : '0'}\"`;\n if (attrs.length === 0) return undefined;\n return `<fileRecoveryPr${attrs}/>`;\n}\n\nfunction serializeFileSharing(\n fs: import('../workbook/file-sharing.js').FileSharing,\n): string | undefined {\n let attrs = '';\n if (fs.readOnlyRecommended !== undefined)\n attrs += ` readOnlyRecommended=\"${fs.readOnlyRecommended ? '1' : '0'}\"`;\n if (fs.userName !== undefined) attrs += ` userName=\"${escapeAttr(fs.userName)}\"`;\n if (fs.reservationPassword !== undefined)\n attrs += ` reservationPassword=\"${escapeAttr(fs.reservationPassword)}\"`;\n if (fs.algorithmName !== undefined) attrs += ` algorithmName=\"${escapeAttr(fs.algorithmName)}\"`;\n if (fs.hashValue !== undefined) attrs += ` hashValue=\"${escapeAttr(fs.hashValue)}\"`;\n if (fs.saltValue !== undefined) attrs += ` saltValue=\"${escapeAttr(fs.saltValue)}\"`;\n if (fs.spinCount !== undefined) attrs += ` spinCount=\"${fs.spinCount}\"`;\n if (attrs.length === 0) return undefined;\n return `<fileSharing${attrs}/>`;\n}\n\nfunction serializeFileVersion(\n fv: import('../workbook/file-version.js').FileVersion,\n): string | undefined {\n let attrs = '';\n if (fv.appName !== undefined) attrs += ` appName=\"${escapeAttr(fv.appName)}\"`;\n if (fv.lastEdited !== undefined) attrs += ` lastEdited=\"${escapeAttr(fv.lastEdited)}\"`;\n if (fv.lowestEdited !== undefined) attrs += ` lowestEdited=\"${escapeAttr(fv.lowestEdited)}\"`;\n if (fv.rupBuild !== undefined) attrs += ` rupBuild=\"${escapeAttr(fv.rupBuild)}\"`;\n if (fv.codeName !== undefined) attrs += ` codeName=\"${escapeAttr(fv.codeName)}\"`;\n if (attrs.length === 0) return undefined;\n return `<fileVersion${attrs}/>`;\n}\n\nfunction effectiveWorkbookProperties(\n wb: Workbook,\n): import('../workbook/workbook-properties.js').WorkbookProperties | undefined {\n const explicit = wb.workbookProperties;\n if (explicit) {\n // Mirror the canonical date1904 flag if the typed model omits it.\n if (wb.date1904 && explicit.date1904 === undefined) {\n return { ...explicit, date1904: true };\n }\n return explicit;\n }\n if (wb.date1904) return { date1904: true };\n return undefined;\n}\n\nfunction serializeWorkbookProperties(\n wp: import('../workbook/workbook-properties.js').WorkbookProperties,\n): string | undefined {\n let attrs = '';\n const boolKeys: ReadonlyArray<keyof import('../workbook/workbook-properties.js').WorkbookProperties> = [\n 'date1904',\n 'dateCompatibility',\n 'showBorderUnselectedTables',\n 'filterPrivacy',\n 'promptedSolutions',\n 'showInkAnnotation',\n 'backupFile',\n 'saveExternalLinkValues',\n 'hidePivotFieldList',\n 'showPivotChartFilter',\n 'allowRefreshQuery',\n 'publishItems',\n 'checkCompatibility',\n 'autoCompressPictures',\n 'refreshAllConnections',\n ];\n for (const k of boolKeys) {\n const v = wp[k];\n if (v !== undefined) attrs += ` ${k}=\"${v ? '1' : '0'}\"`;\n }\n if (wp.showObjects !== undefined) attrs += ` showObjects=\"${wp.showObjects}\"`;\n if (wp.updateLinks !== undefined) attrs += ` updateLinks=\"${wp.updateLinks}\"`;\n if (wp.codeName !== undefined) attrs += ` codeName=\"${escapeAttr(wp.codeName)}\"`;\n if (wp.defaultThemeVersion !== undefined) attrs += ` defaultThemeVersion=\"${wp.defaultThemeVersion}\"`;\n if (attrs.length === 0) return undefined;\n return `<workbookPr${attrs}/>`;\n}\n\nfunction serializeCustomWorkbookViews(\n views: ReadonlyArray<import('../workbook/views.js').CustomWorkbookView>,\n): string {\n const parts: string[] = ['<customWorkbookViews>'];\n for (const v of views) {\n let attrs = ` name=\"${escapeAttr(v.name)}\" guid=\"${escapeAttr(v.guid)}\"`;\n if (v.autoUpdate !== undefined) attrs += ` autoUpdate=\"${v.autoUpdate ? '1' : '0'}\"`;\n if (v.mergeInterval !== undefined) attrs += ` mergeInterval=\"${v.mergeInterval}\"`;\n if (v.changesSavedWin !== undefined) attrs += ` changesSavedWin=\"${v.changesSavedWin ? '1' : '0'}\"`;\n if (v.onlySync !== undefined) attrs += ` onlySync=\"${v.onlySync ? '1' : '0'}\"`;\n if (v.personalView !== undefined) attrs += ` personalView=\"${v.personalView ? '1' : '0'}\"`;\n if (v.includePrintSettings !== undefined)\n attrs += ` includePrintSettings=\"${v.includePrintSettings ? '1' : '0'}\"`;\n if (v.includeHiddenRowCol !== undefined)\n attrs += ` includeHiddenRowCol=\"${v.includeHiddenRowCol ? '1' : '0'}\"`;\n if (v.maximized !== undefined) attrs += ` maximized=\"${v.maximized ? '1' : '0'}\"`;\n if (v.minimized !== undefined) attrs += ` minimized=\"${v.minimized ? '1' : '0'}\"`;\n if (v.showHorizontalScroll !== undefined)\n attrs += ` showHorizontalScroll=\"${v.showHorizontalScroll ? '1' : '0'}\"`;\n if (v.showVerticalScroll !== undefined)\n attrs += ` showVerticalScroll=\"${v.showVerticalScroll ? '1' : '0'}\"`;\n if (v.showSheetTabs !== undefined) attrs += ` showSheetTabs=\"${v.showSheetTabs ? '1' : '0'}\"`;\n if (v.xWindow !== undefined) attrs += ` xWindow=\"${v.xWindow}\"`;\n if (v.yWindow !== undefined) attrs += ` yWindow=\"${v.yWindow}\"`;\n attrs += ` windowWidth=\"${v.windowWidth}\" windowHeight=\"${v.windowHeight}\"`;\n if (v.tabRatio !== undefined) attrs += ` tabRatio=\"${v.tabRatio}\"`;\n attrs += ` activeSheetId=\"${v.activeSheetId}\"`;\n if (v.showFormulaBar !== undefined) attrs += ` showFormulaBar=\"${v.showFormulaBar ? '1' : '0'}\"`;\n if (v.showStatusbar !== undefined) attrs += ` showStatusbar=\"${v.showStatusbar ? '1' : '0'}\"`;\n if (v.showComments !== undefined) attrs += ` showComments=\"${v.showComments}\"`;\n if (v.showObjects !== undefined) attrs += ` showObjects=\"${v.showObjects}\"`;\n parts.push(`<customWorkbookView${attrs}/>`);\n }\n parts.push('</customWorkbookViews>');\n return parts.join('');\n}\n\nfunction serializeBookViews(\n views: ReadonlyArray<import('../workbook/views.js').WorkbookView>,\n prefixOf: Readonly<Record<string, string>>,\n): string {\n const parts: string[] = ['<bookViews>'];\n for (const v of views) {\n let attrs = '';\n if (v.visibility !== undefined) attrs += ` visibility=\"${v.visibility}\"`;\n if (v.minimized !== undefined) attrs += ` minimized=\"${v.minimized ? '1' : '0'}\"`;\n if (v.showHorizontalScroll !== undefined)\n attrs += ` showHorizontalScroll=\"${v.showHorizontalScroll ? '1' : '0'}\"`;\n if (v.showVerticalScroll !== undefined)\n attrs += ` showVerticalScroll=\"${v.showVerticalScroll ? '1' : '0'}\"`;\n if (v.showSheetTabs !== undefined) attrs += ` showSheetTabs=\"${v.showSheetTabs ? '1' : '0'}\"`;\n if (v.xWindow !== undefined) attrs += ` xWindow=\"${v.xWindow}\"`;\n if (v.yWindow !== undefined) attrs += ` yWindow=\"${v.yWindow}\"`;\n if (v.windowWidth !== undefined) attrs += ` windowWidth=\"${v.windowWidth}\"`;\n if (v.windowHeight !== undefined) attrs += ` windowHeight=\"${v.windowHeight}\"`;\n if (v.tabRatio !== undefined) attrs += ` tabRatio=\"${v.tabRatio}\"`;\n if (v.firstSheet !== undefined) attrs += ` firstSheet=\"${v.firstSheet}\"`;\n if (v.activeTab !== undefined) attrs += ` activeTab=\"${v.activeTab}\"`;\n if (v.autoFilterDateGrouping !== undefined)\n attrs += ` autoFilterDateGrouping=\"${v.autoFilterDateGrouping ? '1' : '0'}\"`;\n // Namespaced extras (`xr2:uid`) only make sense with a prefix the root\n // declares; without one the attribute would be unreadable, so skip it.\n for (const [clark, value] of Object.entries(v.extAttrs ?? {})) {\n const { ns, local } = parseQName(clark);\n const prefix = prefixOf[ns];\n if (prefix === undefined) continue;\n attrs += ` ${prefix}:${local}=\"${escapeAttr(value)}\"`;\n }\n parts.push(`<workbookView${attrs}/>`);\n }\n parts.push('</bookViews>');\n return parts.join('');\n}\n\nfunction serializeWorkbookProtection(\n wp: import('../workbook/protection.js').WorkbookProtection,\n): string | undefined {\n let attrs = '';\n const strAttrs = [\n 'workbookPassword',\n 'workbookPasswordCharacterSet',\n 'workbookAlgorithmName',\n 'workbookHashValue',\n 'workbookSaltValue',\n 'revisionsPassword',\n 'revisionsPasswordCharacterSet',\n 'revisionsAlgorithmName',\n 'revisionsHashValue',\n 'revisionsSaltValue',\n ] as const;\n for (const k of strAttrs) {\n const v = wp[k];\n if (v !== undefined) attrs += ` ${k}=\"${escapeAttr(v)}\"`;\n }\n if (wp.workbookSpinCount !== undefined) attrs += ` workbookSpinCount=\"${wp.workbookSpinCount}\"`;\n if (wp.revisionsSpinCount !== undefined) attrs += ` revisionsSpinCount=\"${wp.revisionsSpinCount}\"`;\n if (wp.lockStructure !== undefined) attrs += ` lockStructure=\"${wp.lockStructure ? '1' : '0'}\"`;\n if (wp.lockWindows !== undefined) attrs += ` lockWindows=\"${wp.lockWindows ? '1' : '0'}\"`;\n if (wp.lockRevision !== undefined) attrs += ` lockRevision=\"${wp.lockRevision ? '1' : '0'}\"`;\n if (attrs.length === 0) return undefined;\n return `<workbookProtection${attrs}/>`;\n}\n\n// Local aliases keep the dense call sites below readable. Canonical\n// implementations live in utils/escape.ts so all three writers (this file,\n// xml/serializer, xml/stream-writer) agree on how `>` / whitespace are\n// handled.\nconst escapeText = escapeXmlText;\nconst escapeAttr = escapeXmlAttr;\n\n/**\n * Serialise an XmlNode child of `<workbook>` for inline injection back into the\n * workbook XML stream. Reuses serializeXml then strips the declaration.\n * Captured nodes carry Clark-notation names, so `inScopePrefixes` is what keeps\n * a child on the prefix the root declared for it instead of a freshly\n * allocated one — which matters when `mc:Ignorable` names those prefixes.\n */\nfunction serializeChildNode(\n node: import('../xml/tree.js').XmlNode,\n inScopePrefixes?: Readonly<Record<string, string>>,\n): string {\n const bytes = serializeXmlNode(node, {\n xmlDeclaration: false,\n ...(inScopePrefixes ? { inScopePrefixes } : {}),\n });\n return new TextDecoder().decode(bytes);\n}\n"],"mappings":";;;;;;;;;;;AAkEA,MAAM,kBAAkB;AACxB,MAAM,iBAAiB;AACvB,MAAM,iBAAiB,GAAG,WAAW;AACrC,MAAM,gBAAgB,GAAG,OAAO;AAChC,MAAM,mBAAmB,GAAG,OAAO;AACnC,MAAM,YAAY,GAAG,OAAO;AAC5B,MAAM,YAAY,GAAG,OAAO;AAC5B,MAAM,aAAa;AACnB,MAAM,eAAe,GAAG,OAAO;AAC/B,MAAM,kBAAkB,GAAG,OAAO;AAClC,MAAM,gBAAgB;AACtB,MAAM,mBAAmB;AACzB,MAAM,cAAc,GAAG,OAAO;AAC9B,MAAM,eAAe;AACrB,MAAM,YAAY,GAAG,OAAO;AAC5B,MAAM,cAAc;AACpB,MAAM,aAAa;AACnB,MAAM,YAAY,GAAG,OAAO;AAC5B,MAAM,iBAAiB,GAAG,OAAO;AACjC,MAAM,kBAAkB;AACxB,MAAM,wBAAwB,GAAG,OAAO;;;;;;AAuBxC,eAAsB,gBAAgB,IAAc,OAAoB,CAAC,GAAwB;CAC/F,MAAM,OAAO,iBAAiB;CAC9B,MAAM,aAAa,IAAI,MAAM,IAAI;CACjC,OAAO,KAAK,OAAO;AACrB;AAEA,MAAM,yBAA8D;CAClE,MAAM,SAAuB,CAAC;CAC9B,IAAI;CACJ,MAAM,iBAA6B;EACjC,IAAI,cAAc,KAAA,GAAW,OAAO;EACpC,IAAI,QAAQ;EACZ,KAAK,MAAM,KAAK,QAAQ,SAAS,EAAE;EACnC,MAAM,MAAM,IAAI,WAAW,KAAK;EAChC,IAAI,MAAM;EACV,KAAK,MAAM,KAAK,QAAQ;GACtB,IAAI,IAAI,GAAG,GAAG;GACd,OAAO,EAAE;EACX;EACA,YAAY;EACZ,OAAO,SAAS;EAChB,OAAO;CACT;CACA,OAAO;EACL,UAAU;GACR,OAAO;IACL,MAAM,OAAyB;KAC7B,IAAI,cAAc,KAAA,GAChB,MAAM,IAAI,eAAe,0CAA0C;KAErE,IAAI,EAAE,iBAAiB,aACrB,MAAM,IAAI,eAAe,iDAAiD;KAE5E,OAAO,KAAK,KAAK;IACnB;IACA,MAAM,SAA8B;KAClC,OAAO,SAAS;IAClB;IACA,QAAc;KACZ,IAAI,cAAc,KAAA,GAAW;KAC7B,4BAAY,IAAI,WAAW,CAAC;KAC5B,OAAO,SAAS;IAClB;GACF;EACF;EACA,SAAqB;GACnB,OAAO,SAAS;EAClB;CACF;AACF;;;;;;;;;;;AAYA,MAAM,uBAAuB,OAAuB;CAClD,MAAM,uBAAO,IAAI,IAAoB;CACrC,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,OAAO,QAAQ,KAAK;EACzC,MAAM,MAAM,GAAG,OAAO;EACtB,IAAI,CAAC,KAAK;EACV,MAAM,QAAQ,IAAI,MAAM;EACxB,MAAM,SAAS,mBAAmB,KAAK;EACvC,IAAI,QACF,MAAM,IAAI,mBAAmB,uBAAuB,EAAE,WAAW,MAAM,KAAK,QAAQ;EAEtF,MAAM,QAAQ,MAAM,YAAY;EAChC,MAAM,QAAQ,KAAK,IAAI,KAAK;EAC5B,IAAI,UAAU,KAAA,GACZ,MAAM,IAAI,mBACR,uBAAuB,EAAE,WAAW,MAAM,wBAAwB,MAAM,gEAE1E;EAEF,KAAK,IAAI,OAAO,CAAC;CACnB;AACF;;AAGA,eAAsB,aAAa,IAAc,MAAgB,OAAoB,CAAC,GAAkB;CACtG,oBAAoB,EAAE;CACtB,MAAM,SAAS,gBAAgB,MAAM,IAAI;CACzC,IAAI;EACF,MAAM,iBAAiB,IAAI,MAAM;CACnC,SAAS,KAAK;EAIZ,OAAO,MAAM,GAAG;EAChB,MAAM;CACR;AACF;AAEA,eAAe,iBAAiB,IAAc,QAA2D;CAEvG,MAAM,MAAM,kBAAkB;CAc9B,MAAM,aAA0B,CAAC;CAEjC,MAAM,aAAuD,CAAC;CAC9D,IAAI,cAAc;CAQlB,MAAM,eAA8B,CAAC;CAIrC,IAAI,iBAAiB,cAAc,IAAI,sCAAsC;CAS7E,MAAM,eAA8B,CAAC;CACrC,IAAI,gBAAgB;CAGpB,MAAM,aAMD,CAAC;CACN,IAAI,cAAc;CAGlB,MAAM,iBAA2D,CAAC;CAClE,IAAI,mBAAmB;CASvB,MAAM,aAA0B,CAAC;CACjC,MAAM,4BAAY,IAAI,IAAY;CAGlC,IAAI,cAAc,cAAc,IAAI,0BAA0B;CAI9D,IAAI,kBAAkB;CACtB,IAAI,mBAAmB;CAIvB,MAAM,8BAAc,IAAI,IAAY;CACpC,KAAK,MAAM,OAAO,GAAG,QACnB,IAAI,IAAI,QAAQ,KAAA,GAAW,YAAY,IAAI,IAAI,GAAG;CAEpD,IAAI,GAAG;OACA,MAAM,KAAK,OAAO,OAAO,GAAG,sBAAsB,GACrD,IAAI,OAAO,MAAM,UAAU,YAAY,IAAI,CAAC;CAAA;CAGhD,IAAI,GAAG,oBACL,KAAK,MAAM,KAAK,GAAG,oBAAoB,YAAY,IAAI,EAAE,EAAE;CAE7D,IAAI,gBAAgB;CACpB,MAAM,oBAA4B;EAChC,IAAI,KAAK,MAAM;EACf,OAAO,YAAY,IAAI,EAAE,GAAG;GAC1B;GACA,KAAK,MAAM;EACb;EACA,YAAY,IAAI,EAAE;EAClB;EACA,OAAO;CACT;CACA,GAAG,OAAO,SAAS,KAAK,OAAO;EAC7B,MAAM,eAAe,IAAI,SAAS;EAClC,MAAM,SAAS,eAAe,oBAAoB,iBAAiB,QAAQ,mBAAmB,gBAAgB;EAC9G,MAAM,cAAc,eAChB,uBAAuB,iBAAiB,QACxC,sBAAsB,gBAAgB;EAC1C,IAAI,cAAc;OACb;EACL,MAAM,YAAY,kBAAkB;EAKpC,MAAM,mCAAmB,IAAI,IAAY;EACzC,MAAM,kBAAkB,IAAI,SAAS,cAAe,IAAI,MAAM,cAAc,CAAC,IAAK,CAAC;EACnF,KAAK,MAAM,KAAK,iBAAiB,iBAAiB,IAAI,EAAE,EAAE;EAC1D,IAAI,iBAAiB;EACrB,MAAM,yBAAiC;GACrC,IAAI,KAAK,MAAM;GACf,OAAO,iBAAiB,IAAI,EAAE,KAAK,UAAU,KAAK,MAAM,MAAM,EAAE,OAAO,EAAE,GAAG;IAC1E;IACA,KAAK,MAAM;GACb;GACA;GACA,OAAO;EACT;EACA,MAAM,iBAAiB,UAA4C;GACjE,MAAM,UAAU;GAChB,MAAM,MAAM,iBAAiB;GAC7B,UAAU,KAAK,KAAK;IAClB,IAAI;IACJ,MAAM;IACN,QAAQ,kBAAkB,QAAQ;GACpC,CAAC;GAED,MAAM,WAA4B;IAAE,GAAG;IAAO,IAAI;GAAQ;GAC1D,WAAW,KAAK;IAAE,IAAI;IAAS,OAAO,aAAa,QAAQ;GAAE,CAAC;GAC9D,OAAO,EAAE,IAAI;EACf;EACA,MAAM,oBAAoB,aAAiE;GACzF,MAAM,KAAK;GACX,MAAM,gBAAgB,iBAAiB;GACvC,UAAU,KAAK,KAAK;IAClB,IAAI;IACJ,MAAM;IACN,QAAQ,cAAc,GAAG;GAC3B,CAAC;GACD,MAAM,WAAW,iBAAiB;GAClC,UAAU,KAAK,KAAK;IAClB,IAAI;IACJ,MAAM;IACN,QAAQ,yBAAyB,GAAG;GACtC,CAAC;GACD,aAAa,KAAK;IAChB;IACA,eAAe,gBAAgB,QAAQ;IACvC,UAAU,sBAAsB,QAAQ;GAC1C,CAAC;GACD,OAAO,EAAE,SAAS;EACpB;EACA,MAAM,mBAAmB,YAAsC;GAC7D,MAAM,KAAK;GACX,MAAM,MAAM,iBAAiB;GAC7B,UAAU,KAAK,KAAK;IAClB,IAAI;IACJ,MAAM;IACN,QAAQ,sBAAsB,GAAG;GACnC,CAAC;GAID,MAAM,cAAc,kBAAkB;GAItC,MAAM,kCAAkB,IAAI,IAAY;GACxC,KAAK,MAAM,OAAO,QAAQ,SAAS,QAAQ,CAAC,GAAG;IAC7C,YAAY,KAAK,KAAK,GAAG;IACzB,gBAAgB,IAAI,IAAI,EAAE;GAC5B;GACA,IAAI,iBAAiB;GACrB,MAAM,2BAAmC;IACvC,IAAI,YAAY,MAAM;IACtB,OAAO,gBAAgB,IAAI,SAAS,GAAG,YAAY,MAAM;IACzD,gBAAgB,IAAI,SAAS;IAC7B,OAAO;GACT;GACA,MAAM,cAA6B,CAAC;GACpC,KAAK,MAAM,QAAQ,QAAQ,OACzB,IAAI,KAAK,QAAQ,SAAS,YAAY,KAAK,QAAQ,MAAM,SAAS,KAAK,QAAQ,MAAM,UAAU;IAC7F,MAAM,UAAU;IAChB,MAAM,WAAW,mBAAmB;IAKpC,MAAM,YAAY,KAAK,QAAQ,MAAM,YAAY,KAAA;IACjD,YAAY,KAAK,KAAK;KACpB,IAAI;KACJ,MAAM,YAAY,cAAc;KAChC,QAAQ,kBAAkB,QAAQ;IACpC,CAAC;IACD,IAAI,KAAK,QAAQ,MAAM,SACrB,WAAW,KAAK;KACd,IAAI;KACJ,OAAO,eAAe,KAAK,QAAQ,MAAM,OAAO;KAChD,MAAM;IACR,CAAC;SACI,IAAI,KAAK,QAAQ,MAAM,OAAO;KACnC,MAAM,QAAQ,KAAK,QAAQ,MAAM;KAIjC,IAAI;KACJ,IAAI;KACJ,IAAI,MAAM,cAAc,MAAM,WAAW,OAAO,SAAS,GAAG;MAC1D,MAAM,eAAe;MACrB,gBAAgB;MAChB,gBAAgB,kBAAkB;MAClC,cAAc,KAAK,KAAK;OACtB,IAAI;OACJ,MAAM;OACN,QAAQ,2BAA2B,aAAa;MAClD,CAAC;MACD,eAAe,KAAK;OAClB,IAAI;OACJ,OAAO,kBAAkB,MAAM,UAAU;MAC3C,CAAC;KACH;KACA,MAAM,YAAuC;MAC3C,IAAI;MACJ,OAAO,aAAa,OAAO,kBAAkB,KAAA,IAAY,EAAE,cAAc,IAAI,CAAC,CAAC;MAC/E,MAAM;KACR;KACA,IAAI,eAAe,UAAU,OAAO;KACpC,WAAW,KAAK,SAAS;IAC3B;IACA,YAAY,KAAK;KACf,QAAQ,KAAK;KACb,SAAS;MACP,MAAM;MACN,OAAO;OACL,KAAK;OACL,GAAI,KAAK,QAAQ,MAAM,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;MACrD;KACF;IACF,CAAC;GACH,OAAO,IAAI,KAAK,QAAQ,SAAS,aAAa,KAAK,QAAQ,QAAQ,OAAO;IACxE,MAAM,MAAM,KAAK,QAAQ,QAAQ;IACjC,MAAM,MAAM,uBAAuB,IAAI;IACvC,MAAM,UAAU;IAChB,MAAM,SAAS,mBAAmB;IAClC,YAAY,KAAK,KAAK;KACpB,IAAI;KACJ,MAAM;KACN,QAAQ,iBAAiB,QAAQ,GAAG;IACtC,CAAC;IACD,WAAW,KAAK;KAAE,IAAI;KAAS;KAAK,OAAO,IAAI;IAAM,CAAC;IACtD,UAAU,IAAI,GAAG;IACjB,YAAY,KAAK;KACf,QAAQ,KAAK;KACb,SAAS;MACP,MAAM;MACN,SAAS;OACP,KAAK;OACL,GAAI,KAAK,QAAQ,QAAQ,SAAS,KAAA,IAAY,EAAE,MAAM,KAAK,QAAQ,QAAQ,KAAK,IAAI,CAAC;OACrF,GAAI,KAAK,QAAQ,QAAQ,UAAU,KAAA,IAAY,EAAE,OAAO,KAAK,QAAQ,QAAQ,MAAM,IAAI,CAAC;OACxF,GAAI,KAAK,QAAQ,QAAQ,WAAW,KAAA,IAAY,EAAE,QAAQ,KAAK,QAAQ,QAAQ,OAAO,IAAI,CAAC;OAC3F,GAAI,KAAK,QAAQ,QAAQ,OAAO,EAAE,MAAM,KAAK,QAAQ,QAAQ,KAAK,IAAI,CAAC;MACzE;KACF;IACF,CAAC;GACH,OACE,YAAY,KAAK,IAAI;GAGzB,MAAM,OAAoB;IACxB;IACA,OAAO,eAAe;KACpB,OAAO;KACP,GAAI,QAAQ,cAAc,EAAE,aAAa,QAAQ,YAAY,IAAI,CAAC;IACpE,CAAC;GACH;GACA,IAAI,YAAY,KAAK,SAAS,GAAG,KAAK,OAAO;GAC7C,aAAa,KAAK,IAAI;GACtB,OAAO,EAAE,IAAI;EACf;EACA,IAAI;EACJ,IAAI,IAAI,SAAS,aACf,QAAQ,iBAAiB,IAAI,OAAO;GAClC,eAAe;GACf,QAAQ,GAAG;GACX,UAAU,GAAG;GACb,MAAM;GACN;GACA;GACA;EACF,CAAC;OACI;GAGL,IAAI;GACJ,IAAI,IAAI,MAAM,SAAS,aAAa,gBAAgB,IAAI,MAAM,OAAO,CAAC,CAAC;GACvE,QAAQ,kBAAkB,IAAI,OAAO,eAAe,KAAA,IAAY,EAAE,WAAW,IAAI,CAAC,CAAC;EACrF;EAKA,KAAK,MAAM,KAAK,iBACd,UAAU,KAAK,KAAK;GAAE,IAAI,EAAE;GAAI,MAAM,EAAE;GAAM,QAAQ,EAAE;EAAO,CAAC;EAGlE,MAAM,OAAkB;GACtB,IAFe,IAAI,OAAO,YAAY;GAGtC;GACA;GACA,SAAS,eAAe,iBAAiB,GAAG,OAAO;GACnD;GACA,aAAa,eAAe,kBAAkB;EAChD;EACA,IAAI,UAAU,KAAK,SAAS,GAAG,KAAK,OAAO;EAC3C,WAAW,KAAK,IAAI;CACtB,CAAC;CAOD,MAAM,SAAS,kBAAkB;CACjC,OAAO,OAAO,WAAW,KAAK,OAAO;EACnC,IAAI,EAAE;EACN,MAAM,EAAE;EACR,QAAQ,EAAE;CACZ,EAAE;CACF,MAAM,OAAO,GAAG;CAChB,IAAI,IAAI,QAAQ,SAAS,GACvB,OAAO,KAAK,KAAK;EACf,IAAI,MAAM,iBAAiB,YAAY;EACvC,MAAM,GAAG,OAAO;EAChB,QAAQ;CACV,CAAC;CAEH,OAAO,KAAK,KAAK;EACf,IAAI,MAAM,UAAU,YAAY;EAChC,MAAM,GAAG,OAAO;EAChB,QAAQ;CACV,CAAC;CACD,IAAI,GAAG,UACL,OAAO,KAAK,KAAK;EACf,IAAI,MAAM,SAAS,YAAY;EAC/B,MAAM;EACN,QAAQ;CACV,CAAC;CAEH,IAAI,GAAG,YACL,OAAO,KAAK,KAAK;EACf,IAAI,MAAM,cAAc,YAAY;EACpC,MAAM,GAAG,OAAO;EAChB,QAAQ;CACV,CAAC;CAEH,IAAI,GAAG,oBACL,KAAK,MAAM,KAAK,GAAG,oBACjB,OAAO,KAAK,KAAK;EAAE,IAAI,EAAE;EAAI,MAAM,EAAE;EAAM,QAAQ,EAAE;CAAO,CAAC;CAKjE,MAAM,cAAc,qBAClB,IACA,WAAW,KAAK,MAAM,EAAE,EAAE,CAC5B;CACA,MAAM,OAAO,SAAS,cAAc,IAAI,YAAY,CAAC,CAAC,OAAO,WAAW,CAAC;CACzE,MAAM,OAAO,SAAS,mBAAmB,YAAY,MAAM,CAAC;CAG5D,KAAK,MAAM,KAAK,YAAY;EAC1B,MAAM,OAAO,SAAS,EAAE,aAAa,EAAE,KAAK;EAC5C,IAAI,EAAE,MAAM;GAEV,MAAM,QAAQ,EAAE,YAAY,YAAY,GAAG;GAC3C,MAAM,MAAM,EAAE,YAAY,MAAM,GAAG,KAAK;GACxC,MAAM,OAAO,EAAE,YAAY,MAAM,QAAQ,CAAC;GAC1C,MAAM,OAAO,SAAS,GAAG,IAAI,SAAS,KAAK,QAAQ,YAAY,EAAE,IAAI,CAAC;EACxE;CACF;CAGA,KAAK,MAAM,KAAK,YACd,MAAM,OAAO,SAAS,kBAAkB,EAAE,GAAG,OAAO,EAAE,KAAK;CAI7D,KAAK,MAAM,KAAK,cAAc;EAC5B,MAAM,OAAO,SAAS,cAAc,EAAE,GAAG,OAAO,EAAE,aAAa;EAC/D,MAAM,OAAO,SAAS,yBAAyB,EAAE,GAAG,OAAO,EAAE,QAAQ;CACvE;CAGA,KAAK,MAAM,KAAK,cAAc;EAC5B,MAAM,OAAO,SAAS,sBAAsB,EAAE,GAAG,OAAO,EAAE,KAAK;EAC/D,IAAI,EAAE,MACJ,MAAM,OAAO,SAAS,4BAA4B,EAAE,GAAG,YAAY,YAAY,EAAE,IAAI,CAAC;CAE1F;CAGA,KAAK,MAAM,KAAK,YAAY;EAC1B,MAAM,OAAO,SAAS,kBAAkB,EAAE,GAAG,OAAO,EAAE,KAAK;EAC3D,IAAI,EAAE,MACJ,MAAM,OAAO,SAAS,wBAAwB,EAAE,GAAG,YAAY,YAAY,EAAE,IAAI,CAAC;CAEtF;CAGA,KAAK,MAAM,MAAM,gBACf,MAAM,OAAO,SAAS,2BAA2B,GAAG,GAAG,OAAO,GAAG,KAAK;CAIxE,KAAK,MAAM,OAAO,YAChB,MAAM,OAAO,SAAS,iBAAiB,IAAI,GAAG,GAAG,IAAI,OAAO,IAAI,KAAK;CAIvE,MAAM,OAAO,SAAS,WAAW,kBAAkB,GAAG,MAAM,CAAC;CAC7D,IAAI,IAAI,QAAQ,SAAS,GACvB,MAAM,OAAO,SAAS,oBAAoB,qBAAqB,GAAG,CAAC;CAKrE,IAAI,GAAG,UAAU,MAAM,OAAO,SAAS,WAAW,GAAG,QAAQ;CAG7D,IAAI,GAAG,YAAY,MAAM,OAAO,SAAS,UAAU,iBAAiB,GAAG,UAAU,CAAC;CAClF,IAAI,GAAG,eAAe,MAAM,OAAO,SAAS,SAAS,qBAAqB,GAAG,aAAa,CAAC;CAC3F,IAAI,GAAG,kBAAkB,MAAM,OAAO,SAAS,YAAY,mBAAmB,GAAG,gBAAgB,CAAC;CAGlG,IAAI,GAAG,YAAY,MAAM,OAAO,SAAS,qBAAqB,GAAG,UAAU;CAC3E,IAAI,GAAG,cAAc,MAAM,OAAO,SAAS,8BAA8B,GAAG,YAAY;CAGxF,IAAI,GAAG,aACL,KAAK,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,MAAM,OAAO,SAAS,MAAM,KAAK;CAI/E,MAAM,WAA0B,EAAE,MAAM,CAAC,EAAE;CAC3C,SAAS,KAAK,KAAK;EACjB,IAAI;EACJ,MAAM,GAAG,OAAO;EAChB,QAAQ;CACV,CAAC;CACD,IAAI,GAAG,YACL,SAAS,KAAK,KAAK;EACjB,IAAI,MAAM,SAAS,KAAK,SAAS;EACjC,MAAM;EACN,QAAQ;CACV,CAAC;CAEH,IAAI,GAAG,eACL,SAAS,KAAK,KAAK;EACjB,IAAI,MAAM,SAAS,KAAK,SAAS;EACjC,MAAM;EACN,QAAQ;CACV,CAAC;CAEH,IAAI,GAAG,kBACL,SAAS,KAAK,KAAK;EACjB,IAAI,MAAM,SAAS,KAAK,SAAS;EACjC,MAAM;EACN,QAAQ;CACV,CAAC;CAEH,MAAM,OAAO,SAAS,eAAe,YAAY,QAAQ,CAAC;CAG1D,MAAM,WAAW,aAAa;CAC9B,WAAW,UAAU,QAAQ,0DAA0D;CACvF,WAAW,UAAU,OAAO,iBAAiB;CAI7C,IAAI,GAAG,qBACL,KAAK,MAAM,CAAC,KAAK,OAAO,GAAG,qBAAqB,WAAW,UAAU,KAAK,EAAE;CAG9E,MAAM,sBAAsB,GAAG,aAC3B,yDACA;CACJ,YAAY,UAAU,IAAI,gBAAgB,mBAAmB;CAC7D,KAAK,MAAM,KAAK,YACd,YAAY,UAAU,IAAI,EAAE,eAAe,EAAE,WAAW;CAE1D,YAAY,UAAU,IAAI,aAAa,WAAW;CAClD,IAAI,IAAI,QAAQ,SAAS,GACvB,YAAY,UAAU,IAAI,sBAAsB,mBAAmB;CAErE,IAAI,GAAG,UAAU,YAAY,UAAU,IAAI,aAAa,UAAU;CAClE,KAAK,MAAM,KAAK,YACd,YAAY,UAAU,mBAAmB,EAAE,GAAG,OAAO,UAAU;CAEjE,IAAI,aAAa,SAAS,GACxB,WAAW,UAAU,OAAO,gBAAgB;CAE9C,KAAK,MAAM,KAAK,cACd,YAAY,UAAU,eAAe,EAAE,GAAG,OAAO,aAAa;CAEhE,KAAK,MAAM,KAAK,cACd,YAAY,UAAU,uBAAuB,EAAE,GAAG,OAAO,YAAY;CAEvE,KAAK,MAAM,KAAK,YACd,YAAY,UAAU,mBAAmB,EAAE,GAAG,OAAO,EAAE,OAAO,eAAe,UAAU;CAEzF,KAAK,MAAM,MAAM,gBACf,YAAY,UAAU,4BAA4B,GAAG,GAAG,OAAO,YAAY;CAE7E,IAAI,GAAG,YACL,WAAW,UAAU,OAAO,sCAAsC;CAEpE,IAAI,GAAG,aACL,KAAK,MAAM,QAAQ,GAAG,YAAY,KAAK,GAAG;EACxC,MAAM,KAAK,GAAG,yBAAyB,IAAI,IAAI;EAC/C,IAAI,OAAO,KAAA,GAAW,YAAY,UAAU,IAAI,QAAQ,EAAE;CAC5D;CAIF,KAAK,MAAM,OAAO,WAAW;EAC3B,MAAM,OAAO,OAAO,QAAQ,sBAAsB,CAAC,CAAC,MAAM,GAAG,OAAO,MAAM,GAAG,KAAK,CAAC,EAAA,CAAG;EAGtF,IAAI,KAAK,WAAW,UAAU,KAAK,kBAAkB,IAAI;CAC3D;CACA,IAAI,GAAG,YAAY,YAAY,UAAU,IAAI,YAAY,eAAe;CACxE,IAAI,GAAG,eAAe,YAAY,UAAU,IAAI,WAAW,cAAc;CACzE,IAAI,GAAG,kBAAkB,YAAY,UAAU,IAAI,cAAc,WAAW;CAC5E,MAAM,OAAO,SAAS,mBAAmB,gBAAgB,QAAQ,CAAC;CAGlE,MAAM,OAAO,SAAS;AACxB;;;;;;AAOA,SAAS,cAAc,IAAc,SAAyB;CAC5D,IAAI,MAAM;CACV,IAAI,GAAG,aACL,KAAK,MAAM,QAAQ,GAAG,YAAY,KAAK,GAAG;EACxC,MAAM,IAAI,QAAQ,KAAK,IAAI;EAC3B,IAAI,IAAI,OAAO,KAAA,GAAW,MAAM,KAAK,IAAI,KAAK,OAAO,SAAS,EAAE,IAAI,EAAE,CAAC;CACzE;CAEF,OAAO,MAAM;AACf;;AAGA,SAAS,qBAAqB,IAAc,WAA0C;CAKpF,MAAM,SAAS,GAAG,iBAAiB,cAAc,CAAC;CAClD,MAAM,UAAU,OAAO,KAAK,MAAM,UAAU,EAAE,OAAO,IAAI,WAAW,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE;CACrF,MAAM,YAAY,GAAG,iBAAiB;CACtC,MAAM,gBAAgB,cAAc,KAAA,IAAY,kBAAkB,WAAW,SAAS,EAAE,KAAK;CAC7F,MAAM,kBAA0C;GAAG,gBAAgB;GAAK,SAAS;CAAI;CACrF,KAAK,MAAM,KAAK,QAAQ,gBAAgB,EAAE,QAAQ,EAAE;CACpD,MAAM,aAAa,SACjB,mBAAmB,MAAM,eAAe;CAE1C,MAAM,QAAkB,CACtB,iEACA,oBAAoB,cAAc,aAAa,OAAO,GAAG,UAAU,cAAc,EACnF;CACA,IAAI,GAAG,aAAa;EAClB,MAAM,KAAK,qBAAqB,GAAG,WAAW;EAC9C,IAAI,IAAI,MAAM,KAAK,EAAE;CACvB;CACA,IAAI,GAAG,aAAa;EAClB,MAAM,KAAK,qBAAqB,GAAG,WAAW;EAC9C,IAAI,IAAI,MAAM,KAAK,EAAE;CACvB;CAIA,MAAM,cAAc,4BAA4B,EAAE;CAClD,IAAI,aAAa;EACf,MAAM,KAAK,4BAA4B,WAAW;EAClD,IAAI,IAAI,MAAM,KAAK,EAAE;CACvB;CACA,IAAI,GAAG,oBAAoB;EACzB,MAAM,KAAK,4BAA4B,GAAG,kBAAkB;EAC5D,IAAI,IAAI,MAAM,KAAK,EAAE;CACvB;CAIA,IAAI,GAAG,mBAAmB,cACxB,KAAK,MAAM,QAAQ,GAAG,kBAAkB,cAAc,MAAM,KAAK,UAAU,IAAI,CAAC;CAElF,IAAI,GAAG,aAAa,GAAG,UAAU,SAAS,GAAG,MAAM,KAAK,mBAAmB,GAAG,WAAW,eAAe,CAAC;CACzG,MAAM,KAAK,UAAU;CACrB,GAAG,OAAO,SAAS,KAAK,MAAM;EAC5B,MAAM,YAAY,IAAI,UAAU,YAAY,KAAK,WAAW,IAAI,MAAM;EACtE,MAAM,MAAM,UAAU,MAAM,MAAM,IAAI;EACtC,MAAM,KAAK,gBAAgB,WAAW,IAAI,MAAM,KAAK,EAAE,aAAa,IAAI,QAAQ,GAAG,UAAU,SAAS,IAAI,IAAI;CAChH,CAAC;CACD,MAAM,KAAK,WAAW;CAQtB,IAAI,GAAG,gBAAgB;EACrB,MAAM,KAAK,wBAAwB,GAAG,cAAc;EACpD,IAAI,IAAI,MAAM,KAAK,EAAE;CACvB;CACA,IAAI,GAAG,sBAAsB,GAAG,mBAAmB,SAAS,GAAG;EAC7D,MAAM,QAAkB,CAAC,sBAAsB;EAC/C,KAAK,MAAM,MAAM,GAAG,oBAClB,MAAM,KAAK,4BAA4B,WAAW,GAAG,GAAG,EAAE,IAAI;EAEhE,MAAM,KAAK,uBAAuB;EAClC,MAAM,KAAK,MAAM,KAAK,EAAE,CAAC;CAC3B;CACA,IAAI,GAAG,aAAa,SAAS,GAAG;EAC9B,MAAM,KAAK,gBAAgB;EAC3B,KAAK,MAAM,MAAM,GAAG,cAAc;GAChC,IAAI,QAAQ,UAAU,WAAW,GAAG,IAAI,EAAE;GAC1C,IAAI,GAAG,UAAU,KAAA,GAAW,SAAS,kBAAkB,GAAG,MAAM;GAChE,IAAI,GAAG,QAAQ,SAAS;GACxB,IAAI,GAAG,YAAY,KAAA,GAAW,SAAS,aAAa,WAAW,GAAG,OAAO,EAAE;GAC3E,MAAM,KAAK,eAAe,MAAM,GAAG,WAAW,qBAAqB,GAAG,KAAK,CAAC,EAAE,eAAe;EAC/F;EACA,MAAM,KAAK,iBAAiB;CAC9B;CACA,IAAI,GAAG,gBAAgB;EACrB,MAAM,KAAK,wBAAwB,GAAG,cAAc;EACpD,IAAI,IAAI,MAAM,KAAK,EAAE;CACvB;CAKA,IAAI,GAAG,YAAY,KAAA,GACjB,MAAM,KAAK,iBAAiB,WAAW,GAAG,OAAO,EAAE,IAAI;CAEzD,IAAI,GAAG,uBAAuB,GAAG,oBAAoB,SAAS,GAC5D,MAAM,KAAK,6BAA6B,GAAG,mBAAmB,CAAC;CAEjE,IAAI,GAAG,eAAe,GAAG,YAAY,SAAS,GAAG;EAC/C,MAAM,QAAkB,CAAC,eAAe;EACxC,KAAK,MAAM,MAAM,GAAG,aAClB,MAAM,KAAK,wBAAwB,GAAG,QAAQ,UAAU,WAAW,GAAG,GAAG,EAAE,IAAI;EAEjF,MAAM,KAAK,gBAAgB;EAC3B,MAAM,KAAK,MAAM,KAAK,EAAE,CAAC;CAC3B;CACA,IAAI,GAAG,YAAY;EACjB,MAAM,MAAM,oBAAoB,GAAG,UAAU;EAC7C,IAAI,KAAK,MAAM,KAAK,GAAG;CACzB;CACA,IAAI,GAAG,iBAAiB,GAAG,cAAc,SAAS,GAChD,MAAM,KAAK,uBAAuB,GAAG,aAAa,CAAC;CAErD,IAAI,GAAG,gBAAgB;EACrB,MAAM,KAAK,wBAAwB,GAAG,cAAc;EACpD,IAAI,IAAI,MAAM,KAAK,EAAE;CACvB;CACA,IAAI,GAAG,mBAAmB,aACxB,KAAK,MAAM,QAAQ,GAAG,kBAAkB,aAAa,MAAM,KAAK,UAAU,IAAI,CAAC;CAEjF,MAAM,KAAK,aAAa;CACxB,OAAO,MAAM,KAAK,EAAE;AACtB;AAEA,SAAS,wBACP,IACoB;CACpB,IAAI,QAAQ;CACZ,IAAI,GAAG,WAAW,KAAA,GAAW,SAAS,YAAY,GAAG,OAAO;CAC5D,IAAI,GAAG,aAAa,KAAA,GAAW,SAAS,cAAc,GAAG,SAAS;CAClE,IAAI,GAAG,mBAAmB,KAAA,GAAW,SAAS,oBAAoB,GAAG,iBAAiB,MAAM,IAAI;CAChG,IAAI,GAAG,YAAY,KAAA,GAAW,SAAS,aAAa,GAAG,QAAQ;CAC/D,IAAI,GAAG,YAAY,KAAA,GAAW,SAAS,aAAa,GAAG,UAAU,MAAM,IAAI;CAC3E,IAAI,GAAG,iBAAiB,KAAA,GAAW,SAAS,kBAAkB,GAAG,aAAa;CAC9E,IAAI,GAAG,iBAAiB,KAAA,GAAW,SAAS,kBAAkB,GAAG,aAAa;CAC9E,IAAI,GAAG,kBAAkB,KAAA,GAAW,SAAS,mBAAmB,GAAG,gBAAgB,MAAM,IAAI;CAC7F,IAAI,GAAG,kBAAkB,KAAA,GAAW,SAAS,mBAAmB,GAAG,gBAAgB,MAAM,IAAI;CAC7F,IAAI,GAAG,eAAe,KAAA,GAAW,SAAS,gBAAgB,GAAG,aAAa,MAAM,IAAI;CACpF,IAAI,GAAG,mBAAmB,KAAA,GAAW,SAAS,oBAAoB,GAAG,iBAAiB,MAAM,IAAI;CAChG,IAAI,GAAG,0BAA0B,KAAA,GAAW,SAAS,2BAA2B,GAAG,sBAAsB;CACzG,IAAI,GAAG,kBAAkB,KAAA,GAAW,SAAS,mBAAmB,GAAG,gBAAgB,MAAM,IAAI;CAC7F,IAAI,MAAM,WAAW,GAAG,OAAO,KAAA;CAC/B,OAAO,UAAU,MAAM;AACzB;AAEA,SAAS,wBACP,IACoB;CACpB,IAAI,QAAQ;CACZ,IAAI,GAAG,sBAAsB,KAAA,GAAW,SAAS,uBAAuB,GAAG,kBAAkB;CAC7F,IAAI,GAAG,OAAO,WAAW,KAAK,MAAM,WAAW,GAAG,OAAO,KAAA;CACzD,IAAI,GAAG,OAAO,WAAW,GAAG,OAAO,kBAAkB,MAAM;CAC3D,MAAM,QAAkB,CAAC,kBAAkB,MAAM,EAAE;CACnD,KAAK,MAAM,KAAK,GAAG,QAAQ,MAAM,KAAK,wBAAwB,WAAW,EAAE,IAAI,EAAE,IAAI;CACrF,MAAM,KAAK,mBAAmB;CAC9B,OAAO,MAAM,KAAK,EAAE;AACtB;AAEA,SAAS,oBACP,KACoB;CACpB,IAAI,QAAQ;CACZ,IAAI,IAAI,UAAU,KAAA,GAAW,SAAS,WAAW,IAAI,QAAQ,MAAM,IAAI;CACvE,IAAI,IAAI,SAAS,KAAA,GAAW,SAAS,UAAU,IAAI,KAAK;CACxD,IAAI,MAAM,WAAW,GAAG,OAAO,KAAA;CAC/B,OAAO,cAAc,MAAM;AAC7B;AAEA,SAAS,uBACP,MACQ;CACR,MAAM,QAAkB,CAAC,iBAAiB;CAC1C,KAAK,MAAM,KAAK,MAAM;EACpB,IAAI,QAAQ;EACZ,IAAI,EAAE,iBAAiB,KAAA,GAAW,SAAS,kBAAkB,WAAW,EAAE,YAAY,EAAE;EACxF,IAAI,EAAE,SAAS,KAAA,GAAW,SAAS,UAAU,WAAW,EAAE,IAAI,EAAE;EAChE,IAAI,EAAE,QAAQ,KAAA,GAAW,SAAS,SAAS,WAAW,EAAE,GAAG,EAAE;EAC7D,MAAM,KAAK,gBAAgB,MAAM,GAAG;CACtC;CACA,MAAM,KAAK,kBAAkB;CAC7B,OAAO,MAAM,KAAK,EAAE;AACtB;AAEA,SAAS,wBACP,IACoB;CACpB,IAAI,QAAQ;CACZ,IAAI,GAAG,gBAAgB,KAAA,GAAW,SAAS,iBAAiB,GAAG,cAAc,MAAM,IAAI;CACvF,IAAI,GAAG,cAAc,KAAA,GAAW,SAAS,eAAe,GAAG,YAAY,MAAM,IAAI;CACjF,IAAI,GAAG,oBAAoB,KAAA,GAAW,SAAS,qBAAqB,GAAG,kBAAkB,MAAM,IAAI;CACnG,IAAI,GAAG,eAAe,KAAA,GAAW,SAAS,gBAAgB,GAAG,aAAa,MAAM,IAAI;CACpF,IAAI,MAAM,WAAW,GAAG,OAAO,KAAA;CAC/B,OAAO,kBAAkB,MAAM;AACjC;AAEA,SAAS,qBACP,IACoB;CACpB,IAAI,QAAQ;CACZ,IAAI,GAAG,wBAAwB,KAAA,GAC7B,SAAS,yBAAyB,GAAG,sBAAsB,MAAM,IAAI;CACvE,IAAI,GAAG,aAAa,KAAA,GAAW,SAAS,cAAc,WAAW,GAAG,QAAQ,EAAE;CAC9E,IAAI,GAAG,wBAAwB,KAAA,GAC7B,SAAS,yBAAyB,WAAW,GAAG,mBAAmB,EAAE;CACvE,IAAI,GAAG,kBAAkB,KAAA,GAAW,SAAS,mBAAmB,WAAW,GAAG,aAAa,EAAE;CAC7F,IAAI,GAAG,cAAc,KAAA,GAAW,SAAS,eAAe,WAAW,GAAG,SAAS,EAAE;CACjF,IAAI,GAAG,cAAc,KAAA,GAAW,SAAS,eAAe,WAAW,GAAG,SAAS,EAAE;CACjF,IAAI,GAAG,cAAc,KAAA,GAAW,SAAS,eAAe,GAAG,UAAU;CACrE,IAAI,MAAM,WAAW,GAAG,OAAO,KAAA;CAC/B,OAAO,eAAe,MAAM;AAC9B;AAEA,SAAS,qBACP,IACoB;CACpB,IAAI,QAAQ;CACZ,IAAI,GAAG,YAAY,KAAA,GAAW,SAAS,aAAa,WAAW,GAAG,OAAO,EAAE;CAC3E,IAAI,GAAG,eAAe,KAAA,GAAW,SAAS,gBAAgB,WAAW,GAAG,UAAU,EAAE;CACpF,IAAI,GAAG,iBAAiB,KAAA,GAAW,SAAS,kBAAkB,WAAW,GAAG,YAAY,EAAE;CAC1F,IAAI,GAAG,aAAa,KAAA,GAAW,SAAS,cAAc,WAAW,GAAG,QAAQ,EAAE;CAC9E,IAAI,GAAG,aAAa,KAAA,GAAW,SAAS,cAAc,WAAW,GAAG,QAAQ,EAAE;CAC9E,IAAI,MAAM,WAAW,GAAG,OAAO,KAAA;CAC/B,OAAO,eAAe,MAAM;AAC9B;AAEA,SAAS,4BACP,IAC6E;CAC7E,MAAM,WAAW,GAAG;CACpB,IAAI,UAAU;EAEZ,IAAI,GAAG,YAAY,SAAS,aAAa,KAAA,GACvC,OAAO;GAAE,GAAG;GAAU,UAAU;EAAK;EAEvC,OAAO;CACT;CACA,IAAI,GAAG,UAAU,OAAO,EAAE,UAAU,KAAK;AAE3C;AAEA,SAAS,4BACP,IACoB;CACpB,IAAI,QAAQ;CAkBZ,KAAK,MAAM,KAAK;EAhBd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAEqB,GAAG;EACxB,MAAM,IAAI,GAAG;EACb,IAAI,MAAM,KAAA,GAAW,SAAS,IAAI,EAAE,IAAI,IAAI,MAAM,IAAI;CACxD;CACA,IAAI,GAAG,gBAAgB,KAAA,GAAW,SAAS,iBAAiB,GAAG,YAAY;CAC3E,IAAI,GAAG,gBAAgB,KAAA,GAAW,SAAS,iBAAiB,GAAG,YAAY;CAC3E,IAAI,GAAG,aAAa,KAAA,GAAW,SAAS,cAAc,WAAW,GAAG,QAAQ,EAAE;CAC9E,IAAI,GAAG,wBAAwB,KAAA,GAAW,SAAS,yBAAyB,GAAG,oBAAoB;CACnG,IAAI,MAAM,WAAW,GAAG,OAAO,KAAA;CAC/B,OAAO,cAAc,MAAM;AAC7B;AAEA,SAAS,6BACP,OACQ;CACR,MAAM,QAAkB,CAAC,uBAAuB;CAChD,KAAK,MAAM,KAAK,OAAO;EACrB,IAAI,QAAQ,UAAU,WAAW,EAAE,IAAI,EAAE,UAAU,WAAW,EAAE,IAAI,EAAE;EACtE,IAAI,EAAE,eAAe,KAAA,GAAW,SAAS,gBAAgB,EAAE,aAAa,MAAM,IAAI;EAClF,IAAI,EAAE,kBAAkB,KAAA,GAAW,SAAS,mBAAmB,EAAE,cAAc;EAC/E,IAAI,EAAE,oBAAoB,KAAA,GAAW,SAAS,qBAAqB,EAAE,kBAAkB,MAAM,IAAI;EACjG,IAAI,EAAE,aAAa,KAAA,GAAW,SAAS,cAAc,EAAE,WAAW,MAAM,IAAI;EAC5E,IAAI,EAAE,iBAAiB,KAAA,GAAW,SAAS,kBAAkB,EAAE,eAAe,MAAM,IAAI;EACxF,IAAI,EAAE,yBAAyB,KAAA,GAC7B,SAAS,0BAA0B,EAAE,uBAAuB,MAAM,IAAI;EACxE,IAAI,EAAE,wBAAwB,KAAA,GAC5B,SAAS,yBAAyB,EAAE,sBAAsB,MAAM,IAAI;EACtE,IAAI,EAAE,cAAc,KAAA,GAAW,SAAS,eAAe,EAAE,YAAY,MAAM,IAAI;EAC/E,IAAI,EAAE,cAAc,KAAA,GAAW,SAAS,eAAe,EAAE,YAAY,MAAM,IAAI;EAC/E,IAAI,EAAE,yBAAyB,KAAA,GAC7B,SAAS,0BAA0B,EAAE,uBAAuB,MAAM,IAAI;EACxE,IAAI,EAAE,uBAAuB,KAAA,GAC3B,SAAS,wBAAwB,EAAE,qBAAqB,MAAM,IAAI;EACpE,IAAI,EAAE,kBAAkB,KAAA,GAAW,SAAS,mBAAmB,EAAE,gBAAgB,MAAM,IAAI;EAC3F,IAAI,EAAE,YAAY,KAAA,GAAW,SAAS,aAAa,EAAE,QAAQ;EAC7D,IAAI,EAAE,YAAY,KAAA,GAAW,SAAS,aAAa,EAAE,QAAQ;EAC7D,SAAS,iBAAiB,EAAE,YAAY,kBAAkB,EAAE,aAAa;EACzE,IAAI,EAAE,aAAa,KAAA,GAAW,SAAS,cAAc,EAAE,SAAS;EAChE,SAAS,mBAAmB,EAAE,cAAc;EAC5C,IAAI,EAAE,mBAAmB,KAAA,GAAW,SAAS,oBAAoB,EAAE,iBAAiB,MAAM,IAAI;EAC9F,IAAI,EAAE,kBAAkB,KAAA,GAAW,SAAS,mBAAmB,EAAE,gBAAgB,MAAM,IAAI;EAC3F,IAAI,EAAE,iBAAiB,KAAA,GAAW,SAAS,kBAAkB,EAAE,aAAa;EAC5E,IAAI,EAAE,gBAAgB,KAAA,GAAW,SAAS,iBAAiB,EAAE,YAAY;EACzE,MAAM,KAAK,sBAAsB,MAAM,GAAG;CAC5C;CACA,MAAM,KAAK,wBAAwB;CACnC,OAAO,MAAM,KAAK,EAAE;AACtB;AAEA,SAAS,mBACP,OACA,UACQ;CACR,MAAM,QAAkB,CAAC,aAAa;CACtC,KAAK,MAAM,KAAK,OAAO;EACrB,IAAI,QAAQ;EACZ,IAAI,EAAE,eAAe,KAAA,GAAW,SAAS,gBAAgB,EAAE,WAAW;EACtE,IAAI,EAAE,cAAc,KAAA,GAAW,SAAS,eAAe,EAAE,YAAY,MAAM,IAAI;EAC/E,IAAI,EAAE,yBAAyB,KAAA,GAC7B,SAAS,0BAA0B,EAAE,uBAAuB,MAAM,IAAI;EACxE,IAAI,EAAE,uBAAuB,KAAA,GAC3B,SAAS,wBAAwB,EAAE,qBAAqB,MAAM,IAAI;EACpE,IAAI,EAAE,kBAAkB,KAAA,GAAW,SAAS,mBAAmB,EAAE,gBAAgB,MAAM,IAAI;EAC3F,IAAI,EAAE,YAAY,KAAA,GAAW,SAAS,aAAa,EAAE,QAAQ;EAC7D,IAAI,EAAE,YAAY,KAAA,GAAW,SAAS,aAAa,EAAE,QAAQ;EAC7D,IAAI,EAAE,gBAAgB,KAAA,GAAW,SAAS,iBAAiB,EAAE,YAAY;EACzE,IAAI,EAAE,iBAAiB,KAAA,GAAW,SAAS,kBAAkB,EAAE,aAAa;EAC5E,IAAI,EAAE,aAAa,KAAA,GAAW,SAAS,cAAc,EAAE,SAAS;EAChE,IAAI,EAAE,eAAe,KAAA,GAAW,SAAS,gBAAgB,EAAE,WAAW;EACtE,IAAI,EAAE,cAAc,KAAA,GAAW,SAAS,eAAe,EAAE,UAAU;EACnE,IAAI,EAAE,2BAA2B,KAAA,GAC/B,SAAS,4BAA4B,EAAE,yBAAyB,MAAM,IAAI;EAG5E,KAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,EAAE,YAAY,CAAC,CAAC,GAAG;GAC7D,MAAM,EAAE,IAAI,UAAU,WAAW,KAAK;GACtC,MAAM,SAAS,SAAS;GACxB,IAAI,WAAW,KAAA,GAAW;GAC1B,SAAS,IAAI,OAAO,GAAG,MAAM,IAAI,WAAW,KAAK,EAAE;EACrD;EACA,MAAM,KAAK,gBAAgB,MAAM,GAAG;CACtC;CACA,MAAM,KAAK,cAAc;CACzB,OAAO,MAAM,KAAK,EAAE;AACtB;AAEA,SAAS,4BACP,IACoB;CACpB,IAAI,QAAQ;CAaZ,KAAK,MAAM,KAAK;EAXd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAEqB,GAAG;EACxB,MAAM,IAAI,GAAG;EACb,IAAI,MAAM,KAAA,GAAW,SAAS,IAAI,EAAE,IAAI,WAAW,CAAC,EAAE;CACxD;CACA,IAAI,GAAG,sBAAsB,KAAA,GAAW,SAAS,uBAAuB,GAAG,kBAAkB;CAC7F,IAAI,GAAG,uBAAuB,KAAA,GAAW,SAAS,wBAAwB,GAAG,mBAAmB;CAChG,IAAI,GAAG,kBAAkB,KAAA,GAAW,SAAS,mBAAmB,GAAG,gBAAgB,MAAM,IAAI;CAC7F,IAAI,GAAG,gBAAgB,KAAA,GAAW,SAAS,iBAAiB,GAAG,cAAc,MAAM,IAAI;CACvF,IAAI,GAAG,iBAAiB,KAAA,GAAW,SAAS,kBAAkB,GAAG,eAAe,MAAM,IAAI;CAC1F,IAAI,MAAM,WAAW,GAAG,OAAO,KAAA;CAC/B,OAAO,sBAAsB,MAAM;AACrC;AAMA,MAAM,aAAa;AACnB,MAAM,aAAa;;;;;;;;AASnB,SAAS,mBACP,MACA,iBACQ;CACR,MAAM,QAAQA,aAAiB,MAAM;EACnC,gBAAgB;EAChB,GAAI,kBAAkB,EAAE,gBAAgB,IAAI,CAAC;CAC/C,CAAC;CACD,OAAO,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK;AACvC"}
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import type { CellValue } from '../cell/cell.js';
|
|
2
2
|
import type { XlsxSink } from '../io/sink.js';
|
|
3
3
|
import { type CellStyleSpec } from '../styles/stylesheet.js';
|
|
4
|
+
import { type CompressionLevel } from '../zip/writer.js';
|
|
4
5
|
export interface WriteOnlyOptions {
|
|
5
6
|
/** Reserved — currently ignored (the buffered backend doesn't honour it). */
|
|
6
7
|
estimatedMaxRow?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Last-modified timestamp for every ZIP entry. Same reproducibility story as
|
|
10
|
+
* `SaveOptions.mtime`: unset, fflate stamps the wall clock per entry and two
|
|
11
|
+
* runs over identical rows differ in bytes. Recorded as the date's UTC wall
|
|
12
|
+
* time, to a two-second resolution, and the year has to fall in 1980-2099.
|
|
13
|
+
*/
|
|
14
|
+
mtime?: Date;
|
|
15
|
+
/** Deflate level, 0 (no compression) to 9 (smallest). Defaults to fflate's own 6. */
|
|
16
|
+
compressionLevel?: CompressionLevel;
|
|
7
17
|
}
|
|
8
18
|
/**
|
|
9
19
|
* A cell's look on the write-only path. Identical to the modelled writer's
|
|
@@ -38,4 +48,4 @@ export interface WriteOnlyWorkbook {
|
|
|
38
48
|
abort(cause?: unknown): void;
|
|
39
49
|
}
|
|
40
50
|
/** Open a workbook for streaming write-only output. */
|
|
41
|
-
export declare function createWriteOnlyWorkbook(sink: XlsxSink,
|
|
51
|
+
export declare function createWriteOnlyWorkbook(sink: XlsxSink, opts?: WriteOnlyOptions): Promise<WriteOnlyWorkbook>;
|
package/dist/streaming.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import { ct as buildXfPatch, ht as makeStylesheet, it as addCellXf, lt as defaul
|
|
|
10
10
|
import { D as makeSharedStrings, S as validateSheetTitle, j as sharedStringsToBytes, k as parseSharedStringsXml } from "./workbook-B15-T4cs.mjs";
|
|
11
11
|
import { i as parseStylesheetXml, n as parseDate1904, r as resolveRelTarget } from "./load-CJdGh50R.mjs";
|
|
12
12
|
import { t as openZip } from "./reader-BNaUTVCy.mjs";
|
|
13
|
-
import { t as createZipWriter } from "./writer-
|
|
13
|
+
import { t as createZipWriter } from "./writer-Z7cF_CBk.mjs";
|
|
14
14
|
import { n as iterParse, t as utf8ByteLength } from "./utf8-Tn3nzyik.mjs";
|
|
15
15
|
//#region src/streaming/read-only.ts
|
|
16
16
|
const SHEET_TAG = `{${SHEET_MAIN_NS}}sheet`;
|
|
@@ -477,7 +477,7 @@ const makeWriteOnlyWorksheet = (state, title, sheetId) => {
|
|
|
477
477
|
* closure rather than on a class instance per the project-wide "no classes"
|
|
478
478
|
* rule (CLAUDE.md).
|
|
479
479
|
*/
|
|
480
|
-
const makeWriteOnlyWorkbook = (sink) => {
|
|
480
|
+
const makeWriteOnlyWorkbook = (sink, zipOpts) => {
|
|
481
481
|
const styles = makeStylesheet();
|
|
482
482
|
addCellXf(styles, defaultCellXf());
|
|
483
483
|
const state = {
|
|
@@ -486,7 +486,7 @@ const makeWriteOnlyWorkbook = (sink) => {
|
|
|
486
486
|
sheets: [],
|
|
487
487
|
finalised: false,
|
|
488
488
|
hasOpenWorksheet: false,
|
|
489
|
-
writer: createZipWriter(sink)
|
|
489
|
+
writer: createZipWriter(sink, zipOpts)
|
|
490
490
|
};
|
|
491
491
|
const addWorksheet = async (title) => {
|
|
492
492
|
if (state.finalised) throw new OpenXmlIoError("addWorksheet: workbook already finalised");
|
|
@@ -574,8 +574,8 @@ const serializeWorkbookXml = (sheets) => {
|
|
|
574
574
|
return parts.join("");
|
|
575
575
|
};
|
|
576
576
|
/** Open a workbook for streaming write-only output. */
|
|
577
|
-
async function createWriteOnlyWorkbook(sink,
|
|
578
|
-
return makeWriteOnlyWorkbook(sink);
|
|
577
|
+
async function createWriteOnlyWorkbook(sink, opts = {}) {
|
|
578
|
+
return makeWriteOnlyWorkbook(sink, opts);
|
|
579
579
|
}
|
|
580
580
|
//#endregion
|
|
581
581
|
export { createWriteOnlyWorkbook, loadWorkbookStream };
|
package/dist/streaming.mjs.map
CHANGED
|
@@ -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 { makeSharedStrings, parseSharedStringsXml, type SharedStringsTable } from '../workbook/shared-strings.js';\nimport { ARC_CONTENT_TYPES, ARC_ROOT_RELS, ARC_SHARED_STRINGS, ARC_STYLE, REL_NS, SHEET_MAIN_NS } from '../xml/namespaces.js';\nimport { findById, relsFromBytes } from '../packaging/relationships.js';\nimport { manifestFromBytes } from '../packaging/manifest.js';\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\nimport type { DecompressionLimits } from '../zip/decompression-guard.js';\nimport { type ZipArchive, openZip } from '../zip/reader.js';\nimport type { CellValue, ExcelErrorCode } from '../cell/cell.js';\nimport { ERROR_CODES } from '../utils/inference.js';\nimport { iterParse, type SaxEvent, type SaxInput } from '../xml/iterparse.js';\nimport { parseXml } from '../xml/parser.js';\nimport { findChild, findChildren, type XmlNode } from '../xml/tree.js';\nimport type { XlsxSource } from '../io/source.js';\nimport { coordinateToTuple } from '../utils/coordinate.js';\nimport { type Stylesheet, makeStylesheet } from '../styles/stylesheet.js';\nimport { parseStylesheetXml } from '../styles/stylesheet-reader.js';\nimport { parseDate1904, resolveRelTarget } from '../io/load.js';\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 /** Date1904 mode toggles between Excel's two epoch systems. */\n date1904: boolean;\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 = (root: XmlNode, workbookPath: string, archive: ZipArchive): SheetEntry[] => {\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 date1904: boolean,\n archive: ZipArchive,\n entries: ReadonlyMap<string, SheetEntry>,\n sst: ReadonlyArray<string>,\n): ReadOnlyWorkbook => ({\n sheetNames,\n styles,\n date1904,\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 workbookRoot = parseXml(archive.read(workbookPath));\n const sheetEntries = parseSheetList(workbookRoot, 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 parseDate1904(workbookRoot),\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.js';\nimport type { XlsxSink } from '../io/sink.js';\nimport { addDefault, addOverride, makeManifest, manifestToBytes } from '../packaging/manifest.js';\nimport { makeRelationships, relsToBytes } from '../packaging/relationships.js';\nimport {\n addCellXf,\n buildXfPatch,\n type CellStyleSpec,\n defaultCellXf,\n makeStylesheet,\n type Stylesheet,\n} from '../styles/stylesheet.js';\nimport { stylesheetToBytes } from '../styles/stylesheet-writer.js';\nimport { escapeXmlAttr } from '../utils/escape.js';\nimport { OpenXmlIoError } from '../utils/exceptions.js';\nimport { utf8ByteLength } from '../utils/utf8.js';\nimport { makeSharedStrings, sharedStringsToBytes } from '../workbook/shared-strings.js';\nimport { validateSheetTitle } from '../workbook/workbook.js';\nimport { serializeCell } from '../worksheet/writer.js';\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.js';\nimport { createZipWriter } from '../zip/writer.js';\n\nconst escapeAttr = escapeXmlAttr;\n\nexport interface WriteOnlyOptions {\n /** Reserved — currently ignored (the buffered backend doesn't honour it). */\n estimatedMaxRow?: number;\n}\n\n/**\n * A cell's look on the write-only path. Identical to the modelled writer's\n * {@link CellStyleSpec}: the two writers resolve a spec to an xf through the\n * same builder, so an axis added to one is honoured by both.\n */\nexport type WriteOnlyStyle = CellStyleSpec;\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/**\n * Allocate a CellXf id for a style spec. `defaultCellXf` omits `xfId`, which\n * skips the cellStyleXfs bounds check and matches what Excel emits when there\n * is no parent style.\n */\nconst allocateXfId = (ss: Stylesheet, style: WriteOnlyStyle): number =>\n addCellXf(ss, { ...defaultCellXf(), ...buildXfPatch(ss, style) });\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.js').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, styles: state.styles, 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.js';\nimport { ARC_CONTENT_TYPES, ARC_ROOT_RELS, ARC_SHARED_STRINGS, ARC_STYLE, REL_NS, SHEET_MAIN_NS } from '../xml/namespaces.js';\nimport { findById, relsFromBytes } from '../packaging/relationships.js';\nimport { manifestFromBytes } from '../packaging/manifest.js';\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\nimport type { DecompressionLimits } from '../zip/decompression-guard.js';\nimport { type ZipArchive, openZip } from '../zip/reader.js';\nimport type { CellValue, ExcelErrorCode } from '../cell/cell.js';\nimport { ERROR_CODES } from '../utils/inference.js';\nimport { iterParse, type SaxEvent, type SaxInput } from '../xml/iterparse.js';\nimport { parseXml } from '../xml/parser.js';\nimport { findChild, findChildren, type XmlNode } from '../xml/tree.js';\nimport type { XlsxSource } from '../io/source.js';\nimport { coordinateToTuple } from '../utils/coordinate.js';\nimport { type Stylesheet, makeStylesheet } from '../styles/stylesheet.js';\nimport { parseStylesheetXml } from '../styles/stylesheet-reader.js';\nimport { parseDate1904, resolveRelTarget } from '../io/load.js';\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 /** Date1904 mode toggles between Excel's two epoch systems. */\n date1904: boolean;\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 = (root: XmlNode, workbookPath: string, archive: ZipArchive): SheetEntry[] => {\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 date1904: boolean,\n archive: ZipArchive,\n entries: ReadonlyMap<string, SheetEntry>,\n sst: ReadonlyArray<string>,\n): ReadOnlyWorkbook => ({\n sheetNames,\n styles,\n date1904,\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 workbookRoot = parseXml(archive.read(workbookPath));\n const sheetEntries = parseSheetList(workbookRoot, 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 parseDate1904(workbookRoot),\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.js';\nimport type { XlsxSink } from '../io/sink.js';\nimport { addDefault, addOverride, makeManifest, manifestToBytes } from '../packaging/manifest.js';\nimport { makeRelationships, relsToBytes } from '../packaging/relationships.js';\nimport {\n addCellXf,\n buildXfPatch,\n type CellStyleSpec,\n defaultCellXf,\n makeStylesheet,\n type Stylesheet,\n} from '../styles/stylesheet.js';\nimport { stylesheetToBytes } from '../styles/stylesheet-writer.js';\nimport { escapeXmlAttr } from '../utils/escape.js';\nimport { OpenXmlIoError } from '../utils/exceptions.js';\nimport { utf8ByteLength } from '../utils/utf8.js';\nimport { makeSharedStrings, sharedStringsToBytes } from '../workbook/shared-strings.js';\nimport { validateSheetTitle } from '../workbook/workbook.js';\nimport { serializeCell } from '../worksheet/writer.js';\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.js';\nimport { type CompressionLevel, createZipWriter, type ZipWriterOptions } from '../zip/writer.js';\n\nconst escapeAttr = escapeXmlAttr;\n\nexport interface WriteOnlyOptions {\n /** Reserved — currently ignored (the buffered backend doesn't honour it). */\n estimatedMaxRow?: number;\n /**\n * Last-modified timestamp for every ZIP entry. Same reproducibility story as\n * `SaveOptions.mtime`: unset, fflate stamps the wall clock per entry and two\n * runs over identical rows differ in bytes. Recorded as the date's UTC wall\n * time, to a two-second resolution, and the year has to fall in 1980-2099.\n */\n mtime?: Date;\n /** Deflate level, 0 (no compression) to 9 (smallest). Defaults to fflate's own 6. */\n compressionLevel?: CompressionLevel;\n}\n\n/**\n * A cell's look on the write-only path. Identical to the modelled writer's\n * {@link CellStyleSpec}: the two writers resolve a spec to an xf through the\n * same builder, so an axis added to one is honoured by both.\n */\nexport type WriteOnlyStyle = CellStyleSpec;\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/**\n * Allocate a CellXf id for a style spec. `defaultCellXf` omits `xfId`, which\n * skips the cellStyleXfs bounds check and matches what Excel emits when there\n * is no parent style.\n */\nconst allocateXfId = (ss: Stylesheet, style: WriteOnlyStyle): number =>\n addCellXf(ss, { ...defaultCellXf(), ...buildXfPatch(ss, style) });\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.js').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, styles: state.styles, 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, zipOpts: ZipWriterOptions): 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, zipOpts),\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, opts);\n}\n\n"],"mappings":"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"}
|
|
@@ -89,6 +89,31 @@ function applyZip64EntryCountPatch(finalChunk, totalEntries) {
|
|
|
89
89
|
//#endregion
|
|
90
90
|
//#region src/zip/writer.ts
|
|
91
91
|
const ZIP32_MAX_ENTRIES = 65535;
|
|
92
|
+
const LOCAL_TIMESTAMP_OFFSET = 10;
|
|
93
|
+
const CENTRAL_TIMESTAMP_OFFSET = 12;
|
|
94
|
+
const CENTRAL_HEADER_SIZE = 46;
|
|
95
|
+
const CENTRAL_NAME_LENGTH_OFFSET = 28;
|
|
96
|
+
const CENTRAL_EXTRA_LENGTH_OFFSET = 30;
|
|
97
|
+
const CENTRAL_COMMENT_LENGTH_OFFSET = 32;
|
|
98
|
+
/** Supported year range of the fflate ZIP backend. */
|
|
99
|
+
const MIN_ZIP_YEAR = 1980;
|
|
100
|
+
const MAX_ZIP_YEAR = 2099;
|
|
101
|
+
/** Encode UTC components directly: local dates cannot represent a DST gap. */
|
|
102
|
+
const toZipStamp = (mtime) => {
|
|
103
|
+
const ms = mtime.getTime();
|
|
104
|
+
if (Number.isNaN(ms)) throw new OpenXmlIoError("createZipWriter: mtime is an invalid Date");
|
|
105
|
+
const year = mtime.getUTCFullYear();
|
|
106
|
+
if (year < MIN_ZIP_YEAR || year > MAX_ZIP_YEAR) throw new OpenXmlIoError(`createZipWriter: mtime ${mtime.toISOString()} is outside the supported ZIP timestamp range (${MIN_ZIP_YEAR}-${MAX_ZIP_YEAR})`);
|
|
107
|
+
return year - MIN_ZIP_YEAR << 25 | mtime.getUTCMonth() + 1 << 21 | mtime.getUTCDate() << 16 | mtime.getUTCHours() << 11 | mtime.getUTCMinutes() << 5 | mtime.getUTCSeconds() >> 1;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* fflate looks the level up in a table and falls back to 6 for anything off
|
|
111
|
+
* the end, so an out-of-range level would quietly produce default output. The
|
|
112
|
+
* union type catches that for TypeScript callers; this catches it for the rest.
|
|
113
|
+
*/
|
|
114
|
+
const validateCompressionLevel = (level) => {
|
|
115
|
+
if (!Number.isInteger(level) || level < 0 || level > 9) throw new OpenXmlIoError(`createZipWriter: compressionLevel must be an integer in [0, 9]; got ${level}`);
|
|
116
|
+
};
|
|
92
117
|
/**
|
|
93
118
|
* ZIP writer backed by fflate's streaming `Zip` class. Entries are pushed
|
|
94
119
|
* through `ZipDeflate` / `ZipPassThrough` streams as they arrive, so peak
|
|
@@ -102,8 +127,20 @@ const ZIP32_MAX_ENTRIES = 65535;
|
|
|
102
127
|
* (`toFile`, `toWritable`) forward each chunk to disk / the wrapped writable
|
|
103
128
|
* without ever holding the full archive resident. Either kind plugs in here.
|
|
104
129
|
*/
|
|
105
|
-
function createZipWriter(sink) {
|
|
130
|
+
function createZipWriter(sink, opts = {}) {
|
|
131
|
+
if (opts.compressionLevel !== void 0) validateCompressionLevel(opts.compressionLevel);
|
|
132
|
+
const stamp = opts.mtime === void 0 ? void 0 : toZipStamp(opts.mtime);
|
|
106
133
|
const writer = sink.toBytes();
|
|
134
|
+
const deflateOpts = opts.compressionLevel === void 0 ? void 0 : { level: opts.compressionLevel };
|
|
135
|
+
let pendingLocalHeader = false;
|
|
136
|
+
const newEntry = (path, compress) => {
|
|
137
|
+
const file = compress ? new ZipDeflate(path, deflateOpts) : new ZipPassThrough(path);
|
|
138
|
+
if (stamp !== void 0) {
|
|
139
|
+
file.mtime = new Date(2e3, 0, 1);
|
|
140
|
+
pendingLocalHeader = true;
|
|
141
|
+
}
|
|
142
|
+
return file;
|
|
143
|
+
};
|
|
107
144
|
let finalised;
|
|
108
145
|
let endCalled = false;
|
|
109
146
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -118,8 +155,23 @@ function createZipWriter(sink) {
|
|
|
118
155
|
errors.push(err instanceof Error ? err : new Error(String(err)));
|
|
119
156
|
return;
|
|
120
157
|
}
|
|
121
|
-
if (chunk && chunk.byteLength > 0)
|
|
122
|
-
|
|
158
|
+
if (chunk && chunk.byteLength > 0) {
|
|
159
|
+
if (stamp !== void 0 && (final || pendingLocalHeader)) {
|
|
160
|
+
const view = new DataView(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
161
|
+
if (final) {
|
|
162
|
+
let offset = 0;
|
|
163
|
+
for (let i = 0; i < seen.size; i++) {
|
|
164
|
+
view.setUint32(offset + CENTRAL_TIMESTAMP_OFFSET, stamp, true);
|
|
165
|
+
offset += CENTRAL_HEADER_SIZE + view.getUint16(offset + CENTRAL_NAME_LENGTH_OFFSET, true) + view.getUint16(offset + CENTRAL_EXTRA_LENGTH_OFFSET, true) + view.getUint16(offset + CENTRAL_COMMENT_LENGTH_OFFSET, true);
|
|
166
|
+
}
|
|
167
|
+
} else if (pendingLocalHeader) {
|
|
168
|
+
view.setUint32(LOCAL_TIMESTAMP_OFFSET, stamp, true);
|
|
169
|
+
pendingLocalHeader = false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (final) finalChunk = chunk;
|
|
173
|
+
else writer.write(chunk);
|
|
174
|
+
}
|
|
123
175
|
if (final && zipFinishResolve) {
|
|
124
176
|
zipFinishResolve();
|
|
125
177
|
zipFinishResolve = void 0;
|
|
@@ -132,11 +184,11 @@ function createZipWriter(sink) {
|
|
|
132
184
|
if (seen.has(path)) throw new OpenXmlIoError(`createZipWriter: duplicate entry "${path}"`);
|
|
133
185
|
};
|
|
134
186
|
return {
|
|
135
|
-
async addEntry(path, bytes,
|
|
187
|
+
async addEntry(path, bytes, entryOpts) {
|
|
136
188
|
if (!(bytes instanceof Uint8Array)) throw new OpenXmlIoError("createZipWriter: ReadableStream entries are not yet supported (deferred to streaming writer)");
|
|
137
189
|
guardAdd(path);
|
|
138
190
|
seen.add(path);
|
|
139
|
-
const file =
|
|
191
|
+
const file = newEntry(path, entryOpts?.compress ?? true);
|
|
140
192
|
try {
|
|
141
193
|
zip.add(file);
|
|
142
194
|
file.push(bytes, true);
|
|
@@ -145,11 +197,11 @@ function createZipWriter(sink) {
|
|
|
145
197
|
}
|
|
146
198
|
if (errors.length > 0) throw new OpenXmlIoError("createZipWriter: stream error during addEntry", { cause: errors[0] });
|
|
147
199
|
},
|
|
148
|
-
addStreamingEntry(path,
|
|
200
|
+
addStreamingEntry(path, entryOpts) {
|
|
149
201
|
guardAdd(path);
|
|
150
202
|
seen.add(path);
|
|
151
203
|
streamingOpen = true;
|
|
152
|
-
const file =
|
|
204
|
+
const file = newEntry(path, entryOpts?.compress ?? true);
|
|
153
205
|
try {
|
|
154
206
|
zip.add(file);
|
|
155
207
|
} catch (cause) {
|
|
@@ -218,4 +270,4 @@ function createZipWriter(sink) {
|
|
|
218
270
|
//#endregion
|
|
219
271
|
export { createZipWriter as t };
|
|
220
272
|
|
|
221
|
-
//# sourceMappingURL=writer-
|
|
273
|
+
//# sourceMappingURL=writer-Z7cF_CBk.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writer-Z7cF_CBk.mjs","names":["ZIP32_MAX_ENTRIES"],"sources":["../src/zip/zip64-patch.ts","../src/zip/writer.ts"],"sourcesContent":["// ZIP64 post-processing for archives whose entry count exceeds the\n// 16-bit ZIP32 cap (65535). fflate's `Zip` writer always emits a\n// plain ZIP32 EOCD; for archives with more entries we keep its\n// per-entry LFH/CDH layout (correct as long as no individual size or\n// offset overflows 32 bits) and splice in a ZIP64 End-of-Central-\n// Directory record + locator before the EOCD, then patch the EOCD's\n// entry-count fields with the 0xFFFF sentinel that signals \"consult\n// the ZIP64 record for the real values\".\n//\n// The input is fflate's *final* chunk — the trailing [CD | EOCD] block\n// it emits in one ondata callback when `Zip.end()` is called. The\n// preceding entry-data chunks have already been streamed to the sink,\n// so we operate on the final chunk in isolation. The global EOCD\n// offset (needed for the ZIP64 locator) is derivable from cd_offset +\n// cd_size carried in the EOCD itself, so no external bookkeeping is\n// required.\n//\n// Out of scope: per-entry sizes or central-directory offsets > 4 GiB.\n// xlsx archives don't approach those limits in practice; we throw a\n// clear error if we detect overflow there.\n\nimport { OpenXmlIoError, OpenXmlNotImplementedError } from '../utils/exceptions.js';\n\nconst ZIP32_MAX_ENTRIES = 0xffff;\nconst ZIP32_MAX_U32 = 0xffffffff;\nconst SIG_EOCD = 0x06054b50;\nconst SIG_ZIP64_EOCD = 0x06064b50;\nconst SIG_ZIP64_EOCD_LOCATOR = 0x07064b50;\n\nconst ZIP64_EOCD_SIZE = 56;\nconst ZIP64_LOCATOR_SIZE = 20;\n\nconst u16 = (b: Uint8Array, o: number): number => (b[o] ?? 0) | ((b[o + 1] ?? 0) << 8);\n\nconst u32 = (b: Uint8Array, o: number): number => {\n const v0 = b[o] ?? 0;\n const v1 = b[o + 1] ?? 0;\n const v2 = b[o + 2] ?? 0;\n const v3 = b[o + 3] ?? 0;\n return (v0 | (v1 << 8) | (v2 << 16) | (v3 << 24)) >>> 0;\n};\n\nconst writeU16 = (b: Uint8Array, o: number, v: number): void => {\n b[o] = v & 0xff;\n b[o + 1] = (v >>> 8) & 0xff;\n};\n\nconst writeU32 = (b: Uint8Array, o: number, v: number): void => {\n b[o] = v & 0xff;\n b[o + 1] = (v >>> 8) & 0xff;\n b[o + 2] = (v >>> 16) & 0xff;\n b[o + 3] = (v >>> 24) & 0xff;\n};\n\nconst writeU64 = (b: Uint8Array, o: number, v: number): void => {\n // JS Number safely represents integers up to 2^53 - 1, well beyond\n // anything we'd ever emit here. Split via Math.floor + modulo to\n // avoid bit-shift truncation at 32 bits.\n const lo = v >>> 0;\n const hi = Math.floor(v / 0x100000000) >>> 0;\n writeU32(b, o, lo);\n writeU32(b, o + 4, hi);\n};\n\nconst findEocdOffset = (bytes: Uint8Array): number => {\n // EOCD is min 22 bytes and may be followed by up to 65535 bytes of\n // archive comment. Scan backwards from the latest possible position.\n const minOffset = Math.max(0, bytes.length - (22 + 0xffff));\n for (let p = bytes.length - 22; p >= minOffset; p--) {\n if (u32(bytes, p) === SIG_EOCD) {\n const commentLen = u16(bytes, p + 20);\n if (p + 22 + commentLen === bytes.length) return p;\n }\n }\n throw new OpenXmlIoError('zip64-patch: no End-of-Central-Directory signature found');\n};\n\n/**\n * Splice ZIP64 EOCD record + locator into fflate's final chunk and\n * patch the trailing EOCD entry-count fields with the 0xFFFF sentinel.\n *\n * `finalChunk` must be the [CD | EOCD] block fflate emits as its last\n * ondata callback (everything before it is per-entry LFH/data/DD that\n * we leave untouched). Returns a new chunk; the input is not mutated.\n *\n * Assumes per-entry sizes and central-directory offset fit in 32 bits;\n * throws if not (xlsx archives never approach those limits).\n */\nexport function applyZip64EntryCountPatch(finalChunk: Uint8Array, totalEntries: number): Uint8Array {\n if (totalEntries <= ZIP32_MAX_ENTRIES) return finalChunk;\n\n const eocdOffset = findEocdOffset(finalChunk);\n\n const cdSize = u32(finalChunk, eocdOffset + 12);\n const cdOffset = u32(finalChunk, eocdOffset + 16);\n const commentLen = u16(finalChunk, eocdOffset + 20);\n\n if (cdSize === ZIP32_MAX_U32 || cdOffset === ZIP32_MAX_U32) {\n throw new OpenXmlNotImplementedError(\n 'zip64-patch: archive size or central-directory offset exceeds 4 GiB; full ZIP64 size support is not implemented (xlsx in practice stays well under 4 GiB).',\n );\n }\n\n // Where the EOCD starts in the *global* archive (before our patch).\n // CD precedes EOCD with no gap, so global EOCD offset is just\n // cd_offset + cd_size — the locator points here.\n const globalEocdOffset = cdOffset + cdSize;\n\n const eocdLen = 22 + commentLen;\n const newChunkLen = eocdOffset + ZIP64_EOCD_SIZE + ZIP64_LOCATOR_SIZE + eocdLen;\n const out = new Uint8Array(newChunkLen);\n\n // Original CD bytes (everything before the EOCD).\n out.set(finalChunk.subarray(0, eocdOffset), 0);\n\n // ZIP64 EOCD record (56 bytes total).\n const zip64Eocd = out.subarray(eocdOffset, eocdOffset + ZIP64_EOCD_SIZE);\n writeU32(zip64Eocd, 0, SIG_ZIP64_EOCD);\n // size_of_zip64_eocd = total_size - 12 (size field excludes signature + this field itself)\n writeU64(zip64Eocd, 4, ZIP64_EOCD_SIZE - 12);\n writeU16(zip64Eocd, 12, 45); // version made by (4.5 — first ZIP64 spec)\n writeU16(zip64Eocd, 14, 45); // version needed\n writeU32(zip64Eocd, 16, 0); // disk_number\n writeU32(zip64Eocd, 20, 0); // disk_with_cd\n writeU64(zip64Eocd, 24, totalEntries); // entries_on_this_disk\n writeU64(zip64Eocd, 32, totalEntries); // total_entries\n writeU64(zip64Eocd, 40, cdSize); // cd_size\n writeU64(zip64Eocd, 48, cdOffset); // cd_offset\n\n // ZIP64 EOCD locator (20 bytes).\n const locOffset = eocdOffset + ZIP64_EOCD_SIZE;\n const locator = out.subarray(locOffset, locOffset + ZIP64_LOCATOR_SIZE);\n writeU32(locator, 0, SIG_ZIP64_EOCD_LOCATOR);\n writeU32(locator, 4, 0); // disk_with_zip64_eocd\n writeU64(locator, 8, globalEocdOffset);\n writeU32(locator, 16, 1); // total_disks\n\n // New EOCD: copy original then patch entry counts to the 0xFFFF\n // sentinel. (fflate writes the low 16 bits of the true count there,\n // which confuses readers that don't first look for the ZIP64 record.)\n const newEocdOffset = locOffset + ZIP64_LOCATOR_SIZE;\n out.set(finalChunk.subarray(eocdOffset, eocdOffset + eocdLen), newEocdOffset);\n writeU16(out, newEocdOffset + 8, ZIP32_MAX_ENTRIES);\n writeU16(out, newEocdOffset + 10, ZIP32_MAX_ENTRIES);\n\n return out;\n}\n","// ZIP write layer. Streaming-deflate via fflate's `Zip` + per-entry\n// `ZipDeflate` / `ZipPassThrough` so the writer never holds the whole archive\n// in memory. Each addEntry pushes its bytes through the deflate stream and the\n// resulting ZIP chunks land on the sink one at a time — the buffered\n// `toBytes()` sink concatenates them on finish, while a streaming sink can\n// flush them as they arrive.\n//\n// ZIP64 (entry count > 65535): fflate's `Zip` emits a plain ZIP32 EOCD in all\n// cases, so on finalize we splice in a ZIP64 EOCD record + locator when needed\n// via `applyZip64EntryCountPatch`. That keeps the per-entry LFH/CDH layout\n// fflate produces and only rewrites the trailing records.\n//\n// Scope: this covers the entry-count-overflow case (the limit xlsx archives\n// realistically hit — `tens of millions of cells` → tens of thousands of\n// worksheet entries via the streaming writer). Per-entry compressed/uncompressed\n// sizes and the central-directory offset must still fit in 32 bits (≤ 4 GiB\n// each); a single >4 GiB entry would need full ZIP64 size support and\n// `applyZip64EntryCountPatch` throws `OpenXmlNotImplementedError` if we ever\n// detect that. xlsx workbooks don't approach that limit in practice, but the\n// constraint is real — surface it in your own size estimates.\n\nimport { Zip, ZipDeflate, ZipPassThrough } from 'fflate';\nimport type { XlsxSink } from '../io/sink.js';\nimport { OpenXmlIoError } from '../utils/exceptions.js';\nimport { applyZip64EntryCountPatch } from './zip64-patch.js';\n\nconst ZIP32_MAX_ENTRIES = 0xffff;\nconst LOCAL_TIMESTAMP_OFFSET = 10;\nconst CENTRAL_TIMESTAMP_OFFSET = 12;\nconst CENTRAL_HEADER_SIZE = 46;\nconst CENTRAL_NAME_LENGTH_OFFSET = 28;\nconst CENTRAL_EXTRA_LENGTH_OFFSET = 30;\nconst CENTRAL_COMMENT_LENGTH_OFFSET = 32;\n\n/** Deflate effort: 0 skips compression, 9 is the slowest and smallest. */\nexport type CompressionLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;\n\n/** Supported year range of the fflate ZIP backend. */\nconst MIN_ZIP_YEAR = 1980;\nconst MAX_ZIP_YEAR = 2099;\n\nexport interface ZipWriterOptions {\n /**\n * Last-modified timestamp stamped into every entry's local header and\n * central-directory record. ZIP has no \"no timestamp\" encoding, so fflate\n * defaults each entry to the wall clock and two archives built from\n * identical input differ in bytes. Pin this to get reproducible output for\n * golden-file tests or content-addressed caching.\n *\n * Recorded as the date's UTC wall time, to a two-second resolution, with the\n * year required to fall in 1980-2099. The DOS field carries no timezone, so\n * writing local components would leave the bytes depending on the writer's\n * `TZ`, which is the opposite of what pinning a stamp is for.\n */\n mtime?: Date;\n /** Deflate level handed to fflate. Defaults to fflate's own 6. */\n compressionLevel?: CompressionLevel;\n}\n\nexport interface ZipWriter {\n /**\n * Stage an entry. Bytes are pushed through fflate's `ZipDeflate` /\n * `ZipPassThrough` stream synchronously, so the deflated chunks land on the\n * sink as the call runs (no per-entry buffering — see the streaming-behaviour\n * test in `tests/phase-1/zip/writer.test.ts`). Streams (`ReadableStream`)\n * are not accepted today; pass an already-materialised entry, or use\n * {@link addStreamingEntry} for chunked writes.\n *\n * `compress` defaults to `true`. Pass `false` for already-compressed payloads\n * (PNG/JPEG/zip-as-binary content like vbaProject.bin) so we don't pay\n * deflate costs for no gain.\n */\n addEntry(path: string, bytes: Uint8Array | ReadableStream<Uint8Array>, opts?: { compress?: boolean }): Promise<void>;\n\n /**\n * Open a streaming entry. Returns a writer the caller can `write()` chunks to\n * and `end()` to seal the entry. Each chunk pushes through the same fflate\n * `ZipDeflate` / `ZipPassThrough` machinery as `addEntry`, so peak memory\n * stays at one chunk + deflate scratch even for multi-GB worksheets.\n *\n * Sequencing: only one streaming entry may be open at a time — `addEntry` and\n * a second `addStreamingEntry` both throw until the current entry's `end()`\n * resolves.\n */\n addStreamingEntry(path: string, opts?: { compress?: boolean }): StreamingEntryWriter;\n\n /**\n * Build the central directory and flush all bytes through the sink.\n * Idempotent; subsequent calls resolve to the same payload.\n */\n finalize(): Promise<Uint8Array>;\n\n /**\n * Release the sink and underlying writer without producing a valid archive.\n * Use this from a surrounding catch block when serialization fails part-way\n * through — without it, streaming sinks (`toFile` / `toWritable`) keep their\n * file descriptors / writables open and the half-written xlsx looks valid on\n * disk. Idempotent; safe to call after `finalize()`.\n */\n abort(cause?: unknown): void;\n}\n\n/** Writer handle for a single streaming entry. */\nexport interface StreamingEntryWriter {\n /** Push a chunk of bytes (already-encoded). Throws after `end()`. */\n write(chunk: Uint8Array): void;\n /** Seal the entry. Subsequent `write()` throws. Idempotent. */\n end(): Promise<void>;\n}\n\n/** Encode UTC components directly: local dates cannot represent a DST gap. */\nconst toZipStamp = (mtime: Date): number => {\n const ms = mtime.getTime();\n if (Number.isNaN(ms)) {\n throw new OpenXmlIoError('createZipWriter: mtime is an invalid Date');\n }\n const year = mtime.getUTCFullYear();\n if (year < MIN_ZIP_YEAR || year > MAX_ZIP_YEAR) {\n throw new OpenXmlIoError(\n `createZipWriter: mtime ${mtime.toISOString()} is outside the supported ZIP timestamp range (${MIN_ZIP_YEAR}-${MAX_ZIP_YEAR})`,\n );\n }\n return ((year - MIN_ZIP_YEAR) << 25)\n | ((mtime.getUTCMonth() + 1) << 21)\n | (mtime.getUTCDate() << 16)\n | (mtime.getUTCHours() << 11)\n | (mtime.getUTCMinutes() << 5)\n | (mtime.getUTCSeconds() >> 1);\n};\n\n/**\n * fflate looks the level up in a table and falls back to 6 for anything off\n * the end, so an out-of-range level would quietly produce default output. The\n * union type catches that for TypeScript callers; this catches it for the rest.\n */\nconst validateCompressionLevel = (level: number): void => {\n if (!Number.isInteger(level) || level < 0 || level > 9) {\n throw new OpenXmlIoError(`createZipWriter: compressionLevel must be an integer in [0, 9]; got ${level}`);\n }\n};\n\n/**\n * ZIP writer backed by fflate's streaming `Zip` class. Entries are pushed\n * through `ZipDeflate` / `ZipPassThrough` streams as they arrive, so peak\n * memory stays at the size of the in-flight entry plus the output buffer rather\n * than the full archive.\n *\n * The sink contract is `toBytes()`, but that name is historical: the sink is\n * driven by a chunked `write(chunk)` API that fans bytes out as they arrive.\n * The buffered Node/browser sinks (`toBuffer`, `toBlob`, `toArrayBuffer`)\n * concatenate the chunks for a single-shot result; streaming sinks\n * (`toFile`, `toWritable`) forward each chunk to disk / the wrapped writable\n * without ever holding the full archive resident. Either kind plugs in here.\n */\nexport function createZipWriter(sink: XlsxSink, opts: ZipWriterOptions = {}): ZipWriter {\n // Both options are checked before the sink is opened: a bad one otherwise\n // surfaces from fflate half-way through the first entry, by which time a file\n // sink holds a partial archive.\n if (opts.compressionLevel !== undefined) validateCompressionLevel(opts.compressionLevel);\n const stamp = opts.mtime === undefined ? undefined : toZipStamp(opts.mtime);\n const writer = sink.toBytes();\n const deflateOpts = opts.compressionLevel === undefined ? undefined : { level: opts.compressionLevel };\n let pendingLocalHeader = false;\n // fflate clones mtime and reads local getters, so even a Date subclass cannot\n // represent UTC times in a local DST gap. Give it a safe placeholder and patch\n // only its header chunks below; payload chunks must never be signature-scanned.\n const newEntry = (path: string, compress: boolean): ZipDeflate | ZipPassThrough => {\n const file = compress ? new ZipDeflate(path, deflateOpts) : new ZipPassThrough(path);\n if (stamp !== undefined) {\n file.mtime = new Date(2000, 0, 1);\n pendingLocalHeader = true;\n }\n return file;\n };\n let finalised: Promise<Uint8Array> | undefined;\n let endCalled = false;\n const seen = new Set<string>();\n const errors: Error[] = [];\n // fflate emits the [CD | EOCD] block in a single ondata call with\n // `final=true`. We capture only that chunk so we can apply the ZIP64 patch on\n // finalize; all preceding entry-data chunks stream straight to the sink to\n // preserve the writer's incremental flushing contract.\n let finalChunk: Uint8Array | undefined;\n let zipFinishResolve: (() => void) | undefined;\n const zipFinishPromise = new Promise<void>((resolve) => {\n zipFinishResolve = resolve;\n });\n\n const zip = new Zip((err, chunk, final) => {\n if (err) {\n errors.push(err instanceof Error ? err : new Error(String(err)));\n return;\n }\n // ZipDeflate emits an empty trailer chunk on the final callback even when\n // there are no bytes; guard against pushing an undefined chunk.\n if (chunk && chunk.byteLength > 0) {\n if (stamp !== undefined && (final || pendingLocalHeader)) {\n const view = new DataView(chunk.buffer, chunk.byteOffset, chunk.byteLength);\n if (final) {\n // fflate emits the complete central directory plus EOCD as one chunk.\n let offset = 0;\n for (let i = 0; i < seen.size; i++) {\n view.setUint32(offset + CENTRAL_TIMESTAMP_OFFSET, stamp, true);\n offset += CENTRAL_HEADER_SIZE + view.getUint16(offset + CENTRAL_NAME_LENGTH_OFFSET, true)\n + view.getUint16(offset + CENTRAL_EXTRA_LENGTH_OFFSET, true)\n + view.getUint16(offset + CENTRAL_COMMENT_LENGTH_OFFSET, true);\n }\n } else if (pendingLocalHeader) {\n // With one entry open at a time, its first chunk is the local header.\n view.setUint32(LOCAL_TIMESTAMP_OFFSET, stamp, true);\n pendingLocalHeader = false;\n }\n }\n if (final) {\n // Buffer the trailing CD + EOCD block; written after possible patch.\n finalChunk = chunk;\n } else {\n writer.write(chunk);\n }\n }\n if (final && zipFinishResolve) {\n zipFinishResolve();\n zipFinishResolve = undefined;\n }\n });\n\n let streamingOpen = false;\n\n const guardAdd = (path: string): void => {\n if (finalised !== undefined) {\n throw new OpenXmlIoError('createZipWriter: addEntry after finalize');\n }\n if (streamingOpen) {\n throw new OpenXmlIoError('createZipWriter: a streaming entry is still open — call end() first');\n }\n if (seen.has(path)) {\n throw new OpenXmlIoError(`createZipWriter: duplicate entry \"${path}\"`);\n }\n };\n\n return {\n async addEntry(path, bytes, entryOpts) {\n if (!(bytes instanceof Uint8Array)) {\n throw new OpenXmlIoError(\n 'createZipWriter: ReadableStream entries are not yet supported (deferred to streaming writer)',\n );\n }\n guardAdd(path);\n seen.add(path);\n const file = newEntry(path, entryOpts?.compress ?? true);\n try {\n zip.add(file);\n file.push(bytes, /* final */ true);\n } catch (cause) {\n throw new OpenXmlIoError(`createZipWriter: failed to add entry \"${path}\"`, { cause });\n }\n if (errors.length > 0) {\n throw new OpenXmlIoError('createZipWriter: stream error during addEntry', { cause: errors[0] });\n }\n },\n\n addStreamingEntry(path, entryOpts) {\n guardAdd(path);\n seen.add(path);\n streamingOpen = true;\n const file = newEntry(path, entryOpts?.compress ?? true);\n try {\n zip.add(file);\n } catch (cause) {\n streamingOpen = false;\n throw new OpenXmlIoError(`createZipWriter: failed to open streaming entry \"${path}\"`, { cause });\n }\n let ended = false;\n return {\n write(chunk: Uint8Array): void {\n if (ended) throw new OpenXmlIoError(`createZipWriter: write after end on \"${path}\"`);\n if (!(chunk instanceof Uint8Array)) {\n throw new OpenXmlIoError(`createZipWriter: streaming entry \"${path}\" chunk is not a Uint8Array`);\n }\n if (chunk.byteLength === 0) return;\n try {\n file.push(chunk, /* final */ false);\n } catch (cause) {\n throw new OpenXmlIoError(`createZipWriter: failed to push chunk on \"${path}\"`, { cause });\n }\n if (errors.length > 0) {\n throw new OpenXmlIoError('createZipWriter: stream error during write', { cause: errors[0] });\n }\n },\n async end(): Promise<void> {\n if (ended) return;\n ended = true;\n try {\n file.push(new Uint8Array(0), /* final */ true);\n } catch (cause) {\n throw new OpenXmlIoError(`createZipWriter: failed to end streaming entry \"${path}\"`, { cause });\n }\n streamingOpen = false;\n if (errors.length > 0) {\n throw new OpenXmlIoError('createZipWriter: stream error during end', { cause: errors[0] });\n }\n },\n };\n },\n\n async finalize() {\n if (finalised !== undefined) return finalised;\n if (streamingOpen) {\n throw new OpenXmlIoError('createZipWriter: cannot finalize while a streaming entry is open');\n }\n finalised = (async () => {\n try {\n if (!endCalled) {\n zip.end();\n endCalled = true;\n }\n } catch (cause) {\n throw new OpenXmlIoError('createZipWriter: failed to finalize zip archive', { cause });\n }\n await zipFinishPromise;\n if (errors.length > 0) {\n throw new OpenXmlIoError('createZipWriter: stream error during finalize', { cause: errors[0] });\n }\n\n // Apply the ZIP64 patch to fflate's [CD | EOCD] tail when the entry\n // count exceeds ZIP32's 16-bit cap, then flush the (possibly patched)\n // tail to the sink.\n if (finalChunk) {\n const patched =\n seen.size > ZIP32_MAX_ENTRIES\n ? applyZip64EntryCountPatch(finalChunk, seen.size)\n : finalChunk;\n writer.write(patched);\n }\n return writer.finish();\n })();\n return finalised;\n },\n\n abort(cause?: unknown): void {\n if (finalised !== undefined) return;\n // Mark finalised so any subsequent addEntry / finalize short-circuits.\n finalised = Promise.resolve(new Uint8Array(0));\n // Drop fflate's listener — we don't care about further `ondata` callbacks.\n if (zipFinishResolve) {\n zipFinishResolve();\n zipFinishResolve = undefined;\n }\n writer.abort?.(cause);\n },\n };\n}\n"],"mappings":"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"}
|
package/dist/zip/index.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ export type { DecompressionLimits, ResolvedDecompressionLimits, } from './decomp
|
|
|
2
2
|
export { DEFAULT_DECOMPRESSION_LIMITS } from './decompression-guard.js';
|
|
3
3
|
export type { OpenZipOptions, ZipArchive } from './reader.js';
|
|
4
4
|
export { openZip } from './reader.js';
|
|
5
|
-
export type { StreamingEntryWriter, ZipWriter } from './writer.js';
|
|
5
|
+
export type { CompressionLevel, StreamingEntryWriter, ZipWriter, ZipWriterOptions } from './writer.js';
|
|
6
6
|
export { createZipWriter } from './writer.js';
|
package/dist/zip/writer.d.ts
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
import type { XlsxSink } from '../io/sink.js';
|
|
2
|
+
/** Deflate effort: 0 skips compression, 9 is the slowest and smallest. */
|
|
3
|
+
export type CompressionLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
|
|
4
|
+
export interface ZipWriterOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Last-modified timestamp stamped into every entry's local header and
|
|
7
|
+
* central-directory record. ZIP has no "no timestamp" encoding, so fflate
|
|
8
|
+
* defaults each entry to the wall clock and two archives built from
|
|
9
|
+
* identical input differ in bytes. Pin this to get reproducible output for
|
|
10
|
+
* golden-file tests or content-addressed caching.
|
|
11
|
+
*
|
|
12
|
+
* Recorded as the date's UTC wall time, to a two-second resolution, with the
|
|
13
|
+
* year required to fall in 1980-2099. The DOS field carries no timezone, so
|
|
14
|
+
* writing local components would leave the bytes depending on the writer's
|
|
15
|
+
* `TZ`, which is the opposite of what pinning a stamp is for.
|
|
16
|
+
*/
|
|
17
|
+
mtime?: Date;
|
|
18
|
+
/** Deflate level handed to fflate. Defaults to fflate's own 6. */
|
|
19
|
+
compressionLevel?: CompressionLevel;
|
|
20
|
+
}
|
|
2
21
|
export interface ZipWriter {
|
|
3
22
|
/**
|
|
4
23
|
* Stage an entry. Bytes are pushed through fflate's `ZipDeflate` /
|
|
@@ -62,4 +81,4 @@ export interface StreamingEntryWriter {
|
|
|
62
81
|
* (`toFile`, `toWritable`) forward each chunk to disk / the wrapped writable
|
|
63
82
|
* without ever holding the full archive resident. Either kind plugs in here.
|
|
64
83
|
*/
|
|
65
|
-
export declare function createZipWriter(sink: XlsxSink): ZipWriter;
|
|
84
|
+
export declare function createZipWriter(sink: XlsxSink, opts?: ZipWriterOptions): ZipWriter;
|
package/dist/zip.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { n as DEFAULT_DECOMPRESSION_LIMITS, t as openZip } from "./reader-BNaUTVCy.mjs";
|
|
2
|
-
import { t as createZipWriter } from "./writer-
|
|
2
|
+
import { t as createZipWriter } from "./writer-Z7cF_CBk.mjs";
|
|
3
3
|
export { DEFAULT_DECOMPRESSION_LIMITS, createZipWriter, openZip };
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"save-a0InIbtJ.mjs","names":["serializeXmlNode"],"sources":["../src/io/save.ts"],"sourcesContent":["// Public `saveWorkbook` entry point.\n//\n// **Stage 1 minimum**: emits the bare set of parts a Workbook needs to\n// round-trip through `loadWorkbook`:\n//\n// [Content_Types].xml\n// _rels/.rels\n// xl/workbook.xml\n// xl/_rels/workbook.xml.rels\n// xl/worksheets/sheetN.xml ...\n// xl/styles.xml\n// xl/sharedStrings.xml (only when sst is non-empty)\n//\n// docProps / theme / VBA / drawings / charts are reserved for later iterations\n// — load tolerates their absence.\n\nimport { escapeXmlAttr, escapeXmlText } from '../utils/escape.js';\nimport { chartToBytes } from '../chart/chart-xml.js';\nimport { chartExToBytes } from '../chart/cx/chartex-xml.js';\nimport { userShapesToBytes } from '../chart/user-shapes-xml.js';\nimport { chartsheetToBytes } from '../chartsheet/chartsheet-xml.js';\nimport type { Drawing, DrawingItem } from '../drawing/drawing.js';\nimport { drawingToBytes } from '../drawing/drawing-xml.js';\nimport { IMAGE_FORMAT_EXTENSION, IMAGE_FORMAT_MIME, type XlsxImageFormat } from '../drawing/image.js';\nimport type { XlsxSink } from '../io/sink.js';\nimport { OpenXmlIoError, OpenXmlSchemaError } from '../utils/exceptions.js';\nimport { normalizeFormulaText } from '../utils/formula-text.js';\nimport { corePropsToBytes } from '../packaging/core.js';\nimport { customPropsToBytes } from '../packaging/custom.js';\nimport { extendedPropsToBytes } from '../packaging/extended.js';\nimport { addDefault, addOverride, makeManifest, manifestToBytes } from '../packaging/manifest.js';\nimport { makeRelationships, type Relationships, relsToBytes } from '../packaging/relationships.js';\nimport { stylesheetToBytes } from '../styles/stylesheet-writer.js';\nimport { makeSharedStrings, sharedStringsToBytes } from '../workbook/shared-strings.js';\nimport { type Workbook, validateSheetTitle } from '../workbook/workbook.js';\nimport type { LegacyComment } from '../worksheet/comments.js';\nimport { commentsToBytes, placeholderVmlDrawing } from '../worksheet/comments-xml.js';\nimport type { TableDefinition } from '../worksheet/table.js';\nimport { tableToBytes } from '../worksheet/table-xml.js';\nimport { worksheetToBytes } from '../worksheet/writer.js';\nimport { serializeXml as serializeXmlNode } from '../xml/serializer.js';\nimport {\n ARC_APP,\n ARC_CONTENT_TYPES,\n ARC_CORE,\n ARC_CUSTOM,\n ARC_ROOT_RELS,\n ARC_SHARED_STRINGS,\n ARC_STYLE,\n ARC_THEME,\n ARC_WORKBOOK,\n ARC_WORKBOOK_RELS,\n CHARTEX_TYPE,\n CPROPS_TYPE,\n parseQName,\n PKG_REL_NS,\n REL_NS,\n SHARED_STRINGS_TYPE,\n SHEET_MAIN_NS,\n STYLES_TYPE,\n THEME_TYPE,\n WORKSHEET_TYPE,\n XLSX_TYPE,\n} from '../xml/namespaces.js';\nimport { createZipWriter } from '../zip/writer.js';\n\nconst CORE_PROPS_TYPE = 'application/vnd.openxmlformats-package.core-properties+xml';\nconst EXT_PROPS_TYPE = 'application/vnd.openxmlformats-officedocument.extended-properties+xml';\nconst CORE_PROPS_REL = `${PKG_REL_NS}/metadata/core-properties`;\nconst EXT_PROPS_REL = `${REL_NS}/extended-properties`;\nconst CUSTOM_PROPS_REL = `${REL_NS}/custom-properties`;\nconst THEME_REL = `${REL_NS}/theme`;\nconst TABLE_REL = `${REL_NS}/table`;\nconst TABLE_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml';\nconst COMMENTS_REL = `${REL_NS}/comments`;\nconst VML_DRAWING_REL = `${REL_NS}/vmlDrawing`;\nconst COMMENTS_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml';\nconst VML_DRAWING_TYPE = 'application/vnd.openxmlformats-officedocument.vmlDrawing';\nconst DRAWING_REL = `${REL_NS}/drawing`;\nconst DRAWING_TYPE = 'application/vnd.openxmlformats-officedocument.drawing+xml';\nconst CHART_REL = `${REL_NS}/chart`;\nconst CHARTEX_REL = 'http://schemas.microsoft.com/office/2014/relationships/chartEx';\nconst CHART_TYPE = 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml';\nconst IMAGE_REL = `${REL_NS}/image`;\nconst CHARTSHEET_REL = `${REL_NS}/chartsheet`;\nconst CHARTSHEET_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml';\nconst CHART_USER_SHAPES_REL = `${REL_NS}/chartUserShapes`;\n\nexport interface SaveOptions {\n /** Reserved — passes through to the underlying ZIP writer when implemented. */\n compressionLevel?: number;\n}\n\n/** Convenience: serialise a Workbook to an in-memory `Uint8Array` xlsx.\n *\n * Browser-safe: this path uses an in-memory `Uint8Array` sink and never\n * touches Node's `Buffer` global, so it works unchanged in browser bundles.\n */\nexport async function workbookToBytes(wb: Workbook, opts: SaveOptions = {}): Promise<Uint8Array> {\n const sink = toUint8ArraySink();\n await saveWorkbook(wb, sink, opts);\n return sink.result();\n}\n\nconst toUint8ArraySink = (): XlsxSink & { result(): Uint8Array } => {\n const chunks: Uint8Array[] = [];\n let finalised: Uint8Array | undefined;\n const finalise = (): Uint8Array => {\n if (finalised !== undefined) return finalised;\n let total = 0;\n for (const c of chunks) total += c.byteLength;\n const out = new Uint8Array(total);\n let off = 0;\n for (const c of chunks) {\n out.set(c, off);\n off += c.byteLength;\n }\n finalised = out;\n chunks.length = 0;\n return out;\n };\n return {\n toBytes() {\n return {\n write(chunk: Uint8Array): void {\n if (finalised !== undefined) {\n throw new OpenXmlIoError('workbookToBytes sink: write after finish');\n }\n if (!(chunk instanceof Uint8Array)) {\n throw new OpenXmlIoError('workbookToBytes sink: chunk is not a Uint8Array');\n }\n chunks.push(chunk);\n },\n async finish(): Promise<Uint8Array> {\n return finalise();\n },\n abort(): void {\n if (finalised !== undefined) return;\n finalised = new Uint8Array(0);\n chunks.length = 0;\n },\n };\n },\n result(): Uint8Array {\n return finalise();\n },\n };\n};\n\n/**\n * Validate every sheet title against Excel's character + length rules and\n * confirm titles are unique within the workbook. Catches bad state introduced\n * via direct mutation (`ws.title = ...`) that bypassed the public mutators.\n *\n * Without this gate, a workbook with an invalid sheet name would round-trip to\n * an xlsx Excel rejects with a generic \"file is corrupt\" dialog on open —\n * making it hard to diagnose. Raising at save time keeps the bad state\n * recoverable: callers can `renameSheet` and retry.\n */\nconst validateSheetTitles = (wb: Workbook): void => {\n const seen = new Map<string, number>();\n for (let i = 0; i < wb.sheets.length; i++) {\n const ref = wb.sheets[i];\n if (!ref) continue;\n const title = ref.sheet.title;\n const reason = validateSheetTitle(title);\n if (reason) {\n throw new OpenXmlSchemaError(`saveWorkbook: sheet[${i}] title \"${title}\": ${reason}`);\n }\n const lower = title.toLowerCase();\n const prior = seen.get(lower);\n if (prior !== undefined) {\n throw new OpenXmlSchemaError(\n `saveWorkbook: sheet[${i}] title \"${title}\" collides with sheet[${prior}]` +\n ` (Excel treats sheet names case-insensitively for uniqueness).`,\n );\n }\n seen.set(lower, i);\n }\n};\n\n/** Save a workbook through the given sink. Returns once `finalize()` resolves. */\nexport async function saveWorkbook(wb: Workbook, sink: XlsxSink, _opts: SaveOptions = {}): Promise<void> {\n validateSheetTitles(wb);\n const writer = createZipWriter(sink);\n try {\n await saveWorkbookImpl(wb, writer);\n } catch (err) {\n // Release the sink so streaming destinations (`toFile` / `toWritable`)\n // don't leave a half-written file looking valid. abort() is idempotent so\n // a successful finalize() above is a no-op here.\n writer.abort(err);\n throw err;\n }\n}\n\nasync function saveWorkbookImpl(wb: Workbook, writer: ReturnType<typeof createZipWriter>): Promise<void> {\n // ---- 1. assemble the per-sheet rels + serialise each worksheet ----------\n const sst = makeSharedStrings();\n interface SheetEmit {\n id: string;\n target: string;\n bytes: Uint8Array;\n /** OOXML relationship type used in workbook.xml.rels. */\n relType: string;\n /** ZIP archive path the bytes are written to. */\n archivePath: string;\n /** Override content type for [Content_Types].xml. */\n contentType: string;\n /** Per-sheet rels — populated only if the sheet has hyperlinks / drawings / etc. */\n rels?: Relationships;\n }\n const sheetEmits: SheetEmit[] = [];\n // Workbook-global table counter so xl/tables/tableN.xml ids stay unique.\n const tableEmits: Array<{ id: number; bytes: Uint8Array }> = [];\n let nextTableId = 1;\n // Same counter pattern for comments parts. The comments part and the VML\n // drawing share their N because Excel always emits them paired.\n interface CommentEmit {\n id: number;\n commentsBytes: Uint8Array;\n vmlBytes: Uint8Array;\n }\n const commentEmits: CommentEmit[] = [];\n // Passthrough carries form-control / header-footer VML under the same\n // xl/drawings/vmlDrawingN.vml naming, so start past whatever is already\n // taken — the zip writer rejects duplicate entries.\n let nextCommentsId = nextFreeIndex(wb, /^xl\\/drawings\\/vmlDrawing(\\d+)\\.vml$/);\n // Drawings: workbook-global drawingN counter for xl/drawings/drawingN.xml +\n // matching drawing-rels file (when chart items are present).\n interface DrawingEmit {\n id: number;\n bytes: Uint8Array;\n /** Drawing rels — when non-empty we also emit the rels file. */\n rels?: Relationships;\n }\n const drawingEmits: DrawingEmit[] = [];\n let nextDrawingId = 1;\n // Chart parts share a workbook-global counter so xl/charts/chartN.xml ids\n // stay unique.\n const chartEmits: Array<{\n id: number;\n bytes: Uint8Array;\n isCx: boolean;\n /** Per-chart rels file (only emitted when chart.userShapes is set). */\n rels?: Relationships;\n }> = [];\n let nextChartId = 1;\n // Workbook-global counter for chartDrawing parts. Each chart with userShapes\n // set produces one xl/drawings/chartDrawingN.xml entry.\n const userShapeEmits: Array<{ id: number; bytes: Uint8Array }> = [];\n let nextUserShapesId = 1;\n // Workbook-global counter for image media parts. Excel uses\n // xl/media/imageN.{ext} where N is shared across the package.\n interface ImageEmit {\n id: number;\n /** File extension (without the dot) — also drives the manifest Default. */\n ext: string;\n bytes: Uint8Array;\n }\n const imageEmits: ImageEmit[] = [];\n const imageExts = new Set<string>();\n // Same collision guard as for VML: images referenced by passthrough parts\n // keep their original xl/media/imageN.* names.\n let nextImageId = nextFreeIndex(wb, /^xl\\/media\\/image(\\d+)\\./);\n // Track separate counters so worksheet / chartsheet IDs do not collide (Excel\n // uses xl/worksheets/sheetN.xml and xl/chartsheets/sheetM.xml independently\n // of one another).\n let nextWorksheetId = 1;\n let nextChartsheetId = 1;\n // Pre-claim every original rId so freshly allocated ones (sheets without a\n // captured rId, plus modeled non-sheet rels) avoid collision with captured\n // workbookRelsExtras and the modeled non-sheet original ids.\n const claimedRIds = new Set<string>();\n for (const ref of wb.sheets) {\n if (ref.rId !== undefined) claimedRIds.add(ref.rId);\n }\n if (wb.workbookRelOriginalIds) {\n for (const v of Object.values(wb.workbookRelOriginalIds)) {\n if (typeof v === 'string') claimedRIds.add(v);\n }\n }\n if (wb.workbookRelsExtras) {\n for (const e of wb.workbookRelsExtras) claimedRIds.add(e.id);\n }\n let nextRIdCursor = 1;\n const allocateRId = (): string => {\n let id = `rId${nextRIdCursor}`;\n while (claimedRIds.has(id)) {\n nextRIdCursor++;\n id = `rId${nextRIdCursor}`;\n }\n claimedRIds.add(id);\n nextRIdCursor++;\n return id;\n };\n wb.sheets.forEach((ref, _i) => {\n const isChartsheet = ref.kind === 'chartsheet';\n const target = isChartsheet ? `chartsheets/sheet${nextChartsheetId}.xml` : `worksheets/sheet${nextWorksheetId}.xml`;\n const archivePath = isChartsheet\n ? `xl/chartsheets/sheet${nextChartsheetId}.xml`\n : `xl/worksheets/sheet${nextWorksheetId}.xml`;\n if (isChartsheet) nextChartsheetId++;\n else nextWorksheetId++;\n const sheetRels = makeRelationships();\n // Pre-claim every captured relsExtras rId so freshly allocated modeled rels\n // never clash. The extras themselves are appended to sheetRels at the end\n // so any rId already used inside the worksheet body (drawing / hyperlink\n // refs) keeps pointing at our newly emitted rel.\n const sheetRelsClaimed = new Set<string>();\n const sheetRelsExtras = ref.kind === 'worksheet' ? (ref.sheet.relsExtras ?? []) : [];\n for (const e of sheetRelsExtras) sheetRelsClaimed.add(e.id);\n let sheetRIdCursor = 1;\n const allocateSheetRId = (): string => {\n let id = `rId${sheetRIdCursor}`;\n while (sheetRelsClaimed.has(id) || sheetRels.rels.some((r) => r.id === id)) {\n sheetRIdCursor++;\n id = `rId${sheetRIdCursor}`;\n }\n sheetRIdCursor++;\n return id;\n };\n const registerTable = (table: TableDefinition): { rId: string } => {\n const tableId = nextTableId++;\n const rId = allocateSheetRId();\n sheetRels.rels.push({\n id: rId,\n type: TABLE_REL,\n target: `../tables/table${tableId}.xml`,\n });\n // Emit the table part with its workbook-global id baked in.\n const xmlTable: TableDefinition = { ...table, id: tableId };\n tableEmits.push({ id: tableId, bytes: tableToBytes(xmlTable) });\n return { rId };\n };\n const registerComments = (comments: ReadonlyArray<LegacyComment>): { vmlRelId: string } => {\n const id = nextCommentsId++;\n const commentsRelId = allocateSheetRId();\n sheetRels.rels.push({\n id: commentsRelId,\n type: COMMENTS_REL,\n target: `../comments${id}.xml`,\n });\n const vmlRelId = allocateSheetRId();\n sheetRels.rels.push({\n id: vmlRelId,\n type: VML_DRAWING_REL,\n target: `../drawings/vmlDrawing${id}.vml`,\n });\n commentEmits.push({\n id,\n commentsBytes: commentsToBytes(comments),\n vmlBytes: placeholderVmlDrawing(comments),\n });\n return { vmlRelId };\n };\n const registerDrawing = (drawing: Drawing): { rId: string } => {\n const id = nextDrawingId++;\n const rId = allocateSheetRId();\n sheetRels.rels.push({\n id: rId,\n type: DRAWING_REL,\n target: `../drawings/drawing${id}.xml`,\n });\n // Walk drawing items: for each chart with a payload, allocate a\n // workbook-global chartN id and a per-drawing rId. Emit the chart part +\n // collect a drawing-rels entry so drawing.xml's <c:chart r:id> resolves.\n const drawingRels = makeRelationships();\n // Rels the drawing's verbatim nodes reference keep their original ids —\n // those ids are baked into XML we re-emit untouched — so the allocator\n // below has to step over them.\n const usedDrawingRIds = new Set<string>();\n for (const rel of drawing.rawRels?.rels ?? []) {\n drawingRels.rels.push(rel);\n usedDrawingRIds.add(rel.id);\n }\n let nextDrawingRId = 1;\n const allocateDrawingRId = (): string => {\n let candidate = `rId${nextDrawingRId++}`;\n while (usedDrawingRIds.has(candidate)) candidate = `rId${nextDrawingRId++}`;\n usedDrawingRIds.add(candidate);\n return candidate;\n };\n const itemsForXml: DrawingItem[] = [];\n for (const item of drawing.items) {\n if (item.content.kind === 'chart' && (item.content.chart.space || item.content.chart.cxSpace)) {\n const chartId = nextChartId++;\n const chartRId = allocateDrawingRId();\n // chartex parts use a different relationship Type than the legacy\n // ECMA-376 charts. Excel rejects the workbook when the drawing rels\n // claim a `relationships/chart` target that actually contains a\n // `cx:chartSpace` root.\n const isCxChart = item.content.chart.cxSpace !== undefined;\n drawingRels.rels.push({\n id: chartRId,\n type: isCxChart ? CHARTEX_REL : CHART_REL,\n target: `../charts/chart${chartId}.xml`,\n });\n if (item.content.chart.cxSpace) {\n chartEmits.push({\n id: chartId,\n bytes: chartExToBytes(item.content.chart.cxSpace),\n isCx: true,\n });\n } else if (item.content.chart.space) {\n const space = item.content.chart.space;\n // If the chart carries user shapes, allocate the chartDrawingN part\n // + a per-chart rels file referencing it, then bake the resulting\n // r:id into the chart's <c:userShapes> element.\n let userShapesRId: string | undefined;\n let chartRelsFile: Relationships | undefined;\n if (space.userShapes && space.userShapes.shapes.length > 0) {\n const userShapesId = nextUserShapesId++;\n userShapesRId = 'rId1';\n chartRelsFile = makeRelationships();\n chartRelsFile.rels.push({\n id: userShapesRId,\n type: CHART_USER_SHAPES_REL,\n target: `../drawings/chartDrawing${userShapesId}.xml`,\n });\n userShapeEmits.push({\n id: userShapesId,\n bytes: userShapesToBytes(space.userShapes),\n });\n }\n const chartEmit: typeof chartEmits[number] = {\n id: chartId,\n bytes: chartToBytes(space, userShapesRId !== undefined ? { userShapesRId } : {}),\n isCx: false,\n };\n if (chartRelsFile) chartEmit.rels = chartRelsFile;\n chartEmits.push(chartEmit);\n }\n itemsForXml.push({\n anchor: item.anchor,\n content: {\n kind: 'chart',\n chart: {\n rId: chartRId,\n ...(item.content.chart.cxSpace ? { isCx: true } : {}),\n },\n },\n });\n } else if (item.content.kind === 'picture' && item.content.picture.image) {\n const img = item.content.picture.image;\n const ext = IMAGE_FORMAT_EXTENSION[img.format];\n const imageId = nextImageId++;\n const picRId = allocateDrawingRId();\n drawingRels.rels.push({\n id: picRId,\n type: IMAGE_REL,\n target: `../media/image${imageId}.${ext}`,\n });\n imageEmits.push({ id: imageId, ext, bytes: img.bytes });\n imageExts.add(ext);\n itemsForXml.push({\n anchor: item.anchor,\n content: {\n kind: 'picture',\n picture: {\n rId: picRId,\n ...(item.content.picture.name !== undefined ? { name: item.content.picture.name } : {}),\n ...(item.content.picture.descr !== undefined ? { descr: item.content.picture.descr } : {}),\n ...(item.content.picture.hidden !== undefined ? { hidden: item.content.picture.hidden } : {}),\n ...(item.content.picture.spPr ? { spPr: item.content.picture.spPr } : {}),\n },\n },\n });\n } else {\n itemsForXml.push(item);\n }\n }\n const emit: DrawingEmit = {\n id,\n bytes: drawingToBytes({\n items: itemsForXml,\n ...(drawing.rawChildren ? { rawChildren: drawing.rawChildren } : {}),\n }),\n };\n if (drawingRels.rels.length > 0) emit.rels = drawingRels;\n drawingEmits.push(emit);\n return { rId };\n };\n let bytes: Uint8Array;\n if (ref.kind === 'worksheet') {\n bytes = worksheetToBytes(ref.sheet, {\n sharedStrings: sst,\n styles: wb.styles,\n date1904: wb.date1904,\n rels: sheetRels,\n registerTable,\n registerComments,\n registerDrawing,\n });\n } else {\n // Chartsheet: register the drawing (if any), then emit the chartsheet\n // part with the resulting r:id baked in.\n let drawingRId: string | undefined;\n if (ref.sheet.drawing) drawingRId = registerDrawing(ref.sheet.drawing).rId;\n bytes = chartsheetToBytes(ref.sheet, drawingRId !== undefined ? { drawingRId } : {});\n }\n // Append captured per-sheet rels passthrough (pivotTable / queryTable /\n // printerSettings / oleObject / customProperty / threadedComment …)\n // verbatim. Their original rIds were pre-claimed so the modeled allocations\n // above never collide with them.\n for (const e of sheetRelsExtras) {\n sheetRels.rels.push({ id: e.id, type: e.type, target: e.target });\n }\n const sheetRId = ref.rId ?? allocateRId();\n const emit: SheetEmit = {\n id: sheetRId,\n target,\n bytes,\n relType: isChartsheet ? CHARTSHEET_REL : `${REL_NS}/worksheet`,\n archivePath,\n contentType: isChartsheet ? CHARTSHEET_TYPE : WORKSHEET_TYPE,\n };\n if (sheetRels.rels.length > 0) emit.rels = sheetRels;\n sheetEmits.push(emit);\n });\n\n // ---- 2. workbook rels -- sheets first, then sst (if any), then styles, then\n // theme / vbaProject, then any captured workbookRelsExtras (e.g.\n // pivotCacheDefinition rels referenced by `<pivotCaches>`). Modeled non-sheet\n // rels prefer the rId captured at load time so any captured extras XML using\n // that Id still resolves after the round-trip.\n const wbRels = makeRelationships();\n wbRels.rels = sheetEmits.map((e) => ({\n id: e.id,\n type: e.relType,\n target: e.target,\n }));\n const orig = wb.workbookRelOriginalIds;\n if (sst.entries.length > 0) {\n wbRels.rels.push({\n id: orig?.sharedStrings ?? allocateRId(),\n type: `${REL_NS}/sharedStrings`,\n target: 'sharedStrings.xml',\n });\n }\n wbRels.rels.push({\n id: orig?.styles ?? allocateRId(),\n type: `${REL_NS}/styles`,\n target: 'styles.xml',\n });\n if (wb.themeXml) {\n wbRels.rels.push({\n id: orig?.theme ?? allocateRId(),\n type: THEME_REL,\n target: 'theme/theme1.xml',\n });\n }\n if (wb.vbaProject) {\n wbRels.rels.push({\n id: orig?.vbaProject ?? allocateRId(),\n type: `${REL_NS}/vbaProject`,\n target: 'vbaProject.bin',\n });\n }\n if (wb.workbookRelsExtras) {\n for (const e of wb.workbookRelsExtras) {\n wbRels.rels.push({ id: e.id, type: e.type, target: e.target });\n }\n }\n\n // ---- 3. workbook.xml ----------------------------------------------------\n const workbookXml = serializeWorkbookXml(\n wb,\n sheetEmits.map((e) => e.id),\n );\n await writer.addEntry(ARC_WORKBOOK, new TextEncoder().encode(workbookXml));\n await writer.addEntry(ARC_WORKBOOK_RELS, relsToBytes(wbRels));\n\n // ---- 4. each worksheet / chartsheet (and its rels file when present) --\n for (const e of sheetEmits) {\n await writer.addEntry(e.archivePath, e.bytes);\n if (e.rels) {\n // The rels file sits alongside its part: `xl/<dir>/_rels/<file>.rels`.\n const slash = e.archivePath.lastIndexOf('/');\n const dir = e.archivePath.slice(0, slash);\n const file = e.archivePath.slice(slash + 1);\n await writer.addEntry(`${dir}/_rels/${file}.rels`, relsToBytes(e.rels));\n }\n }\n\n // ---- 4b. table parts ----------------------------------------------------\n for (const t of tableEmits) {\n await writer.addEntry(`xl/tables/table${t.id}.xml`, t.bytes);\n }\n\n // ---- 4c. comments parts + matching VML drawings ------------------------\n for (const c of commentEmits) {\n await writer.addEntry(`xl/comments${c.id}.xml`, c.commentsBytes);\n await writer.addEntry(`xl/drawings/vmlDrawing${c.id}.vml`, c.vmlBytes);\n }\n\n // ---- 4d. drawings + their rels (when charts are embedded) -------------\n for (const d of drawingEmits) {\n await writer.addEntry(`xl/drawings/drawing${d.id}.xml`, d.bytes);\n if (d.rels) {\n await writer.addEntry(`xl/drawings/_rels/drawing${d.id}.xml.rels`, relsToBytes(d.rels));\n }\n }\n\n // ---- 4e. chart parts (and per-chart rels when user shapes attach) -----\n for (const c of chartEmits) {\n await writer.addEntry(`xl/charts/chart${c.id}.xml`, c.bytes);\n if (c.rels) {\n await writer.addEntry(`xl/charts/_rels/chart${c.id}.xml.rels`, relsToBytes(c.rels));\n }\n }\n\n // ---- 4f. user-shape drawings (xl/drawings/chartDrawingN.xml) ----------\n for (const us of userShapeEmits) {\n await writer.addEntry(`xl/drawings/chartDrawing${us.id}.xml`, us.bytes);\n }\n\n // ---- 4g. embedded images (xl/media/imageN.{ext}) ----------------------\n for (const img of imageEmits) {\n await writer.addEntry(`xl/media/image${img.id}.${img.ext}`, img.bytes);\n }\n\n // ---- 5. styles.xml + sharedStrings.xml (if any) -------------------------\n await writer.addEntry(ARC_STYLE, stylesheetToBytes(wb.styles));\n if (sst.entries.length > 0) {\n await writer.addEntry(ARC_SHARED_STRINGS, sharedStringsToBytes(sst));\n }\n\n // ---- 5b. theme1.xml (passthrough) — only when wb carries one. The theme rel\n // was already added to wbRels above so workbook.xml.rels references it.\n if (wb.themeXml) await writer.addEntry(ARC_THEME, wb.themeXml);\n\n // ---- 5c. docProps/{core,app,custom}.xml (when present) ------------------\n if (wb.properties) await writer.addEntry(ARC_CORE, corePropsToBytes(wb.properties));\n if (wb.appProperties) await writer.addEntry(ARC_APP, extendedPropsToBytes(wb.appProperties));\n if (wb.customProperties) await writer.addEntry(ARC_CUSTOM, customPropsToBytes(wb.customProperties));\n\n // ---- 5d. VBA project + signature (when present) -------------------------\n if (wb.vbaProject) await writer.addEntry('xl/vbaProject.bin', wb.vbaProject);\n if (wb.vbaSignature) await writer.addEntry('xl/vbaProjectSignature.bin', wb.vbaSignature);\n\n // ---- 5e. Pass-through bytes (pivot / activeX / customXml / etc.) -------\n if (wb.passthrough) {\n for (const [path, bytes] of wb.passthrough) await writer.addEntry(path, bytes);\n }\n\n // ---- 6. root rels -------------------------------------------------------\n const rootRels: Relationships = { rels: [] };\n rootRels.rels.push({\n id: 'rId1',\n type: `${REL_NS}/officeDocument`,\n target: 'xl/workbook.xml',\n });\n if (wb.properties) {\n rootRels.rels.push({\n id: `rId${rootRels.rels.length + 1}`,\n type: CORE_PROPS_REL,\n target: 'docProps/core.xml',\n });\n }\n if (wb.appProperties) {\n rootRels.rels.push({\n id: `rId${rootRels.rels.length + 1}`,\n type: EXT_PROPS_REL,\n target: 'docProps/app.xml',\n });\n }\n if (wb.customProperties) {\n rootRels.rels.push({\n id: `rId${rootRels.rels.length + 1}`,\n type: CUSTOM_PROPS_REL,\n target: 'docProps/custom.xml',\n });\n }\n await writer.addEntry(ARC_ROOT_RELS, relsToBytes(rootRels));\n\n // ---- 7. [Content_Types].xml --------------------------------------------\n const manifest = makeManifest();\n addDefault(manifest, 'rels', 'application/vnd.openxmlformats-package.relationships+xml');\n addDefault(manifest, 'xml', 'application/xml');\n // Defaults carried over for passthrough parts go first: addDefault is\n // last-wins, so the writer's own registrations below (vml for comments, bin\n // for VBA, image formats) still take precedence on a shared extension.\n if (wb.passthroughDefaults) {\n for (const [ext, ct] of wb.passthroughDefaults) addDefault(manifest, ext, ct);\n }\n // VBA-bearing workbooks promote the workbook content type to xlsm.\n const workbookContentType = wb.vbaProject\n ? 'application/vnd.ms-excel.sheet.macroEnabled.main+xml'\n : XLSX_TYPE;\n addOverride(manifest, `/${ARC_WORKBOOK}`, workbookContentType);\n for (const e of sheetEmits) {\n addOverride(manifest, `/${e.archivePath}`, e.contentType);\n }\n addOverride(manifest, `/${ARC_STYLE}`, STYLES_TYPE);\n if (sst.entries.length > 0) {\n addOverride(manifest, `/${ARC_SHARED_STRINGS}`, SHARED_STRINGS_TYPE);\n }\n if (wb.themeXml) addOverride(manifest, `/${ARC_THEME}`, THEME_TYPE);\n for (const t of tableEmits) {\n addOverride(manifest, `/xl/tables/table${t.id}.xml`, TABLE_TYPE);\n }\n if (commentEmits.length > 0) {\n addDefault(manifest, 'vml', VML_DRAWING_TYPE);\n }\n for (const c of commentEmits) {\n addOverride(manifest, `/xl/comments${c.id}.xml`, COMMENTS_TYPE);\n }\n for (const d of drawingEmits) {\n addOverride(manifest, `/xl/drawings/drawing${d.id}.xml`, DRAWING_TYPE);\n }\n for (const c of chartEmits) {\n addOverride(manifest, `/xl/charts/chart${c.id}.xml`, c.isCx ? CHARTEX_TYPE : CHART_TYPE);\n }\n for (const us of userShapeEmits) {\n addOverride(manifest, `/xl/drawings/chartDrawing${us.id}.xml`, DRAWING_TYPE);\n }\n if (wb.vbaProject) {\n addDefault(manifest, 'bin', 'application/vnd.ms-office.vbaProject');\n }\n if (wb.passthrough) {\n for (const path of wb.passthrough.keys()) {\n const ct = wb.passthroughContentTypes?.get(path);\n if (ct !== undefined) addOverride(manifest, `/${path}`, ct);\n }\n }\n // Each unique image extension gets a Default entry (`<Default Extension=\"png\"\n // ContentType=\"image/png\"/>`).\n for (const ext of imageExts) {\n const fmt = (Object.entries(IMAGE_FORMAT_EXTENSION).find(([, e]) => e === ext) ?? [])[0] as\n | XlsxImageFormat\n | undefined;\n if (fmt) addDefault(manifest, ext, IMAGE_FORMAT_MIME[fmt]);\n }\n if (wb.properties) addOverride(manifest, `/${ARC_CORE}`, CORE_PROPS_TYPE);\n if (wb.appProperties) addOverride(manifest, `/${ARC_APP}`, EXT_PROPS_TYPE);\n if (wb.customProperties) addOverride(manifest, `/${ARC_CUSTOM}`, CPROPS_TYPE);\n await writer.addEntry(ARC_CONTENT_TYPES, manifestToBytes(manifest));\n\n // ---- 8. close ----------------------------------------------------------\n await writer.finalize();\n}\n\n/**\n * 1 + the highest N among passthrough paths matching `pattern` (whose first\n * capture group is N), so freshly numbered modeled parts never collide with a\n * carried-over one.\n */\nfunction nextFreeIndex(wb: Workbook, pattern: RegExp): number {\n let max = 0;\n if (wb.passthrough) {\n for (const path of wb.passthrough.keys()) {\n const m = pattern.exec(path);\n if (m?.[1] !== undefined) max = Math.max(max, Number.parseInt(m[1], 10));\n }\n }\n return max + 1;\n}\n\n/** Serialise the minimum `<workbook><sheets/></workbook>` Excel needs to load a sheet list. */\nfunction serializeWorkbookXml(wb: Workbook, sheetRIds: ReadonlyArray<string>): string {\n // Excel's markup-compatibility header: the extra prefixes it declares on the\n // root and the `mc:Ignorable` list naming them. Both are re-emitted exactly\n // as written, and the same prefix map is handed to every captured child so\n // `xr:revisionPtr` doesn't come back as `x16:revisionPtr`.\n const rootNs = wb.workbookXmlRoot?.namespaces ?? [];\n const nsDecls = rootNs.map((d) => ` xmlns:${d.prefix}=\"${escapeAttr(d.ns)}\"`).join('');\n const ignorable = wb.workbookXmlRoot?.ignorable;\n const ignorableAttr = ignorable !== undefined ? ` mc:Ignorable=\"${escapeAttr(ignorable)}\"` : '';\n const inScopePrefixes: Record<string, string> = { [SHEET_MAIN_NS]: '', [REL_NS]: 'r' };\n for (const d of rootNs) inScopePrefixes[d.ns] ??= d.prefix;\n const emitExtra = (node: import('../xml/tree.js').XmlNode): string =>\n serializeChildNode(node, inScopePrefixes);\n\n const parts: string[] = [\n '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>',\n `<workbook xmlns=\"${SHEET_MAIN_NS}\" xmlns:r=\"${REL_NS}\"${nsDecls}${ignorableAttr}>`,\n ];\n if (wb.fileVersion) {\n const fv = serializeFileVersion(wb.fileVersion);\n if (fv) parts.push(fv);\n }\n if (wb.fileSharing) {\n const fs = serializeFileSharing(wb.fileSharing);\n if (fs) parts.push(fs);\n }\n // Emit <workbookPr> from the typed model; fall back to a minimal {date1904:\n // true} synthesis so a fresh workbook (no load history) still round-trips\n // through Excel with the right epoch.\n const effectiveWp = effectiveWorkbookProperties(wb);\n if (effectiveWp) {\n const wp = serializeWorkbookProperties(effectiveWp);\n if (wp) parts.push(wp);\n }\n if (wb.workbookProtection) {\n const wp = serializeWorkbookProtection(wb.workbookProtection);\n if (wp) parts.push(wp);\n }\n // The unmodeled head — `mc:AlternateContent`, `xr:revisionPtr` — sits after\n // `<workbookPr>` in what Excel writes, and CT_Workbook has no slot for it,\n // so it goes here rather than ahead of the modeled elements.\n if (wb.workbookXmlExtras?.beforeSheets) {\n for (const node of wb.workbookXmlExtras.beforeSheets) parts.push(emitExtra(node));\n }\n if (wb.bookViews && wb.bookViews.length > 0) parts.push(serializeBookViews(wb.bookViews, inScopePrefixes));\n parts.push('<sheets>');\n wb.sheets.forEach((ref, i) => {\n const stateAttr = ref.state === 'visible' ? '' : ` state=\"${ref.state}\"`;\n const rId = sheetRIds[i] ?? `rId${i + 1}`;\n parts.push(`<sheet name=\"${escapeAttr(ref.sheet.title)}\" sheetId=\"${ref.sheetId}\"${stateAttr} r:id=\"${rId}\"/>`);\n });\n parts.push('</sheets>');\n // ECMA-376 §18.2.27 makes CT_Workbook an xsd:sequence, so the order below is\n // normative: functionGroups, externalReferences, definedNames, calcPr,\n // oleSize, customWorkbookViews, pivotCaches, smartTagPr, smartTagTypes,\n // webPublishing, fileRecoveryPr, webPublishObjects, extLst. Excel rejects a\n // workbook whose children are out of sequence. `afterSheets` is the\n // unmodeled tail (webPublishing / webPublishObjects / extLst /\n // mc:AlternateContent), so it goes last.\n if (wb.functionGroups) {\n const fg = serializeFunctionGroups(wb.functionGroups);\n if (fg) parts.push(fg);\n }\n if (wb.externalReferences && wb.externalReferences.length > 0) {\n const inner: string[] = ['<externalReferences>'];\n for (const er of wb.externalReferences) {\n inner.push(`<externalReference r:id=\"${escapeAttr(er.rId)}\"/>`);\n }\n inner.push('</externalReferences>');\n parts.push(inner.join(''));\n }\n if (wb.definedNames.length > 0) {\n parts.push('<definedNames>');\n for (const dn of wb.definedNames) {\n let attrs = ` name=\"${escapeAttr(dn.name)}\"`;\n if (dn.scope !== undefined) attrs += ` localSheetId=\"${dn.scope}\"`;\n if (dn.hidden) attrs += ' hidden=\"1\"';\n if (dn.comment !== undefined) attrs += ` comment=\"${escapeAttr(dn.comment)}\"`;\n parts.push(`<definedName${attrs}>${escapeText(normalizeFormulaText(dn.value))}</definedName>`);\n }\n parts.push('</definedNames>');\n }\n if (wb.calcProperties) {\n const cp = serializeCalcProperties(wb.calcProperties);\n if (cp) parts.push(cp);\n }\n // No default `<calcPr>` — Excel handles its absence by inserting one on save.\n // Hand-rolled `calcId` values (especially the modern 191029) made Excel\n // reject workbooks whose future-function formulas (LET / LAMBDA / FILTER…)\n // couldn't be evaluated against the declared engine.\n if (wb.oleSize !== undefined) {\n parts.push(`<oleSize ref=\"${escapeAttr(wb.oleSize)}\"/>`);\n }\n if (wb.customWorkbookViews && wb.customWorkbookViews.length > 0) {\n parts.push(serializeCustomWorkbookViews(wb.customWorkbookViews));\n }\n if (wb.pivotCaches && wb.pivotCaches.length > 0) {\n const inner: string[] = ['<pivotCaches>'];\n for (const pc of wb.pivotCaches) {\n inner.push(`<pivotCache cacheId=\"${pc.cacheId}\" r:id=\"${escapeAttr(pc.rId)}\"/>`);\n }\n inner.push('</pivotCaches>');\n parts.push(inner.join(''));\n }\n if (wb.smartTagPr) {\n const stp = serializeSmartTagPr(wb.smartTagPr);\n if (stp) parts.push(stp);\n }\n if (wb.smartTagTypes && wb.smartTagTypes.length > 0) {\n parts.push(serializeSmartTagTypes(wb.smartTagTypes));\n }\n if (wb.fileRecoveryPr) {\n const fp = serializeFileRecoveryPr(wb.fileRecoveryPr);\n if (fp) parts.push(fp);\n }\n if (wb.workbookXmlExtras?.afterSheets) {\n for (const node of wb.workbookXmlExtras.afterSheets) parts.push(emitExtra(node));\n }\n parts.push('</workbook>');\n return parts.join('');\n}\n\nfunction serializeCalcProperties(\n cp: import('../workbook/calc-properties.js').CalcProperties,\n): string | undefined {\n let attrs = '';\n if (cp.calcId !== undefined) attrs += ` calcId=\"${cp.calcId}\"`;\n if (cp.calcMode !== undefined) attrs += ` calcMode=\"${cp.calcMode}\"`;\n if (cp.fullCalcOnLoad !== undefined) attrs += ` fullCalcOnLoad=\"${cp.fullCalcOnLoad ? '1' : '0'}\"`;\n if (cp.refMode !== undefined) attrs += ` refMode=\"${cp.refMode}\"`;\n if (cp.iterate !== undefined) attrs += ` iterate=\"${cp.iterate ? '1' : '0'}\"`;\n if (cp.iterateCount !== undefined) attrs += ` iterateCount=\"${cp.iterateCount}\"`;\n if (cp.iterateDelta !== undefined) attrs += ` iterateDelta=\"${cp.iterateDelta}\"`;\n if (cp.fullPrecision !== undefined) attrs += ` fullPrecision=\"${cp.fullPrecision ? '1' : '0'}\"`;\n if (cp.calcCompleted !== undefined) attrs += ` calcCompleted=\"${cp.calcCompleted ? '1' : '0'}\"`;\n if (cp.calcOnSave !== undefined) attrs += ` calcOnSave=\"${cp.calcOnSave ? '1' : '0'}\"`;\n if (cp.concurrentCalc !== undefined) attrs += ` concurrentCalc=\"${cp.concurrentCalc ? '1' : '0'}\"`;\n if (cp.concurrentManualCount !== undefined) attrs += ` concurrentManualCount=\"${cp.concurrentManualCount}\"`;\n if (cp.forceFullCalc !== undefined) attrs += ` forceFullCalc=\"${cp.forceFullCalc ? '1' : '0'}\"`;\n if (attrs.length === 0) return undefined;\n return `<calcPr${attrs}/>`;\n}\n\nfunction serializeFunctionGroups(\n fg: import('../workbook/function-groups.js').FunctionGroups,\n): string | undefined {\n let attrs = '';\n if (fg.builtInGroupCount !== undefined) attrs += ` builtInGroupCount=\"${fg.builtInGroupCount}\"`;\n if (fg.groups.length === 0 && attrs.length === 0) return undefined;\n if (fg.groups.length === 0) return `<functionGroups${attrs}/>`;\n const inner: string[] = [`<functionGroups${attrs}>`];\n for (const g of fg.groups) inner.push(`<functionGroup name=\"${escapeAttr(g.name)}\"/>`);\n inner.push('</functionGroups>');\n return inner.join('');\n}\n\nfunction serializeSmartTagPr(\n stp: import('../workbook/smart-tags.js').SmartTagProperties,\n): string | undefined {\n let attrs = '';\n if (stp.embed !== undefined) attrs += ` embed=\"${stp.embed ? '1' : '0'}\"`;\n if (stp.show !== undefined) attrs += ` show=\"${stp.show}\"`;\n if (attrs.length === 0) return undefined;\n return `<smartTagPr${attrs}/>`;\n}\n\nfunction serializeSmartTagTypes(\n tags: ReadonlyArray<import('../workbook/smart-tags.js').SmartTagType>,\n): string {\n const inner: string[] = ['<smartTagTypes>'];\n for (const t of tags) {\n let attrs = '';\n if (t.namespaceUri !== undefined) attrs += ` namespaceUri=\"${escapeAttr(t.namespaceUri)}\"`;\n if (t.name !== undefined) attrs += ` name=\"${escapeAttr(t.name)}\"`;\n if (t.url !== undefined) attrs += ` url=\"${escapeAttr(t.url)}\"`;\n inner.push(`<smartTagType${attrs}/>`);\n }\n inner.push('</smartTagTypes>');\n return inner.join('');\n}\n\nfunction serializeFileRecoveryPr(\n fp: import('../workbook/file-recovery.js').FileRecoveryProperties,\n): string | undefined {\n let attrs = '';\n if (fp.autoRecover !== undefined) attrs += ` autoRecover=\"${fp.autoRecover ? '1' : '0'}\"`;\n if (fp.crashSave !== undefined) attrs += ` crashSave=\"${fp.crashSave ? '1' : '0'}\"`;\n if (fp.dataExtractLoad !== undefined) attrs += ` dataExtractLoad=\"${fp.dataExtractLoad ? '1' : '0'}\"`;\n if (fp.repairLoad !== undefined) attrs += ` repairLoad=\"${fp.repairLoad ? '1' : '0'}\"`;\n if (attrs.length === 0) return undefined;\n return `<fileRecoveryPr${attrs}/>`;\n}\n\nfunction serializeFileSharing(\n fs: import('../workbook/file-sharing.js').FileSharing,\n): string | undefined {\n let attrs = '';\n if (fs.readOnlyRecommended !== undefined)\n attrs += ` readOnlyRecommended=\"${fs.readOnlyRecommended ? '1' : '0'}\"`;\n if (fs.userName !== undefined) attrs += ` userName=\"${escapeAttr(fs.userName)}\"`;\n if (fs.reservationPassword !== undefined)\n attrs += ` reservationPassword=\"${escapeAttr(fs.reservationPassword)}\"`;\n if (fs.algorithmName !== undefined) attrs += ` algorithmName=\"${escapeAttr(fs.algorithmName)}\"`;\n if (fs.hashValue !== undefined) attrs += ` hashValue=\"${escapeAttr(fs.hashValue)}\"`;\n if (fs.saltValue !== undefined) attrs += ` saltValue=\"${escapeAttr(fs.saltValue)}\"`;\n if (fs.spinCount !== undefined) attrs += ` spinCount=\"${fs.spinCount}\"`;\n if (attrs.length === 0) return undefined;\n return `<fileSharing${attrs}/>`;\n}\n\nfunction serializeFileVersion(\n fv: import('../workbook/file-version.js').FileVersion,\n): string | undefined {\n let attrs = '';\n if (fv.appName !== undefined) attrs += ` appName=\"${escapeAttr(fv.appName)}\"`;\n if (fv.lastEdited !== undefined) attrs += ` lastEdited=\"${escapeAttr(fv.lastEdited)}\"`;\n if (fv.lowestEdited !== undefined) attrs += ` lowestEdited=\"${escapeAttr(fv.lowestEdited)}\"`;\n if (fv.rupBuild !== undefined) attrs += ` rupBuild=\"${escapeAttr(fv.rupBuild)}\"`;\n if (fv.codeName !== undefined) attrs += ` codeName=\"${escapeAttr(fv.codeName)}\"`;\n if (attrs.length === 0) return undefined;\n return `<fileVersion${attrs}/>`;\n}\n\nfunction effectiveWorkbookProperties(\n wb: Workbook,\n): import('../workbook/workbook-properties.js').WorkbookProperties | undefined {\n const explicit = wb.workbookProperties;\n if (explicit) {\n // Mirror the canonical date1904 flag if the typed model omits it.\n if (wb.date1904 && explicit.date1904 === undefined) {\n return { ...explicit, date1904: true };\n }\n return explicit;\n }\n if (wb.date1904) return { date1904: true };\n return undefined;\n}\n\nfunction serializeWorkbookProperties(\n wp: import('../workbook/workbook-properties.js').WorkbookProperties,\n): string | undefined {\n let attrs = '';\n const boolKeys: ReadonlyArray<keyof import('../workbook/workbook-properties.js').WorkbookProperties> = [\n 'date1904',\n 'dateCompatibility',\n 'showBorderUnselectedTables',\n 'filterPrivacy',\n 'promptedSolutions',\n 'showInkAnnotation',\n 'backupFile',\n 'saveExternalLinkValues',\n 'hidePivotFieldList',\n 'showPivotChartFilter',\n 'allowRefreshQuery',\n 'publishItems',\n 'checkCompatibility',\n 'autoCompressPictures',\n 'refreshAllConnections',\n ];\n for (const k of boolKeys) {\n const v = wp[k];\n if (v !== undefined) attrs += ` ${k}=\"${v ? '1' : '0'}\"`;\n }\n if (wp.showObjects !== undefined) attrs += ` showObjects=\"${wp.showObjects}\"`;\n if (wp.updateLinks !== undefined) attrs += ` updateLinks=\"${wp.updateLinks}\"`;\n if (wp.codeName !== undefined) attrs += ` codeName=\"${escapeAttr(wp.codeName)}\"`;\n if (wp.defaultThemeVersion !== undefined) attrs += ` defaultThemeVersion=\"${wp.defaultThemeVersion}\"`;\n if (attrs.length === 0) return undefined;\n return `<workbookPr${attrs}/>`;\n}\n\nfunction serializeCustomWorkbookViews(\n views: ReadonlyArray<import('../workbook/views.js').CustomWorkbookView>,\n): string {\n const parts: string[] = ['<customWorkbookViews>'];\n for (const v of views) {\n let attrs = ` name=\"${escapeAttr(v.name)}\" guid=\"${escapeAttr(v.guid)}\"`;\n if (v.autoUpdate !== undefined) attrs += ` autoUpdate=\"${v.autoUpdate ? '1' : '0'}\"`;\n if (v.mergeInterval !== undefined) attrs += ` mergeInterval=\"${v.mergeInterval}\"`;\n if (v.changesSavedWin !== undefined) attrs += ` changesSavedWin=\"${v.changesSavedWin ? '1' : '0'}\"`;\n if (v.onlySync !== undefined) attrs += ` onlySync=\"${v.onlySync ? '1' : '0'}\"`;\n if (v.personalView !== undefined) attrs += ` personalView=\"${v.personalView ? '1' : '0'}\"`;\n if (v.includePrintSettings !== undefined)\n attrs += ` includePrintSettings=\"${v.includePrintSettings ? '1' : '0'}\"`;\n if (v.includeHiddenRowCol !== undefined)\n attrs += ` includeHiddenRowCol=\"${v.includeHiddenRowCol ? '1' : '0'}\"`;\n if (v.maximized !== undefined) attrs += ` maximized=\"${v.maximized ? '1' : '0'}\"`;\n if (v.minimized !== undefined) attrs += ` minimized=\"${v.minimized ? '1' : '0'}\"`;\n if (v.showHorizontalScroll !== undefined)\n attrs += ` showHorizontalScroll=\"${v.showHorizontalScroll ? '1' : '0'}\"`;\n if (v.showVerticalScroll !== undefined)\n attrs += ` showVerticalScroll=\"${v.showVerticalScroll ? '1' : '0'}\"`;\n if (v.showSheetTabs !== undefined) attrs += ` showSheetTabs=\"${v.showSheetTabs ? '1' : '0'}\"`;\n if (v.xWindow !== undefined) attrs += ` xWindow=\"${v.xWindow}\"`;\n if (v.yWindow !== undefined) attrs += ` yWindow=\"${v.yWindow}\"`;\n attrs += ` windowWidth=\"${v.windowWidth}\" windowHeight=\"${v.windowHeight}\"`;\n if (v.tabRatio !== undefined) attrs += ` tabRatio=\"${v.tabRatio}\"`;\n attrs += ` activeSheetId=\"${v.activeSheetId}\"`;\n if (v.showFormulaBar !== undefined) attrs += ` showFormulaBar=\"${v.showFormulaBar ? '1' : '0'}\"`;\n if (v.showStatusbar !== undefined) attrs += ` showStatusbar=\"${v.showStatusbar ? '1' : '0'}\"`;\n if (v.showComments !== undefined) attrs += ` showComments=\"${v.showComments}\"`;\n if (v.showObjects !== undefined) attrs += ` showObjects=\"${v.showObjects}\"`;\n parts.push(`<customWorkbookView${attrs}/>`);\n }\n parts.push('</customWorkbookViews>');\n return parts.join('');\n}\n\nfunction serializeBookViews(\n views: ReadonlyArray<import('../workbook/views.js').WorkbookView>,\n prefixOf: Readonly<Record<string, string>>,\n): string {\n const parts: string[] = ['<bookViews>'];\n for (const v of views) {\n let attrs = '';\n if (v.visibility !== undefined) attrs += ` visibility=\"${v.visibility}\"`;\n if (v.minimized !== undefined) attrs += ` minimized=\"${v.minimized ? '1' : '0'}\"`;\n if (v.showHorizontalScroll !== undefined)\n attrs += ` showHorizontalScroll=\"${v.showHorizontalScroll ? '1' : '0'}\"`;\n if (v.showVerticalScroll !== undefined)\n attrs += ` showVerticalScroll=\"${v.showVerticalScroll ? '1' : '0'}\"`;\n if (v.showSheetTabs !== undefined) attrs += ` showSheetTabs=\"${v.showSheetTabs ? '1' : '0'}\"`;\n if (v.xWindow !== undefined) attrs += ` xWindow=\"${v.xWindow}\"`;\n if (v.yWindow !== undefined) attrs += ` yWindow=\"${v.yWindow}\"`;\n if (v.windowWidth !== undefined) attrs += ` windowWidth=\"${v.windowWidth}\"`;\n if (v.windowHeight !== undefined) attrs += ` windowHeight=\"${v.windowHeight}\"`;\n if (v.tabRatio !== undefined) attrs += ` tabRatio=\"${v.tabRatio}\"`;\n if (v.firstSheet !== undefined) attrs += ` firstSheet=\"${v.firstSheet}\"`;\n if (v.activeTab !== undefined) attrs += ` activeTab=\"${v.activeTab}\"`;\n if (v.autoFilterDateGrouping !== undefined)\n attrs += ` autoFilterDateGrouping=\"${v.autoFilterDateGrouping ? '1' : '0'}\"`;\n // Namespaced extras (`xr2:uid`) only make sense with a prefix the root\n // declares; without one the attribute would be unreadable, so skip it.\n for (const [clark, value] of Object.entries(v.extAttrs ?? {})) {\n const { ns, local } = parseQName(clark);\n const prefix = prefixOf[ns];\n if (prefix === undefined) continue;\n attrs += ` ${prefix}:${local}=\"${escapeAttr(value)}\"`;\n }\n parts.push(`<workbookView${attrs}/>`);\n }\n parts.push('</bookViews>');\n return parts.join('');\n}\n\nfunction serializeWorkbookProtection(\n wp: import('../workbook/protection.js').WorkbookProtection,\n): string | undefined {\n let attrs = '';\n const strAttrs = [\n 'workbookPassword',\n 'workbookPasswordCharacterSet',\n 'workbookAlgorithmName',\n 'workbookHashValue',\n 'workbookSaltValue',\n 'revisionsPassword',\n 'revisionsPasswordCharacterSet',\n 'revisionsAlgorithmName',\n 'revisionsHashValue',\n 'revisionsSaltValue',\n ] as const;\n for (const k of strAttrs) {\n const v = wp[k];\n if (v !== undefined) attrs += ` ${k}=\"${escapeAttr(v)}\"`;\n }\n if (wp.workbookSpinCount !== undefined) attrs += ` workbookSpinCount=\"${wp.workbookSpinCount}\"`;\n if (wp.revisionsSpinCount !== undefined) attrs += ` revisionsSpinCount=\"${wp.revisionsSpinCount}\"`;\n if (wp.lockStructure !== undefined) attrs += ` lockStructure=\"${wp.lockStructure ? '1' : '0'}\"`;\n if (wp.lockWindows !== undefined) attrs += ` lockWindows=\"${wp.lockWindows ? '1' : '0'}\"`;\n if (wp.lockRevision !== undefined) attrs += ` lockRevision=\"${wp.lockRevision ? '1' : '0'}\"`;\n if (attrs.length === 0) return undefined;\n return `<workbookProtection${attrs}/>`;\n}\n\n// Local aliases keep the dense call sites below readable. Canonical\n// implementations live in utils/escape.ts so all three writers (this file,\n// xml/serializer, xml/stream-writer) agree on how `>` / whitespace are\n// handled.\nconst escapeText = escapeXmlText;\nconst escapeAttr = escapeXmlAttr;\n\n/**\n * Serialise an XmlNode child of `<workbook>` for inline injection back into the\n * workbook XML stream. Reuses serializeXml then strips the declaration.\n * Captured nodes carry Clark-notation names, so `inScopePrefixes` is what keeps\n * a child on the prefix the root declared for it instead of a freshly\n * allocated one — which matters when `mc:Ignorable` names those prefixes.\n */\nfunction serializeChildNode(\n node: import('../xml/tree.js').XmlNode,\n inScopePrefixes?: Readonly<Record<string, string>>,\n): string {\n const bytes = serializeXmlNode(node, {\n xmlDeclaration: false,\n ...(inScopePrefixes ? { inScopePrefixes } : {}),\n });\n return new TextDecoder().decode(bytes);\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"writer-IyFccCRJ.mjs","names":["ZIP32_MAX_ENTRIES"],"sources":["../src/zip/zip64-patch.ts","../src/zip/writer.ts"],"sourcesContent":["// ZIP64 post-processing for archives whose entry count exceeds the\n// 16-bit ZIP32 cap (65535). fflate's `Zip` writer always emits a\n// plain ZIP32 EOCD; for archives with more entries we keep its\n// per-entry LFH/CDH layout (correct as long as no individual size or\n// offset overflows 32 bits) and splice in a ZIP64 End-of-Central-\n// Directory record + locator before the EOCD, then patch the EOCD's\n// entry-count fields with the 0xFFFF sentinel that signals \"consult\n// the ZIP64 record for the real values\".\n//\n// The input is fflate's *final* chunk — the trailing [CD | EOCD] block\n// it emits in one ondata callback when `Zip.end()` is called. The\n// preceding entry-data chunks have already been streamed to the sink,\n// so we operate on the final chunk in isolation. The global EOCD\n// offset (needed for the ZIP64 locator) is derivable from cd_offset +\n// cd_size carried in the EOCD itself, so no external bookkeeping is\n// required.\n//\n// Out of scope: per-entry sizes or central-directory offsets > 4 GiB.\n// xlsx archives don't approach those limits in practice; we throw a\n// clear error if we detect overflow there.\n\nimport { OpenXmlIoError, OpenXmlNotImplementedError } from '../utils/exceptions.js';\n\nconst ZIP32_MAX_ENTRIES = 0xffff;\nconst ZIP32_MAX_U32 = 0xffffffff;\nconst SIG_EOCD = 0x06054b50;\nconst SIG_ZIP64_EOCD = 0x06064b50;\nconst SIG_ZIP64_EOCD_LOCATOR = 0x07064b50;\n\nconst ZIP64_EOCD_SIZE = 56;\nconst ZIP64_LOCATOR_SIZE = 20;\n\nconst u16 = (b: Uint8Array, o: number): number => (b[o] ?? 0) | ((b[o + 1] ?? 0) << 8);\n\nconst u32 = (b: Uint8Array, o: number): number => {\n const v0 = b[o] ?? 0;\n const v1 = b[o + 1] ?? 0;\n const v2 = b[o + 2] ?? 0;\n const v3 = b[o + 3] ?? 0;\n return (v0 | (v1 << 8) | (v2 << 16) | (v3 << 24)) >>> 0;\n};\n\nconst writeU16 = (b: Uint8Array, o: number, v: number): void => {\n b[o] = v & 0xff;\n b[o + 1] = (v >>> 8) & 0xff;\n};\n\nconst writeU32 = (b: Uint8Array, o: number, v: number): void => {\n b[o] = v & 0xff;\n b[o + 1] = (v >>> 8) & 0xff;\n b[o + 2] = (v >>> 16) & 0xff;\n b[o + 3] = (v >>> 24) & 0xff;\n};\n\nconst writeU64 = (b: Uint8Array, o: number, v: number): void => {\n // JS Number safely represents integers up to 2^53 - 1, well beyond\n // anything we'd ever emit here. Split via Math.floor + modulo to\n // avoid bit-shift truncation at 32 bits.\n const lo = v >>> 0;\n const hi = Math.floor(v / 0x100000000) >>> 0;\n writeU32(b, o, lo);\n writeU32(b, o + 4, hi);\n};\n\nconst findEocdOffset = (bytes: Uint8Array): number => {\n // EOCD is min 22 bytes and may be followed by up to 65535 bytes of\n // archive comment. Scan backwards from the latest possible position.\n const minOffset = Math.max(0, bytes.length - (22 + 0xffff));\n for (let p = bytes.length - 22; p >= minOffset; p--) {\n if (u32(bytes, p) === SIG_EOCD) {\n const commentLen = u16(bytes, p + 20);\n if (p + 22 + commentLen === bytes.length) return p;\n }\n }\n throw new OpenXmlIoError('zip64-patch: no End-of-Central-Directory signature found');\n};\n\n/**\n * Splice ZIP64 EOCD record + locator into fflate's final chunk and\n * patch the trailing EOCD entry-count fields with the 0xFFFF sentinel.\n *\n * `finalChunk` must be the [CD | EOCD] block fflate emits as its last\n * ondata callback (everything before it is per-entry LFH/data/DD that\n * we leave untouched). Returns a new chunk; the input is not mutated.\n *\n * Assumes per-entry sizes and central-directory offset fit in 32 bits;\n * throws if not (xlsx archives never approach those limits).\n */\nexport function applyZip64EntryCountPatch(finalChunk: Uint8Array, totalEntries: number): Uint8Array {\n if (totalEntries <= ZIP32_MAX_ENTRIES) return finalChunk;\n\n const eocdOffset = findEocdOffset(finalChunk);\n\n const cdSize = u32(finalChunk, eocdOffset + 12);\n const cdOffset = u32(finalChunk, eocdOffset + 16);\n const commentLen = u16(finalChunk, eocdOffset + 20);\n\n if (cdSize === ZIP32_MAX_U32 || cdOffset === ZIP32_MAX_U32) {\n throw new OpenXmlNotImplementedError(\n 'zip64-patch: archive size or central-directory offset exceeds 4 GiB; full ZIP64 size support is not implemented (xlsx in practice stays well under 4 GiB).',\n );\n }\n\n // Where the EOCD starts in the *global* archive (before our patch).\n // CD precedes EOCD with no gap, so global EOCD offset is just\n // cd_offset + cd_size — the locator points here.\n const globalEocdOffset = cdOffset + cdSize;\n\n const eocdLen = 22 + commentLen;\n const newChunkLen = eocdOffset + ZIP64_EOCD_SIZE + ZIP64_LOCATOR_SIZE + eocdLen;\n const out = new Uint8Array(newChunkLen);\n\n // Original CD bytes (everything before the EOCD).\n out.set(finalChunk.subarray(0, eocdOffset), 0);\n\n // ZIP64 EOCD record (56 bytes total).\n const zip64Eocd = out.subarray(eocdOffset, eocdOffset + ZIP64_EOCD_SIZE);\n writeU32(zip64Eocd, 0, SIG_ZIP64_EOCD);\n // size_of_zip64_eocd = total_size - 12 (size field excludes signature + this field itself)\n writeU64(zip64Eocd, 4, ZIP64_EOCD_SIZE - 12);\n writeU16(zip64Eocd, 12, 45); // version made by (4.5 — first ZIP64 spec)\n writeU16(zip64Eocd, 14, 45); // version needed\n writeU32(zip64Eocd, 16, 0); // disk_number\n writeU32(zip64Eocd, 20, 0); // disk_with_cd\n writeU64(zip64Eocd, 24, totalEntries); // entries_on_this_disk\n writeU64(zip64Eocd, 32, totalEntries); // total_entries\n writeU64(zip64Eocd, 40, cdSize); // cd_size\n writeU64(zip64Eocd, 48, cdOffset); // cd_offset\n\n // ZIP64 EOCD locator (20 bytes).\n const locOffset = eocdOffset + ZIP64_EOCD_SIZE;\n const locator = out.subarray(locOffset, locOffset + ZIP64_LOCATOR_SIZE);\n writeU32(locator, 0, SIG_ZIP64_EOCD_LOCATOR);\n writeU32(locator, 4, 0); // disk_with_zip64_eocd\n writeU64(locator, 8, globalEocdOffset);\n writeU32(locator, 16, 1); // total_disks\n\n // New EOCD: copy original then patch entry counts to the 0xFFFF\n // sentinel. (fflate writes the low 16 bits of the true count there,\n // which confuses readers that don't first look for the ZIP64 record.)\n const newEocdOffset = locOffset + ZIP64_LOCATOR_SIZE;\n out.set(finalChunk.subarray(eocdOffset, eocdOffset + eocdLen), newEocdOffset);\n writeU16(out, newEocdOffset + 8, ZIP32_MAX_ENTRIES);\n writeU16(out, newEocdOffset + 10, ZIP32_MAX_ENTRIES);\n\n return out;\n}\n","// ZIP write layer. Streaming-deflate via fflate's `Zip` + per-entry\n// `ZipDeflate` / `ZipPassThrough` so the writer never holds the whole archive\n// in memory. Each addEntry pushes its bytes through the deflate stream and the\n// resulting ZIP chunks land on the sink one at a time — the buffered\n// `toBytes()` sink concatenates them on finish, while a streaming sink can\n// flush them as they arrive.\n//\n// ZIP64 (entry count > 65535): fflate's `Zip` emits a plain ZIP32 EOCD in all\n// cases, so on finalize we splice in a ZIP64 EOCD record + locator when needed\n// via `applyZip64EntryCountPatch`. That keeps the per-entry LFH/CDH layout\n// fflate produces and only rewrites the trailing records.\n//\n// Scope: this covers the entry-count-overflow case (the limit xlsx archives\n// realistically hit — `tens of millions of cells` → tens of thousands of\n// worksheet entries via the streaming writer). Per-entry compressed/uncompressed\n// sizes and the central-directory offset must still fit in 32 bits (≤ 4 GiB\n// each); a single >4 GiB entry would need full ZIP64 size support and\n// `applyZip64EntryCountPatch` throws `OpenXmlNotImplementedError` if we ever\n// detect that. xlsx workbooks don't approach that limit in practice, but the\n// constraint is real — surface it in your own size estimates.\n\nimport { Zip, ZipDeflate, ZipPassThrough } from 'fflate';\nimport type { XlsxSink } from '../io/sink.js';\nimport { OpenXmlIoError } from '../utils/exceptions.js';\nimport { applyZip64EntryCountPatch } from './zip64-patch.js';\n\nconst ZIP32_MAX_ENTRIES = 0xffff;\n\nexport interface ZipWriter {\n /**\n * Stage an entry. Bytes are pushed through fflate's `ZipDeflate` /\n * `ZipPassThrough` stream synchronously, so the deflated chunks land on the\n * sink as the call runs (no per-entry buffering — see the streaming-behaviour\n * test in `tests/phase-1/zip/writer.test.ts`). Streams (`ReadableStream`)\n * are not accepted today; pass an already-materialised entry, or use\n * {@link addStreamingEntry} for chunked writes.\n *\n * `compress` defaults to `true`. Pass `false` for already-compressed payloads\n * (PNG/JPEG/zip-as-binary content like vbaProject.bin) so we don't pay\n * deflate costs for no gain.\n */\n addEntry(path: string, bytes: Uint8Array | ReadableStream<Uint8Array>, opts?: { compress?: boolean }): Promise<void>;\n\n /**\n * Open a streaming entry. Returns a writer the caller can `write()` chunks to\n * and `end()` to seal the entry. Each chunk pushes through the same fflate\n * `ZipDeflate` / `ZipPassThrough` machinery as `addEntry`, so peak memory\n * stays at one chunk + deflate scratch even for multi-GB worksheets.\n *\n * Sequencing: only one streaming entry may be open at a time — `addEntry` and\n * a second `addStreamingEntry` both throw until the current entry's `end()`\n * resolves.\n */\n addStreamingEntry(path: string, opts?: { compress?: boolean }): StreamingEntryWriter;\n\n /**\n * Build the central directory and flush all bytes through the sink.\n * Idempotent; subsequent calls resolve to the same payload.\n */\n finalize(): Promise<Uint8Array>;\n\n /**\n * Release the sink and underlying writer without producing a valid archive.\n * Use this from a surrounding catch block when serialization fails part-way\n * through — without it, streaming sinks (`toFile` / `toWritable`) keep their\n * file descriptors / writables open and the half-written xlsx looks valid on\n * disk. Idempotent; safe to call after `finalize()`.\n */\n abort(cause?: unknown): void;\n}\n\n/** Writer handle for a single streaming entry. */\nexport interface StreamingEntryWriter {\n /** Push a chunk of bytes (already-encoded). Throws after `end()`. */\n write(chunk: Uint8Array): void;\n /** Seal the entry. Subsequent `write()` throws. Idempotent. */\n end(): Promise<void>;\n}\n\n/**\n * ZIP writer backed by fflate's streaming `Zip` class. Entries are pushed\n * through `ZipDeflate` / `ZipPassThrough` streams as they arrive, so peak\n * memory stays at the size of the in-flight entry plus the output buffer rather\n * than the full archive.\n *\n * The sink contract is `toBytes()`, but that name is historical: the sink is\n * driven by a chunked `write(chunk)` API that fans bytes out as they arrive.\n * The buffered Node/browser sinks (`toBuffer`, `toBlob`, `toArrayBuffer`)\n * concatenate the chunks for a single-shot result; streaming sinks\n * (`toFile`, `toWritable`) forward each chunk to disk / the wrapped writable\n * without ever holding the full archive resident. Either kind plugs in here.\n */\nexport function createZipWriter(sink: XlsxSink): ZipWriter {\n const writer = sink.toBytes();\n let finalised: Promise<Uint8Array> | undefined;\n let endCalled = false;\n const seen = new Set<string>();\n const errors: Error[] = [];\n // fflate emits the [CD | EOCD] block in a single ondata call with\n // `final=true`. We capture only that chunk so we can apply the ZIP64 patch on\n // finalize; all preceding entry-data chunks stream straight to the sink to\n // preserve the writer's incremental flushing contract.\n let finalChunk: Uint8Array | undefined;\n let zipFinishResolve: (() => void) | undefined;\n const zipFinishPromise = new Promise<void>((resolve) => {\n zipFinishResolve = resolve;\n });\n\n const zip = new Zip((err, chunk, final) => {\n if (err) {\n errors.push(err instanceof Error ? err : new Error(String(err)));\n return;\n }\n // ZipDeflate emits an empty trailer chunk on the final callback even when\n // there are no bytes; guard against pushing an undefined chunk.\n if (chunk && chunk.byteLength > 0) {\n if (final) {\n // Buffer the trailing CD + EOCD block; written after possible patch.\n finalChunk = chunk;\n } else {\n writer.write(chunk);\n }\n }\n if (final && zipFinishResolve) {\n zipFinishResolve();\n zipFinishResolve = undefined;\n }\n });\n\n let streamingOpen = false;\n\n const guardAdd = (path: string): void => {\n if (finalised !== undefined) {\n throw new OpenXmlIoError('createZipWriter: addEntry after finalize');\n }\n if (streamingOpen) {\n throw new OpenXmlIoError('createZipWriter: a streaming entry is still open — call end() first');\n }\n if (seen.has(path)) {\n throw new OpenXmlIoError(`createZipWriter: duplicate entry \"${path}\"`);\n }\n };\n\n return {\n async addEntry(path, bytes, opts) {\n if (!(bytes instanceof Uint8Array)) {\n throw new OpenXmlIoError(\n 'createZipWriter: ReadableStream entries are not yet supported (deferred to streaming writer)',\n );\n }\n guardAdd(path);\n seen.add(path);\n const compress = opts?.compress ?? true;\n const file = compress ? new ZipDeflate(path) : new ZipPassThrough(path);\n try {\n zip.add(file);\n file.push(bytes, /* final */ true);\n } catch (cause) {\n throw new OpenXmlIoError(`createZipWriter: failed to add entry \"${path}\"`, { cause });\n }\n if (errors.length > 0) {\n throw new OpenXmlIoError('createZipWriter: stream error during addEntry', { cause: errors[0] });\n }\n },\n\n addStreamingEntry(path, opts) {\n guardAdd(path);\n seen.add(path);\n streamingOpen = true;\n const compress = opts?.compress ?? true;\n const file = compress ? new ZipDeflate(path) : new ZipPassThrough(path);\n try {\n zip.add(file);\n } catch (cause) {\n streamingOpen = false;\n throw new OpenXmlIoError(`createZipWriter: failed to open streaming entry \"${path}\"`, { cause });\n }\n let ended = false;\n return {\n write(chunk: Uint8Array): void {\n if (ended) throw new OpenXmlIoError(`createZipWriter: write after end on \"${path}\"`);\n if (!(chunk instanceof Uint8Array)) {\n throw new OpenXmlIoError(`createZipWriter: streaming entry \"${path}\" chunk is not a Uint8Array`);\n }\n if (chunk.byteLength === 0) return;\n try {\n file.push(chunk, /* final */ false);\n } catch (cause) {\n throw new OpenXmlIoError(`createZipWriter: failed to push chunk on \"${path}\"`, { cause });\n }\n if (errors.length > 0) {\n throw new OpenXmlIoError('createZipWriter: stream error during write', { cause: errors[0] });\n }\n },\n async end(): Promise<void> {\n if (ended) return;\n ended = true;\n try {\n file.push(new Uint8Array(0), /* final */ true);\n } catch (cause) {\n throw new OpenXmlIoError(`createZipWriter: failed to end streaming entry \"${path}\"`, { cause });\n }\n streamingOpen = false;\n if (errors.length > 0) {\n throw new OpenXmlIoError('createZipWriter: stream error during end', { cause: errors[0] });\n }\n },\n };\n },\n\n async finalize() {\n if (finalised !== undefined) return finalised;\n if (streamingOpen) {\n throw new OpenXmlIoError('createZipWriter: cannot finalize while a streaming entry is open');\n }\n finalised = (async () => {\n try {\n if (!endCalled) {\n zip.end();\n endCalled = true;\n }\n } catch (cause) {\n throw new OpenXmlIoError('createZipWriter: failed to finalize zip archive', { cause });\n }\n await zipFinishPromise;\n if (errors.length > 0) {\n throw new OpenXmlIoError('createZipWriter: stream error during finalize', { cause: errors[0] });\n }\n\n // Apply the ZIP64 patch to fflate's [CD | EOCD] tail when the entry\n // count exceeds ZIP32's 16-bit cap, then flush the (possibly patched)\n // tail to the sink.\n if (finalChunk) {\n const patched =\n seen.size > ZIP32_MAX_ENTRIES\n ? applyZip64EntryCountPatch(finalChunk, seen.size)\n : finalChunk;\n writer.write(patched);\n }\n return writer.finish();\n })();\n return finalised;\n },\n\n abort(cause?: unknown): void {\n if (finalised !== undefined) return;\n // Mark finalised so any subsequent addEntry / finalize short-circuits.\n finalised = Promise.resolve(new Uint8Array(0));\n // Drop fflate's listener — we don't care about further `ondata` callbacks.\n if (zipFinishResolve) {\n zipFinishResolve();\n zipFinishResolve = undefined;\n }\n writer.abort?.(cause);\n },\n };\n}\n"],"mappings":"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"}
|