@amqp-contract/worker 0.7.0 → 0.8.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/dist/index.d.cts CHANGED
@@ -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
- * Infer consumer handler type for a specific consumer.
54
- * Handlers always receive a single message by default.
55
- * For batch processing, use consumerOptions to configure batch behavior.
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 WorkerInferConsumerHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = (message: WorkerInferConsumerInput<TContract, TName>) => Promise<void>;
93
+ type WorkerInferSafeConsumerHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = (message: WorkerInferConsumerInput<TContract, TName>) => Future<Result<void, HandlerError>>;
58
94
  /**
59
- * Infer consumer handler type for batch processing.
60
- * Batch handlers receive an array of messages.
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 WorkerInferConsumerBatchHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = (messages: Array<WorkerInferConsumerInput<TContract, TName>>) => Promise<void>;
104
+ type WorkerInferSafeConsumerBatchHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = (messages: Array<WorkerInferConsumerInput<TContract, TName>>) => Future<Result<void, HandlerError>>;
63
105
  /**
64
- * Infer handler entry for a consumer - either a function or a tuple of [handler, options].
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: `async (message) => { ... }`
68
- * 2. Handler with prefetch: `[async (message) => { ... }, { prefetch: 10 }]`
69
- * 3. Batch handler: `[async (messages) => { ... }, { batchSize: 5, batchTimeout: 1000 }]`
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 }]`
70
112
  */
