@qvac/harness 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +70 -1
  2. package/NOTICE.md +16 -0
  3. package/dist-lib/lib/capabilities/completion.d.ts +1 -0
  4. package/dist-lib/lib/capabilities/completion.js +46 -8
  5. package/dist-lib/lib/capabilities/tool-turns.d.ts +3 -0
  6. package/dist-lib/lib/capabilities/tool-turns.js +7 -0
  7. package/dist-lib/lib/context-window.js +2 -2
  8. package/dist-lib/lib/helpers/run-outcome.d.ts +4 -1
  9. package/dist-lib/lib/helpers/run-outcome.js +14 -8
  10. package/dist-lib/lib/helpers/tool-call-markup.js +4 -4
  11. package/dist-lib/lib/hooks/built-in/arg-repair.js +7 -0
  12. package/dist-lib/lib/office/engine/codes.d.ts +62 -0
  13. package/dist-lib/lib/office/engine/codes.js +61 -0
  14. package/dist-lib/lib/office/engine/docx/document.d.ts +216 -0
  15. package/dist-lib/lib/office/engine/docx/document.js +1152 -0
  16. package/dist-lib/lib/office/engine/docx/index.d.ts +7 -0
  17. package/dist-lib/lib/office/engine/docx/index.js +8 -0
  18. package/dist-lib/lib/office/engine/docx/styles.d.ts +31 -0
  19. package/dist-lib/lib/office/engine/docx/styles.js +173 -0
  20. package/dist-lib/lib/office/engine/docx/template.d.ts +18 -0
  21. package/dist-lib/lib/office/engine/docx/template.js +133 -0
  22. package/dist-lib/lib/office/engine/docx/text.d.ts +55 -0
  23. package/dist-lib/lib/office/engine/docx/text.js +538 -0
  24. package/dist-lib/lib/office/engine/drawingml/chart.d.ts +63 -0
  25. package/dist-lib/lib/office/engine/drawingml/chart.js +539 -0
  26. package/dist-lib/lib/office/engine/drawingml/text.d.ts +35 -0
  27. package/dist-lib/lib/office/engine/drawingml/text.js +352 -0
  28. package/dist-lib/lib/office/engine/errors.d.ts +6 -0
  29. package/dist-lib/lib/office/engine/errors.js +12 -0
  30. package/dist-lib/lib/office/engine/image.d.ts +8 -0
  31. package/dist-lib/lib/office/engine/image.js +48 -0
  32. package/dist-lib/lib/office/engine/index.d.ts +12 -0
  33. package/dist-lib/lib/office/engine/index.js +7 -0
  34. package/dist-lib/lib/office/engine/opc/content-types.d.ts +17 -0
  35. package/dist-lib/lib/office/engine/opc/content-types.js +74 -0
  36. package/dist-lib/lib/office/engine/opc/media.d.ts +13 -0
  37. package/dist-lib/lib/office/engine/opc/media.js +11 -0
  38. package/dist-lib/lib/office/engine/opc/package.d.ts +51 -0
  39. package/dist-lib/lib/office/engine/opc/package.js +324 -0
  40. package/dist-lib/lib/office/engine/opc/paths.d.ts +7 -0
  41. package/dist-lib/lib/office/engine/opc/paths.js +46 -0
  42. package/dist-lib/lib/office/engine/opc/properties.d.ts +7 -0
  43. package/dist-lib/lib/office/engine/opc/properties.js +75 -0
  44. package/dist-lib/lib/office/engine/opc/relationships.d.ts +23 -0
  45. package/dist-lib/lib/office/engine/opc/relationships.js +65 -0
  46. package/dist-lib/lib/office/engine/opc/zip.d.ts +15 -0
  47. package/dist-lib/lib/office/engine/opc/zip.js +264 -0
  48. package/dist-lib/lib/office/engine/pptx/fit.d.ts +12 -0
  49. package/dist-lib/lib/office/engine/pptx/fit.js +37 -0
  50. package/dist-lib/lib/office/engine/pptx/index.d.ts +8 -0
  51. package/dist-lib/lib/office/engine/pptx/index.js +8 -0
  52. package/dist-lib/lib/office/engine/pptx/inherit.d.ts +29 -0
  53. package/dist-lib/lib/office/engine/pptx/inherit.js +155 -0
  54. package/dist-lib/lib/office/engine/pptx/notes.d.ts +5 -0
  55. package/dist-lib/lib/office/engine/pptx/notes.js +85 -0
  56. package/dist-lib/lib/office/engine/pptx/presentation.d.ts +54 -0
  57. package/dist-lib/lib/office/engine/pptx/presentation.js +749 -0
  58. package/dist-lib/lib/office/engine/pptx/read.d.ts +9 -0
  59. package/dist-lib/lib/office/engine/pptx/read.js +66 -0
  60. package/dist-lib/lib/office/engine/pptx/shapes.d.ts +52 -0
  61. package/dist-lib/lib/office/engine/pptx/shapes.js +276 -0
  62. package/dist-lib/lib/office/engine/pptx/slides.d.ts +27 -0
  63. package/dist-lib/lib/office/engine/pptx/slides.js +123 -0
  64. package/dist-lib/lib/office/engine/pptx/template.d.ts +20 -0
  65. package/dist-lib/lib/office/engine/pptx/template.js +391 -0
  66. package/dist-lib/lib/office/engine/pptx/types.d.ts +120 -0
  67. package/dist-lib/lib/office/engine/pptx/types.js +1 -0
  68. package/dist-lib/lib/office/engine/units.d.ts +3 -0
  69. package/dist-lib/lib/office/engine/units.js +12 -0
  70. package/dist-lib/lib/office/engine/utf8.d.ts +2 -0
  71. package/dist-lib/lib/office/engine/utf8.js +103 -0
  72. package/dist-lib/lib/office/engine/xlsx/formula.d.ts +49 -0
  73. package/dist-lib/lib/office/engine/xlsx/formula.js +381 -0
  74. package/dist-lib/lib/office/engine/xlsx/index.d.ts +8 -0
  75. package/dist-lib/lib/office/engine/xlsx/index.js +8 -0
  76. package/dist-lib/lib/office/engine/xlsx/refs.d.ts +22 -0
  77. package/dist-lib/lib/office/engine/xlsx/refs.js +82 -0
  78. package/dist-lib/lib/office/engine/xlsx/reshape.d.ts +21 -0
  79. package/dist-lib/lib/office/engine/xlsx/reshape.js +424 -0
  80. package/dist-lib/lib/office/engine/xlsx/sheet.d.ts +108 -0
  81. package/dist-lib/lib/office/engine/xlsx/sheet.js +662 -0
  82. package/dist-lib/lib/office/engine/xlsx/strings.d.ts +15 -0
  83. package/dist-lib/lib/office/engine/xlsx/strings.js +93 -0
  84. package/dist-lib/lib/office/engine/xlsx/styles.d.ts +38 -0
  85. package/dist-lib/lib/office/engine/xlsx/styles.js +369 -0
  86. package/dist-lib/lib/office/engine/xlsx/template.d.ts +7 -0
  87. package/dist-lib/lib/office/engine/xlsx/template.js +54 -0
  88. package/dist-lib/lib/office/engine/xlsx/workbook.d.ts +161 -0
  89. package/dist-lib/lib/office/engine/xlsx/workbook.js +850 -0
  90. package/dist-lib/lib/office/engine/xlsx/write.d.ts +2 -0
  91. package/dist-lib/lib/office/engine/xlsx/write.js +42 -0
  92. package/dist-lib/lib/office/engine/xml.d.ts +45 -0
  93. package/dist-lib/lib/office/engine/xml.js +258 -0
  94. package/dist-lib/lib/tools/exec/scope.d.ts +2 -0
  95. package/dist-lib/lib/tools/exec/scope.js +33 -2
  96. package/dist-lib/lib/tools/pdf/build.d.ts +43 -0
  97. package/dist-lib/lib/tools/pdf/build.js +150 -0
  98. package/dist-lib/lib/tools/pdf/images.d.ts +7 -0
  99. package/dist-lib/lib/tools/pdf/images.js +82 -0
  100. package/dist-lib/lib/tools/pdf/request.d.ts +63 -0
  101. package/dist-lib/lib/tools/pdf/request.js +335 -0
  102. package/dist-lib/lib/tools/pdf.d.ts +6 -0
  103. package/dist-lib/lib/tools/pdf.js +302 -0
  104. package/dist-lib/lib/tools/skill.js +14 -0
  105. package/dist-lib/lib/tools/weather-lookup/geocode.d.ts +26 -0
  106. package/dist-lib/lib/tools/weather-lookup/geocode.js +110 -0
  107. package/dist-lib/lib/tools/weather-lookup/place.js +5 -1
  108. package/dist-lib/lib/tools/weather-lookup.d.ts +2 -1
  109. package/dist-lib/lib/tools/weather-lookup.js +115 -84
  110. package/package.json +29 -1
  111. package/worker/harness-sidecar.mjs +3 -1
