@amqp-contract/worker 0.7.0 → 0.9.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/README.md +33 -4
- package/dist/index.cjs +397 -95
- package/dist/index.d.cts +362 -99
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +362 -99
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +394 -96
- package/dist/index.mjs.map +1 -1
- package/docs/index.md +995 -213
- package/package.json +8 -8
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Logger } from "@amqp-contract/core";
|
|
1
|
+
import { Logger, TelemetryProvider } from "@amqp-contract/core";
|
|
2
2
|
import { AmqpConnectionManagerOptions, ConnectionUrl } from "amqp-connection-manager";
|
|
3
3
|
import { ConsumerDefinition, ContractDefinition, InferConsumerNames } from "@amqp-contract/contract";
|
|
4
4
|
import { Future, Result } from "@swan-io/boxed";
|
|
@@ -27,6 +27,33 @@ declare class MessageValidationError extends WorkerError {
|
|
|
27
27
|
readonly issues: unknown;
|
|
28
28
|
constructor(consumerName: string, issues: unknown);
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Retryable errors - transient failures that may succeed on retry
|
|
32
|
+
* Examples: network timeouts, rate limiting, temporary service unavailability
|
|
33
|
+
*
|
|
34
|
+
* Use this error type when the operation might succeed if retried.
|
|
35
|
+
* The worker will apply exponential backoff and retry the message.
|
|
36
|
+
*/
|
|
37
|
+
declare class RetryableError extends WorkerError {
|
|
38
|
+
readonly cause?: unknown | undefined;
|
|
39
|
+
constructor(message: string, cause?: unknown | undefined);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Non-retryable errors - permanent failures that should not be retried
|
|
43
|
+
* Examples: invalid data, business rule violations, permanent external failures
|
|
44
|
+
*
|
|
45
|
+
* Use this error type when retrying would not help - the message will be
|
|
46
|
+
* immediately sent to the dead letter queue (DLQ) if configured.
|
|
47
|
+
*/
|
|
48
|
+
declare class NonRetryableError extends WorkerError {
|
|
49
|
+
readonly cause?: unknown | undefined;
|
|
50
|
+
constructor(message: string, cause?: unknown | undefined);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Union type representing all handler errors.
|
|
54
|
+
* Use this type when defining handlers that explicitly signal error outcomes.
|
|
55
|
+
*/
|
|
56
|
+
type HandlerError = RetryableError | NonRetryableError;
|
|
30
57
|
//#endregion
|
|
31
58
|
//#region src/types.d.ts
|
|
32
59
|
/**
|
|
@@ -50,40 +77,129 @@ type InferConsumer<TContract extends ContractDefinition, TName extends InferCons
|
|
|
50
77
|
*/
|
|
51
78
|
type WorkerInferConsumerInput<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = ConsumerInferInput<InferConsumer<TContract, TName>>;
|
|
52
79
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
80
|
+
* Safe consumer handler type for a specific consumer.
|
|
81
|
+
* Returns a `Future<Result<void, HandlerError>>` for explicit error handling.
|
|
82
|
+
*
|
|
83
|
+
* **Recommended over unsafe handlers** for better error control:
|
|
84
|
+
* - RetryableError: Message will be retried with exponential backoff
|
|
85
|
+
* - NonRetryableError: Message will be immediately sent to DLQ
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* const handler: WorkerInferSafeConsumerHandler<typeof contract, 'processOrder'> =
|
|
90
|
+
* (message) => Future.value(Result.Ok(undefined));
|
|
91
|
+
* ```
|
|
56
92
|
*/
|
|
57
|
-
type
|
|
93
|
+
type WorkerInferSafeConsumerHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = (message: WorkerInferConsumerInput<TContract, TName>) => Future<Result<void, HandlerError>>;
|
|
58
94
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
95
|
+
* Safe consumer handler type for batch processing.
|
|
96
|
+
* Returns a `Future<Result<void, HandlerError>>` for explicit error handling.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* const handler: WorkerInferSafeConsumerBatchHandler<typeof contract, 'processOrders'> =
|
|
101
|
+
* (messages) => Future.value(Result.Ok(undefined));
|
|
102
|
+
* ```
|
|
61
103
|
*/
|
|
62
|
-
type
|
|
104
|
+
type WorkerInferSafeConsumerBatchHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = (messages: Array<WorkerInferConsumerInput<TContract, TName>>) => Future<Result<void, HandlerError>>;
|
|
63
105
|
/**
|
|
64
|
-
*
|
|
106
|
+
* Safe handler entry for a consumer - either a function or a tuple of [handler, options].
|
|
65
107
|
*
|
|
66
108
|
* Three patterns are supported:
|
|
67
|
-
* 1. Simple handler: `
|
|
68
|
-
* 2. Handler with prefetch: `[
|
|
69
|
-
* 3. Batch handler: `[
|
|
109
|
+
* 1. Simple handler: `(message) => Future.value(Result.Ok(undefined))`
|
|
110
|
+
* 2. Handler with prefetch: `[(message) => ..., { prefetch: 10 }]`
|
|
111
|
+
* 3. Batch handler: `[(messages) => ..., { batchSize: 5, batchTimeout: 1000 }]`
|
|
112
|
+
*/
|
|
113
|
+
type WorkerInferSafeConsumerHandlerEntry<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = WorkerInferSafeConsumerHandler<TContract, TName> | readonly [WorkerInferSafeConsumerHandler<TContract, TName>, {
|
|
114
|
+
prefetch?: number;
|
|
115
|
+
batchSize?: never;
|
|
116
|
+
batchTimeout?: never;
|
|
117
|
+
}] | readonly [WorkerInferSafeConsumerBatchHandler<TContract, TName>, {
|
|
118
|
+
prefetch?: number;
|
|
119
|
+
batchSize: number;
|
|
120
|
+
batchTimeout?: number;
|
|
121
|
+
}];
|
|
122
|
+
/**
|
|
123
|
+
* Safe consumer handlers for a contract.
|
|
124
|
+
* All handlers return `Future<Result<void, HandlerError>>` for explicit error control.
|
|
125
|
+
*/
|
|
126
|
+
type WorkerInferSafeConsumerHandlers<TContract extends ContractDefinition> = { [K in InferConsumerNames<TContract>]: WorkerInferSafeConsumerHandlerEntry<TContract, K> };
|
|
127
|
+
/**
|
|
128
|
+
* Unsafe consumer handler type for a specific consumer.
|
|
129
|
+
* Returns a `Promise<void>` - throws exceptions on error.
|
|
130
|
+
*
|
|
131
|
+
* @deprecated Prefer using safe handlers (WorkerInferSafeConsumerHandler) that return
|
|
132
|
+
* `Future<Result<void, HandlerError>>` for better error handling.
|
|
133
|
+
*
|
|
134
|
+
* **Note:** When using unsafe handlers:
|
|
135
|
+
* - All thrown errors are treated as retryable by default (when retry is configured)
|
|
136
|
+
* - Use RetryableError or NonRetryableError to control retry behavior explicitly
|
|
137
|
+
*/
|
|
138
|
+
type WorkerInferUnsafeConsumerHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = (message: WorkerInferConsumerInput<TContract, TName>) => Promise<void>;
|
|
139
|
+
/**
|
|
140
|
+
* Unsafe consumer handler type for batch processing.
|
|
141
|
+
* Returns a `Promise<void>` - throws exceptions on error.
|
|
142
|
+
*
|
|
143
|
+
* @deprecated Prefer using safe handlers (WorkerInferSafeConsumerBatchHandler) that return
|
|
144
|
+
* `Future<Result<void, HandlerError>>` for better error handling.
|
|
145
|
+
*/
|
|
146
|
+
type WorkerInferUnsafeConsumerBatchHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = (messages: Array<WorkerInferConsumerInput<TContract, TName>>) => Promise<void>;
|
|
147
|
+
/**
|
|
148
|
+
* Unsafe handler entry for a consumer - either a function or a tuple of [handler, options].
|
|
149
|
+
*
|
|
150
|
+
* @deprecated Prefer using safe handler entries (WorkerInferSafeConsumerHandlerEntry).
|
|
70
151
|
*/
|
|
71
|
-
type
|
|
152
|
+
type WorkerInferUnsafeConsumerHandlerEntry<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = WorkerInferUnsafeConsumerHandler<TContract, TName> | readonly [WorkerInferUnsafeConsumerHandler<TContract, TName>, {
|
|
72
153
|
prefetch?: number;
|
|
73
154
|
batchSize?: never;
|
|
74
155
|
batchTimeout?: never;
|
|
75
|
-
}] | readonly [
|
|
156
|
+
}] | readonly [WorkerInferUnsafeConsumerBatchHandler<TContract, TName>, {
|
|
76
157
|
prefetch?: number;
|
|
77
158
|
batchSize: number;
|
|
78
159
|
batchTimeout?: number;
|
|
79
160
|
}];
|
|
80
161
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
162
|
+
* Unsafe consumer handlers for a contract.
|
|
163
|
+
*
|
|
164
|
+
* @deprecated Prefer using safe handlers (WorkerInferSafeConsumerHandlers).
|
|
165
|
+
*/
|
|
166
|
+
type WorkerInferUnsafeConsumerHandlers<TContract extends ContractDefinition> = { [K in InferConsumerNames<TContract>]: WorkerInferUnsafeConsumerHandlerEntry<TContract, K> };
|
|
167
|
+
/**
|
|
168
|
+
* @deprecated Use WorkerInferUnsafeConsumerHandler instead
|
|
169
|
+
*/
|
|
170
|
+
type WorkerInferConsumerHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = WorkerInferUnsafeConsumerHandler<TContract, TName>;
|
|
171
|
+
/**
|
|
172
|
+
* @deprecated Use WorkerInferUnsafeConsumerBatchHandler instead
|
|
83
173
|
*/
|
|
84
|
-
type
|
|
174
|
+
type WorkerInferConsumerBatchHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = WorkerInferUnsafeConsumerBatchHandler<TContract, TName>;
|
|
175
|
+
/**
|
|
176
|
+
* @deprecated Use WorkerInferUnsafeConsumerHandlerEntry instead
|
|
177
|
+
*/
|
|
178
|
+
type WorkerInferConsumerHandlerEntry<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = WorkerInferUnsafeConsumerHandlerEntry<TContract, TName>;
|
|
179
|
+
/**
|
|
180
|
+
* @deprecated Use WorkerInferUnsafeConsumerHandlers instead
|
|
181
|
+
*/
|
|
182
|
+
type WorkerInferConsumerHandlers<TContract extends ContractDefinition> = WorkerInferUnsafeConsumerHandlers<TContract>;
|
|
85
183
|
//#endregion
|
|
86
184
|
//#region src/worker.d.ts
|
|
185
|
+
/**
|
|
186
|
+
* Retry configuration options for handling failed message processing.
|
|
187
|
+
*
|
|
188
|
+
* When enabled, the worker will automatically retry failed messages using
|
|
189
|
+
* RabbitMQ's native TTL + Dead Letter Exchange (DLX) pattern.
|
|
190
|
+
*/
|
|
191
|
+
type RetryOptions = {
|
|
192
|
+
/** Maximum retry attempts before sending to DLQ (default: 3) */
|
|
193
|
+
maxRetries?: number;
|
|
194
|
+
/** Initial delay in ms before first retry (default: 1000) */
|
|
195
|
+
initialDelayMs?: number;
|
|
196
|
+
/** Maximum delay in ms between retries (default: 30000) */
|
|
197
|
+
maxDelayMs?: number;
|
|
198
|
+
/** Exponential backoff multiplier (default: 2) */
|
|
199
|
+
backoffMultiplier?: number;
|
|
200
|
+
/** Add jitter to prevent thundering herd (default: true) */
|
|
201
|
+
jitter?: boolean;
|
|
202
|
+
};
|
|
87
203
|
/**
|
|
88
204
|
* Options for creating a type-safe AMQP worker.
|
|
89
205
|
*
|
|
@@ -117,21 +233,41 @@ type WorkerInferConsumerHandlers<TContract extends ContractDefinition> = { [K in
|
|
|
117
233
|
* connectionOptions: {
|
|
118
234
|
* heartbeatIntervalInSeconds: 30
|
|
119
235
|
* },
|
|
120
|
-
* logger: myLogger
|
|
236
|
+
* logger: myLogger,
|
|
237
|
+
* retry: {
|
|
238
|
+
* maxRetries: 3,
|
|
239
|
+
* initialDelayMs: 1000,
|
|
240
|
+
* maxDelayMs: 30000,
|
|
241
|
+
* backoffMultiplier: 2,
|
|
242
|
+
* jitter: true
|
|
243
|
+
* }
|
|
121
244
|
* };
|
|
122
245
|
* ```
|
|
123
246
|
*/
|
|
124
247
|
type CreateWorkerOptions<TContract extends ContractDefinition> = {
|
|
125
248
|
/** The AMQP contract definition specifying consumers and their message schemas */
|
|
126
249
|
contract: TContract;
|
|
127
|
-
/**
|
|
128
|
-
|
|
250
|
+
/**
|
|
251
|
+
* Handlers for each consumer defined in the contract.
|
|
252
|
+
* Handlers must return `Future<Result<void, HandlerError>>` for explicit error handling.
|
|
253
|
+
* Use defineHandler() to create safe handlers, or defineUnsafeHandler() which wraps
|
|
254
|
+
* Promise-based handlers into safe handlers internally.
|
|
255
|
+
*/
|
|
256
|
+
handlers: WorkerInferSafeConsumerHandlers<TContract>;
|
|
129
257
|
/** AMQP broker URL(s). Multiple URLs provide failover support */
|
|
130
258
|
urls: ConnectionUrl[];
|
|
131
259
|
/** Optional connection configuration (heartbeat, reconnect settings, etc.) */
|
|
132
260
|
connectionOptions?: AmqpConnectionManagerOptions | undefined;
|
|
133
261
|
/** Optional logger for logging message consumption and errors */
|
|
134
262
|
logger?: Logger | undefined;
|
|
263
|
+
/** Retry configuration - when undefined, uses legacy behavior (immediate requeue) */
|
|
264
|
+
retry?: RetryOptions | undefined;
|
|
265
|
+
/**
|
|
266
|
+
* Optional telemetry provider for tracing and metrics.
|
|
267
|
+
* If not provided, uses the default provider which attempts to load OpenTelemetry.
|
|
268
|
+
* OpenTelemetry instrumentation is automatically enabled if @opentelemetry/api is installed.
|
|
269
|
+
*/
|
|
270
|
+
telemetry?: TelemetryProvider | undefined;
|
|
135
271
|
};
|
|
136
272
|
/**
|
|
137
273
|
* Type-safe AMQP worker for consuming messages from RabbitMQ.
|
|
@@ -177,10 +313,16 @@ declare class TypedAmqpWorker<TContract extends ContractDefinition> {
|
|
|
177
313
|
private readonly contract;
|
|
178
314
|
private readonly amqpClient;
|
|
179
315
|
private readonly logger?;
|
|
316
|
+
/**
|
|
317
|
+
* Internal handler type - always safe handlers (`Future<Result>`).
|
|
318
|
+
* Unsafe handlers are wrapped into safe handlers by defineUnsafeHandler/defineUnsafeHandlers.
|
|
319
|
+
*/
|
|
180
320
|
private readonly actualHandlers;
|
|
181
321
|
private readonly consumerOptions;
|
|
182
322
|
private readonly batchTimers;
|
|
183
323
|
private readonly consumerTags;
|
|
324
|
+
private readonly retryConfig;
|
|
325
|
+
private readonly telemetry;
|
|
184
326
|
private constructor();
|
|
185
327
|
/**
|
|
186
328
|
* Create a type-safe AMQP worker from a contract.
|
|
@@ -198,17 +340,13 @@ declare class TypedAmqpWorker<TContract extends ContractDefinition> {
|
|
|
198
340
|
*
|
|
199
341
|
* @example
|
|
200
342
|
* ```typescript
|
|
201
|
-
* const
|
|
343
|
+
* const worker = await TypedAmqpWorker.create({
|
|
202
344
|
* contract: myContract,
|
|
203
345
|
* handlers: {
|
|
204
346
|
* processOrder: async (msg) => console.log('Order:', msg.orderId)
|
|
205
347
|
* },
|
|
206
348
|
* urls: ['amqp://localhost']
|
|
207
349
|
* }).resultToPromise();
|
|
208
|
-
*
|
|
209
|
-
* if (workerResult.isError()) {
|
|
210
|
-
* console.error('Failed to create worker:', workerResult.error);
|
|
211
|
-
* }
|
|
212
350
|
* ```
|
|
213
351
|
*/
|
|
214
352
|
static create<TContract extends ContractDefinition>({
|
|
@@ -216,7 +354,9 @@ declare class TypedAmqpWorker<TContract extends ContractDefinition> {
|
|
|
216
354
|
handlers,
|
|
217
355
|
urls,
|
|
218
356
|
connectionOptions,
|
|
219
|
-
logger
|
|
357
|
+
logger,
|
|
358
|
+
retry,
|
|
359
|
+
telemetry
|
|
220
360
|
}: CreateWorkerOptions<TContract>): Future<Result<TypedAmqpWorker<TContract>, TechnicalError>>;
|
|
221
361
|
/**
|
|
222
362
|
* Close the AMQP channel and connection.
|
|
@@ -235,6 +375,11 @@ declare class TypedAmqpWorker<TContract extends ContractDefinition> {
|
|
|
235
375
|
* ```
|
|
236
376
|
*/
|
|
237
377
|
close(): Future<Result<void, TechnicalError>>;
|
|
378
|
+
/**
|
|
379
|
+
* Set up wait queues for retry mechanism.
|
|
380
|
+
* Creates and binds wait queues for each consumer queue that has DLX configuration.
|
|
381
|
+
*/
|
|
382
|
+
private setupWaitQueues;
|
|
238
383
|
/**
|
|
239
384
|
* Start consuming messages for all consumers
|
|
240
385
|
*/
|
|
@@ -246,149 +391,267 @@ declare class TypedAmqpWorker<TContract extends ContractDefinition> {
|
|
|
246
391
|
private consume;
|
|
247
392
|
/**
|
|
248
393
|
* Parse and validate a message from AMQP
|
|
249
|
-
* @returns Future<Result<validated message, void
|
|
394
|
+
* @returns `Future<Result<validated message, void>>` - Ok with validated message, or Error (already handled with nack)
|
|
250
395
|
*/
|
|
251
396
|
private parseAndValidateMessage;
|
|
252
397
|
/**
|
|
253
398
|
* Consume messages one at a time
|
|
254
399
|
*/
|
|
255
400
|
private consumeSingle;
|
|
401
|
+
/**
|
|
402
|
+
* Handle batch processing error by applying error handling to all messages.
|
|
403
|
+
*/
|
|
404
|
+
private handleBatchError;
|
|
256
405
|
/**
|
|
257
406
|
* Consume messages in batches
|
|
258
407
|
*/
|
|
259
408
|
private consumeBatch;
|
|
409
|
+
/**
|
|
410
|
+
* Handle error in message processing with retry logic.
|
|
411
|
+
*
|
|
412
|
+
* Flow:
|
|
413
|
+
* 1. If NonRetryableError -> send directly to DLQ (no retry)
|
|
414
|
+
* 2. If no retry config -> legacy behavior (immediate requeue)
|
|
415
|
+
* 3. If max retries exceeded -> send to DLQ
|
|
416
|
+
* 4. Otherwise -> publish to wait queue with TTL for retry
|
|
417
|
+
*/
|
|
418
|
+
private handleError;
|
|
419
|
+
/**
|
|
420
|
+
* Calculate retry delay with exponential backoff and optional jitter.
|
|
421
|
+
*/
|
|
422
|
+
private calculateRetryDelay;
|
|
423
|
+
/**
|
|
424
|
+
* Parse message content for republishing.
|
|
425
|
+
* Prevents double JSON serialization by converting Buffer to object when possible.
|
|
426
|
+
*/
|
|
427
|
+
private parseMessageContentForRetry;
|
|
428
|
+
/**
|
|
429
|
+
* Publish message to wait queue for retry after TTL expires.
|
|
430
|
+
*
|
|
431
|
+
* ┌─────────────────────────────────────────────────────────────────┐
|
|
432
|
+
* │ Retry Flow (Native RabbitMQ TTL + DLX Pattern) │
|
|
433
|
+
* ├─────────────────────────────────────────────────────────────────┤
|
|
434
|
+
* │ │
|
|
435
|
+
* │ 1. Handler throws any Error │
|
|
436
|
+
* │ ↓ │
|
|
437
|
+
* │ 2. Worker publishes to DLX with routing key: {queue}-wait │
|
|
438
|
+
* │ ↓ │
|
|
439
|
+
* │ 3. DLX routes to wait queue: {queue}-wait │
|
|
440
|
+
* │ (with expiration: calculated backoff delay) │
|
|
441
|
+
* │ ↓ │
|
|
442
|
+
* │ 4. Message waits in queue until TTL expires │
|
|
443
|
+
* │ ↓ │
|
|
444
|
+
* │ 5. Expired message dead-lettered to DLX │
|
|
445
|
+
* │ (with routing key: {queue}) │
|
|
446
|
+
* │ ↓ │
|
|
447
|
+
* │ 6. DLX routes back to main queue → RETRY │
|
|
448
|
+
* │ ↓ │
|
|
449
|
+
* │ 7. If retries exhausted: nack without requeue → DLQ │
|
|
450
|
+
* │ │
|
|
451
|
+
* └─────────────────────────────────────────────────────────────────┘
|
|
452
|
+
*/
|
|
453
|
+
private publishForRetry;
|
|
454
|
+
/**
|
|
455
|
+
* Send message to dead letter queue.
|
|
456
|
+
* Nacks the message without requeue, relying on DLX configuration.
|
|
457
|
+
*/
|
|
458
|
+
private sendToDLQ;
|
|
260
459
|
}
|
|
261
460
|
//#endregion
|
|
262
461
|
//#region src/handlers.d.ts
|
|
263
462
|
/**
|
|
264
463
|
* Define a type-safe handler for a specific consumer in a contract.
|
|
265
464
|
*
|
|
266
|
-
* This
|
|
267
|
-
* providing
|
|
465
|
+
* **Recommended:** This function creates handlers that return `Future<Result<void, HandlerError>>`,
|
|
466
|
+
* providing explicit error handling and better control over retry behavior.
|
|
268
467
|
*
|
|
269
468
|
* Supports three patterns:
|
|
270
469
|
* 1. Simple handler: just the function (single message handler)
|
|
271
470
|
* 2. Handler with prefetch: [handler, { prefetch: 10 }] (single message handler with config)
|
|
272
471
|
* 3. Batch handler: [batchHandler, { batchSize: 5, batchTimeout: 1000 }] (REQUIRES batchSize config)
|
|
273
472
|
*
|
|
274
|
-
* **Important**: Batch handlers (handlers that accept an array of messages) MUST include
|
|
275
|
-
* batchSize configuration. You cannot create a batch handler without specifying batchSize.
|
|
276
|
-
*
|
|
277
473
|
* @template TContract - The contract definition type
|
|
278
474
|
* @template TName - The consumer name from the contract
|
|
279
475
|
* @param contract - The contract definition containing the consumer
|
|
280
476
|
* @param consumerName - The name of the consumer from the contract
|
|
281
|
-
* @param handler - The
|
|
477
|
+
* @param handler - The handler function that returns `Future<Result<void, HandlerError>>`
|
|
282
478
|
* @param options - Optional consumer options (prefetch, batchSize, batchTimeout)
|
|
283
|
-
* - For single-message handlers: { prefetch?: number } is optional
|
|
284
|
-
* - For batch handlers: { batchSize: number, batchTimeout?: number } is REQUIRED
|
|
285
479
|
* @returns A type-safe handler that can be used with TypedAmqpWorker
|
|
286
480
|
*
|
|
287
481
|
* @example
|
|
288
482
|
* ```typescript
|
|
289
|
-
* import { defineHandler } from '@amqp-contract/worker';
|
|
483
|
+
* import { defineHandler, RetryableError, NonRetryableError } from '@amqp-contract/worker';
|
|
484
|
+
* import { Future, Result } from '@swan-io/boxed';
|
|
290
485
|
* import { orderContract } from './contract';
|
|
291
486
|
*
|
|
292
|
-
* // Simple
|
|
487
|
+
* // Simple handler with explicit error handling using mapError
|
|
293
488
|
* const processOrderHandler = defineHandler(
|
|
294
489
|
* orderContract,
|
|
295
490
|
* 'processOrder',
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
* );
|
|
301
|
-
*
|
|
302
|
-
* // Single-message handler with prefetch
|
|
303
|
-
* const processOrderWithPrefetch = defineHandler(
|
|
304
|
-
* orderContract,
|
|
305
|
-
* 'processOrder',
|
|
306
|
-
* async (message) => {
|
|
307
|
-
* await processOrder(message);
|
|
308
|
-
* },
|
|
309
|
-
* { prefetch: 10 }
|
|
491
|
+
* (message) =>
|
|
492
|
+
* Future.fromPromise(processPayment(message))
|
|
493
|
+
* .mapOk(() => undefined)
|
|
494
|
+
* .mapError((error) => new RetryableError('Payment failed', error))
|
|
310
495
|
* );
|
|
311
496
|
*
|
|
312
|
-
* //
|
|
313
|
-
* const
|
|
497
|
+
* // Handler with validation (non-retryable error)
|
|
498
|
+
* const validateOrderHandler = defineHandler(
|
|
314
499
|
* orderContract,
|
|
315
|
-
* '
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
500
|
+
* 'validateOrder',
|
|
501
|
+
* (message) => {
|
|
502
|
+
* if (message.amount < 1) {
|
|
503
|
+
* // Won't be retried - goes directly to DLQ
|
|
504
|
+
* return Future.value(Result.Error(new NonRetryableError('Invalid order amount')));
|
|
505
|
+
* }
|
|
506
|
+
* return Future.value(Result.Ok(undefined));
|
|
507
|
+
* }
|
|
321
508
|
* );
|
|
322
509
|
* ```
|
|
323
510
|
*/
|
|
324
|
-
declare function defineHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler:
|
|
325
|
-
declare function defineHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler:
|
|
511
|
+
declare function defineHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler: WorkerInferSafeConsumerHandler<TContract, TName>): WorkerInferSafeConsumerHandlerEntry<TContract, TName>;
|
|
512
|
+
declare function defineHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler: WorkerInferSafeConsumerHandler<TContract, TName>, options: {
|
|
326
513
|
prefetch?: number;
|
|
327
514
|
batchSize?: never;
|
|
328
515
|
batchTimeout?: never;
|
|
329
|
-
}):
|
|
330
|
-
declare function defineHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler:
|
|
516
|
+
}): WorkerInferSafeConsumerHandlerEntry<TContract, TName>;
|
|
517
|
+
declare function defineHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler: WorkerInferSafeConsumerBatchHandler<TContract, TName>, options: {
|
|
331
518
|
prefetch?: number;
|
|
332
519
|
batchSize: number;
|
|
333
520
|
batchTimeout?: number;
|
|
334
|
-
}):
|
|
521
|
+
}): WorkerInferSafeConsumerHandlerEntry<TContract, TName>;
|
|
335
522
|
/**
|
|
336
523
|
* Define multiple type-safe handlers for consumers in a contract.
|
|
337
524
|
*
|
|
338
|
-
* This
|
|
339
|
-
*
|
|
525
|
+
* **Recommended:** This function creates handlers that return `Future<Result<void, HandlerError>>`,
|
|
526
|
+
* providing explicit error handling and better control over retry behavior.
|
|
340
527
|
*
|
|
341
528
|
* @template TContract - The contract definition type
|
|
342
529
|
* @param contract - The contract definition containing the consumers
|
|
343
|
-
* @param handlers - An object with
|
|
530
|
+
* @param handlers - An object with handler functions for each consumer
|
|
344
531
|
* @returns A type-safe handlers object that can be used with TypedAmqpWorker
|
|
345
532
|
*
|
|
346
533
|
* @example
|
|
347
534
|
* ```typescript
|
|
348
|
-
* import { defineHandlers } from '@amqp-contract/worker';
|
|
535
|
+
* import { defineHandlers, RetryableError } from '@amqp-contract/worker';
|
|
536
|
+
* import { Future } from '@swan-io/boxed';
|
|
349
537
|
* import { orderContract } from './contract';
|
|
350
538
|
*
|
|
351
|
-
* // Define all handlers at once
|
|
352
539
|
* const handlers = defineHandlers(orderContract, {
|
|
353
|
-
* processOrder:
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
* shipOrder: async (message) => {
|
|
362
|
-
* await prepareShipment(message);
|
|
363
|
-
* },
|
|
364
|
-
* });
|
|
365
|
-
*
|
|
366
|
-
* // Use the handlers in worker
|
|
367
|
-
* const worker = await TypedAmqpWorker.create({
|
|
368
|
-
* contract: orderContract,
|
|
369
|
-
* handlers,
|
|
370
|
-
* connection: 'amqp://localhost',
|
|
540
|
+
* processOrder: (message) =>
|
|
541
|
+
* Future.fromPromise(processPayment(message))
|
|
542
|
+
* .mapOk(() => undefined)
|
|
543
|
+
* .mapError((error) => new RetryableError('Payment failed', error)),
|
|
544
|
+
* notifyOrder: (message) =>
|
|
545
|
+
* Future.fromPromise(sendNotification(message))
|
|
546
|
+
* .mapOk(() => undefined)
|
|
547
|
+
* .mapError((error) => new RetryableError('Notification failed', error)),
|
|
371
548
|
* });
|
|
372
549
|
* ```
|
|
550
|
+
*/
|
|
551
|
+
declare function defineHandlers<TContract extends ContractDefinition>(contract: TContract, handlers: WorkerInferSafeConsumerHandlers<TContract>): WorkerInferSafeConsumerHandlers<TContract>;
|
|
552
|
+
/**
|
|
553
|
+
* Unsafe handler type for single messages (internal use).
|
|
554
|
+
*/
|
|
555
|
+
type UnsafeHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = (message: WorkerInferConsumerInput<TContract, TName>) => Promise<void>;
|
|
556
|
+
/**
|
|
557
|
+
* Unsafe handler type for batch messages (internal use).
|
|
558
|
+
*/
|
|
559
|
+
type UnsafeBatchHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = (messages: Array<WorkerInferConsumerInput<TContract, TName>>) => Promise<void>;
|
|
560
|
+
/**
|
|
561
|
+
* Define an unsafe handler for a specific consumer in a contract.
|
|
562
|
+
*
|
|
563
|
+
* @deprecated Use `defineHandler` instead for explicit error handling with `Future<Result>`.
|
|
564
|
+
*
|
|
565
|
+
* **Warning:** Unsafe handlers use exception-based error handling:
|
|
566
|
+
* - All thrown errors are treated as retryable by default
|
|
567
|
+
* - Harder to reason about which errors should be retried
|
|
568
|
+
* - May lead to unexpected retry behavior
|
|
569
|
+
*
|
|
570
|
+
* **Note:** Internally, this function wraps the Promise-based handler into a Future-based
|
|
571
|
+
* safe handler for consistent processing in the worker.
|
|
572
|
+
*
|
|
573
|
+
* @template TContract - The contract definition type
|
|
574
|
+
* @template TName - The consumer name from the contract
|
|
575
|
+
* @param contract - The contract definition containing the consumer
|
|
576
|
+
* @param consumerName - The name of the consumer from the contract
|
|
577
|
+
* @param handler - The async handler function that processes messages
|
|
578
|
+
* @param options - Optional consumer options (prefetch, batchSize, batchTimeout)
|
|
579
|
+
* @returns A type-safe handler that can be used with TypedAmqpWorker
|
|
373
580
|
*
|
|
374
581
|
* @example
|
|
375
582
|
* ```typescript
|
|
376
|
-
*
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
*
|
|
583
|
+
* import { defineUnsafeHandler } from '@amqp-contract/worker';
|
|
584
|
+
*
|
|
585
|
+
* // ⚠️ Consider using defineHandler for better error handling
|
|
586
|
+
* const processOrderHandler = defineUnsafeHandler(
|
|
587
|
+
* orderContract,
|
|
588
|
+
* 'processOrder',
|
|
589
|
+
* async (message) => {
|
|
590
|
+
* // Throws on error - will be retried
|
|
591
|
+
* await processPayment(message);
|
|
592
|
+
* }
|
|
593
|
+
* );
|
|
594
|
+
* ```
|
|
595
|
+
*/
|
|
596
|
+
declare function defineUnsafeHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler: UnsafeHandler<TContract, TName>): WorkerInferSafeConsumerHandlerEntry<TContract, TName>;
|
|
597
|
+
declare function defineUnsafeHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler: UnsafeHandler<TContract, TName>, options: {
|
|
598
|
+
prefetch?: number;
|
|
599
|
+
batchSize?: never;
|
|
600
|
+
batchTimeout?: never;
|
|
601
|
+
}): WorkerInferSafeConsumerHandlerEntry<TContract, TName>;
|
|
602
|
+
declare function defineUnsafeHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler: UnsafeBatchHandler<TContract, TName>, options: {
|
|
603
|
+
prefetch?: number;
|
|
604
|
+
batchSize: number;
|
|
605
|
+
batchTimeout?: number;
|
|
606
|
+
}): WorkerInferSafeConsumerHandlerEntry<TContract, TName>;
|
|
607
|
+
/**
|
|
608
|
+
* Unsafe handler entry type for internal use.
|
|
609
|
+
*/
|
|
610
|
+
type UnsafeHandlerEntry<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = UnsafeHandler<TContract, TName> | readonly [UnsafeHandler<TContract, TName>, {
|
|
611
|
+
prefetch?: number;
|
|
612
|
+
batchSize?: never;
|
|
613
|
+
batchTimeout?: never;
|
|
614
|
+
}] | readonly [UnsafeBatchHandler<TContract, TName>, {
|
|
615
|
+
prefetch?: number;
|
|
616
|
+
batchSize: number;
|
|
617
|
+
batchTimeout?: number;
|
|
618
|
+
}];
|
|
619
|
+
/**
|
|
620
|
+
* Unsafe handlers object type for internal use.
|
|
621
|
+
*/
|
|
622
|
+
type UnsafeHandlers<TContract extends ContractDefinition> = { [K in InferConsumerNames<TContract>]: UnsafeHandlerEntry<TContract, K> };
|
|
623
|
+
/**
|
|
624
|
+
* Define multiple unsafe handlers for consumers in a contract.
|
|
380
625
|
*
|
|
381
|
-
*
|
|
382
|
-
* await sendNotification(message);
|
|
383
|
-
* }
|
|
626
|
+
* @deprecated Use `defineHandlers` instead for explicit error handling with `Future<Result>`.
|
|
384
627
|
*
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
*
|
|
628
|
+
* **Warning:** Unsafe handlers use exception-based error handling.
|
|
629
|
+
* Consider migrating to safe handlers for better error control.
|
|
630
|
+
*
|
|
631
|
+
* **Note:** Internally, this function wraps all Promise-based handlers into Future-based
|
|
632
|
+
* safe handlers for consistent processing in the worker.
|
|
633
|
+
*
|
|
634
|
+
* @template TContract - The contract definition type
|
|
635
|
+
* @param contract - The contract definition containing the consumers
|
|
636
|
+
* @param handlers - An object with async handler functions for each consumer
|
|
637
|
+
* @returns A type-safe handlers object that can be used with TypedAmqpWorker
|
|
638
|
+
*
|
|
639
|
+
* @example
|
|
640
|
+
* ```typescript
|
|
641
|
+
* import { defineUnsafeHandlers } from '@amqp-contract/worker';
|
|
642
|
+
*
|
|
643
|
+
* // ⚠️ Consider using defineHandlers for better error handling
|
|
644
|
+
* const handlers = defineUnsafeHandlers(orderContract, {
|
|
645
|
+
* processOrder: async (message) => {
|
|
646
|
+
* await processPayment(message);
|
|
647
|
+
* },
|
|
648
|
+
* notifyOrder: async (message) => {
|
|
649
|
+
* await sendNotification(message);
|
|
650
|
+
* },
|
|
388
651
|
* });
|
|
389
652
|
* ```
|
|
390
653
|
*/
|
|
391
|
-
declare function
|
|
654
|
+
declare function defineUnsafeHandlers<TContract extends ContractDefinition>(contract: TContract, handlers: UnsafeHandlers<TContract>): WorkerInferSafeConsumerHandlers<TContract>;
|
|
392
655
|
//#endregion
|
|
393
|
-
export { type CreateWorkerOptions, MessageValidationError, TechnicalError, TypedAmqpWorker, type WorkerInferConsumerBatchHandler, type WorkerInferConsumerHandler, type WorkerInferConsumerHandlerEntry, type WorkerInferConsumerHandlers, type WorkerInferConsumerInput, defineHandler, defineHandlers };
|
|
656
|
+
export { type CreateWorkerOptions, type HandlerError, MessageValidationError, NonRetryableError, type RetryOptions, RetryableError, TechnicalError, TypedAmqpWorker, type WorkerInferConsumerBatchHandler, type WorkerInferConsumerHandler, type WorkerInferConsumerHandlerEntry, type WorkerInferConsumerHandlers, type WorkerInferConsumerInput, type WorkerInferSafeConsumerBatchHandler, type WorkerInferSafeConsumerHandler, type WorkerInferSafeConsumerHandlerEntry, type WorkerInferSafeConsumerHandlers, type WorkerInferUnsafeConsumerBatchHandler, type WorkerInferUnsafeConsumerHandler, type WorkerInferUnsafeConsumerHandlerEntry, type WorkerInferUnsafeConsumerHandlers, defineHandler, defineHandlers, defineUnsafeHandler, defineUnsafeHandlers };
|
|
394
657
|
//# sourceMappingURL=index.d.cts.map
|