@shbernal/ts-xlsx 2.0.0 → 3.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 (147) hide show
  1. package/README.md +20 -10
  2. package/dist/bytes.d.ts +24 -0
  3. package/dist/bytes.js +34 -0
  4. package/dist/core/address.d.ts +26 -4
  5. package/dist/core/address.js +21 -9
  6. package/dist/core/autofilter.d.ts +12 -1
  7. package/dist/core/autofilter.js +35 -12
  8. package/dist/core/axis-handle.d.ts +8 -0
  9. package/dist/core/axis-handle.js +14 -0
  10. package/dist/core/cell.d.ts +24 -0
  11. package/dist/core/cell.js +9 -1
  12. package/dist/core/color-resolution.d.ts +1 -1
  13. package/dist/core/color-resolution.js +7 -8
  14. package/dist/core/column.d.ts +4 -1
  15. package/dist/core/column.js +31 -35
  16. package/dist/core/conditional-formatting-overlay.d.ts +5 -0
  17. package/dist/core/conditional-formatting-overlay.js +11 -0
  18. package/dist/core/conditional-formatting.d.ts +45 -5
  19. package/dist/core/conditional-formatting.js +74 -0
  20. package/dist/core/data-validation-overlay.d.ts +11 -0
  21. package/dist/core/data-validation-overlay.js +21 -2
  22. package/dist/core/data-validation.d.ts +9 -2
  23. package/dist/core/data-validation.js +26 -0
  24. package/dist/core/grid-edits.d.ts +16 -0
  25. package/dist/core/grid-edits.js +26 -8
  26. package/dist/core/grid-shift.d.ts +19 -0
  27. package/dist/core/grid-shift.js +8 -0
  28. package/dist/core/image.d.ts +2 -0
  29. package/dist/core/image.js +2 -0
  30. package/dist/core/merge-index.d.ts +31 -0
  31. package/dist/core/merge-index.js +60 -0
  32. package/dist/core/merge.d.ts +12 -11
  33. package/dist/core/merge.js +36 -10
  34. package/dist/core/page-setup.d.ts +86 -2
  35. package/dist/core/page-setup.js +34 -1
  36. package/dist/core/pivot-table.d.ts +2 -0
  37. package/dist/core/pivot-table.js +23 -18
  38. package/dist/core/protection.d.ts +3 -0
  39. package/dist/core/protection.js +20 -14
  40. package/dist/core/row-input.d.ts +7 -0
  41. package/dist/core/row-input.js +10 -8
  42. package/dist/core/row.d.ts +4 -1
  43. package/dist/core/row.js +19 -23
  44. package/dist/core/style.d.ts +78 -7
  45. package/dist/core/style.js +90 -80
  46. package/dist/core/table.d.ts +26 -12
  47. package/dist/core/table.js +67 -33
  48. package/dist/core/theme.d.ts +3 -1
  49. package/dist/core/theme.js +4 -0
  50. package/dist/core/used-extent.d.ts +44 -0
  51. package/dist/core/used-extent.js +102 -0
  52. package/dist/core/workbook.d.ts +2 -2
  53. package/dist/core/worksheet-comments.d.ts +8 -0
  54. package/dist/core/worksheet-comments.js +18 -0
  55. package/dist/core/worksheet.d.ts +31 -7
  56. package/dist/core/worksheet.js +72 -86
  57. package/dist/customui/ribbon.js +23 -24
  58. package/dist/entries/core.d.ts +3 -3
  59. package/dist/entries/csv.d.ts +1 -1
  60. package/dist/entries/node-unavailable.d.ts +15 -0
  61. package/dist/entries/node-unavailable.js +20 -0
  62. package/dist/entries/node.d.ts +1 -0
  63. package/dist/entries/node.js +1 -0
  64. package/dist/entries/xlsx.d.ts +0 -1
  65. package/dist/entries/xlsx.js +0 -1
  66. package/dist/io/csv/read.js +1 -1
  67. package/dist/io/csv/write.d.ts +12 -2
  68. package/dist/io/csv/write.js +17 -2
  69. package/dist/io/opc/inflate.js +3 -13
  70. package/dist/io/opc/part-paths.d.ts +1 -0
  71. package/dist/io/opc/part-paths.js +15 -0
  72. package/dist/io/opc/read-opc.d.ts +22 -3
  73. package/dist/io/opc/read-opc.js +11 -39
  74. package/dist/io/opc/rels.d.ts +12 -6
  75. package/dist/io/opc/rels.js +3 -5
  76. package/dist/io/style/xf-style.d.ts +33 -4
  77. package/dist/io/style/xf-style.js +12 -1
  78. package/dist/io/xlsb/read-styles.js +6 -3
  79. package/dist/io/xlsb/read.d.ts +1 -1
  80. package/dist/io/xlsb/read.js +5 -8
  81. package/dist/io/xlsx/cell-accumulator.d.ts +2 -4
  82. package/dist/io/xlsx/cell-accumulator.js +20 -58
  83. package/dist/io/xlsx/cell-value.d.ts +1 -1
  84. package/dist/io/xlsx/cell-value.js +7 -4
  85. package/dist/io/xlsx/color-xml.d.ts +1 -1
  86. package/dist/io/xlsx/color-xml.js +8 -7
  87. package/dist/io/xlsx/comments.js +6 -3
  88. package/dist/io/xlsx/conditional-formatting.d.ts +4 -3
  89. package/dist/io/xlsx/conditional-formatting.js +81 -65
  90. package/dist/io/xlsx/data-validation.d.ts +10 -8
  91. package/dist/io/xlsx/data-validation.js +44 -41
  92. package/dist/io/xlsx/edit-vba.js +4 -11
  93. package/dist/io/xlsx/hyperlinks.d.ts +4 -2
  94. package/dist/io/xlsx/hyperlinks.js +26 -31
  95. package/dist/io/xlsx/images.js +33 -35
  96. package/dist/io/xlsx/package-plan.d.ts +9 -1
  97. package/dist/io/xlsx/package-plan.js +29 -16
  98. package/dist/io/xlsx/part-names.d.ts +41 -0
  99. package/dist/io/xlsx/part-names.js +46 -0
  100. package/dist/io/xlsx/read-pivot.js +4 -9
  101. package/dist/io/xlsx/read-rows.js +12 -13
  102. package/dist/io/xlsx/read-shared-strings.js +9 -47
  103. package/dist/io/xlsx/read-styles.d.ts +1 -1
  104. package/dist/io/xlsx/read-styles.js +65 -43
  105. package/dist/io/xlsx/read-worksheet.d.ts +10 -1
  106. package/dist/io/xlsx/read-worksheet.js +48 -63
  107. package/dist/io/xlsx/read.d.ts +3 -4
  108. package/dist/io/xlsx/read.js +124 -146
  109. package/dist/io/xlsx/rich-runs.d.ts +41 -7
  110. package/dist/io/xlsx/rich-runs.js +95 -30
  111. package/dist/io/xlsx/sheet-properties.d.ts +1 -1
  112. package/dist/io/xlsx/sheet-properties.js +23 -26
  113. package/dist/io/xlsx/styles.d.ts +3 -15
  114. package/dist/io/xlsx/styles.js +83 -105
  115. package/dist/io/xlsx/tables.js +14 -13
  116. package/dist/io/xlsx/theme-xml.js +8 -5
  117. package/dist/io/xlsx/threaded-comments.js +20 -19
  118. package/dist/io/xlsx/workbook-xml.js +43 -36
  119. package/dist/io/xlsx/worksheet-xml.d.ts +1 -1
  120. package/dist/io/xlsx/worksheet-xml.js +47 -52
  121. package/dist/io/xlsx/write-stream.d.ts +14 -1
  122. package/dist/io/xlsx/write-stream.js +40 -33
  123. package/dist/io/xlsx/write.d.ts +5 -0
  124. package/dist/io/xlsx/write.js +155 -131
  125. package/dist/sha512.d.ts +2 -0
  126. package/dist/sha512.js +146 -0
  127. package/dist/token-set.d.ts +15 -0
  128. package/dist/token-set.js +4 -0
  129. package/dist/vba/bytes.d.ts +1 -2
  130. package/dist/vba/bytes.js +1 -12
  131. package/dist/vba/cfb-format.d.ts +24 -0
  132. package/dist/vba/cfb-format.js +12 -0
  133. package/dist/vba/cfb-writer.js +17 -17
  134. package/dist/vba/cfb.js +1 -7
  135. package/dist/vba/dir-records.d.ts +53 -0
  136. package/dist/vba/dir-records.js +28 -0
  137. package/dist/vba/ms-ovba.js +32 -11
  138. package/dist/vba/project-editor.js +5 -34
  139. package/dist/vba/project.js +15 -31
  140. package/dist/vba/vba-encoding.js +1 -1
  141. package/dist/xml/xml-read.d.ts +117 -72
  142. package/dist/xml/xml-read.js +123 -169
  143. package/dist/xml/xml-scan.d.ts +100 -0
  144. package/dist/xml/xml-scan.js +208 -0
  145. package/dist/xml/xml.d.ts +44 -2
  146. package/dist/xml/xml.js +17 -5
  147. package/package.json +22 -6
