@upyo/pool 0.3.0-dev.36
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 +20 -0
- package/README.md +389 -0
- package/dist/index.cjs +401 -0
- package/dist/index.d.cts +363 -0
- package/dist/index.d.ts +363 -0
- package/dist/index.js +396 -0
- package/package.json +74 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { Message, Receipt, Transport, TransportOptions } from "@upyo/core";
|
|
2
|
+
|
|
3
|
+
//#region src/strategies/strategy.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Result of transport selection by a strategy.
|
|
7
|
+
* @since 0.3.0
|
|
8
|
+
*/
|
|
9
|
+
interface TransportSelection {
|
|
10
|
+
/**
|
|
11
|
+
* The selected transport entry.
|
|
12
|
+
*/
|
|
13
|
+
readonly entry: ResolvedTransportEntry;
|
|
14
|
+
/**
|
|
15
|
+
* Index of the selected transport in the original list.
|
|
16
|
+
*/
|
|
17
|
+
readonly index: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Base interface for transport selection strategies.
|
|
21
|
+
* @since 0.3.0
|
|
22
|
+
*/
|
|
23
|
+
interface Strategy {
|
|
24
|
+
/**
|
|
25
|
+
* Selects a transport for sending a message.
|
|
26
|
+
*
|
|
27
|
+
* @param message The message to send.
|
|
28
|
+
* @param transports Available transports.
|
|
29
|
+
* @param attemptedIndices Indices of transports that have already been
|
|
30
|
+
* attempted.
|
|
31
|
+
* @returns The selected transport or `undefined` if no suitable transport is
|
|
32
|
+
* available.
|
|
33
|
+
*/
|
|
34
|
+
select(message: Message, transports: readonly ResolvedTransportEntry[], attemptedIndices: Set<number>): TransportSelection | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Resets any internal state of the strategy.
|
|
37
|
+
*/
|
|
38
|
+
reset(): void;
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/config.d.ts
|
|
42
|
+
/**
|
|
43
|
+
* Strategy for selecting transports in a pool.
|
|
44
|
+
* @since 0.3.0
|
|
45
|
+
*/
|
|
46
|
+
type PoolStrategy = "round-robin" | "weighted" | "priority" | "selector-based";
|
|
47
|
+
/**
|
|
48
|
+
* Function that determines if a transport should handle a specific message.
|
|
49
|
+
* @since 0.3.0
|
|
50
|
+
*/
|
|
51
|
+
type TransportSelector = (message: Message) => boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Configuration for a transport entry in the pool.
|
|
54
|
+
* @since 0.3.0
|
|
55
|
+
*/
|
|
56
|
+
interface TransportEntry {
|
|
57
|
+
/**
|
|
58
|
+
* The transport instance to use.
|
|
59
|
+
*/
|
|
60
|
+
readonly transport: Transport;
|
|
61
|
+
/**
|
|
62
|
+
* Weight for weighted distribution strategy.
|
|
63
|
+
* Higher values mean more traffic. Defaults to 1.
|
|
64
|
+
*/
|
|
65
|
+
readonly weight?: number;
|
|
66
|
+
/**
|
|
67
|
+
* Priority for priority-based failover strategy.
|
|
68
|
+
* Higher values are tried first. Defaults to 0.
|
|
69
|
+
*/
|
|
70
|
+
readonly priority?: number;
|
|
71
|
+
/**
|
|
72
|
+
* Selector function for selector-based routing.
|
|
73
|
+
* If provided, this transport will only be used for messages
|
|
74
|
+
* where the selector returns true.
|
|
75
|
+
*/
|
|
76
|
+
readonly selector?: TransportSelector;
|
|
77
|
+
/**
|
|
78
|
+
* Whether this transport is enabled.
|
|
79
|
+
* Disabled transports are skipped. Defaults to true.
|
|
80
|
+
*/
|
|
81
|
+
readonly enabled?: boolean;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Configuration options for the pool transport.
|
|
85
|
+
* @since 0.3.0
|
|
86
|
+
*/
|
|
87
|
+
interface PoolConfig {
|
|
88
|
+
/**
|
|
89
|
+
* The strategy to use for selecting transports.
|
|
90
|
+
* Can be a built-in strategy name or a custom Strategy instance.
|
|
91
|
+
*/
|
|
92
|
+
readonly strategy: PoolStrategy | Strategy;
|
|
93
|
+
/**
|
|
94
|
+
* The transports in the pool.
|
|
95
|
+
*/
|
|
96
|
+
readonly transports: readonly TransportEntry[];
|
|
97
|
+
/**
|
|
98
|
+
* Maximum number of retry attempts when a transport fails.
|
|
99
|
+
* Set to 0 to disable retries. Defaults to the number of transports.
|
|
100
|
+
*/
|
|
101
|
+
readonly maxRetries?: number;
|
|
102
|
+
/**
|
|
103
|
+
* Timeout in milliseconds for each send attempt.
|
|
104
|
+
* If not specified, no timeout is applied.
|
|
105
|
+
*/
|
|
106
|
+
readonly timeout?: number;
|
|
107
|
+
/**
|
|
108
|
+
* Whether to continue trying other transports after a successful send
|
|
109
|
+
* when using selector-based strategy. Defaults to false.
|
|
110
|
+
*/
|
|
111
|
+
readonly continueOnSuccess?: boolean;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Resolved pool configuration with defaults applied.
|
|
115
|
+
* @since 0.3.0
|
|
116
|
+
*/
|
|
117
|
+
interface ResolvedPoolConfig {
|
|
118
|
+
readonly strategy: PoolStrategy | Strategy;
|
|
119
|
+
readonly transports: readonly ResolvedTransportEntry[];
|
|
120
|
+
readonly maxRetries: number;
|
|
121
|
+
readonly timeout?: number;
|
|
122
|
+
readonly continueOnSuccess: boolean;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Resolved transport entry with defaults applied.
|
|
126
|
+
* @since 0.3.0
|
|
127
|
+
*/
|
|
128
|
+
interface ResolvedTransportEntry {
|
|
129
|
+
readonly transport: Transport;
|
|
130
|
+
readonly weight: number;
|
|
131
|
+
readonly priority: number;
|
|
132
|
+
readonly selector?: TransportSelector;
|
|
133
|
+
readonly enabled: boolean;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Creates a resolved pool configuration with defaults applied.
|
|
137
|
+
*
|
|
138
|
+
* @param config The pool configuration.
|
|
139
|
+
* @returns The resolved configuration with defaults.
|
|
140
|
+
* @throws {Error} If the configuration is invalid.
|
|
141
|
+
* @since 0.3.0
|
|
142
|
+
*/
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/pool-transport.d.ts
|
|
145
|
+
/**
|
|
146
|
+
* Pool transport that combines multiple transports with various load balancing
|
|
147
|
+
* and failover strategies.
|
|
148
|
+
*
|
|
149
|
+
* This transport implements the same `Transport` interface, making it a drop-in
|
|
150
|
+
* replacement for any single transport. It distributes messages across multiple
|
|
151
|
+
* underlying transports based on the configured strategy.
|
|
152
|
+
*
|
|
153
|
+
* @example Round-robin load balancing
|
|
154
|
+
* ```typescript
|
|
155
|
+
* import { PoolTransport } from "@upyo/pool";
|
|
156
|
+
*
|
|
157
|
+
* const transport = new PoolTransport({
|
|
158
|
+
* strategy: "round-robin",
|
|
159
|
+
* transports: [
|
|
160
|
+
* { transport: mailgunTransport },
|
|
161
|
+
* { transport: sendgridTransport },
|
|
162
|
+
* { transport: sesTransport },
|
|
163
|
+
* ],
|
|
164
|
+
* });
|
|
165
|
+
* ```
|
|
166
|
+
*
|
|
167
|
+
* @example Priority-based failover
|
|
168
|
+
* ```typescript
|
|
169
|
+
* const transport = new PoolTransport({
|
|
170
|
+
* strategy: "priority",
|
|
171
|
+
* transports: [
|
|
172
|
+
* { transport: primaryTransport, priority: 100 },
|
|
173
|
+
* { transport: backupTransport, priority: 50 },
|
|
174
|
+
* { transport: lastResortTransport, priority: 10 },
|
|
175
|
+
* ],
|
|
176
|
+
* });
|
|
177
|
+
* ```
|
|
178
|
+
*
|
|
179
|
+
* @example Custom routing with selectors
|
|
180
|
+
* ```typescript
|
|
181
|
+
* const transport = new PoolTransport({
|
|
182
|
+
* strategy: "selector-based",
|
|
183
|
+
* transports: [
|
|
184
|
+
* {
|
|
185
|
+
* transport: bulkEmailTransport,
|
|
186
|
+
* selector: (msg) => msg.tags?.includes("newsletter"),
|
|
187
|
+
* },
|
|
188
|
+
* {
|
|
189
|
+
* transport: transactionalTransport,
|
|
190
|
+
* selector: (msg) => msg.priority === "high",
|
|
191
|
+
* },
|
|
192
|
+
* { transport: defaultTransport }, // Catches everything else
|
|
193
|
+
* ],
|
|
194
|
+
* });
|
|
195
|
+
* ```
|
|
196
|
+
*
|
|
197
|
+
* @since 0.3.0
|
|
198
|
+
*/
|
|
199
|
+
declare class PoolTransport implements Transport, AsyncDisposable {
|
|
200
|
+
/**
|
|
201
|
+
* The resolved configuration used by this pool transport.
|
|
202
|
+
*/
|
|
203
|
+
readonly config: ResolvedPoolConfig;
|
|
204
|
+
private readonly strategy;
|
|
205
|
+
/**
|
|
206
|
+
* Creates a new PoolTransport instance.
|
|
207
|
+
*
|
|
208
|
+
* @param config Configuration options for the pool transport.
|
|
209
|
+
* @throws {Error} If the configuration is invalid.
|
|
210
|
+
*/
|
|
211
|
+
constructor(config: PoolConfig);
|
|
212
|
+
/**
|
|
213
|
+
* Sends a single email message using the pool strategy.
|
|
214
|
+
*
|
|
215
|
+
* The transport is selected based on the configured strategy. If the
|
|
216
|
+
* selected transport fails, the pool will retry with other transports
|
|
217
|
+
* up to the configured retry limit.
|
|
218
|
+
*
|
|
219
|
+
* @param message The email message to send.
|
|
220
|
+
* @param options Optional transport options including abort signal.
|
|
221
|
+
* @returns A promise that resolves to a receipt indicating success or failure.
|
|
222
|
+
*/
|
|
223
|
+
send(message: Message, options?: TransportOptions): Promise<Receipt>;
|
|
224
|
+
/**
|
|
225
|
+
* Sends multiple email messages using the pool strategy.
|
|
226
|
+
*
|
|
227
|
+
* Each message is sent individually using the `send` method, respecting
|
|
228
|
+
* the configured strategy and retry logic.
|
|
229
|
+
*
|
|
230
|
+
* @param messages An iterable or async iterable of messages to send.
|
|
231
|
+
* @param options Optional transport options including abort signal.
|
|
232
|
+
* @returns An async iterable of receipts, one for each message.
|
|
233
|
+
*/
|
|
234
|
+
sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt>;
|
|
235
|
+
/**
|
|
236
|
+
* Disposes of all underlying transports that support disposal.
|
|
237
|
+
*
|
|
238
|
+
* This method is called automatically when using the `await using` syntax.
|
|
239
|
+
* It ensures proper cleanup of resources held by the underlying transports.
|
|
240
|
+
*/
|
|
241
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
242
|
+
/**
|
|
243
|
+
* Creates a strategy instance based on the strategy type or returns the provided strategy.
|
|
244
|
+
*/
|
|
245
|
+
private createStrategy;
|
|
246
|
+
/**
|
|
247
|
+
* Creates send options with timeout if configured.
|
|
248
|
+
*/
|
|
249
|
+
private createSendOptions;
|
|
250
|
+
}
|
|
251
|
+
//#endregion
|
|
252
|
+
//#region src/strategies/round-robin-strategy.d.ts
|
|
253
|
+
/**
|
|
254
|
+
* Round-robin strategy that cycles through transports in order.
|
|
255
|
+
*
|
|
256
|
+
* This strategy maintains an internal counter and selects transports
|
|
257
|
+
* in a circular fashion, ensuring even distribution of messages across
|
|
258
|
+
* all enabled transports.
|
|
259
|
+
* @since 0.3.0
|
|
260
|
+
*/
|
|
261
|
+
declare class RoundRobinStrategy implements Strategy {
|
|
262
|
+
private currentIndex;
|
|
263
|
+
/**
|
|
264
|
+
* Selects the next transport in round-robin order.
|
|
265
|
+
*
|
|
266
|
+
* @param _message The message to send (unused in this strategy).
|
|
267
|
+
* @param transports Available transports.
|
|
268
|
+
* @param attemptedIndices Indices of transports that have already been
|
|
269
|
+
* attempted.
|
|
270
|
+
* @returns The selected transport or `undefined` if all transports have been
|
|
271
|
+
* attempted.
|
|
272
|
+
*/
|
|
273
|
+
select(_message: Message, transports: readonly ResolvedTransportEntry[], attemptedIndices: Set<number>): TransportSelection | undefined;
|
|
274
|
+
/**
|
|
275
|
+
* Resets the round-robin counter to start from the beginning.
|
|
276
|
+
*/
|
|
277
|
+
reset(): void;
|
|
278
|
+
}
|
|
279
|
+
//#endregion
|
|
280
|
+
//#region src/strategies/weighted-strategy.d.ts
|
|
281
|
+
/**
|
|
282
|
+
* Weighted strategy that distributes traffic based on configured weights.
|
|
283
|
+
*
|
|
284
|
+
* This strategy uses weighted random selection to distribute messages
|
|
285
|
+
* across transports proportionally to their configured weights.
|
|
286
|
+
* A transport with weight 2 will receive approximately twice as many
|
|
287
|
+
* messages as a transport with weight 1.
|
|
288
|
+
* @since 0.3.0
|
|
289
|
+
*/
|
|
290
|
+
declare class WeightedStrategy implements Strategy {
|
|
291
|
+
/**
|
|
292
|
+
* Selects a transport based on weighted random distribution.
|
|
293
|
+
*
|
|
294
|
+
* @param _message The message to send (unused in this strategy).
|
|
295
|
+
* @param transports Available transports.
|
|
296
|
+
* @param attemptedIndices Indices of transports that have already been
|
|
297
|
+
* attempted.
|
|
298
|
+
* @returns The selected transport or `undefined` if all transports have been
|
|
299
|
+
* attempted.
|
|
300
|
+
*/
|
|
301
|
+
select(_message: Message, transports: readonly ResolvedTransportEntry[], attemptedIndices: Set<number>): TransportSelection | undefined;
|
|
302
|
+
/**
|
|
303
|
+
* Resets the strategy (no-op for weighted strategy as it's stateless).
|
|
304
|
+
*/
|
|
305
|
+
reset(): void;
|
|
306
|
+
}
|
|
307
|
+
//#endregion
|
|
308
|
+
//#region src/strategies/priority-strategy.d.ts
|
|
309
|
+
/**
|
|
310
|
+
* Priority strategy that selects transports based on priority values.
|
|
311
|
+
*
|
|
312
|
+
* This strategy always attempts to use the highest priority transport
|
|
313
|
+
* first, falling back to lower priority transports only when higher
|
|
314
|
+
* priority ones fail. Transports with the same priority are considered
|
|
315
|
+
* equivalent and one is selected randomly.
|
|
316
|
+
* @since 0.3.0
|
|
317
|
+
*/
|
|
318
|
+
declare class PriorityStrategy implements Strategy {
|
|
319
|
+
/**
|
|
320
|
+
* Selects the highest priority transport that hasn't been attempted.
|
|
321
|
+
*
|
|
322
|
+
* @param _message The message to send (unused in this strategy).
|
|
323
|
+
* @param transports Available transports.
|
|
324
|
+
* @param attemptedIndices Indices of transports that have already been
|
|
325
|
+
* attempted.
|
|
326
|
+
* @returns The selected transport or `undefined` if all transports have been
|
|
327
|
+
* attempted.
|
|
328
|
+
*/
|
|
329
|
+
select(_message: Message, transports: readonly ResolvedTransportEntry[], attemptedIndices: Set<number>): TransportSelection | undefined;
|
|
330
|
+
/**
|
|
331
|
+
* Resets the strategy (no-op for priority strategy as it's stateless).
|
|
332
|
+
*/
|
|
333
|
+
reset(): void;
|
|
334
|
+
}
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region src/strategies/selector-strategy.d.ts
|
|
337
|
+
/**
|
|
338
|
+
* Selector strategy that routes messages based on custom selector functions.
|
|
339
|
+
*
|
|
340
|
+
* This strategy evaluates each transport's selector function (if provided)
|
|
341
|
+
* to determine if it should handle a specific message. Transports without
|
|
342
|
+
* selectors are considered as catch-all fallbacks. Among matching transports,
|
|
343
|
+
* one is selected randomly.
|
|
344
|
+
* @since 0.3.0
|
|
345
|
+
*/
|
|
346
|
+
declare class SelectorStrategy implements Strategy {
|
|
347
|
+
/**
|
|
348
|
+
* Selects a transport based on selector function matching.
|
|
349
|
+
*
|
|
350
|
+
* @param message The message to send.
|
|
351
|
+
* @param transports Available transports.
|
|
352
|
+
* @param attemptedIndices Indices of transports that have already been
|
|
353
|
+
* attempted.
|
|
354
|
+
* @returns The selected transport or `undefined` if no transport matches.
|
|
355
|
+
*/
|
|
356
|
+
select(message: Message, transports: readonly ResolvedTransportEntry[], attemptedIndices: Set<number>): TransportSelection | undefined;
|
|
357
|
+
/**
|
|
358
|
+
* Resets the strategy (no-op for selector strategy as it's stateless).
|
|
359
|
+
*/
|
|
360
|
+
reset(): void;
|
|
361
|
+
}
|
|
362
|
+
//#endregion
|
|
363
|
+
export { PoolConfig, PoolStrategy, PoolTransport, PriorityStrategy, ResolvedPoolConfig, ResolvedTransportEntry, RoundRobinStrategy, SelectorStrategy, Strategy, TransportEntry, TransportSelection, TransportSelector, WeightedStrategy };
|