2.8 KiB
validateOpenApiRequiresGraphqlOrRoutes
Purpose
Rejects manifests that declare an OpenAPI contribution but expose neither HTTP routes nor a GraphQL contribution. The OpenAPI emitter needs a surface to document; without one the emission step would produce an empty document that wastes admin-UI real estate.
Features
- No-op success when
openapi.contributesis false or absent - Accepts either
routes(any prefix) orgraphql.contributes === true - Stable
MANIFEST.OPENAPI_REQUIRES_GRAPHQL_OR_ROUTEScode - Manifest name surfaced in the error context
Basic Example
import { validateOpenApiRequiresGraphqlOrRoutes, parseManifest } from "@suaveplan/api-manifest";
const r = validateOpenApiRequiresGraphqlOrRoutes(parseManifest(raw));
if (!r.ok) throw r.error;
Advanced Example
import { validateOpenApiRequiresGraphqlOrRoutes } from "@suaveplan/api-manifest/validators";
import { parseManifest } from "@suaveplan/api-manifest/parse";
const manifest = parseManifest(raw);
const check = validateOpenApiRequiresGraphqlOrRoutes(manifest);
if (!check.ok) console.warn("OpenAPI contrib will be empty:", manifest.name);
API Reference
validateOpenApiRequiresGraphqlOrRoutes(manifest: PluginManifest): ValidationResult- Failure code:
MANIFEST.OPENAPI_REQUIRES_GRAPHQL_OR_ROUTES.
Implementation Notes
GraphQL contribution is detected by graphql.contributes === true rather than mere presence of the block; a { contributes: false } block is a legal opt-out state. Routes contribution is detected by mere presence — any prefix counts because the host will introspect handlers at boot. The check is intentionally cheap: a single boolean expression over four readily-available fields.
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.