Files
skills/docs/api-stack/api/api-manifest/src/schema/openapi.md
2026-05-29 08:20:56 +01:00

46 lines
2.6 KiB
Markdown

# `openapiSchema`
## Purpose
OpenAPI contribution block. When `contributes: true` the host emits a per-plugin OpenAPI document by introspecting the registered routes (and, if `graphql.contributes` is also true, the GraphQL schema). The cross-field validator `validateOpenApiRequiresGraphqlOrRoutes` enforces that there is actually something to document.
## Features
- `contributes` boolean discriminator
- Optional `tagDisplayName` for the rendered docs UI
- Optional `externalDocs` URL
- Cross-field coupling to `routes`/`graphql` enforced by a separate validator
## Basic Example
```ts
import { openapiSchema } from "@suaveplan/api-manifest/schema";
openapiSchema.parse({ contributes: false });
```
## Advanced Example
```ts
import { openapiSchema } from "@suaveplan/api-manifest/schema";
const api = openapiSchema.parse({
contributes: true,
tagDisplayName: "Billing & Subscriptions",
externalDocs: "https://docs.acme.io/billing",
});
```
## API Reference
- `openapiSchema: z.ZodObject<{ contributes, tagDisplayName?, externalDocs? }>`
- `OpenApiBlock = z.infer<typeof openapiSchema>`
## Implementation Notes
The `externalDocs` URL is checked with Zod's built-in URL validator, which rejects schemes other than http/https; that matches the OpenAPI 3.1 spec's expectations. `tagDisplayName` is rendered by the docs UI as the grouping label for this plugin's routes. The block is intentionally minimal — the host's OpenAPI emitter owns the actual schema generation.
## Cross-package Notes
This module is part of the `@suaveplan/api-manifest` Stage-1 contract package. It sits beneath every Stage-2 consumer (`api-host`, `api-loader`, the admin CLI, the IDE plugin) and above the foundation packages (`@suaveplan/error`, `@suaveplan/types`). The strict-mode top-level rejection of unknown keys means upgrading from manifest v1 to v2 is an OpenSpec-driven event: a new key landing in a consumer manifest is a parse failure rather than a silent drift, which is exactly the contract this stage exists to guarantee.
When the host reloads a plugin without a full restart, every manifest field this module validates is compared against the install-time `manifestHash`. A mismatch triggers a hard rollback via the `RollbackJournal` because the host's invariants (route mounts, registered capabilities, declared scopes) are derived from these exact bytes. Treat the schemas and validators here as the contract surface, not just an input filter — downstream code reads `manifest.foo.bar` and relies on those keys being present, well-typed, and non-undefined in the documented optionality regions.