@xylex-group/athena 3.0.2 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) 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 +322 -1
  7. package/dist/browser.cjs.map +1 -1
  8. package/dist/browser.d.cts +5 -5
  9. package/dist/browser.d.ts +5 -5
  10. package/dist/browser.js +301 -2
  11. package/dist/browser.js.map +1 -1
  12. package/dist/cli/index.cjs +5381 -3772
  13. package/dist/cli/index.cjs.map +1 -1
  14. package/dist/cli/index.d.cts +17 -10
  15. package/dist/cli/index.d.ts +17 -10
  16. package/dist/cli/index.js +5385 -3776
  17. package/dist/cli/index.js.map +1 -1
  18. package/dist/index.cjs +1723 -26
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +28 -6
  21. package/dist/index.d.ts +28 -6
  22. package/dist/index.js +1695 -28
  23. package/dist/index.js.map +1 -1
  24. package/dist/model-form-BbdzJ9Uh.d.ts +259 -0
  25. package/dist/model-form-BhWWsA-7.d.cts +259 -0
  26. package/dist/{module-CkUM6v58.d.ts → module-DKkJKOuJ.d.ts} +2 -2
  27. package/dist/{module-CB25egcO.d.cts → module-xFd7_Igv.d.cts} +2 -2
  28. package/dist/next/client.cjs +1 -1
  29. package/dist/next/client.cjs.map +1 -1
  30. package/dist/next/client.js +1 -1
  31. package/dist/next/client.js.map +1 -1
  32. package/dist/next/server.cjs +1 -1
  33. package/dist/next/server.cjs.map +1 -1
  34. package/dist/next/server.js +1 -1
  35. package/dist/next/server.js.map +1 -1
  36. package/dist/pipeline-DuKzYGia.d.cts +93 -0
  37. package/dist/pipeline-wer2G9xW.d.ts +93 -0
  38. package/dist/react.cjs +400 -3
  39. package/dist/react.cjs.map +1 -1
  40. package/dist/react.d.cts +67 -2
  41. package/dist/react.d.ts +67 -2
  42. package/dist/react.js +400 -4
  43. package/dist/react.js.map +1 -1
  44. package/dist/{types-BBm-kEBL.d.cts → types-1ztXDgPa.d.cts} +62 -2
  45. package/dist/{types-BaAMXCqK.d.ts → types-CccOiXPo.d.ts} +62 -2
  46. package/package.json +1 -1
  47. package/dist/model-form-Dh0sisdL.d.ts +0 -97
  48. package/dist/model-form-oOAJUVd4.d.cts +0 -97
  49. package/dist/pipeline-B8aN2EHe.d.ts +0 -8
  50. package/dist/pipeline-D4W-Cc-A.d.cts +0 -8
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, readFile, stat } from 'fs/promises';
6
6
 
7
7
  // src/gateway/errors.ts
