Application events

white-label-mediator

A small synchronous standards-based EventTarget for exchanging named application intent without direct module coupling.

Install and requirements

  • Node.js ^22.18.0 or >=24.11.0 for installation/development
  • Native EventTarget, CustomEvent, AbortController, and AbortSignal.any support
  • No window or document dependency
import Mediator from 'white-label-mediator';
const mediator = new Mediator();

Publish and subscribe

Delivery is synchronous through the native EventTarget contract. Payloads belong in CustomEvent.detail, and dispatching with no subscribers is valid.

The publisher does not know who listens. Keep callback references so the owner of a subscription can remove it later. dispatchEvent() returns native cancellation state, not whether a subscriber existed.

const renderCart = event => console.log(event.detail.quantity);
mediator.addEventListener('cart:updated', renderCart);
mediator.dispatchEvent(new CustomEvent('cart:updated', {detail: {quantity: 3}}));

Lifecycle and ownership

Individual components should remove only their own subscriptions with removeEventListener(). destroy() is for the mediator itself leaving the application and removes listeners owned by that mediator instance.

Typed event maps constrain listener registration and narrow CustomEvent.detail inside typed callbacks; dispatchEvent() stays native and payload construction remains application-owned.

type Events = {
    ready: string;
};
const mediator = new Mediator<Events>();

White Label integrations

Router can listen for router:navigate CustomEvents and reads navigation metadata from event.detail. Model can relay namespaced model:<name>:<event> CustomEvents to any structurally compatible EventTarget.

Mediator does not own payload schemas, state changes, rendering, navigation, or lifecycle policy outside its own listeners.

Serverless event scope

Mediator is useful for decoupling modules inside one request or function instance. Prefer a request-scoped Mediator when listeners or payloads are request-specific, and destroy it when that request-owned lifecycle ends.

Mediator is an in-memory event bus, not distributed infrastructure. Use the cloud platform or another external system for queues, pub/sub, durable events, retries, or communication between separate function instances.

Mediator API

new Mediator<Events>()

Create an independent EventTarget-based application event bus, optionally with a compile-time detail map.

Returns
A Mediator instance.
Runtime
Browser + Node.js

initialize()

Start the lifecycle for composition/chaining.

Returns
The same Mediator instance.
Runtime
Browser + Node.js

addEventListener(type, callback, options?)

Subscribe using native EventTarget semantics; typed maps narrow CustomEvent.detail for known event names.

ParameterTypeDefaultDescription
typestring—Event name.
callbackEventListener | EventListenerObject—Listener invoked synchronously during dispatch.
optionsboolean | AddEventListenerOptions—Native capture, once, passive, and signal options.
Returns
undefined, matching EventTarget.
Runtime
Browser + Node.js

removeEventListener(type, callback, options?)

Release an owned subscription using native EventTarget matching semantics.

Returns
undefined, matching EventTarget.
Runtime
Browser + Node.js

dispatchEvent(event)

Synchronously dispatch an Event or CustomEvent through the native EventTarget signature.

ParameterTypeDefaultDescription
eventEvent | CustomEvent—Application event; put payload data in CustomEvent.detail.
Returns
Native EventTarget boolean: false only when a cancelable event was canceled; otherwise true.
Runtime
Browser + Node.js
  • The return value does not indicate whether any listener received the event.

destroy()

Remove every listener owned by this mediator instance and reset lifecycle tracking for reuse.

Returns
The same Mediator instance after cleanup.
Runtime
Browser + Node.js

TypeScript

Mediator<Events> narrows addEventListener() event names and typed listener CustomEvent.detail values at compile time.

dispatchEvent() remains the native EventTarget method; event maps do not type-check publication payloads or validate them at runtime.

Security and trust boundaries

Mediator is an in-process coordination primitive, not an authorization or tenant/request-isolation boundary.

CustomEvent.detail is passed by reference; Mediator does not clone, freeze, sanitize, or validate it.

Code that holds a privileged mediator can dispatch events or call destroy(), and synchronous listeners can block or throw according to native EventTarget behavior. Do not expose a privileged mediator directly to untrusted plugins.

Design boundaries

Mediator dispatches named application events. It does not own application state, rendering, routing, networking, persistence, or business behavior.