@zdavison/matador 3.0.6 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/core/fanout.d.ts +3 -2
  2. package/dist/core/fanout.d.ts.map +1 -1
  3. package/dist/core/fanout.js +4 -4
  4. package/dist/core/fanout.test.js +32 -24
  5. package/dist/core/matador.d.ts.map +1 -1
  6. package/dist/core/matador.js +16 -7
  7. package/dist/core/matador.test.js +32 -1
  8. package/dist/errors/index.d.ts +1 -1
  9. package/dist/errors/index.d.ts.map +1 -1
  10. package/dist/errors/index.js +2 -2
  11. package/dist/errors/matador-errors.d.ts +28 -0
  12. package/dist/errors/matador-errors.d.ts.map +1 -1
  13. package/dist/errors/matador-errors.js +41 -0
  14. package/dist/index.cjs +325 -80
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +3 -3
  17. package/dist/index.d.ts +3 -3
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +3 -3
  20. package/dist/index.js.map +1 -1
  21. package/dist/topology/builder.d.ts +26 -1
  22. package/dist/topology/builder.d.ts.map +1 -1
  23. package/dist/topology/builder.js +72 -0
  24. package/dist/topology/builder.test.js +179 -1
  25. package/dist/topology/index.d.ts +2 -2
  26. package/dist/topology/index.d.ts.map +1 -1
  27. package/dist/topology/index.js +1 -1
  28. package/dist/topology/types.d.ts +108 -6
  29. package/dist/topology/types.d.ts.map +1 -1
  30. package/dist/topology/types.js +50 -10
  31. package/dist/transport/local/local-transport.d.ts.map +1 -1
  32. package/dist/transport/local/local-transport.js +5 -2
  33. package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.d.ts +2 -0
  34. package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.d.ts.map +1 -0
  35. package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.js +110 -0
  36. package/dist/transport/rabbitmq/rabbitmq-transport.d.ts +24 -0
  37. package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -1
  38. package/dist/transport/rabbitmq/rabbitmq-transport.js +145 -68
  39. package/dist/transport/rabbitmq/rabbitmq-transport.test.js +130 -0
  40. package/package.json +6 -2
@@ -1,5 +1,5 @@
1
1
  import type { HasDescription } from '../errors/index.js';
2
- import type { DeadLetterConfig, QueueDefinition, RetryConfig, Topology } from './types.js';
2
+ import type { DeadLetterConfig, QueueDefinition, RetryConfig, Topology, TopologyNaming } from './types.js';
3
3
  /**
4
4
  * Options for adding a queue.
5
5
  * Excludes 'name' as that is provided as the first argument to addQueue.
@@ -25,10 +25,35 @@ export declare class TopologyBuilder {
25
25
  private queues;
26
26
  private deadLetter;
27
27
  private retry;
28
+ private naming;
29
+ private prefix;
28
30
  /**
29
31
  * Sets the namespace prefix for all queues.
30
32
  */
31
33
  withNamespace(namespace: string): this;
34
+ /**
35
+ * Sets the prefix prepended to every default-derived broker resource name
36
+ * (e.g. `matador.{namespace}.{queue}`), so Matador-managed queues and
37
+ * exchanges are identifiable. Pass `null` to disable prefixing.
38
+ *
39
+ * Only applies to default names — a {@link withNaming} override fully owns
40
+ * its output and is never prefixed.
41
+ *
42
+ * @default 'matador'
43
+ * @example
44
+ * TopologyBuilder.create().withNamespace('myapp') // matador.myapp.events
45
+ * TopologyBuilder.create().withNamespace('myapp').withGlobalPrefix('acme') // acme.myapp.events
46
+ * TopologyBuilder.create().withNamespace('myapp').withGlobalPrefix(null) // myapp.events
47
+ */
48
+ withGlobalPrefix(prefix: string | null): this;
49
+ /**
50
+ * Overrides how broker resource names are derived.
51
+ * Use during migrations to keep pre-existing queue/exchange names so a
52
+ * rolling deploy keeps routing through the resources already declared on
53
+ * the broker.
54
+ * @see TopologyNaming
55
+ */
56
+ withNaming(naming: TopologyNaming): this;
32
57
  /**
33
58
  * Adds a queue to the topology.
34
59
  * @param definition - A complete QueueDefinition object
@@ -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;IAQ7B;;;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,EACR,cAAc,EACf,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;IACF,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,MAAM,CAAiC;IAE/C;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKtC;;;;;;;;;;;;;OAaG;IACH,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAK7C;;;;;;OAMG;IACH,UAAU,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAKxC;;;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;IAU7B;;;OAGG;IACH,KAAK,IAAI,QAAQ;CAkBlB"}
@@ -40,6 +40,8 @@ export class TopologyBuilder {
40
40
  defaultDelayMs: 1000,
41
41
  maxDelayMs: 300000, // 5 minutes
42
42
  };
43
+ naming;
44
+ prefix = 'matador';
43
45
  /**
44
46
  * Sets the namespace prefix for all queues.
45
47
  */
