3.6 KiB
@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'ssecuritySchemes, andhost.json(server URLs, contact, license). Returns aOpenapiEmitterwithgetJson()andgetYaml(). - ADDED: Zod-to-OpenAPI conversion via
@asteasolutions/zod-to-openapi— established, OpenAPI 3.1, handles refs / discriminators / coercion. Plugins importzfromzoddirectly (genesis convention). - ADDED: Namespacing rules — tags from plugin
displayName; components/schemas prefixed (billing.Invoice);operationId = <pluginName>.<method>.<routePath>; security inherited from the active auth adapter except onpublicRoutes. - ADDED: HTTP endpoints (mounted by the host):
GET /openapi.json,GET /openapi.yaml,GET /docs,GET /docs/{plugin}(per-plugin filtered view). - ADDED:
openapi.generationcapability — optional. A plugin opts in by declaringopenapi.contributes = true. Plugins NOT opting in still get their routes documented unlessopenapi: falseis 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 (warnlogs but emits;refuse-installraisesOPENAPI_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.