docfy-core
v0.6.1
Published
Pure OpenAPI document-model + Copy-for-AI transformer shared by docfy-ui and docfy-mcp
Readme
docfy-core
Pure OpenAPI document model — spec normalization, example generator, response
schema validation, and the "Copy for AI"/llms.txt transformers — extracted
from docfy-ui, with no React/DOM dependencies. Consumed by
docfy-ui, docfy-mcp, and
nest-docfy.
Install
npm install docfy-coreShips both an ESM build (dist/, the package's "type": "module" default)
and a CommonJS build (dist/cjs/, via the exports["."].require condition)
— import/require() both resolve correctly regardless of the consuming
project's module system.
Usage
import { normalizeDocument, operationToAiText } from 'docfy-core';
const document = await normalizeDocument(rawOpenApiSpec);
const endpoint = document.tagGroups[0].endpoints[0];
const aiText = operationToAiText(endpoint);Validating a live response against its declared schema
import { validateAgainstSchema } from 'docfy-core';
const mismatches = validateAgainstSchema(endpoint.responses[0].schema, liveResponseBody);
// [] when it matches; otherwise [{ path: 'body.name', message: 'required property missing' }, ...]Structural only (no pattern/format/numeric bounds, no $ref resolution
since normalizeDocument() already dereferences everything) — built to catch
drift (missing/renamed fields, wrong types) at request time, the same class
of bug diffDocuments() catches between two specs.
llms.txt / llms-full.txt
import { buildLlmsTxt, buildLlmsFullTxt } from 'docfy-core';
buildLlmsTxt(document, { docsBaseUrl: 'https://api.example.com/docs' });
// # My API
//
// ## Users
// - [GET /users](https://api.example.com/docs/Users/listUsers): List all users
buildLlmsFullTxt(document); // same header, each endpoint expanded to its full "Copy for AI" textSerializes the document model into the llms.txt
convention — lets an agent discover an API's shape with a plain curl, no
MCP server required. nest-docfy's DocfyUiModule.setup({ llmsTxt: ... })
serves both routes automatically.
uniqueEndpoints()
import { uniqueEndpoints } from 'docfy-core';
for (const endpoint of uniqueEndpoints(document)) {
/* ... */
}Flattens document.tagGroups into one entry per endpoint, deduplicated by
method path — an endpoint declared under multiple tags appears once per
tag group by design, so anything acting on each real endpoint exactly once
(a mock route, a contract test) should use this instead of
tagGroups.flatMap(...).
lintSpec()
import { lintSpec } from 'docfy-core';
const issues = lintSpec(document);
// [{ method: 'GET', path: '/users', rule: 'missing-summary', message: 'no summary declared' }, ...]Checks OpenAPI spec quality — not decorator/controller coverage (that's
nestjs-docfy check/lint, which work from source .docs.ts files) but the
final document itself, a Redocly-CLI/Spectral-style governance check: works
against any OpenAPI 3.0/3.1 document, generated by this library or not.
| Rule | Flags |
| ------------------------------ | ----------------------------------------------------------------- |
| missing-summary | Endpoint has no summary |
| missing-description | Endpoint has no description |
| missing-tags | Endpoint has no tags (falls into the synthetic "Default" group) |
| no-error-response | No 4xx/5xx response declared |
| missing-response-description | A declared response has no description |
| duplicate-operation-id | Two different endpoints share the same operationId |
nest-docfy's nestjs-docfy lint-spec --spec ... CLI command runs this
against a spec file/URL directly.
Scripts
npm run build— compiles both the ESM (dist/) and CJS (dist/cjs/) outputsnpm test— runs the test suite (vitest)npm run typecheck—tsc --noEmit
