opsinjs

API

One page per exported symbol, at a guessable URL, so every type name in the documentation is a link rather than a dead end.

Every type and interface this site's lib/ exports has a page here, addressed by its exact exported identifier:

opsinjs.pensievelabs.org/docs/reference/api/ClinicalStatus
opsinjs.pensievelabs.org/docs/reference/api/HealthCategory
opsinjs.pensievelabs.org/docs/reference/api/ReferenceRange

The identifier is used verbatim, with the same case, no kebab-casing and no pluralisation. That is the whole design: an inline <ApiLink> in prose, a link from a props table, and a program guessing a URL from a type name it read in an error message all arrive at the same page without a lookup table.

How this is generated

Source: the export declarations under apps/www/lib/, which are this documentation site's own TypeScript rather than a published package. No @opsinjs package has ever been released and, under decision 0002, none is going to be: opsinjs is distributed by copying source into your repository. So read these pages as the definitions behind the site and the copied components, never as the API of a module you can install. Script: scripts/build-reference.mts, which reads declaration lines textually rather than through fumadocs-typescript, because a reference generator has to run before anything in the repository compiles. Command: pnpm run generate. This index page is hand-written; every page beneath it is generated, and assert-ia.mts fails the build if an <ApiLink> anywhere in the corpus names a symbol with no page.

Why per-symbol pages and not one long page

A single API document is the format every generated documentation tool reaches for and it fails in the same three ways each time. Anchors churn, so a link that worked last release lands halfway down the wrong section. The page grows past the point where a browser's find-in-page is useful. And a program that fetches it gets forty thousand tokens of unrelated types in order to answer one question about one union. A page per symbol costs one file each and fixes all three, and the .md twin of a symbol page is small enough to paste into a prompt whole.

Only types and interfaces earn one. Exported functions and constants are listed in Types with the file they are declared in, which is what you actually want for a function; a page apiece for every STATUS_META would bury the symbols an <ApiLink> has to resolve to.

What a symbol page contains

Four things, and no more than a line scanner can honestly produce: the symbol's name, its doc comment in full, a table giving its kind and the file it is declared in, and its whole declaration, copied from the source rather than reconstructed. An interface shows its members and a union shows its cases, because for a type declared across twenty lines the opening line is the one part of it the page title has already told you. Field comments are stripped from that block, because a paragraph inside a signature hides the shape the block is there to show; the prose is in the source, at the path the table names.

Improve a page by improving its doc comment. The first sentence of it is also the page's description, which is the search snippet and the card subtitle, so the generator writes that line rather than preserving it and a page's summary cannot drift from the symbol's.

For the health types the page is only ever half the answer. The declaration says there are four status levels; only Clinical status semantics says what attention obliges you to do, and that is the half a type reference cannot carry. The two pages here that keep hand-written prose above their generated block, below, both do that linking by hand; for the rest, follow the symbol back to the doctrine pillar yourself.

Two things a symbol page still does not contain, and both are the part such a page is usually judged on. Defaults are not listed, because a default lives in the function that applies the type rather than in the type. Nor is the set of components and functions that consume the symbol, which is the part a type reference usually omits and the part you usually need. Both need a type checker rather than a textual scan, and until the generator grows one, the declaration and its file are what these pages promise.

Not written yet. List defaults and consumers on the per-symbol pages. It needs build-reference.mts to read the declarations through a TypeScript program instead of a line scan. Members and unions are expanded already, by capturing the declaration to its closing brace or its last case.Owner: engineering

What exists today

A page for every type and interface under lib/, each one written by pnpm run generate and replaced wholesale on the next run. That covers the colour model, the status and category vocabulary, the catalogue, preset and registry shapes, and the error types. The full list, with the functions and constants that do not get a page, is Types.

Two of them predate the generator and are still half hand-written above their generated block:

Their prose was written as a specification, before the types were declared, and it is still marked as one. Where that prose and the generated block below it disagree, the generated block is what the code says and the prose is what was intended. No other symbol page here has two halves like that; every other one is generated end to end.

On this page