UNPKG

@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
"use strict"; 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