nestjs-doctor 0.1.4 → 0.2.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.
package/README.md CHANGED
@@ -64,22 +64,37 @@ No config, no plugins, no setup.
64
64
 
65
65
  ## CI
66
66
 
67
- Exit code `1` when errors are found. Use `--score` for threshold checks:
67
+ Install as a devDependency for deterministic, cacheable CI runs:
68
68
 
69
69
  ```bash
70
- SCORE=$(npx nestjs-doctor@latest . --score)
71
- if [ "$SCORE" -lt 75 ]; then
72
- echo "Health score $SCORE is below threshold (75)"
73
- exit 1
74
- fi
70
+ pnpm add -D nestjs-doctor
75
71
  ```
76
72
 
73
+ Then use `--min-score` to enforce a minimum health score:
74
+
75
+ ```bash
76
+ npx nestjs-doctor . --min-score 75
77
+ ```
78
+
79
+ Or add it as a script in `package.json`:
80
+
81
+ ```json
82
+ {
83
+ "scripts": {
84
+ "health": "nestjs-doctor . --min-score 75"
85
+ }
86
+ }
87
+ ```
88
+
89
+ Exit codes: `1` when the score is below threshold or errors are found, `2` for invalid input. Works with all output modes (`--score`, `--json`, default).
90
+
77
91
  ```
78
92
  Usage: nestjs-doctor [directory] [options]
79
93
 
80
94
  --verbose Show file paths and line numbers per diagnostic
81
95
  --score Output only the numeric score (for CI)
82
96
  --json JSON output (for tooling)
97
+ --min-score <n> Minimum passing score (0-100). Exits with code 1 if below threshold
83
98
  --config <p> Path to config file
84
99
  -h, --help Show help
85
100
  ```
@@ -92,6 +107,7 @@ Optional. Create `nestjs-doctor.config.json` in your project root:
92
107
 
