@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 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 || AUTO_COLS.has(col.name);
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 ?? `\0synthetic\0${c.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.every((c) => c.unique || t.primary_key === c.name);
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.mode.toUpperCase() !== "OUT");
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
- // Types TypeScript auto-g\xE9n\xE9r\xE9s par Aurabase (@aurabase/gen)
212
- // NE PAS MODIFIER \u2014 r\xE9g\xE9n\xE9rer avec \`npx @aurabase/gen\`
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
- var VERSION = "0.7.3";
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 <apiKey> [--out <file>]
488
+ aurabase-gen --url <projectUrl> --key <serviceRoleKey> [--out <file>]
442
489
 
443
490
  OPTIONS:
444
- --url <url> Aurabase project URL (e.g. https://my-project.aurabase.cloud)
445
- [Auto-detected: NEXT_PUBLIC_AURABASE_URL]
446
- --key <key> Project API key (anon or service_role)
447
- [Auto-detected: AURABASE_SERVICE_ROLE_KEY, then NEXT_PUBLIC_AURABASE_ANON_KEY]
448
- -o, --out <file> Output file (e.g. src/types/database.types.ts)
449
- If omitted, the result is written to stdout.
450
- -s, --schema <name> Database schema name (default: 'public')
451
- -t, --type-name <t> Name of the main generated interface (default: 'Database')
452
- --allow-empty Generate the types even if some tables have no declared columns
453
- -h, --help Show this help
454
- -v, --version Show the version
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 aura_anon_123 --out db.types.ts
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 ?? process.env.NEXT_PUBLIC_AURABASE_ANON_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 API key is missing.\n");
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 the CLI arguments (--url and --key) or in your environment variables (.env / .env.local):"
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(" NEXT_PUBLIC_AURABASE_ANON_KEY=aura_anon_...\n");
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 = "0.7.3";
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 = "0.7.3";
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 { writeFileSync, existsSync, readFileSync } from 'fs';
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 || AUTO_COLS.has(col.name);
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 ?? `\0synthetic\0${c.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.every((c) => c.unique || t.primary_key === c.name);
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.mode.toUpperCase() !== "OUT");
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
- // Types TypeScript auto-g\xE9n\xE9r\xE9s par Aurabase (@aurabase/gen)
210
- // NE PAS MODIFIER \u2014 r\xE9g\xE9n\xE9rer avec \`npx @aurabase/gen\`
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
- var VERSION = "0.7.3";
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 <apiKey> [--out <file>]
486
+ aurabase-gen --url <projectUrl> --key <serviceRoleKey> [--out <file>]
440
487
 
441
488
  OPTIONS:
442
- --url <url> Aurabase project URL (e.g. https://my-project.aurabase.cloud)
443
- [Auto-detected: NEXT_PUBLIC_AURABASE_URL]
444
- --key <key> Project API key (anon or service_role)
445
- [Auto-detected: AURABASE_SERVICE_ROLE_KEY, then NEXT_PUBLIC_AURABASE_ANON_KEY]
446
- -o, --out <file> Output file (e.g. src/types/database.types.ts)
447
- If omitted, the result is written to stdout.
448
- -s, --schema <name> Database schema name (default: 'public')
449
- -t, --type-name <t> Name of the main generated interface (default: 'Database')
450
- --allow-empty Generate the types even if some tables have no declared columns
451
- -h, --help Show this help
452
- -v, --version Show the version
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 aura_anon_123 --out db.types.ts
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 ?? process.env.NEXT_PUBLIC_AURABASE_ANON_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 API key is missing.\n");
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 the CLI arguments (--url and --key) or in your environment variables (.env / .env.local):"
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(" NEXT_PUBLIC_AURABASE_ANON_KEY=aura_anon_...\n");
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 || AUTO_COLS.has(col.name);
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 ?? `\0synthetic\0${c.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.every((c) => c.unique || t.primary_key === c.name);
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.mode.toUpperCase() !== "OUT");
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
- // Types TypeScript auto-g\xE9n\xE9r\xE9s par Aurabase (@aurabase/gen)
207
- // NE PAS MODIFIER \u2014 r\xE9g\xE9n\xE9rer avec \`npx @aurabase/gen\`
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 || AUTO_COLS.has(col.name);
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 ?? `\0synthetic\0${c.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.every((c) => c.unique || t.primary_key === c.name);
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.mode.toUpperCase() !== "OUT");
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
- // Types TypeScript auto-g\xE9n\xE9r\xE9s par Aurabase (@aurabase/gen)
205
- // NE PAS MODIFIER \u2014 r\xE9g\xE9n\xE9rer avec \`npx @aurabase/gen\`
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.4",
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.4",
53
- "@aurabase/aurabase-js": "0.7.4"
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.6",
58
+ "vitest": "^4.1.12",
59
59
  "@types/node": "20.14.15"
60
60
  },
61
61
  "scripts": {