@zudojs/events 0.0.1 → 1.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.
- package/README.md +70 -11
- package/dist/eventBus/eventBus.core.d.ts +28 -2
- package/dist/eventBus/eventBus.core.js +104 -31
- package/dist/eventBus/eventBus.publish.d.ts +23 -5
- package/dist/eventBus/eventBus.publish.js +72 -32
- package/dist/eventBus/eventBus.registration.d.ts +24 -13
- package/dist/eventBus/eventBus.registration.js +31 -28
- package/dist/eventBus/eventBus.type.d.ts +75 -3
- package/dist/eventBus/eventBus.type.js +9 -0
- package/dist/eventBus/index.d.ts +2 -0
- package/dist/eventBus/index.js +2 -0
- package/dist/eventEmitter/eventEmitter.abort.d.ts +11 -2
- package/dist/eventEmitter/eventEmitter.abort.js +20 -5
- package/dist/eventEmitter/eventEmitter.core.d.ts +14 -3
- package/dist/eventEmitter/eventEmitter.core.js +63 -55
- package/dist/eventEmitter/eventEmitter.parallel.d.ts +7 -1
- package/dist/eventEmitter/eventEmitter.parallel.js +31 -13
- package/dist/eventEmitter/eventEmitter.sequential.d.ts +17 -1
- package/dist/eventEmitter/eventEmitter.sequential.js +19 -12
- package/dist/eventEmitter/eventEmitter.type.d.ts +60 -0
- package/dist/eventErrors/eventError.base.d.ts +50 -3
- package/dist/eventErrors/eventError.base.js +64 -3
- package/dist/eventHandler/eventHandler.core.d.ts +19 -0
- package/dist/eventHandler/eventHandler.core.js +67 -4
- package/dist/eventMiddleware/eventMiddleware.builder.js +5 -11
- package/dist/eventMiddleware/eventMiddleware.helper.js +0 -1
- package/dist/eventMiddleware/eventMiddleware.pipeline.d.ts +5 -0
- package/dist/eventMiddleware/eventMiddleware.pipeline.js +33 -9
- package/dist/eventRegistry/eventRegistry.lifecycle.d.ts +10 -5
- package/dist/eventRegistry/eventRegistry.lifecycle.js +23 -10
- package/dist/eventRegistry/eventRegistry.queries.d.ts +16 -7
- package/dist/eventRegistry/eventRegistry.queries.js +22 -13
- package/dist/eventRegistry/eventRegistry.registration.d.ts +14 -7
- package/dist/eventRegistry/eventRegistry.registration.js +120 -41
- package/dist/eventRegistry/eventRegistry.store.d.ts +12 -5
- package/dist/eventRegistry/eventRegistry.store.js +37 -12
- package/dist/eventRegistry/eventRegistry.type.d.ts +87 -2
- package/dist/eventRegistry/eventRegistry.type.js +5 -0
- package/dist/eventSubscription/eventSubscription.core.d.ts +5 -0
- package/dist/eventSubscription/eventSubscription.core.js +17 -3
- package/dist/eventTypes/eventDefinition.type.d.ts +11 -4
- package/dist/eventTypes/eventDefinition.type.js +49 -17
- package/dist/eventTypes/eventPayload.type.d.ts +5 -0
- package/dist/eventTypes/eventPayload.type.js +56 -15
- package/dist/eventTypes/eventType.type.d.ts +22 -5
- package/dist/eventTypes/eventType.type.js +33 -11
- package/dist/eventTypes/index.d.ts +2 -2
- package/dist/eventTypes/index.js +2 -2
- package/package.json +15 -7
- package/dist/.tsbuildinfo +0 -1
- package/dist/eventBus/eventBus.core.d.ts.map +0 -1
- package/dist/eventBus/eventBus.core.js.map +0 -1
- package/dist/eventBus/eventBus.factory.d.ts.map +0 -1
- package/dist/eventBus/eventBus.factory.js.map +0 -1
- package/dist/eventBus/eventBus.publish.d.ts.map +0 -1
- package/dist/eventBus/eventBus.publish.js.map +0 -1
- package/dist/eventBus/eventBus.registration.d.ts.map +0 -1
- package/dist/eventBus/eventBus.registration.js.map +0 -1
- package/dist/eventBus/eventBus.type.d.ts.map +0 -1
- package/dist/eventBus/eventBus.type.js.map +0 -1
- package/dist/eventBus/index.d.ts.map +0 -1
- package/dist/eventBus/index.js.map +0 -1
- package/dist/eventEmitter/eventEmitter.abort.d.ts.map +0 -1
- package/dist/eventEmitter/eventEmitter.abort.js.map +0 -1
- package/dist/eventEmitter/eventEmitter.core.d.ts.map +0 -1
- package/dist/eventEmitter/eventEmitter.core.js.map +0 -1
- package/dist/eventEmitter/eventEmitter.parallel.d.ts.map +0 -1
- package/dist/eventEmitter/eventEmitter.parallel.js.map +0 -1
- package/dist/eventEmitter/eventEmitter.sequential.d.ts.map +0 -1
- package/dist/eventEmitter/eventEmitter.sequential.js.map +0 -1
- package/dist/eventEmitter/eventEmitter.type.d.ts.map +0 -1
- package/dist/eventEmitter/eventEmitter.type.js.map +0 -1
- package/dist/eventEmitter/index.d.ts.map +0 -1
- package/dist/eventEmitter/index.js.map +0 -1
- package/dist/eventErrors/eventError.base.d.ts.map +0 -1
- package/dist/eventErrors/eventError.base.js.map +0 -1
- package/dist/eventErrors/index.d.ts.map +0 -1
- package/dist/eventErrors/index.js.map +0 -1
- package/dist/eventHandler/eventHandler.core.d.ts.map +0 -1
- package/dist/eventHandler/eventHandler.core.js.map +0 -1
- package/dist/eventHandler/index.d.ts.map +0 -1
- package/dist/eventHandler/index.js.map +0 -1
- package/dist/eventMiddleware/eventMiddleware.builder.d.ts.map +0 -1
- package/dist/eventMiddleware/eventMiddleware.builder.js.map +0 -1
- package/dist/eventMiddleware/eventMiddleware.helper.d.ts.map +0 -1
- package/dist/eventMiddleware/eventMiddleware.helper.js.map +0 -1
- package/dist/eventMiddleware/eventMiddleware.pipeline.d.ts.map +0 -1
- package/dist/eventMiddleware/eventMiddleware.pipeline.js.map +0 -1
- package/dist/eventMiddleware/eventMiddleware.type.d.ts.map +0 -1
- package/dist/eventMiddleware/eventMiddleware.type.js.map +0 -1
- package/dist/eventMiddleware/index.d.ts.map +0 -1
- package/dist/eventMiddleware/index.js.map +0 -1
- package/dist/eventRegistry/eventRegistry.lifecycle.d.ts.map +0 -1
- package/dist/eventRegistry/eventRegistry.lifecycle.js.map +0 -1
- package/dist/eventRegistry/eventRegistry.queries.d.ts.map +0 -1
- package/dist/eventRegistry/eventRegistry.queries.js.map +0 -1
- package/dist/eventRegistry/eventRegistry.registration.d.ts.map +0 -1
- package/dist/eventRegistry/eventRegistry.registration.js.map +0 -1
- package/dist/eventRegistry/eventRegistry.store.d.ts.map +0 -1
- package/dist/eventRegistry/eventRegistry.store.js.map +0 -1
- package/dist/eventRegistry/eventRegistry.type.d.ts.map +0 -1
- package/dist/eventRegistry/eventRegistry.type.js.map +0 -1
- package/dist/eventRegistry/index.d.ts.map +0 -1
- package/dist/eventRegistry/index.js.map +0 -1
- package/dist/eventSubscription/eventSubscription.core.d.ts.map +0 -1
- package/dist/eventSubscription/eventSubscription.core.js.map +0 -1
- package/dist/eventSubscription/index.d.ts.map +0 -1
- package/dist/eventSubscription/index.js.map +0 -1
- package/dist/eventTypes/eventDefinition.type.d.ts.map +0 -1
- package/dist/eventTypes/eventDefinition.type.js.map +0 -1
- package/dist/eventTypes/eventPayload.type.d.ts.map +0 -1
- package/dist/eventTypes/eventPayload.type.js.map +0 -1
- package/dist/eventTypes/eventType.type.d.ts.map +0 -1
- package/dist/eventTypes/eventType.type.js.map +0 -1
- package/dist/eventTypes/index.d.ts.map +0 -1
- package/dist/eventTypes/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -11,32 +11,91 @@ npm install @zudojs/events
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import { createEventBus } from "@zudojs/events";
|
|
14
|
+
import { createEventBus, defineEvent } from "@zudojs/events";
|
|
15
|
+
|
|
16
|
+
interface UserCreated {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly name: string;
|
|
19
|
+
}
|
|
15
20
|
|
|
16
21
|
const bus = createEventBus();
|
|
17
22
|
|
|
23
|
+
const UserCreatedEvent = defineEvent<"user.created", UserCreated>("user.created");
|
|
24
|
+
|
|
18
25
|
bus.on("user.created", (event) => {
|
|
19
|
-
console.log("New user:", event.payload.id);
|
|
26
|
+
console.log("New user:", (event.payload as UserCreated).id);
|
|
20
27
|
});
|
|
21
28
|
|
|
22
|
-
|
|
29
|
+
// Publish from an event definition …
|
|
30
|
+
await bus.publish(UserCreatedEvent.create({ id: "123", name: "Alice" }));
|
|
31
|
+
|
|
32
|
+
// … or from plain event input.
|
|
33
|
+
await bus.publishEvent({
|
|
23
34
|
type: "user.created",
|
|
24
|
-
payload: { id: "
|
|
35
|
+
payload: { id: "124", name: "Bob" },
|
|
25
36
|
});
|
|
26
37
|
```
|
|
27
38
|
|
|
39
|
+
`bus.emit()` accepts either a full `Event` or an `EventInput` and behaves like
|
|
40
|
+
`publish` / `publishEvent` respectively.
|
|
41
|
+
|
|
28
42
|
## Features
|
|
29
43
|
|
|
30
|
-
- Event bus with middleware pipeline
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
- Wildcard
|
|
34
|
-
-
|
|
35
|
-
-
|
|
44
|
+
- Event bus with a middleware pipeline (`use()`, constructor and per-publish middleware)
|
|
45
|
+
- Sequential or parallel handler dispatch with `THROW` / `CONTINUE` error modes
|
|
46
|
+
- Handler priorities, one-time handlers, per-handler timeouts
|
|
47
|
+
- Wildcard subscriptions (`"user.*"`, `"*"`)
|
|
48
|
+
- Event registry for typed definitions; handlers registered on the registry are dispatched by the bus
|
|
49
|
+
- Deep-frozen events (`freezeEvents`, on by default) so handlers cannot alter what other handlers see
|
|
50
|
+
- Listener-leak warnings (`maxListeners`) and an `onError` hook for fire-and-forget publishes
|
|
51
|
+
- Typed error classes from `@zudojs/errors` (`EventHandlerError`, `EventMiddlewareError`, `EventDispatchAbortedError`, …)
|
|
52
|
+
|
|
53
|
+
## Lifecycle
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
CREATED ──(first use / start)──▶ ACTIVE ◀──(start)── STOPPED
|
|
57
|
+
│ ▲
|
|
58
|
+
└──────(stop)─────────┘
|
|
59
|
+
▼
|
|
60
|
+
DISPOSED
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- A `CREATED` bus starts itself on the first `publish` / `on`.
|
|
64
|
+
- `stop()` moves the bus to `STOPPED`; publishing or subscribing then throws
|
|
65
|
+
`EventBusStoppedError` until `start()` is called. Handlers and definitions are kept.
|
|
66
|
+
- `dispose()` is final; every operation throws `EventBusDisposedError`.
|
|
67
|
+
|
|
68
|
+
## Publish results
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
const result = await bus.publish(event);
|
|
72
|
+
|
|
73
|
+
result.handled; // true when at least one handler succeeded
|
|
74
|
+
result.handlerCount; // handlers invoked
|
|
75
|
+
result.succeeded; // handlers that completed
|
|
76
|
+
result.failed; // handlers that threw
|
|
77
|
+
result.errors; // EventHandlerError[] (cause = the raw thrown value)
|
|
78
|
+
result.shortCircuited; // true when a middleware did not call next()
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
In the default `CONTINUE` error mode handler failures are collected in
|
|
82
|
+
`result.errors` (and forwarded to the `onError` option). In `THROW` mode the first
|
|
83
|
+
`EventHandlerError` rejects the publish. Errors thrown by a middleware itself are
|
|
84
|
+
wrapped as `EventMiddlewareError`; handler errors and aborts pass through unwrapped.
|
|
85
|
+
|
|
86
|
+
## Registry
|
|
87
|
+
|
|
88
|
+
`bus.register(defineEvent("order.placed"))` records a definition; with
|
|
89
|
+
`requireRegistration: true` the bus rejects unregistered types with
|
|
90
|
+
`EventTypeNotFoundError`. `bus.unregister(type)` removes only the definition; pass
|
|
91
|
+
`{ removeHandlers: true }` to also drop handlers subscribed to exactly that type.
|
|
92
|
+
|
|
93
|
+
Event types are normalised (trimmed, lower-cased) everywhere, so `"User.Created"`
|
|
94
|
+
and `"user.created"` refer to the same event.
|
|
36
95
|
|
|
37
96
|
## Use Cases
|
|
38
97
|
|
|
39
98
|
- Decoupling application components
|
|
40
99
|
- Audit logging and change tracking
|
|
41
100
|
- Real-time notifications
|
|
42
|
-
- CQRS event
|
|
101
|
+
- CQRS event publishing (see `@zudojs/cqrs`)
|
|
@@ -14,6 +14,11 @@ import { EventBusState } from "./eventBus.type.js";
|
|
|
14
14
|
export { EventBusState } from "./eventBus.type.js";
|
|
15
15
|
/**
|
|
16
16
|
* High-level event bus.
|
|
17
|
+
*
|
|
18
|
+
* Lifecycle: CREATED → ACTIVE ⇄ STOPPED → DISPOSED. A CREATED bus
|
|
19
|
+
* starts itself on first use; a STOPPED bus rejects publishing and
|
|
20
|
+
* subscribing until start() is called; a DISPOSED bus rejects
|
|
21
|
+
* everything.
|
|
17
22
|
*/
|
|
18
23
|
export declare class EventBus {
|
|
19
24
|
private readonly emitter;
|
|
@@ -24,6 +29,11 @@ export declare class EventBus {
|
|
|
24
29
|
private state;
|
|
25
30
|
constructor(options?: EventBusOptions);
|
|
26
31
|
start(): this;
|
|
32
|
+
/**
|
|
33
|
+
* Stops the bus. Publishing and subscribing throw
|
|
34
|
+
* EventBusStoppedError until start() is called again; handlers
|
|
35
|
+
* and definitions are kept.
|
|
36
|
+
*/
|
|
27
37
|
stop(): this;
|
|
28
38
|
register<TType extends EventType, TPayload>(definition: EventDefinition<TType, TPayload>): import("../index.js").RegisteredEventDefinition<TType, TPayload>;
|
|
29
39
|
on<TEvent extends Event = Event>(eventType: EventTypePattern, handler: EventHandlerLike<TEvent>, options?: Omit<EventHandlerOptions, "eventType">): EventSubscription;
|
|
@@ -33,10 +43,22 @@ export declare class EventBus {
|
|
|
33
43
|
use(middleware: EventMiddlewareLike, options?: EventMiddlewareOptions): () => void;
|
|
34
44
|
publish<TEvent extends Event>(event: TEvent, options?: PublishOptions): Promise<EventPublishResult<TEvent>>;
|
|
35
45
|
publishEvent<TPayload>(input: EventInput<TPayload>, options?: PublishOptions): Promise<EventPublishResult<Event<TPayload>>>;
|
|
36
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Publishes an event or event input. A full Event is published
|
|
48
|
+
* as is; an EventInput ({ type, payload, ... }) is turned into
|
|
49
|
+
* an event first.
|
|
50
|
+
*/
|
|
51
|
+
emit<TPayload>(input: Event<TPayload> | EventInput<TPayload>, options?: PublishOptions): Promise<EventPublishResult<Event<TPayload>>>;
|
|
37
52
|
getDefinition<TType extends EventType, TPayload = unknown>(eventType: TType): import("../index.js").RegisteredEventDefinition<TType, TPayload> | undefined;
|
|
38
53
|
hasEvent(eventType: EventType): boolean;
|
|
39
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Removes an event definition. Handlers are not affected unless
|
|
56
|
+
* `removeHandlers` is true, in which case every handler whose
|
|
57
|
+
* pattern is exactly this event type is unregistered too.
|
|
58
|
+
*/
|
|
59
|
+
unregister(eventType: EventType, options?: {
|
|
60
|
+
readonly removeHandlers?: boolean;
|
|
61
|
+
}): boolean;
|
|
40
62
|
getRegistry(): EventRegistry;
|
|
41
63
|
getEmitter(): EventEmitter;
|
|
42
64
|
getDefinitions(): readonly import("../index.js").RegisteredEventDefinition<string, unknown>[];
|
|
@@ -48,6 +70,10 @@ export declare class EventBus {
|
|
|
48
70
|
subscribe(listener: EventBusListener): () => void;
|
|
49
71
|
dispose(): void;
|
|
50
72
|
toError(error: unknown, event?: Event): EventError;
|
|
73
|
+
private publishDependencies;
|
|
74
|
+
/**
|
|
75
|
+
* Auto-starts a CREATED bus; rejects a STOPPED or DISPOSED one.
|
|
76
|
+
*/
|
|
51
77
|
private ensureUsable;
|
|
52
78
|
private ensureNotDisposed;
|
|
53
79
|
private notify;
|
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Event bus core class for Zudojs.
|
|
3
3
|
*/
|
|
4
|
+
import { isEvent } from "../eventTypes/eventDefinition.type.js";
|
|
4
5
|
import { EventEmitter } from "../eventEmitter/eventEmitter.core.js";
|
|
5
6
|
import { EventErrorMode } from "../eventEmitter/eventEmitter.type.js";
|
|
6
7
|
import { EventRegistry } from "../eventRegistry/eventRegistry.store.js";
|
|
7
|
-
import { EventError, toEventError } from "../eventErrors/eventError.base.js";
|
|
8
|
+
import { EventBusDisposedError, EventBusStoppedError, EventError, toEventError, } from "../eventErrors/eventError.base.js";
|
|
8
9
|
import { EventBusState } from "./eventBus.type.js";
|
|
9
10
|
export { EventBusState } from "./eventBus.type.js";
|
|
10
11
|
import { busOn, busOnce, busOnAny, busOff, busUse, registerMiddlewareItem, } from "./eventBus.registration.js";
|
|
11
12
|
import { busPublish, busPublishEvent } from "./eventBus.publish.js";
|
|
12
13
|
/**
|
|
13
14
|
* High-level event bus.
|
|
15
|
+
*
|
|
16
|
+
* Lifecycle: CREATED → ACTIVE ⇄ STOPPED → DISPOSED. A CREATED bus
|
|
17
|
+
* starts itself on first use; a STOPPED bus rejects publishing and
|
|
18
|
+
* subscribing until start() is called; a DISPOSED bus rejects
|
|
19
|
+
* everything.
|
|
14
20
|
*/
|
|
15
21
|
export class EventBus {
|
|
16
22
|
emitter;
|
|
@@ -22,11 +28,27 @@ export class EventBus {
|
|
|
22
28
|
constructor(options = {}) {
|
|
23
29
|
this.options = {
|
|
24
30
|
requireRegistration: options.requireRegistration ?? false,
|
|
31
|
+
onError: options.onError,
|
|
25
32
|
};
|
|
26
|
-
this.registry = new EventRegistry(
|
|
33
|
+
this.registry = new EventRegistry({
|
|
34
|
+
...options.registry,
|
|
35
|
+
maxHandlersPerPattern: options.emitter?.maxListeners,
|
|
36
|
+
onWarning: options.onWarning,
|
|
37
|
+
onError: options.onError
|
|
38
|
+
? (error, context) => options.onError?.(error, {
|
|
39
|
+
source: context.source,
|
|
40
|
+
})
|
|
41
|
+
: undefined,
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* The emitter stores its handlers in the bus registry so
|
|
45
|
+
* handlers registered through either surface are dispatched.
|
|
46
|
+
*/
|
|
27
47
|
this.emitter = new EventEmitter({
|
|
28
|
-
|
|
48
|
+
mode: options.emitter?.mode,
|
|
29
49
|
errorMode: options.emitter?.errorMode ?? EventErrorMode.CONTINUE,
|
|
50
|
+
freezeEvents: options.emitter?.freezeEvents,
|
|
51
|
+
store: this.registry,
|
|
30
52
|
});
|
|
31
53
|
this.busMiddleware = (options.middleware ?? []).map((m, index) => registerMiddlewareItem(m, index));
|
|
32
54
|
}
|
|
@@ -42,12 +64,17 @@ export class EventBus {
|
|
|
42
64
|
});
|
|
43
65
|
return this;
|
|
44
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Stops the bus. Publishing and subscribing throw
|
|
69
|
+
* EventBusStoppedError until start() is called again; handlers
|
|
70
|
+
* and definitions are kept.
|
|
71
|
+
*/
|
|
45
72
|
stop() {
|
|
46
73
|
this.ensureNotDisposed();
|
|
47
74
|
if (this.state !== EventBusState.ACTIVE) {
|
|
48
75
|
return this;
|
|
49
76
|
}
|
|
50
|
-
this.state = EventBusState.
|
|
77
|
+
this.state = EventBusState.STOPPED;
|
|
51
78
|
this.notify({
|
|
52
79
|
type: "stopped",
|
|
53
80
|
timestamp: new Date(),
|
|
@@ -55,17 +82,17 @@ export class EventBus {
|
|
|
55
82
|
return this;
|
|
56
83
|
}
|
|
57
84
|
register(definition) {
|
|
58
|
-
this.ensureUsable();
|
|
85
|
+
this.ensureUsable("register");
|
|
59
86
|
return this.registry.register(definition);
|
|
60
87
|
}
|
|
61
88
|
on(eventType, handler, options = {}) {
|
|
62
|
-
return busOn(this.emitter, eventType, handler, options, () => this.ensureUsable());
|
|
89
|
+
return busOn(this.emitter, eventType, handler, options, () => this.ensureUsable("subscribe"));
|
|
63
90
|
}
|
|
64
91
|
once(eventType, handler, options = {}) {
|
|
65
|
-
return busOnce(this.emitter, eventType, handler, options, () => this.ensureUsable());
|
|
92
|
+
return busOnce(this.emitter, eventType, handler, options, () => this.ensureUsable("subscribe"));
|
|
66
93
|
}
|
|
67
94
|
onAny(handler, options = {}) {
|
|
68
|
-
return busOnAny(this.emitter, handler, options, () => this.ensureUsable());
|
|
95
|
+
return busOnAny(this.emitter, handler, options, () => this.ensureUsable("subscribe"));
|
|
69
96
|
}
|
|
70
97
|
off(subscription) {
|
|
71
98
|
return busOff(this.emitter, subscription, () => this.ensureNotDisposed());
|
|
@@ -75,41 +102,63 @@ export class EventBus {
|
|
|
75
102
|
return busUse(this.busMiddleware, middleware, options);
|
|
76
103
|
}
|
|
77
104
|
async publish(event, options = {}) {
|
|
78
|
-
return busPublish(event, options, this.
|
|
105
|
+
return busPublish(event, options, this.publishDependencies());
|
|
79
106
|
}
|
|
80
107
|
async publishEvent(input, options = {}) {
|
|
81
|
-
return busPublishEvent(input, options, this.
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
108
|
+
return busPublishEvent(input, options, this.publishDependencies());
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Publishes an event or event input. A full Event is published
|
|
112
|
+
* as is; an EventInput ({ type, payload, ... }) is turned into
|
|
113
|
+
* an event first.
|
|
114
|
+
*/
|
|
115
|
+
async emit(input, options = {}) {
|
|
116
|
+
if (isEvent(input)) {
|
|
117
|
+
return this.publish(input, options);
|
|
118
|
+
}
|
|
119
|
+
return this.publishEvent(input, options);
|
|
85
120
|
}
|
|
86
121
|
getDefinition(eventType) {
|
|
87
|
-
this.
|
|
122
|
+
this.ensureNotDisposed();
|
|
88
123
|
return this.registry.get(eventType);
|
|
89
124
|
}
|
|
90
125
|
hasEvent(eventType) {
|
|
91
|
-
this.
|
|
126
|
+
this.ensureNotDisposed();
|
|
92
127
|
return this.registry.has(eventType);
|
|
93
128
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
129
|
+
/**
|
|
130
|
+
* Removes an event definition. Handlers are not affected unless
|
|
131
|
+
* `removeHandlers` is true, in which case every handler whose
|
|
132
|
+
* pattern is exactly this event type is unregistered too.
|
|
133
|
+
*/
|
|
134
|
+
unregister(eventType, options = {}) {
|
|
135
|
+
this.ensureNotDisposed();
|
|
136
|
+
const removed = this.registry.unregister(eventType);
|
|
137
|
+
if (options.removeHandlers) {
|
|
138
|
+
const definition = this.registry.getHandlersForType(eventType);
|
|
139
|
+
for (const handler of definition) {
|
|
140
|
+
if (handler.eventType !== "*" && !handler.eventType.endsWith(".*")) {
|
|
141
|
+
this.registry.unregisterHandler(handler.id);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return removed;
|
|
97
146
|
}
|
|
98
147
|
getRegistry() {
|
|
99
|
-
this.
|
|
148
|
+
this.ensureNotDisposed();
|
|
100
149
|
return this.registry;
|
|
101
150
|
}
|
|
102
151
|
getEmitter() {
|
|
103
|
-
this.
|
|
152
|
+
this.ensureNotDisposed();
|
|
104
153
|
return this.emitter;
|
|
105
154
|
}
|
|
106
155
|
getDefinitions() {
|
|
107
|
-
this.
|
|
156
|
+
this.ensureNotDisposed();
|
|
108
157
|
return this.registry.getDefinitions();
|
|
109
158
|
}
|
|
110
159
|
getHandlers() {
|
|
111
|
-
this.
|
|
112
|
-
return this.
|
|
160
|
+
this.ensureNotDisposed();
|
|
161
|
+
return this.registry.getHandlers();
|
|
113
162
|
}
|
|
114
163
|
getState() {
|
|
115
164
|
return this.state;
|
|
@@ -121,7 +170,7 @@ export class EventBus {
|
|
|
121
170
|
return this.registry.eventCount;
|
|
122
171
|
}
|
|
123
172
|
get handlerCount() {
|
|
124
|
-
return this.
|
|
173
|
+
return this.registry.handlerCount;
|
|
125
174
|
}
|
|
126
175
|
subscribe(listener) {
|
|
127
176
|
this.ensureNotDisposed();
|
|
@@ -145,17 +194,32 @@ export class EventBus {
|
|
|
145
194
|
eventId: event?.id,
|
|
146
195
|
});
|
|
147
196
|
}
|
|
148
|
-
|
|
197
|
+
publishDependencies() {
|
|
198
|
+
return {
|
|
199
|
+
emitter: this.emitter,
|
|
200
|
+
registry: this.registry,
|
|
201
|
+
busMiddleware: this.busMiddleware,
|
|
202
|
+
requireRegistration: this.options.requireRegistration,
|
|
203
|
+
ensureUsable: () => this.ensureUsable("publish"),
|
|
204
|
+
notify: (e) => this.notify(e),
|
|
205
|
+
onError: this.options.onError,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Auto-starts a CREATED bus; rejects a STOPPED or DISPOSED one.
|
|
210
|
+
*/
|
|
211
|
+
ensureUsable(operation) {
|
|
149
212
|
this.ensureNotDisposed();
|
|
213
|
+
if (this.state === EventBusState.STOPPED) {
|
|
214
|
+
throw new EventBusStoppedError(operation);
|
|
215
|
+
}
|
|
150
216
|
if (this.state === EventBusState.CREATED) {
|
|
151
217
|
this.start();
|
|
152
218
|
}
|
|
153
219
|
}
|
|
154
220
|
ensureNotDisposed() {
|
|
155
221
|
if (this.state === EventBusState.DISPOSED) {
|
|
156
|
-
throw new
|
|
157
|
-
code: "EVENT_BUS_DISPOSED",
|
|
158
|
-
});
|
|
222
|
+
throw new EventBusDisposedError();
|
|
159
223
|
}
|
|
160
224
|
}
|
|
161
225
|
notify(event) {
|
|
@@ -163,11 +227,20 @@ export class EventBus {
|
|
|
163
227
|
try {
|
|
164
228
|
listener(event);
|
|
165
229
|
}
|
|
166
|
-
catch {
|
|
230
|
+
catch (error) {
|
|
167
231
|
/**
|
|
168
|
-
* Observers must never be able to break
|
|
169
|
-
*
|
|
232
|
+
* Observers must never be able to break event bus
|
|
233
|
+
* operations; failures go to the onError hook.
|
|
170
234
|
*/
|
|
235
|
+
try {
|
|
236
|
+
this.options.onError?.(error, {
|
|
237
|
+
source: "observer",
|
|
238
|
+
event: event.event,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// Ignore failures of the error hook itself.
|
|
243
|
+
}
|
|
171
244
|
}
|
|
172
245
|
}
|
|
173
246
|
}
|
|
@@ -2,16 +2,34 @@
|
|
|
2
2
|
* Event bus publish methods for Zudojs.
|
|
3
3
|
*/
|
|
4
4
|
import type { Event, EventInput } from "../eventTypes/eventDefinition.type.js";
|
|
5
|
-
import { EventEmitter } from "../eventEmitter/eventEmitter.core.js";
|
|
6
|
-
import {
|
|
5
|
+
import type { EventEmitter } from "../eventEmitter/eventEmitter.core.js";
|
|
6
|
+
import type { EventEmitResult } from "../eventEmitter/eventEmitter.type.js";
|
|
7
|
+
import type { EventRegistry } from "../eventRegistry/eventRegistry.store.js";
|
|
7
8
|
import type { RegisteredEventMiddleware } from "../eventMiddleware/eventMiddleware.type.js";
|
|
8
|
-
import type { PublishOptions, EventPublishResult, EventBusEvent } from "./eventBus.type.js";
|
|
9
|
+
import type { PublishOptions, EventPublishResult, EventBusEvent, EventBusErrorContext } from "./eventBus.type.js";
|
|
10
|
+
/**
|
|
11
|
+
* Dependencies the publish functions need from the bus.
|
|
12
|
+
*/
|
|
13
|
+
export interface BusPublishDependencies {
|
|
14
|
+
readonly emitter: EventEmitter;
|
|
15
|
+
readonly registry: EventRegistry;
|
|
16
|
+
readonly busMiddleware: readonly RegisteredEventMiddleware[];
|
|
17
|
+
readonly requireRegistration: boolean;
|
|
18
|
+
readonly ensureUsable: () => void;
|
|
19
|
+
readonly notify: (e: EventBusEvent) => void;
|
|
20
|
+
readonly onError?: (error: unknown, context: EventBusErrorContext) => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Determines whether a middleware pipeline result is the emit
|
|
24
|
+
* result produced by the terminal handler dispatch.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isEventEmitResult(value: unknown): value is EventEmitResult;
|
|
9
27
|
/**
|
|
10
28
|
* Publishes an event through the bus.
|
|
11
29
|
*/
|
|
12
|
-
export declare function busPublish<TEvent extends Event>(event: TEvent, options: PublishOptions,
|
|
30
|
+
export declare function busPublish<TEvent extends Event>(event: TEvent, options: PublishOptions, deps: BusPublishDependencies): Promise<EventPublishResult<TEvent>>;
|
|
13
31
|
/**
|
|
14
32
|
* Creates and publishes an event from input data.
|
|
15
33
|
*/
|
|
16
|
-
export declare function busPublishEvent<TPayload>(input: EventInput<TPayload>, options: PublishOptions,
|
|
34
|
+
export declare function busPublishEvent<TPayload>(input: EventInput<TPayload>, options: PublishOptions, deps: BusPublishDependencies): Promise<EventPublishResult<Event<TPayload>>>;
|
|
17
35
|
//# sourceMappingURL=eventBus.publish.d.ts.map
|
|
@@ -1,78 +1,118 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Event bus publish methods for Zudojs.
|
|
3
3
|
*/
|
|
4
|
-
import { createEvent } from "../eventTypes/eventDefinition.type.js";
|
|
5
|
-
import {
|
|
6
|
-
import { EventRegistry } from "../eventRegistry/eventRegistry.store.js";
|
|
7
|
-
import { EventDispatchAbortedError, EventError, toEventError, } from "../eventErrors/eventError.base.js";
|
|
4
|
+
import { createEvent, isEvent } from "../eventTypes/eventDefinition.type.js";
|
|
5
|
+
import { EventDispatchAbortedError, EventTypeNotFoundError, InvalidEventError, } from "../eventErrors/eventError.base.js";
|
|
8
6
|
import { createEventMiddlewareContext } from "../eventMiddleware/eventMiddleware.helper.js";
|
|
9
7
|
import { executeEventMiddlewarePipeline } from "../eventMiddleware/eventMiddleware.pipeline.js";
|
|
10
8
|
import { registerMiddlewareItem } from "./eventBus.registration.js";
|
|
9
|
+
/**
|
|
10
|
+
* Determines whether a middleware pipeline result is the emit
|
|
11
|
+
* result produced by the terminal handler dispatch.
|
|
12
|
+
*/
|
|
13
|
+
export function isEventEmitResult(value) {
|
|
14
|
+
return (typeof value === "object" &&
|
|
15
|
+
value !== null &&
|
|
16
|
+
typeof value.handled === "boolean" &&
|
|
17
|
+
Array.isArray(value.results) &&
|
|
18
|
+
Array.isArray(value.errors));
|
|
19
|
+
}
|
|
11
20
|
/**
|
|
12
21
|
* Publishes an event through the bus.
|
|
13
22
|
*/
|
|
14
|
-
export async function busPublish(event, options,
|
|
15
|
-
ensureUsable();
|
|
23
|
+
export async function busPublish(event, options, deps) {
|
|
24
|
+
deps.ensureUsable();
|
|
25
|
+
if (!isEvent(event)) {
|
|
26
|
+
throw new InvalidEventError("publish() requires an Event (use publishEvent() for event input).");
|
|
27
|
+
}
|
|
16
28
|
if (options.signal?.aborted) {
|
|
17
29
|
throw new EventDispatchAbortedError("Event dispatch was aborted.", {
|
|
18
|
-
eventType: event?.type,
|
|
19
|
-
eventId: event?.id,
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
if (requireRegistration && !registry.has(event.type)) {
|
|
23
|
-
throw new EventError(`Event type "${event.type}" is not registered.`, {
|
|
24
30
|
eventType: event.type,
|
|
25
31
|
eventId: event.id,
|
|
26
32
|
});
|
|
27
33
|
}
|
|
34
|
+
if (deps.requireRegistration && !deps.registry.has(event.type)) {
|
|
35
|
+
throw new EventTypeNotFoundError(event.type);
|
|
36
|
+
}
|
|
28
37
|
const allMiddleware = [
|
|
29
|
-
...busMiddleware,
|
|
30
|
-
...(options.middleware ?? []).map((m, index) => registerMiddlewareItem(m, index
|
|
38
|
+
...deps.busMiddleware,
|
|
39
|
+
...(options.middleware ?? []).map((m, index) => registerMiddlewareItem(m, index, "publish-mw")),
|
|
31
40
|
];
|
|
32
41
|
const middlewareContext = createEventMiddlewareContext(event, {
|
|
33
42
|
signal: options.signal,
|
|
34
43
|
metadata: options.metadata,
|
|
35
44
|
});
|
|
36
45
|
const terminal = async () => {
|
|
37
|
-
return emitter.emit(event, {
|
|
46
|
+
return deps.emitter.emit(event, {
|
|
38
47
|
mode: options.mode,
|
|
39
48
|
errorMode: options.errorMode,
|
|
40
49
|
signal: options.signal,
|
|
41
50
|
metadata: options.metadata,
|
|
42
51
|
});
|
|
43
52
|
};
|
|
44
|
-
let
|
|
45
|
-
let
|
|
53
|
+
let emitResult;
|
|
54
|
+
let middlewareExecutions;
|
|
46
55
|
if (allMiddleware.length > 0) {
|
|
47
56
|
const pipelineResult = await executeEventMiddlewarePipeline(allMiddleware, middlewareContext, terminal);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
};
|
|
57
|
+
middlewareExecutions = pipelineResult.executions;
|
|
58
|
+
if (isEventEmitResult(pipelineResult.result)) {
|
|
59
|
+
emitResult = pipelineResult.result;
|
|
60
|
+
}
|
|
53
61
|
}
|
|
54
62
|
else {
|
|
55
|
-
|
|
63
|
+
emitResult = await terminal();
|
|
56
64
|
}
|
|
57
|
-
notify({
|
|
65
|
+
deps.notify({
|
|
58
66
|
type: "published",
|
|
59
67
|
event,
|
|
60
68
|
timestamp: new Date(),
|
|
61
69
|
});
|
|
70
|
+
if (emitResult === undefined) {
|
|
71
|
+
/**
|
|
72
|
+
* A middleware short-circuited (did not call next()); no
|
|
73
|
+
* handler ran.
|
|
74
|
+
*/
|
|
75
|
+
return {
|
|
76
|
+
event,
|
|
77
|
+
handled: false,
|
|
78
|
+
handlerCount: 0,
|
|
79
|
+
succeeded: 0,
|
|
80
|
+
failed: 0,
|
|
81
|
+
results: [],
|
|
82
|
+
errors: [],
|
|
83
|
+
shortCircuited: true,
|
|
84
|
+
middlewareExecutions,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (deps.onError && emitResult.errors.length > 0) {
|
|
88
|
+
for (const error of emitResult.errors) {
|
|
89
|
+
try {
|
|
90
|
+
deps.onError(error, { source: "handler", event });
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
/**
|
|
94
|
+
* A failing error hook must not break publishing.
|
|
95
|
+
*/
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
62
99
|
return {
|
|
63
|
-
event,
|
|
64
|
-
handled:
|
|
65
|
-
handlerCount:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
100
|
+
event: emitResult.event,
|
|
101
|
+
handled: emitResult.handled,
|
|
102
|
+
handlerCount: emitResult.results.length,
|
|
103
|
+
succeeded: emitResult.succeeded,
|
|
104
|
+
failed: emitResult.failed,
|
|
105
|
+
results: emitResult.results.map((execution) => execution.result),
|
|
106
|
+
errors: emitResult.errors,
|
|
107
|
+
shortCircuited: false,
|
|
108
|
+
middlewareExecutions,
|
|
69
109
|
};
|
|
70
110
|
}
|
|
71
111
|
/**
|
|
72
112
|
* Creates and publishes an event from input data.
|
|
73
113
|
*/
|
|
74
|
-
export async function busPublishEvent(input, options,
|
|
114
|
+
export async function busPublishEvent(input, options, deps) {
|
|
75
115
|
const event = createEvent(input);
|
|
76
|
-
return busPublish(event, options,
|
|
116
|
+
return busPublish(event, options, deps);
|
|
77
117
|
}
|
|
78
118
|
//# sourceMappingURL=eventBus.publish.js.map
|
|
@@ -6,30 +6,29 @@ import type { EventTypePattern } from "../eventTypes/eventType.type.js";
|
|
|
6
6
|
import type { EventHandlerLike, EventHandlerOptions } from "../eventHandler/eventHandler.core.js";
|
|
7
7
|
import type { EventSubscription } from "../eventSubscription/eventSubscription.core.js";
|
|
8
8
|
import type { EventMiddlewareLike, EventMiddlewareOptions, RegisteredEventMiddleware } from "../eventMiddleware/eventMiddleware.type.js";
|
|
9
|
+
import type { RegisteredEventDefinition } from "../eventRegistry/eventRegistry.type.js";
|
|
10
|
+
import type { EventBusMiddlewareItem } from "./eventBus.type.js";
|
|
9
11
|
/**
|
|
10
12
|
* Registers an event definition on the given registry.
|
|
11
13
|
*/
|
|
12
14
|
export declare function busRegister<TType extends EventType, TPayload>(registry: {
|
|
13
|
-
register:
|
|
14
|
-
}, definition: EventDefinition<TType, TPayload>, ensureUsable: () => void):
|
|
15
|
+
register: (definition: EventDefinition<TType, TPayload>) => RegisteredEventDefinition<TType, TPayload>;
|
|
16
|
+
}, definition: EventDefinition<TType, TPayload>, ensureUsable: () => void): RegisteredEventDefinition<TType, TPayload>;
|
|
17
|
+
interface HandlerSource {
|
|
18
|
+
on: <TEvent extends Event = Event>(eventType: EventTypePattern, handler: EventHandlerLike<TEvent>, options: Omit<EventHandlerOptions, "eventType">) => EventSubscription;
|
|
19
|
+
}
|
|
15
20
|
/**
|
|
16
21
|
* Registers a handler on the emitter.
|
|
17
22
|
*/
|
|
18
|
-
export declare function busOn<TEvent extends Event = Event>(emitter:
|
|
19
|
-
on: (eventType: EventTypePattern, handler: EventHandlerLike<TEvent>, options: Omit<EventHandlerOptions, "eventType">) => EventSubscription;
|
|
20
|
-
}, eventType: EventTypePattern, handler: EventHandlerLike<TEvent>, options: Omit<EventHandlerOptions, "eventType"> | undefined, ensureUsable: () => void): EventSubscription;
|
|
23
|
+
export declare function busOn<TEvent extends Event = Event>(emitter: HandlerSource, eventType: EventTypePattern, handler: EventHandlerLike<TEvent>, options: Omit<EventHandlerOptions, "eventType"> | undefined, ensureUsable: () => void): EventSubscription;
|
|
21
24
|
/**
|
|
22
25
|
* Registers a one-time handler.
|
|
23
26
|
*/
|
|
24
|
-
export declare function busOnce<TEvent extends Event = Event>(emitter:
|
|
25
|
-
on: (eventType: EventTypePattern, handler: EventHandlerLike<TEvent>, options: Omit<EventHandlerOptions, "eventType">) => EventSubscription;
|
|
26
|
-
}, eventType: EventTypePattern, handler: EventHandlerLike<TEvent>, options: Omit<EventHandlerOptions, "eventType" | "once"> | undefined, ensureUsable: () => void): EventSubscription;
|
|
27
|
+
export declare function busOnce<TEvent extends Event = Event>(emitter: HandlerSource, eventType: EventTypePattern, handler: EventHandlerLike<TEvent>, options: Omit<EventHandlerOptions, "eventType" | "once"> | undefined, ensureUsable: () => void): EventSubscription;
|
|
27
28
|
/**
|
|
28
29
|
* Registers a wildcard handler.
|
|
29
30
|
*/
|
|
30
|
-
export declare function busOnAny<TEvent extends Event = Event>(emitter:
|
|
31
|
-
on: (eventType: EventTypePattern, handler: EventHandlerLike<TEvent>, options: Omit<EventHandlerOptions, "eventType">) => EventSubscription;
|
|
32
|
-
}, handler: EventHandlerLike<TEvent>, options: Omit<EventHandlerOptions, "eventType"> | undefined, ensureUsable: () => void): EventSubscription;
|
|
31
|
+
export declare function busOnAny<TEvent extends Event = Event>(emitter: HandlerSource, handler: EventHandlerLike<TEvent>, options: Omit<EventHandlerOptions, "eventType"> | undefined, ensureUsable: () => void): EventSubscription;
|
|
33
32
|
/**
|
|
34
33
|
* Removes a handler.
|
|
35
34
|
*/
|
|
@@ -37,11 +36,23 @@ export declare function busOff(emitter: {
|
|
|
37
36
|
off: (subscription: EventSubscription) => boolean;
|
|
38
37
|
}, subscription: EventSubscription, ensureNotDisposed: () => void): boolean;
|
|
39
38
|
/**
|
|
40
|
-
* Adds middleware to the bus.
|
|
39
|
+
* Adds middleware to the bus. The middleware is validated
|
|
40
|
+
* eagerly (invalid middleware or a non-finite priority throw
|
|
41
|
+
* here, not on the next publish).
|
|
41
42
|
*/
|
|
42
43
|
export declare function busUse(busMiddleware: RegisteredEventMiddleware[], middleware: EventMiddlewareLike, options?: EventMiddlewareOptions): () => void;
|
|
44
|
+
/**
|
|
45
|
+
* Determines whether a value is an already registered middleware
|
|
46
|
+
* (created by createEventMiddleware or a builder helper).
|
|
47
|
+
*/
|
|
48
|
+
export declare function isRegisteredEventMiddleware(value: unknown): value is RegisteredEventMiddleware;
|
|
43
49
|
/**
|
|
44
50
|
* Normalizes a middleware item into a RegisteredEventMiddleware.
|
|
51
|
+
*
|
|
52
|
+
* `prefix` keeps generated ids stable per source ("bus-mw" for
|
|
53
|
+
* constructor middleware, "publish-mw" for per-publication
|
|
54
|
+
* middleware).
|
|
45
55
|
*/
|
|
46
|
-
export declare function registerMiddlewareItem(item:
|
|
56
|
+
export declare function registerMiddlewareItem(item: EventBusMiddlewareItem, index: number, prefix?: string): RegisteredEventMiddleware;
|
|
57
|
+
export {};
|
|
47
58
|
//# sourceMappingURL=eventBus.registration.d.ts.map
|