@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,200 +0,0 @@
1
- import type { Topology } from '../topology/types.js';
2
- import type { Envelope } from '../types/index.js';
3
- import type { TransportCapabilities } from './capabilities.js';
4
-
5
- /**
6
- * Transport-specific send options.
7
- * Each transport can define its own options under its transport name key.
8
- */
9
- export interface TransportSendOptions {
10
- /** RabbitMQ-specific send options */
11
- readonly rabbitmq?: RabbitMQSendOptions | undefined;
12
- }
13
-
14
- /**
15
- * RabbitMQ-specific options for sending messages.
16
- */
17
- export interface RabbitMQSendOptions {
18
- /** Message expiration in milliseconds */
19
- readonly expiration?: number | undefined;
20
-
21
- /** Message persistence mode */
22
- readonly persistent?: boolean | undefined;
23
-
24
- /** Routing key override */
25
- readonly routingKey?: string | undefined;
26
- }
27
-
28
- /**
29
- * Options for sending a message.
30
- */
31
- export interface SendOptions {
32
- /** Delay delivery by this many milliseconds */
33
- readonly delay?: number | undefined;
34
-
35
- /** Priority level (0-255, higher = more important) */
36
- readonly priority?: number | undefined;
37
-
38
- /** Transport-specific options */
39
- readonly transport?: TransportSendOptions | undefined;
40
- }
41
-
42
- /**
43
- * Transport-specific subscribe options.
44
- * Each transport can define its own options under its transport name key.
45
- */
46
- export interface TransportSubscribeOptions {
47
- /** RabbitMQ-specific subscribe options */
48
- readonly rabbitmq?: RabbitMQSubscribeOptions | undefined;
49
- }
50
-
51
- /**
52
- * RabbitMQ-specific options for subscribing.
53
- */
54
- export interface RabbitMQSubscribeOptions {
55
- /** Consumer tag */
56
- readonly consumerTag?: string | undefined;
57
-
58
- /** Prefetch count (overrides concurrency option) */
59
- readonly prefetch?: number | undefined;
60
-
61
- /** Exclusive consumer */
62
- readonly exclusive?: boolean | undefined;
63
- }
64
-
65
- /**
66
- * Options for subscribing to a queue.
67
- */
68
- export interface SubscribeOptions {
69
- /** Concurrency hint (number of concurrent handlers) */
70
- readonly concurrency?: number | undefined;
71
-
72
- /** Override default delivery semantics */
73
- readonly deliveryMode?: 'at-least-once' | 'at-most-once' | undefined;
74
-
75
- /** Transport-specific options */
76
- readonly transport?: TransportSubscribeOptions | undefined;
77
- }
78
-
79
- /**
80
- * Receipt for a received message, used for acknowledgment.
81
- */
82
- export interface MessageReceipt {
83
- /** Opaque handle for the transport to identify the message */
84
- readonly handle: unknown;
85
-
86
- /** True if this is a redelivery (transport-reported if capable) */
87
- readonly redelivered: boolean;
88
-
89
- /** 1-based attempt number (transport-reported if capable, else from envelope) */
90
- readonly attemptNumber: number;
91
-
92
- /**
93
- * Native delivery count from the transport.
94
- * Tracks how many times this specific message was delivered without acknowledgment.
95
- * Used for poison message detection to prevent crash loops.
96
- * For transports that don't track this, defaults to attemptNumber.
97
- */
98
- readonly deliveryCount: number;
99
-
100
- /** Original queue/topic the message came from */
101
- readonly sourceQueue: string;
102
-
103
- /**
104
- * The name of the transport that received this message (e.g., 'local', 'rabbitmq').
105
- * For MultiTransport, this is the actual underlying transport, not the wrapper name.
106
- */
107
- readonly sourceTransport: string;
108
- }
109
-
110
- /**
111
- * Handler function for processing received messages.
112
- */
113
- export type MessageHandler = (
114
- envelope: Envelope,
115
- receipt: MessageReceipt,
116
- ) => Promise<void>;
117
-
118
- /**
119
- * Subscription handle for managing active subscriptions.
120
- */
121
- export interface Subscription {
122
- /** Cancels the subscription */
123
- unsubscribe(): Promise<void>;
124
-
125
- /** Whether the subscription is currently active */
126
- readonly isActive: boolean;
127
- }
128
-
129
- /**
130
- * Transport interface - the minimal abstraction for message delivery.
131
- * Transports handle only I/O; all business logic lives in Matador core.
132
- */
133
- export interface Transport {
134
- /** Transport identifier */
135
- readonly name: string;
136
-
137
- /** Capabilities supported by this transport */
138
- readonly capabilities: TransportCapabilities;
139
-
140
- /**
141
- * Establishes connection to the message broker.
142
- * Should handle initial connection with retries.
143
- */
144
- connect(): Promise<void>;
145
-
146
- /**
147
- * Gracefully disconnects from the message broker.
148
- * Should close all consumers before connection.
149
- */
150
- disconnect(): Promise<void>;
151
-
152
- /**
153
- * Returns whether the transport is currently connected.
154
- */
155
- isConnected(): boolean;
156
-
157
- /**
158
- * Translates and applies the generic topology to the transport.
159
- * Creates necessary queues, exchanges, topics, etc.
160
- */
161
- applyTopology(topology: Topology): Promise<void>;
162
-
163
- /**
164
- * Sends a message to the specified queue.
165
- * @returns The name of the transport that was used (useful for MultiTransport)
166
- */
167
- send(
168
- queue: string,
169
- envelope: Envelope,
170
- options?: SendOptions,
171
- ): Promise<Transport['name']>;
172
-
173
- /**
174
- * Subscribes to messages on the specified queue.
175
- * The handler receives decoded envelopes and receipts.
176
- */
177
- subscribe(
178
- queue: string,
179
- handler: MessageHandler,
180
- options?: SubscribeOptions,
181
- ): Promise<Subscription>;
182
-
183
- /**
184
- * Acknowledges/completes a message.
185
- * Called after processing is done (success, retry scheduled, or dead-lettered).
186
- */
187
- complete(receipt: MessageReceipt): Promise<void>;
188
-
189
- /**
190
- * Sends a message to the dead-letter queue.
191
- * For transports with native DL routing, may use native mechanism.
192
- * For others, sends to DLQ then completes original.
193
- */
194
- sendToDeadLetter?(
195
- receipt: MessageReceipt,
196
- dlqName: string,
197
- envelope: Envelope,
198
- reason: string,
199
- ): Promise<void>;
200
- }
@@ -1,53 +0,0 @@
1
- /**
2
- * Delivery semantics for message processing.
3
- * - 'at-least-once': Acknowledge after processing (may redeliver on failure)
4
- * - 'at-most-once': Acknowledge before processing (no redelivery, may lose messages)
5
- */
6
- export type DeliveryMode = 'at-least-once' | 'at-most-once';
7
-
8
- /**
9
- * Importance level for subscribers, used for monitoring and alerting prioritization.
10
- */
11
- export type Importance =
12
- | 'can-ignore'
13
- | 'should-investigate'
14
- | 'must-investigate';
15
-
16
- /**
17
- * Idempotency declaration for subscribers.
18
- * - 'yes': Safe to retry on failure (subscriber handles duplicates)
19
- * - 'no': Not safe to retry, may cause duplicate side effects
20
- * - 'unknown': Idempotency not determined (default)
21
- * - 'resumable': Uses checkpoint-based idempotency via io() calls
22
- */
23
- export type Idempotency = 'yes' | 'no' | 'unknown' | 'resumable';
24
-
25
- /**
26
- * Result of a validation operation.
27
- */
28
- export interface ValidationResult {
29
- readonly valid: boolean;
30
- readonly errors: readonly ValidationError[];
31
- }
32
-
33
- /**
34
- * Individual validation error.
35
- */
36
- export interface ValidationError {
37
- readonly path: string;
38
- readonly message: string;
39
- }
40
-
41
- /**
42
- * Creates a successful validation result.
43
- */
44
- export function validResult(): ValidationResult {
45
- return { valid: true, errors: [] };
46
- }
47
-
48
- /**
49
- * Creates a failed validation result.
50
- */
51
- export function invalidResult(errors: ValidationError[]): ValidationResult {
52
- return { valid: false, errors };
53
- }
@@ -1,18 +0,0 @@
1
- import type { SendResult } from '../core/fanout.js';
2
- import type { Event, EventClass, EventOptions } from './event.js';
3
-
4
- /**
5
- * Interface for dispatching events.
6
- * Implemented by Matador to allow subscribers to send events.
7
- */
8
- export interface Dispatcher {
9
- /**
10
- * Sends an event to all registered subscribers.
11
- */
12
- send<T>(
13
- eventClass: EventClass<T>,
14
- data: T,
15
- options?: EventOptions,
16
- ): Promise<SendResult>;
17
- send<T>(event: Event<T>, options?: EventOptions): Promise<SendResult>;
18
- }
@@ -1,244 +0,0 @@
1
- import type { Importance } from './common.js';
2
- import type { Event, EventStatic } from './event.js';
3
-
4
- /**
5
- * Message envelope containing the event data and routing/observability metadata.
6
- * This is the transport-agnostic message format used throughout Matador.
7
- */
8
- export interface Envelope<T = unknown> {
9
- /** Unique message ID (UUID v4) */
10
- readonly id: string;
11
-
12
- /** The event data */
13
- readonly data: T;
14
-
15
- /** Routing, processing state, and observability metadata */
16
- readonly docket: Docket;
17
- }
18
-
19
- /**
20
- * Metadata associated with an envelope for routing, processing state, and observability.
21
- */
22
- export interface Docket {
23
- // === Routing ===
24
-
25
- /** Event key for routing */
26
- readonly eventKey: string;
27
-
28
- /** Human-readable description of the event (for observability/logging) */
29
- readonly eventDescription?: string | undefined;
30
-
31
- /** Target subscriber name for 1:1 routing */
32
- readonly targetSubscriber: string;
33
-
34
- /** Original queue before any dead-letter routing */
35
- originalQueue?: string | undefined;
36
-
37
- /** Scheduled processing time for delayed messages (ISO 8601 string) */
38
- scheduledFor?: string | undefined;
39
-
40
- // === Processing State ===
41
-
42
- /**
43
- * Attempt counter managed by Matador (1-based).
44
- * Incremented on each retry. Used when transport doesn't track attempts.
45
- */
46
- attempts: number;
47
-
48
- /** When the envelope was first created (ISO 8601 string) */
49
- readonly createdAt: string;
50
-
51
- /** Error message from first failure (for debugging) */
52
- firstError?: string | undefined;
53
-
54
- /** Error message from most recent failure */
55
- lastError?: string | undefined;
56
-
57
- // === Observability ===
58
-
59
- /** Importance level for monitoring */
60
- readonly importance: Importance;
61
-
62
- /** Correlation ID for request tracing */
63
- readonly correlationId?: string | undefined;
64
-
65
- /**
66
- * Custom metadata provided by the application.
67
- * This is the merged result of universal metadata (from loadUniversalMetadata hook)
68
- * and event-specific metadata (from dispatch options). Event-specific metadata
69
- * overrides universal metadata when keys conflict.
70
- */
71
- readonly metadata?: Record<string, unknown> | undefined;
72
- }
73
-
74
- /**
75
- * Fields from Docket that can be specified when creating an envelope.
76
- */
77
- type DocketCreateFields = Pick<
78
- Docket,
79
- | 'eventKey'
80
- | 'eventDescription'
81
- | 'targetSubscriber'
82
- | 'importance'
83
- | 'correlationId'
84
- >;
85
-
86
- /**
87
- * Options for creating an envelope.
88
- */
89
- export interface CreateEnvelopeOptions<T> extends DocketCreateFields {
90
- /** Optional custom ID (defaults to UUID v4) */
91
- readonly id?: string | undefined;
92
-
93
- /** The event data */
94
- readonly data: T;
95
-
96
- /**
97
- * Event-specific metadata to include in the docket.
98
- * Will be merged with universal metadata, with these values taking precedence.
99
- */
100
- readonly metadata?: Record<string, unknown> | undefined;
101
-
102
- /**
103
- * Universal metadata loaded from the loadUniversalMetadata hook.
104
- * This is provided by the fanout engine, not by the caller.
105
- * @internal
106
- */
107
- readonly universalMetadata?: Record<string, unknown> | undefined;
108
-
109
- /** Delay processing by this many milliseconds */
110
- readonly delayMs?: number | undefined;
111
- }
112
-
113
- /**
114
- * Creates a new envelope with the provided options.
115
- */
116
- export function createEnvelope<T>(
117
- options: CreateEnvelopeOptions<T>,
118
- ): Envelope<T> {
119
- const now = new Date().toISOString();
120
-
121
- // Merge universal metadata with event-specific metadata
122
- // Event-specific metadata takes precedence
123
- const mergedMetadata =
124
- options.universalMetadata || options.metadata
125
- ? { ...options.universalMetadata, ...options.metadata }
126
- : undefined;
127
-
128
- return {
129
- id: options.id ?? crypto.randomUUID(),
130
- data: options.data,
131
- docket: {
132
- // Routing
133
- eventKey: options.eventKey,
134
- ...(options.eventDescription !== undefined && {
135
- eventDescription: options.eventDescription,
136
- }),
137
- targetSubscriber: options.targetSubscriber,
138
- ...(options.delayMs !== undefined &&
139
- options.delayMs > 0 && {
140
- scheduledFor: new Date(Date.now() + options.delayMs).toISOString(),
141
- }),
142
- // Processing state
143
- attempts: 1,
144
- createdAt: now,
145
- // Observability
146
- importance: options.importance,
147
- ...(options.correlationId !== undefined && {
148
- correlationId: options.correlationId,
149
- }),
150
- ...(mergedMetadata !== undefined && { metadata: mergedMetadata }),
151
- },
152
- };
153
- }
154
-
155
- /**
156
- * Options for creating a dummy envelope.
157
- * All fields are optional and will use sensible defaults if not provided.
158
- */
159
- export interface CreateDummyEnvelopeOptions {
160
- readonly id?: string | undefined;
161
- readonly eventKey?: string | undefined;
162
- readonly eventDescription?: string | undefined;
163
- readonly targetSubscriber?: string | undefined;
164
- readonly importance?: Importance | undefined;
165
- readonly correlationId?: string | undefined;
166
- readonly metadata?: Record<string, unknown> | undefined;
167
- readonly delayMs?: number | undefined;
168
- }
169
-
170
- /**
171
- * Type helper to extract the data type from an Event, or return T as-is.
172
- */
173
- type ExtractData<T> = T extends Event<infer D> ? D : T;
174
-
175
- /**
176
- * Checks if a value is a Matador Event (has data property and constructor with key).
177
- */
178
- function isEvent<T>(value: unknown): value is Event<T> {
179
- return (
180
- typeof value === 'object' &&
181
- value !== null &&
182
- 'data' in value &&
183
- typeof (value.constructor as EventStatic).key === 'string'
184
- );
185
- }
186
-
187
- /**
188
- * Helper to create a test envelope for a given event instance or raw data.
189
- * Useful for unit testing subscriber callbacks directly.
190
- *
191
- * When passing an Event instance, the eventKey and eventDescription will be
192
- * automatically extracted from the event class (unless overridden in options).
193
- *
194
- * @example With raw data
195
- * ```typescript
196
- * const envelope = createDummyEnvelope({ userId: '123', email: 'test@example.com' });
197
- * ```
198
- *
199
- * @example With an Event instance
200
- * ```typescript
201
- * const event = new UserCreatedEvent({ userId: '123', email: 'test@example.com' });
202
- * const envelope = createDummyEnvelope(event);
203
- * // eventKey is automatically set to UserCreatedEvent.key
204
- * await mySubscriber.callback(envelope, event.data);
205
- * ```
206
- *
207
- * @example With options
208
- * ```typescript
209
- * const event = new UserCreatedEvent({ userId: '123', email: 'test@example.com' });
210
- * const envelope = createDummyEnvelope(event, {
211
- * metadata: { traceId: 'abc-123' },
212
- * correlationId: 'request-456',
213
- * });
214
- * ```
215
- */
216
- export function createDummyEnvelope<T>(
217
- dataOrEvent: T,
218
- options?: CreateDummyEnvelopeOptions,
219
- ): Envelope<ExtractData<T>> {
220
- let data: ExtractData<T>;
221
- let eventKey = options?.eventKey ?? 'dummy.event.key';
222
- let eventDescription = options?.eventDescription;
223
-
224
- if (isEvent(dataOrEvent)) {
225
- data = dataOrEvent.data as ExtractData<T>;
226
- const eventStatic = dataOrEvent.constructor as EventStatic;
227
- eventKey = options?.eventKey ?? eventStatic.key;
228
- eventDescription = options?.eventDescription ?? eventStatic.description;
229
- } else {
230
- data = dataOrEvent as ExtractData<T>;
231
- }
232
-
233
- return createEnvelope({
234
- data,
235
- eventKey,
236
- eventDescription,
237
- targetSubscriber: options?.targetSubscriber ?? 'dummy-subscriber',
238
- importance: options?.importance ?? 'can-ignore',
239
- id: options?.id,
240
- correlationId: options?.correlationId,
241
- metadata: options?.metadata,
242
- delayMs: options?.delayMs,
243
- });
244
- }
@@ -1,157 +0,0 @@
1
- import { describe, expect, it } from 'bun:test';
2
- import { createEnvelope } from './envelope.js';
3
- import { MatadorEvent } from './event.js';
4
-
5
- class UserCreatedEvent extends MatadorEvent {
6
- static readonly key = 'user.created';
7
- static readonly description = 'Fired when a new user is created';
8
-
9
- constructor(public data: { userId: string; email: string }) {
10
- super();
11
- }
12
- }
13
-
14
- class OrderPlacedEvent extends MatadorEvent {
15
- static readonly key = 'order.placed';
16
- static readonly description = 'Fired when an order is placed';
17
- static readonly aliases = ['order.created'];
18
-
19
- constructor(public data: { orderId: string; amount: number }) {
20
- super();
21
- }
22
- }
23
-
24
- class MinimalEvent extends MatadorEvent {
25
- static readonly key = 'minimal.event';
26
-
27
- constructor(public data: { id: string }) {
28
- super();
29
- }
30
- }
31
-
32
- describe('Event', () => {
33
- describe('static fields', () => {
34
- it('should have static key field on class', () => {
35
- expect(UserCreatedEvent.key).toBe('user.created');
36
- expect(OrderPlacedEvent.key).toBe('order.placed');
37
- expect(MinimalEvent.key).toBe('minimal.event');
38
- });
39
-
40
- it('should have static description field on class when defined', () => {
41
- expect(UserCreatedEvent.description).toBe(
42
- 'Fired when a new user is created',
43
- );
44
- expect(OrderPlacedEvent.description).toBe(
45
- 'Fired when an order is placed',
46
- );
47
- expect(MinimalEvent.description).toBeUndefined();
48
- });
49
-
50
- it('should have static aliases field on class when defined', () => {
51
- expect(UserCreatedEvent.aliases).toBeUndefined();
52
- expect(OrderPlacedEvent.aliases).toEqual(['order.created']);
53
- expect(MinimalEvent.aliases).toBeUndefined();
54
- });
55
- });
56
-
57
- describe('instance data', () => {
58
- it('should have instance data field', () => {
59
- const event = new UserCreatedEvent({
60
- userId: 'usr_001',
61
- email: 'test@example.com',
62
- });
63
-
64
- expect(event.data).toEqual({
65
- userId: 'usr_001',
66
- email: 'test@example.com',
67
- });
68
- });
69
- });
70
- });
71
-
72
- describe('Envelope with eventDescription', () => {
73
- it('should include eventDescription in docket when provided', () => {
74
- const event = new UserCreatedEvent({
75
- userId: 'usr_123',
76
- email: 'test@example.com',
77
- });
78
-
79
- const envelope = createEnvelope({
80
- eventKey: UserCreatedEvent.key,
81
- eventDescription: UserCreatedEvent.description,
82
- targetSubscriber: 'test-subscriber',
83
- data: event.data,
84
- importance: 'should-investigate',
85
- });
86
-
87
- expect(envelope.docket.eventKey).toBe('user.created');
88
- expect(envelope.docket.eventDescription).toBe(
89
- 'Fired when a new user is created',
90
- );
91
- });
92
-
93
- it('should not include eventDescription in docket when undefined', () => {
94
- const event = new MinimalEvent({ id: 'min_123' });
95
-
96
- const envelope = createEnvelope({
97
- eventKey: MinimalEvent.key,
98
- eventDescription: MinimalEvent.description,
99
- targetSubscriber: 'test-subscriber',
100
- data: event.data,
101
- importance: 'should-investigate',
102
- });
103
-
104
- expect(envelope.docket.eventKey).toBe('minimal.event');
105
- expect(envelope.docket.eventDescription).toBeUndefined();
106
- });
107
-
108
- it('should serialize envelope with eventDescription for logging', () => {
109
- const event = new OrderPlacedEvent({
110
- orderId: 'ord_456',
111
- amount: 99.99,
112
- });
113
-
114
- const envelope = createEnvelope({
115
- eventKey: OrderPlacedEvent.key,
116
- eventDescription: OrderPlacedEvent.description,
117
- targetSubscriber: 'order-processor',
118
- data: event.data,
119
- importance: 'must-investigate',
120
- });
121
-
122
- const serialized = JSON.stringify(envelope);
123
- const parsed = JSON.parse(serialized);
124
-
125
- expect(parsed.docket.eventKey).toBe('order.placed');
126
- expect(parsed.docket.eventDescription).toBe(
127
- 'Fired when an order is placed',
128
- );
129
- expect(parsed.data.orderId).toBe('ord_456');
130
- });
131
-
132
- it('should include eventDescription in hook logging context', () => {
133
- const event = new UserCreatedEvent({
134
- userId: 'usr_error',
135
- email: 'error@example.com',
136
- });
137
-
138
- const envelope = createEnvelope({
139
- eventKey: UserCreatedEvent.key,
140
- eventDescription: UserCreatedEvent.description,
141
- targetSubscriber: 'user-handler',
142
- data: event.data,
143
- importance: 'should-investigate',
144
- });
145
-
146
- // Simulating what would be logged in onEnqueueError hook
147
- const errorLog = {
148
- message: 'Failed to enqueue event',
149
- eventKey: envelope.docket.eventKey,
150
- eventDescription: envelope.docket.eventDescription,
151
- data: envelope.data,
152
- };
153
-
154
- expect(errorLog.eventKey).toBe('user.created');
155
- expect(errorLog.eventDescription).toBe('Fired when a new user is created');
156
- });
157
- });