@xylex-group/athena 1.6.1 → 1.6.2

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.
@@ -1,6 +1,25 @@
1
+ import { r as runSchemaGenerator } from '../pipeline-CA2aexDl.cjs';
2
+ import '../types-v6UyGg-x.cjs';
3
+
4
+ interface GenerateCommand {
5
+ command: 'generate';
6
+ configPath?: string;
7
+ dryRun: boolean;
8
+ }
9
+ interface HelpCommand {
10
+ command: 'help';
11
+ topic: 'root' | 'generate';
12
+ }
13
+ type CliCommand = GenerateCommand | HelpCommand;
14
+ interface CliRuntime {
15
+ runGenerator?: typeof runSchemaGenerator;
16
+ log?: (message: string) => void;
17
+ }
18
+ declare function usage(topic?: HelpCommand['topic']): string;
19
+ declare function parseCommand(argv: string[]): CliCommand;
1
20
  /**
2
21
  * CLI entrypoint used by `bin/athena-js.js`.
3
22
  */
4
- declare function runCLI(argv: string[]): Promise<void>;
23
+ declare function runCLI(argv: string[], runtime?: CliRuntime): Promise<void>;
5
24
 
6
- export { runCLI };
25
+ export { type CliRuntime, parseCommand, runCLI, usage };
@@ -1,6 +1,25 @@
1
+ import { r as runSchemaGenerator } from '../pipeline-BQzpSLD3.js';
2
+ import '../types-v6UyGg-x.js';
3
+
4
+ interface GenerateCommand {
5
+ command: 'generate';
6
+ configPath?: string;
7
+ dryRun: boolean;
8
+ }
9
+ interface HelpCommand {
10
+ command: 'help';
11
+ topic: 'root' | 'generate';
12
+ }
13
+ type CliCommand = GenerateCommand | HelpCommand;
14
+ interface CliRuntime {
15
+ runGenerator?: typeof runSchemaGenerator;
16
+ log?: (message: string) => void;
17
+ }
18
+ declare function usage(topic?: HelpCommand['topic']): string;
19
+ declare function parseCommand(argv: string[]): CliCommand;
1
20
  /**
2
21
  * CLI entrypoint used by `bin/athena-js.js`.
3
22
  */
4
- declare function runCLI(argv: string[]): Promise<void>;
23
+ declare function runCLI(argv: string[], runtime?: CliRuntime): Promise<void>;
5
24
 
6
- export { runCLI };
25
+ export { type CliRuntime, parseCommand, runCLI, usage };
package/dist/cli/index.js CHANGED
@@ -298,10 +298,10 @@ var DEFAULT_CONFIG_CANDIDATES = [
298
298
  ".athena.config.js"
299
299
  ];
300
300
  var DEFAULT_TARGETS = {
301
- model: "src/generated/{database_kebab}/{schema_kebab}/{model_kebab}.model.ts",
302
- schema: "src/generated/{database_kebab}/{schema_kebab}/index.ts",
303
- database: "src/generated/{database_kebab}/index.ts",
304
- registry: "src/generated/index.ts"
301
+ model: "athena/models/{model_kebab}.ts",
302
+ schema: "athena/schema.ts",
303
+ database: "athena/relations.ts",
304
+ registry: "athena/config.ts"
305
305
  };
