@openhi/ours
v0.1.1
Published
The Open Unified Resource Standard in TypeScript: resource shapes, ontology assembly, and the checks a publisher should pass before publishing.
Readme
@openhi/ours
The Open Unified Resource Standard in TypeScript: the resource shapes, a way to assemble an ontology from documents however you obtained them, and the cross-resource checks a publisher should pass before publishing.
OURS is a shared format for describing which standards you use, how you use them, and how data moves between them, so another system can integrate without asking you first.
npm install @openhi/oursFull documentation, including the specification itself, is at ours.dev.
Reading a published ontology
An ontology is discoverable from a domain alone, at /.well-known/ours.json.
import { assembleBundle, wellKnownOntologyUrl } from "@openhi/ours";
const get = (url: string) => fetch(url, { redirect: "follow" }).then((r) => r.json());
const ontology = await get(wellKnownOntologyUrl("example.org"));
const models = await get(ontology.models);
const bundle = assembleBundle({ ontology, models: [models] });
for (const model of bundle.models.values()) {
console.log(model.name, model.mapsTo?.map((m) => m.schema));
}assembleBundle takes documents, not URLs or paths. Transport is deliberately
not this package's business: the same ontology may be read from disk, fetched
over HTTP, or built in memory by a generator, and all three should produce the
same object.
A document may be a single resource or a collection Bundle; resourcesIn
unwraps either, so a reader never has to care which a publisher chose.
Publishing nothing, explicitly
models, vocabularies and mappings are all required on an Ontology. A
publisher with no vocabularies serves an empty collection at the URL rather
than leaving the pointer out:
import { emptyBundle } from "@openhi/ours";
// GET https://ours.example.org/vocabularies.json
serve(emptyBundle());An absent URL cannot be told apart from one that has not been published yet, so a consumer would have to guess whether to keep looking. An empty bundle at a live URL is a definite answer, and the point of the format is that nobody should have to ask.
Validating before you publish
Parsing tells you a model is well formed. It cannot tell you the schema it points at was actually published, or that a relationship names a model that exists. Those are the failures a consumer meets as a broken integration rather than as a parse error.
import { validateBundle, hasErrors } from "@openhi/ours";
const issues = validateBundle(bundle);
if (hasErrors(issues)) {
for (const issue of issues) console.error(issue.level, issue.resource, issue.message);
process.exit(1);
}Errors are things that will break a consumer. Warnings are things worth
knowing: a model with no mapsTo is valid, but it is invisible to the
integration OURS exists to enable. Pass { warnOnMissingMapsTo: false } for an
ontology that is deliberately all your own.
Vocabularies are JSON Schema
Every Vocabulary also serialises to an ordinary JSON Schema enumeration,
published beside it as .schema.json. A model binds a property to it with a
plain $ref, so any off-the-shelf validator enforces the codes without knowing
what OURS is.
import { vocabularySchemaFor, vocabularySchemaUrl } from "@openhi/ours";What is here
| Export | |
|---|---|
| ontologySchema, modelSchema, vocabularySchema, bundleSchema | The resource shapes, as zod schemas |
| wellKnownOntologyUrl | Find an ontology from a domain alone |
| parseResource, resourcesIn | Read one document, whatever kind it is |
| assembleBundle, toPublishedBundles | In-memory ontology, and back again |
| validateBundle, hasErrors | Cross-resource checks |
| walkSchema, refsIn, resolveRef | JSON Schema helpers |
| walkSchemaInScope, scopedRefsIn, declaredIds | The same, aware of $id scope |
Types are exported alongside every schema. The API reference covers each one.
License
MIT. Copyright OpenHI, Inc.
