appflare 0.2.36 → 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/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
- declare function schema(tables: Record<string, TableDefinition>): SchemaDefinition;
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
- declare function schema(tables: Record<string, TableDefinition>): SchemaDefinition;
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,i={}){if(typeof e=="string"){this.definition={kind:"column",type:e,sqlName:i.sqlName,length:i.length};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,i="id",o={}){return this.with({references:{table:e,column:i,onDelete:o.onDelete,onUpdate:o.onUpdate}})}toDefinition(){return {...this.definition}}};function a(n,e={}){let i={},o={};for(let[t,r]of Object.entries(n)){if(r instanceof l){i[t]=r.toDefinition();continue}if(r.kind==="relation"){o[t]=r;continue}throw new Error(`Invalid table field '${t}'. Use column builders or relation helpers.`)}return {kind:"table",sqlName:e.sqlName,columns:i,relations:o}}function u(n){return {kind:"schema",tables:n}}function s(n){if(typeof n!="object"||n===null)return false;let e=n;return e.kind==="schema"&&typeof e.tables=="object"}function p(n,e,i={}){let o=typeof e=="string"?e:void 0,t=typeof e=="string"?i:e??i;return {kind:"relation",relation:"one",targetTable:n,field:t.field??o,referenceField:t.referenceField,fkType:t.fkType,sqlName:t.sqlName,notNull:t.notNull,nullable:t.nullable,onDelete:t.onDelete,onUpdate:t.onUpdate}}function d(n,e,i={}){let o=typeof e=="string"?e:void 0,t=typeof e=="string"?i:e??i;return {kind:"relation",relation:"many",targetTable:n,field:t.field??o,referenceField:t.referenceField,fkType:t.fkType,sqlName:t.sqlName,notNull:t.notNull,nullable:t.nullable,onDelete:t.onDelete,onUpdate:t.onUpdate}}function f(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}}var c={int:(n={})=>new l("int",n),number:(n={})=>new l("int",n),string:(n={})=>new l("string",n),boolean:(n={})=>new l("boolean",n),date:(n={})=>new l("date",n),uuid:(n={})=>new l({kind:"column",type:"string",sqlName:n.sqlName,length:n.length??36,primaryKey:true,notNull:true,nullable:false,uuidPrimaryKey:true,runtimeDefaultFn:()=>crypto.randomUUID()}),one:(n,e,i={})=>p(n,e,i),many:(n,e,i={})=>d(n,e,i),manyToMany:(n,e={})=>f(n,e)};exports.isSchemaDefinition=s;exports.schema=u;exports.table=a;exports.v=c;
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,i={}){if(typeof e=="string"){this.definition={kind:"column",type:e,sqlName:i.sqlName,length:i.length};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,i="id",o={}){return this.with({references:{table:e,column:i,onDelete:o.onDelete,onUpdate:o.onUpdate}})}toDefinition(){return {...this.definition}}};function a(n,e={}){let i={},o={};for(let[t,r]of Object.entries(n)){if(r instanceof l){i[t]=r.toDefinition();continue}if(r.kind==="relation"){o[t]=r;continue}throw new Error(`Invalid table field '${t}'. Use column builders or relation helpers.`)}return {kind:"table",sqlName:e.sqlName,columns:i,relations:o}}function u(n){return {kind:"schema",tables:n}}function s(n){if(typeof n!="object"||n===null)return false;let e=n;return e.kind==="schema"&&typeof e.tables=="object"}function p(n,e,i={}){let o=typeof e=="string"?e:void 0,t=typeof e=="string"?i:e??i;return {kind:"relation",relation:"one",targetTable:n,field:t.field??o,referenceField:t.referenceField,fkType:t.fkType,sqlName:t.sqlName,notNull:t.notNull,nullable:t.nullable,onDelete:t.onDelete,onUpdate:t.onUpdate}}function d(n,e,i={}){let o=typeof e=="string"?e:void 0,t=typeof e=="string"?i:e??i;return {kind:"relation",relation:"many",targetTable:n,field:t.field??o,referenceField:t.referenceField,fkType:t.fkType,sqlName:t.sqlName,notNull:t.notNull,nullable:t.nullable,onDelete:t.onDelete,onUpdate:t.onUpdate}}function f(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}}var c={int:(n={})=>new l("int",n),number:(n={})=>new l("int",n),string:(n={})=>new l("string",n),boolean:(n={})=>new l("boolean",n),date:(n={})=>new l("date",n),uuid:(n={})=>new l({kind:"column",type:"string",sqlName:n.sqlName,length:n.length??36,primaryKey:true,notNull:true,nullable:false,uuidPrimaryKey:true,runtimeDefaultFn:()=>crypto.randomUUID()}),one:(n,e,i={})=>p(n,e,i),many:(n,e,i={})=>d(n,e,i),manyToMany:(n,e={})=>f(n,e)};export{s as isSchemaDefinition,u as schema,a as table,c as v};
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appflare",
3
- "version": "0.2.36",
3
+ "version": "0.2.37",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
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 { kind?: string; tables?: unknown };
287
- return candidate.kind === "schema" && typeof candidate.tables === "object";
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,