@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
|
@@ -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[];
|
|
@@ -3,13 +3,18 @@
|
|
|
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
|
-
import { DuplicateEventDefinitionError, EventDefinitionNotFoundError, DuplicateEventHandlerError, EventHandlerNotFoundError, } from "../eventErrors/eventError.base.js";
|
|
9
|
-
import {
|
|
9
|
+
import { DuplicateEventDefinitionError, EventDefinitionNotFoundError, DuplicateEventHandlerError, EventHandlerNotFoundError, EventRegistryDisposedError, } from "../eventErrors/eventError.base.js";
|
|
10
|
+
import { DEFAULT_MAX_HANDLERS_PER_PATTERN } from "./eventRegistry.type.js";
|
|
11
|
+
import { normalizeRegistryEventType, registryRegister, registryRegisterHandler, registryUnregister, registryUnregisterHandler, } from "./eventRegistry.registration.js";
|
|
10
12
|
import { getHandlersForEvent, getHandlersForType, getAllDefinitions, getAllHandlers, } from "./eventRegistry.queries.js";
|
|
11
13
|
import { registryClear, registryDispose, registryNotify, } from "./eventRegistry.lifecycle.js";
|
|
12
|
-
export { DuplicateEventDefinitionError, EventDefinitionNotFoundError, DuplicateEventHandlerError, EventHandlerNotFoundError, };
|
|
14
|
+
export { DuplicateEventDefinitionError, EventDefinitionNotFoundError, DuplicateEventHandlerError, EventHandlerNotFoundError, EventRegistryDisposedError, };
|
|
15
|
+
function defaultWarning(warning) {
|
|
16
|
+
console.warn(`[@zudojs/events] ${warning.message}`);
|
|
17
|
+
}
|
|
13
18
|
/**
|
|
14
19
|
* Main event registry.
|
|
15
20
|
*/
|
|
@@ -17,23 +22,34 @@ export class EventRegistry {
|
|
|
17
22
|
definitions = new Map();
|
|
18
23
|
handlers = new Map();
|
|
19
24
|
listeners = new Set();
|
|
25
|
+
warnedPatterns = new Set();
|
|
20
26
|
options;
|
|
21
27
|
disposed = false;
|
|
22
28
|
constructor(options = {}) {
|
|
29
|
+
const maxHandlersPerPattern = options.maxHandlersPerPattern ?? DEFAULT_MAX_HANDLERS_PER_PATTERN;
|
|
30
|
+
if (typeof maxHandlersPerPattern !== "number" ||
|
|
31
|
+
!Number.isFinite(maxHandlersPerPattern) ||
|
|
32
|
+
maxHandlersPerPattern < 0) {
|
|
33
|
+
throw new RangeError("maxHandlersPerPattern must be a non-negative finite number.");
|
|
34
|
+
}
|
|
23
35
|
this.options = {
|
|
24
36
|
allowDuplicateDefinitions: options.allowDuplicateDefinitions ?? false,
|
|
25
|
-
|
|
37
|
+
onDuplicateHandlerId: options.onDuplicateHandlerId ??
|
|
38
|
+
(options.allowDuplicateHandlerIds ? "replace" : "throw"),
|
|
39
|
+
maxHandlersPerPattern,
|
|
40
|
+
onWarning: options.onWarning ?? defaultWarning,
|
|
41
|
+
onError: options.onError,
|
|
26
42
|
};
|
|
27
43
|
}
|
|
28
44
|
register(definition) {
|
|
29
45
|
return registryRegister(definition, this.definitions, this.options, () => this.ensureActive(), (c) => this.notify(c));
|
|
30
46
|
}
|
|
31
47
|
registerHandler(eventType, handler, options = {}) {
|
|
32
|
-
return registryRegisterHandler(eventType, handler, options, this.handlers, this.options, () => this.ensureActive(), (c) => this.notify(c));
|
|
48
|
+
return registryRegisterHandler(eventType, handler, options, this.handlers, this.options, () => this.ensureActive(), (c) => this.notify(c), this.warnedPatterns);
|
|
33
49
|
}
|
|
34
50
|
get(eventType) {
|
|
35
51
|
this.ensureActive();
|
|
36
|
-
return this.definitions.get(eventType);
|
|
52
|
+
return this.definitions.get(normalizeRegistryEventType(eventType));
|
|
37
53
|
}
|
|
38
54
|
require(eventType) {
|
|
39
55
|
const definition = this.get(eventType);
|
|
@@ -44,14 +60,14 @@ export class EventRegistry {
|
|
|
44
60
|
}
|
|
45
61
|
has(eventType) {
|
|
46
62
|
this.ensureActive();
|
|
47
|
-
return this.definitions.has(eventType);
|
|
63
|
+
return this.definitions.has(normalizeRegistryEventType(eventType));
|
|
48
64
|
}
|
|
49
65
|
unregister(eventType) {
|
|
50
66
|
return registryUnregister(eventType, this.definitions, () => this.ensureActive(), (c) => this.notify(c));
|
|
51
67
|
}
|
|
52
68
|
getHandler(handlerId) {
|
|
53
69
|
this.ensureActive();
|
|
54
|
-
return this.handlers.get(handlerId);
|
|
70
|
+
return this.handlers.get(handlerId)?.registration;
|
|
55
71
|
}
|
|
56
72
|
requireHandler(handlerId) {
|
|
57
73
|
const handler = this.getHandler(handlerId);
|
|
@@ -64,8 +80,16 @@ export class EventRegistry {
|
|
|
64
80
|
this.ensureActive();
|
|
65
81
|
return this.handlers.has(handlerId);
|
|
66
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Returns the subscription created for a handler id, if the
|
|
85
|
+
* handler is still registered.
|
|
86
|
+
*/
|
|
87
|
+
getHandlerSubscription(handlerId) {
|
|
88
|
+
this.ensureActive();
|
|
89
|
+
return this.handlers.get(handlerId)?.subscription;
|
|
90
|
+
}
|
|
67
91
|
unregisterHandler(handlerId) {
|
|
68
|
-
return registryUnregisterHandler(handlerId, this.handlers, () => this.ensureActive()
|
|
92
|
+
return registryUnregisterHandler(handlerId, this.handlers, () => this.ensureActive());
|
|
69
93
|
}
|
|
70
94
|
getDefinitions() {
|
|
71
95
|
this.ensureActive();
|
|
@@ -98,6 +122,7 @@ export class EventRegistry {
|
|
|
98
122
|
}
|
|
99
123
|
clear() {
|
|
100
124
|
registryClear(this.definitions, this.handlers, () => this.ensureActive(), (c) => this.notify(c));
|
|
125
|
+
this.warnedPatterns.clear();
|
|
101
126
|
}
|
|
102
127
|
dispose() {
|
|
103
128
|
registryDispose(this.disposed, this.definitions, this.handlers, this.listeners, () => this.ensureActive(), (c) => this.notify(c));
|
|
@@ -107,11 +132,11 @@ export class EventRegistry {
|
|
|
107
132
|
return this.disposed;
|
|
108
133
|
}
|
|
109
134
|
notify(change) {
|
|
110
|
-
registryNotify(change, this.listeners);
|
|
135
|
+
registryNotify(change, this.listeners, this.options.onError);
|
|
111
136
|
}
|
|
112
137
|
ensureActive() {
|
|
113
138
|
if (this.disposed) {
|
|
114
|
-
throw new
|
|
139
|
+
throw new EventRegistryDisposedError();
|
|
115
140
|
}
|
|
116
141
|
}
|
|
117
142
|
}
|
|
@@ -1,11 +1,73 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Event registry type definitions for Zudojs.
|
|
3
3
|
*/
|
|
4
|
-
import type { EventDefinition, EventType } from "../eventTypes/eventDefinition.type.js";
|
|
5
|
-
import type {
|
|
4
|
+
import type { Event, EventDefinition, EventType } from "../eventTypes/eventDefinition.type.js";
|
|
5
|
+
import type { EventTypePattern } from "../eventTypes/eventType.type.js";
|
|
6
|
+
import type { EventHandlerLike, EventHandlerOptions, RegisteredEventHandler } from "../eventHandler/eventHandler.core.js";
|
|
7
|
+
import type { EventSubscription } from "../eventSubscription/eventSubscription.core.js";
|
|
8
|
+
/**
|
|
9
|
+
* Policy applied when a handler is registered with an id that
|
|
10
|
+
* is already in use.
|
|
11
|
+
*
|
|
12
|
+
* - "throw" → DuplicateEventHandlerError (default)
|
|
13
|
+
* - "replace" → the previous handler is unregistered (its
|
|
14
|
+
* subscription is cancelled) and the new one stored
|
|
15
|
+
*/
|
|
16
|
+
export type DuplicateHandlerIdPolicy = "throw" | "replace";
|
|
17
|
+
/**
|
|
18
|
+
* Default number of handlers allowed per event pattern before a
|
|
19
|
+
* leak warning is emitted.
|
|
20
|
+
*/
|
|
21
|
+
export declare const DEFAULT_MAX_HANDLERS_PER_PATTERN = 100;
|
|
22
|
+
/**
|
|
23
|
+
* Warning emitted when a registry limit is exceeded.
|
|
24
|
+
*/
|
|
25
|
+
export interface EventRegistryWarning {
|
|
26
|
+
readonly type: "handler.limit";
|
|
27
|
+
readonly pattern: EventTypePattern;
|
|
28
|
+
readonly count: number;
|
|
29
|
+
readonly limit: number;
|
|
30
|
+
readonly message: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Context passed to onError hooks.
|
|
34
|
+
*/
|
|
35
|
+
export interface EventRegistryErrorContext {
|
|
36
|
+
readonly source: "observer";
|
|
37
|
+
readonly change?: EventRegistryChange;
|
|
38
|
+
}
|
|
6
39
|
export interface EventRegistryOptions {
|
|
40
|
+
/**
|
|
41
|
+
* Allow re-registering an event type. The previous definition
|
|
42
|
+
* is replaced.
|
|
43
|
+
*/
|
|
7
44
|
readonly allowDuplicateDefinitions?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* @deprecated Use `onDuplicateHandlerId: "replace"`. When true,
|
|
47
|
+
* registering a handler id that already exists replaces the
|
|
48
|
+
* previous handler (it never allowed two handlers to coexist).
|
|
49
|
+
*/
|
|
8
50
|
readonly allowDuplicateHandlerIds?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* What to do when a handler id is already registered.
|
|
53
|
+
* Defaults to "throw".
|
|
54
|
+
*/
|
|
55
|
+
readonly onDuplicateHandlerId?: DuplicateHandlerIdPolicy;
|
|
56
|
+
/**
|
|
57
|
+
* Maximum handlers per event pattern before a leak warning is
|
|
58
|
+
* emitted through `onWarning` (or console.warn). Use 0 to
|
|
59
|
+
* disable. Defaults to 100.
|
|
60
|
+
*/
|
|
61
|
+
readonly maxHandlersPerPattern?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Receives limit warnings. Defaults to console.warn.
|
|
64
|
+
*/
|
|
65
|
+
readonly onWarning?: (warning: EventRegistryWarning) => void;
|
|
66
|
+
/**
|
|
67
|
+
* Receives errors thrown by registry observers, which are
|
|
68
|
+
* otherwise swallowed so they cannot break registry mutations.
|
|
69
|
+
*/
|
|
70
|
+
readonly onError?: (error: unknown, context: EventRegistryErrorContext) => void;
|
|
9
71
|
}
|
|
10
72
|
export declare enum EventRegistryChangeType {
|
|
11
73
|
EVENT_REGISTERED = "event.registered",
|
|
@@ -25,4 +87,27 @@ export interface RegisteredEventDefinition<TType extends EventType = EventType,
|
|
|
25
87
|
readonly definition: EventDefinition<TType, TPayload>;
|
|
26
88
|
readonly registeredAt: Date;
|
|
27
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Internal handler entry: the registration plus the subscription
|
|
92
|
+
* handed to the caller, so the registry can cancel it.
|
|
93
|
+
*/
|
|
94
|
+
export interface EventHandlerEntry {
|
|
95
|
+
readonly registration: RegisteredEventHandler;
|
|
96
|
+
readonly subscription: EventSubscription;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Minimal handler store contract used by EventEmitter.
|
|
100
|
+
*
|
|
101
|
+
* EventRegistry implements it; the emitter stores its handlers in
|
|
102
|
+
* a registry so that a bus has a single source of truth for
|
|
103
|
+
* handlers.
|
|
104
|
+
*/
|
|
105
|
+
export interface EventHandlerStore {
|
|
106
|
+
registerHandler<TEvent extends Event = Event>(eventType: EventTypePattern, handler: EventHandlerLike<TEvent>, options?: Omit<EventHandlerOptions, "eventType">): EventSubscription;
|
|
107
|
+
unregisterHandler(handlerId: string): boolean;
|
|
108
|
+
hasHandler(handlerId: string): boolean;
|
|
109
|
+
getHandlers(): readonly RegisteredEventHandler[];
|
|
110
|
+
getHandlersForEvent(event: Event): readonly RegisteredEventHandler[];
|
|
111
|
+
readonly handlerCount: number;
|
|
112
|
+
}
|
|
28
113
|
//# sourceMappingURL=eventRegistry.type.d.ts.map
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Event registry type definitions for Zudojs.
|
|
3
3
|
*/
|
|
4
|
+
/**
|
|
5
|
+
* Default number of handlers allowed per event pattern before a
|
|
6
|
+
* leak warning is emitted.
|
|
7
|
+
*/
|
|
8
|
+
export const DEFAULT_MAX_HANDLERS_PER_PATTERN = 100;
|
|
4
9
|
export var EventRegistryChangeType;
|
|
5
10
|
(function (EventRegistryChangeType) {
|
|
6
11
|
EventRegistryChangeType["EVENT_REGISTERED"] = "event.registered";
|
|
@@ -135,6 +135,11 @@ export declare class EventSubscriptionGroup implements EventSubscription {
|
|
|
135
135
|
remove(subscription: EventSubscription): boolean;
|
|
136
136
|
/**
|
|
137
137
|
* Unsubscribes every subscription in the group.
|
|
138
|
+
*
|
|
139
|
+
* Every subscription is attempted even if one throws; the
|
|
140
|
+
* failures are then re-thrown together as an AggregateError and
|
|
141
|
+
* the failed subscriptions stay in the group so the call can be
|
|
142
|
+
* retried.
|
|
138
143
|
*/
|
|
139
144
|
unsubscribe(): void;
|
|
140
145
|
/**
|