@@ -0,0 +1,93 @@
1
+ import * as xml from '../xml.js';
2
+ const X = xml.ns.x;
3
+ const TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml';
4
+ const REL = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings';
5
+ const XML_NS = 'http://www.w3.org/XML/1998/namespace';
6
+ const ESCAPE = /_x([0-9A-Fa-f]{4})_/g;
7
+ const LITERAL = /_(?=x[0-9A-Fa-f]{4}_)/g;
8
+ const UNWRITABLE = /[\u0000-\u0008\u000B-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
9
+ function decodeText(text) {
10
+ return text.replace(ESCAPE, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
11
+ }
12
+ function encodeText(text) {
13
+ return text
14
+ .replace(LITERAL, '_x005F_')
15
+ .replace(UNWRITABLE, (ch) => `_x${ch.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0')}_`);
16
+ }
17
+ export function runText(container) {
18
+ const t = xml.child(container, X, 't');
19
+ if (t) {
20
+ return decodeText(xml.text(t));
21
+ }
22
+ return xml
23
+ .children(container, X, 'r')
24
+ .map((r) => decodeText(xml.text(xml.child(r, X, 't'))))
25
+ .join('');
26
+ }
27
+ export class SharedStrings {
28
+ package;
29
+ workbookPart;
30
+ part;
31
+ items;
32
+ index;
33
+ constructor(pkg, workbookPart) {
34
+ this.package = pkg;
35
+ this.workbookPart = workbookPart;
36
+ this.part = pkg.related(workbookPart, REL)[0] ?? null;
37
+ this.items = null;
38
+ this.index = new Map();
39
+ }
40
+ get(i) {
41
+ return this.load()[i] ?? null;
42
+ }
43
+ add(value) {
44
+ const items = this.load();
45
+ const known = this.index.get(value);
46
+ if (known !== undefined) {
47
+ return known;
48
+ }
49
+ const part = this.ensurePart();
50
+ const doc = this.package.xml(part);
51
+ const top = xml.root(doc);
52
+ const si = xml.named(doc, 'si', X);
53
+ const t = xml.append(si, xml.named(doc, 't', X));
54
+ if (/^\s|\s$/.test(value)) {
55
+ t.setAttributeNS(XML_NS, 'xml:space', 'preserve');
56
+ }
57
+ t.appendChild(doc.createTextNode(encodeText(value)));
58
+ top.appendChild(si);
59
+ const i = items.length;
60
+ items.push(value);
61
+ this.index.set(value, i);
62
+ top.setAttribute('uniqueCount', String(items.length));
63
+ top.removeAttribute('count');
64
+ this.package.touch(part);
65
+ return i;
66
+ }
67
+ ensurePart() {
68
+ if (this.part)
69
+ return this.part;
70
+ const part = '/xl/sharedStrings.xml';
71
+ this.package.add(part, xml.parse(`<sst xmlns="${X}"/>`), TYPE);
72
+ this.package.rels(this.workbookPart).add(REL, part);
73
+ this.part = part;
74
+ return part;
75
+ }
76
+ load() {
77
+ if (this.items)
78
+ return this.items;
79
+ const items = [];
80
+ this.items = items;
81
+ if (!this.part) {
82
+ return items;
83
+ }
84
+ for (const si of xml.children(this.package.root(this.part), X, 'si')) {
85
+ const value = runText(si);
86
+ if (!this.index.has(value)) {
87
+ this.index.set(value, items.length);
88
+ }
89
+ items.push(value);
90
+ }
91
+ return items;
92
+ }
93
+ }
@@ -0,0 +1,38 @@
1
+ import type { Document } from '../xml.ts';
2
+ import type { Package } from '../opc/package.ts';
3
+ export type CellAlign = 'left' | 'center' | 'right' | 'justify';
4
+ export interface CellStyle {
5
+ format?: string;
6
+ bold?: boolean;
7
+ italic?: boolean;
8
+ underline?: boolean;
9
+ color?: string;
10
+ fill?: string;
11
+ size?: number;
12
+ font?: string;
13
+ align?: CellAlign;
14
+ wrap?: boolean;
15
+ }
16
+ export declare function isDateCode(code: string): boolean;
17
+ export declare function validate(style: unknown): CellStyle;
18
+ export declare class Styles {
19
+ package: Package;
20
+ workbookPart: string;
21
+ part: string | null;
22
+ constructor(pkg: Package, workbookPart: string);
23
+ get doc(): Document;
24
+ formatCode(xfIndex: number): string;
25
+ isDate(xfIndex: number): boolean;
26
+ derive(xfIndex: number, input: unknown): number;
27
+ private applyTo;
28
+ private align;
29
+ private ensurePart;
30
+ private touch;
31
+ private xfs;
32
+ private list;
33
+ private code;
34
+ private formatId;
35
+ private font;
36
+ private fill;
37
+ private dedupe;
38
+ }
@@ -0,0 +1,369 @@
1
+ import * as xml from '../xml.js';
2
+ import { DocsError } from '../errors.js';
3
+ import { isRecord } from '../../../guards.js';
4
+ const X = xml.ns.x;
5
+ const TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml';
6
+ const REL = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles';
7
+ const KNOWN = [
8
+ 'format',
9
+ 'bold',
10
+ 'italic',
11
+ 'underline',
12
+ 'color',
13
+ 'fill',
14
+ 'size',
15
+ 'font',
16
+ 'align',
17
+ 'wrap'
18
+ ];
19
+ const TOGGLES = ['bold', 'italic', 'underline', 'wrap'];
20
+ const COLORS = ['color', 'fill'];
21
+ const FONT_KEYS = ['bold', 'italic', 'underline', 'color', 'size', 'font'];
22
+ const ALIGN = {
23
+ left: 'left',
24
+ center: 'center',
25
+ right: 'right',
26
+ justify: 'justify'
27
+ };
28
+ const BUILTIN = new Map([
29
+ [0, 'General'],
30
+ [1, '0'],
31
+ [2, '0.00'],
32
+ [3, '#,##0'],
33
+ [4, '#,##0.00'],
34
+ [9, '0%'],
35
+ [10, '0.00%'],
36
+ [11, '0.00E+00'],
37
+ [12, '# ?/?'],
38
+ [13, '# ??/??'],
39
+ [14, 'mm-dd-yy'],
40
+ [15, 'd-mmm-yy'],
41
+ [16, 'd-mmm'],
42
+ [17, 'mmm-yy'],
43
+ [18, 'h:mm AM/PM'],
44
+ [19, 'h:mm:ss AM/PM'],
45
+ [20, 'h:mm'],
46
+ [21, 'h:mm:ss'],
47
+ [22, 'm/d/yy h:mm'],
48
+ [37, '#,##0 ;(#,##0)'],
49
+ [38, '#,##0 ;[Red](#,##0)'],
50
+ [39, '#,##0.00;(#,##0.00)'],
51
+ [40, '#,##0.00;[Red](#,##0.00)'],
52
+ [45, 'mm:ss'],
53
+ [46, '[h]:mm:ss'],
54
+ [47, 'mmss.0'],
55
+ [48, '##0.0E+0'],
56
+ [49, '@']
57
+ ]);
58
+ const DATE_IDS = new Set([14, 15, 16, 17, 18, 19, 20, 21, 22, 45, 46, 47]);
59
+ const STYLE_ORDER = [
60
+ 'numFmts',
61
+ 'fonts',
62
+ 'fills',
63
+ 'borders',
64
+ 'cellStyleXfs',
65
+ 'cellXfs',
66
+ 'cellStyles',
67
+ 'dxfs',
68
+ 'tableStyles',
69
+ 'colors',
70
+ 'extLst'
71
+ ];
72
+ const FONT_ORDER = [
73
+ 'b',
74
+ 'i',
75
+ 'strike',
76
+ 'condense',
77
+ 'extend',
78
+ 'outline',
79
+ 'shadow',
80
+ 'u',
81
+ 'vertAlign',
82
+ 'sz',
83
+ 'color',
84
+ 'name',
85
+ 'family',
86
+ 'charset',
87
+ 'scheme'
88
+ ];
89
+ const DEFAULT = `<styleSheet xmlns="${X}"><fonts count="1"><font><sz val="11"/><name val="Calibri"/><family val="2"/></font></fonts><fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills><borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders><cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs><cellXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/></cellXfs><cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles></styleSheet>`;
90
+ export function isDateCode(code) {
91
+ const stripped = code
92
+ .replace(/"[^"]*"/g, '')
93
+ .replace(/\\./g, '')
94
+ .replace(/\[(?!h\]|m\]|s\])[^\]]*\]/gi, '');
95
+ if (/^general$/i.test(stripped.trim())) {
96
+ return false;
97
+ }
98
+ return /[dmyhs]/i.test(stripped.split(';')[0]);
99
+ }
100
+ function isAlign(value) {
101
+ return typeof value === 'string' && value in ALIGN;
102
+ }
103
+ function checkValues(style) {
104
+ const { format, size, font, align } = style;
105
+ if (format !== undefined && !(typeof format === 'string' && format.length <= 255)) {
106
+ throw new DocsError('INVALID_STYLE', 'format must be a number format like "0.00" or "yyyy-mm-dd"');
107
+ }
108
+ const toggle = TOGGLES.find((key) => style[key] !== undefined && typeof style[key] !== 'boolean');
109
+ if (toggle) {
110
+ throw new DocsError('INVALID_STYLE', `${toggle} must be true or false`);
111
+ }
112
+ for (const key of COLORS) {
113
+ const value = style[key];
114
+ if (value !== undefined && !(typeof value === 'string' && /^#?[0-9a-fA-F]{6}$/.test(value))) {
115
+ throw new DocsError('INVALID_STYLE', `${key} must be a hex RGB value like "1A73E8", got ${String(value)}`);
116
+ }
117
+ }
118
+ if (size !== undefined && !(typeof size === 'number' && size >= 1 && size <= 409)) {
119
+ throw new DocsError('INVALID_STYLE', `size is in points, from 1 to 409, got ${String(size)}`);
120
+ }
121
+ if (font !== undefined && !(typeof font === 'string' && font.trim())) {
122
+ throw new DocsError('INVALID_STYLE', 'font must be a typeface name');
123
+ }
124
+ if (align !== undefined && !isAlign(align)) {
125
+ throw new DocsError('INVALID_STYLE', `align must be left, center, right or justify, got ${String(align)}`);
126
+ }
127
+ }
128
+ function str(value) {
129
+ return typeof value === 'string' ? value : undefined;
130
+ }
131
+ function bool(value) {
132
+ return typeof value === 'boolean' ? value : undefined;
133
+ }
134
+ export function validate(style) {
135
+ if (!isRecord(style) || Array.isArray(style)) {
136
+ throw new DocsError('INVALID_STYLE', 'style must be an object');
137
+ }
138
+ const unknownKey = Object.keys(style).find((key) => !KNOWN.includes(key));
139
+ if (unknownKey) {
140
+ throw new DocsError('INVALID_STYLE', `unknown cell style "${unknownKey}"; use ${KNOWN.join(', ')}`);
141
+ }
142
+ checkValues(style);
143
+ return {
144
+ format: str(style.format),
145
+ bold: bool(style.bold),
146
+ italic: bool(style.italic),
147
+ underline: bool(style.underline),
148
+ color: str(style.color),
149
+ fill: str(style.fill),
150
+ size: typeof style.size === 'number' ? style.size : undefined,
151
+ font: str(style.font),
152
+ align: isAlign(style.align) ? style.align : undefined,
153
+ wrap: bool(style.wrap)
154
+ };
155
+ }
156
+ function argb(color) {
157
+ return 'FF' + color.replace('#', '').toUpperCase();
158
+ }
159
+ function setFontChild(font, local, attributes) {
160
+ const doc = xml.owner(font);
161
+ const el = xml.child(font, X, local) ?? xml.insertOrdered(font, xml.named(doc, local, X), FONT_ORDER);
162
+ for (const attr of Array.from(el.attributes)) {
163
+ el.removeAttribute(attr.name);
164
+ }
165
+ xml.setAttributes(el, attributes);
166
+ }
167
+ function dropFontChild(font, local) {
168
+ for (const el of xml.children(font, X, local)) {
169
+ xml.remove(el);
170
+ }
171
+ }
172
+ function toggleFont(font, local, on) {
173
+ if (on === undefined)
174
+ return;
175
+ if (on) {
176
+ setFontChild(font, local, {});
177
+ }
178
+ else {
179
+ dropFontChild(font, local);
180
+ }
181
+ }
182
+ function editFont(font, style) {
183
+ toggleFont(font, 'b', style.bold);
184
+ toggleFont(font, 'i', style.italic);
185
+ toggleFont(font, 'u', style.underline);
186
+ if (style.size !== undefined) {
187
+ setFontChild(font, 'sz', { val: style.size });
188
+ }
189
+ if (style.color !== undefined) {
190
+ setFontChild(font, 'color', { rgb: argb(style.color) });
191
+ }
192
+ if (style.font !== undefined) {
193
+ setFontChild(font, 'name', { val: style.font });
194
+ dropFontChild(font, 'scheme');
195
+ }
196
+ }
197
+ export class Styles {
198
+ package;
199
+ workbookPart;
200
+ part;
201
+ constructor(pkg, workbookPart) {
202
+ this.package = pkg;
203
+ this.workbookPart = workbookPart;
204
+ this.part = pkg.related(workbookPart, REL)[0] ?? null;
205
+ }
206
+ get doc() {
207
+ return this.package.xml(this.ensurePart());
208
+ }
209
+ formatCode(xfIndex) {
210
+ if (!this.part)
211
+ return 'General';
212
+ const xf = this.xfs()[xfIndex || 0];
213
+ return this.code(xf ? Number(xf.getAttribute('numFmtId') || 0) : 0);
214
+ }
215
+ isDate(xfIndex) {
216
+ if (!this.part || !xfIndex)
217
+ return false;
218
+ const xf = this.xfs()[xfIndex];
219
+ if (!xf) {
220
+ return false;
221
+ }
222
+ const id = Number(xf.getAttribute('numFmtId') || 0);
223
+ if (DATE_IDS.has(id)) {
224
+ return true;
225
+ }
226
+ return id >= 164 && isDateCode(this.code(id));
227
+ }
228
+ derive(xfIndex, input) {
229
+ const style = validate(input);
230
+ const doc = this.doc;
231
+ const xfs = this.xfs();
232
+ const base = xfs[xfIndex || 0] ?? xfs[0];
233
+ const xf = base
234
+ ? xml.clone(base)
235
+ : xml.setAttributes(xml.named(doc, 'xf', X), {
236
+ numFmtId: 0,
237
+ fontId: 0,
238
+ fillId: 0,
239
+ borderId: 0,
240
+ xfId: 0
241
+ });
242
+ this.applyTo(xf, style);
243
+ const key = xf.toString();
244
+ const existing = xfs.findIndex((el) => el.toString() === key);
245
+ if (existing !== -1) {
246
+ return existing;
247
+ }
248
+ const list = this.list('cellXfs');
249
+ list.appendChild(xf);
250
+ const count = xml.children(list, X, 'xf').length;
251
+ list.setAttribute('count', String(count));
252
+ this.touch();
253
+ return count - 1;
254
+ }
255
+ applyTo(xf, style) {
256
+ if (style.format !== undefined) {
257
+ xf.setAttribute('numFmtId', String(this.formatId(style.format)));
258
+ xf.setAttribute('applyNumberFormat', '1');
259
+ }
260
+ if (FONT_KEYS.some((k) => style[k] !== undefined)) {
261
+ xf.setAttribute('fontId', String(this.font(Number(xf.getAttribute('fontId') || 0), style)));
262
+ xf.setAttribute('applyFont', '1');
263
+ }
264
+ if (style.fill !== undefined) {
265
+ xf.setAttribute('fillId', String(this.fill(style.fill)));
266
+ xf.setAttribute('applyFill', '1');
267
+ }
268
+ if (style.align !== undefined || style.wrap !== undefined) {
269
+ this.align(xf, style);
270
+ }
271
+ }
272
+ align(xf, style) {
273
+ const doc = xml.owner(xf);
274
+ let alignment = xml.child(xf, X, 'alignment');
275
+ if (!alignment) {
276
+ alignment = xml.named(doc, 'alignment', X);
277
+ xf.insertBefore(alignment, xf.firstChild);
278
+ }
279
+ if (style.align !== undefined) {
280
+ alignment.setAttribute('horizontal', ALIGN[style.align]);
281
+ }
282
+ if (style.wrap !== undefined) {
283
+ alignment.setAttribute('wrapText', style.wrap ? '1' : '0');
284
+ }
285
+ xf.setAttribute('applyAlignment', '1');
286
+ }
287
+ ensurePart() {
288
+ if (this.part)
289
+ return this.part;
290
+ const part = '/xl/styles.xml';
291
+ this.package.add(part, xml.parse(DEFAULT), TYPE);
292
+ this.package.rels(this.workbookPart).add(REL, part);
293
+ this.part = part;
294
+ return part;
295
+ }
296
+ touch() {
297
+ this.package.touch(this.ensurePart());
298
+ }
299
+ xfs() {
300
+ const list = xml.child(xml.root(this.doc), X, 'cellXfs');
301
+ return list ? xml.children(list, X, 'xf') : [];
302
+ }
303
+ list(local) {
304
+ const doc = this.doc;
305
+ const top = xml.root(doc);
306
+ const existing = xml.child(top, X, local);
307
+ if (existing) {
308
+ return existing;
309
+ }
310
+ return xml.insertOrdered(top, xml.setAttributes(xml.named(doc, local, X), { count: 0 }), STYLE_ORDER);
311
+ }
312
+ code(id) {
313
+ const builtin = BUILTIN.get(id);
314
+ if (builtin !== undefined) {
315
+ return builtin;
316
+ }
317
+ const list = xml.child(xml.root(this.doc), X, 'numFmts');
318
+ const el = list
319
+ ? xml.children(list, X, 'numFmt').find((n) => Number(n.getAttribute('numFmtId')) === id)
320
+ : undefined;
321
+ return el?.getAttribute('formatCode') || 'General';
322
+ }
323
+ formatId(code) {
324
+ for (const [id, value] of BUILTIN) {
325
+ if (value === code)
326
+ return id;
327
+ }
328
+ const list = this.list('numFmts');
329
+ const existing = xml.children(list, X, 'numFmt');
330
+ const match = existing.find((n) => n.getAttribute('formatCode') === code);
331
+ if (match) {
332
+ return Number(match.getAttribute('numFmtId'));
333
+ }
334
+ const id = Math.max(163, ...existing.map((n) => Number(n.getAttribute('numFmtId')))) + 1;
335
+ list.appendChild(xml.setAttributes(xml.named(this.doc, 'numFmt', X), { numFmtId: id, formatCode: code }));
336
+ list.setAttribute('count', String(existing.length + 1));
337
+ this.touch();
338
+ return id;
339
+ }
340
+ font(baseId, style) {
341
+ const list = this.list('fonts');
342
+ const fonts = xml.children(list, X, 'font');
343
+ const base = fonts[baseId];
344
+ const font = base ? xml.clone(base) : xml.named(this.doc, 'font', X);
345
+ editFont(font, style);
346
+ return this.dedupe(list, { el: font, siblings: fonts });
347
+ }
348
+ fill(color) {
349
+ const doc = this.doc;
350
+ const list = this.list('fills');
351
+ const fills = xml.children(list, X, 'fill');
352
+ const fill = xml.named(doc, 'fill', X);
353
+ const pattern = xml.append(fill, xml.setAttributes(xml.named(doc, 'patternFill', X), { patternType: 'solid' }));
354
+ pattern.appendChild(xml.setAttributes(xml.named(doc, 'fgColor', X), { rgb: argb(color) }));
355
+ pattern.appendChild(xml.setAttributes(xml.named(doc, 'bgColor', X), { indexed: 64 }));
356
+ return this.dedupe(list, { el: fill, siblings: fills });
357
+ }
358
+ dedupe(list, entry) {
359
+ const key = entry.el.toString();
360
+ const existing = entry.siblings.findIndex((el) => el.toString() === key);
361
+ if (existing !== -1) {
362
+ return existing;
363
+ }
364
+ list.appendChild(entry.el);
365
+ list.setAttribute('count', String(entry.siblings.length + 1));
366
+ this.touch();
367
+ return entry.siblings.length;
368
+ }
369
+ }
@@ -0,0 +1,7 @@
1
+ import type { Package } from '../opc/package.ts';
2
+ export interface CreateOptions {
3
+ sheets?: string[];
4
+ title?: string;
5
+ strict?: boolean;
6
+ }
7
+ export declare function build(pkg: Package, opts?: CreateOptions): void;
@@ -0,0 +1,54 @@
1
+ import * as xml from '../xml.js';
2
+ import { DocsError } from '../errors.js';
3
+ const { x: X, r: R } = xml.ns;
4
+ const REL_BASE = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/';
5
+ const SML = 'application/vnd.openxmlformats-officedocument.spreadsheetml.';
6
+ const CORE_REL = 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties';
7
+ function escape(value) {
8
+ return value
9
+ .replace(/&/g, '&amp;')
10
+ .replace(/</g, '&lt;')
11
+ .replace(/>/g, '&gt;')
12
+ .replace(/"/g, '&quot;');
13
+ }
14
+ function sheetNames(input) {
15
+ const names = input === undefined ? ['Sheet1'] : input;
16
+ if (!Array.isArray(names) || names.length === 0) {
17
+ throw new DocsError('INVALID_OPTIONS', 'sheets must be a non-empty array of sheet names');
18
+ }
19
+ const seen = new Set();
20
+ return names.map((name) => {
21
+ if (typeof name !== 'string' ||
22
+ name.length < 1 ||
23
+ name.length > 31 ||
24
+ /[:\\/?*[\]]/.test(name)) {
25
+ throw new DocsError('INVALID_NAME', `"${String(name)}" is not a valid sheet name`);
26
+ }
27
+ if (seen.has(name.toLowerCase())) {
28
+ throw new DocsError('INVALID_NAME', `the sheet name "${name}" is repeated`);
29
+ }
30
+ seen.add(name.toLowerCase());
31
+ return name;
32
+ });
33
+ }
34
+ function addSheets(pkg, names) {
35
+ const sheets = names
36
+ .map((name, i) => `<sheet name="${escape(name)}" sheetId="${i + 1}" r:id="rId${i + 1}"/>`)
37
+ .join('');
38
+ pkg.add('/xl/workbook.xml', xml.parse(`<workbook xmlns="${X}" xmlns:r="${R}"><workbookPr/><bookViews><workbookView activeTab="0"/></bookViews><sheets>${sheets}</sheets><calcPr calcId="191029"/></workbook>`), SML + 'sheet.main+xml');
39
+ names.forEach((_, i) => {
40
+ const part = `/xl/worksheets/sheet${i + 1}.xml`;
41
+ const selected = i === 0 ? ' tabSelected="1"' : '';
42
+ pkg.add(part, xml.parse(`<worksheet xmlns="${X}" xmlns:r="${R}"><sheetViews><sheetView workbookViewId="0"${selected}/></sheetViews><sheetFormatPr defaultRowHeight="15"/><sheetData/><pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/></worksheet>`), SML + 'worksheet+xml');
43
+ pkg.rels('/xl/workbook.xml').add(REL_BASE + 'worksheet', part);
44
+ });
45
+ }
46
+ export function build(pkg, opts = {}) {
47
+ addSheets(pkg, sheetNames(opts.sheets));
48
+ const title = typeof opts.title === 'string' && opts.title ? `<dc:title>${escape(opts.title)}</dc:title>` : '';
49
+ pkg.add('/docProps/core.xml', xml.parse(`<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/">${title}</cp:coreProperties>`), 'application/vnd.openxmlformats-package.core-properties+xml');
50
+ pkg.add('/docProps/app.xml', xml.parse('<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"><Application>QVAC</Application></Properties>'), 'application/vnd.openxmlformats-officedocument.extended-properties+xml');
51
+ pkg.rels('/').add(REL_BASE + 'officeDocument', '/xl/workbook.xml');
52
+ pkg.rels('/').add(CORE_REL, '/docProps/core.xml');
53
+ pkg.rels('/').add(REL_BASE + 'extended-properties', '/docProps/app.xml');
54
+ }