@shbernal/ts-xlsx 3.0.0 → 3.2.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 (277) hide show
  1. package/README.md +34 -24
  2. package/dist/bytes.d.ts +10 -2
  3. package/dist/bytes.js +22 -0
  4. package/dist/core/address.d.ts +96 -2
  5. package/dist/core/address.js +87 -33
  6. package/dist/core/array-formula-ranges.d.ts +33 -0
  7. package/dist/core/array-formula-ranges.js +62 -0
  8. package/dist/core/autofilter.d.ts +4 -3
  9. package/dist/core/autofilter.js +31 -30
  10. package/dist/core/axis-handle.d.ts +2 -0
  11. package/dist/core/axis-handle.js +5 -2
  12. package/dist/core/cell.d.ts +4 -3
  13. package/dist/core/cell.js +4 -6
  14. package/dist/core/clone.d.ts +29 -0
  15. package/dist/core/clone.js +24 -0
  16. package/dist/core/color-resolution.js +4 -6
  17. package/dist/core/column.d.ts +1 -0
  18. package/dist/core/column.js +6 -2
  19. package/dist/core/comment-thread.d.ts +7 -3
  20. package/dist/core/comment-thread.js +2 -1
  21. package/dist/core/conditional-formatting-overlay.d.ts +11 -1
  22. package/dist/core/conditional-formatting-overlay.js +30 -3
  23. package/dist/core/conditional-formatting.d.ts +107 -11
  24. package/dist/core/conditional-formatting.js +76 -19
  25. package/dist/core/containers.d.ts +12 -0
  26. package/dist/core/containers.js +5 -0
  27. package/dist/core/data-validation-overlay.d.ts +7 -1
  28. package/dist/core/data-validation-overlay.js +19 -4
  29. package/dist/core/data-validation.d.ts +22 -0
  30. package/dist/core/data-validation.js +30 -4
  31. package/dist/core/date-format.d.ts +20 -0
  32. package/dist/core/date-format.js +170 -0
  33. package/dist/core/date.d.ts +43 -8
  34. package/dist/core/date.js +19 -3
  35. package/dist/core/formula-references.d.ts +26 -0
  36. package/dist/core/formula-references.js +173 -0
  37. package/dist/core/formula-scan.d.ts +2 -0
  38. package/dist/core/formula-scan.js +58 -0
  39. package/dist/core/formula.d.ts +76 -17
  40. package/dist/core/formula.js +87 -80
  41. package/dist/core/function-values.d.ts +4 -0
  42. package/dist/core/function-values.js +58 -0
  43. package/dist/core/future-functions.d.ts +3 -0
  44. package/dist/core/{modern-functions.js → future-functions.js} +119 -88
  45. package/dist/core/grid-edits.d.ts +30 -3
  46. package/dist/core/grid-edits.js +248 -81
  47. package/dist/core/grid-shift.d.ts +58 -9
  48. package/dist/core/grid-shift.js +26 -4
  49. package/dist/core/hyperlink.d.ts +39 -0
  50. package/dist/core/hyperlink.js +88 -0
  51. package/dist/core/image.d.ts +49 -14
  52. package/dist/core/image.js +16 -5
  53. package/dist/core/internal.d.ts +10 -2
  54. package/dist/core/limits.d.ts +25 -0
  55. package/dist/core/limits.js +4 -0
  56. package/dist/core/merge.d.ts +18 -4
  57. package/dist/core/merge.js +33 -20
  58. package/dist/core/page-setup.d.ts +26 -11
  59. package/dist/core/pivot-table.d.ts +49 -10
  60. package/dist/core/pivot-table.js +112 -60
  61. package/dist/core/protection.d.ts +66 -4
  62. package/dist/core/protection.js +6 -11
  63. package/dist/core/range.js +34 -30
  64. package/dist/core/row-input.js +2 -2
  65. package/dist/core/row.d.ts +1 -0
  66. package/dist/core/row.js +6 -2
  67. package/dist/core/structured-reference.d.ts +7 -0
  68. package/dist/core/structured-reference.js +7 -0
  69. package/dist/core/style.d.ts +85 -70
  70. package/dist/core/style.js +92 -28
  71. package/dist/core/table-style.d.ts +1 -1
  72. package/dist/core/table-style.js +4 -4
  73. package/dist/core/table.d.ts +64 -16
  74. package/dist/core/table.js +87 -40
  75. package/dist/core/theme.d.ts +2 -0
  76. package/dist/core/theme.js +9 -5
  77. package/dist/core/value.d.ts +60 -28
  78. package/dist/core/value.js +48 -43
  79. package/dist/core/workbook-media.d.ts +34 -0
  80. package/dist/core/workbook-media.js +44 -0
  81. package/dist/core/workbook-styles.d.ts +83 -1
  82. package/dist/core/workbook-styles.js +4 -0
  83. package/dist/core/workbook-theme.js +5 -3
  84. package/dist/core/workbook-vba.js +3 -2
  85. package/dist/core/workbook.d.ts +41 -3
  86. package/dist/core/workbook.js +46 -41
  87. package/dist/core/worksheet-comments.d.ts +2 -1
  88. package/dist/core/worksheet-comments.js +20 -13
  89. package/dist/core/worksheet-merges.d.ts +49 -0
  90. package/dist/core/worksheet-merges.js +65 -0
  91. package/dist/core/worksheet-model.d.ts +10 -2
  92. package/dist/core/worksheet-model.js +28 -9
  93. package/dist/core/worksheet-pictures.d.ts +3 -3
  94. package/dist/core/worksheet-pictures.js +7 -6
  95. package/dist/core/worksheet.d.ts +165 -27
  96. package/dist/core/worksheet.js +206 -89
  97. package/dist/customui/ribbon.js +2 -1
  98. package/dist/entries/core.d.ts +21 -14
  99. package/dist/entries/core.js +14 -7
  100. package/dist/entries/errors.d.ts +1 -0
  101. package/dist/entries/errors.js +1 -0
  102. package/dist/entries/xlsx.d.ts +1 -1
  103. package/dist/errors.d.ts +64 -0
  104. package/dist/errors.js +22 -0
  105. package/dist/hex.d.ts +8 -0
  106. package/dist/hex.js +3 -0
  107. package/dist/io/cell-metadata/metadata.d.ts +42 -0
  108. package/dist/io/cell-metadata/metadata.js +35 -0
  109. package/dist/io/cell-metadata/rich-values.d.ts +11 -0
  110. package/dist/io/cell-metadata/rich-values.js +80 -0
  111. package/dist/io/csv/delimiter.d.ts +21 -0
  112. package/dist/io/csv/delimiter.js +18 -0
  113. package/dist/io/csv/errors.d.ts +14 -0
  114. package/dist/io/csv/errors.js +5 -0
  115. package/dist/io/csv/read.d.ts +6 -1
  116. package/dist/io/csv/read.js +51 -22
  117. package/dist/io/csv/write.d.ts +19 -5
  118. package/dist/io/csv/write.js +24 -44
  119. package/dist/io/opc/inflate.js +14 -0
  120. package/dist/io/opc/namespaces.d.ts +16 -0
  121. package/dist/io/opc/namespaces.js +4 -0
  122. package/dist/io/opc/part-paths.d.ts +9 -0
  123. package/dist/io/opc/part-paths.js +5 -5
  124. package/dist/io/opc/read-opc.d.ts +58 -14
  125. package/dist/io/opc/read-opc.js +49 -21
  126. package/dist/io/opc/read-options.d.ts +5 -1
  127. package/dist/io/opc/sniff-format.d.ts +3 -3
  128. package/dist/io/opc/sniff-format.js +2 -2
  129. package/dist/io/opc/strict-relationships.d.ts +2 -0
  130. package/dist/io/opc/strict-relationships.js +12 -0
  131. package/dist/io/read-policy/column-budget.d.ts +26 -0
  132. package/dist/io/read-policy/column-budget.js +18 -0
  133. package/dist/io/read-policy/read-repair.d.ts +74 -0
  134. package/dist/io/read-policy/read-repair.js +119 -0
  135. package/dist/io/style/cell-style-resolution.d.ts +25 -0
  136. package/dist/io/style/cell-style-resolution.js +30 -0
  137. package/dist/io/style/xf-style.d.ts +23 -1
  138. package/dist/io/style/xf-style.js +8 -0
  139. package/dist/io/xlsb/formula.d.ts +2 -2
  140. package/dist/io/xlsb/formula.js +52 -35
  141. package/dist/io/xlsb/primitives.js +8 -14
  142. package/dist/io/xlsb/ptg-functions.d.ts +19 -10
  143. package/dist/io/xlsb/ptg-functions.js +495 -596
  144. package/dist/io/xlsb/read-metadata.d.ts +7 -0
  145. package/dist/io/xlsb/read-metadata.js +69 -0
  146. package/dist/io/xlsb/read-styles.js +45 -81
  147. package/dist/io/xlsb/read-worksheet.d.ts +26 -1
  148. package/dist/io/xlsb/read-worksheet.js +148 -106
  149. package/dist/io/xlsb/read.d.ts +6 -3
  150. package/dist/io/xlsb/read.js +70 -31
  151. package/dist/io/xlsb/record-stream.d.ts +30 -0
  152. package/dist/io/xlsb/record-stream.js +20 -0
  153. package/dist/io/xlsb/record-types.d.ts +14 -5
  154. package/dist/io/xlsb/record-types.js +14 -5
  155. package/dist/io/xlsb/sheet-protection.d.ts +20 -0
  156. package/dist/io/xlsb/sheet-protection.js +60 -0
  157. package/dist/io/xlsx/cell-accumulator.d.ts +24 -9
  158. package/dist/io/xlsx/cell-accumulator.js +74 -21
  159. package/dist/io/xlsx/cell-metadata.d.ts +39 -0
  160. package/dist/io/xlsx/cell-metadata.js +162 -0
  161. package/dist/io/xlsx/cell-value.d.ts +19 -5
  162. package/dist/io/xlsx/cell-value.js +45 -30
  163. package/dist/io/xlsx/color-xml.js +3 -2
  164. package/dist/io/xlsx/column-span.d.ts +11 -0
  165. package/dist/io/xlsx/column-span.js +9 -0
  166. package/dist/io/xlsx/comments.d.ts +9 -30
  167. package/dist/io/xlsx/comments.js +25 -83
  168. package/dist/io/xlsx/conditional-formatting.d.ts +43 -23
  169. package/dist/io/xlsx/conditional-formatting.js +437 -138
  170. package/dist/io/xlsx/data-validation.d.ts +12 -7
  171. package/dist/io/xlsx/data-validation.js +67 -53
  172. package/dist/io/xlsx/edit-vba.d.ts +7 -2
  173. package/dist/io/xlsx/edit-vba.js +52 -52
  174. package/dist/io/xlsx/font-xml.d.ts +7 -0
  175. package/dist/io/xlsx/font-xml.js +104 -0
  176. package/dist/io/xlsx/hyperlinks.d.ts +19 -21
  177. package/dist/io/xlsx/hyperlinks.js +12 -32
  178. package/dist/io/xlsx/images.d.ts +49 -16
  179. package/dist/io/xlsx/images.js +149 -50
  180. package/dist/io/xlsx/namespaces.d.ts +24 -0
  181. package/dist/io/xlsx/namespaces.js +11 -0
  182. package/dist/io/xlsx/package-plan.d.ts +60 -15
  183. package/dist/io/xlsx/package-plan.js +83 -18
  184. package/dist/io/xlsx/part-names.d.ts +6 -8
  185. package/dist/io/xlsx/part-names.js +3 -7
  186. package/dist/io/xlsx/pivot.d.ts +5 -5
  187. package/dist/io/xlsx/pivot.js +26 -24
  188. package/dist/io/xlsx/preserved-splices.d.ts +6 -0
  189. package/dist/io/xlsx/preserved-splices.js +74 -0
  190. package/dist/io/xlsx/read-comments.d.ts +29 -0
  191. package/dist/io/xlsx/read-comments.js +75 -0
  192. package/dist/io/xlsx/read-parts.d.ts +59 -0
  193. package/dist/io/xlsx/read-parts.js +259 -0
  194. package/dist/io/xlsx/read-pivot.js +11 -4
  195. package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +8 -5
  196. package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +24 -4
  197. package/dist/io/xlsx/read-rows.d.ts +35 -11
  198. package/dist/io/xlsx/read-rows.js +73 -49
  199. package/dist/io/xlsx/read-shared-strings.js +3 -2
  200. package/dist/io/xlsx/read-styles.d.ts +0 -30
  201. package/dist/io/xlsx/read-styles.js +45 -105
  202. package/dist/io/xlsx/read-workbook-xml.d.ts +42 -0
  203. package/dist/io/xlsx/read-workbook-xml.js +175 -0
  204. package/dist/io/xlsx/read-worksheet.d.ts +9 -3
  205. package/dist/io/xlsx/read-worksheet.js +70 -177
  206. package/dist/io/xlsx/read.d.ts +3 -14
  207. package/dist/io/xlsx/read.js +74 -411
  208. package/dist/io/xlsx/relationships.d.ts +3 -0
  209. package/dist/io/xlsx/relationships.js +4 -0
  210. package/dist/io/xlsx/rich-text.js +1 -1
  211. package/dist/io/xlsx/row-position.d.ts +20 -0
  212. package/dist/io/xlsx/row-position.js +11 -0
  213. package/dist/io/xlsx/row-xml.d.ts +139 -0
  214. package/dist/io/xlsx/row-xml.js +222 -0
  215. package/dist/io/xlsx/shared-formulas.js +2 -2
  216. package/dist/io/xlsx/sheet-properties.d.ts +10 -1
  217. package/dist/io/xlsx/sheet-properties.js +182 -20
  218. package/dist/io/xlsx/strict-parts.d.ts +7 -0
  219. package/dist/io/xlsx/strict-parts.js +171 -0
  220. package/dist/io/xlsx/style-elements.d.ts +41 -0
  221. package/dist/io/xlsx/style-elements.js +136 -0
  222. package/dist/io/xlsx/styles.d.ts +17 -3
  223. package/dist/io/xlsx/styles.js +13 -209
  224. package/dist/io/xlsx/tables.d.ts +10 -2
  225. package/dist/io/xlsx/tables.js +55 -52
  226. package/dist/io/xlsx/theme-xml.d.ts +32 -4
  227. package/dist/io/xlsx/theme-xml.js +106 -71
  228. package/dist/io/xlsx/threaded-comments.d.ts +14 -11
  229. package/dist/io/xlsx/threaded-comments.js +16 -8
  230. package/dist/io/xlsx/workbook-xml.d.ts +45 -6
  231. package/dist/io/xlsx/workbook-xml.js +62 -54
  232. package/dist/io/xlsx/worksheet-xml.d.ts +26 -70
  233. package/dist/io/xlsx/worksheet-xml.js +69 -282
  234. package/dist/io/xlsx/write-stream.d.ts +90 -19
  235. package/dist/io/xlsx/write-stream.js +80 -25
  236. package/dist/io/xlsx/write.d.ts +12 -6
  237. package/dist/io/xlsx/write.js +226 -123
  238. package/dist/rel-type.d.ts +25 -0
  239. package/dist/rel-type.js +9 -0
  240. package/dist/token-set.d.ts +13 -0
  241. package/dist/token-set.js +4 -1
  242. package/dist/vba/bytes.d.ts +16 -5
  243. package/dist/vba/bytes.js +27 -6
  244. package/dist/vba/cfb-format.d.ts +55 -0
  245. package/dist/vba/cfb-format.js +35 -0
  246. package/dist/vba/cfb-writer.d.ts +8 -0
  247. package/dist/vba/cfb-writer.js +40 -40
  248. package/dist/vba/cfb.d.ts +21 -4
  249. package/dist/vba/cfb.js +124 -90
  250. package/dist/vba/codepage.js +2 -1
  251. package/dist/vba/dir-records.d.ts +6 -0
  252. package/dist/vba/dir-records.js +10 -2
  253. package/dist/vba/errors.d.ts +11 -4
  254. package/dist/vba/index.d.ts +0 -2
  255. package/dist/vba/index.js +0 -2
  256. package/dist/vba/ms-ovba.d.ts +6 -2
  257. package/dist/vba/ms-ovba.js +66 -22
  258. package/dist/vba/project-editor.js +129 -119
  259. package/dist/vba/project.d.ts +15 -1
  260. package/dist/vba/project.js +38 -13
  261. package/dist/vba/vba-encoding.d.ts +4 -10
  262. package/dist/vba/vba-encoding.js +3 -17
  263. package/dist/xml/xml-attrs.d.ts +37 -0
  264. package/dist/xml/xml-attrs.js +30 -0
  265. package/dist/xml/xml-chars.d.ts +40 -0
  266. package/dist/xml/xml-chars.js +12 -0
  267. package/dist/xml/xml-namespaces.d.ts +31 -0
  268. package/dist/xml/xml-namespaces.js +50 -0
  269. package/dist/xml/xml-read.d.ts +101 -11
  270. package/dist/xml/xml-read.js +150 -18
  271. package/dist/xml/xml-scan.d.ts +25 -34
  272. package/dist/xml/xml-scan.js +69 -46
  273. package/dist/xml/xml.d.ts +59 -26
  274. package/dist/xml/xml.js +45 -23
  275. package/package.json +15 -9
  276. package/skills/ts-xlsx-upstream/SKILL.md +21 -13
  277. package/dist/core/modern-functions.d.ts +0 -1
