@xylex-group/athena 1.6.0 → 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",
@@ -329,6 +329,13 @@ function normalizeOutputConfig(output) {
329
329
  }
330
330
  };
331
331
  }
332
+ function isAthenaGeneratorConfig(value) {
333
+ if (!value || typeof value !== "object") {
334
+ return false;
335
+ }
336
+ const record = value;
337
+ return Boolean(record.provider && typeof record.provider === "object") && Boolean(record.output && typeof record.output === "object");
338
+ }
332
339
  function normalizeGeneratorConfig(input) {
333
340
  return {
334
341
  provider: input.provider,
@@ -357,18 +364,41 @@ function findGeneratorConfigPath(cwd = process.cwd()) {
357
364
  return void 0;
358
365
  }
359
366
  function extractConfigExport(module) {
360
- if (module && typeof module === "object") {
361
- const record = module;
367
+ const visited = /* @__PURE__ */ new Set();
368
+ const queue = [module];
369
+ while (queue.length > 0) {
370
+ const current = queue.shift();
371
+ if (!current || typeof current !== "object" || visited.has(current)) {
372
+ continue;
373
+ }
374
+ visited.add(current);
375
+ const record = current;
376
+ if (isAthenaGeneratorConfig(record)) {
377
+ return record;
378
+ }
362
379
  const defaultExport = record.default;
363
380
  if (defaultExport && typeof defaultExport === "object") {
364
- return defaultExport;
381
+ queue.push(defaultExport);
365
382
  }
366
383
  const namedConfigExport = record.config;
367
384
  if (namedConfigExport && typeof namedConfigExport === "object") {
368
- return namedConfigExport;
385
+ queue.push(namedConfigExport);
386
+ }
387
+ const moduleExports = record["module.exports"];
388
+ if (moduleExports && typeof moduleExports === "object") {
389
+ queue.push(moduleExports);
369
390
  }
370
391
  }
371
- throw new Error("Generator config file must export a config object as default export or `config`.");
392
+ throw new Error(
393
+ "Generator config file must export a config object as default export or `config`."
394
+ );
395
+ }
396
+ function importConfigModule(moduleSpecifier) {
397
+ const runtimeImport = new Function(
398
+ "moduleSpecifier",
399
+ "return import(moduleSpecifier)"
400
+ );
401
+ return runtimeImport(moduleSpecifier);
372
402
  }
373
403
  async function loadGeneratorConfig(options = {}) {
374
404
  const cwd = options.cwd ?? process.cwd();
@@ -379,7 +409,7 @@ async function loadGeneratorConfig(options = {}) {
379
409
  );
380
410
  }
381
411
  const moduleUrl = pathToFileURL(resolvedPath);
382
- const module = await import(`${moduleUrl.href}?cacheBust=${Date.now()}`);
412
+ const module = await importConfigModule(`${moduleUrl.href}?cacheBust=${Date.now()}`);
383
413
  const rawConfig = extractConfigExport(module);
384
414
  return {
385
415
  configPath: resolvedPath,
@@ -2027,6 +2057,58 @@ function toTypeKind(code) {
2027
2057
  function escapeSqlLiteral(value) {
2028
2058
  return value.replace(/'/g, "''");
2029
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
+ }
2030
2112
  function buildSchemaArrayLiteral(schemas) {
2031
2113
  const normalized = schemas.length > 0 ? schemas : ["public"];
2032
2114
  const literals = normalized.map((schema) => `'${escapeSqlLiteral(schema)}'`).join(", ");
@@ -2066,8 +2148,10 @@ var PostgresCatalogSnapshotAssembler = class {
2066
2148
  addPrimaryKeyRows(primaryKeyRows) {
2067
2149
  for (const row of primaryKeyRows) {
2068
2150
  const table = this.ensureTable(row.schema_name, row.table_name);
2069
- table.primaryKey = row.columns;
2070
- 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) {
2071
2155
  const column = table.columns[columnName];
2072
2156
  if (column) {
2073
2157
  column.isPrimaryKey = true;
@@ -2079,23 +2163,27 @@ var PostgresCatalogSnapshotAssembler = class {
2079
2163
  for (const row of foreignKeyRows) {
2080
2164
  const sourceTable = this.ensureTable(row.source_schema, row.source_table);
2081
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;
2082
2170
  const sourceRelationKind = row.source_is_unique ? "one-to-one" : "many-to-one";
2083
2171
  this.upsertRelation(sourceTable, relationKey(row.constraint_name, row.target_table), {
2084
2172
  name: row.constraint_name,
2085
2173
  kind: sourceRelationKind,
2086
- sourceColumns: row.source_columns,
2174
+ sourceColumns,
2087
2175
  targetSchema: row.target_schema,
2088
2176
  targetModel: row.target_table,
2089
- targetColumns: row.target_columns
2177
+ targetColumns
2090
2178
  });
2091
2179
  const targetRelationKind = row.source_is_unique ? "one-to-one" : "one-to-many";
2092
2180
  this.upsertRelation(targetTable, relationKey(row.source_table), {
2093
2181
  name: relationKey(row.source_table, row.constraint_name),
2094
2182
  kind: targetRelationKind,
2095
- sourceColumns: row.target_columns,
2183
+ sourceColumns: targetColumns,
2096
2184
  targetSchema: row.source_schema,
2097
2185
  targetModel: row.source_table,
2098
- targetColumns: row.source_columns
2186
+ targetColumns: sourceColumns
2099
2187
  });
2100
2188
  }
2101
2189
  }
@@ -2397,7 +2485,7 @@ async function runSchemaGenerator(options = {}) {
2397
2485
  }
2398
2486
 
2399
2487
  // src/cli/index.ts
2400
- function usage() {
2488
+ function rootUsage() {
2401
2489
  return [
2402
2490
  "athena-js CLI",
2403
2491
  "",
@@ -2406,12 +2494,42 @@ function usage() {
2406
2494
  "",
2407
2495
  "Examples:",
2408
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",
2409
2515
  " athena-js generate --config ./athena.config.ts --dry-run"
2410
2516
  ].join("\n");
2411
2517
  }
2518
+ function usage(topic = "root") {
2519
+ return topic === "generate" ? generateUsage() : rootUsage();
2520
+ }
2412
2521
  function parseCommand(argv) {
2413
- if (argv.length === 0 || argv[0] === "help" || argv[0] === "--help" || argv[0] === "-h") {
2414
- 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]}".`);
2415
2533
  }
2416
2534
  const [command, ...rest] = argv;
2417
2535
  if (command !== "generate") {
@@ -2421,13 +2539,16 @@ function parseCommand(argv) {
2421
2539
  let dryRun = false;
2422
2540
  for (let index = 0; index < rest.length; index += 1) {
2423
2541
  const token = rest[index];
2542
+ if (token === "--help" || token === "-h") {
2543
+ return { command: "help", topic: "generate" };
2544
+ }
2424
2545
  if (token === "--dry-run") {
2425
2546
  dryRun = true;
2426
2547
  continue;
2427
2548
  }
2428
2549
  if (token === "--config") {
2429
2550
  const nextValue = rest[index + 1];
2430
- if (!nextValue) {
2551
+ if (!nextValue || nextValue.startsWith("-")) {
2431
2552
  throw new Error("Missing value for --config option.");
2432
2553
  }
2433
2554
  configPath = nextValue;
@@ -2442,29 +2563,70 @@ function parseCommand(argv) {
2442
2563
  dryRun
2443
2564
  };
2444
2565
  }
2445
- 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;
2446
2603
  const parsed = parseCommand(argv);
2447
2604
  if (parsed.command === "help") {
2448
- console.log(usage());
2605
+ log(usage(parsed.topic));
2449
2606
  return;
2450
2607
  }
2451
- const result = await runSchemaGenerator({
2452
- configPath: parsed.configPath,
2453
- dryRun: parsed.dryRun
2454
- });
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
+ }
2455
2617
  if (parsed.dryRun) {
2456
- console.log(`[dry-run] Generated ${result.files.length} files from ${result.configPath}`);
2618
+ log(`[dry-run] Generated ${result.files.length} files from ${result.configPath}`);
2457
2619
  for (const file of result.files) {
2458
- console.log(` - ${file.path}`);
2620
+ log(` - ${file.path}`);
2459
2621
  }
2460
2622
  return;
2461
2623
  }
2462
- console.log(`Generated ${result.writtenFiles.length} files from ${result.configPath}`);
2624
+ log(`Generated ${result.writtenFiles.length} files from ${result.configPath}`);
2463
2625
  for (const filePath of result.writtenFiles) {
2464
- console.log(` - ${filePath}`);
2626
+ log(` - ${filePath}`);
2465
2627
  }
2466
2628
  }
2467
2629
 
2468
- export { runCLI };
2630
+ export { parseCommand, runCLI, usage };
2469
2631
  //# sourceMappingURL=index.js.map
2470
2632
  //# sourceMappingURL=index.js.map