@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/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { existsSync, readFileSync } from 'fs';
|
|
3
3
|
import { resolve, dirname, posix } from 'path';
|
|
4
4
|
import { pathToFileURL } from 'url';
|
|
5
|
-
import { mkdir, writeFile, stat } from 'fs/promises';
|
|
5
|
+
import { mkdir, writeFile, stat, readFile } from 'fs/promises';
|
|
6
6
|
|
|
7
7
|
// src/gateway/errors.ts
|
|
8
8
|
var AthenaGatewayError = class _AthenaGatewayError extends Error {
|
|
@@ -2007,7 +2007,7 @@ function buildAthenaGatewayUrl(baseUrl, path) {
|
|
|
2007
2007
|
|
|
2008
2008
|
// package.json
|
|
2009
2009
|
var package_default = {
|
|
2010
|
-
version: "3.0.
|
|
2010
|
+
version: "3.0.3"
|
|
2011
2011
|
};
|
|
2012
2012
|
|
|
2013
2013
|
// src/sdk-version.ts
|
|
@@ -11717,6 +11717,12 @@ var ATHENA_DIRECT_TARGETS = {
|
|
|
11717
11717
|
};
|
|
11718
11718
|
var DEFAULT_OUTPUT_FORMAT = "table-builder";
|
|
11719
11719
|
var DEFAULT_OUTPUT_PRESET = "athena-direct";
|
|
11720
|
+
var DEFAULT_ARTIFACT_WRITE_POLICY = "merge";
|
|
11721
|
+
var ARTIFACT_WRITE_POLICIES = /* @__PURE__ */ new Set([
|
|
11722
|
+
"merge",
|
|
11723
|
+
"skip",
|
|
11724
|
+
"overwrite"
|
|
11725
|
+
]);
|
|
11720
11726
|
var DEFAULT_NAMING = {
|
|
11721
11727
|
modelType: "pascal",
|
|
11722
11728
|
modelConst: "camel",
|
|
@@ -11996,6 +12002,23 @@ function normalizeFilterConfig(input) {
|
|
|
11996
12002
|
excludeTables: normalizeTableSelection(input?.excludeTables)
|
|
11997
12003
|
};
|
|
11998
12004
|
}
|
|
12005
|
+
function normalizeArtifactWritePolicy(value, fieldName) {
|
|
12006
|
+
if (value === void 0 || value === null || value === "") {
|
|
12007
|
+
return DEFAULT_ARTIFACT_WRITE_POLICY;
|
|
12008
|
+
}
|
|
12009
|
+
if (typeof value === "string" && ARTIFACT_WRITE_POLICIES.has(value)) {
|
|
12010
|
+
return value;
|
|
12011
|
+
}
|
|
12012
|
+
throw new Error(
|
|
12013
|
+
`Invalid output.artifactWrite.${fieldName}: expected one of merge | skip | overwrite, received ${JSON.stringify(value)}.`
|
|
12014
|
+
);
|
|
12015
|
+
}
|
|
12016
|
+
function normalizeArtifactWriteConfig(input) {
|
|
12017
|
+
return {
|
|
12018
|
+
database: normalizeArtifactWritePolicy(input?.database, "database"),
|
|
12019
|
+
registry: normalizeArtifactWritePolicy(input?.registry, "registry")
|
|
12020
|
+
};
|
|
12021
|
+
}
|
|
11999
12022
|
function normalizeOutputConfig(output) {
|
|
12000
12023
|
const preset = output?.preset ?? DEFAULT_OUTPUT_PRESET;
|
|
12001
12024
|
return {
|
|
@@ -12007,7 +12030,8 @@ function normalizeOutputConfig(output) {
|
|
|
12007
12030
|
},
|
|
12008
12031
|
placeholderMap: {
|
|
12009
12032
|
...output?.placeholderMap ?? {}
|
|
12010
|
-
}
|
|
12033
|
+
},
|
|
12034
|
+
artifactWrite: normalizeArtifactWriteConfig(output?.artifactWrite)
|
|
12011
12035
|
};
|
|
12012
12036
|
}
|
|
12013
12037
|
function normalizeProviderConfig(provider) {
|
|
@@ -13268,9 +13292,764 @@ function resolveGeneratorProvider(providerConfig, experimentalFlags) {
|
|
|
13268
13292
|
}
|
|
13269
13293
|
throw new Error(`Unsupported generator provider kind: ${providerConfig.kind ?? "unknown"}`);
|
|
13270
13294
|
}
|
|
13271
|
-
|
|
13272
|
-
|
|
13295
|
+
|
|
13296
|
+
// src/generator/artifact-merge.ts
|
|
13297
|
+
var IMPORT_RE = /^import\s+(?:type\s+)?(?:\{([^}]*)\}|([A-Za-z_$][\w$]*))\s+from\s+(['"])([^'"]+)\3\s*;?\s*$/gm;
|
|
13298
|
+
var DEFINE_EXPORT_RE = /export\s+const\s+([A-Za-z_$][\w$]*)\s*=\s*(define(?:Database|Registry))\s*\(\s*\{/g;
|
|
13299
|
+
var META_EXPORT_RE = /export\s+const\s+__athena_schema_meta\s*=\s*\{/g;
|
|
13300
|
+
var KNOWN_META_KEYS = /* @__PURE__ */ new Set([
|
|
13301
|
+
"schemaVersion",
|
|
13302
|
+
"generatedAt",
|
|
13303
|
+
"database",
|
|
13304
|
+
"outputPreset",
|
|
13305
|
+
"outputFormat"
|
|
13306
|
+
]);
|
|
13307
|
+
function detectNewline(source) {
|
|
13308
|
+
return source.includes("\r\n") ? "\r\n" : "\n";
|
|
13309
|
+
}
|
|
13310
|
+
function detectStyle(source) {
|
|
13311
|
+
const newline = detectNewline(source);
|
|
13312
|
+
const single = (source.match(/'/g) ?? []).length;
|
|
13313
|
+
const double = (source.match(/"/g) ?? []).length;
|
|
13314
|
+
const quote = double > single ? '"' : "'";
|
|
13315
|
+
const importLines = source.split(/\r?\n/).filter((line) => line.trimStart().startsWith("import "));
|
|
13316
|
+
const semicolons = importLines.length > 0 ? importLines.filter((line) => line.trimEnd().endsWith(";")).length >= importLines.length / 2 : false;
|
|
13317
|
+
const objectLines = source.split(/\r?\n/).map((line) => line.trim()).filter((line) => /^[A-Za-z0-9_'"]+\s*:\s*.+/.test(line));
|
|
13318
|
+
const trailingComma = objectLines.length > 0 ? objectLines.filter((line) => line.endsWith(",")).length >= Math.ceil(objectLines.length / 2) : true;
|
|
13319
|
+
const indentMatch = source.match(/\n([ \t]+)\S/);
|
|
13320
|
+
const indent = indentMatch?.[1] ?? " ";
|
|
13321
|
+
return { quote, semicolons, trailingComma, indent, newline };
|
|
13322
|
+
}
|
|
13323
|
+
function quoteString(value, quote) {
|
|
13324
|
+
const escaped = value.replace(/\\/g, "\\\\").replace(new RegExp(quote, "g"), `\\${quote}`);
|
|
13325
|
+
return `${quote}${escaped}${quote}`;
|
|
13326
|
+
}
|
|
13327
|
+
function parseNamedImports(source) {
|
|
13328
|
+
const imports = [];
|
|
13329
|
+
IMPORT_RE.lastIndex = 0;
|
|
13330
|
+
let match;
|
|
13331
|
+
while ((match = IMPORT_RE.exec(source)) !== null) {
|
|
13332
|
+
const named = match[1];
|
|
13333
|
+
const defaultName = match[2];
|
|
13334
|
+
const module = match[4];
|
|
13335
|
+
const names = named ? named.split(",").map((part) => part.trim()).filter(Boolean).map((part) => {
|
|
13336
|
+
const alias = part.split(/\s+as\s+/);
|
|
13337
|
+
return (alias[1] ?? alias[0]).trim();
|
|
13338
|
+
}) : defaultName ? [defaultName] : [];
|
|
13339
|
+
imports.push({
|
|
13340
|
+
names,
|
|
13341
|
+
module,
|
|
13342
|
+
raw: match[0],
|
|
13343
|
+
start: match.index,
|
|
13344
|
+
end: match.index + match[0].length
|
|
13345
|
+
});
|
|
13346
|
+
}
|
|
13347
|
+
return imports;
|
|
13348
|
+
}
|
|
13349
|
+
function findMatchingBrace(source, openIndex) {
|
|
13350
|
+
let depth = 0;
|
|
13351
|
+
let inSingle = false;
|
|
13352
|
+
let inDouble = false;
|
|
13353
|
+
let inTemplate = false;
|
|
13354
|
+
let escaped = false;
|
|
13355
|
+
for (let i = openIndex; i < source.length; i += 1) {
|
|
13356
|
+
const ch = source[i];
|
|
13357
|
+
if (escaped) {
|
|
13358
|
+
escaped = false;
|
|
13359
|
+
continue;
|
|
13360
|
+
}
|
|
13361
|
+
if (ch === "\\" && (inSingle || inDouble || inTemplate)) {
|
|
13362
|
+
escaped = true;
|
|
13363
|
+
continue;
|
|
13364
|
+
}
|
|
13365
|
+
if (!inDouble && !inTemplate && ch === "'") {
|
|
13366
|
+
inSingle = !inSingle;
|
|
13367
|
+
continue;
|
|
13368
|
+
}
|
|
13369
|
+
if (!inSingle && !inTemplate && ch === '"') {
|
|
13370
|
+
inDouble = !inDouble;
|
|
13371
|
+
continue;
|
|
13372
|
+
}
|
|
13373
|
+
if (!inSingle && !inDouble && ch === "`") {
|
|
13374
|
+
inTemplate = !inTemplate;
|
|
13375
|
+
continue;
|
|
13376
|
+
}
|
|
13377
|
+
if (inSingle || inDouble || inTemplate) {
|
|
13378
|
+
continue;
|
|
13379
|
+
}
|
|
13380
|
+
if (ch === "{") {
|
|
13381
|
+
depth += 1;
|
|
13382
|
+
} else if (ch === "}") {
|
|
13383
|
+
depth -= 1;
|
|
13384
|
+
if (depth === 0) {
|
|
13385
|
+
return i;
|
|
13386
|
+
}
|
|
13387
|
+
}
|
|
13388
|
+
}
|
|
13389
|
+
return -1;
|
|
13390
|
+
}
|
|
13391
|
+
function parseObjectEntries(body) {
|
|
13392
|
+
const entries = [];
|
|
13393
|
+
const lines = body.split(/\r?\n/);
|
|
13394
|
+
for (const line of lines) {
|
|
13395
|
+
const trimmed = line.trim();
|
|
13396
|
+
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("/*")) {
|
|
13397
|
+
continue;
|
|
13398
|
+
}
|
|
13399
|
+
const withoutComma = trimmed.endsWith(",") ? trimmed.slice(0, -1).trimEnd() : trimmed;
|
|
13400
|
+
const match = withoutComma.match(/^([A-Za-z_$][\w$]*|['"][^'"]+['"])\s*:\s*(.+)$/);
|
|
13401
|
+
if (!match) {
|
|
13402
|
+
continue;
|
|
13403
|
+
}
|
|
13404
|
+
const keyRaw = match[1];
|
|
13405
|
+
const key = keyRaw.startsWith("'") || keyRaw.startsWith('"') ? keyRaw.slice(1, -1) : keyRaw;
|
|
13406
|
+
entries.push({
|
|
13407
|
+
key,
|
|
13408
|
+
value: match[2].trim(),
|
|
13409
|
+
raw: trimmed
|
|
13410
|
+
});
|
|
13411
|
+
}
|
|
13412
|
+
return entries;
|
|
13413
|
+
}
|
|
13414
|
+
function parseDefineBlocks(source) {
|
|
13415
|
+
const blocks = [];
|
|
13416
|
+
DEFINE_EXPORT_RE.lastIndex = 0;
|
|
13417
|
+
let match;
|
|
13418
|
+
while ((match = DEFINE_EXPORT_RE.exec(source)) !== null) {
|
|
13419
|
+
const exportName = match[1];
|
|
13420
|
+
const callName = match[2];
|
|
13421
|
+
const openBrace = match.index + match[0].lastIndexOf("{");
|
|
13422
|
+
const closeBrace = findMatchingBrace(source, openBrace);
|
|
13423
|
+
if (closeBrace < 0) {
|
|
13424
|
+
continue;
|
|
13425
|
+
}
|
|
13426
|
+
const body = source.slice(openBrace + 1, closeBrace);
|
|
13427
|
+
const fullEnd = (() => {
|
|
13428
|
+
let i = closeBrace + 1;
|
|
13429
|
+
while (i < source.length && /\s/.test(source[i])) i += 1;
|
|
13430
|
+
if (source[i] === ")") i += 1;
|
|
13431
|
+
while (i < source.length && /\s/.test(source[i])) i += 1;
|
|
13432
|
+
if (source[i] === ";") i += 1;
|
|
13433
|
+
return i;
|
|
13434
|
+
})();
|
|
13435
|
+
blocks.push({
|
|
13436
|
+
kind: callName === "defineDatabase" ? "database" : "registry",
|
|
13437
|
+
exportName,
|
|
13438
|
+
callName,
|
|
13439
|
+
entries: parseObjectEntries(body),
|
|
13440
|
+
bodyStart: openBrace + 1,
|
|
13441
|
+
bodyEnd: closeBrace,
|
|
13442
|
+
fullStart: match.index,
|
|
13443
|
+
fullEnd,
|
|
13444
|
+
raw: source.slice(match.index, fullEnd)
|
|
13445
|
+
});
|
|
13446
|
+
}
|
|
13447
|
+
return blocks;
|
|
13448
|
+
}
|
|
13449
|
+
function parseMetaBlock(source) {
|
|
13450
|
+
META_EXPORT_RE.lastIndex = 0;
|
|
13451
|
+
const match = META_EXPORT_RE.exec(source);
|
|
13452
|
+
if (!match) {
|
|
13453
|
+
return void 0;
|
|
13454
|
+
}
|
|
13455
|
+
const openBrace = match.index + match[0].lastIndexOf("{");
|
|
13456
|
+
const closeBrace = findMatchingBrace(source, openBrace);
|
|
13457
|
+
if (closeBrace < 0) {
|
|
13458
|
+
return void 0;
|
|
13459
|
+
}
|
|
13460
|
+
let fullEnd = closeBrace + 1;
|
|
13461
|
+
const after = source.slice(fullEnd);
|
|
13462
|
+
const asConst = after.match(/^\s*as\s+const\s*;?/);
|
|
13463
|
+
if (asConst) {
|
|
13464
|
+
fullEnd += asConst[0].length;
|
|
13465
|
+
} else if (source[fullEnd] === ";") {
|
|
13466
|
+
fullEnd += 1;
|
|
13467
|
+
}
|
|
13468
|
+
return {
|
|
13469
|
+
entries: parseObjectEntries(source.slice(openBrace + 1, closeBrace)),
|
|
13470
|
+
bodyStart: openBrace + 1,
|
|
13471
|
+
bodyEnd: closeBrace,
|
|
13472
|
+
fullStart: match.index,
|
|
13473
|
+
fullEnd,
|
|
13474
|
+
raw: source.slice(match.index, fullEnd)
|
|
13475
|
+
};
|
|
13476
|
+
}
|
|
13477
|
+
function hasImportBinding(imports, name, module) {
|
|
13478
|
+
return imports.some(
|
|
13479
|
+
(item) => item.names.includes(name) && (module === void 0)
|
|
13480
|
+
);
|
|
13481
|
+
}
|
|
13482
|
+
function findImportForName(imports, name) {
|
|
13483
|
+
return imports.find((item) => item.names.includes(name));
|
|
13484
|
+
}
|
|
13485
|
+
function normalizeModulePath(modulePath) {
|
|
13486
|
+
return modulePath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\.ts$/, "");
|
|
13487
|
+
}
|
|
13488
|
+
function formatImport(names, modulePath, style) {
|
|
13489
|
+
const body = `import { ${names.join(", ")} } from ${quoteString(modulePath, style.quote)}`;
|
|
13490
|
+
return style.semicolons ? `${body};` : body;
|
|
13491
|
+
}
|
|
13492
|
+
function formatObjectEntry(key, value, style, isLast) {
|
|
13493
|
+
const needsQuote = !/^[A-Za-z_$][\w$]*$/.test(key);
|
|
13494
|
+
const renderedKey = needsQuote ? quoteString(key, style.quote) : key;
|
|
13495
|
+
const comma = !isLast || style.trailingComma ? "," : "";
|
|
13496
|
+
return `${style.indent}${renderedKey}: ${value}${comma}`;
|
|
13497
|
+
}
|
|
13498
|
+
function replaceRange(source, start, end, insertion) {
|
|
13499
|
+
return source.slice(0, start) + insertion + source.slice(end);
|
|
13500
|
+
}
|
|
13501
|
+
function collectDuplicateKeys(entries) {
|
|
13502
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13503
|
+
const dupes = [];
|
|
13504
|
+
for (const entry of entries) {
|
|
13505
|
+
if (seen.has(entry.key)) {
|
|
13506
|
+
dupes.push(entry.key);
|
|
13507
|
+
}
|
|
13508
|
+
seen.add(entry.key);
|
|
13509
|
+
}
|
|
13510
|
+
return dupes;
|
|
13511
|
+
}
|
|
13512
|
+
function collectDuplicateImportBindings(imports) {
|
|
13513
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13514
|
+
const dupes = [];
|
|
13515
|
+
for (const item of imports) {
|
|
13516
|
+
for (const name of item.names) {
|
|
13517
|
+
if (seen.has(name)) {
|
|
13518
|
+
dupes.push(name);
|
|
13519
|
+
}
|
|
13520
|
+
seen.add(name);
|
|
13521
|
+
}
|
|
13522
|
+
}
|
|
13523
|
+
return dupes;
|
|
13524
|
+
}
|
|
13525
|
+
function lintArtifactSource(source, kind) {
|
|
13526
|
+
const errors = [];
|
|
13527
|
+
const imports = parseNamedImports(source);
|
|
13528
|
+
const dupImports = collectDuplicateImportBindings(imports);
|
|
13529
|
+
if (dupImports.length > 0) {
|
|
13530
|
+
errors.push(`duplicate import bindings: ${dupImports.join(", ")}`);
|
|
13531
|
+
}
|
|
13532
|
+
const blocks = parseDefineBlocks(source).filter((block2) => block2.kind === kind);
|
|
13533
|
+
if (blocks.length === 0) {
|
|
13534
|
+
errors.push(`missing export const \u2026 = define${kind === "database" ? "Database" : "Registry"}({\u2026})`);
|
|
13535
|
+
return errors;
|
|
13536
|
+
}
|
|
13537
|
+
if (blocks.length > 1) {
|
|
13538
|
+
errors.push(`multiple define${kind === "database" ? "Database" : "Registry"} exports found`);
|
|
13539
|
+
}
|
|
13540
|
+
const block = blocks[0];
|
|
13541
|
+
const dupKeys = collectDuplicateKeys(block.entries);
|
|
13542
|
+
if (dupKeys.length > 0) {
|
|
13543
|
+
errors.push(`duplicate object keys: ${dupKeys.join(", ")}`);
|
|
13544
|
+
}
|
|
13545
|
+
for (const entry of block.entries) {
|
|
13546
|
+
const valueId = entry.value.match(/^[A-Za-z_$][\w$]*$/)?.[0];
|
|
13547
|
+
if (valueId && !hasImportBinding(imports, valueId) && valueId !== block.exportName) {
|
|
13548
|
+
if (!source.includes(`const ${valueId}`) && !source.includes(`function ${valueId}`)) {
|
|
13549
|
+
errors.push(`value "${valueId}" for key "${entry.key}" is not imported`);
|
|
13550
|
+
}
|
|
13551
|
+
}
|
|
13552
|
+
}
|
|
13553
|
+
if (kind === "database" && !hasImportBinding(imports, "defineDatabase")) {
|
|
13554
|
+
errors.push("missing defineDatabase import");
|
|
13555
|
+
}
|
|
13556
|
+
if (kind === "registry" && !hasImportBinding(imports, "defineRegistry")) {
|
|
13557
|
+
errors.push("missing defineRegistry import");
|
|
13558
|
+
}
|
|
13559
|
+
return errors;
|
|
13560
|
+
}
|
|
13561
|
+
function preservedCustomUnits(existing, generated, kind) {
|
|
13562
|
+
const custom = [];
|
|
13563
|
+
const existingImports = parseNamedImports(existing);
|
|
13564
|
+
const generatedImports = parseNamedImports(generated);
|
|
13565
|
+
const generatedModules = new Set(generatedImports.map((item) => normalizeModulePath(item.module)));
|
|
13566
|
+
const generatedNames = new Set(generatedImports.flatMap((item) => item.names));
|
|
13567
|
+
for (const item of existingImports) {
|
|
13568
|
+
const moduleNorm = normalizeModulePath(item.module);
|
|
13569
|
+
if (moduleNorm.includes("@xylex-group/athena")) {
|
|
13570
|
+
continue;
|
|
13571
|
+
}
|
|
13572
|
+
const unexpectedNames = item.names.filter((name) => !generatedNames.has(name));
|
|
13573
|
+
if (unexpectedNames.length > 0 && !generatedModules.has(moduleNorm)) {
|
|
13574
|
+
custom.push(`import { ${unexpectedNames.join(", ")} } from '${item.module}'`);
|
|
13575
|
+
} else if (unexpectedNames.length > 0) {
|
|
13576
|
+
custom.push(`import binding(s): ${unexpectedNames.join(", ")}`);
|
|
13577
|
+
}
|
|
13578
|
+
}
|
|
13579
|
+
const existingBlocks = parseDefineBlocks(existing).filter((block) => block.kind === kind);
|
|
13580
|
+
const generatedBlocks = parseDefineBlocks(generated).filter((block) => block.kind === kind);
|
|
13581
|
+
const generatedKeys = new Set(generatedBlocks[0]?.entries.map((entry) => entry.key) ?? []);
|
|
13582
|
+
for (const entry of existingBlocks[0]?.entries ?? []) {
|
|
13583
|
+
if (!generatedKeys.has(entry.key)) {
|
|
13584
|
+
custom.push(`${kind} entry: ${entry.key}: ${entry.value}`);
|
|
13585
|
+
}
|
|
13586
|
+
}
|
|
13587
|
+
if (kind === "registry") {
|
|
13588
|
+
const existingMeta = parseMetaBlock(existing);
|
|
13589
|
+
const generatedMeta = parseMetaBlock(generated);
|
|
13590
|
+
const generatedMetaKeys = new Set(generatedMeta?.entries.map((entry) => entry.key) ?? []);
|
|
13591
|
+
for (const entry of existingMeta?.entries ?? []) {
|
|
13592
|
+
if (!generatedMetaKeys.has(entry.key) && !KNOWN_META_KEYS.has(entry.key)) {
|
|
13593
|
+
custom.push(`meta entry: ${entry.key}: ${entry.value}`);
|
|
13594
|
+
}
|
|
13595
|
+
}
|
|
13596
|
+
}
|
|
13597
|
+
const exportConstRe = /^export\s+const\s+([A-Za-z_$][\w$]*)\b/gm;
|
|
13598
|
+
const generatedExports = /* @__PURE__ */ new Set();
|
|
13599
|
+
let match;
|
|
13600
|
+
exportConstRe.lastIndex = 0;
|
|
13601
|
+
while ((match = exportConstRe.exec(generated)) !== null) {
|
|
13602
|
+
generatedExports.add(match[1]);
|
|
13603
|
+
}
|
|
13604
|
+
exportConstRe.lastIndex = 0;
|
|
13605
|
+
while ((match = exportConstRe.exec(existing)) !== null) {
|
|
13606
|
+
if (!generatedExports.has(match[1]) && match[1] !== "__athena_schema_meta") {
|
|
13607
|
+
custom.push(`export const ${match[1]}`);
|
|
13608
|
+
}
|
|
13609
|
+
}
|
|
13610
|
+
return custom;
|
|
13611
|
+
}
|
|
13612
|
+
function insertImportAfterPackageImports(source, importLine, style) {
|
|
13613
|
+
const imports = parseNamedImports(source);
|
|
13614
|
+
if (imports.length === 0) {
|
|
13615
|
+
return `${importLine}${style.newline}${source}`;
|
|
13616
|
+
}
|
|
13617
|
+
let anchor = imports[imports.length - 1];
|
|
13618
|
+
for (let i = imports.length - 1; i >= 0; i -= 1) {
|
|
13619
|
+
if (imports[i].module.startsWith(".")) {
|
|
13620
|
+
anchor = imports[i];
|
|
13621
|
+
break;
|
|
13622
|
+
}
|
|
13623
|
+
}
|
|
13624
|
+
const insertAt = anchor.end;
|
|
13625
|
+
const before = source.slice(0, insertAt);
|
|
13626
|
+
const after = source.slice(insertAt);
|
|
13627
|
+
const needsLeadingNl = !before.endsWith("\n");
|
|
13628
|
+
const prefix = needsLeadingNl ? style.newline : "";
|
|
13629
|
+
return `${before}${prefix}${importLine}${after.startsWith("\n") || after.startsWith("\r\n") ? "" : style.newline}${after}`;
|
|
13630
|
+
}
|
|
13631
|
+
function rewriteObjectBody(entries, style) {
|
|
13632
|
+
if (entries.length === 0) {
|
|
13633
|
+
return style.newline;
|
|
13634
|
+
}
|
|
13635
|
+
const lines = entries.map(
|
|
13636
|
+
(entry, index) => formatObjectEntry(entry.key, entry.value, style, index === entries.length - 1)
|
|
13637
|
+
);
|
|
13638
|
+
return `${style.newline}${lines.join(style.newline)}${style.newline}`;
|
|
13639
|
+
}
|
|
13640
|
+
function mergeDatabaseArtifact(existing, generated) {
|
|
13641
|
+
const style = detectStyle(existing);
|
|
13642
|
+
const generatedStyle = detectStyle(generated);
|
|
13643
|
+
const effectiveStyle = {
|
|
13644
|
+
...style,
|
|
13645
|
+
// Prefer existing fingerprint; fall back to generated if existing is empty-ish
|
|
13646
|
+
quote: existing.includes('"') || existing.includes("'") ? style.quote : generatedStyle.quote
|
|
13647
|
+
};
|
|
13648
|
+
const generatedImports = parseNamedImports(generated);
|
|
13649
|
+
const existingBlocks = parseDefineBlocks(existing).filter((block) => block.kind === "database");
|
|
13650
|
+
const generatedBlocks = parseDefineBlocks(generated).filter((block) => block.kind === "database");
|
|
13651
|
+
if (existingBlocks.length === 0 || generatedBlocks.length === 0) {
|
|
13652
|
+
return {
|
|
13653
|
+
action: "skip",
|
|
13654
|
+
skipReason: "merge-unparseable",
|
|
13655
|
+
added: [],
|
|
13656
|
+
preservedCustom: [],
|
|
13657
|
+
conflicts: [],
|
|
13658
|
+
lintErrors: [],
|
|
13659
|
+
detail: "could not locate defineDatabase({\u2026}) export for merge"
|
|
13660
|
+
};
|
|
13661
|
+
}
|
|
13662
|
+
const generatedBlock = generatedBlocks[0];
|
|
13663
|
+
const conflicts = [];
|
|
13664
|
+
const added = [];
|
|
13665
|
+
let next = existing;
|
|
13666
|
+
if (!hasImportBinding(parseNamedImports(next), "defineDatabase")) {
|
|
13667
|
+
const pkgImport = generatedImports.find((item) => item.names.includes("defineDatabase"));
|
|
13668
|
+
if (pkgImport) {
|
|
13669
|
+
const line = formatImport(["defineDatabase"], pkgImport.module, effectiveStyle);
|
|
13670
|
+
next = insertImportAfterPackageImports(next, line, effectiveStyle);
|
|
13671
|
+
added.push("import defineDatabase");
|
|
13672
|
+
}
|
|
13673
|
+
}
|
|
13674
|
+
let workingImports = parseNamedImports(next);
|
|
13675
|
+
let workingBlocks = parseDefineBlocks(next).filter((block) => block.kind === "database");
|
|
13676
|
+
let workingBlock = workingBlocks[0];
|
|
13677
|
+
const entryMap = new Map(workingBlock.entries.map((entry) => [entry.key, entry]));
|
|
13678
|
+
for (const desired of generatedBlock.entries) {
|
|
13679
|
+
const existingEntry = entryMap.get(desired.key);
|
|
13680
|
+
if (existingEntry) {
|
|
13681
|
+
if (existingEntry.value !== desired.value) {
|
|
13682
|
+
conflicts.push(
|
|
13683
|
+
`key "${desired.key}" maps to ${existingEntry.value} (existing) vs ${desired.value} (generated)`
|
|
13684
|
+
);
|
|
13685
|
+
}
|
|
13686
|
+
continue;
|
|
13687
|
+
}
|
|
13688
|
+
const desiredImport = generatedImports.find((item) => item.names.includes(desired.value));
|
|
13689
|
+
if (desiredImport) {
|
|
13690
|
+
const existingForName = findImportForName(workingImports, desired.value);
|
|
13691
|
+
if (existingForName) {
|
|
13692
|
+
if (normalizeModulePath(existingForName.module) !== normalizeModulePath(desiredImport.module)) {
|
|
13693
|
+
conflicts.push(
|
|
13694
|
+
`binding "${desired.value}" imported from '${existingForName.module}' vs '${desiredImport.module}'`
|
|
13695
|
+
);
|
|
13696
|
+
continue;
|
|
13697
|
+
}
|
|
13698
|
+
} else {
|
|
13699
|
+
const line = formatImport([desired.value], desiredImport.module, effectiveStyle);
|
|
13700
|
+
next = insertImportAfterPackageImports(next, line, effectiveStyle);
|
|
13701
|
+
added.push(`import ${desired.value}`);
|
|
13702
|
+
workingImports = parseNamedImports(next);
|
|
13703
|
+
}
|
|
13704
|
+
}
|
|
13705
|
+
workingBlocks = parseDefineBlocks(next).filter((block) => block.kind === "database");
|
|
13706
|
+
workingBlock = workingBlocks[0];
|
|
13707
|
+
const nextEntries = [...workingBlock.entries, { key: desired.key, value: desired.value, raw: "" }];
|
|
13708
|
+
const body = rewriteObjectBody(nextEntries, effectiveStyle);
|
|
13709
|
+
next = replaceRange(next, workingBlock.bodyStart, workingBlock.bodyEnd, body);
|
|
13710
|
+
entryMap.set(desired.key, { key: desired.key, value: desired.value, raw: "" });
|
|
13711
|
+
added.push(`database entry: ${desired.key}`);
|
|
13712
|
+
}
|
|
13713
|
+
if (conflicts.length > 0 && added.length === 0) {
|
|
13714
|
+
return {
|
|
13715
|
+
action: "skip",
|
|
13716
|
+
skipReason: "merge-conflict",
|
|
13717
|
+
added: [],
|
|
13718
|
+
preservedCustom: preservedCustomUnits(existing, generated, "database"),
|
|
13719
|
+
conflicts,
|
|
13720
|
+
lintErrors: [],
|
|
13721
|
+
detail: conflicts.join("; ")
|
|
13722
|
+
};
|
|
13723
|
+
}
|
|
13724
|
+
const lintErrors = lintArtifactSource(next, "database");
|
|
13725
|
+
if (lintErrors.length > 0) {
|
|
13726
|
+
return {
|
|
13727
|
+
action: "skip",
|
|
13728
|
+
skipReason: "merge-lint-failed",
|
|
13729
|
+
added,
|
|
13730
|
+
preservedCustom: preservedCustomUnits(existing, generated, "database"),
|
|
13731
|
+
conflicts,
|
|
13732
|
+
lintErrors,
|
|
13733
|
+
detail: lintErrors.join("; ")
|
|
13734
|
+
};
|
|
13735
|
+
}
|
|
13736
|
+
const preservedCustom = preservedCustomUnits(next, generated, "database");
|
|
13737
|
+
if (next === existing || next.replace(/\r\n/g, "\n") === existing.replace(/\r\n/g, "\n")) {
|
|
13738
|
+
return {
|
|
13739
|
+
action: "unchanged",
|
|
13740
|
+
content: existing,
|
|
13741
|
+
skipReason: "already-current",
|
|
13742
|
+
added: [],
|
|
13743
|
+
preservedCustom,
|
|
13744
|
+
conflicts,
|
|
13745
|
+
lintErrors: [],
|
|
13746
|
+
detail: conflicts.length > 0 ? conflicts.join("; ") : void 0
|
|
13747
|
+
};
|
|
13748
|
+
}
|
|
13749
|
+
return {
|
|
13750
|
+
action: "write",
|
|
13751
|
+
content: next.endsWith("\n") ? next : `${next}${effectiveStyle.newline}`,
|
|
13752
|
+
writeReason: "merged",
|
|
13753
|
+
added,
|
|
13754
|
+
preservedCustom,
|
|
13755
|
+
conflicts,
|
|
13756
|
+
lintErrors: []
|
|
13757
|
+
};
|
|
13758
|
+
}
|
|
13759
|
+
function mergeMetaFields(existingMeta, generatedMeta) {
|
|
13760
|
+
const added = [];
|
|
13761
|
+
if (!generatedMeta) {
|
|
13762
|
+
return { entries: existingMeta?.entries ?? [], changed: false, added };
|
|
13763
|
+
}
|
|
13764
|
+
if (!existingMeta) {
|
|
13765
|
+
return {
|
|
13766
|
+
entries: generatedMeta.entries,
|
|
13767
|
+
changed: true,
|
|
13768
|
+
added: generatedMeta.entries.map((entry) => `meta entry: ${entry.key}`)
|
|
13769
|
+
};
|
|
13770
|
+
}
|
|
13771
|
+
const map = new Map(existingMeta.entries.map((entry) => [entry.key, entry]));
|
|
13772
|
+
let changed = false;
|
|
13773
|
+
for (const desired of generatedMeta.entries) {
|
|
13774
|
+
const current = map.get(desired.key);
|
|
13775
|
+
if (!current) {
|
|
13776
|
+
map.set(desired.key, desired);
|
|
13777
|
+
added.push(`meta entry: ${desired.key}`);
|
|
13778
|
+
changed = true;
|
|
13779
|
+
continue;
|
|
13780
|
+
}
|
|
13781
|
+
if (KNOWN_META_KEYS.has(desired.key) && current.value !== desired.value) {
|
|
13782
|
+
map.set(desired.key, desired);
|
|
13783
|
+
added.push(`meta refresh: ${desired.key}`);
|
|
13784
|
+
changed = true;
|
|
13785
|
+
}
|
|
13786
|
+
}
|
|
13787
|
+
const ordered = [];
|
|
13788
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13789
|
+
for (const entry of existingMeta.entries) {
|
|
13790
|
+
const next = map.get(entry.key);
|
|
13791
|
+
if (next) {
|
|
13792
|
+
ordered.push(next);
|
|
13793
|
+
seen.add(entry.key);
|
|
13794
|
+
}
|
|
13795
|
+
}
|
|
13796
|
+
for (const entry of generatedMeta.entries) {
|
|
13797
|
+
if (!seen.has(entry.key)) {
|
|
13798
|
+
const next = map.get(entry.key);
|
|
13799
|
+
if (next) {
|
|
13800
|
+
ordered.push(next);
|
|
13801
|
+
seen.add(entry.key);
|
|
13802
|
+
}
|
|
13803
|
+
}
|
|
13804
|
+
}
|
|
13805
|
+
for (const [key, entry] of map) {
|
|
13806
|
+
if (!seen.has(key)) {
|
|
13807
|
+
ordered.push(entry);
|
|
13808
|
+
}
|
|
13809
|
+
}
|
|
13810
|
+
return { entries: ordered, changed, added };
|
|
13273
13811
|
}
|
|
13812
|
+
function mergeRegistryArtifact(existing, generated) {
|
|
13813
|
+
const style = detectStyle(existing);
|
|
13814
|
+
const effectiveStyle = style;
|
|
13815
|
+
const generatedImports = parseNamedImports(generated);
|
|
13816
|
+
const existingBlocks = parseDefineBlocks(existing).filter((block) => block.kind === "registry");
|
|
13817
|
+
const generatedBlocks = parseDefineBlocks(generated).filter((block) => block.kind === "registry");
|
|
13818
|
+
if (existingBlocks.length === 0 || generatedBlocks.length === 0) {
|
|
13819
|
+
return {
|
|
13820
|
+
action: "skip",
|
|
13821
|
+
skipReason: "merge-unparseable",
|
|
13822
|
+
added: [],
|
|
13823
|
+
preservedCustom: [],
|
|
13824
|
+
conflicts: [],
|
|
13825
|
+
lintErrors: [],
|
|
13826
|
+
detail: "could not locate defineRegistry({\u2026}) export for merge"
|
|
13827
|
+
};
|
|
13828
|
+
}
|
|
13829
|
+
const existingBlock = existingBlocks[0];
|
|
13830
|
+
const generatedBlock = generatedBlocks[0];
|
|
13831
|
+
const conflicts = [];
|
|
13832
|
+
const added = [];
|
|
13833
|
+
let next = existing;
|
|
13834
|
+
if (!hasImportBinding(parseNamedImports(next), "defineRegistry")) {
|
|
13835
|
+
const pkgImport = generatedImports.find((item) => item.names.includes("defineRegistry"));
|
|
13836
|
+
if (pkgImport) {
|
|
13837
|
+
next = insertImportAfterPackageImports(
|
|
13838
|
+
next,
|
|
13839
|
+
formatImport(["defineRegistry"], pkgImport.module, effectiveStyle),
|
|
13840
|
+
effectiveStyle
|
|
13841
|
+
);
|
|
13842
|
+
added.push("import defineRegistry");
|
|
13843
|
+
}
|
|
13844
|
+
}
|
|
13845
|
+
const preferredDbValue = existingBlock.entries[0]?.value ?? generatedBlock.entries[0]?.value;
|
|
13846
|
+
const generatedDbImport = generatedImports.find((item) => item.names.includes(generatedBlock.entries[0]?.value ?? "")) ?? generatedImports.find((item) => item.module.startsWith("."));
|
|
13847
|
+
if (preferredDbValue && generatedDbImport) {
|
|
13848
|
+
const existingForName = findImportForName(parseNamedImports(next), preferredDbValue);
|
|
13849
|
+
if (!existingForName) {
|
|
13850
|
+
const modulePath = generatedDbImport.module;
|
|
13851
|
+
const importName = findImportForName(generatedImports, preferredDbValue)?.names[0] ?? generatedBlock.entries[0]?.value ?? preferredDbValue;
|
|
13852
|
+
if (!hasImportBinding(parseNamedImports(next), importName)) {
|
|
13853
|
+
next = insertImportAfterPackageImports(
|
|
13854
|
+
next,
|
|
13855
|
+
formatImport([importName], modulePath, effectiveStyle),
|
|
13856
|
+
effectiveStyle
|
|
13857
|
+
);
|
|
13858
|
+
added.push(`import ${importName}`);
|
|
13859
|
+
}
|
|
13860
|
+
} else if (normalizeModulePath(existingForName.module) !== normalizeModulePath(generatedDbImport.module)) {
|
|
13861
|
+
conflicts.push(
|
|
13862
|
+
`binding "${preferredDbValue}" imported from '${existingForName.module}' vs '${generatedDbImport.module}'`
|
|
13863
|
+
);
|
|
13864
|
+
}
|
|
13865
|
+
}
|
|
13866
|
+
let workingBlocks = parseDefineBlocks(next).filter((block) => block.kind === "registry");
|
|
13867
|
+
let workingBlock = workingBlocks[0];
|
|
13868
|
+
const entryMap = new Map(workingBlock.entries.map((entry) => [entry.key, entry]));
|
|
13869
|
+
for (const desired of generatedBlock.entries) {
|
|
13870
|
+
const existingEntry = entryMap.get(desired.key);
|
|
13871
|
+
if (existingEntry) {
|
|
13872
|
+
continue;
|
|
13873
|
+
}
|
|
13874
|
+
const dbImport = parseNamedImports(next).find(
|
|
13875
|
+
(item) => item.module.startsWith(".") && item.names.some((name) => name !== "defineRegistry")
|
|
13876
|
+
);
|
|
13877
|
+
const value = dbImport?.names[0] ?? desired.value;
|
|
13878
|
+
const nextEntries = [...workingBlock.entries, { key: desired.key, value, raw: "" }];
|
|
13879
|
+
const body = rewriteObjectBody(nextEntries, effectiveStyle);
|
|
13880
|
+
next = replaceRange(next, workingBlock.bodyStart, workingBlock.bodyEnd, body);
|
|
13881
|
+
entryMap.set(desired.key, { key: desired.key, value, raw: "" });
|
|
13882
|
+
added.push(`registry entry: ${desired.key}`);
|
|
13883
|
+
workingBlocks = parseDefineBlocks(next).filter((block) => block.kind === "registry");
|
|
13884
|
+
workingBlock = workingBlocks[0];
|
|
13885
|
+
}
|
|
13886
|
+
const existingMeta = parseMetaBlock(next);
|
|
13887
|
+
const generatedMeta = parseMetaBlock(generated);
|
|
13888
|
+
const structuralAdded = added.length > 0;
|
|
13889
|
+
if (generatedMeta) {
|
|
13890
|
+
if (!existingMeta) {
|
|
13891
|
+
const blocks = parseDefineBlocks(next).filter((block) => block.kind === "registry");
|
|
13892
|
+
const insertAt = blocks[0]?.fullStart ?? next.length;
|
|
13893
|
+
const metaBody = rewriteObjectBody(generatedMeta.entries, effectiveStyle);
|
|
13894
|
+
const metaBlock = `export const __athena_schema_meta = {${metaBody}} as const${effectiveStyle.semicolons ? ";" : ""}${effectiveStyle.newline}${effectiveStyle.newline}`;
|
|
13895
|
+
next = replaceRange(next, insertAt, insertAt, metaBlock);
|
|
13896
|
+
added.push("__athena_schema_meta");
|
|
13897
|
+
} else {
|
|
13898
|
+
const desiredEntries = structuralAdded ? generatedMeta.entries : generatedMeta.entries.filter((entry) => {
|
|
13899
|
+
return !existingMeta.entries.some((current) => current.key === entry.key);
|
|
13900
|
+
});
|
|
13901
|
+
if (structuralAdded) {
|
|
13902
|
+
const merged = mergeMetaFields(existingMeta, generatedMeta);
|
|
13903
|
+
if (merged.changed) {
|
|
13904
|
+
const metaNow = parseMetaBlock(next);
|
|
13905
|
+
if (metaNow) {
|
|
13906
|
+
const body = rewriteObjectBody(merged.entries, effectiveStyle);
|
|
13907
|
+
next = replaceRange(next, metaNow.bodyStart, metaNow.bodyEnd, body);
|
|
13908
|
+
added.push(...merged.added);
|
|
13909
|
+
}
|
|
13910
|
+
}
|
|
13911
|
+
} else if (desiredEntries.length > 0) {
|
|
13912
|
+
const map = new Map(existingMeta.entries.map((entry) => [entry.key, entry]));
|
|
13913
|
+
for (const entry of desiredEntries) {
|
|
13914
|
+
map.set(entry.key, entry);
|
|
13915
|
+
added.push(`meta entry: ${entry.key}`);
|
|
13916
|
+
}
|
|
13917
|
+
const ordered = [
|
|
13918
|
+
...existingMeta.entries.map((entry) => map.get(entry.key)),
|
|
13919
|
+
...desiredEntries.filter((entry) => !existingMeta.entries.some((e) => e.key === entry.key))
|
|
13920
|
+
];
|
|
13921
|
+
const metaNow = parseMetaBlock(next);
|
|
13922
|
+
if (metaNow) {
|
|
13923
|
+
const body = rewriteObjectBody(ordered, effectiveStyle);
|
|
13924
|
+
next = replaceRange(next, metaNow.bodyStart, metaNow.bodyEnd, body);
|
|
13925
|
+
}
|
|
13926
|
+
}
|
|
13927
|
+
}
|
|
13928
|
+
}
|
|
13929
|
+
if (conflicts.length > 0 && added.length === 0) {
|
|
13930
|
+
return {
|
|
13931
|
+
action: "skip",
|
|
13932
|
+
skipReason: "merge-conflict",
|
|
13933
|
+
added: [],
|
|
13934
|
+
preservedCustom: preservedCustomUnits(existing, generated, "registry"),
|
|
13935
|
+
conflicts,
|
|
13936
|
+
lintErrors: [],
|
|
13937
|
+
detail: conflicts.join("; ")
|
|
13938
|
+
};
|
|
13939
|
+
}
|
|
13940
|
+
const lintErrors = lintArtifactSource(next, "registry");
|
|
13941
|
+
if (lintErrors.length > 0) {
|
|
13942
|
+
return {
|
|
13943
|
+
action: "skip",
|
|
13944
|
+
skipReason: "merge-lint-failed",
|
|
13945
|
+
added,
|
|
13946
|
+
preservedCustom: preservedCustomUnits(existing, generated, "registry"),
|
|
13947
|
+
conflicts,
|
|
13948
|
+
lintErrors,
|
|
13949
|
+
detail: lintErrors.join("; ")
|
|
13950
|
+
};
|
|
13951
|
+
}
|
|
13952
|
+
const preservedCustom = preservedCustomUnits(next, generated, "registry");
|
|
13953
|
+
if (next.replace(/\r\n/g, "\n") === existing.replace(/\r\n/g, "\n")) {
|
|
13954
|
+
return {
|
|
13955
|
+
action: "unchanged",
|
|
13956
|
+
content: existing,
|
|
13957
|
+
skipReason: "already-current",
|
|
13958
|
+
added: [],
|
|
13959
|
+
preservedCustom,
|
|
13960
|
+
conflicts,
|
|
13961
|
+
lintErrors: []
|
|
13962
|
+
};
|
|
13963
|
+
}
|
|
13964
|
+
return {
|
|
13965
|
+
action: "write",
|
|
13966
|
+
content: next.endsWith("\n") ? next : `${next}${effectiveStyle.newline}`,
|
|
13967
|
+
writeReason: "merged",
|
|
13968
|
+
added,
|
|
13969
|
+
preservedCustom,
|
|
13970
|
+
conflicts,
|
|
13971
|
+
lintErrors: []
|
|
13972
|
+
};
|
|
13973
|
+
}
|
|
13974
|
+
function mergeProtectedArtifact(kind, existing, generated, policy) {
|
|
13975
|
+
if (policy === "overwrite") {
|
|
13976
|
+
if (existing.replace(/\r\n/g, "\n") === generated.replace(/\r\n/g, "\n")) {
|
|
13977
|
+
return {
|
|
13978
|
+
action: "unchanged",
|
|
13979
|
+
content: existing,
|
|
13980
|
+
skipReason: "already-current",
|
|
13981
|
+
added: [],
|
|
13982
|
+
preservedCustom: [],
|
|
13983
|
+
conflicts: [],
|
|
13984
|
+
lintErrors: []
|
|
13985
|
+
};
|
|
13986
|
+
}
|
|
13987
|
+
return {
|
|
13988
|
+
action: "write",
|
|
13989
|
+
content: generated,
|
|
13990
|
+
writeReason: "overwritten",
|
|
13991
|
+
added: ["full overwrite"],
|
|
13992
|
+
preservedCustom: [],
|
|
13993
|
+
conflicts: [],
|
|
13994
|
+
lintErrors: []
|
|
13995
|
+
};
|
|
13996
|
+
}
|
|
13997
|
+
if (policy === "skip") {
|
|
13998
|
+
return {
|
|
13999
|
+
action: "skip",
|
|
14000
|
+
skipReason: "protected-existing-file",
|
|
14001
|
+
added: [],
|
|
14002
|
+
preservedCustom: preservedCustomUnits(existing, generated, kind),
|
|
14003
|
+
conflicts: [],
|
|
14004
|
+
lintErrors: [],
|
|
14005
|
+
detail: "artifactWrite policy is skip"
|
|
14006
|
+
};
|
|
14007
|
+
}
|
|
14008
|
+
return kind === "database" ? mergeDatabaseArtifact(existing, generated) : mergeRegistryArtifact(existing, generated);
|
|
14009
|
+
}
|
|
14010
|
+
function resolveArtifactWritePlan(file, existingContent, policy) {
|
|
14011
|
+
if (existingContent === null) {
|
|
14012
|
+
return {
|
|
14013
|
+
action: "write",
|
|
14014
|
+
content: file.content,
|
|
14015
|
+
writeReason: "created",
|
|
14016
|
+
added: ["created"],
|
|
14017
|
+
preservedCustom: [],
|
|
14018
|
+
conflicts: [],
|
|
14019
|
+
lintErrors: []
|
|
14020
|
+
};
|
|
14021
|
+
}
|
|
14022
|
+
if (file.kind === "model" || file.kind === "schema" || policy === "always") {
|
|
14023
|
+
if (existingContent.replace(/\r\n/g, "\n") === file.content.replace(/\r\n/g, "\n")) {
|
|
14024
|
+
return {
|
|
14025
|
+
action: "unchanged",
|
|
14026
|
+
content: existingContent,
|
|
14027
|
+
skipReason: "already-current",
|
|
14028
|
+
added: [],
|
|
14029
|
+
preservedCustom: [],
|
|
14030
|
+
conflicts: [],
|
|
14031
|
+
lintErrors: []
|
|
14032
|
+
};
|
|
14033
|
+
}
|
|
14034
|
+
return {
|
|
14035
|
+
action: "write",
|
|
14036
|
+
content: file.content,
|
|
14037
|
+
writeReason: existingContent ? "overwritten" : "created",
|
|
14038
|
+
added: ["overwritten"],
|
|
14039
|
+
preservedCustom: [],
|
|
14040
|
+
conflicts: [],
|
|
14041
|
+
lintErrors: []
|
|
14042
|
+
};
|
|
14043
|
+
}
|
|
14044
|
+
return mergeProtectedArtifact(
|
|
14045
|
+
file.kind,
|
|
14046
|
+
existingContent,
|
|
14047
|
+
file.content,
|
|
14048
|
+
policy
|
|
14049
|
+
);
|
|
14050
|
+
}
|
|
14051
|
+
|
|
14052
|
+
// src/generator/pipeline.ts
|
|
13274
14053
|
async function fileExists(path) {
|
|
13275
14054
|
try {
|
|
13276
14055
|
await stat(path);
|
|
@@ -13279,25 +14058,70 @@ async function fileExists(path) {
|
|
|
13279
14058
|
return false;
|
|
13280
14059
|
}
|
|
13281
14060
|
}
|
|
13282
|
-
async function
|
|
14061
|
+
async function readExisting(path) {
|
|
14062
|
+
try {
|
|
14063
|
+
return await readFile(path, "utf8");
|
|
14064
|
+
} catch {
|
|
14065
|
+
return null;
|
|
14066
|
+
}
|
|
14067
|
+
}
|
|
14068
|
+
function policyForArtifact(file, config) {
|
|
14069
|
+
if (file.kind === "model" || file.kind === "schema") {
|
|
14070
|
+
return "always";
|
|
14071
|
+
}
|
|
14072
|
+
if (file.kind === "database") {
|
|
14073
|
+
return config.output.artifactWrite.database;
|
|
14074
|
+
}
|
|
14075
|
+
return config.output.artifactWrite.registry;
|
|
14076
|
+
}
|
|
14077
|
+
async function writeArtifacts(files, cwd, config, dryRun) {
|
|
13283
14078
|
const writtenFiles = [];
|
|
14079
|
+
const writtenDetails = [];
|
|
13284
14080
|
const skippedFiles = [];
|
|
13285
14081
|
for (const file of files) {
|
|
13286
14082
|
const absolutePath = resolve(cwd, file.path);
|
|
13287
|
-
|
|
14083
|
+
const exists = await fileExists(absolutePath);
|
|
14084
|
+
const existingContent = exists ? await readExisting(absolutePath) : null;
|
|
14085
|
+
const policy = policyForArtifact(file, config);
|
|
14086
|
+
const plan = resolveArtifactWritePlan(file, existingContent, policy);
|
|
14087
|
+
if (plan.action === "skip" || plan.action === "unchanged") {
|
|
13288
14088
|
skippedFiles.push({
|
|
13289
14089
|
kind: file.kind,
|
|
13290
14090
|
path: file.path,
|
|
13291
|
-
reason: "
|
|
14091
|
+
reason: plan.skipReason ?? "already-current",
|
|
14092
|
+
detail: plan.detail,
|
|
14093
|
+
preservedCustom: plan.preservedCustom.length > 0 ? plan.preservedCustom : void 0,
|
|
14094
|
+
conflicts: plan.conflicts.length > 0 ? plan.conflicts : void 0,
|
|
14095
|
+
lintErrors: plan.lintErrors.length > 0 ? plan.lintErrors : void 0
|
|
13292
14096
|
});
|
|
13293
14097
|
continue;
|
|
13294
14098
|
}
|
|
13295
|
-
|
|
13296
|
-
|
|
14099
|
+
if (!plan.content || !plan.writeReason) {
|
|
14100
|
+
skippedFiles.push({
|
|
14101
|
+
kind: file.kind,
|
|
14102
|
+
path: file.path,
|
|
14103
|
+
reason: "merge-lint-failed",
|
|
14104
|
+
detail: "merge produced no content",
|
|
14105
|
+
lintErrors: plan.lintErrors
|
|
14106
|
+
});
|
|
14107
|
+
continue;
|
|
14108
|
+
}
|
|
14109
|
+
if (!dryRun) {
|
|
14110
|
+
await mkdir(dirname(absolutePath), { recursive: true });
|
|
14111
|
+
await writeFile(absolutePath, plan.content, "utf8");
|
|
14112
|
+
}
|
|
13297
14113
|
writtenFiles.push(file.path);
|
|
14114
|
+
writtenDetails.push({
|
|
14115
|
+
kind: file.kind,
|
|
14116
|
+
path: file.path,
|
|
14117
|
+
reason: plan.writeReason,
|
|
14118
|
+
added: plan.added.length > 0 ? plan.added : void 0,
|
|
14119
|
+
preservedCustom: plan.preservedCustom.length > 0 ? plan.preservedCustom : void 0
|
|
14120
|
+
});
|
|
13298
14121
|
}
|
|
13299
14122
|
return {
|
|
13300
14123
|
writtenFiles,
|
|
14124
|
+
writtenDetails,
|
|
13301
14125
|
skippedFiles
|
|
13302
14126
|
};
|
|
13303
14127
|
}
|
|
@@ -13313,12 +14137,18 @@ async function runSchemaGenerator(options = {}) {
|
|
|
13313
14137
|
schemas: resolveProviderSchemas(config.provider)
|
|
13314
14138
|
});
|
|
13315
14139
|
const generated = generateArtifactsFromSnapshot(snapshot, config);
|
|
13316
|
-
const writeResult =
|
|
14140
|
+
const writeResult = await writeArtifacts(
|
|
14141
|
+
generated.files,
|
|
14142
|
+
cwd,
|
|
14143
|
+
config,
|
|
14144
|
+
options.dryRun === true
|
|
14145
|
+
);
|
|
13317
14146
|
return {
|
|
13318
14147
|
...generated,
|
|
13319
14148
|
configPath,
|
|
13320
14149
|
config,
|
|
13321
14150
|
writtenFiles: writeResult.writtenFiles,
|
|
14151
|
+
writtenDetails: writeResult.writtenDetails,
|
|
13322
14152
|
skippedFiles: writeResult.skippedFiles
|
|
13323
14153
|
};
|
|
13324
14154
|
}
|