Initial Commit
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
# Extend api-plugin + api with host-seeded ServiceRegistry slots
|
||||
|
||||
**Packages**: `@suaveplan/api-plugin`, `@suaveplan/api`
|
||||
**Status**: PROPOSED
|
||||
**Type**: feature (additive — no breaking changes)
|
||||
|
||||
- **Spec deltas**: ./specs/api-plugin/spec.md, ./specs/api/spec.md
|
||||
|
||||
## Why
|
||||
|
||||
The just-shipped 5-package api split exposed a foundation-layer gap: `PluginContext` provides `getService<K extends keyof ServiceRegistry>(name)` but nothing on the host side ever seeds `app`, `openAPIRegistry`, `migrationRunner`, or `db` into the registry. The first concrete consumer (the in-flight `api-plugin-auth`, blocked at scaffold time) cannot resolve those services without forcing operators to register them by hand — which breaks the plug-and-play promise.
|
||||
|
||||
Fix: add `PluginRegistry.seedServices(map)` to `@suaveplan/api-plugin`, augment `ServiceRegistry` from `@suaveplan/api` with the canonical host slots, and seed them in `SuaveApp.initialize()` BEFORE `registry.initialize()` runs so every plugin's `onRegister` / `onInit` / `onReady` can resolve them via typed `ctx.getService("app")`, etc.
|
||||
|
||||
## What changes
|
||||
|
||||
### @suaveplan/api-plugin (additive)
|
||||
|
||||
- ADDED: `PluginRegistry.seedServices<K extends keyof ServiceRegistry>(map: { [K in keyof ServiceRegistry]?: ServiceRegistry[K] }): void` — bulk-registers entries in an internal seeded-services map. Idempotent; later seeds override earlier ones. Throws `PluginError(PLUGIN_INVALID_STATE)` if called after `initialize()` has started.
|
||||
- MODIFIED: `createPluginContext` (and the per-plugin context factory) pre-populates the shared service map from the registry's seeded entries before plugin `onRegister`. Plugins calling `ctx.getService("X")` see the seeded entry without needing a sibling plugin's `registerService` to have run first.
|
||||
- ADDED: 1 new error code: `PLUGIN_INVALID_STATE` (already part of the spec's 10 → 11 codes total — used by `seedServices` post-initialize).
|
||||
- No public-surface removals.
|
||||
|
||||
### @suaveplan/api (additive)
|
||||
|
||||
- ADDED: ServiceRegistry module augmentation declaring canonical host slots:
|
||||
|
||||
```ts
|
||||
// src/services/registry-augmentation.ts (genesis-original)
|
||||
declare module "@suaveplan/api-plugin/service-registry" {
|
||||
interface ServiceRegistry {
|
||||
app: OpenAPIHono;
|
||||
openAPIRegistry: OpenAPIRegistry;
|
||||
migrationRunner?: MigrationRunner;
|
||||
db?: SurrealAdapter;
|
||||
logger: LoggerLike;
|
||||
eventBus: EventBusLike;
|
||||
clock: Clock;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- MODIFIED: `SuaveAppOptions` gains optional `db?: SurrealAdapter` field (for plugins that need direct DB access — wired into the seeded slot). Defaults to `undefined`; no behaviour change for callers that don't supply it.
|
||||
- MODIFIED: `SuaveApp.initialize()` calls `this._registry.seedServices({ app: this._hono, openAPIRegistry: this._openAPIRegistry, migrationRunner: this._migrations, db: this._db, logger: this._logger, eventBus: this._events, clock: this._clock })` immediately before `this._registry.initialize()`.
|
||||
- ADDED: subpath export `./services/registry-augmentation` so consumers can side-effect-import the augmentation if they only consume sub-packages.
|
||||
- No public-surface removals.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- **Backwards-compatible runtime shim.** This is additive — existing callers of `ctx.getService("X")` for non-augmented keys continue to return `undefined` and silently work. No deprecation needed.
|
||||
- **Per-request services (request-id, tenant, etc.).** Those still flow via Hono `c.get(key)` — they're per-request, not per-plugin.
|
||||
- **Plugin manifest-side service declarations.** Plugins continue to declare what they `registerService` via the existing surface; this change only adds host-seeded slots.
|
||||
|
||||
## Source mapping
|
||||
|
||||
Genesis-original — surfaced by `add-api-plugin-auth`'s scaffold-time blocker (the subagent stopped because `PluginContext.app` doesn't exist on the shipped surface, then noticed `SuaveApp` never seeds `app`/`openAPIRegistry`/etc into the registry).
|
||||
|
||||
## Impact
|
||||
|
||||
### Affected dependents
|
||||
|
||||
- `@suaveplan/api-plugin-auth` (in-flight) — unblocks. Resolves `app` / `openAPIRegistry` / `migrationRunner` via typed `ctx.getService` instead of operator pre-registration.
|
||||
- `@suaveplan/api-plugin-user`, `@suaveplan/api-plugin-vault` (in-flight) — inherit the same resolution path.
|
||||
- Every future `@suaveplan/api-plugin-*` package — same.
|
||||
|
||||
Risk: low. Additive on both packages; no existing API surface changes. Existing api-plugin + api tests stay green (verified by post-patch dependent sweep).
|
||||
Reference in New Issue
Block a user