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.
Files changed (156) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1278 -0
  3. package/dist/batch.cjs +2 -0
  4. package/dist/batch.cjs.map +1 -0
  5. package/dist/batch.d.cts +197 -0
  6. package/dist/batch.d.ts +197 -0
  7. package/dist/batch.js +2 -0
  8. package/dist/batch.js.map +1 -0
  9. package/dist/circuit-breaker.cjs +2 -0
  10. package/dist/circuit-breaker.cjs.map +1 -0
  11. package/dist/circuit-breaker.d.cts +208 -0
  12. package/dist/circuit-breaker.d.ts +208 -0
  13. package/dist/circuit-breaker.js +2 -0
  14. package/dist/circuit-breaker.js.map +1 -0
  15. package/dist/conditional.cjs +2 -0
  16. package/dist/conditional.cjs.map +1 -0
  17. package/dist/conditional.d.cts +249 -0
  18. package/dist/conditional.d.ts +249 -0
  19. package/dist/conditional.js +2 -0
  20. package/dist/conditional.js.map +1 -0
  21. package/dist/core-BuTBsR0x.d.cts +2325 -0
  22. package/dist/core-BuTBsR0x.d.ts +2325 -0
  23. package/dist/core.cjs +2 -0
  24. package/dist/core.cjs.map +1 -0
  25. package/dist/core.d.cts +3 -0
  26. package/dist/core.d.ts +3 -0
  27. package/dist/core.js +2 -0
  28. package/dist/core.js.map +1 -0
  29. package/dist/devtools.cjs +11 -0
  30. package/dist/devtools.cjs.map +1 -0
  31. package/dist/devtools.d.cts +176 -0
  32. package/dist/devtools.d.ts +176 -0
  33. package/dist/devtools.js +11 -0
  34. package/dist/devtools.js.map +1 -0
  35. package/dist/duration.cjs +2 -0
  36. package/dist/duration.cjs.map +1 -0
  37. package/dist/duration.d.cts +246 -0
  38. package/dist/duration.d.ts +246 -0
  39. package/dist/duration.js +2 -0
  40. package/dist/duration.js.map +1 -0
  41. package/dist/hitl.cjs +2 -0
  42. package/dist/hitl.cjs.map +1 -0
  43. package/dist/hitl.d.cts +337 -0
  44. package/dist/hitl.d.ts +337 -0
  45. package/dist/hitl.js +2 -0
  46. package/dist/hitl.js.map +1 -0
  47. package/dist/index.cjs +2 -0
  48. package/dist/index.cjs.map +1 -0
  49. package/dist/index.d.cts +4 -0
  50. package/dist/index.d.ts +4 -0
  51. package/dist/index.js +2 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/match.cjs +2 -0
  54. package/dist/match.cjs.map +1 -0
  55. package/dist/match.d.cts +209 -0
  56. package/dist/match.d.ts +209 -0
  57. package/dist/match.js +2 -0
  58. package/dist/match.js.map +1 -0
  59. package/dist/otel.cjs +2 -0
  60. package/dist/otel.cjs.map +1 -0
  61. package/dist/otel.d.cts +185 -0
  62. package/dist/otel.d.ts +185 -0
  63. package/dist/otel.js +2 -0
  64. package/dist/otel.js.map +1 -0
  65. package/dist/persistence.cjs +2 -0
  66. package/dist/persistence.cjs.map +1 -0
  67. package/dist/persistence.d.cts +572 -0
  68. package/dist/persistence.d.ts +572 -0
  69. package/dist/persistence.js +2 -0
  70. package/dist/persistence.js.map +1 -0
  71. package/dist/policies.cjs +2 -0
  72. package/dist/policies.cjs.map +1 -0
  73. package/dist/policies.d.cts +378 -0
  74. package/dist/policies.d.ts +378 -0
  75. package/dist/policies.js +2 -0
  76. package/dist/policies.js.map +1 -0
  77. package/dist/ratelimit.cjs +2 -0
  78. package/dist/ratelimit.cjs.map +1 -0
  79. package/dist/ratelimit.d.cts +279 -0
  80. package/dist/ratelimit.d.ts +279 -0
  81. package/dist/ratelimit.js +2 -0
  82. package/dist/ratelimit.js.map +1 -0
  83. package/dist/reliability.cjs +2 -0
  84. package/dist/reliability.cjs.map +1 -0
  85. package/dist/reliability.d.cts +5 -0
  86. package/dist/reliability.d.ts +5 -0
  87. package/dist/reliability.js +2 -0
  88. package/dist/reliability.js.map +1 -0
  89. package/dist/resource.cjs +2 -0
  90. package/dist/resource.cjs.map +1 -0
  91. package/dist/resource.d.cts +171 -0
  92. package/dist/resource.d.ts +171 -0
  93. package/dist/resource.js +2 -0
  94. package/dist/resource.js.map +1 -0
  95. package/dist/retry.cjs +2 -0
  96. package/dist/retry.cjs.map +1 -0
  97. package/dist/retry.d.cts +2 -0
  98. package/dist/retry.d.ts +2 -0
  99. package/dist/retry.js +2 -0
  100. package/dist/retry.js.map +1 -0
  101. package/dist/saga.cjs +2 -0
  102. package/dist/saga.cjs.map +1 -0
  103. package/dist/saga.d.cts +231 -0
  104. package/dist/saga.d.ts +231 -0
  105. package/dist/saga.js +2 -0
  106. package/dist/saga.js.map +1 -0
  107. package/dist/schedule.cjs +2 -0
  108. package/dist/schedule.cjs.map +1 -0
  109. package/dist/schedule.d.cts +387 -0
  110. package/dist/schedule.d.ts +387 -0
  111. package/dist/schedule.js +2 -0
  112. package/dist/schedule.js.map +1 -0
  113. package/dist/tagged-error.cjs +2 -0
  114. package/dist/tagged-error.cjs.map +1 -0
  115. package/dist/tagged-error.d.cts +252 -0
  116. package/dist/tagged-error.d.ts +252 -0
  117. package/dist/tagged-error.js +2 -0
  118. package/dist/tagged-error.js.map +1 -0
  119. package/dist/testing.cjs +2 -0
  120. package/dist/testing.cjs.map +1 -0
  121. package/dist/testing.d.cts +228 -0
  122. package/dist/testing.d.ts +228 -0
  123. package/dist/testing.js +2 -0
  124. package/dist/testing.js.map +1 -0
  125. package/dist/visualize.cjs +1573 -0
  126. package/dist/visualize.cjs.map +1 -0
  127. package/dist/visualize.d.cts +1415 -0
  128. package/dist/visualize.d.ts +1415 -0
  129. package/dist/visualize.js +1573 -0
  130. package/dist/visualize.js.map +1 -0
  131. package/dist/webhook.cjs +2 -0
  132. package/dist/webhook.cjs.map +1 -0
  133. package/dist/webhook.d.cts +469 -0
  134. package/dist/webhook.d.ts +469 -0
  135. package/dist/webhook.js +2 -0
  136. package/dist/webhook.js.map +1 -0
  137. package/dist/workflow-entry-C6nH8ByN.d.ts +858 -0
  138. package/dist/workflow-entry-RRTlSg_4.d.cts +858 -0
  139. package/dist/workflow.cjs +2 -0
  140. package/dist/workflow.cjs.map +1 -0
  141. package/dist/workflow.d.cts +2 -0
  142. package/dist/workflow.d.ts +2 -0
  143. package/dist/workflow.js +2 -0
  144. package/dist/workflow.js.map +1 -0
  145. package/docs/advanced.md +1548 -0
  146. package/docs/api.md +513 -0
  147. package/docs/coming-from-neverthrow.md +1013 -0
  148. package/docs/match.md +417 -0
  149. package/docs/pino-logging-example.md +396 -0
  150. package/docs/policies.md +508 -0
  151. package/docs/resource-management.md +509 -0
  152. package/docs/schedule.md +467 -0
  153. package/docs/tagged-error.md +785 -0
  154. package/docs/visualization.md +430 -0
  155. package/docs/visualize-examples.md +330 -0
  156. 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