@ash-technologia/fathom 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/LICENSE +21 -0
- package/README.md +725 -0
- package/dist/analyzers/architecture/graph.d.ts +29 -0
- package/dist/analyzers/architecture/graph.d.ts.map +1 -0
- package/dist/analyzers/architecture/graph.js +347 -0
- package/dist/analyzers/architecture/graph.js.map +1 -0
- package/dist/analyzers/architecture/imports.d.ts +24 -0
- package/dist/analyzers/architecture/imports.d.ts.map +1 -0
- package/dist/analyzers/architecture/imports.js +155 -0
- package/dist/analyzers/architecture/imports.js.map +1 -0
- package/dist/analyzers/architecture/index.d.ts +13 -0
- package/dist/analyzers/architecture/index.d.ts.map +1 -0
- package/dist/analyzers/architecture/index.js +217 -0
- package/dist/analyzers/architecture/index.js.map +1 -0
- package/dist/analyzers/architecture/types.d.ts +65 -0
- package/dist/analyzers/architecture/types.d.ts.map +1 -0
- package/dist/analyzers/architecture/types.js +2 -0
- package/dist/analyzers/architecture/types.js.map +1 -0
- package/dist/analyzers/cicd/index.d.ts +11 -0
- package/dist/analyzers/cicd/index.d.ts.map +1 -0
- package/dist/analyzers/cicd/index.js +157 -0
- package/dist/analyzers/cicd/index.js.map +1 -0
- package/dist/analyzers/dependencies/index.d.ts +11 -0
- package/dist/analyzers/dependencies/index.d.ts.map +1 -0
- package/dist/analyzers/dependencies/index.js +403 -0
- package/dist/analyzers/dependencies/index.js.map +1 -0
- package/dist/analyzers/dependencies/lockfiles.d.ts +19 -0
- package/dist/analyzers/dependencies/lockfiles.d.ts.map +1 -0
- package/dist/analyzers/dependencies/lockfiles.js +172 -0
- package/dist/analyzers/dependencies/lockfiles.js.map +1 -0
- package/dist/analyzers/dependencies/online.d.ts +19 -0
- package/dist/analyzers/dependencies/online.d.ts.map +1 -0
- package/dist/analyzers/dependencies/online.js +116 -0
- package/dist/analyzers/dependencies/online.js.map +1 -0
- package/dist/analyzers/dependencies/types.d.ts +49 -0
- package/dist/analyzers/dependencies/types.d.ts.map +1 -0
- package/dist/analyzers/dependencies/types.js +2 -0
- package/dist/analyzers/dependencies/types.js.map +1 -0
- package/dist/analyzers/documentation/index.d.ts +11 -0
- package/dist/analyzers/documentation/index.d.ts.map +1 -0
- package/dist/analyzers/documentation/index.js +166 -0
- package/dist/analyzers/documentation/index.js.map +1 -0
- package/dist/analyzers/git/index.d.ts +11 -0
- package/dist/analyzers/git/index.d.ts.map +1 -0
- package/dist/analyzers/git/index.js +180 -0
- package/dist/analyzers/git/index.js.map +1 -0
- package/dist/analyzers/project/index.d.ts +11 -0
- package/dist/analyzers/project/index.d.ts.map +1 -0
- package/dist/analyzers/project/index.js +95 -0
- package/dist/analyzers/project/index.js.map +1 -0
- package/dist/analyzers/quality/index.d.ts +11 -0
- package/dist/analyzers/quality/index.d.ts.map +1 -0
- package/dist/analyzers/quality/index.js +198 -0
- package/dist/analyzers/quality/index.js.map +1 -0
- package/dist/analyzers/security/index.d.ts +11 -0
- package/dist/analyzers/security/index.d.ts.map +1 -0
- package/dist/analyzers/security/index.js +236 -0
- package/dist/analyzers/security/index.js.map +1 -0
- package/dist/analyzers/testing/index.d.ts +11 -0
- package/dist/analyzers/testing/index.d.ts.map +1 -0
- package/dist/analyzers/testing/index.js +134 -0
- package/dist/analyzers/testing/index.js.map +1 -0
- package/dist/baseline/baseline.d.ts +20 -0
- package/dist/baseline/baseline.d.ts.map +1 -0
- package/dist/baseline/baseline.js +134 -0
- package/dist/baseline/baseline.js.map +1 -0
- package/dist/baseline/compare.d.ts +14 -0
- package/dist/baseline/compare.d.ts.map +1 -0
- package/dist/baseline/compare.js +113 -0
- package/dist/baseline/compare.js.map +1 -0
- package/dist/baseline/types.d.ts +25 -0
- package/dist/baseline/types.d.ts.map +1 -0
- package/dist/baseline/types.js +2 -0
- package/dist/baseline/types.js.map +1 -0
- package/dist/cli/commands.d.ts +39 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +277 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +29 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/options.d.ts +55 -0
- package/dist/cli/options.d.ts.map +1 -0
- package/dist/cli/options.js +258 -0
- package/dist/cli/options.js.map +1 -0
- package/dist/core/analyzer.d.ts +33 -0
- package/dist/core/analyzer.d.ts.map +1 -0
- package/dist/core/analyzer.js +19 -0
- package/dist/core/analyzer.js.map +1 -0
- package/dist/core/context.d.ts +90 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +159 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/errors.d.ts +74 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +109 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/findings.d.ts +59 -0
- package/dist/core/findings.d.ts.map +1 -0
- package/dist/core/findings.js +34 -0
- package/dist/core/findings.js.map +1 -0
- package/dist/core/orchestrator.d.ts +44 -0
- package/dist/core/orchestrator.d.ts.map +1 -0
- package/dist/core/orchestrator.js +160 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/result.d.ts +169 -0
- package/dist/core/result.d.ts.map +1 -0
- package/dist/core/result.js +2 -0
- package/dist/core/result.js.map +1 -0
- package/dist/detectors/framework.d.ts +22 -0
- package/dist/detectors/framework.d.ts.map +1 -0
- package/dist/detectors/framework.js +147 -0
- package/dist/detectors/framework.js.map +1 -0
- package/dist/detectors/language.d.ts +13 -0
- package/dist/detectors/language.d.ts.map +1 -0
- package/dist/detectors/language.js +100 -0
- package/dist/detectors/language.js.map +1 -0
- package/dist/detectors/package-manager.d.ts +17 -0
- package/dist/detectors/package-manager.d.ts.map +1 -0
- package/dist/detectors/package-manager.js +67 -0
- package/dist/detectors/package-manager.js.map +1 -0
- package/dist/detectors/project-type.d.ts +20 -0
- package/dist/detectors/project-type.d.ts.map +1 -0
- package/dist/detectors/project-type.js +48 -0
- package/dist/detectors/project-type.js.map +1 -0
- package/dist/diff/analyzer.d.ts +16 -0
- package/dist/diff/analyzer.d.ts.map +1 -0
- package/dist/diff/analyzer.js +296 -0
- package/dist/diff/analyzer.js.map +1 -0
- package/dist/diff/git-diff.d.ts +44 -0
- package/dist/diff/git-diff.d.ts.map +1 -0
- package/dist/diff/git-diff.js +256 -0
- package/dist/diff/git-diff.js.map +1 -0
- package/dist/diff/types.d.ts +75 -0
- package/dist/diff/types.d.ts.map +1 -0
- package/dist/diff/types.js +2 -0
- package/dist/diff/types.js.map +1 -0
- package/dist/integrations/github/context.d.ts +22 -0
- package/dist/integrations/github/context.d.ts.map +1 -0
- package/dist/integrations/github/context.js +115 -0
- package/dist/integrations/github/context.js.map +1 -0
- package/dist/integrations/github/types.d.ts +30 -0
- package/dist/integrations/github/types.d.ts.map +1 -0
- package/dist/integrations/github/types.js +2 -0
- package/dist/integrations/github/types.js.map +1 -0
- package/dist/plugins/context.d.ts +13 -0
- package/dist/plugins/context.d.ts.map +1 -0
- package/dist/plugins/context.js +102 -0
- package/dist/plugins/context.js.map +1 -0
- package/dist/plugins/examples/react.d.ts +8 -0
- package/dist/plugins/examples/react.d.ts.map +1 -0
- package/dist/plugins/examples/react.js +129 -0
- package/dist/plugins/examples/react.js.map +1 -0
- package/dist/plugins/index.d.ts +5 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +4 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/registry.d.ts +45 -0
- package/dist/plugins/registry.d.ts.map +1 -0
- package/dist/plugins/registry.js +236 -0
- package/dist/plugins/registry.js.map +1 -0
- package/dist/plugins/types.d.ts +148 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +2 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/reporters/html.d.ts +11 -0
- package/dist/reporters/html.d.ts.map +1 -0
- package/dist/reporters/html.js +1352 -0
- package/dist/reporters/html.js.map +1 -0
- package/dist/reporters/json.d.ts +10 -0
- package/dist/reporters/json.d.ts.map +1 -0
- package/dist/reporters/json.js +11 -0
- package/dist/reporters/json.js.map +1 -0
- package/dist/reporters/markdown.d.ts +7 -0
- package/dist/reporters/markdown.d.ts.map +1 -0
- package/dist/reporters/markdown.js +74 -0
- package/dist/reporters/markdown.js.map +1 -0
- package/dist/reporters/sarif.d.ts +133 -0
- package/dist/reporters/sarif.d.ts.map +1 -0
- package/dist/reporters/sarif.js +268 -0
- package/dist/reporters/sarif.js.map +1 -0
- package/dist/reporters/terminal.d.ts +17 -0
- package/dist/reporters/terminal.d.ts.map +1 -0
- package/dist/reporters/terminal.js +423 -0
- package/dist/reporters/terminal.js.map +1 -0
- package/dist/rules/categories.d.ts +8 -0
- package/dist/rules/categories.d.ts.map +1 -0
- package/dist/rules/categories.js +23 -0
- package/dist/rules/categories.js.map +1 -0
- package/dist/rules/definitions.d.ts +25 -0
- package/dist/rules/definitions.d.ts.map +1 -0
- package/dist/rules/definitions.js +429 -0
- package/dist/rules/definitions.js.map +1 -0
- package/dist/rules/registry.d.ts +36 -0
- package/dist/rules/registry.d.ts.map +1 -0
- package/dist/rules/registry.js +64 -0
- package/dist/rules/registry.js.map +1 -0
- package/dist/rules/severity.d.ts +13 -0
- package/dist/rules/severity.d.ts.map +1 -0
- package/dist/rules/severity.js +15 -0
- package/dist/rules/severity.js.map +1 -0
- package/dist/scoring/confidence.d.ts +12 -0
- package/dist/scoring/confidence.d.ts.map +1 -0
- package/dist/scoring/confidence.js +22 -0
- package/dist/scoring/confidence.js.map +1 -0
- package/dist/scoring/score.d.ts +27 -0
- package/dist/scoring/score.d.ts.map +1 -0
- package/dist/scoring/score.js +81 -0
- package/dist/scoring/score.js.map +1 -0
- package/dist/scoring/weights.d.ts +11 -0
- package/dist/scoring/weights.d.ts.map +1 -0
- package/dist/scoring/weights.js +26 -0
- package/dist/scoring/weights.js.map +1 -0
- package/dist/utils/filesystem.d.ts +65 -0
- package/dist/utils/filesystem.d.ts.map +1 -0
- package/dist/utils/filesystem.js +299 -0
- package/dist/utils/filesystem.js.map +1 -0
- package/dist/utils/git.d.ts +47 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +125 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/logger.d.ts +14 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +25 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/paths.d.ts +30 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +48 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/timing.d.ts +12 -0
- package/dist/utils/timing.d.ts.map +1 -0
- package/dist/utils/timing.js +19 -0
- package/dist/utils/timing.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { createFindingId } from '../../core/findings.js';
|
|
3
|
+
import { createTimer } from '../../utils/timing.js';
|
|
4
|
+
import { readFileSafe } from '../../utils/filesystem.js';
|
|
5
|
+
import { extractPathAliases, extractRawSpecifiers, resolveImportSpecifier } from './imports.js';
|
|
6
|
+
import { buildArchitectureModel } from './graph.js';
|
|
7
|
+
export class ArchitectureAnalyzer {
|
|
8
|
+
id = 'architecture';
|
|
9
|
+
name = 'Architecture';
|
|
10
|
+
category = 'architecture';
|
|
11
|
+
description = 'Analyzes repository layout, dependency graph, circular dependencies, and architectural boundary violations.';
|
|
12
|
+
async analyze(context) {
|
|
13
|
+
const elapsed = createTimer();
|
|
14
|
+
const findings = [];
|
|
15
|
+
const warnings = [];
|
|
16
|
+
const allNormalized = context.files.map((f) => f.relativePath);
|
|
17
|
+
const knownFiles = new Set(allNormalized);
|
|
18
|
+
// 1. Detect path aliases from tsconfig.json / jsconfig.json
|
|
19
|
+
let aliasConfig = null;
|
|
20
|
+
const tsconfigFile = context.files.find((f) => f.relativePath === 'tsconfig.json' || f.relativePath === 'jsconfig.json');
|
|
21
|
+
if (tsconfigFile) {
|
|
22
|
+
const content = await readFileSafe(tsconfigFile.absolutePath);
|
|
23
|
+
aliasConfig = extractPathAliases(content);
|
|
24
|
+
}
|
|
25
|
+
// 2. Extract imports from source files
|
|
26
|
+
const fileDataList = [];
|
|
27
|
+
for (const file of context.files) {
|
|
28
|
+
if (!file.isSource)
|
|
29
|
+
continue;
|
|
30
|
+
// Skip files larger than 1MB from full AST text import scanning to preserve bounded memory
|
|
31
|
+
if (file.sizeBytes > 1024 * 1024)
|
|
32
|
+
continue;
|
|
33
|
+
const content = await readFileSafe(file.absolutePath);
|
|
34
|
+
if (!content)
|
|
35
|
+
continue;
|
|
36
|
+
const lines = content.split(/\r?\n/);
|
|
37
|
+
const rawSpecifiers = extractRawSpecifiers(content, file.extension);
|
|
38
|
+
const resolvedImports = [];
|
|
39
|
+
const externalImports = [];
|
|
40
|
+
for (const spec of rawSpecifiers) {
|
|
41
|
+
const resolved = resolveImportSpecifier(spec, file.relativePath, knownFiles, aliasConfig);
|
|
42
|
+
if (resolved.resolvedPath) {
|
|
43
|
+
resolvedImports.push(resolved.resolvedPath);
|
|
44
|
+
}
|
|
45
|
+
else if (resolved.isExternal && resolved.packageName) {
|
|
46
|
+
externalImports.push(resolved.packageName);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
fileDataList.push({
|
|
50
|
+
relativePath: file.relativePath,
|
|
51
|
+
lineCount: lines.length,
|
|
52
|
+
isTest: file.isTest,
|
|
53
|
+
imports: [...new Set(resolvedImports)],
|
|
54
|
+
externalImports: [...new Set(externalImports)],
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
// 3. Build architecture graph model & detect issues
|
|
58
|
+
const { summary, warnings: archWarnings } = buildArchitectureModel(fileDataList);
|
|
59
|
+
// 4. Structural layout components (ARCH-001 backwards compatibility)
|
|
60
|
+
const hasFrontend = context.frameworks.some((fw) => ['React', 'Vue', 'Angular', 'Svelte', 'Next.js', 'Astro'].includes(fw.name)) ||
|
|
61
|
+
allNormalized.some((f) => f.startsWith('frontend/') || f.startsWith('client/') || f.startsWith('web/')) ||
|
|
62
|
+
summary.layers.some((l) => l.name.toLowerCase() === 'frontend');
|
|
63
|
+
const hasBackend = context.frameworks.some((fw) => [
|
|
64
|
+
'Express',
|
|
65
|
+
'NestJS',
|
|
66
|
+
'Fastify',
|
|
67
|
+
'Hono',
|
|
68
|
+
'FastAPI',
|
|
69
|
+
'Django',
|
|
70
|
+
'Flask',
|
|
71
|
+
'Spring Boot',
|
|
72
|
+
].includes(fw.name)) ||
|
|
73
|
+
allNormalized.some((f) => f.startsWith('backend/') || f.startsWith('server/') || f.startsWith('api/')) ||
|
|
74
|
+
summary.layers.some((l) => l.name.toLowerCase() === 'backend');
|
|
75
|
+
const hasSharedPackages = allNormalized.some((f) => f.startsWith('packages/')) ||
|
|
76
|
+
allNormalized.some((f) => f.startsWith('libs/') || f.startsWith('shared/'));
|
|
77
|
+
const hasAppsDir = allNormalized.some((f) => f.startsWith('apps/'));
|
|
78
|
+
const isMonorepo = context.projectType.type === 'monorepo';
|
|
79
|
+
const hasPublicDir = allNormalized.some((f) => f.startsWith('public/') || f.startsWith('static/') || f.startsWith('assets/'));
|
|
80
|
+
const hasSrcDir = allNormalized.some((f) => f.startsWith('src/'));
|
|
81
|
+
const hasDocsDir = allNormalized.some((f) => f.startsWith('docs/') || f.startsWith('documentation/'));
|
|
82
|
+
// ARCH-001: Structure observations
|
|
83
|
+
const observations = [];
|
|
84
|
+
if (isMonorepo)
|
|
85
|
+
observations.push('Monorepo structure detected');
|
|
86
|
+
if (hasAppsDir)
|
|
87
|
+
observations.push('Multi-app layout (apps/ directory)');
|
|
88
|
+
if (hasFrontend && hasBackend)
|
|
89
|
+
observations.push('Full-stack project (frontend + backend)');
|
|
90
|
+
else if (hasFrontend)
|
|
91
|
+
observations.push('Frontend project');
|
|
92
|
+
else if (hasBackend)
|
|
93
|
+
observations.push('Backend/API project');
|
|
94
|
+
if (hasSharedPackages)
|
|
95
|
+
observations.push('Shared packages detected');
|
|
96
|
+
if (hasPublicDir)
|
|
97
|
+
observations.push('Public/static assets directory');
|
|
98
|
+
if (hasSrcDir)
|
|
99
|
+
observations.push('Source organized under src/');
|
|
100
|
+
if (hasDocsDir)
|
|
101
|
+
observations.push('Documentation directory present');
|
|
102
|
+
if (observations.length > 0) {
|
|
103
|
+
findings.push({
|
|
104
|
+
id: createFindingId('ARCH-001'),
|
|
105
|
+
ruleId: 'ARCH-001',
|
|
106
|
+
category: 'architecture',
|
|
107
|
+
severity: 'info',
|
|
108
|
+
title: `Architecture layout: ${observations.slice(0, 2).join(', ')}`,
|
|
109
|
+
description: `Project architectural structure detected: ${observations.join('; ')}.`,
|
|
110
|
+
recommendation: 'Maintain separation of concerns and clear boundary interfaces.',
|
|
111
|
+
confidence: 0.9,
|
|
112
|
+
autoFixable: false,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
// 5. Emit findings from graph analysis (ARCH-002 to ARCH-006)
|
|
116
|
+
for (const w of archWarnings) {
|
|
117
|
+
if (w.type === 'circular') {
|
|
118
|
+
findings.push({
|
|
119
|
+
id: createFindingId('ARCH-002', w.files.join('-')),
|
|
120
|
+
ruleId: 'ARCH-002',
|
|
121
|
+
category: 'architecture',
|
|
122
|
+
severity: 'high',
|
|
123
|
+
title: 'Circular dependency detected',
|
|
124
|
+
description: w.message,
|
|
125
|
+
recommendation: 'Refactor common code into an independent shared module or break cyclic imports.',
|
|
126
|
+
confidence: 0.95,
|
|
127
|
+
location: { file: w.files[0] ?? '' },
|
|
128
|
+
autoFixable: false,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
else if (w.type === 'boundary') {
|
|
132
|
+
findings.push({
|
|
133
|
+
id: createFindingId('ARCH-003', w.files.join('-')),
|
|
134
|
+
ruleId: 'ARCH-003',
|
|
135
|
+
category: 'architecture',
|
|
136
|
+
severity: 'high',
|
|
137
|
+
title: 'Architectural boundary violation',
|
|
138
|
+
description: w.message,
|
|
139
|
+
recommendation: 'Enforce architectural layer boundaries. Backend services should not depend on UI components, and client code must not import server-only primitives.',
|
|
140
|
+
confidence: 0.9,
|
|
141
|
+
location: { file: w.files[0] ?? '' },
|
|
142
|
+
autoFixable: false,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
else if (w.type === 'coupling') {
|
|
146
|
+
findings.push({
|
|
147
|
+
id: createFindingId('ARCH-004', w.files[0] ?? ''),
|
|
148
|
+
ruleId: 'ARCH-004',
|
|
149
|
+
category: 'architecture',
|
|
150
|
+
severity: 'low',
|
|
151
|
+
title: `Deep coupling in ${path.basename(w.files[0] ?? '')}`,
|
|
152
|
+
description: w.message,
|
|
153
|
+
recommendation: 'Consider introducing an intermediary service or facade to reduce direct fan-out dependencies.',
|
|
154
|
+
confidence: 0.8,
|
|
155
|
+
location: { file: w.files[0] ?? '' },
|
|
156
|
+
autoFixable: false,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
else if (w.type === 'large') {
|
|
160
|
+
findings.push({
|
|
161
|
+
id: createFindingId('ARCH-005', w.files[0] ?? ''),
|
|
162
|
+
ruleId: 'ARCH-005',
|
|
163
|
+
category: 'architecture',
|
|
164
|
+
severity: 'low',
|
|
165
|
+
title: `Large monolithic module: ${path.basename(w.files[0] ?? '')}`,
|
|
166
|
+
description: w.message,
|
|
167
|
+
recommendation: 'Split module into smaller, single-responsibility components with focused interfaces.',
|
|
168
|
+
confidence: 0.85,
|
|
169
|
+
location: { file: w.files[0] ?? '' },
|
|
170
|
+
autoFixable: false,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
else if (w.type === 'orphan') {
|
|
174
|
+
findings.push({
|
|
175
|
+
id: createFindingId('ARCH-006', w.files[0] ?? ''),
|
|
176
|
+
ruleId: 'ARCH-006',
|
|
177
|
+
category: 'architecture',
|
|
178
|
+
severity: 'info',
|
|
179
|
+
title: `Orphaned source file: ${path.basename(w.files[0] ?? '')}`,
|
|
180
|
+
description: w.message,
|
|
181
|
+
recommendation: 'Verify whether this file is dead code or intended as an unreferenced public API entry point.',
|
|
182
|
+
confidence: 0.75,
|
|
183
|
+
location: { file: w.files[0] ?? '' },
|
|
184
|
+
autoFixable: false,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const metrics = {
|
|
189
|
+
hasFrontend,
|
|
190
|
+
hasBackend,
|
|
191
|
+
hasSharedPackages,
|
|
192
|
+
hasAppsDir,
|
|
193
|
+
isMonorepo,
|
|
194
|
+
hasPublicDir,
|
|
195
|
+
hasSrcDir,
|
|
196
|
+
hasDocsDir,
|
|
197
|
+
observations,
|
|
198
|
+
nodeCount: summary.nodeCount,
|
|
199
|
+
edgeCount: summary.edgeCount,
|
|
200
|
+
circularDependencyCount: summary.circularDependencies.length,
|
|
201
|
+
layerCount: summary.layers.length,
|
|
202
|
+
layers: JSON.stringify(summary.layers),
|
|
203
|
+
warnings: JSON.stringify(summary.warnings),
|
|
204
|
+
};
|
|
205
|
+
return {
|
|
206
|
+
analyzerId: this.id,
|
|
207
|
+
analyzerName: this.name,
|
|
208
|
+
category: this.category,
|
|
209
|
+
status: 'success',
|
|
210
|
+
findings,
|
|
211
|
+
metrics,
|
|
212
|
+
durationMs: elapsed(),
|
|
213
|
+
warnings,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analyzers/architecture/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAK7B,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAChG,OAAO,EAAE,sBAAsB,EAAiB,MAAM,YAAY,CAAC;AAGnE,MAAM,OAAO,oBAAoB;IACtB,EAAE,GAAG,cAAc,CAAC;IACpB,IAAI,GAAG,cAAc,CAAC;IACtB,QAAQ,GAAG,cAAuB,CAAC;IACnC,WAAW,GAClB,6GAA6G,CAAC;IAEhH,KAAK,CAAC,OAAO,CAAC,OAA0B;QACtC,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAc,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAa,EAAE,CAAC;QAE9B,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;QAC/D,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC;QAE1C,4DAA4D;QAC5D,IAAI,WAAW,GAAG,IAAI,CAAC;QACvB,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CACrC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,eAAe,IAAI,CAAC,CAAC,YAAY,KAAK,eAAe,CAChF,CAAC;QACF,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;YAC9D,WAAW,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC5C,CAAC;QAED,uCAAuC;QACvC,MAAM,YAAY,GAAe,EAAE,CAAC;QACpC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YACjC,IAAI,CAAC,IAAI,CAAC,QAAQ;gBAAE,SAAS;YAC7B,2FAA2F;YAC3F,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,GAAG,IAAI;gBAAE,SAAS;YAE3C,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACtD,IAAI,CAAC,OAAO;gBAAE,SAAS;YAEvB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACrC,MAAM,aAAa,GAAG,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YAEpE,MAAM,eAAe,GAAa,EAAE,CAAC;YACrC,MAAM,eAAe,GAAa,EAAE,CAAC;YAErC,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;gBACjC,MAAM,QAAQ,GAAG,sBAAsB,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;gBAC1F,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC;oBAC1B,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;gBAC9C,CAAC;qBAAM,IAAI,QAAQ,CAAC,UAAU,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;oBACvD,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;gBAC7C,CAAC;YACH,CAAC;YAED,YAAY,CAAC,IAAI,CAAC;gBAChB,YAAY,EAAE,IAAI,CAAC,YAAY;gBAC/B,SAAS,EAAE,KAAK,CAAC,MAAM;gBACvB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC;gBACtC,eAAe,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC;aAC/C,CAAC,CAAC;QACL,CAAC;QAED,oDAAoD;QACpD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,sBAAsB,CAAC,YAAY,CAAC,CAAC;QAEjF,qEAAqE;QACrE,MAAM,WAAW,GACf,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAC7B,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,CAC5E;YACD,aAAa,CAAC,IAAI,CAChB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CACpF;YACD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,UAAU,CAAC,CAAC;QAElE,MAAM,UAAU,GACd,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAC7B;YACE,SAAS;YACT,QAAQ;YACR,SAAS;YACT,MAAM;YACN,SAAS;YACT,QAAQ;YACR,OAAO;YACP,aAAa;SACd,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,CACpB;YACD,aAAa,CAAC,IAAI,CAChB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CACnF;YACD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,CAAC;QAEjE,MAAM,iBAAiB,GACrB,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;YACpD,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;QAE9E,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QACpE,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,KAAK,UAAU,CAAC;QAE3D,MAAM,YAAY,GAAG,aAAa,CAAC,IAAI,CACrC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,CACrF,CAAC;QACF,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QAClE,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CACnC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAC/D,CAAC;QAEF,mCAAmC;QACnC,MAAM,YAAY,GAAa,EAAE,CAAC;QAClC,IAAI,UAAU;YAAE,YAAY,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QACjE,IAAI,UAAU;YAAE,YAAY,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;QACxE,IAAI,WAAW,IAAI,UAAU;YAAE,YAAY,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;aACvF,IAAI,WAAW;YAAE,YAAY,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;aACvD,IAAI,UAAU;YAAE,YAAY,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QAC9D,IAAI,iBAAiB;YAAE,YAAY,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACrE,IAAI,YAAY;YAAE,YAAY,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;QACtE,IAAI,SAAS;YAAE,YAAY,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAChE,IAAI,UAAU;YAAE,YAAY,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;QAErE,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,QAAQ,CAAC,IAAI,CAAC;gBACZ,EAAE,EAAE,eAAe,CAAC,UAAU,CAAC;gBAC/B,MAAM,EAAE,UAAU;gBAClB,QAAQ,EAAE,cAAc;gBACxB,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,wBAAwB,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBACpE,WAAW,EAAE,6CAA6C,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBACpF,cAAc,EAAE,gEAAgE;gBAChF,UAAU,EAAE,GAAG;gBACf,WAAW,EAAE,KAAK;aACnB,CAAC,CAAC;QACL,CAAC;QAED,8DAA8D;QAC9D,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;YAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC1B,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,eAAe,CAAC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBAClD,MAAM,EAAE,UAAU;oBAClB,QAAQ,EAAE,cAAc;oBACxB,QAAQ,EAAE,MAAM;oBAChB,KAAK,EAAE,8BAA8B;oBACrC,WAAW,EAAE,CAAC,CAAC,OAAO;oBACtB,cAAc,EACZ,iFAAiF;oBACnF,UAAU,EAAE,IAAI;oBAChB,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE;oBACpC,WAAW,EAAE,KAAK;iBACnB,CAAC,CAAC;YACL,CAAC;iBAAM,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBACjC,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,eAAe,CAAC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBAClD,MAAM,EAAE,UAAU;oBAClB,QAAQ,EAAE,cAAc;oBACxB,QAAQ,EAAE,MAAM;oBAChB,KAAK,EAAE,kCAAkC;oBACzC,WAAW,EAAE,CAAC,CAAC,OAAO;oBACtB,cAAc,EACZ,sJAAsJ;oBACxJ,UAAU,EAAE,GAAG;oBACf,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE;oBACpC,WAAW,EAAE,KAAK;iBACnB,CAAC,CAAC;YACL,CAAC;iBAAM,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBACjC,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,eAAe,CAAC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;oBACjD,MAAM,EAAE,UAAU;oBAClB,QAAQ,EAAE,cAAc;oBACxB,QAAQ,EAAE,KAAK;oBACf,KAAK,EAAE,oBAAoB,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE;oBAC5D,WAAW,EAAE,CAAC,CAAC,OAAO;oBACtB,cAAc,EACZ,+FAA+F;oBACjG,UAAU,EAAE,GAAG;oBACf,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE;oBACpC,WAAW,EAAE,KAAK;iBACnB,CAAC,CAAC;YACL,CAAC;iBAAM,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAC9B,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,eAAe,CAAC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;oBACjD,MAAM,EAAE,UAAU;oBAClB,QAAQ,EAAE,cAAc;oBACxB,QAAQ,EAAE,KAAK;oBACf,KAAK,EAAE,4BAA4B,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE;oBACpE,WAAW,EAAE,CAAC,CAAC,OAAO;oBACtB,cAAc,EACZ,sFAAsF;oBACxF,UAAU,EAAE,IAAI;oBAChB,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE;oBACpC,WAAW,EAAE,KAAK;iBACnB,CAAC,CAAC;YACL,CAAC;iBAAM,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC/B,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,eAAe,CAAC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;oBACjD,MAAM,EAAE,UAAU;oBAClB,QAAQ,EAAE,cAAc;oBACxB,QAAQ,EAAE,MAAM;oBAChB,KAAK,EAAE,yBAAyB,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE;oBACjE,WAAW,EAAE,CAAC,CAAC,OAAO;oBACtB,cAAc,EACZ,8FAA8F;oBAChG,UAAU,EAAE,IAAI;oBAChB,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE;oBACpC,WAAW,EAAE,KAAK;iBACnB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAAY;YACvB,WAAW;YACX,UAAU;YACV,iBAAiB;YACjB,UAAU;YACV,UAAU;YACV,YAAY;YACZ,SAAS;YACT,UAAU;YACV,YAAY;YACZ,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,uBAAuB,EAAE,OAAO,CAAC,oBAAoB,CAAC,MAAM;YAC5D,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM;YACjC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC;YACtC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC;SAC3C,CAAC;QAEF,OAAO;YACL,UAAU,EAAE,IAAI,CAAC,EAAE;YACnB,YAAY,EAAE,IAAI,CAAC,IAAI;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,MAAM,EAAE,SAAS;YACjB,QAAQ;YACR,OAAO;YACP,UAAU,EAAE,OAAO,EAAE;YACrB,QAAQ;SACT,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Architectural layer categories.
|
|
3
|
+
*/
|
|
4
|
+
export type ArchitectureLayer = 'frontend' | 'backend' | 'shared' | 'config' | 'test' | 'unknown';
|
|
5
|
+
/**
|
|
6
|
+
* Functional component roles within layers.
|
|
7
|
+
*/
|
|
8
|
+
export type ArchitectureRole = 'component' | 'page' | 'route' | 'controller' | 'service' | 'model' | 'util' | 'entry' | 'unknown';
|
|
9
|
+
/**
|
|
10
|
+
* A node in the in-memory architecture graph.
|
|
11
|
+
*/
|
|
12
|
+
export interface ArchitectureNode {
|
|
13
|
+
id: string;
|
|
14
|
+
type: 'file' | 'directory' | 'package';
|
|
15
|
+
layer: ArchitectureLayer;
|
|
16
|
+
role: ArchitectureRole;
|
|
17
|
+
lineCount: number;
|
|
18
|
+
inDegree: number;
|
|
19
|
+
outDegree: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A directed edge representing an import or reference in the architecture graph.
|
|
23
|
+
*/
|
|
24
|
+
export interface ArchitectureEdge {
|
|
25
|
+
source: string;
|
|
26
|
+
target: string;
|
|
27
|
+
type: 'import' | 're-export' | 'reference';
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Serialized in-memory architecture graph representation.
|
|
31
|
+
*/
|
|
32
|
+
export interface ArchitectureGraph {
|
|
33
|
+
nodes: ArchitectureNode[];
|
|
34
|
+
edges: ArchitectureEdge[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Layer breakdown for reporting.
|
|
38
|
+
*/
|
|
39
|
+
export interface LayerSummary {
|
|
40
|
+
name: string;
|
|
41
|
+
components: Array<{
|
|
42
|
+
name: string;
|
|
43
|
+
fileCount: number;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* High-level architecture warning.
|
|
48
|
+
*/
|
|
49
|
+
export interface ArchitectureWarning {
|
|
50
|
+
type: 'circular' | 'boundary' | 'coupling' | 'large' | 'orphan';
|
|
51
|
+
severity: 'high' | 'medium' | 'low' | 'info';
|
|
52
|
+
message: string;
|
|
53
|
+
files: string[];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* High-level architecture summary for reporting and CLI display.
|
|
57
|
+
*/
|
|
58
|
+
export interface ArchitectureSummary {
|
|
59
|
+
layers: LayerSummary[];
|
|
60
|
+
warnings: ArchitectureWarning[];
|
|
61
|
+
nodeCount: number;
|
|
62
|
+
edgeCount: number;
|
|
63
|
+
circularDependencies: string[][];
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/analyzers/architecture/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAElG;;GAEG;AACH,MAAM,MAAM,gBAAgB,GACxB,WAAW,GACX,MAAM,GACN,OAAO,GACP,YAAY,GACZ,SAAS,GACT,OAAO,GACP,MAAM,GACN,OAAO,GACP,SAAS,CAAC;AAEd;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;IACvC,KAAK,EAAE,iBAAiB,CAAC;IACzB,IAAI,EAAE,gBAAgB,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAC;CAC5C;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxD;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;IAChE,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB,EAAE,MAAM,EAAE,EAAE,CAAC;CAClC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/analyzers/architecture/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Analyzer } from '../../core/analyzer.js';
|
|
2
|
+
import type { RepositoryContext } from '../../core/context.js';
|
|
3
|
+
import type { AnalyzerResult } from '../../core/result.js';
|
|
4
|
+
export declare class CICDAnalyzer implements Analyzer {
|
|
5
|
+
readonly id = "cicd";
|
|
6
|
+
readonly name = "CI/CD";
|
|
7
|
+
readonly category: "cicd";
|
|
8
|
+
readonly description = "Checks for CI/CD configuration and workflow quality.";
|
|
9
|
+
analyze(context: RepositoryContext): Promise<AnalyzerResult>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analyzers/cicd/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAW,MAAM,sBAAsB,CAAC;AAoDpE,qBAAa,YAAa,YAAW,QAAQ;IAC3C,QAAQ,CAAC,EAAE,UAAU;IACrB,QAAQ,CAAC,IAAI,WAAW;IACxB,QAAQ,CAAC,QAAQ,EAAG,MAAM,CAAU;IACpC,QAAQ,CAAC,WAAW,0DAA0D;IAExE,OAAO,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC;CA+HnE"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { createFindingId } from '../../core/findings.js';
|
|
2
|
+
import { createTimer } from '../../utils/timing.js';
|
|
3
|
+
import { readFileSafe } from '../../utils/filesystem.js';
|
|
4
|
+
const GITHUB_ACTIONS_DIR = '.github/workflows';
|
|
5
|
+
const CIRCLECI_CONFIG = '.circleci/config.yml';
|
|
6
|
+
const GITLAB_CI = '.gitlab-ci.yml';
|
|
7
|
+
const TRAVIS_CI = '.travis.yml';
|
|
8
|
+
const JENKINS = 'Jenkinsfile';
|
|
9
|
+
const CI_WORKFLOW_INDICATORS = [
|
|
10
|
+
GITHUB_ACTIONS_DIR,
|
|
11
|
+
CIRCLECI_CONFIG,
|
|
12
|
+
GITLAB_CI,
|
|
13
|
+
TRAVIS_CI,
|
|
14
|
+
JENKINS,
|
|
15
|
+
'azure-pipelines.yml',
|
|
16
|
+
'bitbucket-pipelines.yml',
|
|
17
|
+
'.buildkite/pipeline.yml',
|
|
18
|
+
];
|
|
19
|
+
/** Keywords in CI config that suggest test execution */
|
|
20
|
+
const TEST_EXECUTION_KEYWORDS = [
|
|
21
|
+
'test',
|
|
22
|
+
'vitest',
|
|
23
|
+
'jest',
|
|
24
|
+
'pytest',
|
|
25
|
+
'mocha',
|
|
26
|
+
'jasmine',
|
|
27
|
+
'karma',
|
|
28
|
+
'rspec',
|
|
29
|
+
'go test',
|
|
30
|
+
'cargo test',
|
|
31
|
+
'mvn test',
|
|
32
|
+
];
|
|
33
|
+
/** Keywords in CI config that suggest build execution */
|
|
34
|
+
const BUILD_EXECUTION_KEYWORDS = [
|
|
35
|
+
'build',
|
|
36
|
+
'compile',
|
|
37
|
+
'tsc',
|
|
38
|
+
'webpack',
|
|
39
|
+
'vite',
|
|
40
|
+
'rollup',
|
|
41
|
+
'esbuild',
|
|
42
|
+
'maven',
|
|
43
|
+
'gradle',
|
|
44
|
+
'cargo build',
|
|
45
|
+
];
|
|
46
|
+
export class CICDAnalyzer {
|
|
47
|
+
id = 'cicd';
|
|
48
|
+
name = 'CI/CD';
|
|
49
|
+
category = 'cicd';
|
|
50
|
+
description = 'Checks for CI/CD configuration and workflow quality.';
|
|
51
|
+
async analyze(context) {
|
|
52
|
+
const elapsed = createTimer();
|
|
53
|
+
const findings = [];
|
|
54
|
+
const warnings = [];
|
|
55
|
+
// CI-001: GitHub Actions
|
|
56
|
+
const githubWorkflows = context.files.filter((f) => f.relativePath.startsWith('.github/workflows/') &&
|
|
57
|
+
(f.extension === '.yml' || f.extension === '.yaml'));
|
|
58
|
+
const hasGitHubActions = githubWorkflows.length > 0;
|
|
59
|
+
if (hasGitHubActions) {
|
|
60
|
+
findings.push({
|
|
61
|
+
id: createFindingId('CI-001'),
|
|
62
|
+
ruleId: 'CI-001',
|
|
63
|
+
category: 'cicd',
|
|
64
|
+
severity: 'info',
|
|
65
|
+
title: `GitHub Actions detected (${githubWorkflows.length} workflow${githubWorkflows.length > 1 ? 's' : ''})`,
|
|
66
|
+
description: `Detected ${githubWorkflows.length} GitHub Actions workflow file(s).`,
|
|
67
|
+
recommendation: 'Ensure workflows cover branch protection, testing, and linting.',
|
|
68
|
+
confidence: 0.99,
|
|
69
|
+
autoFixable: false,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// CI-002: Any CI workflow
|
|
73
|
+
const hasCIConfig = hasGitHubActions ||
|
|
74
|
+
context.files.some((f) => CI_WORKFLOW_INDICATORS.some((indicator) => f.relativePath === indicator || f.relativePath.startsWith(indicator + '/')));
|
|
75
|
+
if (!hasCIConfig) {
|
|
76
|
+
findings.push({
|
|
77
|
+
id: createFindingId('CI-002'),
|
|
78
|
+
ruleId: 'CI-002',
|
|
79
|
+
category: 'cicd',
|
|
80
|
+
severity: 'medium',
|
|
81
|
+
title: 'No CI/CD configuration detected',
|
|
82
|
+
description: 'Automated CI/CD helps catch issues before they reach production.',
|
|
83
|
+
recommendation: 'Consider setting up GitHub Actions, CircleCI, or another CI provider.',
|
|
84
|
+
confidence: 0.85,
|
|
85
|
+
autoFixable: false,
|
|
86
|
+
references: ['https://docs.github.com/en/actions'],
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
// CI-003: Tests in CI
|
|
91
|
+
let testsInCI = false;
|
|
92
|
+
// CI-004: Build in CI
|
|
93
|
+
let buildInCI = false;
|
|
94
|
+
// Read all CI workflow files and check their content
|
|
95
|
+
const ciFiles = [
|
|
96
|
+
...githubWorkflows,
|
|
97
|
+
...context.files.filter((f) => f.relativePath === CIRCLECI_CONFIG ||
|
|
98
|
+
f.relativePath === GITLAB_CI ||
|
|
99
|
+
f.relativePath === TRAVIS_CI),
|
|
100
|
+
];
|
|
101
|
+
for (const ciFile of ciFiles) {
|
|
102
|
+
const content = await readFileSafe(ciFile.absolutePath, 256 * 1024);
|
|
103
|
+
if (!content)
|
|
104
|
+
continue;
|
|
105
|
+
const lower = content.toLowerCase();
|
|
106
|
+
if (TEST_EXECUTION_KEYWORDS.some((kw) => lower.includes(kw))) {
|
|
107
|
+
testsInCI = true;
|
|
108
|
+
}
|
|
109
|
+
if (BUILD_EXECUTION_KEYWORDS.some((kw) => lower.includes(kw))) {
|
|
110
|
+
buildInCI = true;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (!testsInCI) {
|
|
114
|
+
findings.push({
|
|
115
|
+
id: createFindingId('CI-003'),
|
|
116
|
+
ruleId: 'CI-003',
|
|
117
|
+
category: 'cicd',
|
|
118
|
+
severity: 'medium',
|
|
119
|
+
title: 'CI workflow does not appear to run tests',
|
|
120
|
+
description: 'Running tests in CI helps catch regressions automatically.',
|
|
121
|
+
recommendation: 'Add a test step to your CI workflow.',
|
|
122
|
+
confidence: 0.7,
|
|
123
|
+
autoFixable: false,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (!buildInCI) {
|
|
127
|
+
findings.push({
|
|
128
|
+
id: createFindingId('CI-004'),
|
|
129
|
+
ruleId: 'CI-004',
|
|
130
|
+
category: 'cicd',
|
|
131
|
+
severity: 'low',
|
|
132
|
+
title: 'CI workflow does not appear to run a build step',
|
|
133
|
+
description: 'Building in CI validates that the project compiles correctly.',
|
|
134
|
+
recommendation: 'Add a build step to your CI workflow.',
|
|
135
|
+
confidence: 0.65,
|
|
136
|
+
autoFixable: false,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const metrics = {
|
|
141
|
+
hasCIConfig,
|
|
142
|
+
hasGitHubActions,
|
|
143
|
+
ciWorkflowCount: githubWorkflows.length,
|
|
144
|
+
};
|
|
145
|
+
return {
|
|
146
|
+
analyzerId: this.id,
|
|
147
|
+
analyzerName: this.name,
|
|
148
|
+
category: this.category,
|
|
149
|
+
status: 'success',
|
|
150
|
+
findings,
|
|
151
|
+
metrics,
|
|
152
|
+
durationMs: elapsed(),
|
|
153
|
+
warnings,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analyzers/cicd/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Analyzer } from '../../core/analyzer.js';
|
|
2
|
+
import type { RepositoryContext } from '../../core/context.js';
|
|
3
|
+
import type { AnalyzerResult } from '../../core/result.js';
|
|
4
|
+
export declare class DependencyAnalyzer implements Analyzer {
|
|
5
|
+
readonly id = "dependencies";
|
|
6
|
+
readonly name = "Dependencies";
|
|
7
|
+
readonly category: "dependencies";
|
|
8
|
+
readonly description = "Checks package manifests, lockfiles, duplicate versions, suspicious configurations, and dependency health.";
|
|
9
|
+
analyze(context: RepositoryContext): Promise<AnalyzerResult>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analyzers/dependencies/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAW,MAAM,sBAAsB,CAAC;AAuEpE,qBAAa,kBAAmB,YAAW,QAAQ;IACjD,QAAQ,CAAC,EAAE,kBAAkB;IAC7B,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,QAAQ,EAAG,cAAc,CAAU;IAC5C,QAAQ,CAAC,WAAW,gHAC2F;IAEzG,OAAO,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC;CAkYnE"}
|