@zintrust/workers 0.1.27

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 (178) hide show
  1. package/README.md +861 -0
  2. package/dist/AnomalyDetection.d.ts +102 -0
  3. package/dist/AnomalyDetection.js +321 -0
  4. package/dist/AutoScaler.d.ts +127 -0
  5. package/dist/AutoScaler.js +425 -0
  6. package/dist/BroadcastWorker.d.ts +21 -0
  7. package/dist/BroadcastWorker.js +24 -0
  8. package/dist/CanaryController.d.ts +103 -0
  9. package/dist/CanaryController.js +380 -0
  10. package/dist/ChaosEngineering.d.ts +79 -0
  11. package/dist/ChaosEngineering.js +216 -0
  12. package/dist/CircuitBreaker.d.ts +106 -0
  13. package/dist/CircuitBreaker.js +374 -0
  14. package/dist/ClusterLock.d.ts +90 -0
  15. package/dist/ClusterLock.js +385 -0
  16. package/dist/ComplianceManager.d.ts +177 -0
  17. package/dist/ComplianceManager.js +556 -0
  18. package/dist/DatacenterOrchestrator.d.ts +133 -0
  19. package/dist/DatacenterOrchestrator.js +404 -0
  20. package/dist/DeadLetterQueue.d.ts +122 -0
  21. package/dist/DeadLetterQueue.js +539 -0
  22. package/dist/HealthMonitor.d.ts +42 -0
  23. package/dist/HealthMonitor.js +301 -0
  24. package/dist/MultiQueueWorker.d.ts +89 -0
  25. package/dist/MultiQueueWorker.js +277 -0
  26. package/dist/NotificationWorker.d.ts +21 -0
  27. package/dist/NotificationWorker.js +23 -0
  28. package/dist/Observability.d.ts +153 -0
  29. package/dist/Observability.js +530 -0
  30. package/dist/PluginManager.d.ts +123 -0
  31. package/dist/PluginManager.js +392 -0
  32. package/dist/PriorityQueue.d.ts +117 -0
  33. package/dist/PriorityQueue.js +244 -0
  34. package/dist/ResourceMonitor.d.ts +164 -0
  35. package/dist/ResourceMonitor.js +605 -0
  36. package/dist/SLAMonitor.d.ts +110 -0
  37. package/dist/SLAMonitor.js +274 -0
  38. package/dist/WorkerFactory.d.ts +193 -0
  39. package/dist/WorkerFactory.js +1507 -0
  40. package/dist/WorkerInit.d.ts +85 -0
  41. package/dist/WorkerInit.js +223 -0
  42. package/dist/WorkerMetrics.d.ts +114 -0
  43. package/dist/WorkerMetrics.js +509 -0
  44. package/dist/WorkerRegistry.d.ts +145 -0
  45. package/dist/WorkerRegistry.js +319 -0
  46. package/dist/WorkerShutdown.d.ts +61 -0
  47. package/dist/WorkerShutdown.js +159 -0
  48. package/dist/WorkerVersioning.d.ts +107 -0
  49. package/dist/WorkerVersioning.js +300 -0
  50. package/dist/build-manifest.json +462 -0
  51. package/dist/config/workerConfig.d.ts +3 -0
  52. package/dist/config/workerConfig.js +19 -0
  53. package/dist/createQueueWorker.d.ts +23 -0
  54. package/dist/createQueueWorker.js +113 -0
  55. package/dist/dashboard/index.d.ts +1 -0
  56. package/dist/dashboard/index.js +1 -0
  57. package/dist/dashboard/types.d.ts +117 -0
  58. package/dist/dashboard/types.js +1 -0
  59. package/dist/dashboard/workers-api.d.ts +4 -0
  60. package/dist/dashboard/workers-api.js +638 -0
  61. package/dist/dashboard/workers-dashboard-ui.d.ts +3 -0
  62. package/dist/dashboard/workers-dashboard-ui.js +1026 -0
  63. package/dist/dashboard/workers-dashboard.d.ts +4 -0
  64. package/dist/dashboard/workers-dashboard.js +904 -0
  65. package/dist/helper/index.d.ts +5 -0
  66. package/dist/helper/index.js +10 -0
  67. package/dist/http/WorkerApiController.d.ts +38 -0
  68. package/dist/http/WorkerApiController.js +312 -0
  69. package/dist/http/WorkerController.d.ts +374 -0
  70. package/dist/http/WorkerController.js +1351 -0
  71. package/dist/http/middleware/CustomValidation.d.ts +92 -0
  72. package/dist/http/middleware/CustomValidation.js +270 -0
  73. package/dist/http/middleware/DatacenterValidator.d.ts +3 -0
  74. package/dist/http/middleware/DatacenterValidator.js +94 -0
  75. package/dist/http/middleware/EditWorkerValidation.d.ts +7 -0
  76. package/dist/http/middleware/EditWorkerValidation.js +55 -0
  77. package/dist/http/middleware/FeaturesValidator.d.ts +3 -0
  78. package/dist/http/middleware/FeaturesValidator.js +60 -0
  79. package/dist/http/middleware/InfrastructureValidator.d.ts +31 -0
  80. package/dist/http/middleware/InfrastructureValidator.js +226 -0
  81. package/dist/http/middleware/OptionsValidator.d.ts +3 -0
  82. package/dist/http/middleware/OptionsValidator.js +112 -0
  83. package/dist/http/middleware/PayloadSanitizer.d.ts +7 -0
  84. package/dist/http/middleware/PayloadSanitizer.js +42 -0
  85. package/dist/http/middleware/ProcessorPathSanitizer.d.ts +3 -0
  86. package/dist/http/middleware/ProcessorPathSanitizer.js +74 -0
  87. package/dist/http/middleware/QueueNameSanitizer.d.ts +3 -0
  88. package/dist/http/middleware/QueueNameSanitizer.js +45 -0
  89. package/dist/http/middleware/ValidateDriver.d.ts +7 -0
  90. package/dist/http/middleware/ValidateDriver.js +20 -0
  91. package/dist/http/middleware/VersionSanitizer.d.ts +3 -0
  92. package/dist/http/middleware/VersionSanitizer.js +25 -0
  93. package/dist/http/middleware/WorkerNameSanitizer.d.ts +3 -0
  94. package/dist/http/middleware/WorkerNameSanitizer.js +46 -0
  95. package/dist/http/middleware/WorkerValidationChain.d.ts +27 -0
  96. package/dist/http/middleware/WorkerValidationChain.js +185 -0
  97. package/dist/index.d.ts +46 -0
  98. package/dist/index.js +48 -0
  99. package/dist/routes/workers.d.ts +12 -0
  100. package/dist/routes/workers.js +81 -0
  101. package/dist/storage/WorkerStore.d.ts +45 -0
  102. package/dist/storage/WorkerStore.js +195 -0
  103. package/dist/type.d.ts +76 -0
  104. package/dist/type.js +1 -0
  105. package/dist/ui/router/ui.d.ts +3 -0
  106. package/dist/ui/router/ui.js +83 -0
  107. package/dist/ui/types/worker-ui.d.ts +229 -0
  108. package/dist/ui/types/worker-ui.js +5 -0
  109. package/package.json +53 -0
  110. package/src/AnomalyDetection.ts +434 -0
  111. package/src/AutoScaler.ts +654 -0
  112. package/src/BroadcastWorker.ts +34 -0
  113. package/src/CanaryController.ts +531 -0
  114. package/src/ChaosEngineering.ts +301 -0
  115. package/src/CircuitBreaker.ts +495 -0
  116. package/src/ClusterLock.ts +499 -0
  117. package/src/ComplianceManager.ts +815 -0
  118. package/src/DatacenterOrchestrator.ts +561 -0
  119. package/src/DeadLetterQueue.ts +733 -0
  120. package/src/HealthMonitor.ts +390 -0
  121. package/src/MultiQueueWorker.ts +431 -0
  122. package/src/NotificationWorker.ts +33 -0
  123. package/src/Observability.ts +696 -0
  124. package/src/PluginManager.ts +551 -0
  125. package/src/PriorityQueue.ts +351 -0
  126. package/src/ResourceMonitor.ts +769 -0
  127. package/src/SLAMonitor.ts +408 -0
  128. package/src/WorkerFactory.ts +2108 -0
  129. package/src/WorkerInit.ts +313 -0
  130. package/src/WorkerMetrics.ts +709 -0
  131. package/src/WorkerRegistry.ts +443 -0
  132. package/src/WorkerShutdown.ts +210 -0
  133. package/src/WorkerVersioning.ts +422 -0
  134. package/src/config/workerConfig.ts +25 -0
  135. package/src/createQueueWorker.ts +174 -0
  136. package/src/dashboard/index.ts +6 -0
  137. package/src/dashboard/types.ts +141 -0
  138. package/src/dashboard/workers-api.ts +785 -0
  139. package/src/dashboard/zintrust.svg +30 -0
  140. package/src/helper/index.ts +11 -0
  141. package/src/http/WorkerApiController.ts +369 -0
  142. package/src/http/WorkerController.ts +1512 -0
  143. package/src/http/middleware/CustomValidation.ts +360 -0
  144. package/src/http/middleware/DatacenterValidator.ts +124 -0
  145. package/src/http/middleware/EditWorkerValidation.ts +74 -0
  146. package/src/http/middleware/FeaturesValidator.ts +82 -0
  147. package/src/http/middleware/InfrastructureValidator.ts +295 -0
  148. package/src/http/middleware/OptionsValidator.ts +144 -0
  149. package/src/http/middleware/PayloadSanitizer.ts +52 -0
  150. package/src/http/middleware/ProcessorPathSanitizer.ts +86 -0
  151. package/src/http/middleware/QueueNameSanitizer.ts +55 -0
  152. package/src/http/middleware/ValidateDriver.ts +29 -0
  153. package/src/http/middleware/VersionSanitizer.ts +30 -0
  154. package/src/http/middleware/WorkerNameSanitizer.ts +56 -0
  155. package/src/http/middleware/WorkerValidationChain.ts +230 -0
  156. package/src/index.ts +98 -0
  157. package/src/routes/workers.ts +154 -0
  158. package/src/storage/WorkerStore.ts +240 -0
  159. package/src/type.ts +89 -0
  160. package/src/types/queue-monitor.d.ts +38 -0
  161. package/src/types/queue-redis.d.ts +38 -0
  162. package/src/ui/README.md +13 -0
  163. package/src/ui/components/JsonEditor.js +670 -0
  164. package/src/ui/components/JsonViewer.js +387 -0
  165. package/src/ui/components/WorkerCard.js +178 -0
  166. package/src/ui/components/WorkerExpandPanel.js +257 -0
  167. package/src/ui/components/fetcher.js +42 -0
  168. package/src/ui/components/sla-scorecard.js +32 -0
  169. package/src/ui/components/styles.css +30 -0
  170. package/src/ui/components/table-expander.js +34 -0
  171. package/src/ui/integration/worker-ui-integration.js +565 -0
  172. package/src/ui/router/ui.ts +99 -0
  173. package/src/ui/services/workerApi.js +240 -0
  174. package/src/ui/types/worker-ui.ts +283 -0
  175. package/src/ui/utils/jsonValidator.js +444 -0
  176. package/src/ui/workers/index.html +202 -0
  177. package/src/ui/workers/main.js +1781 -0
  178. package/src/ui/workers/styles.css +1350 -0
