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/ReferenceRangeThe 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.
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: engineeringWhat 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:
- ClinicalStatus, the four clinical status levels
- HealthCategory, the subject-identity categories
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.