93
108
  ```json
94
109
  {
110
+ "minScore": 75,
95
111
  "ignore": {
96
112
  "rules": ["architecture/no-orm-in-services"],
97
113
  "files": ["src/generated/**"]
@@ -117,6 +133,7 @@ Or use a `"nestjs-doctor"` key in `package.json`.
117
133
  |-----|------|-------------|
118
134
  | `include` | `string[]` | Glob patterns to scan (default: `["**/*.ts"]`) |
119
135
  | `exclude` | `string[]` | Glob patterns to skip (default includes `node_modules`, `dist`, test files) |
136
+ | `minScore` | `number` | Minimum passing score (0-100). Exits with code 1 if below threshold |
120
137
  | `ignore.rules` | `string[]` | Rule IDs to suppress |
121
138
  | `ignore.files` | `string[]` | Glob patterns for files whose diagnostics are hidden |
122
139
  | `rules` | `Record<string, boolean>` | Enable/disable individual rules |
@@ -53,6 +53,7 @@ interface NestjsDoctorConfig {
53
53
  exclude?: string[];
54
54
  ignore?: NestjsDoctorIgnoreConfig;
55
55
  include?: string[];
56
+ minScore?: number;
56
57
  rules?: Record<string, RuleOverride | boolean>;
57
58
  thresholds?: {
58
59
  godModuleProviders?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/engine/module-graph.ts","../../src/engine/type-resolver.ts","../../src/types/diagnostic.ts","../../src/types/config.ts","../../src/rules/types.ts","../../src/rules/index.ts","../../src/types/errors.ts","../../src/types/result.ts","../../src/api/index.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/engine/module-graph.ts","../../src/engine/type-resolver.ts","../../src/types/diagnostic.ts","../../src/types/config.ts","../../src/rules/types.ts","../../src/rules/index.ts","../../src/types/errors.ts","../../src/types/result.ts","../../src/api/index.ts"],"mappings":"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"}
@@ -53,6 +53,7 @@ interface NestjsDoctorConfig {
53
53
  exclude?: string[];
54
54
  ignore?: NestjsDoctorIgnoreConfig;
55
55
  include?: string[];
56
+ minScore?: number;
56
57
  rules?: Record<string, RuleOverride | boolean>;
57
58
  thresholds?: {
58
59
  godModuleProviders?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/engine/module-graph.ts","../../src/engine/type-resolver.ts","../../src/types/diagnostic.ts","../../src/types/config.ts","../../src/rules/types.ts","../../src/rules/index.ts","../../src/types/errors.ts","../../src/types/result.ts","../../src/api/index.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/engine/module-graph.ts","../../src/engine/type-resolver.ts","../../src/types/diagnostic.ts","../../src/types/config.ts","../../src/rules/types.ts","../../src/rules/index.ts","../../src/types/errors.ts","../../src/types/result.ts","../../src/api/index.ts"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["GENERIC_TYPE_REGEX","HTTP_DECORATORS","DOTTED_SUFFIX_REGEX","GENERIC_TYPE_REGEX","ORM_TYPES","extractTypeName","DOTTED_SUFFIX_REGEX","GENERIC_TYPE_REGEX","extractTypeName","LOOP_KINDS"],"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/no-async-without-await.ts","../../src/rules/correctness/no-duplicate-module-metadata.ts","../../src/rules/correctness/no-duplicate-routes.ts","../../src/rules/correctness/no-empty-handlers.ts","../../src/rules/correctness/no-missing-filter-catch.ts","../../src/rules/correctness/no-missing-guard-method.ts","../../src/rules/correctness/no-missing-injectable.ts","../../src/rules/correctness/no-missing-interceptor-method.ts","../../src/rules/correctness/no-missing-module-decorator.ts","../../src/rules/correctness/no-missing-pipe-method.ts","../../src/rules/correctness/prefer-readonly-injection.ts","../../src/rules/correctness/require-inject-decorator.ts","../../src/rules/correctness/require-lifecycle-interface.ts","../../src/rules/performance/no-blocking-constructor.ts","../../src/rules/performance/no-dynamic-require.ts","../../src/rules/performance/no-logging-in-loops.ts","../../src/rules/performance/no-orphan-modules.ts","../../src/rules/performance/no-query-in-loop.ts","../../src/rules/performance/no-sync-io.ts","../../src/rules/performance/no-unnecessary-async.ts","../../src/rules/performance/no-unused-module-exports.ts","../../src/rules/performance/no-unused-providers.ts","../../src/rules/performance/prefer-pagination.ts","../../src/rules/security/no-csrf-disabled.ts","../../src/rules/security/no-dangerous-redirects.ts","../../src/rules/security/no-eval.ts","../../src/rules/security/no-exposed-env-vars.ts","../../src/rules/security/no-exposed-stack-trace.ts","../../src/rules/security/no-hardcoded-secrets.ts","../../src/rules/security/no-unsafe-raw-query.ts","../../src/rules/security/no-weak-crypto.ts","../../src/rules/security/no-wildcard-cors.ts","../../src/rules/security/require-auth-guard.ts","../../src/rules/security/require-validation-pipe.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/types/errors.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\tproviderToModule: 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\t// Build inverse index: provider name → module\n\tconst providerToModule = new Map<string, ModuleNode>();\n\tfor (const mod of modules.values()) {\n\t\tfor (const provider of mod.providers) {\n\t\t\tproviderToModule.set(provider, mod);\n\t\t}\n\t}\n\n\treturn { modules, edges, providerToModule };\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\treturn graph.providerToModule.get(providerName);\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 RuleError {\n\terror: unknown;\n\truleId: string;\n}\n\nexport interface RunRulesResult {\n\tdiagnostics: Diagnostic[];\n\terrors: RuleError[];\n}\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): RunRulesResult {\n\tconst diagnostics: Diagnostic[] = [];\n\tconst errors: RuleError[] = [];\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 (error) {\n\t\t\t\terrors.push({ ruleId: rule.meta.id, error });\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 (error) {\n\t\t\terrors.push({ ruleId: rule.meta.id, error });\n\t\t}\n\t}\n\n\treturn { diagnostics, errors };\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 { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nexport const noAsyncWithoutAwait: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-async-without-await\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Async functions/methods should contain at least one await expression\",\n\t\thelp: \"Either add an await expression or remove the async keyword.\",\n\t},\n\n\tcheck(context) {\n\t\t// Check class methods\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tfor (const method of cls.getMethods()) {\n\t\t\t\tif (!method.isAsync()) {\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\tconst awaitExpressions = body.getDescendantsOfKind(\n\t\t\t\t\tSyntaxKind.AwaitExpression\n\t\t\t\t);\n\n\t\t\t\t// Exclude nested async functions/arrow functions\n\t\t\t\tconst directAwaits = awaitExpressions.filter((expr) => {\n\t\t\t\t\tlet parent = expr.getParent();\n\t\t\t\t\twhile (parent && parent !== body) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tparent.getKind() === SyntaxKind.ArrowFunction ||\n\t\t\t\t\t\t\tparent.getKind() === SyntaxKind.FunctionExpression ||\n\t\t\t\t\t\t\tparent.getKind() === SyntaxKind.FunctionDeclaration\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tparent = parent.getParent();\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tif (directAwaits.length === 0) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Async method '${method.getName()}()' has no await expression.`,\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\n\t\t// Check standalone functions\n\t\tfor (const fn of context.sourceFile.getFunctions()) {\n\t\t\tif (!fn.isAsync()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst body = fn.getBody();\n\t\t\tif (!body) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst awaitExpressions = body.getDescendantsOfKind(\n\t\t\t\tSyntaxKind.AwaitExpression\n\t\t\t);\n\n\t\t\tconst directAwaits = awaitExpressions.filter((expr) => {\n\t\t\t\tlet parent = expr.getParent();\n\t\t\t\twhile (parent && parent !== body) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tparent.getKind() === SyntaxKind.ArrowFunction ||\n\t\t\t\t\t\tparent.getKind() === SyntaxKind.FunctionExpression ||\n\t\t\t\t\t\tparent.getKind() === SyntaxKind.FunctionDeclaration\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tparent = parent.getParent();\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tif (directAwaits.length === 0) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Async function '${fn.getName() ?? \"anonymous\"}()' has no await expression.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: fn.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 { isModule } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nconst MODULE_ARRAY_PROPS = [\"providers\", \"controllers\", \"imports\", \"exports\"];\n\nexport const noDuplicateModuleMetadata: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-duplicate-module-metadata\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Same identifier should not appear twice in a module metadata array\",\n\t\thelp: \"Remove the duplicate entry from the module metadata.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tif (!isModule(cls)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\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 args = moduleDecorator.getArguments()[0];\n\t\t\tif (!args || args.getKind() !== SyntaxKind.ObjectLiteralExpression) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst obj = args.asKind(SyntaxKind.ObjectLiteralExpression);\n\t\t\tif (!obj) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const propName of MODULE_ARRAY_PROPS) {\n\t\t\t\tconst prop = obj.getProperty(propName);\n\t\t\t\tif (!prop) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst arrayLiteral = prop.getChildrenOfKind(\n\t\t\t\t\tSyntaxKind.ArrayLiteralExpression\n\t\t\t\t)[0];\n\t\t\t\tif (!arrayLiteral) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst seen = new Set<string>();\n\t\t\t\tfor (const element of arrayLiteral.getElements()) {\n\t\t\t\t\tconst text = element.getText();\n\t\t\t\t\tif (seen.has(text)) {\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: `Duplicate '${text}' in @Module() ${propName} array.`,\n\t\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\t\tline: element.getStartLineNumber(),\n\t\t\t\t\t\t\tcolumn: 1,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tseen.add(text);\n\t\t\t\t\t}\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 HTTP_METHOD_DECORATORS = new Set([\n\t\"Get\",\n\t\"Post\",\n\t\"Put\",\n\t\"Delete\",\n\t\"Patch\",\n\t\"All\",\n\t\"Head\",\n\t\"Options\",\n]);\n\nexport const noDuplicateRoutes: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-duplicate-routes\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Same HTTP method + route path should not appear twice in a single controller\",\n\t\thelp: \"Remove or rename one of the duplicate route handlers.\",\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 routeMap = new Map<string, string>();\n\n\t\t\tfor (const method of cls.getMethods()) {\n\t\t\t\tfor (const decorator of method.getDecorators()) {\n\t\t\t\t\tconst decoratorName = decorator.getName();\n\t\t\t\t\tif (!HTTP_METHOD_DECORATORS.has(decoratorName)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst args = decorator.getArguments();\n\t\t\t\t\tconst path = args.length > 0 ? args[0].getText() : '\"\"';\n\t\t\t\t\tconst routeKey = `${decoratorName}:${path}`;\n\n\t\t\t\t\tconst existing = routeMap.get(routeKey);\n\t\t\t\t\tif (existing) {\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: `Duplicate route: @${decoratorName}(${path}) is already defined in '${existing}()'.`,\n\t\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\t\tline: method.getStartLineNumber(),\n\t\t\t\t\t\t\tcolumn: 1,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\trouteMap.set(routeKey, method.getName());\n\t\t\t\t\t}\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 HTTP_DECORATORS = new Set([\n\t\"Get\",\n\t\"Post\",\n\t\"Put\",\n\t\"Delete\",\n\t\"Patch\",\n\t\"All\",\n\t\"Head\",\n\t\"Options\",\n]);\n\nexport const noEmptyHandlers: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-empty-handlers\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"warning\",\n\t\tdescription: \"Controller HTTP handlers should not have empty bodies\",\n\t\thelp: \"Add implementation to the handler method or remove it if unnecessary.\",\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\tconst hasHttpDecorator = method\n\t\t\t\t\t.getDecorators()\n\t\t\t\t\t.some((d) => HTTP_DECORATORS.has(d.getName()));\n\n\t\t\t\tif (!hasHttpDecorator) {\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\tconst statements = body.getStatements();\n\t\t\t\tif (statements.length === 0) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Handler '${method.getName()}()' has an empty body.`,\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 { hasDecorator } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const noMissingFilterCatch: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-missing-filter-catch\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Exception filter classes decorated with @Catch() must implement the catch() method\",\n\t\thelp: \"Add a catch(exception, host: ArgumentsHost) method to the filter class.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tif (!hasDecorator(cls, \"Catch\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst hasCatchMethod = cls\n\t\t\t\t.getMethods()\n\t\t\t\t.some((m) => m.getName() === \"catch\");\n\t\t\tif (!hasCatchMethod) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Exception filter '${cls.getName()}' has @Catch() but is missing the 'catch()' method.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: cls.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 { hasDecorator } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const noMissingGuardMethod: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-missing-guard-method\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"error\",\n\t\tdescription: \"Guard classes must implement the canActivate() method\",\n\t\thelp: \"Add a canActivate(context: ExecutionContext) method to the guard class.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tconst name = cls.getName() ?? \"\";\n\t\t\tif (!name.endsWith(\"Guard\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!hasDecorator(cls, \"Injectable\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst hasCanActivate = cls\n\t\t\t\t.getMethods()\n\t\t\t\t.some((m) => m.getName() === \"canActivate\");\n\t\t\tif (!hasCanActivate) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Guard '${name}' is missing the 'canActivate()' method.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: cls.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 { ClassDeclaration } from \"ts-morph\";\nimport type { ProjectRule } from \"../types.js\";\n\nexport const noMissingInjectable: ProjectRule = {\n\tmeta: {\n\t\tid: \"correctness/no-missing-injectable\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Class listed in a module's providers must have the @Injectable() decorator\",\n\t\thelp: \"Add @Injectable() decorator to the class.\",\n\t\tscope: \"project\",\n\t},\n\n\tcheck(context) {\n\t\tconst providerNames = new Set(\n\t\t\t[...context.providers.values()].map((p) => p.name)\n\t\t);\n\n\t\t// Build a class-name-to-declaration index for O(1) lookups\n\t\tconst classIndex = new Map<\n\t\t\tstring,\n\t\t\t{ cls: ClassDeclaration; filePath: string }[]\n\t\t>();\n\t\tfor (const filePath of context.files) {\n\t\t\tconst sourceFile = context.project.getSourceFile(filePath);\n\t\t\tif (!sourceFile) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const cls of sourceFile.getClasses()) {\n\t\t\t\tconst name = cls.getName();\n\t\t\t\tif (name) {\n\t\t\t\t\tconst entries = classIndex.get(name) ?? [];\n\t\t\t\t\tentries.push({ cls, filePath });\n\t\t\t\t\tclassIndex.set(name, entries);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (const mod of context.moduleGraph.modules.values()) {\n\t\t\tfor (const providerName of mod.providers) {\n\t\t\t\tif (providerNames.has(providerName)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// O(1) lookup instead of iterating all files\n\t\t\t\tconst classEntries = classIndex.get(providerName);\n\t\t\t\tif (!classEntries) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfor (const { cls, filePath } of classEntries) {\n\t\t\t\t\tif (!cls.getDecorator(\"Injectable\")) {\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tfilePath,\n\t\t\t\t\t\t\tmessage: `Class '${providerName}' is listed in '${mod.name}' providers but is missing @Injectable() decorator.`,\n\t\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\t\tline: cls.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 { hasDecorator } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const noMissingInterceptorMethod: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-missing-interceptor-method\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"error\",\n\t\tdescription: \"Interceptor classes must implement the intercept() method\",\n\t\thelp: \"Add an intercept(context: ExecutionContext, next: CallHandler) method to the interceptor class.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tconst name = cls.getName() ?? \"\";\n\t\t\tif (!name.endsWith(\"Interceptor\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!hasDecorator(cls, \"Injectable\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst hasIntercept = cls\n\t\t\t\t.getMethods()\n\t\t\t\t.some((m) => m.getName() === \"intercept\");\n\t\t\tif (!hasIntercept) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Interceptor '${name}' is missing the 'intercept()' method.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: cls.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 { hasDecorator } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const noMissingModuleDecorator: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-missing-module-decorator\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"warning\",\n\t\tdescription: \"Classes named *Module should have a @Module() decorator\",\n\t\thelp: \"Add @Module({}) decorator to the class, or rename it if it is not a NestJS module.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tconst name = cls.getName() ?? \"\";\n\t\t\tif (!name.endsWith(\"Module\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Skip common non-NestJS module names\n\t\t\tif (name === \"Module\" || name === \"DynamicModule\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!hasDecorator(cls, \"Module\")) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Class '${name}' is named like a module but is missing the @Module() decorator.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: cls.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 { hasDecorator } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const noMissingPipeMethod: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-missing-pipe-method\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"error\",\n\t\tdescription: \"Pipe classes must implement the transform() method\",\n\t\thelp: \"Add a transform(value: any, metadata: ArgumentMetadata) method to the pipe class.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tconst name = cls.getName() ?? \"\";\n\t\t\tif (!name.endsWith(\"Pipe\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!hasDecorator(cls, \"Injectable\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst hasTransform = cls\n\t\t\t\t.getMethods()\n\t\t\t\t.some((m) => m.getName() === \"transform\");\n\t\t\tif (!hasTransform) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Pipe '${name}' is missing the 'transform()' method.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: cls.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 { hasDecorator } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const requireInjectDecorator: Rule = {\n\tmeta: {\n\t\tid: \"correctness/require-inject-decorator\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Constructor parameters without type annotations must have @Inject() decorator for NestJS DI to resolve them\",\n\t\thelp: \"Add a type annotation or @Inject() decorator to the constructor parameter.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tif (\n\t\t\t\t!(hasDecorator(cls, \"Injectable\") || hasDecorator(cls, \"Controller\"))\n\t\t\t) {\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\tconst hasInject = param\n\t\t\t\t\t.getDecorators()\n\t\t\t\t\t.some((d) => d.getName() === \"Inject\");\n\n\t\t\t\tif (!(typeNode || hasInject)) {\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()}' in '${cls.getName()}' has no type annotation and no @Inject() decorator — NestJS cannot resolve it.`,\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: param.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 { Rule } from \"../types.js\";\n\nconst LIFECYCLE_MAP: Record<string, string> = {\n\tonModuleInit: \"OnModuleInit\",\n\tonModuleDestroy: \"OnModuleDestroy\",\n\tonApplicationBootstrap: \"OnApplicationBootstrap\",\n\tonApplicationShutdown: \"OnApplicationShutdown\",\n\tbeforeApplicationShutdown: \"BeforeApplicationShutdown\",\n};\n\nexport const requireLifecycleInterface: Rule = {\n\tmeta: {\n\t\tid: \"correctness/require-lifecycle-interface\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Classes with lifecycle methods should implement the corresponding NestJS interface\",\n\t\thelp: \"Add 'implements OnModuleInit' (or the appropriate interface) to the class declaration.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tconst implementedInterfaces = cls.getImplements().map((i) => i.getText());\n\n\t\t\tfor (const method of cls.getMethods()) {\n\t\t\t\tconst methodName = method.getName();\n\t\t\t\tconst expectedInterface = LIFECYCLE_MAP[methodName];\n\t\t\t\tif (!expectedInterface) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (!implementedInterfaces.some((i) => i.includes(expectedInterface))) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Class '${cls.getName()}' has '${methodName}()' but does not implement '${expectedInterface}'.`,\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 { SyntaxKind } from \"ts-morph\";\nimport { hasDecorator } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nconst BLOCKING_KINDS = new Set([\n\tSyntaxKind.ForStatement,\n\tSyntaxKind.ForOfStatement,\n\tSyntaxKind.ForInStatement,\n\tSyntaxKind.WhileStatement,\n\tSyntaxKind.DoStatement,\n\tSyntaxKind.AwaitExpression,\n]);\n\nexport const noBlockingConstructor: Rule = {\n\tmeta: {\n\t\tid: \"performance/no-blocking-constructor\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Constructors in Injectable/Controller classes should not contain heavy operations\",\n\t\thelp: \"Move complex initialization logic to onModuleInit() lifecycle method instead.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tif (\n\t\t\t\t!(hasDecorator(cls, \"Injectable\") || hasDecorator(cls, \"Controller\"))\n\t\t\t) {\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\tconst body = ctor.getBody();\n\t\t\tif (!body) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const descendant of body.getDescendants()) {\n\t\t\t\tif (BLOCKING_KINDS.has(descendant.getKind())) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Constructor in '${cls.getName()}' contains blocking operation — use onModuleInit() instead.`,\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: ctor.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\t},\n};\n","import { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nexport const noDynamicRequire: Rule = {\n\tmeta: {\n\t\tid: \"performance/no-dynamic-require\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Dynamic require() with variable arguments prevents bundler optimization\",\n\t\thelp: \"Use static import statements or dynamic import() with string literals.\",\n\t},\n\n\tcheck(context) {\n\t\tconst callExpressions = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.CallExpression\n\t\t);\n\n\t\tfor (const call of callExpressions) {\n\t\t\tif (call.getExpression().getText() !== \"require\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst args = call.getArguments();\n\t\t\tif (args.length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst firstArg = args[0];\n\t\t\t// Allow string literals (static requires)\n\t\t\tif (firstArg.getKind() === SyntaxKind.StringLiteral) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tcontext.report({\n\t\t\t\tfilePath: context.filePath,\n\t\t\t\tmessage:\n\t\t\t\t\t\"Dynamic require() with non-literal argument prevents bundler optimization.\",\n\t\t\t\thelp: this.meta.help,\n\t\t\t\tline: call.getStartLineNumber(),\n\t\t\t\tcolumn: 1,\n\t\t\t});\n\t\t}\n\t},\n};\n","import { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nconst LOOP_KINDS = new Set([\n\tSyntaxKind.ForStatement,\n\tSyntaxKind.ForOfStatement,\n\tSyntaxKind.ForInStatement,\n\tSyntaxKind.WhileStatement,\n\tSyntaxKind.DoStatement,\n]);\n\nconst CONSOLE_METHODS = new Set([\n\t\"log\",\n\t\"warn\",\n\t\"error\",\n\t\"info\",\n\t\"debug\",\n\t\"trace\",\n]);\n\nexport const noLoggingInLoops: Rule = {\n\tmeta: {\n\t\tid: \"performance/no-logging-in-loops\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"info\",\n\t\tdescription:\n\t\t\t\"Logging inside loops can degrade performance and flood log output\",\n\t\thelp: \"Move logging outside the loop, aggregate results, or use conditional logging.\",\n\t},\n\n\tcheck(context) {\n\t\tconst callExpressions = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.CallExpression\n\t\t);\n\n\t\tfor (const call of callExpressions) {\n\t\t\tconst exprText = call.getExpression().getText();\n\n\t\t\t// Check for console.log/warn/error etc.\n\t\t\tconst isConsoleCall =\n\t\t\t\texprText.startsWith(\"console.\") &&\n\t\t\t\tCONSOLE_METHODS.has(exprText.split(\".\")[1]);\n\n\t\t\t// Check for this.logger.log/warn/error etc.\n\t\t\tconst isLoggerCall =\n\t\t\t\texprText.startsWith(\"this.logger.\") &&\n\t\t\t\tCONSOLE_METHODS.has(exprText.split(\".\")[2]);\n\n\t\t\tif (!(isConsoleCall || isLoggerCall)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Check if inside a loop\n\t\t\tlet parent = call.getParent();\n\t\t\twhile (parent) {\n\t\t\t\tif (LOOP_KINDS.has(parent.getKind())) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Logging call '${exprText}()' inside a loop.`,\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: call.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\tparent = parent.getParent();\n\t\t\t}\n\t\t}\n\t},\n};\n","import type { ProjectRule } from \"../types.js\";\n\nexport const noOrphanModules: ProjectRule = {\n\tmeta: {\n\t\tid: \"performance/no-orphan-modules\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"info\",\n\t\tdescription:\n\t\t\t\"Module is never imported by any other module and may be dead code\",\n\t\thelp: \"Import this module in another module or remove it if it is unused.\",\n\t\tscope: \"project\",\n\t},\n\n\tcheck(context) {\n\t\t// Collect all module names that are imported by at least one other module\n\t\tconst importedModules = new Set<string>();\n\t\tfor (const mod of context.moduleGraph.modules.values()) {\n\t\t\tfor (const imp of mod.imports) {\n\t\t\t\timportedModules.add(imp);\n\t\t\t}\n\t\t}\n\n\t\tfor (const mod of context.moduleGraph.modules.values()) {\n\t\t\t// Skip AppModule — it's the root and is never imported\n\t\t\tif (mod.name === \"AppModule\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!importedModules.has(mod.name)) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: mod.filePath,\n\t\t\t\t\tmessage: `Module '${mod.name}' is never imported by any other module.`,\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 { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nconst LOOP_KINDS = new Set([\n\tSyntaxKind.ForStatement,\n\tSyntaxKind.ForOfStatement,\n\tSyntaxKind.ForInStatement,\n\tSyntaxKind.WhileStatement,\n\tSyntaxKind.DoStatement,\n]);\n\nexport const noQueryInLoop: Rule = {\n\tmeta: {\n\t\tid: \"performance/no-query-in-loop\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Await expressions inside loops cause N+1 query patterns — batch or parallelize instead\",\n\t\thelp: \"Use Promise.all(), batch queries, or database-level operations (e.g., WHERE IN) instead of awaiting in a loop.\",\n\t},\n\n\tcheck(context) {\n\t\tconst awaitExpressions = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.AwaitExpression\n\t\t);\n\n\t\tfor (const awaitExpr of awaitExpressions) {\n\t\t\tlet parent = awaitExpr.getParent();\n\t\t\twhile (parent) {\n\t\t\t\tif (LOOP_KINDS.has(parent.getKind())) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\"Await expression inside a loop — potential N+1 query pattern.\",\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: awaitExpr.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\tparent = parent.getParent();\n\t\t\t}\n\t\t}\n\t},\n};\n","import { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nconst SYNC_IO_METHODS = new Set([\n\t\"readFileSync\",\n\t\"writeFileSync\",\n\t\"existsSync\",\n\t\"mkdirSync\",\n\t\"readdirSync\",\n\t\"statSync\",\n\t\"accessSync\",\n\t\"appendFileSync\",\n\t\"copyFileSync\",\n\t\"renameSync\",\n\t\"unlinkSync\",\n]);\n\nexport const noSyncIo: Rule = {\n\tmeta: {\n\t\tid: \"performance/no-sync-io\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Synchronous I/O calls block the event loop and should be avoided in NestJS applications\",\n\t\thelp: \"Use the async variant (e.g., readFile instead of readFileSync) with await.\",\n\t},\n\n\tcheck(context) {\n\t\tconst callExpressions = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.CallExpression\n\t\t);\n\n\t\tfor (const call of callExpressions) {\n\t\t\tconst expr = call.getExpression();\n\t\t\tconst methodName = expr.getText().split(\".\").pop() ?? \"\";\n\n\t\t\tif (!SYNC_IO_METHODS.has(methodName)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tcontext.report({\n\t\t\t\tfilePath: context.filePath,\n\t\t\t\tmessage: `Synchronous I/O call '${methodName}()' blocks the event loop.`,\n\t\t\t\thelp: this.meta.help,\n\t\t\t\tline: call.getStartLineNumber(),\n\t\t\t\tcolumn: 1,\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 noUnnecessaryAsync: Rule = {\n\tmeta: {\n\t\tid: \"performance/no-unnecessary-async\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"info\",\n\t\tdescription:\n\t\t\t\"Async functions without await add unnecessary promise wrapping overhead\",\n\t\thelp: \"Remove the async keyword or add await if async behavior is needed.\",\n\t},\n\n\tcheck(context) {\n\t\t// Check class methods\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tfor (const method of cls.getMethods()) {\n\t\t\t\tif (!method.isAsync()) {\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\tconst awaitExpressions = body.getDescendantsOfKind(\n\t\t\t\t\tSyntaxKind.AwaitExpression\n\t\t\t\t);\n\n\t\t\t\t// Only count awaits directly in this function, not nested\n\t\t\t\tconst directAwaits = awaitExpressions.filter((expr) => {\n\t\t\t\t\tlet parent = expr.getParent();\n\t\t\t\t\twhile (parent && parent !== body) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tparent.getKind() === SyntaxKind.ArrowFunction ||\n\t\t\t\t\t\t\tparent.getKind() === SyntaxKind.FunctionExpression ||\n\t\t\t\t\t\t\tparent.getKind() === SyntaxKind.FunctionDeclaration\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tparent = parent.getParent();\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tif (directAwaits.length === 0) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Async method '${method.getName()}()' contains no await — unnecessary promise wrapping.`,\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 type { ProjectRule } from \"../types.js\";\n\nexport const noUnusedModuleExports: ProjectRule = {\n\tmeta: {\n\t\tid: \"performance/no-unused-module-exports\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"info\",\n\t\tdescription:\n\t\t\t\"Module exports a provider that no importing module actually uses\",\n\t\thelp: \"Remove the unused export or use the provider in an importing module.\",\n\t\tscope: \"project\",\n\t},\n\n\tcheck(context) {\n\t\t// For each module, check if its exports are actually used by importing modules\n\t\tfor (const mod of context.moduleGraph.modules.values()) {\n\t\t\tif (mod.exports.length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Find modules that import this one\n\t\t\tconst importingModules: string[] = [];\n\t\t\tfor (const otherMod of context.moduleGraph.modules.values()) {\n\t\t\t\tif (otherMod.name === mod.name) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (otherMod.imports.includes(mod.name)) {\n\t\t\t\t\timportingModules.push(otherMod.name);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If no module imports this one, all exports are unused\n\t\t\tif (importingModules.length === 0) {\n\t\t\t\tcontinue; // no-orphan-modules handles this case\n\t\t\t}\n\n\t\t\t// Collect all provider dependencies from importing modules\n\t\t\tconst usedProviders = new Set<string>();\n\t\t\tfor (const importerName of importingModules) {\n\t\t\t\tconst importer = context.moduleGraph.modules.get(importerName);\n\t\t\t\tif (!importer) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Check which providers in the importing module depend on exported providers\n\t\t\t\tfor (const providerName of importer.providers) {\n\t\t\t\t\tconst provider = context.providers.get(providerName);\n\t\t\t\t\tif (!provider) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tfor (const dep of provider.dependencies) {\n\t\t\t\t\t\tusedProviders.add(dep);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Check controllers too\n\t\t\t\tfor (const controllerName of importer.controllers) {\n\t\t\t\t\tfor (const filePath of context.files) {\n\t\t\t\t\t\tconst sourceFile = context.project.getSourceFile(filePath);\n\t\t\t\t\t\tif (!sourceFile) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (const cls of sourceFile.getClasses()) {\n\t\t\t\t\t\t\tif (cls.getName() !== controllerName) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst ctor = cls.getConstructors()[0];\n\t\t\t\t\t\t\tif (!ctor) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfor (const param of ctor.getParameters()) {\n\t\t\t\t\t\t\t\tconst typeText = param.getType().getText();\n\t\t\t\t\t\t\t\tconst simpleName =\n\t\t\t\t\t\t\t\t\ttypeText.split(\".\").pop()?.split(\"<\")[0] ?? typeText;\n\t\t\t\t\t\t\t\tusedProviders.add(simpleName);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const exportedName of mod.exports) {\n\t\t\t\tif (!usedProviders.has(exportedName)) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: mod.filePath,\n\t\t\t\t\t\tmessage: `Module '${mod.name}' exports '${exportedName}' but no importing module uses it.`,\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: mod.classDeclaration.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 { ProjectRule } from \"../types.js\";\n\nconst SKIP_SUFFIXES = [\n\t\"Guard\",\n\t\"Interceptor\",\n\t\"Filter\",\n\t\"Middleware\",\n\t\"Strategy\",\n];\n\nexport const noUnusedProviders: ProjectRule = {\n\tmeta: {\n\t\tid: \"performance/no-unused-providers\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Injectable providers that are never injected by any other provider may be dead code\",\n\t\thelp: \"Remove the unused provider or inject it where needed.\",\n\t\tscope: \"project\",\n\t},\n\n\tcheck(context) {\n\t\t// Collect all dependency names from all providers\n\t\tconst allDependencies = new Set<string>();\n\t\tfor (const provider of context.providers.values()) {\n\t\t\tfor (const dep of provider.dependencies) {\n\t\t\t\tallDependencies.add(dep);\n\t\t\t}\n\t\t}\n\n\t\t// Also collect controller dependencies\n\t\tfor (const filePath of context.files) {\n\t\t\tconst sourceFile = context.project.getSourceFile(filePath);\n\t\t\tif (!sourceFile) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const cls of sourceFile.getClasses()) {\n\t\t\t\tif (!cls.getDecorator(\"Controller\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst ctor = cls.getConstructors()[0];\n\t\t\t\tif (!ctor) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tfor (const param of ctor.getParameters()) {\n\t\t\t\t\tconst typeText = param.getType().getText();\n\t\t\t\t\tconst simpleName =\n\t\t\t\t\t\ttypeText.split(\".\").pop()?.split(\"<\")[0] ?? typeText;\n\t\t\t\t\tallDependencies.add(simpleName);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (const provider of context.providers.values()) {\n\t\t\tconst name = provider.name;\n\n\t\t\t// Skip common infrastructure patterns\n\t\t\tif (SKIP_SUFFIXES.some((suffix) => name.endsWith(suffix))) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Skip if it's used as a dependency somewhere\n\t\t\tif (allDependencies.has(name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Skip if it's in module exports (it may be used externally)\n\t\t\tlet isExported = false;\n\t\t\tfor (const mod of context.moduleGraph.modules.values()) {\n\t\t\t\tif (mod.exports.includes(name)) {\n\t\t\t\t\tisExported = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (isExported) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tcontext.report({\n\t\t\t\tfilePath: provider.filePath,\n\t\t\t\tmessage: `Provider '${name}' is never injected by any other provider or controller.`,\n\t\t\t\thelp: this.meta.help,\n\t\t\t\tline: provider.classDeclaration.getStartLineNumber(),\n\t\t\t\tcolumn: 1,\n\t\t\t});\n\t\t}\n\t},\n};\n","import { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nconst FIND_MANY_METHODS = new Set([\"findMany\", \"find\"]);\nconst PAGINATION_ARGS = new Set([\n\t\"take\",\n\t\"skip\",\n\t\"limit\",\n\t\"offset\",\n\t\"page\",\n\t\"perPage\",\n\t\"pageSize\",\n]);\n\nexport const preferPagination: Rule = {\n\tmeta: {\n\t\tid: \"performance/prefer-pagination\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"info\",\n\t\tdescription:\n\t\t\t\"Database findMany/find queries should include pagination to avoid loading excessive data\",\n\t\thelp: \"Add pagination arguments (take/skip, limit/offset) to the query.\",\n\t},\n\n\tcheck(context) {\n\t\tconst callExpressions = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.CallExpression\n\t\t);\n\n\t\tfor (const call of callExpressions) {\n\t\t\tconst expr = call.getExpression();\n\t\t\tconst methodName = expr.getText().split(\".\").pop() ?? \"\";\n\n\t\t\tif (!FIND_MANY_METHODS.has(methodName)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst args = call.getArguments();\n\n\t\t\t// No args at all — no pagination\n\t\t\tif (args.length === 0) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `'${methodName}()' called without pagination arguments.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: call.getStartLineNumber(),\n\t\t\t\t\tcolumn: 1,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Check if object argument contains pagination keys\n\t\t\tconst firstArg = args[0];\n\t\t\tif (firstArg.getKind() !== SyntaxKind.ObjectLiteralExpression) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst obj = firstArg.asKind(SyntaxKind.ObjectLiteralExpression);\n\t\t\tif (!obj) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst props = obj\n\t\t\t\t.getProperties()\n\t\t\t\t.map((p) => p.getText().split(\":\")[0].trim());\n\t\t\tconst hasPagination = props.some((p) => PAGINATION_ARGS.has(p));\n\n\t\t\tif (!hasPagination) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `'${methodName}()' called without pagination arguments.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: call.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 noCsrfDisabled: Rule = {\n\tmeta: {\n\t\tid: \"security/no-csrf-disabled\",\n\t\tcategory: \"security\",\n\t\tseverity: \"error\",\n\t\tdescription: \"CSRF protection should not be explicitly disabled\",\n\t\thelp: \"Enable CSRF protection or remove the explicit disabling of it.\",\n\t},\n\n\tcheck(context) {\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\tif (name !== \"csrf\" && name !== \"csrfProtection\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\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.getText() === \"false\") {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `CSRF protection explicitly disabled (${name}: false).`,\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 { SyntaxKind } from \"ts-morph\";\nimport { isController } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const noDangerousRedirects: Rule = {\n\tmeta: {\n\t\tid: \"security/no-dangerous-redirects\",\n\t\tcategory: \"security\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Redirects using user-controlled input (from @Query/@Param) are an open redirect vulnerability\",\n\t\thelp: \"Validate redirect URLs against an allowlist of safe destinations.\",\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// Collect parameter names decorated with @Query or @Param\n\t\t\t\tconst userInputParams = new Set<string>();\n\t\t\t\tfor (const param of method.getParameters()) {\n\t\t\t\t\tconst hasUserInputDecorator = param\n\t\t\t\t\t\t.getDecorators()\n\t\t\t\t\t\t.some((d) => d.getName() === \"Query\" || d.getName() === \"Param\");\n\t\t\t\t\tif (hasUserInputDecorator) {\n\t\t\t\t\t\tuserInputParams.add(param.getName());\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (userInputParams.size === 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Check for res.redirect() calls with user input\n\t\t\t\tconst callExpressions = method.getDescendantsOfKind(\n\t\t\t\t\tSyntaxKind.CallExpression\n\t\t\t\t);\n\n\t\t\t\tfor (const call of callExpressions) {\n\t\t\t\t\tconst exprText = call.getExpression().getText();\n\t\t\t\t\tif (!exprText.endsWith(\"redirect\")) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const arg of call.getArguments()) {\n\t\t\t\t\t\tconst argText = arg.getText();\n\t\t\t\t\t\tif (userInputParams.has(argText)) {\n\t\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\t\t\tmessage: `Redirect uses user-controlled parameter '${argText}' — open redirect risk.`,\n\t\t\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\t\t\tline: call.getStartLineNumber(),\n\t\t\t\t\t\t\t\tcolumn: 1,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Check for @Redirect() decorator with dynamic value\n\t\t\t\tconst redirectDecorator = method\n\t\t\t\t\t.getDecorators()\n\t\t\t\t\t.find((d) => d.getName() === \"Redirect\");\n\t\t\t\tif (redirectDecorator) {\n\t\t\t\t\tfor (const arg of redirectDecorator.getArguments()) {\n\t\t\t\t\t\tconst argText = arg.getText();\n\t\t\t\t\t\tif (userInputParams.has(argText)) {\n\t\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\t\t\tmessage: `@Redirect() uses user-controlled parameter '${argText}' — open redirect risk.`,\n\t\t\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\t\t\tline: redirectDecorator.getStartLineNumber(),\n\t\t\t\t\t\t\t\tcolumn: 1,\n\t\t\t\t\t\t\t});\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 { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nexport const noEval: Rule = {\n\tmeta: {\n\t\tid: \"security/no-eval\",\n\t\tcategory: \"security\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Usage of eval() or new Function() is a security risk and should be avoided\",\n\t\thelp: \"Refactor to avoid eval() and new Function(). Use safer alternatives like JSON.parse() or a sandboxed interpreter.\",\n\t},\n\n\tcheck(context) {\n\t\tconst callExpressions = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.CallExpression\n\t\t);\n\n\t\tfor (const call of callExpressions) {\n\t\t\tconst exprText = call.getExpression().getText();\n\t\t\tif (exprText === \"eval\") {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: \"Usage of eval() is a security risk.\",\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: call.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 for new Function()\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\tif (expr.getExpression().getText() === \"Function\") {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: \"Usage of new Function() is a security risk.\",\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 { SyntaxKind } from \"ts-morph\";\nimport { hasDecorator } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const noExposedEnvVars: Rule = {\n\tmeta: {\n\t\tid: \"security/no-exposed-env-vars\",\n\t\tcategory: \"security\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Use NestJS ConfigService instead of direct process.env access in Injectable/Controller classes\",\n\t\thelp: \"Inject ConfigService and use configService.get('VAR_NAME') instead of process.env.VAR_NAME.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tif (\n\t\t\t\t!(hasDecorator(cls, \"Injectable\") || hasDecorator(cls, \"Controller\"))\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst propertyAccesses = cls.getDescendantsOfKind(\n\t\t\t\tSyntaxKind.PropertyAccessExpression\n\t\t\t);\n\n\t\t\tfor (const access of propertyAccesses) {\n\t\t\t\tconst expr = access.getExpression();\n\t\t\t\tif (expr.getText() !== \"process.env\") {\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: `Direct 'process.env.${access.getName()}' access in '${cls.getName()}'. Use ConfigService instead.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: access.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\nconst ERROR_VAR_PATTERN = /^(error|err|e|ex|exception)$/;\n\nexport const noExposedStackTrace: Rule = {\n\tmeta: {\n\t\tid: \"security/no-exposed-stack-trace\",\n\t\tcategory: \"security\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Stack traces should not be exposed in responses — they leak internal implementation details\",\n\t\thelp: \"Log the stack trace internally and return a generic error message to the client.\",\n\t},\n\n\tcheck(context) {\n\t\tconst propertyAccesses = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.PropertyAccessExpression\n\t\t);\n\n\t\tfor (const access of propertyAccesses) {\n\t\t\tif (access.getName() !== \"stack\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst exprText = access.getExpression().getText();\n\t\t\t// Match common error variable names accessing .stack\n\t\t\tif (\n\t\t\t\t!(\n\t\t\t\t\tERROR_VAR_PATTERN.test(exprText) ||\n\t\t\t\t\texprText.endsWith(\".error\") ||\n\t\t\t\t\texprText.endsWith(\".err\")\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Check if it's being returned or passed to a response\n\t\t\tconst parent = access.getParent();\n\t\t\tif (!parent) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst parentKind = parent.getKind();\n\t\t\tif (\n\t\t\t\tparentKind === SyntaxKind.ReturnStatement ||\n\t\t\t\tparentKind === SyntaxKind.PropertyAssignment ||\n\t\t\t\tparentKind === SyntaxKind.ShorthandPropertyAssignment ||\n\t\t\t\tparentKind === SyntaxKind.CallExpression\n\t\t\t) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Stack trace '${exprText}.stack' may be exposed in response — leaks implementation details.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: access.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\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\nconst PLACEHOLDER_VALUES = new Set([\n\t\"your-secret-here\",\n\t\"changeme\",\n\t\"password\",\n]);\n\nfunction isSuspiciousValue(value: string): boolean {\n\treturn (\n\t\tvalue.length >= 8 &&\n\t\t!value.includes(\"${\") &&\n\t\t!value.startsWith(\"process.env\") &&\n\t\t!PLACEHOLDER_VALUES.has(value)\n\t);\n}\n\nfunction hasSuspiciousName(name: string): boolean {\n\treturn VARIABLE_NAME_PATTERNS.some((p) => p.test(name));\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 declarations and property 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 || initializer.getKind() !== SyntaxKind.StringLiteral) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!hasSuspiciousName(name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst value = initializer.getText().slice(1, -1);\n\t\t\tif (isSuspiciousValue(value)) {\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\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 || initializer.getKind() !== SyntaxKind.StringLiteral) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!hasSuspiciousName(name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst value = initializer.getText().slice(1, -1);\n\t\t\tif (isSuspiciousValue(value)) {\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 { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nconst RAW_QUERY_METHODS = new Set([\n\t\"$executeRaw\",\n\t\"$queryRaw\",\n\t\"$executeRawUnsafe\",\n\t\"$queryRawUnsafe\",\n\t\"query\",\n]);\n\nexport const noUnsafeRawQuery: Rule = {\n\tmeta: {\n\t\tid: \"security/no-unsafe-raw-query\",\n\t\tcategory: \"security\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Raw SQL queries with template literal interpolation are a SQL injection risk\",\n\t\thelp: \"Use parameterized queries or Prisma's tagged template (Prisma.sql`...`) instead of string interpolation.\",\n\t},\n\n\tcheck(context) {\n\t\tconst callExpressions = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.CallExpression\n\t\t);\n\n\t\tfor (const call of callExpressions) {\n\t\t\tconst expr = call.getExpression();\n\t\t\tconst methodName = expr.getText().split(\".\").pop() ?? \"\";\n\n\t\t\tif (!RAW_QUERY_METHODS.has(methodName)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst args = call.getArguments();\n\t\t\tif (args.length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst firstArg = args[0];\n\t\t\t// Check for template literals with expressions (interpolation)\n\t\t\tif (firstArg.getKind() === SyntaxKind.TemplateExpression) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Raw SQL query '${methodName}()' uses template literal interpolation — SQL injection risk.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: call.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\nconst WEAK_ALGORITHMS = new Set([\"md5\", \"sha1\"]);\n\nexport const noWeakCrypto: Rule = {\n\tmeta: {\n\t\tid: \"security/no-weak-crypto\",\n\t\tcategory: \"security\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Weak hashing algorithms (MD5, SHA1) should not be used for security purposes\",\n\t\thelp: \"Use a stronger algorithm like SHA-256 or bcrypt for password hashing.\",\n\t},\n\n\tcheck(context) {\n\t\tconst callExpressions = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.CallExpression\n\t\t);\n\n\t\tfor (const call of callExpressions) {\n\t\t\tconst exprText = call.getExpression().getText();\n\t\t\tif (!exprText.endsWith(\"createHash\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst args = call.getArguments();\n\t\t\tif (args.length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst firstArg = args[0];\n\t\t\tif (firstArg.getKind() !== SyntaxKind.StringLiteral) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst algorithm = firstArg.getText().slice(1, -1).toLowerCase();\n\t\t\tif (WEAK_ALGORITHMS.has(algorithm)) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Weak hashing algorithm '${algorithm}' used in createHash().`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: call.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 noWildcardCors: Rule = {\n\tmeta: {\n\t\tid: \"security/no-wildcard-cors\",\n\t\tcategory: \"security\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"CORS should not be configured with origin: '*' or origin: true — allows any domain\",\n\t\thelp: \"Specify allowed origins explicitly instead of using wildcard CORS.\",\n\t},\n\n\tcheck(context) {\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\tif (prop.getName() !== \"origin\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\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\tconst text = initializer.getText();\n\t\t\tif (text === \"'*'\" || text === '\"*\"' || text === \"true\") {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `CORS configured with origin: ${text} — allows requests from any domain.`,\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 { isController } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const requireAuthGuard: Rule = {\n\tmeta: {\n\t\tid: \"security/require-auth-guard\",\n\t\tcategory: \"security\",\n\t\tseverity: \"info\",\n\t\tdescription:\n\t\t\t\"Controller classes should have @UseGuards() at class or method level\",\n\t\thelp: \"Add @UseGuards(AuthGuard) to the controller class or its handler methods.\",\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\t// Check class-level guards\n\t\t\tconst hasClassGuard = cls\n\t\t\t\t.getDecorators()\n\t\t\t\t.some((d) => d.getName() === \"UseGuards\");\n\t\t\tif (hasClassGuard) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Check if any method has guards\n\t\t\tconst hasMethodGuard = cls\n\t\t\t\t.getMethods()\n\t\t\t\t.some((method) =>\n\t\t\t\t\tmethod.getDecorators().some((d) => d.getName() === \"UseGuards\")\n\t\t\t\t);\n\t\t\tif (hasMethodGuard) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tcontext.report({\n\t\t\t\tfilePath: context.filePath,\n\t\t\t\tmessage: `Controller '${cls.getName()}' has no @UseGuards() protection.`,\n\t\t\t\thelp: this.meta.help,\n\t\t\t\tline: cls.getStartLineNumber(),\n\t\t\t\tcolumn: 1,\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\nexport const requireValidationPipe: Rule = {\n\tmeta: {\n\t\tid: \"security/require-validation-pipe\",\n\t\tcategory: \"security\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Handlers with @Body() parameters should have validation via @UsePipes(ValidationPipe)\",\n\t\thelp: \"Add @UsePipes(new ValidationPipe()) to the method or controller, or set up global validation.\",\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 hasClassPipe = cls\n\t\t\t\t.getDecorators()\n\t\t\t\t.some((d) => d.getName() === \"UsePipes\");\n\n\t\t\tfor (const method of cls.getMethods()) {\n\t\t\t\t// Check if any parameter has @Body() decorator\n\t\t\t\tconst hasBodyParam = method\n\t\t\t\t\t.getParameters()\n\t\t\t\t\t.some((param) =>\n\t\t\t\t\t\tparam.getDecorators().some((d) => d.getName() === \"Body\")\n\t\t\t\t\t);\n\t\t\t\tif (!hasBodyParam) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (hasClassPipe) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst hasMethodPipe = method\n\t\t\t\t\t.getDecorators()\n\t\t\t\t\t.some((d) => d.getName() === \"UsePipes\");\n\t\t\t\tif (hasMethodPipe) {\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: `Handler '${method.getName()}()' has @Body() parameter but no validation pipe.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: method.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 { noAsyncWithoutAwait } from \"./correctness/no-async-without-await.js\";\nimport { noDuplicateModuleMetadata } from \"./correctness/no-duplicate-module-metadata.js\";\nimport { noDuplicateRoutes } from \"./correctness/no-duplicate-routes.js\";\nimport { noEmptyHandlers } from \"./correctness/no-empty-handlers.js\";\nimport { noMissingFilterCatch } from \"./correctness/no-missing-filter-catch.js\";\nimport { noMissingGuardMethod } from \"./correctness/no-missing-guard-method.js\";\nimport { noMissingInjectable } from \"./correctness/no-missing-injectable.js\";\nimport { noMissingInterceptorMethod } from \"./correctness/no-missing-interceptor-method.js\";\nimport { noMissingModuleDecorator } from \"./correctness/no-missing-module-decorator.js\";\nimport { noMissingPipeMethod } from \"./correctness/no-missing-pipe-method.js\";\nimport { preferReadonlyInjection } from \"./correctness/prefer-readonly-injection.js\";\nimport { requireInjectDecorator } from \"./correctness/require-inject-decorator.js\";\nimport { requireLifecycleInterface } from \"./correctness/require-lifecycle-interface.js\";\nimport { noBlockingConstructor } from \"./performance/no-blocking-constructor.js\";\nimport { noDynamicRequire } from \"./performance/no-dynamic-require.js\";\nimport { noLoggingInLoops } from \"./performance/no-logging-in-loops.js\";\nimport { noOrphanModules } from \"./performance/no-orphan-modules.js\";\nimport { noQueryInLoop } from \"./performance/no-query-in-loop.js\";\nimport { noSyncIo } from \"./performance/no-sync-io.js\";\nimport { noUnnecessaryAsync } from \"./performance/no-unnecessary-async.js\";\nimport { noUnusedModuleExports } from \"./performance/no-unused-module-exports.js\";\nimport { noUnusedProviders } from \"./performance/no-unused-providers.js\";\nimport { preferPagination } from \"./performance/prefer-pagination.js\";\nimport { noCsrfDisabled } from \"./security/no-csrf-disabled.js\";\nimport { noDangerousRedirects } from \"./security/no-dangerous-redirects.js\";\nimport { noEval } from \"./security/no-eval.js\";\nimport { noExposedEnvVars } from \"./security/no-exposed-env-vars.js\";\nimport { noExposedStackTrace } from \"./security/no-exposed-stack-trace.js\";\nimport { noHardcodedSecrets } from \"./security/no-hardcoded-secrets.js\";\nimport { noUnsafeRawQuery } from \"./security/no-unsafe-raw-query.js\";\nimport { noWeakCrypto } from \"./security/no-weak-crypto.js\";\nimport { noWildcardCors } from \"./security/no-wildcard-cors.js\";\nimport { requireAuthGuard } from \"./security/require-auth-guard.js\";\nimport { requireValidationPipe } from \"./security/require-validation-pipe.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 — file-scoped\n\tpreferReadonlyInjection,\n\trequireLifecycleInterface,\n\tnoEmptyHandlers,\n\tnoDuplicateRoutes,\n\tnoMissingGuardMethod,\n\tnoMissingPipeMethod,\n\tnoMissingFilterCatch,\n\tnoMissingInterceptorMethod,\n\tnoAsyncWithoutAwait,\n\tnoDuplicateModuleMetadata,\n\tnoMissingModuleDecorator,\n\trequireInjectDecorator,\n\n\t// Correctness — project-scoped\n\tnoMissingInjectable,\n\n\t// Security\n\tnoHardcodedSecrets,\n\tnoWildcardCors,\n\tnoUnsafeRawQuery,\n\trequireAuthGuard,\n\tnoEval,\n\tnoWeakCrypto,\n\tnoExposedEnvVars,\n\trequireValidationPipe,\n\tnoCsrfDisabled,\n\tnoExposedStackTrace,\n\tnoDangerousRedirects,\n\n\t// Performance — file-scoped\n\tnoSyncIo,\n\tnoQueryInLoop,\n\tnoLoggingInLoops,\n\tnoUnnecessaryAsync,\n\tnoBlockingConstructor,\n\tpreferPagination,\n\tnoDynamicRequire,\n\n\t// Performance — project-scoped\n\tnoUnusedProviders,\n\tnoUnusedModuleExports,\n\tnoOrphanModules,\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\n/**\n * Calculates a health score from 0-100 based on diagnostics and file count.\n *\n * Scoring formula: `score = 100 - (totalPenalty / fileCount) * PENALTY_SCALE`\n *\n * Each diagnostic contributes a penalty of `severityWeight * categoryMultiplier`:\n * - A security error costs 3.0 * 1.5 = 4.5 penalty points\n * - A performance info costs 0.5 * 0.8 = 0.4 penalty points\n *\n * The penalty is normalized by file count so that a 10-file project and a\n * 500-file project with the same issue density receive similar scores.\n *\n * PENALTY_SCALE (10) was calibrated so that an average of ~1 error per file\n * (normalized penalty ≈ 10) brings the score to 0. In practice:\n * - 1 error per 10 files → penalty/file ≈ 0.45 → score ≈ 95 (Excellent)\n * - 1 error per 3 files → penalty/file ≈ 1.5 → score ≈ 85 (Good)\n * - 1 error per file → penalty/file ≈ 4.5 → score ≈ 55 (Fair)\n * - 2 errors per file → penalty/file ≈ 9.0 → score ≈ 10 (Critical)\n */\nconst PENALTY_SCALE = 10;\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\tconst normalizedPenalty = totalPenalty / fileCount;\n\tconst value = Math.max(\n\t\t0,\n\t\tMath.min(100, Math.round(100 - normalizedPenalty * PENALTY_SCALE))\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\n/**\n * Merges user config with defaults.\n *\n * Merge semantics:\n * - `include`: user replaces defaults entirely (user likely wants a specific scope)\n * - `exclude`: user values are appended to defaults (additive, keeps safe defaults)\n * - `ignore.rules`: user replaces defaults (no default ignored rules)\n * - `ignore.files`: user replaces defaults (no default ignored files)\n * - `rules`, `categories`, `thresholds`: shallow-merged with user taking precedence\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 { join } from \"node:path\";\nimport { glob } from \"tinyglobby\";\nimport type { NestjsDoctorConfig } from \"../types/config.js\";\nimport { DEFAULT_CONFIG } from \"../types/config.js\";\nimport type { MonorepoInfo } from \"./project-detector.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\nexport async function collectMonorepoFiles(\n\ttargetPath: string,\n\tmonorepo: MonorepoInfo,\n\tconfig: NestjsDoctorConfig = {}\n): Promise<Map<string, string[]>> {\n\tconst result = new Map<string, string[]>();\n\n\tfor (const [name, root] of monorepo.projects) {\n\t\tconst projectPath = join(targetPath, root);\n\t\tconst files = await collectFiles(projectPath, config);\n\t\tresult.set(name, files);\n\t}\n\n\treturn result;\n}\n","import picomatch from \"picomatch\";\n\nexport const compileGlobPattern = (pattern: string): RegExp => {\n\treturn picomatch.makeRe(pattern, { windows: false });\n};\n\nexport const matchGlobPattern = (\n\tfilePath: string,\n\tpattern: string\n): boolean => {\n\treturn picomatch.isMatch(filePath, pattern, { windows: false });\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\ninterface NestCliProject {\n\tcompilerOptions?: Record<string, unknown>;\n\tentryFile?: string;\n\troot?: string;\n\tsourceRoot?: string;\n\ttype?: string;\n}\n\ninterface NestCliJson {\n\tmonorepo?: boolean;\n\tprojects?: Record<string, NestCliProject>;\n\troot?: string;\n\tsourceRoot?: string;\n}\n\nexport interface MonorepoInfo {\n\tprojects: Map<string, string>; // name -> root path (relative)\n}\n\nexport async function detectMonorepo(\n\ttargetPath: string\n): Promise<MonorepoInfo | null> {\n\tconst cliPath = join(targetPath, \"nest-cli.json\");\n\n\ttry {\n\t\tconst raw = await readFile(cliPath, \"utf-8\");\n\t\tconst config = JSON.parse(raw) as NestCliJson;\n\n\t\tif (!(config.monorepo && config.projects)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst projects = new Map<string, string>();\n\t\tfor (const [name, project] of Object.entries(config.projects)) {\n\t\t\tconst root = project.root ?? name;\n\t\t\tprojects.set(name, root);\n\t\t}\n\n\t\tif (projects.size === 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn { projects };\n\t} catch {\n\t\treturn null;\n\t}\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 { join } from \"node:path\";\nimport { 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 { Diagnostic } from \"../types/diagnostic.js\";\nimport type {\n\tDiagnoseResult,\n\tDiagnoseSummary,\n\tMonorepoResult,\n\tRuleErrorInfo,\n\tSubProjectResult,\n} from \"../types/result.js\";\nimport { loadConfig } from \"./config-loader.js\";\nimport { collectFiles, collectMonorepoFiles } from \"./file-collector.js\";\nimport { filterIgnoredDiagnostics } from \"./filter-diagnostics.js\";\nimport { detectMonorepo, detectProject } from \"./project-detector.js\";\n\nfunction formatRuleError(error: unknown): string {\n\tif (error instanceof Error) {\n\t\treturn error.message;\n\t}\n\treturn String(error);\n}\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\tconst astProject = createAstParser(files);\n\tconst moduleGraph = buildModuleGraph(astProject, files);\n\tconst providers = resolveProviders(astProject, files);\n\tconst rules = filterRules(config);\n\tconst { diagnostics: rawDiagnostics, errors } = runRules(\n\t\tastProject,\n\t\tfiles,\n\t\trules,\n\t\t{ moduleGraph, providers, config }\n\t);\n\tconst diagnostics = filterIgnoredDiagnostics(rawDiagnostics, config);\n\n\tconst score = calculateScore(diagnostics, files.length);\n\tconst summary = buildSummary(diagnostics);\n\tconst ruleErrors: RuleErrorInfo[] = errors.map((e) => ({\n\t\truleId: e.ruleId,\n\t\terror: formatRuleError(e.error),\n\t}));\n\tconst elapsedMs = performance.now() - startTime;\n\n\treturn {\n\t\tscore,\n\t\tdiagnostics,\n\t\tproject: {\n\t\t\t...project,\n\t\t\tfileCount: files.length,\n\t\t\tmoduleCount: moduleGraph.modules.size,\n\t\t},\n\t\tsummary,\n\t\truleErrors,\n\t\telapsedMs,\n\t};\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\nexport async function scanMonorepo(\n\ttargetPath: string,\n\toptions: { config?: string } = {}\n): Promise<MonorepoResult> {\n\tconst startTime = performance.now();\n\tconst monorepo = await detectMonorepo(targetPath);\n\n\tif (!monorepo) {\n\t\t// Not a monorepo — scan as single project and wrap result\n\t\tconst result = await scan(targetPath, options);\n\t\treturn {\n\t\t\tisMonorepo: false,\n\t\t\tsubProjects: [{ name: \"default\", result }],\n\t\t\tcombined: result,\n\t\t\telapsedMs: result.elapsedMs,\n\t\t};\n\t}\n\n\tconst rootConfig = await loadConfig(targetPath, options.config);\n\tconst filesByProject = await collectMonorepoFiles(\n\t\ttargetPath,\n\t\tmonorepo,\n\t\trootConfig\n\t);\n\n\tconst subProjects: SubProjectResult[] = [];\n\tconst allDiagnostics: Diagnostic[] = [];\n\tconst allRuleErrors: RuleErrorInfo[] = [];\n\tlet totalFiles = 0;\n\n\tfor (const [name, files] of filesByProject) {\n\t\tif (files.length === 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst projectPath = join(targetPath, monorepo.projects.get(name)!);\n\t\tconst project = await detectProject(projectPath);\n\n\t\t// Load per-project config if available, falling back to root config\n\t\tconst projectConfig = await loadConfigWithFallback(projectPath, rootConfig);\n\n\t\tconst astProject = createAstParser(files);\n\t\tconst moduleGraph = buildModuleGraph(astProject, files);\n\t\tconst providers = resolveProviders(astProject, files);\n\t\tconst rules = filterRules(projectConfig);\n\t\tconst { diagnostics: rawDiagnostics, errors } = runRules(\n\t\t\tastProject,\n\t\t\tfiles,\n\t\t\trules,\n\t\t\t{ moduleGraph, providers, config: projectConfig }\n\t\t);\n\t\tconst diagnostics = filterIgnoredDiagnostics(rawDiagnostics, projectConfig);\n\n\t\tconst score = calculateScore(diagnostics, files.length);\n\t\tconst summary = buildSummary(diagnostics);\n\t\tconst ruleErrors: RuleErrorInfo[] = errors.map((e) => ({\n\t\t\truleId: e.ruleId,\n\t\t\terror: formatRuleError(e.error),\n\t\t}));\n\n\t\tconst result: DiagnoseResult = {\n\t\t\tscore,\n\t\t\tdiagnostics,\n\t\t\tproject: {\n\t\t\t\t...project,\n\t\t\t\tfileCount: files.length,\n\t\t\t\tmoduleCount: moduleGraph.modules.size,\n\t\t\t},\n\t\t\tsummary,\n\t\t\truleErrors,\n\t\t\telapsedMs: 0,\n\t\t};\n\n\t\tsubProjects.push({ name, result });\n\t\tallDiagnostics.push(...diagnostics);\n\t\tallRuleErrors.push(...ruleErrors);\n\t\ttotalFiles += files.length;\n\t}\n\n\tconst combinedScore = calculateScore(allDiagnostics, totalFiles);\n\tconst combinedSummary = buildSummary(allDiagnostics);\n\tconst elapsedMs = performance.now() - startTime;\n\n\tconst combined: DiagnoseResult = {\n\t\tscore: combinedScore,\n\t\tdiagnostics: allDiagnostics,\n\t\tproject: {\n\t\t\tname: \"monorepo\",\n\t\t\tnestVersion: subProjects[0]?.result.project.nestVersion ?? null,\n\t\t\torm: subProjects[0]?.result.project.orm ?? null,\n\t\t\tframework: subProjects[0]?.result.project.framework ?? null,\n\t\t\tfileCount: totalFiles,\n\t\t\tmoduleCount: subProjects.reduce(\n\t\t\t\t(sum, sp) => sum + sp.result.project.moduleCount,\n\t\t\t\t0\n\t\t\t),\n\t\t},\n\t\tsummary: combinedSummary,\n\t\truleErrors: allRuleErrors,\n\t\telapsedMs,\n\t};\n\n\treturn {\n\t\tisMonorepo: true,\n\t\tsubProjects,\n\t\tcombined,\n\t\telapsedMs,\n\t};\n}\n\nasync function loadConfigWithFallback(\n\tprojectPath: string,\n\tfallback: NestjsDoctorConfig\n): Promise<NestjsDoctorConfig> {\n\ttry {\n\t\treturn await loadConfig(projectPath);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction buildSummary(diagnostics: Diagnostic[]): DiagnoseSummary {\n\tconst summary: DiagnoseSummary = {\n\t\ttotal: 0,\n\t\terrors: 0,\n\t\twarnings: 0,\n\t\tinfo: 0,\n\t\tbyCategory: {\n\t\t\tsecurity: 0,\n\t\t\tperformance: 0,\n\t\t\tcorrectness: 0,\n\t\t\tarchitecture: 0,\n\t\t},\n\t};\n\n\tfor (const d of diagnostics) {\n\t\tsummary.total++;\n\t\tif (d.severity === \"error\") {\n\t\t\tsummary.errors++;\n\t\t} else if (d.severity === \"warning\") {\n\t\t\tsummary.warnings++;\n\t\t} else {\n\t\t\tsummary.info++;\n\t\t}\n\t\tsummary.byCategory[d.category]++;\n\t}\n\n\treturn summary;\n}\n","export class NestjsDoctorError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"NestjsDoctorError\";\n\t}\n}\n\nexport class ConfigurationError extends NestjsDoctorError {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"ConfigurationError\";\n\t}\n}\n\nexport class ScanError extends NestjsDoctorError {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"ScanError\";\n\t}\n}\n\nexport class ValidationError extends NestjsDoctorError {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"ValidationError\";\n\t}\n}\n","import { existsSync, statSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { scan, scanMonorepo } from \"../core/scanner.js\";\nimport { ValidationError } from \"../types/errors.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 {\n\tConfigurationError,\n\tNestjsDoctorError,\n\tScanError,\n\tValidationError,\n} from \"../types/errors.js\";\nexport type {\n\tDiagnoseResult,\n\tDiagnoseSummary,\n\tMonorepoResult,\n\tProjectInfo,\n\tRuleErrorInfo,\n\tScore,\n\tSubProjectResult,\n} from \"../types/result.js\";\n\nfunction validatePath(path: string): string {\n\tif (!path || path.trim() === \"\") {\n\t\tthrow new ValidationError(\n\t\t\t\"Path must be a non-empty string. Received an empty path.\"\n\t\t);\n\t}\n\n\tconst resolved = resolve(path);\n\n\tif (!existsSync(resolved)) {\n\t\tthrow new ValidationError(`Path does not exist: ${resolved}`);\n\t}\n\n\tconst stat = statSync(resolved);\n\tif (!stat.isDirectory()) {\n\t\tthrow new ValidationError(\n\t\t\t`Path must be a directory, not a file: ${resolved}`\n\t\t);\n\t}\n\n\treturn resolved;\n}\n\n/**\n * Scans a single NestJS project and returns a health diagnostic result.\n *\n * @param path - Path to the NestJS project root directory.\n * @param options - Optional configuration: `config` specifies a path to a config file.\n * @returns A `DiagnoseResult` containing the health score, diagnostics, and summary.\n * @throws {ValidationError} If the path is empty, doesn't exist, or isn't a directory.\n */\nexport async function diagnose(\n\tpath: string,\n\toptions: { config?: string } = {}\n) {\n\tconst targetPath = validatePath(path);\n\treturn await scan(targetPath, options);\n}\n\n/**\n * Scans a NestJS monorepo and returns per-project and combined diagnostics.\n *\n * Auto-detects monorepo structure from `nest-cli.json`. If the target is not a\n * monorepo, falls back to a single-project scan wrapped in the monorepo result format.\n *\n * @param path - Path to the monorepo root directory.\n * @param options - Optional configuration: `config` specifies a path to a config file.\n * @returns A `MonorepoResult` with sub-project results and combined score.\n * @throws {ValidationError} If the path is empty, doesn't exist, or isn't a directory.\n */\nexport async function diagnoseMonorepo(\n\tpath: string,\n\toptions: { config?: string } = {}\n) {\n\tconst targetPath = validatePath(path);\n\treturn await scanMonorepo(targetPath, options);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["GENERIC_TYPE_REGEX","HTTP_DECORATORS","DOTTED_SUFFIX_REGEX","GENERIC_TYPE_REGEX","ORM_TYPES","extractTypeName","DOTTED_SUFFIX_REGEX","GENERIC_TYPE_REGEX","extractTypeName","LOOP_KINDS"],"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/no-async-without-await.ts","../../src/rules/correctness/no-duplicate-module-metadata.ts","../../src/rules/correctness/no-duplicate-routes.ts","../../src/rules/correctness/no-empty-handlers.ts","../../src/rules/correctness/no-missing-filter-catch.ts","../../src/rules/correctness/no-missing-guard-method.ts","../../src/rules/correctness/no-missing-injectable.ts","../../src/rules/correctness/no-missing-interceptor-method.ts","../../src/rules/correctness/no-missing-module-decorator.ts","../../src/rules/correctness/no-missing-pipe-method.ts","../../src/rules/correctness/prefer-readonly-injection.ts","../../src/rules/correctness/require-inject-decorator.ts","../../src/rules/correctness/require-lifecycle-interface.ts","../../src/rules/performance/no-blocking-constructor.ts","../../src/rules/performance/no-dynamic-require.ts","../../src/rules/performance/no-logging-in-loops.ts","../../src/rules/performance/no-orphan-modules.ts","../../src/rules/performance/no-query-in-loop.ts","../../src/rules/performance/no-sync-io.ts","../../src/rules/performance/no-unnecessary-async.ts","../../src/rules/performance/no-unused-module-exports.ts","../../src/rules/performance/no-unused-providers.ts","../../src/rules/performance/prefer-pagination.ts","../../src/rules/security/no-csrf-disabled.ts","../../src/rules/security/no-dangerous-redirects.ts","../../src/rules/security/no-eval.ts","../../src/rules/security/no-exposed-env-vars.ts","../../src/rules/security/no-exposed-stack-trace.ts","../../src/rules/security/no-hardcoded-secrets.ts","../../src/rules/security/no-unsafe-raw-query.ts","../../src/rules/security/no-weak-crypto.ts","../../src/rules/security/no-wildcard-cors.ts","../../src/rules/security/require-auth-guard.ts","../../src/rules/security/require-validation-pipe.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/types/errors.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\tproviderToModule: 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\t// Build inverse index: provider name → module\n\tconst providerToModule = new Map<string, ModuleNode>();\n\tfor (const mod of modules.values()) {\n\t\tfor (const provider of mod.providers) {\n\t\t\tproviderToModule.set(provider, mod);\n\t\t}\n\t}\n\n\treturn { modules, edges, providerToModule };\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\treturn graph.providerToModule.get(providerName);\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 RuleError {\n\terror: unknown;\n\truleId: string;\n}\n\nexport interface RunRulesResult {\n\tdiagnostics: Diagnostic[];\n\terrors: RuleError[];\n}\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): RunRulesResult {\n\tconst diagnostics: Diagnostic[] = [];\n\tconst errors: RuleError[] = [];\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 (error) {\n\t\t\t\terrors.push({ ruleId: rule.meta.id, error });\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 (error) {\n\t\t\terrors.push({ ruleId: rule.meta.id, error });\n\t\t}\n\t}\n\n\treturn { diagnostics, errors };\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 { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nexport const noAsyncWithoutAwait: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-async-without-await\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Async functions/methods should contain at least one await expression\",\n\t\thelp: \"Either add an await expression or remove the async keyword.\",\n\t},\n\n\tcheck(context) {\n\t\t// Check class methods\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tfor (const method of cls.getMethods()) {\n\t\t\t\tif (!method.isAsync()) {\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\tconst awaitExpressions = body.getDescendantsOfKind(\n\t\t\t\t\tSyntaxKind.AwaitExpression\n\t\t\t\t);\n\n\t\t\t\t// Exclude nested async functions/arrow functions\n\t\t\t\tconst directAwaits = awaitExpressions.filter((expr) => {\n\t\t\t\t\tlet parent = expr.getParent();\n\t\t\t\t\twhile (parent && parent !== body) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tparent.getKind() === SyntaxKind.ArrowFunction ||\n\t\t\t\t\t\t\tparent.getKind() === SyntaxKind.FunctionExpression ||\n\t\t\t\t\t\t\tparent.getKind() === SyntaxKind.FunctionDeclaration\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tparent = parent.getParent();\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tif (directAwaits.length === 0) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Async method '${method.getName()}()' has no await expression.`,\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\n\t\t// Check standalone functions\n\t\tfor (const fn of context.sourceFile.getFunctions()) {\n\t\t\tif (!fn.isAsync()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst body = fn.getBody();\n\t\t\tif (!body) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst awaitExpressions = body.getDescendantsOfKind(\n\t\t\t\tSyntaxKind.AwaitExpression\n\t\t\t);\n\n\t\t\tconst directAwaits = awaitExpressions.filter((expr) => {\n\t\t\t\tlet parent = expr.getParent();\n\t\t\t\twhile (parent && parent !== body) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tparent.getKind() === SyntaxKind.ArrowFunction ||\n\t\t\t\t\t\tparent.getKind() === SyntaxKind.FunctionExpression ||\n\t\t\t\t\t\tparent.getKind() === SyntaxKind.FunctionDeclaration\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tparent = parent.getParent();\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tif (directAwaits.length === 0) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Async function '${fn.getName() ?? \"anonymous\"}()' has no await expression.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: fn.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 { isModule } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nconst MODULE_ARRAY_PROPS = [\"providers\", \"controllers\", \"imports\", \"exports\"];\n\nexport const noDuplicateModuleMetadata: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-duplicate-module-metadata\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Same identifier should not appear twice in a module metadata array\",\n\t\thelp: \"Remove the duplicate entry from the module metadata.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tif (!isModule(cls)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\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 args = moduleDecorator.getArguments()[0];\n\t\t\tif (!args || args.getKind() !== SyntaxKind.ObjectLiteralExpression) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst obj = args.asKind(SyntaxKind.ObjectLiteralExpression);\n\t\t\tif (!obj) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const propName of MODULE_ARRAY_PROPS) {\n\t\t\t\tconst prop = obj.getProperty(propName);\n\t\t\t\tif (!prop) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst arrayLiteral = prop.getChildrenOfKind(\n\t\t\t\t\tSyntaxKind.ArrayLiteralExpression\n\t\t\t\t)[0];\n\t\t\t\tif (!arrayLiteral) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst seen = new Set<string>();\n\t\t\t\tfor (const element of arrayLiteral.getElements()) {\n\t\t\t\t\tconst text = element.getText();\n\t\t\t\t\tif (seen.has(text)) {\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: `Duplicate '${text}' in @Module() ${propName} array.`,\n\t\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\t\tline: element.getStartLineNumber(),\n\t\t\t\t\t\t\tcolumn: 1,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tseen.add(text);\n\t\t\t\t\t}\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 HTTP_METHOD_DECORATORS = new Set([\n\t\"Get\",\n\t\"Post\",\n\t\"Put\",\n\t\"Delete\",\n\t\"Patch\",\n\t\"All\",\n\t\"Head\",\n\t\"Options\",\n]);\n\nexport const noDuplicateRoutes: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-duplicate-routes\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Same HTTP method + route path should not appear twice in a single controller\",\n\t\thelp: \"Remove or rename one of the duplicate route handlers.\",\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 routeMap = new Map<string, string>();\n\n\t\t\tfor (const method of cls.getMethods()) {\n\t\t\t\tfor (const decorator of method.getDecorators()) {\n\t\t\t\t\tconst decoratorName = decorator.getName();\n\t\t\t\t\tif (!HTTP_METHOD_DECORATORS.has(decoratorName)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst args = decorator.getArguments();\n\t\t\t\t\tconst path = args.length > 0 ? args[0].getText() : '\"\"';\n\t\t\t\t\tconst routeKey = `${decoratorName}:${path}`;\n\n\t\t\t\t\tconst existing = routeMap.get(routeKey);\n\t\t\t\t\tif (existing) {\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: `Duplicate route: @${decoratorName}(${path}) is already defined in '${existing}()'.`,\n\t\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\t\tline: method.getStartLineNumber(),\n\t\t\t\t\t\t\tcolumn: 1,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\trouteMap.set(routeKey, method.getName());\n\t\t\t\t\t}\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 HTTP_DECORATORS = new Set([\n\t\"Get\",\n\t\"Post\",\n\t\"Put\",\n\t\"Delete\",\n\t\"Patch\",\n\t\"All\",\n\t\"Head\",\n\t\"Options\",\n]);\n\nexport const noEmptyHandlers: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-empty-handlers\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"warning\",\n\t\tdescription: \"Controller HTTP handlers should not have empty bodies\",\n\t\thelp: \"Add implementation to the handler method or remove it if unnecessary.\",\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\tconst hasHttpDecorator = method\n\t\t\t\t\t.getDecorators()\n\t\t\t\t\t.some((d) => HTTP_DECORATORS.has(d.getName()));\n\n\t\t\t\tif (!hasHttpDecorator) {\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\tconst statements = body.getStatements();\n\t\t\t\tif (statements.length === 0) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Handler '${method.getName()}()' has an empty body.`,\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 { hasDecorator } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const noMissingFilterCatch: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-missing-filter-catch\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Exception filter classes decorated with @Catch() must implement the catch() method\",\n\t\thelp: \"Add a catch(exception, host: ArgumentsHost) method to the filter class.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tif (!hasDecorator(cls, \"Catch\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst hasCatchMethod = cls\n\t\t\t\t.getMethods()\n\t\t\t\t.some((m) => m.getName() === \"catch\");\n\t\t\tif (!hasCatchMethod) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Exception filter '${cls.getName()}' has @Catch() but is missing the 'catch()' method.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: cls.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 { hasDecorator } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const noMissingGuardMethod: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-missing-guard-method\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"error\",\n\t\tdescription: \"Guard classes must implement the canActivate() method\",\n\t\thelp: \"Add a canActivate(context: ExecutionContext) method to the guard class.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tconst name = cls.getName() ?? \"\";\n\t\t\tif (!name.endsWith(\"Guard\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!hasDecorator(cls, \"Injectable\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst hasCanActivate = cls\n\t\t\t\t.getMethods()\n\t\t\t\t.some((m) => m.getName() === \"canActivate\");\n\t\t\tif (!hasCanActivate) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Guard '${name}' is missing the 'canActivate()' method.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: cls.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 { ClassDeclaration } from \"ts-morph\";\nimport type { ProjectRule } from \"../types.js\";\n\nexport const noMissingInjectable: ProjectRule = {\n\tmeta: {\n\t\tid: \"correctness/no-missing-injectable\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Class listed in a module's providers must have the @Injectable() decorator\",\n\t\thelp: \"Add @Injectable() decorator to the class.\",\n\t\tscope: \"project\",\n\t},\n\n\tcheck(context) {\n\t\tconst providerNames = new Set(\n\t\t\t[...context.providers.values()].map((p) => p.name)\n\t\t);\n\n\t\t// Build a class-name-to-declaration index for O(1) lookups\n\t\tconst classIndex = new Map<\n\t\t\tstring,\n\t\t\t{ cls: ClassDeclaration; filePath: string }[]\n\t\t>();\n\t\tfor (const filePath of context.files) {\n\t\t\tconst sourceFile = context.project.getSourceFile(filePath);\n\t\t\tif (!sourceFile) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const cls of sourceFile.getClasses()) {\n\t\t\t\tconst name = cls.getName();\n\t\t\t\tif (name) {\n\t\t\t\t\tconst entries = classIndex.get(name) ?? [];\n\t\t\t\t\tentries.push({ cls, filePath });\n\t\t\t\t\tclassIndex.set(name, entries);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (const mod of context.moduleGraph.modules.values()) {\n\t\t\tfor (const providerName of mod.providers) {\n\t\t\t\tif (providerNames.has(providerName)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// O(1) lookup instead of iterating all files\n\t\t\t\tconst classEntries = classIndex.get(providerName);\n\t\t\t\tif (!classEntries) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfor (const { cls, filePath } of classEntries) {\n\t\t\t\t\tif (!cls.getDecorator(\"Injectable\")) {\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tfilePath,\n\t\t\t\t\t\t\tmessage: `Class '${providerName}' is listed in '${mod.name}' providers but is missing @Injectable() decorator.`,\n\t\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\t\tline: cls.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 { hasDecorator } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const noMissingInterceptorMethod: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-missing-interceptor-method\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"error\",\n\t\tdescription: \"Interceptor classes must implement the intercept() method\",\n\t\thelp: \"Add an intercept(context: ExecutionContext, next: CallHandler) method to the interceptor class.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tconst name = cls.getName() ?? \"\";\n\t\t\tif (!name.endsWith(\"Interceptor\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!hasDecorator(cls, \"Injectable\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst hasIntercept = cls\n\t\t\t\t.getMethods()\n\t\t\t\t.some((m) => m.getName() === \"intercept\");\n\t\t\tif (!hasIntercept) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Interceptor '${name}' is missing the 'intercept()' method.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: cls.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 { hasDecorator } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const noMissingModuleDecorator: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-missing-module-decorator\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"warning\",\n\t\tdescription: \"Classes named *Module should have a @Module() decorator\",\n\t\thelp: \"Add @Module({}) decorator to the class, or rename it if it is not a NestJS module.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tconst name = cls.getName() ?? \"\";\n\t\t\tif (!name.endsWith(\"Module\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Skip common non-NestJS module names\n\t\t\tif (name === \"Module\" || name === \"DynamicModule\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!hasDecorator(cls, \"Module\")) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Class '${name}' is named like a module but is missing the @Module() decorator.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: cls.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 { hasDecorator } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const noMissingPipeMethod: Rule = {\n\tmeta: {\n\t\tid: \"correctness/no-missing-pipe-method\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"error\",\n\t\tdescription: \"Pipe classes must implement the transform() method\",\n\t\thelp: \"Add a transform(value: any, metadata: ArgumentMetadata) method to the pipe class.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tconst name = cls.getName() ?? \"\";\n\t\t\tif (!name.endsWith(\"Pipe\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!hasDecorator(cls, \"Injectable\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst hasTransform = cls\n\t\t\t\t.getMethods()\n\t\t\t\t.some((m) => m.getName() === \"transform\");\n\t\t\tif (!hasTransform) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Pipe '${name}' is missing the 'transform()' method.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: cls.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 { hasDecorator } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const requireInjectDecorator: Rule = {\n\tmeta: {\n\t\tid: \"correctness/require-inject-decorator\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Constructor parameters without type annotations must have @Inject() decorator for NestJS DI to resolve them\",\n\t\thelp: \"Add a type annotation or @Inject() decorator to the constructor parameter.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tif (\n\t\t\t\t!(hasDecorator(cls, \"Injectable\") || hasDecorator(cls, \"Controller\"))\n\t\t\t) {\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\tconst hasInject = param\n\t\t\t\t\t.getDecorators()\n\t\t\t\t\t.some((d) => d.getName() === \"Inject\");\n\n\t\t\t\tif (!(typeNode || hasInject)) {\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()}' in '${cls.getName()}' has no type annotation and no @Inject() decorator — NestJS cannot resolve it.`,\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: param.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 { Rule } from \"../types.js\";\n\nconst LIFECYCLE_MAP: Record<string, string> = {\n\tonModuleInit: \"OnModuleInit\",\n\tonModuleDestroy: \"OnModuleDestroy\",\n\tonApplicationBootstrap: \"OnApplicationBootstrap\",\n\tonApplicationShutdown: \"OnApplicationShutdown\",\n\tbeforeApplicationShutdown: \"BeforeApplicationShutdown\",\n};\n\nexport const requireLifecycleInterface: Rule = {\n\tmeta: {\n\t\tid: \"correctness/require-lifecycle-interface\",\n\t\tcategory: \"correctness\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Classes with lifecycle methods should implement the corresponding NestJS interface\",\n\t\thelp: \"Add 'implements OnModuleInit' (or the appropriate interface) to the class declaration.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tconst implementedInterfaces = cls.getImplements().map((i) => i.getText());\n\n\t\t\tfor (const method of cls.getMethods()) {\n\t\t\t\tconst methodName = method.getName();\n\t\t\t\tconst expectedInterface = LIFECYCLE_MAP[methodName];\n\t\t\t\tif (!expectedInterface) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (!implementedInterfaces.some((i) => i.includes(expectedInterface))) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Class '${cls.getName()}' has '${methodName}()' but does not implement '${expectedInterface}'.`,\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 { SyntaxKind } from \"ts-morph\";\nimport { hasDecorator } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nconst BLOCKING_KINDS = new Set([\n\tSyntaxKind.ForStatement,\n\tSyntaxKind.ForOfStatement,\n\tSyntaxKind.ForInStatement,\n\tSyntaxKind.WhileStatement,\n\tSyntaxKind.DoStatement,\n\tSyntaxKind.AwaitExpression,\n]);\n\nexport const noBlockingConstructor: Rule = {\n\tmeta: {\n\t\tid: \"performance/no-blocking-constructor\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Constructors in Injectable/Controller classes should not contain heavy operations\",\n\t\thelp: \"Move complex initialization logic to onModuleInit() lifecycle method instead.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tif (\n\t\t\t\t!(hasDecorator(cls, \"Injectable\") || hasDecorator(cls, \"Controller\"))\n\t\t\t) {\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\tconst body = ctor.getBody();\n\t\t\tif (!body) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const descendant of body.getDescendants()) {\n\t\t\t\tif (BLOCKING_KINDS.has(descendant.getKind())) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Constructor in '${cls.getName()}' contains blocking operation — use onModuleInit() instead.`,\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: ctor.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\t},\n};\n","import { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nexport const noDynamicRequire: Rule = {\n\tmeta: {\n\t\tid: \"performance/no-dynamic-require\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Dynamic require() with variable arguments prevents bundler optimization\",\n\t\thelp: \"Use static import statements or dynamic import() with string literals.\",\n\t},\n\n\tcheck(context) {\n\t\tconst callExpressions = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.CallExpression\n\t\t);\n\n\t\tfor (const call of callExpressions) {\n\t\t\tif (call.getExpression().getText() !== \"require\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst args = call.getArguments();\n\t\t\tif (args.length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst firstArg = args[0];\n\t\t\t// Allow string literals (static requires)\n\t\t\tif (firstArg.getKind() === SyntaxKind.StringLiteral) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tcontext.report({\n\t\t\t\tfilePath: context.filePath,\n\t\t\t\tmessage:\n\t\t\t\t\t\"Dynamic require() with non-literal argument prevents bundler optimization.\",\n\t\t\t\thelp: this.meta.help,\n\t\t\t\tline: call.getStartLineNumber(),\n\t\t\t\tcolumn: 1,\n\t\t\t});\n\t\t}\n\t},\n};\n","import { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nconst LOOP_KINDS = new Set([\n\tSyntaxKind.ForStatement,\n\tSyntaxKind.ForOfStatement,\n\tSyntaxKind.ForInStatement,\n\tSyntaxKind.WhileStatement,\n\tSyntaxKind.DoStatement,\n]);\n\nconst CONSOLE_METHODS = new Set([\n\t\"log\",\n\t\"warn\",\n\t\"error\",\n\t\"info\",\n\t\"debug\",\n\t\"trace\",\n]);\n\nexport const noLoggingInLoops: Rule = {\n\tmeta: {\n\t\tid: \"performance/no-logging-in-loops\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"info\",\n\t\tdescription:\n\t\t\t\"Logging inside loops can degrade performance and flood log output\",\n\t\thelp: \"Move logging outside the loop, aggregate results, or use conditional logging.\",\n\t},\n\n\tcheck(context) {\n\t\tconst callExpressions = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.CallExpression\n\t\t);\n\n\t\tfor (const call of callExpressions) {\n\t\t\tconst exprText = call.getExpression().getText();\n\n\t\t\t// Check for console.log/warn/error etc.\n\t\t\tconst isConsoleCall =\n\t\t\t\texprText.startsWith(\"console.\") &&\n\t\t\t\tCONSOLE_METHODS.has(exprText.split(\".\")[1]);\n\n\t\t\t// Check for this.logger.log/warn/error etc.\n\t\t\tconst isLoggerCall =\n\t\t\t\texprText.startsWith(\"this.logger.\") &&\n\t\t\t\tCONSOLE_METHODS.has(exprText.split(\".\")[2]);\n\n\t\t\tif (!(isConsoleCall || isLoggerCall)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Check if inside a loop\n\t\t\tlet parent = call.getParent();\n\t\t\twhile (parent) {\n\t\t\t\tif (LOOP_KINDS.has(parent.getKind())) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Logging call '${exprText}()' inside a loop.`,\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: call.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\tparent = parent.getParent();\n\t\t\t}\n\t\t}\n\t},\n};\n","import type { ProjectRule } from \"../types.js\";\n\nexport const noOrphanModules: ProjectRule = {\n\tmeta: {\n\t\tid: \"performance/no-orphan-modules\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"info\",\n\t\tdescription:\n\t\t\t\"Module is never imported by any other module and may be dead code\",\n\t\thelp: \"Import this module in another module or remove it if it is unused.\",\n\t\tscope: \"project\",\n\t},\n\n\tcheck(context) {\n\t\t// Collect all module names that are imported by at least one other module\n\t\tconst importedModules = new Set<string>();\n\t\tfor (const mod of context.moduleGraph.modules.values()) {\n\t\t\tfor (const imp of mod.imports) {\n\t\t\t\timportedModules.add(imp);\n\t\t\t}\n\t\t}\n\n\t\tfor (const mod of context.moduleGraph.modules.values()) {\n\t\t\t// Skip AppModule — it's the root and is never imported\n\t\t\tif (mod.name === \"AppModule\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!importedModules.has(mod.name)) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: mod.filePath,\n\t\t\t\t\tmessage: `Module '${mod.name}' is never imported by any other module.`,\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 { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nconst LOOP_KINDS = new Set([\n\tSyntaxKind.ForStatement,\n\tSyntaxKind.ForOfStatement,\n\tSyntaxKind.ForInStatement,\n\tSyntaxKind.WhileStatement,\n\tSyntaxKind.DoStatement,\n]);\n\nexport const noQueryInLoop: Rule = {\n\tmeta: {\n\t\tid: \"performance/no-query-in-loop\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Await expressions inside loops cause N+1 query patterns — batch or parallelize instead\",\n\t\thelp: \"Use Promise.all(), batch queries, or database-level operations (e.g., WHERE IN) instead of awaiting in a loop.\",\n\t},\n\n\tcheck(context) {\n\t\tconst awaitExpressions = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.AwaitExpression\n\t\t);\n\n\t\tfor (const awaitExpr of awaitExpressions) {\n\t\t\tlet parent = awaitExpr.getParent();\n\t\t\twhile (parent) {\n\t\t\t\tif (LOOP_KINDS.has(parent.getKind())) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\"Await expression inside a loop — potential N+1 query pattern.\",\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: awaitExpr.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\tparent = parent.getParent();\n\t\t\t}\n\t\t}\n\t},\n};\n","import { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nconst SYNC_IO_METHODS = new Set([\n\t\"readFileSync\",\n\t\"writeFileSync\",\n\t\"existsSync\",\n\t\"mkdirSync\",\n\t\"readdirSync\",\n\t\"statSync\",\n\t\"accessSync\",\n\t\"appendFileSync\",\n\t\"copyFileSync\",\n\t\"renameSync\",\n\t\"unlinkSync\",\n]);\n\nexport const noSyncIo: Rule = {\n\tmeta: {\n\t\tid: \"performance/no-sync-io\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Synchronous I/O calls block the event loop and should be avoided in NestJS applications\",\n\t\thelp: \"Use the async variant (e.g., readFile instead of readFileSync) with await.\",\n\t},\n\n\tcheck(context) {\n\t\tconst callExpressions = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.CallExpression\n\t\t);\n\n\t\tfor (const call of callExpressions) {\n\t\t\tconst expr = call.getExpression();\n\t\t\tconst methodName = expr.getText().split(\".\").pop() ?? \"\";\n\n\t\t\tif (!SYNC_IO_METHODS.has(methodName)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tcontext.report({\n\t\t\t\tfilePath: context.filePath,\n\t\t\t\tmessage: `Synchronous I/O call '${methodName}()' blocks the event loop.`,\n\t\t\t\thelp: this.meta.help,\n\t\t\t\tline: call.getStartLineNumber(),\n\t\t\t\tcolumn: 1,\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 noUnnecessaryAsync: Rule = {\n\tmeta: {\n\t\tid: \"performance/no-unnecessary-async\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"info\",\n\t\tdescription:\n\t\t\t\"Async functions without await add unnecessary promise wrapping overhead\",\n\t\thelp: \"Remove the async keyword or add await if async behavior is needed.\",\n\t},\n\n\tcheck(context) {\n\t\t// Check class methods\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tfor (const method of cls.getMethods()) {\n\t\t\t\tif (!method.isAsync()) {\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\tconst awaitExpressions = body.getDescendantsOfKind(\n\t\t\t\t\tSyntaxKind.AwaitExpression\n\t\t\t\t);\n\n\t\t\t\t// Only count awaits directly in this function, not nested\n\t\t\t\tconst directAwaits = awaitExpressions.filter((expr) => {\n\t\t\t\t\tlet parent = expr.getParent();\n\t\t\t\t\twhile (parent && parent !== body) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tparent.getKind() === SyntaxKind.ArrowFunction ||\n\t\t\t\t\t\t\tparent.getKind() === SyntaxKind.FunctionExpression ||\n\t\t\t\t\t\t\tparent.getKind() === SyntaxKind.FunctionDeclaration\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tparent = parent.getParent();\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tif (directAwaits.length === 0) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\tmessage: `Async method '${method.getName()}()' contains no await — unnecessary promise wrapping.`,\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 type { ProjectRule } from \"../types.js\";\n\nexport const noUnusedModuleExports: ProjectRule = {\n\tmeta: {\n\t\tid: \"performance/no-unused-module-exports\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"info\",\n\t\tdescription:\n\t\t\t\"Module exports a provider that no importing module actually uses\",\n\t\thelp: \"Remove the unused export or use the provider in an importing module.\",\n\t\tscope: \"project\",\n\t},\n\n\tcheck(context) {\n\t\t// For each module, check if its exports are actually used by importing modules\n\t\tfor (const mod of context.moduleGraph.modules.values()) {\n\t\t\tif (mod.exports.length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Find modules that import this one\n\t\t\tconst importingModules: string[] = [];\n\t\t\tfor (const otherMod of context.moduleGraph.modules.values()) {\n\t\t\t\tif (otherMod.name === mod.name) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (otherMod.imports.includes(mod.name)) {\n\t\t\t\t\timportingModules.push(otherMod.name);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If no module imports this one, all exports are unused\n\t\t\tif (importingModules.length === 0) {\n\t\t\t\tcontinue; // no-orphan-modules handles this case\n\t\t\t}\n\n\t\t\t// Collect all provider dependencies from importing modules\n\t\t\tconst usedProviders = new Set<string>();\n\t\t\tfor (const importerName of importingModules) {\n\t\t\t\tconst importer = context.moduleGraph.modules.get(importerName);\n\t\t\t\tif (!importer) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Check which providers in the importing module depend on exported providers\n\t\t\t\tfor (const providerName of importer.providers) {\n\t\t\t\t\tconst provider = context.providers.get(providerName);\n\t\t\t\t\tif (!provider) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tfor (const dep of provider.dependencies) {\n\t\t\t\t\t\tusedProviders.add(dep);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Check controllers too\n\t\t\t\tfor (const controllerName of importer.controllers) {\n\t\t\t\t\tfor (const filePath of context.files) {\n\t\t\t\t\t\tconst sourceFile = context.project.getSourceFile(filePath);\n\t\t\t\t\t\tif (!sourceFile) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (const cls of sourceFile.getClasses()) {\n\t\t\t\t\t\t\tif (cls.getName() !== controllerName) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst ctor = cls.getConstructors()[0];\n\t\t\t\t\t\t\tif (!ctor) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfor (const param of ctor.getParameters()) {\n\t\t\t\t\t\t\t\tconst typeText = param.getType().getText();\n\t\t\t\t\t\t\t\tconst simpleName =\n\t\t\t\t\t\t\t\t\ttypeText.split(\".\").pop()?.split(\"<\")[0] ?? typeText;\n\t\t\t\t\t\t\t\tusedProviders.add(simpleName);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const exportedName of mod.exports) {\n\t\t\t\tif (!usedProviders.has(exportedName)) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tfilePath: mod.filePath,\n\t\t\t\t\t\tmessage: `Module '${mod.name}' exports '${exportedName}' but no importing module uses it.`,\n\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\tline: mod.classDeclaration.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 { ProjectRule } from \"../types.js\";\n\nconst SKIP_SUFFIXES = [\n\t\"Guard\",\n\t\"Interceptor\",\n\t\"Filter\",\n\t\"Middleware\",\n\t\"Strategy\",\n];\n\nexport const noUnusedProviders: ProjectRule = {\n\tmeta: {\n\t\tid: \"performance/no-unused-providers\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Injectable providers that are never injected by any other provider may be dead code\",\n\t\thelp: \"Remove the unused provider or inject it where needed.\",\n\t\tscope: \"project\",\n\t},\n\n\tcheck(context) {\n\t\t// Collect all dependency names from all providers\n\t\tconst allDependencies = new Set<string>();\n\t\tfor (const provider of context.providers.values()) {\n\t\t\tfor (const dep of provider.dependencies) {\n\t\t\t\tallDependencies.add(dep);\n\t\t\t}\n\t\t}\n\n\t\t// Also collect controller dependencies\n\t\tfor (const filePath of context.files) {\n\t\t\tconst sourceFile = context.project.getSourceFile(filePath);\n\t\t\tif (!sourceFile) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const cls of sourceFile.getClasses()) {\n\t\t\t\tif (!cls.getDecorator(\"Controller\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst ctor = cls.getConstructors()[0];\n\t\t\t\tif (!ctor) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tfor (const param of ctor.getParameters()) {\n\t\t\t\t\tconst typeText = param.getType().getText();\n\t\t\t\t\tconst simpleName =\n\t\t\t\t\t\ttypeText.split(\".\").pop()?.split(\"<\")[0] ?? typeText;\n\t\t\t\t\tallDependencies.add(simpleName);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (const provider of context.providers.values()) {\n\t\t\tconst name = provider.name;\n\n\t\t\t// Skip common infrastructure patterns\n\t\t\tif (SKIP_SUFFIXES.some((suffix) => name.endsWith(suffix))) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Skip if it's used as a dependency somewhere\n\t\t\tif (allDependencies.has(name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Skip if it's in module exports (it may be used externally)\n\t\t\tlet isExported = false;\n\t\t\tfor (const mod of context.moduleGraph.modules.values()) {\n\t\t\t\tif (mod.exports.includes(name)) {\n\t\t\t\t\tisExported = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (isExported) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tcontext.report({\n\t\t\t\tfilePath: provider.filePath,\n\t\t\t\tmessage: `Provider '${name}' is never injected by any other provider or controller.`,\n\t\t\t\thelp: this.meta.help,\n\t\t\t\tline: provider.classDeclaration.getStartLineNumber(),\n\t\t\t\tcolumn: 1,\n\t\t\t});\n\t\t}\n\t},\n};\n","import { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nconst FIND_MANY_METHODS = new Set([\"findMany\", \"find\"]);\nconst PAGINATION_ARGS = new Set([\n\t\"take\",\n\t\"skip\",\n\t\"limit\",\n\t\"offset\",\n\t\"page\",\n\t\"perPage\",\n\t\"pageSize\",\n]);\n\nexport const preferPagination: Rule = {\n\tmeta: {\n\t\tid: \"performance/prefer-pagination\",\n\t\tcategory: \"performance\",\n\t\tseverity: \"info\",\n\t\tdescription:\n\t\t\t\"Database findMany/find queries should include pagination to avoid loading excessive data\",\n\t\thelp: \"Add pagination arguments (take/skip, limit/offset) to the query.\",\n\t},\n\n\tcheck(context) {\n\t\tconst callExpressions = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.CallExpression\n\t\t);\n\n\t\tfor (const call of callExpressions) {\n\t\t\tconst expr = call.getExpression();\n\t\t\tconst methodName = expr.getText().split(\".\").pop() ?? \"\";\n\n\t\t\tif (!FIND_MANY_METHODS.has(methodName)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst args = call.getArguments();\n\n\t\t\t// No args at all — no pagination\n\t\t\tif (args.length === 0) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `'${methodName}()' called without pagination arguments.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: call.getStartLineNumber(),\n\t\t\t\t\tcolumn: 1,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Check if object argument contains pagination keys\n\t\t\tconst firstArg = args[0];\n\t\t\tif (firstArg.getKind() !== SyntaxKind.ObjectLiteralExpression) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst obj = firstArg.asKind(SyntaxKind.ObjectLiteralExpression);\n\t\t\tif (!obj) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst props = obj\n\t\t\t\t.getProperties()\n\t\t\t\t.map((p) => p.getText().split(\":\")[0].trim());\n\t\t\tconst hasPagination = props.some((p) => PAGINATION_ARGS.has(p));\n\n\t\t\tif (!hasPagination) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `'${methodName}()' called without pagination arguments.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: call.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 noCsrfDisabled: Rule = {\n\tmeta: {\n\t\tid: \"security/no-csrf-disabled\",\n\t\tcategory: \"security\",\n\t\tseverity: \"error\",\n\t\tdescription: \"CSRF protection should not be explicitly disabled\",\n\t\thelp: \"Enable CSRF protection or remove the explicit disabling of it.\",\n\t},\n\n\tcheck(context) {\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\tif (name !== \"csrf\" && name !== \"csrfProtection\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\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.getText() === \"false\") {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `CSRF protection explicitly disabled (${name}: false).`,\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 { SyntaxKind } from \"ts-morph\";\nimport { isController } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const noDangerousRedirects: Rule = {\n\tmeta: {\n\t\tid: \"security/no-dangerous-redirects\",\n\t\tcategory: \"security\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Redirects using user-controlled input (from @Query/@Param) are an open redirect vulnerability\",\n\t\thelp: \"Validate redirect URLs against an allowlist of safe destinations.\",\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// Collect parameter names decorated with @Query or @Param\n\t\t\t\tconst userInputParams = new Set<string>();\n\t\t\t\tfor (const param of method.getParameters()) {\n\t\t\t\t\tconst hasUserInputDecorator = param\n\t\t\t\t\t\t.getDecorators()\n\t\t\t\t\t\t.some((d) => d.getName() === \"Query\" || d.getName() === \"Param\");\n\t\t\t\t\tif (hasUserInputDecorator) {\n\t\t\t\t\t\tuserInputParams.add(param.getName());\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (userInputParams.size === 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Check for res.redirect() calls with user input\n\t\t\t\tconst callExpressions = method.getDescendantsOfKind(\n\t\t\t\t\tSyntaxKind.CallExpression\n\t\t\t\t);\n\n\t\t\t\tfor (const call of callExpressions) {\n\t\t\t\t\tconst exprText = call.getExpression().getText();\n\t\t\t\t\tif (!exprText.endsWith(\"redirect\")) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const arg of call.getArguments()) {\n\t\t\t\t\t\tconst argText = arg.getText();\n\t\t\t\t\t\tif (userInputParams.has(argText)) {\n\t\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\t\t\tmessage: `Redirect uses user-controlled parameter '${argText}' — open redirect risk.`,\n\t\t\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\t\t\tline: call.getStartLineNumber(),\n\t\t\t\t\t\t\t\tcolumn: 1,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Check for @Redirect() decorator with dynamic value\n\t\t\t\tconst redirectDecorator = method\n\t\t\t\t\t.getDecorators()\n\t\t\t\t\t.find((d) => d.getName() === \"Redirect\");\n\t\t\t\tif (redirectDecorator) {\n\t\t\t\t\tfor (const arg of redirectDecorator.getArguments()) {\n\t\t\t\t\t\tconst argText = arg.getText();\n\t\t\t\t\t\tif (userInputParams.has(argText)) {\n\t\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\t\t\t\tmessage: `@Redirect() uses user-controlled parameter '${argText}' — open redirect risk.`,\n\t\t\t\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\t\t\t\tline: redirectDecorator.getStartLineNumber(),\n\t\t\t\t\t\t\t\tcolumn: 1,\n\t\t\t\t\t\t\t});\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 { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nexport const noEval: Rule = {\n\tmeta: {\n\t\tid: \"security/no-eval\",\n\t\tcategory: \"security\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Usage of eval() or new Function() is a security risk and should be avoided\",\n\t\thelp: \"Refactor to avoid eval() and new Function(). Use safer alternatives like JSON.parse() or a sandboxed interpreter.\",\n\t},\n\n\tcheck(context) {\n\t\tconst callExpressions = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.CallExpression\n\t\t);\n\n\t\tfor (const call of callExpressions) {\n\t\t\tconst exprText = call.getExpression().getText();\n\t\t\tif (exprText === \"eval\") {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: \"Usage of eval() is a security risk.\",\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: call.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 for new Function()\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\tif (expr.getExpression().getText() === \"Function\") {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: \"Usage of new Function() is a security risk.\",\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 { SyntaxKind } from \"ts-morph\";\nimport { hasDecorator } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const noExposedEnvVars: Rule = {\n\tmeta: {\n\t\tid: \"security/no-exposed-env-vars\",\n\t\tcategory: \"security\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Use NestJS ConfigService instead of direct process.env access in Injectable/Controller classes\",\n\t\thelp: \"Inject ConfigService and use configService.get('VAR_NAME') instead of process.env.VAR_NAME.\",\n\t},\n\n\tcheck(context) {\n\t\tfor (const cls of context.sourceFile.getClasses()) {\n\t\t\tif (\n\t\t\t\t!(hasDecorator(cls, \"Injectable\") || hasDecorator(cls, \"Controller\"))\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst propertyAccesses = cls.getDescendantsOfKind(\n\t\t\t\tSyntaxKind.PropertyAccessExpression\n\t\t\t);\n\n\t\t\tfor (const access of propertyAccesses) {\n\t\t\t\tconst expr = access.getExpression();\n\t\t\t\tif (expr.getText() !== \"process.env\") {\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: `Direct 'process.env.${access.getName()}' access in '${cls.getName()}'. Use ConfigService instead.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: access.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\nconst ERROR_VAR_PATTERN = /^(error|err|e|ex|exception)$/;\n\nexport const noExposedStackTrace: Rule = {\n\tmeta: {\n\t\tid: \"security/no-exposed-stack-trace\",\n\t\tcategory: \"security\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Stack traces should not be exposed in responses — they leak internal implementation details\",\n\t\thelp: \"Log the stack trace internally and return a generic error message to the client.\",\n\t},\n\n\tcheck(context) {\n\t\tconst propertyAccesses = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.PropertyAccessExpression\n\t\t);\n\n\t\tfor (const access of propertyAccesses) {\n\t\t\tif (access.getName() !== \"stack\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst exprText = access.getExpression().getText();\n\t\t\t// Match common error variable names accessing .stack\n\t\t\tif (\n\t\t\t\t!(\n\t\t\t\t\tERROR_VAR_PATTERN.test(exprText) ||\n\t\t\t\t\texprText.endsWith(\".error\") ||\n\t\t\t\t\texprText.endsWith(\".err\")\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Check if it's being returned or passed to a response\n\t\t\tconst parent = access.getParent();\n\t\t\tif (!parent) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst parentKind = parent.getKind();\n\t\t\tif (\n\t\t\t\tparentKind === SyntaxKind.ReturnStatement ||\n\t\t\t\tparentKind === SyntaxKind.PropertyAssignment ||\n\t\t\t\tparentKind === SyntaxKind.ShorthandPropertyAssignment ||\n\t\t\t\tparentKind === SyntaxKind.CallExpression\n\t\t\t) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Stack trace '${exprText}.stack' may be exposed in response — leaks implementation details.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: access.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\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\nconst PLACEHOLDER_VALUES = new Set([\n\t\"your-secret-here\",\n\t\"changeme\",\n\t\"password\",\n]);\n\nfunction isSuspiciousValue(value: string): boolean {\n\treturn (\n\t\tvalue.length >= 8 &&\n\t\t!value.includes(\"${\") &&\n\t\t!value.startsWith(\"process.env\") &&\n\t\t!PLACEHOLDER_VALUES.has(value)\n\t);\n}\n\nfunction hasSuspiciousName(name: string): boolean {\n\treturn VARIABLE_NAME_PATTERNS.some((p) => p.test(name));\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 declarations and property 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 || initializer.getKind() !== SyntaxKind.StringLiteral) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!hasSuspiciousName(name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst value = initializer.getText().slice(1, -1);\n\t\t\tif (isSuspiciousValue(value)) {\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\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 || initializer.getKind() !== SyntaxKind.StringLiteral) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!hasSuspiciousName(name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst value = initializer.getText().slice(1, -1);\n\t\t\tif (isSuspiciousValue(value)) {\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 { SyntaxKind } from \"ts-morph\";\nimport type { Rule } from \"../types.js\";\n\nconst RAW_QUERY_METHODS = new Set([\n\t\"$executeRaw\",\n\t\"$queryRaw\",\n\t\"$executeRawUnsafe\",\n\t\"$queryRawUnsafe\",\n\t\"query\",\n]);\n\nexport const noUnsafeRawQuery: Rule = {\n\tmeta: {\n\t\tid: \"security/no-unsafe-raw-query\",\n\t\tcategory: \"security\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"Raw SQL queries with template literal interpolation are a SQL injection risk\",\n\t\thelp: \"Use parameterized queries or Prisma's tagged template (Prisma.sql`...`) instead of string interpolation.\",\n\t},\n\n\tcheck(context) {\n\t\tconst callExpressions = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.CallExpression\n\t\t);\n\n\t\tfor (const call of callExpressions) {\n\t\t\tconst expr = call.getExpression();\n\t\t\tconst methodName = expr.getText().split(\".\").pop() ?? \"\";\n\n\t\t\tif (!RAW_QUERY_METHODS.has(methodName)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst args = call.getArguments();\n\t\t\tif (args.length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst firstArg = args[0];\n\t\t\t// Check for template literals with expressions (interpolation)\n\t\t\tif (firstArg.getKind() === SyntaxKind.TemplateExpression) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Raw SQL query '${methodName}()' uses template literal interpolation — SQL injection risk.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: call.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\nconst WEAK_ALGORITHMS = new Set([\"md5\", \"sha1\"]);\n\nexport const noWeakCrypto: Rule = {\n\tmeta: {\n\t\tid: \"security/no-weak-crypto\",\n\t\tcategory: \"security\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Weak hashing algorithms (MD5, SHA1) should not be used for security purposes\",\n\t\thelp: \"Use a stronger algorithm like SHA-256 or bcrypt for password hashing.\",\n\t},\n\n\tcheck(context) {\n\t\tconst callExpressions = context.sourceFile.getDescendantsOfKind(\n\t\t\tSyntaxKind.CallExpression\n\t\t);\n\n\t\tfor (const call of callExpressions) {\n\t\t\tconst exprText = call.getExpression().getText();\n\t\t\tif (!exprText.endsWith(\"createHash\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst args = call.getArguments();\n\t\t\tif (args.length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst firstArg = args[0];\n\t\t\tif (firstArg.getKind() !== SyntaxKind.StringLiteral) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst algorithm = firstArg.getText().slice(1, -1).toLowerCase();\n\t\t\tif (WEAK_ALGORITHMS.has(algorithm)) {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `Weak hashing algorithm '${algorithm}' used in createHash().`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: call.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 noWildcardCors: Rule = {\n\tmeta: {\n\t\tid: \"security/no-wildcard-cors\",\n\t\tcategory: \"security\",\n\t\tseverity: \"error\",\n\t\tdescription:\n\t\t\t\"CORS should not be configured with origin: '*' or origin: true — allows any domain\",\n\t\thelp: \"Specify allowed origins explicitly instead of using wildcard CORS.\",\n\t},\n\n\tcheck(context) {\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\tif (prop.getName() !== \"origin\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\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\tconst text = initializer.getText();\n\t\t\tif (text === \"'*'\" || text === '\"*\"' || text === \"true\") {\n\t\t\t\tcontext.report({\n\t\t\t\t\tfilePath: context.filePath,\n\t\t\t\t\tmessage: `CORS configured with origin: ${text} — allows requests from any domain.`,\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 { isController } from \"../../engine/decorator-utils.js\";\nimport type { Rule } from \"../types.js\";\n\nexport const requireAuthGuard: Rule = {\n\tmeta: {\n\t\tid: \"security/require-auth-guard\",\n\t\tcategory: \"security\",\n\t\tseverity: \"info\",\n\t\tdescription:\n\t\t\t\"Controller classes should have @UseGuards() at class or method level\",\n\t\thelp: \"Add @UseGuards(AuthGuard) to the controller class or its handler methods.\",\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\t// Check class-level guards\n\t\t\tconst hasClassGuard = cls\n\t\t\t\t.getDecorators()\n\t\t\t\t.some((d) => d.getName() === \"UseGuards\");\n\t\t\tif (hasClassGuard) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Check if any method has guards\n\t\t\tconst hasMethodGuard = cls\n\t\t\t\t.getMethods()\n\t\t\t\t.some((method) =>\n\t\t\t\t\tmethod.getDecorators().some((d) => d.getName() === \"UseGuards\")\n\t\t\t\t);\n\t\t\tif (hasMethodGuard) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tcontext.report({\n\t\t\t\tfilePath: context.filePath,\n\t\t\t\tmessage: `Controller '${cls.getName()}' has no @UseGuards() protection.`,\n\t\t\t\thelp: this.meta.help,\n\t\t\t\tline: cls.getStartLineNumber(),\n\t\t\t\tcolumn: 1,\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\nexport const requireValidationPipe: Rule = {\n\tmeta: {\n\t\tid: \"security/require-validation-pipe\",\n\t\tcategory: \"security\",\n\t\tseverity: \"warning\",\n\t\tdescription:\n\t\t\t\"Handlers with @Body() parameters should have validation via @UsePipes(ValidationPipe)\",\n\t\thelp: \"Add @UsePipes(new ValidationPipe()) to the method or controller, or set up global validation.\",\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 hasClassPipe = cls\n\t\t\t\t.getDecorators()\n\t\t\t\t.some((d) => d.getName() === \"UsePipes\");\n\n\t\t\tfor (const method of cls.getMethods()) {\n\t\t\t\t// Check if any parameter has @Body() decorator\n\t\t\t\tconst hasBodyParam = method\n\t\t\t\t\t.getParameters()\n\t\t\t\t\t.some((param) =>\n\t\t\t\t\t\tparam.getDecorators().some((d) => d.getName() === \"Body\")\n\t\t\t\t\t);\n\t\t\t\tif (!hasBodyParam) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (hasClassPipe) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst hasMethodPipe = method\n\t\t\t\t\t.getDecorators()\n\t\t\t\t\t.some((d) => d.getName() === \"UsePipes\");\n\t\t\t\tif (hasMethodPipe) {\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: `Handler '${method.getName()}()' has @Body() parameter but no validation pipe.`,\n\t\t\t\t\thelp: this.meta.help,\n\t\t\t\t\tline: method.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 { noAsyncWithoutAwait } from \"./correctness/no-async-without-await.js\";\nimport { noDuplicateModuleMetadata } from \"./correctness/no-duplicate-module-metadata.js\";\nimport { noDuplicateRoutes } from \"./correctness/no-duplicate-routes.js\";\nimport { noEmptyHandlers } from \"./correctness/no-empty-handlers.js\";\nimport { noMissingFilterCatch } from \"./correctness/no-missing-filter-catch.js\";\nimport { noMissingGuardMethod } from \"./correctness/no-missing-guard-method.js\";\nimport { noMissingInjectable } from \"./correctness/no-missing-injectable.js\";\nimport { noMissingInterceptorMethod } from \"./correctness/no-missing-interceptor-method.js\";\nimport { noMissingModuleDecorator } from \"./correctness/no-missing-module-decorator.js\";\nimport { noMissingPipeMethod } from \"./correctness/no-missing-pipe-method.js\";\nimport { preferReadonlyInjection } from \"./correctness/prefer-readonly-injection.js\";\nimport { requireInjectDecorator } from \"./correctness/require-inject-decorator.js\";\nimport { requireLifecycleInterface } from \"./correctness/require-lifecycle-interface.js\";\nimport { noBlockingConstructor } from \"./performance/no-blocking-constructor.js\";\nimport { noDynamicRequire } from \"./performance/no-dynamic-require.js\";\nimport { noLoggingInLoops } from \"./performance/no-logging-in-loops.js\";\nimport { noOrphanModules } from \"./performance/no-orphan-modules.js\";\nimport { noQueryInLoop } from \"./performance/no-query-in-loop.js\";\nimport { noSyncIo } from \"./performance/no-sync-io.js\";\nimport { noUnnecessaryAsync } from \"./performance/no-unnecessary-async.js\";\nimport { noUnusedModuleExports } from \"./performance/no-unused-module-exports.js\";\nimport { noUnusedProviders } from \"./performance/no-unused-providers.js\";\nimport { preferPagination } from \"./performance/prefer-pagination.js\";\nimport { noCsrfDisabled } from \"./security/no-csrf-disabled.js\";\nimport { noDangerousRedirects } from \"./security/no-dangerous-redirects.js\";\nimport { noEval } from \"./security/no-eval.js\";\nimport { noExposedEnvVars } from \"./security/no-exposed-env-vars.js\";\nimport { noExposedStackTrace } from \"./security/no-exposed-stack-trace.js\";\nimport { noHardcodedSecrets } from \"./security/no-hardcoded-secrets.js\";\nimport { noUnsafeRawQuery } from \"./security/no-unsafe-raw-query.js\";\nimport { noWeakCrypto } from \"./security/no-weak-crypto.js\";\nimport { noWildcardCors } from \"./security/no-wildcard-cors.js\";\nimport { requireAuthGuard } from \"./security/require-auth-guard.js\";\nimport { requireValidationPipe } from \"./security/require-validation-pipe.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 — file-scoped\n\tpreferReadonlyInjection,\n\trequireLifecycleInterface,\n\tnoEmptyHandlers,\n\tnoDuplicateRoutes,\n\tnoMissingGuardMethod,\n\tnoMissingPipeMethod,\n\tnoMissingFilterCatch,\n\tnoMissingInterceptorMethod,\n\tnoAsyncWithoutAwait,\n\tnoDuplicateModuleMetadata,\n\tnoMissingModuleDecorator,\n\trequireInjectDecorator,\n\n\t// Correctness — project-scoped\n\tnoMissingInjectable,\n\n\t// Security\n\tnoHardcodedSecrets,\n\tnoWildcardCors,\n\tnoUnsafeRawQuery,\n\trequireAuthGuard,\n\tnoEval,\n\tnoWeakCrypto,\n\tnoExposedEnvVars,\n\trequireValidationPipe,\n\tnoCsrfDisabled,\n\tnoExposedStackTrace,\n\tnoDangerousRedirects,\n\n\t// Performance — file-scoped\n\tnoSyncIo,\n\tnoQueryInLoop,\n\tnoLoggingInLoops,\n\tnoUnnecessaryAsync,\n\tnoBlockingConstructor,\n\tpreferPagination,\n\tnoDynamicRequire,\n\n\t// Performance — project-scoped\n\tnoUnusedProviders,\n\tnoUnusedModuleExports,\n\tnoOrphanModules,\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\n/**\n * Calculates a health score from 0-100 based on diagnostics and file count.\n *\n * Scoring formula: `score = 100 - (totalPenalty / fileCount) * PENALTY_SCALE`\n *\n * Each diagnostic contributes a penalty of `severityWeight * categoryMultiplier`:\n * - A security error costs 3.0 * 1.5 = 4.5 penalty points\n * - A performance info costs 0.5 * 0.8 = 0.4 penalty points\n *\n * The penalty is normalized by file count so that a 10-file project and a\n * 500-file project with the same issue density receive similar scores.\n *\n * PENALTY_SCALE (10) was calibrated so that an average of ~1 error per file\n * (normalized penalty ≈ 10) brings the score to 0. In practice:\n * - 1 error per 10 files → penalty/file ≈ 0.45 → score ≈ 95 (Excellent)\n * - 1 error per 3 files → penalty/file ≈ 1.5 → score ≈ 85 (Good)\n * - 1 error per file → penalty/file ≈ 4.5 → score ≈ 55 (Fair)\n * - 2 errors per file → penalty/file ≈ 9.0 → score ≈ 10 (Critical)\n */\nconst PENALTY_SCALE = 10;\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\tconst normalizedPenalty = totalPenalty / fileCount;\n\tconst value = Math.max(\n\t\t0,\n\t\tMath.min(100, Math.round(100 - normalizedPenalty * PENALTY_SCALE))\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\tminScore?: number;\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\n/**\n * Merges user config with defaults.\n *\n * Merge semantics:\n * - `include`: user replaces defaults entirely (user likely wants a specific scope)\n * - `exclude`: user values are appended to defaults (additive, keeps safe defaults)\n * - `ignore.rules`: user replaces defaults (no default ignored rules)\n * - `ignore.files`: user replaces defaults (no default ignored files)\n * - `rules`, `categories`, `thresholds`: shallow-merged with user taking precedence\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 { join } from \"node:path\";\nimport { glob } from \"tinyglobby\";\nimport type { NestjsDoctorConfig } from \"../types/config.js\";\nimport { DEFAULT_CONFIG } from \"../types/config.js\";\nimport type { MonorepoInfo } from \"./project-detector.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\nexport async function collectMonorepoFiles(\n\ttargetPath: string,\n\tmonorepo: MonorepoInfo,\n\tconfig: NestjsDoctorConfig = {}\n): Promise<Map<string, string[]>> {\n\tconst result = new Map<string, string[]>();\n\n\tfor (const [name, root] of monorepo.projects) {\n\t\tconst projectPath = join(targetPath, root);\n\t\tconst files = await collectFiles(projectPath, config);\n\t\tresult.set(name, files);\n\t}\n\n\treturn result;\n}\n","import picomatch from \"picomatch\";\n\nexport const compileGlobPattern = (pattern: string): RegExp => {\n\treturn picomatch.makeRe(pattern, { windows: false });\n};\n\nexport const matchGlobPattern = (\n\tfilePath: string,\n\tpattern: string\n): boolean => {\n\treturn picomatch.isMatch(filePath, pattern, { windows: false });\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\ninterface NestCliProject {\n\tcompilerOptions?: Record<string, unknown>;\n\tentryFile?: string;\n\troot?: string;\n\tsourceRoot?: string;\n\ttype?: string;\n}\n\ninterface NestCliJson {\n\tmonorepo?: boolean;\n\tprojects?: Record<string, NestCliProject>;\n\troot?: string;\n\tsourceRoot?: string;\n}\n\nexport interface MonorepoInfo {\n\tprojects: Map<string, string>; // name -> root path (relative)\n}\n\nexport async function detectMonorepo(\n\ttargetPath: string\n): Promise<MonorepoInfo | null> {\n\tconst cliPath = join(targetPath, \"nest-cli.json\");\n\n\ttry {\n\t\tconst raw = await readFile(cliPath, \"utf-8\");\n\t\tconst config = JSON.parse(raw) as NestCliJson;\n\n\t\tif (!(config.monorepo && config.projects)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst projects = new Map<string, string>();\n\t\tfor (const [name, project] of Object.entries(config.projects)) {\n\t\t\tconst root = project.root ?? name;\n\t\t\tprojects.set(name, root);\n\t\t}\n\n\t\tif (projects.size === 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn { projects };\n\t} catch {\n\t\treturn null;\n\t}\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 { join } from \"node:path\";\nimport { 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 { Diagnostic } from \"../types/diagnostic.js\";\nimport type {\n\tDiagnoseResult,\n\tDiagnoseSummary,\n\tMonorepoResult,\n\tRuleErrorInfo,\n\tSubProjectResult,\n} from \"../types/result.js\";\nimport { loadConfig } from \"./config-loader.js\";\nimport { collectFiles, collectMonorepoFiles } from \"./file-collector.js\";\nimport { filterIgnoredDiagnostics } from \"./filter-diagnostics.js\";\nimport { detectMonorepo, detectProject } from \"./project-detector.js\";\n\nfunction formatRuleError(error: unknown): string {\n\tif (error instanceof Error) {\n\t\treturn error.message;\n\t}\n\treturn String(error);\n}\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\tconst astProject = createAstParser(files);\n\tconst moduleGraph = buildModuleGraph(astProject, files);\n\tconst providers = resolveProviders(astProject, files);\n\tconst rules = filterRules(config);\n\tconst { diagnostics: rawDiagnostics, errors } = runRules(\n\t\tastProject,\n\t\tfiles,\n\t\trules,\n\t\t{ moduleGraph, providers, config }\n\t);\n\tconst diagnostics = filterIgnoredDiagnostics(rawDiagnostics, config);\n\n\tconst score = calculateScore(diagnostics, files.length);\n\tconst summary = buildSummary(diagnostics);\n\tconst ruleErrors: RuleErrorInfo[] = errors.map((e) => ({\n\t\truleId: e.ruleId,\n\t\terror: formatRuleError(e.error),\n\t}));\n\tconst elapsedMs = performance.now() - startTime;\n\n\treturn {\n\t\tscore,\n\t\tdiagnostics,\n\t\tproject: {\n\t\t\t...project,\n\t\t\tfileCount: files.length,\n\t\t\tmoduleCount: moduleGraph.modules.size,\n\t\t},\n\t\tsummary,\n\t\truleErrors,\n\t\telapsedMs,\n\t};\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\nexport async function scanMonorepo(\n\ttargetPath: string,\n\toptions: { config?: string } = {}\n): Promise<MonorepoResult> {\n\tconst startTime = performance.now();\n\tconst monorepo = await detectMonorepo(targetPath);\n\n\tif (!monorepo) {\n\t\t// Not a monorepo — scan as single project and wrap result\n\t\tconst result = await scan(targetPath, options);\n\t\treturn {\n\t\t\tisMonorepo: false,\n\t\t\tsubProjects: [{ name: \"default\", result }],\n\t\t\tcombined: result,\n\t\t\telapsedMs: result.elapsedMs,\n\t\t};\n\t}\n\n\tconst rootConfig = await loadConfig(targetPath, options.config);\n\tconst filesByProject = await collectMonorepoFiles(\n\t\ttargetPath,\n\t\tmonorepo,\n\t\trootConfig\n\t);\n\n\tconst subProjects: SubProjectResult[] = [];\n\tconst allDiagnostics: Diagnostic[] = [];\n\tconst allRuleErrors: RuleErrorInfo[] = [];\n\tlet totalFiles = 0;\n\n\tfor (const [name, files] of filesByProject) {\n\t\tif (files.length === 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst projectPath = join(targetPath, monorepo.projects.get(name)!);\n\t\tconst project = await detectProject(projectPath);\n\n\t\t// Load per-project config if available, falling back to root config\n\t\tconst projectConfig = await loadConfigWithFallback(projectPath, rootConfig);\n\n\t\tconst astProject = createAstParser(files);\n\t\tconst moduleGraph = buildModuleGraph(astProject, files);\n\t\tconst providers = resolveProviders(astProject, files);\n\t\tconst rules = filterRules(projectConfig);\n\t\tconst { diagnostics: rawDiagnostics, errors } = runRules(\n\t\t\tastProject,\n\t\t\tfiles,\n\t\t\trules,\n\t\t\t{ moduleGraph, providers, config: projectConfig }\n\t\t);\n\t\tconst diagnostics = filterIgnoredDiagnostics(rawDiagnostics, projectConfig);\n\n\t\tconst score = calculateScore(diagnostics, files.length);\n\t\tconst summary = buildSummary(diagnostics);\n\t\tconst ruleErrors: RuleErrorInfo[] = errors.map((e) => ({\n\t\t\truleId: e.ruleId,\n\t\t\terror: formatRuleError(e.error),\n\t\t}));\n\n\t\tconst result: DiagnoseResult = {\n\t\t\tscore,\n\t\t\tdiagnostics,\n\t\t\tproject: {\n\t\t\t\t...project,\n\t\t\t\tfileCount: files.length,\n\t\t\t\tmoduleCount: moduleGraph.modules.size,\n\t\t\t},\n\t\t\tsummary,\n\t\t\truleErrors,\n\t\t\telapsedMs: 0,\n\t\t};\n\n\t\tsubProjects.push({ name, result });\n\t\tallDiagnostics.push(...diagnostics);\n\t\tallRuleErrors.push(...ruleErrors);\n\t\ttotalFiles += files.length;\n\t}\n\n\tconst combinedScore = calculateScore(allDiagnostics, totalFiles);\n\tconst combinedSummary = buildSummary(allDiagnostics);\n\tconst elapsedMs = performance.now() - startTime;\n\n\tconst combined: DiagnoseResult = {\n\t\tscore: combinedScore,\n\t\tdiagnostics: allDiagnostics,\n\t\tproject: {\n\t\t\tname: \"monorepo\",\n\t\t\tnestVersion: subProjects[0]?.result.project.nestVersion ?? null,\n\t\t\torm: subProjects[0]?.result.project.orm ?? null,\n\t\t\tframework: subProjects[0]?.result.project.framework ?? null,\n\t\t\tfileCount: totalFiles,\n\t\t\tmoduleCount: subProjects.reduce(\n\t\t\t\t(sum, sp) => sum + sp.result.project.moduleCount,\n\t\t\t\t0\n\t\t\t),\n\t\t},\n\t\tsummary: combinedSummary,\n\t\truleErrors: allRuleErrors,\n\t\telapsedMs,\n\t};\n\n\treturn {\n\t\tisMonorepo: true,\n\t\tsubProjects,\n\t\tcombined,\n\t\telapsedMs,\n\t};\n}\n\nasync function loadConfigWithFallback(\n\tprojectPath: string,\n\tfallback: NestjsDoctorConfig\n): Promise<NestjsDoctorConfig> {\n\ttry {\n\t\treturn await loadConfig(projectPath);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction buildSummary(diagnostics: Diagnostic[]): DiagnoseSummary {\n\tconst summary: DiagnoseSummary = {\n\t\ttotal: 0,\n\t\terrors: 0,\n\t\twarnings: 0,\n\t\tinfo: 0,\n\t\tbyCategory: {\n\t\t\tsecurity: 0,\n\t\t\tperformance: 0,\n\t\t\tcorrectness: 0,\n\t\t\tarchitecture: 0,\n\t\t},\n\t};\n\n\tfor (const d of diagnostics) {\n\t\tsummary.total++;\n\t\tif (d.severity === \"error\") {\n\t\t\tsummary.errors++;\n\t\t} else if (d.severity === \"warning\") {\n\t\t\tsummary.warnings++;\n\t\t} else {\n\t\t\tsummary.info++;\n\t\t}\n\t\tsummary.byCategory[d.category]++;\n\t}\n\n\treturn summary;\n}\n","export class NestjsDoctorError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"NestjsDoctorError\";\n\t}\n}\n\nexport class ConfigurationError extends NestjsDoctorError {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"ConfigurationError\";\n\t}\n}\n\nexport class ScanError extends NestjsDoctorError {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"ScanError\";\n\t}\n}\n\nexport class ValidationError extends NestjsDoctorError {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"ValidationError\";\n\t}\n}\n","import { existsSync, statSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { scan, scanMonorepo } from \"../core/scanner.js\";\nimport { ValidationError } from \"../types/errors.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 {\n\tConfigurationError,\n\tNestjsDoctorError,\n\tScanError,\n\tValidationError,\n} from \"../types/errors.js\";\nexport type {\n\tDiagnoseResult,\n\tDiagnoseSummary,\n\tMonorepoResult,\n\tProjectInfo,\n\tRuleErrorInfo,\n\tScore,\n\tSubProjectResult,\n} from \"../types/result.js\";\n\nfunction validatePath(path: string): string {\n\tif (!path || path.trim() === \"\") {\n\t\tthrow new ValidationError(\n\t\t\t\"Path must be a non-empty string. Received an empty path.\"\n\t\t);\n\t}\n\n\tconst resolved = resolve(path);\n\n\tif (!existsSync(resolved)) {\n\t\tthrow new ValidationError(`Path does not exist: ${resolved}`);\n\t}\n\n\tconst stat = statSync(resolved);\n\tif (!stat.isDirectory()) {\n\t\tthrow new ValidationError(\n\t\t\t`Path must be a directory, not a file: ${resolved}`\n\t\t);\n\t}\n\n\treturn resolved;\n}\n\n/**\n * Scans a single NestJS project and returns a health diagnostic result.\n *\n * @param path - Path to the NestJS project root directory.\n * @param options - Optional configuration: `config` specifies a path to a config file.\n * @returns A `DiagnoseResult` containing the health score, diagnostics, and summary.\n * @throws {ValidationError} If the path is empty, doesn't exist, or isn't a directory.\n */\nexport async function diagnose(\n\tpath: string,\n\toptions: { config?: string } = {}\n) {\n\tconst targetPath = validatePath(path);\n\treturn await scan(targetPath, options);\n}\n\n/**\n * Scans a NestJS monorepo and returns per-project and combined diagnostics.\n *\n * Auto-detects monorepo structure from `nest-cli.json`. If the target is not a\n * monorepo, falls back to a single-project scan wrapped in the monorepo result format.\n *\n * @param path - Path to the monorepo root directory.\n * @param options - Optional configuration: `config` specifies a path to a config file.\n * @returns A `MonorepoResult` with sub-project results and combined score.\n * @throws {ValidationError} If the path is empty, doesn't exist, or isn't a directory.\n */\nexport async function diagnoseMonorepo(\n\tpath: string,\n\toptions: { config?: string } = {}\n) {\n\tconst targetPath = validatePath(path);\n\treturn await scanMonorepo(targetPath, options);\n}\n"],"mappings":"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"}
