@pedigree-fhir/core
v0.1.0
Published
Headless, framework-agnostic core for FHIR-driven pedigree charts: parsing, model, layout, state, validation.
Readme
@pedigree-fhir/core
Framework-agnostic core for FHIR-driven pedigree graphs: parsing, relationship inference, PSC semantics, layout, state, editing, history, and validation.
Responsibilities
@pedigree-fhir/core owns the domain and state model for the library. It does not depend on React and does not render anything. Instead, it gives consumers:
- FHIR parsing from
PatientandFamilyMemberHistory - relationship inference for common pedigree topology
- PSC-aware semantics and layout geometry
- a headless store with selection, layout options, edits, and history
- validation rules and registry composition
Public surface
The package currently exports these main areas:
fhir/*parsePedigreeserializePedigree- explicit
R4*type aliases forPatient,FamilyMemberHistory, and related FHIR datatypes - genetics extension helpers
- relationship-code helpers and display-label resolvers
model/*- graph and individual/couple types
- ID helpers
inferRelationships
psc/*- pedigree semantics such as sex, vital state, carrier state, twins, and adoption
layout/*- layout types
computeLayout
state/*createPedigreeStore- individual, graph, and couple edits
- history helpers
validation/*defaultRegistryvalidateFhirInput- custom registry composition
- diagnostic/rule types
See src/index.ts for the actual export list.
Core workflow
For most consumers, the canonical flow is:
import {
createPedigreeStore,
defaultRegistry,
inferRelationships,
parsePedigree,
} from '@pedigree-fhir/core';
const inputDiagnostics = validateFhirInput(patient, familyHistory);
const parsed = parsePedigree(patient, familyHistory);
const graph = inferRelationships(parsed);
const store = createPedigreeStore({
graph,
layoutOptions: {},
});
const graphDiagnostics = defaultRegistry().validate(store.getState().graph);Parsing and inference
The package currently makes its FHIR wire contract explicit via exported R4*
type aliases such as R4Patient and R4FamilyMemberHistory. That keeps the
library aligned to the current R4 runtime behavior while leaving room for a
future R5 strategy to be added intentionally rather than through ambient globals.
parsePedigree(patient, familyHistory) performs the deterministic FHIR-to-graph conversion:
- constructs the proband from
Patient - turns
FamilyMemberHistoryrecords into individuals - reads genetics-parent and genetics-sibling extension data
- creates explicit couples and child-of relationships when the source is clear
- groups twin declarations transitively
inferRelationships(graph) then fills in structural relationships from pedigree evidence such as:
- direct mother/father/sibling relationships
- maternal and paternal grandparent sides
- fabricated parents or grandparents where the topology is strongly implied
This split is deliberate: parsing handles explicit source data, while inference handles predictable pedigree structure recovery.
Relationship-code helpers can also drive consumer labels. For example,
resolveIndividualDisplayLabel(individual, { preferRelationshipLabel: true })
lets a renderer prefer readable relative labels like "mother" or
"natural sister" while still falling back to individual.name when needed.
Layout
computeLayout(graph, options) returns headless geometry rather than a rendered chart. The important outputs are:
nodes: individual positionspartnerEdges: partner-line SVG path strings, including consanguinity handlingparentDrops: sibship drop geometry and twin-junction metadatabounds: overall chart bounds
That geometry is what downstream renderers consume. The current layout is still proband-centered, but it now supports visible descendant rows for gen-0 couples in addition to the ancestor structure above the proband.
Store and edits
createPedigreeStore produces a framework-agnostic external store with:
getState()dispatch(action)subscribe(listener)
The store keeps:
- the current
graph layoutOptions- selected individual ID
- edit history
The action surface includes:
- selection changes
- layout option updates
- individual semantic edits
- graph edits such as adding/removing relatives
- couple edits such as consanguinity and twin state
- undo/redo
Validation
Validation is split into two layers:
validateFhirInput(patient, familyHistory)catches raw source-data issues that parsing may otherwise skip or normalize, such as missing IDs, duplicate IDs, dangling genetics references, malformed parent/sibling extensions, and patient-reference mismatches.defaultRegistry().validate(graph)runs graph-level pedigree rules after parsing and inference.
The built-in validation registry currently includes:
- completeness checks
- sex/relationship consistency checks
- cycle detection
- unknown code detection
- broken structural reference checks
- twin-group consistency checks
Use defaultRegistry() for the standard graph rule set, or createRegistry() to customize the active rules.
Design constraints
The core package is intentionally:
- headless: no UI assumptions
- pure-data-first: graph and geometry are explicit outputs
- editable: state mutations happen through a typed action surface
- test-heavy: the repo’s package coverage bar is 100%
