@zdavison/matador 4.0.3 → 4.1.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/matador.d.ts.map +1 -1
- package/dist/core/matador.js +4 -1
- package/dist/core/matador.test.js +94 -1
- package/dist/index.cjs +111 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pipeline/pipeline.d.ts +30 -2
- package/dist/pipeline/pipeline.d.ts.map +1 -1
- package/dist/pipeline/pipeline.js +61 -9
- package/dist/pipeline/pipeline.test.js +123 -0
- package/dist/retry/index.d.ts +1 -1
- package/dist/retry/index.d.ts.map +1 -1
- package/dist/retry/policy.d.ts +25 -0
- package/dist/retry/policy.d.ts.map +1 -1
- package/dist/retry/standard-policy.d.ts +19 -1
- package/dist/retry/standard-policy.d.ts.map +1 -1
- package/dist/retry/standard-policy.js +39 -2
- package/dist/retry/standard-policy.test.js +65 -0
- package/dist/topology/builder.test.js +7 -0
- package/dist/topology/types.d.ts +14 -0
- package/dist/topology/types.d.ts.map +1 -1
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -1
- package/dist/transport/rabbitmq/rabbitmq-transport.js +3 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.test.js +28 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"matador.d.ts","sourceRoot":"","sources":["../../src/core/matador.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"matador.d.ts","sourceRoot":"","sources":["../../src/core/matador.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAQ/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAKrD,OAAO,KAAK,EAAgB,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,EACV,aAAa,EACb,UAAU,EACV,KAAK,EACL,UAAU,EACV,YAAY,EACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGnE;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAE9B,6BAA6B;IAC7B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAE5B,uEAAuE;IACvE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAE/B,sDAAsD;IACtD,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAErD,sCAAsC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;IAEnC,0BAA0B;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAE/C,6BAA6B;IAC7B,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAE9D;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IAEvD;;;;;OAKG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAEjD;;;;;OAKG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE/C;;;;;;;OAOG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE9C;;;;;;;;OAQG;IACH,QAAQ,CAAC,2BAA2B,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3D;AAED;;;;;;;;;GASG;AACH,qBAAa,OAAQ,YAAW,UAAU;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAChD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsB;IACpD,OAAO,CAAC,OAAO,CAAS;IAExB;;;;;;OAMG;gBACS,MAAM,EAAE,aAAa,EAAE,KAAK,CAAC,EAAE,YAAY;IAgDvD;;OAEG;IACH,QAAQ,CAAC,CAAC,EACR,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EACzB,WAAW,EAAE,SAAS,aAAa,EAAE,GACpC,IAAI;IAKP;;;;;;;;;;;;;;;;;OAiBG;IACH,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAc3C;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoE5B;;;;;;;;;;;;OAYG;IACG,IAAI,CAAC,CAAC,EACV,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EACzB,IAAI,EAAE,CAAC,EACP,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,UAAU,CAAC;IAChB,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IAuC3E;;OAEG;IACH,gBAAgB,IAAI,aAAa;IAIjC;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACG,WAAW,CAAC,SAAS,SAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAatD;;;;;;;;;;;OAWG;IACG,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IAQvC;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAU/B;;OAEG;IACH,WAAW,IAAI,OAAO;YAIR,cAAc;CAM7B"}
|
package/dist/core/matador.js
CHANGED
|
@@ -141,6 +141,10 @@ export class Matador {
|
|
|
141
141
|
else {
|
|
142
142
|
this.hooks.logger.info('[Matador] 🟡 Worker not subscribing to any queues (consumeFrom is empty).');
|
|
143
143
|
}
|
|
144
|
+
// Mark as started
|
|
145
|
+
// Needed for the subscribe calls below, as we might start getting messages
|
|
146
|
+
// that could send events before the code here finishes subscribing to all the queues
|
|
147
|
+
this.started = true;
|
|
144
148
|
for (const queueName of this.consumeFrom) {
|
|
145
149
|
const qualifiedName = resolveTargetQueueName(this.topology, queueName);
|
|
146
150
|
const queueDef = findQueueDefinition(this.topology, queueName);
|
|
@@ -158,7 +162,6 @@ export class Matador {
|
|
|
158
162
|
: undefined);
|
|
159
163
|
this.subscriptions.push(subscription);
|
|
160
164
|
}
|
|
161
|
-
this.started = true;
|
|
162
165
|
}
|
|
163
166
|
async send(eventOrClass, dataOrOptions, maybeOptions) {
|
|
164
167
|
if (!this.started) {
|
|
@@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, mock } from 'bun:test';
|
|
|
2
2
|
import { SomeSendError, TransportSendError } from '../errors/index.js';
|
|
3
3
|
import { TopologyBuilder } from '../topology/builder.js';
|
|
4
4
|
import { LocalTransport, MultiTransport } from '../transport/index.js';
|
|
5
|
-
import { MatadorEvent, createSubscriber } from '../types/index.js';
|
|
5
|
+
import { MatadorEvent, createDummyEnvelope, createSubscriber, } from '../types/index.js';
|
|
6
6
|
import { Matador } from './matador.js';
|
|
7
7
|
class UserCreatedEvent extends MatadorEvent {
|
|
8
8
|
data;
|
|
@@ -623,6 +623,99 @@ describe('Matador', () => {
|
|
|
623
623
|
expect(matador.isConnected()).toBe(true);
|
|
624
624
|
});
|
|
625
625
|
});
|
|
626
|
+
describe('start() race between subscribing and marking started', () => {
|
|
627
|
+
it('should allow send() from a subscriber callback fired while start() is still subscribing later queues', async () => {
|
|
628
|
+
const topology = TopologyBuilder.create()
|
|
629
|
+
.withNamespace('test')
|
|
630
|
+
.addQueue('events')
|
|
631
|
+
.addQueue('notifications')
|
|
632
|
+
.build();
|
|
633
|
+
let sendError;
|
|
634
|
+
const userSub = createSubscriber({
|
|
635
|
+
name: 'handle-user',
|
|
636
|
+
description: 'Handles user events',
|
|
637
|
+
callback: async (_envelope, context) => {
|
|
638
|
+
try {
|
|
639
|
+
await context.matador.send(OrderPlacedEvent, {
|
|
640
|
+
orderId: 'order-1',
|
|
641
|
+
amount: 100,
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
catch (err) {
|
|
645
|
+
sendError = err;
|
|
646
|
+
}
|
|
647
|
+
},
|
|
648
|
+
});
|
|
649
|
+
const orderSub = createSubscriber({
|
|
650
|
+
name: 'handle-order',
|
|
651
|
+
description: 'Handles order events',
|
|
652
|
+
callback: async () => { },
|
|
653
|
+
});
|
|
654
|
+
const schema = {
|
|
655
|
+
[UserCreatedEvent.key]: [UserCreatedEvent, [userSub]],
|
|
656
|
+
[OrderPlacedEvent.key]: [OrderPlacedEvent, [orderSub]],
|
|
657
|
+
};
|
|
658
|
+
// Simulates a broker delivering a message on the first subscribed
|
|
659
|
+
// queue before subscribe() has resolved for a later queue - the
|
|
660
|
+
// exact window in which `started` used to still be false.
|
|
661
|
+
let capturedHandler;
|
|
662
|
+
let releaseSecondSubscribe;
|
|
663
|
+
const secondSubscribeGate = new Promise((resolve) => {
|
|
664
|
+
releaseSecondSubscribe = resolve;
|
|
665
|
+
});
|
|
666
|
+
const raceTransport = {
|
|
667
|
+
name: 'race-transport',
|
|
668
|
+
capabilities: {
|
|
669
|
+
deliveryModes: ['at-least-once'],
|
|
670
|
+
delayedMessages: false,
|
|
671
|
+
deadLetterRouting: 'native',
|
|
672
|
+
attemptTracking: true,
|
|
673
|
+
concurrencyModel: 'prefetch',
|
|
674
|
+
ordering: 'none',
|
|
675
|
+
priorities: false,
|
|
676
|
+
},
|
|
677
|
+
isConnected: () => true,
|
|
678
|
+
connect: mock(async () => { }),
|
|
679
|
+
disconnect: mock(async () => { }),
|
|
680
|
+
send: mock(async () => 'race-transport'),
|
|
681
|
+
subscribe: mock(async (queueName, handler) => {
|
|
682
|
+
if (queueName === 'matador.test.notifications') {
|
|
683
|
+
await secondSubscribeGate;
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
capturedHandler = handler;
|
|
687
|
+
}
|
|
688
|
+
return { unsubscribe: mock(async () => { }), isActive: true };
|
|
689
|
+
}),
|
|
690
|
+
applyTopology: mock(async () => { }),
|
|
691
|
+
complete: mock(async () => { }),
|
|
692
|
+
};
|
|
693
|
+
matador = new Matador({
|
|
694
|
+
transport: raceTransport,
|
|
695
|
+
topology,
|
|
696
|
+
schema,
|
|
697
|
+
consumeFrom: ['events', 'notifications'],
|
|
698
|
+
});
|
|
699
|
+
const startPromise = matador.start();
|
|
700
|
+
// Wait for the first queue's subscribe() to resolve and capture its
|
|
701
|
+
// handler, while the second queue's subscribe() is still pending.
|
|
702
|
+
while (!capturedHandler) {
|
|
703
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
704
|
+
}
|
|
705
|
+
const envelope = createDummyEnvelope(new UserCreatedEvent({ userId: '123', email: 'test@example.com' }), { targetSubscriber: 'handle-user' });
|
|
706
|
+
await capturedHandler?.(envelope, {
|
|
707
|
+
handle: 'msg-1',
|
|
708
|
+
redelivered: false,
|
|
709
|
+
attemptNumber: 1,
|
|
710
|
+
deliveryCount: 1,
|
|
711
|
+
sourceQueue: 'matador.test.events',
|
|
712
|
+
sourceTransport: 'race-transport',
|
|
713
|
+
});
|
|
714
|
+
expect(sendError).toBeUndefined();
|
|
715
|
+
releaseSecondSubscribe?.();
|
|
716
|
+
await startPromise;
|
|
717
|
+
});
|
|
718
|
+
});
|
|
626
719
|
describe('self-dispatch during graceful shutdown (stopReceiving)', () => {
|
|
627
720
|
it('LOCAL transport only: a self-dispatched follow-up should still be delivered after stopReceiving()', async () => {
|
|
628
721
|
const topology = TopologyBuilder.create()
|
package/dist/index.cjs
CHANGED
|
@@ -1893,10 +1893,20 @@ var ProcessingPipeline = class {
|
|
|
1893
1893
|
durationMs: performance.now() - startTime
|
|
1894
1894
|
};
|
|
1895
1895
|
}
|
|
1896
|
+
const isResumable = isResumableSubscriber(subscriber);
|
|
1897
|
+
const shouldProcessResult = await this.handleShouldProcess(
|
|
1898
|
+
envelope,
|
|
1899
|
+
subscriberDef,
|
|
1900
|
+
isResumable,
|
|
1901
|
+
receipt,
|
|
1902
|
+
startTime
|
|
1903
|
+
);
|
|
1904
|
+
if (!shouldProcessResult.success) {
|
|
1905
|
+
return shouldProcessResult;
|
|
1906
|
+
}
|
|
1896
1907
|
let result;
|
|
1897
1908
|
let error;
|
|
1898
1909
|
let context;
|
|
1899
|
-
const isResumable = isResumableSubscriber(subscriber);
|
|
1900
1910
|
let existingCheckpoint;
|
|
1901
1911
|
if (isResumable) {
|
|
1902
1912
|
existingCheckpoint = await this.checkpointStore.get(envelope.id);
|
|
@@ -1972,10 +1982,65 @@ var ProcessingPipeline = class {
|
|
|
1972
1982
|
subscriber: subscriberDef,
|
|
1973
1983
|
receipt
|
|
1974
1984
|
});
|
|
1985
|
+
return await this.handleFailure(
|
|
1986
|
+
error,
|
|
1987
|
+
decision,
|
|
1988
|
+
envelope,
|
|
1989
|
+
subscriberDef,
|
|
1990
|
+
isResumable,
|
|
1991
|
+
receipt,
|
|
1992
|
+
durationMs
|
|
1993
|
+
);
|
|
1994
|
+
}
|
|
1995
|
+
/**
|
|
1996
|
+
* Runs the retry policy's pre-processing check.
|
|
1997
|
+
*
|
|
1998
|
+
* This returns a terminal `ProcessResult` if the message should be skipped (dead-lettered/discarded) without ever invoking the callback,
|
|
1999
|
+
* or a success result if shouldProcess passes and we should continue processing the message.
|
|
2000
|
+
* @param envelope - The message envelope
|
|
2001
|
+
* @param subscriberDef - The subscriber definition
|
|
2002
|
+
* @param isResumable - Whether the subscriber is resumable
|
|
2003
|
+
* @param receipt - The message receipt
|
|
2004
|
+
* @param startTime - The start time of the processing
|
|
2005
|
+
* @returns A `ProcessResult` indicating if we should continue with processing the message, or fail it preemptively
|
|
2006
|
+
*/
|
|
2007
|
+
async handleShouldProcess(envelope, subscriberDef, isResumable, receipt, startTime) {
|
|
2008
|
+
const decision = this.retryPolicy.shouldProcess({ envelope, receipt });
|
|
2009
|
+
if (decision.action === "process") {
|
|
2010
|
+
return {
|
|
2011
|
+
success: true,
|
|
2012
|
+
durationMs: 0
|
|
2013
|
+
};
|
|
2014
|
+
}
|
|
2015
|
+
const durationMs = performance.now() - startTime;
|
|
2016
|
+
const error = new Error(decision.reason);
|
|
2017
|
+
return await this.handleFailure(
|
|
2018
|
+
error,
|
|
2019
|
+
decision,
|
|
2020
|
+
envelope,
|
|
2021
|
+
subscriberDef,
|
|
2022
|
+
isResumable,
|
|
2023
|
+
receipt,
|
|
2024
|
+
durationMs
|
|
2025
|
+
);
|
|
2026
|
+
}
|
|
2027
|
+
/**
|
|
2028
|
+
* Performs failure handling and cleanup based on retry decision (e.g. dead-letter).
|
|
2029
|
+
*
|
|
2030
|
+
* @param error - The error from the processing attempt
|
|
2031
|
+
* @param decision - The decision from executing the appropriate retry policy check
|
|
2032
|
+
* @param envelope - The message envelope
|
|
2033
|
+
* @param subscriberDef - The subscriber definition
|
|
2034
|
+
* @param isResumable - Whether the subscriber is resumable
|
|
2035
|
+
* @param receipt - The message receipt
|
|
2036
|
+
* @param durationMs - The duration of processing
|
|
2037
|
+
* @returns A `ProcessResult` indicating failure
|
|
2038
|
+
*/
|
|
2039
|
+
async handleFailure(error, decision, envelope, subscriberDef, isResumable, receipt, durationMs) {
|
|
1975
2040
|
envelope.docket.lastError = error.message;
|
|
1976
2041
|
envelope.docket.firstError ??= error.message;
|
|
1977
|
-
if (decision.action === "dead-letter" &&
|
|
1978
|
-
await
|
|
2042
|
+
if (decision.action === "dead-letter" && isResumable) {
|
|
2043
|
+
await this.checkpointStore.delete(envelope.id);
|
|
1979
2044
|
await this.hooks.onCheckpointCleared?.({
|
|
1980
2045
|
envelope,
|
|
1981
2046
|
subscriber: subscriberDef,
|
|
@@ -2081,15 +2146,31 @@ var StandardRetryPolicy = class {
|
|
|
2081
2146
|
constructor(config = {}) {
|
|
2082
2147
|
this.config = { ...defaultRetryConfig, ...config };
|
|
2083
2148
|
}
|
|
2149
|
+
/**
|
|
2150
|
+
* Check if the message should be dead-lettered before the subscriber callback is invoked
|
|
2151
|
+
*
|
|
2152
|
+
* Uses the same delivery-count threshold as `shouldRetry` poison check, so an
|
|
2153
|
+
* already-poisoned message is dead-lettered without ever running the callback again.
|
|
2154
|
+
*
|
|
2155
|
+
* @param context - The shouldProcess context.
|
|
2156
|
+
* @returns A 'process' decision if the message is not poisoned; a dead-letter decision otherwise.
|
|
2157
|
+
*/
|
|
2158
|
+
shouldProcess(context) {
|
|
2159
|
+
const poisonError = this.checkPoisoned(context.envelope, context.receipt);
|
|
2160
|
+
if (poisonError) {
|
|
2161
|
+
return {
|
|
2162
|
+
action: "dead-letter",
|
|
2163
|
+
queue: "undeliverable",
|
|
2164
|
+
reason: poisonError.message
|
|
2165
|
+
};
|
|
2166
|
+
}
|
|
2167
|
+
return { action: "process" };
|
|
2168
|
+
}
|
|
2084
2169
|
shouldRetry(context) {
|
|
2085
2170
|
const { envelope, error, subscriber, receipt } = context;
|
|
2086
2171
|
const errorMessage = error.message;
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
envelope.id,
|
|
2090
|
-
receipt.deliveryCount,
|
|
2091
|
-
this.config.maxDeliveries
|
|
2092
|
-
);
|
|
2172
|
+
const poisonError = this.checkPoisoned(envelope, receipt);
|
|
2173
|
+
if (poisonError) {
|
|
2093
2174
|
return {
|
|
2094
2175
|
action: "dead-letter",
|
|
2095
2176
|
queue: "undeliverable",
|
|
@@ -2151,6 +2232,23 @@ var StandardRetryPolicy = class {
|
|
|
2151
2232
|
const delay = this.config.baseDelay * this.config.backoffMultiplier ** (attempt - 1);
|
|
2152
2233
|
return Math.min(delay, this.config.maxDelay);
|
|
2153
2234
|
}
|
|
2235
|
+
/**
|
|
2236
|
+
* Checks the native delivery count against the poison threshold
|
|
2237
|
+
*
|
|
2238
|
+
* @param envelope - The message envelope.
|
|
2239
|
+
* @param receipt - The message receipt.
|
|
2240
|
+
* @returns A MessageMaybePoisonedError if the message is poisoned; null otherwise
|
|
2241
|
+
*/
|
|
2242
|
+
checkPoisoned(envelope, receipt) {
|
|
2243
|
+
if (receipt.deliveryCount >= this.config.maxDeliveries) {
|
|
2244
|
+
return new MessageMaybePoisonedError(
|
|
2245
|
+
envelope.id,
|
|
2246
|
+
receipt.deliveryCount,
|
|
2247
|
+
this.config.maxDeliveries
|
|
2248
|
+
);
|
|
2249
|
+
}
|
|
2250
|
+
return null;
|
|
2251
|
+
}
|
|
2154
2252
|
};
|
|
2155
2253
|
|
|
2156
2254
|
// src/schema/types.ts
|
|
@@ -2461,6 +2559,7 @@ var Matador = class {
|
|
|
2461
2559
|
"[Matador] \u{1F7E1} Worker not subscribing to any queues (consumeFrom is empty)."
|
|
2462
2560
|
);
|
|
2463
2561
|
}
|
|
2562
|
+
this.started = true;
|
|
2464
2563
|
for (const queueName of this.consumeFrom) {
|
|
2465
2564
|
const qualifiedName = resolveTargetQueueName(this.topology, queueName);
|
|
2466
2565
|
const queueDef = findQueueDefinition(this.topology, queueName);
|
|
@@ -2479,7 +2578,6 @@ var Matador = class {
|
|
|
2479
2578
|
);
|
|
2480
2579
|
this.subscriptions.push(subscription);
|
|
2481
2580
|
}
|
|
2482
|
-
this.started = true;
|
|
2483
2581
|
}
|
|
2484
2582
|
async send(eventOrClass, dataOrOptions, maybeOptions) {
|
|
2485
2583
|
if (!this.started) {
|
|
@@ -3626,6 +3724,9 @@ var RabbitMQTransport = class {
|
|
|
3626
3724
|
if (queueDef.consumerTimeout) {
|
|
3627
3725
|
queueOptions.arguments["x-consumer-timeout"] = queueDef.consumerTimeout;
|
|
3628
3726
|
}
|
|
3727
|
+
if (queueDef.singleActiveConsumer) {
|
|
3728
|
+
queueOptions.arguments["x-single-active-consumer"] = true;
|
|
3729
|
+
}
|
|
3629
3730
|
return queueOptions;
|
|
3630
3731
|
}
|
|
3631
3732
|
async assertWorkQueue(channel, topology, queueDef) {
|