@qvac/harness 0.7.3 → 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 (106) hide show
  1. package/CHANGELOG.md +45 -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 +28 -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/package.json +25 -1
  106. package/worker/harness-sidecar.mjs +3 -1
@@ -0,0 +1,324 @@
1
+ import * as xml from '../xml.js';
2
+ import * as zip from './zip.js';
3
+ import * as paths from './paths.js';
4
+ import { ContentTypes, CONTENT_TYPES_NAME } from './content-types.js';
5
+ import { Relationships } from './relationships.js';
6
+ import { DocsError } from '../errors.js';
7
+ const RELATIONSHIPS_TYPE = 'application/vnd.openxmlformats-package.relationships+xml';
8
+ const STORED = new Set(['png', 'jpg', 'jpeg', 'gif', 'mp3', 'mp4', 'm4a', 'wav', 'zip']);
9
+ const DEFAULT_LIMITS = { maxEntries: 10000, maxSize: 512 * 1024 * 1024, maxRatio: 1000 };
10
+ function toBytes(input) {
11
+ if (input instanceof Uint8Array)
12
+ return input;
13
+ if (input instanceof ArrayBuffer) {
14
+ return new Uint8Array(input);
15
+ }
16
+ throw new DocsError('INVALID_INPUT', 'expected the file as a Uint8Array or Buffer');
17
+ }
18
+ function isDocument(data) {
19
+ return !(data instanceof Uint8Array);
20
+ }
21
+ export class Package {
22
+ static strict = false;
23
+ strict;
24
+ contentTypes;
25
+ parts;
26
+ folded;
27
+ order;
28
+ relsBySource;
29
+ removed;
30
+ edited;
31
+ strays;
32
+ constructor(entries, opts = {}) {
33
+ this.strict = opts.strict ?? Package.strict;
34
+ this.parts = new Map();
35
+ this.folded = new Map();
36
+ this.order = entries.map(([name]) => name);
37
+ this.relsBySource = new Map();
38
+ this.removed = false;
39
+ this.edited = false;
40
+ for (const [name, bytes] of entries) {
41
+ this.store(name, { bytes, doc: null, dirty: false });
42
+ }
43
+ const types = this.parts.get(CONTENT_TYPES_NAME);
44
+ if (!types?.bytes) {
45
+ throw new DocsError('NOT_OOXML', 'the file has no [Content_Types].xml, so it is not an Office document');
46
+ }
47
+ this.contentTypes = new ContentTypes(types.bytes);
48
+ this.forget(CONTENT_TYPES_NAME);
49
+ this.strays = this.unreachable();
50
+ }
51
+ static open(bytes, opts = {}) {
52
+ const limits = { ...DEFAULT_LIMITS, ...opts.limits };
53
+ const entries = zip
54
+ .read(toBytes(bytes), limits)
55
+ .map((e) => [paths.normalize(e.name), e.data]);
56
+ return new Package(entries, opts);
57
+ }
58
+ static create(opts = {}) {
59
+ const types = xml.serialize(xml.parse(`<Types xmlns="${xml.ns.ct}"/>`));
60
+ const pkg = new Package([[CONTENT_TYPES_NAME, types]], opts);
61
+ pkg.contentTypes.addDefault('rels', RELATIONSHIPS_TYPE);
62
+ pkg.contentTypes.addDefault('xml', 'application/xml');
63
+ return pkg;
64
+ }
65
+ get changed() {
66
+ return (this.edited ||
67
+ this.contentTypes.dirty ||
68
+ [...this.relsBySource.values()].some((rels) => rels.dirty));
69
+ }
70
+ names() {
71
+ return [...this.parts.keys()].filter((name) => !name.endsWith('.rels'));
72
+ }
73
+ has(name) {
74
+ return name !== null && this.find(name) !== null;
75
+ }
76
+ bytes(name) {
77
+ const part = this.require(name);
78
+ if (part.doc && part.dirty) {
79
+ return xml.serialize(part.doc);
80
+ }
81
+ if (part.bytes) {
82
+ return part.bytes;
83
+ }
84
+ if (part.doc) {
85
+ return xml.serialize(part.doc);
86
+ }
87
+ throw new DocsError('PART_MISSING', `${name} has no content`);
88
+ }
89
+ xml(name) {
90
+ const part = this.require(name);
91
+ if (part.doc) {
92
+ return part.doc;
93
+ }
94
+ const doc = xml.parse(part.bytes ?? new Uint8Array(0), name);
95
+ part.doc = doc;
96
+ if (this.strict) {
97
+ part.snapshot = xml.toString(doc);
98
+ }
99
+ return doc;
100
+ }
101
+ root(name) {
102
+ return xml.root(this.xml(name));
103
+ }
104
+ touch(name) {
105
+ this.require(name).dirty = true;
106
+ this.edited = true;
107
+ }
108
+ contentType(name) {
109
+ return this.contentTypes.get(this.find(name) ?? name);
110
+ }
111
+ add(name, data, contentType) {
112
+ if (this.find(name) !== null) {
113
+ throw new DocsError('PART_EXISTS', `${name} already exists in the package`);
114
+ }
115
+ const doc = isDocument(data);
116
+ this.store(name, { bytes: doc ? null : data, doc: doc ? data : null, dirty: doc });
117
+ this.order.push(name);
118
+ if (contentType && this.contentTypes.get(name) !== contentType) {
119
+ this.contentTypes.setOverride(name, contentType);
120
+ }
121
+ this.edited = true;
122
+ }
123
+ write(name, data) {
124
+ const part = this.require(name);
125
+ const doc = isDocument(data);
126
+ part.bytes = doc ? null : data;
127
+ part.doc = doc ? data : null;
128
+ part.dirty = doc;
129
+ delete part.snapshot;
130
+ this.edited = true;
131
+ }
132
+ remove(input) {
133
+ const name = this.find(input);
134
+ if (name === null) {
135
+ return false;
136
+ }
137
+ this.forget(name);
138
+ this.relsBySource.delete(name);
139
+ const rels = this.find(paths.relsFor(name));
140
+ if (rels !== null) {
141
+ this.forget(rels);
142
+ }
143
+ this.contentTypes.removeOverride(name);
144
+ this.removed = true;
145
+ this.edited = true;
146
+ return true;
147
+ }
148
+ rels(source) {
149
+ const cached = this.relsBySource.get(source);
150
+ if (cached) {
151
+ return cached;
152
+ }
153
+ const stored = this.find(paths.relsFor(source));
154
+ const rels = new Relationships(source, stored ? (this.parts.get(stored)?.bytes ?? null) : null);
155
+ this.relsBySource.set(source, rels);
156
+ return rels;
157
+ }
158
+ related(source, type) {
159
+ const out = [];
160
+ for (const rel of this.rels(source).byType(type)) {
161
+ const part = rel.external ? null : this.find(rel.part);
162
+ if (part !== null) {
163
+ out.push(part);
164
+ }
165
+ }
166
+ return out;
167
+ }
168
+ dropUnreferenced(source, types) {
169
+ const used = referencedIds(this.root(source));
170
+ const rels = this.rels(source);
171
+ const dropped = rels.all().filter((rel) => types.includes(rel.type) && !used.has(rel.id));
172
+ for (const rel of dropped) {
173
+ rels.remove(rel.id);
174
+ }
175
+ if (dropped.length) {
176
+ this.removed = true;
177
+ }
178
+ return dropped.map((rel) => rel.id);
179
+ }
180
+ freeName(pattern) {
181
+ let n = 1;
182
+ while (this.find(pattern.replace('%d', String(n))) !== null) {
183
+ n++;
184
+ }
185
+ return pattern.replace('%d', String(n));
186
+ }
187
+ reachable() {
188
+ const seen = new Set();
189
+ const queue = ['/'];
190
+ for (let source = queue.shift(); source !== undefined; source = queue.shift()) {
191
+ for (const rel of this.rels(source).all()) {
192
+ const part = rel.external ? null : this.resolveExisting(rel.part);
193
+ if (part && !seen.has(part)) {
194
+ seen.add(part);
195
+ queue.push(part);
196
+ }
197
+ }
198
+ }
199
+ return seen;
200
+ }
201
+ prune() {
202
+ const keep = this.reachable();
203
+ const removed = this.names().filter((name) => !keep.has(name) && !this.strays.has(name));
204
+ for (const name of removed) {
205
+ this.remove(name);
206
+ }
207
+ return removed;
208
+ }
209
+ unreachable() {
210
+ try {
211
+ const keep = this.reachable();
212
+ return new Set(this.names().filter((name) => !keep.has(name)));
213
+ }
214
+ catch {
215
+ return new Set();
216
+ }
217
+ }
218
+ save() {
219
+ if (this.strict) {
220
+ this.checkUntouched();
221
+ }
222
+ if (this.removed) {
223
+ this.prune();
224
+ this.removed = false;
225
+ }
226
+ this.edited = this.changed;
227
+ this.flushRels();
228
+ const untyped = [...this.parts.keys()].some((name) => name.endsWith('.rels') && !this.contentTypes.get(name));
229
+ if (untyped) {
230
+ this.contentTypes.addDefault('rels', RELATIONSHIPS_TYPE);
231
+ }
232
+ const names = [...new Set(this.order)].filter((name) => this.parts.has(name));
233
+ names.sort((a, b) => Number(a !== '/_rels/.rels') - Number(b !== '/_rels/.rels'));
234
+ return zip.write([
235
+ { name: '[Content_Types].xml', data: this.contentTypes.serialize(), compress: true },
236
+ ...names.map((name) => ({
237
+ name: name.slice(1),
238
+ data: this.bytes(name),
239
+ compress: !STORED.has(paths.extension(name))
240
+ }))
241
+ ]);
242
+ }
243
+ checkUntouched() {
244
+ for (const [name, part] of this.parts) {
245
+ if (part.doc &&
246
+ !part.dirty &&
247
+ part.snapshot !== undefined &&
248
+ xml.toString(part.doc) !== part.snapshot) {
249
+ throw new DocsError('UNTOUCHED_EDIT', `${name} was changed without touch(), so the edit would be lost`);
250
+ }
251
+ }
252
+ }
253
+ flushRels() {
254
+ for (const [source, rels] of this.relsBySource) {
255
+ if (!rels.dirty)
256
+ continue;
257
+ const stored = this.find(rels.name);
258
+ const existing = stored ? this.parts.get(stored) : undefined;
259
+ const bytes = rels.serialize();
260
+ if (existing) {
261
+ existing.bytes = bytes;
262
+ existing.doc = null;
263
+ existing.dirty = false;
264
+ }
265
+ else if (source === '/' || this.find(source) !== null) {
266
+ this.store(rels.name, { bytes, doc: null, dirty: false });
267
+ this.order.push(rels.name);
268
+ }
269
+ rels.dirty = false;
270
+ }
271
+ }
272
+ require(name) {
273
+ const stored = this.find(name);
274
+ const part = stored === null ? undefined : this.parts.get(stored);
275
+ if (!part) {
276
+ throw new DocsError('PART_MISSING', `${name} is not in the package`);
277
+ }
278
+ return part;
279
+ }
280
+ resolveExisting(name) {
281
+ if (name === null)
282
+ return null;
283
+ const found = this.find(name);
284
+ if (found !== null) {
285
+ return found;
286
+ }
287
+ try {
288
+ return this.find(decodeURI(name));
289
+ }
290
+ catch {
291
+ return null;
292
+ }
293
+ }
294
+ find(name) {
295
+ if (name === null)
296
+ return null;
297
+ if (this.parts.has(name))
298
+ return name;
299
+ return this.folded.get(name.toLowerCase()) ?? null;
300
+ }
301
+ store(name, part) {
302
+ this.parts.set(name, part);
303
+ this.folded.set(name.toLowerCase(), name);
304
+ }
305
+ forget(name) {
306
+ this.parts.delete(name);
307
+ if (this.folded.get(name.toLowerCase()) === name) {
308
+ this.folded.delete(name.toLowerCase());
309
+ }
310
+ }
311
+ }
312
+ export function referencedIds(top) {
313
+ const used = new Set();
314
+ const stack = [top];
315
+ for (let el = stack.pop(); el; el = stack.pop()) {
316
+ for (const attr of Array.from(el.attributes)) {
317
+ if (attr.namespaceURI === xml.ns.r) {
318
+ used.add(attr.value);
319
+ }
320
+ }
321
+ stack.push(...xml.children(el));
322
+ }
323
+ return used;
324
+ }
@@ -0,0 +1,7 @@
1
+ export declare function dirname(partName: string): string;
2
+ export declare function basename(partName: string): string;
3
+ export declare function extension(partName: string): string;
4
+ export declare function normalize(path: string): string;
5
+ export declare function relsFor(source: string): string;
6
+ export declare function resolve(source: string, target: string): string;
7
+ export declare function relative(source: string, target: string): string;
@@ -0,0 +1,46 @@
1
+ export function dirname(partName) {
2
+ return partName.slice(0, partName.lastIndexOf('/') + 1);
3
+ }
4
+ export function basename(partName) {
5
+ return partName.slice(partName.lastIndexOf('/') + 1);
6
+ }
7
+ export function extension(partName) {
8
+ const base = basename(partName);
9
+ const dot = base.lastIndexOf('.');
10
+ return dot === -1 ? '' : base.slice(dot + 1).toLowerCase();
11
+ }
12
+ export function normalize(path) {
13
+ const out = [];
14
+ for (const segment of path.split('/')) {
15
+ if (segment === '' || segment === '.')
16
+ continue;
17
+ if (segment === '..') {
18
+ out.pop();
19
+ }
20
+ else {
21
+ out.push(segment);
22
+ }
23
+ }
24
+ return '/' + out.join('/');
25
+ }
26
+ export function relsFor(source) {
27
+ if (source === '/')
28
+ return '/_rels/.rels';
29
+ return `${dirname(source)}_rels/${basename(source)}.rels`;
30
+ }
31
+ export function resolve(source, target) {
32
+ const clean = target.split('#')[0];
33
+ if (clean.startsWith('/')) {
34
+ return normalize(clean);
35
+ }
36
+ return normalize(dirname(source) + clean);
37
+ }
38
+ export function relative(source, target) {
39
+ const from = dirname(source).split('/').filter(Boolean);
40
+ const to = target.split('/').filter(Boolean);
41
+ let common = 0;
42
+ while (common < from.length && common < to.length - 1 && from[common] === to[common]) {
43
+ common++;
44
+ }
45
+ return [...from.slice(common).map(() => '..'), ...to.slice(common)].join('/');
46
+ }
@@ -0,0 +1,7 @@
1
+ import type { Package } from './package.ts';
2
+ export interface SaveOptions {
3
+ modified?: Date;
4
+ modifiedBy?: string;
5
+ }
6
+ export declare function applySaveOptions(pkg: Package, opts?: SaveOptions): void;
7
+ export declare function refreshSummary(pkg: Package, counts?: Record<string, number>): void;
@@ -0,0 +1,75 @@
1
+ import * as xml from '../xml.js';
2
+ import { DocsError } from '../errors.js';
3
+ const CORE_REL = 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties';
4
+ const APP_REL = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties';
5
+ const CORE_TYPE = 'application/vnd.openxmlformats-package.core-properties+xml';
6
+ const CP = 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties';
7
+ const DC = 'http://purl.org/dc/elements/1.1/';
8
+ const DCTERMS = 'http://purl.org/dc/terms/';
9
+ const XSI = 'http://www.w3.org/2001/XMLSchema-instance';
10
+ const EP = 'http://schemas.openxmlformats.org/officeDocument/2006/extended-properties';
11
+ function setField(doc, field, value) {
12
+ const top = xml.root(doc);
13
+ const local = field.name.split(':')[1];
14
+ const el = xml.child(top, field.namespace, local) ??
15
+ xml.append(top, xml.named(doc, field.name, field.namespace));
16
+ xml.setText(el, value);
17
+ return el;
18
+ }
19
+ function corePart(pkg) {
20
+ const existing = pkg.related('/', CORE_REL)[0];
21
+ if (existing) {
22
+ return existing;
23
+ }
24
+ const part = '/docProps/core.xml';
25
+ pkg.add(part, xml.parse(`<cp:coreProperties xmlns:cp="${CP}" xmlns:dc="${DC}"/>`), CORE_TYPE);
26
+ pkg.rels('/').add(CORE_REL, part);
27
+ return part;
28
+ }
29
+ export function applySaveOptions(pkg, opts = {}) {
30
+ const { modified, modifiedBy } = opts;
31
+ if (modified === undefined && modifiedBy === undefined) {
32
+ return;
33
+ }
34
+ if (modified !== undefined && !(modified instanceof Date && !Number.isNaN(modified.getTime()))) {
35
+ throw new DocsError('INVALID_OPTIONS', 'modified must be a valid Date');
36
+ }
37
+ if (modifiedBy !== undefined && typeof modifiedBy !== 'string') {
38
+ throw new DocsError('INVALID_OPTIONS', 'modifiedBy must be text');
39
+ }
40
+ const part = corePart(pkg);
41
+ const doc = pkg.xml(part);
42
+ if (modifiedBy !== undefined) {
43
+ setField(doc, { namespace: CP, name: 'cp:lastModifiedBy' }, modifiedBy);
44
+ }
45
+ if (modified !== undefined) {
46
+ const stamp = modified.toISOString().replace(/\.\d{3}Z$/, 'Z');
47
+ const el = setField(doc, { namespace: DCTERMS, name: 'dcterms:modified' }, stamp);
48
+ el.setAttributeNS(XSI, 'xsi:type', 'dcterms:W3CDTF');
49
+ }
50
+ pkg.touch(part);
51
+ }
52
+ export function refreshSummary(pkg, counts = {}) {
53
+ const part = pkg.related('/', APP_REL)[0];
54
+ if (!part) {
55
+ return;
56
+ }
57
+ const top = pkg.root(part);
58
+ let changed = false;
59
+ for (const local of ['HeadingPairs', 'TitlesOfParts']) {
60
+ for (const el of xml.children(top, EP, local)) {
61
+ xml.remove(el);
62
+ changed = true;
63
+ }
64
+ }
65
+ for (const [local, value] of Object.entries(counts)) {
66
+ const el = xml.child(top, EP, local);
67
+ if (el && xml.text(el) !== String(value)) {
68
+ xml.setText(el, String(value));
69
+ changed = true;
70
+ }
71
+ }
72
+ if (changed) {
73
+ pkg.touch(part);
74
+ }
75
+ }
@@ -0,0 +1,23 @@
1
+ import type { Document, Element } from '../xml.ts';
2
+ export interface Relationship {
3
+ id: string;
4
+ type: string;
5
+ target: string;
6
+ external: boolean;
7
+ part: string | null;
8
+ }
9
+ export declare class Relationships {
10
+ source: string;
11
+ name: string;
12
+ doc: Document;
13
+ dirty: boolean;
14
+ constructor(source: string, bytes: Uint8Array | null);
15
+ get root(): Element;
16
+ all(): Relationship[];
17
+ get(id: string | null): Relationship | null;
18
+ byType(type: string): Relationship[];
19
+ targeting(partName: string): Relationship[];
20
+ add(type: string, partName: string): string;
21
+ remove(id: string): boolean;
22
+ serialize(): Uint8Array;
23
+ }
@@ -0,0 +1,65 @@
1
+ import * as xml from '../xml.js';
2
+ import * as paths from './paths.js';
3
+ export class Relationships {
4
+ source;
5
+ name;
6
+ doc;
7
+ dirty;
8
+ constructor(source, bytes) {
9
+ this.source = source;
10
+ this.name = paths.relsFor(source);
11
+ this.doc = xml.parse(bytes ?? `<Relationships xmlns="${xml.ns.rels}"/>`, this.name);
12
+ this.dirty = false;
13
+ }
14
+ get root() {
15
+ return xml.root(this.doc);
16
+ }
17
+ all() {
18
+ return xml.children(this.root, xml.ns.rels, 'Relationship').map((el) => {
19
+ const external = el.getAttribute('TargetMode') === 'External';
20
+ const target = xml.attr(el, 'Target');
21
+ return {
22
+ id: xml.attr(el, 'Id'),
23
+ type: xml.attr(el, 'Type'),
24
+ target,
25
+ external,
26
+ part: external || !target ? null : paths.resolve(this.source, target)
27
+ };
28
+ });
29
+ }
30
+ get(id) {
31
+ return this.all().find((rel) => rel.id === id) ?? null;
32
+ }
33
+ byType(type) {
34
+ return this.all().filter((rel) => rel.type === type);
35
+ }
36
+ targeting(partName) {
37
+ return this.all().filter((rel) => rel.part === partName);
38
+ }
39
+ add(type, partName) {
40
+ const taken = new Set(this.all().map((rel) => rel.id));
41
+ let n = taken.size + 1;
42
+ while (taken.has(`rId${n}`)) {
43
+ n++;
44
+ }
45
+ const id = `rId${n}`;
46
+ const el = xml.named(this.doc, 'Relationship', xml.ns.rels);
47
+ this.root.appendChild(xml.setAttributes(el, { Id: id, Type: type, Target: paths.relative(this.source, partName) }));
48
+ this.dirty = true;
49
+ return id;
50
+ }
51
+ remove(id) {
52
+ const el = xml
53
+ .children(this.root, xml.ns.rels, 'Relationship')
54
+ .find((r) => r.getAttribute('Id') === id);
55
+ if (!el) {
56
+ return false;
57
+ }
58
+ xml.remove(el);
59
+ this.dirty = true;
60
+ return true;
61
+ }
62
+ serialize() {
63
+ return xml.serialize(this.doc);
64
+ }
65
+ }
@@ -0,0 +1,15 @@
1
+ export interface Limits {
2
+ maxEntries: number;
3
+ maxSize: number;
4
+ maxRatio: number;
5
+ }
6
+ export interface ZipEntry {
7
+ name: string;
8
+ data: Uint8Array;
9
+ }
10
+ export interface ZipInput extends ZipEntry {
11
+ compress: boolean;
12
+ }
13
+ export declare function crc32(data: Uint8Array): number;
14
+ export declare function read(bytes: Uint8Array, limits: Limits): ZipEntry[];
15
+ export declare function write(entries: ZipInput[]): Uint8Array;