node:events

Elide implements [Node.js EventEmitter][nodejs-events] — the synchronous pub/sub primitive that backs process, streams, and most other Node objects. Construction, listener registration, emission, removal, and the static EventEmitter.once / EventEmitter.listenerCount helpers all match Node's documented semantics.

[nodejs-events]: https://nodejs.org/api/events.html

Module surface

ts
declare class EventEmitter {
  static defaultMaxListeners: number;            // default: 10

  static once(emitter: EventEmitter, eventName: string, options?: { signal?: AbortSignal }): Promise<unknown[]>;
  static listenerCount(emitter: EventEmitter, eventName: string): number;

  constructor(options?: { captureRejections?: boolean });

  on(eventName: string, listener: (...args: unknown[]) => void): this;
  addListener(eventName: string, listener: (...args: unknown[]) => void): this;
  once(eventName: string, listener: (...args: unknown[]) => void): this;
  prependListener(eventName: string, listener: (...args: unknown[]) => void): this;
  prependOnceListener(eventName: string, listener: (...args: unknown[]) => void): this;
  off(eventName: string, listener: (...args: unknown[]) => void): this;
  removeListener(eventName: string, listener: (...args: unknown[]) => void): this;
  removeAllListeners(eventName?: string): this;

  emit(eventName: string, ...args: unknown[]): boolean;

  listenerCount(eventName: string): number;
  listeners(eventName: string): Function[];
  rawListeners(eventName: string): Function[];   // once-wrappers preserved; `.listener` accessor available
  eventNames(): string[];

  setMaxListeners(n: number): this;              // 0 means unlimited
  getMaxListeners(): number;
}

export = EventEmitter;
require('node:events') returns the EventEmitter class itself; require('node:events').EventEmitter is a self-reference so destructuring imports work both ways.

Semantics

  • Listener invocation is synchronous and in registration order; emit returns true when at least one listener fired and false when the bucket was empty.
  • 'error' event with no listener rethrows the first emitted argument as a guest exception, matching Node's documented behaviour. Always register an error listener.
  • once-wrappers are guest functions that auto-remove themselves on first fire and expose the original listener at .listener (Node convention). rawListeners(name) returns the wrapper array; listeners(name) returns the unwrapped (user-visible) array.
  • 'newListener' and 'removeListener' meta-events fire synchronously during registration changes and are suppressed for recursion on themselves (registering a newListener listener does not re-trigger).
  • emit is variadic: every argument after the event name is forwarded to listeners verbatim, with this bound to the emitter for non-arrow listener functions.

Static EventEmitter.once(emitter, name, options?)

Returns a Promise that resolves with the array of arguments from the next emission of name, or rejects on 'error'. Supports options.signal for cancellation when the signal is already aborted at construction time. Cooperative cancellation through a signal that aborts after the Promise was created is not yet wired — register your own signal.onabort for that case.

Module helpers

ExportTypeNotes
EventEmitterconstructorThe class itself; default and named export are the same identity.
once(em, name, options?) => PromiseSame as EventEmitter.once.
listenerCount(em, name) => numberSame as EventEmitter.listenerCount. Deprecated in Node in favour of em.listenerCount(name) but kept for source compatibility.

Implementation notes

  • Listener storage is a per-instance insertion-ordered map (Map), walked front-to-back during emit. Snapshots are taken before iteration so once self-removal and reentrant on/off during dispatch don't disturb the active walk.
  • setMaxListeners(0) is the documented Node sentinel for "unlimited"; we honour it. Otherwise exceeding the per-event limit prints the classic MaxListenersExceededWarning to stderr and continues — matching Node's "warn-not-throw" stance.
  • EventEmitter.defaultMaxListeners is stored per realm — each realm has its own value (default 10), shared across every emitter in that realm that hasn't overridden its own ceiling. It is writable: assigning EventEmitter.defaultMaxListeners = N updates the realm default, and a negative value throws a RangeError.
  • The captureRejections constructor option is accepted but currently ignored (a no-op); a rejected promise from an async listener is not redirected to an error event.