Typed DOM-style event dispatching with cross-platform-consistent behavior.
evt provides a small EventTarget-compatible dispatcher for libraries that
want:
- typed event maps
- predictable listener dispatch behavior
- no dependence on runtime-specific
EventTargetinternals - an optional cancelable
"error"event for listener failures
pnpm add @hornjs/evtimport { EventDispatcher } from "@hornjs/evt";
type Events = {
ping: Event;
message: CustomEvent<{ text: string }>;
};
const dispatcher = new EventDispatcher<Events>();
dispatcher.addEventListener("message", function (event) {
console.log(event.detail.text);
console.log(this === dispatcher); // true
});
dispatcher.dispatchEvent(
new CustomEvent("message", {
detail: { text: "hello" },
}),
);Listener this is typed as the concrete dispatcher instance, so subclasses keep
their own instance type.
class Bus extends EventDispatcher<{ ping: Event }> {
label = "bus";
}
const bus = new Bus();
bus.addEventListener("ping", function () {
console.log(this.label); // "bus"
});By default, listener exceptions are reported through the global error reporting channel and do not abort the current dispatch loop.
If you want thrown listener errors to first dispatch a cancelable "error"
event, enable dispatchErrorEvent.
import { EventDispatcher, EventDispatcherErrorEvent } from "@hornjs/evt";
type Events = {
ping: Event;
error: EventDispatcherErrorEvent;
};
const dispatcher = new EventDispatcher<Events>({
dispatchErrorEvent: true,
});
dispatcher.addEventListener("error", (event) => {
console.error(event.error);
console.log(event.causeEvent.type); // "ping"
event.preventDefault(); // marks the error as handled
});If an "error" event is not canceled, the original exception is still reported
globally.
- Event names are string keys from the event map.
- Listener identity matches the DOM model:
listener + capture. onceandsignalbehave likeaddEventListener().- Dispatch models the at-target phase only. There is no bubbling tree.
event.target,event.currentTarget, andevent.eventPhaseare patched to follow DOM-style dispatch semantics.- The implementation uses its own listener registry instead of delegating to the
host runtime's
EventTarget, so behavior stays consistent across platforms.
EventDispatcher<EventMap>EventDispatcherOptionsEventDispatcherErrorEventEVENT_PHASE_NONEEVENT_PHASE_AT_TARGET