@@ -0,0 +1,39 @@
1
+ import type { ErrorCode } from '../../core/value.ts';
2
+ import { type CellMetadataIndex, type WorkbookMetadata } from '../cell-metadata/metadata.ts';
3
+ import type { PartRelationships } from '../opc/read-opc.ts';
4
+ /**
5
+ * What every `cm` and `vm` in the workbook resolves to: the metadata part and the two rich-value parts,
6
+ * each found through the workbook's relationships by type. Both worksheet readers resolve their cells
7
+ * through this one answer.
8
+ */
9
+ export declare function readCellMetadata(workbookRels: PartRelationships): CellMetadataIndex;
10
+ /** Read `xl/metadata.xml` as far as a cell's `cm` and `vm` reach into it. */
11
+ export declare function parseMetadataPart(xml: string): WorkbookMetadata;
12
+ /**
13
+ * Records what the cells written so far point into, so the metadata part and the rich-value parts are
14
+ * emitted only for a workbook that needs them. Filled during the sheet pass, as the shared-strings table
15
+ * is, because the streaming writer renders a row long before the package around it is assembled.
16
+ */
17
+ export declare class WorkbookMetadataTable {
18
+ #private;
19
+ /** The `cm` a dynamic-array formula's cell is written with. Every such cell shares the one block. */
20
+ markDynamicArray(): number;
21
+ /**
22
+ * The `vm` a cell holding `error` is written with, beside a `<v>` of `#VALUE!`, or `undefined` for
23
+ * an error Excel reads literally, which needs no metadata.
24
+ */
25
+ markRichValueError(error: ErrorCode): number | undefined;
26
+ /** Whether no cell points into the metadata part; the writer omits it entirely when so. */
27
+ get isEmpty(): boolean;
28
+ /** Whether a cell points at a rich value, so the rich-value parts are emitted beside the metadata. */
29
+ get hasRichValues(): boolean;
30
+ /** Serialise the `xl/metadata.xml` part, in the form Excel saves it. */
31
+ toXml(): string;
32
+ /**
33
+ * Serialise `xl/richData/rdrichvalue.xml`: one rich value per error, in the order the `vm`s were handed
34
+ * out, each naming its `errorType` and the unknown sub-type, 0.
35
+ */
36
+ richValuesXml(): string;
37
+ }
38
+ /** `xl/richData/rdrichvaluestructure.xml`: the one structure every rich value the writer emits has. */
39
+ export declare const RICH_VALUE_STRUCTURES_XML: string;
@@ -0,0 +1,162 @@
1
+ import { numInteger } from '../../xml/xml-attrs.js';
2
+ import { parseXml } from '../../xml/xml-read.js';
3
+ import { boolStrict, localName } from '../../xml/xml-scan.js';
4
+ import { XML_DECLARATION } from '../../xml/xml.js';
5
+ import { DYNAMIC_ARRAY_TYPE, indexCellMetadata, RICH_VALUE_TYPE, } from '../cell-metadata/metadata.js';
6
+ import { ERROR_STRUCTURE, parseRichValueErrors, RICH_VALUE_ERROR_TYPES, } from '../cell-metadata/rich-values.js';
7
+ import { DYNAMIC_ARRAY_NS, DYNAMIC_ARRAY_PROPERTIES_EXT_URI, RICH_DATA_NS, RICH_VALUE_BLOCK_EXT_URI, SPREADSHEETML_NS, } from './namespaces.js';
8
+ export function readCellMetadata(workbookRels) {
9
+ return indexCellMetadata(parseMetadataPart(workbookRels.relatedText('sheetMetadata') ?? ''), parseRichValueErrors(workbookRels.relatedText('rdRichValueStructure') ?? '', workbookRels.relatedText('rdRichValue') ?? ''));
10
+ }
11
+ export function parseMetadataPart(xml) {
12
+ const typeNames = [];
13
+ const dynamicArrayBlocks = [];
14
+ const richValueBlocks = [];
15
+ const cellBlocks = [];
16
+ const valueBlocks = [];
17
+ let container;
18
+ const records = () => container === 'cells'
19
+ ? cellBlocks.at(-1)
20
+ : container === 'values'
21
+ ? valueBlocks.at(-1)
22
+ : undefined;
23
+ if (xml === '')
24
+ return { typeNames, dynamicArrayBlocks, richValueBlocks, cellBlocks, valueBlocks };
25
+ parseXml(xml, {
26
+ onOpen(name, attrs, selfClosing) {
27
+ switch (localName(name)) {
28
+ case 'metadataType':
29
+ typeNames.push(attrs.name ?? '');
30
+ break;
31
+ case 'futureMetadata':
32
+ container = selfClosing
33
+ ? undefined
34
+ : attrs.name === DYNAMIC_ARRAY_TYPE
35
+ ? 'dynamicArrays'
36
+ : attrs.name === RICH_VALUE_TYPE
37
+ ? 'richValues'
38
+ : undefined;
39
+ break;
40
+ case 'cellMetadata':
41
+ container = selfClosing ? undefined : 'cells';
42
+ break;
43
+ case 'valueMetadata':
44
+ container = selfClosing ? undefined : 'values';
45
+ break;
46
+ case 'bk':
47
+ if (container === 'dynamicArrays')
48
+ dynamicArrayBlocks.push(false);
49
+ else if (container === 'richValues')
50
+ richValueBlocks.push(-1);
51
+ else if (container === 'cells')
52
+ cellBlocks.push([]);
53
+ else if (container === 'values')
54
+ valueBlocks.push([]);
55
+ break;
56
+ case 'dynamicArrayProperties':
57
+ if (container === 'dynamicArrays' && dynamicArrayBlocks.length > 0) {
58
+ dynamicArrayBlocks[dynamicArrayBlocks.length - 1] = boolStrict(attrs.fDynamic);
59
+ }
60
+ break;
61
+ case 'rvb':
62
+ if (container === 'richValues' && richValueBlocks.length > 0) {
63
+ richValueBlocks[richValueBlocks.length - 1] = numInteger(attrs.i, 0) ?? -1;
64
+ }
65
+ break;
66
+ case 'rc': {
67
+ const block = records();
68
+ const type = numInteger(attrs.t, 1);
69
+ const value = numInteger(attrs.v, 0);
70
+ if (block !== undefined && type !== undefined && value !== undefined) {
71
+ block.push({ type, value });
72
+ }
73
+ break;
74
+ }
75
+ default:
76
+ break;
77
+ }
78
+ },
79
+ onClose(name) {
80
+ const local = localName(name);
81
+ if (local === 'futureMetadata' || local === 'cellMetadata' || local === 'valueMetadata') {
82
+ container = undefined;
83
+ }
84
+ },
85
+ });
86
+ return { typeNames, dynamicArrayBlocks, richValueBlocks, cellBlocks, valueBlocks };
87
+ }
88
+ const METADATA_TYPE_FLAGS = 'minSupportedVersion="120000" copy="1" pasteAll="1" pasteValues="1" merge="1" splitFirst="1" ' +
89
+ 'rowColShift="1" clearFormats="1" clearComments="1" assign="1" coerce="1"';
90
+ export class WorkbookMetadataTable {
91
+ #dynamicArrays = false;
92
+ #errors = new Map();
93
+ markDynamicArray() {
94
+ this.#dynamicArrays = true;
95
+ return 1;
96
+ }
97
+ markRichValueError(error) {
98
+ if (!RICH_VALUE_ERROR_TYPES.has(error))
99
+ return undefined;
100
+ const known = this.#errors.get(error);
101
+ if (known !== undefined)
102
+ return known;
103
+ const vm = this.#errors.size + 1;
104
+ this.#errors.set(error, vm);
105
+ return vm;
106
+ }
107
+ get isEmpty() {
108
+ return !this.#dynamicArrays && this.#errors.size === 0;
109
+ }
110
+ get hasRichValues() {
111
+ return this.#errors.size > 0;
112
+ }
113
+ toXml() {
114
+ const dynamic = this.#dynamicArrays;
115
+ const errorCount = this.#errors.size;
116
+ const types = [
117
+ ...(dynamic
118
+ ? [`<metadataType name="${DYNAMIC_ARRAY_TYPE}" ${METADATA_TYPE_FLAGS} cellMeta="1"/>`]
119
+ : []),
120
+ ...(errorCount > 0
121
+ ? [`<metadataType name="${RICH_VALUE_TYPE}" ${METADATA_TYPE_FLAGS}/>`]
122
+ : []),
123
+ ];
124
+ const richValueType = types.length;
125
+ const indices = Array.from({ length: errorCount }, (_, index) => index);
126
+ return (XML_DECLARATION +
127
+ `<metadata xmlns="${SPREADSHEETML_NS}"` +
128
+ (errorCount > 0 ? ` xmlns:xlrd="${RICH_DATA_NS}"` : '') +
129
+ (dynamic ? ` xmlns:xda="${DYNAMIC_ARRAY_NS}"` : '') +
130
+ '>' +
131
+ `<metadataTypes count="${types.length}">${types.join('')}</metadataTypes>` +
132
+ (dynamic
133
+ ? `<futureMetadata name="${DYNAMIC_ARRAY_TYPE}" count="1"><bk><extLst>` +
134
+ `<ext uri="${DYNAMIC_ARRAY_PROPERTIES_EXT_URI}">` +
135
+ '<xda:dynamicArrayProperties fDynamic="1" fCollapsed="0"/>' +
136
+ '</ext></extLst></bk></futureMetadata>'
137
+ : '') +
138
+ (errorCount > 0
139
+ ? `<futureMetadata name="${RICH_VALUE_TYPE}" count="${errorCount}">` +
140
+ indices
141
+ .map((index) => `<bk><extLst><ext uri="${RICH_VALUE_BLOCK_EXT_URI}"><xlrd:rvb i="${index}"/></ext></extLst></bk>`)
142
+ .join('') +
143
+ '</futureMetadata>'
144
+ : '') +
145
+ (dynamic ? '<cellMetadata count="1"><bk><rc t="1" v="0"/></bk></cellMetadata>' : '') +
146
+ (errorCount > 0
147
+ ? `<valueMetadata count="${errorCount}">` +
148
+ indices.map((index) => `<bk><rc t="${richValueType}" v="${index}"/></bk>`).join('') +
149
+ '</valueMetadata>'
150
+ : '') +
151
+ '</metadata>');
152
+ }
153
+ richValuesXml() {
154
+ const values = [...this.#errors.keys()].map((error) => `<rv s="0"><v>${RICH_VALUE_ERROR_TYPES.get(error)}</v><v>0</v></rv>`);
155
+ return (XML_DECLARATION +
156
+ `<rvData xmlns="${RICH_DATA_NS}" count="${values.length}">${values.join('')}</rvData>`);
157
+ }
158
+ }
159
+ export const RICH_VALUE_STRUCTURES_XML = XML_DECLARATION +
160
+ `<rvStructures xmlns="${RICH_DATA_NS}" count="1">` +
161
+ `<s t="${ERROR_STRUCTURE}"><k n="errorType" t="i"/><k n="subType" t="i"/></s>` +
162
+ '</rvStructures>';
@@ -1,4 +1,5 @@
1
- import { type CellValue, type FormulaResult, type RichTextRun, type RichTextValue } from '../../core/value.ts';
1
+ import { type DateEpoch } from '../../core/date.ts';
2
+ import { type CellValue, type ErrorCode, type FormulaResult, type RichTextRun, type RichTextValue } from '../../core/value.ts';
2
3
  /**
3
4
  * One entry of the shared-strings pool. A `<si>` built from a bare `<t>` is a plain string; a `<si>`
4
5
  * built from `<r>` runs is rich text, so a `t="s"` cell can resolve to either kind, and rich text
@@ -11,8 +12,17 @@ export interface RawCell {
11
12
  readonly type: string;
12
13
  readonly hasFormula: boolean;
13
14
  readonly formula: string;
15
+ /** The range an `<f t="array">` fills when it starts at this cell, as Excel spells a `ref`. */
16
+ readonly arrayRef?: string | undefined;
17
+ /** Whether the cell's `cm` points at cell metadata marking a dynamic array. */
18
+ readonly dynamicArray?: boolean;
19
+ /** The error the cell's `vm` names through a rich value, which is the error a `t="e"` cell holds. */
20
+ readonly valueError?: ErrorCode | undefined;
21
+ /** Whether a `<v>` was present, `<v/>` included. */
14
22
  readonly hasValue: boolean;
15
23
  readonly valueText: string;
24
+ /** Whether an `<is>` was present, `<is/>` included. */
25
+ readonly hasInlineString: boolean;
16
26
  readonly inlineText: string;
17
27
  /** The formatted runs of a rich inline string, when the `<is>` held `<r>` elements rather than a
18
28
  * bare `<t>`. Absent (or empty) for a plain inline string, which decodes to `inlineText`. */
@@ -20,13 +30,17 @@ export interface RawCell {
20
30
  }
21
31
  /**
22
32
  * Decode a gathered cell into its model value. A formula cell becomes a `{formula, result?}`
23
- * object (the on-disk `_xlfn.`/`_xlpm.` mangling stripped back to the readable name); a plain
33
+ * object (the on-disk `_xlfn.`/`_xlpm.` mangling stripped back to the readable name), or an array
34
+ * formula over its range, dynamic when the cell's metadata says so; a plain
24
35
  * numeric cell under a date number format becomes a {@link Date}; everything else decodes by its
25
- * `t` type. `numFmt` is the cell's resolved number-format code, used only for date detection.
36
+ * `t` type. `numFmt` is the cell's resolved number-format code, used only for date detection, and
37
+ * `epoch` the workbook's date system, which is what a serial under such a format counts from.
38
+ * `definedNames` is the workbook's defined names as `definedNameKeys` spells them, which decide whether
39
+ * a function passed as a value sheds its `_xleta.`.
26
40
  */
27
- export declare function decodeCellContent(raw: RawCell, sharedStrings: readonly SharedString[], numFmt: string | undefined): CellValue;
41
+ export declare function decodeCellContent(raw: RawCell, sharedStrings: readonly SharedString[], numFmt: string | undefined, epoch: DateEpoch, definedNames: ReadonlySet<string>): CellValue;
28
42
  /** Decode a formula's cached `<v>` result by its `t` type, coercing a numeric result under a date
29
43
  * `numFmt` to a {@link Date} exactly as a bare numeric cell is, so a date-valued formula result
30
44
  * (e.g. `TODAY()`) reads back as a Date, not a serial. Shared by the buffered reader's shared-formula
31
45
  * clone resolution, which caches a result the same way a plain formula cell does. */
32
- export declare function decodeFormulaResult(type: string, valueText: string, numFmt?: string): FormulaResult | undefined;
46
+ export declare function decodeFormulaResult(type: string, valueText: string, numFmt: string | undefined, epoch: DateEpoch, valueError?: ErrorCode): FormulaResult | undefined;
@@ -1,58 +1,73 @@
1
- import { isDateFormat, serialToDate } from '../../core/date.js';
1
+ import { coerceDateSerial, parseDateText } from '../../core/date.js';
2
2
  import { unmangleFunctions } from '../../core/formula.js';
3
3
  import { isErrorCode, } from '../../core/value.js';
4
- import { boolStrict, decodeSpreadsheetText, numFinite } from '../../xml/xml-scan.js';
5
- export function decodeCellContent(raw, sharedStrings, numFmt) {
4
+ import { decodeSpreadsheetText, numFinite, numInteger } from '../../xml/xml-attrs.js';
5
+ import { boolTristate } from '../../xml/xml-scan.js';
6
+ export function decodeCellContent(raw, sharedStrings, numFmt, epoch, definedNames) {
6
7
  if (raw.hasFormula) {
7
- const stored = unmangleFunctions(raw.formula);
8
- const result = raw.hasValue ? decodeFormulaResult(raw.type, raw.valueText, numFmt) : undefined;
9
- return result === undefined ? { formula: stored } : { formula: stored, result };
8
+ const stored = unmangleFunctions(raw.formula, definedNames);
9
+ const result = raw.hasValue
10
+ ? decodeFormulaResult(raw.type, raw.valueText, numFmt, epoch, raw.valueError)
11
+ : undefined;
12
+ const cached = result === undefined ? {} : { result };
13
+ if (raw.arrayRef !== undefined) {
14
+ return {
15
+ shareType: 'array',
16
+ formula: stored,
17
+ ref: raw.arrayRef,
18
+ ...(raw.dynamicArray === true ? { dynamic: true } : {}),
19
+ ...cached,
20
+ };
21
+ }
22
+ return { formula: stored, ...cached };
10
23
  }
11
24
  if (raw.type === 'inlineStr' && raw.richTextRuns !== undefined && raw.richTextRuns.length > 0) {
12
25
  return { richText: raw.richTextRuns };
13
26
  }
14
- const value = decodeValue(raw.type, raw.valueText, raw.inlineText, raw.hasValue, sharedStrings);
15
- return typeof value === 'number' && numFmt !== undefined && isDateFormat(numFmt)
16
- ? serialToDate(value)
17
- : value;
27
+ const value = decodeValue(raw, sharedStrings);
28
+ return coerceDateSerial(value, numFmt, epoch);
18
29
  }
19
- function decodeValue(type, valueText, inlineText, hasValue, sharedStrings) {
20
- switch (type) {
30
+ function decodeValue(raw, sharedStrings) {
31
+ const { valueText } = raw;
32
+ switch (raw.type) {
21
33
  case 'inlineStr':
22
- return inlineText;
34
+ return raw.hasInlineString ? raw.inlineText : null;
23
35
  case 'str':
24
- return decodeSpreadsheetText(valueText);
36
+ return raw.hasValue ? decodeSpreadsheetText(valueText) : null;
25
37
  case 'd':
26
- return valueText === '' ? null : new Date(valueText);
38
+ return parseDateText(valueText);
27
39
  case 's': {
28
- const index = Number(valueText);
29
- return Number.isInteger(index) ? (sharedStrings[index] ?? '') : '';
40
+ const index = numInteger(valueText, 0);
41
+ return index === undefined ? null : (sharedStrings[index] ?? null);
30
42
  }
31
43
  case 'b':
32
- return boolStrict(valueText);
44
+ return boolTristate(valueText) ?? null;
33
45
  case 'e':
34
- return isErrorCode(valueText) ? { error: valueText } : valueText;
35
- default: {
36
- if (!hasValue)
46
+ if (valueText === '')
37
47
  return null;
48
+ return errorOf(valueText, raw.valueError);
49
+ default:
38
50
  return numFinite(valueText) ?? null;
39
- }
40
51
  }
41
52
  }
42
- export function decodeFormulaResult(type, valueText, numFmt) {
43
- const result = decodeResult(type, valueText);
44
- return typeof result === 'number' && numFmt !== undefined && isDateFormat(numFmt)
45
- ? serialToDate(result)
46
- : result;
53
+ export function decodeFormulaResult(type, valueText, numFmt, epoch, valueError) {
54
+ return coerceDateSerial(decodeResult(type, valueText, valueError), numFmt, epoch);
55
+ }
56
+ function errorOf(valueText, valueError) {
57
+ if (valueError !== undefined)
58
+ return { error: valueError };
59
+ return isErrorCode(valueText) ? { error: valueText } : valueText;
47
60
  }
48
- function decodeResult(type, valueText) {
61
+ function decodeResult(type, valueText, valueError) {
49
62
  switch (type) {
50
63
  case 'str':
51
64
  return decodeSpreadsheetText(valueText);
52
65
  case 'b':
53
- return boolStrict(valueText);
66
+ return boolTristate(valueText);
54
67
  case 'e':
55
- return isErrorCode(valueText) ? { error: valueText } : valueText;
68
+ if (valueText === '')
69
+ return undefined;
70
+ return errorOf(valueText, valueError);
56
71
  default:
57
72
  return numFinite(valueText);
58
73
  }
@@ -1,10 +1,11 @@
1
1
  import { parseArgb } from '../../core/style.js';
2
- import { numFinite, numInteger } from '../../xml/xml-scan.js';
2
+ import { quoted } from '../../errors.js';
3
+ import { numFinite, numInteger } from '../../xml/xml-attrs.js';
3
4
  import { numberText } from '../../xml/xml.js';
4
5
  function normalizeArgb(argb) {
5
6
  const rgb = parseArgb(argb);
6
7
  if (rgb === undefined) {
7
- throw new SyntaxError(`Invalid ARGB colour ${JSON.stringify(argb)}: expected 6 or 8 hexadecimal digits`);
8
+ throw new SyntaxError(`invalid ARGB colour ${quoted(argb)}: expected 6 or 8 hexadecimal digits`);
8
9
  }
9
10
  return rgb;
10
11
  }
@@ -0,0 +1,11 @@
1
+ import type { XmlAttributes } from '../../xml/xml-scan.ts';
2
+ import { type ColumnRecordBudget } from '../read-policy/column-budget.ts';
3
+ /**
4
+ * The columns a `<col min max>` element actually applies to, or `undefined` for one that applies to
5
+ * none: unreadable bounds, or anything {@link clampColumnSpan} refuses. Both XML worksheet readers
6
+ * ask this, so the two cannot read one `<col>` as covering different columns.
7
+ */
8
+ export declare function takeColumnSpan(attrs: XmlAttributes, budget: ColumnRecordBudget): {
9
+ first: number;
10
+ last: number;
11
+ } | undefined;
@@ -0,0 +1,9 @@
1
+ import { numInteger } from '../../xml/xml-attrs.js';
2
+ import { clampColumnSpan } from '../read-policy/column-budget.js';
3
+ export function takeColumnSpan(attrs, budget) {
4
+ const first = numInteger(attrs.min, 1);
5
+ const last = numInteger(attrs.max, 1);
6
+ if (first === undefined || last === undefined)
7
+ return undefined;
8
+ return clampColumnSpan(first, last, budget);
9
+ }
@@ -1,6 +1,8 @@
1
+ import type { Cell } from '../../core/cell.ts';
1
2
  import type { CommentThread } from '../../core/comment-thread.ts';
2
3
  import type { Worksheet } from '../../core/worksheet.ts';
3
- /** A comment bound for `comments{n}.xml`, paired with the coordinates the VML anchor needs. */
4
+ /** A comment bound for `comments{n}.xml`, paired with the coordinates the VML anchor needs.
5
+ */
4
6
  export interface CommentCell {
5
7
  readonly ref: string;
6
8
  /** 1-based row of the commented cell. */
@@ -28,7 +30,9 @@ export interface CommentCell {
28
30
  * Ordered by cell, row-major, the way Excel writes the list, so a fallback lands interleaved among the
29
31
  * notes rather than appended after them, and the VML shapes follow the same order.
30
32
  */
31
- export declare function collectComments(sheet: Worksheet, threads: readonly CommentThread[]): CommentCell[];
33
+ export declare function collectComments(cells: Iterable<Cell>, threads: readonly CommentThread[], alreadyCollected?: readonly CommentCell[]): CommentCell[];
34
+ /** The notes among a run of cells, for a writer that must ask before the cells are evicted. */
35
+ export declare function collectNotes(cells: Iterable<Cell>): CommentCell[];
32
36
  /**
33
37
  * The `xl/comments{n}.xml` part.
34
38
  *
@@ -44,31 +48,6 @@ export declare function commentsXml(comments: readonly CommentCell[]): string;
44
48
  * pixel-exact layout. A thread's fallback shape is `ObjectType="Note"` like any other: Excel draws the
45
49
  * threaded-comment card itself and only needs the shape to exist. */
46
50
  export declare function vmlDrawingXml(comments: readonly CommentCell[]): string;
47
- /** One `<comment>` read back from a comments part. */
48
- export interface ParsedComment {
49
- readonly text: string;
50
- /**
51
- * The thread head id this comment is the legacy fallback for, read off its synthetic `tc={headId}`
52
- * author; absent for a user's own note.
53
- */
54
- readonly threadId?: string;
55
- }
56
- /**
57
- * Parse a `comments{n}.xml` part into a map of A1 reference → comment. Text runs within one comment are
58
- * concatenated; an author-name run is Excel's own convention and is not stripped, so a note reads back
59
- * as exactly the text that was written.
60
- */
61
- export declare function parseComments(xml: string): Map<string, ParsedComment>;
62
- /**
63
- * Apply a parsed comments part onto a sheet's cells as notes, addressing each by its A1 reference.
64
- *
65
- * A thread's legacy fallback is not a note and does not become one: its text is boilerplate wrapping a
66
- * copy of the conversation, so surfacing it as `cell.note` hands the caller garbage, and on write it
67
- * would be re-emitted as a plain note, destroying the `tc=`/`xr:uid` binding and leaving Excel unable to
68
- * see the thread at all.
69
- *
70
- * Suppressed only for a conversation the reader actually holds: a file whose thread part is missing or
71
- * damaged has nothing else left, so there the boilerplate is kept rather than the content lost. Call
72
- * after the sheet's threads are restored, since that is what this reads to decide.
73
- */
74
- export declare function applyNotes(sheet: Worksheet, comments: ReadonlyMap<string, ParsedComment>): void;
51
+ /** Every cell a sheet still holds, row-major: what the writer gathers a sheet's notes from, beside the
52
+ * notes the streaming writer took off the rows it already flushed. */
53
+ export declare function liveCells(sheet: Worksheet): Generator<Cell, void, undefined>;
@@ -1,20 +1,29 @@
1
1
  import { tryDecodeCellRef } from '../../core/address.js';
2
- import { parseXml } from '../../xml/xml-read.js';
3
- import { decodeSpreadsheetText, localName, numInteger } from '../../xml/xml-scan.js';
4
2
  import { escapeText, textAttr, textElement, XML_DECLARATION } from '../../xml/xml.js';
5
3
  import { MARKUP_COMPATIBILITY_NS, REVISION_NS, SPREADSHEETML_NS } from './namespaces.js';
6
- export function collectComments(sheet, threads) {
4
+ export function collectComments(cells, threads, alreadyCollected = []) {
7
5
  const fallbacks = threadFallbacks(threads);
8
6
  const anchored = new Set(fallbacks.map((fallback) => fallback.ref));
9
- const comments = [...fallbacks];
10
- for (const { cells } of sheet.rows()) {
11
- for (const cell of cells) {
12
- if (cell.note === undefined || anchored.has(cell.address))
13
- continue;
14
- comments.push({ ref: cell.address, row: cell.row, col: cell.col, text: cell.note });
15
- }
7
+ const comments = [...fallbacks, ...alreadyCollected];
8
+ for (const cell of cells) {
9
+ if (cell.note === undefined || anchored.has(cell.address))
10
+ continue;
11
+ comments.push(noteOn(cell, cell.note));
12
+ }
13
+ return comments
14
+ .filter((comment) => comment.threadId !== undefined || !anchored.has(comment.ref))
15
+ .sort((a, b) => a.row - b.row || a.col - b.col);
16
+ }
17
+ export function collectNotes(cells) {
18
+ const notes = [];
19
+ for (const cell of cells) {
20
+ if (cell.note !== undefined)
21
+ notes.push(noteOn(cell, cell.note));
16
22
  }
17
- return comments.sort((a, b) => a.row - b.row || a.col - b.col);
23
+ return notes;
24
+ }
25
+ function noteOn(cell, text) {
26
+ return { ref: cell.address, row: cell.row, col: cell.col, text };
18
27
  }
19
28
  function threadFallbacks(threads) {
20
29
  const fallbacks = [];
@@ -76,13 +85,14 @@ const VML_HEADER = '<xml xmlns:v="urn:schemas-microsoft-com:vml" ' +
76
85
  '<v:shapetype id="_x0000_t202" coordsize="21600,21600" o:spt="202" ' +
77
86
  'path="m,l,21600r21600,l21600,xe"><v:stroke joinstyle="miter"/>' +
78
87
  '<v:path gradientshapeok="t" o:connecttype="rect"/></v:shapetype>';
88
+ const FIRST_SHAPE_ID = 1025;
79
89
  export function vmlDrawingXml(comments) {
80
90
  const shapes = comments
81
91
  .map((comment, i) => {
82
92
  const row0 = comment.row - 1;
83
93
  const col0 = comment.col - 1;
84
94
  const anchor = `${col0 + 1}, 15, ${row0}, 2, ${col0 + 3}, 15, ${row0 + 4}, 4`;
85
- return (`<v:shape id="_x0000_s${1025 + i}" type="#_x0000_t202" ` +
95
+ return (`<v:shape id="_x0000_s${FIRST_SHAPE_ID + i}" type="#_x0000_t202" ` +
86
96
  'style="position:absolute;margin-left:59.25pt;margin-top:1.5pt;width:108pt;height:59.25pt;' +
87
97
  `z-index:${i + 1};visibility:hidden" fillcolor="#ffffe1" o:insetmode="auto">` +
88
98
  '<v:fill color2="#ffffe1"/><v:shadow on="t" color="black" obscured="t"/>' +
@@ -95,75 +105,7 @@ export function vmlDrawingXml(comments) {
95
105
  .join('');
96
106
  return `${VML_HEADER}${shapes}</xml>`;
97
107
  }
98
- const COMMENT_EMPTY_CLOSES = new Set(['author']);
99
- const THREAD_AUTHOR_PREFIX = 'tc=';
100
- export function parseComments(xml) {
101
- const comments = new Map();
102
- const authors = [];
103
- let currentRef;
104
- let currentAuthorId;
105
- let capture;
106
- let buffer = '';
107
- parseXml(xml, {
108
- onOpen(name, attrs) {
109
- const local = localName(name);
110
- if (local === 'comment') {
111
- currentRef = attrs.ref;
112
- currentAuthorId = attrs.authorId;
113
- buffer = '';
114
- }
115
- else if (local === 'author') {
116
- capture = 'author';
117
- buffer = '';
118
- }
119
- else if (local === 'text') {
120
- capture = 'text';
121
- }
122
- },
123
- onText(text) {
124
- if (capture !== undefined)
125
- buffer += text;
126
- },
127
- onClose(name) {
128
- const local = localName(name);
129
- if (local === 'author') {
130
- authors.push(buffer);
131
- capture = undefined;
132
- buffer = '';
133
- }
134
- else if (local === 'text') {
135
- buffer = decodeSpreadsheetText(buffer);
136
- capture = undefined;
137
- }
138
- else if (local === 'comment' && currentRef !== undefined) {
139
- const threadId = threadIdOf(authors[numInteger(currentAuthorId, 0) ?? -1]);
140
- comments.set(currentRef, {
141
- text: buffer,
142
- ...(threadId !== undefined ? { threadId } : {}),
143
- });
144
- currentRef = undefined;
145
- currentAuthorId = undefined;
146
- }
147
- },
148
- }, { closeEmptyElements: COMMENT_EMPTY_CLOSES });
149
- return comments;
150
- }
151
- function threadIdOf(author) {
152
- if (author === undefined || !author.startsWith(THREAD_AUTHOR_PREFIX))
153
- return undefined;
154
- const id = author.slice(THREAD_AUTHOR_PREFIX.length);
155
- return id === '' ? undefined : id;
156
- }
157
- export function applyNotes(sheet, comments) {
158
- const headIds = new Set(sheet.commentThreads.flatMap((thread) => {
159
- const head = thread.comments[0];
160
- return head === undefined ? [] : [head.id];
161
- }));
162
- for (const [ref, comment] of comments) {
163
- if (comment.threadId !== undefined && headIds.has(comment.threadId))
164
- continue;
165
- if (tryDecodeCellRef(ref) === undefined)
166
- continue;
167
- sheet.getCell(ref).note = comment.text;
168
- }
108
+ export function* liveCells(sheet) {
109
+ for (const { cells } of sheet.rows())
110
+ yield* cells;
169
111
  }