@amqp-contract/contract 0.14.0 → 0.16.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 +1 -3
- package/dist/index.cjs +122 -141
- package/dist/index.d.cts +259 -160
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +259 -160
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +121 -141
- package/dist/index.mjs.map +1 -1
- package/docs/index.md +476 -268
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -58,10 +58,8 @@ const orderCreatedEvent = defineEventPublisher(ordersExchange, orderMessage, {
|
|
|
58
58
|
const orderQueue = defineQueue("order-processing", { durable: true });
|
|
59
59
|
const analyticsQueue = defineQueue("analytics", { durable: true });
|
|
60
60
|
|
|
61
|
-
// Compose contract -
|
|
61
|
+
// Compose contract - exchanges, queues, bindings auto-extracted
|
|
62
62
|
const contract = defineContract({
|
|
63
|
-
exchanges: { orders: ordersExchange },
|
|
64
|
-
queues: { orderQueue, analyticsQueue },
|
|
65
63
|
publishers: {
|
|
66
64
|
// EventPublisherConfig → auto-extracted to publisher
|
|
67
65
|
orderCreated: orderCreatedEvent,
|
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
1
2
|
|
|
2
3
|
//#region src/builder/exchange.ts
|
|
3
4
|
/**
|
|
@@ -269,76 +270,17 @@ function wrapWithTtlBackoffInfrastructure(queue) {
|
|
|
269
270
|
},
|
|
270
271
|
retry: resolveTtlBackoffOptions(void 0)
|
|
271
272
|
};
|
|
273
|
+
const waitQueueBinding = defineQueueBindingInternal(waitQueue, dlx, { routingKey: waitQueueName });
|
|
274
|
+
const mainQueueRetryBinding = defineQueueBindingInternal(queue, dlx, { routingKey: queue.name });
|
|
272
275
|
return {
|
|
273
276
|
__brand: "QueueWithTtlBackoffInfrastructure",
|
|
274
277
|
queue,
|
|
278
|
+
deadLetter: queue.deadLetter,
|
|
275
279
|
waitQueue,
|
|
276
|
-
waitQueueBinding
|
|
277
|
-
mainQueueRetryBinding
|
|
280
|
+
waitQueueBinding,
|
|
281
|
+
mainQueueRetryBinding
|
|
278
282
|
};
|
|
279
283
|
}
|
|
280
|
-
/**
|
|
281
|
-
* Define an AMQP queue.
|
|
282
|
-
*
|
|
283
|
-
* A queue stores messages until they are consumed by workers. Queues can be bound to exchanges
|
|
284
|
-
* to receive messages based on routing rules.
|
|
285
|
-
*
|
|
286
|
-
* By default, queues are created as quorum queues which provide better durability and
|
|
287
|
-
* high-availability. Use `type: 'classic'` for special cases like non-durable queues
|
|
288
|
-
* or priority queues.
|
|
289
|
-
*
|
|
290
|
-
* @param name - The name of the queue
|
|
291
|
-
* @param options - Optional queue configuration
|
|
292
|
-
* @param options.type - Queue type: 'quorum' (default, recommended) or 'classic'
|
|
293
|
-
* @param options.durable - If true, the queue survives broker restarts. Quorum queues are always durable.
|
|
294
|
-
* @param options.exclusive - If true, the queue can only be used by the declaring connection. Only supported with classic queues.
|
|
295
|
-
* @param options.autoDelete - If true, the queue is deleted when the last consumer unsubscribes (default: false)
|
|
296
|
-
* @param options.deadLetter - Dead letter configuration for handling failed messages
|
|
297
|
-
* @param options.maxPriority - Maximum priority level for priority queue (1-255, recommended: 1-10). Only supported with classic queues.
|
|
298
|
-
* @param options.arguments - Additional AMQP arguments (e.g., x-message-ttl)
|
|
299
|
-
* @returns A queue definition
|
|
300
|
-
*
|
|
301
|
-
* @example
|
|
302
|
-
* ```typescript
|
|
303
|
-
* // Quorum queue (default, recommended for production)
|
|
304
|
-
* const orderQueue = defineQueue('order-processing');
|
|
305
|
-
*
|
|
306
|
-
* // Explicit quorum queue with dead letter exchange
|
|
307
|
-
* const dlx = defineExchange('orders-dlx', 'topic', { durable: true });
|
|
308
|
-
* const orderQueueWithDLX = defineQueue('order-processing', {
|
|
309
|
-
* type: 'quorum',
|
|
310
|
-
* deadLetter: {
|
|
311
|
-
* exchange: dlx,
|
|
312
|
-
* routingKey: 'order.failed'
|
|
313
|
-
* },
|
|
314
|
-
* arguments: {
|
|
315
|
-
* 'x-message-ttl': 86400000, // 24 hours
|
|
316
|
-
* }
|
|
317
|
-
* });
|
|
318
|
-
*
|
|
319
|
-
* // Classic queue (for special cases)
|
|
320
|
-
* const tempQueue = defineQueue('temp-queue', {
|
|
321
|
-
* type: 'classic',
|
|
322
|
-
* durable: false,
|
|
323
|
-
* autoDelete: true,
|
|
324
|
-
* });
|
|
325
|
-
*
|
|
326
|
-
* // Priority queue (requires classic type)
|
|
327
|
-
* const taskQueue = defineQueue('urgent-tasks', {
|
|
328
|
-
* type: 'classic',
|
|
329
|
-
* durable: true,
|
|
330
|
-
* maxPriority: 10,
|
|
331
|
-
* });
|
|
332
|
-
*
|
|
333
|
-
* // Queue with TTL-backoff retry (returns infrastructure automatically)
|
|
334
|
-
* const dlx = defineExchange('orders-dlx', 'direct', { durable: true });
|
|
335
|
-
* const orderQueue = defineQueue('order-processing', {
|
|
336
|
-
* deadLetter: { exchange: dlx },
|
|
337
|
-
* retry: { mode: 'ttl-backoff', maxRetries: 5 },
|
|
338
|
-
* });
|
|
339
|
-
* // orderQueue is QueueWithTtlBackoffInfrastructure, pass directly to defineContract
|
|
340
|
-
* ```
|
|
341
|
-
*/
|
|
342
284
|
function defineQueue(name, options) {
|
|
343
285
|
const opts = options ?? {};
|
|
344
286
|
const type = opts.type ?? "quorum";
|
|
@@ -353,18 +295,18 @@ function defineQueue(name, options) {
|
|
|
353
295
|
if (inputRetry.mode === "quorum-native") {
|
|
354
296
|
if (quorumOpts.deliveryLimit === void 0) throw new Error(`Queue "${name}" uses quorum-native retry mode but deliveryLimit is not configured. Quorum-native retry requires deliveryLimit to be set.`);
|
|
355
297
|
}
|
|
356
|
-
const retry
|
|
357
|
-
const queueDefinition
|
|
298
|
+
const retry = inputRetry.mode === "quorum-native" ? inputRetry : resolveTtlBackoffOptions(inputRetry);
|
|
299
|
+
const queueDefinition = {
|
|
358
300
|
...baseProps,
|
|
359
301
|
type: "quorum",
|
|
360
|
-
retry
|
|
302
|
+
retry
|
|
361
303
|
};
|
|
362
304
|
if (quorumOpts.deliveryLimit !== void 0) {
|
|
363
305
|
if (quorumOpts.deliveryLimit < 1 || !Number.isInteger(quorumOpts.deliveryLimit)) throw new Error(`Invalid deliveryLimit: ${quorumOpts.deliveryLimit}. Must be a positive integer.`);
|
|
364
|
-
queueDefinition
|
|
306
|
+
queueDefinition.deliveryLimit = quorumOpts.deliveryLimit;
|
|
365
307
|
}
|
|
366
|
-
if (retry
|
|
367
|
-
return queueDefinition
|
|
308
|
+
if (retry.mode === "ttl-backoff" && queueDefinition.deadLetter) return wrapWithTtlBackoffInfrastructure(queueDefinition);
|
|
309
|
+
return queueDefinition;
|
|
368
310
|
}
|
|
369
311
|
const classicOpts = opts;
|
|
370
312
|
if (classicOpts.retry?.mode === "quorum-native") throw new Error(`Queue "${name}" uses quorum-native retry mode but is a classic queue. Quorum-native retry requires quorum queues (type: "quorum").`);
|
|
@@ -411,10 +353,10 @@ function defineQueue(name, options) {
|
|
|
411
353
|
* deliveryLimit: 3, // Retry up to 3 times
|
|
412
354
|
* });
|
|
413
355
|
*
|
|
356
|
+
* // Use in a contract — exchanges, queues, and bindings are auto-extracted
|
|
414
357
|
* const contract = defineContract({
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
* // ...
|
|
358
|
+
* publishers: { ... },
|
|
359
|
+
* consumers: { processOrder: defineEventConsumer(event, orderQueue) },
|
|
418
360
|
* });
|
|
419
361
|
* ```
|
|
420
362
|
*
|
|
@@ -469,10 +411,10 @@ function defineQuorumQueue(name, options) {
|
|
|
469
411
|
* maxDelayMs: 30000, // Cap at 30s
|
|
470
412
|
* });
|
|
471
413
|
*
|
|
414
|
+
* // Use in a contract — wait queue, bindings, and DLX are auto-extracted
|
|
472
415
|
* const contract = defineContract({
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
* // ... bindings auto-generated
|
|
416
|
+
* publishers: { ... },
|
|
417
|
+
* consumers: { processOrder: defineEventConsumer(event, extractQueue(orderQueue)) },
|
|
476
418
|
* });
|
|
477
419
|
*
|
|
478
420
|
* // To access the underlying queue definition (e.g., for the queue name):
|
|
@@ -682,10 +624,14 @@ function defineEventConsumer(eventPublisher, queue, options) {
|
|
|
682
624
|
const bindingArguments = options?.arguments ?? eventPublisher.arguments;
|
|
683
625
|
if (bindingArguments !== void 0) bindingOptions.arguments = bindingArguments;
|
|
684
626
|
const binding = defineQueueBindingInternal(queue, exchange, bindingOptions);
|
|
627
|
+
const consumer = defineConsumer(queue, message);
|
|
685
628
|
return {
|
|
686
629
|
__brand: "EventConsumerResult",
|
|
687
|
-
consumer
|
|
688
|
-
binding
|
|
630
|
+
consumer,
|
|
631
|
+
binding,
|
|
632
|
+
exchange,
|
|
633
|
+
queue: consumer.queue,
|
|
634
|
+
deadLetterExchange: consumer.queue.deadLetter?.exchange
|
|
689
635
|
};
|
|
690
636
|
}
|
|
691
637
|
/**
|
|
@@ -714,11 +660,14 @@ function isEventConsumerResult(value) {
|
|
|
714
660
|
* @internal
|
|
715
661
|
*/
|
|
716
662
|
function defineCommandConsumer(queue, exchange, message, options) {
|
|
663
|
+
const consumer = defineConsumer(queue, message);
|
|
717
664
|
return {
|
|
718
665
|
__brand: "CommandConsumerConfig",
|
|
719
|
-
consumer
|
|
666
|
+
consumer,
|
|
720
667
|
binding: defineQueueBindingInternal(queue, exchange, options),
|
|
721
668
|
exchange,
|
|
669
|
+
queue: consumer.queue,
|
|
670
|
+
deadLetterExchange: consumer.queue.deadLetter?.exchange,
|
|
722
671
|
message,
|
|
723
672
|
routingKey: options?.routingKey
|
|
724
673
|
};
|
|
@@ -750,19 +699,17 @@ function isCommandConsumerConfig(value) {
|
|
|
750
699
|
* Define an AMQP contract.
|
|
751
700
|
*
|
|
752
701
|
* A contract is the central definition of your AMQP messaging topology. It brings together
|
|
753
|
-
*
|
|
702
|
+
* publishers and consumers in a single, type-safe definition. Exchanges, queues, and bindings
|
|
703
|
+
* are automatically extracted from publishers and consumers.
|
|
754
704
|
*
|
|
755
705
|
* The contract is used by both clients (for publishing) and workers (for consuming) to ensure
|
|
756
706
|
* type safety throughout your messaging infrastructure. TypeScript will infer all message types
|
|
757
707
|
* and publisher/consumer names from the contract.
|
|
758
708
|
*
|
|
759
|
-
* @param definition - The contract definition containing
|
|
760
|
-
* @param definition.exchanges - Named exchange definitions
|
|
761
|
-
* @param definition.queues - Named queue definitions
|
|
762
|
-
* @param definition.bindings - Named binding definitions (queue-to-exchange or exchange-to-exchange)
|
|
709
|
+
* @param definition - The contract definition containing publishers and consumers
|
|
763
710
|
* @param definition.publishers - Named publisher definitions for sending messages
|
|
764
711
|
* @param definition.consumers - Named consumer definitions for receiving messages
|
|
765
|
-
* @returns The
|
|
712
|
+
* @returns The contract definition with fully inferred exchanges, queues, bindings, publishers, and consumers
|
|
766
713
|
*
|
|
767
714
|
* @example
|
|
768
715
|
* ```typescript
|
|
@@ -770,16 +717,20 @@ function isCommandConsumerConfig(value) {
|
|
|
770
717
|
* defineContract,
|
|
771
718
|
* defineExchange,
|
|
772
719
|
* defineQueue,
|
|
773
|
-
*
|
|
774
|
-
*
|
|
775
|
-
* defineConsumer,
|
|
720
|
+
* defineEventPublisher,
|
|
721
|
+
* defineEventConsumer,
|
|
776
722
|
* defineMessage,
|
|
777
723
|
* } from '@amqp-contract/contract';
|
|
778
724
|
* import { z } from 'zod';
|
|
779
725
|
*
|
|
780
726
|
* // Define resources
|
|
781
727
|
* const ordersExchange = defineExchange('orders', 'topic', { durable: true });
|
|
782
|
-
* const
|
|
728
|
+
* const dlx = defineExchange('orders-dlx', 'direct', { durable: true });
|
|
729
|
+
* const orderQueue = defineQueue('order-processing', {
|
|
730
|
+
* deadLetter: { exchange: dlx },
|
|
731
|
+
* retry: { mode: 'quorum-native' },
|
|
732
|
+
* deliveryLimit: 3,
|
|
733
|
+
* });
|
|
783
734
|
* const orderMessage = defineMessage(
|
|
784
735
|
* z.object({
|
|
785
736
|
* orderId: z.string(),
|
|
@@ -787,76 +738,114 @@ function isCommandConsumerConfig(value) {
|
|
|
787
738
|
* })
|
|
788
739
|
* );
|
|
789
740
|
*
|
|
790
|
-
* //
|
|
741
|
+
* // Define event publisher
|
|
742
|
+
* const orderCreatedEvent = defineEventPublisher(ordersExchange, orderMessage, {
|
|
743
|
+
* routingKey: 'order.created',
|
|
744
|
+
* });
|
|
745
|
+
*
|
|
746
|
+
* // Compose contract - exchanges, queues, bindings are auto-extracted
|
|
791
747
|
* export const contract = defineContract({
|
|
792
|
-
* exchanges: {
|
|
793
|
-
* orders: ordersExchange,
|
|
794
|
-
* },
|
|
795
|
-
* queues: {
|
|
796
|
-
* orderProcessing: orderQueue,
|
|
797
|
-
* },
|
|
798
|
-
* bindings: {
|
|
799
|
-
* orderBinding: defineQueueBinding(orderQueue, ordersExchange, {
|
|
800
|
-
* routingKey: 'order.created',
|
|
801
|
-
* }),
|
|
802
|
-
* },
|
|
803
748
|
* publishers: {
|
|
804
|
-
* orderCreated:
|
|
805
|
-
* routingKey: 'order.created',
|
|
806
|
-
* }),
|
|
749
|
+
* orderCreated: orderCreatedEvent,
|
|
807
750
|
* },
|
|
808
751
|
* consumers: {
|
|
809
|
-
* processOrder:
|
|
752
|
+
* processOrder: defineEventConsumer(orderCreatedEvent, orderQueue),
|
|
810
753
|
* },
|
|
811
754
|
* });
|
|
812
755
|
*
|
|
813
756
|
* // TypeScript now knows:
|
|
757
|
+
* // - contract.exchanges.orders, contract.exchanges['orders-dlx']
|
|
758
|
+
* // - contract.queues['order-processing']
|
|
759
|
+
* // - contract.bindings.processOrderBinding
|
|
814
760
|
* // - client.publish('orderCreated', { orderId: string, amount: number })
|
|
815
|
-
* // - handler:
|
|
761
|
+
* // - handler: (message: { orderId: string, amount: number }) => Future<Result<void, HandlerError>>
|
|
816
762
|
* ```
|
|
817
763
|
*/
|
|
818
764
|
function defineContract(definition) {
|
|
819
|
-
const { publishers: inputPublishers, consumers: inputConsumers
|
|
820
|
-
const result =
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
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
|
|
834
|
-
};
|
|
835
|
-
}
|
|
765
|
+
const { publishers: inputPublishers, consumers: inputConsumers } = definition;
|
|
766
|
+
const result = {
|
|
767
|
+
exchanges: {},
|
|
768
|
+
queues: {},
|
|
769
|
+
bindings: {},
|
|
770
|
+
publishers: {},
|
|
771
|
+
consumers: {}
|
|
772
|
+
};
|
|
836
773
|
if (inputPublishers && Object.keys(inputPublishers).length > 0) {
|
|
837
774
|
const processedPublishers = {};
|
|
775
|
+
const exchanges = {};
|
|
838
776
|
for (const [name, entry] of Object.entries(inputPublishers)) if (isEventPublisherConfig(entry)) {
|
|
777
|
+
exchanges[entry.exchange.name] = entry.exchange;
|
|
839
778
|
const publisherOptions = {};
|
|
840
779
|
if (entry.routingKey !== void 0) publisherOptions.routingKey = entry.routingKey;
|
|
841
780
|
processedPublishers[name] = definePublisherInternal(entry.exchange, entry.message, publisherOptions);
|
|
842
|
-
} else
|
|
781
|
+
} else {
|
|
782
|
+
const publisher = entry;
|
|
783
|
+
exchanges[publisher.exchange.name] = publisher.exchange;
|
|
784
|
+
processedPublishers[name] = publisher;
|
|
785
|
+
}
|
|
843
786
|
result.publishers = processedPublishers;
|
|
787
|
+
result.exchanges = {
|
|
788
|
+
...result.exchanges,
|
|
789
|
+
...exchanges
|
|
790
|
+
};
|
|
844
791
|
}
|
|
845
792
|
if (inputConsumers && Object.keys(inputConsumers).length > 0) {
|
|
846
793
|
const processedConsumers = {};
|
|
847
794
|
const consumerBindings = {};
|
|
795
|
+
const queues = {};
|
|
796
|
+
const exchanges = {};
|
|
848
797
|
for (const [name, entry] of Object.entries(inputConsumers)) if (isEventConsumerResult(entry)) {
|
|
849
798
|
processedConsumers[name] = entry.consumer;
|
|
850
799
|
consumerBindings[`${name}Binding`] = entry.binding;
|
|
800
|
+
const queueEntry = entry.consumer.queue;
|
|
801
|
+
queues[queueEntry.name] = queueEntry;
|
|
802
|
+
exchanges[entry.binding.exchange.name] = entry.binding.exchange;
|
|
803
|
+
if (queueEntry.deadLetter?.exchange) exchanges[queueEntry.deadLetter.exchange.name] = queueEntry.deadLetter.exchange;
|
|
851
804
|
} else if (isCommandConsumerConfig(entry)) {
|
|
852
805
|
processedConsumers[name] = entry.consumer;
|
|
853
806
|
consumerBindings[`${name}Binding`] = entry.binding;
|
|
854
|
-
|
|
807
|
+
const queueEntry = entry.consumer.queue;
|
|
808
|
+
queues[queueEntry.name] = queueEntry;
|
|
809
|
+
exchanges[entry.exchange.name] = entry.exchange;
|
|
810
|
+
if (queueEntry.deadLetter?.exchange) exchanges[queueEntry.deadLetter.exchange.name] = queueEntry.deadLetter.exchange;
|
|
811
|
+
} else {
|
|
812
|
+
const consumer = entry;
|
|
813
|
+
processedConsumers[name] = consumer;
|
|
814
|
+
const queueEntry = consumer.queue;
|
|
815
|
+
queues[queueEntry.name] = queueEntry;
|
|
816
|
+
if (queueEntry.deadLetter?.exchange) exchanges[queueEntry.deadLetter.exchange.name] = queueEntry.deadLetter.exchange;
|
|
817
|
+
}
|
|
818
|
+
for (const queue of Object.values(queues)) if (queue.retry?.mode === "ttl-backoff" && queue.deadLetter) {
|
|
819
|
+
const dlx = queue.deadLetter.exchange;
|
|
820
|
+
const waitQueueName = `${queue.name}-wait`;
|
|
821
|
+
const waitQueue = {
|
|
822
|
+
name: waitQueueName,
|
|
823
|
+
type: "quorum",
|
|
824
|
+
durable: queue.durable ?? true,
|
|
825
|
+
deadLetter: {
|
|
826
|
+
exchange: dlx,
|
|
827
|
+
routingKey: queue.name
|
|
828
|
+
},
|
|
829
|
+
retry: resolveTtlBackoffOptions(void 0)
|
|
830
|
+
};
|
|
831
|
+
queues[waitQueueName] = waitQueue;
|
|
832
|
+
consumerBindings[`${queue.name}WaitBinding`] = defineQueueBindingInternal(waitQueue, dlx, { routingKey: waitQueueName });
|
|
833
|
+
consumerBindings[`${queue.name}RetryBinding`] = defineQueueBindingInternal(queue, dlx, { routingKey: queue.name });
|
|
834
|
+
exchanges[dlx.name] = dlx;
|
|
835
|
+
}
|
|
855
836
|
result.consumers = processedConsumers;
|
|
856
|
-
|
|
837
|
+
result.bindings = {
|
|
857
838
|
...result.bindings,
|
|
858
839
|
...consumerBindings
|
|
859
840
|
};
|
|
841
|
+
result.queues = {
|
|
842
|
+
...result.queues,
|
|
843
|
+
...queues
|
|
844
|
+
};
|
|
845
|
+
result.exchanges = {
|
|
846
|
+
...result.exchanges,
|
|
847
|
+
...exchanges
|
|
848
|
+
};
|
|
860
849
|
}
|
|
861
850
|
return result;
|
|
862
851
|
}
|
|
@@ -894,23 +883,15 @@ function defineContract(definition) {
|
|
|
894
883
|
* },
|
|
895
884
|
* });
|
|
896
885
|
*
|
|
897
|
-
* //
|
|
898
|
-
* const retryInfra = defineTtlBackoffRetryInfrastructure(orderQueue);
|
|
899
|
-
*
|
|
900
|
-
* // Spread into contract
|
|
886
|
+
* // Infrastructure is auto-extracted when using defineContract:
|
|
901
887
|
* const contract = defineContract({
|
|
902
|
-
*
|
|
903
|
-
*
|
|
904
|
-
* orderProcessing: orderQueue,
|
|
905
|
-
* orderProcessingWait: retryInfra.waitQueue,
|
|
906
|
-
* },
|
|
907
|
-
* bindings: {
|
|
908
|
-
* ...// your other bindings
|
|
909
|
-
* orderWaitBinding: retryInfra.waitQueueBinding,
|
|
910
|
-
* orderRetryBinding: retryInfra.mainQueueRetryBinding,
|
|
911
|
-
* },
|
|
912
|
-
* // ... publishers and consumers
|
|
888
|
+
* publishers: { ... },
|
|
889
|
+
* consumers: { processOrder: defineEventConsumer(event, extractQueue(orderQueue)) },
|
|
913
890
|
* });
|
|
891
|
+
* // contract.queues includes the wait queue, contract.bindings includes retry bindings
|
|
892
|
+
*
|
|
893
|
+
* // Or generate manually for advanced use cases:
|
|
894
|
+
* const retryInfra = defineTtlBackoffRetryInfrastructure(orderQueue);
|
|
914
895
|
* ```
|
|
915
896
|
*/
|
|
916
897
|
function defineTtlBackoffRetryInfrastructure(queueEntry, options) {
|