@openally/emitt
v1.0.1
Published
Type-safe EventEmitter for Node.js and Browser
Readme
Emitter<Events> is a drop-in-shaped EventEmitter with no dependency on Node's events module or the DOM EventTarget: it runs unmodified in Node.js and the browser, and every method is typed against your own Events map instead of any[].
Requirements
- Node.js v24 or higher
Getting Started
This package is available in the Node Package Repository and can be easily installed with npm or yarn.
$ npm i @openally/emitt
# or
$ yarn add @openally/emittUsage example
import { Emitter } from "@openally/emitt";
interface MyEvents {
connect: (host: string) => void;
[key: symbol]: (payload: unknown) => void;
}
const emitter = new Emitter<MyEvents>();
emitter.on("connect", (host) => {
console.log(`connected to ${host}`);
});
emitter.emit("connect", "localhost");Events defaults to an untyped string | symbol event map when omitted:
type EventMap = Record<string | symbol, (...args: any[]) => void>;Table of contents
Emitter API
Constructor
class Emitter<Events extends EventMap = Record<string | symbol, (...args: any[]) => void>> {
constructor();
}Both string and symbol keys are supported for event names.
const emitter = new Emitter<MyEvents>();addListener, on
addListener<E extends keyof Events>(event: E, listener: Events[E]): this;
on<E extends keyof Events>(event: E, listener: Events[E]): this;Register a listener for event. on is an alias of addListener. Warns via console.warn once the listener count for that event exceeds getMaxListeners() (see getMaxListeners, setMaxListeners).
emitter.on("connect", (host) => console.log(host));once
once<E extends keyof Events>(event: E, listener: Events[E]): this;Register a listener that is automatically removed after firing a single time.
emitter.once("connect", (host) => console.log(host));
emitter.emit("connect", "localhost"); // logs "localhost"
emitter.emit("connect", "localhost"); // listener already removed, does nothingprependListener
prependListener<E extends keyof Events>(event: E, listener: Events[E]): this;Same as on, but inserts the listener at the beginning of the list instead of the end.
emitter.on("bar", () => order.push("second"));
emitter.prependListener("bar", () => order.push("first"));
emitter.emit("bar"); // order === ["first", "second"]prependOnceListener
prependOnceListener<E extends keyof Events>(event: E, listener: Events[E]): this;Combines prependListener and once: runs first, and only once.
off, removeListener
off<E extends keyof Events>(event: E, listener: Events[E]): this;
removeListener<E extends keyof Events>(event: E, listener: Events[E]): this;Remove a previously registered listener. off is an alias of removeListener. Works with the original function reference even when it was registered via once/prependOnceListener.
function listener(host: string) {
console.log(host);
}
emitter.on("connect", listener);
emitter.off("connect", listener);removeAllListeners
removeAllListeners<E extends keyof Events>(event?: E): this;Remove every listener for event, or every listener for every event when called without an argument.
emitter.removeAllListeners("connect");
emitter.removeAllListeners(); // clears everythingemit
emit<E extends keyof Events>(event: E, ...args: Parameters<Events[E]>): boolean;Synchronously call every listener registered for event with the given arguments, in registration order. Returns true if there was at least one listener, false otherwise.
emitter.emit("connect", "localhost"); // true
emitter.emit("unknown-event"); // false, no listenerseventNames
eventNames(): (keyof Events | string | symbol)[];List the event names (string and symbol) that currently have at least one listener.
emitter.eventNames(); // ["connect", Symbol(baz)]rawListeners
rawListeners<E extends keyof Events>(event: E): Events[E][];Same as listeners, but returns the internal wrapper function for listeners registered via once/prependOnceListener instead of the original function reference.
listeners
listeners<E extends keyof Events>(event: E): Events[E][];Return a copy of the listeners registered for event, unwrapping once listeners back to the original function passed in.
emitter.once("connect", listener);
emitter.listeners("connect"); // [listener]listenerCount
listenerCount<E extends keyof Events>(event: E): number;Number of listeners currently registered for event.
getMaxListeners, setMaxListeners
getMaxListeners(): number;
setMaxListeners(maxListeners: number): this;Get/set the listener-count threshold that triggers the memory-leak warning (default 10, matching Node's EventEmitter). Pass 0 to disable the warning.
emitter.setMaxListeners(0); // disable the warningHelpers
Standalone helpers inspired by Node's events module, typed against a given Emitter<Events> instance (resolved argument/tuple types instead of Node's any[]).
import { Emitter, once, on, addAbortListener } from "@openally/emitt";once (helper)
function once<Events extends EventMap, E extends keyof Events>(
emitter: Emitter<Events>,
event: E,
options?: AbortOptions
): Promise<Parameters<Events[E]>>;Resolve with the typed argument tuple of the next matching emit().
const [host] = await once(emitter, "connect");Accepts an { signal } option (AbortOptions) to cancel waiting via an AbortSignal; rejects with the signal's reason (or a generic abort Error) when aborted, and always removes its internal listener on cleanup/rejection.
on (helper)
function on<Events extends EventMap, E extends keyof Events>(
emitter: Emitter<Events>,
event: E,
options?: AbortOptions
): AsyncGenerator<Parameters<Events[E]>, void, void>;Async-iterate every occurrence of event, queuing emissions that happen faster than they are consumed.
for await (const [host] of on(emitter, "connect")) {
console.log(host);
}Same { signal } option as once; throws with the signal's reason (or a generic abort Error) when aborted, and always removes its internal listener when the loop exits.
addAbortListener
function addAbortListener(signal: AbortSignal, listener: (event: Event) => void): Disposable;Independent of Emitter — listens once for signal's abort event (firing immediately via queueMicrotask if already aborted) and returns a Disposable, so it works with using.
function example(signal: AbortSignal) {
using _ = addAbortListener(signal, () => {
// cleanup
});
}License
MIT
