@deniscuciuc/redis-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.
Files changed (74) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/LICENSE +21 -0
  3. package/README.md +244 -0
  4. package/analyzerrc.example.json +17 -0
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +137 -0
  8. package/dist/src/analyzers/memory-analyzer.d.ts +5 -0
  9. package/dist/src/analyzers/memory-analyzer.d.ts.map +1 -0
  10. package/dist/src/analyzers/memory-analyzer.js +54 -0
  11. package/dist/src/analyzers/performance-analyzer.d.ts +6 -0
  12. package/dist/src/analyzers/performance-analyzer.d.ts.map +1 -0
  13. package/dist/src/analyzers/performance-analyzer.js +78 -0
  14. package/dist/src/analyzers/persistence-analyzer.d.ts +5 -0
  15. package/dist/src/analyzers/persistence-analyzer.d.ts.map +1 -0
  16. package/dist/src/analyzers/persistence-analyzer.js +59 -0
  17. package/dist/src/analyzers/replication-analyzer.d.ts +5 -0
  18. package/dist/src/analyzers/replication-analyzer.d.ts.map +1 -0
  19. package/dist/src/analyzers/replication-analyzer.js +52 -0
  20. package/dist/src/cli/options.d.ts +24 -0
  21. package/dist/src/cli/options.d.ts.map +1 -0
  22. package/dist/src/cli/options.js +155 -0
  23. package/dist/src/cli/runner.d.ts +13 -0
  24. package/dist/src/cli/runner.d.ts.map +1 -0
  25. package/dist/src/cli/runner.js +214 -0
  26. package/dist/src/collectors/stats-collector.d.ts +15 -0
  27. package/dist/src/collectors/stats-collector.d.ts.map +1 -0
  28. package/dist/src/collectors/stats-collector.js +151 -0
  29. package/dist/src/config/loader.d.ts +13 -0
  30. package/dist/src/config/loader.d.ts.map +1 -0
  31. package/dist/src/config/loader.js +63 -0
  32. package/dist/src/constants.d.ts +52 -0
  33. package/dist/src/constants.d.ts.map +1 -0
  34. package/dist/src/constants.js +93 -0
  35. package/dist/src/health.d.ts +10 -0
  36. package/dist/src/health.d.ts.map +1 -0
  37. package/dist/src/health.js +100 -0
  38. package/dist/src/interactive/display.d.ts +13 -0
  39. package/dist/src/interactive/display.d.ts.map +1 -0
  40. package/dist/src/interactive/display.js +130 -0
  41. package/dist/src/interactive/index.d.ts +23 -0
  42. package/dist/src/interactive/index.d.ts.map +1 -0
  43. package/dist/src/interactive/index.js +236 -0
  44. package/dist/src/interactive/menus.d.ts +25 -0
  45. package/dist/src/interactive/menus.d.ts.map +1 -0
  46. package/dist/src/interactive/menus.js +49 -0
  47. package/dist/src/reporters/diff-reporter.d.ts +21 -0
  48. package/dist/src/reporters/diff-reporter.d.ts.map +1 -0
  49. package/dist/src/reporters/diff-reporter.js +96 -0
  50. package/dist/src/reporters/html-reporter.d.ts +9 -0
  51. package/dist/src/reporters/html-reporter.d.ts.map +1 -0
  52. package/dist/src/reporters/html-reporter.js +140 -0
  53. package/dist/src/reporters/report-generator.d.ts +23 -0
  54. package/dist/src/reporters/report-generator.d.ts.map +1 -0
  55. package/dist/src/reporters/report-generator.js +239 -0
  56. package/dist/src/types.d.ts +184 -0
  57. package/dist/src/types.d.ts.map +1 -0
  58. package/dist/src/types.js +2 -0
  59. package/dist/src/utils/format.d.ts +6 -0
  60. package/dist/src/utils/format.d.ts.map +1 -0
  61. package/dist/src/utils/format.js +32 -0
  62. package/dist/src/utils/print.d.ts +8 -0
  63. package/dist/src/utils/print.d.ts.map +1 -0
  64. package/dist/src/utils/print.js +42 -0
  65. package/dist/src/watch/runner.d.ts +8 -0
  66. package/dist/src/watch/runner.d.ts.map +1 -0
  67. package/dist/src/watch/runner.js +49 -0
  68. package/dist/tests/analysis-and-reports.test.d.ts +2 -0
  69. package/dist/tests/analysis-and-reports.test.d.ts.map +1 -0
  70. package/dist/tests/analysis-and-reports.test.js +172 -0
  71. package/dist/tests/collector-and-options.test.d.ts +2 -0
  72. package/dist/tests/collector-and-options.test.d.ts.map +1 -0
  73. package/dist/tests/collector-and-options.test.js +110 -0
  74. package/package.json +82 -0
