@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.
- package/CHANGELOG.md +31 -0
- package/LICENSE +21 -0
- package/README.md +244 -0
- package/analyzerrc.example.json +17 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +137 -0
- package/dist/src/analyzers/memory-analyzer.d.ts +5 -0
- package/dist/src/analyzers/memory-analyzer.d.ts.map +1 -0
- package/dist/src/analyzers/memory-analyzer.js +54 -0
- package/dist/src/analyzers/performance-analyzer.d.ts +6 -0
- package/dist/src/analyzers/performance-analyzer.d.ts.map +1 -0
- package/dist/src/analyzers/performance-analyzer.js +78 -0
- package/dist/src/analyzers/persistence-analyzer.d.ts +5 -0
- package/dist/src/analyzers/persistence-analyzer.d.ts.map +1 -0
- package/dist/src/analyzers/persistence-analyzer.js +59 -0
- package/dist/src/analyzers/replication-analyzer.d.ts +5 -0
- package/dist/src/analyzers/replication-analyzer.d.ts.map +1 -0
- package/dist/src/analyzers/replication-analyzer.js +52 -0
- package/dist/src/cli/options.d.ts +24 -0
- package/dist/src/cli/options.d.ts.map +1 -0
- package/dist/src/cli/options.js +155 -0
- package/dist/src/cli/runner.d.ts +13 -0
- package/dist/src/cli/runner.d.ts.map +1 -0
- package/dist/src/cli/runner.js +214 -0
- package/dist/src/collectors/stats-collector.d.ts +15 -0
- package/dist/src/collectors/stats-collector.d.ts.map +1 -0
- package/dist/src/collectors/stats-collector.js +151 -0
- package/dist/src/config/loader.d.ts +13 -0
- package/dist/src/config/loader.d.ts.map +1 -0
- package/dist/src/config/loader.js +63 -0
- package/dist/src/constants.d.ts +52 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +93 -0
- package/dist/src/health.d.ts +10 -0
- package/dist/src/health.d.ts.map +1 -0
- package/dist/src/health.js +100 -0
- package/dist/src/interactive/display.d.ts +13 -0
- package/dist/src/interactive/display.d.ts.map +1 -0
- package/dist/src/interactive/display.js +130 -0
- package/dist/src/interactive/index.d.ts +23 -0
- package/dist/src/interactive/index.d.ts.map +1 -0
- package/dist/src/interactive/index.js +236 -0
- package/dist/src/interactive/menus.d.ts +25 -0
- package/dist/src/interactive/menus.d.ts.map +1 -0
- package/dist/src/interactive/menus.js +49 -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 +96 -0
- package/dist/src/reporters/html-reporter.d.ts +9 -0
- package/dist/src/reporters/html-reporter.d.ts.map +1 -0
- package/dist/src/reporters/html-reporter.js +140 -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 +239 -0
- package/dist/src/types.d.ts +184 -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/dist/src/watch/runner.d.ts +8 -0
- package/dist/src/watch/runner.d.ts.map +1 -0
- package/dist/src/watch/runner.js +49 -0
- package/dist/tests/analysis-and-reports.test.d.ts +2 -0
- package/dist/tests/analysis-and-reports.test.d.ts.map +1 -0
- package/dist/tests/analysis-and-reports.test.js +172 -0
- package/dist/tests/collector-and-options.test.d.ts +2 -0
- package/dist/tests/collector-and-options.test.d.ts.map +1 -0
- package/dist/tests/collector-and-options.test.js +110 -0
- 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":"AAAA,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,SAAS;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,0BAA0B,EAAE,MAAM,CAAC;IACnC,2BAA2B,EAAE,MAAM,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IACjC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uBAAuB,EAAE,MAAM,CAAC;IAChC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,IAAI,GAAG,KAAK,CAAC;IAClC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE,OAAO,CAAC;IACpB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,sBAAsB,EAAE,IAAI,GAAG,KAAK,CAAC;IACrC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,IAAI,GAAG,SAAS,GAAG,UAAU,CAAC;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,IAAI,GAAG,SAAS,GAAG,UAAU,CAAC;IAChD,eAAe,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IACnC,GAAG,EAAE;QACJ,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,UAAU,EAAE,IAAI,GAAG,KAAK,CAAC;QACzB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,cAAc,EAAE,OAAO,CAAC;KACxB,CAAC;IACF,GAAG,EAAE;QACJ,OAAO,EAAE,OAAO,CAAC;QACjB,UAAU,EAAE,IAAI,GAAG,KAAK,CAAC;QACzB,iBAAiB,EAAE,OAAO,CAAC;QAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,QAAQ,EAAE,IAAI,GAAG,SAAS,GAAG,UAAU,CAAC;IACxC,eAAe,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,YAAY,CAAC;IACxC,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,IAAI,GAAG,SAAS,GAAG,UAAU,CAAC;IACxC,eAAe,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,eAAe,EAAE,kBAAkB,EAAE,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC/B,WAAW,EAAE,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,mBAAmB,CAAC;IACjC,WAAW,EAAE,mBAAmB,CAAC;IACjC,YAAY,EAAE,mBAAmB,CAAC;IAClC,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,eAAe,EAAE,cAAc,EAAE,CAAC;CAClC;AAED,MAAM,MAAM,UAAU,GAAG,eAAe,CAAC;AAEzC,MAAM,WAAW,eAAe;IAC/B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB"}
|
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"file":"analysis-and-reports.test.d.ts","sourceRoot":"","sources":["../../tests/analysis-and-reports.test.ts"],"names":[],"mappings":""}
|