@zdavison/matador 4.0.2 → 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.
Files changed (49) hide show
  1. package/dist/core/fanout.d.ts +51 -0
  2. package/dist/core/fanout.d.ts.map +1 -1
  3. package/dist/core/fanout.js +141 -28
  4. package/dist/core/fanout.test.js +533 -7
  5. package/dist/core/matador.d.ts +33 -0
  6. package/dist/core/matador.d.ts.map +1 -1
  7. package/dist/core/matador.js +9 -2
  8. package/dist/core/matador.test.js +336 -7
  9. package/dist/errors/index.d.ts +1 -1
  10. package/dist/errors/index.d.ts.map +1 -1
  11. package/dist/errors/index.js +2 -2
  12. package/dist/errors/matador-errors.d.ts +10 -8
  13. package/dist/errors/matador-errors.d.ts.map +1 -1
  14. package/dist/errors/matador-errors.js +18 -14
  15. package/dist/index.cjs +326 -92
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +2 -2
  21. package/dist/index.js.map +1 -1
  22. package/dist/pipeline/pipeline.d.ts +30 -2
  23. package/dist/pipeline/pipeline.d.ts.map +1 -1
  24. package/dist/pipeline/pipeline.js +61 -9
  25. package/dist/pipeline/pipeline.test.js +123 -0
  26. package/dist/retry/index.d.ts +1 -1
  27. package/dist/retry/index.d.ts.map +1 -1
  28. package/dist/retry/policy.d.ts +25 -0
  29. package/dist/retry/policy.d.ts.map +1 -1
  30. package/dist/retry/standard-policy.d.ts +19 -1
  31. package/dist/retry/standard-policy.d.ts.map +1 -1
  32. package/dist/retry/standard-policy.js +39 -2
  33. package/dist/retry/standard-policy.test.js +65 -0
  34. package/dist/topology/builder.test.js +7 -0
  35. package/dist/topology/types.d.ts +14 -0
  36. package/dist/topology/types.d.ts.map +1 -1
  37. package/dist/transport/local/local-transport.d.ts +6 -0
  38. package/dist/transport/local/local-transport.d.ts.map +1 -1
  39. package/dist/transport/local/local-transport.js +35 -18
  40. package/dist/transport/local/local-transport.test.js +40 -4
  41. package/dist/transport/multi/multi-transport.d.ts.map +1 -1
  42. package/dist/transport/multi/multi-transport.js +3 -0
  43. package/dist/transport/multi/multi-transport.test.js +82 -0
  44. package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -1
  45. package/dist/transport/rabbitmq/rabbitmq-transport.js +34 -27
  46. package/dist/transport/rabbitmq/rabbitmq-transport.test.js +28 -0
  47. package/dist/transport/transport.d.ts +6 -0
  48. package/dist/transport/transport.d.ts.map +1 -1
  49. package/package.json +1 -1
@@ -66,6 +66,10 @@ export class Matador {
66
66
  hooks: this.hooks,
67
67
  topology: this.topology,
68
68
  defaultQueue,
69
+ maxRetryBufferSize: config.maxRetryBufferSize,
70
+ maxRetryAttempts: config.maxRetryAttempts,
71
+ retryIntervalMs: config.retryIntervalMs,
72
+ maxConsecutiveFlushFailures: config.maxConsecutiveFlushFailures,
69
73
  });
70
74
  // Create shutdown manager
71
75
  this.shutdownManager = new ShutdownManager(() => this.fanout.eventsBeingEnqueuedCount, () => this.unsubscribeAll(), () => this.transport.disconnect(), config.shutdownConfig);
