@autobe/agent
Version:
AI backend server code generator
330 lines (323 loc) • 13.7 kB
JavaScript
;
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