@@ -10,6 +10,59 @@ import picomatch from "picomatch";
10
10
  import pc from "picocolors";
11
11
  import ora from "ora";
12
12
 
13
+ //#region src/types/config.ts
14
+ const DEFAULT_CONFIG = {
15
+ include: ["**/*.ts"],
16
+ exclude: [
17
+ "node_modules/**",
18
+ "dist/**",
19
+ "build/**",
20
+ "coverage/**",
21
+ "**/*.spec.ts",
22
+ "**/*.test.ts",
23
+ "**/*.e2e-spec.ts",
24
+ "**/*.d.ts"
25
+ ]
26
+ };
27
+
28
+ //#endregion
29
+ //#region src/core/config-loader.ts
30
+ const CONFIG_FILENAMES = ["nestjs-doctor.config.json", ".nestjs-doctor.json"];
31
+ async function loadConfig(targetPath, configPath) {
32
+ if (configPath) return readConfigFile(configPath);
33
+ for (const filename of CONFIG_FILENAMES) try {
34
+ return await readConfigFile(join(targetPath, filename));
35
+ } catch {}
36
+ try {
37
+ const pkgRaw = await readFile(join(targetPath, "package.json"), "utf-8");
38
+ const pkg = JSON.parse(pkgRaw);
39
+ if (pkg["nestjs-doctor"] && typeof pkg["nestjs-doctor"] === "object") return mergeConfig(pkg["nestjs-doctor"]);
40
+ } catch {}
41
+ return { ...DEFAULT_CONFIG };
42
+ }
43
+ async function readConfigFile(path) {
44
+ const raw = await readFile(path, "utf-8");
45
+ return mergeConfig(JSON.parse(raw));
46
+ }
47
+ /**
48
+ * Merges user config with defaults.
49
+ *
50
+ * Merge semantics:
51
+ * - `include`: user replaces defaults entirely (user likely wants a specific scope)
52
+ * - `exclude`: user values are appended to defaults (additive, keeps safe defaults)
53
+ * - `ignore.rules`: user replaces defaults (no default ignored rules)
54
+ * - `ignore.files`: user replaces defaults (no default ignored files)
55
+ * - `rules`, `categories`, `thresholds`: shallow-merged with user taking precedence
56
+ */
57
+ function mergeConfig(userConfig) {
58
+ return {
59
+ ...DEFAULT_CONFIG,
60
+ ...userConfig,
61
+ exclude: [...DEFAULT_CONFIG.exclude ?? [], ...userConfig.exclude ?? []]
62
+ };
63
+ }
64
+
65
+ //#endregion
13
66
  //#region src/core/project-detector.ts
