@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.
@@ -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 };