@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.
Files changed (40) hide show
  1. package/dist/core/fanout.d.ts +3 -2
  2. package/dist/core/fanout.d.ts.map +1 -1
  3. package/dist/core/fanout.js +4 -4
  4. package/dist/core/fanout.test.js +32 -24
  5. package/dist/core/matador.d.ts.map +1 -1
  6. package/dist/core/matador.js +16 -7
  7. package/dist/core/matador.test.js +32 -1
  8. package/dist/errors/index.d.ts +1 -1
  9. package/dist/errors/index.d.ts.map +1 -1
  10. package/dist/errors/index.js +2 -2
  11. package/dist/errors/matador-errors.d.ts +28 -0
  12. package/dist/errors/matador-errors.d.ts.map +1 -1
  13. package/dist/errors/matador-errors.js +41 -0
  14. package/dist/index.cjs +325 -80
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +3 -3
  17. package/dist/index.d.ts +3 -3
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +3 -3
  20. package/dist/index.js.map +1 -1
  21. package/dist/topology/builder.d.ts +26 -1
  22. package/dist/topology/builder.d.ts.map +1 -1
  23. package/dist/topology/builder.js +72 -0
  24. package/dist/topology/builder.test.js +179 -1
  25. package/dist/topology/index.d.ts +2 -2
  26. package/dist/topology/index.d.ts.map +1 -1
  27. package/dist/topology/index.js +1 -1
  28. package/dist/topology/types.d.ts +108 -6
  29. package/dist/topology/types.d.ts.map +1 -1
  30. package/dist/topology/types.js +50 -10
  31. package/dist/transport/local/local-transport.d.ts.map +1 -1
  32. package/dist/transport/local/local-transport.js +5 -2
  33. package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.d.ts +2 -0
  34. package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.d.ts.map +1 -0
  35. package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.js +110 -0
  36. package/dist/transport/rabbitmq/rabbitmq-transport.d.ts +24 -0
  37. package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -1
  38. package/dist/transport/rabbitmq/rabbitmq-transport.js +145 -68
  39. package/dist/transport/rabbitmq/rabbitmq-transport.test.js +130 -0
  40. package/package.json +6 -2
@@ -1,4 +1,5 @@
1
1
  import { describe, expect, it, mock } from 'bun:test';
2
+ import { TopologyBuilder } from '../../topology/index.js';
2
3
  import { RabbitMQTransport, redactAmqpUrl } from './rabbitmq-transport.js';
3
4
  describe('redactAmqpUrl', () => {
4
5
  it('should redact username and password with 4 asterisks', () => {
@@ -98,3 +99,132 @@ describe('RabbitMQTransport', () => {
98
99
  });
99
100
  });
100
101
  });
