@xylex-group/athena 3.0.2 → 3.0.3

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 (42) hide show
  1. package/README.md +1 -1
  2. package/dist/billing.cjs +1 -1
  3. package/dist/billing.cjs.map +1 -1
  4. package/dist/billing.js +1 -1
  5. package/dist/billing.js.map +1 -1
  6. package/dist/browser.cjs +1 -1
  7. package/dist/browser.cjs.map +1 -1
  8. package/dist/browser.d.cts +4 -4
  9. package/dist/browser.d.ts +4 -4
  10. package/dist/browser.js +1 -1
  11. package/dist/browser.js.map +1 -1
  12. package/dist/cli/index.cjs +909 -13
  13. package/dist/cli/index.cjs.map +1 -1
  14. package/dist/cli/index.d.cts +2 -2
  15. package/dist/cli/index.d.ts +2 -2
  16. package/dist/cli/index.js +910 -14
  17. package/dist/cli/index.js.map +1 -1
  18. package/dist/index.cjs +840 -10
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +4 -4
  21. package/dist/index.d.ts +4 -4
  22. package/dist/index.js +841 -11
  23. package/dist/index.js.map +1 -1
  24. package/dist/{module-CkUM6v58.d.ts → module-BruTcxe0.d.ts} +1 -1
  25. package/dist/{module-CB25egcO.d.cts → module-CPG3ULxQ.d.cts} +1 -1
  26. package/dist/next/client.cjs +1 -1
  27. package/dist/next/client.cjs.map +1 -1
  28. package/dist/next/client.js +1 -1
  29. package/dist/next/client.js.map +1 -1
  30. package/dist/next/server.cjs +1 -1
  31. package/dist/next/server.cjs.map +1 -1
  32. package/dist/next/server.js +1 -1
  33. package/dist/next/server.js.map +1 -1
  34. package/dist/{pipeline-D4W-Cc-A.d.cts → pipeline-CIzV9f7b.d.cts} +1 -1
  35. package/dist/{pipeline-B8aN2EHe.d.ts → pipeline-DNlc8Ayn.d.ts} +1 -1
  36. package/dist/react.cjs +1 -1
  37. package/dist/react.cjs.map +1 -1
  38. package/dist/react.js +1 -1
  39. package/dist/react.js.map +1 -1
  40. package/dist/{types-BaAMXCqK.d.ts → types-D6tZ9aoq.d.ts} +34 -1
  41. package/dist/{types-BBm-kEBL.d.cts → types-DFr2cL1N.d.cts} +34 -1
  42. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -1,10 +1,766 @@
1
- import { mkdir, writeFile, stat } from 'fs/promises';
1
+ import { mkdir, writeFile, stat, readFile } from 'fs/promises';
2
2
  import { resolve, dirname, posix } from 'path';
3
3
  import { existsSync, readFileSync } from 'fs';
4
4
  import { pathToFileURL } from 'url';
5
5
 
6
6
  // src/generator/pipeline.ts
7
7
 
