Getting started
Install Fox Events, create a typed channel, and start emitting and listening in a few lines.
Install
npm install fox-eventsyarn add fox-eventsQuick example
Create a channel with a payload type, then use emit, on, and once as you need:
import { Fox } from "fox-events";
const userLogin = Fox.channel<{ userId: string }>("user:login");
userLogin.on((payload) => {
console.log("Login:", payload.userId);
});
userLogin.emit({ userId: "u-1" });
const first = await userLogin.once();
console.log("First login:", first.userId);For the mental model (who publishes, who listens, channel, on vs once, timeout), see Publishers and listeners.
Instance API (channel)
Use Fox.channel<T>(name) when you want a long-lived channel with typed payloads. You get emit, emitAsync, on, once, and trail.
const channel = Fox.channel<{ userId: string }>("user:login");
channel.emit({ userId: "u-1" });
await channel.emitAsync({ userId: "u-2" });
const off = channel.on((payload) => {
console.log(payload.userId);
});
off();
const payload = await channel.once();
const payloadWithTimeout = await channel.once({ timeout: 5000 });
const history = channel.trail();
console.log(history.published, history.received, history.unsubscribed);
channel.clearTrail();For trail options (maxHistory), types, and toText(), see Trail (History).
Sync and async
emit(name, payload)— Fires immediately and returns void. Listeners run synchronously; the caller does not wait. Use when you don’t need to wait for middleware or persistence (e.g. UI events, navigation).
emitAsync(name, payload)— Returns a Promise that resolves after all middleware (and any persistence) have run. Use when the event goes through middleware (e.g. storage, Bridge) and you need to wait for it to be processed before continuing (e.g. “save then redirect”).
// Fire-and-forget: UI or in-memory only
channel.emit("user:login", { userId: "u-1" });
// Wait for persistence / middleware (e.g. IndexedDB, Bridge to RN)
await channel.emitAsync("user:login", { userId: "u-1" });
// Now safe to navigate or read from storageWithout middleware, emit and emitAsync behave the same for listeners; the only difference is that emitAsync returns a Promise you can await. With middleware (e.g. storage), emitAsync waits until the middleware has finished.
Static API
When you don’t need a channel instance, use Fox.emit, Fox.on, and Fox.once with the event name:
Fox.emit("app:ready", { version: "1.0" });
await Fox.emitAsync("app:ready", { version: "1.0" });
const off = Fox.on("app:ready", (payload) => {});
off();
const payload = await Fox.once("app:ready");
const payloadWithTimeout = await Fox.once("app:ready", { timeout: 3000 });Custom EventTarget
You can inject your own EventTarget (e.g. for testing or scoping):
const customTarget = new EventTarget();
const channel = Fox.channel("user:action", {
deps: { eventTarget: customTarget },
});