nestjs-doctor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["files: string[]","project: Project","files: string[]","node: ModuleNode","obj: ObjectLiteralExpression","propertyName: string","graph: ModuleGraph","cycles: string[][]","node: string","path: string[]","rule: AnyRule","project: Project","files: string[]","rules: AnyRule[]","options: RunRulesOptions","diagnostics: Diagnostic[]","fileRules: Rule[]","projectRules: ProjectRule[]","context: RuleContext","context: ProjectRuleContext","GENERIC_TYPE_REGEX","project: Project","files: string[]","typeText: string","noBarrelExportInternals: Rule","cls: ClassDeclaration","name: string","noBusinessLogicInControllers: Rule","noCircularModuleDeps: ProjectRule","noGodModule: ProjectRule","noGodService: ProjectRule","noManualInstantiation: Rule","DOTTED_SUFFIX_REGEX","GENERIC_TYPE_REGEX","ORM_TYPES","noOrmInControllers: Rule","extractTypeName","typeText: string","DOTTED_SUFFIX_REGEX","GENERIC_TYPE_REGEX","noOrmInServices: Rule","extractTypeName","typeText: string","noRepositoryInControllers: Rule","typeText: string","preferConstructorInjection: Rule","preferInterfaceInjection: Rule","requireFeatureModules: ProjectRule","requireModuleBoundaries: Rule","preferReadonlyInjection: Rule","noHardcodedSecrets: Rule","allRules: AnyRule[]","score: number","SEVERITY_WEIGHTS: Record<Severity, number>","CATEGORY_MULTIPLIERS: Record<Category, number>","diagnostics: Diagnostic[]","fileCount: number","DEFAULT_CONFIG: NestjsDoctorConfig","targetPath: string","configPath?: string","path: string","userConfig: NestjsDoctorConfig","targetPath: string","config: NestjsDoctorConfig","pattern: string","diagnostics: Diagnostic[]","config: NestjsDoctorConfig","targetPath: string","pkg: PackageJson","version: string | undefined","deps: Record<string, string>","targetPath: string","options: { config?: string }","config: NestjsDoctorConfig","diagnostics: DiagnoseResult[\"diagnostics\"]","path: string","options: { config?: string }"],"sources":["../../src/engine/ast-parser.ts","../../src/engine/module-graph.ts","../../src/rules/types.ts","../../src/engine/rule-runner.ts","../../src/engine/type-resolver.ts","../../src/rules/architecture/no-barrel-export-internals.ts","../../src/engine/decorator-utils.ts","../../src/rules/architecture/no-business-logic-in-controllers.ts","../../src/rules/architecture/no-circular-module-deps.ts","../../src/rules/architecture/no-god-module.ts","../../src/rules/architecture/no-god-service.ts","../../src/rules/architecture/no-manual-instantiation.ts","../../src/rules/architecture/no-orm-in-controllers.ts","../../src/rules/architecture/no-orm-in-services.ts","../../src/rules/architecture/no-repository-in-controllers.ts","../../src/rules/architecture/prefer-constructor-injection.ts","../../src/rules/architecture/prefer-interface-injection.ts","../../src/rules/architecture/require-feature-modules.ts","../../src/rules/architecture/require-module-boundaries.ts","../../src/rules/correctness/prefer-readonly-injection.ts","../../src/rules/security/no-hardcoded-secrets.ts","../../src/rules/index.ts","../../src/scorer/labels.ts","../../src/scorer/weights.ts","../../src/scorer/index.ts","../../src/types/config.ts","../../src/core/config-loader.ts","../../src/core/file-collector.ts","../../src/core/match-glob-pattern.ts","../../src/core/filter-diagnostics.ts","../../src/core/project-detector.ts","../../src/core/scanner.ts","../../src/api/index.ts"],"sourcesContent":["import { Project } from \"ts-morph\";\n\nexport function createAstParser(files: string[]): Project {\n\tconst project = new Project({\n\t\tcompilerOptions: {\n\t\t\tstrict: true,\n\t\t\ttarget: 99, // ESNext\n\t\t\tmodule: 99, // ESNext\n\t\t\tskipFileDependencyResolution: true,\n\t\t},\n\t\tskipAddingFilesFromTsConfig: true,\n\t});\n\n\tfor (const file of files) {\n\t\tproject.addSourceFileAtPath(file);\n\t}\n\n\treturn project;\n}\n","import type {\n\tClassDeclaration,\n\tObjectLiteralExpression,\n\tProject,\n} from \"ts-morph\";\nimport { SyntaxKind } from \"ts-morph\";\n\nconst FORWARD_REF_REGEX = /=>\\s*(\\w+)/;\n\nexport interface ModuleNode {\n\tclassDeclaration: ClassDeclaration;\n\tcontrollers: string[];\n\texports: string[];\n\tfilePath: string;\n\timports: string[];\n\tname: string;\n\tproviders: string[];\n}\n\nexport interface ModuleGraph {\n\tedges: Map<string, Set<string>>;\n\tmodules: Map<string, ModuleNode>;\n}\n\nexport function buildModuleGraph(\n\tproject: Project,\n\tfiles: string[]\n): ModuleGraph {\n\tconst modules = new Map<string, ModuleNode>();\n\tconst edges = new Map<string, Set<string>>();\n\n\t// First pass: collect all @Module() classes\n\tfor (const filePath of files) {\n\t\tconst sourceFile = project.getSourceFile(filePath);\n\t\tif (!sourceFile) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (const cls of sourceFile.getClasses()) {\n\t\t\tconst moduleDecorator = cls.getDecorator(\"Module\");\n\t\t\tif (!moduleDecorator) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst name = cls.getName() ?? \"AnonymousModule\";\n\t\t\tconst args = moduleDecorator.getArguments()[0];\n\n\t\t\tconst node: ModuleNode = {\n\t\t\t\tname,\n\t\t\t\tfilePath,\n\t\t\t\tclassDeclaration: cls,\n\t\t\t\timports: [],\n\t\t\t\texports: [],\n\t\t\t\tproviders: [],\n\t\t\t\tcontrollers: [],\n\t\t\t};\n\n\t\t\tif (args && args.getKind() === SyntaxKind.ObjectLiteralExpression) {\n\t\t\t\tconst obj = args.asKind(SyntaxKind.ObjectLiteralExpression);\n\t\t\t\tif (obj) {\n\t\t\t\t\tnode.imports = extractArrayPropertyNames(obj, \"imports\");\n\t\t\t\t\tnode.exports = extractArrayPropertyNames(obj, \"exports\");\n\t\t\t\t\tnode.providers = extractArrayPropertyNames(obj, \"providers\");\n\t\t\t\t\tnode.controllers = extractArrayPropertyNames(obj, \"controllers\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmodules.set(name, node);\n\t\t}\n\t}\n\n\t// Second pass: build edges from import relationships\n\tfor (const [name, node] of modules) {\n\t\tconst importSet = new Set<string>();\n\t\tfor (const imp of node.imports) {\n\t\t\tif (modules.has(imp)) {\n\t\t\t\timportSet.add(imp);\n\t\t\t}\n\t\t}\n\t\tedges.set(name, importSet);\n\t}\n\n\treturn { modules, edges };\n}\n\nfunction extractArrayPropertyNames(\n\tobj: ObjectLiteralExpression,\n\tpropertyName: string\n): string[] {\n\tconst prop = obj.getProperty(propertyName);\n\tif (!prop) {\n\t\treturn [];\n\t}\n\n\tconst initializer = prop.getChildrenOfKind(\n\t\tSyntaxKind.ArrayLiteralExpression\n\t)[0];\n\tif (!initializer) {\n\t\treturn [];\n\t}\n\n\treturn initializer.getElements().map((el) => {\n\t\tconst text = el.getText();\n\t\t// Handle forwardRef(() => SomeModule)\n\t\tif (text.startsWith(\"forwardRef\")) {\n\t\t\tconst match = text.match(FORWARD_REF_REGEX);\n\t\t\treturn match ? match[1] : text;\n\t\t}\n\t\t// Handle spread operator\n\t\tif (text.startsWith(\"...\")) {\n\t\t\treturn text.slice(3).trim();\n\t\t}\n\t\treturn text;\n\t});\n}\n\nexport function findCircularDeps(graph: ModuleGraph): string[][] {\n\tconst cycles: string[][] = [];\n\tconst visited = new Set<string>();\n\tconst recursionStack = new Set<string>();\n\n\tfunction dfs(node: string, path: string[]): void {\n\t\tvisited.add(node);\n\t\trecursionStack.add(node);\n\n\t\tconst neighbors = graph.edges.get(node) ?? new Set();\n\t\tfor (const neighbor of neighbors) {\n\t\t\tif (!visited.has(neighbor)) {\n\t\t\t\tdfs(neighbor, [...path, neighbor]);\n\t\t\t} else if (recursionStack.has(neighbor)) {\n\t\t\t\tconst cycleStart = path.indexOf(neighbor);\n\t\t\t\tif (cycleStart !== -1) {\n\t\t\t\t\tcycles.push(path.slice(cycleStart));\n\t\t\t\t} else {\n\t\t\t\t\tcycles.push([...path, neighbor]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\trecursionStack.delete(node);\n\t}\n\n\tfor (const moduleName of graph.modules.keys()) {\n\t\tif (!visited.has(moduleName)) {\n\t\t\tdfs(moduleName, [moduleName]);\n\t\t}\n\t}\n\n\treturn cycles;\n}\n\nexport function getModuleByClassName(\n\tgraph: ModuleGraph,\n\tclassName: string\n): ModuleNode | undefined {\n\treturn graph.modules.get(className);\n}\n\nexport function findProviderModule(\n\tgraph: ModuleGraph,\n\tproviderName: string\n): ModuleNode | undefined {\n\tfor (const mod of graph.modules.values()) {\n\t\tif (mod.providers.includes(providerName)) {\n\t\t\treturn mod;\n\t\t}\n\t}\n\treturn undefined;\n}\n","import type { Project, SourceFile } from \"ts-morph\";\nimport type { ModuleGraph } from \"../engine/module-graph.js\";\nimport type { ProviderInfo } from \"../engine/type-resolver.js\";\nimport type { NestjsDoctorConfig } from \"../types/config.js\";\nimport type { Category, Diagnostic, Severity } from \"../types/diagnostic.js\";\n\nexport type RuleScope = \"file\" | \"project\";\n\nexport interface RuleMeta {\n\tcategory: Category;\n\tdescription: string;\n\thelp: string;\n\tid: string;\n\tscope?: RuleScope;\n\tseverity: Severity;\n}\n\nexport interface RuleContext {\n\tfilePath: string;\n\treport(diagnostic: Omit<Diagnostic, \"rule\" | \"category\" | \"severity\">): void;\n\tsourceFile: SourceFile;\n}\n\nexport interface ProjectRuleContext {\n\tconfig: NestjsDoctorConfig;\n\tfiles: string[];\n\tmoduleGraph: ModuleGraph;\n\tproject: Project;\n\tproviders: Map<string, ProviderInfo>;\n\treport(diagnostic: Omit<Diagnostic, \"rule\" | \"category\" | \"severity\">): void;\n}\n\nexport interface Rule {\n\tcheck(context: RuleContext): void;\n\tmeta: RuleMeta;\n}\n\nexport interface ProjectRule {\n\tcheck(context: ProjectRuleContext): void;\n\tmeta: RuleMeta;\n}\n\nexport type AnyRule = Rule | ProjectRule;\n\nexport function isProjectRule(rule: AnyRule): rule is ProjectRule {\n\treturn rule.meta.scope === \"project\";\n}\n","import type { Project } from \"ts-morph\";\nimport type {\n\tAnyRule,\n\tProjectRule,\n\tProjectRuleContext,\n\tRule,\n\tRuleContext,\n} from \"../rules/types.js\";\nimport { isProjectRule } from \"../rules/types.js\";\nimport type { NestjsDoctorConfig } from \"../types/config.js\";\nimport type { Diagnostic } from \"../types/diagnostic.js\";\nimport type { ModuleGraph } from \"./module-graph.js\";\nimport type { ProviderInfo } from \"./type-resolver.js\";\n\nexport interface RunRulesOptions {\n\tconfig: NestjsDoctorConfig;\n\tmoduleGraph: ModuleGraph;\n\tproviders: Map<string, ProviderInfo>;\n}\n\nexport function runRules(\n\tproject: Project,\n\tfiles: string[],\n\trules: AnyRule[],\n\toptions: RunRulesOptions\n): Diagnostic[] {\n\tconst diagnostics: Diagnostic[] = [];\n\n\tconst fileRules: Rule[] = [];\n\tconst projectRules: ProjectRule[] = [];\n\n\tfor (const rule of rules) {\n\t\tif (isProjectRule(rule)) {\n\t\t\tprojectRules.push(rule);\n\t\t} else {\n\t\t\tfileRules.push(rule);\n\t\t}\n\t}\n\n\t// Run file-scoped rules\n\tfor (const filePath of files) {\n\t\tconst sourceFile = project.getSourceFile(filePath);\n\t\tif (!sourceFile) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (const rule of fileRules) {\n\t\t\tconst context: RuleContext = {\n\t\t\t\tsourceFile,\n\t\t\t\tfilePath,\n\t\t\t\treport(partial) {\n\t\t\t\t\tdiagnostics.push({\n\t\t\t\t\t\t...partial,\n\t\t\t\t\t\trule: rule.meta.id,\n\t\t\t\t\t\tcategory: rule.meta.category,\n\t\t\t\t\t\tseverity: rule.meta.severity,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\trule.check(context);\n\t\t\t} catch {\n\t\t\t\t// Rule failed — skip silently\n\t\t\t}\n\t\t}\n\t}\n\n\t// Run project-scoped rules\n\tfor (const rule of projectRules) {\n\t\tconst context: ProjectRuleContext = {\n\t\t\tproject,\n\t\t\tfiles,\n\t\t\tmoduleGraph: options.moduleGraph,\n\t\t\tproviders: options.providers,\n\t\t\tconfig: options.config,\n\t\t\treport(partial) {\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\t...partial,\n\t\t\t\t\trule: rule.meta.id,\n\t\t\t\t\tcategory: rule.meta.category,\n\t\t\t\t\tseverity: rule.meta.severity,\n\t\t\t\t});\n\t\t\t},\n\t\t};\n\n\t\ttry {\n\t\t\trule.check(context);\n\t\t} catch {\n\t\t\t// Rule failed — skip silently\n\t\t}\n\t}\n\n\treturn diagnostics;\n}\n","import type { ClassDeclaration, Project } from \"ts-morph\";\n\nconst IMPORT_TYPE_REGEX = /import\\([^)]+\\)\\.(\\w+)/;\nconst GENERIC_TYPE_REGEX = /^(\\w+)</;\n\nexport interface ProviderInfo {\n\tclassDeclaration: ClassDeclaration;\n\tdependencies: string[];\n\tfilePath: string;\n\tname: string;\n\tpublicMethodCount: number;\n}\n\nexport function resolveProviders(\n\tproject: Project,\n\tfiles: string[]\n): Map<string, ProviderInfo> {\n\tconst providers = new Map<string, ProviderInfo>();\n\n\tfor (const filePath of files) {\n\t\tconst sourceFile = project.getSourceFile(filePath);\n\t\tif (!sourceFile) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (const cls of sourceFile.getClasses()) {\n\t\t\tif (!cls.getDecorator(\"Injectable\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst name = cls.getName();\n\t\t\tif (!name) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst ctor = cls.getConstructors()[0];\n\t\t\tconst dependencies = ctor\n\t\t\t\t? ctor.getParameters().map((p) => {\n\t\t\t\t\t\tconst typeText = p.getType().getText();\n\t\t\t\t\t\treturn extractSimpleTypeName(typeText);\n\t\t\t\t\t})\n\t\t\t\t: [];\n\n\t\t\tconst publicMethodCount = cls.getMethods().filter((m) => {\n\t\t\t\tconst scope = m.getScope();\n\t\t\t\t// In TS, no modifier = public\n\t\t\t\treturn !scope || scope === \"public\";\n\t\t\t}).length;\n\n\t\t\tproviders.set(name, {\n\t\t\t\tname,\n\t\t\t\tfilePath,\n\t\t\t\tclassDeclaration: cls,\n\t\t\t\tdependencies,\n\t\t\t\tpublicMethodCount,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn providers;\n}\n\nfunction extractSimpleTypeName(typeText: string): string {\n\t// Handle import(\"...\").ClassName\n\tconst importMatch = typeText.match(IMPORT_TYPE_REGEX);\n\tif (importMatch) {\n\t\treturn importMatch[1];\n\t}\n\t// Handle generic types Repository<User>\n\tconst genericMatch = typeText.match(GENERIC_TYPE_REGEX);\n\tif (genericMatch) {\n\t\treturn genericMatch[1];\n\t}\n\treturn typeText;\n}\n","import type { Rule } from \"../types.js\";\n\nconst INTERNAL_PATTERNS = [\n\t/Repository$/,\n\t/\\.repository$/,\n\t/\\.entity$/,\n\t/\\.schema$/,\n\t/\\.guard$/,\n\t/\\.interceptor$/,\n\t/\\.pipe$/,\n\t/\\.filter$/,\n\t/\\.strategy$/,\n];\n\nexport const noBarrelExportInternals: Rule = {\n\tmeta: {\n\t\tid: \"architecture/no-barrel-export-internals\",\n\t\tcategory: \"architecture\",\n\t\tseverity: \"info\",\n\t\tdescription:\n\t\t\t\"Don't re-export internal implementation details from barrel files\",\n\t\thelp: \"Only export the module's public API (services, DTOs, interfaces) from index.ts files.\",\n\t},\n\n\tcheck(context) {\n\t\t// Only check barrel files (index.ts)\n\t\tif (!context.filePath.endsWith(\"/index.ts\")) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const exportDecl of context.sourceFile.getExportDeclarations()) {\n\t\t\tconst moduleSpecifier = exportDecl.getModuleSpecifierValue();\n\t\t\tif (!moduleSpecifier) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst isInternal = INTERNAL_PATTERNS.some((p) => p.test(moduleSpecifier));\n\n\t\t\tif (isInternal) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Barrel file re-exports internal module '${moduleSpecifier}'.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: exportDecl.getStartLineNumber(),\n\t\t\t\t\tcolumn: 1,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Also check named exports that reference internal types\n\t\t\tfor (const namedExport of exportDecl.getNamedExports()) {\n\t\t\t\tconst name = namedExport.getName();\n\t\t\t\tif (\n\t\t\t\t\tname.endsWith(\"Repository\") ||\n\t\t\t\t\tname.endsWith(\"Entity\") ||\n\t\t\t\t\tname.endsWith(\"Schema\")\n\t\t\t\t) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Barrel file re-exports internal type '${name}'.`,\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: namedExport.getStartLineNumber(),\n\t\t\t\t\t\tcolumn: 1,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n};\n","import type { ClassDeclaration, Decorator } from \"ts-morph\";\n\nexport type NestClassType =\n\t| \"controller\"\n\t| \"service\"\n\t| \"module\"\n\t| \"guard\"\n\t| \"interceptor\"\n\t| \"pipe\"\n\t| \"filter\"\n\t| \"resolver\"\n\t| \"gateway\"\n\t| \"unknown\";\n\nconst NEST_CLASS_DECORATORS: Record<string, NestClassType> = {\n\tController: \"controller\",\n\tInjectable: \"service\",\n\tModule: \"module\",\n\tGuard: \"guard\",\n\tUseInterceptors: \"interceptor\",\n\tUsePipes: \"pipe\",\n\tCatch: \"filter\",\n\tResolver: \"resolver\",\n\tWebSocketGateway: \"gateway\",\n};\n\nexport function hasDecorator(cls: ClassDeclaration, name: string): boolean {\n\treturn cls.getDecorator(name) !== undefined;\n}\n\nexport function getDecoratorArgs(decorator: Decorator): string | undefined {\n\tconst args = decorator.getArguments();\n\tif (args.length === 0) {\n\t\treturn undefined;\n\t}\n\treturn args[0].getText();\n}\n\nexport function getClassType(cls: ClassDeclaration): NestClassType {\n\tfor (const [decoratorName, type] of Object.entries(NEST_CLASS_DECORATORS)) {\n\t\tif (hasDecorator(cls, decoratorName)) {\n\t\t\treturn type;\n\t\t}\n\t}\n\treturn \"unknown\";\n}\n\nexport function isController(cls: ClassDeclaration): boolean {\n\treturn hasDecorator(cls, \"Controller\");\n}\n\nexport function isService(cls: ClassDeclaration): boolean {\n\treturn hasDecorator(cls, \"Injectable\");\n}\n\nexport function isModule(cls: ClassDeclaration): boolean {\n\treturn hasDecorator(cls, \"Module\");\n}\n\nexport function getConstructorParams(\n\tcls: ClassDeclaration\n): { name: string; type: string; isReadonly: boolean }[] {\n\tconst ctor = cls.getConstructors()[0];\n\tif (!ctor) {\n\t\treturn [];\n\t}\n\n\treturn ctor.getParameters().map((param) => ({\n\t\tname: param.getName(),\n\t\ttype: param.getType().getText(),\n\t\tisReadonly: param.isReadonly(),\n\t}));\n}\n","import { SyntaxKind } from \"ts-morph\";\nimport { isController } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nconst HTTP_DECORATORS = new Set([\n\t\"Get\",\n\t\"Post\",\n\t\"Put\",\n\t\"Patch\",\n\t\"Delete\",\n\t\"Head\",\n\t\"Options\",\n\t\"All\",\n]);\n\nexport const noBusinessLogicInControllers: Rule = {\n\tmeta: {\n\t\tid: \"architecture/no-business-logic-in-controllers\",\n\t\tcategory: \"architecture\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Controllers should only handle HTTP concerns — move business logic to services\",\n\t\thelp: \"Extract branches, loops, and complex calculations into a service method.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tif (!isController(cls)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const method of cls.getMethods()) {\n\t\t\t\t// Only check endpoint handlers (methods with HTTP decorators)\n\t\t\t\tconst isEndpoint = method\n\t\t\t\t\t.getDecorators()\n\t\t\t\t\t.some((d) => HTTP_DECORATORS.has(d.getName()));\n\t\t\t\tif (!isEndpoint) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst body = method.getBody();\n\t\t\t\tif (!body) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Count control flow statements\n\t\t\t\tconst ifStatements = body.getDescendantsOfKind(SyntaxKind.IfStatement);\n\t\t\t\tconst forStatements = body.getDescendantsOfKind(\n\t\t\t\t\tSyntaxKind.ForStatement\n\t\t\t\t);\n\t\t\t\tconst forInStatements = body.getDescendantsOfKind(\n\t\t\t\t\tSyntaxKind.ForInStatement\n\t\t\t\t);\n\t\t\t\tconst forOfStatements = body.getDescendantsOfKind(\n\t\t\t\t\tSyntaxKind.ForOfStatement\n\t\t\t\t);\n\t\t\t\tconst whileStatements = body.getDescendantsOfKind(\n\t\t\t\t\tSyntaxKind.WhileStatement\n\t\t\t\t);\n\t\t\t\tconst switchStatements = body.getDescendantsOfKind(\n\t\t\t\t\tSyntaxKind.SwitchStatement\n\t\t\t\t);\n\n\t\t\t\tconst loopCount =\n\t\t\t\t\tforStatements.length +\n\t\t\t\t\tforInStatements.length +\n\t\t\t\t\tforOfStatements.length +\n\t\t\t\t\twhileStatements.length;\n\n\t\t\t\t// Allow simple guard clauses (1 if), but flag complex logic\n\t\t\t\tif (\n\t\t\t\t\tifStatements.length > 1 ||\n\t\t\t\t\tloopCount > 0 ||\n\t\t\t\t\tswitchStatements.length > 0\n\t\t\t\t) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Controller method '${method.getName()}' contains business logic (${ifStatements.length} if, ${loopCount} loops, ${switchStatements.length} switch). Move to a service.`,\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: method.getStartLineNumber(),\n\t\t\t\t\t\tcolumn: 1,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Check for complex expressions: array methods like map, filter, reduce\n\t\t\t\tconst callExpressions = body.getDescendantsOfKind(\n\t\t\t\t\tSyntaxKind.CallExpression\n\t\t\t\t);\n\t\t\t\tconst complexArrayOps = callExpressions.filter((call) => {\n\t\t\t\t\tconst expr = call.getExpression();\n\t\t\t\t\tif (expr.getKind() === SyntaxKind.PropertyAccessExpression) {\n\t\t\t\t\t\tconst propAccess = expr.asKind(SyntaxKind.PropertyAccessExpression);\n\t\t\t\t\t\tconst name = propAccess?.getName();\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\tname === \"map\" ||\n\t\t\t\t\t\t\tname === \"filter\" ||\n\t\t\t\t\t\t\tname === \"reduce\" ||\n\t\t\t\t\t\t\tname === \"sort\" ||\n\t\t\t\t\t\t\tname === \"flatMap\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t});\n\n\t\t\t\tif (complexArrayOps.length > 1) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Controller method '${method.getName()}' contains data transformation logic (${complexArrayOps.length} array operations). Move to a service.`,\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: method.getStartLineNumber(),\n\t\t\t\t\t\tcolumn: 1,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n};\n","import { findCircularDeps } from \"../../engine/module-graph.js\";\nimport type { ProjectRule } from \"../types.js\";\n\nexport const noCircularModuleDeps: ProjectRule = {\n\tmeta: {\n\t\tid: \"architecture/no-circular-module-deps\",\n\t\tcategory: \"architecture\",\n\t\tseverity: \"error\",\n\t\tdescription: \"Circular dependencies in @Module() import graph\",\n\t\thelp: \"Break the cycle by extracting shared logic into a separate module or using forwardRef().\",\n\t\tscope: \"project\",\n\t},\n\n\tcheck(context) {\n\t\tconst cycles = findCircularDeps(context.moduleGraph);\n\n\t\tfor (const cycle of cycles) {\n\t\t\tconst cycleStr = cycle.join(\" -> \");\n\t\t\tconst firstModule = context.moduleGraph.modules.get(cycle[0]);\n\n\t\t\tcontext.report({\n\t\t\t\tfilePath: firstModule?.filePath ?? \"unknown\",\n\t\t\t\tmessage: `Circular module dependency detected: ${cycleStr}`,\n\t\t\t\thelp: this.meta.help,\n\t\t\t\tline: firstModule?.classDeclaration.getStartLineNumber() ?? 1,\n\t\t\t\tcolumn: 1,\n\t\t\t});\n\t\t}\n\t},\n};\n","import type { ProjectRule } from \"../types.js\";\n\nconst DEFAULT_MAX_PROVIDERS = 10;\nconst DEFAULT_MAX_IMPORTS = 15;\n\nexport const noGodModule: ProjectRule = {\n\tmeta: {\n\t\tid: \"architecture/no-god-module\",\n\t\tcategory: \"architecture\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Modules with too many providers or imports should be split into smaller feature modules\",\n\t\thelp: \"Split this module into smaller, focused feature modules.\",\n\t\tscope: \"project\",\n\t},\n\n\tcheck(context) {\n\t\tconst maxProviders =\n\t\t\tcontext.config.thresholds?.godModuleProviders ?? DEFAULT_MAX_PROVIDERS;\n\t\tconst maxImports =\n\t\t\tcontext.config.thresholds?.godModuleImports ?? DEFAULT_MAX_IMPORTS;\n\n\t\tfor (const mod of context.moduleGraph.modules.values()) {\n\t\t\tif (mod.providers.length > maxProviders) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: mod.filePath,\n\t\t\t\t\tmessage: `Module '${mod.name}' has ${mod.providers.length} providers (max: ${maxProviders}). Consider splitting into smaller modules.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: mod.classDeclaration.getStartLineNumber(),\n\t\t\t\t\tcolumn: 1,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (mod.imports.length > maxImports) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: mod.filePath,\n\t\t\t\t\tmessage: `Module '${mod.name}' has ${mod.imports.length} imports (max: ${maxImports}). Consider grouping into feature modules.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: mod.classDeclaration.getStartLineNumber(),\n\t\t\t\t\tcolumn: 1,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t},\n};\n","import type { ProjectRule } from \"../types.js\";\n\nconst DEFAULT_MAX_METHODS = 10;\nconst DEFAULT_MAX_DEPS = 8;\n\nexport const noGodService: ProjectRule = {\n\tmeta: {\n\t\tid: \"architecture/no-god-service\",\n\t\tcategory: \"architecture\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Services with too many public methods or dependencies should be split\",\n\t\thelp: \"Split this service into smaller, focused services with single responsibilities.\",\n\t\tscope: \"project\",\n\t},\n\n\tcheck(context) {\n\t\tconst maxMethods =\n\t\t\tcontext.config.thresholds?.godServiceMethods ?? DEFAULT_MAX_METHODS;\n\t\tconst maxDeps =\n\t\t\tcontext.config.thresholds?.godServiceDeps ?? DEFAULT_MAX_DEPS;\n\n\t\tfor (const provider of context.providers.values()) {\n\t\t\tif (provider.publicMethodCount > maxMethods) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: provider.filePath,\n\t\t\t\t\tmessage: `Service '${provider.name}' has ${provider.publicMethodCount} public methods (max: ${maxMethods}). Consider splitting.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: provider.classDeclaration.getStartLineNumber(),\n\t\t\t\t\tcolumn: 1,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (provider.dependencies.length > maxDeps) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: provider.filePath,\n\t\t\t\t\tmessage: `Service '${provider.name}' has ${provider.dependencies.length} dependencies (max: ${maxDeps}). Consider splitting.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: provider.classDeclaration.getStartLineNumber(),\n\t\t\t\t\tcolumn: 1,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t},\n};\n","import { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nexport const noManualInstantiation: Rule = {\n\tmeta: {\n\t\tid: \"architecture/no-manual-instantiation\",\n\t\tcategory: \"architecture\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Do not manually instantiate @Injectable classes — use NestJS dependency injection\",\n\t\thelp: \"Register the class as a provider in a module and inject it via the constructor.\",\n\t},\n\n\tcheck(context) {\n\t\tconst newExpressions = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.NewExpression\n\t\t);\n\n\t\tfor (const expr of newExpressions) {\n\t\t\tconst exprText = expr.getExpression().getText();\n\n\t\t\t// Check if the instantiated class name follows NestJS service patterns\n\t\t\tif (\n\t\t\t\texprText.endsWith(\"Service\") ||\n\t\t\t\texprText.endsWith(\"Repository\") ||\n\t\t\t\texprText.endsWith(\"Guard\") ||\n\t\t\t\texprText.endsWith(\"Interceptor\") ||\n\t\t\t\texprText.endsWith(\"Pipe\") ||\n\t\t\t\texprText.endsWith(\"Filter\") ||\n\t\t\t\texprText.endsWith(\"Gateway\") ||\n\t\t\t\texprText.endsWith(\"Resolver\")\n\t\t\t) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Manual instantiation of '${exprText}' detected. Use dependency injection instead.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: expr.getStartLineNumber(),\n\t\t\t\t\tcolumn: 1,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t},\n};\n","import { isController } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nconst DOTTED_SUFFIX_REGEX = /\\.(\\w+)$/;\nconst GENERIC_TYPE_REGEX = /^(\\w+)</;\n\nconst ORM_TYPES = new Set([\n\t\"PrismaService\",\n\t\"PrismaClient\",\n\t\"EntityManager\",\n\t\"DataSource\",\n\t\"Repository\",\n\t\"Connection\",\n\t\"MongooseModel\",\n\t\"InjectModel\",\n\t\"InjectRepository\",\n\t\"MikroORM\",\n\t\"DrizzleService\",\n]);\n\nexport const noOrmInControllers: Rule = {\n\tmeta: {\n\t\tid: \"architecture/no-orm-in-controllers\",\n\t\tcategory: \"architecture\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Controllers must not inject ORM services directly — use a service layer\",\n\t\thelp: \"Inject a service that wraps the ORM instead of using the ORM directly in controllers.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tif (!isController(cls)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst ctor = cls.getConstructors()[0];\n\t\t\tif (!ctor) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const param of ctor.getParameters()) {\n\t\t\t\tconst typeText = param.getType().getText();\n\t\t\t\tconst typeName = extractTypeName(typeText);\n\n\t\t\t\tif (ORM_TYPES.has(typeName)) {\n\t\t\t\t\tconst nameNode = param.getNameNode();\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Controller injects ORM type '${typeName}' directly. Use a service layer.`,\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: nameNode.getStartLineNumber(),\n\t\t\t\t\t\tcolumn: nameNode.getStartLinePos() + 1,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check for ORM-related decorator injections (@InjectRepository, @InjectModel)\n\t\t\tfor (const param of cls.getConstructors()[0]?.getParameters() ?? []) {\n\t\t\t\tfor (const decorator of param.getDecorators()) {\n\t\t\t\t\tconst name = decorator.getName();\n\t\t\t\t\tif (name === \"InjectRepository\" || name === \"InjectModel\") {\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\t\tmessage: `Controller uses @${name}() decorator. Move data access to a service.`,\n\t\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\t\tline: decorator.getStartLineNumber(),\n\t\t\t\t\t\t\tcolumn: 1,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n};\n\nfunction extractTypeName(typeText: string): string {\n\tconst match = typeText.match(DOTTED_SUFFIX_REGEX);\n\tif (match) {\n\t\treturn match[1];\n\t}\n\tconst genericMatch = typeText.match(GENERIC_TYPE_REGEX);\n\tif (genericMatch) {\n\t\treturn genericMatch[1];\n\t}\n\treturn typeText;\n}\n","import { isService } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nconst DOTTED_SUFFIX_REGEX = /\\.(\\w+)$/;\nconst GENERIC_TYPE_REGEX = /^(\\w+)</;\n\nconst ORM_TYPES = new Set([\n\t\"PrismaService\",\n\t\"PrismaClient\",\n\t\"EntityManager\",\n\t\"DataSource\",\n\t\"Connection\",\n\t\"MikroORM\",\n]);\n\nexport const noOrmInServices: Rule = {\n\tmeta: {\n\t\tid: \"architecture/no-orm-in-services\",\n\t\tcategory: \"architecture\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Services should use repository abstractions instead of ORM directly\",\n\t\thelp: \"Create a repository class that wraps ORM calls and inject that instead.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tif (!isService(cls)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Skip classes that are themselves repositories\n\t\t\tconst className = cls.getName() ?? \"\";\n\t\t\tif (className.endsWith(\"Repository\") || className.endsWith(\"Repo\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst ctor = cls.getConstructors()[0];\n\t\t\tif (!ctor) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const param of ctor.getParameters()) {\n\t\t\t\tconst typeText = param.getType().getText();\n\t\t\t\tconst typeName = extractTypeName(typeText);\n\n\t\t\t\tif (ORM_TYPES.has(typeName)) {\n\t\t\t\t\tconst nameNode = param.getNameNode();\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Service injects ORM type '${typeName}' directly. Consider using a repository abstraction.`,\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: nameNode.getStartLineNumber(),\n\t\t\t\t\t\tcolumn: nameNode.getStartLinePos() + 1,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Check for @InjectRepository/@InjectModel\n\t\t\t\tfor (const decorator of param.getDecorators()) {\n\t\t\t\t\tconst name = decorator.getName();\n\t\t\t\t\tif (name === \"InjectRepository\" || name === \"InjectModel\") {\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\t\tmessage: `Service uses @${name}() directly. Consider wrapping in a repository class.`,\n\t\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\t\tline: decorator.getStartLineNumber(),\n\t\t\t\t\t\t\tcolumn: 1,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n};\n\nfunction extractTypeName(typeText: string): string {\n\tconst match = typeText.match(DOTTED_SUFFIX_REGEX);\n\tif (match) {\n\t\treturn match[1];\n\t}\n\tconst genericMatch = typeText.match(GENERIC_TYPE_REGEX);\n\tif (genericMatch) {\n\t\treturn genericMatch[1];\n\t}\n\treturn typeText;\n}\n","import { isController } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nconst DOTTED_SUFFIX_REGEX = /\\.(\\w+)$/;\nconst GENERIC_TYPE_REGEX = /^(\\w+)</;\nconst REPOSITORY_PATTERNS = [/Repository$/, /Repo$/];\n\nexport const noRepositoryInControllers: Rule = {\n\tmeta: {\n\t\tid: \"architecture/no-repository-in-controllers\",\n\t\tcategory: \"architecture\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Controllers must not inject repositories directly — use the service layer\",\n\t\thelp: \"Move database access to a service and inject the service into the controller instead.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tif (!isController(cls)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst ctor = cls.getConstructors()[0];\n\t\t\tif (!ctor) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const param of ctor.getParameters()) {\n\t\t\t\tconst typeText = param.getType().getText();\n\t\t\t\tconst typeName = extractTypeName(typeText);\n\n\t\t\t\tif (REPOSITORY_PATTERNS.some((p) => p.test(typeName))) {\n\t\t\t\t\tconst nameNode = param.getNameNode();\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Controller injects repository '${typeName}' directly. Use a service layer instead.`,\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: nameNode.getStartLineNumber(),\n\t\t\t\t\t\tcolumn: nameNode.getStartLinePos() + 1,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Also check import declarations for repository imports\n\t\t\tfor (const imp of context.sourceFile.getImportDeclarations()) {\n\t\t\t\tconst moduleSpecifier = imp.getModuleSpecifierValue();\n\t\t\t\tif (\n\t\t\t\t\tmoduleSpecifier.includes(\"/repositories/\") ||\n\t\t\t\t\tmoduleSpecifier.includes(\"/repositories\")\n\t\t\t\t) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Controller imports from repository path '${moduleSpecifier}'.`,\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: imp.getStartLineNumber(),\n\t\t\t\t\t\tcolumn: 1,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n};\n\nfunction extractTypeName(typeText: string): string {\n\t// Handle import(\"...\").ClassName patterns from ts-morph\n\tconst match = typeText.match(DOTTED_SUFFIX_REGEX);\n\tif (match) {\n\t\treturn match[1];\n\t}\n\t// Handle generic types like Repository<User>\n\tconst genericMatch = typeText.match(GENERIC_TYPE_REGEX);\n\tif (genericMatch) {\n\t\treturn genericMatch[1];\n\t}\n\treturn typeText;\n}\n","import { isController, isService } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const preferConstructorInjection: Rule = {\n\tmeta: {\n\t\tid: \"architecture/prefer-constructor-injection\",\n\t\tcategory: \"architecture\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Prefer constructor injection over @Inject() property injection\",\n\t\thelp: \"Move the dependency to a constructor parameter instead of using property injection.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tif (!(isService(cls) || isController(cls))) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const prop of cls.getProperties()) {\n\t\t\t\tconst hasInjectDecorator = prop.getDecorator(\"Inject\");\n\t\t\t\tif (!hasInjectDecorator) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Property '${prop.getName()}' uses @Inject() decorator. Prefer constructor injection.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: prop.getStartLineNumber(),\n\t\t\t\t\tcolumn: 1,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t},\n};\n","import { isService } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const preferInterfaceInjection: Rule = {\n\tmeta: {\n\t\tid: \"architecture/prefer-interface-injection\",\n\t\tcategory: \"architecture\",\n\t\tseverity: \"info\",\n\t\tdescription:\n\t\t\t\"Consider injecting abstract classes instead of concrete implementations for testability\",\n\t\thelp: \"Create an abstract class and use a custom provider token for better testability.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tif (!isService(cls)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst ctor = cls.getConstructors()[0];\n\t\t\tif (!ctor) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const param of ctor.getParameters()) {\n\t\t\t\tconst typeNode = param.getTypeNode();\n\t\t\t\tif (!typeNode) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst typeText = typeNode.getText();\n\n\t\t\t\t// Skip common framework types\n\t\t\t\tif (\n\t\t\t\t\ttypeText === \"ConfigService\" ||\n\t\t\t\t\ttypeText === \"Logger\" ||\n\t\t\t\t\ttypeText === \"EventEmitter2\" ||\n\t\t\t\t\ttypeText === \"HttpService\" ||\n\t\t\t\t\ttypeText === \"JwtService\" ||\n\t\t\t\t\ttypeText === \"ModuleRef\" ||\n\t\t\t\t\ttypeText.startsWith(\"Cache\")\n\t\t\t\t) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Flag concrete service injections (ends with Service, Repository, etc.)\n\t\t\t\tif (\n\t\t\t\t\ttypeText.endsWith(\"Service\") &&\n\t\t\t\t\t!typeText.startsWith(\"Abstract\") &&\n\t\t\t\t\t!typeText.startsWith(\"I\")\n\t\t\t\t) {\n\t\t\t\t\t// Only flag if the service has many deps (suggesting it's complex enough to abstract)\n\t\t\t\t\t// This is a soft suggestion, so we keep it minimal\n\t\t\t\t\t// We only flag if both names end with Service (cross-service injection)\n\t\t\t\t\tconst className = cls.getName() ?? \"\";\n\t\t\t\t\tif (className.endsWith(\"Service\") && typeText !== className) {\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\t\tmessage: `Service '${className}' injects concrete '${typeText}'. Consider using an abstract class for testability.`,\n\t\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\t\tline: param.getStartLineNumber(),\n\t\t\t\t\t\t\tcolumn: 1,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n};\n","import type { ProjectRule } from \"../types.js\";\n\nconst MAX_DIRECT_PROVIDERS = 5;\n\nexport const requireFeatureModules: ProjectRule = {\n\tmeta: {\n\t\tid: \"architecture/require-feature-modules\",\n\t\tcategory: \"architecture\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"AppModule should import feature modules rather than declaring many providers directly\",\n\t\thelp: \"Group related providers into feature modules and import them in AppModule.\",\n\t\tscope: \"project\",\n\t},\n\n\tcheck(context) {\n\t\tconst appModule = context.moduleGraph.modules.get(\"AppModule\");\n\t\tif (!appModule) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\tappModule.providers.length > MAX_DIRECT_PROVIDERS &&\n\t\t\tappModule.imports.length < appModule.providers.length\n\t\t) {\n\t\t\tcontext.report({\n\t\t\t\tfilePath: appModule.filePath,\n\t\t\t\tmessage: `AppModule declares ${appModule.providers.length} providers directly. Group them into feature modules.`,\n\t\t\t\thelp: this.meta.help,\n\t\t\t\tline: appModule.classDeclaration.getStartLineNumber(),\n\t\t\t\tcolumn: 1,\n\t\t\t});\n\t\t}\n\t},\n};\n","import type { Rule } from \"../types.js\";\n\n// Detect deep imports into other feature modules' internals\n// e.g., import { Foo } from '../users/repositories/users.repository'\nconst INTERNAL_PATHS = [\n\t\"/repositories/\",\n\t\"/entities/\",\n\t\"/dto/\",\n\t\"/guards/\",\n\t\"/interceptors/\",\n\t\"/pipes/\",\n\t\"/strategies/\",\n];\n\nexport const requireModuleBoundaries: Rule = {\n\tmeta: {\n\t\tid: \"architecture/require-module-boundaries\",\n\t\tcategory: \"architecture\",\n\t\tseverity: \"info\",\n\t\tdescription: \"Avoid deep imports into other feature modules' internals\",\n\t\thelp: \"Import from the module's public API (barrel export) instead of reaching into its internals.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const imp of context.sourceFile.getImportDeclarations()) {\n\t\t\tconst moduleSpecifier = imp.getModuleSpecifierValue();\n\n\t\t\t// Only check relative imports\n\t\t\tif (!moduleSpecifier.startsWith(\".\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Check if the import reaches into another module's internals\n\t\t\t// Pattern: going up (../) then into another feature's subdirectory\n\t\t\tif (!moduleSpecifier.includes(\"../\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst crossesModuleBoundary = INTERNAL_PATHS.some((p) =>\n\t\t\t\tmoduleSpecifier.includes(p)\n\t\t\t);\n\n\t\t\tif (crossesModuleBoundary) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Import '${moduleSpecifier}' reaches into another module's internals.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: imp.getStartLineNumber(),\n\t\t\t\t\tcolumn: 1,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t},\n};\n","import { isController, isService } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const preferReadonlyInjection: Rule = {\n\tmeta: {\n\t\tid: \"correctness/prefer-readonly-injection\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Constructor DI parameters should be readonly to prevent accidental reassignment\",\n\t\thelp: \"Add the 'readonly' modifier to the constructor parameter.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tif (!(isService(cls) || isController(cls))) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst ctor = cls.getConstructors()[0];\n\t\t\tif (!ctor) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const param of ctor.getParameters()) {\n\t\t\t\t// Only check parameter properties (those with access modifiers)\n\t\t\t\tif (\n\t\t\t\t\t!(\n\t\t\t\t\t\tparam.hasModifier(\"private\") ||\n\t\t\t\t\t\tparam.hasModifier(\"protected\") ||\n\t\t\t\t\t\tparam.hasModifier(\"public\")\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (!param.isReadonly()) {\n\t\t\t\t\tconst nameNode = param.getNameNode();\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Constructor parameter '${param.getName()}' should be readonly.`,\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: nameNode.getStartLineNumber(),\n\t\t\t\t\t\tcolumn: nameNode.getStartLinePos() + 1,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n};\n","import { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nconst SECRET_PATTERNS = [\n\t{ pattern: /^[A-Za-z0-9+/]{40,}={0,2}$/, name: \"Base64 key\" },\n\t{ pattern: /^sk[-_][a-zA-Z0-9]{20,}$/, name: \"Secret key\" },\n\t{ pattern: /^pk[-_][a-zA-Z0-9]{20,}$/, name: \"Public key (in source)\" },\n\t{\n\t\tpattern: /^ghp_[a-zA-Z0-9]{36,}$/,\n\t\tname: \"GitHub personal access token\",\n\t},\n\t{\n\t\tpattern: /^github_pat_[a-zA-Z0-9_]{22,}$/,\n\t\tname: \"GitHub fine-grained PAT\",\n\t},\n\t{ pattern: /^gho_[a-zA-Z0-9]{36,}$/, name: \"GitHub OAuth token\" },\n\t{ pattern: /^xox[bpras]-[a-zA-Z0-9-]+$/, name: \"Slack token\" },\n\t{\n\t\tpattern: /^eyJ[a-zA-Z0-9_-]{10,}\\.[a-zA-Z0-9_-]{10,}\\./,\n\t\tname: \"JWT token\",\n\t},\n\t{ pattern: /^AKIA[0-9A-Z]{16}$/, name: \"AWS Access Key ID\" },\n\t{\n\t\tpattern: /^[a-f0-9]{64}$/,\n\t\tname: \"Hex-encoded secret (64 chars)\",\n\t},\n];\n\nconst VARIABLE_NAME_PATTERNS = [\n\t/secret/i,\n\t/password/i,\n\t/passwd/i,\n\t/api[_-]?key/i,\n\t/auth[_-]?token/i,\n\t/private[_-]?key/i,\n\t/access[_-]?key/i,\n\t/client[_-]?secret/i,\n];\n\nexport const noHardcodedSecrets: Rule = {\n\tmeta: {\n\t\tid: \"security/no-hardcoded-secrets\",\n\t\tcategory: \"security\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Detect hardcoded secrets, API keys, and tokens in source code\",\n\t\thelp: \"Move secrets to environment variables and access them via ConfigService.\",\n\t},\n\n\tcheck(context) {\n\t\t// Check all string literals in the file\n\t\tconst stringLiterals = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.StringLiteral\n\t\t);\n\n\t\tfor (const literal of stringLiterals) {\n\t\t\tconst value = literal.getLiteralValue();\n\n\t\t\t// Skip short strings and imports\n\t\t\tif (value.length < 16) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (literal.getParent()?.getKind() === SyntaxKind.ImportDeclaration) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const { pattern, name } of SECRET_PATTERNS) {\n\t\t\t\tif (pattern.test(value)) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Possible hardcoded ${name} detected.`,\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: literal.getStartLineNumber(),\n\t\t\t\t\t\tcolumn: 1,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Check variable assignments with suspicious names\n\t\tconst variableDeclarations = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.VariableDeclaration\n\t\t);\n\n\t\tfor (const decl of variableDeclarations) {\n\t\t\tconst name = decl.getName();\n\t\t\tconst initializer = decl.getInitializer();\n\t\t\tif (!initializer) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (initializer.getKind() !== SyntaxKind.StringLiteral) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst isSuspiciousName = VARIABLE_NAME_PATTERNS.some((p) => p.test(name));\n\t\t\tif (!isSuspiciousName) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst value = initializer.getText().slice(1, -1); // Remove quotes\n\t\t\t// Flag if the value looks like an actual secret (not a placeholder)\n\t\t\tif (\n\t\t\t\tvalue.length >= 8 &&\n\t\t\t\t!value.includes(\"${\") &&\n\t\t\t\t!value.startsWith(\"process.env\") &&\n\t\t\t\tvalue !== \"your-secret-here\" &&\n\t\t\t\tvalue !== \"changeme\" &&\n\t\t\t\tvalue !== \"password\"\n\t\t\t) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Variable '${name}' appears to contain a hardcoded secret.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: decl.getStartLineNumber(),\n\t\t\t\t\tcolumn: 1,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Check property assignments in objects\n\t\tconst propertyAssignments = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.PropertyAssignment\n\t\t);\n\n\t\tfor (const prop of propertyAssignments) {\n\t\t\tconst name = prop.getName();\n\t\t\tconst initializer = prop.getInitializer();\n\t\t\tif (!initializer) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (initializer.getKind() !== SyntaxKind.StringLiteral) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst isSuspiciousName = VARIABLE_NAME_PATTERNS.some((p) => p.test(name));\n\t\t\tif (!isSuspiciousName) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst value = initializer.getText().slice(1, -1);\n\t\t\tif (\n\t\t\t\tvalue.length >= 8 &&\n\t\t\t\t!value.includes(\"${\") &&\n\t\t\t\t!value.startsWith(\"process.env\")\n\t\t\t) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Property '${name}' appears to contain a hardcoded secret.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: prop.getStartLineNumber(),\n\t\t\t\t\tcolumn: 1,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t},\n};\n","import { noBarrelExportInternals } from \"./architecture/no-barrel-export-internals.js\";\nimport { noBusinessLogicInControllers } from \"./architecture/no-business-logic-in-controllers.js\";\nimport { noCircularModuleDeps } from \"./architecture/no-circular-module-deps.js\";\nimport { noGodModule } from \"./architecture/no-god-module.js\";\nimport { noGodService } from \"./architecture/no-god-service.js\";\nimport { noManualInstantiation } from \"./architecture/no-manual-instantiation.js\";\nimport { noOrmInControllers } from \"./architecture/no-orm-in-controllers.js\";\nimport { noOrmInServices } from \"./architecture/no-orm-in-services.js\";\nimport { noRepositoryInControllers } from \"./architecture/no-repository-in-controllers.js\";\nimport { preferConstructorInjection } from \"./architecture/prefer-constructor-injection.js\";\nimport { preferInterfaceInjection } from \"./architecture/prefer-interface-injection.js\";\nimport { requireFeatureModules } from \"./architecture/require-feature-modules.js\";\nimport { requireModuleBoundaries } from \"./architecture/require-module-boundaries.js\";\nimport { preferReadonlyInjection } from \"./correctness/prefer-readonly-injection.js\";\nimport { noHardcodedSecrets } from \"./security/no-hardcoded-secrets.js\";\nimport type { AnyRule } from \"./types.js\";\n\nexport const allRules: AnyRule[] = [\n\t// Architecture — file-scoped\n\tnoBusinessLogicInControllers,\n\tnoRepositoryInControllers,\n\tnoOrmInControllers,\n\tnoOrmInServices,\n\tnoManualInstantiation,\n\tpreferConstructorInjection,\n\tpreferInterfaceInjection,\n\trequireModuleBoundaries,\n\tnoBarrelExportInternals,\n\n\t// Architecture — project-scoped\n\tnoCircularModuleDeps,\n\tnoGodModule,\n\tnoGodService,\n\trequireFeatureModules,\n\n\t// Correctness\n\tpreferReadonlyInjection,\n\n\t// Security\n\tnoHardcodedSecrets,\n];\n\nexport function getRules(): AnyRule[] {\n\treturn [...allRules];\n}\n","export function getScoreLabel(score: number): string {\n\tif (score >= 90) {\n\t\treturn \"Excellent\";\n\t}\n\tif (score >= 75) {\n\t\treturn \"Good\";\n\t}\n\tif (score >= 50) {\n\t\treturn \"Fair\";\n\t}\n\tif (score >= 25) {\n\t\treturn \"Poor\";\n\t}\n\treturn \"Critical\";\n}\n","import type { Category, Severity } from \"../types/diagnostic.js\";\n\nexport const SEVERITY_WEIGHTS: Record<Severity, number> = {\n\terror: 3.0,\n\twarning: 1.5,\n\tinfo: 0.5,\n};\n\nexport const CATEGORY_MULTIPLIERS: Record<Category, number> = {\n\tsecurity: 1.5,\n\tcorrectness: 1.3,\n\tarchitecture: 1.0,\n\tperformance: 0.8,\n};\n","import type { Diagnostic } from \"../types/diagnostic.js\";\nimport type { Score } from \"../types/result.js\";\nimport { getScoreLabel } from \"./labels.js\";\nimport { CATEGORY_MULTIPLIERS, SEVERITY_WEIGHTS } from \"./weights.js\";\n\nexport function calculateScore(\n\tdiagnostics: Diagnostic[],\n\tfileCount: number\n): Score {\n\tif (fileCount === 0) {\n\t\treturn { value: 100, label: getScoreLabel(100) };\n\t}\n\n\tlet totalPenalty = 0;\n\n\tfor (const d of diagnostics) {\n\t\tconst severityWeight = SEVERITY_WEIGHTS[d.severity];\n\t\tconst categoryMultiplier = CATEGORY_MULTIPLIERS[d.category];\n\t\ttotalPenalty += severityWeight * categoryMultiplier;\n\t}\n\n\t// Normalize by file count so larger projects aren't penalized more\n\tconst normalizedPenalty = totalPenalty / fileCount;\n\n\t// Convert to 0-100 scale\n\t// A penalty of ~10 per file should bring score close to 0\n\tconst value = Math.max(\n\t\t0,\n\t\tMath.min(100, Math.round(100 - normalizedPenalty * 10))\n\t);\n\n\treturn { value, label: getScoreLabel(value) };\n}\n","import type { Category, Severity } from \"./diagnostic.js\";\n\nexport interface RuleOverride {\n\tenabled?: boolean;\n\tseverity?: Severity;\n}\n\nexport interface NestjsDoctorIgnoreConfig {\n\tfiles?: string[];\n\trules?: string[];\n}\n\nexport interface NestjsDoctorConfig {\n\tcategories?: Partial<Record<Category, boolean>>;\n\texclude?: string[];\n\tignore?: NestjsDoctorIgnoreConfig;\n\tinclude?: string[];\n\trules?: Record<string, RuleOverride | boolean>;\n\tthresholds?: {\n\t\tgodModuleProviders?: number;\n\t\tgodModuleImports?: number;\n\t\tgodServiceMethods?: number;\n\t\tgodServiceDeps?: number;\n\t};\n}\n\nexport const DEFAULT_CONFIG: NestjsDoctorConfig = {\n\tinclude: [\"**/*.ts\"],\n\texclude: [\n\t\t\"node_modules/**\",\n\t\t\"dist/**\",\n\t\t\"build/**\",\n\t\t\"coverage/**\",\n\t\t\"**/*.spec.ts\",\n\t\t\"**/*.test.ts\",\n\t\t\"**/*.e2e-spec.ts\",\n\t\t\"**/*.d.ts\",\n\t],\n};\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { DEFAULT_CONFIG, type NestjsDoctorConfig } from \"../types/config.js\";\n\nconst CONFIG_FILENAMES = [\"nestjs-doctor.config.json\", \".nestjs-doctor.json\"];\n\nexport async function loadConfig(\n\ttargetPath: string,\n\tconfigPath?: string\n): Promise<NestjsDoctorConfig> {\n\tif (configPath) {\n\t\treturn readConfigFile(configPath);\n\t}\n\n\t// Try known config file names\n\tfor (const filename of CONFIG_FILENAMES) {\n\t\ttry {\n\t\t\treturn await readConfigFile(join(targetPath, filename));\n\t\t} catch {\n\t\t\t// File doesn't exist, try next\n\t\t}\n\t}\n\n\t// Try package.json \"nestjs-doctor\" key\n\ttry {\n\t\tconst pkgRaw = await readFile(join(targetPath, \"package.json\"), \"utf-8\");\n\t\tconst pkg = JSON.parse(pkgRaw) as Record<string, unknown>;\n\t\tif (pkg[\"nestjs-doctor\"] && typeof pkg[\"nestjs-doctor\"] === \"object\") {\n\t\t\treturn mergeConfig(pkg[\"nestjs-doctor\"] as NestjsDoctorConfig);\n\t\t}\n\t} catch {\n\t\t// No package.json or no key\n\t}\n\n\treturn { ...DEFAULT_CONFIG };\n}\n\nasync function readConfigFile(path: string): Promise<NestjsDoctorConfig> {\n\tconst raw = await readFile(path, \"utf-8\");\n\tconst parsed = JSON.parse(raw) as NestjsDoctorConfig;\n\treturn mergeConfig(parsed);\n}\n\nfunction mergeConfig(userConfig: NestjsDoctorConfig): NestjsDoctorConfig {\n\treturn {\n\t\t...DEFAULT_CONFIG,\n\t\t...userConfig,\n\t\texclude: [...(DEFAULT_CONFIG.exclude ?? []), ...(userConfig.exclude ?? [])],\n\t};\n}\n","import { glob } from \"tinyglobby\";\nimport type { NestjsDoctorConfig } from \"../types/config.js\";\nimport { DEFAULT_CONFIG } from \"../types/config.js\";\n\nexport async function collectFiles(\n\ttargetPath: string,\n\tconfig: NestjsDoctorConfig = {}\n): Promise<string[]> {\n\tconst include = config.include ?? DEFAULT_CONFIG.include!;\n\tconst exclude = config.exclude ?? DEFAULT_CONFIG.exclude!;\n\n\tconst files = await glob(include, {\n\t\tcwd: targetPath,\n\t\tabsolute: true,\n\t\tignore: exclude,\n\t});\n\n\treturn files.sort();\n}\n","const REGEX_SPECIAL_CHARACTERS = /[.+^${}()|[\\]\\\\]/g;\n\nexport const compileGlobPattern = (pattern: string): RegExp => {\n\tconst normalizedPattern = pattern.replace(/\\\\/g, \"/\");\n\n\tlet regexSource = \"^\";\n\tlet characterIndex = 0;\n\n\twhile (characterIndex < normalizedPattern.length) {\n\t\tif (\n\t\t\tnormalizedPattern[characterIndex] === \"*\" &&\n\t\t\tnormalizedPattern[characterIndex + 1] === \"*\"\n\t\t) {\n\t\t\tif (normalizedPattern[characterIndex + 2] === \"/\") {\n\t\t\t\tregexSource += \"(?:.+/)?\";\n\t\t\t\tcharacterIndex += 3;\n\t\t\t} else {\n\t\t\t\tregexSource += \".*\";\n\t\t\t\tcharacterIndex += 2;\n\t\t\t}\n\t\t} else if (normalizedPattern[characterIndex] === \"*\") {\n\t\t\tregexSource += \"[^/]*\";\n\t\t\tcharacterIndex++;\n\t\t} else if (normalizedPattern[characterIndex] === \"?\") {\n\t\t\tregexSource += \"[^/]\";\n\t\t\tcharacterIndex++;\n\t\t} else {\n\t\t\tregexSource += normalizedPattern[characterIndex].replace(\n\t\t\t\tREGEX_SPECIAL_CHARACTERS,\n\t\t\t\t\"\\\\$&\"\n\t\t\t);\n\t\t\tcharacterIndex++;\n\t\t}\n\t}\n\n\tregexSource += \"$\";\n\treturn new RegExp(regexSource);\n};\n\nexport const matchGlobPattern = (\n\tfilePath: string,\n\tpattern: string\n): boolean => {\n\tconst normalizedPath = filePath.replace(/\\\\/g, \"/\");\n\treturn compileGlobPattern(pattern).test(normalizedPath);\n};\n","import type { NestjsDoctorConfig } from \"../types/config.js\";\nimport type { Diagnostic } from \"../types/diagnostic.js\";\nimport { compileGlobPattern } from \"./match-glob-pattern.js\";\n\nexport const filterIgnoredDiagnostics = (\n\tdiagnostics: Diagnostic[],\n\tconfig: NestjsDoctorConfig\n): Diagnostic[] => {\n\tconst ignoredRules = new Set(\n\t\tArray.isArray(config.ignore?.rules) ? config.ignore.rules : []\n\t);\n\tconst ignoredFilePatterns = Array.isArray(config.ignore?.files)\n\t\t? config.ignore.files.map(compileGlobPattern)\n\t\t: [];\n\n\tif (ignoredRules.size === 0 && ignoredFilePatterns.length === 0) {\n\t\treturn diagnostics;\n\t}\n\n\treturn diagnostics.filter((diagnostic) => {\n\t\tif (ignoredRules.has(diagnostic.rule)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst normalizedPath = diagnostic.filePath.replace(/\\\\/g, \"/\");\n\t\tif (ignoredFilePatterns.some((pattern) => pattern.test(normalizedPath))) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t});\n};\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { ProjectInfo } from \"../types/result.js\";\n\ninterface PackageJson {\n\tdependencies?: Record<string, string>;\n\tdevDependencies?: Record<string, string>;\n\tname?: string;\n}\n\nexport async function detectProject(targetPath: string): Promise<ProjectInfo> {\n\tconst pkgPath = join(targetPath, \"package.json\");\n\tlet pkg: PackageJson = {};\n\n\ttry {\n\t\tconst raw = await readFile(pkgPath, \"utf-8\");\n\t\tpkg = JSON.parse(raw) as PackageJson;\n\t} catch {\n\t\t// No package.json found — use defaults\n\t}\n\n\tconst allDeps = { ...pkg.dependencies, ...pkg.devDependencies };\n\n\tconst nestVersion = extractVersion(allDeps[\"@nestjs/core\"]);\n\tconst orm = detectOrm(allDeps);\n\tconst framework = detectFramework(allDeps);\n\n\treturn {\n\t\tname: pkg.name ?? \"unknown\",\n\t\tnestVersion,\n\t\torm,\n\t\tframework,\n\t\tmoduleCount: 0,\n\t\tfileCount: 0,\n\t};\n}\n\nfunction extractVersion(version: string | undefined): string | null {\n\tif (!version) {\n\t\treturn null;\n\t}\n\treturn version.replace(/[\\^~>=<]/g, \"\");\n}\n\nfunction detectOrm(deps: Record<string, string>): string | null {\n\tif (deps[\"@prisma/client\"]) {\n\t\treturn \"prisma\";\n\t}\n\tif (deps.typeorm) {\n\t\treturn \"typeorm\";\n\t}\n\tif (deps[\"@mikro-orm/core\"]) {\n\t\treturn \"mikro-orm\";\n\t}\n\tif (deps.sequelize) {\n\t\treturn \"sequelize\";\n\t}\n\tif (deps.mongoose) {\n\t\treturn \"mongoose\";\n\t}\n\tif (deps[\"drizzle-orm\"]) {\n\t\treturn \"drizzle\";\n\t}\n\treturn null;\n}\n\nfunction detectFramework(\n\tdeps: Record<string, string>\n): \"express\" | \"fastify\" | null {\n\tif (deps[\"@nestjs/platform-fastify\"]) {\n\t\treturn \"fastify\";\n\t}\n\tif (deps[\"@nestjs/platform-express\"]) {\n\t\treturn \"express\";\n\t}\n\t// Default NestJS uses express\n\tif (deps[\"@nestjs/core\"]) {\n\t\treturn \"express\";\n\t}\n\treturn null;\n}\n","import { performance } from \"node:perf_hooks\";\nimport { createAstParser } from \"../engine/ast-parser.js\";\nimport { buildModuleGraph } from \"../engine/module-graph.js\";\nimport { runRules } from \"../engine/rule-runner.js\";\nimport { resolveProviders } from \"../engine/type-resolver.js\";\nimport { allRules } from \"../rules/index.js\";\nimport { calculateScore } from \"../scorer/index.js\";\nimport type { NestjsDoctorConfig } from \"../types/config.js\";\nimport type { DiagnoseResult, DiagnoseSummary } from \"../types/result.js\";\nimport { loadConfig } from \"./config-loader.js\";\nimport { collectFiles } from \"./file-collector.js\";\nimport { filterIgnoredDiagnostics } from \"./filter-diagnostics.js\";\nimport { detectProject } from \"./project-detector.js\";\n\nexport async function scan(\n\ttargetPath: string,\n\toptions: { config?: string } = {}\n): Promise<DiagnoseResult> {\n\tconst startTime = performance.now();\n\n\tconst config = await loadConfig(targetPath, options.config);\n\tconst project = await detectProject(targetPath);\n\tconst files = await collectFiles(targetPath, config);\n\n\tproject.fileCount = files.length;\n\n\tconst astProject = createAstParser(files);\n\tconst moduleGraph = buildModuleGraph(astProject, files);\n\tconst providers = resolveProviders(astProject, files);\n\tconst rules = filterRules(config);\n\tconst rawDiagnostics = runRules(astProject, files, rules, {\n\t\tmoduleGraph,\n\t\tproviders,\n\t\tconfig,\n\t});\n\tconst diagnostics = filterIgnoredDiagnostics(rawDiagnostics, config);\n\n\tproject.moduleCount = moduleGraph.modules.size;\n\n\tconst score = calculateScore(diagnostics, files.length);\n\tconst summary = buildSummary(diagnostics);\n\tconst elapsedMs = performance.now() - startTime;\n\n\treturn { score, diagnostics, project, summary, elapsedMs };\n}\n\nfunction filterRules(config: NestjsDoctorConfig) {\n\treturn allRules.filter((rule) => {\n\t\tconst ruleConfig = config.rules?.[rule.meta.id];\n\t\tif (ruleConfig === false) {\n\t\t\treturn false;\n\t\t}\n\t\tif (typeof ruleConfig === \"object\" && ruleConfig.enabled === false) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst categoryEnabled = config.categories?.[rule.meta.category];\n\t\tif (categoryEnabled === false) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t});\n}\n\nfunction buildSummary(\n\tdiagnostics: DiagnoseResult[\"diagnostics\"]\n): DiagnoseSummary {\n\treturn {\n\t\ttotal: diagnostics.length,\n\t\terrors: diagnostics.filter((d) => d.severity === \"error\").length,\n\t\twarnings: diagnostics.filter((d) => d.severity === \"warning\").length,\n\t\tinfo: diagnostics.filter((d) => d.severity === \"info\").length,\n\t\tbyCategory: {\n\t\t\tsecurity: diagnostics.filter((d) => d.category === \"security\").length,\n\t\t\tperformance: diagnostics.filter((d) => d.category === \"performance\")\n\t\t\t\t.length,\n\t\t\tcorrectness: diagnostics.filter((d) => d.category === \"correctness\")\n\t\t\t\t.length,\n\t\t\tarchitecture: diagnostics.filter((d) => d.category === \"architecture\")\n\t\t\t\t.length,\n\t\t},\n\t};\n}\n","import { resolve } from \"node:path\";\nimport { scan } from \"../core/scanner.js\";\n\n// biome-ignore lint/performance/noBarrelFile: this is the public API surface\nexport { getRules } from \"../rules/index.js\";\nexport type {\n\tAnyRule,\n\tProjectRule,\n\tProjectRuleContext,\n\tRule,\n\tRuleContext,\n\tRuleMeta,\n} from \"../rules/types.js\";\nexport type { NestjsDoctorConfig } from \"../types/config.js\";\nexport type { Category, Diagnostic, Severity } from \"../types/diagnostic.js\";\nexport type {\n\tDiagnoseResult,\n\tDiagnoseSummary,\n\tProjectInfo,\n\tScore,\n} from \"../types/result.js\";\n\nexport async function diagnose(\n\tpath: string,\n\toptions: { config?: string } = {}\n) {\n\tconst targetPath = resolve(path);\n\treturn await scan(targetPath, options);\n}\n"],"mappings":"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"}