Recipes
Idiomatic compositions.
Feature-scoped lifecycle
function mountUsersFeature() { events.subscribe("users.created", onCreate); events.subscribe("users.updated", onUpdate); events.subscribe("users.deleted", onDelete);}
function unmountUsersFeature() { events.unsubscribeNamespace("users");}Veto / “before” hooks
events.subscribe("save.before", (data) => { if (!isValid(data)) return false;});
events.subscribe("save.before", (data) => { if (containsSecrets(data)) return false;});
const ok = events.trigger("save.before", payload);if (ok === false) { // some hook vetoed it return;}performSave(payload);events.trigger("save.after", payload);Aggregating responses
// Each plugin contributes a column definition.events.subscribe("table.columns", () => ({ field: "name", label: "Name" }));events.subscribe("table.columns", () => ({ field: "email", label: "Email" }));
const { results } = events.triggerAll("table.columns");// results === [{field: "name", ...}, {field: "email", ...}]Async chains
events.subscribe("file.uploaded", async (file) => { await scanForViruses(file);});events.subscribe("file.uploaded", async (file) => { await generateThumbnail(file);});
// Sequential — second handler waits for first to settle.await events.triggerAsync("file.uploaded", uploaded);For parallel dispatch, use subscriptions + Promise.all:
await Promise.all( events.subscriptions("file.uploaded").map(s => s.dispatch(uploaded)),);Disposable subscriptions in React (without @mongez/react-atom)
import events from "@mongez/events";import { useEffect } from "react";
function Notifications() { useEffect(() => { const sub = events.subscribe("toast.show", showToast); return () => sub.unsubscribe(); }, []); return null;}If you’re already using @mongez/react-atom, prefer atoms with onChange over raw events — they give you typed state with the same subscribe / unsubscribe ergonomics.
Test teardown
import events from "@mongez/events";
afterEach(() => { events.unsubscribe(); // wipes the whole bus});Debugging — list live subscriptions under a namespace
const snapshot = events.getByNamespaceArray("users");console.table(snapshot.map(e => ({ event: e.event, count: e.subscriptions.length })));getByNamespaceArray matches at segment boundaries, so you need a real
namespace prefix — passing "" matches nothing because the matcher is
event === namespace || event.startsWith(namespace + ".") and no real
event name starts with .. There is no public “every event” iterator;
for full-bus introspection, expose a helper from your own side or walk
the namespaces you actually subscribe under (atoms, users, etc.).