@cosyte/fhir 0.1.1 → 0.1.2
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/LICENSE +1 -1
- package/README.md +27 -25
- package/dist/index.cjs +14 -6
- package/dist/index.d.cts +32 -17
- package/dist/index.d.ts +32 -17
- package/dist/index.mjs +14 -6
- package/package.json +7 -7
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<a href="https://cosyte.com">
|
|
2
2
|
<picture>
|
|
3
3
|
<source media="(prefers-color-scheme: dark)" srcset="https://cosyte.com/tile/cosyte-lockup-tile-on-dark-1200x300.png">
|
|
4
|
-
<img alt="Cosyte
|
|
4
|
+
<img alt="The Cosyte logo on its own white ground: the icon beside the word Cosyte." src="https://cosyte.com/tile/cosyte-lockup-tile-on-light-1200x300.png">
|
|
5
5
|
</picture>
|
|
6
6
|
</a>
|
|
7
7
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
[](https://www.npmjs.com/package/@cosyte/fhir)
|
|
14
14
|
[](https://github.com/cosyte/fhir/actions/workflows/ci.yml)
|
|
15
|
-
[](
|
|
15
|
+
[](https://github.com/cosyte/fhir/blob/main/LICENSE)
|
|
16
16
|
[](https://nodejs.org)
|
|
17
17
|
|
|
18
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.
|
|
@@ -20,18 +20,18 @@ FHIR R4 for Node.js and TypeScript: a resource model that keeps every value exac
|
|
|
20
20
|
## Why this exists
|
|
21
21
|
|
|
22
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
|
|
23
|
+
`0.010`, an `integer64` has to keep every digit, and a status, a modifier, or a negation must never be
|
|
24
24
|
skipped, because a missed `refuted` reads as its opposite. We built `@cosyte/fhir` for the engineer
|
|
25
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
26
|
Core and SMART on FHIR) and has to get those values right without reading the specification first.
|
|
27
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
|
|
28
|
+
turns `0.010` into the number `0.01` and knows nothing about modifiers. This library reads, writes,
|
|
29
29
|
and validates FHIR R4 with zero runtime dependencies, and tells you with a coded reason what it could
|
|
30
30
|
not read safely.
|
|
31
31
|
|
|
32
32
|
## Status
|
|
33
33
|
|
|
34
|
-
**The 0.1 line.** From 0.1 the public API is settled: the exported names, options, result shapes and
|
|
34
|
+
**The 0.1 line.** From 0.1 the public API is settled: the exported names, options, result shapes, and
|
|
35
35
|
issue codes are the surface we keep stable. While the package is below 1.0, a breaking change ships
|
|
36
36
|
in a minor version and is called out in the
|
|
37
37
|
[changelog](https://github.com/cosyte/fhir/blob/main/CHANGELOG.md). Upgrading from 0.0.10 changes
|
|
@@ -47,10 +47,10 @@ and DSTU2 documents can be read, since the model is schema-free, but they are no
|
|
|
47
47
|
their own definitions.
|
|
48
48
|
|
|
49
49
|
Not covered yet: typed per-resource models (the built-in structural schemas cover the base elements
|
|
50
|
-
plus
|
|
51
|
-
`PROFILE_SLICE_UNCHECKED`), FHIRPath outside the supported subset (reported
|
|
52
|
-
never passed), bundled terminology or US Core content, spec-clean JSON written
|
|
53
|
-
of XML, unit conversion, and executing a transaction Bundle.
|
|
50
|
+
plus the eight modeled resource types), `type` and `profile` slicing discriminators and reslicing
|
|
51
|
+
(reported `PROFILE_SLICE_UNCHECKED`), FHIRPath outside the supported subset (reported
|
|
52
|
+
`INVARIANT_UNCHECKED`, never passed), bundled terminology or US Core content, spec-clean JSON written
|
|
53
|
+
from a model read out of XML, unit conversion, and executing a transaction Bundle.
|
|
54
54
|
|
|
55
55
|
## Install
|
|
56
56
|
|
|
@@ -59,9 +59,9 @@ of XML, unit conversion, and executing a transaction Bundle.
|
|
|
59
59
|
pnpm add @cosyte/fhir
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
Requires Node
|
|
63
|
-
both, so `import` and `require` both work and neither needs a
|
|
64
|
-
runtime dependencies, and it imports no Node built-in.
|
|
62
|
+
Requires Node.js 22 or later, tested on 22 and 24. The package ships dual ESM and CommonJS builds
|
|
63
|
+
with type declarations for both, so `import` and `require` both work and neither needs a
|
|
64
|
+
compatibility shim. There are zero runtime dependencies, and it imports no Node built-in.
|
|
65
65
|
|
|
66
66
|
## Usage
|
|
67
67
|
|
|
@@ -115,13 +115,13 @@ Everything ships from one entry point; there are no subpath imports.
|
|
|
115
115
|
- **Validate.** `validateResource` returns `valid`, the issues, and `toOperationOutcome()` to render
|
|
116
116
|
them as an `OperationOutcome`. Pass the `profiles` you load with `loadStructureDefinition` or
|
|
117
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.
|
|
118
|
+
- **Read status and negation.** `readSafety` surfaces status, modifiers, and negations.
|
|
119
119
|
`assertSafeToSummarize` throws `FhirSafetyError`, carrying locations only, when a resource holds
|
|
120
120
|
something it cannot summarize honestly, such as a `modifierExtension` it does not understand.
|
|
121
121
|
- **Read measured values.** `readObservationValue` branches on the `value[x]` type and keeps the
|
|
122
122
|
UCUM `code`, and `readMedicationDoses` reads dose quantities. No unit is ever converted.
|
|
123
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`
|
|
124
|
+
executes neither, `resolveReference` resolves relative, absolute, logical, and `#fragment`
|
|
125
125
|
references, and `streamNdjson` reads a Bulk Data export line by line, reporting a malformed line
|
|
126
126
|
and carrying on.
|
|
127
127
|
|
|
@@ -130,16 +130,16 @@ Everything ships from one entry point; there are no subpath imports.
|
|
|
130
130
|
FHIR resources carry protected health information, so treat every document you hand this library
|
|
131
131
|
as PHI.
|
|
132
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,
|
|
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
135
|
imports no Node built-in, and calls no terminology service unless you pass one in.
|
|
136
136
|
- **Diagnostics carry no values.** An issue is a code and a FHIRPath location, never the value it
|
|
137
137
|
was raised over. A location can repeat an element name the document supplied when that name has
|
|
138
138
|
the published form of a FHIR name, so treat a location as derived from the document.
|
|
139
139
|
- **The XML reader accepts plain XML only.** Anything else is refused with a typed `FhirXmlError`
|
|
140
|
-
that carries a code and
|
|
141
|
-
and resolves no URI. The troubleshooting pages
|
|
142
|
-
[docs.cosyte.com/fhir](https://docs.cosyte.com/fhir) list each refusal and its code.
|
|
140
|
+
that carries a code and an offset into the input string (in UTF-16 code units, not bytes), never a
|
|
141
|
+
slice of the document. The reader performs no I/O and resolves no URI. The troubleshooting pages
|
|
142
|
+
on [docs.cosyte.com/fhir](https://docs.cosyte.com/fhir) list each refusal and its code.
|
|
143
143
|
- **Nesting is bounded.** Both readers stop at a fixed depth with a typed error, so a deeply nested
|
|
144
144
|
document fails cleanly rather than exhausting the stack.
|
|
145
145
|
- **A status or a negation is not skipped silently.** `assertSafeToSummarize` refuses a resource
|
|
@@ -151,7 +151,7 @@ as PHI.
|
|
|
151
151
|
## Documentation
|
|
152
152
|
|
|
153
153
|
- [docs.cosyte.com/fhir](https://docs.cosyte.com/fhir): installation, the quickstart, core
|
|
154
|
-
concepts, guides, current limits and troubleshooting.
|
|
154
|
+
concepts, guides, current limits, and troubleshooting.
|
|
155
155
|
- [`documentation/capabilities.md`](https://github.com/cosyte/fhir/blob/main/documentation/capabilities.md):
|
|
156
156
|
the full capability readout, layer by layer, with what each one checks and what it leaves open.
|
|
157
157
|
- [`documentation/`](https://github.com/cosyte/fhir/tree/main/documentation) in the repository: the
|
|
@@ -162,7 +162,7 @@ as PHI.
|
|
|
162
162
|
|
|
163
163
|
## Contributing
|
|
164
164
|
|
|
165
|
-
Questions, bug reports and FHIR quirks from the servers and EHRs you connect to all belong in
|
|
165
|
+
Questions, bug reports, and FHIR quirks from the servers and EHRs you connect to all belong in
|
|
166
166
|
[GitHub issues](https://github.com/cosyte/fhir/issues). Pull requests are welcome; every fixture
|
|
167
167
|
must be synthetic, never a real patient's data.
|
|
168
168
|
|
|
@@ -174,10 +174,12 @@ pnpm lint
|
|
|
174
174
|
pnpm test
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
-
Every meaningful change gets a
|
|
178
|
-
change while the version is below 1.0, `patch` for a fix) and
|
|
179
|
-
entry. See [`CONTRIBUTING.md`](https://github.com/cosyte/fhir/blob/main/CONTRIBUTING.md).
|
|
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 its summary becomes the change's
|
|
179
|
+
entry in `CHANGELOG.md`. See [`CONTRIBUTING.md`](https://github.com/cosyte/fhir/blob/main/CONTRIBUTING.md).
|
|
180
180
|
|
|
181
181
|
## License
|
|
182
182
|
|
|
183
|
-
MIT, copyright Cosyte. See [LICENSE](
|
|
183
|
+
MIT, copyright Cosyte LLC. See [LICENSE](https://github.com/cosyte/fhir/blob/main/LICENSE). The names of
|
|
184
|
+
the standards and code systems this package works with belong to their owners; see
|
|
185
|
+
[TRADEMARKS.md](https://github.com/cosyte/fhir/blob/main/TRADEMARKS.md).
|
package/dist/index.cjs
CHANGED
|
@@ -555,7 +555,7 @@ var FATAL_CODES = {
|
|
|
555
555
|
* JSON nested deeper than the reader's fixed bound. Well-formed but pathological input (a tower of
|
|
556
556
|
* `[[[[…]]]]` / `{"a":{"a":…}}`) is refused as a **DoS guard**, turning what would otherwise be a
|
|
557
557
|
* V8 stack overflow (`RangeError`, environment-dependent, untyped) into a typed, value-free fatal
|
|
558
|
-
* carrying
|
|
558
|
+
* carrying an `offset`. Mirrors the XML reader's `MAX_DEPTH_EXCEEDED` (fuzzing: deep
|
|
559
559
|
* nesting must never crash/hang/OOM, always a typed error or a bounded rejection).
|
|
560
560
|
*/
|
|
561
561
|
MAX_DEPTH_EXCEEDED: "MAX_DEPTH_EXCEEDED"
|
|
@@ -564,12 +564,16 @@ var FhirCodecError = class extends Error {
|
|
|
564
564
|
code;
|
|
565
565
|
/** FHIRPath location of the failure, when it has one (misalignment). */
|
|
566
566
|
expression;
|
|
567
|
-
/**
|
|
567
|
+
/**
|
|
568
|
+
* Where in the input the failure was detected, when it has one (malformed JSON), as an index into
|
|
569
|
+
* the input string in UTF-16 code units (JavaScript string indexing), not a byte offset: the two
|
|
570
|
+
* differ once a non-ASCII character precedes the failure.
|
|
571
|
+
*/
|
|
568
572
|
offset;
|
|
569
573
|
/**
|
|
570
574
|
* @param code - The fatal reason.
|
|
571
575
|
* @param message - A PHI-safe description, must not embed any input value.
|
|
572
|
-
* @param location - Either a FHIRPath `expression` or a
|
|
576
|
+
* @param location - Either a FHIRPath `expression` or a string-index `offset`.
|
|
573
577
|
* @internal
|
|
574
578
|
*/
|
|
575
579
|
constructor(code, message, location = {}) {
|
|
@@ -6653,12 +6657,16 @@ var XML_FATAL_CODES = {
|
|
|
6653
6657
|
};
|
|
6654
6658
|
var FhirXmlError = class extends Error {
|
|
6655
6659
|
code;
|
|
6656
|
-
/**
|
|
6660
|
+
/**
|
|
6661
|
+
* Where in the input the failure was detected, as an index into the input string in UTF-16 code
|
|
6662
|
+
* units (JavaScript string indexing), not a byte offset: the two differ once a non-ASCII character
|
|
6663
|
+
* precedes the failure.
|
|
6664
|
+
*/
|
|
6657
6665
|
offset;
|
|
6658
6666
|
/**
|
|
6659
6667
|
* @param code - The fatal reason.
|
|
6660
6668
|
* @param message - A PHI-safe description, must not embed any input value.
|
|
6661
|
-
* @param offset - The
|
|
6669
|
+
* @param offset - The string index (UTF-16 code units) where the failure was detected.
|
|
6662
6670
|
* @internal
|
|
6663
6671
|
*/
|
|
6664
6672
|
constructor(code, message, offset) {
|
|
@@ -7550,7 +7558,7 @@ async function* streamNdjson(source, options = {}) {
|
|
|
7550
7558
|
}
|
|
7551
7559
|
|
|
7552
7560
|
// src/index.ts
|
|
7553
|
-
var VERSION = "0.1.
|
|
7561
|
+
var VERSION = "0.1.2";
|
|
7554
7562
|
|
|
7555
7563
|
exports.ABSENCE_CODES = ABSENCE_CODES;
|
|
7556
7564
|
exports.ALLERGY_CLINICAL_SYSTEM = ALLERGY_CLINICAL_SYSTEM;
|
package/dist/index.d.cts
CHANGED
|
@@ -793,7 +793,7 @@ declare function parseReference(raw: string): ParsedReference;
|
|
|
793
793
|
* **PHI discipline.** A FHIR resource is PHI by default, and diagnostics are the leak
|
|
794
794
|
* vector. Every issue and every error here is **value-free by construction**: it carries a coded
|
|
795
795
|
* `code`, a `severity`, and an `expression` (a FHIRPath *location* such as `Patient.name[0].given[1]`),
|
|
796
|
-
* never the offending value. The `FhirCodecError` for malformed JSON carries
|
|
796
|
+
* never the offending value. The `FhirCodecError` for malformed JSON carries an `offset`, not
|
|
797
797
|
* the surrounding text.
|
|
798
798
|
*
|
|
799
799
|
* @packageDocumentation
|
|
@@ -1122,7 +1122,7 @@ declare const FATAL_CODES: {
|
|
|
1122
1122
|
* JSON nested deeper than the reader's fixed bound. Well-formed but pathological input (a tower of
|
|
1123
1123
|
* `[[[[…]]]]` / `{"a":{"a":…}}`) is refused as a **DoS guard**, turning what would otherwise be a
|
|
1124
1124
|
* V8 stack overflow (`RangeError`, environment-dependent, untyped) into a typed, value-free fatal
|
|
1125
|
-
* carrying
|
|
1125
|
+
* carrying an `offset`. Mirrors the XML reader's `MAX_DEPTH_EXCEEDED` (fuzzing: deep
|
|
1126
1126
|
* nesting must never crash/hang/OOM, always a typed error or a bounded rejection).
|
|
1127
1127
|
*/
|
|
1128
1128
|
readonly MAX_DEPTH_EXCEEDED: "MAX_DEPTH_EXCEEDED";
|
|
@@ -1150,12 +1150,16 @@ declare class FhirCodecError extends Error {
|
|
|
1150
1150
|
readonly code: FatalCode;
|
|
1151
1151
|
/** FHIRPath location of the failure, when it has one (misalignment). */
|
|
1152
1152
|
readonly expression: string | undefined;
|
|
1153
|
-
/**
|
|
1153
|
+
/**
|
|
1154
|
+
* Where in the input the failure was detected, when it has one (malformed JSON), as an index into
|
|
1155
|
+
* the input string in UTF-16 code units (JavaScript string indexing), not a byte offset: the two
|
|
1156
|
+
* differ once a non-ASCII character precedes the failure.
|
|
1157
|
+
*/
|
|
1154
1158
|
readonly offset: number | undefined;
|
|
1155
1159
|
/**
|
|
1156
1160
|
* @param code - The fatal reason.
|
|
1157
1161
|
* @param message - A PHI-safe description, must not embed any input value.
|
|
1158
|
-
* @param location - Either a FHIRPath `expression` or a
|
|
1162
|
+
* @param location - Either a FHIRPath `expression` or a string-index `offset`.
|
|
1159
1163
|
* @internal
|
|
1160
1164
|
*/
|
|
1161
1165
|
constructor(code: FatalCode, message: string, location?: {
|
|
@@ -1175,7 +1179,7 @@ declare class FhirCodecError extends Error {
|
|
|
1175
1179
|
*
|
|
1176
1180
|
* The output is a raw JSON tree ({@link RawJson}) that preserves member order (FHIR allows
|
|
1177
1181
|
* `resourceType` in any position) and number literals verbatim. Malformed input throws a value-free
|
|
1178
|
-
* {@link FhirCodecError} carrying
|
|
1182
|
+
* {@link FhirCodecError} carrying an offset, never a snippet (the snippet could be PHI).
|
|
1179
1183
|
*
|
|
1180
1184
|
* @packageDocumentation
|
|
1181
1185
|
*/
|
|
@@ -1219,7 +1223,7 @@ type RawJson = RawObject | RawArray | RawString | RawNumber | RawBool | RawNull;
|
|
|
1219
1223
|
* Parse a JSON document into a {@link RawJson} tree that preserves number literals verbatim and
|
|
1220
1224
|
* member order. Throws {@link FhirCodecError} (`MALFORMED_JSON`) on invalid input, or
|
|
1221
1225
|
* (`MAX_DEPTH_EXCEEDED`) when nesting passes the reader's fixed depth bound, both value-free, with a
|
|
1222
|
-
*
|
|
1226
|
+
* `offset` and no snippet.
|
|
1223
1227
|
*
|
|
1224
1228
|
* @param src - The JSON text.
|
|
1225
1229
|
* @example
|
|
@@ -5499,8 +5503,8 @@ declare function matchesPattern(instance: FhirNode | undefined, pattern: FhirNod
|
|
|
5499
5503
|
*
|
|
5500
5504
|
* Profile validation and slicing discriminators are expressed as **element paths** relative to a
|
|
5501
5505
|
* resource or a slice element (e.g. `clinicalStatus.coding.code`, `value[x]`, `$this`). This module
|
|
5502
|
-
* resolves such a path to the set of model nodes it selects, flattening repeating elements
|
|
5503
|
-
* honoring `[x]` choice variants. It is a deliberately small, FHIRPath-*shaped* navigator, not the
|
|
5506
|
+
* resolves such a path to the set of model nodes it selects, flattening the repeating elements it
|
|
5507
|
+
* walks through and honoring `[x]` choice variants. It is a deliberately small, FHIRPath-*shaped* navigator, not the
|
|
5504
5508
|
* FHIRPath engine: it walks dotted member access and choice suffixes,
|
|
5505
5509
|
* which is exactly what StructureDefinition discriminator paths and fixed/pattern locations use, and
|
|
5506
5510
|
* nothing more (no functions, no filters, no arithmetic).
|
|
@@ -5511,8 +5515,11 @@ declare function matchesPattern(instance: FhirNode | undefined, pattern: FhirNod
|
|
|
5511
5515
|
/**
|
|
5512
5516
|
* Resolve an element path against a node, returning every node it selects (empty when nothing
|
|
5513
5517
|
* matches). `$this` (or the empty path) selects the node itself. A `[x]` segment matches any concrete
|
|
5514
|
-
* choice variant (`value[x]` → `valueQuantity`, `valueString`, …).
|
|
5515
|
-
* so `coding.code` on a `CodeableConcept` with three codings yields three
|
|
5518
|
+
* choice variant (`value[x]` → `valueQuantity`, `valueString`, …). A repeating element the path
|
|
5519
|
+
* walks through is flattened, so `coding.code` on a `CodeableConcept` with three codings yields three
|
|
5520
|
+
* nodes. A repeating element in the last segment is not: it comes back as its list node
|
|
5521
|
+
* (`kind: "list"`, members on `items`), so `name` on a Patient with two names yields one node.
|
|
5522
|
+
* Flatten it with `isList(node) ? node.items : [node]`.
|
|
5516
5523
|
*
|
|
5517
5524
|
* @param node - The starting node (a resource, a slice element instance, …).
|
|
5518
5525
|
* @param path - A dotted element path relative to `node` (`""` / `"$this"` selects `node`).
|
|
@@ -5692,7 +5699,9 @@ interface ProfileOptions {
|
|
|
5692
5699
|
* ```ts
|
|
5693
5700
|
* import { collectProfileIssues, loadStructureDefinition, parseResource } from "@cosyte/fhir";
|
|
5694
5701
|
* const profile = loadStructureDefinition(parseResource(usCoreAllergyJson).resource);
|
|
5695
|
-
*
|
|
5702
|
+
* if (profile !== undefined) {
|
|
5703
|
+
* const issues = collectProfileIssues(parseResource(allergyJson).resource, profile);
|
|
5704
|
+
* }
|
|
5696
5705
|
* ```
|
|
5697
5706
|
*/
|
|
5698
5707
|
declare function collectProfileIssues(resource: FhirComplex, profile: StructureDefinition, options?: ProfileOptions): ValidationIssue[];
|
|
@@ -5769,7 +5778,9 @@ interface InvariantOptions {
|
|
|
5769
5778
|
* ```ts
|
|
5770
5779
|
* import { collectInvariantIssues, loadStructureDefinition, parseResource } from "@cosyte/fhir";
|
|
5771
5780
|
* const profile = loadStructureDefinition(parseResource(usCoreProfileJson).resource);
|
|
5772
|
-
*
|
|
5781
|
+
* if (profile !== undefined) {
|
|
5782
|
+
* const issues = collectInvariantIssues(parseResource(instanceJson).resource, profile);
|
|
5783
|
+
* }
|
|
5773
5784
|
* ```
|
|
5774
5785
|
*/
|
|
5775
5786
|
declare function collectInvariantIssues(resource: FhirComplex, profile: StructureDefinition, options?: InvariantOptions): ValidationIssue[];
|
|
@@ -6325,7 +6336,7 @@ type XmlNode = XmlElement | XmlText;
|
|
|
6325
6336
|
* decoding the five predefined entities and numeric character references. **Refuses** any DTD
|
|
6326
6337
|
* (`DTD_FORBIDDEN`) or non-predefined entity (`UNDEFINED_ENTITY`), and bounds nesting depth, so it
|
|
6327
6338
|
* is XXE- and billion-laughs-safe and never crashes on adversarial input. Throws
|
|
6328
|
-
* {@link FhirXmlError} on any refusal or well-formedness error, with
|
|
6339
|
+
* {@link FhirXmlError} on any refusal or well-formedness error, with an `offset` and no snippet.
|
|
6329
6340
|
*
|
|
6330
6341
|
* @param src - The XML text.
|
|
6331
6342
|
* @example
|
|
@@ -6363,7 +6374,7 @@ declare function readRawXml(src: string): XmlElement;
|
|
|
6363
6374
|
* {@link XML_FATAL_CODES.MAX_DEPTH_EXCEEDED} so adversarial input yields a typed error, never a stack
|
|
6364
6375
|
* overflow.
|
|
6365
6376
|
*
|
|
6366
|
-
* Every message is **value-free**: it carries a coded reason and
|
|
6377
|
+
* Every message is **value-free**: it carries a coded reason and an `offset`, never
|
|
6367
6378
|
* a slice of the offending document, because that slice could be PHI.
|
|
6368
6379
|
*
|
|
6369
6380
|
* @packageDocumentation
|
|
@@ -6394,7 +6405,7 @@ type XmlFatalCode = (typeof XML_FATAL_CODES)[keyof typeof XML_FATAL_CODES];
|
|
|
6394
6405
|
/**
|
|
6395
6406
|
* Thrown by the XML reader on an unrecoverable failure, a well-formedness error, a **refused** DTD
|
|
6396
6407
|
* or entity (the safety refusals), or nesting past the depth bound. Carries the coded reason and a
|
|
6397
|
-
*
|
|
6408
|
+
* `offset` into the input, and, by design, **no** slice of the offending input, because that slice could be
|
|
6398
6409
|
* PHI.
|
|
6399
6410
|
*
|
|
6400
6411
|
* @example
|
|
@@ -6411,12 +6422,16 @@ type XmlFatalCode = (typeof XML_FATAL_CODES)[keyof typeof XML_FATAL_CODES];
|
|
|
6411
6422
|
*/
|
|
6412
6423
|
declare class FhirXmlError extends Error {
|
|
6413
6424
|
readonly code: XmlFatalCode;
|
|
6414
|
-
/**
|
|
6425
|
+
/**
|
|
6426
|
+
* Where in the input the failure was detected, as an index into the input string in UTF-16 code
|
|
6427
|
+
* units (JavaScript string indexing), not a byte offset: the two differ once a non-ASCII character
|
|
6428
|
+
* precedes the failure.
|
|
6429
|
+
*/
|
|
6415
6430
|
readonly offset: number;
|
|
6416
6431
|
/**
|
|
6417
6432
|
* @param code - The fatal reason.
|
|
6418
6433
|
* @param message - A PHI-safe description, must not embed any input value.
|
|
6419
|
-
* @param offset - The
|
|
6434
|
+
* @param offset - The string index (UTF-16 code units) where the failure was detected.
|
|
6420
6435
|
* @internal
|
|
6421
6436
|
*/
|
|
6422
6437
|
constructor(code: XmlFatalCode, message: string, offset: number);
|
package/dist/index.d.ts
CHANGED
|
@@ -793,7 +793,7 @@ declare function parseReference(raw: string): ParsedReference;
|
|
|
793
793
|
* **PHI discipline.** A FHIR resource is PHI by default, and diagnostics are the leak
|
|
794
794
|
* vector. Every issue and every error here is **value-free by construction**: it carries a coded
|
|
795
795
|
* `code`, a `severity`, and an `expression` (a FHIRPath *location* such as `Patient.name[0].given[1]`),
|
|
796
|
-
* never the offending value. The `FhirCodecError` for malformed JSON carries
|
|
796
|
+
* never the offending value. The `FhirCodecError` for malformed JSON carries an `offset`, not
|
|
797
797
|
* the surrounding text.
|
|
798
798
|
*
|
|
799
799
|
* @packageDocumentation
|
|
@@ -1122,7 +1122,7 @@ declare const FATAL_CODES: {
|
|
|
1122
1122
|
* JSON nested deeper than the reader's fixed bound. Well-formed but pathological input (a tower of
|
|
1123
1123
|
* `[[[[…]]]]` / `{"a":{"a":…}}`) is refused as a **DoS guard**, turning what would otherwise be a
|
|
1124
1124
|
* V8 stack overflow (`RangeError`, environment-dependent, untyped) into a typed, value-free fatal
|
|
1125
|
-
* carrying
|
|
1125
|
+
* carrying an `offset`. Mirrors the XML reader's `MAX_DEPTH_EXCEEDED` (fuzzing: deep
|
|
1126
1126
|
* nesting must never crash/hang/OOM, always a typed error or a bounded rejection).
|
|
1127
1127
|
*/
|
|
1128
1128
|
readonly MAX_DEPTH_EXCEEDED: "MAX_DEPTH_EXCEEDED";
|
|
@@ -1150,12 +1150,16 @@ declare class FhirCodecError extends Error {
|
|
|
1150
1150
|
readonly code: FatalCode;
|
|
1151
1151
|
/** FHIRPath location of the failure, when it has one (misalignment). */
|
|
1152
1152
|
readonly expression: string | undefined;
|
|
1153
|
-
/**
|
|
1153
|
+
/**
|
|
1154
|
+
* Where in the input the failure was detected, when it has one (malformed JSON), as an index into
|
|
1155
|
+
* the input string in UTF-16 code units (JavaScript string indexing), not a byte offset: the two
|
|
1156
|
+
* differ once a non-ASCII character precedes the failure.
|
|
1157
|
+
*/
|
|
1154
1158
|
readonly offset: number | undefined;
|
|
1155
1159
|
/**
|
|
1156
1160
|
* @param code - The fatal reason.
|
|
1157
1161
|
* @param message - A PHI-safe description, must not embed any input value.
|
|
1158
|
-
* @param location - Either a FHIRPath `expression` or a
|
|
1162
|
+
* @param location - Either a FHIRPath `expression` or a string-index `offset`.
|
|
1159
1163
|
* @internal
|
|
1160
1164
|
*/
|
|
1161
1165
|
constructor(code: FatalCode, message: string, location?: {
|
|
@@ -1175,7 +1179,7 @@ declare class FhirCodecError extends Error {
|
|
|
1175
1179
|
*
|
|
1176
1180
|
* The output is a raw JSON tree ({@link RawJson}) that preserves member order (FHIR allows
|
|
1177
1181
|
* `resourceType` in any position) and number literals verbatim. Malformed input throws a value-free
|
|
1178
|
-
* {@link FhirCodecError} carrying
|
|
1182
|
+
* {@link FhirCodecError} carrying an offset, never a snippet (the snippet could be PHI).
|
|
1179
1183
|
*
|
|
1180
1184
|
* @packageDocumentation
|
|
1181
1185
|
*/
|
|
@@ -1219,7 +1223,7 @@ type RawJson = RawObject | RawArray | RawString | RawNumber | RawBool | RawNull;
|
|
|
1219
1223
|
* Parse a JSON document into a {@link RawJson} tree that preserves number literals verbatim and
|
|
1220
1224
|
* member order. Throws {@link FhirCodecError} (`MALFORMED_JSON`) on invalid input, or
|
|
1221
1225
|
* (`MAX_DEPTH_EXCEEDED`) when nesting passes the reader's fixed depth bound, both value-free, with a
|
|
1222
|
-
*
|
|
1226
|
+
* `offset` and no snippet.
|
|
1223
1227
|
*
|
|
1224
1228
|
* @param src - The JSON text.
|
|
1225
1229
|
* @example
|
|
@@ -5499,8 +5503,8 @@ declare function matchesPattern(instance: FhirNode | undefined, pattern: FhirNod
|
|
|
5499
5503
|
*
|
|
5500
5504
|
* Profile validation and slicing discriminators are expressed as **element paths** relative to a
|
|
5501
5505
|
* resource or a slice element (e.g. `clinicalStatus.coding.code`, `value[x]`, `$this`). This module
|
|
5502
|
-
* resolves such a path to the set of model nodes it selects, flattening repeating elements
|
|
5503
|
-
* honoring `[x]` choice variants. It is a deliberately small, FHIRPath-*shaped* navigator, not the
|
|
5506
|
+
* resolves such a path to the set of model nodes it selects, flattening the repeating elements it
|
|
5507
|
+
* walks through and honoring `[x]` choice variants. It is a deliberately small, FHIRPath-*shaped* navigator, not the
|
|
5504
5508
|
* FHIRPath engine: it walks dotted member access and choice suffixes,
|
|
5505
5509
|
* which is exactly what StructureDefinition discriminator paths and fixed/pattern locations use, and
|
|
5506
5510
|
* nothing more (no functions, no filters, no arithmetic).
|
|
@@ -5511,8 +5515,11 @@ declare function matchesPattern(instance: FhirNode | undefined, pattern: FhirNod
|
|
|
5511
5515
|
/**
|
|
5512
5516
|
* Resolve an element path against a node, returning every node it selects (empty when nothing
|
|
5513
5517
|
* matches). `$this` (or the empty path) selects the node itself. A `[x]` segment matches any concrete
|
|
5514
|
-
* choice variant (`value[x]` → `valueQuantity`, `valueString`, …).
|
|
5515
|
-
* so `coding.code` on a `CodeableConcept` with three codings yields three
|
|
5518
|
+
* choice variant (`value[x]` → `valueQuantity`, `valueString`, …). A repeating element the path
|
|
5519
|
+
* walks through is flattened, so `coding.code` on a `CodeableConcept` with three codings yields three
|
|
5520
|
+
* nodes. A repeating element in the last segment is not: it comes back as its list node
|
|
5521
|
+
* (`kind: "list"`, members on `items`), so `name` on a Patient with two names yields one node.
|
|
5522
|
+
* Flatten it with `isList(node) ? node.items : [node]`.
|
|
5516
5523
|
*
|
|
5517
5524
|
* @param node - The starting node (a resource, a slice element instance, …).
|
|
5518
5525
|
* @param path - A dotted element path relative to `node` (`""` / `"$this"` selects `node`).
|
|
@@ -5692,7 +5699,9 @@ interface ProfileOptions {
|
|
|
5692
5699
|
* ```ts
|
|
5693
5700
|
* import { collectProfileIssues, loadStructureDefinition, parseResource } from "@cosyte/fhir";
|
|
5694
5701
|
* const profile = loadStructureDefinition(parseResource(usCoreAllergyJson).resource);
|
|
5695
|
-
*
|
|
5702
|
+
* if (profile !== undefined) {
|
|
5703
|
+
* const issues = collectProfileIssues(parseResource(allergyJson).resource, profile);
|
|
5704
|
+
* }
|
|
5696
5705
|
* ```
|
|
5697
5706
|
*/
|
|
5698
5707
|
declare function collectProfileIssues(resource: FhirComplex, profile: StructureDefinition, options?: ProfileOptions): ValidationIssue[];
|
|
@@ -5769,7 +5778,9 @@ interface InvariantOptions {
|
|
|
5769
5778
|
* ```ts
|
|
5770
5779
|
* import { collectInvariantIssues, loadStructureDefinition, parseResource } from "@cosyte/fhir";
|
|
5771
5780
|
* const profile = loadStructureDefinition(parseResource(usCoreProfileJson).resource);
|
|
5772
|
-
*
|
|
5781
|
+
* if (profile !== undefined) {
|
|
5782
|
+
* const issues = collectInvariantIssues(parseResource(instanceJson).resource, profile);
|
|
5783
|
+
* }
|
|
5773
5784
|
* ```
|
|
5774
5785
|
*/
|
|
5775
5786
|
declare function collectInvariantIssues(resource: FhirComplex, profile: StructureDefinition, options?: InvariantOptions): ValidationIssue[];
|
|
@@ -6325,7 +6336,7 @@ type XmlNode = XmlElement | XmlText;
|
|
|
6325
6336
|
* decoding the five predefined entities and numeric character references. **Refuses** any DTD
|
|
6326
6337
|
* (`DTD_FORBIDDEN`) or non-predefined entity (`UNDEFINED_ENTITY`), and bounds nesting depth, so it
|
|
6327
6338
|
* is XXE- and billion-laughs-safe and never crashes on adversarial input. Throws
|
|
6328
|
-
* {@link FhirXmlError} on any refusal or well-formedness error, with
|
|
6339
|
+
* {@link FhirXmlError} on any refusal or well-formedness error, with an `offset` and no snippet.
|
|
6329
6340
|
*
|
|
6330
6341
|
* @param src - The XML text.
|
|
6331
6342
|
* @example
|
|
@@ -6363,7 +6374,7 @@ declare function readRawXml(src: string): XmlElement;
|
|
|
6363
6374
|
* {@link XML_FATAL_CODES.MAX_DEPTH_EXCEEDED} so adversarial input yields a typed error, never a stack
|
|
6364
6375
|
* overflow.
|
|
6365
6376
|
*
|
|
6366
|
-
* Every message is **value-free**: it carries a coded reason and
|
|
6377
|
+
* Every message is **value-free**: it carries a coded reason and an `offset`, never
|
|
6367
6378
|
* a slice of the offending document, because that slice could be PHI.
|
|
6368
6379
|
*
|
|
6369
6380
|
* @packageDocumentation
|
|
@@ -6394,7 +6405,7 @@ type XmlFatalCode = (typeof XML_FATAL_CODES)[keyof typeof XML_FATAL_CODES];
|
|
|
6394
6405
|
/**
|
|
6395
6406
|
* Thrown by the XML reader on an unrecoverable failure, a well-formedness error, a **refused** DTD
|
|
6396
6407
|
* or entity (the safety refusals), or nesting past the depth bound. Carries the coded reason and a
|
|
6397
|
-
*
|
|
6408
|
+
* `offset` into the input, and, by design, **no** slice of the offending input, because that slice could be
|
|
6398
6409
|
* PHI.
|
|
6399
6410
|
*
|
|
6400
6411
|
* @example
|
|
@@ -6411,12 +6422,16 @@ type XmlFatalCode = (typeof XML_FATAL_CODES)[keyof typeof XML_FATAL_CODES];
|
|
|
6411
6422
|
*/
|
|
6412
6423
|
declare class FhirXmlError extends Error {
|
|
6413
6424
|
readonly code: XmlFatalCode;
|
|
6414
|
-
/**
|
|
6425
|
+
/**
|
|
6426
|
+
* Where in the input the failure was detected, as an index into the input string in UTF-16 code
|
|
6427
|
+
* units (JavaScript string indexing), not a byte offset: the two differ once a non-ASCII character
|
|
6428
|
+
* precedes the failure.
|
|
6429
|
+
*/
|
|
6415
6430
|
readonly offset: number;
|
|
6416
6431
|
/**
|
|
6417
6432
|
* @param code - The fatal reason.
|
|
6418
6433
|
* @param message - A PHI-safe description, must not embed any input value.
|
|
6419
|
-
* @param offset - The
|
|
6434
|
+
* @param offset - The string index (UTF-16 code units) where the failure was detected.
|
|
6420
6435
|
* @internal
|
|
6421
6436
|
*/
|
|
6422
6437
|
constructor(code: XmlFatalCode, message: string, offset: number);
|
package/dist/index.mjs
CHANGED
|
@@ -553,7 +553,7 @@ var FATAL_CODES = {
|
|
|
553
553
|
* JSON nested deeper than the reader's fixed bound. Well-formed but pathological input (a tower of
|
|
554
554
|
* `[[[[…]]]]` / `{"a":{"a":…}}`) is refused as a **DoS guard**, turning what would otherwise be a
|
|
555
555
|
* V8 stack overflow (`RangeError`, environment-dependent, untyped) into a typed, value-free fatal
|
|
556
|
-
* carrying
|
|
556
|
+
* carrying an `offset`. Mirrors the XML reader's `MAX_DEPTH_EXCEEDED` (fuzzing: deep
|
|
557
557
|
* nesting must never crash/hang/OOM, always a typed error or a bounded rejection).
|
|
558
558
|
*/
|
|
559
559
|
MAX_DEPTH_EXCEEDED: "MAX_DEPTH_EXCEEDED"
|
|
@@ -562,12 +562,16 @@ var FhirCodecError = class extends Error {
|
|
|
562
562
|
code;
|
|
563
563
|
/** FHIRPath location of the failure, when it has one (misalignment). */
|
|
564
564
|
expression;
|
|
565
|
-
/**
|
|
565
|
+
/**
|
|
566
|
+
* Where in the input the failure was detected, when it has one (malformed JSON), as an index into
|
|
567
|
+
* the input string in UTF-16 code units (JavaScript string indexing), not a byte offset: the two
|
|
568
|
+
* differ once a non-ASCII character precedes the failure.
|
|
569
|
+
*/
|
|
566
570
|
offset;
|
|
567
571
|
/**
|
|
568
572
|
* @param code - The fatal reason.
|
|
569
573
|
* @param message - A PHI-safe description, must not embed any input value.
|
|
570
|
-
* @param location - Either a FHIRPath `expression` or a
|
|
574
|
+
* @param location - Either a FHIRPath `expression` or a string-index `offset`.
|
|
571
575
|
* @internal
|
|
572
576
|
*/
|
|
573
577
|
constructor(code, message, location = {}) {
|
|
@@ -6651,12 +6655,16 @@ var XML_FATAL_CODES = {
|
|
|
6651
6655
|
};
|
|
6652
6656
|
var FhirXmlError = class extends Error {
|
|
6653
6657
|
code;
|
|
6654
|
-
/**
|
|
6658
|
+
/**
|
|
6659
|
+
* Where in the input the failure was detected, as an index into the input string in UTF-16 code
|
|
6660
|
+
* units (JavaScript string indexing), not a byte offset: the two differ once a non-ASCII character
|
|
6661
|
+
* precedes the failure.
|
|
6662
|
+
*/
|
|
6655
6663
|
offset;
|
|
6656
6664
|
/**
|
|
6657
6665
|
* @param code - The fatal reason.
|
|
6658
6666
|
* @param message - A PHI-safe description, must not embed any input value.
|
|
6659
|
-
* @param offset - The
|
|
6667
|
+
* @param offset - The string index (UTF-16 code units) where the failure was detected.
|
|
6660
6668
|
* @internal
|
|
6661
6669
|
*/
|
|
6662
6670
|
constructor(code, message, offset) {
|
|
@@ -7548,7 +7556,7 @@ async function* streamNdjson(source, options = {}) {
|
|
|
7548
7556
|
}
|
|
7549
7557
|
|
|
7550
7558
|
// src/index.ts
|
|
7551
|
-
var VERSION = "0.1.
|
|
7559
|
+
var VERSION = "0.1.2";
|
|
7552
7560
|
|
|
7553
7561
|
export { ABSENCE_CODES, ALLERGY_CLINICAL_SYSTEM, ALLERGY_SUBSTANCE_VALUESET, ALLERGY_VERIFICATION_SYSTEM, BINDING_STRENGTHS, BUNDLE_TYPES, CODE_SYSTEM_VERSION_RECORD_CODES, CODE_SYSTEM_VERSION_RECORD_SYSTEM, CONDITION_CATEGORY_SYSTEM, CONDITION_CLINICAL_SYSTEM, CONDITION_VERIFICATION_SYSTEM, CPT_SYSTEM, CVX_SYSTEM, DATA_ABSENT_REASON_URL, DISCRIMINATOR_TYPES, ENTERED_IN_ERROR, FATAL_CODES, FHIR_XML_NAMESPACE, FhirCodecError, FhirDecimal, FhirInteger64, FhirProfileError, FhirSafetyError, FhirSerializeError, FhirXmlError, ICD10CM_SYSTEM, ICD9CM_SYSTEM, ISSUE_CODES, ISSUE_SEVERITIES, ISSUE_TYPES, InvalidProfileError, KNOWN_MODIFIER_EXTENSION_URLS, KNOWN_SYSTEMS, LOINC_SYSTEM, MAX_REFERENCE_DEPTH, MEDICATION_VALUESET, MODIFIER_ELEMENT_ROOT_TYPES, NDC_SYSTEM, NDJSON_ERROR_CODES, NOT_DONE, NOT_TAKEN, NO_KNOWN_ALLERGY, OBSERVATION_CATEGORY_SYSTEM, OBSERVATION_VALUE_TYPES, PATIENT_IDENTIFIER_STARTER, PRIMITIVE_TYPES, REFUTED, RXNORM_SYSTEM, SAFETY_RESOURCE_TYPES, SERIALIZE_ERROR_CODES, SNOMED_SCT, STARTER_PROFILES, STARTER_PROFILE_BASE_URL, TERMINOLOGY_BINDINGS, UCUM_SYSTEM, UNBOUNDED, UnsupportedFhirPathError, VALIDATION_CODES, VERSION, VITAL_SIGNS_CATEGORY, VITAL_SIGNS_PROFILE, VITAL_SIGN_OBSERVATION_STARTER, VITAL_SIGN_UNITS, WITHHELD, XHTML_NAMESPACE, XML_FATAL_CODES, absenceMarkers, arrayWrappedScalars, assertSafeToSummarize, baseSchema, buildBindingRegistry, buildBundleIndex, buildRegistry, codeOf, codingsOf, collectBundleIssues, collectInvariantIssues, collectProfileIssues, collectProfileVersionIssues, collectQuantityIssues, collectSafetyIssues, collectTerminologyIssues, complex, conflictingAbsenceMarkers, containedIndex, convertToBoolean, decimal, decimalPrecisionAtRisk, defineProfile, diagnosticFor, droppedText, duplicateProperty, entryProcessing, evaluateInvariant, generateSnapshot, getAllProperties, getProperty, hasCodeAnySystem, hasCoding, hasContainedCycle, integer64, isAbsenceCode, isAtomicBundle, isChoice, isComplex, isDroppedText, isForeignRoot, isKnownSystem, isList, isNestedArray, isPrimitive, isPrimitiveType, isRetracted, isUndefinedNull, list, loadStructureDefinition, locateDoseQuantities, matchSlices, matchesFixed, matchesPattern, misplacedPrimitiveExtension, mixedXmlSpelling, modifierElements, nearMissNegationCodes, nestedArray, nestedArrayContent, nestedArrays, nodesEquivalent, parseFhirPath, parseNdjsonLine, parseReference, parseResource, parseResourceXml, pathExists, primitive, readBundle, readInterpretations, readMedicationDoses, readObservationValue, readQuantity, readRawJson, readRawXml, readReferenceRanges, readSafety, requiredVitalSignUnits, resolveElement, resolvePath, resolveReference, resolveSlices, resourceType, serializeResource, serializeResourceXml, shadowedProperties, snapshotElements, starterProfile, streamNdjson, toOperationOutcome, tokenize, undefinedJsonNull, unexpectedXmlContent, unhandledModifierExtensions, unknownProperty, unreadableAbsenceMarkers, unreadableBooleans, unreadableNegationCodes, validatePrimitiveValue, validateResource, validateUcumShape, validationIssue, wouldLosePrecisionAsDouble };
|
|
7554
7562
|
//# sourceMappingURL=index.mjs.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosyte/fhir",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "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.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fhir",
|
|
@@ -68,12 +68,12 @@
|
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@arethetypeswrong/cli": "0.18.4",
|
|
70
70
|
"@changesets/cli": "2.31.0",
|
|
71
|
-
"@cosyte/eslint-config": "^0.
|
|
72
|
-
"@cosyte/prettier-config": "^0.
|
|
73
|
-
"@cosyte/test-utils": "^0.
|
|
74
|
-
"@cosyte/tsconfig": "^0.
|
|
75
|
-
"@cosyte/tsup-config": "^0.
|
|
76
|
-
"@cosyte/vitest-config": "^0.
|
|
71
|
+
"@cosyte/eslint-config": "^0.1.1",
|
|
72
|
+
"@cosyte/prettier-config": "^0.1.1",
|
|
73
|
+
"@cosyte/test-utils": "^0.1.1",
|
|
74
|
+
"@cosyte/tsconfig": "^0.1.1",
|
|
75
|
+
"@cosyte/tsup-config": "^0.1.1",
|
|
76
|
+
"@cosyte/vitest-config": "^0.1.1",
|
|
77
77
|
"@types/node": "^22.0.0",
|
|
78
78
|
"@vitest/coverage-v8": "4.1.4",
|
|
79
79
|
"eslint": "10.5.0",
|