@zdavison/matador 3.0.2 → 3.0.5
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/cli.ts +66 -52
- package/dist/checkpoint/context.test.js +4 -0
- package/dist/codec/rabbitmq-codec.test.js +36 -41
- package/dist/core/fanout.test.js +12 -2
- package/dist/index.cjs +89 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pipeline/pipeline.test.js +16 -4
- package/dist/schema/types.test.js +4 -0
- package/dist/topology/builder.d.ts.map +1 -1
- package/dist/topology/builder.js +66 -41
- package/dist/transport/local/local-transport.test.js +6 -2
- package/dist/transport/multi/multi-transport.d.ts.map +1 -1
- package/dist/transport/multi/multi-transport.js +2 -0
- package/dist/transport/multi/multi-transport.test.js +6 -2
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts +1 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -1
- package/dist/transport/rabbitmq/rabbitmq-transport.js +23 -30
- package/package.json +1 -1
|
@@ -690,7 +690,10 @@ describe('ProcessingPipeline', () => {
|
|
|
690
690
|
const receipt = createReceipt({ sourceQueue: 'test-queue' });
|
|
691
691
|
await pipeline.process(new Uint8Array(), receipt);
|
|
692
692
|
const calls = sendMock.mock.calls;
|
|
693
|
-
const
|
|
693
|
+
const firstCall = calls[0];
|
|
694
|
+
if (!firstCall)
|
|
695
|
+
throw new Error('expected sendMock to be called');
|
|
696
|
+
const sentEnvelope = firstCall[1];
|
|
694
697
|
expect(sentEnvelope.docket.lastError).toBe('Test error message');
|
|
695
698
|
expect(sentEnvelope.docket.firstError).toBe('Test error message');
|
|
696
699
|
});
|
|
@@ -803,7 +806,10 @@ describe('ProcessingPipeline', () => {
|
|
|
803
806
|
const receipt = createReceipt({ sourceQueue: 'original-queue' });
|
|
804
807
|
await pipeline.process(new Uint8Array(), receipt);
|
|
805
808
|
const calls = sendToDeadLetterMock.mock.calls;
|
|
806
|
-
const
|
|
809
|
+
const firstCall = calls[0];
|
|
810
|
+
if (!firstCall)
|
|
811
|
+
throw new Error('expected sendToDeadLetter to be called');
|
|
812
|
+
const sentEnvelope = firstCall[2];
|
|
807
813
|
expect(sentEnvelope.docket.originalQueue).toBe('original-queue');
|
|
808
814
|
});
|
|
809
815
|
it('should handle discard decision', async () => {
|
|
@@ -956,7 +962,10 @@ describe('ProcessingPipeline', () => {
|
|
|
956
962
|
const receipt = createReceipt({ sourceQueue: 'test-queue' });
|
|
957
963
|
await pipeline.process(new Uint8Array(), receipt);
|
|
958
964
|
const calls = sendMock.mock.calls;
|
|
959
|
-
const
|
|
965
|
+
const firstCall = calls[0];
|
|
966
|
+
if (!firstCall)
|
|
967
|
+
throw new Error('expected sendMock to be called');
|
|
968
|
+
const sentEnvelope = firstCall[1];
|
|
960
969
|
expect(sentEnvelope.docket.firstError).toBe('First failure');
|
|
961
970
|
expect(sentEnvelope.docket.lastError).toBe('First failure');
|
|
962
971
|
});
|
|
@@ -994,7 +1003,10 @@ describe('ProcessingPipeline', () => {
|
|
|
994
1003
|
const receipt = createReceipt({ sourceQueue: 'test-queue' });
|
|
995
1004
|
await pipeline.process(new Uint8Array(), receipt);
|
|
996
1005
|
const calls = sendMock.mock.calls;
|
|
997
|
-
const
|
|
1006
|
+
const firstCall = calls[0];
|
|
1007
|
+
if (!firstCall)
|
|
1008
|
+
throw new Error('expected sendMock to be called');
|
|
1009
|
+
const sentEnvelope = firstCall[1];
|
|
998
1010
|
expect(sentEnvelope.docket.firstError).toBe('Original error');
|
|
999
1011
|
expect(sentEnvelope.docket.lastError).toBe('Second failure');
|
|
1000
1012
|
});
|
|
@@ -76,6 +76,8 @@ describe('installPlugins', () => {
|
|
|
76
76
|
const schema = installPlugins(baseSchema, [{ subscriber: globalSub }]);
|
|
77
77
|
// Check user.created has both subscribers
|
|
78
78
|
const userEntry = schema[UserCreatedEvent.key];
|
|
79
|
+
if (!userEntry)
|
|
80
|
+
throw new Error('expected user.created entry');
|
|
79
81
|
expect(isSchemaEntryTuple(userEntry)).toBe(true);
|
|
80
82
|
const [, userSubs] = userEntry;
|
|
81
83
|
expect(userSubs).toHaveLength(2);
|
|
@@ -183,6 +185,8 @@ describe('installPlugins', () => {
|
|
|
183
185
|
};
|
|
184
186
|
const schema = installPlugins(baseSchema, [{ subscriber: globalSub }]);
|
|
185
187
|
const entry = schema[UserCreatedEvent.key];
|
|
188
|
+
if (!entry)
|
|
189
|
+
throw new Error('expected user.created entry');
|
|
186
190
|
expect(isSchemaEntryTuple(entry)).toBe(true);
|
|
187
191
|
const [eventClass, subs] = entry;
|
|
188
192
|
expect(eventClass).toBe(UserCreatedEvent);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/topology/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,QAAQ,EACT,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;AAWzD;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,KAAM,YAAW,cAAc;aASxD,MAAM,EAAE,SAAS,MAAM,EAAE;IAR3C,QAAQ,CAAC,WAAW,SAIuC;gBAGzD,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,SAAS,MAAM,EAAE;CAK5C;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B;;OAEG;IACH,MAAM,CAAC,MAAM,IAAI,eAAe;IAIhC,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,UAAU,CAGhB;IACF,OAAO,CAAC,KAAK,CAIX;IAEF;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKtC;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI;IAapD;;;OAGG;IACH,KAAK,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI;IACxC;;;;OAIG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI;IAWjD;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,IAAI;IAQvD;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI;IAS7C;;OAEG;IACH,YAAY,IAAI,IAAI;IAKpB;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAQzB;;OAEG;IACH,QAAQ,IAAI,SAAS,MAAM,EAAE;
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/topology/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,QAAQ,EACT,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;AAWzD;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,KAAM,YAAW,cAAc;aASxD,MAAM,EAAE,SAAS,MAAM,EAAE;IAR3C,QAAQ,CAAC,WAAW,SAIuC;gBAGzD,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,SAAS,MAAM,EAAE;CAK5C;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B;;OAEG;IACH,MAAM,CAAC,MAAM,IAAI,eAAe;IAIhC,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,UAAU,CAGhB;IACF,OAAO,CAAC,KAAK,CAIX;IAEF;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKtC;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI;IAapD;;;OAGG;IACH,KAAK,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI;IACxC;;;;OAIG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI;IAWjD;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,IAAI;IAQvD;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI;IAS7C;;OAEG;IACH,YAAY,IAAI,IAAI;IAKpB;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAQzB;;OAEG;IACH,QAAQ,IAAI,SAAS,MAAM,EAAE;IAQ7B;;;OAGG;IACH,KAAK,IAAI,QAAQ;CAgBlB"}
|
package/dist/topology/builder.js
CHANGED
|
@@ -104,47 +104,11 @@ export class TopologyBuilder {
|
|
|
104
104
|
* Validates the topology configuration.
|
|
105
105
|
*/
|
|
106
106
|
validate() {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
issues.push('Namespace must start with a letter and contain only alphanumeric characters, underscores, and hyphens');
|
|
113
|
-
}
|
|
114
|
-
if (this.queues.length === 0) {
|
|
115
|
-
issues.push('At least one queue is required');
|
|
116
|
-
}
|
|
117
|
-
const queueNames = new Set();
|
|
118
|
-
for (const queue of this.queues) {
|
|
119
|
-
if (!queue.name || queue.name.trim() === '') {
|
|
120
|
-
issues.push('Queue name cannot be empty');
|
|
121
|
-
}
|
|
122
|
-
else if (!queue.exact && !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(queue.name)) {
|
|
123
|
-
// Skip pattern validation for exact queues (allows names like 'matador.shared.id-platform')
|
|
124
|
-
issues.push(`Queue name "${queue.name}" must start with a letter and contain only alphanumeric characters, underscores, and hyphens`);
|
|
125
|
-
}
|
|
126
|
-
else if (queueNames.has(queue.name)) {
|
|
127
|
-
issues.push(`Duplicate queue name: "${queue.name}"`);
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
queueNames.add(queue.name);
|
|
131
|
-
}
|
|
132
|
-
if (queue.concurrency !== undefined && queue.concurrency < 1) {
|
|
133
|
-
issues.push(`Queue "${queue.name}" concurrency must be at least 1`);
|
|
134
|
-
}
|
|
135
|
-
if (queue.consumerTimeout !== undefined && queue.consumerTimeout < 0) {
|
|
136
|
-
issues.push(`Queue "${queue.name}" consumer timeout must be non-negative`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (this.retry.enabled) {
|
|
140
|
-
if (this.retry.defaultDelayMs < 0) {
|
|
141
|
-
issues.push('Default retry delay must be non-negative');
|
|
142
|
-
}
|
|
143
|
-
if (this.retry.maxDelayMs < this.retry.defaultDelayMs) {
|
|
144
|
-
issues.push('Max retry delay must be greater than or equal to default delay');
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
return issues;
|
|
107
|
+
return [
|
|
108
|
+
...validateNamespace(this.namespace),
|
|
109
|
+
...validateQueues(this.queues),
|
|
110
|
+
...validateRetry(this.retry),
|
|
111
|
+
];
|
|
148
112
|
}
|
|
149
113
|
/**
|
|
150
114
|
* Builds the topology configuration.
|
|
@@ -163,3 +127,64 @@ export class TopologyBuilder {
|
|
|
163
127
|
};
|
|
164
128
|
}
|
|
165
129
|
}
|
|
130
|
+
const IDENTIFIER_PATTERN = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
|
|
131
|
+
function validateNamespace(namespace) {
|
|
132
|
+
if (!namespace || namespace.trim() === '') {
|
|
133
|
+
return ['Namespace is required'];
|
|
134
|
+
}
|
|
135
|
+
if (!IDENTIFIER_PATTERN.test(namespace)) {
|
|
136
|
+
return [
|
|
137
|
+
'Namespace must start with a letter and contain only alphanumeric characters, underscores, and hyphens',
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
function validateQueueName(queue, seen) {
|
|
143
|
+
if (!queue.name || queue.name.trim() === '') {
|
|
144
|
+
return ['Queue name cannot be empty'];
|
|
145
|
+
}
|
|
146
|
+
if (!queue.exact && !IDENTIFIER_PATTERN.test(queue.name)) {
|
|
147
|
+
return [
|
|
148
|
+
`Queue name "${queue.name}" must start with a letter and contain only alphanumeric characters, underscores, and hyphens`,
|
|
149
|
+
];
|
|
150
|
+
}
|
|
151
|
+
if (seen.has(queue.name)) {
|
|
152
|
+
return [`Duplicate queue name: "${queue.name}"`];
|
|
153
|
+
}
|
|
154
|
+
seen.add(queue.name);
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
function validateQueueLimits(queue) {
|
|
158
|
+
const issues = [];
|
|
159
|
+
if (queue.concurrency !== undefined && queue.concurrency < 1) {
|
|
160
|
+
issues.push(`Queue "${queue.name}" concurrency must be at least 1`);
|
|
161
|
+
}
|
|
162
|
+
if (queue.consumerTimeout !== undefined && queue.consumerTimeout < 0) {
|
|
163
|
+
issues.push(`Queue "${queue.name}" consumer timeout must be non-negative`);
|
|
164
|
+
}
|
|
165
|
+
return issues;
|
|
166
|
+
}
|
|
167
|
+
function validateQueues(queues) {
|
|
168
|
+
const issues = [];
|
|
169
|
+
if (queues.length === 0) {
|
|
170
|
+
issues.push('At least one queue is required');
|
|
171
|
+
}
|
|
172
|
+
const seen = new Set();
|
|
173
|
+
for (const queue of queues) {
|
|
174
|
+
issues.push(...validateQueueName(queue, seen));
|
|
175
|
+
issues.push(...validateQueueLimits(queue));
|
|
176
|
+
}
|
|
177
|
+
return issues;
|
|
178
|
+
}
|
|
179
|
+
function validateRetry(retry) {
|
|
180
|
+
if (!retry.enabled)
|
|
181
|
+
return [];
|
|
182
|
+
const issues = [];
|
|
183
|
+
if (retry.defaultDelayMs < 0) {
|
|
184
|
+
issues.push('Default retry delay must be non-negative');
|
|
185
|
+
}
|
|
186
|
+
if (retry.maxDelayMs < retry.defaultDelayMs) {
|
|
187
|
+
issues.push('Max retry delay must be greater than or equal to default delay');
|
|
188
|
+
}
|
|
189
|
+
return issues;
|
|
190
|
+
}
|
|
@@ -56,7 +56,8 @@ describe('LocalTransport', () => {
|
|
|
56
56
|
const envelope = createTestEnvelope();
|
|
57
57
|
await transport.send('test-queue', envelope);
|
|
58
58
|
const received = await transport.receiveOne('test-queue');
|
|
59
|
-
|
|
59
|
+
if (!received)
|
|
60
|
+
throw new Error('expected received message');
|
|
60
61
|
await transport.complete(received.receipt);
|
|
61
62
|
expect(transport.getQueueSize('test-queue')).toBe(0);
|
|
62
63
|
expect(transport.getCompleted()).toHaveLength(1);
|
|
@@ -112,7 +113,8 @@ describe('LocalTransport', () => {
|
|
|
112
113
|
const envelope = createTestEnvelope();
|
|
113
114
|
await transport.send('test-queue', envelope);
|
|
114
115
|
const received = await transport.receiveOne('test-queue');
|
|
115
|
-
|
|
116
|
+
if (!received)
|
|
117
|
+
throw new Error('expected received message');
|
|
116
118
|
await transport.sendToDeadLetter(received.receipt, 'dlq', envelope, 'test error');
|
|
117
119
|
expect(transport.getQueueSize('test-queue')).toBe(0);
|
|
118
120
|
expect(transport.getQueueSize('test-queue.dlq')).toBe(1);
|
|
@@ -145,6 +147,8 @@ describe('LocalTransport', () => {
|
|
|
145
147
|
await transport.connect();
|
|
146
148
|
await transport.send('test-queue', createTestEnvelope());
|
|
147
149
|
const received = await transport.receiveOne('test-queue');
|
|
150
|
+
if (!received)
|
|
151
|
+
throw new Error('expected received message');
|
|
148
152
|
await transport.complete(received.receipt);
|
|
149
153
|
transport.clear();
|
|
150
154
|
expect(transport.getQueueSize('test-queue')).toBe(0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multi-transport.d.ts","sourceRoot":"","sources":["../../../src/transport/multi/multi-transport.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACV,MAAM,iBAAiB,CAAC;AAgBzB;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAE1C;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MACzB,MAAM,GACN,SAAS,GACT,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAEhC;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,IAAI,CAAC;CAC1E;AAmBD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,cAAe,YAAW,SAAS;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC;IAC7C,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;IAE5B,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAE1C,2DAA2D;IAC3D,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAElC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,SAAS,CAAS;gBAEd,MAAM,EAAE,oBAAoB,EAAE,KAAK,GAAE,mBAAwB;IAcnE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAMxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC,WAAW,IAAI,OAAO;IAKhB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAKhD,IAAI,CACR,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"multi-transport.d.ts","sourceRoot":"","sources":["../../../src/transport/multi/multi-transport.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACV,MAAM,iBAAiB,CAAC;AAgBzB;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAE1C;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MACzB,MAAM,GACN,SAAS,GACT,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAEhC;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,IAAI,CAAC;CAC1E;AAmBD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,cAAe,YAAW,SAAS;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC;IAC7C,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;IAE5B,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAE1C,2DAA2D;IAC3D,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAElC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,SAAS,CAAS;gBAEd,MAAM,EAAE,oBAAoB,EAAE,KAAK,GAAE,mBAAwB;IAcnE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAMxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC,WAAW,IAAI,OAAO;IAKhB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAKhD,IAAI,CACR,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IA0C7B;;OAEG;YACW,eAAe;IAwB7B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IASnB,SAAS,CACb,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,YAAY,CAAC;IA0BlB,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAKhD,gBAAgB,CACpB,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;CAOjB"}
|
|
@@ -120,6 +120,8 @@ describe('MultiTransport', () => {
|
|
|
120
120
|
await transportWithCallback.send('test-queue', envelope);
|
|
121
121
|
expect(fallbackContexts).toHaveLength(1);
|
|
122
122
|
const ctx = fallbackContexts[0];
|
|
123
|
+
if (!ctx)
|
|
124
|
+
throw new Error('expected fallback context');
|
|
123
125
|
expect(ctx.failedTransport).toBe('local');
|
|
124
126
|
expect(ctx.nextTransport).toBe('local');
|
|
125
127
|
expect(ctx.queue).toBe('test-queue');
|
|
@@ -206,7 +208,8 @@ describe('MultiTransport', () => {
|
|
|
206
208
|
it('should complete using primary transport', async () => {
|
|
207
209
|
await primary.send('test-queue', createTestEnvelope());
|
|
208
210
|
const received = await primary.receiveOne('test-queue');
|
|
209
|
-
|
|
211
|
+
if (!received)
|
|
212
|
+
throw new Error('expected received message');
|
|
210
213
|
await transport.complete(received.receipt);
|
|
211
214
|
expect(primary.getCompleted()).toHaveLength(1);
|
|
212
215
|
});
|
|
@@ -219,7 +222,8 @@ describe('MultiTransport', () => {
|
|
|
219
222
|
const envelope = createTestEnvelope();
|
|
220
223
|
await primary.send('test-queue', envelope);
|
|
221
224
|
const received = await primary.receiveOne('test-queue');
|
|
222
|
-
|
|
225
|
+
if (!received)
|
|
226
|
+
throw new Error('expected received message');
|
|
223
227
|
await transport.sendToDeadLetter(received.receipt, 'dlq', envelope, 'test error');
|
|
224
228
|
expect(primary.getQueueSize('test-queue')).toBe(0);
|
|
225
229
|
expect(primary.getQueueSize('test-queue.dlq')).toBe(1);
|
|
@@ -66,6 +66,7 @@ export declare class RabbitMQTransport implements Transport {
|
|
|
66
66
|
private doConnect;
|
|
67
67
|
private doDisconnect;
|
|
68
68
|
private setupDelayedExchange;
|
|
69
|
+
private buildWorkQueueOptions;
|
|
69
70
|
private assertWorkQueue;
|
|
70
71
|
private assertRetryQueue;
|
|
71
72
|
private assertDeadLetterQueues;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rabbitmq-transport.d.ts","sourceRoot":"","sources":["../../../src/transport/rabbitmq/rabbitmq-transport.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,sBAAsB,CAAC;AAClE,OAAO,KAAK,EAAmB,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACV,MAAM,iBAAiB,CAAC;AAEzB;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,8BAA8B;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB,0DAA0D;IAC1D,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAEhC,uCAAuC;IACvC,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,uBAAuB,CAAC,GAAG,SAAS,CAAC;IAEnE,uDAAuD;IACvD,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAE5C,wDAAwD;IACxD,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE9C,8EAA8E;IAC9E,QAAQ,CAAC,qBAAqB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAErD,wDAAwD;IACxD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAmBD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGjD;AAED;;GAEG;AACH,qBAAa,iBAAkB,YAAW,SAAS;IACjD,QAAQ,CAAC,IAAI,cAAc;IAE3B,OAAO,CAAC,aAAa,CAQnB;IAEF,IAAI,YAAY,IAAI,qBAAqB,CAExC;IAED,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAoB;IACtD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAmC;IACjE,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuB;IAE7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAIrB;IAEF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,wBAAwB,CAAS;gBAE7B,MAAM,EAAE,uBAAuB;IAkBrC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIjC,WAAW,IAAI,OAAO;IAIhB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA0ChD,IAAI,CACR,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IA0DvB,SAAS,CACb,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,YAAY,CAAC;IAwFlB,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAahD,gBAAgB,CACpB,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;IA6ChB;;;;;OAKG;YACW,uBAAuB;YAqCvB,SAAS;YAqCT,YAAY;YA+CZ,oBAAoB;
|
|
1
|
+
{"version":3,"file":"rabbitmq-transport.d.ts","sourceRoot":"","sources":["../../../src/transport/rabbitmq/rabbitmq-transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -406,41 +406,34 @@ export class RabbitMQTransport {
|
|
|
406
406
|
});
|
|
407
407
|
});
|
|
408
408
|
}
|
|
409
|
+
buildWorkQueueOptions(topology, queueDef) {
|
|
410
|
+
const queueOptions = {
|
|
411
|
+
durable: true,
|
|
412
|
+
arguments: {},
|
|
413
|
+
};
|
|
414
|
+
if (this.config.quorumQueues && !queueDef.exact) {
|
|
415
|
+
queueOptions.arguments['x-queue-type'] = 'quorum';
|
|
416
|
+
}
|
|
417
|
+
if (topology.deadLetter.unhandled.enabled ||
|
|
418
|
+
topology.deadLetter.undeliverable.enabled) {
|
|
419
|
+
queueOptions.arguments['x-dead-letter-exchange'] =
|
|
420
|
+
this.getDLXExchangeName(topology.namespace);
|
|
421
|
+
}
|
|
422
|
+
if (queueDef.priorities) {
|
|
423
|
+
queueOptions.arguments['x-max-priority'] = 10;
|
|
424
|
+
}
|
|
425
|
+
if (queueDef.consumerTimeout) {
|
|
426
|
+
queueOptions.arguments['x-consumer-timeout'] = queueDef.consumerTimeout;
|
|
427
|
+
}
|
|
428
|
+
return queueOptions;
|
|
429
|
+
}
|
|
409
430
|
async assertWorkQueue(channel, topology, queueDef) {
|
|
410
431
|
const queueName = queueDef.exact
|
|
411
432
|
? queueDef.name
|
|
412
433
|
: `${topology.namespace}.${queueDef.name}`;
|
|
413
434
|
const rabbitmqOptions = queueDef.transport?.rabbitmq?.options;
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
await channel.assertQueue(queueName, rabbitmqOptions);
|
|
417
|
-
}
|
|
418
|
-
else {
|
|
419
|
-
// Use computed defaults
|
|
420
|
-
const queueOptions = {
|
|
421
|
-
durable: true,
|
|
422
|
-
arguments: {},
|
|
423
|
-
};
|
|
424
|
-
// Use quorum queues for durability
|
|
425
|
-
if (this.config.quorumQueues && !queueDef.exact) {
|
|
426
|
-
queueOptions.arguments['x-queue-type'] = 'quorum';
|
|
427
|
-
}
|
|
428
|
-
// Set up dead-letter exchange routing
|
|
429
|
-
const dlxExchange = this.getDLXExchangeName(topology.namespace);
|
|
430
|
-
if (topology.deadLetter.unhandled.enabled ||
|
|
431
|
-
topology.deadLetter.undeliverable.enabled) {
|
|
432
|
-
queueOptions.arguments['x-dead-letter-exchange'] = dlxExchange;
|
|
433
|
-
}
|
|
434
|
-
// Enable priority if requested
|
|
435
|
-
if (queueDef.priorities) {
|
|
436
|
-
queueOptions.arguments['x-max-priority'] = 10;
|
|
437
|
-
}
|
|
438
|
-
// Set consumer timeout if specified
|
|
439
|
-
if (queueDef.consumerTimeout) {
|
|
440
|
-
queueOptions.arguments['x-consumer-timeout'] = queueDef.consumerTimeout;
|
|
441
|
-
}
|
|
442
|
-
await channel.assertQueue(queueName, queueOptions);
|
|
443
|
-
}
|
|
435
|
+
const queueOptions = rabbitmqOptions ?? this.buildWorkQueueOptions(topology, queueDef);
|
|
436
|
+
await channel.assertQueue(queueName, queueOptions);
|
|
444
437
|
// Bind queue to main exchange
|
|
445
438
|
const mainExchange = this.getMainExchangeName(topology.namespace);
|
|
446
439
|
await channel.bindQueue(queueName, mainExchange, queueName);
|