# `@suaveplan/api-openapi` — OpenAPI 3.1 document emitter **Package**: `@suaveplan/api-openapi` **Status**: PROPOSED **Type**: new-package **Stage**: 8 — see umbrella `openspec/changes/api-plugin-foundation/` ## Why Plugins author routes with Zod schemas (design doc §7). The API host needs to emit a single merged OpenAPI 3.1 document combining every plugin's routes, every manifest's metadata, the active auth adapter's `securitySchemes`, and host-level info. Without this, plugin authors would each write hand-maintained OpenAPI documents that drift from runtime behaviour. Auto-emitting from the Zod contract is the only sustainable answer. ## What changes - ADDED: `createOpenapiEmitter({ manifests, routes, authAdapter, hostConfig })` — reads each plugin's manifest (`name`, `displayName`, `description`, `scopes`, route prefix, `publicRoutes`), every registered route's Zod schemas, the active auth adapter's `securitySchemes`, and `host.json` (server URLs, contact, license). Returns a `OpenapiEmitter` with `getJson()` and `getYaml()`. - ADDED: Zod-to-OpenAPI conversion via `@asteasolutions/zod-to-openapi` — established, OpenAPI 3.1, handles refs / discriminators / coercion. Plugins import `z` from `zod` directly (genesis convention). - ADDED: Namespacing rules — tags from plugin `displayName`; components/schemas prefixed (`billing.Invoice`); `operationId = ..`; security inherited from the active auth adapter except on `publicRoutes`. - ADDED: HTTP endpoints (mounted by the host): `GET /openapi.json`, `GET /openapi.yaml`, `GET /docs`, `GET /docs/{plugin}` (per-plugin filtered view). - ADDED: `openapi.generation` capability — optional. A plugin opts in by declaring `openapi.contributes = true`. Plugins NOT opting in still get their routes documented unless `openapi: false` is per-route. - ADDED: `host.openapi.strictness` (`"warn" | "refuse-install"`) — when a route is missing a request/response schema, the emitter either logs a warning or the host refuses to install the plugin. Defaults to `"warn"` in dev, `"refuse-install"` in production. - ADDED: Error codes (`OPENAPI_*`): `MISSING_SCHEMA`, `SCHEMA_CONVERSION_FAILED`, `OPERATION_ID_COLLISION`, `UNKNOWN_PLUGIN_IN_FILTER`. ## Out of scope - The UI itself — Scalar / Swagger / ReDoc each ship in their own sibling package. - Authoring OpenAPI by hand — plugins MUST use Zod. - Generating client SDKs from the emitted document — separate change. ## Source mapping Genesis-original. Source of design intent: `docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md`. | Requirement | Design doc section | |---|---| | Auto-emit OpenAPI 3.1 from Zod | §7 | | Endpoints `/openapi.json`, `/openapi.yaml`, `/docs`, `/docs/{plugin}` | §7 | | Namespacing rules | §7 | | `host.openapi.strictness` for missing schemas | §7 (this proposal locks the policy) | ## Verification approach - `src/emitter/emitter.test.ts` — full emission against a fixture manifest + route set; assert tags, components, operationIds, security all correct. - `src/emitter/emitter.spec-conformance.test.ts` — strictness modes (`warn` logs but emits; `refuse-install` raises `OPENAPI_MISSING_SCHEMA`). - `src/endpoints/endpoints.test.ts` — every endpoint returns the right Content-Type and body shape (json vs yaml; filtered view excludes other plugins). - `src/namespacing/namespacing.test.ts` — operationId determinism + collision detection. - `tests/dep-graph.test.ts` — runtime deps (`api-types`, `error`, `logger`, `zod`, `@asteasolutions/zod-to-openapi`). ## Migration impact None — additive only. The host mounts the OpenAPI endpoints when this package is installed.