@deniscuciuc/compose-analyzer 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/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +164 -0
- package/analyzerrc.example.json +3 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/package.json +81 -0
- package/dist/src/analyzers/image-analyzer.d.ts +8 -0
- package/dist/src/analyzers/image-analyzer.d.ts.map +1 -0
- package/dist/src/analyzers/image-analyzer.js +61 -0
- package/dist/src/analyzers/network-analyzer.d.ts +8 -0
- package/dist/src/analyzers/network-analyzer.d.ts.map +1 -0
- package/dist/src/analyzers/network-analyzer.js +48 -0
- package/dist/src/analyzers/reliability-analyzer.d.ts +8 -0
- package/dist/src/analyzers/reliability-analyzer.d.ts.map +1 -0
- package/dist/src/analyzers/reliability-analyzer.js +84 -0
- package/dist/src/analyzers/resource-analyzer.d.ts +8 -0
- package/dist/src/analyzers/resource-analyzer.d.ts.map +1 -0
- package/dist/src/analyzers/resource-analyzer.js +34 -0
- package/dist/src/analyzers/security-analyzer.d.ts +9 -0
- package/dist/src/analyzers/security-analyzer.d.ts.map +1 -0
- package/dist/src/analyzers/security-analyzer.js +82 -0
- package/dist/src/cli/options.d.ts +16 -0
- package/dist/src/cli/options.d.ts.map +1 -0
- package/dist/src/cli/options.js +129 -0
- package/dist/src/cli/runner.d.ts +6 -0
- package/dist/src/cli/runner.d.ts.map +1 -0
- package/dist/src/cli/runner.js +234 -0
- package/dist/src/collectors/compose-collector.d.ts +19 -0
- package/dist/src/collectors/compose-collector.d.ts.map +1 -0
- package/dist/src/collectors/compose-collector.js +140 -0
- package/dist/src/collectors/docker-collector.d.ts +8 -0
- package/dist/src/collectors/docker-collector.d.ts.map +1 -0
- package/dist/src/collectors/docker-collector.js +96 -0
- package/dist/src/config/loader.d.ts +3 -0
- package/dist/src/config/loader.d.ts.map +1 -0
- package/dist/src/config/loader.js +41 -0
- package/dist/src/constants.d.ts +23 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +70 -0
- package/dist/src/health-score.d.ts +3 -0
- package/dist/src/health-score.d.ts.map +1 -0
- package/dist/src/health-score.js +14 -0
- package/dist/src/interactive/display.d.ts +11 -0
- package/dist/src/interactive/display.d.ts.map +1 -0
- package/dist/src/interactive/display.js +117 -0
- package/dist/src/interactive/index.d.ts +15 -0
- package/dist/src/interactive/index.d.ts.map +1 -0
- package/dist/src/interactive/index.js +218 -0
- package/dist/src/interactive/menus.d.ts +68 -0
- package/dist/src/interactive/menus.d.ts.map +1 -0
- package/dist/src/interactive/menus.js +32 -0
- package/dist/src/reporters/diff-reporter.d.ts +21 -0
- package/dist/src/reporters/diff-reporter.d.ts.map +1 -0
- package/dist/src/reporters/diff-reporter.js +87 -0
- package/dist/src/reporters/html-reporter.d.ts +12 -0
- package/dist/src/reporters/html-reporter.d.ts.map +1 -0
- package/dist/src/reporters/html-reporter.js +226 -0
- package/dist/src/reporters/report-generator.d.ts +23 -0
- package/dist/src/reporters/report-generator.d.ts.map +1 -0
- package/dist/src/reporters/report-generator.js +326 -0
- package/dist/src/types.d.ts +198 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/utils/format.d.ts +6 -0
- package/dist/src/utils/format.d.ts.map +1 -0
- package/dist/src/utils/format.js +32 -0
- package/dist/src/utils/print.d.ts +8 -0
- package/dist/src/utils/print.d.ts.map +1 -0
- package/dist/src/utils/print.js +42 -0
- package/package.json +80 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ReportGenerator = void 0;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
const print_1 = require("../utils/print");
|
|
40
|
+
const html_reporter_1 = require("./html-reporter");
|
|
41
|
+
class ReportGenerator {
|
|
42
|
+
outputDir;
|
|
43
|
+
constructor(outputDir = "./reports", _options = {}) {
|
|
44
|
+
this.outputDir = outputDir;
|
|
45
|
+
}
|
|
46
|
+
async generateFullReport(report, timestamp) {
|
|
47
|
+
const ts = timestamp ?? new Date().toISOString().replace(/[:.]/g, "-");
|
|
48
|
+
const filename = `compose-analysis-${ts}.md`;
|
|
49
|
+
const filepath = path.join(this.outputDir, filename);
|
|
50
|
+
await this.ensureOutputDir();
|
|
51
|
+
fs.writeFileSync(filepath, this.buildMarkdownReport(report));
|
|
52
|
+
return filepath;
|
|
53
|
+
}
|
|
54
|
+
async generateJsonReport(report, timestamp) {
|
|
55
|
+
const ts = timestamp ?? new Date().toISOString().replace(/[:.]/g, "-");
|
|
56
|
+
const filename = `compose-analysis-${ts}.json`;
|
|
57
|
+
const filepath = path.join(this.outputDir, filename);
|
|
58
|
+
await this.ensureOutputDir();
|
|
59
|
+
fs.writeFileSync(filepath, JSON.stringify(report, null, 2));
|
|
60
|
+
return filepath;
|
|
61
|
+
}
|
|
62
|
+
async generateHtmlReport(report, timestamp) {
|
|
63
|
+
const ts = timestamp ?? new Date().toISOString().replace(/[:.]/g, "-");
|
|
64
|
+
const filename = `compose-analysis-${ts}.html`;
|
|
65
|
+
const filepath = path.join(this.outputDir, filename);
|
|
66
|
+
await this.ensureOutputDir();
|
|
67
|
+
fs.writeFileSync(filepath, html_reporter_1.HtmlReporter.generate(report));
|
|
68
|
+
return filepath;
|
|
69
|
+
}
|
|
70
|
+
printSummary(report) {
|
|
71
|
+
(0, print_1.printSection)("Compose Analysis Summary");
|
|
72
|
+
(0, print_1.printRow)("Health score", `${report.healthScore}/100 ${(0, print_1.healthEmoji)(report.healthScore)} ${(0, print_1.healthLabel)(report.healthScore)}`);
|
|
73
|
+
(0, print_1.printRow)("Compose file", report.composeFile);
|
|
74
|
+
(0, print_1.printRow)("Compose version", report.version ?? "not declared");
|
|
75
|
+
(0, print_1.printRow)("Services", report.metrics.totalServices);
|
|
76
|
+
(0, print_1.printRow)("Issues", report.metrics.totalIssues);
|
|
77
|
+
(0, print_1.printRow)("Critical / high", `${report.metrics.criticalIssues} / ${report.metrics.highIssues}`);
|
|
78
|
+
if (report.allIssues.length === 0) {
|
|
79
|
+
(0, print_1.printBullet)("No issues detected.");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
(0, print_1.printBullet)(`Top issues (${Math.min(report.allIssues.length, 5)} shown):`);
|
|
83
|
+
for (const issue of report.allIssues.slice(0, 5)) {
|
|
84
|
+
(0, print_1.printSubBullet)(`[${issue.severity}] ${issue.service} — ${issue.title}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async ensureOutputDir() {
|
|
88
|
+
if (!fs.existsSync(this.outputDir)) {
|
|
89
|
+
fs.mkdirSync(this.outputDir, { recursive: true });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
buildMarkdownReport(report) {
|
|
93
|
+
return [
|
|
94
|
+
this.buildHeader(report),
|
|
95
|
+
this.buildExecutiveSummary(report),
|
|
96
|
+
this.buildMetricsSection(report),
|
|
97
|
+
this.buildIssuesSection(report),
|
|
98
|
+
this.buildImageSection(report),
|
|
99
|
+
this.buildSecuritySection(report),
|
|
100
|
+
this.buildReliabilitySection(report),
|
|
101
|
+
this.buildResourceSection(report),
|
|
102
|
+
this.buildNetworkSection(report),
|
|
103
|
+
this.buildDockerRuntimeSection(report),
|
|
104
|
+
this.buildRecommendationsSection(report),
|
|
105
|
+
]
|
|
106
|
+
.filter(Boolean)
|
|
107
|
+
.join("\n\n");
|
|
108
|
+
}
|
|
109
|
+
buildHeader(report) {
|
|
110
|
+
return `# Compose Analysis Report
|
|
111
|
+
|
|
112
|
+
**Compose file:** ${report.composeFile}
|
|
113
|
+
**Generated:** ${new Date(report.generatedAt).toISOString()}
|
|
114
|
+
**Tool:** Compose Analyzer
|
|
115
|
+
|
|
116
|
+
---`;
|
|
117
|
+
}
|
|
118
|
+
buildExecutiveSummary(report) {
|
|
119
|
+
const findings = [
|
|
120
|
+
report.metrics.criticalIssues > 0
|
|
121
|
+
? `- **${report.metrics.criticalIssues}** critical issues need immediate attention`
|
|
122
|
+
: "- No critical issues detected",
|
|
123
|
+
report.metrics.highIssues > 0
|
|
124
|
+
? `- **${report.metrics.highIssues}** high-severity issues should be prioritized`
|
|
125
|
+
: "- No high-severity issues detected",
|
|
126
|
+
report.images.unpinnedImages.length > 0
|
|
127
|
+
? `- **${report.images.unpinnedImages.length}** services use unpinned images`
|
|
128
|
+
: "- All image references are pinned or build-only",
|
|
129
|
+
report.resources.missingLimits.length > 0
|
|
130
|
+
? `- **${report.resources.missingLimits.length}** services are missing resource limits`
|
|
131
|
+
: "- All services define CPU and memory limits",
|
|
132
|
+
];
|
|
133
|
+
return `## Executive Summary
|
|
134
|
+
|
|
135
|
+
### Health Score: ${report.healthScore}/100 ${(0, print_1.healthEmoji)(report.healthScore)}
|
|
136
|
+
|
|
137
|
+
### Key Findings
|
|
138
|
+
${findings.join("\n")}
|
|
139
|
+
|
|
140
|
+
### Quick Stats
|
|
141
|
+
| Metric | Value |
|
|
142
|
+
| --- | --- |
|
|
143
|
+
| Compose version | ${report.version ?? "not declared"} |
|
|
144
|
+
| Services | ${report.metrics.totalServices} |
|
|
145
|
+
| Services with build | ${report.metrics.servicesWithBuild} |
|
|
146
|
+
| Named volumes | ${report.metrics.namedVolumes} |
|
|
147
|
+
| Total issues | ${report.metrics.totalIssues} |
|
|
148
|
+
| Critical issues | ${report.metrics.criticalIssues} |
|
|
149
|
+
| High issues | ${report.metrics.highIssues} |`;
|
|
150
|
+
}
|
|
151
|
+
buildMetricsSection(report) {
|
|
152
|
+
return `## Metrics
|
|
153
|
+
|
|
154
|
+
| Metric | Value |
|
|
155
|
+
| --- | --- |
|
|
156
|
+
| Total services | ${report.metrics.totalServices} |
|
|
157
|
+
| Services with build contexts | ${report.metrics.servicesWithBuild} |
|
|
158
|
+
| Named volumes | ${report.metrics.namedVolumes} |
|
|
159
|
+
| Total issues | ${report.metrics.totalIssues} |
|
|
160
|
+
| Critical issues | ${report.metrics.criticalIssues} |
|
|
161
|
+
| High issues | ${report.metrics.highIssues} |`;
|
|
162
|
+
}
|
|
163
|
+
buildIssuesSection(report) {
|
|
164
|
+
if (report.allIssues.length === 0) {
|
|
165
|
+
return "## Issues\n\nNo issues detected.";
|
|
166
|
+
}
|
|
167
|
+
const rows = report.allIssues
|
|
168
|
+
.map((issue) => {
|
|
169
|
+
const fix = issue.fix ? issue.fix.replaceAll("\n", "<br />") : "—";
|
|
170
|
+
return `| ${issue.severity} | ${issue.service} | ${issue.category} | ${issue.title} | ${issue.detail} | ${fix} |`;
|
|
171
|
+
})
|
|
172
|
+
.join("\n");
|
|
173
|
+
return `## Issues\n\n| Severity | Service | Category | Title | Detail | Fix |
|
|
174
|
+
| --- | --- | --- | --- | --- | --- |
|
|
175
|
+
${rows}`;
|
|
176
|
+
}
|
|
177
|
+
buildImageSection(report) {
|
|
178
|
+
const lines = [
|
|
179
|
+
`## Image Analysis`,
|
|
180
|
+
``,
|
|
181
|
+
`### Unpinned Images (${report.images.unpinnedImages.length})`,
|
|
182
|
+
];
|
|
183
|
+
if (report.images.unpinnedImages.length === 0) {
|
|
184
|
+
lines.push("No unpinned images found.");
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
lines.push(`| Service | Image | Recommendation |`, `| --- | --- | --- |`);
|
|
188
|
+
for (const entry of report.images.unpinnedImages) {
|
|
189
|
+
lines.push(`| ${entry.service} | ${entry.image} | ${entry.recommendation} |`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
lines.push("", `### :latest Tags (${report.images.latestTagImages.length})`);
|
|
193
|
+
if (report.images.latestTagImages.length === 0) {
|
|
194
|
+
lines.push("No :latest tags found.");
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
lines.push(`| Service | Image |`, `| --- | --- |`);
|
|
198
|
+
for (const entry of report.images.latestTagImages) {
|
|
199
|
+
lines.push(`| ${entry.service} | ${entry.image} |`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return lines.join("\n");
|
|
203
|
+
}
|
|
204
|
+
buildSecuritySection(report) {
|
|
205
|
+
return [
|
|
206
|
+
`## Security Analysis`,
|
|
207
|
+
``,
|
|
208
|
+
`### Secrets in Environment (${report.security.secretsInEnv.length})`,
|
|
209
|
+
report.security.secretsInEnv.length === 0
|
|
210
|
+
? "No suspicious plain-text environment secrets found."
|
|
211
|
+
: [
|
|
212
|
+
`| Service | Variable | Pattern |`,
|
|
213
|
+
`| --- | --- | --- |`,
|
|
214
|
+
...report.security.secretsInEnv.map((entry) => `| ${entry.service} | ${entry.variable} | ${entry.pattern} |`),
|
|
215
|
+
].join("\n"),
|
|
216
|
+
``,
|
|
217
|
+
`### Privileged Services (${report.security.privilegedServices.length})`,
|
|
218
|
+
report.security.privilegedServices.length === 0
|
|
219
|
+
? "No privileged containers found."
|
|
220
|
+
: report.security.privilegedServices
|
|
221
|
+
.map((entry) => `- ${entry.service}`)
|
|
222
|
+
.join("\n"),
|
|
223
|
+
``,
|
|
224
|
+
`### Exposed Sensitive Ports (${report.security.exposedDatabases.length})`,
|
|
225
|
+
report.security.exposedDatabases.length === 0
|
|
226
|
+
? "No exposed database or broker ports found."
|
|
227
|
+
: [
|
|
228
|
+
`| Service | Image | Port | Bind Address |`,
|
|
229
|
+
`| --- | --- | --- | --- |`,
|
|
230
|
+
...report.security.exposedDatabases.map((entry) => `| ${entry.service} | ${entry.image} | ${entry.port} | ${entry.bindAddr} |`),
|
|
231
|
+
].join("\n"),
|
|
232
|
+
].join("\n");
|
|
233
|
+
}
|
|
234
|
+
buildReliabilitySection(report) {
|
|
235
|
+
return [
|
|
236
|
+
`## Reliability Analysis`,
|
|
237
|
+
``,
|
|
238
|
+
`### Missing Healthchecks (${report.reliability.missingHealthcheck.length})`,
|
|
239
|
+
report.reliability.missingHealthcheck.length === 0
|
|
240
|
+
? "All services define healthchecks."
|
|
241
|
+
: report.reliability.missingHealthcheck
|
|
242
|
+
.map((entry) => `- ${entry.service}`)
|
|
243
|
+
.join("\n"),
|
|
244
|
+
``,
|
|
245
|
+
`### Unsafe depends_on (${report.reliability.unsafeDepends.length})`,
|
|
246
|
+
report.reliability.unsafeDepends.length === 0
|
|
247
|
+
? "No unsafe depends_on conditions found."
|
|
248
|
+
: [
|
|
249
|
+
`| Service | Dependency | Condition |`,
|
|
250
|
+
`| --- | --- | --- |`,
|
|
251
|
+
...report.reliability.unsafeDepends.map((entry) => `| ${entry.service} | ${entry.dependency} | ${entry.condition} |`),
|
|
252
|
+
].join("\n"),
|
|
253
|
+
``,
|
|
254
|
+
`### Missing Restart Policies (${report.reliability.missingRestart.length})`,
|
|
255
|
+
report.reliability.missingRestart.length === 0
|
|
256
|
+
? "All services define a restart policy."
|
|
257
|
+
: report.reliability.missingRestart
|
|
258
|
+
.map((entry) => `- ${entry.service}`)
|
|
259
|
+
.join("\n"),
|
|
260
|
+
].join("\n");
|
|
261
|
+
}
|
|
262
|
+
buildResourceSection(report) {
|
|
263
|
+
return [
|
|
264
|
+
`## Resource Analysis`,
|
|
265
|
+
``,
|
|
266
|
+
`### Services Missing Limits (${report.resources.missingLimits.length})`,
|
|
267
|
+
report.resources.missingLimits.length === 0
|
|
268
|
+
? "All services define CPU and memory limits."
|
|
269
|
+
: [
|
|
270
|
+
`| Service | Missing |`,
|
|
271
|
+
`| --- | --- |`,
|
|
272
|
+
...report.resources.missingLimits.map((entry) => `| ${entry.service} | ${entry.missing.join(", ")} |`),
|
|
273
|
+
].join("\n"),
|
|
274
|
+
].join("\n");
|
|
275
|
+
}
|
|
276
|
+
buildNetworkSection(report) {
|
|
277
|
+
return [
|
|
278
|
+
`## Network Analysis`,
|
|
279
|
+
``,
|
|
280
|
+
`### Services on Default Network (${report.networks.servicesOnDefaultNetwork.length})`,
|
|
281
|
+
report.networks.servicesOnDefaultNetwork.length === 0
|
|
282
|
+
? "No default-network usage detected."
|
|
283
|
+
: report.networks.servicesOnDefaultNetwork
|
|
284
|
+
.map((entry) => `- ${entry.service}`)
|
|
285
|
+
.join("\n"),
|
|
286
|
+
``,
|
|
287
|
+
`### Random Host Bindings (${report.networks.exposedInternalPorts.length})`,
|
|
288
|
+
report.networks.exposedInternalPorts.length === 0
|
|
289
|
+
? "No random host bindings detected."
|
|
290
|
+
: [
|
|
291
|
+
`| Service | Port | Note |`,
|
|
292
|
+
`| --- | --- | --- |`,
|
|
293
|
+
...report.networks.exposedInternalPorts.map((entry) => `| ${entry.service} | ${entry.port} | ${entry.note} |`),
|
|
294
|
+
].join("\n"),
|
|
295
|
+
].join("\n");
|
|
296
|
+
}
|
|
297
|
+
buildDockerRuntimeSection(report) {
|
|
298
|
+
if (!report.dockerRuntime) {
|
|
299
|
+
return "";
|
|
300
|
+
}
|
|
301
|
+
if (!report.dockerRuntime.available) {
|
|
302
|
+
return `## Docker Runtime\n\n${report.dockerRuntime.error ?? "Docker runtime unavailable."}`;
|
|
303
|
+
}
|
|
304
|
+
return [
|
|
305
|
+
`## Docker Runtime`,
|
|
306
|
+
``,
|
|
307
|
+
`| Service | Container | State | Status | Health | Image |`,
|
|
308
|
+
`| --- | --- | --- | --- | --- | --- |`,
|
|
309
|
+
...report.dockerRuntime.services.map((service) => `| ${service.service} | ${service.containerName ?? "—"} | ${service.state ?? "—"} | ${service.status ?? "—"} | ${service.health ?? "—"} | ${service.image ?? "—"} |`),
|
|
310
|
+
].join("\n");
|
|
311
|
+
}
|
|
312
|
+
buildRecommendationsSection(report) {
|
|
313
|
+
if (report.recommendations.length === 0) {
|
|
314
|
+
return "## Recommendations\n\nNo recommendations.";
|
|
315
|
+
}
|
|
316
|
+
return `## Recommendations\n\n${report.recommendations
|
|
317
|
+
.map((recommendation) => {
|
|
318
|
+
const fix = recommendation.fix
|
|
319
|
+
? `\n - Fix: ${recommendation.fix}`
|
|
320
|
+
: "";
|
|
321
|
+
return `- **${recommendation.priority.toUpperCase()}** ${recommendation.service}: ${recommendation.message}${fix}`;
|
|
322
|
+
})
|
|
323
|
+
.join("\n")}`;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
exports.ReportGenerator = ReportGenerator;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
export interface ComposeService {
|
|
2
|
+
name?: string;
|
|
3
|
+
image?: string;
|
|
4
|
+
build?: string | {
|
|
5
|
+
context?: string;
|
|
6
|
+
dockerfile?: string;
|
|
7
|
+
};
|
|
8
|
+
ports?: Array<string | {
|
|
9
|
+
target: number;
|
|
10
|
+
published?: number | string;
|
|
11
|
+
host_ip?: string;
|
|
12
|
+
protocol?: string;
|
|
13
|
+
}>;
|
|
14
|
+
environment?: Record<string, string | null> | string[];
|
|
15
|
+
env_file?: string | string[];
|
|
16
|
+
volumes?: Array<string | {
|
|
17
|
+
type?: string;
|
|
18
|
+
source?: string;
|
|
19
|
+
target?: string;
|
|
20
|
+
}>;
|
|
21
|
+
networks?: string[] | Record<string, unknown>;
|
|
22
|
+
depends_on?: string[] | Record<string, {
|
|
23
|
+
condition?: string;
|
|
24
|
+
}>;
|
|
25
|
+
healthcheck?: {
|
|
26
|
+
test: string | string[];
|
|
27
|
+
interval?: string;
|
|
28
|
+
timeout?: string;
|
|
29
|
+
retries?: number;
|
|
30
|
+
start_period?: string;
|
|
31
|
+
disable?: boolean;
|
|
32
|
+
};
|
|
33
|
+
restart?: string;
|
|
34
|
+
deploy?: {
|
|
35
|
+
resources?: {
|
|
36
|
+
limits?: {
|
|
37
|
+
cpus?: string;
|
|
38
|
+
memory?: string;
|
|
39
|
+
};
|
|
40
|
+
reservations?: {
|
|
41
|
+
cpus?: string;
|
|
42
|
+
memory?: string;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
mem_limit?: string;
|
|
47
|
+
mem_reservation?: string;
|
|
48
|
+
cpus?: number | string;
|
|
49
|
+
user?: string;
|
|
50
|
+
privileged?: boolean;
|
|
51
|
+
cap_add?: string[];
|
|
52
|
+
cap_drop?: string[];
|
|
53
|
+
secrets?: string[];
|
|
54
|
+
labels?: Record<string, string> | string[];
|
|
55
|
+
}
|
|
56
|
+
export interface ComposeFile {
|
|
57
|
+
version?: string;
|
|
58
|
+
services: Record<string, ComposeService>;
|
|
59
|
+
networks?: Record<string, unknown>;
|
|
60
|
+
volumes?: Record<string, unknown>;
|
|
61
|
+
secrets?: Record<string, unknown>;
|
|
62
|
+
}
|
|
63
|
+
export type ComposeSeverity = "critical" | "high" | "medium" | "low";
|
|
64
|
+
export type ComposeCategory = "image" | "security" | "reliability" | "resources" | "network";
|
|
65
|
+
export interface ComposeIssue {
|
|
66
|
+
service: string;
|
|
67
|
+
severity: ComposeSeverity;
|
|
68
|
+
category: ComposeCategory;
|
|
69
|
+
title: string;
|
|
70
|
+
detail: string;
|
|
71
|
+
fix?: string;
|
|
72
|
+
}
|
|
73
|
+
export interface ImageAnalysis {
|
|
74
|
+
unpinnedImages: {
|
|
75
|
+
service: string;
|
|
76
|
+
image: string;
|
|
77
|
+
recommendation: string;
|
|
78
|
+
}[];
|
|
79
|
+
latestTagImages: {
|
|
80
|
+
service: string;
|
|
81
|
+
image: string;
|
|
82
|
+
}[];
|
|
83
|
+
issues: ComposeIssue[];
|
|
84
|
+
}
|
|
85
|
+
export interface SecurityAnalysis {
|
|
86
|
+
secretsInEnv: {
|
|
87
|
+
service: string;
|
|
88
|
+
variable: string;
|
|
89
|
+
pattern: string;
|
|
90
|
+
}[];
|
|
91
|
+
privilegedServices: {
|
|
92
|
+
service: string;
|
|
93
|
+
}[];
|
|
94
|
+
exposedDatabases: {
|
|
95
|
+
service: string;
|
|
96
|
+
image: string;
|
|
97
|
+
port: string;
|
|
98
|
+
bindAddr: string;
|
|
99
|
+
}[];
|
|
100
|
+
issues: ComposeIssue[];
|
|
101
|
+
}
|
|
102
|
+
export interface ReliabilityAnalysis {
|
|
103
|
+
missingHealthcheck: {
|
|
104
|
+
service: string;
|
|
105
|
+
}[];
|
|
106
|
+
unsafeDepends: {
|
|
107
|
+
service: string;
|
|
108
|
+
dependency: string;
|
|
109
|
+
condition: string;
|
|
110
|
+
}[];
|
|
111
|
+
missingRestart: {
|
|
112
|
+
service: string;
|
|
113
|
+
}[];
|
|
114
|
+
insecureRestartPolicy: {
|
|
115
|
+
service: string;
|
|
116
|
+
policy: string;
|
|
117
|
+
}[];
|
|
118
|
+
issues: ComposeIssue[];
|
|
119
|
+
}
|
|
120
|
+
export interface ResourceAnalysis {
|
|
121
|
+
missingLimits: {
|
|
122
|
+
service: string;
|
|
123
|
+
missing: string[];
|
|
124
|
+
}[];
|
|
125
|
+
issues: ComposeIssue[];
|
|
126
|
+
}
|
|
127
|
+
export interface NetworkAnalysis {
|
|
128
|
+
servicesOnDefaultNetwork: {
|
|
129
|
+
service: string;
|
|
130
|
+
}[];
|
|
131
|
+
exposedInternalPorts: {
|
|
132
|
+
service: string;
|
|
133
|
+
port: string;
|
|
134
|
+
note: string;
|
|
135
|
+
}[];
|
|
136
|
+
issues: ComposeIssue[];
|
|
137
|
+
}
|
|
138
|
+
export interface ComposeMetrics {
|
|
139
|
+
totalServices: number;
|
|
140
|
+
servicesWithBuild: number;
|
|
141
|
+
namedVolumes: number;
|
|
142
|
+
totalIssues: number;
|
|
143
|
+
criticalIssues: number;
|
|
144
|
+
highIssues: number;
|
|
145
|
+
}
|
|
146
|
+
export interface Recommendation {
|
|
147
|
+
priority: ComposeSeverity;
|
|
148
|
+
service: string;
|
|
149
|
+
message: string;
|
|
150
|
+
fix?: string;
|
|
151
|
+
}
|
|
152
|
+
export interface DockerRuntimeService {
|
|
153
|
+
service: string;
|
|
154
|
+
containerName?: string;
|
|
155
|
+
image?: string;
|
|
156
|
+
state?: string;
|
|
157
|
+
status?: string;
|
|
158
|
+
health?: string;
|
|
159
|
+
}
|
|
160
|
+
export interface DockerRuntimeSummary {
|
|
161
|
+
available: boolean;
|
|
162
|
+
error?: string;
|
|
163
|
+
services: DockerRuntimeService[];
|
|
164
|
+
}
|
|
165
|
+
export interface FullComposeReport {
|
|
166
|
+
generatedAt: Date;
|
|
167
|
+
composeFile: string;
|
|
168
|
+
version?: string;
|
|
169
|
+
healthScore: number;
|
|
170
|
+
metrics: ComposeMetrics;
|
|
171
|
+
images: ImageAnalysis;
|
|
172
|
+
security: SecurityAnalysis;
|
|
173
|
+
reliability: ReliabilityAnalysis;
|
|
174
|
+
resources: ResourceAnalysis;
|
|
175
|
+
networks: NetworkAnalysis;
|
|
176
|
+
allIssues: ComposeIssue[];
|
|
177
|
+
recommendations: Recommendation[];
|
|
178
|
+
dockerRuntime?: DockerRuntimeSummary;
|
|
179
|
+
}
|
|
180
|
+
export interface NormalizedPort {
|
|
181
|
+
raw: string;
|
|
182
|
+
hostIp?: string;
|
|
183
|
+
published?: string;
|
|
184
|
+
target: string;
|
|
185
|
+
protocol: string;
|
|
186
|
+
exposesToAllInterfaces: boolean;
|
|
187
|
+
randomHostBinding: boolean;
|
|
188
|
+
}
|
|
189
|
+
export interface AnalyzerOptions {
|
|
190
|
+
outputDir?: string;
|
|
191
|
+
withDocker?: boolean;
|
|
192
|
+
quiet?: boolean;
|
|
193
|
+
html?: boolean;
|
|
194
|
+
}
|
|
195
|
+
export interface AnalyzerConfig {
|
|
196
|
+
output?: string;
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,KAAK,CAAC,EAAE,KAAK,CACV,MAAM,GACN;QACA,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACjB,CACH,CAAC;IACF,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,EAAE,CAAC;IACvD,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,OAAO,CAAC,EAAE,KAAK,CACZ,MAAM,GACN;QACA,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACf,CACH,CAAC;IACF,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9C,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/D,WAAW,CAAC,EAAE;QACb,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,OAAO,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;IACF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE;QACR,SAAS,CAAC,EAAE;YACX,MAAM,CAAC,EAAE;gBAAE,IAAI,CAAC,EAAE,MAAM,CAAC;gBAAC,MAAM,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;YAC5C,YAAY,CAAC,EAAE;gBAAE,IAAI,CAAC,EAAE,MAAM,CAAC;gBAAC,MAAM,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;SAClD,CAAC;KACF,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,WAAW;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AACrE,MAAM,MAAM,eAAe,GACxB,OAAO,GACP,UAAU,GACV,aAAa,GACb,WAAW,GACX,SAAS,CAAC;AAEb,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,EAAE,eAAe,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,aAAa;IAC7B,cAAc,EAAE;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,cAAc,EAAE,MAAM,CAAC;KACvB,EAAE,CAAC;IACJ,eAAe,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtD,MAAM,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAChC,YAAY,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACvE,kBAAkB,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1C,gBAAgB,EAAE;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;KACjB,EAAE,CAAC;IACJ,MAAM,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IACnC,kBAAkB,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1C,aAAa,EAAE;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;KAClB,EAAE,CAAC;IACJ,cAAc,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtC,qBAAqB,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7D,MAAM,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAChC,aAAa,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IACxD,MAAM,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC/B,wBAAwB,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAChD,oBAAoB,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxE,MAAM,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,eAAe,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,oBAAoB,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,iBAAiB;IACjC,WAAW,EAAE,IAAI,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,WAAW,EAAE,mBAAmB,CAAC;IACjC,SAAS,EAAE,gBAAgB,CAAC;IAC5B,QAAQ,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,aAAa,CAAC,EAAE,oBAAoB,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,sBAAsB,EAAE,OAAO,CAAC;IAChC,iBAAiB,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function formatBytes(bytes: number): string;
|
|
2
|
+
export declare function formatDuration(ms: number): string;
|
|
3
|
+
export declare function formatMs(ms: number): string;
|
|
4
|
+
export declare function formatNumber(value: number): string;
|
|
5
|
+
export declare function formatPercent(value: number, decimals?: number): string;
|
|
6
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/utils/format.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKjD;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAIjD;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,MAAM,CAEjE"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatBytes = formatBytes;
|
|
4
|
+
exports.formatDuration = formatDuration;
|
|
5
|
+
exports.formatMs = formatMs;
|
|
6
|
+
exports.formatNumber = formatNumber;
|
|
7
|
+
exports.formatPercent = formatPercent;
|
|
8
|
+
function formatBytes(bytes) {
|
|
9
|
+
if (bytes < 1024)
|
|
10
|
+
return `${bytes} B`;
|
|
11
|
+
if (bytes < 1_048_576)
|
|
12
|
+
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
13
|
+
if (bytes < 1_073_741_824)
|
|
14
|
+
return `${(bytes / 1_048_576).toFixed(2)} MB`;
|
|
15
|
+
return `${(bytes / 1_073_741_824).toFixed(2)} GB`;
|
|
16
|
+
}
|
|
17
|
+
function formatDuration(ms) {
|
|
18
|
+
if (ms < 1000)
|
|
19
|
+
return `${ms.toFixed(0)}ms`;
|
|
20
|
+
if (ms < 60_000)
|
|
21
|
+
return `${(ms / 1000).toFixed(2)}s`;
|
|
22
|
+
return `${(ms / 60_000).toFixed(2)}m`;
|
|
23
|
+
}
|
|
24
|
+
function formatMs(ms) {
|
|
25
|
+
return formatDuration(ms);
|
|
26
|
+
}
|
|
27
|
+
function formatNumber(value) {
|
|
28
|
+
return value.toLocaleString("en-US");
|
|
29
|
+
}
|
|
30
|
+
function formatPercent(value, decimals = 2) {
|
|
31
|
+
return `${value.toFixed(decimals)}%`;
|
|
32
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function printSection(title: string): void;
|
|
2
|
+
export declare function printRow(label: string, value: string | number): void;
|
|
3
|
+
export declare function printBullet(line: string): void;
|
|
4
|
+
export declare function printSubBullet(line: string): void;
|
|
5
|
+
export declare function printSeparator(): void;
|
|
6
|
+
export declare function healthEmoji(score: number): string;
|
|
7
|
+
export declare function healthLabel(score: number): string;
|
|
8
|
+
//# sourceMappingURL=print.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"print.d.ts","sourceRoot":"","sources":["../../../src/utils/print.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAEhD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEpE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKjD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKjD"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printSection = printSection;
|
|
4
|
+
exports.printRow = printRow;
|
|
5
|
+
exports.printBullet = printBullet;
|
|
6
|
+
exports.printSubBullet = printSubBullet;
|
|
7
|
+
exports.printSeparator = printSeparator;
|
|
8
|
+
exports.healthEmoji = healthEmoji;
|
|
9
|
+
exports.healthLabel = healthLabel;
|
|
10
|
+
function printSection(title) {
|
|
11
|
+
console.log(`\n━━━ ${title} ━━━`);
|
|
12
|
+
}
|
|
13
|
+
function printRow(label, value) {
|
|
14
|
+
console.log(` ${label}: ${value}`);
|
|
15
|
+
}
|
|
16
|
+
function printBullet(line) {
|
|
17
|
+
console.log(` • ${line}`);
|
|
18
|
+
}
|
|
19
|
+
function printSubBullet(line) {
|
|
20
|
+
console.log(` ${line}`);
|
|
21
|
+
}
|
|
22
|
+
function printSeparator() {
|
|
23
|
+
console.log("");
|
|
24
|
+
}
|
|
25
|
+
function healthEmoji(score) {
|
|
26
|
+
if (score >= 90)
|
|
27
|
+
return "🟢";
|
|
28
|
+
if (score >= 70)
|
|
29
|
+
return "🟡";
|
|
30
|
+
if (score >= 50)
|
|
31
|
+
return "🟠";
|
|
32
|
+
return "🔴";
|
|
33
|
+
}
|
|
34
|
+
function healthLabel(score) {
|
|
35
|
+
if (score >= 90)
|
|
36
|
+
return "Excellent";
|
|
37
|
+
if (score >= 70)
|
|
38
|
+
return "Good";
|
|
39
|
+
if (score >= 50)
|
|
40
|
+
return "Warning";
|
|
41
|
+
return "Critical";
|
|
42
|
+
}
|