@zdavison/matador 2.0.8 → 2.0.10

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 (77) hide show
  1. package/dist/index.d.cts +1 -1
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/package.json +6 -2
  5. package/examples/config.ts +0 -126
  6. package/examples/event.ts +0 -26
  7. package/examples/order-event.json +0 -19
  8. package/src/checkpoint/context.test.ts +0 -510
  9. package/src/checkpoint/context.ts +0 -213
  10. package/src/checkpoint/index.ts +0 -30
  11. package/src/checkpoint/stores/memory.ts +0 -47
  12. package/src/checkpoint/stores/noop.ts +0 -22
  13. package/src/checkpoint/stores/stores.test.ts +0 -177
  14. package/src/checkpoint/types.ts +0 -147
  15. package/src/codec/codec.ts +0 -42
  16. package/src/codec/header-aware-codec.ts +0 -41
  17. package/src/codec/index.ts +0 -11
  18. package/src/codec/json-codec.ts +0 -69
  19. package/src/codec/rabbitmq-codec.test.ts +0 -516
  20. package/src/codec/rabbitmq-codec.ts +0 -336
  21. package/src/core/fanout.test.ts +0 -1350
  22. package/src/core/fanout.ts +0 -184
  23. package/src/core/index.ts +0 -12
  24. package/src/core/matador.test.ts +0 -575
  25. package/src/core/matador.ts +0 -357
  26. package/src/core/shutdown.test.ts +0 -853
  27. package/src/core/shutdown.ts +0 -165
  28. package/src/errors/checkpoint-errors.ts +0 -62
  29. package/src/errors/has-description.ts +0 -25
  30. package/src/errors/index.ts +0 -58
  31. package/src/errors/matador-errors.ts +0 -477
  32. package/src/errors/retry-errors.test.ts +0 -175
  33. package/src/errors/retry-errors.ts +0 -188
  34. package/src/hooks/index.ts +0 -14
  35. package/src/hooks/safe-hooks.ts +0 -200
  36. package/src/hooks/types.ts +0 -226
  37. package/src/index.ts +0 -241
  38. package/src/pipeline/index.ts +0 -2
  39. package/src/pipeline/pipeline.test.ts +0 -1377
  40. package/src/pipeline/pipeline.ts +0 -393
  41. package/src/retry/index.ts +0 -4
  42. package/src/retry/policy.ts +0 -46
  43. package/src/retry/standard-policy.test.ts +0 -290
  44. package/src/retry/standard-policy.ts +0 -156
  45. package/src/schema/index.ts +0 -16
  46. package/src/schema/registry.test.ts +0 -339
  47. package/src/schema/registry.ts +0 -229
  48. package/src/schema/types.test.ts +0 -280
  49. package/src/schema/types.ts +0 -217
  50. package/src/topology/builder.test.ts +0 -451
  51. package/src/topology/builder.ts +0 -238
  52. package/src/topology/index.ts +0 -19
  53. package/src/topology/types.ts +0 -183
  54. package/src/transport/capabilities.ts +0 -88
  55. package/src/transport/connection-manager.ts +0 -218
  56. package/src/transport/index.ts +0 -42
  57. package/src/transport/local/local-transport.test.ts +0 -262
  58. package/src/transport/local/local-transport.ts +0 -330
  59. package/src/transport/multi/multi-transport.test.ts +0 -320
  60. package/src/transport/multi/multi-transport.ts +0 -294
  61. package/src/transport/rabbitmq/rabbitmq-transport.test.ts +0 -120
  62. package/src/transport/rabbitmq/rabbitmq-transport.ts +0 -782
  63. package/src/transport/transport.ts +0 -200
  64. package/src/types/common.ts +0 -53
  65. package/src/types/dispatcher.ts +0 -18
  66. package/src/types/envelope.ts +0 -244
  67. package/src/types/event.test.ts +0 -157
  68. package/src/types/event.ts +0 -112
  69. package/src/types/index.ts +0 -62
  70. package/src/types/subscriber.ts +0 -333
  71. package/test/e2e/multi-transport.e2e.test.ts +0 -237
  72. package/test/e2e/rabbitmq-transport.e2e.test.ts +0 -618
  73. package/test/e2e/transport-compliance.e2e.test.ts +0 -506
  74. package/test/integration/matador.integration.test.ts +0 -634
  75. package/tsconfig.json +0 -29
  76. package/tsconfig.tsbuildinfo +0 -1
  77. package/tsup.config.ts +0 -13
