@objectstack/core 0.9.0 → 0.9.2
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/{ENHANCED_FEATURES.md → ADVANCED_FEATURES.md} +13 -13
- package/CHANGELOG.md +15 -0
- package/PHASE2_IMPLEMENTATION.md +388 -0
- package/README.md +60 -11
- package/REFACTORING_SUMMARY.md +40 -0
- package/dist/api-registry-plugin.test.js +20 -20
- package/dist/dependency-resolver.d.ts +62 -0
- package/dist/dependency-resolver.d.ts.map +1 -0
- package/dist/dependency-resolver.js +317 -0
- package/dist/dependency-resolver.test.d.ts +2 -0
- package/dist/dependency-resolver.test.d.ts.map +1 -0
- package/dist/dependency-resolver.test.js +241 -0
- package/dist/health-monitor.d.ts +65 -0
- package/dist/health-monitor.d.ts.map +1 -0
- package/dist/health-monitor.js +269 -0
- package/dist/health-monitor.test.d.ts +2 -0
- package/dist/health-monitor.test.d.ts.map +1 -0
- package/dist/health-monitor.test.js +68 -0
- package/dist/hot-reload.d.ts +79 -0
- package/dist/hot-reload.d.ts.map +1 -0
- package/dist/hot-reload.js +313 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/kernel-base.d.ts +2 -2
- package/dist/kernel-base.js +2 -2
- package/dist/kernel.d.ts +79 -31
- package/dist/kernel.d.ts.map +1 -1
- package/dist/kernel.js +383 -73
- package/dist/kernel.test.js +373 -122
- package/dist/lite-kernel.d.ts +55 -0
- package/dist/lite-kernel.d.ts.map +1 -0
- package/dist/lite-kernel.js +112 -0
- package/dist/lite-kernel.test.d.ts +2 -0
- package/dist/lite-kernel.test.d.ts.map +1 -0
- package/dist/lite-kernel.test.js +161 -0
- package/dist/logger.d.ts +3 -2
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +61 -18
- package/dist/plugin-loader.d.ts +11 -0
- package/dist/plugin-loader.d.ts.map +1 -1
- package/dist/plugin-loader.js +34 -10
- package/dist/plugin-loader.test.js +9 -0
- package/dist/security/index.d.ts +3 -0
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +4 -0
- package/dist/security/permission-manager.d.ts +96 -0
- package/dist/security/permission-manager.d.ts.map +1 -0
- package/dist/security/permission-manager.js +235 -0
- package/dist/security/permission-manager.test.d.ts +2 -0
- package/dist/security/permission-manager.test.d.ts.map +1 -0
- package/dist/security/permission-manager.test.js +220 -0
- package/dist/security/plugin-signature-verifier.js +3 -3
- package/dist/security/sandbox-runtime.d.ts +115 -0
- package/dist/security/sandbox-runtime.d.ts.map +1 -0
- package/dist/security/sandbox-runtime.js +310 -0
- package/dist/security/security-scanner.d.ts +92 -0
- package/dist/security/security-scanner.d.ts.map +1 -0
- package/dist/security/security-scanner.js +273 -0
- package/examples/{enhanced-kernel-example.ts → kernel-features-example.ts} +6 -6
- package/examples/phase2-integration.ts +355 -0
- package/package.json +2 -2
- package/src/api-registry-plugin.test.ts +20 -20
- package/src/dependency-resolver.test.ts +287 -0
- package/src/dependency-resolver.ts +388 -0
- package/src/health-monitor.test.ts +81 -0
- package/src/health-monitor.ts +316 -0
- package/src/hot-reload.ts +388 -0
- package/src/index.ts +6 -1
- package/src/kernel-base.ts +2 -2
- package/src/kernel.test.ts +469 -134
- package/src/kernel.ts +464 -78
- package/src/lite-kernel.test.ts +200 -0
- package/src/lite-kernel.ts +135 -0
- package/src/logger.ts +64 -18
- package/src/plugin-loader.test.ts +10 -1
- package/src/plugin-loader.ts +42 -13
- package/src/security/index.ts +19 -0
- package/src/security/permission-manager.test.ts +256 -0
- package/src/security/permission-manager.ts +336 -0
- package/src/security/plugin-signature-verifier.ts +3 -3
- package/src/security/sandbox-runtime.ts +432 -0
- package/src/security/security-scanner.ts +365 -0
- package/dist/enhanced-kernel.d.ts +0 -103
- package/dist/enhanced-kernel.d.ts.map +0 -1
- package/dist/enhanced-kernel.js +0 -403
- package/dist/enhanced-kernel.test.d.ts +0 -2
- package/dist/enhanced-kernel.test.d.ts.map +0 -1
- package/dist/enhanced-kernel.test.js +0 -412
- package/src/enhanced-kernel.test.ts +0 -535
- package/src/enhanced-kernel.ts +0 -496
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { PluginHealthMonitor } from './health-monitor.js';
|
|
3
|
+
import { createLogger } from './logger.js';
|
|
4
|
+
import type { PluginHealthCheck } from '@objectstack/spec/system';
|
|
5
|
+
|
|
6
|
+
describe('PluginHealthMonitor', () => {
|
|
7
|
+
let monitor: PluginHealthMonitor;
|
|
8
|
+
let logger: ReturnType<typeof createLogger>;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
logger = createLogger({ level: 'silent' });
|
|
12
|
+
monitor = new PluginHealthMonitor(logger);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should register plugin for health monitoring', () => {
|
|
16
|
+
const config: PluginHealthCheck = {
|
|
17
|
+
interval: 5000,
|
|
18
|
+
timeout: 1000,
|
|
19
|
+
failureThreshold: 3,
|
|
20
|
+
successThreshold: 1,
|
|
21
|
+
autoRestart: false,
|
|
22
|
+
maxRestartAttempts: 3,
|
|
23
|
+
restartBackoff: 'exponential',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
monitor.registerPlugin('test-plugin', config);
|
|
27
|
+
expect(monitor.getHealthStatus('test-plugin')).toBe('unknown');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should report healthy status initially', () => {
|
|
31
|
+
const config: PluginHealthCheck = {
|
|
32
|
+
interval: 5000,
|
|
33
|
+
timeout: 1000,
|
|
34
|
+
failureThreshold: 3,
|
|
35
|
+
successThreshold: 1,
|
|
36
|
+
autoRestart: false,
|
|
37
|
+
maxRestartAttempts: 3,
|
|
38
|
+
restartBackoff: 'fixed',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
monitor.registerPlugin('test-plugin', config);
|
|
42
|
+
expect(monitor.getHealthStatus('test-plugin')).toBe('unknown');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should get all health statuses', () => {
|
|
46
|
+
const config: PluginHealthCheck = {
|
|
47
|
+
interval: 5000,
|
|
48
|
+
timeout: 1000,
|
|
49
|
+
failureThreshold: 3,
|
|
50
|
+
successThreshold: 1,
|
|
51
|
+
autoRestart: false,
|
|
52
|
+
maxRestartAttempts: 3,
|
|
53
|
+
restartBackoff: 'linear',
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
monitor.registerPlugin('plugin1', config);
|
|
57
|
+
monitor.registerPlugin('plugin2', config);
|
|
58
|
+
|
|
59
|
+
const statuses = monitor.getAllHealthStatuses();
|
|
60
|
+
expect(statuses.size).toBe(2);
|
|
61
|
+
expect(statuses.has('plugin1')).toBe(true);
|
|
62
|
+
expect(statuses.has('plugin2')).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should shutdown cleanly', () => {
|
|
66
|
+
const config: PluginHealthCheck = {
|
|
67
|
+
interval: 5000,
|
|
68
|
+
timeout: 1000,
|
|
69
|
+
failureThreshold: 3,
|
|
70
|
+
successThreshold: 1,
|
|
71
|
+
autoRestart: false,
|
|
72
|
+
maxRestartAttempts: 3,
|
|
73
|
+
restartBackoff: 'exponential',
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
monitor.registerPlugin('test-plugin', config);
|
|
77
|
+
monitor.shutdown();
|
|
78
|
+
|
|
79
|
+
expect(monitor.getAllHealthStatuses().size).toBe(0);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PluginHealthStatus,
|
|
3
|
+
PluginHealthCheck,
|
|
4
|
+
PluginHealthReport
|
|
5
|
+
} from '@objectstack/spec/system';
|
|
6
|
+
import type { ObjectLogger } from './logger.js';
|
|
7
|
+
import type { Plugin } from './types.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Plugin Health Monitor
|
|
11
|
+
*
|
|
12
|
+
* Monitors plugin health status and performs automatic recovery actions.
|
|
13
|
+
* Implements the advanced lifecycle health monitoring protocol.
|
|
14
|
+
*/
|
|
15
|
+
export class PluginHealthMonitor {
|
|
16
|
+
private logger: ObjectLogger;
|
|
17
|
+
private healthChecks = new Map<string, PluginHealthCheck>();
|
|
18
|
+
private healthStatus = new Map<string, PluginHealthStatus>();
|
|
19
|
+
private healthReports = new Map<string, PluginHealthReport>();
|
|
20
|
+
private checkIntervals = new Map<string, NodeJS.Timeout>();
|
|
21
|
+
private failureCounters = new Map<string, number>();
|
|
22
|
+
private successCounters = new Map<string, number>();
|
|
23
|
+
private restartAttempts = new Map<string, number>();
|
|
24
|
+
|
|
25
|
+
constructor(logger: ObjectLogger) {
|
|
26
|
+
this.logger = logger.child({ component: 'HealthMonitor' });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Register a plugin for health monitoring
|
|
31
|
+
*/
|
|
32
|
+
registerPlugin(pluginName: string, config: PluginHealthCheck): void {
|
|
33
|
+
this.healthChecks.set(pluginName, config);
|
|
34
|
+
this.healthStatus.set(pluginName, 'unknown');
|
|
35
|
+
this.failureCounters.set(pluginName, 0);
|
|
36
|
+
this.successCounters.set(pluginName, 0);
|
|
37
|
+
this.restartAttempts.set(pluginName, 0);
|
|
38
|
+
|
|
39
|
+
this.logger.info('Plugin registered for health monitoring', {
|
|
40
|
+
plugin: pluginName,
|
|
41
|
+
interval: config.interval
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Start monitoring a plugin
|
|
47
|
+
*/
|
|
48
|
+
startMonitoring(pluginName: string, plugin: Plugin): void {
|
|
49
|
+
const config = this.healthChecks.get(pluginName);
|
|
50
|
+
if (!config) {
|
|
51
|
+
this.logger.warn('Cannot start monitoring - plugin not registered', { plugin: pluginName });
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Clear any existing interval
|
|
56
|
+
this.stopMonitoring(pluginName);
|
|
57
|
+
|
|
58
|
+
// Set up periodic health checks
|
|
59
|
+
const interval = setInterval(() => {
|
|
60
|
+
this.performHealthCheck(pluginName, plugin, config).catch(error => {
|
|
61
|
+
this.logger.error('Health check failed with error', {
|
|
62
|
+
plugin: pluginName,
|
|
63
|
+
error
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}, config.interval);
|
|
67
|
+
|
|
68
|
+
this.checkIntervals.set(pluginName, interval);
|
|
69
|
+
this.logger.info('Health monitoring started', { plugin: pluginName });
|
|
70
|
+
|
|
71
|
+
// Perform initial health check
|
|
72
|
+
this.performHealthCheck(pluginName, plugin, config).catch(error => {
|
|
73
|
+
this.logger.error('Initial health check failed', {
|
|
74
|
+
plugin: pluginName,
|
|
75
|
+
error
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Stop monitoring a plugin
|
|
82
|
+
*/
|
|
83
|
+
stopMonitoring(pluginName: string): void {
|
|
84
|
+
const interval = this.checkIntervals.get(pluginName);
|
|
85
|
+
if (interval) {
|
|
86
|
+
clearInterval(interval);
|
|
87
|
+
this.checkIntervals.delete(pluginName);
|
|
88
|
+
this.logger.info('Health monitoring stopped', { plugin: pluginName });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Perform a health check on a plugin
|
|
94
|
+
*/
|
|
95
|
+
private async performHealthCheck(
|
|
96
|
+
pluginName: string,
|
|
97
|
+
plugin: Plugin,
|
|
98
|
+
config: PluginHealthCheck
|
|
99
|
+
): Promise<void> {
|
|
100
|
+
const startTime = Date.now();
|
|
101
|
+
let status: PluginHealthStatus = 'healthy';
|
|
102
|
+
let message: string | undefined;
|
|
103
|
+
const checks: Array<{ name: string; status: 'passed' | 'failed' | 'warning'; message?: string }> = [];
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
// Check if plugin has a custom health check method
|
|
107
|
+
if (config.checkMethod && typeof (plugin as any)[config.checkMethod] === 'function') {
|
|
108
|
+
const checkResult = await Promise.race([
|
|
109
|
+
(plugin as any)[config.checkMethod](),
|
|
110
|
+
this.timeout(config.timeout, `Health check timeout after ${config.timeout}ms`)
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
if (checkResult === false || (checkResult && checkResult.status === 'unhealthy')) {
|
|
114
|
+
status = 'unhealthy';
|
|
115
|
+
message = checkResult?.message || 'Custom health check failed';
|
|
116
|
+
checks.push({ name: config.checkMethod, status: 'failed', message });
|
|
117
|
+
} else {
|
|
118
|
+
checks.push({ name: config.checkMethod, status: 'passed' });
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
// Default health check - just verify plugin is loaded
|
|
122
|
+
checks.push({ name: 'plugin-loaded', status: 'passed' });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Update counters based on result
|
|
126
|
+
if (status === 'healthy') {
|
|
127
|
+
this.successCounters.set(pluginName, (this.successCounters.get(pluginName) || 0) + 1);
|
|
128
|
+
this.failureCounters.set(pluginName, 0);
|
|
129
|
+
|
|
130
|
+
// Recover from unhealthy state if we have enough successes
|
|
131
|
+
const currentStatus = this.healthStatus.get(pluginName);
|
|
132
|
+
if (currentStatus === 'unhealthy' || currentStatus === 'degraded') {
|
|
133
|
+
const successCount = this.successCounters.get(pluginName) || 0;
|
|
134
|
+
if (successCount >= config.successThreshold) {
|
|
135
|
+
this.healthStatus.set(pluginName, 'healthy');
|
|
136
|
+
this.logger.info('Plugin recovered to healthy state', { plugin: pluginName });
|
|
137
|
+
} else {
|
|
138
|
+
this.healthStatus.set(pluginName, 'recovering');
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
this.healthStatus.set(pluginName, 'healthy');
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
this.failureCounters.set(pluginName, (this.failureCounters.get(pluginName) || 0) + 1);
|
|
145
|
+
this.successCounters.set(pluginName, 0);
|
|
146
|
+
|
|
147
|
+
const failureCount = this.failureCounters.get(pluginName) || 0;
|
|
148
|
+
if (failureCount >= config.failureThreshold) {
|
|
149
|
+
this.healthStatus.set(pluginName, 'unhealthy');
|
|
150
|
+
this.logger.warn('Plugin marked as unhealthy', {
|
|
151
|
+
plugin: pluginName,
|
|
152
|
+
failures: failureCount
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Attempt auto-restart if configured
|
|
156
|
+
if (config.autoRestart) {
|
|
157
|
+
await this.attemptRestart(pluginName, plugin, config);
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
this.healthStatus.set(pluginName, 'degraded');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} catch (error) {
|
|
164
|
+
status = 'failed';
|
|
165
|
+
message = error instanceof Error ? error.message : 'Unknown error';
|
|
166
|
+
this.failureCounters.set(pluginName, (this.failureCounters.get(pluginName) || 0) + 1);
|
|
167
|
+
this.healthStatus.set(pluginName, 'failed');
|
|
168
|
+
|
|
169
|
+
checks.push({
|
|
170
|
+
name: 'health-check',
|
|
171
|
+
status: 'failed',
|
|
172
|
+
message: message
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
this.logger.error('Health check exception', {
|
|
176
|
+
plugin: pluginName,
|
|
177
|
+
error
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Create health report
|
|
182
|
+
const report: PluginHealthReport = {
|
|
183
|
+
status: this.healthStatus.get(pluginName) || 'unknown',
|
|
184
|
+
timestamp: new Date().toISOString(),
|
|
185
|
+
message,
|
|
186
|
+
metrics: {
|
|
187
|
+
uptime: Date.now() - startTime,
|
|
188
|
+
},
|
|
189
|
+
checks: checks.length > 0 ? checks : undefined,
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
this.healthReports.set(pluginName, report);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Attempt to restart a plugin
|
|
197
|
+
*/
|
|
198
|
+
private async attemptRestart(
|
|
199
|
+
pluginName: string,
|
|
200
|
+
plugin: Plugin,
|
|
201
|
+
config: PluginHealthCheck
|
|
202
|
+
): Promise<void> {
|
|
203
|
+
const attempts = this.restartAttempts.get(pluginName) || 0;
|
|
204
|
+
|
|
205
|
+
if (attempts >= config.maxRestartAttempts) {
|
|
206
|
+
this.logger.error('Max restart attempts reached, giving up', {
|
|
207
|
+
plugin: pluginName,
|
|
208
|
+
attempts
|
|
209
|
+
});
|
|
210
|
+
this.healthStatus.set(pluginName, 'failed');
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
this.restartAttempts.set(pluginName, attempts + 1);
|
|
215
|
+
|
|
216
|
+
// Calculate backoff delay
|
|
217
|
+
const delay = this.calculateBackoff(attempts, config.restartBackoff);
|
|
218
|
+
|
|
219
|
+
this.logger.info('Scheduling plugin restart', {
|
|
220
|
+
plugin: pluginName,
|
|
221
|
+
attempt: attempts + 1,
|
|
222
|
+
delay
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
// Call destroy and init to restart
|
|
229
|
+
if (plugin.destroy) {
|
|
230
|
+
await plugin.destroy();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Note: Full restart would require kernel context
|
|
234
|
+
// This is a simplified version - actual implementation would need kernel integration
|
|
235
|
+
this.logger.info('Plugin restarted', { plugin: pluginName });
|
|
236
|
+
|
|
237
|
+
// Reset counters on successful restart
|
|
238
|
+
this.failureCounters.set(pluginName, 0);
|
|
239
|
+
this.successCounters.set(pluginName, 0);
|
|
240
|
+
this.healthStatus.set(pluginName, 'recovering');
|
|
241
|
+
} catch (error) {
|
|
242
|
+
this.logger.error('Plugin restart failed', {
|
|
243
|
+
plugin: pluginName,
|
|
244
|
+
error
|
|
245
|
+
});
|
|
246
|
+
this.healthStatus.set(pluginName, 'failed');
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Calculate backoff delay for restarts
|
|
252
|
+
*/
|
|
253
|
+
private calculateBackoff(attempt: number, strategy: 'fixed' | 'linear' | 'exponential'): number {
|
|
254
|
+
const baseDelay = 1000; // 1 second base
|
|
255
|
+
|
|
256
|
+
switch (strategy) {
|
|
257
|
+
case 'fixed':
|
|
258
|
+
return baseDelay;
|
|
259
|
+
case 'linear':
|
|
260
|
+
return baseDelay * (attempt + 1);
|
|
261
|
+
case 'exponential':
|
|
262
|
+
return baseDelay * Math.pow(2, attempt);
|
|
263
|
+
default:
|
|
264
|
+
return baseDelay;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Get current health status of a plugin
|
|
270
|
+
*/
|
|
271
|
+
getHealthStatus(pluginName: string): PluginHealthStatus | undefined {
|
|
272
|
+
return this.healthStatus.get(pluginName);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Get latest health report for a plugin
|
|
277
|
+
*/
|
|
278
|
+
getHealthReport(pluginName: string): PluginHealthReport | undefined {
|
|
279
|
+
return this.healthReports.get(pluginName);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Get all health statuses
|
|
284
|
+
*/
|
|
285
|
+
getAllHealthStatuses(): Map<string, PluginHealthStatus> {
|
|
286
|
+
return new Map(this.healthStatus);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Shutdown health monitor
|
|
291
|
+
*/
|
|
292
|
+
shutdown(): void {
|
|
293
|
+
// Stop all monitoring intervals
|
|
294
|
+
for (const pluginName of this.checkIntervals.keys()) {
|
|
295
|
+
this.stopMonitoring(pluginName);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
this.healthChecks.clear();
|
|
299
|
+
this.healthStatus.clear();
|
|
300
|
+
this.healthReports.clear();
|
|
301
|
+
this.failureCounters.clear();
|
|
302
|
+
this.successCounters.clear();
|
|
303
|
+
this.restartAttempts.clear();
|
|
304
|
+
|
|
305
|
+
this.logger.info('Health monitor shutdown complete');
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Timeout helper
|
|
310
|
+
*/
|
|
311
|
+
private timeout<T>(ms: number, message: string): Promise<T> {
|
|
312
|
+
return new Promise((_, reject) => {
|
|
313
|
+
setTimeout(() => reject(new Error(message)), ms);
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|