71
- type WorkerInferConsumerHandlerEntry<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = WorkerInferConsumerHandler<TContract, TName> | readonly [WorkerInferConsumerHandler<TContract, TName>, {
113
+ type WorkerInferSafeConsumerHandlerEntry<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = WorkerInferSafeConsumerHandler<TContract, TName> | readonly [WorkerInferSafeConsumerHandler<TContract, TName>, {
72
114
  prefetch?: number;
73
115
  batchSize?: never;
74
116
  batchTimeout?: never;
75
- }] | readonly [WorkerInferConsumerBatchHandler<TContract, TName>, {
117
+ }] | readonly [WorkerInferSafeConsumerBatchHandler<TContract, TName>, {
76
118
  prefetch?: number;
77
119
  batchSize: number;
78
120
  batchTimeout?: number;
79
121
  }];
80
122
  /**
81
- * Infer all consumer handlers for a contract.
82
- * Handlers can be either single-message handlers, batch handlers, or a tuple of [handler, options].
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).
83
151
  */
84
- type WorkerInferConsumerHandlers<TContract extends ContractDefinition> = { [K in InferConsumerNames<TContract>]: WorkerInferConsumerHandlerEntry<TContract, K> };
152
+ type WorkerInferUnsafeConsumerHandlerEntry<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = WorkerInferUnsafeConsumerHandler<TContract, TName> | readonly [WorkerInferUnsafeConsumerHandler<TContract, TName>, {
153
+ prefetch?: number;
154
+ batchSize?: never;
155
+ batchTimeout?: never;
156
+ }] | readonly [WorkerInferUnsafeConsumerBatchHandler<TContract, TName>, {
157
+ prefetch?: number;
158
+ batchSize: number;
159
+ batchTimeout?: number;
160
+ }];
161
+ /**
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
173
+ */
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,35 @@ 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
- /** Handlers for each consumer defined in the contract. Can be a function or a tuple of [handler, options] */
128
- handlers: WorkerInferConsumerHandlers<TContract>;
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;
135
265
  };
136
266
  /**
137
267
  * Type-safe AMQP worker for consuming messages from RabbitMQ.
@@ -177,10 +307,15 @@ declare class TypedAmqpWorker<TContract extends ContractDefinition> {
177
307
  private readonly contract;
178
308
  private readonly amqpClient;
179
309
  private readonly logger?;
310
+ /**
311
+ * Internal handler type - always safe handlers (`Future<Result>`).
312
+ * Unsafe handlers are wrapped into safe handlers by defineUnsafeHandler/defineUnsafeHandlers.
313
+ */
180
314
  private readonly actualHandlers;
181
315
  private readonly consumerOptions;
182
316
  private readonly batchTimers;
183
317
  private readonly consumerTags;
318
+ private readonly retryConfig;
184
319
  private constructor();
185
320
  /**
186
321
  * Create a type-safe AMQP worker from a contract.
@@ -198,17 +333,13 @@ declare class TypedAmqpWorker<TContract extends ContractDefinition> {
198
333
  *
199
334
  * @example
200
335
  * ```typescript
201
- * const workerResult = await TypedAmqpWorker.create({
336
+ * const worker = await TypedAmqpWorker.create({
202
337
  * contract: myContract,
203
338
  * handlers: {
204
339
  * processOrder: async (msg) => console.log('Order:', msg.orderId)
205
340
  * },
206
341
  * urls: ['amqp://localhost']
207
342
  * }).resultToPromise();
208
- *
209
- * if (workerResult.isError()) {
210
- * console.error('Failed to create worker:', workerResult.error);
211
- * }
212
343
  * ```
213
344
  */
214
345
  static create<TContract extends ContractDefinition>({
@@ -216,7 +347,8 @@ declare class TypedAmqpWorker<TContract extends ContractDefinition> {
216
347
  handlers,
217
348
  urls,
218
349
  connectionOptions,
219
- logger
350
+ logger,
351
+ retry
220
352
  }: CreateWorkerOptions<TContract>): Future<Result<TypedAmqpWorker<TContract>, TechnicalError>>;
221
353
  /**
222
354
  * Close the AMQP channel and connection.
@@ -235,6 +367,11 @@ declare class TypedAmqpWorker<TContract extends ContractDefinition> {
235
367
  * ```
236
368
  */
237
369
  close(): Future<Result<void, TechnicalError>>;
370
+ /**
371
+ * Set up wait queues for retry mechanism.
372
+ * Creates and binds wait queues for each consumer queue that has DLX configuration.
373
+ */
374
+ private setupWaitQueues;
238
375
  /**
239
376
  * Start consuming messages for all consumers
240
377
  */
@@ -246,149 +383,267 @@ declare class TypedAmqpWorker<TContract extends ContractDefinition> {
246
383
  private consume;
247
384
  /**
248
385
  * Parse and validate a message from AMQP
249
- * @returns Future<Result<validated message, void>> - Ok with validated message, or Error (already handled with nack)
386
+ * @returns `Future<Result<validated message, void>>` - Ok with validated message, or Error (already handled with nack)
250
387
  */
251
388
  private parseAndValidateMessage;
252
389
  /**
253
390
  * Consume messages one at a time
254
391
  */
255
392
  private consumeSingle;
393
+ /**
394
+ * Handle batch processing error by applying error handling to all messages.
395
+ */
396
+ private handleBatchError;
256
397
  /**
257
398
  * Consume messages in batches
258
399
  */
259
400
  private consumeBatch;
401
+ /**
402
+ * Handle error in message processing with retry logic.
403
+ *
404
+ * Flow:
405
+ * 1. If NonRetryableError -> send directly to DLQ (no retry)
406
+ * 2. If no retry config -> legacy behavior (immediate requeue)
407
+ * 3. If max retries exceeded -> send to DLQ
408
+ * 4. Otherwise -> publish to wait queue with TTL for retry
409
+ */
410
+ private handleError;
411
+ /**
412
+ * Calculate retry delay with exponential backoff and optional jitter.
413
+ */
414
+ private calculateRetryDelay;
415
+ /**
416
+ * Parse message content for republishing.
417
+ * Prevents double JSON serialization by converting Buffer to object when possible.
418
+ */
419
+ private parseMessageContentForRetry;
420
+ /**
421
+ * Publish message to wait queue for retry after TTL expires.
422
+ *
423
+ * ┌─────────────────────────────────────────────────────────────────┐
424
+ * │ Retry Flow (Native RabbitMQ TTL + DLX Pattern) │
425
+ * ├─────────────────────────────────────────────────────────────────┤
426
+ * │ │
427
+ * │ 1. Handler throws any Error │
428
+ * │ ↓ │
429
+ * │ 2. Worker publishes to DLX with routing key: {queue}-wait │
430
+ * │ ↓ │
431
+ * │ 3. DLX routes to wait queue: {queue}-wait │
432
+ * │ (with expiration: calculated backoff delay) │
433
+ * │ ↓ │
434
+ * │ 4. Message waits in queue until TTL expires │
435
+ * │ ↓ │
436
+ * │ 5. Expired message dead-lettered to DLX │
437
+ * │ (with routing key: {queue}) │
438
+ * │ ↓ │
439
+ * │ 6. DLX routes back to main queue → RETRY │
440
+ * │ ↓ │
441
+ * │ 7. If retries exhausted: nack without requeue → DLQ │
442
+ * │ │
443
+ * └─────────────────────────────────────────────────────────────────┘
444
+ */
445
+ private publishForRetry;
446
+ /**
447
+ * Send message to dead letter queue.
448
+ * Nacks the message without requeue, relying on DLX configuration.
449
+ */
450
+ private sendToDLQ;
260
451
  }
261
452
  //#endregion
262
453
  //#region src/handlers.d.ts
263
454
  /**
264
455
  * Define a type-safe handler for a specific consumer in a contract.
265
456
  *
266
- * This utility allows you to define handlers outside of the worker creation,
267
- * providing better code organization and reusability.
457
+ * **Recommended:** This function creates handlers that return `Future<Result<void, HandlerError>>`,
458
+ * providing explicit error handling and better control over retry behavior.
268
459
  *
269
460
  * Supports three patterns:
270
461
  * 1. Simple handler: just the function (single message handler)
271
462
  * 2. Handler with prefetch: [handler, { prefetch: 10 }] (single message handler with config)
272
463
  * 3. Batch handler: [batchHandler, { batchSize: 5, batchTimeout: 1000 }] (REQUIRES batchSize config)
273
464
  *
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
465
  * @template TContract - The contract definition type
278
466
  * @template TName - The consumer name from the contract
279
467
  * @param contract - The contract definition containing the consumer
280
468
  * @param consumerName - The name of the consumer from the contract
281
- * @param handler - The async handler function that processes messages (single or batch)
469
+ * @param handler - The handler function that returns `Future<Result<void, HandlerError>>`
282
470
  * @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
471
  * @returns A type-safe handler that can be used with TypedAmqpWorker
286
472
  *
287
473
  * @example
288
474
  * ```typescript
289
- * import { defineHandler } from '@amqp-contract/worker';
475
+ * import { defineHandler, RetryableError, NonRetryableError } from '@amqp-contract/worker';
476
+ * import { Future, Result } from '@swan-io/boxed';
290
477
  * import { orderContract } from './contract';
291
478
  *
292
- * // Simple single-message handler without options
479
+ * // Simple handler with explicit error handling using mapError
293
480
  * const processOrderHandler = defineHandler(
294
481
  * orderContract,
295
482
  * 'processOrder',
296
- * async (message) => {
297
- * console.log('Processing order:', message.orderId);
298
- * await processPayment(message);
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 }
483
+ * (message) =>
484
+ * Future.fromPromise(processPayment(message))
485
+ * .mapOk(() => undefined)
486
+ * .mapError((error) => new RetryableError('Payment failed', error))
310
487
  * );
311
488
  *
312
- * // Batch handler - MUST include batchSize
313
- * const processBatchOrders = defineHandler(
489
+ * // Handler with validation (non-retryable error)
490
+ * const validateOrderHandler = defineHandler(
314
491
  * orderContract,
315
- * 'processOrders',
316
- * async (messages) => {
317
- * // messages is an array - batchSize configuration is REQUIRED
318
- * await db.insertMany(messages);
319
- * },
320
- * { batchSize: 5, batchTimeout: 1000 }
492
+ * 'validateOrder',
493
+ * (message) => {
494
+ * if (message.amount < 1) {
495
+ * // Won't be retried - goes directly to DLQ
496
+ * return Future.value(Result.Error(new NonRetryableError('Invalid order amount')));
497
+ * }
498
+ * return Future.value(Result.Ok(undefined));
499
+ * }
321
500
  * );
322
501
  * ```
323
502
  */
324
- declare function defineHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler: WorkerInferConsumerHandler<TContract, TName>): WorkerInferConsumerHandlerEntry<TContract, TName>;
325
- declare function defineHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler: WorkerInferConsumerHandler<TContract, TName>, options: {
503
+ declare function defineHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler: WorkerInferSafeConsumerHandler<TContract, TName>): WorkerInferSafeConsumerHandlerEntry<TContract, TName>;
504
+ declare function defineHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler: WorkerInferSafeConsumerHandler<TContract, TName>, options: {
326
505
  prefetch?: number;
327
506
  batchSize?: never;
328
507
  batchTimeout?: never;
329
- }): WorkerInferConsumerHandlerEntry<TContract, TName>;
330
- declare function defineHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler: WorkerInferConsumerBatchHandler<TContract, TName>, options: {
508
+ }): WorkerInferSafeConsumerHandlerEntry<TContract, TName>;
509
+ declare function defineHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler: WorkerInferSafeConsumerBatchHandler<TContract, TName>, options: {
331
510
  prefetch?: number;
332
511
  batchSize: number;
333
512
  batchTimeout?: number;
334
- }): WorkerInferConsumerHandlerEntry<TContract, TName>;
513
+ }): WorkerInferSafeConsumerHandlerEntry<TContract, TName>;
335
514
  /**
336
515
  * Define multiple type-safe handlers for consumers in a contract.
337
516
  *
338
- * This utility allows you to define all handlers at once outside of the worker creation,
339
- * ensuring type safety and providing better code organization.
517
+ * **Recommended:** This function creates handlers that return `Future<Result<void, HandlerError>>`,
518
+ * providing explicit error handling and better control over retry behavior.
340
519
  *
341
520
  * @template TContract - The contract definition type
342
521
  * @param contract - The contract definition containing the consumers
343
- * @param handlers - An object with async handler functions for each consumer
522
+ * @param handlers - An object with handler functions for each consumer
344
523
  * @returns A type-safe handlers object that can be used with TypedAmqpWorker
345
524
  *
346
525
  * @example
347
526
  * ```typescript
348
- * import { defineHandlers } from '@amqp-contract/worker';
527
+ * import { defineHandlers, RetryableError } from '@amqp-contract/worker';
528
+ * import { Future } from '@swan-io/boxed';
349
529
  * import { orderContract } from './contract';
350
530
  *
351
- * // Define all handlers at once
352
531
  * const handlers = defineHandlers(orderContract, {
353
- * processOrder: async (message) => {
354
- * // message is fully typed based on the contract
355
- * console.log('Processing order:', message.orderId);
356
- * await processPayment(message);
357
- * },
358
- * notifyOrder: async (message) => {
359
- * await sendNotification(message);
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',
532
+ * processOrder: (message) =>
533
+ * Future.fromPromise(processPayment(message))
534
+ * .mapOk(() => undefined)
535
+ * .mapError((error) => new RetryableError('Payment failed', error)),
536
+ * notifyOrder: (message) =>
537
+ * Future.fromPromise(sendNotification(message))
538
+ * .mapOk(() => undefined)
539
+ * .mapError((error) => new RetryableError('Notification failed', error)),
371
540
  * });
372
541
  * ```
542
+ */
543
+ declare function defineHandlers<TContract extends ContractDefinition>(contract: TContract, handlers: WorkerInferSafeConsumerHandlers<TContract>): WorkerInferSafeConsumerHandlers<TContract>;
544
+ /**
545
+ * Unsafe handler type for single messages (internal use).
546
+ */
547
+ type UnsafeHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = (message: WorkerInferConsumerInput<TContract, TName>) => Promise<void>;
548
+ /**
549
+ * Unsafe handler type for batch messages (internal use).
550
+ */
551
+ type UnsafeBatchHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = (messages: Array<WorkerInferConsumerInput<TContract, TName>>) => Promise<void>;
552
+ /**
553
+ * Define an unsafe handler for a specific consumer in a contract.
554
+ *
555
+ * @deprecated Use `defineHandler` instead for explicit error handling with `Future<Result>`.
556
+ *
557
+ * **Warning:** Unsafe handlers use exception-based error handling:
558
+ * - All thrown errors are treated as retryable by default
559
+ * - Harder to reason about which errors should be retried
560
+ * - May lead to unexpected retry behavior
561
+ *
562
+ * **Note:** Internally, this function wraps the Promise-based handler into a Future-based
563
+ * safe handler for consistent processing in the worker.
564
+ *
565
+ * @template TContract - The contract definition type
566
+ * @template TName - The consumer name from the contract
567
+ * @param contract - The contract definition containing the consumer
568
+ * @param consumerName - The name of the consumer from the contract
569
+ * @param handler - The async handler function that processes messages
570
+ * @param options - Optional consumer options (prefetch, batchSize, batchTimeout)
571
+ * @returns A type-safe handler that can be used with TypedAmqpWorker
373
572
  *
374
573
  * @example
375
574
  * ```typescript
376
- * // Separate handler definitions for better organization
377
- * async function handleProcessOrder(message: WorkerInferConsumerInput<typeof orderContract, 'processOrder'>) {
378
- * await processOrder(message);
379
- * }
575
+ * import { defineUnsafeHandler } from '@amqp-contract/worker';
380
576
  *
381
- * async function handleNotifyOrder(message: WorkerInferConsumerInput<typeof orderContract, 'notifyOrder'>) {
382
- * await sendNotification(message);
383
- * }
577
+ * // ⚠️ Consider using defineHandler for better error handling
578
+ * const processOrderHandler = defineUnsafeHandler(
579
+ * orderContract,
580
+ * 'processOrder',
581
+ * async (message) => {
582
+ * // Throws on error - will be retried
583
+ * await processPayment(message);
584
+ * }
585
+ * );
586
+ * ```
587
+ */
588
+ declare function defineUnsafeHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler: UnsafeHandler<TContract, TName>): WorkerInferSafeConsumerHandlerEntry<TContract, TName>;
589
+ declare function defineUnsafeHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler: UnsafeHandler<TContract, TName>, options: {
590
+ prefetch?: number;
591
+ batchSize?: never;
592
+ batchTimeout?: never;
593
+ }): WorkerInferSafeConsumerHandlerEntry<TContract, TName>;
594
+ declare function defineUnsafeHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler: UnsafeBatchHandler<TContract, TName>, options: {
595
+ prefetch?: number;
596
+ batchSize: number;
597
+ batchTimeout?: number;
598
+ }): WorkerInferSafeConsumerHandlerEntry<TContract, TName>;
599
+ /**
600
+ * Unsafe handler entry type for internal use.
601
+ */
602
+ type UnsafeHandlerEntry<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = UnsafeHandler<TContract, TName> | readonly [UnsafeHandler<TContract, TName>, {
603
+ prefetch?: number;
604
+ batchSize?: never;
605
+ batchTimeout?: never;
606
+ }] | readonly [UnsafeBatchHandler<TContract, TName>, {
607
+ prefetch?: number;
608
+ batchSize: number;
609
+ batchTimeout?: number;
610
+ }];
611
+ /**
612
+ * Unsafe handlers object type for internal use.
613
+ */
614
+ type UnsafeHandlers<TContract extends ContractDefinition> = { [K in InferConsumerNames<TContract>]: UnsafeHandlerEntry<TContract, K> };
615
+ /**
616
+ * Define multiple unsafe handlers for consumers in a contract.
384
617
  *
385
- * const handlers = defineHandlers(orderContract, {
386
- * processOrder: handleProcessOrder,
387
- * notifyOrder: handleNotifyOrder,
618
+ * @deprecated Use `defineHandlers` instead for explicit error handling with `Future<Result>`.
619
+ *
620
+ * **Warning:** Unsafe handlers use exception-based error handling.
621
+ * Consider migrating to safe handlers for better error control.
622
+ *
623
+ * **Note:** Internally, this function wraps all Promise-based handlers into Future-based
624
+ * safe handlers for consistent processing in the worker.
625
+ *
626
+ * @template TContract - The contract definition type
627
+ * @param contract - The contract definition containing the consumers
628
+ * @param handlers - An object with async handler functions for each consumer
629
+ * @returns A type-safe handlers object that can be used with TypedAmqpWorker
630
+ *
631
+ * @example
632
+ * ```typescript
633
+ * import { defineUnsafeHandlers } from '@amqp-contract/worker';
634
+ *
635
+ * // ⚠️ Consider using defineHandlers for better error handling
636
+ * const handlers = defineUnsafeHandlers(orderContract, {
637
+ * processOrder: async (message) => {
638
+ * await processPayment(message);
639
+ * },
640
+ * notifyOrder: async (message) => {
641
+ * await sendNotification(message);
642
+ * },
388
643
  * });
389
644
  * ```
390
645
  */
391
- declare function defineHandlers<TContract extends ContractDefinition>(contract: TContract, handlers: WorkerInferConsumerHandlers<TContract>): WorkerInferConsumerHandlers<TContract>;
646
+ declare function defineUnsafeHandlers<TContract extends ContractDefinition>(contract: TContract, handlers: UnsafeHandlers<TContract>): WorkerInferSafeConsumerHandlers<TContract>;
392
647
  //#endregion
393
- export { type CreateWorkerOptions, MessageValidationError, TechnicalError, TypedAmqpWorker, type WorkerInferConsumerBatchHandler, type WorkerInferConsumerHandler, type WorkerInferConsumerHandlerEntry, type WorkerInferConsumerHandlers, type WorkerInferConsumerInput, defineHandler, defineHandlers };
648
+ 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
649
  //# sourceMappingURL=index.d.cts.map