@vielzeug/herald
v1.0.3
Published
Typed event bus — pub/sub with namespaces, wildcards, and once-listeners
Readme
@vielzeug/herald
Zero-dependency typed event bus with subscribe/emit, wait(), async streams, AbortSignal support, bus piping, and test helpers.
Package: @vielzeug/herald · Category: Events
Key exports: createBus, pipeEvents, createTestBus
When to use: Decoupled inter-module communication via a typed event bus. Supports subscribe/emit, one-time await, async iteration, event piping, and AbortSignal lifecycle management.
Related: @vielzeug/ripple · @vielzeug/wayfinder · @vielzeug/familiar
@vielzeug/herald is part of Vielzeug and ships as a zero-dependency TypeScript package with ESM+CJS output.
Installation
pnpm add @vielzeug/herald
npm install @vielzeug/herald
yarn add @vielzeug/heraldQuick Start
import { BusDisposedError, createBus, pipeEvents } from '@vielzeug/herald';
type AppEvents = {
'user:login': { userId: string; email: string };
'user:logout': void;
};
const bus = createBus<AppEvents>();
// Subscribe
bus.on('user:login', ({ userId }) => {
console.log('Logged in:', userId);
});
// Emit
bus.emit('user:login', { email: '[email protected]', userId: '42' });
bus.emit('user:logout');
// Await the next event
const nextLogin = await bus.wait('user:login');
// Race multiple events — result is a discriminated union
const nextSessionEvent = await bus.waitAny(['user:login', 'user:logout'] as const);
if (nextSessionEvent.event === 'user:login') {
console.log(nextSessionEvent.payload.userId);
}
// Stream all future emits as an async generator
for await (const payload of bus.events('user:login', { signal: AbortSignal.timeout(5_000) })) {
console.log(payload.email);
}
// Forward selected events from one bus to another
const auditBus = createBus<AppEvents>();
const unpipe = pipeEvents(bus, auditBus, ['user:login', 'user:logout']);
// unpipe() — stop forwarding; also stops automatically when either bus disposes
// Disposal signal — fire external cleanup when the bus is torn down
otherBus.on('count', handler, { signal: bus.disposalSignal });
// Handle disposal in async code
try {
await bus.wait('user:login', { signal: AbortSignal.timeout(1_000) });
} catch (err) {
if (err instanceof BusDisposedError) {
console.log('Bus was disposed');
}
}Documentation
License
MIT © Helmuth Saatkamp — part of the Vielzeug monorepo.
