@zdavison/matador 2.0.8 → 2.0.9
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/package.json +1 -1
- package/src/index.ts +1 -0
- package/dist/checkpoint/context.d.ts +0 -59
- package/dist/checkpoint/context.d.ts.map +0 -1
- package/dist/checkpoint/context.js +0 -140
- package/dist/checkpoint/context.test.d.ts +0 -2
- package/dist/checkpoint/context.test.d.ts.map +0 -1
- package/dist/checkpoint/context.test.js +0 -424
- package/dist/checkpoint/index.d.ts +0 -7
- package/dist/checkpoint/index.d.ts.map +0 -1
- package/dist/checkpoint/index.js +0 -6
- package/dist/checkpoint/stores/memory.d.ts +0 -29
- package/dist/checkpoint/stores/memory.d.ts.map +0 -1
- package/dist/checkpoint/stores/memory.js +0 -39
- package/dist/checkpoint/stores/noop.d.ts +0 -14
- package/dist/checkpoint/stores/noop.d.ts.map +0 -1
- package/dist/checkpoint/stores/noop.js +0 -18
- package/dist/checkpoint/stores/stores.test.d.ts +0 -2
- package/dist/checkpoint/stores/stores.test.d.ts.map +0 -1
- package/dist/checkpoint/stores/stores.test.js +0 -146
- package/dist/checkpoint/types.d.ts +0 -119
- package/dist/checkpoint/types.d.ts.map +0 -1
- package/dist/checkpoint/types.js +0 -1
- package/dist/codec/codec.d.ts +0 -29
- package/dist/codec/codec.d.ts.map +0 -1
- package/dist/codec/codec.js +0 -15
- package/dist/codec/header-aware-codec.d.ts +0 -36
- package/dist/codec/header-aware-codec.d.ts.map +0 -1
- package/dist/codec/header-aware-codec.js +0 -1
- package/dist/codec/index.d.ts +0 -6
- package/dist/codec/index.d.ts.map +0 -1
- package/dist/codec/index.js +0 -3
- package/dist/codec/json-codec.d.ts +0 -13
- package/dist/codec/json-codec.d.ts.map +0 -1
- package/dist/codec/json-codec.js +0 -64
- package/dist/codec/rabbitmq-codec.d.ts +0 -28
- package/dist/codec/rabbitmq-codec.d.ts.map +0 -1
- package/dist/codec/rabbitmq-codec.js +0 -242
- package/dist/codec/rabbitmq-codec.test.d.ts +0 -2
- package/dist/codec/rabbitmq-codec.test.d.ts.map +0 -1
- package/dist/codec/rabbitmq-codec.test.js +0 -433
- package/dist/core/fanout.d.ts +0 -59
- package/dist/core/fanout.d.ts.map +0 -1
- package/dist/core/fanout.js +0 -121
- package/dist/core/fanout.test.d.ts +0 -2
- package/dist/core/fanout.test.d.ts.map +0 -1
- package/dist/core/fanout.test.js +0 -1054
- package/dist/core/index.d.ts +0 -7
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -3
- package/dist/core/matador.d.ts +0 -133
- package/dist/core/matador.d.ts.map +0 -1
- package/dist/core/matador.js +0 -228
- package/dist/core/matador.test.d.ts +0 -2
- package/dist/core/matador.test.d.ts.map +0 -1
- package/dist/core/matador.test.js +0 -446
- package/dist/core/shutdown.d.ts +0 -78
- package/dist/core/shutdown.d.ts.map +0 -1
- package/dist/core/shutdown.js +0 -111
- package/dist/core/shutdown.test.d.ts +0 -2
- package/dist/core/shutdown.test.d.ts.map +0 -1
- package/dist/core/shutdown.test.js +0 -524
- package/dist/errors/checkpoint-errors.d.ts +0 -30
- package/dist/errors/checkpoint-errors.d.ts.map +0 -1
- package/dist/errors/checkpoint-errors.js +0 -49
- package/dist/errors/has-description.d.ts +0 -18
- package/dist/errors/has-description.d.ts.map +0 -1
- package/dist/errors/has-description.js +0 -9
- package/dist/errors/index.d.ts +0 -6
- package/dist/errors/index.d.ts.map +0 -1
- package/dist/errors/index.js +0 -22
- package/dist/errors/matador-errors.d.ts +0 -183
- package/dist/errors/matador-errors.d.ts.map +0 -1
- package/dist/errors/matador-errors.js +0 -376
- package/dist/errors/retry-errors.d.ts +0 -102
- package/dist/errors/retry-errors.d.ts.map +0 -1
- package/dist/errors/retry-errors.js +0 -155
- package/dist/errors/retry-errors.test.d.ts +0 -2
- package/dist/errors/retry-errors.test.d.ts.map +0 -1
- package/dist/errors/retry-errors.test.js +0 -136
- package/dist/hooks/index.d.ts +0 -4
- package/dist/hooks/index.d.ts.map +0 -1
- package/dist/hooks/index.js +0 -2
- package/dist/hooks/safe-hooks.d.ts +0 -32
- package/dist/hooks/safe-hooks.d.ts.map +0 -1
- package/dist/hooks/safe-hooks.js +0 -120
- package/dist/hooks/types.d.ts +0 -172
- package/dist/hooks/types.d.ts.map +0 -1
- package/dist/hooks/types.js +0 -9
- package/dist/index.cjs +0 -3228
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -23
- package/dist/index.d.ts +0 -23
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -31
- package/dist/index.js.map +0 -1
- package/dist/pipeline/index.d.ts +0 -3
- package/dist/pipeline/index.d.ts.map +0 -1
- package/dist/pipeline/index.js +0 -1
- package/dist/pipeline/pipeline.d.ts +0 -63
- package/dist/pipeline/pipeline.d.ts.map +0 -1
- package/dist/pipeline/pipeline.js +0 -273
- package/dist/pipeline/pipeline.test.d.ts +0 -2
- package/dist/pipeline/pipeline.test.d.ts.map +0 -1
- package/dist/pipeline/pipeline.test.js +0 -1112
- package/dist/retry/index.d.ts +0 -4
- package/dist/retry/index.d.ts.map +0 -1
- package/dist/retry/index.js +0 -1
- package/dist/retry/policy.d.ts +0 -43
- package/dist/retry/policy.d.ts.map +0 -1
- package/dist/retry/policy.js +0 -1
- package/dist/retry/standard-policy.d.ts +0 -44
- package/dist/retry/standard-policy.d.ts.map +0 -1
- package/dist/retry/standard-policy.js +0 -102
- package/dist/retry/standard-policy.test.d.ts +0 -2
- package/dist/retry/standard-policy.test.d.ts.map +0 -1
- package/dist/retry/standard-policy.test.js +0 -196
- package/dist/schema/index.d.ts +0 -4
- package/dist/schema/index.d.ts.map +0 -1
- package/dist/schema/index.js +0 -2
- package/dist/schema/registry.d.ts +0 -63
- package/dist/schema/registry.d.ts.map +0 -1
- package/dist/schema/registry.js +0 -174
- package/dist/schema/registry.test.d.ts +0 -2
- package/dist/schema/registry.test.d.ts.map +0 -1
- package/dist/schema/registry.test.js +0 -278
- package/dist/schema/types.d.ts +0 -158
- package/dist/schema/types.d.ts.map +0 -1
- package/dist/schema/types.js +0 -74
- package/dist/schema/types.test.d.ts +0 -2
- package/dist/schema/types.test.d.ts.map +0 -1
- package/dist/schema/types.test.js +0 -242
- package/dist/topology/builder.d.ts +0 -80
- package/dist/topology/builder.d.ts.map +0 -1
- package/dist/topology/builder.js +0 -165
- package/dist/topology/builder.test.d.ts +0 -2
- package/dist/topology/builder.test.d.ts.map +0 -1
- package/dist/topology/builder.test.js +0 -360
- package/dist/topology/index.d.ts +0 -5
- package/dist/topology/index.d.ts.map +0 -1
- package/dist/topology/index.js +0 -2
- package/dist/topology/types.d.ts +0 -128
- package/dist/topology/types.d.ts.map +0 -1
- package/dist/topology/types.js +0 -28
- package/dist/transport/capabilities.d.ts +0 -66
- package/dist/transport/capabilities.d.ts.map +0 -1
- package/dist/transport/capabilities.js +0 -18
- package/dist/transport/connection-manager.d.ts +0 -95
- package/dist/transport/connection-manager.d.ts.map +0 -1
- package/dist/transport/connection-manager.js +0 -144
- package/dist/transport/index.d.ts +0 -11
- package/dist/transport/index.d.ts.map +0 -1
- package/dist/transport/index.js +0 -5
- package/dist/transport/local/local-transport.d.ts +0 -62
- package/dist/transport/local/local-transport.d.ts.map +0 -1
- package/dist/transport/local/local-transport.js +0 -241
- package/dist/transport/local/local-transport.test.d.ts +0 -2
- package/dist/transport/local/local-transport.test.d.ts.map +0 -1
- package/dist/transport/local/local-transport.test.js +0 -192
- package/dist/transport/multi/multi-transport.d.ts +0 -94
- package/dist/transport/multi/multi-transport.d.ts.map +0 -1
- package/dist/transport/multi/multi-transport.js +0 -184
- package/dist/transport/multi/multi-transport.test.d.ts +0 -2
- package/dist/transport/multi/multi-transport.test.d.ts.map +0 -1
- package/dist/transport/multi/multi-transport.test.js +0 -236
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts +0 -83
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +0 -1
- package/dist/transport/rabbitmq/rabbitmq-transport.js +0 -541
- package/dist/transport/rabbitmq/rabbitmq-transport.test.d.ts +0 -2
- package/dist/transport/rabbitmq/rabbitmq-transport.test.d.ts.map +0 -1
- package/dist/transport/rabbitmq/rabbitmq-transport.test.js +0 -100
- package/dist/transport/transport.d.ts +0 -152
- package/dist/transport/transport.d.ts.map +0 -1
- package/dist/transport/transport.js +0 -1
- package/dist/types/common.d.ts +0 -41
- package/dist/types/common.d.ts.map +0 -1
- package/dist/types/common.js +0 -12
- package/dist/types/dispatcher.d.ts +0 -14
- package/dist/types/dispatcher.d.ts.map +0 -1
- package/dist/types/dispatcher.js +0 -1
- package/dist/types/envelope.d.ts +0 -131
- package/dist/types/envelope.d.ts.map +0 -1
- package/dist/types/envelope.js +0 -99
- package/dist/types/event.d.ts +0 -92
- package/dist/types/event.d.ts.map +0 -1
- package/dist/types/event.js +0 -26
- package/dist/types/event.test.d.ts +0 -2
- package/dist/types/event.test.d.ts.map +0 -1
- package/dist/types/event.test.js +0 -130
- package/dist/types/index.d.ts +0 -11
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -4
- package/dist/types/subscriber.d.ts +0 -227
- package/dist/types/subscriber.d.ts.map +0 -1
- package/dist/types/subscriber.js +0 -97
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it } from 'bun:test';
|
|
2
|
-
import { createEnvelope } from '../../types/index.js';
|
|
3
|
-
import { LocalTransport } from './local-transport.js';
|
|
4
|
-
describe('LocalTransport', () => {
|
|
5
|
-
let transport;
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
transport = new LocalTransport();
|
|
8
|
-
});
|
|
9
|
-
describe('connection', () => {
|
|
10
|
-
it('should start disconnected', () => {
|
|
11
|
-
expect(transport.isConnected()).toBe(false);
|
|
12
|
-
});
|
|
13
|
-
it('should connect successfully', async () => {
|
|
14
|
-
await transport.connect();
|
|
15
|
-
expect(transport.isConnected()).toBe(true);
|
|
16
|
-
});
|
|
17
|
-
it('should disconnect successfully', async () => {
|
|
18
|
-
await transport.connect();
|
|
19
|
-
await transport.disconnect();
|
|
20
|
-
expect(transport.isConnected()).toBe(false);
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
describe('capabilities', () => {
|
|
24
|
-
it('should report correct capabilities', () => {
|
|
25
|
-
expect(transport.name).toBe('local');
|
|
26
|
-
expect(transport.capabilities.deliveryModes).toContain('at-least-once');
|
|
27
|
-
expect(transport.capabilities.delayedMessages).toBe(true);
|
|
28
|
-
expect(transport.capabilities.deadLetterRouting).toBe('manual');
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
describe('send and receive', () => {
|
|
32
|
-
beforeEach(async () => {
|
|
33
|
-
await transport.connect();
|
|
34
|
-
});
|
|
35
|
-
it('should throw when sending while disconnected', async () => {
|
|
36
|
-
await transport.disconnect();
|
|
37
|
-
const envelope = createTestEnvelope();
|
|
38
|
-
await expect(transport.send('test-queue', envelope)).rejects.toThrow('is not connected');
|
|
39
|
-
});
|
|
40
|
-
it('should send and receive a message', async () => {
|
|
41
|
-
const envelope = createTestEnvelope();
|
|
42
|
-
await transport.send('test-queue', envelope);
|
|
43
|
-
const received = await transport.receiveOne('test-queue');
|
|
44
|
-
expect(received).not.toBeNull();
|
|
45
|
-
expect(received?.envelope.id).toBe(envelope.id);
|
|
46
|
-
expect(received?.envelope.data).toEqual({ test: 'data' });
|
|
47
|
-
});
|
|
48
|
-
it('should track queue size correctly', async () => {
|
|
49
|
-
expect(transport.getQueueSize('test-queue')).toBe(0);
|
|
50
|
-
await transport.send('test-queue', createTestEnvelope());
|
|
51
|
-
expect(transport.getQueueSize('test-queue')).toBe(1);
|
|
52
|
-
await transport.send('test-queue', createTestEnvelope());
|
|
53
|
-
expect(transport.getQueueSize('test-queue')).toBe(2);
|
|
54
|
-
});
|
|
55
|
-
it('should complete messages and remove from queue', async () => {
|
|
56
|
-
const envelope = createTestEnvelope();
|
|
57
|
-
await transport.send('test-queue', envelope);
|
|
58
|
-
const received = await transport.receiveOne('test-queue');
|
|
59
|
-
expect(received).not.toBeNull();
|
|
60
|
-
await transport.complete(received.receipt);
|
|
61
|
-
expect(transport.getQueueSize('test-queue')).toBe(0);
|
|
62
|
-
expect(transport.getCompleted()).toHaveLength(1);
|
|
63
|
-
});
|
|
64
|
-
it('should return null when queue is empty', async () => {
|
|
65
|
-
const received = await transport.receiveOne('empty-queue');
|
|
66
|
-
expect(received).toBeNull();
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
describe('subscriptions', () => {
|
|
70
|
-
beforeEach(async () => {
|
|
71
|
-
await transport.connect();
|
|
72
|
-
});
|
|
73
|
-
it('should throw when subscribing while disconnected', async () => {
|
|
74
|
-
await transport.disconnect();
|
|
75
|
-
await expect(transport.subscribe('test-queue', async () => { })).rejects.toThrow('is not connected');
|
|
76
|
-
});
|
|
77
|
-
it('should deliver messages to subscribers', async () => {
|
|
78
|
-
const receivedMessages = [];
|
|
79
|
-
await transport.subscribe('test-queue', async (envelope) => {
|
|
80
|
-
receivedMessages.push(envelope);
|
|
81
|
-
});
|
|
82
|
-
const envelope = createTestEnvelope();
|
|
83
|
-
await transport.send('test-queue', envelope);
|
|
84
|
-
expect(receivedMessages).toHaveLength(1);
|
|
85
|
-
expect(receivedMessages[0].id).toBe(envelope.id);
|
|
86
|
-
});
|
|
87
|
-
it('should deliver pending messages when subscribing', async () => {
|
|
88
|
-
const envelope = createTestEnvelope();
|
|
89
|
-
await transport.send('test-queue', envelope);
|
|
90
|
-
const receivedMessages = [];
|
|
91
|
-
await transport.subscribe('test-queue', async (env) => {
|
|
92
|
-
receivedMessages.push(env);
|
|
93
|
-
});
|
|
94
|
-
expect(receivedMessages).toHaveLength(1);
|
|
95
|
-
});
|
|
96
|
-
it('should stop delivering after unsubscribe', async () => {
|
|
97
|
-
const receivedMessages = [];
|
|
98
|
-
const subscription = await transport.subscribe('test-queue', async (envelope) => {
|
|
99
|
-
receivedMessages.push(envelope);
|
|
100
|
-
});
|
|
101
|
-
await transport.send('test-queue', createTestEnvelope());
|
|
102
|
-
expect(receivedMessages).toHaveLength(1);
|
|
103
|
-
await subscription.unsubscribe();
|
|
104
|
-
expect(subscription.isActive).toBe(false);
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
describe('dead letter queue', () => {
|
|
108
|
-
beforeEach(async () => {
|
|
109
|
-
await transport.connect();
|
|
110
|
-
});
|
|
111
|
-
it('should send messages to dead letter queue', async () => {
|
|
112
|
-
const envelope = createTestEnvelope();
|
|
113
|
-
await transport.send('test-queue', envelope);
|
|
114
|
-
const received = await transport.receiveOne('test-queue');
|
|
115
|
-
expect(received).not.toBeNull();
|
|
116
|
-
await transport.sendToDeadLetter(received.receipt, 'dlq', envelope, 'test error');
|
|
117
|
-
expect(transport.getQueueSize('test-queue')).toBe(0);
|
|
118
|
-
expect(transport.getQueueSize('test-queue.dlq')).toBe(1);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
describe('topology', () => {
|
|
122
|
-
it('should apply topology and create queues', async () => {
|
|
123
|
-
const topology = {
|
|
124
|
-
namespace: 'test',
|
|
125
|
-
queues: [{ name: 'events' }, { name: 'notifications' }],
|
|
126
|
-
deadLetter: {
|
|
127
|
-
unhandled: { enabled: true },
|
|
128
|
-
undeliverable: { enabled: true },
|
|
129
|
-
},
|
|
130
|
-
retry: {
|
|
131
|
-
enabled: true,
|
|
132
|
-
defaultDelayMs: 1000,
|
|
133
|
-
maxDelayMs: 30000,
|
|
134
|
-
},
|
|
135
|
-
};
|
|
136
|
-
await transport.connect();
|
|
137
|
-
await transport.applyTopology(topology);
|
|
138
|
-
// Queues should be created
|
|
139
|
-
expect(transport.getQueueSize('test.events')).toBe(0);
|
|
140
|
-
expect(transport.getQueueSize('test.notifications')).toBe(0);
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
describe('clear', () => {
|
|
144
|
-
it('should reset all state', async () => {
|
|
145
|
-
await transport.connect();
|
|
146
|
-
await transport.send('test-queue', createTestEnvelope());
|
|
147
|
-
const received = await transport.receiveOne('test-queue');
|
|
148
|
-
await transport.complete(received.receipt);
|
|
149
|
-
transport.clear();
|
|
150
|
-
expect(transport.getQueueSize('test-queue')).toBe(0);
|
|
151
|
-
expect(transport.getCompleted()).toHaveLength(0);
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
describe('delayed messages', () => {
|
|
155
|
-
beforeEach(async () => {
|
|
156
|
-
await transport.connect();
|
|
157
|
-
});
|
|
158
|
-
it('should return immediately and delay message delivery', async () => {
|
|
159
|
-
const envelope = createTestEnvelope();
|
|
160
|
-
// Send returns immediately (non-blocking)
|
|
161
|
-
await transport.send('test-queue', envelope, {
|
|
162
|
-
delay: 100,
|
|
163
|
-
});
|
|
164
|
-
// Message should not be in queue immediately
|
|
165
|
-
expect(transport.getQueueSize('test-queue')).toBe(0);
|
|
166
|
-
// Wait for the delay to pass
|
|
167
|
-
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
168
|
-
// Message should now be in queue
|
|
169
|
-
expect(transport.getQueueSize('test-queue')).toBe(1);
|
|
170
|
-
});
|
|
171
|
-
it('should cancel delayed messages on disconnect', async () => {
|
|
172
|
-
const envelope = createTestEnvelope();
|
|
173
|
-
await transport.send('test-queue', envelope, {
|
|
174
|
-
delay: 100,
|
|
175
|
-
});
|
|
176
|
-
// Disconnect before delay expires
|
|
177
|
-
await transport.disconnect();
|
|
178
|
-
// Wait past the original delay
|
|
179
|
-
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
180
|
-
// Message should never have been delivered
|
|
181
|
-
expect(transport.getQueueSize('test-queue')).toBe(0);
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
function createTestEnvelope() {
|
|
186
|
-
return createEnvelope({
|
|
187
|
-
eventKey: 'test.event',
|
|
188
|
-
targetSubscriber: 'test-subscriber',
|
|
189
|
-
data: { test: 'data' },
|
|
190
|
-
importance: 'should-investigate',
|
|
191
|
-
});
|
|
192
|
-
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import type { TransportFallbackContext } from '../../hooks/index.js';
|
|
2
|
-
import type { Topology } from '../../topology/types.js';
|
|
3
|
-
import type { Envelope } from '../../types/index.js';
|
|
4
|
-
import type { TransportCapabilities } from '../capabilities.js';
|
|
5
|
-
import type { MessageHandler, MessageReceipt, SendOptions, SubscribeOptions, Subscription, Transport } from '../transport.js';
|
|
6
|
-
/**
|
|
7
|
-
* Configuration for MultiTransport.
|
|
8
|
-
*/
|
|
9
|
-
export interface MultiTransportConfig {
|
|
10
|
-
/**
|
|
11
|
-
* Ordered list of transports.
|
|
12
|
-
* First transport is primary, rest are used as fallbacks when primary fails
|
|
13
|
-
* (if fallback is enabled), or can be explicitly selected via the getDesiredBackend hook.
|
|
14
|
-
*/
|
|
15
|
-
readonly transports: readonly Transport[];
|
|
16
|
-
/**
|
|
17
|
-
* Whether to automatically try fallback transports when the primary/selected fails.
|
|
18
|
-
* When true (default), send() will try each transport in order until one succeeds.
|
|
19
|
-
* When false, send() will only use the selected transport and throw immediately on failure.
|
|
20
|
-
*
|
|
21
|
-
* @default true
|
|
22
|
-
*/
|
|
23
|
-
readonly fallbackEnabled?: boolean;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Hooks for MultiTransport.
|
|
27
|
-
*/
|
|
28
|
-
export interface MultiTransportHooks {
|
|
29
|
-
/**
|
|
30
|
-
* Hook to dynamically select which backend to use.
|
|
31
|
-
* Return the transport name (e.g., 'local', 'rabbitmq') or undefined to use primary.
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```typescript
|
|
35
|
-
* getDesiredBackend: async () => {
|
|
36
|
-
* if (process.env.SANDBOX === 'true') return 'local';
|
|
37
|
-
* const backend = await runtimeConfig.get('events.backend');
|
|
38
|
-
* return backend || 'rabbitmq';
|
|
39
|
-
* }
|
|
40
|
-
* ```
|
|
41
|
-
*/
|
|
42
|
-
readonly getDesiredBackend?: () => string | undefined | Promise<string | undefined>;
|
|
43
|
-
/**
|
|
44
|
-
* Called when a fallback transport is used successfully after the selected one fails.
|
|
45
|
-
* Only called when fallbackEnabled is true.
|
|
46
|
-
*/
|
|
47
|
-
readonly onEnqueueFallback?: (context: TransportFallbackContext) => void;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* A transport wrapper that manages multiple transports.
|
|
51
|
-
*
|
|
52
|
-
* Features:
|
|
53
|
-
* - All transports are connected upfront
|
|
54
|
-
* - send() tries transports in order until one succeeds (fallback behavior)
|
|
55
|
-
* - Specific transport can be selected via getDesiredBackend hook
|
|
56
|
-
* - Subscriptions are created on ALL transports
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```typescript
|
|
60
|
-
* const transport = new MultiTransport(
|
|
61
|
-
* { transports: [rabbitMQTransport, localTransport] },
|
|
62
|
-
* { onEnqueueFallback: (ctx) => console.warn(`Fallback to ${ctx.nextTransport}`) },
|
|
63
|
-
* );
|
|
64
|
-
* ```
|
|
65
|
-
*/
|
|
66
|
-
export declare class MultiTransport implements Transport {
|
|
67
|
-
readonly name: string;
|
|
68
|
-
readonly capabilities: TransportCapabilities;
|
|
69
|
-
readonly primary: Transport;
|
|
70
|
-
/** All available transports, in order of preference (primary first). */
|
|
71
|
-
readonly transports: readonly Transport[];
|
|
72
|
-
/** Whether fallback to secondary transports is enabled. */
|
|
73
|
-
readonly fallbackEnabled: boolean;
|
|
74
|
-
private readonly hooks;
|
|
75
|
-
private connected;
|
|
76
|
-
constructor(config: MultiTransportConfig, hooks?: MultiTransportHooks);
|
|
77
|
-
connect(): Promise<void>;
|
|
78
|
-
disconnect(): Promise<void>;
|
|
79
|
-
isConnected(): boolean;
|
|
80
|
-
applyTopology(topology: Topology): Promise<void>;
|
|
81
|
-
send(queue: string, envelope: Envelope, options?: SendOptions): Promise<Transport['name']>;
|
|
82
|
-
/**
|
|
83
|
-
* Selects the transport to use based on the getDesiredBackend hook.
|
|
84
|
-
*/
|
|
85
|
-
private selectTransport;
|
|
86
|
-
/**
|
|
87
|
-
* Returns transports in order, with the selected transport first.
|
|
88
|
-
*/
|
|
89
|
-
private getTransportOrder;
|
|
90
|
-
subscribe(queue: string, handler: MessageHandler, options?: SubscribeOptions): Promise<Subscription>;
|
|
91
|
-
complete(receipt: MessageReceipt): Promise<void>;
|
|
92
|
-
sendToDeadLetter(receipt: MessageReceipt, dlqName: string, envelope: Envelope, reason: string): Promise<void>;
|
|
93
|
-
}
|
|
94
|
-
//# sourceMappingURL=multi-transport.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"multi-transport.d.ts","sourceRoot":"","sources":["../../../src/transport/multi/multi-transport.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACrE,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;AAgBzB;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAE1C;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MACzB,MAAM,GACN,SAAS,GACT,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAEhC;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,IAAI,CAAC;CAC1E;AAmBD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,cAAe,YAAW,SAAS;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC;IAC7C,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;IAE5B,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAE1C,2DAA2D;IAC3D,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAElC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,SAAS,CAAS;gBAEd,MAAM,EAAE,oBAAoB,EAAE,KAAK,GAAE,mBAAwB;IAcnE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAMxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC,WAAW,IAAI,OAAO;IAKhB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAKhD,IAAI,CACR,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAyC7B;;OAEG;YACW,eAAe;IAwB7B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IASnB,SAAS,CACb,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,YAAY,CAAC;IA0BlB,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAKhD,gBAAgB,CACpB,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;CAOjB"}
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import { AllTransportsFailedError } from '../../errors/index.js';
|
|
2
|
-
/**
|
|
3
|
-
* Symbol key used to store the source transport on wrapped receipts.
|
|
4
|
-
*/
|
|
5
|
-
const SOURCE_TRANSPORT = Symbol('sourceTransport');
|
|
6
|
-
/**
|
|
7
|
-
* Extracts the source transport from a tagged receipt.
|
|
8
|
-
*/
|
|
9
|
-
function getSourceTransport(receipt) {
|
|
10
|
-
return receipt[SOURCE_TRANSPORT];
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Computes merged capabilities from multiple transports.
|
|
14
|
-
* Uses the most restrictive/conservative interpretation.
|
|
15
|
-
*/
|
|
16
|
-
function mergeCapabilities(transports) {
|
|
17
|
-
const primary = transports[0];
|
|
18
|
-
if (!primary) {
|
|
19
|
-
throw new Error('At least one transport is required');
|
|
20
|
-
}
|
|
21
|
-
// For multi-transport scenarios, we use primary's capabilities since that's
|
|
22
|
-
// what we'll use for subscriptions and most operations
|
|
23
|
-
return primary.capabilities;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* A transport wrapper that manages multiple transports.
|
|
27
|
-
*
|
|
28
|
-
* Features:
|
|
29
|
-
* - All transports are connected upfront
|
|
30
|
-
* - send() tries transports in order until one succeeds (fallback behavior)
|
|
31
|
-
* - Specific transport can be selected via getDesiredBackend hook
|
|
32
|
-
* - Subscriptions are created on ALL transports
|
|
33
|
-
*
|
|
34
|
-
* @example
|
|
35
|
-
* ```typescript
|
|
36
|
-
* const transport = new MultiTransport(
|
|
37
|
-
* { transports: [rabbitMQTransport, localTransport] },
|
|
38
|
-
* { onEnqueueFallback: (ctx) => console.warn(`Fallback to ${ctx.nextTransport}`) },
|
|
39
|
-
* );
|
|
40
|
-
* ```
|
|
41
|
-
*/
|
|
42
|
-
export class MultiTransport {
|
|
43
|
-
name;
|
|
44
|
-
capabilities;
|
|
45
|
-
primary;
|
|
46
|
-
/** All available transports, in order of preference (primary first). */
|
|
47
|
-
transports;
|
|
48
|
-
/** Whether fallback to secondary transports is enabled. */
|
|
49
|
-
fallbackEnabled;
|
|
50
|
-
hooks;
|
|
51
|
-
connected = false;
|
|
52
|
-
constructor(config, hooks = {}) {
|
|
53
|
-
const primary = config.transports[0];
|
|
54
|
-
if (!primary) {
|
|
55
|
-
throw new Error('At least one transport is required');
|
|
56
|
-
}
|
|
57
|
-
this.primary = primary;
|
|
58
|
-
this.transports = config.transports;
|
|
59
|
-
this.fallbackEnabled = config.fallbackEnabled ?? true;
|
|
60
|
-
this.hooks = hooks;
|
|
61
|
-
this.name = `multi(${this.transports.map((t) => t.name).join(',')})`;
|
|
62
|
-
this.capabilities = mergeCapabilities(this.transports);
|
|
63
|
-
}
|
|
64
|
-
async connect() {
|
|
65
|
-
// Connect all transports in parallel
|
|
66
|
-
await Promise.all(this.transports.map((t) => t.connect()));
|
|
67
|
-
this.connected = true;
|
|
68
|
-
}
|
|
69
|
-
async disconnect() {
|
|
70
|
-
// Disconnect all transports in parallel
|
|
71
|
-
await Promise.all(this.transports.map((t) => t.disconnect()));
|
|
72
|
-
this.connected = false;
|
|
73
|
-
}
|
|
74
|
-
isConnected() {
|
|
75
|
-
// Consider connected if primary is connected
|
|
76
|
-
return this.connected && this.primary.isConnected();
|
|
77
|
-
}
|
|
78
|
-
async applyTopology(topology) {
|
|
79
|
-
// Apply topology to all transports so they're all ready
|
|
80
|
-
await Promise.all(this.transports.map((t) => t.applyTopology(topology)));
|
|
81
|
-
}
|
|
82
|
-
async send(queue, envelope, options) {
|
|
83
|
-
// Determine which transport to use
|
|
84
|
-
const selectedTransport = await this.selectTransport();
|
|
85
|
-
// If fallback is disabled, only use selected transport
|
|
86
|
-
if (!this.fallbackEnabled) {
|
|
87
|
-
return selectedTransport.send(queue, envelope, options);
|
|
88
|
-
}
|
|
89
|
-
// Build transport order: selected first, then others
|
|
90
|
-
const transportOrder = this.getTransportOrder(selectedTransport);
|
|
91
|
-
const errors = [];
|
|
92
|
-
for (let i = 0; i < transportOrder.length; i++) {
|
|
93
|
-
const transport = transportOrder[i];
|
|
94
|
-
try {
|
|
95
|
-
return await transport.send(queue, envelope, options);
|
|
96
|
-
}
|
|
97
|
-
catch (error) {
|
|
98
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
99
|
-
errors.push(err);
|
|
100
|
-
// Notify about fallback before trying next transport
|
|
101
|
-
const nextTransport = transportOrder[i + 1];
|
|
102
|
-
if (nextTransport && this.hooks.onEnqueueFallback) {
|
|
103
|
-
this.hooks.onEnqueueFallback({
|
|
104
|
-
envelope,
|
|
105
|
-
queue,
|
|
106
|
-
failedTransport: transport.name,
|
|
107
|
-
nextTransport: nextTransport.name,
|
|
108
|
-
error: err,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
// Continue to next transport
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
// All transports failed
|
|
115
|
-
throw new AllTransportsFailedError(queue, errors);
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Selects the transport to use based on the getDesiredBackend hook.
|
|
119
|
-
*/
|
|
120
|
-
async selectTransport() {
|
|
121
|
-
if (!this.hooks.getDesiredBackend) {
|
|
122
|
-
return this.primary;
|
|
123
|
-
}
|
|
124
|
-
try {
|
|
125
|
-
const desiredBackend = await this.hooks.getDesiredBackend();
|
|
126
|
-
if (!desiredBackend) {
|
|
127
|
-
return this.primary;
|
|
128
|
-
}
|
|
129
|
-
const selected = this.transports.find((t) => t.name === desiredBackend);
|
|
130
|
-
if (selected) {
|
|
131
|
-
return selected;
|
|
132
|
-
}
|
|
133
|
-
// Desired backend not found, use primary
|
|
134
|
-
return this.primary;
|
|
135
|
-
}
|
|
136
|
-
catch {
|
|
137
|
-
// Hook threw, use primary
|
|
138
|
-
return this.primary;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Returns transports in order, with the selected transport first.
|
|
143
|
-
*/
|
|
144
|
-
getTransportOrder(selected) {
|
|
145
|
-
if (selected === this.primary) {
|
|
146
|
-
return this.transports;
|
|
147
|
-
}
|
|
148
|
-
// Put selected first, then the rest (excluding selected)
|
|
149
|
-
return [selected, ...this.transports.filter((t) => t !== selected)];
|
|
150
|
-
}
|
|
151
|
-
async subscribe(queue, handler, options) {
|
|
152
|
-
// Subscribe on ALL transports so messages are processed regardless of
|
|
153
|
-
// which transport they were enqueued to
|
|
154
|
-
const subscriptions = await Promise.all(this.transports.map((transport) => {
|
|
155
|
-
// Wrap handler to tag receipts with source transport
|
|
156
|
-
const wrappedHandler = (envelope, receipt) => {
|
|
157
|
-
const taggedReceipt = Object.assign(receipt, {
|
|
158
|
-
[SOURCE_TRANSPORT]: transport,
|
|
159
|
-
});
|
|
160
|
-
return handler(envelope, taggedReceipt);
|
|
161
|
-
};
|
|
162
|
-
return transport.subscribe(queue, wrappedHandler, options);
|
|
163
|
-
}));
|
|
164
|
-
return {
|
|
165
|
-
unsubscribe: async () => {
|
|
166
|
-
await Promise.all(subscriptions.map((s) => s.unsubscribe()));
|
|
167
|
-
},
|
|
168
|
-
get isActive() {
|
|
169
|
-
return subscriptions.some((s) => s.isActive);
|
|
170
|
-
},
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
async complete(receipt) {
|
|
174
|
-
const transport = getSourceTransport(receipt) ?? this.primary;
|
|
175
|
-
return transport.complete(receipt);
|
|
176
|
-
}
|
|
177
|
-
async sendToDeadLetter(receipt, dlqName, envelope, reason) {
|
|
178
|
-
const transport = getSourceTransport(receipt) ?? this.primary;
|
|
179
|
-
if (transport.sendToDeadLetter) {
|
|
180
|
-
return transport.sendToDeadLetter(receipt, dlqName, envelope, reason);
|
|
181
|
-
}
|
|
182
|
-
throw new Error('Transport does not support dead letter routing');
|
|
183
|
-
}
|
|
184
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"multi-transport.test.d.ts","sourceRoot":"","sources":["../../../src/transport/multi/multi-transport.test.ts"],"names":[],"mappings":""}
|