# @nwire/app

> The composition root. `createApp`, `appCompose`, `definePlugin`,
> runtime, framework events.

```bash
pnpm add @nwire/app
```

## Quick start

```ts
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):

```ts
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.

```ts
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.

```ts
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 App
- `appCompose(...apps)` — merges Apps for monolith composition
- `definePlugin(name, setup)` — plugin factory
- `App`, `CreateAppOptions`, `PluginContext`, `PluginDefinition` — core types
- `Runtime`, `createRuntime` — the per-App runtime
- `FrameworkEventBus`, lifecycle events — `AppBooting`, `AppBooted`, etc.

## Related

- [`@nwire/endpoint`](../core-endpoint) — mounts the App under adopters
- [`@nwire/wires`](../core-wires) — binding helpers used by `app.wire`
- [`@nwire/container`](../core-container) — DI binding the App owns
- [`@nwire/forge`](../core-forge) — `createForgePlugin` for actions/workflows
