@oalacea/daemon 0.6.4 → 0.7.1
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 +268 -58
- package/bin/Dockerfile +158 -16
- package/bin/docker-entrypoint.sh +293 -0
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +22 -2
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/commands/command.types.d.ts +216 -0
- package/dist/cli/commands/command.types.d.ts.map +1 -0
- package/dist/cli/commands/command.types.js +64 -0
- package/dist/cli/commands/command.types.js.map +1 -0
- package/dist/cli/commands/history.command.d.ts +91 -0
- package/dist/cli/commands/history.command.d.ts.map +1 -0
- package/dist/cli/commands/history.command.js +336 -0
- package/dist/cli/commands/history.command.js.map +1 -0
- package/dist/cli/commands/index.d.ts +14 -3
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +7 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/optimize.command.d.ts +110 -0
- package/dist/cli/commands/optimize.command.d.ts.map +1 -0
- package/dist/cli/commands/optimize.command.js +497 -0
- package/dist/cli/commands/optimize.command.js.map +1 -0
- package/dist/cli/commands/report.command.d.ts +110 -0
- package/dist/cli/commands/report.command.d.ts.map +1 -0
- package/dist/cli/commands/report.command.js +532 -0
- package/dist/cli/commands/report.command.js.map +1 -0
- package/dist/cli/commands/review.command.d.ts +110 -0
- package/dist/cli/commands/review.command.d.ts.map +1 -0
- package/dist/cli/commands/review.command.js +520 -0
- package/dist/cli/commands/review.command.js.map +1 -0
- package/dist/cli/commands/score.command.d.ts +47 -0
- package/dist/cli/commands/score.command.d.ts.map +1 -0
- package/dist/cli/commands/score.command.js +261 -0
- package/dist/cli/commands/score.command.js.map +1 -0
- package/dist/cli/utils/index.d.ts +10 -0
- package/dist/cli/utils/index.d.ts.map +1 -0
- package/dist/cli/utils/index.js +10 -0
- package/dist/cli/utils/index.js.map +1 -0
- package/dist/cli/utils/output.d.ts +192 -0
- package/dist/cli/utils/output.d.ts.map +1 -0
- package/dist/cli/utils/output.js +411 -0
- package/dist/cli/utils/output.js.map +1 -0
- package/dist/cli/utils/progress.d.ts +204 -0
- package/dist/cli/utils/progress.d.ts.map +1 -0
- package/dist/cli/utils/progress.js +396 -0
- package/dist/cli/utils/progress.js.map +1 -0
- package/dist/core/types/index.d.ts +1 -0
- package/dist/core/types/index.d.ts.map +1 -1
- package/dist/core/types/project.types.d.ts +3 -3
- package/dist/core/types/project.types.d.ts.map +1 -1
- package/dist/core/types/scoring.types.d.ts +301 -0
- package/dist/core/types/scoring.types.d.ts.map +1 -0
- package/dist/core/types/scoring.types.js +8 -0
- package/dist/core/types/scoring.types.js.map +1 -0
- package/dist/services/detection/framework-detector.d.ts.map +1 -1
- package/dist/services/detection/framework-detector.js +74 -5
- package/dist/services/detection/framework-detector.js.map +1 -1
- package/dist/services/index.d.ts +12 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +14 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/optimization/detectors/bug-detector.d.ts +82 -0
- package/dist/services/optimization/detectors/bug-detector.d.ts.map +1 -0
- package/dist/services/optimization/detectors/bug-detector.js +443 -0
- package/dist/services/optimization/detectors/bug-detector.js.map +1 -0
- package/dist/services/optimization/detectors/code-smell-detector.d.ts +108 -0
- package/dist/services/optimization/detectors/code-smell-detector.d.ts.map +1 -0
- package/dist/services/optimization/detectors/code-smell-detector.js +569 -0
- package/dist/services/optimization/detectors/code-smell-detector.js.map +1 -0
- package/dist/services/optimization/detectors/index.d.ts +7 -0
- package/dist/services/optimization/detectors/index.d.ts.map +1 -0
- package/dist/services/optimization/detectors/index.js +7 -0
- package/dist/services/optimization/detectors/index.js.map +1 -0
- package/dist/services/optimization/detectors/perf-detector.d.ts +80 -0
- package/dist/services/optimization/detectors/perf-detector.d.ts.map +1 -0
- package/dist/services/optimization/detectors/perf-detector.js +451 -0
- package/dist/services/optimization/detectors/perf-detector.js.map +1 -0
- package/dist/services/optimization/index.d.ts +61 -0
- package/dist/services/optimization/index.d.ts.map +1 -0
- package/dist/services/optimization/index.js +69 -0
- package/dist/services/optimization/index.js.map +1 -0
- package/dist/services/optimization/optimization.service.d.ts +65 -0
- package/dist/services/optimization/optimization.service.d.ts.map +1 -0
- package/dist/services/optimization/optimization.service.js +511 -0
- package/dist/services/optimization/optimization.service.js.map +1 -0
- package/dist/services/optimization/optimization.types.d.ts +343 -0
- package/dist/services/optimization/optimization.types.d.ts.map +1 -0
- package/dist/services/optimization/optimization.types.js +8 -0
- package/dist/services/optimization/optimization.types.js.map +1 -0
- package/dist/services/optimization/optimizers/code-optimizer.d.ts +87 -0
- package/dist/services/optimization/optimizers/code-optimizer.d.ts.map +1 -0
- package/dist/services/optimization/optimizers/code-optimizer.js +436 -0
- package/dist/services/optimization/optimizers/code-optimizer.js.map +1 -0
- package/dist/services/optimization/optimizers/index.d.ts +7 -0
- package/dist/services/optimization/optimizers/index.d.ts.map +1 -0
- package/dist/services/optimization/optimizers/index.js +7 -0
- package/dist/services/optimization/optimizers/index.js.map +1 -0
- package/dist/services/optimization/optimizers/perf-optimizer.d.ts +64 -0
- package/dist/services/optimization/optimizers/perf-optimizer.d.ts.map +1 -0
- package/dist/services/optimization/optimizers/perf-optimizer.js +330 -0
- package/dist/services/optimization/optimizers/perf-optimizer.js.map +1 -0
- package/dist/services/optimization/optimizers/refact-optimizer.d.ts +82 -0
- package/dist/services/optimization/optimizers/refact-optimizer.d.ts.map +1 -0
- package/dist/services/optimization/optimizers/refact-optimizer.js +354 -0
- package/dist/services/optimization/optimizers/refact-optimizer.js.map +1 -0
- package/dist/services/optimization/patterns/anti-patterns.d.ts +31 -0
- package/dist/services/optimization/patterns/anti-patterns.d.ts.map +1 -0
- package/dist/services/optimization/patterns/anti-patterns.js +501 -0
- package/dist/services/optimization/patterns/anti-patterns.js.map +1 -0
- package/dist/services/optimization/patterns/index.d.ts +5 -0
- package/dist/services/optimization/patterns/index.d.ts.map +1 -0
- package/dist/services/optimization/patterns/index.js +5 -0
- package/dist/services/optimization/patterns/index.js.map +1 -0
- package/dist/services/reporting/export/chart.exporter.d.ts +59 -0
- package/dist/services/reporting/export/chart.exporter.d.ts.map +1 -0
- package/dist/services/reporting/export/chart.exporter.js +350 -0
- package/dist/services/reporting/export/chart.exporter.js.map +1 -0
- package/dist/services/reporting/export/index.d.ts +9 -0
- package/dist/services/reporting/export/index.d.ts.map +1 -0
- package/dist/services/reporting/export/index.js +10 -0
- package/dist/services/reporting/export/index.js.map +1 -0
- package/dist/services/reporting/export/pdf.exporter.d.ts +133 -0
- package/dist/services/reporting/export/pdf.exporter.d.ts.map +1 -0
- package/dist/services/reporting/export/pdf.exporter.js +270 -0
- package/dist/services/reporting/export/pdf.exporter.js.map +1 -0
- package/dist/services/reporting/history.service.d.ts +93 -0
- package/dist/services/reporting/history.service.d.ts.map +1 -0
- package/dist/services/reporting/history.service.js +285 -0
- package/dist/services/reporting/history.service.js.map +1 -0
- package/dist/services/reporting/index.d.ts +15 -0
- package/dist/services/reporting/index.d.ts.map +1 -0
- package/dist/services/reporting/index.js +16 -0
- package/dist/services/reporting/index.js.map +1 -0
- package/dist/services/reporting/report.service.d.ts +102 -0
- package/dist/services/reporting/report.service.d.ts.map +1 -0
- package/dist/services/reporting/report.service.js +240 -0
- package/dist/services/reporting/report.service.js.map +1 -0
- package/dist/services/reporting/reporting.types.d.ts +329 -0
- package/dist/services/reporting/reporting.types.d.ts.map +1 -0
- package/dist/services/reporting/reporting.types.js +8 -0
- package/dist/services/reporting/reporting.types.js.map +1 -0
- package/dist/services/reporting/templates/html.template.d.ts +81 -0
- package/dist/services/reporting/templates/html.template.d.ts.map +1 -0
- package/dist/services/reporting/templates/html.template.js +741 -0
- package/dist/services/reporting/templates/html.template.js.map +1 -0
- package/dist/services/reporting/templates/json.template.d.ts +85 -0
- package/dist/services/reporting/templates/json.template.d.ts.map +1 -0
- package/dist/services/reporting/templates/json.template.js +308 -0
- package/dist/services/reporting/templates/json.template.js.map +1 -0
- package/dist/services/reporting/templates/markdown.template.d.ts +69 -0
- package/dist/services/reporting/templates/markdown.template.d.ts.map +1 -0
- package/dist/services/reporting/templates/markdown.template.js +311 -0
- package/dist/services/reporting/templates/markdown.template.js.map +1 -0
- package/dist/services/reporting/trend-analyzer.d.ts +73 -0
- package/dist/services/reporting/trend-analyzer.d.ts.map +1 -0
- package/dist/services/reporting/trend-analyzer.js +291 -0
- package/dist/services/reporting/trend-analyzer.js.map +1 -0
- package/dist/services/review/analyzers/dependency-analyzer.d.ts +87 -0
- package/dist/services/review/analyzers/dependency-analyzer.d.ts.map +1 -0
- package/dist/services/review/analyzers/dependency-analyzer.js +458 -0
- package/dist/services/review/analyzers/dependency-analyzer.js.map +1 -0
- package/dist/services/review/analyzers/index.d.ts +13 -0
- package/dist/services/review/analyzers/index.d.ts.map +1 -0
- package/dist/services/review/analyzers/index.js +13 -0
- package/dist/services/review/analyzers/index.js.map +1 -0
- package/dist/services/review/analyzers/nestjs-analyzer.d.ts +210 -0
- package/dist/services/review/analyzers/nestjs-analyzer.d.ts.map +1 -0
- package/dist/services/review/analyzers/nestjs-analyzer.js +571 -0
- package/dist/services/review/analyzers/nestjs-analyzer.js.map +1 -0
- package/dist/services/review/analyzers/performance-analyzer.d.ts +91 -0
- package/dist/services/review/analyzers/performance-analyzer.d.ts.map +1 -0
- package/dist/services/review/analyzers/performance-analyzer.js +589 -0
- package/dist/services/review/analyzers/performance-analyzer.js.map +1 -0
- package/dist/services/review/analyzers/security-analyzer.d.ts +96 -0
- package/dist/services/review/analyzers/security-analyzer.d.ts.map +1 -0
- package/dist/services/review/analyzers/security-analyzer.js +512 -0
- package/dist/services/review/analyzers/security-analyzer.js.map +1 -0
- package/dist/services/review/analyzers/static-analyzer.d.ts +90 -0
- package/dist/services/review/analyzers/static-analyzer.d.ts.map +1 -0
- package/dist/services/review/analyzers/static-analyzer.js +423 -0
- package/dist/services/review/analyzers/static-analyzer.js.map +1 -0
- package/dist/services/review/fixers/auto-fixer.d.ts +94 -0
- package/dist/services/review/fixers/auto-fixer.d.ts.map +1 -0
- package/dist/services/review/fixers/auto-fixer.js +404 -0
- package/dist/services/review/fixers/auto-fixer.js.map +1 -0
- package/dist/services/review/fixers/index.d.ts +11 -0
- package/dist/services/review/fixers/index.d.ts.map +1 -0
- package/dist/services/review/fixers/index.js +11 -0
- package/dist/services/review/fixers/index.js.map +1 -0
- package/dist/services/review/fixers/refactor-suggester.d.ts +100 -0
- package/dist/services/review/fixers/refactor-suggester.d.ts.map +1 -0
- package/dist/services/review/fixers/refactor-suggester.js +555 -0
- package/dist/services/review/fixers/refactor-suggester.js.map +1 -0
- package/dist/services/review/fixers/test-generator.d.ts +99 -0
- package/dist/services/review/fixers/test-generator.d.ts.map +1 -0
- package/dist/services/review/fixers/test-generator.js +458 -0
- package/dist/services/review/fixers/test-generator.js.map +1 -0
- package/dist/services/review/index.d.ts +14 -0
- package/dist/services/review/index.d.ts.map +1 -0
- package/dist/services/review/index.js +14 -0
- package/dist/services/review/index.js.map +1 -0
- package/dist/services/review/reporters/fix-reporter.d.ts +67 -0
- package/dist/services/review/reporters/fix-reporter.d.ts.map +1 -0
- package/dist/services/review/reporters/fix-reporter.js +437 -0
- package/dist/services/review/reporters/fix-reporter.js.map +1 -0
- package/dist/services/review/reporters/index.d.ts +10 -0
- package/dist/services/review/reporters/index.d.ts.map +1 -0
- package/dist/services/review/reporters/index.js +10 -0
- package/dist/services/review/reporters/index.js.map +1 -0
- package/dist/services/review/reporters/score-reporter.d.ts +84 -0
- package/dist/services/review/reporters/score-reporter.d.ts.map +1 -0
- package/dist/services/review/reporters/score-reporter.js +560 -0
- package/dist/services/review/reporters/score-reporter.js.map +1 -0
- package/dist/services/review/review.service.d.ts +129 -0
- package/dist/services/review/review.service.d.ts.map +1 -0
- package/dist/services/review/review.service.js +396 -0
- package/dist/services/review/review.service.js.map +1 -0
- package/dist/services/review/review.types.d.ts +443 -0
- package/dist/services/review/review.types.d.ts.map +1 -0
- package/dist/services/review/review.types.js +11 -0
- package/dist/services/review/review.types.js.map +1 -0
- package/dist/services/scoring/dimensions/accessibility.analyzer.d.ts +53 -0
- package/dist/services/scoring/dimensions/accessibility.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/accessibility.analyzer.js +260 -0
- package/dist/services/scoring/dimensions/accessibility.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/backend-logic.analyzer.d.ts +138 -0
- package/dist/services/scoring/dimensions/backend-logic.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/backend-logic.analyzer.js +713 -0
- package/dist/services/scoring/dimensions/backend-logic.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/business-logic.analyzer.d.ts +142 -0
- package/dist/services/scoring/dimensions/business-logic.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/business-logic.analyzer.js +747 -0
- package/dist/services/scoring/dimensions/business-logic.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/code-quality.analyzer.d.ts +142 -0
- package/dist/services/scoring/dimensions/code-quality.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/code-quality.analyzer.js +685 -0
- package/dist/services/scoring/dimensions/code-quality.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/index.d.ts +18 -0
- package/dist/services/scoring/dimensions/index.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/index.js +27 -0
- package/dist/services/scoring/dimensions/index.js.map +1 -0
- package/dist/services/scoring/dimensions/performance.analyzer.d.ts +125 -0
- package/dist/services/scoring/dimensions/performance.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/performance.analyzer.js +615 -0
- package/dist/services/scoring/dimensions/performance.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/security.analyzer.d.ts +53 -0
- package/dist/services/scoring/dimensions/security.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/security.analyzer.js +327 -0
- package/dist/services/scoring/dimensions/security.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/seo.analyzer.d.ts +77 -0
- package/dist/services/scoring/dimensions/seo.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/seo.analyzer.js +502 -0
- package/dist/services/scoring/dimensions/seo.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/test-coverage.analyzer.d.ts +106 -0
- package/dist/services/scoring/dimensions/test-coverage.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/test-coverage.analyzer.js +496 -0
- package/dist/services/scoring/dimensions/test-coverage.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/ui-ux.analyzer.d.ts +126 -0
- package/dist/services/scoring/dimensions/ui-ux.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/ui-ux.analyzer.js +665 -0
- package/dist/services/scoring/dimensions/ui-ux.analyzer.js.map +1 -0
- package/dist/services/scoring/index.d.ts +10 -0
- package/dist/services/scoring/index.d.ts.map +1 -0
- package/dist/services/scoring/index.js +10 -0
- package/dist/services/scoring/index.js.map +1 -0
- package/dist/services/scoring/scoring-service.d.ts +222 -0
- package/dist/services/scoring/scoring-service.d.ts.map +1 -0
- package/dist/services/scoring/scoring-service.js +636 -0
- package/dist/services/scoring/scoring-service.js.map +1 -0
- package/package.json +13 -4
- package/templates/README.md +183 -0
- package/templates/nestjs/controller.spec.ts +203 -0
- package/templates/nestjs/e2e/api.e2e-spec.ts +451 -0
- package/templates/nestjs/e2e/auth.e2e-spec.ts +533 -0
- package/templates/nestjs/fixtures/test-module.ts +311 -0
- package/templates/nestjs/guard.spec.ts +314 -0
- package/templates/nestjs/interceptor.spec.ts +458 -0
- package/templates/nestjs/module.spec.ts +173 -0
- package/templates/nestjs/pipe.spec.ts +474 -0
- package/templates/nestjs/service.spec.ts +296 -0
- package/templates/rust/Cargo.toml +72 -0
- package/templates/rust/actix-controller.test.rs +114 -0
- package/templates/rust/axum-handler.test.rs +117 -0
- package/templates/rust/integration.test.rs +63 -0
- package/templates/rust/rocket-route.test.rs +106 -0
- package/templates/rust/unit.test.rs +38 -0
|
@@ -0,0 +1,747 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Business Logic Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Analyzes business logic quality including domain separation, business rule consistency,
|
|
5
|
+
* edge case handling, and state management appropriateness.
|
|
6
|
+
*
|
|
7
|
+
* @module services/scoring/dimensions/business-logic
|
|
8
|
+
*/
|
|
9
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
10
|
+
import { join, extname, relative } from 'node:path';
|
|
11
|
+
import { existsSync } from 'node:fs';
|
|
12
|
+
import { CommandExecutor } from '../../../shared/utils/command-executor.js';
|
|
13
|
+
import { createLogger } from '../../../shared/utils/logger.js';
|
|
14
|
+
/**
|
|
15
|
+
* Business Logic Analyzer
|
|
16
|
+
*
|
|
17
|
+
* Evaluates business logic quality across multiple dimensions:
|
|
18
|
+
* - Domain separation (features/ pattern)
|
|
19
|
+
* - Business rule consistency
|
|
20
|
+
* - Edge case handling
|
|
21
|
+
* - State management appropriateness
|
|
22
|
+
*/
|
|
23
|
+
export class BusinessLogicAnalyzer {
|
|
24
|
+
/** Analyzer configuration */
|
|
25
|
+
config = {
|
|
26
|
+
dimension: 'business-logic',
|
|
27
|
+
defaultWeight: 0.05,
|
|
28
|
+
estimatedDuration: 20000,
|
|
29
|
+
supportedFrameworks: ['Next.js', 'React', 'Vue', 'Nuxt', 'Svelte', 'Angular', 'Remix', 'SvelteKit', 'Astro', 'Gatsby'],
|
|
30
|
+
};
|
|
31
|
+
logger;
|
|
32
|
+
executor;
|
|
33
|
+
_checkDomainSeparation;
|
|
34
|
+
_checkBusinessRules;
|
|
35
|
+
_checkEdgeCaseHandling;
|
|
36
|
+
_checkStateManagement;
|
|
37
|
+
constructor(options = {}) {
|
|
38
|
+
this.logger = createLogger('BusinessLogicAnalyzer');
|
|
39
|
+
this.executor = new CommandExecutor();
|
|
40
|
+
this._checkDomainSeparation = options.checkDomainSeparation ?? true;
|
|
41
|
+
this._checkBusinessRules = options.checkBusinessRules ?? true;
|
|
42
|
+
this._checkEdgeCaseHandling = options.checkEdgeCaseHandling ?? true;
|
|
43
|
+
this._checkStateManagement = options.checkStateManagement ?? true;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get the dimension this analyzer handles
|
|
47
|
+
*/
|
|
48
|
+
getDimension() {
|
|
49
|
+
return 'business-logic';
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get the default weight for this dimension
|
|
53
|
+
*/
|
|
54
|
+
getWeight() {
|
|
55
|
+
return 0.10; // 10% weight in overall score
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Analyze business logic for a project
|
|
59
|
+
*
|
|
60
|
+
* @param projectPath - Path to the project root
|
|
61
|
+
* @param _framework - Detected framework (optional, for framework-specific analysis)
|
|
62
|
+
* @param _options - Scoring options (optional)
|
|
63
|
+
* @returns Dimension score with business logic metrics
|
|
64
|
+
*/
|
|
65
|
+
async analyze(projectPath, _framework, _options) {
|
|
66
|
+
const startTime = performance.now();
|
|
67
|
+
this.logger.info(`Analyzing business logic for: ${projectPath}`);
|
|
68
|
+
const issues = [];
|
|
69
|
+
const improvements = [];
|
|
70
|
+
try {
|
|
71
|
+
// Analyze project structure
|
|
72
|
+
const structureType = this._checkDomainSeparation ? await this.analyzeProjectStructure(projectPath) : 'flat';
|
|
73
|
+
// Analyze feature modules
|
|
74
|
+
const featureModules = this._checkDomainSeparation ? await this.analyzeFeatureModules(projectPath) : [];
|
|
75
|
+
// Check state management
|
|
76
|
+
const stateManagement = this._checkStateManagement ? await this.analyzeStateManagement(projectPath) : 'unknown';
|
|
77
|
+
// Check business rules consistency
|
|
78
|
+
const ruleConsistency = this._checkBusinessRules ? await this.checkBusinessRuleConsistency(projectPath) : 0;
|
|
79
|
+
// Check edge case handling
|
|
80
|
+
const edgeCaseHandling = this._checkEdgeCaseHandling ? await this.checkEdgeCaseHandlingPatterns(projectPath) : 0;
|
|
81
|
+
// Check DDD patterns
|
|
82
|
+
const dddAdherence = await this.checkDddPatterns(projectPath);
|
|
83
|
+
const metrics = {
|
|
84
|
+
domainSeparation: this.calculateDomainSeparationScore(structureType, featureModules),
|
|
85
|
+
structureType,
|
|
86
|
+
ruleConsistency,
|
|
87
|
+
edgeCaseHandling,
|
|
88
|
+
stateManagementScore: this.calculateStateManagementScore(stateManagement),
|
|
89
|
+
stateManagement,
|
|
90
|
+
featureIsolation: this.calculateFeatureIsolation(featureModules),
|
|
91
|
+
dddAdherence,
|
|
92
|
+
};
|
|
93
|
+
// Build issues list
|
|
94
|
+
issues.push(...this.identifyBusinessLogicIssues(metrics));
|
|
95
|
+
// Build improvements list
|
|
96
|
+
improvements.push(...this.generateBusinessLogicImprovements(metrics));
|
|
97
|
+
// Calculate final score
|
|
98
|
+
const score = this.calculateBusinessLogicScore(metrics);
|
|
99
|
+
const duration = Math.round(performance.now() - startTime);
|
|
100
|
+
return {
|
|
101
|
+
dimension: this.getDimension(),
|
|
102
|
+
score,
|
|
103
|
+
weight: this.getWeight(),
|
|
104
|
+
weightedScore: score * this.getWeight(),
|
|
105
|
+
issues,
|
|
106
|
+
improvements,
|
|
107
|
+
metadata: {
|
|
108
|
+
itemsChecked: 5 + featureModules.length,
|
|
109
|
+
itemsPassed: this.countPassedChecks(metrics),
|
|
110
|
+
metrics: {
|
|
111
|
+
structureType: metrics.structureType,
|
|
112
|
+
domainSeparation: metrics.domainSeparation,
|
|
113
|
+
ruleConsistency: metrics.ruleConsistency,
|
|
114
|
+
edgeCaseHandling: metrics.edgeCaseHandling,
|
|
115
|
+
stateManagement: metrics.stateManagement,
|
|
116
|
+
stateManagementScore: metrics.stateManagementScore,
|
|
117
|
+
featureIsolation: metrics.featureIsolation,
|
|
118
|
+
dddAdherence: metrics.dddAdherence,
|
|
119
|
+
featureModuleCount: featureModules.length,
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
this.logger.error('Error analyzing business logic', error);
|
|
126
|
+
issues.push({
|
|
127
|
+
severity: 'medium',
|
|
128
|
+
category: 'architecture',
|
|
129
|
+
description: `Failed to analyze business logic: ${error instanceof Error ? error.message : String(error)}`,
|
|
130
|
+
fixable: false,
|
|
131
|
+
});
|
|
132
|
+
const duration = Math.round(performance.now() - startTime);
|
|
133
|
+
return {
|
|
134
|
+
dimension: this.getDimension(),
|
|
135
|
+
score: 50,
|
|
136
|
+
weight: this.getWeight(),
|
|
137
|
+
weightedScore: 50 * this.getWeight(),
|
|
138
|
+
issues,
|
|
139
|
+
improvements,
|
|
140
|
+
metadata: { error: String(error) },
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Analyze project structure
|
|
146
|
+
*/
|
|
147
|
+
async analyzeProjectStructure(projectPath) {
|
|
148
|
+
const srcDir = join(projectPath, 'src');
|
|
149
|
+
const appDir = join(projectPath, 'app');
|
|
150
|
+
// Check for features directory pattern
|
|
151
|
+
const featuresDir = join(srcDir, 'features');
|
|
152
|
+
if (existsSync(featuresDir)) {
|
|
153
|
+
return 'features';
|
|
154
|
+
}
|
|
155
|
+
// Check for layered architecture
|
|
156
|
+
if (existsSync(join(srcDir, 'services')) && existsSync(join(srcDir, 'controllers'))) {
|
|
157
|
+
return 'layered';
|
|
158
|
+
}
|
|
159
|
+
// Check for modular structure
|
|
160
|
+
const modulesDir = join(srcDir, 'modules');
|
|
161
|
+
if (existsSync(modulesDir)) {
|
|
162
|
+
return 'modular';
|
|
163
|
+
}
|
|
164
|
+
// Check if flat (everything in one directory)
|
|
165
|
+
if (existsSync(srcDir)) {
|
|
166
|
+
try {
|
|
167
|
+
const entries = await readdir(srcDir, { withFileTypes: true });
|
|
168
|
+
const dirs = entries.filter((e) => e.isDirectory());
|
|
169
|
+
if (dirs.length <= 3) {
|
|
170
|
+
return 'flat';
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// Ignore
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return 'unknown';
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Analyze feature modules
|
|
181
|
+
*/
|
|
182
|
+
async analyzeFeatureModules(projectPath) {
|
|
183
|
+
const modules = [];
|
|
184
|
+
const featuresDir = join(projectPath, 'src', 'features');
|
|
185
|
+
const modulesDir = join(projectPath, 'src', 'modules');
|
|
186
|
+
const appDir = join(projectPath, 'app');
|
|
187
|
+
if (existsSync(featuresDir)) {
|
|
188
|
+
await this.scanFeaturesDirectory(featuresDir, modules);
|
|
189
|
+
}
|
|
190
|
+
else if (existsSync(modulesDir)) {
|
|
191
|
+
await this.scanFeaturesDirectory(modulesDir, modules);
|
|
192
|
+
}
|
|
193
|
+
else if (existsSync(appDir)) {
|
|
194
|
+
// Analyze app directory structure
|
|
195
|
+
await this.scanAppDirectory(appDir, modules);
|
|
196
|
+
}
|
|
197
|
+
return modules;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Scan features directory for feature modules
|
|
201
|
+
*/
|
|
202
|
+
async scanFeaturesDirectory(dir, modules) {
|
|
203
|
+
try {
|
|
204
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
205
|
+
for (const entry of entries) {
|
|
206
|
+
const fullPath = join(dir, entry.name);
|
|
207
|
+
if (entry.isDirectory()) {
|
|
208
|
+
// Skip common non-feature directories
|
|
209
|
+
if (['shared', 'common', 'lib', 'utils', 'components'].includes(entry.name)) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
const feature = await this.analyzeFeatureModule(fullPath, entry.name);
|
|
213
|
+
if (feature) {
|
|
214
|
+
modules.push(feature);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
// Directory not accessible
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Scan app directory for feature modules
|
|
225
|
+
*/
|
|
226
|
+
async scanAppDirectory(dir, modules) {
|
|
227
|
+
try {
|
|
228
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
229
|
+
for (const entry of entries) {
|
|
230
|
+
const fullPath = join(dir, entry.name);
|
|
231
|
+
if (entry.isDirectory()) {
|
|
232
|
+
if (entry.name.startsWith('(') && entry.name.endsWith(')')) {
|
|
233
|
+
// Next.js route group - treat as potential feature
|
|
234
|
+
const feature = await this.analyzeFeatureModule(fullPath, entry.name);
|
|
235
|
+
if (feature) {
|
|
236
|
+
modules.push(feature);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
await this.scanAppDirectory(fullPath, modules);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
// Directory not accessible
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Analyze a single feature module
|
|
251
|
+
*/
|
|
252
|
+
async analyzeFeatureModule(modulePath, name) {
|
|
253
|
+
try {
|
|
254
|
+
const entries = await readdir(modulePath, { withFileTypes: true });
|
|
255
|
+
const feature = {
|
|
256
|
+
name,
|
|
257
|
+
path: relative(process.cwd(), modulePath),
|
|
258
|
+
hasComponents: false,
|
|
259
|
+
hasHooks: false,
|
|
260
|
+
hasServices: false,
|
|
261
|
+
hasTypes: false,
|
|
262
|
+
hasActions: false,
|
|
263
|
+
isolationScore: 0,
|
|
264
|
+
};
|
|
265
|
+
let hasFeatureFiles = 0;
|
|
266
|
+
let totalFeatureFiles = 0;
|
|
267
|
+
for (const entry of entries) {
|
|
268
|
+
const ext = extname(entry.name);
|
|
269
|
+
if (entry.isFile()) {
|
|
270
|
+
totalFeatureFiles++;
|
|
271
|
+
if (entry.name.includes('.test.') || entry.name.includes('.spec.')) {
|
|
272
|
+
// Test files are good
|
|
273
|
+
hasFeatureFiles++;
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (['.ts', '.tsx', '.js', '.jsx'].includes(ext)) {
|
|
277
|
+
try {
|
|
278
|
+
const content = await readFile(join(modulePath, entry.name), 'utf-8');
|
|
279
|
+
// Check for feature-specific patterns
|
|
280
|
+
if (/component|Component/.test(content) || entry.name.includes('component')) {
|
|
281
|
+
feature.hasComponents = true;
|
|
282
|
+
hasFeatureFiles++;
|
|
283
|
+
}
|
|
284
|
+
if (/use[A-Z]/.test(content) || entry.name.includes('hook') || entry.name.includes('use')) {
|
|
285
|
+
feature.hasHooks = true;
|
|
286
|
+
hasFeatureFiles++;
|
|
287
|
+
}
|
|
288
|
+
if (/service|Service/.test(content) || entry.name.includes('service')) {
|
|
289
|
+
feature.hasServices = true;
|
|
290
|
+
hasFeatureFiles++;
|
|
291
|
+
}
|
|
292
|
+
if (/interface|type/.test(content) || entry.name.endsWith('.types.ts') || entry.name.endsWith('.types.js')) {
|
|
293
|
+
feature.hasTypes = true;
|
|
294
|
+
hasFeatureFiles++;
|
|
295
|
+
}
|
|
296
|
+
if (/action|Action|server/.test(content)) {
|
|
297
|
+
feature.hasActions = true;
|
|
298
|
+
hasFeatureFiles++;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
// Skip unreadable files
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// Calculate isolation score based on self-contained files
|
|
308
|
+
feature.isolationScore = totalFeatureFiles > 0 ? hasFeatureFiles / totalFeatureFiles : 0;
|
|
309
|
+
return feature;
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Analyze state management
|
|
317
|
+
*/
|
|
318
|
+
async analyzeStateManagement(projectPath) {
|
|
319
|
+
try {
|
|
320
|
+
const pkgPath = join(projectPath, 'package.json');
|
|
321
|
+
const pkgContent = await readFile(pkgPath, 'utf-8');
|
|
322
|
+
const pkg = JSON.parse(pkgContent);
|
|
323
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
324
|
+
if ('@tanstack/react-query' in deps || '@tanstack/react-query' in deps) {
|
|
325
|
+
return 'tanstack-query';
|
|
326
|
+
}
|
|
327
|
+
if ('zustand' in deps) {
|
|
328
|
+
return 'zustand';
|
|
329
|
+
}
|
|
330
|
+
if ('@reduxjs/toolkit' in deps || 'redux' in deps) {
|
|
331
|
+
return 'redux';
|
|
332
|
+
}
|
|
333
|
+
// Check if using only React Context
|
|
334
|
+
const srcDir = join(projectPath, 'src');
|
|
335
|
+
if (existsSync(srcDir)) {
|
|
336
|
+
const hasContext = await this.directoryContainsPattern(srcDir, /createContext|useContext|Context\.Provider/);
|
|
337
|
+
if (hasContext) {
|
|
338
|
+
return 'context';
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return 'local';
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
return 'unknown';
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Check business rule consistency
|
|
349
|
+
*/
|
|
350
|
+
async checkBusinessRuleConsistency(projectPath) {
|
|
351
|
+
// Check for consistent validation patterns
|
|
352
|
+
const srcDir = join(projectPath, 'src');
|
|
353
|
+
if (!existsSync(srcDir)) {
|
|
354
|
+
return 50; // Neutral score
|
|
355
|
+
}
|
|
356
|
+
let hasValidation = false;
|
|
357
|
+
let hasSchema = false;
|
|
358
|
+
let hasConstants = false;
|
|
359
|
+
await this.scanDirectoryForPatterns(srcDir, [
|
|
360
|
+
{ pattern: /zod\.|Joi|yup\./, found: () => { hasValidation = true; } },
|
|
361
|
+
{ pattern: /schema|Schema/i, found: () => { hasSchema = true; } },
|
|
362
|
+
{ pattern: /const\s+[A-Z_]+\s*=/, found: () => { hasConstants = true; } },
|
|
363
|
+
]);
|
|
364
|
+
let score = 0;
|
|
365
|
+
if (hasValidation)
|
|
366
|
+
score += 40;
|
|
367
|
+
if (hasSchema)
|
|
368
|
+
score += 30;
|
|
369
|
+
if (hasConstants)
|
|
370
|
+
score += 30;
|
|
371
|
+
return score;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Check edge case handling
|
|
375
|
+
*/
|
|
376
|
+
async checkEdgeCaseHandlingPatterns(projectPath) {
|
|
377
|
+
const srcDir = join(projectPath, 'src');
|
|
378
|
+
const appDir = join(projectPath, 'app');
|
|
379
|
+
const dirsToCheck = [];
|
|
380
|
+
if (existsSync(srcDir))
|
|
381
|
+
dirsToCheck.push(srcDir);
|
|
382
|
+
if (existsSync(appDir))
|
|
383
|
+
dirsToCheck.push(appDir);
|
|
384
|
+
let edgeCasePatterns = 0;
|
|
385
|
+
let totalPatterns = 0;
|
|
386
|
+
const edgeCaseRegexes = [
|
|
387
|
+
/if\s*\(\s*!/,
|
|
388
|
+
/\|\|/,
|
|
389
|
+
/\?\?/,
|
|
390
|
+
/else\s*if/,
|
|
391
|
+
/try\s*{/,
|
|
392
|
+
/catch\s*\(/,
|
|
393
|
+
/throw\s+new/,
|
|
394
|
+
/\.default/,
|
|
395
|
+
/undefined/i,
|
|
396
|
+
/null/i,
|
|
397
|
+
];
|
|
398
|
+
for (const dir of dirsToCheck) {
|
|
399
|
+
await this.scanDirectoryForEdgeCases(dir, (count) => {
|
|
400
|
+
edgeCasePatterns += count;
|
|
401
|
+
totalPatterns += edgeCaseRegexes.length;
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
if (totalPatterns === 0)
|
|
405
|
+
return 50;
|
|
406
|
+
return Math.min(100, (edgeCasePatterns / totalPatterns) * 100);
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Scan directory for edge case patterns
|
|
410
|
+
*/
|
|
411
|
+
async scanDirectoryForEdgeCases(dir, callback) {
|
|
412
|
+
try {
|
|
413
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
414
|
+
for (const entry of entries) {
|
|
415
|
+
const fullPath = join(dir, entry.name);
|
|
416
|
+
if (entry.isDirectory()) {
|
|
417
|
+
if (!['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
|
|
418
|
+
await this.scanDirectoryForEdgeCases(fullPath, callback);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
else if (entry.isFile()) {
|
|
422
|
+
const ext = extname(entry.name);
|
|
423
|
+
if (['.ts', '.tsx', '.js', '.jsx'].includes(ext)) {
|
|
424
|
+
try {
|
|
425
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
426
|
+
let count = 0;
|
|
427
|
+
const edgeCaseRegexes = [
|
|
428
|
+
/if\s*\(\s*!/,
|
|
429
|
+
/\|\|/,
|
|
430
|
+
/\?\?/,
|
|
431
|
+
/else\s*if/,
|
|
432
|
+
/try\s*{/,
|
|
433
|
+
/catch\s*\(/,
|
|
434
|
+
];
|
|
435
|
+
for (const regex of edgeCaseRegexes) {
|
|
436
|
+
if (regex.test(content)) {
|
|
437
|
+
count++;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
callback(count);
|
|
441
|
+
}
|
|
442
|
+
catch {
|
|
443
|
+
// Skip unreadable files
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
catch {
|
|
450
|
+
// Directory not accessible
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Check DDD patterns
|
|
455
|
+
*/
|
|
456
|
+
async checkDddPatterns(projectPath) {
|
|
457
|
+
let score = 0;
|
|
458
|
+
const srcDir = join(projectPath, 'src');
|
|
459
|
+
if (!existsSync(srcDir)) {
|
|
460
|
+
return 0;
|
|
461
|
+
}
|
|
462
|
+
// Check for DDD directory structure
|
|
463
|
+
const dddDirs = ['domain', 'application', 'infrastructure', 'interfaces'];
|
|
464
|
+
for (const dddDir of dddDirs) {
|
|
465
|
+
if (existsSync(join(srcDir, dddDir))) {
|
|
466
|
+
score += 25;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return Math.min(100, score);
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Scan directory for patterns
|
|
473
|
+
*/
|
|
474
|
+
async scanDirectoryForPatterns(dir, patterns) {
|
|
475
|
+
try {
|
|
476
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
477
|
+
for (const entry of entries) {
|
|
478
|
+
const fullPath = join(dir, entry.name);
|
|
479
|
+
if (entry.isDirectory()) {
|
|
480
|
+
if (!['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
|
|
481
|
+
await this.scanDirectoryForPatterns(fullPath, patterns);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
else if (entry.isFile()) {
|
|
485
|
+
const ext = extname(entry.name);
|
|
486
|
+
if (['.ts', '.tsx', '.js', '.jsx'].includes(ext)) {
|
|
487
|
+
try {
|
|
488
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
489
|
+
for (const { pattern, found } of patterns) {
|
|
490
|
+
if (pattern.test(content)) {
|
|
491
|
+
found();
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
catch {
|
|
497
|
+
// Skip unreadable files
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
catch {
|
|
504
|
+
// Directory not accessible
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Check if directory contains pattern
|
|
509
|
+
*/
|
|
510
|
+
async directoryContainsPattern(dir, pattern) {
|
|
511
|
+
let found = false;
|
|
512
|
+
await this.scanDirectoryForPatterns(dir, [
|
|
513
|
+
{ pattern, found: () => { found = true; } },
|
|
514
|
+
]);
|
|
515
|
+
return found;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Calculate domain separation score
|
|
519
|
+
*/
|
|
520
|
+
calculateDomainSeparationScore(structureType, featureModules) {
|
|
521
|
+
let score = 0;
|
|
522
|
+
// Base score from structure type
|
|
523
|
+
switch (structureType) {
|
|
524
|
+
case 'features':
|
|
525
|
+
score += 50;
|
|
526
|
+
break;
|
|
527
|
+
case 'layered':
|
|
528
|
+
score += 30;
|
|
529
|
+
break;
|
|
530
|
+
case 'modular':
|
|
531
|
+
score += 40;
|
|
532
|
+
break;
|
|
533
|
+
case 'flat':
|
|
534
|
+
score += 10;
|
|
535
|
+
break;
|
|
536
|
+
default:
|
|
537
|
+
score += 20;
|
|
538
|
+
}
|
|
539
|
+
// Bonus for well-structured feature modules
|
|
540
|
+
if (featureModules.length > 0) {
|
|
541
|
+
const avgIsolation = featureModules.reduce((sum, m) => sum + m.isolationScore, 0) / featureModules.length;
|
|
542
|
+
score += avgIsolation * 50;
|
|
543
|
+
}
|
|
544
|
+
return Math.min(100, score);
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Calculate feature isolation score
|
|
548
|
+
*/
|
|
549
|
+
calculateFeatureIsolation(featureModules) {
|
|
550
|
+
if (featureModules.length === 0)
|
|
551
|
+
return 0;
|
|
552
|
+
let totalIsolation = 0;
|
|
553
|
+
for (const module of featureModules) {
|
|
554
|
+
let hasAll = 0;
|
|
555
|
+
if (module.hasComponents)
|
|
556
|
+
hasAll++;
|
|
557
|
+
if (module.hasHooks)
|
|
558
|
+
hasAll++;
|
|
559
|
+
if (module.hasTypes)
|
|
560
|
+
hasAll++;
|
|
561
|
+
if (module.hasActions)
|
|
562
|
+
hasAll++;
|
|
563
|
+
totalIsolation += hasAll / 4;
|
|
564
|
+
}
|
|
565
|
+
return (totalIsolation / featureModules.length) * 100;
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Calculate state management score
|
|
569
|
+
*/
|
|
570
|
+
calculateStateManagementScore(stateManagement) {
|
|
571
|
+
const scores = {
|
|
572
|
+
'tanstack-query': 100,
|
|
573
|
+
'zustand': 90,
|
|
574
|
+
'redux': 80,
|
|
575
|
+
'context': 60,
|
|
576
|
+
'local': 40,
|
|
577
|
+
'unknown': 50,
|
|
578
|
+
};
|
|
579
|
+
return scores[stateManagement];
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Identify business logic issues
|
|
583
|
+
*/
|
|
584
|
+
identifyBusinessLogicIssues(metrics) {
|
|
585
|
+
const issues = [];
|
|
586
|
+
if (metrics.structureType === 'flat') {
|
|
587
|
+
issues.push({
|
|
588
|
+
severity: 'medium',
|
|
589
|
+
category: 'architecture',
|
|
590
|
+
description: 'Flat project structure detected - consider feature-based organization',
|
|
591
|
+
fixable: true,
|
|
592
|
+
suggestion: 'Organize code by features using the features/ pattern for better maintainability',
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
if (metrics.domainSeparation < 50) {
|
|
596
|
+
issues.push({
|
|
597
|
+
severity: 'medium',
|
|
598
|
+
category: 'architecture',
|
|
599
|
+
description: 'Poor domain separation - business logic mixed with UI',
|
|
600
|
+
fixable: true,
|
|
601
|
+
suggestion: 'Separate business logic into services and use feature-based organization',
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
if (metrics.ruleConsistency < 60) {
|
|
605
|
+
issues.push({
|
|
606
|
+
severity: 'low',
|
|
607
|
+
category: 'architecture',
|
|
608
|
+
description: 'Inconsistent business rule patterns detected',
|
|
609
|
+
fixable: true,
|
|
610
|
+
suggestion: 'Centralize validation rules and use schemas for consistent validation',
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
if (metrics.edgeCaseHandling < 50) {
|
|
614
|
+
issues.push({
|
|
615
|
+
severity: 'low',
|
|
616
|
+
category: 'error-handling',
|
|
617
|
+
description: 'Insufficient edge case handling detected',
|
|
618
|
+
fixable: true,
|
|
619
|
+
suggestion: 'Add proper null checks, error handling, and default values throughout the codebase',
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
if (metrics.stateManagement === 'local' && metrics.stateManagementScore < 50) {
|
|
623
|
+
issues.push({
|
|
624
|
+
severity: 'low',
|
|
625
|
+
category: 'architecture',
|
|
626
|
+
description: 'Only local state management detected - consider state management library for server data',
|
|
627
|
+
fixable: true,
|
|
628
|
+
suggestion: 'Use TanStack Query for server state and Zustand for client state',
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
return issues;
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Generate business logic improvements
|
|
635
|
+
*/
|
|
636
|
+
generateBusinessLogicImprovements(metrics) {
|
|
637
|
+
const improvements = [];
|
|
638
|
+
if (metrics.structureType === 'flat' || metrics.domainSeparation < 60) {
|
|
639
|
+
improvements.push({
|
|
640
|
+
type: 'refactor',
|
|
641
|
+
description: 'Reorganize code into feature-based modules',
|
|
642
|
+
effort: 'significant',
|
|
643
|
+
impact: 'high',
|
|
644
|
+
steps: [
|
|
645
|
+
'Create features/ directory',
|
|
646
|
+
'Group related components, hooks, and services by feature',
|
|
647
|
+
'Create barrel exports for each feature',
|
|
648
|
+
'Update imports to use feature paths',
|
|
649
|
+
],
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
if (metrics.ruleConsistency < 70) {
|
|
653
|
+
improvements.push({
|
|
654
|
+
type: 'refactor',
|
|
655
|
+
description: 'Centralize business rules and validation',
|
|
656
|
+
effort: 'moderate',
|
|
657
|
+
impact: 'medium',
|
|
658
|
+
steps: [
|
|
659
|
+
'Create schemas using Zod or similar',
|
|
660
|
+
'Extract business rules into service functions',
|
|
661
|
+
'Define constants for business values',
|
|
662
|
+
'Create shared validation utilities',
|
|
663
|
+
],
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
if (metrics.edgeCaseHandling < 60) {
|
|
667
|
+
improvements.push({
|
|
668
|
+
type: 'refactor',
|
|
669
|
+
description: 'Improve edge case handling',
|
|
670
|
+
effort: 'moderate',
|
|
671
|
+
impact: 'medium',
|
|
672
|
+
steps: [
|
|
673
|
+
'Add null checks for all external data',
|
|
674
|
+
'Implement proper error boundaries',
|
|
675
|
+
'Use default values for optional props',
|
|
676
|
+
'Handle loading and error states consistently',
|
|
677
|
+
],
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
if (metrics.stateManagement === 'local') {
|
|
681
|
+
improvements.push({
|
|
682
|
+
type: 'refactor',
|
|
683
|
+
description: 'Implement appropriate state management strategy',
|
|
684
|
+
effort: 'moderate',
|
|
685
|
+
impact: 'medium',
|
|
686
|
+
steps: [
|
|
687
|
+
'Use TanStack Query for server state',
|
|
688
|
+
'Use Zustand for global client state',
|
|
689
|
+
'Keep component-local state for UI-only state',
|
|
690
|
+
'Avoid prop drilling with context or state library',
|
|
691
|
+
],
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
improvements.push({
|
|
695
|
+
type: 'refactor',
|
|
696
|
+
description: 'Consider Domain-Driven Design patterns for complex business logic',
|
|
697
|
+
effort: 'major',
|
|
698
|
+
impact: 'medium',
|
|
699
|
+
steps: [
|
|
700
|
+
'Identify bounded contexts in the application',
|
|
701
|
+
'Create domain models with business logic',
|
|
702
|
+
'Separate domain logic from application logic',
|
|
703
|
+
'Implement repository patterns for data access',
|
|
704
|
+
],
|
|
705
|
+
});
|
|
706
|
+
return improvements;
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Calculate business logic score
|
|
710
|
+
*/
|
|
711
|
+
calculateBusinessLogicScore(metrics) {
|
|
712
|
+
let score = 0;
|
|
713
|
+
// Domain separation (30 points)
|
|
714
|
+
score += (metrics.domainSeparation / 100) * 30;
|
|
715
|
+
// Business rule consistency (25 points)
|
|
716
|
+
score += (metrics.ruleConsistency / 100) * 25;
|
|
717
|
+
// Edge case handling (20 points)
|
|
718
|
+
score += (metrics.edgeCaseHandling / 100) * 20;
|
|
719
|
+
// State management (15 points)
|
|
720
|
+
score += (metrics.stateManagementScore / 100) * 15;
|
|
721
|
+
// Feature isolation (10 points)
|
|
722
|
+
score += (metrics.featureIsolation / 100) * 10;
|
|
723
|
+
return Math.min(100, score);
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Count passed checks for metadata
|
|
727
|
+
*/
|
|
728
|
+
countPassedChecks(metrics) {
|
|
729
|
+
let count = 0;
|
|
730
|
+
if (metrics.domainSeparation >= 70)
|
|
731
|
+
count++;
|
|
732
|
+
if (metrics.ruleConsistency >= 70)
|
|
733
|
+
count++;
|
|
734
|
+
if (metrics.edgeCaseHandling >= 60)
|
|
735
|
+
count++;
|
|
736
|
+
if (metrics.stateManagementScore >= 70)
|
|
737
|
+
count++;
|
|
738
|
+
if (metrics.featureIsolation >= 60)
|
|
739
|
+
count++;
|
|
740
|
+
return count;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Default analyzer instance
|
|
745
|
+
*/
|
|
746
|
+
export const businessLogicAnalyzer = new BusinessLogicAnalyzer();
|
|
747
|
+
//# sourceMappingURL=business-logic.analyzer.js.map
|