@aurabase/gen 0.7.4 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +104 -31
- package/dist/cli.d.cts +3 -1
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +105 -32
- package/dist/index.cjs +50 -12
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +50 -12
- package/package.json +4 -4
package/dist/cli.cjs
CHANGED
|
@@ -21,11 +21,13 @@ function mapColType(colType, ctx) {
|
|
|
21
21
|
case "uuid":
|
|
22
22
|
case "timestamp":
|
|
23
23
|
case "timestamptz":
|
|
24
|
+
case "tsvector":
|
|
24
25
|
case "date":
|
|
25
26
|
case "time":
|
|
26
27
|
case "bytea":
|
|
27
|
-
case "numeric":
|
|
28
28
|
return "string";
|
|
29
|
+
case "numeric":
|
|
30
|
+
return ctx.numericAsString ? "string" : "number";
|
|
29
31
|
case "int":
|
|
30
32
|
case "int2":
|
|
31
33
|
case "int4":
|
|
@@ -53,20 +55,29 @@ function mapColType(colType, ctx) {
|
|
|
53
55
|
}
|
|
54
56
|
function mapPgType(raw, ctx) {
|
|
55
57
|
if (raw.startsWith("_")) return `${mapPgType(raw.slice(1), ctx)}[]`;
|
|
58
|
+
if (raw.endsWith("[]")) return `${mapPgType(raw.slice(0, -2), ctx)}[]`;
|
|
56
59
|
switch (raw) {
|
|
57
60
|
case "int2":
|
|
58
61
|
case "int4":
|
|
59
62
|
case "int8":
|
|
60
63
|
case "float4":
|
|
61
64
|
case "float8":
|
|
65
|
+
case "smallint":
|
|
66
|
+
case "integer":
|
|
67
|
+
case "bigint":
|
|
68
|
+
case "real":
|
|
69
|
+
case "double precision":
|
|
62
70
|
return "number";
|
|
63
71
|
case "numeric":
|
|
64
|
-
return "string";
|
|
72
|
+
return ctx.numericAsString ? "string" : "number";
|
|
65
73
|
case "bool":
|
|
74
|
+
case "boolean":
|
|
66
75
|
return "boolean";
|
|
67
76
|
case "text":
|
|
68
77
|
case "varchar":
|
|
69
78
|
case "bpchar":
|
|
79
|
+
case "character varying":
|
|
80
|
+
case "character":
|
|
70
81
|
case "citext":
|
|
71
82
|
case "uuid":
|
|
72
83
|
case "date":
|
|
@@ -74,8 +85,15 @@ function mapPgType(raw, ctx) {
|
|
|
74
85
|
case "timetz":
|
|
75
86
|
case "timestamp":
|
|
76
87
|
case "timestamptz":
|
|
88
|
+
case "time without time zone":
|
|
89
|
+
case "time with time zone":
|
|
90
|
+
case "timestamp without time zone":
|
|
91
|
+
case "timestamp with time zone":
|
|
92
|
+
case "tsvector":
|
|
77
93
|
case "bytea":
|
|
78
94
|
return "string";
|
|
95
|
+
case "vector":
|
|
96
|
+
return "number[]";
|
|
79
97
|
case "json":
|
|
80
98
|
case "jsonb":
|
|
81
99
|
return "Json";
|
|
@@ -94,15 +112,14 @@ function tsType(col, ctx) {
|
|
|
94
112
|
function genRow(cols, ctx) {
|
|
95
113
|
return cols.map((c) => ` ${c.name}: ${tsType(c, ctx)}`).join("\n");
|
|
96
114
|
}
|
|
97
|
-
var AUTO_COLS = /* @__PURE__ */ new Set(["id", "created_at", "updated_at"]);
|
|
98
115
|
function isInsertOptional(col) {
|
|
99
|
-
return col.nullable || col.default_val != null ||
|
|
116
|
+
return col.nullable || col.default_val != null || col.is_identity === true;
|
|
100
117
|
}
|
|
101
118
|
function genInsert(cols, ctx) {
|
|
102
|
-
return cols.map((c) => ` ${c.name}${isInsertOptional(c) ? "?" : ""}: ${tsType(c, ctx)}`).join("\n");
|
|
119
|
+
return cols.filter((c) => c.is_generated !== true).map((c) => ` ${c.name}${isInsertOptional(c) ? "?" : ""}: ${tsType(c, ctx)}`).join("\n");
|
|
103
120
|
}
|
|
104
121
|
function genUpdate(cols, ctx) {
|
|
105
|
-
return cols.map((c) => ` ${c.name}?: ${tsType(c, ctx)}`).join("\n");
|
|
122
|
+
return cols.filter((c) => c.is_generated !== true).map((c) => ` ${c.name}?: ${tsType(c, ctx)}`).join("\n");
|
|
106
123
|
}
|
|
107
124
|
function genRelationships(t) {
|
|
108
125
|
const fks = t.columns.filter((c) => c.references);
|
|
@@ -111,7 +128,7 @@ function genRelationships(t) {
|
|
|
111
128
|
for (const c of fks) {
|
|
112
129
|
const ref = c.references;
|
|
113
130
|
const name = ref.constraint_name ?? `${t.name}_${c.name}_fkey`;
|
|
114
|
-
const key = ref.constraint_name ??
|
|
131
|
+
const key = ref.constraint_name ?? `__synthetic__${c.name}`;
|
|
115
132
|
let g = groups.get(key);
|
|
116
133
|
if (!g) {
|
|
117
134
|
g = { name, columns: [], refTable: ref.table, refColumns: [], cols: [] };
|
|
@@ -121,8 +138,19 @@ function genRelationships(t) {
|
|
|
121
138
|
g.refColumns.push(ref.column);
|
|
122
139
|
g.cols.push(c);
|
|
123
140
|
}
|
|
141
|
+
const sameColSet = (a, b) => {
|
|
142
|
+
if (!a || a.length !== b.length || a.length === 0) return false;
|
|
143
|
+
const setA = new Set(a);
|
|
144
|
+
return b.every((c) => setA.has(c));
|
|
145
|
+
};
|
|
124
146
|
const entries = [...groups.values()].map((g) => {
|
|
125
|
-
const isOneToOne = g.cols.
|
|
147
|
+
const isOneToOne = g.cols.length === 1 ? g.cols[0].unique || t.primary_key === g.cols[0].name || t.primary_key_columns?.length === 1 && t.primary_key_columns[0] === g.cols[0].name || Boolean(
|
|
148
|
+
t.unique_constraints?.some((uc) => uc.length === 1 && uc[0] === g.cols[0].name)
|
|
149
|
+
) || Boolean(
|
|
150
|
+
t.indexes?.some(
|
|
151
|
+
(idx) => idx.unique && idx.columns.length === 1 && idx.columns[0] === g.cols[0].name
|
|
152
|
+
)
|
|
153
|
+
) : sameColSet(t.primary_key_columns, g.columns) || Boolean(t.unique_constraints?.some((uc) => sameColSet(uc, g.columns))) || Boolean(t.indexes?.some((idx) => idx.unique && sameColSet(idx.columns, g.columns)));
|
|
126
154
|
const cols = g.columns.map((c) => `'${c}'`).join(", ");
|
|
127
155
|
const refCols = g.refColumns.map((c) => `'${c}'`).join(", ");
|
|
128
156
|
return ` {
|
|
@@ -138,12 +166,21 @@ ${entries.join(",\n")}
|
|
|
138
166
|
]`;
|
|
139
167
|
}
|
|
140
168
|
function genFunctionArgs(fn, ctx) {
|
|
141
|
-
const inputArgs = fn.args.filter((a) => a
|
|
169
|
+
const inputArgs = fn.args.filter((a) => !isReturnColumn(a));
|
|
142
170
|
if (inputArgs.length === 0) return "Record<string, never>";
|
|
143
171
|
const entries = inputArgs.map((a) => `${a.name}: ${mapPgType(a.data_type, ctx)}`).join("; ");
|
|
144
172
|
return `{ ${entries} }`;
|
|
145
173
|
}
|
|
174
|
+
function isReturnColumn(a) {
|
|
175
|
+
const m = a.mode.toUpperCase();
|
|
176
|
+
return m === "OUT" || m === "TABLE";
|
|
177
|
+
}
|
|
146
178
|
function mapReturnType(fn, ctx) {
|
|
179
|
+
const cols = fn.args.filter(isReturnColumn);
|
|
180
|
+
if (fn.returns === "record" && cols.length > 0) {
|
|
181
|
+
const row = `{ ${cols.map((c) => `${c.name}: ${mapPgType(c.data_type, ctx)}`).join("; ")} }`;
|
|
182
|
+
return fn.is_set_returning ? `${row}[]` : row;
|
|
183
|
+
}
|
|
147
184
|
const base = mapPgType(fn.returns, ctx);
|
|
148
185
|
if (!fn.is_set_returning) return base;
|
|
149
186
|
return base === "unknown" ? "Record<string, unknown>[]" : `${base}[]`;
|
|
@@ -208,8 +245,8 @@ ${entries.join("\n")}
|
|
|
208
245
|
}`;
|
|
209
246
|
}
|
|
210
247
|
var HEADER = `// ============================================================
|
|
211
|
-
//
|
|
212
|
-
//
|
|
248
|
+
// Auto-generated TypeScript types by Aurabase (@aurabase/gen)
|
|
249
|
+
// DO NOT EDIT \u2014 regenerate with \`npx @aurabase/gen\`
|
|
213
250
|
// ============================================================
|
|
214
251
|
`;
|
|
215
252
|
function generateDatabaseTypes(tables, options = {}) {
|
|
@@ -222,7 +259,8 @@ function generateDatabaseTypes(tables, options = {}) {
|
|
|
222
259
|
typeName,
|
|
223
260
|
schemaName,
|
|
224
261
|
enumNames: new Set(enums.map((e) => e.name)),
|
|
225
|
-
relationNames
|
|
262
|
+
relationNames,
|
|
263
|
+
numericAsString: options.numericAsString === true
|
|
226
264
|
};
|
|
227
265
|
const functionsBody = genFunctionsBody(options.functions ?? [], ctx);
|
|
228
266
|
const enumsBody = genEnumsBody(enums);
|
|
@@ -345,7 +383,10 @@ export type Enums<
|
|
|
345
383
|
}
|
|
346
384
|
|
|
347
385
|
// src/cli.ts
|
|
348
|
-
|
|
386
|
+
function resolvePackageVersion() {
|
|
387
|
+
return "0.7.5";
|
|
388
|
+
}
|
|
389
|
+
var VERSION = resolvePackageVersion();
|
|
349
390
|
function loadEnvFiles() {
|
|
350
391
|
const envCandidates = [".env.local", ".env.development.local", ".env.development", ".env"];
|
|
351
392
|
for (const file of envCandidates) {
|
|
@@ -421,6 +462,12 @@ function parseArgs(argv) {
|
|
|
421
462
|
case "--allow-empty":
|
|
422
463
|
a.allowEmpty = true;
|
|
423
464
|
break;
|
|
465
|
+
case "--check":
|
|
466
|
+
a.check = true;
|
|
467
|
+
break;
|
|
468
|
+
case "--numeric-as-string":
|
|
469
|
+
a.numericAsString = true;
|
|
470
|
+
break;
|
|
424
471
|
case "--help":
|
|
425
472
|
case "-h":
|
|
426
473
|
a.help = true;
|
|
@@ -438,27 +485,29 @@ aurabase-gen (v${VERSION}) \u2014 TypeScript type generator for Aurabase
|
|
|
438
485
|
|
|
439
486
|
USAGE:
|
|
440
487
|
npx @aurabase/gen [options]
|
|
441
|
-
aurabase-gen --url <projectUrl> --key <
|
|
488
|
+
aurabase-gen --url <projectUrl> --key <serviceRoleKey> [--out <file>]
|
|
442
489
|
|
|
443
490
|
OPTIONS:
|
|
444
|
-
--url <url>
|
|
445
|
-
|
|
446
|
-
--key <key>
|
|
447
|
-
|
|
448
|
-
-o, --out <file>
|
|
449
|
-
|
|
450
|
-
-s, --schema <name>
|
|
451
|
-
-t, --type-name <t>
|
|
452
|
-
--allow-empty
|
|
453
|
-
-
|
|
454
|
-
-
|
|
491
|
+
--url <url> Aurabase project URL (e.g. https://my-project.aurabase.cloud)
|
|
492
|
+
[Auto-detected: NEXT_PUBLIC_AURABASE_URL]
|
|
493
|
+
--key <key> Project service_role key (aura_sk_...) for schema introspection
|
|
494
|
+
[Auto-detected: AURABASE_SERVICE_ROLE_KEY]
|
|
495
|
+
-o, --out <file> Output file (e.g. src/types/database.types.ts)
|
|
496
|
+
If omitted, the result is written to stdout.
|
|
497
|
+
-s, --schema <name> Database schema name (default: 'public')
|
|
498
|
+
-t, --type-name <t> Name of the main generated interface (default: 'Database')
|
|
499
|
+
--allow-empty Generate the types even if some tables have no declared columns
|
|
500
|
+
--numeric-as-string Map Postgres numeric columns/args to string instead of number
|
|
501
|
+
--check Exit non-zero if --out differs from regenerated types (CI guard)
|
|
502
|
+
-h, --help Show this help
|
|
503
|
+
-v, --version Show the version
|
|
455
504
|
|
|
456
505
|
EXAMPLES:
|
|
457
506
|
# Generation reading the environment variables automatically (.env / .env.local):
|
|
458
507
|
npx @aurabase/gen --out src/types/database.types.ts
|
|
459
508
|
|
|
460
509
|
# Explicit parameters:
|
|
461
|
-
npx @aurabase/gen --url https://xyz.aurabase.cloud --key
|
|
510
|
+
npx @aurabase/gen --url https://xyz.aurabase.cloud --key aura_sk_... --out db.types.ts
|
|
462
511
|
`;
|
|
463
512
|
async function main() {
|
|
464
513
|
loadEnvFiles();
|
|
@@ -472,17 +521,26 @@ async function main() {
|
|
|
472
521
|
process.exit(0);
|
|
473
522
|
}
|
|
474
523
|
const url = args.url ?? process.env.NEXT_PUBLIC_AURABASE_URL;
|
|
475
|
-
const key = args.key ?? process.env.AURABASE_SERVICE_ROLE_KEY
|
|
524
|
+
const key = args.key ?? process.env.AURABASE_SERVICE_ROLE_KEY;
|
|
476
525
|
if (!url || !key) {
|
|
477
|
-
console.error("[aurabase-gen] Error: the project URL and/or
|
|
526
|
+
console.error("[aurabase-gen] Error: the project URL and/or service_role key is missing.\n");
|
|
478
527
|
console.error(
|
|
479
|
-
"Provide them through
|
|
528
|
+
"Provide them through CLI arguments (--url and --key) or environment variables (.env / .env.local):"
|
|
480
529
|
);
|
|
481
530
|
console.error(" NEXT_PUBLIC_AURABASE_URL=https://<slug>.aurabase.cloud");
|
|
482
|
-
console.error("
|
|
531
|
+
console.error(" AURABASE_SERVICE_ROLE_KEY=aura_sk_...\n");
|
|
483
532
|
console.error("Run `npx @aurabase/gen --help` to see all the available options.");
|
|
484
533
|
process.exit(1);
|
|
485
534
|
}
|
|
535
|
+
if (key.startsWith("aura_anon_") || key.startsWith("aura_auth_")) {
|
|
536
|
+
console.error(
|
|
537
|
+
"[aurabase-gen] Error: schema introspection requires a service_role key (aura_sk_...), not an anon/auth key."
|
|
538
|
+
);
|
|
539
|
+
console.error(
|
|
540
|
+
"Set AURABASE_SERVICE_ROLE_KEY or pass --key aura_sk_... (never commit service_role keys)."
|
|
541
|
+
);
|
|
542
|
+
process.exit(1);
|
|
543
|
+
}
|
|
486
544
|
const schemaName = args.schema ?? process.env.AURABASE_SCHEMA ?? "public";
|
|
487
545
|
const outPath = args.out ?? process.env.AURABASE_TYPES_OUT;
|
|
488
546
|
const client = aurabaseJs.createClient(url, key);
|
|
@@ -507,8 +565,23 @@ async function main() {
|
|
|
507
565
|
typeName: args.typeName,
|
|
508
566
|
functions,
|
|
509
567
|
enums,
|
|
510
|
-
views
|
|
568
|
+
views,
|
|
569
|
+
numericAsString: args.numericAsString
|
|
511
570
|
});
|
|
571
|
+
if (args.check) {
|
|
572
|
+
if (!outPath) {
|
|
573
|
+
console.error("[aurabase-gen] Error: --check requires --out <file>.");
|
|
574
|
+
process.exit(1);
|
|
575
|
+
}
|
|
576
|
+
if (!fs.existsSync(outPath) || fs.readFileSync(outPath, "utf-8") !== output) {
|
|
577
|
+
console.error(
|
|
578
|
+
`[aurabase-gen] Types in ${outPath} are out of date. Re-run aurabase-gen without --check.`
|
|
579
|
+
);
|
|
580
|
+
process.exit(1);
|
|
581
|
+
}
|
|
582
|
+
console.error(`[aurabase-gen] \u2713 ${outPath} is up to date.`);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
512
585
|
if (outPath) {
|
|
513
586
|
fs.writeFileSync(outPath, output);
|
|
514
587
|
console.error(
|
package/dist/cli.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
declare const VERSION
|
|
2
|
+
declare const VERSION: string;
|
|
3
3
|
/**
|
|
4
4
|
* Charge les fichiers .env locaux s'ils existent dans le répertoire courant.
|
|
5
5
|
* Tente .env.local, .env.development.local, .env.development, .env par ordre de priorité.
|
|
@@ -26,6 +26,8 @@ interface Args {
|
|
|
26
26
|
schema?: string;
|
|
27
27
|
typeName?: string;
|
|
28
28
|
allowEmpty?: boolean;
|
|
29
|
+
check?: boolean;
|
|
30
|
+
numericAsString?: boolean;
|
|
29
31
|
help?: boolean;
|
|
30
32
|
version?: boolean;
|
|
31
33
|
}
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
declare const VERSION
|
|
2
|
+
declare const VERSION: string;
|
|
3
3
|
/**
|
|
4
4
|
* Charge les fichiers .env locaux s'ils existent dans le répertoire courant.
|
|
5
5
|
* Tente .env.local, .env.development.local, .env.development, .env par ordre de priorité.
|
|
@@ -26,6 +26,8 @@ interface Args {
|
|
|
26
26
|
schema?: string;
|
|
27
27
|
typeName?: string;
|
|
28
28
|
allowEmpty?: boolean;
|
|
29
|
+
check?: boolean;
|
|
30
|
+
numericAsString?: boolean;
|
|
29
31
|
help?: boolean;
|
|
30
32
|
version?: boolean;
|
|
31
33
|
}
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
3
3
|
import { resolve } from 'path';
|
|
4
4
|
import { createClient } from '@aurabase/aurabase-js';
|
|
5
5
|
|
|
@@ -19,11 +19,13 @@ function mapColType(colType, ctx) {
|
|
|
19
19
|
case "uuid":
|
|
20
20
|
case "timestamp":
|
|
21
21
|
case "timestamptz":
|
|
22
|
+
case "tsvector":
|
|
22
23
|
case "date":
|
|
23
24
|
case "time":
|
|
24
25
|
case "bytea":
|
|
25
|
-
case "numeric":
|
|
26
26
|
return "string";
|
|
27
|
+
case "numeric":
|
|
28
|
+
return ctx.numericAsString ? "string" : "number";
|
|
27
29
|
case "int":
|
|
28
30
|
case "int2":
|
|
29
31
|
case "int4":
|
|
@@ -51,20 +53,29 @@ function mapColType(colType, ctx) {
|
|
|
51
53
|
}
|
|
52
54
|
function mapPgType(raw, ctx) {
|
|
53
55
|
if (raw.startsWith("_")) return `${mapPgType(raw.slice(1), ctx)}[]`;
|
|
56
|
+
if (raw.endsWith("[]")) return `${mapPgType(raw.slice(0, -2), ctx)}[]`;
|
|
54
57
|
switch (raw) {
|
|
55
58
|
case "int2":
|
|
56
59
|
case "int4":
|
|
57
60
|
case "int8":
|
|
58
61
|
case "float4":
|
|
59
62
|
case "float8":
|
|
63
|
+
case "smallint":
|
|
64
|
+
case "integer":
|
|
65
|
+
case "bigint":
|
|
66
|
+
case "real":
|
|
67
|
+
case "double precision":
|
|
60
68
|
return "number";
|
|
61
69
|
case "numeric":
|
|
62
|
-
return "string";
|
|
70
|
+
return ctx.numericAsString ? "string" : "number";
|
|
63
71
|
case "bool":
|
|
72
|
+
case "boolean":
|
|
64
73
|
return "boolean";
|
|
65
74
|
case "text":
|
|
66
75
|
case "varchar":
|
|
67
76
|
case "bpchar":
|
|
77
|
+
case "character varying":
|
|
78
|
+
case "character":
|
|
68
79
|
case "citext":
|
|
69
80
|
case "uuid":
|
|
70
81
|
case "date":
|
|
@@ -72,8 +83,15 @@ function mapPgType(raw, ctx) {
|
|
|
72
83
|
case "timetz":
|
|
73
84
|
case "timestamp":
|
|
74
85
|
case "timestamptz":
|
|
86
|
+
case "time without time zone":
|
|
87
|
+
case "time with time zone":
|
|
88
|
+
case "timestamp without time zone":
|
|
89
|
+
case "timestamp with time zone":
|
|
90
|
+
case "tsvector":
|
|
75
91
|
case "bytea":
|
|
76
92
|
return "string";
|
|
93
|
+
case "vector":
|
|
94
|
+
return "number[]";
|
|
77
95
|
case "json":
|
|
78
96
|
case "jsonb":
|
|
79
97
|
return "Json";
|
|
@@ -92,15 +110,14 @@ function tsType(col, ctx) {
|
|
|
92
110
|
function genRow(cols, ctx) {
|
|
93
111
|
return cols.map((c) => ` ${c.name}: ${tsType(c, ctx)}`).join("\n");
|
|
94
112
|
}
|
|
95
|
-
var AUTO_COLS = /* @__PURE__ */ new Set(["id", "created_at", "updated_at"]);
|
|
96
113
|
function isInsertOptional(col) {
|
|
97
|
-
return col.nullable || col.default_val != null ||
|
|
114
|
+
return col.nullable || col.default_val != null || col.is_identity === true;
|
|
98
115
|
}
|
|
99
116
|
function genInsert(cols, ctx) {
|
|
100
|
-
return cols.map((c) => ` ${c.name}${isInsertOptional(c) ? "?" : ""}: ${tsType(c, ctx)}`).join("\n");
|
|
117
|
+
return cols.filter((c) => c.is_generated !== true).map((c) => ` ${c.name}${isInsertOptional(c) ? "?" : ""}: ${tsType(c, ctx)}`).join("\n");
|
|
101
118
|
}
|
|
102
119
|
function genUpdate(cols, ctx) {
|
|
103
|
-
return cols.map((c) => ` ${c.name}?: ${tsType(c, ctx)}`).join("\n");
|
|
120
|
+
return cols.filter((c) => c.is_generated !== true).map((c) => ` ${c.name}?: ${tsType(c, ctx)}`).join("\n");
|
|
104
121
|
}
|
|
105
122
|
function genRelationships(t) {
|
|
106
123
|
const fks = t.columns.filter((c) => c.references);
|
|
@@ -109,7 +126,7 @@ function genRelationships(t) {
|
|
|
109
126
|
for (const c of fks) {
|
|
110
127
|
const ref = c.references;
|
|
111
128
|
const name = ref.constraint_name ?? `${t.name}_${c.name}_fkey`;
|
|
112
|
-
const key = ref.constraint_name ??
|
|
129
|
+
const key = ref.constraint_name ?? `__synthetic__${c.name}`;
|
|
113
130
|
let g = groups.get(key);
|
|
114
131
|
if (!g) {
|
|
115
132
|
g = { name, columns: [], refTable: ref.table, refColumns: [], cols: [] };
|
|
@@ -119,8 +136,19 @@ function genRelationships(t) {
|
|
|
119
136
|
g.refColumns.push(ref.column);
|
|
120
137
|
g.cols.push(c);
|
|
121
138
|
}
|
|
139
|
+
const sameColSet = (a, b) => {
|
|
140
|
+
if (!a || a.length !== b.length || a.length === 0) return false;
|
|
141
|
+
const setA = new Set(a);
|
|
142
|
+
return b.every((c) => setA.has(c));
|
|
143
|
+
};
|
|
122
144
|
const entries = [...groups.values()].map((g) => {
|
|
123
|
-
const isOneToOne = g.cols.
|
|
145
|
+
const isOneToOne = g.cols.length === 1 ? g.cols[0].unique || t.primary_key === g.cols[0].name || t.primary_key_columns?.length === 1 && t.primary_key_columns[0] === g.cols[0].name || Boolean(
|
|
146
|
+
t.unique_constraints?.some((uc) => uc.length === 1 && uc[0] === g.cols[0].name)
|
|
147
|
+
) || Boolean(
|
|
148
|
+
t.indexes?.some(
|
|
149
|
+
(idx) => idx.unique && idx.columns.length === 1 && idx.columns[0] === g.cols[0].name
|
|
150
|
+
)
|
|
151
|
+
) : sameColSet(t.primary_key_columns, g.columns) || Boolean(t.unique_constraints?.some((uc) => sameColSet(uc, g.columns))) || Boolean(t.indexes?.some((idx) => idx.unique && sameColSet(idx.columns, g.columns)));
|
|
124
152
|
const cols = g.columns.map((c) => `'${c}'`).join(", ");
|
|
125
153
|
const refCols = g.refColumns.map((c) => `'${c}'`).join(", ");
|
|
126
154
|
return ` {
|
|
@@ -136,12 +164,21 @@ ${entries.join(",\n")}
|
|
|
136
164
|
]`;
|
|
137
165
|
}
|
|
138
166
|
function genFunctionArgs(fn, ctx) {
|
|
139
|
-
const inputArgs = fn.args.filter((a) => a
|
|
167
|
+
const inputArgs = fn.args.filter((a) => !isReturnColumn(a));
|
|
140
168
|
if (inputArgs.length === 0) return "Record<string, never>";
|
|
141
169
|
const entries = inputArgs.map((a) => `${a.name}: ${mapPgType(a.data_type, ctx)}`).join("; ");
|
|
142
170
|
return `{ ${entries} }`;
|
|
143
171
|
}
|
|
172
|
+
function isReturnColumn(a) {
|
|
173
|
+
const m = a.mode.toUpperCase();
|
|
174
|
+
return m === "OUT" || m === "TABLE";
|
|
175
|
+
}
|
|
144
176
|
function mapReturnType(fn, ctx) {
|
|
177
|
+
const cols = fn.args.filter(isReturnColumn);
|
|
178
|
+
if (fn.returns === "record" && cols.length > 0) {
|
|
179
|
+
const row = `{ ${cols.map((c) => `${c.name}: ${mapPgType(c.data_type, ctx)}`).join("; ")} }`;
|
|
180
|
+
return fn.is_set_returning ? `${row}[]` : row;
|
|
181
|
+
}
|
|
145
182
|
const base = mapPgType(fn.returns, ctx);
|
|
146
183
|
if (!fn.is_set_returning) return base;
|
|
147
184
|
return base === "unknown" ? "Record<string, unknown>[]" : `${base}[]`;
|
|
@@ -206,8 +243,8 @@ ${entries.join("\n")}
|
|
|
206
243
|
}`;
|
|
207
244
|
}
|
|
208
245
|
var HEADER = `// ============================================================
|
|
209
|
-
//
|
|
210
|
-
//
|
|
246
|
+
// Auto-generated TypeScript types by Aurabase (@aurabase/gen)
|
|
247
|
+
// DO NOT EDIT \u2014 regenerate with \`npx @aurabase/gen\`
|
|
211
248
|
// ============================================================
|
|
212
249
|
`;
|
|
213
250
|
function generateDatabaseTypes(tables, options = {}) {
|
|
@@ -220,7 +257,8 @@ function generateDatabaseTypes(tables, options = {}) {
|
|
|
220
257
|
typeName,
|
|
221
258
|
schemaName,
|
|
222
259
|
enumNames: new Set(enums.map((e) => e.name)),
|
|
223
|
-
relationNames
|
|
260
|
+
relationNames,
|
|
261
|
+
numericAsString: options.numericAsString === true
|
|
224
262
|
};
|
|
225
263
|
const functionsBody = genFunctionsBody(options.functions ?? [], ctx);
|
|
226
264
|
const enumsBody = genEnumsBody(enums);
|
|
@@ -343,7 +381,10 @@ export type Enums<
|
|
|
343
381
|
}
|
|
344
382
|
|
|
345
383
|
// src/cli.ts
|
|
346
|
-
|
|
384
|
+
function resolvePackageVersion() {
|
|
385
|
+
return "0.7.5";
|
|
386
|
+
}
|
|
387
|
+
var VERSION = resolvePackageVersion();
|
|
347
388
|
function loadEnvFiles() {
|
|
348
389
|
const envCandidates = [".env.local", ".env.development.local", ".env.development", ".env"];
|
|
349
390
|
for (const file of envCandidates) {
|
|
@@ -419,6 +460,12 @@ function parseArgs(argv) {
|
|
|
419
460
|
case "--allow-empty":
|
|
420
461
|
a.allowEmpty = true;
|
|
421
462
|
break;
|
|
463
|
+
case "--check":
|
|
464
|
+
a.check = true;
|
|
465
|
+
break;
|
|
466
|
+
case "--numeric-as-string":
|
|
467
|
+
a.numericAsString = true;
|
|
468
|
+
break;
|
|
422
469
|
case "--help":
|
|
423
470
|
case "-h":
|
|
424
471
|
a.help = true;
|
|
@@ -436,27 +483,29 @@ aurabase-gen (v${VERSION}) \u2014 TypeScript type generator for Aurabase
|
|
|
436
483
|
|
|
437
484
|
USAGE:
|
|
438
485
|
npx @aurabase/gen [options]
|
|
439
|
-
aurabase-gen --url <projectUrl> --key <
|
|
486
|
+
aurabase-gen --url <projectUrl> --key <serviceRoleKey> [--out <file>]
|
|
440
487
|
|
|
441
488
|
OPTIONS:
|
|
442
|
-
--url <url>
|
|
443
|
-
|
|
444
|
-
--key <key>
|
|
445
|
-
|
|
446
|
-
-o, --out <file>
|
|
447
|
-
|
|
448
|
-
-s, --schema <name>
|
|
449
|
-
-t, --type-name <t>
|
|
450
|
-
--allow-empty
|
|
451
|
-
-
|
|
452
|
-
-
|
|
489
|
+
--url <url> Aurabase project URL (e.g. https://my-project.aurabase.cloud)
|
|
490
|
+
[Auto-detected: NEXT_PUBLIC_AURABASE_URL]
|
|
491
|
+
--key <key> Project service_role key (aura_sk_...) for schema introspection
|
|
492
|
+
[Auto-detected: AURABASE_SERVICE_ROLE_KEY]
|
|
493
|
+
-o, --out <file> Output file (e.g. src/types/database.types.ts)
|
|
494
|
+
If omitted, the result is written to stdout.
|
|
495
|
+
-s, --schema <name> Database schema name (default: 'public')
|
|
496
|
+
-t, --type-name <t> Name of the main generated interface (default: 'Database')
|
|
497
|
+
--allow-empty Generate the types even if some tables have no declared columns
|
|
498
|
+
--numeric-as-string Map Postgres numeric columns/args to string instead of number
|
|
499
|
+
--check Exit non-zero if --out differs from regenerated types (CI guard)
|
|
500
|
+
-h, --help Show this help
|
|
501
|
+
-v, --version Show the version
|
|
453
502
|
|
|
454
503
|
EXAMPLES:
|
|
455
504
|
# Generation reading the environment variables automatically (.env / .env.local):
|
|
456
505
|
npx @aurabase/gen --out src/types/database.types.ts
|
|
457
506
|
|
|
458
507
|
# Explicit parameters:
|
|
459
|
-
npx @aurabase/gen --url https://xyz.aurabase.cloud --key
|
|
508
|
+
npx @aurabase/gen --url https://xyz.aurabase.cloud --key aura_sk_... --out db.types.ts
|
|
460
509
|
`;
|
|
461
510
|
async function main() {
|
|
462
511
|
loadEnvFiles();
|
|
@@ -470,17 +519,26 @@ async function main() {
|
|
|
470
519
|
process.exit(0);
|
|
471
520
|
}
|
|
472
521
|
const url = args.url ?? process.env.NEXT_PUBLIC_AURABASE_URL;
|
|
473
|
-
const key = args.key ?? process.env.AURABASE_SERVICE_ROLE_KEY
|
|
522
|
+
const key = args.key ?? process.env.AURABASE_SERVICE_ROLE_KEY;
|
|
474
523
|
if (!url || !key) {
|
|
475
|
-
console.error("[aurabase-gen] Error: the project URL and/or
|
|
524
|
+
console.error("[aurabase-gen] Error: the project URL and/or service_role key is missing.\n");
|
|
476
525
|
console.error(
|
|
477
|
-
"Provide them through
|
|
526
|
+
"Provide them through CLI arguments (--url and --key) or environment variables (.env / .env.local):"
|
|
478
527
|
);
|
|
479
528
|
console.error(" NEXT_PUBLIC_AURABASE_URL=https://<slug>.aurabase.cloud");
|
|
480
|
-
console.error("
|
|
529
|
+
console.error(" AURABASE_SERVICE_ROLE_KEY=aura_sk_...\n");
|
|
481
530
|
console.error("Run `npx @aurabase/gen --help` to see all the available options.");
|
|
482
531
|
process.exit(1);
|
|
483
532
|
}
|
|
533
|
+
if (key.startsWith("aura_anon_") || key.startsWith("aura_auth_")) {
|
|
534
|
+
console.error(
|
|
535
|
+
"[aurabase-gen] Error: schema introspection requires a service_role key (aura_sk_...), not an anon/auth key."
|
|
536
|
+
);
|
|
537
|
+
console.error(
|
|
538
|
+
"Set AURABASE_SERVICE_ROLE_KEY or pass --key aura_sk_... (never commit service_role keys)."
|
|
539
|
+
);
|
|
540
|
+
process.exit(1);
|
|
541
|
+
}
|
|
484
542
|
const schemaName = args.schema ?? process.env.AURABASE_SCHEMA ?? "public";
|
|
485
543
|
const outPath = args.out ?? process.env.AURABASE_TYPES_OUT;
|
|
486
544
|
const client = createClient(url, key);
|
|
@@ -505,8 +563,23 @@ async function main() {
|
|
|
505
563
|
typeName: args.typeName,
|
|
506
564
|
functions,
|
|
507
565
|
enums,
|
|
508
|
-
views
|
|
566
|
+
views,
|
|
567
|
+
numericAsString: args.numericAsString
|
|
509
568
|
});
|
|
569
|
+
if (args.check) {
|
|
570
|
+
if (!outPath) {
|
|
571
|
+
console.error("[aurabase-gen] Error: --check requires --out <file>.");
|
|
572
|
+
process.exit(1);
|
|
573
|
+
}
|
|
574
|
+
if (!existsSync(outPath) || readFileSync(outPath, "utf-8") !== output) {
|
|
575
|
+
console.error(
|
|
576
|
+
`[aurabase-gen] Types in ${outPath} are out of date. Re-run aurabase-gen without --check.`
|
|
577
|
+
);
|
|
578
|
+
process.exit(1);
|
|
579
|
+
}
|
|
580
|
+
console.error(`[aurabase-gen] \u2713 ${outPath} is up to date.`);
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
510
583
|
if (outPath) {
|
|
511
584
|
writeFileSync(outPath, output);
|
|
512
585
|
console.error(
|
package/dist/index.cjs
CHANGED
|
@@ -16,11 +16,13 @@ function mapColType(colType, ctx) {
|
|
|
16
16
|
case "uuid":
|
|
17
17
|
case "timestamp":
|
|
18
18
|
case "timestamptz":
|
|
19
|
+
case "tsvector":
|
|
19
20
|
case "date":
|
|
20
21
|
case "time":
|
|
21
22
|
case "bytea":
|
|
22
|
-
case "numeric":
|
|
23
23
|
return "string";
|
|
24
|
+
case "numeric":
|
|
25
|
+
return ctx.numericAsString ? "string" : "number";
|
|
24
26
|
case "int":
|
|
25
27
|
case "int2":
|
|
26
28
|
case "int4":
|
|
@@ -48,20 +50,29 @@ function mapColType(colType, ctx) {
|
|
|
48
50
|
}
|
|
49
51
|
function mapPgType(raw, ctx) {
|
|
50
52
|
if (raw.startsWith("_")) return `${mapPgType(raw.slice(1), ctx)}[]`;
|
|
53
|
+
if (raw.endsWith("[]")) return `${mapPgType(raw.slice(0, -2), ctx)}[]`;
|
|
51
54
|
switch (raw) {
|
|
52
55
|
case "int2":
|
|
53
56
|
case "int4":
|
|
54
57
|
case "int8":
|
|
55
58
|
case "float4":
|
|
56
59
|
case "float8":
|
|
60
|
+
case "smallint":
|
|
61
|
+
case "integer":
|
|
62
|
+
case "bigint":
|
|
63
|
+
case "real":
|
|
64
|
+
case "double precision":
|
|
57
65
|
return "number";
|
|
58
66
|
case "numeric":
|
|
59
|
-
return "string";
|
|
67
|
+
return ctx.numericAsString ? "string" : "number";
|
|
60
68
|
case "bool":
|
|
69
|
+
case "boolean":
|
|
61
70
|
return "boolean";
|
|
62
71
|
case "text":
|
|
63
72
|
case "varchar":
|
|
64
73
|
case "bpchar":
|
|
74
|
+
case "character varying":
|
|
75
|
+
case "character":
|
|
65
76
|
case "citext":
|
|
66
77
|
case "uuid":
|
|
67
78
|
case "date":
|
|
@@ -69,8 +80,15 @@ function mapPgType(raw, ctx) {
|
|
|
69
80
|
case "timetz":
|
|
70
81
|
case "timestamp":
|
|
71
82
|
case "timestamptz":
|
|
83
|
+
case "time without time zone":
|
|
84
|
+
case "time with time zone":
|
|
85
|
+
case "timestamp without time zone":
|
|
86
|
+
case "timestamp with time zone":
|
|
87
|
+
case "tsvector":
|
|
72
88
|
case "bytea":
|
|
73
89
|
return "string";
|
|
90
|
+
case "vector":
|
|
91
|
+
return "number[]";
|
|
74
92
|
case "json":
|
|
75
93
|
case "jsonb":
|
|
76
94
|
return "Json";
|
|
@@ -89,15 +107,14 @@ function tsType(col, ctx) {
|
|
|
89
107
|
function genRow(cols, ctx) {
|
|
90
108
|
return cols.map((c) => ` ${c.name}: ${tsType(c, ctx)}`).join("\n");
|
|
91
109
|
}
|
|
92
|
-
var AUTO_COLS = /* @__PURE__ */ new Set(["id", "created_at", "updated_at"]);
|
|
93
110
|
function isInsertOptional(col) {
|
|
94
|
-
return col.nullable || col.default_val != null ||
|
|
111
|
+
return col.nullable || col.default_val != null || col.is_identity === true;
|
|
95
112
|
}
|
|
96
113
|
function genInsert(cols, ctx) {
|
|
97
|
-
return cols.map((c) => ` ${c.name}${isInsertOptional(c) ? "?" : ""}: ${tsType(c, ctx)}`).join("\n");
|
|
114
|
+
return cols.filter((c) => c.is_generated !== true).map((c) => ` ${c.name}${isInsertOptional(c) ? "?" : ""}: ${tsType(c, ctx)}`).join("\n");
|
|
98
115
|
}
|
|
99
116
|
function genUpdate(cols, ctx) {
|
|
100
|
-
return cols.map((c) => ` ${c.name}?: ${tsType(c, ctx)}`).join("\n");
|
|
117
|
+
return cols.filter((c) => c.is_generated !== true).map((c) => ` ${c.name}?: ${tsType(c, ctx)}`).join("\n");
|
|
101
118
|
}
|
|
102
119
|
function genRelationships(t) {
|
|
103
120
|
const fks = t.columns.filter((c) => c.references);
|
|
@@ -106,7 +123,7 @@ function genRelationships(t) {
|
|
|
106
123
|
for (const c of fks) {
|
|
107
124
|
const ref = c.references;
|
|
108
125
|
const name = ref.constraint_name ?? `${t.name}_${c.name}_fkey`;
|
|
109
|
-
const key = ref.constraint_name ??
|
|
126
|
+
const key = ref.constraint_name ?? `__synthetic__${c.name}`;
|
|
110
127
|
let g = groups.get(key);
|
|
111
128
|
if (!g) {
|
|
112
129
|
g = { name, columns: [], refTable: ref.table, refColumns: [], cols: [] };
|
|
@@ -116,8 +133,19 @@ function genRelationships(t) {
|
|
|
116
133
|
g.refColumns.push(ref.column);
|
|
117
134
|
g.cols.push(c);
|
|
118
135
|
}
|
|
136
|
+
const sameColSet = (a, b) => {
|
|
137
|
+
if (!a || a.length !== b.length || a.length === 0) return false;
|
|
138
|
+
const setA = new Set(a);
|
|
139
|
+
return b.every((c) => setA.has(c));
|
|
140
|
+
};
|
|
119
141
|
const entries = [...groups.values()].map((g) => {
|
|
120
|
-
const isOneToOne = g.cols.
|
|
142
|
+
const isOneToOne = g.cols.length === 1 ? g.cols[0].unique || t.primary_key === g.cols[0].name || t.primary_key_columns?.length === 1 && t.primary_key_columns[0] === g.cols[0].name || Boolean(
|
|
143
|
+
t.unique_constraints?.some((uc) => uc.length === 1 && uc[0] === g.cols[0].name)
|
|
144
|
+
) || Boolean(
|
|
145
|
+
t.indexes?.some(
|
|
146
|
+
(idx) => idx.unique && idx.columns.length === 1 && idx.columns[0] === g.cols[0].name
|
|
147
|
+
)
|
|
148
|
+
) : sameColSet(t.primary_key_columns, g.columns) || Boolean(t.unique_constraints?.some((uc) => sameColSet(uc, g.columns))) || Boolean(t.indexes?.some((idx) => idx.unique && sameColSet(idx.columns, g.columns)));
|
|
121
149
|
const cols = g.columns.map((c) => `'${c}'`).join(", ");
|
|
122
150
|
const refCols = g.refColumns.map((c) => `'${c}'`).join(", ");
|
|
123
151
|
return ` {
|
|
@@ -133,12 +161,21 @@ ${entries.join(",\n")}
|
|
|
133
161
|
]`;
|
|
134
162
|
}
|
|
135
163
|
function genFunctionArgs(fn, ctx) {
|
|
136
|
-
const inputArgs = fn.args.filter((a) => a
|
|
164
|
+
const inputArgs = fn.args.filter((a) => !isReturnColumn(a));
|
|
137
165
|
if (inputArgs.length === 0) return "Record<string, never>";
|
|
138
166
|
const entries = inputArgs.map((a) => `${a.name}: ${mapPgType(a.data_type, ctx)}`).join("; ");
|
|
139
167
|
return `{ ${entries} }`;
|
|
140
168
|
}
|
|
169
|
+
function isReturnColumn(a) {
|
|
170
|
+
const m = a.mode.toUpperCase();
|
|
171
|
+
return m === "OUT" || m === "TABLE";
|
|
172
|
+
}
|
|
141
173
|
function mapReturnType(fn, ctx) {
|
|
174
|
+
const cols = fn.args.filter(isReturnColumn);
|
|
175
|
+
if (fn.returns === "record" && cols.length > 0) {
|
|
176
|
+
const row = `{ ${cols.map((c) => `${c.name}: ${mapPgType(c.data_type, ctx)}`).join("; ")} }`;
|
|
177
|
+
return fn.is_set_returning ? `${row}[]` : row;
|
|
178
|
+
}
|
|
142
179
|
const base = mapPgType(fn.returns, ctx);
|
|
143
180
|
if (!fn.is_set_returning) return base;
|
|
144
181
|
return base === "unknown" ? "Record<string, unknown>[]" : `${base}[]`;
|
|
@@ -203,8 +240,8 @@ ${entries.join("\n")}
|
|
|
203
240
|
}`;
|
|
204
241
|
}
|
|
205
242
|
var HEADER = `// ============================================================
|
|
206
|
-
//
|
|
207
|
-
//
|
|
243
|
+
// Auto-generated TypeScript types by Aurabase (@aurabase/gen)
|
|
244
|
+
// DO NOT EDIT \u2014 regenerate with \`npx @aurabase/gen\`
|
|
208
245
|
// ============================================================
|
|
209
246
|
`;
|
|
210
247
|
function generateDatabaseTypes(tables, options = {}) {
|
|
@@ -217,7 +254,8 @@ function generateDatabaseTypes(tables, options = {}) {
|
|
|
217
254
|
typeName,
|
|
218
255
|
schemaName,
|
|
219
256
|
enumNames: new Set(enums.map((e) => e.name)),
|
|
220
|
-
relationNames
|
|
257
|
+
relationNames,
|
|
258
|
+
numericAsString: options.numericAsString === true
|
|
221
259
|
};
|
|
222
260
|
const functionsBody = genFunctionsBody(options.functions ?? [], ctx);
|
|
223
261
|
const enumsBody = genEnumsBody(enums);
|
package/dist/index.d.cts
CHANGED
|
@@ -6,6 +6,8 @@ interface GenerateOptions {
|
|
|
6
6
|
functions?: SchemaFunction[];
|
|
7
7
|
enums?: SchemaEnum[];
|
|
8
8
|
views?: SchemaTable[];
|
|
9
|
+
/** When true, maps Postgres `numeric` columns/args to `string` instead of `number`. */
|
|
10
|
+
numericAsString?: boolean;
|
|
9
11
|
}
|
|
10
12
|
declare function generateDatabaseTypes(tables: SchemaTable[], options?: GenerateOptions): string;
|
|
11
13
|
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ interface GenerateOptions {
|
|
|
6
6
|
functions?: SchemaFunction[];
|
|
7
7
|
enums?: SchemaEnum[];
|
|
8
8
|
views?: SchemaTable[];
|
|
9
|
+
/** When true, maps Postgres `numeric` columns/args to `string` instead of `number`. */
|
|
10
|
+
numericAsString?: boolean;
|
|
9
11
|
}
|
|
10
12
|
declare function generateDatabaseTypes(tables: SchemaTable[], options?: GenerateOptions): string;
|
|
11
13
|
|
package/dist/index.js
CHANGED
|
@@ -14,11 +14,13 @@ function mapColType(colType, ctx) {
|
|
|
14
14
|
case "uuid":
|
|
15
15
|
case "timestamp":
|
|
16
16
|
case "timestamptz":
|
|
17
|
+
case "tsvector":
|
|
17
18
|
case "date":
|
|
18
19
|
case "time":
|
|
19
20
|
case "bytea":
|
|
20
|
-
case "numeric":
|
|
21
21
|
return "string";
|
|
22
|
+
case "numeric":
|
|
23
|
+
return ctx.numericAsString ? "string" : "number";
|
|
22
24
|
case "int":
|
|
23
25
|
case "int2":
|
|
24
26
|
case "int4":
|
|
@@ -46,20 +48,29 @@ function mapColType(colType, ctx) {
|
|
|
46
48
|
}
|
|
47
49
|
function mapPgType(raw, ctx) {
|
|
48
50
|
if (raw.startsWith("_")) return `${mapPgType(raw.slice(1), ctx)}[]`;
|
|
51
|
+
if (raw.endsWith("[]")) return `${mapPgType(raw.slice(0, -2), ctx)}[]`;
|
|
49
52
|
switch (raw) {
|
|
50
53
|
case "int2":
|
|
51
54
|
case "int4":
|
|
52
55
|
case "int8":
|
|
53
56
|
case "float4":
|
|
54
57
|
case "float8":
|
|
58
|
+
case "smallint":
|
|
59
|
+
case "integer":
|
|
60
|
+
case "bigint":
|
|
61
|
+
case "real":
|
|
62
|
+
case "double precision":
|
|
55
63
|
return "number";
|
|
56
64
|
case "numeric":
|
|
57
|
-
return "string";
|
|
65
|
+
return ctx.numericAsString ? "string" : "number";
|
|
58
66
|
case "bool":
|
|
67
|
+
case "boolean":
|
|
59
68
|
return "boolean";
|
|
60
69
|
case "text":
|
|
61
70
|
case "varchar":
|
|
62
71
|
case "bpchar":
|
|
72
|
+
case "character varying":
|
|
73
|
+
case "character":
|
|
63
74
|
case "citext":
|
|
64
75
|
case "uuid":
|
|
65
76
|
case "date":
|
|
@@ -67,8 +78,15 @@ function mapPgType(raw, ctx) {
|
|
|
67
78
|
case "timetz":
|
|
68
79
|
case "timestamp":
|
|
69
80
|
case "timestamptz":
|
|
81
|
+
case "time without time zone":
|
|
82
|
+
case "time with time zone":
|
|
83
|
+
case "timestamp without time zone":
|
|
84
|
+
case "timestamp with time zone":
|
|
85
|
+
case "tsvector":
|
|
70
86
|
case "bytea":
|
|
71
87
|
return "string";
|
|
88
|
+
case "vector":
|
|
89
|
+
return "number[]";
|
|
72
90
|
case "json":
|
|
73
91
|
case "jsonb":
|
|
74
92
|
return "Json";
|
|
@@ -87,15 +105,14 @@ function tsType(col, ctx) {
|
|
|
87
105
|
function genRow(cols, ctx) {
|
|
88
106
|
return cols.map((c) => ` ${c.name}: ${tsType(c, ctx)}`).join("\n");
|
|
89
107
|
}
|
|
90
|
-
var AUTO_COLS = /* @__PURE__ */ new Set(["id", "created_at", "updated_at"]);
|
|
91
108
|
function isInsertOptional(col) {
|
|
92
|
-
return col.nullable || col.default_val != null ||
|
|
109
|
+
return col.nullable || col.default_val != null || col.is_identity === true;
|
|
93
110
|
}
|
|
94
111
|
function genInsert(cols, ctx) {
|
|
95
|
-
return cols.map((c) => ` ${c.name}${isInsertOptional(c) ? "?" : ""}: ${tsType(c, ctx)}`).join("\n");
|
|
112
|
+
return cols.filter((c) => c.is_generated !== true).map((c) => ` ${c.name}${isInsertOptional(c) ? "?" : ""}: ${tsType(c, ctx)}`).join("\n");
|
|
96
113
|
}
|
|
97
114
|
function genUpdate(cols, ctx) {
|
|
98
|
-
return cols.map((c) => ` ${c.name}?: ${tsType(c, ctx)}`).join("\n");
|
|
115
|
+
return cols.filter((c) => c.is_generated !== true).map((c) => ` ${c.name}?: ${tsType(c, ctx)}`).join("\n");
|
|
99
116
|
}
|
|
100
117
|
function genRelationships(t) {
|
|
101
118
|
const fks = t.columns.filter((c) => c.references);
|
|
@@ -104,7 +121,7 @@ function genRelationships(t) {
|
|
|
104
121
|
for (const c of fks) {
|
|
105
122
|
const ref = c.references;
|
|
106
123
|
const name = ref.constraint_name ?? `${t.name}_${c.name}_fkey`;
|
|
107
|
-
const key = ref.constraint_name ??
|
|
124
|
+
const key = ref.constraint_name ?? `__synthetic__${c.name}`;
|
|
108
125
|
let g = groups.get(key);
|
|
109
126
|
if (!g) {
|
|
110
127
|
g = { name, columns: [], refTable: ref.table, refColumns: [], cols: [] };
|
|
@@ -114,8 +131,19 @@ function genRelationships(t) {
|
|
|
114
131
|
g.refColumns.push(ref.column);
|
|
115
132
|
g.cols.push(c);
|
|
116
133
|
}
|
|
134
|
+
const sameColSet = (a, b) => {
|
|
135
|
+
if (!a || a.length !== b.length || a.length === 0) return false;
|
|
136
|
+
const setA = new Set(a);
|
|
137
|
+
return b.every((c) => setA.has(c));
|
|
138
|
+
};
|
|
117
139
|
const entries = [...groups.values()].map((g) => {
|
|
118
|
-
const isOneToOne = g.cols.
|
|
140
|
+
const isOneToOne = g.cols.length === 1 ? g.cols[0].unique || t.primary_key === g.cols[0].name || t.primary_key_columns?.length === 1 && t.primary_key_columns[0] === g.cols[0].name || Boolean(
|
|
141
|
+
t.unique_constraints?.some((uc) => uc.length === 1 && uc[0] === g.cols[0].name)
|
|
142
|
+
) || Boolean(
|
|
143
|
+
t.indexes?.some(
|
|
144
|
+
(idx) => idx.unique && idx.columns.length === 1 && idx.columns[0] === g.cols[0].name
|
|
145
|
+
)
|
|
146
|
+
) : sameColSet(t.primary_key_columns, g.columns) || Boolean(t.unique_constraints?.some((uc) => sameColSet(uc, g.columns))) || Boolean(t.indexes?.some((idx) => idx.unique && sameColSet(idx.columns, g.columns)));
|
|
119
147
|
const cols = g.columns.map((c) => `'${c}'`).join(", ");
|
|
120
148
|
const refCols = g.refColumns.map((c) => `'${c}'`).join(", ");
|
|
121
149
|
return ` {
|
|
@@ -131,12 +159,21 @@ ${entries.join(",\n")}
|
|
|
131
159
|
]`;
|
|
132
160
|
}
|
|
133
161
|
function genFunctionArgs(fn, ctx) {
|
|
134
|
-
const inputArgs = fn.args.filter((a) => a
|
|
162
|
+
const inputArgs = fn.args.filter((a) => !isReturnColumn(a));
|
|
135
163
|
if (inputArgs.length === 0) return "Record<string, never>";
|
|
136
164
|
const entries = inputArgs.map((a) => `${a.name}: ${mapPgType(a.data_type, ctx)}`).join("; ");
|
|
137
165
|
return `{ ${entries} }`;
|
|
138
166
|
}
|
|
167
|
+
function isReturnColumn(a) {
|
|
168
|
+
const m = a.mode.toUpperCase();
|
|
169
|
+
return m === "OUT" || m === "TABLE";
|
|
170
|
+
}
|
|
139
171
|
function mapReturnType(fn, ctx) {
|
|
172
|
+
const cols = fn.args.filter(isReturnColumn);
|
|
173
|
+
if (fn.returns === "record" && cols.length > 0) {
|
|
174
|
+
const row = `{ ${cols.map((c) => `${c.name}: ${mapPgType(c.data_type, ctx)}`).join("; ")} }`;
|
|
175
|
+
return fn.is_set_returning ? `${row}[]` : row;
|
|
176
|
+
}
|
|
140
177
|
const base = mapPgType(fn.returns, ctx);
|
|
141
178
|
if (!fn.is_set_returning) return base;
|
|
142
179
|
return base === "unknown" ? "Record<string, unknown>[]" : `${base}[]`;
|
|
@@ -201,8 +238,8 @@ ${entries.join("\n")}
|
|
|
201
238
|
}`;
|
|
202
239
|
}
|
|
203
240
|
var HEADER = `// ============================================================
|
|
204
|
-
//
|
|
205
|
-
//
|
|
241
|
+
// Auto-generated TypeScript types by Aurabase (@aurabase/gen)
|
|
242
|
+
// DO NOT EDIT \u2014 regenerate with \`npx @aurabase/gen\`
|
|
206
243
|
// ============================================================
|
|
207
244
|
`;
|
|
208
245
|
function generateDatabaseTypes(tables, options = {}) {
|
|
@@ -215,7 +252,8 @@ function generateDatabaseTypes(tables, options = {}) {
|
|
|
215
252
|
typeName,
|
|
216
253
|
schemaName,
|
|
217
254
|
enumNames: new Set(enums.map((e) => e.name)),
|
|
218
|
-
relationNames
|
|
255
|
+
relationNames,
|
|
256
|
+
numericAsString: options.numericAsString === true
|
|
219
257
|
};
|
|
220
258
|
const functionsBody = genFunctionsBody(options.functions ?? [], ctx);
|
|
221
259
|
const enumsBody = genEnumsBody(enums);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aurabase/gen",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5",
|
|
4
4
|
"description": "Générateur de types TypeScript Database pour le SDK Aurabase (CLI aurabase-gen)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -49,13 +49,13 @@
|
|
|
49
49
|
"types"
|
|
50
50
|
],
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@aurabase/core": "0.7.
|
|
53
|
-
"@aurabase/aurabase-js": "0.7.
|
|
52
|
+
"@aurabase/core": "0.7.5",
|
|
53
|
+
"@aurabase/aurabase-js": "0.7.5"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"tsup": "^8.0.0",
|
|
57
57
|
"typescript": "^5.5.0",
|
|
58
|
-
"vitest": "^4.1.
|
|
58
|
+
"vitest": "^4.1.12",
|
|
59
59
|
"@types/node": "20.14.15"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|