@felixgeelhaar/govee-api-client 1.1.0 → 2.0.1
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/README.md +160 -7
- package/dist/GoveeClient.d.ts +31 -0
- package/dist/GoveeClient.d.ts.map +1 -1
- package/dist/GoveeClient.js +33 -0
- package/dist/GoveeClient.js.map +1 -1
- package/dist/errors/GoveeApiError.d.ts +1 -1
- package/dist/errors/GoveeApiError.d.ts.map +1 -1
- package/dist/errors/GoveeApiError.js +9 -2
- package/dist/errors/GoveeApiError.js.map +1 -1
- package/dist/errors/ValidationError.d.ts +28 -0
- package/dist/errors/ValidationError.d.ts.map +1 -0
- package/dist/errors/ValidationError.js +44 -0
- package/dist/errors/ValidationError.js.map +1 -0
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +1 -0
- package/dist/errors/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/GoveeDeviceRepository.d.ts.map +1 -1
- package/dist/infrastructure/GoveeDeviceRepository.js +26 -3
- package/dist/infrastructure/GoveeDeviceRepository.js.map +1 -1
- package/dist/infrastructure/SlidingWindowRateLimiter.d.ts +83 -0
- package/dist/infrastructure/SlidingWindowRateLimiter.d.ts.map +1 -0
- package/dist/infrastructure/SlidingWindowRateLimiter.js +218 -0
- package/dist/infrastructure/SlidingWindowRateLimiter.js.map +1 -0
- package/dist/infrastructure/index.d.ts +2 -0
- package/dist/infrastructure/index.d.ts.map +1 -1
- package/dist/infrastructure/index.js +2 -0
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/infrastructure/response-schemas.d.ts +82 -0
- package/dist/infrastructure/response-schemas.d.ts.map +1 -0
- package/dist/infrastructure/response-schemas.js +59 -0
- package/dist/infrastructure/response-schemas.js.map +1 -0
- package/dist/infrastructure/retry/IntegrationGuide.d.ts +133 -0
- package/dist/infrastructure/retry/IntegrationGuide.d.ts.map +1 -0
- package/dist/infrastructure/retry/IntegrationGuide.js +295 -0
- package/dist/infrastructure/retry/IntegrationGuide.js.map +1 -0
- package/dist/infrastructure/retry/RetryConfigPresets.d.ts +114 -0
- package/dist/infrastructure/retry/RetryConfigPresets.d.ts.map +1 -0
- package/dist/infrastructure/retry/RetryConfigPresets.js +406 -0
- package/dist/infrastructure/retry/RetryConfigPresets.js.map +1 -0
- package/dist/infrastructure/retry/RetryPolicy.d.ts +148 -0
- package/dist/infrastructure/retry/RetryPolicy.d.ts.map +1 -0
- package/dist/infrastructure/retry/RetryPolicy.js +373 -0
- package/dist/infrastructure/retry/RetryPolicy.js.map +1 -0
- package/dist/infrastructure/retry/RetryableRepository.d.ts +75 -0
- package/dist/infrastructure/retry/RetryableRepository.d.ts.map +1 -0
- package/dist/infrastructure/retry/RetryableRepository.js +142 -0
- package/dist/infrastructure/retry/RetryableRepository.js.map +1 -0
- package/dist/infrastructure/retry/RetryableRequest.d.ts +132 -0
- package/dist/infrastructure/retry/RetryableRequest.d.ts.map +1 -0
- package/dist/infrastructure/retry/RetryableRequest.js +248 -0
- package/dist/infrastructure/retry/RetryableRequest.js.map +1 -0
- package/dist/infrastructure/retry/index.d.ts +35 -0
- package/dist/infrastructure/retry/index.d.ts.map +1 -0
- package/dist/infrastructure/retry/index.js +36 -0
- package/dist/infrastructure/retry/index.js.map +1 -0
- package/dist/services/GoveeControlService.d.ts +53 -0
- package/dist/services/GoveeControlService.d.ts.map +1 -1
- package/dist/services/GoveeControlService.js +138 -12
- package/dist/services/GoveeControlService.js.map +1 -1
- package/docs/EXAMPLES.md +799 -0
- package/docs/LLM_API_REFERENCE.md +425 -0
- package/docs/TYPE_DEFINITIONS.md +803 -0
- package/package.json +25 -16
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { RateLimitError, NetworkError, GoveeApiError, InvalidApiKeyError, } from '../../errors';
|
|
2
|
+
/**
|
|
3
|
+
* Circuit breaker state management
|
|
4
|
+
*/
|
|
5
|
+
class CircuitBreaker {
|
|
6
|
+
constructor(config, logger) {
|
|
7
|
+
this.config = config;
|
|
8
|
+
this.logger = logger;
|
|
9
|
+
this.state = 'closed';
|
|
10
|
+
this.failureCount = 0;
|
|
11
|
+
this.lastFailureTime = 0;
|
|
12
|
+
this.successCount = 0;
|
|
13
|
+
}
|
|
14
|
+
canExecute() {
|
|
15
|
+
const now = Date.now();
|
|
16
|
+
switch (this.state) {
|
|
17
|
+
case 'closed':
|
|
18
|
+
return true;
|
|
19
|
+
case 'open':
|
|
20
|
+
if (now - this.lastFailureTime >= this.config.recoveryTimeoutMs) {
|
|
21
|
+
this.logger?.info('Circuit breaker transitioning to half-open state');
|
|
22
|
+
this.state = 'half-open';
|
|
23
|
+
this.successCount = 0;
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
case 'half-open':
|
|
28
|
+
return true;
|
|
29
|
+
default:
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
recordSuccess() {
|
|
34
|
+
this.failureCount = 0;
|
|
35
|
+
if (this.state === 'half-open') {
|
|
36
|
+
this.successCount++;
|
|
37
|
+
if (this.successCount >= this.config.halfOpenSuccessThreshold) {
|
|
38
|
+
this.logger?.info('Circuit breaker transitioning to closed state');
|
|
39
|
+
this.state = 'closed';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
recordFailure() {
|
|
44
|
+
this.failureCount++;
|
|
45
|
+
this.lastFailureTime = Date.now();
|
|
46
|
+
if (this.state === 'closed' && this.failureCount >= this.config.failureThreshold) {
|
|
47
|
+
this.logger?.warn('Circuit breaker transitioning to open state');
|
|
48
|
+
this.state = 'open';
|
|
49
|
+
}
|
|
50
|
+
else if (this.state === 'half-open') {
|
|
51
|
+
this.logger?.warn('Circuit breaker returning to open state');
|
|
52
|
+
this.state = 'open';
|
|
53
|
+
this.successCount = 0;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
getState() {
|
|
57
|
+
return this.state;
|
|
58
|
+
}
|
|
59
|
+
reset() {
|
|
60
|
+
this.state = 'closed';
|
|
61
|
+
this.failureCount = 0;
|
|
62
|
+
this.lastFailureTime = 0;
|
|
63
|
+
this.successCount = 0;
|
|
64
|
+
this.logger?.info('Circuit breaker reset to closed state');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Enterprise-grade retry policy with exponential backoff, jitter, and circuit breaker
|
|
69
|
+
*/
|
|
70
|
+
export class RetryPolicy {
|
|
71
|
+
constructor(config) {
|
|
72
|
+
this.config = config;
|
|
73
|
+
this.logger = config.logger;
|
|
74
|
+
this.validateConfig();
|
|
75
|
+
if (config.circuitBreaker?.enabled) {
|
|
76
|
+
this.circuitBreaker = new CircuitBreaker(config.circuitBreaker, this.logger);
|
|
77
|
+
}
|
|
78
|
+
this.metrics = {
|
|
79
|
+
totalAttempts: 0,
|
|
80
|
+
successfulRetries: 0,
|
|
81
|
+
failedRetries: 0,
|
|
82
|
+
totalRetryTimeMs: 0,
|
|
83
|
+
averageRetryDelayMs: 0,
|
|
84
|
+
circuitBreakerState: this.circuitBreaker?.getState() || 'closed',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
validateConfig() {
|
|
88
|
+
const { backoff, condition, jitter } = this.config;
|
|
89
|
+
if (backoff.initialDelayMs <= 0 || backoff.maxDelayMs <= 0) {
|
|
90
|
+
throw new Error('Retry delays must be positive');
|
|
91
|
+
}
|
|
92
|
+
if (backoff.initialDelayMs > backoff.maxDelayMs) {
|
|
93
|
+
throw new Error('Initial delay cannot exceed maximum delay');
|
|
94
|
+
}
|
|
95
|
+
if (condition.maxAttempts <= 0 || condition.maxTotalTimeMs <= 0) {
|
|
96
|
+
throw new Error('Retry limits must be positive');
|
|
97
|
+
}
|
|
98
|
+
if (jitter.factor !== undefined && (jitter.factor < 0 || jitter.factor > 1)) {
|
|
99
|
+
throw new Error('Jitter factor must be between 0 and 1');
|
|
100
|
+
}
|
|
101
|
+
if (backoff.type === 'exponential' && (backoff.multiplier || 2) <= 1) {
|
|
102
|
+
throw new Error('Exponential backoff multiplier must be greater than 1');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Determines if an error should trigger a retry attempt
|
|
107
|
+
*/
|
|
108
|
+
shouldRetry(error, attempt, elapsedTimeMs) {
|
|
109
|
+
const { condition } = this.config;
|
|
110
|
+
// Check circuit breaker
|
|
111
|
+
if (this.circuitBreaker && !this.circuitBreaker.canExecute()) {
|
|
112
|
+
this.logger?.debug('Retry blocked by circuit breaker');
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
// Check attempt limit
|
|
116
|
+
if (attempt >= condition.maxAttempts) {
|
|
117
|
+
this.logger?.debug(`Maximum attempts reached: ${attempt}/${condition.maxAttempts}`);
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
// Check time limit
|
|
121
|
+
if (elapsedTimeMs >= condition.maxTotalTimeMs) {
|
|
122
|
+
this.logger?.debug(`Maximum retry time exceeded: ${elapsedTimeMs}ms/${condition.maxTotalTimeMs}ms`);
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
// Custom retry decision
|
|
126
|
+
if (condition.shouldRetry) {
|
|
127
|
+
return condition.shouldRetry(error, attempt, elapsedTimeMs);
|
|
128
|
+
}
|
|
129
|
+
// Check if error type is retryable
|
|
130
|
+
const isRetryableType = condition.retryableErrorTypes.some(ErrorType => error instanceof ErrorType);
|
|
131
|
+
if (!isRetryableType) {
|
|
132
|
+
this.logger?.debug(`Error type not retryable: ${error.constructor.name}`);
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
// Specific retry logic based on error type
|
|
136
|
+
if (error instanceof RateLimitError) {
|
|
137
|
+
return error.canRetry();
|
|
138
|
+
}
|
|
139
|
+
if (error instanceof NetworkError) {
|
|
140
|
+
return error.isRetryable();
|
|
141
|
+
}
|
|
142
|
+
if (error instanceof GoveeApiError) {
|
|
143
|
+
// Retry on server errors (5xx) and some client errors
|
|
144
|
+
const retryableStatusCodes = [
|
|
145
|
+
408,
|
|
146
|
+
429,
|
|
147
|
+
500,
|
|
148
|
+
502,
|
|
149
|
+
503,
|
|
150
|
+
504,
|
|
151
|
+
...condition.retryableStatusCodes,
|
|
152
|
+
];
|
|
153
|
+
return retryableStatusCodes.includes(error.statusCode);
|
|
154
|
+
}
|
|
155
|
+
if (error instanceof InvalidApiKeyError) {
|
|
156
|
+
// Never retry authentication errors
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Calculates the delay before the next retry attempt
|
|
163
|
+
*/
|
|
164
|
+
calculateDelay(attempt, error) {
|
|
165
|
+
const { backoff, jitter } = this.config;
|
|
166
|
+
// Handle rate limit errors with specific retry-after values
|
|
167
|
+
if (error instanceof RateLimitError && error.canRetry()) {
|
|
168
|
+
const rateLimitDelay = error.getRetryAfterMs();
|
|
169
|
+
return this.applyJitter(rateLimitDelay, attempt);
|
|
170
|
+
}
|
|
171
|
+
let baseDelay;
|
|
172
|
+
switch (backoff.type) {
|
|
173
|
+
case 'fixed':
|
|
174
|
+
baseDelay = backoff.initialDelayMs;
|
|
175
|
+
break;
|
|
176
|
+
case 'linear':
|
|
177
|
+
baseDelay = backoff.initialDelayMs * attempt;
|
|
178
|
+
break;
|
|
179
|
+
case 'exponential':
|
|
180
|
+
const multiplier = backoff.multiplier || 2;
|
|
181
|
+
baseDelay = backoff.initialDelayMs * Math.pow(multiplier, attempt - 1);
|
|
182
|
+
break;
|
|
183
|
+
case 'custom':
|
|
184
|
+
if (!backoff.customBackoff) {
|
|
185
|
+
throw new Error('Custom backoff function not provided');
|
|
186
|
+
}
|
|
187
|
+
baseDelay = backoff.customBackoff(attempt, error);
|
|
188
|
+
break;
|
|
189
|
+
default:
|
|
190
|
+
throw new Error(`Unknown backoff type: ${backoff.type}`);
|
|
191
|
+
}
|
|
192
|
+
// Ensure delay doesn't exceed maximum
|
|
193
|
+
baseDelay = Math.min(baseDelay, backoff.maxDelayMs);
|
|
194
|
+
return this.applyJitter(baseDelay, attempt);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Applies jitter to prevent thundering herd problems
|
|
198
|
+
*/
|
|
199
|
+
applyJitter(baseDelay, attempt) {
|
|
200
|
+
const { jitter } = this.config;
|
|
201
|
+
const factor = jitter.factor || 0.1;
|
|
202
|
+
switch (jitter.type) {
|
|
203
|
+
case 'none':
|
|
204
|
+
return baseDelay;
|
|
205
|
+
case 'full':
|
|
206
|
+
// Random value between 0 and baseDelay
|
|
207
|
+
return Math.random() * baseDelay;
|
|
208
|
+
case 'equal':
|
|
209
|
+
// baseDelay/2 + random(0, baseDelay/2)
|
|
210
|
+
return baseDelay / 2 + Math.random() * (baseDelay / 2);
|
|
211
|
+
case 'decorrelated':
|
|
212
|
+
// More sophisticated jitter that considers previous delays
|
|
213
|
+
const minDelay = this.config.backoff.initialDelayMs;
|
|
214
|
+
const maxJitter = baseDelay * factor;
|
|
215
|
+
return Math.min(this.config.backoff.maxDelayMs, Math.random() * (baseDelay * 3 - minDelay) + minDelay);
|
|
216
|
+
default:
|
|
217
|
+
throw new Error(`Unknown jitter type: ${jitter.type}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Records a successful operation
|
|
222
|
+
*/
|
|
223
|
+
recordSuccess() {
|
|
224
|
+
this.circuitBreaker?.recordSuccess();
|
|
225
|
+
this.updateMetrics(true);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Records a failed operation
|
|
229
|
+
*/
|
|
230
|
+
recordFailure(error) {
|
|
231
|
+
this.circuitBreaker?.recordFailure();
|
|
232
|
+
this.updateMetrics(false, error);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Updates retry metrics
|
|
236
|
+
*/
|
|
237
|
+
updateMetrics(success, error) {
|
|
238
|
+
if (!this.config.enableMetrics)
|
|
239
|
+
return;
|
|
240
|
+
this.metrics.totalAttempts++;
|
|
241
|
+
this.metrics.circuitBreakerState = this.circuitBreaker?.getState() || 'closed';
|
|
242
|
+
this.metrics.lastRetryTimestamp = new Date();
|
|
243
|
+
if (success) {
|
|
244
|
+
this.metrics.successfulRetries++;
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
this.metrics.failedRetries++;
|
|
248
|
+
this.metrics.lastError = error;
|
|
249
|
+
}
|
|
250
|
+
// Update average delay (simplified calculation)
|
|
251
|
+
if (this.metrics.totalAttempts > 1) {
|
|
252
|
+
this.metrics.averageRetryDelayMs =
|
|
253
|
+
this.metrics.totalRetryTimeMs / (this.metrics.totalAttempts - 1);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Gets current retry metrics
|
|
258
|
+
*/
|
|
259
|
+
getMetrics() {
|
|
260
|
+
return { ...this.metrics };
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Resets retry metrics and circuit breaker
|
|
264
|
+
*/
|
|
265
|
+
reset() {
|
|
266
|
+
this.circuitBreaker?.reset();
|
|
267
|
+
Object.assign(this.metrics, {
|
|
268
|
+
totalAttempts: 0,
|
|
269
|
+
successfulRetries: 0,
|
|
270
|
+
failedRetries: 0,
|
|
271
|
+
totalRetryTimeMs: 0,
|
|
272
|
+
averageRetryDelayMs: 0,
|
|
273
|
+
circuitBreakerState: this.circuitBreaker?.getState() || 'closed',
|
|
274
|
+
lastError: undefined,
|
|
275
|
+
lastRetryTimestamp: undefined,
|
|
276
|
+
});
|
|
277
|
+
this.logger?.info('Retry policy metrics and circuit breaker reset');
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Creates a default retry policy optimized for Govee API
|
|
281
|
+
*/
|
|
282
|
+
static createGoveeOptimized(logger) {
|
|
283
|
+
return new RetryPolicy({
|
|
284
|
+
backoff: {
|
|
285
|
+
type: 'exponential',
|
|
286
|
+
initialDelayMs: 1000, // Start with 1 second
|
|
287
|
+
maxDelayMs: 30000, // Cap at 30 seconds
|
|
288
|
+
multiplier: 2.0,
|
|
289
|
+
},
|
|
290
|
+
jitter: {
|
|
291
|
+
type: 'equal',
|
|
292
|
+
factor: 0.1,
|
|
293
|
+
},
|
|
294
|
+
condition: {
|
|
295
|
+
maxAttempts: 3,
|
|
296
|
+
maxTotalTimeMs: 60000, // 1 minute total
|
|
297
|
+
retryableStatusCodes: [408, 502, 503, 504],
|
|
298
|
+
retryableErrorTypes: [RateLimitError, NetworkError, GoveeApiError],
|
|
299
|
+
},
|
|
300
|
+
circuitBreaker: {
|
|
301
|
+
enabled: true,
|
|
302
|
+
failureThreshold: 5,
|
|
303
|
+
recoveryTimeoutMs: 30000,
|
|
304
|
+
halfOpenSuccessThreshold: 2,
|
|
305
|
+
},
|
|
306
|
+
logger,
|
|
307
|
+
enableMetrics: true,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Creates a conservative retry policy for production environments
|
|
312
|
+
*/
|
|
313
|
+
static createConservative(logger) {
|
|
314
|
+
return new RetryPolicy({
|
|
315
|
+
backoff: {
|
|
316
|
+
type: 'exponential',
|
|
317
|
+
initialDelayMs: 2000,
|
|
318
|
+
maxDelayMs: 60000,
|
|
319
|
+
multiplier: 1.5,
|
|
320
|
+
},
|
|
321
|
+
jitter: {
|
|
322
|
+
type: 'decorrelated',
|
|
323
|
+
factor: 0.2,
|
|
324
|
+
},
|
|
325
|
+
condition: {
|
|
326
|
+
maxAttempts: 2,
|
|
327
|
+
maxTotalTimeMs: 120000,
|
|
328
|
+
retryableStatusCodes: [429, 502, 503, 504],
|
|
329
|
+
retryableErrorTypes: [RateLimitError, NetworkError],
|
|
330
|
+
},
|
|
331
|
+
circuitBreaker: {
|
|
332
|
+
enabled: true,
|
|
333
|
+
failureThreshold: 3,
|
|
334
|
+
recoveryTimeoutMs: 60000,
|
|
335
|
+
halfOpenSuccessThreshold: 3,
|
|
336
|
+
},
|
|
337
|
+
logger,
|
|
338
|
+
enableMetrics: true,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Creates an aggressive retry policy for development/testing
|
|
343
|
+
*/
|
|
344
|
+
static createAggressive(logger) {
|
|
345
|
+
return new RetryPolicy({
|
|
346
|
+
backoff: {
|
|
347
|
+
type: 'exponential',
|
|
348
|
+
initialDelayMs: 500,
|
|
349
|
+
maxDelayMs: 15000,
|
|
350
|
+
multiplier: 2.5,
|
|
351
|
+
},
|
|
352
|
+
jitter: {
|
|
353
|
+
type: 'full',
|
|
354
|
+
factor: 0.3,
|
|
355
|
+
},
|
|
356
|
+
condition: {
|
|
357
|
+
maxAttempts: 5,
|
|
358
|
+
maxTotalTimeMs: 45000,
|
|
359
|
+
retryableStatusCodes: [400, 408, 429, 500, 502, 503, 504],
|
|
360
|
+
retryableErrorTypes: [RateLimitError, NetworkError, GoveeApiError],
|
|
361
|
+
},
|
|
362
|
+
circuitBreaker: {
|
|
363
|
+
enabled: false,
|
|
364
|
+
failureThreshold: 10,
|
|
365
|
+
recoveryTimeoutMs: 15000,
|
|
366
|
+
halfOpenSuccessThreshold: 1,
|
|
367
|
+
},
|
|
368
|
+
logger,
|
|
369
|
+
enableMetrics: true,
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
//# sourceMappingURL=RetryPolicy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RetryPolicy.js","sourceRoot":"","sources":["../../../src/infrastructure/retry/RetryPolicy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Logger } from 'pino';
|
|
2
|
+
import { IGoveeDeviceRepository } from '../../domain/repositories/IGoveeDeviceRepository';
|
|
3
|
+
import { GoveeDevice } from '../../domain/entities/GoveeDevice';
|
|
4
|
+
import { DeviceState } from '../../domain/entities/DeviceState';
|
|
5
|
+
import { Command } from '../../domain/entities/Command';
|
|
6
|
+
import { RetryExecutor, RetryResult } from './RetryableRequest';
|
|
7
|
+
/**
|
|
8
|
+
* Configuration for RetryableRepository
|
|
9
|
+
*/
|
|
10
|
+
export interface RetryableRepositoryConfig {
|
|
11
|
+
/** The underlying repository to wrap */
|
|
12
|
+
repository: IGoveeDeviceRepository;
|
|
13
|
+
/** Retry executor to use */
|
|
14
|
+
retryExecutor?: RetryExecutor;
|
|
15
|
+
/** Logger for retry operations */
|
|
16
|
+
logger?: Logger;
|
|
17
|
+
/** Enable request ID generation */
|
|
18
|
+
enableRequestIds?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Repository wrapper that adds retry functionality to any IGoveeDeviceRepository implementation
|
|
22
|
+
*/
|
|
23
|
+
export declare class RetryableRepository implements IGoveeDeviceRepository {
|
|
24
|
+
private readonly repository;
|
|
25
|
+
private readonly retryExecutor;
|
|
26
|
+
private readonly logger?;
|
|
27
|
+
private readonly enableRequestIds;
|
|
28
|
+
private requestIdCounter;
|
|
29
|
+
constructor(config: RetryableRepositoryConfig);
|
|
30
|
+
/**
|
|
31
|
+
* Generates a unique request ID
|
|
32
|
+
*/
|
|
33
|
+
private generateRequestId;
|
|
34
|
+
/**
|
|
35
|
+
* Finds all devices with retry logic
|
|
36
|
+
*/
|
|
37
|
+
findAll(): Promise<GoveeDevice[]>;
|
|
38
|
+
/**
|
|
39
|
+
* Finds device state with retry logic
|
|
40
|
+
*/
|
|
41
|
+
findState(deviceId: string, sku: string): Promise<DeviceState>;
|
|
42
|
+
/**
|
|
43
|
+
* Sends command with retry logic
|
|
44
|
+
*/
|
|
45
|
+
sendCommand(deviceId: string, sku: string, command: Command): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Gets detailed retry result for findAll operation
|
|
48
|
+
*/
|
|
49
|
+
findAllWithRetryResult(): Promise<RetryResult<GoveeDevice[]>>;
|
|
50
|
+
/**
|
|
51
|
+
* Gets detailed retry result for findState operation
|
|
52
|
+
*/
|
|
53
|
+
findStateWithRetryResult(deviceId: string, sku: string): Promise<RetryResult<DeviceState>>;
|
|
54
|
+
/**
|
|
55
|
+
* Gets detailed retry result for sendCommand operation
|
|
56
|
+
*/
|
|
57
|
+
sendCommandWithRetryResult(deviceId: string, sku: string, command: Command): Promise<RetryResult<void>>;
|
|
58
|
+
/**
|
|
59
|
+
* Gets current retry metrics
|
|
60
|
+
*/
|
|
61
|
+
getRetryMetrics(): Readonly<import("./RetryPolicy").RetryMetrics>;
|
|
62
|
+
/**
|
|
63
|
+
* Resets retry metrics
|
|
64
|
+
*/
|
|
65
|
+
resetRetryMetrics(): void;
|
|
66
|
+
/**
|
|
67
|
+
* Gets the underlying repository (for direct access when needed)
|
|
68
|
+
*/
|
|
69
|
+
getUnderlyingRepository(): IGoveeDeviceRepository;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Import the RetryResult type for convenience
|
|
73
|
+
*/
|
|
74
|
+
export { RetryResult, RetryAttempt } from './RetryableRequest';
|
|
75
|
+
//# sourceMappingURL=RetryableRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RetryableRepository.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/retry/RetryableRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAC9B,OAAO,EAAE,sBAAsB,EAAE,MAAM,kDAAkD,CAAC;AAC1F,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACxD,OAAO,EACL,aAAa,EAGb,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAG5B;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,wCAAwC;IACxC,UAAU,EAAE,sBAAsB,CAAC;IACnC,4BAA4B;IAC5B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;GAEG;AACH,qBAAa,mBAAoB,YAAW,sBAAsB;IAChE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyB;IACpD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAC3C,OAAO,CAAC,gBAAgB,CAAK;gBAEjB,MAAM,EAAE,yBAAyB;IAQ7C;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAcvC;;OAEG;IACG,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAgBpE;;OAEG;IACG,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBjF;;OAEG;IACG,sBAAsB,IAAI,OAAO,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;IAcnE;;OAEG;IACG,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IAgBhG;;OAEG;IACG,0BAA0B,CAC9B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAiB7B;;OAEG;IACH,eAAe;IAIf;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAIzB;;OAEG;IACH,uBAAuB,IAAI,sBAAsB;CAGlD;AAED;;GAEG;AACH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { RetryExecutorFactory, } from './RetryableRequest';
|
|
2
|
+
/**
|
|
3
|
+
* Repository wrapper that adds retry functionality to any IGoveeDeviceRepository implementation
|
|
4
|
+
*/
|
|
5
|
+
export class RetryableRepository {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.requestIdCounter = 0;
|
|
8
|
+
this.repository = config.repository;
|
|
9
|
+
this.retryExecutor =
|
|
10
|
+
config.retryExecutor || RetryExecutorFactory.createForGoveeApi(config.logger);
|
|
11
|
+
this.logger = config.logger;
|
|
12
|
+
this.enableRequestIds = config.enableRequestIds ?? true;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Generates a unique request ID
|
|
16
|
+
*/
|
|
17
|
+
generateRequestId(operation) {
|
|
18
|
+
if (!this.enableRequestIds) {
|
|
19
|
+
return `${operation}-${Date.now()}`;
|
|
20
|
+
}
|
|
21
|
+
return `${operation}-${++this.requestIdCounter}-${Date.now()}`;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Finds all devices with retry logic
|
|
25
|
+
*/
|
|
26
|
+
async findAll() {
|
|
27
|
+
const request = {
|
|
28
|
+
id: this.generateRequestId('findAll'),
|
|
29
|
+
description: 'Fetch all Govee devices',
|
|
30
|
+
execute: () => this.repository.findAll(),
|
|
31
|
+
context: {
|
|
32
|
+
operation: 'findAll',
|
|
33
|
+
timestamp: new Date().toISOString(),
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
return this.retryExecutor.execute(request);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Finds device state with retry logic
|
|
40
|
+
*/
|
|
41
|
+
async findState(deviceId, sku) {
|
|
42
|
+
const request = {
|
|
43
|
+
id: this.generateRequestId('findState'),
|
|
44
|
+
description: `Fetch state for device ${deviceId} (${sku})`,
|
|
45
|
+
execute: () => this.repository.findState(deviceId, sku),
|
|
46
|
+
context: {
|
|
47
|
+
operation: 'findState',
|
|
48
|
+
deviceId,
|
|
49
|
+
sku,
|
|
50
|
+
timestamp: new Date().toISOString(),
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
return this.retryExecutor.execute(request);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Sends command with retry logic
|
|
57
|
+
*/
|
|
58
|
+
async sendCommand(deviceId, sku, command) {
|
|
59
|
+
const request = {
|
|
60
|
+
id: this.generateRequestId('sendCommand'),
|
|
61
|
+
description: `Send ${command.toObject().name} command to device ${deviceId} (${sku})`,
|
|
62
|
+
execute: () => this.repository.sendCommand(deviceId, sku, command),
|
|
63
|
+
context: {
|
|
64
|
+
operation: 'sendCommand',
|
|
65
|
+
deviceId,
|
|
66
|
+
sku,
|
|
67
|
+
command: command.toObject(),
|
|
68
|
+
timestamp: new Date().toISOString(),
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
return this.retryExecutor.execute(request);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Gets detailed retry result for findAll operation
|
|
75
|
+
*/
|
|
76
|
+
async findAllWithRetryResult() {
|
|
77
|
+
const request = {
|
|
78
|
+
id: this.generateRequestId('findAll'),
|
|
79
|
+
description: 'Fetch all Govee devices (with retry details)',
|
|
80
|
+
execute: () => this.repository.findAll(),
|
|
81
|
+
context: {
|
|
82
|
+
operation: 'findAll',
|
|
83
|
+
timestamp: new Date().toISOString(),
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
return this.retryExecutor.executeWithResult(request);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Gets detailed retry result for findState operation
|
|
90
|
+
*/
|
|
91
|
+
async findStateWithRetryResult(deviceId, sku) {
|
|
92
|
+
const request = {
|
|
93
|
+
id: this.generateRequestId('findState'),
|
|
94
|
+
description: `Fetch state for device ${deviceId} (${sku}) with retry details`,
|
|
95
|
+
execute: () => this.repository.findState(deviceId, sku),
|
|
96
|
+
context: {
|
|
97
|
+
operation: 'findState',
|
|
98
|
+
deviceId,
|
|
99
|
+
sku,
|
|
100
|
+
timestamp: new Date().toISOString(),
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
return this.retryExecutor.executeWithResult(request);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Gets detailed retry result for sendCommand operation
|
|
107
|
+
*/
|
|
108
|
+
async sendCommandWithRetryResult(deviceId, sku, command) {
|
|
109
|
+
const request = {
|
|
110
|
+
id: this.generateRequestId('sendCommand'),
|
|
111
|
+
description: `Send ${command.toObject().name} command to device ${deviceId} (${sku}) with retry details`,
|
|
112
|
+
execute: () => this.repository.sendCommand(deviceId, sku, command),
|
|
113
|
+
context: {
|
|
114
|
+
operation: 'sendCommand',
|
|
115
|
+
deviceId,
|
|
116
|
+
sku,
|
|
117
|
+
command: command.toObject(),
|
|
118
|
+
timestamp: new Date().toISOString(),
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
return this.retryExecutor.executeWithResult(request);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Gets current retry metrics
|
|
125
|
+
*/
|
|
126
|
+
getRetryMetrics() {
|
|
127
|
+
return this.retryExecutor.getMetrics();
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Resets retry metrics
|
|
131
|
+
*/
|
|
132
|
+
resetRetryMetrics() {
|
|
133
|
+
this.retryExecutor.resetMetrics();
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Gets the underlying repository (for direct access when needed)
|
|
137
|
+
*/
|
|
138
|
+
getUnderlyingRepository() {
|
|
139
|
+
return this.repository;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=RetryableRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RetryableRepository.js","sourceRoot":"","sources":["../../../src/infrastructure/retry/RetryableRepository.ts"],"names":[],"mappings":"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"}
|