@office-kit/xlsx 0.11.1 → 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.
Files changed (75) hide show
  1. package/README.md +4 -1
  2. package/dist/cell/cell.d.ts +44 -9
  3. package/dist/cell/index.d.ts +1 -1
  4. package/dist/{cell-BucgGwl3.mjs → cell-Df3cKRIl.mjs} +72 -30
  5. package/dist/cell-Df3cKRIl.mjs.map +1 -0
  6. package/dist/{cell-style-DuvS_Lsg.mjs → cell-style-BFmJOmcx.mjs} +130 -93
  7. package/dist/cell-style-BFmJOmcx.mjs.map +1 -0
  8. package/dist/cell.mjs +2 -2
  9. package/dist/{coordinate-96Ecci4d.mjs → coordinate-Bt5Gni2I.mjs} +19 -8
  10. package/dist/coordinate-Bt5Gni2I.mjs.map +1 -0
  11. package/dist/{defined-names-CviWmtQg.mjs → defined-names-Ctu3F6ls.mjs} +4 -3
  12. package/dist/defined-names-Ctu3F6ls.mjs.map +1 -0
  13. package/dist/{differential-DvKU8fJt.mjs → differential-H9SjEeIU.mjs} +2 -2
  14. package/dist/{differential-DvKU8fJt.mjs.map → differential-H9SjEeIU.mjs.map} +1 -1
  15. package/dist/{drawing-BxzLuryn.mjs → drawing-ZJ3h4VHD.mjs} +2 -2
  16. package/dist/{drawing-BxzLuryn.mjs.map → drawing-ZJ3h4VHD.mjs.map} +1 -1
  17. package/dist/drawing.mjs +1 -1
  18. package/dist/{inference-BrI9uIY4.mjs → inference-BMvv9cUY.mjs} +18 -3
  19. package/dist/inference-BMvv9cUY.mjs.map +1 -0
  20. package/dist/io/save.d.ts +15 -3
  21. package/dist/io.mjs +2 -2
  22. package/dist/{load-Bghb1YHY.mjs → load-CJdGh50R.mjs} +6 -6
  23. package/dist/{load-Bghb1YHY.mjs.map → load-CJdGh50R.mjs.map} +1 -1
  24. package/dist/node.mjs +1 -1
  25. package/dist/{save-Bqcf8w3g.mjs → save-CjarR7Rp.mjs} +10 -8
  26. package/dist/save-CjarR7Rp.mjs.map +1 -0
  27. package/dist/streaming/write-only.d.ts +18 -14
  28. package/dist/streaming.mjs +21 -51
  29. package/dist/streaming.mjs.map +1 -1
  30. package/dist/styles/cell-style.d.ts +78 -32
  31. package/dist/styles/fonts.d.ts +10 -1
  32. package/dist/styles/index.d.ts +3 -3
  33. package/dist/styles/stylesheet.d.ts +26 -0
  34. package/dist/styles.mjs +4 -4
  35. package/dist/{stylesheet-writer-HxaX7bGy.mjs → stylesheet-writer-D7Uug85X.mjs} +31 -18
  36. package/dist/stylesheet-writer-D7Uug85X.mjs.map +1 -0
  37. package/dist/{table-1P8we2gS.mjs → table-Ccro4rrz.mjs} +11 -9
  38. package/dist/table-Ccro4rrz.mjs.map +1 -0
  39. package/dist/utils/coordinate.d.ts +30 -4
  40. package/dist/utils/formula-text.d.ts +7 -0
  41. package/dist/utils/index.d.ts +1 -1
  42. package/dist/utils.mjs +2 -2
  43. package/dist/workbook/calc-properties.d.ts +8 -1
  44. package/dist/workbook/index.d.ts +1 -1
  45. package/dist/{workbook-BlZVXiyG.mjs → workbook-B15-T4cs.mjs} +6 -6
  46. package/dist/{workbook-BlZVXiyG.mjs.map → workbook-B15-T4cs.mjs.map} +1 -1
  47. package/dist/workbook.mjs +45 -3
  48. package/dist/workbook.mjs.map +1 -1
  49. package/dist/worksheet/cell-range.d.ts +10 -3
  50. package/dist/worksheet/data-validations.d.ts +1 -1
  51. package/dist/worksheet/index.d.ts +3 -3
  52. package/dist/worksheet/table-validate.d.ts +7 -0
  53. package/dist/worksheet/table.d.ts +4 -1
  54. package/dist/worksheet/views.d.ts +5 -0
  55. package/dist/worksheet/worksheet.d.ts +106 -65
  56. package/dist/worksheet/writer.d.ts +7 -0
  57. package/dist/{worksheet-C5Ensf_j.mjs → worksheet-4xW-i8j9.mjs} +235 -51
  58. package/dist/worksheet-4xW-i8j9.mjs.map +1 -0
  59. package/dist/worksheet.mjs +3 -3
  60. package/dist/{writer-IyFccCRJ.mjs → writer-Z7cF_CBk.mjs} +60 -8
  61. package/dist/writer-Z7cF_CBk.mjs.map +1 -0
  62. package/dist/zip/index.d.ts +1 -1
  63. package/dist/zip/writer.d.ts +20 -1
  64. package/dist/zip.mjs +1 -1
  65. package/package.json +1 -1
  66. package/dist/cell-BucgGwl3.mjs.map +0 -1
  67. package/dist/cell-style-DuvS_Lsg.mjs.map +0 -1
  68. package/dist/coordinate-96Ecci4d.mjs.map +0 -1
  69. package/dist/defined-names-CviWmtQg.mjs.map +0 -1
  70. package/dist/inference-BrI9uIY4.mjs.map +0 -1
  71. package/dist/save-Bqcf8w3g.mjs.map +0 -1
  72. package/dist/stylesheet-writer-HxaX7bGy.mjs.map +0 -1
  73. package/dist/table-1P8we2gS.mjs.map +0 -1
  74. package/dist/worksheet-C5Ensf_j.mjs.map +0 -1
  75. package/dist/writer-IyFccCRJ.mjs.map +0 -1
@@ -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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"}
@@ -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';
@@ -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-IyFccCRJ.mjs";
2
+ import { t as createZipWriter } from "./writer-Z7cF_CBk.mjs";
3
3
  export { DEFAULT_DECOMPRESSION_LIMITS, createZipWriter, openZip };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@office-kit/xlsx",
3
- "version": "0.11.1",
3
+ "version": "0.13.0",
4
4
  "description": "TypeScript library for reading and writing Excel xlsx files in Node and the browser.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1 +0,0 @@
