@office-kit/xlsx 0.8.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 (220) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +319 -0
  3. package/THIRD_PARTY_NOTICES.md +56 -0
  4. package/dist/cell/cell.d.ts +234 -0
  5. package/dist/cell/index.d.ts +4 -0
  6. package/dist/cell/rich-text.d.ts +37 -0
  7. package/dist/cell-D9CaNKnU.mjs +320 -0
  8. package/dist/cell-D9CaNKnU.mjs.map +1 -0
  9. package/dist/cell-style-BEDjMX1y.mjs +1579 -0
  10. package/dist/cell-style-BEDjMX1y.mjs.map +1 -0
  11. package/dist/cell.mjs +2 -0
  12. package/dist/chart/chart-xml.d.ts +16 -0
  13. package/dist/chart/chart.d.ts +735 -0
  14. package/dist/chart/cx/chartex-xml.d.ts +6 -0
  15. package/dist/chart/cx/chartex.d.ts +279 -0
  16. package/dist/chart/index.d.ts +6 -0
  17. package/dist/chart/user-shapes-xml.d.ts +4 -0
  18. package/dist/chart/user-shapes.d.ts +61 -0
  19. package/dist/chart.mjs +232 -0
  20. package/dist/chart.mjs.map +1 -0
  21. package/dist/chartsheet/chartsheet-xml.d.ts +17 -0
  22. package/dist/chartsheet/chartsheet.d.ts +121 -0
  23. package/dist/chartsheet/index.d.ts +2 -0
  24. package/dist/chartsheet-C3-tqkPy.mjs +23 -0
  25. package/dist/chartsheet-C3-tqkPy.mjs.map +1 -0
  26. package/dist/chartsheet.mjs +2 -0
  27. package/dist/colors-ovWAwnZI.mjs +67 -0
  28. package/dist/colors-ovWAwnZI.mjs.map +1 -0
  29. package/dist/compat/numbers.d.ts +14 -0
  30. package/dist/coordinate-96Ecci4d.mjs +276 -0
  31. package/dist/coordinate-96Ecci4d.mjs.map +1 -0
  32. package/dist/datetime-B2ySVlXt.mjs +71 -0
  33. package/dist/datetime-B2ySVlXt.mjs.map +1 -0
  34. package/dist/defined-names-CviWmtQg.mjs +89 -0
  35. package/dist/defined-names-CviWmtQg.mjs.map +1 -0
  36. package/dist/differential-D4dg-qtZ.mjs +37 -0
  37. package/dist/differential-D4dg-qtZ.mjs.map +1 -0
  38. package/dist/drawing/anchor.d.ts +63 -0
  39. package/dist/drawing/dml/colors.d.ts +109 -0
  40. package/dist/drawing/dml/dml-xml.d.ts +35 -0
  41. package/dist/drawing/dml/effect.d.ts +92 -0
  42. package/dist/drawing/dml/fill.d.ts +115 -0
  43. package/dist/drawing/dml/geometry.d.ts +113 -0
  44. package/dist/drawing/dml/line.d.ts +41 -0
  45. package/dist/drawing/dml/shape-properties.d.ts +33 -0
  46. package/dist/drawing/dml/text.d.ts +218 -0
  47. package/dist/drawing/drawing-xml.d.ts +5 -0
  48. package/dist/drawing/drawing.d.ts +117 -0
  49. package/dist/drawing/image.d.ts +40 -0
  50. package/dist/drawing/index.d.ts +14 -0
  51. package/dist/drawing-BxzLuryn.mjs +415 -0
  52. package/dist/drawing-BxzLuryn.mjs.map +1 -0
  53. package/dist/drawing.mjs +119 -0
  54. package/dist/drawing.mjs.map +1 -0
  55. package/dist/escape-DFTE7ZJc.mjs +51 -0
  56. package/dist/escape-DFTE7ZJc.mjs.map +1 -0
  57. package/dist/exceptions-D-CFwxgm.mjs +37 -0
  58. package/dist/exceptions-D-CFwxgm.mjs.map +1 -0
  59. package/dist/formula/tokenizer.d.ts +61 -0
  60. package/dist/formula/translate.d.ts +67 -0
  61. package/dist/inference-B3ES3KEJ.mjs +42 -0
  62. package/dist/inference-B3ES3KEJ.mjs.map +1 -0
  63. package/dist/io/browser.d.ts +41 -0
  64. package/dist/io/index.d.ts +7 -0
  65. package/dist/io/load.d.ts +46 -0
  66. package/dist/io/node-fs.d.ts +62 -0
  67. package/dist/io/node-save.d.ts +3 -0
  68. package/dist/io/node.d.ts +17 -0
  69. package/dist/io/save.d.ts +14 -0
  70. package/dist/io/sink.d.ts +54 -0
  71. package/dist/io/source.d.ts +14 -0
  72. package/dist/io.mjs +212 -0
  73. package/dist/io.mjs.map +1 -0
  74. package/dist/load-D5cbhoGx.mjs +1069 -0
  75. package/dist/load-D5cbhoGx.mjs.map +1 -0
  76. package/dist/manifest-Dps1-OpP.mjs +801 -0
  77. package/dist/manifest-Dps1-OpP.mjs.map +1 -0
  78. package/dist/node.d.ts +3 -0
  79. package/dist/node.mjs +308 -0
  80. package/dist/node.mjs.map +1 -0
  81. package/dist/packaging/core.d.ts +45 -0
  82. package/dist/packaging/custom.d.ts +62 -0
  83. package/dist/packaging/extended.d.ts +45 -0
  84. package/dist/packaging/index.d.ts +10 -0
  85. package/dist/packaging/manifest.d.ts +24 -0
  86. package/dist/packaging/relationships.d.ts +30 -0
  87. package/dist/packaging.mjs +2 -0
  88. package/dist/parser-DuLejQy1.mjs +156 -0
  89. package/dist/parser-DuLejQy1.mjs.map +1 -0
  90. package/dist/reader-D1fNW9k1.mjs +534 -0
  91. package/dist/reader-D1fNW9k1.mjs.map +1 -0
  92. package/dist/save-RohQtgEZ.mjs +745 -0
  93. package/dist/save-RohQtgEZ.mjs.map +1 -0
  94. package/dist/schema/core.d.ts +133 -0
  95. package/dist/schema/index.d.ts +3 -0
  96. package/dist/schema/serialize.d.ts +6 -0
  97. package/dist/schema.mjs +2 -0
  98. package/dist/serialize-55EnT30e.mjs +254 -0
  99. package/dist/serialize-55EnT30e.mjs.map +1 -0
  100. package/dist/serializer-BwbgHYJV.mjs +116 -0
  101. package/dist/serializer-BwbgHYJV.mjs.map +1 -0
  102. package/dist/streaming/index.d.ts +2 -0
  103. package/dist/streaming/read-only.d.ts +38 -0
  104. package/dist/streaming/write-only.d.ts +47 -0
  105. package/dist/streaming.mjs +612 -0
  106. package/dist/streaming.mjs.map +1 -0
  107. package/dist/styles/alignment.d.ts +33 -0
  108. package/dist/styles/alignment.schema.d.ts +3 -0
  109. package/dist/styles/borders.d.ts +40 -0
  110. package/dist/styles/borders.schema.d.ts +4 -0
  111. package/dist/styles/cell-style.d.ts +270 -0
  112. package/dist/styles/colors.d.ts +128 -0
  113. package/dist/styles/colors.schema.d.ts +3 -0
  114. package/dist/styles/differential.d.ts +41 -0
  115. package/dist/styles/fills.d.ts +54 -0
  116. package/dist/styles/fills.schema.d.ts +6 -0
  117. package/dist/styles/fonts.d.ts +44 -0
  118. package/dist/styles/fonts.schema.d.ts +3 -0
  119. package/dist/styles/index.d.ts +21 -0
  120. package/dist/styles/named-styles.d.ts +52 -0
  121. package/dist/styles/numbers.d.ts +39 -0
  122. package/dist/styles/numbers.schema.d.ts +3 -0
  123. package/dist/styles/protection.d.ts +9 -0
  124. package/dist/styles/protection.schema.d.ts +3 -0
  125. package/dist/styles/stylesheet-reader.d.ts +7 -0
  126. package/dist/styles/stylesheet-writer.d.ts +3 -0
  127. package/dist/styles/stylesheet.d.ts +95 -0
  128. package/dist/styles.mjs +4 -0
  129. package/dist/stylesheet-writer-C2eRmn22.mjs +8624 -0
  130. package/dist/stylesheet-writer-C2eRmn22.mjs.map +1 -0
  131. package/dist/table-DkX6UniA.mjs +113 -0
  132. package/dist/table-DkX6UniA.mjs.map +1 -0
  133. package/dist/tree-Bbs1C8Rc.mjs +192 -0
  134. package/dist/tree-Bbs1C8Rc.mjs.map +1 -0
  135. package/dist/units-rOMQqXh2.mjs +41 -0
  136. package/dist/units-rOMQqXh2.mjs.map +1 -0
  137. package/dist/user-shapes-DfmCGKB0.mjs +252 -0
  138. package/dist/user-shapes-DfmCGKB0.mjs.map +1 -0
  139. package/dist/utf8-D91g1XTG.mjs +143 -0
  140. package/dist/utf8-D91g1XTG.mjs.map +1 -0
  141. package/dist/utils/coordinate.d.ts +103 -0
  142. package/dist/utils/css.d.ts +18 -0
  143. package/dist/utils/datetime.d.ts +38 -0
  144. package/dist/utils/escape.d.ts +34 -0
  145. package/dist/utils/exceptions.d.ts +34 -0
  146. package/dist/utils/index.d.ts +11 -0
  147. package/dist/utils/inference.d.ts +24 -0
  148. package/dist/utils/stable-stringify.d.ts +7 -0
  149. package/dist/utils/units.d.ts +14 -0
  150. package/dist/utils/utf8.d.ts +1 -0
  151. package/dist/utils.mjs +39 -0
  152. package/dist/utils.mjs.map +1 -0
  153. package/dist/workbook/calc-properties.d.ts +47 -0
  154. package/dist/workbook/defined-names.d.ts +121 -0
  155. package/dist/workbook/file-recovery.d.ts +11 -0
  156. package/dist/workbook/file-sharing.d.ts +14 -0
  157. package/dist/workbook/file-version.d.ts +13 -0
  158. package/dist/workbook/function-groups.d.ts +10 -0
  159. package/dist/workbook/index.d.ts +24 -0
  160. package/dist/workbook/protection.d.ts +35 -0
  161. package/dist/workbook/shared-strings.d.ts +57 -0
  162. package/dist/workbook/smart-tags.d.ts +13 -0
  163. package/dist/workbook/views.d.ts +89 -0
  164. package/dist/workbook/workbook-properties.d.ts +57 -0
  165. package/dist/workbook/workbook.d.ts +643 -0
  166. package/dist/workbook-HGYNRBlV.mjs +636 -0
  167. package/dist/workbook-HGYNRBlV.mjs.map +1 -0
  168. package/dist/workbook.mjs +58 -0
  169. package/dist/workbook.mjs.map +1 -0
  170. package/dist/worksheet/auto-filter.d.ts +34 -0
  171. package/dist/worksheet/cell-range.d.ts +121 -0
  172. package/dist/worksheet/comments-xml.d.ts +24 -0
  173. package/dist/worksheet/comments.d.ts +13 -0
  174. package/dist/worksheet/conditional-formatting.d.ts +150 -0
  175. package/dist/worksheet/custom-sheet-views.d.ts +43 -0
  176. package/dist/worksheet/data-consolidate.d.ts +29 -0
  177. package/dist/worksheet/data-validations.d.ts +72 -0
  178. package/dist/worksheet/dimensions.d.ts +40 -0
  179. package/dist/worksheet/errors.d.ts +40 -0
  180. package/dist/worksheet/hyperlinks.d.ts +42 -0
  181. package/dist/worksheet/index.d.ts +46 -0
  182. package/dist/worksheet/ole-objects.d.ts +37 -0
  183. package/dist/worksheet/page-setup.d.ts +173 -0
  184. package/dist/worksheet/phonetic.d.ts +11 -0
  185. package/dist/worksheet/properties.d.ts +34 -0
  186. package/dist/worksheet/protected-ranges.d.ts +19 -0
  187. package/dist/worksheet/protection.d.ts +44 -0
  188. package/dist/worksheet/reader.d.ts +38 -0
  189. package/dist/worksheet/scenarios.d.ts +36 -0
  190. package/dist/worksheet/smart-tags.d.ts +23 -0
  191. package/dist/worksheet/sort-state.d.ts +28 -0
  192. package/dist/worksheet/table-xml.d.ts +5 -0
  193. package/dist/worksheet/table.d.ts +80 -0
  194. package/dist/worksheet/views.d.ts +47 -0
  195. package/dist/worksheet/web-publish.d.ts +21 -0
  196. package/dist/worksheet/worksheet.d.ts +935 -0
  197. package/dist/worksheet/writer.d.ts +72 -0
  198. package/dist/worksheet-CmCNoIgD.mjs +1726 -0
  199. package/dist/worksheet-CmCNoIgD.mjs.map +1 -0
  200. package/dist/worksheet.mjs +247 -0
  201. package/dist/worksheet.mjs.map +1 -0
  202. package/dist/writer-DspzfkNA.mjs +221 -0
  203. package/dist/writer-DspzfkNA.mjs.map +1 -0
  204. package/dist/xml/index.d.ts +10 -0
  205. package/dist/xml/iterparse.d.ts +22 -0
  206. package/dist/xml/namespaces.d.ts +91 -0
  207. package/dist/xml/parser.d.ts +7 -0
  208. package/dist/xml/serializer.d.ts +14 -0
  209. package/dist/xml/stream-writer.d.ts +39 -0
  210. package/dist/xml/tree.d.ts +37 -0
  211. package/dist/xml.mjs +140 -0
  212. package/dist/xml.mjs.map +1 -0
  213. package/dist/zip/decompression-guard.d.ts +70 -0
  214. package/dist/zip/index.d.ts +6 -0
  215. package/dist/zip/random-access-reader.d.ts +16 -0
  216. package/dist/zip/reader.d.ts +45 -0
  217. package/dist/zip/writer.d.ts +65 -0
  218. package/dist/zip/zip64-patch.d.ts +12 -0
  219. package/dist/zip.mjs +3 -0
  220. package/package.json +147 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workbook-HGYNRBlV.mjs","names":[],"sources":["../src/workbook/shared-strings.ts","../src/workbook/workbook.ts"],"sourcesContent":["// Shared-strings table read/write.\n//\n// Excel pulls every plain-string cell out of the sheet bodies and into\n// `xl/sharedStrings.xml` so duplicates compress well. The format is a flat list\n// of `<si>` entries, each holding either a single `<t>` (plain text) or a\n// sequence of `<r><rPr/>?<t/></r>` runs (rich text).\n//\n// Rich-text entries are kept as their full `RichText` runs so per-run fonts\n// (bold / italic / colour / size / …) survive the round-trip. Plain strings\n// still dedup against literal text; rich text is kept distinct per-cell\n// (Excel's writer doesn't dedupe rich-text either — formatting equality is\n// rarely worth comparing).\n\nimport type { RichText } from '../cell/rich-text';\nimport { type Color, colorToHex } from '../styles/colors';\nimport { escapeCellString, escapeXmlAttr, escapeXmlText, unescapeCellString } from '../utils/escape';\nimport { OpenXmlSchemaError } from '../utils/exceptions';\nimport { qname, SHEET_MAIN_NS } from '../xml/namespaces';\nimport { parseXml } from '../xml/parser';\nimport { findChild, findChildren, type XmlNode } from '../xml/tree';\n\nconst SST_TAG = `{${SHEET_MAIN_NS}}sst`;\nconst SI_TAG = `{${SHEET_MAIN_NS}}si`;\nconst T_TAG = `{${SHEET_MAIN_NS}}t`;\nconst R_TAG = `{${SHEET_MAIN_NS}}r`;\n\n/** A single SST entry: either a plain string or a rich-text run array. */\nexport type SharedStringEntry = string | { kind: 'rich-text'; runs: RichText };\n\n/**\n * Mutable shared-strings accumulator + lookup table. The same shape is used\n * during read (just populate `entries`) and write (call `addSharedString` from\n * the worksheet writer; emit to bytes at the end).\n */\nexport interface SharedStringsTable {\n /** Insertion-ordered list of unique entries. */\n entries: SharedStringEntry[];\n /** Reverse lookup keyed by literal text — rich-text entries skip this map. */\n index: Map<string, number>;\n}\n\nexport function makeSharedStrings(): SharedStringsTable {\n return { entries: [], index: new Map() };\n}\n\n/**\n * Insert a string and return its index. Idempotent: calling with the same value\n * twice gives the same index. Empty strings are deduped just like everything\n * else.\n */\nexport function addSharedString(table: SharedStringsTable, value: string): number {\n const cached = table.index.get(value);\n if (cached !== undefined) return cached;\n const id = table.entries.length;\n table.entries.push(value);\n table.index.set(value, id);\n return id;\n}\n\n/** Look up a shared-string index by its literal text. Returns `undefined` for unknown values. */\nexport function getSharedStringIndex(table: SharedStringsTable, value: string): number | undefined {\n return table.index.get(value);\n}\n\n/**\n * Read a shared-string by its 0-based index. Returns `undefined` for\n * out-of-range. Rich-text entries surface their concatenated plain text so\n * callers that want only the textual body don't need to know about the\n * discriminated union.\n */\nexport function getSharedStringAt(table: SharedStringsTable, index: number): string | undefined {\n const entry = table.entries[index];\n if (entry === undefined) return undefined;\n if (typeof entry === 'string') return entry;\n return entry.runs.map((r) => r.text).join('');\n}\n\n/** Number of unique entries in the SST. */\nexport function sharedStringCount(table: SharedStringsTable): number {\n return table.entries.length;\n}\n\n// ---- read ------------------------------------------------------------------\n\n/** Concatenate every `<t>` text node found inside an arbitrary XmlNode tree. */\nconst collectText = (node: XmlNode): string => {\n // Most common case: a direct `<si><t>x</t></si>` — bypass the recursion.\n if (node.children.length === 1) {\n const only = node.children[0];\n if (only && only.name === T_TAG) return unescapeCellString(only.text ?? '');\n }\n let out = '';\n for (const child of node.children) {\n if (child.name === T_TAG) {\n out += child.text ?? '';\n } else if (child.name === R_TAG) {\n const t = findChild(child, T_TAG);\n if (t?.text) out += t.text;\n }\n }\n return unescapeCellString(out);\n};\n\n/**\n * Parse a `xl/sharedStrings.xml` payload. Returns the table directly (rather\n * than just the array) so the worksheet writer can keep appending to it without\n * rebuilding the index.\n *\n * Rich-text runs are preserved as their full per-run formatting so\n * round-tripping a file with rich text doesn't drop the styling.\n */\nexport function parseSharedStringsXml(bytes: Uint8Array | string): SharedStringsTable {\n const root = parseXml(bytes);\n if (root.name !== SST_TAG) {\n throw new OpenXmlSchemaError(`parseSharedStringsXml: root is \"${root.name}\", expected sst`);\n }\n const table = makeSharedStrings();\n for (const si of findChildren(root, SI_TAG)) {\n const entry = parseSi(si);\n // Don't dedup — Excel preserves duplicate `<si>` entries by index, and the\n // worksheet `t=\"s\"` references depend on slot, not on text equality.\n const id = table.entries.length;\n table.entries.push(entry);\n if (typeof entry === 'string' && !table.index.has(entry)) table.index.set(entry, id);\n }\n return table;\n}\n\nconst parseSi = (si: XmlNode): SharedStringEntry => {\n // Rich-text si has one or more <r> children. Plain si has a single <t>.\n const runEls = findChildren(si, R_TAG);\n if (runEls.length > 0) {\n const runs: Array<{ text: string; font?: import('../cell/rich-text').InlineFont }> = [];\n for (const rEl of runEls) {\n const tEl = findChild(rEl, T_TAG);\n const text = unescapeCellString(tEl?.text ?? '');\n const rPrEl = findChild(rEl, qname(SHEET_MAIN_NS, 'rPr'));\n const font = rPrEl ? parseRunPr(rPrEl) : undefined;\n runs.push(font !== undefined ? { text, font } : { text });\n }\n return { kind: 'rich-text', runs: Object.freeze(runs) };\n }\n return collectText(si);\n};\n\nconst parseRunPr = (rPr: XmlNode): import('../cell/rich-text').InlineFont | undefined => {\n type InlineFontMutable = {\n -readonly [K in keyof import('../cell/rich-text').InlineFont]: import('../cell/rich-text').InlineFont[K];\n };\n const f: InlineFontMutable = {};\n for (const child of rPr.children) {\n const local = child.name.replace(/^\\{[^}]+\\}/, '');\n const valAttr = child.attrs['val'];\n switch (local) {\n case 'rFont':\n case 'name':\n if (valAttr !== undefined) f.name = valAttr;\n break;\n case 'sz':\n if (valAttr !== undefined) f.sz = Number.parseFloat(valAttr);\n break;\n case 'b':\n f.b = valAttr === undefined ? true : valAttr !== '0' && valAttr !== 'false';\n break;\n case 'i':\n f.i = valAttr === undefined ? true : valAttr !== '0' && valAttr !== 'false';\n break;\n case 'u': {\n const v = (valAttr ?? 'single') as import('../cell/rich-text').InlineUnderline;\n f.u = v;\n break;\n }\n case 'strike':\n f.strike = valAttr === undefined ? true : valAttr !== '0' && valAttr !== 'false';\n break;\n case 'vertAlign':\n if (valAttr !== undefined) f.vertAlign = valAttr as import('../cell/rich-text').InlineVertAlign;\n break;\n case 'family':\n if (valAttr !== undefined) f.family = Number.parseInt(valAttr, 10);\n break;\n case 'charset':\n if (valAttr !== undefined) f.charset = Number.parseInt(valAttr, 10);\n break;\n case 'scheme':\n if (valAttr !== undefined) f.scheme = valAttr as 'major' | 'minor';\n break;\n case 'color': {\n const c: { rgb?: string; theme?: number; indexed?: number; tint?: number; auto?: boolean } = {};\n if (child.attrs['rgb'] !== undefined) c.rgb = child.attrs['rgb'];\n if (child.attrs['theme'] !== undefined) c.theme = Number.parseInt(child.attrs['theme'], 10);\n if (child.attrs['indexed'] !== undefined) c.indexed = Number.parseInt(child.attrs['indexed'], 10);\n if (child.attrs['tint'] !== undefined) c.tint = Number.parseFloat(child.attrs['tint']);\n if (child.attrs['auto'] !== undefined) c.auto = child.attrs['auto'] === '1' || child.attrs['auto'] === 'true';\n f.color = c as Color;\n break;\n }\n }\n }\n return Object.keys(f).length === 0 ? undefined : Object.freeze(f as import('../cell/rich-text').InlineFont);\n};\n\n// ---- write -----------------------------------------------------------------\n\nconst XML_HEADER = '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>';\n\n/**\n * Serialise a SharedStringsTable to its OOXML bytes. The `count` attribute\n * tracks total references (we don't know that here so we report the same as\n * `uniqueCount` — readers tolerate the discrepancy and Excel ignores `count` in\n * practice). `uniqueCount` always matches `entries.length`.\n */\nexport function sharedStringsToBytes(table: SharedStringsTable): Uint8Array {\n return new TextEncoder().encode(serializeSharedStrings(table));\n}\n\nexport function serializeSharedStrings(table: SharedStringsTable): string {\n const total = table.entries.length;\n const parts: string[] = [XML_HEADER, `<sst xmlns=\"${SHEET_MAIN_NS}\" count=\"${total}\" uniqueCount=\"${total}\">`];\n for (const value of table.entries) {\n parts.push(serializeSi(value));\n }\n parts.push('</sst>');\n return parts.join('');\n}\n\nconst serializeSi = (value: SharedStringEntry): string => {\n if (typeof value === 'string') {\n // Whitespace at either end needs xml:space=\"preserve\" so Excel doesn't\n // collapse it. Mirrors openpyxl's emitter.\n const preserve = value.length > 0 && (value[0] === ' ' || value[value.length - 1] === ' ' || /[\\t\\n]/.test(value));\n const tAttr = preserve ? ' xml:space=\"preserve\"' : '';\n return `<si><t${tAttr}>${escapeXmlText(escapeCellString(value))}</t></si>`;\n }\n return `<si>${serializeRichTextRuns(value.runs)}</si>`;\n};\n\n/**\n * Serialise a sequence of `<r>...<r>` runs — shared by the SST `<si>` writer\n * and the worksheet's inline-string (`t=\"inlineStr\"`) cell writer.\n */\nexport function serializeRichTextRuns(runs: import('../cell/rich-text').RichText): string {\n const parts: string[] = [];\n for (const run of runs) {\n parts.push('<r>');\n if (run.font) parts.push(serializeInlineFont(run.font));\n const text = run.text;\n const preserve = text.length > 0 && (text[0] === ' ' || text[text.length - 1] === ' ' || /[\\t\\n]/.test(text));\n const tAttr = preserve ? ' xml:space=\"preserve\"' : '';\n parts.push(`<t${tAttr}>${escapeXmlText(escapeCellString(text))}</t>`);\n parts.push('</r>');\n }\n return parts.join('');\n}\n\nconst serializeInlineFont = (f: import('../cell/rich-text').InlineFont): string => {\n // Element order per ECMA-376 §17.4.4.10 (CT_RPrElt). Excel's parser is\n // sensitive to ordering — out-of-order children make the run silently fall\n // back to the cell's font.\n const parts: string[] = ['<rPr>'];\n if (f.name !== undefined) parts.push(`<rFont val=\"${escapeXmlAttr(f.name)}\"/>`);\n if (f.charset !== undefined) parts.push(`<charset val=\"${f.charset}\"/>`);\n if (f.family !== undefined) parts.push(`<family val=\"${f.family}\"/>`);\n if (f.b) parts.push('<b/>');\n if (f.i) parts.push('<i/>');\n if (f.strike) parts.push('<strike/>');\n if (f.outline) parts.push('<outline/>');\n if (f.shadow) parts.push('<shadow/>');\n if (f.condense) parts.push('<condense/>');\n if (f.extend) parts.push('<extend/>');\n if (f.color) parts.push(serializeRunColor(f.color));\n if (f.sz !== undefined) parts.push(`<sz val=\"${f.sz}\"/>`);\n if (f.u) parts.push(`<u val=\"${f.u}\"/>`);\n if (f.vertAlign) parts.push(`<vertAlign val=\"${f.vertAlign}\"/>`);\n if (f.scheme) parts.push(`<scheme val=\"${f.scheme}\"/>`);\n parts.push('</rPr>');\n return parts.join('');\n};\n\nconst serializeRunColor = (c: Color): string => {\n const attrs: string[] = [];\n if (c.rgb !== undefined) attrs.push(`rgb=\"${escapeXmlAttr(c.rgb)}\"`);\n else if (c.theme !== undefined) attrs.push(`theme=\"${c.theme}\"`);\n else if (c.indexed !== undefined) attrs.push(`indexed=\"${c.indexed}\"`);\n else if (c.auto !== undefined) attrs.push(`auto=\"${c.auto ? '1' : '0'}\"`);\n if (c.tint !== undefined) attrs.push(`tint=\"${c.tint}\"`);\n // Fallback to colorToHex if nothing was set above (shouldn't happen, but\n // safe).\n if (attrs.length === 0) {\n const hex = colorToHex(c);\n if (hex !== undefined) attrs.push(`rgb=\"${hex}\"`);\n }\n return `<color${attrs.length > 0 ? ` ${attrs.join(' ')}` : ''}/>`;\n};\n","// Workbook root model. / §4.2 / the Workbook is a plain mutable object the user\n// composes via free functions. The Stylesheet pool is held inline so styling\n// operations don't need a side channel.\n\nimport type { Chartsheet } from '../chartsheet/chartsheet';\nimport { makeChartsheet } from '../chartsheet/chartsheet';\nimport { makeAbsoluteAnchor } from '../drawing/anchor';\nimport { type ChartReference, makeChartDrawingItem, makeDrawing } from '../drawing/drawing';\nimport type { CoreProperties } from '../packaging/core';\nimport type { CustomProperties } from '../packaging/custom';\nimport type { ExtendedProperties } from '../packaging/extended';\nimport {\n getCellAlignment,\n getCellBorder,\n getCellFill,\n getCellFont,\n getCellNumberFormat,\n getCellProtection,\n} from '../styles/cell-style';\nimport type { Stylesheet } from '../styles/stylesheet';\nimport { makeStylesheet } from '../styles/stylesheet';\nimport { OpenXmlSchemaError } from '../utils/exceptions';\nimport { type CellValue, isFormulaValue } from '../cell/cell';\nimport type { Alignment } from '../styles/alignment';\nimport type { Border } from '../styles/borders';\nimport type { Fill } from '../styles/fills';\nimport type { Font } from '../styles/fonts';\nimport type { Protection } from '../styles/protection';\nimport { coordinateToTuple, parseSheetRange } from '../utils/coordinate';\nimport { multiCellRangeContainsCell, parseRange, rangeContainsCell, rangeToString } from '../worksheet/cell-range';\nimport type { LegacyComment } from '../worksheet/comments';\nimport type { Hyperlink } from '../worksheet/hyperlinks';\nimport type { CellsByKindCounts, Worksheet } from '../worksheet/worksheet';\nimport {\n classifyCellValue,\n countCellsByKind,\n getCell,\n getCellComment,\n getCellHyperlink,\n getMergedRangeAt,\n isWorksheetEmpty,\n makeWorksheet,\n setCellByCoord,\n} from '../worksheet/worksheet';\n\nexport type SheetState = 'visible' | 'hidden' | 'veryHidden';\n\n/**\n * Discriminated union over the two kinds of sheet a workbook can host. Both\n * variants share `title` (via `sheet.title`) plus the OOXML `sheetId` and\n * `state` attributes; consumers narrow on `kind` to reach the worksheet- vs\n * chartsheet-specific data.\n */\nexport type SheetRef =\n | { kind: 'worksheet'; sheet: Worksheet; sheetId: number; state: SheetState; rId?: string }\n | { kind: 'chartsheet'; sheet: Chartsheet; sheetId: number; state: SheetState; rId?: string };\n\nexport interface Workbook {\n sheets: SheetRef[];\n /** Index into `sheets` of the sheet shown when Excel opens the file. */\n activeSheetIndex: number;\n /** Style pool; cells reference its cellXfs by index. */\n styles: Stylesheet;\n /** Date1904 mode toggles between Excel's two epoch systems. */\n date1904: boolean;\n /** Document properties (docProps/core.xml), typically auto-filled on save. */\n properties?: CoreProperties;\n appProperties?: ExtendedProperties;\n customProperties?: CustomProperties;\n /** Author display names, shared between threaded comments. */\n authors: string[];\n /** Workbook + sheet-scope defined names (named ranges, print areas etc). */\n definedNames: import('./defined-names').DefinedName[];\n /**\n * Raw `xl/theme/theme1.xml` payload kept verbatim across read → write. The\n * theme XML is large and seldom edited by writers; we just shuttle it.\n */\n themeXml?: Uint8Array;\n /**\n * `xl/vbaProject.bin` payload (macro-enabled workbooks). Round-tripped\n * byte-identical when present; the writer also promotes the workbook Override\n * to `vnd.ms-excel.sheet.macroEnabled.main+xml`.\n */\n vbaProject?: Uint8Array;\n /** `xl/vbaProjectSignature.bin` payload, when the macros are signed. */\n vbaSignature?: Uint8Array;\n /**\n * Pass-through bytes for parts we don't model (pivot tables, ActiveX\n * controls, OLE embeddings, customUI ribbons, customXml items …). Keys are\n * archive-relative paths; values are the raw bytes the loader pulled out of\n * the zip and the writer pushes back in unchanged.\n */\n passthrough?: Map<string, Uint8Array>;\n /**\n * Override content type per pass-through path. Excel uses these in\n * `[Content_Types].xml` so manifest validation stays intact across\n * round-trips. Paths without an explicit override fall back to the archive\n * Default extension.\n */\n passthroughContentTypes?: Map<string, string>;\n /**\n * Top-level `<workbook>` children that aren't `<sheets>` or `<definedNames>`\n * (e.g. `<fileVersion>`, `<workbookPr>`, `<bookViews>`, `<calcPr>`,\n * `<pivotCaches>`, `<extLst>`). Captured verbatim so re-saving keeps\n * Excel-rendering fidelity for things we don't model. Split into the two\n * halves the writer needs: anything before `<sheets>` is emitted ahead of the\n * `<sheets>` element, the rest after `<definedNames>`.\n */\n workbookXmlExtras?: {\n beforeSheets: import('../xml/tree').XmlNode[];\n afterSheets: import('../xml/tree').XmlNode[];\n };\n /**\n * `<workbookProtection>` — locks structure / window / revision tracking with\n * the modern hash quad or the legacy 16-bit hash. Round-tripped verbatim;\n * password hashing helpers come later.\n */\n workbookProtection?: import('./protection').WorkbookProtection;\n /**\n * `<bookViews>` — the workbook's window/tab-strip presets. Most workbooks\n * have a single entry whose `firstSheet` / `activeTab` drive the tab the user\n * sees first. Stored as an array because Excel allows multiple views (rare).\n */\n bookViews?: import('./views').WorkbookView[];\n /**\n * `<customWorkbookViews>` — saved per-user view presets used by the\n * deprecated \"Shared Workbook\" feature. Each entry carries its own window\n * position, active sheet, and visibility toggles.\n */\n customWorkbookViews?: import('./views').CustomWorkbookView[];\n /** `<calcPr>` — calculation engine settings (calcMode / iterate / fullPrecision etc.). */\n calcProperties?: import('./calc-properties').CalcProperties;\n /** `<fileVersion>` — Office app/version metadata Excel records on save. */\n fileVersion?: import('./file-version').FileVersion;\n /** `<fileSharing>` — read-only-recommended toggle + write-protection password. */\n fileSharing?: import('./file-sharing').FileSharing;\n /**\n * `<oleSize ref=\"…\">` — bounding range Excel uses when the workbook is\n * embedded as an OLE object inside another Office document.\n */\n oleSize?: string;\n /** `<fileRecoveryPr>` — autoRecover-style flags Excel writes after a recovery save. */\n fileRecoveryPr?: import('./file-recovery').FileRecoveryProperties;\n /**\n * `<pivotCaches>` — links from workbook root to xl/pivotCache parts. The\n * underlying parts survive via the passthrough archive; this typed array\n * preserves the cacheId ↔ rId mapping for consumers that want to introspect\n * the pivot links.\n */\n pivotCaches?: ReadonlyArray<{ cacheId: number; rId: string }>;\n /**\n * `<externalReferences>` — links from workbook root to xl/externalLinks\n * parts. The numeric token in cross-workbook formulas like `[1]Sheet!A1` is\n * the 1-based index into this array. Underlying parts continue via\n * passthrough archive.\n */\n externalReferences?: ReadonlyArray<{ rId: string }>;\n /** `<smartTagPr>` — Excel 2003 smart-tag persistence flags. */\n smartTagPr?: import('./smart-tags').SmartTagProperties;\n /** `<smartTagTypes>` — Excel 2003 smart-tag type registrations. */\n smartTagTypes?: ReadonlyArray<import('./smart-tags').SmartTagType>;\n /** `<functionGroups>` — built-in + user-defined XLL function groups. */\n functionGroups?: import('./function-groups').FunctionGroups;\n /**\n * `<workbookPr>` — VBA codeName, defaultThemeVersion, link-update prompt\n * mode, etc. `date1904` is mirrored here for completeness but the canonical\n * source remains `wb.date1904`.\n */\n workbookProperties?: import('./workbook-properties').WorkbookProperties;\n /**\n * Workbook-level rels that don't match a modeled type. Re-emitted with their\n * original Id so captured `<pivotCaches r:id=\"…\"/>` etc. still resolve after\n * a round-trip.\n */\n workbookRelsExtras?: ReadonlyArray<{ id: string; type: string; target: string }>;\n /**\n * Original rIds for the modeled non-sheet workbook rels so a captured extras\n * XML referencing one of them still resolves after re-save.\n */\n workbookRelOriginalIds?: {\n sharedStrings?: string;\n styles?: string;\n theme?: string;\n vbaProject?: string;\n };\n}\n\n/** Build an empty Workbook ready to host worksheets. */\nexport function createWorkbook(opts?: { date1904?: boolean }): Workbook {\n return {\n sheets: [],\n activeSheetIndex: 0,\n styles: makeStylesheet(),\n date1904: opts?.date1904 ?? false,\n authors: [],\n definedNames: [],\n };\n}\n\n/**\n * Validate a sheet title against Excel's character + length rules. Returns the\n * reason string when the title is rejected; `undefined` when valid. The same\n * rules apply to worksheets and chartsheets.\n *\n * Rules:\n * - Type must be `string`; non-empty; length ≤ 31.\n * - May not contain any of `:`, `\\`, `/`, `?`, `*`, `[`, `]`.\n * - May not start or end with an apostrophe `'`.\n * - May not be the literal `\"History\"` (case-insensitive — Excel\n * reserves that name for the change-tracking sheet).\n *\n * Uniqueness is **not** checked here; pass through `addWorksheet` /\n * `renameSheet` for the workbook-aware duplicate check.\n */\nexport function validateSheetTitle(title: unknown): string | undefined {\n if (typeof title !== 'string') return 'must be a string';\n if (title.length === 0) return 'must be 1..31 chars';\n if (title.length > 31) return 'must be 1..31 chars';\n if (/[:\\\\/?*[\\]]/.test(title)) return 'must not contain : \\\\ / ? * [ ]';\n if (title.startsWith(\"'\") || title.endsWith(\"'\")) return 'must not start or end with an apostrophe';\n if (title.toLowerCase() === 'history') return '\"History\" is reserved by Excel';\n return undefined;\n}\n\n/** Boolean form of {@link validateSheetTitle}. */\nexport const isValidSheetTitle = (title: unknown): title is string => validateSheetTitle(title) === undefined;\n\n/**\n * Pick a unique sheet title based on `base`. If `base` itself is available,\n * it's returned verbatim. Otherwise the helper appends ` (2)`, ` (3)`, … until\n * it finds a free slot. The returned title always satisfies {@link\n * validateSheetTitle} — if the base+suffix would exceed 31 chars, the base is\n * truncated to fit.\n *\n * Excel treats sheet names as case-insensitive for uniqueness, so `Data` and\n * `data` collide; the helper applies the same rule.\n *\n * Useful for \"duplicate sheet\" / \"import\" flows where you want Excel-like\n * automatic uniqueification (\"Sheet1 (2)\").\n */\nexport function pickUniqueSheetTitle(wb: Workbook, base: string): string {\n const reason = validateSheetTitle(base);\n if (reason) {\n throw new OpenXmlSchemaError(`pickUniqueSheetTitle: base \"${base}\" is not a valid sheet title (${reason})`);\n }\n const used = new Set<string>();\n for (const s of wb.sheets) used.add(s.sheet.title.toLowerCase());\n if (!used.has(base.toLowerCase())) return base;\n for (let n = 2; n < 1000; n++) {\n const suffix = ` (${n})`;\n const room = 31 - suffix.length;\n const truncatedBase = base.length > room ? base.slice(0, room) : base;\n const candidate = `${truncatedBase}${suffix}`;\n if (!used.has(candidate.toLowerCase())) return candidate;\n }\n throw new OpenXmlSchemaError(`pickUniqueSheetTitle: exhausted candidates for \"${base}\"`);\n}\n\n// Excel treats sheet names as case-insensitive for uniqueness — \"Data\" and\n// \"data\" cannot co-exist in the same workbook. `ignoreIndex` lets renameSheet\n// pass its own slot so a case-only rename (\"Data\" → \"data\") still succeeds.\nconst validateUniqueTitle = (wb: Workbook, title: string, ignoreIndex?: number): void => {\n const reason = validateSheetTitle(title);\n if (reason) {\n throw new OpenXmlSchemaError(`Worksheet title \"${title}\": ${reason}`);\n }\n const lower = title.toLowerCase();\n for (let i = 0; i < wb.sheets.length; i++) {\n if (i === ignoreIndex) continue;\n const s = wb.sheets[i];\n if (s && s.sheet.title.toLowerCase() === lower) {\n throw new OpenXmlSchemaError(`Worksheet title \"${title}\" is already in use`);\n }\n }\n};\n\nconst allocateSheetId = (wb: Workbook): number => {\n // sheetId is 1-based and unique. Allocate the smallest unused integer.\n const used = new Set<number>();\n for (const s of wb.sheets) used.add(s.sheetId);\n let n = 1;\n while (used.has(n)) n++;\n return n;\n};\n\n/** Add a Worksheet to the Workbook. Returns the sheet for further population. */\nexport function addWorksheet(wb: Workbook, title: string, opts?: { index?: number; state?: SheetState }): Worksheet {\n validateUniqueTitle(wb, title);\n const sheet = makeWorksheet(title);\n const ref: SheetRef = {\n kind: 'worksheet',\n sheet,\n sheetId: allocateSheetId(wb),\n state: opts?.state ?? 'visible',\n };\n if (opts?.index === undefined) {\n wb.sheets.push(ref);\n } else {\n if (opts.index < 0 || opts.index > wb.sheets.length) {\n throw new OpenXmlSchemaError(`addWorksheet: index ${opts.index} out of range`);\n }\n wb.sheets.splice(opts.index, 0, ref);\n }\n return sheet;\n}\n\n/**\n * 0-based tab-strip index of the sheet (worksheet *or* chartsheet) with the\n * given title, or `-1` when not present. Useful when the caller wants to act on\n * the index for `setActiveSheet` / `swapSheets` / similar operations without\n * manually scanning `wb.sheets`.\n */\nexport function getSheetIndex(wb: Workbook, title: string): number {\n for (let i = 0; i < wb.sheets.length; i++) {\n const ref = wb.sheets[i];\n if (ref && ref.sheet.title === title) return i;\n }\n return -1;\n}\n\n/**\n * True iff the workbook has a sheet (worksheet *or* chartsheet) with the given\n * title. Thin shortcut over {@link getSheetIndex}.\n */\nexport function hasSheet(wb: Workbook, title: string): boolean {\n return getSheetIndex(wb, title) >= 0;\n}\n\n/**\n * Count sheets in the workbook, with optional kind/state filters. Mirrors the\n * filter shape of {@link getSheetTitles} but skips the array allocation when\n * the caller only needs the count.\n */\nexport function countSheets(\n wb: Workbook,\n opts: { kind?: 'worksheet' | 'chartsheet'; state?: SheetState } = {},\n): number {\n let n = 0;\n for (const ref of wb.sheets) {\n if (opts.kind !== undefined && ref.kind !== opts.kind) continue;\n if (opts.state !== undefined && ref.state !== opts.state) continue;\n n++;\n }\n return n;\n}\n\n/**\n * Sheet titles in tab-strip order. By default returns titles for every sheet\n * (worksheets + chartsheets). Optional filters narrow to one kind\n * (`'worksheet'` / `'chartsheet'`) or one state (`'visible' | 'hidden' |\n * 'veryHidden'`).\n */\nexport function getSheetTitles(\n wb: Workbook,\n opts: { kind?: 'worksheet' | 'chartsheet'; state?: SheetState } = {},\n): string[] {\n const out: string[] = [];\n for (const ref of wb.sheets) {\n if (opts.kind !== undefined && ref.kind !== opts.kind) continue;\n if (opts.state !== undefined && ref.state !== opts.state) continue;\n out.push(ref.sheet.title);\n }\n return out;\n}\n\n/**\n * True iff the workbook has a **worksheet** (not a chartsheet) with the given\n * title. Distinct from {@link hasSheet} (matches either kind) and {@link\n * hasChartsheet} (chartsheets only).\n */\nexport function hasWorksheet(wb: Workbook, title: string): boolean {\n return getSheet(wb, title) !== undefined;\n}\n\n/**\n * True iff the workbook has a **chartsheet** (not a worksheet) with the given\n * title. Distinct from {@link hasSheet}, which matches either kind. Use this\n * when the caller needs to discriminate before calling chartsheet-only\n * operations.\n */\nexport function hasChartsheet(wb: Workbook, title: string): boolean {\n for (const ref of wb.sheets) {\n if (ref.kind === 'chartsheet' && ref.sheet.title === title) return true;\n }\n return false;\n}\n\n/** Look up a Worksheet by title. Returns undefined for missing names or chartsheets. */\nexport function getSheet(wb: Workbook, title: string): Worksheet | undefined {\n for (const s of wb.sheets) {\n if (s.kind === 'worksheet' && s.sheet.title === title) return s.sheet;\n }\n return undefined;\n}\n\n/** Look up a Worksheet by index in the sheets array. Returns undefined for chartsheet slots. */\nexport function getSheetByIndex(wb: Workbook, idx: number): Worksheet | undefined {\n const ref = wb.sheets[idx];\n return ref?.kind === 'worksheet' ? ref.sheet : undefined;\n}\n\n/** Look up a Chartsheet by title. Returns undefined for missing names or worksheets. */\nexport function getChartsheet(wb: Workbook, title: string): Chartsheet | undefined {\n for (const s of wb.sheets) {\n if (s.kind === 'chartsheet' && s.sheet.title === title) return s.sheet;\n }\n return undefined;\n}\n\n/** Add a Chartsheet to the Workbook. Returns the chartsheet for further population. */\nexport function addChartsheet(\n wb: Workbook,\n title: string,\n opts?: { index?: number; state?: SheetState; chart?: ChartReference },\n): Chartsheet {\n validateUniqueTitle(wb, title);\n const cs = makeChartsheet(title);\n // When the caller supplies a ChartReference, wrap it in a single-anchor\n // drawing so the writer emits xl/drawings/drawingN.xml + chart part.\n if (opts?.chart) {\n // The drawing wraps the chart in a single absoluteAnchor sized to a\n // standard A4-landscape page (Excel re-flows on open if needed).\n cs.drawing = makeDrawing([\n makeChartDrawingItem(makeAbsoluteAnchor({ x: 0, y: 0, cx: 9144000, cy: 6858000 }), opts.chart),\n ]);\n }\n const ref: SheetRef = {\n kind: 'chartsheet',\n sheet: cs,\n sheetId: allocateSheetId(wb),\n state: opts?.state ?? 'visible',\n };\n if (opts?.index === undefined) {\n wb.sheets.push(ref);\n } else {\n if (opts.index < 0 || opts.index > wb.sheets.length) {\n throw new OpenXmlSchemaError(`addChartsheet: index ${opts.index} out of range`);\n }\n wb.sheets.splice(opts.index, 0, ref);\n }\n return cs;\n}\n\n/** All worksheet titles, in display order. */\nexport function sheetNames(wb: Workbook): string[] {\n return wb.sheets.map((s) => s.sheet.title);\n}\n\n/** Remove a sheet by title. No-op if the title is not registered. */\nexport function removeSheet(wb: Workbook, title: string): void {\n const i = wb.sheets.findIndex((s) => s.sheet.title === title);\n if (i < 0) return;\n wb.sheets.splice(i, 1);\n // Clamp activeSheetIndex within bounds.\n if (wb.activeSheetIndex >= wb.sheets.length) {\n wb.activeSheetIndex = Math.max(0, wb.sheets.length - 1);\n }\n}\n\n/** Set the active sheet by title; throws on unknown title. */\nexport function setActiveSheet(wb: Workbook, title: string): void {\n const i = wb.sheets.findIndex((s) => s.sheet.title === title);\n if (i < 0) throw new OpenXmlSchemaError(`setActiveSheet: no sheet named \"${title}\"`);\n wb.activeSheetIndex = i;\n}\n\n/**\n * Rename a sheet from `oldTitle` to `newTitle`. Throws if no sheet matches\n * `oldTitle`, or if `newTitle` collides with an existing sheet (Excel requires\n * sheet names to be unique within a workbook).\n */\nexport function renameSheet(wb: Workbook, oldTitle: string, newTitle: string): void {\n const i = wb.sheets.findIndex((s) => s.sheet.title === oldTitle);\n if (i < 0) throw new OpenXmlSchemaError(`renameSheet: no sheet named \"${oldTitle}\"`);\n if (oldTitle === newTitle) return;\n // Excel allows case-only renames (\"Data\" → \"data\"); ignore the current slot\n // when checking uniqueness so the rename validates against everything else.\n validateUniqueTitle(wb, newTitle, i);\n const ref = wb.sheets[i];\n if (ref) ref.sheet.title = newTitle;\n}\n\n/**\n * Set the visibility state on a sheet by title. Throws on unknown title.\n * Refuses to hide the last visible sheet: an .xlsx with every sheet hidden\n * fails to open in Excel (\"Excel cannot use the object linking and embedding\n * features because no sheet is visible\"). Catching it here keeps the\n * workbook recoverable instead of producing a save Excel will reject.\n */\nexport function setSheetState(wb: Workbook, title: string, state: SheetState): void {\n const ref = wb.sheets.find((s) => s.sheet.title === title);\n if (!ref) throw new OpenXmlSchemaError(`setSheetState: no sheet named \"${title}\"`);\n if (ref.state === state) return;\n if (state !== 'visible' && ref.state === 'visible') {\n let otherVisible = false;\n for (const candidate of wb.sheets) {\n if (candidate !== ref && candidate.state === 'visible') {\n otherVisible = true;\n break;\n }\n }\n if (!otherVisible) {\n throw new OpenXmlSchemaError(\n `setSheetState: cannot hide \"${title}\" — it's the last visible sheet,` +\n ' and Excel refuses to open a workbook with every sheet hidden. Make another sheet' +\n ' visible first (or call showSheet()).',\n );\n }\n }\n ref.state = state;\n}\n\n/** Look up the current visibility state. Throws on unknown title. */\nexport function getSheetState(wb: Workbook, title: string): SheetState {\n const ref = wb.sheets.find((s) => s.sheet.title === title);\n if (!ref) throw new OpenXmlSchemaError(`getSheetState: no sheet named \"${title}\"`);\n return ref.state;\n}\n\n/**\n * Hide a sheet (`state: 'hidden'`). Equivalent to right-click → Hide in Excel —\n * the user can re-show it via the Unhide dialog.\n */\nexport function hideSheet(wb: Workbook, title: string): void {\n setSheetState(wb, title, 'hidden');\n}\n\n/**\n * Mark a sheet as very-hidden (`state: 'veryHidden'`). Excel won't surface it\n * in the Unhide dialog — only reachable via VBA / API.\n */\nexport function veryHideSheet(wb: Workbook, title: string): void {\n setSheetState(wb, title, 'veryHidden');\n}\n\n/** Make a hidden / veryHidden sheet visible. */\nexport function showSheet(wb: Workbook, title: string): void {\n setSheetState(wb, title, 'visible');\n}\n\n/**\n * Bulk-update visibility state for many sheets in one call. `entries` is a\n * `Record<title, state>` map; missing titles throw via the underlying\n * `setSheetState`.\n */\nexport function setSheetStates(wb: Workbook, entries: Record<string, SheetState>): void {\n for (const [title, state] of Object.entries(entries)) {\n setSheetState(wb, title, state);\n }\n}\n\n/**\n * Show every hidden / veryHidden worksheet. Returns the count unhidden. Useful\n * for spreadsheet-wide auditing.\n */\nexport function showAllSheets(wb: Workbook): number {\n let n = 0;\n for (const ref of wb.sheets) {\n if (ref.state !== 'visible') {\n ref.state = 'visible';\n n++;\n }\n }\n return n;\n}\n\n/**\n * Move a sheet to a new tab-strip position. `toIndex` is clamped to `[0,\n * sheets.length - 1]`. Adjusts `activeSheetIndex` so the same sheet stays\n * active across the move.\n */\nexport function moveSheet(wb: Workbook, title: string, toIndex: number): void {\n const from = wb.sheets.findIndex((s) => s.sheet.title === title);\n if (from < 0) throw new OpenXmlSchemaError(`moveSheet: no sheet named \"${title}\"`);\n if (!Number.isInteger(toIndex)) {\n throw new OpenXmlSchemaError(`moveSheet: toIndex must be an integer; got ${toIndex}`);\n }\n const dest = Math.max(0, Math.min(wb.sheets.length - 1, toIndex));\n if (from === dest) return;\n const wasActive = wb.activeSheetIndex === from;\n const [moved] = wb.sheets.splice(from, 1);\n if (moved) wb.sheets.splice(dest, 0, moved);\n if (wasActive) {\n wb.activeSheetIndex = dest;\n } else {\n // Re-index activeSheetIndex if the move shifted it.\n let cur = wb.activeSheetIndex;\n if (from < cur) cur -= 1;\n if (dest <= cur) cur += 1;\n wb.activeSheetIndex = Math.max(0, Math.min(wb.sheets.length - 1, cur));\n }\n}\n\n/**\n * Swap the tab-strip positions of two sheets by title. Both titles must exist;\n * throws otherwise. `activeSheetIndex` follows the moved sheet so the same\n * sheet stays active across the swap.\n */\nexport function swapSheets(wb: Workbook, titleA: string, titleB: string): void {\n const i = wb.sheets.findIndex((s) => s.sheet.title === titleA);\n const j = wb.sheets.findIndex((s) => s.sheet.title === titleB);\n if (i < 0) throw new OpenXmlSchemaError(`swapSheets: no sheet named \"${titleA}\"`);\n if (j < 0) throw new OpenXmlSchemaError(`swapSheets: no sheet named \"${titleB}\"`);\n if (i === j) return;\n const a = wb.sheets[i];\n const b = wb.sheets[j];\n if (!a || !b) return;\n wb.sheets[i] = b;\n wb.sheets[j] = a;\n if (wb.activeSheetIndex === i) wb.activeSheetIndex = j;\n else if (wb.activeSheetIndex === j) wb.activeSheetIndex = i;\n}\n\n/**\n * Duplicate a worksheet end-to-end and append it as `newTitle`. Mirrors Excel's\n * \"Move or Copy → Create a copy\" command. Cells, dimensions, styles (via shared\n * cellXf ids), comments, hyperlinks, conditional formatting, page setup, etc.\n * all carry over verbatim — only fields that must stay workbook-unique get\n * rewritten:\n *\n * - sheet `title` → `newTitle`\n * - sheet `sheetId` → freshly allocated\n * - each table's `id` → max(workbook table ids) + 1\n * - each table's `displayName` → suffixed with `opts.tableSuffix`\n * (default `\"_2\"`) so it doesn't collide with the original\n *\n * The new sheet is inserted at the optional `index` (default: appended).\n */\nexport function duplicateSheet(\n wb: Workbook,\n sourceTitle: string,\n newTitle: string,\n opts: { index?: number; state?: SheetState; tableSuffix?: string } = {},\n): Worksheet {\n validateUniqueTitle(wb, newTitle);\n const sourceRef = wb.sheets.find((s) => s.kind === 'worksheet' && s.sheet.title === sourceTitle);\n if (!sourceRef || sourceRef.kind !== 'worksheet') {\n throw new OpenXmlSchemaError(`duplicateSheet: no worksheet named \"${sourceTitle}\"`);\n }\n const cloned = structuredClone(sourceRef.sheet);\n cloned.title = newTitle;\n\n // Table id + displayName must stay workbook-unique. Walk every other sheet to\n // find the next free id and renumber/rename in place.\n const suffix = opts.tableSuffix ?? '_2';\n let nextTableId = 0;\n const usedDisplayNames = new Set<string>();\n for (const s of wb.sheets) {\n if (s.kind !== 'worksheet') continue;\n for (const t of s.sheet.tables) {\n if (t.id > nextTableId) nextTableId = t.id;\n usedDisplayNames.add(t.displayName);\n }\n }\n for (const t of cloned.tables) {\n nextTableId += 1;\n t.id = nextTableId;\n let candidate = `${t.displayName}${suffix}`;\n let n = 2;\n while (usedDisplayNames.has(candidate)) {\n candidate = `${t.displayName}${suffix}${n}`;\n n += 1;\n }\n t.displayName = candidate;\n if (t.name === undefined) t.name = candidate;\n usedDisplayNames.add(candidate);\n }\n\n const ref: SheetRef = {\n kind: 'worksheet',\n sheet: cloned,\n sheetId: allocateSheetId(wb),\n state: opts.state ?? 'visible',\n };\n if (opts.index === undefined) {\n wb.sheets.push(ref);\n } else {\n if (opts.index < 0 || opts.index > wb.sheets.length) {\n throw new OpenXmlSchemaError(`duplicateSheet: index ${opts.index} out of range`);\n }\n wb.sheets.splice(opts.index, 0, ref);\n }\n return cloned;\n}\n\n/**\n * Aggregate counts about a workbook's content. Useful for quick QA after large\n * mutations or for surfacing a \"what's in this file\" banner. All counts walk\n * the typed model — they do **not** save the workbook to bytes — so the cost is\n * O(workbook content).\n */\nexport interface WorkbookStats {\n /** Total worksheets (excludes chartsheets). */\n worksheetCount: number;\n /** Total chartsheets. */\n chartsheetCount: number;\n /** Sum of populated cells across every worksheet. */\n cellCount: number;\n /** Sum of formula cells. */\n formulaCount: number;\n /** Sum of legacyComments across every worksheet. */\n commentCount: number;\n /** Sum of hyperlinks across every worksheet. */\n hyperlinkCount: number;\n /** Sum of mergedCells ranges. */\n mergedRangeCount: number;\n /** Sum of Excel tables. */\n tableCount: number;\n /** Workbook-level defined names. */\n definedNameCount: number;\n /** Custom-property entry count, 0 when no docProps/custom.xml. */\n customPropertyCount: number;\n}\n\nexport function getWorkbookStats(wb: Workbook): WorkbookStats {\n let worksheetCount = 0;\n let chartsheetCount = 0;\n let cellCount = 0;\n let formulaCount = 0;\n let commentCount = 0;\n let hyperlinkCount = 0;\n let mergedRangeCount = 0;\n let tableCount = 0;\n for (const ref of wb.sheets) {\n if (ref.kind === 'worksheet') {\n worksheetCount++;\n const ws = ref.sheet;\n for (const rowMap of ws.rows.values()) {\n for (const cell of rowMap.values()) {\n cellCount++;\n if (isFormulaValue(cell.value)) formulaCount++;\n }\n }\n commentCount += ws.legacyComments.length;\n hyperlinkCount += ws.hyperlinks.length;\n mergedRangeCount += ws.mergedCells.length;\n tableCount += ws.tables.length;\n } else {\n chartsheetCount++;\n }\n }\n return {\n worksheetCount,\n chartsheetCount,\n cellCount,\n formulaCount,\n commentCount,\n hyperlinkCount,\n mergedRangeCount,\n tableCount,\n definedNameCount: wb.definedNames.length,\n customPropertyCount: wb.customProperties?.properties.length ?? 0,\n };\n}\n\n/**\n * Workbook-wide value-kind histogram. Sums {@link countCellsByKind} across\n * every Worksheet (chartsheets contribute no cells). Buckets have the same\n * shape as the per-worksheet result; an empty workbook returns all-zero counts.\n */\nexport function getWorkbookCellsByKind(wb: Workbook): CellsByKindCounts {\n const out: CellsByKindCounts = {\n null: 0,\n string: 0,\n number: 0,\n boolean: 0,\n date: 0,\n duration: 0,\n error: 0,\n 'rich-text': 0,\n formula: 0,\n };\n for (const ws of iterWorksheets(wb)) {\n const partial = countCellsByKind(ws);\n out.null += partial.null;\n out.string += partial.string;\n out.number += partial.number;\n out.boolean += partial.boolean;\n out.date += partial.date;\n out.duration += partial.duration;\n out.error += partial.error;\n out['rich-text'] += partial['rich-text'];\n out.formula += partial.formula;\n }\n return out;\n}\n\n/**\n * Resolve a sheet-qualified A1 address (`'Sheet1!A1'`) to its Cell, or\n * `undefined` when the cell isn't materialised. Throws on malformed addresses,\n * missing sheets, or range inputs.\n */\nexport function getCellAtAddress(wb: Workbook, address: string): import('../cell/cell').Cell | undefined {\n const { sheet: sheetTitle, range } = parseSheetRange(address);\n if (range.includes(':')) {\n throw new OpenXmlSchemaError(\n `getCellAtAddress: address \"${address}\" refers to a range, not a single cell`,\n );\n }\n const ws = getSheet(wb, sheetTitle);\n if (!ws) {\n throw new OpenXmlSchemaError(`getCellAtAddress: sheet \"${sheetTitle}\" not found`);\n }\n const { col, row } = coordinateToTuple(range);\n return getCell(ws, row, col);\n}\n\n/**\n * Set a single cell by sheet-qualified A1 address. Throws on malformed\n * addresses, missing sheets, or range inputs.\n */\nexport function setCellAtAddress(\n wb: Workbook,\n address: string,\n value: CellValue,\n): import('../cell/cell').Cell {\n const { sheet: sheetTitle, range } = parseSheetRange(address);\n if (range.includes(':')) {\n throw new OpenXmlSchemaError(\n `setCellAtAddress: address \"${address}\" refers to a range, not a single cell`,\n );\n }\n const ws = getSheet(wb, sheetTitle);\n if (!ws) {\n throw new OpenXmlSchemaError(`setCellAtAddress: sheet \"${sheetTitle}\" not found`);\n }\n return setCellByCoord(ws, range, value);\n}\n\n/**\n * True iff every Worksheet in the workbook is empty (per {@link\n * isWorksheetEmpty}). Chartsheets carry no cells so they never affect the\n * result. A workbook with zero worksheets is also empty by this definition.\n *\n * Short-circuits on the first non-empty worksheet.\n */\nexport function isWorkbookEmpty(wb: Workbook): boolean {\n for (const ws of iterWorksheets(wb)) {\n if (!isWorksheetEmpty(ws)) return false;\n }\n return true;\n}\n\n/**\n * Per-sheet entry inside {@link WorkbookOverview}. Holds enough metadata to\n * make a \"what's in this workbook\" panel useful without forcing the caller to\n * walk every worksheet themselves.\n */\nexport interface WorkbookSheetOverview {\n title: string;\n kind: 'worksheet' | 'chartsheet';\n state: SheetState;\n /** Populated cells in the sheet (0 for chartsheets). */\n cellCount: number;\n /** Populated formula cells (0 for chartsheets). */\n formulaCount: number;\n /** Tables registered on the sheet. */\n tableCount: number;\n /** Drawing items (charts + pictures) on the sheet. */\n drawingItemCount: number;\n}\n\n/**\n * High-level \"what's in this workbook\" snapshot. Combines the aggregate counts\n * from {@link getWorkbookStats} and value-kind histogram from {@link\n * getWorkbookCellsByKind} with per-sheet metadata. JSON-serialisable; suitable\n * for a UI banner / debug dump.\n */\nexport interface WorkbookOverview {\n worksheetCount: number;\n chartsheetCount: number;\n cellCount: number;\n formulaCount: number;\n commentCount: number;\n hyperlinkCount: number;\n mergedRangeCount: number;\n tableCount: number;\n definedNameCount: number;\n customPropertyCount: number;\n cellsByKind: CellsByKindCounts;\n sheets: WorkbookSheetOverview[];\n}\n\nexport function describeWorkbook(wb: Workbook): WorkbookOverview {\n // Single-pass aggregation: a cell-by-cell walk dominates this function's\n // cost on million-cell workbooks, so we compute the workbook stats, the\n // value-kind histogram, AND each sheet's cell/formula counts in one sweep\n // instead of triple-scanning.\n let worksheetCount = 0;\n let chartsheetCount = 0;\n let cellCount = 0;\n let formulaCount = 0;\n let commentCount = 0;\n let hyperlinkCount = 0;\n let mergedRangeCount = 0;\n let tableCount = 0;\n const cellsByKind: CellsByKindCounts = {\n null: 0,\n string: 0,\n number: 0,\n boolean: 0,\n date: 0,\n duration: 0,\n error: 0,\n 'rich-text': 0,\n formula: 0,\n };\n const sheets: WorkbookSheetOverview[] = wb.sheets.map((ref) => {\n if (ref.kind === 'chartsheet') {\n chartsheetCount++;\n return {\n title: ref.sheet.title,\n kind: 'chartsheet',\n state: ref.state,\n cellCount: 0,\n formulaCount: 0,\n tableCount: 0,\n drawingItemCount: ref.sheet.drawing?.items.length ?? 0,\n };\n }\n worksheetCount++;\n const ws = ref.sheet;\n let sheetCellCount = 0;\n let sheetFormulaCount = 0;\n for (const rowMap of ws.rows.values()) {\n for (const cell of rowMap.values()) {\n sheetCellCount++;\n const bucket = classifyCellValue(cell.value);\n cellsByKind[bucket]++;\n if (isFormulaValue(cell.value)) sheetFormulaCount++;\n }\n }\n cellCount += sheetCellCount;\n formulaCount += sheetFormulaCount;\n commentCount += ws.legacyComments.length;\n hyperlinkCount += ws.hyperlinks.length;\n mergedRangeCount += ws.mergedCells.length;\n tableCount += ws.tables.length;\n return {\n title: ws.title,\n kind: 'worksheet',\n state: ref.state,\n cellCount: sheetCellCount,\n formulaCount: sheetFormulaCount,\n tableCount: ws.tables.length,\n drawingItemCount: ws.drawing?.items.length ?? 0,\n };\n });\n return {\n worksheetCount,\n chartsheetCount,\n cellCount,\n formulaCount,\n commentCount,\n hyperlinkCount,\n mergedRangeCount,\n tableCount,\n definedNameCount: wb.definedNames.length,\n customPropertyCount: wb.customProperties?.properties.length ?? 0,\n cellsByKind,\n sheets,\n };\n}\n\n/**\n * Debug-friendly snapshot of everything resolved for a single cell: its value,\n * the full style chain (font / fill / border / alignment / protection /\n * numberFormat), the applied hyperlink + comment, the merged range it sits\n * inside (if any), and the names of any tables / the count of CF / DV blocks\n * that target it.\n *\n * Designed for `console.log`-style introspection — JSON-serialisable and stable\n * in shape regardless of which axes are populated.\n *\n * Throws when `sheetTitle` doesn't resolve. When `ref` is a valid A1 coordinate\n * but no cell exists there, `exists` is `false` and the style chain reflects\n * the workbook defaults.\n */\nexport interface CellSummary {\n ref: string;\n sheet: string;\n exists: boolean;\n value: CellValue | undefined;\n styleId: number;\n font: Font;\n fill: Fill;\n border: Border;\n alignment: Alignment;\n protection: Protection;\n numberFormat: string;\n hyperlink: Hyperlink | undefined;\n comment: LegacyComment | undefined;\n mergedRange: string | undefined;\n inTables: string[];\n inDataValidations: number;\n inConditionalFormatting: number;\n}\n\nexport function getCellSummary(wb: Workbook, sheetTitle: string, ref: string): CellSummary {\n const ws = getSheet(wb, sheetTitle);\n if (!ws) throw new OpenXmlSchemaError(`getCellSummary: sheet \"${sheetTitle}\" not found`);\n const { col, row } = coordinateToTuple(ref);\n const cell = getCell(ws, row, col);\n // Synthesize a placeholder cell so getCell* helpers can resolve defaults even\n // for unmaterialised coordinates.\n const probe = cell ?? { row, col, value: null, styleId: 0 };\n const merged = getMergedRangeAt(ws, row, col);\n const inTables: string[] = [];\n for (const t of ws.tables) {\n if (rangeContainsCell(parseRange(t.ref), row, col)) inTables.push(t.displayName);\n }\n let inDv = 0;\n for (const dv of ws.dataValidations) {\n if (multiCellRangeContainsCell(dv.sqref, row, col)) inDv++;\n }\n let inCf = 0;\n for (const cf of ws.conditionalFormatting) {\n if (multiCellRangeContainsCell(cf.sqref, row, col)) inCf++;\n }\n return {\n ref,\n sheet: sheetTitle,\n exists: cell !== undefined,\n value: cell?.value,\n styleId: probe.styleId,\n font: getCellFont(wb, probe),\n fill: getCellFill(wb, probe),\n border: getCellBorder(wb, probe),\n alignment: getCellAlignment(wb, probe),\n protection: getCellProtection(wb, probe),\n numberFormat: getCellNumberFormat(wb, probe),\n hyperlink: cell ? getCellHyperlink(ws, cell) : undefined,\n comment: cell ? getCellComment(ws, cell) : undefined,\n mergedRange: merged ? rangeToString(merged) : undefined,\n inTables,\n inDataValidations: inDv,\n inConditionalFormatting: inCf,\n };\n}\n\n/**\n * Iterate over every Worksheet in the workbook (skips chartsheets). Yields each\n * worksheet in tab-strip order.\n */\nexport function* iterWorksheets(wb: Workbook): IterableIterator<Worksheet> {\n for (const ref of wb.sheets) {\n if (ref.kind === 'worksheet') yield ref.sheet;\n }\n}\n\n/**\n * Iterate only over Worksheets whose tab-strip state is `'visible'`. Hidden /\n * veryHidden sheets are skipped. Useful for reports that should ignore\n * back-office sheets the author has hidden.\n */\nexport function* iterVisibleWorksheets(wb: Workbook): IterableIterator<Worksheet> {\n for (const ref of wb.sheets) {\n if (ref.kind === 'worksheet' && ref.state === 'visible') yield ref.sheet;\n }\n}\n\n/**\n * Iterate Worksheets matching the supplied state. Pass `'hidden'` to skim\n * back-office sheets, `'veryHidden'` to find sheets only accessible via VBA,\n * etc.\n */\nexport function* iterWorksheetsByState(\n wb: Workbook,\n state: SheetState,\n): IterableIterator<Worksheet> {\n for (const ref of wb.sheets) {\n if (ref.kind === 'worksheet' && ref.state === state) yield ref.sheet;\n }\n}\n\n/**\n * Iterate every cell across every worksheet in the workbook. Yields `{ sheet,\n * cell }` pairs in tab-strip order, then row-then-column within each sheet.\n * Useful for workbook-wide audits / find-and-replace passes.\n */\nexport function* iterAllCells(\n wb: Workbook,\n): IterableIterator<{ sheet: Worksheet; cell: import('../cell/cell').Cell }> {\n for (const sheet of iterWorksheets(wb)) {\n const rowKeys = [...sheet.rows.keys()].sort((a, b) => a - b);\n for (const r of rowKeys) {\n const rowMap = sheet.rows.get(r);\n if (!rowMap) continue;\n const cols = [...rowMap.keys()].sort((a, b) => a - b);\n for (const c of cols) {\n const cell = rowMap.get(c);\n if (cell !== undefined) yield { sheet, cell };\n }\n }\n }\n}\n\n/**\n * Collect every merged range across every worksheet. Each entry carries the\n * merge bounds plus a back-reference to the owning sheet, in tab-strip order.\n * Equivalent to walking `iterWorksheets` and concatenating each sheet's\n * `mergedCells`.\n */\nexport function getAllMergedRanges(\n wb: Workbook,\n): ReadonlyArray<{ sheet: Worksheet; range: import('../worksheet/cell-range').CellRange }> {\n const out: Array<{ sheet: Worksheet; range: import('../worksheet/cell-range').CellRange }> = [];\n for (const sheet of iterWorksheets(wb)) {\n for (const range of sheet.mergedCells) out.push({ sheet, range });\n }\n return out;\n}\n\n/**\n * Collect every hyperlink across every worksheet. Each entry pairs the\n * hyperlink with a back-reference to the owning sheet, in tab-strip order.\n */\nexport function getAllHyperlinks(\n wb: Workbook,\n): ReadonlyArray<{ sheet: Worksheet; hyperlink: import('../worksheet/hyperlinks').Hyperlink }> {\n const out: Array<{ sheet: Worksheet; hyperlink: import('../worksheet/hyperlinks').Hyperlink }> = [];\n for (const sheet of iterWorksheets(wb)) {\n for (const h of sheet.hyperlinks) out.push({ sheet, hyperlink: h });\n }\n return out;\n}\n\n/**\n * Collect every legacy comment across every worksheet. Each entry pairs the\n * comment with a back-reference to the owning sheet, in tab-strip order.\n */\nexport function getAllComments(\n wb: Workbook,\n): ReadonlyArray<{ sheet: Worksheet; comment: import('../worksheet/comments').LegacyComment }> {\n const out: Array<{ sheet: Worksheet; comment: import('../worksheet/comments').LegacyComment }> = [];\n for (const sheet of iterWorksheets(wb)) {\n for (const c of sheet.legacyComments) out.push({ sheet, comment: c });\n }\n return out;\n}\n\n/**\n * Collect every Excel table across every worksheet. Each entry pairs the\n * TableDefinition with a back-reference to the owning sheet, in tab-strip\n * order.\n */\nexport function getAllTables(\n wb: Workbook,\n): ReadonlyArray<{ sheet: Worksheet; table: import('../worksheet/table').TableDefinition }> {\n const out: Array<{ sheet: Worksheet; table: import('../worksheet/table').TableDefinition }> = [];\n for (const sheet of iterWorksheets(wb)) {\n for (const t of sheet.tables) out.push({ sheet, table: t });\n }\n return out;\n}\n\n/**\n * Locate an Excel table by `displayName` across the whole workbook. Excel\n * enforces uniqueness at the workbook level, so the first match wins. Returns\n * the owning sheet + the table itself, or `undefined` when nothing matches.\n */\nexport function findTable(\n wb: Workbook,\n displayName: string,\n): { sheet: Worksheet; table: import('../worksheet/table').TableDefinition } | undefined {\n for (const sheet of iterWorksheets(wb)) {\n for (const t of sheet.tables) {\n if (t.displayName === displayName) return { sheet, table: t };\n }\n }\n return undefined;\n}\n\n/**\n * First cell across the workbook satisfying `predicate`. Walks every worksheet\n * in tab-strip order, then row-then-column within each sheet (same order as\n * {@link iterAllCells}). Returns `{ sheet, cell }` for the match, or\n * `undefined` when nothing matches.\n */\nexport function findCellInWorkbook(\n wb: Workbook,\n predicate: (cell: import('../cell/cell').Cell, sheet: Worksheet) => boolean,\n): { sheet: Worksheet; cell: import('../cell/cell').Cell } | undefined {\n for (const { sheet, cell } of iterAllCells(wb)) {\n if (predicate(cell, sheet)) return { sheet, cell };\n }\n return undefined;\n}\n\n/**\n * Every cell across the workbook satisfying `predicate`. Same iteration order\n * as {@link iterAllCells}. Returns an array of `{ sheet, cell }` matches.\n */\nexport function findCellsInWorkbook(\n wb: Workbook,\n predicate: (cell: import('../cell/cell').Cell, sheet: Worksheet) => boolean,\n): ReadonlyArray<{ sheet: Worksheet; cell: import('../cell/cell').Cell }> {\n const out: Array<{ sheet: Worksheet; cell: import('../cell/cell').Cell }> = [];\n for (const { sheet, cell } of iterAllCells(wb)) {\n if (predicate(cell, sheet)) out.push({ sheet, cell });\n }\n return out;\n}\n\n/**\n * Workbook-wide find-and-replace. Same matching rule as `replaceCellValues` but\n * walks every worksheet via {@link iterAllCells}. `search` is either an\n * exact-string match (string-valued cells only) or a predicate `(value, cell,\n * sheet) → boolean`. `replacement` is the new `CellValue`. Returns the count of\n * cells changed across all sheets.\n */\nexport function replaceCellValuesInWorkbook(\n wb: Workbook,\n search:\n | string\n | ((value: import('../cell/cell').CellValue, cell: import('../cell/cell').Cell, sheet: Worksheet) => boolean),\n replacement: import('../cell/cell').CellValue,\n): number {\n let n = 0;\n const matchFn =\n typeof search === 'string'\n ? (v: import('../cell/cell').CellValue) => typeof v === 'string' && v === search\n : (v: import('../cell/cell').CellValue, c: import('../cell/cell').Cell, s: Worksheet) => search(v, c, s);\n for (const { sheet, cell } of iterAllCells(wb)) {\n if (matchFn(cell.value, cell, sheet)) {\n cell.value = replacement;\n n++;\n }\n }\n return n;\n}\n\n/**\n * Collect every data-validation block across every worksheet. Each entry pairs\n * the validation with a back-reference to the owning sheet, in tab-strip order.\n */\nexport function getAllDataValidations(\n wb: Workbook,\n): ReadonlyArray<{ sheet: Worksheet; validation: import('../worksheet/data-validations').DataValidation }> {\n const out: Array<{\n sheet: Worksheet;\n validation: import('../worksheet/data-validations').DataValidation;\n }> = [];\n for (const sheet of iterWorksheets(wb)) {\n for (const v of sheet.dataValidations) out.push({ sheet, validation: v });\n }\n return out;\n}\n\n/**\n * Collect every image (picture) DrawingItem across every worksheet, each paired\n * with its owning sheet in tab-strip order.\n */\nexport function getAllImages(\n wb: Workbook,\n): ReadonlyArray<{ sheet: Worksheet; item: import('../drawing/drawing').DrawingItem }> {\n const out: Array<{ sheet: Worksheet; item: import('../drawing/drawing').DrawingItem }> = [];\n for (const sheet of iterWorksheets(wb)) {\n if (!sheet.drawing) continue;\n for (const item of sheet.drawing.items) {\n if (item.content.kind === 'picture') out.push({ sheet, item });\n }\n }\n return out;\n}\n\n/**\n * Collect every chart DrawingItem across every worksheet, each paired with its\n * owning sheet in tab-strip order.\n */\nexport function getAllCharts(\n wb: Workbook,\n): ReadonlyArray<{ sheet: Worksheet; item: import('../drawing/drawing').DrawingItem }> {\n const out: Array<{ sheet: Worksheet; item: import('../drawing/drawing').DrawingItem }> = [];\n for (const sheet of iterWorksheets(wb)) {\n if (!sheet.drawing) continue;\n for (const item of sheet.drawing.items) {\n if (item.content.kind === 'chart') out.push({ sheet, item });\n }\n }\n return out;\n}\n\n/**\n * Collect every conditional-formatting block across every worksheet. Each entry\n * pairs the CF block with a back-reference to the owning sheet, in tab-strip\n * order.\n */\nexport function getAllConditionalFormatting(\n wb: Workbook,\n): ReadonlyArray<{\n sheet: Worksheet;\n formatting: import('../worksheet/conditional-formatting').ConditionalFormatting;\n}> {\n const out: Array<{\n sheet: Worksheet;\n formatting: import('../worksheet/conditional-formatting').ConditionalFormatting;\n }> = [];\n for (const sheet of iterWorksheets(wb)) {\n for (const cf of sheet.conditionalFormatting) out.push({ sheet, formatting: cf });\n }\n return out;\n}\n\n/**\n * Iterate over every Chartsheet in the workbook. Yields in tab-strip order,\n * skipping regular worksheets.\n */\nexport function* iterChartsheets(wb: Workbook): IterableIterator<Chartsheet> {\n for (const ref of wb.sheets) {\n if (ref.kind === 'chartsheet') yield ref.sheet;\n }\n}\n\n/** Convenience: array of every Worksheet in tab-strip order. */\nexport function listWorksheets(wb: Workbook): Worksheet[] {\n return [...iterWorksheets(wb)];\n}\n\n/** Convenience: array of every Chartsheet in tab-strip order. */\nexport function listChartsheets(wb: Workbook): Chartsheet[] {\n return [...iterChartsheets(wb)];\n}\n\n/** Currently active sheet (worksheet only), or undefined if the active slot is empty or a chartsheet. */\nexport function getActiveSheet(wb: Workbook): Worksheet | undefined {\n const ref = wb.sheets[wb.activeSheetIndex];\n return ref?.kind === 'worksheet' ? ref.sheet : undefined;\n}\n\n/**\n * Title of whichever sheet (worksheet *or* chartsheet) is currently marked\n * active via `wb.activeSheetIndex`. Returns `undefined` for an empty workbook\n * or an out-of-range index.\n *\n * Distinct from {@link getActiveSheet} (which only returns worksheets and\n * yields `undefined` when the active slot is a chartsheet) — this matches\n * `wb.activeSheetIndex` regardless of kind.\n */\nexport function getActiveSheetTitle(wb: Workbook): string | undefined {\n return wb.sheets[wb.activeSheetIndex]?.sheet.title;\n}\n\n/**\n * True iff `title` matches the workbook's currently active sheet (any kind).\n * Empty workbook returns `false` (no active sheet).\n */\nexport function isActiveSheet(wb: Workbook, title: string): boolean {\n return getActiveSheetTitle(wb) === title;\n}\n\n/** Read-only view onto the customXml/* pass-through parts. */\nexport function listCustomXmlParts(wb: Workbook): Array<{ path: string; content: Uint8Array }> {\n if (!wb.passthrough) return [];\n const out: Array<{ path: string; content: Uint8Array }> = [];\n for (const [path, content] of wb.passthrough) {\n if (path.startsWith('customXml/')) out.push({ path, content });\n }\n return out;\n}\n\n/**\n * JSON.stringify replacer that drops the Stylesheet's internal dedup Maps. Use\n * as `JSON.stringify(workbook, jsonReplacer)` when the workbook needs to\n * round-trip through plain JSON (tests, debug dumps). The dedup maps are\n * reconstructed lazily on first add.\n */\nexport function jsonReplacer(_key: string, value: unknown): unknown {\n if (value instanceof Map) {\n return { __map__: [...value.entries()] };\n }\n return value;\n}\n\n/** Companion reviver for {@link jsonReplacer}. */\nexport function jsonReviver(_key: string, value: unknown): unknown {\n if (typeof value === 'object' && value !== null && Array.isArray((value as { __map__?: unknown[] }).__map__)) {\n return new Map((value as { __map__: Array<[unknown, unknown]> }).__map__);\n }\n return value;\n}\n"],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ import { C as addSharedString, E as makeSharedStrings, T as getSharedStringIndex, _ as renameSheet, a as getActiveSheet, b as setSheetState, c as getChartsheet, d as getWorkbookCellsByKind, f as getWorkbookStats, g as removeSheet, h as moveSheet, i as describeWorkbook, k as sharedStringCount, l as getSheet, m as listCustomXmlParts, n as addWorksheet, o as getCellAtAddress, p as iterWorksheets, r as createWorkbook, s as getCellSummary, t as addChartsheet, u as getSheetState, v as setActiveSheet, w as getSharedStringAt, x as sheetNames, y as setCellAtAddress } from "./workbook-HGYNRBlV.mjs";
