import { getColumnsForTable, getForeignKeysReferencing } from '#services/database_inspector' import { mapDBTypeToFrontend, mapDBTypeToTs, mapDBTypeToValidator, } from '#services/module_generator/db_types' import { camelCase, capitalize } from '#services/module_generator/naming' export interface EnhancedColumn { name: string camelCase: string type: string isPrimary: boolean isPassword: boolean isEmail: boolean isDateTime: boolean isSpecial: boolean isRequired: boolean isNullable: boolean validatorType: string needsTrim: boolean } export interface TestField { camelCase: string sampleValue: string | number | boolean column_name: string } export interface ModuleContext { hasPassword: boolean enhancedColumns: EnhancedColumn[] createFields: TestField[] updateFields: TestField[] insertJson: string frontendFields: Array<{ name: string; type: string }> relationships: { imports: string[]; code: string[] } } /** Derives everything the templates and builders need from the table schema. */ export async function buildModuleContext(table: string): Promise { const columns = await getColumnsForTable(table) const enhancedColumns = columns.map((col): EnhancedColumn => { const colName = col.column_name const type = mapDBTypeToTs(col.data_type) + (col.is_nullable === 'YES' ? ' | null' : '') return { name: colName, camelCase: camelCase(colName), type, isPrimary: colName === 'id', isPassword: colName === 'password', isEmail: colName === 'email', isDateTime: ['created_at', 'updated_at'].includes(colName), isSpecial: ['created_at', 'updated_at', 'deleted_at'].includes(colName), isRequired: col.is_nullable === 'NO', isNullable: col.is_nullable === 'YES', validatorType: mapDBTypeToValidator(col.data_type), needsTrim: mapDBTypeToValidator(col.data_type) === 'string' && colName !== 'email' && colName !== 'password', } }) const sampleFor = (col: EnhancedColumn) => { const t = col.validatorType if (t === 'number') return 1 if (t === 'boolean') return false if (t === 'date') return `'${new Date().toISOString()}'` return `'sample_${col.camelCase}'` } const toTestField = (col: EnhancedColumn): TestField => ({ camelCase: col.camelCase, sampleValue: sampleFor(col), column_name: col.name, }) const createFields = enhancedColumns.filter((c) => !c.isPrimary && !c.isSpecial).map(toTestField) const updateFields = enhancedColumns.filter((c) => !c.isPrimary && !c.isDateTime).map(toTestField) // minimal insert json for show/update/destroy fixtures const insertJson = `{ ${createFields.map((f) => ` ${f.column_name}: ${f.sampleValue}`).join(',\n')} }` const frontendFields = columns.map((col) => ({ name: col.column_name, type: mapDBTypeToFrontend(col.data_type) + (col.is_nullable === 'YES' ? ' | null' : ''), })) return { hasPassword: columns.some((col) => col.column_name === 'password'), enhancedColumns, createFields, updateFields, insertJson, frontendFields, relationships: await buildRelationships(table), } } /** Lucid relationship decorators inferred from the `_id` column convention. */ async function buildRelationships(table: string) { const lines: string[] = [] const imports: Set = new Set() const columns = await getColumnsForTable(table) const references = await getForeignKeysReferencing(table) for (const col of columns) { if (col.column_name.endsWith('_id')) { const refTable = col.column_name.replace('_id', '') const refModel = capitalize(refTable) imports.add('belongsTo') lines.push( ` @belongsTo(() => ${refModel})\n declare ${refTable}: BelongsTo` ) } } for (const ref of references) { const relModel = capitalize(ref.table_name) imports.add('hasMany') lines.push( ` @hasMany(() => ${relModel})\n declare ${ref.table_name}: HasMany` ) } return { imports: Array.from(imports), code: lines, } }