@amqp-contract/contract 0.12.0 → 0.14.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 CHANGED
@@ -18,13 +18,21 @@ pnpm add @amqp-contract/contract
18
18
 
19
19
  ## Quick Start
20
20
 
21
- ### Recommended: Publisher-First / Consumer-First Patterns
21
+ ### Recommended: Event / Command Patterns
22
22
 
23
- For robust contract definitions with guaranteed consistency, use `definePublisherFirst` (for events) or `defineConsumerFirst` (for commands):
23
+ For robust contract definitions with guaranteed consistency, use Event or Command patterns:
24
+
25
+ | Pattern | Use Case | Flow |
26
+ | ----------- | ------------------------------------------ | -------------------------------------------------- |
27
+ | **Event** | One publisher, many consumers (broadcast) | `defineEventPublisher` → `defineEventConsumer` |
28
+ | **Command** | Many publishers, one consumer (task queue) | `defineCommandConsumer` → `defineCommandPublisher` |
24
29
 
25
30
  ```typescript
26
31
  import {
27
- definePublisherFirst,
32
+ defineEventPublisher,
33
+ defineEventConsumer,
34
+ defineCommandConsumer,
35
+ defineCommandPublisher,
28
36
  defineContract,
29
37
  defineExchange,
30
38
  defineQueue,
@@ -32,7 +40,7 @@ import {
32
40
  } from "@amqp-contract/contract";
33
41
  import { z } from "zod";
34
42
 
35
- // Event-oriented pattern: publisher doesn't need to know about queues
43
+ // Event pattern: publisher broadcasts, consumers subscribe
36
44
  const ordersExchange = defineExchange("orders", "topic", { durable: true });
37
45
  const orderMessage = defineMessage(
38
46
  z.object({
@@ -41,28 +49,30 @@ const orderMessage = defineMessage(
41
49
  }),
42
50
  );
43
51
 
44
- const { publisher: orderCreatedPublisher, createConsumer: createOrderCreatedConsumer } =
45
- definePublisherFirst(ordersExchange, orderMessage, { routingKey: "order.created" });
52
+ // Define event publisher
53
+ const orderCreatedEvent = defineEventPublisher(ordersExchange, orderMessage, {
54
+ routingKey: "order.created",
55
+ });
46
56
 
47
57
  // Multiple queues can consume the same event
48
58
  const orderQueue = defineQueue("order-processing", { durable: true });
49
- const { consumer, binding } = createOrderCreatedConsumer(orderQueue);
50
-
51
- // For topic exchanges, consumers can override with their own pattern
52
59
  const analyticsQueue = defineQueue("analytics", { durable: true });
53
- const { consumer: analyticsConsumer, binding: analyticsBinding } = createOrderCreatedConsumer(
54
- analyticsQueue,
55
- "order.*",
56
- ); // Subscribe to all order events
57
60
 
61
+ // Compose contract - configs go directly, bindings auto-generated
58
62
  const contract = defineContract({
59
63
  exchanges: { orders: ordersExchange },
60
64
  queues: { orderQueue, analyticsQueue },
61
- bindings: { orderBinding: binding, analyticsBinding },
62
- publishers: { orderCreated: orderCreatedPublisher },
65
+ publishers: {
66
+ // EventPublisherConfig auto-extracted to publisher
67
+ orderCreated: orderCreatedEvent,
68
+ },
63
69
  consumers: {
64
- processOrder: consumer,
65
- trackOrders: analyticsConsumer,
70
+ // EventConsumerResult → auto-extracted to consumer + binding
71
+ processOrder: defineEventConsumer(orderCreatedEvent, orderQueue),
72
+ // For topic exchanges, consumers can override with their own pattern
73
+ trackOrders: defineEventConsumer(orderCreatedEvent, analyticsQueue, {
74
+ routingKey: "order.*", // Subscribe to all order events
75
+ }),
66
76
  },
67
77
  });
68
78
  ```
