import { ColumnShapeOutput, ColumnsShape, ColumnTypesBase, Db, getColumnTypes, Query, } from 'pqb'; import { MapRelations, Relation, RelationThunks } from './relations/relations'; import { OrchidORM } from './orm'; export type ModelClass = new () => T; export type ModelClasses = Record; export type ModelToDb = Db< T['table'], T['columns']['shape'], 'relations' extends keyof T ? T['relations'] extends RelationThunks ? { [K in keyof T['relations']]: Relation; } : Query['relations'] : Query['relations'], T['columnTypes'] > & { definedAs: string; db: OrchidORM }; export type DbModel = ModelToDb> & Omit>, keyof Query>; type ModelConfig = { shape: ColumnsShape; type: unknown; }; type ScopeFn = ( q: DbModel, ) => Scope; export type Model = { table: string; columns: ModelConfig; schema?: string; columnTypes: ColumnTypesBase; }; export const createModel = (options: { columnTypes: CT; }) => { return class Model { table!: string; columns!: ModelConfig; schema?: string; columnTypes: CT; constructor() { this.columnTypes = options.columnTypes; } setColumns = ( fn: (t: CT) => T, ): { shape: T; type: ColumnShapeOutput } => { const shape = getColumnTypes(options.columnTypes, fn); return { shape, type: undefined as unknown as ColumnShapeOutput, }; }; belongsTo< Self extends this, Related extends ModelClass, Scope extends Query, Options extends { primaryKey: keyof InstanceType['columns']['shape']; foreignKey: keyof Self['columns']['shape']; scope?: ScopeFn; required?: boolean; }, >(this: Self, fn: () => Related, options: Options) { return { type: 'belongsTo' as const, returns: 'one' as const, fn, options, }; } hasOne< Self extends this, Related extends ModelClass, Scope extends Query, Through extends string, Source extends string, Options extends ( | { primaryKey: keyof Self['columns']['shape']; foreignKey: keyof InstanceType['columns']['shape']; } | { through: Through; source: Source; } ) & { scope?: ScopeFn; required?: boolean; }, >(this: Self, fn: () => Related, options: Options) { return { type: 'hasOne' as const, returns: 'one' as const, fn, options, }; } hasMany< Self extends this, Related extends ModelClass, Scope extends Query, Through extends string, Source extends string, Options extends ( | { primaryKey: keyof Self['columns']['shape']; foreignKey: keyof InstanceType['columns']['shape']; } | { through: Through; source: Source; } ) & { scope?: ScopeFn; required?: boolean; }, >(this: Self, fn: () => Related, options: Options) { return { type: 'hasMany' as const, returns: 'many' as const, fn, options, }; } hasAndBelongsToMany< Self extends this, Related extends ModelClass, Scope extends Query, Options extends { primaryKey: keyof Self['columns']['shape']; associationPrimaryKey: keyof InstanceType['columns']['shape']; foreignKey: string; associationForeignKey: string; joinTable: string; scope?: ScopeFn; required?: boolean; }, >(this: Self, fn: () => Related, options: Options) { return { type: 'hasAndBelongsToMany' as const, returns: 'many' as const, fn, options, }; } }; };