@xylex-group/athena 1.5.0 → 1.6.1

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.
@@ -0,0 +1,2502 @@
1
+ 'use strict';
2
+
3
+ var promises = require('fs/promises');
4
+ var path = require('path');
5
+ var fs = require('fs');
6
+ var url = require('url');
7
+ var pg = require('pg');
8
+
9
+ // src/generator/pipeline.ts
10
+
11
+ // src/generator/naming.ts
12
+ var IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
13
+ var RESERVED_IDENTIFIERS = /* @__PURE__ */ new Set([
14
+ "break",
15
+ "case",
16
+ "catch",
17
+ "class",
18
+ "const",
19
+ "continue",
20
+ "debugger",
21
+ "default",
22
+ "delete",
23
+ "do",
24
+ "else",
25
+ "enum",
26
+ "export",
27
+ "extends",
28
+ "false",
29
+ "finally",
30
+ "for",
31
+ "function",
32
+ "if",
33
+ "import",
34
+ "in",
35
+ "instanceof",
36
+ "new",
37
+ "null",
38
+ "return",
39
+ "super",
40
+ "switch",
41
+ "this",
42
+ "throw",
43
+ "true",
44
+ "try",
45
+ "typeof",
46
+ "var",
47
+ "void",
48
+ "while",
49
+ "with",
50
+ "as",
51
+ "implements",
52
+ "interface",
53
+ "let",
54
+ "package",
55
+ "private",
56
+ "protected",
57
+ "public",
58
+ "static",
59
+ "yield",
60
+ "any",
61
+ "boolean",
62
+ "constructor",
63
+ "declare",
64
+ "get",
65
+ "module",
66
+ "require",
67
+ "number",
68
+ "set",
69
+ "string",
70
+ "symbol",
71
+ "type",
72
+ "from",
73
+ "of"
74
+ ]);
75
+ function splitWords(input) {
76
+ return input.trim().replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z0-9]+)/g, "$1 $2").replace(/[^A-Za-z0-9]+/g, " ").split(" ").map((word) => word.trim()).filter((word) => word.length > 0);
77
+ }
78
+ function capitalize(word) {
79
+ return word.length > 0 ? `${word[0].toUpperCase()}${word.slice(1).toLowerCase()}` : word;
80
+ }
81
+ function ensureValidIdentifier(candidate, fallback) {
82
+ const normalized = candidate.length > 0 ? candidate : fallback;
83
+ const prefixed = /^[0-9]/.test(normalized) ? `_${normalized}` : normalized;
84
+ if (RESERVED_IDENTIFIERS.has(prefixed)) {
85
+ return `${prefixed}_value`;
86
+ }
87
+ return prefixed;
88
+ }
89
+ function applyNamingStyle(input, style) {
90
+ const words = splitWords(input);
91
+ if (words.length === 0) {
92
+ return "";
93
+ }
94
+ switch (style) {
95
+ case "preserve":
96
+ return input;
97
+ case "camel": {
98
+ const [first, ...rest] = words;
99
+ return `${first.toLowerCase()}${rest.map(capitalize).join("")}`;
100
+ }
101
+ case "pascal":
102
+ return words.map(capitalize).join("");
103
+ case "snake":
104
+ return words.map((word) => word.toLowerCase()).join("_");
105
+ case "kebab":
106
+ return words.map((word) => word.toLowerCase()).join("-");
107
+ default:
108
+ return input;
109
+ }
110
+ }
111
+ function toSafeIdentifier(input, style, fallback = "value") {
112
+ const transformed = applyNamingStyle(input, style);
113
+ const normalized = transformed.replace(/[^A-Za-z0-9_$]+/g, "_").replace(/^_+|_+$/g, "");
114
+ const fallbackValue = splitWords(input).join("");
115
+ return ensureValidIdentifier(
116
+ normalized.length > 0 ? normalized : fallbackValue,
117
+ fallback
118
+ );
119
+ }
120
+ function escapeTypePropertyName(propertyName) {
121
+ if (IDENTIFIER_PATTERN.test(propertyName) && !RESERVED_IDENTIFIERS.has(propertyName)) {
122
+ return propertyName;
123
+ }
124
+ return `'${propertyName.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
125
+ }
126
+ function escapeStringLiteral(value) {
127
+ return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
128
+ }
129
+
130
+ // src/generator/placeholders.ts
131
+ function createStyleTokens(prefix, value) {
132
+ return {
133
+ [prefix]: value,
134
+ [`${prefix}_camel`]: applyNamingStyle(value, "camel"),
135
+ [`${prefix}_pascal`]: applyNamingStyle(value, "pascal"),
136
+ [`${prefix}_snake`]: applyNamingStyle(value, "snake"),
137
+ [`${prefix}_kebab`]: applyNamingStyle(value, "kebab")
138
+ };
139
+ }
140
+ function renderTemplate(template, tokenMap) {
141
+ return template.replace(/\{([A-Za-z0-9_]+)\}/g, (_match, token) => {
142
+ if (!(token in tokenMap)) {
143
+ throw new Error(`Unknown placeholder token "${token}" in template "${template}"`);
144
+ }
145
+ return tokenMap[token];
146
+ });
147
+ }
148
+ function resolvePlaceholderMap(baseTokens, outputConfig) {
149
+ const resolved = {
150
+ ...baseTokens
151
+ };
152
+ const entries = Object.entries(outputConfig.placeholderMap ?? {});
153
+ for (let index = 0; index < entries.length; index += 1) {
154
+ const [key, value] = entries[index];
155
+ let current = value;
156
+ for (let depth = 0; depth < 8; depth += 1) {
157
+ const next = renderTemplate(current, resolved);
158
+ if (next === current) {
159
+ break;
160
+ }
161
+ current = next;
162
+ }
163
+ resolved[key] = current;
164
+ }
165
+ return resolved;
166
+ }
167
+ function renderOutputPath(template, context, outputConfig) {
168
+ const baseTokens = {
169
+ provider: context.provider,
170
+ kind: context.kind,
171
+ ...createStyleTokens("database", context.database),
172
+ ...createStyleTokens("schema", context.schema),
173
+ ...createStyleTokens("model", context.model)
174
+ };
175
+ const tokens = resolvePlaceholderMap(baseTokens, outputConfig);
176
+ return renderTemplate(template, tokens);
177
+ }
178
+
179
+ // src/generator/postgres-type-mapping.ts
180
+ var NUMBER_TYPES = /* @__PURE__ */ new Set([
181
+ "int2",
182
+ "int4",
183
+ "float4",
184
+ "float8",
185
+ "smallint",
186
+ "integer",
187
+ "real",
188
+ "double precision"
189
+ ]);
190
+ var STRING_NUMERIC_TYPES = /* @__PURE__ */ new Set([
191
+ "int8",
192
+ "bigint",
193
+ "serial8",
194
+ "bigserial",
195
+ "numeric",
196
+ "decimal",
197
+ "money"
198
+ ]);
199
+ var TEXT_TYPES = /* @__PURE__ */ new Set([
200
+ "char",
201
+ "bpchar",
202
+ "varchar",
203
+ "character varying",
204
+ "text",
205
+ "citext",
206
+ "name",
207
+ "uuid",
208
+ "date",
209
+ "time",
210
+ "timetz",
211
+ "timestamp",
212
+ "timestamptz",
213
+ "interval",
214
+ "inet",
215
+ "cidr",
216
+ "macaddr",
217
+ "macaddr8",
218
+ "point",
219
+ "line",
220
+ "lseg",
221
+ "box",
222
+ "path",
223
+ "polygon",
224
+ "circle",
225
+ "bit",
226
+ "varbit",
227
+ "xml",
228
+ "tsvector",
229
+ "tsquery",
230
+ "jsonpath"
231
+ ]);
232
+ function normalizeTypeLabel(column) {
233
+ const preferred = (column.udtName || column.dataType).toLowerCase().trim();
234
+ if (column.arrayDimensions > 0 && preferred.startsWith("_")) {
235
+ return preferred.slice(1);
236
+ }
237
+ return preferred;
238
+ }
239
+ function wrapArrayType(baseType, depth) {
240
+ let wrapped = baseType;
241
+ for (let index = 0; index < depth; index += 1) {
242
+ wrapped = `Array<${wrapped}>`;
243
+ }
244
+ return wrapped;
245
+ }
246
+ function resolveScalarType(column) {
247
+ const label = normalizeTypeLabel(column);
248
+ if (NUMBER_TYPES.has(label)) {
249
+ return "number";
250
+ }
251
+ if (STRING_NUMERIC_TYPES.has(label)) {
252
+ return "string";
253
+ }
254
+ if (label === "bool" || label === "boolean") {
255
+ return "boolean";
256
+ }
257
+ if (label === "bytea") {
258
+ return "Buffer";
259
+ }
260
+ if (label === "json" || label === "jsonb") {
261
+ return "Record<string, unknown>";
262
+ }
263
+ if (TEXT_TYPES.has(label)) {
264
+ return "string";
265
+ }
266
+ if (label.endsWith("range") || label.endsWith("multirange")) {
267
+ return "string";
268
+ }
269
+ return "unknown";
270
+ }
271
+ function resolveKindAwareType(column) {
272
+ if (column.typeKind === "enum") {
273
+ const values = column.enumValues ?? [];
274
+ if (values.length === 0) {
275
+ return "string";
276
+ }
277
+ return values.map((value) => `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`).join(" | ");
278
+ }
279
+ if (column.typeKind === "composite") {
280
+ return "Record<string, unknown>";
281
+ }
282
+ if (column.typeKind === "domain" || column.typeKind === "range" || column.typeKind === "multirange") {
283
+ return "string";
284
+ }
285
+ return resolveScalarType(column);
286
+ }
287
+ function resolvePostgresColumnType(column) {
288
+ const baseType = resolveKindAwareType(column);
289
+ if (!column.arrayDimensions || column.arrayDimensions <= 0) {
290
+ return baseType;
291
+ }
292
+ return wrapArrayType(baseType, column.arrayDimensions);
293
+ }
294
+ var DEFAULT_CONFIG_CANDIDATES = [
295
+ "athena.config.ts",
296
+ "athena.config.js",
297
+ "athena-js.config.ts",
298
+ "athena-js.config.js",
299
+ ".athena.config.ts",
300
+ ".athena.config.js"
301
+ ];
302
+ var DEFAULT_TARGETS = {
303
+ model: "src/generated/{database_kebab}/{schema_kebab}/{model_kebab}.model.ts",
304
+ schema: "src/generated/{database_kebab}/{schema_kebab}/index.ts",
305
+ database: "src/generated/{database_kebab}/index.ts",
306
+ registry: "src/generated/index.ts"
307
+ };
308
+ var DEFAULT_NAMING = {
309
+ modelType: "pascal",
310
+ modelConst: "camel",
311
+ schemaConst: "camel",
312
+ databaseConst: "camel",
313
+ registryConst: "camel"
314
+ };
315
+ var DEFAULT_FEATURES = {
316
+ emitRelations: true,
317
+ emitRegistry: true
318
+ };
319
+ var DEFAULT_EXPERIMENTAL_FLAGS = {
320
+ postgresGatewayIntrospection: false,
321
+ scyllaProviderContracts: true
322
+ };
323
+ function normalizeOutputConfig(output) {
324
+ return {
325
+ targets: {
326
+ ...DEFAULT_TARGETS,
327
+ ...output.targets ?? {}
328
+ },
329
+ placeholderMap: {
330
+ ...output.placeholderMap ?? {}
331
+ }
332
+ };
333
+ }
334
+ function isAthenaGeneratorConfig(value) {
335
+ if (!value || typeof value !== "object") {
336
+ return false;
337
+ }
338
+ const record = value;
339
+ return Boolean(record.provider && typeof record.provider === "object") && Boolean(record.output && typeof record.output === "object");
340
+ }
341
+ function normalizeGeneratorConfig(input) {
342
+ return {
343
+ provider: input.provider,
344
+ output: normalizeOutputConfig(input.output),
345
+ naming: {
346
+ ...DEFAULT_NAMING,
347
+ ...input.naming ?? {}
348
+ },
349
+ features: {
350
+ ...DEFAULT_FEATURES,
351
+ ...input.features ?? {}
352
+ },
353
+ experimental: {
354
+ ...DEFAULT_EXPERIMENTAL_FLAGS,
355
+ ...input.experimental ?? {}
356
+ }
357
+ };
358
+ }
359
+ function findGeneratorConfigPath(cwd = process.cwd()) {
360
+ for (const candidate of DEFAULT_CONFIG_CANDIDATES) {
361
+ const absolutePath = path.resolve(cwd, candidate);
362
+ if (fs.existsSync(absolutePath)) {
363
+ return absolutePath;
364
+ }
365
+ }
366
+ return void 0;
367
+ }
368
+ function extractConfigExport(module) {
369
+ const visited = /* @__PURE__ */ new Set();
370
+ const queue = [module];
371
+ while (queue.length > 0) {
372
+ const current = queue.shift();
373
+ if (!current || typeof current !== "object" || visited.has(current)) {
374
+ continue;
375
+ }
376
+ visited.add(current);
377
+ const record = current;
378
+ if (isAthenaGeneratorConfig(record)) {
379
+ return record;
380
+ }
381
+ const defaultExport = record.default;
382
+ if (defaultExport && typeof defaultExport === "object") {
383
+ queue.push(defaultExport);
384
+ }
385
+ const namedConfigExport = record.config;
386
+ if (namedConfigExport && typeof namedConfigExport === "object") {
387
+ queue.push(namedConfigExport);
388
+ }
389
+ const moduleExports = record["module.exports"];
390
+ if (moduleExports && typeof moduleExports === "object") {
391
+ queue.push(moduleExports);
392
+ }
393
+ }
394
+ throw new Error(
395
+ "Generator config file must export a config object as default export or `config`."
396
+ );
397
+ }
398
+ function importConfigModule(moduleSpecifier) {
399
+ const runtimeImport = new Function(
400
+ "moduleSpecifier",
401
+ "return import(moduleSpecifier)"
402
+ );
403
+ return runtimeImport(moduleSpecifier);
404
+ }
405
+ async function loadGeneratorConfig(options = {}) {
406
+ const cwd = options.cwd ?? process.cwd();
407
+ const resolvedPath = options.configPath ? path.resolve(cwd, options.configPath) : findGeneratorConfigPath(cwd);
408
+ if (!resolvedPath) {
409
+ throw new Error(
410
+ `No generator config found in ${cwd}. Expected one of: ${DEFAULT_CONFIG_CANDIDATES.join(", ")}`
411
+ );
412
+ }
413
+ const moduleUrl = url.pathToFileURL(resolvedPath);
414
+ const module = await importConfigModule(`${moduleUrl.href}?cacheBust=${Date.now()}`);
415
+ const rawConfig = extractConfigExport(module);
416
+ return {
417
+ configPath: resolvedPath,
418
+ config: normalizeGeneratorConfig(rawConfig)
419
+ };
420
+ }
421
+
422
+ // src/generator/renderer.ts
423
+ function normalizePath(pathValue) {
424
+ return pathValue.replace(/\\/g, "/");
425
+ }
426
+ function withoutTypeScriptExtension(pathValue) {
427
+ return pathValue.replace(/\.tsx?$/i, "");
428
+ }
429
+ function toModuleImportPath(fromFile, targetFile) {
430
+ const relativePath = withoutTypeScriptExtension(
431
+ normalizePath(path.posix.relative(path.posix.dirname(fromFile), targetFile))
432
+ );
433
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
434
+ }
435
+ function renderObjectKey(key) {
436
+ const escaped = escapeTypePropertyName(key);
437
+ return escaped.startsWith("'") ? escaped : escaped;
438
+ }
439
+ function renderRelation(relation) {
440
+ const through = relation.through ? `,
441
+ through: {
442
+ schema: ${escapeStringLiteral(relation.through.schema)},
443
+ model: ${escapeStringLiteral(relation.through.model)},
444
+ sourceColumns: [${relation.through.sourceColumns.map((value) => escapeStringLiteral(value)).join(", ")}],
445
+ targetColumns: [${relation.through.targetColumns.map((value) => escapeStringLiteral(value)).join(", ")}],
446
+ }` : "";
447
+ return `{
448
+ kind: ${escapeStringLiteral(relation.kind)},
449
+ sourceColumns: [${relation.sourceColumns.map((value) => escapeStringLiteral(value)).join(", ")}],
450
+ targetSchema: ${escapeStringLiteral(relation.targetSchema)},
451
+ targetModel: ${escapeStringLiteral(relation.targetModel)},
452
+ targetColumns: [${relation.targetColumns.map((value) => escapeStringLiteral(value)).join(", ")}]${through}
453
+ }`;
454
+ }
455
+ function renderModelArtifact(snapshot, descriptor, config) {
456
+ const columnLines = Object.values(descriptor.table.columns).map((column) => {
457
+ const propertyName = escapeTypePropertyName(column.name);
458
+ const baseType = resolvePostgresColumnType(column);
459
+ const isOptional = column.isNullable;
460
+ const typeWithNullability = column.isNullable ? `${baseType} | null` : baseType;
461
+ return ` ${propertyName}${isOptional ? "?" : ""}: ${typeWithNullability}`;
462
+ }).join("\n");
463
+ const nullableLines = Object.values(descriptor.table.columns).map((column) => ` ${renderObjectKey(column.name)}: ${column.isNullable ? "true" : "false"}`).join(",\n");
464
+ const relationEntries = Object.entries(descriptor.table.relations);
465
+ const relationBlock = config.features.emitRelations && relationEntries.length > 0 ? `,
466
+ relations: {
467
+ ${relationEntries.map(([relationKey2, relationValue]) => ` ${renderObjectKey(relationKey2)}: ${renderRelation(relationValue)}`).join(",\n")}
468
+ }` : "";
469
+ const content = `import { defineModel } from '@xylex-group/athena'
470
+
471
+ export interface ${descriptor.rowTypeName} {
472
+ ${columnLines}
473
+ }
474
+
475
+ export type ${descriptor.insertTypeName} = Partial<${descriptor.rowTypeName}>
476
+ export type ${descriptor.updateTypeName} = Partial<${descriptor.insertTypeName}>
477
+
478
+ export const ${descriptor.modelConstName} = defineModel<${descriptor.rowTypeName}, ${descriptor.insertTypeName}, ${descriptor.updateTypeName}>({
479
+ meta: {
480
+ database: ${escapeStringLiteral(snapshot.database)},
481
+ schema: ${escapeStringLiteral(descriptor.schemaName)},
482
+ model: ${escapeStringLiteral(descriptor.tableName)},
483
+ tableName: ${escapeStringLiteral(`${descriptor.schemaName}.${descriptor.tableName}`)},
484
+ primaryKey: [${descriptor.table.primaryKey.map((value) => escapeStringLiteral(value)).join(", ")}],
485
+ nullable: {
486
+ ${nullableLines}
487
+ }${relationBlock}
488
+ }
489
+ })
490
+ `;
491
+ return {
492
+ kind: "model",
493
+ path: descriptor.filePath,
494
+ content
495
+ };
496
+ }
497
+ function renderSchemaArtifact(descriptor) {
498
+ const importLines = descriptor.models.map((modelDescriptor) => {
499
+ const importPath = toModuleImportPath(descriptor.filePath, modelDescriptor.filePath);
500
+ return `import { ${modelDescriptor.modelConstName} } from '${importPath}'`;
501
+ }).join("\n");
502
+ const modelEntries = descriptor.models.map((modelDescriptor) => ` ${renderObjectKey(modelDescriptor.tableName)}: ${modelDescriptor.modelConstName}`).join(",\n");
503
+ const content = `import { defineSchema } from '@xylex-group/athena'
504
+ ${importLines ? `
505
+ ${importLines}
506
+ ` : "\n"}
507
+ export const ${descriptor.schemaConstName} = defineSchema({
508
+ ${modelEntries}
509
+ })
510
+ `;
511
+ return {
512
+ kind: "schema",
513
+ path: descriptor.filePath,
514
+ content
515
+ };
516
+ }
517
+ function renderDatabaseArtifact(descriptor) {
518
+ const importLines = descriptor.schemas.map((schemaDescriptor) => {
519
+ const importPath = toModuleImportPath(descriptor.filePath, schemaDescriptor.filePath);
520
+ return `import { ${schemaDescriptor.schemaConstName} } from '${importPath}'`;
521
+ }).join("\n");
522
+ const schemaEntries = descriptor.schemas.map((schemaDescriptor) => ` ${renderObjectKey(schemaDescriptor.schemaName)}: ${schemaDescriptor.schemaConstName}`).join(",\n");
523
+ const content = `import { defineDatabase } from '@xylex-group/athena'
524
+ ${importLines ? `
525
+ ${importLines}
526
+ ` : "\n"}
527
+ export const ${descriptor.databaseConstName} = defineDatabase({
528
+ ${schemaEntries}
529
+ })
530
+ `;
531
+ return {
532
+ kind: "database",
533
+ path: descriptor.filePath,
534
+ content
535
+ };
536
+ }
537
+ function renderRegistryArtifact(registryPath, databasePath, databaseConstName, registryConstName, databaseName) {
538
+ const databaseImportPath = toModuleImportPath(registryPath, databasePath);
539
+ const content = `import { defineRegistry } from '@xylex-group/athena'
540
+ import { ${databaseConstName} } from '${databaseImportPath}'
541
+
542
+ export const ${registryConstName} = defineRegistry({
543
+ ${renderObjectKey(databaseName)}: ${databaseConstName}
544
+ })
545
+ `;
546
+ return {
547
+ kind: "registry",
548
+ path: registryPath,
549
+ content
550
+ };
551
+ }
552
+ function assertNoDuplicatePaths(files) {
553
+ const seen = /* @__PURE__ */ new Set();
554
+ for (const file of files) {
555
+ if (seen.has(file.path)) {
556
+ throw new Error(`Generator output collision detected for path: ${file.path}`);
557
+ }
558
+ seen.add(file.path);
559
+ }
560
+ }
561
+ var ArtifactComposer = class {
562
+ constructor(snapshot, config) {
563
+ this.snapshot = snapshot;
564
+ this.config = config;
565
+ }
566
+ compose() {
567
+ const providerName = this.snapshot.backend;
568
+ const databaseName = this.snapshot.database;
569
+ const modelDescriptors = [];
570
+ for (const schemaName of Object.keys(this.snapshot.schemas).sort()) {
571
+ const schema = this.snapshot.schemas[schemaName];
572
+ for (const tableName of Object.keys(schema.tables).sort()) {
573
+ const table = schema.tables[tableName];
574
+ const rowTypeName = `${toSafeIdentifier(`${schemaName} ${tableName}`, this.config.naming.modelType, "Model")}Row`;
575
+ const insertTypeName = `${toSafeIdentifier(`${schemaName} ${tableName}`, this.config.naming.modelType, "Model")}Insert`;
576
+ const updateTypeName = `${toSafeIdentifier(`${schemaName} ${tableName}`, this.config.naming.modelType, "Model")}Update`;
577
+ const modelConstName = `${toSafeIdentifier(`${schemaName} ${tableName} model`, this.config.naming.modelConst, "model")}`;
578
+ const modelPath = normalizePath(
579
+ renderOutputPath(this.config.output.targets.model, {
580
+ provider: providerName,
581
+ kind: "model",
582
+ database: databaseName,
583
+ schema: schemaName,
584
+ model: tableName
585
+ }, this.config.output)
586
+ );
587
+ modelDescriptors.push({
588
+ schemaName,
589
+ tableName,
590
+ filePath: modelPath,
591
+ rowTypeName,
592
+ insertTypeName,
593
+ updateTypeName,
594
+ modelConstName,
595
+ table
596
+ });
597
+ }
598
+ }
599
+ const schemaDescriptors = Object.keys(this.snapshot.schemas).sort().map((schemaName) => {
600
+ const schemaPath = normalizePath(
601
+ renderOutputPath(this.config.output.targets.schema, {
602
+ provider: providerName,
603
+ kind: "schema",
604
+ database: databaseName,
605
+ schema: schemaName,
606
+ model: "index"
607
+ }, this.config.output)
608
+ );
609
+ return {
610
+ schemaName,
611
+ filePath: schemaPath,
612
+ schemaConstName: toSafeIdentifier(
613
+ `${schemaName} schema`,
614
+ this.config.naming.schemaConst,
615
+ "schema"
616
+ ),
617
+ models: modelDescriptors.filter((model) => model.schemaName === schemaName)
618
+ };
619
+ });
620
+ const databasePath = normalizePath(
621
+ renderOutputPath(this.config.output.targets.database, {
622
+ provider: providerName,
623
+ kind: "database",
624
+ database: databaseName,
625
+ schema: "index",
626
+ model: "index"
627
+ }, this.config.output)
628
+ );
629
+ const databaseDescriptor = {
630
+ filePath: databasePath,
631
+ databaseConstName: toSafeIdentifier(
632
+ `${databaseName} database`,
633
+ this.config.naming.databaseConst,
634
+ "database"
635
+ ),
636
+ schemas: schemaDescriptors
637
+ };
638
+ const files = [];
639
+ for (const modelDescriptor of modelDescriptors) {
640
+ files.push(renderModelArtifact(this.snapshot, modelDescriptor, this.config));
641
+ }
642
+ for (const schemaDescriptor of schemaDescriptors) {
643
+ files.push(renderSchemaArtifact(schemaDescriptor));
644
+ }
645
+ files.push(renderDatabaseArtifact(databaseDescriptor));
646
+ if (this.config.features.emitRegistry) {
647
+ const registryPath = normalizePath(
648
+ renderOutputPath(this.config.output.targets.registry, {
649
+ provider: providerName,
650
+ kind: "registry",
651
+ database: databaseName,
652
+ schema: "index",
653
+ model: "index"
654
+ }, this.config.output)
655
+ );
656
+ files.push(
657
+ renderRegistryArtifact(
658
+ registryPath,
659
+ databaseDescriptor.filePath,
660
+ databaseDescriptor.databaseConstName,
661
+ toSafeIdentifier("registry", this.config.naming.registryConst, "registry"),
662
+ databaseName
663
+ )
664
+ );
665
+ }
666
+ assertNoDuplicatePaths(files);
667
+ return {
668
+ snapshot: this.snapshot,
669
+ files
670
+ };
671
+ }
672
+ };
673
+ function generateArtifactsFromSnapshot(snapshot, config) {
674
+ const normalizedConfig = "naming" in config && "features" in config && "experimental" in config ? config : normalizeGeneratorConfig(config);
675
+ return new ArtifactComposer(snapshot, normalizedConfig).compose();
676
+ }
677
+
678
+ // src/gateway/errors.ts
679
+ var AthenaGatewayError = class _AthenaGatewayError extends Error {
680
+ code;
681
+ status;
682
+ endpoint;
683
+ method;
684
+ requestId;
685
+ hint;
686
+ causeDetail;
687
+ constructor(input) {
688
+ super(input.message);
689
+ this.name = "AthenaGatewayError";
690
+ this.code = input.code;
691
+ this.status = input.status ?? 0;
692
+ this.endpoint = input.endpoint;
693
+ this.method = input.method;
694
+ this.requestId = input.requestId;
695
+ this.hint = input.hint;
696
+ this.causeDetail = input.cause;
697
+ }
698
+ toDetails() {
699
+ return {
700
+ code: this.code,
701
+ message: this.message,
702
+ status: this.status,
703
+ endpoint: this.endpoint,
704
+ method: this.method,
705
+ requestId: this.requestId,
706
+ hint: this.hint,
707
+ cause: this.causeDetail
708
+ };
709
+ }
710
+ static fromResponse(response, fallback) {
711
+ const details = response.errorDetails;
712
+ if (details) {
713
+ return new _AthenaGatewayError({
714
+ code: details.code,
715
+ message: details.message,
716
+ status: details.status,
717
+ endpoint: details.endpoint ?? fallback.endpoint,
718
+ method: details.method ?? fallback.method,
719
+ requestId: details.requestId ?? fallback.requestId,
720
+ hint: details.hint,
721
+ cause: details.cause
722
+ });
723
+ }
724
+ return new _AthenaGatewayError({
725
+ code: "HTTP_ERROR",
726
+ message: response.error ?? "Gateway request failed",
727
+ status: response.status,
728
+ endpoint: fallback.endpoint,
729
+ method: fallback.method,
730
+ requestId: fallback.requestId
731
+ });
732
+ }
733
+ };
734
+
735
+ // src/gateway/client.ts
736
+ var DEFAULT_BASE_URL = "https://athena-db.com";
737
+ var DEFAULT_CLIENT = "railway_direct";
738
+ var FALLBACK_SDK_VERSION = "1.3.0";
739
+ var SDK_NAME = "xylex-group/athena";
740
+ var SDK_VERSION = typeof process !== "undefined" && process?.env?.npm_package_version ? process.env.npm_package_version : FALLBACK_SDK_VERSION;
741
+ var SDK_HEADER_VALUE = `${SDK_NAME} ${SDK_VERSION}`;
742
+ function parseResponseBody(rawText, contentType) {
743
+ if (!rawText) {
744
+ return { parsed: null, parseFailed: false };
745
+ }
746
+ const contentTypeSuggestsJson = contentType?.toLowerCase().includes("application/json") ?? false;
747
+ const looksJson = contentTypeSuggestsJson || rawText.startsWith("{") || rawText.startsWith("[");
748
+ if (!looksJson) {
749
+ return { parsed: rawText, parseFailed: false };
750
+ }
751
+ try {
752
+ return { parsed: JSON.parse(rawText), parseFailed: false };
753
+ } catch {
754
+ return { parsed: rawText, parseFailed: true };
755
+ }
756
+ }
757
+ function normalizeHeaderValue(value) {
758
+ return value ? value : void 0;
759
+ }
760
+ function isRecord(value) {
761
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
762
+ }
763
+ function resolveRequestId(headers) {
764
+ return headers.get("x-request-id") ?? headers.get("x-correlation-id") ?? headers.get("x-athena-request-id") ?? void 0;
765
+ }
766
+ function resolveErrorMessage(payload, fallback) {
767
+ if (isRecord(payload)) {
768
+ const messageCandidates = [payload.error, payload.message, payload.details];
769
+ for (const candidate of messageCandidates) {
770
+ if (typeof candidate === "string" && candidate.trim().length > 0) {
771
+ return candidate.trim();
772
+ }
773
+ }
774
+ }
775
+ if (typeof payload === "string" && payload.trim().length > 0) {
776
+ return payload.trim();
777
+ }
778
+ return fallback;
779
+ }
780
+ function detailsFromError(error) {
781
+ return error.toDetails();
782
+ }
783
+ function toQueryScalar(value) {
784
+ if (value === null || value === void 0) return "null";
785
+ if (typeof value === "boolean") return value ? "true" : "false";
786
+ if (typeof value === "number") return Number.isFinite(value) ? String(value) : "null";
787
+ return String(value);
788
+ }
789
+ function toQueryArray(values) {
790
+ return `{${values.map(toQueryScalar).join(",")}}`;
791
+ }
792
+ function toRpcArgumentQueryValue(value) {
793
+ if (Array.isArray(value)) return toQueryArray(value);
794
+ if (value && typeof value === "object") return JSON.stringify(value);
795
+ return toQueryScalar(value);
796
+ }
797
+ function toRpcFilterQueryValue(filter) {
798
+ const value = filter.value;
799
+ switch (filter.operator) {
800
+ case "in": {
801
+ if (!Array.isArray(value)) {
802
+ throw new AthenaGatewayError({
803
+ code: "UNKNOWN_ERROR",
804
+ message: `RPC filter "${filter.column}" with operator "in" requires an array value`,
805
+ status: 0
806
+ });
807
+ }
808
+ return `in.${toQueryArray(value)}`;
809
+ }
810
+ case "is":
811
+ return `is.${toQueryScalar(value)}`;
812
+ case "eq":
813
+ case "neq":
814
+ case "gt":
815
+ case "gte":
816
+ case "lt":
817
+ case "lte":
818
+ case "like":
819
+ case "ilike":
820
+ return `${filter.operator}.${toQueryScalar(value)}`;
821
+ }
822
+ }
823
+ function buildRpcGetEndpoint(payload) {
824
+ const functionName = (payload.function_name ?? payload.function).trim();
825
+ if (!functionName) {
826
+ throw new AthenaGatewayError({
827
+ code: "UNKNOWN_ERROR",
828
+ message: "rpc requires a function name",
829
+ status: 0,
830
+ endpoint: "/gateway/rpc",
831
+ method: "GET"
832
+ });
833
+ }
834
+ const query = new URLSearchParams();
835
+ if (payload.schema) query.set("schema", payload.schema);
836
+ if (payload.select) query.set("select", payload.select);
837
+ if (payload.count) query.set("count", payload.count);
838
+ if (payload.head) query.set("head", "true");
839
+ if (typeof payload.limit === "number") query.set("limit", String(payload.limit));
840
+ if (typeof payload.offset === "number") query.set("offset", String(payload.offset));
841
+ if (payload.order?.column) {
842
+ query.set(
843
+ "order",
844
+ payload.order.ascending === false ? `${payload.order.column}.desc` : payload.order.column
845
+ );
846
+ }
847
+ if (payload.args) {
848
+ for (const [key, value] of Object.entries(payload.args)) {
849
+ query.set(key, toRpcArgumentQueryValue(value));
850
+ }
851
+ }
852
+ if (payload.filters?.length) {
853
+ for (const filter of payload.filters) {
854
+ if (payload.args && Object.prototype.hasOwnProperty.call(payload.args, filter.column)) {
855
+ throw new AthenaGatewayError({
856
+ code: "UNKNOWN_ERROR",
857
+ message: `RPC filter "${filter.column}" conflicts with RPC argument "${filter.column}" in GET mode`,
858
+ status: 0
859
+ });
860
+ }
861
+ query.set(filter.column, toRpcFilterQueryValue(filter));
862
+ }
863
+ }
864
+ const endpoint = `/rpc/${encodeURIComponent(functionName)}`;
865
+ const queryText = query.toString();
866
+ const withQuery = queryText ? `${endpoint}?${queryText}` : endpoint;
867
+ return withQuery;
868
+ }
869
+ function buildHeaders(config, options) {
870
+ const mergedStripNulls = options?.stripNulls ?? true;
871
+ const extraHeaders = {
872
+ ...config.headers ?? {},
873
+ ...options?.headers ?? {}
874
+ };
875
+ const headerClient = extraHeaders["x-athena-client"] ?? extraHeaders["X-Athena-Client"];
876
+ const finalClient = options?.client ?? config.client ?? (typeof headerClient === "string" ? headerClient : void 0) ?? DEFAULT_CLIENT;
877
+ const finalApiKey = options?.apiKey ?? config.apiKey;
878
+ const finalPublishEvent = options?.publishEvent ?? config.publishEvent;
879
+ const headers = {
880
+ "Content-Type": "application/json",
881
+ "X-Athena-Sdk": SDK_HEADER_VALUE
882
+ };
883
+ if (options?.userId ?? config.userId) {
884
+ headers["X-User-Id"] = options?.userId ?? config.userId ?? "";
885
+ }
886
+ if (options?.organizationId ?? config.organizationId) {
887
+ headers["X-Organization-Id"] = options?.organizationId ?? config.organizationId ?? "";
888
+ }
889
+ if (finalClient) {
890
+ headers["X-Athena-Client"] = finalClient;
891
+ }
892
+ const finalBackend = options?.backend ?? config.backend;
893
+ if (finalBackend) {
894
+ const type = typeof finalBackend === "string" ? finalBackend : finalBackend.type;
895
+ if (type) headers["X-Backend-Type"] = type;
896
+ }
897
+ if (typeof mergedStripNulls === "boolean") {
898
+ headers["X-Strip-Nulls"] = mergedStripNulls ? "true" : "false";
899
+ }
900
+ if (finalPublishEvent) {
901
+ headers["X-Publish-Event"] = finalPublishEvent;
902
+ }
903
+ if (finalApiKey) {
904
+ headers["apikey"] = finalApiKey;
905
+ headers["x-api-key"] = headers["x-api-key"] ?? finalApiKey;
906
+ }
907
+ const athenaClientKeys = ["x-athena-client", "X-Athena-Client"];
908
+ Object.entries(extraHeaders).forEach(([key, value]) => {
909
+ if (athenaClientKeys.includes(key)) return;
910
+ const normalized = normalizeHeaderValue(value);
911
+ if (normalized) {
912
+ headers[key] = normalized;
913
+ }
914
+ });
915
+ return headers;
916
+ }
917
+ async function callAthena(config, endpoint, method, payload, options) {
918
+ const baseUrl = (options?.baseUrl ?? config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
919
+ const url = `${baseUrl}${endpoint}`;
920
+ const headers = buildHeaders(config, options);
921
+ try {
922
+ const requestInit = {
923
+ method,
924
+ headers
925
+ };
926
+ if (method !== "GET") {
927
+ requestInit.body = JSON.stringify(payload);
928
+ }
929
+ const response = await fetch(url, requestInit);
930
+ const rawText = await response.text();
931
+ const requestId = resolveRequestId(response.headers);
932
+ const parsedBody = parseResponseBody(
933
+ rawText ?? "",
934
+ response.headers.get("content-type")
935
+ );
936
+ if (parsedBody.parseFailed) {
937
+ const invalidJsonError = new AthenaGatewayError({
938
+ code: "INVALID_JSON",
939
+ message: "Gateway returned malformed JSON",
940
+ status: response.status,
941
+ endpoint,
942
+ method,
943
+ requestId,
944
+ hint: "Verify the gateway response body is valid JSON.",
945
+ cause: rawText.slice(0, 300)
946
+ });
947
+ return {
948
+ ok: false,
949
+ status: response.status,
950
+ data: null,
951
+ error: invalidJsonError.message,
952
+ errorDetails: detailsFromError(invalidJsonError),
953
+ raw: parsedBody.parsed
954
+ };
955
+ }
956
+ const parsed = parsedBody.parsed;
957
+ const parsedPayload = isRecord(parsed) ? parsed : null;
958
+ if (!response.ok) {
959
+ const httpError = new AthenaGatewayError({
960
+ code: "HTTP_ERROR",
961
+ message: resolveErrorMessage(
962
+ parsed,
963
+ `Athena gateway ${method} ${endpoint} failed with status ${response.status}`
964
+ ),
965
+ status: response.status,
966
+ endpoint,
967
+ method,
968
+ requestId
969
+ });
970
+ return {
971
+ ok: false,
972
+ status: response.status,
973
+ data: null,
974
+ error: httpError.message,
975
+ errorDetails: detailsFromError(httpError),
976
+ raw: parsed
977
+ };
978
+ }
979
+ const payloadData = parsedPayload && "data" in parsedPayload ? parsedPayload.data : parsed;
980
+ const payloadCount = parsedPayload && "count" in parsedPayload ? typeof parsedPayload.count === "number" || parsedPayload.count === null ? parsedPayload.count : void 0 : void 0;
981
+ return {
982
+ ok: true,
983
+ status: response.status,
984
+ data: payloadData ?? null,
985
+ count: payloadCount,
986
+ error: void 0,
987
+ errorDetails: null,
988
+ raw: parsed
989
+ };
990
+ } catch (callError) {
991
+ const message = callError instanceof Error ? callError.message : String(callError);
992
+ const networkError = new AthenaGatewayError({
993
+ code: "NETWORK_ERROR",
994
+ message: `Network error while calling ${method} ${endpoint}: ${message}`,
995
+ endpoint,
996
+ method,
997
+ cause: message,
998
+ hint: "Check gateway URL, DNS, and network reachability."
999
+ });
1000
+ return {
1001
+ ok: false,
1002
+ status: 0,
1003
+ data: null,
1004
+ error: networkError.message,
1005
+ errorDetails: detailsFromError(networkError),
1006
+ raw: null
1007
+ };
1008
+ }
1009
+ }
1010
+ function createAthenaGatewayClient(config = {}) {
1011
+ return {
1012
+ baseUrl: (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, ""),
1013
+ buildHeaders(options) {
1014
+ return buildHeaders(config, options);
1015
+ },
1016
+ fetchGateway(payload, options) {
1017
+ return callAthena(config, "/gateway/fetch", "POST", payload, options);
1018
+ },
1019
+ insertGateway(payload, options) {
1020
+ return callAthena(config, "/gateway/insert", "PUT", payload, options);
1021
+ },
1022
+ updateGateway(payload, options) {
1023
+ return callAthena(config, "/gateway/update", "POST", payload, options);
1024
+ },
1025
+ deleteGateway(payload, options) {
1026
+ return callAthena(config, "/gateway/delete", "DELETE", payload, options);
1027
+ },
1028
+ rpcGateway(payload, options) {
1029
+ if (options?.get) {
1030
+ const endpoint = buildRpcGetEndpoint(payload);
1031
+ return callAthena(config, endpoint, "GET", null, options);
1032
+ }
1033
+ return callAthena(config, "/gateway/rpc", "POST", payload, options);
1034
+ },
1035
+ queryGateway(payload, options) {
1036
+ return callAthena(config, "/gateway/query", "POST", payload, options);
1037
+ }
1038
+ };
1039
+ }
1040
+
1041
+ // src/sql-identifiers.ts
1042
+ var SIMPLE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
1043
+ var COMPOSITE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
1044
+ var ALIAS_PATTERN = /^([A-Za-z_][A-Za-z0-9_.]*)\s+(?:as\s+)?([A-Za-z_][A-Za-z0-9_]*)$/i;
1045
+ function quoteIdentifierSegment(identifier) {
1046
+ return `"${identifier.replace(/"/g, '""')}"`;
1047
+ }
1048
+ function quoteQualifiedIdentifier(identifier) {
1049
+ return identifier.split(".").map((segment) => quoteIdentifierSegment(segment)).join(".");
1050
+ }
1051
+ function quoteSelectToken(token) {
1052
+ if (token === "*") return token;
1053
+ if (COMPOSITE_IDENTIFIER_PATTERN.test(token)) {
1054
+ return quoteQualifiedIdentifier(token);
1055
+ }
1056
+ const aliasMatch = ALIAS_PATTERN.exec(token);
1057
+ if (!aliasMatch) {
1058
+ return token;
1059
+ }
1060
+ const [, baseIdentifier, aliasIdentifier] = aliasMatch;
1061
+ if (!COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) || !SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier)) {
1062
+ return token;
1063
+ }
1064
+ return `${quoteQualifiedIdentifier(baseIdentifier)} AS ${quoteIdentifierSegment(aliasIdentifier)}`;
1065
+ }
1066
+ function canAutoQuoteToken(token) {
1067
+ if (token === "*") return true;
1068
+ if (COMPOSITE_IDENTIFIER_PATTERN.test(token)) return true;
1069
+ const aliasMatch = ALIAS_PATTERN.exec(token);
1070
+ if (!aliasMatch) return false;
1071
+ const [, baseIdentifier, aliasIdentifier] = aliasMatch;
1072
+ return COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) && SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier);
1073
+ }
1074
+ function splitTopLevelCommaSeparated(input) {
1075
+ const parts = [];
1076
+ let buffer = "";
1077
+ let singleQuoted = false;
1078
+ let doubleQuoted = false;
1079
+ let depth = 0;
1080
+ for (let index = 0; index < input.length; index += 1) {
1081
+ const char = input[index];
1082
+ const next = index + 1 < input.length ? input[index + 1] : "";
1083
+ if (singleQuoted) {
1084
+ buffer += char;
1085
+ if (char === "'" && next === "'") {
1086
+ buffer += next;
1087
+ index += 1;
1088
+ continue;
1089
+ }
1090
+ if (char === "'") {
1091
+ singleQuoted = false;
1092
+ }
1093
+ continue;
1094
+ }
1095
+ if (doubleQuoted) {
1096
+ buffer += char;
1097
+ if (char === '"' && next === '"') {
1098
+ buffer += next;
1099
+ index += 1;
1100
+ continue;
1101
+ }
1102
+ if (char === '"') {
1103
+ doubleQuoted = false;
1104
+ }
1105
+ continue;
1106
+ }
1107
+ if (char === "'") {
1108
+ singleQuoted = true;
1109
+ buffer += char;
1110
+ continue;
1111
+ }
1112
+ if (char === '"') {
1113
+ doubleQuoted = true;
1114
+ buffer += char;
1115
+ continue;
1116
+ }
1117
+ if (char === "(") {
1118
+ depth += 1;
1119
+ buffer += char;
1120
+ continue;
1121
+ }
1122
+ if (char === ")") {
1123
+ depth -= 1;
1124
+ if (depth < 0) return null;
1125
+ buffer += char;
1126
+ continue;
1127
+ }
1128
+ if (char === "," && depth === 0) {
1129
+ parts.push(buffer.trim());
1130
+ buffer = "";
1131
+ continue;
1132
+ }
1133
+ buffer += char;
1134
+ }
1135
+ if (singleQuoted || doubleQuoted || depth !== 0) {
1136
+ return null;
1137
+ }
1138
+ if (buffer.trim().length > 0) {
1139
+ parts.push(buffer.trim());
1140
+ }
1141
+ return parts;
1142
+ }
1143
+ function quoteSelectColumnsExpression(columns) {
1144
+ const trimmed = columns.trim();
1145
+ if (!trimmed || trimmed === "*") return trimmed || "*";
1146
+ const tokens = splitTopLevelCommaSeparated(trimmed);
1147
+ if (!tokens || tokens.length === 0) {
1148
+ return trimmed;
1149
+ }
1150
+ if (!tokens.every(canAutoQuoteToken)) {
1151
+ return trimmed;
1152
+ }
1153
+ return tokens.map(quoteSelectToken).join(", ");
1154
+ }
1155
+
1156
+ // src/client.ts
1157
+ var DEFAULT_COLUMNS = "*";
1158
+ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1159
+ var SAFE_CAST_PATTERN = /^[a-z_][a-z0-9_]*(?:\[\])?$/i;
1160
+ function formatResult(response) {
1161
+ const result = {
1162
+ data: response.data ?? null,
1163
+ error: response.error ?? null,
1164
+ errorDetails: response.errorDetails ?? null,
1165
+ status: response.status,
1166
+ raw: response.raw
1167
+ };
1168
+ if (response.count !== void 0) {
1169
+ result.count = response.count;
1170
+ }
1171
+ return result;
1172
+ }
1173
+ function toSingleResult(response) {
1174
+ const payload = response.data;
1175
+ const singleData = Array.isArray(payload) ? payload.length ? payload[0] : null : payload ?? null;
1176
+ return {
1177
+ ...response,
1178
+ data: singleData
1179
+ };
1180
+ }
1181
+ function mergeOptions(...options) {
1182
+ return options.reduce((acc, next) => {
1183
+ if (!next) return acc;
1184
+ return { ...acc, ...next };
1185
+ }, void 0);
1186
+ }
1187
+ function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
1188
+ let selectedColumns = defaultColumns === null ? void 0 : defaultColumns;
1189
+ let selectedOptions;
1190
+ let promise = null;
1191
+ const run = (columns, options) => {
1192
+ const payloadColumns = columns ?? selectedColumns;
1193
+ const payloadOptions = options ?? selectedOptions;
1194
+ if (!promise) {
1195
+ promise = executor(payloadColumns, payloadOptions);
1196
+ }
1197
+ return promise;
1198
+ };
1199
+ const mutationQuery = {
1200
+ select(columns = selectedColumns, options) {
1201
+ selectedColumns = columns;
1202
+ selectedOptions = options ?? selectedOptions;
1203
+ return run(columns, options);
1204
+ },
1205
+ returning(columns = selectedColumns, options) {
1206
+ return mutationQuery.select(columns, options);
1207
+ },
1208
+ single(columns = selectedColumns, options) {
1209
+ selectedColumns = columns;
1210
+ selectedOptions = options ?? selectedOptions;
1211
+ return run(columns, options).then(toSingleResult);
1212
+ },
1213
+ maybeSingle(columns = selectedColumns, options) {
1214
+ return mutationQuery.single(columns, options);
1215
+ },
1216
+ then(onfulfilled, onrejected) {
1217
+ return run(selectedColumns, selectedOptions).then(onfulfilled, onrejected);
1218
+ },
1219
+ catch(onrejected) {
1220
+ return run(selectedColumns, selectedOptions).catch(onrejected);
1221
+ },
1222
+ finally(onfinally) {
1223
+ return run(selectedColumns, selectedOptions).finally(onfinally);
1224
+ }
1225
+ };
1226
+ return mutationQuery;
1227
+ }
1228
+ function getResourceId(state) {
1229
+ const candidate = state.conditions.find(
1230
+ (condition) => condition.operator === "eq" && (condition.column === "resource_id" || condition.column === "id")
1231
+ );
1232
+ return candidate?.value?.toString();
1233
+ }
1234
+ function stringifyFilterValue(value) {
1235
+ if (Array.isArray(value)) {
1236
+ return value.join(",");
1237
+ }
1238
+ return String(value);
1239
+ }
1240
+ function isUuidString(value) {
1241
+ return UUID_PATTERN.test(value.trim());
1242
+ }
1243
+ function isUuidIdentifierColumn(column) {
1244
+ return column === "id" || /(?:^|_)uuid(?:_|$)/i.test(column) || /_id$/i.test(column);
1245
+ }
1246
+ function shouldUseUuidTextComparison(column, value) {
1247
+ return typeof value === "string" && isUuidString(value) && isUuidIdentifierColumn(column);
1248
+ }
1249
+ function normalizeCast(cast) {
1250
+ const normalized = cast.trim().toLowerCase();
1251
+ if (!SAFE_CAST_PATTERN.test(normalized)) {
1252
+ throw new Error(`Invalid cast type "${cast}"`);
1253
+ }
1254
+ return normalized;
1255
+ }
1256
+ function escapeSqlStringLiteral(value) {
1257
+ return value.replace(/'/g, "''");
1258
+ }
1259
+ function toSqlLiteral(value) {
1260
+ if (value === null) return "NULL";
1261
+ if (typeof value === "number") return Number.isFinite(value) ? String(value) : "NULL";
1262
+ if (typeof value === "boolean") return value ? "TRUE" : "FALSE";
1263
+ return `'${escapeSqlStringLiteral(value)}'`;
1264
+ }
1265
+ function withCast(expression, cast) {
1266
+ if (!cast) return expression;
1267
+ return `${expression}::${normalizeCast(cast)}`;
1268
+ }
1269
+ function buildSelectColumnsClause(columns) {
1270
+ if (Array.isArray(columns)) {
1271
+ return columns.map((column) => quoteQualifiedIdentifier(column)).join(", ");
1272
+ }
1273
+ return quoteSelectColumnsExpression(columns);
1274
+ }
1275
+ function conditionToSqlClause(condition) {
1276
+ if (!condition.column) return null;
1277
+ const column = withCast(quoteQualifiedIdentifier(condition.column), condition.column_cast);
1278
+ const value = condition.value;
1279
+ const sqlOperator = {
1280
+ eq: "=",
1281
+ neq: "!=",
1282
+ gt: ">",
1283
+ gte: ">=",
1284
+ lt: "<",
1285
+ lte: "<=",
1286
+ like: "LIKE",
1287
+ ilike: "ILIKE"
1288
+ };
1289
+ switch (condition.operator) {
1290
+ case "eq":
1291
+ case "neq":
1292
+ case "gt":
1293
+ case "gte":
1294
+ case "lt":
1295
+ case "lte":
1296
+ case "like":
1297
+ case "ilike": {
1298
+ if (Array.isArray(value) || value === void 0) return null;
1299
+ const rhs = withCast(toSqlLiteral(value), condition.value_cast);
1300
+ return `${column} ${sqlOperator[condition.operator]} ${rhs}`;
1301
+ }
1302
+ case "is": {
1303
+ if (value === null) return `${column} IS NULL`;
1304
+ if (value === true) return `${column} IS TRUE`;
1305
+ if (value === false) return `${column} IS FALSE`;
1306
+ return null;
1307
+ }
1308
+ case "in": {
1309
+ if (!Array.isArray(value)) return null;
1310
+ if (value.length === 0) return "FALSE";
1311
+ const values = value.map((item) => withCast(toSqlLiteral(item), condition.value_cast));
1312
+ return `${column} IN (${values.join(", ")})`;
1313
+ }
1314
+ default:
1315
+ return null;
1316
+ }
1317
+ }
1318
+ function buildTypedSelectQuery(input) {
1319
+ const whereClauses = [];
1320
+ for (const condition of input.conditions) {
1321
+ const clause = conditionToSqlClause(condition);
1322
+ if (!clause) return null;
1323
+ whereClauses.push(clause);
1324
+ }
1325
+ let limit = input.limit;
1326
+ let offset = input.offset;
1327
+ if (limit === void 0 && input.pageSize !== void 0) {
1328
+ limit = input.pageSize;
1329
+ }
1330
+ if (offset === void 0 && input.pageSize !== void 0 && input.currentPage !== void 0 && input.currentPage > 0) {
1331
+ offset = (input.currentPage - 1) * input.pageSize;
1332
+ }
1333
+ const sqlParts = [
1334
+ `SELECT ${buildSelectColumnsClause(input.columns)} FROM ${quoteQualifiedIdentifier(input.tableName)}`
1335
+ ];
1336
+ if (whereClauses.length > 0) {
1337
+ sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
1338
+ }
1339
+ if (input.order?.field) {
1340
+ const direction = input.order.direction === "descending" ? "DESC" : "ASC";
1341
+ sqlParts.push(`ORDER BY ${quoteQualifiedIdentifier(input.order.field)} ${direction}`);
1342
+ }
1343
+ if (limit !== void 0) {
1344
+ sqlParts.push(`LIMIT ${Math.max(0, Math.trunc(limit))}`);
1345
+ }
1346
+ if (offset !== void 0) {
1347
+ sqlParts.push(`OFFSET ${Math.max(0, Math.trunc(offset))}`);
1348
+ }
1349
+ return `${sqlParts.join(" ")};`;
1350
+ }
1351
+ function createFilterMethods(state, addCondition, self) {
1352
+ return {
1353
+ eq(column, value) {
1354
+ if (shouldUseUuidTextComparison(column, value)) {
1355
+ addCondition("eq", column, value, { columnCast: "text" });
1356
+ } else {
1357
+ addCondition("eq", column, value);
1358
+ }
1359
+ return self;
1360
+ },
1361
+ eqCast(column, value, cast) {
1362
+ addCondition("eq", column, value, { valueCast: cast });
1363
+ return self;
1364
+ },
1365
+ eqUuid(column, value) {
1366
+ addCondition("eq", column, value, { valueCast: "uuid" });
1367
+ return self;
1368
+ },
1369
+ match(filters) {
1370
+ Object.entries(filters).forEach(([column, value]) => {
1371
+ if (shouldUseUuidTextComparison(column, value)) {
1372
+ addCondition("eq", column, value, { columnCast: "text" });
1373
+ } else {
1374
+ addCondition("eq", column, value);
1375
+ }
1376
+ });
1377
+ return self;
1378
+ },
1379
+ range(from, to) {
1380
+ state.offset = from;
1381
+ state.limit = to - from + 1;
1382
+ return self;
1383
+ },
1384
+ limit(count) {
1385
+ state.limit = count;
1386
+ return self;
1387
+ },
1388
+ offset(count) {
1389
+ state.offset = count;
1390
+ return self;
1391
+ },
1392
+ currentPage(value) {
1393
+ state.currentPage = value;
1394
+ return self;
1395
+ },
1396
+ pageSize(value) {
1397
+ state.pageSize = value;
1398
+ return self;
1399
+ },
1400
+ totalPages(value) {
1401
+ state.totalPages = value;
1402
+ return self;
1403
+ },
1404
+ order(column, options) {
1405
+ state.order = {
1406
+ field: column,
1407
+ direction: options?.ascending === false ? "descending" : "ascending"
1408
+ };
1409
+ return self;
1410
+ },
1411
+ gt(column, value) {
1412
+ addCondition("gt", column, value);
1413
+ return self;
1414
+ },
1415
+ gte(column, value) {
1416
+ addCondition("gte", column, value);
1417
+ return self;
1418
+ },
1419
+ lt(column, value) {
1420
+ addCondition("lt", column, value);
1421
+ return self;
1422
+ },
1423
+ lte(column, value) {
1424
+ addCondition("lte", column, value);
1425
+ return self;
1426
+ },
1427
+ neq(column, value) {
1428
+ addCondition("neq", column, value);
1429
+ return self;
1430
+ },
1431
+ like(column, value) {
1432
+ addCondition("like", column, value);
1433
+ return self;
1434
+ },
1435
+ ilike(column, value) {
1436
+ addCondition("ilike", column, value);
1437
+ return self;
1438
+ },
1439
+ is(column, value) {
1440
+ addCondition("is", column, value);
1441
+ return self;
1442
+ },
1443
+ in(column, values) {
1444
+ addCondition("in", column, values);
1445
+ return self;
1446
+ },
1447
+ contains(column, values) {
1448
+ addCondition("contains", column, values);
1449
+ return self;
1450
+ },
1451
+ containedBy(column, values) {
1452
+ addCondition("containedBy", column, values);
1453
+ return self;
1454
+ },
1455
+ not(columnOrExpression, operator, value) {
1456
+ if (operator != null && value !== void 0) {
1457
+ addCondition("not", void 0, `${columnOrExpression}.${operator}.${stringifyFilterValue(value)}`);
1458
+ } else {
1459
+ addCondition("not", void 0, columnOrExpression);
1460
+ }
1461
+ return self;
1462
+ },
1463
+ or(expression) {
1464
+ addCondition("or", void 0, expression);
1465
+ return self;
1466
+ }
1467
+ };
1468
+ }
1469
+ function toRpcSelect(columns) {
1470
+ if (!columns) return void 0;
1471
+ return Array.isArray(columns) ? columns.join(",") : columns;
1472
+ }
1473
+ function createRpcFilterMethods(filters, self) {
1474
+ const addFilter = (operator, column, value) => {
1475
+ filters.push({ column, operator, value });
1476
+ };
1477
+ return {
1478
+ eq(column, value) {
1479
+ addFilter("eq", column, value);
1480
+ return self;
1481
+ },
1482
+ neq(column, value) {
1483
+ addFilter("neq", column, value);
1484
+ return self;
1485
+ },
1486
+ gt(column, value) {
1487
+ addFilter("gt", column, value);
1488
+ return self;
1489
+ },
1490
+ gte(column, value) {
1491
+ addFilter("gte", column, value);
1492
+ return self;
1493
+ },
1494
+ lt(column, value) {
1495
+ addFilter("lt", column, value);
1496
+ return self;
1497
+ },
1498
+ lte(column, value) {
1499
+ addFilter("lte", column, value);
1500
+ return self;
1501
+ },
1502
+ like(column, value) {
1503
+ addFilter("like", column, value);
1504
+ return self;
1505
+ },
1506
+ ilike(column, value) {
1507
+ addFilter("ilike", column, value);
1508
+ return self;
1509
+ },
1510
+ is(column, value) {
1511
+ addFilter("is", column, value);
1512
+ return self;
1513
+ },
1514
+ in(column, values) {
1515
+ addFilter("in", column, values);
1516
+ return self;
1517
+ }
1518
+ };
1519
+ }
1520
+ function createRpcBuilder(functionName, args, baseOptions, client) {
1521
+ const state = {
1522
+ filters: []
1523
+ };
1524
+ let selectedColumns;
1525
+ let selectedOptions;
1526
+ let promise = null;
1527
+ const executeRpc = async (columns, options) => {
1528
+ const mergedOptions = mergeOptions(baseOptions, options);
1529
+ const payload = {
1530
+ function: functionName,
1531
+ args,
1532
+ schema: mergedOptions?.schema,
1533
+ select: toRpcSelect(columns),
1534
+ filters: state.filters.length ? [...state.filters] : void 0,
1535
+ count: mergedOptions?.count,
1536
+ head: mergedOptions?.head,
1537
+ limit: state.limit,
1538
+ offset: state.offset,
1539
+ order: state.order
1540
+ };
1541
+ const response = await client.rpcGateway(payload, mergedOptions);
1542
+ return formatResult(response);
1543
+ };
1544
+ const run = (columns, options) => {
1545
+ const payloadColumns = columns ?? selectedColumns;
1546
+ const payloadOptions = options ?? selectedOptions;
1547
+ if (!promise) {
1548
+ promise = executeRpc(payloadColumns, payloadOptions);
1549
+ }
1550
+ return promise;
1551
+ };
1552
+ const builder = {};
1553
+ const filterMethods = createRpcFilterMethods(state.filters, builder);
1554
+ Object.assign(builder, filterMethods, {
1555
+ select(columns = selectedColumns, options) {
1556
+ selectedColumns = columns;
1557
+ selectedOptions = options ?? selectedOptions;
1558
+ return run(columns, options);
1559
+ },
1560
+ async single(columns, options) {
1561
+ const result = await run(columns, options);
1562
+ return toSingleResult(result);
1563
+ },
1564
+ maybeSingle(columns, options) {
1565
+ return builder.single(columns, options);
1566
+ },
1567
+ order(column, options) {
1568
+ state.order = { column, ascending: options?.ascending ?? true };
1569
+ return builder;
1570
+ },
1571
+ limit(count) {
1572
+ state.limit = count;
1573
+ return builder;
1574
+ },
1575
+ offset(count) {
1576
+ state.offset = count;
1577
+ return builder;
1578
+ },
1579
+ range(from, to) {
1580
+ state.offset = from;
1581
+ state.limit = to - from + 1;
1582
+ return builder;
1583
+ },
1584
+ then(onfulfilled, onrejected) {
1585
+ return run(selectedColumns, selectedOptions).then(onfulfilled, onrejected);
1586
+ },
1587
+ catch(onrejected) {
1588
+ return run(selectedColumns, selectedOptions).catch(onrejected);
1589
+ },
1590
+ finally(onfinally) {
1591
+ return run(selectedColumns, selectedOptions).finally(onfinally);
1592
+ }
1593
+ });
1594
+ return builder;
1595
+ }
1596
+ function createTableBuilder(tableName, client) {
1597
+ const state = {
1598
+ conditions: []
1599
+ };
1600
+ const addCondition = (operator, column, value, hints) => {
1601
+ const condition = { operator };
1602
+ if (column) {
1603
+ condition.column = column;
1604
+ if (operator === "eq") {
1605
+ condition.eq_column = column;
1606
+ }
1607
+ }
1608
+ if (value !== void 0) {
1609
+ condition.value = value;
1610
+ if (operator === "eq") {
1611
+ condition.eq_value = value;
1612
+ }
1613
+ }
1614
+ if (hints?.valueCast) {
1615
+ condition.value_cast = hints.valueCast;
1616
+ if (operator === "eq") {
1617
+ condition.eq_value_cast = hints.valueCast;
1618
+ }
1619
+ }
1620
+ if (hints?.columnCast) {
1621
+ condition.column_cast = hints.columnCast;
1622
+ if (operator === "eq") {
1623
+ condition.eq_column_cast = hints.columnCast;
1624
+ }
1625
+ }
1626
+ state.conditions.push(condition);
1627
+ };
1628
+ const builder = {};
1629
+ const filterMethods = createFilterMethods(state, addCondition, builder);
1630
+ const runSelect = async (columns = DEFAULT_COLUMNS, options) => {
1631
+ const conditions = state.conditions.length ? state.conditions.map((condition) => ({ ...condition })) : void 0;
1632
+ const hasTypedEqualityComparison = conditions?.some(
1633
+ (condition) => condition.operator === "eq" && (condition.value_cast !== void 0 || condition.column_cast !== void 0)
1634
+ ) ?? false;
1635
+ if (hasTypedEqualityComparison && !options?.head && !options?.count && conditions) {
1636
+ const query = buildTypedSelectQuery({
1637
+ tableName,
1638
+ columns,
1639
+ conditions,
1640
+ limit: state.limit,
1641
+ offset: state.offset,
1642
+ currentPage: state.currentPage,
1643
+ pageSize: state.pageSize,
1644
+ order: state.order
1645
+ });
1646
+ if (query) {
1647
+ const queryResponse = await client.queryGateway({ query }, options);
1648
+ return formatResult(queryResponse);
1649
+ }
1650
+ }
1651
+ const payload = {
1652
+ table_name: tableName,
1653
+ columns,
1654
+ conditions,
1655
+ limit: state.limit,
1656
+ offset: state.offset,
1657
+ current_page: state.currentPage,
1658
+ page_size: state.pageSize,
1659
+ total_pages: state.totalPages,
1660
+ sort_by: state.order,
1661
+ strip_nulls: options?.stripNulls ?? true,
1662
+ count: options?.count,
1663
+ head: options?.head
1664
+ };
1665
+ const response = await client.fetchGateway(payload, options);
1666
+ return formatResult(response);
1667
+ };
1668
+ const createSelectChain = (columns, options) => {
1669
+ const chain = {};
1670
+ const filterMethods2 = createFilterMethods(state, addCondition, chain);
1671
+ Object.assign(chain, filterMethods2, {
1672
+ async single(cols, opts) {
1673
+ const r = await runSelect(cols ?? columns, opts ?? options);
1674
+ return toSingleResult(r);
1675
+ },
1676
+ maybeSingle(cols, opts) {
1677
+ return chain.single(cols, opts);
1678
+ },
1679
+ then(onfulfilled, onrejected) {
1680
+ return runSelect(columns, options).then(onfulfilled, onrejected);
1681
+ },
1682
+ catch(onrejected) {
1683
+ return runSelect(columns, options).catch(onrejected);
1684
+ },
1685
+ finally(onfinally) {
1686
+ return runSelect(columns, options).finally(onfinally);
1687
+ }
1688
+ });
1689
+ return chain;
1690
+ };
1691
+ Object.assign(builder, filterMethods, {
1692
+ reset() {
1693
+ state.conditions = [];
1694
+ state.limit = void 0;
1695
+ state.offset = void 0;
1696
+ state.order = void 0;
1697
+ state.currentPage = void 0;
1698
+ state.pageSize = void 0;
1699
+ state.totalPages = void 0;
1700
+ return builder;
1701
+ },
1702
+ select(columns = DEFAULT_COLUMNS, options) {
1703
+ return createSelectChain(columns, options);
1704
+ },
1705
+ insert(values, options) {
1706
+ if (Array.isArray(values)) {
1707
+ const executeInsertMany = async (columns, selectOptions) => {
1708
+ const mergedOptions = mergeOptions(options, selectOptions);
1709
+ const payload = {
1710
+ table_name: tableName,
1711
+ insert_body: values
1712
+ };
1713
+ if (columns) payload.columns = columns;
1714
+ if (mergedOptions?.count) payload.count = mergedOptions.count;
1715
+ if (mergedOptions?.head) payload.head = mergedOptions.head;
1716
+ if (mergedOptions?.defaultToNull !== void 0) {
1717
+ payload.default_to_null = mergedOptions.defaultToNull;
1718
+ }
1719
+ const response = await client.insertGateway(payload, mergedOptions);
1720
+ return formatResult(response);
1721
+ };
1722
+ return createMutationQuery(executeInsertMany);
1723
+ }
1724
+ const executeInsertOne = async (columns, selectOptions) => {
1725
+ const mergedOptions = mergeOptions(options, selectOptions);
1726
+ const payload = {
1727
+ table_name: tableName,
1728
+ insert_body: values
1729
+ };
1730
+ if (columns) payload.columns = columns;
1731
+ if (mergedOptions?.count) payload.count = mergedOptions.count;
1732
+ if (mergedOptions?.head) payload.head = mergedOptions.head;
1733
+ if (mergedOptions?.defaultToNull !== void 0) {
1734
+ payload.default_to_null = mergedOptions.defaultToNull;
1735
+ }
1736
+ const response = await client.insertGateway(payload, mergedOptions);
1737
+ return formatResult(response);
1738
+ };
1739
+ return createMutationQuery(executeInsertOne);
1740
+ },
1741
+ upsert(values, options) {
1742
+ if (Array.isArray(values)) {
1743
+ const executeUpsertMany = async (columns, selectOptions) => {
1744
+ const mergedOptions = mergeOptions(options, selectOptions);
1745
+ const payload = {
1746
+ table_name: tableName,
1747
+ insert_body: values,
1748
+ update_body: options?.updateBody ? options.updateBody : void 0
1749
+ };
1750
+ if (columns) payload.columns = columns;
1751
+ if (options?.onConflict) payload.on_conflict = options.onConflict;
1752
+ if (mergedOptions?.count) payload.count = mergedOptions.count;
1753
+ if (mergedOptions?.head) payload.head = mergedOptions.head;
1754
+ if (mergedOptions?.defaultToNull !== void 0) {
1755
+ payload.default_to_null = mergedOptions.defaultToNull;
1756
+ }
1757
+ const response = await client.insertGateway(payload, mergedOptions);
1758
+ return formatResult(response);
1759
+ };
1760
+ return createMutationQuery(executeUpsertMany);
1761
+ }
1762
+ const executeUpsertOne = async (columns, selectOptions) => {
1763
+ const mergedOptions = mergeOptions(options, selectOptions);
1764
+ const payload = {
1765
+ table_name: tableName,
1766
+ insert_body: values,
1767
+ update_body: options?.updateBody ? options.updateBody : void 0
1768
+ };
1769
+ if (columns) payload.columns = columns;
1770
+ if (options?.onConflict) payload.on_conflict = options.onConflict;
1771
+ if (mergedOptions?.count) payload.count = mergedOptions.count;
1772
+ if (mergedOptions?.head) payload.head = mergedOptions.head;
1773
+ if (mergedOptions?.defaultToNull !== void 0) {
1774
+ payload.default_to_null = mergedOptions.defaultToNull;
1775
+ }
1776
+ const response = await client.insertGateway(payload, mergedOptions);
1777
+ return formatResult(response);
1778
+ };
1779
+ return createMutationQuery(executeUpsertOne);
1780
+ },
1781
+ update(values, options) {
1782
+ const executeUpdate = async (columns, selectOptions) => {
1783
+ const filters = state.conditions.length ? [...state.conditions] : void 0;
1784
+ const mergedOptions = mergeOptions(options, selectOptions);
1785
+ const payload = {
1786
+ table_name: tableName,
1787
+ set: values,
1788
+ conditions: filters,
1789
+ strip_nulls: mergedOptions?.stripNulls ?? true
1790
+ };
1791
+ if (state.order) payload.sort_by = state.order;
1792
+ if (state.currentPage !== void 0) payload.current_page = state.currentPage;
1793
+ if (state.pageSize !== void 0) payload.page_size = state.pageSize;
1794
+ if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
1795
+ if (columns) payload.columns = columns;
1796
+ const response = await client.updateGateway(payload, mergedOptions);
1797
+ return formatResult(response);
1798
+ };
1799
+ const mutation = createMutationQuery(executeUpdate, null);
1800
+ const updateChain = {};
1801
+ const filterMethods2 = createFilterMethods(state, addCondition, updateChain);
1802
+ Object.assign(updateChain, filterMethods2, mutation);
1803
+ return updateChain;
1804
+ },
1805
+ delete(options) {
1806
+ const filters = state.conditions.length ? [...state.conditions] : void 0;
1807
+ const resourceId = options?.resourceId ?? getResourceId(state);
1808
+ if (!resourceId && !filters?.length) {
1809
+ throw new Error('delete requires a resource_id either via eq("resource_id", ...) or options.resourceId');
1810
+ }
1811
+ const executeDelete = async (columns, selectOptions) => {
1812
+ const mergedOptions = mergeOptions(options, selectOptions);
1813
+ const payload = {
1814
+ table_name: tableName,
1815
+ resource_id: resourceId,
1816
+ conditions: filters
1817
+ };
1818
+ if (state.order) payload.sort_by = state.order;
1819
+ if (state.currentPage !== void 0) payload.current_page = state.currentPage;
1820
+ if (state.pageSize !== void 0) payload.page_size = state.pageSize;
1821
+ if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
1822
+ if (columns) payload.columns = columns;
1823
+ const response = await client.deleteGateway(payload, mergedOptions);
1824
+ return formatResult(response);
1825
+ };
1826
+ return createMutationQuery(executeDelete, null);
1827
+ },
1828
+ async single(columns, options) {
1829
+ const response = await builder.select(columns, options);
1830
+ return toSingleResult(response);
1831
+ },
1832
+ async maybeSingle(columns, options) {
1833
+ return builder.single(columns, options);
1834
+ }
1835
+ });
1836
+ return builder;
1837
+ }
1838
+ function createQueryBuilder(client) {
1839
+ return async function query(query, options) {
1840
+ const normalizedQuery = query.trim();
1841
+ if (!normalizedQuery) {
1842
+ throw new Error("query requires a non-empty string");
1843
+ }
1844
+ const response = await client.queryGateway({ query: normalizedQuery }, options);
1845
+ return formatResult(response);
1846
+ };
1847
+ }
1848
+ function createClientFromConfig(config) {
1849
+ const gateway = createAthenaGatewayClient({
1850
+ baseUrl: config.baseUrl,
1851
+ apiKey: config.apiKey,
1852
+ client: config.client,
1853
+ backend: config.backend,
1854
+ headers: config.headers
1855
+ });
1856
+ return {
1857
+ from(table) {
1858
+ return createTableBuilder(table, gateway);
1859
+ },
1860
+ rpc(fn, args, options) {
1861
+ const normalizedFn = fn.trim();
1862
+ if (!normalizedFn) {
1863
+ throw new Error("rpc requires a function name");
1864
+ }
1865
+ return createRpcBuilder(
1866
+ normalizedFn,
1867
+ args,
1868
+ options,
1869
+ gateway
1870
+ );
1871
+ },
1872
+ query: createQueryBuilder(gateway)
1873
+ };
1874
+ }
1875
+ var DEFAULT_BACKEND = { type: "athena" };
1876
+ function toBackendConfig(b) {
1877
+ if (!b) return DEFAULT_BACKEND;
1878
+ return typeof b === "string" ? { type: b } : b;
1879
+ }
1880
+ var AthenaClientBuilderImpl = class {
1881
+ baseUrl;
1882
+ apiKey;
1883
+ backendConfig = DEFAULT_BACKEND;
1884
+ clientName;
1885
+ defaultHeaders;
1886
+ isHealthTrackingEnabled = false;
1887
+ url(url) {
1888
+ this.baseUrl = url;
1889
+ return this;
1890
+ }
1891
+ key(apiKey) {
1892
+ this.apiKey = apiKey;
1893
+ return this;
1894
+ }
1895
+ backend(backend) {
1896
+ this.backendConfig = toBackendConfig(backend);
1897
+ return this;
1898
+ }
1899
+ client(clientName) {
1900
+ this.clientName = clientName;
1901
+ return this;
1902
+ }
1903
+ headers(headers) {
1904
+ this.defaultHeaders = headers;
1905
+ return this;
1906
+ }
1907
+ healthTracking(enabled) {
1908
+ this.isHealthTrackingEnabled = enabled;
1909
+ return this;
1910
+ }
1911
+ build() {
1912
+ if (!this.baseUrl || !this.apiKey) {
1913
+ throw new Error("AthenaClient requires url and key; call .url() and .key() before .build()");
1914
+ }
1915
+ return createClientFromConfig({
1916
+ baseUrl: this.baseUrl,
1917
+ apiKey: this.apiKey,
1918
+ client: this.clientName,
1919
+ backend: this.backendConfig,
1920
+ headers: this.defaultHeaders,
1921
+ healthTracking: this.isHealthTrackingEnabled
1922
+ });
1923
+ }
1924
+ };
1925
+ var AthenaClient = class _AthenaClient {
1926
+ /** Create a fluent builder for a strongly-typed Athena SDK client. */
1927
+ static builder() {
1928
+ return new AthenaClientBuilderImpl();
1929
+ }
1930
+ /** Build a client from process environment variables. */
1931
+ static fromEnvironment() {
1932
+ const url = process.env.ATHENA_URL ?? process.env.ATHENA_GATEWAY_URL;
1933
+ const key = process.env.ATHENA_API_KEY ?? process.env.ATHENA_GATEWAY_API_KEY;
1934
+ if (!url || !key) {
1935
+ throw new Error(
1936
+ "ATHENA_URL and ATHENA_API_KEY (or ATHENA_GATEWAY_URL and ATHENA_GATEWAY_API_KEY) are required"
1937
+ );
1938
+ }
1939
+ return _AthenaClient.builder().url(url).key(key).build();
1940
+ }
1941
+ };
1942
+ function createClient(url, apiKey, options) {
1943
+ const b = AthenaClient.builder().url(url).key(apiKey).backend(toBackendConfig(options?.backend));
1944
+ if (options?.client) b.client(options.client);
1945
+ if (options?.headers && Object.keys(options.headers).length > 0) b.headers(options.headers);
1946
+ return b.build();
1947
+ }
1948
+
1949
+ // src/schema/postgres-introspection-core.ts
1950
+ var POSTGRES_CATALOG_SQL = {
1951
+ columns: `
1952
+ SELECT
1953
+ n.nspname AS schema_name,
1954
+ c.relname AS table_name,
1955
+ a.attname AS column_name,
1956
+ format_type(a.atttypid, a.atttypmod) AS data_type,
1957
+ t.typname AS udt_name,
1958
+ t.typtype AS type_kind_code,
1959
+ t.oid AS type_oid,
1960
+ NOT a.attnotnull AS is_nullable,
1961
+ (ad.adbin IS NOT NULL) AS has_default,
1962
+ (a.attgenerated <> '') AS is_generated,
1963
+ a.attndims AS array_dimensions
1964
+ FROM pg_attribute a
1965
+ JOIN pg_class c ON c.oid = a.attrelid
1966
+ JOIN pg_namespace n ON n.oid = c.relnamespace
1967
+ JOIN pg_type t ON t.oid = a.atttypid
1968
+ LEFT JOIN pg_attrdef ad ON ad.adrelid = a.attrelid AND ad.adnum = a.attnum
1969
+ WHERE c.relkind IN ('r', 'p')
1970
+ AND a.attnum > 0
1971
+ AND NOT a.attisdropped
1972
+ AND n.nspname = ANY($1::text[])
1973
+ ORDER BY n.nspname, c.relname, a.attnum;
1974
+ `,
1975
+ enums: `
1976
+ SELECT
1977
+ t.oid AS type_oid,
1978
+ e.enumlabel AS enum_label
1979
+ FROM pg_type t
1980
+ JOIN pg_enum e ON e.enumtypid = t.oid
1981
+ ORDER BY t.oid, e.enumsortorder;
1982
+ `,
1983
+ primaryKeys: `
1984
+ SELECT
1985
+ n.nspname AS schema_name,
1986
+ c.relname AS table_name,
1987
+ ARRAY_AGG(a.attname ORDER BY ck.ordinality) AS columns
1988
+ FROM pg_constraint con
1989
+ JOIN pg_class c ON c.oid = con.conrelid
1990
+ JOIN pg_namespace n ON n.oid = c.relnamespace
1991
+ JOIN unnest(con.conkey) WITH ORDINALITY AS ck(attnum, ordinality) ON TRUE
1992
+ JOIN pg_attribute a ON a.attrelid = c.oid AND a.attnum = ck.attnum
1993
+ WHERE con.contype = 'p'
1994
+ AND n.nspname = ANY($1::text[])
1995
+ GROUP BY n.nspname, c.relname
1996
+ ORDER BY n.nspname, c.relname;
1997
+ `,
1998
+ foreignKeys: `
1999
+ SELECT
2000
+ sn.nspname AS source_schema,
2001
+ sc.relname AS source_table,
2002
+ con.conname AS constraint_name,
2003
+ ARRAY_AGG(sa.attname ORDER BY cols.ordinality) AS source_columns,
2004
+ tn.nspname AS target_schema,
2005
+ tc.relname AS target_table,
2006
+ ARRAY_AGG(ta.attname ORDER BY cols.ordinality) AS target_columns,
2007
+ EXISTS (
2008
+ SELECT 1
2009
+ FROM pg_constraint uq
2010
+ WHERE uq.conrelid = con.conrelid
2011
+ AND uq.contype IN ('p', 'u')
2012
+ AND uq.conkey = con.conkey
2013
+ ) AS source_is_unique
2014
+ FROM pg_constraint con
2015
+ JOIN pg_class sc ON sc.oid = con.conrelid
2016
+ JOIN pg_namespace sn ON sn.oid = sc.relnamespace
2017
+ JOIN pg_class tc ON tc.oid = con.confrelid
2018
+ JOIN pg_namespace tn ON tn.oid = tc.relnamespace
2019
+ JOIN unnest(con.conkey, con.confkey) WITH ORDINALITY AS cols(source_attnum, target_attnum, ordinality) ON TRUE
2020
+ JOIN pg_attribute sa ON sa.attrelid = con.conrelid AND sa.attnum = cols.source_attnum
2021
+ JOIN pg_attribute ta ON ta.attrelid = con.confrelid AND ta.attnum = cols.target_attnum
2022
+ WHERE con.contype = 'f'
2023
+ AND sn.nspname = ANY($1::text[])
2024
+ AND tn.nspname = ANY($1::text[])
2025
+ GROUP BY
2026
+ sn.nspname,
2027
+ sc.relname,
2028
+ con.conname,
2029
+ tn.nspname,
2030
+ tc.relname,
2031
+ con.conkey,
2032
+ con.conrelid
2033
+ ORDER BY sn.nspname, sc.relname, con.conname;
2034
+ `
2035
+ };
2036
+ function tableKey(schema, table) {
2037
+ return `${schema}.${table}`;
2038
+ }
2039
+ function relationKey(...parts) {
2040
+ const base = parts.join("_").replace(/[^A-Za-z0-9_]+/g, "_").replace(/^_+|_+$/g, "");
2041
+ return base.length > 0 ? base : "relation";
2042
+ }
2043
+ function toTypeKind(code) {
2044
+ switch (code) {
2045
+ case "e":
2046
+ return "enum";
2047
+ case "d":
2048
+ return "domain";
2049
+ case "r":
2050
+ return "range";
2051
+ case "m":
2052
+ return "multirange";
2053
+ case "c":
2054
+ return "composite";
2055
+ default:
2056
+ return "scalar";
2057
+ }
2058
+ }
2059
+ function escapeSqlLiteral(value) {
2060
+ return value.replace(/'/g, "''");
2061
+ }
2062
+ function buildSchemaArrayLiteral(schemas) {
2063
+ const normalized = schemas.length > 0 ? schemas : ["public"];
2064
+ const literals = normalized.map((schema) => `'${escapeSqlLiteral(schema)}'`).join(", ");
2065
+ return `ARRAY[${literals}]`;
2066
+ }
2067
+ function inlineSchemaLiteral(sql, schemas) {
2068
+ const schemaArray = `${buildSchemaArrayLiteral(schemas)}::text[]`;
2069
+ return sql.replace(/\$1::text\[\]/g, schemaArray);
2070
+ }
2071
+ function buildGatewayCatalogQueries(schemas) {
2072
+ return {
2073
+ columns: inlineSchemaLiteral(POSTGRES_CATALOG_SQL.columns, schemas),
2074
+ enums: POSTGRES_CATALOG_SQL.enums,
2075
+ primaryKeys: inlineSchemaLiteral(POSTGRES_CATALOG_SQL.primaryKeys, schemas),
2076
+ foreignKeys: inlineSchemaLiteral(POSTGRES_CATALOG_SQL.foreignKeys, schemas)
2077
+ };
2078
+ }
2079
+ var PostgresCatalogSnapshotAssembler = class {
2080
+ schemas = {};
2081
+ addColumnRows(columnRows, enumMap) {
2082
+ for (const row of columnRows) {
2083
+ const table = this.ensureTable(row.schema_name, row.table_name);
2084
+ table.columns[row.column_name] = {
2085
+ name: row.column_name,
2086
+ dataType: row.data_type,
2087
+ udtName: row.udt_name,
2088
+ typeKind: toTypeKind(row.type_kind_code),
2089
+ isNullable: row.is_nullable,
2090
+ isPrimaryKey: false,
2091
+ hasDefault: row.has_default,
2092
+ isGenerated: row.is_generated,
2093
+ arrayDimensions: row.array_dimensions ?? 0,
2094
+ enumValues: enumMap.get(row.type_oid)
2095
+ };
2096
+ }
2097
+ }
2098
+ addPrimaryKeyRows(primaryKeyRows) {
2099
+ for (const row of primaryKeyRows) {
2100
+ const table = this.ensureTable(row.schema_name, row.table_name);
2101
+ table.primaryKey = row.columns;
2102
+ for (const columnName of row.columns) {
2103
+ const column = table.columns[columnName];
2104
+ if (column) {
2105
+ column.isPrimaryKey = true;
2106
+ }
2107
+ }
2108
+ }
2109
+ }
2110
+ addForeignKeyRows(foreignKeyRows) {
2111
+ for (const row of foreignKeyRows) {
2112
+ const sourceTable = this.ensureTable(row.source_schema, row.source_table);
2113
+ const targetTable = this.ensureTable(row.target_schema, row.target_table);
2114
+ const sourceRelationKind = row.source_is_unique ? "one-to-one" : "many-to-one";
2115
+ this.upsertRelation(sourceTable, relationKey(row.constraint_name, row.target_table), {
2116
+ name: row.constraint_name,
2117
+ kind: sourceRelationKind,
2118
+ sourceColumns: row.source_columns,
2119
+ targetSchema: row.target_schema,
2120
+ targetModel: row.target_table,
2121
+ targetColumns: row.target_columns
2122
+ });
2123
+ const targetRelationKind = row.source_is_unique ? "one-to-one" : "one-to-many";
2124
+ this.upsertRelation(targetTable, relationKey(row.source_table), {
2125
+ name: relationKey(row.source_table, row.constraint_name),
2126
+ kind: targetRelationKind,
2127
+ sourceColumns: row.target_columns,
2128
+ targetSchema: row.source_schema,
2129
+ targetModel: row.source_table,
2130
+ targetColumns: row.source_columns
2131
+ });
2132
+ }
2133
+ }
2134
+ addManyToManyRows(foreignKeyRows) {
2135
+ const bySourceTable = /* @__PURE__ */ new Map();
2136
+ for (const fk of foreignKeyRows) {
2137
+ const key = tableKey(fk.source_schema, fk.source_table);
2138
+ const current = bySourceTable.get(key) ?? {
2139
+ schema: fk.source_schema,
2140
+ table: fk.source_table,
2141
+ foreignKeys: []
2142
+ };
2143
+ current.foreignKeys.push(fk);
2144
+ bySourceTable.set(key, current);
2145
+ }
2146
+ for (const candidate of bySourceTable.values()) {
2147
+ const bridgeTable = this.schemas[candidate.schema]?.tables[candidate.table];
2148
+ if (!bridgeTable) continue;
2149
+ const primaryKey = bridgeTable.primaryKey;
2150
+ if (candidate.foreignKeys.length !== 2 || primaryKey.length === 0) continue;
2151
+ const combinedForeignColumns = Array.from(
2152
+ new Set(candidate.foreignKeys.flatMap((fk) => fk.source_columns))
2153
+ );
2154
+ if (combinedForeignColumns.length !== primaryKey.length || !primaryKey.every((column) => combinedForeignColumns.includes(column))) {
2155
+ continue;
2156
+ }
2157
+ const [first, second] = candidate.foreignKeys;
2158
+ const firstTarget = this.schemas[first.target_schema]?.tables[first.target_table];
2159
+ const secondTarget = this.schemas[second.target_schema]?.tables[second.target_table];
2160
+ if (!firstTarget || !secondTarget) {
2161
+ continue;
2162
+ }
2163
+ this.upsertRelation(firstTarget, relationKey(second.target_table), {
2164
+ name: relationKey(candidate.table, first.constraint_name, second.constraint_name),
2165
+ kind: "many-to-many",
2166
+ sourceColumns: first.target_columns,
2167
+ targetSchema: second.target_schema,
2168
+ targetModel: second.target_table,
2169
+ targetColumns: second.target_columns,
2170
+ through: {
2171
+ schema: candidate.schema,
2172
+ model: candidate.table,
2173
+ sourceColumns: first.source_columns,
2174
+ targetColumns: second.source_columns
2175
+ }
2176
+ });
2177
+ this.upsertRelation(secondTarget, relationKey(first.target_table), {
2178
+ name: relationKey(candidate.table, second.constraint_name, first.constraint_name),
2179
+ kind: "many-to-many",
2180
+ sourceColumns: second.target_columns,
2181
+ targetSchema: first.target_schema,
2182
+ targetModel: first.target_table,
2183
+ targetColumns: first.target_columns,
2184
+ through: {
2185
+ schema: candidate.schema,
2186
+ model: candidate.table,
2187
+ sourceColumns: second.source_columns,
2188
+ targetColumns: first.source_columns
2189
+ }
2190
+ });
2191
+ }
2192
+ }
2193
+ toSchemas() {
2194
+ return this.schemas;
2195
+ }
2196
+ ensureTable(schemaName, tableName) {
2197
+ if (!this.schemas[schemaName]) {
2198
+ this.schemas[schemaName] = {
2199
+ name: schemaName,
2200
+ tables: {}
2201
+ };
2202
+ }
2203
+ const schema = this.schemas[schemaName];
2204
+ if (!schema.tables[tableName]) {
2205
+ schema.tables[tableName] = {
2206
+ schema: schemaName,
2207
+ name: tableName,
2208
+ columns: {},
2209
+ primaryKey: [],
2210
+ relations: {}
2211
+ };
2212
+ }
2213
+ return schema.tables[tableName];
2214
+ }
2215
+ upsertRelation(table, baseKey, relation) {
2216
+ let key = baseKey;
2217
+ let suffix = 2;
2218
+ while (table.relations[key]) {
2219
+ key = `${baseKey}_${suffix}`;
2220
+ suffix += 1;
2221
+ }
2222
+ table.relations[key] = relation;
2223
+ }
2224
+ };
2225
+ var PgCatalogClient = class {
2226
+ constructor(pool) {
2227
+ this.pool = pool;
2228
+ }
2229
+ async queryColumns(schemas) {
2230
+ const result = await this.pool.query(POSTGRES_CATALOG_SQL.columns, [schemas]);
2231
+ return result.rows;
2232
+ }
2233
+ async queryEnums() {
2234
+ const result = await this.pool.query(POSTGRES_CATALOG_SQL.enums);
2235
+ const enumMap = /* @__PURE__ */ new Map();
2236
+ for (const row of result.rows) {
2237
+ const existing = enumMap.get(row.type_oid) ?? [];
2238
+ existing.push(row.enum_label);
2239
+ enumMap.set(row.type_oid, existing);
2240
+ }
2241
+ return enumMap;
2242
+ }
2243
+ async queryPrimaryKeys(schemas) {
2244
+ const result = await this.pool.query(POSTGRES_CATALOG_SQL.primaryKeys, [schemas]);
2245
+ return result.rows;
2246
+ }
2247
+ async queryForeignKeys(schemas) {
2248
+ const result = await this.pool.query(POSTGRES_CATALOG_SQL.foreignKeys, [schemas]);
2249
+ return result.rows;
2250
+ }
2251
+ };
2252
+ var PostgresIntrospectionProvider = class {
2253
+ backend = "postgresql";
2254
+ connectionString;
2255
+ database;
2256
+ constructor(options) {
2257
+ this.connectionString = options.connectionString;
2258
+ this.database = options.database ?? "postgres";
2259
+ }
2260
+ async inspect(options) {
2261
+ const schemas = options?.schemas && options.schemas.length > 0 ? options.schemas : ["public"];
2262
+ const pool = new pg.Pool({
2263
+ connectionString: this.connectionString
2264
+ });
2265
+ const catalogClient = new PgCatalogClient(pool);
2266
+ try {
2267
+ const [columnRows, enumMap, primaryKeyRows, foreignKeyRows] = await Promise.all([
2268
+ catalogClient.queryColumns(schemas),
2269
+ catalogClient.queryEnums(),
2270
+ catalogClient.queryPrimaryKeys(schemas),
2271
+ catalogClient.queryForeignKeys(schemas)
2272
+ ]);
2273
+ const assembler = new PostgresCatalogSnapshotAssembler();
2274
+ assembler.addColumnRows(columnRows, enumMap);
2275
+ assembler.addPrimaryKeyRows(primaryKeyRows);
2276
+ assembler.addForeignKeyRows(foreignKeyRows);
2277
+ assembler.addManyToManyRows(foreignKeyRows);
2278
+ return {
2279
+ backend: "postgresql",
2280
+ database: this.database,
2281
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2282
+ schemas: assembler.toSchemas()
2283
+ };
2284
+ } finally {
2285
+ await pool.end();
2286
+ }
2287
+ }
2288
+ };
2289
+ function createPostgresIntrospectionProvider(options) {
2290
+ return new PostgresIntrospectionProvider(options);
2291
+ }
2292
+
2293
+ // src/generator/providers.ts
2294
+ var AthenaGatewayCatalogClient = class {
2295
+ constructor(client) {
2296
+ this.client = client;
2297
+ }
2298
+ async queryRows(query) {
2299
+ const result = await this.client.query(query);
2300
+ if (result.error || result.status < 200 || result.status >= 300) {
2301
+ throw new Error(result.error ?? `Gateway query failed with status ${result.status}`);
2302
+ }
2303
+ return result.data ?? [];
2304
+ }
2305
+ async queryColumns(query) {
2306
+ return this.queryRows(query);
2307
+ }
2308
+ async queryEnums(query) {
2309
+ const rows = await this.queryRows(query);
2310
+ const enumMap = /* @__PURE__ */ new Map();
2311
+ for (const row of rows) {
2312
+ const existing = enumMap.get(row.type_oid) ?? [];
2313
+ existing.push(row.enum_label);
2314
+ enumMap.set(row.type_oid, existing);
2315
+ }
2316
+ return enumMap;
2317
+ }
2318
+ async queryPrimaryKeys(query) {
2319
+ return this.queryRows(query);
2320
+ }
2321
+ async queryForeignKeys(query) {
2322
+ return this.queryRows(query);
2323
+ }
2324
+ };
2325
+ var AthenaGatewayPostgresIntrospectionProvider = class {
2326
+ constructor(config) {
2327
+ this.config = config;
2328
+ this.client = createClient(this.config.gatewayUrl, this.config.apiKey, {
2329
+ backend: {
2330
+ type: this.config.backend ?? "postgresql"
2331
+ }
2332
+ });
2333
+ }
2334
+ backend = "postgresql";
2335
+ client;
2336
+ async inspect(options) {
2337
+ const schemas = options?.schemas && options.schemas.length > 0 ? options.schemas : this.config.schemas && this.config.schemas.length > 0 ? this.config.schemas : ["public"];
2338
+ const catalogClient = new AthenaGatewayCatalogClient(this.client);
2339
+ const queries = buildGatewayCatalogQueries(schemas);
2340
+ const [columnRows, enumMap, primaryKeyRows, foreignKeyRows] = await Promise.all([
2341
+ catalogClient.queryColumns(queries.columns),
2342
+ catalogClient.queryEnums(queries.enums),
2343
+ catalogClient.queryPrimaryKeys(queries.primaryKeys),
2344
+ catalogClient.queryForeignKeys(queries.foreignKeys)
2345
+ ]);
2346
+ const assembler = new PostgresCatalogSnapshotAssembler();
2347
+ assembler.addColumnRows(columnRows, enumMap);
2348
+ assembler.addPrimaryKeyRows(primaryKeyRows);
2349
+ assembler.addForeignKeyRows(foreignKeyRows);
2350
+ assembler.addManyToManyRows(foreignKeyRows);
2351
+ return {
2352
+ backend: "postgresql",
2353
+ database: this.config.database,
2354
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2355
+ schemas: assembler.toSchemas()
2356
+ };
2357
+ }
2358
+ };
2359
+ var ScyllaIntrospectionProvider = class {
2360
+ constructor(config) {
2361
+ this.config = config;
2362
+ }
2363
+ backend = "scylladb";
2364
+ async inspect() {
2365
+ throw new Error(
2366
+ `Scylla introspection provider is not implemented yet for keyspace ${this.config.keyspace}.`
2367
+ );
2368
+ }
2369
+ };
2370
+ function createPostgresProvider(config) {
2371
+ return createPostgresIntrospectionProvider({
2372
+ connectionString: config.connectionString,
2373
+ database: config.database
2374
+ });
2375
+ }
2376
+ function resolveGeneratorProvider(providerConfig, experimentalFlags) {
2377
+ if (providerConfig.kind === "postgres" && providerConfig.mode === "direct") {
2378
+ return createPostgresProvider(providerConfig);
2379
+ }
2380
+ if (providerConfig.kind === "postgres" && providerConfig.mode === "gateway") {
2381
+ return new AthenaGatewayPostgresIntrospectionProvider(providerConfig);
2382
+ }
2383
+ if (providerConfig.kind === "scylla") {
2384
+ if (!experimentalFlags.scyllaProviderContracts) {
2385
+ throw new Error(
2386
+ "Scylla provider contracts are disabled. Set experimental.scyllaProviderContracts=true to enable placeholders."
2387
+ );
2388
+ }
2389
+ return new ScyllaIntrospectionProvider(providerConfig);
2390
+ }
2391
+ throw new Error(`Unsupported generator provider kind: ${providerConfig.kind ?? "unknown"}`);
2392
+ }
2393
+
2394
+ // src/generator/pipeline.ts
2395
+ function extractProviderSchemas(providerConfig) {
2396
+ if (!("schemas" in providerConfig) || !providerConfig.schemas || providerConfig.schemas.length === 0) {
2397
+ return void 0;
2398
+ }
2399
+ return providerConfig.schemas;
2400
+ }
2401
+ async function writeArtifacts(files, cwd) {
2402
+ const writtenFiles = [];
2403
+ for (const file of files) {
2404
+ const absolutePath = path.resolve(cwd, file.path);
2405
+ await promises.mkdir(path.dirname(absolutePath), { recursive: true });
2406
+ await promises.writeFile(absolutePath, file.content, "utf8");
2407
+ writtenFiles.push(file.path);
2408
+ }
2409
+ return writtenFiles;
2410
+ }
2411
+ async function runSchemaGenerator(options = {}) {
2412
+ const cwd = options.cwd ?? process.cwd();
2413
+ const configOptions = {
2414
+ cwd,
2415
+ configPath: options.configPath
2416
+ };
2417
+ const { configPath, config } = await loadGeneratorConfig(configOptions);
2418
+ const provider = options.provider ?? resolveGeneratorProvider(config.provider, config.experimental);
2419
+ const snapshot = await provider.inspect({
2420
+ schemas: extractProviderSchemas(config.provider)
2421
+ });
2422
+ const generated = generateArtifactsFromSnapshot(snapshot, config);
2423
+ const writtenFiles = options.dryRun ? [] : await writeArtifacts(generated.files, cwd);
2424
+ return {
2425
+ ...generated,
2426
+ configPath,
2427
+ writtenFiles
2428
+ };
2429
+ }
2430
+
2431
+ // src/cli/index.ts
2432
+ function usage() {
2433
+ return [
2434
+ "athena-js CLI",
2435
+ "",
2436
+ "Usage:",
2437
+ " athena-js generate [--config <path>] [--dry-run]",
2438
+ "",
2439
+ "Examples:",
2440
+ " athena-js generate",
2441
+ " athena-js generate --config ./athena.config.ts --dry-run"
2442
+ ].join("\n");
2443
+ }
2444
+ function parseCommand(argv) {
2445
+ if (argv.length === 0 || argv[0] === "help" || argv[0] === "--help" || argv[0] === "-h") {
2446
+ return { command: "help" };
2447
+ }
2448
+ const [command, ...rest] = argv;
2449
+ if (command !== "generate") {
2450
+ throw new Error(`Unknown command "${command}".`);
2451
+ }
2452
+ let configPath;
2453
+ let dryRun = false;
2454
+ for (let index = 0; index < rest.length; index += 1) {
2455
+ const token = rest[index];
2456
+ if (token === "--dry-run") {
2457
+ dryRun = true;
2458
+ continue;
2459
+ }
2460
+ if (token === "--config") {
2461
+ const nextValue = rest[index + 1];
2462
+ if (!nextValue) {
2463
+ throw new Error("Missing value for --config option.");
2464
+ }
2465
+ configPath = nextValue;
2466
+ index += 1;
2467
+ continue;
2468
+ }
2469
+ throw new Error(`Unknown option "${token}".`);
2470
+ }
2471
+ return {
2472
+ command: "generate",
2473
+ configPath,
2474
+ dryRun
2475
+ };
2476
+ }
2477
+ async function runCLI(argv) {
2478
+ const parsed = parseCommand(argv);
2479
+ if (parsed.command === "help") {
2480
+ console.log(usage());
2481
+ return;
2482
+ }
2483
+ const result = await runSchemaGenerator({
2484
+ configPath: parsed.configPath,
2485
+ dryRun: parsed.dryRun
2486
+ });
2487
+ if (parsed.dryRun) {
2488
+ console.log(`[dry-run] Generated ${result.files.length} files from ${result.configPath}`);
2489
+ for (const file of result.files) {
2490
+ console.log(` - ${file.path}`);
2491
+ }
2492
+ return;
2493
+ }
2494
+ console.log(`Generated ${result.writtenFiles.length} files from ${result.configPath}`);
2495
+ for (const filePath of result.writtenFiles) {
2496
+ console.log(` - ${filePath}`);
2497
+ }
2498
+ }
2499
+
2500
+ exports.runCLI = runCLI;
2501
+ //# sourceMappingURL=index.cjs.map
2502
+ //# sourceMappingURL=index.cjs.map