@teneo-protocol/sdk 1.0.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/.dockerignore +14 -0
- package/.env.test.example +14 -0
- package/.eslintrc.json +26 -0
- package/.github/workflows/claude-code-review.yml +78 -0
- package/.github/workflows/claude-reviewer.yml +64 -0
- package/.github/workflows/publish-npm.yml +38 -0
- package/.github/workflows/push-to-main.yml +23 -0
- package/.node-version +1 -0
- package/.prettierrc +11 -0
- package/Dockerfile +25 -0
- package/LICENCE +661 -0
- package/README.md +709 -0
- package/dist/constants.d.ts +42 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +45 -0
- package/dist/constants.js.map +1 -0
- package/dist/core/websocket-client.d.ts +261 -0
- package/dist/core/websocket-client.d.ts.map +1 -0
- package/dist/core/websocket-client.js +875 -0
- package/dist/core/websocket-client.js.map +1 -0
- package/dist/formatters/response-formatter.d.ts +354 -0
- package/dist/formatters/response-formatter.d.ts.map +1 -0
- package/dist/formatters/response-formatter.js +575 -0
- package/dist/formatters/response-formatter.js.map +1 -0
- package/dist/handlers/message-handler-registry.d.ts +155 -0
- package/dist/handlers/message-handler-registry.d.ts.map +1 -0
- package/dist/handlers/message-handler-registry.js +216 -0
- package/dist/handlers/message-handler-registry.js.map +1 -0
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts +112 -0
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agent-selected-handler.js +40 -0
- package/dist/handlers/message-handlers/agent-selected-handler.js.map +1 -0
- package/dist/handlers/message-handlers/agents-list-handler.d.ts +14 -0
- package/dist/handlers/message-handlers/agents-list-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agents-list-handler.js +25 -0
- package/dist/handlers/message-handlers/agents-list-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-error-handler.d.ts +71 -0
- package/dist/handlers/message-handlers/auth-error-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-error-handler.js +30 -0
- package/dist/handlers/message-handlers/auth-error-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-message-handler.d.ts +18 -0
- package/dist/handlers/message-handlers/auth-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-message-handler.js +60 -0
- package/dist/handlers/message-handlers/auth-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-required-handler.d.ts +76 -0
- package/dist/handlers/message-handlers/auth-required-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-required-handler.js +23 -0
- package/dist/handlers/message-handlers/auth-required-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-success-handler.d.ts +18 -0
- package/dist/handlers/message-handlers/auth-success-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-success-handler.js +51 -0
- package/dist/handlers/message-handlers/auth-success-handler.js.map +1 -0
- package/dist/handlers/message-handlers/base-handler.d.ts +55 -0
- package/dist/handlers/message-handlers/base-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/base-handler.js +83 -0
- package/dist/handlers/message-handlers/base-handler.js.map +1 -0
- package/dist/handlers/message-handlers/challenge-handler.d.ts +73 -0
- package/dist/handlers/message-handlers/challenge-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/challenge-handler.js +47 -0
- package/dist/handlers/message-handlers/challenge-handler.js.map +1 -0
- package/dist/handlers/message-handlers/error-message-handler.d.ts +76 -0
- package/dist/handlers/message-handlers/error-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/error-message-handler.js +29 -0
- package/dist/handlers/message-handlers/error-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/index.d.ts +28 -0
- package/dist/handlers/message-handlers/index.d.ts.map +1 -0
- package/dist/handlers/message-handlers/index.js +100 -0
- package/dist/handlers/message-handlers/index.js.map +1 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts +122 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.js +30 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts +104 -0
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/ping-pong-handler.js +36 -0
- package/dist/handlers/message-handlers/ping-pong-handler.js.map +1 -0
- package/dist/handlers/message-handlers/regular-message-handler.d.ts +56 -0
- package/dist/handlers/message-handlers/regular-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/regular-message-handler.js +59 -0
- package/dist/handlers/message-handlers/regular-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts +81 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.js +48 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/task-response-handler.d.ts +14 -0
- package/dist/handlers/message-handlers/task-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/task-response-handler.js +44 -0
- package/dist/handlers/message-handlers/task-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/types.d.ts +51 -0
- package/dist/handlers/message-handlers/types.d.ts.map +1 -0
- package/dist/handlers/message-handlers/types.js +7 -0
- package/dist/handlers/message-handlers/types.js.map +1 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts +81 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.js +48 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.js.map +1 -0
- package/dist/handlers/webhook-handler.d.ts +202 -0
- package/dist/handlers/webhook-handler.d.ts.map +1 -0
- package/dist/handlers/webhook-handler.js +511 -0
- package/dist/handlers/webhook-handler.js.map +1 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +217 -0
- package/dist/index.js.map +1 -0
- package/dist/managers/agent-registry.d.ts +173 -0
- package/dist/managers/agent-registry.d.ts.map +1 -0
- package/dist/managers/agent-registry.js +310 -0
- package/dist/managers/agent-registry.js.map +1 -0
- package/dist/managers/connection-manager.d.ts +134 -0
- package/dist/managers/connection-manager.d.ts.map +1 -0
- package/dist/managers/connection-manager.js +176 -0
- package/dist/managers/connection-manager.js.map +1 -0
- package/dist/managers/index.d.ts +9 -0
- package/dist/managers/index.d.ts.map +1 -0
- package/dist/managers/index.js +16 -0
- package/dist/managers/index.js.map +1 -0
- package/dist/managers/message-router.d.ts +112 -0
- package/dist/managers/message-router.d.ts.map +1 -0
- package/dist/managers/message-router.js +260 -0
- package/dist/managers/message-router.js.map +1 -0
- package/dist/managers/room-manager.d.ts +165 -0
- package/dist/managers/room-manager.d.ts.map +1 -0
- package/dist/managers/room-manager.js +227 -0
- package/dist/managers/room-manager.js.map +1 -0
- package/dist/teneo-sdk.d.ts +703 -0
- package/dist/teneo-sdk.d.ts.map +1 -0
- package/dist/teneo-sdk.js +907 -0
- package/dist/teneo-sdk.js.map +1 -0
- package/dist/types/config.d.ts +1047 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +720 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/error-codes.d.ts +29 -0
- package/dist/types/error-codes.d.ts.map +1 -0
- package/dist/types/error-codes.js +41 -0
- package/dist/types/error-codes.js.map +1 -0
- package/dist/types/events.d.ts +616 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +261 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/health.d.ts +40 -0
- package/dist/types/health.d.ts.map +1 -0
- package/dist/types/health.js +6 -0
- package/dist/types/health.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +123 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/messages.d.ts +3734 -0
- package/dist/types/messages.d.ts.map +1 -0
- package/dist/types/messages.js +482 -0
- package/dist/types/messages.js.map +1 -0
- package/dist/types/validation.d.ts +81 -0
- package/dist/types/validation.d.ts.map +1 -0
- package/dist/types/validation.js +115 -0
- package/dist/types/validation.js.map +1 -0
- package/dist/utils/bounded-queue.d.ts +127 -0
- package/dist/utils/bounded-queue.d.ts.map +1 -0
- package/dist/utils/bounded-queue.js +181 -0
- package/dist/utils/bounded-queue.js.map +1 -0
- package/dist/utils/circuit-breaker.d.ts +141 -0
- package/dist/utils/circuit-breaker.d.ts.map +1 -0
- package/dist/utils/circuit-breaker.js +215 -0
- package/dist/utils/circuit-breaker.js.map +1 -0
- package/dist/utils/deduplication-cache.d.ts +110 -0
- package/dist/utils/deduplication-cache.d.ts.map +1 -0
- package/dist/utils/deduplication-cache.js +177 -0
- package/dist/utils/deduplication-cache.js.map +1 -0
- package/dist/utils/event-waiter.d.ts +101 -0
- package/dist/utils/event-waiter.d.ts.map +1 -0
- package/dist/utils/event-waiter.js +118 -0
- package/dist/utils/event-waiter.js.map +1 -0
- package/dist/utils/index.d.ts +51 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +72 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +22 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +91 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +122 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +190 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/retry-policy.d.ts +191 -0
- package/dist/utils/retry-policy.d.ts.map +1 -0
- package/dist/utils/retry-policy.js +225 -0
- package/dist/utils/retry-policy.js.map +1 -0
- package/dist/utils/secure-private-key.d.ts +113 -0
- package/dist/utils/secure-private-key.d.ts.map +1 -0
- package/dist/utils/secure-private-key.js +188 -0
- package/dist/utils/secure-private-key.js.map +1 -0
- package/dist/utils/signature-verifier.d.ts +143 -0
- package/dist/utils/signature-verifier.d.ts.map +1 -0
- package/dist/utils/signature-verifier.js +238 -0
- package/dist/utils/signature-verifier.js.map +1 -0
- package/dist/utils/ssrf-validator.d.ts +36 -0
- package/dist/utils/ssrf-validator.d.ts.map +1 -0
- package/dist/utils/ssrf-validator.js +195 -0
- package/dist/utils/ssrf-validator.js.map +1 -0
- package/examples/.env.example +17 -0
- package/examples/basic-usage.ts +211 -0
- package/examples/production-dashboard/.env.example +153 -0
- package/examples/production-dashboard/package.json +39 -0
- package/examples/production-dashboard/public/dashboard.html +642 -0
- package/examples/production-dashboard/server.ts +753 -0
- package/examples/webhook-integration.ts +239 -0
- package/examples/x-influencer-battle-redesign.html +1065 -0
- package/examples/x-influencer-battle-server.ts +217 -0
- package/examples/x-influencer-battle.html +787 -0
- package/package.json +65 -0
- package/src/constants.ts +43 -0
- package/src/core/websocket-client.test.ts +512 -0
- package/src/core/websocket-client.ts +1056 -0
- package/src/formatters/response-formatter.test.ts +571 -0
- package/src/formatters/response-formatter.ts +677 -0
- package/src/handlers/message-handler-registry.ts +239 -0
- package/src/handlers/message-handlers/agent-selected-handler.ts +40 -0
- package/src/handlers/message-handlers/agents-list-handler.ts +26 -0
- package/src/handlers/message-handlers/auth-error-handler.ts +31 -0
- package/src/handlers/message-handlers/auth-message-handler.ts +66 -0
- package/src/handlers/message-handlers/auth-required-handler.ts +23 -0
- package/src/handlers/message-handlers/auth-success-handler.ts +57 -0
- package/src/handlers/message-handlers/base-handler.ts +101 -0
- package/src/handlers/message-handlers/challenge-handler.ts +57 -0
- package/src/handlers/message-handlers/error-message-handler.ts +27 -0
- package/src/handlers/message-handlers/index.ts +77 -0
- package/src/handlers/message-handlers/list-rooms-response-handler.ts +28 -0
- package/src/handlers/message-handlers/ping-pong-handler.ts +30 -0
- package/src/handlers/message-handlers/regular-message-handler.ts +65 -0
- package/src/handlers/message-handlers/subscribe-response-handler.ts +47 -0
- package/src/handlers/message-handlers/task-response-handler.ts +45 -0
- package/src/handlers/message-handlers/types.ts +77 -0
- package/src/handlers/message-handlers/unsubscribe-response-handler.ts +47 -0
- package/src/handlers/webhook-handler.test.ts +789 -0
- package/src/handlers/webhook-handler.ts +576 -0
- package/src/index.ts +269 -0
- package/src/managers/agent-registry.test.ts +466 -0
- package/src/managers/agent-registry.ts +347 -0
- package/src/managers/connection-manager.ts +195 -0
- package/src/managers/index.ts +9 -0
- package/src/managers/message-router.ts +349 -0
- package/src/managers/room-manager.ts +248 -0
- package/src/teneo-sdk.ts +1022 -0
- package/src/types/config.test.ts +325 -0
- package/src/types/config.ts +799 -0
- package/src/types/error-codes.ts +44 -0
- package/src/types/events.test.ts +302 -0
- package/src/types/events.ts +382 -0
- package/src/types/health.ts +46 -0
- package/src/types/index.ts +199 -0
- package/src/types/messages.test.ts +660 -0
- package/src/types/messages.ts +570 -0
- package/src/types/validation.ts +123 -0
- package/src/utils/bounded-queue.test.ts +356 -0
- package/src/utils/bounded-queue.ts +205 -0
- package/src/utils/circuit-breaker.test.ts +394 -0
- package/src/utils/circuit-breaker.ts +262 -0
- package/src/utils/deduplication-cache.test.ts +380 -0
- package/src/utils/deduplication-cache.ts +198 -0
- package/src/utils/event-waiter.test.ts +381 -0
- package/src/utils/event-waiter.ts +172 -0
- package/src/utils/index.ts +74 -0
- package/src/utils/logger.ts +87 -0
- package/src/utils/rate-limiter.test.ts +341 -0
- package/src/utils/rate-limiter.ts +211 -0
- package/src/utils/retry-policy.test.ts +558 -0
- package/src/utils/retry-policy.ts +272 -0
- package/src/utils/secure-private-key.test.ts +356 -0
- package/src/utils/secure-private-key.ts +205 -0
- package/src/utils/signature-verifier.test.ts +464 -0
- package/src/utils/signature-verifier.ts +298 -0
- package/src/utils/ssrf-validator.test.ts +372 -0
- package/src/utils/ssrf-validator.ts +224 -0
- package/tests/integration/real-server.test.ts +740 -0
- package/tests/integration/websocket.test.ts +381 -0
- package/tests/integration-setup.ts +16 -0
- package/tests/setup.ts +34 -0
- package/tsconfig.json +32 -0
- package/vitest.config.ts +42 -0
- package/vitest.integration.config.ts +23 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Token Bucket Rate Limiter
|
|
4
|
+
* Prevents message flooding and ensures fair resource usage
|
|
5
|
+
*
|
|
6
|
+
* Uses the token bucket algorithm: tokens are added at a constant rate,
|
|
7
|
+
* and each operation consumes one token. When no tokens are available,
|
|
8
|
+
* operations must wait until tokens are replenished.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.TokenBucketRateLimiter = exports.RateLimitError = void 0;
|
|
12
|
+
/**
|
|
13
|
+
* Error thrown when rate limit is exceeded with tryConsume()
|
|
14
|
+
*/
|
|
15
|
+
class RateLimitError extends Error {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = 'RateLimitError';
|
|
19
|
+
if (Error.captureStackTrace) {
|
|
20
|
+
Error.captureStackTrace(this, RateLimitError);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.RateLimitError = RateLimitError;
|
|
25
|
+
/**
|
|
26
|
+
* Token bucket rate limiter for controlling operation frequency
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* // Allow 10 messages per second with burst of 20
|
|
31
|
+
* const limiter = new TokenBucketRateLimiter(10, 20);
|
|
32
|
+
*
|
|
33
|
+
* // Non-blocking check
|
|
34
|
+
* if (limiter.tryConsume()) {
|
|
35
|
+
* await sendMessage(msg);
|
|
36
|
+
* } else {
|
|
37
|
+
* console.log('Rate limit exceeded');
|
|
38
|
+
* }
|
|
39
|
+
*
|
|
40
|
+
* // Blocking wait (auto-waits for token)
|
|
41
|
+
* await limiter.consume();
|
|
42
|
+
* await sendMessage(msg);
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
class TokenBucketRateLimiter {
|
|
46
|
+
/**
|
|
47
|
+
* Creates a new token bucket rate limiter
|
|
48
|
+
*
|
|
49
|
+
* @param tokensPerSecond - Rate at which tokens are added (operations per second)
|
|
50
|
+
* @param maxBurst - Maximum tokens that can accumulate (burst capacity)
|
|
51
|
+
* @throws {Error} If tokensPerSecond or maxBurst is less than 1
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* // 10 ops/sec, burst up to 20
|
|
56
|
+
* const limiter = new TokenBucketRateLimiter(10, 20);
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
constructor(tokensPerSecond, maxBurst) {
|
|
60
|
+
this.tokensPerSecond = tokensPerSecond;
|
|
61
|
+
this.maxBurst = maxBurst;
|
|
62
|
+
if (tokensPerSecond < 1) {
|
|
63
|
+
throw new Error('TokenBucketRateLimiter tokensPerSecond must be at least 1');
|
|
64
|
+
}
|
|
65
|
+
if (maxBurst < 1) {
|
|
66
|
+
throw new Error('TokenBucketRateLimiter maxBurst must be at least 1');
|
|
67
|
+
}
|
|
68
|
+
this.tokens = maxBurst; // Start with full bucket
|
|
69
|
+
this.lastRefill = Date.now();
|
|
70
|
+
this.refillInterval = 1000 / tokensPerSecond; // ms between tokens
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Try to consume one token without blocking
|
|
74
|
+
*
|
|
75
|
+
* @returns true if token was consumed, false if no tokens available
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* if (limiter.tryConsume()) {
|
|
80
|
+
* // Proceed with operation
|
|
81
|
+
* } else {
|
|
82
|
+
* // Rate limited - handle accordingly
|
|
83
|
+
* }
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
tryConsume() {
|
|
87
|
+
this.refill();
|
|
88
|
+
if (this.tokens >= 1) {
|
|
89
|
+
this.tokens -= 1;
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Consume one token, waiting if necessary until a token is available
|
|
96
|
+
*
|
|
97
|
+
* @param timeout - Optional max wait time in ms (default: no timeout)
|
|
98
|
+
* @returns Promise that resolves when token is consumed
|
|
99
|
+
* @throws {RateLimitError} If timeout is exceeded
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* // Wait indefinitely for token
|
|
104
|
+
* await limiter.consume();
|
|
105
|
+
*
|
|
106
|
+
* // Wait max 5 seconds
|
|
107
|
+
* try {
|
|
108
|
+
* await limiter.consume(5000);
|
|
109
|
+
* } catch (error) {
|
|
110
|
+
* console.log('Timeout waiting for rate limit');
|
|
111
|
+
* }
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
async consume(timeout) {
|
|
115
|
+
const startTime = Date.now();
|
|
116
|
+
while (true) {
|
|
117
|
+
if (this.tryConsume()) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// Check timeout
|
|
121
|
+
const elapsed = Date.now() - startTime;
|
|
122
|
+
if (timeout !== undefined && elapsed >= timeout) {
|
|
123
|
+
throw new RateLimitError(`Rate limit timeout: no token available after ${timeout}ms`);
|
|
124
|
+
}
|
|
125
|
+
// Calculate wait time until next token
|
|
126
|
+
// If timeout is specified, don't wait longer than remaining timeout
|
|
127
|
+
const baseWaitTime = Math.min(this.refillInterval, 100);
|
|
128
|
+
const waitTime = timeout !== undefined
|
|
129
|
+
? Math.min(baseWaitTime, timeout - elapsed)
|
|
130
|
+
: baseWaitTime;
|
|
131
|
+
// If waitTime is very small or negative, check timeout immediately
|
|
132
|
+
if (waitTime <= 0) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
await this.sleep(waitTime);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get the number of tokens currently available
|
|
140
|
+
* Refills tokens before returning count
|
|
141
|
+
*
|
|
142
|
+
* @returns Number of tokens available (may be fractional before consumption)
|
|
143
|
+
*/
|
|
144
|
+
getAvailableTokens() {
|
|
145
|
+
this.refill();
|
|
146
|
+
return this.tokens;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Get rate limiter configuration
|
|
150
|
+
*
|
|
151
|
+
* @returns Configuration object with rate and burst capacity
|
|
152
|
+
*/
|
|
153
|
+
getConfig() {
|
|
154
|
+
return {
|
|
155
|
+
tokensPerSecond: this.tokensPerSecond,
|
|
156
|
+
maxBurst: this.maxBurst
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Reset the rate limiter to full capacity
|
|
161
|
+
* Useful for testing or manual reset scenarios
|
|
162
|
+
*/
|
|
163
|
+
reset() {
|
|
164
|
+
this.tokens = this.maxBurst;
|
|
165
|
+
this.lastRefill = Date.now();
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Refill tokens based on elapsed time since last refill
|
|
169
|
+
* Called automatically before token consumption
|
|
170
|
+
*/
|
|
171
|
+
refill() {
|
|
172
|
+
const now = Date.now();
|
|
173
|
+
const elapsed = now - this.lastRefill;
|
|
174
|
+
if (elapsed > 0) {
|
|
175
|
+
// Calculate tokens to add based on elapsed time
|
|
176
|
+
const tokensToAdd = elapsed / this.refillInterval;
|
|
177
|
+
// Add tokens, capped at maxBurst
|
|
178
|
+
this.tokens = Math.min(this.tokens + tokensToAdd, this.maxBurst);
|
|
179
|
+
this.lastRefill = now;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Sleep helper for async waiting
|
|
184
|
+
*/
|
|
185
|
+
sleep(ms) {
|
|
186
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
exports.TokenBucketRateLimiter = TokenBucketRateLimiter;
|
|
190
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/utils/rate-limiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RetryPolicy - Configurable retry strategy for connection and webhook retries
|
|
3
|
+
* Supports exponential, linear, and constant backoff strategies
|
|
4
|
+
*/
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
/**
|
|
7
|
+
* Retry strategy types
|
|
8
|
+
*/
|
|
9
|
+
export type RetryStrategyType = "exponential" | "linear" | "constant";
|
|
10
|
+
/**
|
|
11
|
+
* Retry strategy configuration
|
|
12
|
+
*/
|
|
13
|
+
export interface RetryStrategy {
|
|
14
|
+
/** Strategy type: exponential, linear, or constant */
|
|
15
|
+
type: RetryStrategyType;
|
|
16
|
+
/** Base delay in milliseconds (first retry delay) */
|
|
17
|
+
baseDelay: number;
|
|
18
|
+
/** Maximum delay cap in milliseconds */
|
|
19
|
+
maxDelay: number;
|
|
20
|
+
/** Maximum number of retry attempts (0 = no retries) */
|
|
21
|
+
maxAttempts: number;
|
|
22
|
+
/** Add random jitter to prevent thundering herd */
|
|
23
|
+
jitter: boolean;
|
|
24
|
+
/** Backoff multiplier for exponential strategy (default: 2) */
|
|
25
|
+
backoffMultiplier?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Zod schema for retry strategy validation
|
|
29
|
+
*/
|
|
30
|
+
export declare const RetryStrategySchema: z.ZodObject<{
|
|
31
|
+
type: z.ZodEnum<["exponential", "linear", "constant"]>;
|
|
32
|
+
baseDelay: z.ZodNumber;
|
|
33
|
+
maxDelay: z.ZodNumber;
|
|
34
|
+
maxAttempts: z.ZodNumber;
|
|
35
|
+
jitter: z.ZodBoolean;
|
|
36
|
+
backoffMultiplier: z.ZodOptional<z.ZodNumber>;
|
|
37
|
+
}, "strip", z.ZodTypeAny, {
|
|
38
|
+
type: "exponential" | "linear" | "constant";
|
|
39
|
+
baseDelay: number;
|
|
40
|
+
maxDelay: number;
|
|
41
|
+
maxAttempts: number;
|
|
42
|
+
jitter: boolean;
|
|
43
|
+
backoffMultiplier?: number | undefined;
|
|
44
|
+
}, {
|
|
45
|
+
type: "exponential" | "linear" | "constant";
|
|
46
|
+
baseDelay: number;
|
|
47
|
+
maxDelay: number;
|
|
48
|
+
maxAttempts: number;
|
|
49
|
+
jitter: boolean;
|
|
50
|
+
backoffMultiplier?: number | undefined;
|
|
51
|
+
}>;
|
|
52
|
+
/**
|
|
53
|
+
* RetryPolicy - Calculates retry delays and determines retry eligibility
|
|
54
|
+
*
|
|
55
|
+
* Supports three retry strategies:
|
|
56
|
+
* - **Exponential**: Delay increases exponentially (e.g., 1s, 2s, 4s, 8s, ...)
|
|
57
|
+
* - **Linear**: Delay increases linearly (e.g., 1s, 2s, 3s, 4s, ...)
|
|
58
|
+
* - **Constant**: Delay remains constant (e.g., 1s, 1s, 1s, 1s, ...)
|
|
59
|
+
*
|
|
60
|
+
* All strategies respect maxDelay cap and support optional jitter.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* // Exponential backoff with default multiplier of 2
|
|
65
|
+
* const policy = RetryPolicy.exponential(1000, 60000, 10, true);
|
|
66
|
+
* console.log(policy.calculateDelay(1)); // ~1000ms
|
|
67
|
+
* console.log(policy.calculateDelay(2)); // ~2000ms
|
|
68
|
+
* console.log(policy.calculateDelay(3)); // ~4000ms
|
|
69
|
+
*
|
|
70
|
+
* // Linear backoff
|
|
71
|
+
* const policy = RetryPolicy.linear(2000, 30000, 5, false);
|
|
72
|
+
* console.log(policy.calculateDelay(1)); // 2000ms
|
|
73
|
+
* console.log(policy.calculateDelay(2)); // 4000ms
|
|
74
|
+
* console.log(policy.calculateDelay(3)); // 6000ms
|
|
75
|
+
*
|
|
76
|
+
* // Constant delay
|
|
77
|
+
* const policy = RetryPolicy.constant(5000, 3);
|
|
78
|
+
* console.log(policy.calculateDelay(1)); // 5000ms
|
|
79
|
+
* console.log(policy.calculateDelay(2)); // 5000ms
|
|
80
|
+
* console.log(policy.calculateDelay(3)); // 5000ms
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export declare class RetryPolicy {
|
|
84
|
+
private readonly strategy;
|
|
85
|
+
/**
|
|
86
|
+
* Creates a new retry policy with the specified strategy
|
|
87
|
+
*
|
|
88
|
+
* @param strategy - Retry strategy configuration
|
|
89
|
+
* @throws {z.ZodError} If strategy is invalid
|
|
90
|
+
*/
|
|
91
|
+
constructor(strategy: RetryStrategy);
|
|
92
|
+
/**
|
|
93
|
+
* Calculates the retry delay for a given attempt number
|
|
94
|
+
*
|
|
95
|
+
* @param attempt - Attempt number (1-indexed: 1 = first retry)
|
|
96
|
+
* @returns Delay in milliseconds before next retry
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* const policy = RetryPolicy.exponential(1000, 60000, 10, true);
|
|
101
|
+
*
|
|
102
|
+
* // First retry after 1st failure
|
|
103
|
+
* policy.calculateDelay(1); // ~1000ms (+ jitter if enabled)
|
|
104
|
+
*
|
|
105
|
+
* // Second retry after 2nd failure
|
|
106
|
+
* policy.calculateDelay(2); // ~2000ms (+ jitter if enabled)
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
calculateDelay(attempt: number): number;
|
|
110
|
+
/**
|
|
111
|
+
* Determines if a retry should be attempted based on attempt count
|
|
112
|
+
*
|
|
113
|
+
* @param attempt - Current attempt number (1-indexed)
|
|
114
|
+
* @returns True if retry should be attempted, false otherwise
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```typescript
|
|
118
|
+
* const policy = RetryPolicy.exponential(1000, 60000, 3, false);
|
|
119
|
+
*
|
|
120
|
+
* policy.shouldRetry(1); // true
|
|
121
|
+
* policy.shouldRetry(2); // true
|
|
122
|
+
* policy.shouldRetry(3); // true
|
|
123
|
+
* policy.shouldRetry(4); // false - exceeded maxAttempts
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
shouldRetry(attempt: number): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Gets the retry strategy configuration
|
|
129
|
+
*
|
|
130
|
+
* @returns Copy of the retry strategy
|
|
131
|
+
*/
|
|
132
|
+
getStrategy(): Readonly<RetryStrategy>;
|
|
133
|
+
/**
|
|
134
|
+
* Creates a retry policy with exponential backoff
|
|
135
|
+
*
|
|
136
|
+
* Delay formula: `min(baseDelay * multiplier^(attempt-1), maxDelay) + jitter`
|
|
137
|
+
*
|
|
138
|
+
* @param baseDelay - Initial delay in milliseconds
|
|
139
|
+
* @param maxDelay - Maximum delay cap in milliseconds
|
|
140
|
+
* @param maxAttempts - Maximum retry attempts
|
|
141
|
+
* @param jitter - Add random jitter (0-1000ms)
|
|
142
|
+
* @param backoffMultiplier - Exponential multiplier (default: 2)
|
|
143
|
+
* @returns RetryPolicy instance with exponential strategy
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```typescript
|
|
147
|
+
* // Standard exponential backoff (2^n)
|
|
148
|
+
* const policy = RetryPolicy.exponential(1000, 60000, 10, true);
|
|
149
|
+
*
|
|
150
|
+
* // Faster exponential backoff (3^n)
|
|
151
|
+
* const aggressive = RetryPolicy.exponential(1000, 60000, 10, true, 3);
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
static exponential(baseDelay: number, maxDelay: number, maxAttempts: number, jitter: boolean, backoffMultiplier?: number): RetryPolicy;
|
|
155
|
+
/**
|
|
156
|
+
* Creates a retry policy with linear backoff
|
|
157
|
+
*
|
|
158
|
+
* Delay formula: `min(baseDelay * attempt, maxDelay) + jitter`
|
|
159
|
+
*
|
|
160
|
+
* @param baseDelay - Delay increment per attempt in milliseconds
|
|
161
|
+
* @param maxDelay - Maximum delay cap in milliseconds
|
|
162
|
+
* @param maxAttempts - Maximum retry attempts
|
|
163
|
+
* @param jitter - Add random jitter (0-1000ms)
|
|
164
|
+
* @returns RetryPolicy instance with linear strategy
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```typescript
|
|
168
|
+
* // Linear backoff: 2s, 4s, 6s, 8s, ...
|
|
169
|
+
* const policy = RetryPolicy.linear(2000, 30000, 10, false);
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
static linear(baseDelay: number, maxDelay: number, maxAttempts: number, jitter: boolean): RetryPolicy;
|
|
173
|
+
/**
|
|
174
|
+
* Creates a retry policy with constant delay
|
|
175
|
+
*
|
|
176
|
+
* Delay formula: `baseDelay + jitter`
|
|
177
|
+
*
|
|
178
|
+
* @param delay - Constant delay in milliseconds
|
|
179
|
+
* @param maxAttempts - Maximum retry attempts
|
|
180
|
+
* @param jitter - Add random jitter (0-1000ms, default: false)
|
|
181
|
+
* @returns RetryPolicy instance with constant strategy
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```typescript
|
|
185
|
+
* // Retry every 5 seconds, max 3 times
|
|
186
|
+
* const policy = RetryPolicy.constant(5000, 3);
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
static constant(delay: number, maxAttempts: number, jitter?: boolean): RetryPolicy;
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=retry-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-policy.d.ts","sourceRoot":"","sources":["../../src/utils/retry-policy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEtE;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,sDAAsD;IACtD,IAAI,EAAE,iBAAiB,CAAC;IACxB,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,MAAM,EAAE,OAAO,CAAC;IAChB,+DAA+D;IAC/D,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;EAO9B,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IAEzC;;;;;OAKG;gBACS,QAAQ,EAAE,aAAa;IAUnC;;;;;;;;;;;;;;;;OAgBG;IACI,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAoC9C;;;;;;;;;;;;;;;OAeG;IACI,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAI5C;;;;OAIG;IACI,WAAW,IAAI,QAAQ,CAAC,aAAa,CAAC;IAM7C;;;;;;;;;;;;;;;;;;;;OAoBG;WACW,WAAW,CACvB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,OAAO,EACf,iBAAiB,GAAE,MAAU,GAC5B,WAAW;IAWd;;;;;;;;;;;;;;;;OAgBG;WACW,MAAM,CAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,OAAO,GACd,WAAW;IAUd;;;;;;;;;;;;;;;OAeG;WACW,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAE,OAAe,GAAG,WAAW;CASjG"}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* RetryPolicy - Configurable retry strategy for connection and webhook retries
|
|
4
|
+
* Supports exponential, linear, and constant backoff strategies
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.RetryPolicy = exports.RetryStrategySchema = void 0;
|
|
8
|
+
const zod_1 = require("zod");
|
|
9
|
+
/**
|
|
10
|
+
* Zod schema for retry strategy validation
|
|
11
|
+
*/
|
|
12
|
+
exports.RetryStrategySchema = zod_1.z.object({
|
|
13
|
+
type: zod_1.z.enum(["exponential", "linear", "constant"]),
|
|
14
|
+
baseDelay: zod_1.z.number().min(0).max(300000), // 0-5 minutes
|
|
15
|
+
maxDelay: zod_1.z.number().min(0).max(3600000), // 0-1 hour
|
|
16
|
+
maxAttempts: zod_1.z.number().min(0).max(1000),
|
|
17
|
+
jitter: zod_1.z.boolean(),
|
|
18
|
+
backoffMultiplier: zod_1.z.number().min(1).max(10).optional()
|
|
19
|
+
});
|
|
20
|
+
/**
|
|
21
|
+
* RetryPolicy - Calculates retry delays and determines retry eligibility
|
|
22
|
+
*
|
|
23
|
+
* Supports three retry strategies:
|
|
24
|
+
* - **Exponential**: Delay increases exponentially (e.g., 1s, 2s, 4s, 8s, ...)
|
|
25
|
+
* - **Linear**: Delay increases linearly (e.g., 1s, 2s, 3s, 4s, ...)
|
|
26
|
+
* - **Constant**: Delay remains constant (e.g., 1s, 1s, 1s, 1s, ...)
|
|
27
|
+
*
|
|
28
|
+
* All strategies respect maxDelay cap and support optional jitter.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* // Exponential backoff with default multiplier of 2
|
|
33
|
+
* const policy = RetryPolicy.exponential(1000, 60000, 10, true);
|
|
34
|
+
* console.log(policy.calculateDelay(1)); // ~1000ms
|
|
35
|
+
* console.log(policy.calculateDelay(2)); // ~2000ms
|
|
36
|
+
* console.log(policy.calculateDelay(3)); // ~4000ms
|
|
37
|
+
*
|
|
38
|
+
* // Linear backoff
|
|
39
|
+
* const policy = RetryPolicy.linear(2000, 30000, 5, false);
|
|
40
|
+
* console.log(policy.calculateDelay(1)); // 2000ms
|
|
41
|
+
* console.log(policy.calculateDelay(2)); // 4000ms
|
|
42
|
+
* console.log(policy.calculateDelay(3)); // 6000ms
|
|
43
|
+
*
|
|
44
|
+
* // Constant delay
|
|
45
|
+
* const policy = RetryPolicy.constant(5000, 3);
|
|
46
|
+
* console.log(policy.calculateDelay(1)); // 5000ms
|
|
47
|
+
* console.log(policy.calculateDelay(2)); // 5000ms
|
|
48
|
+
* console.log(policy.calculateDelay(3)); // 5000ms
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
class RetryPolicy {
|
|
52
|
+
/**
|
|
53
|
+
* Creates a new retry policy with the specified strategy
|
|
54
|
+
*
|
|
55
|
+
* @param strategy - Retry strategy configuration
|
|
56
|
+
* @throws {z.ZodError} If strategy is invalid
|
|
57
|
+
*/
|
|
58
|
+
constructor(strategy) {
|
|
59
|
+
// Validate strategy with Zod
|
|
60
|
+
this.strategy = exports.RetryStrategySchema.parse(strategy);
|
|
61
|
+
// Additional validation: maxDelay must be >= baseDelay
|
|
62
|
+
if (this.strategy.maxDelay < this.strategy.baseDelay) {
|
|
63
|
+
throw new Error("maxDelay must be greater than or equal to baseDelay");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Calculates the retry delay for a given attempt number
|
|
68
|
+
*
|
|
69
|
+
* @param attempt - Attempt number (1-indexed: 1 = first retry)
|
|
70
|
+
* @returns Delay in milliseconds before next retry
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* const policy = RetryPolicy.exponential(1000, 60000, 10, true);
|
|
75
|
+
*
|
|
76
|
+
* // First retry after 1st failure
|
|
77
|
+
* policy.calculateDelay(1); // ~1000ms (+ jitter if enabled)
|
|
78
|
+
*
|
|
79
|
+
* // Second retry after 2nd failure
|
|
80
|
+
* policy.calculateDelay(2); // ~2000ms (+ jitter if enabled)
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
calculateDelay(attempt) {
|
|
84
|
+
// Validate attempt number
|
|
85
|
+
if (attempt < 1) {
|
|
86
|
+
throw new Error("Attempt number must be >= 1");
|
|
87
|
+
}
|
|
88
|
+
let delay;
|
|
89
|
+
switch (this.strategy.type) {
|
|
90
|
+
case "exponential": {
|
|
91
|
+
const multiplier = this.strategy.backoffMultiplier || 2;
|
|
92
|
+
delay = this.strategy.baseDelay * Math.pow(multiplier, attempt - 1);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
case "linear": {
|
|
96
|
+
delay = this.strategy.baseDelay * attempt;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case "constant": {
|
|
100
|
+
delay = this.strategy.baseDelay;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Cap at maxDelay
|
|
105
|
+
delay = Math.min(delay, this.strategy.maxDelay);
|
|
106
|
+
// Add jitter if enabled
|
|
107
|
+
if (this.strategy.jitter) {
|
|
108
|
+
// Add random jitter between 0 and 1000ms
|
|
109
|
+
delay += Math.random() * 1000;
|
|
110
|
+
}
|
|
111
|
+
return Math.floor(delay);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Determines if a retry should be attempted based on attempt count
|
|
115
|
+
*
|
|
116
|
+
* @param attempt - Current attempt number (1-indexed)
|
|
117
|
+
* @returns True if retry should be attempted, false otherwise
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* const policy = RetryPolicy.exponential(1000, 60000, 3, false);
|
|
122
|
+
*
|
|
123
|
+
* policy.shouldRetry(1); // true
|
|
124
|
+
* policy.shouldRetry(2); // true
|
|
125
|
+
* policy.shouldRetry(3); // true
|
|
126
|
+
* policy.shouldRetry(4); // false - exceeded maxAttempts
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
shouldRetry(attempt) {
|
|
130
|
+
return attempt <= this.strategy.maxAttempts;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Gets the retry strategy configuration
|
|
134
|
+
*
|
|
135
|
+
* @returns Copy of the retry strategy
|
|
136
|
+
*/
|
|
137
|
+
getStrategy() {
|
|
138
|
+
return { ...this.strategy };
|
|
139
|
+
}
|
|
140
|
+
// Static factory methods for common patterns
|
|
141
|
+
/**
|
|
142
|
+
* Creates a retry policy with exponential backoff
|
|
143
|
+
*
|
|
144
|
+
* Delay formula: `min(baseDelay * multiplier^(attempt-1), maxDelay) + jitter`
|
|
145
|
+
*
|
|
146
|
+
* @param baseDelay - Initial delay in milliseconds
|
|
147
|
+
* @param maxDelay - Maximum delay cap in milliseconds
|
|
148
|
+
* @param maxAttempts - Maximum retry attempts
|
|
149
|
+
* @param jitter - Add random jitter (0-1000ms)
|
|
150
|
+
* @param backoffMultiplier - Exponential multiplier (default: 2)
|
|
151
|
+
* @returns RetryPolicy instance with exponential strategy
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* // Standard exponential backoff (2^n)
|
|
156
|
+
* const policy = RetryPolicy.exponential(1000, 60000, 10, true);
|
|
157
|
+
*
|
|
158
|
+
* // Faster exponential backoff (3^n)
|
|
159
|
+
* const aggressive = RetryPolicy.exponential(1000, 60000, 10, true, 3);
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
static exponential(baseDelay, maxDelay, maxAttempts, jitter, backoffMultiplier = 2) {
|
|
163
|
+
return new RetryPolicy({
|
|
164
|
+
type: "exponential",
|
|
165
|
+
baseDelay,
|
|
166
|
+
maxDelay,
|
|
167
|
+
maxAttempts,
|
|
168
|
+
jitter,
|
|
169
|
+
backoffMultiplier
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Creates a retry policy with linear backoff
|
|
174
|
+
*
|
|
175
|
+
* Delay formula: `min(baseDelay * attempt, maxDelay) + jitter`
|
|
176
|
+
*
|
|
177
|
+
* @param baseDelay - Delay increment per attempt in milliseconds
|
|
178
|
+
* @param maxDelay - Maximum delay cap in milliseconds
|
|
179
|
+
* @param maxAttempts - Maximum retry attempts
|
|
180
|
+
* @param jitter - Add random jitter (0-1000ms)
|
|
181
|
+
* @returns RetryPolicy instance with linear strategy
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```typescript
|
|
185
|
+
* // Linear backoff: 2s, 4s, 6s, 8s, ...
|
|
186
|
+
* const policy = RetryPolicy.linear(2000, 30000, 10, false);
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
static linear(baseDelay, maxDelay, maxAttempts, jitter) {
|
|
190
|
+
return new RetryPolicy({
|
|
191
|
+
type: "linear",
|
|
192
|
+
baseDelay,
|
|
193
|
+
maxDelay,
|
|
194
|
+
maxAttempts,
|
|
195
|
+
jitter
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Creates a retry policy with constant delay
|
|
200
|
+
*
|
|
201
|
+
* Delay formula: `baseDelay + jitter`
|
|
202
|
+
*
|
|
203
|
+
* @param delay - Constant delay in milliseconds
|
|
204
|
+
* @param maxAttempts - Maximum retry attempts
|
|
205
|
+
* @param jitter - Add random jitter (0-1000ms, default: false)
|
|
206
|
+
* @returns RetryPolicy instance with constant strategy
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```typescript
|
|
210
|
+
* // Retry every 5 seconds, max 3 times
|
|
211
|
+
* const policy = RetryPolicy.constant(5000, 3);
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
static constant(delay, maxAttempts, jitter = false) {
|
|
215
|
+
return new RetryPolicy({
|
|
216
|
+
type: "constant",
|
|
217
|
+
baseDelay: delay,
|
|
218
|
+
maxDelay: delay,
|
|
219
|
+
maxAttempts,
|
|
220
|
+
jitter
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
exports.RetryPolicy = RetryPolicy;
|
|
225
|
+
//# sourceMappingURL=retry-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-policy.js","sourceRoot":"","sources":["../../src/utils/retry-policy.ts"],"names":[],"mappings":"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"}
|