/** * A minimal OPC/xlsx container, written by hand. * * An `.xlsx` file is a zip of XML parts, so this needs no dependency at all - * which is the point. The workbook has to be produced inside a Cloudflare * Worker (#355), where `fs` and Node streams do not exist and bundle size is a * budget, and every xlsx library on npm is both larger than this file and * built for Node. * * Entries are written with the **stored** method rather than deflated. Excel * accepts stored entries, and it keeps writing SYNCHRONOUS: the only * compressor available in a Worker is `CompressionStream`, which is async, and * an async writer would infect every caller including ApertureX's synchronous * `SourceStore` seam (ADR 0100). Reading is a different matter and is async, * because Excel re-saves deflated. * * Timestamps are fixed rather than current, so identical input produces * identical bytes. `export rtm` already holds that line - "the output carries * no timestamp, so identical inputs produce identical bytes and CI can diff * it" - and a workbook that differs on every run could not be reviewed. */ /** How a sheet appears in Excel's tab strip. */ export type SheetState = 'visible' | 'hidden' | 'veryHidden'; /** * A cell's ROLE, not its appearance (#416). * * The set is closed on purpose, and named for roles so that it stays closed: * a caller asking for a fourth should be asking for a fourth ROLE — "this * cell is stale", "this cell is derived" — and the test for admitting one is * whether it can be named without reference to how it looks. A request for * `blue`, or for a shade matching a client's deck, is the request that turns * a style vocabulary into a styling engine, and refusing it is the closed * set doing its job. * * Three will be wrong eventually. It should be wrong LOUDLY, by someone * filing for a role, rather than quietly by callers approximating a missing * role with the nearest available one — which is how `muted` would come to * mean two things. * * Named rather than given as colours for a second reason: the styles part is * then a CONSTANT, so "identical inputs produce identical bytes" stays * trivially true instead of becoming something a reviewer has to re-derive. */ export type CellStyle = 'header' | 'muted' | 'emphasis'; export interface WorkbookSheet { readonly name: string; /** Defaults to visible. `veryHidden` cannot be unhidden from Excel's UI. */ readonly state?: SheetState; /** Rows of cells. Every value is written as an inline string. */ readonly rows: readonly (readonly string[])[]; /** Rows to keep on screen when scrolling, usually 1 for a header. */ readonly frozenRows?: number; /** * Applied to every row BELOW the first, by column index. Row 1 takes * `headerStyle` instead, so a header band is not overwritten by the column * role beneath it. */ readonly columnStyles?: readonly (CellStyle | undefined)[]; /** Applied to row 1 only. */ readonly headerStyle?: CellStyle; /** Column widths in characters, by column index. */ readonly columnWidths?: readonly number[]; } /** * XML text escaping. * * Control characters are dropped rather than escaped: XML 1.0 cannot represent * most of them at all, and a NUL reaching a spreadsheet is the failure mode * that silently defeats every text tool downstream. */ export declare const escapeXml: (value: string) => string; /** `0 -> A`, `26 -> AA`, the spreadsheet column alphabet. */ export declare const columnName: (index: number) => string; /** * Excel forbids these in a sheet name, and caps it at 31 characters. A name * that collides after truncation would produce a workbook Excel refuses to * open, so callers keep their names short and distinct rather than relying on * repair here. */ export declare const sheetNameIsLegal: (name: string) => boolean; /** * The workbook, as bytes. Synchronous, dependency-free, and deterministic: * the same sheets always produce the same bytes. */ export declare const writeXlsx: (sheets: readonly WorkbookSheet[]) => Uint8Array;