@@ -0,0 +1,408 @@
1
+ /**
2
+ * SLA Monitor
3
+ * SLA compliance checks and violation tracking for workers
4
+ * Sealed namespace for immutability
5
+ */
6
+
7
+ import { ErrorFactory, Logger, generateUuid } from '@zintrust/core';
8
+ import { HealthMonitor, type HealthCheckResult } from './HealthMonitor';
9
+ import { WorkerMetrics, type MetricPoint, type MetricType } from './WorkerMetrics';
10
+
11
+ export type ITimeRange = {
12
+ start: Date;
13
+ end: Date;
14
+ };
15
+
16
+ export interface ISLAConfig {
17
+ workerName: string;
18
+ metrics: {
19
+ maxLatencyP99: number; // milliseconds
20
+ minThroughput: number; // jobs per minute
21
+ minAvailability: number; // percentage (99.9)
22
+ maxErrorRate: number; // percentage (1.0)
23
+ maxRecoveryTime: number; // seconds
24
+ };
25
+ alerting: {
26
+ channels: string[];
27
+ escalation: boolean;
28
+ cooldown: number; // minutes between alerts
29
+ };
30
+ reporting: {
31
+ interval: 'daily' | 'weekly' | 'monthly';
32
+ recipients: string[];
33
+ };
34
+ }
35
+
36
+ export type ISLAStatus = {
37
+ workerName: string;
38
+ status: 'compliant' | 'warning' | 'breach';
39
+ evaluatedAt: Date;
40
+ checks: {
41
+ latencyP99: { value: number; threshold: number; status: 'pass' | 'warn' | 'fail' };
42
+ throughput: { value: number; threshold: number; status: 'pass' | 'warn' | 'fail' };
43
+ availability: { value: number; threshold: number; status: 'pass' | 'warn' | 'fail' };
44
+ errorRate: { value: number; threshold: number; status: 'pass' | 'warn' | 'fail' };
45
+ recoveryTime: { value: number; threshold: number; status: 'pass' | 'warn' | 'fail' };
46
+ };
47
+ };
48
+
49
+ export type ISLAViolation = {
50
+ id: string;
51
+ workerName: string;
52
+ metric: keyof ISLAStatus['checks'];
53
+ expected: number;
54
+ actual: number;
55
+ timestamp: Date;
56
+ severity: 'warning' | 'critical';
57
+ message: string;
58
+ };
59
+
60
+ export type ISLAReport = {
61
+ generatedAt: Date;
62
+ period: ITimeRange;
63
+ totalWorkers: number;
64
+ totalChecks: number;
65
+ totalViolations: number;
66
+ complianceRate: number;
67
+ violations: ReadonlyArray<ISLAViolation>;
68
+ perWorker: Array<{
69
+ workerName: string;
70
+ violations: number;
71
+ complianceRate: number;
72
+ }>;
73
+ };
74
+
75
+ const slaConfigs = new Map<string, ISLAConfig>();
76
+ const violationHistory = new Map<string, ISLAViolation[]>();
77
+ const lastAlertAt = new Map<string, Date>();
78
+
79
+ const DEFAULT_LOOKBACK_MS = 60 * 60 * 1000;
80
+
81
+ const quantile = (points: ReadonlyArray<MetricPoint>, percentile: number): number => {
82
+ if (points.length === 0) return 0;
83
+ const sorted = [...points].sort((a, b) => a.value - b.value);
84
+ const index = Math.min(sorted.length - 1, Math.max(0, Math.floor(percentile * sorted.length)));
85
+ return sorted[index]?.value ?? 0;
86
+ };
87
+
88
+ const warnOnMaxThreshold = (value: number, threshold: number): boolean =>
89
+ value >= threshold * 0.9 && value <= threshold;
90
+
91
+ const warnOnMinThreshold = (value: number, threshold: number): boolean =>
92
+ value <= threshold * 1.1 && value >= threshold;
93
+
94
+ const evaluateMaxThreshold = (value: number, threshold: number): 'pass' | 'warn' | 'fail' => {
95
+ if (value > threshold) return 'fail';
96
+ if (warnOnMaxThreshold(value, threshold)) return 'warn';
97
+ return 'pass';
98
+ };
99
+
100
+ const evaluateMinThreshold = (value: number, threshold: number): 'pass' | 'warn' | 'fail' => {
101
+ if (value < threshold) return 'fail';
102
+ if (warnOnMinThreshold(value, threshold)) return 'warn';
103
+ return 'pass';
104
+ };
105
+
106
+ const storeViolation = (violation: ISLAViolation): void => {
107
+ const history = violationHistory.get(violation.workerName) ?? [];
108
+ history.push(violation);
109
+ if (history.length > 1000) {
110
+ history.shift();
111
+ }
112
+ violationHistory.set(violation.workerName, history);
113
+ };
114
+
115
+ const calculateAvailability = (checks: HealthCheckResult[]): number => {
116
+ if (checks.length === 0) return 0;
117
+ const upCount = checks.filter(
118
+ (check) => check.status === 'healthy' || check.status === 'degraded'
119
+ ).length;
120
+ return (upCount / checks.length) * 100;
121
+ };
122
+
123
+ const calculateRecoveryTime = (checks: HealthCheckResult[]): number => {
124
+ if (checks.length < 2) return 0;
125
+
126
+ const sorted = [...checks].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
127
+ let currentDownAt: Date | null = null;
128
+ let maxRecoverySeconds = 0;
129
+
130
+ for (const check of sorted) {
131
+ const isDown = check.status === 'critical';
132
+ if (isDown && currentDownAt === null) {
133
+ currentDownAt = check.timestamp;
134
+ }
135
+
136
+ if (!isDown && currentDownAt !== null) {
137
+ const recoverySeconds = (check.timestamp.getTime() - currentDownAt.getTime()) / 1000;
138
+ maxRecoverySeconds = Math.max(maxRecoverySeconds, recoverySeconds);
139
+ currentDownAt = null;
140
+ }
141
+ }
142
+
143
+ return Math.round(maxRecoverySeconds);
144
+ };
145
+
146
+ const loadHealthChecks = async (
147
+ workerName: string,
148
+ range: ITimeRange
149
+ ): Promise<HealthCheckResult[]> => {
150
+ const history = HealthMonitor.getHealthHistory(workerName);
151
+ const filtered = history.filter(
152
+ (check) =>
153
+ check.timestamp.getTime() >= range.start.getTime() &&
154
+ check.timestamp.getTime() <= range.end.getTime()
155
+ );
156
+
157
+ if (filtered.length > 0) return filtered;
158
+
159
+ try {
160
+ const current = HealthMonitor.getCurrentHealth(workerName);
161
+ return current ? [current] : [];
162
+ } catch (error) {
163
+ Logger.debug('Failed to get current health for SLA check', error as Error);
164
+ return [];
165
+ }
166
+ };
167
+
168
+ const getMetricPoints = async (
169
+ workerName: string,
170
+ metricType: MetricType,
171
+ range: ITimeRange
172
+ ): Promise<ReadonlyArray<MetricPoint>> => {
173
+ const entry = await WorkerMetrics.query({
174
+ workerName,
175
+ metricType,
176
+ granularity: 'hourly',
177
+ startDate: range.start,
178
+ endDate: range.end,
179
+ });
180
+
181
+ return entry.points;
182
+ };
183
+
184
+ const collectSlaMetrics = async (
185
+ workerName: string,
186
+ range: ITimeRange
187
+ ): Promise<{
188
+ latencyP99: number;
189
+ throughput: number;
190
+ availability: number;
191
+ errorRate: number;
192
+ recoveryTime: number;
193
+ }> => {
194
+ const [durationPoints, processedAgg, errorAgg, healthChecks] = await Promise.all([
195
+ getMetricPoints(workerName, 'duration', range),
196
+ WorkerMetrics.aggregate({
197
+ workerName,
198
+ metricType: 'processed',
199
+ granularity: 'hourly',
200
+ startDate: range.start,
201
+ endDate: range.end,
202
+ }),
203
+ WorkerMetrics.aggregate({
204
+ workerName,
205
+ metricType: 'errors',
206
+ granularity: 'hourly',
207
+ startDate: range.start,
208
+ endDate: range.end,
209
+ }),
210
+ loadHealthChecks(workerName, range),
211
+ ]);
212
+
213
+ const latencyP99 = quantile(durationPoints, 0.99);
214
+ const minutes = Math.max(1, (range.end.getTime() - range.start.getTime()) / 60000);
215
+ const throughput = processedAgg.total / minutes;
216
+ const errorRate = processedAgg.total > 0 ? (errorAgg.total / processedAgg.total) * 100 : 0;
217
+ const availability = calculateAvailability(healthChecks);
218
+ const recoveryTime = calculateRecoveryTime(healthChecks);
219
+
220
+ return { latencyP99, throughput, availability, errorRate, recoveryTime };
221
+ };
222
+
223
+ const buildChecks = (
224
+ metrics: Awaited<ReturnType<typeof collectSlaMetrics>>,
225
+ config: ISLAConfig
226
+ ): ISLAStatus['checks'] => ({
227
+ latencyP99: {
228
+ value: metrics.latencyP99,
229
+ threshold: config.metrics.maxLatencyP99,
230
+ status: evaluateMaxThreshold(metrics.latencyP99, config.metrics.maxLatencyP99),
231
+ },
232
+ throughput: {
233
+ value: metrics.throughput,
234
+ threshold: config.metrics.minThroughput,
235
+ status: evaluateMinThreshold(metrics.throughput, config.metrics.minThroughput),
236
+ },
237
+ availability: {
238
+ value: metrics.availability,
239
+ threshold: config.metrics.minAvailability,
240
+ status: evaluateMinThreshold(metrics.availability, config.metrics.minAvailability),
241
+ },
242
+ errorRate: {
243
+ value: metrics.errorRate,
244
+ threshold: config.metrics.maxErrorRate,
245
+ status: evaluateMaxThreshold(metrics.errorRate, config.metrics.maxErrorRate),
246
+ },
247
+ recoveryTime: {
248
+ value: metrics.recoveryTime,
249
+ threshold: config.metrics.maxRecoveryTime,
250
+ status: evaluateMaxThreshold(metrics.recoveryTime, config.metrics.maxRecoveryTime),
251
+ },
252
+ });
253
+
254
+ const buildSlaStatus = (checks: ISLAStatus['checks']): ISLAStatus['status'] => {
255
+ const hasFailures = Object.values(checks).some((check) => check.status === 'fail');
256
+ if (hasFailures) return 'breach';
257
+
258
+ const hasWarnings = Object.values(checks).some((check) => check.status === 'warn');
259
+ return hasWarnings ? 'warning' : 'compliant';
260
+ };
261
+
262
+ const buildViolation = (params: {
263
+ workerName: string;
264
+ metric: keyof ISLAStatus['checks'];
265
+ expected: number;
266
+ actual: number;
267
+ severity: ISLAViolation['severity'];
268
+ message: string;
269
+ }): ISLAViolation => ({
270
+ id: generateUuid(),
271
+ workerName: params.workerName,
272
+ metric: params.metric,
273
+ expected: params.expected,
274
+ actual: params.actual,
275
+ timestamp: new Date(),
276
+ severity: params.severity,
277
+ message: params.message,
278
+ });
279
+
280
+ /**
281
+ * SLA Monitor - Sealed namespace
282
+ */
283
+ export const SLAMonitor = Object.freeze({
284
+ /**
285
+ * Define SLA for a worker
286
+ */
287
+ defineSLA(config: ISLAConfig): void {
288
+ slaConfigs.set(config.workerName, { ...config });
289
+ Logger.info(`SLA defined for worker "${config.workerName}"`);
290
+ },
291
+
292
+ /**
293
+ * Check SLA compliance for a worker
294
+ */
295
+ async checkCompliance(workerName: string): Promise<ISLAStatus> {
296
+ const config = slaConfigs.get(workerName);
297
+ if (!config) {
298
+ throw ErrorFactory.createNotFoundError(`SLA config not found for worker "${workerName}"`);
299
+ }
300
+
301
+ const range: ITimeRange = {
302
+ start: new Date(Date.now() - DEFAULT_LOOKBACK_MS),
303
+ end: new Date(),
304
+ };
305
+
306
+ const metrics = await collectSlaMetrics(workerName, range);
307
+ const checks = buildChecks(metrics, config);
308
+ const status = buildSlaStatus(checks);
309
+
310
+ Object.entries(checks)
311
+ .filter(([, detail]) => detail.status === 'fail')
312
+ .forEach(([metricKey, detail]) => {
313
+ const violation = buildViolation({
314
+ workerName,
315
+ metric: metricKey as keyof ISLAStatus['checks'],
316
+ expected: detail.threshold,
317
+ actual: detail.value,
318
+ severity: 'critical',
319
+ message: `SLA breach for ${metricKey}: ${detail.value} (expected ${detail.threshold})`,
320
+ });
321
+ storeViolation(violation);
322
+ SLAMonitor.alertOnViolation(violation);
323
+ });
324
+
325
+ return {
326
+ workerName,
327
+ status,
328
+ evaluatedAt: new Date(),
329
+ checks,
330
+ };
331
+ },
332
+
333
+ /**
334
+ * Get SLA violations for a worker
335
+ */
336
+ getViolations(workerName: string, timeRange: ITimeRange): ReadonlyArray<ISLAViolation> {
337
+ const history = violationHistory.get(workerName) ?? [];
338
+ return history.filter(
339
+ (violation) =>
340
+ violation.timestamp.getTime() >= timeRange.start.getTime() &&
341
+ violation.timestamp.getTime() <= timeRange.end.getTime()
342
+ );
343
+ },
344
+
345
+ /**
346
+ * Get SLA compliance report
347
+ */
348
+ getComplianceReport(timeRange: ITimeRange): ISLAReport {
349
+ const violations: ISLAViolation[] = [];
350
+ const perWorker: ISLAReport['perWorker'] = [];
351
+
352
+ for (const [workerName] of slaConfigs.entries()) {
353
+ const workerViolations = SLAMonitor.getViolations(workerName, timeRange);
354
+ violations.push(...workerViolations);
355
+
356
+ const totalChecks = slaConfigs.size * 5;
357
+ const breachCount = workerViolations.length;
358
+ const complianceRate = totalChecks > 0 ? (totalChecks - breachCount) / totalChecks : 1;
359
+
360
+ perWorker.push({
361
+ workerName,
362
+ violations: breachCount,
363
+ complianceRate,
364
+ });
365
+ }
366
+
367
+ const totalChecks = slaConfigs.size * 5;
368
+ const totalViolations = violations.length;
369
+ const complianceRate = totalChecks > 0 ? (totalChecks - totalViolations) / totalChecks : 1;
370
+
371
+ return {
372
+ generatedAt: new Date(),
373
+ period: timeRange,
374
+ totalWorkers: slaConfigs.size,
375
+ totalChecks,
376
+ totalViolations,
377
+ complianceRate,
378
+ violations,
379
+ perWorker,
380
+ };
381
+ },
382
+
383
+ /**
384
+ * Alert on SLA violation
385
+ */
386
+ alertOnViolation(violation: ISLAViolation): void {
387
+ const config = slaConfigs.get(violation.workerName);
388
+ if (!config) return;
389
+
390
+ const lastAlert = lastAlertAt.get(violation.workerName);
391
+ const cooldownMs = config.alerting.cooldown * 60 * 1000;
392
+ if (lastAlert && Date.now() - lastAlert.getTime() < cooldownMs) {
393
+ return;
394
+ }
395
+
396
+ lastAlertAt.set(violation.workerName, new Date());
397
+
398
+ Logger.warn(`SLA violation for ${violation.workerName}`, {
399
+ metric: violation.metric,
400
+ expected: violation.expected,
401
+ actual: violation.actual,
402
+ severity: violation.severity,
403
+ channels: config.alerting.channels,
404
+ });
405
+ },
406
+ });
407
+
408
+ export default SLAMonitor;