14
67
  async function detectMonorepo(targetPath) {
15
68
  const cliPath = join(targetPath, "nest-cli.json");
@@ -2246,59 +2299,6 @@ function calculateScore(diagnostics, fileCount) {
2246
2299
  };
2247
2300
  }
2248
2301
 
2249
- //#endregion
2250
- //#region src/types/config.ts
2251
- const DEFAULT_CONFIG = {
2252
- include: ["**/*.ts"],
2253
- exclude: [
2254
- "node_modules/**",
2255
- "dist/**",
2256
- "build/**",
2257
- "coverage/**",
2258
- "**/*.spec.ts",
2259
- "**/*.test.ts",
2260
- "**/*.e2e-spec.ts",
2261
- "**/*.d.ts"
2262
- ]
2263
- };
2264
-
2265
- //#endregion
2266
- //#region src/core/config-loader.ts
2267
- const CONFIG_FILENAMES = ["nestjs-doctor.config.json", ".nestjs-doctor.json"];
2268
- async function loadConfig(targetPath, configPath) {
2269
- if (configPath) return readConfigFile(configPath);
2270
- for (const filename of CONFIG_FILENAMES) try {
2271
- return await readConfigFile(join(targetPath, filename));
2272
- } catch {}
2273
- try {
2274
- const pkgRaw = await readFile(join(targetPath, "package.json"), "utf-8");
2275
- const pkg = JSON.parse(pkgRaw);
2276
- if (pkg["nestjs-doctor"] && typeof pkg["nestjs-doctor"] === "object") return mergeConfig(pkg["nestjs-doctor"]);
2277
- } catch {}
2278
- return { ...DEFAULT_CONFIG };
2279
- }
2280
- async function readConfigFile(path) {
2281
- const raw = await readFile(path, "utf-8");
2282
- return mergeConfig(JSON.parse(raw));
2283
- }
2284
- /**
2285
- * Merges user config with defaults.
2286
- *
2287
- * Merge semantics:
2288
- * - `include`: user replaces defaults entirely (user likely wants a specific scope)
2289
- * - `exclude`: user values are appended to defaults (additive, keeps safe defaults)
2290
- * - `ignore.rules`: user replaces defaults (no default ignored rules)
2291
- * - `ignore.files`: user replaces defaults (no default ignored files)
2292
- * - `rules`, `categories`, `thresholds`: shallow-merged with user taking precedence
2293
- */
2294
- function mergeConfig(userConfig) {
2295
- return {
2296
- ...DEFAULT_CONFIG,
2297
- ...userConfig,
2298
- exclude: [...DEFAULT_CONFIG.exclude ?? [], ...userConfig.exclude ?? []]
2299
- };
2300
- }
2301
-
2302
2302
  //#endregion