@@ -0,0 +1,239 @@
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 format_1 = require("../utils/format");
40
+ const print_1 = require("../utils/print");
41
+ const html_reporter_1 = require("./html-reporter");
42
+ class ReportGenerator {
43
+ outputDir;
44
+ constructor(outputDir = "./reports", _options = {}) {
45
+ this.outputDir = outputDir;
46
+ void _options;
47
+ }
48
+ async generateFullReport(report, timestamp) {
49
+ const ts = timestamp ?? new Date().toISOString().replace(/[:.]/g, "-");
50
+ const filepath = path.join(this.outputDir, `redis-analysis-${ts}.md`);
51
+ await this.ensureOutputDir();
52
+ fs.writeFileSync(filepath, this.buildMarkdownReport(report));
53
+ return filepath;
54
+ }
55
+ async generateJsonReport(report, timestamp) {
56
+ const ts = timestamp ?? new Date().toISOString().replace(/[:.]/g, "-");
57
+ const filepath = path.join(this.outputDir, `redis-analysis-${ts}.json`);
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 filepath = path.join(this.outputDir, `redis-analysis-${ts}.html`);
65
+ await this.ensureOutputDir();
66
+ fs.writeFileSync(filepath, html_reporter_1.HtmlReporter.generate(report));
67
+ return filepath;
68
+ }
69
+ printSummary(report) {
70
+ (0, print_1.printRow)("Health score", `${report.healthScore}/100 ${(0, print_1.healthEmoji)(report.healthScore)} ${(0, print_1.healthLabel)(report.healthScore)}`);
71
+ (0, print_1.printRow)("Redis version", report.metrics.version);
72
+ (0, print_1.printRow)("Mode", report.metrics.mode);
73
+ (0, print_1.printRow)("Used memory", report.memory.usedMemoryHuman);
74
+ (0, print_1.printRow)("Hit rate", (0, format_1.formatPercent)(report.hitRate.hitRate));
75
+ (0, print_1.printRow)("Slow log length", report.slowCommands.totalLogged);
76
+ (0, print_1.printSeparator)();
77
+ for (const recommendation of report.recommendations.slice(0, 5)) {
78
+ (0, print_1.printBullet)(`[${recommendation.priority}] ${recommendation.message}`);
79
+ }
80
+ }
81
+ async ensureOutputDir() {
82
+ if (!fs.existsSync(this.outputDir)) {
83
+ fs.mkdirSync(this.outputDir, { recursive: true });
84
+ }
85
+ }
86
+ buildMarkdownReport(report) {
87
+ return [
88
+ this.buildHeader(report),
89
+ this.buildExecutiveSummary(report),
90
+ this.buildMetricsSection(report),
91
+ this.buildMemorySection(report),
92
+ this.buildHitRateSection(report),
93
+ this.buildPersistenceSection(report),
94
+ this.buildReplicationSection(report),
95
+ this.buildSlowCommandsSection(report),
96
+ this.buildKeyspacesSection(report.keyspaces),
97
+ this.buildConfigSection(report.config),
98
+ this.buildRecommendationsSection(report.recommendations),
99
+ ].join("\n\n");
100
+ }
101
+ buildHeader(report) {
102
+ return `# Redis Analysis Report
103
+
104
+ **Target:** ${report.host}:${report.port}/${report.db}
105
+ **Generated:** ${report.generatedAt.toISOString()}
106
+ **Tool:** Redis Analyzer
107
+
108
+ ---`;
109
+ }
110
+ buildExecutiveSummary(report) {
111
+ const issues = report.recommendations
112
+ .slice(0, 5)
113
+ .map((item) => `- **${item.priority}** ${item.message}`);
114
+ return `## Executive Summary
115
+
116
+ ### Health Score: ${report.healthScore}/100 ${(0, print_1.healthEmoji)(report.healthScore)}
117
+
118
+ ### Key Findings
119
+ ${issues.length > 0 ? issues.join("\n") : "- No critical issues found"}
120
+
121
+ ### Quick Stats
122
+ | Metric | Value |
123
+ |--------|-------|
124
+ | Redis Version | ${report.metrics.version} |
125
+ | Used Memory | ${report.memory.usedMemoryHuman} |
126
+ | Hit Rate | ${(0, format_1.formatPercent)(report.hitRate.hitRate)} |
127
+ | Ops / sec | ${(0, format_1.formatNumber)(report.metrics.opsPerSec)} |
128
+ | Total Keys | ${(0, format_1.formatNumber)(report.metrics.totalKeyCount)} |`;
129
+ }
130
+ buildMetricsSection(report) {
131
+ return `## Core Metrics
132
+
133
+ | Metric | Value |
134
+ |--------|-------|
135
+ | Mode | ${report.metrics.mode} |
136
+ | Uptime | ${report.metrics.uptimeDays} days |
137
+ | Connected Clients | ${report.metrics.connectedClients} |
138
+ | Blocked Clients | ${report.metrics.blockedClients} |
139
+ | Max Clients | ${report.metrics.maxClients} |
140
+ | Ops / sec | ${(0, format_1.formatNumber)(report.metrics.opsPerSec)} |
141
+ | Total Keys | ${(0, format_1.formatNumber)(report.metrics.totalKeyCount)} |
142
+ | Keyspaces | ${report.metrics.keyspacesCount} |
143
+ | Rejected Connections | ${(0, format_1.formatNumber)(report.metrics.rejectedConnections)} |`;
144
+ }
145
+ buildMemorySection(report) {
146
+ return `## Memory
147
+
148
+ | Metric | Value |
149
+ |--------|-------|
150
+ | Used Memory | ${report.memory.usedMemoryHuman} |
151
+ | Max Memory | ${report.memory.maxMemoryHuman || "unlimited"} |
152
+ | Usage Percent | ${(0, format_1.formatPercent)(report.memory.usagePercent)} |
153
+ | Fragmentation Ratio | ${report.memory.fragRatio.toFixed(2)} |
154
+ | Fragmentation Waste | ${(0, format_1.formatBytes)(report.memory.fragBytes)} |
155
+ | RSS Overhead | ${(0, format_1.formatBytes)(report.memory.rssOverhead)} |
156
+ | Peak Memory | ${(0, format_1.formatBytes)(report.memory.peakMemory)} |
157
+ | Dataset Memory | ${(0, format_1.formatBytes)(report.memory.dataMemory)} |
158
+ | Overhead Memory | ${(0, format_1.formatBytes)(report.memory.overheadMemory)} |`;
159
+ }
160
+ buildHitRateSection(report) {
161
+ return `## Cache Hit Rate
162
+
163
+ | Metric | Value |
164
+ |--------|-------|
165
+ | Hits | ${(0, format_1.formatNumber)(report.hitRate.hits)} |
166
+ | Misses | ${(0, format_1.formatNumber)(report.hitRate.misses)} |
167
+ | Hit Rate | ${(0, format_1.formatPercent)(report.hitRate.hitRate)} |
168
+ | Evicted Keys | ${(0, format_1.formatNumber)(report.hitRate.evictedKeys)} |
169
+ | Expired Keys | ${(0, format_1.formatNumber)(report.hitRate.expiredKeys)} |
170
+ | Eviction Policy | ${report.hitRate.evictionPolicy} |`;
171
+ }
172
+ buildPersistenceSection(report) {
173
+ return `## Persistence
174
+
175
+ | Metric | Value |
176
+ |--------|-------|
177
+ | RDB Enabled | ${report.persistence.rdb.enabled ? "yes" : "no"} |
178
+ | Seconds Since Last Save | ${(0, format_1.formatNumber)(report.persistence.rdb.secondsSinceLastSave)} |
179
+ | RDB Status | ${report.persistence.rdb.lastStatus} |
180
+ | RDB Changes Since Save | ${(0, format_1.formatNumber)(report.persistence.rdb.changesSinceLastSave)} |
181
+ | AOF Enabled | ${report.persistence.aof.enabled ? "yes" : "no"} |
182
+ | AOF Status | ${report.persistence.aof.lastStatus} |
183
+ | AOF Rewrite In Progress | ${report.persistence.aof.rewriteInProgress ? "yes" : "no"} |`;
184
+ }
185
+ buildReplicationSection(report) {
186
+ return `## Replication
187
+
188
+ | Metric | Value |
189
+ |--------|-------|
190
+ | Role | ${report.replication.role} |
191
+ | Connected Replicas | ${report.replication.connectedSlaves} |
192
+ | Link Status | ${report.replication.linkStatus ?? "n/a"} |
193
+ | Lag Seconds | ${report.replication.lagSeconds ?? "n/a"} |
194
+ | Sync In Progress | ${report.replication.syncInProgress ? "yes" : "no"} |`;
195
+ }
196
+ buildSlowCommandsSection(report) {
197
+ const topCommands = report.slowCommands.topCommandTypes
198
+ .map((command) => `| ${command.command} | ${(0, format_1.formatNumber)(command.count)} | ${(0, format_1.formatDuration)(command.avgMs)} |`)
199
+ .join("\n");
200
+ return `## Slow Commands
201
+
202
+ | Metric | Value |
203
+ |--------|-------|
204
+ | Logged Slow Commands | ${(0, format_1.formatNumber)(report.slowCommands.totalLogged)} |
205
+ | Threshold | ${(0, format_1.formatDuration)(report.slowCommands.threshold / 1000)} |
206
+
207
+ ### Top Slow Command Types
208
+ | Command | Count | Avg Duration |
209
+ |---------|-------|--------------|
210
+ ${topCommands || "| none | 0 | 0ms |"}`;
211
+ }
212
+ buildKeyspacesSection(keyspaces) {
213
+ const rows = keyspaces
214
+ .map((keyspace) => `| db${keyspace.db} | ${(0, format_1.formatNumber)(keyspace.keys)} | ${(0, format_1.formatNumber)(keyspace.expires)} | ${(0, format_1.formatDuration)(keyspace.avgTtl)} |`)
215
+ .join("\n");
216
+ return `## Keyspaces
217
+
218
+ | DB | Keys | Expiring Keys | Avg TTL |
219
+ |----|------|---------------|---------|
220
+ ${rows || "| none | 0 | 0 | 0ms |"}`;
221
+ }
222
+ buildConfigSection(config) {
223
+ const rows = Object.entries(config)
224
+ .map(([key, value]) => `| ${key} | ${value} |`)
225
+ .join("\n");
226
+ return `## Important Configuration
227
+
228
+ | Setting | Value |
229
+ |---------|-------|
230
+ ${rows || "| none | n/a |"}`;
231
+ }
232
+ buildRecommendationsSection(recommendations) {
233
+ const items = recommendations.map((item) => `- **${item.priority}** \`${item.category}\` — ${item.message}`);
234
+ return `## Recommendations
235
+
236
+ ${items.length > 0 ? items.join("\n") : "- No recommendations"}`;
237
+ }
238
+ }
239
+ exports.ReportGenerator = ReportGenerator;
@@ -0,0 +1,184 @@
1
+ export interface RedisConnection {
2
+ host: string;
3
+ port: number;
4
+ password?: string;
5
+ db: number;
6
+ tls: boolean;
7
+ uri?: string;
8
+ }
9
+ export interface RedisInfo {
10
+ redisVersion: string;
11
+ redisMode: string;
12
+ os: string;
13
+ uptimeInSeconds: number;
14
+ uptimeInDays: number;
15
+ tcpPort: number;
16
+ executablePath: string;
17
+ configFile: string;
18
+ connectedClients: number;
19
+ blockedClients: number;
20
+ maxClients: number;
21
+ clientRecentMaxInputBuffer: number;
22
+ clientRecentMaxOutputBuffer: number;
23
+ usedMemory: number;
24
+ usedMemoryHuman: string;
25
+ usedMemoryRss: number;
26
+ usedMemoryRssHuman: string;
27
+ usedMemoryPeak: number;
28
+ usedMemoryPeakHuman: string;
29
+ usedMemoryPeakPerc: number;
30
+ usedMemoryOverhead: number;
31
+ usedMemoryDataset: number;
32
+ memFragmentationRatio: number;
33
+ memFragmentationBytes: number;
34
+ maxmemory: number;
35
+ maxmemoryHuman: string;
36
+ maxmemoryPolicy: string;
37
+ totalCommandsProcessed: number;
38
+ instantaneousOpsPerSec: number;
39
+ totalNetInputBytes: number;
40
+ totalNetOutputBytes: number;
41
+ rejectedConnections: number;
42
+ expiredKeys: number;
43
+ evictedKeys: number;
44
+ keyspaceHits: number;
45
+ keyspaceMisses: number;
46
+ role: "master" | "slave";
47
+ connectedSlaves: number;
48
+ masterLinkStatus?: "up" | "down";
49
+ masterLastIoSecondsAgo?: number;
50
+ masterSyncInProgress?: boolean;
51
+ masterReplOffset?: number;
52
+ slaveReplOffset?: number;
53
+ rdbChangesSinceLastSave: number;
54
+ rdbBgsaveInProgress: boolean;
55
+ rdbLastSaveTime: number;
56
+ rdbLastBgsaveStatus: "ok" | "err";
57
+ rdbLastBgsaveTimeSec: number;
58
+ aofEnabled: boolean;
59
+ aofRewriteInProgress: boolean;
60
+ aofLastRewriteTimeSec: number;
61
+ aofLastBgrewriteStatus: "ok" | "err";
62
+ aofCurrentSize?: number;
63
+ keyspaces: KeyspaceInfo[];
64
+ }
65
+ export interface KeyspaceInfo {
66
+ db: number;
67
+ keys: number;
68
+ expires: number;
69
+ avgTtl: number;
70
+ }
71
+ export interface SlowCommand {
72
+ id: number;
73
+ timestamp: number;
74
+ durationMicros: number;
75
+ durationMs: number;
76
+ command: string[];
77
+ commandPreview: string;
78
+ clientAddr?: string;
79
+ }
80
+ export interface MemoryAnalysis {
81
+ usedMemory: number;
82
+ usedMemoryHuman: string;
83
+ maxMemory: number;
84
+ maxMemoryHuman: string;
85
+ usagePercent: number;
86
+ fragRatio: number;
87
+ fragBytes: number;
88
+ fragSeverity: "ok" | "warning" | "critical";
89
+ rssOverhead: number;
90
+ peakMemory: number;
91
+ dataMemory: number;
92
+ overheadMemory: number;
93
+ recommendations: string[];
94
+ }
95
+ export interface HitRateAnalysis {
96
+ hits: number;
97
+ misses: number;
98
+ hitRate: number;
99
+ evictedKeys: number;
100
+ expiredKeys: number;
101
+ evictionPolicy: string;
102
+ evictionSeverity: "ok" | "warning" | "critical";
103
+ recommendations: string[];
104
+ }
105
+ export interface PersistenceAnalysis {
106
+ rdb: {
107
+ enabled: boolean;
108
+ lastSaveTime: number;
109
+ secondsSinceLastSave: number;
110
+ lastStatus: "ok" | "err";
111
+ changesSinceLastSave: number;
112
+ saveInProgress: boolean;
113
+ };
114
+ aof: {
115
+ enabled: boolean;
116
+ lastStatus: "ok" | "err";
117
+ rewriteInProgress: boolean;
118
+ currentSizeBytes?: number;
119
+ };
120
+ severity: "ok" | "warning" | "critical";
121
+ recommendations: string[];
122
+ }
123
+ export interface ReplicationAnalysis {
124
+ role: "master" | "slave" | "standalone";
125
+ connectedSlaves: number;
126
+ linkStatus?: "up" | "down";
127
+ lagSeconds?: number;
128
+ syncInProgress?: boolean;
129
+ severity: "ok" | "warning" | "critical";
130
+ recommendations: string[];
131
+ }
132
+ export interface SlowCommandSummary {
133
+ command: string;
134
+ count: number;
135
+ avgMs: number;
136
+ }
137
+ export interface SlowCommandAnalysis {
138
+ totalLogged: number;
139
+ commands: SlowCommand[];
140
+ topCommandTypes: SlowCommandSummary[];
141
+ threshold: number;
142
+ recommendations: string[];
143
+ }
144
+ export interface RedisMetrics {
145
+ version: string;
146
+ mode: string;
147
+ uptimeDays: number;
148
+ connectedClients: number;
149
+ blockedClients: number;
150
+ maxClients: number;
151
+ opsPerSec: number;
152
+ totalKeyCount: number;
153
+ keyspacesCount: number;
154
+ rejectedConnections: number;
155
+ }
156
+ export interface Recommendation {
157
+ priority: "critical" | "high" | "medium" | "low";
158
+ category: string;
159
+ message: string;
160
+ action?: string;
161
+ }
162
+ export interface FullRedisReport {
163
+ generatedAt: Date;
164
+ host: string;
165
+ port: number;
166
+ db: number;
167
+ healthScore: number;
168
+ metrics: RedisMetrics;
169
+ memory: MemoryAnalysis;
170
+ hitRate: HitRateAnalysis;
171
+ persistence: PersistenceAnalysis;
172
+ replication: ReplicationAnalysis;
173
+ slowCommands: SlowCommandAnalysis;
174
+ keyspaces: KeyspaceInfo[];
175
+ config: Record<string, string>;
176
+ recommendations: Recommendation[];
177
+ }
178
+ export type FullReport = FullRedisReport;
179
+ export interface AnalyzerOptions {
180
+ slowCommandThreshold?: number;
181
+ maxSlowCommands?: number;
182
+ outputDir?: string;
183
+ }
184
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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
+ }
@@ -0,0 +1,8 @@
1
+ export interface WatchOptions {
2
+ intervalSeconds: number;
3
+ command: string;
4
+ runCommand: () => Promise<void>;
5
+ }
6
+ export declare function validateWatchCommand(command: string): void;
7
+ export declare function runWatchLoop(options: WatchOptions): Promise<void>;
8
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/watch/runner.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAY1D;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA6CvE"}
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateWatchCommand = validateWatchCommand;
4
+ exports.runWatchLoop = runWatchLoop;
5
+ const constants_1 = require("../constants");
6
+ const CLEAR = "\x1Bc";
7
+ function validateWatchCommand(command) {
8
+ if (constants_1.WATCH_BLOCKED.has(command)) {
9
+ throw new Error(`--watch cannot be used with '${command}' (write operation).`);
10
+ }
11
+ if (!constants_1.WATCH_ALLOWED.has(command)) {
12
+ throw new Error(`--watch is not supported for '${command}'. Supported: ${Array.from(constants_1.WATCH_ALLOWED).join(", ")}`);
13
+ }
14
+ }
15
+ async function runWatchLoop(options) {
16
+ validateWatchCommand(options.command);
17
+ let stopped = false;
18
+ const handleSigInt = () => {
19
+ stopped = true;
20
+ };
21
+ process.on("SIGINT", handleSigInt);
22
+ try {
23
+ while (!stopped) {
24
+ process.stdout.write(CLEAR);
25
+ process.stdout.write(`[watch] command: ${options.command} | interval: ${options.intervalSeconds}s | updated: ${new Date().toLocaleTimeString()} | Ctrl+C to stop\n\n`);
26
+ try {
27
+ await options.runCommand();
28
+ }
29
+ catch (error) {
30
+ const message = error instanceof Error ? error.message : String(error);
31
+ process.stderr.write(`\nWatch iteration failed: ${message}\n`);
32
+ }
33
+ for (let remaining = options.intervalSeconds; remaining > 0; remaining--) {
34
+ if (stopped) {
35
+ break;
36
+ }
37
+ process.stdout.write(`\rNext update in ${remaining}s... `);
38
+ await new Promise((resolve) => setTimeout(resolve, 1000));
39
+ }
40
+ if (!stopped) {
41
+ process.stdout.write("\r");
42
+ }
43
+ }
44
+ }
45
+ finally {
46
+ process.off("SIGINT", handleSigInt);
47
+ process.stdout.write("\nWatch stopped.\n");
48
+ }
49
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=analysis-and-reports.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analysis-and-reports.test.d.ts","sourceRoot":"","sources":["../../tests/analysis-and-reports.test.ts"],"names":[],"mappings":""}