@@ -1,12 +1,13 @@
1
1
  import { strToU8, zip, zipSync } from 'fflate';
2
2
  import { AuthoringError } from '../../errors.js';
3
- import { THEME_PART_PATH } from '../opc/part-paths.js';
3
+ import { relativePartPath, relsPathFor, THEME_PART_PATH } from '../opc/part-paths.js';
4
4
  import { relsPartXml } from '../opc/rels.js';
5
5
  import { FIXED_ENTRY_MTIME } from '../opc/zip-mtime.js';
6
6
  import { collectComments, commentsXml, vmlDrawingXml } from './comments.js';
7
7
  import { collectHyperlinks, planHyperlinks } from './hyperlinks.js';
8
8
  import { drawingRelsXml, drawingXml } from './images.js';
9
9
  import { planMedia, planPreservedParts, SheetRelIds, } from './package-plan.js';
10
+ import { APP_PROPS_PART, commentsPart, CORE_PROPS_PART, drawingPart, mediaPart, PERSONS_PART, pivotCacheDefinitionPart, pivotCacheRecordsPart, pivotTablePart, printerSettingsPart, SHARED_STRINGS_PART, STYLES_PART, tablePart, threadedCommentsPart, vmlDrawingPart, WORKBOOK_PART, worksheetPart, } from './part-names.js';
10
11
  import { pivotCacheDefinitionXml, pivotCacheRecordsXml, pivotTableXml } from './pivot.js';
11
12
  import { REL } from './relationships.js';
12
13
  import { SharedStringTable } from './shared-strings.js';
@@ -47,6 +48,72 @@ export function createStyleRegistry(workbook) {
47
48
  styles.addTableStyle(style);
48
49
  return styles;
49
50
  }
