@veloxts/events 0.8.0 → 0.8.2
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/dist/domain/emitter.d.ts +87 -0
- package/dist/domain/emitter.js +133 -0
- package/dist/domain/event.d.ts +65 -0
- package/dist/domain/event.js +48 -0
- package/dist/domain/index.d.ts +8 -0
- package/dist/domain/index.js +8 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/manager.js +14 -0
- package/dist/types.d.ts +43 -0
- package/package.json +8 -8
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DomainEventEmitter
|
|
3
|
+
*
|
|
4
|
+
* Typed in-process event emitter for domain events. Used internally by the
|
|
5
|
+
* procedure builder's `.emits()` method and by `ctx.events.emit()` for
|
|
6
|
+
* imperative usage.
|
|
7
|
+
*
|
|
8
|
+
* Listeners receive `event.data` (the typed payload), not the event instance.
|
|
9
|
+
* Sequential listeners run first (in registration order), then concurrent
|
|
10
|
+
* listeners run via Promise.allSettled. Listener errors are caught and logged
|
|
11
|
+
* — they must never propagate to the caller, since the mutation already succeeded.
|
|
12
|
+
*/
|
|
13
|
+
import type { DomainEvent, DomainEventClass } from './event.js';
|
|
14
|
+
export interface ListenerOptions {
|
|
15
|
+
/**
|
|
16
|
+
* When true, this listener executes in registration order, awaiting
|
|
17
|
+
* completion before the next sequential listener starts.
|
|
18
|
+
*
|
|
19
|
+
* Sequential listeners all complete before concurrent listeners begin.
|
|
20
|
+
*
|
|
21
|
+
* @default false
|
|
22
|
+
*/
|
|
23
|
+
sequential?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Placeholder — stored but not yet implemented.
|
|
26
|
+
*
|
|
27
|
+
* TODO (v1.1): Implement automatic retry logic for retryable listeners
|
|
28
|
+
* using an exponential back-off strategy with configurable attempts.
|
|
29
|
+
*
|
|
30
|
+
* @default false
|
|
31
|
+
*/
|
|
32
|
+
retryable?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Typed in-process domain event emitter.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const emitter = new DomainEventEmitter();
|
|
40
|
+
*
|
|
41
|
+
* emitter.on(OrderCreatedEvent, async (data) => {
|
|
42
|
+
* await sendConfirmationEmail(data.orderId);
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* // In a mutation:
|
|
46
|
+
* await emitter.emit(new OrderCreatedEvent({ orderId: 'order-123', total: 99.99 }));
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare class DomainEventEmitter {
|
|
50
|
+
/**
|
|
51
|
+
* Registry keyed by `name`. Each value is an array of listener entries
|
|
52
|
+
* typed as `ListenerEntry<Record<string, unknown>>` so they can be stored
|
|
53
|
+
* together in one map — we narrow the type at registration/retrieval time.
|
|
54
|
+
*/
|
|
55
|
+
private readonly _listeners;
|
|
56
|
+
/**
|
|
57
|
+
* Register a typed listener for a domain event class.
|
|
58
|
+
*
|
|
59
|
+
* The handler receives `event.data` — the typed payload — not the event
|
|
60
|
+
* instance itself.
|
|
61
|
+
*
|
|
62
|
+
* @param eventClass - The domain event class (used as the key via `.name`).
|
|
63
|
+
* @param handler - Called with the event's `data` payload on each emit.
|
|
64
|
+
* @param options - `{ sequential?, retryable? }`
|
|
65
|
+
*/
|
|
66
|
+
on<TData extends Record<string, unknown>>(eventClass: DomainEventClass<TData>, handler: (data: TData) => void | Promise<void>, options?: ListenerOptions): void;
|
|
67
|
+
/**
|
|
68
|
+
* Remove a previously registered listener.
|
|
69
|
+
*
|
|
70
|
+
* @param eventClass - The domain event class the listener was registered for.
|
|
71
|
+
* @param handler - The exact handler reference passed to `on()`.
|
|
72
|
+
*/
|
|
73
|
+
off<TData extends Record<string, unknown>>(eventClass: DomainEventClass<TData>, handler: (data: TData) => void | Promise<void>): void;
|
|
74
|
+
/**
|
|
75
|
+
* Fire a domain event to all registered listeners.
|
|
76
|
+
*
|
|
77
|
+
* Execution order:
|
|
78
|
+
* 1. Sequential listeners run first, in registration order (each awaited).
|
|
79
|
+
* 2. Concurrent listeners run together via Promise.allSettled.
|
|
80
|
+
*
|
|
81
|
+
* Errors from any listener are caught and logged — they never propagate
|
|
82
|
+
* to the caller. The mutation that triggered the event already succeeded.
|
|
83
|
+
*
|
|
84
|
+
* @param event - The domain event instance to dispatch.
|
|
85
|
+
*/
|
|
86
|
+
emit<TData extends Record<string, unknown>>(event: DomainEvent<TData>): Promise<void>;
|
|
87
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DomainEventEmitter
|
|
3
|
+
*
|
|
4
|
+
* Typed in-process event emitter for domain events. Used internally by the
|
|
5
|
+
* procedure builder's `.emits()` method and by `ctx.events.emit()` for
|
|
6
|
+
* imperative usage.
|
|
7
|
+
*
|
|
8
|
+
* Listeners receive `event.data` (the typed payload), not the event instance.
|
|
9
|
+
* Sequential listeners run first (in registration order), then concurrent
|
|
10
|
+
* listeners run via Promise.allSettled. Listener errors are caught and logged
|
|
11
|
+
* — they must never propagate to the caller, since the mutation already succeeded.
|
|
12
|
+
*/
|
|
13
|
+
import { createLogger } from '@veloxts/core';
|
|
14
|
+
const log = createLogger('events');
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// DomainEventEmitter
|
|
17
|
+
// =============================================================================
|
|
18
|
+
/**
|
|
19
|
+
* Typed in-process domain event emitter.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const emitter = new DomainEventEmitter();
|
|
24
|
+
*
|
|
25
|
+
* emitter.on(OrderCreatedEvent, async (data) => {
|
|
26
|
+
* await sendConfirmationEmail(data.orderId);
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* // In a mutation:
|
|
30
|
+
* await emitter.emit(new OrderCreatedEvent({ orderId: 'order-123', total: 99.99 }));
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class DomainEventEmitter {
|
|
34
|
+
/**
|
|
35
|
+
* Registry keyed by `name`. Each value is an array of listener entries
|
|
36
|
+
* typed as `ListenerEntry<Record<string, unknown>>` so they can be stored
|
|
37
|
+
* together in one map — we narrow the type at registration/retrieval time.
|
|
38
|
+
*/
|
|
39
|
+
_listeners = new Map();
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// on()
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
/**
|
|
44
|
+
* Register a typed listener for a domain event class.
|
|
45
|
+
*
|
|
46
|
+
* The handler receives `event.data` — the typed payload — not the event
|
|
47
|
+
* instance itself.
|
|
48
|
+
*
|
|
49
|
+
* @param eventClass - The domain event class (used as the key via `.name`).
|
|
50
|
+
* @param handler - Called with the event's `data` payload on each emit.
|
|
51
|
+
* @param options - `{ sequential?, retryable? }`
|
|
52
|
+
*/
|
|
53
|
+
on(eventClass, handler, options = {}) {
|
|
54
|
+
const { sequential = false, retryable = false } = options;
|
|
55
|
+
const key = eventClass.name;
|
|
56
|
+
if (!this._listeners.has(key)) {
|
|
57
|
+
this._listeners.set(key, []);
|
|
58
|
+
}
|
|
59
|
+
// Cast: we store all handlers as (data: Record<string, unknown>) => … but
|
|
60
|
+
// they are only invoked with the correctly-typed payload for their key.
|
|
61
|
+
const entry = {
|
|
62
|
+
handler: handler,
|
|
63
|
+
sequential,
|
|
64
|
+
retryable,
|
|
65
|
+
};
|
|
66
|
+
this._listeners.get(key)?.push(entry);
|
|
67
|
+
}
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// off()
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
/**
|
|
72
|
+
* Remove a previously registered listener.
|
|
73
|
+
*
|
|
74
|
+
* @param eventClass - The domain event class the listener was registered for.
|
|
75
|
+
* @param handler - The exact handler reference passed to `on()`.
|
|
76
|
+
*/
|
|
77
|
+
off(eventClass, handler) {
|
|
78
|
+
const key = eventClass.name;
|
|
79
|
+
const entries = this._listeners.get(key);
|
|
80
|
+
if (!entries)
|
|
81
|
+
return;
|
|
82
|
+
const filtered = entries.filter((entry) => entry.handler !== handler);
|
|
83
|
+
if (filtered.length === 0) {
|
|
84
|
+
this._listeners.delete(key);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
this._listeners.set(key, filtered);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// emit()
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
/**
|
|
94
|
+
* Fire a domain event to all registered listeners.
|
|
95
|
+
*
|
|
96
|
+
* Execution order:
|
|
97
|
+
* 1. Sequential listeners run first, in registration order (each awaited).
|
|
98
|
+
* 2. Concurrent listeners run together via Promise.allSettled.
|
|
99
|
+
*
|
|
100
|
+
* Errors from any listener are caught and logged — they never propagate
|
|
101
|
+
* to the caller. The mutation that triggered the event already succeeded.
|
|
102
|
+
*
|
|
103
|
+
* @param event - The domain event instance to dispatch.
|
|
104
|
+
*/
|
|
105
|
+
async emit(event) {
|
|
106
|
+
const key = event.constructor.name;
|
|
107
|
+
const entries = this._listeners.get(key);
|
|
108
|
+
if (!entries || entries.length === 0)
|
|
109
|
+
return;
|
|
110
|
+
const sequential = entries.filter((e) => e.sequential);
|
|
111
|
+
const concurrent = entries.filter((e) => !e.sequential);
|
|
112
|
+
// --- Sequential listeners (in registration order) ---
|
|
113
|
+
for (const entry of sequential) {
|
|
114
|
+
try {
|
|
115
|
+
await entry.handler(event.data);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
// Listener errors must never propagate — the mutation already succeeded.
|
|
119
|
+
// Log the error and continue to the next listener.
|
|
120
|
+
log.error(`Sequential listener for "${key}" threw an error:`, err);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// --- Concurrent listeners (all at once) ---
|
|
124
|
+
if (concurrent.length > 0) {
|
|
125
|
+
const results = await Promise.allSettled(concurrent.map((entry) => entry.handler(event.data)));
|
|
126
|
+
for (const result of results) {
|
|
127
|
+
if (result.status === 'rejected') {
|
|
128
|
+
log.error(`Concurrent listener for "${key}" threw an error:`, result.reason);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DomainEvent Base Class
|
|
3
|
+
*
|
|
4
|
+
* Abstract base class for internal server-side domain events used in
|
|
5
|
+
* business logic (e.g., OrderCreated, UserRegistered). These are distinct
|
|
6
|
+
* from broadcast events (WebSocket/SSE) which are client-facing.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Abstract base class for all domain events.
|
|
10
|
+
*
|
|
11
|
+
* Domain events represent meaningful occurrences within the business domain
|
|
12
|
+
* (e.g., "OrderCreated", "OrderFulfilled"). They carry typed payloads and
|
|
13
|
+
* support optional correlation IDs for distributed tracing.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* interface OrderCreatedData {
|
|
18
|
+
* orderId: string;
|
|
19
|
+
* customerId: string;
|
|
20
|
+
* total: number;
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* class OrderCreatedEvent extends DomainEvent<OrderCreatedData> {}
|
|
24
|
+
*
|
|
25
|
+
* const event = new OrderCreatedEvent(
|
|
26
|
+
* { orderId: 'order-123', customerId: 'cust-456', total: 99.99 },
|
|
27
|
+
* { correlationId: 'req-abc' }
|
|
28
|
+
* );
|
|
29
|
+
*
|
|
30
|
+
* console.log(OrderCreatedEvent.name); // 'OrderCreatedEvent'
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare abstract class DomainEvent<TData extends Record<string, unknown> = Record<string, unknown>> {
|
|
34
|
+
/** The typed payload carried by this event. */
|
|
35
|
+
readonly data: TData;
|
|
36
|
+
/** The date/time at which this event was instantiated. */
|
|
37
|
+
readonly timestamp: Date;
|
|
38
|
+
/** Optional correlation ID for distributed tracing across services. */
|
|
39
|
+
readonly correlationId?: string;
|
|
40
|
+
constructor(data: TData, options?: {
|
|
41
|
+
correlationId?: string;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Type representing the constructor signature of a concrete DomainEvent subclass.
|
|
46
|
+
*
|
|
47
|
+
* Use this when you need to hold a reference to an event class itself
|
|
48
|
+
* (e.g., to register listeners or dispatch by class reference).
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* function on<TData extends Record<string, unknown>>(
|
|
53
|
+
* EventClass: DomainEventClass<TData>,
|
|
54
|
+
* handler: (event: DomainEvent<TData>) => void,
|
|
55
|
+
* ): void {
|
|
56
|
+
* // register handler keyed by EventClass.name
|
|
57
|
+
* }
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export type DomainEventClass<TData extends Record<string, unknown> = Record<string, unknown>> = {
|
|
61
|
+
new (data: TData, options?: {
|
|
62
|
+
correlationId?: string;
|
|
63
|
+
}): DomainEvent<TData>;
|
|
64
|
+
readonly name: string;
|
|
65
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DomainEvent Base Class
|
|
3
|
+
*
|
|
4
|
+
* Abstract base class for internal server-side domain events used in
|
|
5
|
+
* business logic (e.g., OrderCreated, UserRegistered). These are distinct
|
|
6
|
+
* from broadcast events (WebSocket/SSE) which are client-facing.
|
|
7
|
+
*/
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// DomainEvent
|
|
10
|
+
// =============================================================================
|
|
11
|
+
/**
|
|
12
|
+
* Abstract base class for all domain events.
|
|
13
|
+
*
|
|
14
|
+
* Domain events represent meaningful occurrences within the business domain
|
|
15
|
+
* (e.g., "OrderCreated", "OrderFulfilled"). They carry typed payloads and
|
|
16
|
+
* support optional correlation IDs for distributed tracing.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* interface OrderCreatedData {
|
|
21
|
+
* orderId: string;
|
|
22
|
+
* customerId: string;
|
|
23
|
+
* total: number;
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* class OrderCreatedEvent extends DomainEvent<OrderCreatedData> {}
|
|
27
|
+
*
|
|
28
|
+
* const event = new OrderCreatedEvent(
|
|
29
|
+
* { orderId: 'order-123', customerId: 'cust-456', total: 99.99 },
|
|
30
|
+
* { correlationId: 'req-abc' }
|
|
31
|
+
* );
|
|
32
|
+
*
|
|
33
|
+
* console.log(OrderCreatedEvent.name); // 'OrderCreatedEvent'
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export class DomainEvent {
|
|
37
|
+
/** The typed payload carried by this event. */
|
|
38
|
+
data;
|
|
39
|
+
/** The date/time at which this event was instantiated. */
|
|
40
|
+
timestamp;
|
|
41
|
+
/** Optional correlation ID for distributed tracing across services. */
|
|
42
|
+
correlationId;
|
|
43
|
+
constructor(data, options) {
|
|
44
|
+
this.data = data;
|
|
45
|
+
this.timestamp = new Date();
|
|
46
|
+
this.correlationId = options?.correlationId;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain Events — barrel export
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the DomainEvent base class, its class type, and the
|
|
5
|
+
* DomainEventEmitter with its listener options.
|
|
6
|
+
*/
|
|
7
|
+
export { DomainEventEmitter, type ListenerOptions as DomainListenerOptions } from './emitter.js';
|
|
8
|
+
export { DomainEvent, type DomainEventClass } from './event.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
* @packageDocumentation
|
|
38
38
|
*/
|
|
39
39
|
export { type ChannelAuthSigner, createChannelAuthSigner } from './auth.js';
|
|
40
|
+
export { DomainEvent, type DomainEventClass, DomainEventEmitter, type DomainListenerOptions, } from './domain/index.js';
|
|
40
41
|
export { createSseDriver, DRIVER_NAME as SSE_DRIVER } from './drivers/sse.js';
|
|
41
42
|
export { createWsDriver, DRIVER_NAME as WS_DRIVER } from './drivers/ws.js';
|
|
42
43
|
export { createEventsManager, createManagerFromDriver, type EventsManager, events, } from './manager.js';
|
package/dist/index.js
CHANGED
|
@@ -38,6 +38,8 @@
|
|
|
38
38
|
*/
|
|
39
39
|
// Auth
|
|
40
40
|
export { createChannelAuthSigner } from './auth.js';
|
|
41
|
+
// Domain Events
|
|
42
|
+
export { DomainEvent, DomainEventEmitter, } from './domain/index.js';
|
|
41
43
|
// Drivers
|
|
42
44
|
export { createSseDriver, DRIVER_NAME as SSE_DRIVER } from './drivers/sse.js';
|
|
43
45
|
export { createWsDriver, DRIVER_NAME as WS_DRIVER } from './drivers/ws.js';
|
package/dist/manager.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Wraps the underlying driver with a clean, Laravel-inspired interface.
|
|
6
6
|
*/
|
|
7
7
|
import { createLogger } from '@veloxts/core';
|
|
8
|
+
import { DomainEventEmitter } from './domain/emitter.js';
|
|
8
9
|
const log = createLogger('events');
|
|
9
10
|
/**
|
|
10
11
|
* Create an events manager with the specified driver.
|
|
@@ -55,6 +56,7 @@ export async function createEventsManager(options = {}) {
|
|
|
55
56
|
* @returns Events manager instance
|
|
56
57
|
*/
|
|
57
58
|
export function createManagerFromDriver(driver) {
|
|
59
|
+
const domainEmitter = new DomainEventEmitter();
|
|
58
60
|
const manager = {
|
|
59
61
|
driver,
|
|
60
62
|
async broadcast(channel, event, data, except) {
|
|
@@ -104,6 +106,18 @@ export function createManagerFromDriver(driver) {
|
|
|
104
106
|
async channels() {
|
|
105
107
|
return driver.getChannels();
|
|
106
108
|
},
|
|
109
|
+
// -------------------------------------------------------------------------
|
|
110
|
+
// Domain Event Methods
|
|
111
|
+
// -------------------------------------------------------------------------
|
|
112
|
+
async emit(event) {
|
|
113
|
+
await domainEmitter.emit(event);
|
|
114
|
+
},
|
|
115
|
+
on(eventClass, handler, options) {
|
|
116
|
+
domainEmitter.on(eventClass, handler, options);
|
|
117
|
+
},
|
|
118
|
+
off(eventClass, handler) {
|
|
119
|
+
domainEmitter.off(eventClass, handler);
|
|
120
|
+
},
|
|
107
121
|
async close() {
|
|
108
122
|
await driver.close();
|
|
109
123
|
},
|
package/dist/types.d.ts
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* Uses discriminated unions for type-safe driver configuration.
|
|
6
6
|
*/
|
|
7
7
|
import type { FastifyRequest } from 'fastify';
|
|
8
|
+
import type { ListenerOptions as DomainListenerOptions } from './domain/emitter.js';
|
|
9
|
+
import type { DomainEvent, DomainEventClass } from './domain/event.js';
|
|
8
10
|
/**
|
|
9
11
|
* Channel type determines access control.
|
|
10
12
|
* - public: Anyone can subscribe
|
|
@@ -285,4 +287,45 @@ export interface EventsManager {
|
|
|
285
287
|
* Close the manager and clean up resources.
|
|
286
288
|
*/
|
|
287
289
|
close(): Promise<void>;
|
|
290
|
+
/**
|
|
291
|
+
* Emit a domain event to all registered listeners.
|
|
292
|
+
*
|
|
293
|
+
* Domain events are in-process events for business logic (e.g., OrderCreated,
|
|
294
|
+
* UserRegistered). They are distinct from broadcast events (WebSocket/SSE)
|
|
295
|
+
* which are client-facing.
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* ```typescript
|
|
299
|
+
* await ctx.events.emit(new OrderCreatedEvent({
|
|
300
|
+
* orderId: '123',
|
|
301
|
+
* total: 99.99,
|
|
302
|
+
* }));
|
|
303
|
+
* ```
|
|
304
|
+
*/
|
|
305
|
+
emit<TData extends Record<string, unknown>>(event: DomainEvent<TData>): Promise<void>;
|
|
306
|
+
/**
|
|
307
|
+
* Register a typed listener for a domain event class.
|
|
308
|
+
*
|
|
309
|
+
* The handler receives `event.data` — the typed payload — not the event
|
|
310
|
+
* instance itself.
|
|
311
|
+
*
|
|
312
|
+
* @param eventClass - The domain event class to listen for.
|
|
313
|
+
* @param handler - Called with the event's `data` payload on each emit.
|
|
314
|
+
* @param options - `{ sequential?, retryable? }`
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* ```typescript
|
|
318
|
+
* events.on(OrderCreatedEvent, async (data) => {
|
|
319
|
+
* await sendConfirmationEmail(data.orderId);
|
|
320
|
+
* });
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
on<TData extends Record<string, unknown>>(eventClass: DomainEventClass<TData>, handler: (data: TData) => void | Promise<void>, options?: DomainListenerOptions): void;
|
|
324
|
+
/**
|
|
325
|
+
* Remove a previously registered domain event listener.
|
|
326
|
+
*
|
|
327
|
+
* @param eventClass - The domain event class the listener was registered for.
|
|
328
|
+
* @param handler - The exact handler reference passed to `on()`.
|
|
329
|
+
*/
|
|
330
|
+
off<TData extends Record<string, unknown>>(eventClass: DomainEventClass<TData>, handler: (data: TData) => void | Promise<void>): void;
|
|
288
331
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veloxts/events",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Real-time event broadcasting for VeloxTS framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"superjson": "2.2.6",
|
|
31
31
|
"ws": "8.19.0",
|
|
32
32
|
"zod": "4.3.6",
|
|
33
|
-
"@veloxts/core": "0.8.
|
|
33
|
+
"@veloxts/core": "0.8.2"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"fastify": "^5.7.4",
|
|
@@ -42,14 +42,14 @@
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@types/node": "25.
|
|
45
|
+
"@types/node": "25.5.0",
|
|
46
46
|
"@types/ws": "8.18.1",
|
|
47
|
-
"@vitest/coverage-v8": "4.0
|
|
48
|
-
"fastify": "5.
|
|
49
|
-
"ioredis": "5.
|
|
47
|
+
"@vitest/coverage-v8": "4.1.0",
|
|
48
|
+
"fastify": "5.8.2",
|
|
49
|
+
"ioredis": "5.10.0",
|
|
50
50
|
"typescript": "5.9.3",
|
|
51
|
-
"vitest": "4.0
|
|
52
|
-
"@veloxts/testing": "0.8.
|
|
51
|
+
"vitest": "4.1.0",
|
|
52
|
+
"@veloxts/testing": "0.8.2"
|
|
53
53
|
},
|
|
54
54
|
"publishConfig": {
|
|
55
55
|
"access": "public"
|