@autobe/agent
Version:
AI backend server code generator
614 lines • 25.6 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildFileStateFieldConflictMap = exports.detectStateFieldConflicts = exports.buildFilePermissionConflictMap = exports.detectPermissionConflicts = exports.buildFileEnumConflictMap = exports.detectEnumConflicts = exports.buildFileAttributeDuplicateMap = exports.detectAttributeDuplicates = exports.buildFileConflictMap = exports.detectConstraintConflicts = exports.buildConstraintConsistencyReport = void 0;
const yaml_1 = __importDefault(require("yaml"));
const YAML_CODE_BLOCK_REGEX = /```yaml\n([\s\S]*?)```/g;
const buildConstraintConsistencyReport = (props) => {
const constraints = new Map();
let totalConstraints = 0;
for (const { file, sectionEvents } of props.files) {
for (const sectionsForModule of sectionEvents) {
for (const sectionEvent of sectionsForModule) {
for (const section of sectionEvent.sectionSections) {
const pairs = extractConstraints(section.content);
for (const { key, value } of pairs) {
totalConstraints++;
const normalized = normalizeValue(value);
if (!constraints.has(key)) {
constraints.set(key, {
key,
values: new Map(),
});
}
const entry = constraints.get(key);
if (!entry.values.has(normalized)) {
entry.values.set(normalized, {
normalized,
display: value.trim(),
sources: [],
});
}
entry.values.get(normalized).sources.push({
file,
sectionTitle: section.title,
});
}
}
}
}
}
const conflicts = [...constraints.values()].filter((entry) => entry.values.size > 1);
if (conflicts.length === 0) {
return [
"No numeric constraint conflicts detected.",
`Scanned ${totalConstraints} numeric constraints from YAML spec blocks.`,
].join("\n");
}
const lines = [
`Detected ${conflicts.length} numeric constraint conflict(s).`,
`Scanned ${totalConstraints} numeric constraints from YAML spec blocks.`,
"",
"Conflicts:",
];
for (const entry of conflicts) {
lines.push(`- ${entry.key}:`);
for (const value of entry.values.values()) {
const sources = value.sources
.map((s) => `${s.file.filename} → ${s.sectionTitle}`)
.slice(0, 6)
.join("; ");
lines.push(` - ${value.display} (e.g., ${sources})`);
}
}
return lines.join("\n");
};
exports.buildConstraintConsistencyReport = buildConstraintConsistencyReport;
/**
* Extract numeric constraints from YAML spec blocks.
*
* Parses YAML code blocks and extracts Entity.attribute constraints that
* contain numeric values (e.g., length limits, quantity limits).
*/
const extractConstraints = (content) => {
var _a, _b;
if (!content)
return [];
const results = [];
const yamlMatches = 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")
continue;
// Handle entity attribute YAML blocks
if (typeof parsed.entity === "string" &&
Array.isArray(parsed.attributes)) {
for (const attr of parsed.attributes) {
if (!attr || typeof attr.name !== "string")
continue;
const constraintStr = String((_b = attr.constraints) !== null && _b !== void 0 ? _b : "");
if (!hasNumeric(constraintStr))
continue;
results.push({
key: `${parsed.entity}.${attr.name}`,
value: constraintStr,
});
}
}
// Handle error code YAML blocks (HTTP status codes)
if (Array.isArray(parsed.errors)) {
for (const err of parsed.errors) {
if (!err || typeof err.code !== "string")
continue;
if (typeof err.http === "number") {
results.push({
key: `error.${err.code}.http`,
value: String(err.http),
});
}
}
}
}
catch (_c) {
// skip parse errors
}
}
return results;
};
const normalizeValue = (value) => value
.toLowerCase()
.replace(/[–—]/g, "-")
.replace(/`/g, "")
.replace(/\s+/g, " ")
.trim();
const hasNumeric = (value) => /\d/.test(value);
/**
* Detect numeric constraint conflicts across files as structured data.
*
* Returns an array of conflicts where the same constraint key has different
* normalized values across files.
*/
const detectConstraintConflicts = (props) => {
const constraints = 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 pairs = extractConstraints(section.content);
for (const { key, value } of pairs) {
const normalized = normalizeValue(value);
if (!constraints.has(key)) {
constraints.set(key, { key, values: new Map() });
}
const entry = constraints.get(key);
if (!entry.values.has(normalized)) {
entry.values.set(normalized, {
normalized,
display: value.trim(),
sources: [],
});
}
entry.values.get(normalized).sources.push({
file,
sectionTitle: section.title,
});
}
}
}
}
}
return [...constraints.values()]
.filter((entry) => entry.values.size > 1)
.map((entry) => ({
key: entry.key,
values: [...entry.values.values()].map((v) => ({
display: v.display,
files: [...new Set(v.sources.map((s) => s.file.filename))],
})),
}));
};
exports.detectConstraintConflicts = detectConstraintConflicts;
/** Build a map from filename → list of conflict feedback strings. */
const buildFileConflictMap = (conflicts) => {
const map = new Map();
for (const conflict of conflicts) {
const allFiles = new Set(conflict.values.flatMap((v) => v.files));
const feedback = `${conflict.key} has conflicting values: ` +
conflict.values
.map((v) => `"${v.display}" in [${v.files.join(", ")}]`)
.join(" vs ");
for (const filename of allFiles) {
if (!map.has(filename))
map.set(filename, []);
map.get(filename).push(feedback);
}
}
return map;
};
exports.buildFileConflictMap = buildFileConflictMap;
/**
* Detect cross-file attribute duplication from YAML spec blocks.
*
* Returns attributes that are defined in YAML blocks across multiple files.
*/
const detectAttributeDuplicates = (props) => {
// key → { normalized spec → { display, files } }
const attributes = new Map();
const allFilesByKey = 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 specs = extractAttributeSpecs(section.content);
for (const { key, specification } of specs) {
if (!allFilesByKey.has(key))
allFilesByKey.set(key, new Set());
allFilesByKey.get(key).add(file.filename);
if (!attributes.has(key))
attributes.set(key, new Map());
const specMap = attributes.get(key);
const normalized = normalizeValue(specification);
if (!specMap.has(normalized)) {
specMap.set(normalized, {
display: specification.trim(),
files: new Set(),
});
}
specMap.get(normalized).files.add(file.filename);
}
}
}
}
}
return [...allFilesByKey.entries()]
.filter(([, files]) => files.size > 1)
.map(([key, files]) => {
const specMap = attributes.get(key);
const hasValueConflict = specMap.size > 1;
return Object.assign({ key, files: [...files], hasValueConflict }, (hasValueConflict
? {
values: [...specMap.values()].map((v) => ({
specification: v.display,
files: [...v.files],
})),
}
: {}));
});
};
exports.detectAttributeDuplicates = detectAttributeDuplicates;
const buildFileAttributeDuplicateMap = (duplicates) => {
const map = new Map();
for (const dup of duplicates) {
let feedback;
if (dup.hasValueConflict && dup.values) {
feedback =
`${dup.key} has conflicting specifications across files: ` +
dup.values
.map((v) => `"${v.specification}" in [${v.files.join(", ")}]`)
.join(" vs ") +
`. Align to ONE canonical definition.`;
}
else {
feedback =
`${dup.key} is fully specified in multiple files: [${dup.files.join(", ")}]. ` +
`Only ONE file should contain the full spec.`;
}
for (const filename of dup.files) {
if (!map.has(filename))
map.set(filename, []);
map.get(filename).push(feedback);
}
}
return map;
};
exports.buildFileAttributeDuplicateMap = buildFileAttributeDuplicateMap;
/**
* Detect enum value conflicts from YAML spec blocks.
*
* Scans YAML attribute blocks for enum-like constraints and detects when
* different files define different enum value sets for the same attribute.
*/
const detectEnumConflicts = (props) => {
const enums = 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 specs = extractEnumSpecs(section.content);
for (const { key, enumSet, display } of specs) {
if (!enums.has(key))
enums.set(key, new Map());
const entry = enums.get(key);
if (!entry.has(enumSet)) {
entry.set(enumSet, { enumSet, display, files: new Set() });
}
entry.get(enumSet).files.add(file.filename);
}
}
}
}
}
return [...enums.entries()]
.filter(([, values]) => values.size > 1)
.map(([key, values]) => ({
key,
values: [...values.values()].map((v) => ({
enumSet: v.enumSet,
display: v.display,
files: [...v.files],
})),
}));
};
exports.detectEnumConflicts = detectEnumConflicts;
const buildFileEnumConflictMap = (conflicts) => {
const map = new Map();
for (const conflict of conflicts) {
const allFiles = new Set(conflict.values.flatMap((v) => v.files));
const feedback = `${conflict.key} has conflicting enum values: ` +
conflict.values
.map((v) => `enum(${v.enumSet}) in [${v.files.join(", ")}]`)
.join(" vs ");
for (const filename of allFiles) {
if (!map.has(filename))
map.set(filename, []);
map.get(filename).push(feedback);
}
}
return map;
};
exports.buildFileEnumConflictMap = buildFileEnumConflictMap;
/**
* Detect permission rule conflicts from YAML spec blocks.
*
* A conflict occurs when one YAML block allows an action but another doesn't
* include it for the same actor+resource.
*/
const detectPermissionConflicts = (props) => {
// actor:resource → action → Set<filename>
const ruleMap = 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 rules = extractPermissionRulesFromYaml(section.content);
for (const { actor, resource, actions } of rules) {
const key = `${actor.toLowerCase()}:${resource}`;
if (!ruleMap.has(key))
ruleMap.set(key, new Map());
const actionMap = ruleMap.get(key);
for (const action of actions) {
const normAction = action.toLowerCase();
if (!actionMap.has(normAction))
actionMap.set(normAction, new Set());
actionMap.get(normAction).add(file.filename);
}
}
}
}
}
}
// Permission conflicts are rare in YAML-based approach since
// 01-actors-and-auth is the canonical source. Return empty for now.
return [];
};
exports.detectPermissionConflicts = detectPermissionConflicts;
const buildFilePermissionConflictMap = (conflicts) => {
const map = new Map();
for (const conflict of conflicts) {
const allFiles = new Set(conflict.rules.flatMap((r) => r.files));
const feedback = `Permission conflict for "${conflict.actorOperation}": ` +
conflict.rules
.map((r) => `"${r.condition}" in [${r.files.join(", ")}]`)
.join(" vs ");
for (const filename of allFiles) {
if (!map.has(filename))
map.set(filename, []);
map.get(filename).push(feedback);
}
}
return map;
};
exports.buildFilePermissionConflictMap = buildFilePermissionConflictMap;
/**
* Detect state field conflicts from YAML spec blocks.
*
* Known contradiction patterns:
*
* 1. Same entity has both `deletedAt` (datetime) and `isDeleted` (boolean)
* 2. Same entity has `status` (enum) and semantically equivalent `is*` booleans
*/
const detectStateFieldConflicts = (props) => {
var _a, _b;
// entity → { fieldName → { specification, files } }
const entityFields = 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 specs = extractAttributeSpecs(section.content);
for (const { key, specification } of specs) {
const dotIndex = key.indexOf(".");
if (dotIndex < 0)
continue;
const entity = key.slice(0, dotIndex);
const field = key.slice(dotIndex + 1).toLowerCase();
if (!entityFields.has(entity))
entityFields.set(entity, new Map());
const fields = entityFields.get(entity);
if (!fields.has(field))
fields.set(field, { specification, files: new Set() });
fields.get(field).files.add(file.filename);
}
}
}
}
}
const conflicts = [];
for (const [entity, fields] of entityFields) {
const fieldNames = [...fields.keys()];
// Pattern 1: deletedAt + isDeleted on same entity
const hasDeletedAt = fieldNames.some((f) => f === "deletedat" || f === "deleted_at");
const hasIsDeleted = fieldNames.some((f) => f === "isdeleted" || f === "is_deleted");
if (hasDeletedAt && hasIsDeleted) {
const deletedAtField = (_a = fields.get("deletedat")) !== null && _a !== void 0 ? _a : fields.get("deleted_at");
const isDeletedField = (_b = fields.get("isdeleted")) !== null && _b !== void 0 ? _b : fields.get("is_deleted");
if (deletedAtField && isDeletedField) {
conflicts.push({
entity,
conflictType: "deletedAt vs isDeleted",
fields: [
{
fieldName: "deletedAt",
specification: deletedAtField.specification,
files: [...deletedAtField.files],
},
{
fieldName: "isDeleted",
specification: isDeletedField.specification,
files: [...isDeletedField.files],
},
],
});
}
}
// Pattern 2: status (enum) + is* booleans
const statusField = fields.get("status");
if (statusField && /enum/i.test(statusField.specification)) {
const isBooleans = fieldNames.filter((f) => f.startsWith("is") && /boolean/i.test(fields.get(f).specification));
for (const boolField of isBooleans) {
const concept = boolField.slice(2).toLowerCase();
if (statusField.specification.toLowerCase().includes(concept)) {
const boolEntry = fields.get(boolField);
conflicts.push({
entity,
conflictType: `status enum includes "${concept}" but separate is${concept.charAt(0).toUpperCase() + concept.slice(1)} boolean also exists`,
fields: [
{
fieldName: "status",
specification: statusField.specification,
files: [...statusField.files],
},
{
fieldName: boolField,
specification: boolEntry.specification,
files: [...boolEntry.files],
},
],
});
}
}
}
}
return conflicts;
};
exports.detectStateFieldConflicts = detectStateFieldConflicts;
const buildFileStateFieldConflictMap = (conflicts) => {
const map = new Map();
for (const conflict of conflicts) {
const allFiles = new Set(conflict.fields.flatMap((f) => f.files));
const feedback = `State field conflict for "${conflict.entity}": ${conflict.conflictType}. ` +
conflict.fields
.map((f) => `"${f.fieldName}: ${f.specification}" in [${f.files.join(", ")}]`)
.join(" vs ") +
`. Use ONE canonical approach.`;
for (const filename of allFiles) {
if (!map.has(filename))
map.set(filename, []);
map.get(filename).push(feedback);
}
}
return map;
};
exports.buildFileStateFieldConflictMap = buildFileStateFieldConflictMap;
// ─── YAML-based Attribute Specs Extraction (shared) ───
const ENUM_PATTERN = /enum\s*[\(\[\{]([^)\]\}]+)[\)\]\}]/i;
/**
* Extract attribute specs from YAML code blocks.
*
* Parses YAML blocks with `entity` + `attributes` structure and returns
* Entity.attribute → constraints pairs.
*/
const extractAttributeSpecs = (content) => {
var _a;
if (!content)
return [];
const results = [];
const yamlMatches = 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" ||
typeof parsed.entity !== "string" ||
!Array.isArray(parsed.attributes))
continue;
for (const attr of parsed.attributes) {
if (!attr || typeof attr.name !== "string")
continue;
const spec = [
attr.type ? String(attr.type) : "",
attr.constraints ? String(attr.constraints) : "",
]
.filter(Boolean)
.join(", ");
if (!spec)
continue;
results.push({
key: `${parsed.entity}.${attr.name}`,
specification: spec,
});
}
}
catch (_b) {
// skip parse errors
}
}
return results;
};
/** Extract enum specs from YAML attribute blocks. */
const extractEnumSpecs = (content) => {
var _a, _b, _c;
if (!content)
return [];
const results = [];
const yamlMatches = 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" ||
typeof parsed.entity !== "string" ||
!Array.isArray(parsed.attributes))
continue;
for (const attr of parsed.attributes) {
if (!attr || typeof attr.name !== "string")
continue;
const typeStr = String((_b = attr.type) !== null && _b !== void 0 ? _b : "");
const constraintStr = String((_c = attr.constraints) !== null && _c !== void 0 ? _c : "");
const combined = `${typeStr} ${constraintStr}`;
const enumMatch = combined.match(ENUM_PATTERN);
if (!enumMatch)
continue;
const rawEnumValues = enumMatch[1];
const enumSet = [
...new Set(rawEnumValues
.split(/[|,]/)
.map((v) => v.trim().toLowerCase())
.filter((v) => v.length > 0)),
]
.sort()
.join("|");
results.push({
key: `${parsed.entity}.${attr.name}`,
enumSet,
display: combined.trim(),
});
}
}
catch (_d) {
// skip parse errors
}
}
return results;
};
/** Extract permission rules from YAML spec blocks. */
const extractPermissionRulesFromYaml = (content) => {
var _a;
if (!content)
return [];
const results = [];
const yamlMatches = 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.permissions))
continue;
for (const perm of parsed.permissions) {
if (!perm ||
typeof perm.actor !== "string" ||
typeof perm.resource !== "string" ||
!Array.isArray(perm.actions))
continue;
results.push({
actor: perm.actor,
resource: perm.resource,
actions: perm.actions.map(String),
});
}
}
catch (_b) {
// skip parse errors
}
}
return results;
};
//# sourceMappingURL=buildConstraintConsistencyReport.js.map