51
+ function planSheet(context) {
52
+ const { sheet, index, media, preserved, numbering } = context;
53
+ const rels = new SheetRelIds();
54
+ const tables = sheet.tables.map((table) => ({
55
+ table,
56
+ number: ++numbering.table,
57
+ relId: rels.next(),
58
+ }));
59
+ let drawing = null;
60
+ if (sheet.images.length > 0) {
61
+ const images = sheet.images.map((image, j) => {
62
+ const { number, image: registered } = media.resolve(image.imageId);
63
+ return {
64
+ anchor: image.anchor,
65
+ embedId: `rId${j + 1}`,
66
+ mediaNumber: number,
67
+ extension: registered.extension,
68
+ };
69
+ });
70
+ drawing = { number: ++numbering.drawing, relId: rels.next(), images };
71
+ }
72
+ const threads = sheet.commentThreads.filter((thread) => thread.comments.length > 0);
73
+ const sheetComments = collectComments(sheet, threads);
74
+ const comments = sheetComments.length === 0
75
+ ? null
76
+ : {
77
+ number: index + 1,
78
+ comments: sheetComments,
79
+ vmlRelId: rels.next(),
80
+ commentsRelId: rels.next(),
81
+ };
82
+ const threadedComments = threads.length === 0 ? null : { number: index + 1, threads, relId: rels.next() };
83
+ const printerData = sheet.pageSetup.printerSettings;
84
+ const printerSettings = printerData === undefined ? null : { number: index + 1, data: printerData, relId: rels.next() };
85
+ const hyperlinks = planHyperlinks(collectHyperlinks(sheet), rels);
86
+ let background = null;
87
+ if (sheet.backgroundImageId !== undefined) {
88
+ const { number, image } = media.resolve(sheet.backgroundImageId);
89
+ background = { relId: rels.next(), mediaNumber: number, extension: image.extension };
90
+ }
91
+ const preservedRefs = (preserved.perSheet[index] ?? []).map((reference) => ({ ...reference, relId: rels.next() }));
92
+ const pivots = sheet.pivotTables.map((table) => {
93
+ const number = ++numbering.pivot;
94
+ return { number, cacheId: String(number), table, sheetRelId: rels.next(), workbookRelId: '' };
95
+ });
96
+ return {
97
+ tables,
98
+ drawing,
99
+ comments,
100
+ threadedComments,
101
+ printerSettings,
102
+ hyperlinks,
103
+ background,
104
+ preservedRefs,
105
+ pivots,
106
+ };
107
+ }
108
+ function numbersOf(plans, pick) {
109
+ const numbers = [];
110
+ for (const plan of plans) {
111
+ const part = pick(plan);
112
+ if (part !== null)
113
+ numbers.push(part.number);
114
+ }
115
+ return numbers;
116
+ }
50
117
  function resolveSheetReferences(plan) {
51
118
  const refs = plan.preservedRefs;
52
119
  const preservedDrawingRelId = refs.find((ref) => ref.element === 'drawing')?.relId ?? null;
@@ -63,125 +130,65 @@ function resolveSheetReferences(plan) {
63
130
  slicerRelIds,
64
131
  };
65
132
  }
