UNPKG

@autobe/agent

Version:

AI backend server code generator

200 lines 8.74 kB
"use strict"; 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