@shbernal/ts-xlsx 3.1.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 (214) hide show
  1. package/README.md +30 -23
  2. package/dist/bytes.d.ts +3 -2
  3. package/dist/core/address.d.ts +36 -0
  4. package/dist/core/address.js +47 -9
  5. package/dist/core/array-formula-ranges.d.ts +33 -0
  6. package/dist/core/array-formula-ranges.js +62 -0
  7. package/dist/core/color-resolution.js +2 -2
  8. package/dist/core/column.js +3 -2
  9. package/dist/core/comment-thread.d.ts +7 -3
  10. package/dist/core/conditional-formatting-overlay.d.ts +9 -0
  11. package/dist/core/conditional-formatting-overlay.js +28 -1
  12. package/dist/core/conditional-formatting.d.ts +98 -10
  13. package/dist/core/conditional-formatting.js +42 -1
  14. package/dist/core/data-validation-overlay.d.ts +5 -0
  15. package/dist/core/data-validation-overlay.js +15 -0
  16. package/dist/core/data-validation.d.ts +16 -0
  17. package/dist/core/data-validation.js +15 -0
  18. package/dist/core/date-format.js +14 -4
  19. package/dist/core/date.js +1 -1
  20. package/dist/core/formula-references.d.ts +26 -0
  21. package/dist/core/formula-references.js +173 -0
  22. package/dist/core/formula-scan.d.ts +2 -0
  23. package/dist/core/formula-scan.js +58 -0
  24. package/dist/core/formula.d.ts +51 -18
  25. package/dist/core/formula.js +77 -86
  26. package/dist/core/function-values.d.ts +4 -0
  27. package/dist/core/function-values.js +58 -0
  28. package/dist/core/future-functions.d.ts +3 -0
  29. package/dist/core/{modern-functions.js → future-functions.js} +119 -88
  30. package/dist/core/grid-edits.d.ts +28 -0
  31. package/dist/core/grid-edits.js +193 -25
  32. package/dist/core/hyperlink.d.ts +39 -0
  33. package/dist/core/hyperlink.js +88 -0
  34. package/dist/core/image.d.ts +36 -5
  35. package/dist/core/image.js +9 -0
  36. package/dist/core/internal.d.ts +3 -1
  37. package/dist/core/limits.d.ts +5 -3
  38. package/dist/core/merge.d.ts +13 -0
  39. package/dist/core/merge.js +15 -1
  40. package/dist/core/page-setup.d.ts +26 -11
  41. package/dist/core/pivot-table.d.ts +49 -10
  42. package/dist/core/pivot-table.js +55 -21
  43. package/dist/core/protection.d.ts +66 -4
  44. package/dist/core/protection.js +4 -0
  45. package/dist/core/row.js +3 -2
  46. package/dist/core/structured-reference.d.ts +7 -0
  47. package/dist/core/structured-reference.js +7 -0
  48. package/dist/core/table.d.ts +38 -8
  49. package/dist/core/table.js +37 -7
  50. package/dist/core/theme.d.ts +2 -0
  51. package/dist/core/theme.js +4 -1
  52. package/dist/core/value.d.ts +51 -28
  53. package/dist/core/value.js +15 -12
  54. package/dist/core/workbook-styles.d.ts +17 -1
  55. package/dist/core/workbook-styles.js +4 -0
  56. package/dist/core/workbook-theme.js +5 -3
  57. package/dist/core/workbook.d.ts +20 -3
  58. package/dist/core/workbook.js +26 -5
  59. package/dist/core/worksheet-merges.d.ts +9 -4
  60. package/dist/core/worksheet-merges.js +17 -11
  61. package/dist/core/worksheet-model.d.ts +1 -1
  62. package/dist/core/worksheet-model.js +4 -0
  63. package/dist/core/worksheet-pictures.d.ts +3 -3
  64. package/dist/core/worksheet-pictures.js +7 -6
  65. package/dist/core/worksheet.d.ts +110 -21
  66. package/dist/core/worksheet.js +174 -49
  67. package/dist/entries/core.d.ts +7 -5
  68. package/dist/entries/core.js +4 -3
  69. package/dist/entries/errors.d.ts +1 -0
  70. package/dist/entries/errors.js +1 -0
  71. package/dist/errors.js +1 -1
  72. package/dist/io/cell-metadata/metadata.d.ts +42 -0
  73. package/dist/io/cell-metadata/metadata.js +35 -0
  74. package/dist/io/cell-metadata/rich-values.d.ts +11 -0
  75. package/dist/io/cell-metadata/rich-values.js +80 -0
  76. package/dist/io/csv/delimiter.d.ts +14 -2
  77. package/dist/io/csv/delimiter.js +12 -0
  78. package/dist/io/csv/errors.d.ts +14 -0
  79. package/dist/io/csv/errors.js +5 -0
  80. package/dist/io/csv/read.d.ts +6 -1
  81. package/dist/io/csv/read.js +46 -24
  82. package/dist/io/csv/write.d.ts +10 -3
  83. package/dist/io/csv/write.js +16 -7
  84. package/dist/io/opc/part-paths.js +4 -4
  85. package/dist/io/opc/read-opc.d.ts +4 -0
  86. package/dist/io/opc/read-opc.js +10 -4
  87. package/dist/io/opc/strict-relationships.d.ts +2 -0
  88. package/dist/io/opc/strict-relationships.js +12 -0
  89. package/dist/io/read-policy/column-budget.d.ts +26 -0
  90. package/dist/io/{xlsx → read-policy}/column-budget.js +4 -8
  91. package/dist/io/read-policy/read-repair.d.ts +74 -0
  92. package/dist/io/read-policy/read-repair.js +119 -0
  93. package/dist/io/style/cell-style-resolution.d.ts +25 -0
  94. package/dist/io/style/cell-style-resolution.js +30 -0
  95. package/dist/io/xlsb/formula.js +4 -2
  96. package/dist/io/xlsb/ptg-functions.js +110 -0
  97. package/dist/io/xlsb/read-metadata.d.ts +7 -0
  98. package/dist/io/xlsb/read-metadata.js +69 -0
  99. package/dist/io/xlsb/read-worksheet.d.ts +6 -0
  100. package/dist/io/xlsb/read-worksheet.js +95 -37
  101. package/dist/io/xlsb/read.js +26 -8
  102. package/dist/io/xlsb/record-types.d.ts +13 -0
  103. package/dist/io/xlsb/record-types.js +13 -0
  104. package/dist/io/xlsb/sheet-protection.d.ts +20 -0
  105. package/dist/io/xlsb/sheet-protection.js +60 -0
  106. package/dist/io/xlsx/cell-accumulator.d.ts +20 -12
  107. package/dist/io/xlsx/cell-accumulator.js +49 -19
  108. package/dist/io/xlsx/cell-metadata.d.ts +39 -0
  109. package/dist/io/xlsx/cell-metadata.js +162 -0
  110. package/dist/io/xlsx/cell-value.d.ts +16 -4
  111. package/dist/io/xlsx/cell-value.js +38 -21
  112. package/dist/io/xlsx/color-xml.js +1 -1
  113. package/dist/io/xlsx/column-span.d.ts +11 -0
  114. package/dist/io/xlsx/column-span.js +9 -0
  115. package/dist/io/xlsx/comments.d.ts +3 -28
  116. package/dist/io/xlsx/comments.js +3 -65
  117. package/dist/io/xlsx/conditional-formatting.d.ts +42 -25
  118. package/dist/io/xlsx/conditional-formatting.js +424 -129
  119. package/dist/io/xlsx/data-validation.d.ts +12 -7
  120. package/dist/io/xlsx/data-validation.js +41 -29
  121. package/dist/io/xlsx/edit-vba.js +43 -36
  122. package/dist/io/xlsx/hyperlinks.d.ts +16 -50
  123. package/dist/io/xlsx/hyperlinks.js +8 -37
  124. package/dist/io/xlsx/images.d.ts +49 -16
  125. package/dist/io/xlsx/images.js +145 -44
  126. package/dist/io/xlsx/namespaces.d.ts +8 -0
  127. package/dist/io/xlsx/namespaces.js +4 -0
  128. package/dist/io/xlsx/package-plan.d.ts +48 -15
  129. package/dist/io/xlsx/package-plan.js +46 -9
  130. package/dist/io/xlsx/part-names.d.ts +6 -8
  131. package/dist/io/xlsx/part-names.js +3 -7
  132. package/dist/io/xlsx/pivot.d.ts +5 -5
  133. package/dist/io/xlsx/pivot.js +23 -22
  134. package/dist/io/xlsx/preserved-splices.d.ts +6 -0
  135. package/dist/io/xlsx/preserved-splices.js +74 -0
  136. package/dist/io/xlsx/read-comments.d.ts +29 -0
  137. package/dist/io/xlsx/read-comments.js +75 -0
  138. package/dist/io/xlsx/read-parts.d.ts +2 -2
  139. package/dist/io/xlsx/read-parts.js +44 -37
  140. package/dist/io/xlsx/read-pivot.js +9 -3
  141. package/dist/io/xlsx/read-rich-runs.d.ts +8 -5
  142. package/dist/io/xlsx/read-rich-runs.js +18 -1
  143. package/dist/io/xlsx/read-rows.d.ts +18 -7
  144. package/dist/io/xlsx/read-rows.js +47 -37
  145. package/dist/io/xlsx/read-styles.d.ts +0 -25
  146. package/dist/io/xlsx/read-styles.js +39 -34
  147. package/dist/io/xlsx/read-workbook-xml.d.ts +6 -0
  148. package/dist/io/xlsx/read-workbook-xml.js +9 -4
  149. package/dist/io/xlsx/read-worksheet.d.ts +8 -3
  150. package/dist/io/xlsx/read-worksheet.js +45 -23
  151. package/dist/io/xlsx/read.d.ts +0 -4
  152. package/dist/io/xlsx/read.js +17 -21
  153. package/dist/io/xlsx/relationships.d.ts +3 -0
  154. package/dist/io/xlsx/relationships.js +4 -0
  155. package/dist/io/xlsx/row-position.d.ts +3 -1
  156. package/dist/io/xlsx/row-xml.d.ts +18 -7
  157. package/dist/io/xlsx/row-xml.js +54 -35
  158. package/dist/io/xlsx/sheet-properties.js +68 -42
  159. package/dist/io/xlsx/strict-parts.d.ts +7 -0
  160. package/dist/io/xlsx/strict-parts.js +171 -0
  161. package/dist/io/xlsx/style-elements.d.ts +0 -2
  162. package/dist/io/xlsx/style-elements.js +9 -29
  163. package/dist/io/xlsx/styles.d.ts +5 -0
  164. package/dist/io/xlsx/styles.js +11 -5
  165. package/dist/io/xlsx/tables.d.ts +10 -2
  166. package/dist/io/xlsx/tables.js +49 -42
  167. package/dist/io/xlsx/theme-xml.d.ts +6 -0
  168. package/dist/io/xlsx/theme-xml.js +16 -19
  169. package/dist/io/xlsx/threaded-comments.d.ts +6 -1
  170. package/dist/io/xlsx/threaded-comments.js +14 -7
  171. package/dist/io/xlsx/workbook-xml.d.ts +13 -24
  172. package/dist/io/xlsx/workbook-xml.js +15 -33
  173. package/dist/io/xlsx/worksheet-xml.d.ts +5 -2
  174. package/dist/io/xlsx/worksheet-xml.js +37 -69
  175. package/dist/io/xlsx/write-stream.d.ts +28 -6
  176. package/dist/io/xlsx/write-stream.js +42 -17
  177. package/dist/io/xlsx/write.d.ts +11 -4
  178. package/dist/io/xlsx/write.js +153 -90
  179. package/dist/rel-type.d.ts +9 -0
  180. package/dist/rel-type.js +3 -0
  181. package/dist/vba/bytes.d.ts +2 -0
  182. package/dist/vba/bytes.js +11 -0
  183. package/dist/vba/cfb-format.d.ts +47 -0
  184. package/dist/vba/cfb-format.js +34 -0
  185. package/dist/vba/cfb-writer.d.ts +1 -2
  186. package/dist/vba/cfb-writer.js +33 -36
  187. package/dist/vba/cfb.d.ts +9 -4
  188. package/dist/vba/cfb.js +55 -43
  189. package/dist/vba/dir-records.d.ts +6 -0
  190. package/dist/vba/dir-records.js +10 -2
  191. package/dist/vba/errors.d.ts +4 -4
  192. package/dist/vba/index.d.ts +0 -2
  193. package/dist/vba/index.js +0 -2
  194. package/dist/vba/ms-ovba.d.ts +6 -2
  195. package/dist/vba/ms-ovba.js +15 -6
  196. package/dist/vba/project-editor.js +102 -87
  197. package/dist/vba/project.d.ts +4 -0
  198. package/dist/vba/project.js +12 -6
  199. package/dist/vba/vba-encoding.d.ts +4 -10
  200. package/dist/vba/vba-encoding.js +2 -15
  201. package/dist/xml/xml-read.d.ts +29 -1
  202. package/dist/xml/xml-read.js +56 -0
  203. package/dist/xml/xml-scan.d.ts +13 -0
  204. package/dist/xml/xml-scan.js +58 -7
  205. package/dist/xml/xml.d.ts +33 -1
  206. package/dist/xml/xml.js +22 -0
  207. package/package.json +5 -4
  208. package/skills/ts-xlsx-upstream/SKILL.md +21 -13
  209. package/dist/core/modern-functions.d.ts +0 -1
  210. package/dist/io/xlsx/cell-style-resolution.d.ts +0 -25
  211. package/dist/io/xlsx/cell-style-resolution.js +0 -29
  212. package/dist/io/xlsx/column-budget.d.ts +0 -26
  213. package/dist/io/xlsx/read-repair.d.ts +0 -26
  214. package/dist/io/xlsx/read-repair.js +0 -39
