@hazeljs/resilience 0.2.0-beta.41

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 (60) hide show
  1. package/LICENSE +192 -0
  2. package/README.md +95 -0
  3. package/dist/__tests__/bulkhead-timeout.test.d.ts +2 -0
  4. package/dist/__tests__/bulkhead-timeout.test.d.ts.map +1 -0
  5. package/dist/__tests__/bulkhead-timeout.test.js +74 -0
  6. package/dist/__tests__/circuit-breaker.test.d.ts +2 -0
  7. package/dist/__tests__/circuit-breaker.test.d.ts.map +1 -0
  8. package/dist/__tests__/circuit-breaker.test.js +160 -0
  9. package/dist/__tests__/decorators.test.d.ts +2 -0
  10. package/dist/__tests__/decorators.test.d.ts.map +1 -0
  11. package/dist/__tests__/decorators.test.js +288 -0
  12. package/dist/__tests__/index.test.d.ts +2 -0
  13. package/dist/__tests__/index.test.d.ts.map +1 -0
  14. package/dist/__tests__/index.test.js +50 -0
  15. package/dist/__tests__/metrics.test.d.ts +2 -0
  16. package/dist/__tests__/metrics.test.d.ts.map +1 -0
  17. package/dist/__tests__/metrics.test.js +83 -0
  18. package/dist/__tests__/rate-limiter.test.d.ts +2 -0
  19. package/dist/__tests__/rate-limiter.test.d.ts.map +1 -0
  20. package/dist/__tests__/rate-limiter.test.js +143 -0
  21. package/dist/__tests__/retry-policy.test.d.ts +2 -0
  22. package/dist/__tests__/retry-policy.test.d.ts.map +1 -0
  23. package/dist/__tests__/retry-policy.test.js +84 -0
  24. package/dist/__tests__/sliding-window.test.d.ts +2 -0
  25. package/dist/__tests__/sliding-window.test.d.ts.map +1 -0
  26. package/dist/__tests__/sliding-window.test.js +93 -0
  27. package/dist/bulkhead/bulkhead.d.ts +34 -0
  28. package/dist/bulkhead/bulkhead.d.ts.map +1 -0
  29. package/dist/bulkhead/bulkhead.js +97 -0
  30. package/dist/circuit-breaker/circuit-breaker-registry.d.ts +38 -0
  31. package/dist/circuit-breaker/circuit-breaker-registry.d.ts.map +1 -0
  32. package/dist/circuit-breaker/circuit-breaker-registry.js +61 -0
  33. package/dist/circuit-breaker/circuit-breaker.d.ts +51 -0
  34. package/dist/circuit-breaker/circuit-breaker.d.ts.map +1 -0
  35. package/dist/circuit-breaker/circuit-breaker.js +182 -0
  36. package/dist/circuit-breaker/sliding-window.d.ts +49 -0
  37. package/dist/circuit-breaker/sliding-window.d.ts.map +1 -0
  38. package/dist/circuit-breaker/sliding-window.js +89 -0
  39. package/dist/decorators/index.d.ts +51 -0
  40. package/dist/decorators/index.d.ts.map +1 -0
  41. package/dist/decorators/index.js +133 -0
  42. package/dist/index.d.ts +18 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +55 -0
  45. package/dist/metrics/metrics-collector.d.ts +69 -0
  46. package/dist/metrics/metrics-collector.d.ts.map +1 -0
  47. package/dist/metrics/metrics-collector.js +180 -0
  48. package/dist/rate-limiter/rate-limiter.d.ts +72 -0
  49. package/dist/rate-limiter/rate-limiter.d.ts.map +1 -0
  50. package/dist/rate-limiter/rate-limiter.js +147 -0
  51. package/dist/retry/retry-policy.d.ts +19 -0
  52. package/dist/retry/retry-policy.d.ts.map +1 -0
  53. package/dist/retry/retry-policy.js +87 -0
  54. package/dist/timeout/timeout.d.ts +23 -0
  55. package/dist/timeout/timeout.d.ts.map +1 -0
  56. package/dist/timeout/timeout.js +55 -0
  57. package/dist/types/index.d.ts +135 -0
  58. package/dist/types/index.d.ts.map +1 -0
  59. package/dist/types/index.js +61 -0
  60. package/package.json +63 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/rate-limiter/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,iBAAiB,EAAkB,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAElF;;;GAGG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAS;gBAEf,QAAQ,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM;IAOzD;;OAEG;IACH,UAAU,IAAI,OAAO;IASrB;;OAEG;IACH,eAAe,IAAI,MAAM;IAOzB,OAAO,CAAC,MAAM;CAMf;AAED;;;GAGG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAS;gBAEhB,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAOzC;;OAEG;IACH,UAAU,IAAI,OAAO;IAYrB;;OAEG;IACH,eAAe,IAAI,MAAM;IAOzB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,KAAK;CAQd;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAA4C;IAC3D,OAAO,CAAC,QAAQ,CAAsB;gBAE1B,MAAM,EAAE,iBAAiB;IAWrC;;OAEG;IACG,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAQlD;;OAEG;IACH,UAAU,IAAI,OAAO;IAIrB;;OAEG;IACH,eAAe,IAAI,MAAM;IAIzB;;OAEG;IACH,WAAW,IAAI,mBAAmB;CAGnC"}
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ /**
3
+ * Rate Limiter
4
+ * Token bucket and sliding window implementations for rate limiting.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.RateLimiter = exports.SlidingWindowLimiter = exports.TokenBucketLimiter = void 0;
8
+ const types_1 = require("../types");
9
+ /**
10
+ * Token Bucket Rate Limiter
11
+ * Allows bursts up to the bucket capacity and refills at a steady rate.
12
+ */
13
+ class TokenBucketLimiter {
14
+ constructor(capacity, refillRatePerSecond) {
15
+ this.capacity = capacity;
16
+ this.tokens = capacity;
17
+ this.refillRate = refillRatePerSecond / 1000;
18
+ this.lastRefill = Date.now();
19
+ }
20
+ /**
21
+ * Try to consume a token. Returns true if allowed.
22
+ */
23
+ tryAcquire() {
24
+ this.refill();
25
+ if (this.tokens >= 1) {
26
+ this.tokens -= 1;
27
+ return true;
28
+ }
29
+ return false;
30
+ }
31
+ /**
32
+ * Get time in ms until the next token is available
33
+ */
34
+ getRetryAfterMs() {
35
+ this.refill();
36
+ if (this.tokens >= 1)
37
+ return 0;
38
+ const needed = 1 - this.tokens;
39
+ return Math.ceil(needed / this.refillRate);
40
+ }
41
+ refill() {
42
+ const now = Date.now();
43
+ const elapsed = now - this.lastRefill;
44
+ this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillRate);
45
+ this.lastRefill = now;
46
+ }
47
+ }
48
+ exports.TokenBucketLimiter = TokenBucketLimiter;
49
+ /**
50
+ * Sliding Window Counter Rate Limiter
51
+ * Tracks request counts in small sub-windows for more accurate rate limiting.
52
+ */
53
+ class SlidingWindowLimiter {
54
+ constructor(max, windowMs) {
55
+ this.windows = new Map();
56
+ this.max = max;
57
+ this.windowMs = windowMs;
58
+ // Use 10 sub-windows for granularity
59
+ this.subWindowMs = Math.max(1, Math.floor(windowMs / 10));
60
+ }
61
+ /**
62
+ * Try to record a request. Returns true if within limit.
63
+ */
64
+ tryAcquire() {
65
+ this.evict();
66
+ const count = this.getCurrentCount();
67
+ if (count >= this.max) {
68
+ return false;
69
+ }
70
+ const subKey = this.getCurrentSubKey();
71
+ this.windows.set(subKey, (this.windows.get(subKey) || 0) + 1);
72
+ return true;
73
+ }
74
+ /**
75
+ * Get time in ms until a slot opens up
76
+ */
77
+ getRetryAfterMs() {
78
+ this.evict();
79
+ if (this.getCurrentCount() < this.max)
80
+ return 0;
81
+ // Earliest sub-window will expire after subWindowMs
82
+ return this.subWindowMs;
83
+ }
84
+ getCurrentCount() {
85
+ let total = 0;
86
+ for (const count of this.windows.values()) {
87
+ total += count;
88
+ }
89
+ return total;
90
+ }
91
+ getCurrentSubKey() {
92
+ return Math.floor(Date.now() / this.subWindowMs);
93
+ }
94
+ evict() {
95
+ const cutoffKey = Math.floor((Date.now() - this.windowMs) / this.subWindowMs);
96
+ for (const key of this.windows.keys()) {
97
+ if (key <= cutoffKey) {
98
+ this.windows.delete(key);
99
+ }
100
+ }
101
+ }
102
+ }
103
+ exports.SlidingWindowLimiter = SlidingWindowLimiter;
104
+ /**
105
+ * Unified RateLimiter that wraps the configured strategy
106
+ */
107
+ class RateLimiter {
108
+ constructor(config) {
109
+ this.strategy = config.strategy;
110
+ if (config.strategy === 'token-bucket') {
111
+ const refillRate = config.refillRate ?? config.max / (config.window / 1000);
112
+ this.limiter = new TokenBucketLimiter(config.max, refillRate);
113
+ }
114
+ else {
115
+ this.limiter = new SlidingWindowLimiter(config.max, config.window);
116
+ }
117
+ }
118
+ /**
119
+ * Execute a function within rate limit constraints
120
+ */
121
+ async execute(fn) {
122
+ if (!this.limiter.tryAcquire()) {
123
+ const retryAfter = this.limiter.getRetryAfterMs();
124
+ throw new types_1.RateLimitError(`Rate limit exceeded. Retry after ${retryAfter}ms`, retryAfter);
125
+ }
126
+ return fn();
127
+ }
128
+ /**
129
+ * Try to acquire permission (consumes a token/slot)
130
+ */
131
+ tryAcquire() {
132
+ return this.limiter.tryAcquire();
133
+ }
134
+ /**
135
+ * Get time in ms until the next request is allowed
136
+ */
137
+ getRetryAfterMs() {
138
+ return this.limiter.getRetryAfterMs();
139
+ }
140
+ /**
141
+ * Get the strategy in use
142
+ */
143
+ getStrategy() {
144
+ return this.strategy;
145
+ }
146
+ }
147
+ exports.RateLimiter = RateLimiter;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Retry Policy
3
+ * Configurable retry with exponential backoff, jitter, and retryable-error predicates.
4
+ */
5
+ import { RetryConfig } from '../types';
6
+ export declare class RetryPolicy {
7
+ private config;
8
+ constructor(config?: Partial<RetryConfig>);
9
+ /**
10
+ * Execute a function with retry logic
11
+ */
12
+ execute<T>(fn: () => Promise<T>): Promise<T>;
13
+ /**
14
+ * Get the configured max attempts
15
+ */
16
+ getMaxAttempts(): number;
17
+ private sleep;
18
+ }
19
+ //# sourceMappingURL=retry-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry-policy.d.ts","sourceRoot":"","sources":["../../src/retry/retry-policy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAwC,MAAM,UAAU,CAAC;AAoD7E,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAwB;gBAE1B,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM;IAI7C;;OAEG;IACG,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IA2ClD;;OAEG;IACH,cAAc,IAAI,MAAM;IAIxB,OAAO,CAAC,KAAK;CAGd"}
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ /**
3
+ * Retry Policy
4
+ * Configurable retry with exponential backoff, jitter, and retryable-error predicates.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.RetryPolicy = void 0;
8
+ const types_1 = require("../types");
9
+ const DEFAULT_CONFIG = {
10
+ maxAttempts: 3,
11
+ backoff: 'exponential',
12
+ baseDelay: 1000,
13
+ maxDelay: 30000,
14
+ jitter: true,
15
+ retryPredicate: undefined,
16
+ onRetry: undefined,
17
+ };
18
+ /**
19
+ * Calculate delay based on backoff strategy
20
+ */
21
+ function calculateDelay(strategy, attempt, baseDelay, maxDelay, jitter) {
22
+ let delay;
23
+ switch (strategy) {
24
+ case 'fixed':
25
+ delay = baseDelay;
26
+ break;
27
+ case 'linear':
28
+ delay = baseDelay * attempt;
29
+ break;
30
+ case 'exponential':
31
+ delay = baseDelay * Math.pow(2, attempt - 1);
32
+ break;
33
+ default:
34
+ delay = baseDelay;
35
+ }
36
+ // Cap at max delay
37
+ delay = Math.min(delay, maxDelay);
38
+ // Add jitter (random value between 0 and delay)
39
+ if (jitter) {
40
+ delay = delay * (0.5 + Math.random() * 0.5);
41
+ }
42
+ return Math.floor(delay);
43
+ }
44
+ class RetryPolicy {
45
+ constructor(config = {}) {
46
+ this.config = { ...DEFAULT_CONFIG, ...config };
47
+ }
48
+ /**
49
+ * Execute a function with retry logic
50
+ */
51
+ async execute(fn) {
52
+ let lastError;
53
+ // Attempt 0 is the initial call, attempts 1..maxAttempts are retries
54
+ for (let attempt = 0; attempt <= this.config.maxAttempts; attempt++) {
55
+ try {
56
+ return await fn();
57
+ }
58
+ catch (error) {
59
+ lastError = error;
60
+ // Check if this error is retryable
61
+ if (this.config.retryPredicate && !this.config.retryPredicate(error)) {
62
+ throw error;
63
+ }
64
+ // If we've exhausted all retries, throw
65
+ if (attempt >= this.config.maxAttempts) {
66
+ break;
67
+ }
68
+ // Notify retry listener
69
+ this.config.onRetry?.(error, attempt + 1);
70
+ // Wait before next attempt
71
+ const delay = calculateDelay(this.config.backoff, attempt + 1, this.config.baseDelay, this.config.maxDelay, this.config.jitter);
72
+ await this.sleep(delay);
73
+ }
74
+ }
75
+ throw new types_1.RetryExhaustedError(`Retry exhausted after ${this.config.maxAttempts} attempts`, this.config.maxAttempts, lastError);
76
+ }
77
+ /**
78
+ * Get the configured max attempts
79
+ */
80
+ getMaxAttempts() {
81
+ return this.config.maxAttempts;
82
+ }
83
+ sleep(ms) {
84
+ return new Promise((resolve) => setTimeout(resolve, ms));
85
+ }
86
+ }
87
+ exports.RetryPolicy = RetryPolicy;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Timeout
3
+ * Promise-based timeout wrapper with cancellation support.
4
+ */
5
+ import { TimeoutConfig } from '../types';
6
+ export declare class Timeout {
7
+ private durationMs;
8
+ private message;
9
+ constructor(config: TimeoutConfig | number);
10
+ /**
11
+ * Execute a function with a timeout
12
+ */
13
+ execute<T>(fn: () => Promise<T>): Promise<T>;
14
+ /**
15
+ * Get the configured duration
16
+ */
17
+ getDuration(): number;
18
+ }
19
+ /**
20
+ * Convenience function to wrap a promise with a timeout
21
+ */
22
+ export declare function withTimeout<T>(fn: () => Promise<T>, durationMs: number, message?: string): Promise<T>;
23
+ //# sourceMappingURL=timeout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../../src/timeout/timeout.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAgB,MAAM,UAAU,CAAC;AAEvD,qBAAa,OAAO;IAClB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,aAAa,GAAG,MAAM;IAU1C;;OAEG;IACG,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAmBlD;;OAEG;IACH,WAAW,IAAI,MAAM;CAGtB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,CAAC,EACjC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,CAAC,CAAC,CAGZ"}
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ /**
3
+ * Timeout
4
+ * Promise-based timeout wrapper with cancellation support.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.Timeout = void 0;
8
+ exports.withTimeout = withTimeout;
9
+ const types_1 = require("../types");
10
+ class Timeout {
11
+ constructor(config) {
12
+ if (typeof config === 'number') {
13
+ this.durationMs = config;
14
+ this.message = 'Operation timed out';
15
+ }
16
+ else {
17
+ this.durationMs = config.duration;
18
+ this.message = config.message || 'Operation timed out';
19
+ }
20
+ }
21
+ /**
22
+ * Execute a function with a timeout
23
+ */
24
+ async execute(fn) {
25
+ let timeoutId;
26
+ const timeoutPromise = new Promise((_, reject) => {
27
+ timeoutId = setTimeout(() => {
28
+ reject(new types_1.TimeoutError(`${this.message} (after ${this.durationMs}ms)`));
29
+ }, this.durationMs);
30
+ });
31
+ try {
32
+ const result = await Promise.race([fn(), timeoutPromise]);
33
+ return result;
34
+ }
35
+ finally {
36
+ if (timeoutId) {
37
+ clearTimeout(timeoutId);
38
+ }
39
+ }
40
+ }
41
+ /**
42
+ * Get the configured duration
43
+ */
44
+ getDuration() {
45
+ return this.durationMs;
46
+ }
47
+ }
48
+ exports.Timeout = Timeout;
49
+ /**
50
+ * Convenience function to wrap a promise with a timeout
51
+ */
52
+ async function withTimeout(fn, durationMs, message) {
53
+ const timeout = new Timeout({ duration: durationMs, message });
54
+ return timeout.execute(fn);
55
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * @hazeljs/resilience - Type Definitions
3
+ */
4
+ export declare enum CircuitState {
5
+ CLOSED = "CLOSED",
6
+ OPEN = "OPEN",
7
+ HALF_OPEN = "HALF_OPEN"
8
+ }
9
+ export interface SlidingWindowConfig {
10
+ /** 'count' uses last N calls; 'time' uses a rolling time window */
11
+ type: 'count' | 'time';
12
+ /** For 'count': number of calls. For 'time': window duration in ms */
13
+ size: number;
14
+ }
15
+ export interface CircuitBreakerConfig {
16
+ /** Number of failures before opening the circuit */
17
+ failureThreshold: number;
18
+ /** Number of successes in HALF_OPEN before closing */
19
+ successThreshold: number;
20
+ /** Max time a single call may take before being considered failed (ms) */
21
+ timeout: number;
22
+ /** Time to wait in OPEN before transitioning to HALF_OPEN (ms) */
23
+ resetTimeout: number;
24
+ /** Sliding window configuration for failure rate calculation */
25
+ slidingWindow?: SlidingWindowConfig;
26
+ /** Custom predicate to determine if an error should count as a failure */
27
+ failurePredicate?: (error: unknown) => boolean;
28
+ /** Name of the fallback method on the same class */
29
+ fallback?: string;
30
+ /** Callback when state changes */
31
+ onStateChange?: (from: CircuitState, to: CircuitState) => void;
32
+ }
33
+ export interface CircuitBreakerMetrics {
34
+ totalRequests: number;
35
+ successCount: number;
36
+ failureCount: number;
37
+ failureRate: number;
38
+ state: CircuitState;
39
+ lastFailureTime?: number;
40
+ lastSuccessTime?: number;
41
+ averageResponseTime: number;
42
+ p99ResponseTime: number;
43
+ }
44
+ export type BackoffStrategy = 'fixed' | 'exponential' | 'linear';
45
+ export interface RetryConfig {
46
+ /** Maximum number of retry attempts (not counting the initial call) */
47
+ maxAttempts: number;
48
+ /** Backoff strategy between retries */
49
+ backoff: BackoffStrategy;
50
+ /** Base delay in ms */
51
+ baseDelay: number;
52
+ /** Maximum delay between retries in ms */
53
+ maxDelay?: number;
54
+ /** Whether to add jitter to delay to avoid thundering herd */
55
+ jitter?: boolean;
56
+ /** Custom predicate to decide if an error is retryable */
57
+ retryPredicate?: (error: unknown) => boolean;
58
+ /** Called before each retry attempt */
59
+ onRetry?: (error: unknown, attempt: number) => void;
60
+ }
61
+ export interface TimeoutConfig {
62
+ /** Timeout duration in ms */
63
+ duration: number;
64
+ /** Custom error message */
65
+ message?: string;
66
+ }
67
+ export interface BulkheadConfig {
68
+ /** Maximum concurrent executions */
69
+ maxConcurrent: number;
70
+ /** Maximum number of calls waiting in queue */
71
+ maxQueue: number;
72
+ /** Time a call can wait in the queue before being rejected (ms) */
73
+ queueTimeout?: number;
74
+ }
75
+ export interface BulkheadMetrics {
76
+ activeCalls: number;
77
+ queueLength: number;
78
+ maxConcurrent: number;
79
+ maxQueue: number;
80
+ rejectedCount: number;
81
+ }
82
+ export type RateLimiterStrategy = 'token-bucket' | 'sliding-window';
83
+ export interface RateLimiterConfig {
84
+ /** Strategy to use */
85
+ strategy: RateLimiterStrategy;
86
+ /** Maximum number of requests allowed in the window */
87
+ max: number;
88
+ /** Time window in ms (e.g. 60000 for 1 minute) */
89
+ window: number;
90
+ /** For token-bucket: refill rate (tokens per second). Defaults to max/window. */
91
+ refillRate?: number;
92
+ }
93
+ export interface MetricsSnapshot {
94
+ totalCalls: number;
95
+ successCalls: number;
96
+ failureCalls: number;
97
+ failureRate: number;
98
+ averageResponseTime: number;
99
+ p50ResponseTime: number;
100
+ p95ResponseTime: number;
101
+ p99ResponseTime: number;
102
+ minResponseTime: number;
103
+ maxResponseTime: number;
104
+ lastCallTime?: number;
105
+ }
106
+ export interface MetricsEntry {
107
+ timestamp: number;
108
+ duration: number;
109
+ success: boolean;
110
+ error?: string;
111
+ }
112
+ export declare class ResilienceError extends Error {
113
+ readonly code: string;
114
+ constructor(message: string, code: string);
115
+ }
116
+ export declare class CircuitBreakerError extends ResilienceError {
117
+ readonly state: CircuitState;
118
+ constructor(message: string, state: CircuitState);
119
+ }
120
+ export declare class TimeoutError extends ResilienceError {
121
+ constructor(message?: string);
122
+ }
123
+ export declare class BulkheadError extends ResilienceError {
124
+ constructor(message?: string);
125
+ }
126
+ export declare class RateLimitError extends ResilienceError {
127
+ readonly retryAfterMs?: number | undefined;
128
+ constructor(message?: string, retryAfterMs?: number | undefined);
129
+ }
130
+ export declare class RetryExhaustedError extends ResilienceError {
131
+ readonly attempts: number;
132
+ readonly lastError: unknown;
133
+ constructor(message: string, attempts: number, lastError: unknown);
134
+ }
135
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ /**
3
+ * @hazeljs/resilience - Type Definitions
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RetryExhaustedError = exports.RateLimitError = exports.BulkheadError = exports.TimeoutError = exports.CircuitBreakerError = exports.ResilienceError = exports.CircuitState = void 0;
7
+ // ─── Circuit Breaker ───
8
+ var CircuitState;
9
+ (function (CircuitState) {
10
+ CircuitState["CLOSED"] = "CLOSED";
11
+ CircuitState["OPEN"] = "OPEN";
12
+ CircuitState["HALF_OPEN"] = "HALF_OPEN";
13
+ })(CircuitState || (exports.CircuitState = CircuitState = {}));
14
+ // ─── Common ───
15
+ class ResilienceError extends Error {
16
+ constructor(message, code) {
17
+ super(message);
18
+ this.code = code;
19
+ this.name = 'ResilienceError';
20
+ }
21
+ }
22
+ exports.ResilienceError = ResilienceError;
23
+ class CircuitBreakerError extends ResilienceError {
24
+ constructor(message, state) {
25
+ super(message, 'CIRCUIT_OPEN');
26
+ this.state = state;
27
+ this.name = 'CircuitBreakerError';
28
+ }
29
+ }
30
+ exports.CircuitBreakerError = CircuitBreakerError;
31
+ class TimeoutError extends ResilienceError {
32
+ constructor(message = 'Operation timed out') {
33
+ super(message, 'TIMEOUT');
34
+ this.name = 'TimeoutError';
35
+ }
36
+ }
37
+ exports.TimeoutError = TimeoutError;
38
+ class BulkheadError extends ResilienceError {
39
+ constructor(message = 'Bulkhead capacity exceeded') {
40
+ super(message, 'BULKHEAD_FULL');
41
+ this.name = 'BulkheadError';
42
+ }
43
+ }
44
+ exports.BulkheadError = BulkheadError;
45
+ class RateLimitError extends ResilienceError {
46
+ constructor(message = 'Rate limit exceeded', retryAfterMs) {
47
+ super(message, 'RATE_LIMITED');
48
+ this.retryAfterMs = retryAfterMs;
49
+ this.name = 'RateLimitError';
50
+ }
51
+ }
52
+ exports.RateLimitError = RateLimitError;
53
+ class RetryExhaustedError extends ResilienceError {
54
+ constructor(message, attempts, lastError) {
55
+ super(message, 'RETRY_EXHAUSTED');
56
+ this.attempts = attempts;
57
+ this.lastError = lastError;
58
+ this.name = 'RetryExhaustedError';
59
+ }
60
+ }
61
+ exports.RetryExhaustedError = RetryExhaustedError;
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@hazeljs/resilience",
3
+ "version": "0.2.0-beta.41",
4
+ "description": "Fault-tolerance and resilience patterns for HazelJS - Circuit Breaker, Retry, Bulkhead, Timeout, Rate Limiter",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "test": "jest --coverage --passWithNoTests",
13
+ "test:ci": "jest --coverage --coverageReporters=text --coverageReporters=lcov --coverageReporters=clover --no-coverage-threshold",
14
+ "test:watch": "jest --watch",
15
+ "lint": "eslint \"src/**/*.ts\"",
16
+ "lint:fix": "eslint \"src/**/*.ts\" --fix",
17
+ "format": "prettier --write \"src/**/*.ts\"",
18
+ "clean": "rm -rf dist"
19
+ },
20
+ "dependencies": {
21
+ "@hazeljs/core": "^0.2.0-beta.41",
22
+ "reflect-metadata": "^0.2.2"
23
+ },
24
+ "devDependencies": {
25
+ "@types/jest": "^29.5.14",
26
+ "@types/node": "^20.17.50",
27
+ "@typescript-eslint/eslint-plugin": "^8.18.2",
28
+ "@typescript-eslint/parser": "^8.18.2",
29
+ "eslint": "^8.56.0",
30
+ "eslint-config-prettier": "^9.1.0",
31
+ "eslint-plugin-prettier": "^5.1.3",
32
+ "jest": "^29.7.0",
33
+ "prettier": "^3.2.5",
34
+ "ts-jest": "^29.1.2",
35
+ "typescript": "^5.3.3"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/hazel-js/hazeljs.git",
43
+ "directory": "packages/resilience"
44
+ },
45
+ "keywords": [
46
+ "hazeljs",
47
+ "resilience",
48
+ "circuit-breaker",
49
+ "retry",
50
+ "bulkhead",
51
+ "timeout",
52
+ "rate-limiter",
53
+ "fault-tolerance",
54
+ "microservices"
55
+ ],
56
+ "author": "Muhammad Arslan <marslan@hazeljs.com>",
57
+ "license": "MIT",
58
+ "bugs": {
59
+ "url": "https://github.com/hazeljs/hazel-js/issues"
60
+ },
61
+ "homepage": "https://hazeljs.com",
62
+ "gitHead": "b50699eaa4847a8cfe9c346d7e4464b4af7f3120"
63
+ }