@absolutejs/artifacts 0.2.0 → 0.3.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.
package/dist/index.js CHANGED
@@ -58,35 +58,635 @@ var defineArtifactRegistry = (definitions) => ({
58
58
  // src/generators.ts
59
59
  var createArtifactGeneratorRegistry = (initial = []) => {
60
60
  const generators = new Map(initial.map((generator) => [generator.kind, generator]));
61
+ const generateResult = async (input) => {
62
+ const generator = generators.get(input.kind);
63
+ if (!generator) {
64
+ throw new ArtifactError("generator_unavailable", `No generator is registered for ${input.kind} artifacts`);
65
+ }
66
+ const context = { ownerId: input.ownerId };
67
+ const result = await generator.generate(input, context);
68
+ const issues = await generator.validate?.(result, input, context) ?? [];
69
+ if (issues.length > 0) {
70
+ throw new ArtifactError("batch_validation_failed", issues.map((issue) => `${issue.code}: ${issue.message}`).join("; "));
71
+ }
72
+ return result;
73
+ };
74
+ const bundleInput = (input, result) => ({
75
+ assets: result.assets,
76
+ content: result.content,
77
+ createdBy: input.createdBy,
78
+ kind: input.kind,
79
+ metadata: {
80
+ ...result.metadata,
81
+ ...result.warnings?.length ? { generationWarnings: result.warnings } : {}
82
+ },
83
+ provenance: result.provenance,
84
+ title: result.title ?? input.title ?? `Generated ${input.kind}`
85
+ });
61
86
  return {
62
87
  generate: async (service, input) => {
63
- const generator = generators.get(input.kind);
64
- if (!generator) {
65
- throw new ArtifactError("generator_unavailable", `No generator is registered for ${input.kind} artifacts`);
66
- }
67
- const result = await generator.generate(input, {
68
- ownerId: input.ownerId
69
- });
70
- const artifact = await service.createBundle(input.ownerId, {
71
- assets: result.assets,
72
- content: result.content,
73
- createdBy: input.createdBy,
74
- kind: input.kind,
75
- metadata: {
76
- ...result.metadata,
77
- ...result.warnings?.length ? { generationWarnings: result.warnings } : {}
78
- },
79
- provenance: result.provenance,
80
- title: result.title ?? input.title ?? `Generated ${input.kind}`
81
- });
88
+ const result = await generateResult(input);
89
+ const artifact = await service.createBundle(input.ownerId, bundleInput(input, result));
82
90
  return artifact;
83
91
  },
92
+ generateBatch: async (service, input) => {
93
+ const generated = await Promise.all(input.items.map(async (item) => {
94
+ const generationInput = {
95
+ createdBy: item.createdBy,
96
+ input: item.input,
97
+ kind: item.kind,
98
+ ownerId: input.ownerId,
99
+ prompt: item.prompt,
100
+ title: item.title
101
+ };
102
+ const generator = generators.get(item.kind);
103
+ if (!generator) {
104
+ throw new ArtifactError("generator_unavailable", `No generator is registered for ${item.kind} artifacts`);
105
+ }
106
+ const context = { ownerId: input.ownerId };
107
+ const result = await generator.generate(generationInput, context);
108
+ const issues = await generator.validate?.(result, generationInput, context) ?? [];
109
+ return {
110
+ artifact: bundleInput(generationInput, result),
111
+ evidence: item.evidence,
112
+ generationIssues: issues,
113
+ key: item.key
114
+ };
115
+ }));
116
+ const staged = await service.stageBatch(input.ownerId, {
117
+ bundleId: input.bundleId,
118
+ commitMode: input.commitMode,
119
+ evidence: input.evidence,
120
+ items: generated.map(({ generationIssues: _issues, ...item }) => item),
121
+ metadata: input.metadata,
122
+ provenance: input.provenance
123
+ }, {
124
+ validators: [
125
+ () => generated.flatMap((item) => item.generationIssues.map((generationIssue) => ({
126
+ ...generationIssue,
127
+ itemKey: item.key
128
+ }))),
129
+ ...input.validators ?? []
130
+ ]
131
+ });
132
+ return staged.commit();
133
+ },
84
134
  kinds: () => [...generators.keys()],
85
135
  register: (generator) => {
86
136
  generators.set(generator.kind, generator);
87
137
  }
88
138
  };
89
139
  };
140
+ // node_modules/fflate/esm/index.mjs
141
+ import { createRequire } from "module";
142
+ var require2 = createRequire("/");
143
+ var _a;
144
+ var Worker;
145
+ var isMarkedAsUntransferable;
146
+ try {
147
+ _a = require2("worker_threads"), Worker = _a.Worker, isMarkedAsUntransferable = _a.isMarkedAsUntransferable;
148
+ } catch (e) {}
149
+ var u8 = Uint8Array;
150
+ var u16 = Uint16Array;
151
+ var i32 = Int32Array;
152
+ var fleb = new u8([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0, 0]);
153
+ var fdeb = new u8([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0]);
154
+ var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
155
+ var freb = function(eb, start) {
156
+ var b = new u16(31);
157
+ for (var i = 0;i < 31; ++i) {
158
+ b[i] = start += 1 << eb[i - 1];
159
+ }
160
+ var r = new i32(b[30]);
161
+ for (var i = 1;i < 30; ++i) {
162
+ for (var j = b[i];j < b[i + 1]; ++j) {
163
+ r[j] = j - b[i] << 5 | i;
164
+ }
165
+ }
166
+ return { b, r };
167
+ };
168
+ var _a = freb(fleb, 2);
169
+ var fl = _a.b;
170
+ var revfl = _a.r;
171
+ fl[28] = 258, revfl[258] = 28;
172
+ var _b = freb(fdeb, 0);
173
+ var fd = _b.b;
174
+ var revfd = _b.r;
175
+ var rev = new u16(32768);
176
+ for (i = 0;i < 32768; ++i) {
177
+ x = (i & 43690) >> 1 | (i & 21845) << 1;
178
+ x = (x & 52428) >> 2 | (x & 13107) << 2;
179
+ x = (x & 61680) >> 4 | (x & 3855) << 4;
180
+ rev[i] = ((x & 65280) >> 8 | (x & 255) << 8) >> 1;
181
+ }
182
+ var x;
183
+ var i;
184
+ var hMap = function(cd, mb, r) {
185
+ var s = cd.length;
186
+ var i2 = 0;
187
+ var l = new u16(mb);
188
+ for (;i2 < s; ++i2) {
189
+ if (cd[i2])
190
+ ++l[cd[i2] - 1];
191
+ }
192
+ var le = new u16(mb);
193
+ for (i2 = 1;i2 < mb; ++i2) {
194
+ le[i2] = le[i2 - 1] + l[i2 - 1] << 1;
195
+ }
196
+ var co;
197
+ if (r) {
198
+ co = new u16(1 << mb);
199
+ var rvb = 15 - mb;
200
+ for (i2 = 0;i2 < s; ++i2) {
201
+ if (cd[i2]) {
202
+ var sv = i2 << 4 | cd[i2];
203
+ var r_1 = mb - cd[i2];
204
+ var v = le[cd[i2] - 1]++ << r_1;
205
+ for (var m = v | (1 << r_1) - 1;v <= m; ++v) {
206
+ co[rev[v] >> rvb] = sv;
207
+ }
208
+ }
209
+ }
210
+ } else {
211
+ co = new u16(s);
212
+ for (i2 = 0;i2 < s; ++i2) {
213
+ if (cd[i2]) {
214
+ co[i2] = rev[le[cd[i2] - 1]++] >> 15 - cd[i2];
215
+ }
216
+ }
217
+ }
218
+ return co;
219
+ };
220
+ var flt = new u8(288);
221
+ for (i = 0;i < 144; ++i)
222
+ flt[i] = 8;
223
+ var i;
224
+ for (i = 144;i < 256; ++i)
225
+ flt[i] = 9;
226
+ var i;
227
+ for (i = 256;i < 280; ++i)
228
+ flt[i] = 7;
229
+ var i;
230
+ for (i = 280;i < 288; ++i)
231
+ flt[i] = 8;
232
+ var i;
233
+ var fdt = new u8(32);
234
+ for (i = 0;i < 32; ++i)
235
+ fdt[i] = 5;
236
+ var i;
237
+ var flrm = /* @__PURE__ */ hMap(flt, 9, 1);
238
+ var fdrm = /* @__PURE__ */ hMap(fdt, 5, 1);
239
+ var max = function(a) {
240
+ var m = a[0];
241
+ for (var i2 = 1;i2 < a.length; ++i2) {
242
+ if (a[i2] > m)
243
+ m = a[i2];
244
+ }
245
+ return m;
246
+ };
247
+ var bits = function(d, p, m) {
248
+ var o = p / 8 | 0;
249
+ return (d[o] | d[o + 1] << 8) >> (p & 7) & m;
250
+ };
251
+ var bits16 = function(d, p) {
252
+ var o = p / 8 | 0;
253
+ return (d[o] | d[o + 1] << 8 | d[o + 2] << 16) >> (p & 7);
254
+ };
255
+ var shft = function(p) {
256
+ return (p + 7) / 8 | 0;
257
+ };
258
+ var slc = function(v, s, e) {
259
+ if (s == null || s < 0)
260
+ s = 0;
261
+ if (e == null || e > v.length)
262
+ e = v.length;
263
+ return new u8(v.subarray(s, e));
264
+ };
265
+ var ec = [
266
+ "unexpected EOF",
267
+ "invalid block type",
268
+ "invalid length/literal",
269
+ "invalid distance",
270
+ "stream finished",
271
+ "no stream handler",
272
+ ,
273
+ "no callback",
274
+ "invalid UTF-8 data",
275
+ "extra field too long",
276
+ "date not in range 1980-2099",
277
+ "filename too long",
278
+ "stream finishing",
279
+ "invalid zip data"
280
+ ];
281
+ var err = function(ind, msg, nt) {
282
+ var e = new Error(msg || ec[ind]);
283
+ e.code = ind;
284
+ if (Error.captureStackTrace)
285
+ Error.captureStackTrace(e, err);
286
+ if (!nt)
287
+ throw e;
288
+ return e;
289
+ };
290
+ var inflt = function(dat, st, buf, dict) {
291
+ var sl = dat.length, dl = dict ? dict.length : 0;
292
+ if (!sl || st.f && !st.l)
293
+ return buf || new u8(0);
294
+ var noBuf = !buf;
295
+ var resize = noBuf || st.i != 2;
296
+ var noSt = st.i;
297
+ if (noBuf)
298
+ buf = new u8(sl * 3);
299
+ var cbuf = function(l2) {
300
+ var bl = buf.length;
301
+ if (l2 > bl) {
302
+ var nbuf = new u8(Math.max(bl * 2, l2));
303
+ nbuf.set(buf);
304
+ buf = nbuf;
305
+ }
306
+ };
307
+ var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, lm = st.l, dm = st.d, lbt = st.m, dbt = st.n;
308
+ var tbts = sl * 8;
309
+ do {
310
+ if (!lm) {
311
+ final = bits(dat, pos, 1);
312
+ var type = bits(dat, pos + 1, 3);
313
+ pos += 3;
314
+ if (!type) {
315
+ var s = shft(pos) + 4, l = dat[s - 4] | dat[s - 3] << 8, t = s + l;
316
+ if (t > sl) {
317
+ if (noSt)
318
+ err(0);
319
+ break;
320
+ }
321
+ if (resize)
322
+ cbuf(bt + l);
323
+ buf.set(dat.subarray(s, t), bt);
324
+ st.b = bt += l, st.p = pos = t * 8, st.f = final;
325
+ continue;
326
+ } else if (type == 1)
327
+ lm = flrm, dm = fdrm, lbt = 9, dbt = 5;
328
+ else if (type == 2) {
329
+ var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4;
330
+ var tl = hLit + bits(dat, pos + 5, 31) + 1;
331
+ pos += 14;
332
+ var ldt = new u8(tl);
333
+ var clt = new u8(19);
334
+ for (var i2 = 0;i2 < hcLen; ++i2) {
335
+ clt[clim[i2]] = bits(dat, pos + i2 * 3, 7);
336
+ }
337
+ pos += hcLen * 3;
338
+ var clb = max(clt), clbmsk = (1 << clb) - 1;
339
+ var clm = hMap(clt, clb, 1);
340
+ for (var i2 = 0;i2 < tl; ) {
341
+ var r = clm[bits(dat, pos, clbmsk)];
342
+ pos += r & 15;
343
+ var s = r >> 4;
344
+ if (s < 16) {
345
+ ldt[i2++] = s;
346
+ } else {
347
+ var c = 0, n = 0;
348
+ if (s == 16)
349
+ n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i2 - 1];
350
+ else if (s == 17)
351
+ n = 3 + bits(dat, pos, 7), pos += 3;
352
+ else if (s == 18)
353
+ n = 11 + bits(dat, pos, 127), pos += 7;
354
+ while (n--)
355
+ ldt[i2++] = c;
356
+ }
357
+ }
358
+ var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);
359
+ lbt = max(lt);
360
+ dbt = max(dt);
361
+ lm = hMap(lt, lbt, 1);
362
+ dm = hMap(dt, dbt, 1);
363
+ } else
364
+ err(1);
365
+ if (pos > tbts) {
366
+ if (noSt)
367
+ err(0);
368
+ break;
369
+ }
370
+ }
371
+ if (resize)
372
+ cbuf(bt + 131072);
373
+ var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;
374
+ var lpos = pos;
375
+ for (;; lpos = pos) {
376
+ var c = lm[bits16(dat, pos) & lms], sym = c >> 4;
377
+ pos += c & 15;
378
+ if (pos > tbts) {
379
+ if (noSt)
380
+ err(0);
381
+ break;
382
+ }
383
+ if (!c)
384
+ err(2);
385
+ if (sym < 256)
386
+ buf[bt++] = sym;
387
+ else if (sym == 256) {
388
+ lpos = pos, lm = null;
389
+ break;
390
+ } else {
391
+ var add = sym - 254;
392
+ if (sym > 264) {
393
+ var i2 = sym - 257, b = fleb[i2];
394
+ add = bits(dat, pos, (1 << b) - 1) + fl[i2];
395
+ pos += b;
396
+ }
397
+ var d = dm[bits16(dat, pos) & dms], dsym = d >> 4;
398
+ if (!d)
399
+ err(3);
400
+ pos += d & 15;
401
+ var dt = fd[dsym];
402
+ if (dsym > 3) {
403
+ var b = fdeb[dsym];
404
+ dt += bits16(dat, pos) & (1 << b) - 1, pos += b;
405
+ }
406
+ if (pos > tbts) {
407
+ if (noSt)
408
+ err(0);
409
+ break;
410
+ }
411
+ if (resize)
412
+ cbuf(bt + 131072);
413
+ var end = bt + add;
414
+ if (bt < dt) {
415
+ var shift = dl - dt, dend = Math.min(dt, end);
416
+ if (shift + bt < 0)
417
+ err(3);
418
+ for (;bt < dend; ++bt)
419
+ buf[bt] = dict[shift + bt];
420
+ }
421
+ for (;bt < end; ++bt)
422
+ buf[bt] = buf[bt - dt];
423
+ }
424
+ }
425
+ st.l = lm, st.p = lpos, st.b = bt, st.f = final;
426
+ if (lm)
427
+ final = 1, st.m = lbt, st.d = dm, st.n = dbt;
428
+ } while (!final);
429
+ return bt != buf.length && noBuf ? slc(buf, 0, bt) : buf.subarray(0, bt);
430
+ };
431
+ var et = /* @__PURE__ */ new u8(0);
432
+ var b2 = function(d, b) {
433
+ return d[b] | d[b + 1] << 8;
434
+ };
435
+ var b4 = function(d, b) {
436
+ return (d[b] | d[b + 1] << 8 | d[b + 2] << 16 | d[b + 3] << 24) >>> 0;
437
+ };
438
+ var b8 = function(d, b) {
439
+ return b4(d, b) + b4(d, b + 4) * 4294967296;
440
+ };
441
+ function inflateSync(data, opts) {
442
+ return inflt(data, { i: 2 }, opts && opts.out, opts && opts.dictionary);
443
+ }
444
+ var td = typeof TextDecoder != "undefined" && /* @__PURE__ */ new TextDecoder;
445
+ var tds = 0;
446
+ try {
447
+ td.decode(et, { stream: true });
448
+ tds = 1;
449
+ } catch (e) {}
450
+ var dutf8 = function(d) {
451
+ for (var r = "", i2 = 0;; ) {
452
+ var c = d[i2++];
453
+ var eb = (c > 127) + (c > 223) + (c > 239);
454
+ if (i2 + eb > d.length)
455
+ return { s: r, r: slc(d, i2 - 1) };
456
+ if (!eb)
457
+ r += String.fromCharCode(c);
458
+ else if (eb == 3) {
459
+ c = ((c & 15) << 18 | (d[i2++] & 63) << 12 | (d[i2++] & 63) << 6 | d[i2++] & 63) - 65536, r += String.fromCharCode(55296 | c >> 10, 56320 | c & 1023);
460
+ } else if (eb & 1)
461
+ r += String.fromCharCode((c & 31) << 6 | d[i2++] & 63);
462
+ else
463
+ r += String.fromCharCode((c & 15) << 12 | (d[i2++] & 63) << 6 | d[i2++] & 63);
464
+ }
465
+ };
466
+ function strFromU8(dat, latin1) {
467
+ if (latin1) {
468
+ var r = "";
469
+ for (var i2 = 0;i2 < dat.length; i2 += 16384)
470
+ r += String.fromCharCode.apply(null, dat.subarray(i2, i2 + 16384));
471
+ return r;
472
+ } else if (td) {
473
+ return td.decode(dat);
474
+ } else {
475
+ var _a2 = dutf8(dat), s = _a2.s, r = _a2.r;
476
+ if (r.length)
477
+ err(8);
478
+ return s;
479
+ }
480
+ }
481
+ var slzh = function(d, b) {
482
+ return b + 30 + b2(d, b + 26) + b2(d, b + 28);
483
+ };
484
+ var zh = function(d, b, z) {
485
+ var fnl = b2(d, b + 28), efl = b2(d, b + 30), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl;
486
+ var _a2 = z64hs(d, es, efl, z, b4(d, b + 20), b4(d, b + 24), b4(d, b + 42)), sc = _a2[0], su = _a2[1], off = _a2[2];
487
+ return [b2(d, b + 10), sc, su, fn, es + efl + b2(d, b + 32), off];
488
+ };
489
+ var z64hs = function(d, b, l, z, sc, su, off) {
490
+ var nsc = sc == 4294967295, nsu = su == 4294967295, noff = off == 4294967295, e = b + l;
491
+ var nf = nsc + nsu + noff;
492
+ if (z && nf) {
493
+ for (;b + 4 < e; b += 4 + b2(d, b + 2)) {
494
+ if (b2(d, b) == 1) {
495
+ return [
496
+ nsc ? b8(d, b + 4 + 8 * nsu) : sc,
497
+ nsu ? b8(d, b + 4) : su,
498
+ noff ? b8(d, b + 4 + 8 * (nsu + nsc)) : off,
499
+ 1
500
+ ];
501
+ }
502
+ }
503
+ if (z < 2)
504
+ err(13);
505
+ }
506
+ return [sc, su, off, 0];
507
+ };
508
+ function unzipSync(data, opts) {
509
+ var files = {};
510
+ var e = data.length - 22;
511
+ for (;b4(data, e) != 101010256; --e) {
512
+ if (!e || data.length - e > 65558)
513
+ err(13);
514
+ }
515
+ var c = b2(data, e + 8);
516
+ if (!c)
517
+ return {};
518
+ var o = b4(data, e + 16);
519
+ var z = b4(data, e - 20) == 117853008;
520
+ if (z) {
521
+ var ze = b4(data, e - 12);
522
+ z = b4(data, ze) == 101075792;
523
+ if (z) {
524
+ c = b4(data, ze + 32);
525
+ o = b4(data, ze + 48);
526
+ }
527
+ }
528
+ var fltr = opts && opts.filter;
529
+ for (var i2 = 0;i2 < c; ++i2) {
530
+ var _a2 = zh(data, o, z), c_2 = _a2[0], sc = _a2[1], su = _a2[2], fn = _a2[3], no = _a2[4], off = _a2[5], b = slzh(data, off);
531
+ o = no;
532
+ if (!fltr || fltr({
533
+ name: fn,
534
+ size: sc,
535
+ originalSize: su,
536
+ compression: c_2
537
+ })) {
538
+ if (!c_2)
539
+ files[fn] = slc(data, b, b + sc);
540
+ else if (c_2 == 8)
541
+ files[fn] = inflateSync(data.subarray(b, b + sc), { out: new u8(su) });
542
+ else
543
+ err(14, "unknown compression type " + c_2);
544
+ }
545
+ }
546
+ return files;
547
+ }
548
+
549
+ // src/validation.ts
550
+ var decode = (data) => new TextDecoder().decode(data);
551
+ var issue = (code, message, path) => ({
552
+ code,
553
+ message,
554
+ ...path ? { path } : {}
555
+ });
556
+ var parseCsvRows = (value) => {
557
+ const rows = [];
558
+ let row = [];
559
+ let field = "";
560
+ let quoted = false;
561
+ for (let index = 0;index < value.length; index += 1) {
562
+ const character = value[index];
563
+ if (character === '"') {
564
+ if (quoted && value[index + 1] === '"') {
565
+ field += '"';
566
+ index += 1;
567
+ } else {
568
+ quoted = !quoted;
569
+ }
570
+ } else if (character === "," && !quoted) {
571
+ row.push(field);
572
+ field = "";
573
+ } else if ((character === `
574
+ ` || character === "\r") && !quoted) {
575
+ if (character === "\r" && value[index + 1] === `
576
+ `)
577
+ index += 1;
578
+ row.push(field);
579
+ rows.push(row);
580
+ row = [];
581
+ field = "";
582
+ } else {
583
+ field += character;
584
+ }
585
+ }
586
+ if (quoted)
587
+ throw new Error("CSV contains an unterminated quoted field");
588
+ if (field.length > 0 || row.length > 0) {
589
+ row.push(field);
590
+ rows.push(row);
591
+ }
592
+ return rows.filter((candidate) => candidate.some((cell) => cell.length > 0));
593
+ };
594
+ var validateCsv = (data, path) => {
595
+ const rows = parseCsvRows(decode(data));
596
+ if (rows.length === 0)
597
+ return [issue("csv_empty", "CSV has no rows", path)];
598
+ const columns = rows[0].length;
599
+ const inconsistent = rows.findIndex((row) => row.length !== columns);
600
+ return inconsistent < 0 ? [] : [
601
+ issue("csv_column_mismatch", `CSV row ${inconsistent + 1} has ${rows[inconsistent].length} columns; expected ${columns}`, path)
602
+ ];
603
+ };
604
+ var validateEmail = (data, path) => {
605
+ const value = decode(data).replace(/\r\n?/g, `
606
+ `);
607
+ const separator = value.indexOf(`
608
+
609
+ `);
610
+ if (separator < 0) {
611
+ return [
612
+ issue("email_missing_body_separator", "Email must contain headers followed by a blank line and body", path)
613
+ ];
614
+ }
615
+ const headers = value.slice(0, separator);
616
+ const required = ["to", "subject"].filter((name) => !new RegExp(`^${name}:\\s*\\S+`, "imu").test(headers));
617
+ return required.map((name) => issue("email_missing_header", `Email is missing a non-empty ${name} header`, path));
618
+ };
619
+ var validateXml = (value) => {
620
+ const stack = [];
621
+ const withoutOpaqueSections = value.replace(/<!--[\s\S]*?-->/gu, "").replace(/<!\[CDATA\[[\s\S]*?\]\]>/gu, "").replace(/<\?[\s\S]*?\?>/gu, "");
622
+ for (const match of withoutOpaqueSections.matchAll(/<\s*(\/?)\s*([\w:.-]+)([^>]*)>/gu)) {
623
+ const closing = match[1] === "/";
624
+ const name = match[2];
625
+ const suffix = match[3] ?? "";
626
+ if (!closing && suffix.trimEnd().endsWith("/"))
627
+ continue;
628
+ if (!closing) {
629
+ stack.push(name);
630
+ continue;
631
+ }
632
+ const expected = stack.pop();
633
+ if (expected !== name) {
634
+ throw new Error(expected ? `expected closing tag for <${expected}> but found </${name}>` : `unexpected closing tag </${name}>`);
635
+ }
636
+ }
637
+ if (stack.length > 0)
638
+ throw new Error(`unclosed tag <${stack.at(-1)}>`);
639
+ };
640
+ var validateZip = (data, path, officePresentation) => {
641
+ let entries;
642
+ try {
643
+ entries = unzipSync(data);
644
+ } catch (error) {
645
+ return [
646
+ issue("zip_invalid", `ZIP archive could not be opened: ${error instanceof Error ? error.message : String(error)}`, path)
647
+ ];
648
+ }
649
+ const names = Object.keys(entries).filter((name) => !name.endsWith("/"));
650
+ if (names.length === 0) {
651
+ return [issue("zip_empty", "ZIP archive has no files", path)];
652
+ }
653
+ if (!officePresentation)
654
+ return [];
655
+ const required = ["[Content_Types].xml", "ppt/presentation.xml"];
656
+ const missing = required.filter((name) => !entries[name]);
657
+ if (!names.some((name) => /^ppt\/slides\/slide\d+\.xml$/u.test(name))) {
658
+ missing.push("ppt/slides/slide*.xml");
659
+ }
660
+ const issues = missing.map((name) => issue("presentation_entry_missing", `Presentation is missing ${name}`, path));
661
+ for (const name of names.filter((candidate) => candidate.endsWith(".xml") || candidate.endsWith(".rels"))) {
662
+ try {
663
+ validateXml(decode(entries[name]));
664
+ } catch (error) {
665
+ issues.push(issue("presentation_xml_invalid", `${name}: ${error instanceof Error ? error.message : String(error)}`, path));
666
+ }
667
+ }
668
+ return issues;
669
+ };
670
+ var validateGeneratedArtifactFormats = (result) => (result.assets ?? []).flatMap((asset, index) => {
671
+ const mediaType = asset.mediaType.toLowerCase();
672
+ const path = `assets[${index}](${asset.name})`;
673
+ try {
674
+ if (mediaType.includes("csv"))
675
+ return validateCsv(asset.data, path);
676
+ if (mediaType === "message/rfc822")
677
+ return validateEmail(asset.data, path);
678
+ if (mediaType.includes("presentationml.presentation")) {
679
+ return validateZip(asset.data, path, true);
680
+ }
681
+ if (mediaType.includes("zip"))
682
+ return validateZip(asset.data, path, false);
683
+ return [];
684
+ } catch (error) {
685
+ return [
686
+ issue("format_invalid", error instanceof Error ? error.message : String(error), path)
687
+ ];
688
+ }
689
+ });
90
690
  // src/standardKinds.ts