@@ -1,5 +1,6 @@
1
1
  import { encodeAddress, tryDecodeCellRef } from '../../core/address.js';
2
2
  import { MENTION_OFFSET_MAX, } from '../../core/comment-thread.js';
3
+ import { AuthoringError, quoted } from '../../errors.js';
3
4
  import { decodeSpreadsheetText, numInteger } from '../../xml/xml-attrs.js';
4
5
  import { parseXml, TextCapture } from '../../xml/xml-read.js';
5
6
  import { boolStrict, localName } from '../../xml/xml-scan.js';
@@ -132,8 +133,7 @@ function commentFrom(message, personById) {
132
133
  id: message.id,
133
134
  text: message.text,
134
135
  mentions: message.mentions.map((mention) => mentionOf(mention, personById)),
135
- ...(author !== undefined ? { author } : {}),
136
- ...(message.personId !== undefined ? { personId: message.personId } : {}),
136
+ ...(author !== undefined ? { author, personId: author.id } : {}),
137
137
  ...(message.date !== undefined ? { date: message.date } : {}),
138
138
  };
139
139
  }
@@ -147,27 +147,34 @@ function mentionOf(mention, personById) {
147
147
  ...(mention.mentionId !== undefined ? { mentionId: mention.mentionId } : {}),
148
148
  };
