@autobe/agent
Version:
AI backend server code generator
200 lines • 8.74 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildFileErrorCodeConflictMap = exports.detectErrorCodeConflicts = exports.validateErrorCodes = void 0;
const yaml_1 = __importDefault(require("yaml"));
// ─── YAML Block Extraction ───
const YAML_CODE_BLOCK_REGEX = /```yaml\n([\s\S]*?)```/g;
/**
* Extract canonical error codes from 04-business-rules YAML spec blocks.
*
* Expects YAML blocks with structure:
*
* ```yaml
* errors:
* - code: TODO_NOT_FOUND
* http: 404
* condition: "requested todo does not exist"
* ```
*/
const extractCanonicalErrorCodes = (fileIndex, sectionEvents) => {
var _a, _b;
const entries = [];
const errors = [];
for (const sectionsForModule of sectionEvents) {
for (const sectionEvent of sectionsForModule) {
for (const section of sectionEvent.sectionSections) {
const yamlMatches = section.content.matchAll(YAML_CODE_BLOCK_REGEX);
for (const match of yamlMatches) {
const yamlContent = (_a = match[1]) !== null && _a !== void 0 ? _a : "";
try {
const parsed = yaml_1.default.parse(yamlContent);
if (parsed &&
typeof parsed === "object" &&
Array.isArray(parsed.errors)) {
for (const err of parsed.errors) {
if (err && typeof err.code === "string") {
entries.push({
code: err.code,
http: typeof err.http === "number" ? err.http : 400,
condition: String((_b = err.condition) !== null && _b !== void 0 ? _b : ""),
});
}
}
}
}
catch (e) {
errors.push({
fileIndex,
sectionTitle: section.title,
error: `YAML parse error: ${e instanceof Error ? e.message : String(e)}`,
});
}
}
}
}
}
return { entries, errors };
};
// ─── Backtick Reference Extraction ───
/** Match backtick `UPPER_SNAKE_CASE` patterns (error codes) */
const BACKTICK_ERROR_CODE_REGEX = /`([A-Z][A-Z0-9_]+)`/g;
/** Extract backtick `ERROR_CODE` references from section content. */
const extractBacktickErrorCodeReferences = (fileIndex, sectionEvents) => {
const refs = [];
for (const sectionsForModule of sectionEvents) {
for (const sectionEvent of sectionsForModule) {
for (const section of sectionEvent.sectionSections) {
const matches = section.content.matchAll(BACKTICK_ERROR_CODE_REGEX);
for (const match of matches) {
// Skip common non-error-code patterns (HTTP methods, etc.)
const code = match[1];
if (code === "GET" ||
code === "POST" ||
code === "PUT" ||
code === "PATCH" ||
code === "DELETE" ||
code === "HEAD" ||
code === "OPTIONS")
continue;
refs.push({
code,
fileIndex,
sectionTitle: section.title,
});
}
}
}
}
return refs;
};
// ─── Main Validation Function ───
/**
* Validate error code references across files using YAML canonical definitions.
*
* 1. Extracts canonical error codes from 04-business-rules YAML blocks
* 2. Extracts backtick `ERROR_CODE` references from 03-functional-requirements
* 3. Reports undefined references
*/
const validateErrorCodes = (props) => {
let canonical = [];
const parseErrors = [];
// Extract canonical from 04-business-rules
const businessRulesIndex = props.files.findIndex((f) => f.file.filename === "04-business-rules.md");
if (businessRulesIndex >= 0) {
const result = extractCanonicalErrorCodes(businessRulesIndex, props.files[businessRulesIndex].sectionEvents);
canonical = result.entries;
parseErrors.push(...result.errors);
}
// Build canonical lookup set
const canonicalSet = new Set(canonical.map((e) => e.code));
// Extract references from 03-functional-requirements
const references = [];
for (let i = 0; i < props.files.length; i++) {
const filename = props.files[i].file.filename;
if (filename === "03-functional-requirements.md") {
references.push(...extractBacktickErrorCodeReferences(i, props.files[i].sectionEvents));
}
}
const undefinedReferences = references.filter((ref) => !canonicalSet.has(ref.code));
return { canonical, references, undefinedReferences, parseErrors };
};
exports.validateErrorCodes = validateErrorCodes;
/**
* Detect error code conflicts across files.
*
* Now operates on YAML-extracted data. A conflict occurs when the same error
* code appears with different HTTP statuses across YAML blocks.
*/
const detectErrorCodeConflicts = (props) => {
var _a;
// code → { http → Set<filename> }
const codeMap = new Map();
for (const { file, sectionEvents } of props.files) {
for (const sectionsForModule of sectionEvents) {
for (const sectionEvent of sectionsForModule) {
for (const section of sectionEvent.sectionSections) {
const yamlMatches = section.content.matchAll(YAML_CODE_BLOCK_REGEX);
for (const match of yamlMatches) {
const yamlContent = (_a = match[1]) !== null && _a !== void 0 ? _a : "";
try {
const parsed = yaml_1.default.parse(yamlContent);
if (parsed &&
typeof parsed === "object" &&
Array.isArray(parsed.errors)) {
for (const err of parsed.errors) {
if (!err || typeof err.code !== "string")
continue;
const code = err.code;
const http = typeof err.http === "number" ? err.http : 400;
if (!codeMap.has(code))
codeMap.set(code, new Map());
const httpMap = codeMap.get(code);
if (!httpMap.has(http))
httpMap.set(http, new Set());
httpMap.get(http).add(file.filename);
}
}
}
catch (_b) {
// skip parse errors
}
}
}
}
}
}
return [...codeMap.entries()]
.filter(([, httpMap]) => httpMap.size > 1)
.map(([code, httpMap]) => ({
conditionKey: code,
codes: [...httpMap.entries()].map(([http, files]) => ({
errorCode: code,
httpStatus: http,
files: [...files],
})),
}));
};
exports.detectErrorCodeConflicts = detectErrorCodeConflicts;
/** Build a map from filename → list of error code conflict feedback strings. */
const buildFileErrorCodeConflictMap = (conflicts) => {
const map = new Map();
for (const conflict of conflicts) {
const allFiles = new Set(conflict.codes.flatMap((c) => c.files));
const feedback = `Error code conflict for "${conflict.conditionKey}": ` +
conflict.codes
.map((c) => `HTTP ${c.httpStatus} in [${c.files.join(", ")}]`)
.join(" vs ") +
`. Use ONE canonical HTTP status.`;
for (const filename of allFiles) {
if (!map.has(filename))
map.set(filename, []);
map.get(filename).push(feedback);
}
}
return map;
};
exports.buildFileErrorCodeConflictMap = buildFileErrorCodeConflictMap;
//# sourceMappingURL=buildErrorCodeRegistry.js.map