@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yuichiro Yamashita
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,319 @@
1
+ <p align="center">
2
+ <img src="./assets/logo.png" alt="@office-kit/xlsx" width="180" height="180" />
3
+ </p>
4
+
5
+ # @office-kit/xlsx
6
+
7
+ A TypeScript library for reading and writing Excel `.xlsx` workbooks
8
+ from Node 22+ and modern browsers, with no runtime dependencies on
9
+ Python or Excel. Inspired by [openpyxl](https://openpyxl.readthedocs.io/).
10
+
11
+ > **Status: pre-1.0 alpha.** The core read / write / streaming pipeline is
12
+ > in place and round-trips real-world fixtures (including pivot tables and
13
+ > macro-enabled `.xlsm`), but APIs may shift before `1.0`.
14
+
15
+ ## Why @office-kit/xlsx?
16
+
17
+ The JavaScript xlsx ecosystem in 2026 is split between **commercial upsell
18
+ tiers** and **stalled open-source projects**. SheetJS Community Edition
19
+ deliberately omits styling, charts, images, pivots, conditional formatting,
20
+ and data validation on write — those live in [SheetJS Pro][sjs-pro], a paid
21
+ tier. ExcelJS is MIT but
22
+ [has not had a meaningful release since October 2023][exceljs-discussion]
23
+ and its maintainers explicitly call it inactive; the dependency footprint
24
+ unpacks to 21.8 MB. excel4node was [archived in 2022][e4n-archive].
25
+ xlsx-js-style is frozen at a 2022 SheetJS fork.
26
+
27
+ @office-kit/xlsx is the third option: an actively-developed, pure-MIT,
28
+ TypeScript-first library with no Pro tier and no missing features behind a
29
+ paywall.
30
+
31
+ [sjs-pro]: https://sheetjs.com/pro/
32
+ [exceljs-discussion]: https://github.com/exceljs/exceljs/discussions/2987
33
+ [e4n-archive]: https://github.com/natergj/excel4node
34
+
35
+ | Concern | Other libraries | @office-kit/xlsx |
36
+ |------------------------|----------------------------------------------------------------------------------|-----------------------------------------------------------------------|
37
+ | TypeScript types | hand-written `.d.ts` retrofitted (SheetJS) or community typings (xlsx-populate, excel4node) | first-party, written in TS under `exactOptionalPropertyTypes` + `noUncheckedIndexedAccess` |
38
+ | Bundle size | ExcelJS unpacks to 21.8 MB; xlsx ~7.5 MB | full lib ≤120 KB min+brotli (currently ~85 KB); streaming entry ~49 KB |
39
+ | Streaming | SheetJS docs explicitly note the zip central-directory layout prevents true streaming; ExcelJS supports both directions but the lib is heavy | both read iter and write append, with fixed-memory budget for tens of millions of rows |
40
+ | Charts (write) | none in ExcelJS, xlsx-js-style, SheetJS CE; gated behind SheetJS Pro | 16 legacy `c:` + 8 modern `cx:` chart kinds (Sunburst, Treemap, Waterfall, Histogram, Pareto, Funnel, BoxWhisker, RegionMap) |
41
+ | Pivots / VBA / OLE | ExcelJS drops pivot tables on read ([#261][exceljs-pivot]); others vary | byte-identical passthrough so Excel 365 still renders parts we don't model |
42
+ | Maintenance | ExcelJS stalled since 2023; excel4node archived 2022; xlsx-js-style frozen 2022; SheetJS npm artifact frozen 2022 (still distributed via private CDN) | active |
43
+ | License | SheetJS CE strips features for Pro upsell; SheetJS Pro pricing not published | MIT, single tier, no upsell |
44
+ | Conformance | none of the major libraries validate against ECMA-376 in CI | 3-tier validator (OPC structure + ECMA-376 XSD + semantic invariants) gates every CI build, including a fast-check property-based oracle |
45
+ | Modules | monolithic root barrel | subpath imports — `@office-kit/xlsx/io`, `/streaming`, `/cell`, `/styles`, etc., each independently tree-shakable |
46
+
47
+ [exceljs-pivot]: https://github.com/exceljs/exceljs/issues/261
48
+
49
+ ### Where each existing library still wins
50
+
51
+ - **Read simple xlsx in the browser** → [`read-excel-file`][rexf] is excellent.
52
+ - **Write simple xlsx with images** → [`write-excel-file`][wexf] is excellent.
53
+ - **Template-based fidelity preservation with password protection** → `xlsx-populate`.
54
+ - **Non-xlsx formats** (XLS / XLSB / ODS / CSV / HTML) → SheetJS Community.
55
+ - **Commercial budget + long shopping list** → SheetJS Pro.
56
+
57
+ [rexf]: https://www.npmjs.com/package/read-excel-file
58
+ [wexf]: https://www.npmjs.com/package/write-excel-file
59
+
60
+ ### @office-kit/xlsx's home turf
61
+
62
+ - You write modern TypeScript and want types that actually behave under
63
+ strict mode (cell values are a discriminated union, not `any`).
64
+ - You produce **large** xlsx files (tens of millions of cells) and care
65
+ about heap budget.
66
+ - You need **charts**, conditional formatting, data validation, defined
67
+ names, tables, ZIP64 (entry-count overflow; see "What's supported" for the
68
+ 4 GiB-per-entry caveat) — and want them in MIT.
69
+ - You round-trip xlsx files that contain pivot tables, VBA macros,
70
+ threaded comments, Power Query metadata, or customXml — and need them
71
+ preserved byte-for-byte.
72
+ - You want **proof** that the bytes you emit are valid OOXML, not "Excel
73
+ happens to open them today."
74
+
75
+ ### When NOT to use @office-kit/xlsx
76
+
77
+ Honest list:
78
+
79
+ - **Pre-1.0**: API may shift before 1.0. Pin the version for long-running
80
+ projects.
81
+ - **`.xlsx` only**: no `.xls` (BIFF), `.xlsb`, `.ods`, or `.csv`. Use
82
+ SheetJS for those.
83
+ - **Node 22+ required**: relies on built-in `Web Streams`, `Blob`, and
84
+ `fetch`. Node 18 / 20 (EOL) are not supported.
85
+ - **Browser stress-test history is shorter** than ExcelJS's. If you ship
86
+ to millions of browser users today, run your own benchmark first.
87
+ - **Visual QA in Excel 365** is on the human-verification list; the schema
88
+ gate proves spec compliance, not that every chart renders pixel-perfect.
89
+
90
+ ### Motivation
91
+
92
+ The reasons @office-kit/xlsx exists, written down so future contributors don't
93
+ relitigate them:
94
+
95
+ 1. **The reference implementation is in Python.** [openpyxl][openpyxl] has
96
+ spent 15 years collecting Excel / LibreOffice corner cases. @office-kit/xlsx
97
+ consumes its fixture corpus directly (`reference/openpyxl/` is a git
98
+ submodule), so edge cases the Python world solved years ago don't get
99
+ re-discovered painfully in JS.
100
+ 2. **The 2010-era JS stack is heavy.** Most existing libraries pull in
101
+ `jszip`, `lodash`, `archiver`, `xmlbuilder`, `sax`. In 2026 we have
102
+ `fflate`, `fast-xml-parser`, and `saxes` — the toolchain is an order
103
+ of magnitude lighter. @office-kit/xlsx ships with three runtime dependencies.
104
+ 3. **TypeScript-first changes the API surface.** A library authored in TS
105
+ under strict-mode flags from day one exposes different ergonomics than
106
+ `.d.ts` typings retrofitted onto an old JS codebase.
107
+ 4. **"Schema-valid" should be a CI gate, not a vibe.** ECMA-376 is
108
+ downloadable; xmllint is free; vendoring the schemas costs <1 MB. There
109
+ is no good reason a 2026 library shouldn't validate every byte it
110
+ emits against the spec.
111
+ 5. **No Pro tier.** Charts, pivots passthrough, conditional formatting,
112
+ ZIP64 write — all MIT. Nothing held back.
113
+
114
+ [openpyxl]: https://openpyxl.readthedocs.io/
115
+
116
+ ## Install
117
+
118
+ ```sh
119
+ pnpm add @office-kit/xlsx # or npm / yarn / bun
120
+ ```
121
+
122
+ Requires Node `>=22` for the built-in `Web Streams`, `Blob`, and `fetch`
123
+ globals.
124
+
125
+ ## Subpath entries
126
+
127
+ The package has no root barrel — every export lives behind a section
128
+ subpath, so your editor's autocomplete only surfaces what's relevant to
129
+ the area you're working in. Each export has exactly one home (no
130
+ convenience re-exports).
131
+
132
+ | Import | Use case |
133
+ |------------------------|---------------------------------------------------|
134
+ | `@office-kit/xlsx/io` | `loadWorkbook` / `saveWorkbook` / `workbookToBytes` plus byte-level Source/Sink + browser helpers (Blob/Response/Stream) |
135
+ | `@office-kit/xlsx/node` | Node fs glue (`fromFile` / `toFile` / `fromBuffer` / `toBuffer` / `fromReadable` / `toWritable`) |
136
+ | `@office-kit/xlsx/streaming` | Read-only iter (`loadWorkbookStream`) + write-only append (`createWriteOnlyWorkbook`) |
137
+ | `@office-kit/xlsx/workbook` | `createWorkbook`, `addWorksheet`, defined names |
138
+ | `@office-kit/xlsx/worksheet` | `setCell`, `getCell`, `mergeCells`, tables, … |
139
+ | `@office-kit/xlsx/cell` | Cell value-model + inline rich text |
140
+ | `@office-kit/xlsx/styles` | Fonts, fills, borders, alignment, number formats |
141
+ | `@office-kit/xlsx/chart` | `c:` and `cx:` chart kinds |
142
+ | `@office-kit/xlsx/chartsheet` | Standalone chartsheets |
143
+ | `@office-kit/xlsx/drawing` | Anchors, images, chart placement |
144
+
145
+ Other subpaths: `@office-kit/xlsx/packaging`, `@office-kit/xlsx/utils`, `@office-kit/xlsx/xml`,
146
+ `@office-kit/xlsx/zip`, `@office-kit/xlsx/schema`. All exports are tree-shakable
147
+ (`"sideEffects": false`).
148
+
149
+ Bundle budgets (min + brotli):
150
+
151
+ - `@office-kit/xlsx/streaming` ≤ 80 KB (currently ~49 KB)
152
+ - `@office-kit/xlsx/io` ≤ 120 KB (currently ~85 KB)
153
+
154
+ ## Quick examples
155
+
156
+ For a one-page lookup of task → exact functions to import and call, see
157
+ the [Cheatsheet](https://baseballyama.github.io/@office-kit/xlsx/docs/cheatsheet).
158
+ For prose-style worked examples (styling, charts, validation, streaming),
159
+ see the [Recipes](https://baseballyama.github.io/@office-kit/xlsx/docs/recipes).
160
+
161
+ ### Read + edit + write
162
+
163
+ ```ts
164
+ import { loadWorkbook, workbookToBytes } from '@office-kit/xlsx/io';
165
+ import { setCell } from '@office-kit/xlsx/worksheet';
166
+ import { fromBuffer } from '@office-kit/xlsx/node';
167
+ import { readFile, writeFile } from 'node:fs/promises';
168
+
169
+ const wb = await loadWorkbook(fromBuffer(await readFile('input.xlsx')));
170
+ const sheet = wb.sheets[0];
171
+ if (sheet?.kind === 'worksheet') {
172
+ setCell(sheet.sheet, /* row */ 1, /* col */ 1, 'Hello from @office-kit/xlsx');
173
+ }
174
+ await writeFile('output.xlsx', await workbookToBytes(wb));
175
+ ```
176
+
177
+ ### Read directly from disk (Node)
178
+
179
+ ```ts
180
+ import { loadWorkbook, saveWorkbook } from '@office-kit/xlsx/io';
181
+ import { fromFile, toFile } from '@office-kit/xlsx/node';
182
+
183
+ const wb = await loadWorkbook(fromFile('input.xlsx'));
184
+ // …mutate wb…
185
+ await saveWorkbook(wb, toFile('output.xlsx'));
186
+ ```
187
+
188
+ ### Read directly from a `fetch` response (browser)
189
+
190
+ ```ts
191
+ import { fromResponse, loadWorkbook } from '@office-kit/xlsx/io';
192
+
193
+ const response = await fetch('/sheet.xlsx');
194
+ const wb = await loadWorkbook(fromResponse(response));
195
+ ```
196
+
197
+ ### Streaming write — millions of rows in a fixed memory budget
198
+
199
+ ```ts
200
+ import { createWriteOnlyWorkbook } from '@office-kit/xlsx/streaming';
201
+ import { toFile } from '@office-kit/xlsx/node';
202
+
203
+ const sink = toFile('big.xlsx');
204
+ const wb = await createWriteOnlyWorkbook(sink);
205
+ const ws = await wb.addWorksheet('Data');
206
+ ws.setColumnWidth(1, 24); // must precede the first appendRow
207
+ for (let r = 0; r < 10_000_000; r++) {
208
+ await ws.appendRow([r, `row-${r}`, r * Math.PI]);
209
+ }
210
+ await ws.close();
211
+ await wb.finalize();
212
+ ```
213
+
214
+ The streaming writer pushes each row through deflate as it arrives, and
215
+ `toFile` forwards each deflated chunk to disk (honouring write-stream
216
+ backpressure) — peak memory stays at one pending-row buffer plus deflate
217
+ scratch, regardless of total archive size. The same is true of `toWritable`;
218
+ buffered sinks (`toBuffer` / `toBlob` / `toArrayBuffer`) instead keep the
219
+ full archive resident so `result()` can hand it back in one piece.
220
+
221
+ ### Streaming read — iterate row-by-row without materialising the sheet
222
+
223
+ ```ts
224
+ import { loadWorkbookStream } from '@office-kit/xlsx/streaming';
225
+ import { fromFile } from '@office-kit/xlsx/node';
226
+
227
+ const wb = await loadWorkbookStream(fromFile('big.xlsx'));
228
+ const sheet = wb.openWorksheet(wb.sheetNames[0] ?? '');
229
+ for await (const row of sheet.iterRows({ minRow: 1, maxRow: 100 })) {
230
+ console.log(row.map((c) => c.value));
231
+ }
232
+ await wb.close();
233
+ ```
234
+
235
+ The whole-sheet iteration path (default / `minRow <= 1`) inflates the
236
+ worksheet entry chunk-by-chunk straight into the SAX parser, so the inflated
237
+ worksheet body is never fully resident. Note: ZIP requires random access to
238
+ its central directory, so the **compressed archive bytes are loaded up
239
+ front**. A 200 MB compressed xlsx therefore needs ~200 MB resident, plus the
240
+ inflate window + SAX state per active iterator — not the multi-GB inflated
241
+ worksheet payload. Band queries (`minRow > 1`) build a row-offset index once
242
+ per sheet, which does materialise that sheet's inflated bytes; subsequent
243
+ band queries reuse the cached index.
244
+
245
+ ### Migrating from openpyxl
246
+
247
+ @office-kit/xlsx is shaped after openpyxl, but a few defaults differ. The most
248
+ common surprise for direct ports:
249
+
250
+ - **`createWorkbook()` returns an empty workbook with no sheets.**
251
+ `openpyxl.Workbook()` creates a default sheet named `Sheet` that
252
+ callers usually remove with `wb.remove(wb.active)`. @office-kit/xlsx skips
253
+ that step — call `addWorksheet(wb, 'Data')` directly. Translating a
254
+ `remove(active)` call literally produces a no-op (or, worse, a guard
255
+ that hides a real bug elsewhere).
256
+ - **`setCell(ws, row, col, value)`** is the @office-kit/xlsx equivalent of
257
+ openpyxl's `ws.cell(row=r, column=c, value=v)`. Coordinates are
258
+ 1-based on both sides.
259
+ - **`makeBorder({ left: makeSide({ style: 'thin' }) })`** is the
260
+ @office-kit/xlsx equivalent of openpyxl's
261
+ `Border(left=Side(style='thin'))`. Same with `makeFill`, `makeFont`,
262
+ etc. — every style primitive has a `make*` constructor under
263
+ `@office-kit/xlsx/styles`.
264
+
265
+ ## What's supported
266
+
267
+ - ✅ Cell values: number, string (sharedStrings), boolean, error, formulas
268
+ (normal / array / shared / dataTable), inline rich text
269
+ - ✅ Styles: Font, Fill, Border, Alignment, Protection, NumberFormat, full
270
+ Stylesheet pool with dedup, named styles + DXF
271
+ - ✅ Worksheet rich features: mergedCells, sheetView/freezePanes, columnDims,
272
+ rowDims, hyperlinks, defined names, data validations, autoFilter, Tables,
273
+ legacy comments, conditional formatting
274
+ - ✅ Drawings: anchors, images (PNG/JPEG/GIF/BMP/WebP/TIFF/SVG/EMF/WMF) with
275
+ format + dimension auto-detection, picture frames in worksheets and charts
276
+ - ✅ Charts: 16 legacy `c:` chart kinds + 8 `cx:` chartex kinds (Sunburst,
277
+ Treemap, Waterfall, Histogram, Pareto, Funnel, BoxWhisker, RegionMap),
278
+ spPr / txPr / dLbls / trendline / errBars wiring, chartsheets, UserShapes
279
+ - ✅ Pivot tables / VBA / OLE / threaded comments / external links / Power
280
+ Query metadata / customXml / customUI: byte-identical passthrough so
281
+ Excel 365 still renders parts we don't model. The `<workbook>` body
282
+ extras and per-sheet rels chain are preserved end-to-end.
283
+ - ✅ Encrypted xlsx detection (CFB Compound Document magic): clear error
284
+ pointing at `msoffcrypto-tool` for decryption.
285
+ - ✅ ZIP64 write — partial: workbooks with > 65 535 entries get a ZIP64 EOCD
286
+ record + locator spliced into the final chunk. Read works too. **Limit:**
287
+ individual entry sizes and the central-directory offset must still fit in
288
+ 32 bits (≤ 4 GiB each); xlsx archives never approach that in practice, but
289
+ if you genuinely need a single >4 GiB entry the writer will throw. Tracked
290
+ in [`src/zip/zip64-patch.ts`][zip64-patch].
291
+
292
+ [zip64-patch]: ./src/zip/zip64-patch.ts
293
+
294
+ ## Development
295
+
296
+ The test suite reads fixtures from the `reference/openpyxl` git submodule, so
297
+ clone with submodules (or run `pnpm install`, which auto-inits via the
298
+ `prepare` script):
299
+
300
+ ```sh
301
+ git clone --recursive https://github.com/office-kit/xlsx.git
302
+ # or, if you already cloned without --recursive:
303
+ git submodule update --init --recursive
304
+
305
+ pnpm install
306
+ pnpm typecheck
307
+ pnpm lint
308
+ pnpm test # vitest, ~2100 tests
309
+ pnpm test:perf # write-only throughput + heap-budget bench
310
+ pnpm build # tsdown + tsc → dist/
311
+ pnpm size # size-limit guards on each bundle
312
+ ```
313
+
314
+ [Nix flake](flake.nix) included — `nix develop` (or [direnv](https://direnv.net/)
315
+ with `use flake`) gives a pinned Node 22 + pnpm 10 + Python 3 environment.
316
+
317
+ ## License
318
+
319
+ MIT — see [LICENSE](LICENSE) and [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
@@ -0,0 +1,56 @@
1
+ # Third Party Notices
2
+
3
+ `@office-kit/xlsx` is a TypeScript port of, and incorporates work derived from, the
4
+ following projects.
5
+
6
+ ## openpyxl (MIT)
7
+
8
+ Source: <https://foss.heptapod.net/openpyxl/openpyxl> (canonical, Mercurial),
9
+ mirrored at <https://github.com/quintagroup/openpyxl> (Git, used as this
10
+ repository's `reference/openpyxl` submodule for porting reference only).
11
+
12
+ ```
13
+ Copyright (c) 2010 openpyxl
14
+
15
+ Permission is hereby granted, free of charge, to any person obtaining a copy
16
+ of this software and associated documentation files (the "Software"), to deal
17
+ in the Software without restriction, including without limitation the rights
18
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19
+ copies of the Software, and to permit persons to whom the Software is
20
+ furnished to do so, subject to the following conditions:
21
+
22
+ The above copyright notice and this permission notice shall be included in
23
+ all copies or substantial portions of the Software.
24
+
25
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31
+ THE SOFTWARE.
32
+ ```
33
+
34
+ ## Runtime dependencies
35
+
36
+ The following npm packages ship as part of `@office-kit/xlsx` at runtime. Each is
37
+ licensed under its own terms — links go to the upstream repository where the
38
+ license text lives.
39
+
40
+ - **fflate** (MIT) — <https://github.com/101arrowz/fflate>
41
+ ZIP / DEFLATE I/O. Used by `src/zip/reader.ts` (`unzipSync` fallback +
42
+ `inflateSync`) and `src/zip/writer.ts` (`Zip` + `ZipDeflate` /
43
+ `ZipPassThrough` streaming-deflate writer).
44
+ - **saxes** (ISC) — <https://github.com/lddubeau/saxes>
45
+ Streaming XML parser. Drives the SAX iter API in `src/xml/iterparse.ts`
46
+ which the streaming read-only path in `src/streaming/read-only.ts` uses.
47
+ - **fast-xml-parser** (MIT) —
48
+ <https://github.com/NaturalIntelligence/fast-xml-parser>
49
+ DOM-style XML parser used by `src/xml/parser.ts` for the eager
50
+ load path; the streaming read-only path uses saxes instead.
51
+
52
+ ## Dev dependencies
53
+
54
+ Dev-only dependencies declared in `package.json` (vitest, oxlint, tsdown,
55
+ typescript, fast-check, size-limit, …) are subject to their upstream
56
+ licenses; consult `pnpm-lock.yaml` for resolved versions.
@@ -0,0 +1,234 @@
1
+ import { type RichText } from './rich-text';
2
+ /** Excel error tokens. */
3
+ export type ExcelErrorCode = '#NULL!' | '#DIV/0!' | '#VALUE!' | '#REF!' | '#NAME?' | '#NUM!' | '#N/A' | '#GETTING_DATA';
4
+ /** Formula sub-kind — drives the OOXML `<f t="…">` attribute. */
5
+ export type FormulaKind = 'normal' | 'array' | 'shared' | 'dataTable';
6
+ export interface FormulaValue {
7
+ readonly kind: 'formula';
8
+ readonly formula: string;
9
+ readonly t: FormulaKind;
10
+ /** Cached value Excel last computed for the cell, used when `data_only` reads it back. */
11
+ readonly cachedValue?: number | string | boolean;
12
+ /** Range string (`"A1:A10"`) for array / shared / dataTable formulas. */
13
+ readonly ref?: string;
14
+ /** Shared-formula index. */
15
+ readonly si?: number;
16
+ /** Data-table specific fields (mirrors openpyxl DataTableFormula). */
17
+ readonly r1?: string;
18
+ readonly r2?: string;
19
+ readonly dt2D?: boolean;
20
+ readonly dtr?: boolean;
21
+ readonly del1?: boolean;
22
+ readonly del2?: boolean;
23
+ readonly aca?: boolean;
24
+ readonly ca?: boolean;
25
+ }
26
+ export type CellValue = number | string | boolean | Date | {
27
+ kind: 'duration';
28
+ ms: number;
29
+ } | {
30
+ kind: 'error';
31
+ code: ExcelErrorCode;
32
+ } | {
33
+ kind: 'rich-text';
34
+ runs: RichText;
35
+ } | FormulaValue | null;
36
+ export interface Cell {
37
+ /** 1-based row index. */
38
+ row: number;
39
+ /** 1-based column index. */
40
+ col: number;
41
+ /** Effective cell value. `null` represents an empty cell. */
42
+ value: CellValue;
43
+ /** Index into Workbook.styles.cellXfs. 0 = default. */
44
+ styleId: number;
45
+ /** Optional reference to a Hyperlink registered on the worksheet. */
46
+ hyperlinkId?: number;
47
+ /** Optional reference to a Comment registered on the worksheet. */
48
+ commentId?: number;
49
+ }
50
+ /** Marker subtype for the placeholder cells inside a merged range (top-left holds the value). */
51
+ export interface MergedCell extends Cell {
52
+ merged: true;
53
+ }
54
+ /** Build a fresh Cell. Validates coordinates against the OOXML grid bounds. */
55
+ export declare function makeCell(row: number, col: number, value?: CellValue, styleId?: number): Cell;
56
+ /** Format a Cell's coordinate as the canonical "A1" string. */
57
+ export declare function getCoordinate(c: Cell): string;
58
+ /**
59
+ * Direct value setter. No type inference, no validation beyond the union — the
60
+ * caller is in charge. Use {@link bindValue} for the "do what I mean" path.
61
+ */
62
+ export declare function setCellValue(c: Cell, value: CellValue): void;
63
+ /**
64
+ * "Smart" setter: infers the cell value from a JS runtime value.
65
+ * - `string` starting with `=` → formula
66
+ * - `string` matching an Excel error token → error variant
67
+ * - other primitives / Date / null pass through verbatim
68
+ *
69
+ * Intentionally not the default — explicit is clearer for typed code, and
70
+ * inferring on every write costs measurable time on the worksheet write hot
71
+ * path.
72
+ */
73
+ export declare function bindValue(c: Cell, value: number | string | boolean | Date | null): void;
74
+ /** Plain `=A1+B1` style formula. Cached value is optional but recommended for round-trip. */
75
+ export declare function setFormula(c: Cell, formula: string, opts?: {
76
+ cachedValue?: FormulaValue['cachedValue'];
77
+ }): void;
78
+ /** Array (CSE) formula spanning a `ref` range. */
79
+ export declare function setArrayFormula(c: Cell, ref: string, formula: string, opts?: {
80
+ cachedValue?: FormulaValue['cachedValue'];
81
+ }): void;
82
+ /**
83
+ * Shared formula. The first cell in the group carries the formula text + ref;
84
+ * subsequent cells with the same `si` carry only the index and Excel
85
+ * reconstructs the formula via reference shifting.
86
+ */
87
+ export declare function setSharedFormula(c: Cell, si: number, formula?: string, ref?: string, opts?: {
88
+ cachedValue?: FormulaValue['cachedValue'];
89
+ }): void;
90
+ /**
91
+ * Excel data-table formula (`<f t="dataTable">`). These appear as the "What-if
92
+ * Analysis → Data Table" feature output: a 1- or 2-variable sensitivity grid
93
+ * where the formula references one or two input cells. The wire format mirrors
94
+ * openpyxl's `DataTableFormula`:
95
+ *
96
+ * - `ref` — inclusive cell range the formula spans.
97
+ * - `r1`, `r2`— input cell coordinates ("$A$1" etc.).
98
+ * - `dt2D` — true for two-variable tables (uses both r1 and r2).
99
+ * - `dtr` — row-direction flag (true) vs column-direction (false).
100
+ * - `del1`/`del2` — Excel marks one of these true when the
101
+ * corresponding input cell has been deleted; the formula keeps round-tripping
102
+ * so Excel can show the warning state.
103
+ * - `aca`/`ca` — alwaysCalculate / calculate flags.
104
+ */
105
+ export interface DataTableFormulaOpts {
106
+ ref: string;
107
+ r1?: string;
108
+ r2?: string;
109
+ dt2D?: boolean;
110
+ dtr?: boolean;
111
+ del1?: boolean;
112
+ del2?: boolean;
113
+ aca?: boolean;
114
+ ca?: boolean;
115
+ cachedValue?: FormulaValue['cachedValue'];
116
+ }
117
+ /**
118
+ * Set a data-table formula on a cell. Preserves all the dt-specific attributes
119
+ * so the writer can re-emit `<f t="dataTable" r1="..." />` verbatim and Excel
120
+ * keeps treating the cell as a Data Table cell.
121
+ */
122
+ export declare function setDataTableFormula(c: Cell, formula: string, opts: DataTableFormulaOpts): void;
123
+ /** Build a `{ kind: 'error', code }` cell value. */
124
+ export declare function makeErrorValue(code: ExcelErrorCode): {
125
+ kind: 'error';
126
+ code: ExcelErrorCode;
127
+ };
128
+ /** Build a `{ kind: 'duration', ms }` cell value. */
129
+ export declare function makeDurationValue(ms: number): {
130
+ kind: 'duration';
131
+ ms: number;
132
+ };
133
+ /** True iff `c.value` is the formula variant. */
134
+ export declare function isFormulaCell(c: Cell): boolean;
135
+ /** True iff `c.value` is the rich-text variant. */
136
+ export declare function isRichTextCell(c: Cell): boolean;
137
+ /** Returns true iff the cell has no content. */
138
+ export declare function isEmptyCell(c: Cell): boolean;
139
+ /**
140
+ * Type guard for `MergedCell` — true iff the cell is a placeholder for a
141
+ * merged-range covered cell (the top-left of a merged range holds the value;
142
+ * the rest are `MergedCell`). Use this to filter merge-placeholders out of
143
+ * value-walking loops.
144
+ */
145
+ export declare function isMergedCell(c: Cell): c is MergedCell;
146
+ /** Returns true iff the cell holds an Excel error value (`#REF!`, `#NAME?`, …). */
147
+ export declare function isErrorCell(c: Cell): boolean;
148
+ /**
149
+ * Get the formula text from a formula-bearing cell, or `undefined` for
150
+ * non-formula cells. Equivalent to: isFormulaValue(c.value) ? c.value.formula :
151
+ * undefined but spares callers the type-narrow + member access.
152
+ */
153
+ export declare function getFormulaText(c: Cell): string | undefined;
154
+ /**
155
+ * Get the cached value Excel last computed for a formula cell, or `undefined`
156
+ * for non-formula / uncached cells. Useful for `data_only` read paths that want
157
+ * the displayed result without re-evaluating.
158
+ */
159
+ export declare function getCachedFormulaValue(c: Cell): number | string | boolean | undefined;
160
+ /** True iff `v` is the formula variant. */
161
+ export declare function isFormulaValue(v: CellValue): v is FormulaValue;
162
+ /** True iff `v` is the rich-text variant. */
163
+ export declare function isRichTextValue(v: CellValue): v is {
164
+ kind: 'rich-text';
165
+ runs: RichText;
166
+ };
167
+ /** True iff `v` is the error variant. */
168
+ export declare function isErrorValue(v: CellValue): v is {
169
+ kind: 'error';
170
+ code: ExcelErrorCode;
171
+ };
172
+ /** True iff `v` is the duration variant. */
173
+ export declare function isDurationValue(v: CellValue): v is {
174
+ kind: 'duration';
175
+ ms: number;
176
+ };
177
+ export interface CellValueAsStringOptions {
178
+ /** Renderer for `Date` cells. Defaults to `d => d.toISOString()`. */
179
+ dateFormat?: (value: Date) => string;
180
+ /** Replacement for the `null` cell value. Defaults to `''`. */
181
+ emptyText?: string;
182
+ }
183
+ /**
184
+ * Coerce a CellValue to its plain-string display form. Numbers / booleans
185
+ * convert via `String`; rich text concatenates run text; formulas yield the
186
+ * cached value (or empty string when uncached); errors yield their Excel token;
187
+ * durations yield `"<ms> ms"` with no formatting; Dates yield
188
+ * `Date.toISOString()`; `null` yields `""`.
189
+ *
190
+ * Pass `opts.dateFormat` to override the Date renderer (e.g. a locale-specific
191
+ * format) and `opts.emptyText` to substitute a different placeholder for
192
+ * `null` cells.
193
+ */
194
+ export declare function cellValueAsString(v: CellValue, opts?: CellValueAsStringOptions): string;
195
+ /**
196
+ * Coerce a CellValue to `boolean | undefined`. Booleans pass through; `'TRUE'`
197
+ * / `'true'` and `'FALSE'` / `'false'` (case-insensitive) parse to true /
198
+ * false; numbers yield `false` for 0 and `true` for any other finite value
199
+ * (matching Excel's truthy-number coercion); formula cells return their cached
200
+ * boolean if any. Everything else (null, Date, error, duration, rich-text,
201
+ * non-bool strings) yields `undefined`.
202
+ */
203
+ export declare function cellValueAsBoolean(v: CellValue): boolean | undefined;
204
+ /**
205
+ * Coerce a CellValue to a `Date` when one is meaningful. Pass-through for
206
+ * `Date`-typed values; ISO-8601 strings (anything `new Date(s)` parses to a
207
+ * finite time) round-trip; durations are interpreted as `new Date(ms)`.
208
+ * Numbers, booleans, formulas, errors, rich text, and null all return
209
+ * `undefined` — this helper does **not** apply the Excel-serial-to-Date
210
+ * conversion (use `excelToDate` for that).
211
+ */
212
+ export declare function cellValueAsDate(v: CellValue): Date | undefined;
213
+ /**
214
+ * Coerce a CellValue to a number when one is meaningful. Booleans yield 0/1;
215
+ * numeric strings parse via `Number(s)`; rich-text concats then parses;
216
+ * formulas with a numeric cached value pass through. Returns `undefined` when
217
+ * there's no sensible numeric reading (text strings, errors, dates, durations,
218
+ * null, empty).
219
+ */
220
+ export declare function cellValueAsNumber(v: CellValue): number | undefined;
221
+ /**
222
+ * Map a CellValue to the most natural JS primitive for display / export.
223
+ * Unlike `cellValueAsString`/`cellValueAsNumber`/etc., which each force a
224
+ * single target type and return `undefined` when the value can't be coerced,
225
+ * this returns whatever primitive best represents the union variant:
226
+ *
227
+ * - `null` → `null`
228
+ * - `string` / `number` / `boolean` / `Date` → passthrough
229
+ * - rich text → joined run text (`string`)
230
+ * - formula → recursive on `cachedValue` (`null` when uncached)
231
+ * - error → error code (`string`)
232
+ * - duration → `ms` (`number`)
233
+ */
234
+ export declare function cellValueAsPrimitive(v: CellValue): string | number | boolean | Date | null;
@@ -0,0 +1,4 @@
1
+ export type { Cell, CellValue, CellValueAsStringOptions, DataTableFormulaOpts, ExcelErrorCode, FormulaKind, FormulaValue, MergedCell, } from './cell';
2
+ export { bindValue, cellValueAsBoolean, cellValueAsDate, cellValueAsNumber, cellValueAsPrimitive, cellValueAsString, getCachedFormulaValue, getCoordinate, getFormulaText, isDurationValue, isEmptyCell, isErrorCell, isErrorValue, isFormulaCell, isFormulaValue, isMergedCell, isRichTextCell, isRichTextValue, makeCell, makeDurationValue, makeErrorValue, setArrayFormula, setCellValue, setDataTableFormula, setFormula, setSharedFormula, } from './cell';
3
+ export type { InlineFont, InlineUnderline, InlineVertAlign, RichText, TextRun, } from './rich-text';
4
+ export { makeRichText, makeTextRun, richTextToString } from './rich-text';
@@ -0,0 +1,37 @@
1
+ import type { Color } from '../styles/colors';
2
+ /** Underline styles per openpyxl's cell-level NestedNoneSet. */
3
+ export type InlineUnderline = 'single' | 'double' | 'singleAccounting' | 'doubleAccounting';
4
+ export type InlineVertAlign = 'baseline' | 'superscript' | 'subscript';
5
+ export interface InlineFont {
6
+ readonly name?: string;
7
+ readonly sz?: number;
8
+ readonly b?: boolean;
9
+ readonly i?: boolean;
10
+ readonly u?: InlineUnderline;
11
+ readonly strike?: boolean;
12
+ readonly outline?: boolean;
13
+ readonly shadow?: boolean;
14
+ readonly condense?: boolean;
15
+ readonly extend?: boolean;
16
+ readonly vertAlign?: InlineVertAlign;
17
+ readonly color?: Color;
18
+ readonly family?: number;
19
+ readonly charset?: number;
20
+ readonly scheme?: 'major' | 'minor';
21
+ }
22
+ export interface TextRun {
23
+ readonly text: string;
24
+ readonly font?: InlineFont;
25
+ }
26
+ /** A frozen array of TextRuns. The shared cell value shape under `kind: 'rich-text'`. */
27
+ export type RichText = ReadonlyArray<TextRun>;
28
+ export declare function makeTextRun(text: string, font?: InlineFont): TextRun;
29
+ export declare function makeRichText(runs: ReadonlyArray<TextRun | {
30
+ text: string;
31
+ font?: InlineFont;
32
+ }>): RichText;
33
+ /**
34
+ * Concatenate the plain-text content of a rich-text value (rich-text
35
+ * read paths often want the raw text without formatting).
36
+ */
37
+ export declare function richTextToString(rt: RichText): string;