91
691
  import { Type } from "typebox";
92
692
  var FileArtifactContentSchema = Type.Object({
@@ -192,6 +792,7 @@ var createArtifactService = (options) => {
192
792
  const now = () => (options.clock ?? (() => new Date))().toISOString();
193
793
  const idFactory = options.idFactory ?? (() => crypto.randomUUID());
194
794
  const eventIdFactory = options.eventIdFactory ?? (() => crypto.randomUUID());
795
+ const batchIdFactory = options.batchIdFactory ?? (() => crypto.randomUUID());
195
796
  const event = (artifact, type, payload) => ({
196
797
  artifactId: artifact.id,
197
798
  createdAt: now(),
@@ -391,6 +992,231 @@ var createArtifactService = (options) => {
391
992
  throw error;
392
993
  }
393
994
  },
995
+ stageBatch: async (ownerId, input, stageOptions = {}) => {
996
+ if (input.items.length === 0) {
997
+ throw new ArtifactError("invalid_content", "An artifact batch must contain at least one artifact");
998
+ }
999
+ const keys = input.items.map((item) => item.key.trim());
1000
+ if (keys.some((key) => key.length === 0) || new Set(keys).size !== keys.length) {
1001
+ throw new ArtifactError("invalid_content", "Artifact batch item keys must be non-empty and unique");
1002
+ }
1003
+ const bundleId = input.bundleId ?? batchIdFactory();
1004
+ const stagedAt = now();
1005
+ const sharedEvidence = input.evidence ?? [];
1006
+ const staged = [];
1007
+ const evidenceJson = (evidence) => evidence.map((reference) => ({
1008
+ ...reference.capturedAt ? { capturedAt: reference.capturedAt } : {},
1009
+ ...reference.excerpt ? { excerpt: reference.excerpt } : {},
1010
+ ...reference.metadata ? { metadata: reference.metadata } : {},
1011
+ ...reference.sourceId ? { sourceId: reference.sourceId } : {},
1012
+ ...reference.sourceUrl ? { sourceUrl: reference.sourceUrl } : {}
1013
+ }));
1014
+ try {
1015
+ for (const [index, item] of input.items.entries()) {
1016
+ const { assets: assetInputs = [], ...artifactInput } = item.artifact;
1017
+ validateAssetInputs(item.artifact.kind, assetInputs);
1018
+ const evidence = [...sharedEvidence, ...item.evidence ?? []];
1019
+ const provisional = buildRecord(ownerId, {
1020
+ ...artifactInput,
1021
+ metadata: {
1022
+ ...input.metadata,
1023
+ ...artifactInput.metadata,
1024
+ artifactBatch: {
1025
+ bundleId,
1026
+ evidence: evidenceJson(evidence),
1027
+ itemKey: item.key
1028
+ }
1029
+ },
1030
+ provenance: {
1031
+ ...input.provenance,
1032
+ ...artifactInput.provenance,
1033
+ evidence,
1034
+ sourceIds: [
1035
+ ...new Set([
1036
+ ...input.provenance?.sourceIds ?? [],
1037
+ ...artifactInput.provenance?.sourceIds ?? [],
1038
+ ...evidence.flatMap((reference) => reference.sourceId ? [reference.sourceId] : [])
1039
+ ])
1040
+ ]
1041
+ }
1042
+ }, []);
1043
+ if (assetInputs.length === 0) {
1044
+ staged.push({ evidence, key: item.key, record: provisional });
1045
+ continue;
1046
+ }
1047
+ const assetStore = requireAssetTransactions();
1048
+ const transaction = await assetStore.stage(assetInputs, {
1049
+ artifact: provisional,
1050
+ idempotencyKey: `artifact-batch:${bundleId}:${index}`
1051
+ });
1052
+ staged.push({
1053
+ evidence,
1054
+ key: item.key,
1055
+ record: {
1056
+ ...provisional,
1057
+ assets: validateAssets(item.artifact.kind, transaction.references)
1058
+ },
1059
+ transaction
1060
+ });
1061
+ }
1062
+ } catch (error) {
1063
+ await Promise.allSettled(staged.map((item) => item.transaction?.rollback()));
1064
+ throw error;
1065
+ }
1066
+ let validationIssues;
1067
+ try {
1068
+ validationIssues = (await Promise.all((stageOptions.validators ?? []).map((validate) => validate({
1069
+ bundleId,
1070
+ evidence: sharedEvidence,
1071
+ items: staged,
1072
+ ownerId
1073
+ })))).flat();
1074
+ } catch (error) {
1075
+ await Promise.allSettled(staged.map((item) => item.transaction?.rollback()));
1076
+ throw error;
1077
+ }
1078
+ const validation = validationIssues.length === 0 ? { valid: true } : { issues: validationIssues, valid: false };
1079
+ let settled;
1080
+ const receiptItems = () => staged.map(({ key, record }) => ({
1081
+ artifactId: record.id,
1082
+ key,
1083
+ kind: record.kind,
1084
+ revision: record.revision,
1085
+ title: record.title
1086
+ }));
1087
+ const finish = (partial) => {
1088
+ settled = {
1089
+ bundleId,
1090
+ completedAt: now(),
1091
+ items: partial.items ?? receiptItems(),
1092
+ ownerId,
1093
+ stagedAt,
1094
+ validation,
1095
+ ...partial
1096
+ };
1097
+ return settled;
1098
+ };
1099
+ const rollbackTransactions = async (candidates = staged) => {
1100
+ const failures = [];
1101
+ for (const item of candidates) {
1102
+ if (!item.transaction)
1103
+ continue;
1104
+ try {
1105
+ await item.transaction.rollback();
1106
+ } catch {
1107
+ failures.push(item.key);
1108
+ }
1109
+ }
1110
+ return failures;
1111
+ };
1112
+ if (!validation.valid) {
1113
+ const failures = await rollbackTransactions();
1114
+ finish({
1115
+ archivedArtifactIds: [],
1116
+ atomic: Boolean(options.store.createBatch),
1117
+ error: failures.length === 0 ? "Artifact batch validation failed" : `Artifact batch validation failed; asset rollback failed for: ${failures.join(", ")}`,
1118
+ status: failures.length === 0 ? "rolled_back" : "partial_failure"
1119
+ });
1120
+ }
1121
+ return {
1122
+ bundleId,
1123
+ commit: async () => {
1124
+ if (settled)
1125
+ return settled;
1126
+ const transactions = staged.flatMap((item) => item.transaction ? [item.transaction] : []);
1127
+ const entries = staged.map(({ key, record }) => ({
1128
+ events: [
1129
+ event(record, "artifact.created", { bundleId, itemKey: key }),
1130
+ event(record, "artifact.generated", { bundleId, itemKey: key })
1131
+ ],
1132
+ record
1133
+ }));
1134
+ if (options.store.createBatch) {
1135
+ try {
1136
+ for (const transaction of transactions)
1137
+ await transaction.commit();
1138
+ await options.store.createBatch(entries);
1139
+ return finish({
1140
+ archivedArtifactIds: [],
1141
+ atomic: true,
1142
+ status: "committed"
1143
+ });
1144
+ } catch (error) {
1145
+ const failures = await rollbackTransactions();
1146
+ return finish({
1147
+ archivedArtifactIds: [],
1148
+ atomic: true,
1149
+ error: error instanceof Error ? error.message : String(error),
1150
+ status: failures.length === 0 ? "rolled_back" : "partial_failure"
1151
+ });
1152
+ }
1153
+ }
1154
+ if ((input.commitMode ?? "require_atomic") === "require_atomic") {
1155
+ const failures = await rollbackTransactions();
1156
+ return finish({
1157
+ archivedArtifactIds: [],
1158
+ atomic: false,
1159
+ error: "The configured artifact store does not support atomic batches",
1160
+ status: failures.length === 0 ? "rolled_back" : "partial_failure"
1161
+ });
1162
+ }
1163
+ const created = [];
1164
+ try {
1165
+ for (const transaction of transactions)
1166
+ await transaction.commit();
1167
+ for (const [index, entry] of entries.entries()) {
1168
+ await options.store.create(entry.record, entry.events);
1169
+ created.push(staged[index]);
1170
+ }
1171
+ return finish({
1172
+ archivedArtifactIds: [],
1173
+ atomic: false,
1174
+ status: "committed"
1175
+ });
1176
+ } catch (error) {
1177
+ const archivedArtifactIds = [];
1178
+ const archiveFailures = [];
1179
+ for (const item of created) {
1180
+ try {
1181
+ await service.archive(ownerId, item.record.id);
1182
+ archivedArtifactIds.push(item.record.id);
1183
+ } catch {
1184
+ archiveFailures.push(item.record.id);
1185
+ }
1186
+ }
1187
+ const uncreated = staged.filter((item) => !created.includes(item));
1188
+ const rollbackFailures = await rollbackTransactions(uncreated);
1189
+ const failed = [...archiveFailures, ...rollbackFailures];
1190
+ return finish({
1191
+ archivedArtifactIds,
1192
+ atomic: false,
1193
+ error: error instanceof Error ? error.message : String(error),
1194
+ items: receiptItems().map((item) => ({
1195
+ ...item,
1196
+ ...archivedArtifactIds.includes(item.artifactId) ? { archived: true } : {}
1197
+ })),
1198
+ status: failed.length === 0 ? "rolled_back" : "partial_failure"
1199
+ });
1200
+ }
1201
+ },
1202
+ evidence: sharedEvidence,
1203
+ items: staged,
1204
+ ownerId,
1205
+ rollback: async (reason = "Artifact batch rolled back before commit") => {
1206
+ if (settled)
1207
+ return settled;
1208
+ const failures = await rollbackTransactions();
1209
+ return finish({
1210
+ archivedArtifactIds: [],
1211
+ atomic: Boolean(options.store.createBatch),
1212
+ error: failures.length === 0 ? reason : `${reason}; asset rollback failed for: ${failures.join(", ")}`,
1213
+ status: failures.length === 0 ? "rolled_back" : "partial_failure"
1214
+ });
1215
+ },
1216
+ stagedAt,
1217
+ validation
1218
+ };
1219
+ },
394
1220
  detach: async (ownerId, artifactId, assetId, expectedRevision) => {
395
1221
  const current = await get(ownerId, artifactId);
396
1222
  requireCapability(current, "attach");
@@ -650,6 +1476,21 @@ var createMemoryArtifactStore = (initial = []) => {
650
1476
  for (const event of newEvents)
651
1477
  events.set(event.id, clone(event));
652
1478
  },
1479
+ createBatch: async (entries) => {
1480
+ const ids = entries.map(({ record }) => record.id);
1481
+ if (new Set(ids).size !== ids.length) {
1482
+ throw new Error("Duplicate artifact id in batch");
1483
+ }
1484
+ const duplicate = ids.find((id) => records.has(id));
1485
+ if (duplicate)
1486
+ throw new Error(`Duplicate artifact id: ${duplicate}`);
1487
+ for (const { events: newEvents = [], record } of entries) {
1488
+ records.set(record.id, clone(record));
1489
+ revisions.set(record.id, [clone(record)]);
1490
+ for (const event of newEvents)
1491
+ events.set(event.id, clone(event));
1492
+ }
1493
+ },
653
1494
  get: async (ownerId, artifactId) => {
654
1495
  const record = records.get(artifactId);
655
1496
  return record?.ownerId === ownerId ? clone(record) : null;
@@ -855,17 +1696,18 @@ var createArtifactTools = (options) => ({
855
1696
  }
856
1697
  });
857
1698
  export {
858
- standardArtifactDefinitions,
859
- isJsonValue,
860
- defineArtifactRegistry,
861
- createMemoryArtifactStore,
862
- createMemoryArtifactAssetStore,
863
- createArtifactTools,
864
- createArtifactService,
865
- createArtifactRendererRegistry,
866
- createArtifactGeneratorRegistry,
867
- STANDARD_ARTIFACT_KIND_NAMES,
868
- ArtifactError,
1699
+ ARTIFACT_EVENT_TYPES,
869
1700
  ARTIFACT_STATUSES,
870
- ARTIFACT_EVENT_TYPES
1701
+ ArtifactError,
1702
+ STANDARD_ARTIFACT_KIND_NAMES,
1703
+ createArtifactGeneratorRegistry,
1704
+ createArtifactRendererRegistry,
1705
+ createArtifactService,
1706
+ createArtifactTools,
1707
+ createMemoryArtifactAssetStore,
1708
+ createMemoryArtifactStore,
1709
+ defineArtifactRegistry,
1710
+ isJsonValue,
1711
+ standardArtifactDefinitions,
1712
+ validateGeneratedArtifactFormats
871
1713
  };