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

131 lines 5.22 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AggregateBoundaryDetector = void 0; const AggregateBoundaryViolation_1 = require("../../domain/value-objects/AggregateBoundaryViolation"); const rules_1 = require("../../shared/constants/rules"); const AggregatePathAnalyzer_1 = require("../strategies/AggregatePathAnalyzer"); const FolderRegistry_1 = require("../strategies/FolderRegistry"); const ImportValidator_1 = require("../strategies/ImportValidator"); /** * Detects aggregate boundary violations in Domain-Driven Design * * This detector enforces DDD aggregate rules: * - Aggregates should reference each other only by ID or Value Objects * - Direct entity references across aggregates create tight coupling * - Each aggregate should be independently modifiable * * Folder structure patterns detected: * - domain/aggregates/order/Order.ts * - domain/order/Order.ts (aggregate name from parent folder) * - domain/entities/order/Order.ts * * @example * ```typescript * const detector = new AggregateBoundaryDetector() * * // Detect violations in order aggregate * const code = ` * import { User } from '../user/User' * import { UserId } from '../user/value-objects/UserId' * ` * const violations = detector.detectViolations( * code, * 'src/domain/aggregates/order/Order.ts', * 'domain' * ) * * // violations will contain 1 violation for direct User entity import * // but not for UserId (value object is allowed) * console.log(violations.length) // 1 * ``` */ class AggregateBoundaryDetector { folderRegistry; pathAnalyzer; importValidator; constructor() { this.folderRegistry = new FolderRegistry_1.FolderRegistry(); this.pathAnalyzer = new AggregatePathAnalyzer_1.AggregatePathAnalyzer(this.folderRegistry); this.importValidator = new ImportValidator_1.ImportValidator(this.folderRegistry, this.pathAnalyzer); } /** * Detects aggregate boundary violations in the given code * * Analyzes import statements to identify direct entity references * across aggregate boundaries in the domain layer. * * @param code - Source code to analyze * @param filePath - Path to the file being analyzed * @param layer - The architectural layer of the file (should be 'domain') * @returns Array of detected aggregate boundary violations */ detectViolations(code, filePath, layer) { if (layer !== rules_1.LAYERS.DOMAIN) { return []; } const currentAggregate = this.pathAnalyzer.extractAggregateFromPath(filePath); if (!currentAggregate) { return []; } return this.analyzeImports(code, filePath, currentAggregate); } /** * Checks if a file path belongs to an aggregate * * Extracts aggregate name from paths like: * - domain/aggregates/order/Order.ts → 'order' * - domain/order/Order.ts → 'order' * - domain/entities/order/Order.ts → 'order' * * @param filePath - The file path to check * @returns The aggregate name if found, undefined otherwise */ extractAggregateFromPath(filePath) { return this.pathAnalyzer.extractAggregateFromPath(filePath); } /** * Checks if an import path references an entity from another aggregate * * @param importPath - The import path to analyze * @param currentAggregate - The aggregate of the current file * @returns True if the import crosses aggregate boundaries inappropriately */ isAggregateBoundaryViolation(importPath, currentAggregate) { return this.importValidator.isViolation(importPath, currentAggregate); } /** * Analyzes all imports in code and detects violations */ analyzeImports(code, filePath, currentAggregate) { const violations = []; const lines = code.split("\n"); for (let i = 0; i < lines.length; i++) { const line = lines[i]; const lineNumber = i + 1; const imports = this.importValidator.extractImports(line); for (const importPath of imports) { const violation = this.checkImport(importPath, currentAggregate, filePath, lineNumber); if (violation) { violations.push(violation); } } } return violations; } /** * Checks a single import for boundary violations */ checkImport(importPath, currentAggregate, filePath, lineNumber) { if (!this.importValidator.isViolation(importPath, currentAggregate)) { return undefined; } const targetAggregate = this.pathAnalyzer.extractAggregateFromImport(importPath); const entityName = this.pathAnalyzer.extractEntityName(importPath); if (targetAggregate && entityName) { return AggregateBoundaryViolation_1.AggregateBoundaryViolation.create(currentAggregate, targetAggregate, entityName, importPath, filePath, lineNumber); } return undefined; } } exports.AggregateBoundaryDetector = AggregateBoundaryDetector; //# sourceMappingURL=AggregateBoundaryDetector.js.map