@zdavison/matador 3.0.8 → 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.
- package/dist/core/fanout.d.ts +3 -2
- package/dist/core/fanout.d.ts.map +1 -1
- package/dist/core/fanout.js +4 -4
- package/dist/core/fanout.test.js +32 -24
- package/dist/core/matador.d.ts.map +1 -1
- package/dist/core/matador.js +16 -7
- package/dist/core/matador.test.js +32 -1
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +2 -2
- package/dist/errors/matador-errors.d.ts +28 -0
- package/dist/errors/matador-errors.d.ts.map +1 -1
- package/dist/errors/matador-errors.js +41 -0
- package/dist/index.cjs +325 -80
- 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 +3 -3
- package/dist/index.js.map +1 -1
- package/dist/topology/builder.d.ts +26 -1
- package/dist/topology/builder.d.ts.map +1 -1
- package/dist/topology/builder.js +72 -0
- package/dist/topology/builder.test.js +179 -1
- 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 +108 -6
- package/dist/topology/types.d.ts.map +1 -1
- package/dist/topology/types.js +50 -10
- package/dist/transport/local/local-transport.d.ts.map +1 -1
- package/dist/transport/local/local-transport.js +5 -2
- package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.d.ts +2 -0
- package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.d.ts.map +1 -0
- package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.js +110 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts +24 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -1
- package/dist/transport/rabbitmq/rabbitmq-transport.js +145 -68
- package/dist/transport/rabbitmq/rabbitmq-transport.test.js +130 -0
- 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,
|
|
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"}
|
package/dist/topology/builder.js
CHANGED
|
@@ -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
|
+
});
|
package/dist/topology/index.d.ts
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/topology/index.js
CHANGED
|
@@ -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';
|
package/dist/topology/types.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
|
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
|
|
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":"
|
|
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
|
@@ -1,28 +1,68 @@
|
|
|
1
|
+
import { UnknownQueueReferenceError } from '../errors/matador-errors.js';
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
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
|
|
5
|
-
return `${
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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;
|
|
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 =
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"rabbitmq-transport-reconnection.test.d.ts","sourceRoot":"","sources":["../../../src/transport/rabbitmq/rabbitmq-transport-reconnection.test.ts"],"names":[],"mappings":""}
|