Files
skills/plugins/document-navigator/docs/api-stack/api/api-openapi
admin 9bf56a806b Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into
plugins/document-navigator/docs/ so the plugin is self-contained and
portable. docs/superpowers/ (specs and plans) stays at the repo root.

Moved via cp+rm rather than git mv because directory rename() fails with
EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the
identical-content files at commit time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 01:10:00 +01:00
..

@suaveplan/api-openapi - OpenAPI 3.1 registry, typed RouteBuilder, schema registry, and document merger for the Suaveplan API. Single source of truth that wires into Hono via OpenAPIRegistry.applyTo(app) — eliminating drift between the introspection registry and the served /openapi.json. Ships StandardResponses spanning the full SuaveplanError statusCode range (400/401/403/404/409/422/429/500/503/504), an ErrorResponseSchema carrying the cause field of SuaveplanError.toJSON(), an instance-only SchemaRegistry (no global singleton) with extend/partial/pick/omit guarded by OPENAPI_INVALID_BASE_SCHEMA, mergeOpenAPIDocuments / prefixPaths / filterPathsByTag / removeInternalRoutes transforms, and a definePluginRoutes helper that registers atomically against both Hono and the registry.

OpenAPI 3.1 registry, typed RouteBuilder, schema registry, and document merger for the Suaveplan API. Single source of truth that wires into Hono via OpenAPIRegistry.applyTo(app), eliminating drift between registry introspection and the served /openapi.json.

