Type-safe event emitter with wildcards, async support, and backpressure. Zero deps. TypeScript-first.
npm install @corvid-agent/emitterimport { Emitter } from "@corvid-agent/emitter";
type Events = {
"user:login": { id: string; name: string };
"user:logout": { id: string };
"error": Error;
};
const emitter = new Emitter<Events>();
// Subscribe — fully typed payload
emitter.on("user:login", (payload) => {
console.log(`${payload.name} logged in`);
});
// Emit
emitter.emit("user:login", { id: "42", name: "Alice" });const sub = emitter.on("user:login", handler);
// Later:
sub.off();emitter.once("ready", (payload) => {
console.log("Fired once, then auto-removed");
});* matches a single segment. ** matches one or more segments. Segments are delimited by : or ..
// Matches "user:login", "user:logout", etc.
emitter.on("user:*", (payload) => { ... });
// Matches "user:profile:update", "user:a:b:c", etc.
emitter.on("user:**", (payload) => { ... });
// Catch-all — matches every event
emitter.on("**", (payload) => { ... });Higher priority listeners fire first. Default priority is 0.
emitter.on("save", backupHandler, { priority: 10 });
emitter.on("save", logHandler, { priority: 1 });
// backupHandler fires before logHandleremitAsync() awaits each listener sequentially in priority order.
emitter.on("data:save", async (record) => {
await db.insert(record);
});
emitter.on("data:save", async (record) => {
await cache.invalidate(record.id);
});
// Both complete before emitAsync resolves
await emitter.emitAsync("data:save", record);By default, if a listener throws, the error propagates and stops subsequent listeners from firing. Pass an onError handler to catch errors per-listener, allowing remaining listeners to still execute.
const emitter = new Emitter<Events>({
onError: (err, event) => console.error(`Error in ${event}:`, err),
});
emitter.on("save", () => {
throw new Error("db failure");
});
emitter.on("save", () => {
console.log("still runs");
});
emitter.emit("save", record); // both listeners are calledThis works for both emit() and emitAsync().
Buffer events while paused, replay them on resume.
emitter.pause();
emitter.emit("tick", 1); // buffered
emitter.emit("tick", 2); // buffered
emitter.resume(); // both fire now, in orderUse resumeAsync() to await async listeners during replay.
const payload = await emitter.wait("user:login");
console.log(payload.id);Compose emitters by piping events from one to another.
const source = new Emitter<Events>();
const sink = new Emitter<Events>();
// Forward all events
const sub = source.pipe(sink);
// Forward specific events only
source.pipe(sink, ["user:login", "user:logout"]);
// Forward by wildcard pattern
source.pipe(sink, "user:*");
// Disconnect the pipe
sub.off();Pipes can be chained: a.pipe(b) then b.pipe(c) forwards events from a through b to c.
Create a new emitter. T is an EventMap — a record of event names to payload types.
| Option | Type | Default | Description |
|---|---|---|---|
onError |
(error: unknown, event: string) => void |
undefined |
Called when a listener throws. When set, errors are caught per-listener so remaining listeners still execute. |
Subscribe to an event. Returns a Subscription with an off() method.
| Option | Type | Default | Description |
|---|---|---|---|
once |
boolean |
false |
Auto-remove after first firing |
priority |
number |
0 |
Higher values fire first |
Shorthand for .on(event, listener, { once: true }).
Emit synchronously. Returns true if any listener was called.
Emit and await all listeners sequentially.
Pause emission. Events are buffered until resume().
Resume and replay buffered events synchronously.
Resume and replay buffered events, awaiting each.
Returns a promise that resolves on the next emission of event.
Number of listeners for a specific event (excludes wildcards).
Array of event names with registered listeners (excludes wildcards).
Forward events to another emitter. filter can be:
- omitted — forward all events
string[]— forward only the listed event namesstring— forward events matching a wildcard pattern
Returns a Subscription whose off() disconnects the pipe.
Remove listeners for a specific event, or all listeners if no event given.
Remove all listeners, drain buffers, unpause.
Set max listeners per event before a warning is logged. Default: 10. Set to 0 to disable.
Set max buffer size for paused events. Default: 1000.
Standalone utility. Test if a wildcard pattern matches an event name.
MIT