Skip to content

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 nameTenseDispatch 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:

EventModeUse case
ActionAboutToDispatchseries-bailAuthorization, validation, rate limiting
ActionWasDispatchedparallelAudit, metrics, tracing
ActionFailedparallelError reporting, retry decisions
EventWasPublishedparallelOutbox, projections, reactions
ActorTransitionedparallelStudio Live, state-change audits

See @nwire/forge source for the full list.

See also

MIT licensed.