@shbernal/ts-xlsx 1.0.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 (223) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +230 -0
  3. package/dist/core/address.d.ts +49 -0
  4. package/dist/core/address.js +125 -0
  5. package/dist/core/autofilter.d.ts +55 -0
  6. package/dist/core/autofilter.js +48 -0
  7. package/dist/core/cell.d.ts +132 -0
  8. package/dist/core/cell.js +249 -0
  9. package/dist/core/color-resolution.d.ts +58 -0
  10. package/dist/core/color-resolution.js +170 -0
  11. package/dist/core/column.d.ts +87 -0
  12. package/dist/core/column.js +167 -0
  13. package/dist/core/comment-thread.d.ts +116 -0
  14. package/dist/core/comment-thread.js +62 -0
  15. package/dist/core/conditional-formatting-overlay.d.ts +15 -0
  16. package/dist/core/conditional-formatting-overlay.js +24 -0
  17. package/dist/core/conditional-formatting.d.ts +70 -0
  18. package/dist/core/conditional-formatting.js +35 -0
  19. package/dist/core/containers.d.ts +2 -0
  20. package/dist/core/containers.js +25 -0
  21. package/dist/core/data-validation-overlay.d.ts +28 -0
  22. package/dist/core/data-validation-overlay.js +56 -0
  23. package/dist/core/data-validation.d.ts +38 -0
  24. package/dist/core/data-validation.js +13 -0
  25. package/dist/core/date.d.ts +25 -0
  26. package/dist/core/date.js +58 -0
  27. package/dist/core/formula.d.ts +49 -0
  28. package/dist/core/formula.js +319 -0
  29. package/dist/core/grid-edits.d.ts +22 -0
  30. package/dist/core/grid-edits.js +206 -0
  31. package/dist/core/image.d.ts +76 -0
  32. package/dist/core/image.js +65 -0
  33. package/dist/core/internal.d.ts +11 -0
  34. package/dist/core/internal.js +29 -0
  35. package/dist/core/merge.d.ts +13 -0
  36. package/dist/core/merge.js +26 -0
  37. package/dist/core/modern-functions.d.ts +1 -0
  38. package/dist/core/modern-functions.js +165 -0
  39. package/dist/core/page-setup.d.ts +94 -0
  40. package/dist/core/page-setup.js +6 -0
  41. package/dist/core/pivot-table.d.ts +113 -0
  42. package/dist/core/pivot-table.js +249 -0
  43. package/dist/core/preserved.d.ts +58 -0
  44. package/dist/core/preserved.js +5 -0
  45. package/dist/core/protection.d.ts +67 -0
  46. package/dist/core/protection.js +68 -0
  47. package/dist/core/range.d.ts +112 -0
  48. package/dist/core/range.js +283 -0
  49. package/dist/core/row.d.ts +73 -0
  50. package/dist/core/row.js +135 -0
  51. package/dist/core/style.d.ts +246 -0
  52. package/dist/core/style.js +125 -0
  53. package/dist/core/table-style.d.ts +67 -0
  54. package/dist/core/table-style.js +90 -0
  55. package/dist/core/table.d.ts +194 -0
  56. package/dist/core/table.js +291 -0
  57. package/dist/core/theme.d.ts +90 -0
  58. package/dist/core/theme.js +272 -0
  59. package/dist/core/value.d.ts +115 -0
  60. package/dist/core/value.js +144 -0
  61. package/dist/core/workbook-protection.d.ts +27 -0
  62. package/dist/core/workbook-protection.js +25 -0
  63. package/dist/core/workbook.d.ts +528 -0
  64. package/dist/core/workbook.js +732 -0
  65. package/dist/core/worksheet-model.d.ts +34 -0
  66. package/dist/core/worksheet-model.js +100 -0
  67. package/dist/core/worksheet.d.ts +647 -0
  68. package/dist/core/worksheet.js +1025 -0
  69. package/dist/customui/errors.d.ts +15 -0
  70. package/dist/customui/errors.js +15 -0
  71. package/dist/customui/index.d.ts +2 -0
  72. package/dist/customui/index.js +7 -0
  73. package/dist/customui/ribbon.d.ts +86 -0
  74. package/dist/customui/ribbon.js +180 -0
  75. package/dist/entries/core.d.ts +23 -0
  76. package/dist/entries/core.js +20 -0
  77. package/dist/entries/csv.d.ts +2 -0
  78. package/dist/entries/csv.js +6 -0
  79. package/dist/entries/customui.d.ts +1 -0
  80. package/dist/entries/customui.js +3 -0
  81. package/dist/entries/errors.d.ts +7 -0
  82. package/dist/entries/errors.js +20 -0
  83. package/dist/entries/vba.d.ts +2 -0
  84. package/dist/entries/vba.js +7 -0
  85. package/dist/entries/xlsb.d.ts +1 -0
  86. package/dist/entries/xlsb.js +7 -0
  87. package/dist/entries/xlsx.d.ts +5 -0
  88. package/dist/entries/xlsx.js +18 -0
  89. package/dist/errors.d.ts +65 -0
  90. package/dist/errors.js +66 -0
  91. package/dist/index.d.ts +7 -0
  92. package/dist/index.js +19 -0
  93. package/dist/io/csv/read.d.ts +15 -0
  94. package/dist/io/csv/read.js +113 -0
  95. package/dist/io/csv/write.d.ts +28 -0
  96. package/dist/io/csv/write.js +124 -0
  97. package/dist/io/opc/errors.d.ts +46 -0
  98. package/dist/io/opc/errors.js +49 -0
  99. package/dist/io/opc/inflate.d.ts +15 -0
  100. package/dist/io/opc/inflate.js +91 -0
  101. package/dist/io/opc/namespaces.d.ts +18 -0
  102. package/dist/io/opc/namespaces.js +18 -0
  103. package/dist/io/opc/part-paths.d.ts +4 -0
  104. package/dist/io/opc/part-paths.js +35 -0
  105. package/dist/io/opc/read-opc.d.ts +24 -0
  106. package/dist/io/opc/read-opc.js +153 -0
  107. package/dist/io/opc/read-options.d.ts +10 -0
  108. package/dist/io/opc/read-options.js +7 -0
  109. package/dist/io/opc/rels.d.ts +15 -0
  110. package/dist/io/opc/rels.js +28 -0
  111. package/dist/io/opc/sniff-format.d.ts +27 -0
  112. package/dist/io/opc/sniff-format.js +92 -0
  113. package/dist/io/style/xf-style.d.ts +47 -0
  114. package/dist/io/style/xf-style.js +95 -0
  115. package/dist/io/xlsb/errors.d.ts +15 -0
  116. package/dist/io/xlsb/errors.js +15 -0
  117. package/dist/io/xlsb/formula.d.ts +48 -0
  118. package/dist/io/xlsb/formula.js +398 -0
  119. package/dist/io/xlsb/primitives.d.ts +86 -0
  120. package/dist/io/xlsb/primitives.js +230 -0
  121. package/dist/io/xlsb/ptg-functions.d.ts +16 -0
  122. package/dist/io/xlsb/ptg-functions.js +659 -0
  123. package/dist/io/xlsb/read-shared-strings.d.ts +2 -0
  124. package/dist/io/xlsb/read-shared-strings.js +22 -0
  125. package/dist/io/xlsb/read-styles.d.ts +4 -0
  126. package/dist/io/xlsb/read-styles.js +386 -0
  127. package/dist/io/xlsb/read-worksheet.d.ts +9 -0
  128. package/dist/io/xlsb/read-worksheet.js +301 -0
  129. package/dist/io/xlsb/read.d.ts +20 -0
  130. package/dist/io/xlsb/read.js +199 -0
  131. package/dist/io/xlsb/record-stream.d.ts +14 -0
  132. package/dist/io/xlsb/record-stream.js +56 -0
  133. package/dist/io/xlsb/record-types.d.ts +55 -0
  134. package/dist/io/xlsb/record-types.js +73 -0
  135. package/dist/io/xlsx/cell-accumulator.d.ts +25 -0
  136. package/dist/io/xlsx/cell-accumulator.js +172 -0
  137. package/dist/io/xlsx/cell-value.d.ts +32 -0
  138. package/dist/io/xlsx/cell-value.js +85 -0
  139. package/dist/io/xlsx/comments.d.ts +74 -0
  140. package/dist/io/xlsx/comments.js +243 -0
  141. package/dist/io/xlsx/conditional-formatting.d.ts +32 -0
  142. package/dist/io/xlsx/conditional-formatting.js +464 -0
  143. package/dist/io/xlsx/data-validation.d.ts +20 -0
  144. package/dist/io/xlsx/data-validation.js +258 -0
  145. package/dist/io/xlsx/edit-vba.d.ts +23 -0
  146. package/dist/io/xlsx/edit-vba.js +139 -0
  147. package/dist/io/xlsx/errors.d.ts +17 -0
  148. package/dist/io/xlsx/errors.js +17 -0
  149. package/dist/io/xlsx/hyperlinks.d.ts +42 -0
  150. package/dist/io/xlsx/hyperlinks.js +139 -0
  151. package/dist/io/xlsx/images.d.ts +34 -0
  152. package/dist/io/xlsx/images.js +218 -0
  153. package/dist/io/xlsx/namespaces.d.ts +55 -0
  154. package/dist/io/xlsx/namespaces.js +55 -0
  155. package/dist/io/xlsx/package-plan.d.ts +103 -0
  156. package/dist/io/xlsx/package-plan.js +168 -0
  157. package/dist/io/xlsx/pivot-read.d.ts +5 -0
  158. package/dist/io/xlsx/pivot-read.js +134 -0
  159. package/dist/io/xlsx/pivot.d.ts +12 -0
  160. package/dist/io/xlsx/pivot.js +141 -0
  161. package/dist/io/xlsx/read-rows.d.ts +85 -0
  162. package/dist/io/xlsx/read-rows.js +286 -0
  163. package/dist/io/xlsx/read-styles.d.ts +8 -0
  164. package/dist/io/xlsx/read-styles.js +551 -0
  165. package/dist/io/xlsx/read-worksheet.d.ts +4 -0
  166. package/dist/io/xlsx/read-worksheet.js +528 -0
  167. package/dist/io/xlsx/read.d.ts +32 -0
  168. package/dist/io/xlsx/read.js +680 -0
  169. package/dist/io/xlsx/relationships.d.ts +33 -0
  170. package/dist/io/xlsx/relationships.js +47 -0
  171. package/dist/io/xlsx/rich-runs.d.ts +13 -0
  172. package/dist/io/xlsx/rich-runs.js +59 -0
  173. package/dist/io/xlsx/rich-text.d.ts +7 -0
  174. package/dist/io/xlsx/rich-text.js +23 -0
  175. package/dist/io/xlsx/shared-formulas.d.ts +6 -0
  176. package/dist/io/xlsx/shared-formulas.js +52 -0
  177. package/dist/io/xlsx/shared-strings-read.d.ts +2 -0
  178. package/dist/io/xlsx/shared-strings-read.js +70 -0
  179. package/dist/io/xlsx/shared-strings.d.ts +17 -0
  180. package/dist/io/xlsx/shared-strings.js +50 -0
  181. package/dist/io/xlsx/sheet-properties.d.ts +13 -0
  182. package/dist/io/xlsx/sheet-properties.js +211 -0
  183. package/dist/io/xlsx/styles.d.ts +138 -0
  184. package/dist/io/xlsx/styles.js +821 -0
  185. package/dist/io/xlsx/tables.d.ts +9 -0
  186. package/dist/io/xlsx/tables.js +208 -0
  187. package/dist/io/xlsx/threaded-comments.d.ts +102 -0
  188. package/dist/io/xlsx/threaded-comments.js +308 -0
  189. package/dist/io/xlsx/workbook-xml.d.ts +12 -0
  190. package/dist/io/xlsx/workbook-xml.js +353 -0
  191. package/dist/io/xlsx/worksheet-xml.d.ts +86 -0
  192. package/dist/io/xlsx/worksheet-xml.js +643 -0
  193. package/dist/io/xlsx/write-stream.d.ts +171 -0
  194. package/dist/io/xlsx/write-stream.js +399 -0
  195. package/dist/io/xlsx/write.d.ts +81 -0
  196. package/dist/io/xlsx/write.js +396 -0
  197. package/dist/io/xlsx/x14-ext.d.ts +9 -0
  198. package/dist/io/xlsx/x14-ext.js +12 -0
  199. package/dist/vba/cfb-writer.d.ts +19 -0
  200. package/dist/vba/cfb-writer.js +277 -0
  201. package/dist/vba/cfb.d.ts +16 -0
  202. package/dist/vba/cfb.js +324 -0
  203. package/dist/vba/codepage.d.ts +18 -0
  204. package/dist/vba/codepage.js +87 -0
  205. package/dist/vba/errors.d.ts +24 -0
  206. package/dist/vba/errors.js +24 -0
  207. package/dist/vba/index.d.ts +5 -0
  208. package/dist/vba/index.js +15 -0
  209. package/dist/vba/ms-ovba.d.ts +12 -0
  210. package/dist/vba/ms-ovba.js +176 -0
  211. package/dist/vba/project-editor.d.ts +62 -0
  212. package/dist/vba/project-editor.js +377 -0
  213. package/dist/vba/project.d.ts +45 -0
  214. package/dist/vba/project.js +160 -0
  215. package/dist/vba/vba-encoding.d.ts +14 -0
  216. package/dist/vba/vba-encoding.js +39 -0
  217. package/dist/xml/errors.d.ts +15 -0
  218. package/dist/xml/errors.js +15 -0
  219. package/dist/xml/xml-read.d.ts +107 -0
  220. package/dist/xml/xml-read.js +283 -0
  221. package/dist/xml/xml.d.ts +41 -0
  222. package/dist/xml/xml.js +86 -0
  223. package/package.json +117 -0