66
- export function buildPackageParts(workbook, options = {}) {
67
- const sheets = workbook.worksheets;
68
- if (sheets.length === 0) {
69
- throw new AuthoringError('cannot write a workbook with no worksheets: a zero-sheet package is corrupt to Excel');
70
- }
71
- const sharedStrings = options.useSharedStrings ? new SharedStringTable() : null;
133
+ function assignWorkbookRelIds(context) {
134
+ const { sheetCount, hasSharedStrings, hasPersons, preservedWorkbook, pivots } = context;
135
+ const modeledCount = sheetCount + FIXED_WORKBOOK_REL_COUNT + (hasSharedStrings ? 1 : 0);
136
+ const personsRelId = hasPersons ? `rId${modeledCount + 1}` : null;
137
+ const preservedBase = modeledCount + (personsRelId === null ? 0 : 1);
138
+ const preservedWorkbookRels = preservedWorkbook.map((ref, i) => ({
139
+ ...ref,
140
+ relId: `rId${preservedBase + 1 + i}`,
141
+ }));
142
+ const pivotBase = preservedBase + preservedWorkbook.length;
143
+ pivots.forEach((pivot, i) => {
144
+ pivot.workbookRelId = `rId${pivotBase + 1 + i}`;
145
+ });
146
+ return { personsRelId, preservedWorkbookRels };
147
+ }
148
+ function planPackage(workbook, sheets) {
72
149
  const media = planMedia(workbook, sheets);
73
150
  const generatedDrawingCount = sheets.filter((sheet) => sheet.images.length > 0).length;
74
151
  const preserved = planPreservedParts(workbook, generatedDrawingCount, media.parts.length);
75
- let tableNumber = 0;
76
- let drawingNumber = 0;
77
- let pivotNumber = 0;
78
- const perSheet = sheets.map((sheet, i) => {
79
- const rels = new SheetRelIds();
80
- const tables = sheet.tables.map((table) => ({
81
- table,
82
- number: ++tableNumber,
83
- relId: rels.next(),
84
- }));
85
- let drawing = null;
86
- if (sheet.images.length > 0) {
87
- const images = sheet.images.map((image, j) => {
88
- const registered = workbook.getImage(image.imageId);
89
- return {
90
- anchor: image.anchor,
91
- embedId: `rId${j + 1}`,
92
- mediaNumber: media.numberById.get(image.imageId),
93
- extension: registered.extension,
94
- };
95
- });
96
- drawing = { number: ++drawingNumber, relId: rels.next(), images };
97
- }
98
- const threads = sheet.commentThreads.filter((thread) => thread.comments.length > 0);
99
- const sheetComments = collectComments(sheet, threads);
100
- const comments = sheetComments.length === 0
101
- ? null
102
- : {
103
- number: i + 1,
104
- comments: sheetComments,
105
- vmlRelId: rels.next(),
106
- commentsRelId: rels.next(),
107
- };
108
- const threadedComments = threads.length === 0 ? null : { number: i + 1, threads, relId: rels.next() };
109
- const printerData = sheet.pageSetup.printerSettings;
110
- const printerSettings = printerData === undefined ? null : { number: i + 1, data: printerData, relId: rels.next() };
111
- const hyperlinks = planHyperlinks(collectHyperlinks(sheet), rels);
112
- let background = null;
113
- if (sheet.backgroundImageId !== undefined) {
114
- const registered = workbook.getImage(sheet.backgroundImageId);
115
- if (registered === undefined) {
116
- throw new AuthoringError(`sheet "${sheet.name}" sets background image id ${sheet.backgroundImageId}, which is not registered on the workbook`);
117
- }
118
- background = {
119
- relId: rels.next(),
120
- mediaNumber: media.numberById.get(sheet.backgroundImageId),
121
- extension: registered.extension,
122
- };
123
- }
124
- const preservedRefs = (preserved.perSheet[i] ?? []).map((reference) => ({ ...reference, relId: rels.next() }));
125
- const pivots = sheet.pivotTables.map((table) => {
126
- const number = ++pivotNumber;
127
- return { number, cacheId: String(number), table, sheetRelId: rels.next(), workbookRelId: '' };
128
- });
129
- return {
130
- tables,
131
- drawing,
132
- comments,
133
- threadedComments,
134
- printerSettings,
135
- hyperlinks,
136
- background,
137
- preservedRefs,
138
- pivots,
139
- };
140
- });
141
- const allTables = perSheet.flatMap((plan) => plan.tables);
142
- const allPivots = perSheet.flatMap((plan) => plan.pivots);
143
- const styles = options.styles ?? createStyleRegistry(workbook);
144
- const sheetXml = sheets.map((sheet, i) => {
145
- const plan = perSheet[i];
146
- return worksheetXml(sheet, plan.tables, styles, resolveSheetReferences(plan), plan.hyperlinks, sharedStrings, i === workbook.activeTabIndex, options.flushed?.get(sheet));
152
+ const numbering = { table: 0, drawing: 0, pivot: 0 };
153
+ const perSheet = sheets.map((sheet, index) => planSheet({ sheet, index, media, preserved, numbering }));
154
+ return {
155
+ media,
156
+ preserved,
157
+ perSheet,
158
+ allTables: perSheet.flatMap((plan) => plan.tables),
159
+ allPivots: perSheet.flatMap((plan) => plan.pivots),
160
+ };
161
+ }
162
+ function serialiseSheets(workbook, sheets, plan, styles, sharedStrings, flushed) {
163
+ return sheets.map((sheet, i) => {
164
+ const sheetPlan = plan.perSheet[i];
165
+ return worksheetXml(sheet, sheetPlan.tables, styles, resolveSheetReferences(sheetPlan), sheetPlan.hyperlinks, sharedStrings, i === workbook.activeTabIndex, flushed?.get(sheet));
147
166
  });
167
+ }
168
+ function emitPackageParts(context) {
169
+ const { workbook, sheets, plan, styles, sharedStrings, sheetXml } = context;
170
+ const { media, preserved, perSheet, allTables, allPivots } = plan;
148
171
  const hasSharedStrings = sharedStrings !== null && !sharedStrings.isEmpty;
149
- const commentNumbers = perSheet
150
- .map((plan) => plan.comments)
151
- .filter((c) => c !== null)
152
- .map((c) => c.number);
153
- const drawingNumbers = perSheet
154
- .map((plan) => plan.drawing)
155
- .filter((d) => d !== null)
156
- .map((d) => d.number);
157
- const printerSettingsNumbers = perSheet
158
- .map((plan) => plan.printerSettings)
159
- .filter((p) => p !== null)
160
- .map((p) => p.number);
161
- const threadedCommentNumbers = perSheet
162
- .map((plan) => plan.threadedComments)
163
- .filter((t) => t !== null)
164
- .map((t) => t.number);
172
+ const commentNumbers = numbersOf(perSheet, (sheetPlan) => sheetPlan.comments);
173
+ const drawingNumbers = numbersOf(perSheet, (sheetPlan) => sheetPlan.drawing);
174
+ const printerSettingsNumbers = numbersOf(perSheet, (sheetPlan) => sheetPlan.printerSettings);
175
+ const threadedCommentNumbers = numbersOf(perSheet, (sheetPlan) => sheetPlan.threadedComments);
165
176
  const persons = threadedCommentNumbers.length === 0 ? [] : workbook.persons;
166
- const modeledWorkbookRelCount = sheets.length + FIXED_WORKBOOK_REL_COUNT + (hasSharedStrings ? 1 : 0);
167
- const personsRelId = persons.length === 0 ? null : `rId${modeledWorkbookRelCount + 1}`;
168
- const workbookRelBase = modeledWorkbookRelCount + (personsRelId === null ? 0 : 1);
169
- const preservedWorkbookRels = preserved.workbook.map((ref, i) => ({
170
- ...ref,
171
- relId: `rId${workbookRelBase + 1 + i}`,
172
- }));
173
- const pivotWorkbookRelBase = workbookRelBase + preserved.workbook.length;
174
- allPivots.forEach((pivot, i) => {
175
- pivot.workbookRelId = `rId${pivotWorkbookRelBase + 1 + i}`;
177
+ const { personsRelId, preservedWorkbookRels } = assignWorkbookRelIds({
178
+ sheetCount: sheets.length,
179
+ hasSharedStrings,
180
+ hasPersons: persons.length > 0,
181
+ preservedWorkbook: preserved.workbook,
182
+ pivots: allPivots,
176
183
  });
177
184
  const files = {
178
185
  '[Content_Types].xml': strToU8(contentTypesXml(sheets.length, allTables, commentNumbers, drawingNumbers, printerSettingsNumbers, media.extensions, hasSharedStrings, preserved.parts, allPivots, preservedWorkbookRels, threadedCommentNumbers, persons.length > 0)),
179
186
  '_rels/.rels': strToU8(rootRelsXml(preserved.root)),
180
- 'docProps/core.xml': strToU8(corePropsXml(workbook.properties)),
181
- 'docProps/app.xml': strToU8(appPropsXml(workbook.properties)),
182
- 'xl/workbook.xml': strToU8(workbookXml(workbook, preservedWorkbookRels, allPivots)),
183
- 'xl/_rels/workbook.xml.rels': strToU8(workbookRelsXml(sheets.length, hasSharedStrings, personsRelId, preservedWorkbookRels, allPivots)),
184
- 'xl/styles.xml': strToU8(styles.toXml()),
187
+ [CORE_PROPS_PART]: strToU8(corePropsXml(workbook.properties)),
188
+ [APP_PROPS_PART]: strToU8(appPropsXml(workbook.properties)),
189
+ [WORKBOOK_PART]: strToU8(workbookXml(workbook, preservedWorkbookRels, allPivots)),
190
+ [relsPathFor(WORKBOOK_PART)]: strToU8(workbookRelsXml(sheets.length, hasSharedStrings, personsRelId, preservedWorkbookRels, allPivots)),
191
+ [STYLES_PART]: strToU8(styles.toXml()),
185
192
  };
186
193
  if (!preserved.themeEmitted) {
187
194
  const overrides = workbook.themeOverrides;
@@ -190,26 +197,37 @@ export function buildPackageParts(workbook, options = {}) {
190
197
  : applyThemeOverrides(DEFAULT_THEME_XML, overrides));
191
198
  }
192
199
  if (hasSharedStrings) {
193
- files['xl/sharedStrings.xml'] = strToU8(sharedStrings.toXml());
200
+ files[SHARED_STRINGS_PART] = strToU8(sharedStrings.toXml());
194
201
  }
195
202
  if (persons.length > 0)
196
- files['xl/persons/person.xml'] = strToU8(personsXml(persons));
203
+ files[PERSONS_PART] = strToU8(personsXml(persons));
197
204
  for (const part of media.parts) {
198
- files[`xl/media/image${part.number}.${part.extension}`] = part.data;
205
+ files[mediaPart(part.number, part.extension)] = part.data;
199
206
  }
200
207
  emitSheetParts(files, perSheet, sheetXml);
201
208
  for (const { table, number } of allTables) {
202
- files[`xl/tables/table${number}.xml`] = strToU8(tableXml(table, number));
209
+ files[tablePart(number)] = strToU8(tableXml(table, number));
203
210
  }
204
211
  emitPivotParts(files, allPivots);
205
212
  emitPreservedParts(files, preserved.parts);
206
213
  return files;
207
214
  }
215
+ export function buildPackageParts(workbook, options = {}) {
216
+ const sheets = workbook.worksheets;
217
+ if (sheets.length === 0) {
218
+ throw new AuthoringError('cannot write a workbook with no worksheets: a zero-sheet package is corrupt to Excel');
219
+ }
220
+ const sharedStrings = options.useSharedStrings ? new SharedStringTable() : null;
221
+ const styles = options.styles ?? createStyleRegistry(workbook);
222
+ const plan = planPackage(workbook, sheets);
223
+ const sheetXml = serialiseSheets(workbook, sheets, plan, styles, sharedStrings, options.flushed);
224
+ return emitPackageParts({ workbook, sheets, plan, styles, sharedStrings, sheetXml });
225
+ }
208
226
  function emitSheetParts(files, perSheet, sheetXml) {
209
227
  perSheet.forEach((plan, i) => {
210
228
  const { tables, drawing, comments, threadedComments, printerSettings, background, hyperlinks, preservedRefs, pivots, } = plan;
211
229
  const hasExternalHyperlink = hyperlinks.some((link) => link.relId !== undefined);
212
- files[`xl/worksheets/sheet${i + 1}.xml`] = strToU8(sheetXml[i]);
230
+ files[worksheetPart(i + 1)] = strToU8(sheetXml[i]);
213
231
  if (tables.length > 0 ||
214
232
  drawing !== null ||
215
233
  comments !== null ||
@@ -219,42 +237,48 @@ function emitSheetParts(files, perSheet, sheetXml) {
219
237
  hasExternalHyperlink ||
220
238
  preservedRefs.length > 0 ||
221
239
  pivots.length > 0) {
222
- files[`xl/worksheets/_rels/sheet${i + 1}.xml.rels`] = strToU8(worksheetRelsXml(tables, drawing, comments, threadedComments, printerSettings, background, hyperlinks, preservedRefs, pivots));
240
+ files[relsPathFor(worksheetPart(i + 1))] = strToU8(worksheetRelsXml(tables, drawing, comments, threadedComments, printerSettings, background, hyperlinks, preservedRefs, pivots));
223
241
  }
224
242
  if (printerSettings !== null) {
225
- files[`xl/printerSettings/printerSettings${printerSettings.number}.bin`] =
226
- printerSettings.data;
243
+ files[printerSettingsPart(printerSettings.number)] = printerSettings.data;
227
244
  }
228
245
  if (drawing !== null) {
229
- files[`xl/drawings/drawing${drawing.number}.xml`] = strToU8(drawingXml(drawing.images));
230
- const targets = drawing.images.map((image) => `../media/image${image.mediaNumber}.${image.extension}`);
231
- files[`xl/drawings/_rels/drawing${drawing.number}.xml.rels`] = strToU8(drawingRelsXml(targets));
246
+ const drawingPath = drawingPart(drawing.number);
247
+ files[drawingPath] = strToU8(drawingXml(drawing.images));
248
+ const targets = drawing.images.map((image) => relativePartPath(drawingPath, mediaPart(image.mediaNumber, image.extension)));
249
+ files[relsPathFor(drawingPath)] = strToU8(drawingRelsXml(targets));
232
250
  }
233
251
  if (comments !== null) {
234
- files[`xl/comments${comments.number}.xml`] = strToU8(commentsXml(comments.comments));
235
- files[`xl/drawings/vmlDrawing${comments.number}.vml`] = strToU8(vmlDrawingXml(comments.comments));
252
+ files[commentsPart(comments.number)] = strToU8(commentsXml(comments.comments));
253
+ files[vmlDrawingPart(comments.number)] = strToU8(vmlDrawingXml(comments.comments));
236
254
  }
237
255
  if (threadedComments !== null) {
238
- files[`xl/threadedComments/threadedComment${threadedComments.number}.xml`] = strToU8(threadedCommentsXml(threadedComments.threads));
256
+ files[threadedCommentsPart(threadedComments.number)] = strToU8(threadedCommentsXml(threadedComments.threads));
239
257
  }
240
258
  });
241
259
  }
242
260
  function emitPivotParts(files, allPivots) {
243
261
  for (const pivot of allPivots) {
244
262
  const { number, cacheId, table } = pivot;
245
- files[`xl/pivotTables/pivotTable${number}.xml`] = strToU8(pivotTableXml(table, `PivotTable${number}`, cacheId));
246
- files[`xl/pivotTables/_rels/pivotTable${number}.xml.rels`] = strToU8(relsPartXml([
263
+ const tablePath = pivotTablePart(number);
264
+ const definitionPath = pivotCacheDefinitionPart(number);
265
+ files[tablePath] = strToU8(pivotTableXml(table, `PivotTable${number}`, cacheId));
266
+ files[relsPathFor(tablePath)] = strToU8(relsPartXml([
247
267
  {
248
268
  id: 'rId1',
249
269
  type: REL.pivotCacheDefinition,
250
- target: `../pivotCache/pivotCacheDefinition${number}.xml`,
270
+ target: relativePartPath(tablePath, definitionPath),
251
271
  },
252
272
  ]));
253
- files[`xl/pivotCache/pivotCacheDefinition${number}.xml`] = strToU8(pivotCacheDefinitionXml(table));
254
- files[`xl/pivotCache/_rels/pivotCacheDefinition${number}.xml.rels`] = strToU8(relsPartXml([
255
- { id: 'rId1', type: REL.pivotCacheRecords, target: `pivotCacheRecords${number}.xml` },
273
+ files[definitionPath] = strToU8(pivotCacheDefinitionXml(table));
274
+ files[relsPathFor(definitionPath)] = strToU8(relsPartXml([
275
+ {
276
+ id: 'rId1',
277
+ type: REL.pivotCacheRecords,
278
+ target: relativePartPath(definitionPath, pivotCacheRecordsPart(number)),
279
+ },
256
280
  ]));
257
- files[`xl/pivotCache/pivotCacheRecords${number}.xml`] = strToU8(pivotCacheRecordsXml(table));
281
+ files[pivotCacheRecordsPart(number)] = strToU8(pivotCacheRecordsXml(table));
258
282
  }
259
283
  }
260
284
  function emitPreservedParts(files, parts) {
@@ -0,0 +1,2 @@
1
+ /** The SHA-512 digest of `message`, as its 64 bytes. */
2
+ export declare function sha512(message: Uint8Array): Uint8Array;
package/dist/sha512.js ADDED
@@ -0,0 +1,146 @@
1
+ const BLOCK_BYTES = 128;
2
+ const DIGEST_BYTES = 64;
3
+ const K = new Uint32Array([
4
+ 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc,
5
+ 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118,
6
+ 0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe, 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2,
7
+ 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694,
8
+ 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65,
9
+ 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5,
10
+ 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4,
11
+ 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70,
12
+ 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df,
13
+ 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b,
14
+ 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30,
15
+ 0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910, 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8,
16
+ 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8,
17
+ 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3,
18
+ 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec,
19
+ 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b,
20
+ 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178,
21
+ 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b,
22
+ 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c,
23
+ 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817,
24
+ ]);
25
+ const INITIAL = new Uint32Array([
26
+ 0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1,
27
+ 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179,
28
+ ]);
29
+ function word(words, index) {
30
+ return words[index] ?? 0;
31
+ }
32
+ export function sha512(message) {
33
+ const blocks = Math.ceil((message.length + 17) / BLOCK_BYTES);
34
+ const padded = new Uint8Array(blocks * BLOCK_BYTES);
35
+ padded.set(message);
36
+ padded[message.length] = 0x80;
37
+ const view = new DataView(padded.buffer);
38
+ const bits = message.length * 8;
39
+ view.setUint32(padded.length - 8, Math.floor(bits / 0x1_0000_0000), false);
40
+ view.setUint32(padded.length - 4, bits >>> 0, false);
41
+ const h = Uint32Array.from(INITIAL);
42
+ const schedule = new Uint32Array(160);
43
+ for (let block = 0; block < blocks; block++)
44
+ compress(h, view, block * BLOCK_BYTES, schedule);
45
+ const digest = new Uint8Array(DIGEST_BYTES);
46
+ const out = new DataView(digest.buffer);
47
+ for (let i = 0; i < 16; i++)
48
+ out.setUint32(i * 4, word(h, i), false);
49
+ return digest;
50
+ }
51
+ function compress(h, input, offset, schedule) {
52
+ for (let t = 0; t < 16; t++) {
53
+ schedule[t * 2] = input.getUint32(offset + t * 8, false);
54
+ schedule[t * 2 + 1] = input.getUint32(offset + t * 8 + 4, false);
55
+ }
56
+ for (let t = 16; t < 80; t++) {
57
+ const xHi = word(schedule, (t - 15) * 2);
58
+ const xLo = word(schedule, (t - 15) * 2 + 1);
59
+ const s0Hi = ((xHi >>> 1) | (xLo << 31)) ^ ((xHi >>> 8) | (xLo << 24)) ^ (xHi >>> 7);
60
+ const s0Lo = ((xLo >>> 1) | (xHi << 31)) ^ ((xLo >>> 8) | (xHi << 24)) ^ ((xLo >>> 7) | (xHi << 25));
61
+ const yHi = word(schedule, (t - 2) * 2);
62
+ const yLo = word(schedule, (t - 2) * 2 + 1);
63
+ const s1Hi = ((yHi >>> 19) | (yLo << 13)) ^ ((yLo >>> 29) | (yHi << 3)) ^ (yHi >>> 6);
64
+ const s1Lo = ((yLo >>> 19) | (yHi << 13)) ^ ((yHi >>> 29) | (yLo << 3)) ^ ((yLo >>> 6) | (yHi << 26));
65
+ const lo = (s1Lo >>> 0) +
66
+ word(schedule, (t - 7) * 2 + 1) +
67
+ (s0Lo >>> 0) +
68
+ word(schedule, (t - 16) * 2 + 1);
69
+ const hi = (s1Hi >>> 0) +
70
+ word(schedule, (t - 7) * 2) +
71
+ (s0Hi >>> 0) +
72
+ word(schedule, (t - 16) * 2) +
73
+ Math.floor(lo / 0x1_0000_0000);
74
+ schedule[t * 2] = hi >>> 0;
75
+ schedule[t * 2 + 1] = lo >>> 0;
76
+ }
77
+ let aHi = word(h, 0);
78
+ let aLo = word(h, 1);
79
+ let bHi = word(h, 2);
80
+ let bLo = word(h, 3);
81
+ let cHi = word(h, 4);
82
+ let cLo = word(h, 5);
83
+ let dHi = word(h, 6);
84
+ let dLo = word(h, 7);
85
+ let eHi = word(h, 8);
86
+ let eLo = word(h, 9);
87
+ let fHi = word(h, 10);
88
+ let fLo = word(h, 11);
89
+ let gHi = word(h, 12);
90
+ let gLo = word(h, 13);
91
+ let hHi = word(h, 14);
92
+ let hLo = word(h, 15);
93
+ for (let t = 0; t < 80; t++) {
94
+ const bigS1Hi = ((eHi >>> 14) | (eLo << 18)) ^ ((eHi >>> 18) | (eLo << 14)) ^ ((eLo >>> 9) | (eHi << 23));
95
+ const bigS1Lo = ((eLo >>> 14) | (eHi << 18)) ^ ((eLo >>> 18) | (eHi << 14)) ^ ((eHi >>> 9) | (eLo << 23));
96
+ const chHi = (eHi & fHi) ^ (~eHi & gHi);
97
+ const chLo = (eLo & fLo) ^ (~eLo & gLo);
98
+ const t1Lo = hLo + (bigS1Lo >>> 0) + (chLo >>> 0) + word(K, t * 2 + 1) + word(schedule, t * 2 + 1);
99
+ const t1Hi = (hHi +
100
+ (bigS1Hi >>> 0) +
101
+ (chHi >>> 0) +
102
+ word(K, t * 2) +
103
+ word(schedule, t * 2) +
104
+ Math.floor(t1Lo / 0x1_0000_0000)) >>>
105
+ 0;
106
+ const t1LoWrapped = t1Lo >>> 0;
107
+ const bigS0Hi = ((aHi >>> 28) | (aLo << 4)) ^ ((aLo >>> 2) | (aHi << 30)) ^ ((aLo >>> 7) | (aHi << 25));
108
+ const bigS0Lo = ((aLo >>> 28) | (aHi << 4)) ^ ((aHi >>> 2) | (aLo << 30)) ^ ((aHi >>> 7) | (aLo << 25));
109
+ const majHi = (aHi & bHi) ^ (aHi & cHi) ^ (bHi & cHi);
110
+ const majLo = (aLo & bLo) ^ (aLo & cLo) ^ (bLo & cLo);
111
+ const t2Lo = (bigS0Lo >>> 0) + (majLo >>> 0);
112
+ const t2Hi = ((bigS0Hi >>> 0) + (majHi >>> 0) + Math.floor(t2Lo / 0x1_0000_0000)) >>> 0;
113
+ const t2LoWrapped = t2Lo >>> 0;
114
+ hHi = gHi;
115
+ hLo = gLo;
116
+ gHi = fHi;
117
+ gLo = fLo;
118
+ fHi = eHi;
119
+ fLo = eLo;
120
+ const nextELo = dLo + t1LoWrapped;
121
+ eHi = (dHi + t1Hi + Math.floor(nextELo / 0x1_0000_0000)) >>> 0;
122
+ eLo = nextELo >>> 0;
123
+ dHi = cHi;
124
+ dLo = cLo;
125
+ cHi = bHi;
126
+ cLo = bLo;
127
+ bHi = aHi;
128
+ bLo = aLo;
129
+ const nextALo = t1LoWrapped + t2LoWrapped;
130
+ aHi = (t1Hi + t2Hi + Math.floor(nextALo / 0x1_0000_0000)) >>> 0;
131
+ aLo = nextALo >>> 0;
132
+ }
133
+ addInto(h, 0, aHi, aLo);
134
+ addInto(h, 2, bHi, bLo);
135
+ addInto(h, 4, cHi, cLo);
136
+ addInto(h, 6, dHi, dLo);
137
+ addInto(h, 8, eHi, eLo);
138
+ addInto(h, 10, fHi, fLo);
139
+ addInto(h, 12, gHi, gLo);
140
+ addInto(h, 14, hHi, hLo);
141
+ }
142
+ function addInto(h, index, hi, lo) {
143
+ const sumLo = word(h, index + 1) + lo;
144
+ h[index] = (word(h, index) + hi + Math.floor(sumLo / 0x1_0000_0000)) >>> 0;
145
+ h[index + 1] = sumLo >>> 0;
146
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Build a narrowing predicate for a closed token union.
3
+ *
4
+ * `members` is keyed by the union, so the compiler refuses a foreign member and an omitted one
5
+ * alike. A bare list of string literals is checked one way only: it cannot name a token the union
6
+ * does not, but it can quietly omit one the union does, and a guard narrower than its union
7
+ * silently drops a token real files legitimately carry.
8
+ *
9
+ * Deriving the union from the list instead (`(typeof TOKENS)[number]`, as `table-style.ts` does)
10
+ * would make divergence impossible rather than merely detected, and is the better shape for a type
11
+ * that is not public. It is not available to a published union: the API reference renders a
12
+ * declaration by slicing its own source text, so a derived alias reaches the docs as that
13
+ * expression instead of as its members.
14
+ */
15
+ export declare function tokenSet<T extends string>(members: Record<T, true>): (value: string) => value is T;
@@ -0,0 +1,4 @@
1
+ export function tokenSet(members) {
2
+ const set = new Set(Object.keys(members));
3
+ return (value) => set.has(value);
4
+ }
@@ -1,3 +1,4 @@
1
+ export { concat } from '../bytes.ts';
1
2
  /** Read a little-endian `uint16`. @throws {VbaParseError} if the two bytes are not both in `buf`. */
2
3
  export declare function readU16(buf: Uint8Array, at: number): number;
3
4
  /** Read a little-endian `uint32`. @throws {VbaParseError} if the four bytes are not all in `buf`. */
@@ -8,5 +9,3 @@ export declare function readU32(buf: Uint8Array, at: number): number;
8
9
  * producer and a half code unit carries nothing to decode.
9
10
  */
10
11
  export declare function decodeUtf16le(bytes: Uint8Array): string;
11
- /** Join byte chunks into one buffer. */
12
- export declare function concat(chunks: Uint8Array[]): Uint8Array;
package/dist/vba/bytes.js CHANGED
@@ -1,3 +1,4 @@
1
+ export { concat } from '../bytes.js';
1
2
  import { VbaParseError } from './errors.js';
2
3
  function truncated(at, need, length) {
3
4
  return new VbaParseError(`read of ${need} bytes at offset ${at} runs past the end of a ${length}-byte buffer`);
@@ -26,15 +27,3 @@ export function decodeUtf16le(bytes) {
26
27
  }
27
28
  return s;
28
29
  }
29
- export function concat(chunks) {
30
- let total = 0;
31
- for (const c of chunks)
32
- total += c.length;
33
- const out = new Uint8Array(total);
34
- let off = 0;
35
- for (const c of chunks) {
36
- out.set(c, off);
37
- off += c.length;
38
- }
39
- return out;
40
- }
@@ -0,0 +1,24 @@
1
+ /** Sector chain markers ([MS-CFB] 2.2). */
2
+ export declare const FREESECT = 4294967295;
3
+ export declare const ENDOFCHAIN = 4294967294;
4
+ export declare const FATSECT = 4294967293;
5
+ export declare const DIFSECT = 4294967292;
6
+ /**
7
+ * Sector values 0xFFFFFFFA..0xFFFFFFFF are the reserved markers above, not data-sector indices, so
8
+ * any value at or above this ceiling is chain-terminal. A reader walking a hostile chain tests the
9
+ * ceiling rather than the four markers by name: an unassigned reserved value is terminal too.
10
+ */
11
+ export declare const MAX_REGULAR_SECTOR = 4294967290;
12
+ /** Directory-tree links reuse the sector convention: NOSTREAM, and any value at or above
13
+ * {@link MAX_REGULAR_SECTOR}, mean "no such sibling or child". */
14
+ export declare const NOSTREAM = 4294967295;
15
+ /** Object types ([MS-CFB] 2.6.1). */
16
+ export declare const TYPE_EMPTY = 0;
17
+ export declare const TYPE_STORAGE = 1;
18
+ export declare const TYPE_STREAM = 2;
19
+ export declare const TYPE_ROOT = 5;
20
+ /** A directory entry is a fixed 128 bytes ([MS-CFB] 2.6.1), whatever the sector size. */
21
+ export declare const DIR_ENTRY_SIZE = 128;
22
+ /** 32 UTF-16 code units including the NUL terminator. This is also why a VBA module name is capped
23
+ * at 31: the module is a stream, and the stream's name is the module's. */
24
+ export declare const MAX_NAME_CHARS = 31;
@@ -0,0 +1,12 @@
1
+ export const FREESECT = 0xffffffff;
2
+ export const ENDOFCHAIN = 0xfffffffe;
3
+ export const FATSECT = 0xfffffffd;
4
+ export const DIFSECT = 0xfffffffc;
5
+ export const MAX_REGULAR_SECTOR = 0xfffffffa;
6
+ export const NOSTREAM = 0xffffffff;
7
+ export const TYPE_EMPTY = 0;
8
+ export const TYPE_STORAGE = 1;
9
+ export const TYPE_STREAM = 2;
10
+ export const TYPE_ROOT = 5;
11
+ export const DIR_ENTRY_SIZE = 128;
12
+ export const MAX_NAME_CHARS = 31;