appflare 0.2.35 → 0.2.37
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/Documentation.md +8 -8
- package/cli/commands/index.ts +1 -1
- package/cli/generate.ts +24 -0
- package/cli/index.ts +1 -1
- package/cli/schema-compiler.ts +180 -2
- package/cli/templates/core/README.md +1 -1
- package/cli/templates/core/client/handlers/index.ts +7 -0
- package/cli/templates/handlers/generators/handlers.ts +2 -2
- package/cli/templates/handlers/generators/registration/modules/realtime/utils.ts +4 -10
- package/cli/templates/handlers/generators/types/context.ts +8 -2
- package/cli/templates/handlers/generators/types/query-definitions/filter-and-where-types.ts +6 -16
- package/cli/templates/handlers/generators/types/query-definitions/query-helper-functions.ts +216 -17
- package/cli/templates/handlers/index.ts +2 -1
- package/cli/templates/handlers/types.ts +2 -2
- package/dist/cli/index.js +502 -301
- package/dist/cli/index.mjs +502 -301
- package/dist/index.d.mts +81 -2
- package/dist/index.d.ts +81 -2
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/schema.ts +229 -3
package/dist/index.d.mts
CHANGED
|
@@ -85,10 +85,37 @@ type LoadedAppflareConfig = {
|
|
|
85
85
|
config: NormalizedAppflareConfig;
|
|
86
86
|
};
|
|
87
87
|
|
|
88
|
-
type ColumnType = "int" | "string" | "boolean" | "date";
|
|
88
|
+
type ColumnType = "int" | "string" | "boolean" | "date" | "enum" | "json";
|
|
89
|
+
type JsonShape = {
|
|
90
|
+
kind: "array";
|
|
91
|
+
element: JsonShape;
|
|
92
|
+
} | {
|
|
93
|
+
kind: "object";
|
|
94
|
+
shape: Record<string, JsonShape>;
|
|
95
|
+
} | {
|
|
96
|
+
kind: "string";
|
|
97
|
+
} | {
|
|
98
|
+
kind: "number";
|
|
99
|
+
} | {
|
|
100
|
+
kind: "boolean";
|
|
101
|
+
} | {
|
|
102
|
+
kind: "date";
|
|
103
|
+
} | {
|
|
104
|
+
kind: "unknown";
|
|
105
|
+
};
|
|
106
|
+
type EnumDefinition = {
|
|
107
|
+
kind: "enum";
|
|
108
|
+
name: string;
|
|
109
|
+
sqlName?: string;
|
|
110
|
+
values: readonly string[];
|
|
111
|
+
};
|
|
89
112
|
type ColumnBuilderOptions = {
|
|
90
113
|
sqlName?: string;
|
|
91
114
|
length?: number;
|
|
115
|
+
enumValues?: readonly string[];
|
|
116
|
+
enumRef?: string;
|
|
117
|
+
isArray?: boolean;
|
|
118
|
+
jsonShape?: JsonShape;
|
|
92
119
|
};
|
|
93
120
|
type PrimaryKeyOptions = {
|
|
94
121
|
autoIncrement?: boolean;
|
|
@@ -153,6 +180,14 @@ type ColumnDefinition = {
|
|
|
153
180
|
nowDefault?: boolean;
|
|
154
181
|
runtimeDefaultFn?: () => unknown;
|
|
155
182
|
references?: ColumnReference;
|
|
183
|
+
/** For enum columns: the allowed values. */
|
|
184
|
+
enumValues?: readonly string[];
|
|
185
|
+
/** For enum columns: reference to a named enum definition. */
|
|
186
|
+
enumRef?: string;
|
|
187
|
+
/** When true, the column stores an array of enum values. */
|
|
188
|
+
isArray?: boolean;
|
|
189
|
+
/** For JSON columns: the shape of the stored data. */
|
|
190
|
+
jsonShape?: JsonShape;
|
|
156
191
|
};
|
|
157
192
|
type OneRelationDefinition = {
|
|
158
193
|
kind: "relation";
|
|
@@ -208,7 +243,21 @@ type TableDefinition = {
|
|
|
208
243
|
type SchemaDefinition = {
|
|
209
244
|
kind: "schema";
|
|
210
245
|
tables: Record<string, TableDefinition>;
|
|
246
|
+
enums: Record<string, EnumDefinition>;
|
|
211
247
|
};
|
|
248
|
+
declare class EnumBuilder<T extends string> {
|
|
249
|
+
private def;
|
|
250
|
+
constructor(name: string, values: readonly T[]);
|
|
251
|
+
sql(name: string): EnumBuilder<T>;
|
|
252
|
+
toDefinition(): EnumDefinition;
|
|
253
|
+
getValues(): readonly T[];
|
|
254
|
+
}
|
|
255
|
+
declare class JsonArrayBuilder {
|
|
256
|
+
private _element;
|
|
257
|
+
constructor(element: JsonShape);
|
|
258
|
+
get shape(): JsonShape;
|
|
259
|
+
toColumnBuilder(): ColumnBuilder;
|
|
260
|
+
}
|
|
212
261
|
declare class ColumnBuilder {
|
|
213
262
|
private definition;
|
|
214
263
|
constructor(typeOrDefinition: ColumnType | ColumnDefinition, options?: ColumnBuilderOptions);
|
|
@@ -228,12 +277,19 @@ declare class ColumnBuilder {
|
|
|
228
277
|
*/
|
|
229
278
|
defaultNow(): ColumnBuilder;
|
|
230
279
|
references(table: string, column?: string, actions?: Pick<ColumnReference, "onDelete" | "onUpdate">): ColumnBuilder;
|
|
280
|
+
/** Marks the column as an array (e.g., for enum arrays). */
|
|
281
|
+
array(): ColumnBuilder;
|
|
231
282
|
toDefinition(): ColumnDefinition;
|
|
232
283
|
}
|
|
233
284
|
declare function table(shape: TableShape, options?: TableOptions): TableDefinition;
|
|
234
|
-
|
|
285
|
+
type SchemaOptions = {
|
|
286
|
+
enums?: Record<string, EnumDefinition>;
|
|
287
|
+
};
|
|
288
|
+
declare function schema(tables: Record<string, TableDefinition>, options?: SchemaOptions): SchemaDefinition;
|
|
235
289
|
declare function isSchemaDefinition(value: unknown): value is SchemaDefinition;
|
|
236
290
|
declare const v: {
|
|
291
|
+
/** Define a reusable enum that can be referenced by multiple columns. */
|
|
292
|
+
defineEnum: <T extends string>(name: string, values: readonly T[]) => EnumBuilder<T>;
|
|
237
293
|
int: (options?: ColumnBuilderOptions) => ColumnBuilder;
|
|
238
294
|
number: (options?: ColumnBuilderOptions) => ColumnBuilder;
|
|
239
295
|
string: (options?: ColumnBuilderOptions) => ColumnBuilder;
|
|
@@ -245,6 +301,29 @@ declare const v: {
|
|
|
245
301
|
* `crypto.randomUUID()` at runtime (no SQL sequence).
|
|
246
302
|
*/
|
|
247
303
|
uuid: (options?: ColumnBuilderOptions) => ColumnBuilder;
|
|
304
|
+
/**
|
|
305
|
+
* Enum column builder.
|
|
306
|
+
* Pass an EnumBuilder (from v.defineEnum) or inline values.
|
|
307
|
+
*/
|
|
308
|
+
enum: <T extends string>(valuesOrEnum: readonly T[] | EnumBuilder<T>, options?: ColumnBuilderOptions) => ColumnBuilder;
|
|
309
|
+
/**
|
|
310
|
+
* Enum array column builder.
|
|
311
|
+
* Stores multiple enum values in a single column.
|
|
312
|
+
*/
|
|
313
|
+
enumArray: <T extends string>(valuesOrEnum: readonly T[] | EnumBuilder<T>, options?: ColumnBuilderOptions) => ColumnBuilder;
|
|
314
|
+
/**
|
|
315
|
+
* JSON array column builder.
|
|
316
|
+
* Stored as JSON text, auto-serialized on insert/query.
|
|
317
|
+
* Use v.string(), v.number(), v.object(), or v.array() as the element type.
|
|
318
|
+
* Example: v.array(v.string()), v.array(v.object({ name: v.string() }))
|
|
319
|
+
*/
|
|
320
|
+
array: (element: JsonArrayBuilder | ColumnBuilder) => ColumnBuilder;
|
|
321
|
+
/**
|
|
322
|
+
* JSON object column builder.
|
|
323
|
+
* Stored as JSON text, auto-serialized on insert/query.
|
|
324
|
+
* Pass a shape record: v.object({ name: v.string(), count: v.number() })
|
|
325
|
+
*/
|
|
326
|
+
object: <TShape extends Record<string, JsonShape | ColumnBuilder>>(shape: TShape) => ColumnBuilder;
|
|
248
327
|
one: (targetTable: string, fieldOrOptions?: string | RelationOneOptions, options?: RelationOneOptions) => OneRelationDefinition;
|
|
249
328
|
many: (targetTable: string, fieldOrOptions?: string | RelationManyOptions, options?: RelationManyOptions) => ManyRelationDefinition;
|
|
250
329
|
manyToMany: (targetTable: string, options?: RelationManyToManyOptions) => ManyToManyRelationDefinition;
|
package/dist/index.d.ts
CHANGED
|
@@ -85,10 +85,37 @@ type LoadedAppflareConfig = {
|
|
|
85
85
|
config: NormalizedAppflareConfig;
|
|
86
86
|
};
|
|
87
87
|
|
|
88
|
-
type ColumnType = "int" | "string" | "boolean" | "date";
|
|
88
|
+
type ColumnType = "int" | "string" | "boolean" | "date" | "enum" | "json";
|
|
89
|
+
type JsonShape = {
|
|
90
|
+
kind: "array";
|
|
91
|
+
element: JsonShape;
|
|
92
|
+
} | {
|
|
93
|
+
kind: "object";
|
|
94
|
+
shape: Record<string, JsonShape>;
|
|
95
|
+
} | {
|
|
96
|
+
kind: "string";
|
|
97
|
+
} | {
|
|
98
|
+
kind: "number";
|
|
99
|
+
} | {
|
|
100
|
+
kind: "boolean";
|
|
101
|
+
} | {
|
|
102
|
+
kind: "date";
|
|
103
|
+
} | {
|
|
104
|
+
kind: "unknown";
|
|
105
|
+
};
|
|
106
|
+
type EnumDefinition = {
|
|
107
|
+
kind: "enum";
|
|
108
|
+
name: string;
|
|
109
|
+
sqlName?: string;
|
|
110
|
+
values: readonly string[];
|
|
111
|
+
};
|
|
89
112
|
type ColumnBuilderOptions = {
|
|
90
113
|
sqlName?: string;
|
|
91
114
|
length?: number;
|
|
115
|
+
enumValues?: readonly string[];
|
|
116
|
+
enumRef?: string;
|
|
117
|
+
isArray?: boolean;
|
|
118
|
+
jsonShape?: JsonShape;
|
|
92
119
|
};
|
|
93
120
|
type PrimaryKeyOptions = {
|
|
94
121
|
autoIncrement?: boolean;
|
|
@@ -153,6 +180,14 @@ type ColumnDefinition = {
|
|
|
153
180
|
nowDefault?: boolean;
|
|
154
181
|
runtimeDefaultFn?: () => unknown;
|
|
155
182
|
references?: ColumnReference;
|
|
183
|
+
/** For enum columns: the allowed values. */
|
|
184
|
+
enumValues?: readonly string[];
|
|
185
|
+
/** For enum columns: reference to a named enum definition. */
|
|
186
|
+
enumRef?: string;
|
|
187
|
+
/** When true, the column stores an array of enum values. */
|
|
188
|
+
isArray?: boolean;
|
|
189
|
+
/** For JSON columns: the shape of the stored data. */
|
|
190
|
+
jsonShape?: JsonShape;
|
|
156
191
|
};
|
|
157
192
|
type OneRelationDefinition = {
|
|
158
193
|
kind: "relation";
|
|
@@ -208,7 +243,21 @@ type TableDefinition = {
|
|
|
208
243
|
type SchemaDefinition = {
|
|
209
244
|
kind: "schema";
|
|
210
245
|
tables: Record<string, TableDefinition>;
|
|
246
|
+
enums: Record<string, EnumDefinition>;
|
|
211
247
|
};
|
|
248
|
+
declare class EnumBuilder<T extends string> {
|
|
249
|
+
private def;
|
|
250
|
+
constructor(name: string, values: readonly T[]);
|
|
251
|
+
sql(name: string): EnumBuilder<T>;
|
|
252
|
+
toDefinition(): EnumDefinition;
|
|
253
|
+
getValues(): readonly T[];
|
|
254
|
+
}
|
|
255
|
+
declare class JsonArrayBuilder {
|
|
256
|
+
private _element;
|
|
257
|
+
constructor(element: JsonShape);
|
|
258
|
+
get shape(): JsonShape;
|
|
259
|
+
toColumnBuilder(): ColumnBuilder;
|
|
260
|
+
}
|
|
212
261
|
declare class ColumnBuilder {
|
|
213
262
|
private definition;
|
|
214
263
|
constructor(typeOrDefinition: ColumnType | ColumnDefinition, options?: ColumnBuilderOptions);
|
|
@@ -228,12 +277,19 @@ declare class ColumnBuilder {
|
|
|
228
277
|
*/
|
|
229
278
|
defaultNow(): ColumnBuilder;
|
|
230
279
|
references(table: string, column?: string, actions?: Pick<ColumnReference, "onDelete" | "onUpdate">): ColumnBuilder;
|
|
280
|
+
/** Marks the column as an array (e.g., for enum arrays). */
|
|
281
|
+
array(): ColumnBuilder;
|
|
231
282
|
toDefinition(): ColumnDefinition;
|
|
232
283
|
}
|
|
233
284
|
declare function table(shape: TableShape, options?: TableOptions): TableDefinition;
|
|
234
|
-
|
|
285
|
+
type SchemaOptions = {
|
|
286
|
+
enums?: Record<string, EnumDefinition>;
|
|
287
|
+
};
|
|
288
|
+
declare function schema(tables: Record<string, TableDefinition>, options?: SchemaOptions): SchemaDefinition;
|
|
235
289
|
declare function isSchemaDefinition(value: unknown): value is SchemaDefinition;
|
|
236
290
|
declare const v: {
|
|
291
|
+
/** Define a reusable enum that can be referenced by multiple columns. */
|
|
292
|
+
defineEnum: <T extends string>(name: string, values: readonly T[]) => EnumBuilder<T>;
|
|
237
293
|
int: (options?: ColumnBuilderOptions) => ColumnBuilder;
|
|
238
294
|
number: (options?: ColumnBuilderOptions) => ColumnBuilder;
|
|
239
295
|
string: (options?: ColumnBuilderOptions) => ColumnBuilder;
|
|
@@ -245,6 +301,29 @@ declare const v: {
|
|
|
245
301
|
* `crypto.randomUUID()` at runtime (no SQL sequence).
|
|
246
302
|
*/
|
|
247
303
|
uuid: (options?: ColumnBuilderOptions) => ColumnBuilder;
|
|
304
|
+
/**
|
|
305
|
+
* Enum column builder.
|
|
306
|
+
* Pass an EnumBuilder (from v.defineEnum) or inline values.
|
|
307
|
+
*/
|
|
308
|
+
enum: <T extends string>(valuesOrEnum: readonly T[] | EnumBuilder<T>, options?: ColumnBuilderOptions) => ColumnBuilder;
|
|
309
|
+
/**
|
|
310
|
+
* Enum array column builder.
|
|
311
|
+
* Stores multiple enum values in a single column.
|
|
312
|
+
*/
|
|
313
|
+
enumArray: <T extends string>(valuesOrEnum: readonly T[] | EnumBuilder<T>, options?: ColumnBuilderOptions) => ColumnBuilder;
|
|
314
|
+
/**
|
|
315
|
+
* JSON array column builder.
|
|
316
|
+
* Stored as JSON text, auto-serialized on insert/query.
|
|
317
|
+
* Use v.string(), v.number(), v.object(), or v.array() as the element type.
|
|
318
|
+
* Example: v.array(v.string()), v.array(v.object({ name: v.string() }))
|
|
319
|
+
*/
|
|
320
|
+
array: (element: JsonArrayBuilder | ColumnBuilder) => ColumnBuilder;
|
|
321
|
+
/**
|
|
322
|
+
* JSON object column builder.
|
|
323
|
+
* Stored as JSON text, auto-serialized on insert/query.
|
|
324
|
+
* Pass a shape record: v.object({ name: v.string(), count: v.number() })
|
|
325
|
+
*/
|
|
326
|
+
object: <TShape extends Record<string, JsonShape | ColumnBuilder>>(shape: TShape) => ColumnBuilder;
|
|
248
327
|
one: (targetTable: string, fieldOrOptions?: string | RelationOneOptions, options?: RelationOneOptions) => OneRelationDefinition;
|
|
249
328
|
many: (targetTable: string, fieldOrOptions?: string | RelationManyOptions, options?: RelationManyOptions) => ManyRelationDefinition;
|
|
250
329
|
manyToMany: (targetTable: string, options?: RelationManyToManyOptions) => ManyToManyRelationDefinition;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
'use strict';var l=class n{definition;constructor(e,
|
|
1
|
+
'use strict';var l=class n{def;constructor(e,t){this.def={kind:"enum",name:e,values:t};}sql(e){return new n(e,this.def.values)}toDefinition(){return {...this.def}}getValues(){return this.def.values}};function d(n,e){return new l(n,e)}var u=class{_element;constructor(e){this._element=e;}get shape(){return {kind:"array",element:this._element}}toColumnBuilder(){return new o("json",{jsonShape:this.shape})}};var o=class n{definition;constructor(e,t={}){if(typeof e=="string"){this.definition={kind:"column",type:e,sqlName:t.sqlName,length:t.length,enumValues:t.enumValues,enumRef:t.enumRef,jsonShape:t.jsonShape};return}this.definition={...e};}with(e){return new n({...this.definition,...e})}sql(e){return this.with({sqlName:e})}notNull(){return this.with({notNull:true,nullable:false})}nullable(){return this.with({nullable:true,notNull:false})}primaryKey(e={}){return this.with({primaryKey:true,autoIncrement:e.autoIncrement??this.definition.autoIncrement})}unique(e){return this.with({unique:e?{name:e}:true})}index(e){return this.with({index:e?{name:e}:true})}default(e){return this.with({sqlDefault:e})}defaultFn(e){return this.with({runtimeDefaultFn:e})}defaultNow(){return this.with({nowDefault:true,runtimeDefaultFn:()=>new Date})}references(e,t="id",r={}){return this.with({references:{table:e,column:t,onDelete:r.onDelete,onUpdate:r.onUpdate}})}array(){return this.with({isArray:true})}toDefinition(){return {...this.definition}}};function p(n,e={}){let t={},r={};for(let[i,a]of Object.entries(n)){if(a instanceof o){t[i]=a.toDefinition();continue}if(a.kind==="relation"){r[i]=a;continue}throw new Error(`Invalid table field '${i}'. Use column builders or relation helpers.`)}return {kind:"table",sqlName:e.sqlName,columns:t,relations:r}}function m(n,e={}){return {kind:"schema",tables:n,enums:e.enums??{}}}function f(n){if(typeof n!="object"||n===null)return false;let e=n;return e.kind==="schema"&&typeof e.tables=="object"&&(typeof e.enums=="object"||e.enums===void 0)}function c(n,e,t={}){let r=typeof e=="string"?e:void 0,i=typeof e=="string"?t:e??t;return {kind:"relation",relation:"one",targetTable:n,field:i.field??r,referenceField:i.referenceField,fkType:i.fkType,sqlName:i.sqlName,notNull:i.notNull,nullable:i.nullable,onDelete:i.onDelete,onUpdate:i.onUpdate}}function y(n,e,t={}){let r=typeof e=="string"?e:void 0,i=typeof e=="string"?t:e??t;return {kind:"relation",relation:"many",targetTable:n,field:i.field??r,referenceField:i.referenceField,fkType:i.fkType,sqlName:i.sqlName,notNull:i.notNull,nullable:i.nullable,onDelete:i.onDelete,onUpdate:i.onUpdate}}function g(n,e={}){return {kind:"relation",relation:"manyToMany",targetTable:n,referenceField:e.referenceField,targetReferenceField:e.targetReferenceField,junctionTable:e.junctionTable,sourceField:e.sourceField,targetField:e.targetField,sourceSqlName:e.sourceSqlName,targetSqlName:e.targetSqlName,onDelete:e.onDelete,onUpdate:e.onUpdate}}function s(n){let e=n.toDefinition();return e.jsonShape?e.jsonShape:e.type==="int"?{kind:"number"}:e.type==="string"?{kind:"string"}:e.type==="boolean"?{kind:"boolean"}:e.type==="date"?{kind:"date"}:e.type==="enum"&&e.enumValues?{kind:"string"}:{kind:"unknown"}}var h={defineEnum:(n,e)=>d(n,e),int:(n={})=>new o("int",n),number:(n={})=>new o("int",n),string:(n={})=>new o("string",n),boolean:(n={})=>new o("boolean",n),date:(n={})=>new o("date",n),uuid:(n={})=>new o({kind:"column",type:"string",sqlName:n.sqlName,length:n.length??36,primaryKey:true,notNull:true,nullable:false,uuidPrimaryKey:true,runtimeDefaultFn:()=>crypto.randomUUID()}),enum:(n,e={})=>{if(n instanceof l){let t=n.toDefinition();return new o("enum",{...e,enumValues:t.values,enumRef:t.name})}return new o("enum",{...e,enumValues:n})},enumArray:(n,e={})=>{if(n instanceof l){let t=n.toDefinition();return new o("enum",{...e,enumValues:t.values,enumRef:t.name,isArray:true})}return new o("enum",{...e,enumValues:n,isArray:true})},array:n=>{if(n instanceof u)return n.toColumnBuilder();if(n instanceof o){let e=n.toDefinition();if(e.jsonShape)return new o("json",{jsonShape:e.jsonShape});let t={kind:"array",element:s(n)};return new o("json",{jsonShape:t})}return new o("json",{jsonShape:{kind:"unknown"}})},object:n=>{let e={};for(let[t,r]of Object.entries(n))r instanceof o?e[t]=s(r):e[t]=r;return new o("json",{jsonShape:{kind:"object",shape:e}})},one:(n,e,t={})=>c(n,e,t),many:(n,e,t={})=>y(n,e,t),manyToMany:(n,e={})=>g(n,e)};exports.isSchemaDefinition=f;exports.schema=m;exports.table=p;exports.v=h;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var l=class n{definition;constructor(e,
|
|
1
|
+
var l=class n{def;constructor(e,t){this.def={kind:"enum",name:e,values:t};}sql(e){return new n(e,this.def.values)}toDefinition(){return {...this.def}}getValues(){return this.def.values}};function d(n,e){return new l(n,e)}var u=class{_element;constructor(e){this._element=e;}get shape(){return {kind:"array",element:this._element}}toColumnBuilder(){return new o("json",{jsonShape:this.shape})}};var o=class n{definition;constructor(e,t={}){if(typeof e=="string"){this.definition={kind:"column",type:e,sqlName:t.sqlName,length:t.length,enumValues:t.enumValues,enumRef:t.enumRef,jsonShape:t.jsonShape};return}this.definition={...e};}with(e){return new n({...this.definition,...e})}sql(e){return this.with({sqlName:e})}notNull(){return this.with({notNull:true,nullable:false})}nullable(){return this.with({nullable:true,notNull:false})}primaryKey(e={}){return this.with({primaryKey:true,autoIncrement:e.autoIncrement??this.definition.autoIncrement})}unique(e){return this.with({unique:e?{name:e}:true})}index(e){return this.with({index:e?{name:e}:true})}default(e){return this.with({sqlDefault:e})}defaultFn(e){return this.with({runtimeDefaultFn:e})}defaultNow(){return this.with({nowDefault:true,runtimeDefaultFn:()=>new Date})}references(e,t="id",r={}){return this.with({references:{table:e,column:t,onDelete:r.onDelete,onUpdate:r.onUpdate}})}array(){return this.with({isArray:true})}toDefinition(){return {...this.definition}}};function p(n,e={}){let t={},r={};for(let[i,a]of Object.entries(n)){if(a instanceof o){t[i]=a.toDefinition();continue}if(a.kind==="relation"){r[i]=a;continue}throw new Error(`Invalid table field '${i}'. Use column builders or relation helpers.`)}return {kind:"table",sqlName:e.sqlName,columns:t,relations:r}}function m(n,e={}){return {kind:"schema",tables:n,enums:e.enums??{}}}function f(n){if(typeof n!="object"||n===null)return false;let e=n;return e.kind==="schema"&&typeof e.tables=="object"&&(typeof e.enums=="object"||e.enums===void 0)}function c(n,e,t={}){let r=typeof e=="string"?e:void 0,i=typeof e=="string"?t:e??t;return {kind:"relation",relation:"one",targetTable:n,field:i.field??r,referenceField:i.referenceField,fkType:i.fkType,sqlName:i.sqlName,notNull:i.notNull,nullable:i.nullable,onDelete:i.onDelete,onUpdate:i.onUpdate}}function y(n,e,t={}){let r=typeof e=="string"?e:void 0,i=typeof e=="string"?t:e??t;return {kind:"relation",relation:"many",targetTable:n,field:i.field??r,referenceField:i.referenceField,fkType:i.fkType,sqlName:i.sqlName,notNull:i.notNull,nullable:i.nullable,onDelete:i.onDelete,onUpdate:i.onUpdate}}function g(n,e={}){return {kind:"relation",relation:"manyToMany",targetTable:n,referenceField:e.referenceField,targetReferenceField:e.targetReferenceField,junctionTable:e.junctionTable,sourceField:e.sourceField,targetField:e.targetField,sourceSqlName:e.sourceSqlName,targetSqlName:e.targetSqlName,onDelete:e.onDelete,onUpdate:e.onUpdate}}function s(n){let e=n.toDefinition();return e.jsonShape?e.jsonShape:e.type==="int"?{kind:"number"}:e.type==="string"?{kind:"string"}:e.type==="boolean"?{kind:"boolean"}:e.type==="date"?{kind:"date"}:e.type==="enum"&&e.enumValues?{kind:"string"}:{kind:"unknown"}}var h={defineEnum:(n,e)=>d(n,e),int:(n={})=>new o("int",n),number:(n={})=>new o("int",n),string:(n={})=>new o("string",n),boolean:(n={})=>new o("boolean",n),date:(n={})=>new o("date",n),uuid:(n={})=>new o({kind:"column",type:"string",sqlName:n.sqlName,length:n.length??36,primaryKey:true,notNull:true,nullable:false,uuidPrimaryKey:true,runtimeDefaultFn:()=>crypto.randomUUID()}),enum:(n,e={})=>{if(n instanceof l){let t=n.toDefinition();return new o("enum",{...e,enumValues:t.values,enumRef:t.name})}return new o("enum",{...e,enumValues:n})},enumArray:(n,e={})=>{if(n instanceof l){let t=n.toDefinition();return new o("enum",{...e,enumValues:t.values,enumRef:t.name,isArray:true})}return new o("enum",{...e,enumValues:n,isArray:true})},array:n=>{if(n instanceof u)return n.toColumnBuilder();if(n instanceof o){let e=n.toDefinition();if(e.jsonShape)return new o("json",{jsonShape:e.jsonShape});let t={kind:"array",element:s(n)};return new o("json",{jsonShape:t})}return new o("json",{jsonShape:{kind:"unknown"}})},object:n=>{let e={};for(let[t,r]of Object.entries(n))r instanceof o?e[t]=s(r):e[t]=r;return new o("json",{jsonShape:{kind:"object",shape:e}})},one:(n,e,t={})=>c(n,e,t),many:(n,e,t={})=>y(n,e,t),manyToMany:(n,e={})=>g(n,e)};export{f as isSchemaDefinition,m as schema,p as table,h as v};
|
package/package.json
CHANGED
package/schema.ts
CHANGED
|
@@ -1,8 +1,28 @@
|
|
|
1
|
-
export type ColumnType = "int" | "string" | "boolean" | "date";
|
|
1
|
+
export type ColumnType = "int" | "string" | "boolean" | "date" | "enum" | "json";
|
|
2
|
+
|
|
3
|
+
export type JsonShape =
|
|
4
|
+
| { kind: "array"; element: JsonShape }
|
|
5
|
+
| { kind: "object"; shape: Record<string, JsonShape> }
|
|
6
|
+
| { kind: "string" }
|
|
7
|
+
| { kind: "number" }
|
|
8
|
+
| { kind: "boolean" }
|
|
9
|
+
| { kind: "date" }
|
|
10
|
+
| { kind: "unknown" };
|
|
11
|
+
|
|
12
|
+
export type EnumDefinition = {
|
|
13
|
+
kind: "enum";
|
|
14
|
+
name: string;
|
|
15
|
+
sqlName?: string;
|
|
16
|
+
values: readonly string[];
|
|
17
|
+
};
|
|
2
18
|
|
|
3
19
|
export type ColumnBuilderOptions = {
|
|
4
20
|
sqlName?: string;
|
|
5
21
|
length?: number;
|
|
22
|
+
enumValues?: readonly string[];
|
|
23
|
+
enumRef?: string;
|
|
24
|
+
isArray?: boolean;
|
|
25
|
+
jsonShape?: JsonShape;
|
|
6
26
|
};
|
|
7
27
|
|
|
8
28
|
export type PrimaryKeyOptions = {
|
|
@@ -75,6 +95,14 @@ export type ColumnDefinition = {
|
|
|
75
95
|
nowDefault?: boolean;
|
|
76
96
|
runtimeDefaultFn?: () => unknown;
|
|
77
97
|
references?: ColumnReference;
|
|
98
|
+
/** For enum columns: the allowed values. */
|
|
99
|
+
enumValues?: readonly string[];
|
|
100
|
+
/** For enum columns: reference to a named enum definition. */
|
|
101
|
+
enumRef?: string;
|
|
102
|
+
/** When true, the column stores an array of enum values. */
|
|
103
|
+
isArray?: boolean;
|
|
104
|
+
/** For JSON columns: the shape of the stored data. */
|
|
105
|
+
jsonShape?: JsonShape;
|
|
78
106
|
};
|
|
79
107
|
|
|
80
108
|
export type OneRelationDefinition = {
|
|
@@ -141,8 +169,72 @@ export type TableDefinition = {
|
|
|
141
169
|
export type SchemaDefinition = {
|
|
142
170
|
kind: "schema";
|
|
143
171
|
tables: Record<string, TableDefinition>;
|
|
172
|
+
enums: Record<string, EnumDefinition>;
|
|
144
173
|
};
|
|
145
174
|
|
|
175
|
+
export class EnumBuilder<T extends string> {
|
|
176
|
+
private def: EnumDefinition;
|
|
177
|
+
|
|
178
|
+
public constructor(name: string, values: readonly T[]) {
|
|
179
|
+
this.def = {
|
|
180
|
+
kind: "enum",
|
|
181
|
+
name,
|
|
182
|
+
values: values as readonly string[],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
public sql(name: string): EnumBuilder<T> {
|
|
187
|
+
return new EnumBuilder<T>(name, this.def.values as readonly T[]);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
public toDefinition(): EnumDefinition {
|
|
191
|
+
return { ...this.def };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
public getValues(): readonly T[] {
|
|
195
|
+
return this.def.values as readonly T[];
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function defineEnum<T extends string>(
|
|
200
|
+
name: string,
|
|
201
|
+
values: readonly T[],
|
|
202
|
+
): EnumBuilder<T> {
|
|
203
|
+
return new EnumBuilder(name, values);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export class JsonArrayBuilder {
|
|
207
|
+
private _element: JsonShape;
|
|
208
|
+
|
|
209
|
+
public constructor(element: JsonShape) {
|
|
210
|
+
this._element = element;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
public get shape(): JsonShape {
|
|
214
|
+
return { kind: "array", element: this._element };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
public toColumnBuilder(): ColumnBuilder {
|
|
218
|
+
return new ColumnBuilder("json", { jsonShape: this.shape });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export class JsonObjectBuilder<TShape extends Record<string, JsonShape>> {
|
|
223
|
+
private _shape: TShape;
|
|
224
|
+
|
|
225
|
+
public constructor(shape: TShape) {
|
|
226
|
+
this._shape = shape;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
public get shape(): JsonShape {
|
|
230
|
+
return { kind: "object", shape: this._shape };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
public toColumnBuilder(): ColumnBuilder {
|
|
234
|
+
return new ColumnBuilder("json", { jsonShape: this.shape });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
146
238
|
export class ColumnBuilder {
|
|
147
239
|
private definition: ColumnDefinition;
|
|
148
240
|
|
|
@@ -156,6 +248,9 @@ export class ColumnBuilder {
|
|
|
156
248
|
type: typeOrDefinition,
|
|
157
249
|
sqlName: options.sqlName,
|
|
158
250
|
length: options.length,
|
|
251
|
+
enumValues: options.enumValues,
|
|
252
|
+
enumRef: options.enumRef,
|
|
253
|
+
jsonShape: options.jsonShape,
|
|
159
254
|
};
|
|
160
255
|
return;
|
|
161
256
|
}
|
|
@@ -234,6 +329,11 @@ export class ColumnBuilder {
|
|
|
234
329
|
});
|
|
235
330
|
}
|
|
236
331
|
|
|
332
|
+
/** Marks the column as an array (e.g., for enum arrays). */
|
|
333
|
+
public array(): ColumnBuilder {
|
|
334
|
+
return this.with({ isArray: true });
|
|
335
|
+
}
|
|
336
|
+
|
|
237
337
|
public toDefinition(): ColumnDefinition {
|
|
238
338
|
return { ...this.definition };
|
|
239
339
|
}
|
|
@@ -270,12 +370,18 @@ export function table(
|
|
|
270
370
|
};
|
|
271
371
|
}
|
|
272
372
|
|
|
373
|
+
export type SchemaOptions = {
|
|
374
|
+
enums?: Record<string, EnumDefinition>;
|
|
375
|
+
};
|
|
376
|
+
|
|
273
377
|
export function schema(
|
|
274
378
|
tables: Record<string, TableDefinition>,
|
|
379
|
+
options: SchemaOptions = {},
|
|
275
380
|
): SchemaDefinition {
|
|
276
381
|
return {
|
|
277
382
|
kind: "schema",
|
|
278
383
|
tables,
|
|
384
|
+
enums: options.enums ?? {},
|
|
279
385
|
};
|
|
280
386
|
}
|
|
281
387
|
|
|
@@ -283,8 +389,16 @@ export function isSchemaDefinition(value: unknown): value is SchemaDefinition {
|
|
|
283
389
|
if (typeof value !== "object" || value === null) {
|
|
284
390
|
return false;
|
|
285
391
|
}
|
|
286
|
-
const candidate = value as {
|
|
287
|
-
|
|
392
|
+
const candidate = value as {
|
|
393
|
+
kind?: string;
|
|
394
|
+
tables?: unknown;
|
|
395
|
+
enums?: unknown;
|
|
396
|
+
};
|
|
397
|
+
return (
|
|
398
|
+
candidate.kind === "schema" &&
|
|
399
|
+
typeof candidate.tables === "object" &&
|
|
400
|
+
(typeof candidate.enums === "object" || candidate.enums === undefined)
|
|
401
|
+
);
|
|
288
402
|
}
|
|
289
403
|
|
|
290
404
|
// ---------------------------------------------------------------------------
|
|
@@ -363,7 +477,28 @@ function buildManyToManyRelation(
|
|
|
363
477
|
// Public API
|
|
364
478
|
// ---------------------------------------------------------------------------
|
|
365
479
|
|
|
480
|
+
function columnBuilderToJsonShape(builder: ColumnBuilder): JsonShape {
|
|
481
|
+
const def = builder.toDefinition();
|
|
482
|
+
if (def.jsonShape) return def.jsonShape;
|
|
483
|
+
if (def.type === "int") return { kind: "number" };
|
|
484
|
+
if (def.type === "string") return { kind: "string" };
|
|
485
|
+
if (def.type === "boolean") return { kind: "boolean" };
|
|
486
|
+
if (def.type === "date") return { kind: "date" };
|
|
487
|
+
if (def.type === "enum" && def.enumValues) {
|
|
488
|
+
return {
|
|
489
|
+
kind: "string",
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
return { kind: "unknown" };
|
|
493
|
+
}
|
|
494
|
+
|
|
366
495
|
export const v = {
|
|
496
|
+
/** Define a reusable enum that can be referenced by multiple columns. */
|
|
497
|
+
defineEnum: <T extends string>(
|
|
498
|
+
name: string,
|
|
499
|
+
values: readonly T[],
|
|
500
|
+
): EnumBuilder<T> => defineEnum(name, values),
|
|
501
|
+
|
|
367
502
|
int: (options: ColumnBuilderOptions = {}) =>
|
|
368
503
|
new ColumnBuilder("int", options),
|
|
369
504
|
number: (options: ColumnBuilderOptions = {}) =>
|
|
@@ -393,6 +528,97 @@ export const v = {
|
|
|
393
528
|
runtimeDefaultFn: () => crypto.randomUUID(),
|
|
394
529
|
}),
|
|
395
530
|
|
|
531
|
+
/**
|
|
532
|
+
* Enum column builder.
|
|
533
|
+
* Pass an EnumBuilder (from v.defineEnum) or inline values.
|
|
534
|
+
*/
|
|
535
|
+
enum: <T extends string>(
|
|
536
|
+
valuesOrEnum: readonly T[] | EnumBuilder<T>,
|
|
537
|
+
options: ColumnBuilderOptions = {},
|
|
538
|
+
): ColumnBuilder => {
|
|
539
|
+
if (valuesOrEnum instanceof EnumBuilder) {
|
|
540
|
+
const def = valuesOrEnum.toDefinition();
|
|
541
|
+
return new ColumnBuilder("enum", {
|
|
542
|
+
...options,
|
|
543
|
+
enumValues: def.values,
|
|
544
|
+
enumRef: def.name,
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
return new ColumnBuilder("enum", {
|
|
548
|
+
...options,
|
|
549
|
+
enumValues: valuesOrEnum,
|
|
550
|
+
});
|
|
551
|
+
},
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Enum array column builder.
|
|
555
|
+
* Stores multiple enum values in a single column.
|
|
556
|
+
*/
|
|
557
|
+
enumArray: <T extends string>(
|
|
558
|
+
valuesOrEnum: readonly T[] | EnumBuilder<T>,
|
|
559
|
+
options: ColumnBuilderOptions = {},
|
|
560
|
+
): ColumnBuilder => {
|
|
561
|
+
if (valuesOrEnum instanceof EnumBuilder) {
|
|
562
|
+
const def = valuesOrEnum.toDefinition();
|
|
563
|
+
return new ColumnBuilder("enum", {
|
|
564
|
+
...options,
|
|
565
|
+
enumValues: def.values,
|
|
566
|
+
enumRef: def.name,
|
|
567
|
+
isArray: true,
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
return new ColumnBuilder("enum", {
|
|
571
|
+
...options,
|
|
572
|
+
enumValues: valuesOrEnum,
|
|
573
|
+
isArray: true,
|
|
574
|
+
});
|
|
575
|
+
},
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* JSON array column builder.
|
|
579
|
+
* Stored as JSON text, auto-serialized on insert/query.
|
|
580
|
+
* Use v.string(), v.number(), v.object(), or v.array() as the element type.
|
|
581
|
+
* Example: v.array(v.string()), v.array(v.object({ name: v.string() }))
|
|
582
|
+
*/
|
|
583
|
+
array: (element: JsonArrayBuilder | ColumnBuilder): ColumnBuilder => {
|
|
584
|
+
if (element instanceof JsonArrayBuilder) {
|
|
585
|
+
return element.toColumnBuilder();
|
|
586
|
+
}
|
|
587
|
+
if (element instanceof ColumnBuilder) {
|
|
588
|
+
const def = element.toDefinition();
|
|
589
|
+
if (def.jsonShape) {
|
|
590
|
+
return new ColumnBuilder("json", { jsonShape: def.jsonShape });
|
|
591
|
+
}
|
|
592
|
+
const shape: JsonShape = {
|
|
593
|
+
kind: "array",
|
|
594
|
+
element: columnBuilderToJsonShape(element),
|
|
595
|
+
};
|
|
596
|
+
return new ColumnBuilder("json", { jsonShape: shape });
|
|
597
|
+
}
|
|
598
|
+
return new ColumnBuilder("json", { jsonShape: { kind: "unknown" } });
|
|
599
|
+
},
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* JSON object column builder.
|
|
603
|
+
* Stored as JSON text, auto-serialized on insert/query.
|
|
604
|
+
* Pass a shape record: v.object({ name: v.string(), count: v.number() })
|
|
605
|
+
*/
|
|
606
|
+
object: <TShape extends Record<string, JsonShape | ColumnBuilder>>(
|
|
607
|
+
shape: TShape,
|
|
608
|
+
): ColumnBuilder => {
|
|
609
|
+
const resolved: Record<string, JsonShape> = {};
|
|
610
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
611
|
+
if (value instanceof ColumnBuilder) {
|
|
612
|
+
resolved[key] = columnBuilderToJsonShape(value);
|
|
613
|
+
} else {
|
|
614
|
+
resolved[key] = value;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return new ColumnBuilder("json", {
|
|
618
|
+
jsonShape: { kind: "object", shape: resolved },
|
|
619
|
+
});
|
|
620
|
+
},
|
|
621
|
+
|
|
396
622
|
one: (
|
|
397
623
|
targetTable: string,
|
|
398
624
|
fieldOrOptions?: string | RelationOneOptions,
|