@docspack/openapi
v0.2.0
Published
What docspack does with an OpenAPI document: a dense per-operation digest for an agent, code samples and a request builder for a page, and a try-it console.
Maintainers
Readme
@docspack/openapi
What docspack does with an OpenAPI document.
Reading the document and rendering it as LAPIS is @docspack/lapis, which is publishable
on its own because that conversion is useful to people who will never touch docspack. This package is
everything built on top of it — and it re-exports the model, so you need one import rather than two:
- for an agent — a dense per-operation digest: the base URL, the credential, the inputs with
their types, the body shape, the response, the failures and a runnable
curlcall, in LAPIS notation; - for a person — a request builder and code samples in cURL, JavaScript, Python and Go, plus the behaviour for a try-it console.
Both come from the same parse and the same request builder, so a sample on a page, the request the console sends and the example an agent is answered with cannot disagree.
Its only dependency is @docspack/lapis, whose main entry has none of its own, so this runs in the
CLI and in a browser alike.
import { parseOpenApi, operationDigest, codeSamples, toGroups } from "@docspack/openapi";
// `parseOpenApi` and `toGroups` come from @docspack/lapis, re-exported here.
const document = parseOpenApi(JSON.parse(await readFile("openapi.json", "utf8")));
const [operation] = document.operations;
operationDigest(document, operation); // the dense text an agent gets
codeSamples(document, operation); // cURL, JavaScript, Python, Go
toGroups(document); // operations grouped by tag, for a sidebarWhy a digest rather than the document
An agent handed an OpenAPI document is handed every operation in the API to answer a question about one. Measured against Stripe's published spec — 594 operations, 1,454 schemas, 8.0 MB of JSON:
| | estimated tokens | | --- | --- | | The whole document, as published | 2,007,014 | | The whole document, as LAPIS | 193,593 | | One operation's digest (median) | 830 |
Against the smallest correct slice of OpenAPI JSON for a single operation — the operation, the
schemas it references, its security scheme and its server — a digest is about 72% smaller
(tests/digest.test.ts holds that number to a floor).
Three limits make that possible, and each of them exists because its absence was measured:
- Named types are carried by name. The full transitive closure from one Stripe operation reaches most of the API, because every object has an expandable field pointing at another: digests came out at 66,500 tokens each. The walk is breadth-first from the operation's own schemas and stops at a token budget, naming what did not fit.
- Inline objects expand two levels and list twelve fields. A document that names nothing pays for it here.
- Enumerations list eight members. Stripe's merchant-category enum has 297 and appears four times in one operation — 25,637 characters on one line.
Anything elided is counted (+32 more, # not expanded here: …), because "there is more to this
shape" and "this is the whole shape" are different claims.
LAPIS, and where the digest differs from it
LAPIS (v0.1.0, CC BY 4.0) is an open format for describing an API
to a language model. It is adopted rather than replaced by a notation of our own: a format a model
has already seen is one it parses without being taught, and the saving is structural either way. The
renderer, and its one documented extension, are in @docspack/lapis.
Two further deviations belong to the digest specifically, because it describes one operation rather than a whole API:
- Errors are listed per digest, as
<status> <Type> # <description>, rather than in a document-wide[errors]section with@ops:bindings.toLapis— the whole-document rendering — does emit the centralized form, which is where LAPIS's largest single saving comes from. - The success status rides along as a
#comment on the<line. LAPIS's output marker carries a type and nothing else, and201versus204changes what a caller does next.
The try-it console
@docspack/openapi/console is a DOM module, not a component:
import { mountApiConsoles } from "@docspack/openapi/console";
mountApiConsoles(); // claims every [data-api-console] on the pageThe markup renders statically — a labelled, filled-in form showing exactly what the endpoint takes —
and this makes the Send button work. The credential is never stored: it is read from the field at
send time and kept in nothing that outlives the request. No localStorage, no sessionStorage, no
cookie.
@docspack/sheaf-react renders markup this module understands.
Licence
MIT. The LAPIS specification is CC BY 4.0 and belongs to its authors; this package implements a renderer for it.
