@zudojs/events 1.2.0 → 1.3.1
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 +57 -1
- package/dist/eventBus/eventBus.core.d.ts +8 -3
- package/dist/eventBus/eventBus.core.js +5 -0
- package/dist/eventBus/eventBus.registration.d.ts +9 -2
- package/dist/eventBus/eventBus.registration.js +15 -1
- package/dist/eventBus/eventBus.type.d.ts +6 -1
- package/dist/eventEmitter/eventEmitter.parallel.d.ts +2 -1
- package/dist/eventEmitter/eventEmitter.parallel.js +1 -1
- package/dist/eventEmitter/eventEmitter.sequential.d.ts +7 -1
- package/dist/eventEmitter/eventEmitter.sequential.js +13 -1
- package/dist/eventEmitter/eventEmitter.type.d.ts +2 -1
- package/dist/eventErrors/eventError.base.d.ts +5 -17
- package/dist/eventErrors/eventError.base.js +5 -32
- package/dist/eventHandler/eventHandler.core.d.ts +9 -2
- package/dist/eventHandler/eventHandler.core.js +19 -5
- package/dist/eventTypes/eventType.type.d.ts +16 -1
- package/dist/eventTypes/eventType.type.js +16 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -50,13 +50,39 @@ await bus.publishEvent({
|
|
|
50
50
|
- Event bus with a middleware pipeline (`use()`, constructor and per-publish middleware)
|
|
51
51
|
- Sequential or parallel handler dispatch with `THROW` / `CONTINUE` error modes
|
|
52
52
|
- Handler priorities, one-time handlers, per-handler timeouts
|
|
53
|
-
- Wildcard subscriptions (`"user.*"`, `"*"`)
|
|
53
|
+
- Wildcard subscriptions (`"user.*"`, `"*"`) — multi-level, see [Wildcard patterns](#wildcard-patterns)
|
|
54
54
|
- Event registry for typed definitions; handlers registered on the registry are dispatched by the bus
|
|
55
55
|
- Deep-frozen events (`freezeEvents`, on by default) so handlers cannot alter what other handlers see: handlers receive a frozen *copy* (`createFrozenEventSnapshot`), so the publisher's own objects are never frozen, and Map, Set and Date values (including `event.timestamp`) become read-only variants that throw on mutation. Class instances are passed by reference.
|
|
56
56
|
- A handler unsubscribed by an earlier handler in the same dispatch, or left over after the bus is disposed mid-dispatch, does not run
|
|
57
57
|
- Listener-leak warnings (`maxListeners`, reported through `onWarning` or, by default, `process.emitWarning` with type `ZudojsEventsWarning`) and an `onError` hook for fire-and-forget publishes
|
|
58
58
|
- Typed error classes from `@zudojs/errors` (`EventHandlerError`, `EventMiddlewareError`, `EventDispatchAbortedError`, …)
|
|
59
59
|
|
|
60
|
+
## Wildcard patterns
|
|
61
|
+
|
|
62
|
+
A handler's event type can be an exact type, a namespace wildcard, or the
|
|
63
|
+
catch-all `"*"`:
|
|
64
|
+
|
|
65
|
+
| Pattern | Matches | Does not match |
|
|
66
|
+
|---------|---------|----------------|
|
|
67
|
+
| `"task.created"` | `task.created` | `task.created.v2`, `task` |
|
|
68
|
+
| `"task.*"` | `task`, `task.created`, `task.sub.created` (any depth) | `tasks.created`, `order.created` |
|
|
69
|
+
| `"*"` | every event | — |
|
|
70
|
+
|
|
71
|
+
A namespace wildcard is **multi-level**: `"task.*"` matches every event
|
|
72
|
+
under `task` at any depth, plus the bare `task` event, like a prefix match
|
|
73
|
+
on whole segments. There is no single-level form — `"task.*.created"`,
|
|
74
|
+
`"task.cre*"` and other positions are rejected as invalid patterns. To
|
|
75
|
+
handle only direct children, filter in the handler:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { getEventTypeSegments } from "@zudojs/events";
|
|
79
|
+
|
|
80
|
+
bus.on("task.*", (event) => {
|
|
81
|
+
if (getEventTypeSegments(event.type).length !== 2) return; // task.x only
|
|
82
|
+
// ...
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
60
86
|
## Lifecycle
|
|
61
87
|
|
|
62
88
|
```
|
|
@@ -71,6 +97,9 @@ CREATED ──(first use / start)──▶ ACTIVE ◀──(start)── STOPPED
|
|
|
71
97
|
- `stop()` moves the bus to `STOPPED`; publishing or subscribing then throws
|
|
72
98
|
`EventBusStoppedError` until `start()` is called. Handlers and definitions are kept.
|
|
73
99
|
- `dispose()` is final; every operation throws `EventBusDisposedError`.
|
|
100
|
+
- Both errors are defined in `@zudojs/errors` (as `EventError` subclasses) and
|
|
101
|
+
re-exported from `@zudojs/events`, so `instanceof` works whichever package you
|
|
102
|
+
import them from.
|
|
74
103
|
|
|
75
104
|
## Publish results
|
|
76
105
|
|
|
@@ -90,6 +119,33 @@ In the default `CONTINUE` error mode handler failures are collected in
|
|
|
90
119
|
`EventHandlerError` rejects the publish. Errors thrown by a middleware itself are
|
|
91
120
|
wrapped as `EventMiddlewareError`; handler errors and aborts pass through unwrapped.
|
|
92
121
|
|
|
122
|
+
## Middleware
|
|
123
|
+
|
|
124
|
+
`bus.use()` accepts everything the `middleware` constructor option does: a
|
|
125
|
+
middleware function or `{ handle }` object, or a registered middleware from
|
|
126
|
+
`createEventMiddleware()` or a builder helper (`validateEventMiddleware`,
|
|
127
|
+
`beforeEvent`, `aroundEvent`, `timingEventMiddleware`, …). It returns a function
|
|
128
|
+
that removes the middleware again.
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
const remove = bus.use(validateEventMiddleware((event) => event.payload != null));
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Timeouts and aborts
|
|
135
|
+
|
|
136
|
+
A handler registered with `timeoutMs` fails with `EventTimeoutError` when it
|
|
137
|
+
does not settle in time, and the `context.signal` it received is aborted with
|
|
138
|
+
that error as its `reason`, so a handler that listens to the signal can stop
|
|
139
|
+
its work. Timing out one handler does not abort the dispatch or other handlers.
|
|
140
|
+
|
|
141
|
+
Pass `signal` to `publish()` to abort a sequential dispatch. The publish rejects
|
|
142
|
+
with `EventDispatchAbortedError` (carrying the partial `results` and `errors`)
|
|
143
|
+
when the signal is aborted before a handler starts **or while any handler is
|
|
144
|
+
running — including the last or only one**, even if that handler then returns
|
|
145
|
+
normally. In `PARALLEL` mode every handler has already started, so only a
|
|
146
|
+
signal that is aborted before dispatch begins rejects; handlers can still
|
|
147
|
+
observe `context.signal`.
|
|
148
|
+
|
|
93
149
|
## Registry
|
|
94
150
|
|
|
95
151
|
`bus.register(defineEvent("order.placed"))` records a definition; with
|
|
@@ -8,8 +8,8 @@ import type { EventSubscription } from "../eventSubscription/eventSubscription.c
|
|
|
8
8
|
import { EventEmitter } from "../eventEmitter/eventEmitter.core.js";
|
|
9
9
|
import { EventRegistry } from "../eventRegistry/eventRegistry.store.js";
|
|
10
10
|
import { EventError } from "../eventErrors/eventError.base.js";
|
|
11
|
-
import type {
|
|
12
|
-
import type { EventBusOptions, PublishOptions, EventPublishResult, EventBusListener } from "./eventBus.type.js";
|
|
11
|
+
import type { EventMiddlewareOptions } from "../eventMiddleware/eventMiddleware.type.js";
|
|
12
|
+
import type { EventBusMiddlewareItem, EventBusOptions, PublishOptions, EventPublishResult, EventBusListener } from "./eventBus.type.js";
|
|
13
13
|
import { EventBusState } from "./eventBus.type.js";
|
|
14
14
|
export { EventBusState } from "./eventBus.type.js";
|
|
15
15
|
/**
|
|
@@ -40,7 +40,12 @@ export declare class EventBus {
|
|
|
40
40
|
once<TEvent extends Event = Event>(eventType: EventTypePattern, handler: EventHandlerLike<TEvent>, options?: Omit<EventHandlerOptions, "eventType" | "once">): EventSubscription;
|
|
41
41
|
onAny<TEvent extends Event = Event>(handler: EventHandlerLike<TEvent>, options?: Omit<EventHandlerOptions, "eventType">): EventSubscription;
|
|
42
42
|
off(subscription: EventSubscription): boolean;
|
|
43
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Adds bus middleware: a middleware function or object, or a
|
|
45
|
+
* registered middleware from createEventMiddleware() or a builder
|
|
46
|
+
* helper such as validateEventMiddleware().
|
|
47
|
+
*/
|
|
48
|
+
use(middleware: EventBusMiddlewareItem, options?: EventMiddlewareOptions): () => void;
|
|
44
49
|
publish<TEvent extends Event>(event: TEvent, options?: PublishOptions): Promise<EventPublishResult<TEvent>>;
|
|
45
50
|
publishEvent<TPayload>(input: EventInput<TPayload>, options?: PublishOptions): Promise<EventPublishResult<Event<TPayload>>>;
|
|
46
51
|
/**
|
|
@@ -100,6 +100,11 @@ export class EventBus {
|
|
|
100
100
|
off(subscription) {
|
|
101
101
|
return busOff(this.emitter, subscription, () => this.ensureNotDisposed());
|
|
102
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Adds bus middleware: a middleware function or object, or a
|
|
105
|
+
* registered middleware from createEventMiddleware() or a builder
|
|
106
|
+
* helper such as validateEventMiddleware().
|
|
107
|
+
*/
|
|
103
108
|
use(middleware, options = {}) {
|
|
104
109
|
this.ensureNotDisposed();
|
|
105
110
|
return busUse(this.busMiddleware, middleware, options);
|
|
@@ -5,7 +5,7 @@ import type { Event, EventDefinition, EventType } from "../eventTypes/eventDefin
|
|
|
5
5
|
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
|
-
import type {
|
|
8
|
+
import type { EventMiddlewareOptions, RegisteredEventMiddleware } from "../eventMiddleware/eventMiddleware.type.js";
|
|
9
9
|
import type { RegisteredEventDefinition } from "../eventRegistry/eventRegistry.type.js";
|
|
10
10
|
import type { EventBusMiddlewareItem } from "./eventBus.type.js";
|
|
11
11
|
/**
|
|
@@ -39,8 +39,15 @@ export declare function busOff(emitter: {
|
|
|
39
39
|
* Adds middleware to the bus. The middleware is validated
|
|
40
40
|
* eagerly (invalid middleware or a non-finite priority throw
|
|
41
41
|
* here, not on the next publish).
|
|
42
|
+
*
|
|
43
|
+
* Accepts the same items as the `middleware` constructor option: a
|
|
44
|
+
* plain middleware function or object, or a registered middleware
|
|
45
|
+
* made by createEventMiddleware() or a builder helper such as
|
|
46
|
+
* validateEventMiddleware(). For a registered middleware its own id,
|
|
47
|
+
* description, priority and enabled flag are kept unless `options`
|
|
48
|
+
* overrides them.
|
|
42
49
|
*/
|
|
43
|
-
export declare function busUse(busMiddleware: RegisteredEventMiddleware[], middleware:
|
|
50
|
+
export declare function busUse(busMiddleware: RegisteredEventMiddleware[], middleware: EventBusMiddlewareItem, options?: EventMiddlewareOptions): () => void;
|
|
44
51
|
/**
|
|
45
52
|
* Determines whether a value is an already registered middleware
|
|
46
53
|
* (created by createEventMiddleware or a builder helper).
|
|
@@ -42,9 +42,23 @@ export function busOff(emitter, subscription, ensureNotDisposed) {
|
|
|
42
42
|
* Adds middleware to the bus. The middleware is validated
|
|
43
43
|
* eagerly (invalid middleware or a non-finite priority throw
|
|
44
44
|
* here, not on the next publish).
|
|
45
|
+
*
|
|
46
|
+
* Accepts the same items as the `middleware` constructor option: a
|
|
47
|
+
* plain middleware function or object, or a registered middleware
|
|
48
|
+
* made by createEventMiddleware() or a builder helper such as
|
|
49
|
+
* validateEventMiddleware(). For a registered middleware its own id,
|
|
50
|
+
* description, priority and enabled flag are kept unless `options`
|
|
51
|
+
* overrides them.
|
|
45
52
|
*/
|
|
46
53
|
export function busUse(busMiddleware, middleware, options = {}) {
|
|
47
|
-
const registered =
|
|
54
|
+
const registered = isRegisteredEventMiddleware(middleware)
|
|
55
|
+
? createEventMiddleware(middleware.middleware, {
|
|
56
|
+
id: options.id ?? middleware.id,
|
|
57
|
+
description: options.description ?? middleware.description,
|
|
58
|
+
priority: options.priority ?? middleware.priority,
|
|
59
|
+
enabled: options.enabled ?? middleware.enabled,
|
|
60
|
+
})
|
|
61
|
+
: createEventMiddleware(middleware, options);
|
|
48
62
|
busMiddleware.push(registered);
|
|
49
63
|
return () => {
|
|
50
64
|
const idx = busMiddleware.indexOf(registered);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Event bus type definitions for Zudojs.
|
|
3
3
|
*/
|
|
4
|
+
import type { EventHandlerError } from "@zudojs/errors";
|
|
4
5
|
import type { Event } from "../eventTypes/eventDefinition.type.js";
|
|
5
6
|
import type { EventEmitterMode, EventErrorMode } from "../eventEmitter/eventEmitter.type.js";
|
|
6
7
|
import type { EventMiddlewareLike, RegisteredEventMiddleware } from "../eventMiddleware/eventMiddleware.type.js";
|
|
@@ -93,7 +94,11 @@ export interface EventPublishResult<TEvent extends Event = Event> {
|
|
|
93
94
|
*/
|
|
94
95
|
readonly failed: number;
|
|
95
96
|
readonly results: readonly unknown[];
|
|
96
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Handler failures, each wrapped as EventHandlerError (its
|
|
99
|
+
* `cause` is the raw thrown value).
|
|
100
|
+
*/
|
|
101
|
+
readonly errors: readonly EventHandlerError[];
|
|
97
102
|
/**
|
|
98
103
|
* True when a middleware did not call next(), so no handler ran.
|
|
99
104
|
*/
|
|
@@ -8,11 +8,12 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { Event } from "../eventTypes/eventDefinition.type.js";
|
|
10
10
|
import type { EventHandlerContext, RegisteredEventHandler } from "../eventHandler/eventHandler.core.js";
|
|
11
|
+
import { type EventHandlerError } from "../eventErrors/eventError.base.js";
|
|
11
12
|
import type { EventHandlerExecutionResult } from "./eventEmitter.type.js";
|
|
12
13
|
import { EventErrorMode } from "./eventEmitter.type.js";
|
|
13
14
|
import type { DispatchHooks } from "./eventEmitter.sequential.js";
|
|
14
15
|
/**
|
|
15
16
|
* Executes handlers concurrently.
|
|
16
17
|
*/
|
|
17
|
-
export declare function emitParallel<TEvent extends Event>(handlers: readonly RegisteredEventHandler<TEvent>[], event: TEvent, context: EventHandlerContext<TEvent>, errorMode: EventErrorMode, results: EventHandlerExecutionResult[], errors:
|
|
18
|
+
export declare function emitParallel<TEvent extends Event>(handlers: readonly RegisteredEventHandler<TEvent>[], event: TEvent, context: EventHandlerContext<TEvent>, errorMode: EventErrorMode, results: EventHandlerExecutionResult[], errors: EventHandlerError[], hooks: DispatchHooks): Promise<void>;
|
|
18
19
|
//# sourceMappingURL=eventEmitter.parallel.d.ts.map
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* aborted when dispatch begins rejects the emit.
|
|
8
8
|
*/
|
|
9
9
|
import { executeRegisteredEventHandler } from "../eventHandler/eventHandler.core.js";
|
|
10
|
-
import { createEventHandlerError } from "../eventErrors/eventError.base.js";
|
|
10
|
+
import { createEventHandlerError, } from "../eventErrors/eventError.base.js";
|
|
11
11
|
import { EventErrorMode } from "./eventEmitter.type.js";
|
|
12
12
|
import { createAbortError } from "./eventEmitter.abort.js";
|
|
13
13
|
/**
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { Event } from "../eventTypes/eventDefinition.type.js";
|
|
5
5
|
import type { EventHandlerContext, RegisteredEventHandler } from "../eventHandler/eventHandler.core.js";
|
|
6
|
+
import { type EventHandlerError } from "../eventErrors/eventError.base.js";
|
|
6
7
|
import type { EventHandlerExecutionResult } from "./eventEmitter.type.js";
|
|
7
8
|
import { EventErrorMode } from "./eventEmitter.type.js";
|
|
8
9
|
/**
|
|
@@ -23,6 +24,11 @@ export interface DispatchHooks {
|
|
|
23
24
|
}
|
|
24
25
|
/**
|
|
25
26
|
* Executes handlers sequentially.
|
|
27
|
+
*
|
|
28
|
+
* Rejects with EventDispatchAbortedError when the dispatch signal is
|
|
29
|
+
* aborted before a handler starts or while any handler — including
|
|
30
|
+
* the last or only one — is running, even if that handler then
|
|
31
|
+
* returns normally.
|
|
26
32
|
*/
|
|
27
|
-
export declare function emitSequential<TEvent extends Event>(handlers: readonly RegisteredEventHandler<TEvent>[], event: TEvent, context: EventHandlerContext<TEvent>, errorMode: EventErrorMode, results: EventHandlerExecutionResult[], errors:
|
|
33
|
+
export declare function emitSequential<TEvent extends Event>(handlers: readonly RegisteredEventHandler<TEvent>[], event: TEvent, context: EventHandlerContext<TEvent>, errorMode: EventErrorMode, results: EventHandlerExecutionResult[], errors: EventHandlerError[], hooks: DispatchHooks): Promise<void>;
|
|
28
34
|
//# sourceMappingURL=eventEmitter.sequential.d.ts.map
|
|
@@ -2,11 +2,16 @@
|
|
|
2
2
|
* Sequential event handler dispatch for Zudojs.
|
|
3
3
|
*/
|
|
4
4
|
import { executeRegisteredEventHandler } from "../eventHandler/eventHandler.core.js";
|
|
5
|
-
import { createEventHandlerError } from "../eventErrors/eventError.base.js";
|
|
5
|
+
import { createEventHandlerError, } from "../eventErrors/eventError.base.js";
|
|
6
6
|
import { EventErrorMode } from "./eventEmitter.type.js";
|
|
7
7
|
import { createAbortError } from "./eventEmitter.abort.js";
|
|
8
8
|
/**
|
|
9
9
|
* Executes handlers sequentially.
|
|
10
|
+
*
|
|
11
|
+
* Rejects with EventDispatchAbortedError when the dispatch signal is
|
|
12
|
+
* aborted before a handler starts or while any handler — including
|
|
13
|
+
* the last or only one — is running, even if that handler then
|
|
14
|
+
* returns normally.
|
|
10
15
|
*/
|
|
11
16
|
export async function emitSequential(handlers, event, context, errorMode, results, errors, hooks) {
|
|
12
17
|
for (const handler of handlers) {
|
|
@@ -49,5 +54,12 @@ export async function emitSequential(handlers, event, context, errorMode, result
|
|
|
49
54
|
}
|
|
50
55
|
}
|
|
51
56
|
}
|
|
57
|
+
// The check at the top of the loop only runs before the *next*
|
|
58
|
+
// handler, so an abort during the last (or only) handler used to
|
|
59
|
+
// resolve as a normal, successful dispatch — while the same abort
|
|
60
|
+
// with a handler still to come rejected.
|
|
61
|
+
if (context.signal.aborted) {
|
|
62
|
+
throw createAbortError(event, results, errors);
|
|
63
|
+
}
|
|
52
64
|
}
|
|
53
65
|
//# sourceMappingURL=eventEmitter.sequential.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Event emitter type definitions for Zudojs.
|
|
3
3
|
*/
|
|
4
|
+
import type { EventHandlerError } from "@zudojs/errors";
|
|
4
5
|
import type { Event } from "../eventTypes/eventDefinition.type.js";
|
|
5
6
|
import type { RegisteredEventHandler } from "../eventHandler/eventHandler.core.js";
|
|
6
7
|
import type { EventSubscription } from "../eventSubscription/eventSubscription.core.js";
|
|
@@ -87,7 +88,7 @@ export interface EventEmitResult<TEvent extends Event = Event> {
|
|
|
87
88
|
* Handler failures wrapped as EventHandlerError (cause holds the
|
|
88
89
|
* raw thrown value).
|
|
89
90
|
*/
|
|
90
|
-
readonly errors: readonly
|
|
91
|
+
readonly errors: readonly EventHandlerError[];
|
|
91
92
|
/**
|
|
92
93
|
* Number of handlers that completed successfully.
|
|
93
94
|
*/
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
* @zudojs/events/eventErrors/eventError.base
|
|
3
3
|
*
|
|
4
4
|
* Event error types are centralized in @zudojs/errors and
|
|
5
|
-
* re-exported here
|
|
6
|
-
*
|
|
5
|
+
* re-exported here, including EventBusStoppedError and
|
|
6
|
+
* EventBusDisposedError. EventDispatchAbortedError is extended here
|
|
7
|
+
* to carry the partial results of an aborted dispatch.
|
|
7
8
|
*/
|
|
8
|
-
import {
|
|
9
|
-
export { EventError, createEventError, isEventError, toEventError, EventPublishError, InvalidEventError, EventTypeNotFoundError, EventHandlerError, createEventHandlerError, EventHandlerNotFoundError, DuplicateEventHandlerError, DuplicateEventDefinitionError, EventDefinitionNotFoundError, EventEmitterDisposedError, EventRegistryDisposedError, EventSubscriptionClosedError, EventListenerLimitExceededError, EventTimeoutError, EventMiddlewareError, EventSerializationError, EventDeserializationError, } from "@zudojs/errors";
|
|
9
|
+
import { EventDispatchAbortedError as BaseEventDispatchAbortedError } from "@zudojs/errors";
|
|
10
|
+
export { EventError, createEventError, isEventError, toEventError, EventPublishError, InvalidEventError, EventTypeNotFoundError, EventHandlerError, createEventHandlerError, EventHandlerNotFoundError, DuplicateEventHandlerError, DuplicateEventDefinitionError, EventDefinitionNotFoundError, EventEmitterDisposedError, EventRegistryDisposedError, EventSubscriptionClosedError, EventListenerLimitExceededError, EventTimeoutError, EventMiddlewareError, EventSerializationError, EventDeserializationError, EventBusDisposedError, EventBusStoppedError, } from "@zudojs/errors";
|
|
10
11
|
/**
|
|
11
12
|
* Options for EventDispatchAbortedError.
|
|
12
13
|
*/
|
|
@@ -32,17 +33,4 @@ export declare class EventDispatchAbortedError extends BaseEventDispatchAbortedE
|
|
|
32
33
|
readonly errors: readonly unknown[];
|
|
33
34
|
constructor(message?: string, options?: EventDispatchAbortedErrorOptions);
|
|
34
35
|
}
|
|
35
|
-
/**
|
|
36
|
-
* Error thrown when an EventBus is used after dispose().
|
|
37
|
-
*/
|
|
38
|
-
export declare class EventBusDisposedError extends EventError {
|
|
39
|
-
constructor();
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Error thrown when publishing or subscribing on a stopped
|
|
43
|
-
* EventBus. Call start() to resume.
|
|
44
|
-
*/
|
|
45
|
-
export declare class EventBusStoppedError extends EventError {
|
|
46
|
-
constructor(operation: string);
|
|
47
|
-
}
|
|
48
36
|
//# sourceMappingURL=eventError.base.d.ts.map
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
* @zudojs/events/eventErrors/eventError.base
|
|
3
3
|
*
|
|
4
4
|
* Event error types are centralized in @zudojs/errors and
|
|
5
|
-
* re-exported here
|
|
6
|
-
*
|
|
5
|
+
* re-exported here, including EventBusStoppedError and
|
|
6
|
+
* EventBusDisposedError. EventDispatchAbortedError is extended here
|
|
7
|
+
* to carry the partial results of an aborted dispatch.
|
|
7
8
|
*/
|
|
8
|
-
import {
|
|
9
|
-
export { EventError, createEventError, isEventError, toEventError, EventPublishError, InvalidEventError, EventTypeNotFoundError, EventHandlerError, createEventHandlerError, EventHandlerNotFoundError, DuplicateEventHandlerError, DuplicateEventDefinitionError, EventDefinitionNotFoundError, EventEmitterDisposedError, EventRegistryDisposedError, EventSubscriptionClosedError, EventListenerLimitExceededError, EventTimeoutError, EventMiddlewareError, EventSerializationError, EventDeserializationError, } from "@zudojs/errors";
|
|
9
|
+
import { EventDispatchAbortedError as BaseEventDispatchAbortedError } from "@zudojs/errors";
|
|
10
|
+
export { EventError, createEventError, isEventError, toEventError, EventPublishError, InvalidEventError, EventTypeNotFoundError, EventHandlerError, createEventHandlerError, EventHandlerNotFoundError, DuplicateEventHandlerError, DuplicateEventDefinitionError, EventDefinitionNotFoundError, EventEmitterDisposedError, EventRegistryDisposedError, EventSubscriptionClosedError, EventListenerLimitExceededError, EventTimeoutError, EventMiddlewareError, EventSerializationError, EventDeserializationError, EventBusDisposedError, EventBusStoppedError, } from "@zudojs/errors";
|
|
10
11
|
/**
|
|
11
12
|
* Error thrown when event dispatch is aborted through an
|
|
12
13
|
* AbortSignal. Carries the partial results and errors gathered
|
|
@@ -24,32 +25,4 @@ export class EventDispatchAbortedError extends BaseEventDispatchAbortedError {
|
|
|
24
25
|
this.errors = Object.freeze([...(options.errors ?? [])]);
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Error thrown when an EventBus is used after dispose().
|
|
29
|
-
*/
|
|
30
|
-
export class EventBusDisposedError extends EventError {
|
|
31
|
-
constructor() {
|
|
32
|
-
super("Event bus has already been disposed.", {
|
|
33
|
-
code: ErrorCode.LIFECYCLE_DISPOSED,
|
|
34
|
-
statusCode: 500,
|
|
35
|
-
expose: false,
|
|
36
|
-
isOperational: false,
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Error thrown when publishing or subscribing on a stopped
|
|
42
|
-
* EventBus. Call start() to resume.
|
|
43
|
-
*/
|
|
44
|
-
export class EventBusStoppedError extends EventError {
|
|
45
|
-
constructor(operation) {
|
|
46
|
-
super(`Cannot ${operation} on a stopped event bus. Call start() first.`, {
|
|
47
|
-
code: ErrorCode.LIFECYCLE_STATE,
|
|
48
|
-
statusCode: 500,
|
|
49
|
-
expose: false,
|
|
50
|
-
isOperational: true,
|
|
51
|
-
metadata: { operation },
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
28
|
//# sourceMappingURL=eventError.base.js.map
|
|
@@ -172,8 +172,15 @@ export declare function executeEventHandler<TEvent extends Event>(handler: Event
|
|
|
172
172
|
/**
|
|
173
173
|
* Executes a registered handler, applying its timeout when one
|
|
174
174
|
* is configured. A timed-out execution rejects with
|
|
175
|
-
* EventTimeoutError
|
|
176
|
-
*
|
|
175
|
+
* EventTimeoutError and aborts the `context.signal` the handler
|
|
176
|
+
* received (its `reason` is that EventTimeoutError), so a handler
|
|
177
|
+
* that observes the signal can stop its work. A handler that
|
|
178
|
+
* ignores the signal keeps running, but its eventual result is
|
|
179
|
+
* ignored.
|
|
180
|
+
*
|
|
181
|
+
* The handler's signal also follows the dispatch signal: aborting
|
|
182
|
+
* the publish aborts it too. Timing out one handler does not abort
|
|
183
|
+
* the dispatch or any other handler.
|
|
177
184
|
*/
|
|
178
185
|
export declare function executeRegisteredEventHandler<TEvent extends Event>(registration: RegisteredEventHandler<TEvent>, event: TEvent, context: EventHandlerContext<TEvent>): Promise<EventHandlerResult>;
|
|
179
186
|
/**
|
|
@@ -117,8 +117,15 @@ export async function executeEventHandler(handler, event, context) {
|
|
|
117
117
|
/**
|
|
118
118
|
* Executes a registered handler, applying its timeout when one
|
|
119
119
|
* is configured. A timed-out execution rejects with
|
|
120
|
-
* EventTimeoutError
|
|
121
|
-
*
|
|
120
|
+
* EventTimeoutError and aborts the `context.signal` the handler
|
|
121
|
+
* received (its `reason` is that EventTimeoutError), so a handler
|
|
122
|
+
* that observes the signal can stop its work. A handler that
|
|
123
|
+
* ignores the signal keeps running, but its eventual result is
|
|
124
|
+
* ignored.
|
|
125
|
+
*
|
|
126
|
+
* The handler's signal also follows the dispatch signal: aborting
|
|
127
|
+
* the publish aborts it too. Timing out one handler does not abort
|
|
128
|
+
* the dispatch or any other handler.
|
|
122
129
|
*/
|
|
123
130
|
export async function executeRegisteredEventHandler(registration, event, context) {
|
|
124
131
|
const timeoutMs = registration.timeoutMs;
|
|
@@ -126,17 +133,24 @@ export async function executeRegisteredEventHandler(registration, event, context
|
|
|
126
133
|
return executeEventHandler(registration.handler, event, context);
|
|
127
134
|
}
|
|
128
135
|
let timer;
|
|
136
|
+
const deadline = new AbortController();
|
|
137
|
+
const handlerContext = Object.freeze({
|
|
138
|
+
...context,
|
|
139
|
+
signal: AbortSignal.any([context.signal, deadline.signal]),
|
|
140
|
+
});
|
|
129
141
|
const timeout = new Promise((_resolve, reject) => {
|
|
130
142
|
timer = setTimeout(() => {
|
|
131
|
-
|
|
143
|
+
const error = new EventTimeoutError(timeoutMs, {
|
|
132
144
|
eventType: event.type,
|
|
133
145
|
eventId: event.id,
|
|
134
|
-
})
|
|
146
|
+
});
|
|
147
|
+
deadline.abort(error);
|
|
148
|
+
reject(error);
|
|
135
149
|
}, timeoutMs);
|
|
136
150
|
});
|
|
137
151
|
try {
|
|
138
152
|
return await Promise.race([
|
|
139
|
-
executeEventHandler(registration.handler, event,
|
|
153
|
+
executeEventHandler(registration.handler, event, handlerContext),
|
|
140
154
|
timeout,
|
|
141
155
|
]);
|
|
142
156
|
}
|
|
@@ -55,6 +55,10 @@ export declare function isValidEventType(value: unknown): value is EventType;
|
|
|
55
55
|
* Supported forms are an exact event type, a namespace wildcard
|
|
56
56
|
* ("user.*") and the catch-all "*". Wildcards in any other
|
|
57
57
|
* position ("user.*.created", "user.cre*") are rejected.
|
|
58
|
+
*
|
|
59
|
+
* A namespace wildcard is multi-level: "user.*" matches every event
|
|
60
|
+
* under "user" at any depth, and "user" itself (see matchesEventType).
|
|
61
|
+
* There is no single-level form.
|
|
58
62
|
*/
|
|
59
63
|
export declare function isValidEventTypePattern(value: unknown): value is EventTypePattern;
|
|
60
64
|
/**
|
|
@@ -112,8 +116,16 @@ export declare function normalizeEventTypePattern(pattern: string): EventTypePat
|
|
|
112
116
|
* "user.created" matches "user.*"
|
|
113
117
|
* "user" matches "user.*" (a namespace pattern also
|
|
114
118
|
* matches the bare namespace event)
|
|
119
|
+
* "user.profile.updated" matches "user.*" (multi-level: the
|
|
120
|
+
* wildcard covers every depth below the namespace)
|
|
115
121
|
* "user.created" matches "*"
|
|
116
122
|
* "order.created" does not match "user.*"
|
|
123
|
+
* "username.set" does not match "user.*" (segments, not prefixes)
|
|
124
|
+
*
|
|
125
|
+
* A "*" segment stands for the rest of the type, not for one segment,
|
|
126
|
+
* and there is no single-level wildcard. To handle only direct children,
|
|
127
|
+
* subscribe to "user.*" and check
|
|
128
|
+
* `getEventTypeSegments(event.type).length === 2` in the handler.
|
|
117
129
|
*
|
|
118
130
|
* Both arguments are expected to be normalized (see
|
|
119
131
|
* normalizeEventType / normalizeEventTypePattern); no
|
|
@@ -133,10 +145,13 @@ export declare function isChildEventType(type: EventType, parent: EventType): bo
|
|
|
133
145
|
/**
|
|
134
146
|
* Creates a wildcard pattern for an event namespace.
|
|
135
147
|
*
|
|
148
|
+
* The pattern is multi-level: it matches every event type under the
|
|
149
|
+
* namespace at any depth, and the bare namespace itself.
|
|
150
|
+
*
|
|
136
151
|
* Example:
|
|
137
152
|
*
|
|
138
153
|
* createEventTypePattern("user")
|
|
139
|
-
* → "user.*"
|
|
154
|
+
* → "user.*" (matches "user", "user.created", "user.profile.updated")
|
|
140
155
|
*/
|
|
141
156
|
export declare function createEventTypePattern(namespace: string): EventTypePattern;
|
|
142
157
|
/**
|
|
@@ -38,6 +38,10 @@ export function isValidEventType(value) {
|
|
|
38
38
|
* Supported forms are an exact event type, a namespace wildcard
|
|
39
39
|
* ("user.*") and the catch-all "*". Wildcards in any other
|
|
40
40
|
* position ("user.*.created", "user.cre*") are rejected.
|
|
41
|
+
*
|
|
42
|
+
* A namespace wildcard is multi-level: "user.*" matches every event
|
|
43
|
+
* under "user" at any depth, and "user" itself (see matchesEventType).
|
|
44
|
+
* There is no single-level form.
|
|
41
45
|
*/
|
|
42
46
|
export function isValidEventTypePattern(value) {
|
|
43
47
|
if (value === "*") {
|
|
@@ -164,8 +168,16 @@ export function normalizeEventTypePattern(pattern) {
|
|
|
164
168
|
* "user.created" matches "user.*"
|
|
165
169
|
* "user" matches "user.*" (a namespace pattern also
|
|
166
170
|
* matches the bare namespace event)
|
|
171
|
+
* "user.profile.updated" matches "user.*" (multi-level: the
|
|
172
|
+
* wildcard covers every depth below the namespace)
|
|
167
173
|
* "user.created" matches "*"
|
|
168
174
|
* "order.created" does not match "user.*"
|
|
175
|
+
* "username.set" does not match "user.*" (segments, not prefixes)
|
|
176
|
+
*
|
|
177
|
+
* A "*" segment stands for the rest of the type, not for one segment,
|
|
178
|
+
* and there is no single-level wildcard. To handle only direct children,
|
|
179
|
+
* subscribe to "user.*" and check
|
|
180
|
+
* `getEventTypeSegments(event.type).length === 2` in the handler.
|
|
169
181
|
*
|
|
170
182
|
* Both arguments are expected to be normalized (see
|
|
171
183
|
* normalizeEventType / normalizeEventTypePattern); no
|
|
@@ -201,10 +213,13 @@ export function isChildEventType(type, parent) {
|
|
|
201
213
|
/**
|
|
202
214
|
* Creates a wildcard pattern for an event namespace.
|
|
203
215
|
*
|
|
216
|
+
* The pattern is multi-level: it matches every event type under the
|
|
217
|
+
* namespace at any depth, and the bare namespace itself.
|
|
218
|
+
*
|
|
204
219
|
* Example:
|
|
205
220
|
*
|
|
206
221
|
* createEventTypePattern("user")
|
|
207
|
-
* → "user.*"
|
|
222
|
+
* → "user.*" (matches "user", "user.created", "user.profile.updated")
|
|
208
223
|
*/
|
|
209
224
|
export function createEventTypePattern(namespace) {
|
|
210
225
|
const normalized = normalizeEventType(namespace);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zudojs/events",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Event-driven architecture with event bus, emitter, middleware, and registry for decoupled communication.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -20,15 +20,15 @@
|
|
|
20
20
|
],
|
|
21
21
|
"sideEffects": false,
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@zudojs/constants": "1.1.
|
|
24
|
-
"@zudojs/errors": "1.
|
|
25
|
-
"@zudojs/middleware": "1.0
|
|
23
|
+
"@zudojs/constants": "1.1.2",
|
|
24
|
+
"@zudojs/errors": "1.3.0",
|
|
25
|
+
"@zudojs/middleware": "1.1.0"
|
|
26
26
|
},
|
|
27
27
|
"engines": {
|
|
28
28
|
"node": ">=24.0.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"vitest": "^
|
|
31
|
+
"vitest": "^5.0.1",
|
|
32
32
|
"typescript": "7.0.2"
|
|
33
33
|
},
|
|
34
34
|
"license": "MIT",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"event-bus",
|
|
46
46
|
"pubsub"
|
|
47
47
|
],
|
|
48
|
-
"homepage": "https://
|
|
48
|
+
"homepage": "https://zudojs.oyinlola.site/docs/packages-events",
|
|
49
49
|
"bugs": {
|
|
50
50
|
"url": "https://github.com/oyinlola-tech/zudo/issues"
|
|
51
51
|
},
|