From bf6387bbd41640ec8a6c56a1a167c6aa49345432 Mon Sep 17 00:00:00 2001 From: Patrick Sullivan Date: Sat, 26 Aug 2023 04:54:37 -0400 Subject: [PATCH] feat(tools-storm-plugins-drizzle): Added support for Postgresql and table relations --- .env | 2 +- .github/workflows/ci.yml | 3 +- .../server/attachment/prisma/schema.prisma | 2 +- .../typescript/server/attachment/schema.storm | 2 +- .../shared/utilities/src/common/date-time.ts | 8 + .../shared/utilities/src/common/string-fns.ts | 21 +- tools/storm/plugins/drizzle/src/generator.ts | 127 ++++----- .../storm/plugins/drizzle/src/transformer.ts | 7 +- .../src/utils/postgresql.schema-gen.ts | 79 ++++++ .../plugins/drizzle/src/utils/schema-gen.ts | 250 +++++++++++------- .../drizzle/src/utils/sqlite.schema-gen.ts | 87 ++++++ 11 files changed, 419 insertions(+), 169 deletions(-) create mode 100644 tools/storm/plugins/drizzle/src/utils/postgresql.schema-gen.ts create mode 100644 tools/storm/plugins/drizzle/src/utils/sqlite.schema-gen.ts diff --git a/.env b/.env index 4cf02943..36596a6a 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -CI=true +CI=false TYPESENSE_API_KEY=xyz NODE_OPTIONS="--trace-warnings --require helios-opentelemetry-sdk --heapsnapshot-near-heap-limit=3 --heapsnapshot-signal=SIGTERM" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73ef6725..494c4f47 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,7 +75,8 @@ jobs: # - run: npx nx affected -t test --parallel=3 --configuration=ci --base=${{ github.event.before }} - name: Build repository packages - run: pnpm nx affected -t build --parallel=3 --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} + run: pnpm nx affected -t build --parallel=3 + # run: pnpm nx affected -t build --parallel=3 --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} #- name: Run Tests # uses: nick-fields/retry@v2.8.3 diff --git a/libs/contact/typescript/server/attachment/prisma/schema.prisma b/libs/contact/typescript/server/attachment/prisma/schema.prisma index 7d9447aa..fa430925 100644 --- a/libs/contact/typescript/server/attachment/prisma/schema.prisma +++ b/libs/contact/typescript/server/attachment/prisma/schema.prisma @@ -74,7 +74,7 @@ model Contact { /// @@allow('create,update,delete,read', true) model ContactAttachment { - id String @id() @default(cuid()) + id String @id() @default(uuid()) createdAt DateTime @default(now()) createdBy String updatedAt DateTime? @updatedAt() diff --git a/libs/contact/typescript/server/attachment/schema.storm b/libs/contact/typescript/server/attachment/schema.storm index ee3d51dd..ab9ada2c 100644 --- a/libs/contact/typescript/server/attachment/schema.storm +++ b/libs/contact/typescript/server/attachment/schema.storm @@ -125,7 +125,7 @@ model Contact { // A file attachment included by a Contact model ContactAttachment { // The unique identifier for the ContactAttachment - id String @id @default(cuid()) + id String @id @default(uuid()) // A timestamp of when the ContactAttachment was created createdAt DateTime @default(now()) diff --git a/libs/core/typescript/shared/utilities/src/common/date-time.ts b/libs/core/typescript/shared/utilities/src/common/date-time.ts index b9e96e86..aa491082 100644 --- a/libs/core/typescript/shared/utilities/src/common/date-time.ts +++ b/libs/core/typescript/shared/utilities/src/common/date-time.ts @@ -151,6 +151,14 @@ export class DateTime extends Temporal.Instant implements IDateTime { return DateTime.getDuration(this, dateTimeTo); } + /** + * It returns the current `DateTime` object as a JavaScript `Date` object + * @returns A JavaScript `Date` object. + */ + public asJsDate(): Date { + return new Date(this.epochMilliseconds); + } + /** * Internal identifier field used by architecture to identify the specific object */ diff --git a/libs/core/typescript/shared/utilities/src/common/string-fns.ts b/libs/core/typescript/shared/utilities/src/common/string-fns.ts index 71f3e8d7..d010bdf5 100644 --- a/libs/core/typescript/shared/utilities/src/common/string-fns.ts +++ b/libs/core/typescript/shared/utilities/src/common/string-fns.ts @@ -1,7 +1,8 @@ -import { dash } from "radash"; +import { dash, snake } from "radash"; /** * Upper case the first character of an input string. + * @example ThisIsAnExample */ export const upperCaseFirst = (input?: string): string | undefined => { return input ? input.charAt(0).toUpperCase() + input.substr(1) : input; @@ -9,6 +10,7 @@ export const upperCaseFirst = (input?: string): string | undefined => { /** * Lower case the first character of an input string. + * @example thisIsAnExample */ export const lowerCaseFirst = (input?: string): string | undefined => { return input ? input.charAt(0).toLowerCase() + input.substr(1) : input; @@ -16,7 +18,24 @@ export const lowerCaseFirst = (input?: string): string | undefined => { /** * Convert the input string to kebab case. + * @example this-is-an-example */ export const kebabCase = (input?: string): string | undefined => { return dash(input); }; + +/** + * Convert the input string to snake case. + * @example this_is_an_example + */ +export const snakeCase = (input?: string): string | undefined => { + return snake(input); +}; + +/** + * Convert the input string to constant case. + * @example THIS_IS_AN_EXAMPLE + */ +export const constantCase = (input?: string): string | undefined => { + return snake(input).toUpperCase(); +}; diff --git a/tools/storm/plugins/drizzle/src/generator.ts b/tools/storm/plugins/drizzle/src/generator.ts index ca0f1fc5..6b773cf1 100644 --- a/tools/storm/plugins/drizzle/src/generator.ts +++ b/tools/storm/plugins/drizzle/src/generator.ts @@ -4,12 +4,12 @@ import { upperCaseFirst } from "@open-system/core-shared-utilities/common/string-fns"; import { - ArrayExpr, DataModel, + DataSource, Enum, - MemberAccessExpr, Model, isDataModel, + isDataSource, isEnum } from "@open-system/tools-storm-language/ast"; import { getDefaultOutputFolder } from "@open-system/tools-storm-schema/plugins/plugin-utils"; @@ -22,17 +22,19 @@ import { emitProject, getDataModels, getFileHeader, - isForeignKeyField, + getLiteral, resolvePath, saveProject } from "@open-system/tools-storm-schema/sdk"; -import { DMMF } from "@prisma/generator-helper"; +import { ConnectorType, DMMF } from "@prisma/generator-helper"; import { promises as fs } from "fs"; import { join } from "path"; import { Project } from "ts-morph"; import Transformer from "./transformer"; +import { PostgresqlSchemaGenerator } from "./utils/postgresql.schema-gen"; import removeDir from "./utils/removeDir"; -import { makeFieldSchema } from "./utils/schema-gen"; +import { SchemaGenerator } from "./utils/schema-gen"; +import { SqliteSchemaGenerator } from "./utils/sqlite.schema-gen"; export async function generate( model: Model, @@ -71,46 +73,22 @@ export async function generate( model ); - /*const dataSource = model.declarations.find((d): d is DataSource => + const dataSource = model.declarations.find((d): d is DataSource => isDataSource(d) ); const dataSourceProvider = getLiteral( dataSource?.fields.find(f => f.name === "provider")?.value - ) as ConnectorType;*/ + ) as ConnectorType; - await generateModelSchemas(project, model, output); - - /*if (options.modelOnly !== true) { - // detailed object schemas referenced from input schemas - Transformer.provider = dataSourceProvider; - addMissingInputObjectTypes(inputObjectTypes, outputObjectTypes, models); - const aggregateOperationSupport = - resolveAggregateOperationSupport(inputObjectTypes); - //await generateObjectSchemas(inputObjectTypes, project, output, model); - - // input schemas - const transformer = new Transformer({ - models, - modelOperations, - aggregateOperationSupport, - project, - storm: model - }); - await transformer.generateInputSchemas(); - }*/ + await generateModelSchemas(project, model, output, dataSourceProvider); // create barrel file const exports = [ `export * as schemas from './schemas'`, `export * as enums from './enums'` ]; - /*if (options.modelOnly !== true) { - exports.push( - `export * as input from './input'`, - `export * as objects from './objects'` - ); - }*/ + project.createSourceFile(join(output, "index.ts"), exports.join(";\n"), { overwrite: true }); @@ -173,11 +151,14 @@ async function generateEnumSchemas( async function generateModelSchemas( project: Project, storm: Model, - output: string + output: string, + dataSourceProvider: ConnectorType ) { const schemaNames: string[] = []; for (const dm of getDataModels(storm)) { - schemaNames.push(await generateModelSchema(dm, project, output)); + schemaNames.push( + await generateModelSchema(dm, project, output, dataSourceProvider) + ); } project.createSourceFile( @@ -190,8 +171,23 @@ async function generateModelSchemas( async function generateModelSchema( model: DataModel, project: Project, - output: string + output: string, + dataSourceProvider: ConnectorType ) { + let generator: SchemaGenerator; + switch (dataSourceProvider) { + case "sqlite": + generator = new SqliteSchemaGenerator(); + break; + case "postgresql": + generator = new PostgresqlSchemaGenerator(); + break; + default: + throw new Error( + `Unsupported data source provider: ${dataSourceProvider}` + ); + } + const schemaName = `${kebabCase(model.name)}.schema`; const sf = project.createSourceFile( join(output, "schemas", `${schemaName}.ts`), @@ -204,15 +200,12 @@ async function generateModelSchema( const fields = model.fields.filter( field => !AUXILIARY_FIELDS.includes(field.name) && - // scalar fields only !isDataModel(field.type.reference?.ref) ); writer.writeLine("/* eslint-disable */"); writer.writeLine(getFileHeader("Drizzle ORM")); - writer.writeLine( - `import { blob, integer, real, text, sqliteTable } from "drizzle-orm/sqlite-core";` - ); + writer.writeLine(generator.importStatement); writer.writeLine( `import { UniqueIdGenerator } from "@open-system/core-shared-utilities/common/unique-id-generator";` ); @@ -231,45 +224,33 @@ async function generateModelSchema( } } + model.fields + .filter(modelField => isDataModel(modelField.type.reference?.ref)) + .forEach(modelField => { + writer.writeLine( + `import { ${lowerCaseFirst( + modelField.type.reference.ref.name + )} } from "./${kebabCase( + modelField.type.reference.ref.name + )}.schema";` + ); + }); + // create base schema - writer.write( - ` -export const ${lowerCaseFirst(model.name)} = sqliteTable("${lowerCaseFirst( - model.name - )}", ` - ); + writer.writeLine(""); + writer.write(generator.getTableSchema(model.name)); writer.inlineBlock(() => { fields.forEach(field => { - let fieldLine = `${field.name}: ${makeFieldSchema(field)}`; - - if (isForeignKeyField(field)) { - // field.attributes.find(a => a.name === "relation") - const attribute = field.attributes.find( - attr => attr.decl.ref?.name === "@relation" - ); - if (attribute) { - //const foreignKeyFields = attr.args.find(a => a.name === "fields"); - const referencesFields = attribute.args.find( - a => a.name === "references" - )?.value as ArrayExpr; - referencesFields.items.forEach(item => { - const reference = item as MemberAccessExpr; - - /*const modelFields = model.fields.filter(field => - isDataModel(field.type.reference?.ref) - );*/ - - fieldLine += `.references(() => ${lowerCaseFirst( - field.type.reference?.ref.name - )}.${reference?.member?.ref?.name})`; - }); - } - } - - writer.writeLine(`${fieldLine},`); + writer.writeLine( + `${field.name}: ${generator.getFieldSchema(model, field)},` + ); }); }); writer.writeLine(");"); + writer.writeLine(""); + + writer.writeLine(""); }); + return schemaName; } diff --git a/tools/storm/plugins/drizzle/src/transformer.ts b/tools/storm/plugins/drizzle/src/transformer.ts index 33ccfaf2..5999a6eb 100644 --- a/tools/storm/plugins/drizzle/src/transformer.ts +++ b/tools/storm/plugins/drizzle/src/transformer.ts @@ -1,5 +1,8 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ -import { kebabCase } from "@open-system/core-shared-utilities/common/string-fns"; +import { + constantCase, + kebabCase +} from "@open-system/core-shared-utilities/common/string-fns"; import { Model } from "@open-system/tools-storm-language/ast"; import { AUXILIARY_FIELDS, @@ -61,7 +64,7 @@ export default class Transformer { )}\n${this.generateExportStatement( `${name}`, ` { -${filteredValues.map(v => `${v}: "${v}"`).join(", \n")} +${filteredValues.map(v => `${constantCase(v)}: "${v}"`).join(", \n")} }` )}`; this.project.createSourceFile(filePath, content, { overwrite: true }); diff --git a/tools/storm/plugins/drizzle/src/utils/postgresql.schema-gen.ts b/tools/storm/plugins/drizzle/src/utils/postgresql.schema-gen.ts new file mode 100644 index 00000000..06c5ed92 --- /dev/null +++ b/tools/storm/plugins/drizzle/src/utils/postgresql.schema-gen.ts @@ -0,0 +1,79 @@ +import { + constantCase, + lowerCaseFirst +} from "@open-system/core-shared-utilities/common/string-fns"; +import { DataModelField, isEnum } from "@open-system/tools-storm-language/ast"; +import { SchemaGenerator } from "./schema-gen"; + +export class PostgresqlSchemaGenerator extends SchemaGenerator { + public override readonly tableMethod = "pgTable"; + public override readonly importStatement = `import { blob, integer, real, text, pgTable, relations } from "drizzle-orm/pg-core";`; + + public override getTableSchema(modelName: string) { + return `export const ${lowerCaseFirst(modelName)} = ${ + this.tableMethod + }("${lowerCaseFirst(modelName)}", `; + } + + protected getColumnTypeSchema(field: DataModelField) { + let schema: string; + + if (field.type.reference?.ref && isEnum(field.type.reference?.ref)) { + schema = `text("${field.name}", { enum: [${field.type.reference.ref.fields + .map(f => `${field.type.reference.ref.name}.${constantCase(f.name)}`) + .join(", ")}] })`; + } else { + switch (field.type.type) { + case "Int": + schema = `integer("${field.name}", { mode: "number" })`; + break; + case "Float": + case "Decimal": + schema = `real("${field.name}")`; + break; + case "BigInt": + schema = `blob("${field.name}", { mode: "bigint" })`; + break; + case "String": + schema = `text("${field.name}")`; + break; + case "Boolean": + schema = `integer("${field.name}", { mode: "boolean" })`; + break; + case "DateTime": + schema = `integer("${field.name}", { mode: "timestamp" })`; + break; + case "Bytes": + schema = `blob("${field.name}", { mode: "buffer" })`; + break; + default: + schema = `text("${field.name}")`; + break; + } + } + + if (field.type.array) { + schema = `blob("${field.name}", { mode: "json" }).$type<${ + field.type.type === "Int" + ? "number" + : field.type.type === "BigInt" + ? "bigint" + : field.type.type === "Float" + ? "number" + : field.type.type === "Decimal" + ? "number" + : field.type.type === "String" + ? "string" + : field.type.type === "Boolean" + ? "boolean" + : field.type.type === "DateTime" + ? "Date" + : field.type.type === "Bytes" + ? "Uint8Array" + : "any" + }[]>()`; + } + + return schema; + } +} diff --git a/tools/storm/plugins/drizzle/src/utils/schema-gen.ts b/tools/storm/plugins/drizzle/src/utils/schema-gen.ts index e5b156c3..d3b48bb5 100644 --- a/tools/storm/plugins/drizzle/src/utils/schema-gen.ts +++ b/tools/storm/plugins/drizzle/src/utils/schema-gen.ts @@ -1,108 +1,180 @@ +import { lowerCaseFirst } from "@open-system/core-shared-utilities/common/string-fns"; import { + ArrayExpr, + DataModel, DataModelField, InvocationExpr, LiteralExpr, - isEnum + ReferenceExpr, + isDataModel } from "@open-system/tools-storm-language/ast"; +import { isForeignKeyField } from "@open-system/tools-storm-schema/sdk/utils"; -export function makeFieldSchema(field: DataModelField) { - let schema = makeDrizzleSchema(field); +export abstract class SchemaGenerator { + public abstract readonly tableMethod: string; + public abstract readonly importStatement: string; - for (const attr of field.attributes) { - switch (attr.decl.ref?.name) { - case "@default": - { - if (attr.args.length > 0) { - const functionName = (attr.args[0].value as InvocationExpr) - ?.function?.ref?.name; - const value = (attr.args[0].value as LiteralExpr)?.value; - schema += - functionName && functionName.startsWith("now") - ? ".$defaultFn(() => new Date(DateTime.current.epochMilliseconds))" - : functionName && functionName.startsWith("cuid") - ? ".$defaultFn(() => UniqueIdGenerator.generate())" - : value - ? `.default(${value})` - : ""; + public abstract getTableSchema(modelName: string): string; + + public getFieldSchema(model: DataModel, field: DataModelField): string { + let schema = this.getColumnTypeSchema(field); + + for (const attr of field.attributes) { + switch (attr.decl.ref?.name) { + case "@default": + { + if (attr.args.length > 0) { + const functionName = (attr.args[0].value as InvocationExpr) + ?.function?.ref?.name; + const value = (attr.args[0].value as LiteralExpr)?.value; + schema += + functionName && functionName.startsWith("now") + ? ".$defaultFn(() => DateTime.current.asJsDate())" + : functionName && + (functionName.startsWith("cuid") || + functionName.startsWith("uuid")) + ? ".$defaultFn(() => UniqueIdGenerator.generate())" + : value + ? `.default(${value})` + : ""; + } } - } - break; - case "@unique": - schema += ".unique()"; - break; - case "@id": - schema += ".primaryKey()"; - break; - default: - break; + break; + case "@unique": + schema += ".unique()"; + break; + case "@id": + schema += ".primaryKey()"; + break; + default: + break; + } } - } - if (!field.type.optional) { - schema += ".notNull()"; - } + if (!field.type.optional) { + schema += ".notNull()"; + } - return schema; -} + const modelFields = model.fields.filter(modelField => + isDataModel(modelField.type.reference?.ref) + ); -function makeDrizzleSchema(field: DataModelField) { - let schema: string; + if (isForeignKeyField(field)) { + for (const modelField of modelFields) { + const attribute = modelField.attributes.find( + attr => attr.decl.ref?.name === "@relation" + ); + if (attribute) { + const foreignKeyFields = attribute.args.find( + a => a.name === "fields" + ); + const foreignKeyField = ( + foreignKeyFields.value as ArrayExpr + ).items.find( + item => (item as ReferenceExpr)?.target?.ref?.name === field.name + ); - if (field.type.reference?.ref && isEnum(field.type.reference?.ref)) { - schema = `text("${field.name}", { enum: [${field.type.reference.ref.fields - .map(f => `${field.type.reference.ref.name}.${f.name}`) - .join(", ")}] })`; - } else { - switch (field.type.type) { - case "Int": - schema = `integer("${field.name}", { mode: "number" })`; - break; - case "Float": - case "Decimal": - schema = `real("${field.name}")`; - break; - case "BigInt": - schema = `blob("${field.name}", { mode: "bigint" })`; - break; - case "String": - schema = `text("${field.name}")`; - break; - case "Boolean": - schema = `integer("${field.name}", { mode: "boolean" })`; - break; - case "DateTime": - schema = `integer("${field.name}", { mode: "timestamp" })`; - break; - case "Bytes": - schema = `blob("${field.name}", { mode: "buffer" })`; - break; - default: - schema = `text("${field.name}")`; - break; + if (foreignKeyField) { + const referencesFields = attribute.args.find( + a => a.name === "references" + ); + + (referencesFields.value as ArrayExpr)?.items.forEach(item => { + schema += `.references(() => ${lowerCaseFirst( + modelField.type.reference.ref.name + )}.${(item as ReferenceExpr)?.target?.ref?.name})`; + }); + } + } + } } + + return schema; } - if (field.type.array) { - schema = `blob("${field.name}", { mode: "json" }).$type<${ - field.type.type === "Int" - ? "number" - : field.type.type === "BigInt" - ? "bigint" - : field.type.type === "Float" - ? "number" - : field.type.type === "Decimal" - ? "number" - : field.type.type === "String" - ? "string" - : field.type.type === "Boolean" - ? "boolean" - : field.type.type === "DateTime" - ? "Date" - : field.type.type === "Bytes" - ? "Uint8Array" - : "any" - }[]>()`; + public getRelationsSchema(model: DataModel): string { + const relationFields = model.fields.filter(relationField => + isDataModel(relationField.type.reference?.ref) + ); + if (relationFields.length > 0) { + return ` + export const ${lowerCaseFirst( + model.name + )}Relations = relations(${lowerCaseFirst(model.name)}, ({ ${ + relationFields.some(relationField => + relationField.attributes.every( + attr => attr.decl.ref?.name !== "@relation" + ) + ) + ? "many" + : "" + }${ + relationFields.some(relationField => + relationField.attributes.some( + attr => attr.decl.ref?.name === "@relation" + ) + ) + ? `${ + relationFields.some(relationField => + relationField.attributes.every( + attr => attr.decl.ref?.name !== "@relation" + ) + ) + ? ", " + : "" + }one` + : "" + } }) => ({ + ${relationFields.map( + relationField => + `${relationField.name}: ${ + relationField.attributes.every( + attr => attr.decl.ref?.name !== "@relation" + ) + ? "many" + : "one" + }(${lowerCaseFirst(relationField.type.reference.ref.name)}${ + relationField.attributes.some( + attr => attr.decl.ref?.name === "@relation" + ) + ? `, { + ${relationField.attributes + .find(attr => attr.decl.ref?.name === "@relation") + .args.map(arg => { + if (arg.name === "fields") { + return `fields: [${(arg.value as ArrayExpr).items + .map( + item => + `${lowerCaseFirst(model.name)}.${ + (item as ReferenceExpr)?.target?.ref?.name + }` + ) + .join('", "')}], + `; + } else if (arg.name === "references") { + return `references: [${(arg.value as ArrayExpr).items + .map( + item => + `${lowerCaseFirst(relationField.type.reference.ref.name)}.${ + (item as ReferenceExpr)?.target?.ref?.name + }` + ) + .join('", "')}], + `; + } + + return ""; + })} + }` + : "" + }),` + )} + })); + `; + } + + return ""; } - return schema; + protected abstract getColumnTypeSchema(field: DataModelField): string; } diff --git a/tools/storm/plugins/drizzle/src/utils/sqlite.schema-gen.ts b/tools/storm/plugins/drizzle/src/utils/sqlite.schema-gen.ts new file mode 100644 index 00000000..7a1321bc --- /dev/null +++ b/tools/storm/plugins/drizzle/src/utils/sqlite.schema-gen.ts @@ -0,0 +1,87 @@ +import { + constantCase, + lowerCaseFirst +} from "@open-system/core-shared-utilities/common/string-fns"; +import { + DataModel, + DataModelField, + isEnum +} from "@open-system/tools-storm-language/ast"; +import { SchemaGenerator } from "./schema-gen"; + +export class SqliteSchemaGenerator extends SchemaGenerator { + public override readonly tableMethod = "sqliteTable"; + public override readonly importStatement = `import { blob, integer, real, text, sqliteTable } from "drizzle-orm/sqlite-core";`; + + public override getTableSchema(modelName: string) { + return `export const ${lowerCaseFirst(modelName)} = ${ + this.tableMethod + }("${lowerCaseFirst(modelName)}", `; + } + + public override getRelationsSchema(_model: DataModel): string { + return ""; + } + + protected getColumnTypeSchema(field: DataModelField) { + let schema: string; + + if (field.type.reference?.ref && isEnum(field.type.reference?.ref)) { + schema = `text("${field.name}", { enum: [${field.type.reference.ref.fields + .map(f => `${field.type.reference.ref.name}.${constantCase(f.name)}`) + .join(", ")}] })`; + } else { + switch (field.type.type) { + case "Int": + schema = `integer("${field.name}", { mode: "number" })`; + break; + case "Float": + case "Decimal": + schema = `real("${field.name}")`; + break; + case "BigInt": + schema = `blob("${field.name}", { mode: "bigint" })`; + break; + case "String": + schema = `text("${field.name}")`; + break; + case "Boolean": + schema = `integer("${field.name}", { mode: "boolean" })`; + break; + case "DateTime": + schema = `integer("${field.name}", { mode: "timestamp" })`; + break; + case "Bytes": + schema = `blob("${field.name}", { mode: "buffer" })`; + break; + default: + schema = `text("${field.name}")`; + break; + } + } + + if (field.type.array) { + schema = `blob("${field.name}", { mode: "json" }).$type<${ + field.type.type === "Int" + ? "number" + : field.type.type === "BigInt" + ? "bigint" + : field.type.type === "Float" + ? "number" + : field.type.type === "Decimal" + ? "number" + : field.type.type === "String" + ? "string" + : field.type.type === "Boolean" + ? "boolean" + : field.type.type === "DateTime" + ? "Date" + : field.type.type === "Bytes" + ? "Uint8Array" + : "any" + }[]>()`; + } + + return schema; + } +}