@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Event bus registration and subscription methods for Zudojs.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { createEventMiddleware, isEventMiddleware, } from "../eventMiddleware/eventMiddleware.helper.js";
|
|
5
5
|
/**
|
|
6
6
|
* Registers an event definition on the given registry.
|
|
7
7
|
*/
|
|
@@ -39,17 +39,12 @@ export function busOff(emitter, subscription, ensureNotDisposed) {
|
|
|
39
39
|
return emitter.off(subscription);
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
* Adds middleware to the bus.
|
|
42
|
+
* Adds middleware to the bus. The middleware is validated
|
|
43
|
+
* eagerly (invalid middleware or a non-finite priority throw
|
|
44
|
+
* here, not on the next publish).
|
|
43
45
|
*/
|
|
44
46
|
export function busUse(busMiddleware, middleware, options = {}) {
|
|
45
|
-
const
|
|
46
|
-
const registered = {
|
|
47
|
-
id,
|
|
48
|
-
description: options.description,
|
|
49
|
-
priority: options.priority ?? 0,
|
|
50
|
-
enabled: options.enabled ?? true,
|
|
51
|
-
middleware,
|
|
52
|
-
};
|
|
47
|
+
const registered = createEventMiddleware(middleware, options);
|
|
53
48
|
busMiddleware.push(registered);
|
|
54
49
|
return () => {
|
|
55
50
|
const idx = busMiddleware.indexOf(registered);
|
|
@@ -58,27 +53,35 @@ export function busUse(busMiddleware, middleware, options = {}) {
|
|
|
58
53
|
}
|
|
59
54
|
};
|
|
60
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Determines whether a value is an already registered middleware
|
|
58
|
+
* (created by createEventMiddleware or a builder helper).
|
|
59
|
+
*/
|
|
60
|
+
export function isRegisteredEventMiddleware(value) {
|
|
61
|
+
return (typeof value === "object" &&
|
|
62
|
+
value !== null &&
|
|
63
|
+
typeof value.id === "string" &&
|
|
64
|
+
typeof value.enabled === "boolean" &&
|
|
65
|
+
isEventMiddleware(value.middleware));
|
|
66
|
+
}
|
|
61
67
|
/**
|
|
62
68
|
* Normalizes a middleware item into a RegisteredEventMiddleware.
|
|
69
|
+
*
|
|
70
|
+
* `prefix` keeps generated ids stable per source ("bus-mw" for
|
|
71
|
+
* constructor middleware, "publish-mw" for per-publication
|
|
72
|
+
* middleware).
|
|
63
73
|
*/
|
|
64
|
-
export function registerMiddlewareItem(item, index) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
priority:
|
|
70
|
-
enabled:
|
|
71
|
-
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
if (isObjectEventMiddleware(item)) {
|
|
75
|
-
return {
|
|
76
|
-
id,
|
|
77
|
-
priority: 0,
|
|
78
|
-
enabled: true,
|
|
79
|
-
middleware: item,
|
|
80
|
-
};
|
|
74
|
+
export function registerMiddlewareItem(item, index, prefix = "bus-mw") {
|
|
75
|
+
if (isRegisteredEventMiddleware(item)) {
|
|
76
|
+
return createEventMiddleware(item.middleware, {
|
|
77
|
+
id: item.id,
|
|
78
|
+
description: item.description,
|
|
79
|
+
priority: item.priority,
|
|
80
|
+
enabled: item.enabled,
|
|
81
|
+
});
|
|
81
82
|
}
|
|
82
|
-
return item
|
|
83
|
+
return createEventMiddleware(item, {
|
|
84
|
+
id: `${prefix}-${index}`,
|
|
85
|
+
});
|
|
83
86
|
}
|
|
84
87
|
//# sourceMappingURL=eventBus.registration.js.map
|
|
@@ -3,33 +3,96 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { Event } from "../eventTypes/eventDefinition.type.js";
|
|
5
5
|
import type { EventEmitterMode, EventErrorMode } from "../eventEmitter/eventEmitter.type.js";
|
|
6
|
-
import type { EventMiddlewareLike } from "../eventMiddleware/eventMiddleware.type.js";
|
|
6
|
+
import type { EventMiddlewareLike, RegisteredEventMiddleware } from "../eventMiddleware/eventMiddleware.type.js";
|
|
7
|
+
import type { DuplicateHandlerIdPolicy, EventRegistryWarning } from "../eventRegistry/eventRegistry.type.js";
|
|
8
|
+
/**
|
|
9
|
+
* Middleware accepted by bus options: a plain middleware function
|
|
10
|
+
* or object, or a registered middleware created with
|
|
11
|
+
* createEventMiddleware() / the builder helpers.
|
|
12
|
+
*/
|
|
13
|
+
export type EventBusMiddlewareItem = EventMiddlewareLike | RegisteredEventMiddleware;
|
|
14
|
+
/**
|
|
15
|
+
* Context passed to the bus `onError` hook.
|
|
16
|
+
*/
|
|
17
|
+
export interface EventBusErrorContext {
|
|
18
|
+
/**
|
|
19
|
+
* "handler" → a handler failed during a CONTINUE-mode publish
|
|
20
|
+
* "observer" → a bus lifecycle observer threw
|
|
21
|
+
*/
|
|
22
|
+
readonly source: "handler" | "observer";
|
|
23
|
+
readonly event?: Event;
|
|
24
|
+
}
|
|
7
25
|
export interface EventBusOptions {
|
|
8
26
|
readonly emitter?: {
|
|
9
27
|
readonly mode?: EventEmitterMode;
|
|
10
28
|
readonly errorMode?: EventErrorMode;
|
|
29
|
+
/**
|
|
30
|
+
* Deep-freeze events (payload included) before dispatch.
|
|
31
|
+
* Defaults to true.
|
|
32
|
+
*/
|
|
11
33
|
readonly freezeEvents?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Maximum handlers per event pattern before a leak warning is
|
|
36
|
+
* emitted (0 disables). Defaults to 100.
|
|
37
|
+
*/
|
|
38
|
+
readonly maxListeners?: number;
|
|
12
39
|
};
|
|
13
40
|
readonly registry?: {
|
|
14
41
|
readonly allowDuplicateDefinitions?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* @deprecated Use `onDuplicateHandlerId: "replace"`.
|
|
44
|
+
*/
|
|
15
45
|
readonly allowDuplicateHandlerIds?: boolean;
|
|
46
|
+
readonly onDuplicateHandlerId?: DuplicateHandlerIdPolicy;
|
|
16
47
|
};
|
|
48
|
+
/**
|
|
49
|
+
* Reject publishes for event types that were not registered
|
|
50
|
+
* with register(). Defaults to false.
|
|
51
|
+
*/
|
|
17
52
|
readonly requireRegistration?: boolean;
|
|
18
|
-
readonly middleware?: readonly
|
|
53
|
+
readonly middleware?: readonly EventBusMiddlewareItem[];
|
|
54
|
+
/**
|
|
55
|
+
* Receives leak warnings. Defaults to console.warn.
|
|
56
|
+
*/
|
|
57
|
+
readonly onWarning?: (warning: EventRegistryWarning) => void;
|
|
58
|
+
/**
|
|
59
|
+
* Receives handler errors from CONTINUE-mode publishes and
|
|
60
|
+
* errors thrown by bus/registry observers. Useful for
|
|
61
|
+
* fire-and-forget publishes whose result is never inspected.
|
|
62
|
+
*/
|
|
63
|
+
readonly onError?: (error: unknown, context: EventBusErrorContext) => void;
|
|
19
64
|
}
|
|
20
65
|
export interface PublishOptions {
|
|
21
66
|
readonly mode?: EventEmitterMode;
|
|
22
67
|
readonly errorMode?: EventErrorMode;
|
|
23
68
|
readonly signal?: AbortSignal;
|
|
24
69
|
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
25
|
-
readonly middleware?: readonly
|
|
70
|
+
readonly middleware?: readonly EventBusMiddlewareItem[];
|
|
26
71
|
}
|
|
27
72
|
export interface EventPublishResult<TEvent extends Event = Event> {
|
|
28
73
|
readonly event: TEvent;
|
|
74
|
+
/**
|
|
75
|
+
* True when at least one handler completed successfully.
|
|
76
|
+
*/
|
|
29
77
|
readonly handled: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Number of handlers invoked.
|
|
80
|
+
*/
|
|
30
81
|
readonly handlerCount: number;
|
|
82
|
+
/**
|
|
83
|
+
* Number of handlers that completed successfully.
|
|
84
|
+
*/
|
|
85
|
+
readonly succeeded: number;
|
|
86
|
+
/**
|
|
87
|
+
* Number of handlers that threw.
|
|
88
|
+
*/
|
|
89
|
+
readonly failed: number;
|
|
31
90
|
readonly results: readonly unknown[];
|
|
32
91
|
readonly errors: readonly unknown[];
|
|
92
|
+
/**
|
|
93
|
+
* True when a middleware did not call next(), so no handler ran.
|
|
94
|
+
*/
|
|
95
|
+
readonly shortCircuited: boolean;
|
|
33
96
|
readonly middlewareExecutions?: readonly {
|
|
34
97
|
readonly middlewareId: string;
|
|
35
98
|
readonly result: unknown;
|
|
@@ -37,8 +100,17 @@ export interface EventPublishResult<TEvent extends Event = Event> {
|
|
|
37
100
|
}[];
|
|
38
101
|
}
|
|
39
102
|
export declare enum EventBusState {
|
|
103
|
+
/**
|
|
104
|
+
* Created, never started. The first publish/subscribe starts
|
|
105
|
+
* the bus automatically.
|
|
106
|
+
*/
|
|
40
107
|
CREATED = "created",
|
|
41
108
|
ACTIVE = "active",
|
|
109
|
+
/**
|
|
110
|
+
* Stopped with stop(). Publishing and subscribing throw
|
|
111
|
+
* EventBusStoppedError until start() is called again.
|
|
112
|
+
*/
|
|
113
|
+
STOPPED = "stopped",
|
|
42
114
|
DISPOSED = "disposed"
|
|
43
115
|
}
|
|
44
116
|
export interface EventBusEvent {
|
|
@@ -3,8 +3,17 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export var EventBusState;
|
|
5
5
|
(function (EventBusState) {
|
|
6
|
+
/**
|
|
7
|
+
* Created, never started. The first publish/subscribe starts
|
|
8
|
+
* the bus automatically.
|
|
9
|
+
*/
|
|
6
10
|
EventBusState["CREATED"] = "created";
|
|
7
11
|
EventBusState["ACTIVE"] = "active";
|
|
12
|
+
/**
|
|
13
|
+
* Stopped with stop(). Publishing and subscribing throw
|
|
14
|
+
* EventBusStoppedError until start() is called again.
|
|
15
|
+
*/
|
|
16
|
+
EventBusState["STOPPED"] = "stopped";
|
|
8
17
|
EventBusState["DISPOSED"] = "disposed";
|
|
9
18
|
})(EventBusState || (EventBusState = {}));
|
|
10
19
|
//# sourceMappingURL=eventBus.type.js.map
|
package/dist/eventBus/index.d.ts
CHANGED
|
@@ -6,4 +6,6 @@
|
|
|
6
6
|
export * from "./eventBus.type.js";
|
|
7
7
|
export * from "./eventBus.core.js";
|
|
8
8
|
export * from "./eventBus.factory.js";
|
|
9
|
+
export { isRegisteredEventMiddleware } from "./eventBus.registration.js";
|
|
10
|
+
export { isEventEmitResult } from "./eventBus.publish.js";
|
|
9
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/eventBus/index.js
CHANGED
|
@@ -6,4 +6,6 @@
|
|
|
6
6
|
export * from "./eventBus.type.js";
|
|
7
7
|
export * from "./eventBus.core.js";
|
|
8
8
|
export * from "./eventBus.factory.js";
|
|
9
|
+
export { isRegisteredEventMiddleware } from "./eventBus.registration.js";
|
|
10
|
+
export { isEventEmitResult } from "./eventBus.publish.js";
|
|
9
11
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Event emitter abort error helper for Zudojs.
|
|
3
3
|
*/
|
|
4
|
+
import type { Event } from "../eventTypes/eventDefinition.type.js";
|
|
5
|
+
import { EventDispatchAbortedError } from "../eventErrors/eventError.base.js";
|
|
6
|
+
import type { EventHandlerExecutionResult } from "./eventEmitter.type.js";
|
|
4
7
|
/**
|
|
5
|
-
* Creates
|
|
8
|
+
* Creates the error thrown when dispatch is aborted through an
|
|
9
|
+
* AbortSignal. Partial results and errors gathered before the
|
|
10
|
+
* abort are attached to the error.
|
|
6
11
|
*/
|
|
7
|
-
export declare function createAbortError():
|
|
12
|
+
export declare function createAbortError(event?: Event, results?: readonly EventHandlerExecutionResult[], errors?: readonly unknown[]): EventDispatchAbortedError;
|
|
13
|
+
/**
|
|
14
|
+
* Determines whether an error represents an aborted dispatch.
|
|
15
|
+
*/
|
|
16
|
+
export declare function isAbortError(error: unknown): boolean;
|
|
8
17
|
//# sourceMappingURL=eventEmitter.abort.d.ts.map
|
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Event emitter abort error helper for Zudojs.
|
|
3
3
|
*/
|
|
4
|
+
import { EventDispatchAbortedError } from "../eventErrors/eventError.base.js";
|
|
4
5
|
/**
|
|
5
|
-
* Creates
|
|
6
|
+
* Creates the error thrown when dispatch is aborted through an
|
|
7
|
+
* AbortSignal. Partial results and errors gathered before the
|
|
8
|
+
* abort are attached to the error.
|
|
6
9
|
*/
|
|
7
|
-
export function createAbortError() {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
export function createAbortError(event, results = [], errors = []) {
|
|
11
|
+
return new EventDispatchAbortedError("Event dispatch was aborted.", {
|
|
12
|
+
eventType: event?.type,
|
|
13
|
+
eventId: event?.id,
|
|
14
|
+
results,
|
|
15
|
+
errors,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Determines whether an error represents an aborted dispatch.
|
|
20
|
+
*/
|
|
21
|
+
export function isAbortError(error) {
|
|
22
|
+
return (error instanceof EventDispatchAbortedError ||
|
|
23
|
+
(typeof error === "object" &&
|
|
24
|
+
error !== null &&
|
|
25
|
+
error.name === "AbortError"));
|
|
11
26
|
}
|
|
12
27
|
//# sourceMappingURL=eventEmitter.abort.js.map
|
|
@@ -1,34 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Event emitter core class for Zudojs.
|
|
3
|
+
*
|
|
4
|
+
* The emitter dispatches events to handlers. Handlers are stored
|
|
5
|
+
* in an EventHandlerStore (an EventRegistry by default) so that a
|
|
6
|
+
* bus and its registry share one set of handlers.
|
|
3
7
|
*/
|
|
4
8
|
import type { Event, EventInput } from "../eventTypes/eventDefinition.type.js";
|
|
5
9
|
import type { EventTypePattern } from "../eventTypes/eventType.type.js";
|
|
6
10
|
import type { EventHandlerLike, EventHandlerOptions, RegisteredEventHandler } from "../eventHandler/eventHandler.core.js";
|
|
7
11
|
import type { EventSubscription } from "../eventSubscription/eventSubscription.core.js";
|
|
8
12
|
import { EventSubscriptionGroup } from "../eventSubscription/eventSubscription.core.js";
|
|
13
|
+
import type { EventHandlerStore } from "../eventRegistry/eventRegistry.type.js";
|
|
9
14
|
import type { EventEmitterOptions, EmitOptions, EventEmitResult } from "./eventEmitter.type.js";
|
|
10
15
|
/**
|
|
11
16
|
* Main local event emitter.
|
|
12
17
|
*/
|
|
13
18
|
export declare class EventEmitter {
|
|
14
|
-
private readonly
|
|
19
|
+
private readonly store;
|
|
15
20
|
private readonly options;
|
|
16
21
|
private disposed;
|
|
17
22
|
constructor(options?: EventEmitterOptions);
|
|
18
23
|
on<TEvent extends Event = Event>(eventType: EventTypePattern, handler: EventHandlerLike<TEvent>, options?: Omit<EventHandlerOptions, "eventType">): EventSubscription;
|
|
19
24
|
once<TEvent extends Event = Event>(eventType: EventTypePattern, handler: EventHandlerLike<TEvent>, options?: Omit<EventHandlerOptions, "eventType" | "once">): EventSubscription;
|
|
20
25
|
onAny<TEvent extends Event = Event>(handler: EventHandlerLike<TEvent>, options?: Omit<EventHandlerOptions, "eventType">): EventSubscription;
|
|
26
|
+
/**
|
|
27
|
+
* Cancels a subscription. Returns true when the subscription
|
|
28
|
+
* was active (and is now cancelled).
|
|
29
|
+
*/
|
|
21
30
|
off(subscription: EventSubscription): boolean;
|
|
22
31
|
emit<TEvent extends Event>(event: TEvent, options?: EmitOptions): Promise<EventEmitResult<TEvent>>;
|
|
23
32
|
emitEvent<TPayload>(input: EventInput<TPayload>, options?: EmitOptions): Promise<EventEmitResult<Event<TPayload>>>;
|
|
24
33
|
get listenerCount(): number;
|
|
34
|
+
/**
|
|
35
|
+
* Returns the handler store backing this emitter.
|
|
36
|
+
*/
|
|
37
|
+
getStore(): EventHandlerStore;
|
|
25
38
|
getRegistrations(): readonly RegisteredEventHandler[];
|
|
26
39
|
removeAllListeners(): void;
|
|
27
40
|
createSubscriptionGroup(): EventSubscriptionGroup;
|
|
28
41
|
dispose(): void;
|
|
29
42
|
isDisposed(): boolean;
|
|
30
|
-
private addRegistration;
|
|
31
|
-
private removeHandler;
|
|
32
43
|
private ensureActive;
|
|
33
44
|
}
|
|
34
45
|
/**
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Event emitter core class for Zudojs.
|
|
3
|
+
*
|
|
4
|
+
* The emitter dispatches events to handlers. Handlers are stored
|
|
5
|
+
* in an EventHandlerStore (an EventRegistry by default) so that a
|
|
6
|
+
* bus and its registry share one set of handlers.
|
|
3
7
|
*/
|
|
4
|
-
import { createEvent } from "../eventTypes/eventDefinition.type.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
8
|
+
import { createEvent, isEvent } from "../eventTypes/eventDefinition.type.js";
|
|
9
|
+
import { deepFreeze } from "../eventTypes/eventPayload.type.js";
|
|
10
|
+
import { createEventHandlerContext } from "../eventHandler/eventHandler.core.js";
|
|
11
|
+
import { EventSubscriptionGroup } from "../eventSubscription/eventSubscription.core.js";
|
|
12
|
+
import { EventRegistry } from "../eventRegistry/eventRegistry.store.js";
|
|
13
|
+
import { EventEmitterDisposedError, InvalidEventError, } from "../eventErrors/eventError.base.js";
|
|
7
14
|
import { EventEmitterMode, EventErrorMode } from "./eventEmitter.type.js";
|
|
8
15
|
import { emitSequential } from "./eventEmitter.sequential.js";
|
|
9
16
|
import { emitParallel } from "./eventEmitter.parallel.js";
|
|
@@ -12,7 +19,7 @@ import { createAbortError } from "./eventEmitter.abort.js";
|
|
|
12
19
|
* Main local event emitter.
|
|
13
20
|
*/
|
|
14
21
|
export class EventEmitter {
|
|
15
|
-
|
|
22
|
+
store;
|
|
16
23
|
options;
|
|
17
24
|
disposed = false;
|
|
18
25
|
constructor(options = {}) {
|
|
@@ -21,14 +28,16 @@ export class EventEmitter {
|
|
|
21
28
|
errorMode: options.errorMode ?? EventErrorMode.THROW,
|
|
22
29
|
freezeEvents: options.freezeEvents ?? true,
|
|
23
30
|
};
|
|
31
|
+
this.store =
|
|
32
|
+
options.store ??
|
|
33
|
+
new EventRegistry({
|
|
34
|
+
maxHandlersPerPattern: options.maxListeners,
|
|
35
|
+
onWarning: options.onWarning,
|
|
36
|
+
});
|
|
24
37
|
}
|
|
25
38
|
on(eventType, handler, options = {}) {
|
|
26
39
|
this.ensureActive();
|
|
27
|
-
|
|
28
|
-
...options,
|
|
29
|
-
eventType,
|
|
30
|
-
});
|
|
31
|
-
return this.addRegistration(registration);
|
|
40
|
+
return this.store.registerHandler(eventType, handler, options);
|
|
32
41
|
}
|
|
33
42
|
once(eventType, handler, options = {}) {
|
|
34
43
|
return this.on(eventType, handler, {
|
|
@@ -39,48 +48,64 @@ export class EventEmitter {
|
|
|
39
48
|
onAny(handler, options = {}) {
|
|
40
49
|
return this.on("*", handler, options);
|
|
41
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Cancels a subscription. Returns true when the subscription
|
|
53
|
+
* was active (and is now cancelled).
|
|
54
|
+
*/
|
|
42
55
|
off(subscription) {
|
|
43
56
|
this.ensureActive();
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
return removed;
|
|
57
|
+
const wasActive = subscription.active;
|
|
58
|
+
subscription.unsubscribe();
|
|
59
|
+
return wasActive;
|
|
49
60
|
}
|
|
50
61
|
async emit(event, options = {}) {
|
|
51
62
|
this.ensureActive();
|
|
63
|
+
if (!isEvent(event)) {
|
|
64
|
+
throw new InvalidEventError("emit() requires an Event (use emitEvent() for event input).");
|
|
65
|
+
}
|
|
52
66
|
if (options.signal?.aborted) {
|
|
53
|
-
throw createAbortError();
|
|
67
|
+
throw createAbortError(event);
|
|
54
68
|
}
|
|
55
69
|
const mode = options.mode ?? this.options.mode;
|
|
56
70
|
const errorMode = options.errorMode ?? this.options.errorMode;
|
|
57
|
-
const
|
|
58
|
-
const
|
|
71
|
+
const dispatched = this.options.freezeEvents ? deepFreeze(event) : event;
|
|
72
|
+
const handlers = this.store.getHandlersForEvent(dispatched);
|
|
73
|
+
const context = createEventHandlerContext(dispatched, {
|
|
59
74
|
signal: options.signal,
|
|
60
75
|
metadata: options.metadata,
|
|
61
76
|
});
|
|
77
|
+
const results = [];
|
|
78
|
+
const errors = [];
|
|
62
79
|
if (handlers.length === 0) {
|
|
63
80
|
return {
|
|
64
|
-
event,
|
|
81
|
+
event: dispatched,
|
|
65
82
|
handled: false,
|
|
66
|
-
results
|
|
67
|
-
errors
|
|
83
|
+
results,
|
|
84
|
+
errors,
|
|
85
|
+
succeeded: 0,
|
|
86
|
+
failed: 0,
|
|
68
87
|
};
|
|
69
88
|
}
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
89
|
+
const hooks = {
|
|
90
|
+
isRegistered: (handlerId) => this.store.hasHandler(handlerId),
|
|
91
|
+
removeOnce: (handlerId) => {
|
|
92
|
+
this.store.unregisterHandler(handlerId);
|
|
93
|
+
},
|
|
94
|
+
};
|
|
73
95
|
if (mode === EventEmitterMode.PARALLEL) {
|
|
74
|
-
await emitParallel(handlers,
|
|
96
|
+
await emitParallel(handlers, dispatched, context, errorMode, results, errors, hooks);
|
|
75
97
|
}
|
|
76
98
|
else {
|
|
77
|
-
await emitSequential(handlers,
|
|
99
|
+
await emitSequential(handlers, dispatched, context, errorMode, results, errors, hooks);
|
|
78
100
|
}
|
|
101
|
+
const succeeded = results.filter((result) => result.ok).length;
|
|
79
102
|
return {
|
|
80
|
-
event,
|
|
81
|
-
handled:
|
|
103
|
+
event: dispatched,
|
|
104
|
+
handled: succeeded > 0,
|
|
82
105
|
results,
|
|
83
106
|
errors,
|
|
107
|
+
succeeded,
|
|
108
|
+
failed: results.length - succeeded,
|
|
84
109
|
};
|
|
85
110
|
}
|
|
86
111
|
async emitEvent(input, options = {}) {
|
|
@@ -88,18 +113,22 @@ export class EventEmitter {
|
|
|
88
113
|
return this.emit(event, options);
|
|
89
114
|
}
|
|
90
115
|
get listenerCount() {
|
|
91
|
-
return this.
|
|
116
|
+
return this.store.handlerCount;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Returns the handler store backing this emitter.
|
|
120
|
+
*/
|
|
121
|
+
getStore() {
|
|
122
|
+
return this.store;
|
|
92
123
|
}
|
|
93
124
|
getRegistrations() {
|
|
94
|
-
return
|
|
125
|
+
return this.store.getHandlers();
|
|
95
126
|
}
|
|
96
127
|
removeAllListeners() {
|
|
97
128
|
this.ensureActive();
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
subscription.unsubscribe();
|
|
129
|
+
for (const registration of this.store.getHandlers()) {
|
|
130
|
+
this.store.unregisterHandler(registration.id);
|
|
101
131
|
}
|
|
102
|
-
this.listeners.clear();
|
|
103
132
|
}
|
|
104
133
|
createSubscriptionGroup() {
|
|
105
134
|
this.ensureActive();
|
|
@@ -115,30 +144,9 @@ export class EventEmitter {
|
|
|
115
144
|
isDisposed() {
|
|
116
145
|
return this.disposed;
|
|
117
146
|
}
|
|
118
|
-
addRegistration(registration) {
|
|
119
|
-
const subscription = createEventSubscription(() => {
|
|
120
|
-
this.listeners.delete(registration.id);
|
|
121
|
-
}, {
|
|
122
|
-
id: registration.id,
|
|
123
|
-
description: registration.description,
|
|
124
|
-
});
|
|
125
|
-
this.listeners.set(registration.id, {
|
|
126
|
-
registration,
|
|
127
|
-
subscription,
|
|
128
|
-
});
|
|
129
|
-
return subscription;
|
|
130
|
-
}
|
|
131
|
-
removeHandler(id) {
|
|
132
|
-
const listener = this.listeners.get(id);
|
|
133
|
-
if (!listener) {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
this.listeners.delete(id);
|
|
137
|
-
listener.subscription.unsubscribe();
|
|
138
|
-
}
|
|
139
147
|
ensureActive() {
|
|
140
148
|
if (this.disposed) {
|
|
141
|
-
throw new
|
|
149
|
+
throw new EventEmitterDisposedError();
|
|
142
150
|
}
|
|
143
151
|
}
|
|
144
152
|
}
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Parallel event handler dispatch for Zudojs.
|
|
3
|
+
*
|
|
4
|
+
* All handlers start together; an AbortSignal that fires after
|
|
5
|
+
* the handlers have started cannot stop them. Handlers can observe
|
|
6
|
+
* `context.signal` to cooperate. Only a signal that is already
|
|
7
|
+
* aborted when dispatch begins rejects the emit.
|
|
3
8
|
*/
|
|
4
9
|
import type { Event } from "../eventTypes/eventDefinition.type.js";
|
|
5
10
|
import type { EventHandlerContext, RegisteredEventHandler } from "../eventHandler/eventHandler.core.js";
|
|
6
11
|
import type { EventHandlerExecutionResult } from "./eventEmitter.type.js";
|
|
7
12
|
import { EventErrorMode } from "./eventEmitter.type.js";
|
|
13
|
+
import type { DispatchHooks } from "./eventEmitter.sequential.js";
|
|
8
14
|
/**
|
|
9
15
|
* Executes handlers concurrently.
|
|
10
16
|
*/
|
|
11
|
-
export declare function emitParallel<TEvent extends Event>(handlers: readonly RegisteredEventHandler<TEvent>[], event: TEvent, context: EventHandlerContext<TEvent>, errorMode: EventErrorMode, results: EventHandlerExecutionResult[], errors: unknown[],
|
|
17
|
+
export declare function emitParallel<TEvent extends Event>(handlers: readonly RegisteredEventHandler<TEvent>[], event: TEvent, context: EventHandlerContext<TEvent>, errorMode: EventErrorMode, results: EventHandlerExecutionResult[], errors: unknown[], hooks: DispatchHooks): Promise<void>;
|
|
12
18
|
//# sourceMappingURL=eventEmitter.parallel.d.ts.map
|
|
@@ -1,35 +1,53 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Parallel event handler dispatch for Zudojs.
|
|
3
|
+
*
|
|
4
|
+
* All handlers start together; an AbortSignal that fires after
|
|
5
|
+
* the handlers have started cannot stop them. Handlers can observe
|
|
6
|
+
* `context.signal` to cooperate. Only a signal that is already
|
|
7
|
+
* aborted when dispatch begins rejects the emit.
|
|
3
8
|
*/
|
|
4
|
-
import {
|
|
9
|
+
import { executeRegisteredEventHandler } from "../eventHandler/eventHandler.core.js";
|
|
10
|
+
import { createEventHandlerError } from "../eventErrors/eventError.base.js";
|
|
5
11
|
import { EventErrorMode } from "./eventEmitter.type.js";
|
|
6
12
|
import { createAbortError } from "./eventEmitter.abort.js";
|
|
7
13
|
/**
|
|
8
14
|
* Executes handlers concurrently.
|
|
9
15
|
*/
|
|
10
|
-
export async function emitParallel(handlers, event, context, errorMode, results, errors,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
export async function emitParallel(handlers, event, context, errorMode, results, errors, hooks) {
|
|
17
|
+
if (context.signal.aborted) {
|
|
18
|
+
throw createAbortError(event, results, errors);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Consume once-handlers synchronously before anything starts so
|
|
22
|
+
* an overlapping dispatch cannot invoke them a second time.
|
|
23
|
+
*/
|
|
24
|
+
const runnable = handlers.filter((handler) => {
|
|
25
|
+
if (!handler.once) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
if (!hooks.isRegistered(handler.id)) {
|
|
29
|
+
return false;
|
|
14
30
|
}
|
|
31
|
+
hooks.removeOnce(handler.id);
|
|
32
|
+
return true;
|
|
33
|
+
});
|
|
34
|
+
const executions = runnable.map(async (handler) => {
|
|
15
35
|
const started = performance.now();
|
|
16
36
|
try {
|
|
17
|
-
const result = await
|
|
18
|
-
|
|
37
|
+
const result = await executeRegisteredEventHandler(handler, event, context);
|
|
38
|
+
return {
|
|
19
39
|
handlerId: handler.id,
|
|
20
40
|
eventId: event.id,
|
|
41
|
+
ok: true,
|
|
21
42
|
result,
|
|
22
43
|
duration: performance.now() - started,
|
|
23
44
|
};
|
|
24
|
-
if (handler.once) {
|
|
25
|
-
onOnceHandler(handler.id);
|
|
26
|
-
}
|
|
27
|
-
return execution;
|
|
28
45
|
}
|
|
29
46
|
catch (error) {
|
|
30
47
|
return {
|
|
31
48
|
handlerId: handler.id,
|
|
32
49
|
eventId: event.id,
|
|
50
|
+
ok: false,
|
|
33
51
|
result: undefined,
|
|
34
52
|
duration: performance.now() - started,
|
|
35
53
|
error,
|
|
@@ -39,8 +57,8 @@ export async function emitParallel(handlers, event, context, errorMode, results,
|
|
|
39
57
|
const settled = await Promise.all(executions);
|
|
40
58
|
for (const result of settled) {
|
|
41
59
|
results.push(result);
|
|
42
|
-
if (result.
|
|
43
|
-
errors.push(result.error);
|
|
60
|
+
if (!result.ok) {
|
|
61
|
+
errors.push(createEventHandlerError(result.handlerId, event.type, event.id, result.error));
|
|
44
62
|
}
|
|
45
63
|
}
|
|
46
64
|
if (errors.length > 0 && errorMode === EventErrorMode.THROW) {
|
|
@@ -5,8 +5,24 @@ import type { Event } from "../eventTypes/eventDefinition.type.js";
|
|
|
5
5
|
import type { EventHandlerContext, RegisteredEventHandler } from "../eventHandler/eventHandler.core.js";
|
|
6
6
|
import type { EventHandlerExecutionResult } from "./eventEmitter.type.js";
|
|
7
7
|
import { EventErrorMode } from "./eventEmitter.type.js";
|
|
8
|
+
/**
|
|
9
|
+
* Callbacks the dispatch strategies use to interact with the
|
|
10
|
+
* handler store.
|
|
11
|
+
*/
|
|
12
|
+
export interface DispatchHooks {
|
|
13
|
+
/**
|
|
14
|
+
* Returns whether a handler is still registered.
|
|
15
|
+
*/
|
|
16
|
+
readonly isRegistered: (handlerId: string) => boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Removes a once-handler. Called before the handler runs so a
|
|
19
|
+
* throwing or concurrently dispatched once-handler never fires
|
|
20
|
+
* twice.
|
|
21
|
+
*/
|
|
22
|
+
readonly removeOnce: (handlerId: string) => void;
|
|
23
|
+
}
|
|
8
24
|
/**
|
|
9
25
|
* Executes handlers sequentially.
|
|
10
26
|
*/
|
|
11
|
-
export declare function emitSequential<TEvent extends Event>(handlers: readonly RegisteredEventHandler<TEvent>[], event: TEvent, context: EventHandlerContext<TEvent>, errorMode: EventErrorMode, results: EventHandlerExecutionResult[], errors: unknown[],
|
|
27
|
+
export declare function emitSequential<TEvent extends Event>(handlers: readonly RegisteredEventHandler<TEvent>[], event: TEvent, context: EventHandlerContext<TEvent>, errorMode: EventErrorMode, results: EventHandlerExecutionResult[], errors: unknown[], hooks: DispatchHooks): Promise<void>;
|
|
12
28
|
//# sourceMappingURL=eventEmitter.sequential.d.ts.map
|