@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
140 lines • 7.17 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RepositoryViolationDetector = void 0;
const RepositoryViolation_1 = require("../../domain/value-objects/RepositoryViolation");
const rules_1 = require("../../shared/constants/rules");
const Messages_1 = require("../../domain/constants/Messages");
/**
* Detects specific repository pattern violations
*
* Handles detection of ORM types, non-domain methods, concrete repositories,
* and repository instantiation violations.
*/
class RepositoryViolationDetector {
ormMatcher;
methodValidator;
constructor(ormMatcher, methodValidator) {
this.ormMatcher = ormMatcher;
this.methodValidator = methodValidator;
}
/**
* Detects ORM types in repository interface
*/
detectOrmTypes(code, filePath, layer) {
const violations = [];
const lines = code.split("\n");
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const lineNumber = i + 1;
this.detectOrmInMethod(line, lineNumber, filePath, layer, violations);
this.detectOrmInLine(line, lineNumber, filePath, layer, violations);
}
return violations;
}
/**
* Detects non-domain method names
*/
detectNonDomainMethods(code, filePath, layer) {
const violations = [];
const lines = code.split("\n");
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const lineNumber = i + 1;
const methodMatch = /^\s*(\w+)\s*\(/.exec(line);
if (methodMatch) {
const methodName = methodMatch[1];
if (!this.methodValidator.isDomainMethodName(methodName) &&
!line.trim().startsWith("//")) {
const suggestion = this.methodValidator.suggestDomainMethodName(methodName);
violations.push(RepositoryViolation_1.RepositoryViolation.create(rules_1.REPOSITORY_VIOLATION_TYPES.NON_DOMAIN_METHOD_NAME, filePath, layer || rules_1.LAYERS.DOMAIN, lineNumber, `Method '${methodName}' uses technical name instead of domain language. ${suggestion}`, undefined, undefined, methodName));
}
}
}
return violations;
}
/**
* Detects concrete repository usage
*/
detectConcreteRepositoryUsage(code, filePath, layer) {
const violations = [];
const lines = code.split("\n");
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const lineNumber = i + 1;
this.detectConcreteInConstructor(line, lineNumber, filePath, layer, violations);
this.detectConcreteInField(line, lineNumber, filePath, layer, violations);
}
return violations;
}
/**
* Detects new Repository() instantiation
*/
detectNewInstantiation(code, filePath, layer) {
const violations = [];
const lines = code.split("\n");
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const lineNumber = i + 1;
const newRepositoryMatch = /new\s+([A-Z]\w*Repository)\s*\(/.exec(line);
if (newRepositoryMatch && !line.trim().startsWith("//")) {
const repositoryName = newRepositoryMatch[1];
violations.push(RepositoryViolation_1.RepositoryViolation.create(rules_1.REPOSITORY_VIOLATION_TYPES.NEW_REPOSITORY_IN_USE_CASE, filePath, layer || rules_1.LAYERS.APPLICATION, lineNumber, `Use case creates repository with 'new ${repositoryName}()'`, undefined, repositoryName));
}
}
return violations;
}
/**
* Detects ORM types in method signatures
*/
detectOrmInMethod(line, lineNumber, filePath, layer, violations) {
const methodMatch = /(\w+)\s*\([^)]*:\s*([^)]+)\)\s*:\s*.*?(?:Promise<([^>]+)>|([A-Z]\w+))/.exec(line);
if (methodMatch) {
const params = methodMatch[2];
const returnType = methodMatch[3] || methodMatch[4];
if (this.ormMatcher.isOrmType(params)) {
const ormType = this.ormMatcher.extractOrmType(params);
violations.push(RepositoryViolation_1.RepositoryViolation.create(rules_1.REPOSITORY_VIOLATION_TYPES.ORM_TYPE_IN_INTERFACE, filePath, layer || rules_1.LAYERS.DOMAIN, lineNumber, `Method parameter uses ORM type: ${ormType}`, ormType));
}
if (returnType && this.ormMatcher.isOrmType(returnType)) {
const ormType = this.ormMatcher.extractOrmType(returnType);
violations.push(RepositoryViolation_1.RepositoryViolation.create(rules_1.REPOSITORY_VIOLATION_TYPES.ORM_TYPE_IN_INTERFACE, filePath, layer || rules_1.LAYERS.DOMAIN, lineNumber, `Method return type uses ORM type: ${ormType}`, ormType));
}
}
}
/**
* Detects ORM types in general code line
*/
detectOrmInLine(line, lineNumber, filePath, layer, violations) {
if (this.ormMatcher.isOrmType(line) && !line.trim().startsWith("//")) {
const ormType = this.ormMatcher.extractOrmType(line);
violations.push(RepositoryViolation_1.RepositoryViolation.create(rules_1.REPOSITORY_VIOLATION_TYPES.ORM_TYPE_IN_INTERFACE, filePath, layer || rules_1.LAYERS.DOMAIN, lineNumber, `Repository interface contains ORM-specific type: ${ormType}`, ormType));
}
}
/**
* Detects concrete repository in constructor
*/
detectConcreteInConstructor(line, lineNumber, filePath, layer, violations) {
const constructorParamMatch = /constructor\s*\([^)]*(?:private|public|protected)\s+(?:readonly\s+)?(\w+)\s*:\s*([A-Z]\w*Repository)/.exec(line);
if (constructorParamMatch) {
const repositoryType = constructorParamMatch[2];
if (!repositoryType.startsWith("I")) {
violations.push(RepositoryViolation_1.RepositoryViolation.create(rules_1.REPOSITORY_VIOLATION_TYPES.CONCRETE_REPOSITORY_IN_USE_CASE, filePath, layer || rules_1.LAYERS.APPLICATION, lineNumber, `Use case depends on concrete repository '${repositoryType}'`, undefined, repositoryType));
}
}
}
/**
* Detects concrete repository in field
*/
detectConcreteInField(line, lineNumber, filePath, layer, violations) {
const fieldMatch = /(?:private|public|protected)\s+(?:readonly\s+)?(\w+)\s*:\s*([A-Z]\w*Repository)/.exec(line);
if (fieldMatch) {
const repositoryType = fieldMatch[2];
if (!repositoryType.startsWith("I") &&
!line.includes(Messages_1.REPOSITORY_PATTERN_MESSAGES.CONSTRUCTOR)) {
violations.push(RepositoryViolation_1.RepositoryViolation.create(rules_1.REPOSITORY_VIOLATION_TYPES.CONCRETE_REPOSITORY_IN_USE_CASE, filePath, layer || rules_1.LAYERS.APPLICATION, lineNumber, `Use case field uses concrete repository '${repositoryType}'`, undefined, repositoryType));
}
}
}
}
exports.RepositoryViolationDetector = RepositoryViolationDetector;
//# sourceMappingURL=RepositoryViolationDetector.js.map