awaitly 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/LICENSE +21 -0
- package/README.md +1278 -0
- package/dist/batch.cjs +2 -0
- package/dist/batch.cjs.map +1 -0
- package/dist/batch.d.cts +197 -0
- package/dist/batch.d.ts +197 -0
- package/dist/batch.js +2 -0
- package/dist/batch.js.map +1 -0
- package/dist/circuit-breaker.cjs +2 -0
- package/dist/circuit-breaker.cjs.map +1 -0
- package/dist/circuit-breaker.d.cts +208 -0
- package/dist/circuit-breaker.d.ts +208 -0
- package/dist/circuit-breaker.js +2 -0
- package/dist/circuit-breaker.js.map +1 -0
- package/dist/conditional.cjs +2 -0
- package/dist/conditional.cjs.map +1 -0
- package/dist/conditional.d.cts +249 -0
- package/dist/conditional.d.ts +249 -0
- package/dist/conditional.js +2 -0
- package/dist/conditional.js.map +1 -0
- package/dist/core-BuTBsR0x.d.cts +2325 -0
- package/dist/core-BuTBsR0x.d.ts +2325 -0
- package/dist/core.cjs +2 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +3 -0
- package/dist/core.d.ts +3 -0
- package/dist/core.js +2 -0
- package/dist/core.js.map +1 -0
- package/dist/devtools.cjs +11 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.d.cts +176 -0
- package/dist/devtools.d.ts +176 -0
- package/dist/devtools.js +11 -0
- package/dist/devtools.js.map +1 -0
- package/dist/duration.cjs +2 -0
- package/dist/duration.cjs.map +1 -0
- package/dist/duration.d.cts +246 -0
- package/dist/duration.d.ts +246 -0
- package/dist/duration.js +2 -0
- package/dist/duration.js.map +1 -0
- package/dist/hitl.cjs +2 -0
- package/dist/hitl.cjs.map +1 -0
- package/dist/hitl.d.cts +337 -0
- package/dist/hitl.d.ts +337 -0
- package/dist/hitl.js +2 -0
- package/dist/hitl.js.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/match.cjs +2 -0
- package/dist/match.cjs.map +1 -0
- package/dist/match.d.cts +209 -0
- package/dist/match.d.ts +209 -0
- package/dist/match.js +2 -0
- package/dist/match.js.map +1 -0
- package/dist/otel.cjs +2 -0
- package/dist/otel.cjs.map +1 -0
- package/dist/otel.d.cts +185 -0
- package/dist/otel.d.ts +185 -0
- package/dist/otel.js +2 -0
- package/dist/otel.js.map +1 -0
- package/dist/persistence.cjs +2 -0
- package/dist/persistence.cjs.map +1 -0
- package/dist/persistence.d.cts +572 -0
- package/dist/persistence.d.ts +572 -0
- package/dist/persistence.js +2 -0
- package/dist/persistence.js.map +1 -0
- package/dist/policies.cjs +2 -0
- package/dist/policies.cjs.map +1 -0
- package/dist/policies.d.cts +378 -0
- package/dist/policies.d.ts +378 -0
- package/dist/policies.js +2 -0
- package/dist/policies.js.map +1 -0
- package/dist/ratelimit.cjs +2 -0
- package/dist/ratelimit.cjs.map +1 -0
- package/dist/ratelimit.d.cts +279 -0
- package/dist/ratelimit.d.ts +279 -0
- package/dist/ratelimit.js +2 -0
- package/dist/ratelimit.js.map +1 -0
- package/dist/reliability.cjs +2 -0
- package/dist/reliability.cjs.map +1 -0
- package/dist/reliability.d.cts +5 -0
- package/dist/reliability.d.ts +5 -0
- package/dist/reliability.js +2 -0
- package/dist/reliability.js.map +1 -0
- package/dist/resource.cjs +2 -0
- package/dist/resource.cjs.map +1 -0
- package/dist/resource.d.cts +171 -0
- package/dist/resource.d.ts +171 -0
- package/dist/resource.js +2 -0
- package/dist/resource.js.map +1 -0
- package/dist/retry.cjs +2 -0
- package/dist/retry.cjs.map +1 -0
- package/dist/retry.d.cts +2 -0
- package/dist/retry.d.ts +2 -0
- package/dist/retry.js +2 -0
- package/dist/retry.js.map +1 -0
- package/dist/saga.cjs +2 -0
- package/dist/saga.cjs.map +1 -0
- package/dist/saga.d.cts +231 -0
- package/dist/saga.d.ts +231 -0
- package/dist/saga.js +2 -0
- package/dist/saga.js.map +1 -0
- package/dist/schedule.cjs +2 -0
- package/dist/schedule.cjs.map +1 -0
- package/dist/schedule.d.cts +387 -0
- package/dist/schedule.d.ts +387 -0
- package/dist/schedule.js +2 -0
- package/dist/schedule.js.map +1 -0
- package/dist/tagged-error.cjs +2 -0
- package/dist/tagged-error.cjs.map +1 -0
- package/dist/tagged-error.d.cts +252 -0
- package/dist/tagged-error.d.ts +252 -0
- package/dist/tagged-error.js +2 -0
- package/dist/tagged-error.js.map +1 -0
- package/dist/testing.cjs +2 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +228 -0
- package/dist/testing.d.ts +228 -0
- package/dist/testing.js +2 -0
- package/dist/testing.js.map +1 -0
- package/dist/visualize.cjs +1573 -0
- package/dist/visualize.cjs.map +1 -0
- package/dist/visualize.d.cts +1415 -0
- package/dist/visualize.d.ts +1415 -0
- package/dist/visualize.js +1573 -0
- package/dist/visualize.js.map +1 -0
- package/dist/webhook.cjs +2 -0
- package/dist/webhook.cjs.map +1 -0
- package/dist/webhook.d.cts +469 -0
- package/dist/webhook.d.ts +469 -0
- package/dist/webhook.js +2 -0
- package/dist/webhook.js.map +1 -0
- package/dist/workflow-entry-C6nH8ByN.d.ts +858 -0
- package/dist/workflow-entry-RRTlSg_4.d.cts +858 -0
- package/dist/workflow.cjs +2 -0
- package/dist/workflow.cjs.map +1 -0
- package/dist/workflow.d.cts +2 -0
- package/dist/workflow.d.ts +2 -0
- package/dist/workflow.js +2 -0
- package/dist/workflow.js.map +1 -0
- package/docs/advanced.md +1548 -0
- package/docs/api.md +513 -0
- package/docs/coming-from-neverthrow.md +1013 -0
- package/docs/match.md +417 -0
- package/docs/pino-logging-example.md +396 -0
- package/docs/policies.md +508 -0
- package/docs/resource-management.md +509 -0
- package/docs/schedule.md +467 -0
- package/docs/tagged-error.md +785 -0
- package/docs/visualization.md +430 -0
- package/docs/visualize-examples.md +330 -0
- package/package.json +227 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { R as Result, A as AsyncResult } from './core-BuTBsR0x.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Rate Limiting / Concurrency Control
|
|
5
|
+
*
|
|
6
|
+
* Control throughput for steps that hit rate-limited APIs or shared resources.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { createRateLimiter, createConcurrencyLimiter } from 'awaitly';
|
|
11
|
+
*
|
|
12
|
+
* // Rate limiting (requests per second)
|
|
13
|
+
* const rateLimiter = createRateLimiter({ maxPerSecond: 10 });
|
|
14
|
+
*
|
|
15
|
+
* // Concurrency limiting (max concurrent)
|
|
16
|
+
* const concurrencyLimiter = createConcurrencyLimiter({ maxConcurrent: 5 });
|
|
17
|
+
*
|
|
18
|
+
* const result = await workflow(async (step) => {
|
|
19
|
+
* // Wrap operations with rate limiting
|
|
20
|
+
* const data = await rateLimiter.execute(() =>
|
|
21
|
+
* step(() => callRateLimitedApi())
|
|
22
|
+
* );
|
|
23
|
+
*
|
|
24
|
+
* // Wrap batch operations with concurrency control
|
|
25
|
+
* const results = await concurrencyLimiter.executeAll(
|
|
26
|
+
* ids.map(id => () => step(() => fetchItem(id)))
|
|
27
|
+
* );
|
|
28
|
+
*
|
|
29
|
+
* return { data, results };
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Configuration for rate limiter.
|
|
36
|
+
*/
|
|
37
|
+
interface RateLimiterConfig {
|
|
38
|
+
/**
|
|
39
|
+
* Maximum operations per second.
|
|
40
|
+
*/
|
|
41
|
+
maxPerSecond: number;
|
|
42
|
+
/**
|
|
43
|
+
* Burst capacity - allows brief spikes above the rate.
|
|
44
|
+
* @default maxPerSecond * 2
|
|
45
|
+
*/
|
|
46
|
+
burstCapacity?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Strategy when rate limit is exceeded.
|
|
49
|
+
* - 'wait': Wait until a slot is available (default)
|
|
50
|
+
* - 'reject': Reject immediately with error
|
|
51
|
+
* @default 'wait'
|
|
52
|
+
*/
|
|
53
|
+
strategy?: "wait" | "reject";
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Configuration for concurrency limiter.
|
|
57
|
+
*/
|
|
58
|
+
interface ConcurrencyLimiterConfig {
|
|
59
|
+
/**
|
|
60
|
+
* Maximum concurrent operations.
|
|
61
|
+
*/
|
|
62
|
+
maxConcurrent: number;
|
|
63
|
+
/**
|
|
64
|
+
* Strategy when limit is reached.
|
|
65
|
+
* - 'queue': Queue and wait (default)
|
|
66
|
+
* - 'reject': Reject immediately
|
|
67
|
+
* @default 'queue'
|
|
68
|
+
*/
|
|
69
|
+
strategy?: "queue" | "reject";
|
|
70
|
+
/**
|
|
71
|
+
* Maximum queue size (only for 'queue' strategy).
|
|
72
|
+
* @default Infinity
|
|
73
|
+
*/
|
|
74
|
+
maxQueueSize?: number;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Error when rate/concurrency limit is exceeded and strategy is 'reject'.
|
|
78
|
+
*/
|
|
79
|
+
interface RateLimitExceededError {
|
|
80
|
+
type: "RATE_LIMIT_EXCEEDED";
|
|
81
|
+
limiterName: string;
|
|
82
|
+
retryAfterMs?: number;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Error when concurrency limit queue is full.
|
|
86
|
+
*/
|
|
87
|
+
interface QueueFullError {
|
|
88
|
+
type: "QUEUE_FULL";
|
|
89
|
+
limiterName: string;
|
|
90
|
+
queueSize: number;
|
|
91
|
+
maxQueueSize: number;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Type guard for RateLimitExceededError.
|
|
95
|
+
*/
|
|
96
|
+
declare function isRateLimitExceededError(error: unknown): error is RateLimitExceededError;
|
|
97
|
+
/**
|
|
98
|
+
* Type guard for QueueFullError.
|
|
99
|
+
*/
|
|
100
|
+
declare function isQueueFullError(error: unknown): error is QueueFullError;
|
|
101
|
+
/**
|
|
102
|
+
* Statistics for rate limiter.
|
|
103
|
+
*/
|
|
104
|
+
interface RateLimiterStats {
|
|
105
|
+
availableTokens: number;
|
|
106
|
+
maxTokens: number;
|
|
107
|
+
tokensPerSecond: number;
|
|
108
|
+
waitingCount: number;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Statistics for concurrency limiter.
|
|
112
|
+
*/
|
|
113
|
+
interface ConcurrencyLimiterStats {
|
|
114
|
+
activeCount: number;
|
|
115
|
+
maxConcurrent: number;
|
|
116
|
+
queueSize: number;
|
|
117
|
+
maxQueueSize: number;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Rate limiter interface.
|
|
121
|
+
*/
|
|
122
|
+
interface RateLimiter {
|
|
123
|
+
/**
|
|
124
|
+
* Execute an operation with rate limiting.
|
|
125
|
+
* @param operation - The operation to execute
|
|
126
|
+
* @returns The operation result
|
|
127
|
+
*/
|
|
128
|
+
execute<T>(operation: () => T | Promise<T>): Promise<T>;
|
|
129
|
+
/**
|
|
130
|
+
* Execute a Result-returning operation with rate limiting.
|
|
131
|
+
*/
|
|
132
|
+
executeResult<T, E>(operation: () => Result<T, E> | AsyncResult<T, E>): AsyncResult<T, E | RateLimitExceededError>;
|
|
133
|
+
/**
|
|
134
|
+
* Get current statistics.
|
|
135
|
+
*/
|
|
136
|
+
getStats(): RateLimiterStats;
|
|
137
|
+
/**
|
|
138
|
+
* Reset the rate limiter.
|
|
139
|
+
*/
|
|
140
|
+
reset(): void;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Create a token bucket rate limiter.
|
|
144
|
+
*
|
|
145
|
+
* @param name - Name for the limiter (used in errors)
|
|
146
|
+
* @param config - Rate limiter configuration
|
|
147
|
+
* @returns A RateLimiter instance
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```typescript
|
|
151
|
+
* const limiter = createRateLimiter('api-calls', {
|
|
152
|
+
* maxPerSecond: 10,
|
|
153
|
+
* burstCapacity: 20,
|
|
154
|
+
* });
|
|
155
|
+
*
|
|
156
|
+
* // In workflow
|
|
157
|
+
* const data = await limiter.execute(() =>
|
|
158
|
+
* step(() => callApi())
|
|
159
|
+
* );
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
declare function createRateLimiter(name: string, config: RateLimiterConfig): RateLimiter;
|
|
163
|
+
/**
|
|
164
|
+
* Concurrency limiter interface.
|
|
165
|
+
*/
|
|
166
|
+
interface ConcurrencyLimiter {
|
|
167
|
+
/**
|
|
168
|
+
* Execute an operation with concurrency limiting.
|
|
169
|
+
* @param operation - The operation to execute
|
|
170
|
+
* @returns The operation result
|
|
171
|
+
*/
|
|
172
|
+
execute<T>(operation: () => T | Promise<T>): Promise<T>;
|
|
173
|
+
/**
|
|
174
|
+
* Execute multiple operations with concurrency control.
|
|
175
|
+
* @param operations - Array of operation factories
|
|
176
|
+
* @returns Array of results (in order)
|
|
177
|
+
*/
|
|
178
|
+
executeAll<T>(operations: Array<() => T | Promise<T>>): Promise<T[]>;
|
|
179
|
+
/**
|
|
180
|
+
* Execute a Result-returning operation with concurrency limiting.
|
|
181
|
+
*/
|
|
182
|
+
executeResult<T, E>(operation: () => Result<T, E> | AsyncResult<T, E>): AsyncResult<T, E | QueueFullError>;
|
|
183
|
+
/**
|
|
184
|
+
* Get current statistics.
|
|
185
|
+
*/
|
|
186
|
+
getStats(): ConcurrencyLimiterStats;
|
|
187
|
+
/**
|
|
188
|
+
* Reset the concurrency limiter.
|
|
189
|
+
*/
|
|
190
|
+
reset(): void;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Create a concurrency limiter.
|
|
194
|
+
*
|
|
195
|
+
* @param name - Name for the limiter (used in errors)
|
|
196
|
+
* @param config - Concurrency limiter configuration
|
|
197
|
+
* @returns A ConcurrencyLimiter instance
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```typescript
|
|
201
|
+
* const limiter = createConcurrencyLimiter('db-pool', {
|
|
202
|
+
* maxConcurrent: 10,
|
|
203
|
+
* });
|
|
204
|
+
*
|
|
205
|
+
* // Execute with concurrency control
|
|
206
|
+
* const results = await limiter.executeAll(
|
|
207
|
+
* ids.map(id => () => fetchItem(id))
|
|
208
|
+
* );
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
declare function createConcurrencyLimiter(name: string, config: ConcurrencyLimiterConfig): ConcurrencyLimiter;
|
|
212
|
+
/**
|
|
213
|
+
* Configuration for combined rate + concurrency limiter.
|
|
214
|
+
*/
|
|
215
|
+
interface CombinedLimiterConfig {
|
|
216
|
+
/**
|
|
217
|
+
* Rate limiting configuration.
|
|
218
|
+
*/
|
|
219
|
+
rate?: RateLimiterConfig;
|
|
220
|
+
/**
|
|
221
|
+
* Concurrency limiting configuration.
|
|
222
|
+
*/
|
|
223
|
+
concurrency?: ConcurrencyLimiterConfig;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Create a combined rate + concurrency limiter.
|
|
227
|
+
*
|
|
228
|
+
* Operations are first rate-limited, then concurrency-limited.
|
|
229
|
+
*
|
|
230
|
+
* @param name - Name for the limiter
|
|
231
|
+
* @param config - Combined limiter configuration
|
|
232
|
+
* @returns An object with both limiters and a combined execute function
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```typescript
|
|
236
|
+
* const limiter = createCombinedLimiter('api', {
|
|
237
|
+
* rate: { maxPerSecond: 10 },
|
|
238
|
+
* concurrency: { maxConcurrent: 5 },
|
|
239
|
+
* });
|
|
240
|
+
*
|
|
241
|
+
* const result = await limiter.execute(() => callApi());
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
declare function createCombinedLimiter(name: string, config: CombinedLimiterConfig): {
|
|
245
|
+
rate?: RateLimiter;
|
|
246
|
+
concurrency?: ConcurrencyLimiter;
|
|
247
|
+
execute: <T>(operation: () => T | Promise<T>) => Promise<T>;
|
|
248
|
+
};
|
|
249
|
+
/**
|
|
250
|
+
* Preset configurations for common use cases.
|
|
251
|
+
*/
|
|
252
|
+
declare const rateLimiterPresets: {
|
|
253
|
+
/**
|
|
254
|
+
* Typical API rate limit (10 req/s).
|
|
255
|
+
*/
|
|
256
|
+
readonly api: {
|
|
257
|
+
maxPerSecond: number;
|
|
258
|
+
burstCapacity: number;
|
|
259
|
+
strategy: "wait";
|
|
260
|
+
};
|
|
261
|
+
/**
|
|
262
|
+
* Database pool limit (concurrent connections).
|
|
263
|
+
*/
|
|
264
|
+
readonly database: {
|
|
265
|
+
maxConcurrent: number;
|
|
266
|
+
strategy: "queue";
|
|
267
|
+
maxQueueSize: number;
|
|
268
|
+
};
|
|
269
|
+
/**
|
|
270
|
+
* Aggressive rate limit for external APIs (5 req/s).
|
|
271
|
+
*/
|
|
272
|
+
readonly external: {
|
|
273
|
+
maxPerSecond: number;
|
|
274
|
+
burstCapacity: number;
|
|
275
|
+
strategy: "wait";
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
export { type CombinedLimiterConfig, type ConcurrencyLimiter, type ConcurrencyLimiterConfig, type ConcurrencyLimiterStats, type QueueFullError, type RateLimitExceededError, type RateLimiter, type RateLimiterConfig, type RateLimiterStats, createCombinedLimiter, createConcurrencyLimiter, createRateLimiter, isQueueFullError, isRateLimitExceededError, rateLimiterPresets };
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { R as Result, A as AsyncResult } from './core-BuTBsR0x.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Rate Limiting / Concurrency Control
|
|
5
|
+
*
|
|
6
|
+
* Control throughput for steps that hit rate-limited APIs or shared resources.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { createRateLimiter, createConcurrencyLimiter } from 'awaitly';
|
|
11
|
+
*
|
|
12
|
+
* // Rate limiting (requests per second)
|
|
13
|
+
* const rateLimiter = createRateLimiter({ maxPerSecond: 10 });
|
|
14
|
+
*
|
|
15
|
+
* // Concurrency limiting (max concurrent)
|
|
16
|
+
* const concurrencyLimiter = createConcurrencyLimiter({ maxConcurrent: 5 });
|
|
17
|
+
*
|
|
18
|
+
* const result = await workflow(async (step) => {
|
|
19
|
+
* // Wrap operations with rate limiting
|
|
20
|
+
* const data = await rateLimiter.execute(() =>
|
|
21
|
+
* step(() => callRateLimitedApi())
|
|
22
|
+
* );
|
|
23
|
+
*
|
|
24
|
+
* // Wrap batch operations with concurrency control
|
|
25
|
+
* const results = await concurrencyLimiter.executeAll(
|
|
26
|
+
* ids.map(id => () => step(() => fetchItem(id)))
|
|
27
|
+
* );
|
|
28
|
+
*
|
|
29
|
+
* return { data, results };
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Configuration for rate limiter.
|
|
36
|
+
*/
|
|
37
|
+
interface RateLimiterConfig {
|
|
38
|
+
/**
|
|
39
|
+
* Maximum operations per second.
|
|
40
|
+
*/
|
|
41
|
+
maxPerSecond: number;
|
|
42
|
+
/**
|
|
43
|
+
* Burst capacity - allows brief spikes above the rate.
|
|
44
|
+
* @default maxPerSecond * 2
|
|
45
|
+
*/
|
|
46
|
+
burstCapacity?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Strategy when rate limit is exceeded.
|
|
49
|
+
* - 'wait': Wait until a slot is available (default)
|
|
50
|
+
* - 'reject': Reject immediately with error
|
|
51
|
+
* @default 'wait'
|
|
52
|
+
*/
|
|
53
|
+
strategy?: "wait" | "reject";
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Configuration for concurrency limiter.
|
|
57
|
+
*/
|
|
58
|
+
interface ConcurrencyLimiterConfig {
|
|
59
|
+
/**
|
|
60
|
+
* Maximum concurrent operations.
|
|
61
|
+
*/
|
|
62
|
+
maxConcurrent: number;
|
|
63
|
+
/**
|
|
64
|
+
* Strategy when limit is reached.
|
|
65
|
+
* - 'queue': Queue and wait (default)
|
|
66
|
+
* - 'reject': Reject immediately
|
|
67
|
+
* @default 'queue'
|
|
68
|
+
*/
|
|
69
|
+
strategy?: "queue" | "reject";
|
|
70
|
+
/**
|
|
71
|
+
* Maximum queue size (only for 'queue' strategy).
|
|
72
|
+
* @default Infinity
|
|
73
|
+
*/
|
|
74
|
+
maxQueueSize?: number;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Error when rate/concurrency limit is exceeded and strategy is 'reject'.
|
|
78
|
+
*/
|
|
79
|
+
interface RateLimitExceededError {
|
|
80
|
+
type: "RATE_LIMIT_EXCEEDED";
|
|
81
|
+
limiterName: string;
|
|
82
|
+
retryAfterMs?: number;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Error when concurrency limit queue is full.
|
|
86
|
+
*/
|
|
87
|
+
interface QueueFullError {
|
|
88
|
+
type: "QUEUE_FULL";
|
|
89
|
+
limiterName: string;
|
|
90
|
+
queueSize: number;
|
|
91
|
+
maxQueueSize: number;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Type guard for RateLimitExceededError.
|
|
95
|
+
*/
|
|
96
|
+
declare function isRateLimitExceededError(error: unknown): error is RateLimitExceededError;
|
|
97
|
+
/**
|
|
98
|
+
* Type guard for QueueFullError.
|
|
99
|
+
*/
|
|
100
|
+
declare function isQueueFullError(error: unknown): error is QueueFullError;
|
|
101
|
+
/**
|
|
102
|
+
* Statistics for rate limiter.
|
|
103
|
+
*/
|
|
104
|
+
interface RateLimiterStats {
|
|
105
|
+
availableTokens: number;
|
|
106
|
+
maxTokens: number;
|
|
107
|
+
tokensPerSecond: number;
|
|
108
|
+
waitingCount: number;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Statistics for concurrency limiter.
|
|
112
|
+
*/
|
|
113
|
+
interface ConcurrencyLimiterStats {
|
|
114
|
+
activeCount: number;
|
|
115
|
+
maxConcurrent: number;
|
|
116
|
+
queueSize: number;
|
|
117
|
+
maxQueueSize: number;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Rate limiter interface.
|
|
121
|
+
*/
|
|
122
|
+
interface RateLimiter {
|
|
123
|
+
/**
|
|
124
|
+
* Execute an operation with rate limiting.
|
|
125
|
+
* @param operation - The operation to execute
|
|
126
|
+
* @returns The operation result
|
|
127
|
+
*/
|
|
128
|
+
execute<T>(operation: () => T | Promise<T>): Promise<T>;
|
|
129
|
+
/**
|
|
130
|
+
* Execute a Result-returning operation with rate limiting.
|
|
131
|
+
*/
|
|
132
|
+
executeResult<T, E>(operation: () => Result<T, E> | AsyncResult<T, E>): AsyncResult<T, E | RateLimitExceededError>;
|
|
133
|
+
/**
|
|
134
|
+
* Get current statistics.
|
|
135
|
+
*/
|
|
136
|
+
getStats(): RateLimiterStats;
|
|
137
|
+
/**
|
|
138
|
+
* Reset the rate limiter.
|
|
139
|
+
*/
|
|
140
|
+
reset(): void;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Create a token bucket rate limiter.
|
|
144
|
+
*
|
|
145
|
+
* @param name - Name for the limiter (used in errors)
|
|
146
|
+
* @param config - Rate limiter configuration
|
|
147
|
+
* @returns A RateLimiter instance
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```typescript
|
|
151
|
+
* const limiter = createRateLimiter('api-calls', {
|
|
152
|
+
* maxPerSecond: 10,
|
|
153
|
+
* burstCapacity: 20,
|
|
154
|
+
* });
|
|
155
|
+
*
|
|
156
|
+
* // In workflow
|
|
157
|
+
* const data = await limiter.execute(() =>
|
|
158
|
+
* step(() => callApi())
|
|
159
|
+
* );
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
declare function createRateLimiter(name: string, config: RateLimiterConfig): RateLimiter;
|
|
163
|
+
/**
|
|
164
|
+
* Concurrency limiter interface.
|
|
165
|
+
*/
|
|
166
|
+
interface ConcurrencyLimiter {
|
|
167
|
+
/**
|
|
168
|
+
* Execute an operation with concurrency limiting.
|
|
169
|
+
* @param operation - The operation to execute
|
|
170
|
+
* @returns The operation result
|
|
171
|
+
*/
|
|
172
|
+
execute<T>(operation: () => T | Promise<T>): Promise<T>;
|
|
173
|
+
/**
|
|
174
|
+
* Execute multiple operations with concurrency control.
|
|
175
|
+
* @param operations - Array of operation factories
|
|
176
|
+
* @returns Array of results (in order)
|
|
177
|
+
*/
|
|
178
|
+
executeAll<T>(operations: Array<() => T | Promise<T>>): Promise<T[]>;
|
|
179
|
+
/**
|
|
180
|
+
* Execute a Result-returning operation with concurrency limiting.
|
|
181
|
+
*/
|
|
182
|
+
executeResult<T, E>(operation: () => Result<T, E> | AsyncResult<T, E>): AsyncResult<T, E | QueueFullError>;
|
|
183
|
+
/**
|
|
184
|
+
* Get current statistics.
|
|
185
|
+
*/
|
|
186
|
+
getStats(): ConcurrencyLimiterStats;
|
|
187
|
+
/**
|
|
188
|
+
* Reset the concurrency limiter.
|
|
189
|
+
*/
|
|
190
|
+
reset(): void;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Create a concurrency limiter.
|
|
194
|
+
*
|
|
195
|
+
* @param name - Name for the limiter (used in errors)
|
|
196
|
+
* @param config - Concurrency limiter configuration
|
|
197
|
+
* @returns A ConcurrencyLimiter instance
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```typescript
|
|
201
|
+
* const limiter = createConcurrencyLimiter('db-pool', {
|
|
202
|
+
* maxConcurrent: 10,
|
|
203
|
+
* });
|
|
204
|
+
*
|
|
205
|
+
* // Execute with concurrency control
|
|
206
|
+
* const results = await limiter.executeAll(
|
|
207
|
+
* ids.map(id => () => fetchItem(id))
|
|
208
|
+
* );
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
declare function createConcurrencyLimiter(name: string, config: ConcurrencyLimiterConfig): ConcurrencyLimiter;
|
|
212
|
+
/**
|
|
213
|
+
* Configuration for combined rate + concurrency limiter.
|
|
214
|
+
*/
|
|
215
|
+
interface CombinedLimiterConfig {
|
|
216
|
+
/**
|
|
217
|
+
* Rate limiting configuration.
|
|
218
|
+
*/
|
|
219
|
+
rate?: RateLimiterConfig;
|
|
220
|
+
/**
|
|
221
|
+
* Concurrency limiting configuration.
|
|
222
|
+
*/
|
|
223
|
+
concurrency?: ConcurrencyLimiterConfig;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Create a combined rate + concurrency limiter.
|
|
227
|
+
*
|
|
228
|
+
* Operations are first rate-limited, then concurrency-limited.
|
|
229
|
+
*
|
|
230
|
+
* @param name - Name for the limiter
|
|
231
|
+
* @param config - Combined limiter configuration
|
|
232
|
+
* @returns An object with both limiters and a combined execute function
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```typescript
|
|
236
|
+
* const limiter = createCombinedLimiter('api', {
|
|
237
|
+
* rate: { maxPerSecond: 10 },
|
|
238
|
+
* concurrency: { maxConcurrent: 5 },
|
|
239
|
+
* });
|
|
240
|
+
*
|
|
241
|
+
* const result = await limiter.execute(() => callApi());
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
declare function createCombinedLimiter(name: string, config: CombinedLimiterConfig): {
|
|
245
|
+
rate?: RateLimiter;
|
|
246
|
+
concurrency?: ConcurrencyLimiter;
|
|
247
|
+
execute: <T>(operation: () => T | Promise<T>) => Promise<T>;
|
|
248
|
+
};
|
|
249
|
+
/**
|
|
250
|
+
* Preset configurations for common use cases.
|
|
251
|
+
*/
|
|
252
|
+
declare const rateLimiterPresets: {
|
|
253
|
+
/**
|
|
254
|
+
* Typical API rate limit (10 req/s).
|
|
255
|
+
*/
|
|
256
|
+
readonly api: {
|
|
257
|
+
maxPerSecond: number;
|
|
258
|
+
burstCapacity: number;
|
|
259
|
+
strategy: "wait";
|
|
260
|
+
};
|
|
261
|
+
/**
|
|
262
|
+
* Database pool limit (concurrent connections).
|
|
263
|
+
*/
|
|
264
|
+
readonly database: {
|
|
265
|
+
maxConcurrent: number;
|
|
266
|
+
strategy: "queue";
|
|
267
|
+
maxQueueSize: number;
|
|
268
|
+
};
|
|
269
|
+
/**
|
|
270
|
+
* Aggressive rate limit for external APIs (5 req/s).
|
|
271
|
+
*/
|
|
272
|
+
readonly external: {
|
|
273
|
+
maxPerSecond: number;
|
|
274
|
+
burstCapacity: number;
|
|
275
|
+
strategy: "wait";
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
export { type CombinedLimiterConfig, type ConcurrencyLimiter, type ConcurrencyLimiterConfig, type ConcurrencyLimiterStats, type QueueFullError, type RateLimitExceededError, type RateLimiter, type RateLimiterConfig, type RateLimiterStats, createCombinedLimiter, createConcurrencyLimiter, createRateLimiter, isQueueFullError, isRateLimitExceededError, rateLimiterPresets };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var Y=e=>({ok:!0,value:e}),M=(e,E)=>({ok:!1,error:e,...E?.cause!==void 0?{cause:E.cause}:{}});var ae=e=>typeof e=="object"&&e!==null&&e.type==="UNEXPECTED_ERROR",V=Symbol.for("step_timeout_marker");function G(e){return typeof e!="object"||e===null?!1:e.type==="STEP_TIMEOUT"?!0:V in e}function ie(e){if(!(typeof e!="object"||e===null)){if(e.type==="STEP_TIMEOUT"){let E=e;return{timeoutMs:E.timeoutMs,stepName:E.stepName,stepKey:E.stepKey,attempt:E.attempt}}if(V in e)return e[V]}}var ee=Symbol("early-exit");function ce(e,E){return{[ee]:!0,error:e,meta:E}}function pe(e){return typeof e=="object"&&e!==null&&e[ee]===!0}var te=Symbol("mapper-exception");function le(e){return{[te]:!0,thrown:e}}function Ee(e){return typeof e=="object"&&e!==null&&e[te]===!0}function ye(e){return typeof e=="string"?{name:e}:e??{}}function J(e,E){let{backoff:y,initialDelay:C,maxDelay:g,jitter:d}=E,u;switch(y){case"fixed":u=C;break;case"linear":u=C*e;break;case"exponential":u=C*Math.pow(2,e-1);break}if(u=Math.min(u,g),d){let n=u*.25*Math.random();u=u+n}return Math.floor(u)}function B(e){return new Promise(E=>setTimeout(E,e))}var H=Symbol("timeout");async function me(e,E,y){let C=new AbortController,g=E.error??{type:"STEP_TIMEOUT",stepName:y.name,stepKey:y.key,timeoutMs:E.ms,attempt:y.attempt},d,u=new Promise((A,c)=>{d=setTimeout(()=>{C.abort(),c({[H]:!0,error:g})},E.ms)}),n;E.signal?n=Promise.resolve(e(C.signal)):n=Promise.resolve(e());try{return await Promise.race([n,u])}catch(A){if(typeof A=="object"&&A!==null&&A[H]===!0){let c=A.error;if(typeof c=="object"&&c!==null&&c.type!=="STEP_TIMEOUT"){let h={timeoutMs:E.ms,stepName:y.name,stepKey:y.key,attempt:y.attempt};V in c?c[V]=h:Object.defineProperty(c,V,{value:h,enumerable:!1,writable:!0,configurable:!1})}throw c}throw A}finally{clearTimeout(d)}}var N={backoff:"exponential",initialDelay:100,maxDelay:3e4,jitter:!0,retryOn:()=>!0,onRetry:()=>{}};async function Z(e,E){let{onError:y,onEvent:C,catchUnexpected:g,workflowId:d,context:u}=E&&typeof E=="object"?E:{},n=d??crypto.randomUUID(),A=!y&&!g,c=[],h=0,U=o=>o??`step_${++h}`,t=o=>{let b=o.context!==void 0||u===void 0?o:{...o,context:u};if(b.type==="step_success"){let I=b.stepId;for(let F=c.length-1;F>=0;F--){let Q=c[F];if(Q.type==="race"&&!Q.winnerId){Q.winnerId=I;break}}}C?.(b,u)},x=ce,D=o=>pe(o),L=(o,b)=>A?b?.origin==="result"?{type:"UNEXPECTED_ERROR",cause:{type:"STEP_FAILURE",origin:"result",error:o,...b.resultCause!==void 0?{cause:b.resultCause}:{}}}:b?.origin==="throw"?{type:"UNEXPECTED_ERROR",cause:{type:"STEP_FAILURE",origin:"throw",error:o,thrown:b.thrown}}:{type:"UNEXPECTED_ERROR",cause:{type:"STEP_FAILURE",origin:"result",error:o}}:o,se=o=>({type:"UNEXPECTED_ERROR",cause:o.meta.origin==="result"?{type:"STEP_FAILURE",origin:"result",error:o.error,...o.meta.resultCause!==void 0?{cause:o.meta.resultCause}:{}}:{type:"STEP_FAILURE",origin:"throw",error:o.error,thrown:o.meta.thrown}});try{let b=function(T,i){let a=`scope_${Date.now()}_${Math.random().toString(36).slice(2,8)}`;return(async()=>{let r=performance.now(),s=!1;c.push({scopeId:a,type:"parallel"});let R=()=>{if(s)return;s=!0;let l=c.findIndex(p=>p.scopeId===a);l!==-1&&c.splice(l,1),t({type:"scope_end",workflowId:n,scopeId:a,ts:Date.now(),durationMs:performance.now()-r})};t({type:"scope_start",workflowId:n,scopeId:a,scopeType:"parallel",name:T,ts:Date.now()});try{let l=await i();if(R(),!l.ok)throw y?.(l.error,T,u),x(l.error,{origin:"result",resultCause:l.cause});return l.value}catch(l){throw R(),l}})()},I=function(T,i){let a=Object.keys(T),r=i.name??`Parallel(${a.join(", ")})`,s=`scope_${Date.now()}_${Math.random().toString(36).slice(2,8)}`;return(async()=>{let R=performance.now(),l=!1;c.push({scopeId:s,type:"parallel"});let p=()=>{if(l)return;l=!0;let w=c.findIndex(f=>f.scopeId===s);w!==-1&&c.splice(w,1),t({type:"scope_end",workflowId:n,scopeId:s,ts:Date.now(),durationMs:performance.now()-R})};t({type:"scope_start",workflowId:n,scopeId:s,scopeType:"parallel",name:r,ts:Date.now()});try{let w=await new Promise(v=>{if(a.length===0){v([]);return}let _=!1,S=a.length,X=new Array(a.length);for(let O=0;O<a.length;O++){let j=a[O],W=O;Promise.resolve(T[j]()).catch(k=>M({type:"PROMISE_REJECTED",cause:k},{cause:{type:"PROMISE_REJECTION",reason:k}})).then(k=>{if(!_){if(!k.ok){_=!0,v([{key:j,result:k}]);return}X[W]={key:j,result:k},S--,S===0&&v(X)}})}});p();let f={};for(let{key:v,result:_}of w){if(!_.ok)throw y?.(_.error,v,u),x(_.error,{origin:"result",resultCause:_.cause});f[v]=_.value}return f}catch(w){throw p(),w}})()};var de=b,fe=I;let o=(T,i)=>(async()=>{let a=ye(i),{name:r,key:s,retry:R,timeout:l}=a,p=U(s),w=C,f=w?performance.now():0;if(!(typeof T=="function")){if(R&&R.attempts>1)throw new Error("step: retry options require a function operation. Direct Promise/Result values cannot be re-executed on retry. Wrap your operation in a function: step(() => yourOperation, { retry: {...} })");if(l)throw new Error("step: timeout options require a function operation. Direct Promise/Result values cannot be wrapped with timeout after they've started. Wrap your operation in a function: step(() => yourOperation, { timeout: {...} })")}let S={attempts:Math.max(1,R?.attempts??1),backoff:R?.backoff??N.backoff,initialDelay:R?.initialDelay??N.initialDelay,maxDelay:R?.maxDelay??N.maxDelay,jitter:R?.jitter??N.jitter,retryOn:R?.retryOn??N.retryOn,onRetry:R?.onRetry??N.onRetry};C&&t({type:"step_start",workflowId:n,stepId:p,stepKey:s,name:r,ts:Date.now()});let X;for(let k=1;k<=S.attempts;k++){let ue=w?performance.now():0;try{let m;if(typeof T=="function"?l?m=await me(T,l,{name:r,key:s,attempt:k}):m=await T():m=await T,m.ok){let K=performance.now()-f;return t({type:"step_success",workflowId:n,stepId:p,stepKey:s,name:r,ts:Date.now(),durationMs:K}),s&&t({type:"step_complete",workflowId:n,stepKey:s,name:r,ts:Date.now(),durationMs:K,result:m}),m.value}if(X=m,k<S.attempts&&S.retryOn(m.error,k)){let K=J(k,S);t({type:"step_retry",workflowId:n,stepId:p,stepKey:s,name:r,ts:Date.now(),attempt:k+1,maxAttempts:S.attempts,delayMs:K,error:m.error}),S.onRetry(m.error,k,K),await B(K);continue}S.attempts>1&&t({type:"step_retries_exhausted",workflowId:n,stepId:p,stepKey:s,name:r,ts:Date.now(),durationMs:performance.now()-f,attempts:k,lastError:m.error});break}catch(m){let K=performance.now()-ue;if(D(m))throw t({type:"step_aborted",workflowId:n,stepId:p,stepKey:s,name:r,ts:Date.now(),durationMs:K}),m;if(G(m)){let P=ie(m),$=l?.ms??P?.timeoutMs??0;if(t({type:"step_timeout",workflowId:n,stepId:p,stepKey:s,name:r,ts:Date.now(),timeoutMs:$,attempt:k}),k<S.attempts&&S.retryOn(m,k)){let z=J(k,S);t({type:"step_retry",workflowId:n,stepId:p,stepKey:s,name:r,ts:Date.now(),attempt:k+1,maxAttempts:S.attempts,delayMs:z,error:m}),S.onRetry(m,k,z),await B(z);continue}S.attempts>1&&t({type:"step_retries_exhausted",workflowId:n,stepId:p,stepKey:s,name:r,ts:Date.now(),durationMs:performance.now()-f,attempts:k,lastError:m})}if(k<S.attempts&&S.retryOn(m,k)){let P=J(k,S);t({type:"step_retry",workflowId:n,stepId:p,stepKey:s,name:r,ts:Date.now(),attempt:k+1,maxAttempts:S.attempts,delayMs:P,error:m}),S.onRetry(m,k,P),await B(P);continue}S.attempts>1&&!G(m)&&t({type:"step_retries_exhausted",workflowId:n,stepId:p,stepKey:s,name:r,ts:Date.now(),durationMs:performance.now()-f,attempts:k,lastError:m});let q=performance.now()-f;if(g){let P;try{P=g(m)}catch($){throw le($)}throw t({type:"step_error",workflowId:n,stepId:p,stepKey:s,name:r,ts:Date.now(),durationMs:q,error:P}),s&&t({type:"step_complete",workflowId:n,stepKey:s,name:r,ts:Date.now(),durationMs:q,result:M(P,{cause:m}),meta:{origin:"throw",thrown:m}}),y?.(P,r,u),x(P,{origin:"throw",thrown:m})}else{let P={type:"UNEXPECTED_ERROR",cause:{type:"UNCAUGHT_EXCEPTION",thrown:m}};throw t({type:"step_error",workflowId:n,stepId:p,stepKey:s,name:r,ts:Date.now(),durationMs:q,error:P}),s&&t({type:"step_complete",workflowId:n,stepKey:s,name:r,ts:Date.now(),durationMs:q,result:M(P,{cause:m}),meta:{origin:"throw",thrown:m}}),m}}}let O=X,j=performance.now()-f,W=L(O.error,{origin:"result",resultCause:O.cause});throw t({type:"step_error",workflowId:n,stepId:p,stepKey:s,name:r,ts:Date.now(),durationMs:j,error:W}),s&&t({type:"step_complete",workflowId:n,stepKey:s,name:r,ts:Date.now(),durationMs:j,result:O,meta:{origin:"result",resultCause:O.cause}}),y?.(O.error,r,u),x(O.error,{origin:"result",resultCause:O.cause})})();o.try=(T,i)=>{let a=i.name,r=i.key,s=U(r),R="error"in i?()=>i.error:i.onError,l=C;return(async()=>{let p=l?performance.now():0;C&&t({type:"step_start",workflowId:n,stepId:s,stepKey:r,name:a,ts:Date.now()});try{let w=await T(),f=performance.now()-p;return t({type:"step_success",workflowId:n,stepId:s,stepKey:r,name:a,ts:Date.now(),durationMs:f}),r&&t({type:"step_complete",workflowId:n,stepKey:r,name:a,ts:Date.now(),durationMs:f,result:Y(w)}),w}catch(w){let f=R(w),v=performance.now()-p,_=L(f,{origin:"throw",thrown:w});throw t({type:"step_error",workflowId:n,stepId:s,stepKey:r,name:a,ts:Date.now(),durationMs:v,error:_}),r&&t({type:"step_complete",workflowId:n,stepKey:r,name:a,ts:Date.now(),durationMs:v,result:M(f,{cause:w}),meta:{origin:"throw",thrown:w}}),y?.(f,a,u),x(f,{origin:"throw",thrown:w})}})()},o.fromResult=(T,i)=>{let a=i.name,r=i.key,s=U(r),R="error"in i?()=>i.error:i.onError,l=C;return(async()=>{let p=l?performance.now():0;C&&t({type:"step_start",workflowId:n,stepId:s,stepKey:r,name:a,ts:Date.now()});let w=await T();if(w.ok){let f=performance.now()-p;return t({type:"step_success",workflowId:n,stepId:s,stepKey:r,name:a,ts:Date.now(),durationMs:f}),r&&t({type:"step_complete",workflowId:n,stepKey:r,name:a,ts:Date.now(),durationMs:f,result:Y(w.value)}),w.value}else{let f=R(w.error),v=performance.now()-p,_=L(f,{origin:"result",resultCause:w.error});throw t({type:"step_error",workflowId:n,stepId:s,stepKey:r,name:a,ts:Date.now(),durationMs:v,error:_}),r&&t({type:"step_complete",workflowId:n,stepKey:r,name:a,ts:Date.now(),durationMs:v,result:M(f,{cause:w.error}),meta:{origin:"result",resultCause:w.error}}),y?.(f,a,u),x(f,{origin:"result",resultCause:w.error})}})()},o.retry=(T,i)=>o(T,{name:i.name,key:i.key,retry:{attempts:i.attempts,backoff:i.backoff,initialDelay:i.initialDelay,maxDelay:i.maxDelay,jitter:i.jitter,retryOn:i.retryOn,onRetry:i.onRetry},timeout:i.timeout}),o.withTimeout=(T,i)=>o(T,{name:i.name,key:i.key,timeout:i}),o.parallel=((...T)=>{if(typeof T[0]=="string"){let i=T[0],a=T[1];return b(i,a)}else{let i=T[0],a=T[1]??{};return I(i,a)}}),o.race=(T,i)=>{let a=`scope_${Date.now()}_${Math.random().toString(36).slice(2,8)}`;return(async()=>{let r=performance.now(),s=!1,R={scopeId:a,type:"race",winnerId:void 0};c.push(R);let l=()=>{if(s)return;s=!0;let p=c.findIndex(w=>w.scopeId===a);p!==-1&&c.splice(p,1),t({type:"scope_end",workflowId:n,scopeId:a,ts:Date.now(),durationMs:performance.now()-r,winnerId:R.winnerId})};t({type:"scope_start",workflowId:n,scopeId:a,scopeType:"race",name:T,ts:Date.now()});try{let p=await i();if(l(),!p.ok)throw y?.(p.error,T,u),x(p.error,{origin:"result",resultCause:p.cause});return p.value}catch(p){throw l(),p}})()},o.allSettled=(T,i)=>{let a=`scope_${Date.now()}_${Math.random().toString(36).slice(2,8)}`;return(async()=>{let r=performance.now(),s=!1;c.push({scopeId:a,type:"allSettled"});let R=()=>{if(s)return;s=!0;let l=c.findIndex(p=>p.scopeId===a);l!==-1&&c.splice(l,1),t({type:"scope_end",workflowId:n,scopeId:a,ts:Date.now(),durationMs:performance.now()-r})};t({type:"scope_start",workflowId:n,scopeId:a,scopeType:"allSettled",name:T,ts:Date.now()});try{let l=await i();if(R(),!l.ok)throw y?.(l.error,T,u),x(l.error,{origin:"result",resultCause:l.cause});return l.value}catch(l){throw R(),l}})()};let Q=await e(o);return Y(Q)}catch(o){if(Ee(o))throw o.thrown;if(D(o)){let I=o.meta.origin==="throw"?o.meta.thrown:o.meta.resultCause;if(g||y)return M(o.error,{cause:I});if(ae(o.error))return M(o.error,{cause:I});let F=se(o);return M(F,{cause:I})}if(g){let I=g(o);return y?.(I,"unexpected",u),M(I,{cause:o})}let b={type:"UNEXPECTED_ERROR",cause:{type:"UNCAUGHT_EXCEPTION",thrown:o}};return y?.(b,"unexpected",u),M(b,{cause:o})}}Z.strict=(e,E)=>Z(e,E);function Te(e){return typeof e=="object"&&e!==null&&e.type==="RATE_LIMIT_EXCEEDED"}function ne(e){return typeof e=="object"&&e!==null&&e.type==="QUEUE_FULL"}function re(e,E){let{maxPerSecond:y,strategy:C="wait"}=E,g=E.burstCapacity??y*2,d=g,u=Date.now(),n=y/1e3,A=[];function c(){let t=Date.now(),D=(t-u)*n;d=Math.min(g,d+D),u=t}function h(){if(c(),d>=1)return d-=1,0;let t=1-d;return Math.ceil(t/n)}async function U(){return new Promise(t=>{let x=()=>{let D=h();D===0?t():(A.push(x),setTimeout(()=>{let L=A.indexOf(x);L!==-1&&(A.splice(L,1),x())},D))};x()})}return{async execute(t){let x=h();if(x>0){if(C==="reject")throw{type:"RATE_LIMIT_EXCEEDED",limiterName:e,retryAfterMs:x};await U()}return t()},async executeResult(t){let x=h();if(x>0){if(C==="reject")return M({type:"RATE_LIMIT_EXCEEDED",limiterName:e,retryAfterMs:x});await U()}return t()},getStats(){return c(),{availableTokens:Math.floor(d),maxTokens:g,tokensPerSecond:y,waitingCount:A.length}},reset(){d=g,u=Date.now(),A.length=0}}}function oe(e,E){let{maxConcurrent:y,strategy:C="queue",maxQueueSize:g=1/0}=E,d=0,u=[];async function n(){if(d<y){d++;return}if(C==="reject")throw{type:"QUEUE_FULL",limiterName:e,queueSize:u.length,maxQueueSize:g};if(u.length>=g)throw{type:"QUEUE_FULL",limiterName:e,queueSize:u.length,maxQueueSize:g};return new Promise((c,h)=>{u.push({resolve:c,reject:h})})}function A(){d--,u.length>0&&d<y&&(d++,u.shift()?.resolve())}return{async execute(c){await n();try{return await c()}finally{A()}},async executeAll(c){let h=new Array(c.length),U=[];for(let t=0;t<c.length;t++){let x=t,D=this.execute(c[x]).then(L=>{h[x]=L});U.push(D)}return await Promise.all(U),h},async executeResult(c){try{await n()}catch(h){if(ne(h))return M(h);throw h}try{return await c()}finally{A()}},getStats(){return{activeCount:d,maxConcurrent:y,queueSize:u.length,maxQueueSize:g}},reset(){for(d=0;u.length>0;)u.shift()?.reject(new Error("Limiter reset"))}}}function we(e,E){let y=E.rate?re(`${e}-rate`,E.rate):void 0,C=E.concurrency?oe(`${e}-concurrency`,E.concurrency):void 0;return{rate:y,concurrency:C,async execute(g){let d=g;if(y){let u=d;d=()=>y.execute(u)}return C?C.execute(d):d()}}}var ke={api:{maxPerSecond:10,burstCapacity:20,strategy:"wait"},database:{maxConcurrent:10,strategy:"queue",maxQueueSize:100},external:{maxPerSecond:5,burstCapacity:10,strategy:"wait"}};export{we as createCombinedLimiter,oe as createConcurrencyLimiter,re as createRateLimiter,ne as isQueueFullError,Te as isRateLimitExceededError,ke as rateLimiterPresets};
|
|
2
|
+
//# sourceMappingURL=ratelimit.js.map
|