Skip to content
herald logoHeraldEvents
Typed synchronous event delivery with wildcard subscriptions, one-shot waits, tracing, and AbortSignal lifecycle.
Version
v3.0.0
Size
1.4 KB gzip
Dependencies
Zero dependencies
BrowserNode ≥22SSRDeno
createBusHeraldErrorBusDisposedErrorHeraldConfigError

Why Herald?

Raw event emitters lose payload inference and leave one-shot waiting, cancellation, diagnostics, and teardown to every caller. Herald keeps events temporal: use Ripple when you need retained state.

ts
// Before
const listeners = new Set<(payload: unknown) => void>();
listeners.add((payload) => loadProfile((payload as { id: string }).id));

// After
import { createBus } from '@vielzeug/herald';

interface AppEvents {
  'user:login': { id: string };
}

function loadProfile(id: string): void {
  console.log(id);
}

const bus = createBus<AppEvents>();
bus.on('user:login', ({ id }) => loadProfile(id));

Delivery is synchronous and temporal: events are not retained or replayed. Listener failures do not stop later listeners; emit() rethrows the first failure after dispatch, while tap() exposes every failure for diagnostics.

FeatureHeraldmittEventEmitter3
Bundle size1.4 KB~200 B~1.5 kB
Typed payloads
One-shot async wait
AbortSignal lifecycle
Runtime activity tracing
Zero dependencies

Use Herald when modules need typed temporal event delivery with owned lifecycle.

Consider Ripple when consumers need current state and replayed values.

Installation

sh
pnpm add @vielzeug/herald
sh
npm install @vielzeug/herald
sh
yarn add @vielzeug/herald

Quick Start

ts
import { createBus } from '@vielzeug/herald';

interface AppEvents {
  'user:login': { id: string };
  'user:logout': void;
}

const bus = createBus<AppEvents>();
const stop = bus.on('user:login', ({ id }) => console.log(id));

bus.emit('user:login', { id: '42' });
stop();
bus.dispose();

Features

  • on() / once() — typed subscriptions with explicit teardown
  • onAny() — cross-cutting event observation
  • tap() — observe bus activity for logging and diagnostics
  • wait() / waitAny() — one-shot async coordination
  • listenerCount() / wildcardCount() / eventNames() — listener inspection
  • error tap events — isolate listener failures without interrupting delivery
  • AbortSignal — subscription, wait, tap, and disposal ownership
  • createTestBus() — emitted-payload recording for tests

Documentation

See Also