@revealui/resilience 0.2.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/dist/index.js ADDED
@@ -0,0 +1,915 @@
1
+ // src/logger.ts
2
+ var resilienceLogger = console;
3
+ function configureResilienceLogger(logger) {
4
+ resilienceLogger = logger;
5
+ }
6
+ function getResilienceLogger() {
7
+ return resilienceLogger;
8
+ }
9
+
10
+ // src/circuit-breaker.ts
11
+ var DEFAULT_CONFIG = {
12
+ failureThreshold: 5,
13
+ successThreshold: 2,
14
+ timeout: 6e4,
15
+ resetTimeout: 3e4,
16
+ volumeThreshold: 10,
17
+ errorFilter: () => true,
18
+ onStateChange: () => {
19
+ },
20
+ onTrip: () => {
21
+ },
22
+ onReset: () => {
23
+ }
24
+ };
25
+ var CircuitBreaker = class {
26
+ state = "closed";
27
+ failures = 0;
28
+ successes = 0;
29
+ consecutiveFailures = 0;
30
+ consecutiveSuccesses = 0;
31
+ totalCalls = 0;
32
+ totalFailures = 0;
33
+ totalSuccesses = 0;
34
+ lastFailureTime;
35
+ lastSuccessTime;
36
+ stateChangedAt = Date.now();
37
+ resetTimer;
38
+ config;
39
+ constructor(config = {}) {
40
+ this.config = { ...DEFAULT_CONFIG, ...config };
41
+ }
42
+ /**
43
+ * Execute function with circuit breaker
44
+ */
45
+ async execute(fn) {
46
+ if (this.state === "open") {
47
+ if (Date.now() - this.stateChangedAt >= this.config.resetTimeout) {
48
+ this.transitionTo("half-open");
49
+ } else {
50
+ throw new CircuitBreakerOpenError("Circuit breaker is open");
51
+ }
52
+ }
53
+ this.totalCalls++;
54
+ try {
55
+ const result = await fn();
56
+ this.onSuccess();
57
+ return result;
58
+ } catch (error) {
59
+ const err = error instanceof Error ? error : new Error(String(error));
60
+ this.onFailure(err);
61
+ throw error;
62
+ }
63
+ }
64
+ /**
65
+ * Handle successful execution
66
+ */
67
+ onSuccess() {
68
+ this.successes++;
69
+ this.consecutiveSuccesses++;
70
+ this.totalSuccesses++;
71
+ this.consecutiveFailures = 0;
72
+ this.lastSuccessTime = Date.now();
73
+ if (this.state === "half-open") {
74
+ if (this.consecutiveSuccesses >= this.config.successThreshold) {
75
+ this.transitionTo("closed");
76
+ }
77
+ }
78
+ if (this.state === "closed") {
79
+ this.failures = 0;
80
+ }
81
+ }
82
+ /**
83
+ * Handle failed execution
84
+ */
85
+ onFailure(error) {
86
+ if (!this.config.errorFilter(error)) {
87
+ return;
88
+ }
89
+ this.failures++;
90
+ this.consecutiveFailures++;
91
+ this.totalFailures++;
92
+ this.consecutiveSuccesses = 0;
93
+ this.lastFailureTime = Date.now();
94
+ if (this.state === "half-open") {
95
+ this.transitionTo("open");
96
+ } else if (this.state === "closed") {
97
+ if (this.consecutiveFailures >= this.config.failureThreshold && this.totalCalls >= this.config.volumeThreshold) {
98
+ this.transitionTo("open");
99
+ }
100
+ }
101
+ }
102
+ /**
103
+ * Transition to new state
104
+ */
105
+ transitionTo(newState) {
106
+ if (this.state === newState) return;
107
+ const oldState = this.state;
108
+ this.state = newState;
109
+ this.stateChangedAt = Date.now();
110
+ if (newState === "half-open" || newState === "closed") {
111
+ this.consecutiveFailures = 0;
112
+ this.consecutiveSuccesses = 0;
113
+ }
114
+ if (this.resetTimer) {
115
+ clearTimeout(this.resetTimer);
116
+ this.resetTimer = void 0;
117
+ }
118
+ if (newState === "open") {
119
+ this.resetTimer = setTimeout(() => {
120
+ this.transitionTo("half-open");
121
+ }, this.config.resetTimeout);
122
+ this.config.onTrip();
123
+ }
124
+ if (newState === "closed" && oldState === "half-open") {
125
+ this.failures = 0;
126
+ this.config.onReset();
127
+ }
128
+ this.config.onStateChange(newState);
129
+ getResilienceLogger().info(
130
+ `Circuit breaker state changed: ${oldState} -> ${newState}`,
131
+ this.getStats()
132
+ );
133
+ }
134
+ /**
135
+ * Get current state
136
+ */
137
+ getState() {
138
+ return this.state;
139
+ }
140
+ /**
141
+ * Get statistics
142
+ */
143
+ getStats() {
144
+ return {
145
+ state: this.state,
146
+ failures: this.failures,
147
+ successes: this.successes,
148
+ consecutiveFailures: this.consecutiveFailures,
149
+ consecutiveSuccesses: this.consecutiveSuccesses,
150
+ totalCalls: this.totalCalls,
151
+ totalFailures: this.totalFailures,
152
+ totalSuccesses: this.totalSuccesses,
153
+ lastFailureTime: this.lastFailureTime,
154
+ lastSuccessTime: this.lastSuccessTime,
155
+ stateChangedAt: this.stateChangedAt
156
+ };
157
+ }
158
+ /**
159
+ * Manually open circuit
160
+ */
161
+ trip() {
162
+ this.transitionTo("open");
163
+ }
164
+ /**
165
+ * Manually close circuit
166
+ */
167
+ reset() {
168
+ this.failures = 0;
169
+ this.successes = 0;
170
+ this.consecutiveFailures = 0;
171
+ this.consecutiveSuccesses = 0;
172
+ this.transitionTo("closed");
173
+ }
174
+ /**
175
+ * Force state to half-open
176
+ */
177
+ halfOpen() {
178
+ this.transitionTo("half-open");
179
+ }
180
+ /**
181
+ * Check if circuit is open
182
+ */
183
+ isOpen() {
184
+ return this.state === "open";
185
+ }
186
+ /**
187
+ * Check if circuit is closed
188
+ */
189
+ isClosed() {
190
+ return this.state === "closed";
191
+ }
192
+ /**
193
+ * Check if circuit is half-open
194
+ */
195
+ isHalfOpen() {
196
+ return this.state === "half-open";
197
+ }
198
+ /**
199
+ * Get failure rate
200
+ */
201
+ getFailureRate() {
202
+ if (this.totalCalls === 0) return 0;
203
+ return this.totalFailures / this.totalCalls;
204
+ }
205
+ /**
206
+ * Get success rate
207
+ */
208
+ getSuccessRate() {
209
+ if (this.totalCalls === 0) return 0;
210
+ return this.totalSuccesses / this.totalCalls;
211
+ }
212
+ /**
213
+ * Cleanup
214
+ */
215
+ destroy() {
216
+ if (this.resetTimer) {
217
+ clearTimeout(this.resetTimer);
218
+ }
219
+ }
220
+ };
221
+ var CircuitBreakerOpenError = class extends Error {
222
+ constructor(message = "Circuit breaker is open") {
223
+ super(message);
224
+ this.name = "CircuitBreakerOpenError";
225
+ }
226
+ };
227
+ var CircuitBreakerRegistry = class {
228
+ breakers = /* @__PURE__ */ new Map();
229
+ /**
230
+ * Get or create circuit breaker
231
+ */
232
+ get(name, config) {
233
+ let breaker = this.breakers.get(name);
234
+ if (!breaker) {
235
+ breaker = new CircuitBreaker(config);
236
+ this.breakers.set(name, breaker);
237
+ }
238
+ return breaker;
239
+ }
240
+ /**
241
+ * Check if breaker exists
242
+ */
243
+ has(name) {
244
+ return this.breakers.has(name);
245
+ }
246
+ /**
247
+ * Remove circuit breaker
248
+ */
249
+ remove(name) {
250
+ const breaker = this.breakers.get(name);
251
+ if (breaker) {
252
+ breaker.destroy();
253
+ return this.breakers.delete(name);
254
+ }
255
+ return false;
256
+ }
257
+ /**
258
+ * Get all breakers
259
+ */
260
+ getAll() {
261
+ return new Map(this.breakers);
262
+ }
263
+ /**
264
+ * Get all statistics
265
+ */
266
+ getAllStats() {
267
+ const stats = {};
268
+ for (const [name, breaker] of this.breakers) {
269
+ stats[name] = breaker.getStats();
270
+ }
271
+ return stats;
272
+ }
273
+ /**
274
+ * Reset all breakers
275
+ */
276
+ resetAll() {
277
+ for (const breaker of this.breakers.values()) {
278
+ breaker.reset();
279
+ }
280
+ }
281
+ /**
282
+ * Clear all breakers
283
+ */
284
+ clear() {
285
+ for (const breaker of this.breakers.values()) {
286
+ breaker.destroy();
287
+ }
288
+ this.breakers.clear();
289
+ }
290
+ };
291
+ var circuitBreakerRegistry = new CircuitBreakerRegistry();
292
+ function CircuitBreak(nameOrConfig = {}) {
293
+ return (target, propertyKey, descriptor) => {
294
+ const originalMethod = descriptor.value;
295
+ const name = typeof nameOrConfig === "string" ? nameOrConfig : `${target.constructor.name}.${propertyKey}`;
296
+ const config = typeof nameOrConfig === "object" ? nameOrConfig : void 0;
297
+ descriptor.value = async function(...args) {
298
+ const breaker = circuitBreakerRegistry.get(name, config);
299
+ return breaker.execute(() => originalMethod.apply(this, args));
300
+ };
301
+ return descriptor;
302
+ };
303
+ }
304
+ async function withCircuitBreaker(name, fn, config) {
305
+ const breaker = circuitBreakerRegistry.get(name, config);
306
+ return breaker.execute(fn);
307
+ }
308
+ function createCircuitBreakerMiddleware(name, config) {
309
+ const breaker = circuitBreakerRegistry.get(name, config);
310
+ return async (_request, next) => {
311
+ return breaker.execute(next);
312
+ };
313
+ }
314
+ async function fetchWithCircuitBreaker(name, url, init, config) {
315
+ const breaker = circuitBreakerRegistry.get(name, config);
316
+ return breaker.execute(async () => {
317
+ const response = await fetch(url, init);
318
+ if (response.status >= 500) {
319
+ const error = new Error(`HTTP ${response.status}: ${response.statusText}`);
320
+ error.statusCode = response.status;
321
+ throw error;
322
+ }
323
+ return response;
324
+ });
325
+ }
326
+ var AdaptiveCircuitBreaker = class extends CircuitBreaker {
327
+ errorRateWindow = [];
328
+ windowSize = 100;
329
+ adaptiveThreshold;
330
+ constructor(config = {}) {
331
+ super(config);
332
+ this.adaptiveThreshold = config.failureThreshold || 5;
333
+ }
334
+ /**
335
+ * Execute with adaptive thresholds
336
+ */
337
+ async execute(fn) {
338
+ try {
339
+ const result = await super.execute(fn);
340
+ this.recordSuccess();
341
+ return result;
342
+ } catch (error) {
343
+ this.recordFailure();
344
+ throw error;
345
+ }
346
+ }
347
+ /**
348
+ * Record success in window
349
+ */
350
+ recordSuccess() {
351
+ this.errorRateWindow.push(0);
352
+ this.trimWindow();
353
+ this.adjustThreshold();
354
+ }
355
+ /**
356
+ * Record failure in window
357
+ */
358
+ recordFailure() {
359
+ this.errorRateWindow.push(1);
360
+ this.trimWindow();
361
+ this.adjustThreshold();
362
+ }
363
+ /**
364
+ * Trim window to size
365
+ */
366
+ trimWindow() {
367
+ if (this.errorRateWindow.length > this.windowSize) {
368
+ this.errorRateWindow.shift();
369
+ }
370
+ }
371
+ /**
372
+ * Adjust threshold based on error rate
373
+ */
374
+ adjustThreshold() {
375
+ const errorRate = this.getWindowErrorRate();
376
+ if (errorRate < 0.1) {
377
+ this.adaptiveThreshold = Math.min(this.adaptiveThreshold + 1, 20);
378
+ } else if (errorRate > 0.5) {
379
+ this.adaptiveThreshold = Math.max(this.adaptiveThreshold - 1, 2);
380
+ }
381
+ this.config.failureThreshold = this.adaptiveThreshold;
382
+ }
383
+ /**
384
+ * Get error rate in window
385
+ */
386
+ getWindowErrorRate() {
387
+ if (this.errorRateWindow.length === 0) return 0;
388
+ const errors = this.errorRateWindow.reduce((sum, val) => sum + val, 0);
389
+ return errors / this.errorRateWindow.length;
390
+ }
391
+ /**
392
+ * Get adaptive threshold
393
+ */
394
+ getAdaptiveThreshold() {
395
+ return this.adaptiveThreshold;
396
+ }
397
+ };
398
+ var Bulkhead = class {
399
+ activeRequests = 0;
400
+ queue = [];
401
+ maxConcurrent;
402
+ maxQueue;
403
+ constructor(maxConcurrent = 10, maxQueue = 100) {
404
+ this.maxConcurrent = maxConcurrent;
405
+ this.maxQueue = maxQueue;
406
+ }
407
+ /**
408
+ * Execute with bulkhead
409
+ */
410
+ async execute(fn) {
411
+ if (this.activeRequests >= this.maxConcurrent) {
412
+ if (this.queue.length >= this.maxQueue) {
413
+ throw new Error("Bulkhead queue is full");
414
+ }
415
+ await new Promise((resolve) => {
416
+ this.queue.push(resolve);
417
+ });
418
+ }
419
+ this.activeRequests++;
420
+ try {
421
+ return await fn();
422
+ } finally {
423
+ this.activeRequests--;
424
+ const next = this.queue.shift();
425
+ if (next) {
426
+ next();
427
+ }
428
+ }
429
+ }
430
+ /**
431
+ * Get active requests
432
+ */
433
+ getActiveRequests() {
434
+ return this.activeRequests;
435
+ }
436
+ /**
437
+ * Get queue size
438
+ */
439
+ getQueueSize() {
440
+ return this.queue.length;
441
+ }
442
+ /**
443
+ * Get statistics
444
+ */
445
+ getStats() {
446
+ return {
447
+ activeRequests: this.activeRequests,
448
+ queueSize: this.queue.length,
449
+ maxConcurrent: this.maxConcurrent,
450
+ maxQueue: this.maxQueue
451
+ };
452
+ }
453
+ };
454
+ var ResilientOperation = class {
455
+ constructor(fn, circuitBreaker, bulkhead) {
456
+ this.fn = fn;
457
+ this.circuitBreaker = circuitBreaker;
458
+ this.bulkhead = bulkhead;
459
+ }
460
+ /**
461
+ * Execute with all resilience patterns
462
+ */
463
+ async execute() {
464
+ const executeFn = async () => {
465
+ if (this.bulkhead) {
466
+ return this.bulkhead.execute(this.fn);
467
+ }
468
+ return this.fn();
469
+ };
470
+ if (this.circuitBreaker) {
471
+ return this.circuitBreaker.execute(executeFn);
472
+ }
473
+ return executeFn();
474
+ }
475
+ };
476
+ function createResilientFunction(fn, options = {}) {
477
+ const breaker = options.circuitBreaker ? new CircuitBreaker(options.circuitBreaker) : void 0;
478
+ const bulkhead = options.bulkhead ? new Bulkhead(options.bulkhead.maxConcurrent, options.bulkhead.maxQueue) : void 0;
479
+ const operation = new ResilientOperation(fn, breaker, bulkhead);
480
+ return () => operation.execute();
481
+ }
482
+
483
+ // src/retry.ts
484
+ import { randomInt } from "crypto";
485
+ var DEFAULT_CONFIG2 = {
486
+ maxRetries: 3,
487
+ baseDelay: 1e3,
488
+ maxDelay: 3e4,
489
+ exponentialBackoff: true,
490
+ jitter: true,
491
+ retryableErrors: (error) => {
492
+ if ("statusCode" in error) {
493
+ const statusCode = error.statusCode;
494
+ if (statusCode !== void 0 && statusCode >= 400 && statusCode < 500) {
495
+ return statusCode === 408 || statusCode === 429;
496
+ }
497
+ }
498
+ return true;
499
+ },
500
+ onRetry: () => {
501
+ }
502
+ };
503
+ async function retry(fn, config = {}, options = {}) {
504
+ const mergedConfig = { ...DEFAULT_CONFIG2, ...config };
505
+ let lastError = new Error("Retry failed");
506
+ for (let attempt = 0; attempt <= mergedConfig.maxRetries; attempt++) {
507
+ try {
508
+ if (options.signal?.aborted) {
509
+ throw new Error("Request aborted");
510
+ }
511
+ return await fn();
512
+ } catch (error) {
513
+ lastError = error instanceof Error ? error : new Error(String(error));
514
+ if (attempt === mergedConfig.maxRetries) {
515
+ throw lastError;
516
+ }
517
+ if (!mergedConfig.retryableErrors(lastError)) {
518
+ throw lastError;
519
+ }
520
+ mergedConfig.onRetry(lastError, attempt + 1);
521
+ const delay = calculateDelay(
522
+ attempt,
523
+ mergedConfig.baseDelay,
524
+ mergedConfig.maxDelay,
525
+ mergedConfig.exponentialBackoff,
526
+ mergedConfig.jitter
527
+ );
528
+ await sleep(delay, options.signal);
529
+ }
530
+ }
531
+ throw lastError;
532
+ }
533
+ function calculateDelay(attempt, baseDelay, maxDelay, exponentialBackoff, jitter) {
534
+ let delay = baseDelay;
535
+ if (exponentialBackoff) {
536
+ delay = Math.min(baseDelay * 2 ** attempt, maxDelay);
537
+ }
538
+ if (jitter) {
539
+ const jitterAmount = Math.ceil(delay * 0.25);
540
+ if (jitterAmount > 0) {
541
+ delay = delay + randomInt(jitterAmount * 2 + 1) - jitterAmount;
542
+ }
543
+ }
544
+ return Math.floor(Math.min(delay, maxDelay));
545
+ }
546
+ function sleep(ms, signal) {
547
+ return new Promise((resolve, reject) => {
548
+ if (signal?.aborted) {
549
+ reject(new Error("Request aborted"));
550
+ return;
551
+ }
552
+ const timeout = setTimeout(resolve, ms);
553
+ if (signal) {
554
+ signal.addEventListener("abort", () => {
555
+ clearTimeout(timeout);
556
+ reject(new Error("Request aborted"));
557
+ });
558
+ }
559
+ });
560
+ }
561
+ async function fetchWithRetry(url, init, config) {
562
+ const abortController = new AbortController();
563
+ const signal = init?.signal || abortController.signal;
564
+ return retry(
565
+ async () => {
566
+ const response = await fetch(url, {
567
+ ...init,
568
+ signal
569
+ });
570
+ if (!response.ok) {
571
+ const error = new Error(`HTTP ${response.status}: ${response.statusText}`);
572
+ error.statusCode = response.status;
573
+ error.response = response;
574
+ throw error;
575
+ }
576
+ return response;
577
+ },
578
+ {
579
+ ...config,
580
+ retryableErrors: (error) => {
581
+ if (config?.retryableErrors && !config.retryableErrors(error)) {
582
+ return false;
583
+ }
584
+ if ("statusCode" in error) {
585
+ const statusCode = error.statusCode;
586
+ if (statusCode !== void 0 && statusCode >= 400 && statusCode < 500) {
587
+ return statusCode === 408 || statusCode === 429;
588
+ }
589
+ }
590
+ return true;
591
+ }
592
+ },
593
+ { signal }
594
+ );
595
+ }
596
+ var RetryableOperation = class {
597
+ constructor(fn, config = {}) {
598
+ this.fn = fn;
599
+ this.config = { ...DEFAULT_CONFIG2, ...config };
600
+ this.abortController = new AbortController();
601
+ }
602
+ config;
603
+ abortController;
604
+ attempts = 0;
605
+ lastError;
606
+ /**
607
+ * Execute with retry
608
+ */
609
+ async execute() {
610
+ return retry(this.fn, this.config, { signal: this.abortController.signal });
611
+ }
612
+ /**
613
+ * Abort operation
614
+ */
615
+ abort() {
616
+ this.abortController.abort();
617
+ }
618
+ /**
619
+ * Get retry statistics
620
+ */
621
+ getStats() {
622
+ return {
623
+ attempts: this.attempts,
624
+ lastError: this.lastError
625
+ };
626
+ }
627
+ };
628
+ function Retryable(config) {
629
+ return (_target, _propertyKey, descriptor) => {
630
+ const originalMethod = descriptor.value;
631
+ descriptor.value = async function(...args) {
632
+ return retry(() => originalMethod.apply(this, args), config);
633
+ };
634
+ return descriptor;
635
+ };
636
+ }
637
+ function createRetryMiddleware(config = {}) {
638
+ return async (_request, next) => {
639
+ return retry(next, config);
640
+ };
641
+ }
642
+ async function retryBatch(operations, config = {}) {
643
+ return Promise.all(
644
+ operations.map(async (op) => {
645
+ try {
646
+ return await retry(op, config);
647
+ } catch (error) {
648
+ return error instanceof Error ? error : new Error(String(error));
649
+ }
650
+ })
651
+ );
652
+ }
653
+ async function retryWithFallback(primary, fallback, config = {}) {
654
+ try {
655
+ return await retry(primary, config);
656
+ } catch (error) {
657
+ getResilienceLogger().warn("Primary operation failed, trying fallback", {
658
+ error: error instanceof Error ? error.message : String(error)
659
+ });
660
+ return fallback();
661
+ }
662
+ }
663
+ async function retryIf(fn, condition, config = {}) {
664
+ return retry(fn, {
665
+ ...config,
666
+ retryableErrors: (error) => {
667
+ const originalCheck = config.retryableErrors?.(error) ?? DEFAULT_CONFIG2.retryableErrors(error);
668
+ if (!originalCheck) return false;
669
+ return condition(error, 0);
670
+ }
671
+ });
672
+ }
673
+ async function retryUntil(fn, predicate, config = {}, maxAttempts = 10) {
674
+ let attempts = 0;
675
+ while (attempts < maxAttempts) {
676
+ try {
677
+ const result = await fn();
678
+ if (predicate(result)) {
679
+ return result;
680
+ }
681
+ attempts++;
682
+ if (attempts >= maxAttempts) {
683
+ throw new Error("Max attempts reached without matching predicate");
684
+ }
685
+ const delay = calculateDelay(
686
+ attempts - 1,
687
+ config.baseDelay ?? DEFAULT_CONFIG2.baseDelay,
688
+ config.maxDelay ?? DEFAULT_CONFIG2.maxDelay,
689
+ config.exponentialBackoff ?? DEFAULT_CONFIG2.exponentialBackoff,
690
+ config.jitter ?? DEFAULT_CONFIG2.jitter
691
+ );
692
+ await sleep(delay);
693
+ } catch (error) {
694
+ attempts++;
695
+ if (attempts >= maxAttempts) {
696
+ throw error;
697
+ }
698
+ const delay = calculateDelay(
699
+ attempts - 1,
700
+ config.baseDelay ?? DEFAULT_CONFIG2.baseDelay,
701
+ config.maxDelay ?? DEFAULT_CONFIG2.maxDelay,
702
+ config.exponentialBackoff ?? DEFAULT_CONFIG2.exponentialBackoff,
703
+ config.jitter ?? DEFAULT_CONFIG2.jitter
704
+ );
705
+ await sleep(delay);
706
+ }
707
+ }
708
+ throw new Error("Max attempts reached");
709
+ }
710
+ var ExponentialBackoff = class {
711
+ constructor(baseDelay = 1e3, maxDelay = 3e4, maxAttempts = 10, jitter = true) {
712
+ this.baseDelay = baseDelay;
713
+ this.maxDelay = maxDelay;
714
+ this.maxAttempts = maxAttempts;
715
+ this.jitter = jitter;
716
+ }
717
+ async *[Symbol.asyncIterator]() {
718
+ for (let attempt = 0; attempt < this.maxAttempts; attempt++) {
719
+ const delay = calculateDelay(attempt, this.baseDelay, this.maxDelay, true, this.jitter);
720
+ yield delay;
721
+ await sleep(delay);
722
+ }
723
+ }
724
+ };
725
+ var RetryPolicyBuilder = class {
726
+ config = {};
727
+ /**
728
+ * Set max retries
729
+ */
730
+ maxRetries(count) {
731
+ this.config.maxRetries = count;
732
+ return this;
733
+ }
734
+ /**
735
+ * Set base delay
736
+ */
737
+ baseDelay(ms) {
738
+ this.config.baseDelay = ms;
739
+ return this;
740
+ }
741
+ /**
742
+ * Set max delay
743
+ */
744
+ maxDelay(ms) {
745
+ this.config.maxDelay = ms;
746
+ return this;
747
+ }
748
+ /**
749
+ * Enable/disable exponential backoff
750
+ */
751
+ exponentialBackoff(enabled = true) {
752
+ this.config.exponentialBackoff = enabled;
753
+ return this;
754
+ }
755
+ /**
756
+ * Enable/disable jitter
757
+ */
758
+ jitter(enabled = true) {
759
+ this.config.jitter = enabled;
760
+ return this;
761
+ }
762
+ /**
763
+ * Set custom retryable errors function
764
+ */
765
+ retryOn(fn) {
766
+ this.config.retryableErrors = fn;
767
+ return this;
768
+ }
769
+ /**
770
+ * Set retry callback
771
+ */
772
+ onRetry(fn) {
773
+ this.config.onRetry = fn;
774
+ return this;
775
+ }
776
+ /**
777
+ * Build retry config
778
+ */
779
+ build() {
780
+ return this.config;
781
+ }
782
+ /**
783
+ * Execute function with built policy
784
+ */
785
+ async execute(fn) {
786
+ return retry(fn, this.build());
787
+ }
788
+ };
789
+ var RetryPolicies = {
790
+ /**
791
+ * Default policy - 3 retries with exponential backoff
792
+ */
793
+ default: () => ({
794
+ maxRetries: 3,
795
+ baseDelay: 1e3,
796
+ maxDelay: 3e4,
797
+ exponentialBackoff: true,
798
+ jitter: true
799
+ }),
800
+ /**
801
+ * Aggressive policy - more retries, faster backoff
802
+ */
803
+ aggressive: () => ({
804
+ maxRetries: 5,
805
+ baseDelay: 500,
806
+ maxDelay: 1e4,
807
+ exponentialBackoff: true,
808
+ jitter: true
809
+ }),
810
+ /**
811
+ * Conservative policy - fewer retries, longer backoff
812
+ */
813
+ conservative: () => ({
814
+ maxRetries: 2,
815
+ baseDelay: 2e3,
816
+ maxDelay: 6e4,
817
+ exponentialBackoff: true,
818
+ jitter: true
819
+ }),
820
+ /**
821
+ * Linear backoff policy
822
+ */
823
+ linear: () => ({
824
+ maxRetries: 3,
825
+ baseDelay: 1e3,
826
+ maxDelay: 1e4,
827
+ exponentialBackoff: false,
828
+ jitter: false
829
+ }),
830
+ /**
831
+ * Immediate retry policy - no delay
832
+ */
833
+ immediate: () => ({
834
+ maxRetries: 3,
835
+ baseDelay: 0,
836
+ maxDelay: 0,
837
+ exponentialBackoff: false,
838
+ jitter: false
839
+ }),
840
+ /**
841
+ * Network error only policy
842
+ */
843
+ networkOnly: () => ({
844
+ maxRetries: 3,
845
+ baseDelay: 1e3,
846
+ maxDelay: 3e4,
847
+ exponentialBackoff: true,
848
+ jitter: true,
849
+ retryableErrors: (error) => error.name === "NetworkError"
850
+ }),
851
+ /**
852
+ * Idempotent operations policy (safe to retry)
853
+ */
854
+ idempotent: () => ({
855
+ maxRetries: 5,
856
+ baseDelay: 1e3,
857
+ maxDelay: 3e4,
858
+ exponentialBackoff: true,
859
+ jitter: true
860
+ })
861
+ };
862
+ var GlobalRetryConfig = class {
863
+ config = RetryPolicies.default();
864
+ /**
865
+ * Set global retry config
866
+ */
867
+ setConfig(config) {
868
+ this.config = { ...this.config, ...config };
869
+ }
870
+ /**
871
+ * Get global retry config
872
+ */
873
+ getConfig() {
874
+ return this.config;
875
+ }
876
+ /**
877
+ * Reset to default config
878
+ */
879
+ reset() {
880
+ this.config = RetryPolicies.default();
881
+ }
882
+ };
883
+ var globalRetryConfig = new GlobalRetryConfig();
884
+ export {
885
+ AdaptiveCircuitBreaker,
886
+ Bulkhead,
887
+ CircuitBreak,
888
+ CircuitBreaker,
889
+ CircuitBreakerOpenError,
890
+ CircuitBreakerRegistry,
891
+ ExponentialBackoff,
892
+ ResilientOperation,
893
+ RetryPolicies,
894
+ RetryPolicyBuilder,
895
+ Retryable,
896
+ RetryableOperation,
897
+ calculateDelay,
898
+ circuitBreakerRegistry,
899
+ configureResilienceLogger,
900
+ createCircuitBreakerMiddleware,
901
+ createResilientFunction,
902
+ createRetryMiddleware,
903
+ fetchWithCircuitBreaker,
904
+ fetchWithRetry,
905
+ getResilienceLogger,
906
+ globalRetryConfig,
907
+ retry,
908
+ retryBatch,
909
+ retryIf,
910
+ retryUntil,
911
+ retryWithFallback,
912
+ sleep,
913
+ withCircuitBreaker
914
+ };
915
+ //# sourceMappingURL=index.js.map