UNPKG

@autobe/agent

Version:

AI backend server code generator

165 lines (152 loc) 5.38 kB
import { AutoBeDatabase, AutoBeInterfaceSchemaDesign, AutoBeOpenApi, } from "@autobe/interface"; import { StringUtil } from "@autobe/utils"; import { LlmTypeChecker, NamingConvention } from "@typia/utils"; import { plural } from "pluralize"; import { ILlmApplication, ILlmFunction, ILlmSchema, IValidation } from "typia"; import { AutoBeDatabaseModelProgrammer } from "../../database/programmers/AutoBeDatabaseModelProgrammer"; import { AutoBeJsonSchemaValidator } from "../utils/AutoBeJsonSchemaValidator"; export namespace AutoBeInterfaceSchemaProgrammer { export interface IDatabaseSchemaMember { key: string; nullable: boolean; /** * Database column type for type compatibility validation. * * - `null` means this member is a relation (object/array), not a column, so * primitive type checking should be skipped. */ type: AutoBeDatabase.IPlainField["type"] | null; } export const getDatabaseSchemaName = (typeName: string): string => plural(NamingConvention.snake(typeName.split(".")[0]!.substring(1))); export const getNeighborDatabaseSchemas = (props: { typeName: string; application: AutoBeDatabase.IApplication; }): AutoBeDatabase.IModel[] | undefined => { const expected: string = getDatabaseSchemaName(props.typeName); const found: AutoBeDatabase.IModel | undefined = props.application.files .flatMap((f) => f.models) .find((m) => m.name === expected); if (found === undefined) return; return AutoBeDatabaseModelProgrammer.getNeighbors({ application: props.application, model: found, }); }; export const getDatabaseSchemaProperties = (props: { everyModels: AutoBeDatabase.IModel[]; model: AutoBeDatabase.IModel; }): IDatabaseSchemaMember[] => [ // Primary key { key: props.model.primaryField.name, nullable: false, type: "uuid", }, // Plain fields (columns like title, description, created_at, etc.) ...props.model.plainFields.map((f) => ({ key: f.name, nullable: f.nullable, type: f.type, })), // Foreign key columns (e.g., todo_app_user_id) ...props.model.foreignFields.map((f) => ({ key: f.name, nullable: f.nullable, type: "uuid" as const, })), // Foreign key relation names (e.g., user) — relations, not columns ...props.model.foreignFields.map((f) => ({ key: f.relation.name, nullable: f.nullable, type: null, })), // Opposite relations from other models — relations, not columns ...props.everyModels .map((m) => m.foreignFields .filter((f) => f.relation.targetModel === props.model.name) .map((f) => ({ key: f.relation.oppositeName, nullable: f.unique, type: null, })), ) .flat(), ]; export const validate = (props: { // common errors: IValidation.IError[]; path: string; everyModels: AutoBeDatabase.IModel[]; operations: AutoBeOpenApi.IOperation[]; // specific typeName: string; design: AutoBeInterfaceSchemaDesign; }): void => { AutoBeJsonSchemaValidator.validateSchema({ errors: props.errors, operations: props.operations, typeName: props.typeName, schema: props.design.schema, path: `${props.path}.schema`, }); if ( props.design.databaseSchema !== null && props.everyModels.some((m) => m.name === props.design.databaseSchema) === false ) props.errors.push({ path: `${props.path}.databaseSchema`, expected: props.everyModels .map((m) => JSON.stringify(m.name)) .join(" | "), value: props.design.databaseSchema, description: StringUtil.trim` You set "databaseSchema" to ${JSON.stringify(props.design.databaseSchema)}, but no database model with that name exists. Available database models are: ${props.everyModels.map((m) => `- ${m.name}`).join("\n")} Fix: Use one of the available model names above, or set to null if this type does not map to a single database table (e.g., computed/aggregated types, types composed purely by business logic, or embedded JSON structures). `, }); }; export const fixApplication = (props: { application: ILlmApplication; everyModels: AutoBeDatabase.IModel[]; }): void => { const func: ILlmFunction = props.application.functions[0]; fixDatabaseSchema({ $defs: func.parameters.$defs, parameters: func.parameters, everyModels: props.everyModels, }); }; const fixDatabaseSchema = (props: { $defs: Record<string, ILlmSchema>; parameters: ILlmSchema.IParameters; everyModels: AutoBeDatabase.IModel[]; }): void => { LlmTypeChecker.visit({ $defs: props.$defs, schema: props.parameters, closure: (next) => { if (LlmTypeChecker.isObject(next) === false) return; const member: ILlmSchema | undefined = next.properties.databaseSchema; if (member === undefined || LlmTypeChecker.isAnyOf(member) === false) return; const value: ILlmSchema | undefined = member.anyOf.find((x) => LlmTypeChecker.isString(x), ); if (value === undefined) return; }, }); }; }