@@ -47,6 +49,35 @@ export class TopologyBuilder {
47
49
  this.namespace = namespace;
48
50
  return this;
49
51
  }
52
+ /**
53
+ * Sets the prefix prepended to every default-derived broker resource name
54
+ * (e.g. `matador.{namespace}.{queue}`), so Matador-managed queues and
55
+ * exchanges are identifiable. Pass `null` to disable prefixing.
56
+ *
57
+ * Only applies to default names — a {@link withNaming} override fully owns
58
+ * its output and is never prefixed.
59
+ *
60
+ * @default 'matador'
61
+ * @example
62
+ * TopologyBuilder.create().withNamespace('myapp') // matador.myapp.events
63
+ * TopologyBuilder.create().withNamespace('myapp').withGlobalPrefix('acme') // acme.myapp.events
64
+ * TopologyBuilder.create().withNamespace('myapp').withGlobalPrefix(null) // myapp.events
65
+ */
66
+ withGlobalPrefix(prefix) {
67
+ this.prefix = prefix;
68
+ return this;
69
+ }
70
+ /**
71
+ * Overrides how broker resource names are derived.
72
+ * Use during migrations to keep pre-existing queue/exchange names so a
73
+ * rolling deploy keeps routing through the resources already declared on
74
+ * the broker.
75
+ * @see TopologyNaming
76
+ */
77
+ withNaming(naming) {
78
+ this.naming = naming;
79
+ return this;
80
+ }
50
81
  addQueue(nameOrDefinition, options = {}) {
51
82
  if (isQueueDefinition(nameOrDefinition)) {
52
83
  this.queues.push(nameOrDefinition);
@@ -106,8 +137,10 @@ export class TopologyBuilder {
106
137
  validate() {
107
138
  return [
108
139
  ...validateNamespace(this.namespace),
140
+ ...validatePrefix(this.prefix),
109
141
  ...validateQueues(this.queues),
110
142
  ...validateRetry(this.retry),
143
+ ...validateNaming(this.naming),
111
144
  ];
112
145
  }
113
146
  /**
@@ -124,6 +157,8 @@ export class TopologyBuilder {
124
157
  queues: [...this.queues],
125
158
  deadLetter: this.deadLetter,
126
159
  retry: this.retry,
160
+ naming: this.naming,
161
+ prefix: this.prefix,
127
162
  };
128
163
  }
129
164
  }
@@ -139,6 +174,21 @@ function validateNamespace(namespace) {
139
174
  }
140
175
  return [];
141
176
  }
177
+ function validatePrefix(prefix) {
178
+ // null/undefined explicitly disable prefixing.
179
+ if (prefix == null) {
180
+ return [];
181
+ }
182
+ if (prefix.trim() === '') {
183
+ return ['Prefix must be a non-empty string, or null to disable prefixing'];
184
+ }
185
+ if (!IDENTIFIER_PATTERN.test(prefix)) {
186
+ return [
187
+ 'Prefix must start with a letter and contain only alphanumeric characters, underscores, and hyphens',
188
+ ];
189
+ }
190
+ return [];
191
+ }
142
192
  function validateQueueName(queue, seen) {
143
193
  if (!queue.name || queue.name.trim() === '') {
144
194
  return ['Queue name cannot be empty'];
@@ -176,6 +226,28 @@ function validateQueues(queues) {
176
226
  }
177
227
  return issues;
178
228
  }
229
+ function validateNaming(naming) {
230
+ if (!naming)
231
+ return [];
232
+ const issues = [];
233
+ for (const field of [
234
+ 'queue',
235
+ 'mainExchange',
236
+ 'dlxExchange',
237
+ 'delayedExchange',
238
+ ]) {
239
+ const value = naming[field];
240
+ if (value !== undefined && typeof value !== 'function') {
241
+ issues.push(`Naming override "${field}" must be a function`);
242
+ }
243
+ }
244
+ if (naming.dlxExchangeType !== undefined &&
245
+ naming.dlxExchangeType !== 'direct' &&
246
+ naming.dlxExchangeType !== 'topic') {
247
+ issues.push(`Naming override "dlxExchangeType" must be 'direct' or 'topic'`);
248
+ }
249
+ return issues;
250
+ }
179
251
  function validateRetry(retry) {
180
252
  if (!retry.enabled)
181
253
  return [];
@@ -1,6 +1,7 @@
1
1
  import { describe, expect, it } from 'bun:test';
2
+ import { UnknownQueueReferenceError } from '../errors/matador-errors.js';
2
3
  import { TopologyBuilder, TopologyValidationError } from './builder.js';
3
- import { resolveQueueName } from './types.js';
4
+ import { findQueueDefinition, getDeadLetterQueueName, getQualifiedQueueName, getRetryQueueName, resolveQueueName, resolveTargetQueueName, } from './types.js';
4
5
  describe('TopologyBuilder', () => {
5
6
  describe('withNamespace', () => {
6
7
  it('should set the namespace', () => {
@@ -358,3 +359,180 @@ describe('resolveQueueName', () => {
358
359
  expect(resolveQueueName('myapp', queueDef)).toBe('matador.shared.id-platform');
359
360
  });
360
361
  });
362
+ describe('TopologyNaming', () => {
363
+ const v1Naming = {
364
+ queue: (ns, q) => `matador.${ns}.${q}`,
365
+ mainExchange: (ns) => `matador.${ns}`,
366
+ dlxExchange: (ns) => `matador.${ns}.dlx-undeliverable`,
367
+ dlxExchangeType: 'topic',
368
+ delayedExchange: (ns) => `matador.${ns}.delayed`,
369
+ };
370
+ describe('builder validation', () => {
371
+ it('should accept a valid naming configuration', () => {
372
+ const topology = TopologyBuilder.create()
373
+ .withNamespace('myapp')
374
+ .withNaming(v1Naming)
375
+ .addQueue('events')
376
+ .build();
377
+ expect(topology.naming?.queue?.('myapp', 'events')).toBe('matador.myapp.events');
378
+ expect(topology.naming?.mainExchange?.('myapp')).toBe('matador.myapp');
379
+ });
380
+ it('should accept partial naming overrides', () => {
381
+ const topology = TopologyBuilder.create()
382
+ .withNamespace('myapp')
383
+ .withNaming({ queue: (ns, q) => `legacy.matador.${ns}.${q}` })
384
+ .addQueue('events')
385
+ .build();
386
+ expect(topology.naming?.queue?.('myapp', 'events')).toBe('legacy.matador.myapp.events');
387
+ });
388
+ it('should reject a non-function naming override', () => {
389
+ expect(() => TopologyBuilder.create()
390
+ .withNamespace('myapp')
391
+ .withNaming({ queue: 'matador.' })
392
+ .addQueue('events')
393
+ .build()).toThrow(TopologyValidationError);
394
+ });
395
+ it('should reject an invalid dlxExchangeType', () => {
396
+ expect(() => TopologyBuilder.create()
397
+ .withNamespace('myapp')
398
+ .withNaming({
399
+ dlxExchangeType: 'fanout',
400
+ })
401
+ .addQueue('events')
402
+ .build()).toThrow(TopologyValidationError);
403
+ });
404
+ });
405
+ describe('name resolution with naming overrides', () => {
406
+ const topology = TopologyBuilder.create()
407
+ .withNamespace('myapp')
408
+ .withNaming({ queue: (ns, q) => `matador.${ns}.${q}` })
409
+ .addQueue('events')
410
+ .addQueue({
411
+ name: 'matador.shared.id-platform',
412
+ exact: true,
413
+ })
414
+ .build();
415
+ it('should build qualified queue names via the strategy', () => {
416
+ expect(getQualifiedQueueName('myapp', 'events', topology.naming)).toBe('matador.myapp.events');
417
+ });
418
+ it('should build resolved queue definitions via the strategy', () => {
419
+ const queueDef = { name: 'events' };
420
+ expect(resolveQueueName('myapp', queueDef, topology.naming)).toBe('matador.myapp.events');
421
+ });
422
+ it('should not transform exact queue definitions', () => {
423
+ const queueDef = { name: 'matador.shared.id-platform', exact: true };
424
+ expect(resolveQueueName('myapp', queueDef, topology.naming)).toBe('matador.shared.id-platform');
425
+ });
426
+ it('should resolve a target queue by exact name as-is', () => {
427
+ expect(resolveTargetQueueName(topology, 'matador.shared.id-platform')).toBe('matador.shared.id-platform');
428
+ });
429
+ it('should build non-exact target queues via the strategy', () => {
430
+ expect(resolveTargetQueueName(topology, 'events')).toBe('matador.myapp.events');
431
+ });
432
+ it('should derive retry and DLQ names from the strategy name', () => {
433
+ expect(getRetryQueueName('myapp', 'events', topology.naming)).toBe('matador.myapp.events.retry');
434
+ expect(getDeadLetterQueueName('myapp', 'events', 'unhandled', topology.naming)).toBe('matador.myapp.events.unhandled');
435
+ });
436
+ it('should find queue definitions by name', () => {
437
+ expect(findQueueDefinition(topology, 'events')?.name).toBe('events');
438
+ expect(findQueueDefinition(topology, 'matador.shared.id-platform')?.name).toBe('matador.shared.id-platform');
439
+ expect(findQueueDefinition(topology, 'missing')).toBeUndefined();
440
+ });
441
+ it('should throw for a queue reference not declared in the topology', () => {
442
+ // A targetQueue/consumeFrom name that was never added to the topology
443
+ // must fail loudly rather than be silently namespace-qualified and
444
+ // routed to a queue nobody consumes.
445
+ expect(() => resolveTargetQueueName(topology, 'ext-service.payments')).toThrow(UnknownQueueReferenceError);
446
+ });
447
+ });
448
+ });
449
+ describe('resource name prefix', () => {
450
+ describe('default', () => {
451
+ it('prefixes resource names with "matador" by default', () => {
452
+ const topology = TopologyBuilder.create()
453
+ .withNamespace('myapp')
454
+ .addQueue('events')
455
+ .build();
456
+ expect(topology.prefix).toBe('matador');
457
+ expect(resolveTargetQueueName(topology, 'events')).toBe('matador.myapp.events');
458
+ });
459
+ it('inherits the default prefix into retry and DLQ names', () => {
460
+ expect(getRetryQueueName('myapp', 'events', undefined, 'matador')).toBe('matador.myapp.events.retry');
461
+ expect(getDeadLetterQueueName('myapp', 'events', 'unhandled', undefined, 'matador')).toBe('matador.myapp.events.unhandled');
462
+ });
463
+ });
464
+ describe('withGlobalPrefix', () => {
465
+ it('uses a custom prefix string', () => {
466
+ const topology = TopologyBuilder.create()
467
+ .withNamespace('myapp')
468
+ .withGlobalPrefix('acme')
469
+ .addQueue('events')
470
+ .build();
471
+ expect(topology.prefix).toBe('acme');
472
+ expect(resolveTargetQueueName(topology, 'events')).toBe('acme.myapp.events');
473
+ });
474
+ it('disables prefixing when set to null', () => {
475
+ const topology = TopologyBuilder.create()
476
+ .withNamespace('myapp')
477
+ .withGlobalPrefix(null)
478
+ .addQueue('events')
479
+ .build();
480
+ expect(topology.prefix).toBeNull();
481
+ expect(resolveTargetQueueName(topology, 'events')).toBe('myapp.events');
482
+ });
483
+ });
484
+ describe('helper-level prefixing', () => {
485
+ it('applies a non-null prefix to the default qualified name', () => {
486
+ expect(getQualifiedQueueName('myapp', 'events', undefined, 'acme')).toBe('acme.myapp.events');
487
+ });
488
+ it('omits the prefix when null or undefined', () => {
489
+ expect(getQualifiedQueueName('myapp', 'events', undefined, null)).toBe('myapp.events');
490
+ expect(getQualifiedQueueName('myapp', 'events', undefined, undefined)).toBe('myapp.events');
491
+ });
492
+ });
493
+ describe('composition with withNaming', () => {
494
+ it('does not prefix names produced by a withNaming override', () => {
495
+ const topology = TopologyBuilder.create()
496
+ .withNamespace('myapp')
497
+ .withGlobalPrefix('matador')
498
+ .withNaming({ queue: (_ns, q) => `legacy-${q}` })
499
+ .addQueue('events')
500
+ .build();
501
+ // The override fully owns the name; the prefix is not prepended.
502
+ expect(resolveTargetQueueName(topology, 'events')).toBe('legacy-events');
503
+ });
504
+ });
505
+ describe('validation', () => {
506
+ it('rejects an empty-string prefix', () => {
507
+ expect(() => TopologyBuilder.create()
508
+ .withNamespace('myapp')
509
+ .withGlobalPrefix('')
510
+ .addQueue('events')
511
+ .build()).toThrow(TopologyValidationError);
512
+ });
513
+ it('rejects a prefix that is not a valid identifier', () => {
514
+ expect(() => TopologyBuilder.create()
515
+ .withNamespace('myapp')
516
+ .withGlobalPrefix('1bad')
517
+ .addQueue('events')
518
+ .build()).toThrow(TopologyValidationError);
519
+ expect(() => TopologyBuilder.create()
520
+ .withNamespace('myapp')
521
+ .withGlobalPrefix('has space')
522
+ .addQueue('events')
523
+ .build()).toThrow(TopologyValidationError);
524
+ });
525
+ it('accepts null (no prefix) and valid identifier prefixes', () => {
526
+ expect(() => TopologyBuilder.create()
527
+ .withNamespace('myapp')
528
+ .withGlobalPrefix(null)
529
+ .addQueue('events')
530
+ .build()).not.toThrow();
531
+ expect(() => TopologyBuilder.create()
532
+ .withNamespace('myapp')
533
+ .withGlobalPrefix('acme_co-1')
534
+ .addQueue('events')
535
+ .build()).not.toThrow();
536
+ });
537
+ });
538
+ });
@@ -1,5 +1,5 @@
1
- export type { DeadLetterConfig, DeadLetterQueueConfig, QueueDefinition, RabbitMQQueueDefinition, RabbitMQQueueOptions, RetryConfig, Topology, TransportQueueOptions, } from './types.js';
2
- export { getDeadLetterQueueName, getQualifiedQueueName, getRetryQueueName, resolveQueueName, } from './types.js';
1
+ export type { DeadLetterConfig, DeadLetterQueueConfig, QueueDefinition, RabbitMQQueueDefinition, RabbitMQQueueOptions, RetryConfig, Topology, TopologyNaming, TransportQueueOptions, } from './types.js';
2
+ export { applyPrefix, findQueueDefinition, getDeadLetterQueueName, getQualifiedQueueName, getRetryQueueName, resolveQueueName, resolveTargetQueueName, } 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,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
+ {"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,cAAc,EACd,qBAAqB,GACtB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EAChB,sBAAsB,GACvB,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, resolveQueueName, } from './types.js';
1
+ export { applyPrefix, findQueueDefinition, getDeadLetterQueueName, getQualifiedQueueName, getRetryQueueName, resolveQueueName, resolveTargetQueueName, } from './types.js';
2
2
  export { TopologyBuilder, TopologyValidationError } from './builder.js';
@@ -11,6 +11,83 @@ export interface Topology {
11
11
  readonly deadLetter: DeadLetterConfig;
12
12
  /** Retry queue configuration */
13
13
  readonly retry: RetryConfig;
14
+ /**
15
+ * Optional overrides for how broker resource names are derived.
16
+ * Intended for migrations: lets an adopter keep pre-existing queue and
17
+ * exchange names (e.g. from Matador v1) so a rolling deploy keeps using
18
+ * the broker resources already in place.
19
+ */
20
+ readonly naming?: TopologyNaming | undefined;
21
+ /**
22
+ * Prefix prepended to every default-derived broker resource name so
23
+ * Matador-managed queues and exchanges are identifiable
24
+ * (e.g. `matador.{namespace}.{queue}`).
25
+ *
26
+ * `null` disables prefixing. Only applies to default names — a `naming`
27
+ * override fully owns its output and is never prefixed.
28
+ *
29
+ * Topologies built via {@link TopologyBuilder} default this to `'matador'`.
30
+ * When absent (e.g. a hand-built topology literal) no prefix is applied.
31
+ *
32
+ * @default 'matador'
33
+ */
34
+ readonly prefix?: string | null | undefined;
35
+ }
36
+ /**
37
+ * Strategy for deriving broker resource names.
38
+ *
39
+ * Each builder receives the namespace as an explicit argument and returns the
40
+ * final name, so the namespace is always an input to the strategy — it is
41
+ * never applied separately on top of the result. Provide only the builders you
42
+ * want to override; the rest keep Matador's defaults:
43
+ * - work queue: `${namespace}.${queueName}`
44
+ * - main exchange: `${namespace}.exchange`
45
+ * - dead-letter exchange: `${namespace}.dlx`
46
+ * - delayed exchange: `${namespace}.delayed`
47
+ *
48
+ * Retry queues and DLQs have no builder of their own: they are derived from the
49
+ * work-queue name (`<queue>.retry`, `<queue>.unhandled`, `<queue>.undeliverable`),
50
+ * so overriding `queue` carries your prefix into them automatically.
51
+ *
52
+ * Intended for migrations: an adopter can keep pre-existing names (e.g. from
53
+ * Matador v1) so a rolling deploy keeps using the broker resources already in
54
+ * place instead of draining or dual-consuming.
55
+ *
56
+ * @example Keep Matador v1 names (`matador.{namespace}.{queue}`):
57
+ * ```typescript
58
+ * TopologyBuilder.create()
59
+ * .withNamespace('myapp')
60
+ * .withNaming({
61
+ * queue: (ns, q) => `matador.${ns}.${q}`,
62
+ * mainExchange: (ns) => `matador.${ns}`,
63
+ * dlxExchange: (ns) => `matador.${ns}.dlx-undeliverable`,
64
+ * dlxExchangeType: 'topic',
65
+ * delayedExchange: (ns) => `matador.${ns}.delayed`,
66
+ * })
67
+ * ```
68
+ */
69
+ export interface TopologyNaming {
70
+ /**
71
+ * Builds the qualified work-queue name from the namespace and queue name.
72
+ * Retry queues and DLQs are derived from the result automatically
73
+ * (`<queue>.retry`, `<queue>.unhandled`, `<queue>.undeliverable`).
74
+ * Default builds `${namespace}.${queueName}`.
75
+ */
76
+ readonly queue?: ((namespace: string, queueName: string) => string) | undefined;
77
+ /** Builds the main exchange name. Default builds `${namespace}.exchange`. */
78
+ readonly mainExchange?: ((namespace: string) => string) | undefined;
79
+ /** Builds the dead-letter exchange name. Default builds `${namespace}.dlx`. */
80
+ readonly dlxExchange?: ((namespace: string) => string) | undefined;
81
+ /**
82
+ * Exchange type used when asserting the dead-letter exchange.
83
+ * Useful when an existing DLX was declared as `topic` — asserting it as
84
+ * `direct` would fail with PRECONDITION_FAILED. Literal (non-wildcard)
85
+ * binding keys behave identically on a topic exchange.
86
+ * Default: `'direct'`.
87
+ */
88
+ readonly dlxExchangeType?: 'direct' | 'topic' | undefined;
89
+ /** Builds the delayed-message exchange name. Default builds `${namespace}.delayed`. */
90
+ readonly delayedExchange?: ((namespace: string) => string) | undefined;
14
91
  }
15
92
  /**
16
93
  * Individual queue definition.
@@ -109,20 +186,45 @@ export interface RetryConfig {
109
186
  readonly maxDelayMs: number;
110
187
  }
111
188
  /**
112
- * Gets the fully qualified queue name with namespace prefix.
189
+ * Prepends a resource-name prefix unless it is disabled.
190
+ * `null`/`undefined` mean no prefix; a string is prepended as `${prefix}.`.
191
+ */
192
+ export declare function applyPrefix(prefix: string | null | undefined, name: string): string;
193
+ /**
194
+ * Gets the fully qualified work-queue name.
195
+ * An optional naming strategy may override how the name is derived; otherwise
196
+ * the default is `${namespace}.${queueName}`, optionally prefixed.
113
197
  */
114
- export declare function getQualifiedQueueName(namespace: string, queueName: string): string;
198
+ export declare function getQualifiedQueueName(namespace: string, queueName: string, naming?: TopologyNaming, prefix?: string | null): string;
115
199
  /**
116
200
  * Gets the dead-letter queue name for a given queue.
117
201
  */
118
- export declare function getDeadLetterQueueName(namespace: string, queueName: string, dlqType: 'unhandled' | 'undeliverable'): string;
202
+ export declare function getDeadLetterQueueName(namespace: string, queueName: string, dlqType: 'unhandled' | 'undeliverable', naming?: TopologyNaming, prefix?: string | null): string;
119
203
  /**
120
204
  * Gets the retry queue name for a given queue.
121
205
  */
122
- export declare function getRetryQueueName(namespace: string, queueName: string): string;
206
+ export declare function getRetryQueueName(namespace: string, queueName: string, naming?: TopologyNaming, prefix?: string | null): string;
123
207
  /**
124
208
  * Resolves the actual queue name for a given queue definition.
125
- * When exact: true, returns name as-is. Otherwise, returns namespace.name.
209
+ * When exact: true, returns name as-is. Otherwise, returns the
210
+ * namespace-qualified name (honoring any naming overrides and prefix).
211
+ */
212
+ export declare function resolveQueueName(namespace: string, queueDef: QueueDefinition, naming?: TopologyNaming, prefix?: string | null): string;
213
+ /**
214
+ * Finds the queue definition a local queue reference points at.
215
+ */
216
+ export declare function findQueueDefinition(topology: Topology, queueName: string): QueueDefinition | undefined;
217
+ /**
218
+ * Resolves a local queue reference (a subscriber's `targetQueue` or a
219
+ * `consumeFrom` entry) to the transport-level queue name.
220
+ *
221
+ * The reference must be declared in the topology via `.addQueue(...)`:
222
+ * an exact queue resolves to its name as-is; a Matador-owned queue is
223
+ * namespace-qualified (honoring any naming overrides). An undeclared
224
+ * reference throws {@link UnknownQueueReferenceError} rather than being
225
+ * silently qualified and routed to a queue nobody consumes.
226
+ *
227
+ * @throws UnknownQueueReferenceError if `queueName` is not in `topology.queues`.
126
228
  */
127
- export declare function resolveQueueName(namespace: string, queueDef: QueueDefinition): string;
229
+ export declare function resolveTargetQueueName(topology: Topology, queueName: string): string;
128
230
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/topology/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/topology/types.ts"],"names":[],"mappings":"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"}
@@ -1,28 +1,68 @@
1
+ import { UnknownQueueReferenceError } from '../errors/matador-errors.js';
1
2
  /**
2
- * Gets the fully qualified queue name with namespace prefix.
3
+ * Prepends a resource-name prefix unless it is disabled.
4
+ * `null`/`undefined` mean no prefix; a string is prepended as `${prefix}.`.
3
5
  */
4
- export function getQualifiedQueueName(namespace, queueName) {
5
- return `${namespace}.${queueName}`;
6
+ export function applyPrefix(prefix, name) {
7
+ return prefix == null ? name : `${prefix}.${name}`;
8
+ }
9
+ /**
10
+ * Gets the fully qualified work-queue name.
11
+ * An optional naming strategy may override how the name is derived; otherwise
12
+ * the default is `${namespace}.${queueName}`, optionally prefixed.
13
+ */
14
+ export function getQualifiedQueueName(namespace, queueName, naming, prefix) {
15
+ return (naming?.queue?.(namespace, queueName) ??
16
+ applyPrefix(prefix, `${namespace}.${queueName}`));
6
17
  }
7
18
  /**
8
19
  * Gets the dead-letter queue name for a given queue.
9
20
  */
10
- export function getDeadLetterQueueName(namespace, queueName, dlqType) {
11
- return `${namespace}.${queueName}.${dlqType}`;
21
+ export function getDeadLetterQueueName(namespace, queueName, dlqType, naming, prefix) {
22
+ return `${getQualifiedQueueName(namespace, queueName, naming, prefix)}.${dlqType}`;
12
23
  }
13
24
  /**
14
25
  * Gets the retry queue name for a given queue.
15
26
  */
16
- export function getRetryQueueName(namespace, queueName) {
17
- return `${namespace}.${queueName}.retry`;
27
+ export function getRetryQueueName(namespace, queueName, naming, prefix) {
28
+ return `${getQualifiedQueueName(namespace, queueName, naming, prefix)}.retry`;
18
29
  }
19
30
  /**
20
31
  * Resolves the actual queue name for a given queue definition.
21
- * When exact: true, returns name as-is. Otherwise, returns namespace.name.
32
+ * When exact: true, returns name as-is. Otherwise, returns the
33
+ * namespace-qualified name (honoring any naming overrides and prefix).
22
34
  */
23
- export function resolveQueueName(namespace, queueDef) {
35
+ export function resolveQueueName(namespace, queueDef, naming, prefix) {
24
36
  if (queueDef.exact) {
25
37
  return queueDef.name;
26
38
  }
27
- return `${namespace}.${queueDef.name}`;
39
+ return getQualifiedQueueName(namespace, queueDef.name, naming, prefix);
40
+ }
41
+ /**
42
+ * Finds the queue definition a local queue reference points at.
43
+ */
44
+ export function findQueueDefinition(topology, queueName) {
45
+ return topology.queues.find((q) => q.name === queueName);
46
+ }
47
+ /**
48
+ * Resolves a local queue reference (a subscriber's `targetQueue` or a
49
+ * `consumeFrom` entry) to the transport-level queue name.
50
+ *
51
+ * The reference must be declared in the topology via `.addQueue(...)`:
52
+ * an exact queue resolves to its name as-is; a Matador-owned queue is
53
+ * namespace-qualified (honoring any naming overrides). An undeclared
54
+ * reference throws {@link UnknownQueueReferenceError} rather than being
55
+ * silently qualified and routed to a queue nobody consumes.
56
+ *
57
+ * @throws UnknownQueueReferenceError if `queueName` is not in `topology.queues`.
58
+ */
59
+ export function resolveTargetQueueName(topology, queueName) {
60
+ const def = findQueueDefinition(topology, queueName);
61
+ if (def === undefined) {
62
+ throw new UnknownQueueReferenceError(queueName);
63
+ }
64
+ if (def.exact) {
65
+ return def.name;
66
+ }
67
+ return getQualifiedQueueName(topology.namespace, queueName, topology.naming, topology.prefix);
28
68
  }
@@ -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;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"}
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;AAExD,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;IAsBhD,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;IAoC5B,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,5 +1,6 @@
1
1
  import { TransportNotConnectedError } from '../../errors/index.js';
2
2
  import { consoleLogger } from '../../hooks/index.js';
3
+ import { resolveQueueName } from '../../topology/types.js';
3
4
  /**
4
5
  * Capabilities of the LocalTransport.
5
6
  */
@@ -58,7 +59,7 @@ export class LocalTransport {
58
59
  // retries by re-enqueueing messages with a delay to the original queue, matching
59
60
  // how the pipeline schedules retries via transport.send() with a delay option.
60
61
  for (const queueDef of topology.queues) {
61
- const queueName = `${topology.namespace}.${queueDef.name}`;
62
+ const queueName = resolveQueueName(topology.namespace, queueDef, topology.naming, topology.prefix);
62
63
  if (!this.queues.has(queueName)) {
63
64
  this.queues.set(queueName, []);
64
65
  }
@@ -104,8 +105,10 @@ export class LocalTransport {
104
105
  }
105
106
  async deliverToSubscribers(queue, message) {
106
107
  const subs = this.subscriptions.get(queue);
107
- if (!subs)
108
+ if (!subs) {
109
+ this.logger.warn(`[Matador][LocalTransport] 🟡 No subscriptions found for queue '${queue}', message will be lost.`);
108
110
  return;
111
+ }
109
112
  for (const sub of subs) {
110
113
  if (!sub.active || message.completed)
111
114
  continue;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rabbitmq-transport-reconnection.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rabbitmq-transport-reconnection.test.d.ts","sourceRoot":"","sources":["../../../src/transport/rabbitmq/rabbitmq-transport-reconnection.test.ts"],"names":[],"mappings":""}