@@ -0,0 +1,272 @@
1
+ // The workbook theme's colour scheme — the twelve colours a `theme="n"` reference resolves against.
2
+ //
3
+ // The theme part itself is carried opaquely by the model (see `Workbook.restoreThemePart`); this
4
+ // module reads just the `<a:clrScheme>` out of it, because that is the only piece a colour reference
5
+ // needs. Everything else in a theme (the format scheme's gradients, line and effect styles) is
6
+ // nobody's business here.
7
+ import { AuthoringError } from "../errors.js";
8
+ /**
9
+ * The twelve colour-scheme slots **in the order a `theme="n"` attribute indexes them**.
10
+ *
11
+ * This order is not the order the slots appear in the theme part. ISO/IEC 29500 §20.1.6.2 documents
12
+ * the `<a:clrScheme>` child sequence as `dk1, lt1, dk2, lt2, accent1…6, hlink, folHlink`, and that is
13
+ * how the XML is written — but SpreadsheetML's `theme="n"` does **not** index that sequence. Excel
14
+ * swaps each dark/light pair: index 0 is `lt1`, 1 is `dk1`, 2 is `lt2`, 3 is `dk2`.
15
+ *
16
+ * Verified against Excel Desktop rather than inferred, because the two orders differ only in the
17
+ * first four entries and reading either one into the other silently inverts text against background
18
+ * — see `docs/knowledge/specs/theme-color-index-order.md` and the recorded observation in
19
+ * `test/corpus/fixtures/excel-oracle/theme-color-index-order.json`. The stylesheet's own default font
20
+ * is the everyday witness: it carries `<color theme="1"/>` and renders black, which is `dk1`.
21
+ */
22
+ export const THEME_COLOR_SLOTS = [
23
+ 'lt1',
24
+ 'dk1',
25
+ 'lt2',
26
+ 'dk2',
27
+ 'accent1',
28
+ 'accent2',
29
+ 'accent3',
30
+ 'accent4',
31
+ 'accent5',
32
+ 'accent6',
33
+ 'hlink',
34
+ 'folHlink',
35
+ ];
36
+ /** The Office default colour scheme, matching the theme part the writer emits for a workbook with none. */
37
+ export const DEFAULT_THEME_COLOR_SCHEME = {
38
+ lt1: 'FFFFFF',
39
+ dk1: '000000',
40
+ lt2: 'E7E6E6',
41
+ dk2: '44546A',
42
+ accent1: '4472C4',
43
+ accent2: 'ED7D31',
44
+ accent3: 'A5A5A5',
45
+ accent4: 'FFC000',
46
+ accent5: '5B9BD5',
47
+ accent6: '70AD47',
48
+ hlink: '0563C1',
49
+ folHlink: '954F72',
50
+ };
51
+ // One `<a:slot>` of a `<a:clrScheme>` and the colour element inside it. Two colour models appear in
52
+ // practice: `<a:srgbClr val="RRGGBB"/>` states the colour directly, while `<a:sysClr val="windowText"
53
+ // lastClr="000000"/>` defers to an operating-system colour and records what it last resolved to.
54
+ // `dk1`/`lt1` are almost always the sysClr form, so a reader that only understands srgbClr resolves
55
+ // nothing for the two most-referenced slots in any workbook.
56
+ const SCHEME_SLOT = new RegExp('<a:(dk1|lt1|dk2|lt2|accent[1-6]|hlink|folHlink)\\b[^>]*>' + '\\s*<a:(srgbClr|sysClr)\\b([^>]*)>', 'g');
57
+ /**
58
+ * Extract the colour scheme from a theme part. Returns only the slots the part actually declares in a
59
+ * colour model this reader understands; an unrecognised one is dropped rather than guessed at, so a
60
+ * caller can tell "the theme says nothing here" from "the theme says black".
61
+ *
62
+ * Reads the `<a:clrScheme>` block alone. A theme carries a font scheme and a format scheme too, but
63
+ * neither participates in resolving a colour, and scanning the whole part would let a `<a:srgbClr>`
64
+ * buried in a gradient stop masquerade as a scheme slot.
65
+ */
66
+ export function parseThemeColorScheme(themeXml) {
67
+ const block = /<a:clrScheme\b[^>]*>([\s\S]*?)<\/a:clrScheme>/.exec(themeXml);
68
+ if (block === null)
69
+ return {};
70
+ const scheme = {};
71
+ for (const match of (block[1] ?? '').matchAll(SCHEME_SLOT)) {
72
+ const slot = match[1];
73
+ const attrs = match[3] ?? '';
74
+ // A sysClr's `val` is a system-colour name ("windowText"), not a colour — its `lastClr` is the
75
+ // concrete value the authoring application last resolved that name to, and is the only thing here
76
+ // a consumer without the same OS theme can use.
77
+ const source = match[2] === 'sysClr' ? /\blastClr="([^"]*)"/ : /\bval="([^"]*)"/;
78
+ const value = source.exec(attrs)?.[1];
79
+ if (value !== undefined && /^[0-9a-fA-F]{6}$/.test(value))
80
+ scheme[slot] = value;
81
+ }
82
+ return scheme;
83
+ }
84
+ /**
85
+ * The body typeface a workbook falls back to when neither its theme nor its styles part names one —
86
+ * the face the default theme nominates, and so the face every `scheme="minor"` font resolves to.
87
+ * Named rather than inlined because it is also the last resort of the default-font chain
88
+ * ({@link Workbook.defaultFont}), and the two must not drift.
89
+ */
90
+ export const OFFICE_BODY_FACE = 'Calibri';
91
+ /** The Office default typefaces, matching the theme part the writer emits for a workbook with none. */
92
+ export const DEFAULT_THEME_FONTS = {
93
+ major: 'Calibri Light',
94
+ minor: OFFICE_BODY_FACE,
95
+ };
96
+ /** Extract the major/minor latin typefaces from a theme part's `<a:fontScheme>`. */
97
+ export function parseThemeFontScheme(themeXml) {
98
+ const block = /<a:fontScheme\b[^>]*>([\s\S]*?)<\/a:fontScheme>/.exec(themeXml);
99
+ if (block === null)
100
+ return {};
101
+ const face = (which) => {
102
+ const font = new RegExp(`<a:${which}\\b[^>]*>([\\s\\S]*?)</a:${which}>`).exec(block[1] ?? '');
103
+ return /<a:latin\b[^>]*\btypeface="([^"]*)"/.exec(font?.[1] ?? '')?.[1];
104
+ };
105
+ const scheme = {};
106
+ const major = face('majorFont');
107
+ const minor = face('minorFont');
108
+ if (major !== undefined)
109
+ scheme.major = major;
110
+ if (minor !== undefined)
111
+ scheme.minor = minor;
112
+ return scheme;
113
+ }
114
+ // The `<a:clrScheme>` child order — dk1, lt1, dk2, lt2, accent1..6, hlink, folHlink. Not the order
115
+ // `theme="n"` indexes (see THEME_COLOR_SLOTS); this is the sequence CT_ColorScheme requires the
116
+ // elements to be written in, and writing them in index order would be schema-invalid.
117
+ const SCHEME_ELEMENT_ORDER = [
118
+ 'dk1',
119
+ 'lt1',
120
+ 'dk2',
121
+ 'lt2',
122
+ 'accent1',
123
+ 'accent2',
124
+ 'accent3',
125
+ 'accent4',
126
+ 'accent5',
127
+ 'accent6',
128
+ 'hlink',
129
+ 'folHlink',
130
+ ];
131
+ /**
132
+ * Apply authored colour/font overrides to a theme part, returning the new part text.
133
+ *
134
+ * Surgical by design: the base part rides through untouched except for the `<a:clrScheme>` and
135
+ * `<a:fontScheme>` blocks, and within those, only what the caller actually named. The format scheme —
136
+ * the gradients, line styles and effect styles that make a theme look like a theme — is left exactly
137
+ * as it was, because nobody hand-authors `fillStyleLst` gradient stops from a spreadsheet API and
138
+ * regenerating it would replace a designer's work with the Office default.
139
+ *
140
+ * A slot the caller did not override keeps its **verbatim source element**, not a re-serialisation of
141
+ * its value. That matters for `dk1`/`lt1`, which Excel writes as `<a:sysClr val="windowText"
142
+ * lastClr="000000"/>`: rewriting those as `<a:srgbClr>` would pin them to one machine's resolved
143
+ * window colours and break dark-mode following.
144
+ */
145
+ export function applyThemeOverrides(baseXml, overrides) {
146
+ let xml = baseXml;
147
+ const colors = overrides.colors ?? {};
148
+ if (Object.keys(colors).length > 0) {
149
+ const sourceElements = parseThemeColorElements(baseXml);
150
+ const body = SCHEME_ELEMENT_ORDER.map((slot) => {
151
+ const authored = colors[slot];
152
+ const inner = authored !== undefined
153
+ ? `<a:srgbClr val="${normalizeSchemeValue(authored)}"/>`
154
+ : (sourceElements[slot] ??
155
+ `<a:srgbClr val="${DEFAULT_THEME_COLOR_SCHEME[slot]}"/>`);
156
+ return `<a:${slot}>${inner}</a:${slot}>`;
157
+ }).join('');
158
+ xml = replaceBlockBody(xml, 'clrScheme', body);
159
+ }
160
+ const { major, minor } = overrides.fonts ?? {};
161
+ if (major !== undefined)
162
+ xml = replaceLatinTypeface(xml, 'majorFont', major);
163
+ if (minor !== undefined)
164
+ xml = replaceLatinTypeface(xml, 'minorFont', minor);
165
+ return xml;
166
+ }
167
+ /**
168
+ * Each colour slot's verbatim inner element from a theme part — `<a:srgbClr val="…"/>` or
169
+ * `<a:sysClr val="…" lastClr="…"/>`. The value-level counterpart is {@link parseThemeColorScheme};
170
+ * this keeps the *encoding* so an untouched slot can be re-emitted exactly as the source wrote it.
171
+ */
172
+ export function parseThemeColorElements(themeXml) {
173
+ const block = /<a:clrScheme\b[^>]*>([\s\S]*?)<\/a:clrScheme>/.exec(themeXml);
174
+ if (block === null)
175
+ return {};
176
+ const elements = {};
177
+ const pattern = /<a:(dk1|lt1|dk2|lt2|accent[1-6]|hlink|folHlink)>([\s\S]*?)<\/a:\1>|<a:(dk1|lt1|dk2|lt2|accent[1-6]|hlink|folHlink)\/>/g;
178
+ for (const match of (block[1] ?? '').matchAll(pattern)) {
179
+ const slot = (match[1] ?? match[3]);
180
+ const inner = match[2];
181
+ if (inner !== undefined && inner !== '')
182
+ elements[slot] = inner;
183
+ }
184
+ return elements;
185
+ }
186
+ // A theme colour is a bare 6-hex RGB — DrawingML has no alpha channel on `<a:srgbClr val>`. The two
187
+ // conveniences the rest of the library accepts (a leading '#', an 8-hex ARGB) are accepted and
188
+ // reduced here; anything else is a caller's bug and is refused rather than written as corrupt XML,
189
+ // which Excel does not report — it renders the slot as flat black.
190
+ function normalizeSchemeValue(value) {
191
+ const hex = value.startsWith('#') ? value.slice(1) : value;
192
+ const rgb = hex.length === 8 ? hex.slice(2) : hex;
193
+ if (!/^[0-9a-fA-F]{6}$/.test(rgb)) {
194
+ throw new AuthoringError(`Invalid theme colour ${JSON.stringify(value)}: expected 6 hexadecimal digits (RRGGBB)`);
195
+ }
196
+ return rgb.toUpperCase();
197
+ }
198
+ // Replace the body of `<a:name>…</a:name>`, keeping the element's own attributes (the scheme's
199
+ // display name). A base with no such block is left alone: this authors an existing theme, and a theme
200
+ // that declares no colour scheme at all is not one an override can repair.
201
+ function replaceBlockBody(xml, name, body) {
202
+ const pattern = new RegExp(`(<a:${name}\\b[^>]*>)[\\s\\S]*?(</a:${name}>)`);
203
+ return xml.replace(pattern, (_all, open, close) => `${open}${body}${close}`);
204
+ }
205
+ // Swap just the `<a:latin typeface="…"/>` inside one of the two font slots, leaving its `panose` and
206
+ // the east-asian/complex-script faces beside it as they were.
207
+ function replaceLatinTypeface(xml, which, typeface) {
208
+ const pattern = new RegExp(`(<a:${which}\\b[^>]*>[\\s\\S]*?<a:latin\\b)[^>]*(/>)`);
209
+ return xml.replace(pattern, (_all, open, close) => `${open} typeface="${escapeXmlAttr(typeface)}"${close}`);
210
+ }
211
+ function escapeXmlAttr(value) {
212
+ return value
213
+ .replace(/&/g, '&amp;')
214
+ .replace(/</g, '&lt;')
215
+ .replace(/>/g, '&gt;')
216
+ .replace(/"/g, '&quot;');
217
+ }
218
+ /**
219
+ * The theme part a workbook with no theme of its own ships — the standard Office theme.
220
+ *
221
+ * A spreadsheet must carry one even when nobody configured it: the stylesheet's own default font
222
+ * references `theme="1"`, which a consumer can only resolve against this part, so the two travel
223
+ * together. It is also the base {@link applyThemeOverrides} authors on top of when a workbook was
224
+ * built from scratch rather than read from a file.
225
+ */
226
+ export const DEFAULT_THEME_XML = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
227
+ '<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme">' +
228
+ '<a:themeElements>' +
229
+ '<a:clrScheme name="Office">' +
230
+ '<a:dk1><a:sysClr val="windowText" lastClr="000000"/></a:dk1>' +
231
+ '<a:lt1><a:sysClr val="window" lastClr="FFFFFF"/></a:lt1>' +
232
+ '<a:dk2><a:srgbClr val="44546A"/></a:dk2>' +
233
+ '<a:lt2><a:srgbClr val="E7E6E6"/></a:lt2>' +
234
+ '<a:accent1><a:srgbClr val="4472C4"/></a:accent1>' +
235
+ '<a:accent2><a:srgbClr val="ED7D31"/></a:accent2>' +
236
+ '<a:accent3><a:srgbClr val="A5A5A5"/></a:accent3>' +
237
+ '<a:accent4><a:srgbClr val="FFC000"/></a:accent4>' +
238
+ '<a:accent5><a:srgbClr val="5B9BD5"/></a:accent5>' +
239
+ '<a:accent6><a:srgbClr val="70AD47"/></a:accent6>' +
240
+ '<a:hlink><a:srgbClr val="0563C1"/></a:hlink>' +
241
+ '<a:folHlink><a:srgbClr val="954F72"/></a:folHlink>' +
242
+ '</a:clrScheme>' +
243
+ '<a:fontScheme name="Office">' +
244
+ '<a:majorFont><a:latin typeface="Calibri Light" panose="020F0302020204030204"/><a:ea typeface=""/><a:cs typeface=""/></a:majorFont>' +
245
+ '<a:minorFont><a:latin typeface="Calibri" panose="020F0502020204030204"/><a:ea typeface=""/><a:cs typeface=""/></a:minorFont>' +
246
+ '</a:fontScheme>' +
247
+ '<a:fmtScheme name="Office">' +
248
+ '<a:fillStyleLst>' +
249
+ '<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>' +
250
+ '<a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:lumMod val="110000"/><a:satMod val="105000"/><a:tint val="67000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:lumMod val="105000"/><a:satMod val="103000"/><a:tint val="73000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:lumMod val="105000"/><a:satMod val="109000"/><a:tint val="81000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="5400000" scaled="0"/></a:gradFill>' +
251
+ '<a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:satMod val="103000"/><a:lumMod val="102000"/><a:tint val="94000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:satMod val="110000"/><a:lumMod val="100000"/><a:shade val="100000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:lumMod val="99000"/><a:satMod val="120000"/><a:shade val="78000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="5400000" scaled="0"/></a:gradFill>' +
252
+ '</a:fillStyleLst>' +
253
+ '<a:lnStyleLst>' +
254
+ '<a:ln w="6350" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/><a:miter lim="800000"/></a:ln>' +
255
+ '<a:ln w="12700" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/><a:miter lim="800000"/></a:ln>' +
256
+ '<a:ln w="19050" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/><a:miter lim="800000"/></a:ln>' +
257
+ '</a:lnStyleLst>' +
258
+ '<a:effectStyleLst>' +
259
+ '<a:effectStyle><a:effectLst/></a:effectStyle>' +
260
+ '<a:effectStyle><a:effectLst/></a:effectStyle>' +
261
+ '<a:effectStyle><a:effectLst><a:outerShdw blurRad="57150" dist="19050" dir="5400000" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="63000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle>' +
262
+ '</a:effectStyleLst>' +
263
+ '<a:bgFillStyleLst>' +
264
+ '<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>' +
265
+ '<a:solidFill><a:schemeClr val="phClr"><a:tint val="95000"/><a:satMod val="170000"/></a:schemeClr></a:solidFill>' +
266
+ '<a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="93000"/><a:satMod val="150000"/><a:shade val="98000"/><a:lumMod val="102000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:tint val="98000"/><a:satMod val="130000"/><a:shade val="90000"/><a:lumMod val="103000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="63000"/><a:satMod val="120000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="5400000" scaled="0"/></a:gradFill>' +
267
+ '</a:bgFillStyleLst>' +
268
+ '</a:fmtScheme>' +
269
+ '</a:themeElements>' +
270
+ '<a:objectDefaults/>' +
271
+ '<a:extraClrSchemeLst/>' +
272
+ '</a:theme>';
@@ -0,0 +1,115 @@
1
+ import type { Font } from './style.ts';
2
+ /** The observable kind of a cell's value. Both formula shapes report as `Formula`. */
3
+ export declare const ValueType: {
4
+ readonly Null: "null";
5
+ readonly Number: "number";
6
+ readonly String: "string";
7
+ readonly Boolean: "boolean";
8
+ readonly Date: "date";
9
+ readonly Error: "error";
10
+ readonly Formula: "formula";
11
+ readonly RichText: "richText";
12
+ readonly Hyperlink: "hyperlink";
13
+ };
14
+ export type ValueType = (typeof ValueType)[keyof typeof ValueType];
15
+ /** The canonical Excel error literals a cell (or formula result) can carry. */
16
+ export declare const ERROR_CODES: readonly ["#N/A", "#REF!", "#NAME?", "#DIV/0!", "#NULL!", "#VALUE!", "#NUM!", "#SPILL!", "#CALC!", "#GETTING_DATA"];
17
+ export type ErrorCode = (typeof ERROR_CODES)[number];
18
+ /** An in-cell error, e.g. `{error: '#REF!'}`. */
19
+ export interface ErrorValue {
20
+ readonly error: ErrorCode;
21
+ }
22
+ /** One formatted run of a rich-text value. */
23
+ export interface RichTextRun {
24
+ readonly text: string;
25
+ readonly font?: Font;
26
+ }
27
+ /** A value composed of independently-formatted text runs. */
28
+ export interface RichTextValue {
29
+ readonly richText: readonly RichTextRun[];
30
+ }
31
+ /** A hyperlink cell: a URL plus the text (plain or rich) shown in the cell. */
32
+ export interface HyperlinkValue {
33
+ readonly hyperlink: string;
34
+ readonly text: string | RichTextValue;
35
+ readonly tooltip?: string;
36
+ /** The clickable extent (`'D1:H1'`) when the link spans a range whose top-left corner is this
37
+ * cell. Absent for an ordinary single-cell link. The destination and label live on the top-left
38
+ * cell; `range` records how far Excel highlights the clickable area so it survives a round-trip. */
39
+ readonly range?: string;
40
+ }
41
+ /** The cached result a formula carries — any scalar, a date, or an error. */
42
+ export type FormulaResult = number | string | boolean | Date | ErrorValue;
43
+ /** A cell whose value is computed by its own formula. */
44
+ export interface FormulaValue {
45
+ readonly formula: string;
46
+ readonly result?: FormulaResult;
47
+ }
48
+ /**
49
+ * A cell that participates in a shared formula — a clone of a master formula cell filled across a
50
+ * range. `sharedFormula` is the master cell's address (e.g. `'B1'`); the master itself is a plain
51
+ * {@link FormulaValue}. On read, the clone's own formula is the master's translated to the clone's
52
+ * position and `result` is the clone's cached value; on write, the clones of a master collapse into
53
+ * OOXML's shared-formula grouping.
54
+ */
55
+ export interface SharedFormulaValue {
56
+ readonly sharedFormula: string;
57
+ /** The master's formula translated to this cell's position. Filled in on read; a clone assigned by
58
+ * a caller carries only `sharedFormula`, and the writer recovers the formula from the master. */
59
+ readonly formula?: string;
60
+ readonly result?: FormulaResult;
61
+ }
62
+ /**
63
+ * A cell computed by a What-If-Analysis data table (`<f t="dataTable">`) — the OOXML formula kind that
64
+ * fills a range by re-evaluating a model against a grid of substituted input cells. The library does
65
+ * not evaluate it; it preserves the declaration so a read-modify-write cycle re-emits it verbatim
66
+ * rather than silently dropping the data-table kind.
67
+ */
68
+ export interface DataTableFormulaValue {
69
+ readonly shareType: 'dataTable';
70
+ /** The range the data table fills, e.g. `'B2:B5'`. */
71
+ readonly ref: string;
72
+ /** Whether the table substitutes two inputs (a 2-D data table) rather than one. */
73
+ readonly dataTable2D?: boolean;
74
+ /** For a 1-D table, whether the input runs along the row rather than down the column. */
75
+ readonly dataTableRow?: boolean;
76
+ /** The first (row) input-cell reference. */
77
+ readonly r1?: string;
78
+ /** The second (column) input-cell reference, present for a 2-D table. */
79
+ readonly r2?: string;
80
+ readonly result?: FormulaResult;
81
+ }
82
+ /** Everything a cell's value can be. `null` is the empty cell. */
83
+ export type CellValue = null | number | string | boolean | Date | ErrorValue | FormulaValue | SharedFormulaValue | DataTableFormulaValue | RichTextValue | HyperlinkValue;
84
+ export declare function isErrorValue(value: CellValue): value is ErrorValue;
85
+ export declare function isFormulaValue(value: CellValue): value is FormulaValue;
86
+ export declare function isSharedFormulaValue(value: CellValue): value is SharedFormulaValue;
87
+ export declare function isDataTableFormulaValue(value: CellValue): value is DataTableFormulaValue;
88
+ export declare function isRichTextValue(value: CellValue): value is RichTextValue;
89
+ export declare function isHyperlinkValue(value: CellValue): value is HyperlinkValue;
90
+ /**
91
+ * Flatten a rich-text value to its plain text by concatenating every run's text in order. This is the
92
+ * text a consumer that cannot render per-run formatting (a CSV field, a pivot cache entry) sees, and
93
+ * the string a rich cell reads as when its formatting is discarded.
94
+ */
95
+ export declare function richTextToPlain(value: RichTextValue): string;
96
+ /**
97
+ * Classify a value into its observable {@link ValueType}. This is total over
98
+ * {@link CellValue}: every legal value has exactly one type. A `Date` is a date even
99
+ * when its time is `NaN` (an invalid date is still a date-typed cell); serialization,
100
+ * not the model, decides what to do with it.
101
+ */
102
+ export declare function detectValueType(value: CellValue): ValueType;
103
+ /** Whether a string is one of Excel's canonical error literals. */
104
+ export declare function isErrorCode(text: string): text is ErrorCode;
105
+ /**
106
+ * Normalise a raw assignment into a stored {@link CellValue}. `undefined` becomes the
107
+ * empty cell (`null`); every other kind is validated by {@link detectValueType}. The
108
+ * model never rewrites one value *kind* into another (a numeric-looking string stays a
109
+ * string) — the single exception is formula text, which is canonicalised to the OOXML
110
+ * stored form (no leading `=`) so round-trips are idempotent regardless of how the
111
+ * caller supplied it.
112
+ *
113
+ * @throws {TypeError} if the value is not a recognised cell-value shape.
114
+ */
115
+ export declare function coerceCellValue(value: CellValue | undefined): CellValue;
@@ -0,0 +1,144 @@
1
+ // The cell value model — the second bedrock primitive after addressing.
2
+ //
3
+ // A cell holds exactly one value, and that value's *type* is observable and drives
4
+ // everything downstream (serialization, number-format application, formula results).
5
+ // The honest shape here is a discriminated union: a value is either a JS primitive
6
+ // (null / number / string / boolean / Date) or one of the structural OOXML value
7
+ // shapes (error, formula, shared formula, rich text, hyperlink). There is no
8
+ // stringly-typed sentinel and no silent coercion between kinds — a numeric-looking
9
+ // string stays a string, because the caller's chosen type is the source of truth.
10
+ /** The observable kind of a cell's value. Both formula shapes report as `Formula`. */
11
+ export const ValueType = {
12
+ Null: 'null',
13
+ Number: 'number',
14
+ String: 'string',
15
+ Boolean: 'boolean',
16
+ Date: 'date',
17
+ Error: 'error',
18
+ Formula: 'formula',
19
+ RichText: 'richText',
20
+ Hyperlink: 'hyperlink',
21
+ };
22
+ /** The canonical Excel error literals a cell (or formula result) can carry. */
23
+ export const ERROR_CODES = [
24
+ '#N/A',
25
+ '#REF!',
26
+ '#NAME?',
27
+ '#DIV/0!',
28
+ '#NULL!',
29
+ '#VALUE!',
30
+ '#NUM!',
31
+ '#SPILL!',
32
+ '#CALC!',
33
+ '#GETTING_DATA',
34
+ ];
35
+ const ERROR_SET = new Set(ERROR_CODES);
36
+ // Whether a value is a non-null object carrying `key` — the object-shaped {@link CellValue} kinds are all
37
+ // discriminated by the presence of a single property, so every guard below narrows through this one test.
38
+ function hasKey(value, key) {
39
+ return typeof value === 'object' && value !== null && key in value;
40
+ }
41
+ export function isErrorValue(value) {
42
+ return hasKey(value, 'error');
43
+ }
44
+ export function isFormulaValue(value) {
45
+ // A shared-formula clone resolved on read carries both its master address (`sharedFormula`) and the
46
+ // translated `formula`; it is a SharedFormulaValue, so exclude it here to keep the two kinds distinct.
47
+ return hasKey(value, 'formula') && !('sharedFormula' in value);
48
+ }
49
+ export function isSharedFormulaValue(value) {
50
+ return hasKey(value, 'sharedFormula');
51
+ }
52
+ export function isDataTableFormulaValue(value) {
53
+ return hasKey(value, 'shareType') && value.shareType === 'dataTable';
54
+ }
55
+ export function isRichTextValue(value) {
56
+ return hasKey(value, 'richText');
57
+ }
58
+ export function isHyperlinkValue(value) {
59
+ return hasKey(value, 'hyperlink');
60
+ }
61
+ /**
62
+ * Flatten a rich-text value to its plain text by concatenating every run's text in order. This is the
63
+ * text a consumer that cannot render per-run formatting (a CSV field, a pivot cache entry) sees, and
64
+ * the string a rich cell reads as when its formatting is discarded.
65
+ */
66
+ export function richTextToPlain(value) {
67
+ return value.richText.map((run) => run.text).join('');
68
+ }
69
+ /**
70
+ * Classify a value into its observable {@link ValueType}. This is total over
71
+ * {@link CellValue}: every legal value has exactly one type. A `Date` is a date even
72
+ * when its time is `NaN` (an invalid date is still a date-typed cell); serialization,
73
+ * not the model, decides what to do with it.
74
+ */
75
+ export function detectValueType(value) {
76
+ if (value === null)
77
+ return ValueType.Null;
78
+ switch (typeof value) {
79
+ case 'number':
80
+ return ValueType.Number;
81
+ case 'string':
82
+ return ValueType.String;
83
+ case 'boolean':
84
+ return ValueType.Boolean;
85
+ default:
86
+ break;
87
+ }
88
+ if (value instanceof Date)
89
+ return ValueType.Date;
90
+ // Order matters: a hyperlink whose text is rich must classify as Hyperlink, and a
91
+ // formula carrying a result must classify as Formula — check the outer shape first.
92
+ if (isHyperlinkValue(value))
93
+ return ValueType.Hyperlink;
94
+ if (isFormulaValue(value) || isSharedFormulaValue(value) || isDataTableFormulaValue(value)) {
95
+ return ValueType.Formula;
96
+ }
97
+ if (isRichTextValue(value))
98
+ return ValueType.RichText;
99
+ if (isErrorValue(value))
100
+ return ValueType.Error;
101
+ throw new TypeError(`unsupported cell value: ${describe(value)}`);
102
+ }
103
+ /** Whether a string is one of Excel's canonical error literals. */
104
+ export function isErrorCode(text) {
105
+ return ERROR_SET.has(text);
106
+ }
107
+ function describe(value) {
108
+ if (typeof value === 'object' && value !== null) {
109
+ return `object with keys [${Object.keys(value).join(', ')}]`;
110
+ }
111
+ return String(value);
112
+ }
113
+ /**
114
+ * Normalise a raw assignment into a stored {@link CellValue}. `undefined` becomes the
115
+ * empty cell (`null`); every other kind is validated by {@link detectValueType}. The
116
+ * model never rewrites one value *kind* into another (a numeric-looking string stays a
117
+ * string) — the single exception is formula text, which is canonicalised to the OOXML
118
+ * stored form (no leading `=`) so round-trips are idempotent regardless of how the
119
+ * caller supplied it.
120
+ *
121
+ * @throws {TypeError} if the value is not a recognised cell-value shape.
122
+ */
123
+ export function coerceCellValue(value) {
124
+ if (value === undefined)
125
+ return null;
126
+ // detectValueType throws on an unrecognised object shape, so this both validates
127
+ // and gives a precise error at the assignment site rather than deep in serialization.
128
+ detectValueType(value);
129
+ if (isFormulaValue(value)) {
130
+ const formula = stripLeadingEquals(value.formula);
131
+ return formula === value.formula ? value : { ...value, formula };
132
+ }
133
+ // A shared formula's `sharedFormula` is a master cell address, not formula text, so there is no
134
+ // leading `=` to canonicalise — it passes through as given.
135
+ return value;
136
+ }
137
+ /**
138
+ * OOXML stores a formula's text in `<f>` without the UI's leading `=`. Strip a single
139
+ * leading `=` so the stored form is canonical and a strict consumer (Google Sheets,
140
+ * WPS) — which rejects a stored formula beginning with `=` — accepts the file.
141
+ */
142
+ function stripLeadingEquals(formula) {
143
+ return formula.startsWith('=') ? formula.slice(1) : formula;
144
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The password/agile-hash attributes a `<workbookProtection>` element may carry, preserved verbatim
3
+ * so a hash-guarded workbook round-trips without the library ever interpreting the credential. Both
4
+ * the legacy 16-bit `*Password` hashes and the modern agile `*AlgorithmName`/`*HashValue`/
5
+ * `*SaltValue`/`*SpinCount` quartets appear here, for the structure guard (`workbook*`) and the
6
+ * revisions guard (`revisions*`). The reader accepts only these names, so a hostile or unknown
7
+ * attribute is never echoed back into the output.
8
+ */
9
+ export declare const WORKBOOK_PROTECTION_CREDENTIAL_ATTRS: readonly ["workbookPassword", "workbookAlgorithmName", "workbookHashValue", "workbookSaltValue", "workbookSpinCount", "revisionsPassword", "revisionsAlgorithmName", "revisionsHashValue", "revisionsSaltValue", "revisionsSpinCount"];
10
+ /** One of the attribute names {@link WORKBOOK_PROTECTION_CREDENTIAL_ATTRS} enumerates. */
11
+ export type WorkbookProtectionCredentialAttr = (typeof WORKBOOK_PROTECTION_CREDENTIAL_ATTRS)[number];
12
+ /**
13
+ * A workbook's structure/window protection. The three lock flags each default to `false` (absent),
14
+ * matching OOXML: an omitted attribute leaves that aspect unlocked. The optional {@link credentials}
15
+ * bag carries the opaque password attributes verbatim — the library never verifies a password, it
16
+ * only refuses to lose one.
17
+ */
18
+ export interface WorkbookProtection {
19
+ /** Lock the workbook structure — no adding, deleting, reordering, or unhiding sheets. */
20
+ readonly lockStructure?: boolean;
21
+ /** Lock the workbook window geometry. */
22
+ readonly lockWindows?: boolean;
23
+ /** Lock the revision-tracking state. */
24
+ readonly lockRevision?: boolean;
25
+ /** Preserved password/agile-hash attributes, keyed by their OOXML attribute name. */
26
+ readonly credentials?: Readonly<Partial<Record<WorkbookProtectionCredentialAttr, string>>>;
27
+ }
@@ -0,0 +1,25 @@
1
+ // Workbook-level protection: the `<workbookProtection>` element in CT_Workbook that locks the
2
+ // workbook's *structure* (sheets cannot be added, deleted, reordered, or unhidden) and/or its
3
+ // *windows*. It is distinct from worksheet-level `<sheetProtection>` — that guards a single sheet's
4
+ // cells; this guards the workbook shell. A weak integrity signal, but a real one: dropping it on a
5
+ // passthrough save silently unlocks a file the author locked, so the model preserves it faithfully.
6
+ /**
7
+ * The password/agile-hash attributes a `<workbookProtection>` element may carry, preserved verbatim
8
+ * so a hash-guarded workbook round-trips without the library ever interpreting the credential. Both
9
+ * the legacy 16-bit `*Password` hashes and the modern agile `*AlgorithmName`/`*HashValue`/
10
+ * `*SaltValue`/`*SpinCount` quartets appear here, for the structure guard (`workbook*`) and the
11
+ * revisions guard (`revisions*`). The reader accepts only these names, so a hostile or unknown
12
+ * attribute is never echoed back into the output.
13
+ */
14
+ export const WORKBOOK_PROTECTION_CREDENTIAL_ATTRS = [
15
+ 'workbookPassword',
16
+ 'workbookAlgorithmName',
17
+ 'workbookHashValue',
18
+ 'workbookSaltValue',
19
+ 'workbookSpinCount',
20
+ 'revisionsPassword',
21
+ 'revisionsAlgorithmName',
22
+ 'revisionsHashValue',
23
+ 'revisionsSaltValue',
24
+ 'revisionsSpinCount',
25
+ ];