@@ -137,6 +141,10 @@ export class Matador {
137
141
  else {
138
142
  this.hooks.logger.info('[Matador] 🟡 Worker not subscribing to any queues (consumeFrom is empty).');
139
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;
140
148
  for (const queueName of this.consumeFrom) {
141
149
  const qualifiedName = resolveTargetQueueName(this.topology, queueName);
142
150
  const queueDef = findQueueDefinition(this.topology, queueName);
@@ -154,7 +162,6 @@ export class Matador {
154
162
  : undefined);
155
163
  this.subscriptions.push(subscription);
156
164
  }
157
- this.started = true;
158
165
  }
159
166
  async send(eventOrClass, dataOrOptions, maybeOptions) {
160
167
  if (!this.started) {
@@ -247,7 +254,7 @@ export class Matador {
247
254
  }
248
255
  async unsubscribeAll() {
249
256
  for (const subscription of this.subscriptions) {
250
- await subscription.unsubscribe();
257
+ await (subscription.pauseForShutdown ?? subscription.unsubscribe)();
251
258
  }
252
259
  this.subscriptions.length = 0;
253
260
  }
@@ -1,8 +1,8 @@
1
1
  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
- import { LocalTransport } from '../transport/local/local-transport.js';
5
- import { MatadorEvent, createSubscriber } from '../types/index.js';
4
+ import { LocalTransport, MultiTransport } from '../transport/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;
@@ -22,6 +22,24 @@ class OrderPlacedEvent extends MatadorEvent {
22
22
  this.data = data;
23
23
  }
24
24
  }
25
+ class StepOneEvent extends MatadorEvent {
26
+ data;
27
+ static key = 'step.one';
28
+ static description = 'First step of a chain';
29
+ constructor(data) {
30
+ super();
31
+ this.data = data;
32
+ }
33
+ }
34
+ class StepTwoEvent extends MatadorEvent {
35
+ data;
36
+ static key = 'step.two';
37
+ static description = 'Follow-up step dispatched from within step one';
38
+ constructor(data) {
39
+ super();
40
+ this.data = data;
41
+ }
42
+ }
25
43
  describe('Matador', () => {
26
44
  let transport;
27
45
  let matador;
@@ -208,7 +226,12 @@ describe('Matador', () => {
208
226
  const schema = {
209
227
  [UserCreatedEvent.key]: [UserCreatedEvent, [subscriber]],
210
228
  };
211
- matador = new Matador({ transport, topology, schema });
229
+ matador = new Matador({
230
+ transport,
231
+ topology,
232
+ schema,
233
+ consumeFrom: ['events'],
234
+ });
212
235
  await matador.start();
213
236
  const event = new UserCreatedEvent({
214
237
  userId: '123',
@@ -231,7 +254,12 @@ describe('Matador', () => {
231
254
  const schema = {
232
255
  [UserCreatedEvent.key]: [UserCreatedEvent, [subscriber]],
233
256
  };
234
- matador = new Matador({ transport, topology, schema });
257
+ matador = new Matador({
258
+ transport,
259
+ topology,
260
+ schema,
261
+ consumeFrom: ['events'],
262
+ });
235
263
  await matador.start();
236
264
  // Use the shorthand: send(EventClass, data)
237
265
  const result = await matador.send(UserCreatedEvent, {
@@ -264,7 +292,12 @@ describe('Matador', () => {
264
292
  const schema = {
265
293
  [UserCreatedEvent.key]: [UserCreatedEvent, [sub1, sub2, sub3]],
266
294
  };
267
- matador = new Matador({ transport, topology, schema });
295
+ matador = new Matador({
296
+ transport,
297
+ topology,
298
+ schema,
299
+ consumeFrom: ['events'],
300
+ });
268
301
  await matador.start();
269
302
  const event = new UserCreatedEvent({
270
303
  userId: '123',
@@ -286,7 +319,12 @@ describe('Matador', () => {
286
319
  const schema = {
287
320
  [UserCreatedEvent.key]: [UserCreatedEvent, [subscriber]],
288
321
  };
289
- matador = new Matador({ transport, topology, schema });
322
+ matador = new Matador({
323
+ transport,
324
+ topology,
325
+ schema,
326
+ consumeFrom: ['events'],
327
+ });
290
328
  await matador.start();
291
329
  const event = new UserCreatedEvent({
292
330
  userId: '123',
@@ -481,8 +519,19 @@ describe('Matador', () => {
481
519
  const schema = {
482
520
  [UserCreatedEvent.key]: [UserCreatedEvent, [subscriber]],
483
521
  };
484
- matador = new Matador({ transport, topology, schema });
522
+ matador = new Matador({
523
+ transport,
524
+ topology,
525
+ schema,
526
+ consumeFrom: ['events'],
527
+ });
485
528
  await matador.start();
529
+ // stopReceiving() unsubscribes this instance's own handler. With a real
530
+ // broker, the queue would still exist independently (another replica,
531
+ // or this instance after restart, could pick the message up) — simulate
532
+ // that here with a subscription outside Matador's own lifecycle so
533
+ // LocalTransport still has somewhere to deliver to.
534
+ await transport.subscribe('matador.test.events', async () => { });
486
535
  await matador.stopReceiving();
487
536
  const event = new UserCreatedEvent({
488
537
  userId: '123',
@@ -574,4 +623,284 @@ describe('Matador', () => {
574
623
  expect(matador.isConnected()).toBe(true);
575
624
  });
576
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
+ });
719
+ describe('self-dispatch during graceful shutdown (stopReceiving)', () => {
720
+ it('LOCAL transport only: a self-dispatched follow-up should still be delivered after stopReceiving()', async () => {
721
+ const topology = TopologyBuilder.create()
722
+ .withNamespace('test')
723
+ .addQueue('events')
724
+ .build();
725
+ const received = [];
726
+ let releaseGate;
727
+ const gate = new Promise((resolve) => {
728
+ releaseGate = resolve;
729
+ });
730
+ let markStarted;
731
+ const started = new Promise((resolve) => {
732
+ markStarted = resolve;
733
+ });
734
+ const stepOneSub = createSubscriber({
735
+ name: 'step-one-handler',
736
+ description: 'Handles step one',
737
+ callback: async (envelope, context) => {
738
+ markStarted?.();
739
+ await gate;
740
+ await context.matador.send(StepTwoEvent, { id: envelope.data.id });
741
+ },
742
+ });
743
+ const stepTwoSub = createSubscriber({
744
+ name: 'step-two-handler',
745
+ description: 'Handles step two',
746
+ callback: async (envelope) => {
747
+ received.push(envelope.data.id);
748
+ },
749
+ });
750
+ const schema = {
751
+ [StepOneEvent.key]: [StepOneEvent, [stepOneSub]],
752
+ [StepTwoEvent.key]: [StepTwoEvent, [stepTwoSub]],
753
+ };
754
+ matador = new Matador({
755
+ transport,
756
+ topology,
757
+ schema,
758
+ consumeFrom: ['events'],
759
+ });
760
+ await matador.start();
761
+ void matador.send(StepOneEvent, { id: 'abc' });
762
+ await started;
763
+ // Graceful shutdown begins while step-one is still in flight.
764
+ await matador.stopReceiving();
765
+ // The in-flight handler now self-dispatches its follow-up event.
766
+ releaseGate?.();
767
+ await new Promise((resolve) => setTimeout(resolve, 50));
768
+ // LocalTransport is process-local — no other consumer could ever pick
769
+ // this up, so it should still be delivered here.
770
+ expect(received).toEqual(['abc']);
771
+ });
772
+ it('MULTI transport (external primary + local fallback): a dispatched event that falls back to local should still be delivered after stopReceiving()', async () => {
773
+ const topology = TopologyBuilder.create()
774
+ .withNamespace('test')
775
+ .addQueue('events')
776
+ .build();
777
+ const received = [];
778
+ let releaseGate;
779
+ const gate = new Promise((resolve) => {
780
+ releaseGate = resolve;
781
+ });
782
+ let markStarted;
783
+ const started = new Promise((resolve) => {
784
+ markStarted = resolve;
785
+ });
786
+ // Always-failing "external" transport, forcing every send to fall
787
+ // back to the local transport below.
788
+ const externalMock = {
789
+ name: 'rabbitmq',
790
+ capabilities: {
791
+ deliveryModes: ['at-least-once'],
792
+ delayedMessages: false,
793
+ deadLetterRouting: 'native',
794
+ attemptTracking: true,
795
+ concurrencyModel: 'prefetch',
796
+ ordering: 'none',
797
+ priorities: false,
798
+ },
799
+ isConnected: () => true,
800
+ connect: mock(async () => { }),
801
+ disconnect: mock(async () => { }),
802
+ send: mock(async () => {
803
+ throw new Error('RabbitMQ down');
804
+ }),
805
+ subscribe: mock(async () => ({
806
+ unsubscribe: mock(async () => { }),
807
+ isActive: true,
808
+ })),
809
+ applyTopology: mock(async () => { }),
810
+ complete: mock(async () => { }),
811
+ };
812
+ const localTransport = new LocalTransport();
813
+ const multiTransport = new MultiTransport({
814
+ transports: [externalMock, localTransport],
815
+ });
816
+ const stepOneSub = createSubscriber({
817
+ name: 'step-one-handler',
818
+ description: 'Handles step one',
819
+ callback: async (envelope, context) => {
820
+ markStarted?.();
821
+ await gate;
822
+ await context.matador.send(StepTwoEvent, { id: envelope.data.id });
823
+ },
824
+ });
825
+ const stepTwoSub = createSubscriber({
826
+ name: 'step-two-handler',
827
+ description: 'Handles step two',
828
+ callback: async (envelope) => {
829
+ received.push(envelope.data.id);
830
+ },
831
+ });
832
+ const schema = {
833
+ [StepOneEvent.key]: [StepOneEvent, [stepOneSub]],
834
+ [StepTwoEvent.key]: [StepTwoEvent, [stepTwoSub]],
835
+ };
836
+ matador = new Matador({
837
+ transport: multiTransport,
838
+ topology,
839
+ schema,
840
+ consumeFrom: ['events'],
841
+ });
842
+ await matador.start();
843
+ void matador.send(StepOneEvent, { id: 'abc' });
844
+ await started;
845
+ await matador.stopReceiving();
846
+ releaseGate?.();
847
+ await new Promise((resolve) => setTimeout(resolve, 50));
848
+ // The follow-up falls back to the local transport. Local is
849
+ // process-local — no other consumer could ever pick it up — so it
850
+ // should still be delivered here, even though the external transport's
851
+ // own subscription was (correctly) cut off immediately.
852
+ expect(received).toEqual(['abc']);
853
+ });
854
+ it('RABBIT-like (external) transport only: send() after stopReceiving() should still succeed regardless of local subscription state', async () => {
855
+ const topology = TopologyBuilder.create()
856
+ .withNamespace('test')
857
+ .addQueue('events')
858
+ .build();
859
+ const subscriber = createSubscriber({
860
+ name: 'step-one-handler',
861
+ description: 'Handles step one',
862
+ callback: async () => { },
863
+ });
864
+ const schema = {
865
+ [StepOneEvent.key]: [StepOneEvent, [subscriber]],
866
+ };
867
+ const externalTransport = {
868
+ name: 'rabbitmq',
869
+ capabilities: {
870
+ deliveryModes: ['at-least-once'],
871
+ delayedMessages: false,
872
+ deadLetterRouting: 'native',
873
+ attemptTracking: true,
874
+ concurrencyModel: 'prefetch',
875
+ ordering: 'none',
876
+ priorities: false,
877
+ },
878
+ isConnected: () => true,
879
+ connect: mock(async () => { }),
880
+ disconnect: mock(async () => { }),
881
+ send: mock(async () => 'rabbitmq'),
882
+ subscribe: mock(async () => ({
883
+ unsubscribe: mock(async () => { }),
884
+ isActive: true,
885
+ })),
886
+ applyTopology: mock(async () => { }),
887
+ complete: mock(async () => { }),
888
+ };
889
+ matador = new Matador({
890
+ transport: externalTransport,
891
+ topology,
892
+ schema,
893
+ consumeFrom: ['events'],
894
+ });
895
+ await matador.start();
896
+ await matador.stopReceiving();
897
+ const result = await matador.send(StepOneEvent, { id: 'xyz' });
898
+ // A real broker doesn't need an active local consumer to accept a
899
+ // publish — another replica (or this instance, once it stops
900
+ // pretending to be shut down) could still consume it later. This case
901
+ // is not part of the bug — included as a contrast baseline.
902
+ expect(result.subscribersSent).toBe(1);
903
+ expect(result.errors).toHaveLength(0);
904
+ });
905
+ });
577
906
  });
@@ -1,6 +1,6 @@
1
1
  export type { HasDescription } from './has-description.js';
2
2
  export { hasDescription } from './has-description.js';
3
3
  export { assertEvent, DontRetry, DoRetry, EventAssertionError, isAssertionError, isDontRetry, isDoRetry, RetryControlError, } from './retry-errors.js';
4
- export { MatadorError, isMatadorError, NotStartedError, isNotStartedError, ShutdownInProgressError, TransportNotConnectedError, isTransportNotConnectedError, TransportClosedError, TransportSendError, SomeSendError, AllTransportsFailedError, DelayedMessagesNotSupportedError, EventNotRegisteredError, isEventNotRegisteredError, SubscriberNotRegisteredError, isSubscriberNotRegisteredError, NoSubscribersExistError, InvalidSchemaError, SubscriberIsStubError, LocalTransportCannotProcessStubError, QueueNotFoundError, UnknownQueueReferenceError, isUnknownQueueReferenceError, InvalidEventError, MessageMaybePoisonedError, isMessageMaybePoisonedError, IdempotentMessageCannotRetryError, isIdempotentMessageCannotRetryError, TimeoutError, } from './matador-errors.js';
4
+ export { MatadorError, isMatadorError, NotStartedError, isNotStartedError, ShutdownInProgressError, TransportNotConnectedError, isTransportNotConnectedError, TransportClosedError, TransportSendError, SomeSendError, AllTransportsFailedError, DelayedMessagesNotSupportedError, LocalTransportNoActiveSubscriberError, EventNotRegisteredError, isEventNotRegisteredError, SubscriberNotRegisteredError, isSubscriberNotRegisteredError, NoSubscribersExistError, InvalidSchemaError, SubscriberIsStubError, QueueNotFoundError, UnknownQueueReferenceError, isUnknownQueueReferenceError, InvalidEventError, MessageMaybePoisonedError, isMessageMaybePoisonedError, IdempotentMessageCannotRetryError, isIdempotentMessageCannotRetryError, TimeoutError, } from './matador-errors.js';
5
5
  export { CheckpointStoreError, DuplicateIoKeyError, isCheckpointStoreError, isDuplicateIoKeyError, } from './checkpoint-errors.js';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EACL,WAAW,EACX,SAAS,EACT,OAAO,EACP,mBAAmB,EACnB,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAEL,YAAY,EACZ,cAAc,EAEd,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EAEvB,0BAA0B,EAC1B,4BAA4B,EAC5B,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,wBAAwB,EACxB,gCAAgC,EAEhC,uBAAuB,EACvB,yBAAyB,EACzB,4BAA4B,EAC5B,8BAA8B,EAC9B,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,EACrB,oCAAoC,EAEpC,kBAAkB,EAClB,0BAA0B,EAC1B,4BAA4B,EAE5B,iBAAiB,EAEjB,yBAAyB,EACzB,2BAA2B,EAC3B,iCAAiC,EACjC,mCAAmC,EAEnC,YAAY,GACb,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAEL,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EACL,WAAW,EACX,SAAS,EACT,OAAO,EACP,mBAAmB,EACnB,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAEL,YAAY,EACZ,cAAc,EAEd,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EAEvB,0BAA0B,EAC1B,4BAA4B,EAC5B,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,wBAAwB,EACxB,gCAAgC,EAChC,qCAAqC,EAErC,uBAAuB,EACvB,yBAAyB,EACzB,4BAA4B,EAC5B,8BAA8B,EAC9B,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,EAErB,kBAAkB,EAClB,0BAA0B,EAC1B,4BAA4B,EAE5B,iBAAiB,EAEjB,yBAAyB,EACzB,2BAA2B,EAC3B,iCAAiC,EACjC,mCAAmC,EAEnC,YAAY,GACb,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAEL,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC"}
@@ -6,9 +6,9 @@ MatadorError, isMatadorError,
6
6
  // Lifecycle errors
7
7
  NotStartedError, isNotStartedError, ShutdownInProgressError,
8
8
  // Transport errors
9
- TransportNotConnectedError, isTransportNotConnectedError, TransportClosedError, TransportSendError, SomeSendError, AllTransportsFailedError, DelayedMessagesNotSupportedError,
9
+ TransportNotConnectedError, isTransportNotConnectedError, TransportClosedError, TransportSendError, SomeSendError, AllTransportsFailedError, DelayedMessagesNotSupportedError, LocalTransportNoActiveSubscriberError,
10
10
  // Schema & configuration errors
11
- EventNotRegisteredError, isEventNotRegisteredError, SubscriberNotRegisteredError, isSubscriberNotRegisteredError, NoSubscribersExistError, InvalidSchemaError, SubscriberIsStubError, LocalTransportCannotProcessStubError,
11
+ EventNotRegisteredError, isEventNotRegisteredError, SubscriberNotRegisteredError, isSubscriberNotRegisteredError, NoSubscribersExistError, InvalidSchemaError, SubscriberIsStubError,
12
12
  // Queue errors
13
13
  QueueNotFoundError, UnknownQueueReferenceError, isUnknownQueueReferenceError,
14
14
  // Event validation errors
@@ -98,6 +98,16 @@ export declare class DelayedMessagesNotSupportedError extends MatadorError {
98
98
  readonly description: string;
99
99
  constructor(transportName: string);
100
100
  }
101
+ /**
102
+ * Thrown when a LocalTransport is sent a message for a queue with no active
103
+ * subscriber in the current process (e.g. the subscriber is a SubscriberStub
104
+ * implemented in another service, or matador.start() hasn't subscribed yet).
105
+ */
106
+ export declare class LocalTransportNoActiveSubscriberError extends MatadorError {
107
+ readonly queue: string;
108
+ readonly description: string;
109
+ constructor(queue: string);
110
+ }
101
111
  /**
102
112
  * Thrown when an event is not registered in the schema.
103
113
  */
@@ -139,14 +149,6 @@ export declare class SubscriberIsStubError extends MatadorError {
139
149
  readonly description: string;
140
150
  constructor(subscriberName: string);
141
151
  }
142
- /**
143
- * Thrown when a LocalTransport tries to process a stub subscriber.
144
- */
145
- export declare class LocalTransportCannotProcessStubError extends MatadorError {
146
- readonly subscriberName: string;
147
- readonly description: string;
148
- constructor(subscriberName: string);
149
- }
150
152
  /**
151
153
  * Thrown when a subscriber's `targetQueue` or a `consumeFrom` entry references
152
154
  * a queue that was never declared in the topology.
@@ -1 +1 @@
1
- {"version":3,"file":"matador-errors.d.ts","sourceRoot":"","sources":["../../src/errors/matador-errors.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"matador-errors.d.ts","sourceRoot":"","sources":["../../src/errors/matador-errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,8BAAsB,YAAa,SAAQ,KAAK;IAC9C;;;OAGG;IACH,SAAiB,IAAI,EAAE,MAAM,CAAC;IAE9B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBAE1B,OAAO,EAAE,MAAM;IAY3B;;OAEG;IACH,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAQlC;AAMD;;GAEG;AACH,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,QAAQ,CAAC,WAAW,SAGkD;gBAE1D,SAAS,SAAc;CAKpC;AAED;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,YAAY;IACvD,QAAQ,CAAC,WAAW,SAI4C;;CAKjE;AAMD;;GAEG;AACH,qBAAa,0BAA2B,SAAQ,YAAY;aAQxC,aAAa,EAAE,MAAM;IAPvC,QAAQ,CAAC,WAAW,SAI+C;gBAGjD,aAAa,EAAE,MAAM,EACrC,SAAS,SAAc;CAM1B;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,YAAY;aAOxB,aAAa,EAAE,MAAM;IANjD,QAAQ,CAAC,WAAW,SAIkC;gBAE1B,aAAa,EAAE,MAAM;CAKlD;AAED;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,YAAY;aAStC,KAAK,EAAE,MAAM;aACb,MAAM,EAAE,SAAS,KAAK,EAAE;IAT1C,QAAQ,CAAC,WAAW,SAK4C;gBAG9C,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,SAAS,KAAK,EAAE;CAO3C;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;aAUhC,KAAK,EAAE,MAAM;aACb,KAAK,EAAE,KAAK;IAV9B,QAAQ,CAAC,WAAW,SAM+C;gBAGjD,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,KAAK;IAKrB,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAU3C;AAED;;;GAGG;AACH,qBAAa,aAAc,SAAQ,YAAY;aAO3B,QAAQ,EAAE,MAAM;aAChB,MAAM,EAAE,aAAa,CAAC;QACpC,cAAc,EAAE,MAAM,CAAC;QACvB,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,KAAK,CAAC;KACd,CAAC;IAXJ,QAAQ,CAAC,WAAW,SAG6D;gBAG/D,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,aAAa,CAAC;QACpC,cAAc,EAAE,MAAM,CAAC;QACvB,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,KAAK,CAAC;KACd,CAAC;CASL;AAED;;GAEG;AACH,qBAAa,gCAAiC,SAAQ,YAAY;aAQpC,aAAa,EAAE,MAAM;IAPjD,QAAQ,CAAC,WAAW,SAKkE;gBAE1D,aAAa,EAAE,MAAM;CAMlD;AAED;;;;GAIG;AACH,qBAAa,qCAAsC,SAAQ,YAAY;aASzC,KAAK,EAAE,MAAM;IARzC,QAAQ,CAAC,WAAW,SAM6C;gBAErC,KAAK,EAAE,MAAM;CAK1C;AAMD;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,YAAY;aAO3B,QAAQ,EAAE,MAAM;IAN5C,QAAQ,CAAC,WAAW,SAI0D;gBAElD,QAAQ,EAAE,MAAM;CAK7C;AAED;;GAEG;AACH,qBAAa,4BAA6B,SAAQ,YAAY;aAU1C,cAAc,EAAE,MAAM;aACtB,QAAQ,CAAC,EAAE,MAAM;IAVnC,QAAQ,CAAC,WAAW,SAMmC;gBAGrC,cAAc,EAAE,MAAM,EACtB,QAAQ,CAAC,EAAE,MAAM,YAAA;CAMpC;AAED;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,YAAY;aAO3B,QAAQ,EAAE,MAAM;IAN5C,QAAQ,CAAC,WAAW,SAIiE;gBAEzD,QAAQ,EAAE,MAAM;CAK7C;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;aAWhC,KAAK,CAAC,EAAE,MAAM;IAVhC,QAAQ,CAAC,WAAW,SAM+B;gBAGjD,OAAO,EAAE,MAAM,EACC,KAAK,CAAC,EAAE,MAAM,YAAA;CAIjC;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,YAAY;aAQzB,cAAc,EAAE,MAAM;IAPlD,QAAQ,CAAC,WAAW,SAK4D;gBAEpD,cAAc,EAAE,MAAM;CAKnD;AAMD;;;GAGG;AACH,qBAAa,0BAA2B,SAAQ,YAAY;aAW9B,SAAS,EAAE,MAAM;IAV7C,QAAQ,CAAC,WAAW,SAQH;gBAEW,SAAS,EAAE,MAAM;CAK9C;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;aAOtB,SAAS,EAAE,MAAM;IAN7C,QAAQ,CAAC,WAAW,SAI2C;gBAEnC,SAAS,EAAE,MAAM;CAK9C;AAMD;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,YAAY;aAU/B,KAAK,CAAC,EAAE,MAAM;IAThC,QAAQ,CAAC,WAAW,SAKqC;gBAGvD,OAAO,EAAE,MAAM,EACC,KAAK,CAAC,EAAE,MAAM,YAAA;CAIjC;AAMD;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,YAAY;aAWvC,OAAO,EAAE,MAAM;aACf,aAAa,EAAE,MAAM;aACrB,aAAa,EAAE,MAAM;IAZvC,QAAQ,CAAC,WAAW,SAOyC;gBAG3C,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM;CAMxC;AAED;;GAEG;AACH,qBAAa,iCAAkC,SAAQ,YAAY;aAU/C,OAAO,EAAE,MAAM;aACf,cAAc,EAAE,MAAM;IAVxC,QAAQ,CAAC,WAAW,SAMqD;gBAGvD,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM;CAMzC;AAMD;;GAEG;AACH,qBAAa,YAAa,SAAQ,YAAY;aAS1B,SAAS,EAAE,MAAM;aACjB,SAAS,EAAE,MAAM;IATnC,QAAQ,CAAC,WAAW,SAKyC;gBAG3C,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM;CAIpC;AAMD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAEpE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAE1E;AAED,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,0BAA0B,CAErC;AAED,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,uBAAuB,CAElC;AAED,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,4BAA4B,CAEvC;AAED,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,0BAA0B,CAErC;AAED,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,yBAAyB,CAEpC;AAED,wBAAgB,mCAAmC,CACjD,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,iCAAiC,CAE5C"}
@@ -168,6 +168,24 @@ export class DelayedMessagesNotSupportedError extends MatadorError {
168
168
  this.transportName = transportName;
169
169
  }
170
170
  }
171
+ /**
172
+ * Thrown when a LocalTransport is sent a message for a queue with no active
173
+ * subscriber in the current process (e.g. the subscriber is a SubscriberStub
174
+ * implemented in another service, or matador.start() hasn't subscribed yet).
175
+ */
176
+ export class LocalTransportNoActiveSubscriberError extends MatadorError {
177
+ queue;
178
+ description = 'The LocalTransport has no active subscriber for this queue in the current process, ' +
179
+ 'so it cannot guarantee delivery. ' +
180
+ 'ACTION: If the subscriber is a SubscriberStub (implemented in another service), ' +
181
+ 'the local transport can never deliver it locally — a distributed transport like ' +
182
+ 'RabbitMQ is required. If the subscriber should run in this process, ensure ' +
183
+ 'matador.start() has subscribed to this queue before sending.';
184
+ constructor(queue) {
185
+ super(`LocalTransport has no active subscriber for queue "${queue}"; message cannot be delivered.`);
186
+ this.queue = queue;
187
+ }
188
+ }
171
189
  // ============================================================================
172
190
  // Schema & Configuration Errors
173
191
  // ============================================================================
@@ -248,20 +266,6 @@ export class SubscriberIsStubError extends MatadorError {
248
266
  this.subscriberName = subscriberName;
249
267
  }
250
268
  }
251
- /**
252
- * Thrown when a LocalTransport tries to process a stub subscriber.
253
- */
254
- export class LocalTransportCannotProcessStubError extends MatadorError {
255
- subscriberName;
256
- description = 'The LocalTransport cannot process events for SubscriberStubs. ' +
257
- 'ACTION: SubscriberStubs represent remote implementations that only RabbitMQ can route. ' +
258
- 'If using LocalTransport for testing, provide mock implementations instead of stubs. ' +
259
- 'For production fallback scenarios, be aware that stub-targeted events will be dropped.';
260
- constructor(subscriberName) {
261
- super(`LocalTransport cannot process stub subscriber "${subscriberName}". Stub subscribers require a distributed transport like RabbitMQ.`);
262
- this.subscriberName = subscriberName;
263
- }
264
- }
265
269
  // ============================================================================
266
270
  // Queue Errors
267
271
  // ============================================================================