5.3 KiB
Add @suaveplan/api-openapi — OpenAPI registry + route factory (3 of 5)
Package: @suaveplan/api-openapi (NEW, server-tier)
Status: PROPOSED
Type: new-package (split from upstream @suaveplan/api)
- Spec delta: ./specs/api-openapi/spec.md
- Companions:
add-api-plugin,add-api-tenant,add-api-migrations,enterprise-api - Position in chain: depends on
add-api-plugin(consumers register routes via pluginonInit)
Why
Upstream @suaveplan/api's openapi/ (1,066 LOC) ships an OpenAPIRegistry, a doc merger, a typed RouteBuilder, and a global SchemaRegistry. Splitting it lets @suaveplan/api-schemas (the Zod boundary) and downstream api-plugin-* packages compose a focused OpenAPI surface without dragging the Hono app orchestrator along.
The upstream layer has two notable issues genesis fixes: (1) the OpenAPIRegistry and Hono's OpenAPIHono.doc() are not connected — registry tracks routes for introspection but the actual doc is generated by Hono, so drift between the two is silent; (2) getSchemaRegistry() is a global singleton, untestable in parallel and conflicting with multi-app deployments.
What changes
Net-new package at 0.1.0. ADDED:
OpenAPIRegistryclass — single source of truth. Tracksinfo,servers,securitySchemes,tags,routes. Methods:registerSecurityScheme,registerTag,registerRoute,getRoutes,getRoutesByPlugin,getRoutesByTag,buildDocument(): OpenAPIDocument(returns the full 3.1.0 doc, not just info+servers).- Hono integration:
OpenAPIRegistry.applyTo(app: OpenAPIHono)— registers a singleapp.doc(specPath, ...)handler that serves the registry-built document. Eliminates the drift between registry state and Hono doc output. RouteBuilderfluent API — type-preserving viaRouteBuilder<TParams, TQuery, TBody, TResponse>generics;.params(z)/.query(z)/.body(z)/.response(z)thread types through;.build()returnsTypedRouteConfigcompatible with@hono/zod-openapi.route(method, path)factory + shorthandsGET/POST/PUT/PATCH/DELETE(path).StandardResponses—400/401/403/404/422/500/503(widened from upstream's 400/401/403/404/500 to cover the full SuaveplanError statusCode range).ErrorResponseSchema— Zod schema matching@suaveplan/errorwire shape ({ success: false, error: { code, message, details?, requestId?, timestamp, cause? } }).successResponse(T),paginatedResponse(T),errorResponse(desc)— response wrapper schemas.SchemaRegistryclass — local instance (no global singleton). Methods:register,get,has,getNames,getAll,remove,clear,extend(baseName, name, ext),partial(baseName, name),pick(baseName, name, keys),omit(baseName, name, keys).defineSchema(schema, meta, registry)requires an explicit registry instance.CommonSchemas—uuid,email,url,datetime,positiveInt,nonNegativeInt,page,limit,sortOrder(Zod schemas with.openapi()metadata).OpenAPIDocument+PathItem+Operationtypes — structural shape for cross-tool merging.mergeOpenAPIDocuments(base, ...docs)— same path-merge / components-spread / tag-dedup semantics as upstream.prefixPaths(doc, prefix),filterPathsByTag(doc, tags),removeInternalRoutes(doc)— doc transforms.BearerAuthScheme,ApiKeySchemesecurity-scheme presets.OpenAPIError extends SuaveplanError<OpenAPIErrorCode>— codesOPENAPI_INVALID_INPUT,OPENAPI_SCHEMA_NOT_FOUND,OPENAPI_INVALID_BASE_SCHEMA(e.g.extendon a non-ZodObject).- Plugin integration:
definePluginRoutes(routes: TypedRouteConfig[], registry: OpenAPIRegistry)helper that plugins call fromonInitto register routes against both Hono (app.openapi) and the registry.
Out of scope
- OpenAPI 3.0 support. Genesis ships 3.1.0 only (matches Hono
OpenAPIHono.getOpenAPI31Document). A back-compat 3.0 transform is a follow-up. - YAML / TOML emit. Doc is emitted as JSON. YAML adapter belongs in a separate package.
- Spec validation against the 3.1 schema. We trust
@hono/zod-openapi's output structure; we don't reparse our own emitted spec. A validator package is out of scope. - OpenAPI UI rendering (Swagger / Redoc / Scalar / Altair). These ship as separate plugins (
api-openapi-ui-swagger, etc — all in the in-flight change set).
Source mapping
| Capability | Upstream source | Notes |
|---|---|---|
OpenAPIRegistry |
../suaveplan/packages/server/api/src/openapi/openapi-registry.ts L1-L238 |
port + add buildDocument() that emits the full doc + applyTo(app) Hono integration |
| Doc merging + transforms | ../suaveplan/packages/server/api/src/openapi/openapi-merger.ts L1-L271 |
verbatim port |
RouteBuilder + factories + StandardResponses + helpers |
../suaveplan/packages/server/api/src/openapi/route-factory.ts L1-L298 |
port + widen StandardResponses to include 422 and 503 |
SchemaRegistry + CommonSchemas |
../suaveplan/packages/server/api/src/openapi/schema-registry.ts L1-L255 |
port + drop global singleton; require explicit registry instance |
Impact
Affected dependents
Depends on add-api-plugin (for the PluginDescriptor/PluginContext types) and indirectly on @suaveplan/api-schemas (existing on genesis). Will be consumed by @suaveplan/api (orchestrator) and every @suaveplan/api-plugin-* package that ships routes.