2303
2303
  //#region src/core/file-collector.ts
2304
2304
  async function collectFiles(targetPath, config = {}) {
@@ -2519,12 +2519,34 @@ const flags = {
2519
2519
  description: "JSON output",
2520
2520
  default: false
2521
2521
  },
2522
+ "min-score": {
2523
+ type: "string",
2524
+ description: "Minimum passing score (0-100). Exits with code 1 if below threshold"
2525
+ },
2522
2526
  config: {
2523
2527
  type: "string",
2524
2528
  description: "Config file path"
2525
2529
  }
2526
2530
  };
2527
2531
 
2532
+ //#endregion
2533
+ //#region src/cli/min-score.ts
2534
+ const validateMinScoreArg = (raw) => {
2535
+ if (raw.trim() === "") return `Invalid --min-score value: "${raw}". Must be an integer between 0 and 100.`;
2536
+ const num = Number(raw);
2537
+ if (!Number.isInteger(num)) return `Invalid --min-score value: "${raw}". Must be an integer between 0 and 100.`;
2538
+ if (num < 0 || num > 100) return `Invalid --min-score value: "${raw}". Must be an integer between 0 and 100.`;
2539
+ return null;
2540
+ };
2541
+ const resolveMinScore = (cliValue, configValue) => {
2542
+ if (cliValue !== void 0) return Number(cliValue);
2543
+ return configValue;
2544
+ };
2545
+ const checkMinScore = (actualScore, minScore) => {
2546
+ if (minScore === void 0) return true;
2547
+ return actualScore >= minScore;
2548
+ };
2549
+
2528
2550
  //#endregion
