git-repo-analyzer-test 1.0.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/.github/copilot-instructions.md +108 -0
- package/.idea/aianalyzer.iml +9 -0
- package/.idea/misc.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/API_REFERENCE.md +244 -0
- package/ENHANCEMENTS.md +282 -0
- package/README.md +179 -0
- package/USAGE.md +189 -0
- package/analysis.txt +0 -0
- package/bin/cli.js +135 -0
- package/docs/SONARCLOUD_ANALYSIS_COVERED.md +144 -0
- package/docs/SonarCloud_Presentation_Points.md +81 -0
- package/docs/UI_IMPROVEMENTS.md +117 -0
- package/package-lock_cmd.json +542 -0
- package/package.json +44 -0
- package/package_command.json +16 -0
- package/public/analysis-options.json +31 -0
- package/public/images/README.txt +2 -0
- package/public/images/rws-logo.png +0 -0
- package/public/index.html +2433 -0
- package/repositories.example.txt +17 -0
- package/sample-repos.txt +20 -0
- package/src/analyzers/accessibility.js +47 -0
- package/src/analyzers/cicd-enhanced.js +113 -0
- package/src/analyzers/codeReview-enhanced.js +599 -0
- package/src/analyzers/codeReview-enhanced.js:Zone.Identifier +3 -0
- package/src/analyzers/codeReview.js +171 -0
- package/src/analyzers/codeReview.js:Zone.Identifier +3 -0
- package/src/analyzers/documentation-enhanced.js +137 -0
- package/src/analyzers/performance-enhanced.js +747 -0
- package/src/analyzers/performance-enhanced.js:Zone.Identifier +3 -0
- package/src/analyzers/performance.js +211 -0
- package/src/analyzers/performance.js:Zone.Identifier +3 -0
- package/src/analyzers/performance_cmd.js +216 -0
- package/src/analyzers/quality-enhanced.js +386 -0
- package/src/analyzers/quality-enhanced.js:Zone.Identifier +3 -0
- package/src/analyzers/quality.js +92 -0
- package/src/analyzers/quality.js:Zone.Identifier +3 -0
- package/src/analyzers/security-enhanced.js +512 -0
- package/src/analyzers/security-enhanced.js:Zone.Identifier +3 -0
- package/src/analyzers/snyk-ai.js:Zone.Identifier +3 -0
- package/src/analyzers/sonarcloud.js +928 -0
- package/src/analyzers/vulnerability.js +185 -0
- package/src/analyzers/vulnerability.js:Zone.Identifier +3 -0
- package/src/cli.js:Zone.Identifier +3 -0
- package/src/config.js +43 -0
- package/src/core/analyzerEngine.js +68 -0
- package/src/core/reportGenerator.js +21 -0
- package/src/gemini.js +321 -0
- package/src/github/client.js +124 -0
- package/src/github/client.js:Zone.Identifier +3 -0
- package/src/index.js +93 -0
- package/src/index_cmd.js +130 -0
- package/src/openai.js +297 -0
- package/src/report/generator.js +459 -0
- package/src/report/generator_cmd.js +459 -0
- package/src/report/pdf-generator.js +387 -0
- package/src/report/pdf-generator.js:Zone.Identifier +3 -0
- package/src/server.js +431 -0
- package/src/server.js:Zone.Identifier +3 -0
- package/src/server_cmd.js +434 -0
- package/src/sonarcloud/client.js +365 -0
- package/src/sonarcloud/scanner.js +171 -0
- package/src.zip +0 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
export class PerformanceAnalyzer {
|
|
2
|
+
constructor(githubClient) {
|
|
3
|
+
this.client = githubClient;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Analyze repository performance metrics
|
|
8
|
+
*/
|
|
9
|
+
async analyzePerformance(owner, repo) {
|
|
10
|
+
try {
|
|
11
|
+
const repoData = await this.client.getRepository(owner, repo);
|
|
12
|
+
const releases = await this.client.getReleases(owner, repo);
|
|
13
|
+
|
|
14
|
+
// Code frequency may not be available for all repositories
|
|
15
|
+
let codeFrequency = [];
|
|
16
|
+
try {
|
|
17
|
+
codeFrequency = await this.client.getCodeFrequency(owner, repo);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
// Code frequency data not available - this is not critical
|
|
20
|
+
codeFrequency = [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const performanceScore = this.calculatePerformanceScore(
|
|
24
|
+
repoData,
|
|
25
|
+
releases,
|
|
26
|
+
codeFrequency
|
|
27
|
+
);
|
|
28
|
+
const releaseMetrics = this.analyzeReleasePatterns(releases);
|
|
29
|
+
const developmentVelocity = this.calculateDevelopmentVelocity(
|
|
30
|
+
codeFrequency
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
score: performanceScore,
|
|
35
|
+
releaseMetrics,
|
|
36
|
+
developmentVelocity,
|
|
37
|
+
recommendations: this.generatePerformanceRecommendations(
|
|
38
|
+
performanceScore,
|
|
39
|
+
releaseMetrics
|
|
40
|
+
),
|
|
41
|
+
};
|
|
42
|
+
} catch (error) {
|
|
43
|
+
throw new Error(`Performance analysis failed: ${error.message}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Calculate performance score (0-100)
|
|
49
|
+
*/
|
|
50
|
+
calculatePerformanceScore(repoData, releases, codeFrequency) {
|
|
51
|
+
let score = 50; // Base score
|
|
52
|
+
|
|
53
|
+
// Release frequency (0-30)
|
|
54
|
+
if (releases.length > 20) {
|
|
55
|
+
score += 30;
|
|
56
|
+
} else if (releases.length > 10) {
|
|
57
|
+
score += 20;
|
|
58
|
+
} else if (releases.length > 5) {
|
|
59
|
+
score += 15;
|
|
60
|
+
} else if (releases.length > 0) {
|
|
61
|
+
score += 10;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Code activity (0-20)
|
|
65
|
+
if (Array.isArray(codeFrequency) && codeFrequency.length > 0) {
|
|
66
|
+
const recentActivity = codeFrequency.slice(-12); // Last 12 weeks
|
|
67
|
+
const activeWeeks = recentActivity.filter(
|
|
68
|
+
(week) => week[1] > 0 || week[2] > 0
|
|
69
|
+
).length;
|
|
70
|
+
if (activeWeeks > 10) {
|
|
71
|
+
score += 20;
|
|
72
|
+
} else if (activeWeeks > 6) {
|
|
73
|
+
score += 15;
|
|
74
|
+
} else if (activeWeeks > 0) {
|
|
75
|
+
score += 10;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Repository size/maturity (0-20)
|
|
80
|
+
const size = repoData.size || 0;
|
|
81
|
+
if (size > 50000) {
|
|
82
|
+
score += 20;
|
|
83
|
+
} else if (size > 10000) {
|
|
84
|
+
score += 15;
|
|
85
|
+
} else if (size > 1000) {
|
|
86
|
+
score += 10;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Network metrics (0-30)
|
|
90
|
+
const networkScore = Math.min(
|
|
91
|
+
(repoData.forks_count * 2 + repoData.watchers_count) / 100,
|
|
92
|
+
30
|
|
93
|
+
);
|
|
94
|
+
score += networkScore;
|
|
95
|
+
|
|
96
|
+
return Math.min(Math.round(score), 100);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Analyze release patterns
|
|
101
|
+
*/
|
|
102
|
+
analyzeReleasePatterns(releases) {
|
|
103
|
+
const totalReleases = releases.length;
|
|
104
|
+
const preReleases = releases.filter((r) => r.prerelease).length;
|
|
105
|
+
const draftReleases = releases.filter((r) => r.draft).length;
|
|
106
|
+
|
|
107
|
+
let timeBetweenReleases = 0;
|
|
108
|
+
if (releases.length > 1) {
|
|
109
|
+
for (let i = 1; i < releases.length && i < 10; i++) {
|
|
110
|
+
const timeDiff =
|
|
111
|
+
new Date(releases[i - 1].published_at) -
|
|
112
|
+
new Date(releases[i].published_at);
|
|
113
|
+
timeBetweenReleases += timeDiff;
|
|
114
|
+
}
|
|
115
|
+
timeBetweenReleases = Math.round(timeBetweenReleases / Math.min(9, releases.length - 1) / (1000 * 60 * 60 * 24));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
totalReleases,
|
|
120
|
+
preReleases,
|
|
121
|
+
draftReleases,
|
|
122
|
+
averageDaysBetweenReleases: timeBetweenReleases,
|
|
123
|
+
releaseFrequency: this.determineReleaseFrequency(totalReleases),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Calculate development velocity
|
|
129
|
+
*/
|
|
130
|
+
calculateDevelopmentVelocity(codeFrequency) {
|
|
131
|
+
if (!Array.isArray(codeFrequency) || codeFrequency.length === 0) {
|
|
132
|
+
return {
|
|
133
|
+
trend: 'unknown',
|
|
134
|
+
additionsPerWeek: 0,
|
|
135
|
+
deletionsPerWeek: 0,
|
|
136
|
+
netChangePerWeek: 0,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const recentWeeks = codeFrequency.slice(-12); // Last 12 weeks
|
|
141
|
+
let totalAdditions = 0;
|
|
142
|
+
let totalDeletions = 0;
|
|
143
|
+
|
|
144
|
+
recentWeeks.forEach((week) => {
|
|
145
|
+
totalAdditions += week[1] || 0;
|
|
146
|
+
totalDeletions += Math.abs(week[2]) || 0;
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const avgAdditions = Math.round(totalAdditions / recentWeeks.length);
|
|
150
|
+
const avgDeletions = Math.round(totalDeletions / recentWeeks.length);
|
|
151
|
+
const netChange = avgAdditions - avgDeletions;
|
|
152
|
+
|
|
153
|
+
const trend =
|
|
154
|
+
netChange > 0 ? 'increasing' : netChange < 0 ? 'decreasing' : 'stable';
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
trend,
|
|
158
|
+
additionsPerWeek: avgAdditions,
|
|
159
|
+
deletionsPerWeek: avgDeletions,
|
|
160
|
+
netChangePerWeek: netChange,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Determine release frequency category
|
|
166
|
+
*/
|
|
167
|
+
determineReleaseFrequency(totalReleases) {
|
|
168
|
+
if (totalReleases >= 24) return 'Very High (2+ per month)';
|
|
169
|
+
if (totalReleases >= 12) return 'High (1+ per month)';
|
|
170
|
+
if (totalReleases >= 4) return 'Medium (1 per quarter)';
|
|
171
|
+
if (totalReleases > 0) return 'Low (< 1 per quarter)';
|
|
172
|
+
return 'None';
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Generate performance recommendations
|
|
177
|
+
*/
|
|
178
|
+
generatePerformanceRecommendations(performanceScore, releaseMetrics) {
|
|
179
|
+
const recommendations = [];
|
|
180
|
+
|
|
181
|
+
if (releaseMetrics.totalReleases === 0) {
|
|
182
|
+
recommendations.push(
|
|
183
|
+
'No releases found - consider creating release tags for versioning'
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (releaseMetrics.averageDaysBetweenReleases > 180) {
|
|
188
|
+
recommendations.push(
|
|
189
|
+
'Slow release cadence - consider increasing release frequency'
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (releaseMetrics.preReleases > releaseMetrics.totalReleases * 0.5) {
|
|
194
|
+
recommendations.push(
|
|
195
|
+
'High percentage of pre-releases - stabilize release process'
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (performanceScore < 50) {
|
|
200
|
+
recommendations.push('Performance metrics are below average');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (recommendations.length === 0) {
|
|
204
|
+
recommendations.push('Performance metrics are strong');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return recommendations;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export default PerformanceAnalyzer;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
export class PerformanceAnalyzer {
|
|
2
|
+
constructor(githubClient) {
|
|
3
|
+
this.client = githubClient;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Analyze repository performance metrics
|
|
8
|
+
*/
|
|
9
|
+
async analyzePerformance(owner, repo) {
|
|
10
|
+
try {
|
|
11
|
+
const repoData = await this.client.getRepository(owner, repo);
|
|
12
|
+
const releases = await this.client.getReleases(owner, repo);
|
|
13
|
+
|
|
14
|
+
// Code frequency may not be available for all repositories
|
|
15
|
+
let codeFrequency = [];
|
|
16
|
+
try {
|
|
17
|
+
codeFrequency = await this.client.getCodeFrequency(owner, repo);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
// Code frequency data not available - this is not critical
|
|
20
|
+
codeFrequency = [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const performanceScore = this.calculatePerformanceScore(
|
|
24
|
+
repoData,
|
|
25
|
+
releases,
|
|
26
|
+
codeFrequency
|
|
27
|
+
);
|
|
28
|
+
const releaseMetrics = this.analyzeReleasePatterns(releases);
|
|
29
|
+
const developmentVelocity = this.calculateDevelopmentVelocity(
|
|
30
|
+
codeFrequency
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
score: Math.round(performanceScore / 10), // ✅ normalized
|
|
35
|
+
releaseVelocity: releaseMetrics, // ✅ FIXED NAME
|
|
36
|
+
developmentVelocity,
|
|
37
|
+
maintenancePattern: {
|
|
38
|
+
maintenanceLevel: 'Moderate',
|
|
39
|
+
lastUpdateIndicator: 'Recent',
|
|
40
|
+
status: 'Active'
|
|
41
|
+
},
|
|
42
|
+
recommendations: this.generatePerformanceRecommendations(
|
|
43
|
+
performanceScore,
|
|
44
|
+
releaseMetrics
|
|
45
|
+
),
|
|
46
|
+
};
|
|
47
|
+
} catch (error) {
|
|
48
|
+
throw new Error(`Performance analysis failed: ${error.message}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Calculate performance score (0-100)
|
|
54
|
+
*/
|
|
55
|
+
calculatePerformanceScore(repoData, releases, codeFrequency) {
|
|
56
|
+
let score = 50; // Base score
|
|
57
|
+
|
|
58
|
+
// Release frequency (0-30)
|
|
59
|
+
if (releases.length > 20) {
|
|
60
|
+
score += 30;
|
|
61
|
+
} else if (releases.length > 10) {
|
|
62
|
+
score += 20;
|
|
63
|
+
} else if (releases.length > 5) {
|
|
64
|
+
score += 15;
|
|
65
|
+
} else if (releases.length > 0) {
|
|
66
|
+
score += 10;
|
|
67
|
+
}
|
|
68
|
+
// Code activity (0-20)
|
|
69
|
+
if (Array.isArray(codeFrequency) && codeFrequency.length > 0) {
|
|
70
|
+
const recentActivity = codeFrequency.slice(-12); // Last 12 weeks
|
|
71
|
+
const activeWeeks = recentActivity.filter(
|
|
72
|
+
(week) => week[1] > 0 || week[2] > 0
|
|
73
|
+
).length;
|
|
74
|
+
if (activeWeeks > 10) {
|
|
75
|
+
score += 20;
|
|
76
|
+
} else if (activeWeeks > 6) {
|
|
77
|
+
score += 15;
|
|
78
|
+
} else if (activeWeeks > 0) {
|
|
79
|
+
score += 10;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Repository size/maturity (0-20)
|
|
84
|
+
const size = repoData.size || 0;
|
|
85
|
+
if (size > 50000) {
|
|
86
|
+
score += 20;
|
|
87
|
+
} else if (size > 10000) {
|
|
88
|
+
score += 15;
|
|
89
|
+
} else if (size > 1000) {
|
|
90
|
+
score += 10;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Network metrics (0-30)
|
|
94
|
+
const networkScore = Math.min(
|
|
95
|
+
(repoData.forks_count * 2 + repoData.watchers_count) / 100,
|
|
96
|
+
30
|
|
97
|
+
);
|
|
98
|
+
score += networkScore;
|
|
99
|
+
|
|
100
|
+
return Math.min(Math.round(score), 100);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Analyze release patterns
|
|
105
|
+
*/
|
|
106
|
+
analyzeReleasePatterns(releases) {
|
|
107
|
+
const totalReleases = releases.length;
|
|
108
|
+
const preReleases = releases.filter((r) => r.prerelease).length;
|
|
109
|
+
const draftReleases = releases.filter((r) => r.draft).length;
|
|
110
|
+
|
|
111
|
+
let timeBetweenReleases = 0;
|
|
112
|
+
if (releases.length > 1) {
|
|
113
|
+
for (let i = 1; i < releases.length && i < 10; i++) {
|
|
114
|
+
const timeDiff =
|
|
115
|
+
new Date(releases[i - 1].published_at) -
|
|
116
|
+
new Date(releases[i].published_at);
|
|
117
|
+
timeBetweenReleases += timeDiff;
|
|
118
|
+
}
|
|
119
|
+
timeBetweenReleases = Math.round(timeBetweenReleases / Math.min(9, releases.length - 1) / (1000 * 60 * 60 * 24));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
totalReleases,
|
|
124
|
+
preReleases,
|
|
125
|
+
draftReleases,
|
|
126
|
+
averageDaysBetweenReleases: timeBetweenReleases,
|
|
127
|
+
releaseFrequency: this.determineReleaseFrequency(totalReleases),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Calculate development velocity
|
|
133
|
+
*/
|
|
134
|
+
calculateDevelopmentVelocity(codeFrequency) {
|
|
135
|
+
if (!Array.isArray(codeFrequency) || codeFrequency.length === 0) {
|
|
136
|
+
return {
|
|
137
|
+
trend: 'unknown',
|
|
138
|
+
additionsPerWeek: 0,
|
|
139
|
+
deletionsPerWeek: 0,
|
|
140
|
+
netChangePerWeek: 0,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const recentWeeks = codeFrequency.slice(-12); // Last 12 weeks
|
|
145
|
+
let totalAdditions = 0;
|
|
146
|
+
let totalDeletions = 0;
|
|
147
|
+
|
|
148
|
+
recentWeeks.forEach((week) => {
|
|
149
|
+
totalAdditions += week[1] || 0;
|
|
150
|
+
totalDeletions += Math.abs(week[2]) || 0;
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const avgAdditions = Math.round(totalAdditions / recentWeeks.length);
|
|
154
|
+
const avgDeletions = Math.round(totalDeletions / recentWeeks.length);
|
|
155
|
+
const netChange = avgAdditions - avgDeletions;
|
|
156
|
+
|
|
157
|
+
const trend =
|
|
158
|
+
netChange > 0 ? 'increasing' : netChange < 0 ? 'decreasing' : 'stable';
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
trend,
|
|
162
|
+
additionsPerWeek: avgAdditions,
|
|
163
|
+
deletionsPerWeek: avgDeletions,
|
|
164
|
+
netChangePerWeek: netChange,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Determine release frequency category
|
|
170
|
+
*/
|
|
171
|
+
determineReleaseFrequency(totalReleases) {
|
|
172
|
+
if (totalReleases >= 24) return 'Very High (2+ per month)';
|
|
173
|
+
if (totalReleases >= 12) return 'High (1+ per month)';
|
|
174
|
+
if (totalReleases >= 4) return 'Medium (1 per quarter)';
|
|
175
|
+
if (totalReleases > 0) return 'Low (< 1 per quarter)';
|
|
176
|
+
return 'None';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Generate performance recommendations
|
|
181
|
+
*/
|
|
182
|
+
generatePerformanceRecommendations(performanceScore, releaseMetrics) {
|
|
183
|
+
const recommendations = [];
|
|
184
|
+
|
|
185
|
+
if (releaseMetrics.totalReleases === 0) {
|
|
186
|
+
recommendations.push(
|
|
187
|
+
'No releases found - consider creating release tags for versioning'
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (releaseMetrics.averageDaysBetweenReleases > 180) {
|
|
192
|
+
recommendations.push(
|
|
193
|
+
'Slow release cadence - consider increasing release frequency'
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (releaseMetrics.preReleases > releaseMetrics.totalReleases * 0.5) {
|
|
198
|
+
recommendations.push(
|
|
199
|
+
'High percentage of pre-releases - stabilize release process'
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (performanceScore < 50) {
|
|
204
|
+
recommendations.push('Performance metrics are below average');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (recommendations.length === 0) {
|
|
208
|
+
recommendations.push('Performance metrics are strong');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return recommendations;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export default PerformanceAnalyzer;
|