@@ -72,7 +82,7 @@ const contract = defineContract({
72
82
  - ✅ Guaranteed message schema consistency between publishers and consumers
73
83
  - ✅ Routing key validation and type safety
74
84
  - ✅ Full type safety with TypeScript inference
75
- - ✅ Event-oriented (publisher-first) and command-oriented (consumer-first) patterns
85
+ - ✅ Event-oriented and command-oriented patterns
76
86
  - ✅ Flexible routing key patterns for topic exchanges
77
87
 
78
88
  ## Documentation
@@ -80,8 +90,8 @@ const contract = defineContract({
80
90
  📖 **[Read the full documentation →](https://btravers.github.io/amqp-contract)**
81
91
 
82
92
  - [Getting Started Guide](https://btravers.github.io/amqp-contract/guide/defining-contracts)
83
- - [Publisher-First Pattern](https://btravers.github.io/amqp-contract/guide/defining-contracts#publisher-first-pattern)
84
- - [Consumer-First Pattern](https://btravers.github.io/amqp-contract/guide/defining-contracts#consumer-first-pattern)
93
+ - [Event Pattern](https://btravers.github.io/amqp-contract/guide/defining-contracts#event-pattern)
94
+ - [Command Pattern](https://btravers.github.io/amqp-contract/guide/defining-contracts#command-pattern)
85
95
  - [Complete API Reference](https://btravers.github.io/amqp-contract/api/contract)
86
96
 
87
97
  ## License
package/dist/index.cjs CHANGED
@@ -165,24 +165,87 @@ function resolveTtlBackoffOptions(options) {
165
165
  }
166
166
  /**
167
167
  * Type guard to check if a queue entry is a QueueWithTtlBackoffInfrastructure.
168
- * @internal
168
+ *
169
+ * When you configure a queue with TTL-backoff retry and a dead letter exchange,
170
+ * `defineQueue` returns a `QueueWithTtlBackoffInfrastructure` instead of a plain
171
+ * `QueueDefinition`. This type guard helps you distinguish between the two.
172
+ *
173
+ * **When to use:**
174
+ * - When you need to check the type of a queue entry at runtime
175
+ * - When writing generic code that handles both plain queues and infrastructure wrappers
176
+ *
177
+ * **Related functions:**
178
+ * - `extractQueue()` - Use this to get the underlying queue definition from either type
179
+ *
180
+ * @param entry - The queue entry to check
181
+ * @returns True if the entry is a QueueWithTtlBackoffInfrastructure, false otherwise
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * const queue = defineQueue('orders', {
186
+ * deadLetter: { exchange: dlx },
187
+ * retry: { mode: 'ttl-backoff' },
188
+ * });
189
+ *
190
+ * if (isQueueWithTtlBackoffInfrastructure(queue)) {
191
+ * // queue has .queue, .waitQueue, .waitQueueBinding, .mainQueueRetryBinding
192
+ * console.log('Wait queue:', queue.waitQueue.name);
193
+ * } else {
194
+ * // queue is a plain QueueDefinition
195
+ * console.log('Queue:', queue.name);
196
+ * }
197
+ * ```
169
198
  */
170
199
  function isQueueWithTtlBackoffInfrastructure(entry) {
171
200
  return typeof entry === "object" && entry !== null && "__brand" in entry && entry.__brand === "QueueWithTtlBackoffInfrastructure";
172
201
  }
173
202
  /**
174
203
  * Extract the plain QueueDefinition from a QueueEntry.
175
- * If the entry is a QueueWithTtlBackoffInfrastructure, returns the inner queue.
176
- * Otherwise, returns the entry as-is.
204
+ *
205
+ * **Why this function exists:**
206
+ * When you configure a queue with TTL-backoff retry and a dead letter exchange,
207
+ * `defineQueue` (or `defineTtlBackoffQueue`) returns a wrapper object that includes
208
+ * the main queue, wait queue, and bindings. This function extracts the underlying
209
+ * queue definition so you can access properties like `name`, `type`, etc.
210
+ *
211
+ * **When to use:**
212
+ * - When you need to access queue properties (name, type, deadLetter, etc.)
213
+ * - When passing a queue to functions that expect a plain QueueDefinition
214
+ * - Works safely on both plain queues and infrastructure wrappers
215
+ *
216
+ * **How it works:**
217
+ * - If the entry is a `QueueWithTtlBackoffInfrastructure`, returns `entry.queue`
218
+ * - Otherwise, returns the entry as-is (it's already a plain QueueDefinition)
177
219
  *
178
220
  * @param entry - The queue entry (either plain QueueDefinition or QueueWithTtlBackoffInfrastructure)
179
221
  * @returns The plain QueueDefinition
180
222
  *
181
223
  * @example
182
224
  * ```typescript
183
- * const queue = defineQueue('orders', { retry: { mode: 'ttl-backoff' }, deadLetter: { exchange: dlx } });
184
- * const plainQueue = extractQueue(queue); // Returns the inner QueueDefinition
225
+ * import { defineQueue, defineTtlBackoffQueue, extractQueue } from '@amqp-contract/contract';
226
+ *
227
+ * // TTL-backoff queue returns a wrapper
228
+ * const orderQueue = defineTtlBackoffQueue('orders', {
229
+ * deadLetterExchange: dlx,
230
+ * maxRetries: 3,
231
+ * });
232
+ *
233
+ * // Use extractQueue to access the queue name
234
+ * const queueName = extractQueue(orderQueue).name; // 'orders'
235
+ *
236
+ * // Also works safely on plain queues
237
+ * const plainQueue = defineQueue('simple', { type: 'quorum', retry: { mode: 'quorum-native' } });
238
+ * const plainName = extractQueue(plainQueue).name; // 'simple'
239
+ *
240
+ * // Access other properties
241
+ * const queueDef = extractQueue(orderQueue);
242
+ * console.log(queueDef.name); // 'orders'
243
+ * console.log(queueDef.type); // 'quorum'
244
+ * console.log(queueDef.deadLetter); // { exchange: dlx, ... }
185
245
  * ```
246
+ *
247
+ * @see isQueueWithTtlBackoffInfrastructure - Type guard to check if extraction is needed
248
+ * @see defineTtlBackoffQueue - Creates queues with TTL-backoff infrastructure
186
249
  */
187
250
  function extractQueue(entry) {
188
251
  if (isQueueWithTtlBackoffInfrastructure(entry)) return entry.queue;
@@ -322,6 +385,126 @@ function defineQueue(name, options) {
322
385
  if (retry.mode === "ttl-backoff" && queueDefinition.deadLetter) return wrapWithTtlBackoffInfrastructure(queueDefinition);
323
386
  return queueDefinition;
324
387
  }
388
+ /**
389
+ * Create a quorum queue with quorum-native retry.
390
+ *
391
+ * This is a simplified helper that enforces best practices:
392
+ * - Uses quorum queues (recommended for most use cases)
393
+ * - Requires dead letter exchange for failed message handling
394
+ * - Uses quorum-native retry mode (simpler than TTL-backoff)
395
+ *
396
+ * **When to use:**
397
+ * - You want simple, immediate retries without exponential backoff
398
+ * - You don't need configurable delays between retries
399
+ * - You want the simplest retry configuration
400
+ *
401
+ * @param name - The queue name
402
+ * @param options - Configuration options
403
+ * @returns A quorum queue definition with quorum-native retry
404
+ *
405
+ * @example
406
+ * ```typescript
407
+ * const dlx = defineExchange('orders-dlx', 'direct', { durable: true });
408
+ *
409
+ * const orderQueue = defineQuorumQueue('order-processing', {
410
+ * deadLetterExchange: dlx,
411
+ * deliveryLimit: 3, // Retry up to 3 times
412
+ * });
413
+ *
414
+ * const contract = defineContract({
415
+ * exchanges: { dlx },
416
+ * queues: { orderProcessing: orderQueue },
417
+ * // ...
418
+ * });
419
+ * ```
420
+ *
421
+ * @see defineQueue - For full queue configuration options
422
+ * @see defineTtlBackoffQueue - For queues with exponential backoff retry
423
+ */
424
+ function defineQuorumQueue(name, options) {
425
+ const { deadLetterExchange, deadLetterRoutingKey, deliveryLimit, autoDelete, arguments: args } = options;
426
+ const queueOptions = {
427
+ type: "quorum",
428
+ deadLetter: deadLetterRoutingKey ? {
429
+ exchange: deadLetterExchange,
430
+ routingKey: deadLetterRoutingKey
431
+ } : { exchange: deadLetterExchange },
432
+ deliveryLimit,
433
+ retry: { mode: "quorum-native" }
434
+ };
435
+ if (autoDelete !== void 0) queueOptions.autoDelete = autoDelete;
436
+ if (args !== void 0) queueOptions.arguments = args;
437
+ return defineQueue(name, queueOptions);
438
+ }
439
+ /**
440
+ * Create a queue with TTL-backoff retry (exponential backoff).
441
+ *
442
+ * This is a simplified helper that enforces best practices:
443
+ * - Uses quorum queues (recommended for most use cases)
444
+ * - Requires dead letter exchange for retry routing
445
+ * - Uses TTL-backoff retry mode with configurable delays
446
+ * - Automatically generates wait queue and bindings
447
+ *
448
+ * **When to use:**
449
+ * - You need exponential backoff between retries
450
+ * - You want configurable delays (initial delay, max delay, jitter)
451
+ * - You're processing messages that may need time before retry
452
+ *
453
+ * **Returns:** A `QueueWithTtlBackoffInfrastructure` object that includes the
454
+ * main queue, wait queue, and bindings. Pass this directly to `defineContract`
455
+ * and it will be expanded automatically.
456
+ *
457
+ * @param name - The queue name
458
+ * @param options - Configuration options
459
+ * @returns A queue with TTL-backoff infrastructure
460
+ *
461
+ * @example
462
+ * ```typescript
463
+ * const dlx = defineExchange('orders-dlx', 'direct', { durable: true });
464
+ *
465
+ * const orderQueue = defineTtlBackoffQueue('order-processing', {
466
+ * deadLetterExchange: dlx,
467
+ * maxRetries: 5,
468
+ * initialDelayMs: 1000, // Start with 1s delay
469
+ * maxDelayMs: 30000, // Cap at 30s
470
+ * });
471
+ *
472
+ * const contract = defineContract({
473
+ * exchanges: { dlx },
474
+ * queues: { orderProcessing: orderQueue }, // Wait queue auto-added
475
+ * // ... bindings auto-generated
476
+ * });
477
+ *
478
+ * // To access the underlying queue definition (e.g., for the queue name):
479
+ * import { extractQueue } from '@amqp-contract/contract';
480
+ * const queueName = extractQueue(orderQueue).name;
481
+ * ```
482
+ *
483
+ * @see defineQueue - For full queue configuration options
484
+ * @see defineQuorumQueue - For queues with quorum-native retry (simpler, immediate retries)
485
+ * @see extractQueue - To access the underlying queue definition
486
+ */
487
+ function defineTtlBackoffQueue(name, options) {
488
+ const { deadLetterExchange, deadLetterRoutingKey, maxRetries, initialDelayMs, maxDelayMs, backoffMultiplier, jitter, autoDelete, arguments: args } = options;
489
+ const deadLetter = deadLetterRoutingKey ? {
490
+ exchange: deadLetterExchange,
491
+ routingKey: deadLetterRoutingKey
492
+ } : { exchange: deadLetterExchange };
493
+ const retryOptions = { mode: "ttl-backoff" };
494
+ if (maxRetries !== void 0) retryOptions.maxRetries = maxRetries;
495
+ if (initialDelayMs !== void 0) retryOptions.initialDelayMs = initialDelayMs;
496
+ if (maxDelayMs !== void 0) retryOptions.maxDelayMs = maxDelayMs;
497
+ if (backoffMultiplier !== void 0) retryOptions.backoffMultiplier = backoffMultiplier;
498
+ if (jitter !== void 0) retryOptions.jitter = jitter;
499
+ const queueOptions = {
500
+ type: "quorum",
501
+ deadLetter,
502
+ retry: retryOptions
503
+ };
504
+ if (autoDelete !== void 0) queueOptions.autoDelete = autoDelete;
505
+ if (args !== void 0) queueOptions.arguments = args;
506
+ return defineQueue(name, queueOptions);
507
+ }
325
508
 
326
509
  //#endregion
327
510
  //#region src/builder/publisher.ts
@@ -360,6 +543,52 @@ function definePublisherInternal(exchange, message, options) {
360
543
  //#endregion
361
544
  //#region src/builder/consumer.ts
362
545
  /**
546
+ * Type guard to check if an entry is an EventConsumerResult.
547
+ */
548
+ function isEventConsumerResultEntry(entry) {
549
+ return "__brand" in entry && entry.__brand === "EventConsumerResult";
550
+ }
551
+ /**
552
+ * Type guard to check if an entry is a CommandConsumerConfig.
553
+ */
554
+ function isCommandConsumerConfigEntry(entry) {
555
+ return "__brand" in entry && entry.__brand === "CommandConsumerConfig";
556
+ }
557
+ /**
558
+ * Extract the ConsumerDefinition from any ConsumerEntry type.
559
+ *
560
+ * Handles the following entry types:
561
+ * - ConsumerDefinition: returned as-is
562
+ * - EventConsumerResult: returns the nested `.consumer` property
563
+ * - CommandConsumerConfig: returns the nested `.consumer` property
564
+ *
565
+ * Use this function when you need to access the underlying ConsumerDefinition
566
+ * from a consumer entry that may have been created with defineEventConsumer
567
+ * or defineCommandConsumer.
568
+ *
569
+ * @param entry - The consumer entry to extract from
570
+ * @returns The underlying ConsumerDefinition
571
+ *
572
+ * @example
573
+ * ```typescript
574
+ * // Works with plain ConsumerDefinition
575
+ * const consumer1 = defineConsumer(queue, message);
576
+ * extractConsumer(consumer1).queue.name; // "my-queue"
577
+ *
578
+ * // Works with EventConsumerResult
579
+ * const consumer2 = defineEventConsumer(eventPublisher, queue);
580
+ * extractConsumer(consumer2).queue.name; // "my-queue"
581
+ *
582
+ * // Works with CommandConsumerConfig
583
+ * const consumer3 = defineCommandConsumer(queue, exchange, message, { routingKey: "cmd" });
584
+ * extractConsumer(consumer3).queue.name; // "my-queue"
585
+ * ```
586
+ */
587
+ function extractConsumer(entry) {
588
+ if (isEventConsumerResultEntry(entry) || isCommandConsumerConfigEntry(entry)) return entry.consumer;
589
+ return entry;
590
+ }
591
+ /**
363
592
  * Define a message consumer.
364
593
  *
365
594
  * A consumer receives and processes messages from a queue. The message schema is validated
@@ -368,6 +597,19 @@ function definePublisherInternal(exchange, message, options) {
368
597
  * Consumers are associated with a specific queue and message type. When you create a worker
369
598
  * with this consumer, it will process messages from the queue according to the schema.
370
599
  *
600
+ * **Which pattern to use:**
601
+ *
602
+ * | Pattern | Best for | Description |
603
+ * |---------|----------|-------------|
604
+ * | `definePublisher` + `defineConsumer` | Independent definition | Define publishers and consumers separately with manual schema consistency |
605
+ * | `defineEventPublisher` + `defineEventConsumer` | Event broadcasting | Define event publisher first, create consumers that subscribe to it |
606
+ * | `defineCommandConsumer` + `defineCommandPublisher` | Task queues | Define command consumer first, create publishers that send commands to it |
607
+ *
608
+ * Use `defineCommandConsumer` when:
609
+ * - One consumer receives from multiple publishers
610
+ * - You want automatic schema consistency between consumer and publishers
611
+ * - You're building task queue or command patterns
612
+ *
371
613
  * @param queue - The queue definition to consume from
372
614
  * @param message - The message definition with payload schema
373
615
  * @param options - Optional consumer configuration
@@ -400,6 +642,9 @@ function definePublisherInternal(exchange, message, options) {
400
642
  * // connection
401
643
  * // });
402
644
  * ```
645
+ *
646
+ * @see defineCommandConsumer - For task queue patterns with automatic schema consistency
647
+ * @see defineEventPublisher - For event-driven patterns with automatic schema consistency
403
648
  */
404
649
  function defineConsumer(queue, message, options) {
405
650
  return {
@@ -409,6 +654,96 @@ function defineConsumer(queue, message, options) {
409
654
  };
410
655
  }
411
656
 
657
+ //#endregion
658
+ //#region src/builder/event.ts
659
+ /**
660
+ * Implementation of defineEventPublisher.
661
+ * @internal
662
+ */
663
+ function defineEventPublisher(exchange, message, options) {
664
+ const config = {
665
+ __brand: "EventPublisherConfig",
666
+ exchange,
667
+ message,
668
+ routingKey: options?.routingKey
669
+ };
670
+ if (options?.arguments !== void 0) config.arguments = options.arguments;
671
+ return config;
672
+ }
673
+ /**
674
+ * Implementation of defineEventConsumer.
675
+ * @internal
676
+ */
677
+ function defineEventConsumer(eventPublisher, queue, options) {
678
+ const { exchange, message, routingKey: publisherRoutingKey } = eventPublisher;
679
+ const bindingRoutingKey = options?.routingKey ?? publisherRoutingKey;
680
+ const bindingOptions = {};
681
+ if (bindingRoutingKey !== void 0) bindingOptions.routingKey = bindingRoutingKey;
682
+ const bindingArguments = options?.arguments ?? eventPublisher.arguments;
683
+ if (bindingArguments !== void 0) bindingOptions.arguments = bindingArguments;
684
+ const binding = defineQueueBindingInternal(queue, exchange, bindingOptions);
685
+ return {
686
+ __brand: "EventConsumerResult",
687
+ consumer: defineConsumer(queue, message),
688
+ binding
689
+ };
690
+ }
691
+ /**
692
+ * Type guard to check if a value is an EventPublisherConfig.
693
+ *
694
+ * @param value - The value to check
695
+ * @returns True if the value is an EventPublisherConfig
696
+ */
697
+ function isEventPublisherConfig(value) {
698
+ return typeof value === "object" && value !== null && "__brand" in value && value.__brand === "EventPublisherConfig";
699
+ }
700
+ /**
701
+ * Type guard to check if a value is an EventConsumerResult.
702
+ *
703
+ * @param value - The value to check
704
+ * @returns True if the value is an EventConsumerResult
705
+ */
706
+ function isEventConsumerResult(value) {
707
+ return typeof value === "object" && value !== null && "__brand" in value && value.__brand === "EventConsumerResult";
708
+ }
709
+
710
+ //#endregion
711
+ //#region src/builder/command.ts
712
+ /**
713
+ * Implementation of defineCommandConsumer.
714
+ * @internal
715
+ */
716
+ function defineCommandConsumer(queue, exchange, message, options) {
717
+ return {
718
+ __brand: "CommandConsumerConfig",
719
+ consumer: defineConsumer(queue, message),
720
+ binding: defineQueueBindingInternal(queue, exchange, options),
721
+ exchange,
722
+ message,
723
+ routingKey: options?.routingKey
724
+ };
725
+ }
726
+ /**
727
+ * Implementation of defineCommandPublisher.
728
+ * @internal
729
+ */
730
+ function defineCommandPublisher(commandConsumer, options) {
731
+ const { exchange, message, routingKey: consumerRoutingKey } = commandConsumer;
732
+ const publisherRoutingKey = options?.routingKey ?? consumerRoutingKey;
733
+ const publisherOptions = {};
734
+ if (publisherRoutingKey !== void 0) publisherOptions.routingKey = publisherRoutingKey;
735
+ return definePublisherInternal(exchange, message, publisherOptions);
736
+ }
737
+ /**
738
+ * Type guard to check if a value is a CommandConsumerConfig.
739
+ *
740
+ * @param value - The value to check
741
+ * @returns True if the value is a CommandConsumerConfig
742
+ */
743
+ function isCommandConsumerConfig(value) {
744
+ return typeof value === "object" && value !== null && "__brand" in value && value.__brand === "CommandConsumerConfig";
745
+ }
746
+
412
747
  //#endregion
413
748
  //#region src/builder/contract.ts
414
749
  /**
@@ -481,100 +816,49 @@ function defineConsumer(queue, message, options) {
481
816
  * ```
482
817
  */
483
818
  function defineContract(definition) {
484
- if (!definition.queues || Object.keys(definition.queues).length === 0) return definition;
485
- const queues = definition.queues;
486
- const expandedQueues = {};
487
- const autoBindings = {};
488
- for (const [name, entry] of Object.entries(queues)) if (isQueueWithTtlBackoffInfrastructure(entry)) {
489
- expandedQueues[name] = entry.queue;
490
- expandedQueues[`${name}Wait`] = entry.waitQueue;
491
- autoBindings[`${name}WaitBinding`] = entry.waitQueueBinding;
492
- autoBindings[`${name}RetryBinding`] = entry.mainQueueRetryBinding;
493
- } else expandedQueues[name] = entry;
494
- if (Object.keys(autoBindings).length > 0) {
495
- const mergedBindings = {
496
- ...definition.bindings,
497
- ...autoBindings
498
- };
499
- return {
500
- ...definition,
501
- queues: expandedQueues,
502
- bindings: mergedBindings
819
+ const { publishers: inputPublishers, consumers: inputConsumers, ...rest } = definition;
820
+ const result = rest;
821
+ if (definition.queues && Object.keys(definition.queues).length > 0) {
822
+ const expandedQueues = {};
823
+ const queueBindings = {};
824
+ for (const [name, entry] of Object.entries(definition.queues)) if (isQueueWithTtlBackoffInfrastructure(entry)) {
825
+ expandedQueues[name] = entry.queue;
826
+ expandedQueues[`${name}Wait`] = entry.waitQueue;
827
+ queueBindings[`${name}WaitBinding`] = entry.waitQueueBinding;
828
+ queueBindings[`${name}RetryBinding`] = entry.mainQueueRetryBinding;
829
+ } else expandedQueues[name] = entry;
830
+ result.queues = expandedQueues;
831
+ if (Object.keys(queueBindings).length > 0) result.bindings = {
832
+ ...result.bindings,
833
+ ...queueBindings
503
834
  };
504
835
  }
505
- return {
506
- ...definition,
507
- queues: expandedQueues
508
- };
509
- }
510
-
511
- //#endregion
512
- //#region src/builder/publisher-first.ts
513
- /**
514
- * Implementation of definePublisherFirst.
515
- * @internal
516
- */
517
- function definePublisherFirst(exchange, message, options) {
518
- const publisher = definePublisherInternal(exchange, message, options);
519
- if (exchange.type === "topic") {
520
- const createConsumer$1 = (queue, routingKey) => {
521
- const binding = defineQueueBindingInternal(queue, exchange, routingKey ? {
522
- ...options,
523
- routingKey
524
- } : options);
525
- return {
526
- consumer: defineConsumer(queue, message),
527
- binding
528
- };
529
- };
530
- return {
531
- publisher,
532
- createConsumer: createConsumer$1
533
- };
836
+ if (inputPublishers && Object.keys(inputPublishers).length > 0) {
837
+ const processedPublishers = {};
838
+ for (const [name, entry] of Object.entries(inputPublishers)) if (isEventPublisherConfig(entry)) {
839
+ const publisherOptions = {};
840
+ if (entry.routingKey !== void 0) publisherOptions.routingKey = entry.routingKey;
841
+ processedPublishers[name] = definePublisherInternal(entry.exchange, entry.message, publisherOptions);
842
+ } else processedPublishers[name] = entry;
843
+ result.publishers = processedPublishers;
534
844
  }
535
- const createConsumer = (queue) => {
536
- const binding = defineQueueBindingInternal(queue, exchange, options);
537
- return {
538
- consumer: defineConsumer(queue, message),
539
- binding
540
- };
541
- };
542
- return {
543
- publisher,
544
- createConsumer
545
- };
546
- }
547
-
548
- //#endregion
549
- //#region src/builder/consumer-first.ts
550
- /**
551
- * Implementation of defineConsumerFirst.
552
- * @internal
553
- */
554
- function defineConsumerFirst(queue, exchange, message, options) {
555
- const consumer = defineConsumer(queue, message);
556
- const binding = defineQueueBindingInternal(queue, exchange, options);
557
- if (exchange.type === "topic") {
558
- const createPublisher$1 = (routingKey) => {
559
- return definePublisherInternal(exchange, message, {
560
- ...options,
561
- routingKey
562
- });
563
- };
564
- return {
565
- consumer,
566
- binding,
567
- createPublisher: createPublisher$1
845
+ if (inputConsumers && Object.keys(inputConsumers).length > 0) {
846
+ const processedConsumers = {};
847
+ const consumerBindings = {};
848
+ for (const [name, entry] of Object.entries(inputConsumers)) if (isEventConsumerResult(entry)) {
849
+ processedConsumers[name] = entry.consumer;
850
+ consumerBindings[`${name}Binding`] = entry.binding;
851
+ } else if (isCommandConsumerConfig(entry)) {
852
+ processedConsumers[name] = entry.consumer;
853
+ consumerBindings[`${name}Binding`] = entry.binding;
854
+ } else processedConsumers[name] = entry;
855
+ result.consumers = processedConsumers;
856
+ if (Object.keys(consumerBindings).length > 0) result.bindings = {
857
+ ...result.bindings,
858
+ ...consumerBindings
568
859
  };
569
860
  }
570
- const createPublisher = () => {
571
- return definePublisherInternal(exchange, message, options);
572
- };
573
- return {
574
- consumer,
575
- binding,
576
- createPublisher
577
- };
861
+ return result;
578
862
  }
579
863
 
580
864
  //#endregion
@@ -650,15 +934,24 @@ function defineTtlBackoffRetryInfrastructure(queueEntry, options) {
650
934
  }
651
935
 
652
936
  //#endregion
937
+ exports.defineCommandConsumer = defineCommandConsumer;
938
+ exports.defineCommandPublisher = defineCommandPublisher;
653
939
  exports.defineConsumer = defineConsumer;
654
- exports.defineConsumerFirst = defineConsumerFirst;
655
940
  exports.defineContract = defineContract;
941
+ exports.defineEventConsumer = defineEventConsumer;
942
+ exports.defineEventPublisher = defineEventPublisher;
656
943
  exports.defineExchange = defineExchange;
657
944
  exports.defineExchangeBinding = defineExchangeBinding;
658
945
  exports.defineMessage = defineMessage;
659
946
  exports.definePublisher = definePublisher;
660
- exports.definePublisherFirst = definePublisherFirst;
661
947
  exports.defineQueue = defineQueue;
662
948
  exports.defineQueueBinding = defineQueueBinding;
949
+ exports.defineQuorumQueue = defineQuorumQueue;
950
+ exports.defineTtlBackoffQueue = defineTtlBackoffQueue;
663
951
  exports.defineTtlBackoffRetryInfrastructure = defineTtlBackoffRetryInfrastructure;
664
- exports.extractQueue = extractQueue;
952
+ exports.extractConsumer = extractConsumer;
953
+ exports.extractQueue = extractQueue;
954
+ exports.isCommandConsumerConfig = isCommandConsumerConfig;
955
+ exports.isEventConsumerResult = isEventConsumerResult;
956
+ exports.isEventPublisherConfig = isEventPublisherConfig;
957
+ exports.isQueueWithTtlBackoffInfrastructure = isQueueWithTtlBackoffInfrastructure;