102
+ describe('dead-letter and retry queue naming under withNaming', () => {
103
+ // A v1-style naming strategy: a migrating deployment keeps Matador v1's
104
+ // `matador.{ns}.{queue}` names so DLQ/retry resources stay identifiable.
105
+ const NS = 'myapp';
106
+ const v1Naming = {
107
+ queue: (ns, q) => `matador.${ns}.${q}`,
108
+ mainExchange: (ns) => `matador.${ns}`,
109
+ dlxExchange: (ns) => `matador.${ns}.dlx-undeliverable`,
110
+ dlxExchangeType: 'topic',
111
+ };
112
+ function buildTopology() {
113
+ return TopologyBuilder.create()
114
+ .withNamespace(NS)
115
+ .withNaming(v1Naming)
116
+ .addQueue('events')
117
+ .build();
118
+ }
119
+ function createRecordingChannel() {
120
+ const assertQueueCalls = [];
121
+ const bindQueueCalls = [];
122
+ const channel = {
123
+ assertQueue: (name, options) => {
124
+ assertQueueCalls.push({ name, options });
125
+ return Promise.resolve({ queue: name });
126
+ },
127
+ bindQueue: (queue, exchange, routingKey) => {
128
+ bindQueueCalls.push({ queue, exchange, routingKey });
129
+ return Promise.resolve({});
130
+ },
131
+ };
132
+ return { channel, assertQueueCalls, bindQueueCalls };
133
+ }
134
+ function createTransport() {
135
+ return new RabbitMQTransport({
136
+ url: 'amqp://localhost:5672',
137
+ connectionName: 'test',
138
+ });
139
+ }
140
+ it('derives the retry queue name through the naming strategy', async () => {
141
+ const transport = createTransport();
142
+ const topology = buildTopology();
143
+ const queueDef = topology.queues[0];
144
+ const { channel, assertQueueCalls, bindQueueCalls } = createRecordingChannel();
145
+ await transport.assertRetryQueue(channel, topology, queueDef);
146
+ expect(assertQueueCalls.map((c) => c.name)).toEqual([
147
+ 'matador.myapp.events.retry',
148
+ ]);
149
+ // Retry queue dead-letters back to the (overridden) main exchange using the
150
+ // resolved work-queue name as the routing key.
151
+ const retryOptions = assertQueueCalls[0]?.options;
152
+ expect(retryOptions.arguments['x-dead-letter-exchange']).toBe('matador.myapp');
153
+ expect(retryOptions.arguments['x-dead-letter-routing-key']).toBe('matador.myapp.events');
154
+ expect(bindQueueCalls).toEqual([
155
+ {
156
+ queue: 'matador.myapp.events.retry',
157
+ exchange: 'matador.myapp',
158
+ routingKey: 'matador.myapp.events.retry',
159
+ },
160
+ ]);
161
+ });
162
+ it('derives DLQ names through the naming strategy and binds them to the overridden DLX', async () => {
163
+ const transport = createTransport();
164
+ const topology = buildTopology();
165
+ const { channel, assertQueueCalls, bindQueueCalls } = createRecordingChannel();
166
+ const assertDeadLetterQueues = transport.assertDeadLetterQueues.bind(transport);
167
+ await assertDeadLetterQueues(channel, topology, 'unhandled');
168
+ await assertDeadLetterQueues(channel, topology, 'undeliverable');
169
+ expect(assertQueueCalls.map((c) => c.name)).toEqual([
170
+ 'matador.myapp.events.unhandled',
171
+ 'matador.myapp.events.undeliverable',
172
+ ]);
173
+ expect(bindQueueCalls).toEqual([
174
+ {
175
+ queue: 'matador.myapp.events.unhandled',
176
+ exchange: 'matador.myapp.dlx-undeliverable',
177
+ routingKey: 'matador.myapp.events.unhandled',
178
+ },
179
+ {
180
+ queue: 'matador.myapp.events.undeliverable',
181
+ exchange: 'matador.myapp.dlx-undeliverable',
182
+ routingKey: 'matador.myapp.events.undeliverable',
183
+ },
184
+ ]);
185
+ });
186
+ });
187
+ describe('confirmPublish', () => {
188
+ function createTransport(publishTimeoutMs) {
189
+ return new RabbitMQTransport({
190
+ url: 'amqp://localhost:5672',
191
+ connectionName: 'test',
192
+ ...(publishTimeoutMs !== undefined ? { publishTimeoutMs } : {}),
193
+ });
194
+ }
195
+ function callConfirmPublish(transport, publish) {
196
+ const fakeChannel = {
197
+ publish: (_exchange, _routingKey, _buffer, _options, cb) => {
198
+ publish(cb);
199
+ return true;
200
+ },
201
+ };
202
+ return transport.confirmPublish(fakeChannel, 'ex', 'queue', Buffer.from('x'), {});
203
+ }
204
+ it('resolves when the broker confirms the publish', async () => {
205
+ const transport = createTransport();
206
+ await callConfirmPublish(transport, (cb) => cb(null));
207
+ });
208
+ it('rejects with TransportSendError when the broker nacks', async () => {
209
+ const transport = createTransport();
210
+ await expect(callConfirmPublish(transport, (cb) => cb(new Error('nacked')))).rejects.toMatchObject({
211
+ name: 'TransportSendError',
212
+ queue: 'queue',
213
+ });
214
+ });
215
+ it('rejects when the broker never confirms within the timeout', async () => {
216
+ const transport = createTransport(50);
217
+ await expect(callConfirmPublish(transport, () => {
218
+ // Broker never calls back.
219
+ })).rejects.toMatchObject({ name: 'TransportSendError' });
220
+ });
221
+ it('ignores a late confirm after the timeout already rejected', async () => {
222
+ const transport = createTransport(50);
223
+ let lateCallback;
224
+ await expect(callConfirmPublish(transport, (cb) => {
225
+ lateCallback = cb;
226
+ })).rejects.toMatchObject({ name: 'TransportSendError' });
227
+ // Late confirm must not throw or double-settle.
228
+ lateCallback?.(null);
229
+ });
230
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zdavison/matador",
3
- "version": "3.0.8",
3
+ "version": "4.0.0",
4
4
  "description": "A batteries-included event system.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -67,5 +67,9 @@
67
67
  "files": [
68
68
  "dist",
69
69
  "cli.ts"
70
- ]
70
+ ],
71
+ "repository": {
72
+ "type": "git",
73
+ "url": "https://github.com/meetsmore/matador.git"
74
+ }
71
75
  }