@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 +30 -20
- package/dist/index.cjs +390 -97
- package/dist/index.d.cts +725 -358
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +725 -358
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +379 -95
- package/dist/index.mjs.map +1 -1
- package/docs/index.md +766 -213
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,13 +18,21 @@ pnpm add @amqp-contract/contract
|
|
|
18
18
|
|
|
19
19
|
## Quick Start
|
|
20
20
|
|
|
21
|
-
### Recommended:
|
|
21
|
+
### Recommended: Event / Command Patterns
|
|
22
22
|
|
|
23
|
-
For robust contract definitions with guaranteed consistency, use
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
45
|
-
|
|
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
|
-
|
|
62
|
-
|
|
65
|
+
publishers: {
|
|
66
|
+
// EventPublisherConfig → auto-extracted to publisher
|
|
67
|
+
orderCreated: orderCreatedEvent,
|
|
68
|
+
},
|
|
63
69
|
consumers: {
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
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
|
-
- [
|
|
84
|
-
- [
|
|
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
|
-
*
|
|
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
|
-
*
|
|
176
|
-
*
|
|
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
|
-
*
|
|
184
|
-
*
|
|
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
|
-
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
...
|
|
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
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
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
|
-
|
|
536
|
-
const
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
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
|
-
|
|
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.
|
|
952
|
+
exports.extractConsumer = extractConsumer;
|
|
953
|
+
exports.extractQueue = extractQueue;
|
|
954
|
+
exports.isCommandConsumerConfig = isCommandConsumerConfig;
|
|
955
|
+
exports.isEventConsumerResult = isEventConsumerResult;
|
|
956
|
+
exports.isEventPublisherConfig = isEventPublisherConfig;
|
|
957
|
+
exports.isQueueWithTtlBackoffInfrastructure = isQueueWithTtlBackoffInfrastructure;
|