@zudojs/events 1.1.0 → 1.3.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 +30 -0
- package/dist/eventBus/eventBus.core.d.ts +8 -3
- package/dist/eventBus/eventBus.core.js +22 -8
- package/dist/eventBus/eventBus.registration.d.ts +9 -2
- package/dist/eventBus/eventBus.registration.js +15 -1
- package/dist/eventBus/eventBus.type.d.ts +11 -1
- package/dist/eventEmitter/eventEmitter.core.js +1 -0
- 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 +7 -1
- package/dist/eventErrors/eventError.base.d.ts +5 -24
- package/dist/eventErrors/eventError.base.js +5 -46
- package/dist/eventErrors/eventWarning.helper.d.ts +33 -0
- package/dist/eventErrors/eventWarning.helper.js +49 -0
- package/dist/eventHandler/eventHandler.core.d.ts +9 -2
- package/dist/eventHandler/eventHandler.core.js +19 -5
- package/dist/eventRegistry/eventRegistry.lifecycle.d.ts +3 -1
- package/dist/eventRegistry/eventRegistry.lifecycle.js +7 -1
- package/dist/eventRegistry/eventRegistry.registration.d.ts +1 -0
- package/dist/eventRegistry/eventRegistry.registration.js +18 -6
- package/dist/eventRegistry/eventRegistry.store.js +3 -5
- package/dist/eventRegistry/eventRegistry.type.d.ts +10 -0
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -71,6 +71,9 @@ CREATED ──(first use / start)──▶ ACTIVE ◀──(start)── STOPPED
|
|
|
71
71
|
- `stop()` moves the bus to `STOPPED`; publishing or subscribing then throws
|
|
72
72
|
`EventBusStoppedError` until `start()` is called. Handlers and definitions are kept.
|
|
73
73
|
- `dispose()` is final; every operation throws `EventBusDisposedError`.
|
|
74
|
+
- Both errors are defined in `@zudojs/errors` (as `EventError` subclasses) and
|
|
75
|
+
re-exported from `@zudojs/events`, so `instanceof` works whichever package you
|
|
76
|
+
import them from.
|
|
74
77
|
|
|
75
78
|
## Publish results
|
|
76
79
|
|
|
@@ -90,6 +93,33 @@ In the default `CONTINUE` error mode handler failures are collected in
|
|
|
90
93
|
`EventHandlerError` rejects the publish. Errors thrown by a middleware itself are
|
|
91
94
|
wrapped as `EventMiddlewareError`; handler errors and aborts pass through unwrapped.
|
|
92
95
|
|
|
96
|
+
## Middleware
|
|
97
|
+
|
|
98
|
+
`bus.use()` accepts everything the `middleware` constructor option does: a
|
|
99
|
+
middleware function or `{ handle }` object, or a registered middleware from
|
|
100
|
+
`createEventMiddleware()` or a builder helper (`validateEventMiddleware`,
|
|
101
|
+
`beforeEvent`, `aroundEvent`, `timingEventMiddleware`, …). It returns a function
|
|
102
|
+
that removes the middleware again.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
const remove = bus.use(validateEventMiddleware((event) => event.payload != null));
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Timeouts and aborts
|
|
109
|
+
|
|
110
|
+
A handler registered with `timeoutMs` fails with `EventTimeoutError` when it
|
|
111
|
+
does not settle in time, and the `context.signal` it received is aborted with
|
|
112
|
+
that error as its `reason`, so a handler that listens to the signal can stop
|
|
113
|
+
its work. Timing out one handler does not abort the dispatch or other handlers.
|
|
114
|
+
|
|
115
|
+
Pass `signal` to `publish()` to abort a sequential dispatch. The publish rejects
|
|
116
|
+
with `EventDispatchAbortedError` (carrying the partial `results` and `errors`)
|
|
117
|
+
when the signal is aborted before a handler starts **or while any handler is
|
|
118
|
+
running — including the last or only one**, even if that handler then returns
|
|
119
|
+
normally. In `PARALLEL` mode every handler has already started, so only a
|
|
120
|
+
signal that is aborted before dispatch begins rejects; handlers can still
|
|
121
|
+
observe `context.signal`.
|
|
122
|
+
|
|
93
123
|
## Registry
|
|
94
124
|
|
|
95
125
|
`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
|
/**
|
|
@@ -7,6 +7,7 @@ import { EventErrorMode } from "../eventEmitter/eventEmitter.type.js";
|
|
|
7
7
|
import { EventRegistry } from "../eventRegistry/eventRegistry.store.js";
|
|
8
8
|
import { normalizeRegistryEventType } from "../eventRegistry/eventRegistry.registration.js";
|
|
9
9
|
import { EventBusDisposedError, EventBusStoppedError, EventError, toEventError, } from "../eventErrors/eventError.base.js";
|
|
10
|
+
import { warnObserverError } from "../eventErrors/eventWarning.helper.js";
|
|
10
11
|
import { EventBusState } from "./eventBus.type.js";
|
|
11
12
|
export { EventBusState } from "./eventBus.type.js";
|
|
12
13
|
import { busOn, busOnce, busOnAny, busOff, busUse, registerMiddlewareItem, } from "./eventBus.registration.js";
|
|
@@ -34,6 +35,7 @@ export class EventBus {
|
|
|
34
35
|
this.registry = new EventRegistry({
|
|
35
36
|
...options.registry,
|
|
36
37
|
maxHandlersPerPattern: options.emitter?.maxListeners,
|
|
38
|
+
enforceHandlerLimit: options.emitter?.enforceHandlerLimit,
|
|
37
39
|
onWarning: options.onWarning,
|
|
38
40
|
onError: options.onError
|
|
39
41
|
? (error, context) => options.onError?.(error, {
|
|
@@ -98,6 +100,11 @@ export class EventBus {
|
|
|
98
100
|
off(subscription) {
|
|
99
101
|
return busOff(this.emitter, subscription, () => this.ensureNotDisposed());
|
|
100
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
|
+
*/
|
|
101
108
|
use(middleware, options = {}) {
|
|
102
109
|
this.ensureNotDisposed();
|
|
103
110
|
return busUse(this.busMiddleware, middleware, options);
|
|
@@ -235,16 +242,23 @@ export class EventBus {
|
|
|
235
242
|
catch (error) {
|
|
236
243
|
/**
|
|
237
244
|
* Observers must never be able to break event bus
|
|
238
|
-
* operations; failures go to the onError hook
|
|
245
|
+
* operations; failures go to the onError hook, or to Node's
|
|
246
|
+
* process warning channel when no hook is configured, so a
|
|
247
|
+
* broken observer is never silently discarded.
|
|
239
248
|
*/
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
249
|
+
if (this.options.onError) {
|
|
250
|
+
try {
|
|
251
|
+
this.options.onError(error, {
|
|
252
|
+
source: "observer",
|
|
253
|
+
event: event.event,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
// Ignore failures of the error hook itself.
|
|
258
|
+
}
|
|
245
259
|
}
|
|
246
|
-
|
|
247
|
-
|
|
260
|
+
else {
|
|
261
|
+
warnObserverError(error, "An event bus observer", this.listeners);
|
|
248
262
|
}
|
|
249
263
|
}
|
|
250
264
|
}
|
|
@@ -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";
|
|
@@ -36,6 +37,11 @@ export interface EventBusOptions {
|
|
|
36
37
|
* emitted (0 disables). Defaults to 100.
|
|
37
38
|
*/
|
|
38
39
|
readonly maxListeners?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Refuse a registration that would exceed `maxListeners` instead of
|
|
42
|
+
* warning about it. Defaults to `false`.
|
|
43
|
+
*/
|
|
44
|
+
readonly enforceHandlerLimit?: boolean;
|
|
39
45
|
};
|
|
40
46
|
readonly registry?: {
|
|
41
47
|
readonly allowDuplicateDefinitions?: boolean;
|
|
@@ -88,7 +94,11 @@ export interface EventPublishResult<TEvent extends Event = Event> {
|
|
|
88
94
|
*/
|
|
89
95
|
readonly failed: number;
|
|
90
96
|
readonly results: readonly unknown[];
|
|
91
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Handler failures, each wrapped as EventHandlerError (its
|
|
99
|
+
* `cause` is the raw thrown value).
|
|
100
|
+
*/
|
|
101
|
+
readonly errors: readonly EventHandlerError[];
|
|
92
102
|
/**
|
|
93
103
|
* True when a middleware did not call next(), so no handler ran.
|
|
94
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";
|
|
@@ -42,6 +43,11 @@ export interface EventEmitterOptions {
|
|
|
42
43
|
* Defaults to 100.
|
|
43
44
|
*/
|
|
44
45
|
readonly maxListeners?: number;
|
|
46
|
+
/**
|
|
47
|
+
* Refuse a registration that would exceed `maxListeners` instead of warning
|
|
48
|
+
* about it. Only applies to the private store. Defaults to `false`.
|
|
49
|
+
*/
|
|
50
|
+
readonly enforceHandlerLimit?: boolean;
|
|
45
51
|
/**
|
|
46
52
|
* Receives leak warnings. Only applies to the private store.
|
|
47
53
|
* Defaults to `process.emitWarning` (type `ZudojsEventsWarning`).
|
|
@@ -82,7 +88,7 @@ export interface EventEmitResult<TEvent extends Event = Event> {
|
|
|
82
88
|
* Handler failures wrapped as EventHandlerError (cause holds the
|
|
83
89
|
* raw thrown value).
|
|
84
90
|
*/
|
|
85
|
-
readonly errors: readonly
|
|
91
|
+
readonly errors: readonly EventHandlerError[];
|
|
86
92
|
/**
|
|
87
93
|
* Number of handlers that completed successfully.
|
|
88
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, 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,24 +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
|
-
/**
|
|
49
|
-
* Error thrown when the listener limit of an emitter or registry
|
|
50
|
-
* is exceeded and the limit is configured to be enforced.
|
|
51
|
-
*/
|
|
52
|
-
export declare class EventListenerLimitExceededError extends EventError {
|
|
53
|
-
constructor(pattern: string, limit: number);
|
|
54
|
-
}
|
|
55
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, 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,46 +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
|
-
/**
|
|
56
|
-
* Error thrown when the listener limit of an emitter or registry
|
|
57
|
-
* is exceeded and the limit is configured to be enforced.
|
|
58
|
-
*/
|
|
59
|
-
export class EventListenerLimitExceededError extends EventError {
|
|
60
|
-
constructor(pattern, limit) {
|
|
61
|
-
super(`Listener limit of ${limit} exceeded for event pattern "${pattern}".`, {
|
|
62
|
-
code: ErrorCode.LIFECYCLE_STATE,
|
|
63
|
-
statusCode: 500,
|
|
64
|
-
expose: false,
|
|
65
|
-
metadata: { pattern, limit },
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
28
|
//# sourceMappingURL=eventError.base.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-level warning sink for @zudojs/events.
|
|
3
|
+
*
|
|
4
|
+
* @module eventErrors/eventWarning
|
|
5
|
+
*/
|
|
6
|
+
/** Warning code for a registry or emitter handler-limit breach. */
|
|
7
|
+
export declare const EVENT_HANDLER_LIMIT_WARNING_CODE = "ZUDOJS_EVENTS_HANDLER_LIMIT";
|
|
8
|
+
/** Warning code for a bus or registry observer that threw. */
|
|
9
|
+
export declare const EVENT_OBSERVER_ERROR_WARNING_CODE = "ZUDOJS_EVENTS_OBSERVER_ERROR";
|
|
10
|
+
/**
|
|
11
|
+
* Emits a diagnostic on Node's process warning channel — the same one
|
|
12
|
+
* `EventEmitter` uses for `MaxListenersExceededWarning`, so it honours
|
|
13
|
+
* `--no-warnings` and `process.on("warning")` instead of writing to the
|
|
14
|
+
* console. A no-op where `process.emitWarning` is unavailable.
|
|
15
|
+
*
|
|
16
|
+
* @param message - The warning text, emitted with a package prefix.
|
|
17
|
+
* @param code - The machine-readable warning code.
|
|
18
|
+
*/
|
|
19
|
+
export declare function emitEventsWarning(message: string, code: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* Default sink for an observer that threw: with no `onError` hook
|
|
22
|
+
* configured the failure would otherwise be swallowed entirely.
|
|
23
|
+
*
|
|
24
|
+
* Emitted at most once per scope, the way the handler-limit warning is
|
|
25
|
+
* emitted at most once per pattern, so a broken observer on a busy bus
|
|
26
|
+
* reports itself without flooding the warning channel.
|
|
27
|
+
*
|
|
28
|
+
* @param error - The value the observer threw.
|
|
29
|
+
* @param source - A short description of which observer channel failed.
|
|
30
|
+
* @param scope - The bus or registry the observer belongs to.
|
|
31
|
+
*/
|
|
32
|
+
export declare function warnObserverError(error: unknown, source: string, scope: object): void;
|
|
33
|
+
//# sourceMappingURL=eventWarning.helper.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-level warning sink for @zudojs/events.
|
|
3
|
+
*
|
|
4
|
+
* @module eventErrors/eventWarning
|
|
5
|
+
*/
|
|
6
|
+
/** Warning code for a registry or emitter handler-limit breach. */
|
|
7
|
+
export const EVENT_HANDLER_LIMIT_WARNING_CODE = "ZUDOJS_EVENTS_HANDLER_LIMIT";
|
|
8
|
+
/** Warning code for a bus or registry observer that threw. */
|
|
9
|
+
export const EVENT_OBSERVER_ERROR_WARNING_CODE = "ZUDOJS_EVENTS_OBSERVER_ERROR";
|
|
10
|
+
/**
|
|
11
|
+
* Emits a diagnostic on Node's process warning channel — the same one
|
|
12
|
+
* `EventEmitter` uses for `MaxListenersExceededWarning`, so it honours
|
|
13
|
+
* `--no-warnings` and `process.on("warning")` instead of writing to the
|
|
14
|
+
* console. A no-op where `process.emitWarning` is unavailable.
|
|
15
|
+
*
|
|
16
|
+
* @param message - The warning text, emitted with a package prefix.
|
|
17
|
+
* @param code - The machine-readable warning code.
|
|
18
|
+
*/
|
|
19
|
+
export function emitEventsWarning(message, code) {
|
|
20
|
+
const emit = globalThis.process?.emitWarning;
|
|
21
|
+
emit?.(`[@zudojs/events] ${message}`, {
|
|
22
|
+
type: "ZudojsEventsWarning",
|
|
23
|
+
code,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/** Scopes that have already reported a failing observer. */
|
|
27
|
+
const warnedObserverScopes = new WeakSet();
|
|
28
|
+
/**
|
|
29
|
+
* Default sink for an observer that threw: with no `onError` hook
|
|
30
|
+
* configured the failure would otherwise be swallowed entirely.
|
|
31
|
+
*
|
|
32
|
+
* Emitted at most once per scope, the way the handler-limit warning is
|
|
33
|
+
* emitted at most once per pattern, so a broken observer on a busy bus
|
|
34
|
+
* reports itself without flooding the warning channel.
|
|
35
|
+
*
|
|
36
|
+
* @param error - The value the observer threw.
|
|
37
|
+
* @param source - A short description of which observer channel failed.
|
|
38
|
+
* @param scope - The bus or registry the observer belongs to.
|
|
39
|
+
*/
|
|
40
|
+
export function warnObserverError(error, source, scope) {
|
|
41
|
+
if (warnedObserverScopes.has(scope))
|
|
42
|
+
return;
|
|
43
|
+
warnedObserverScopes.add(scope);
|
|
44
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
45
|
+
emitEventsWarning(`${source} threw and was ignored: ${detail}. ` +
|
|
46
|
+
"Configure the `onError` option to handle observer failures. " +
|
|
47
|
+
"This warning is emitted once.", EVENT_OBSERVER_ERROR_WARNING_CODE);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=eventWarning.helper.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
|
}
|
|
@@ -18,7 +18,9 @@ export declare function registryDispose(disposed: boolean, definitions: Map<Even
|
|
|
18
18
|
* Notifies registry listeners.
|
|
19
19
|
*
|
|
20
20
|
* Observer failures never break registry mutations; they are
|
|
21
|
-
* forwarded to the `onError` hook when one is configured
|
|
21
|
+
* forwarded to the `onError` hook when one is configured, and to
|
|
22
|
+
* Node's process warning channel when one is not, so a broken
|
|
23
|
+
* observer is never silently discarded.
|
|
22
24
|
*/
|
|
23
25
|
export declare function registryNotify(change: EventRegistryChange, listeners: Set<EventRegistryListener>, onError?: (error: unknown, context: EventRegistryErrorContext) => void): void;
|
|
24
26
|
//# sourceMappingURL=eventRegistry.lifecycle.d.ts.map
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Event registry lifecycle methods for Zudojs.
|
|
3
3
|
*/
|
|
4
4
|
import { registryUnregister } from "./eventRegistry.registration.js";
|
|
5
|
+
import { warnObserverError } from "../eventErrors/eventWarning.helper.js";
|
|
5
6
|
/**
|
|
6
7
|
* Clears all handlers and definitions from the registry.
|
|
7
8
|
*
|
|
@@ -34,7 +35,9 @@ export function registryDispose(disposed, definitions, handlers, listeners, ensu
|
|
|
34
35
|
* Notifies registry listeners.
|
|
35
36
|
*
|
|
36
37
|
* Observer failures never break registry mutations; they are
|
|
37
|
-
* forwarded to the `onError` hook when one is configured
|
|
38
|
+
* forwarded to the `onError` hook when one is configured, and to
|
|
39
|
+
* Node's process warning channel when one is not, so a broken
|
|
40
|
+
* observer is never silently discarded.
|
|
38
41
|
*/
|
|
39
42
|
export function registryNotify(change, listeners, onError) {
|
|
40
43
|
for (const listener of listeners) {
|
|
@@ -52,6 +55,9 @@ export function registryNotify(change, listeners, onError) {
|
|
|
52
55
|
*/
|
|
53
56
|
}
|
|
54
57
|
}
|
|
58
|
+
else {
|
|
59
|
+
warnObserverError(error, "An event registry observer", listeners);
|
|
60
|
+
}
|
|
55
61
|
}
|
|
56
62
|
}
|
|
57
63
|
}
|
|
@@ -23,6 +23,7 @@ export declare function registryRegister<TType extends EventType, TPayload>(defi
|
|
|
23
23
|
export declare function registryRegisterHandler<TEvent extends Event = Event>(eventType: EventTypePattern, handler: EventHandlerLike<TEvent>, handlerOptions: Omit<EventHandlerOptions, "eventType">, handlers: Map<string, EventHandlerEntry>, options: {
|
|
24
24
|
onDuplicateHandlerId: DuplicateHandlerIdPolicy;
|
|
25
25
|
maxHandlersPerPattern: number;
|
|
26
|
+
enforceHandlerLimit?: boolean;
|
|
26
27
|
onWarning: (warning: EventRegistryWarning) => void;
|
|
27
28
|
}, ensureActive: () => void, notify: (change: EventRegistryChange) => void, warnedPatterns: Set<string>): EventSubscription;
|
|
28
29
|
/**
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { normalizeEventType } from "../eventTypes/eventType.type.js";
|
|
5
5
|
import { createEventHandler } from "../eventHandler/eventHandler.core.js";
|
|
6
6
|
import { createEventSubscription } from "../eventSubscription/eventSubscription.core.js";
|
|
7
|
-
import { DuplicateEventDefinitionError, DuplicateEventHandlerError, InvalidEventError, } from "../eventErrors/eventError.base.js";
|
|
7
|
+
import { DuplicateEventDefinitionError, DuplicateEventHandlerError, EventListenerLimitExceededError, InvalidEventError, } from "../eventErrors/eventError.base.js";
|
|
8
8
|
import { EventRegistryChangeType } from "./eventRegistry.type.js";
|
|
9
9
|
/**
|
|
10
10
|
* Normalizes an event type for registry lookups, converting
|
|
@@ -104,7 +104,7 @@ export function registryRegisterHandler(eventType, handler, handlerOptions, hand
|
|
|
104
104
|
description: registration.description,
|
|
105
105
|
});
|
|
106
106
|
handlers.set(registration.id, { registration, subscription });
|
|
107
|
-
checkHandlerLimit(registration.eventType, handlers, options.maxHandlersPerPattern, options.onWarning, warnedPatterns);
|
|
107
|
+
checkHandlerLimit(registration.eventType, handlers, options.maxHandlersPerPattern, options.onWarning, warnedPatterns, options.enforceHandlerLimit === true, registration.id);
|
|
108
108
|
notify({
|
|
109
109
|
type: EventRegistryChangeType.HANDLER_REGISTERED,
|
|
110
110
|
eventType: registration.eventType,
|
|
@@ -114,11 +114,19 @@ export function registryRegisterHandler(eventType, handler, handlerOptions, hand
|
|
|
114
114
|
return subscription;
|
|
115
115
|
}
|
|
116
116
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
117
|
+
* Reports a pattern whose handler count has passed the configured limit.
|
|
118
|
+
*
|
|
119
|
+
* Warns once per pattern by default. Under `enforceHandlerLimit` it instead
|
|
120
|
+
* removes the handler just registered and throws
|
|
121
|
+
* {@link EventListenerLimitExceededError}, so a refused registration leaves
|
|
122
|
+
* the registry exactly as it was — and it throws on every breach, not only
|
|
123
|
+
* the first, because each one is a separate fault.
|
|
119
124
|
*/
|
|
120
|
-
function checkHandlerLimit(pattern, handlers, limit, onWarning, warnedPatterns) {
|
|
121
|
-
if (limit <= 0
|
|
125
|
+
function checkHandlerLimit(pattern, handlers, limit, onWarning, warnedPatterns, enforce, registrationId) {
|
|
126
|
+
if (limit <= 0) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (!enforce && warnedPatterns.has(pattern)) {
|
|
122
130
|
return;
|
|
123
131
|
}
|
|
124
132
|
let count = 0;
|
|
@@ -130,6 +138,10 @@ function checkHandlerLimit(pattern, handlers, limit, onWarning, warnedPatterns)
|
|
|
130
138
|
if (count <= limit) {
|
|
131
139
|
return;
|
|
132
140
|
}
|
|
141
|
+
if (enforce) {
|
|
142
|
+
handlers.delete(registrationId);
|
|
143
|
+
throw new EventListenerLimitExceededError(pattern, count, limit);
|
|
144
|
+
}
|
|
133
145
|
warnedPatterns.add(pattern);
|
|
134
146
|
const warning = {
|
|
135
147
|
type: "handler.limit",
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* higher-level routing belongs to EventBus.
|
|
8
8
|
*/
|
|
9
9
|
import { DuplicateEventDefinitionError, EventDefinitionNotFoundError, DuplicateEventHandlerError, EventHandlerNotFoundError, EventRegistryDisposedError, } from "../eventErrors/eventError.base.js";
|
|
10
|
+
import { EVENT_HANDLER_LIMIT_WARNING_CODE, emitEventsWarning, } from "../eventErrors/eventWarning.helper.js";
|
|
10
11
|
import { DEFAULT_MAX_HANDLERS_PER_PATTERN } from "./eventRegistry.type.js";
|
|
11
12
|
import { normalizeRegistryEventType, registryRegister, registryRegisterHandler, registryUnregister, registryUnregisterHandler, } from "./eventRegistry.registration.js";
|
|
12
13
|
import { getHandlersForEvent, getHandlersForType, getAllDefinitions, getAllHandlers, } from "./eventRegistry.queries.js";
|
|
@@ -19,11 +20,7 @@ export { DuplicateEventDefinitionError, EventDefinitionNotFoundError, DuplicateE
|
|
|
19
20
|
* console directly. A no-op where `process.emitWarning` is unavailable.
|
|
20
21
|
*/
|
|
21
22
|
function defaultWarning(warning) {
|
|
22
|
-
|
|
23
|
-
emit?.(`[@zudojs/events] ${warning.message}`, {
|
|
24
|
-
type: "ZudojsEventsWarning",
|
|
25
|
-
code: "ZUDOJS_EVENTS_HANDLER_LIMIT",
|
|
26
|
-
});
|
|
23
|
+
emitEventsWarning(warning.message, EVENT_HANDLER_LIMIT_WARNING_CODE);
|
|
27
24
|
}
|
|
28
25
|
/**
|
|
29
26
|
* Main event registry.
|
|
@@ -47,6 +44,7 @@ export class EventRegistry {
|
|
|
47
44
|
onDuplicateHandlerId: options.onDuplicateHandlerId ??
|
|
48
45
|
(options.allowDuplicateHandlerIds ? "replace" : "throw"),
|
|
49
46
|
maxHandlersPerPattern,
|
|
47
|
+
enforceHandlerLimit: options.enforceHandlerLimit ?? false,
|
|
50
48
|
onWarning: options.onWarning ?? defaultWarning,
|
|
51
49
|
onError: options.onError,
|
|
52
50
|
};
|
|
@@ -59,6 +59,16 @@ export interface EventRegistryOptions {
|
|
|
59
59
|
* disable. Defaults to 100.
|
|
60
60
|
*/
|
|
61
61
|
readonly maxHandlersPerPattern?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Refuse a registration that would exceed `maxHandlersPerPattern` instead of
|
|
64
|
+
* warning about it. Defaults to `false`.
|
|
65
|
+
*
|
|
66
|
+
* The default is a one-shot warning, which reports a suspected leak without
|
|
67
|
+
* interrupting a working application. Set this where a breached limit is a
|
|
68
|
+
* fault you would rather fail on: `register` then throws
|
|
69
|
+
* `EventListenerLimitExceededError` and the handler is not registered.
|
|
70
|
+
*/
|
|
71
|
+
readonly enforceHandlerLimit?: boolean;
|
|
62
72
|
/**
|
|
63
73
|
* Receives limit warnings. Defaults to `process.emitWarning` (type `ZudojsEventsWarning`).
|
|
64
74
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zudojs/events",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
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
|
},
|