Framework events + tense-based dispatch
@nwire/app ships a FrameworkEventBus that fires events at every lifecycle moment. Plugins subscribe with on(EventName, handler).
The tense of the event name picks the dispatch mode:
| Event name | Tense | Dispatch mode |
|---|---|---|
AppRegistering | "is registering" | series-bail (a plugin can reject) |
AppBooting | "is booting" | series-bail |
AppBooted | "has booted" | parallel |
AppReady | "is ready" | parallel |
AppShuttingDown | "is shutting down" | series (ordered) |
AppShutdown | "has shut down" | parallel |
Why tense
The dispatch mode usually matches the tense:
- Past tense ("X has happened") →
parallel. The thing already happened; listeners react, can't undo. - Present continuous ("X is happening") →
series-bail. The thing is in flight; a listener can throw to abort. - Imperative ("X") →
series. Ordered side effects.
Naming with tense in mind means the listener author already knows what mode to expect.
Example
ts
import { definePlugin } from "@nwire/app"
const tracingPlugin = definePlugin("tracing", ({ on, boot }) => {
on("AppBooting", ({ app }) => {
if (!process.env.OTEL_EXPORTER_OTLP_ENDPOINT) {
throw new Error("OTEL endpoint not configured") // aborts boot
}
})
on("AppBooted", ({ app }) => {
console.log(`booted in ${app.bootMs}ms`)
})
on("AppShuttingDown", async () => {
await tracer.forceFlush()
})
})Action-level events
@nwire/forge adds these on top:
| Event | Mode | Use case |
|---|---|---|
ActionAboutToDispatch | series-bail | Authorization, validation, rate limiting |
ActionWasDispatched | parallel | Audit, metrics, tracing |
ActionFailed | parallel | Error reporting, retry decisions |
EventWasPublished | parallel | Outbox, projections, reactions |
ActorTransitioned | parallel | Studio Live, state-change audits |
See @nwire/forge source for the full list.