columns.ts110 lines · main
1/**
2 * Column builders. Each builder is immutable — every chained call
3 * (.notNull(), .primaryKey(), .default(), .references(), .unique())
4 * returns a new builder rather than mutating the previous one. This lets
5 * users store and reuse intermediate builders without surprise.
6 */
7
8export type OnDelete = 'cascade' | 'set null' | 'restrict';
9
10export interface ColumnDef {
11 readonly sqlType: string;
12 readonly nullable: boolean;
13 readonly primaryKey: boolean;
14 readonly unique: boolean;
15 readonly default?: string;
16 readonly references?: {
17 readonly table: string;
18 readonly column: string;
19 readonly onDelete?: OnDelete;
20 };
21}
22
23export class ColumnBuilder<TDef extends ColumnDef = ColumnDef> {
24 constructor(readonly def: TDef) {}
25
26 private with(patch: Partial<ColumnDef>): ColumnBuilder<TDef> {
27 return new ColumnBuilder({ ...this.def, ...patch } as TDef);
28 }
29
30 notNull(): ColumnBuilder<TDef> {
31 return this.with({ nullable: false });
32 }
33
34 primaryKey(): ColumnBuilder<TDef> {
35 return this.with({ primaryKey: true, nullable: false });
36 }
37
38 unique(): ColumnBuilder<TDef> {
39 return this.with({ unique: true });
40 }
41
42 default(sqlExpression: string): ColumnBuilder<TDef> {
43 return this.with({ default: sqlExpression });
44 }
45
46 references(
47 table: string,
48 column: string = 'id',
49 options?: { onDelete?: OnDelete },
50 ): ColumnBuilder<TDef> {
51 return this.with({
52 references: { table, column, onDelete: options?.onDelete },
53 });
54 }
55}
56
57function col(sqlType: string): ColumnBuilder {
58 return new ColumnBuilder({
59 sqlType,
60 nullable: true,
61 primaryKey: false,
62 unique: false,
63 });
64}
65
66export function text(): ColumnBuilder {
67 return col('text');
68}
69
70export function varchar(length: number): ColumnBuilder {
71 if (!Number.isInteger(length) || length <= 0 || length > 10_485_760) {
72 throw new Error(`varchar length must be a positive integer, got ${length}`);
73 }
74 return col(`varchar(${length})`);
75}
76
77export function integer(): ColumnBuilder {
78 return col('integer');
79}
80
81export function bigint(): ColumnBuilder {
82 return col('bigint');
83}
84
85export function boolean(): ColumnBuilder {
86 return col('boolean');
87}
88
89export function timestamp(): ColumnBuilder {
90 return col('timestamptz');
91}
92
93export function jsonb(): ColumnBuilder {
94 return col('jsonb');
95}
96
97export function uuid(): ColumnBuilder {
98 return col('uuid');
99}
100
101export function vector(_dimensions: number): ColumnBuilder {
102 // Briven's data plane runs on DoltGres, which does not support the
103 // `vector(N)` column type — emitting it produces SQL that only fails
104 // later at apply time with an opaque syntax error. Fail early here
105 // instead. Vector search is a planned feature that will arrive with
106 // LanceDB, at which point this builder can construct a real column.
107 throw new Error(
108 'vector columns are not supported yet on DoltGres — vector search is coming with LanceDB',
109 );
110}