@@ -1,339 +0,0 @@
1
- import { beforeEach, describe, expect, it } from 'bun:test';
2
- import { MatadorEvent, createSubscriber } from '../types/index.js';
3
- import { SchemaError, SchemaRegistry } from './registry.js';
4
-
5
- class TestEvent extends MatadorEvent {
6
- static readonly key = 'test.event';
7
- static readonly description = 'A test event';
8
-
9
- constructor(public data: { id: string }) {
10
- super();
11
- }
12
- }
13
-
14
- class AliasedEvent extends MatadorEvent {
15
- static readonly key = 'user.created.v2';
16
- static readonly description = 'User created event with aliases';
17
- static readonly aliases = ['user.created', 'user.created.v1'];
18
-
19
- constructor(public data: { name: string }) {
20
- super();
21
- }
22
- }
23
-
24
- describe('SchemaRegistry', () => {
25
- let registry: SchemaRegistry;
26
-
27
- beforeEach(() => {
28
- registry = new SchemaRegistry();
29
- });
30
-
31
- describe('register', () => {
32
- it('should register an event with subscribers', () => {
33
- const subscriber = createSubscriber({
34
- name: 'test-subscriber',
35
- description: 'Test subscriber',
36
- callback: async () => {},
37
- });
38
-
39
- registry.register(TestEvent, [subscriber]);
40
-
41
- expect(registry.hasEvent('test.event')).toBe(true);
42
- expect(registry.getSubscribers('test.event')).toHaveLength(1);
43
- });
44
-
45
- it('should throw when registering duplicate event without override', () => {
46
- const subscriber = createSubscriber({
47
- name: 'test-subscriber',
48
- description: 'Test subscriber',
49
- callback: async () => {},
50
- });
51
-
52
- registry.register(TestEvent, [subscriber]);
53
-
54
- expect(() => registry.register(TestEvent, [subscriber])).toThrow(
55
- SchemaError,
56
- );
57
- });
58
-
59
- it('should allow override when option is set', () => {
60
- const subscriber1 = createSubscriber({
61
- name: 'sub-1',
62
- description: 'Subscriber 1',
63
- callback: async () => {},
64
- });
65
- const subscriber2 = createSubscriber({
66
- name: 'sub-2',
67
- description: 'Subscriber 2',
68
- callback: async () => {},
69
- });
70
-
71
- registry.register(TestEvent, [subscriber1]);
72
- registry.register(TestEvent, [subscriber2], { override: true });
73
-
74
- const subscribers = registry.getSubscribers('test.event');
75
- expect(subscribers).toHaveLength(1);
76
- expect(subscribers[0]?.name).toBe('sub-2');
77
- });
78
-
79
- it('should register aliases', () => {
80
- const subscriber = createSubscriber({
81
- name: 'test-subscriber',
82
- description: 'Test subscriber',
83
- callback: async () => {},
84
- });
85
-
86
- registry.register(AliasedEvent, [subscriber]);
87
-
88
- expect(registry.hasEvent('user.created.v2')).toBe(true);
89
- expect(registry.hasEvent('user.created')).toBe(true);
90
- expect(registry.hasEvent('user.created.v1')).toBe(true);
91
- });
92
-
93
- it('should throw on duplicate aliases without override', () => {
94
- class EventWithSameAlias extends MatadorEvent {
95
- static readonly key = 'another.event';
96
- static readonly description = 'Another event';
97
- static readonly aliases = ['user.created']; // conflicts with AliasedEvent
98
-
99
- constructor(public data: { x: number }) {
100
- super();
101
- }
102
- }
103
-
104
- const sub1 = createSubscriber({
105
- name: 'sub-1',
106
- description: 'Subscriber 1',
107
- callback: async () => {},
108
- });
109
- const sub2 = createSubscriber({
110
- name: 'sub-2',
111
- description: 'Subscriber 2',
112
- callback: async () => {},
113
- });
114
-
115
- registry.register(AliasedEvent, [sub1]);
116
-
117
- expect(() => registry.register(EventWithSameAlias, [sub2])).toThrow(
118
- 'Alias "user.created" is already registered',
119
- );
120
- });
121
- });
122
-
123
- describe('getEventClass', () => {
124
- it('should return event class by key', () => {
125
- const subscriber = createSubscriber({
126
- name: 'test-subscriber',
127
- description: 'Test subscriber',
128
- callback: async () => {},
129
- });
130
- registry.register(TestEvent, [subscriber]);
131
-
132
- const eventClass = registry.getEventClass('test.event');
133
- expect(eventClass).toBeDefined();
134
- expect(eventClass?.key).toBe('test.event');
135
- });
136
-
137
- it('should return event class by alias', () => {
138
- const subscriber = createSubscriber({
139
- name: 'test-subscriber',
140
- description: 'Test subscriber',
141
- callback: async () => {},
142
- });
143
- registry.register(AliasedEvent, [subscriber]);
144
-
145
- const eventClass = registry.getEventClass('user.created');
146
- expect(eventClass).toBeDefined();
147
- expect(eventClass?.key).toBe('user.created.v2');
148
- });
149
-
150
- it('should return undefined for unknown key', () => {
151
- expect(registry.getEventClass('unknown.event')).toBeUndefined();
152
- });
153
- });
154
-
155
- describe('getSubscribers', () => {
156
- it('should return all subscribers for an event', () => {
157
- const sub1 = createSubscriber({
158
- name: 'sub-1',
159
- description: 'Subscriber 1',
160
- callback: async () => {},
161
- });
162
- const sub2 = createSubscriber({
163
- name: 'sub-2',
164
- description: 'Subscriber 2',
165
- callback: async () => {},
166
- });
167
-
168
- registry.register(TestEvent, [sub1, sub2]);
169
-
170
- const subscribers = registry.getSubscribers('test.event');
171
- expect(subscribers).toHaveLength(2);
172
- expect(subscribers[0]?.name).toBe('sub-1');
173
- expect(subscribers[1]?.name).toBe('sub-2');
174
- });
175
-
176
- it('should return empty array for unknown event', () => {
177
- expect(registry.getSubscribers('unknown.event')).toEqual([]);
178
- });
179
- });
180
-
181
- describe('getSubscriber', () => {
182
- it('should return specific subscriber by name', () => {
183
- const sub1 = createSubscriber({
184
- name: 'sub-1',
185
- description: 'Subscriber 1',
186
- callback: async () => {},
187
- });
188
- const sub2 = createSubscriber({
189
- name: 'sub-2',
190
- description: 'Subscriber 2',
191
- callback: async () => {},
192
- });
193
-
194
- registry.register(TestEvent, [sub1, sub2]);
195
-
196
- const subscriber = registry.getSubscriber('test.event', 'sub-2');
197
- expect(subscriber?.name).toBe('sub-2');
198
- });
199
-
200
- it('should return undefined for unknown subscriber', () => {
201
- const sub = createSubscriber({
202
- name: 'sub-1',
203
- description: 'Subscriber 1',
204
- callback: async () => {},
205
- });
206
- registry.register(TestEvent, [sub]);
207
-
208
- expect(registry.getSubscriber('test.event', 'unknown')).toBeUndefined();
209
- });
210
- });
211
-
212
- describe('getSubscriberDefinition', () => {
213
- it('should return subscriber definition for pipeline', () => {
214
- const sub = createSubscriber({
215
- name: 'sub-1',
216
- description: 'Subscriber 1',
217
- callback: async () => {},
218
- idempotent: 'yes',
219
- importance: 'must-investigate',
220
- targetQueue: 'custom-queue',
221
- });
222
-
223
- registry.register(TestEvent, [sub]);
224
-
225
- const definition = registry.getSubscriberDefinition(
226
- 'test.event',
227
- 'sub-1',
228
- );
229
- expect(definition).toEqual({
230
- name: 'sub-1',
231
- description: 'Subscriber 1',
232
- idempotent: 'yes',
233
- importance: 'must-investigate',
234
- targetQueue: 'custom-queue',
235
- });
236
- });
237
-
238
- it('should return undefined for unknown subscriber', () => {
239
- expect(
240
- registry.getSubscriberDefinition('unknown', 'unknown'),
241
- ).toBeUndefined();
242
- });
243
- });
244
-
245
- describe('getExecutableSubscriber', () => {
246
- it('should return subscriber with callback', () => {
247
- const callback = async () => {};
248
- const sub = createSubscriber({
249
- name: 'sub-1',
250
- description: 'Subscriber 1',
251
- callback,
252
- });
253
-
254
- registry.register(TestEvent, [sub]);
255
-
256
- const executable = registry.getExecutableSubscriber(
257
- 'test.event',
258
- 'sub-1',
259
- );
260
- expect(executable).toBeDefined();
261
- expect(executable?.callback).toBe(callback);
262
- });
263
- });
264
-
265
- describe('getEventKeys', () => {
266
- it('should return all registered event keys', () => {
267
- const sub1 = createSubscriber({
268
- name: 'sub-1',
269
- description: 'Subscriber 1',
270
- callback: async () => {},
271
- });
272
- const sub2 = createSubscriber({
273
- name: 'sub-2',
274
- description: 'Subscriber 2',
275
- callback: async () => {},
276
- });
277
-
278
- registry.register(TestEvent, [sub1]);
279
- registry.register(AliasedEvent, [sub2]);
280
-
281
- const keys = registry.getEventKeys();
282
- expect(keys).toContain('test.event');
283
- expect(keys).toContain('user.created.v2');
284
- expect(keys).not.toContain('user.created'); // aliases not included
285
- });
286
- });
287
-
288
- describe('validate', () => {
289
- it('should pass validation for valid schema', () => {
290
- const sub = createSubscriber({
291
- name: 'sub-1',
292
- description: 'Subscriber 1',
293
- callback: async () => {},
294
- });
295
- registry.register(TestEvent, [sub]);
296
-
297
- const result = registry.validate();
298
- expect(result.valid).toBe(true);
299
- expect(result.issues).toHaveLength(0);
300
- });
301
-
302
- it('should detect duplicate subscriber names', () => {
303
- const sub1 = createSubscriber({
304
- name: 'duplicate-name',
305
- description: 'Duplicate subscriber',
306
- callback: async () => {},
307
- });
308
- const sub2 = createSubscriber({
309
- name: 'duplicate-name',
310
- description: 'Duplicate subscriber',
311
- callback: async () => {},
312
- });
313
-
314
- registry.register(TestEvent, [sub1, sub2]);
315
-
316
- const result = registry.validate();
317
- expect(result.valid).toBe(false);
318
- expect(result.issues).toHaveLength(1);
319
- expect(result.issues[0]?.severity).toBe('error');
320
- expect(result.issues[0]?.message).toContain('Duplicate subscriber name');
321
- });
322
- });
323
-
324
- describe('clear', () => {
325
- it('should remove all entries', () => {
326
- const sub = createSubscriber({
327
- name: 'sub-1',
328
- description: 'Subscriber 1',
329
- callback: async () => {},
330
- });
331
- registry.register(TestEvent, [sub]);
332
-
333
- registry.clear();
334
-
335
- expect(registry.hasEvent('test.event')).toBe(false);
336
- expect(registry.getEventKeys()).toHaveLength(0);
337
- });
338
- });
339
- });
@@ -1,229 +0,0 @@
1
- import type { HasDescription } from '../errors/index.js';
2
- import type {
3
- AnySubscriber,
4
- EventClass,
5
- MatadorEvent,
6
- Subscriber,
7
- SubscriberDefinition,
8
- } from '../types/index.js';
9
- import { isSubscriber } from '../types/index.js';
10
- import type {
11
- RegisterOptions,
12
- SchemaEntry,
13
- SchemaIssue,
14
- SchemaValidationResult,
15
- } from './types.js';
16
-
17
- /**
18
- * Error thrown when schema operations fail.
19
- */
20
- export class SchemaError extends Error implements HasDescription {
21
- readonly description =
22
- 'A schema operation failed, such as registering a duplicate event or ' +
23
- 'conflicting alias. This is a configuration error that should be fixed ' +
24
- 'during development. Review the event and subscriber registrations to ' +
25
- 'resolve the conflict.';
26
-
27
- constructor(message: string) {
28
- super(message);
29
- this.name = 'SchemaError';
30
- }
31
- }
32
-
33
- /**
34
- * Registry for managing event-subscriber relationships.
35
- */
36
- export class SchemaRegistry {
37
- private readonly entries = new Map<string, SchemaEntry>();
38
- private readonly aliases = new Map<string, string>();
39
-
40
- /**
41
- * Registers an event class with its subscribers.
42
- */
43
- register<T>(
44
- eventClass: EventClass<T>,
45
- subscribers: readonly AnySubscriber[],
46
- options: RegisterOptions = {},
47
- ): void {
48
- const key = eventClass.key;
49
-
50
- if (this.entries.has(key) && !options.override) {
51
- throw new SchemaError(`Event "${key}" is already registered`);
52
- }
53
-
54
- this.entries.set(key, {
55
- eventClass,
56
- subscribers,
57
- } as SchemaEntry);
58
-
59
- // Register aliases
60
- if (eventClass.aliases) {
61
- for (const alias of eventClass.aliases) {
62
- if (
63
- this.aliases.has(alias) &&
64
- this.aliases.get(alias) !== key &&
65
- !options.override
66
- ) {
67
- throw new SchemaError(
68
- `Alias "${alias}" is already registered for event "${this.aliases.get(alias)}"`,
69
- );
70
- }
71
- this.aliases.set(alias, key);
72
- }
73
- }
74
- }
75
-
76
- /**
77
- * Gets the event class for a given key.
78
- */
79
- getEventClass(key: string): EventClass | undefined {
80
- const entry = this.entries.get(key);
81
- if (entry) return entry.eventClass;
82
-
83
- // Check aliases
84
- const aliasKey = this.aliases.get(key);
85
- if (aliasKey) {
86
- return this.entries.get(aliasKey)?.eventClass;
87
- }
88
-
89
- return undefined;
90
- }
91
-
92
- /**
93
- * Gets all subscribers for an event key.
94
- */
95
- getSubscribers(eventKey: string): readonly AnySubscriber[] {
96
- const entry = this.getEntry(eventKey);
97
- return entry?.subscribers ?? [];
98
- }
99
-
100
- /**
101
- * Gets a specific subscriber by event key and subscriber name.
102
- */
103
- getSubscriber(
104
- eventKey: string,
105
- subscriberName: string,
106
- ): AnySubscriber | undefined {
107
- const subscribers = this.getSubscribers(eventKey);
108
- return subscribers.find((s) => s.name === subscriberName);
109
- }
110
-
111
- /**
112
- * Gets a subscriber definition for the pipeline.
113
- */
114
- getSubscriberDefinition(
115
- eventKey: string,
116
- subscriberName: string,
117
- ): SubscriberDefinition | undefined {
118
- const subscriber = this.getSubscriber(eventKey, subscriberName);
119
- if (!subscriber) return undefined;
120
-
121
- const def: SubscriberDefinition = {
122
- name: subscriber.name,
123
- idempotent: subscriber.idempotent ?? 'unknown',
124
- importance: subscriber.importance ?? 'should-investigate',
125
- ...('description' in subscriber &&
126
- subscriber.description !== undefined && {
127
- description: subscriber.description,
128
- }),
129
- };
130
-
131
- if (subscriber.targetQueue !== undefined) {
132
- (def as { targetQueue?: string }).targetQueue = subscriber.targetQueue;
133
- }
134
-
135
- return def;
136
- }
137
-
138
- /**
139
- * Gets a subscriber with callback for execution.
140
- */
141
- getExecutableSubscriber(
142
- eventKey: string,
143
- subscriberName: string,
144
- ): Subscriber<MatadorEvent<unknown>> | undefined {
145
- const subscriber = this.getSubscriber(eventKey, subscriberName);
146
- if (!subscriber || !isSubscriber(subscriber)) return undefined;
147
- return subscriber;
148
- }
149
-
150
- /**
151
- * Gets the event class by alias.
152
- */
153
- getEventByAlias(alias: string): EventClass | undefined {
154
- const key = this.aliases.get(alias);
155
- if (!key) return undefined;
156
- return this.entries.get(key)?.eventClass;
157
- }
158
-
159
- /**
160
- * Checks if an event key is registered.
161
- */
162
- hasEvent(key: string): boolean {
163
- return this.entries.has(key) || this.aliases.has(key);
164
- }
165
-
166
- /**
167
- * Gets all registered event keys.
168
- */
169
- getEventKeys(): readonly string[] {
170
- return [...this.entries.keys()];
171
- }
172
-
173
- /**
174
- * Validates the schema for issues.
175
- */
176
- validate(): SchemaValidationResult {
177
- const issues: SchemaIssue[] = [];
178
-
179
- for (const [key, entry] of this.entries) {
180
- // Check for duplicate subscriber names
181
- const names = new Set<string>();
182
- for (const subscriber of entry.subscribers) {
183
- if (names.has(subscriber.name)) {
184
- issues.push({
185
- severity: 'error',
186
- eventKey: key,
187
- message: `Duplicate subscriber name: "${subscriber.name}"`,
188
- });
189
- }
190
- names.add(subscriber.name);
191
- }
192
-
193
- // Check for subscribers without callbacks (stubs in consuming instance)
194
- const stubs = entry.subscribers.filter((s) => !isSubscriber(s));
195
- if (stubs.length === entry.subscribers.length && stubs.length > 0) {
196
- issues.push({
197
- severity: 'warning',
198
- eventKey: key,
199
- message: 'All subscribers are stubs, no local handlers',
200
- });
201
- }
202
- }
203
-
204
- return {
205
- valid: issues.filter((i) => i.severity === 'error').length === 0,
206
- issues,
207
- };
208
- }
209
-
210
- /**
211
- * Clears all registered entries.
212
- */
213
- clear(): void {
214
- this.entries.clear();
215
- this.aliases.clear();
216
- }
217
-
218
- private getEntry(eventKey: string): SchemaEntry | undefined {
219
- const entry = this.entries.get(eventKey);
220
- if (entry) return entry;
221
-
222
- const aliasKey = this.aliases.get(eventKey);
223
- if (aliasKey) {
224
- return this.entries.get(aliasKey);
225
- }
226
-
227
- return undefined;
228
- }
229
- }