@bernierllc/retry-suite 0.1.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.
@@ -0,0 +1,406 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RetriesList = exports.AlertsList = exports.MetricsOverview = exports.RetryDashboard = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ /*
6
+ Copyright (c) 2025 Bernier LLC
7
+
8
+ This file is licensed to the client under a limited-use license.
9
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
10
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
11
+ */
12
+ const react_1 = require("react");
13
+ const RetryDashboard = ({ apiBaseUrl = '/api' }) => {
14
+ const [metrics, setMetrics] = (0, react_1.useState)(null);
15
+ const [alerts, setAlerts] = (0, react_1.useState)([]);
16
+ const [retries, setRetries] = (0, react_1.useState)([]);
17
+ const [loading, setLoading] = (0, react_1.useState)(true);
18
+ const [error, setError] = (0, react_1.useState)(null);
19
+ const [isConnected, setIsConnected] = (0, react_1.useState)(false);
20
+ (0, react_1.useEffect)(() => {
21
+ loadInitialData();
22
+ const cleanup = setupRealTimeUpdates();
23
+ return cleanup;
24
+ }, [apiBaseUrl]);
25
+ const loadInitialData = async () => {
26
+ try {
27
+ setLoading(true);
28
+ setError(null);
29
+ const [metricsRes, alertsRes, retriesRes] = await Promise.all([
30
+ fetch(`${apiBaseUrl}/metrics`),
31
+ fetch(`${apiBaseUrl}/alerts`),
32
+ fetch(`${apiBaseUrl}/retries`)
33
+ ]);
34
+ if (!metricsRes.ok)
35
+ throw new Error('Failed to load metrics');
36
+ if (!alertsRes.ok)
37
+ throw new Error('Failed to load alerts');
38
+ if (!retriesRes.ok)
39
+ throw new Error('Failed to load retries');
40
+ const [metricsData, alertsData, retriesData] = await Promise.all([
41
+ metricsRes.json(),
42
+ alertsRes.json(),
43
+ retriesRes.json()
44
+ ]);
45
+ setMetrics(metricsData);
46
+ setAlerts(alertsData);
47
+ setRetries(retriesData);
48
+ }
49
+ catch (err) {
50
+ setError(err.message);
51
+ }
52
+ finally {
53
+ setLoading(false);
54
+ }
55
+ };
56
+ const setupRealTimeUpdates = () => {
57
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
58
+ const wsUrl = `${protocol}//${window.location.host}`;
59
+ try {
60
+ const ws = new WebSocket(wsUrl);
61
+ ws.onopen = () => {
62
+ setIsConnected(true);
63
+ setError(null);
64
+ };
65
+ ws.onmessage = (event) => {
66
+ try {
67
+ const data = JSON.parse(event.data);
68
+ handleRealTimeUpdate(data);
69
+ }
70
+ catch (err) {
71
+ console.error('Failed to parse WebSocket message:', err);
72
+ }
73
+ };
74
+ ws.onerror = (error) => {
75
+ console.error('WebSocket error:', error);
76
+ setIsConnected(false);
77
+ };
78
+ ws.onclose = () => {
79
+ setIsConnected(false);
80
+ setTimeout(() => setupRealTimeUpdates(), 5000);
81
+ };
82
+ return () => {
83
+ ws.close();
84
+ };
85
+ }
86
+ catch (err) {
87
+ console.error('Failed to setup WebSocket:', err);
88
+ setIsConnected(false);
89
+ }
90
+ };
91
+ const handleRealTimeUpdate = (data) => {
92
+ switch (data.type) {
93
+ case 'metrics_update':
94
+ if (data.data?.metrics)
95
+ setMetrics(data.data.metrics);
96
+ if (data.data?.retries)
97
+ setRetries(data.data.retries);
98
+ break;
99
+ case 'alert_update':
100
+ if (data.data)
101
+ setAlerts(data.data);
102
+ break;
103
+ default:
104
+ console.log('Unknown real-time update:', data);
105
+ }
106
+ };
107
+ const refreshData = () => {
108
+ loadInitialData();
109
+ };
110
+ const acknowledgeAlert = async (alertId) => {
111
+ try {
112
+ const response = await fetch(`${apiBaseUrl}/alerts/${alertId}/acknowledge`, {
113
+ method: 'POST'
114
+ });
115
+ if (response.ok) {
116
+ setAlerts(prev => prev.map(alert => alert.id === alertId
117
+ ? { ...alert, acknowledged: true, acknowledgedAt: new Date() }
118
+ : alert));
119
+ }
120
+ }
121
+ catch (err) {
122
+ console.error('Failed to acknowledge alert:', err);
123
+ }
124
+ };
125
+ const handleRetryAction = async (retryId, action) => {
126
+ try {
127
+ const response = await fetch(`${apiBaseUrl}/retries/${retryId}/${action}`, {
128
+ method: 'POST'
129
+ });
130
+ if (response.ok) {
131
+ await loadInitialData();
132
+ }
133
+ }
134
+ catch (err) {
135
+ console.error(`Failed to ${action} retry:`, err);
136
+ }
137
+ };
138
+ if (loading) {
139
+ return ((0, jsx_runtime_1.jsx)("div", { style: styles.container, children: (0, jsx_runtime_1.jsx)("div", { style: styles.loading, children: "Loading Retry Suite Dashboard..." }) }));
140
+ }
141
+ if (error) {
142
+ return ((0, jsx_runtime_1.jsx)("div", { style: styles.container, children: (0, jsx_runtime_1.jsxs)("div", { style: styles.error, children: [(0, jsx_runtime_1.jsx)("h2", { children: "Error" }), (0, jsx_runtime_1.jsx)("p", { children: error }), (0, jsx_runtime_1.jsx)("button", { onClick: refreshData, style: styles.button, children: "Retry" })] }) }));
143
+ }
144
+ return ((0, jsx_runtime_1.jsxs)("div", { style: styles.container, children: [(0, jsx_runtime_1.jsxs)("header", { style: styles.header, children: [(0, jsx_runtime_1.jsx)("h1", { children: "Retry Suite Dashboard" }), (0, jsx_runtime_1.jsxs)("div", { style: styles.headerActions, children: [(0, jsx_runtime_1.jsxs)("div", { style: styles.connectionStatus, children: ["Status: ", isConnected ?
145
+ (0, jsx_runtime_1.jsx)("span", { style: styles.connected, children: "Connected" }) :
146
+ (0, jsx_runtime_1.jsx)("span", { style: styles.disconnected, children: "Disconnected" })] }), (0, jsx_runtime_1.jsx)("button", { onClick: refreshData, style: styles.button, children: "Refresh" })] })] }), (0, jsx_runtime_1.jsxs)("div", { style: styles.content, children: [(0, jsx_runtime_1.jsx)("div", { style: styles.metricsPanel, children: (0, jsx_runtime_1.jsx)(exports.MetricsOverview, { metrics: metrics }) }), (0, jsx_runtime_1.jsx)("div", { style: styles.alertsPanel, children: (0, jsx_runtime_1.jsx)(exports.AlertsList, { alerts: alerts, onAcknowledge: acknowledgeAlert }) }), (0, jsx_runtime_1.jsx)("div", { style: styles.retriesPanel, children: (0, jsx_runtime_1.jsx)(exports.RetriesList, { retries: retries, onAction: handleRetryAction }) })] })] }));
147
+ };
148
+ exports.RetryDashboard = RetryDashboard;
149
+ const MetricsOverview = ({ metrics }) => {
150
+ if (!metrics) {
151
+ return (0, jsx_runtime_1.jsx)("div", { style: styles.loading, children: "Loading metrics..." });
152
+ }
153
+ return ((0, jsx_runtime_1.jsxs)("div", { style: styles.metricsOverview, children: [(0, jsx_runtime_1.jsx)("h2", { children: "Metrics Overview" }), (0, jsx_runtime_1.jsxs)("div", { style: styles.metricsGrid, children: [(0, jsx_runtime_1.jsxs)("div", { style: styles.metricCard, children: [(0, jsx_runtime_1.jsx)("h3", { children: "Total Retries" }), (0, jsx_runtime_1.jsx)("div", { style: styles.metricValue, children: metrics.totalRetries })] }), (0, jsx_runtime_1.jsxs)("div", { style: styles.metricCard, children: [(0, jsx_runtime_1.jsx)("h3", { children: "Success Rate" }), (0, jsx_runtime_1.jsxs)("div", { style: {
154
+ ...styles.metricValue,
155
+ color: metrics.retrySuccessRate >= 80 ? '#28a745' : '#dc3545'
156
+ }, children: [metrics.retrySuccessRate.toFixed(1), "%"] })] }), (0, jsx_runtime_1.jsxs)("div", { style: styles.metricCard, children: [(0, jsx_runtime_1.jsx)("h3", { children: "Average Time" }), (0, jsx_runtime_1.jsxs)("div", { style: styles.metricValue, children: [metrics.averageRetryTime.toFixed(0), "ms"] })] }), (0, jsx_runtime_1.jsxs)("div", { style: styles.metricCard, children: [(0, jsx_runtime_1.jsx)("h3", { children: "Active Retries" }), (0, jsx_runtime_1.jsx)("div", { style: styles.metricValue, children: metrics.activeRetries || 0 })] }), (0, jsx_runtime_1.jsxs)("div", { style: styles.metricCard, children: [(0, jsx_runtime_1.jsx)("h3", { children: "Failed Retries" }), (0, jsx_runtime_1.jsx)("div", { style: styles.metricValue, children: metrics.failedRetries })] }), (0, jsx_runtime_1.jsxs)("div", { style: styles.metricCard, children: [(0, jsx_runtime_1.jsx)("h3", { children: "Successful Retries" }), (0, jsx_runtime_1.jsx)("div", { style: styles.metricValue, children: metrics.successfulRetries })] })] })] }));
157
+ };
158
+ exports.MetricsOverview = MetricsOverview;
159
+ const AlertsList = ({ alerts, onAcknowledge }) => {
160
+ const unacknowledgedAlerts = alerts.filter(alert => !alert.acknowledged);
161
+ return ((0, jsx_runtime_1.jsxs)("div", { style: styles.alertsList, children: [(0, jsx_runtime_1.jsxs)("h2", { children: ["Alerts (", unacknowledgedAlerts.length, " unacknowledged)"] }), alerts.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { style: styles.emptyState, children: "No alerts" })) : ((0, jsx_runtime_1.jsx)("div", { style: styles.alertItems, children: alerts.map(alert => ((0, jsx_runtime_1.jsxs)("div", { style: {
162
+ ...styles.alertItem,
163
+ opacity: alert.acknowledged ? 0.6 : 1
164
+ }, children: [(0, jsx_runtime_1.jsxs)("div", { style: styles.alertHeader, children: [(0, jsx_runtime_1.jsx)("span", { style: {
165
+ ...styles.alertSeverity,
166
+ backgroundColor: getSeverityColor(alert.severity)
167
+ }, children: alert.severity.toUpperCase() }), (0, jsx_runtime_1.jsx)("span", { style: styles.alertType, children: alert.type }), (0, jsx_runtime_1.jsx)("span", { style: styles.alertTime, children: alert.timestamp.toLocaleString() })] }), (0, jsx_runtime_1.jsx)("div", { style: styles.alertMessage, children: alert.message }), !alert.acknowledged && ((0, jsx_runtime_1.jsx)("button", { onClick: () => onAcknowledge(alert.id), style: styles.alertButton, children: "Acknowledge" })), alert.acknowledged && ((0, jsx_runtime_1.jsxs)("div", { style: styles.acknowledgedInfo, children: ["Acknowledged ", alert.acknowledgedAt?.toLocaleString()] }))] }, alert.id))) }))] }));
168
+ };
169
+ exports.AlertsList = AlertsList;
170
+ const RetriesList = ({ retries, onAction }) => {
171
+ return ((0, jsx_runtime_1.jsxs)("div", { style: styles.retriesList, children: [(0, jsx_runtime_1.jsxs)("h2", { children: ["Active Retries (", retries.length, ")"] }), retries.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { style: styles.emptyState, children: "No active retries" })) : ((0, jsx_runtime_1.jsx)("div", { style: styles.retryItems, children: retries.map(retry => ((0, jsx_runtime_1.jsxs)("div", { style: styles.retryItem, children: [(0, jsx_runtime_1.jsxs)("div", { style: styles.retryHeader, children: [(0, jsx_runtime_1.jsx)("span", { style: styles.retryId, children: retry.id }), (0, jsx_runtime_1.jsx)("span", { style: {
172
+ ...styles.retryStatus,
173
+ backgroundColor: getStatusColor(retry.status)
174
+ }, children: retry.status.toUpperCase() })] }), (0, jsx_runtime_1.jsxs)("div", { style: styles.retryDetails, children: [(0, jsx_runtime_1.jsxs)("div", { children: ["Attempts: ", retry.attempts, " / ", retry.maxAttempts] }), retry.lastAttempt && ((0, jsx_runtime_1.jsxs)("div", { children: ["Last: ", retry.lastAttempt.toLocaleString()] })), retry.nextAttempt && ((0, jsx_runtime_1.jsxs)("div", { children: ["Next: ", retry.nextAttempt.toLocaleString()] })), retry.error && ((0, jsx_runtime_1.jsxs)("div", { style: styles.retryError, children: ["Error: ", retry.error] }))] }), (0, jsx_runtime_1.jsxs)("div", { style: styles.retryActions, children: [retry.status === 'running' && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => onAction(retry.id, 'pause'), style: styles.actionButton, children: "Pause" }), (0, jsx_runtime_1.jsx)("button", { onClick: () => onAction(retry.id, 'cancel'), style: { ...styles.actionButton, backgroundColor: '#dc3545' }, children: "Cancel" })] })), retry.status === 'paused' && ((0, jsx_runtime_1.jsx)("button", { onClick: () => onAction(retry.id, 'resume'), style: styles.actionButton, children: "Resume" }))] })] }, retry.id))) }))] }));
175
+ };
176
+ exports.RetriesList = RetriesList;
177
+ const getSeverityColor = (severity) => {
178
+ switch (severity) {
179
+ case 'low': return '#28a745';
180
+ case 'medium': return '#ffc107';
181
+ case 'high': return '#fd7e14';
182
+ case 'critical': return '#dc3545';
183
+ default: return '#6c757d';
184
+ }
185
+ };
186
+ const getStatusColor = (status) => {
187
+ switch (status) {
188
+ case 'pending': return '#6c757d';
189
+ case 'running': return '#007bff';
190
+ case 'completed': return '#28a745';
191
+ case 'failed': return '#dc3545';
192
+ case 'cancelled': return '#6c757d';
193
+ case 'paused': return '#ffc107';
194
+ default: return '#6c757d';
195
+ }
196
+ };
197
+ const styles = {
198
+ container: {
199
+ fontFamily: 'Arial, sans-serif',
200
+ maxWidth: '1200px',
201
+ margin: '0 auto',
202
+ padding: '20px'
203
+ },
204
+ header: {
205
+ display: 'flex',
206
+ justifyContent: 'space-between',
207
+ alignItems: 'center',
208
+ marginBottom: '30px',
209
+ padding: '20px',
210
+ backgroundColor: '#f8f9fa',
211
+ borderRadius: '8px'
212
+ },
213
+ headerActions: {
214
+ display: 'flex',
215
+ alignItems: 'center',
216
+ gap: '20px'
217
+ },
218
+ connectionStatus: {
219
+ fontSize: '14px'
220
+ },
221
+ connected: {
222
+ color: '#28a745',
223
+ fontWeight: 'bold'
224
+ },
225
+ disconnected: {
226
+ color: '#dc3545',
227
+ fontWeight: 'bold'
228
+ },
229
+ content: {
230
+ display: 'grid',
231
+ gridTemplateColumns: '1fr',
232
+ gap: '30px'
233
+ },
234
+ metricsPanel: {
235
+ backgroundColor: 'white',
236
+ padding: '20px',
237
+ borderRadius: '8px',
238
+ boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
239
+ },
240
+ alertsPanel: {
241
+ backgroundColor: 'white',
242
+ padding: '20px',
243
+ borderRadius: '8px',
244
+ boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
245
+ },
246
+ retriesPanel: {
247
+ backgroundColor: 'white',
248
+ padding: '20px',
249
+ borderRadius: '8px',
250
+ boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
251
+ },
252
+ metricsOverview: {},
253
+ metricsGrid: {
254
+ display: 'grid',
255
+ gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
256
+ gap: '20px',
257
+ marginTop: '20px'
258
+ },
259
+ metricCard: {
260
+ padding: '20px',
261
+ backgroundColor: '#f8f9fa',
262
+ borderRadius: '8px',
263
+ textAlign: 'center'
264
+ },
265
+ metricValue: {
266
+ fontSize: '32px',
267
+ fontWeight: 'bold',
268
+ marginTop: '10px',
269
+ color: '#333'
270
+ },
271
+ loading: {
272
+ textAlign: 'center',
273
+ padding: '40px',
274
+ fontSize: '18px',
275
+ color: '#6c757d'
276
+ },
277
+ error: {
278
+ textAlign: 'center',
279
+ padding: '40px',
280
+ color: '#dc3545'
281
+ },
282
+ button: {
283
+ padding: '10px 20px',
284
+ backgroundColor: '#007bff',
285
+ color: 'white',
286
+ border: 'none',
287
+ borderRadius: '4px',
288
+ cursor: 'pointer',
289
+ fontSize: '14px'
290
+ },
291
+ emptyState: {
292
+ textAlign: 'center',
293
+ padding: '40px',
294
+ color: '#6c757d',
295
+ fontStyle: 'italic'
296
+ },
297
+ alertsList: {},
298
+ alertItems: {
299
+ display: 'flex',
300
+ flexDirection: 'column',
301
+ gap: '15px',
302
+ marginTop: '20px'
303
+ },
304
+ alertItem: {
305
+ padding: '15px',
306
+ border: '1px solid #dee2e6',
307
+ borderRadius: '8px',
308
+ backgroundColor: '#fff'
309
+ },
310
+ alertHeader: {
311
+ display: 'flex',
312
+ alignItems: 'center',
313
+ gap: '10px',
314
+ marginBottom: '10px'
315
+ },
316
+ alertSeverity: {
317
+ padding: '4px 8px',
318
+ borderRadius: '4px',
319
+ fontSize: '12px',
320
+ fontWeight: 'bold',
321
+ color: 'white'
322
+ },
323
+ alertType: {
324
+ fontSize: '14px',
325
+ color: '#6c757d'
326
+ },
327
+ alertTime: {
328
+ fontSize: '14px',
329
+ color: '#6c757d',
330
+ marginLeft: 'auto'
331
+ },
332
+ alertMessage: {
333
+ marginBottom: '10px',
334
+ fontSize: '16px'
335
+ },
336
+ alertButton: {
337
+ padding: '5px 15px',
338
+ backgroundColor: '#28a745',
339
+ color: 'white',
340
+ border: 'none',
341
+ borderRadius: '4px',
342
+ cursor: 'pointer',
343
+ fontSize: '12px'
344
+ },
345
+ acknowledgedInfo: {
346
+ fontSize: '12px',
347
+ color: '#6c757d',
348
+ fontStyle: 'italic'
349
+ },
350
+ retriesList: {},
351
+ retryItems: {
352
+ display: 'flex',
353
+ flexDirection: 'column',
354
+ gap: '15px',
355
+ marginTop: '20px'
356
+ },
357
+ retryItem: {
358
+ padding: '15px',
359
+ border: '1px solid #dee2e6',
360
+ borderRadius: '8px',
361
+ backgroundColor: '#fff'
362
+ },
363
+ retryHeader: {
364
+ display: 'flex',
365
+ alignItems: 'center',
366
+ justifyContent: 'space-between',
367
+ marginBottom: '10px'
368
+ },
369
+ retryId: {
370
+ fontFamily: 'monospace',
371
+ fontSize: '14px',
372
+ color: '#333'
373
+ },
374
+ retryStatus: {
375
+ padding: '4px 8px',
376
+ borderRadius: '4px',
377
+ fontSize: '12px',
378
+ fontWeight: 'bold',
379
+ color: 'white'
380
+ },
381
+ retryDetails: {
382
+ display: 'flex',
383
+ flexDirection: 'column',
384
+ gap: '5px',
385
+ marginBottom: '10px',
386
+ fontSize: '14px',
387
+ color: '#6c757d'
388
+ },
389
+ retryError: {
390
+ color: '#dc3545',
391
+ fontWeight: 'bold'
392
+ },
393
+ retryActions: {
394
+ display: 'flex',
395
+ gap: '10px'
396
+ },
397
+ actionButton: {
398
+ padding: '5px 15px',
399
+ backgroundColor: '#007bff',
400
+ color: 'white',
401
+ border: 'none',
402
+ borderRadius: '4px',
403
+ cursor: 'pointer',
404
+ fontSize: '12px'
405
+ }
406
+ };
@@ -0,0 +1,20 @@
1
+ import { RetrySuiteConfig, ConfigResult, ValidationResult } from '../types';
2
+ export declare class ConfigurationManager {
3
+ private config;
4
+ private configValidators;
5
+ constructor(initialConfig: RetrySuiteConfig);
6
+ private setupValidators;
7
+ updateConfiguration(updates: Partial<RetrySuiteConfig>): Promise<ConfigResult>;
8
+ getConfiguration(): RetrySuiteConfig;
9
+ validateConfiguration(config: RetrySuiteConfig): ValidationResult;
10
+ private validateRetryManager;
11
+ private validateRetryStorage;
12
+ private validateRetryMonitoring;
13
+ private validateAdmin;
14
+ private validateIntegrations;
15
+ private validateLogging;
16
+ private notifyConfigurationChange;
17
+ private getConfigChanges;
18
+ resetToDefaults(): RetrySuiteConfig;
19
+ private getDefaultConfiguration;
20
+ }