The type system
Meridian's engine ships with no clinical types. Instead it ships a language for describing types — a small meta type-system — and three open registries that plugins fill at startup. Everything downstream (connection validity, runtime checking, editor tooltips) is derived from those descriptors. This is the same "empty core" principle that governs the rest of the platform (see Architecture).
This document explains the model and why it is shaped this way. For the exhaustive variant table, see the TypeRef reference; for the registry APIs, see Type-system registries.
TypeRef: describing a shape
Every port and every field declares its shape with a recursive TypeRef
(libs/shared/src/domain/types.ts). There are
seven variants:
primitive— a scalar (Decimal,Text,Integer,Boolean,DateTime).coded— a code bound to a terminology (a value-set).quantity— a dimension-aware measurement, unit-checked against UCUM.object— a structured datatype or resource from the registry.ref— a reference to an entity.list— a homogeneous collection.any— a flow port whose type is inferred from what it is wired to.
The point of a closed set of variants over an open set of registered vocabularies:
the engine understands the seven ways a shape can be composed, while knowing nothing
about which concrete objects, code systems, or dimensions exist. A plugin adds
Observation or renal-stage; the engine already knows how to compare, validate, and
display anything expressed with them.
Three open registries
The vocabulary lives in three registries, all empty at boot:
OBJECT_TYPES— descriptors forobjecttypes (datatypes, clinical resources), filled byregisterType.VALUE_SETS— terminologies backingcodedtypes, filled byregisterValueSet. A value-set may beexternal: true: its concepts are not enumerated but resolved by a terminology server through theterminologyport.DIMENSIONS— physical dimensions backingquantitytypes (a canonical unit plus conversion factors), filled byregisterDimension.
A plugin populates these during loading, in dependency order, so a value-set or type is always registered before a node that references it.
What the engine derives
From these descriptors alone, three capabilities fall out for free — no per-type code:
- Structural compatibility of connections — subtyping by fields, by value-set, and
by dimension (
type-system.ts). This is what lets the editor reject an incompatible wire before a run (see Two levels of validation). - Runtime value validation at every port boundary.
- Field metadata exported as JSON for the UX (tooltips, dropdowns, the palette).
No clinical TypeScript in the core
The core hosts no clinical TypeScript interface. A plugin that wants local static typing
for its own behavior code generates it from its manifest (import type declarations,
erased at runtime — e.g. external-plugins/demo/generated/types.ts). The result is zero
coupling: the engine depends on the descriptors, never on a plugin's compiled types.
See Codegen internals for how
that projection works.