@cosyte/fhir 0.0.10 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +173 -6
- package/dist/index.cjs +2844 -1262
- package/dist/index.d.cts +948 -85
- package/dist/index.d.ts +948 -85
- package/dist/index.mjs +2842 -1263
- package/package.json +25 -7
package/README.md
CHANGED
|
@@ -1,16 +1,183 @@
|
|
|
1
|
+
<a href="https://cosyte.com">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://cosyte.com/tile/cosyte-lockup-tile-on-dark-1200x300.png">
|
|
4
|
+
<img alt="Cosyte: a plus mark set in two overlapping rounded squares, one solid and one outlined, beside the Cosyte wordmark" src="https://cosyte.com/tile/cosyte-lockup-tile-on-light-1200x300.png">
|
|
5
|
+
</picture>
|
|
6
|
+
</a>
|
|
7
|
+
|
|
1
8
|
# @cosyte/fhir
|
|
2
9
|
|
|
3
|
-
|
|
4
|
-
|
|
10
|
+
> Read real-world FHIR R4, keep every value exactly as it was written, and validate it against your
|
|
11
|
+
> profiles.
|
|
12
|
+
|
|
13
|
+
[](https://www.npmjs.com/package/@cosyte/fhir)
|
|
14
|
+
[](https://github.com/cosyte/fhir/actions/workflows/ci.yml)
|
|
15
|
+
[](./LICENSE)
|
|
16
|
+
[](https://nodejs.org)
|
|
17
|
+
|
|
18
|
+
FHIR R4 for Node.js and TypeScript: a resource model that keeps every value exactly as written, JSON and XML codecs, layered validation, and a safety readout for status and negation.
|
|
19
|
+
|
|
20
|
+
## Why this exists
|
|
21
|
+
|
|
22
|
+
Reading FHIR correctly takes more than a JSON parse: a `decimal` written `0.010` has to stay
|
|
23
|
+
`0.010`, an `integer64` has to keep every digit, and a status, a modifier or a negation must never be
|
|
24
|
+
skipped, because a missed `refuted` reads as its opposite. We built `@cosyte/fhir` for the engineer
|
|
25
|
+
who reads FHIR R4 from a server or an EHR (the format ONC HTI-1 binds §170.315(g)(10) to, with US
|
|
26
|
+
Core and SMART on FHIR) and has to get those values right without reading the specification first.
|
|
27
|
+
Type definitions alone describe a resource's shape and check nothing at run time, and `JSON.parse`
|
|
28
|
+
turns `0.010` into the number `0.01` and knows nothing about modifiers. This library reads, writes
|
|
29
|
+
and validates FHIR R4 with zero runtime dependencies, and tells you with a coded reason what it could
|
|
30
|
+
not read safely.
|
|
31
|
+
|
|
32
|
+
## Status
|
|
33
|
+
|
|
34
|
+
**The 0.1 line.** From 0.1 the public API is settled: the exported names, options, result shapes and
|
|
35
|
+
issue codes are the surface we keep stable. While the package is below 1.0, a breaking change ships
|
|
36
|
+
in a minor version and is called out in the
|
|
37
|
+
[changelog](https://github.com/cosyte/fhir/blob/main/CHANGELOG.md). Upgrading from 0.0.10 changes
|
|
38
|
+
verdicts: R4's own constraints are now evaluated with no profile supplied, so a document that read
|
|
39
|
+
valid can read invalid. The changelog sets out each change.
|
|
40
|
+
|
|
41
|
+
What it covers, against FHIR R4 (`4.0.1`): reading and writing JSON and XML into one immutable
|
|
42
|
+
model; validation in layers (structure, cardinality, value domains, R4's own constraints on the
|
|
43
|
+
eight modeled resource types, Quantity and UCUM, binding strength, and profiles you supply or author
|
|
44
|
+
with `defineProfile`, including their FHIRPath invariants); the `readSafety` and
|
|
45
|
+
`assertSafeToSummarize` safety readout; and Bundles, references and streaming Bulk Data NDJSON. R5
|
|
46
|
+
and DSTU2 documents can be read, since the model is schema-free, but they are not validated against
|
|
47
|
+
their own definitions.
|
|
5
48
|
|
|
6
|
-
|
|
49
|
+
Not covered yet: typed per-resource models (the built-in structural schemas cover the base elements
|
|
50
|
+
plus `Patient`), `type` and `profile` slicing discriminators and reslicing (reported
|
|
51
|
+
`PROFILE_SLICE_UNCHECKED`), FHIRPath outside the supported subset (reported `INVARIANT_UNCHECKED`,
|
|
52
|
+
never passed), bundled terminology or US Core content, spec-clean JSON written from a model read out
|
|
53
|
+
of XML, unit conversion, and executing a transaction Bundle.
|
|
7
54
|
|
|
8
55
|
## Install
|
|
9
56
|
|
|
10
|
-
```
|
|
11
|
-
npm install @cosyte/fhir
|
|
57
|
+
```bash
|
|
58
|
+
# pnpm (recommended). Also works with: npm install @cosyte/fhir | yarn add @cosyte/fhir
|
|
59
|
+
pnpm add @cosyte/fhir
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Requires Node `>=22.0.0`. The package ships dual ESM and CommonJS builds with type declarations for
|
|
63
|
+
both, so `import` and `require` both work and neither needs a compatibility shim. There are zero
|
|
64
|
+
runtime dependencies, and it imports no Node built-in.
|
|
65
|
+
|
|
66
|
+
## Usage
|
|
67
|
+
|
|
68
|
+
Read a document, keep its exact values, and get a verdict. The Observation below is synthetic: every
|
|
69
|
+
value in it is fabricated, and the same document is committed as a test fixture in this repository.
|
|
70
|
+
|
|
71
|
+
```ts runnable
|
|
72
|
+
import { parseResource, readObservationValue, validateResource } from "@cosyte/fhir";
|
|
73
|
+
|
|
74
|
+
const document = `{
|
|
75
|
+
"resourceType": "Observation",
|
|
76
|
+
"id": "syn-0001",
|
|
77
|
+
"status": "final",
|
|
78
|
+
"code": { "coding": [{ "system": "http://loinc.org", "code": "8480-6" }] },
|
|
79
|
+
"subject": { "reference": "Patient/syn-0001" },
|
|
80
|
+
"effectiveDateTime": "2026-01-05",
|
|
81
|
+
"valueQuantity": {
|
|
82
|
+
"value": 120.0,
|
|
83
|
+
"unit": "mmHg",
|
|
84
|
+
"system": "http://unitsofmeasure.org",
|
|
85
|
+
"code": "mm[Hg]"
|
|
86
|
+
}
|
|
87
|
+
}`;
|
|
88
|
+
|
|
89
|
+
const { resource, issues } = parseResource(document);
|
|
90
|
+
|
|
91
|
+
// The magnitude was written 120.0: the read keeps that exact form and says the protection mattered.
|
|
92
|
+
issues.map((issue) => issue.code); // => ["DECIMAL_PRECISION_AT_RISK"]
|
|
93
|
+
|
|
94
|
+
// Branch on the value[x] type before touching a magnitude, and compare on the UCUM code.
|
|
95
|
+
const reading = readObservationValue(resource);
|
|
96
|
+
reading?.type; // => "Quantity"
|
|
97
|
+
reading?.quantity?.value?.raw; // => "120.0"
|
|
98
|
+
reading?.quantity?.code; // => "mm[Hg]"
|
|
99
|
+
|
|
100
|
+
validateResource(resource).valid; // => true
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The quickstart on [docs.cosyte.com/fhir](https://docs.cosyte.com/fhir) walks the same document step
|
|
104
|
+
by step, and [`examples/`](https://github.com/cosyte/fhir/tree/main/examples) holds four runnable
|
|
105
|
+
programs: read and validate, the safety readout, JSON and XML, and Bundles with Bulk Data NDJSON.
|
|
106
|
+
|
|
107
|
+
## API
|
|
108
|
+
|
|
109
|
+
Everything ships from one entry point; there are no subpath imports.
|
|
110
|
+
|
|
111
|
+
- **Read and write.** `parseResource` and `serializeResource` for JSON, `parseResourceXml` and
|
|
112
|
+
`serializeResourceXml` for XML, over one schema-free model, and `nodesEquivalent` to compare a
|
|
113
|
+
model read from one format with a model read from the other. A `decimal` or an `integer64` keeps
|
|
114
|
+
its exact text (`FhirDecimal`, `FhirInteger64`) and never passes through a JavaScript number.
|
|
115
|
+
- **Validate.** `validateResource` returns `valid`, the issues, and `toOperationOutcome()` to render
|
|
116
|
+
them as an `OperationOutcome`. Pass the `profiles` you load with `loadStructureDefinition` or
|
|
117
|
+
author with `defineProfile`, and a `terminology` service of your own for code validity.
|
|
118
|
+
- **Read status and negation.** `readSafety` surfaces status, modifiers and negations.
|
|
119
|
+
`assertSafeToSummarize` throws `FhirSafetyError`, carrying locations only, when a resource holds
|
|
120
|
+
something it cannot summarize honestly, such as a `modifierExtension` it does not understand.
|
|
121
|
+
- **Read measured values.** `readObservationValue` branches on the `value[x]` type and keeps the
|
|
122
|
+
UCUM `code`, and `readMedicationDoses` reads dose quantities. No unit is ever converted.
|
|
123
|
+
- **Bundles and NDJSON.** `readBundle` tells a transaction (all or nothing) from a batch and
|
|
124
|
+
executes neither, `resolveReference` resolves relative, absolute, logical and `#fragment`
|
|
125
|
+
references, and `streamNdjson` reads a Bulk Data export line by line, reporting a malformed line
|
|
126
|
+
and carrying on.
|
|
127
|
+
|
|
128
|
+
## PHI and safety
|
|
129
|
+
|
|
130
|
+
FHIR resources carry protected health information, so treat every document you hand this library
|
|
131
|
+
as PHI.
|
|
132
|
+
|
|
133
|
+
- **It works in memory.** It reads, validates and writes in memory and nothing else. It opens no
|
|
134
|
+
socket, writes nothing to disk and fetches nothing at run time; it has zero runtime dependencies,
|
|
135
|
+
imports no Node built-in, and calls no terminology service unless you pass one in.
|
|
136
|
+
- **Diagnostics carry no values.** An issue is a code and a FHIRPath location, never the value it
|
|
137
|
+
was raised over. A location can repeat an element name the document supplied when that name has
|
|
138
|
+
the published form of a FHIR name, so treat a location as derived from the document.
|
|
139
|
+
- **The XML reader accepts plain XML only.** Anything else is refused with a typed `FhirXmlError`
|
|
140
|
+
that carries a code and a byte offset, never a slice of the document. The reader performs no I/O
|
|
141
|
+
and resolves no URI. The troubleshooting pages on
|
|
142
|
+
[docs.cosyte.com/fhir](https://docs.cosyte.com/fhir) list each refusal and its code.
|
|
143
|
+
- **Nesting is bounded.** Both readers stop at a fixed depth with a typed error, so a deeply nested
|
|
144
|
+
document fails cleanly rather than exhausting the stack.
|
|
145
|
+
- **A status or a negation is not skipped silently.** `assertSafeToSummarize` refuses a resource
|
|
146
|
+
carrying a `modifierExtension` it does not understand, and each other shape it cannot summarize
|
|
147
|
+
honestly, rather than letting a summary walk past it.
|
|
148
|
+
- **What you still own.** Transport, storage, retention, access control, audit logging, and every
|
|
149
|
+
log line your own code writes. This library makes no compliance claim on your behalf.
|
|
150
|
+
|
|
151
|
+
## Documentation
|
|
152
|
+
|
|
153
|
+
- [docs.cosyte.com/fhir](https://docs.cosyte.com/fhir): installation, the quickstart, core
|
|
154
|
+
concepts, guides, current limits and troubleshooting.
|
|
155
|
+
- [`documentation/capabilities.md`](https://github.com/cosyte/fhir/blob/main/documentation/capabilities.md):
|
|
156
|
+
the full capability readout, layer by layer, with what each one checks and what it leaves open.
|
|
157
|
+
- [`documentation/`](https://github.com/cosyte/fhir/tree/main/documentation) in the repository: the
|
|
158
|
+
capability readout, the FHIRPath coverage measured against the shared R4 conformance suite, and
|
|
159
|
+
the architecture decisions.
|
|
160
|
+
- [`CHANGELOG.md`](https://github.com/cosyte/fhir/blob/main/CHANGELOG.md): every change, with the
|
|
161
|
+
breaking ones called out.
|
|
162
|
+
|
|
163
|
+
## Contributing
|
|
164
|
+
|
|
165
|
+
Questions, bug reports and FHIR quirks from the servers and EHRs you connect to all belong in
|
|
166
|
+
[GitHub issues](https://github.com/cosyte/fhir/issues). Pull requests are welcome; every fixture
|
|
167
|
+
must be synthetic, never a real patient's data.
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
pnpm install
|
|
171
|
+
pnpm build # dual ESM + CJS + .d.ts
|
|
172
|
+
pnpm typecheck
|
|
173
|
+
pnpm lint
|
|
174
|
+
pnpm test
|
|
12
175
|
```
|
|
13
176
|
|
|
177
|
+
Every meaningful change gets a Changeset (`pnpm changeset`: `minor` for new capability or a breaking
|
|
178
|
+
change while the version is below 1.0, `patch` for a fix) and a `CHANGELOG.md` `[Unreleased]`
|
|
179
|
+
entry. See [`CONTRIBUTING.md`](https://github.com/cosyte/fhir/blob/main/CONTRIBUTING.md).
|
|
180
|
+
|
|
14
181
|
## License
|
|
15
182
|
|
|
16
|
-
MIT
|
|
183
|
+
MIT, copyright Cosyte. See [LICENSE](./LICENSE).
|