Documentation tree

  • emitter — createOpenapiEmitter is the aggregator the spec calls for: it ingests every plugin's manifest, every registered route…
    • emitter — createOpenapiEmitter is the aggregator the spec calls for: it ingests every plugin's manifest, every registered route…
  • endpoints — endpoints mounts the four HTTP routes the design doc reserves for the OpenAPI surface — /openapi.json, `/openapi.ya…
    • endpoints — endpoints mounts the four HTTP routes the design doc reserves for the OpenAPI surface — /openapi.json, `/openapi.ya…
  • error-codes — Defines the stable error-code registry and typed error class for @suaveplan/api-openapi. Every public failure path in…
    • error-codes — Defines the stable error-code registry and typed error class for @suaveplan/api-openapi. Every public failure path in…
  • merger — OpenAPI document transforms — merge, prefix, filter, redact internal routes. Multi-plugin servers compose the final pub…
    • merger — OpenAPI document transforms — merge, prefix, filter, redact internal routes. Multi-plugin servers compose the final pub…
  • namespacing — The namespacing module turns a plugin's structural identity (name, displayName) plus a registered route's `(metho…
    • namespacing — The namespacing module turns a plugin's structural identity (name, displayName) plus a registered route's `(metho…
  • plugin-integration — definePluginRoutes is the single helper plugins call from their onInit to register routes. Every call atomically up…
    • plugin-integration — definePluginRoutes is the single helper plugins call from their onInit to register routes. Every call atomically up…
  • registry — Centralised OpenAPI 3.1 registry that holds info, servers, securitySchemes, tags, and routes. Acts as the sin…
    • registry — Centralised OpenAPI 3.1 registry that holds info, servers, securitySchemes, tags, and routes. Acts as the sin…
  • route-factory — Typed route construction utilities for @hono/zod-openapi. The RouteBuilder is a fluent generic that threads `TParam…
    • route-factory — Typed route construction utilities for @hono/zod-openapi. The RouteBuilder is a fluent generic that threads `TParam…
  • schema-registry — Instance-based registry for reusable Zod schemas. Each schema is bound to a stable name and may be derived into related…
    • common-schemas — Library of common Zod schemas decorated with OpenAPI example metadata. Covers the universal HTTP query / path / pagin…
    • schema-registry — Instance-based registry for reusable Zod schemas. Each schema is bound to a stable name and may be derived into related…
  • strictness — strictness resolves the host's missing-schema policy for the OpenAPI emitter and applies it consistently across every…
    • strictness — strictness resolves the host's missing-schema policy for the OpenAPI emitter and applies it consistently across every…
  • types — Structural TypeScript types for OpenAPI 3.1 documents, supporting shapes (info / server / security scheme), registered-…
    • openapi — Structural TypeScript types for OpenAPI 3.1 documents, supporting shapes (info / server / security scheme), registered-…

Features

  • OpenAPIRegistry — tracks info, servers, securitySchemes, tags, routes. buildDocument() emits a full 3.1 document. applyTo(app) wires a Hono GET handler that serves buildDocument() output — no drift possible.
  • RouteBuilder — fluent generic that threads TParams/TQuery/TBody/TResponse through to the final TypedRouteConfig. Hono's app.openapi(route, handler) then infers c.req.valid() to the exact Zod type.
  • StandardResponses — covers the full SuaveplanError statusCode range (400/401/403/404/409/422/429/500/503/504).
  • ErrorResponseSchema — 1:1 match for SuaveplanError.toJSON() including the cause field.
  • SchemaRegistry — instance-based (no singleton), extend/partial/pick/omit derivation helpers that raise typed OPENAPI.SCHEMA_NOT_FOUND / OPENAPI.INVALID_BASE_SCHEMA errors.
  • CommonSchemas — uuid, email, url, datetime, positiveInt, nonNegativeInt, page, limit, sortOrder.
  • mergeOpenAPIDocuments / prefixPaths / filterPathsByTag / removeInternalRoutes — pure document transforms.
  • definePluginRoutes(routes, { app, registry, pluginName }) — atomic dual-surface registration; the only way a plugin should add routes.

Quick start

Build a typed route

import z from "zod";
import { GET, successResponse } from "@suaveplan/api-openapi/route-factory";

const ListUsers = GET("/users")
    .tags("users")
    .operationId("listUsers")
    .response(successResponse(z.array(z.object({ id: z.string() }))))
    .build();

With auth + body validation

import { POST, successResponse } from "@suaveplan/api-openapi/route-factory";

const CreateUser = POST("/users")
    .tags("users")
    .operationId("createUser")
    .security([{ Bearer: [] }])
    .body(z.object({ email: z.string().email() }))
    .response(successResponse(z.object({ id: z.string() })))
    .build();

Paginated response

import { GET, paginatedResponse } from "@suaveplan/api-openapi/route-factory";

const ListUsersPaginated = GET("/users")
    .tags("users")
    .query(
        z.object({
            page: z.coerce.number().int().min(1).default(1),
            limit: z.coerce.number().int().min(1).max(100).default(20),
        })
    )
    .response(paginatedResponse(z.object({ id: z.string() })))
    .build();

Schema registry derivation

import z from "zod";
import { createSchemaRegistry } from "@suaveplan/api-openapi/schema-registry";

const reg = createSchemaRegistry();
reg.register(
    "User",
    z.object({
        id: z.string().uuid(),
        email: z.string().email(),
        password: z.string(),
    })
);

const UserPublic = reg.omit("User", "UserPublic", ["password"]);
const UserPatch = reg.partial("User", "UserPatch");
const UserAudit = reg.extend("User", "UserAudit", {
    lastSeenAt: z.string().datetime(),
});

Merge multiple plugin docs

import {
    mergeOpenAPIDocuments,
    prefixPaths,
    removeInternalRoutes,
} from "@suaveplan/api-openapi/merger";

const final = removeInternalRoutes(
    mergeOpenAPIDocuments(
        baseDoc,
        prefixPaths(usersDoc, "/v1"),
        prefixPaths(projectsDoc, "/v1")
    )
);

Plugin pattern with definePluginRoutes

import { OpenAPIHono } from "@hono/zod-openapi";
import { createOpenAPIRegistry } from "@suaveplan/api-openapi/registry";
import { definePluginRoutes } from "@suaveplan/api-openapi/plugin-integration";

const app = new OpenAPIHono();
const registry = createOpenAPIRegistry({
    info: { title: "Public API", version: "1.0.0" },
});

definePluginRoutes(
    [
        {
            route: ListUsers,
            handler: (c) => c.json({ success: true as const, data: [] }, 200),
        },
        {
            route: CreateUser,
            handler: (c) =>
                c.json(
                    { success: true as const, data: { id: "1" } },
                    200
                ),
        },
    ],
    { app, registry, pluginName: "users" }
);

// Single line connects registry → Hono. No drift possible.
registry.applyTo(app);

Tier

Server-tier (Hono-typed). Runs in Bun + Node.

Modules

  • ./error-codes — stable OPENAPI.* registry (7 codes) + OpenAPIError class.
  • ./types — structural OpenAPIDocument, Operation, PathItem, RegisteredRoute, security-scheme types, HTTP_METHODS.
  • ./registry — OpenAPIRegistry, createOpenAPIRegistry, applyTo(app) (Hono GET handler), default BearerAuthScheme / ApiKeyScheme.
  • ./route-factory — fluent RouteBuilder (GET/POST/PUT/ PATCH/DELETE), successResponse / errorResponse / paginatedResponse, StandardResponses (400-504), ErrorResponseSchema matching SuaveplanError.toJSON().
  • ./schema-registry — instance-based SchemaRegistry with extend/partial/pick/omit derivation + CommonSchemas.
  • ./merger — mergeOpenAPIDocuments, prefixPaths, filterPathsByTag, removeInternalRoutes, extractOpenAPIDoc.
  • ./plugin-integration — definePluginRoutes (atomic dual-surface registration; the only way a plugin should add routes).
  • ./namespacing — assignOperationIds, tagFor, componentPrefix, normaliseRoutePath. Deterministic operationId (<plugin>.<method>.<oasPath>) with OPENAPI.OPERATION_ID_COLLISION on duplicates.
  • ./strictness — resolveStrictness, reportMissingSchema, getMissingSchemaCounter. warn vs refuse-install policy + api.openapi.missing_schema_total OTel counter.
  • ./emitter — createOpenapiEmitter({ manifests, routes, authAdapter, hostConfig }). The Stage-8.6 aggregator: namespacing + strictness + security inheritance + memoised JSON/YAML/filtered output. Emits api.openapi.emission_total and api.openapi.conversion_error_total OTel counters.
  • ./endpoints — createOpenapiRouter / mountOpenapiEndpoints. Mounts GET /openapi.json, /openapi.yaml, /docs, /docs/:plugin.

License

MIT