@zudojs/events 0.1.0 → 1.0.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/LICENSE +21 -0
- package/README.md +70 -11
- package/dist/eventBus/eventBus.core.d.ts +28 -2
- package/dist/eventBus/eventBus.core.js +109 -31
- package/dist/eventBus/eventBus.publish.d.ts +23 -5
- package/dist/eventBus/eventBus.publish.js +78 -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 +72 -56
- 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 +62 -22
- 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 +26 -14
- 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 middleware pipeline execution for Zudojs.
|
|
3
3
|
*/
|
|
4
|
-
import { EventMiddlewareError, toEventError, } from "../eventErrors/eventError.base.js";
|
|
4
|
+
import { EventDispatchAbortedError, EventMiddlewareError, toEventError, } from "../eventErrors/eventError.base.js";
|
|
5
5
|
import { sortEventMiddleware, executeEventMiddleware, } from "./eventMiddleware.helper.js";
|
|
6
6
|
/**
|
|
7
7
|
* Executes a middleware pipeline.
|
|
@@ -13,6 +13,11 @@ import { sortEventMiddleware, executeEventMiddleware, } from "./eventMiddleware.
|
|
|
13
13
|
* → handler
|
|
14
14
|
* ← middleware B
|
|
15
15
|
* ← middleware A
|
|
16
|
+
*
|
|
17
|
+
* Only errors thrown by a middleware function itself are wrapped
|
|
18
|
+
* in EventMiddlewareError. Errors coming back through next() —
|
|
19
|
+
* handler failures, aborts, downstream middleware errors — are
|
|
20
|
+
* re-thrown untouched so callers can discriminate them.
|
|
16
21
|
*/
|
|
17
22
|
export async function executeEventMiddlewarePipeline(middleware, context, terminal) {
|
|
18
23
|
const started = performance.now();
|
|
@@ -21,7 +26,7 @@ export async function executeEventMiddlewarePipeline(middleware, context, termin
|
|
|
21
26
|
let index = -1;
|
|
22
27
|
const dispatch = async (currentIndex) => {
|
|
23
28
|
if (context.signal.aborted) {
|
|
24
|
-
throw createAbortError();
|
|
29
|
+
throw createAbortError(context);
|
|
25
30
|
}
|
|
26
31
|
if (currentIndex === activeMiddleware.length) {
|
|
27
32
|
return terminal();
|
|
@@ -39,6 +44,13 @@ export async function executeEventMiddlewarePipeline(middleware, context, termin
|
|
|
39
44
|
}
|
|
40
45
|
const middlewareStarted = performance.now();
|
|
41
46
|
let nextCalled = false;
|
|
47
|
+
/**
|
|
48
|
+
* Errors that surfaced through next() belong to downstream
|
|
49
|
+
* code, not to this middleware; they must pass through
|
|
50
|
+
* unwrapped.
|
|
51
|
+
*/
|
|
52
|
+
let downstreamThrew = false;
|
|
53
|
+
let downstreamError;
|
|
42
54
|
const next = async () => {
|
|
43
55
|
if (nextCalled) {
|
|
44
56
|
throw new EventMiddlewareError(`Middleware "${current.id}" called next() more than once.`, {
|
|
@@ -48,7 +60,14 @@ export async function executeEventMiddlewarePipeline(middleware, context, termin
|
|
|
48
60
|
});
|
|
49
61
|
}
|
|
50
62
|
nextCalled = true;
|
|
51
|
-
|
|
63
|
+
try {
|
|
64
|
+
return await dispatch(currentIndex + 1);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
downstreamThrew = true;
|
|
68
|
+
downstreamError = error;
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
52
71
|
};
|
|
53
72
|
try {
|
|
54
73
|
const result = await executeEventMiddleware(current.middleware, context, next);
|
|
@@ -60,7 +79,11 @@ export async function executeEventMiddlewarePipeline(middleware, context, termin
|
|
|
60
79
|
return result;
|
|
61
80
|
}
|
|
62
81
|
catch (error) {
|
|
63
|
-
if (error
|
|
82
|
+
if (downstreamThrew && error === downstreamError) {
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
if (error instanceof EventMiddlewareError ||
|
|
86
|
+
error instanceof EventDispatchAbortedError) {
|
|
64
87
|
throw error;
|
|
65
88
|
}
|
|
66
89
|
throw new EventMiddlewareError(`Event middleware "${current.id}" failed.`, {
|
|
@@ -82,12 +105,13 @@ export async function executeEventMiddlewarePipeline(middleware, context, termin
|
|
|
82
105
|
};
|
|
83
106
|
}
|
|
84
107
|
/**
|
|
85
|
-
* Creates
|
|
86
|
-
*
|
|
108
|
+
* Creates the abort error thrown when the pipeline observes an
|
|
109
|
+
* aborted signal.
|
|
87
110
|
*/
|
|
88
|
-
function createAbortError() {
|
|
89
|
-
return new
|
|
90
|
-
|
|
111
|
+
function createAbortError(context) {
|
|
112
|
+
return new EventDispatchAbortedError("Event dispatch was aborted.", {
|
|
113
|
+
eventType: context.event?.type,
|
|
114
|
+
eventId: context.event?.id,
|
|
91
115
|
});
|
|
92
116
|
}
|
|
93
117
|
//# sourceMappingURL=eventMiddleware.pipeline.js.map
|
|
@@ -2,18 +2,23 @@
|
|
|
2
2
|
* Event registry lifecycle methods for Zudojs.
|
|
3
3
|
*/
|
|
4
4
|
import type { EventType } from "../eventTypes/eventDefinition.type.js";
|
|
5
|
-
import type {
|
|
6
|
-
import type { EventRegistryChange, EventRegistryListener, RegisteredEventDefinition } from "./eventRegistry.type.js";
|
|
5
|
+
import type { EventHandlerEntry, EventRegistryChange, EventRegistryErrorContext, EventRegistryListener, RegisteredEventDefinition } from "./eventRegistry.type.js";
|
|
7
6
|
/**
|
|
8
7
|
* Clears all handlers and definitions from the registry.
|
|
8
|
+
*
|
|
9
|
+
* Every handler subscription is cancelled, so subscriptions held
|
|
10
|
+
* by callers report `active: false` afterwards.
|
|
9
11
|
*/
|
|
10
|
-
export declare function registryClear(definitions: Map<EventType, RegisteredEventDefinition>, handlers: Map<string,
|
|
12
|
+
export declare function registryClear(definitions: Map<EventType, RegisteredEventDefinition>, handlers: Map<string, EventHandlerEntry>, ensureActive: () => void, notify: (change: EventRegistryChange) => void): void;
|
|
11
13
|
/**
|
|
12
14
|
* Disposes the registry.
|
|
13
15
|
*/
|
|
14
|
-
export declare function registryDispose(disposed: boolean, definitions: Map<EventType, RegisteredEventDefinition>, handlers: Map<string,
|
|
16
|
+
export declare function registryDispose(disposed: boolean, definitions: Map<EventType, RegisteredEventDefinition>, handlers: Map<string, EventHandlerEntry>, listeners: Set<EventRegistryListener>, ensureActive: () => void, notify: (change: EventRegistryChange) => void): void;
|
|
15
17
|
/**
|
|
16
18
|
* Notifies registry listeners.
|
|
19
|
+
*
|
|
20
|
+
* Observer failures never break registry mutations; they are
|
|
21
|
+
* forwarded to the `onError` hook when one is configured.
|
|
17
22
|
*/
|
|
18
|
-
export declare function registryNotify(change: EventRegistryChange, listeners: Set<EventRegistryListener
|
|
23
|
+
export declare function registryNotify(change: EventRegistryChange, listeners: Set<EventRegistryListener>, onError?: (error: unknown, context: EventRegistryErrorContext) => void): void;
|
|
19
24
|
//# sourceMappingURL=eventRegistry.lifecycle.d.ts.map
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Event registry lifecycle methods for Zudojs.
|
|
3
3
|
*/
|
|
4
|
-
import { registryUnregister
|
|
4
|
+
import { registryUnregister } from "./eventRegistry.registration.js";
|
|
5
5
|
/**
|
|
6
6
|
* Clears all handlers and definitions from the registry.
|
|
7
|
+
*
|
|
8
|
+
* Every handler subscription is cancelled, so subscriptions held
|
|
9
|
+
* by callers report `active: false` afterwards.
|
|
7
10
|
*/
|
|
8
11
|
export function registryClear(definitions, handlers, ensureActive, notify) {
|
|
9
12
|
ensureActive();
|
|
10
|
-
const
|
|
11
|
-
for (const
|
|
12
|
-
|
|
13
|
+
const entries = [...handlers.values()];
|
|
14
|
+
for (const entry of entries) {
|
|
15
|
+
entry.subscription.unsubscribe();
|
|
13
16
|
}
|
|
17
|
+
handlers.clear();
|
|
14
18
|
const eventTypes = [...definitions.keys()];
|
|
15
19
|
for (const eventType of eventTypes) {
|
|
16
20
|
registryUnregister(eventType, definitions, ensureActive, notify);
|
|
@@ -28,17 +32,26 @@ export function registryDispose(disposed, definitions, handlers, listeners, ensu
|
|
|
28
32
|
}
|
|
29
33
|
/**
|
|
30
34
|
* Notifies registry listeners.
|
|
35
|
+
*
|
|
36
|
+
* Observer failures never break registry mutations; they are
|
|
37
|
+
* forwarded to the `onError` hook when one is configured.
|
|
31
38
|
*/
|
|
32
|
-
export function registryNotify(change, listeners) {
|
|
39
|
+
export function registryNotify(change, listeners, onError) {
|
|
33
40
|
for (const listener of listeners) {
|
|
34
41
|
try {
|
|
35
42
|
listener(change);
|
|
36
43
|
}
|
|
37
|
-
catch {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (onError) {
|
|
46
|
+
try {
|
|
47
|
+
onError(error, { source: "observer", change });
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
/**
|
|
51
|
+
* A failing error hook must not break the mutation either.
|
|
52
|
+
*/
|
|
53
|
+
}
|
|
54
|
+
}
|
|
42
55
|
}
|
|
43
56
|
}
|
|
44
57
|
}
|
|
@@ -3,21 +3,30 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { Event, EventType } from "../eventTypes/eventDefinition.type.js";
|
|
5
5
|
import type { RegisteredEventHandler } from "../eventHandler/eventHandler.core.js";
|
|
6
|
-
import type { RegisteredEventDefinition } from "./eventRegistry.type.js";
|
|
6
|
+
import type { EventHandlerEntry, RegisteredEventDefinition } from "./eventRegistry.type.js";
|
|
7
7
|
/**
|
|
8
|
-
* Returns handlers matching an event
|
|
8
|
+
* Returns enabled handlers matching an event, sorted by priority,
|
|
9
|
+
* exactly as the emitter would dispatch them.
|
|
10
|
+
*
|
|
11
|
+
* The event's type is normalized before matching, mirroring
|
|
12
|
+
* getHandlersForType(). Handler patterns are normalized when they
|
|
13
|
+
* are registered, so an event whose `type` preserves its original
|
|
14
|
+
* casing (as CQRS domain events do) still routes to the handlers
|
|
15
|
+
* that registered for it. Only the routing key is normalized; the
|
|
16
|
+
* event object handed to handlers is left untouched.
|
|
9
17
|
*/
|
|
10
|
-
export declare function getHandlersForEvent(handlers: Map<string,
|
|
18
|
+
export declare function getHandlersForEvent(handlers: Map<string, EventHandlerEntry>, event: Event): readonly RegisteredEventHandler[];
|
|
11
19
|
/**
|
|
12
|
-
* Returns handlers matching an event type
|
|
20
|
+
* Returns enabled handlers matching an event type, sorted by
|
|
21
|
+
* priority. The type is normalized before matching.
|
|
13
22
|
*/
|
|
14
|
-
export declare function getHandlersForType(handlers: Map<string,
|
|
23
|
+
export declare function getHandlersForType(handlers: Map<string, EventHandlerEntry>, eventType: EventType): readonly RegisteredEventHandler[];
|
|
15
24
|
/**
|
|
16
25
|
* Returns all event definitions as an array.
|
|
17
26
|
*/
|
|
18
27
|
export declare function getAllDefinitions(definitions: Map<EventType, RegisteredEventDefinition>): readonly RegisteredEventDefinition[];
|
|
19
28
|
/**
|
|
20
|
-
* Returns all handlers as an array.
|
|
29
|
+
* Returns all handlers as an array (registration order).
|
|
21
30
|
*/
|
|
22
|
-
export declare function getAllHandlers(handlers: Map<string,
|
|
31
|
+
export declare function getAllHandlers(handlers: Map<string, EventHandlerEntry>): readonly RegisteredEventHandler[];
|
|
23
32
|
//# sourceMappingURL=eventRegistry.queries.d.ts.map
|
|
@@ -1,24 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Event registry query functions for Zudojs.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { getMatchingEventHandlers } from "../eventHandler/eventHandler.core.js";
|
|
5
|
+
import { normalizeRegistryEventType } from "./eventRegistry.registration.js";
|
|
5
6
|
/**
|
|
6
|
-
* Returns handlers matching an event
|
|
7
|
+
* Returns enabled handlers matching an event, sorted by priority,
|
|
8
|
+
* exactly as the emitter would dispatch them.
|
|
9
|
+
*
|
|
10
|
+
* The event's type is normalized before matching, mirroring
|
|
11
|
+
* getHandlersForType(). Handler patterns are normalized when they
|
|
12
|
+
* are registered, so an event whose `type` preserves its original
|
|
13
|
+
* casing (as CQRS domain events do) still routes to the handlers
|
|
14
|
+
* that registered for it. Only the routing key is normalized; the
|
|
15
|
+
* event object handed to handlers is left untouched.
|
|
7
16
|
*/
|
|
8
17
|
export function getHandlersForEvent(handlers, event) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
return matchesEventType(event.type, handler.eventType);
|
|
14
|
-
});
|
|
18
|
+
const type = normalizeRegistryEventType(event.type);
|
|
19
|
+
const routed = event.type === type ? event : { ...event, type };
|
|
20
|
+
return getMatchingEventHandlers(getAllHandlers(handlers), routed);
|
|
15
21
|
}
|
|
16
22
|
/**
|
|
17
|
-
* Returns handlers matching an event type
|
|
23
|
+
* Returns enabled handlers matching an event type, sorted by
|
|
24
|
+
* priority. The type is normalized before matching.
|
|
18
25
|
*/
|
|
19
26
|
export function getHandlersForType(handlers, eventType) {
|
|
20
|
-
const type =
|
|
21
|
-
return
|
|
27
|
+
const type = normalizeRegistryEventType(eventType);
|
|
28
|
+
return getMatchingEventHandlers(getAllHandlers(handlers), {
|
|
29
|
+
type,
|
|
30
|
+
});
|
|
22
31
|
}
|
|
23
32
|
/**
|
|
24
33
|
* Returns all event definitions as an array.
|
|
@@ -27,9 +36,9 @@ export function getAllDefinitions(definitions) {
|
|
|
27
36
|
return [...definitions.values()];
|
|
28
37
|
}
|
|
29
38
|
/**
|
|
30
|
-
* Returns all handlers as an array.
|
|
39
|
+
* Returns all handlers as an array (registration order).
|
|
31
40
|
*/
|
|
32
41
|
export function getAllHandlers(handlers) {
|
|
33
|
-
return [...handlers.values()];
|
|
42
|
+
return [...handlers.values()].map((entry) => entry.registration);
|
|
34
43
|
}
|
|
35
44
|
//# sourceMappingURL=eventRegistry.queries.js.map
|
|
@@ -3,9 +3,14 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { Event, EventDefinition, EventType } from "../eventTypes/eventDefinition.type.js";
|
|
5
5
|
import type { EventTypePattern } from "../eventTypes/eventType.type.js";
|
|
6
|
-
import type { EventHandlerLike, EventHandlerOptions
|
|
6
|
+
import type { EventHandlerLike, EventHandlerOptions } from "../eventHandler/eventHandler.core.js";
|
|
7
7
|
import type { EventSubscription } from "../eventSubscription/eventSubscription.core.js";
|
|
8
|
-
import type { EventRegistryChange, RegisteredEventDefinition } from "./eventRegistry.type.js";
|
|
8
|
+
import type { DuplicateHandlerIdPolicy, EventHandlerEntry, EventRegistryChange, EventRegistryWarning, RegisteredEventDefinition } from "./eventRegistry.type.js";
|
|
9
|
+
/**
|
|
10
|
+
* Normalizes an event type for registry lookups, converting
|
|
11
|
+
* validation failures into InvalidEventError.
|
|
12
|
+
*/
|
|
13
|
+
export declare function normalizeRegistryEventType(eventType: string): EventType;
|
|
9
14
|
/**
|
|
10
15
|
* Registers an event definition.
|
|
11
16
|
*/
|
|
@@ -15,15 +20,17 @@ export declare function registryRegister<TType extends EventType, TPayload>(defi
|
|
|
15
20
|
/**
|
|
16
21
|
* Registers a handler.
|
|
17
22
|
*/
|
|
18
|
-
export declare function registryRegisterHandler<TEvent extends Event = Event>(eventType: EventTypePattern, handler: EventHandlerLike<TEvent>, handlerOptions: Omit<EventHandlerOptions, "eventType">, handlers: Map<string,
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
export declare function registryRegisterHandler<TEvent extends Event = Event>(eventType: EventTypePattern, handler: EventHandlerLike<TEvent>, handlerOptions: Omit<EventHandlerOptions, "eventType">, handlers: Map<string, EventHandlerEntry>, options: {
|
|
24
|
+
onDuplicateHandlerId: DuplicateHandlerIdPolicy;
|
|
25
|
+
maxHandlersPerPattern: number;
|
|
26
|
+
onWarning: (warning: EventRegistryWarning) => void;
|
|
27
|
+
}, ensureActive: () => void, notify: (change: EventRegistryChange) => void, warnedPatterns: Set<string>): EventSubscription;
|
|
21
28
|
/**
|
|
22
29
|
* Unregisters an event definition.
|
|
23
30
|
*/
|
|
24
31
|
export declare function registryUnregister(eventType: EventType, definitions: Map<EventType, RegisteredEventDefinition>, ensureActive: () => void, notify: (change: EventRegistryChange) => void): boolean;
|
|
25
32
|
/**
|
|
26
|
-
* Unregisters a handler.
|
|
33
|
+
* Unregisters a handler by id, cancelling its subscription.
|
|
27
34
|
*/
|
|
28
|
-
export declare function registryUnregisterHandler(handlerId: string, handlers: Map<string,
|
|
35
|
+
export declare function registryUnregisterHandler(handlerId: string, handlers: Map<string, EventHandlerEntry>, ensureActive: () => void): boolean;
|
|
29
36
|
//# sourceMappingURL=eventRegistry.registration.d.ts.map
|
|
@@ -1,26 +1,57 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Event registry registration methods for Zudojs.
|
|
3
3
|
*/
|
|
4
|
+
import { normalizeEventType } from "../eventTypes/eventType.type.js";
|
|
4
5
|
import { createEventHandler } from "../eventHandler/eventHandler.core.js";
|
|
5
|
-
import { isValidEventType, isValidEventTypePattern, normalizeEventType, } from "../eventTypes/eventType.type.js";
|
|
6
6
|
import { createEventSubscription } from "../eventSubscription/eventSubscription.core.js";
|
|
7
|
-
import { DuplicateEventDefinitionError, DuplicateEventHandlerError, } from "../eventErrors/eventError.base.js";
|
|
7
|
+
import { DuplicateEventDefinitionError, DuplicateEventHandlerError, InvalidEventError, } from "../eventErrors/eventError.base.js";
|
|
8
8
|
import { EventRegistryChangeType } from "./eventRegistry.type.js";
|
|
9
|
+
/**
|
|
10
|
+
* Normalizes an event type for registry lookups, converting
|
|
11
|
+
* validation failures into InvalidEventError.
|
|
12
|
+
*/
|
|
13
|
+
export function normalizeRegistryEventType(eventType) {
|
|
14
|
+
try {
|
|
15
|
+
return normalizeEventType(eventType);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
throw new InvalidEventError(`Invalid event type "${String(eventType)}".`, {
|
|
19
|
+
eventType: typeof eventType === "string" ? eventType : undefined,
|
|
20
|
+
cause: error,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
9
24
|
/**
|
|
10
25
|
* Registers an event definition.
|
|
11
26
|
*/
|
|
12
27
|
export function registryRegister(definition, definitions, options, ensureActive, notify) {
|
|
13
28
|
ensureActive();
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
29
|
+
if (typeof definition !== "object" ||
|
|
30
|
+
definition === null ||
|
|
31
|
+
typeof definition.type !== "string" ||
|
|
32
|
+
typeof definition.create !== "function") {
|
|
33
|
+
throw new InvalidEventError("Event definition must be created with defineEvent().");
|
|
17
34
|
}
|
|
35
|
+
const type = normalizeRegistryEventType(definition.type);
|
|
18
36
|
if (definitions.has(type) && !options.allowDuplicateDefinitions) {
|
|
19
37
|
throw new DuplicateEventDefinitionError(type);
|
|
20
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* The stored definition always stamps the normalized type so
|
|
41
|
+
* events it creates are publishable under the registered type.
|
|
42
|
+
*/
|
|
43
|
+
const normalizedDefinition = definition.type === type
|
|
44
|
+
? definition
|
|
45
|
+
: Object.freeze({
|
|
46
|
+
type: type,
|
|
47
|
+
create: (payload, createOptions) => ({
|
|
48
|
+
...definition.create(payload, createOptions),
|
|
49
|
+
type,
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
21
52
|
const registered = Object.freeze({
|
|
22
53
|
type: type,
|
|
23
|
-
definition,
|
|
54
|
+
definition: normalizedDefinition,
|
|
24
55
|
registeredAt: new Date(),
|
|
25
56
|
});
|
|
26
57
|
definitions.set(type, registered);
|
|
@@ -34,43 +65,96 @@ export function registryRegister(definition, definitions, options, ensureActive,
|
|
|
34
65
|
/**
|
|
35
66
|
* Registers a handler.
|
|
36
67
|
*/
|
|
37
|
-
export function registryRegisterHandler(eventType, handler, handlerOptions, handlers, options, ensureActive, notify) {
|
|
68
|
+
export function registryRegisterHandler(eventType, handler, handlerOptions, handlers, options, ensureActive, notify, warnedPatterns) {
|
|
38
69
|
ensureActive();
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
? "*"
|
|
44
|
-
: eventType.endsWith(".*")
|
|
45
|
-
? `${normalizeEventType(eventType.slice(0, -2))}.*`
|
|
46
|
-
: normalizeEventType(eventType);
|
|
70
|
+
/**
|
|
71
|
+
* createEventHandler validates the handler, the pattern and the
|
|
72
|
+
* options, and normalizes the pattern.
|
|
73
|
+
*/
|
|
47
74
|
const registration = createEventHandler(handler, {
|
|
48
75
|
...handlerOptions,
|
|
49
|
-
eventType
|
|
76
|
+
eventType,
|
|
50
77
|
});
|
|
51
|
-
|
|
52
|
-
|
|
78
|
+
const existing = handlers.get(registration.id);
|
|
79
|
+
if (existing) {
|
|
80
|
+
if (options.onDuplicateHandlerId === "throw") {
|
|
81
|
+
throw new DuplicateEventHandlerError(registration.id);
|
|
82
|
+
}
|
|
83
|
+
existing.subscription.unsubscribe();
|
|
53
84
|
}
|
|
54
|
-
|
|
85
|
+
const subscription = createEventSubscription(() => {
|
|
86
|
+
/**
|
|
87
|
+
* Identity check: only remove the entry if it still belongs
|
|
88
|
+
* to this registration. A replacement registered under the
|
|
89
|
+
* same id must not be removed by the old subscription. No
|
|
90
|
+
* ensureActive here so teardown after dispose is a no-op.
|
|
91
|
+
*/
|
|
92
|
+
if (handlers.get(registration.id)?.registration !== registration) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
handlers.delete(registration.id);
|
|
96
|
+
notify({
|
|
97
|
+
type: EventRegistryChangeType.HANDLER_UNREGISTERED,
|
|
98
|
+
eventType: registration.eventType,
|
|
99
|
+
handler: registration,
|
|
100
|
+
timestamp: new Date(),
|
|
101
|
+
});
|
|
102
|
+
}, {
|
|
103
|
+
id: registration.id,
|
|
104
|
+
description: registration.description,
|
|
105
|
+
});
|
|
106
|
+
handlers.set(registration.id, { registration, subscription });
|
|
107
|
+
checkHandlerLimit(registration.eventType, handlers, options.maxHandlersPerPattern, options.onWarning, warnedPatterns);
|
|
55
108
|
notify({
|
|
56
109
|
type: EventRegistryChangeType.HANDLER_REGISTERED,
|
|
57
|
-
eventType:
|
|
110
|
+
eventType: registration.eventType,
|
|
58
111
|
handler: registration,
|
|
59
112
|
timestamp: new Date(),
|
|
60
113
|
});
|
|
61
|
-
return
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
114
|
+
return subscription;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Emits a leak warning (once per pattern) when the number of
|
|
118
|
+
* handlers for a pattern exceeds the configured limit.
|
|
119
|
+
*/
|
|
120
|
+
function checkHandlerLimit(pattern, handlers, limit, onWarning, warnedPatterns) {
|
|
121
|
+
if (limit <= 0 || warnedPatterns.has(pattern)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
let count = 0;
|
|
125
|
+
for (const entry of handlers.values()) {
|
|
126
|
+
if (entry.registration.eventType === pattern) {
|
|
127
|
+
count++;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (count <= limit) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
warnedPatterns.add(pattern);
|
|
134
|
+
const warning = {
|
|
135
|
+
type: "handler.limit",
|
|
136
|
+
pattern,
|
|
137
|
+
count,
|
|
138
|
+
limit,
|
|
139
|
+
message: `Possible event handler leak: ${count} handlers registered for ` +
|
|
140
|
+
`"${pattern}" (limit ${limit}). Unsubscribe handlers you no longer ` +
|
|
141
|
+
"need or raise maxHandlersPerPattern / maxListeners.",
|
|
142
|
+
};
|
|
143
|
+
try {
|
|
144
|
+
onWarning(warning);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
/**
|
|
148
|
+
* A failing warning hook must not break registration.
|
|
149
|
+
*/
|
|
150
|
+
}
|
|
67
151
|
}
|
|
68
152
|
/**
|
|
69
153
|
* Unregisters an event definition.
|
|
70
154
|
*/
|
|
71
155
|
export function registryUnregister(eventType, definitions, ensureActive, notify) {
|
|
72
156
|
ensureActive();
|
|
73
|
-
const type =
|
|
157
|
+
const type = normalizeRegistryEventType(eventType);
|
|
74
158
|
const removed = definitions.delete(type);
|
|
75
159
|
if (removed) {
|
|
76
160
|
notify({
|
|
@@ -82,23 +166,18 @@ export function registryUnregister(eventType, definitions, ensureActive, notify)
|
|
|
82
166
|
return removed;
|
|
83
167
|
}
|
|
84
168
|
/**
|
|
85
|
-
* Unregisters a handler.
|
|
169
|
+
* Unregisters a handler by id, cancelling its subscription.
|
|
86
170
|
*/
|
|
87
|
-
export function registryUnregisterHandler(handlerId, handlers, ensureActive
|
|
171
|
+
export function registryUnregisterHandler(handlerId, handlers, ensureActive) {
|
|
88
172
|
ensureActive();
|
|
89
|
-
const
|
|
90
|
-
if (!
|
|
173
|
+
const entry = handlers.get(handlerId);
|
|
174
|
+
if (!entry) {
|
|
91
175
|
return false;
|
|
92
176
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
handler,
|
|
99
|
-
timestamp: new Date(),
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
return removed;
|
|
177
|
+
/**
|
|
178
|
+
* The subscription callback removes the entry and notifies.
|
|
179
|
+
*/
|
|
180
|
+
entry.subscription.unsubscribe();
|
|
181
|
+
return !handlers.has(handlerId);
|
|
103
182
|
}
|
|
104
183
|
//# sourceMappingURL=eventRegistry.registration.js.map
|
|
@@ -3,22 +3,24 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The registry owns event definitions and registered handlers.
|
|
5
5
|
* It does not perform event dispatching. Dispatching belongs to
|
|
6
|
-
* EventEmitter
|
|
6
|
+
* EventEmitter (which stores its handlers in a registry) and
|
|
7
|
+
* higher-level routing belongs to EventBus.
|
|
7
8
|
*/
|
|
8
9
|
import type { Event, EventDefinition, EventType } from "../eventTypes/eventDefinition.type.js";
|
|
9
10
|
import type { EventTypePattern } from "../eventTypes/eventType.type.js";
|
|
10
11
|
import type { EventHandlerLike, EventHandlerOptions, RegisteredEventHandler } from "../eventHandler/eventHandler.core.js";
|
|
11
12
|
import type { EventSubscription } from "../eventSubscription/eventSubscription.core.js";
|
|
12
|
-
import { DuplicateEventDefinitionError, EventDefinitionNotFoundError, DuplicateEventHandlerError, EventHandlerNotFoundError } from "../eventErrors/eventError.base.js";
|
|
13
|
-
import type { EventRegistryOptions, EventRegistryListener, RegisteredEventDefinition } from "./eventRegistry.type.js";
|
|
14
|
-
export { DuplicateEventDefinitionError, EventDefinitionNotFoundError, DuplicateEventHandlerError, EventHandlerNotFoundError, };
|
|
13
|
+
import { DuplicateEventDefinitionError, EventDefinitionNotFoundError, DuplicateEventHandlerError, EventHandlerNotFoundError, EventRegistryDisposedError } from "../eventErrors/eventError.base.js";
|
|
14
|
+
import type { EventHandlerStore, EventRegistryOptions, EventRegistryListener, RegisteredEventDefinition } from "./eventRegistry.type.js";
|
|
15
|
+
export { DuplicateEventDefinitionError, EventDefinitionNotFoundError, DuplicateEventHandlerError, EventHandlerNotFoundError, EventRegistryDisposedError, };
|
|
15
16
|
/**
|
|
16
17
|
* Main event registry.
|
|
17
18
|
*/
|
|
18
|
-
export declare class EventRegistry {
|
|
19
|
+
export declare class EventRegistry implements EventHandlerStore {
|
|
19
20
|
private readonly definitions;
|
|
20
21
|
private readonly handlers;
|
|
21
22
|
private readonly listeners;
|
|
23
|
+
private readonly warnedPatterns;
|
|
22
24
|
private readonly options;
|
|
23
25
|
private disposed;
|
|
24
26
|
constructor(options?: EventRegistryOptions);
|
|
@@ -31,6 +33,11 @@ export declare class EventRegistry {
|
|
|
31
33
|
getHandler(handlerId: string): RegisteredEventHandler | undefined;
|
|
32
34
|
requireHandler(handlerId: string): RegisteredEventHandler;
|
|
33
35
|
hasHandler(handlerId: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Returns the subscription created for a handler id, if the
|
|
38
|
+
* handler is still registered.
|
|
39
|
+
*/
|
|
40
|
+
getHandlerSubscription(handlerId: string): EventSubscription | undefined;
|
|
34
41
|
unregisterHandler(handlerId: string): boolean;
|
|
35
42
|
getDefinitions(): readonly RegisteredEventDefinition[];
|
|
36
43
|
getHandlers(): readonly RegisteredEventHandler[];
|