@cosyte/transform 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +326 -0
- package/LICENSE +21 -0
- package/README.md +128 -0
- package/dist/index.cjs +2702 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1271 -0
- package/dist/index.d.ts +1271 -0
- package/dist/index.mjs +2652 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +108 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
Versions and publishing are managed with [Changesets](https://github.com/changesets/changesets);
|
|
8
|
+
this file is maintained by hand (Changesets handles the version bump and publish only).
|
|
9
|
+
|
|
10
|
+
## [Unreleased]
|
|
11
|
+
|
|
12
|
+
The first pre-alpha release (`0.0.1`) will ship the initial public API surface. The package begins
|
|
13
|
+
its public history at `0.0.x`, per the cosyte version ladder (`0.0.x` until first alpha).
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **Phase 6 — terminology value translation of coded fields** (roadmap §Phase 6). Coded fields that
|
|
18
|
+
earlier phases carried **structurally** (code preserved, system recognized) are now
|
|
19
|
+
**value-translated** through their IG segment-map `mappedVia` ConceptMaps. Adds a `$translate`-shaped,
|
|
20
|
+
additive, fail-safe engine — `toFhirCodeableConceptVia(cwe, map, ctx?)` — and the license-clean value
|
|
21
|
+
maps it applies, each transcribed and verified **firsthand against the raw published IG ConceptMap
|
|
22
|
+
JSON** (`hl7.fhir.uv.v2mappings`, STU1), down to the nested `TypeInfo → mappedVia` extension rows.
|
|
23
|
+
- **RXR-1 route → `route`/`dosageInstruction.route`** via `table-hl70162-to-v2-0162`
|
|
24
|
+
(`ROUTE_VALUE_MAP`): a 41-code identity group into `v2-0162` plus a 6-code remap into
|
|
25
|
+
`v3-RouteOfAdministration` (`ID→IDINJ`, `IM→IM`, `IV→IVINJ`, `PO→PO`, `SC→SQ`, `TD→TRNSDERM`).
|
|
26
|
+
- **RXR-2 site → `site`/`dosageInstruction.site`** via `table-hl70550-to-v2-0550` (`SITE_VALUE_MAP`):
|
|
27
|
+
the 443-code body-part identity map into `v2-0550`, transcribed verbatim (including the IG's
|
|
28
|
+
as-published `Â` encoding artifacts, preserved for source-fidelity and inert).
|
|
29
|
+
- **SCH-8 appointment type → `appointmentType`** via `table-hl70277-to-v2-0277`
|
|
30
|
+
(`APPOINTMENT_TYPE_VALUE_MAP`).
|
|
31
|
+
- **RXO-9 allow-substitution → `substitution.allowedCodeableConcept`** via `table-hl70161-to-v2-0161`
|
|
32
|
+
(`SUBSTITUTION_VALUE_MAP`, `N`/`G`/`T`), translate-or-withhold — a substitution permission is never
|
|
33
|
+
emitted from an unrecognized code.
|
|
34
|
+
- **OBR-5 priority → `ServiceRequest.priority`** via `table-hl70485-to-request-priority`
|
|
35
|
+
(`SERVICE_REQUEST_PRIORITY_MAP`: `S→stat`, `A→asap`, `R→routine`; every other v2-0485 code — the
|
|
36
|
+
whole `T{S,M,H,D,W,L}<integer>` timing-critical family and `PRN` included — is in the IG's
|
|
37
|
+
`(unmapped)` group → flagged, `priority` left absent).
|
|
38
|
+
|
|
39
|
+
**Grounding discipline — no invented targets.** A source code in the IG map's `(unmapped)` group is
|
|
40
|
+
flagged `TRANSFORM_CODE_UNMAPPED` and the raw coding preserved (or the value withheld), never coerced.
|
|
41
|
+
Translation is **additive** (the derived coding is added alongside the preserved raw coding, including
|
|
42
|
+
any CWE.4/5/6 alternate triplet and CWE.7 version) and **bound-table-guarded** — it fires only when the
|
|
43
|
+
field's primary coding is from the source table (CWE.3 absent or naming it); a field declaring a
|
|
44
|
+
_foreign_ coding system is carried structurally + flagged, never asserted as the standard concept. Two
|
|
45
|
+
fields the IG maps into **SNOMED CT** — **RXR-4 method** (`table-hl70165-to-sct`) and **SCH-7 reason**
|
|
46
|
+
(`table-hl70276-to-sct`) — stay structurally carried (BYO ConceptMap): SNOMED is license-encumbered
|
|
47
|
+
and **not bundled** (§5). Two fields the IG ships **no** value ConceptMap for — **TXA-2 document type**
|
|
48
|
+
and **RXA-5 vaccineCode** — are documented as such and left structural, never given an invented
|
|
49
|
+
translation (ADR 0018). Zero encumbered terminology content is bundled. New public surface (additions
|
|
50
|
+
only): `toFhirCodeableConceptVia`, `codeableConceptFromTarget`, `CodedTarget`, `CodedValueMap`,
|
|
51
|
+
`ROUTE_VALUE_MAP`, `SITE_VALUE_MAP`, `APPOINTMENT_TYPE_VALUE_MAP`, `SUBSTITUTION_VALUE_MAP`,
|
|
52
|
+
`SERVICE_REQUEST_PRIORITY_MAP`, and the target-system URI constants. No new issue codes.
|
|
53
|
+
|
|
54
|
+
- **Phase 5 — the thin IG singles: VXU_V04 → Immunization; SIU_S12 → Appointment; MDM_T02 →
|
|
55
|
+
DocumentReference** (roadmap §Phase 5). `toFhir(msg, opts?)` now assembles the three single-trigger IG
|
|
56
|
+
message families into a FHIR R4 message `Bundle`, alongside the Phase-2 `Patient`/`Encounter`. Every
|
|
57
|
+
segment/field/table/datatype map is grounded firsthand on the published HL7 v2-to-FHIR IG
|
|
58
|
+
(`hl7.fhir.uv.v2mappings`, STU1) ConceptMaps and cited in-source. **With Phase 5 the v2→FHIR direction
|
|
59
|
+
is feature-complete for the IG-covered message set.**
|
|
60
|
+
- **VXU_V04: RXA (+ RXR, ORC) → `Immunization`** (IG _Segment RXA/RXR/ORC to Immunization_; per the
|
|
61
|
+
VXU message map each `ORC` creates the Immunization and the `RXA`/`RXR` are incorporated): RXA-5 →
|
|
62
|
+
`vaccineCode`, RXA-3 → `occurrenceDateTime`, RXA-6/7 → `doseQuantity`, RXA-15 → `lotNumber`, RXA-16 →
|
|
63
|
+
`expirationDate`, RXA-18 → `statusReason`, RXA-19 → `reasonCode`, RXA-22/ORC-9 → `recorded`, RXR-1/2
|
|
64
|
+
→ `route`/`site`, ORC-2/3 → `identifier` (PLAC/FILL), and `patient`/`encounter` wired to the bundle.
|
|
65
|
+
`status` follows the IG's three conditioned status rows: a delete action (RXA-21 = `D`) → the
|
|
66
|
+
IG-assigned `entered-in-error`, an unvalued RXA-20 → the IG-assigned `completed`, and a valued RXA-20
|
|
67
|
+
→ the **`HL70322` → Event Status** ConceptMap (`IMMUNIZATION_STATUS_MAP`); a valued RXA-20 the map has
|
|
68
|
+
no target for is flagged and the Immunization withheld, never guessed. The order-group `OBX`s become
|
|
69
|
+
standalone patient `Observation`s.
|
|
70
|
+
- **SIU_S12: SCH (+ AIS, PID) → `Appointment`** (IG _Segment SCH/AIS/PID to Appointment_ + _Datatype TQ
|
|
71
|
+
to Appointment_): SCH-25 → `status` via the **`HL70278` → AppointmentStatus** ConceptMap
|
|
72
|
+
(`APPOINTMENT_STATUS_MAP`), SCH-1/2 → `identifier`, SCH-8 → `appointmentType`, SCH-7 → `reasonCode`,
|
|
73
|
+
SCH-9/10 → `minutesDuration`, SCH-11 TQ.4/TQ.5 → `start`/`end`, AIS-3 → `serviceType`, and the bundle
|
|
74
|
+
Patient wired as the required `participant`. An IG-unmatched filler status withholds the Appointment;
|
|
75
|
+
the participant's IG-unsourced required `status` is a `data-absent-reason` primitive, never fabricated.
|
|
76
|
+
- **MDM_T02: TXA (+ OBX) → `DocumentReference`** (IG _Segment TXA/OBX to DocumentReference_): TXA-2 →
|
|
77
|
+
`type`, TXA-6 → `date`, TXA-12 → `masterIdentifier`, TXA-16 → `identifier`, TXA-25 → `description`,
|
|
78
|
+
the `OBX` body → `content.attachment` (TX/FT base64-encoded verbatim, RP as a URL), and `subject`
|
|
79
|
+
wired to the Patient. `status` is grounded only for TXA-19 = `AV` → `current` (the IG ships no value
|
|
80
|
+
ConceptMap; `AV` has exactly one faithful `document-reference-status` target); every other
|
|
81
|
+
availability code withholds the resource and TXA-17 → `docStatus` is left absent (no IG value map).
|
|
82
|
+
- Fail-safe throughout: timezone-naked instants (Appointment `start`/`end`, DocumentReference `date`,
|
|
83
|
+
Immunization date fields) are dropped + flagged rather than assigned a fabricated UTC offset; a
|
|
84
|
+
non-mapped VXU/SIU/MDM trigger is segment-assembled + flagged, never invented; every emitted resource
|
|
85
|
+
passes the conservative-emit gate against `@cosyte/fhir` before it ships. New public exports:
|
|
86
|
+
`IMMUNIZATION_STATUS_MAP`, `APPOINTMENT_STATUS_MAP`, `IG_MAPPED_IMMUNIZATION_TRIGGERS`,
|
|
87
|
+
`IG_MAPPED_APPOINTMENT_TRIGGERS`, `IG_MAPPED_DOCUMENT_TRIGGERS`.
|
|
88
|
+
|
|
89
|
+
- **Phase 4 — ORM_O01 / OML_O21 → ServiceRequest; RXO → MedicationRequest, the order-entry graph**
|
|
90
|
+
(roadmap §Phase 4). `toFhir(msg, opts?)` now assembles order messages into a FHIR R4 message
|
|
91
|
+
`Bundle`: each ORC-anchored order becomes a **`ServiceRequest`** (an `OBR` order detail) or a
|
|
92
|
+
**`MedicationRequest`** (an `RXO` pharmacy detail), alongside the Phase-2 `Patient`/`Encounter`.
|
|
93
|
+
Every segment/field/table map is grounded firsthand on the published HL7 v2-to-FHIR IG
|
|
94
|
+
(`hl7.fhir.uv.v2mappings`, STU1) ConceptMaps and cited in-source.
|
|
95
|
+
- **ORC + OBR → `ServiceRequest`** (IG _Segment ORC/OBR to ServiceRequest_; per the ORM_O01/OML_O21
|
|
96
|
+
message maps the `ORC` creates the request and the `OBR` is incorporated into it): ORC-2/3 or
|
|
97
|
+
OBR-2/3 → `identifier` (PLAC/FILL, v2-0203), ORC-1 → `status` via the **`HL70119` → request-status**
|
|
98
|
+
ConceptMap (`REQUEST_STATUS_MAP`, only when ORC-5 is not valued), OBR-4 → `code`, OBR-6 →
|
|
99
|
+
`occurrenceDateTime`, ORC-9 → `authoredOn` (for a `NW` order), OBR-31 → `reasonCode`, and
|
|
100
|
+
`subject`/`encounter` wired to the bundle's Patient/Encounter. `intent` is fixed to the
|
|
101
|
+
`request-intent` code `order` from the order-message context (the IG maps `→ intent` with no value
|
|
102
|
+
table) — a resource-level constant, not a fabricated per-code row.
|
|
103
|
+
- **RXO (+ RXR) → `MedicationRequest`** (IG _Segment RXO/RXR to MedicationRequest_): RXO-1 →
|
|
104
|
+
`medicationCodeableConcept`, RXO-2/3 (+ RXO-4 units) → `dosageInstruction.doseAndRate.doseRange`,
|
|
105
|
+
RXR-1/2/4 → `dosageInstruction.route`/`.site`/`.method`, RXO-11/12 → `dispenseRequest.quantity`,
|
|
106
|
+
RXO-13 → `dispenseRequest.numberOfRepeatsAllowed`, ORC-9 → `authoredOn`. Doses/dispense amounts
|
|
107
|
+
carry the magnitude **precision-exact** and gate units against UCUM (a non-UCUM unit is preserved
|
|
108
|
+
verbatim, `code`/`system` absent + flagged) — never a rescaled magnitude or a fabricated UCUM code.
|
|
109
|
+
- **The fail-safes (never a confident wrong request).** A `ServiceRequest.status` that cannot be
|
|
110
|
+
grounded — an ORC-1 in the IG's HL70119 `(unmapped)` group, or a valued ORC-5 the IG routes through
|
|
111
|
+
an unspecified mapping — is left absent + flagged (`TRANSFORM_CODE_UNMAPPED`) and the required-`status`
|
|
112
|
+
emit gate **withholds** the request rather than guessing. The IG grounds **no** `MedicationRequest`
|
|
113
|
+
status, so it is set to the value-set's own `unknown` (an honest "not known", asserting nothing) +
|
|
114
|
+
flagged (`TRANSFORM_REQUIRED_ELEMENT_UNKNOWN`) — the `request-status` codes the HL70119 table yields
|
|
115
|
+
(`revoked`, …) are **not** valid `medicationrequest-status` codes and are never borrowed. **`RXE` has
|
|
116
|
+
no IG segment map (nor RDE message map) in STU1**, so any `RXE` is flagged (`TRANSFORM_ELEMENT_DROPPED`)
|
|
117
|
+
rather than assembled from a guessed layout, and non-`ORM^O01`/`OML^O21` order families (`OMP`, `OMG`,
|
|
118
|
+
`RDE`, …) are flagged `TRANSFORM_SEGMENT_ASSEMBLED`.
|
|
119
|
+
- New public export: **`REQUEST_STATUS_MAP`** (HL70119 → request-status) and
|
|
120
|
+
**`IG_MAPPED_ORDER_TRIGGERS`**.
|
|
121
|
+
- **Phase 3 — ORU^R01 → DiagnosticReport + Observation, the results graph** (roadmap §Phase 3).
|
|
122
|
+
`toFhir(msg, opts?)` now assembles a parsed HL7 v2 **ORU^R01** message into a FHIR R4 message
|
|
123
|
+
`Bundle` carrying a **`DiagnosticReport`** per OBR with its **`Observation`** results, alongside the
|
|
124
|
+
Phase-2 `Patient`/`Encounter`. Every segment/field/table map is grounded firsthand on the published
|
|
125
|
+
HL7 v2-to-FHIR IG (`hl7.fhir.uv.v2mappings`, STU1) ConceptMaps and cited in-source.
|
|
126
|
+
- **OBR → `DiagnosticReport`** (IG _Segment OBR to DiagnosticReport_): OBR-2/3 → `identifier`
|
|
127
|
+
(PLAC/FILL, v2-0203), OBR-4 → `code`, OBR-7/8 → `effectiveDateTime`/`effectivePeriod`, OBR-22 →
|
|
128
|
+
`issued` (a zoned `instant` only; a naked/date-only value is dropped + flagged), OBR-24 →
|
|
129
|
+
`category` (v2-0074), OBR-25 → `status` via the `HL70123` → diagnostic-report-status ConceptMap
|
|
130
|
+
(`DIAGNOSTIC_REPORT_STATUS_MAP`), and the OBX children → `result` references.
|
|
131
|
+
- **OBX → `Observation`** (IG _Segment OBX to Observation_): **OBX-2 discriminates OBX-5 →
|
|
132
|
+
`value[x]`** (NM → `valueQuantity`, CWE/CE/CF/CNE/IS → `valueCodeableConcept`, SN → structured
|
|
133
|
+
`valueQuantity`/`valueRange`/`valueRatio`, ST/TX/FT → `valueString`; a type with no first-class
|
|
134
|
+
target preserves the raw value as `valueString` + flags it — never a fabricated `Quantity`), OBX-3
|
|
135
|
+
→ `code`, OBX-6 → `valueQuantity` units (UCUM-gated), OBX-7 → `referenceRange.text`, OBX-8 →
|
|
136
|
+
`interpretation` via the `HL70078` ConceptMap (`HL70078_INTERPRETATION_CODES`), OBX-11 → `status`
|
|
137
|
+
via the `HL70085` → observation-status ConceptMap (`OBSERVATION_STATUS_MAP`), OBX-14 →
|
|
138
|
+
`effectiveDateTime`.
|
|
139
|
+
- **The "never a confident wrong result" fail-safes.** A **corrected (`C`) / cancelled (`X`)** result
|
|
140
|
+
is modelled exactly and **never emitted as `final`**; an OBX-11/OBR-25 status the IG map has **no
|
|
141
|
+
target** for leaves `status` absent + flagged (`TRANSFORM_CODE_UNMAPPED`) and the required-`status`
|
|
142
|
+
emit gate **withholds** the resource (`TRANSFORM_RESOURCE_INVALID`) rather than guessing; an
|
|
143
|
+
**unrecognized abnormal flag** is surfaced and dropped, never coerced to normal; a numeric
|
|
144
|
+
magnitude is carried through **precision-exact** (read from the raw OBX-5, not a lossy JS `number`).
|
|
145
|
+
- New public surface: `IG_MAPPED_ORU_TRIGGERS`, `DIAGNOSTIC_REPORT_STATUS_MAP`,
|
|
146
|
+
`OBSERVATION_STATUS_MAP`, `HL70078_INTERPRETATION_CODES`. No new issue codes — the existing
|
|
147
|
+
`TRANSFORM_CODE_UNMAPPED` now also covers a table code with no IG-ConceptMap target (its message was
|
|
148
|
+
generalized accordingly), and `TRANSFORM_ELEMENT_DROPPED` covers a deferred richer `value[x]` type.
|
|
149
|
+
- **Phase 2 — ADT → Patient + Encounter, the first message-level assembly** (roadmap §Phase 2). The
|
|
150
|
+
top-level entry `toFhir(msg, opts?)` assembles a parsed HL7 v2 **ADT** message into a FHIR R4
|
|
151
|
+
**message `Bundle`** (a `MessageHeader` first, then the focal resources), establishing the
|
|
152
|
+
message-map → resource-graph pattern later phases reuse. Every segment→resource and field→element
|
|
153
|
+
map is grounded firsthand on the published HL7 v2-to-FHIR IG (`hl7.fhir.uv.v2mappings`, STU1)
|
|
154
|
+
ConceptMaps and cited in-source.
|
|
155
|
+
- **PID → `Patient`** (IG _Segment PID to Patient_): PID-3 → `identifier` (via `toFhirIdentifier`),
|
|
156
|
+
PID-5 → `name`, PID-7 → `birthDate` (reduced to `date` precision; a birth time is dropped +
|
|
157
|
+
flagged), PID-8 → `gender` via the `HL70001` → administrative-gender ConceptMap
|
|
158
|
+
(`ADMINISTRATIVE_GENDER_MAP`; an unmapped sex code leaves `gender` absent, never guessed),
|
|
159
|
+
PID-11 → `address`.
|
|
160
|
+
- **PV1 → `Encounter`** (IG _Segment PV1 to Encounter_ + both `HL70004` tables): PV1-2 → `class` via
|
|
161
|
+
`HL70004` → V3 ActCode (`ENCOUNTER_CLASS_V3_MAP`; the self-mapped classes stay in v2-0004), PV1-2/
|
|
162
|
+
PV1-45 → `status` via `HL70004` → Encounter Status (`ENCOUNTER_STATUS_MAP`; a valued discharge is
|
|
163
|
+
`finished`), PV1-19 → `identifier` (type `VN`), PV1-44/45 → `period`. `Encounter.subject` is wired
|
|
164
|
+
to the bundle Patient.
|
|
165
|
+
- **NK1 → `RelatedPerson`** (IG _Segment NK1 to RelatedPerson_): NK1-2 → `name`, NK1-3 →
|
|
166
|
+
`relationship`, NK1-4 → `address`, `patient` wired to the bundle Patient.
|
|
167
|
+
- **MSH → `MessageHeader`** + the `Bundle` envelope: MSH-9 → `eventCoding` (v2-0003), MSH-3 →
|
|
168
|
+
`source` (the required-but-underivable `source.endpoint` URL is emitted with a `data-absent-reason`
|
|
169
|
+
extension, never fabricated), MSH-7 → `Bundle.timestamp` (only a fully-zoned instant qualifies),
|
|
170
|
+
MSH-10 → `Bundle.identifier`; `MessageHeader.focus` and every intra-bundle reference wire to
|
|
171
|
+
`urn:uuid:` fullUrls that always resolve within the bundle.
|
|
172
|
+
- **Two message-level fail-safes.** A non-IG-mapped trigger is assembled from the reusable segment
|
|
173
|
+
maps and flagged `TRANSFORM_SEGMENT_ASSEMBLED` (never a fabricated message map); every produced
|
|
174
|
+
resource passes a **conservative-emit gate** against `@cosyte/fhir.validateResource` (`Patient`
|
|
175
|
+
strict against the built-in schema; the other types against minimal required-cardinality schemas
|
|
176
|
+
in lenient mode) and a structurally-invalid one — e.g. an Encounter with no `class`/`status` from
|
|
177
|
+
an unmapped patient class — is withheld + flagged `TRANSFORM_RESOURCE_INVALID`, never shipped invalid.
|
|
178
|
+
- New public surface: `toFhir`, `TransformResult`, `IG_MAPPED_ADT_TRIGGERS`, and the exported table
|
|
179
|
+
maps. New **additions-only** issue codes: `TRANSFORM_SEGMENT_ASSEMBLED`, `TRANSFORM_RESOURCE_INVALID`,
|
|
180
|
+
`TRANSFORM_REQUIRED_ELEMENT_UNKNOWN`. `TransformOptions` gains `namingSystem` and a `generateId`
|
|
181
|
+
allocator (for reproducible fullUrls). Property + fuzz coverage over the message boundary
|
|
182
|
+
(never-throw, value-free registered issues, references resolve, every Patient validates strict).
|
|
183
|
+
- **Phase 1 — the datatype foundation + the value-free diagnostic channel** (roadmap §Phase 1).
|
|
184
|
+
`@cosyte/transform` is the HL7 v2 → FHIR R4 **transformation** tier (a consumer of `@cosyte/hl7` +
|
|
185
|
+
`@cosyte/fhir`), not a parser. Every mapping is grounded firsthand on the published HL7 v2-to-FHIR
|
|
186
|
+
Implementation Guide (`hl7.fhir.uv.v2mappings`, STU Edition 1) datatype/table ConceptMaps.
|
|
187
|
+
- The six safety-critical datatype converters, each fail-safe and IG-grounded:
|
|
188
|
+
`toFhirDateTime` (DTM/TS → `dateTime`; a timezone-less time is reduced to date precision, never a
|
|
189
|
+
guessed UTC), `toFhirIdentifier` (CX → `Identifier`; the assigning authority resolves via a
|
|
190
|
+
NamingSystem registry, **never** synthesized from HD.1 alone), `toFhirCodeableConcept` (CWE/CE →
|
|
191
|
+
`CodeableConcept`; an unmapped code is preserved + flagged, never coerced),
|
|
192
|
+
`toFhirHumanName` (XPN → `HumanName`; HL70200 → name-use), `toFhirAddress` (XAD → `Address`; the
|
|
193
|
+
value-conditional XAD.7 split over HL70190 → address-use/type), `toFhirQuantity` (NM + units →
|
|
194
|
+
`Quantity`; magnitude carried precision-exact, non-UCUM unit preserved verbatim, never converted).
|
|
195
|
+
- The `OperationOutcome`-shaped, **value-free** diagnostic channel: `TransformIssue`, the stable
|
|
196
|
+
`ISSUE_CODES` + `FATAL_CODES` registries (`key === value`; renaming/removing one is breaking),
|
|
197
|
+
the `issue` factory, and `toOperationOutcome(issues)`.
|
|
198
|
+
- The minimal NamingSystem resolver: `createNamingSystem`, `DEFAULT_V2_CODE_SYSTEMS`,
|
|
199
|
+
`V2_0203_SYSTEM` — HD → `Identifier.system` (safe OID/UUID auto-derivation only) and v2 Table 0396
|
|
200
|
+
mnemonic → canonical URI (FHIR-core-fixed systems only; full THO crosswalk deferred to Phase 6).
|
|
201
|
+
- Property + fuzz coverage over the datatype boundary (never-throw, registered value-free issues,
|
|
202
|
+
and an emit gate against `@cosyte/fhir.validateResource`).
|
|
203
|
+
- **`@cosyte/hl7` + `@cosyte/fhir` as peer dependencies**, consumed as vendored `pnpm pack` tarballs
|
|
204
|
+
in `vendor/` for dev/test (ADR 0001 + umbrella ADR 0008), with `scripts/vendor-refresh.sh`. Pinned
|
|
205
|
+
sibling commits: `@cosyte/hl7` `46d50eb`, `@cosyte/fhir` `7a099b2`. **Third-party runtime deps: 0.**
|
|
206
|
+
- Two architecture ADRs (`documentation/decisions/`): `0001` — the transformation tier may depend on
|
|
207
|
+
the parser tier; third-party runtime deps stay zero. `0002` — terminology is a separate
|
|
208
|
+
`@cosyte/terminology` sibling; value translation is BYO-ConceptMap.
|
|
209
|
+
|
|
210
|
+
### Changed
|
|
211
|
+
|
|
212
|
+
- **CI-REQUIRED-CHECKS: the build checks now BIND on `main`, and DEPENDABOT-PR-QUEUE: dependencies
|
|
213
|
+
are watched.** Until `PUBLIC-SURFACE-HYGIENE` (#11) this repo had **no ruleset at all**; that
|
|
214
|
+
change created `19914044` requiring exactly one context, `no-internal-refs`. The result is the
|
|
215
|
+
shape worth naming: **a repo can have a ruleset and still not bind its build.** `ci / verify` on
|
|
216
|
+
both matrix legs, `ci / actionlint` and `codeql / analyze` all stayed advisory, so any of them
|
|
217
|
+
could be red and the merge would still land on the branch that publishes. The four contexts are
|
|
218
|
+
now **folded into `19914044`** (renamed `ci-required-checks`) rather than added as a second
|
|
219
|
+
ruleset, deliberately: `ncpdp` is the cautionary case, where a correctly pinned base ruleset sat
|
|
220
|
+
beside two later rulesets that pinned nothing and the repo read as "pinned" because one of its
|
|
221
|
+
rulesets was. One ruleset per repo is one place to audit. Final state, read back live: five
|
|
222
|
+
contexts, each pinned to `integration_id: 15368`; `bypass_actors: []`; `~DEFAULT_BRANCH`; plus
|
|
223
|
+
`deletion` and `non_fast_forward`.
|
|
224
|
+
- **The context names were read off real check runs**, never off a workflow `name:` field.
|
|
225
|
+
Provenance stated exactly, because "two heads" is not true of all five: the four build contexts
|
|
226
|
+
were read off **two** independent `pull_request` heads (`66715e5b`, head of #11; `460bfcf8`,
|
|
227
|
+
head of #7); `no-internal-refs` could only be read off `66715e5b`, since `460bfcf8` predates the
|
|
228
|
+
workflow that emits it. All five were then confirmed together on this change's own PR (#12,
|
|
229
|
+
first head `57a62b2`), which read `BLOCKED` until they landed and `CLEAN` after, on that head
|
|
230
|
+
and on every later one. The
|
|
231
|
+
workflow named `Public-surface gate` emits the context `no-internal-refs`, and requiring a name
|
|
232
|
+
nothing emits leaves every PR **pending**, not failing, forever. `scorecard / analysis` and
|
|
233
|
+
`release / release` are excluded because neither has a `pull_request` trigger; the Advanced
|
|
234
|
+
Security `CodeQL` check (app `57789`) is excluded because it reports **alert state**, not
|
|
235
|
+
whether the analysis ran. No workflow here carries a `paths:` filter.
|
|
236
|
+
- **What the ruleset still does not protect, measured rather than asserted.** A required _job_
|
|
237
|
+
gates its _steps_, but the suites that job runs are chosen by the `include` glob in
|
|
238
|
+
`vitest.config.ts`, and the shared `@cosyte/vitest-config` sets no `test.include` of its own, so
|
|
239
|
+
that line decides today. It is not the only lever: the `test`/`test:coverage` script bodies in
|
|
240
|
+
`package.json` are plain `vitest run` invocations, and a path argument or `--exclude` added
|
|
241
|
+
there drops suites without touching the glob. Narrow either and
|
|
242
|
+
`test/messages/property.test.ts` stops running with the job green. Coverage is a thin,
|
|
243
|
+
incidental backstop: excluding that one file takes
|
|
244
|
+
`src/messages/**` branch coverage from **90.11% to 88.82%**, breaching the `>= 90` gate, so the
|
|
245
|
+
deletion is caught today, by 1.29 points, over the incidental fact that the property run is the
|
|
246
|
+
only thing reaching some branches, and never for the loss of the properties themselves. Banners
|
|
247
|
+
on `ci.yml` and `vitest.config.ts` say so.
|
|
248
|
+
- **The cost, expected and measured.** Requiring a context blocks any open PR that cannot emit it.
|
|
249
|
+
**PR #10 ("Version Packages", head `2996df7`) has zero check runs and reports `BLOCKED`**, and it is
|
|
250
|
+
the structural case, since Changesets opens it as `github-actions[bot]` on the default
|
|
251
|
+
`GITHUB_TOKEN` and GitHub starts no workflow runs for that token's events. The escape (one empty
|
|
252
|
+
commit onto `changeset-release/main`) is written on `release.yml`; a bypass actor is refused.
|
|
253
|
+
- **`.github/dependabot.yml` added** (weekly `npm` + `github-actions`, limit 5, dev-dependency
|
|
254
|
+
group). Zero open Dependabot PRs here meant nothing was looking. Two limits are stated in the
|
|
255
|
+
file rather than left to be discovered: `dependabot_security_updates` reads `disabled` on this
|
|
256
|
+
repo, so an advisory opens no fix PR; and Dependabot resolves neither the `file:vendor/*.tgz`
|
|
257
|
+
specifiers nor a peer dependency's registry move, so **both routes to `@cosyte/hl7` and
|
|
258
|
+
`@cosyte/fhir` are unwatched** and stay a `pnpm vendor:refresh` job by hand. Whether the pnpm
|
|
259
|
+
updater tolerates that `file:` shape at all is recorded as unobserved.
|
|
260
|
+
- **Nothing inside this repo can observe its own ruleset.** Delete it and the suite stays green,
|
|
261
|
+
`verify.sh` stays green, and the docs keep asserting protection. `CLAUDE.md` gained a "Branch
|
|
262
|
+
protection (and the limits of this claim)" section that says so and gives the `gh api` calls,
|
|
263
|
+
including `?includes_parents=true`, because checking one ruleset is how `ncpdp` was missed.
|
|
264
|
+
- **The changeset for this entry is deliberately not consumer-facing.** None of the above is
|
|
265
|
+
observable by someone installing the package, so its headline names `CodeQL`, `actionlint` and
|
|
266
|
+
`Dependabot`, which the shared `cosyte/.github` release-note renderer classifies as internal-only
|
|
267
|
+
and **drops from the published release body** rather than rewording into it. Verified by running
|
|
268
|
+
that renderer's `collectHeadlines` over this repo's eight pending changesets: seven kept, this
|
|
269
|
+
one dropped. Recorded because the earlier wording said the same thing in words the classifier
|
|
270
|
+
does not know, and would have published it.
|
|
271
|
+
- **Also noted, not fixed here:** three of the five required names
|
|
272
|
+
(`ci / verify (22|24, ubuntu-latest)`, `ci / actionlint`) are produced by the DEFAULT inputs of
|
|
273
|
+
`cosyte/.github/.github/workflows/ci.yml@main`, a different repo on a floating ref. Changing a
|
|
274
|
+
default there strands every PR in every repo pinning this set. Fails closed, ecosystem-wide,
|
|
275
|
+
and written into `CLAUDE.md`.
|
|
276
|
+
- **PUBLIC-SURFACE-HYGIENE: internal project bookkeeping removed from every surface a consumer
|
|
277
|
+
reads, and a gate added under it.** Founder directive, 2026-07-27: a README, a docs page, an npm
|
|
278
|
+
description, a JSDoc block a consumer's editor renders, and a message their log prints say what
|
|
279
|
+
the software does and what changed, never which internal item, phase or roadmap section produced
|
|
280
|
+
it. Measured on `e6c4531` with the rule set that ships in this change, because a count taken
|
|
281
|
+
against different rules is a different count: **32** violating lines across the public markdown
|
|
282
|
+
surface (`README.md`, `docs-content/intro.md`, `concepts-archetype.md`, `quickstart.md`,
|
|
283
|
+
`guides-overview.md`, `troubleshooting.md`, all of them "Phase N" framing; **zero** item
|
|
284
|
+
identifiers, and zero on the npm metadata) and **54** `src/` doc-comment lines plus **29** blocks
|
|
285
|
+
that matched only once reflowed across their line wraps. The built `dist/index.d.ts` went from
|
|
286
|
+
**45** violating lines to **0**, and `dist/index.d.cts` and the ESM/CJS bundles with it.
|
|
287
|
+
Separately, and **not** found by any rule: one runtime message string, where
|
|
288
|
+
`TRANSFORM_ELEMENT_DROPPED` told a reader an element's conversion was "deferred to a later phase".
|
|
289
|
+
It ends its clause at `phase`, which is the shape the rules deliberately do not cover, so it was a
|
|
290
|
+
reviewer catch. **51** `src/` doc-comment lines carried a roadmap-section citation
|
|
291
|
+
(`(roadmap §4.5)`, `(roadmap §Phase 5)`, or the bare `(§4.7)`) and were cleared by hand; 23 of
|
|
292
|
+
those overlap the 54 counted above, so the union of the two sweeps is 82 lines, not 105. Of the
|
|
293
|
+
51 citations, 20 name the roadmap explicitly and a rule now catches them, 4 read `§Phase N` and
|
|
294
|
+
are caught by the ordinary phase rule, and the remaining **28** are bare section numbers that
|
|
295
|
+
nothing guards; that non-catch is deliberate and its reasoning is recorded in the script. Two `§`
|
|
296
|
+
citations remain in `//` comments, which this convention keeps out of scope.
|
|
297
|
+
- **`pnpm check:no-internal-refs` + its own CI workflow now gate that rule.** Four passes over the
|
|
298
|
+
README, `LICENSE`, `docs-content/`, the npm `description`/`keywords`, `src/` doc comments and
|
|
299
|
+
`src/` string literals, each scanned line by line and again paragraph-joined so a violation that
|
|
300
|
+
straddles a line wrap cannot hide. It is `hl7`'s gate ported shape-first, with `ncpdp`'s
|
|
301
|
+
string-literal fourth pass; the prefix list is `hl7`'s character for character, and the three rule
|
|
302
|
+
widenings on top of it (`phases?`, `/` in the ADR separator, `roadmap §N`) are each named in the
|
|
303
|
+
script and pinned by their own self-test. **The gate raises the floor; it does not seal the
|
|
304
|
+
category,** and the script writes down thirteen numbered residuals plus the boundaries stated at
|
|
305
|
+
each pass, rather than implying otherwise. The four worth knowing here: `phase` ending a clause
|
|
306
|
+
("deferred to a later phase);") is not covered; a bare `(§4.7)` is a deliberate non-catch; a doc
|
|
307
|
+
comment that does not open its own line is invisible to the doc-comment extractor (residual (xi),
|
|
308
|
+
inherited from `hl7`) and a violation split across a template literal's line breaks is invisible
|
|
309
|
+
to the string-literal extractor (stated at that pass, which comes from `ncpdp` rather than `hl7`),
|
|
310
|
+
neither of them reachable on this tree today; and prose about our process stays a reviewer's
|
|
311
|
+
catch. `CHANGELOG.md` is excluded on purpose even though it ships inside the npm
|
|
312
|
+
tarball: this convention names it as one of the places identifiers belong. That contradiction is
|
|
313
|
+
ecosystem-wide and is recorded rather than settled here.
|
|
314
|
+
- **Replaced the parser-template scaffold** with the transformation shape: removed the placeholder
|
|
315
|
+
`parseTransform` / `WARNING_CODES` / `FATAL_CODES` parser stubs and the round-trip property test;
|
|
316
|
+
rewrote `docs-content/`, `README`, and this repo's `CLAUDE.md` for the transformation library.
|
|
317
|
+
|
|
318
|
+
### Deprecated
|
|
319
|
+
|
|
320
|
+
### Removed
|
|
321
|
+
|
|
322
|
+
### Fixed
|
|
323
|
+
|
|
324
|
+
### Security
|
|
325
|
+
|
|
326
|
+
[Unreleased]: https://github.com/cosyte/transform/commits/main
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Cosyte
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# @cosyte/transform
|
|
2
|
+
|
|
3
|
+
> HL7 v2 → FHIR R4 transformation for Node.js and TypeScript — **IG-grounded, fail-safe, value-free
|
|
4
|
+
> diagnostics; never a confident wrong FHIR value**.
|
|
5
|
+
|
|
6
|
+
`@cosyte/transform` is the healthcare **transformation** layer of the cosyte suite. Unlike the
|
|
7
|
+
parsers, it is a **consumer**: it takes already-parsed [`@cosyte/hl7`](https://github.com/cosyte/hl7)
|
|
8
|
+
composites and produces validated [`@cosyte/fhir`](https://github.com/cosyte/fhir) model nodes,
|
|
9
|
+
grounded on the official **HL7 Version 2 to FHIR** Implementation Guide (`hl7.fhir.uv.v2mappings`).
|
|
10
|
+
|
|
11
|
+
> **Status:** pre-alpha (`0.0.x`), not yet published to npm. This release ships the
|
|
12
|
+
> six safety-critical datatype converters and the value-free diagnostic channel, the
|
|
13
|
+
> message-level assembly, HL7 v2 **ADT → FHIR Patient + Encounter**, the **ORU^R01 → FHIR
|
|
14
|
+
> DiagnosticReport + Observation** results graph, the order-entry graph — **ORM_O01 /
|
|
15
|
+
> OML_O21 → ServiceRequest** and **RXO → MedicationRequest** — the thin IG singles —
|
|
16
|
+
> **VXU_V04 → Immunization**, **SIU_S12 → Appointment**, **MDM_T02 → DocumentReference** — all
|
|
17
|
+
> via `toFhir(msg)`, and **terminology value translation** of coded fields: route/site,
|
|
18
|
+
> appointment type, order priority, and substitution are now value-translated through their IG
|
|
19
|
+
> `mappedVia` ConceptMaps via `toFhirCodeableConceptVia`, fail-safe on any code the IG leaves unmapped.
|
|
20
|
+
> The v2→FHIR direction is feature-complete for the IG-covered message set; deeper
|
|
21
|
+
> terminology, profiles, and the reverse FHIR → v2 direction are not implemented.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @cosyte/transform @cosyte/hl7 @cosyte/fhir
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`@cosyte/hl7` and `@cosyte/fhir` are **peer dependencies** — the transform maps between the models
|
|
30
|
+
they own. Its own third-party runtime dependencies are **zero**.
|
|
31
|
+
|
|
32
|
+
## Convert a datatype
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { toFhirHumanName } from "@cosyte/transform";
|
|
36
|
+
|
|
37
|
+
const { value, issues } = toFhirHumanName({
|
|
38
|
+
familyName: "Public",
|
|
39
|
+
givenName: "Jane",
|
|
40
|
+
nameTypeCode: "L", // HL7 Table 0200 "Legal name" → FHIR name-use "official"
|
|
41
|
+
});
|
|
42
|
+
// value: a FHIR HumanName node; issues: [] (clean, fully mapped)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Each converter returns `{ value, issues }` — the FHIR datatype node it could faithfully produce, plus
|
|
46
|
+
the value-free diagnostics it raised.
|
|
47
|
+
|
|
48
|
+
## The fail-safe rule
|
|
49
|
+
|
|
50
|
+
Every conversion is grounded on the IG and **refuses to guess**. On any ambiguity —
|
|
51
|
+
|
|
52
|
+
- a v2 timestamp with a time-of-day but **no timezone** (FHIR forbids time without a zone),
|
|
53
|
+
- an assigning authority that can't be **resolved to a system URI** (never synthesized from a bare
|
|
54
|
+
namespace — that would merge two patients),
|
|
55
|
+
- a code with an **unrecognized or absent coding system**,
|
|
56
|
+
- a unit that isn't valid **UCUM** (magnitudes are never converted),
|
|
57
|
+
|
|
58
|
+
— the converter produces what it _can_ (often reduced in precision) and raises a **typed, value-free
|
|
59
|
+
`TransformIssue`**: a stable code, the v2 location, and the FHIR path — never a value. Render a list
|
|
60
|
+
of issues as a FHIR `OperationOutcome` with `toOperationOutcome(issues)`.
|
|
61
|
+
|
|
62
|
+
## The six datatype converters
|
|
63
|
+
|
|
64
|
+
| Converter | v2 → FHIR |
|
|
65
|
+
| ----------------------- | -------------------------- |
|
|
66
|
+
| `toFhirDateTime` | DTM/TS → `dateTime` |
|
|
67
|
+
| `toFhirIdentifier` | CX → `Identifier` |
|
|
68
|
+
| `toFhirCodeableConcept` | CWE/CE → `CodeableConcept` |
|
|
69
|
+
| `toFhirHumanName` | XPN → `HumanName` |
|
|
70
|
+
| `toFhirAddress` | XAD → `Address` |
|
|
71
|
+
| `toFhirQuantity` | NM + units → `Quantity` |
|
|
72
|
+
|
|
73
|
+
## Assemble a message
|
|
74
|
+
|
|
75
|
+
`toFhir(msg)` takes a parsed `@cosyte/hl7` **ADT** message and returns a FHIR R4 **message `Bundle`** —
|
|
76
|
+
a `MessageHeader`, then the `Patient` and `Encounter` (and `RelatedPerson`) it describes — plus the
|
|
77
|
+
value-free issues, each map grounded firsthand on the IG's segment/table ConceptMaps.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { parseHL7 } from "@cosyte/hl7";
|
|
81
|
+
import { toFhir, createNamingSystem } from "@cosyte/transform";
|
|
82
|
+
|
|
83
|
+
const { bundle, issues } = toFhir(parseHL7(raw), {
|
|
84
|
+
namingSystem: createNamingSystem({ authorities: { HOSP: "urn:oid:1.2.840.114350" } }),
|
|
85
|
+
});
|
|
86
|
+
// bundle.type === "message"; every reference resolves to a urn:uuid: fullUrl inside the bundle.
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
| Segment | FHIR resource | key maps |
|
|
90
|
+
| ------- | --------------- | ------------------------------------------------------------------------ |
|
|
91
|
+
| MSH | `MessageHeader` | MSH-9 → `eventCoding`; MSH-7/10 → `Bundle.timestamp`/`.identifier` |
|
|
92
|
+
| PID | `Patient` | PID-3/5/7/8/11 → `identifier`/`name`/`birthDate`/`gender`/`address` |
|
|
93
|
+
| PV1 | `Encounter` | PV1-2 → `class`/`status` (HL70004); PV1-19/44/45 → `identifier`/`period` |
|
|
94
|
+
| NK1 | `RelatedPerson` | NK1-2/3/4 → `name`/`relationship`/`address` |
|
|
95
|
+
|
|
96
|
+
The fail-safe rule holds at the message level: an unmapped patient class, a naked timestamp, or an
|
|
97
|
+
unresolvable authority becomes a typed issue — never a fabricated value. A trigger the IG has no
|
|
98
|
+
**message** map for is assembled from the segment maps and flagged, never invented; every emitted
|
|
99
|
+
resource is validated against `@cosyte/fhir` before it ships.
|
|
100
|
+
|
|
101
|
+
The same `toFhir(msg)` handles the other message families: **ORU^R01** → `DiagnosticReport` (OBR) +
|
|
102
|
+
`Observation` (OBX), and the order-entry graph — **ORM_O01 / OML_O21** ORC/OBR →
|
|
103
|
+
`ServiceRequest`, **RXO** (+ RXR route) → `MedicationRequest`, with `ServiceRequest.status`
|
|
104
|
+
grounded on the HL70119 → request-status ConceptMap and withheld when it cannot be grounded, and a
|
|
105
|
+
`MedicationRequest` whose IG-ungrounded status is the honest `unknown` rather than a guess. `RXE` has no
|
|
106
|
+
STU1 IG map and is flagged, never assembled.
|
|
107
|
+
|
|
108
|
+
The thin single-trigger families complete the IG-covered message set: **VXU_V04** RXA (+
|
|
109
|
+
RXR route, ORC) → `Immunization` (status via the IG's three conditioned rows — a delete action →
|
|
110
|
+
`entered-in-error`, an unvalued RXA-20 → `completed`, else the HL70322 → event-status ConceptMap, with a
|
|
111
|
+
valued-but-unmapped code withheld), **SIU_S12** SCH/AIS/PID → `Appointment` (status via the
|
|
112
|
+
HL70278 → appointmentstatus ConceptMap, the Patient wired as the required participant, its IG-unsourced
|
|
113
|
+
required status a `data-absent-reason` primitive), and **MDM_T02** TXA/OBX → `DocumentReference` (status
|
|
114
|
+
grounded only for TXA-19 `AV` → `current`, the document body base64-encoded verbatim, carried and never
|
|
115
|
+
interpreted). Timezone-naked instants are dropped and flagged, never assigned a fabricated UTC offset.
|
|
116
|
+
|
|
117
|
+
`toFhirCodeableConceptVia(cwe, map)` applies a license-clean IG value ConceptMap
|
|
118
|
+
(transcribed and verified firsthand against the raw published IG JSON) — **RXR** route/site
|
|
119
|
+
(HL70162/HL70550), **SCH-8** appointment type (HL70277), **RXO-9** substitution (HL70161), and **OBR-5**
|
|
120
|
+
priority (HL70485) — translating the source table code to its FHIR target coding, additively (the raw
|
|
121
|
+
coding is preserved alongside the derived one). It is fail-safe by refusal: a code the IG map leaves in
|
|
122
|
+
its `(unmapped)` group is flagged, never coerced to a neighbour. Fields whose IG target is **SNOMED CT**
|
|
123
|
+
(RXR-4 method, SCH-7 reason) stay structural — SNOMED is not bundled (BYO ConceptMap) — and fields the
|
|
124
|
+
IG ships no value map for (TXA-2 document type, RXA-5 vaccine code) are carried as-is, never invented.
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
MIT © Cosyte
|