codegen.ts89 lines · main
1export interface ColumnSnapshot {
2 sqlType: string;
3 nullable: boolean;
4 primaryKey: boolean;
5 unique: boolean;
6 default?: string;
7 references?: { table: string; column: string; onDelete?: 'cascade' | 'set null' | 'restrict' };
8}
9
10export interface TableSnapshot {
11 columns: Record<string, ColumnSnapshot>;
12 indexes: Array<{ columns: string[]; unique: boolean }>;
13 access?: { read?: string; write?: string };
14}
15
16export interface SchemaSnapshot {
17 version: 1;
18 tables: Record<string, TableSnapshot>;
19}
20
21const README = `# briven/_generated
22
23Generated by briven cli. Do not edit by hand. Regenerated on every \`briven dev\`
24push and at the end of \`briven\` wizard runs.
25`;
26
27function tsTypeFor(sqlType: string): string {
28 if (sqlType === 'text' || sqlType === 'uuid' || sqlType === 'timestamptz') return 'string';
29 if (sqlType.startsWith('varchar(')) return 'string';
30 if (sqlType === 'integer') return 'number';
31 if (sqlType === 'bigint') return 'bigint';
32 if (sqlType === 'boolean') return 'boolean';
33 if (sqlType === 'jsonb') return 'unknown';
34 if (sqlType.startsWith('vector(')) return 'number[]';
35 return 'unknown';
36}
37
38function pascal(s: string): string {
39 return s.charAt(0).toUpperCase() + s.slice(1);
40}
41
42function renderTable(name: string, table: TableSnapshot): string {
43 const lines: string[] = [];
44 lines.push(`export interface ${pascal(name)} {`);
45 for (const [col, def] of Object.entries(table.columns)) {
46 const ts = tsTypeFor(def.sqlType);
47 const tail = def.nullable ? ' | null' : '';
48 lines.push(` ${col}: ${ts}${tail};`);
49 }
50 lines.push('}');
51 return lines.join('\n');
52}
53
54function renderDataModel(snapshot: SchemaSnapshot): string {
55 const interfaces = Object.entries(snapshot.tables)
56 .map(([name, table]) => renderTable(name, table))
57 .join('\n\n');
58 const tableMapEntries = Object.keys(snapshot.tables)
59 .map((name) => ` ${name}: ${pascal(name)};`)
60 .join('\n');
61 const tablesType =
62 Object.keys(snapshot.tables).length === 0
63 ? 'export type Tables = Record<string, never>;'
64 : `export interface Tables {\n${tableMapEntries}\n}`;
65 return `// Generated by briven cli. Do not edit by hand.\n\n${interfaces}${interfaces ? '\n\n' : ''}${tablesType}\n\nexport type Doc<T extends keyof Tables> = Tables[T];\nexport type Id<_T extends keyof Tables> = string;\n`;
66}
67
68function renderApi(functionFilenames: string[]): string {
69 const names = functionFilenames
70 .map((f) => f.replace(/\.ts$/, ''))
71 .filter((n) => /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(n));
72 if (names.length === 0) {
73 return `// Generated by briven cli. Do not edit by hand.\n\nexport const api = {} as const;\n`;
74 }
75 const imports = names.map((n) => `import ${n} from '../functions/${n}.js';`).join('\n');
76 const entries = names.map((n) => ` ${n},`).join('\n');
77 return `// Generated by briven cli. Do not edit by hand.\n\n${imports}\n\nexport const api = {\n${entries}\n} as const;\n`;
78}
79
80export function generate(
81 snapshot: SchemaSnapshot,
82 functionFilenames: string[],
83): Map<string, string> {
84 const out = new Map<string, string>();
85 out.set('briven/_generated/dataModel.d.ts', renderDataModel(snapshot));
86 out.set('briven/_generated/api.ts', renderApi(functionFilenames));
87 out.set('briven/_generated/README.md', README);
88 return out;
89}