@zdavison/matador 2.0.2 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/matador.d.ts +2 -2
- package/dist/core/matador.d.ts.map +1 -1
- package/dist/core/matador.js +6 -0
- package/dist/index.cjs +80 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pipeline/pipeline.d.ts +4 -1
- package/dist/pipeline/pipeline.d.ts.map +1 -1
- package/dist/pipeline/pipeline.js +16 -4
- package/dist/pipeline/pipeline.test.js +15 -1
- package/dist/topology/builder.d.ts +14 -0
- package/dist/topology/builder.d.ts.map +1 -1
- package/dist/topology/builder.js +20 -11
- package/dist/topology/builder.test.js +138 -0
- package/dist/topology/index.d.ts +2 -2
- package/dist/topology/index.d.ts.map +1 -1
- package/dist/topology/index.js +1 -1
- package/dist/topology/types.d.ts +53 -1
- package/dist/topology/types.d.ts.map +1 -1
- package/dist/topology/types.js +10 -0
- package/dist/transport/local/local-transport.d.ts.map +1 -1
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts +8 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -1
- package/dist/transport/rabbitmq/rabbitmq-transport.js +42 -22
- package/dist/transport/rabbitmq/rabbitmq-transport.test.d.ts +2 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.test.d.ts.map +1 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.test.js +98 -0
- package/dist/types/dispatcher.d.ts +14 -0
- package/dist/types/dispatcher.d.ts.map +1 -0
- package/dist/types/dispatcher.js +1 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/subscriber.d.ts +20 -4
- package/dist/types/subscriber.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/matador.ts +14 -1
- package/src/index.ts +9 -0
- package/src/pipeline/pipeline.test.ts +24 -2
- package/src/pipeline/pipeline.ts +28 -6
- package/src/topology/builder.test.ts +176 -0
- package/src/topology/builder.ts +44 -5
- package/src/topology/index.ts +4 -0
- package/src/topology/types.ts +75 -1
- package/src/transport/local/local-transport.ts +4 -1
- package/src/transport/rabbitmq/rabbitmq-transport.test.ts +118 -0
- package/src/transport/rabbitmq/rabbitmq-transport.ts +48 -25
- package/src/types/dispatcher.ts +18 -0
- package/src/types/index.ts +4 -0
- package/src/types/subscriber.ts +22 -3
- package/test/e2e/rabbitmq-transport.e2e.test.ts +287 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -4,7 +4,7 @@ import type { SafeHooks } from '../hooks/index.js';
|
|
|
4
4
|
import type { RetryDecision, RetryPolicy } from '../retry/index.js';
|
|
5
5
|
import type { SchemaRegistry } from '../schema/index.js';
|
|
6
6
|
import type { MessageReceipt, Transport } from '../transport/index.js';
|
|
7
|
-
import type { Envelope, SubscriberDefinition } from '../types/index.js';
|
|
7
|
+
import type { Dispatcher, Envelope, SubscriberDefinition } from '../types/index.js';
|
|
8
8
|
/**
|
|
9
9
|
* Configuration for the processing pipeline.
|
|
10
10
|
*/
|
|
@@ -16,6 +16,8 @@ export interface PipelineConfig {
|
|
|
16
16
|
readonly hooks: SafeHooks;
|
|
17
17
|
/** Optional checkpoint store for resumable subscribers */
|
|
18
18
|
readonly checkpointStore?: CheckpointStore | undefined;
|
|
19
|
+
/** Dispatcher (matador) for sending events from subscriber callbacks */
|
|
20
|
+
readonly dispatcher: Dispatcher;
|
|
19
21
|
}
|
|
20
22
|
/**
|
|
21
23
|
* Result of pipeline processing.
|
|
@@ -44,6 +46,7 @@ export declare class ProcessingPipeline {
|
|
|
44
46
|
private readonly retryPolicy;
|
|
45
47
|
private readonly hooks;
|
|
46
48
|
private readonly checkpointStore;
|
|
49
|
+
private readonly dispatcher;
|
|
47
50
|
constructor(config: PipelineConfig);
|
|
48
51
|
/**
|
|
49
52
|
* Processes a raw message from the transport.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/pipeline/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE1E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAM/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/pipeline/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE1E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAM/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,KAAK,EAEV,UAAU,EACV,QAAQ,EAER,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAG3B;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,0DAA0D;IAC1D,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IACvD,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC9C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;gBAE5B,MAAM,EAAE,cAAc;IAUlC;;OAEG;IACG,OAAO,CACX,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC;YA4NX,mBAAmB;YAqCnB,gBAAgB;IAkB9B;;;OAGG;IACH,OAAO,CAAC,yBAAyB;CA+BlC"}
|
|
@@ -18,6 +18,7 @@ export class ProcessingPipeline {
|
|
|
18
18
|
retryPolicy;
|
|
19
19
|
hooks;
|
|
20
20
|
checkpointStore;
|
|
21
|
+
dispatcher;
|
|
21
22
|
constructor(config) {
|
|
22
23
|
this.transport = config.transport;
|
|
23
24
|
this.schema = config.schema;
|
|
@@ -25,6 +26,7 @@ export class ProcessingPipeline {
|
|
|
25
26
|
this.retryPolicy = config.retryPolicy;
|
|
26
27
|
this.hooks = config.hooks;
|
|
27
28
|
this.checkpointStore = config.checkpointStore ?? new NoOpCheckpointStore();
|
|
29
|
+
this.dispatcher = config.dispatcher;
|
|
28
30
|
}
|
|
29
31
|
/**
|
|
30
32
|
* Processes a raw message from the transport.
|
|
@@ -113,19 +115,29 @@ export class ProcessingPipeline {
|
|
|
113
115
|
},
|
|
114
116
|
});
|
|
115
117
|
}
|
|
118
|
+
// Create base callback context with matador dispatcher
|
|
119
|
+
const callbackContext = { matador: this.dispatcher };
|
|
116
120
|
await this.hooks.onWorkerWrap(envelope, subscriberDef, async () => {
|
|
117
121
|
await this.hooks.onWorkerBeforeProcess(envelope, subscriberDef);
|
|
118
122
|
try {
|
|
119
123
|
if (isResumable && context) {
|
|
120
|
-
// Resumable subscriber: pass context
|
|
124
|
+
// Resumable subscriber: pass combined context (checkpoint + matador)
|
|
121
125
|
// Cast needed because TypeScript can't narrow the union type based on isResumable
|
|
122
126
|
const resumableCallback = subscriber.callback;
|
|
123
|
-
|
|
127
|
+
// Create combined context by binding class methods and adding matador
|
|
128
|
+
const fullContext = {
|
|
129
|
+
io: context.io.bind(context),
|
|
130
|
+
all: context.all.bind(context),
|
|
131
|
+
attempt: context.attempt,
|
|
132
|
+
isRetry: context.isRetry,
|
|
133
|
+
matador: this.dispatcher,
|
|
134
|
+
};
|
|
135
|
+
result = await resumableCallback(envelope, fullContext);
|
|
124
136
|
}
|
|
125
137
|
else {
|
|
126
|
-
// Standard subscriber:
|
|
138
|
+
// Standard subscriber: pass envelope and callback context
|
|
127
139
|
const standardCallback = subscriber.callback;
|
|
128
|
-
result = await standardCallback(envelope);
|
|
140
|
+
result = await standardCallback(envelope, callbackContext);
|
|
129
141
|
}
|
|
130
142
|
}
|
|
131
143
|
catch (e) {
|
|
@@ -255,7 +255,11 @@ describe('ProcessingPipeline', () => {
|
|
|
255
255
|
const receipt = createReceipt();
|
|
256
256
|
const result = await pipeline.process(new Uint8Array(), receipt);
|
|
257
257
|
expect(result.success).toBe(true);
|
|
258
|
-
expect(callbackMock).
|
|
258
|
+
expect(callbackMock).toHaveBeenCalledTimes(1);
|
|
259
|
+
// Callback receives envelope as first argument and context (with matador) as second
|
|
260
|
+
const calls = callbackMock.mock.calls;
|
|
261
|
+
expect(calls[0][0]).toEqual(envelope);
|
|
262
|
+
expect(calls[0][1]).toHaveProperty('matador');
|
|
259
263
|
});
|
|
260
264
|
it('should handle callback throwing error', async () => {
|
|
261
265
|
const envelope = createTestEnvelope();
|
|
@@ -1027,12 +1031,22 @@ function createMockConfig(overrides = {}) {
|
|
|
1027
1031
|
onDecodeError: async () => { },
|
|
1028
1032
|
...overrides.hooks,
|
|
1029
1033
|
};
|
|
1034
|
+
const defaultDispatcher = {
|
|
1035
|
+
send: mock(async () => ({
|
|
1036
|
+
eventKey: 'test.event',
|
|
1037
|
+
subscribersSent: 0,
|
|
1038
|
+
subscribersSkipped: 0,
|
|
1039
|
+
errors: [],
|
|
1040
|
+
})),
|
|
1041
|
+
...overrides.dispatcher,
|
|
1042
|
+
};
|
|
1030
1043
|
return {
|
|
1031
1044
|
transport: defaultTransport,
|
|
1032
1045
|
schema: { ...defaultSchema, ...overrides.schema },
|
|
1033
1046
|
codec: defaultCodec,
|
|
1034
1047
|
retryPolicy: defaultRetryPolicy,
|
|
1035
1048
|
hooks: defaultHooks,
|
|
1049
|
+
dispatcher: defaultDispatcher,
|
|
1036
1050
|
};
|
|
1037
1051
|
}
|
|
1038
1052
|
function createTestEnvelope() {
|
|
@@ -31,10 +31,24 @@ export declare class TopologyBuilder {
|
|
|
31
31
|
withNamespace(namespace: string): this;
|
|
32
32
|
/**
|
|
33
33
|
* Adds a queue to the topology.
|
|
34
|
+
* @param definition - A complete QueueDefinition object
|
|
35
|
+
*/
|
|
36
|
+
addQueue(definition: QueueDefinition): this;
|
|
37
|
+
/**
|
|
38
|
+
* Adds a queue to the topology.
|
|
39
|
+
* @param name - Queue name
|
|
40
|
+
* @param options - Queue options
|
|
34
41
|
*/
|
|
35
42
|
addQueue(name: string, options?: QueueOptions): this;
|
|
36
43
|
/**
|
|
37
44
|
* Alias for addQueue().
|
|
45
|
+
* @param definition - A complete QueueDefinition object
|
|
46
|
+
*/
|
|
47
|
+
queue(definition: QueueDefinition): this;
|
|
48
|
+
/**
|
|
49
|
+
* Alias for addQueue().
|
|
50
|
+
* @param name - Queue name
|
|
51
|
+
* @param options - Queue options
|
|
38
52
|
*/
|
|
39
53
|
queue(name: string, options?: QueueOptions): this;
|
|
40
54
|
/**
|
|
@@ -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;
|
|
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;IAuD7B;;;OAGG;IACH,KAAK,IAAI,QAAQ;CAgBlB"}
|
package/dist/topology/builder.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guard to check if the argument is a QueueDefinition object.
|
|
3
|
+
*/
|
|
4
|
+
function isQueueDefinition(arg) {
|
|
5
|
+
return typeof arg === 'object' && arg !== null && 'name' in arg;
|
|
6
|
+
}
|
|
1
7
|
/**
|
|
2
8
|
* Error thrown when topology validation fails.
|
|
3
9
|
*/
|
|
@@ -41,18 +47,20 @@ export class TopologyBuilder {
|
|
|
41
47
|
this.namespace = namespace;
|
|
42
48
|
return this;
|
|
43
49
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
addQueue(nameOrDefinition, options = {}) {
|
|
51
|
+
if (isQueueDefinition(nameOrDefinition)) {
|
|
52
|
+
this.queues.push(nameOrDefinition);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
this.queues.push({ name: nameOrDefinition, ...options });
|
|
56
|
+
}
|
|
49
57
|
return this;
|
|
50
58
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return this.addQueue(
|
|
59
|
+
queue(nameOrDefinition, options = {}) {
|
|
60
|
+
if (isQueueDefinition(nameOrDefinition)) {
|
|
61
|
+
return this.addQueue(nameOrDefinition);
|
|
62
|
+
}
|
|
63
|
+
return this.addQueue(nameOrDefinition, options);
|
|
56
64
|
}
|
|
57
65
|
/**
|
|
58
66
|
* Configures dead-letter queue settings.
|
|
@@ -111,7 +119,8 @@ export class TopologyBuilder {
|
|
|
111
119
|
if (!queue.name || queue.name.trim() === '') {
|
|
112
120
|
issues.push('Queue name cannot be empty');
|
|
113
121
|
}
|
|
114
|
-
else if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(queue.name)) {
|
|
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')
|
|
115
124
|
issues.push(`Queue name "${queue.name}" must start with a letter and contain only alphanumeric characters, underscores, and hyphens`);
|
|
116
125
|
}
|
|
117
126
|
else if (queueNames.has(queue.name)) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from 'bun:test';
|
|
2
2
|
import { TopologyBuilder, TopologyValidationError } from './builder.js';
|
|
3
|
+
import { resolveQueueName } from './types.js';
|
|
3
4
|
describe('TopologyBuilder', () => {
|
|
4
5
|
describe('withNamespace', () => {
|
|
5
6
|
it('should set the namespace', () => {
|
|
@@ -218,5 +219,142 @@ describe('TopologyBuilder', () => {
|
|
|
218
219
|
.build();
|
|
219
220
|
expect(topology.queues[0]?.exact).toBe(true);
|
|
220
221
|
});
|
|
222
|
+
it('should allow dots in queue name when exact: true', () => {
|
|
223
|
+
const topology = TopologyBuilder.create()
|
|
224
|
+
.withNamespace('test')
|
|
225
|
+
.addQueue('matador.shared.id-platform', { exact: true })
|
|
226
|
+
.build();
|
|
227
|
+
expect(topology.queues[0]?.name).toBe('matador.shared.id-platform');
|
|
228
|
+
expect(topology.queues[0]?.exact).toBe(true);
|
|
229
|
+
});
|
|
230
|
+
it('should reject dots in queue name when exact: false', () => {
|
|
231
|
+
const builder = TopologyBuilder.create()
|
|
232
|
+
.withNamespace('test')
|
|
233
|
+
.addQueue('invalid.queue.name');
|
|
234
|
+
expect(() => builder.build()).toThrow('must start with a letter');
|
|
235
|
+
});
|
|
236
|
+
it('should allow transport-specific RabbitMQ options with exact queue', () => {
|
|
237
|
+
const topology = TopologyBuilder.create()
|
|
238
|
+
.withNamespace('test')
|
|
239
|
+
.addQueue('matador.shared.id-platform', {
|
|
240
|
+
exact: true,
|
|
241
|
+
transport: {
|
|
242
|
+
rabbitmq: {
|
|
243
|
+
options: {
|
|
244
|
+
durable: true,
|
|
245
|
+
deadLetterExchange: 'matador.shared.dlx-undeliverable',
|
|
246
|
+
arguments: {
|
|
247
|
+
'x-queue-type': 'quorum',
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
})
|
|
253
|
+
.build();
|
|
254
|
+
expect(topology.queues[0]?.name).toBe('matador.shared.id-platform');
|
|
255
|
+
expect(topology.queues[0]?.exact).toBe(true);
|
|
256
|
+
expect(topology.queues[0]?.transport?.rabbitmq?.options?.durable).toBe(true);
|
|
257
|
+
expect(topology.queues[0]?.transport?.rabbitmq?.options?.deadLetterExchange).toBe('matador.shared.dlx-undeliverable');
|
|
258
|
+
expect(topology.queues[0]?.transport?.rabbitmq?.options?.arguments?.['x-queue-type']).toBe('quorum');
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
describe('addQueue with QueueDefinition object', () => {
|
|
262
|
+
it('should accept a QueueDefinition object', () => {
|
|
263
|
+
const queueDef = {
|
|
264
|
+
name: 'events',
|
|
265
|
+
concurrency: 5,
|
|
266
|
+
consumerTimeout: 30000,
|
|
267
|
+
};
|
|
268
|
+
const topology = TopologyBuilder.create()
|
|
269
|
+
.withNamespace('test')
|
|
270
|
+
.addQueue(queueDef)
|
|
271
|
+
.build();
|
|
272
|
+
expect(topology.queues).toHaveLength(1);
|
|
273
|
+
expect(topology.queues[0]?.name).toBe('events');
|
|
274
|
+
expect(topology.queues[0]?.concurrency).toBe(5);
|
|
275
|
+
expect(topology.queues[0]?.consumerTimeout).toBe(30000);
|
|
276
|
+
});
|
|
277
|
+
it('should allow reusing queue definitions across builders', () => {
|
|
278
|
+
const sharedQueue = {
|
|
279
|
+
name: 'shared-queue',
|
|
280
|
+
concurrency: 10,
|
|
281
|
+
exact: true,
|
|
282
|
+
};
|
|
283
|
+
const topology1 = TopologyBuilder.create()
|
|
284
|
+
.withNamespace('app1')
|
|
285
|
+
.addQueue(sharedQueue)
|
|
286
|
+
.build();
|
|
287
|
+
const topology2 = TopologyBuilder.create()
|
|
288
|
+
.withNamespace('app2')
|
|
289
|
+
.addQueue(sharedQueue)
|
|
290
|
+
.build();
|
|
291
|
+
expect(topology1.queues[0]).toEqual(sharedQueue);
|
|
292
|
+
expect(topology2.queues[0]).toEqual(sharedQueue);
|
|
293
|
+
});
|
|
294
|
+
it('should work with queue() alias', () => {
|
|
295
|
+
const queueDef = {
|
|
296
|
+
name: 'analytics',
|
|
297
|
+
priorities: true,
|
|
298
|
+
};
|
|
299
|
+
const topology = TopologyBuilder.create()
|
|
300
|
+
.withNamespace('test')
|
|
301
|
+
.queue(queueDef)
|
|
302
|
+
.build();
|
|
303
|
+
expect(topology.queues[0]?.name).toBe('analytics');
|
|
304
|
+
expect(topology.queues[0]?.priorities).toBe(true);
|
|
305
|
+
});
|
|
306
|
+
it('should mix QueueDefinition objects with name+options style', () => {
|
|
307
|
+
const reusableQueue = {
|
|
308
|
+
name: 'shared',
|
|
309
|
+
concurrency: 3,
|
|
310
|
+
};
|
|
311
|
+
const topology = TopologyBuilder.create()
|
|
312
|
+
.withNamespace('test')
|
|
313
|
+
.addQueue(reusableQueue)
|
|
314
|
+
.addQueue('local', { concurrency: 1 })
|
|
315
|
+
.build();
|
|
316
|
+
expect(topology.queues).toHaveLength(2);
|
|
317
|
+
expect(topology.queues[0]?.name).toBe('shared');
|
|
318
|
+
expect(topology.queues[1]?.name).toBe('local');
|
|
319
|
+
});
|
|
320
|
+
it('should validate QueueDefinition objects the same way', () => {
|
|
321
|
+
const invalidQueue = {
|
|
322
|
+
name: '123invalid',
|
|
323
|
+
concurrency: 5,
|
|
324
|
+
};
|
|
325
|
+
const builder = TopologyBuilder.create()
|
|
326
|
+
.withNamespace('test')
|
|
327
|
+
.addQueue(invalidQueue);
|
|
328
|
+
expect(() => builder.build()).toThrow('must start with a letter');
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
describe('resolveQueueName', () => {
|
|
333
|
+
it('should return namespace.name for regular queues', () => {
|
|
334
|
+
const queueDef = { name: 'events' };
|
|
335
|
+
expect(resolveQueueName('myapp', queueDef)).toBe('myapp.events');
|
|
336
|
+
});
|
|
337
|
+
it('should return name as-is when exact: true', () => {
|
|
338
|
+
const queueDef = { name: 'matador.shared.id-platform', exact: true };
|
|
339
|
+
expect(resolveQueueName('myapp', queueDef)).toBe('matador.shared.id-platform');
|
|
340
|
+
});
|
|
341
|
+
it('should return namespace.name when exact: false', () => {
|
|
342
|
+
const queueDef = { name: 'events', exact: false };
|
|
343
|
+
expect(resolveQueueName('myapp', queueDef)).toBe('myapp.events');
|
|
344
|
+
});
|
|
345
|
+
it('should work with full QueueDefinition including transport options', () => {
|
|
346
|
+
const queueDef = {
|
|
347
|
+
name: 'matador.shared.id-platform',
|
|
348
|
+
exact: true,
|
|
349
|
+
transport: {
|
|
350
|
+
rabbitmq: {
|
|
351
|
+
options: {
|
|
352
|
+
durable: true,
|
|
353
|
+
deadLetterExchange: 'matador.shared.dlx-undeliverable',
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
expect(resolveQueueName('myapp', queueDef)).toBe('matador.shared.id-platform');
|
|
221
359
|
});
|
|
222
360
|
});
|
package/dist/topology/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type { DeadLetterConfig, DeadLetterQueueConfig, QueueDefinition, RetryConfig, Topology, } from './types.js';
|
|
2
|
-
export { getDeadLetterQueueName, getQualifiedQueueName, getRetryQueueName, } from './types.js';
|
|
1
|
+
export type { DeadLetterConfig, DeadLetterQueueConfig, QueueDefinition, RabbitMQQueueDefinition, RabbitMQQueueOptions, RetryConfig, Topology, TransportQueueOptions, } from './types.js';
|
|
2
|
+
export { getDeadLetterQueueName, getQualifiedQueueName, getRetryQueueName, resolveQueueName, } from './types.js';
|
|
3
3
|
export type { QueueOptions } from './builder.js';
|
|
4
4
|
export { TopologyBuilder, TopologyValidationError } from './builder.js';
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/topology/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,eAAe,EACf,WAAW,EACX,QAAQ,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/topology/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,eAAe,EACf,uBAAuB,EACvB,oBAAoB,EACpB,WAAW,EACX,QAAQ,EACR,qBAAqB,GACtB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/topology/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { getDeadLetterQueueName, getQualifiedQueueName, getRetryQueueName, } from './types.js';
|
|
1
|
+
export { getDeadLetterQueueName, getQualifiedQueueName, getRetryQueueName, resolveQueueName, } from './types.js';
|
|
2
2
|
export { TopologyBuilder, TopologyValidationError } from './builder.js';
|
package/dist/topology/types.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export interface Topology {
|
|
|
16
16
|
* Individual queue definition.
|
|
17
17
|
*/
|
|
18
18
|
export interface QueueDefinition {
|
|
19
|
-
/** Queue name (will be prefixed with namespace) */
|
|
19
|
+
/** Queue name (will be prefixed with namespace unless exact: true) */
|
|
20
20
|
readonly name: string;
|
|
21
21
|
/** Concurrency for this queue */
|
|
22
22
|
readonly concurrency?: number | undefined;
|
|
@@ -31,6 +31,53 @@ export interface QueueDefinition {
|
|
|
31
31
|
* queues that are not managed by Matador.
|
|
32
32
|
*/
|
|
33
33
|
readonly exact?: boolean | undefined;
|
|
34
|
+
/** Transport-specific queue options */
|
|
35
|
+
readonly transport?: TransportQueueOptions | undefined;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Transport-specific queue options.
|
|
39
|
+
* Each transport can define its own options under its transport name key.
|
|
40
|
+
*/
|
|
41
|
+
export interface TransportQueueOptions {
|
|
42
|
+
/** RabbitMQ-specific queue options */
|
|
43
|
+
readonly rabbitmq?: RabbitMQQueueDefinition | undefined;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* RabbitMQ-specific queue definition options.
|
|
47
|
+
*/
|
|
48
|
+
export interface RabbitMQQueueDefinition {
|
|
49
|
+
/**
|
|
50
|
+
* Exact RabbitMQ queue assertion options.
|
|
51
|
+
* When provided, these options completely replace all auto-computed defaults
|
|
52
|
+
* (durable, x-queue-type, x-dead-letter-exchange, etc.).
|
|
53
|
+
*/
|
|
54
|
+
readonly options?: RabbitMQQueueOptions | undefined;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* RabbitMQ queue assertion options.
|
|
58
|
+
* Maps to amqplib's Options.AssertQueue.
|
|
59
|
+
*/
|
|
60
|
+
export interface RabbitMQQueueOptions {
|
|
61
|
+
/** Queue survives broker restart */
|
|
62
|
+
readonly durable?: boolean | undefined;
|
|
63
|
+
/** Queue is deleted when last consumer unsubscribes */
|
|
64
|
+
readonly autoDelete?: boolean | undefined;
|
|
65
|
+
/** Queue can only be used by the declaring connection */
|
|
66
|
+
readonly exclusive?: boolean | undefined;
|
|
67
|
+
/** Exchange to which dead-lettered messages are sent */
|
|
68
|
+
readonly deadLetterExchange?: string | undefined;
|
|
69
|
+
/** Routing key for dead-lettered messages */
|
|
70
|
+
readonly deadLetterRoutingKey?: string | undefined;
|
|
71
|
+
/** Message TTL in milliseconds */
|
|
72
|
+
readonly messageTtl?: number | undefined;
|
|
73
|
+
/** Queue expires after this many milliseconds of non-use */
|
|
74
|
+
readonly expires?: number | undefined;
|
|
75
|
+
/** Maximum number of messages in the queue */
|
|
76
|
+
readonly maxLength?: number | undefined;
|
|
77
|
+
/** Maximum priority level (0-255) */
|
|
78
|
+
readonly maxPriority?: number | undefined;
|
|
79
|
+
/** Additional x-* arguments for RabbitMQ */
|
|
80
|
+
readonly arguments?: Record<string, unknown> | undefined;
|
|
34
81
|
}
|
|
35
82
|
/**
|
|
36
83
|
* Dead-letter queue configuration.
|
|
@@ -73,4 +120,9 @@ export declare function getDeadLetterQueueName(namespace: string, queueName: str
|
|
|
73
120
|
* Gets the retry queue name for a given queue.
|
|
74
121
|
*/
|
|
75
122
|
export declare function getRetryQueueName(namespace: string, queueName: string): string;
|
|
123
|
+
/**
|
|
124
|
+
* Resolves the actual queue name for a given queue definition.
|
|
125
|
+
* When exact: true, returns name as-is. Otherwise, returns namespace.name.
|
|
126
|
+
*/
|
|
127
|
+
export declare function resolveQueueName(namespace: string, queueDef: QueueDefinition): string;
|
|
76
128
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/topology/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,sCAAsC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,wCAAwC;IACxC,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;IAE5C,sCAAsC;IACtC,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;IAEtC,gCAAgC;IAChC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/topology/types.ts"],"names":[],"mappings":"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"}
|
package/dist/topology/types.js
CHANGED
|
@@ -16,3 +16,13 @@ export function getDeadLetterQueueName(namespace, queueName, dlqType) {
|
|
|
16
16
|
export function getRetryQueueName(namespace, queueName) {
|
|
17
17
|
return `${namespace}.${queueName}.retry`;
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolves the actual queue name for a given queue definition.
|
|
21
|
+
* When exact: true, returns name as-is. Otherwise, returns namespace.name.
|
|
22
|
+
*/
|
|
23
|
+
export function resolveQueueName(namespace, queueDef) {
|
|
24
|
+
if (queueDef.exact) {
|
|
25
|
+
return queueDef.name;
|
|
26
|
+
}
|
|
27
|
+
return `${namespace}.${queueDef.name}`;
|
|
28
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-transport.d.ts","sourceRoot":"","sources":["../../../src/transport/local/local-transport.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,sBAAsB,CAAC;AAClE,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;AAiCzB;;;GAGG;AACH,qBAAa,cAAe,YAAW,SAAS;IAC9C,QAAQ,CAAC,IAAI,WAAW;IACxB,QAAQ,CAAC,YAAY,wBAAqB;IAE1C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsC;IAC7D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA2C;IACzE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAwB;IAC1D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA4C;IAC1E,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,MAAM,CAAC,EAAE,MAAM;IAIrB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBjC,WAAW,IAAI,OAAO;IAIhB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBhD,IAAI,CACR,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAe7B;;;OAGG;IACH,OAAO,CAAC,sBAAsB;YAkBhB,OAAO;YAgBP,oBAAoB;
|
|
1
|
+
{"version":3,"file":"local-transport.d.ts","sourceRoot":"","sources":["../../../src/transport/local/local-transport.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,sBAAsB,CAAC;AAClE,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;AAiCzB;;;GAGG;AACH,qBAAa,cAAe,YAAW,SAAS;IAC9C,QAAQ,CAAC,IAAI,WAAW;IACxB,QAAQ,CAAC,YAAY,wBAAqB;IAE1C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsC;IAC7D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA2C;IACzE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAwB;IAC1D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA4C;IAC1E,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,MAAM,CAAC,EAAE,MAAM;IAIrB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBjC,WAAW,IAAI,OAAO;IAIhB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBhD,IAAI,CACR,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAe7B;;;OAGG;IACH,OAAO,CAAC,sBAAsB;YAkBhB,OAAO;YAgBP,oBAAoB;IA+B5B,SAAS,CACb,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,YAAY,CAAC;IAsClB,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAMhD,gBAAgB,CACpB,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC;IAQhB;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAMnC;;OAEG;IACH,YAAY,IAAI,SAAS,cAAc,EAAE;IAIzC;;OAEG;IACH,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,QAAQ,EAAE;IAMtD;;OAEG;IACH,KAAK,IAAI,IAAI;IAYb;;;OAGG;IACG,UAAU,CACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,QAAQ,EAAE,QAAQ,CAAC;QAAC,OAAO,EAAE,cAAc,CAAA;KAAE,GAAG,IAAI,CAAC;IAmBlE,OAAO,CAAC,gBAAgB;CAQzB"}
|
|
@@ -21,6 +21,14 @@ export interface RabbitMQTransportConfig {
|
|
|
21
21
|
/** Logger for transport events (defaults to console) */
|
|
22
22
|
readonly logger?: Logger | undefined;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Redacts credentials from an AMQP URL.
|
|
26
|
+
* Replaces username and password with '****' regardless of their length.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* redactAmqpUrl('amqp://user:pass@host:5672') // 'amqp://****:****@host:5672'
|
|
30
|
+
*/
|
|
31
|
+
export declare function redactAmqpUrl(url: string): string;
|
|
24
32
|
/**
|
|
25
33
|
* RabbitMQ transport implementation using amqplib.
|
|
26
34
|
*/
|
|
@@ -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,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;;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;IAiBrC,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;
|
|
1
|
+
{"version":3,"file":"rabbitmq-transport.d.ts","sourceRoot":"","sources":["../../../src/transport/rabbitmq/rabbitmq-transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,17 @@ import { RabbitMQCodec } from '../../codec/rabbitmq-codec.js';
|
|
|
3
3
|
import { DelayedMessagesNotSupportedError, TransportNotConnectedError, } from '../../errors/index.js';
|
|
4
4
|
import { consoleLogger } from '../../hooks/index.js';
|
|
5
5
|
import { ConnectionManager, } from '../connection-manager.js';
|
|
6
|
+
/**
|
|
7
|
+
* Redacts credentials from an AMQP URL.
|
|
8
|
+
* Replaces username and password with '****' regardless of their length.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* redactAmqpUrl('amqp://user:pass@host:5672') // 'amqp://****:****@host:5672'
|
|
12
|
+
*/
|
|
13
|
+
export function redactAmqpUrl(url) {
|
|
14
|
+
const regex = /^(amqps?:\/\/)[^:]+:[^@]+@/;
|
|
15
|
+
return url.replace(regex, '$1****:****@');
|
|
16
|
+
}
|
|
6
17
|
/**
|
|
7
18
|
* RabbitMQ transport implementation using amqplib.
|
|
8
19
|
*/
|
|
@@ -260,6 +271,7 @@ export class RabbitMQTransport {
|
|
|
260
271
|
return queueChannel;
|
|
261
272
|
}
|
|
262
273
|
async doConnect() {
|
|
274
|
+
this.logger.info(`[Matador] ⏳ Connecting to RabbitMQ at '${redactAmqpUrl(this.config.url)}'.`);
|
|
263
275
|
const connection = await amqplib.connect(this.config.url);
|
|
264
276
|
this.connection = connection;
|
|
265
277
|
// Handle connection errors - let ConnectionManager handle reconnection
|
|
@@ -395,29 +407,37 @@ export class RabbitMQTransport {
|
|
|
395
407
|
const queueName = queueDef.exact
|
|
396
408
|
? queueDef.name
|
|
397
409
|
: `${topology.namespace}.${queueDef.name}`;
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
410
|
+
const rabbitmqOptions = queueDef.transport?.rabbitmq?.options;
|
|
411
|
+
// If user provided exact RabbitMQ options, use them directly (replaces all defaults)
|
|
412
|
+
if (rabbitmqOptions) {
|
|
413
|
+
await channel.assertQueue(queueName, rabbitmqOptions);
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
// Use computed defaults
|
|
417
|
+
const queueOptions = {
|
|
418
|
+
durable: true,
|
|
419
|
+
arguments: {},
|
|
420
|
+
};
|
|
421
|
+
// Use quorum queues for durability
|
|
422
|
+
if (this.config.quorumQueues && !queueDef.exact) {
|
|
423
|
+
queueOptions.arguments['x-queue-type'] = 'quorum';
|
|
424
|
+
}
|
|
425
|
+
// Set up dead-letter exchange routing
|
|
426
|
+
const dlxExchange = this.getDLXExchangeName(topology.namespace);
|
|
427
|
+
if (topology.deadLetter.unhandled.enabled ||
|
|
428
|
+
topology.deadLetter.undeliverable.enabled) {
|
|
429
|
+
queueOptions.arguments['x-dead-letter-exchange'] = dlxExchange;
|
|
430
|
+
}
|
|
431
|
+
// Enable priority if requested
|
|
432
|
+
if (queueDef.priorities) {
|
|
433
|
+
queueOptions.arguments['x-max-priority'] = 10;
|
|
434
|
+
}
|
|
435
|
+
// Set consumer timeout if specified
|
|
436
|
+
if (queueDef.consumerTimeout) {
|
|
437
|
+
queueOptions.arguments['x-consumer-timeout'] = queueDef.consumerTimeout;
|
|
438
|
+
}
|
|
439
|
+
await channel.assertQueue(queueName, queueOptions);
|
|
419
440
|
}
|
|
420
|
-
await channel.assertQueue(queueName, queueOptions);
|
|
421
441
|
// Bind queue to main exchange
|
|
422
442
|
const mainExchange = this.getMainExchangeName(topology.namespace);
|
|
423
443
|
await channel.bindQueue(queueName, mainExchange, queueName);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rabbitmq-transport.test.d.ts","sourceRoot":"","sources":["../../../src/transport/rabbitmq/rabbitmq-transport.test.ts"],"names":[],"mappings":""}
|