UNPKG

@autobe/agent

Version:

AI backend server code generator

330 lines (323 loc) 13.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AutoBeRealizeCollectorProgrammer = void 0; const utils_1 = require("@autobe/utils"); const utils_2 = require("@typia/utils"); const typia_1 = __importDefault(require("typia")); var AutoBeRealizeCollectorProgrammer; (function (AutoBeRealizeCollectorProgrammer) { function filter(key) { return key.endsWith(".ICreate"); } AutoBeRealizeCollectorProgrammer.filter = filter; function getName(dtoTypeName) { const replaced = dtoTypeName.replace(".ICreate", ""); const entity = replaced.startsWith("I") ? replaced.substring(1) : replaced; return `${entity}Collector`; } AutoBeRealizeCollectorProgrammer.getName = getName; function getNeighbors(code) { const unique = new Set(); const regex = /(\w+Collector)\.collect/g; while (true) { const match = regex.exec(code); if (match === null) break; unique.add(match[1]); } return Array.from(unique); } AutoBeRealizeCollectorProgrammer.getNeighbors = getNeighbors; function getMappingMetadata(props) { return [ { member: props.model.primaryField.name, kind: "scalar", nullable: false, }, ...props.model.plainFields.map((pf) => ({ member: pf.name, kind: "scalar", nullable: pf.nullable, })), ...props.model.foreignFields.map((f) => ({ member: f.relation.name, kind: "belongsTo", nullable: f.nullable, })), ...props.application.files .map((f) => f.models) .flat() .map((om) => om.foreignFields .filter((fk) => fk.relation.targetModel === props.model.name) .map((fk) => ({ member: fk.relation.oppositeName, kind: fk.unique ? "hasOne" : "hasMany", nullable: null, }))) .flat(), ]; } AutoBeRealizeCollectorProgrammer.getMappingMetadata = getMappingMetadata; function writeTemplate(props) { const mappings = getMappingMetadata(props).map((r) => r.member); return utils_1.StringUtil.trim ` export namespace ${getName(props.plan.dtoTypeName)} { export async function collect(props: { body: ${props.plan.dtoTypeName}; ${ //references props.plan.references .map((r) => `${utils_2.NamingConvention.camel(r.databaseSchemaName)}: IEntity; // ${r.source}`) .join("\n")} ${ // ip utils_1.AutoBeOpenApiTypeChecker.isObject(props.body) && props.body.properties.ip !== undefined && props.model.plainFields.some((f) => f.name === "ip") ? `ip: string;` : ""} ${ // sequence utils_1.AutoBeOpenApiTypeChecker.isObject(props.body) && props.body.properties.sequence !== undefined && utils_1.AutoBeOpenApiTypeChecker.isString(props.body.properties.sequence) && props.model.plainFields.some((f) => f.name === "sequence" && f.type === "int") ? `sequence: number;` : ""} }) { return { ${mappings.map((r) => ` ${r}: ...,`).join("\n")} } satisfies Prisma.${props.plan.databaseSchemaName}CreateInput; } } `; } AutoBeRealizeCollectorProgrammer.writeTemplate = writeTemplate; function writeStructures(ctx, dtoTypeName) { return __awaiter(this, void 0, void 0, function* () { const document = ctx.state().interface.document; const components = { authorizations: [], schemas: {}, }; utils_2.OpenApiTypeChecker.visit({ components: document.components, schema: { $ref: `#/components/schemas/${dtoTypeName}` }, closure: (s) => { if (utils_2.OpenApiTypeChecker.isReference(s)) { const key = s.$ref.split("/").pop(); components.schemas[key] = document.components.schemas[key]; } }, }); const compiler = yield ctx.compiler(); const entries = Object.entries(yield compiler.interface.write({ components, operations: [], }, [])); return Object.fromEntries(entries.filter(([key]) => key.startsWith("src/api/structures"))); }); } AutoBeRealizeCollectorProgrammer.writeStructures = writeStructures; function replaceImportStatements(ctx, props) { return __awaiter(this, void 0, void 0, function* () { const compiler = yield ctx.compiler(); let code = yield compiler.typescript.removeImportStatements(props.code); const imports = writeImportStatements(props); const selfName = getName(props.dtoTypeName); code = [ ...imports, "", ...getNeighbors(code) .filter((trs) => trs !== selfName) .map((trs) => `import { ${trs} } from "./${trs}";`), "", code, ].join("\n"); return yield compiler.typescript.beautify(code); }); } AutoBeRealizeCollectorProgrammer.replaceImportStatements = replaceImportStatements; function writeImportStatements(props) { const typeReferences = new Set(); const visit = (key) => utils_2.OpenApiTypeChecker.visit({ schema: { $ref: `#/components/schemas/${key}`, }, components: { schemas: props.schemas }, closure: (next) => { if (utils_2.OpenApiTypeChecker.isReference(next)) typeReferences.add(next.$ref.split("/").pop().split(".")[0]); }, }); visit(props.dtoTypeName); const imports = [ `import { Prisma } from "@prisma/sdk";`, `import { ArrayUtil } from "@nestia/e2e";`, `import { v4 } from "uuid";`, "", `import { IEntity } from "@ORGANIZATION/PROJECT-api/lib/structures/IEntity";`, ...Array.from(typeReferences).map((ref) => `import { ${ref} } from "@ORGANIZATION/PROJECT-api/lib/structures/${ref}";`), "", `import { MyGlobal } from "../MyGlobal";`, `import { PasswordUtil } from "../utils/PasswordUtil";`, ]; return imports; } function validate(props) { const errors = []; validateMappings({ application: props.application, errors, plan: props.plan, mappings: props.mappings, }); validateEmptyCode({ plan: props.plan, content: props.draft, path: "$input.request.draft", errors, }); validateNeighbors({ plan: props.plan, neighbors: props.neighbors, content: props.draft, path: "$input.request.draft", errors, }); if (props.revise.final !== null) { validateEmptyCode({ plan: props.plan, content: props.revise.final, path: "$input.request.revise.final", errors, }); validateNeighbors({ plan: props.plan, neighbors: props.neighbors, content: props.revise.final, path: "$input.request.revise.final", errors, }); } return errors; } AutoBeRealizeCollectorProgrammer.validate = validate; function validateMappings(props) { const model = props.application.files .map((f) => f.models) .flat() .find((m) => m.name === props.plan.databaseSchemaName); const required = getMappingMetadata({ application: props.application, model, }); props.mappings.forEach((m, i) => { const metadata = required.find((r) => r.member === m.member); if (metadata === undefined) props.errors.push({ path: `$input.request.mappings[${i}].member`, value: m.member, expected: required.map((r) => JSON.stringify(r)).join(" | "), description: utils_1.StringUtil.trim ` '${m.member}' is not a valid Prisma member. Please provide mapping only for existing Prisma members: ${required.map((r) => `- ${r.member}`).join("\n")} `, }); else { if (metadata.kind !== m.kind) props.errors.push({ path: `$input.request.mappings[${i}].kind`, value: m.kind, expected: `"${metadata.kind}"`, description: utils_1.StringUtil.trim ` The mapping kind for Prisma member '${m.member}' is invalid. Expected kind is '${metadata.kind}', but received kind is '${m.kind}'. `, }); if (metadata.nullable !== m.nullable) props.errors.push({ path: `$input.request.mappings[${i}].nullable`, value: m.nullable, expected: `${metadata.nullable}`, description: utils_1.StringUtil.trim ` The mapping nullable for Prisma member '${m.member}' is invalid. Expected nullable is '${metadata.nullable}', but received nullable is '${m.nullable}'. `, }); } }); for (const r of required) { if (props.mappings.some((m) => m.member === r.member)) continue; props.errors.push({ path: "$input.request.mappings[]", value: undefined, expected: utils_1.StringUtil.trim `{ member: "${r.member}"; kind: "${r.kind}"; how: string; }`, description: utils_1.StringUtil.trim ` You missed mapping for required Prisma member '${r.member}'. Make sure to provide mapping for all required members. `, }); } } function validateEmptyCode(props) { const name = getName(props.plan.dtoTypeName); if (props.content.includes(`export namespace ${name}`) === false) props.errors.push({ path: props.path, expected: `Namespace '${name}' to be present in the code.`, value: props.content, description: `The generated code does not contain the expected namespace '${name}'.`, }); } function validateNeighbors(props) { const selfName = getName(props.plan.dtoTypeName); const neighborNames = getNeighbors(props.content); for (const x of neighborNames) if (x !== selfName && props.neighbors.some((y) => getName(y.dtoTypeName) === x) === false) props.errors.push({ path: props.path, expected: `Use existing transformer.`, value: props.content, description: utils_1.StringUtil.trim ` You've imported and utilized ${x}, but it does not exist. Use one of them below, or change to another code: ${props.neighbors .map((y) => `- ${getName(y.dtoTypeName)}`) .join("\n")} `, }); } AutoBeRealizeCollectorProgrammer.fixApplication = (props) => { const mapping = props.definition.functions[0].parameters.$defs["AutoBeRealizeCollectorMapping"]; if (mapping === undefined || utils_2.LlmTypeChecker.isObject(mapping) === false) throw new Error("AutoBeRealizeCollectorMapping type is not defined in the function calling schema."); const member = mapping.properties.member; if (member === undefined || utils_2.LlmTypeChecker.isString(member) === false) throw new Error("AutoBeRealizeCollectorMapping.member is not defined or string type."); member.enum = getMappingMetadata(props).map((m) => m.member); }; })(AutoBeRealizeCollectorProgrammer || (exports.AutoBeRealizeCollectorProgrammer = AutoBeRealizeCollectorProgrammer = {})); //# sourceMappingURL=AutoBeRealizeCollectorProgrammer.js.map