306
306
  var DEFAULT_NAMING = {
307
307
  modelType: "pascal",
@@ -2057,6 +2057,58 @@ function toTypeKind(code) {
2057
2057
  function escapeSqlLiteral(value) {
2058
2058
  return value.replace(/'/g, "''");
2059
2059
  }
2060
+ function parsePostgresArrayLiteral(text) {
2061
+ const body = text.slice(1, -1).trim();
2062
+ if (!body) return [];
2063
+ const values = [];
2064
+ let current = "";
2065
+ let inQuotes = false;
2066
+ let escaped = false;
2067
+ for (const char of body) {
2068
+ if (escaped) {
2069
+ current += char;
2070
+ escaped = false;
2071
+ continue;
2072
+ }
2073
+ if (char === "\\") {
2074
+ escaped = true;
2075
+ continue;
2076
+ }
2077
+ if (char === '"') {
2078
+ inQuotes = !inQuotes;
2079
+ continue;
2080
+ }
2081
+ if (char === "," && !inQuotes) {
2082
+ values.push(current);
2083
+ current = "";
2084
+ continue;
2085
+ }
2086
+ current += char;
2087
+ }
2088
+ values.push(current);
2089
+ return values.map((value) => value.trim()).filter((value) => value.length > 0);
2090
+ }
2091
+ function coerceStringArray(value) {
2092
+ if (Array.isArray(value)) {
2093
+ return value.map((item) => typeof item === "string" ? item : String(item)).map((item) => item.trim()).filter((item) => item.length > 0);
2094
+ }
2095
+ if (typeof value === "string") {
2096
+ const trimmed = value.trim();
2097
+ if (!trimmed) return [];
2098
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
2099
+ try {
2100
+ const parsed = JSON.parse(trimmed);
2101
+ return coerceStringArray(parsed);
2102
+ } catch {
2103
+ }
2104
+ }
2105
+ if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
2106
+ return parsePostgresArrayLiteral(trimmed);
2107
+ }
2108
+ return trimmed.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
2109
+ }
2110
+ return [];
2111
+ }
2060
2112
  function buildSchemaArrayLiteral(schemas) {
2061
2113
  const normalized = schemas.length > 0 ? schemas : ["public"];
2062
2114
  const literals = normalized.map((schema) => `'${escapeSqlLiteral(schema)}'`).join(", ");
@@ -2096,8 +2148,10 @@ var PostgresCatalogSnapshotAssembler = class {
2096
2148
  addPrimaryKeyRows(primaryKeyRows) {
2097
2149
  for (const row of primaryKeyRows) {
2098
2150
  const table = this.ensureTable(row.schema_name, row.table_name);
2099
- table.primaryKey = row.columns;
2100
- for (const columnName of row.columns) {
2151
+ const primaryKeyColumns = coerceStringArray(row.columns);
2152
+ row.columns = primaryKeyColumns;
2153
+ table.primaryKey = primaryKeyColumns;
2154
+ for (const columnName of primaryKeyColumns) {
2101
2155
  const column = table.columns[columnName];
2102
2156
  if (column) {
2103
2157
  column.isPrimaryKey = true;
@@ -2109,23 +2163,27 @@ var PostgresCatalogSnapshotAssembler = class {
2109
2163
  for (const row of foreignKeyRows) {
2110
2164
  const sourceTable = this.ensureTable(row.source_schema, row.source_table);
2111
2165
  const targetTable = this.ensureTable(row.target_schema, row.target_table);
2166
+ const sourceColumns = coerceStringArray(row.source_columns);
2167
+ const targetColumns = coerceStringArray(row.target_columns);
2168
+ row.source_columns = sourceColumns;
2169
+ row.target_columns = targetColumns;
2112
2170
  const sourceRelationKind = row.source_is_unique ? "one-to-one" : "many-to-one";
2113
2171
  this.upsertRelation(sourceTable, relationKey(row.constraint_name, row.target_table), {
2114
2172
  name: row.constraint_name,
2115
2173
  kind: sourceRelationKind,
2116
- sourceColumns: row.source_columns,
2174
+ sourceColumns,
2117
2175
  targetSchema: row.target_schema,
2118
2176
  targetModel: row.target_table,
2119
- targetColumns: row.target_columns
2177
+ targetColumns
2120
2178
  });
2121
2179
  const targetRelationKind = row.source_is_unique ? "one-to-one" : "one-to-many";
2122
2180
  this.upsertRelation(targetTable, relationKey(row.source_table), {
2123
2181
  name: relationKey(row.source_table, row.constraint_name),
2124
2182
  kind: targetRelationKind,
2125
- sourceColumns: row.target_columns,
2183
+ sourceColumns: targetColumns,
2126
2184
  targetSchema: row.source_schema,
2127
2185
  targetModel: row.source_table,
2128
- targetColumns: row.source_columns
2186
+ targetColumns: sourceColumns
2129
2187
  });
2130
2188
  }
2131
2189
  }
@@ -2427,7 +2485,7 @@ async function runSchemaGenerator(options = {}) {
2427
2485
  }
2428
2486
 
2429
2487
  // src/cli/index.ts
2430
- function usage() {
2488
+ function rootUsage() {
2431
2489
  return [
2432
2490
  "athena-js CLI",
2433
2491
  "",
@@ -2436,12 +2494,42 @@ function usage() {
2436
2494
  "",
2437
2495
  "Examples:",
2438
2496
  " athena-js generate",
2497
+ " athena-js generate --config ./athena.config.ts --dry-run",
2498
+ " athena-js generate --help"
2499
+ ].join("\n");
2500
+ }
2501
+ function generateUsage() {
2502
+ return [
2503
+ "athena-js generate",
2504
+ "",
2505
+ "Usage:",
2506
+ " athena-js generate [--config <path>] [--dry-run]",
2507
+ "",
2508
+ "Options:",
2509
+ " --config <path> Explicit path to athena.config.ts or athena-js.config.ts",
2510
+ " --dry-run Build generated files in memory without writing them to disk",
2511
+ " -h, --help Show help for generate",
2512
+ "",
2513
+ "Examples:",
2514
+ " athena-js generate",
2439
2515
  " athena-js generate --config ./athena.config.ts --dry-run"
2440
2516
  ].join("\n");
2441
2517
  }
2518
+ function usage(topic = "root") {
2519
+ return topic === "generate" ? generateUsage() : rootUsage();
2520
+ }
2442
2521
  function parseCommand(argv) {
2443
- if (argv.length === 0 || argv[0] === "help" || argv[0] === "--help" || argv[0] === "-h") {
2444
- return { command: "help" };
2522
+ if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
2523
+ return { command: "help", topic: "root" };
2524
+ }
2525
+ if (argv[0] === "help") {
2526
+ if (argv.length === 1) {
2527
+ return { command: "help", topic: "root" };
2528
+ }
2529
+ if (argv[1] === "generate") {
2530
+ return { command: "help", topic: "generate" };
2531
+ }
2532
+ throw new Error(`Unknown command "${argv[1]}".`);
2445
2533
  }
2446
2534
  const [command, ...rest] = argv;
2447
2535
  if (command !== "generate") {
@@ -2451,13 +2539,16 @@ function parseCommand(argv) {
2451
2539
  let dryRun = false;
2452
2540
  for (let index = 0; index < rest.length; index += 1) {
2453
2541
  const token = rest[index];
2542
+ if (token === "--help" || token === "-h") {
2543
+ return { command: "help", topic: "generate" };
2544
+ }
2454
2545
  if (token === "--dry-run") {
2455
2546
  dryRun = true;
2456
2547
  continue;
2457
2548
  }
2458
2549
  if (token === "--config") {
2459
2550
  const nextValue = rest[index + 1];
2460
- if (!nextValue) {
2551
+ if (!nextValue || nextValue.startsWith("-")) {
2461
2552
  throw new Error("Missing value for --config option.");
2462
2553
  }
2463
2554
  configPath = nextValue;
@@ -2472,29 +2563,70 @@ function parseCommand(argv) {
2472
2563
  dryRun
2473
2564
  };
2474
2565
  }
2475
- async function runCLI(argv) {
2566
+ function normalizeErrorMessage(error) {
2567
+ if (error instanceof Error) {
2568
+ return error.message;
2569
+ }
2570
+ if (typeof error === "string") {
2571
+ return error;
2572
+ }
2573
+ return "Unknown generator error.";
2574
+ }
2575
+ function extractMissingDatabaseName(message) {
2576
+ const match = message.match(/database "([^"]+)" does not exist/i);
2577
+ return match?.[1];
2578
+ }
2579
+ function isErrorWithCode(error) {
2580
+ return typeof error === "object" && error !== null && "code" in error;
2581
+ }
2582
+ function formatGeneratorError(error, configPath) {
2583
+ if (isErrorWithCode(error) && error.code === "3D000") {
2584
+ const message = normalizeErrorMessage(error);
2585
+ const databaseName = extractMissingDatabaseName(message);
2586
+ const databaseLabel = databaseName ? `PostgreSQL database "${databaseName}" does not exist` : "The target PostgreSQL database does not exist";
2587
+ const configLabel = configPath ? `config "${configPath}"` : "the resolved athena config";
2588
+ return new Error(
2589
+ [
2590
+ `${databaseLabel} (code 3D000).`,
2591
+ `Update provider.connectionString (and provider.database, if set) in ${configLabel}, or create that database before running generate.`
2592
+ ].join("\n")
2593
+ );
2594
+ }
2595
+ if (error instanceof Error) {
2596
+ return error;
2597
+ }
2598
+ return new Error(normalizeErrorMessage(error));
2599
+ }
2600
+ async function runCLI(argv, runtime = {}) {
2601
+ const log = runtime.log ?? console.log;
2602
+ const runGenerator = runtime.runGenerator ?? runSchemaGenerator;
2476
2603
  const parsed = parseCommand(argv);
2477
2604
  if (parsed.command === "help") {
2478
- console.log(usage());
2605
+ log(usage(parsed.topic));
2479
2606
  return;
2480
2607
  }
2481
- const result = await runSchemaGenerator({
2482
- configPath: parsed.configPath,
2483
- dryRun: parsed.dryRun
2484
- });
2608
+ let result;
2609
+ try {
2610
+ result = await runGenerator({
2611
+ configPath: parsed.configPath,
2612
+ dryRun: parsed.dryRun
2613
+ });
2614
+ } catch (error) {
2615
+ throw formatGeneratorError(error, parsed.configPath);
2616
+ }
2485
2617
  if (parsed.dryRun) {
2486
- console.log(`[dry-run] Generated ${result.files.length} files from ${result.configPath}`);
2618
+ log(`[dry-run] Generated ${result.files.length} files from ${result.configPath}`);
2487
2619
  for (const file of result.files) {
2488
- console.log(` - ${file.path}`);
2620
+ log(` - ${file.path}`);
2489
2621
  }
2490
2622
  return;
2491
2623
  }
2492
- console.log(`Generated ${result.writtenFiles.length} files from ${result.configPath}`);
2624
+ log(`Generated ${result.writtenFiles.length} files from ${result.configPath}`);
2493
2625
  for (const filePath of result.writtenFiles) {
2494
- console.log(` - ${filePath}`);
2626
+ log(` - ${filePath}`);
2495
2627
  }
2496
2628
  }
2497
2629
 
2498
- export { runCLI };
2630
+ export { parseCommand, runCLI, usage };
2499
2631
  //# sourceMappingURL=index.js.map
2500
2632
  //# sourceMappingURL=index.js.map