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