first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
# `capability-registry` — runtime registry of host-injected capabilities
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The `CapabilityRegistry` is the live, single-instance store of every capability adapter wired into a host process. Stage-4 adapter packages (`api-router-hono`, `api-auth-jwt`, `api-db-adapter`, etc.) register against this registry at host boot. The host's lifecycle driver consults it on every plugin install to (a) verify every entry in `manifest.requiredCapabilities` resolves to a registered adapter and (b) verify the adapter's version satisfies the plugin's `apiHost` semver range.
The registry is also the disposal coordinator — `stop()` on the host invokes `dispose()` here, and disposers run in **reverse-registration** order so deeper consumers (e.g. `db`) shut down before their dependencies (e.g. `logger`).
## Features
- `register(...)` with semver-shape validation (`INVALID_VERSION`), scope-enum validation (`INVALID_SCOPE`), and duplicate detection (`DUPLICATE_REGISTRATION`).
- `lookup(id)` / `require(id)` (strict, throws `NOT_REGISTERED`).
- `assertSatisfies(ranges)` — per-plugin compat check used by the lifecycle driver; throws `NOT_REGISTERED` or `VERSION_MISMATCH` with the failing `(id, range, actual)` context.
- Reverse-registration-order `dispose()` — collects every disposer failure and surfaces them via one aggregated `DISPOSER_FAILED` error so a single bad disposer does not block teardown.
- `defineCapability(...)` — author-facing helper (identity at runtime) used by adapter packages.
## Usage: Basic
```ts
import {
CapabilityRegistry,
defineCapability,
} from "@suaveplan/api-host/capability-registry";
const registry = new CapabilityRegistry();
registry.register(
defineCapability({
id: "logger",
version: "1.0.0",
scope: "host",
provider: logger,
})
);
const entry = registry.require("logger"); // throws CAPABILITY.NOT_REGISTERED if missing.
```
## Usage: Advanced
Plug into a manifest's `requiredCapabilities` resolution:
```ts
import { CapabilityRegistry } from "@suaveplan/api-host/capability-registry";
const registry = new CapabilityRegistry();
// ... register every adapter ...
function installPlugin(manifest: PluginManifest): void {
// The manifest pins ranges via `requiredCapabilities` + per-id range table.
registry.assertSatisfies({
db: "^1.0.0",
"auth.session": "~1.4",
});
// ... proceed with lifecycle install ...
}
// Host teardown — disposers run in REVERSE-registration order.
await registry.dispose();
```
## API Reference
- `class CapabilityRegistry`
- `register(reg)`: void
- `lookup<Id>(id): CapabilityRegistration<Id> | undefined`
- `require<Id>(id): CapabilityRegistration<Id>`
- `has(id): boolean`
- `ids(): readonly CapabilityId[]`
- `size(): number`
- `assertSatisfies(ranges: Record<string, string>): void`
- `dispose(): Promise<void>`
- `defineCapability<const Id>(reg): CapabilityRegistration<Id>`
- `CapabilityScope`: `"host" | "plugin" | "request"`
- `CapabilityRegistration<Id>`: shape consumed by `register`.
## Implementation Notes
Version compatibility uses `@suaveplan/api-manifest/dep-graph`'s `satisfies(version, range)` — the same narrow semver dialect the manifest validator accepts (pinned, caret, tilde, major-wildcard, `*`). Unparseable ranges throw at `register` time inside `satisfies`; the registry catches and re-raises as `CAPABILITY.VERSION_MISMATCH` so the lifecycle driver only ever has to match on one code. Disposers are awaited sequentially in reverse-registration order — concurrent dispose would risk teardown races between adapters that share resources (e.g. `events` dispatching to `logger`). Idempotency: a second `dispose()` call is a no-op, so the host's stop path can call it freely even after a partial failure.