2
+ import { a as makeDefinedName, i as listDefinedNames, n as getDefinedName, o as removeDefinedName, r as getDefinedNameTarget, t as addDefinedName } from "./defined-names-CviWmtQg.mjs";
3
+ //#region src/workbook/protection.ts
4
+ const makeWorkbookProtection = (opts = {}) => {
5
+ const out = {};
6
+ for (const k of [
7
+ "workbookPassword",
8
+ "workbookPasswordCharacterSet",
9
+ "workbookAlgorithmName",
10
+ "workbookHashValue",
11
+ "workbookSaltValue",
12
+ "revisionsPassword",
13
+ "revisionsPasswordCharacterSet",
14
+ "revisionsAlgorithmName",
15
+ "revisionsHashValue",
16
+ "revisionsSaltValue"
17
+ ]) if (opts[k] !== void 0) out[k] = opts[k];
18
+ if (opts.workbookSpinCount !== void 0) out.workbookSpinCount = opts.workbookSpinCount;
19
+ if (opts.revisionsSpinCount !== void 0) out.revisionsSpinCount = opts.revisionsSpinCount;
20
+ if (opts.lockStructure !== void 0) out.lockStructure = opts.lockStructure;
21
+ if (opts.lockWindows !== void 0) out.lockWindows = opts.lockWindows;
22
+ if (opts.lockRevision !== void 0) out.lockRevision = opts.lockRevision;
23
+ return out;
24
+ };
25
+ //#endregion
26
+ //#region src/workbook/views.ts
27
+ const makeWorkbookView = (opts = {}) => ({ ...opts });
28
+ const makeCustomWorkbookView = (opts) => ({ ...opts });
29
+ //#endregion
30
+ //#region src/workbook/calc-properties.ts
31
+ const makeCalcProperties = (opts = {}) => ({ ...opts });
32
+ //#endregion
33
+ //#region src/workbook/workbook-properties.ts
34
+ const makeWorkbookProperties = (opts = {}) => ({ ...opts });
35
+ //#endregion
36
+ //#region src/workbook/file-version.ts
37
+ const makeFileVersion = (opts = {}) => ({ ...opts });
38
+ //#endregion
39
+ //#region src/workbook/file-sharing.ts
40
+ const makeFileSharing = (opts = {}) => ({ ...opts });
41
+ //#endregion
42
+ //#region src/workbook/file-recovery.ts
43
+ const makeFileRecoveryProperties = (opts = {}) => ({ ...opts });
44
+ //#endregion
45
+ //#region src/workbook/smart-tags.ts
46
+ const makeSmartTagProperties = (opts = {}) => ({ ...opts });
47
+ const makeSmartTagType = (opts = {}) => ({ ...opts });
48
+ //#endregion
49
+ //#region src/workbook/function-groups.ts
50
+ const makeFunctionGroup = (name) => ({ name });
51
+ const makeFunctionGroups = (opts = {}) => ({
52
+ groups: opts.groups?.slice() ?? [],
53
+ ...opts.builtInGroupCount !== void 0 ? { builtInGroupCount: opts.builtInGroupCount } : {}
54
+ });
55
+ //#endregion
56
+ export { addChartsheet, addDefinedName, addSharedString, addWorksheet, createWorkbook, describeWorkbook, getActiveSheet, getCellAtAddress, getCellSummary, getChartsheet, getDefinedName, getDefinedNameTarget, getSharedStringAt, getSharedStringIndex, getSheet, getSheetState, getWorkbookCellsByKind, getWorkbookStats, iterWorksheets, listCustomXmlParts, listDefinedNames, makeCalcProperties, makeCustomWorkbookView, makeDefinedName, makeFileRecoveryProperties, makeFileSharing, makeFileVersion, makeFunctionGroup, makeFunctionGroups, makeSharedStrings, makeSmartTagProperties, makeSmartTagType, makeWorkbookProperties, makeWorkbookProtection, makeWorkbookView, moveSheet, removeDefinedName, removeSheet, renameSheet, setActiveSheet, setCellAtAddress, setSheetState, sharedStringCount, sheetNames };
57
+
58
+ //# sourceMappingURL=workbook.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workbook.mjs","names":[],"sources":["../src/workbook/protection.ts","../src/workbook/views.ts","../src/workbook/calc-properties.ts","../src/workbook/workbook-properties.ts","../src/workbook/file-version.ts","../src/workbook/file-sharing.ts","../src/workbook/file-recovery.ts","../src/workbook/smart-tags.ts","../src/workbook/function-groups.ts"],"sourcesContent":["// Workbook-level <workbookProtection>. Per ECMA-376 §18.2.29 and (workbook\n// side).\n//\n// Two parallel password-hash quadruples cover Excel's \"Protect Workbook\"\n// dialog: one set locks structure / window resize, the other locks the\n// revision-tracking history. The wire-form attrs round-trip verbatim —\n// computing a fresh hash from a plaintext password lives behind a future helper\n// (D-tier in the roadmap).\n//\n// Note: the non-hash `workbookPassword` / `revisionsPassword` attrs are the\n// legacy Excel 97/2000 hex hash form. Modern files use the `*HashValue` +\n// `*SaltValue` + `*SpinCount` + `*AlgorithmName` quad instead.\n\nexport interface WorkbookProtection {\n /** Legacy 16-bit hex hash of the workbook password (\"CC1A\" etc.). */\n workbookPassword?: string;\n workbookPasswordCharacterSet?: string;\n workbookAlgorithmName?: string;\n workbookHashValue?: string;\n workbookSaltValue?: string;\n workbookSpinCount?: number;\n /** Legacy 16-bit hex hash of the revisions-tracking password. */\n revisionsPassword?: string;\n revisionsPasswordCharacterSet?: string;\n revisionsAlgorithmName?: string;\n revisionsHashValue?: string;\n revisionsSaltValue?: string;\n revisionsSpinCount?: number;\n /** Lock add/delete/move/rename/hide of sheets. */\n lockStructure?: boolean;\n /** Lock the workbook window size and position. */\n lockWindows?: boolean;\n /** Lock revision tracking — enabled with the \"Track Changes\" feature. */\n lockRevision?: boolean;\n}\n\nexport const makeWorkbookProtection = (opts: WorkbookProtection = {}): WorkbookProtection => {\n const out: WorkbookProtection = {};\n for (const k of [\n 'workbookPassword',\n 'workbookPasswordCharacterSet',\n 'workbookAlgorithmName',\n 'workbookHashValue',\n 'workbookSaltValue',\n 'revisionsPassword',\n 'revisionsPasswordCharacterSet',\n 'revisionsAlgorithmName',\n 'revisionsHashValue',\n 'revisionsSaltValue',\n ] as const) {\n if (opts[k] !== undefined) out[k] = opts[k];\n }\n if (opts.workbookSpinCount !== undefined) out.workbookSpinCount = opts.workbookSpinCount;\n if (opts.revisionsSpinCount !== undefined) out.revisionsSpinCount = opts.revisionsSpinCount;\n if (opts.lockStructure !== undefined) out.lockStructure = opts.lockStructure;\n if (opts.lockWindows !== undefined) out.lockWindows = opts.lockWindows;\n if (opts.lockRevision !== undefined) out.lockRevision = opts.lockRevision;\n return out;\n};\n\n// ---- Workbook ergonomic helpers -----------------------------------------\n\nimport type { Workbook } from './workbook';\n\n/**\n * Lock the workbook with Excel's \"Protect Workbook → Structure\" default\n * (lockStructure=true). Pass `overrides` to also lock windows /\n * revision-tracking, or to attach a password-hash quad. Plaintext password\n * support is deferred until the D-tier hashing helper lands.\n */\nexport const protectWorkbook = (\n wb: Workbook,\n overrides: Partial<WorkbookProtection> = {},\n): WorkbookProtection => {\n wb.workbookProtection = { lockStructure: true, ...overrides };\n return wb.workbookProtection;\n};\n\n/** Drop the workbook-protection record entirely. */\nexport const unprotectWorkbook = (wb: Workbook): void => {\n delete (wb as { workbookProtection?: WorkbookProtection }).workbookProtection;\n};\n\n/** True iff `lockStructure === true`. */\nexport const isWorkbookProtected = (wb: Workbook): boolean =>\n wb.workbookProtection?.lockStructure === true;\n","// Workbook-level <bookViews> typed model. Per ECMA-376 §18.2.30 and\n// openpyxl/openpyxl/workbook/views.py.\n//\n// `<bookViews>` carries one or more `<workbookView>` entries. The\n// first entry drives Excel's default tab strip (firstSheet / activeTab)\n// and window position. Most workbooks have exactly one entry.\n\nexport type WorkbookViewVisibility = 'visible' | 'hidden' | 'veryHidden';\n\nexport interface WorkbookView {\n visibility?: WorkbookViewVisibility;\n minimized?: boolean;\n showHorizontalScroll?: boolean;\n showVerticalScroll?: boolean;\n showSheetTabs?: boolean;\n /** Window x position in screen pixels — Excel restores it when re-opening. */\n xWindow?: number;\n /** Window y position. */\n yWindow?: number;\n windowWidth?: number;\n windowHeight?: number;\n /** Width of the sheet tab strip relative to the horizontal scroll bar (0..1000, default 600). */\n tabRatio?: number;\n /** Index of the leftmost visible sheet tab (0-based). */\n firstSheet?: number;\n /** Index of the currently active sheet tab (0-based). */\n activeTab?: number;\n autoFilterDateGrouping?: boolean;\n}\n\nexport const makeWorkbookView = (opts: WorkbookView = {}): WorkbookView => ({ ...opts });\n\nexport type CustomViewShowComments = 'commNone' | 'commIndicator' | 'commIndAndComment';\nexport type CustomViewShowObjects = 'all' | 'placeholders' | 'none';\n\nexport interface CustomWorkbookView {\n name: string;\n guid: string;\n /** Window width in screen pixels — required when the element is present. */\n windowWidth: number;\n windowHeight: number;\n /** Index (0-based) of the sheet active in this saved view. */\n activeSheetId: number;\n autoUpdate?: boolean;\n /** Auto-merge interval in minutes (for shared workbooks). */\n mergeInterval?: number;\n changesSavedWin?: boolean;\n onlySync?: boolean;\n personalView?: boolean;\n includePrintSettings?: boolean;\n includeHiddenRowCol?: boolean;\n maximized?: boolean;\n minimized?: boolean;\n showHorizontalScroll?: boolean;\n showVerticalScroll?: boolean;\n showSheetTabs?: boolean;\n xWindow?: number;\n yWindow?: number;\n tabRatio?: number;\n showFormulaBar?: boolean;\n showStatusbar?: boolean;\n showComments?: CustomViewShowComments;\n showObjects?: CustomViewShowObjects;\n}\n\nexport const makeCustomWorkbookView = (\n opts: Pick<CustomWorkbookView, 'name' | 'guid' | 'windowWidth' | 'windowHeight' | 'activeSheetId'> &\n Partial<CustomWorkbookView>,\n): CustomWorkbookView => ({ ...opts });\n\nimport type { Workbook } from './workbook';\n\n/**\n * Get-or-create the primary `<workbookView>` entry. Most workbooks have\n * exactly one `<workbookView>`; this helper is the right place to hang\n * tab-strip / window state edits without forcing the caller to allocate\n * the array themselves.\n */\nconst ensurePrimaryView = (wb: Workbook): WorkbookView => {\n const existing = wb.bookViews?.[0];\n if (existing) return existing;\n const fresh = makeWorkbookView();\n wb.bookViews = [fresh];\n return fresh;\n};\n\n/** Get the index of the active sheet tab (0-based) from the primary workbookView, or 0 if unset. */\nexport const getActiveTab = (wb: Workbook): number => wb.bookViews?.[0]?.activeTab ?? 0;\n\n/** Set the active sheet tab (0-based) on the primary workbookView. */\nexport const setActiveTab = (wb: Workbook, index: number): void => {\n ensurePrimaryView(wb).activeTab = index;\n};\n\n/** Get the index of the leftmost visible sheet tab from the primary workbookView, or 0 if unset. */\nexport const getFirstSheet = (wb: Workbook): number => wb.bookViews?.[0]?.firstSheet ?? 0;\n\n/** Set the leftmost visible sheet tab on the primary workbookView. */\nexport const setFirstSheet = (wb: Workbook, index: number): void => {\n ensurePrimaryView(wb).firstSheet = index;\n};\n\n/** Set the tab strip width ratio (0..1000, Excel default 600). */\nexport const setTabRatio = (wb: Workbook, ratio: number): void => {\n ensurePrimaryView(wb).tabRatio = ratio;\n};\n\n/** Toggle the sheet tab strip visibility. */\nexport const setShowSheetTabs = (wb: Workbook, show: boolean): void => {\n ensurePrimaryView(wb).showSheetTabs = show;\n};\n\n/** Toggle the horizontal scroll bar in Excel's window chrome. */\nexport const setShowHorizontalScroll = (wb: Workbook, show: boolean): void => {\n ensurePrimaryView(wb).showHorizontalScroll = show;\n};\n\n/** Toggle the vertical scroll bar in Excel's window chrome. */\nexport const setShowVerticalScroll = (wb: Workbook, show: boolean): void => {\n ensurePrimaryView(wb).showVerticalScroll = show;\n};\n\n/**\n * Toggle the workbook window minimised state. Excel honours this on\n * reopen so the file restores into the minimised state it was saved\n * with.\n */\nexport const setWorkbookMinimized = (wb: Workbook, minimized: boolean): void => {\n ensurePrimaryView(wb).minimized = minimized;\n};\n\n/** Set the visibility of the workbook window itself ('visible' / 'hidden' / 'veryHidden'). */\nexport const setWorkbookVisibility = (wb: Workbook, visibility: WorkbookViewVisibility): void => {\n ensurePrimaryView(wb).visibility = visibility;\n};\n\n/**\n * Set window position + size on the primary workbookView in one call.\n * Pass `undefined` for any axis to leave it untouched.\n */\nexport const setWorkbookWindow = (\n wb: Workbook,\n opts: { xWindow?: number; yWindow?: number; windowWidth?: number; windowHeight?: number },\n): void => {\n const v = ensurePrimaryView(wb);\n if (opts.xWindow !== undefined) v.xWindow = opts.xWindow;\n if (opts.yWindow !== undefined) v.yWindow = opts.yWindow;\n if (opts.windowWidth !== undefined) v.windowWidth = opts.windowWidth;\n if (opts.windowHeight !== undefined) v.windowHeight = opts.windowHeight;\n};\n","// Workbook-level <calcPr>. Per ECMA-376 §18.2.2.\n//\n// `calcId` is a build identifier Excel uses to decide whether to force\n// a recalc when re-opening; modern Excel treats any value as \"use\n// what's there\". The other attrs cover Excel's calculation options\n// (Tools → Options → Formulas).\n\nexport type CalcMode = 'manual' | 'auto' | 'autoNoTable';\nexport type RefMode = 'A1' | 'R1C1';\n\nexport interface CalcProperties {\n /** Excel build/calc-engine identifier; OpenOffice sets 191621, Excel 2016+ 162913 etc. */\n calcId?: number;\n calcMode?: CalcMode;\n /** Force a full recalc when the workbook is loaded. Excel default `true` for safety. */\n fullCalcOnLoad?: boolean;\n refMode?: RefMode;\n /** Allow circular references via iterative calculation. */\n iterate?: boolean;\n iterateCount?: number;\n iterateDelta?: number;\n /** Use full 15-digit precision (vs. displayed precision). */\n fullPrecision?: boolean;\n calcCompleted?: boolean;\n /** Run a recalc on save. */\n calcOnSave?: boolean;\n /** Use multi-threaded calculation. */\n concurrentCalc?: boolean;\n concurrentManualCount?: number;\n /** Force a full recalc on next interaction. */\n forceFullCalc?: boolean;\n}\n\nexport const makeCalcProperties = (opts: CalcProperties = {}): CalcProperties => ({ ...opts });\n\n// ---- Workbook ergonomic helpers ----------------------------------------\n\nimport type { Workbook } from './workbook';\n\nconst ensureCalcProperties = (wb: Workbook): CalcProperties => {\n if (!wb.calcProperties) wb.calcProperties = {};\n return wb.calcProperties;\n};\n\n/**\n * Set the recalculation mode. `'auto'` is Excel's default;\n * `'manual'` requires F9 to recompute formulas; `'autoNoTable'`\n * recomputes everything except data-table cells.\n */\nexport const setCalcMode = (wb: Workbook, mode: CalcMode): void => {\n ensureCalcProperties(wb).calcMode = mode;\n};\n\n/**\n * Toggle iterative calculation (Excel's \"Enable iterative calculation\"\n * option). When `enable` is true and `count` / `delta` are provided,\n * they replace the default Excel limits (100 iterations, 0.001 delta).\n */\nexport const setIterativeCalc = (\n wb: Workbook,\n enable: boolean,\n opts: { count?: number; delta?: number } = {},\n): void => {\n const calc = ensureCalcProperties(wb);\n calc.iterate = enable;\n if (opts.count !== undefined) calc.iterateCount = opts.count;\n if (opts.delta !== undefined) calc.iterateDelta = opts.delta;\n};\n\n/** Toggle \"Recalculate workbook before saving\" (workbook-level). */\nexport const setCalcOnSave = (wb: Workbook, on: boolean): void => {\n ensureCalcProperties(wb).calcOnSave = on;\n};\n\n/** Toggle \"Recalculate workbook on load\" — forces a full recalc on open. */\nexport const setFullCalcOnLoad = (wb: Workbook, on: boolean): void => {\n ensureCalcProperties(wb).fullCalcOnLoad = on;\n};\n\n/** Toggle \"Set precision as displayed\" (false = full 15-digit precision). */\nexport const setFullPrecision = (wb: Workbook, on: boolean): void => {\n ensureCalcProperties(wb).fullPrecision = on;\n};\n","// Workbook-level <workbookPr>. Per ECMA-376 §18.2.28.\n//\n// Mirrors openpyxl/openpyxl/workbook/properties.py WorkbookProperties.\n// `date1904` is already lifted via wb.date1904; everything else lived\n// in workbookXmlExtras passthrough until now. This typed shell promotes\n// all 19 attrs into the modeled workbook so consumers can edit them\n// without rebuilding the XmlNode.\n\nexport type ShowObjectsMode = 'all' | 'placeholders' | 'none';\nexport type UpdateLinksMode = 'userSet' | 'never' | 'always';\n\nexport interface WorkbookProperties {\n /**\n * Mac 1904 epoch flag. Mirrored from `Workbook.date1904` — the\n * canonical source. Setting it here has no effect on the cell-serial\n * conversion path, which keys off the top-level field.\n */\n date1904?: boolean;\n /** Excel 5/95 ↔ 2007 compatibility hint. */\n dateCompatibility?: boolean;\n showObjects?: ShowObjectsMode;\n showBorderUnselectedTables?: boolean;\n filterPrivacy?: boolean;\n promptedSolutions?: boolean;\n showInkAnnotation?: boolean;\n backupFile?: boolean;\n /** Cache external-link values when saving. */\n saveExternalLinkValues?: boolean;\n /** \"Update remote links\" prompt mode. */\n updateLinks?: UpdateLinksMode;\n /** VBA codeName for the workbook (e.g. \"ThisWorkbook\" / \"ЭтаКнига\"). */\n codeName?: string;\n hidePivotFieldList?: boolean;\n showPivotChartFilter?: boolean;\n allowRefreshQuery?: boolean;\n publishItems?: boolean;\n checkCompatibility?: boolean;\n autoCompressPictures?: boolean;\n refreshAllConnections?: boolean;\n /** Theme schema version (Excel 2007 = 124226, 2013+ = 153222). */\n defaultThemeVersion?: number;\n}\n\nexport const makeWorkbookProperties = (opts: WorkbookProperties = {}): WorkbookProperties => ({ ...opts });\n\n// ---- Workbook ergonomic helpers ----------------------------------------\n\nimport type { Workbook } from './workbook';\n\nconst ensureWorkbookProperties = (wb: Workbook): WorkbookProperties => {\n if (!wb.workbookProperties) wb.workbookProperties = {};\n return wb.workbookProperties;\n};\n\n/**\n * Set the workbook-level VBA codeName (\"ThisWorkbook\" by default in\n * Excel; localised forms like \"ЭтаКнига\" round-trip too). Empty string\n * is allowed — Excel writes it that way for codename-stripped files.\n */\nexport const setWorkbookCodeName = (wb: Workbook, codeName: string): void => {\n ensureWorkbookProperties(wb).codeName = codeName;\n};\n\n/**\n * Toggle the Mac 1904 epoch. The canonical flag is `wb.date1904`\n * (drives cell-serial conversion); this helper writes both the\n * canonical field and the mirror on `workbookProperties` so a save\n * emits a consistent `<workbookPr date1904=\"…\">` attribute.\n */\nexport const setDate1904 = (wb: Workbook, on: boolean): void => {\n wb.date1904 = on;\n ensureWorkbookProperties(wb).date1904 = on;\n};\n\n/**\n * Set the \"Update remote links\" prompt mode. `'userSet'` keeps\n * Excel's per-user preference; `'never'` disables the prompt;\n * `'always'` forces it. Mirrors the Trust Center \"External Content\"\n * dropdown.\n */\nexport const setUpdateLinksMode = (wb: Workbook, mode: UpdateLinksMode): void => {\n ensureWorkbookProperties(wb).updateLinks = mode;\n};\n\n/** Toggle the \"filterPrivacy\" hint Excel writes to indicate filter contents may be sensitive. */\nexport const setFilterPrivacy = (wb: Workbook, on: boolean): void => {\n ensureWorkbookProperties(wb).filterPrivacy = on;\n};\n","// Workbook-level <fileVersion>. Per ECMA-376 §18.2.13.\n//\n// Carries Microsoft Office app/version metadata that Excel writes\n// when it saves; round-tripping these values keeps the file looking\n// like Excel's own output to downstream tools that sniff them.\n\nexport interface FileVersion {\n /** Application that last saved the workbook (\"xl\" for Excel). */\n appName?: string;\n /** Build number of the last editor (e.g. \"7.5210\"). */\n lastEdited?: string;\n /** Build number of the lowest editor (oldest Excel that touched the file). */\n lowestEdited?: string;\n /** Internal \"rolled-up build\" number. */\n rupBuild?: string;\n /** GUID identifying the file content (Excel uses it to detect re-saves). */\n codeName?: string;\n}\n\nexport const makeFileVersion = (opts: FileVersion = {}): FileVersion => ({ ...opts });\n","// Workbook-level <fileSharing>. Per ECMA-376 §18.2.12.\n//\n// Carries the workbook's read-only / write-protection settings (Save\n// As → Tools → General Options → \"Modify password\" / \"Read-only\n// recommended\"). The hash quad mirrors sheetProtection / workbookProtection.\n\nexport interface FileSharing {\n /** Mark the workbook as \"Read-only recommended\" — Excel pops a dialog on open. */\n readOnlyRecommended?: boolean;\n /** Author name attached to the read/write password. */\n userName?: string;\n /** Legacy 16-bit hex hash of the reservation password. */\n reservationPassword?: string;\n /** Modern hash quad — algorithmName + hashValue + saltValue + spinCount. */\n algorithmName?: string;\n hashValue?: string;\n saltValue?: string;\n spinCount?: number;\n}\n\nexport const makeFileSharing = (opts: FileSharing = {}): FileSharing => ({ ...opts });\n","// Workbook-level <fileRecoveryPr>. Per ECMA-376 §18.2.11.\n//\n// Excel writes this element after an autorecover sequence to mark the\n// workbook with the recovery state so subsequent opens can prompt the\n// user. Almost always absent in fresh files.\n\nexport interface FileRecoveryProperties {\n /** True after an autorecover save — Excel uses it to display the recovery banner. */\n autoRecover?: boolean;\n /** Persisted crash-recovery flag. */\n crashSave?: boolean;\n /** Mark the file as \"data extracted from a damaged workbook\". */\n dataExtractLoad?: boolean;\n /** Workbook was repaired during load. */\n repairLoad?: boolean;\n}\n\nexport const makeFileRecoveryProperties = (\n opts: FileRecoveryProperties = {},\n): FileRecoveryProperties => ({ ...opts });\n","// Workbook-level <smartTagPr> + <smartTagTypes>. Per ECMA-376 §18.2.26\n// / §18.2.27. Smart tags were Excel 2003's auto-recognized data\n// (stock symbols, dates, names) and are deprecated, but the elements\n// still appear in some legacy workbooks.\n\nexport type SmartTagShowMode = 'all' | 'noIndicator';\n\nexport interface SmartTagProperties {\n /** Embed smart tags into the workbook on save. */\n embed?: boolean;\n show?: SmartTagShowMode;\n}\n\nexport interface SmartTagType {\n namespaceUri?: string;\n name?: string;\n url?: string;\n}\n\nexport const makeSmartTagProperties = (opts: SmartTagProperties = {}): SmartTagProperties => ({ ...opts });\n\nexport const makeSmartTagType = (opts: SmartTagType = {}): SmartTagType => ({ ...opts });\n","// Workbook-level <functionGroups>. Per ECMA-376 §18.2.14.\n//\n// Excel registers built-in function groups (Math/Statistical/Logical/...)\n// implicitly with a `builtInGroupCount` count and supports user-defined\n// XLL function groups appended after that count. Most workbooks don't\n// carry this element at all.\n\nexport interface FunctionGroup {\n name: string;\n}\n\nexport interface FunctionGroups {\n /** Number of built-in groups Excel reserves before user entries (default 16). */\n builtInGroupCount?: number;\n groups: FunctionGroup[];\n}\n\nexport const makeFunctionGroup = (name: string): FunctionGroup => ({ name });\n\nexport const makeFunctionGroups = (opts: Partial<FunctionGroups> = {}): FunctionGroups => ({\n groups: opts.groups?.slice() ?? [],\n ...(opts.builtInGroupCount !== undefined ? { builtInGroupCount: opts.builtInGroupCount } : {}),\n});\n"],"mappings":";;;AAoCA,MAAa,0BAA0B,OAA2B,CAAC,MAA0B;CAC3F,MAAM,MAA0B,CAAC;CACjC,KAAK,MAAM,KAAK;EACd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,GACE,IAAI,KAAK,OAAO,KAAA,GAAW,IAAI,KAAK,KAAK;CAE3C,IAAI,KAAK,sBAAsB,KAAA,GAAW,IAAI,oBAAoB,KAAK;CACvE,IAAI,KAAK,uBAAuB,KAAA,GAAW,IAAI,qBAAqB,KAAK;CACzE,IAAI,KAAK,kBAAkB,KAAA,GAAW,IAAI,gBAAgB,KAAK;CAC/D,IAAI,KAAK,gBAAgB,KAAA,GAAW,IAAI,cAAc,KAAK;CAC3D,IAAI,KAAK,iBAAiB,KAAA,GAAW,IAAI,eAAe,KAAK;CAC7D,OAAO;AACT;;;AC5BA,MAAa,oBAAoB,OAAqB,CAAC,OAAqB,EAAE,GAAG,KAAK;AAmCtF,MAAa,0BACX,UAEwB,EAAE,GAAG,KAAK;;;ACnCpC,MAAa,sBAAsB,OAAuB,CAAC,OAAuB,EAAE,GAAG,KAAK;;;ACU5F,MAAa,0BAA0B,OAA2B,CAAC,OAA2B,EAAE,GAAG,KAAK;;;ACxBxG,MAAa,mBAAmB,OAAoB,CAAC,OAAoB,EAAE,GAAG,KAAK;;;ACCnF,MAAa,mBAAmB,OAAoB,CAAC,OAAoB,EAAE,GAAG,KAAK;;;ACHnF,MAAa,8BACX,OAA+B,CAAC,OACJ,EAAE,GAAG,KAAK;;;ACAxC,MAAa,0BAA0B,OAA2B,CAAC,OAA2B,EAAE,GAAG,KAAK;AAExG,MAAa,oBAAoB,OAAqB,CAAC,OAAqB,EAAE,GAAG,KAAK;;;ACJtF,MAAa,qBAAqB,UAAiC,EAAE,KAAK;AAE1E,MAAa,sBAAsB,OAAgC,CAAC,OAAuB;CACzF,QAAQ,KAAK,QAAQ,MAAM,KAAK,CAAC;CACjC,GAAI,KAAK,sBAAsB,KAAA,IAAY,EAAE,mBAAmB,KAAK,kBAAkB,IAAI,CAAC;AAC9F"}
@@ -0,0 +1,34 @@
1
+ export type FilterColumn = {
2
+ kind: 'filters';
3
+ colId: number;
4
+ /** Discrete values that pass the filter. Stored as strings to match the wire format. */
5
+ values: string[];
6
+ /** Whether blanks are visible. */
7
+ blank?: boolean;
8
+ };
9
+ export interface AutoFilter {
10
+ /** Excel range the filter covers (`"A1:E100"`). */
11
+ ref: string;
12
+ filterColumns: FilterColumn[];
13
+ }
14
+ export declare function makeAutoFilter(opts: {
15
+ ref: string;
16
+ filterColumns?: FilterColumn[];
17
+ }): AutoFilter;
18
+ export declare function makeFilterColumn(opts: {
19
+ colId: number;
20
+ values: ReadonlyArray<string>;
21
+ blank?: boolean;
22
+ }): FilterColumn;
23
+ import type { Worksheet } from './worksheet';
24
+ /** Add an AutoFilter dropdown header strip to the given range. */
25
+ export declare const addAutoFilter: (ws: Worksheet, ref: string) => AutoFilter;
26
+ /**
27
+ * Add a value-list dropdown filter to a column inside the existing AutoFilter
28
+ * range. `colId` is 0-based relative to the AutoFilter left edge.
29
+ */
30
+ export declare const addAutoFilterColumn: (ws: Worksheet, colId: number, values: ReadonlyArray<string>, opts?: {
31
+ blank?: boolean;
32
+ }) => FilterColumn;
33
+ /** Drop the worksheet's AutoFilter entirely. */
34
+ export declare const removeAutoFilter: (ws: Worksheet) => void;
@@ -0,0 +1,121 @@
1
+ import type { Cell } from '../cell/cell';
2
+ import { type CellRangeBoundaries } from '../utils/coordinate';
3
+ /** Re-export under the plan's canonical name. */
4
+ export type CellRange = CellRangeBoundaries;
5
+ /** Build a CellRange from explicit 1-based bounds. */
6
+ export declare function makeCellRange(minRow: number, minCol: number, maxRow: number, maxCol: number): CellRange;
7
+ /** Parse a range expression — wraps {@link rangeBoundaries}. */
8
+ export declare function parseRange(input: string): CellRange;
9
+ /** Format a CellRange back into the canonical OOXML string. */
10
+ export declare function rangeToString(r: CellRange): string;
11
+ /**
12
+ * Compute the bounding A1-style range string for a list of cells. Walks the
13
+ * input once to find min/max row+col. A single-cell input returns a single-cell
14
+ * ref (`"A1"`); two or more cells (even collinear) return the `"A1:B5"` form.
15
+ *
16
+ * Throws when the array is empty — there's no meaningful zero-cell range, and
17
+ * silently returning `""` would defeat downstream `parseRange` consumers.
18
+ */
19
+ export declare function cellRangeFromCells(cells: ReadonlyArray<Pick<Cell, 'row' | 'col'>>): string;
20
+ /** Inclusive containment of a single (row, col) within a range. */
21
+ export declare function rangeContainsCell(r: CellRange, row: number, col: number): boolean;
22
+ /**
23
+ * A1-string convenience for {@link rangeContainsCell}. Parses `cellRef` (e.g.
24
+ * `"B3"`) and `rangeRef` (e.g. `"A1:C5"`) and returns `true` iff the cell sits
25
+ * inside the range (boundary-inclusive). Throws when either input is malformed.
26
+ */
27
+ export declare function isCellInRange(cellRef: string, rangeRef: string): boolean;
28
+ /**
29
+ * A1-string convenience for {@link rangeContainsRange}. Returns `true` iff the
30
+ * `inner` range is wholly contained by `outer` (boundary-inclusive).
31
+ * Single-cell refs are accepted on either side via parseRange. Throws on
32
+ * malformed input.
33
+ */
34
+ export declare function isRangeInRange(inner: string, outer: string): boolean;
35
+ /**
36
+ * A1-string convenience for {@link rangesOverlap}. Returns `true` iff the two
37
+ * ranges share at least one cell. Boundary-inclusive (a 1-row gap = no
38
+ * overlap). Single-cell refs are accepted via parseRange. Throws on malformed
39
+ * input.
40
+ */
41
+ export declare function rangesOverlapStr(a: string, b: string): boolean;
42
+ /**
43
+ * A1-string convenience for {@link unionRange}. Returns the smallest A1 range
44
+ * that contains both inputs (always non-null; ranges that don't overlap still
45
+ * get a valid bounding box).
46
+ */
47
+ export declare function unionRangeStr(a: string, b: string): string;
48
+ /**
49
+ * A1-string convenience for {@link intersectionRange}. Returns the shared
50
+ * rectangular sub-range as A1 string, or `undefined` when the inputs are
51
+ * disjoint.
52
+ */
53
+ export declare function intersectionRangeStr(a: string, b: string): string | undefined;
54
+ /**
55
+ * A1-string convenience for {@link shiftRange}. Translates the range by `(dr,
56
+ * dc)` integer offsets and re-serialises. Negative offsets shift up/left.
57
+ * Throws when the resulting bounds fall outside the OOXML grid (rows
58
+ * 1..1048576, cols 1..16384).
59
+ */
60
+ export declare function shiftRangeStr(range: string, dr: number, dc: number): string;
61
+ /**
62
+ * A1-string convenience for {@link rangeArea}. Returns the inclusive cell count
63
+ * covered by the range (rows × cols). Single-cell refs return 1.
64
+ */
65
+ export declare function rangeAreaStr(range: string): number;
66
+ /**
67
+ * A1-string range dimensions: how many rows and columns the range spans
68
+ * (inclusive). Distinct from {@link rangeAreaStr} which returns the product.
69
+ * Single-cell refs return `{ rows: 1, cols: 1 }`.
70
+ */
71
+ export declare function rangeDimensionsStr(range: string): {
72
+ rows: number;
73
+ cols: number;
74
+ };
75
+ /**
76
+ * Expand (or shrink) an A1 range by adding `deltaRows` to its bottom edge and
77
+ * `deltaCols` to its right edge. The top-left corner is preserved. Negative
78
+ * deltas shrink the range; the result must still have at least 1 row and 1
79
+ * column (otherwise throws).
80
+ *
81
+ * Useful for "this is the data range — also reserve room for a totals row" or
82
+ * "include one more column to the right" patterns.
83
+ */
84
+ export declare function expandRangeStr(range: string, deltaRows: number, deltaCols: number): string;
85
+ /** Inclusive containment of `inner` within `outer`. */
86
+ export declare function rangeContainsRange(outer: CellRange, inner: CellRange): boolean;
87
+ /**
88
+ * Shift a range by (dr, dc) integer offsets. The returned range is clamped to
89
+ * the OOXML grid; callers that want hard bounds should pass values that keep
90
+ * the result inside the spec.
91
+ */
92
+ export declare function shiftRange(r: CellRange, dr: number, dc: number): CellRange;
93
+ /** Bounding-box union of two ranges. Always non-null. */
94
+ export declare function unionRange(a: CellRange, b: CellRange): CellRange;
95
+ /** Returns the rectangular intersection of two ranges, or `null` when disjoint. */
96
+ export declare function intersectionRange(a: CellRange, b: CellRange): CellRange | null;
97
+ /** True iff two ranges share at least one cell. */
98
+ export declare function rangesOverlap(a: CellRange, b: CellRange): boolean;
99
+ /** Inclusive cell count covered by a range. */
100
+ export declare function rangeArea(r: CellRange): number;
101
+ /** Yield every (row, col) coordinate in the range, row-major. */
102
+ export declare function iterRangeCoordinates(r: CellRange): IterableIterator<{
103
+ row: number;
104
+ col: number;
105
+ }>;
106
+ /**
107
+ * Excel's `sqref` attribute: a space-separated list of CellRanges. Used by data
108
+ * validations, conditional formatting, hyperlinks etc.
109
+ */
110
+ export interface MultiCellRange {
111
+ ranges: CellRange[];
112
+ }
113
+ export declare function makeMultiCellRange(ranges?: ReadonlyArray<CellRange>): MultiCellRange;
114
+ /** Parse an sqref string: `"A1:B2 D5 E10:F20"`. Whitespace-delimited. */
115
+ export declare function parseMultiCellRange(input: string): MultiCellRange;
116
+ /** Format a MultiCellRange back into an sqref string. */
117
+ export declare function multiCellRangeToString(m: MultiCellRange): string;
118
+ /** Total cell count across all ranges (no de-duplication of overlaps). */
119
+ export declare function multiCellRangeArea(m: MultiCellRange): number;
120
+ /** True iff any contained range covers (row, col). */
121
+ export declare function multiCellRangeContainsCell(m: MultiCellRange, row: number, col: number): boolean;
@@ -0,0 +1,24 @@
1
+ import type { LegacyComment } from './comments';
2
+ /** Parse a `xl/commentsN.xml` payload into a flat LegacyComment list. */
3
+ export declare function parseCommentsXml(bytes: Uint8Array | string): LegacyComment[];
4
+ /**
5
+ * Serialise a LegacyComment array to a `xl/commentsN.xml` payload. Authors are
6
+ * deduped: each unique `author` becomes one `<author>` entry, and comments
7
+ * reference it by index.
8
+ */
9
+ export declare function commentsToBytes(comments: ReadonlyArray<LegacyComment>): Uint8Array;
10
+ export declare function serializeComments(comments: ReadonlyArray<LegacyComment>): string;
11
+ /**
12
+ * Bare-bones VML drawing payload Excel tolerates as a comment-shape
13
+ * placeholder. We don't preserve the original VML shapes (stage-1 trade-off);
14
+ * this stub guarantees the worksheet rels stay consistent.
15
+ *
16
+ * Excel won't open the file unless every legacy comment is paired with a
17
+ * `<v:shape>` carrying `<x:ClientData ObjectType="Note">` plus the cell anchor
18
+ * row/column — without those, Excel reports "removed Records: Comment from
19
+ * /xl/comments1.xml" and silently drops them.
20
+ *
21
+ * The `<v:shapetype id="_x0000_t202">` / `<v:shape type="#_x0000_t202">` pair
22
+ * mirrors what openpyxl's ShapeWriter emits.
23
+ */
24
+ export declare function placeholderVmlDrawing(comments?: ReadonlyArray<LegacyComment>): Uint8Array;
@@ -0,0 +1,13 @@
1
+ export interface LegacyComment {
2
+ /** Cell reference — typically a single cell ("A1") but Excel allows ranges. */
3
+ ref: string;
4
+ /** Display name of the comment author. */
5
+ author: string;
6
+ /** Plain-text body. Stage-1 doesn't preserve rich-text formatting. */
7
+ text: string;
8
+ }
9
+ export declare function makeLegacyComment(opts: {
10
+ ref: string;
11
+ author: string;
12
+ text: string;
13
+ }): LegacyComment;