1
- {"version":3,"file":"cell-BucgGwl3.mjs","names":[],"sources":["../src/cell/rich-text.ts","../src/cell/cell.ts"],"sourcesContent":["// Inline rich-text runs.\n//\n// A rich-text cell value is `{ kind: 'rich-text', runs }`. Each run is\n// a string segment with an optional InlineFont describing the in-line\n// formatting (font name, size, bold / italic / underline, colour, …).\n// Run-level fields use OOXML's short attribute names (`sz`, `b`, `i`,\n// `u`) so the writer can splice them into `<rPr>` directly without\n// renaming.\n\nimport type { Color } from '../styles/colors.js';\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\n\n/** Underline styles per openpyxl's cell-level NestedNoneSet. */\nexport type InlineUnderline = 'single' | 'double' | 'singleAccounting' | 'doubleAccounting';\n\nexport type InlineVertAlign = 'baseline' | 'superscript' | 'subscript';\n\nexport interface InlineFont {\n readonly name?: string;\n readonly sz?: number;\n readonly b?: boolean;\n readonly i?: boolean;\n readonly u?: InlineUnderline;\n readonly strike?: boolean;\n readonly outline?: boolean;\n readonly shadow?: boolean;\n readonly condense?: boolean;\n readonly extend?: boolean;\n readonly vertAlign?: InlineVertAlign;\n readonly color?: Color;\n readonly family?: number;\n readonly charset?: number;\n readonly scheme?: 'major' | 'minor';\n}\n\nexport interface TextRun {\n readonly text: string;\n readonly font?: InlineFont;\n}\n\n/** A frozen array of TextRuns. The shared cell value shape under `kind: 'rich-text'`. */\nexport type RichText = ReadonlyArray<TextRun>;\n\nexport function makeTextRun(text: string, font?: InlineFont): TextRun {\n if (typeof text !== 'string') {\n throw new OpenXmlSchemaError('makeTextRun: text must be a string');\n }\n return Object.freeze(font !== undefined ? { text, font } : { text });\n}\n\nexport function makeRichText(runs: ReadonlyArray<TextRun | { text: string; font?: InlineFont }>): RichText {\n const out = runs.map((r) => (Object.isFrozen(r) ? (r as TextRun) : makeTextRun(r.text, r.font)));\n return Object.freeze(out);\n}\n\n/**\n * Concatenate the plain-text content of a rich-text value (rich-text\n * read paths often want the raw text without formatting).\n */\nexport function richTextToString(rt: RichText): string {\n let out = '';\n for (const r of rt) out += r.text;\n return out;\n}\n","// Cell value model. Mirrors openpyxl/openpyxl/cell/cell.py.\n//\n// A Cell is a plain mutable object: the worksheet stores millions of these, so\n// per-cell freezes / spreads aren't viable on the hot path. The public surface\n// stays small — makeCell + getCoordinate + targeted setters — and uses\n// discriminated unions for the special CellValue shapes (formula, rich text,\n// duration, error).\n\nimport { columnLetterFromIndex, MAX_COL, MAX_ROW } from '../utils/coordinate.js';\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\nimport { ERROR_CODES } from '../utils/inference.js';\nimport { type RichText, richTextToString } from './rich-text.js';\n\n/** Excel error tokens. */\nexport type ExcelErrorCode = '#NULL!' | '#DIV/0!' | '#VALUE!' | '#REF!' | '#NAME?' | '#NUM!' | '#N/A' | '#GETTING_DATA';\n\n/** Formula sub-kind — drives the OOXML `<f t=\"…\">` attribute. */\nexport type FormulaKind = 'normal' | 'array' | 'shared' | 'dataTable';\n\nexport interface FormulaValue {\n readonly kind: 'formula';\n readonly formula: string;\n readonly t: FormulaKind;\n /** Cached value Excel last computed for the cell, used when `data_only` reads it back. */\n readonly cachedValue?: number | string | boolean;\n /** Range string (`\"A1:A10\"`) for array / shared / dataTable formulas. */\n readonly ref?: string;\n /** Shared-formula index. */\n readonly si?: number;\n /** Data-table specific fields (mirrors openpyxl DataTableFormula). */\n readonly r1?: string;\n readonly r2?: string;\n readonly dt2D?: boolean;\n readonly dtr?: boolean;\n readonly del1?: boolean;\n readonly del2?: boolean;\n readonly aca?: boolean;\n readonly ca?: boolean;\n}\n\nexport type CellValue =\n | number\n | string\n | boolean\n | Date\n | { kind: 'duration'; ms: number }\n | { kind: 'error'; code: ExcelErrorCode }\n | { kind: 'rich-text'; runs: RichText }\n | FormulaValue\n | null;\n\nexport interface Cell {\n /** 1-based row index. */\n row: number;\n /** 1-based column index. */\n col: number;\n /** Effective cell value. `null` represents an empty cell. */\n value: CellValue;\n /** Index into Workbook.styles.cellXfs. 0 = default. */\n styleId: number;\n /** Optional reference to a Hyperlink registered on the worksheet. */\n hyperlinkId?: number;\n /** Optional reference to a Comment registered on the worksheet. */\n commentId?: number;\n}\n\n/** Marker subtype for the placeholder cells inside a merged range (top-left holds the value). */\nexport interface MergedCell extends Cell {\n merged: true;\n}\n\nconst validateCoord = (row: number, col: number): void => {\n if (!Number.isInteger(row) || row < 1 || row > MAX_ROW) {\n throw new OpenXmlSchemaError(`Cell row ${row} out of range [1, ${MAX_ROW}]`);\n }\n if (!Number.isInteger(col) || col < 1 || col > MAX_COL) {\n throw new OpenXmlSchemaError(`Cell col ${col} out of range [1, ${MAX_COL}]`);\n }\n};\n\n/** Build a fresh Cell. Validates coordinates against the OOXML grid bounds. */\nexport function makeCell(row: number, col: number, value: CellValue = null, styleId = 0): Cell {\n validateCoord(row, col);\n return { row, col, value, styleId };\n}\n\n/** Format a Cell's coordinate as the canonical \"A1\" string. */\nexport function getCoordinate(c: Cell): string {\n return `${columnLetterFromIndex(c.col)}${c.row}`;\n}\n\n/**\n * Direct value setter. No type inference, no validation beyond the union — the\n * caller is in charge. Use {@link bindValue} for the \"do what I mean\" path.\n */\nexport function setCellValue(c: Cell, value: CellValue): void {\n c.value = value;\n}\n\n/**\n * \"Smart\" setter: infers the cell value from a JS runtime value.\n * - `string` starting with `=` → formula\n * - `string` matching an Excel error token → error variant\n * - other primitives / Date / null pass through verbatim\n *\n * Intentionally not the default — explicit is clearer for typed code, and\n * inferring on every write costs measurable time on the worksheet write hot\n * path.\n */\nexport function bindValue(c: Cell, value: number | string | boolean | Date | null): void {\n if (typeof value === 'string') {\n if (value.length > 0 && value.charCodeAt(0) === 61 /* '=' */) {\n setFormula(c, value.slice(1));\n return;\n }\n if (ERROR_CODES.has(value)) {\n c.value = { kind: 'error', code: value as ExcelErrorCode };\n return;\n }\n c.value = value;\n return;\n }\n c.value = value;\n}\n\n// ---- formula setters -------------------------------------------------------\n\n/** Plain `=A1+B1` style formula. Cached value is optional but recommended for round-trip. */\nexport function setFormula(c: Cell, formula: string, opts?: { cachedValue?: FormulaValue['cachedValue'] }): void {\n const v: FormulaValue = {\n kind: 'formula',\n t: 'normal',\n formula,\n ...(opts?.cachedValue !== undefined ? { cachedValue: opts.cachedValue } : {}),\n };\n c.value = v;\n}\n\n/** Array (CSE) formula spanning a `ref` range. */\nexport function setArrayFormula(\n c: Cell,\n ref: string,\n formula: string,\n opts?: { cachedValue?: FormulaValue['cachedValue'] },\n): void {\n const v: FormulaValue = {\n kind: 'formula',\n t: 'array',\n formula,\n ref,\n ...(opts?.cachedValue !== undefined ? { cachedValue: opts.cachedValue } : {}),\n };\n c.value = v;\n}\n\n/**\n * Shared formula. The first cell in the group carries the formula text + ref;\n * subsequent cells with the same `si` carry only the index and Excel\n * reconstructs the formula via reference shifting.\n */\nexport function setSharedFormula(\n c: Cell,\n si: number,\n formula?: string,\n ref?: string,\n opts?: { cachedValue?: FormulaValue['cachedValue'] },\n): void {\n if (!Number.isInteger(si) || si < 0) {\n throw new OpenXmlSchemaError(`setSharedFormula: si must be a non-negative integer; got ${si}`);\n }\n const v: FormulaValue = {\n kind: 'formula',\n t: 'shared',\n formula: formula ?? '',\n si,\n ...(ref !== undefined ? { ref } : {}),\n ...(opts?.cachedValue !== undefined ? { cachedValue: opts.cachedValue } : {}),\n };\n c.value = v;\n}\n\n/**\n * Excel data-table formula (`<f t=\"dataTable\">`). These appear as the \"What-if\n * Analysis → Data Table\" feature output: a 1- or 2-variable sensitivity grid\n * where the formula references one or two input cells. The wire format mirrors\n * openpyxl's `DataTableFormula`:\n *\n * - `ref` — inclusive cell range the formula spans.\n * - `r1`, `r2`— input cell coordinates (\"$A$1\" etc.).\n * - `dt2D` — true for two-variable tables (uses both r1 and r2).\n * - `dtr` — row-direction flag (true) vs column-direction (false).\n * - `del1`/`del2` — Excel marks one of these true when the\n * corresponding input cell has been deleted; the formula keeps round-tripping\n * so Excel can show the warning state.\n * - `aca`/`ca` — alwaysCalculate / calculate flags.\n */\nexport interface DataTableFormulaOpts {\n ref: string;\n r1?: string;\n r2?: string;\n dt2D?: boolean;\n dtr?: boolean;\n del1?: boolean;\n del2?: boolean;\n aca?: boolean;\n ca?: boolean;\n cachedValue?: FormulaValue['cachedValue'];\n}\n\n/**\n * Set a data-table formula on a cell. Preserves all the dt-specific attributes\n * so the writer can re-emit `<f t=\"dataTable\" r1=\"...\" />` verbatim and Excel\n * keeps treating the cell as a Data Table cell.\n */\nexport function setDataTableFormula(c: Cell, formula: string, opts: DataTableFormulaOpts): void {\n const v: FormulaValue = {\n kind: 'formula',\n t: 'dataTable',\n formula,\n ref: opts.ref,\n ...(opts.r1 !== undefined ? { r1: opts.r1 } : {}),\n ...(opts.r2 !== undefined ? { r2: opts.r2 } : {}),\n ...(opts.dt2D !== undefined ? { dt2D: opts.dt2D } : {}),\n ...(opts.dtr !== undefined ? { dtr: opts.dtr } : {}),\n ...(opts.del1 !== undefined ? { del1: opts.del1 } : {}),\n ...(opts.del2 !== undefined ? { del2: opts.del2 } : {}),\n ...(opts.aca !== undefined ? { aca: opts.aca } : {}),\n ...(opts.ca !== undefined ? { ca: opts.ca } : {}),\n ...(opts.cachedValue !== undefined ? { cachedValue: opts.cachedValue } : {}),\n };\n c.value = v;\n}\n\n// ---- value-shape helpers ---------------------------------------------------\n\n/** Build a `{ kind: 'error', code }` cell value. */\nexport function makeErrorValue(code: ExcelErrorCode): { kind: 'error'; code: ExcelErrorCode } {\n if (!ERROR_CODES.has(code)) {\n throw new OpenXmlSchemaError(`makeErrorValue: unknown error code \"${code}\"`);\n }\n return Object.freeze({ kind: 'error', code });\n}\n\n/** Build a `{ kind: 'duration', ms }` cell value. */\nexport function makeDurationValue(ms: number): { kind: 'duration'; ms: number } {\n if (!Number.isFinite(ms)) {\n throw new OpenXmlSchemaError(`makeDurationValue: ms \"${ms}\" is not finite`);\n }\n return Object.freeze({ kind: 'duration', ms });\n}\n\n/** True iff `c.value` is the formula variant. */\nexport function isFormulaCell(c: Cell): boolean {\n return isFormulaValue(c.value);\n}\n\n/** True iff `c.value` is the rich-text variant. */\nexport function isRichTextCell(c: Cell): boolean {\n return isRichTextValue(c.value);\n}\n\n/** Returns true iff the cell has no content. */\nexport function isEmptyCell(c: Cell): boolean {\n return c.value === null;\n}\n\n/**\n * Type guard for `MergedCell` — true iff the cell is a placeholder for a\n * merged-range covered cell (the top-left of a merged range holds the value;\n * the rest are `MergedCell`). Use this to filter merge-placeholders out of\n * value-walking loops.\n */\nexport function isMergedCell(c: Cell): c is MergedCell {\n return (c as MergedCell).merged === true;\n}\n\n/** Returns true iff the cell holds an Excel error value (`#REF!`, `#NAME?`, …). */\nexport function isErrorCell(c: Cell): boolean {\n return isErrorValue(c.value);\n}\n\n/**\n * Get the formula text from a formula-bearing cell, or `undefined` for\n * non-formula cells. Equivalent to: isFormulaValue(c.value) ? c.value.formula :\n * undefined but spares callers the type-narrow + member access.\n */\nexport function getFormulaText(c: Cell): string | undefined {\n return isFormulaValue(c.value) ? c.value.formula : undefined;\n}\n\n/**\n * Get the cached value Excel last computed for a formula cell, or `undefined`\n * for non-formula / uncached cells. Useful for `data_only` read paths that want\n * the displayed result without re-evaluating.\n */\nexport function getCachedFormulaValue(c: Cell): number | string | boolean | undefined {\n return isFormulaValue(c.value) ? c.value.cachedValue : undefined;\n}\n\n// ---- value-level type guards + coercion ----------------------------------\n\n/** True iff `v` is the formula variant. */\nexport function isFormulaValue(v: CellValue): v is FormulaValue {\n return typeof v === 'object' && v !== null && (v as { kind?: string }).kind === 'formula';\n}\n\n/** True iff `v` is the rich-text variant. */\nexport function isRichTextValue(v: CellValue): v is { kind: 'rich-text'; runs: RichText } {\n return typeof v === 'object' && v !== null && (v as { kind?: string }).kind === 'rich-text';\n}\n\n/** True iff `v` is the error variant. */\nexport function isErrorValue(v: CellValue): v is { kind: 'error'; code: ExcelErrorCode } {\n return typeof v === 'object' && v !== null && (v as { kind?: string }).kind === 'error';\n}\n\n/** True iff `v` is the duration variant. */\nexport function isDurationValue(v: CellValue): v is { kind: 'duration'; ms: number } {\n return typeof v === 'object' && v !== null && (v as { kind?: string }).kind === 'duration';\n}\n\nexport interface CellValueAsStringOptions {\n /** Renderer for `Date` cells. Defaults to `d => d.toISOString()`. */\n dateFormat?: (value: Date) => string;\n /** Replacement for the `null` cell value. Defaults to `''`. */\n emptyText?: string;\n}\n\n/**\n * Coerce a CellValue to its plain-string display form. Numbers / booleans\n * convert via `String`; rich text concatenates run text; formulas yield the\n * cached value (or empty string when uncached); errors yield their Excel token;\n * durations yield `\"<ms> ms\"` with no formatting; Dates yield\n * `Date.toISOString()`; `null` yields `\"\"`.\n *\n * Pass `opts.dateFormat` to override the Date renderer (e.g. a locale-specific\n * format) and `opts.emptyText` to substitute a different placeholder for\n * `null` cells.\n */\nexport function cellValueAsString(v: CellValue, opts?: CellValueAsStringOptions): string {\n const emptyText = opts?.emptyText ?? '';\n if (v === null) return emptyText;\n if (typeof v === 'string') return v;\n if (typeof v === 'number' || typeof v === 'boolean') return String(v);\n if (v instanceof Date) return opts?.dateFormat ? opts.dateFormat(v) : v.toISOString();\n if (isRichTextValue(v)) return richTextToString(v.runs);\n if (isFormulaValue(v)) {\n if (v.cachedValue === undefined) return '';\n return typeof v.cachedValue === 'string' ? v.cachedValue : String(v.cachedValue);\n }\n if (isErrorValue(v)) return v.code;\n if (isDurationValue(v)) return `${v.ms} ms`;\n return '';\n}\n\n/**\n * Coerce a CellValue to `boolean | undefined`. Booleans pass through; `'TRUE'`\n * / `'true'` and `'FALSE'` / `'false'` (case-insensitive) parse to true /\n * false; numbers yield `false` for 0 and `true` for any other finite value\n * (matching Excel's truthy-number coercion); formula cells return their cached\n * boolean if any. Everything else (null, Date, error, duration, rich-text,\n * non-bool strings) yields `undefined`.\n */\nexport function cellValueAsBoolean(v: CellValue): boolean | undefined {\n if (typeof v === 'boolean') return v;\n if (typeof v === 'number') {\n if (!Number.isFinite(v)) return undefined;\n return v !== 0;\n }\n if (typeof v === 'string') {\n const lc = v.toLowerCase();\n if (lc === 'true') return true;\n if (lc === 'false') return false;\n return undefined;\n }\n if (isFormulaValue(v) && typeof v.cachedValue === 'boolean') return v.cachedValue;\n return undefined;\n}\n\n/**\n * Coerce a CellValue to a `Date` when one is meaningful. Pass-through for\n * `Date`-typed values; ISO-8601 strings (anything `new Date(s)` parses to a\n * finite time) round-trip; durations are interpreted as `new Date(ms)`.\n * Numbers, booleans, formulas, errors, rich text, and null all return\n * `undefined` — this helper does **not** apply the Excel-serial-to-Date\n * conversion (use `excelToDate` for that).\n */\nexport function cellValueAsDate(v: CellValue): Date | undefined {\n if (v instanceof Date) return v;\n if (typeof v === 'string') {\n if (v === '') return undefined;\n const t = Date.parse(v);\n if (!Number.isFinite(t)) return undefined;\n return new Date(t);\n }\n if (isDurationValue(v)) return new Date(v.ms);\n return undefined;\n}\n\n/**\n * Coerce a CellValue to a number when one is meaningful. Booleans yield 0/1;\n * numeric strings parse via `Number(s)`; rich-text concats then parses;\n * formulas with a numeric cached value pass through. Returns `undefined` when\n * there's no sensible numeric reading (text strings, errors, dates, durations,\n * null, empty).\n */\nexport function cellValueAsNumber(v: CellValue): number | undefined {\n if (v === null) return undefined;\n if (typeof v === 'number') return Number.isFinite(v) ? v : undefined;\n if (typeof v === 'boolean') return v ? 1 : 0;\n if (typeof v === 'string') {\n if (v === '') return undefined;\n const n = Number(v);\n return Number.isFinite(n) ? n : undefined;\n }\n if (isFormulaValue(v) && typeof v.cachedValue === 'number') return v.cachedValue;\n return undefined;\n}\n\n/**\n * Map a CellValue to the most natural JS primitive for display / export.\n * Unlike `cellValueAsString`/`cellValueAsNumber`/etc., which each force a\n * single target type and return `undefined` when the value can't be coerced,\n * this returns whatever primitive best represents the union variant:\n *\n * - `null` → `null`\n * - `string` / `number` / `boolean` / `Date` → passthrough\n * - rich text → joined run text (`string`)\n * - formula → recursive on `cachedValue` (`null` when uncached)\n * - error → error code (`string`)\n * - duration → `ms` (`number`)\n */\nexport function cellValueAsPrimitive(v: CellValue): string | number | boolean | Date | null {\n if (v === null) return null;\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') return v;\n if (v instanceof Date) return v;\n if (isRichTextValue(v)) return richTextToString(v.runs);\n if (isFormulaValue(v)) {\n return v.cachedValue === undefined ? null : v.cachedValue;\n }\n if (isErrorValue(v)) return v.code;\n if (isDurationValue(v)) return v.ms;\n return null;\n}\n"],"mappings":";;;;AA2CA,SAAgB,YAAY,MAAc,MAA4B;CACpE,IAAI,OAAO,SAAS,UAClB,MAAM,IAAI,mBAAmB,oCAAoC;CAEnE,OAAO,OAAO,OAAO,SAAS,KAAA,IAAY;EAAE;EAAM;CAAK,IAAI,EAAE,KAAK,CAAC;AACrE;AAEA,SAAgB,aAAa,MAA8E;CACzG,MAAM,MAAM,KAAK,KAAK,MAAO,OAAO,SAAS,CAAC,IAAK,IAAgB,YAAY,EAAE,MAAM,EAAE,IAAI,CAAE;CAC/F,OAAO,OAAO,OAAO,GAAG;AAC1B;;;;;AAMA,SAAgB,iBAAiB,IAAsB;CACrD,IAAI,MAAM;CACV,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE;CAC7B,OAAO;AACT;;;ACQA,MAAM,iBAAiB,KAAa,QAAsB;CACxD,IAAI,CAAC,OAAO,UAAU,GAAG,KAAK,MAAM,KAAK,MAAA,SACvC,MAAM,IAAI,mBAAmB,YAAY,IAAI,oBAAoB,QAAQ,EAAE;CAE7E,IAAI,CAAC,OAAO,UAAU,GAAG,KAAK,MAAM,KAAK,MAAA,OACvC,MAAM,IAAI,mBAAmB,YAAY,IAAI,oBAAoB,QAAQ,EAAE;AAE/E;;AAGA,SAAgB,SAAS,KAAa,KAAa,QAAmB,MAAM,UAAU,GAAS;CAC7F,cAAc,KAAK,GAAG;CACtB,OAAO;EAAE;EAAK;EAAK;EAAO;CAAQ;AACpC;;AAGA,SAAgB,cAAc,GAAiB;CAC7C,OAAO,GAAG,sBAAsB,EAAE,GAAG,IAAI,EAAE;AAC7C;;;;;AAMA,SAAgB,aAAa,GAAS,OAAwB;CAC5D,EAAE,QAAQ;AACZ;;;;;;;;;;;AAYA,SAAgB,UAAU,GAAS,OAAsD;CACvF,IAAI,OAAO,UAAU,UAAU;EAC7B,IAAI,MAAM,SAAS,KAAK,MAAM,WAAW,CAAC,MAAM,IAAc;GAC5D,WAAW,GAAG,MAAM,MAAM,CAAC,CAAC;GAC5B;EACF;EACA,IAAI,YAAY,IAAI,KAAK,GAAG;GAC1B,EAAE,QAAQ;IAAE,MAAM;IAAS,MAAM;GAAwB;GACzD;EACF;EACA,EAAE,QAAQ;EACV;CACF;CACA,EAAE,QAAQ;AACZ;;AAKA,SAAgB,WAAW,GAAS,SAAiB,MAA4D;CAO/G,EAAE,QAAQ;EALR,MAAM;EACN,GAAG;EACH;EACA,GAAI,MAAM,gBAAgB,KAAA,IAAY,EAAE,aAAa,KAAK,YAAY,IAAI,CAAC;CAEnE;AACZ;;AAGA,SAAgB,gBACd,GACA,KACA,SACA,MACM;CAQN,EAAE,QAAQ;EANR,MAAM;EACN,GAAG;EACH;EACA;EACA,GAAI,MAAM,gBAAgB,KAAA,IAAY,EAAE,aAAa,KAAK,YAAY,IAAI,CAAC;CAEnE;AACZ;;;;;;AAOA,SAAgB,iBACd,GACA,IACA,SACA,KACA,MACM;CACN,IAAI,CAAC,OAAO,UAAU,EAAE,KAAK,KAAK,GAChC,MAAM,IAAI,mBAAmB,4DAA4D,IAAI;CAU/F,EAAE,QAAQ;EAPR,MAAM;EACN,GAAG;EACH,SAAS,WAAW;EACpB;EACA,GAAI,QAAQ,KAAA,IAAY,EAAE,IAAI,IAAI,CAAC;EACnC,GAAI,MAAM,gBAAgB,KAAA,IAAY,EAAE,aAAa,KAAK,YAAY,IAAI,CAAC;CAEnE;AACZ;;;;;;AAmCA,SAAgB,oBAAoB,GAAS,SAAiB,MAAkC;CAgB9F,EAAE,QAAQ;EAdR,MAAM;EACN,GAAG;EACH;EACA,KAAK,KAAK;EACV,GAAI,KAAK,OAAO,KAAA,IAAY,EAAE,IAAI,KAAK,GAAG,IAAI,CAAC;EAC/C,GAAI,KAAK,OAAO,KAAA,IAAY,EAAE,IAAI,KAAK,GAAG,IAAI,CAAC;EAC/C,GAAI,KAAK,SAAS,KAAA,IAAY,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;EACrD,GAAI,KAAK,QAAQ,KAAA,IAAY,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC;EAClD,GAAI,KAAK,SAAS,KAAA,IAAY,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;EACrD,GAAI,KAAK,SAAS,KAAA,IAAY,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;EACrD,GAAI,KAAK,QAAQ,KAAA,IAAY,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC;EAClD,GAAI,KAAK,OAAO,KAAA,IAAY,EAAE,IAAI,KAAK,GAAG,IAAI,CAAC;EAC/C,GAAI,KAAK,gBAAgB,KAAA,IAAY,EAAE,aAAa,KAAK,YAAY,IAAI,CAAC;CAElE;AACZ;;AAKA,SAAgB,eAAe,MAA+D;CAC5F,IAAI,CAAC,YAAY,IAAI,IAAI,GACvB,MAAM,IAAI,mBAAmB,uCAAuC,KAAK,EAAE;CAE7E,OAAO,OAAO,OAAO;EAAE,MAAM;EAAS;CAAK,CAAC;AAC9C;;AAGA,SAAgB,kBAAkB,IAA8C;CAC9E,IAAI,CAAC,OAAO,SAAS,EAAE,GACrB,MAAM,IAAI,mBAAmB,0BAA0B,GAAG,gBAAgB;CAE5E,OAAO,OAAO,OAAO;EAAE,MAAM;EAAY;CAAG,CAAC;AAC/C;;AAGA,SAAgB,cAAc,GAAkB;CAC9C,OAAO,eAAe,EAAE,KAAK;AAC/B;;AAGA,SAAgB,eAAe,GAAkB;CAC/C,OAAO,gBAAgB,EAAE,KAAK;AAChC;;AAGA,SAAgB,YAAY,GAAkB;CAC5C,OAAO,EAAE,UAAU;AACrB;;;;;;;AAQA,SAAgB,aAAa,GAA0B;CACrD,OAAQ,EAAiB,WAAW;AACtC;;AAGA,SAAgB,YAAY,GAAkB;CAC5C,OAAO,aAAa,EAAE,KAAK;AAC7B;;;;;;AAOA,SAAgB,eAAe,GAA6B;CAC1D,OAAO,eAAe,EAAE,KAAK,IAAI,EAAE,MAAM,UAAU,KAAA;AACrD;;;;;;AAOA,SAAgB,sBAAsB,GAAgD;CACpF,OAAO,eAAe,EAAE,KAAK,IAAI,EAAE,MAAM,cAAc,KAAA;AACzD;;AAKA,SAAgB,eAAe,GAAiC;CAC9D,OAAO,OAAO,MAAM,YAAY,MAAM,QAAS,EAAwB,SAAS;AAClF;;AAGA,SAAgB,gBAAgB,GAA0D;CACxF,OAAO,OAAO,MAAM,YAAY,MAAM,QAAS,EAAwB,SAAS;AAClF;;AAGA,SAAgB,aAAa,GAA4D;CACvF,OAAO,OAAO,MAAM,YAAY,MAAM,QAAS,EAAwB,SAAS;AAClF;;AAGA,SAAgB,gBAAgB,GAAqD;CACnF,OAAO,OAAO,MAAM,YAAY,MAAM,QAAS,EAAwB,SAAS;AAClF;;;;;;;;;;;;AAoBA,SAAgB,kBAAkB,GAAc,MAAyC;CACvF,MAAM,YAAY,MAAM,aAAa;CACrC,IAAI,MAAM,MAAM,OAAO;CACvB,IAAI,OAAO,MAAM,UAAU,OAAO;CAClC,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,WAAW,OAAO,OAAO,CAAC;CACpE,IAAI,aAAa,MAAM,OAAO,MAAM,aAAa,KAAK,WAAW,CAAC,IAAI,EAAE,YAAY;CACpF,IAAI,gBAAgB,CAAC,GAAG,OAAO,iBAAiB,EAAE,IAAI;CACtD,IAAI,eAAe,CAAC,GAAG;EACrB,IAAI,EAAE,gBAAgB,KAAA,GAAW,OAAO;EACxC,OAAO,OAAO,EAAE,gBAAgB,WAAW,EAAE,cAAc,OAAO,EAAE,WAAW;CACjF;CACA,IAAI,aAAa,CAAC,GAAG,OAAO,EAAE;CAC9B,IAAI,gBAAgB,CAAC,GAAG,OAAO,GAAG,EAAE,GAAG;CACvC,OAAO;AACT;;;;;;;;;AAUA,SAAgB,mBAAmB,GAAmC;CACpE,IAAI,OAAO,MAAM,WAAW,OAAO;CACnC,IAAI,OAAO,MAAM,UAAU;EACzB,IAAI,CAAC,OAAO,SAAS,CAAC,GAAG,OAAO,KAAA;EAChC,OAAO,MAAM;CACf;CACA,IAAI,OAAO,MAAM,UAAU;EACzB,MAAM,KAAK,EAAE,YAAY;EACzB,IAAI,OAAO,QAAQ,OAAO;EAC1B,IAAI,OAAO,SAAS,OAAO;EAC3B;CACF;CACA,IAAI,eAAe,CAAC,KAAK,OAAO,EAAE,gBAAgB,WAAW,OAAO,EAAE;AAExE;;;;;;;;;AAUA,SAAgB,gBAAgB,GAAgC;CAC9D,IAAI,aAAa,MAAM,OAAO;CAC9B,IAAI,OAAO,MAAM,UAAU;EACzB,IAAI,MAAM,IAAI,OAAO,KAAA;EACrB,MAAM,IAAI,KAAK,MAAM,CAAC;EACtB,IAAI,CAAC,OAAO,SAAS,CAAC,GAAG,OAAO,KAAA;EAChC,OAAO,IAAI,KAAK,CAAC;CACnB;CACA,IAAI,gBAAgB,CAAC,GAAG,OAAO,IAAI,KAAK,EAAE,EAAE;AAE9C;;;;;;;;AASA,SAAgB,kBAAkB,GAAkC;CAClE,IAAI,MAAM,MAAM,OAAO,KAAA;CACvB,IAAI,OAAO,MAAM,UAAU,OAAO,OAAO,SAAS,CAAC,IAAI,IAAI,KAAA;CAC3D,IAAI,OAAO,MAAM,WAAW,OAAO,IAAI,IAAI;CAC3C,IAAI,OAAO,MAAM,UAAU;EACzB,IAAI,MAAM,IAAI,OAAO,KAAA;EACrB,MAAM,IAAI,OAAO,CAAC;EAClB,OAAO,OAAO,SAAS,CAAC,IAAI,IAAI,KAAA;CAClC;CACA,IAAI,eAAe,CAAC,KAAK,OAAO,EAAE,gBAAgB,UAAU,OAAO,EAAE;AAEvE;;;;;;;;;;;;;;AAeA,SAAgB,qBAAqB,GAAuD;CAC1F,IAAI,MAAM,MAAM,OAAO;CACvB,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,OAAO,MAAM,WAAW,OAAO;CACrF,IAAI,aAAa,MAAM,OAAO;CAC9B,IAAI,gBAAgB,CAAC,GAAG,OAAO,iBAAiB,EAAE,IAAI;CACtD,IAAI,eAAe,CAAC,GAClB,OAAO,EAAE,gBAAgB,KAAA,IAAY,OAAO,EAAE;CAEhD,IAAI,aAAa,CAAC,GAAG,OAAO,EAAE;CAC9B,IAAI,gBAAgB,CAAC,GAAG,OAAO,EAAE;CACjC,OAAO;AACT"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"cell-style-DuvS_Lsg.mjs","names":[],"sources":["../src/utils/stable-stringify.ts","../src/styles/alignment.ts","../src/styles/borders.ts","../src/styles/fills.ts","../src/styles/fonts.ts","../src/styles/numbers.ts","../src/styles/stylesheet.ts","../src/styles/named-styles.ts","../src/styles/protection.ts","../src/styles/cell-style.ts"],"sourcesContent":["// Deterministic JSON serialiser. Used by the Stylesheet pool to dedupe\n// value-object entries (Font / Fill / Border / CellXf …) by structural\n// equality regardless of property insertion order.\n//\n// Implemented as a JSON.stringify replacer that returns a key-sorted\n// shallow copy of every plain object it encounters; arrays keep their\n// element order. Circular references propagate as JSON.stringify's\n// native RangeError (\"Maximum call stack size exceeded\").\n\nconst sortKeysReplacer = (_key: string, value: unknown): unknown => {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return value;\n const obj = value as Record<string, unknown>;\n const keys = Object.keys(obj).sort();\n const out: Record<string, unknown> = {};\n for (const k of keys) out[k] = obj[k];\n return out;\n};\n\n/**\n * Stringify `value` with object keys sorted recursively. Equal logical\n * values produce the same string regardless of insertion order; arrays\n * preserve their element order. Circular references throw (the error\n * comes straight from JSON.stringify's stack-overflow check).\n */\nexport function stableStringify(value: unknown): string {\n return JSON.stringify(value, sortKeysReplacer);\n}\n","// Cell alignment value object. Mirrors openpyxl/openpyxl/styles/alignment.py.\n\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\n\nexport type HorizontalAlignment =\n | 'general'\n | 'left'\n | 'center'\n | 'right'\n | 'fill'\n | 'justify'\n | 'centerContinuous'\n | 'distributed';\n\nexport type VerticalAlignment = 'top' | 'center' | 'bottom' | 'justify' | 'distributed';\n\nexport const HORIZONTAL_ALIGNMENTS: ReadonlyArray<HorizontalAlignment> = Object.freeze([\n 'general',\n 'left',\n 'center',\n 'right',\n 'fill',\n 'justify',\n 'centerContinuous',\n 'distributed',\n]);\nexport const VERTICAL_ALIGNMENTS: ReadonlyArray<VerticalAlignment> = Object.freeze([\n 'top',\n 'center',\n 'bottom',\n 'justify',\n 'distributed',\n]);\n\nconst HORIZONTAL_SET: ReadonlySet<string> = new Set(HORIZONTAL_ALIGNMENTS);\nconst VERTICAL_SET: ReadonlySet<string> = new Set(VERTICAL_ALIGNMENTS);\n\nexport interface Alignment {\n readonly horizontal?: HorizontalAlignment;\n readonly vertical?: VerticalAlignment;\n /** 0..180 (degrees) OR 255 (vertical stacked text). */\n readonly textRotation?: number;\n readonly wrapText?: boolean;\n readonly shrinkToFit?: boolean;\n /** 0..255 indent levels. */\n readonly indent?: number;\n /** -255..255 relative indent. */\n readonly relativeIndent?: number;\n readonly justifyLastLine?: boolean;\n /** 0 = context-dependent, 1 = LTR, 2 = RTL. */\n readonly readingOrder?: number;\n}\n\nexport function makeAlignment(opts: Partial<Alignment> = {}): Alignment {\n const out: { -readonly [K in keyof Alignment]: Alignment[K] } = {};\n if (opts.horizontal !== undefined) {\n if (!HORIZONTAL_SET.has(opts.horizontal)) {\n throw new OpenXmlSchemaError(\n `Alignment horizontal must be one of [${HORIZONTAL_ALIGNMENTS.join(', ')}]; got \"${opts.horizontal}\"`,\n );\n }\n out.horizontal = opts.horizontal;\n }\n if (opts.vertical !== undefined) {\n if (!VERTICAL_SET.has(opts.vertical)) {\n throw new OpenXmlSchemaError(\n `Alignment vertical must be one of [${VERTICAL_ALIGNMENTS.join(', ')}]; got \"${opts.vertical}\"`,\n );\n }\n out.vertical = opts.vertical;\n }\n if (opts.textRotation !== undefined) {\n const r = opts.textRotation;\n if (!Number.isInteger(r) || r < 0 || (r > 180 && r !== 255)) {\n throw new OpenXmlSchemaError(`Alignment textRotation must be 0..180 or 255; got ${r}`);\n }\n out.textRotation = r;\n }\n if (opts.wrapText !== undefined) out.wrapText = opts.wrapText;\n if (opts.shrinkToFit !== undefined) out.shrinkToFit = opts.shrinkToFit;\n if (opts.indent !== undefined) {\n if (!Number.isFinite(opts.indent) || opts.indent < 0 || opts.indent > 255) {\n throw new OpenXmlSchemaError(`Alignment indent must be 0..255; got ${opts.indent}`);\n }\n out.indent = opts.indent;\n }\n if (opts.relativeIndent !== undefined) {\n const r = opts.relativeIndent;\n if (!Number.isFinite(r) || r < -255 || r > 255) {\n throw new OpenXmlSchemaError(`Alignment relativeIndent must be -255..255; got ${r}`);\n }\n out.relativeIndent = r;\n }\n if (opts.justifyLastLine !== undefined) out.justifyLastLine = opts.justifyLastLine;\n if (opts.readingOrder !== undefined) {\n if (!Number.isFinite(opts.readingOrder) || opts.readingOrder < 0) {\n throw new OpenXmlSchemaError(`Alignment readingOrder must be >= 0; got ${opts.readingOrder}`);\n }\n out.readingOrder = opts.readingOrder;\n }\n return Object.freeze(out);\n}\n\nexport const DEFAULT_ALIGNMENT: Alignment = makeAlignment();\n\nconst HORIZONTAL_TO_TEXT_ALIGN: Record<HorizontalAlignment, string | undefined> = {\n general: undefined, // Excel: numbers right, text left — caller decides per cell type.\n left: 'left',\n center: 'center',\n right: 'right',\n fill: 'left', // CSS has no fill; left is the closest non-stretched approximation.\n justify: 'justify',\n centerContinuous: 'center',\n distributed: 'justify',\n};\n\nconst VERTICAL_TO_VERTICAL_ALIGN: Record<VerticalAlignment, string | undefined> = {\n top: 'top',\n center: 'middle',\n bottom: 'bottom',\n justify: 'middle',\n distributed: 'middle',\n};\n\n/**\n * Translate an {@link Alignment} to a CSS-property record suitable for\n * HTML preview. `horizontal` → `text-align`, `vertical` →\n * `vertical-align` (table-cell semantics), `wrapText` → `white-space:\n * pre-wrap`, `textRotation` → `transform: rotate(<-deg>)` (Excel rotates\n * counter-clockwise relative to CSS) plus `transform-origin` to keep\n * the text anchored, and `indent` → `padding-left: <n>em`. `255`\n * stacked-text rotation maps to a 180° flip with `writing-mode`.\n *\n * Empty / undefined Alignment returns `{}`.\n */\nexport function alignmentToCss(alignment: Alignment | undefined): Record<string, string> {\n const css: Record<string, string> = {};\n if (!alignment) return css;\n if (alignment.horizontal !== undefined) {\n const ta = HORIZONTAL_TO_TEXT_ALIGN[alignment.horizontal];\n if (ta !== undefined) css['text-align'] = ta;\n }\n if (alignment.vertical !== undefined) {\n const va = VERTICAL_TO_VERTICAL_ALIGN[alignment.vertical];\n if (va !== undefined) css['vertical-align'] = va;\n }\n if (alignment.wrapText) css['white-space'] = 'pre-wrap';\n if (alignment.textRotation !== undefined) {\n if (alignment.textRotation === 255) {\n css['writing-mode'] = 'vertical-rl';\n } else if (alignment.textRotation !== 0) {\n // Excel: positive degrees rotate counter-clockwise; CSS rotate() is clockwise → negate.\n css['transform'] = `rotate(-${alignment.textRotation}deg)`;\n css['transform-origin'] = 'center center';\n }\n }\n if (alignment.indent !== undefined && alignment.indent > 0) {\n css['padding-left'] = `${alignment.indent}em`;\n }\n return css;\n}\n","// Border / Side value objects. Mirrors openpyxl/openpyxl/styles/borders.py.\n//\n// A `Border` describes the edges drawn around a cell. Each edge is a `Side`\n// carrying a stroke style and an optional `Color`. These are plain readonly\n// objects; the `make*` constructors freeze their results so the Stylesheet pool\n// can dedupe by reference identity once we wire it up.\n\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\nimport { type Color, colorToHex, makeColor } from './colors.js';\n\nexport type SideStyle =\n // ECMA-376 §18.18.3 ST_BorderStyle lists `none` (no stroke) as a first-class\n // value; Excel omits the side element for it, but OnlyOffice writes it out\n // explicitly, so we must accept and round-trip it (issue #99).\n | 'none'\n | 'thin'\n | 'medium'\n | 'thick'\n | 'double'\n | 'hair'\n | 'dotted'\n | 'dashed'\n | 'dashDot'\n | 'dashDotDot'\n | 'mediumDashed'\n | 'mediumDashDot'\n | 'mediumDashDotDot'\n | 'slantDashDot';\n\nexport const SIDE_STYLES: ReadonlyArray<SideStyle> = Object.freeze([\n 'none',\n 'thin',\n 'medium',\n 'thick',\n 'double',\n 'hair',\n 'dotted',\n 'dashed',\n 'dashDot',\n 'dashDotDot',\n 'mediumDashed',\n 'mediumDashDot',\n 'mediumDashDotDot',\n 'slantDashDot',\n]);\n\nconst SIDE_STYLE_SET: ReadonlySet<string> = new Set(SIDE_STYLES);\n\nexport interface Side {\n readonly style?: SideStyle;\n readonly color?: Color;\n}\n\n/** Build an immutable {@link Side}. */\nexport function makeSide(opts: Partial<Side> = {}): Side {\n const out: { -readonly [K in keyof Side]: Side[K] } = {};\n if (opts.style !== undefined) {\n if (!SIDE_STYLE_SET.has(opts.style)) {\n throw new OpenXmlSchemaError(`Side style must be one of [${SIDE_STYLES.join(', ')}]; got \"${opts.style}\"`);\n }\n out.style = opts.style;\n }\n if (opts.color !== undefined) {\n // Funnel through makeColor so we re-use its validation + freezing.\n out.color = Object.isFrozen(opts.color) ? opts.color : makeColor(opts.color);\n }\n return Object.freeze(out);\n}\n\nexport interface Border {\n /** Left edge. */\n readonly left?: Side;\n readonly right?: Side;\n readonly top?: Side;\n readonly bottom?: Side;\n /** Diagonal stroke (governed together with diagonalUp / diagonalDown). */\n readonly diagonal?: Side;\n /** Vertical stroke between cells of a merged range. */\n readonly vertical?: Side;\n /** Horizontal stroke between cells of a merged range. */\n readonly horizontal?: Side;\n readonly diagonalUp?: boolean;\n readonly diagonalDown?: boolean;\n /** Outline-only flag; defaults to true. */\n readonly outline?: boolean;\n}\n\n/** Build an immutable {@link Border}. */\nexport function makeBorder(opts: Partial<Border> = {}): Border {\n const out: { -readonly [K in keyof Border]: Border[K] } = {};\n if (opts.left !== undefined) out.left = freezeSide(opts.left);\n if (opts.right !== undefined) out.right = freezeSide(opts.right);\n if (opts.top !== undefined) out.top = freezeSide(opts.top);\n if (opts.bottom !== undefined) out.bottom = freezeSide(opts.bottom);\n if (opts.diagonal !== undefined) out.diagonal = freezeSide(opts.diagonal);\n if (opts.vertical !== undefined) out.vertical = freezeSide(opts.vertical);\n if (opts.horizontal !== undefined) out.horizontal = freezeSide(opts.horizontal);\n if (opts.diagonalUp !== undefined) out.diagonalUp = opts.diagonalUp;\n if (opts.diagonalDown !== undefined) out.diagonalDown = opts.diagonalDown;\n if (opts.outline !== undefined) out.outline = opts.outline;\n return Object.freeze(out);\n}\n\nconst freezeSide = (s: Side): Side => (Object.isFrozen(s) ? s : makeSide(s));\n\n/** Default empty side — convenient sentinel for `no edge stroke`. */\nexport const EMPTY_SIDE: Side = makeSide();\n/** Default empty border — every cell starts here until styled otherwise. */\nexport const DEFAULT_BORDER: Border = makeBorder();\n\n/**\n * Map an Excel {@link SideStyle} to a CSS `border` shorthand fragment (`<width>\n * <style>`). Returns `undefined` for unmappable styles or a missing/no-style\n * side. Colour is appended by the caller.\n */\nfunction sideStyleToCss(style: SideStyle | undefined): string | undefined {\n switch (style) {\n case 'thin':\n case 'hair':\n return '1px solid';\n case 'medium':\n return '2px solid';\n case 'thick':\n return '3px solid';\n case 'double':\n return '3px double';\n case 'dotted':\n return '1px dotted';\n case 'dashed':\n case 'dashDot':\n case 'dashDotDot':\n return '1px dashed';\n case 'mediumDashed':\n case 'mediumDashDot':\n case 'mediumDashDotDot':\n case 'slantDashDot':\n return '2px dashed';\n default:\n return undefined;\n }\n}\n\n/**\n * Translate a {@link Border} to a CSS-property record suitable for HTML\n * preview. Each present side becomes `border-<edge>: <width> <style> <#color>`.\n * Theme/auto/missing colours fall back to `currentColor`. Diagonal / vertical /\n * horizontal sides are skipped (CSS has no native equivalent for in-cell\n * strokes). Empty Border returns `{}`.\n */\nexport function borderToCss(border: Border | undefined): Record<string, string> {\n const css: Record<string, string> = {};\n if (!border) return css;\n const sides: Array<['top' | 'right' | 'bottom' | 'left', Side | undefined]> = [\n ['top', border.top],\n ['right', border.right],\n ['bottom', border.bottom],\n ['left', border.left],\n ];\n for (const [edge, side] of sides) {\n if (!side) continue;\n const stroke = sideStyleToCss(side.style);\n if (stroke === undefined) continue;\n const argb = colorToHex(side.color);\n const colour = argb !== undefined ? `#${argb.slice(2)}` : 'currentColor';\n css[`border-${edge}`] = `${stroke} ${colour}`;\n }\n return css;\n}\n","// Pattern + gradient fill value objects. Mirrors\n// openpyxl/openpyxl/styles/fills.py.\n//\n// In OOXML the cell `<fill>` element wraps exactly one of:\n// * <patternFill patternType=\"…\">[<fgColor>, <bgColor>]</patternFill>\n// * <gradientFill type=\"linear|path\" …>[<stop>, …]</gradientFill>\n//\n// The TS port models the inner variants as a discriminated union via a `kind`\n// tag; the wrapper is reconstructed during XML round-trip (see\n// fills.schema.ts). All values are plain readonly + frozen so the Stylesheet\n// pool can dedupe.\n\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\nimport { type Color, colorToHex, makeColor } from './colors.js';\n\n// ---- pattern fills ---------------------------------------------------------\n\n/** Predefined Excel pattern types. `'none'` lives here too as an explicit value. */\nexport type PatternType =\n | 'none'\n | 'solid'\n | 'darkDown'\n | 'darkGray'\n | 'darkGrid'\n | 'darkHorizontal'\n | 'darkTrellis'\n | 'darkUp'\n | 'darkVertical'\n | 'gray0625'\n | 'gray125'\n | 'lightDown'\n | 'lightGray'\n | 'lightGrid'\n | 'lightHorizontal'\n | 'lightTrellis'\n | 'lightUp'\n | 'lightVertical'\n | 'mediumGray';\n\nexport const PATTERN_TYPES: ReadonlyArray<PatternType> = Object.freeze([\n 'none',\n 'solid',\n 'darkDown',\n 'darkGray',\n 'darkGrid',\n 'darkHorizontal',\n 'darkTrellis',\n 'darkUp',\n 'darkVertical',\n 'gray0625',\n 'gray125',\n 'lightDown',\n 'lightGray',\n 'lightGrid',\n 'lightHorizontal',\n 'lightTrellis',\n 'lightUp',\n 'lightVertical',\n 'mediumGray',\n]);\n\nconst PATTERN_TYPE_SET: ReadonlySet<string> = new Set(PATTERN_TYPES);\n\nexport interface PatternFill {\n readonly kind: 'pattern';\n readonly patternType?: PatternType;\n readonly fgColor?: Color;\n readonly bgColor?: Color;\n}\n\nexport function makePatternFill(opts: Partial<Omit<PatternFill, 'kind'>> = {}): PatternFill {\n const out: { -readonly [K in keyof PatternFill]: PatternFill[K] } = { kind: 'pattern' };\n if (opts.patternType !== undefined) {\n if (!PATTERN_TYPE_SET.has(opts.patternType)) {\n throw new OpenXmlSchemaError(\n `PatternFill patternType must be one of [${PATTERN_TYPES.join(', ')}]; got \"${opts.patternType}\"`,\n );\n }\n out.patternType = opts.patternType;\n }\n if (opts.fgColor !== undefined) out.fgColor = freezeColor(opts.fgColor);\n if (opts.bgColor !== undefined) out.bgColor = freezeColor(opts.bgColor);\n return Object.freeze(out);\n}\n\n// ---- gradient fills --------------------------------------------------------\n\nexport interface GradientStop {\n /** 0..1 ratio along the gradient. */\n readonly position: number;\n readonly color: Color;\n}\n\nexport type GradientFillType = 'linear' | 'path';\n\nexport interface GradientFill {\n readonly kind: 'gradient';\n readonly type: GradientFillType;\n /** Rotation angle (degrees). Ignored when `type === 'path'`. */\n readonly degree?: number;\n /** path-mode insets (0..1 from each edge). */\n readonly left?: number;\n readonly right?: number;\n readonly top?: number;\n readonly bottom?: number;\n /** Colour stops along the gradient. */\n readonly stops: ReadonlyArray<GradientStop>;\n}\n\nexport function makeGradientStop(position: number, color: Color | Partial<Color>): GradientStop {\n if (!Number.isFinite(position) || position < 0 || position > 1) {\n throw new OpenXmlSchemaError(`GradientStop position must be in [0, 1]; got ${position}`);\n }\n return Object.freeze({ position, color: freezeColor(color) });\n}\n\nexport function makeGradientFill(opts: Partial<Omit<GradientFill, 'kind'>> = {}): GradientFill {\n const out: { -readonly [K in keyof GradientFill]: GradientFill[K] } = {\n kind: 'gradient',\n type: opts.type ?? 'linear',\n stops: opts.stops?.map((s) => freezeStop(s)) ?? [],\n };\n if (out.type !== 'linear' && out.type !== 'path') {\n throw new OpenXmlSchemaError(`GradientFill type must be \"linear\" or \"path\"; got \"${String(out.type)}\"`);\n }\n if (opts.degree !== undefined) out.degree = opts.degree;\n if (opts.left !== undefined) out.left = opts.left;\n if (opts.right !== undefined) out.right = opts.right;\n if (opts.top !== undefined) out.top = opts.top;\n if (opts.bottom !== undefined) out.bottom = opts.bottom;\n Object.freeze(out.stops);\n return Object.freeze(out);\n}\n\n// ---- Fill = PatternFill | GradientFill -------------------------------------\n\nexport type Fill = PatternFill | GradientFill;\n\n/**\n * Single-arg constructor that defers to the variant-specific maker based on\n * `kind`. Useful when the caller has a plain object in hand and wants the\n * freeze invariant applied uniformly.\n */\nexport function makeFill(opts: Partial<PatternFill> | Partial<GradientFill>): Fill {\n if (opts.kind === 'gradient') return makeGradientFill(opts as Partial<Omit<GradientFill, 'kind'>>);\n return makePatternFill(opts as Partial<Omit<PatternFill, 'kind'>>);\n}\n\n/** The empty PatternFill — Excel's default cellXf[0] points here. */\nexport const DEFAULT_EMPTY_FILL: Fill = makePatternFill();\n/** The 'gray125' PatternFill — Excel's default cellXf[1]. */\nexport const DEFAULT_GRAY_FILL: Fill = makePatternFill({ patternType: 'gray125' });\n\n// ---- internal helpers ------------------------------------------------------\n\nconst freezeColor = (c: Color | Partial<Color>): Color => (Object.isFrozen(c) ? (c as Color) : makeColor(c));\nconst freezeStop = (s: GradientStop): GradientStop => (Object.isFrozen(s) ? s : makeGradientStop(s.position, s.color));\n\nconst argbToCssHex = (color: Color | undefined): string | undefined => {\n const argb = colorToHex(color);\n return argb ? `#${argb.slice(2)}` : undefined;\n};\n\n/**\n * Translate a {@link Fill} to a CSS-property record suitable for HTML preview.\n * `'solid'` PatternFill renders as `background-color`, other pattern types\n * collapse to bgColor (CSS has no built-in equivalent of Excel hatch patterns).\n * GradientFill emits a CSS `background-image` with `linear-gradient(<angle>,\n * …)` for `type='linear'` or `radial-gradient(circle, …)` for `type='path'`.\n *\n * theme/auto colours and unresolvable inputs are skipped (returns `{}`) so\n * callers can spread without overwriting upstream defaults.\n */\nexport function fillToCss(fill: Fill | undefined): Record<string, string> {\n const css: Record<string, string> = {};\n if (!fill) return css;\n if (fill.kind === 'pattern') {\n if (fill.patternType === 'none' || fill.patternType === undefined) return css;\n if (fill.patternType === 'solid') {\n const fg = argbToCssHex(fill.fgColor);\n if (fg !== undefined) css['background-color'] = fg;\n return css;\n }\n // Non-solid patterns collapse to the bg colour as a coarse approximation;\n // CSS has no native Excel hatch equivalent.\n const bg = argbToCssHex(fill.bgColor) ?? argbToCssHex(fill.fgColor);\n if (bg !== undefined) css['background-color'] = bg;\n return css;\n }\n // Gradient fill — need at least one resolvable stop to emit anything.\n const stopParts: string[] = [];\n for (const s of fill.stops) {\n const hex = argbToCssHex(s.color);\n if (hex === undefined) continue;\n stopParts.push(`${hex} ${(s.position * 100).toFixed(2)}%`);\n }\n if (stopParts.length === 0) return css;\n if (fill.type === 'linear') {\n const angle = fill.degree ?? 0;\n css['background-image'] = `linear-gradient(${angle}deg, ${stopParts.join(', ')})`;\n } else {\n css['background-image'] = `radial-gradient(circle, ${stopParts.join(', ')})`;\n }\n return css;\n}\n","// Cell font value object. Mirrors openpyxl/openpyxl/styles/fonts.py.\n//\n// Font is the most varied of the styles slots — most fields are nested\n// elements with a single `val` attribute (`<sz val=\"11\"/>`), the boolean\n// toggles (`<b/>`, `<i/>`, ...) are presence-only marker tags, and\n// `<color>` is a fully nested object element. The schema layer carries\n// each pattern as its own ElementDef kind.\n\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\nimport { type Color, colorToHex, makeColor } from './colors.js';\n\n/** Underline styles per openpyxl's NestedNoneSet. */\nexport type UnderlineStyle = 'single' | 'double' | 'singleAccounting' | 'doubleAccounting';\nexport const UNDERLINE_STYLES: ReadonlyArray<UnderlineStyle> = Object.freeze([\n 'single',\n 'double',\n 'singleAccounting',\n 'doubleAccounting',\n]);\n\nexport type VertAlign = 'baseline' | 'superscript' | 'subscript';\nexport const VERT_ALIGNS: ReadonlyArray<VertAlign> = Object.freeze(['baseline', 'superscript', 'subscript']);\n\nexport type FontScheme = 'major' | 'minor';\nexport const FONT_SCHEMES: ReadonlyArray<FontScheme> = Object.freeze(['major', 'minor']);\n\nconst UNDERLINE_SET: ReadonlySet<string> = new Set(UNDERLINE_STYLES);\nconst VERT_SET: ReadonlySet<string> = new Set(VERT_ALIGNS);\nconst SCHEME_SET: ReadonlySet<string> = new Set(FONT_SCHEMES);\n\nexport interface Font {\n readonly name?: string;\n readonly charset?: number;\n /** Font family code (0..14). */\n readonly family?: number;\n /** Point size; openpyxl name = `sz`. */\n readonly size?: number;\n readonly color?: Color;\n readonly bold?: boolean;\n readonly italic?: boolean;\n readonly strike?: boolean;\n readonly outline?: boolean;\n readonly shadow?: boolean;\n readonly condense?: boolean;\n readonly extend?: boolean;\n readonly underline?: UnderlineStyle;\n readonly vertAlign?: VertAlign;\n readonly scheme?: FontScheme;\n}\n\nexport function makeFont(opts: Partial<Font> = {}): Font {\n const out: { -readonly [K in keyof Font]: Font[K] } = {};\n if (opts.name !== undefined) {\n if (typeof opts.name !== 'string') {\n throw new OpenXmlSchemaError(`Font name must be a string; got ${typeof opts.name}`);\n }\n out.name = opts.name;\n }\n if (opts.charset !== undefined) {\n if (!Number.isInteger(opts.charset)) {\n throw new OpenXmlSchemaError(`Font charset must be an integer; got ${opts.charset}`);\n }\n out.charset = opts.charset;\n }\n if (opts.family !== undefined) {\n if (!Number.isInteger(opts.family) || opts.family < 0 || opts.family > 14) {\n throw new OpenXmlSchemaError(`Font family must be 0..14; got ${opts.family}`);\n }\n out.family = opts.family;\n }\n if (opts.size !== undefined) {\n if (!Number.isFinite(opts.size) || opts.size <= 0) {\n throw new OpenXmlSchemaError(`Font size must be positive; got ${opts.size}`);\n }\n out.size = opts.size;\n }\n if (opts.color !== undefined) {\n out.color = Object.isFrozen(opts.color) ? opts.color : makeColor(opts.color);\n }\n if (opts.bold !== undefined) out.bold = opts.bold;\n if (opts.italic !== undefined) out.italic = opts.italic;\n if (opts.strike !== undefined) out.strike = opts.strike;\n if (opts.outline !== undefined) out.outline = opts.outline;\n if (opts.shadow !== undefined) out.shadow = opts.shadow;\n if (opts.condense !== undefined) out.condense = opts.condense;\n if (opts.extend !== undefined) out.extend = opts.extend;\n if (opts.underline !== undefined) {\n if (!UNDERLINE_SET.has(opts.underline)) {\n throw new OpenXmlSchemaError(\n `Font underline must be one of [${UNDERLINE_STYLES.join(', ')}]; got \"${opts.underline}\"`,\n );\n }\n out.underline = opts.underline;\n }\n if (opts.vertAlign !== undefined) {\n if (!VERT_SET.has(opts.vertAlign)) {\n throw new OpenXmlSchemaError(\n `Font vertAlign must be one of [${VERT_ALIGNS.join(', ')}]; got \"${opts.vertAlign}\"`,\n );\n }\n out.vertAlign = opts.vertAlign;\n }\n if (opts.scheme !== undefined) {\n if (!SCHEME_SET.has(opts.scheme)) {\n throw new OpenXmlSchemaError(`Font scheme must be one of [${FONT_SCHEMES.join(', ')}]; got \"${opts.scheme}\"`);\n }\n out.scheme = opts.scheme;\n }\n return Object.freeze(out);\n}\n\n/** Excel's default cell font: Calibri 11, minor scheme, theme=1 colour. */\nexport const DEFAULT_FONT: Font = makeFont({\n name: 'Calibri',\n size: 11,\n family: 2,\n scheme: 'minor',\n color: makeColor({ theme: 1 }),\n});\n\n/**\n * Translate a {@link Font} to a CSS-property record suitable for HTML\n * rendering / preview. Boolean toggles map to weight/style/decoration,\n * `size` becomes `font-size: <n>pt`, and an explicit rgb/indexed\n * `color` is rendered as `#RRGGBB` (alpha dropped). theme/auto colours\n * are skipped — callers without a theme can't resolve them.\n *\n * `vertAlign='superscript'|'subscript'` lowers `font-size` to 0.83em\n * (W3C convention) and sets `vertical-align`.\n *\n * Returns `{}` for an empty Font so callers can spread it without\n * overwriting upstream defaults.\n */\nexport function fontToCss(font: Font | undefined): Record<string, string> {\n const css: Record<string, string> = {};\n if (!font) return css;\n if (font.name !== undefined) {\n // CSS expects quoted family names that contain spaces; quote unconditionally\n // to keep the generator simple.\n css['font-family'] = `'${font.name.replace(/'/g, \"\\\\'\")}'`;\n }\n if (font.size !== undefined) css['font-size'] = `${font.size}pt`;\n if (font.bold) css['font-weight'] = 'bold';\n if (font.italic) css['font-style'] = 'italic';\n const decorations: string[] = [];\n if (font.underline !== undefined) decorations.push('underline');\n if (font.strike) decorations.push('line-through');\n if (decorations.length > 0) css['text-decoration'] = decorations.join(' ');\n if (font.color !== undefined) {\n const argb = colorToHex(font.color);\n if (argb !== undefined) {\n // Drop alpha (CSS hex with alpha is poorly-supported in Excel-style preview).\n css['color'] = `#${argb.slice(2)}`;\n }\n }\n if (font.vertAlign === 'superscript' || font.vertAlign === 'subscript') {\n css['vertical-align'] = font.vertAlign;\n // Match W3C-recommended scaling for sup/sub when font-size is not explicit.\n if (css['font-size'] === undefined) css['font-size'] = '0.83em';\n }\n return css;\n}\n","// Number-format value object + the OOXML built-in format catalogue.\n// Mirrors openpyxl/openpyxl/styles/numbers.py.\n//\n// Excel keeps two parallel namespaces for number formats:\n// * IDs 0–163 are reserved for the built-in catalogue (sparsely\n// populated; only 38 of them are actually defined).\n// * IDs ≥ 164 are user-defined / locale-specific. The Stylesheet\n// pool allocates these on demand (phase 2 §3.4).\n\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\n\n/** Canonical OOXML built-in number formats — verbatim from openpyxl. */\nexport const BUILTIN_FORMATS: Readonly<Record<number, string>> = Object.freeze({\n 0: 'General',\n 1: '0',\n 2: '0.00',\n 3: '#,##0',\n 4: '#,##0.00',\n 5: '\"$\"#,##0_);(\"$\"#,##0)',\n 6: '\"$\"#,##0_);[Red](\"$\"#,##0)',\n 7: '\"$\"#,##0.00_);(\"$\"#,##0.00)',\n 8: '\"$\"#,##0.00_);[Red](\"$\"#,##0.00)',\n 9: '0%',\n 10: '0.00%',\n 11: '0.00E+00',\n 12: '# ?/?',\n 13: '# ??/??',\n 14: 'mm-dd-yy',\n 15: 'd-mmm-yy',\n 16: 'd-mmm',\n 17: 'mmm-yy',\n 18: 'h:mm AM/PM',\n 19: 'h:mm:ss AM/PM',\n 20: 'h:mm',\n 21: 'h:mm:ss',\n 22: 'm/d/yy h:mm',\n 37: '#,##0_);(#,##0)',\n 38: '#,##0_);[Red](#,##0)',\n 39: '#,##0.00_);(#,##0.00)',\n 40: '#,##0.00_);[Red](#,##0.00)',\n 41: '_(* #,##0_);_(* \\\\(#,##0\\\\);_(* \"-\"_);_(@_)',\n 42: '_(\"$\"* #,##0_);_(\"$\"* \\\\(#,##0\\\\);_(\"$\"* \"-\"_);_(@_)',\n 43: '_(* #,##0.00_);_(* \\\\(#,##0.00\\\\);_(* \"-\"??_);_(@_)',\n 44: '_(\"$\"* #,##0.00_)_(\"$\"* \\\\(#,##0.00\\\\)_(\"$\"* \"-\"??_)_(@_)',\n 45: 'mm:ss',\n 46: '[h]:mm:ss',\n 47: 'mmss.0',\n 48: '##0.0E+0',\n 49: '@',\n});\n\n/** First numFmtId Excel reserves for user-defined formats. */\nexport const BUILTIN_FORMATS_MAX_SIZE = 164;\n\nconst REVERSE: ReadonlyMap<string, number> = new Map(Object.entries(BUILTIN_FORMATS).map(([k, v]) => [v, Number(k)]));\n\n// ---- well-known format codes (named exports for ergonomics) ---------------\n//\n// Inlined rather than indexed off BUILTIN_FORMATS to keep TS' tuple\n// lookup from widening to `string | undefined`.\n\nexport const FORMAT_GENERAL = 'General';\nexport const FORMAT_TEXT = '@';\nexport const FORMAT_NUMBER = '0';\nexport const FORMAT_NUMBER_00 = '0.00';\nexport const FORMAT_PERCENTAGE = '0%';\nexport const FORMAT_PERCENTAGE_00 = '0.00%';\nexport const FORMAT_DATE_DATETIME = 'yyyy-mm-dd h:mm:ss';\nexport const FORMAT_DATE_TIMEDELTA = '[hh]:mm:ss';\nexport const FORMAT_DATE_YYYYMMDD2 = 'yyyy-mm-dd';\n\n// ---- helpers ---------------------------------------------------------------\n\n/** Look up the format code for a given numFmtId, or `undefined` if unknown. */\nexport function builtinFormatCode(id: number): string | undefined {\n return Object.hasOwn(BUILTIN_FORMATS, id) ? BUILTIN_FORMATS[id] : undefined;\n}\n\n/** Look up the numFmtId for a given format code, or `undefined` if not built-in. */\nexport function builtinFormatId(code: string): number | undefined {\n return REVERSE.get(code);\n}\n\n/** True iff `code` is one of the OOXML built-in format strings. */\nexport function isBuiltinFormat(code: string): boolean {\n return REVERSE.has(code);\n}\n\n// ---- date / timedelta heuristics ------------------------------------------\n//\n// Mirror openpyxl's regex strategy verbatim. The two patterns work together\n// to decide whether a format implies a date / time / duration interpretation.\n\nconst COLORS_GROUP = '\\\\[(BLACK|BLUE|CYAN|GREEN|MAGENTA|RED|WHITE|YELLOW)\\\\]';\nconst LITERAL_GROUP = '\"[^\"]*\"';\nconst LOCALE_GROUP = '\\\\[(?!hh?\\\\]|mm?\\\\]|ss?\\\\])[^\\\\]]*\\\\]';\n\nconst STRIP_RE = new RegExp(`${COLORS_GROUP}|${LITERAL_GROUP}|${LOCALE_GROUP}`, 'g');\nconst DATE_TOKEN_RE = /(?<![_\\\\])[dmhysDMHYS]/;\nconst TIMEDELTA_RE = /\\[hh?\\](:mm(:ss(\\.0*)?)?)?|\\[mm?\\](:ss(\\.0*)?)?|\\[ss?\\](\\.0*)?/i;\n\n/**\n * Heuristic: does the format string imply a date / time interpretation?\n * Looks at only the first format section (positive-value branch); strips\n * literals, colour codes and locale modifiers before scanning for date\n * tokens (d/m/h/y/s, case-insensitive) that aren't escaped.\n */\nexport function isDateFormat(code: string | undefined | null): boolean {\n if (code == null) return false;\n const head = code.split(';')[0] ?? '';\n const stripped = head.replace(STRIP_RE, '');\n return DATE_TOKEN_RE.test(stripped);\n}\n\n/** Heuristic: does the format string indicate a duration ([h]:mm:ss etc.)? */\nexport function isTimedeltaFormat(code: string | undefined | null): boolean {\n if (code == null) return false;\n const head = code.split(';')[0] ?? '';\n return TIMEDELTA_RE.test(head);\n}\n\n/** Categorise a date format as 'date', 'time', 'datetime' or undefined. */\nexport function classifyDateFormat(code: string | undefined | null): 'date' | 'time' | 'datetime' | undefined {\n if (!isDateFormat(code)) return undefined;\n // Reach here only with a non-null code.\n const head = (code as string).split(';')[0] ?? '';\n // Same locale / literal stripping the date detector applies.\n const stripped = head.replace(STRIP_RE, '');\n let date = false;\n let time = false;\n for (const ch of stripped) {\n if (ch === 'd' || ch === 'D' || ch === 'y' || ch === 'Y') date = true;\n else if (ch === 'h' || ch === 'H' || ch === 's' || ch === 'S') time = true;\n if (date && time) break;\n }\n if (date && time) return 'datetime';\n if (date) return 'date';\n return 'time';\n}\n\n// ---- NumberFormat value -----------------------------------------------------\n\nexport interface NumberFormat {\n /** Stylesheet-relative numFmtId. */\n readonly numFmtId: number;\n readonly formatCode: string;\n}\n\nexport function makeNumberFormat(opts: { numFmtId: number; formatCode: string }): NumberFormat {\n if (!Number.isInteger(opts.numFmtId) || opts.numFmtId < 0) {\n throw new OpenXmlSchemaError(`NumberFormat numFmtId must be a non-negative integer; got ${opts.numFmtId}`);\n }\n if (typeof opts.formatCode !== 'string') {\n throw new OpenXmlSchemaError('NumberFormat formatCode must be a string');\n }\n return Object.freeze({ numFmtId: opts.numFmtId, formatCode: opts.formatCode });\n}\n","// Stylesheet pool. The workbook holds dedup pools per style component (fonts /\n// fills / borders / numFmts / cellXfs / cellStyleXfs); cells reference entries\n// by index via a `styleId` (an index into cellXfs).\n//\n// All adds run through `add*` free functions that look up the pool via\n// `stableStringify`-keyed maps so the same logical Font / Fill / etc. added\n// 1000× lands in a single pool slot.\n\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\nimport { stableStringify } from '../utils/stable-stringify.js';\nimport type { Alignment } from './alignment.js';\nimport type { Border } from './borders.js';\nimport { DEFAULT_BORDER } from './borders.js';\nimport type { Fill } from './fills.js';\nimport { DEFAULT_EMPTY_FILL, DEFAULT_GRAY_FILL } from './fills.js';\nimport type { Font } from './fonts.js';\nimport { DEFAULT_FONT } from './fonts.js';\nimport { BUILTIN_FORMATS_MAX_SIZE, builtinFormatId } from './numbers.js';\nimport type { Protection } from './protection.js';\n\n/**\n * One entry in the cellXfs / cellStyleXfs pool. Represents the union of\n * indexes-into-other-pools that a cell or named style points at.\n *\n * Mirrors openpyxl's CellStyle (styles/cell_style.py). The TS port keeps it as\n * a plain readonly object — no class, no methods. The Stylesheet `addCellXf`\n * allocates the index; cells store only that.\n */\nexport interface CellXf {\n readonly fontId: number;\n readonly fillId: number;\n readonly borderId: number;\n readonly numFmtId: number;\n /** Index into cellStyleXfs — only set for direct-cell xfs that point at a NamedStyle. */\n readonly xfId?: number;\n readonly alignment?: Alignment;\n readonly protection?: Protection;\n readonly applyFont?: boolean;\n readonly applyFill?: boolean;\n readonly applyBorder?: boolean;\n readonly applyNumberFormat?: boolean;\n readonly applyAlignment?: boolean;\n readonly applyProtection?: boolean;\n readonly pivotButton?: boolean;\n readonly quotePrefix?: boolean;\n}\n\nexport interface Stylesheet {\n fonts: Font[];\n fills: Fill[];\n borders: Border[];\n /** numFmtId → format code (custom IDs only; built-ins implicit). */\n numFmts: Map<number, string>;\n cellXfs: CellXf[];\n cellStyleXfs: CellXf[];\n /** Named styles (Excel's \"Cell Styles\" gallery; populated by addNamedStyle). */\n namedStyles?: Array<import('./named-styles.js').StylesheetNamedStyle>;\n /**\n * The tail of `<styleSheet>` this model doesn't cover — `<tableStyles>`,\n * `<colors>` and `<extLst>` (which is where Excel keeps its x14/x15 slicer\n * and timeline styles). Captured verbatim in document order so re-saving a\n * loaded workbook keeps them; CT_Stylesheet puts all three after `<dxfs>`,\n * so the writer appends them there.\n */\n stylesXmlTail?: Array<import('../xml/tree.js').XmlNode>;\n\n // Internal dedup maps. Underscore-prefixed so JSON / structuredClone\n // serialisation can choose to skip them; never part of the public API.\n _fontIdByKey: Map<string, number>;\n _fillIdByKey: Map<string, number>;\n _borderIdByKey: Map<string, number>;\n _xfIdByKey: Map<string, number>;\n _styleXfIdByKey: Map<string, number>;\n _numFmtIdByCode: Map<string, number>;\n _namedStyleByName?: Map<string, import('./named-styles.js').StylesheetNamedStyle>;\n}\n\n/**\n * Build a fresh Stylesheet pre-populated with Excel's required default entries.\n * Mirrors openpyxl's empty Stylesheet: fonts: [DEFAULT_FONT] (index 0) fills:\n * [DEFAULT_EMPTY_FILL,\n * DEFAULT_GRAY_FILL] (indices 0, 1 — required)\n * borders: [DEFAULT_BORDER] (index 0) cellXfs: empty (indices allocated on\n * demand)\n */\nexport function makeStylesheet(): Stylesheet {\n const fontKey = stableStringify(DEFAULT_FONT);\n const fill0Key = stableStringify(DEFAULT_EMPTY_FILL);\n const fill1Key = stableStringify(DEFAULT_GRAY_FILL);\n const borderKey = stableStringify(DEFAULT_BORDER);\n\n return {\n fonts: [DEFAULT_FONT],\n fills: [DEFAULT_EMPTY_FILL, DEFAULT_GRAY_FILL],\n borders: [DEFAULT_BORDER],\n numFmts: new Map(),\n cellXfs: [],\n cellStyleXfs: [],\n _fontIdByKey: new Map([[fontKey, 0]]),\n _fillIdByKey: new Map([\n [fill0Key, 0],\n [fill1Key, 1],\n ]),\n _borderIdByKey: new Map([[borderKey, 0]]),\n _xfIdByKey: new Map(),\n _styleXfIdByKey: new Map(),\n _numFmtIdByCode: new Map(),\n };\n}\n\n// ---- pool add helpers ------------------------------------------------------\n\n/** Add a Font to the pool, returning its 0-based index. Idempotent. */\nexport function addFont(ss: Stylesheet, font: Font): number {\n return addToPool(font, ss.fonts, ss._fontIdByKey);\n}\n\n/** Add a Fill to the pool, returning its 0-based index. Idempotent. */\nexport function addFill(ss: Stylesheet, fill: Fill): number {\n return addToPool(fill, ss.fills, ss._fillIdByKey);\n}\n\n/** Add a Border to the pool, returning its 0-based index. Idempotent. */\nexport function addBorder(ss: Stylesheet, border: Border): number {\n return addToPool(border, ss.borders, ss._borderIdByKey);\n}\n\n/**\n * Resolve a number-format string to its numFmtId.\n * - Built-in codes return their canonical OOXML ID.\n * - Otherwise the custom code is registered (and allocated an ID\n * ≥ {@link BUILTIN_FORMATS_MAX_SIZE}). Idempotent.\n */\nexport function addNumFmt(ss: Stylesheet, formatCode: string): number {\n const builtin = builtinFormatId(formatCode);\n if (builtin !== undefined) return builtin;\n const cached = ss._numFmtIdByCode.get(formatCode);\n if (cached !== undefined) return cached;\n const id = BUILTIN_FORMATS_MAX_SIZE + ss.numFmts.size;\n ss.numFmts.set(id, formatCode);\n ss._numFmtIdByCode.set(formatCode, id);\n return id;\n}\n\n/** Add a CellXf to the cellXfs pool, returning its 0-based index. Idempotent. */\nexport function addCellXf(ss: Stylesheet, xf: CellXf): number {\n validateCellXfRefs(ss, xf, /* isStyle */ false);\n return addToPool(xf, ss.cellXfs, ss._xfIdByKey);\n}\n\n/** Add a CellXf to the cellStyleXfs pool. Idempotent. */\nexport function addCellStyleXf(ss: Stylesheet, xf: CellXf): number {\n validateCellXfRefs(ss, xf, /* isStyle */ true);\n return addToPool(xf, ss.cellStyleXfs, ss._styleXfIdByKey);\n}\n\n// ---- internals -------------------------------------------------------------\n\nconst addToPool = <T>(value: T, pool: T[], byKey: Map<string, number>): number => {\n const key = stableStringify(value);\n const cached = byKey.get(key);\n if (cached !== undefined) return cached;\n const id = pool.length;\n pool.push(value);\n byKey.set(key, id);\n return id;\n};\n\nconst validateCellXfRefs = (ss: Stylesheet, xf: CellXf, isStyle: boolean): void => {\n if (xf.fontId < 0 || xf.fontId >= ss.fonts.length) {\n throw new OpenXmlSchemaError(`CellXf.fontId ${xf.fontId} out of range [0, ${ss.fonts.length})`);\n }\n if (xf.fillId < 0 || xf.fillId >= ss.fills.length) {\n throw new OpenXmlSchemaError(`CellXf.fillId ${xf.fillId} out of range [0, ${ss.fills.length})`);\n }\n if (xf.borderId < 0 || xf.borderId >= ss.borders.length) {\n throw new OpenXmlSchemaError(`CellXf.borderId ${xf.borderId} out of range [0, ${ss.borders.length})`);\n }\n // numFmtId is permissive: built-ins or any registered custom ID.\n if (!Number.isInteger(xf.numFmtId) || xf.numFmtId < 0) {\n throw new OpenXmlSchemaError(`CellXf.numFmtId must be a non-negative integer; got ${xf.numFmtId}`);\n }\n if (!isStyle && xf.xfId !== undefined) {\n if (xf.xfId < 0 || xf.xfId >= ss.cellStyleXfs.length) {\n throw new OpenXmlSchemaError(`CellXf.xfId ${xf.xfId} out of range [0, ${ss.cellStyleXfs.length})`);\n }\n }\n};\n\n/** Returns the currently registered numFmt entries (built-ins are implicit and not included). */\nexport function getCustomNumFmts(ss: Stylesheet): ReadonlyArray<{ id: number; code: string }> {\n const out: Array<{ id: number; code: string }> = [];\n for (const [id, code] of ss.numFmts.entries()) out.push({ id, code });\n // numFmts is keyed by id; sort for deterministic output.\n out.sort((a, b) => a.id - b.id);\n return out;\n}\n\n/** Read-only snapshot of every Font entry in the pool, indexed by id. */\nexport function listFonts(ss: Stylesheet): ReadonlyArray<Font> {\n return ss.fonts;\n}\n\n/** Read-only snapshot of every Fill entry in the pool, indexed by id. */\nexport function listFills(ss: Stylesheet): ReadonlyArray<Fill> {\n return ss.fills;\n}\n\n/** Read-only snapshot of every Border entry in the pool, indexed by id. */\nexport function listBorders(ss: Stylesheet): ReadonlyArray<Border> {\n return ss.borders;\n}\n\n/** Read-only snapshot of every CellXf entry in the cellXfs pool. */\nexport function listCellXfs(ss: Stylesheet): ReadonlyArray<CellXf> {\n return ss.cellXfs;\n}\n\n/** Read-only snapshot of every CellStyleXf entry (named-style xfs). */\nexport function listCellStyleXfs(ss: Stylesheet): ReadonlyArray<CellXf> {\n return ss.cellStyleXfs;\n}\n\n/**\n * Convenience: build the default `cellXfs[0]` Excel emits — points at the\n * workbook's font 0 / fill 0 / border 0 / numFmtId 0 (General).\n */\nexport function defaultCellXf(): CellXf {\n return Object.freeze({ fontId: 0, fillId: 0, borderId: 0, numFmtId: 0 });\n}\n","// NamedStyle value object + curated built-in catalogue. Mirrors\n// openpyxl/openpyxl/styles/named_styles.py + styles/builtins.py.\n//\n// A `NamedStyle` is a label that bundles Font + Fill + Border +\n// Alignment + Protection + number-format string. Cells reference it\n// via the cellStyleXfs pool. This module:\n// 1. defines the value type;\n// 2. wires `addNamedStyle` (registers sub-objects in the Stylesheet,\n// allocates a cellStyleXf, then attaches the name);\n// 3. ships a curated subset of Excel's \"Cell Styles\" gallery as\n// plain-object specs and an `ensureBuiltinStyle` ergonomics\n// function that registers one on demand.\n//\n// The Accent1..6 with 20/40/60% variants and `pandas_highlight` are\n// deferred — they balloon the bundle without earning their keep on\n// the read/write hot path.\n\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\nimport type { Alignment } from './alignment.js';\nimport type { Border } from './borders.js';\nimport { makeColor } from './colors.js';\nimport type { Fill } from './fills.js';\nimport { makePatternFill } from './fills.js';\nimport type { Font } from './fonts.js';\nimport { makeFont } from './fonts.js';\nimport { addBorder, addCellStyleXf, addFill, addFont, addNumFmt, type CellXf, type Stylesheet } from './stylesheet.js';\n\nexport interface NamedStyle {\n readonly name: string;\n /** OOXML built-in id (0..N); absent for user-defined styles. */\n readonly builtinId?: number;\n readonly customBuiltin?: boolean;\n readonly hidden?: boolean;\n readonly iLevel?: number;\n readonly font?: Font;\n readonly fill?: Fill;\n readonly border?: Border;\n readonly alignment?: Alignment;\n readonly protection?: Protection;\n readonly numberFormat?: string;\n}\n\nimport type { Protection } from './protection.js';\n\n/**\n * NamedStyle as it sits inside the Stylesheet (an `<cellStyles>` entry).\n * Captures the resolved cellStyleXfs index so the writer can emit the\n * `<cellStyle xfId=\"…\" name=\"…\" builtinId=\"…\"/>` element directly.\n */\nexport interface StylesheetNamedStyle {\n readonly name: string;\n readonly xfId: number;\n readonly builtinId?: number;\n readonly customBuiltin?: boolean;\n readonly hidden?: boolean;\n readonly iLevel?: number;\n}\n\n/**\n * Register a NamedStyle on the Stylesheet:\n * 1. add Font / Fill / Border / NumberFormat to their pools\n * 2. push a CellXf with apply* flags into cellStyleXfs\n * 3. append a {name, xfId, builtinId} entry into the workbook's\n * namedStyles list (caller-managed; this function returns the\n * xfId so callers can connect the dots)\n * Idempotent on (name): re-registering by the same name returns the\n * cached xfId.\n */\nexport function addNamedStyle(ss: Stylesheet, style: NamedStyle): number {\n const cached = ss._namedStyleByName?.get(style.name);\n if (cached !== undefined) return cached.xfId;\n\n const fontId = style.font !== undefined ? addFont(ss, style.font) : 0;\n const fillId = style.fill !== undefined ? addFill(ss, style.fill) : 0;\n const borderId = style.border !== undefined ? addBorder(ss, style.border) : 0;\n const numFmtId = style.numberFormat !== undefined ? addNumFmt(ss, style.numberFormat) : 0;\n\n // openpyxl convention: cellStyleXfs entries (the \"base\" XFs of named\n // styles) don't carry apply* flags. The cellXf that bridges a cell to\n // a named style is the one that sets apply* — that's what Excel reads\n // to decide whether to honour the inherited font / fill / border.\n const xf: CellXf = {\n fontId,\n fillId,\n borderId,\n numFmtId,\n ...(style.alignment !== undefined ? { alignment: style.alignment } : {}),\n ...(style.protection !== undefined ? { protection: style.protection } : {}),\n };\n const xfId = addCellStyleXf(ss, xf);\n\n if (ss._namedStyleByName === undefined) ss._namedStyleByName = new Map();\n if (ss.namedStyles === undefined) ss.namedStyles = [];\n const entry: StylesheetNamedStyle = {\n name: style.name,\n xfId,\n ...(style.builtinId !== undefined ? { builtinId: style.builtinId } : {}),\n ...(style.customBuiltin !== undefined ? { customBuiltin: style.customBuiltin } : {}),\n ...(style.hidden !== undefined ? { hidden: style.hidden } : {}),\n ...(style.iLevel !== undefined ? { iLevel: style.iLevel } : {}),\n };\n ss.namedStyles.push(entry);\n ss._namedStyleByName.set(style.name, entry);\n return xfId;\n}\n\n// ---- built-in catalogue ----------------------------------------------------\n//\n// Common ground: the gallery's body fonts are all Calibri 12, family 2,\n// minor scheme, theme-1 colour. Specific styles override font / fill /\n// border / numberFormat as needed.\n\nconst BODY_FONT: Font = makeFont({\n name: 'Calibri',\n family: 2,\n size: 12,\n color: makeColor({ theme: 1 }),\n scheme: 'minor',\n});\n\nconst fillRgb = (rgb: string, fg = true): Fill =>\n makePatternFill(\n fg ? { patternType: 'solid', bgColor: makeColor({ rgb }), fgColor: makeColor({ rgb }) } : { patternType: 'solid' },\n );\n\n/** Curated subset of openpyxl's `styles` dict. Keys match the user-visible names. */\nexport const BUILTIN_NAMED_STYLES: Readonly<Record<string, NamedStyle>> = Object.freeze({\n // -------- core ----------------------------------------------------------\n Normal: { name: 'Normal', builtinId: 0, font: BODY_FONT },\n\n // -------- semantic state ------------------------------------------------\n Good: {\n name: 'Good',\n builtinId: 26,\n font: makeFont({ ...BODY_FONT, color: makeColor({ rgb: 'FF006100' }) }),\n fill: fillRgb('FFC6EFCE'),\n },\n Bad: {\n name: 'Bad',\n builtinId: 27,\n font: makeFont({ ...BODY_FONT, color: makeColor({ rgb: 'FF9C0006' }) }),\n fill: fillRgb('FFFFC7CE'),\n },\n Neutral: {\n name: 'Neutral',\n builtinId: 28,\n font: makeFont({ ...BODY_FONT, color: makeColor({ rgb: 'FF9C5700' }) }),\n fill: fillRgb('FFFFEB9C'),\n },\n\n // -------- data ----------------------------------------------------------\n Calculation: {\n name: 'Calculation',\n builtinId: 22,\n font: makeFont({ ...BODY_FONT, bold: true, color: makeColor({ rgb: 'FFFA7D00' }) }),\n fill: fillRgb('FFF2F2F2'),\n },\n 'Check Cell': {\n name: 'Check Cell',\n builtinId: 23,\n font: makeFont({ ...BODY_FONT, bold: true, color: makeColor({ rgb: 'FFFFFFFF' }) }),\n fill: fillRgb('FFA5A5A5'),\n },\n 'Linked Cell': {\n name: 'Linked Cell',\n builtinId: 24,\n font: makeFont({ ...BODY_FONT, color: makeColor({ rgb: 'FFFA7D00' }) }),\n },\n Note: {\n name: 'Note',\n builtinId: 10,\n font: BODY_FONT,\n fill: fillRgb('FFFFFFC0'),\n },\n 'Warning Text': {\n name: 'Warning Text',\n builtinId: 11,\n font: makeFont({ ...BODY_FONT, color: makeColor({ rgb: 'FFFF0000' }) }),\n },\n Input: {\n name: 'Input',\n builtinId: 20,\n font: makeFont({ ...BODY_FONT, color: makeColor({ rgb: 'FF3F3F76' }) }),\n fill: fillRgb('FFFFCC99'),\n },\n Output: {\n name: 'Output',\n builtinId: 21,\n font: makeFont({ ...BODY_FONT, bold: true, color: makeColor({ rgb: 'FF3F3F3F' }) }),\n fill: fillRgb('FFF2F2F2'),\n },\n 'Explanatory Text': {\n name: 'Explanatory Text',\n builtinId: 53,\n font: makeFont({ ...BODY_FONT, italic: true, color: makeColor({ rgb: 'FF7F7F7F' }) }),\n },\n\n // -------- titles & headings ---------------------------------------------\n Title: {\n name: 'Title',\n builtinId: 15,\n font: makeFont({ name: 'Cambria', family: 2, size: 18, scheme: 'major', color: makeColor({ theme: 3 }) }),\n },\n 'Headline 1': {\n name: 'Headline 1',\n builtinId: 16,\n font: makeFont({ ...BODY_FONT, bold: true, size: 15, color: makeColor({ theme: 3 }) }),\n },\n 'Headline 2': {\n name: 'Headline 2',\n builtinId: 17,\n font: makeFont({ ...BODY_FONT, bold: true, size: 13, color: makeColor({ theme: 3 }) }),\n },\n 'Headline 3': {\n name: 'Headline 3',\n builtinId: 18,\n font: makeFont({ ...BODY_FONT, bold: true, color: makeColor({ theme: 3 }) }),\n },\n 'Headline 4': {\n name: 'Headline 4',\n builtinId: 19,\n font: makeFont({ ...BODY_FONT, bold: true, italic: true, color: makeColor({ theme: 3 }) }),\n },\n Total: {\n name: 'Total',\n builtinId: 25,\n font: makeFont({ ...BODY_FONT, bold: true }),\n },\n\n // -------- numeric -------------------------------------------------------\n Comma: { name: 'Comma', builtinId: 3, font: BODY_FONT, numberFormat: '#,##0.00' },\n 'Comma [0]': { name: 'Comma [0]', builtinId: 6, font: BODY_FONT, numberFormat: '#,##0' },\n Currency: { name: 'Currency', builtinId: 4, font: BODY_FONT, numberFormat: '\"$\"#,##0.00' },\n 'Currency [0]': { name: 'Currency [0]', builtinId: 7, font: BODY_FONT, numberFormat: '\"$\"#,##0' },\n Percent: { name: 'Percent', builtinId: 5, font: BODY_FONT, numberFormat: '0%' },\n\n // -------- hyperlinks ----------------------------------------------------\n Hyperlink: {\n name: 'Hyperlink',\n builtinId: 8,\n font: makeFont({ ...BODY_FONT, underline: 'single', color: makeColor({ theme: 10 }) }),\n },\n 'Followed Hyperlink': {\n name: 'Followed Hyperlink',\n builtinId: 9,\n font: makeFont({ ...BODY_FONT, underline: 'single', color: makeColor({ theme: 11 }) }),\n },\n});\n\n/**\n * Register a built-in style with the supplied Stylesheet (idempotent).\n * Returns the cellStyleXfs index. Throws OpenXmlSchemaError when the\n * name is unknown.\n */\nexport function ensureBuiltinStyle(ss: Stylesheet, name: keyof typeof BUILTIN_NAMED_STYLES | string): number {\n const spec = BUILTIN_NAMED_STYLES[name as keyof typeof BUILTIN_NAMED_STYLES];\n if (spec === undefined) {\n throw new OpenXmlSchemaError(`ensureBuiltinStyle: unknown built-in style \"${String(name)}\"`);\n }\n return addNamedStyle(ss, spec);\n}\n","// Cell protection value object. Mirrors openpyxl/openpyxl/styles/protection.py.\n//\n// Protection only matters when the worksheet itself is protected; until\n// then it's metadata that round-trips with the cell's style.\n\nexport interface Protection {\n /** When true (the default), the cell can't be edited if the sheet is locked. */\n readonly locked?: boolean;\n /** When true, the formula bar hides this cell's contents on a protected sheet. */\n readonly hidden?: boolean;\n}\n\nexport function makeProtection(opts: Partial<Protection> = {}): Protection {\n const out: { -readonly [K in keyof Protection]: Protection[K] } = {};\n if (opts.locked !== undefined) out.locked = opts.locked;\n if (opts.hidden !== undefined) out.hidden = opts.hidden;\n return Object.freeze(out);\n}\n\n/** Excel's Stylesheet always contains an entry equal to {locked:true, hidden:false}. */\nexport const DEFAULT_PROTECTION: Protection = makeProtection({ locked: true, hidden: false });\n","// Cell ↔ Stylesheet bridge.\n//\n// A cell's `styleId` is an index into `Workbook.styles.cellXfs`. Each CellXf\n// points at slots in the font / fill / border / numFmt pools. To \"apply\" a Font\n// to a cell we therefore:\n// 1. read the cell's current CellXf (or `defaultCellXf` if it points\n// at a slot that hasn't been allocated yet — common right after\n// `makeCell` since `cellXfs` starts empty)\n// 2. resolve / register the new component in its pool\n// 3. build a new CellXf that carries the new id + the matching\n// `apply*` flag (Excel needs the flag to honour the override over\n// the underlying NamedStyle)\n// 4. dedup that CellXf via `addCellXf` and write the returned index\n// back to `c.styleId`\n//\n// Following the no-classes rule, this module is just a flat list of free\n// functions; the workbook is passed in so callers don't need to thread the\n// stylesheet manually.\n\nimport type { Cell } from '../cell/cell.js';\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\nimport type { Workbook } from '../workbook/workbook.js';\nimport { parseRange } from '../worksheet/cell-range.js';\nimport { setCell, type Worksheet } from '../worksheet/worksheet.js';\nimport type { Alignment, HorizontalAlignment, VerticalAlignment } from './alignment.js';\nimport { alignmentToCss, makeAlignment } from './alignment.js';\nimport type { Border, SideStyle } from './borders.js';\nimport { borderToCss, DEFAULT_BORDER, makeBorder, makeSide } from './borders.js';\nimport type { Color } from './colors.js';\nimport { makeColor } from './colors.js';\nimport type { Fill } from './fills.js';\nimport { DEFAULT_EMPTY_FILL, fillToCss, makePatternFill } from './fills.js';\nimport type { Font, UnderlineStyle } from './fonts.js';\nimport { DEFAULT_FONT, fontToCss, makeFont } from './fonts.js';\nimport { ensureBuiltinStyle } from './named-styles.js';\nimport { builtinFormatCode } from './numbers.js';\nimport type { Protection } from './protection.js';\nimport { DEFAULT_PROTECTION } from './protection.js';\nimport {\n addBorder,\n addCellXf,\n addFill,\n addFont,\n addNumFmt,\n type CellXf,\n defaultCellXf,\n type Stylesheet,\n} from './stylesheet.js';\n\n/** Default General number format code (numFmtId 0). */\nconst GENERAL_FORMAT_CODE = 'General';\n\n/** Resolve a cell's current CellXf, falling back to defaults when unset. */\nfunction currentXf(ss: Stylesheet, c: Cell): CellXf {\n return ss.cellXfs[c.styleId] ?? defaultCellXf();\n}\n\n// ---- read accessors --------------------------------------------------------\n\nexport function getCellFont(wb: Workbook, c: Cell): Font {\n const xf = currentXf(wb.styles, c);\n return wb.styles.fonts[xf.fontId] ?? DEFAULT_FONT;\n}\n\nexport function getCellFill(wb: Workbook, c: Cell): Fill {\n const xf = currentXf(wb.styles, c);\n return wb.styles.fills[xf.fillId] ?? DEFAULT_EMPTY_FILL;\n}\n\nexport function getCellBorder(wb: Workbook, c: Cell): Border {\n const xf = currentXf(wb.styles, c);\n return wb.styles.borders[xf.borderId] ?? DEFAULT_BORDER;\n}\n\nexport function getCellAlignment(wb: Workbook, c: Cell): Alignment {\n return currentXf(wb.styles, c).alignment ?? {};\n}\n\nexport function getCellProtection(wb: Workbook, c: Cell): Protection {\n return currentXf(wb.styles, c).protection ?? DEFAULT_PROTECTION;\n}\n\n/**\n * Returns the cell's number-format **code** (e.g. `\"0.00\"`, `\"General\"`).\n * Built-in IDs resolve through `builtinFormatCode`; custom IDs come from the\n * workbook's numFmts map.\n */\nexport function getCellNumberFormat(wb: Workbook, c: Cell): string {\n const id = currentXf(wb.styles, c).numFmtId;\n const builtin = builtinFormatCode(id);\n if (builtin !== undefined) return builtin;\n return wb.styles.numFmts.get(id) ?? GENERAL_FORMAT_CODE;\n}\n\n/**\n * Aggregate `fontToCss` + `fillToCss` + `borderToCss` + `alignmentToCss` for a\n * cell into a single CSS-property record. Resolves the cell's `styleId` against\n * the workbook stylesheet, then merges the four partials. On key collision the\n * priority is alignment > border > fill > font (alignment is most specific,\n * font is the broad default). A fully-default cell (`styleId === 0` with empty\n * pools) returns `{}`.\n */\nexport function cellStyleToCss(wb: Workbook, c: Cell): Record<string, string> {\n // Don't pay the resolve cost when the cell points at the default xf and the\n // stylesheet is still in its initial state.\n if (c.styleId === 0) {\n const xf = wb.styles.cellXfs[0];\n if (!xf || (xf.fontId === 0 && xf.fillId === 0 && xf.borderId === 0 && xf.alignment === undefined)) {\n return {};\n }\n }\n const font = getCellFont(wb, c);\n const fill = getCellFill(wb, c);\n const border = getCellBorder(wb, c);\n const alignment = getCellAlignment(wb, c);\n return {\n ...fontToCss(font),\n ...fillToCss(fill),\n ...borderToCss(border),\n ...alignmentToCss(alignment),\n };\n}\n\n// ---- write accessors -------------------------------------------------------\n\n/**\n * Reserve cellXfs[0] for the implicit default xf when the pool is empty.\n * Excel's `<c>` elements without an `s=` attribute resolve to `cellXfs[0]`, so\n * the first time a caller styles any cell we need to make sure that slot stays\n * the default — otherwise unstyled cells in the same sheet would inherit the\n * freshly added styled xf.\n *\n * Idempotent: calling this on a non-empty pool is a no-op.\n */\nconst reserveDefaultXfSlot = (wb: Workbook): void => {\n if (wb.styles.cellXfs.length === 0) addCellXf(wb.styles, defaultCellXf());\n};\n\n/**\n * Replace one field on the cell's CellXf. Centralises the dedup + styleId\n * update so each `setCell*` is a single dispatch.\n */\nfunction applyXfPatch(wb: Workbook, c: Cell, patch: Partial<CellXf>): void {\n reserveDefaultXfSlot(wb);\n const next: CellXf = { ...currentXf(wb.styles, c), ...patch };\n c.styleId = addCellXf(wb.styles, next);\n}\n\nexport function setCellFont(wb: Workbook, c: Cell, font: Font): void {\n const fontId = addFont(wb.styles, font);\n applyXfPatch(wb, c, { fontId, applyFont: true });\n}\n\nexport function setCellFill(wb: Workbook, c: Cell, fill: Fill): void {\n const fillId = addFill(wb.styles, fill);\n applyXfPatch(wb, c, { fillId, applyFill: true });\n}\n\nexport function setCellBorder(wb: Workbook, c: Cell, border: Border): void {\n const borderId = addBorder(wb.styles, border);\n applyXfPatch(wb, c, { borderId, applyBorder: true });\n}\n\nexport function setCellAlignment(wb: Workbook, c: Cell, alignment: Alignment): void {\n applyXfPatch(wb, c, { alignment, applyAlignment: true });\n}\n\nexport function setCellProtection(wb: Workbook, c: Cell, protection: Protection): void {\n applyXfPatch(wb, c, { protection, applyProtection: true });\n}\n\n/**\n * Set the cell's number format by its **code** string. Built-in codes resolve\n * to their canonical id; custom codes are registered via `addNumFmt`.\n */\nexport function setCellNumberFormat(wb: Workbook, c: Cell, formatCode: string): void {\n const numFmtId = addNumFmt(wb.styles, formatCode);\n applyXfPatch(wb, c, { numFmtId, applyNumberFormat: true });\n}\n\n/**\n * Copy the source cell's `styleId` to the target cell. Both cells share the\n * same workbook stylesheet, so the styled appearance carries over without\n * allocating a new xf entry. Pass cells from different workbooks via {@link\n * cloneCellStyle} if you need a deep copy across workbooks.\n */\nexport function copyCellStyle(_wb: Workbook, source: Cell, target: Cell): void {\n target.styleId = source.styleId;\n}\n\n/**\n * Reset a cell back to the default (unstyled) appearance — equivalent to\n * Excel's \"Clear Formatting\" command. After the call, the cell inherits the\n * workbook's default font / fill / border / alignment / protection /\n * numberFormat. The underlying xf pool is **not** shrunk (Excel doesn't bother\n * either; the orphaned xf is harmless).\n */\nexport function clearCellStyle(_wb: Workbook, c: Cell): void {\n c.styleId = 0;\n}\n\n/**\n * Range-level shortcut for {@link clearCellStyle}. Walks every cell actually\n * present in the range and resets its `styleId` to 0; cells that don't exist\n * yet are **not** materialised (no-op for sparse regions, unlike the styled\n * `setRange*` family which has to create cells to make the patch observable).\n */\nexport function clearRangeStyle(wb: Workbook, ws: Worksheet, range: string): void {\n const { minRow, maxRow, minCol, maxCol } = parseRange(range);\n for (let r = minRow; r <= maxRow; r++) {\n const row = ws.rows.get(r);\n if (!row) continue;\n for (let c = minCol; c <= maxCol; c++) {\n const cell = row.get(c);\n if (cell) clearCellStyle(wb, cell);\n }\n }\n}\n\n/**\n * Deep-copy the source cell's full xf (font / fill / border / alignment /\n * protection / numberFormat) into a possibly-different workbook. Returns the\n * new styleId in the target workbook.\n */\nexport function cloneCellStyle(\n sourceWb: Workbook,\n source: Cell,\n targetWb: Workbook,\n target: Cell,\n): number {\n reserveDefaultXfSlot(targetWb);\n const srcXf = currentXf(sourceWb.styles, source);\n const srcFont = sourceWb.styles.fonts[srcXf.fontId] ?? DEFAULT_FONT;\n const srcFill = sourceWb.styles.fills[srcXf.fillId] ?? DEFAULT_EMPTY_FILL;\n const srcBorder = sourceWb.styles.borders[srcXf.borderId] ?? DEFAULT_BORDER;\n const srcNumFmt = builtinFormatCode(srcXf.numFmtId)\n ?? sourceWb.styles.numFmts.get(srcXf.numFmtId)\n ?? GENERAL_FORMAT_CODE;\n const fontId = addFont(targetWb.styles, srcFont);\n const fillId = addFill(targetWb.styles, srcFill);\n const borderId = addBorder(targetWb.styles, srcBorder);\n const numFmtId = addNumFmt(targetWb.styles, srcNumFmt);\n const next: { -readonly [K in keyof CellXf]?: CellXf[K] } = {\n fontId,\n fillId,\n borderId,\n numFmtId,\n };\n if (srcXf.applyFont) next.applyFont = true;\n if (srcXf.applyFill) next.applyFill = true;\n if (srcXf.applyBorder) next.applyBorder = true;\n if (srcXf.applyNumberFormat) next.applyNumberFormat = true;\n if (srcXf.alignment !== undefined) {\n next.alignment = srcXf.alignment;\n next.applyAlignment = true;\n }\n if (srcXf.protection !== undefined) {\n next.protection = srcXf.protection;\n next.applyProtection = true;\n }\n target.styleId = addCellXf(targetWb.styles, next as CellXf);\n return target.styleId;\n}\n\n/**\n * Build a single CellXf id from a multi-axis style spec, then apply it to every\n * cell in `range`. The xf is registered once per style shape, so a 1000-cell\n * range allocates one xf — much faster than looping `setCellStyle` per cell.\n */\nexport function setRangeStyle(\n wb: Workbook,\n ws: Worksheet,\n range: string,\n opts: {\n font?: Font;\n fill?: Fill;\n border?: Border;\n alignment?: Alignment;\n protection?: Protection;\n numberFormat?: string;\n },\n): void {\n const patch: { -readonly [K in keyof CellXf]?: CellXf[K] } = {};\n if (opts.font !== undefined) {\n patch.fontId = addFont(wb.styles, opts.font);\n patch.applyFont = true;\n }\n if (opts.fill !== undefined) {\n patch.fillId = addFill(wb.styles, opts.fill);\n patch.applyFill = true;\n }\n if (opts.border !== undefined) {\n patch.borderId = addBorder(wb.styles, opts.border);\n patch.applyBorder = true;\n }\n if (opts.alignment !== undefined) {\n patch.alignment = opts.alignment;\n patch.applyAlignment = true;\n }\n if (opts.protection !== undefined) {\n patch.protection = opts.protection;\n patch.applyProtection = true;\n }\n if (opts.numberFormat !== undefined) {\n patch.numFmtId = addNumFmt(wb.styles, opts.numberFormat);\n patch.applyNumberFormat = true;\n }\n if (Object.keys(patch).length === 0) return;\n reserveDefaultXfSlot(wb);\n\n const { minRow, maxRow, minCol, maxCol } = parseRange(range);\n // Pre-register the xf for each existing cell — Excel dedupes by value, so the\n // inner xf-pool ends up the same shape for cells already carrying part of the\n // patch as for blanks.\n for (let r = minRow; r <= maxRow; r++) {\n for (let c = minCol; c <= maxCol; c++) {\n let cell = ws.rows.get(r)?.get(c);\n if (!cell) cell = setCell(ws, r, c);\n const next: CellXf = { ...currentXf(wb.styles, cell), ...patch };\n cell.styleId = addCellXf(wb.styles, next);\n }\n }\n}\n\n/**\n * Combined cell-style setter. Each axis is independent — pass any subset and\n * the corresponding `applyXxx` flags get set on the underlying CellXf. Avoids\n * 5+ separate stylesheet round-trips when a caller wants to style a single cell\n * across multiple axes (Excel dedupes the resulting xf record on every call).\n */\nexport function setCellStyle(\n wb: Workbook,\n c: Cell,\n opts: {\n font?: Font;\n fill?: Fill;\n border?: Border;\n alignment?: Alignment;\n protection?: Protection;\n numberFormat?: string;\n },\n): void {\n const patch: { -readonly [K in keyof CellXf]?: CellXf[K] } = {};\n if (opts.font !== undefined) {\n patch.fontId = addFont(wb.styles, opts.font);\n patch.applyFont = true;\n }\n if (opts.fill !== undefined) {\n patch.fillId = addFill(wb.styles, opts.fill);\n patch.applyFill = true;\n }\n if (opts.border !== undefined) {\n patch.borderId = addBorder(wb.styles, opts.border);\n patch.applyBorder = true;\n }\n if (opts.alignment !== undefined) {\n patch.alignment = opts.alignment;\n patch.applyAlignment = true;\n }\n if (opts.protection !== undefined) {\n patch.protection = opts.protection;\n patch.applyProtection = true;\n }\n if (opts.numberFormat !== undefined) {\n patch.numFmtId = addNumFmt(wb.styles, opts.numberFormat);\n patch.applyNumberFormat = true;\n }\n if (Object.keys(patch).length === 0) return;\n applyXfPatch(wb, c, patch as Partial<CellXf>);\n}\n\n// ---- fill presets -------------------------------------------------------\n\n/**\n * Set the cell's background to a solid color. Accepts a hex string\n * (`'FFAAFFAA'`) or a partial `Color` object (`{ theme: 4, tint: 0.4 }`).\n * Equivalent to `setCellFill(wb, c, makePatternFill({ patternType: 'solid',\n * fgColor: makeColor(...) }))`.\n */\nexport function setCellBackgroundColor(wb: Workbook, c: Cell, color: string | Partial<Color>): void {\n const colorObj = typeof color === 'string' ? makeColor({ rgb: color }) : makeColor(color);\n setCellFill(wb, c, makePatternFill({ patternType: 'solid', fgColor: colorObj }));\n}\n\n/** Strip the cell's background fill, returning it to the default. */\nexport function clearCellBackground(wb: Workbook, c: Cell): void {\n setCellFill(wb, c, DEFAULT_EMPTY_FILL);\n}\n\n/**\n * Range-level shortcut for `setCellBackgroundColor`. Each cell in the range\n * gets the same solid pattern fill via `setRangeStyle`, so the fill pool dedups\n * to a single entry across the whole range.\n */\nexport function setRangeBackgroundColor(\n wb: Workbook,\n ws: Worksheet,\n range: string,\n color: string | Partial<Color>,\n): void {\n const colorObj = typeof color === 'string' ? makeColor({ rgb: color }) : makeColor(color);\n setRangeStyle(wb, ws, range, {\n fill: makePatternFill({ patternType: 'solid', fgColor: colorObj }),\n });\n}\n\n/** Range-level shortcut for `setCellFont` (full Font replacement). */\nexport function setRangeFont(\n wb: Workbook,\n ws: Worksheet,\n range: string,\n font: Font,\n): void {\n setRangeStyle(wb, ws, range, { font });\n}\n\n/**\n * Range-level shortcut for `setCellNumberFormat`. Stamps the same format-code\n * onto every cell in the range; the numFmt pool dedups the code so callers\n * don't pay per-cell pool churn.\n */\nexport function setRangeNumberFormat(\n wb: Workbook,\n ws: Worksheet,\n range: string,\n formatCode: string,\n): void {\n setRangeStyle(wb, ws, range, { numberFormat: formatCode });\n}\n\n/**\n * Range-level shortcut for `setCellProtection`. Stamps the same Protection\n * (locked / hidden) onto every cell in the range. Pass a full `Protection`\n * value or a partial — partials default missing fields to `false` per Excel's\n * `<protection>` semantics.\n *\n * Common usage: `setRangeProtection(wb, ws, 'B2:B100', { locked: false })` to\n * leave just an input column editable when the sheet is protected.\n */\nexport function setRangeProtection(\n wb: Workbook,\n ws: Worksheet,\n range: string,\n protection: Protection | Partial<Protection>,\n): void {\n // Funnel partials through the Protection factory so frozen invariant holds.\n const p: Protection = Object.isFrozen(protection)\n ? (protection as Protection)\n : { locked: protection.locked ?? false, hidden: protection.hidden ?? false };\n setRangeStyle(wb, ws, range, { protection: p });\n}\n\n/**\n * Range-level shortcut for `wrapCellText`. Toggles \"Wrap Text\" on every cell in\n * the range while preserving each cell's existing alignment (horizontal /\n * vertical / textRotation / indent are not touched). Empty cells in the range\n * are materialised so the alignment patch is observable on round-trip.\n */\nexport function setRangeWrapText(wb: Workbook, ws: Worksheet, range: string, on = true): void {\n reserveDefaultXfSlot(wb);\n const { minRow, maxRow, minCol, maxCol } = parseRange(range);\n for (let r = minRow; r <= maxRow; r++) {\n for (let c = minCol; c <= maxCol; c++) {\n let cell = ws.rows.get(r)?.get(c);\n if (!cell) cell = setCell(ws, r, c);\n wrapCellText(wb, cell, on);\n }\n }\n}\n\n/**\n * Range-level Alignment setter. Two modes:\n *\n * - `mode: 'merge'` (default) — each cell's existing alignment is\n * preserved; the supplied partial overlays it. Use this when you\n * want to set just `horizontal` or `vertical` without wiping the\n * other axes.\n * - `mode: 'replace'` — each cell's alignment is **wholly replaced**\n * by the supplied value. Indent / textRotation / wrapText that\n * weren't supplied are dropped.\n *\n * Empty cells in the range are materialised so the patch is observable on\n * round-trip.\n */\nexport function setRangeAlignment(\n wb: Workbook,\n ws: Worksheet,\n range: string,\n alignment: Partial<Alignment>,\n mode: 'merge' | 'replace' = 'merge',\n): void {\n reserveDefaultXfSlot(wb);\n const { minRow, maxRow, minCol, maxCol } = parseRange(range);\n if (mode === 'replace') {\n setRangeStyle(wb, ws, range, { alignment: makeAlignment(alignment) });\n return;\n }\n for (let r = minRow; r <= maxRow; r++) {\n for (let c = minCol; c <= maxCol; c++) {\n let cell = ws.rows.get(r)?.get(c);\n if (!cell) cell = setCell(ws, r, c);\n const cur = currentXf(wb.styles, cell).alignment;\n setCellAlignment(wb, cell, mergeAlignment(cur, alignment));\n }\n }\n}\n\n// ---- font presets -------------------------------------------------------\n\nconst mergeFont = (current: Font, patch: Partial<Font>): Font => makeFont({ ...current, ...patch });\n\n/** Toggle bold on a cell. Preserves other font fields. */\nexport function setBold(wb: Workbook, c: Cell, on = true): void {\n setCellFont(wb, c, mergeFont(getCellFont(wb, c), { bold: on }));\n}\n\n/** Toggle italic on a cell. */\nexport function setItalic(wb: Workbook, c: Cell, on = true): void {\n setCellFont(wb, c, mergeFont(getCellFont(wb, c), { italic: on }));\n}\n\n/** Toggle strike-through on a cell. */\nexport function setStrikethrough(wb: Workbook, c: Cell, on = true): void {\n setCellFont(wb, c, mergeFont(getCellFont(wb, c), { strike: on }));\n}\n\n/**\n * Set the underline style. Pass `false` to drop underline; pass `'single' |\n * 'double' | 'singleAccounting' | 'doubleAccounting'` to apply that style; pass\n * `true` for the most common single-line.\n */\nexport function setUnderline(\n wb: Workbook,\n c: Cell,\n style: UnderlineStyle | boolean = 'single',\n): void {\n const cur = getCellFont(wb, c);\n // Strip the existing underline by spreading then overwriting; makeFont\n // ignores `underline: undefined` so passing nothing for the off-case drops it\n // entirely.\n const { underline: _drop, ...rest } = cur;\n if (style === false) {\n setCellFont(wb, c, makeFont(rest));\n return;\n }\n const u = style === true ? 'single' : style;\n setCellFont(wb, c, makeFont({ ...rest, underline: u }));\n}\n\n/** Set the font size in points (e.g. 14). Preserves other fields. */\nexport function setFontSize(wb: Workbook, c: Cell, size: number): void {\n setCellFont(wb, c, mergeFont(getCellFont(wb, c), { size }));\n}\n\n/** Set the font family name (e.g. \"Arial\"). Preserves other fields. */\nexport function setFontName(wb: Workbook, c: Cell, name: string): void {\n setCellFont(wb, c, mergeFont(getCellFont(wb, c), { name }));\n}\n\n/**\n * Set the font color. Accepts a hex string (\"FFAA0033\") or a partial `Color`\n * object (`{ theme: 4, tint: 0.4 }`). Preserves other font fields.\n */\nexport function setFontColor(wb: Workbook, c: Cell, color: string | Partial<Color>): void {\n const colorObj = typeof color === 'string' ? makeColor({ rgb: color }) : makeColor(color);\n setCellFont(wb, c, mergeFont(getCellFont(wb, c), { color: colorObj }));\n}\n\n// ---- alignment presets --------------------------------------------------\n\nconst mergeAlignment = (current: Alignment | undefined, patch: Partial<Alignment>): Alignment => {\n return makeAlignment({ ...current, ...patch });\n};\n\n/**\n * Center a cell horizontally + vertically. Mirrors Excel's \"Merge & Center\" UI\n * button (without the merge — see {@link mergeCells} for that). Preserves any\n * other alignment fields already present.\n */\nexport function centerCell(wb: Workbook, c: Cell): void {\n const cur = currentXf(wb.styles, c).alignment;\n setCellAlignment(wb, c, mergeAlignment(cur, { horizontal: 'center', vertical: 'center' }));\n}\n\n/** Toggle \"Wrap Text\" on a cell, preserving other alignment fields. */\nexport function wrapCellText(wb: Workbook, c: Cell, wrap = true): void {\n const cur = currentXf(wb.styles, c).alignment;\n setCellAlignment(wb, c, mergeAlignment(cur, { wrapText: wrap }));\n}\n\n/** Set the horizontal alignment in isolation. */\nexport function alignCellHorizontal(wb: Workbook, c: Cell, horizontal: HorizontalAlignment): void {\n const cur = currentXf(wb.styles, c).alignment;\n setCellAlignment(wb, c, mergeAlignment(cur, { horizontal }));\n}\n\n/** Set the vertical alignment in isolation. */\nexport function alignCellVertical(wb: Workbook, c: Cell, vertical: VerticalAlignment): void {\n const cur = currentXf(wb.styles, c).alignment;\n setCellAlignment(wb, c, mergeAlignment(cur, { vertical }));\n}\n\n/**\n * Rotate the cell's text. `degrees` accepts 0..180 (clockwise) or 255 for\n * Excel's \"vertical stacked\" mode. Mirrors the rotate icons in the alignment\n * ribbon.\n */\nexport function rotateCellText(wb: Workbook, c: Cell, degrees: number): void {\n const cur = currentXf(wb.styles, c).alignment;\n setCellAlignment(wb, c, mergeAlignment(cur, { textRotation: degrees }));\n}\n\n/** Set or clear the indent level (0..255). */\nexport function indentCell(wb: Workbook, c: Cell, levels: number): void {\n const cur = currentXf(wb.styles, c).alignment;\n setCellAlignment(wb, c, mergeAlignment(cur, { indent: levels }));\n}\n\n// ---- format presets -----------------------------------------------------\n//\n// Mirror Excel's \"Format Cells → Number → Category\" panel. Each preset builds\n// the exact format-code Excel ships with, then routes through the existing\n// `setCellNumberFormat` so the dedup pool sees the same string every time.\n\n/**\n * Format a cell as currency. Produces one of:\n * - default → `\"$#,##0.00\"` (US dollar, 2 decimals)\n * - `{ symbol: \"€\" }` → `\"€#,##0.00\"`\n * - `{ symbol: \"¥\", decimals: 0 }` → `\"¥#,##0\"`\n * - `{ accounting: true }` → `\"_-$* #,##0.00_-;-$* #,##0.00_-;_-$* \\\"-\\\"??_-;_-@_-\"`\n * (Excel's \"Accounting\" subtype with right-aligned symbol).\n */\nexport function setCellAsCurrency(\n wb: Workbook,\n c: Cell,\n opts: { symbol?: string; decimals?: number; accounting?: boolean } = {},\n): void {\n const symbol = opts.symbol ?? '$';\n const decimals = opts.decimals ?? 2;\n const decTail = decimals > 0 ? `.${'0'.repeat(decimals)}` : '';\n const code = opts.accounting\n ? `_-${symbol}* #,##0${decTail}_-;-${symbol}* #,##0${decTail}_-;_-${symbol}* \"-\"${'?'.repeat(decimals)}_-;_-@_-`\n : `${symbol}#,##0${decTail}`;\n setCellNumberFormat(wb, c, code);\n}\n\n/**\n * Format a cell as a percentage. `decimals` defaults to 0 → `\"0%\"`; `decimals:\n * 2` → `\"0.00%\"`. The cell value is multiplied by 100 by Excel during display.\n */\nexport function setCellAsPercent(wb: Workbook, c: Cell, decimals = 0): void {\n if (!Number.isInteger(decimals) || decimals < 0) {\n throw new OpenXmlSchemaError(`setCellAsPercent: decimals must be a non-negative integer; got ${decimals}`);\n }\n const code = decimals === 0 ? '0%' : `0.${'0'.repeat(decimals)}%`;\n setCellNumberFormat(wb, c, code);\n}\n\n/**\n * Format a cell as a date. `format` defaults to Excel's default\n * locale-independent ISO-style date `\"yyyy-mm-dd\"`. Common alternatives:\n * `\"m/d/yyyy\"`, `\"dd-mmm-yy\"`, `\"yyyy-mm-dd hh:mm:ss\"`.\n */\nexport function setCellAsDate(wb: Workbook, c: Cell, format = 'yyyy-mm-dd'): void {\n setCellNumberFormat(wb, c, format);\n}\n\n/**\n * Format a cell as a thousands-separated number. `decimals` defaults to 0 →\n * `\"#,##0\"`; `decimals: 2` → `\"#,##0.00\"`.\n */\nexport function setCellAsNumber(wb: Workbook, c: Cell, decimals = 0): void {\n if (!Number.isInteger(decimals) || decimals < 0) {\n throw new OpenXmlSchemaError(`setCellAsNumber: decimals must be a non-negative integer; got ${decimals}`);\n }\n const code = decimals === 0 ? '#,##0' : `#,##0.${'0'.repeat(decimals)}`;\n setCellNumberFormat(wb, c, code);\n}\n\n// ---- table-header preset ------------------------------------------------\n\n/**\n * Apply Excel's stock \"table header\" formatting to a range: bold white text on\n * a dark fill, plus a thick bottom border. Override any axis via `opts` — pass\n * `bold: false` to drop the bold, or `fillColor: 'FF305496'` for a different\n * shade. Defaults match Excel's \"Table Style Medium 2\" header row.\n */\nexport function formatAsHeader(\n wb: Workbook,\n ws: Worksheet,\n range: string,\n opts: {\n fillColor?: string | Partial<Color>;\n fontColor?: string | Partial<Color>;\n bold?: boolean;\n bottomBorder?: SideStyle | false;\n bottomBorderColor?: string | Partial<Color>;\n } = {},\n): void {\n const fillColor = opts.fillColor === undefined ? 'FF305496' : typeof opts.fillColor === 'string' ? makeColor({ rgb: opts.fillColor }) : makeColor(opts.fillColor);\n const fontColor = opts.fontColor === undefined ? 'FFFFFFFF' : typeof opts.fontColor === 'string' ? makeColor({ rgb: opts.fontColor }) : makeColor(opts.fontColor);\n const bold = opts.bold ?? true;\n const borderStyle = opts.bottomBorder ?? 'medium';\n const borderColorObj = opts.bottomBorderColor === undefined ? undefined : typeof opts.bottomBorderColor === 'string' ? makeColor({ rgb: opts.bottomBorderColor }) : makeColor(opts.bottomBorderColor);\n\n const fillColorObj = typeof fillColor === 'string' ? makeColor({ rgb: fillColor }) : fillColor;\n const fontColorObj = typeof fontColor === 'string' ? makeColor({ rgb: fontColor }) : fontColor;\n\n const styleOpts: Parameters<typeof setRangeStyle>[3] = {\n font: makeFont({ bold, color: fontColorObj }),\n fill: makePatternFill({ patternType: 'solid', fgColor: fillColorObj }),\n };\n if (borderStyle !== false) {\n const side = makeSide({ style: borderStyle, ...(borderColorObj ? { color: borderColorObj } : {}) });\n styleOpts.border = makeBorder({ bottom: side });\n }\n setRangeStyle(wb, ws, range, styleOpts);\n}\n\n// ---- named / built-in style application --------------------------------\n\n/**\n * Apply a built-in Excel style (\"Heading 1\" / \"Total\" / \"Good\" / \"Bad\" /\n * \"Calculation\" / etc.) to a single cell. Registers the built-in on the\n * Stylesheet (idempotent) and points the cell's xf at it via `xfId` while\n * inheriting the matching font/fill/border/ numFmt ids so the cell renders\n * correctly on its own.\n *\n * Throws when `name` isn't in {@link BUILTIN_NAMED_STYLES}; use {@link\n * applyNamedStyle} for user-registered styles.\n */\nexport function applyBuiltinStyle(wb: Workbook, c: Cell, name: string): void {\n const xfId = ensureBuiltinStyle(wb.styles, name);\n applyNamedStyleByXfId(wb, c, xfId);\n}\n\n/**\n * Apply a NamedStyle that's already registered on the workbook (via\n * `addNamedStyle` or `ensureBuiltinStyle`) to a single cell, by name.\n */\nexport function applyNamedStyle(wb: Workbook, c: Cell, name: string): void {\n const entry = wb.styles._namedStyleByName?.get(name);\n if (entry === undefined) {\n throw new OpenXmlSchemaError(`applyNamedStyle: no named style \"${name}\" registered`);\n }\n applyNamedStyleByXfId(wb, c, entry.xfId);\n}\n\nconst applyNamedStyleByXfId = (wb: Workbook, c: Cell, xfId: number): void => {\n const styleXf = wb.styles.cellStyleXfs[xfId];\n if (!styleXf) {\n throw new OpenXmlSchemaError(`applyNamedStyle: cellStyleXfs[${xfId}] missing`);\n }\n // Excel only renders the cellStyleXf's font / fill / border when the cellXf\n // carries the matching `apply*` flag — even a `xfId` reference alone doesn't\n // make Excel inherit the values. So we copy every component the named style\n // touches *and* set the corresponding apply* flag, mirroring what real Excel\n // writes for `s=` referenced cells using a built-in named style.\n const patch: { -readonly [K in keyof CellXf]?: CellXf[K] } = {\n xfId,\n fontId: styleXf.fontId,\n fillId: styleXf.fillId,\n borderId: styleXf.borderId,\n numFmtId: styleXf.numFmtId,\n applyFont: true,\n applyFill: true,\n applyBorder: true,\n applyNumberFormat: true,\n };\n if (styleXf.alignment !== undefined) {\n patch.alignment = styleXf.alignment;\n patch.applyAlignment = true;\n }\n if (styleXf.protection !== undefined) {\n patch.protection = styleXf.protection;\n patch.applyProtection = true;\n }\n applyXfPatch(wb, c, patch as Partial<CellXf>);\n};\n\n// ---- border presets ----------------------------------------------------\n\n/**\n * Apply the same {@link SideStyle} to all four edges of a single cell. Optional\n * color via hex string or `Color` partial. Equivalent to `setCellBorder(wb, c,\n * makeBorder({ left, right, top, bottom: side }))` with all four sides\n * identical.\n */\nexport function setCellBorderAll(\n wb: Workbook,\n c: Cell,\n opts: { style: SideStyle; color?: string | Partial<Color> } = { style: 'thin' },\n): void {\n const colorObj = opts.color === undefined ? undefined : typeof opts.color === 'string' ? makeColor({ rgb: opts.color }) : makeColor(opts.color);\n const side = makeSide({ style: opts.style, ...(colorObj ? { color: colorObj } : {}) });\n setCellBorder(wb, c, makeBorder({ left: side, right: side, top: side, bottom: side }));\n}\n\n/**\n * Draw an outer border around a rectangular range. Cells on the perimeter\n * receive a partial border (only the edges that face outside the range); inner\n * cells are unaffected unless `inner` is provided, in which case every cell in\n * the range receives a border combining its perimeter edges with the `inner`\n * style for the inside edges.\n */\nexport function setRangeBorderBox(\n wb: Workbook,\n ws: Worksheet,\n range: string,\n opts: { style: SideStyle; color?: string | Partial<Color>; inner?: SideStyle } = { style: 'thin' },\n): void {\n const { minRow, maxRow, minCol, maxCol } = parseRange(range);\n const colorObj = opts.color === undefined ? undefined : typeof opts.color === 'string' ? makeColor({ rgb: opts.color }) : makeColor(opts.color);\n const outer = makeSide({ style: opts.style, ...(colorObj ? { color: colorObj } : {}) });\n const inner =\n opts.inner !== undefined\n ? makeSide({ style: opts.inner, ...(colorObj ? { color: colorObj } : {}) })\n : undefined;\n for (let r = minRow; r <= maxRow; r++) {\n for (let col = minCol; col <= maxCol; col++) {\n const onTop = r === minRow;\n const onBottom = r === maxRow;\n const onLeft = col === minCol;\n const onRight = col === maxCol;\n // Skip cells that wouldn't get any styling (interior cells when no\n // inner).\n if (!inner && !onTop && !onBottom && !onLeft && !onRight) continue;\n const sides: { -readonly [K in keyof Border]?: Border[K] } = {};\n const top = onTop ? outer : inner;\n const bottom = onBottom ? outer : inner;\n const left = onLeft ? outer : inner;\n const right = onRight ? outer : inner;\n if (top !== undefined) sides.top = top;\n if (bottom !== undefined) sides.bottom = bottom;\n if (left !== undefined) sides.left = left;\n if (right !== undefined) sides.right = right;\n let cell = ws.rows.get(r)?.get(col);\n if (!cell) cell = setCell(ws, r, col);\n setCellBorder(wb, cell, makeBorder(sides));\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"coordinate-96Ecci4d.mjs","names":[],"sources":["../src/utils/coordinate.ts"],"sourcesContent":["// Worksheet coordinate utilities. Mirrors openpyxl/openpyxl/utils/cell.py.\n//\n// These functions are on the worksheet read/write hot path (millions of calls\n// when streaming a sheet) so the implementations stay branch-light, regex-based\n// only at the entry points, with bounded Map caches for the bidirectional\n// column letter <-> index mapping.\n\nimport { OpenXmlSchemaError } from './exceptions.js';\n\n/** Maximum column index Excel accepts (XFD). */\nexport const MAX_COL = 16384;\n/** Maximum row index Excel accepts. */\nexport const MAX_ROW = 1048576;\n\n// ---- column letter <-> 1-based index ---------------------------------------\n\nconst indexByLetter = new Map<string, number>();\nconst letterByIndex = new Map<number, string>();\n\n/**\n * 1-based column index → spreadsheet column letter (\"A\", \"Z\", \"AA\", \"XFD\").\n * Throws OpenXmlSchemaError when out of range.\n */\nexport function columnLetterFromIndex(n: number): string {\n const cached = letterByIndex.get(n);\n if (cached !== undefined) return cached;\n if (!Number.isInteger(n) || n < 1 || n > MAX_COL) {\n throw new OpenXmlSchemaError(`column index ${n} is out of range [1, ${MAX_COL}]`);\n }\n let m = n;\n let out = '';\n while (m > 0) {\n m -= 1; // shift to 0-based for the modulo\n out = String.fromCharCode(65 + (m % 26)) + out;\n m = Math.floor(m / 26);\n }\n letterByIndex.set(n, out);\n return out;\n}\n\n/**\n * Column letter → 1-based column index. Case-insensitive but at most 3 letters\n * (the spec ceiling). Throws on empty / non-A-Z / over-range.\n */\nexport function columnIndexFromLetter(letter: string): number {\n const cached = indexByLetter.get(letter);\n if (cached !== undefined) return cached;\n if (letter.length === 0 || letter.length > 3) {\n throw new OpenXmlSchemaError(`column letter \"${letter}\" is empty or too long`);\n }\n let n = 0;\n for (let i = 0; i < letter.length; i++) {\n const c = letter.charCodeAt(i);\n let v: number;\n if (c >= 65 && c <= 90)\n v = c - 64; // 'A' = 65\n else if (c >= 97 && c <= 122)\n v = c - 96; // 'a' = 97\n else throw new OpenXmlSchemaError(`column letter \"${letter}\" contains non-letter char`);\n n = n * 26 + v;\n }\n if (n < 1 || n > MAX_COL) {\n throw new OpenXmlSchemaError(`column letter \"${letter}\" expands to out-of-range index ${n}`);\n }\n // Normalise the cache key to upper-case so 'a' / 'A' share a slot.\n const key = letter.toUpperCase();\n indexByLetter.set(key, n);\n if (key !== letter) indexByLetter.set(letter, n);\n return n;\n}\n\n// ---- coordinate parsing ----------------------------------------------------\n\n/** A single-cell coordinate split into its letter and 1-based row. */\nexport interface CellCoordinate {\n column: string;\n row: number;\n}\n\n/** A single-cell coordinate split into 1-based numeric (col, row). */\nexport interface CellCoordinateNumeric {\n col: number;\n row: number;\n}\n\n/** A 1-based rectangular boundary. */\nexport interface CellRangeBoundaries {\n minCol: number;\n minRow: number;\n maxCol: number;\n maxRow: number;\n}\n\nconst COORD_RE = /^[$]?([A-Za-z]{1,3})[$]?([1-9][0-9]*)$/;\nconst COL_RANGE_RE = /^[$]?([A-Za-z]{1,3}):[$]?([A-Za-z]{1,3})$/;\nconst ROW_RANGE_RE = /^[$]?([1-9][0-9]*):[$]?([1-9][0-9]*)$/;\nconst SHEET_RANGE_RE = /^(?:'((?:[^']|'')+)'|([^'!]+))!(.+)$/;\n\n/**\n * Parse a single-cell coordinate string (\"A1\", \"$XFD$1048576\") into its column\n * letter (always uppercased) and 1-based row.\n */\nexport function coordinateFromString(coord: string): CellCoordinate {\n const m = COORD_RE.exec(coord);\n if (m === null || m[1] === undefined || m[2] === undefined) {\n throw new OpenXmlSchemaError(`coordinateFromString: invalid coordinate \"${coord}\"`);\n }\n const column = m[1].toUpperCase();\n const row = Number.parseInt(m[2], 10);\n if (row < 1 || row > MAX_ROW) {\n throw new OpenXmlSchemaError(`coordinateFromString: row ${row} out of range`);\n }\n // Validate column upper bound through the cached helper.\n columnIndexFromLetter(column);\n return { column, row };\n}\n\n/**\n * Same as {@link coordinateFromString} but returning the column as its 1-based\n * numeric index. Thin convenience for the worksheet read path.\n */\nexport function coordinateToTuple(coord: string): CellCoordinateNumeric {\n const c = coordinateFromString(coord);\n return { col: columnIndexFromLetter(c.column), row: c.row };\n}\n\n/** Compose `\"A1\"` from a 1-based (col, row). */\nexport function tupleToCoordinate(col: number, row: number): string {\n if (!Number.isInteger(row) || row < 1 || row > MAX_ROW) {\n throw new OpenXmlSchemaError(`tupleToCoordinate: row ${row} out of range`);\n }\n return `${columnLetterFromIndex(col)}${row}`;\n}\n\n/**\n * Predicate: true iff `s` is a valid single-cell A1 coordinate (`\"A1\"`,\n * `\"XFD1048576\"`). Strings with `$` absolute markers, surrounding whitespace,\n * ranges (`A1:B2`), or out-of-bound row / column return false. Useful for\n * sanitising user input before passing to {@link coordinateToTuple} or\n * `setCellByCoord`.\n */\nexport function isValidCellRef(s: unknown): s is string {\n if (typeof s !== 'string') return false;\n const m = /^([A-Za-z]{1,3})([1-9][0-9]*)$/.exec(s);\n if (!m) return false;\n const colStr = m[1];\n const rowStr = m[2];\n if (colStr === undefined || rowStr === undefined) return false;\n const col = columnIndexFromLetterUnchecked(colStr);\n if (col < 1 || col > MAX_COL) return false;\n const row = Number.parseInt(rowStr, 10);\n if (!Number.isFinite(row) || row < 1 || row > MAX_ROW) return false;\n return true;\n}\n\n/**\n * Predicate: true iff `s` is a valid A1-style range expression — single cell,\n * two-corner range, whole column (`A:A`), or whole row (`1:1`). `$` markers,\n * whitespace, and out-of-bound bounds fail. Sanity-check before {@link\n * rangeBoundaries} / `parseRange`.\n */\nexport function isValidRangeRef(s: unknown): s is string {\n if (typeof s !== 'string' || s.length === 0) return false;\n if (isValidCellRef(s)) return true;\n const ab = /^([A-Za-z]{1,3})([1-9][0-9]*):([A-Za-z]{1,3})([1-9][0-9]*)$/.exec(s);\n if (ab) {\n const [, c1, r1, c2, r2] = ab;\n if (!c1 || !r1 || !c2 || !r2) return false;\n const col1 = columnIndexFromLetterUnchecked(c1);\n const col2 = columnIndexFromLetterUnchecked(c2);\n if (col1 < 1 || col1 > MAX_COL || col2 < 1 || col2 > MAX_COL) return false;\n const row1 = Number.parseInt(r1, 10);\n const row2 = Number.parseInt(r2, 10);\n return row1 >= 1 && row1 <= MAX_ROW && row2 >= 1 && row2 <= MAX_ROW;\n }\n const cc = /^([A-Za-z]{1,3}):([A-Za-z]{1,3})$/.exec(s);\n if (cc) {\n const [, c1, c2] = cc;\n if (!c1 || !c2) return false;\n const col1 = columnIndexFromLetterUnchecked(c1);\n const col2 = columnIndexFromLetterUnchecked(c2);\n return col1 >= 1 && col1 <= MAX_COL && col2 >= 1 && col2 <= MAX_COL;\n }\n const rr = /^([1-9][0-9]*):([1-9][0-9]*)$/.exec(s);\n if (rr) {\n const [, r1, r2] = rr;\n if (!r1 || !r2) return false;\n const row1 = Number.parseInt(r1, 10);\n const row2 = Number.parseInt(r2, 10);\n return row1 >= 1 && row1 <= MAX_ROW && row2 >= 1 && row2 <= MAX_ROW;\n }\n return false;\n}\n\nconst columnIndexFromLetterUnchecked = (letters: string): number => {\n let n = 0;\n const upper = letters.toUpperCase();\n for (let i = 0; i < upper.length; i++) {\n const ch = upper.charCodeAt(i);\n if (ch < 65 || ch > 90) return -1;\n n = n * 26 + (ch - 64);\n }\n return n;\n};\n\n/**\n * Predicate: true iff `s` is a valid 1..3-char column letter (`\"A\"` through\n * `\"XFD\"`, case-insensitive). Empty / over-long / out-of-bound / non-string\n * fails.\n */\nexport function isValidColumnLetter(s: unknown): s is string {\n if (typeof s !== 'string' || s.length === 0 || s.length > 3) return false;\n if (!/^[A-Za-z]+$/.test(s)) return false;\n const col = columnIndexFromLetterUnchecked(s);\n return col >= 1 && col <= MAX_COL;\n}\n\n/**\n * Predicate: true iff `n` is a valid 1-based row index in `[1, 1048576]`.\n * Non-finite / non-integer / out-of-bound fails.\n */\nexport function isValidRowNumber(n: unknown): n is number {\n return typeof n === 'number' && Number.isInteger(n) && n >= 1 && n <= MAX_ROW;\n}\n\n/**\n * Predicate: true iff `n` is a valid 1-based column index in `[1, 16384]`.\n * Non-finite / non-integer / out-of-bound fails.\n */\nexport function isValidColumnNumber(n: unknown): n is number {\n return typeof n === 'number' && Number.isInteger(n) && n >= 1 && n <= MAX_COL;\n}\n\n/**\n * Parse \"A1:B5\" / \"A:A\" / \"1:1\" / single-cell into 1-based (minCol, minRow,\n * maxCol, maxRow). Whole-column ranges fill rows to [1, MAX_ROW]; whole-row\n * ranges fill cols to [1, MAX_COL].\n */\nexport function rangeBoundaries(range: string): CellRangeBoundaries {\n const trimmed = range.trim();\n if (trimmed.length === 0) throw new OpenXmlSchemaError('rangeBoundaries: empty range');\n\n const colon = trimmed.indexOf(':');\n if (colon < 0) {\n // Single-cell shorthand: \"A1\" → A1:A1.\n const c = coordinateFromString(trimmed);\n const col = columnIndexFromLetter(c.column);\n return { minCol: col, minRow: c.row, maxCol: col, maxRow: c.row };\n }\n\n const left = trimmed.slice(0, colon);\n const right = trimmed.slice(colon + 1);\n\n const colsOnly = COL_RANGE_RE.exec(trimmed);\n if (colsOnly !== null && colsOnly[1] !== undefined && colsOnly[2] !== undefined) {\n const minCol = columnIndexFromLetter(colsOnly[1]);\n const maxCol = columnIndexFromLetter(colsOnly[2]);\n return {\n minCol: Math.min(minCol, maxCol),\n minRow: 1,\n maxCol: Math.max(minCol, maxCol),\n maxRow: MAX_ROW,\n };\n }\n\n const rowsOnly = ROW_RANGE_RE.exec(trimmed);\n if (rowsOnly !== null && rowsOnly[1] !== undefined && rowsOnly[2] !== undefined) {\n const minRow = Number.parseInt(rowsOnly[1], 10);\n const maxRow = Number.parseInt(rowsOnly[2], 10);\n if (minRow < 1 || maxRow < 1 || minRow > MAX_ROW || maxRow > MAX_ROW) {\n throw new OpenXmlSchemaError(`rangeBoundaries: row out of range in \"${trimmed}\"`);\n }\n return {\n minCol: 1,\n minRow: Math.min(minRow, maxRow),\n maxCol: MAX_COL,\n maxRow: Math.max(minRow, maxRow),\n };\n }\n\n const a = coordinateFromString(left);\n const b = coordinateFromString(right);\n const ac = columnIndexFromLetter(a.column);\n const bc = columnIndexFromLetter(b.column);\n return {\n minCol: Math.min(ac, bc),\n minRow: Math.min(a.row, b.row),\n maxCol: Math.max(ac, bc),\n maxRow: Math.max(a.row, b.row),\n };\n}\n\n/** Inverse of {@link rangeBoundaries} for the rectangular case. */\nexport function boundariesToRangeString(b: CellRangeBoundaries): string {\n const tl = tupleToCoordinate(b.minCol, b.minRow);\n if (b.minCol === b.maxCol && b.minRow === b.maxRow) return tl;\n const br = tupleToCoordinate(b.maxCol, b.maxRow);\n return `${tl}:${br}`;\n}\n\n/**\n * Parse a sheet-qualified range (\"Sheet1!A1:B5\" / \"'Quarter 1'!A1\"). Sheet\n * names with single quotes inside use SQL-style doubling (\"'Bob''s Sheet'!A1\")\n * — we unescape on the way out.\n */\nexport function parseSheetRange(input: string): {\n sheet: string;\n range: string;\n bounds: CellRangeBoundaries;\n} {\n const m = SHEET_RANGE_RE.exec(input);\n if (m === null) throw new OpenXmlSchemaError(`parseSheetRange: missing \"!\" delimiter in \"${input}\"`);\n const quoted = m[1];\n const bare = m[2];\n const range = m[3];\n if (range === undefined) throw new OpenXmlSchemaError(`parseSheetRange: empty range part in \"${input}\"`);\n const sheet = quoted !== undefined ? quoted.replace(/''/g, \"'\") : (bare ?? '');\n return { sheet, range, bounds: rangeBoundaries(range) };\n}\n\nconst BARE_SHEET_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/**\n * Inverse of {@link parseSheetRange}: format a sheet title + range (or\n * single-cell ref) as `Sheet1!A1` or `'Quarter 1'!A1` per Excel's\n * sheet-qualified syntax. Single quotes inside the title are escaped by\n * doubling (`'Bob''s Sheet'`).\n *\n * Quoting rule: sheet titles consisting only of `[A-Za-z_][A-Za-z0-9_]*` are\n * emitted bare; everything else (spaces, digits-leading, hyphens, apostrophes,\n * punctuation…) gets wrapped in single quotes.\n */\nexport function formatSheetQualifiedRef(sheet: string, ref: string): string {\n if (BARE_SHEET_NAME.test(sheet)) return `${sheet}!${ref}`;\n return `'${sheet.replace(/'/g, \"''\")}'!${ref}`;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"defined-names-CviWmtQg.mjs","names":[],"sources":["../src/workbook/defined-names.ts"],"sourcesContent":["// Workbook-level defined names. Models the OOXML schema's `<definedName>` element.\n//\n// A defined name binds an identifier to a formula-style value (`'Sheet\n// 1'!$A$1:$B$10`, `SUM(A:A)`, etc). Workbook-scope names omit `localSheetId`;\n// sheet-scope names use the 0-based sheet index. Excel reserves a handful of\n// names with the `_xlnm.` prefix for built-in uses (Print_Area, Print_Titles,\n// Sheet_Title, etc) — those round-trip here as plain DefinedName entries since\n// the value semantics are the same.\n\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\n\nexport interface DefinedName {\n /** Identifier — `_xlnm.Print_Area` for built-ins, otherwise user-chosen. */\n name: string;\n /** The formula expression the name points at. */\n value: string;\n /** 0-based sheet index for sheet-scope names; undefined → workbook-scope. */\n scope?: number;\n /** Hidden from the Name Manager when true. */\n hidden?: boolean;\n /** Optional human-readable description. */\n comment?: string;\n}\n\nexport function makeDefinedName(opts: Partial<DefinedName> & { name: string; value: string }): DefinedName {\n return {\n name: opts.name,\n value: opts.value,\n ...(opts.scope !== undefined ? { scope: opts.scope } : {}),\n ...(opts.hidden !== undefined ? { hidden: opts.hidden } : {}),\n ...(opts.comment !== undefined ? { comment: opts.comment } : {}),\n };\n}\n\n// ---- Workbook ergonomic helpers -----------------------------------------\n\nimport { type CellRangeBoundaries, parseSheetRange } from '../utils/coordinate.js';\nimport type { Worksheet } from '../worksheet/worksheet.js';\nimport { getRangeAddress } from '../worksheet/worksheet.js';\nimport type { Workbook } from './workbook.js';\n\n/**\n * One parsed leg of a defined name's value. Defined-name values can be\n * comma-separated multi-range expressions (e.g. `_xlnm.Print_Titles` sets\n * `Sheet!$1:$1,Sheet!$A:$A`); this represents one such leg.\n */\nexport interface DefinedNameTarget {\n sheet: string;\n range: string;\n bounds: CellRangeBoundaries;\n}\n\n/**\n * Add a workbook-scope or sheet-scope defined name. If a defined name with the\n * same `name` (and `scope`) already exists, it's replaced — Excel allows one\n * workbook-scope and one per-sheet-scope name, but not two with the same scope.\n * Returns the resulting `DefinedName`.\n */\nexport const addDefinedName = (\n wb: Workbook,\n opts: Partial<DefinedName> & { name: string; value: string },\n): DefinedName => {\n const dn = makeDefinedName(opts);\n // Replace any existing entry with the same name + scope.\n const idx = wb.definedNames.findIndex((d) => d.name === dn.name && d.scope === dn.scope);\n if (idx >= 0) {\n wb.definedNames[idx] = dn;\n } else {\n wb.definedNames.push(dn);\n }\n return dn;\n};\n\n/**\n * High-level: register a defined name pointing at a worksheet range. Combines\n * {@link getRangeAddress} (sheet-qualified, properly quoted) with {@link\n * addDefinedName}, so the caller doesn't have to assemble the formula string by\n * hand.\n *\n * Pass `opts.localToSheet: true` to scope the name to the worksheet (instead of\n * the workbook). Re-using the same `name` + scope replaces the previous entry\n * (Excel's per-scope-uniqueness rule).\n *\n * Throws when `localToSheet: true` is set but the worksheet isn't on\n * `wb.sheets` — that would be a stale Worksheet reference.\n */\nexport const addDefinedNameForRange = (\n wb: Workbook,\n name: string,\n ws: Worksheet,\n range: string,\n opts: { localToSheet?: boolean; hidden?: boolean; comment?: string } = {},\n): DefinedName => {\n const value = getRangeAddress(ws, range);\n let scope: number | undefined;\n if (opts.localToSheet) {\n const idx = wb.sheets.findIndex((s) => s.sheet === ws);\n if (idx < 0) {\n throw new OpenXmlSchemaError(\n `addDefinedNameForRange: worksheet \"${ws.title}\" is not registered on this workbook`,\n );\n }\n scope = idx;\n }\n return addDefinedName(wb, {\n name,\n value,\n ...(scope !== undefined ? { scope } : {}),\n ...(opts.hidden !== undefined ? { hidden: opts.hidden } : {}),\n ...(opts.comment !== undefined ? { comment: opts.comment } : {}),\n });\n};\n\n/** Look up a defined name by identifier and (optional) sheet scope. */\nexport const getDefinedName = (\n wb: Workbook,\n name: string,\n scope?: number,\n): DefinedName | undefined => wb.definedNames.find((d) => d.name === name && d.scope === scope);\n\n/**\n * Resolve a defined name's `value` into one or more {@link DefinedNameTarget}s.\n * Comma-separated values (e.g. `_xlnm.Print_Titles` typically sets\n * `Sheet!$1:$1,Sheet!$A:$A`) yield one entry per leg; a plain `Sheet!A1:B5`\n * yields a single-element array.\n *\n * Returns `undefined` when the name doesn't exist; throws when the value can't\n * be parsed (e.g. a constant or a non-range formula — defined names are\n * sometimes used for things like `=42` or `=SUM(A:A)` which aren't ranges).\n */\nexport const getDefinedNameTarget = (\n wb: Workbook,\n name: string,\n scope?: number,\n): DefinedNameTarget[] | undefined => {\n const dn = getDefinedName(wb, name, scope);\n if (!dn) return undefined;\n // Defined-name values use `,` as the leg separator. Sheet titles can\n // themselves contain commas inside `'...'` quotes — split on commas that\n // aren't inside an unbalanced single-quoted segment.\n const legs: string[] = [];\n let current = '';\n let inQuote = false;\n for (let i = 0; i < dn.value.length; i++) {\n const c = dn.value[i];\n if (c === \"'\") {\n // Doubled `''` inside a quoted run is the escape for a literal apostrophe\n // — skip the second one without flipping the state.\n if (inQuote && dn.value[i + 1] === \"'\") {\n current += \"''\";\n i++;\n continue;\n }\n inQuote = !inQuote;\n current += c;\n continue;\n }\n if (c === ',' && !inQuote) {\n legs.push(current);\n current = '';\n continue;\n }\n current += c;\n }\n if (current.length > 0) legs.push(current);\n return legs.map((leg) => parseSheetRange(leg));\n};\n\n/**\n * Remove a defined name by identifier + scope. Returns true if any entry was\n * removed.\n */\nexport const removeDefinedName = (wb: Workbook, name: string, scope?: number): boolean => {\n const idx = wb.definedNames.findIndex((d) => d.name === name && d.scope === scope);\n if (idx < 0) return false;\n wb.definedNames.splice(idx, 1);\n return true;\n};\n\n/**\n * Read-only snapshot of every defined name. Pass `{ scope }` to narrow to\n * workbook-scope (`scope: undefined`) or one specific sheet (`scope: 0`) — omit\n * the option entirely to list all.\n */\nexport const listDefinedNames = (\n wb: Workbook,\n opts: { scope?: number | 'workbook' | 'all' } = {},\n): ReadonlyArray<DefinedName> => {\n const scope = opts.scope ?? 'all';\n if (scope === 'all') return wb.definedNames;\n if (scope === 'workbook') return wb.definedNames.filter((d) => d.scope === undefined);\n return wb.definedNames.filter((d) => d.scope === scope);\n};\n\n/**\n * Bulk-remove every defined name matching `predicate`. Returns the count\n * removed. Mirrors {@link removeDataValidations} on worksheets.\n */\nexport const removeDefinedNames = (\n wb: Workbook,\n predicate: (d: DefinedName) => boolean,\n): number => {\n const before = wb.definedNames.length;\n wb.definedNames = wb.definedNames.filter((d) => !predicate(d));\n return before - wb.definedNames.length;\n};\n\n/**\n * Rename a defined name, scoped or workbook-scope. Returns `true` when an entry\n * was renamed. Throws when `newName` is already taken with the same scope\n * (Excel forbids duplicates within a scope).\n */\nexport const renameDefinedName = (\n wb: Workbook,\n oldName: string,\n newName: string,\n scope?: number,\n): boolean => {\n const idx = wb.definedNames.findIndex((d) => d.name === oldName && d.scope === scope);\n if (idx < 0) return false;\n const conflict = wb.definedNames.findIndex((d, i) => i !== idx && d.name === newName && d.scope === scope);\n if (conflict >= 0) {\n throw new OpenXmlSchemaError(`renameDefinedName: \"${newName}\" is already in use at the same scope`);\n }\n const existing = wb.definedNames[idx];\n if (!existing) return false;\n wb.definedNames[idx] = { ...existing, name: newName };\n return true;\n};\n\n/**\n * Read-only snapshot of every `_xlnm.Print_Area` defined name. Each entry is\n * the raw DefinedName carrying `scope` (sheet index) and `value` (the\n * print-area expression like `'Sheet1'!$A$1:$D$10`).\n */\nexport const listPrintAreas = (wb: Workbook): ReadonlyArray<DefinedName> =>\n wb.definedNames.filter((d) => d.name === '_xlnm.Print_Area');\n\n/**\n * Read-only snapshot of every `_xlnm.Print_Titles` defined name. Each entry's\n * `value` is the title-row / title-col expression Excel re-uses on every\n * printed page.\n */\nexport const listPrintTitles = (wb: Workbook): ReadonlyArray<DefinedName> =>\n wb.definedNames.filter((d) => d.name === '_xlnm.Print_Titles');\n\n/**\n * Define the print-area for a given sheet. Excel uses the built-in\n * `_xlnm.Print_Area` defined name with sheet scope.\n */\nexport const setPrintArea = (wb: Workbook, sheetIndex: number, ref: string): DefinedName => {\n return addDefinedName(wb, {\n name: '_xlnm.Print_Area',\n value: ref,\n scope: sheetIndex,\n });\n};\n\n/**\n * Define print-title rows / columns on a sheet. Excel uses the\n * `_xlnm.Print_Titles` defined name. Pass `rows` (\"$1:$1\") to repeat row 1 on\n * every printed page; `cols` (\"$A:$A\") to repeat column A.\n */\nexport const setPrintTitles = (\n wb: Workbook,\n sheetIndex: number,\n opts: { rows?: string; cols?: string; sheetName: string },\n): DefinedName => {\n const parts: string[] = [];\n // The wire form is \"Sheet!$1:$1,Sheet!$A:$A\"; both refs share the sheet\n // prefix.\n if (opts.cols !== undefined) parts.push(`'${opts.sheetName}'!${opts.cols}`);\n if (opts.rows !== undefined) parts.push(`'${opts.sheetName}'!${opts.rows}`);\n if (parts.length === 0) {\n throw new OpenXmlSchemaError('setPrintTitles: at least one of rows or cols must be set');\n }\n return addDefinedName(wb, {\n name: '_xlnm.Print_Titles',\n value: parts.join(','),\n scope: sheetIndex,\n });\n};\n"],"mappings":";;AAwBA,SAAgB,gBAAgB,MAA2E;CACzG,OAAO;EACL,MAAM,KAAK;EACX,OAAO,KAAK;EACZ,GAAI,KAAK,UAAU,KAAA,IAAY,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;EACxD,GAAI,KAAK,WAAW,KAAA,IAAY,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;EAC3D,GAAI,KAAK,YAAY,KAAA,IAAY,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;CAChE;AACF;;;;;;;AA0BA,MAAa,kBACX,IACA,SACgB;CAChB,MAAM,KAAK,gBAAgB,IAAI;CAE/B,MAAM,MAAM,GAAG,aAAa,WAAW,MAAM,EAAE,SAAS,GAAG,QAAQ,EAAE,UAAU,GAAG,KAAK;CACvF,IAAI,OAAO,GACT,GAAG,aAAa,OAAO;MAEvB,GAAG,aAAa,KAAK,EAAE;CAEzB,OAAO;AACT;;AA2CA,MAAa,kBACX,IACA,MACA,UAC4B,GAAG,aAAa,MAAM,MAAM,EAAE,SAAS,QAAQ,EAAE,UAAU,KAAK;;;;;;;;;;;AAY9F,MAAa,wBACX,IACA,MACA,UACoC;CACpC,MAAM,KAAK,eAAe,IAAI,MAAM,KAAK;CACzC,IAAI,CAAC,IAAI,OAAO,KAAA;CAIhB,MAAM,OAAiB,CAAC;CACxB,IAAI,UAAU;CACd,IAAI,UAAU;CACd,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,MAAM,QAAQ,KAAK;EACxC,MAAM,IAAI,GAAG,MAAM;EACnB,IAAI,MAAM,KAAK;GAGb,IAAI,WAAW,GAAG,MAAM,IAAI,OAAO,KAAK;IACtC,WAAW;IACX;IACA;GACF;GACA,UAAU,CAAC;GACX,WAAW;GACX;EACF;EACA,IAAI,MAAM,OAAO,CAAC,SAAS;GACzB,KAAK,KAAK,OAAO;GACjB,UAAU;GACV;EACF;EACA,WAAW;CACb;CACA,IAAI,QAAQ,SAAS,GAAG,KAAK,KAAK,OAAO;CACzC,OAAO,KAAK,KAAK,QAAQ,gBAAgB,GAAG,CAAC;AAC/C;;;;;AAMA,MAAa,qBAAqB,IAAc,MAAc,UAA4B;CACxF,MAAM,MAAM,GAAG,aAAa,WAAW,MAAM,EAAE,SAAS,QAAQ,EAAE,UAAU,KAAK;CACjF,IAAI,MAAM,GAAG,OAAO;CACpB,GAAG,aAAa,OAAO,KAAK,CAAC;CAC7B,OAAO;AACT;;;;;;AAOA,MAAa,oBACX,IACA,OAAgD,CAAC,MAClB;CAC/B,MAAM,QAAQ,KAAK,SAAS;CAC5B,IAAI,UAAU,OAAO,OAAO,GAAG;CAC/B,IAAI,UAAU,YAAY,OAAO,GAAG,aAAa,QAAQ,MAAM,EAAE,UAAU,KAAA,CAAS;CACpF,OAAO,GAAG,aAAa,QAAQ,MAAM,EAAE,UAAU,KAAK;AACxD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"inference-BrI9uIY4.mjs","names":[],"sources":["../src/utils/inference.ts"],"sourcesContent":["// Cell-value type inference. Maps a JS runtime value to the OOXML cell\n// `t` attribute value. Mirrors openpyxl's `Cell._bind_value` /\n// `_TYPES` / error-code path in openpyxl/openpyxl/cell/cell.py.\n\n/**\n * OOXML `t` attribute values. Note that 'inlineStr' is treated\n * separately — the writer chooses between 's' (shared string) and\n * 'inlineStr' based on workbook settings, not the value itself.\n */\nexport type CellDataType = 'n' | 's' | 'b' | 'd' | 'f' | 'e';\n\n/** Excel error tokens. Anything outside this set is treated as a string. */\nexport const ERROR_CODES: ReadonlySet<string> = new Set([\n '#NULL!',\n '#DIV/0!',\n '#VALUE!',\n '#REF!',\n '#NAME?',\n '#NUM!',\n '#N/A',\n '#GETTING_DATA',\n]);\n\n/**\n * Infer the cell `t` attribute for a runtime value.\n *\n * - `boolean` → 'b'\n * - `number` → 'n' (incl. integer numerics; date inference is left to\n * the caller because Excel decides on type via the cell's number\n * format, not the raw value)\n * - `Date` → 'd'\n * - string starting with `=` → 'f' (formula)\n * - string in {@link ERROR_CODES} → 'e'\n * - any other string → 's'\n * - `null` / `undefined` → 'n' (empty)\n *\n * Throws nothing — returns 'n' as the no-information fallback.\n */\nexport function inferCellType(value: unknown): CellDataType {\n if (typeof value === 'boolean') return 'b';\n if (typeof value === 'number') return 'n';\n if (value instanceof Date) return 'd';\n if (typeof value === 'string') {\n if (value.length > 0 && value.charCodeAt(0) === 61 /* '=' */) return 'f';\n if (ERROR_CODES.has(value)) return 'e';\n return 's';\n }\n return 'n';\n}\n"],"mappings":";;AAYA,MAAa,8BAAmC,IAAI,IAAI;CACtD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;;;;;;;;;;;;AAiBD,SAAgB,cAAc,OAA8B;CAC1D,IAAI,OAAO,UAAU,WAAW,OAAO;CACvC,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,IAAI,iBAAiB,MAAM,OAAO;CAClC,IAAI,OAAO,UAAU,UAAU;EAC7B,IAAI,MAAM,SAAS,KAAK,MAAM,WAAW,CAAC,MAAM,IAAc,OAAO;EACrE,IAAI,YAAY,IAAI,KAAK,GAAG,OAAO;EACnC,OAAO;CACT;CACA,OAAO;AACT"}