@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.
Files changed (55) hide show
  1. package/dist/core/matador.d.ts +2 -2
  2. package/dist/core/matador.d.ts.map +1 -1
  3. package/dist/core/matador.js +6 -0
  4. package/dist/index.cjs +80 -32
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +3 -3
  7. package/dist/index.d.ts +3 -3
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/pipeline/pipeline.d.ts +4 -1
  12. package/dist/pipeline/pipeline.d.ts.map +1 -1
  13. package/dist/pipeline/pipeline.js +16 -4
  14. package/dist/pipeline/pipeline.test.js +15 -1
  15. package/dist/topology/builder.d.ts +14 -0
  16. package/dist/topology/builder.d.ts.map +1 -1
  17. package/dist/topology/builder.js +20 -11
  18. package/dist/topology/builder.test.js +138 -0
  19. package/dist/topology/index.d.ts +2 -2
  20. package/dist/topology/index.d.ts.map +1 -1
  21. package/dist/topology/index.js +1 -1
  22. package/dist/topology/types.d.ts +53 -1
  23. package/dist/topology/types.d.ts.map +1 -1
  24. package/dist/topology/types.js +10 -0
  25. package/dist/transport/local/local-transport.d.ts.map +1 -1
  26. package/dist/transport/rabbitmq/rabbitmq-transport.d.ts +8 -0
  27. package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -1
  28. package/dist/transport/rabbitmq/rabbitmq-transport.js +42 -22
  29. package/dist/transport/rabbitmq/rabbitmq-transport.test.d.ts +2 -0
  30. package/dist/transport/rabbitmq/rabbitmq-transport.test.d.ts.map +1 -0
  31. package/dist/transport/rabbitmq/rabbitmq-transport.test.js +98 -0
  32. package/dist/types/dispatcher.d.ts +14 -0
  33. package/dist/types/dispatcher.d.ts.map +1 -0
  34. package/dist/types/dispatcher.js +1 -0
  35. package/dist/types/index.d.ts +2 -1
  36. package/dist/types/index.d.ts.map +1 -1
  37. package/dist/types/subscriber.d.ts +20 -4
  38. package/dist/types/subscriber.d.ts.map +1 -1
  39. package/package.json +1 -1
  40. package/src/core/matador.ts +14 -1
  41. package/src/index.ts +9 -0
  42. package/src/pipeline/pipeline.test.ts +24 -2
  43. package/src/pipeline/pipeline.ts +28 -6
  44. package/src/topology/builder.test.ts +176 -0
  45. package/src/topology/builder.ts +44 -5
  46. package/src/topology/index.ts +4 -0
  47. package/src/topology/types.ts +75 -1
  48. package/src/transport/local/local-transport.ts +4 -1
  49. package/src/transport/rabbitmq/rabbitmq-transport.test.ts +118 -0
  50. package/src/transport/rabbitmq/rabbitmq-transport.ts +48 -25
  51. package/src/types/dispatcher.ts +18 -0
  52. package/src/types/index.ts +4 -0
  53. package/src/types/subscriber.ts +22 -3
  54. package/test/e2e/rabbitmq-transport.e2e.test.ts +287 -0
  55. 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,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAGxE;;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;CACxD;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;gBAEtC,MAAM,EAAE,cAAc;IASlC;;OAEG;IACG,OAAO,CACX,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC;YAgNX,mBAAmB;YAqCnB,gBAAgB;IAkB9B;;;OAGG;IACH,OAAO,CAAC,yBAAyB;CA+BlC"}
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 as second argument
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
- result = await resumableCallback(envelope, context);
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: just pass envelope
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).toHaveBeenCalledWith(envelope);
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;AAEzD;;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;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,IAAI;IAKxD;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,IAAI;IAIrD;;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;IAsD7B;;;OAGG;IACH,KAAK,IAAI,QAAQ;CAgBlB"}
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"}
@@ -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
- * Adds a queue to the topology.
46
- */
47
- addQueue(name, options = {}) {
48
- this.queues.push({ name, ...options });
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
- * Alias for addQueue().
53
- */
54
- queue(name, options = {}) {
55
- return this.addQueue(name, options);
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
  });
@@ -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,GACT,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC"}
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"}
@@ -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';
@@ -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,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,iCAAiC;IACjC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE1C,uCAAuC;IACvC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE9C,kDAAkD;IAClD,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAE1C;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC;IAE1C,sDAAsD;IACtD,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC;CAC/C;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,kCAAkC;IAClC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAE1B,4CAA4C;IAC5C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAE1B,0CAA0C;IAC1C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAEhC,0CAA0C;IAC1C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,MAAM,CAER;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,WAAW,GAAG,eAAe,GACrC,MAAM,CAER;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,MAAM,CAER"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/topology/types.ts"],"names":[],"mappings":"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"}
@@ -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;IA4B5B,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"}
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":"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"}
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 queueOptions = {
399
- durable: true,
400
- arguments: {},
401
- };
402
- // Use quorum queues for durability
403
- if (this.config.quorumQueues && !queueDef.exact) {
404
- queueOptions.arguments['x-queue-type'] = 'quorum';
405
- }
406
- // Set up dead-letter exchange routing
407
- const dlxExchange = this.getDLXExchangeName(topology.namespace);
408
- if (topology.deadLetter.unhandled.enabled ||
409
- topology.deadLetter.undeliverable.enabled) {
410
- queueOptions.arguments['x-dead-letter-exchange'] = dlxExchange;
411
- }
412
- // Enable priority if requested
413
- if (queueDef.priorities) {
414
- queueOptions.arguments['x-max-priority'] = 10;
415
- }
416
- // Set consumer timeout if specified
417
- if (queueDef.consumerTimeout) {
418
- queueOptions.arguments['x-consumer-timeout'] = queueDef.consumerTimeout;
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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rabbitmq-transport.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rabbitmq-transport.test.d.ts","sourceRoot":"","sources":["../../../src/transport/rabbitmq/rabbitmq-transport.test.ts"],"names":[],"mappings":""}