@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,280 +0,0 @@
1
- import { describe, expect, it } from 'bun:test';
2
- import {
3
- MatadorEvent,
4
- createSubscriber,
5
- createSubscriberStub,
6
- } from '../types/index.js';
7
- import { bind, installPlugins, isSchemaEntryTuple } from './types.js';
8
- import type { MatadorSchema } from './types.js';
9
-
10
- class UserCreatedEvent extends MatadorEvent {
11
- static readonly key = 'user.created';
12
- static readonly description = 'User created event';
13
-
14
- constructor(public data: { userId: string }) {
15
- super();
16
- }
17
- }
18
-
19
- class OrderPlacedEvent extends MatadorEvent {
20
- static readonly key = 'order.placed';
21
- static readonly description = 'Order placed event';
22
-
23
- constructor(public data: { orderId: string }) {
24
- super();
25
- }
26
- }
27
-
28
- class ChatMessageSentEvent extends MatadorEvent {
29
- static readonly key = 'chat.message.sent';
30
- static readonly description = 'Chat message sent event';
31
-
32
- constructor(public data: { messageId: string }) {
33
- super();
34
- }
35
- }
36
-
37
- describe('bind', () => {
38
- it('should create a schema entry tuple', () => {
39
- const subscriber = createSubscriber<UserCreatedEvent>({
40
- name: 'test-sub',
41
- description: 'Test subscriber',
42
- callback: async () => {},
43
- });
44
- const entry = bind(UserCreatedEvent, [subscriber]);
45
-
46
- expect(isSchemaEntryTuple(entry)).toBe(true);
47
- expect(entry[0]).toBe(UserCreatedEvent);
48
- expect(entry[1]).toHaveLength(1);
49
- expect(entry[1][0]).toBe(subscriber);
50
- });
51
- });
52
-
53
- describe('isSchemaEntryTuple', () => {
54
- it('should return true for tuple format', () => {
55
- const entry = [UserCreatedEvent, []] as const;
56
- expect(isSchemaEntryTuple(entry)).toBe(true);
57
- });
58
-
59
- it('should return false for object format', () => {
60
- const entry = { eventClass: UserCreatedEvent, subscribers: [] };
61
- expect(isSchemaEntryTuple(entry)).toBe(false);
62
- });
63
- });
64
-
65
- describe('installPlugins', () => {
66
- it('should add plugin subscriber to all events', () => {
67
- const sub1 = createSubscriber<UserCreatedEvent>({
68
- name: 'user-handler',
69
- description: 'Handles user events',
70
- callback: async () => {},
71
- });
72
- const sub2 = createSubscriber<OrderPlacedEvent>({
73
- name: 'order-handler',
74
- description: 'Handles order events',
75
- callback: async () => {},
76
- });
77
- const globalSub = createSubscriber<MatadorEvent>({
78
- name: 'analytics',
79
- description: 'Analytics subscriber',
80
- callback: async () => {},
81
- });
82
-
83
- const baseSchema: MatadorSchema = {
84
- [UserCreatedEvent.key]: [UserCreatedEvent, [sub1]],
85
- [OrderPlacedEvent.key]: [OrderPlacedEvent, [sub2]],
86
- };
87
-
88
- const schema = installPlugins(baseSchema, [{ subscriber: globalSub }]);
89
-
90
- // Check user.created has both subscribers
91
- const userEntry = schema[UserCreatedEvent.key];
92
- expect(isSchemaEntryTuple(userEntry!)).toBe(true);
93
- const [, userSubs] = userEntry as [unknown, readonly unknown[]];
94
- expect(userSubs).toHaveLength(2);
95
- expect(userSubs[0]).toBe(sub1);
96
- expect(userSubs[1]).toBe(globalSub);
97
-
98
- // Check order.placed has both subscribers
99
- const orderEntry = schema[OrderPlacedEvent.key];
100
- const [, orderSubs] = orderEntry as [unknown, readonly unknown[]];
101
- expect(orderSubs).toHaveLength(2);
102
- expect(orderSubs[0]).toBe(sub2);
103
- expect(orderSubs[1]).toBe(globalSub);
104
- });
105
-
106
- it('should respect exclusions', () => {
107
- const sub1 = createSubscriber<UserCreatedEvent>({
108
- name: 'user-handler',
109
- description: 'Handles user events',
110
- callback: async () => {},
111
- });
112
- const sub2 = createSubscriber<OrderPlacedEvent>({
113
- name: 'order-handler',
114
- description: 'Handles order events',
115
- callback: async () => {},
116
- });
117
- const sub3 = createSubscriber<ChatMessageSentEvent>({
118
- name: 'chat-handler',
119
- description: 'Handles chat events',
120
- callback: async () => {},
121
- });
122
- const globalSub = createSubscriber<MatadorEvent>({
123
- name: 'analytics',
124
- description: 'Analytics subscriber',
125
- callback: async () => {},
126
- });
127
-
128
- const baseSchema: MatadorSchema = {
129
- [UserCreatedEvent.key]: [UserCreatedEvent, [sub1]],
130
- [OrderPlacedEvent.key]: [OrderPlacedEvent, [sub2]],
131
- [ChatMessageSentEvent.key]: [ChatMessageSentEvent, [sub3]],
132
- };
133
-
134
- const schema = installPlugins(baseSchema, [
135
- {
136
- subscriber: globalSub,
137
- exclusions: [ChatMessageSentEvent.key],
138
- },
139
- ]);
140
-
141
- // user.created should have analytics
142
- const userEntry = schema[UserCreatedEvent.key];
143
- const [, userSubs] = userEntry as [unknown, readonly unknown[]];
144
- expect(userSubs).toHaveLength(2);
145
-
146
- // order.placed should have analytics
147
- const orderEntry = schema[OrderPlacedEvent.key];
148
- const [, orderSubs] = orderEntry as [unknown, readonly unknown[]];
149
- expect(orderSubs).toHaveLength(2);
150
-
151
- // chat.message.sent should NOT have analytics (excluded)
152
- const chatEntry = schema[ChatMessageSentEvent.key];
153
- const [, chatSubs] = chatEntry as [unknown, readonly unknown[]];
154
- expect(chatSubs).toHaveLength(1);
155
- expect(chatSubs[0]).toBe(sub3);
156
- });
157
-
158
- it('should handle multiple plugins', () => {
159
- const sub1 = createSubscriber<UserCreatedEvent>({
160
- name: 'user-handler',
161
- description: 'Handles user events',
162
- callback: async () => {},
163
- });
164
- const analyticsSub = createSubscriber<MatadorEvent>({
165
- name: 'analytics',
166
- description: 'Analytics subscriber',
167
- callback: async () => {},
168
- });
169
- const loggingSub = createSubscriber<MatadorEvent>({
170
- name: 'logging',
171
- description: 'Logging subscriber',
172
- callback: async () => {},
173
- });
174
-
175
- const baseSchema: MatadorSchema = {
176
- [UserCreatedEvent.key]: [UserCreatedEvent, [sub1]],
177
- };
178
-
179
- const schema = installPlugins(baseSchema, [
180
- { subscriber: analyticsSub },
181
- { subscriber: loggingSub },
182
- ]);
183
-
184
- const entry = schema[UserCreatedEvent.key];
185
- const [, subs] = entry as [unknown, readonly unknown[]];
186
- expect(subs).toHaveLength(3);
187
- expect(subs[0]).toBe(sub1);
188
- expect(subs[1]).toBe(analyticsSub);
189
- expect(subs[2]).toBe(loggingSub);
190
- });
191
-
192
- it('should handle object format schema entries', () => {
193
- const sub1 = createSubscriber<UserCreatedEvent>({
194
- name: 'user-handler',
195
- description: 'Handles user events',
196
- callback: async () => {},
197
- });
198
- const globalSub = createSubscriber<MatadorEvent>({
199
- name: 'analytics',
200
- description: 'Analytics subscriber',
201
- callback: async () => {},
202
- });
203
-
204
- const baseSchema: MatadorSchema = {
205
- [UserCreatedEvent.key]: {
206
- eventClass: UserCreatedEvent,
207
- subscribers: [sub1],
208
- },
209
- };
210
-
211
- const schema = installPlugins(baseSchema, [{ subscriber: globalSub }]);
212
-
213
- const entry = schema[UserCreatedEvent.key];
214
- expect(isSchemaEntryTuple(entry!)).toBe(true);
215
- const [eventClass, subs] = entry as [unknown, readonly unknown[]];
216
- expect(eventClass).toBe(UserCreatedEvent);
217
- expect(subs).toHaveLength(2);
218
- });
219
-
220
- it('should work with subscriber stubs', () => {
221
- const sub1 = createSubscriber<UserCreatedEvent>({
222
- name: 'user-handler',
223
- description: 'Handles user events',
224
- callback: async () => {},
225
- });
226
- const stubSub = createSubscriberStub({
227
- name: 'remote-analytics',
228
- targetQueue: 'analytics-worker',
229
- });
230
-
231
- const baseSchema: MatadorSchema = {
232
- [UserCreatedEvent.key]: [UserCreatedEvent, [sub1]],
233
- };
234
-
235
- const schema = installPlugins(baseSchema, [{ subscriber: stubSub }]);
236
-
237
- const entry = schema[UserCreatedEvent.key];
238
- const [, subs] = entry as [unknown, readonly unknown[]];
239
- expect(subs).toHaveLength(2);
240
- expect(subs[1]).toBe(stubSub);
241
- });
242
-
243
- it('should return empty schema for empty input', () => {
244
- const schema = installPlugins({}, [
245
- {
246
- subscriber: createSubscriber<MatadorEvent>({
247
- name: 'analytics',
248
- description: 'Analytics subscriber',
249
- callback: async () => {},
250
- }),
251
- },
252
- ]);
253
-
254
- expect(Object.keys(schema)).toHaveLength(0);
255
- });
256
-
257
- it('should not modify original schema', () => {
258
- const sub1 = createSubscriber<UserCreatedEvent>({
259
- name: 'user-handler',
260
- description: 'Handles user events',
261
- callback: async () => {},
262
- });
263
- const globalSub = createSubscriber<MatadorEvent>({
264
- name: 'analytics',
265
- description: 'Analytics subscriber',
266
- callback: async () => {},
267
- });
268
-
269
- const baseSchema: MatadorSchema = {
270
- [UserCreatedEvent.key]: [UserCreatedEvent, [sub1]],
271
- };
272
-
273
- installPlugins(baseSchema, [{ subscriber: globalSub }]);
274
-
275
- // Original schema should be unchanged
276
- const entry = baseSchema[UserCreatedEvent.key];
277
- const [, subs] = entry as [unknown, readonly unknown[]];
278
- expect(subs).toHaveLength(1);
279
- });
280
- });
@@ -1,217 +0,0 @@
1
- import type {
2
- AnySubscriber,
3
- EventClass,
4
- MatadorEvent,
5
- Subscriber,
6
- SubscriberStub,
7
- } from '../types/index.js';
8
-
9
- /**
10
- * Type-safe schema entry for a single event type (object format).
11
- * The type parameter enforces that subscribers match the event type.
12
- */
13
- export interface SchemaEntry<T extends MatadorEvent = MatadorEvent> {
14
- /** The event class */
15
- readonly eventClass: EventClass<T['data']>;
16
-
17
- /** Subscribers for this event */
18
- readonly subscribers: readonly (Subscriber<T> | SubscriberStub)[];
19
- }
20
-
21
- /**
22
- * Type-safe schema entry as a tuple: [EventClass, Subscribers[]]
23
- * The type parameter enforces that subscribers match the event type.
24
- *
25
- * @example
26
- * ```typescript
27
- * const entry: SchemaEntryTuple<UserCreatedEvent> = [
28
- * UserCreatedEvent,
29
- * [createSubscriber<UserCreatedEvent>('handler', async (env) => {})]
30
- * ];
31
- * ```
32
- */
33
- export type SchemaEntryTuple<T extends MatadorEvent = MatadorEvent> = readonly [
34
- eventClass: EventClass<T['data']>,
35
- subscribers: readonly (Subscriber<T> | SubscriberStub)[],
36
- ];
37
-
38
- /**
39
- * Creates a type-safe schema entry tuple.
40
- * Ensures that subscribers are compatible with the event class at compile time.
41
- *
42
- * @example
43
- * ```typescript
44
- * const schema = {
45
- * [UserCreatedEvent.key]: bind(UserCreatedEvent, [
46
- * createSubscriber<UserCreatedEvent>('send-email', async (env) => {
47
- * console.log(env.data.email); // Type-safe access
48
- * }),
49
- * ]),
50
- * } satisfies MatadorSchema;
51
- * ```
52
- */
53
- export function bind<T extends MatadorEvent>(
54
- eventClass: EventClass<T['data']>,
55
- subscribers: readonly (Subscriber<T> | SubscriberStub)[],
56
- ): SchemaEntryTuple<T> {
57
- return [eventClass, subscribers] as const;
58
- }
59
-
60
- /**
61
- * Minimal type for event class in schema storage.
62
- * Only requires the static `key` property for routing.
63
- * Constructor constraint is omitted to allow heterogeneous event types.
64
- */
65
- // biome-ignore lint/suspicious/noExplicitAny: Constructor accepts any data type for variance compatibility
66
- export type AnyEventClass = {
67
- readonly key: string;
68
- new (data: any): MatadorEvent;
69
- };
70
-
71
- /**
72
- * Runtime schema entry stored in MatadorSchema (object format).
73
- * Uses AnySubscriber and AnyEventClass to allow heterogeneous event types.
74
- * Type safety is enforced at definition time via `schemaEntry()` helper.
75
- */
76
- export interface RuntimeSchemaEntry {
77
- readonly eventClass: AnyEventClass;
78
- readonly subscribers: readonly AnySubscriber[];
79
- }
80
-
81
- /**
82
- * Runtime schema tuple stored in MatadorSchema.
83
- * Uses AnySubscriber and AnyEventClass to allow heterogeneous event types.
84
- * Type safety is enforced at definition time via `schemaEntry()` helper.
85
- */
86
- export type RuntimeSchemaEntryTuple = readonly [
87
- eventClass: AnyEventClass,
88
- subscribers: readonly AnySubscriber[],
89
- ];
90
-
91
- /**
92
- * Matador schema mapping event keys to their definitions.
93
- * Supports both object format (SchemaEntry) and tuple format (SchemaEntryTuple).
94
- *
95
- * For type-safe schema definitions, use the `bind` helper function
96
- * which ensures subscribers are compatible with their event class.
97
- *
98
- * @example
99
- * ```typescript
100
- * // Type-safe with bind helper:
101
- * const schema = {
102
- * [UserCreatedEvent.key]: bind(UserCreatedEvent, [emailSubscriber]),
103
- * } satisfies MatadorSchema;
104
- *
105
- * // Or inline (less type-safe):
106
- * const schema: MatadorSchema = {
107
- * [UserCreatedEvent.key]: [UserCreatedEvent, [emailSubscriber]],
108
- * };
109
- * ```
110
- */
111
- export type MatadorSchema = {
112
- readonly [eventKey: string]: RuntimeSchemaEntry | RuntimeSchemaEntryTuple;
113
- };
114
-
115
- /**
116
- * Type guard to check if a schema entry is in tuple format.
117
- */
118
- export function isSchemaEntryTuple(
119
- entry: RuntimeSchemaEntry | RuntimeSchemaEntryTuple,
120
- ): entry is RuntimeSchemaEntryTuple {
121
- return Array.isArray(entry);
122
- }
123
-
124
- /**
125
- * Options for schema registration.
126
- */
127
- export interface RegisterOptions {
128
- /** Override existing registration if present */
129
- readonly override?: boolean;
130
- }
131
-
132
- /**
133
- * Result of schema validation.
134
- */
135
- export interface SchemaValidationResult {
136
- readonly valid: boolean;
137
- readonly issues: readonly SchemaIssue[];
138
- }
139
-
140
- /**
141
- * Individual schema issue.
142
- */
143
- export interface SchemaIssue {
144
- readonly severity: 'error' | 'warning';
145
- readonly eventKey: string;
146
- readonly message: string;
147
- }
148
-
149
- /**
150
- * Plugin definition for adding subscribers to multiple events.
151
- */
152
- export interface SchemaPlugin {
153
- /** The subscriber to add to events */
154
- readonly subscriber: AnySubscriber;
155
-
156
- /** Event keys to exclude from this plugin */
157
- readonly exclusions?: readonly string[];
158
- }
159
-
160
- /**
161
- * Installs plugins (global subscribers) into a schema.
162
- * Each plugin subscriber is added to all events except those in its exclusions list.
163
- *
164
- * @param schema - The base schema to extend
165
- * @param plugins - Array of plugins to install
166
- * @returns A new schema with plugin subscribers added
167
- *
168
- * @example
169
- * ```typescript
170
- * const baseSchema: MatadorSchema = {
171
- * [UserCreatedEvent.key]: [UserCreatedEvent, [sendWelcomeEmail]],
172
- * [OrderPlacedEvent.key]: [OrderPlacedEvent, [processOrder]],
173
- * [ChatMessageSent.key]: [ChatMessageSent, [notifyRecipient]],
174
- * };
175
- *
176
- * const schema = installPlugins(baseSchema, [
177
- * {
178
- * subscriber: logToBigQuery,
179
- * exclusions: [ChatMessageSent.key], // Don't log chat messages
180
- * },
181
- * {
182
- * subscriber: trackAnalytics,
183
- * // No exclusions - added to all events
184
- * },
185
- * ]);
186
- * ```
187
- */
188
- export function installPlugins(
189
- schema: MatadorSchema,
190
- plugins: readonly SchemaPlugin[],
191
- ): MatadorSchema {
192
- const result: Record<string, RuntimeSchemaEntry | RuntimeSchemaEntryTuple> =
193
- {};
194
-
195
- for (const [eventKey, entry] of Object.entries(schema)) {
196
- // Get existing event class and subscribers
197
- const [eventClass, existingSubscribers] = isSchemaEntryTuple(entry)
198
- ? [entry[0], entry[1]]
199
- : [entry.eventClass, entry.subscribers];
200
-
201
- // Collect subscribers to add from plugins
202
- const pluginSubscribers: AnySubscriber[] = [];
203
- for (const plugin of plugins) {
204
- if (!plugin.exclusions?.includes(eventKey)) {
205
- pluginSubscribers.push(plugin.subscriber);
206
- }
207
- }
208
-
209
- // Create new entry with combined subscribers
210
- result[eventKey] = [
211
- eventClass,
212
- [...existingSubscribers, ...pluginSubscribers],
213
- ];
214
- }
215
-
216
- return result;
217
- }