2529
2551
  //#region src/cli/output/highlighter.ts
2530
2552
  const highlighter = {
@@ -2838,6 +2860,14 @@ runMain(defineCommand({
2838
2860
  async run({ args }) {
2839
2861
  const targetPath = resolve(args.path ?? ".");
2840
2862
  const isSilent = args.score || args.json;
2863
+ const rawMinScore = args["min-score"];
2864
+ if (rawMinScore !== void 0) {
2865
+ const validationError = validateMinScoreArg(rawMinScore);
2866
+ if (validationError) {
2867
+ logger.error(validationError);
2868
+ process.exit(2);
2869
+ }
2870
+ }
2841
2871
  const monorepo = await detectMonorepo(targetPath);
2842
2872
  if (monorepo) {
2843
2873
  const scanSpinner = isSilent ? null : spinner(`Scanning monorepo (${monorepo.projects.size} projects)...`).start();
@@ -2846,15 +2876,22 @@ runMain(defineCommand({
2846
2876
  const projectNames = result.subProjects.map((sp) => sp.name).join(", ");
2847
2877
  scanSpinner.succeed(`Scanned ${highlighter.info(String(result.combined.project.fileCount))} files across ${highlighter.info(String(result.subProjects.length))} projects (${projectNames})`);
2848
2878
  }
2879
+ const monorepoMinScore = resolveMinScore(rawMinScore, (await loadConfig(targetPath, args.config)).minScore);
2849
2880
  if (args.score) {
2850
2881
  console.log(result.combined.score.value);
2882
+ if (!checkMinScore(result.combined.score.value, monorepoMinScore)) process.exit(1);
2851
2883
  return;
2852
2884
  }
2853
2885
  if (args.json) {
2854
2886
  printJsonReport(result.combined);
2887
+ if (!checkMinScore(result.combined.score.value, monorepoMinScore)) process.exit(1);
2855
2888
  return;
2856
2889
  }
2857
2890
  printMonorepoReport(result, args.verbose ?? false);
2891
+ if (!checkMinScore(result.combined.score.value, monorepoMinScore)) {
2892
+ logger.error(`Score ${result.combined.score.value} is below the minimum threshold of ${monorepoMinScore}.`);
2893
+ process.exit(1);
2894
+ }
2858
2895
  if (result.combined.summary.errors > 0) process.exit(1);
2859
2896
  return;
2860
2897
  }
@@ -2867,15 +2904,22 @@ runMain(defineCommand({
2867
2904
  if (project.orm) detailParts.push(highlighter.info(project.orm));
2868
2905
  scanSpinner.succeed(detailParts.join(" | "));
2869
2906
  }
2907
+ const minScore = resolveMinScore(rawMinScore, (await loadConfig(targetPath, args.config)).minScore);
2870
2908
  if (args.score) {
2871
2909
  console.log(result.score.value);
2910
+ if (!checkMinScore(result.score.value, minScore)) process.exit(1);
2872
2911
  return;
2873
2912
  }
2874
2913
  if (args.json) {
2875
2914
  printJsonReport(result);
2915
+ if (!checkMinScore(result.score.value, minScore)) process.exit(1);
2876
2916
  return;
2877
2917
  }
2878
2918
  printConsoleReport(result, args.verbose ?? false);
2919
+ if (!checkMinScore(result.score.value, minScore)) {
2920
+ logger.error(`Score ${result.score.value} is below the minimum threshold of ${minScore}.`);
2921
+ process.exit(1);
2922
+ }
2879
2923
  if (result.summary.errors > 0) process.exit(1);
2880
2924
  }
2881
2925
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nestjs-doctor",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "description": "Diagnostic CLI tool that scans NestJS codebases and produces a health score with actionable diagnostics",
5
5
  "type": "module",
6
6
  "bin": {