@samiyev/guardian
Version:
Research-backed code quality guardian for AI-assisted development. Detects hardcodes, secrets, circular deps, framework leaks, entity exposure, and 9 architecture violations. Enforces Clean Architecture/DDD principles. Works with GitHub Copilot, Cursor, W
196 lines ⢠8.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.OutputFormatter = void 0;
const constants_1 = require("../../shared/constants");
const constants_2 = require("../constants");
const ViolationGrouper_1 = require("../groupers/ViolationGrouper");
const SEVERITY_LABELS = {
[constants_1.SEVERITY_LEVELS.CRITICAL]: constants_2.SEVERITY_DISPLAY_LABELS.CRITICAL,
[constants_1.SEVERITY_LEVELS.HIGH]: constants_2.SEVERITY_DISPLAY_LABELS.HIGH,
[constants_1.SEVERITY_LEVELS.MEDIUM]: constants_2.SEVERITY_DISPLAY_LABELS.MEDIUM,
[constants_1.SEVERITY_LEVELS.LOW]: constants_2.SEVERITY_DISPLAY_LABELS.LOW,
};
const SEVERITY_HEADER = {
[constants_1.SEVERITY_LEVELS.CRITICAL]: constants_2.SEVERITY_SECTION_HEADERS.CRITICAL,
[constants_1.SEVERITY_LEVELS.HIGH]: constants_2.SEVERITY_SECTION_HEADERS.HIGH,
[constants_1.SEVERITY_LEVELS.MEDIUM]: constants_2.SEVERITY_SECTION_HEADERS.MEDIUM,
[constants_1.SEVERITY_LEVELS.LOW]: constants_2.SEVERITY_SECTION_HEADERS.LOW,
};
class OutputFormatter {
grouper = new ViolationGrouper_1.ViolationGrouper();
displayGroupedViolations(violations, displayFn, limit) {
const grouped = this.grouper.groupBySeverity(violations);
const severities = [
constants_1.SEVERITY_LEVELS.CRITICAL,
constants_1.SEVERITY_LEVELS.HIGH,
constants_1.SEVERITY_LEVELS.MEDIUM,
constants_1.SEVERITY_LEVELS.LOW,
];
let totalDisplayed = 0;
const totalAvailable = violations.length;
for (const severity of severities) {
const items = grouped.get(severity);
if (items && items.length > 0) {
console.warn(SEVERITY_HEADER[severity]);
console.warn(`Found ${String(items.length)} issue(s)\n`);
const itemsToDisplay = limit !== undefined ? items.slice(0, limit - totalDisplayed) : items;
itemsToDisplay.forEach((item, index) => {
displayFn(item, totalDisplayed + index);
});
totalDisplayed += itemsToDisplay.length;
if (limit !== undefined && totalDisplayed >= limit) {
break;
}
}
}
if (limit !== undefined && totalAvailable > limit) {
console.warn(`\nā ļø Showing first ${String(limit)} of ${String(totalAvailable)} issues (use --limit to adjust)\n`);
}
}
formatArchitectureViolation(v, index) {
console.log(`${String(index + 1)}. ${v.file}`);
console.log(` Severity: ${SEVERITY_LABELS[v.severity]}`);
console.log(` Rule: ${v.rule}`);
console.log(` ${v.message}`);
console.log("");
}
formatCircularDependency(cd, index) {
console.log(`${String(index + 1)}. ${cd.message}`);
console.log(` Severity: ${SEVERITY_LABELS[cd.severity]}`);
console.log(" Cycle path:");
cd.cycle.forEach((file, i) => {
console.log(` ${String(i + 1)}. ${file}`);
});
console.log(` ${String(cd.cycle.length + 1)}. ${cd.cycle[0]} (back to start)`);
console.log("");
}
formatNamingViolation(nc, index) {
console.log(`${String(index + 1)}. ${nc.file}`);
console.log(` Severity: ${SEVERITY_LABELS[nc.severity]}`);
console.log(` File: ${nc.fileName}`);
console.log(` Layer: ${nc.layer}`);
console.log(` Type: ${nc.type}`);
console.log(` Message: ${nc.message}`);
if (nc.suggestion) {
console.log(` š” Suggestion: ${nc.suggestion}`);
}
console.log("");
}
formatFrameworkLeak(fl, index) {
console.log(`${String(index + 1)}. ${fl.file}`);
console.log(` Severity: ${SEVERITY_LABELS[fl.severity]}`);
console.log(` Package: ${fl.packageName}`);
console.log(` Category: ${fl.categoryDescription}`);
console.log(` Layer: ${fl.layer}`);
console.log(` Rule: ${fl.rule}`);
console.log(` ${fl.message}`);
console.log(` š” Suggestion: ${fl.suggestion}`);
console.log("");
}
formatEntityExposure(ee, index) {
const location = ee.line ? `${ee.file}:${String(ee.line)}` : ee.file;
console.log(`${String(index + 1)}. ${location}`);
console.log(` Severity: ${SEVERITY_LABELS[ee.severity]}`);
console.log(` Entity: ${ee.entityName}`);
console.log(` Return Type: ${ee.returnType}`);
if (ee.methodName) {
console.log(` Method: ${ee.methodName}`);
}
console.log(` Layer: ${ee.layer}`);
console.log(` Rule: ${ee.rule}`);
console.log(` ${ee.message}`);
console.log(" š” Suggestion:");
ee.suggestion.split("\n").forEach((line) => {
if (line.trim()) {
console.log(` ${line}`);
}
});
console.log("");
}
formatDependencyDirection(dd, index) {
console.log(`${String(index + 1)}. ${dd.file}`);
console.log(` Severity: ${SEVERITY_LABELS[dd.severity]}`);
console.log(` From Layer: ${dd.fromLayer}`);
console.log(` To Layer: ${dd.toLayer}`);
console.log(` Import: ${dd.importPath}`);
console.log(` ${dd.message}`);
console.log(` š” Suggestion: ${dd.suggestion}`);
console.log("");
}
formatRepositoryPattern(rp, index) {
console.log(`${String(index + 1)}. ${rp.file}`);
console.log(` Severity: ${SEVERITY_LABELS[rp.severity]}`);
console.log(` Layer: ${rp.layer}`);
console.log(` Type: ${rp.violationType}`);
console.log(` Details: ${rp.details}`);
console.log(` ${rp.message}`);
console.log(` š” Suggestion: ${rp.suggestion}`);
console.log("");
}
formatAggregateBoundary(ab, index) {
const location = ab.line ? `${ab.file}:${String(ab.line)}` : ab.file;
console.log(`${String(index + 1)}. ${location}`);
console.log(` Severity: ${SEVERITY_LABELS[ab.severity]}`);
console.log(` From Aggregate: ${ab.fromAggregate}`);
console.log(` To Aggregate: ${ab.toAggregate}`);
console.log(` Entity: ${ab.entityName}`);
console.log(` Import: ${ab.importPath}`);
console.log(` ${ab.message}`);
console.log(" š” Suggestion:");
ab.suggestion.split("\n").forEach((line) => {
if (line.trim()) {
console.log(` ${line}`);
}
});
console.log("");
}
formatSecretViolation(sv, index) {
const location = `${sv.file}:${String(sv.line)}:${String(sv.column)}`;
console.log(`${String(index + 1)}. ${location}`);
console.log(` Severity: ${SEVERITY_LABELS[sv.severity]} ā ļø`);
console.log(` Secret Type: ${sv.secretType}`);
console.log(` ${sv.message}`);
console.log(" š CRITICAL: Rotate this secret immediately!");
console.log(" š” Suggestion:");
sv.suggestion.split("\n").forEach((line) => {
if (line.trim()) {
console.log(` ${line}`);
}
});
console.log("");
}
formatHardcodeViolation(hc, index) {
console.log(`${String(index + 1)}. ${hc.file}:${String(hc.line)}:${String(hc.column)}`);
console.log(` Severity: ${SEVERITY_LABELS[hc.severity]}`);
console.log(` Type: ${hc.type}`);
console.log(` Value: ${JSON.stringify(hc.value)}`);
console.log(` Context: ${hc.context.trim()}`);
console.log(` š” Suggested: ${hc.suggestion.constantName}`);
console.log(` š Location: ${hc.suggestion.location}`);
console.log("");
}
formatAnemicModelViolation(am, index) {
const location = am.line ? `${am.file}:${String(am.line)}` : am.file;
console.log(`${String(index + 1)}. ${location}`);
console.log(` Severity: ${SEVERITY_LABELS[am.severity]}`);
console.log(` Class: ${am.className}`);
console.log(` Layer: ${am.layer}`);
console.log(` Methods: ${String(am.methodCount)} | Properties: ${String(am.propertyCount)}`);
if (am.hasPublicSetters) {
console.log(" ā ļø Has public setters (DDD anti-pattern)");
}
if (am.hasOnlyGettersSetters) {
console.log(" ā ļø Only getters/setters (no business logic)");
}
console.log(` ${am.message}`);
console.log(" š” Suggestion:");
am.suggestion.split("\n").forEach((line) => {
if (line.trim()) {
console.log(` ${line}`);
}
});
console.log("");
}
}
exports.OutputFormatter = OutputFormatter;
//# sourceMappingURL=OutputFormatter.js.map