@cosyte/transform 0.0.8 → 0.1.0
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/CHANGELOG.md +200 -0
- package/README.md +195 -20
- package/dist/index.cjs +2563 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1684 -79
- package/dist/index.d.ts +1684 -79
- package/dist/index.mjs +2499 -38
- package/dist/index.mjs.map +1 -1
- package/package.json +46 -28
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { FhirComplex } from '@cosyte/fhir';
|
|
2
|
-
import { HD, TS, CX, CWE, XPN, XAD, NM, Hl7Message } from '@cosyte/hl7';
|
|
1
|
+
import { FhirComplex, FhirNode } from '@cosyte/fhir';
|
|
2
|
+
import { HD, TS, CX, CWE, XPN, XAD, NM, Hl7Message, Segment, Field, BuildMessageInit } from '@cosyte/hl7';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* The stable code registries for the `@cosyte/transform` diagnostic channel.
|
|
@@ -17,6 +17,16 @@ import { HD, TS, CX, CWE, XPN, XAD, NM, Hl7Message } from '@cosyte/hl7';
|
|
|
17
17
|
* entry points. **Nothing in this library throws one today**: the datatype converters and
|
|
18
18
|
* `toFhir` alike fail safe to a value-free issue instead.
|
|
19
19
|
*
|
|
20
|
+
* **The line between the two registries is structural, and it decides where a new code goes.** A
|
|
21
|
+
* `FatalCode` is the type carried by a *thrown* error; a `TransformIssue.code` is typed `IssueCode`
|
|
22
|
+
* and {@link ISSUE_REGISTRY} is exhaustive over {@link ISSUE_CODES} alone. So a condition that is
|
|
23
|
+
* *returned* on the non-throwing `{ value, issues }` channel is an {@link ISSUE_CODES} entry by
|
|
24
|
+
* construction, however severe it is: the missing-trigger refusal on the reverse
|
|
25
|
+
* (FHIR to v2) entry points is returned, never thrown, so it is
|
|
26
|
+
* {@link ISSUE_CODES.TRANSFORM_MISSING_TRIGGER} rather than a fatal code. Adding it to
|
|
27
|
+
* {@link FATAL_CODES} instead would have required widening `TransformIssue.code` to
|
|
28
|
+
* `IssueCode | FatalCode` and blurring exactly that split.
|
|
29
|
+
*
|
|
20
30
|
* @packageDocumentation
|
|
21
31
|
*/
|
|
22
32
|
/**
|
|
@@ -80,6 +90,62 @@ declare const ISSUE_CODES: {
|
|
|
80
90
|
* emitted with a `data-absent-reason` extension (value `unknown`) rather than fabricated, e.g.
|
|
81
91
|
* `MessageHeader.source.endpoint` from an MSH-3 application namespace that is not a URL. */
|
|
82
92
|
readonly TRANSFORM_REQUIRED_ELEMENT_UNKNOWN: "TRANSFORM_REQUIRED_ELEMENT_UNKNOWN";
|
|
93
|
+
/** A reverse (FHIR to v2) conversion was called without the explicit message trigger it requires.
|
|
94
|
+
* No FHIR resource carries a v2 trigger, so it is never inferred from resource content and never
|
|
95
|
+
* defaulted: no message is built and no builder is called. */
|
|
96
|
+
readonly TRANSFORM_MISSING_TRIGGER: "TRANSFORM_MISSING_TRIGGER";
|
|
97
|
+
/** A reverse (FHIR to v2) conversion was handed a resource outside the narrow set of shapes it
|
|
98
|
+
* supports. No v2 message is emitted and no segment layout is guessed. */
|
|
99
|
+
readonly TRANSFORM_UNSUPPORTED_RESOURCE: "TRANSFORM_UNSUPPORTED_RESOURCE";
|
|
100
|
+
/** A resource handed to a reverse (FHIR to v2) conversion is not structurally a FHIR resource of
|
|
101
|
+
* its expected type (not an object, no `resourceType`, or an element carrying the wrong node
|
|
102
|
+
* kind). Nothing is emitted and nothing is thrown. */
|
|
103
|
+
readonly TRANSFORM_RESOURCE_MALFORMED: "TRANSFORM_RESOURCE_MALFORMED";
|
|
104
|
+
/** A populated FHIR element has no v2 field in the narrow reverse map that covers this shape
|
|
105
|
+
* (the IG map defines no source for it, or its inverse is not implemented here). The element is
|
|
106
|
+
* surfaced and left out, never approximated into a neighbouring v2 field. */
|
|
107
|
+
readonly TRANSFORM_NO_V2_TARGET: "TRANSFORM_NO_V2_TARGET";
|
|
108
|
+
/** A FHIR value cannot be carried into its v2 target without altering it (a lexical form v2 has
|
|
109
|
+
* no representation for, or content that exceeds the target's component structure). The v2 field
|
|
110
|
+
* is left absent rather than truncated, rounded, or coerced. */
|
|
111
|
+
readonly TRANSFORM_VALUE_NOT_REPRESENTABLE: "TRANSFORM_VALUE_NOT_REPRESENTABLE";
|
|
112
|
+
/** A FHIR code has no usable inverse in the IG ConceptMap that governs its field: either several
|
|
113
|
+
* v2 source codes map onto it (so the inverse is ambiguous) or the map has no row for it at all.
|
|
114
|
+
* The v2 field is left absent, never resolved to one arbitrary member of the ambiguous set. */
|
|
115
|
+
readonly TRANSFORM_CODE_NOT_INVERTIBLE: "TRANSFORM_CODE_NOT_INVERTIBLE";
|
|
116
|
+
/** A `Coding.system` is not a system this library can name with a v2 coding-system mnemonic, so
|
|
117
|
+
* the coding cannot be written into a v2 coded field. It is flagged rather than emitted with no
|
|
118
|
+
* table context or with a code from an unrelated table. */
|
|
119
|
+
readonly TRANSFORM_CODE_SYSTEM_NOT_V2: "TRANSFORM_CODE_SYSTEM_NOT_V2";
|
|
120
|
+
/** A v2 field the segment's own attribute table marks **required** (usage `R`) is absent from an
|
|
121
|
+
* emitted segment, because the FHIR resource carried no source this reverse map could ground it
|
|
122
|
+
* from, or the source it carried had no faithful v2 form. The field is left absent per v2
|
|
123
|
+
* optionality rules and **declared here**: a placeholder is never written to satisfy v2 structure,
|
|
124
|
+
* and the receiver is never left to discover the gap. Distinct from
|
|
125
|
+
* {@link ISSUE_CODES.TRANSFORM_REQUIRED_ELEMENT_UNKNOWN}, which is the forward direction's
|
|
126
|
+
* FHIR-required element. */
|
|
127
|
+
readonly TRANSFORM_V2_REQUIRED_FIELD_ABSENT: "TRANSFORM_V2_REQUIRED_FIELD_ABSENT";
|
|
128
|
+
/** A reverse (FHIR to v2) conversion produced **no message at all**: nothing in the resource
|
|
129
|
+
* grounded a single field of the target segment, and an empty segment is never emitted. Raised so
|
|
130
|
+
* that an empty-handed conversion is never indistinguishable from a successful one, and separate
|
|
131
|
+
* from the refusals that name their own cause (an absent trigger, an unsupported resource type, a
|
|
132
|
+
* structurally malformed resource). */
|
|
133
|
+
readonly TRANSFORM_NO_V2_MESSAGE_EMITTED: "TRANSFORM_NO_V2_MESSAGE_EMITTED";
|
|
134
|
+
/** A segment the message carried did **not** reach any resource in the returned bundle, and the IG
|
|
135
|
+
* *does* publish a segment map for its name: a **library gap**, fixable here. Raised once per
|
|
136
|
+
* occurrence, located `NAME[k]` with `k` counted 1-based among the occurrences of that name
|
|
137
|
+
* (`DG1[1]`, `DG1[2]`), so a consumer can tell how much of the message the bundle represents
|
|
138
|
+
* without diffing the two by hand. The occurrence may have been read and refused rather than never
|
|
139
|
+
* read: being counted, or already named by another issue, is not the same as reaching a resource. */
|
|
140
|
+
readonly TRANSFORM_SEGMENT_NOT_EMITTED: "TRANSFORM_SEGMENT_NOT_EMITTED";
|
|
141
|
+
/** A segment the message carried did **not** reach any resource in the returned bundle, and its
|
|
142
|
+
* name is **not** one the IG publishes a segment map for: a **standard gap**, not fixable here.
|
|
143
|
+
* Custom Z-segments and `RXE` land on this code, and so does a name this library could not
|
|
144
|
+
* classify at all, because a damaged segment identifier is never re-derived from the line: an
|
|
145
|
+
* `AL1` whose identifier arrived as `AL11` reads as unclassifiable, not as an IG omission. Such an
|
|
146
|
+
* occurrence is located `[#n]` by its 1-based position among the segments of the message, with no
|
|
147
|
+
* name part at all. */
|
|
148
|
+
readonly TRANSFORM_SEGMENT_NO_IG_MAP: "TRANSFORM_SEGMENT_NO_IG_MAP";
|
|
83
149
|
};
|
|
84
150
|
/** A value from {@link ISSUE_CODES}: the type consumers narrow `issue.code` against. */
|
|
85
151
|
type IssueCode = (typeof ISSUE_CODES)[keyof typeof ISSUE_CODES];
|
|
@@ -605,9 +671,13 @@ declare function toFhirQuantity(value: NM, units: CWE, ctx?: TransformContext):
|
|
|
605
671
|
* `toFhir`, the message-level entry point: assemble a parsed HL7 v2 message into a FHIR R4
|
|
606
672
|
* **message Bundle** (a `MessageHeader` first, then the focal resources), grounded on the IG message
|
|
607
673
|
* and segment maps. The **ADT** family becomes **Patient + Encounter** (+ `RelatedPerson`
|
|
608
|
-
* from NK1
|
|
609
|
-
*
|
|
610
|
-
* `
|
|
674
|
+
* from NK1, + one `AllergyIntolerance` per AL1, + one `Condition` per DG1, one `Procedure` per PR1
|
|
675
|
+
* and one `Coverage` per IN1); the **ORU^R01** results graph becomes
|
|
676
|
+
* `DiagnosticReport` + `Observation` (+ one `Specimen` per SPM of the report's SPECIMEN group,
|
|
677
|
+
* referenced from the report that scopes it); the
|
|
678
|
+
* order-entry graph gives **ORM_O01 / OML_O21** ORC/OBR → `ServiceRequest` and **RXO** (+ RXR, + the
|
|
679
|
+
* order's **TQ1** schedule) → `MedicationRequest`; and the thin IG singles give **VXU_V04**
|
|
680
|
+
* RXA/RXR/ORC → `Immunization`,
|
|
611
681
|
* **SIU_S12** SCH/AIS/PID → `Appointment`, and **MDM_T02** TXA/OBX → `DocumentReference`.
|
|
612
682
|
*
|
|
613
683
|
* The fail-safe rule holds at the message level. Two message-level fail-safes join the datatype ones:
|
|
@@ -624,6 +694,12 @@ declare function toFhirQuantity(value: NM, units: CWE, ctx?: TransformContext):
|
|
|
624
694
|
* {@link ISSUE_CODES.TRANSFORM_RESOURCE_INVALID} rather than shipped as silently-invalid FHIR. When
|
|
625
695
|
* the Patient is withheld, the Encounter's `subject` and any RelatedPerson are dropped rather than
|
|
626
696
|
* left dangling: references always resolve within the bundle.
|
|
697
|
+
* - **Segment-level completeness.** Every segment occurrence that contributed nothing to a resource
|
|
698
|
+
* the returned bundle contains raises one value-free issue naming it
|
|
699
|
+
* ({@link SegmentReachLedger}), so silence over an omitted segment can no longer read as
|
|
700
|
+
* completeness. The diagnostic OBSERVES this assembly and never steers it: the occurrences are
|
|
701
|
+
* marked where a resource clears the gate above, the issues are appended after every issue the
|
|
702
|
+
* assembly raised, and no resource, value or existing issue changes because of it.
|
|
627
703
|
*
|
|
628
704
|
* @packageDocumentation
|
|
629
705
|
*/
|
|
@@ -765,6 +841,41 @@ declare const ENCOUNTER_CLASS_V3_MAP: Readonly<Record<string, {
|
|
|
765
841
|
* discharge sets `status = finished` per the PV1→Encounter map.
|
|
766
842
|
*/
|
|
767
843
|
declare const ENCOUNTER_STATUS_MAP: Readonly<Record<string, string>>;
|
|
844
|
+
/**
|
|
845
|
+
* The same Encounter carrying one `diagnosis` entry per Condition the bundle holds, per the ADT_A01
|
|
846
|
+
* message map's `Encounter[1].diagnosis.reference = Condition[1].id` row. Each entry's `condition`
|
|
847
|
+
* points at that Condition's `fullUrl`, so the link resolves inside the bundle.
|
|
848
|
+
*
|
|
849
|
+
* Applied after the diagnoses are built, because the Encounter joins the bundle before them and the
|
|
850
|
+
* produced nodes are immutable: this returns a new node rather than mutating the emitted one.
|
|
851
|
+
*
|
|
852
|
+
* @param encounter - The Encounter node already in the bundle.
|
|
853
|
+
* @param conditionFullUrls - The fullUrls of the Conditions emitted for the same message, in order.
|
|
854
|
+
* @example
|
|
855
|
+
* ```ts
|
|
856
|
+
* // withEncounterDiagnosis(encounter, ["urn:uuid:cond-1"]); // adds one diagnosis entry
|
|
857
|
+
* ```
|
|
858
|
+
*/
|
|
859
|
+
declare function withEncounterDiagnosis(encounter: FhirComplex, conditionFullUrls: readonly string[]): FhirComplex;
|
|
860
|
+
/**
|
|
861
|
+
* The Encounter that should replace the emitted one once the message's diagnoses exist: the same
|
|
862
|
+
* resource carrying the back-references, put through the conservative-emit gate first. When the
|
|
863
|
+
* gate refuses the linked draft the ORIGINAL Encounter is returned unchanged and the link is
|
|
864
|
+
* declared {@link ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED}, so a detail never costs the visit.
|
|
865
|
+
*
|
|
866
|
+
* The gate is a parameter rather than a call into the assembler, so what "the gate refuses this"
|
|
867
|
+
* means stays in one place and the refusal branch is reachable from a test without a hand-built
|
|
868
|
+
* invalid resource: the same shape the allergy emit path uses.
|
|
869
|
+
*
|
|
870
|
+
* @param encounter - The Encounter node already in the bundle.
|
|
871
|
+
* @param conditionFullUrls - The fullUrls of the Conditions emitted for the same message, in order.
|
|
872
|
+
* @param gate - The conservative-emit predicate: `true` when the linked Encounter may replace it.
|
|
873
|
+
* @example
|
|
874
|
+
* ```ts
|
|
875
|
+
* // emitEncounterDiagnosis(encounter, ["urn:uuid:cond-1"], () => true).value; // the linked one
|
|
876
|
+
* ```
|
|
877
|
+
*/
|
|
878
|
+
declare function emitEncounterDiagnosis(encounter: FhirComplex, conditionFullUrls: readonly string[], gate: (resource: FhirComplex) => boolean): ConvertResult<FhirComplex>;
|
|
768
879
|
|
|
769
880
|
/**
|
|
770
881
|
* OBX → FHIR `Observation`: the highest-clinical-stakes segment map,
|
|
@@ -783,13 +894,40 @@ declare const ENCOUNTER_STATUS_MAP: Readonly<Record<string, string>>;
|
|
|
783
894
|
* | OBX-8 Abnormal Flags | `Observation.interpretation` | {@link HL70078_INTERPRETATION_CODES} |
|
|
784
895
|
* | OBX-11 Result Status | `Observation.status` | {@link OBSERVATION_STATUS_MAP} (HL70085) |
|
|
785
896
|
* | OBX-14 Date/Time of Observation | `Observation.effectiveDateTime` | {@link toFhirDateTime} |
|
|
897
|
+
* | (OBSERVATION-group NTE) | `Observation.note` | {@link buildAnnotations} (`NTE[ServiceRequest]`) |
|
|
898
|
+
*
|
|
899
|
+
* **What OBX-2 selects, row by row** (`ConceptMap-segment-obx-to-observation.html`, the OBX-5 rows):
|
|
900
|
+
*
|
|
901
|
+
* | OBX-2 | FHIR target | datatype map |
|
|
902
|
+
* |---|---|---|
|
|
903
|
+
* | `NM` | `valueQuantity` | `NM[Quantity]` |
|
|
904
|
+
* | `SN` | `valueQuantity` (with a comparator) / `valueRange` / `valueRatio` | the row's own conditions |
|
|
905
|
+
* | `CWE`/`CE`/`CF`/`CNE`/`IS` | `valueCodeableConcept` | `CWE[CodeableConcept]` |
|
|
906
|
+
* | `DT`/`DTM`/`TS` | `valueDateTime` | `DTM[DateTime]` |
|
|
907
|
+
* | `ST`/`TX`/`FT` | `valueString` | (direct) |
|
|
908
|
+
* | `DR` | `valuePeriod` | `DR[Period]` |
|
|
909
|
+
* | `NR` | `valueRange` | `NR[Range]` |
|
|
910
|
+
* | `TM` | `valueTime` | (direct, an R4 `time`) |
|
|
911
|
+
* | `NA` | `valueSampledData` | `NA[SampledData]` |
|
|
912
|
+
* | `ED` **and** OBX-5.4 = `Base64` | the {@link OBSERVATION_VALUE_ATTACHMENT_EXTENSION_URL} extension | `ED[Attachment]` |
|
|
913
|
+
* | `RP`, `ID`, anything else | `valueString` + flagged | (no settled target) |
|
|
786
914
|
*
|
|
787
915
|
* **The "never a confident wrong result" fail-safes:**
|
|
788
|
-
* - **OBX-2 drives `value[x]
|
|
789
|
-
*
|
|
790
|
-
*
|
|
791
|
-
*
|
|
792
|
-
*
|
|
916
|
+
* - **OBX-2 drives `value[x]`**, per the table above, and every row of it degrades to the raw OBX-5
|
|
917
|
+
* text as `valueString` + {@link ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED} rather than emit a value
|
|
918
|
+
* the map cannot ground: a `DR` neither of whose bounds is a dateTime this library will emit, an
|
|
919
|
+
* `NR` neither of whose bounds is a faithful `decimal`, a `TM` carrying a UTC offset (an R4 `time`
|
|
920
|
+
* admits none, and discarding one would move the instant), an `NA` carrying a magnitude `decimal`
|
|
921
|
+
* cannot hold unaltered, an `ED` whose OBX-5.4 is not `Base64` (both ED rows are conditioned on
|
|
922
|
+
* it), and `RP`, whose extension target the guide's own comment marks unsettled.
|
|
923
|
+
* - **The published NA row spells its attribute `Observation.valueSampedData`.** That is a typo in
|
|
924
|
+
* the guide; the R4 element is `valueSampledData` and that is what is emitted.
|
|
925
|
+
* - **A SampledData asserts no origin and no period.** R4 makes both `1..1` and the NA map has no
|
|
926
|
+
* source row for either, so they ship value-absent with a `data-absent-reason` and
|
|
927
|
+
* {@link ISSUE_CODES.TRANSFORM_REQUIRED_ELEMENT_UNKNOWN}: an `origin` of 0 or a `period` of 1
|
|
928
|
+
* would be a magnitude this message never sent, on a waveform.
|
|
929
|
+
* - **An ED payload is carried, never read.** OBX-5.5 reaches `Attachment.data` byte-for-byte: no
|
|
930
|
+
* decode, no re-encode, no normalization, no validation, no truncation.
|
|
793
931
|
* - **A corrected/cancelled result never emits as `final`.** OBX-11 `C`→`corrected`, `X`→`cancelled`,
|
|
794
932
|
* `D`/`W`→`entered-in-error` (HL70085). A status code with **no** HL70085 target leaves
|
|
795
933
|
* `Observation.status` absent (flagged {@link ISSUE_CODES.TRANSFORM_CODE_UNMAPPED}); the required-`status`
|
|
@@ -805,6 +943,16 @@ declare const ENCOUNTER_STATUS_MAP: Readonly<Record<string, string>>;
|
|
|
805
943
|
* @packageDocumentation
|
|
806
944
|
*/
|
|
807
945
|
|
|
946
|
+
/**
|
|
947
|
+
* The v3 ObservationInterpretation canonical system (FHIR `Observation.interpretation` binding).
|
|
948
|
+
*
|
|
949
|
+
* @example
|
|
950
|
+
* ```ts
|
|
951
|
+
* import { V3_OBSERVATION_INTERPRETATION_SYSTEM } from "@cosyte/transform";
|
|
952
|
+
* V3_OBSERVATION_INTERPRETATION_SYSTEM.endsWith("v3-ObservationInterpretation"); // => true
|
|
953
|
+
* ```
|
|
954
|
+
*/
|
|
955
|
+
declare const V3_OBSERVATION_INTERPRETATION_SYSTEM = "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation";
|
|
808
956
|
/**
|
|
809
957
|
* HL7 v2 Table 0078 (Abnormal Flags) → FHIR v3 ObservationInterpretation (`Observation.interpretation`),
|
|
810
958
|
* per the IG **Table HL70078 to v3 ObservationInterpretation** ConceptMap. Every mapped row is
|
|
@@ -823,6 +971,54 @@ declare const HL70078_INTERPRETATION_CODES: ReadonlySet<string>;
|
|
|
823
971
|
* **`C`→`corrected` and `X`→`cancelled` guarantee a corrected/cancelled result never emits as `final`.**
|
|
824
972
|
*/
|
|
825
973
|
declare const OBSERVATION_STATUS_MAP: Readonly<Record<string, string>>;
|
|
974
|
+
/**
|
|
975
|
+
* The extension URL the OBX map **fixes** on the `IF OBX-2 EQUALS "ED" AND IF OBX-5.4 EQUALS
|
|
976
|
+
* "Base64"` rows, transcribed from the map's own `Observation.extension.url` assignment. R4 has no
|
|
977
|
+
* `Observation.valueAttachment`, so the guide carries the R5 element as this named extension.
|
|
978
|
+
*
|
|
979
|
+
* @example
|
|
980
|
+
* ```ts
|
|
981
|
+
* import { OBSERVATION_VALUE_ATTACHMENT_EXTENSION_URL } from "@cosyte/transform";
|
|
982
|
+
* OBSERVATION_VALUE_ATTACHMENT_EXTENSION_URL.endsWith("extension-Observation.valueAttachment"); // true
|
|
983
|
+
* ```
|
|
984
|
+
*/
|
|
985
|
+
declare const OBSERVATION_VALUE_ATTACHMENT_EXTENSION_URL = "https://hl7.org/fhir/5.0/StructureDefinition/extension-Observation.valueAttachment";
|
|
986
|
+
/**
|
|
987
|
+
* The OBX-5.4 encoding the OBX map conditions **both** of its `ED` rows on. Any other encoding
|
|
988
|
+
* (`A`, `Hex`, `Ascii`, a local token) leaves those rows unapplied, so no attachment is built.
|
|
989
|
+
*
|
|
990
|
+
* @example
|
|
991
|
+
* ```ts
|
|
992
|
+
* import { ED_BASE64_ENCODING } from "@cosyte/transform";
|
|
993
|
+
* ED_BASE64_ENCODING; // "Base64"
|
|
994
|
+
* ```
|
|
995
|
+
*/
|
|
996
|
+
declare const ED_BASE64_ENCODING = "Base64";
|
|
997
|
+
/**
|
|
998
|
+
* The `SampledData.data` token the Implementation Considerations chapter's own worked example
|
|
999
|
+
* directs for a data point a repetition did not carry (`"set .dimensions to 4 and use E for the
|
|
1000
|
+
* data points not present"`). It is the FHIR-defined "error" token, and it is written **only** for
|
|
1001
|
+
* an absent position, never for a value that arrived and could not be converted.
|
|
1002
|
+
*
|
|
1003
|
+
* @example
|
|
1004
|
+
* ```ts
|
|
1005
|
+
* import { SAMPLED_DATA_ABSENT_POINT } from "@cosyte/transform";
|
|
1006
|
+
* SAMPLED_DATA_ABSENT_POINT; // "E"
|
|
1007
|
+
* ```
|
|
1008
|
+
*/
|
|
1009
|
+
declare const SAMPLED_DATA_ABSENT_POINT = "E";
|
|
1010
|
+
/**
|
|
1011
|
+
* The `data-absent-reason` code carried by `SampledData.origin` and `SampledData.period`, the two
|
|
1012
|
+
* R4-required elements of that datatype that **no row of the IG's NA map, and no line of the
|
|
1013
|
+
* Implementation Considerations chapter it points at, supplies a source for**.
|
|
1014
|
+
*
|
|
1015
|
+
* @example
|
|
1016
|
+
* ```ts
|
|
1017
|
+
* import { SAMPLED_DATA_UNGROUNDED } from "@cosyte/transform";
|
|
1018
|
+
* SAMPLED_DATA_UNGROUNDED; // "unknown"
|
|
1019
|
+
* ```
|
|
1020
|
+
*/
|
|
1021
|
+
declare const SAMPLED_DATA_UNGROUNDED = "unknown";
|
|
826
1022
|
|
|
827
1023
|
/**
|
|
828
1024
|
* OBR → FHIR `DiagnosticReport`, the ORU results-report anchor, grounded firsthand
|
|
@@ -841,6 +1037,7 @@ declare const OBSERVATION_STATUS_MAP: Readonly<Record<string, string>>;
|
|
|
841
1037
|
* | OBR-22 Results Rpt/Status Chng Date/Time | `DiagnosticReport.issued` (an `instant`) | {@link toFhirDateTime} |
|
|
842
1038
|
* | OBR-24 Diagnostic Serv Sect ID | `DiagnosticReport.category` | v2-0074 coding |
|
|
843
1039
|
* | OBR-25 Result Status | `DiagnosticReport.status` | {@link DIAGNOSTIC_REPORT_STATUS_MAP} (HL70123) |
|
|
1040
|
+
* | (SPM children) | `DiagnosticReport.specimen` | reference wiring from the assembler |
|
|
844
1041
|
* | (OBX children) | `DiagnosticReport.result` | reference wiring from the assembler |
|
|
845
1042
|
*
|
|
846
1043
|
* **Fail-safes.** `DiagnosticReport.status` is required (R4 1..1) and the IG itself notes an unvalued
|
|
@@ -849,8 +1046,11 @@ declare const OBSERVATION_STATUS_MAP: Readonly<Record<string, string>>;
|
|
|
849
1046
|
* required-`status` emit gate withholds the report rather than emitting an invalid one or guessing
|
|
850
1047
|
* `final`. `OBR-25 = C`→`corrected` / `X`→`cancelled` are modelled exactly. OBR-22 becomes `issued`
|
|
851
1048
|
* only when it is a fully-zoned instant; a date-only/naked OBR-22 is dropped + flagged (never a
|
|
852
|
-
* fabricated UTC), mirroring `Bundle.timestamp`.
|
|
853
|
-
*
|
|
1049
|
+
* fabricated UTC), mirroring `Bundle.timestamp`. `.specimen` carries only the Specimens the
|
|
1050
|
+
* assembler has already emitted into this bundle, so the reference never resolves to nothing.
|
|
1051
|
+
* Deferred and flagged elsewhere, not silently mapped: OBR-32/34/35 performers (need
|
|
1052
|
+
* PractitionerRole resources), the ORU map's own OBR-to-Specimen row (4.2.2, distinct from the SPM
|
|
1053
|
+
* one), and `basedOn` ServiceRequest.
|
|
854
1054
|
*
|
|
855
1055
|
* @packageDocumentation
|
|
856
1056
|
*/
|
|
@@ -867,6 +1067,125 @@ declare const OBSERVATION_STATUS_MAP: Readonly<Record<string, string>>;
|
|
|
867
1067
|
*/
|
|
868
1068
|
declare const DIAGNOSTIC_REPORT_STATUS_MAP: Readonly<Record<string, string>>;
|
|
869
1069
|
|
|
1070
|
+
/**
|
|
1071
|
+
* SPM → FHIR `Specimen`: the sample a result was taken from, carried into the bundle instead of
|
|
1072
|
+
* lost between the lab feed and the FHIR record. Grounded firsthand on the IG **Segment SPM to
|
|
1073
|
+
* Specimen** ConceptMap and the **ORU_R01 to Bundle** message map (`hl7.fhir.uv.v2mappings`, STU1;
|
|
1074
|
+
* `ConceptMap-segment-spm-to-specimen.html`, `ConceptMap-message-oru-r01-to-bundle.html`), whose
|
|
1075
|
+
* row 4.2.7.1 creates one Specimen per SPM of an ORDER_OBSERVATION's SPECIMEN group and wires
|
|
1076
|
+
* `DiagnosticReport[1].specimen.reference` to it.
|
|
1077
|
+
*
|
|
1078
|
+
* | SPM field | FHIR target | via |
|
|
1079
|
+
* |---|---|---|
|
|
1080
|
+
* | SPM-2 Specimen ID (EIP) | `identifier` (placer, then filler) | EI.1 → `Identifier.value` |
|
|
1081
|
+
* | SPM-4 Specimen Type (CWE) | `Specimen.type` | {@link toFhirCodeableConcept} |
|
|
1082
|
+
* | SPM-6 Specimen Additives (CWE) | `container.additiveCodeableConcept` | {@link toFhirCodeableConcept} |
|
|
1083
|
+
* | SPM-7 Collection Method (CWE) | `collection.method` | {@link toFhirCodeableConcept} |
|
|
1084
|
+
* | SPM-8 Source Site (CWE) | `collection.bodySite` | {@link toFhirCodeableConcept} |
|
|
1085
|
+
* | SPM-14 Specimen Description (ST) | `note` (`Annotation.text`) | one annotation per repetition |
|
|
1086
|
+
* | SPM-17 Collection Date/Time (DR) | `collection.collectedPeriod` / `collectedDateTime` | `IF SPM-17.2 VALUED` |
|
|
1087
|
+
* | SPM-18 Received Date/Time (DTM) | `Specimen.receivedTime` | {@link toFhirDateTime} |
|
|
1088
|
+
* | SPM-24 Specimen Condition (CWE) | `Specimen.condition` | {@link toFhirCodeableConcept} |
|
|
1089
|
+
* | SPM-27 Container Type (CWE) | `container.type` | {@link toFhirCodeableConcept} |
|
|
1090
|
+
* | SPM-30 Accession ID (CX) | `Specimen.accessionIdentifier` | {@link toFhirIdentifier} |
|
|
1091
|
+
* | SPM-31 Other Specimen ID (CX) | `identifier` | {@link toFhirIdentifier} |
|
|
1092
|
+
* | SPM-32 Shipment ID (EI) | `identifier` (type `SHIP`, v2-0203) | the row's own fixed assignment |
|
|
1093
|
+
* | (message-map wiring) | referenced from `DiagnosticReport.specimen` | the assembler |
|
|
1094
|
+
*
|
|
1095
|
+
* **Fail-safes (never a confident wrong specimen).**
|
|
1096
|
+
* - **The status is never asserted.** `SPM-20` is the only row that targets `Specimen.status`, and
|
|
1097
|
+
* it reaches it through a **Table 0136 to specimen-status** value ConceptMap this library does not
|
|
1098
|
+
* carry. R4 binds that element `required`, and every member of the binding is a positive claim
|
|
1099
|
+
* about a sample a clinician may act on, so the element stays absent and the row is declared
|
|
1100
|
+
* {@link ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED}. Unlike `Coverage.status`, `Specimen.status` is
|
|
1101
|
+
* `0..1`, so absence is the spec-clean shape and no `data-absent-reason` is needed to hold a slot.
|
|
1102
|
+
* - **A specimen never points at a specimen that is not there.** `SPM-3 Specimen Parent IDs` targets
|
|
1103
|
+
* `Specimen.parent`, a reference to another Specimen resource; none is built for a parent, so the
|
|
1104
|
+
* row is declared rather than emitted as a link that resolves to nothing.
|
|
1105
|
+
* - **A collection amount is not rebuilt from parts.** `SPM-12` reaches `collection.quantity`
|
|
1106
|
+
* through the `CQ[Quantity]` datatype map, which this library does not carry; assembling one from
|
|
1107
|
+
* CQ.1 and CQ.2 by hand would be a unit gate written twice, so the row is declared instead.
|
|
1108
|
+
* - **An unresolvable coding system costs the system URI, never the code**, and every date/time goes
|
|
1109
|
+
* through the one timezone-safe converter: the datatype converters own both rules and this module
|
|
1110
|
+
* only routes fields to them.
|
|
1111
|
+
*
|
|
1112
|
+
* @packageDocumentation
|
|
1113
|
+
*/
|
|
1114
|
+
|
|
1115
|
+
/**
|
|
1116
|
+
* The Table 0203 identifier-type code the SPM-32 row **assigns** to the shipment identifier
|
|
1117
|
+
* (`Specimen.identifier[3].type.coding.code = "SHIP"`, with the v2-0203 system beside it).
|
|
1118
|
+
*
|
|
1119
|
+
* @example
|
|
1120
|
+
* ```ts
|
|
1121
|
+
* import { SPM_SHIPMENT_IDENTIFIER_TYPE } from "@cosyte/transform";
|
|
1122
|
+
* SPM_SHIPMENT_IDENTIFIER_TYPE; // "SHIP"
|
|
1123
|
+
* ```
|
|
1124
|
+
*/
|
|
1125
|
+
declare const SPM_SHIPMENT_IDENTIFIER_TYPE = "SHIP";
|
|
1126
|
+
/**
|
|
1127
|
+
* Build a FHIR `Specimen` resource node from one parsed HL7 v2 SPM segment. Returns
|
|
1128
|
+
* `{ value: undefined }` when the segment grounds no element of the map at all, because an empty
|
|
1129
|
+
* `Specimen` in a bundle is a claim that a sample was described when nothing about it was sent.
|
|
1130
|
+
*
|
|
1131
|
+
* @param spm - The SPM `@cosyte/hl7` `Segment`.
|
|
1132
|
+
* @param subjectFullUrl - The `urn:uuid:` fullUrl of the bundle's Patient → `Specimen.subject`.
|
|
1133
|
+
* @param ctx - The transform context (naming-system registry + timezone policy).
|
|
1134
|
+
* @example
|
|
1135
|
+
* ```ts
|
|
1136
|
+
* import { parseHL7 } from "@cosyte/hl7";
|
|
1137
|
+
* // const spm = parseHL7(raw).segments("SPM")[0];
|
|
1138
|
+
* // const { value } = buildSpecimen(spm!, "urn:uuid:pat", {});
|
|
1139
|
+
* ```
|
|
1140
|
+
*/
|
|
1141
|
+
declare function buildSpecimen(spm: Segment, subjectFullUrl: string | undefined, ctx: TransformContext): ConvertResult<FhirComplex>;
|
|
1142
|
+
|
|
1143
|
+
/**
|
|
1144
|
+
* NTE → a FHIR `Annotation`, grounded firsthand on the IG **Segment NTE to ServiceRequest**
|
|
1145
|
+
* ConceptMap (`hl7.fhir.uv.v2mappings`, STU1; `ConceptMap-segment-nte-to-servicerequest.html`),
|
|
1146
|
+
* which is the map the ORU_R01 message map files its `Observation.note` row against.
|
|
1147
|
+
*
|
|
1148
|
+
* | NTE field | FHIR target | via |
|
|
1149
|
+
* |---|---|---|
|
|
1150
|
+
* | NTE-3 Comment (FT, `0..-1`) | `Annotation.text` (`markdown`) | one annotation per repetition |
|
|
1151
|
+
* | NTE-6 Entered Date/Time (DTM) | `Annotation.time` | {@link toFhirDateTime} |
|
|
1152
|
+
*
|
|
1153
|
+
* `NTE-1`, `NTE-2`, `NTE-7`, `NTE-8` and `NTE-9` carry no target in the map at all. Two rows do
|
|
1154
|
+
* carry one and are deliberately **declared rather than built**: `NTE-5 Entered By` targets
|
|
1155
|
+
* `note.authorReference(Practitioner)`, and this library builds no Practitioner, so an author would
|
|
1156
|
+
* be a reference resolving to nothing; and `NTE-4 Comment Type` targets a **proposed** extension
|
|
1157
|
+
* the guide has not named yet (its own cell reads `extension??-noteType`), so there is no canonical
|
|
1158
|
+
* URL to write and one is never invented.
|
|
1159
|
+
*
|
|
1160
|
+
* **A note is text the sender wrote, so it is read as text.** `NTE-3` is `FT`: a v2 primitive with
|
|
1161
|
+
* no component structure, where a raw `^` or `&` is content. Each repetition is rendered through
|
|
1162
|
+
* the parser's own display projection, which resolves the v2 escape sequences (`\T\` to `&`,
|
|
1163
|
+
* `\.br\` to a line break) and preserves verbatim any sequence it cannot render. The repetitions
|
|
1164
|
+
* are **not** joined: a separator between them would be a character the wire never carried, and
|
|
1165
|
+
* `Observation.note` is `0..*` precisely so each can stand on its own.
|
|
1166
|
+
*
|
|
1167
|
+
* @packageDocumentation
|
|
1168
|
+
*/
|
|
1169
|
+
|
|
1170
|
+
/**
|
|
1171
|
+
* Every FHIR `Annotation` a run of `NTE` segments contributes, in message order: one per valued
|
|
1172
|
+
* `NTE-3` repetition, each carrying that repetition's rendered text and the segment's `NTE-6` time.
|
|
1173
|
+
*
|
|
1174
|
+
* Returns `undefined` when the segments carry no comment text at all, so a caller adds no empty
|
|
1175
|
+
* `note` element. The diagnostics raised are value-free: no byte of a note ever reaches one.
|
|
1176
|
+
*
|
|
1177
|
+
* @param notes - The `NTE` segments to read, in message order.
|
|
1178
|
+
* @param ctx - The transform context (naming-system registry + timezone policy).
|
|
1179
|
+
* @param issues - The issue sink for the deferred-row and timestamp diagnostics.
|
|
1180
|
+
* @example
|
|
1181
|
+
* ```ts
|
|
1182
|
+
* import { parseHL7 } from "@cosyte/hl7";
|
|
1183
|
+
* // const nte = parseHL7(raw).segments("NTE");
|
|
1184
|
+
* // buildAnnotations(nte, {}, []); // a FHIR list of Annotation nodes, or undefined
|
|
1185
|
+
* ```
|
|
1186
|
+
*/
|
|
1187
|
+
declare function buildAnnotations(notes: readonly Segment[], ctx: TransformContext, issues: TransformIssue[]): FhirNode | undefined;
|
|
1188
|
+
|
|
870
1189
|
/**
|
|
871
1190
|
* ORC + OBR → FHIR `ServiceRequest`: the order-entry request, grounded firsthand
|
|
872
1191
|
* on the IG **Segment ORC to ServiceRequest** and **Segment OBR to ServiceRequest** ConceptMaps and
|
|
@@ -884,8 +1203,10 @@ declare const DIAGNOSTIC_REPORT_STATUS_MAP: Readonly<Record<string, string>>;
|
|
|
884
1203
|
* | ORC-9 Date/Time of Order Event | `authoredOn` | {@link toFhirDateTime}, *IF ORC-1 = `NW`* |
|
|
885
1204
|
* | OBR-4 Universal Service Identifier (CWE) | `ServiceRequest.code` | {@link toFhirCodeableConcept} |
|
|
886
1205
|
* | OBR-5 Priority (v2-0485) | `ServiceRequest.priority` | {@link SERVICE_REQUEST_PRIORITY_MAP} (HL70485) |
|
|
887
|
-
* | OBR-6 Requested Date/Time | `occurrenceDateTime` | {@link toFhirDateTime} |
|
|
1206
|
+
* | OBR-6 Requested Date/Time | `occurrenceDateTime` | {@link toFhirDateTime}, *IF no TQ1 timing* |
|
|
888
1207
|
* | OBR-31 Reason for Study (CWE) | `reasonCode` | {@link toFhirCodeableConcept} |
|
|
1208
|
+
* | TQ1-3 Repeat Pattern (RPT) | `occurrenceTiming` | {@link readTq1} (RPT[Timing], all-or-nothing) |
|
|
1209
|
+
* | TQ1-7 / TQ1-8 Start / End date-time | `occurrenceTiming.repeat.boundsPeriod.start`/`.end` | {@link readTq1} |
|
|
889
1210
|
* | (order message context) | `intent` = `order` | see below |
|
|
890
1211
|
* | (message-map wiring) | `subject` / `encounter` | the bundle's Patient / Encounter |
|
|
891
1212
|
*
|
|
@@ -906,6 +1227,13 @@ declare const DIAGNOSTIC_REPORT_STATUS_MAP: Readonly<Record<string, string>>;
|
|
|
906
1227
|
* OBR-11-conditioned intent refinement is deferred.
|
|
907
1228
|
* - **`subject` (required 1..1).** Wired to the bundle's Patient; a request with no resolvable Patient
|
|
908
1229
|
* is withheld rather than emitted with a dangling/absent subject.
|
|
1230
|
+
* - **`occurrence[x]` is a choice, so exactly one member is ever emitted.** OBR-6 grounds an
|
|
1231
|
+
* `occurrenceDateTime` and a TQ1 grounds an `occurrenceTiming`; a group that would yield both
|
|
1232
|
+
* emits the **timing** (the richer statement, and the one the sender wrote a whole segment for)
|
|
1233
|
+
* and flags the dropped OBR-6. Emitting both would be structurally invalid FHIR, and silently
|
|
1234
|
+
* preferring the single instant would throw away the schedule. The timing itself is
|
|
1235
|
+
* all-or-nothing: see {@link readTq1}, whose every refusal leaves `occurrenceTiming` absent and
|
|
1236
|
+
* therefore hands the choice back to OBR-6 with no issue raised about it.
|
|
909
1237
|
*
|
|
910
1238
|
* - **`priority` (0..1), value-translated.** OBR-5 → `ServiceRequest.priority` via the IG
|
|
911
1239
|
* **Table HL70485 to Request Priority** ConceptMap ({@link SERVICE_REQUEST_PRIORITY_MAP}): only
|
|
@@ -945,6 +1273,290 @@ declare const REQUEST_STATUS_MAP: Readonly<Record<string, string>>;
|
|
|
945
1273
|
*/
|
|
946
1274
|
declare const SERVICE_REQUEST_PRIORITY_MAP: Readonly<Record<string, string>>;
|
|
947
1275
|
|
|
1276
|
+
/**
|
|
1277
|
+
* The message-level completeness diagnostic: which segment occurrences a message carried, which of
|
|
1278
|
+
* them contributed to a resource the returned bundle actually contains, and one value-free issue for
|
|
1279
|
+
* each one that did not.
|
|
1280
|
+
*
|
|
1281
|
+
* The library already refuses to produce a confident wrong FHIR value at the datatype and resource
|
|
1282
|
+
* levels. This is the same rule one level up. A message can carry an `IAM`, a `DG1` or an `IN1` that
|
|
1283
|
+
* nothing here reads, and until now the consumer got a bundle with nothing in it about the allergy
|
|
1284
|
+
* and an issues list with nothing in it about the omission: silence read as completeness, which is a
|
|
1285
|
+
* claim this library cannot make. After this, a consumer reads the issues list and learns which
|
|
1286
|
+
* segments did not reach the bundle, and whether each one is a **library gap**
|
|
1287
|
+
* ({@link ISSUE_CODES.TRANSFORM_SEGMENT_NOT_EMITTED}, the IG publishes a map and this library has
|
|
1288
|
+
* not built it) or a **standard gap** ({@link ISSUE_CODES.TRANSFORM_SEGMENT_NO_IG_MAP}, the IG
|
|
1289
|
+
* publishes no map, or the name could not be classified at all).
|
|
1290
|
+
*
|
|
1291
|
+
* Four rules carry the safety of this, and each one is enforced here rather than assumed upstream:
|
|
1292
|
+
*
|
|
1293
|
+
* - **Contribution, never inspection.** An occurrence "reached" a resource only if the assembly took
|
|
1294
|
+
* at least one of its values, or its identity, into a resource that is present in the returned
|
|
1295
|
+
* bundle. An occurrence that was counted, walked past, or already named by another issue did not
|
|
1296
|
+
* reach anything: the `RXE` and the orphan `OBX` this library counts and refuses are exactly the
|
|
1297
|
+
* omissions a consumer most needs told, so a "the walker touched it" reading would silence the
|
|
1298
|
+
* two cases the diagnostic exists for.
|
|
1299
|
+
* - **A name is rendered only if it passes the segment-identifier shape**
|
|
1300
|
+
* ({@link SEGMENT_IDENTIFIER_SHAPE}), applied by this library to the value the parser published,
|
|
1301
|
+
* as a positive test the value must pass. Not by excluding known markers, not by importing a
|
|
1302
|
+
* constant from the parser package, not by anything that depends on which parser version a
|
|
1303
|
+
* consumer resolved: this library declares a peer range that admits any of them, so a rule keyed
|
|
1304
|
+
* on a marker literal fails OPEN the moment that literal changes, promoting an unvouched value
|
|
1305
|
+
* into a rendering. A shape test fails CLOSED: whatever it does not recognize is located
|
|
1306
|
+
* positionally, and no byte of it is reproduced.
|
|
1307
|
+
* - **A position that carries no segment is not an occurrence.** The parser materializes an empty
|
|
1308
|
+
* position for a blank line so that ordinals stay stable. It carries no name AND no field content,
|
|
1309
|
+
* nothing was sent there, and reporting it would be a false claim of clinical omission at a
|
|
1310
|
+
* position the sender left empty. It is excluded wherever it sits, and it still counts toward the
|
|
1311
|
+
* `[#n]` ordinal of later occurrences so those keep matching the wire. A line that kept its field
|
|
1312
|
+
* separator still carries content and IS reported: the exclusion is for the absence of a segment,
|
|
1313
|
+
* never for the absence of a name.
|
|
1314
|
+
* - **One issue per occurrence, numbered among all occurrences of its name.** Never collapsed,
|
|
1315
|
+
* capped or deduplicated, and `[k]` is the occurrence's position among all occurrences of that
|
|
1316
|
+
* name, never among the reported ones: a message with two `NK1` of which only the second is
|
|
1317
|
+
* reported yields `NK1[2]`.
|
|
1318
|
+
*
|
|
1319
|
+
* @packageDocumentation
|
|
1320
|
+
*/
|
|
1321
|
+
|
|
1322
|
+
/**
|
|
1323
|
+
* The HL7 v2 Chapter 2 segment-identifier shape: three characters, a leading letter, applied to the
|
|
1324
|
+
* name value ITSELF as a positive membership test that must pass before any name is rendered.
|
|
1325
|
+
*
|
|
1326
|
+
* Everything else is a malformed name without exception: the empty string, a withheld marker, a raw
|
|
1327
|
+
* pre-delimiter token, a bare numeric like `202`, a four-character `AL11`. A malformed occurrence has
|
|
1328
|
+
* no name in this library's vocabulary and is identified by position instead.
|
|
1329
|
+
*
|
|
1330
|
+
* @example
|
|
1331
|
+
* ```ts
|
|
1332
|
+
* import { SEGMENT_IDENTIFIER_SHAPE } from "@cosyte/transform";
|
|
1333
|
+
* SEGMENT_IDENTIFIER_SHAPE.test("ZAL"); // true
|
|
1334
|
+
* SEGMENT_IDENTIFIER_SHAPE.test("202"); // false
|
|
1335
|
+
* ```
|
|
1336
|
+
*/
|
|
1337
|
+
declare const SEGMENT_IDENTIFIER_SHAPE: RegExp;
|
|
1338
|
+
/**
|
|
1339
|
+
* Whether a segment name may be rendered: the shape test of {@link SEGMENT_IDENTIFIER_SHAPE} applied
|
|
1340
|
+
* to the value itself.
|
|
1341
|
+
*
|
|
1342
|
+
* Deliberately NOT an exclusion of known-bad literals. An exclusion test admits any value it has not
|
|
1343
|
+
* heard of, which is the wrong direction for a rule whose whole job is to keep message bytes out of
|
|
1344
|
+
* a diagnostic, and it would silently couple this library to one release of the parser package.
|
|
1345
|
+
*
|
|
1346
|
+
* @param name - The value the parser published as the occurrence's segment type.
|
|
1347
|
+
* @example
|
|
1348
|
+
* ```ts
|
|
1349
|
+
* import { isWellFormedSegmentName } from "@cosyte/transform";
|
|
1350
|
+
* isWellFormedSegmentName("ZAL"); // true
|
|
1351
|
+
* isWellFormedSegmentName("AL11"); // false
|
|
1352
|
+
* ```
|
|
1353
|
+
*/
|
|
1354
|
+
declare function isWellFormedSegmentName(name: string): boolean;
|
|
1355
|
+
/**
|
|
1356
|
+
* One segment occurrence of a parsed message: a segment as it appears in the message, in message
|
|
1357
|
+
* order, with everything needed to locate it in a value-free way and nothing else.
|
|
1358
|
+
*/
|
|
1359
|
+
interface SegmentOccurrence {
|
|
1360
|
+
/** The parsed segment this occurrence wraps. */
|
|
1361
|
+
readonly segment: Segment;
|
|
1362
|
+
/** 1-based position among **all** parsed positions, empty ones included, so it matches the wire. */
|
|
1363
|
+
readonly ordinal: number;
|
|
1364
|
+
/** The renderable segment name, present only when it passed the shape test. */
|
|
1365
|
+
readonly name: string | undefined;
|
|
1366
|
+
/** 1-based position among the occurrences of {@link SegmentOccurrence.name}, when there is one. */
|
|
1367
|
+
readonly nameIndex: number | undefined;
|
|
1368
|
+
}
|
|
1369
|
+
/**
|
|
1370
|
+
* Every segment occurrence of a parsed message, in message order.
|
|
1371
|
+
*
|
|
1372
|
+
* A position that carries neither a name nor any field content is the parser's ordinal placeholder
|
|
1373
|
+
* for a blank line: it is not a segment occurrence, it is left out, and it still advances the
|
|
1374
|
+
* ordinal of everything after it. A position that carries field content is an occurrence even when
|
|
1375
|
+
* its name is empty.
|
|
1376
|
+
*
|
|
1377
|
+
* @param msg - The parsed message.
|
|
1378
|
+
* @example
|
|
1379
|
+
* ```ts
|
|
1380
|
+
* import { parseHL7 } from "@cosyte/hl7";
|
|
1381
|
+
* import { enumerateSegmentOccurrences } from "@cosyte/transform";
|
|
1382
|
+
* // enumerateSegmentOccurrences(parseHL7(raw))[0]?.name; // "MSH"
|
|
1383
|
+
* ```
|
|
1384
|
+
*/
|
|
1385
|
+
declare function enumerateSegmentOccurrences(msg: Hl7Message): readonly SegmentOccurrence[];
|
|
1386
|
+
/**
|
|
1387
|
+
* The v2 location of an occurrence: `NAME[k]` for a name that passed the shape test, `[#n]` for one
|
|
1388
|
+
* that did not. Those are the only two shapes, and neither carries a field or component index.
|
|
1389
|
+
*
|
|
1390
|
+
* @param occurrence - The occurrence to locate.
|
|
1391
|
+
* @example
|
|
1392
|
+
* ```ts
|
|
1393
|
+
* import { segmentOccurrenceLocation } from "@cosyte/transform";
|
|
1394
|
+
* // segmentOccurrenceLocation(occ); // "DG1[2]" or "[#4]"
|
|
1395
|
+
* ```
|
|
1396
|
+
*/
|
|
1397
|
+
declare function segmentOccurrenceLocation(occurrence: SegmentOccurrence): string;
|
|
1398
|
+
/**
|
|
1399
|
+
* Tracks, across one message-level assembly, which segment occurrences CONTRIBUTED to a resource the
|
|
1400
|
+
* returned bundle contains, and turns the rest into one value-free issue each.
|
|
1401
|
+
*
|
|
1402
|
+
* The assembly marks an occurrence only where a resource has just cleared the emit gate and joined
|
|
1403
|
+
* the bundle, so "reached" means contributed-and-present by construction: an occurrence that was
|
|
1404
|
+
* read and refused, or that built a resource the gate withheld, is never marked and is therefore
|
|
1405
|
+
* reported.
|
|
1406
|
+
*
|
|
1407
|
+
* @example
|
|
1408
|
+
* ```ts
|
|
1409
|
+
* import { parseHL7 } from "@cosyte/hl7";
|
|
1410
|
+
* import { SegmentReachLedger } from "@cosyte/transform";
|
|
1411
|
+
* // const ledger = new SegmentReachLedger(parseHL7(raw));
|
|
1412
|
+
* // ledger.markFirstOfType("MSH");
|
|
1413
|
+
* // ledger.issues(); // one issue per occurrence that reached nothing
|
|
1414
|
+
* ```
|
|
1415
|
+
*/
|
|
1416
|
+
declare class SegmentReachLedger {
|
|
1417
|
+
private readonly occurrences;
|
|
1418
|
+
private readonly reached;
|
|
1419
|
+
/**
|
|
1420
|
+
* @param msg - The parsed message whose occurrences are being tracked.
|
|
1421
|
+
*/
|
|
1422
|
+
constructor(msg: Hl7Message);
|
|
1423
|
+
/**
|
|
1424
|
+
* Record that each given segment contributed to a resource now present in the bundle. `undefined`
|
|
1425
|
+
* entries are ignored, so a call site can pass an optional part of a group without branching.
|
|
1426
|
+
*/
|
|
1427
|
+
mark(...segments: readonly (Segment | undefined)[]): void;
|
|
1428
|
+
/**
|
|
1429
|
+
* Record that the FIRST occurrence of `type` contributed. The parser's derived views (`patient`,
|
|
1430
|
+
* `visit`, `meta`) are built from the first segment of their type, so this is how a resource built
|
|
1431
|
+
* from a view marks the occurrence it came from.
|
|
1432
|
+
*/
|
|
1433
|
+
markFirstOfType(type: string): void;
|
|
1434
|
+
/**
|
|
1435
|
+
* Record that the `index`-th (0-based) occurrence of `type` contributed: the counterpart of
|
|
1436
|
+
* {@link SegmentReachLedger.markFirstOfType} for the repeating views (`nextOfKin()`), whose
|
|
1437
|
+
* entries are in document order.
|
|
1438
|
+
*/
|
|
1439
|
+
markNthOfType(type: string, index: number): void;
|
|
1440
|
+
/** The occurrences that contributed to no resource present in the bundle, in message order. */
|
|
1441
|
+
reportable(): readonly SegmentOccurrence[];
|
|
1442
|
+
/**
|
|
1443
|
+
* One value-free issue per reportable occurrence, in message order: the library-gap code when the
|
|
1444
|
+
* IG publishes a segment map for the name, the standard-gap code otherwise, and the standard-gap
|
|
1445
|
+
* code for every name that could not be classified at all.
|
|
1446
|
+
*/
|
|
1447
|
+
issues(): readonly TransformIssue[];
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
/**
|
|
1451
|
+
* The set of HL7 v2 segment names the **HL7 Version 2 to FHIR** Implementation Guide publishes a
|
|
1452
|
+
* segment-to-resource map for, transcribed by hand from the guide's own Segment Maps index.
|
|
1453
|
+
*
|
|
1454
|
+
* This is the one fact the completeness diagnostic cannot derive from the message or from this
|
|
1455
|
+
* library's own code: whether a segment the transform did not carry into the Bundle is missing
|
|
1456
|
+
* because this library has not built the mapping yet, or because the standard publishes no mapping
|
|
1457
|
+
* to build. The two answers are different news for a consumer, so the set is recorded here with the
|
|
1458
|
+
* guide version and the retrieval date it was read at, and every entry is auditable against the
|
|
1459
|
+
* published index a reader can open themselves.
|
|
1460
|
+
*
|
|
1461
|
+
* **The set is the guide's, not this library's.** It says nothing about which of these segments the
|
|
1462
|
+
* transform actually reads: `IAM` is on it and is not transformed, `RXE` is off it and is read and
|
|
1463
|
+
* refused. Whether an occurrence is reported at all is decided elsewhere, by whether it reached an
|
|
1464
|
+
* emitted resource; this set only chooses which of the two codes the report carries.
|
|
1465
|
+
*
|
|
1466
|
+
* **Qualified rows collapse onto their segment name.** The index publishes several maps per name and
|
|
1467
|
+
* some of them carry a use qualifier (`MSH[Source]`, `PID[Patient]`, `PV1[EncounterHistory]`,
|
|
1468
|
+
* `OBX[Component]`, `NTE[Comment]`, `PD1[LivingWill]`, `PRT[Location]`, `ROL[GeneralPractitioner]`
|
|
1469
|
+
* and the rest). A qualifier selects a use of the same segment, so every one of them contributes its
|
|
1470
|
+
* base name and nothing else: what a message carries is an `MSH`, never an `MSH[Source]`.
|
|
1471
|
+
*
|
|
1472
|
+
* @packageDocumentation
|
|
1473
|
+
*/
|
|
1474
|
+
/**
|
|
1475
|
+
* The published version of the HL7 Version 2 to FHIR Implementation Guide this set was read from.
|
|
1476
|
+
*
|
|
1477
|
+
* @example
|
|
1478
|
+
* ```ts
|
|
1479
|
+
* import { IG_SEGMENT_MAPS_VERSION } from "@cosyte/transform";
|
|
1480
|
+
* IG_SEGMENT_MAPS_VERSION; // "1.0.0"
|
|
1481
|
+
* ```
|
|
1482
|
+
*/
|
|
1483
|
+
declare const IG_SEGMENT_MAPS_VERSION = "1.0.0";
|
|
1484
|
+
/**
|
|
1485
|
+
* The publication date of {@link IG_SEGMENT_MAPS_VERSION}, so a later release of the guide can be
|
|
1486
|
+
* told apart from a defect in this library.
|
|
1487
|
+
*
|
|
1488
|
+
* @example
|
|
1489
|
+
* ```ts
|
|
1490
|
+
* import { IG_SEGMENT_MAPS_PUBLISHED } from "@cosyte/transform";
|
|
1491
|
+
* IG_SEGMENT_MAPS_PUBLISHED; // "2025-10-07"
|
|
1492
|
+
* ```
|
|
1493
|
+
*/
|
|
1494
|
+
declare const IG_SEGMENT_MAPS_PUBLISHED = "2025-10-07";
|
|
1495
|
+
/**
|
|
1496
|
+
* The date the Segment Maps index was retrieved and transcribed into {@link IG_MAPPED_SEGMENT_NAMES}.
|
|
1497
|
+
*
|
|
1498
|
+
* A classification that surprises a reader is answered by re-reading the index at this date: the set
|
|
1499
|
+
* is a transcription of a published document at a moment, not a live query.
|
|
1500
|
+
*
|
|
1501
|
+
* @example
|
|
1502
|
+
* ```ts
|
|
1503
|
+
* import { IG_SEGMENT_MAPS_RETRIEVED } from "@cosyte/transform";
|
|
1504
|
+
* IG_SEGMENT_MAPS_RETRIEVED; // "2026-08-22"
|
|
1505
|
+
* ```
|
|
1506
|
+
*/
|
|
1507
|
+
declare const IG_SEGMENT_MAPS_RETRIEVED = "2026-08-22";
|
|
1508
|
+
/**
|
|
1509
|
+
* The Segment Maps index this set was transcribed from, so a reader can audit any entry.
|
|
1510
|
+
*
|
|
1511
|
+
* @example
|
|
1512
|
+
* ```ts
|
|
1513
|
+
* import { IG_SEGMENT_MAPS_SOURCE } from "@cosyte/transform";
|
|
1514
|
+
* IG_SEGMENT_MAPS_SOURCE.startsWith("https://"); // true
|
|
1515
|
+
* ```
|
|
1516
|
+
*/
|
|
1517
|
+
declare const IG_SEGMENT_MAPS_SOURCE = "https://hl7.org/fhir/uv/v2mappings/segment_maps.html";
|
|
1518
|
+
/**
|
|
1519
|
+
* Every v2 segment name the guide publishes at least one segment-to-resource map for, at
|
|
1520
|
+
* {@link IG_SEGMENT_MAPS_VERSION}, as of {@link IG_SEGMENT_MAPS_RETRIEVED}.
|
|
1521
|
+
*
|
|
1522
|
+
* Thirty-three names, transcribed chapter by chapter from {@link IG_SEGMENT_MAPS_SOURCE}: Control
|
|
1523
|
+
* (`MSA`, `MSH`, `NTE`, `SFT`), Patient Administration (`AL1`, `EVN`, `IAM`, `MRG`, `NK1`, `PD1`,
|
|
1524
|
+
* `PID`, `PV1`, `PV2`), Order Entry (`OBR`, `ORC`, `TQ1`), Pharmacy and Vaccination (`RXA`, `RXO`,
|
|
1525
|
+
* `RXR`), Financial Management (`DG1`, `IN1`, `IN3`, `PR1`), Observation Reporting (`OBX`, `PRT`,
|
|
1526
|
+
* `SPM`), Medical Records (`TXA`), Scheduling (`AIG`, `AIL`, `AIP`, `AIS`, `SCH`) and Personnel
|
|
1527
|
+
* Management (`ROL`).
|
|
1528
|
+
*
|
|
1529
|
+
* Two absences a reader is most likely to check, because they look like oversights and are not:
|
|
1530
|
+
* **`RXE` is not on this set** (the guide publishes no map for it, which is why this library counts
|
|
1531
|
+
* an `RXE` and refuses to assemble one), and neither is **`GT1`**.
|
|
1532
|
+
*
|
|
1533
|
+
* @example
|
|
1534
|
+
* ```ts
|
|
1535
|
+
* import { IG_MAPPED_SEGMENT_NAMES } from "@cosyte/transform";
|
|
1536
|
+
* IG_MAPPED_SEGMENT_NAMES.has("IAM"); // true
|
|
1537
|
+
* IG_MAPPED_SEGMENT_NAMES.has("RXE"); // false
|
|
1538
|
+
* ```
|
|
1539
|
+
*/
|
|
1540
|
+
declare const IG_MAPPED_SEGMENT_NAMES: ReadonlySet<string>;
|
|
1541
|
+
/**
|
|
1542
|
+
* Whether the guide publishes a segment map for `name`, decided against
|
|
1543
|
+
* {@link IG_MAPPED_SEGMENT_NAMES} and against nothing else.
|
|
1544
|
+
*
|
|
1545
|
+
* The comparison is exact: a name this library could not classify is not in the set, and is reported
|
|
1546
|
+
* as a standard gap for that reason rather than because the guide was consulted and published
|
|
1547
|
+
* nothing. That conflation is deliberate and is documented on the code itself; the alternative would
|
|
1548
|
+
* be re-deriving a name the parser refused to vouch for.
|
|
1549
|
+
*
|
|
1550
|
+
* @param name - A v2 segment name, exactly as the parser published it.
|
|
1551
|
+
* @example
|
|
1552
|
+
* ```ts
|
|
1553
|
+
* import { isIgMappedSegmentName } from "@cosyte/transform";
|
|
1554
|
+
* isIgMappedSegmentName("DG1"); // true
|
|
1555
|
+
* isIgMappedSegmentName("ZAL"); // false
|
|
1556
|
+
* ```
|
|
1557
|
+
*/
|
|
1558
|
+
declare function isIgMappedSegmentName(name: string): boolean;
|
|
1559
|
+
|
|
948
1560
|
/**
|
|
949
1561
|
* VXU_V04 → FHIR `Immunization`: the thin IG single for immunization administration,
|
|
950
1562
|
* grounded firsthand on the IG **VXU_V04 message map** and the **RXA/RXR/ORC → Immunization**
|
|
@@ -1075,76 +1687,655 @@ declare const IMMUNIZATION_STATUS_MAP: Readonly<Record<string, string>>;
|
|
|
1075
1687
|
declare const APPOINTMENT_STATUS_MAP: Readonly<Record<string, string>>;
|
|
1076
1688
|
|
|
1077
1689
|
/**
|
|
1078
|
-
*
|
|
1079
|
-
*
|
|
1080
|
-
*
|
|
1081
|
-
*
|
|
1082
|
-
*
|
|
1083
|
-
*
|
|
1084
|
-
* `ConceptMap-table-*.json`). Each shipped map below cites the exact IG ConceptMap it transcribes and
|
|
1085
|
-
* was verified against that resource's JSON (its `group.element[].target[]` rows and its `unmatched`
|
|
1086
|
-
* `(unmapped)` group). The engine is **fail-safe by refusal**: a source code the IG map has **no
|
|
1087
|
-
* target** for (its `(unmapped)` group) is never coerced to a plausible neighbor. The caller flags it
|
|
1088
|
-
* and preserves the raw coding, or withholds the value, but a target is **never fabricated**.
|
|
1690
|
+
* AL1 to FHIR `AllergyIntolerance`: the allergy an ADT states, carried into the bundle instead of
|
|
1691
|
+
* lost between the v2 feed and the FHIR record. Grounded firsthand on the IG **AL1 to
|
|
1692
|
+
* AllergyIntolerance** segment map and the **ADT_A01 to Bundle** message map
|
|
1693
|
+
* (`hl7.fhir.uv.v2mappings`, STU1; `ConceptMap-segment-al1-to-allergyintolerance.html`,
|
|
1694
|
+
* `ConceptMap-message-adt-a01-to-bundle.html`), which wires
|
|
1695
|
+
* `AllergyIntolerance.patient.reference` to the bundle's Patient and creates one resource per AL1.
|
|
1089
1696
|
*
|
|
1090
|
-
*
|
|
1091
|
-
*
|
|
1092
|
-
* (`
|
|
1093
|
-
*
|
|
1094
|
-
*
|
|
1095
|
-
*
|
|
1096
|
-
*
|
|
1097
|
-
*
|
|
1098
|
-
*
|
|
1099
|
-
*
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
1102
|
-
*
|
|
1103
|
-
*
|
|
1697
|
+
* | v2 field | FHIR target | via |
|
|
1698
|
+
* |---|---|---|
|
|
1699
|
+
* | AL1 (fixed) | `clinicalStatus` (`active`) | the IG's own assignment for constraint ait-1 |
|
|
1700
|
+
* | AL1-2 Allergen Type Code (CWE) | `category` (0..*) | {@link ALLERGY_CATEGORY_VALUE_MAP} (HL70127) |
|
|
1701
|
+
* | AL1-2 Allergen Type Code (CWE) | `category.extension` alternate-codes | {@link ALLERGY_ORIGINAL_CATEGORY_VALUE_MAP} (v2-0127 identity) |
|
|
1702
|
+
* | AL1-2 Allergen Type Code (CWE) | `type` (0..1) | {@link ALLERGY_TYPE_VALUE_MAP} (HL70127) |
|
|
1703
|
+
* | AL1-3 Allergen Code (CWE) | `code` | {@link toFhirCodeableConcept} (structural, no IG value map) |
|
|
1704
|
+
* | AL1-4 Allergy Severity Code (CWE) | `criticality` (0..1) | {@link ALLERGY_CRITICALITY_VALUE_MAP} (HL70128) |
|
|
1705
|
+
* | AL1-4 Allergy Severity Code (CWE) | `criticality.extension` alternate-codes | {@link ALLERGY_ORIGINAL_CRITICALITY_VALUE_MAP} (v2-0128 identity) |
|
|
1706
|
+
* | AL1-5 Allergy Reaction Code (ST) | `reaction.manifestation.text` | verbatim, one manifestation per repetition |
|
|
1707
|
+
* | AL1-6 Identification Date (DT) | `onsetDateTime` | {@link toFhirDateTime}, legacy versions only |
|
|
1708
|
+
* | (message-map wiring) | `patient` (required 1..1) | the bundle's Patient |
|
|
1709
|
+
*
|
|
1710
|
+
* **Fail-safes (never a confident wrong allergy).**
|
|
1711
|
+
* - **Two maps over one component, resolved independently.** The guide publishes a separate Table
|
|
1712
|
+
* 0127 map for `category` and for `type`, with different unmapped sets, so `MA` yields a type and
|
|
1713
|
+
* **no category**. Neither answer is ever borrowed from the other map or from a neighbouring code:
|
|
1714
|
+
* the element the map has no target for is omitted and flagged
|
|
1715
|
+
* {@link ISSUE_CODES.TRANSFORM_CODE_UNMAPPED}.
|
|
1716
|
+
* - **The original code survives whatever the map does.** The IG makes the `alternate-codes`
|
|
1717
|
+
* extension `1..1` wherever AL1-2 or AL1-4 is valued, so the code the sender wrote is carried in
|
|
1718
|
+
* its own v2 code system even when the translating map had no target for it. An unmapped category
|
|
1719
|
+
* is therefore a `category` element with the extension and no code value, not an absent one.
|
|
1720
|
+
* - **`criticality` is the only severity target.** `AllergyIntolerance.reaction.severity` is a LOCAL
|
|
1721
|
+
* VARIATION in the guide, conditioned on a severity that was not used equivalently to criticality,
|
|
1722
|
+
* which no message states. It is never populated here, so a `MO` or `U` severity leaves criticality
|
|
1723
|
+
* absent and flagged rather than reappearing as a reaction grading the source never asserted.
|
|
1724
|
+
* - **An allergy names what it is to.** AL1-3 is the allergen. An AL1 that grounds no allergen code
|
|
1725
|
+
* and no allergen text asserts an allergy without saying to what, so the resource is withheld and
|
|
1726
|
+
* flagged {@link ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED} rather than emitted naming nothing.
|
|
1727
|
+
* - **AL1-6 is legacy input, never a live element.** The IG's own comment on that row reads
|
|
1728
|
+
* "Withdrawn as of 2.7", so `onsetDateTime` is carried only for a message whose MSH-12 is readable
|
|
1729
|
+
* and earlier than 2.7. A valued AL1-6 on any later, absent or unreadable version is dropped and
|
|
1730
|
+
* flagged, never read as an onset the sender may not have meant.
|
|
1731
|
+
* - **A rejected extension costs the extension, not the allergy.** If the alternate-codes extension
|
|
1732
|
+
* would make the resource fail the conservative-emit gate, the same resource is emitted without it
|
|
1733
|
+
* and the drop is flagged, so a consumer keeps the allergy rather than losing it to a detail.
|
|
1734
|
+
*
|
|
1735
|
+
* Deferred and flagged elsewhere, not silently mapped: `IAM` (the guide's other allergy segment),
|
|
1736
|
+
* AL1-1 (the guide states it does not warrant mapping), and `verificationStatus`, which no AL1
|
|
1737
|
+
* component grounds.
|
|
1104
1738
|
*
|
|
1105
1739
|
* @packageDocumentation
|
|
1106
1740
|
*/
|
|
1107
1741
|
|
|
1108
|
-
/** HL7 v2 Table 0162 (Route of Administration) THO CodeSystem URI. */
|
|
1109
|
-
declare const V2_0162_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0162";
|
|
1110
|
-
/** HL7 v3 RouteOfAdministration CodeSystem URI (the IG's remap target for the six common routes). */
|
|
1111
|
-
declare const V3_ROUTE_OF_ADMINISTRATION_SYSTEM = "http://terminology.hl7.org/CodeSystem/v3-RouteOfAdministration";
|
|
1112
|
-
/** HL7 v2 Table 0550 (Body Parts) THO CodeSystem URI. */
|
|
1113
|
-
declare const V2_0550_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0550";
|
|
1114
|
-
/** HL7 v2 Table 0277 (Appointment Type Codes) THO CodeSystem URI. */
|
|
1115
|
-
declare const V2_0277_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0277";
|
|
1116
|
-
/** HL7 v2 Table 0161 (Allow Substitution) THO CodeSystem URI. */
|
|
1117
|
-
declare const V2_0161_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0161";
|
|
1118
|
-
/** A translated FHIR target coding: the target CodeSystem URI + code, and the IG's target display. */
|
|
1119
|
-
interface CodedTarget {
|
|
1120
|
-
/** The FHIR target CodeSystem canonical URI. */
|
|
1121
|
-
readonly system: string;
|
|
1122
|
-
/** The FHIR target code. */
|
|
1123
|
-
readonly code: string;
|
|
1124
|
-
/** The IG map's target display, when it carries one (absent for the identity table maps). */
|
|
1125
|
-
readonly display?: string;
|
|
1126
|
-
}
|
|
1127
1742
|
/**
|
|
1128
|
-
*
|
|
1129
|
-
*
|
|
1130
|
-
*
|
|
1743
|
+
* The code system of the fixed `clinicalStatus` the IG assigns to every AllergyIntolerance built
|
|
1744
|
+
* from an AL1, transcribed from the segment map's own sort-order 0 row.
|
|
1745
|
+
*
|
|
1746
|
+
* @example
|
|
1747
|
+
* ```ts
|
|
1748
|
+
* import { ALLERGY_CLINICAL_STATUS_SYSTEM } from "@cosyte/transform";
|
|
1749
|
+
* ALLERGY_CLINICAL_STATUS_SYSTEM.endsWith("allergyintolerance-clinical"); // true
|
|
1750
|
+
* ```
|
|
1131
1751
|
*/
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1752
|
+
declare const ALLERGY_CLINICAL_STATUS_SYSTEM = "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical";
|
|
1753
|
+
/**
|
|
1754
|
+
* The fixed `clinicalStatus` code the IG assigns. The guide's own note explains why it is fixed
|
|
1755
|
+
* rather than derived: constraint ait-1 requires a clinicalStatus unless the verificationStatus is
|
|
1756
|
+
* `entered-in-error`, and no AL1 component can ground that retraction.
|
|
1757
|
+
*
|
|
1758
|
+
* @example
|
|
1759
|
+
* ```ts
|
|
1760
|
+
* import { ALLERGY_CLINICAL_STATUS_CODE } from "@cosyte/transform";
|
|
1761
|
+
* ALLERGY_CLINICAL_STATUS_CODE; // "active"
|
|
1762
|
+
* ```
|
|
1763
|
+
*/
|
|
1764
|
+
declare const ALLERGY_CLINICAL_STATUS_CODE = "active";
|
|
1765
|
+
/**
|
|
1766
|
+
* The canonical URL of the FHIR `alternate-codes` extension, the IG's fixed assignment for the
|
|
1767
|
+
* `category.extension.url` and `criticality.extension.url` rows.
|
|
1768
|
+
*
|
|
1769
|
+
* @example
|
|
1770
|
+
* ```ts
|
|
1771
|
+
* import { ALTERNATE_CODES_EXTENSION_URL } from "@cosyte/transform";
|
|
1772
|
+
* ALTERNATE_CODES_EXTENSION_URL; // "http://hl7.org/fhir/StructureDefinition/alternate-codes"
|
|
1773
|
+
* ```
|
|
1774
|
+
*/
|
|
1775
|
+
declare const ALTERNATE_CODES_EXTENSION_URL = "http://hl7.org/fhir/StructureDefinition/alternate-codes";
|
|
1776
|
+
/**
|
|
1777
|
+
* The HL7 v2 version at which AL1-6 Identification Date was withdrawn, per the IG map's own comment
|
|
1778
|
+
* on that row ("Withdrawn as of 2.7, Refer to IAM-11 Onset or IAM-13 Reported Date"). A message at
|
|
1779
|
+
* this version or later carries no AL1-6 that this library will read as an onset.
|
|
1780
|
+
*
|
|
1781
|
+
* @example
|
|
1782
|
+
* ```ts
|
|
1783
|
+
* import { AL1_ONSET_WITHDRAWN_AT } from "@cosyte/transform";
|
|
1784
|
+
* AL1_ONSET_WITHDRAWN_AT; // [2, 7]
|
|
1785
|
+
* ```
|
|
1786
|
+
*/
|
|
1787
|
+
declare const AL1_ONSET_WITHDRAWN_AT: readonly number[];
|
|
1788
|
+
/**
|
|
1789
|
+
* Whether a message's MSH-12 version identifier is readable **and** earlier than the version at
|
|
1790
|
+
* which AL1-6 was withdrawn ({@link AL1_ONSET_WITHDRAWN_AT}).
|
|
1791
|
+
*
|
|
1792
|
+
* Fails closed in both directions a version can be unusable: an absent MSH-12 and one this library
|
|
1793
|
+
* cannot compare (a vendor string, an empty component, anything outside the dotted-numeric shape)
|
|
1794
|
+
* both answer `false`, so a valued AL1-6 is dropped and flagged rather than read as an onset on a
|
|
1795
|
+
* message that may never have meant one.
|
|
1796
|
+
*
|
|
1797
|
+
* @param version - The MSH-12 version identifier exactly as the parser published it.
|
|
1798
|
+
* @example
|
|
1799
|
+
* ```ts
|
|
1800
|
+
* import { carriesLegacyOnsetDate } from "@cosyte/transform";
|
|
1801
|
+
* carriesLegacyOnsetDate("2.5.1"); // true
|
|
1802
|
+
* carriesLegacyOnsetDate("2.7"); // false
|
|
1803
|
+
* carriesLegacyOnsetDate(undefined); // false
|
|
1804
|
+
* ```
|
|
1805
|
+
*/
|
|
1806
|
+
declare function carriesLegacyOnsetDate(version: string | undefined): boolean;
|
|
1807
|
+
/**
|
|
1808
|
+
* Every `AL1` occurrence a message carries, in message order. One AllergyIntolerance is created per
|
|
1809
|
+
* occurrence, per the IG message map's `0..-1` cardinality on the AL1 row.
|
|
1810
|
+
*
|
|
1811
|
+
* @param msg - The parsed `@cosyte/hl7` message.
|
|
1812
|
+
* @example
|
|
1813
|
+
* ```ts
|
|
1814
|
+
* import { parseHL7 } from "@cosyte/hl7";
|
|
1815
|
+
* // collectAllergies(parseHL7(raw)).length; // one entry per AL1 in the message
|
|
1816
|
+
* ```
|
|
1817
|
+
*/
|
|
1818
|
+
declare function collectAllergies(msg: Hl7Message): readonly Segment[];
|
|
1819
|
+
/** How much of the IG's AL1 mapping to carry: the alternate-codes extension is the one variable. */
|
|
1820
|
+
interface AllergyBuildOptions {
|
|
1821
|
+
/**
|
|
1822
|
+
* Whether to carry the IG's `alternate-codes` extension on `category` / `criticality`. Defaults to
|
|
1823
|
+
* `true`; the emit path builds a second draft with it off only when the first draft fails the
|
|
1824
|
+
* conservative-emit gate.
|
|
1825
|
+
*/
|
|
1826
|
+
readonly carryAlternateCodes?: boolean;
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
/**
|
|
1830
|
+
* DG1 to FHIR `Condition`: the diagnoses an admit states, carried into the bundle instead of lost
|
|
1831
|
+
* between the v2 feed and the FHIR record. Grounded firsthand on the IG **DG1 to Condition** segment
|
|
1832
|
+
* map and the **ADT_A01 to Bundle** message map (`hl7.fhir.uv.v2mappings`, STU1;
|
|
1833
|
+
* `ConceptMap-segment-dg1-to-condition.html`, `ConceptMap-message-adt-a01-to-bundle.html`), which
|
|
1834
|
+
* wires `Condition[1].subject.reference` to the bundle's Patient, creates one Condition per DG1, and
|
|
1835
|
+
* references it back from `Encounter[1].diagnosis`.
|
|
1836
|
+
*
|
|
1837
|
+
* | v2 field | FHIR target | via |
|
|
1838
|
+
* |---|---|---|
|
|
1839
|
+
* | DG1-3 Diagnosis Code (CWE) | `code` | {@link toFhirCodeableConcept} (structural, no IG value map) |
|
|
1840
|
+
* | DG1-4 Diagnosis Description (ST) | `code.text` | verbatim, overriding the coded field's own text |
|
|
1841
|
+
* | DG1-5 Diagnosis Date/Time (DTM) | `onsetDateTime` | {@link toFhirDateTime} |
|
|
1842
|
+
* | DG1-19 Attestation Date/Time (DTM) | `recordedDate` | {@link toFhirDateTime} |
|
|
1843
|
+
* | DG1-20 Diagnosis Identifier (EI) | `identifier` | {@link toFhirEntityIdentifier} |
|
|
1844
|
+
* | DG1-21 Diagnosis Action Code (ID) | `verificationStatus` | the map's fixed assignment for `D` |
|
|
1845
|
+
* | (message-map wiring) | `subject` (required 1..1) | the bundle's Patient |
|
|
1846
|
+
*
|
|
1847
|
+
* **Fail-safes (never a confident wrong diagnosis).**
|
|
1848
|
+
* - **One action code is mapped, and only one.** The DG1-21 row assigns the fixed value
|
|
1849
|
+
* `entered-in-error` and names no source code; its own comment reads "Other values (A and U) don't
|
|
1850
|
+
* map to anything", and Table 0206 publishes `A`, `D`, `S`, `U` and `X`. `D` (Delete) is therefore
|
|
1851
|
+
* the code the assignment covers: every other published action code leaves `verificationStatus`
|
|
1852
|
+
* absent and raises {@link ISSUE_CODES.TRANSFORM_CODE_UNMAPPED}, never a neighbouring status.
|
|
1853
|
+
* - **A diagnosis with no code and no text is visible, not silent.** Such a DG1 still becomes a
|
|
1854
|
+
* Condition (the message stated a diagnosis), and the empty `code` is declared
|
|
1855
|
+
* {@link ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED} rather than read as an intentionally empty one.
|
|
1856
|
+
* - **`clinicalStatus` is absent because no row grounds it.** The map has no `Condition.clinicalStatus`
|
|
1857
|
+
* row and no DG1 component states one, so it is never filled. R4's `con-3` invariant expects it
|
|
1858
|
+
* wherever `verificationStatus` is not `entered-in-error`, so a Condition built here can fail that
|
|
1859
|
+
* invariant under a full profile validator: that is a declared gap, and filling the element from
|
|
1860
|
+
* nothing is the fabrication this library refuses.
|
|
1861
|
+
*
|
|
1862
|
+
* Deferred and flagged, never silently mapped: `DG1-16` (`asserter`, a Practitioner resource this
|
|
1863
|
+
* library does not build), `DG1-22` (the `condition-dueTo` extension, whose reference resolves by
|
|
1864
|
+
* identifier rather than by bundle position) and the map's `EpisodeOfCare` target, so a diagnosis
|
|
1865
|
+
* ties to an Encounter or to nothing.
|
|
1866
|
+
*
|
|
1867
|
+
* @packageDocumentation
|
|
1868
|
+
*/
|
|
1869
|
+
|
|
1870
|
+
/**
|
|
1871
|
+
* The code system of the `verificationStatus` the DG1-21 row assigns, transcribed from the segment
|
|
1872
|
+
* map's own `Condition.verificationStatus.coding.system` assignment.
|
|
1873
|
+
*
|
|
1874
|
+
* @example
|
|
1875
|
+
* ```ts
|
|
1876
|
+
* import { CONDITION_VERIFICATION_STATUS_SYSTEM } from "@cosyte/transform";
|
|
1877
|
+
* CONDITION_VERIFICATION_STATUS_SYSTEM.endsWith("condition-ver-status"); // true
|
|
1878
|
+
* ```
|
|
1879
|
+
*/
|
|
1880
|
+
declare const CONDITION_VERIFICATION_STATUS_SYSTEM = "http://terminology.hl7.org/CodeSystem/condition-ver-status";
|
|
1881
|
+
/**
|
|
1882
|
+
* The `verificationStatus` code the DG1-21 row assigns, transcribed from the segment map's own
|
|
1883
|
+
* `Condition.verificationStatus.coding.code` assignment.
|
|
1884
|
+
*
|
|
1885
|
+
* @example
|
|
1886
|
+
* ```ts
|
|
1887
|
+
* import { CONDITION_ENTERED_IN_ERROR } from "@cosyte/transform";
|
|
1888
|
+
* CONDITION_ENTERED_IN_ERROR; // "entered-in-error"
|
|
1889
|
+
* ```
|
|
1890
|
+
*/
|
|
1891
|
+
declare const CONDITION_ENTERED_IN_ERROR = "entered-in-error";
|
|
1892
|
+
/**
|
|
1893
|
+
* The Table 0206 (Segment Action Code) code the DG1-21 assignment covers. The row assigns a
|
|
1894
|
+
* retraction and names no source code; `D` is Delete, and the row's own comment excludes the rest.
|
|
1895
|
+
*
|
|
1896
|
+
* @example
|
|
1897
|
+
* ```ts
|
|
1898
|
+
* import { DG1_RETRACTION_ACTION_CODE } from "@cosyte/transform";
|
|
1899
|
+
* DG1_RETRACTION_ACTION_CODE; // "D"
|
|
1900
|
+
* ```
|
|
1901
|
+
*/
|
|
1902
|
+
declare const DG1_RETRACTION_ACTION_CODE = "D";
|
|
1903
|
+
/**
|
|
1904
|
+
* Every `DG1` occurrence a message carries, in message order. One Condition is created per
|
|
1905
|
+
* occurrence, per the IG message map's `0..-1` cardinality on the DG1 row.
|
|
1906
|
+
*
|
|
1907
|
+
* @param msg - The parsed `@cosyte/hl7` message.
|
|
1908
|
+
* @example
|
|
1909
|
+
* ```ts
|
|
1910
|
+
* import { parseHL7 } from "@cosyte/hl7";
|
|
1911
|
+
* // collectDiagnoses(parseHL7(raw)).length; // one entry per DG1 in the message
|
|
1912
|
+
* ```
|
|
1913
|
+
*/
|
|
1914
|
+
declare function collectDiagnoses(msg: Hl7Message): readonly Segment[];
|
|
1915
|
+
/**
|
|
1916
|
+
* The value-free diagnostics for the DG1 rows this library reads and deliberately does not build:
|
|
1917
|
+
* the diagnosing clinician (a Practitioner resource), and the parent diagnosis (a reference resolved
|
|
1918
|
+
* by identifier rather than by bundle position).
|
|
1919
|
+
*
|
|
1920
|
+
* Raised for the occurrence whether or not the Condition itself is emitted, so a deferred row a
|
|
1921
|
+
* message actually valued is in the issues list rather than silently absent.
|
|
1922
|
+
*
|
|
1923
|
+
* @param dg1 - The `DG1` `@cosyte/hl7` `Segment`.
|
|
1924
|
+
* @example
|
|
1925
|
+
* ```ts
|
|
1926
|
+
* // deferredDiagnosisIssues(dg1).length; // one per valued deferred row
|
|
1927
|
+
* ```
|
|
1928
|
+
*/
|
|
1929
|
+
declare function deferredDiagnosisIssues(dg1: Segment): readonly TransformIssue[];
|
|
1930
|
+
/**
|
|
1931
|
+
* Build a FHIR `Condition` resource node from one `DG1` occurrence, wired to the bundle's Patient.
|
|
1932
|
+
* Always produces a Condition: a DG1 states a diagnosis, so an occurrence that grounds neither a
|
|
1933
|
+
* code nor a description is emitted with its empty `code` declared rather than withheld.
|
|
1934
|
+
*
|
|
1935
|
+
* @param dg1 - The `DG1` `@cosyte/hl7` `Segment`.
|
|
1936
|
+
* @param patientFullUrl - The bundle's Patient fullUrl, for `subject` (required 1..1).
|
|
1937
|
+
* @param ctx - The transform context (naming-system registry + timezone policy).
|
|
1938
|
+
* @param v2Location - The occurrence's v2 location, for the codeless diagnostic (e.g. `DG1[0]`).
|
|
1939
|
+
* @example
|
|
1940
|
+
* ```ts
|
|
1941
|
+
* import { parseHL7 } from "@cosyte/hl7";
|
|
1942
|
+
* // const dg1 = parseHL7(raw).segments("DG1")[0];
|
|
1943
|
+
* // const { value } = buildCondition(dg1!, "urn:uuid:pat", {}, "DG1[0]");
|
|
1944
|
+
* ```
|
|
1945
|
+
*/
|
|
1946
|
+
declare function buildCondition(dg1: Segment, patientFullUrl: string, ctx: TransformContext, v2Location: string): ConvertResult<FhirComplex>;
|
|
1947
|
+
|
|
1948
|
+
/**
|
|
1949
|
+
* PR1 to FHIR `Procedure`: the procedures an admit states, carried into the bundle instead of lost
|
|
1950
|
+
* between the v2 feed and the FHIR record. Grounded firsthand on the IG **PR1 to Procedure** segment
|
|
1951
|
+
* map and the **ADT_A01 to Bundle** message map (`hl7.fhir.uv.v2mappings`, STU1;
|
|
1952
|
+
* `ConceptMap-segment-pr1-to-procedure.html`, `ConceptMap-message-adt-a01-to-bundle.html`), which
|
|
1953
|
+
* wires `Procedure.subject.reference` to the bundle's Patient and creates one Procedure per PR1.
|
|
1954
|
+
*
|
|
1955
|
+
* | v2 field | FHIR target | via |
|
|
1956
|
+
* |---|---|---|
|
|
1957
|
+
* | PR1 (narrative row) | `status` (required 1..1) | the row's own `unknown`, the value it directs when the message context does not determine one |
|
|
1958
|
+
* | PR1-3 Procedure Code (CNE) | `code` | {@link toFhirCodeableConcept} (structural, no IG value map) |
|
|
1959
|
+
* | PR1-4 Procedure Description (ST) | `code.text` | verbatim, only `IF PR1-3.9 NOT VALUED` |
|
|
1960
|
+
* | PR1-5 Procedure Date/Time (DTM) | `performedDateTime` / `performedPeriod.start` | {@link toFhirDateTime}, branched on PR1-7 |
|
|
1961
|
+
* | PR1-6 Procedure Functional Type (CWE) | `category` | {@link toFhirCodeableConcept} |
|
|
1962
|
+
* | PR1-7 Procedure Minutes (NM) | `performedPeriod.end` | the period start plus that many minutes |
|
|
1963
|
+
* | PR1-15 Associated Diagnosis Code (CWE) | `reasonCode` | {@link toFhirCodeableConcept} |
|
|
1964
|
+
* | PR1-19 Procedure Identifier (EI) | `identifier` | {@link toFhirEntityIdentifier} |
|
|
1965
|
+
* | (message-map wiring) | `subject` (required 1..1) | the bundle's Patient |
|
|
1966
|
+
*
|
|
1967
|
+
* **Fail-safes (never a confident wrong procedure).**
|
|
1968
|
+
* - **The status is the one the map directs, never one the message did not state.** The map's own
|
|
1969
|
+
* sort-order 0 row reads "The value mapping depends on the message context where the PR1 is used
|
|
1970
|
+
* and to be determined by the implementer. If not clear, use `unknown`". No PR1 component states a
|
|
1971
|
+
* procedure status, so `unknown` is the answer, and `completed` or `in-progress` is never selected
|
|
1972
|
+
* from a message that did not say so.
|
|
1973
|
+
* - **A period needs a start precise enough to add minutes to.** PR1-7 is minutes, and the map's own
|
|
1974
|
+
* comment requires PR1-5 to reach at least minute granularity before a `performedPeriod` is built
|
|
1975
|
+
* from the pair. This library goes by what the conversion could actually carry: a PR1-5 that
|
|
1976
|
+
* reduces to date precision (a naked or partial-precision timestamp, per {@link toFhirDateTime})
|
|
1977
|
+
* is not a start a duration can be added to, so the value stays `performedDateTime` and the end is
|
|
1978
|
+
* declared {@link ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED} rather than computed from a date.
|
|
1979
|
+
* - **The description defers to the coded field's own text.** The PR1-4 row is conditioned
|
|
1980
|
+
* `IF PR1-3.9 NOT VALUED`, so an original text the sender put in the coded field wins and PR1-4 is
|
|
1981
|
+
* dropped and declared rather than overwriting it.
|
|
1982
|
+
*
|
|
1983
|
+
* Deferred and flagged, never silently mapped: `PR1-8`, `PR1-11` and `PR1-12` (`performer.actor`, a
|
|
1984
|
+
* Practitioner resource this library does not build), `PR1-23` (`location`, likewise a Location),
|
|
1985
|
+
* `PR1-25` (`partOf`, a reference resolved by identifier rather than by bundle position) and
|
|
1986
|
+
* `PR1-16`, whose target `Procedure.code.value` is not an element of R4's `Procedure.code`.
|
|
1987
|
+
*
|
|
1988
|
+
* @packageDocumentation
|
|
1989
|
+
*/
|
|
1990
|
+
|
|
1991
|
+
/**
|
|
1992
|
+
* The `Procedure.status` the PR1 map's own narrative row directs when the message context does not
|
|
1993
|
+
* determine one. No PR1 component carries a procedure status, so this is the value every Procedure
|
|
1994
|
+
* built from a PR1 carries.
|
|
1995
|
+
*
|
|
1996
|
+
* @example
|
|
1997
|
+
* ```ts
|
|
1998
|
+
* import { PROCEDURE_STATUS_UNKNOWN } from "@cosyte/transform";
|
|
1999
|
+
* PROCEDURE_STATUS_UNKNOWN; // "unknown"
|
|
2000
|
+
* ```
|
|
2001
|
+
*/
|
|
2002
|
+
declare const PROCEDURE_STATUS_UNKNOWN = "unknown";
|
|
2003
|
+
/**
|
|
2004
|
+
* Every `PR1` occurrence a message carries, in message order. One Procedure is created per
|
|
2005
|
+
* occurrence, per the IG message map's `0..-1` cardinality on the PROCEDURE group.
|
|
2006
|
+
*
|
|
2007
|
+
* @param msg - The parsed `@cosyte/hl7` message.
|
|
2008
|
+
* @example
|
|
2009
|
+
* ```ts
|
|
2010
|
+
* import { parseHL7 } from "@cosyte/hl7";
|
|
2011
|
+
* // collectProcedures(parseHL7(raw)).length; // one entry per PR1 in the message
|
|
2012
|
+
* ```
|
|
2013
|
+
*/
|
|
2014
|
+
declare function collectProcedures(msg: Hl7Message): readonly Segment[];
|
|
2015
|
+
/**
|
|
2016
|
+
* Add whole minutes to a FHIR `dateTime` that carries a time of day, preserving its stated offset and
|
|
2017
|
+
* its fractional seconds exactly. Returns `undefined` when the value is not a full zoned timestamp,
|
|
2018
|
+
* because a date is not a start a duration can be added to.
|
|
2019
|
+
*
|
|
2020
|
+
* @param start - The FHIR `dateTime` lexical value the conversion produced.
|
|
2021
|
+
* @param minutes - The number of minutes to add.
|
|
2022
|
+
* @example
|
|
2023
|
+
* ```ts
|
|
2024
|
+
* import { addMinutes } from "@cosyte/transform";
|
|
2025
|
+
* addMinutes("2026-01-15T10:30:00-05:00", 45); // "2026-01-15T11:15:00-05:00"
|
|
2026
|
+
* addMinutes("2026-01-15", 45); // undefined
|
|
2027
|
+
* ```
|
|
2028
|
+
*/
|
|
2029
|
+
declare function addMinutes(start: string, minutes: number): string | undefined;
|
|
2030
|
+
/**
|
|
2031
|
+
* The value-free diagnostics for the PR1 rows this library reads and deliberately does not build:
|
|
2032
|
+
* the three practitioner roles, the treating organizational unit, the parent procedure, and the code
|
|
2033
|
+
* modifier whose target is not an R4 element.
|
|
2034
|
+
*
|
|
2035
|
+
* Raised for the occurrence whether or not the Procedure itself is emitted, so a deferred row a
|
|
2036
|
+
* message actually valued is in the issues list rather than silently absent.
|
|
2037
|
+
*
|
|
2038
|
+
* @param pr1 - The `PR1` `@cosyte/hl7` `Segment`.
|
|
2039
|
+
* @example
|
|
2040
|
+
* ```ts
|
|
2041
|
+
* // deferredProcedureIssues(pr1).length; // one per valued deferred row
|
|
2042
|
+
* ```
|
|
2043
|
+
*/
|
|
2044
|
+
declare function deferredProcedureIssues(pr1: Segment): readonly TransformIssue[];
|
|
2045
|
+
/**
|
|
2046
|
+
* Build a FHIR `Procedure` resource node from one `PR1` occurrence, wired to the bundle's Patient.
|
|
2047
|
+
* Always produces a Procedure: a PR1 states that a procedure happened, and `status` is grounded on
|
|
2048
|
+
* the map's own row rather than on the message, so nothing here can fail to be groundable.
|
|
2049
|
+
*
|
|
2050
|
+
* @param pr1 - The `PR1` `@cosyte/hl7` `Segment`.
|
|
2051
|
+
* @param patientFullUrl - The bundle's Patient fullUrl, for `subject` (required 1..1).
|
|
2052
|
+
* @param ctx - The transform context (naming-system registry + timezone policy).
|
|
2053
|
+
* @example
|
|
2054
|
+
* ```ts
|
|
2055
|
+
* import { parseHL7 } from "@cosyte/hl7";
|
|
2056
|
+
* // const pr1 = parseHL7(raw).segments("PR1")[0];
|
|
2057
|
+
* // const { value } = buildProcedure(pr1!, "urn:uuid:pat", {});
|
|
2058
|
+
* ```
|
|
2059
|
+
*/
|
|
2060
|
+
declare function buildProcedure(pr1: Segment, patientFullUrl: string, ctx: TransformContext): ConvertResult<FhirComplex>;
|
|
2061
|
+
|
|
2062
|
+
/**
|
|
2063
|
+
* IN1 to FHIR `Coverage`: the insurance an admit states, carried into the bundle instead of lost
|
|
2064
|
+
* between the v2 feed and the FHIR record. Grounded firsthand on the IG **IN1 to Coverage** segment
|
|
2065
|
+
* map and the **ADT_A01 to Bundle** message map (`hl7.fhir.uv.v2mappings`, STU1;
|
|
2066
|
+
* `ConceptMap-segment-in1-to-coverage.html`, `ConceptMap-message-adt-a01-to-bundle.html`), which
|
|
2067
|
+
* wires `Coverage.beneficiary.reference` to the bundle's Patient and creates one Coverage per IN1.
|
|
2068
|
+
*
|
|
2069
|
+
* | v2 field | FHIR target | via |
|
|
2070
|
+
* |---|---|---|
|
|
2071
|
+
* | IN1-2 Health Plan ID (CWE) | `identifier` | CWE.1 as the identifier value |
|
|
2072
|
+
* | IN1-4 Insurance Company Name (XON) | `payor` (required 1..*) | a reference carrying the name as `display` only |
|
|
2073
|
+
* | IN1-10 Insured's Group Emp ID (CX) | the subscriber-id extension, `IF CX.5 IS "SN"` | {@link toFhirIdentifier} |
|
|
2074
|
+
* | IN1-12 Plan Effective Date (DT) | `period.start` | {@link toFhirDateTime} |
|
|
2075
|
+
* | IN1-13 Plan Expiration Date (DT) | `period.end` | {@link toFhirDateTime} |
|
|
2076
|
+
* | IN1-15 Plan Type (CWE) | `type` | {@link toFhirCodeableConcept} |
|
|
2077
|
+
* | IN1-49 Insured's ID Number (CX) | the subscriber-id extension | {@link toFhirIdentifier} |
|
|
2078
|
+
* | (no row at all) | `status` (required 1..1) | a value-absent element, `data-absent-reason` `unknown` |
|
|
2079
|
+
* | (message-map wiring) | `beneficiary` (required 1..1) | the bundle's Patient |
|
|
2080
|
+
*
|
|
2081
|
+
* **Fail-safes (never a confident wrong coverage).**
|
|
2082
|
+
* - **The status is never asserted.** `Coverage.status` occurs nowhere in the segment map, and R4
|
|
2083
|
+
* makes the element required under a required binding whose members are all positive claims about
|
|
2084
|
+
* a policy. So it ships as a value-absent element carrying `data-absent-reason` `unknown`, plus
|
|
2085
|
+
* {@link ISSUE_CODES.TRANSFORM_REQUIRED_ELEMENT_UNKNOWN}: a consumer must read the status as
|
|
2086
|
+
* unknown, and `active` is never the answer this library gives to a message that did not say so.
|
|
2087
|
+
* - **A coverage names who pays.** `payor` is required 1..1 and IN1-4 is the only row that grounds
|
|
2088
|
+
* it, so an IN1 whose insurance company name is absent is **withheld** and declared
|
|
2089
|
+
* {@link ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED} rather than emitted naming no payer.
|
|
2090
|
+
* - **The payer is named, not resolved.** No Organization resource is built here, so `payor` carries
|
|
2091
|
+
* the company name as a `display` and no literal reference, and the missing resource is declared
|
|
2092
|
+
* rather than left to look like a resolvable link.
|
|
2093
|
+
* - **A health plan id keeps its value, never a synthesized system.** IN1-2 is a coded element whose
|
|
2094
|
+
* coding system is a table mnemonic, not an identifier namespace, so only CWE.1 is carried as
|
|
2095
|
+
* `Identifier.value`; a valued CWE.3 is declared dropped rather than turned into an
|
|
2096
|
+
* `Identifier.system` this library would be inventing.
|
|
2097
|
+
*
|
|
2098
|
+
* Deferred and flagged, never silently mapped: `IN1-5` (the payer address, an Organization this
|
|
2099
|
+
* library does not build), `IN1-10` and `IN1-11` (`policyHolder`, likewise an Organization),
|
|
2100
|
+
* `IN1-16` (`subscriber`, a Patient or RelatedPerson reference the guide conditions on IN1-17) and
|
|
2101
|
+
* `IN1-17` (`relationship`, whose value translation needs a Table 0063 to v3 RoleCode ConceptMap
|
|
2102
|
+
* this library does not carry).
|
|
2103
|
+
*
|
|
2104
|
+
* @packageDocumentation
|
|
2105
|
+
*/
|
|
2106
|
+
|
|
2107
|
+
/**
|
|
2108
|
+
* The canonical URL of the subscriber-id extension the IN1-10 and IN1-49 rows assign, transcribed
|
|
2109
|
+
* from the segment map's own `Coverage.extension.url` assignment.
|
|
2110
|
+
*
|
|
2111
|
+
* @example
|
|
2112
|
+
* ```ts
|
|
2113
|
+
* import { COVERAGE_SUBSCRIBER_ID_EXTENSION_URL } from "@cosyte/transform";
|
|
2114
|
+
* COVERAGE_SUBSCRIBER_ID_EXTENSION_URL.endsWith("extension-subscriberId"); // true
|
|
2115
|
+
* ```
|
|
2116
|
+
*/
|
|
2117
|
+
declare const COVERAGE_SUBSCRIBER_ID_EXTENSION_URL = "http://hl7.org/fhir/5.0/StructureDefinition/extension-subscriberId";
|
|
2118
|
+
/**
|
|
2119
|
+
* The `data-absent-reason` code carried by `Coverage.status`, the one FHIR-required element of this
|
|
2120
|
+
* resource that no published row of the segment map grounds.
|
|
2121
|
+
*
|
|
2122
|
+
* @example
|
|
2123
|
+
* ```ts
|
|
2124
|
+
* import { COVERAGE_STATUS_UNKNOWN } from "@cosyte/transform";
|
|
2125
|
+
* COVERAGE_STATUS_UNKNOWN; // "unknown"
|
|
2126
|
+
* ```
|
|
2127
|
+
*/
|
|
2128
|
+
declare const COVERAGE_STATUS_UNKNOWN = "unknown";
|
|
2129
|
+
/**
|
|
2130
|
+
* The Table 0203 identifier-type code the IN1-10 row conditions its subscriber-id assignment on
|
|
2131
|
+
* (`IF CX.5 IS "SN"`, Subscriber Number). Any other identifier type leaves that row unapplied.
|
|
2132
|
+
*
|
|
2133
|
+
* @example
|
|
2134
|
+
* ```ts
|
|
2135
|
+
* import { IN1_SUBSCRIBER_NUMBER_TYPE } from "@cosyte/transform";
|
|
2136
|
+
* IN1_SUBSCRIBER_NUMBER_TYPE; // "SN"
|
|
2137
|
+
* ```
|
|
2138
|
+
*/
|
|
2139
|
+
declare const IN1_SUBSCRIBER_NUMBER_TYPE = "SN";
|
|
2140
|
+
/**
|
|
2141
|
+
* Every `IN1` occurrence a message carries, in message order. One Coverage is created per
|
|
2142
|
+
* occurrence, per the IG message map's INSURANCE group.
|
|
2143
|
+
*
|
|
2144
|
+
* @param msg - The parsed `@cosyte/hl7` message.
|
|
2145
|
+
* @example
|
|
2146
|
+
* ```ts
|
|
2147
|
+
* import { parseHL7 } from "@cosyte/hl7";
|
|
2148
|
+
* // collectCoverages(parseHL7(raw)).length; // one entry per IN1 in the message
|
|
2149
|
+
* ```
|
|
2150
|
+
*/
|
|
2151
|
+
declare function collectCoverages(msg: Hl7Message): readonly Segment[];
|
|
2152
|
+
/**
|
|
2153
|
+
* The insurance company name an IN1 carries, read from XON.1 of IN1-4, or `""` when that component
|
|
2154
|
+
* carries nothing. The only row of the segment map that grounds the required `payor`.
|
|
2155
|
+
*
|
|
2156
|
+
* @param in1 - The `IN1` `@cosyte/hl7` `Segment`.
|
|
2157
|
+
* @example
|
|
2158
|
+
* ```ts
|
|
2159
|
+
* // insuranceCompanyName(in1); // the XON.1 organization name, verbatim
|
|
2160
|
+
* ```
|
|
2161
|
+
*/
|
|
2162
|
+
declare function insuranceCompanyName(in1: Segment): string;
|
|
2163
|
+
/**
|
|
2164
|
+
* The value-free diagnostics for the IN1 rows this library reads and deliberately does not build:
|
|
2165
|
+
* the payer address and the two policy-holder rows (each an Organization resource), the subscriber
|
|
2166
|
+
* (a Patient or RelatedPerson reference), and the relationship, whose value translation needs a
|
|
2167
|
+
* table this library does not carry.
|
|
2168
|
+
*
|
|
2169
|
+
* Raised for the occurrence whether or not the Coverage itself is emitted, so a deferred row a
|
|
2170
|
+
* message actually valued is in the issues list rather than silently absent.
|
|
2171
|
+
*
|
|
2172
|
+
* @param in1 - The `IN1` `@cosyte/hl7` `Segment`.
|
|
2173
|
+
* @example
|
|
2174
|
+
* ```ts
|
|
2175
|
+
* // deferredCoverageIssues(in1).length; // one per valued deferred row
|
|
2176
|
+
* ```
|
|
2177
|
+
*/
|
|
2178
|
+
declare function deferredCoverageIssues(in1: Segment): readonly TransformIssue[];
|
|
2179
|
+
/**
|
|
2180
|
+
* Build a FHIR `Coverage` resource node from one `IN1` occurrence, wired to the bundle's Patient.
|
|
2181
|
+
* Returns `{ value: undefined }` when IN1-4 names no insurance company, because `payor` is required
|
|
2182
|
+
* by R4 and no other row of the segment map grounds it.
|
|
2183
|
+
*
|
|
2184
|
+
* @param in1 - The `IN1` `@cosyte/hl7` `Segment`.
|
|
2185
|
+
* @param patientFullUrl - The bundle's Patient fullUrl, for `beneficiary` (required 1..1).
|
|
2186
|
+
* @param ctx - The transform context (naming-system registry + timezone policy).
|
|
2187
|
+
* @param v2Location - The occurrence's v2 location, for the withholding diagnostic (e.g. `IN1[0]`).
|
|
2188
|
+
* @example
|
|
2189
|
+
* ```ts
|
|
2190
|
+
* import { parseHL7 } from "@cosyte/hl7";
|
|
2191
|
+
* // const in1 = parseHL7(raw).segments("IN1")[0];
|
|
2192
|
+
* // const { value } = buildCoverage(in1!, "urn:uuid:pat", {}, "IN1[0]");
|
|
2193
|
+
* ```
|
|
2194
|
+
*/
|
|
2195
|
+
declare function buildCoverage(in1: Segment, patientFullUrl: string, ctx: TransformContext, v2Location: string): ConvertResult<FhirComplex>;
|
|
2196
|
+
|
|
2197
|
+
/**
|
|
2198
|
+
* Bundle-assembly primitives shared by the segment→resource builders: fresh `urn:uuid:` identities,
|
|
2199
|
+
* FHIR `Reference` nodes, and the small coding/data-absent helpers the message maps need.
|
|
2200
|
+
*
|
|
2201
|
+
* The identities are the wiring of the resource graph: a produced resource is registered under a
|
|
2202
|
+
* `urn:uuid:` `fullUrl`, and every intra-bundle reference (Encounter→subject→Patient,
|
|
2203
|
+
* RelatedPerson→patient→Patient, MessageHeader→focus→…) points at that same `fullUrl` so references
|
|
2204
|
+
* always resolve **within** the bundle. Identities come from {@link TransformOptions.generateId}
|
|
2205
|
+
* (default `crypto.randomUUID`) so output can be made reproducible for golden fixtures.
|
|
2206
|
+
*
|
|
2207
|
+
* @packageDocumentation
|
|
2208
|
+
*/
|
|
2209
|
+
|
|
2210
|
+
/** One EI field read as a FHIR `Identifier`: the value, and whether an authority went unused. */
|
|
2211
|
+
interface EntityIdentifier {
|
|
2212
|
+
/** The `Identifier` node built from EI.1, or `undefined` when EI.1 carries nothing. */
|
|
2213
|
+
readonly identifier: FhirComplex | undefined;
|
|
2214
|
+
/** Whether EI.2, EI.3 or EI.4 carried an assigning authority this library did not resolve. */
|
|
2215
|
+
readonly authorityValued: boolean;
|
|
2216
|
+
}
|
|
2217
|
+
/**
|
|
2218
|
+
* Read one EI (Entity Identifier) field as a FHIR `Identifier`, carrying EI.1 as
|
|
2219
|
+
* `Identifier.value` and nothing else. The same reading {@link orderIdentifier} applies to the
|
|
2220
|
+
* placer/filler order numbers, without the v2-0203 identifier type those rows assign.
|
|
2221
|
+
*
|
|
2222
|
+
* `Identifier.system` is deliberately left absent even when EI.2 to EI.4 name an assigning
|
|
2223
|
+
* authority: a system URI is never synthesized from a bare namespace, because two senders reusing
|
|
2224
|
+
* one namespace would otherwise collide. The caller is told the authority went unused through
|
|
2225
|
+
* {@link EntityIdentifier.authorityValued} so it can raise its own value-free diagnostic.
|
|
2226
|
+
*
|
|
2227
|
+
* @param field - The `EI` field, exactly as the parser published it.
|
|
2228
|
+
* @example
|
|
2229
|
+
* ```ts
|
|
2230
|
+
* import { parseHL7 } from "@cosyte/hl7";
|
|
2231
|
+
* // const dg1 = parseHL7(raw).segments("DG1")[0];
|
|
2232
|
+
* // toFhirEntityIdentifier(dg1!.field(20)).identifier; // { value: "<EI.1>" } or undefined
|
|
2233
|
+
* ```
|
|
2234
|
+
*/
|
|
2235
|
+
declare function toFhirEntityIdentifier(field: Field): EntityIdentifier;
|
|
2236
|
+
/**
|
|
2237
|
+
* The same `CodeableConcept` with its `text` replaced by `textValue`, so a segment row that targets
|
|
2238
|
+
* `code.text` directly can override the original text the datatype map read out of the coded field.
|
|
2239
|
+
*
|
|
2240
|
+
* @param concept - The `CodeableConcept` node the datatype converter produced.
|
|
2241
|
+
* @param textValue - The text the segment map's own row assigns.
|
|
2242
|
+
* @example
|
|
2243
|
+
* ```ts
|
|
2244
|
+
* // withCodeableText(concept, "Diabetes mellitus") -> the same codings, that text
|
|
2245
|
+
* ```
|
|
2246
|
+
*/
|
|
2247
|
+
declare function withCodeableText(concept: FhirComplex, textValue: string): FhirComplex;
|
|
2248
|
+
|
|
2249
|
+
/**
|
|
2250
|
+
* The `$translate`-shaped ConceptMap-application engine + the **license-clean** v2-table → FHIR
|
|
2251
|
+
* value maps it applies. This is the layer that value-**translates** a coded v2
|
|
2252
|
+
* field, not merely carrying its code structurally, but mapping the source table code to the FHIR
|
|
2253
|
+
* target coding the IG's segment-map `mappedVia` ConceptMap prescribes.
|
|
2254
|
+
*
|
|
2255
|
+
* **Grounded firsthand on the raw published IG ConceptMaps** (`hl7.fhir.uv.v2mappings`, STU1;
|
|
2256
|
+
* `ConceptMap-table-*.json`). Each shipped map below cites the exact IG ConceptMap it transcribes and
|
|
2257
|
+
* was verified against that resource's JSON (its `group.element[].target[]` rows and its `unmatched`
|
|
2258
|
+
* `(unmapped)` group). The engine is **fail-safe by refusal**: a source code the IG map has **no
|
|
2259
|
+
* target** for (its `(unmapped)` group) is never coerced to a plausible neighbor. The caller flags it
|
|
2260
|
+
* and preserves the raw coding, or withholds the value, but a target is **never fabricated**.
|
|
2261
|
+
*
|
|
2262
|
+
* Two of the maps below are consumed as **recognizers** rather than as translations, because their
|
|
2263
|
+
* IG rows are identity in the source code: {@link isRepeatPatternCode} (RPT.1 → `Timing.code`) and
|
|
2264
|
+
* {@link TIMING_EVENT_VALUE_MAP} (RPT.8 → `Timing.repeat.when`). What they decide is whether the
|
|
2265
|
+
* published map has a row at all, so the caller can carry the message's own code verbatim or
|
|
2266
|
+
* withhold the element; neither one ever rewrites a code into a different one.
|
|
2267
|
+
*
|
|
2268
|
+
* **License posture.** Only maps whose target CodeSystem is **freely redistributable** are
|
|
2269
|
+
* shipped here: the HL7 THO v2 tables (`v2-0162`, `v2-0550`, `v2-0277`, `v2-0161`, `v2-0127`,
|
|
2270
|
+
* `v2-0128`, `v2-0335`, `v2-0528`), the FHIR R4 core code systems (`allergy-intolerance-category`,
|
|
2271
|
+
* `allergy-intolerance-type`, `allergy-intolerance-criticality`) and HL7 v3
|
|
2272
|
+
* (`v3-RouteOfAdministration`, `v3-TimingEvent`) are HL7's own, license-clean. The IG's `*-to-sct`
|
|
2273
|
+
* value maps,
|
|
2274
|
+
* **RXR-4 method** (`table-hl70165-to-sct`) and **SCH-7 reason** (`table-hl70276-to-sct`), translate
|
|
2275
|
+
* **into SNOMED CT**, which is **license-encumbered and is NOT bundled**; those fields stay
|
|
2276
|
+
* structurally carried (BYO ConceptMap), never value-translated here (see their message modules).
|
|
2277
|
+
*
|
|
2278
|
+
* **Additive, never mutating.** A successful translation preserves the raw source coding
|
|
2279
|
+
* (its code, display, version, and any `CWE.4/5/6` alternate triplet) and *adds* the derived target
|
|
2280
|
+
* coding: recognition never overwrites or discards what the message said; it augments it with the
|
|
2281
|
+
* IG-mapped equivalent. And it only fires when the field's primary coding is genuinely from the map's
|
|
2282
|
+
* **bound source table** (CWE.3 absent, or naming that table): a field that declares a *foreign*
|
|
2283
|
+
* coding system (a local `99…`, SNOMED, …) is carried structurally and flagged, and its code is **never**
|
|
2284
|
+
* asserted to be the standard concept just because the string happens to collide with a table code.
|
|
2285
|
+
*
|
|
2286
|
+
* @packageDocumentation
|
|
2287
|
+
*/
|
|
2288
|
+
|
|
2289
|
+
/** HL7 v2 Table 0162 (Route of Administration) THO CodeSystem URI. */
|
|
2290
|
+
declare const V2_0162_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0162";
|
|
2291
|
+
/** HL7 v3 RouteOfAdministration CodeSystem URI (the IG's remap target for the six common routes). */
|
|
2292
|
+
declare const V3_ROUTE_OF_ADMINISTRATION_SYSTEM = "http://terminology.hl7.org/CodeSystem/v3-RouteOfAdministration";
|
|
2293
|
+
/** HL7 v2 Table 0550 (Body Parts) THO CodeSystem URI. */
|
|
2294
|
+
declare const V2_0550_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0550";
|
|
2295
|
+
/** HL7 v2 Table 0277 (Appointment Type Codes) THO CodeSystem URI. */
|
|
2296
|
+
declare const V2_0277_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0277";
|
|
2297
|
+
/** HL7 v2 Table 0161 (Allow Substitution) THO CodeSystem URI. */
|
|
2298
|
+
declare const V2_0161_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0161";
|
|
2299
|
+
/** HL7 v2 Table 0127 (Allergen Type) THO CodeSystem URI. */
|
|
2300
|
+
declare const V2_0127_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0127";
|
|
2301
|
+
/** HL7 v2 Table 0128 (Allergy Severity) THO CodeSystem URI. */
|
|
2302
|
+
declare const V2_0128_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0128";
|
|
2303
|
+
/** FHIR R4 `AllergyIntoleranceCategory` CodeSystem URI (the IG's AL1-2 category target). */
|
|
2304
|
+
declare const ALLERGY_INTOLERANCE_CATEGORY_SYSTEM = "http://hl7.org/fhir/allergy-intolerance-category";
|
|
2305
|
+
/** FHIR R4 `AllergyIntoleranceType` CodeSystem URI (the IG's AL1-2 type target). */
|
|
2306
|
+
declare const ALLERGY_INTOLERANCE_TYPE_SYSTEM = "http://hl7.org/fhir/allergy-intolerance-type";
|
|
2307
|
+
/** FHIR R4 `AllergyIntoleranceCriticality` CodeSystem URI (the IG's AL1-4 criticality target). */
|
|
2308
|
+
declare const ALLERGY_INTOLERANCE_CRITICALITY_SYSTEM = "http://hl7.org/fhir/allergy-intolerance-criticality";
|
|
2309
|
+
/** A translated FHIR target coding: the target CodeSystem URI + code, and the IG's target display. */
|
|
2310
|
+
interface CodedTarget {
|
|
2311
|
+
/** The FHIR target CodeSystem canonical URI. */
|
|
2312
|
+
readonly system: string;
|
|
2313
|
+
/** The FHIR target code. */
|
|
2314
|
+
readonly code: string;
|
|
2315
|
+
/** The IG map's target display, when it carries one (absent for the identity table maps). */
|
|
2316
|
+
readonly display?: string;
|
|
2317
|
+
}
|
|
2318
|
+
/**
|
|
2319
|
+
* A license-clean v2-table → FHIR value ConceptMap: it translates a **source table code** to its
|
|
2320
|
+
* {@link CodedTarget}, or `undefined` when the code is not in the map's *mapped* group (the IG's
|
|
2321
|
+
* `(unmapped)` group). Never fabricates a target.
|
|
2322
|
+
*/
|
|
2323
|
+
interface CodedValueMap {
|
|
2324
|
+
/** The IG ConceptMap id this map transcribes (its citation). */
|
|
2325
|
+
readonly name: string;
|
|
2326
|
+
/** The source table's canonical CodeSystem URI (what the raw v2 code is a member of when mapped). */
|
|
2327
|
+
readonly sourceSystem: string;
|
|
2328
|
+
/**
|
|
2329
|
+
* The CWE.3 (`nameOfCodingSystem`) mnemonics that denote this map's **bound source table**: the
|
|
2330
|
+
* HL7 forms (`"HL70162"`, `"0162"`, …). Translation applies **only** when the field's primary
|
|
2331
|
+
* coding is from the bound table: CWE.3 absent/empty (a positionally-bound bare code) **or** CWE.3 ∈
|
|
2332
|
+
* this set. A CWE that declares a **different** coding system (a local `99…`, or SNOMED) is not a
|
|
2333
|
+
* source-table code, so the map is not applied to it: the raw coding is carried structurally + its
|
|
2334
|
+
* system flagged, never asserted as the standard concept.
|
|
2335
|
+
*/
|
|
2336
|
+
readonly sourceMnemonics: ReadonlySet<string>;
|
|
2337
|
+
/** Translate a source table code to its FHIR target, or `undefined` when the IG map has no target. */
|
|
2338
|
+
readonly translate: (code: string) => CodedTarget | undefined;
|
|
1148
2339
|
}
|
|
1149
2340
|
/**
|
|
1150
2341
|
* **RXR-1 Route**: IG `ConceptMap/table-hl70162-to-v2-0162`. Two IG groups, transcribed verbatim:
|
|
@@ -1178,6 +2369,111 @@ declare const APPOINTMENT_TYPE_VALUE_MAP: CodedValueMap;
|
|
|
1178
2369
|
* substitution is withheld + flagged, never a fabricated substitution permission.
|
|
1179
2370
|
*/
|
|
1180
2371
|
declare const SUBSTITUTION_VALUE_MAP: CodedValueMap;
|
|
2372
|
+
/**
|
|
2373
|
+
* The published version of the HL7 Version 2 to FHIR Implementation Guide the five **AL1 allergy**
|
|
2374
|
+
* value maps below were transcribed from.
|
|
2375
|
+
*
|
|
2376
|
+
* Recorded per map family rather than once for the file, because the earlier value maps here were
|
|
2377
|
+
* transcribed on their own date and this constant must not backdate or forward-date them.
|
|
2378
|
+
*
|
|
2379
|
+
* @example
|
|
2380
|
+
* ```ts
|
|
2381
|
+
* import { IG_ALLERGY_VALUE_MAPS_VERSION } from "@cosyte/transform";
|
|
2382
|
+
* IG_ALLERGY_VALUE_MAPS_VERSION; // "1.0.0"
|
|
2383
|
+
* ```
|
|
2384
|
+
*/
|
|
2385
|
+
declare const IG_ALLERGY_VALUE_MAPS_VERSION = "1.0.0";
|
|
2386
|
+
/**
|
|
2387
|
+
* The publication date of {@link IG_ALLERGY_VALUE_MAPS_VERSION}, so a later release of the guide can
|
|
2388
|
+
* be told apart from a defect in this library.
|
|
2389
|
+
*
|
|
2390
|
+
* @example
|
|
2391
|
+
* ```ts
|
|
2392
|
+
* import { IG_ALLERGY_VALUE_MAPS_PUBLISHED } from "@cosyte/transform";
|
|
2393
|
+
* IG_ALLERGY_VALUE_MAPS_PUBLISHED; // "2025-10-07"
|
|
2394
|
+
* ```
|
|
2395
|
+
*/
|
|
2396
|
+
declare const IG_ALLERGY_VALUE_MAPS_PUBLISHED = "2025-10-07";
|
|
2397
|
+
/**
|
|
2398
|
+
* The date the five AL1 allergy ConceptMaps were retrieved and transcribed into the maps below. A
|
|
2399
|
+
* target that surprises a reader is answered by re-reading the guide at this date: each map is a
|
|
2400
|
+
* transcription of a published document at a moment, not a live query.
|
|
2401
|
+
*
|
|
2402
|
+
* @example
|
|
2403
|
+
* ```ts
|
|
2404
|
+
* import { IG_ALLERGY_VALUE_MAPS_RETRIEVED } from "@cosyte/transform";
|
|
2405
|
+
* IG_ALLERGY_VALUE_MAPS_RETRIEVED; // "2026-08-28"
|
|
2406
|
+
* ```
|
|
2407
|
+
*/
|
|
2408
|
+
declare const IG_ALLERGY_VALUE_MAPS_RETRIEVED = "2026-08-28";
|
|
2409
|
+
/**
|
|
2410
|
+
* The IG segment map the five AL1 allergy value maps hang off, so a reader can audit any row: it is
|
|
2411
|
+
* the page that names which vocabulary map applies to which AL1 field.
|
|
2412
|
+
*
|
|
2413
|
+
* @example
|
|
2414
|
+
* ```ts
|
|
2415
|
+
* import { IG_ALLERGY_VALUE_MAPS_SOURCE } from "@cosyte/transform";
|
|
2416
|
+
* IG_ALLERGY_VALUE_MAPS_SOURCE.startsWith("https://"); // true
|
|
2417
|
+
* ```
|
|
2418
|
+
*/
|
|
2419
|
+
declare const IG_ALLERGY_VALUE_MAPS_SOURCE = "https://hl7.org/fhir/uv/v2mappings/ConceptMap-segment-al1-to-allergyintolerance.html";
|
|
2420
|
+
/**
|
|
2421
|
+
* **AL1-2 Allergen Type to category**: IG `ConceptMap/table-hl70127-to-allergy-intolerance-category`.
|
|
2422
|
+
* One **mapped** group of six rows into the FHIR R4 `AllergyIntoleranceCategory` code system (`DA`
|
|
2423
|
+
* drug to `medication`, `FA` food to `food`, `EA` environmental to `environment`, `AA` animal to
|
|
2424
|
+
* `biologic`, `PA` plant to `environment`, `LA` pollen to `environment`, each `equivalent`, with the
|
|
2425
|
+
* IG target displays), and an `(unmapped)` group of two: **`MA` and `MC` have no category target**.
|
|
2426
|
+
*
|
|
2427
|
+
* The IG applies this map to `AllergyIntolerance.category` **only**; `AllergyIntolerance.type` has
|
|
2428
|
+
* its own map ({@link ALLERGY_TYPE_VALUE_MAP}) over the same source table, and the two are resolved
|
|
2429
|
+
* independently. `MA` therefore yields a type and no category, which is the guide's answer and not a
|
|
2430
|
+
* gap to be filled: a category is never borrowed from the type map or from a neighbouring code.
|
|
2431
|
+
*/
|
|
2432
|
+
declare const ALLERGY_CATEGORY_VALUE_MAP: CodedValueMap;
|
|
2433
|
+
/**
|
|
2434
|
+
* **AL1-2 Allergen Type to type**: IG `ConceptMap/table-hl70127-to-allergy-intolerance-type`. One
|
|
2435
|
+
* **mapped** group of seven rows into the FHIR R4 `AllergyIntoleranceType` code system (`DA`, `FA`,
|
|
2436
|
+
* `MA`, `EA`, `AA`, `PA`, `LA` all to `allergy`, each `equivalent`, display `Allergy`), and an
|
|
2437
|
+
* `(unmapped)` group of one: **`MC` miscellaneous contraindication has no type target**.
|
|
2438
|
+
*
|
|
2439
|
+
* Read beside {@link ALLERGY_CATEGORY_VALUE_MAP}: the guide publishes two maps over Table 0127 with
|
|
2440
|
+
* different unmapped sets, so a code can be mapped by one and not the other. `MC` is the only code
|
|
2441
|
+
* neither map carries.
|
|
2442
|
+
*/
|
|
2443
|
+
declare const ALLERGY_TYPE_VALUE_MAP: CodedValueMap;
|
|
2444
|
+
/**
|
|
2445
|
+
* **AL1-4 Allergy Severity to criticality**: IG
|
|
2446
|
+
* `ConceptMap/table-hl70128-to-allergy-intolerance-criticality`. One **mapped** group of two rows
|
|
2447
|
+
* into the FHIR R4 `AllergyIntoleranceCriticality` code system (`SV` severe to `high`, `MI` mild to
|
|
2448
|
+
* `low`, both `equivalent`, with the IG target displays), and an `(unmapped)` group of two: **`MO`
|
|
2449
|
+
* moderate and `U` unknown have no criticality target**.
|
|
2450
|
+
*
|
|
2451
|
+
* The unmapped pair is the whole reason this field fails safe. `criticality` is an overall risk
|
|
2452
|
+
* assessment a prescriber acts on, so a moderate severity is left absent and flagged rather than
|
|
2453
|
+
* rounded to `low` or `high`, and `AllergyIntolerance.reaction.severity` is never populated from the
|
|
2454
|
+
* same component instead: the IG offers that only as a local variation, conditioned on a severity
|
|
2455
|
+
* that was **not** used equivalently to criticality, which no v2 message states.
|
|
2456
|
+
*/
|
|
2457
|
+
declare const ALLERGY_CRITICALITY_VALUE_MAP: CodedValueMap;
|
|
2458
|
+
/**
|
|
2459
|
+
* **AL1-2 original code**: IG `ConceptMap/table-hl70127-to-v2-0127`. A single **identity** group of
|
|
2460
|
+
* all eight Table 0127 codes (`DA`, `FA`, `MA`, `MC`, `EA`, `AA`, `PA`, `LA` to themselves, each
|
|
2461
|
+
* `equivalent`, source and target both `v2-0127`).
|
|
2462
|
+
*
|
|
2463
|
+
* This is the map behind the `alternate-codes` extension the IG puts on `category`: whatever the
|
|
2464
|
+
* category map does or does not translate, the code the sender actually wrote survives on the
|
|
2465
|
+
* resource in its own code system, so a consumer can always see what the message said.
|
|
2466
|
+
*/
|
|
2467
|
+
declare const ALLERGY_ORIGINAL_CATEGORY_VALUE_MAP: CodedValueMap;
|
|
2468
|
+
/**
|
|
2469
|
+
* **AL1-4 original code**: IG `ConceptMap/table-hl70128-original-to-v2-0128`. A single **identity**
|
|
2470
|
+
* group of all four Table 0128 codes (`SV`, `MO`, `MI`, `U` to themselves, each `equivalent`, source
|
|
2471
|
+
* and target both `v2-0128`), the map behind the `alternate-codes` extension on `criticality`.
|
|
2472
|
+
*
|
|
2473
|
+
* Its four codes are a superset of the two {@link ALLERGY_CRITICALITY_VALUE_MAP} translates, which
|
|
2474
|
+
* is the point: a `MO` that yields no criticality still yields the original severity code here.
|
|
2475
|
+
*/
|
|
2476
|
+
declare const ALLERGY_ORIGINAL_CRITICALITY_VALUE_MAP: CodedValueMap;
|
|
1181
2477
|
/**
|
|
1182
2478
|
* The bound-table target for a coded element's **primary** coding, or `undefined` when the map does
|
|
1183
2479
|
* not faithfully apply, i.e. the primary declares a coding system that is **not** this map's bound
|
|
@@ -1242,6 +2538,308 @@ declare function codeableConceptFromTarget(target: CodedTarget, text?: string):
|
|
|
1242
2538
|
*/
|
|
1243
2539
|
declare function toFhirCodeableConceptVia(cwe: CodedElement, map: CodedValueMap, ctx?: TransformContext): ConvertResult<FhirComplex>;
|
|
1244
2540
|
|
|
2541
|
+
/**
|
|
2542
|
+
* Writing HL7 v2 field content: the small, safe primitives the reverse (FHIR to v2) maps build on.
|
|
2543
|
+
*
|
|
2544
|
+
* Two disciplines live here.
|
|
2545
|
+
*
|
|
2546
|
+
* **Composite content is structured, never concatenated.** A typed composite (XPN, CX, CWE, XAD) is
|
|
2547
|
+
* handed to `@cosyte/hl7` as a `RawField` of components, so the serializer owns delimiter escaping:
|
|
2548
|
+
* a family name of `Do^e` emits as `Do\S\e` and reads back as `Do^e`, where a hand-built `"Do^e"`
|
|
2549
|
+
* string would have silently become two components. Nothing in this module writes a `^` or a `~`.
|
|
2550
|
+
*
|
|
2551
|
+
* **A lexical form v2 cannot carry is refused, never trimmed to fit.** {@link v2Timestamp} and
|
|
2552
|
+
* {@link v2Number} return `undefined` rather than an approximation, and their callers flag the value
|
|
2553
|
+
* and leave the v2 field absent.
|
|
2554
|
+
*
|
|
2555
|
+
* @packageDocumentation
|
|
2556
|
+
*/
|
|
2557
|
+
|
|
2558
|
+
/**
|
|
2559
|
+
* Invert a forward v2-to-FHIR code map, keeping **only** the targets exactly one v2 code maps to.
|
|
2560
|
+
*
|
|
2561
|
+
* This is the bijective-subset rule as code. Where several v2 codes are "equivalent to" one FHIR
|
|
2562
|
+
* concept (Table 0001 `O`/`A`/`N` all map to `other`), the inverse is ambiguous: the target is
|
|
2563
|
+
* dropped here, and its caller flags `TRANSFORM_CODE_NOT_INVERTIBLE` rather than picking one member
|
|
2564
|
+
* of the set.
|
|
2565
|
+
*
|
|
2566
|
+
* @param forward - A v2 code to FHIR code map, as published by the IG ConceptMap.
|
|
2567
|
+
* @example
|
|
2568
|
+
* ```ts
|
|
2569
|
+
* import { invertCodeMap } from "@cosyte/transform";
|
|
2570
|
+
* invertCodeMap({ F: "female", O: "other", A: "other" }); // => { female: "F" }
|
|
2571
|
+
* ```
|
|
2572
|
+
*/
|
|
2573
|
+
declare function invertCodeMap(forward: Readonly<Record<string, string>>): Readonly<Record<string, string>>;
|
|
2574
|
+
|
|
2575
|
+
/**
|
|
2576
|
+
* Codes and coding systems on the way out to v2, and the caller-supplied context that resolution
|
|
2577
|
+
* needs.
|
|
2578
|
+
*
|
|
2579
|
+
* A v2 coded field names its vocabulary with a Table 0396 mnemonic (`LN`, `SCT`, `UCUM`), so writing
|
|
2580
|
+
* a FHIR `Coding` into one means turning a canonical system URI back into that mnemonic. There is no
|
|
2581
|
+
* algorithm for that, only a registry, so this module inverts the same seed the forward direction
|
|
2582
|
+
* resolves through ({@link DEFAULT_V2_CODE_SYSTEMS} plus whatever the caller adds) and **refuses
|
|
2583
|
+
* everything else**: an unrecognized system is flagged, never written into a coded field with no
|
|
2584
|
+
* table context and never re-coded into a neighbouring table.
|
|
2585
|
+
*
|
|
2586
|
+
* The inversion keeps only URIs exactly one mnemonic names, for the same reason the code-map
|
|
2587
|
+
* inversion does: two mnemonics for one URI is an ambiguity, not a choice to make silently.
|
|
2588
|
+
*
|
|
2589
|
+
* @packageDocumentation
|
|
2590
|
+
*/
|
|
2591
|
+
|
|
2592
|
+
/**
|
|
2593
|
+
* Caller context for a reverse (FHIR to v2) conversion. Every entry is **caller-vetted**: nothing
|
|
2594
|
+
* here is derived from resource content, and omitting all of it is safe (the conversion then flags
|
|
2595
|
+
* what it cannot resolve rather than guessing it).
|
|
2596
|
+
*
|
|
2597
|
+
* @example
|
|
2598
|
+
* ```ts
|
|
2599
|
+
* import { toV2Patient } from "@cosyte/transform";
|
|
2600
|
+
* const options = {
|
|
2601
|
+
* assigningAuthorities: { "urn:oid:1.2.840.114350": "HOSP" },
|
|
2602
|
+
* envelope: { sendingApp: "EHR", sendingFacility: "MAIN" },
|
|
2603
|
+
* };
|
|
2604
|
+
* void options;
|
|
2605
|
+
* void toV2Patient;
|
|
2606
|
+
* ```
|
|
2607
|
+
*/
|
|
2608
|
+
interface ReverseOptions {
|
|
2609
|
+
/**
|
|
2610
|
+
* Extra v2 coding-system mnemonic to canonical URI entries, merged over
|
|
2611
|
+
* {@link DEFAULT_V2_CODE_SYSTEMS} and then inverted. Same shape and direction as the forward
|
|
2612
|
+
* registry's seed, so one declaration serves both directions.
|
|
2613
|
+
*/
|
|
2614
|
+
readonly codeSystems?: Readonly<Record<string, string>>;
|
|
2615
|
+
/**
|
|
2616
|
+
* `Identifier.system` URI to the v2 assigning-authority namespace (HD.1) that stands for it. There
|
|
2617
|
+
* is no derivation from a URI to a namespace, so an identifier whose system is absent here is
|
|
2618
|
+
* emitted with its value and **no** assigning authority, flagged rather than invented.
|
|
2619
|
+
*/
|
|
2620
|
+
readonly assigningAuthorities?: Readonly<Record<string, string>>;
|
|
2621
|
+
/**
|
|
2622
|
+
* MSH envelope fields for the emitted message (sending/receiving application and facility, control
|
|
2623
|
+
* id, timestamp, version, processing id). The message type is never taken from here: it is fixed
|
|
2624
|
+
* by the shape plus the caller's trigger argument.
|
|
2625
|
+
*/
|
|
2626
|
+
readonly envelope?: Omit<BuildMessageInit, "type">;
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
/**
|
|
2630
|
+
* The scaffolding every reverse (FHIR to v2) entry point shares: the required trigger, the resource
|
|
2631
|
+
* gate, and the message the mapped segment is carried in.
|
|
2632
|
+
*
|
|
2633
|
+
* **The trigger is always the caller's.** No FHIR resource carries an HL7 v2 message trigger: not
|
|
2634
|
+
* `Patient`, not `Observation`, not `Encounter`. So it is a required argument, and a missing, empty
|
|
2635
|
+
* or non-string one stops the conversion **before** any builder call, with
|
|
2636
|
+
* {@link ISSUE_CODES.TRANSFORM_MISSING_TRIGGER} on the ordinary `{ value, issues }` channel. It is
|
|
2637
|
+
* never defaulted, never derived from resource content, and never substituted with a placeholder.
|
|
2638
|
+
*
|
|
2639
|
+
* **What is emitted is a message, never a bare segment.** A segment on its own is not parseable HL7
|
|
2640
|
+
* (a v2 parser fatally rejects any input whose first segment is not `MSH`), so each shape builds a
|
|
2641
|
+
* complete message through `buildMessage` and appends its mapped segment to it. The trigger is used
|
|
2642
|
+
* verbatim as the trailing component of the fixed message code the shape itself owns.
|
|
2643
|
+
*
|
|
2644
|
+
* **What is missing is declared, not merely missing.** A FHIR resource carrying no source for a v2
|
|
2645
|
+
* field the segment's own attribute table marks *required* leaves that field absent, because the
|
|
2646
|
+
* alternative is inventing content for a clinical reader. Absent is the right wire; **silent** is
|
|
2647
|
+
* not, so each shape names its required fields ({@link ReverseShape.required}) and every one of them
|
|
2648
|
+
* that ends up unsourced raises {@link ISSUE_CODES.TRANSFORM_V2_REQUIRED_FIELD_ABSENT}. When nothing
|
|
2649
|
+
* at all grounded a field, there is no segment to be missing from and no message is built:
|
|
2650
|
+
* {@link ISSUE_CODES.TRANSFORM_NO_V2_MESSAGE_EMITTED} declares that outcome on its own, so an
|
|
2651
|
+
* empty-handed conversion never looks like a successful one.
|
|
2652
|
+
*
|
|
2653
|
+
* @packageDocumentation
|
|
2654
|
+
*/
|
|
2655
|
+
|
|
2656
|
+
/**
|
|
2657
|
+
* What a reverse conversion returns: the complete `@cosyte/hl7` message it could faithfully build
|
|
2658
|
+
* (or `undefined` when it could not), plus the value-free diagnostics it raised. The same fail-safe
|
|
2659
|
+
* envelope the forward converters return, in the other direction.
|
|
2660
|
+
*
|
|
2661
|
+
* @example
|
|
2662
|
+
* ```ts
|
|
2663
|
+
* import { toV2Patient } from "@cosyte/transform";
|
|
2664
|
+
* // const { value, issues } = toV2Patient(patientNode, "A28");
|
|
2665
|
+
* // value?.toString() -> "MSH|^~\\&|...|ADT^A28|...\rPID|||MRN1\r"
|
|
2666
|
+
* void toV2Patient;
|
|
2667
|
+
* ```
|
|
2668
|
+
*/
|
|
2669
|
+
type ReverseResult = ConvertResult<Hl7Message>;
|
|
2670
|
+
|
|
2671
|
+
/**
|
|
2672
|
+
* FHIR `Patient` to a v2 message carrying a `PID` segment: the inverse of the IG **Segment PID to
|
|
2673
|
+
* Patient** ConceptMap, over the rows whose inverse is defensible.
|
|
2674
|
+
*
|
|
2675
|
+
* | FHIR element | PID field | inverse |
|
|
2676
|
+
* |---|---|---|
|
|
2677
|
+
* | `Patient.identifier` | PID-3 (CX) | value to CX.1, caller-vetted authority to CX.4, Table 0203 type to CX.5 |
|
|
2678
|
+
* | `Patient.name` | PID-5 (XPN) | family/given/prefix/suffix to XPN.1-XPN.5, `use` to XPN.7 where the HL70200 map inverts |
|
|
2679
|
+
* | `Patient.birthDate` | PID-7 (DTM) | lexical date, precision preserved |
|
|
2680
|
+
* | `Patient.gender` | PID-8 | Table 0001 where the map inverts (`female`/`male`/`unknown`) |
|
|
2681
|
+
* | `Patient.address` | PID-11 (XAD) | line/city/state/postalCode/country/district, `use` to XAD.7 where HL70190 inverts |
|
|
2682
|
+
*
|
|
2683
|
+
* **This is the lossy direction, and it says so rather than papering over it.** The published map is
|
|
2684
|
+
* v2 to FHIR; several of its rows are many-to-one, so the inverse is ambiguous and is refused:
|
|
2685
|
+
* `gender` `other` (Table 0001 `O`, `A` and `N` all mean it), name use `official` (`L` and `R`) and
|
|
2686
|
+
* `temp` (`NAV` and `TEMP`), address use `work` (`B` and `O`), and every `Address.type`
|
|
2687
|
+
* (`M` and `SH` both mean `postal`). Each leaves its v2 field absent and raises
|
|
2688
|
+
* {@link ISSUE_CODES.TRANSFORM_CODE_NOT_INVERTIBLE}. Nothing here reconstructs a v2 message that was
|
|
2689
|
+
* transformed to FHIR: it writes what the FHIR resource itself supports, and flags the rest.
|
|
2690
|
+
*
|
|
2691
|
+
* @packageDocumentation
|
|
2692
|
+
*/
|
|
2693
|
+
|
|
2694
|
+
/**
|
|
2695
|
+
* FHIR `administrative-gender` to HL7 v2 Table 0001, the invertible rows of the IG's **HL70001 to
|
|
2696
|
+
* Administrative Gender** map. `other` is absent on purpose: three source codes carry it.
|
|
2697
|
+
*
|
|
2698
|
+
* @example
|
|
2699
|
+
* ```ts
|
|
2700
|
+
* import { GENDER_TO_V2 } from "@cosyte/transform";
|
|
2701
|
+
* GENDER_TO_V2["female"]; // => "F"
|
|
2702
|
+
* GENDER_TO_V2["other"]; // => undefined (ambiguous inverse, flagged instead)
|
|
2703
|
+
* ```
|
|
2704
|
+
*/
|
|
2705
|
+
declare const GENDER_TO_V2: Readonly<Record<string, string>>;
|
|
2706
|
+
/**
|
|
2707
|
+
* FHIR `name-use` to HL7 v2 Table 0200, the invertible rows of the IG's **HL70200 to name-use** map.
|
|
2708
|
+
* `official` and `temp` are absent on purpose: two source codes each.
|
|
2709
|
+
*
|
|
2710
|
+
* @example
|
|
2711
|
+
* ```ts
|
|
2712
|
+
* import { NAME_USE_TO_V2 } from "@cosyte/transform";
|
|
2713
|
+
* NAME_USE_TO_V2["maiden"]; // => "M"
|
|
2714
|
+
* ```
|
|
2715
|
+
*/
|
|
2716
|
+
declare const NAME_USE_TO_V2: Readonly<Record<string, string>>;
|
|
2717
|
+
/**
|
|
2718
|
+
* FHIR `address-use` to HL7 v2 Table 0190, the invertible rows of the IG's **HL70190 to
|
|
2719
|
+
* address-use** map. `work` is absent on purpose (`B` and `O`), as is every `Address.type` row.
|
|
2720
|
+
*
|
|
2721
|
+
* @example
|
|
2722
|
+
* ```ts
|
|
2723
|
+
* import { ADDRESS_USE_TO_V2 } from "@cosyte/transform";
|
|
2724
|
+
* ADDRESS_USE_TO_V2["home"]; // => "H"
|
|
2725
|
+
* ```
|
|
2726
|
+
*/
|
|
2727
|
+
declare const ADDRESS_USE_TO_V2: Readonly<Record<string, string>>;
|
|
2728
|
+
/**
|
|
2729
|
+
* Convert a FHIR R4 `Patient` into a **complete** v2 message carrying a `PID` segment, for the
|
|
2730
|
+
* demographics-only trigger events (`A28`, `A31`, `A29`, ...) that carry a patient with no visit.
|
|
2731
|
+
*
|
|
2732
|
+
* The `trigger` argument is **required and never inferred**: no `Patient` element maps to a v2
|
|
2733
|
+
* message trigger, so the caller supplies it, and a missing, empty or non-string one returns
|
|
2734
|
+
* `{ value: undefined }` plus {@link ISSUE_CODES.TRANSFORM_MISSING_TRIGGER} without calling the
|
|
2735
|
+
* message builder at all. It is used verbatim as MSH-9.2 under the `ADT` message code this shape
|
|
2736
|
+
* fixes; the caller never supplies the code and it never varies per call.
|
|
2737
|
+
*
|
|
2738
|
+
* Lossy by design and never round-trip-safe: a value the inverse of the IG map cannot ground is
|
|
2739
|
+
* flagged and left absent, never guessed, and a resource of another type is refused outright.
|
|
2740
|
+
*
|
|
2741
|
+
* A `PID` field v2 requires (PID-3 Patient Identifier List, PID-5 Patient Name) that this resource
|
|
2742
|
+
* gives no source for stays absent and is declared with
|
|
2743
|
+
* {@link ISSUE_CODES.TRANSFORM_V2_REQUIRED_FIELD_ABSENT}: the emitted message is honest about what
|
|
2744
|
+
* it lacks rather than padded to look conformant. A `Patient` that grounds no `PID` field at all
|
|
2745
|
+
* yields no message and {@link ISSUE_CODES.TRANSFORM_NO_V2_MESSAGE_EMITTED}.
|
|
2746
|
+
*
|
|
2747
|
+
* @param resource - The FHIR `Patient` node (build one with `@cosyte/fhir`'s `parseResource`).
|
|
2748
|
+
* @param trigger - The bare v2 trigger, e.g. `"A28"`. Required; never derived from the resource.
|
|
2749
|
+
* @param options - Caller-vetted reverse context: assigning authorities, code systems, MSH envelope.
|
|
2750
|
+
* @example
|
|
2751
|
+
* ```ts
|
|
2752
|
+
* import { parseResource } from "@cosyte/fhir";
|
|
2753
|
+
* import { toV2Patient } from "@cosyte/transform";
|
|
2754
|
+
*
|
|
2755
|
+
* const { resource } = parseResource('{"resourceType":"Patient","gender":"female"}');
|
|
2756
|
+
* const { value, issues } = toV2Patient(resource, "A28");
|
|
2757
|
+
* // value.toString() starts "MSH|^~\\&|" and carries "ADT^A28" in MSH-9, then a PID segment
|
|
2758
|
+
* void value;
|
|
2759
|
+
* void issues;
|
|
2760
|
+
* ```
|
|
2761
|
+
*/
|
|
2762
|
+
declare function toV2Patient(resource: FhirNode, trigger: string, options?: ReverseOptions): ReverseResult;
|
|
2763
|
+
|
|
2764
|
+
/**
|
|
2765
|
+
* FHIR `Observation` to a v2 message carrying an `OBX` segment: the inverse of the IG **Segment OBX
|
|
2766
|
+
* to Observation** ConceptMap, over the rows whose inverse is defensible.
|
|
2767
|
+
*
|
|
2768
|
+
* | FHIR element | OBX field | inverse |
|
|
2769
|
+
* |---|---|---|
|
|
2770
|
+
* | `Observation.code` | OBX-3 (CWE) | coding to CWE.1/2/3 (+ one alternate triplet), `text` to CWE.9 |
|
|
2771
|
+
* | `Observation.value[x]` | OBX-2 + OBX-5 | the value type OBX-2 declares is derived from which `value[x]` is present |
|
|
2772
|
+
* | `Observation.valueQuantity` units | OBX-6 (CWE) | UCUM code/display, other systems flagged |
|
|
2773
|
+
* | `Observation.referenceRange.text` | OBX-7 | carried verbatim, never composed from `low`/`high` |
|
|
2774
|
+
* | `Observation.interpretation` | OBX-8 | v3 ObservationInterpretation codes the HL70078 map carries |
|
|
2775
|
+
* | `Observation.status` | OBX-11 | Table 0085 where the map inverts |
|
|
2776
|
+
* | `Observation.effectiveDateTime` | OBX-14 | lexical timestamp, precision preserved |
|
|
2777
|
+
*
|
|
2778
|
+
* **OBX-2 is derived, never assumed.** `valueQuantity` writes `NM` (or `SN` when the quantity
|
|
2779
|
+
* carries a comparator, which is the only OBX-2 type that can hold one), `valueCodeableConcept`
|
|
2780
|
+
* writes `CWE`, `valueString` writes `ST`, `valueDateTime` writes `DTM`. A `value[x]` with no
|
|
2781
|
+
* faithful OBX-5 form (`valueRange`, `valueRatio`, `valueBoolean`, ...) writes **nothing** and is
|
|
2782
|
+
* flagged: emitting the magnitude of a comparator-bearing or ranged value as a bare number would be
|
|
2783
|
+
* a confidently wrong result, which is the one thing this library never does.
|
|
2784
|
+
*
|
|
2785
|
+
* `Observation.subject` and `.encounter` are flagged rather than emitted: this shape's input is an
|
|
2786
|
+
* `Observation` alone, and a PID or PV1 assembled from a bare reference would be fabricated.
|
|
2787
|
+
*
|
|
2788
|
+
* @packageDocumentation
|
|
2789
|
+
*/
|
|
2790
|
+
|
|
2791
|
+
/**
|
|
2792
|
+
* FHIR `observation-status` to HL7 v2 Table 0085, the invertible rows of the IG's **HL70085 to
|
|
2793
|
+
* Observation Status** map. `entered-in-error` is absent on purpose (`D` and `W` both carry it), as
|
|
2794
|
+
* are the FHIR statuses the map never targets (`registered`, `unknown`).
|
|
2795
|
+
*
|
|
2796
|
+
* @example
|
|
2797
|
+
* ```ts
|
|
2798
|
+
* import { OBSERVATION_STATUS_TO_V2 } from "@cosyte/transform";
|
|
2799
|
+
* OBSERVATION_STATUS_TO_V2["corrected"]; // => "C"
|
|
2800
|
+
* OBSERVATION_STATUS_TO_V2["entered-in-error"]; // => undefined (ambiguous inverse, flagged instead)
|
|
2801
|
+
* ```
|
|
2802
|
+
*/
|
|
2803
|
+
declare const OBSERVATION_STATUS_TO_V2: Readonly<Record<string, string>>;
|
|
2804
|
+
/**
|
|
2805
|
+
* Convert a FHIR R4 `Observation` into a **complete** v2 message carrying an `OBX` segment, in the
|
|
2806
|
+
* result-report (`ORU`) shape.
|
|
2807
|
+
*
|
|
2808
|
+
* The `trigger` argument is **required and never inferred**: no `Observation` element maps to a v2
|
|
2809
|
+
* message trigger, so the caller supplies it, and a missing, empty or non-string one returns
|
|
2810
|
+
* `{ value: undefined }` plus {@link ISSUE_CODES.TRANSFORM_MISSING_TRIGGER} without calling the
|
|
2811
|
+
* message builder at all. It is used verbatim as MSH-9.2 under the `ORU` message code this shape
|
|
2812
|
+
* fixes.
|
|
2813
|
+
*
|
|
2814
|
+
* The message carries the `OBX` alone: this shape's input is an `Observation`, which names no
|
|
2815
|
+
* patient, and a subject segment assembled from a reference would be fabricated. Lossy by design and
|
|
2816
|
+
* never round-trip-safe.
|
|
2817
|
+
*
|
|
2818
|
+
* An `OBX` field v2 requires (OBX-11 Observation Result Status) that this resource gives no source
|
|
2819
|
+
* for stays absent and is declared with {@link ISSUE_CODES.TRANSFORM_V2_REQUIRED_FIELD_ABSENT},
|
|
2820
|
+
* rather than defaulted to `F`: a result reported as final that the sender never called final is the
|
|
2821
|
+
* confidently wrong value this library exists to refuse. An `Observation` that grounds no `OBX`
|
|
2822
|
+
* field at all yields no message and {@link ISSUE_CODES.TRANSFORM_NO_V2_MESSAGE_EMITTED}.
|
|
2823
|
+
*
|
|
2824
|
+
* @param resource - The FHIR `Observation` node.
|
|
2825
|
+
* @param trigger - The bare v2 trigger, e.g. `"R01"`. Required; never derived from the resource.
|
|
2826
|
+
* @param options - Caller-vetted reverse context: code systems and the MSH envelope.
|
|
2827
|
+
* @example
|
|
2828
|
+
* ```ts
|
|
2829
|
+
* import { parseResource } from "@cosyte/fhir";
|
|
2830
|
+
* import { toV2Observation } from "@cosyte/transform";
|
|
2831
|
+
*
|
|
2832
|
+
* const { resource } = parseResource(
|
|
2833
|
+
* '{"resourceType":"Observation","status":"final","code":{"coding":[{"system":"http://loinc.org","code":"789-8"}]}}',
|
|
2834
|
+
* );
|
|
2835
|
+
* const { value, issues } = toV2Observation(resource, "R01");
|
|
2836
|
+
* // value.toString() carries "ORU^R01" in MSH-9, then an OBX whose OBX-3 is "789-8^^LN"
|
|
2837
|
+
* void value;
|
|
2838
|
+
* void issues;
|
|
2839
|
+
* ```
|
|
2840
|
+
*/
|
|
2841
|
+
declare function toV2Observation(resource: FhirNode, trigger: string, options?: ReverseOptions): ReverseResult;
|
|
2842
|
+
|
|
1245
2843
|
/**
|
|
1246
2844
|
* Public entry point for `@cosyte/transform`: the HL7 v2 → FHIR R4 transformation library.
|
|
1247
2845
|
*
|
|
@@ -1260,8 +2858,15 @@ declare function toFhirCodeableConceptVia(cwe: CodedElement, map: CodedValueMap,
|
|
|
1260
2858
|
* all through `toFhir(msg)`), and the **terminology value-translation** layer: a
|
|
1261
2859
|
* `$translate`-shaped {@link toFhirCodeableConceptVia} engine applying the license-clean IG value
|
|
1262
2860
|
* ConceptMaps to the previously structural-only coded fields (route/site, appointment type, order
|
|
1263
|
-
* priority, substitution).
|
|
1264
|
-
*
|
|
2861
|
+
* priority, substitution).
|
|
2862
|
+
*
|
|
2863
|
+
* It also ships a **narrow reverse path**, FHIR → v2: {@link toV2Patient} and
|
|
2864
|
+
* {@link toV2Observation} emit a complete v2 message carrying a `PID` or an `OBX` built from the
|
|
2865
|
+
* subset of the IG segment maps whose inverse is defensible. Each requires the caller to supply the
|
|
2866
|
+
* v2 trigger, because no FHIR resource carries one. It is **lossy by design and not a round-trip**:
|
|
2867
|
+
* a value the inverse of the IG map cannot ground is flagged and left absent, never guessed.
|
|
2868
|
+
* Terminology **depth beyond these maps**, profiles, and any wider FHIR → v2 conversion are not
|
|
2869
|
+
* implemented.
|
|
1265
2870
|
*
|
|
1266
2871
|
* @packageDocumentation
|
|
1267
2872
|
*/
|
|
@@ -1282,4 +2887,4 @@ declare function toFhirCodeableConceptVia(cwe: CodedElement, map: CodedValueMap,
|
|
|
1282
2887
|
*/
|
|
1283
2888
|
declare const VERSION: string;
|
|
1284
2889
|
|
|
1285
|
-
export { ADDRESS_TYPE_MAP, ADDRESS_USE_MAP, ADMINISTRATIVE_GENDER_MAP, APPOINTMENT_STATUS_MAP, APPOINTMENT_TYPE_VALUE_MAP, type CodedElement, type CodedTarget, type CodedValueMap, type ConvertResult, DEFAULT_V2_CODE_SYSTEMS, DIAGNOSTIC_REPORT_STATUS_MAP, ENCOUNTER_CLASS_V3_MAP, ENCOUNTER_STATUS_MAP, FATAL_CODES, type FatalCode, HL70078_INTERPRETATION_CODES, IG_MAPPED_ADT_TRIGGERS, IG_MAPPED_APPOINTMENT_TRIGGERS, IG_MAPPED_DOCUMENT_TRIGGERS, IG_MAPPED_IMMUNIZATION_TRIGGERS, IG_MAPPED_ORDER_TRIGGERS, IG_MAPPED_ORU_TRIGGERS, IMMUNIZATION_STATUS_MAP, ISSUE_CODES, ISSUE_REGISTRY, type IssueCode, NAME_USE_MAP, type NamingSystemRegistry, type NamingSystemSeed, OBSERVATION_STATUS_MAP, REQUEST_STATUS_MAP, ROUTE_VALUE_MAP, SERVICE_REQUEST_PRIORITY_MAP, SITE_VALUE_MAP, SUBSTITUTION_VALUE_MAP, TRANSFORM_ISSUE_SYSTEM, type TransformContext, type TransformIssue, type TransformOptions, type TransformResult, type TransformSeverity, V2_0161_SYSTEM, V2_0162_SYSTEM, V2_0203_SYSTEM, V2_0277_SYSTEM, V2_0550_SYSTEM, V3_ROUTE_OF_ADMINISTRATION_SYSTEM, VERSION, codeableConceptFromTarget, createNamingSystem, fhirIssueTypeFor, issue, toFhir, toFhirAddress, toFhirCodeableConcept, toFhirCodeableConceptVia, toFhirDateTime, toFhirHumanName, toFhirIdentifier, toFhirQuantity, toOperationOutcome, translateBound };
|
|
2890
|
+
export { ADDRESS_TYPE_MAP, ADDRESS_USE_MAP, ADDRESS_USE_TO_V2, ADMINISTRATIVE_GENDER_MAP, AL1_ONSET_WITHDRAWN_AT, ALLERGY_CATEGORY_VALUE_MAP, ALLERGY_CLINICAL_STATUS_CODE, ALLERGY_CLINICAL_STATUS_SYSTEM, ALLERGY_CRITICALITY_VALUE_MAP, ALLERGY_INTOLERANCE_CATEGORY_SYSTEM, ALLERGY_INTOLERANCE_CRITICALITY_SYSTEM, ALLERGY_INTOLERANCE_TYPE_SYSTEM, ALLERGY_ORIGINAL_CATEGORY_VALUE_MAP, ALLERGY_ORIGINAL_CRITICALITY_VALUE_MAP, ALLERGY_TYPE_VALUE_MAP, ALTERNATE_CODES_EXTENSION_URL, APPOINTMENT_STATUS_MAP, APPOINTMENT_TYPE_VALUE_MAP, type AllergyBuildOptions, CONDITION_ENTERED_IN_ERROR, CONDITION_VERIFICATION_STATUS_SYSTEM, COVERAGE_STATUS_UNKNOWN, COVERAGE_SUBSCRIBER_ID_EXTENSION_URL, type CodedElement, type CodedTarget, type CodedValueMap, type ConvertResult, DEFAULT_V2_CODE_SYSTEMS, DG1_RETRACTION_ACTION_CODE, DIAGNOSTIC_REPORT_STATUS_MAP, ED_BASE64_ENCODING, ENCOUNTER_CLASS_V3_MAP, ENCOUNTER_STATUS_MAP, type EntityIdentifier, FATAL_CODES, type FatalCode, GENDER_TO_V2, HL70078_INTERPRETATION_CODES, IG_ALLERGY_VALUE_MAPS_PUBLISHED, IG_ALLERGY_VALUE_MAPS_RETRIEVED, IG_ALLERGY_VALUE_MAPS_SOURCE, IG_ALLERGY_VALUE_MAPS_VERSION, IG_MAPPED_ADT_TRIGGERS, IG_MAPPED_APPOINTMENT_TRIGGERS, IG_MAPPED_DOCUMENT_TRIGGERS, IG_MAPPED_IMMUNIZATION_TRIGGERS, IG_MAPPED_ORDER_TRIGGERS, IG_MAPPED_ORU_TRIGGERS, IG_MAPPED_SEGMENT_NAMES, IG_SEGMENT_MAPS_PUBLISHED, IG_SEGMENT_MAPS_RETRIEVED, IG_SEGMENT_MAPS_SOURCE, IG_SEGMENT_MAPS_VERSION, IMMUNIZATION_STATUS_MAP, IN1_SUBSCRIBER_NUMBER_TYPE, ISSUE_CODES, ISSUE_REGISTRY, type IssueCode, NAME_USE_MAP, NAME_USE_TO_V2, type NamingSystemRegistry, type NamingSystemSeed, OBSERVATION_STATUS_MAP, OBSERVATION_STATUS_TO_V2, OBSERVATION_VALUE_ATTACHMENT_EXTENSION_URL, PROCEDURE_STATUS_UNKNOWN, REQUEST_STATUS_MAP, ROUTE_VALUE_MAP, type ReverseOptions, type ReverseResult, SAMPLED_DATA_ABSENT_POINT, SAMPLED_DATA_UNGROUNDED, SEGMENT_IDENTIFIER_SHAPE, SERVICE_REQUEST_PRIORITY_MAP, SITE_VALUE_MAP, SPM_SHIPMENT_IDENTIFIER_TYPE, SUBSTITUTION_VALUE_MAP, type SegmentOccurrence, SegmentReachLedger, TRANSFORM_ISSUE_SYSTEM, type TransformContext, type TransformIssue, type TransformOptions, type TransformResult, type TransformSeverity, V2_0127_SYSTEM, V2_0128_SYSTEM, V2_0161_SYSTEM, V2_0162_SYSTEM, V2_0203_SYSTEM, V2_0277_SYSTEM, V2_0550_SYSTEM, V3_OBSERVATION_INTERPRETATION_SYSTEM, V3_ROUTE_OF_ADMINISTRATION_SYSTEM, VERSION, addMinutes, buildAnnotations, buildCondition, buildCoverage, buildProcedure, buildSpecimen, carriesLegacyOnsetDate, codeableConceptFromTarget, collectAllergies, collectCoverages, collectDiagnoses, collectProcedures, createNamingSystem, deferredCoverageIssues, deferredDiagnosisIssues, deferredProcedureIssues, emitEncounterDiagnosis, enumerateSegmentOccurrences, fhirIssueTypeFor, insuranceCompanyName, invertCodeMap, isIgMappedSegmentName, isWellFormedSegmentName, issue, segmentOccurrenceLocation, toFhir, toFhirAddress, toFhirCodeableConcept, toFhirCodeableConceptVia, toFhirDateTime, toFhirEntityIdentifier, toFhirHumanName, toFhirIdentifier, toFhirQuantity, toOperationOutcome, toV2Observation, toV2Patient, translateBound, withCodeableText, withEncounterDiagnosis };
|