@oalacea/daemon 0.6.3 → 0.7.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 +268 -58
- package/bin/Dockerfile +158 -16
- 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/init.command.d.ts +12 -14
- package/dist/cli/commands/init.command.d.ts.map +1 -1
- package/dist/cli/commands/init.command.js +278 -144
- package/dist/cli/commands/init.command.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 +11 -3
- 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,713 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backend Logic Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Analyzes backend code quality including API design, error handling consistency,
|
|
5
|
+
* input validation, rate limiting, and caching strategy.
|
|
6
|
+
*
|
|
7
|
+
* @module services/scoring/dimensions/backend-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
|
+
* Backend Logic Analyzer
|
|
16
|
+
*
|
|
17
|
+
* Evaluates backend code quality across multiple dimensions:
|
|
18
|
+
* - API design (REST conventions, status codes)
|
|
19
|
+
* - Error handling consistency
|
|
20
|
+
* - Input validation
|
|
21
|
+
* - Rate limiting presence
|
|
22
|
+
* - Caching strategy
|
|
23
|
+
*/
|
|
24
|
+
export class BackendLogicAnalyzer {
|
|
25
|
+
/** Analyzer configuration */
|
|
26
|
+
config = {
|
|
27
|
+
dimension: 'backend-logic',
|
|
28
|
+
defaultWeight: 0.10,
|
|
29
|
+
estimatedDuration: 25000,
|
|
30
|
+
supportedFrameworks: ['NestJS', 'Express', 'Fastify', 'Hono', 'Koa', 'Next.js'],
|
|
31
|
+
};
|
|
32
|
+
logger;
|
|
33
|
+
executor;
|
|
34
|
+
checkApiRoutesFlag;
|
|
35
|
+
checkErrorHandlingFlag;
|
|
36
|
+
checkInputValidationFlag;
|
|
37
|
+
checkRateLimitingFlag;
|
|
38
|
+
checkCachingFlag;
|
|
39
|
+
constructor(options = {}) {
|
|
40
|
+
this.logger = createLogger('BackendLogicAnalyzer');
|
|
41
|
+
this.executor = new CommandExecutor();
|
|
42
|
+
this.checkApiRoutesFlag = options.checkApiRoutes ?? true;
|
|
43
|
+
this.checkErrorHandlingFlag = options.checkErrorHandling ?? true;
|
|
44
|
+
this.checkInputValidationFlag = options.checkInputValidation ?? true;
|
|
45
|
+
this.checkRateLimitingFlag = options.checkRateLimiting ?? true;
|
|
46
|
+
this.checkCachingFlag = options.checkCaching ?? true;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get the dimension this analyzer handles
|
|
50
|
+
*/
|
|
51
|
+
getDimension() {
|
|
52
|
+
return 'backend-logic';
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get the default weight for this dimension
|
|
56
|
+
*/
|
|
57
|
+
getWeight() {
|
|
58
|
+
return 0.15; // 15% weight in overall score
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Analyze backend logic for a project
|
|
62
|
+
*
|
|
63
|
+
* @param projectPath - Path to the project root
|
|
64
|
+
* @param _framework - Detected framework (optional, for framework-specific analysis)
|
|
65
|
+
* @param _options - Scoring options (optional)
|
|
66
|
+
* @returns Dimension score with backend metrics
|
|
67
|
+
*/
|
|
68
|
+
async analyze(projectPath, _framework, _options) {
|
|
69
|
+
const startTime = performance.now();
|
|
70
|
+
this.logger.info(`Analyzing backend logic for: ${projectPath}`);
|
|
71
|
+
const issues = [];
|
|
72
|
+
const improvements = [];
|
|
73
|
+
try {
|
|
74
|
+
// Detect backend framework
|
|
75
|
+
const framework = await this.detectBackendFramework(projectPath);
|
|
76
|
+
// Skip if no backend detected
|
|
77
|
+
if (framework === 'unknown') {
|
|
78
|
+
return this.createNoBackendResult(issues, improvements);
|
|
79
|
+
}
|
|
80
|
+
// Analyze API routes
|
|
81
|
+
const apiRoutes = this.checkApiRoutesFlag ? await this.analyzeApiRoutes(projectPath, framework) : [];
|
|
82
|
+
// Check error handling
|
|
83
|
+
const errorHandling = this.checkErrorHandlingFlag ? await this.checkErrorHandling(projectPath, framework) : { hasErrorHandling: false, consistent: false };
|
|
84
|
+
// Check input validation
|
|
85
|
+
const inputValidation = this.checkInputValidationFlag ? await this.checkInputValidation(projectPath, framework) : { hasValidation: false, coverage: 0 };
|
|
86
|
+
// Check rate limiting
|
|
87
|
+
const rateLimiting = this.checkRateLimitingFlag ? await this.checkRateLimiting(projectPath, framework) : { hasRateLimiting: false };
|
|
88
|
+
// Check caching
|
|
89
|
+
const caching = this.checkCachingFlag ? await this.checkCaching(projectPath, framework) : { hasCaching: false, strategy: null };
|
|
90
|
+
// Check API versioning
|
|
91
|
+
const apiVersioning = await this.checkApiVersioning(projectPath, framework);
|
|
92
|
+
const metrics = {
|
|
93
|
+
framework,
|
|
94
|
+
restfulAdherence: this.calculateRestfulAdherence(apiRoutes),
|
|
95
|
+
consistentStatusCodes: this.checkConsistentStatusCodes(apiRoutes),
|
|
96
|
+
hasErrorHandling: errorHandling.hasErrorHandling,
|
|
97
|
+
routesWithErrorHandling: apiRoutes.filter((r) => r.hasErrorHandling).length,
|
|
98
|
+
totalRoutes: apiRoutes.length,
|
|
99
|
+
hasInputValidation: inputValidation.hasValidation,
|
|
100
|
+
routesWithValidation: apiRoutes.filter((r) => r.hasValidation).length,
|
|
101
|
+
hasRateLimiting: rateLimiting.hasRateLimiting,
|
|
102
|
+
hasCaching: caching.hasCaching,
|
|
103
|
+
hasApiVersioning: apiVersioning,
|
|
104
|
+
};
|
|
105
|
+
// Build issues list
|
|
106
|
+
issues.push(...this.identifyBackendIssues(metrics));
|
|
107
|
+
// Build improvements list
|
|
108
|
+
improvements.push(...this.generateBackendImprovements(metrics));
|
|
109
|
+
// Calculate final score
|
|
110
|
+
const score = this.calculateBackendScore(metrics);
|
|
111
|
+
const duration = Math.round(performance.now() - startTime);
|
|
112
|
+
return {
|
|
113
|
+
dimension: this.getDimension(),
|
|
114
|
+
score,
|
|
115
|
+
weight: this.getWeight(),
|
|
116
|
+
weightedScore: score * this.getWeight(),
|
|
117
|
+
issues,
|
|
118
|
+
improvements,
|
|
119
|
+
metadata: {
|
|
120
|
+
itemsChecked: metrics.totalRoutes + 5,
|
|
121
|
+
itemsPassed: this.countPassedChecks(metrics),
|
|
122
|
+
metrics: {
|
|
123
|
+
framework: metrics.framework,
|
|
124
|
+
restfulAdherence: metrics.restfulAdherence,
|
|
125
|
+
consistentStatusCodes: metrics.consistentStatusCodes ? 1 : 0,
|
|
126
|
+
routesWithErrorHandling: metrics.routesWithErrorHandling,
|
|
127
|
+
totalRoutes: metrics.totalRoutes,
|
|
128
|
+
routesWithValidation: metrics.routesWithValidation,
|
|
129
|
+
hasRateLimiting: metrics.hasRateLimiting ? 1 : 0,
|
|
130
|
+
hasCaching: metrics.hasCaching ? 1 : 0,
|
|
131
|
+
hasApiVersioning: metrics.hasApiVersioning ? 1 : 0,
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
this.logger.error('Error analyzing backend logic', error);
|
|
138
|
+
issues.push({
|
|
139
|
+
severity: 'medium',
|
|
140
|
+
category: 'architecture',
|
|
141
|
+
description: `Failed to analyze backend logic: ${error instanceof Error ? error.message : String(error)}`,
|
|
142
|
+
fixable: false,
|
|
143
|
+
});
|
|
144
|
+
const duration = Math.round(performance.now() - startTime);
|
|
145
|
+
return {
|
|
146
|
+
dimension: this.getDimension(),
|
|
147
|
+
score: 50,
|
|
148
|
+
weight: this.getWeight(),
|
|
149
|
+
weightedScore: 50 * this.getWeight(),
|
|
150
|
+
issues,
|
|
151
|
+
improvements,
|
|
152
|
+
metadata: { error: String(error) },
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Detect backend framework
|
|
158
|
+
*/
|
|
159
|
+
async detectBackendFramework(projectPath) {
|
|
160
|
+
try {
|
|
161
|
+
const pkgPath = join(projectPath, 'package.json');
|
|
162
|
+
const pkgContent = await readFile(pkgPath, 'utf-8');
|
|
163
|
+
const pkg = JSON.parse(pkgContent);
|
|
164
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
165
|
+
if ('@nestjs/core' in deps)
|
|
166
|
+
return 'nest';
|
|
167
|
+
if ('express' in deps)
|
|
168
|
+
return 'express';
|
|
169
|
+
if ('fastify' in deps)
|
|
170
|
+
return 'fastify';
|
|
171
|
+
if ('koa' in deps)
|
|
172
|
+
return 'koa';
|
|
173
|
+
if ('hono' in deps)
|
|
174
|
+
return 'hono';
|
|
175
|
+
// Check for Next.js API routes
|
|
176
|
+
if (existsSync(join(projectPath, 'pages', 'api')) || existsSync(join(projectPath, 'app', 'api'))) {
|
|
177
|
+
return 'next-api';
|
|
178
|
+
}
|
|
179
|
+
return 'unknown';
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return 'unknown';
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Analyze API routes
|
|
187
|
+
*/
|
|
188
|
+
async analyzeApiRoutes(projectPath, framework) {
|
|
189
|
+
const routes = [];
|
|
190
|
+
const apiDirs = [
|
|
191
|
+
join(projectPath, 'pages', 'api'),
|
|
192
|
+
join(projectPath, 'app', 'api'),
|
|
193
|
+
join(projectPath, 'src', 'api'),
|
|
194
|
+
join(projectPath, 'api'),
|
|
195
|
+
join(projectPath, 'src', 'routes'),
|
|
196
|
+
join(projectPath, 'routes'),
|
|
197
|
+
];
|
|
198
|
+
for (const apiDir of apiDirs) {
|
|
199
|
+
if (!existsSync(apiDir))
|
|
200
|
+
continue;
|
|
201
|
+
await this.scanDirectoryForApiRoutes(apiDir, routes, framework);
|
|
202
|
+
}
|
|
203
|
+
return routes;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Scan directory for API routes
|
|
207
|
+
*/
|
|
208
|
+
async scanDirectoryForApiRoutes(dir, routes, framework) {
|
|
209
|
+
try {
|
|
210
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
211
|
+
for (const entry of entries) {
|
|
212
|
+
const fullPath = join(dir, entry.name);
|
|
213
|
+
if (entry.isDirectory()) {
|
|
214
|
+
await this.scanDirectoryForApiRoutes(fullPath, routes, framework);
|
|
215
|
+
}
|
|
216
|
+
else if (entry.isFile()) {
|
|
217
|
+
const ext = extname(entry.name);
|
|
218
|
+
if (['.ts', '.tsx', '.js', '.jsx'].includes(ext)) {
|
|
219
|
+
const routeInfo = await this.analyzeApiFile(fullPath, framework);
|
|
220
|
+
if (routeInfo) {
|
|
221
|
+
routes.push(routeInfo);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// Directory not accessible
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Analyze a single API file
|
|
233
|
+
*/
|
|
234
|
+
async analyzeApiFile(filepath, framework) {
|
|
235
|
+
try {
|
|
236
|
+
const content = await readFile(filepath, 'utf-8');
|
|
237
|
+
const relativePath = relative(process.cwd(), filepath);
|
|
238
|
+
// Detect HTTP methods
|
|
239
|
+
const methods = [];
|
|
240
|
+
if (/export\s+(?:const|function|async\s+function)\s+(GET|get|HEAD|head|POST|post|PUT|put|DELETE|delete|PATCH|patch)/.test(content)) {
|
|
241
|
+
const match = content.match(/(?:export\s+(?:const|function|async\s+function)\s+)(GET|get|HEAD|head|POST|post|PUT|put|DELETE|delete|PATCH|patch)/);
|
|
242
|
+
if (match)
|
|
243
|
+
methods.push(match[1].toUpperCase());
|
|
244
|
+
}
|
|
245
|
+
if (framework === 'next-api') {
|
|
246
|
+
if (/export\s+(?:const|async\s+function)\s+default/.test(content)) {
|
|
247
|
+
methods.push('GET'); // Default export for Next.js API routes
|
|
248
|
+
if (content.includes('export const POST'))
|
|
249
|
+
methods.push('POST');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// Check for error handling
|
|
253
|
+
const hasErrorHandling = /try\s*{/.test(content) ||
|
|
254
|
+
/catch\s*\(/.test(content) ||
|
|
255
|
+
/\.catch\(/.test(content) ||
|
|
256
|
+
/error/i.test(content);
|
|
257
|
+
// Check for validation
|
|
258
|
+
const hasValidation = /zod/i.test(content) ||
|
|
259
|
+
/joi/i.test(content) ||
|
|
260
|
+
/yup/i.test(content) ||
|
|
261
|
+
/validation/i.test(content) ||
|
|
262
|
+
/schema/i.test(content);
|
|
263
|
+
// Extract status code
|
|
264
|
+
const statusCodeMatch = content.match(/status\((\d+)\)/);
|
|
265
|
+
const statusCode = statusCodeMatch ? parseInt(statusCodeMatch[1], 10) : null;
|
|
266
|
+
return {
|
|
267
|
+
method: methods[0] || 'GET',
|
|
268
|
+
path: relativePath,
|
|
269
|
+
file: relativePath,
|
|
270
|
+
hasErrorHandling,
|
|
271
|
+
hasValidation,
|
|
272
|
+
hasRateLimit: content.includes('rate') || content.includes('throttle'),
|
|
273
|
+
statusCode,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Check error handling patterns
|
|
282
|
+
*/
|
|
283
|
+
async checkErrorHandling(projectPath, framework) {
|
|
284
|
+
// Analyze consistency of error handling across routes
|
|
285
|
+
const apiRoutes = await this.analyzeApiRoutes(projectPath, framework);
|
|
286
|
+
if (apiRoutes.length === 0) {
|
|
287
|
+
return { hasErrorHandling: false, consistent: false };
|
|
288
|
+
}
|
|
289
|
+
const routesWithErrorHandling = apiRoutes.filter((r) => r.hasErrorHandling).length;
|
|
290
|
+
const hasErrorHandling = routesWithErrorHandling > 0;
|
|
291
|
+
const consistent = routesWithErrorHandling === apiRoutes.length;
|
|
292
|
+
return { hasErrorHandling, consistent };
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Check input validation
|
|
296
|
+
*/
|
|
297
|
+
async checkInputValidation(projectPath, framework) {
|
|
298
|
+
const apiRoutes = await this.analyzeApiRoutes(projectPath, framework);
|
|
299
|
+
if (apiRoutes.length === 0) {
|
|
300
|
+
return { hasValidation: false, coverage: 0 };
|
|
301
|
+
}
|
|
302
|
+
const routesWithValidation = apiRoutes.filter((r) => r.hasValidation).length;
|
|
303
|
+
const hasValidation = routesWithValidation > 0;
|
|
304
|
+
const coverage = routesWithValidation / apiRoutes.length;
|
|
305
|
+
return { hasValidation, coverage };
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Check rate limiting
|
|
309
|
+
*/
|
|
310
|
+
async checkRateLimiting(projectPath, framework) {
|
|
311
|
+
// Check for rate limiting middleware or configuration
|
|
312
|
+
const rateLimitPatterns = [
|
|
313
|
+
/rate-limit/i,
|
|
314
|
+
/express-rate-limit/i,
|
|
315
|
+
/rateLimit/i,
|
|
316
|
+
/throttle/i,
|
|
317
|
+
/x-ratelimit/i,
|
|
318
|
+
];
|
|
319
|
+
// Check package.json for rate limiting dependencies
|
|
320
|
+
try {
|
|
321
|
+
const pkgPath = join(projectPath, 'package.json');
|
|
322
|
+
const pkgContent = await readFile(pkgPath, 'utf-8');
|
|
323
|
+
const pkg = JSON.parse(pkgContent);
|
|
324
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
325
|
+
for (const dep of Object.keys(deps)) {
|
|
326
|
+
if (rateLimitPatterns.some((p) => p.test(dep))) {
|
|
327
|
+
return { hasRateLimiting: true };
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
catch {
|
|
332
|
+
// Ignore
|
|
333
|
+
}
|
|
334
|
+
// Check API routes for rate limiting usage
|
|
335
|
+
const apiRoutes = await this.analyzeApiRoutes(projectPath, framework);
|
|
336
|
+
const hasRateLimiting = apiRoutes.some((r) => r.hasRateLimit);
|
|
337
|
+
return { hasRateLimiting };
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Check caching strategy
|
|
341
|
+
*/
|
|
342
|
+
async checkCaching(projectPath, framework) {
|
|
343
|
+
const cachePatterns = [
|
|
344
|
+
/cache/i,
|
|
345
|
+
/redis/i,
|
|
346
|
+
/memcached/i,
|
|
347
|
+
/swr/i,
|
|
348
|
+
/stale-while-revalidate/i,
|
|
349
|
+
];
|
|
350
|
+
// Check Next.js caching (ISR, revalidate)
|
|
351
|
+
if (framework === 'next-api') {
|
|
352
|
+
try {
|
|
353
|
+
const appDir = join(projectPath, 'app');
|
|
354
|
+
const pagesDir = join(projectPath, 'pages');
|
|
355
|
+
if (existsSync(appDir)) {
|
|
356
|
+
// Check for revalidate in route files
|
|
357
|
+
const hasRevalidate = await this.directoryContainsPattern(appDir, /revalidate/i);
|
|
358
|
+
if (hasRevalidate) {
|
|
359
|
+
return { hasCaching: true, strategy: 'ISR' };
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
// Ignore
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// Check package.json for caching dependencies
|
|
368
|
+
try {
|
|
369
|
+
const pkgPath = join(projectPath, 'package.json');
|
|
370
|
+
const pkgContent = await readFile(pkgPath, 'utf-8');
|
|
371
|
+
const pkg = JSON.parse(pkgContent);
|
|
372
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
373
|
+
for (const dep of Object.keys(deps)) {
|
|
374
|
+
if (cachePatterns.some((p) => p.test(dep))) {
|
|
375
|
+
return { hasCaching: true, strategy: dep };
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
catch {
|
|
380
|
+
// Ignore
|
|
381
|
+
}
|
|
382
|
+
return { hasCaching: false, strategy: null };
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Check if directory contains pattern
|
|
386
|
+
*/
|
|
387
|
+
async directoryContainsPattern(dir, pattern) {
|
|
388
|
+
try {
|
|
389
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
390
|
+
for (const entry of entries) {
|
|
391
|
+
const fullPath = join(dir, entry.name);
|
|
392
|
+
if (entry.isDirectory()) {
|
|
393
|
+
if (!['node_modules', '.git', 'dist', 'build'].includes(entry.name)) {
|
|
394
|
+
if (await this.directoryContainsPattern(fullPath, pattern)) {
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
else if (entry.isFile()) {
|
|
400
|
+
const ext = extname(entry.name);
|
|
401
|
+
if (['.ts', '.tsx', '.js', '.jsx'].includes(ext)) {
|
|
402
|
+
try {
|
|
403
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
404
|
+
if (pattern.test(content)) {
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
catch {
|
|
409
|
+
// Skip unreadable files
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
catch {
|
|
416
|
+
// Directory not accessible
|
|
417
|
+
}
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Check API versioning
|
|
422
|
+
*/
|
|
423
|
+
async checkApiVersioning(projectPath, framework) {
|
|
424
|
+
const versionPatterns = [
|
|
425
|
+
/\/v\d+\//,
|
|
426
|
+
/\/api\/v\d+/,
|
|
427
|
+
/version.*:?/i,
|
|
428
|
+
];
|
|
429
|
+
const apiDirs = [
|
|
430
|
+
join(projectPath, 'app', 'api'),
|
|
431
|
+
join(projectPath, 'pages', 'api'),
|
|
432
|
+
join(projectPath, 'src', 'api'),
|
|
433
|
+
join(projectPath, 'api'),
|
|
434
|
+
];
|
|
435
|
+
for (const apiDir of apiDirs) {
|
|
436
|
+
if (!existsSync(apiDir))
|
|
437
|
+
continue;
|
|
438
|
+
const hasVersioning = await this.directoryContainsPattern(apiDir, /\/v\d+/);
|
|
439
|
+
if (hasVersioning) {
|
|
440
|
+
return true;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Calculate RESTful adherence score
|
|
447
|
+
*/
|
|
448
|
+
calculateRestfulAdherence(routes) {
|
|
449
|
+
if (routes.length === 0)
|
|
450
|
+
return 0;
|
|
451
|
+
let score = 0;
|
|
452
|
+
let maxScore = 0;
|
|
453
|
+
for (const route of routes) {
|
|
454
|
+
maxScore += 10;
|
|
455
|
+
// Proper HTTP method usage (5 points)
|
|
456
|
+
if (['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(route.method)) {
|
|
457
|
+
score += 5;
|
|
458
|
+
}
|
|
459
|
+
// Proper status codes (3 points)
|
|
460
|
+
if (route.statusCode) {
|
|
461
|
+
if (route.method === 'GET' && route.statusCode === 200)
|
|
462
|
+
score += 3;
|
|
463
|
+
if (route.method === 'POST' && route.statusCode === 201)
|
|
464
|
+
score += 3;
|
|
465
|
+
if (route.method === 'DELETE' && (route.statusCode === 204 || route.statusCode === 200))
|
|
466
|
+
score += 3;
|
|
467
|
+
if (route.method === 'PUT' && route.statusCode === 200)
|
|
468
|
+
score += 3;
|
|
469
|
+
}
|
|
470
|
+
// Error handling (2 points)
|
|
471
|
+
if (route.hasErrorHandling) {
|
|
472
|
+
score += 2;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return maxScore > 0 ? (score / maxScore) * 100 : 0;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Check consistent status codes
|
|
479
|
+
*/
|
|
480
|
+
checkConsistentStatusCodes(routes) {
|
|
481
|
+
const statusCodes = new Set(routes.map((r) => r.statusCode).filter((c) => c !== null));
|
|
482
|
+
// Should have consistent status codes for same methods
|
|
483
|
+
return statusCodes.size <= routes.length;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Identify backend issues
|
|
487
|
+
*/
|
|
488
|
+
identifyBackendIssues(metrics) {
|
|
489
|
+
const issues = [];
|
|
490
|
+
if (metrics.totalRoutes === 0) {
|
|
491
|
+
issues.push({
|
|
492
|
+
severity: 'low',
|
|
493
|
+
category: 'architecture',
|
|
494
|
+
description: 'No API routes detected',
|
|
495
|
+
fixable: false,
|
|
496
|
+
});
|
|
497
|
+
return issues;
|
|
498
|
+
}
|
|
499
|
+
if (metrics.restfulAdherence < 60) {
|
|
500
|
+
issues.push({
|
|
501
|
+
severity: 'medium',
|
|
502
|
+
category: 'architecture',
|
|
503
|
+
description: `Low RESTful adherence (${metrics.restfulAdherence.toFixed(0)}%)`,
|
|
504
|
+
fixable: true,
|
|
505
|
+
suggestion: 'Follow REST conventions for HTTP methods and status codes',
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
if (!metrics.consistentStatusCodes) {
|
|
509
|
+
issues.push({
|
|
510
|
+
severity: 'medium',
|
|
511
|
+
category: 'architecture',
|
|
512
|
+
description: 'Inconsistent HTTP status codes across routes',
|
|
513
|
+
fixable: true,
|
|
514
|
+
suggestion: 'Use consistent status codes (200 for GET, 201 for POST, 204 for DELETE)',
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
if (!metrics.hasErrorHandling || metrics.routesWithErrorHandling < metrics.totalRoutes) {
|
|
518
|
+
issues.push({
|
|
519
|
+
severity: 'high',
|
|
520
|
+
category: 'error-handling',
|
|
521
|
+
description: `${metrics.totalRoutes - metrics.routesWithErrorHandling} route(s) lack error handling`,
|
|
522
|
+
fixable: true,
|
|
523
|
+
suggestion: 'Add try-catch blocks and error middleware to all routes',
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
if (!metrics.hasInputValidation || metrics.routesWithValidation < metrics.totalRoutes) {
|
|
527
|
+
issues.push({
|
|
528
|
+
severity: 'high',
|
|
529
|
+
category: 'error-handling',
|
|
530
|
+
description: `${metrics.totalRoutes - metrics.routesWithValidation} route(s) lack input validation`,
|
|
531
|
+
fixable: true,
|
|
532
|
+
suggestion: 'Add input validation using Zod, Joi, or similar',
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
if (!metrics.hasRateLimiting) {
|
|
536
|
+
issues.push({
|
|
537
|
+
severity: 'medium',
|
|
538
|
+
category: 'architecture',
|
|
539
|
+
description: 'No rate limiting detected',
|
|
540
|
+
fixable: true,
|
|
541
|
+
suggestion: 'Implement rate limiting to prevent abuse',
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
if (!metrics.hasCaching) {
|
|
545
|
+
issues.push({
|
|
546
|
+
severity: 'low',
|
|
547
|
+
category: 'architecture',
|
|
548
|
+
description: 'No caching strategy detected',
|
|
549
|
+
fixable: true,
|
|
550
|
+
suggestion: 'Consider caching for frequently accessed data',
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
return issues;
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Generate backend improvements
|
|
557
|
+
*/
|
|
558
|
+
generateBackendImprovements(metrics) {
|
|
559
|
+
const improvements = [];
|
|
560
|
+
if (metrics.restfulAdherence < 80) {
|
|
561
|
+
improvements.push({
|
|
562
|
+
type: 'refactor',
|
|
563
|
+
description: 'Improve RESTful API design',
|
|
564
|
+
effort: 'moderate',
|
|
565
|
+
impact: 'medium',
|
|
566
|
+
steps: [
|
|
567
|
+
'Use proper HTTP methods (GET, POST, PUT, DELETE)',
|
|
568
|
+
'Return appropriate status codes',
|
|
569
|
+
'Use plural nouns for resource names',
|
|
570
|
+
'Implement HATEOAS for linked resources',
|
|
571
|
+
],
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
if (!metrics.hasErrorHandling || metrics.routesWithErrorHandling < metrics.totalRoutes) {
|
|
575
|
+
improvements.push({
|
|
576
|
+
type: 'refactor',
|
|
577
|
+
description: 'Add comprehensive error handling to all API routes',
|
|
578
|
+
effort: 'moderate',
|
|
579
|
+
impact: 'high',
|
|
580
|
+
steps: [
|
|
581
|
+
'Add try-catch blocks to all route handlers',
|
|
582
|
+
'Implement global error middleware',
|
|
583
|
+
'Return consistent error response format',
|
|
584
|
+
'Log errors for debugging',
|
|
585
|
+
],
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
if (!metrics.hasInputValidation || metrics.routesWithValidation < metrics.totalRoutes) {
|
|
589
|
+
improvements.push({
|
|
590
|
+
type: 'type-safe',
|
|
591
|
+
description: 'Add input validation to all API routes',
|
|
592
|
+
effort: 'moderate',
|
|
593
|
+
impact: 'high',
|
|
594
|
+
steps: [
|
|
595
|
+
'Install validation library (Zod, Joi)',
|
|
596
|
+
'Create validation schemas for request bodies',
|
|
597
|
+
'Validate query parameters',
|
|
598
|
+
'Sanitize user input',
|
|
599
|
+
],
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
if (!metrics.hasRateLimiting) {
|
|
603
|
+
improvements.push({
|
|
604
|
+
type: 'refactor',
|
|
605
|
+
description: 'Implement rate limiting',
|
|
606
|
+
effort: 'quick',
|
|
607
|
+
impact: 'medium',
|
|
608
|
+
steps: [
|
|
609
|
+
'Install rate limiting middleware',
|
|
610
|
+
'Configure rate limits per route',
|
|
611
|
+
'Add rate limit headers to responses',
|
|
612
|
+
],
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
if (!metrics.hasCaching) {
|
|
616
|
+
improvements.push({
|
|
617
|
+
type: 'optimize',
|
|
618
|
+
description: 'Implement caching strategy',
|
|
619
|
+
effort: 'moderate',
|
|
620
|
+
impact: 'medium',
|
|
621
|
+
steps: [
|
|
622
|
+
'Identify cacheable endpoints',
|
|
623
|
+
'Set appropriate cache headers',
|
|
624
|
+
'Consider Redis for distributed caching',
|
|
625
|
+
'Implement cache invalidation strategy',
|
|
626
|
+
],
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
if (!metrics.hasApiVersioning) {
|
|
630
|
+
improvements.push({
|
|
631
|
+
type: 'refactor',
|
|
632
|
+
description: 'Add API versioning',
|
|
633
|
+
effort: 'moderate',
|
|
634
|
+
impact: 'low',
|
|
635
|
+
steps: [
|
|
636
|
+
'Add version prefix to routes (e.g., /api/v1)',
|
|
637
|
+
'Document version changes',
|
|
638
|
+
'Maintain backward compatibility',
|
|
639
|
+
],
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
return improvements;
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Calculate backend score
|
|
646
|
+
*/
|
|
647
|
+
calculateBackendScore(metrics) {
|
|
648
|
+
if (metrics.totalRoutes === 0) {
|
|
649
|
+
return 100; // No backend = N/A = perfect for this dimension
|
|
650
|
+
}
|
|
651
|
+
let score = 0;
|
|
652
|
+
// RESTful adherence (25 points)
|
|
653
|
+
score += (metrics.restfulAdherence / 100) * 25;
|
|
654
|
+
// Error handling (25 points)
|
|
655
|
+
const errorHandlingRatio = metrics.routesWithErrorHandling / metrics.totalRoutes;
|
|
656
|
+
score += errorHandlingRatio * 25;
|
|
657
|
+
// Input validation (20 points)
|
|
658
|
+
const validationRatio = metrics.routesWithValidation / metrics.totalRoutes;
|
|
659
|
+
score += validationRatio * 20;
|
|
660
|
+
// Rate limiting (15 points)
|
|
661
|
+
if (metrics.hasRateLimiting)
|
|
662
|
+
score += 15;
|
|
663
|
+
// Caching (10 points)
|
|
664
|
+
if (metrics.hasCaching)
|
|
665
|
+
score += 10;
|
|
666
|
+
// API versioning (5 points)
|
|
667
|
+
if (metrics.hasApiVersioning)
|
|
668
|
+
score += 5;
|
|
669
|
+
return Math.min(100, score);
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Count passed checks for metadata
|
|
673
|
+
*/
|
|
674
|
+
countPassedChecks(metrics) {
|
|
675
|
+
let count = 0;
|
|
676
|
+
if (metrics.restfulAdherence >= 80)
|
|
677
|
+
count++;
|
|
678
|
+
if (metrics.consistentStatusCodes)
|
|
679
|
+
count++;
|
|
680
|
+
if (metrics.routesWithErrorHandling === metrics.totalRoutes && metrics.totalRoutes > 0)
|
|
681
|
+
count++;
|
|
682
|
+
if (metrics.routesWithValidation === metrics.totalRoutes && metrics.totalRoutes > 0)
|
|
683
|
+
count++;
|
|
684
|
+
if (metrics.hasRateLimiting)
|
|
685
|
+
count++;
|
|
686
|
+
if (metrics.hasCaching)
|
|
687
|
+
count++;
|
|
688
|
+
return count;
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Create result when no backend detected
|
|
692
|
+
*/
|
|
693
|
+
createNoBackendResult(issues, improvements) {
|
|
694
|
+
return {
|
|
695
|
+
dimension: this.getDimension(),
|
|
696
|
+
score: 100, // No backend to score
|
|
697
|
+
weight: this.getWeight(),
|
|
698
|
+
weightedScore: 100 * this.getWeight(),
|
|
699
|
+
issues: [],
|
|
700
|
+
improvements: [],
|
|
701
|
+
metadata: {
|
|
702
|
+
itemsChecked: 0,
|
|
703
|
+
itemsPassed: 0,
|
|
704
|
+
metrics: { framework: 'unknown' },
|
|
705
|
+
},
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Default analyzer instance
|
|
711
|
+
*/
|
|
712
|
+
export const backendLogicAnalyzer = new BackendLogicAnalyzer();
|
|
713
|
+
//# sourceMappingURL=backend-logic.analyzer.js.map
|