8
+ // src/generator/artifact-merge.ts
9
+ var IMPORT_RE = /^import\s+(?:type\s+)?(?:\{([^}]*)\}|([A-Za-z_$][\w$]*))\s+from\s+(['"])([^'"]+)\3\s*;?\s*$/gm;
10
+ var DEFINE_EXPORT_RE = /export\s+const\s+([A-Za-z_$][\w$]*)\s*=\s*(define(?:Database|Registry))\s*\(\s*\{/g;
11
+ var META_EXPORT_RE = /export\s+const\s+__athena_schema_meta\s*=\s*\{/g;
12
+ var KNOWN_META_KEYS = /* @__PURE__ */ new Set([
13
+ "schemaVersion",
14
+ "generatedAt",
15
+ "database",
16
+ "outputPreset",
17
+ "outputFormat"
18
+ ]);
19
+ function detectNewline(source) {
20
+ return source.includes("\r\n") ? "\r\n" : "\n";
21
+ }
22
+ function detectStyle(source) {
23
+ const newline = detectNewline(source);
24
+ const single = (source.match(/'/g) ?? []).length;
25
+ const double = (source.match(/"/g) ?? []).length;
26
+ const quote = double > single ? '"' : "'";
27
+ const importLines = source.split(/\r?\n/).filter((line) => line.trimStart().startsWith("import "));
28
+ const semicolons = importLines.length > 0 ? importLines.filter((line) => line.trimEnd().endsWith(";")).length >= importLines.length / 2 : false;
29
+ const objectLines = source.split(/\r?\n/).map((line) => line.trim()).filter((line) => /^[A-Za-z0-9_'"]+\s*:\s*.+/.test(line));
30
+ const trailingComma = objectLines.length > 0 ? objectLines.filter((line) => line.endsWith(",")).length >= Math.ceil(objectLines.length / 2) : true;
31
+ const indentMatch = source.match(/\n([ \t]+)\S/);
32
+ const indent = indentMatch?.[1] ?? " ";
33
+ return { quote, semicolons, trailingComma, indent, newline };
34
+ }
35
+ function quoteString(value, quote) {
36
+ const escaped = value.replace(/\\/g, "\\\\").replace(new RegExp(quote, "g"), `\\${quote}`);
37
+ return `${quote}${escaped}${quote}`;
38
+ }
39
+ function parseNamedImports(source) {
40
+ const imports = [];
41
+ IMPORT_RE.lastIndex = 0;
42
+ let match;
43
+ while ((match = IMPORT_RE.exec(source)) !== null) {
44
+ const named = match[1];
45
+ const defaultName = match[2];
46
+ const module = match[4];
47
+ const names = named ? named.split(",").map((part) => part.trim()).filter(Boolean).map((part) => {
48
+ const alias = part.split(/\s+as\s+/);
49
+ return (alias[1] ?? alias[0]).trim();
50
+ }) : defaultName ? [defaultName] : [];
51
+ imports.push({
52
+ names,
53
+ module,
54
+ raw: match[0],
55
+ start: match.index,
56
+ end: match.index + match[0].length
57
+ });
58
+ }
59
+ return imports;
60
+ }
61
+ function findMatchingBrace(source, openIndex) {
62
+ let depth = 0;
63
+ let inSingle = false;
64
+ let inDouble = false;
65
+ let inTemplate = false;
66
+ let escaped = false;
67
+ for (let i = openIndex; i < source.length; i += 1) {
68
+ const ch = source[i];
69
+ if (escaped) {
70
+ escaped = false;
71
+ continue;
72
+ }
73
+ if (ch === "\\" && (inSingle || inDouble || inTemplate)) {
74
+ escaped = true;
75
+ continue;
76
+ }
77
+ if (!inDouble && !inTemplate && ch === "'") {
78
+ inSingle = !inSingle;
79
+ continue;
80
+ }
81
+ if (!inSingle && !inTemplate && ch === '"') {
82
+ inDouble = !inDouble;
83
+ continue;
84
+ }
85
+ if (!inSingle && !inDouble && ch === "`") {
86
+ inTemplate = !inTemplate;
87
+ continue;
88
+ }
89
+ if (inSingle || inDouble || inTemplate) {
90
+ continue;
91
+ }
92
+ if (ch === "{") {
93
+ depth += 1;
94
+ } else if (ch === "}") {
95
+ depth -= 1;
96
+ if (depth === 0) {
97
+ return i;
98
+ }
99
+ }
100
+ }
101
+ return -1;
102
+ }
103
+ function parseObjectEntries(body) {
104
+ const entries = [];
105
+ const lines = body.split(/\r?\n/);
106
+ for (const line of lines) {
107
+ const trimmed = line.trim();
108
+ if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("/*")) {
109
+ continue;
110
+ }
111
+ const withoutComma = trimmed.endsWith(",") ? trimmed.slice(0, -1).trimEnd() : trimmed;
112
+ const match = withoutComma.match(/^([A-Za-z_$][\w$]*|['"][^'"]+['"])\s*:\s*(.+)$/);
113
+ if (!match) {
114
+ continue;
115
+ }
116
+ const keyRaw = match[1];
117
+ const key = keyRaw.startsWith("'") || keyRaw.startsWith('"') ? keyRaw.slice(1, -1) : keyRaw;
118
+ entries.push({
119
+ key,
120
+ value: match[2].trim(),
121
+ raw: trimmed
122
+ });
123
+ }
124
+ return entries;
125
+ }
126
+ function parseDefineBlocks(source) {
127
+ const blocks = [];
128
+ DEFINE_EXPORT_RE.lastIndex = 0;
129
+ let match;
130
+ while ((match = DEFINE_EXPORT_RE.exec(source)) !== null) {
131
+ const exportName = match[1];
132
+ const callName = match[2];
133
+ const openBrace = match.index + match[0].lastIndexOf("{");
134
+ const closeBrace = findMatchingBrace(source, openBrace);
135
+ if (closeBrace < 0) {
136
+ continue;
137
+ }
138
+ const body = source.slice(openBrace + 1, closeBrace);
139
+ const fullEnd = (() => {
140
+ let i = closeBrace + 1;
141
+ while (i < source.length && /\s/.test(source[i])) i += 1;
142
+ if (source[i] === ")") i += 1;
143
+ while (i < source.length && /\s/.test(source[i])) i += 1;
144
+ if (source[i] === ";") i += 1;
145
+ return i;
146
+ })();
147
+ blocks.push({
148
+ kind: callName === "defineDatabase" ? "database" : "registry",
149
+ exportName,
150
+ callName,
151
+ entries: parseObjectEntries(body),
152
+ bodyStart: openBrace + 1,
153
+ bodyEnd: closeBrace,
154
+ fullStart: match.index,
155
+ fullEnd,
156
+ raw: source.slice(match.index, fullEnd)
157
+ });
158
+ }
159
+ return blocks;
160
+ }
161
+ function parseMetaBlock(source) {
162
+ META_EXPORT_RE.lastIndex = 0;
163
+ const match = META_EXPORT_RE.exec(source);
164
+ if (!match) {
165
+ return void 0;
166
+ }
167
+ const openBrace = match.index + match[0].lastIndexOf("{");
168
+ const closeBrace = findMatchingBrace(source, openBrace);
169
+ if (closeBrace < 0) {
170
+ return void 0;
171
+ }
172
+ let fullEnd = closeBrace + 1;
173
+ const after = source.slice(fullEnd);
174
+ const asConst = after.match(/^\s*as\s+const\s*;?/);
175
+ if (asConst) {
176
+ fullEnd += asConst[0].length;
177
+ } else if (source[fullEnd] === ";") {
178
+ fullEnd += 1;
179
+ }
180
+ return {
181
+ entries: parseObjectEntries(source.slice(openBrace + 1, closeBrace)),
182
+ bodyStart: openBrace + 1,
183
+ bodyEnd: closeBrace,
184
+ fullStart: match.index,
185
+ fullEnd,
186
+ raw: source.slice(match.index, fullEnd)
187
+ };
188
+ }
189
+ function hasImportBinding(imports, name, module) {
190
+ return imports.some(
191
+ (item) => item.names.includes(name) && (module === void 0)
192
+ );
193
+ }
194
+ function findImportForName(imports, name) {
195
+ return imports.find((item) => item.names.includes(name));
196
+ }
197
+ function normalizeModulePath(modulePath) {
198
+ return modulePath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\.ts$/, "");
199
+ }
200
+ function formatImport(names, modulePath, style) {
201
+ const body = `import { ${names.join(", ")} } from ${quoteString(modulePath, style.quote)}`;
202
+ return style.semicolons ? `${body};` : body;
203
+ }
204
+ function formatObjectEntry(key, value, style, isLast) {
205
+ const needsQuote = !/^[A-Za-z_$][\w$]*$/.test(key);
206
+ const renderedKey = needsQuote ? quoteString(key, style.quote) : key;
207
+ const comma = !isLast || style.trailingComma ? "," : "";
208
+ return `${style.indent}${renderedKey}: ${value}${comma}`;
209
+ }
210
+ function replaceRange(source, start, end, insertion) {
211
+ return source.slice(0, start) + insertion + source.slice(end);
212
+ }
213
+ function collectDuplicateKeys(entries) {
214
+ const seen = /* @__PURE__ */ new Set();
215
+ const dupes = [];
216
+ for (const entry of entries) {
217
+ if (seen.has(entry.key)) {
218
+ dupes.push(entry.key);
219
+ }
220
+ seen.add(entry.key);
221
+ }
222
+ return dupes;
223
+ }
224
+ function collectDuplicateImportBindings(imports) {
225
+ const seen = /* @__PURE__ */ new Set();
226
+ const dupes = [];
227
+ for (const item of imports) {
228
+ for (const name of item.names) {
229
+ if (seen.has(name)) {
230
+ dupes.push(name);
231
+ }
232
+ seen.add(name);
233
+ }
234
+ }
235
+ return dupes;
236
+ }
237
+ function lintArtifactSource(source, kind) {
238
+ const errors = [];
239
+ const imports = parseNamedImports(source);
240
+ const dupImports = collectDuplicateImportBindings(imports);
241
+ if (dupImports.length > 0) {
242
+ errors.push(`duplicate import bindings: ${dupImports.join(", ")}`);
243
+ }
244
+ const blocks = parseDefineBlocks(source).filter((block2) => block2.kind === kind);
245
+ if (blocks.length === 0) {
246
+ errors.push(`missing export const \u2026 = define${kind === "database" ? "Database" : "Registry"}({\u2026})`);
247
+ return errors;
248
+ }
249
+ if (blocks.length > 1) {
250
+ errors.push(`multiple define${kind === "database" ? "Database" : "Registry"} exports found`);
251
+ }
252
+ const block = blocks[0];
253
+ const dupKeys = collectDuplicateKeys(block.entries);
254
+ if (dupKeys.length > 0) {
255
+ errors.push(`duplicate object keys: ${dupKeys.join(", ")}`);
256
+ }
257
+ for (const entry of block.entries) {
258
+ const valueId = entry.value.match(/^[A-Za-z_$][\w$]*$/)?.[0];
259
+ if (valueId && !hasImportBinding(imports, valueId) && valueId !== block.exportName) {
260
+ if (!source.includes(`const ${valueId}`) && !source.includes(`function ${valueId}`)) {
261
+ errors.push(`value "${valueId}" for key "${entry.key}" is not imported`);
262
+ }
263
+ }
264
+ }
265
+ if (kind === "database" && !hasImportBinding(imports, "defineDatabase")) {
266
+ errors.push("missing defineDatabase import");
267
+ }
268
+ if (kind === "registry" && !hasImportBinding(imports, "defineRegistry")) {
269
+ errors.push("missing defineRegistry import");
270
+ }
271
+ return errors;
272
+ }
273
+ function preservedCustomUnits(existing, generated, kind) {
274
+ const custom = [];
275
+ const existingImports = parseNamedImports(existing);
276
+ const generatedImports = parseNamedImports(generated);
277
+ const generatedModules = new Set(generatedImports.map((item) => normalizeModulePath(item.module)));
278
+ const generatedNames = new Set(generatedImports.flatMap((item) => item.names));
279
+ for (const item of existingImports) {
280
+ const moduleNorm = normalizeModulePath(item.module);
281
+ if (moduleNorm.includes("@xylex-group/athena")) {
282
+ continue;
283
+ }
284
+ const unexpectedNames = item.names.filter((name) => !generatedNames.has(name));
285
+ if (unexpectedNames.length > 0 && !generatedModules.has(moduleNorm)) {
286
+ custom.push(`import { ${unexpectedNames.join(", ")} } from '${item.module}'`);
287
+ } else if (unexpectedNames.length > 0) {
288
+ custom.push(`import binding(s): ${unexpectedNames.join(", ")}`);
289
+ }
290
+ }
291
+ const existingBlocks = parseDefineBlocks(existing).filter((block) => block.kind === kind);
292
+ const generatedBlocks = parseDefineBlocks(generated).filter((block) => block.kind === kind);
293
+ const generatedKeys = new Set(generatedBlocks[0]?.entries.map((entry) => entry.key) ?? []);
294
+ for (const entry of existingBlocks[0]?.entries ?? []) {
295
+ if (!generatedKeys.has(entry.key)) {
296
+ custom.push(`${kind} entry: ${entry.key}: ${entry.value}`);
297
+ }
298
+ }
299
+ if (kind === "registry") {
300
+ const existingMeta = parseMetaBlock(existing);
301
+ const generatedMeta = parseMetaBlock(generated);
302
+ const generatedMetaKeys = new Set(generatedMeta?.entries.map((entry) => entry.key) ?? []);
303
+ for (const entry of existingMeta?.entries ?? []) {
304
+ if (!generatedMetaKeys.has(entry.key) && !KNOWN_META_KEYS.has(entry.key)) {
305
+ custom.push(`meta entry: ${entry.key}: ${entry.value}`);
306
+ }
307
+ }
308
+ }
309
+ const exportConstRe = /^export\s+const\s+([A-Za-z_$][\w$]*)\b/gm;
310
+ const generatedExports = /* @__PURE__ */ new Set();
311
+ let match;
312
+ exportConstRe.lastIndex = 0;
313
+ while ((match = exportConstRe.exec(generated)) !== null) {
314
+ generatedExports.add(match[1]);
315
+ }
316
+ exportConstRe.lastIndex = 0;
317
+ while ((match = exportConstRe.exec(existing)) !== null) {
318
+ if (!generatedExports.has(match[1]) && match[1] !== "__athena_schema_meta") {
319
+ custom.push(`export const ${match[1]}`);
320
+ }
321
+ }
322
+ return custom;
323
+ }
324
+ function insertImportAfterPackageImports(source, importLine, style) {
325
+ const imports = parseNamedImports(source);
326
+ if (imports.length === 0) {
327
+ return `${importLine}${style.newline}${source}`;
328
+ }
329
+ let anchor = imports[imports.length - 1];
330
+ for (let i = imports.length - 1; i >= 0; i -= 1) {
331
+ if (imports[i].module.startsWith(".")) {
332
+ anchor = imports[i];
333
+ break;
334
+ }
335
+ }
336
+ const insertAt = anchor.end;
337
+ const before = source.slice(0, insertAt);
338
+ const after = source.slice(insertAt);
339
+ const needsLeadingNl = !before.endsWith("\n");
340
+ const prefix = needsLeadingNl ? style.newline : "";
341
+ return `${before}${prefix}${importLine}${after.startsWith("\n") || after.startsWith("\r\n") ? "" : style.newline}${after}`;
342
+ }
343
+ function rewriteObjectBody(entries, style) {
344
+ if (entries.length === 0) {
345
+ return style.newline;
346
+ }
347
+ const lines = entries.map(
348
+ (entry, index) => formatObjectEntry(entry.key, entry.value, style, index === entries.length - 1)
349
+ );
350
+ return `${style.newline}${lines.join(style.newline)}${style.newline}`;
351
+ }
352
+ function mergeDatabaseArtifact(existing, generated) {
353
+ const style = detectStyle(existing);
354
+ const generatedStyle = detectStyle(generated);
355
+ const effectiveStyle = {
356
+ ...style,
357
+ // Prefer existing fingerprint; fall back to generated if existing is empty-ish
358
+ quote: existing.includes('"') || existing.includes("'") ? style.quote : generatedStyle.quote
359
+ };
360
+ const generatedImports = parseNamedImports(generated);
361
+ const existingBlocks = parseDefineBlocks(existing).filter((block) => block.kind === "database");
362
+ const generatedBlocks = parseDefineBlocks(generated).filter((block) => block.kind === "database");
363
+ if (existingBlocks.length === 0 || generatedBlocks.length === 0) {
364
+ return {
365
+ action: "skip",
366
+ skipReason: "merge-unparseable",
367
+ added: [],
368
+ preservedCustom: [],
369
+ conflicts: [],
370
+ lintErrors: [],
371
+ detail: "could not locate defineDatabase({\u2026}) export for merge"
372
+ };
373
+ }
374
+ const generatedBlock = generatedBlocks[0];
375
+ const conflicts = [];
376
+ const added = [];
377
+ let next = existing;
378
+ if (!hasImportBinding(parseNamedImports(next), "defineDatabase")) {
379
+ const pkgImport = generatedImports.find((item) => item.names.includes("defineDatabase"));
380
+ if (pkgImport) {
381
+ const line = formatImport(["defineDatabase"], pkgImport.module, effectiveStyle);
382
+ next = insertImportAfterPackageImports(next, line, effectiveStyle);
383
+ added.push("import defineDatabase");
384
+ }
385
+ }
386
+ let workingImports = parseNamedImports(next);
387
+ let workingBlocks = parseDefineBlocks(next).filter((block) => block.kind === "database");
388
+ let workingBlock = workingBlocks[0];
389
+ const entryMap = new Map(workingBlock.entries.map((entry) => [entry.key, entry]));
390
+ for (const desired of generatedBlock.entries) {
391
+ const existingEntry = entryMap.get(desired.key);
392
+ if (existingEntry) {
393
+ if (existingEntry.value !== desired.value) {
394
+ conflicts.push(
395
+ `key "${desired.key}" maps to ${existingEntry.value} (existing) vs ${desired.value} (generated)`
396
+ );
397
+ }
398
+ continue;
399
+ }
400
+ const desiredImport = generatedImports.find((item) => item.names.includes(desired.value));
401
+ if (desiredImport) {
402
+ const existingForName = findImportForName(workingImports, desired.value);
403
+ if (existingForName) {
404
+ if (normalizeModulePath(existingForName.module) !== normalizeModulePath(desiredImport.module)) {
405
+ conflicts.push(
406
+ `binding "${desired.value}" imported from '${existingForName.module}' vs '${desiredImport.module}'`
407
+ );
408
+ continue;
409
+ }
410
+ } else {
411
+ const line = formatImport([desired.value], desiredImport.module, effectiveStyle);
412
+ next = insertImportAfterPackageImports(next, line, effectiveStyle);
413
+ added.push(`import ${desired.value}`);
414
+ workingImports = parseNamedImports(next);
415
+ }
416
+ }
417
+ workingBlocks = parseDefineBlocks(next).filter((block) => block.kind === "database");
418
+ workingBlock = workingBlocks[0];
419
+ const nextEntries = [...workingBlock.entries, { key: desired.key, value: desired.value, raw: "" }];
420
+ const body = rewriteObjectBody(nextEntries, effectiveStyle);
421
+ next = replaceRange(next, workingBlock.bodyStart, workingBlock.bodyEnd, body);
422
+ entryMap.set(desired.key, { key: desired.key, value: desired.value, raw: "" });
423
+ added.push(`database entry: ${desired.key}`);
424
+ }
425
+ if (conflicts.length > 0 && added.length === 0) {
426
+ return {
427
+ action: "skip",
428
+ skipReason: "merge-conflict",
429
+ added: [],
430
+ preservedCustom: preservedCustomUnits(existing, generated, "database"),
431
+ conflicts,
432
+ lintErrors: [],
433
+ detail: conflicts.join("; ")
434
+ };
435
+ }
436
+ const lintErrors = lintArtifactSource(next, "database");
437
+ if (lintErrors.length > 0) {
438
+ return {
439
+ action: "skip",
440
+ skipReason: "merge-lint-failed",
441
+ added,
442
+ preservedCustom: preservedCustomUnits(existing, generated, "database"),
443
+ conflicts,
444
+ lintErrors,
445
+ detail: lintErrors.join("; ")
446
+ };
447
+ }
448
+ const preservedCustom = preservedCustomUnits(next, generated, "database");
449
+ if (next === existing || next.replace(/\r\n/g, "\n") === existing.replace(/\r\n/g, "\n")) {
450
+ return {
451
+ action: "unchanged",
452
+ content: existing,
453
+ skipReason: "already-current",
454
+ added: [],
455
+ preservedCustom,
456
+ conflicts,
457
+ lintErrors: [],
458
+ detail: conflicts.length > 0 ? conflicts.join("; ") : void 0
459
+ };
460
+ }
461
+ return {
462
+ action: "write",
463
+ content: next.endsWith("\n") ? next : `${next}${effectiveStyle.newline}`,
464
+ writeReason: "merged",
465
+ added,
466
+ preservedCustom,
467
+ conflicts,
468
+ lintErrors: []
469
+ };
470
+ }
471
+ function mergeMetaFields(existingMeta, generatedMeta) {
472
+ const added = [];
473
+ if (!generatedMeta) {
474
+ return { entries: existingMeta?.entries ?? [], changed: false, added };
475
+ }
476
+ if (!existingMeta) {
477
+ return {
478
+ entries: generatedMeta.entries,
479
+ changed: true,
480
+ added: generatedMeta.entries.map((entry) => `meta entry: ${entry.key}`)
481
+ };
482
+ }
483
+ const map = new Map(existingMeta.entries.map((entry) => [entry.key, entry]));
484
+ let changed = false;
485
+ for (const desired of generatedMeta.entries) {
486
+ const current = map.get(desired.key);
487
+ if (!current) {
488
+ map.set(desired.key, desired);
489
+ added.push(`meta entry: ${desired.key}`);
490
+ changed = true;
491
+ continue;
492
+ }
493
+ if (KNOWN_META_KEYS.has(desired.key) && current.value !== desired.value) {
494
+ map.set(desired.key, desired);
495
+ added.push(`meta refresh: ${desired.key}`);
496
+ changed = true;
497
+ }
498
+ }
499
+ const ordered = [];
500
+ const seen = /* @__PURE__ */ new Set();
501
+ for (const entry of existingMeta.entries) {
502
+ const next = map.get(entry.key);
503
+ if (next) {
504
+ ordered.push(next);
505
+ seen.add(entry.key);
506
+ }
507
+ }
508
+ for (const entry of generatedMeta.entries) {
509
+ if (!seen.has(entry.key)) {
510
+ const next = map.get(entry.key);
511
+ if (next) {
512
+ ordered.push(next);
513
+ seen.add(entry.key);
514
+ }
515
+ }
516
+ }
517
+ for (const [key, entry] of map) {
518
+ if (!seen.has(key)) {
519
+ ordered.push(entry);
520
+ }
521
+ }
522
+ return { entries: ordered, changed, added };
523
+ }
524
+ function mergeRegistryArtifact(existing, generated) {
525
+ const style = detectStyle(existing);
526
+ const effectiveStyle = style;
527
+ const generatedImports = parseNamedImports(generated);
528
+ const existingBlocks = parseDefineBlocks(existing).filter((block) => block.kind === "registry");
529
+ const generatedBlocks = parseDefineBlocks(generated).filter((block) => block.kind === "registry");
530
+ if (existingBlocks.length === 0 || generatedBlocks.length === 0) {
531
+ return {
532
+ action: "skip",
533
+ skipReason: "merge-unparseable",
534
+ added: [],
535
+ preservedCustom: [],
536
+ conflicts: [],
537
+ lintErrors: [],
538
+ detail: "could not locate defineRegistry({\u2026}) export for merge"
539
+ };
540
+ }
541
+ const existingBlock = existingBlocks[0];
542
+ const generatedBlock = generatedBlocks[0];
543
+ const conflicts = [];
544
+ const added = [];
545
+ let next = existing;
546
+ if (!hasImportBinding(parseNamedImports(next), "defineRegistry")) {
547
+ const pkgImport = generatedImports.find((item) => item.names.includes("defineRegistry"));
548
+ if (pkgImport) {
549
+ next = insertImportAfterPackageImports(
550
+ next,
551
+ formatImport(["defineRegistry"], pkgImport.module, effectiveStyle),
552
+ effectiveStyle
553
+ );
554
+ added.push("import defineRegistry");
555
+ }
556
+ }
557
+ const preferredDbValue = existingBlock.entries[0]?.value ?? generatedBlock.entries[0]?.value;
558
+ const generatedDbImport = generatedImports.find((item) => item.names.includes(generatedBlock.entries[0]?.value ?? "")) ?? generatedImports.find((item) => item.module.startsWith("."));
559
+ if (preferredDbValue && generatedDbImport) {
560
+ const existingForName = findImportForName(parseNamedImports(next), preferredDbValue);
561
+ if (!existingForName) {
562
+ const modulePath = generatedDbImport.module;
563
+ const importName = findImportForName(generatedImports, preferredDbValue)?.names[0] ?? generatedBlock.entries[0]?.value ?? preferredDbValue;
564
+ if (!hasImportBinding(parseNamedImports(next), importName)) {
565
+ next = insertImportAfterPackageImports(
566
+ next,
567
+ formatImport([importName], modulePath, effectiveStyle),
568
+ effectiveStyle
569
+ );
570
+ added.push(`import ${importName}`);
571
+ }
572
+ } else if (normalizeModulePath(existingForName.module) !== normalizeModulePath(generatedDbImport.module)) {
573
+ conflicts.push(
574
+ `binding "${preferredDbValue}" imported from '${existingForName.module}' vs '${generatedDbImport.module}'`
575
+ );
576
+ }
577
+ }
578
+ let workingBlocks = parseDefineBlocks(next).filter((block) => block.kind === "registry");
579
+ let workingBlock = workingBlocks[0];
580
+ const entryMap = new Map(workingBlock.entries.map((entry) => [entry.key, entry]));
581
+ for (const desired of generatedBlock.entries) {
582
+ const existingEntry = entryMap.get(desired.key);
583
+ if (existingEntry) {
584
+ continue;
585
+ }
586
+ const dbImport = parseNamedImports(next).find(
587
+ (item) => item.module.startsWith(".") && item.names.some((name) => name !== "defineRegistry")
588
+ );
589
+ const value = dbImport?.names[0] ?? desired.value;
590
+ const nextEntries = [...workingBlock.entries, { key: desired.key, value, raw: "" }];
591
+ const body = rewriteObjectBody(nextEntries, effectiveStyle);
592
+ next = replaceRange(next, workingBlock.bodyStart, workingBlock.bodyEnd, body);
593
+ entryMap.set(desired.key, { key: desired.key, value, raw: "" });
594
+ added.push(`registry entry: ${desired.key}`);
595
+ workingBlocks = parseDefineBlocks(next).filter((block) => block.kind === "registry");
596
+ workingBlock = workingBlocks[0];
597
+ }
598
+ const existingMeta = parseMetaBlock(next);
599
+ const generatedMeta = parseMetaBlock(generated);
600
+ const structuralAdded = added.length > 0;
601
+ if (generatedMeta) {
602
+ if (!existingMeta) {
603
+ const blocks = parseDefineBlocks(next).filter((block) => block.kind === "registry");
604
+ const insertAt = blocks[0]?.fullStart ?? next.length;
605
+ const metaBody = rewriteObjectBody(generatedMeta.entries, effectiveStyle);
606
+ const metaBlock = `export const __athena_schema_meta = {${metaBody}} as const${effectiveStyle.semicolons ? ";" : ""}${effectiveStyle.newline}${effectiveStyle.newline}`;
607
+ next = replaceRange(next, insertAt, insertAt, metaBlock);
608
+ added.push("__athena_schema_meta");
609
+ } else {
610
+ const desiredEntries = structuralAdded ? generatedMeta.entries : generatedMeta.entries.filter((entry) => {
611
+ return !existingMeta.entries.some((current) => current.key === entry.key);
612
+ });
613
+ if (structuralAdded) {
614
+ const merged = mergeMetaFields(existingMeta, generatedMeta);
615
+ if (merged.changed) {
616
+ const metaNow = parseMetaBlock(next);
617
+ if (metaNow) {
618
+ const body = rewriteObjectBody(merged.entries, effectiveStyle);
619
+ next = replaceRange(next, metaNow.bodyStart, metaNow.bodyEnd, body);
620
+ added.push(...merged.added);
621
+ }
622
+ }
623
+ } else if (desiredEntries.length > 0) {
624
+ const map = new Map(existingMeta.entries.map((entry) => [entry.key, entry]));
625
+ for (const entry of desiredEntries) {
626
+ map.set(entry.key, entry);
627
+ added.push(`meta entry: ${entry.key}`);
628
+ }
629
+ const ordered = [
630
+ ...existingMeta.entries.map((entry) => map.get(entry.key)),
631
+ ...desiredEntries.filter((entry) => !existingMeta.entries.some((e) => e.key === entry.key))
632
+ ];
633
+ const metaNow = parseMetaBlock(next);
634
+ if (metaNow) {
635
+ const body = rewriteObjectBody(ordered, effectiveStyle);
636
+ next = replaceRange(next, metaNow.bodyStart, metaNow.bodyEnd, body);
637
+ }
638
+ }
639
+ }
640
+ }
641
+ if (conflicts.length > 0 && added.length === 0) {
642
+ return {
643
+ action: "skip",
644
+ skipReason: "merge-conflict",
645
+ added: [],
646
+ preservedCustom: preservedCustomUnits(existing, generated, "registry"),
647
+ conflicts,
648
+ lintErrors: [],
649
+ detail: conflicts.join("; ")
650
+ };
651
+ }
652
+ const lintErrors = lintArtifactSource(next, "registry");
653
+ if (lintErrors.length > 0) {
654
+ return {
655
+ action: "skip",
656
+ skipReason: "merge-lint-failed",
657
+ added,
658
+ preservedCustom: preservedCustomUnits(existing, generated, "registry"),
659
+ conflicts,
660
+ lintErrors,
661
+ detail: lintErrors.join("; ")
662
+ };
663
+ }
664
+ const preservedCustom = preservedCustomUnits(next, generated, "registry");
665
+ if (next.replace(/\r\n/g, "\n") === existing.replace(/\r\n/g, "\n")) {
666
+ return {
667
+ action: "unchanged",
668
+ content: existing,
669
+ skipReason: "already-current",
670
+ added: [],
671
+ preservedCustom,
672
+ conflicts,
673
+ lintErrors: []
674
+ };
675
+ }
676
+ return {
677
+ action: "write",
678
+ content: next.endsWith("\n") ? next : `${next}${effectiveStyle.newline}`,
679
+ writeReason: "merged",
680
+ added,
681
+ preservedCustom,
682
+ conflicts,
683
+ lintErrors: []
684
+ };
685
+ }
686
+ function mergeProtectedArtifact(kind, existing, generated, policy) {
687
+ if (policy === "overwrite") {
688
+ if (existing.replace(/\r\n/g, "\n") === generated.replace(/\r\n/g, "\n")) {
689
+ return {
690
+ action: "unchanged",
691
+ content: existing,
692
+ skipReason: "already-current",
693
+ added: [],
694
+ preservedCustom: [],
695
+ conflicts: [],
696
+ lintErrors: []
697
+ };
698
+ }
699
+ return {
700
+ action: "write",
701
+ content: generated,
702
+ writeReason: "overwritten",
703
+ added: ["full overwrite"],
704
+ preservedCustom: [],
705
+ conflicts: [],
706
+ lintErrors: []
707
+ };
708
+ }
709
+ if (policy === "skip") {
710
+ return {
711
+ action: "skip",
712
+ skipReason: "protected-existing-file",
713
+ added: [],
714
+ preservedCustom: preservedCustomUnits(existing, generated, kind),
715
+ conflicts: [],
716
+ lintErrors: [],
717
+ detail: "artifactWrite policy is skip"
718
+ };
719
+ }
720
+ return kind === "database" ? mergeDatabaseArtifact(existing, generated) : mergeRegistryArtifact(existing, generated);
721
+ }
722
+ function resolveArtifactWritePlan(file, existingContent, policy) {
723
+ if (existingContent === null) {
724
+ return {
725
+ action: "write",
726
+ content: file.content,
727
+ writeReason: "created",
728
+ added: ["created"],
729
+ preservedCustom: [],
730
+ conflicts: [],
731
+ lintErrors: []
732
+ };
733
+ }
734
+ if (file.kind === "model" || file.kind === "schema" || policy === "always") {
735
+ if (existingContent.replace(/\r\n/g, "\n") === file.content.replace(/\r\n/g, "\n")) {
736
+ return {
737
+ action: "unchanged",
738
+ content: existingContent,
739
+ skipReason: "already-current",
740
+ added: [],
741
+ preservedCustom: [],
742
+ conflicts: [],
743
+ lintErrors: []
744
+ };
745
+ }
746
+ return {
747
+ action: "write",
748
+ content: file.content,
749
+ writeReason: existingContent ? "overwritten" : "created",
750
+ added: ["overwritten"],
751
+ preservedCustom: [],
752
+ conflicts: [],
753
+ lintErrors: []
754
+ };
755
+ }
756
+ return mergeProtectedArtifact(
757
+ file.kind,
758
+ existingContent,
759
+ file.content,
760
+ policy
761
+ );
762
+ }
763
+
8
764
  // src/utils/slugify.ts
9
765
  function slugify(input) {
10
766
  return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
@@ -781,6 +1537,12 @@ var ATHENA_DIRECT_TARGETS = {
781
1537
  };
782
1538
  var DEFAULT_OUTPUT_FORMAT = "table-builder";
783
1539
  var DEFAULT_OUTPUT_PRESET = "athena-direct";
1540
+ var DEFAULT_ARTIFACT_WRITE_POLICY = "merge";
1541
+ var ARTIFACT_WRITE_POLICIES = /* @__PURE__ */ new Set([
1542
+ "merge",
1543
+ "skip",
1544
+ "overwrite"
1545
+ ]);
784
1546
  var DEFAULT_NAMING = {
785
1547
  modelType: "pascal",
786
1548
  modelConst: "camel",
@@ -1060,6 +1822,23 @@ function normalizeFilterConfig(input) {
1060
1822
  excludeTables: normalizeTableSelection(input?.excludeTables)
1061
1823
  };
1062
1824
  }
1825
+ function normalizeArtifactWritePolicy(value, fieldName) {
1826
+ if (value === void 0 || value === null || value === "") {
1827
+ return DEFAULT_ARTIFACT_WRITE_POLICY;
1828
+ }
1829
+ if (typeof value === "string" && ARTIFACT_WRITE_POLICIES.has(value)) {
1830
+ return value;
1831
+ }
1832
+ throw new Error(
1833
+ `Invalid output.artifactWrite.${fieldName}: expected one of merge | skip | overwrite, received ${JSON.stringify(value)}.`
1834
+ );
1835
+ }
1836
+ function normalizeArtifactWriteConfig(input) {
1837
+ return {
1838
+ database: normalizeArtifactWritePolicy(input?.database, "database"),
1839
+ registry: normalizeArtifactWritePolicy(input?.registry, "registry")
1840
+ };
1841
+ }
1063
1842
  function normalizeOutputConfig(output) {
1064
1843
  const preset = output?.preset ?? DEFAULT_OUTPUT_PRESET;
1065
1844
  return {
@@ -1071,7 +1850,8 @@ function normalizeOutputConfig(output) {
1071
1850
  },
1072
1851
  placeholderMap: {
1073
1852
  ...output?.placeholderMap ?? {}
1074
- }
1853
+ },
1854
+ artifactWrite: normalizeArtifactWriteConfig(output?.artifactWrite)
1075
1855
  };
1076
1856
  }
1077
1857
  function normalizeProviderConfig(provider) {
@@ -3288,7 +4068,7 @@ function buildAthenaGatewayUrl(baseUrl, path) {
3288
4068
 
3289
4069
  // package.json
3290
4070
  var package_default = {
3291
- version: "3.0.2"
4071
+ version: "3.0.3"
3292
4072
  };
3293
4073
 
3294
4074
  // src/sdk-version.ts
@@ -11743,9 +12523,6 @@ function resolveGeneratorProvider(providerConfig, experimentalFlags) {
11743
12523
  }
11744
12524
 
11745
12525
  // src/generator/pipeline.ts
11746
- function canOverwriteArtifact(file) {
11747
- return file.kind === "model" || file.kind === "schema";
11748
- }
11749
12526
  async function fileExists(path) {
11750
12527
  try {
11751
12528
  await stat(path);
@@ -11754,25 +12531,70 @@ async function fileExists(path) {
11754
12531
  return false;
11755
12532
  }
11756
12533
  }
11757
- async function writeArtifacts(files, cwd) {
12534
+ async function readExisting(path) {
12535
+ try {
12536
+ return await readFile(path, "utf8");
12537
+ } catch {
12538
+ return null;
12539
+ }
12540
+ }
12541
+ function policyForArtifact(file, config) {
12542
+ if (file.kind === "model" || file.kind === "schema") {
12543
+ return "always";
12544
+ }
12545
+ if (file.kind === "database") {
12546
+ return config.output.artifactWrite.database;
12547
+ }
12548
+ return config.output.artifactWrite.registry;
12549
+ }
12550
+ async function writeArtifacts(files, cwd, config, dryRun) {
11758
12551
  const writtenFiles = [];
12552
+ const writtenDetails = [];
11759
12553
  const skippedFiles = [];
11760
12554
  for (const file of files) {
11761
12555
  const absolutePath = resolve(cwd, file.path);
11762
- if (!canOverwriteArtifact(file) && await fileExists(absolutePath)) {
12556
+ const exists = await fileExists(absolutePath);
12557
+ const existingContent = exists ? await readExisting(absolutePath) : null;
12558
+ const policy = policyForArtifact(file, config);
12559
+ const plan = resolveArtifactWritePlan(file, existingContent, policy);
12560
+ if (plan.action === "skip" || plan.action === "unchanged") {
11763
12561
  skippedFiles.push({
11764
12562
  kind: file.kind,
11765
12563
  path: file.path,
11766
- reason: "protected-existing-file"
12564
+ reason: plan.skipReason ?? "already-current",
12565
+ detail: plan.detail,
12566
+ preservedCustom: plan.preservedCustom.length > 0 ? plan.preservedCustom : void 0,
12567
+ conflicts: plan.conflicts.length > 0 ? plan.conflicts : void 0,
12568
+ lintErrors: plan.lintErrors.length > 0 ? plan.lintErrors : void 0
11767
12569
  });
11768
12570
  continue;
11769
12571
  }
11770
- await mkdir(dirname(absolutePath), { recursive: true });
11771
- await writeFile(absolutePath, file.content, "utf8");
12572
+ if (!plan.content || !plan.writeReason) {
12573
+ skippedFiles.push({
12574
+ kind: file.kind,
12575
+ path: file.path,
12576
+ reason: "merge-lint-failed",
12577
+ detail: "merge produced no content",
12578
+ lintErrors: plan.lintErrors
12579
+ });
12580
+ continue;
12581
+ }
12582
+ if (!dryRun) {
12583
+ await mkdir(dirname(absolutePath), { recursive: true });
12584
+ await writeFile(absolutePath, plan.content, "utf8");
12585
+ }
11772
12586
  writtenFiles.push(file.path);
12587
+ writtenDetails.push({
12588
+ kind: file.kind,
12589
+ path: file.path,
12590
+ reason: plan.writeReason,
12591
+ added: plan.added.length > 0 ? plan.added : void 0,
12592
+ preservedCustom: plan.preservedCustom.length > 0 ? plan.preservedCustom : void 0
12593
+ });
11773
12594
  }
11774
12595
  return {
11775
12596
  writtenFiles,
12597
+ writtenDetails,
11776
12598
  skippedFiles
11777
12599
  };
11778
12600
  }
@@ -11788,12 +12610,18 @@ async function runSchemaGenerator(options = {}) {
11788
12610
  schemas: resolveProviderSchemas(config.provider)
11789
12611
  });
11790
12612
  const generated = generateArtifactsFromSnapshot(snapshot, config);
11791
- const writeResult = options.dryRun ? { writtenFiles: [], skippedFiles: [] } : await writeArtifacts(generated.files, cwd);
12613
+ const writeResult = await writeArtifacts(
12614
+ generated.files,
12615
+ cwd,
12616
+ config,
12617
+ options.dryRun === true
12618
+ );
11792
12619
  return {
11793
12620
  ...generated,
11794
12621
  configPath,
11795
12622
  config,
11796
12623
  writtenFiles: writeResult.writtenFiles,
12624
+ writtenDetails: writeResult.writtenDetails,
11797
12625
  skippedFiles: writeResult.skippedFiles
11798
12626
  };
11799
12627
  }
@@ -12083,10 +12911,52 @@ function logCliError(error, errorLog = console.error) {
12083
12911
  }
12084
12912
  function formatSkippedArtifactLine(artifact) {
12085
12913
  if (artifact.reason === "protected-existing-file") {
12086
- return ` [skip] ${artifact.path} (existing ${artifact.kind} artifacts are protected from overwrite; delete or retarget the file to regenerate it)`;
12914
+ return ` [skip] ${artifact.path} (existing ${artifact.kind} artifacts are protected from overwrite; set output.artifactWrite.${artifact.kind}="merge"|"overwrite" or delete/retarget the file)`;
12915
+ }
12916
+ if (artifact.reason === "already-current") {
12917
+ const custom = artifact.preservedCustom && artifact.preservedCustom.length > 0 ? `; preserves ${artifact.preservedCustom.length} non-generated unit(s)` : "";
12918
+ return ` [ok] ${artifact.path} (already current${custom})`;
12919
+ }
12920
+ if (artifact.reason === "merge-conflict") {
12921
+ return ` [skip] ${artifact.path} (merge conflict: ${artifact.detail ?? "see conflicts"}; file left unchanged)`;
12922
+ }
12923
+ if (artifact.reason === "merge-lint-failed") {
12924
+ return ` [skip] ${artifact.path} (merge lint failed: ${artifact.detail ?? "invalid merged TypeScript"}; file left unchanged)`;
12925
+ }
12926
+ if (artifact.reason === "merge-unparseable") {
12927
+ return ` [skip] ${artifact.path} (existing ${artifact.kind} artifact is not mergeable; delete, retarget, or set output.artifactWrite.${artifact.kind}="overwrite")`;
12087
12928
  }
12088
12929
  return ` [skip] ${artifact.path}`;
12089
12930
  }
12931
+ function formatWrittenArtifactLine(artifact) {
12932
+ if (artifact.reason === "merged") {
12933
+ const added = artifact.added && artifact.added.length > 0 ? ` +${artifact.added.length}: ${artifact.added.slice(0, 4).join(", ")}${artifact.added.length > 4 ? "\u2026" : ""}` : "";
12934
+ const custom = artifact.preservedCustom && artifact.preservedCustom.length > 0 ? `; preserves ${artifact.preservedCustom.length} non-generated unit(s)` : "";
12935
+ return ` [merge] ${artifact.path}${added}${custom}`;
12936
+ }
12937
+ if (artifact.reason === "overwritten") {
12938
+ return ` [write] ${artifact.path} (overwritten)`;
12939
+ }
12940
+ return ` - ${artifact.path}`;
12941
+ }
12942
+ function formatCustomPreserveWarnings(result) {
12943
+ const lines = [];
12944
+ for (const artifact of result.writtenDetails) {
12945
+ if (artifact.preservedCustom && artifact.preservedCustom.length > 0) {
12946
+ lines.push(
12947
+ ` [warn] ${artifact.path} preserves non-generated unit(s): ${artifact.preservedCustom.slice(0, 3).join("; ")}${artifact.preservedCustom.length > 3 ? "\u2026" : ""}`
12948
+ );
12949
+ }
12950
+ }
12951
+ for (const artifact of result.skippedFiles) {
12952
+ if (artifact.reason === "already-current" && artifact.preservedCustom && artifact.preservedCustom.length > 0) {
12953
+ lines.push(
12954
+ ` [warn] ${artifact.path} preserves non-generated unit(s): ${artifact.preservedCustom.slice(0, 3).join("; ")}${artifact.preservedCustom.length > 3 ? "\u2026" : ""}`
12955
+ );
12956
+ }
12957
+ }
12958
+ return lines;
12959
+ }
12090
12960
  async function runCLI(argv, runtime = {}) {
12091
12961
  const log = runtime.log ?? console.log;
12092
12962
  const errorLog = runtime.errorLog ?? console.error;
@@ -12128,18 +12998,44 @@ async function runCLI(argv, runtime = {}) {
12128
12998
  for (const file of result.files) {
12129
12999
  log(` - ${file.path}`);
12130
13000
  }
13001
+ if (result.writtenDetails?.length || result.skippedFiles?.length) {
13002
+ for (const artifact of result.writtenDetails ?? []) {
13003
+ if (artifact.kind === "database" || artifact.kind === "registry") {
13004
+ log(formatWrittenArtifactLine(artifact));
13005
+ }
13006
+ }
13007
+ for (const artifact of result.skippedFiles ?? []) {
13008
+ if (artifact.kind === "database" || artifact.kind === "registry") {
13009
+ log(formatSkippedArtifactLine(artifact));
13010
+ }
13011
+ }
13012
+ for (const line of formatCustomPreserveWarnings(result)) {
13013
+ log(line);
13014
+ }
13015
+ }
12131
13016
  return;
12132
13017
  }
12133
13018
  log(`Generated ${result.writtenFiles.length} files from ${result.configPath}`);
12134
13019
  for (const line of formatGeneratorModeLines(result)) {
12135
13020
  log(line);
12136
13021
  }
13022
+ const detailByPath = new Map(
13023
+ (result.writtenDetails ?? []).map((detail) => [detail.path, detail])
13024
+ );
12137
13025
  for (const filePath of result.writtenFiles) {
12138
- log(` - ${filePath}`);
13026
+ const detail = detailByPath.get(filePath);
13027
+ if (detail && (detail.reason === "merged" || detail.reason === "overwritten")) {
13028
+ log(formatWrittenArtifactLine(detail));
13029
+ } else {
13030
+ log(` - ${filePath}`);
13031
+ }
12139
13032
  }
12140
13033
  for (const artifact of result.skippedFiles) {
12141
13034
  log(formatSkippedArtifactLine(artifact));
12142
13035
  }
13036
+ for (const line of formatCustomPreserveWarnings(result)) {
13037
+ log(line);
13038
+ }
12143
13039
  }
12144
13040
 
12145
13041
  export { logCliError, parseCommand, runCLI, usage };