@nwire/app
v0.15.1
Published
Nwire — managed Container with plugin lifecycle, framework hooks, and DI. Composes apps + plugins, boots in order, exposes a Container, fires framework hooks at every lifecycle transition.
Readme
@nwire/app
The composition root.
createApp,appCompose,definePlugin, runtime, framework events.
pnpm add @nwire/appQuick start
import { createApp } from "@nwire/app";
import { post } from "@nwire/wires/http";
import { z } from "zod";
const app = createApp({ appName: "api" });
app.wire(post("/hello", { body: z.object({ name: z.string() }) }), async (input) => ({
message: `Hello, ${input.name}!`,
}));createApp builds an App — a bounded context with its container,
plugins, and wires. The App is the unit you endpoint().mount() later.
Plugins
definePlugin(name, setup) declares an installable plugin. Plugins
receive a PluginContext with bind (register on the container) and
dispose (run on shutdown):
import { definePlugin } from "@nwire/app";
const todoStorePlugin = () =>
definePlugin("todo-store", ({ bind, dispose }) => {
const store = new TodoStore();
bind("todos", store);
dispose(async () => store.close());
});
const app = createApp({
appName: "todo",
plugins: [todoStorePlugin()],
});The runtime boots plugins in declaration order and disposes them in reverse on shutdown.
Multi-app composition
Apps don't have to be the whole process. appCompose(...apps) merges
several Apps into one — each keeps its own container and plugins;
adopters see the merged wire collection but routes back to the source
app's container per dispatch.
import { appCompose, createApp } from "@nwire/app";
const ordersApp = createApp({ appName: "orders" /* ... */ });
const inventoryApp = createApp({ appName: "inventory" /* ... */ });
const monolith = appCompose(ordersApp, inventoryApp);
await endpoint("monolith", { port: 3000 }).use(httpKoa()).mount(monolith).run();Framework events
The runtime fires typed events at lifecycle transitions —
AppBooting, AppBooted, AppReady, PluginBooting, PluginBooted,
AppShuttingDown, AppShutdown. Observe them from a plugin's ctx on —
the payload is typed from the named hook.
import { definePlugin } from "@nwire/app";
const readyLog = definePlugin("ready-log", ({ on }) => {
on("AppBooted", ({ appName, bootedAt }) => {
console.log(`${appName} booted at ${bootedAt}`);
});
});Surface
createApp(opts)— builds an AppappCompose(...apps)— merges Apps for monolith compositiondefinePlugin(name, setup)— plugin factoryApp,CreateAppOptions,PluginContext,PluginDefinition— core typesRuntime,createRuntime— the per-App runtimeFrameworkEventBus, lifecycle events —AppBooting,AppBooted, etc.
Related
@nwire/endpoint— mounts the App under adopters@nwire/wires— binding helpers used byapp.wire@nwire/container— DI binding the App owns@nwire/forge—createForgePluginfor actions/workflows