149
149
  }
150
- export function threadedCommentsXml(threads) {
150
+ export function threadedCommentsXml(threads, personById) {
151
151
  const messages = threads.flatMap((thread) => {
152
152
  const [head, ...replies] = thread.comments;
153
153
  if (head === undefined)
154
154
  return [];
155
155
  return [
156
- threadedCommentXml(thread.ref, head, thread.resolved ? ' done="1"' : ''),
157
- ...replies.map((reply) => threadedCommentXml(thread.ref, reply, ` parentId="${escapeAttr(head.id)}"`)),
156
+ threadedCommentXml(thread.ref, head, thread.resolved ? ' done="1"' : '', personById),
157
+ ...replies.map((reply) => threadedCommentXml(thread.ref, reply, ` parentId="${escapeAttr(head.id)}"`, personById)),
158
158
  ];
159
159
  });
160
160
  return (XML_DECLARATION +
161
161
  `<ThreadedComments xmlns="${THREADED_COMMENTS_NS}">${messages.join('')}</ThreadedComments>`);
162
162
  }
163
- function threadedCommentXml(ref, comment, tail) {
163
+ function threadedCommentXml(ref, comment, tail, personById) {
164
164
  const date = textAttr('dT', comment.date);
165
- const person = textAttr('personId', comment.personId);
165
+ const personId = comment.personId ?? UNKNOWN_AUTHOR;
166
+ if (personId !== UNKNOWN_AUTHOR && personById(personId) === undefined) {
167
+ throw new AuthoringError(`cannot write the threaded comment ${quoted(comment.id)}: its author ${quoted(personId)} is not ` +
168
+ 'a registered person. Register it with Workbook.addPerson, or leave personId out for a ' +
169
+ 'comment with no recorded author');
170
+ }
171
+ const person = textAttr('personId', personId);
166
172
  return (`<threadedComment ref="${escapeAttr(ref)}"${date}${person} id="${escapeAttr(comment.id)}"${tail}>` +
167
173
  `<text>${escapeSpreadsheetText(comment.text)}</text>` +
168
174
  mentionsXml(comment.mentions) +
169
175
  '</threadedComment>');
170
176
  }
177
+ const UNKNOWN_AUTHOR = '{00000000-0000-0000-0000-000000000000}';
171
178
  function mentionsXml(mentions) {
172
179
  const entries = mentions.flatMap(({ personId, mentionId, startIndex, length }) => {
173
180
  if (mentionId === undefined)
@@ -1,26 +1,24 @@
1
1
  import type { Workbook, WorkbookProperties } from '../../core/workbook.ts';
2
- import type { PivotPlan, PreservedPartPlan, PreservedRootReferencePlan, PreservedWorkbookReferencePlan, TablePlan } from './package-plan.ts';
2
+ import type { PivotPlan, PlannedRelationship, PreservedPartPlan, PreservedWorkbookReferencePlan, TablePlan } from './package-plan.ts';
3
3
  export type PreservedWorkbookRel = PreservedWorkbookReferencePlan & {
4
4
  readonly relId: string;
5
5
  };
6
6
  /**
7
- * The workbook part's relationship ids, drawn once by the writer's `assignWorkbookRelIds` and handed
8
- * to every consumer rather than re-derived by any of them.
7
+ * The workbook part's relationships, recorded once by the writer's `planWorkbookRelationships`, with
8
+ * the ids the workbook body cites picked out of them.
9
9
  *
10
- * Declared here because both consumers are here: the rels part wires the ids, and the workbook body
11
- * *references* them from `<sheet r:id>`. The body used to spell that reference `rId${i + 1}`, which
12
- * agreed with the allocator only because sheets happen to be the first ids it hands out. Anything
13
- * laid ahead of them would have re-pointed every sheet at the wrong part, and the package would have
14
- * stayed schema-valid while doing it -- a class of corruption no validator catches and no round-trip
15
- * through this library notices, because the reader resolves the same wrong ids consistently.
10
+ * Declared here because the body is the consumer of the ids: it references them from `<sheet r:id>`.
11
+ * The body used to spell that reference `rId${i + 1}`, which agreed with the allocator only because
12
+ * sheets happen to be the first ids it hands out. Anything laid ahead of them would have re-pointed
13
+ * every sheet at the wrong part, and the package would have stayed schema-valid while doing it -- a
14
+ * class of corruption no validator catches and no round-trip through this library notices, because
15
+ * the reader resolves the same wrong ids consistently.
16
16
  */
17
17
  export interface WorkbookRelPlan {
18
18
  readonly sheetRelIds: readonly string[];
19
- readonly stylesRelId: string;
20
- readonly themeRelId: string;
21
- readonly sharedStringsRelId: string | null;
22
- readonly personsRelId: string | null;
23
19
  readonly preservedWorkbookRels: readonly PreservedWorkbookRel[];
20
+ /** Every relationship the workbook's `.rels` part declares, in id order. */
21
+ readonly relationships: readonly PlannedRelationship[];
24
22
  }
25
23
  /**
26
24
  * What `[Content_Types].xml` needs to know about a package: every part family it declares.
@@ -44,19 +42,10 @@ export interface ContentTypeInputs {
44
42
  readonly preservedWorkbookRefs: readonly PreservedWorkbookReferencePlan[];
45
43
  readonly threadedCommentNumbers: readonly number[];
46
44
  readonly hasPersons: boolean;
45
+ readonly hasCellMetadata: boolean;
46
+ readonly hasRichValues: boolean;
47
47
  }
48
48
  export declare function contentTypesXml(inputs: ContentTypeInputs): string;
49
- export declare function rootRelsXml(rootRefs: readonly PreservedRootReferencePlan[]): string;
50
49
  export declare function workbookXml(workbook: Workbook, plan: WorkbookRelPlan, pivots: readonly PivotPlan[]): string;
51
- /**
52
- * The workbook part's `.rels`, rendered from the ids the planner drew.
53
- *
54
- * Every id arrives in `plan`; none is computed here. It used to re-derive the styles, theme and
55
- * shared-strings ids from the sheet count and a shared constant while `write.ts` independently summed
56
- * the same inputs to place everything after them, so one sequence was spelled in two files and only
57
- * its fixed part was shared. The relationship *order* is still stated here, because that is what this
58
- * function is; the numbering is not, because two numberings of one sequence is how they drift apart.
59
- */
60
- export declare function workbookRelsXml(plan: WorkbookRelPlan, pivots: readonly PivotPlan[]): string;
61
50
  export declare function corePropsXml(properties: WorkbookProperties): string;
62
51
  export declare function appPropsXml(properties: WorkbookProperties): string;
@@ -1,15 +1,14 @@
1
- import { mangleFormula, quoteSheetName } from '../../core/formula.js';
1
+ import { formulaNamesInScope, mangleFormula, quoteSheetName } from '../../core/formula.js';
2
2
  import { WORKBOOK_PROTECTION_CREDENTIAL_ATTRS } from '../../core/workbook-protection.js';
3
3
  import { isVisibility } from '../../core/worksheet.js';
4
4
  import { AuthoringError, InternalError, quoted } from '../../errors.js';
5
5
  import { isRelType } from '../../rel-type.js';
6
6
  import { assertWritableDate, checkedToken, escapeAttr, escapeText, numAttr, textAttr, XML_DECLARATION, } from '../../xml/xml.js';
7
7
  import { extensionOf, THEME_PART_PATH } from '../opc/part-paths.js';
8
- import { relationship, relationshipsPart } from '../opc/rels.js';
9
8
  import { imageContentType } from './images.js';
10
9
  import { SLICER_CACHES_EXT_URI } from './namespaces.js';
11
- import { APP_PROPS_PART, commentsPart, CORE_PROPS_PART, drawingPart, PERSONS_PART, pivotCacheDefinitionPart, pivotCacheRecordsPart, pivotTablePart, SHARED_STRINGS_PART, STYLES_PART, tablePart, targetFromWorkbook, threadedCommentsPart, WORKBOOK_PART, worksheetPart, } from './part-names.js';
12
- import { NS, REL } from './relationships.js';
10
+ import { APP_PROPS_PART, commentsPart, CORE_PROPS_PART, drawingPart, METADATA_PART, PERSONS_PART, pivotCacheDefinitionPart, pivotCacheRecordsPart, pivotTablePart, RICH_VALUE_STRUCTURES_PART, RICH_VALUES_PART, SHARED_STRINGS_PART, STYLES_PART, tablePart, threadedCommentsPart, WORKBOOK_PART, worksheetPart, } from './part-names.js';
11
+ import { NS } from './relationships.js';
13
12
  import { x14Ext } from './x14-ext.js';
14
13
  const CT = {
15
14
  workbook: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml',
@@ -30,6 +29,9 @@ const CT = {
30
29
  pivotCacheRecords: 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml',
31
30
  threadedComments: 'application/vnd.ms-excel.threadedcomments+xml',
32
31
  person: 'application/vnd.ms-excel.person+xml',
32
+ sheetMetadata: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml',
33
+ richValues: 'application/vnd.ms-excel.rdrichvalue+xml',
34
+ richValueStructures: 'application/vnd.ms-excel.rdrichvaluestructure+xml',
33
35
  };
34
36
  export function contentTypesXml(inputs) {
35
37
  const extensionDefaults = buildExtensionDefaults(inputs);
@@ -72,7 +74,7 @@ function contentTypeDefaults(extensionDefaults) {
72
74
  .join('');
73
75
  }
74
76
  function contentTypeOverrides(inputs, extensionDefaults) {
75
- const { sheetCount, tables, drawingNumbers, commentNumbers, threadedCommentNumbers, pivots, hasSharedStrings, hasPersons, preservedParts, preservedWorkbookRefs, } = inputs;
77
+ const { sheetCount, tables, drawingNumbers, commentNumbers, threadedCommentNumbers, pivots, hasSharedStrings, hasPersons, hasCellMetadata, hasRichValues, preservedParts, preservedWorkbookRefs, } = inputs;
76
78
  const preservedOverrides = preservedParts
77
79
  .filter((part) => part.path !== THEME_PART_PATH)
78
80
  .filter((part) => extensionDefaults.get(extensionOf(part.path))?.contentType !== part.contentType)
@@ -91,6 +93,13 @@ function contentTypeOverrides(inputs, extensionDefaults) {
91
93
  override(STYLES_PART, CT.styles),
92
94
  ...(hasSharedStrings ? [override(SHARED_STRINGS_PART, CT.sharedStrings)] : []),
93
95
  ...(hasPersons ? [override(PERSONS_PART, CT.person)] : []),
96
+ ...(hasCellMetadata ? [override(METADATA_PART, CT.sheetMetadata)] : []),
97
+ ...(hasRichValues
98
+ ? [
99
+ override(RICH_VALUES_PART, CT.richValues),
100
+ override(RICH_VALUE_STRUCTURES_PART, CT.richValueStructures),
101
+ ]
102
+ : []),
94
103
  override(CORE_PROPS_PART, CT.core),
95
104
  override(APP_PROPS_PART, CT.app),
96
105
  ...preservedOverrides,
@@ -102,14 +111,6 @@ function override(partPath, contentType) {
102
111
  function defaultType(extension, contentType) {
103
112
  return `<Default Extension="${escapeAttr(extension)}" ContentType="${escapeAttr(contentType)}"/>`;
104
113
  }
105
- export function rootRelsXml(rootRefs) {
106
- return relationshipsPart([
107
- relationship('rId1', REL.officeDocument, WORKBOOK_PART),
108
- relationship('rId2', REL.coreProps, CORE_PROPS_PART),
109
- relationship('rId3', REL.extProps, APP_PROPS_PART),
110
- ...rootRefs.map((ref, i) => relationship(`rId${4 + i}`, ref.relType, ref.entryPath)),
111
- ]);
112
- }
113
114
  export function workbookXml(workbook, plan, pivots) {
114
115
  const preservedRels = plan.preservedWorkbookRels;
115
116
  const sheets = workbook.worksheets;
@@ -225,7 +226,7 @@ function definedNamesXml(workbook) {
225
226
  const commentAttr = textAttr('comment', name.comment);
226
227
  const hiddenAttr = name.hidden ? ' hidden="1"' : '';
227
228
  return (`<definedName name="${escapeAttr(name.name)}"${scopeAttr}${commentAttr}${hiddenAttr}>` +
228
- `${escapeText(mangleFormula(name.refersTo))}</definedName>`);
229
+ `${escapeText(mangleFormula(name.refersTo, formulaNamesInScope(workbook.definedNames, name.scope)))}</definedName>`);
229
230
  });
230
231
  const filterEntries = sheets.flatMap((sheet, index) => sheet.autoFilter === undefined
231
232
  ? []
@@ -242,25 +243,6 @@ function filterDatabaseRefersTo(sheetName, range) {
242
243
  const absolute = range.replace(/([A-Z]+)(\d+)/g, '$$$1$$$2');
243
244
  return `${quoteSheetName(sheetName)}!${absolute}`;
244
245
  }
245
- export function workbookRelsXml(plan, pivots) {
246
- const { personsRelId } = plan;
247
- const preservedRels = plan.preservedWorkbookRels;
248
- return relationshipsPart([
249
- ...plan.sheetRelIds.map((relId, i) => relationship(relId, REL.worksheet, targetFromWorkbook(worksheetPart(i + 1)))),
250
- relationship(plan.stylesRelId, REL.styles, targetFromWorkbook(STYLES_PART)),
251
- relationship(plan.themeRelId, REL.theme, targetFromWorkbook(THEME_PART_PATH)),
252
- ...(plan.sharedStringsRelId === null
253
- ? []
254
- : [
255
- relationship(plan.sharedStringsRelId, REL.sharedStrings, targetFromWorkbook(SHARED_STRINGS_PART)),
256
- ]),
257
- ...(personsRelId === null
258
- ? []
259
- : [relationship(personsRelId, REL.person, targetFromWorkbook(PERSONS_PART))]),
260
- ...preservedRels.map((ref) => relationship(ref.relId, ref.relType, targetFromWorkbook(ref.entryPath))),
261
- ...pivots.map((pivot) => relationship(pivot.workbookRelId, REL.pivotCacheDefinition, targetFromWorkbook(pivotCacheDefinitionPart(pivot.number)))),
262
- ]);
263
- }
264
246
  export function corePropsXml(properties) {
265
247
  const parts = [];
266
248
  if (properties.title !== undefined) {
@@ -1,7 +1,8 @@
1
1
  import type { DateEpoch } from '../../core/date.ts';
2
2
  import type { Worksheet } from '../../core/worksheet.ts';
3
+ import type { WorkbookMetadataTable } from './cell-metadata.ts';
3
4
  import { type HyperlinkPlan } from './hyperlinks.ts';
4
- import type { SheetPlan, TablePlan } from './package-plan.ts';
5
+ import type { TablePlan } from './package-plan.ts';
5
6
  import { type FlushedSheet } from './row-xml.ts';
6
7
  import type { SharedStringTable } from './shared-strings.ts';
7
8
  import type { StyleRegistry } from './styles.ts';
@@ -29,11 +30,13 @@ export interface WorksheetXmlInputs {
29
30
  readonly references: SheetReferences;
30
31
  readonly hyperlinks: readonly HyperlinkPlan[];
31
32
  readonly sharedStrings: SharedStringTable | null;
33
+ readonly cellMetadata: WorkbookMetadataTable;
32
34
  readonly dateEpoch: DateEpoch;
35
+ /** The defined names a bare name in this sheet's formulas resolves to; see `formulaNamesInScope`. */
36
+ readonly formulaNames: ReadonlySet<string>;
33
37
  /** Whether this is the workbook's one selected sheet. */
34
38
  readonly active: boolean;
35
39
  /** The rows the streaming writer already serialised and evicted, absent on the buffered path. */
36
40
  readonly flushed?: FlushedSheet | undefined;
37
41
  }
38
42
  export declare function worksheetXml(inputs: WorksheetXmlInputs): string;
39
- export declare function worksheetRelsXml(plan: SheetPlan): string;
@@ -1,24 +1,25 @@
1
- import { decodeRange, encodeRect } from '../../core/address.js';
1
+ import { encodeRect } from '../../core/address.js';
2
+ import { arrayRangeConflicts, formulaPlacement } from '../../core/array-formula-ranges.js';
3
+ import { mergesOverlappingTables } from '../../core/merge.js';
2
4
  import { pickStyleFacets } from '../../core/style.js';
3
5
  import { AuthoringError, InternalError, quoted } from '../../errors.js';
4
6
  import { escapeAttr, numberText, XML_DECLARATION } from '../../xml/xml.js';
5
- import { relationship, relationshipsPart } from '../opc/rels.js';
6
- import { conditionalFormattingsExtXml, conditionalFormattingsXml, dataBarExtLinks, } from './conditional-formatting.js';
7
+ import { conditionalFormattingsExtXml, conditionalFormattingsXml, planConditionalFormatting, } from './conditional-formatting.js';
7
8
  import { dataValidationsExtXml, dataValidationsXml } from './data-validation.js';
8
- import { hyperlinksXml, isExternalHyperlink } from './hyperlinks.js';
9
+ import { hyperlinksXml } from './hyperlinks.js';
9
10
  import { SLICER_LIST_EXT_URI } from './namespaces.js';
10
- import { commentsPart, drawingPart, mediaPart, pivotTablePart, printerSettingsPart, tablePart, targetFromWorksheet, threadedCommentsPart, vmlDrawingPart, } from './part-names.js';
11
- import { NS, REL } from './relationships.js';
11
+ import { NS } from './relationships.js';
12
12
  import { assertWritableLevel, buildColumnDefaults, Extent, outlineAttr, renderRow, rowOpenTag, } from './row-xml.js';
13
13
  import { planSharedFormulas } from './shared-formulas.js';
14
14
  import { autoFilterXml, headerFooterXml, pageBreaksXml, pageMarginsXml, pageSetupXml, printOptionsXml, sheetProtectionXml, sheetPrXml, sheetViewsXml, } from './sheet-properties.js';
15
15
  import { x14Ext } from './x14-ext.js';
16
16
  export function worksheetXml(inputs) {
17
- const { sheet, tables, styles, references, hyperlinks, sharedStrings, dateEpoch, active, flushed } = inputs;
17
+ const { sheet, tables, styles, references, hyperlinks, sharedStrings, cellMetadata, dateEpoch, formulaNames, active, flushed, } = inputs;
18
18
  validateMerges(sheet);
19
+ validateArrayRanges(sheet, flushed);
19
20
  const columnDefaults = buildColumnDefaults(sheet);
20
21
  const sharedRoles = planSharedFormulas(sheet);
21
- const extLinks = dataBarExtLinks(sheet.conditionalFormattings);
22
+ const formattingPlan = planConditionalFormatting(sheet.conditionalFormattings);
22
23
  const rowOutline = scanRowOutline(sheet, flushed);
23
24
  const collapsedSummaries = rowOutline.collapsedSummaries;
24
25
  const context = {
@@ -28,6 +29,8 @@ export function worksheetXml(inputs) {
28
29
  sharedRoles,
29
30
  collapsedSummaries,
30
31
  dateEpoch,
32
+ formulaNames,
33
+ cellMetadata,
31
34
  };
32
35
  const liveRows = [];
33
36
  const extent = new Extent(flushed?.extent);
@@ -58,8 +61,8 @@ export function worksheetXml(inputs) {
58
61
  sheetProtectionXml(sheet.protection) +
59
62
  autoFilterXml(sheet.autoFilter) +
60
63
  mergeCellsXml(sheet.merges) +
61
- conditionalFormattingsXml(sheet.conditionalFormattings, styles, extLinks) +
62
- dataValidationsXml(sheet.dataValidations) +
64
+ conditionalFormattingsXml(sheet.conditionalFormattings, styles, formattingPlan, formulaNames) +
65
+ dataValidationsXml(sheet.dataValidations, formulaNames) +
63
66
  hyperlinksXml(hyperlinks) +
64
67
  printOptionsXml(sheet.printOptions) +
65
68
  pageMarginsXml(sheet.pageMargins) +
@@ -72,13 +75,13 @@ export function worksheetXml(inputs) {
72
75
  refElement('legacyDrawingHF', references.legacyDrawingHFRelId) +
73
76
  refElement('picture', references.backgroundRelId) +
74
77
  tablePartsXml(tables) +
75
- worksheetExtLstXml(sheet, references.slicerRelIds, extLinks) +
78
+ worksheetExtLstXml(sheet, styles, references.slicerRelIds, formattingPlan, formulaNames) +
76
79
  '</worksheet>');
77
80
  }
78
- function worksheetExtLstXml(sheet, slicerRelIds, extLinks) {
81
+ function worksheetExtLstXml(sheet, styles, slicerRelIds, formattingPlan, formulaNames) {
79
82
  const exts = [
80
- conditionalFormattingsExtXml(sheet.conditionalFormattings, extLinks),
81
- dataValidationsExtXml(sheet.dataValidations),
83
+ conditionalFormattingsExtXml(sheet.conditionalFormattings, styles, formattingPlan, formulaNames),
84
+ dataValidationsExtXml(sheet.dataValidations, formulaNames),
82
85
  slicerListExtXml(slicerRelIds),
83
86
  ].filter((ext) => ext !== '');
84
87
  return exts.length === 0 ? '' : `<extLst>${exts.join('')}</extLst>`;
@@ -90,30 +93,33 @@ function slicerListExtXml(slicerRelIds) {
90
93
  return x14Ext(SLICER_LIST_EXT_URI, `<x14:slicerList>${slicers}</x14:slicerList>`);
91
94
  }
92
95
  function validateMerges(sheet) {
93
- if (sheet.merges.length === 0 || sheet.tables.length === 0)
96
+ const [conflict] = mergesOverlappingTables(sheet.merges, sheet.tables);
97
+ if (conflict === undefined)
94
98
  return;
95
- for (const merge of sheet.merges) {
96
- const { left, right, top, bottom } = decodeRange(merge);
97
- if (left === undefined || right === undefined || top === undefined || bottom === undefined)
98
- continue;
99
- for (const table of sheet.tables) {
100
- const region = table.region;
101
- const overlaps = left <= region.right &&
102
- right >= region.left &&
103
- top <= region.bottom &&
104
- bottom >= region.top;
105
- if (overlaps) {
106
- throw new AuthoringError(`merged range ${merge} overlaps table ${quoted(table.name)} (${table.range}): Excel forbids a merge inside a table`);
107
- }
99
+ const { merge, table } = conflict;
100
+ throw new AuthoringError(`merged range ${merge} overlaps table ${quoted(table.name)} (${table.range}): Excel forbids a merge inside a table`);
101
+ }
102
+ function validateArrayRanges(sheet, flushed) {
103
+ const placements = [...(flushed?.formulas ?? [])];
104
+ for (const { cells } of sheet.rows()) {
105
+ for (const cell of cells) {
106
+ const placement = formulaPlacement(cell.address, cell.col, cell.row, cell.value);
107
+ if (placement !== undefined)
108
+ placements.push(placement);
108
109
  }
109
110
  }
111
+ const [conflict] = arrayRangeConflicts(placements);
112
+ if (conflict === undefined)
113
+ return;
114
+ const { array, other, kind } = conflict;
115
+ throw new AuthoringError(kind === 'formula-inside'
116
+ ? `the array formula in ${array.address} fills ${quoted(array.arrayRef ?? '')}, which holds the formula in ${other}: Excel repairs a package with a formula inside another array formula's range`
117
+ : `the array formula in ${array.address} fills ${quoted(array.arrayRef ?? '')}, which shares cells with the range of the array formula in ${other}: Excel repairs a package whose array formula ranges overlap`);
110
118
  }
111
119
  function mergeCellsXml(merges) {
112
120
  if (merges.length === 0)
113
121
  return '';
114
- const cells = merges
115
- .map((range) => `<mergeCell ref="${escapeAttr(decodeRange(range).dimensions)}"/>`)
116
- .join('');
122
+ const cells = merges.map((range) => `<mergeCell ref="${escapeAttr(range)}"/>`).join('');
117
123
  return `<mergeCells count="${merges.length}">${cells}</mergeCells>`;
118
124
  }
119
125
  function refElement(tag, relId) {
@@ -125,44 +131,6 @@ function tablePartsXml(tables) {
125
131
  const parts = tables.map(({ relId }) => `<tablePart r:id="${relId}"/>`).join('');
126
132
  return `<tableParts count="${tables.length}">${parts}</tableParts>`;
127
133
  }
128
- export function worksheetRelsXml(plan) {
129
- const { tables, drawing, comments, threadedComments, printerSettings, background, hyperlinks, preservedRefs: preservedReferences, pivots, } = plan;
130
- const rels = [
131
- ...tables.map(({ relId, number }) => relationship(relId, REL.table, targetFromWorksheet(tablePart(number)))),
132
- ...pivots.map((pivot) => relationship(pivot.sheetRelId, REL.pivotTable, targetFromWorksheet(pivotTablePart(pivot.number)))),
133
- ...(drawing === null
134
- ? []
135
- : [
136
- relationship(drawing.relId, REL.drawing, targetFromWorksheet(drawingPart(drawing.number))),
137
- ]),
138
- ...(comments === null
139
- ? []
140
- : [
141
- relationship(comments.vmlRelId, REL.vmlDrawing, targetFromWorksheet(vmlDrawingPart(comments.number))),
142
- relationship(comments.commentsRelId, REL.comments, targetFromWorksheet(commentsPart(comments.number))),
143
- ]),
144
- ...(threadedComments === null
145
- ? []
146
- : [
147
- relationship(threadedComments.relId, REL.threadedComment, targetFromWorksheet(threadedCommentsPart(threadedComments.number))),
148
- ]),
149
- ...(printerSettings === null
150
- ? []
151
- : [
152
- relationship(printerSettings.relId, REL.printerSettings, targetFromWorksheet(printerSettingsPart(printerSettings.number))),
153
- ]),
154
- ...(background === null
155
- ? []
156
- : [
157
- relationship(background.relId, REL.image, targetFromWorksheet(mediaPart(background.mediaNumber, background.extension))),
158
- ]),
159
- ...preservedReferences.map((reference) => relationship(reference.relId, reference.relType, targetFromWorksheet(reference.entryPath))),
160
- ...hyperlinks
161
- .filter(isExternalHyperlink)
162
- .map((link) => relationship(link.relId, REL.hyperlink, link.target, { external: true })),
163
- ];
164
- return relationshipsPart(rels);
165
- }
166
134
  const DEFAULT_ROW_HEIGHT = 15;
167
135
  function sheetFormatPr(properties, outlineLevel) {
168
136
  const rowHeight = properties.defaultRowHeight ?? DEFAULT_ROW_HEIGHT;
@@ -4,12 +4,14 @@ import type { Cell } from '../../core/cell.ts';
4
4
  import type { ConditionalFormatting } from '../../core/conditional-formatting.ts';
5
5
  import type { DataValidation } from '../../core/data-validation.ts';
6
6
  import type { DateEpoch } from '../../core/date.ts';
7
+ import type { Hyperlink } from '../../core/hyperlink.ts';
7
8
  import type { AnchorPoint } from '../../core/image.ts';
8
9
  import { INTERNAL } from '../../core/internal.ts';
9
10
  import type { SheetProtectionOptions } from '../../core/protection.ts';
10
11
  import { type CellValue } from '../../core/value.ts';
11
- import { type AddImageOptions, type AddWorksheetOptions, Workbook } from '../../core/workbook.ts';
12
+ import { type AddImageOptions, type AddWorksheetOptions, type DefinedName, Workbook } from '../../core/workbook.ts';
12
13
  import type { Worksheet } from '../../core/worksheet.ts';
14
+ import { WorkbookMetadataTable } from './cell-metadata.ts';
13
15
  import { type FlushedSheet } from './row-xml.ts';
14
16
  import type { StyleRegistry } from './styles.ts';
15
17
  /** Calculation settings applied to the streamed workbook. Mirrors the {@link Workbook} flags. */
@@ -63,13 +65,25 @@ export type WorkbookStreamWriterOptions = SinkOptions & {
63
65
  */
64
66
  export declare class StreamedRow {
65
67
  #private;
66
- constructor(cells: readonly Cell[], sheet: WorksheetStreamWriter | null, number: number);
67
- /** The cells this row materialised, for styling before it is committed. */
68
+ private constructor();
69
+ /** The sheet writer's construction channel, keyed like its own; see `core/internal.ts`. */
70
+ static readonly [INTERNAL]: StreamedRowFactory;
71
+ /** The cells `addRow` materialised, for styling before it is committed. A cell added to the row
72
+ * through {@link WorksheetStreamWriter.getCell} is not among them, and is committed all the same. */
68
73
  get cells(): readonly Cell[];
69
74
  /** Finalise the row: an eager writer serialises it now and releases its cells; otherwise a no-op.
70
75
  * Committing twice is harmless: the second call does nothing rather than re-emitting the row. */
71
76
  commit(): void;
72
77
  }
78
+ /**
79
+ * How a {@link StreamedRow} comes into being. A row belongs to the sheet writer that numbered it, and
80
+ * only that writer knows which number is still open. `sheet` is `null` for a writer that keeps every
81
+ * row live until the workbook commits, where committing a row has nothing to do. Reached as
82
+ * `StreamedRow[INTERNAL]`.
83
+ */
84
+ export interface StreamedRowFactory {
85
+ create(cells: readonly Cell[], sheet: WorksheetStreamWriter | null, number: number): StreamedRow;
86
+ }
73
87
  /**
74
88
  * A worksheet being written incrementally. Append rows with {@link addRow}/{@link addRows}, style
75
89
  * cells through {@link getCell}, then {@link commit} to freeze it, after which any further mutation
@@ -110,6 +124,12 @@ export declare class WorksheetStreamWriter {
110
124
  addDataValidation(sqref: string, rule: DataValidation, options?: {
111
125
  extended?: boolean;
112
126
  }): void;
127
+ /**
128
+ * Put a hyperlink on a cell or a rectangle of cells before the sheet is committed; mirrors
129
+ * {@link Worksheet.addHyperlink}. A link lives beside the grid rather than in a row, so it may cover
130
+ * cells of a row that is already committed.
131
+ */
132
+ addHyperlink(link: Hyperlink): void;
113
133
  /**
114
134
  * Attach a conditional formatting to a range before the sheet is committed. Like every other block,
115
135
  * it lands in its schema-mandated slot, after `<mergeCells>` and before `<dataValidations>` and
@@ -153,7 +173,7 @@ export declare class WorksheetStreamWriter {
153
173
  * {@link WorkbookStreamWriter.addWorksheet}. Reached as `WorksheetStreamWriter[INTERNAL]`.
154
174
  */
155
175
  export interface WorksheetStreamWriterFactory {
156
- create(sheet: Worksheet, eager: boolean, styles: StyleRegistry, dateEpoch: DateEpoch): WorksheetStreamWriter;
176
+ create(sheet: Worksheet, eager: boolean, styles: StyleRegistry, cellMetadata: WorkbookMetadataTable, dateEpoch: DateEpoch, definedNames: readonly DefinedName[]): WorksheetStreamWriter;
157
177
  }
158
178
  /**
159
179
  * What the streaming writer's own machinery may do to a sheet writer that a caller may not, the same
@@ -170,13 +190,15 @@ export interface WorksheetStreamWriterInternals {
170
190
  /**
171
191
  * Serialise an eagerly-committed row and release its cells from the model. Called by
172
192
  * {@link StreamedRow.commit}; the row's `<row>` XML is retained (interned into the workbook's live
173
- * style registry so its ids stay valid) and the cell graph is dropped, bounding peak memory.
193
+ * style registry so its ids stay valid) and the cell graph is dropped, bounding peak memory. The
194
+ * row is rendered from every cell the model holds for it, including any `getCell` added after
195
+ * `addRow` returned.
174
196
  *
175
197
  * @throws {AuthoringError} if the row carries a shared-formula cell: a finished row cannot join the
176
198
  * whole-sheet formula planning, so shared formulas must be authored through
177
199
  * {@link WorksheetStreamWriter.getCell}.
178
200
  */
179
- flushRow(number: number, cells: readonly Cell[]): void;
201
+ flushRow(number: number): void;
180
202
  /** The rows this writer flushed, or undefined if none. Handed to `buildPackageParts` at commit. */
181
203
  flushedSheet(): FlushedSheet | undefined;
182
204
  }