8
8
  var AthenaGatewayError = class _AthenaGatewayError extends Error {
@@ -571,8 +571,8 @@ function computeDelayMs(attempt, error, config) {
571
571
  }
572
572
  function sleep(ms) {
573
573
  if (ms <= 0) return Promise.resolve();
574
- return new Promise((resolve3) => {
575
- setTimeout(resolve3, ms);
574
+ return new Promise((resolve4) => {
575
+ setTimeout(resolve4, ms);
576
576
  });
577
577
  }
578
578
  async function withRetry(config, fn) {
@@ -2007,7 +2007,7 @@ function buildAthenaGatewayUrl(baseUrl, path) {
2007
2007
 
2008
2008
  // package.json
2009
2009
  var package_default = {
2010
- version: "3.0.2"
2010
+ version: "3.1.0"
2011
2011
  };
2012
2012
 
2013
2013
  // src/sdk-version.ts
@@ -5226,7 +5226,7 @@ async function putWithXhr(url, signed, body, signal, onProgress) {
5226
5226
  if (Xhr === void 0) {
5227
5227
  return Promise.reject(new Error("athena.storage.file.upload direct mode requires XMLHttpRequest in this runtime"));
5228
5228
  }
5229
- return new Promise((resolve3, reject) => {
5229
+ return new Promise((resolve4, reject) => {
5230
5230
  const xhr = new Xhr();
5231
5231
  const abort = () => xhr.abort();
5232
5232
  xhr.open("PUT", url);
@@ -5239,7 +5239,7 @@ async function putWithXhr(url, signed, body, signal, onProgress) {
5239
5239
  return;
5240
5240
  }
5241
5241
  onProgress({ loaded: bodySize(body) });
5242
- resolve3(new Response(xhr.response, {
5242
+ resolve4(new Response(xhr.response, {
5243
5243
  status: xhr.status,
5244
5244
  statusText: xhr.statusText,
5245
5245
  headers: parseXhrHeaders(xhr.getAllResponseHeaders())
@@ -5692,7 +5692,7 @@ function putUploadBodyWithXhr(url, source, headers, options, onProgress) {
5692
5692
  if (Xhr === void 0) {
5693
5693
  return Promise.reject(new Error("athena.storage.file.upload requires XMLHttpRequest in this runtime"));
5694
5694
  }
5695
- return new Promise((resolve3, reject) => {
5695
+ return new Promise((resolve4, reject) => {
5696
5696
  const xhr = new Xhr();
5697
5697
  const abort = () => xhr.abort();
5698
5698
  xhr.open("PUT", url);
@@ -5709,7 +5709,7 @@ function putUploadBodyWithXhr(url, source, headers, options, onProgress) {
5709
5709
  return;
5710
5710
  }
5711
5711
  onProgress({ loaded: source.sizeBytes });
5712
- resolve3(new Response(xhr.response, {
5712
+ resolve4(new Response(xhr.response, {
5713
5713
  status: xhr.status,
5714
5714
  statusText: xhr.statusText,
5715
5715
  headers: parseXhrHeaders2(xhr.getAllResponseHeaders())
@@ -10768,6 +10768,305 @@ var Backend = {
10768
10768
  ScyllaDB: { type: "scylladb" }
10769
10769
  };
10770
10770
 
10771
+ // src/query/read-query.ts
10772
+ function normalizeAthenaReadQueryOrderBy(orderBy) {
10773
+ if (!orderBy) {
10774
+ return [];
10775
+ }
10776
+ const list = Array.isArray(orderBy) ? orderBy : [orderBy];
10777
+ return list.filter(
10778
+ (entry) => entry && typeof entry.column === "string" && entry.column.trim().length > 0
10779
+ );
10780
+ }
10781
+ function isRecord12(value) {
10782
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10783
+ }
10784
+ function flattenAthenaRowValue(row, column) {
10785
+ if (!column.relation) {
10786
+ return row[column.column];
10787
+ }
10788
+ const relationValue = row[column.relation.name];
10789
+ if (Array.isArray(relationValue)) {
10790
+ return relationValue.map((entry) => isRecord12(entry) ? entry[column.column] : void 0).filter((entry) => entry !== void 0);
10791
+ }
10792
+ if (isRecord12(relationValue)) {
10793
+ return relationValue[column.column];
10794
+ }
10795
+ return void 0;
10796
+ }
10797
+ function resolveFlatRowKey(row, index, preferredKey) {
10798
+ const preferredValue = preferredKey && preferredKey in row ? row[preferredKey] : void 0;
10799
+ if (preferredValue !== void 0 && preferredValue !== null && `${preferredValue}`.trim().length > 0) {
10800
+ return String(preferredValue);
10801
+ }
10802
+ for (const [key, value] of Object.entries(row)) {
10803
+ if (key === "__rowKey") {
10804
+ continue;
10805
+ }
10806
+ if (value === void 0 || value === null) {
10807
+ continue;
10808
+ }
10809
+ if (Array.isArray(value)) {
10810
+ if (value.length > 0) {
10811
+ return `${key}-${value.join("-")}`;
10812
+ }
10813
+ continue;
10814
+ }
10815
+ const nextValue = String(value).trim();
10816
+ if (nextValue.length > 0) {
10817
+ return nextValue;
10818
+ }
10819
+ }
10820
+ return `row-${index + 1}`;
10821
+ }
10822
+ function flattenAthenaReadQueryRows(rows, columns, preferredKey) {
10823
+ return rows.map((entry, index) => {
10824
+ const sourceRow = isRecord12(entry) ? entry : {};
10825
+ const flatRow = columns.reduce(
10826
+ (currentRow, column) => {
10827
+ currentRow[column.key] = flattenAthenaRowValue(sourceRow, column);
10828
+ return currentRow;
10829
+ },
10830
+ { __rowKey: "" }
10831
+ );
10832
+ flatRow.__rowKey = resolveFlatRowKey(flatRow, index, preferredKey);
10833
+ return flatRow;
10834
+ });
10835
+ }
10836
+ var flattenAthenaRows = flattenAthenaReadQueryRows;
10837
+ function buildAthenaReadQuerySelectString(columns) {
10838
+ const baseColumns = columns.filter((column) => !column.relation).map((column) => column.column);
10839
+ const relationGroups = /* @__PURE__ */ new Map();
10840
+ for (const column of columns) {
10841
+ if (!column.relation) {
10842
+ continue;
10843
+ }
10844
+ const groupKey = [
10845
+ column.relation.name,
10846
+ column.relation.schema ?? "",
10847
+ column.relation.table
10848
+ ].join(":");
10849
+ const existingGroup = relationGroups.get(groupKey);
10850
+ if (existingGroup) {
10851
+ if (!existingGroup.columns.includes(column.column)) {
10852
+ existingGroup.columns.push(column.column);
10853
+ }
10854
+ continue;
10855
+ }
10856
+ relationGroups.set(groupKey, {
10857
+ columns: [column.column],
10858
+ relation: column.relation
10859
+ });
10860
+ }
10861
+ const relationColumns = [...relationGroups.values()].map(
10862
+ ({ columns: relationColumns2, relation }) => {
10863
+ const relationTarget = relation.schema ? `${relation.schema}.${relation.table}` : relation.table;
10864
+ return `${relation.name}:${relationTarget}(${relationColumns2.join(",")})`;
10865
+ }
10866
+ );
10867
+ return [...baseColumns, ...relationColumns].join(",");
10868
+ }
10869
+ var buildAthenaTableSelectString = buildAthenaReadQuerySelectString;
10870
+ function buildAthenaReadQueryFindManySelect(columns) {
10871
+ const select = {};
10872
+ for (const column of columns) {
10873
+ if (!column.relation) {
10874
+ select[column.column] = true;
10875
+ continue;
10876
+ }
10877
+ const existingRelation = select[column.relation.name];
10878
+ if (existingRelation && existingRelation !== true && isRecord12(existingRelation.select)) {
10879
+ existingRelation.select[column.column] = true;
10880
+ continue;
10881
+ }
10882
+ select[column.relation.name] = {
10883
+ ...column.relation.schema ? { schema: column.relation.schema } : {},
10884
+ select: {
10885
+ [column.column]: true
10886
+ }
10887
+ };
10888
+ }
10889
+ return select;
10890
+ }
10891
+ var buildAthenaTableFindManySelect = buildAthenaReadQueryFindManySelect;
10892
+ function buildAthenaReadQueryFindManyWhere(filters) {
10893
+ if (!filters?.length) {
10894
+ return;
10895
+ }
10896
+ return filters.reduce(
10897
+ (where, filter) => {
10898
+ const operator = filter.operator ?? "eq";
10899
+ where[filter.column] = operator === "eq" ? filter.value : { [operator]: filter.value };
10900
+ return where;
10901
+ },
10902
+ {}
10903
+ );
10904
+ }
10905
+ var buildAthenaTableFindManyWhere = buildAthenaReadQueryFindManyWhere;
10906
+ function buildAthenaReadQueryFindManyOrderBy(orderBy) {
10907
+ const normalized = normalizeAthenaReadQueryOrderBy(orderBy);
10908
+ if (normalized.length === 0) {
10909
+ return;
10910
+ }
10911
+ if (normalized.length === 1) {
10912
+ const primary = normalized[0];
10913
+ if (!primary) {
10914
+ return;
10915
+ }
10916
+ return {
10917
+ ascending: primary.direction !== "desc",
10918
+ column: primary.column
10919
+ };
10920
+ }
10921
+ return Object.fromEntries(
10922
+ normalized.map((entry) => [
10923
+ entry.column,
10924
+ entry.direction === "desc" ? "desc" : "asc"
10925
+ ])
10926
+ );
10927
+ }
10928
+ var buildAthenaTableFindManyOrderBy = buildAthenaReadQueryFindManyOrderBy;
10929
+ var normalizeAthenaTableOrderBy = normalizeAthenaReadQueryOrderBy;
10930
+ function applyAthenaReadQueryFilters(queryBuilder, filters) {
10931
+ if (!filters?.length) {
10932
+ return queryBuilder;
10933
+ }
10934
+ return filters.reduce((currentBuilder, filter) => {
10935
+ const operator = filter.operator ?? "eq";
10936
+ const builderMethod = currentBuilder[operator];
10937
+ if (typeof builderMethod !== "function") {
10938
+ return currentBuilder;
10939
+ }
10940
+ return builderMethod.call(currentBuilder, filter.column, filter.value);
10941
+ }, queryBuilder);
10942
+ }
10943
+ var applyAthenaTableFilters = applyAthenaReadQueryFilters;
10944
+ function applyAthenaReadQuerySelectOrder(queryBuilder, orderBy) {
10945
+ const normalized = normalizeAthenaReadQueryOrderBy(orderBy);
10946
+ if (normalized.length === 0) {
10947
+ return queryBuilder;
10948
+ }
10949
+ return normalized.reduce(
10950
+ (builder, entry) => builder.order(entry.column, {
10951
+ ascending: entry.direction !== "desc"
10952
+ }),
10953
+ queryBuilder
10954
+ );
10955
+ }
10956
+ var applyAthenaTableSelectOrder = applyAthenaReadQuerySelectOrder;
10957
+ function applyAthenaReadQuerySelectLimit(queryBuilder, limit) {
10958
+ if (!limit || limit < 1) {
10959
+ return queryBuilder;
10960
+ }
10961
+ return queryBuilder.limit(limit);
10962
+ }
10963
+ var applyAthenaTableSelectLimit = applyAthenaReadQuerySelectLimit;
10964
+ function clampAthenaReadQueryTotalItems(totalItems, limit) {
10965
+ if (!limit || limit < 1) {
10966
+ return totalItems;
10967
+ }
10968
+ return Math.min(totalItems, limit);
10969
+ }
10970
+ var clampAthenaTableTotalItems = clampAthenaReadQueryTotalItems;
10971
+ function resolveCountValue(...candidates) {
10972
+ for (const candidate of candidates) {
10973
+ if (typeof candidate === "number" && Number.isFinite(candidate)) {
10974
+ return Math.max(0, Math.trunc(candidate));
10975
+ }
10976
+ }
10977
+ return 0;
10978
+ }
10979
+ function resolveTotalItemsFromPage({
10980
+ countCandidates,
10981
+ limit,
10982
+ page,
10983
+ pageSize,
10984
+ rowCount
10985
+ }) {
10986
+ const exactCount = resolveCountValue(...countCandidates);
10987
+ if (exactCount > 0) {
10988
+ return clampAthenaReadQueryTotalItems(exactCount, limit);
10989
+ }
10990
+ if (rowCount > 0) {
10991
+ const base = (page - 1) * pageSize + rowCount;
10992
+ const synthetic = rowCount >= pageSize ? base + 1 : base;
10993
+ return clampAthenaReadQueryTotalItems(synthetic, limit);
10994
+ }
10995
+ return clampAthenaReadQueryTotalItems(exactCount, limit);
10996
+ }
10997
+ async function executeAthenaReadQuery({
10998
+ client,
10999
+ page,
11000
+ pageSize,
11001
+ query
11002
+ }) {
11003
+ const db = client.db;
11004
+ const baseBuilder = db.from(query.table, {
11005
+ schema: query.schema
11006
+ });
11007
+ const countBuilder = applyAthenaReadQueryFilters(
11008
+ db.from(query.table, { schema: query.schema }),
11009
+ query.filters
11010
+ );
11011
+ const countResult = await countBuilder.select(query.countColumn, {
11012
+ count: "exact",
11013
+ head: true
11014
+ });
11015
+ if ((query.mode ?? "findMany") === "select") {
11016
+ const selectBuilder = applyAthenaReadQuerySelectLimit(
11017
+ applyAthenaReadQuerySelectOrder(
11018
+ applyAthenaReadQueryFilters(baseBuilder, query.filters),
11019
+ query.orderBy
11020
+ ),
11021
+ query.limit
11022
+ );
11023
+ const result2 = await selectBuilder.currentPage(page).pageSize(pageSize).select(buildAthenaReadQuerySelectString(query.columns), {
11024
+ count: "exact"
11025
+ });
11026
+ const rows2 = flattenAthenaReadQueryRows(
11027
+ Array.isArray(result2.data) ? result2.data : [],
11028
+ query.columns,
11029
+ query.rowKey
11030
+ );
11031
+ return {
11032
+ debugAst: getAthenaDebugAst(result2) ?? void 0,
11033
+ rows: rows2,
11034
+ totalItems: resolveTotalItemsFromPage({
11035
+ countCandidates: [countResult.count, result2.count],
11036
+ limit: query.limit,
11037
+ page,
11038
+ pageSize,
11039
+ rowCount: rows2.length
11040
+ })
11041
+ };
11042
+ }
11043
+ const findManyOrderBy = buildAthenaReadQueryFindManyOrderBy(query.orderBy);
11044
+ const findManyOptions = {
11045
+ ...query.limit ? { limit: query.limit } : {},
11046
+ ...findManyOrderBy ? { orderBy: findManyOrderBy } : {},
11047
+ select: buildAthenaReadQueryFindManySelect(query.columns),
11048
+ ...query.filters?.length ? { where: buildAthenaReadQueryFindManyWhere(query.filters) } : {}
11049
+ };
11050
+ const result = await baseBuilder.currentPage(page).pageSize(pageSize).findMany(findManyOptions);
11051
+ const rows = flattenAthenaReadQueryRows(
11052
+ Array.isArray(result.data) ? result.data : [],
11053
+ query.columns,
11054
+ query.rowKey
11055
+ );
11056
+ return {
11057
+ debugAst: getAthenaDebugAst(result) ?? void 0,
11058
+ rows,
11059
+ totalItems: resolveTotalItemsFromPage({
11060
+ countCandidates: [countResult.count, result.count],
11061
+ limit: query.limit,
11062
+ page,
11063
+ pageSize,
11064
+ rowCount: rows.length
11065
+ })
11066
+ };
11067
+ }
11068
+ var executeAthenaTableQuery = executeAthenaReadQuery;
11069
+
10771
11070
  // src/schema/definitions.ts
10772
11071
  function defineModel(input) {
10773
11072
  return input;
@@ -10788,7 +11087,7 @@ function resolveNullishValue(mode) {
10788
11087
  if (mode === "null") return null;
10789
11088
  return "";
10790
11089
  }
10791
- function isRecord12(value) {
11090
+ function isRecord13(value) {
10792
11091
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
10793
11092
  }
10794
11093
  function isNullableColumn(model, key) {
@@ -10797,7 +11096,7 @@ function isNullableColumn(model, key) {
10797
11096
  }
10798
11097
  function toModelFormDefaults(model, values, options) {
10799
11098
  const source = values;
10800
- if (!isRecord12(source)) {
11099
+ if (!isRecord13(source)) {
10801
11100
  return {};
10802
11101
  }
10803
11102
  const mode = options?.nullishMode ?? "empty-string";
@@ -11717,6 +12016,12 @@ var ATHENA_DIRECT_TARGETS = {
11717
12016
  };
11718
12017
  var DEFAULT_OUTPUT_FORMAT = "table-builder";
11719
12018
  var DEFAULT_OUTPUT_PRESET = "athena-direct";
12019
+ var DEFAULT_ARTIFACT_WRITE_POLICY = "merge";
12020
+ var ARTIFACT_WRITE_POLICIES = /* @__PURE__ */ new Set([
12021
+ "merge",
12022
+ "skip",
12023
+ "overwrite"
12024
+ ]);
11720
12025
  var DEFAULT_NAMING = {
11721
12026
  modelType: "pascal",
11722
12027
  modelConst: "camel",
@@ -11996,6 +12301,23 @@ function normalizeFilterConfig(input) {
11996
12301
  excludeTables: normalizeTableSelection(input?.excludeTables)
11997
12302
  };
11998
12303
  }
12304
+ function normalizeArtifactWritePolicy(value, fieldName) {
12305
+ if (value === void 0 || value === null || value === "") {
12306
+ return DEFAULT_ARTIFACT_WRITE_POLICY;
12307
+ }
12308
+ if (typeof value === "string" && ARTIFACT_WRITE_POLICIES.has(value)) {
12309
+ return value;
12310
+ }
12311
+ throw new Error(
12312
+ `Invalid output.artifactWrite.${fieldName}: expected one of merge | skip | overwrite, received ${JSON.stringify(value)}.`
12313
+ );
12314
+ }
12315
+ function normalizeArtifactWriteConfig(input) {
12316
+ return {
12317
+ database: normalizeArtifactWritePolicy(input?.database, "database"),
12318
+ registry: normalizeArtifactWritePolicy(input?.registry, "registry")
12319
+ };
12320
+ }
11999
12321
  function normalizeOutputConfig(output) {
12000
12322
  const preset = output?.preset ?? DEFAULT_OUTPUT_PRESET;
12001
12323
  return {
@@ -12007,7 +12329,8 @@ function normalizeOutputConfig(output) {
12007
12329
  },
12008
12330
  placeholderMap: {
12009
12331
  ...output?.placeholderMap ?? {}
12010
- }
12332
+ },
12333
+ artifactWrite: normalizeArtifactWriteConfig(output?.artifactWrite)
12011
12334
  };
12012
12335
  }
12013
12336
  function normalizeProviderConfig(provider) {
@@ -12290,6 +12613,486 @@ async function loadGeneratorConfig(options = {}) {
12290
12613
  }
12291
12614
  }
12292
12615
 
12616
+ // src/generator/schema-discovery.ts
12617
+ var DISCOVER_POSTGRES_SCHEMAS_SQL = `
12618
+ SELECT DISTINCT n.nspname AS schema_name
12619
+ FROM pg_namespace n
12620
+ JOIN pg_class c ON c.relnamespace = n.oid
12621
+ WHERE c.relkind IN ('r', 'p')
12622
+ AND n.nspname NOT IN ('pg_catalog', 'information_schema')
12623
+ AND n.nspname NOT LIKE 'pg_toast%'
12624
+ AND n.nspname NOT LIKE 'pg_temp_%'
12625
+ AND n.nspname NOT LIKE 'pg_toast_temp_%'
12626
+ ORDER BY n.nspname;
12627
+ `.trim();
12628
+ var SYSTEM_SCHEMA_PREFIXES = ["pg_"];
12629
+ var SYSTEM_SCHEMAS = /* @__PURE__ */ new Set(["pg_catalog", "information_schema"]);
12630
+ function normalizeDiscoveredSchemas(input) {
12631
+ const schemas = [];
12632
+ const seen = /* @__PURE__ */ new Set();
12633
+ for (const value of input) {
12634
+ const schema = value.trim();
12635
+ if (!schema || seen.has(schema)) {
12636
+ continue;
12637
+ }
12638
+ if (SYSTEM_SCHEMAS.has(schema)) {
12639
+ continue;
12640
+ }
12641
+ if (SYSTEM_SCHEMA_PREFIXES.some((prefix) => schema.startsWith(prefix))) {
12642
+ continue;
12643
+ }
12644
+ seen.add(schema);
12645
+ schemas.push(schema);
12646
+ }
12647
+ return schemas;
12648
+ }
12649
+ function mergeSchemaSelections(configured, discovered) {
12650
+ const result = [];
12651
+ const seen = /* @__PURE__ */ new Set();
12652
+ for (const value of configured ?? []) {
12653
+ const schema = value.trim();
12654
+ if (!schema || seen.has(schema)) {
12655
+ continue;
12656
+ }
12657
+ seen.add(schema);
12658
+ result.push(schema);
12659
+ }
12660
+ for (const value of discovered) {
12661
+ const schema = value.trim();
12662
+ if (!schema || seen.has(schema)) {
12663
+ continue;
12664
+ }
12665
+ seen.add(schema);
12666
+ result.push(schema);
12667
+ }
12668
+ return result.length > 0 ? result : ["public"];
12669
+ }
12670
+ function schemasEqual(left, right) {
12671
+ const a = normalizeDiscoveredSchemas(left ?? []);
12672
+ const b = normalizeDiscoveredSchemas(right ?? []);
12673
+ if (a.length !== b.length) {
12674
+ return false;
12675
+ }
12676
+ const set = new Set(a);
12677
+ return b.every((schema) => set.has(schema));
12678
+ }
12679
+ function coerceSchemaName(row) {
12680
+ const raw = row.schema_name ?? row.schemaName ?? row.nspname;
12681
+ if (typeof raw !== "string") {
12682
+ return void 0;
12683
+ }
12684
+ const trimmed = raw.trim();
12685
+ return trimmed.length > 0 ? trimmed : void 0;
12686
+ }
12687
+ async function loadPgPoolConstructor2() {
12688
+ const module = await import('pg');
12689
+ const poolConstructor = module.Pool ?? module.default?.Pool;
12690
+ if (!poolConstructor) {
12691
+ throw new Error(
12692
+ '@xylex-group/athena: Unable to load the PostgreSQL driver for schema discovery. Ensure "pg" is installed and this API runs in a Node.js server runtime.'
12693
+ );
12694
+ }
12695
+ return poolConstructor;
12696
+ }
12697
+ async function discoverDirectSchemas(config) {
12698
+ const PoolConstructor = await loadPgPoolConstructor2();
12699
+ const pool = new PoolConstructor({ connectionString: config.connectionString });
12700
+ try {
12701
+ const result = await pool.query(DISCOVER_POSTGRES_SCHEMAS_SQL);
12702
+ const names = result.rows.map(coerceSchemaName).filter((value) => Boolean(value));
12703
+ return normalizeDiscoveredSchemas(names);
12704
+ } finally {
12705
+ await pool.end();
12706
+ }
12707
+ }
12708
+ async function discoverGatewaySchemas(config) {
12709
+ const client = createClient({
12710
+ db: { url: config.gatewayUrl },
12711
+ key: config.apiKey,
12712
+ backend: {
12713
+ type: config.backend ?? "postgresql"
12714
+ }
12715
+ });
12716
+ const result = await client.query(DISCOVER_POSTGRES_SCHEMAS_SQL);
12717
+ if (result.error || result.status < 200 || result.status >= 300) {
12718
+ throw new Error(
12719
+ result.error?.message ?? `Gateway schema discovery failed with status ${result.status}`
12720
+ );
12721
+ }
12722
+ const names = (result.data ?? []).map(coerceSchemaName).filter((value) => Boolean(value));
12723
+ return normalizeDiscoveredSchemas(names);
12724
+ }
12725
+ async function discoverPostgresSchemas(provider) {
12726
+ if (provider.kind === "postgres" && provider.mode === "direct") {
12727
+ return discoverDirectSchemas(provider);
12728
+ }
12729
+ if (provider.kind === "postgres" && provider.mode === "gateway") {
12730
+ return discoverGatewaySchemas(provider);
12731
+ }
12732
+ throw new Error(
12733
+ `Schema discovery is only implemented for postgres direct/gateway providers (received ${provider.kind}/${"mode" in provider ? provider.mode : "unknown"}).`
12734
+ );
12735
+ }
12736
+
12737
+ // src/generator/config-file.ts
12738
+ function hasEnv(keys) {
12739
+ for (const key of keys) {
12740
+ const value = process.env[key];
12741
+ if (typeof value === "string" && value.trim().length > 0) {
12742
+ return true;
12743
+ }
12744
+ }
12745
+ return false;
12746
+ }
12747
+ var DIRECT_CONNECTION_KEYS = [
12748
+ "ATHENA_GENERATOR_PG_URL",
12749
+ "DATABASE_URL",
12750
+ "PG_URL",
12751
+ "POSTGRES_URL",
12752
+ "POSTGRESQL_URL"
12753
+ ];
12754
+ var GATEWAY_URL_KEYS = ["ATHENA_URL", "ATHENA_GATEWAY_URL", "ATHENA_GENERATOR_URL"];
12755
+ var GATEWAY_KEY_KEYS = [
12756
+ "ATHENA_API_KEY",
12757
+ "ATHENA_GATEWAY_API_KEY",
12758
+ "ATHENA_GENERATOR_API_KEY"
12759
+ ];
12760
+ function detectGeneratorProviderMode(preferred = "auto") {
12761
+ if (preferred === "direct" || preferred === "gateway") {
12762
+ return preferred;
12763
+ }
12764
+ const hasDirect = hasEnv(DIRECT_CONNECTION_KEYS);
12765
+ const hasGateway = hasEnv(GATEWAY_URL_KEYS) && hasEnv(GATEWAY_KEY_KEYS);
12766
+ if (hasDirect) {
12767
+ return "direct";
12768
+ }
12769
+ if (hasGateway) {
12770
+ return "gateway";
12771
+ }
12772
+ return "direct";
12773
+ }
12774
+ function formatStringArray(values, indent) {
12775
+ if (values.length === 0) {
12776
+ return "[]";
12777
+ }
12778
+ const inner = values.map((value) => `${indent} ${JSON.stringify(value)}`).join(",\n");
12779
+ return `[
12780
+ ${inner},
12781
+ ${indent}]`;
12782
+ }
12783
+ function buildDirectProviderBlock(schemas) {
12784
+ return ` provider: {
12785
+ kind: "postgres",
12786
+ mode: "direct",
12787
+ connectionString: generatorEnv("DATABASE_URL"),
12788
+ database: generatorEnv("ATHENA_GENERATOR_DB", { optional: true }),
12789
+ schemas: ${formatStringArray(schemas, " ")},
12790
+ }`;
12791
+ }
12792
+ function buildGatewayProviderBlock(schemas) {
12793
+ return ` provider: {
12794
+ kind: "postgres",
12795
+ mode: "gateway",
12796
+ gatewayUrl: generatorEnv("ATHENA_URL"),
12797
+ apiKey: generatorEnv("ATHENA_API_KEY"),
12798
+ database: generatorEnv("ATHENA_GENERATOR_DB", { default: "postgres" }),
12799
+ schemas: ${formatStringArray(schemas, " ")},
12800
+ }`;
12801
+ }
12802
+ function renderGeneratorConfigFile(options) {
12803
+ const schemas = options.schemas && options.schemas.length > 0 ? [...options.schemas] : [...DEFAULT_POSTGRES_SCHEMAS];
12804
+ const providerBlock = options.mode === "gateway" ? buildGatewayProviderBlock(schemas) : buildDirectProviderBlock(schemas);
12805
+ return `import { defineGeneratorConfig, generatorEnv } from "@xylex-group/athena";
12806
+
12807
+ export default defineGeneratorConfig({
12808
+ ${providerBlock},
12809
+ output: {
12810
+ format: "table-builder",
12811
+ preset: "athena-direct",
12812
+ targets: {
12813
+ model: "athena/models/{schema_kebab}/{model_kebab}.ts",
12814
+ schema: "athena/schemas/{schema_kebab}.ts",
12815
+ database: "athena/relations.ts",
12816
+ registry: "athena/registry.generated.ts",
12817
+ },
12818
+ placeholderMap: {
12819
+ namespace: "athena",
12820
+ },
12821
+ },
12822
+ naming: {
12823
+ modelType: "pascal",
12824
+ modelConst: "camel",
12825
+ schemaConst: "camel",
12826
+ databaseConst: "camel",
12827
+ registryConst: "camel",
12828
+ },
12829
+ features: {
12830
+ emitRelations: true,
12831
+ emitRegistry: true,
12832
+ },
12833
+ experimental: {
12834
+ postgresGatewayIntrospection: false,
12835
+ scyllaProviderContracts: true,
12836
+ },
12837
+ });
12838
+ `;
12839
+ }
12840
+ function resolveConfigPath(cwd, configPath) {
12841
+ if (configPath) {
12842
+ const absolutePath2 = resolve(cwd, configPath);
12843
+ return {
12844
+ absolutePath: absolutePath2,
12845
+ relativePath: configPath.replace(/\\/g, "/"),
12846
+ exists: existsSync(absolutePath2)
12847
+ };
12848
+ }
12849
+ const discovered = findGeneratorConfigPath(cwd);
12850
+ if (discovered) {
12851
+ const relativePath = discovered.startsWith(cwd) ? discovered.slice(cwd.length).replace(/^[/\\]/, "").replace(/\\/g, "/") : "athena.config.ts";
12852
+ return {
12853
+ absolutePath: discovered,
12854
+ relativePath,
12855
+ exists: true
12856
+ };
12857
+ }
12858
+ const absolutePath = resolve(cwd, "athena.config.ts");
12859
+ return {
12860
+ absolutePath,
12861
+ relativePath: "athena.config.ts",
12862
+ exists: false
12863
+ };
12864
+ }
12865
+ function providerModeFromConfig(provider, preferred) {
12866
+ if (provider && provider.kind === "postgres") {
12867
+ if (provider.mode === "gateway") {
12868
+ return "gateway";
12869
+ }
12870
+ if (provider.mode === "direct") {
12871
+ return "direct";
12872
+ }
12873
+ }
12874
+ return detectGeneratorProviderMode(preferred);
12875
+ }
12876
+ function schemasFromNormalized(config) {
12877
+ if (config.provider.kind !== "postgres") {
12878
+ return [...DEFAULT_POSTGRES_SCHEMAS];
12879
+ }
12880
+ return normalizeSchemaSelection(config.provider.schemas);
12881
+ }
12882
+ function patchSchemasInConfigSource(source, schemas) {
12883
+ const formattedInline = `[${schemas.map((value) => JSON.stringify(value)).join(", ")}]`;
12884
+ const formattedMultiline = formatStringArray(schemas, " ");
12885
+ const listDefaultPattern = /(schemas\s*:\s*generatorEnv\.list\s*\(\s*['"][^'"]+['"]\s*,\s*\{[\s\S]*?default\s*:\s*)(\[[^\]]*\])/m;
12886
+ if (listDefaultPattern.test(source)) {
12887
+ return source.replace(listDefaultPattern, `$1${formattedInline}`);
12888
+ }
12889
+ const literalPattern = /(schemas\s*:\s*)(\[[^\]]*\])/m;
12890
+ if (literalPattern.test(source)) {
12891
+ const usesMultiline = /schemas\s*:\s*\[\s*\n/.test(source);
12892
+ return source.replace(
12893
+ literalPattern,
12894
+ `$1${usesMultiline ? formattedMultiline : formattedInline}`
12895
+ );
12896
+ }
12897
+ const modeLine = /(mode\s*:\s*["'](?:direct|gateway)["']\s*,)/m;
12898
+ if (modeLine.test(source)) {
12899
+ return source.replace(
12900
+ modeLine,
12901
+ `$1
12902
+ schemas: ${formattedInline},`
12903
+ );
12904
+ }
12905
+ return void 0;
12906
+ }
12907
+ function extractLiteralSchemas(source) {
12908
+ const listDefaultMatch = source.match(
12909
+ /schemas\s*:\s*generatorEnv\.list\s*\(\s*['"][^'"]+['"]\s*,\s*\{[\s\S]*?default\s*:\s*(\[[^\]]*\])/m
12910
+ );
12911
+ const literalMatch = listDefaultMatch ?? source.match(/schemas\s*:\s*(\[[^\]]*\])/m);
12912
+ if (!literalMatch) {
12913
+ return void 0;
12914
+ }
12915
+ try {
12916
+ const parsed = JSON.parse(literalMatch[1].replace(/'/g, '"'));
12917
+ if (!Array.isArray(parsed)) {
12918
+ return void 0;
12919
+ }
12920
+ return parsed.map((value) => typeof value === "string" ? value.trim() : "").filter((value) => value.length > 0);
12921
+ } catch {
12922
+ return void 0;
12923
+ }
12924
+ }
12925
+ async function resolveDiscoveredSchemas(options) {
12926
+ if (options.explicit && options.explicit.length > 0) {
12927
+ return {
12928
+ schemas: normalizeSchemaSelection(options.explicit),
12929
+ discovered: false
12930
+ };
12931
+ }
12932
+ if (!options.discoverSchemas) {
12933
+ if (options.loaded) {
12934
+ return {
12935
+ schemas: schemasFromNormalized(options.loaded.config),
12936
+ discovered: false
12937
+ };
12938
+ }
12939
+ return {
12940
+ schemas: [...DEFAULT_POSTGRES_SCHEMAS],
12941
+ discovered: false
12942
+ };
12943
+ }
12944
+ try {
12945
+ let provider = options.provider;
12946
+ if (!provider && options.loaded) {
12947
+ provider = options.loaded.config.provider;
12948
+ }
12949
+ if (!provider) {
12950
+ const probeConfig = {
12951
+ provider: options.mode === "gateway" ? { kind: "postgres", mode: "gateway" } : { kind: "postgres", mode: "direct" }
12952
+ };
12953
+ const normalized = normalizeGeneratorConfig(probeConfig);
12954
+ provider = normalized.provider;
12955
+ }
12956
+ const discovered = await discoverPostgresSchemas(provider);
12957
+ if (discovered.length === 0) {
12958
+ const fallback = options.loaded ? schemasFromNormalized(options.loaded.config) : [...DEFAULT_POSTGRES_SCHEMAS];
12959
+ return { schemas: fallback, discovered: true };
12960
+ }
12961
+ if (options.loaded) {
12962
+ return {
12963
+ schemas: mergeSchemaSelections(schemasFromNormalized(options.loaded.config), discovered),
12964
+ discovered: true
12965
+ };
12966
+ }
12967
+ return { schemas: discovered, discovered: true };
12968
+ } catch (error) {
12969
+ const message = error instanceof Error ? error.message : String(error);
12970
+ const fallback = options.loaded ? schemasFromNormalized(options.loaded.config) : [...DEFAULT_POSTGRES_SCHEMAS];
12971
+ return {
12972
+ schemas: fallback,
12973
+ discovered: false,
12974
+ discoveryError: message
12975
+ };
12976
+ }
12977
+ }
12978
+ async function ensureGeneratorConfigFile(options = {}) {
12979
+ const cwd = options.cwd ?? process.cwd();
12980
+ const dryRun = options.dryRun === true;
12981
+ const discoverSchemas = options.discoverSchemas !== false;
12982
+ const force = options.force === true;
12983
+ const preferredMode = options.mode ?? "auto";
12984
+ const pathInfo = resolveConfigPath(cwd, options.configPath);
12985
+ const changes = [];
12986
+ let loaded = options.loaded;
12987
+ if (!loaded && pathInfo.exists && !force) {
12988
+ try {
12989
+ loaded = await loadGeneratorConfig({
12990
+ cwd,
12991
+ configPath: pathInfo.absolutePath
12992
+ });
12993
+ } catch {
12994
+ }
12995
+ }
12996
+ const mode = providerModeFromConfig(loaded?.config.provider ?? options.provider, preferredMode);
12997
+ const schemaResolution = await resolveDiscoveredSchemas({
12998
+ discoverSchemas,
12999
+ provider: options.provider ?? loaded?.config.provider,
13000
+ loaded,
13001
+ explicit: options.schemas,
13002
+ mode
13003
+ });
13004
+ if (schemaResolution.discoveryError) {
13005
+ changes.push(`schema-discovery-failed: ${schemaResolution.discoveryError}`);
13006
+ } else if (schemaResolution.discovered) {
13007
+ changes.push(`schemas-discovered: ${schemaResolution.schemas.join(",")}`);
13008
+ }
13009
+ if (!pathInfo.exists || force) {
13010
+ const content = renderGeneratorConfigFile({
13011
+ mode,
13012
+ schemas: schemaResolution.schemas
13013
+ });
13014
+ if (!pathInfo.exists) {
13015
+ changes.push("created-modern-config");
13016
+ } else {
13017
+ changes.push("force-rewrote-config");
13018
+ }
13019
+ if (!dryRun) {
13020
+ await mkdir(dirname(pathInfo.absolutePath), { recursive: true });
13021
+ await writeFile(pathInfo.absolutePath, content, "utf8");
13022
+ }
13023
+ return {
13024
+ action: pathInfo.exists ? "updated" : "created",
13025
+ path: pathInfo.relativePath,
13026
+ absolutePath: pathInfo.absolutePath,
13027
+ content,
13028
+ schemas: schemaResolution.schemas,
13029
+ changes,
13030
+ mode,
13031
+ reason: pathInfo.exists ? "force rewrite" : "config file missing"
13032
+ };
13033
+ }
13034
+ const existingSource = await readFile(pathInfo.absolutePath, "utf8");
13035
+ const existingLiteralSchemas = extractLiteralSchemas(existingSource);
13036
+ const existingSchemas = existingLiteralSchemas ?? (loaded ? schemasFromNormalized(loaded.config) : [...DEFAULT_POSTGRES_SCHEMAS]);
13037
+ if (schemasEqual(existingSchemas, schemaResolution.schemas)) {
13038
+ return {
13039
+ action: "unchanged",
13040
+ path: pathInfo.relativePath,
13041
+ absolutePath: pathInfo.absolutePath,
13042
+ content: existingSource,
13043
+ schemas: existingSchemas,
13044
+ changes: changes.length > 0 ? changes : ["schemas-already-current"],
13045
+ mode,
13046
+ reason: "schemas already match discovered/configured set"
13047
+ };
13048
+ }
13049
+ const patched = patchSchemasInConfigSource(existingSource, schemaResolution.schemas);
13050
+ if (!patched) {
13051
+ return {
13052
+ action: "skipped",
13053
+ path: pathInfo.relativePath,
13054
+ absolutePath: pathInfo.absolutePath,
13055
+ content: existingSource,
13056
+ schemas: existingSchemas,
13057
+ changes: [
13058
+ ...changes,
13059
+ "could-not-patch-schemas-surgically",
13060
+ "re-run with force=true to rewrite full template"
13061
+ ],
13062
+ mode,
13063
+ reason: "existing config schemas could not be updated surgically without risking a destructive rewrite"
13064
+ };
13065
+ }
13066
+ if (patched === existingSource) {
13067
+ return {
13068
+ action: "unchanged",
13069
+ path: pathInfo.relativePath,
13070
+ absolutePath: pathInfo.absolutePath,
13071
+ content: existingSource,
13072
+ schemas: existingSchemas,
13073
+ changes: [...changes, "patch-noop"],
13074
+ mode,
13075
+ reason: "patch produced identical content"
13076
+ };
13077
+ }
13078
+ changes.push(
13079
+ `schemas-updated: ${existingSchemas.join(",")} -> ${schemaResolution.schemas.join(",")}`
13080
+ );
13081
+ if (!dryRun) {
13082
+ await writeFile(pathInfo.absolutePath, patched, "utf8");
13083
+ }
13084
+ return {
13085
+ action: "updated",
13086
+ path: pathInfo.relativePath,
13087
+ absolutePath: pathInfo.absolutePath,
13088
+ content: patched,
13089
+ schemas: schemaResolution.schemas,
13090
+ changes,
13091
+ mode,
13092
+ reason: "auto-filled provider.schemas"
13093
+ };
13094
+ }
13095
+
12293
13096
  // src/generator/env.ts
12294
13097
  function readEnvStringValue2(key) {
12295
13098
  if (typeof process === "undefined" || !process.env) {
@@ -13268,9 +14071,764 @@ function resolveGeneratorProvider(providerConfig, experimentalFlags) {
13268
14071
  }
13269
14072
  throw new Error(`Unsupported generator provider kind: ${providerConfig.kind ?? "unknown"}`);
13270
14073
  }
13271
- function canOverwriteArtifact(file) {
13272
- return file.kind === "model" || file.kind === "schema";
14074
+
14075
+ // src/generator/artifact-merge.ts
14076
+ var IMPORT_RE = /^import\s+(?:type\s+)?(?:\{([^}]*)\}|([A-Za-z_$][\w$]*))\s+from\s+(['"])([^'"]+)\3\s*;?\s*$/gm;
14077
+ var DEFINE_EXPORT_RE = /export\s+const\s+([A-Za-z_$][\w$]*)\s*=\s*(define(?:Database|Registry))\s*\(\s*\{/g;
14078
+ var META_EXPORT_RE = /export\s+const\s+__athena_schema_meta\s*=\s*\{/g;
14079
+ var KNOWN_META_KEYS = /* @__PURE__ */ new Set([
14080
+ "schemaVersion",
14081
+ "generatedAt",
14082
+ "database",
14083
+ "outputPreset",
14084
+ "outputFormat"
14085
+ ]);
14086
+ function detectNewline(source) {
14087
+ return source.includes("\r\n") ? "\r\n" : "\n";
14088
+ }
14089
+ function detectStyle(source) {
14090
+ const newline = detectNewline(source);
14091
+ const single = (source.match(/'/g) ?? []).length;
14092
+ const double = (source.match(/"/g) ?? []).length;
14093
+ const quote = double > single ? '"' : "'";
14094
+ const importLines = source.split(/\r?\n/).filter((line) => line.trimStart().startsWith("import "));
14095
+ const semicolons = importLines.length > 0 ? importLines.filter((line) => line.trimEnd().endsWith(";")).length >= importLines.length / 2 : false;
14096
+ const objectLines = source.split(/\r?\n/).map((line) => line.trim()).filter((line) => /^[A-Za-z0-9_'"]+\s*:\s*.+/.test(line));
14097
+ const trailingComma = objectLines.length > 0 ? objectLines.filter((line) => line.endsWith(",")).length >= Math.ceil(objectLines.length / 2) : true;
14098
+ const indentMatch = source.match(/\n([ \t]+)\S/);
14099
+ const indent = indentMatch?.[1] ?? " ";
14100
+ return { quote, semicolons, trailingComma, indent, newline };
14101
+ }
14102
+ function quoteString(value, quote) {
14103
+ const escaped = value.replace(/\\/g, "\\\\").replace(new RegExp(quote, "g"), `\\${quote}`);
14104
+ return `${quote}${escaped}${quote}`;
14105
+ }
14106
+ function parseNamedImports(source) {
14107
+ const imports = [];
14108
+ IMPORT_RE.lastIndex = 0;
14109
+ let match;
14110
+ while ((match = IMPORT_RE.exec(source)) !== null) {
14111
+ const named = match[1];
14112
+ const defaultName = match[2];
14113
+ const module = match[4];
14114
+ const names = named ? named.split(",").map((part) => part.trim()).filter(Boolean).map((part) => {
14115
+ const alias = part.split(/\s+as\s+/);
14116
+ return (alias[1] ?? alias[0]).trim();
14117
+ }) : defaultName ? [defaultName] : [];
14118
+ imports.push({
14119
+ names,
14120
+ module,
14121
+ raw: match[0],
14122
+ start: match.index,
14123
+ end: match.index + match[0].length
14124
+ });
14125
+ }
14126
+ return imports;
14127
+ }
14128
+ function findMatchingBrace(source, openIndex) {
14129
+ let depth = 0;
14130
+ let inSingle = false;
14131
+ let inDouble = false;
14132
+ let inTemplate = false;
14133
+ let escaped = false;
14134
+ for (let i = openIndex; i < source.length; i += 1) {
14135
+ const ch = source[i];
14136
+ if (escaped) {
14137
+ escaped = false;
14138
+ continue;
14139
+ }
14140
+ if (ch === "\\" && (inSingle || inDouble || inTemplate)) {
14141
+ escaped = true;
14142
+ continue;
14143
+ }
14144
+ if (!inDouble && !inTemplate && ch === "'") {
14145
+ inSingle = !inSingle;
14146
+ continue;
14147
+ }
14148
+ if (!inSingle && !inTemplate && ch === '"') {
14149
+ inDouble = !inDouble;
14150
+ continue;
14151
+ }
14152
+ if (!inSingle && !inDouble && ch === "`") {
14153
+ inTemplate = !inTemplate;
14154
+ continue;
14155
+ }
14156
+ if (inSingle || inDouble || inTemplate) {
14157
+ continue;
14158
+ }
14159
+ if (ch === "{") {
14160
+ depth += 1;
14161
+ } else if (ch === "}") {
14162
+ depth -= 1;
14163
+ if (depth === 0) {
14164
+ return i;
14165
+ }
14166
+ }
14167
+ }
14168
+ return -1;
14169
+ }
14170
+ function parseObjectEntries(body) {
14171
+ const entries = [];
14172
+ const lines = body.split(/\r?\n/);
14173
+ for (const line of lines) {
14174
+ const trimmed = line.trim();
14175
+ if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("/*")) {
14176
+ continue;
14177
+ }
14178
+ const withoutComma = trimmed.endsWith(",") ? trimmed.slice(0, -1).trimEnd() : trimmed;
14179
+ const match = withoutComma.match(/^([A-Za-z_$][\w$]*|['"][^'"]+['"])\s*:\s*(.+)$/);
14180
+ if (!match) {
14181
+ continue;
14182
+ }
14183
+ const keyRaw = match[1];
14184
+ const key = keyRaw.startsWith("'") || keyRaw.startsWith('"') ? keyRaw.slice(1, -1) : keyRaw;
14185
+ entries.push({
14186
+ key,
14187
+ value: match[2].trim(),
14188
+ raw: trimmed
14189
+ });
14190
+ }
14191
+ return entries;
13273
14192
  }
14193
+ function parseDefineBlocks(source) {
14194
+ const blocks = [];
14195
+ DEFINE_EXPORT_RE.lastIndex = 0;
14196
+ let match;
14197
+ while ((match = DEFINE_EXPORT_RE.exec(source)) !== null) {
14198
+ const exportName = match[1];
14199
+ const callName = match[2];
14200
+ const openBrace = match.index + match[0].lastIndexOf("{");
14201
+ const closeBrace = findMatchingBrace(source, openBrace);
14202
+ if (closeBrace < 0) {
14203
+ continue;
14204
+ }
14205
+ const body = source.slice(openBrace + 1, closeBrace);
14206
+ const fullEnd = (() => {
14207
+ let i = closeBrace + 1;
14208
+ while (i < source.length && /\s/.test(source[i])) i += 1;
14209
+ if (source[i] === ")") i += 1;
14210
+ while (i < source.length && /\s/.test(source[i])) i += 1;
14211
+ if (source[i] === ";") i += 1;
14212
+ return i;
14213
+ })();
14214
+ blocks.push({
14215
+ kind: callName === "defineDatabase" ? "database" : "registry",
14216
+ exportName,
14217
+ callName,
14218
+ entries: parseObjectEntries(body),
14219
+ bodyStart: openBrace + 1,
14220
+ bodyEnd: closeBrace,
14221
+ fullStart: match.index,
14222
+ fullEnd,
14223
+ raw: source.slice(match.index, fullEnd)
14224
+ });
14225
+ }
14226
+ return blocks;
14227
+ }
14228
+ function parseMetaBlock(source) {
14229
+ META_EXPORT_RE.lastIndex = 0;
14230
+ const match = META_EXPORT_RE.exec(source);
14231
+ if (!match) {
14232
+ return void 0;
14233
+ }
14234
+ const openBrace = match.index + match[0].lastIndexOf("{");
14235
+ const closeBrace = findMatchingBrace(source, openBrace);
14236
+ if (closeBrace < 0) {
14237
+ return void 0;
14238
+ }
14239
+ let fullEnd = closeBrace + 1;
14240
+ const after = source.slice(fullEnd);
14241
+ const asConst = after.match(/^\s*as\s+const\s*;?/);
14242
+ if (asConst) {
14243
+ fullEnd += asConst[0].length;
14244
+ } else if (source[fullEnd] === ";") {
14245
+ fullEnd += 1;
14246
+ }
14247
+ return {
14248
+ entries: parseObjectEntries(source.slice(openBrace + 1, closeBrace)),
14249
+ bodyStart: openBrace + 1,
14250
+ bodyEnd: closeBrace,
14251
+ fullStart: match.index,
14252
+ fullEnd,
14253
+ raw: source.slice(match.index, fullEnd)
14254
+ };
14255
+ }
14256
+ function hasImportBinding(imports, name, module) {
14257
+ return imports.some(
14258
+ (item) => item.names.includes(name) && (module === void 0)
14259
+ );
14260
+ }
14261
+ function findImportForName(imports, name) {
14262
+ return imports.find((item) => item.names.includes(name));
14263
+ }
14264
+ function normalizeModulePath(modulePath) {
14265
+ return modulePath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\.ts$/, "");
14266
+ }
14267
+ function formatImport(names, modulePath, style) {
14268
+ const body = `import { ${names.join(", ")} } from ${quoteString(modulePath, style.quote)}`;
14269
+ return style.semicolons ? `${body};` : body;
14270
+ }
14271
+ function formatObjectEntry(key, value, style, isLast) {
14272
+ const needsQuote = !/^[A-Za-z_$][\w$]*$/.test(key);
14273
+ const renderedKey = needsQuote ? quoteString(key, style.quote) : key;
14274
+ const comma = !isLast || style.trailingComma ? "," : "";
14275
+ return `${style.indent}${renderedKey}: ${value}${comma}`;
14276
+ }
14277
+ function replaceRange(source, start, end, insertion) {
14278
+ return source.slice(0, start) + insertion + source.slice(end);
14279
+ }
14280
+ function collectDuplicateKeys(entries) {
14281
+ const seen = /* @__PURE__ */ new Set();
14282
+ const dupes = [];
14283
+ for (const entry of entries) {
14284
+ if (seen.has(entry.key)) {
14285
+ dupes.push(entry.key);
14286
+ }
14287
+ seen.add(entry.key);
14288
+ }
14289
+ return dupes;
14290
+ }
14291
+ function collectDuplicateImportBindings(imports) {
14292
+ const seen = /* @__PURE__ */ new Set();
14293
+ const dupes = [];
14294
+ for (const item of imports) {
14295
+ for (const name of item.names) {
14296
+ if (seen.has(name)) {
14297
+ dupes.push(name);
14298
+ }
14299
+ seen.add(name);
14300
+ }
14301
+ }
14302
+ return dupes;
14303
+ }
14304
+ function lintArtifactSource(source, kind) {
14305
+ const errors = [];
14306
+ const imports = parseNamedImports(source);
14307
+ const dupImports = collectDuplicateImportBindings(imports);
14308
+ if (dupImports.length > 0) {
14309
+ errors.push(`duplicate import bindings: ${dupImports.join(", ")}`);
14310
+ }
14311
+ const blocks = parseDefineBlocks(source).filter((block2) => block2.kind === kind);
14312
+ if (blocks.length === 0) {
14313
+ errors.push(`missing export const \u2026 = define${kind === "database" ? "Database" : "Registry"}({\u2026})`);
14314
+ return errors;
14315
+ }
14316
+ if (blocks.length > 1) {
14317
+ errors.push(`multiple define${kind === "database" ? "Database" : "Registry"} exports found`);
14318
+ }
14319
+ const block = blocks[0];
14320
+ const dupKeys = collectDuplicateKeys(block.entries);
14321
+ if (dupKeys.length > 0) {
14322
+ errors.push(`duplicate object keys: ${dupKeys.join(", ")}`);
14323
+ }
14324
+ for (const entry of block.entries) {
14325
+ const valueId = entry.value.match(/^[A-Za-z_$][\w$]*$/)?.[0];
14326
+ if (valueId && !hasImportBinding(imports, valueId) && valueId !== block.exportName) {
14327
+ if (!source.includes(`const ${valueId}`) && !source.includes(`function ${valueId}`)) {
14328
+ errors.push(`value "${valueId}" for key "${entry.key}" is not imported`);
14329
+ }
14330
+ }
14331
+ }
14332
+ if (kind === "database" && !hasImportBinding(imports, "defineDatabase")) {
14333
+ errors.push("missing defineDatabase import");
14334
+ }
14335
+ if (kind === "registry" && !hasImportBinding(imports, "defineRegistry")) {
14336
+ errors.push("missing defineRegistry import");
14337
+ }
14338
+ return errors;
14339
+ }
14340
+ function preservedCustomUnits(existing, generated, kind) {
14341
+ const custom = [];
14342
+ const existingImports = parseNamedImports(existing);
14343
+ const generatedImports = parseNamedImports(generated);
14344
+ const generatedModules = new Set(generatedImports.map((item) => normalizeModulePath(item.module)));
14345
+ const generatedNames = new Set(generatedImports.flatMap((item) => item.names));
14346
+ for (const item of existingImports) {
14347
+ const moduleNorm = normalizeModulePath(item.module);
14348
+ if (moduleNorm.includes("@xylex-group/athena")) {
14349
+ continue;
14350
+ }
14351
+ const unexpectedNames = item.names.filter((name) => !generatedNames.has(name));
14352
+ if (unexpectedNames.length > 0 && !generatedModules.has(moduleNorm)) {
14353
+ custom.push(`import { ${unexpectedNames.join(", ")} } from '${item.module}'`);
14354
+ } else if (unexpectedNames.length > 0) {
14355
+ custom.push(`import binding(s): ${unexpectedNames.join(", ")}`);
14356
+ }
14357
+ }
14358
+ const existingBlocks = parseDefineBlocks(existing).filter((block) => block.kind === kind);
14359
+ const generatedBlocks = parseDefineBlocks(generated).filter((block) => block.kind === kind);
14360
+ const generatedKeys = new Set(generatedBlocks[0]?.entries.map((entry) => entry.key) ?? []);
14361
+ for (const entry of existingBlocks[0]?.entries ?? []) {
14362
+ if (!generatedKeys.has(entry.key)) {
14363
+ custom.push(`${kind} entry: ${entry.key}: ${entry.value}`);
14364
+ }
14365
+ }
14366
+ if (kind === "registry") {
14367
+ const existingMeta = parseMetaBlock(existing);
14368
+ const generatedMeta = parseMetaBlock(generated);
14369
+ const generatedMetaKeys = new Set(generatedMeta?.entries.map((entry) => entry.key) ?? []);
14370
+ for (const entry of existingMeta?.entries ?? []) {
14371
+ if (!generatedMetaKeys.has(entry.key) && !KNOWN_META_KEYS.has(entry.key)) {
14372
+ custom.push(`meta entry: ${entry.key}: ${entry.value}`);
14373
+ }
14374
+ }
14375
+ }
14376
+ const exportConstRe = /^export\s+const\s+([A-Za-z_$][\w$]*)\b/gm;
14377
+ const generatedExports = /* @__PURE__ */ new Set();
14378
+ let match;
14379
+ exportConstRe.lastIndex = 0;
14380
+ while ((match = exportConstRe.exec(generated)) !== null) {
14381
+ generatedExports.add(match[1]);
14382
+ }
14383
+ exportConstRe.lastIndex = 0;
14384
+ while ((match = exportConstRe.exec(existing)) !== null) {
14385
+ if (!generatedExports.has(match[1]) && match[1] !== "__athena_schema_meta") {
14386
+ custom.push(`export const ${match[1]}`);
14387
+ }
14388
+ }
14389
+ return custom;
14390
+ }
14391
+ function insertImportAfterPackageImports(source, importLine, style) {
14392
+ const imports = parseNamedImports(source);
14393
+ if (imports.length === 0) {
14394
+ return `${importLine}${style.newline}${source}`;
14395
+ }
14396
+ let anchor = imports[imports.length - 1];
14397
+ for (let i = imports.length - 1; i >= 0; i -= 1) {
14398
+ if (imports[i].module.startsWith(".")) {
14399
+ anchor = imports[i];
14400
+ break;
14401
+ }
14402
+ }
14403
+ const insertAt = anchor.end;
14404
+ const before = source.slice(0, insertAt);
14405
+ const after = source.slice(insertAt);
14406
+ const needsLeadingNl = !before.endsWith("\n");
14407
+ const prefix = needsLeadingNl ? style.newline : "";
14408
+ return `${before}${prefix}${importLine}${after.startsWith("\n") || after.startsWith("\r\n") ? "" : style.newline}${after}`;
14409
+ }
14410
+ function rewriteObjectBody(entries, style) {
14411
+ if (entries.length === 0) {
14412
+ return style.newline;
14413
+ }
14414
+ const lines = entries.map(
14415
+ (entry, index) => formatObjectEntry(entry.key, entry.value, style, index === entries.length - 1)
14416
+ );
14417
+ return `${style.newline}${lines.join(style.newline)}${style.newline}`;
14418
+ }
14419
+ function mergeDatabaseArtifact(existing, generated) {
14420
+ const style = detectStyle(existing);
14421
+ const generatedStyle = detectStyle(generated);
14422
+ const effectiveStyle = {
14423
+ ...style,
14424
+ // Prefer existing fingerprint; fall back to generated if existing is empty-ish
14425
+ quote: existing.includes('"') || existing.includes("'") ? style.quote : generatedStyle.quote
14426
+ };
14427
+ const generatedImports = parseNamedImports(generated);
14428
+ const existingBlocks = parseDefineBlocks(existing).filter((block) => block.kind === "database");
14429
+ const generatedBlocks = parseDefineBlocks(generated).filter((block) => block.kind === "database");
14430
+ if (existingBlocks.length === 0 || generatedBlocks.length === 0) {
14431
+ return {
14432
+ action: "skip",
14433
+ skipReason: "merge-unparseable",
14434
+ added: [],
14435
+ preservedCustom: [],
14436
+ conflicts: [],
14437
+ lintErrors: [],
14438
+ detail: "could not locate defineDatabase({\u2026}) export for merge"
14439
+ };
14440
+ }
14441
+ const generatedBlock = generatedBlocks[0];
14442
+ const conflicts = [];
14443
+ const added = [];
14444
+ let next = existing;
14445
+ if (!hasImportBinding(parseNamedImports(next), "defineDatabase")) {
14446
+ const pkgImport = generatedImports.find((item) => item.names.includes("defineDatabase"));
14447
+ if (pkgImport) {
14448
+ const line = formatImport(["defineDatabase"], pkgImport.module, effectiveStyle);
14449
+ next = insertImportAfterPackageImports(next, line, effectiveStyle);
14450
+ added.push("import defineDatabase");
14451
+ }
14452
+ }
14453
+ let workingImports = parseNamedImports(next);
14454
+ let workingBlocks = parseDefineBlocks(next).filter((block) => block.kind === "database");
14455
+ let workingBlock = workingBlocks[0];
14456
+ const entryMap = new Map(workingBlock.entries.map((entry) => [entry.key, entry]));
14457
+ for (const desired of generatedBlock.entries) {
14458
+ const existingEntry = entryMap.get(desired.key);
14459
+ if (existingEntry) {
14460
+ if (existingEntry.value !== desired.value) {
14461
+ conflicts.push(
14462
+ `key "${desired.key}" maps to ${existingEntry.value} (existing) vs ${desired.value} (generated)`
14463
+ );
14464
+ }
14465
+ continue;
14466
+ }
14467
+ const desiredImport = generatedImports.find((item) => item.names.includes(desired.value));
14468
+ if (desiredImport) {
14469
+ const existingForName = findImportForName(workingImports, desired.value);
14470
+ if (existingForName) {
14471
+ if (normalizeModulePath(existingForName.module) !== normalizeModulePath(desiredImport.module)) {
14472
+ conflicts.push(
14473
+ `binding "${desired.value}" imported from '${existingForName.module}' vs '${desiredImport.module}'`
14474
+ );
14475
+ continue;
14476
+ }
14477
+ } else {
14478
+ const line = formatImport([desired.value], desiredImport.module, effectiveStyle);
14479
+ next = insertImportAfterPackageImports(next, line, effectiveStyle);
14480
+ added.push(`import ${desired.value}`);
14481
+ workingImports = parseNamedImports(next);
14482
+ }
14483
+ }
14484
+ workingBlocks = parseDefineBlocks(next).filter((block) => block.kind === "database");
14485
+ workingBlock = workingBlocks[0];
14486
+ const nextEntries = [...workingBlock.entries, { key: desired.key, value: desired.value, raw: "" }];
14487
+ const body = rewriteObjectBody(nextEntries, effectiveStyle);
14488
+ next = replaceRange(next, workingBlock.bodyStart, workingBlock.bodyEnd, body);
14489
+ entryMap.set(desired.key, { key: desired.key, value: desired.value, raw: "" });
14490
+ added.push(`database entry: ${desired.key}`);
14491
+ }
14492
+ if (conflicts.length > 0 && added.length === 0) {
14493
+ return {
14494
+ action: "skip",
14495
+ skipReason: "merge-conflict",
14496
+ added: [],
14497
+ preservedCustom: preservedCustomUnits(existing, generated, "database"),
14498
+ conflicts,
14499
+ lintErrors: [],
14500
+ detail: conflicts.join("; ")
14501
+ };
14502
+ }
14503
+ const lintErrors = lintArtifactSource(next, "database");
14504
+ if (lintErrors.length > 0) {
14505
+ return {
14506
+ action: "skip",
14507
+ skipReason: "merge-lint-failed",
14508
+ added,
14509
+ preservedCustom: preservedCustomUnits(existing, generated, "database"),
14510
+ conflicts,
14511
+ lintErrors,
14512
+ detail: lintErrors.join("; ")
14513
+ };
14514
+ }
14515
+ const preservedCustom = preservedCustomUnits(next, generated, "database");
14516
+ if (next === existing || next.replace(/\r\n/g, "\n") === existing.replace(/\r\n/g, "\n")) {
14517
+ return {
14518
+ action: "unchanged",
14519
+ content: existing,
14520
+ skipReason: "already-current",
14521
+ added: [],
14522
+ preservedCustom,
14523
+ conflicts,
14524
+ lintErrors: [],
14525
+ detail: conflicts.length > 0 ? conflicts.join("; ") : void 0
14526
+ };
14527
+ }
14528
+ return {
14529
+ action: "write",
14530
+ content: next.endsWith("\n") ? next : `${next}${effectiveStyle.newline}`,
14531
+ writeReason: "merged",
14532
+ added,
14533
+ preservedCustom,
14534
+ conflicts,
14535
+ lintErrors: []
14536
+ };
14537
+ }
14538
+ function mergeMetaFields(existingMeta, generatedMeta) {
14539
+ const added = [];
14540
+ if (!generatedMeta) {
14541
+ return { entries: existingMeta?.entries ?? [], changed: false, added };
14542
+ }
14543
+ if (!existingMeta) {
14544
+ return {
14545
+ entries: generatedMeta.entries,
14546
+ changed: true,
14547
+ added: generatedMeta.entries.map((entry) => `meta entry: ${entry.key}`)
14548
+ };
14549
+ }
14550
+ const map = new Map(existingMeta.entries.map((entry) => [entry.key, entry]));
14551
+ let changed = false;
14552
+ for (const desired of generatedMeta.entries) {
14553
+ const current = map.get(desired.key);
14554
+ if (!current) {
14555
+ map.set(desired.key, desired);
14556
+ added.push(`meta entry: ${desired.key}`);
14557
+ changed = true;
14558
+ continue;
14559
+ }
14560
+ if (KNOWN_META_KEYS.has(desired.key) && current.value !== desired.value) {
14561
+ map.set(desired.key, desired);
14562
+ added.push(`meta refresh: ${desired.key}`);
14563
+ changed = true;
14564
+ }
14565
+ }
14566
+ const ordered = [];
14567
+ const seen = /* @__PURE__ */ new Set();
14568
+ for (const entry of existingMeta.entries) {
14569
+ const next = map.get(entry.key);
14570
+ if (next) {
14571
+ ordered.push(next);
14572
+ seen.add(entry.key);
14573
+ }
14574
+ }
14575
+ for (const entry of generatedMeta.entries) {
14576
+ if (!seen.has(entry.key)) {
14577
+ const next = map.get(entry.key);
14578
+ if (next) {
14579
+ ordered.push(next);
14580
+ seen.add(entry.key);
14581
+ }
14582
+ }
14583
+ }
14584
+ for (const [key, entry] of map) {
14585
+ if (!seen.has(key)) {
14586
+ ordered.push(entry);
14587
+ }
14588
+ }
14589
+ return { entries: ordered, changed, added };
14590
+ }
14591
+ function mergeRegistryArtifact(existing, generated) {
14592
+ const style = detectStyle(existing);
14593
+ const effectiveStyle = style;
14594
+ const generatedImports = parseNamedImports(generated);
14595
+ const existingBlocks = parseDefineBlocks(existing).filter((block) => block.kind === "registry");
14596
+ const generatedBlocks = parseDefineBlocks(generated).filter((block) => block.kind === "registry");
14597
+ if (existingBlocks.length === 0 || generatedBlocks.length === 0) {
14598
+ return {
14599
+ action: "skip",
14600
+ skipReason: "merge-unparseable",
14601
+ added: [],
14602
+ preservedCustom: [],
14603
+ conflicts: [],
14604
+ lintErrors: [],
14605
+ detail: "could not locate defineRegistry({\u2026}) export for merge"
14606
+ };
14607
+ }
14608
+ const existingBlock = existingBlocks[0];
14609
+ const generatedBlock = generatedBlocks[0];
14610
+ const conflicts = [];
14611
+ const added = [];
14612
+ let next = existing;
14613
+ if (!hasImportBinding(parseNamedImports(next), "defineRegistry")) {
14614
+ const pkgImport = generatedImports.find((item) => item.names.includes("defineRegistry"));
14615
+ if (pkgImport) {
14616
+ next = insertImportAfterPackageImports(
14617
+ next,
14618
+ formatImport(["defineRegistry"], pkgImport.module, effectiveStyle),
14619
+ effectiveStyle
14620
+ );
14621
+ added.push("import defineRegistry");
14622
+ }
14623
+ }
14624
+ const preferredDbValue = existingBlock.entries[0]?.value ?? generatedBlock.entries[0]?.value;
14625
+ const generatedDbImport = generatedImports.find((item) => item.names.includes(generatedBlock.entries[0]?.value ?? "")) ?? generatedImports.find((item) => item.module.startsWith("."));
14626
+ if (preferredDbValue && generatedDbImport) {
14627
+ const existingForName = findImportForName(parseNamedImports(next), preferredDbValue);
14628
+ if (!existingForName) {
14629
+ const modulePath = generatedDbImport.module;
14630
+ const importName = findImportForName(generatedImports, preferredDbValue)?.names[0] ?? generatedBlock.entries[0]?.value ?? preferredDbValue;
14631
+ if (!hasImportBinding(parseNamedImports(next), importName)) {
14632
+ next = insertImportAfterPackageImports(
14633
+ next,
14634
+ formatImport([importName], modulePath, effectiveStyle),
14635
+ effectiveStyle
14636
+ );
14637
+ added.push(`import ${importName}`);
14638
+ }
14639
+ } else if (normalizeModulePath(existingForName.module) !== normalizeModulePath(generatedDbImport.module)) {
14640
+ conflicts.push(
14641
+ `binding "${preferredDbValue}" imported from '${existingForName.module}' vs '${generatedDbImport.module}'`
14642
+ );
14643
+ }
14644
+ }
14645
+ let workingBlocks = parseDefineBlocks(next).filter((block) => block.kind === "registry");
14646
+ let workingBlock = workingBlocks[0];
14647
+ const entryMap = new Map(workingBlock.entries.map((entry) => [entry.key, entry]));
14648
+ for (const desired of generatedBlock.entries) {
14649
+ const existingEntry = entryMap.get(desired.key);
14650
+ if (existingEntry) {
14651
+ continue;
14652
+ }
14653
+ const dbImport = parseNamedImports(next).find(
14654
+ (item) => item.module.startsWith(".") && item.names.some((name) => name !== "defineRegistry")
14655
+ );
14656
+ const value = dbImport?.names[0] ?? desired.value;
14657
+ const nextEntries = [...workingBlock.entries, { key: desired.key, value, raw: "" }];
14658
+ const body = rewriteObjectBody(nextEntries, effectiveStyle);
14659
+ next = replaceRange(next, workingBlock.bodyStart, workingBlock.bodyEnd, body);
14660
+ entryMap.set(desired.key, { key: desired.key, value, raw: "" });
14661
+ added.push(`registry entry: ${desired.key}`);
14662
+ workingBlocks = parseDefineBlocks(next).filter((block) => block.kind === "registry");
14663
+ workingBlock = workingBlocks[0];
14664
+ }
14665
+ const existingMeta = parseMetaBlock(next);
14666
+ const generatedMeta = parseMetaBlock(generated);
14667
+ const structuralAdded = added.length > 0;
14668
+ if (generatedMeta) {
14669
+ if (!existingMeta) {
14670
+ const blocks = parseDefineBlocks(next).filter((block) => block.kind === "registry");
14671
+ const insertAt = blocks[0]?.fullStart ?? next.length;
14672
+ const metaBody = rewriteObjectBody(generatedMeta.entries, effectiveStyle);
14673
+ const metaBlock = `export const __athena_schema_meta = {${metaBody}} as const${effectiveStyle.semicolons ? ";" : ""}${effectiveStyle.newline}${effectiveStyle.newline}`;
14674
+ next = replaceRange(next, insertAt, insertAt, metaBlock);
14675
+ added.push("__athena_schema_meta");
14676
+ } else {
14677
+ const desiredEntries = structuralAdded ? generatedMeta.entries : generatedMeta.entries.filter((entry) => {
14678
+ return !existingMeta.entries.some((current) => current.key === entry.key);
14679
+ });
14680
+ if (structuralAdded) {
14681
+ const merged = mergeMetaFields(existingMeta, generatedMeta);
14682
+ if (merged.changed) {
14683
+ const metaNow = parseMetaBlock(next);
14684
+ if (metaNow) {
14685
+ const body = rewriteObjectBody(merged.entries, effectiveStyle);
14686
+ next = replaceRange(next, metaNow.bodyStart, metaNow.bodyEnd, body);
14687
+ added.push(...merged.added);
14688
+ }
14689
+ }
14690
+ } else if (desiredEntries.length > 0) {
14691
+ const map = new Map(existingMeta.entries.map((entry) => [entry.key, entry]));
14692
+ for (const entry of desiredEntries) {
14693
+ map.set(entry.key, entry);
14694
+ added.push(`meta entry: ${entry.key}`);
14695
+ }
14696
+ const ordered = [
14697
+ ...existingMeta.entries.map((entry) => map.get(entry.key)),
14698
+ ...desiredEntries.filter((entry) => !existingMeta.entries.some((e) => e.key === entry.key))
14699
+ ];
14700
+ const metaNow = parseMetaBlock(next);
14701
+ if (metaNow) {
14702
+ const body = rewriteObjectBody(ordered, effectiveStyle);
14703
+ next = replaceRange(next, metaNow.bodyStart, metaNow.bodyEnd, body);
14704
+ }
14705
+ }
14706
+ }
14707
+ }
14708
+ if (conflicts.length > 0 && added.length === 0) {
14709
+ return {
14710
+ action: "skip",
14711
+ skipReason: "merge-conflict",
14712
+ added: [],
14713
+ preservedCustom: preservedCustomUnits(existing, generated, "registry"),
14714
+ conflicts,
14715
+ lintErrors: [],
14716
+ detail: conflicts.join("; ")
14717
+ };
14718
+ }
14719
+ const lintErrors = lintArtifactSource(next, "registry");
14720
+ if (lintErrors.length > 0) {
14721
+ return {
14722
+ action: "skip",
14723
+ skipReason: "merge-lint-failed",
14724
+ added,
14725
+ preservedCustom: preservedCustomUnits(existing, generated, "registry"),
14726
+ conflicts,
14727
+ lintErrors,
14728
+ detail: lintErrors.join("; ")
14729
+ };
14730
+ }
14731
+ const preservedCustom = preservedCustomUnits(next, generated, "registry");
14732
+ if (next.replace(/\r\n/g, "\n") === existing.replace(/\r\n/g, "\n")) {
14733
+ return {
14734
+ action: "unchanged",
14735
+ content: existing,
14736
+ skipReason: "already-current",
14737
+ added: [],
14738
+ preservedCustom,
14739
+ conflicts,
14740
+ lintErrors: []
14741
+ };
14742
+ }
14743
+ return {
14744
+ action: "write",
14745
+ content: next.endsWith("\n") ? next : `${next}${effectiveStyle.newline}`,
14746
+ writeReason: "merged",
14747
+ added,
14748
+ preservedCustom,
14749
+ conflicts,
14750
+ lintErrors: []
14751
+ };
14752
+ }
14753
+ function mergeProtectedArtifact(kind, existing, generated, policy) {
14754
+ if (policy === "overwrite") {
14755
+ if (existing.replace(/\r\n/g, "\n") === generated.replace(/\r\n/g, "\n")) {
14756
+ return {
14757
+ action: "unchanged",
14758
+ content: existing,
14759
+ skipReason: "already-current",
14760
+ added: [],
14761
+ preservedCustom: [],
14762
+ conflicts: [],
14763
+ lintErrors: []
14764
+ };
14765
+ }
14766
+ return {
14767
+ action: "write",
14768
+ content: generated,
14769
+ writeReason: "overwritten",
14770
+ added: ["full overwrite"],
14771
+ preservedCustom: [],
14772
+ conflicts: [],
14773
+ lintErrors: []
14774
+ };
14775
+ }
14776
+ if (policy === "skip") {
14777
+ return {
14778
+ action: "skip",
14779
+ skipReason: "protected-existing-file",
14780
+ added: [],
14781
+ preservedCustom: preservedCustomUnits(existing, generated, kind),
14782
+ conflicts: [],
14783
+ lintErrors: [],
14784
+ detail: "artifactWrite policy is skip"
14785
+ };
14786
+ }
14787
+ return kind === "database" ? mergeDatabaseArtifact(existing, generated) : mergeRegistryArtifact(existing, generated);
14788
+ }
14789
+ function resolveArtifactWritePlan(file, existingContent, policy) {
14790
+ if (existingContent === null) {
14791
+ return {
14792
+ action: "write",
14793
+ content: file.content,
14794
+ writeReason: "created",
14795
+ added: ["created"],
14796
+ preservedCustom: [],
14797
+ conflicts: [],
14798
+ lintErrors: []
14799
+ };
14800
+ }
14801
+ if (file.kind === "model" || file.kind === "schema" || policy === "always") {
14802
+ if (existingContent.replace(/\r\n/g, "\n") === file.content.replace(/\r\n/g, "\n")) {
14803
+ return {
14804
+ action: "unchanged",
14805
+ content: existingContent,
14806
+ skipReason: "already-current",
14807
+ added: [],
14808
+ preservedCustom: [],
14809
+ conflicts: [],
14810
+ lintErrors: []
14811
+ };
14812
+ }
14813
+ return {
14814
+ action: "write",
14815
+ content: file.content,
14816
+ writeReason: existingContent ? "overwritten" : "created",
14817
+ added: ["overwritten"],
14818
+ preservedCustom: [],
14819
+ conflicts: [],
14820
+ lintErrors: []
14821
+ };
14822
+ }
14823
+ return mergeProtectedArtifact(
14824
+ file.kind,
14825
+ existingContent,
14826
+ file.content,
14827
+ policy
14828
+ );
14829
+ }
14830
+
14831
+ // src/generator/pipeline.ts
13274
14832
  async function fileExists(path) {
13275
14833
  try {
13276
14834
  await stat(path);
@@ -13279,47 +14837,156 @@ async function fileExists(path) {
13279
14837
  return false;
13280
14838
  }
13281
14839
  }
13282
- async function writeArtifacts(files, cwd) {
14840
+ async function readExisting(path) {
14841
+ try {
14842
+ return await readFile(path, "utf8");
14843
+ } catch {
14844
+ return null;
14845
+ }
14846
+ }
14847
+ function policyForArtifact(file, config) {
14848
+ if (file.kind === "model" || file.kind === "schema") {
14849
+ return "always";
14850
+ }
14851
+ if (file.kind === "database") {
14852
+ return config.output.artifactWrite.database;
14853
+ }
14854
+ return config.output.artifactWrite.registry;
14855
+ }
14856
+ async function writeArtifacts(files, cwd, config, dryRun) {
13283
14857
  const writtenFiles = [];
14858
+ const writtenDetails = [];
13284
14859
  const skippedFiles = [];
13285
14860
  for (const file of files) {
13286
14861
  const absolutePath = resolve(cwd, file.path);
13287
- if (!canOverwriteArtifact(file) && await fileExists(absolutePath)) {
14862
+ const exists = await fileExists(absolutePath);
14863
+ const existingContent = exists ? await readExisting(absolutePath) : null;
14864
+ const policy = policyForArtifact(file, config);
14865
+ const plan = resolveArtifactWritePlan(file, existingContent, policy);
14866
+ if (plan.action === "skip" || plan.action === "unchanged") {
14867
+ skippedFiles.push({
14868
+ kind: file.kind,
14869
+ path: file.path,
14870
+ reason: plan.skipReason ?? "already-current",
14871
+ detail: plan.detail,
14872
+ preservedCustom: plan.preservedCustom.length > 0 ? plan.preservedCustom : void 0,
14873
+ conflicts: plan.conflicts.length > 0 ? plan.conflicts : void 0,
14874
+ lintErrors: plan.lintErrors.length > 0 ? plan.lintErrors : void 0
14875
+ });
14876
+ continue;
14877
+ }
14878
+ if (!plan.content || !plan.writeReason) {
13288
14879
  skippedFiles.push({
13289
14880
  kind: file.kind,
13290
14881
  path: file.path,
13291
- reason: "protected-existing-file"
14882
+ reason: "merge-lint-failed",
14883
+ detail: "merge produced no content",
14884
+ lintErrors: plan.lintErrors
13292
14885
  });
13293
14886
  continue;
13294
14887
  }
13295
- await mkdir(dirname(absolutePath), { recursive: true });
13296
- await writeFile(absolutePath, file.content, "utf8");
14888
+ if (!dryRun) {
14889
+ await mkdir(dirname(absolutePath), { recursive: true });
14890
+ await writeFile(absolutePath, plan.content, "utf8");
14891
+ }
13297
14892
  writtenFiles.push(file.path);
14893
+ writtenDetails.push({
14894
+ kind: file.kind,
14895
+ path: file.path,
14896
+ reason: plan.writeReason,
14897
+ added: plan.added.length > 0 ? plan.added : void 0,
14898
+ preservedCustom: plan.preservedCustom.length > 0 ? plan.preservedCustom : void 0
14899
+ });
13298
14900
  }
13299
14901
  return {
13300
14902
  writtenFiles,
14903
+ writtenDetails,
13301
14904
  skippedFiles
13302
14905
  };
13303
14906
  }
14907
+ function withPostgresSchemas(config, schemas) {
14908
+ if (config.provider.kind !== "postgres") {
14909
+ return config;
14910
+ }
14911
+ return {
14912
+ ...config,
14913
+ provider: {
14914
+ ...config.provider,
14915
+ schemas
14916
+ }
14917
+ };
14918
+ }
13304
14919
  async function runSchemaGenerator(options = {}) {
13305
14920
  const cwd = options.cwd ?? process.cwd();
14921
+ const dryRun = options.dryRun === true;
14922
+ const writeConfig = options.writeConfig !== false;
14923
+ const discoverSchemas = options.discoverSchemas !== false;
13306
14924
  const configOptions = {
13307
14925
  cwd,
13308
14926
  configPath: options.configPath
13309
14927
  };
13310
- const { configPath, config } = await loadGeneratorConfig(configOptions);
14928
+ const loaded = await loadGeneratorConfig(configOptions);
14929
+ let { configPath, config } = loaded;
14930
+ let configEnsure;
14931
+ let effectiveSchemas = resolveProviderSchemas(config.provider);
14932
+ const canDiscoverLive = discoverSchemas && config.provider.kind === "postgres" && !options.provider;
14933
+ if (canDiscoverLive) {
14934
+ try {
14935
+ const discovered = await discoverPostgresSchemas(config.provider);
14936
+ if (discovered.length > 0) {
14937
+ effectiveSchemas = mergeSchemaSelections(effectiveSchemas, discovered);
14938
+ }
14939
+ } catch {
14940
+ }
14941
+ }
14942
+ config = withPostgresSchemas(config, effectiveSchemas);
14943
+ const shouldEnsureConfig = writeConfig && config.provider.kind === "postgres" && !options.provider;
14944
+ if (shouldEnsureConfig) {
14945
+ const ensureResult = await ensureGeneratorConfigFile({
14946
+ cwd,
14947
+ configPath: options.configPath,
14948
+ dryRun,
14949
+ // Schemas already resolved above — avoid a second discovery round-trip.
14950
+ discoverSchemas: false,
14951
+ provider: config.provider,
14952
+ loaded: { configPath, config },
14953
+ schemas: effectiveSchemas,
14954
+ mode: config.provider.mode
14955
+ });
14956
+ configEnsure = {
14957
+ action: ensureResult.action,
14958
+ path: ensureResult.path,
14959
+ schemas: ensureResult.schemas,
14960
+ changes: ensureResult.changes,
14961
+ reason: ensureResult.reason
14962
+ };
14963
+ if (ensureResult.action === "created" || ensureResult.action === "updated" || ensureResult.action === "unchanged" && configPath === "[environment defaults]") {
14964
+ configPath = ensureResult.absolutePath;
14965
+ if (ensureResult.schemas.length > 0) {
14966
+ effectiveSchemas = ensureResult.schemas;
14967
+ config = withPostgresSchemas(config, effectiveSchemas);
14968
+ }
14969
+ }
14970
+ }
13311
14971
  const provider = options.provider ?? resolveGeneratorProvider(config.provider, config.experimental);
13312
14972
  const snapshot = await provider.inspect({
13313
- schemas: resolveProviderSchemas(config.provider)
14973
+ schemas: effectiveSchemas
13314
14974
  });
13315
14975
  const generated = generateArtifactsFromSnapshot(snapshot, config);
13316
- const writeResult = options.dryRun ? { writtenFiles: [], skippedFiles: [] } : await writeArtifacts(generated.files, cwd);
14976
+ const writeResult = await writeArtifacts(
14977
+ generated.files,
14978
+ cwd,
14979
+ config,
14980
+ dryRun
14981
+ );
13317
14982
  return {
13318
14983
  ...generated,
13319
14984
  configPath,
13320
14985
  config,
13321
14986
  writtenFiles: writeResult.writtenFiles,
13322
- skippedFiles: writeResult.skippedFiles
14987
+ writtenDetails: writeResult.writtenDetails,
14988
+ skippedFiles: writeResult.skippedFiles,
14989
+ configEnsure
13323
14990
  };
13324
14991
  }
13325
14992
 
@@ -13388,15 +15055,15 @@ function readCookieFromHeaders(headers, name) {
13388
15055
  }
13389
15056
  return parseCookies(cookieHeader).get(name);
13390
15057
  }
13391
- function isRecord13(value) {
15058
+ function isRecord14(value) {
13392
15059
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
13393
15060
  }
13394
15061
  function resolveSessionCandidate(value) {
13395
- if (!isRecord13(value)) {
15062
+ if (!isRecord14(value)) {
13396
15063
  return null;
13397
15064
  }
13398
- const session = isRecord13(value.session) ? value.session : void 0;
13399
- const user = isRecord13(value.user) ? value.user : void 0;
15065
+ const session = isRecord14(value.session) ? value.session : void 0;
15066
+ const user = isRecord14(value.user) ? value.user : void 0;
13400
15067
  if (session && typeof session.token === "string" && session.token.length > 0 && user) {
13401
15068
  return {
13402
15069
  session,
@@ -13406,7 +15073,7 @@ function resolveSessionCandidate(value) {
13406
15073
  return null;
13407
15074
  }
13408
15075
  function inferSessionPair(returned) {
13409
- return resolveSessionCandidate(returned) ?? (isRecord13(returned) ? resolveSessionCandidate(returned.data) : null) ?? (isRecord13(returned) ? resolveSessionCandidate(returned.session) : null);
15076
+ return resolveSessionCandidate(returned) ?? (isRecord14(returned) ? resolveSessionCandidate(returned.data) : null) ?? (isRecord14(returned) ? resolveSessionCandidate(returned.session) : null);
13410
15077
  }
13411
15078
  function resolveResponseHeaders(ctx) {
13412
15079
  if (ctx.context.responseHeaders instanceof Headers) {
@@ -13817,6 +15484,6 @@ async function ensureActiveOrganization(options) {
13817
15484
  }
13818
15485
  }
13819
15486
 
13820
- export { ATHENA_AUTH_ADMIN_LIMITS, ATHENA_AUTH_BASE_ERROR_CODES, ATHENA_AUTH_MAX_ADMIN_JSON_BYTES, ATHENA_AUTH_MAX_ADMIN_JSON_DEPTH, ATHENA_AUTH_MAX_TEMPLATE_VARIABLES, ATHENA_AUTH_MAX_TEMPLATE_VARIABLE_LENGTH, AthenaBillingError, AthenaChatError, AthenaConfigurationError, AthenaError, AthenaErrorCategory, AthenaErrorCode, AthenaErrorKind, AthenaGatewayError, AthenaStorageError, AthenaStorageErrorCode, Backend, DEFAULT_MULTIPART_PART_SIZE_BYTES, DEFAULT_MULTIPART_THRESHOLD_BYTES, DEFAULT_POSTGRES_SCHEMAS, assertInt, athenaAuth, billingSdkManifest, boolean, chatSdkManifest, coerceInt, createAthenaStorageError, createAuthReactEmailInput, createBillingModule, createClient, createModelFormAdapter, createPostgresIntrospectionProvider, createStorageFileModule, defineAthenaAuthConfig, defineAuthEmailTemplate, defineDatabase, defineGeneratorConfig, defineModel, defineRegistry, defineSchema, ensureActiveOrganization, enumeration, filterIntrospectionSnapshot, findGeneratorConfigPath, generateArtifactsFromSnapshot, generatorEnv, getAthenaDebugAst, identifier, isAthenaGatewayError, isOk, json, loadGeneratorConfig, normalizeAthenaError, normalizeAthenaGatewayBaseUrl, normalizeGeneratorConfig, normalizeSchemaSelection, normalizeTableSelection, number, parseBooleanFlag2 as parseBooleanFlag, renderAthenaReactEmail, requireAffected, requireSuccess, resolveGeneratorProvider, resolvePostgresColumnType, resolveProviderSchemas, resolveStoragePath, runSchemaGenerator, storageSdkManifest, string, table, toModelFormDefaults, toModelPayload, unwrap, unwrapChatMessage, unwrapChatRoom, unwrapOne, unwrapRows, verifyAthenaGatewayUrl, withRetry };
15487
+ export { ATHENA_AUTH_ADMIN_LIMITS, ATHENA_AUTH_BASE_ERROR_CODES, ATHENA_AUTH_MAX_ADMIN_JSON_BYTES, ATHENA_AUTH_MAX_ADMIN_JSON_DEPTH, ATHENA_AUTH_MAX_TEMPLATE_VARIABLES, ATHENA_AUTH_MAX_TEMPLATE_VARIABLE_LENGTH, AthenaBillingError, AthenaChatError, AthenaConfigurationError, AthenaError, AthenaErrorCategory, AthenaErrorCode, AthenaErrorKind, AthenaGatewayError, AthenaStorageError, AthenaStorageErrorCode, Backend, DEFAULT_MULTIPART_PART_SIZE_BYTES, DEFAULT_MULTIPART_THRESHOLD_BYTES, DEFAULT_POSTGRES_SCHEMAS, applyAthenaReadQueryFilters, applyAthenaReadQuerySelectLimit, applyAthenaReadQuerySelectOrder, applyAthenaTableFilters, applyAthenaTableSelectLimit, applyAthenaTableSelectOrder, assertInt, athenaAuth, billingSdkManifest, boolean, buildAthenaReadQueryFindManyOrderBy, buildAthenaReadQueryFindManySelect, buildAthenaReadQueryFindManyWhere, buildAthenaReadQuerySelectString, buildAthenaTableFindManyOrderBy, buildAthenaTableFindManySelect, buildAthenaTableFindManyWhere, buildAthenaTableSelectString, chatSdkManifest, clampAthenaReadQueryTotalItems, clampAthenaTableTotalItems, coerceInt, createAthenaStorageError, createAuthReactEmailInput, createBillingModule, createClient, createModelFormAdapter, createPostgresIntrospectionProvider, createStorageFileModule, defineAthenaAuthConfig, defineAuthEmailTemplate, defineDatabase, defineGeneratorConfig, defineModel, defineRegistry, defineSchema, detectGeneratorProviderMode, discoverPostgresSchemas, ensureActiveOrganization, ensureGeneratorConfigFile, enumeration, executeAthenaReadQuery, executeAthenaTableQuery, filterIntrospectionSnapshot, findGeneratorConfigPath, flattenAthenaReadQueryRows, flattenAthenaRows, generateArtifactsFromSnapshot, generatorEnv, getAthenaDebugAst, identifier, isAthenaGatewayError, isOk, json, loadGeneratorConfig, mergeSchemaSelections, normalizeAthenaError, normalizeAthenaGatewayBaseUrl, normalizeAthenaReadQueryOrderBy, normalizeAthenaTableOrderBy, normalizeDiscoveredSchemas, normalizeGeneratorConfig, normalizeSchemaSelection, normalizeTableSelection, number, parseBooleanFlag2 as parseBooleanFlag, patchSchemasInConfigSource, renderAthenaReactEmail, renderGeneratorConfigFile, requireAffected, requireSuccess, resolveGeneratorProvider, resolvePostgresColumnType, resolveProviderSchemas, resolveStoragePath, runSchemaGenerator, schemasEqual, storageSdkManifest, string, table, toModelFormDefaults, toModelPayload, unwrap, unwrapChatMessage, unwrapChatRoom, unwrapOne, unwrapRows, verifyAthenaGatewayUrl, withRetry };
13821
15488
  //# sourceMappingURL=index.js.map
13822
15489
  //# sourceMappingURL=index.js.map