@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/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1271 @@
|
|
|
1
|
+
import { FhirComplex } from '@cosyte/fhir';
|
|
2
|
+
import { HD, TS, CX, CWE, XPN, XAD, NM, Hl7Message } from '@cosyte/hl7';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The stable code registries for the `@cosyte/transform` diagnostic channel.
|
|
6
|
+
*
|
|
7
|
+
* Two registries, both `key === value` so they survive `Object.values(...)` into a snapshot
|
|
8
|
+
* tripwire, and both **part of the public contract**: consumers branch on these codes, so renaming
|
|
9
|
+
* or removing one is a **breaking change** (mirrors the parser archetype's stable-warning-code rule).
|
|
10
|
+
* New codes are **additions only**.
|
|
11
|
+
*
|
|
12
|
+
* - {@link ISSUE_CODES} — non-fatal, typed diagnostics carried on a {@link TransformResult}. Every
|
|
13
|
+
* fail-safe decision the transform makes (a naked timestamp, an unresolvable assigning authority,
|
|
14
|
+
* an unmapped code, a non-UCUM unit, a dropped element) surfaces as one of these — **never** a
|
|
15
|
+
* silently-defaulted or fabricated FHIR value.
|
|
16
|
+
* - {@link FATAL_CODES} — the small set of unrecoverable conditions reserved for message-level
|
|
17
|
+
* entry points. **Nothing in this library throws one today**: the datatype converters and
|
|
18
|
+
* `toFhir` alike fail safe to a value-free issue instead.
|
|
19
|
+
*
|
|
20
|
+
* @packageDocumentation
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Stable **non-fatal issue codes** — the typed diagnostics the fail-safe rule emits.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { ISSUE_CODES } from "@cosyte/transform";
|
|
28
|
+
* if (issue.code === ISSUE_CODES.TRANSFORM_TIMESTAMP_NO_TIMEZONE) {
|
|
29
|
+
* // a v2 timestamp had a time-of-day but no timezone offset — reduced to date precision
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare const ISSUE_CODES: {
|
|
34
|
+
/** A v2 DTM/TS carried a time-of-day but no timezone offset. FHIR forbids time-without-zone, so
|
|
35
|
+
* the value was reduced to date precision (or an explicit sender offset applied via options) —
|
|
36
|
+
* **never** a guessed UTC. */
|
|
37
|
+
readonly TRANSFORM_TIMESTAMP_NO_TIMEZONE: "TRANSFORM_TIMESTAMP_NO_TIMEZONE";
|
|
38
|
+
/** A v2 DTM/TS could not be parsed into a valid FHIR date/time; no value was emitted. */
|
|
39
|
+
readonly TRANSFORM_TIMESTAMP_INVALID: "TRANSFORM_TIMESTAMP_INVALID";
|
|
40
|
+
/** The assigning authority (HD) could not be resolved to a FHIR `Identifier.system` URI via the
|
|
41
|
+
* NamingSystem registry. The identifier is emitted with its value and **no** system — a system URI
|
|
42
|
+
* is **never** synthesized from HD.1 alone. */
|
|
43
|
+
readonly TRANSFORM_IDENTIFIER_SYSTEM_UNRESOLVED: "TRANSFORM_IDENTIFIER_SYSTEM_UNRESOLVED";
|
|
44
|
+
/** A coded value could not be mapped to a FHIR concept — either it carried no coding-system context
|
|
45
|
+
* (CWE.3 absent), or it is a table code the IG ConceptMap has **no target** for (e.g. an OBX-11/
|
|
46
|
+
* OBR-25 result status the HL70085/HL70123 map does not carry, or an OBX-8 abnormal flag absent from
|
|
47
|
+
* HL70078). The original code is preserved / the FHIR value left absent; **never** coerced to a
|
|
48
|
+
* neighbor and **never** guessed to `final`/`normal`. */
|
|
49
|
+
readonly TRANSFORM_CODE_UNMAPPED: "TRANSFORM_CODE_UNMAPPED";
|
|
50
|
+
/** A coding-system mnemonic (CWE.3 / CWE.6) was not recognized, so no canonical system URI could
|
|
51
|
+
* be emitted. The code is preserved; a URI is **never** invented. */
|
|
52
|
+
readonly TRANSFORM_CODE_SYSTEM_UNRESOLVED: "TRANSFORM_CODE_SYSTEM_UNRESOLVED";
|
|
53
|
+
/** A unit was not a valid UCUM code (or was not declared UCUM). It is preserved verbatim in
|
|
54
|
+
* `Quantity.unit`; `Quantity.code`/`.system` are left absent — a UCUM code is **never** fabricated
|
|
55
|
+
* and a magnitude is **never** converted. */
|
|
56
|
+
readonly TRANSFORM_UNIT_NOT_UCUM: "TRANSFORM_UNIT_NOT_UCUM";
|
|
57
|
+
/** A v2 numeric value (NM) could not be represented as a FHIR `decimal` without altering it — e.g.
|
|
58
|
+
* a leading `+`, a leading zero, or a trailing dot, which FHIR's `decimal` lexical form forbids. No
|
|
59
|
+
* `Quantity` value is emitted rather than a canonicalized (and therefore altered) magnitude. */
|
|
60
|
+
readonly TRANSFORM_QUANTITY_VALUE_INVALID: "TRANSFORM_QUANTITY_VALUE_INVALID";
|
|
61
|
+
/** A populated source element was not carried to its FHIR target and was dropped — either because
|
|
62
|
+
* the IG map defines no target, or because that target's conversion is **not implemented**
|
|
63
|
+
* (e.g. XTN→ContactPoint telecom, PV1 participants). Surfaced here rather than silently discarded,
|
|
64
|
+
* never a fabricated value. */
|
|
65
|
+
readonly TRANSFORM_ELEMENT_DROPPED: "TRANSFORM_ELEMENT_DROPPED";
|
|
66
|
+
/** An XPN.7 name-type code has no equivalent in the IG's HL70200 → name-use ConceptMap;
|
|
67
|
+
* `HumanName.use` is left absent rather than guessed. */
|
|
68
|
+
readonly TRANSFORM_NAME_USE_UNMAPPED: "TRANSFORM_NAME_USE_UNMAPPED";
|
|
69
|
+
/** An XAD.7 address-type code has no equivalent in the IG's HL70190 → address-use/type
|
|
70
|
+
* ConceptMaps; `Address.use`/`.type` are left absent rather than guessed. */
|
|
71
|
+
readonly TRANSFORM_ADDRESS_USE_UNMAPPED: "TRANSFORM_ADDRESS_USE_UNMAPPED";
|
|
72
|
+
/** The message's trigger event has no IG *message* map, so its resource graph was assembled from
|
|
73
|
+
* the reusable IG *segment* maps rather than a message map. The graph is best-effort and flagged
|
|
74
|
+
* as such — never a fabricated message map. */
|
|
75
|
+
readonly TRANSFORM_SEGMENT_ASSEMBLED: "TRANSFORM_SEGMENT_ASSEMBLED";
|
|
76
|
+
/** A produced FHIR resource failed `@cosyte/fhir` R4 structural validation and was **withheld**
|
|
77
|
+
* from the bundle rather than emitted as a silently-invalid resource — the conservative-emit gate. */
|
|
78
|
+
readonly TRANSFORM_RESOURCE_INVALID: "TRANSFORM_RESOURCE_INVALID";
|
|
79
|
+
/** A FHIR-required element's value could not be faithfully derived from the source, so it was
|
|
80
|
+
* emitted with a `data-absent-reason` extension (value `unknown`) rather than fabricated — e.g.
|
|
81
|
+
* `MessageHeader.source.endpoint` from an MSH-3 application namespace that is not a URL. */
|
|
82
|
+
readonly TRANSFORM_REQUIRED_ELEMENT_UNKNOWN: "TRANSFORM_REQUIRED_ELEMENT_UNKNOWN";
|
|
83
|
+
};
|
|
84
|
+
/** A value from {@link ISSUE_CODES} — the type consumers narrow `issue.code` against. */
|
|
85
|
+
type IssueCode = (typeof ISSUE_CODES)[keyof typeof ISSUE_CODES];
|
|
86
|
+
/**
|
|
87
|
+
* Stable **fatal codes** — the reserved names for unrecoverable input conditions. **Nothing in this
|
|
88
|
+
* library throws one today**; every path, datatype and message level alike, fails safe to a
|
|
89
|
+
* value-free issue instead. They are declared so that a future one is an addition rather than a
|
|
90
|
+
* rename.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* import { FATAL_CODES } from "@cosyte/transform";
|
|
95
|
+
* void FATAL_CODES.EMPTY_INPUT;
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
declare const FATAL_CODES: {
|
|
99
|
+
/** The input carried nothing to transform. */
|
|
100
|
+
readonly EMPTY_INPUT: "EMPTY_INPUT";
|
|
101
|
+
/** The input was not a shape this transform accepts. */
|
|
102
|
+
readonly TRANSFORM_UNSUPPORTED_INPUT: "TRANSFORM_UNSUPPORTED_INPUT";
|
|
103
|
+
};
|
|
104
|
+
/** A value from {@link FATAL_CODES} — the type carried by a thrown fatal error. */
|
|
105
|
+
type FatalCode = (typeof FATAL_CODES)[keyof typeof FATAL_CODES];
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The value-free {@link TransformIssue} — the single diagnostic shape the whole library emits.
|
|
109
|
+
*
|
|
110
|
+
* Every issue is **`OperationOutcome`-shaped and PHI-safe by construction**: it carries the *v2
|
|
111
|
+
* location* (segment / field / component index — never a value) and the *FHIR path* it concerns,
|
|
112
|
+
* plus a **static, per-code** severity and human-readable message drawn from {@link ISSUE_REGISTRY}.
|
|
113
|
+
* Nothing in an issue is interpolated from patient data — the factory takes only positional metadata,
|
|
114
|
+
* so there is no code path by which a name, DOB, MRN, or result value can reach a log line (the
|
|
115
|
+
* discipline `@cosyte/fhir` already proved with its value-free `OperationOutcome`).
|
|
116
|
+
*
|
|
117
|
+
* @packageDocumentation
|
|
118
|
+
*/
|
|
119
|
+
|
|
120
|
+
/** The severity a {@link TransformIssue} carries — an `OperationOutcome.issue.severity` value. */
|
|
121
|
+
type TransformSeverity = "error" | "warning" | "information";
|
|
122
|
+
/**
|
|
123
|
+
* A single value-free diagnostic.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```ts
|
|
127
|
+
* import { ISSUE_CODES } from "@cosyte/transform";
|
|
128
|
+
* // issue.code === ISSUE_CODES.TRANSFORM_TIMESTAMP_NO_TIMEZONE
|
|
129
|
+
* // issue.v2Location === "TS.1"; issue.fhirPath === "dateTime"
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
interface TransformIssue {
|
|
133
|
+
/** The stable code. One of {@link ISSUE_CODES}. */
|
|
134
|
+
readonly code: IssueCode;
|
|
135
|
+
/** The `OperationOutcome`-style severity, fixed per code. */
|
|
136
|
+
readonly severity: TransformSeverity;
|
|
137
|
+
/** The v2 source location — a segment / field / component **index**, never a value. */
|
|
138
|
+
readonly v2Location: string;
|
|
139
|
+
/** The FHIR path this issue concerns (e.g. `Identifier.system`), when applicable. */
|
|
140
|
+
readonly fhirPath?: string;
|
|
141
|
+
/** A static, value-free description drawn from {@link ISSUE_REGISTRY}. */
|
|
142
|
+
readonly message: string;
|
|
143
|
+
}
|
|
144
|
+
/** The fixed metadata for a code: its severity, its FHIR `issue-type` code, and a static message. */
|
|
145
|
+
interface IssueMeta {
|
|
146
|
+
readonly severity: TransformSeverity;
|
|
147
|
+
/** The FHIR `OperationOutcome.issue.code` (R4 `issue-type` value set) this maps to. */
|
|
148
|
+
readonly fhirIssueType: string;
|
|
149
|
+
/** A static, value-free message. Contains no interpolated data — safe to log verbatim. */
|
|
150
|
+
readonly message: string;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* The per-code registry: severity, FHIR `issue-type`, and a static message. Frozen and exhaustive
|
|
154
|
+
* over {@link ISSUE_CODES} (enforced by the `Record<IssueCode, …>` type — a new code will not
|
|
155
|
+
* compile until it has an entry).
|
|
156
|
+
*/
|
|
157
|
+
declare const ISSUE_REGISTRY: Readonly<Record<IssueCode, IssueMeta>>;
|
|
158
|
+
/**
|
|
159
|
+
* Construct a value-free {@link TransformIssue}. The severity and message come from
|
|
160
|
+
* {@link ISSUE_REGISTRY}; the caller supplies only positional metadata, so an issue can carry no PHI.
|
|
161
|
+
*
|
|
162
|
+
* @param code - The stable {@link IssueCode}.
|
|
163
|
+
* @param v2Location - The v2 source location (segment / field / component index) — never a value.
|
|
164
|
+
* @param fhirPath - The FHIR path the issue concerns, when applicable.
|
|
165
|
+
* @example
|
|
166
|
+
* ```ts
|
|
167
|
+
* import { issue, ISSUE_CODES } from "@cosyte/transform";
|
|
168
|
+
* const i = issue(ISSUE_CODES.TRANSFORM_TIMESTAMP_NO_TIMEZONE, "TS.1", "dateTime");
|
|
169
|
+
* void i.severity; // "warning"
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
declare function issue(code: IssueCode, v2Location: string, fhirPath?: string): TransformIssue;
|
|
173
|
+
/**
|
|
174
|
+
* The FHIR `issue-type` code for an {@link IssueCode} — used by the `OperationOutcome` renderer.
|
|
175
|
+
*
|
|
176
|
+
* @param code - The stable {@link IssueCode}.
|
|
177
|
+
* @example
|
|
178
|
+
* ```ts
|
|
179
|
+
* import { fhirIssueTypeFor, ISSUE_CODES } from "@cosyte/transform";
|
|
180
|
+
* fhirIssueTypeFor(ISSUE_CODES.TRANSFORM_IDENTIFIER_SYSTEM_UNRESOLVED); // "not-found"
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
declare function fhirIssueTypeFor(code: IssueCode): string;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* The {@link ConvertResult} envelope every datatype converter returns.
|
|
187
|
+
*
|
|
188
|
+
* A converter never throws on ambiguity and never returns a "confident wrong" value: it returns the
|
|
189
|
+
* FHIR value it could faithfully produce (or `undefined` when nothing can be safely emitted) together
|
|
190
|
+
* with the value-free {@link TransformIssue}s it raised. This is the fail-safe rule as a type.
|
|
191
|
+
*
|
|
192
|
+
* @packageDocumentation
|
|
193
|
+
*/
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* The result of one datatype conversion: the produced FHIR value (or `undefined` when the input was
|
|
197
|
+
* empty or un-emittable) plus the diagnostics raised.
|
|
198
|
+
*
|
|
199
|
+
* @template T - The produced FHIR value's type (a `FhirComplex` datatype node, or a lexical string
|
|
200
|
+
* for `dateTime`).
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```ts
|
|
204
|
+
* import { toFhirHumanName } from "@cosyte/transform";
|
|
205
|
+
* const { value, issues } = toFhirHumanName({ familyName: "Public" });
|
|
206
|
+
* // value is a FHIR HumanName node; issues is [] here
|
|
207
|
+
* void value;
|
|
208
|
+
* void issues;
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
interface ConvertResult<T> {
|
|
212
|
+
/** The produced FHIR value, or `undefined` when nothing could be safely emitted. */
|
|
213
|
+
readonly value: T | undefined;
|
|
214
|
+
/** The value-free diagnostics raised during the conversion, in emission order. */
|
|
215
|
+
readonly issues: readonly TransformIssue[];
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Render a list of {@link TransformIssue}s as a FHIR `OperationOutcome` resource model.
|
|
220
|
+
*
|
|
221
|
+
* The transform's diagnostic channel is `OperationOutcome`-shaped by design; this is the adapter that
|
|
222
|
+
* materializes it as an actual `@cosyte/fhir` resource node a consumer can serialize, validate, or
|
|
223
|
+
* return alongside a bundle. It is **value-free**: every field is drawn from the issue's static
|
|
224
|
+
* metadata (code, severity, message) and its positional locations (v2 location → `diagnostics`, FHIR
|
|
225
|
+
* path → `expression`) — no patient data is reachable here.
|
|
226
|
+
*
|
|
227
|
+
* @packageDocumentation
|
|
228
|
+
*/
|
|
229
|
+
|
|
230
|
+
/** The code system under which the transform's stable issue codes are carried in `details.coding`. */
|
|
231
|
+
declare const TRANSFORM_ISSUE_SYSTEM = "https://cosyte.com/fhir/CodeSystem/transform-issue-codes";
|
|
232
|
+
/**
|
|
233
|
+
* Build a FHIR `OperationOutcome` {@link FhirComplex} from the given issues. An empty list still
|
|
234
|
+
* produces a structurally-valid `OperationOutcome` with a single `information` "all clear" issue
|
|
235
|
+
* (FHIR requires `OperationOutcome.issue` to be non-empty).
|
|
236
|
+
*
|
|
237
|
+
* @param issues - The value-free diagnostics to render.
|
|
238
|
+
* @example
|
|
239
|
+
* ```ts
|
|
240
|
+
* import { toOperationOutcome } from "@cosyte/transform";
|
|
241
|
+
* const oo = toOperationOutcome([]);
|
|
242
|
+
* // oo is a FhirComplex resource node with resourceType "OperationOutcome"
|
|
243
|
+
* void oo;
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
declare function toOperationOutcome(issues: readonly TransformIssue[]): FhirComplex;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* The NamingSystem registry — how an HL7 v2 identity/coding namespace becomes a FHIR canonical URI.
|
|
250
|
+
*
|
|
251
|
+
* Two resolutions live here, and both are **fail-safe by refusal**:
|
|
252
|
+
*
|
|
253
|
+
* 1. **Assigning authority (HD) → `Identifier.system`.** The IG's CX→Identifier map says CX.4 maps to
|
|
254
|
+
* `Identifier.system` *only when the authority is in an identifier registry* — the URI comes from a
|
|
255
|
+
* NamingSystem lookup, **not** string concatenation. There is no deterministic algorithm from an
|
|
256
|
+
* HD to a URI, so this resolver **never synthesizes a system from HD.1 (the bare namespace
|
|
257
|
+
* mnemonic) alone** — doing so would let two hospitals reusing "MR"/"HOSPMRN" collide and merge two
|
|
258
|
+
* patients. The *only* auto-derivations are the unambiguous ones: `urn:oid:<oid>` when HD.2 is a
|
|
259
|
+
* valid OID with HD.3 = `ISO`, and `urn:uuid:<uuid>` when HD.3 = `UUID`. Everything else must be an
|
|
260
|
+
* explicit, caller-seeded registry entry, or it resolves to `undefined` and the caller emits a
|
|
261
|
+
* typed issue.
|
|
262
|
+
*
|
|
263
|
+
* 2. **v2 coding-system mnemonic → canonical URI.** CWE.3/CWE.6 carry a v2 Table 0396 mnemonic
|
|
264
|
+
* (`LN`, `SCT`, …); FHIR needs the canonical URI (`http://loinc.org`, …). {@link DEFAULT_V2_CODE_SYSTEMS}
|
|
265
|
+
* seeds a small, license-clean set of the universally-fixed FHIR-core systems; an unrecognized
|
|
266
|
+
* mnemonic resolves to `undefined` (the caller preserves the raw code and flags it — a URI is
|
|
267
|
+
* never invented). The full HL7 THO crosswalk is not bundled.
|
|
268
|
+
*
|
|
269
|
+
* @packageDocumentation
|
|
270
|
+
*/
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* The HL7 v2 Table 0203 identifier-type code system canonical URI — used to build `Identifier.type`.
|
|
274
|
+
* (FHIR R4 terminologies-systems.html)
|
|
275
|
+
*/
|
|
276
|
+
declare const V2_0203_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0203";
|
|
277
|
+
/**
|
|
278
|
+
* A small, license-clean seed of v2 Table 0396 coding-system mnemonic → canonical URI, for the
|
|
279
|
+
* FHIR-core-fixed systems whose URIs are universally cited (FHIR R4 terminologies-systems.html).
|
|
280
|
+
* The full HL7 THO NamingSystem crosswalk is not bundled; callers extend this via
|
|
281
|
+
* {@link createNamingSystem}. **No terminology *content* is bundled — only identity URIs.**
|
|
282
|
+
*/
|
|
283
|
+
declare const DEFAULT_V2_CODE_SYSTEMS: Readonly<Record<string, string>>;
|
|
284
|
+
/** A seed for {@link createNamingSystem}: extra coding systems and explicit assigning authorities. */
|
|
285
|
+
interface NamingSystemSeed {
|
|
286
|
+
/** Extra v2 coding-system mnemonic → canonical URI entries (merged over the defaults). */
|
|
287
|
+
readonly codeSystems?: Readonly<Record<string, string>>;
|
|
288
|
+
/**
|
|
289
|
+
* Explicit assigning-authority entries, keyed by **HD.1 namespace mnemonic** and/or **HD.2
|
|
290
|
+
* universal ID (OID)** → the `Identifier.system` URI. This is the safe, caller-vetted path the IG
|
|
291
|
+
* calls for; the registry never derives a system from a mnemonic on its own.
|
|
292
|
+
*/
|
|
293
|
+
readonly authorities?: Readonly<Record<string, string>>;
|
|
294
|
+
}
|
|
295
|
+
/** The resolver consulted by the identity- and code-conversion paths. */
|
|
296
|
+
interface NamingSystemRegistry {
|
|
297
|
+
/**
|
|
298
|
+
* Resolve an assigning authority (HD) to a FHIR `Identifier.system` URI, or `undefined` when it
|
|
299
|
+
* cannot be resolved safely. **Never** synthesizes a URI from HD.1 alone.
|
|
300
|
+
*/
|
|
301
|
+
readonly resolveAssigningAuthority: (hd: HD) => string | undefined;
|
|
302
|
+
/** Resolve a v2 coding-system mnemonic to a canonical URI, or `undefined` when unrecognized. */
|
|
303
|
+
readonly resolveCodeSystem: (mnemonic: string) => string | undefined;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Create a {@link NamingSystemRegistry}. With no seed it resolves the built-in FHIR-core code systems
|
|
307
|
+
* and the two unambiguous HD auto-derivations (OID/UUID); a seed adds caller-vetted code systems and
|
|
308
|
+
* explicit assigning-authority URIs.
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```ts
|
|
312
|
+
* import { createNamingSystem } from "@cosyte/transform";
|
|
313
|
+
* const registry = createNamingSystem({
|
|
314
|
+
* authorities: { HOSPMRN: "urn:oid:1.2.840.114350.1.13.1" },
|
|
315
|
+
* });
|
|
316
|
+
* registry.resolveCodeSystem("LN"); // => "http://loinc.org"
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
319
|
+
declare function createNamingSystem(seed?: NamingSystemSeed): NamingSystemRegistry;
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* The options and context threaded through the datatype converters.
|
|
323
|
+
*
|
|
324
|
+
* These are **types only** — no runtime behavior. {@link TransformOptions} carries the policy knobs a
|
|
325
|
+
* caller can set; {@link TransformContext} additionally carries the
|
|
326
|
+
* {@link NamingSystemRegistry} the identity- and code-resolution paths consult.
|
|
327
|
+
*
|
|
328
|
+
* @packageDocumentation
|
|
329
|
+
*/
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Caller/profile policy for a conversion.
|
|
333
|
+
*
|
|
334
|
+
* @remarks
|
|
335
|
+
* `assumeTimezoneOffsetMinutes` is the **only** way a naked v2 timestamp acquires a time-of-day in
|
|
336
|
+
* FHIR: the caller asserts the sender's offset (in minutes east of UTC — e.g. `-300` for US Eastern
|
|
337
|
+
* standard time). It is a *sender-asserted* value, flagged as such; absent it, a naked timestamp is
|
|
338
|
+
* reduced to date precision, **never** assumed to be UTC.
|
|
339
|
+
*/
|
|
340
|
+
interface TransformOptions {
|
|
341
|
+
/** The sender's UTC offset in minutes, asserted by the caller to resolve naked timestamps. */
|
|
342
|
+
readonly assumeTimezoneOffsetMinutes?: number;
|
|
343
|
+
/**
|
|
344
|
+
* The {@link NamingSystemRegistry} the message-level transform threads into every datatype
|
|
345
|
+
* conversion (HD → `Identifier.system`, v2 mnemonic → canonical URI). When omitted, a default
|
|
346
|
+
* registry (`createNamingSystem()`) is used — it resolves only the FHIR-core-fixed systems and the
|
|
347
|
+
* two unambiguous HD auto-derivations, so an un-seeded assigning authority surfaces a typed issue
|
|
348
|
+
* rather than a guessed system.
|
|
349
|
+
*/
|
|
350
|
+
readonly namingSystem?: NamingSystemRegistry;
|
|
351
|
+
/**
|
|
352
|
+
* Allocator for the `urn:uuid:` `Bundle.entry.fullUrl` / reference identities the assembler mints.
|
|
353
|
+
* Defaults to `crypto.randomUUID`. Inject a deterministic generator to make bundle output
|
|
354
|
+
* reproducible (e.g. for golden fixtures). It must return a fresh, unique value per call — the
|
|
355
|
+
* assembler relies on uniqueness for intra-bundle reference integrity.
|
|
356
|
+
*/
|
|
357
|
+
readonly generateId?: () => string;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* The context a converter consults. Carries the {@link NamingSystemRegistry} used to resolve an
|
|
361
|
+
* assigning authority (HD) to an `Identifier.system` and a v2 coding-system mnemonic to a canonical
|
|
362
|
+
* URI, plus the {@link TransformOptions}.
|
|
363
|
+
*/
|
|
364
|
+
interface TransformContext {
|
|
365
|
+
/** The registry used to resolve HD → system and v2 mnemonic → canonical URI. */
|
|
366
|
+
readonly namingSystem?: NamingSystemRegistry;
|
|
367
|
+
/** Conversion policy. */
|
|
368
|
+
readonly options?: TransformOptions;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* DTM / TS → FHIR `dateTime` — the single most dangerous conversion.
|
|
373
|
+
*
|
|
374
|
+
* Grounded on the IG datatype ConceptMap **DTM → dateTime** (`DTM.1` *is equivalent to* the FHIR
|
|
375
|
+
* `dateTime.$value`, with the instruction to "convert v2 date time format to FHIR date time format")
|
|
376
|
+
* and the **FHIR core datatype rule** it defers to: *"if hours and minutes are specified, a timezone
|
|
377
|
+
* offset SHALL be populated"*, and the R4 `dateTime` grammar requires **full `hh:mm:ss` with an
|
|
378
|
+
* offset** whenever a time is present. Two fail-safe consequences follow:
|
|
379
|
+
*
|
|
380
|
+
* - **Naked timestamp (no offset).** A `20260721143000` cannot become a valid zoned FHIR `dateTime`
|
|
381
|
+
* without inventing an offset — assuming UTC would silently shift the clinical instant by hours. So
|
|
382
|
+
* the value is **reduced to date precision** (`2026-07-21`) and {@link ISSUE_CODES.TRANSFORM_TIMESTAMP_NO_TIMEZONE}
|
|
383
|
+
* is raised — unless the caller asserts the sender's offset via `options.assumeTimezoneOffsetMinutes`.
|
|
384
|
+
* - **Partial-precision time (hour/minute only).** FHIR cannot represent `hh` or `hh:mm` without
|
|
385
|
+
* seconds, and padding `:00` would fabricate precision, so such a value is likewise reduced to date
|
|
386
|
+
* precision and flagged — never padded.
|
|
387
|
+
*
|
|
388
|
+
* Date-only precisions (`2026`, `2026-07`, `2026-07-21`) are preserved exactly — never zero-filled —
|
|
389
|
+
* and need no offset (FHIR permits a bare date).
|
|
390
|
+
*
|
|
391
|
+
* @packageDocumentation
|
|
392
|
+
*/
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Convert a parsed HL7 v2 timestamp to a FHIR `dateTime` lexical string, fail-safe on timezone and
|
|
396
|
+
* precision. Returns `{ value: undefined }` only when the timestamp is unparseable.
|
|
397
|
+
*
|
|
398
|
+
* @param ts - A parsed `@cosyte/hl7` `TS` (`DtmParts`).
|
|
399
|
+
* @param options - Conversion policy; `assumeTimezoneOffsetMinutes` supplies a sender-asserted offset.
|
|
400
|
+
* @example
|
|
401
|
+
* ```ts
|
|
402
|
+
* import { parseDtm } from "@cosyte/hl7";
|
|
403
|
+
* import { toFhirDateTime } from "@cosyte/transform";
|
|
404
|
+
* const { value } = toFhirDateTime(parseDtm("20260721"));
|
|
405
|
+
* // value === "2026-07-21"
|
|
406
|
+
* void value;
|
|
407
|
+
* ```
|
|
408
|
+
*/
|
|
409
|
+
declare function toFhirDateTime(ts: TS, options?: TransformOptions): ConvertResult<string>;
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* CX → FHIR `Identifier` — the patient-identity-integrity path.
|
|
413
|
+
*
|
|
414
|
+
* Grounded on the IG datatype ConceptMap **CX → Identifier**: `CX.1` → `Identifier.value`, `CX.4`
|
|
415
|
+
* (assigning authority HD) → `Identifier.system` *when the authority resolves in a registry* (else
|
|
416
|
+
* the IG maps it to `Identifier.assigner` — a message-level Organization reference this library does
|
|
417
|
+
* not build), and `CX.5` (Table 0203) → `Identifier.type`.
|
|
418
|
+
*
|
|
419
|
+
* The load-bearing fail-safe: the assigning authority is resolved through the
|
|
420
|
+
* {@link NamingSystemRegistry}, which **never synthesizes a system URI from HD.1 (the bare namespace)
|
|
421
|
+
* alone** — two hospitals reusing "MR"/"HOSPMRN" would otherwise collide and merge two patients. On
|
|
422
|
+
* an unresolved authority the identifier is emitted with its **value and no system**, plus a typed
|
|
423
|
+
* {@link ISSUE_CODES.TRANSFORM_IDENTIFIER_SYSTEM_UNRESOLVED} issue — never a guessed system.
|
|
424
|
+
*
|
|
425
|
+
* @packageDocumentation
|
|
426
|
+
*/
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Convert a parsed HL7 v2 CX to a FHIR `Identifier` node, fail-safe on the assigning authority.
|
|
430
|
+
* Returns `{ value: undefined }` when the CX carries no identifier value at all.
|
|
431
|
+
*
|
|
432
|
+
* @param cx - A parsed `@cosyte/hl7` `CX`.
|
|
433
|
+
* @param ctx - The transform context; `ctx.namingSystem` resolves the assigning authority (HD).
|
|
434
|
+
* @example
|
|
435
|
+
* ```ts
|
|
436
|
+
* import { toFhirIdentifier, createNamingSystem } from "@cosyte/transform";
|
|
437
|
+
* const { value, issues } = toFhirIdentifier(
|
|
438
|
+
* { idNumber: "12345", assigningAuthority: { namespaceId: "HOSPMRN" } },
|
|
439
|
+
* { namingSystem: createNamingSystem() },
|
|
440
|
+
* );
|
|
441
|
+
* // no registry entry for a bare "HOSPMRN" namespace → value emitted, system absent, one issue
|
|
442
|
+
* void value;
|
|
443
|
+
* void issues;
|
|
444
|
+
* ```
|
|
445
|
+
*/
|
|
446
|
+
declare function toFhirIdentifier(cx: CX, ctx?: TransformContext): ConvertResult<FhirComplex>;
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* CWE / CE → FHIR `CodeableConcept` — the code-fidelity path.
|
|
450
|
+
*
|
|
451
|
+
* Grounded on the IG datatype ConceptMap **CWE → CodeableConcept**: `CWE.1`→`coding[0].code`,
|
|
452
|
+
* `CWE.2`→`coding[0].display`, `CWE.3`→`coding[0].system` **(indirect — the vocabulary table gives
|
|
453
|
+
* the actual URI)**, `CWE.7`→`coding[0].version`, the alternate triplet `CWE.4/5/6`→`coding[1]` (+
|
|
454
|
+
* `CWE.8`→`coding[1].version`), and `CWE.9`→`text`. `CE` is the 6-component subset of `CWE` and maps
|
|
455
|
+
* through the same shape.
|
|
456
|
+
*
|
|
457
|
+
* Two fail-safes, neither of which ever fabricates a code:
|
|
458
|
+
* - **Unrecognized coding-system mnemonic** (CWE.3/CWE.6) → the code is preserved with **no system**,
|
|
459
|
+
* flagged {@link ISSUE_CODES.TRANSFORM_CODE_SYSTEM_UNRESOLVED}. A canonical URI is never invented.
|
|
460
|
+
* - **A bare code with no coding system at all** (CWE.1 present, CWE.3 absent) → preserved verbatim,
|
|
461
|
+
* flagged {@link ISSUE_CODES.TRANSFORM_CODE_UNMAPPED}. Never coerced to a plausible neighbor.
|
|
462
|
+
*
|
|
463
|
+
* This converter does **no ConceptMap value translation** (that is
|
|
464
|
+
* `toFhirCodeableConceptVia`); it recognizes systems and preserves codes.
|
|
465
|
+
*
|
|
466
|
+
* @packageDocumentation
|
|
467
|
+
*/
|
|
468
|
+
|
|
469
|
+
/** The subset of CWE fields a CE also provides — so `toFhirCodeableConcept` accepts both. */
|
|
470
|
+
type CodedElement = Pick<CWE, "identifier" | "text" | "nameOfCodingSystem" | "alternateIdentifier" | "alternateText" | "nameOfAlternateCodingSystem" | "codingSystemVersionId" | "alternateCodingSystemVersionId" | "originalText">;
|
|
471
|
+
/**
|
|
472
|
+
* Convert a parsed HL7 v2 CWE (or CE) to a FHIR `CodeableConcept` node, fail-safe on the coding
|
|
473
|
+
* system and on unmapped codes. Returns `{ value: undefined }` when the element is empty.
|
|
474
|
+
*
|
|
475
|
+
* @param cwe - A parsed `@cosyte/hl7` `CWE` (a `CE` is accepted as its subset).
|
|
476
|
+
* @param ctx - The transform context; `ctx.namingSystem` resolves the coding-system mnemonic.
|
|
477
|
+
* @example
|
|
478
|
+
* ```ts
|
|
479
|
+
* import { toFhirCodeableConcept, createNamingSystem } from "@cosyte/transform";
|
|
480
|
+
* const { value } = toFhirCodeableConcept(
|
|
481
|
+
* { identifier: "789-8", text: "Hemoglobin", nameOfCodingSystem: "LN" },
|
|
482
|
+
* { namingSystem: createNamingSystem() },
|
|
483
|
+
* );
|
|
484
|
+
* // coding[0] === { system: "http://loinc.org", code: "789-8", display: "Hemoglobin" }
|
|
485
|
+
* void value;
|
|
486
|
+
* ```
|
|
487
|
+
*/
|
|
488
|
+
declare function toFhirCodeableConcept(cwe: CodedElement, ctx?: TransformContext): ConvertResult<FhirComplex>;
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* XPN → FHIR `HumanName`.
|
|
492
|
+
*
|
|
493
|
+
* Grounded on the IG datatype ConceptMap **XPN → HumanName**: `XPN.1`→`family`, `XPN.2`→`given[0]`,
|
|
494
|
+
* `XPN.3`→`given[1]`, `XPN.4`→`suffix`, `XPN.5`→`prefix`, `XPN.6` (degree)→`suffix`,
|
|
495
|
+
* `XPN.7`→`use` (via the IG **HL70200 → name-use** ConceptMap), `XPN.14` (professional suffix)→`suffix`.
|
|
496
|
+
*
|
|
497
|
+
* `XPN.7`→`use` is a **lossy code translation**: only the codes the IG's HL70200→name-use map covers
|
|
498
|
+
* are translated; any other name-type code leaves `use` **absent** and raises
|
|
499
|
+
* {@link ISSUE_CODES.TRANSFORM_NAME_USE_UNMAPPED} — never guessed. Not converted, and flagged:
|
|
500
|
+
* the name-validity period (`XPN.12`/`XPN.13`), whose FHIR `period` needs the timezone policy of the
|
|
501
|
+
* dateTime path.
|
|
502
|
+
*
|
|
503
|
+
* @packageDocumentation
|
|
504
|
+
*/
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* HL7 v2 Table 0200 name-type code → FHIR `name-use`, exactly per the IG **HL70200 → name-use**
|
|
508
|
+
* ConceptMap (every row is "is equivalent to"). Codes absent here (A, B, C, F, I, K, NB, NOUSE, P,
|
|
509
|
+
* REL, S, T, U, …) have **no FHIR equivalent** and are surfaced, never guessed.
|
|
510
|
+
*/
|
|
511
|
+
declare const NAME_USE_MAP: Readonly<Record<string, string>>;
|
|
512
|
+
/**
|
|
513
|
+
* Convert a parsed HL7 v2 XPN to a FHIR `HumanName` node. Returns `{ value: undefined }` when the
|
|
514
|
+
* name carries no emittable part.
|
|
515
|
+
*
|
|
516
|
+
* @param xpn - A parsed `@cosyte/hl7` `XPN`.
|
|
517
|
+
* @example
|
|
518
|
+
* ```ts
|
|
519
|
+
* import { toFhirHumanName } from "@cosyte/transform";
|
|
520
|
+
* const { value } = toFhirHumanName({ familyName: "Public", givenName: "Jane", nameTypeCode: "L" });
|
|
521
|
+
* // value === HumanName { use: "official", family: "Public", given: ["Jane"] }
|
|
522
|
+
* void value;
|
|
523
|
+
* ```
|
|
524
|
+
*/
|
|
525
|
+
declare function toFhirHumanName(xpn: XPN): ConvertResult<FhirComplex>;
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* XAD → FHIR `Address`.
|
|
529
|
+
*
|
|
530
|
+
* Grounded on the IG datatype ConceptMap **XAD → Address**: `XAD.1`/`XAD.2`→`line`, `XAD.3`→`city`,
|
|
531
|
+
* `XAD.4`→`state`, `XAD.5`→`postalCode`, `XAD.6`→`country`, `XAD.9` (county/parish)→`district`, and
|
|
532
|
+
* the **value-conditional `XAD.7`** split across two independent FHIR axes:
|
|
533
|
+
* - `XAD.7 IN {BA, BI, C, B, H, O}` → `Address.use` (via the IG **HL70190 → address-use** map);
|
|
534
|
+
* - `XAD.7 IN {M, SH}` → `Address.type` (via the IG **HL70190 → address-type** map);
|
|
535
|
+
* - a vacation address (`V` in Table 0190; the datatype map references `HV`) → an ISO-21090 extension
|
|
536
|
+
* the IG defines — **not emitted**, and flagged rather than invented;
|
|
537
|
+
* - any other code → `use`/`type` left absent, flagged {@link ISSUE_CODES.TRANSFORM_ADDRESS_USE_UNMAPPED}.
|
|
538
|
+
*
|
|
539
|
+
* FHIR deliberately separates `use` (home/work/temp) from `type` (postal/physical), so one v2 code
|
|
540
|
+
* cannot always populate both axes — round-trip is not stable, and the drop is surfaced, never hidden.
|
|
541
|
+
* `XAD.8` (other geographic designation) has no FHIR target and is flagged dropped.
|
|
542
|
+
*
|
|
543
|
+
* @packageDocumentation
|
|
544
|
+
*/
|
|
545
|
+
|
|
546
|
+
/** HL7 v2 Table 0190 address-type code → FHIR `address-use`, exactly per the IG HL70190→address-use map. */
|
|
547
|
+
declare const ADDRESS_USE_MAP: Readonly<Record<string, string>>;
|
|
548
|
+
/** HL7 v2 Table 0190 address-type code → FHIR `address-type`, exactly per the IG HL70190→address-type map. */
|
|
549
|
+
declare const ADDRESS_TYPE_MAP: Readonly<Record<string, string>>;
|
|
550
|
+
/**
|
|
551
|
+
* Convert a parsed HL7 v2 XAD to a FHIR `Address` node. Returns `{ value: undefined }` when the
|
|
552
|
+
* address carries no emittable part.
|
|
553
|
+
*
|
|
554
|
+
* @param xad - A parsed `@cosyte/hl7` `XAD`.
|
|
555
|
+
* @example
|
|
556
|
+
* ```ts
|
|
557
|
+
* import { toFhirAddress } from "@cosyte/transform";
|
|
558
|
+
* const { value } = toFhirAddress({ street: "1 Main St", city: "Boston", stateOrProvince: "MA", addressType: "H" });
|
|
559
|
+
* // value === Address { use: "home", line: ["1 Main St"], city: "Boston", state: "MA" }
|
|
560
|
+
* void value;
|
|
561
|
+
* ```
|
|
562
|
+
*/
|
|
563
|
+
declare function toFhirAddress(xad: XAD): ConvertResult<FhirComplex>;
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* NM + units (CWE) → FHIR `Quantity`.
|
|
567
|
+
*
|
|
568
|
+
* Grounded on the IG datatype maps for OBX-5 (NM)→`Quantity.value` and OBX-6 (CWE units)→
|
|
569
|
+
* `Quantity.unit`/`.code`/`.system`. Two hard fail-safes:
|
|
570
|
+
*
|
|
571
|
+
* - **Never convert magnitudes.** The numeric value is carried through **precision-exact** via
|
|
572
|
+
* `@cosyte/fhir`'s string-backed `decimal` (a dose or lab value is never routed through a JS
|
|
573
|
+
* `number`). UCUM magnitude conversion (mg/dL ↔ mmol/L) is analyte-dependent and a
|
|
574
|
+
* clinical-safety footgun — an explicit non-goal, never a transform side effect.
|
|
575
|
+
* - **Never fabricate a UCUM code.** A unit is emitted into `Quantity.code`/`.system` **only** when
|
|
576
|
+
* it is declared UCUM and passes `@cosyte/fhir`'s UCUM shape check. Any other unit is preserved
|
|
577
|
+
* verbatim in `Quantity.unit` with `code`/`system` absent, flagged
|
|
578
|
+
* {@link ISSUE_CODES.TRANSFORM_UNIT_NOT_UCUM}.
|
|
579
|
+
*
|
|
580
|
+
* @packageDocumentation
|
|
581
|
+
*/
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Convert a parsed HL7 v2 numeric value plus its units to a FHIR `Quantity` node, fail-safe on the
|
|
585
|
+
* unit. Returns `{ value: undefined }` when the numeric magnitude is absent or non-numeric (a
|
|
586
|
+
* Quantity without a value cannot be safely emitted).
|
|
587
|
+
*
|
|
588
|
+
* @param value - A parsed `@cosyte/hl7` `NM` (its `.raw` carries the exact lexical value).
|
|
589
|
+
* @param units - The units component (an OBX-6 `CWE`); its CWE.1 is the candidate UCUM code.
|
|
590
|
+
* @param ctx - The transform context; used to recognize the UCUM coding system.
|
|
591
|
+
* @example
|
|
592
|
+
* ```ts
|
|
593
|
+
* import { toFhirQuantity } from "@cosyte/transform";
|
|
594
|
+
* const { value } = toFhirQuantity(
|
|
595
|
+
* { raw: "5.4", value: 5.4 },
|
|
596
|
+
* { identifier: "mg/dL", nameOfCodingSystem: "UCUM" },
|
|
597
|
+
* );
|
|
598
|
+
* // value === Quantity { value: 5.4, unit: "mg/dL", system: "http://unitsofmeasure.org", code: "mg/dL" }
|
|
599
|
+
* void value;
|
|
600
|
+
* ```
|
|
601
|
+
*/
|
|
602
|
+
declare function toFhirQuantity(value: NM, units: CWE, ctx?: TransformContext): ConvertResult<FhirComplex>;
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* `toFhir` — the message-level entry point: assemble a parsed HL7 v2 message into a FHIR R4
|
|
606
|
+
* **message Bundle** (a `MessageHeader` first, then the focal resources), grounded on the IG message
|
|
607
|
+
* and segment maps. The **ADT** family becomes **Patient + Encounter** (+ `RelatedPerson`
|
|
608
|
+
* from NK1); the **ORU^R01** results graph becomes `DiagnosticReport` + `Observation`; the
|
|
609
|
+
* order-entry graph — **ORM_O01 / OML_O21** ORC/OBR → `ServiceRequest` and **RXO** (+ RXR) →
|
|
610
|
+
* `MedicationRequest`; and the thin IG singles — **VXU_V04** RXA/RXR/ORC → `Immunization`,
|
|
611
|
+
* **SIU_S12** SCH/AIS/PID → `Appointment`, and **MDM_T02** TXA/OBX → `DocumentReference`.
|
|
612
|
+
*
|
|
613
|
+
* The fail-safe rule holds at the message level. Two message-level fail-safes join the datatype ones:
|
|
614
|
+
*
|
|
615
|
+
* - **Segment-assembled, not fabricated.** For an ADT trigger the IG maps ({@link IG_MAPPED_ADT_TRIGGERS}),
|
|
616
|
+
* the graph is message-map-grounded. For any other trigger there is no IG *message* map, so the
|
|
617
|
+
* graph is assembled from the reusable IG *segment* maps and flagged
|
|
618
|
+
* {@link ISSUE_CODES.TRANSFORM_SEGMENT_ASSEMBLED} — never a fabricated message map.
|
|
619
|
+
* - **Conservative emit gate.** Every produced resource is run through `@cosyte/fhir.validateResource`
|
|
620
|
+
* before it can join the bundle — `Patient` against the built-in schema in `strict` mode, and the
|
|
621
|
+
* other types against the minimal required-cardinality {@link EMIT_SCHEMAS} in `lenient` mode (so a
|
|
622
|
+
* missing required element errors and withholds, while unmodeled sibling elements do not
|
|
623
|
+
* false-reject). A resource with any structural error is **withheld** from the bundle and flagged
|
|
624
|
+
* {@link ISSUE_CODES.TRANSFORM_RESOURCE_INVALID} rather than shipped as silently-invalid FHIR. When
|
|
625
|
+
* the Patient is withheld, the Encounter's `subject` and any RelatedPerson are dropped rather than
|
|
626
|
+
* left dangling — references always resolve within the bundle.
|
|
627
|
+
*
|
|
628
|
+
* @packageDocumentation
|
|
629
|
+
*/
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* The immutable result of a message-level transform: the FHIR `Bundle` model and the value-free
|
|
633
|
+
* diagnostics raised assembling it.
|
|
634
|
+
*/
|
|
635
|
+
interface TransformResult {
|
|
636
|
+
/** The FHIR R4 `Bundle` resource node (a message Bundle: MessageHeader first, then focal resources). */
|
|
637
|
+
readonly bundle: FhirComplex;
|
|
638
|
+
/** The value-free diagnostics raised during assembly, in emission order. */
|
|
639
|
+
readonly issues: readonly TransformIssue[];
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* The ADT trigger events the IG ships a **message** map for. Other triggers still transform — from the
|
|
643
|
+
* reusable segment maps — but are flagged {@link ISSUE_CODES.TRANSFORM_SEGMENT_ASSEMBLED}.
|
|
644
|
+
*/
|
|
645
|
+
declare const IG_MAPPED_ADT_TRIGGERS: ReadonlySet<string>;
|
|
646
|
+
/**
|
|
647
|
+
* The ORU trigger events the IG ships a **message** map for. The IG maps only `R01`; other ORU triggers
|
|
648
|
+
* still assemble their results graph from the reusable OBR/OBX segment maps but are flagged
|
|
649
|
+
* {@link ISSUE_CODES.TRANSFORM_SEGMENT_ASSEMBLED}.
|
|
650
|
+
*/
|
|
651
|
+
declare const IG_MAPPED_ORU_TRIGGERS: ReadonlySet<string>;
|
|
652
|
+
/**
|
|
653
|
+
* The order-message trigger events the IG ships a **message** map for, keyed `CODE^TRIGGER`. Only
|
|
654
|
+
* `ORM^O01` and `OML^O21` are message-map-grounded; other order families (`OMP`, `OMG`, `RDE`, …)
|
|
655
|
+
* still assemble their request graph from the reusable ORC/OBR/RXO/RXR segment maps but are flagged
|
|
656
|
+
* {@link ISSUE_CODES.TRANSFORM_SEGMENT_ASSEMBLED}.
|
|
657
|
+
*/
|
|
658
|
+
declare const IG_MAPPED_ORDER_TRIGGERS: ReadonlySet<string>;
|
|
659
|
+
/**
|
|
660
|
+
* The immunization trigger the IG ships a **message** map for. The IG maps only `VXU^V04`; other VXU
|
|
661
|
+
* triggers still assemble their Immunization graph from the reusable RXA/RXR/ORC segment maps but are
|
|
662
|
+
* flagged {@link ISSUE_CODES.TRANSFORM_SEGMENT_ASSEMBLED}.
|
|
663
|
+
*/
|
|
664
|
+
declare const IG_MAPPED_IMMUNIZATION_TRIGGERS: ReadonlySet<string>;
|
|
665
|
+
/**
|
|
666
|
+
* The scheduling trigger the IG ships a **message** map for. The IG maps only `SIU^S12`; other SIU
|
|
667
|
+
* triggers still assemble their Appointment from the reusable SCH/AIS/PID segment maps but are flagged
|
|
668
|
+
* {@link ISSUE_CODES.TRANSFORM_SEGMENT_ASSEMBLED}.
|
|
669
|
+
*/
|
|
670
|
+
declare const IG_MAPPED_APPOINTMENT_TRIGGERS: ReadonlySet<string>;
|
|
671
|
+
/**
|
|
672
|
+
* The medical-document trigger the IG ships a **message** map for. The IG maps only `MDM^T02`; other MDM
|
|
673
|
+
* triggers still assemble their DocumentReference from the reusable TXA/OBX segment maps but are flagged
|
|
674
|
+
* {@link ISSUE_CODES.TRANSFORM_SEGMENT_ASSEMBLED}.
|
|
675
|
+
*/
|
|
676
|
+
declare const IG_MAPPED_DOCUMENT_TRIGGERS: ReadonlySet<string>;
|
|
677
|
+
/**
|
|
678
|
+
* Transform a parsed HL7 v2 message into a FHIR R4 message `Bundle`. Never throws for a well-formed
|
|
679
|
+
* {@link Hl7Message}; every ambiguity, unmapped code, dropped element, or withheld resource surfaces
|
|
680
|
+
* as a value-free {@link TransformIssue}.
|
|
681
|
+
*
|
|
682
|
+
* @param msg - A parsed `@cosyte/hl7` message.
|
|
683
|
+
* @param opts - Transform options: the {@link TransformOptions.namingSystem} registry, the naked-
|
|
684
|
+
* timestamp {@link TransformOptions.assumeTimezoneOffsetMinutes} policy, and a
|
|
685
|
+
* {@link TransformOptions.generateId} allocator for reproducible fullUrls.
|
|
686
|
+
* @example
|
|
687
|
+
* ```ts
|
|
688
|
+
* import { parseHL7 } from "@cosyte/hl7";
|
|
689
|
+
* import { toFhir, createNamingSystem } from "@cosyte/transform";
|
|
690
|
+
* const { bundle, issues } = toFhir(parseHL7(raw), { namingSystem: createNamingSystem() });
|
|
691
|
+
* void bundle;
|
|
692
|
+
* void issues;
|
|
693
|
+
* ```
|
|
694
|
+
*/
|
|
695
|
+
declare function toFhir(msg: Hl7Message, opts?: TransformOptions): TransformResult;
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* PID → FHIR `Patient` — grounded on the IG **Segment PID to Patient** ConceptMap
|
|
699
|
+
* (`hl7.fhir.uv.v2mappings`, STU1). The field→element rows used here, verified firsthand against the
|
|
700
|
+
* published map:
|
|
701
|
+
*
|
|
702
|
+
* | PID field | FHIR target | via |
|
|
703
|
+
* |---|---|---|
|
|
704
|
+
* | PID-3 Patient Identifier List (CX) | `Patient.identifier` | {@link toFhirIdentifier} |
|
|
705
|
+
* | PID-5 Patient Name (XPN) | `Patient.name` | {@link toFhirHumanName} |
|
|
706
|
+
* | PID-7 Date/Time of Birth (DTM) | `Patient.birthDate` | {@link toFhirDateTime} reduced to a `date` |
|
|
707
|
+
* | PID-8 Administrative Sex (CWE) | `Patient.gender` | {@link ADMINISTRATIVE_GENDER_MAP} (Table 0001) |
|
|
708
|
+
* | PID-11 Patient Address (XAD) | `Patient.address` | {@link toFhirAddress} |
|
|
709
|
+
*
|
|
710
|
+
* Fail-safe throughout: each datatype conversion carries its own value-free issues, an unmapped
|
|
711
|
+
* administrative-sex code leaves `gender` absent (never guessed), and a birth *time* (PID-7 with a
|
|
712
|
+
* time-of-day) is dropped to `date` precision with a flag — the US Core `patient-birthTime` extension
|
|
713
|
+
* is a profile concern this library does not cover. Deferred and flagged, not silently mapped:
|
|
714
|
+
* PID-13/14 telecom (no XTN→ContactPoint converter), and PID-10/15/22/29/30 (US Core race/ethnicity
|
|
715
|
+
* extensions, communication language, deceased).
|
|
716
|
+
*
|
|
717
|
+
* @packageDocumentation
|
|
718
|
+
*/
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* HL7 v2 Table 0001 (Administrative Sex) → FHIR `administrative-gender`, exactly per the IG
|
|
722
|
+
* **Table HL70001 to Administrative Gender** ConceptMap (every row "is equivalent to"). `A`
|
|
723
|
+
* (Ambiguous) and `N` (Not applicable) both narrow to `other` — the map's stated equivalence, carried
|
|
724
|
+
* as-is. A code absent here (the map defines no `unmapped` default) leaves `gender` absent and is
|
|
725
|
+
* flagged {@link ISSUE_CODES.TRANSFORM_CODE_UNMAPPED} — never coerced to `unknown`.
|
|
726
|
+
*/
|
|
727
|
+
declare const ADMINISTRATIVE_GENDER_MAP: Readonly<Record<string, string>>;
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* PV1 → FHIR `Encounter` — grounded on the IG **Segment PV1 to Encounter** ConceptMap plus the two
|
|
731
|
+
* **Table HL70004** ConceptMaps (to V3 ActCode, and to Encounter Status), all verified firsthand
|
|
732
|
+
* against the published `hl7.fhir.uv.v2mappings` STU1 maps:
|
|
733
|
+
*
|
|
734
|
+
* | PV1 field | FHIR target | condition / via |
|
|
735
|
+
* |---|---|---|
|
|
736
|
+
* | PV1-2 Patient Class | `Encounter.class` (Coding) | Table 0004 → V3 ActCode ({@link ENCOUNTER_CLASS_V3_MAP}); the self-mapped classes stay in v2-0004 |
|
|
737
|
+
* | PV1-2 Patient Class | `Encounter.status` (code) | Table 0004 → Encounter Status ({@link ENCOUNTER_STATUS_MAP}), *only IF PV1-45 not valued* |
|
|
738
|
+
* | PV1-19 Visit Number | `Encounter.identifier` (type `VN`) | v2-0203 identifier-type system |
|
|
739
|
+
* | PV1-44 Admit Date/Time | `Encounter.period.start` | {@link toFhirDateTime} |
|
|
740
|
+
* | PV1-45 Discharge Date/Time | `Encounter.period.end` + `status = finished` | {@link toFhirDateTime} |
|
|
741
|
+
*
|
|
742
|
+
* Fail-safe: an unmapped patient class leaves `Encounter.class` absent and flags
|
|
743
|
+
* {@link ISSUE_CODES.TRANSFORM_CODE_UNMAPPED} (never coerced); `Encounter.subject` is wired to the
|
|
744
|
+
* bundle's Patient (the identity anchor) and omitted only when no valid Patient was produced.
|
|
745
|
+
* Deferred and flagged, not silently mapped: PV1-3 location (Reference(Location), no Location resource
|
|
746
|
+
* is built), PV1-7/8 attending/referring doctor (Reference(Practitioner), no Practitioner resource
|
|
747
|
+
* is built).
|
|
748
|
+
*
|
|
749
|
+
* @packageDocumentation
|
|
750
|
+
*/
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* HL7 v2 Table 0004 (Patient Class) → FHIR v3 ActCode (`Encounter.class`), per the IG **Table HL70004
|
|
754
|
+
* to V3 ActCode** ConceptMap (each "is equivalent to"). Only these four v2 classes have a v3 ActCode
|
|
755
|
+
* equivalent; the remaining classes (R/B/C/N/U) map to themselves and stay in the v2-0004 system
|
|
756
|
+
* ({@link V2_0004_SYSTEM}) per the same map's V2→V2 rows.
|
|
757
|
+
*/
|
|
758
|
+
declare const ENCOUNTER_CLASS_V3_MAP: Readonly<Record<string, {
|
|
759
|
+
code: string;
|
|
760
|
+
display: string;
|
|
761
|
+
}>>;
|
|
762
|
+
/**
|
|
763
|
+
* HL7 v2 Table 0004 (Patient Class) → FHIR `Encounter.status`, per the IG **Table HL70004 to
|
|
764
|
+
* Encounter Status** ConceptMap. Applied only when PV1-45 (discharge date) is *not* valued — a valued
|
|
765
|
+
* discharge sets `status = finished` per the PV1→Encounter map.
|
|
766
|
+
*/
|
|
767
|
+
declare const ENCOUNTER_STATUS_MAP: Readonly<Record<string, string>>;
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* OBX → FHIR `Observation` — the highest-clinical-stakes segment map,
|
|
771
|
+
* grounded firsthand on the IG **Segment OBX to Observation** ConceptMap plus its two governing table
|
|
772
|
+
* ConceptMaps (`hl7.fhir.uv.v2mappings`, STU1). The rows used here, verified against the published maps
|
|
773
|
+
* (`ConceptMap-segment-obx-to-observation.html`, `-table-hl70078-to-v3-observationinterpretation.html`,
|
|
774
|
+
* `-table-hl70085-to-observation-status.html`):
|
|
775
|
+
*
|
|
776
|
+
* | OBX field | FHIR target | via |
|
|
777
|
+
* |---|---|---|
|
|
778
|
+
* | OBX-2 Value Type | discriminates OBX-5 → `value[x]` | {@link buildValue} — never assume `Quantity` |
|
|
779
|
+
* | OBX-3 Observation Identifier (CWE) | `Observation.code` | {@link toFhirCodeableConcept} |
|
|
780
|
+
* | OBX-5 Observation Value | `Observation.value[x]` | per OBX-2 |
|
|
781
|
+
* | OBX-6 Units (CWE) | `valueQuantity.unit`/`.code`/`.system` | {@link quantityFromRawMagnitude} |
|
|
782
|
+
* | OBX-7 Reference Range | `Observation.referenceRange.text` | mapped to `.text` (never decomposed) |
|
|
783
|
+
* | OBX-8 Abnormal Flags | `Observation.interpretation` | {@link HL70078_INTERPRETATION_CODES} |
|
|
784
|
+
* | OBX-11 Result Status | `Observation.status` | {@link OBSERVATION_STATUS_MAP} (HL70085) |
|
|
785
|
+
* | OBX-14 Date/Time of Observation | `Observation.effectiveDateTime` | {@link toFhirDateTime} |
|
|
786
|
+
*
|
|
787
|
+
* **The "never a confident wrong result" fail-safes:**
|
|
788
|
+
* - **OBX-2 drives `value[x]`.** `NM`→`valueQuantity`, `CWE`/`CE`/`CF`/`CNE`/`IS`→`valueCodeableConcept`,
|
|
789
|
+
* `SN`→structured (`valueQuantity` with a comparator / `valueRange` / `valueRatio`), `ST`/`TX`/`FT`→
|
|
790
|
+
* `valueString`. A value type with no first-class target here (`NA`, `ED`, `DR`, `TM`, `NR`, unknown)
|
|
791
|
+
* preserves the raw value as `valueString` and flags {@link ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED}
|
|
792
|
+
* (the richer FHIR type is deferred) — never a fabricated `Quantity`.
|
|
793
|
+
* - **A corrected/cancelled result never emits as `final`.** OBX-11 `C`→`corrected`, `X`→`cancelled`,
|
|
794
|
+
* `D`/`W`→`entered-in-error` (HL70085). A status code with **no** HL70085 target leaves
|
|
795
|
+
* `Observation.status` absent (flagged {@link ISSUE_CODES.TRANSFORM_CODE_UNMAPPED}); the required-`status`
|
|
796
|
+
* emit gate then **withholds** the Observation rather than shipping it — never coerced to `final`.
|
|
797
|
+
* - **An unrecognized abnormal flag is surfaced, never coerced to normal.** Each OBX-8 flag in
|
|
798
|
+
* {@link HL70078_INTERPRETATION_CODES} becomes an `interpretation` coding (the map is code-preserving);
|
|
799
|
+
* a flag absent from the table is flagged and dropped, never emitted as `N`/normal.
|
|
800
|
+
*
|
|
801
|
+
* Numeric magnitudes (NM, SN) are read from the **raw OBX-5 field** — not the `@cosyte/hl7`
|
|
802
|
+
* `Observation` view's JS `number` — so a reported lab value's exact lexical precision (`120.50`) is
|
|
803
|
+
* carried through the string-backed FHIR `decimal`, never routed through a lossy `number`.
|
|
804
|
+
*
|
|
805
|
+
* @packageDocumentation
|
|
806
|
+
*/
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* HL7 v2 Table 0078 (Abnormal Flags) → FHIR v3 ObservationInterpretation (`Observation.interpretation`),
|
|
810
|
+
* per the IG **Table HL70078 to v3 ObservationInterpretation** ConceptMap. Every mapped row is
|
|
811
|
+
* `is equivalent to` an **identically-spelled** v3 code (the map is code-preserving), so this is the set
|
|
812
|
+
* of codes that carry a target — a flag **absent** from this set has no equivalent and is flagged
|
|
813
|
+
* {@link ISSUE_CODES.TRANSFORM_CODE_UNMAPPED}, never coerced (the IG leaves `AC`/`HM`/`OBX`/`QCF`/`TOX`
|
|
814
|
+
* and any local flag unmapped, and declares no `unmapped` default).
|
|
815
|
+
*/
|
|
816
|
+
declare const HL70078_INTERPRETATION_CODES: ReadonlySet<string>;
|
|
817
|
+
/**
|
|
818
|
+
* HL7 v2 Table 0085 (Observation Result Status) → FHIR `observation-status` (`Observation.status`),
|
|
819
|
+
* per the IG **Table HL70085 to Observation Status** ConceptMap (each `is equivalent to`). Only these
|
|
820
|
+
* seven source codes carry a target; the codes the IG leaves unmapped (`B`, `I`, `N`, `O`, `R`, `S`,
|
|
821
|
+
* `U`, `V`) are **absent here on purpose** — an OBX-11 with one of them (or any local code) leaves
|
|
822
|
+
* `Observation.status` absent + flagged, and the required-`status` emit gate withholds the Observation.
|
|
823
|
+
* **`C`→`corrected` and `X`→`cancelled` guarantee a corrected/cancelled result never emits as `final`.**
|
|
824
|
+
*/
|
|
825
|
+
declare const OBSERVATION_STATUS_MAP: Readonly<Record<string, string>>;
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* OBR → FHIR `DiagnosticReport` — the ORU results-report anchor, grounded firsthand
|
|
829
|
+
* on the IG **Segment OBR to DiagnosticReport** ConceptMap and its **Table HL70123 to Diagnostic Report
|
|
830
|
+
* Status** ConceptMap (`hl7.fhir.uv.v2mappings`, STU1;
|
|
831
|
+
* `ConceptMap-segment-obr-to-diagnosticreport.html`,
|
|
832
|
+
* `ConceptMap-table-hl70123-queries-to-diagnostic-report-status.html`):
|
|
833
|
+
*
|
|
834
|
+
* | OBR field | FHIR target | via |
|
|
835
|
+
* |---|---|---|
|
|
836
|
+
* | OBR-2 Placer Order Number | `identifier` (type `PLAC`) | EI.1 → `Identifier.value`, v2-0203 type |
|
|
837
|
+
* | OBR-3 Filler Order Number | `identifier` (type `FILL`) | EI.1 → `Identifier.value`, v2-0203 type |
|
|
838
|
+
* | OBR-4 Universal Service Identifier (CWE) | `DiagnosticReport.code` | {@link toFhirCodeableConcept} |
|
|
839
|
+
* | OBR-7 Observation Date/Time | `effectiveDateTime` / `effectivePeriod.start` | {@link toFhirDateTime} |
|
|
840
|
+
* | OBR-8 Observation End Date/Time | `effectivePeriod.end` (start moves to the period when valued) | — |
|
|
841
|
+
* | OBR-22 Results Rpt/Status Chng Date/Time | `DiagnosticReport.issued` (an `instant`) | {@link toFhirDateTime} |
|
|
842
|
+
* | OBR-24 Diagnostic Serv Sect ID | `DiagnosticReport.category` | v2-0074 coding |
|
|
843
|
+
* | OBR-25 Result Status | `DiagnosticReport.status` | {@link DIAGNOSTIC_REPORT_STATUS_MAP} (HL70123) |
|
|
844
|
+
* | (OBX children) | `DiagnosticReport.result` | reference wiring from the assembler |
|
|
845
|
+
*
|
|
846
|
+
* **Fail-safes.** `DiagnosticReport.status` is required (R4 1..1) and the IG itself notes an unvalued
|
|
847
|
+
* OBR-25 is an error; so an absent OBR-25 — or an OBR-25 code the HL70123 map does not carry (`A`, `M`,
|
|
848
|
+
* `N`, `Y`, `Z`) — leaves `status` absent + flagged {@link ISSUE_CODES.TRANSFORM_CODE_UNMAPPED}, and the
|
|
849
|
+
* required-`status` emit gate withholds the report rather than emitting an invalid one or guessing
|
|
850
|
+
* `final`. `OBR-25 = C`→`corrected` / `X`→`cancelled` are modelled exactly. OBR-22 becomes `issued`
|
|
851
|
+
* 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`. Deferred and flagged elsewhere, not silently mapped:
|
|
853
|
+
* OBR-32/34/35 performers (need PractitionerRole resources), specimen, and `basedOn` ServiceRequest.
|
|
854
|
+
*
|
|
855
|
+
* @packageDocumentation
|
|
856
|
+
*/
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* HL7 v2 Table 0123 (Result Status) → FHIR `diagnostic-report-status` (`DiagnosticReport.status`), per
|
|
860
|
+
* the IG **Table HL70123 [Queries] to Diagnostic Report Status** ConceptMap (each `is equivalent to`).
|
|
861
|
+
* Only these **eight** source codes carry a target; the IG leaves `A`, `M`, `N`, `Y`, `Z` unmapped
|
|
862
|
+
* (verified firsthand against the published v1.0.0 ConceptMap — `N` "Procedure completed, results
|
|
863
|
+
* pending" is in the `(not mapped)` group, and the map declares no `unmapped` default), so an OBR-25
|
|
864
|
+
* with one of them leaves `status` absent + flagged and the report is withheld. In particular a
|
|
865
|
+
* results-**pending** `N` is **never** emitted as the post-final `appended`. **`C`→`corrected` and
|
|
866
|
+
* `X`→`cancelled` guarantee a corrected/cancelled report never emits as `final`.**
|
|
867
|
+
*/
|
|
868
|
+
declare const DIAGNOSTIC_REPORT_STATUS_MAP: Readonly<Record<string, string>>;
|
|
869
|
+
|
|
870
|
+
/**
|
|
871
|
+
* ORC + OBR → FHIR `ServiceRequest` — the order-entry request, grounded firsthand
|
|
872
|
+
* on the IG **Segment ORC to ServiceRequest** and **Segment OBR to ServiceRequest** ConceptMaps and
|
|
873
|
+
* the **Table HL70119 to Request Status** ConceptMap (`hl7.fhir.uv.v2mappings`, STU1;
|
|
874
|
+
* `ConceptMap-segment-orc-to-servicerequest.html`, `ConceptMap-segment-obr-to-servicerequest.html`,
|
|
875
|
+
* `ConceptMap-table-hl70119-to-request-status.html`). Per the **ORM_O01 / OML_O21 message maps**,
|
|
876
|
+
* `ORC` *creates* the `ServiceRequest` and `OBR` is *incorporated into that same* request, so one
|
|
877
|
+
* builder consumes both:
|
|
878
|
+
*
|
|
879
|
+
* | v2 field | FHIR target | via |
|
|
880
|
+
* |---|---|---|
|
|
881
|
+
* | ORC-1 Order Control | `ServiceRequest.status` | {@link REQUEST_STATUS_MAP} (HL70119), *only IF ORC-5 not valued* |
|
|
882
|
+
* | ORC-2 / OBR-2 Placer Order Number | `identifier` (type `PLAC`) | {@link orderIdentifier} (EI.1, v2-0203) |
|
|
883
|
+
* | ORC-3 / OBR-3 Filler Order Number | `identifier` (type `FILL`) | {@link orderIdentifier} |
|
|
884
|
+
* | ORC-9 Date/Time of Order Event | `authoredOn` | {@link toFhirDateTime}, *IF ORC-1 = `NW`* |
|
|
885
|
+
* | OBR-4 Universal Service Identifier (CWE) | `ServiceRequest.code` | {@link toFhirCodeableConcept} |
|
|
886
|
+
* | OBR-5 Priority (v2-0485) | `ServiceRequest.priority` | {@link SERVICE_REQUEST_PRIORITY_MAP} (HL70485) |
|
|
887
|
+
* | OBR-6 Requested Date/Time | `occurrenceDateTime` | {@link toFhirDateTime} |
|
|
888
|
+
* | OBR-31 Reason for Study (CWE) | `reasonCode` | {@link toFhirCodeableConcept} |
|
|
889
|
+
* | (order message context) | `intent` = `order` | see below |
|
|
890
|
+
* | (message-map wiring) | `subject` / `encounter` | the bundle's Patient / Encounter |
|
|
891
|
+
*
|
|
892
|
+
* **Fail-safes (never a confident wrong request).**
|
|
893
|
+
* - **`status` (required 1..1).** ORC-1 → HL70119 carries a target for only the 19 codes in
|
|
894
|
+
* {@link REQUEST_STATUS_MAP}; the many codes the IG leaves in its `(unmapped)` group (CH, CP, PA,
|
|
895
|
+
* RE, RF, RP, SC, the U* "unable to …" family, …) have **no** target and leave `status` absent +
|
|
896
|
+
* flagged {@link ISSUE_CODES.TRANSFORM_CODE_UNMAPPED}. When **ORC-5** (Order Status) is valued the
|
|
897
|
+
* IG routes status through ORC-5 via an *unspecified* mapping (no code table) — so we cannot derive
|
|
898
|
+
* it, flag ORC-5 unmapped, and leave `status` absent rather than mis-applying the ORC-1 table
|
|
899
|
+
* against the IG's own condition. The required-`status` emit gate then **withholds** the request —
|
|
900
|
+
* never guessed, never coerced.
|
|
901
|
+
* - **`intent` (required 1..1) = `order`.** The IG maps ORC-1/OBR → `intent` as `equivalent` but ships
|
|
902
|
+
* **no value ConceptMap** for it; the ORM_O01/OML_O21 families are, by definition, *order* messages
|
|
903
|
+
* (their message maps create a `ServiceRequest`/`MedicationRequest` per order), so `intent` is fixed
|
|
904
|
+
* to the `request-intent` code `order` from that message context. This is a resource-level constant
|
|
905
|
+
* grounded in the message family — **not** a fabricated per-code translation row. The finer
|
|
906
|
+
* OBR-11-conditioned intent refinement is deferred.
|
|
907
|
+
* - **`subject` (required 1..1).** Wired to the bundle's Patient; a request with no resolvable Patient
|
|
908
|
+
* is withheld rather than emitted with a dangling/absent subject.
|
|
909
|
+
*
|
|
910
|
+
* - **`priority` (0..1) — value-translated.** OBR-5 → `ServiceRequest.priority` via the IG
|
|
911
|
+
* **Table HL70485 to Request Priority** ConceptMap ({@link SERVICE_REQUEST_PRIORITY_MAP}): only
|
|
912
|
+
* `S→stat`, `A→asap`, `R→routine` carry a target (each `equivalent`, and all three are valid
|
|
913
|
+
* `request-priority` members). Every other v2-0485 code — `P`, `C`, `T`, the `T{S,M,H,D,W,L}<integer>`
|
|
914
|
+
* timing-critical family, `PRN` — sits in the IG map's `(unmapped)` group, so a valued OBR-5 the map
|
|
915
|
+
* has no target for leaves `priority` absent + flagged {@link ISSUE_CODES.TRANSFORM_CODE_UNMAPPED},
|
|
916
|
+
* never guessed. (`priority` is 0..1, so an unmapped value simply omits it — the request still emits.)
|
|
917
|
+
*
|
|
918
|
+
* Deferred and flagged elsewhere, not silently mapped: ORC-7/OBR-27 timing (`$this`), ORC-12/OBR-16
|
|
919
|
+
* requester + ORC-21..24 ordering facility/provider (need Practitioner/PractitionerRole/Organization
|
|
920
|
+
* resources), OBR-29 basedOn, specimen, and the ORC-1/ORC-9/OBR-13 order-control/supporting-info
|
|
921
|
+
* extensions.
|
|
922
|
+
*
|
|
923
|
+
* @packageDocumentation
|
|
924
|
+
*/
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* HL7 v2 Table 0119 (Order Control Codes) → FHIR `request-status` (`ServiceRequest.status`), per the
|
|
928
|
+
* IG **Table HL70119 to Request Status** ConceptMap (each `is equivalent to`). Only these **19** source
|
|
929
|
+
* codes carry a target; every other v2-0119 code sits in the IG's explicit `(unmapped)` group
|
|
930
|
+
* (verified firsthand against the published v1.0.0 ConceptMap — CH/CN/CP/DE/LI/NA/OE/OF/OP/OR/PA/RE/
|
|
931
|
+
* RF/RP/RR/RU/SC/SN/SR/SS/UA/UC/UD/UF/UH/UM/UN/UR/UX/XO/XR/XX/MC are all `unmatched`, and the map
|
|
932
|
+
* declares no `unmapped` default), so an ORC-1 with one of them leaves `status` absent + flagged and
|
|
933
|
+
* the request is withheld. `request-status` has no `cancelled`; the cancel/discontinue codes map to
|
|
934
|
+
* `revoked` and the hold codes to `on-hold`, exactly as the IG table specifies.
|
|
935
|
+
*/
|
|
936
|
+
declare const REQUEST_STATUS_MAP: Readonly<Record<string, string>>;
|
|
937
|
+
/**
|
|
938
|
+
* HL7 v2 Table 0485 (Extended Priority Codes) → FHIR `request-priority` (`ServiceRequest.priority`),
|
|
939
|
+
* per the IG **Table HL70485 to Request Priority** ConceptMap (each `is equivalent to`; verified
|
|
940
|
+
* firsthand against the published v1.0.0 ConceptMap). Only these **three** source codes carry a
|
|
941
|
+
* target; every other v2-0485 code — `P` (Preop), `C` (Callback), `T` (Timing critical), the
|
|
942
|
+
* `T{S,M,H,D,W,L}<integer>` timing-critical family, and `PRN` (As needed) — sits in the IG's
|
|
943
|
+
* `(unmapped)` group with no target, so an OBR-5 carrying one leaves `priority` absent + flagged.
|
|
944
|
+
* All three targets (`stat`/`asap`/`routine`) are valid `request-priority` members.
|
|
945
|
+
*/
|
|
946
|
+
declare const SERVICE_REQUEST_PRIORITY_MAP: Readonly<Record<string, string>>;
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* VXU_V04 → FHIR `Immunization` — the thin IG single for immunization administration,
|
|
950
|
+
* grounded firsthand on the IG **VXU_V04 message map** and the **RXA/RXR/ORC → Immunization**
|
|
951
|
+
* segment maps (`hl7.fhir.uv.v2mappings`, STU1; `ConceptMap-message-vxu-v04-to-bundle.json`,
|
|
952
|
+
* `ConceptMap-segment-rxa-to-immunization.json`, `ConceptMap-segment-rxr-to-immunization.json`,
|
|
953
|
+
* `ConceptMap-segment-orc-to-immunization.json`). Per the VXU message map, **each `ORC` creates an
|
|
954
|
+
* `Immunization`** and the `RXA` (+ `RXR` route) in the same order group are *incorporated into it*, so
|
|
955
|
+
* one builder consumes all three; the order-group `OBX`s become standalone patient `Observation`s
|
|
956
|
+
* (`Observation[2]` in the IG bundle), handled by the assembler.
|
|
957
|
+
*
|
|
958
|
+
* | v2 field | FHIR target | via |
|
|
959
|
+
* |---|---|---|
|
|
960
|
+
* | RXA-5 Administered Code (CE) | `vaccineCode` (required 1..1) | {@link toFhirCodeableConcept} (structural — no IG value map) |
|
|
961
|
+
* | RXA-3 Date/Time Start of Administration (DTM) | `occurrenceDateTime` (required, occurrence[x]) | {@link toFhirDateTime} |
|
|
962
|
+
* | RXA-20 Completion Status + RXA-21 Action Code | `status` (required 1..1) | {@link buildStatus} (HL70322 + IG assignments) |
|
|
963
|
+
* | RXA-6 Administered Amount + RXA-7 Units | `doseQuantity` | {@link quantityFromRawMagnitude} |
|
|
964
|
+
* | RXA-15 Substance Lot Number (ST) | `lotNumber` | verbatim |
|
|
965
|
+
* | RXA-16 Substance Expiration Date (DT) | `expirationDate` (a `date`) | {@link toFhirDateTime}, date-only |
|
|
966
|
+
* | RXA-18 Substance/Treatment Refusal Reason (CWE) | `statusReason` | {@link toFhirCodeableConcept} |
|
|
967
|
+
* | RXA-19 Indication (CWE) | `reasonCode` | {@link toFhirCodeableConcept} |
|
|
968
|
+
* | RXA-22 System Entry Date/Time (DTM) | `recorded` | {@link toFhirDateTime} (ORC-9 fallback) |
|
|
969
|
+
* | RXR-1 Route (CWE) | `route` | {@link ROUTE_VALUE_MAP} (HL70162, value-translated) |
|
|
970
|
+
* | RXR-2 Administration Site (CWE) | `site` | {@link SITE_VALUE_MAP} (HL70550, value-translated) |
|
|
971
|
+
* | ORC-2 / ORC-3 Placer/Filler Order Number | `identifier` (PLAC / FILL) | {@link orderIdentifier} |
|
|
972
|
+
* | ORC-9 Date/Time of Order Event | `recorded` (fallback) | {@link toFhirDateTime} |
|
|
973
|
+
* | (message-map wiring) | `patient` (required 1..1) / `encounter` | the bundle's Patient / Encounter |
|
|
974
|
+
*
|
|
975
|
+
* **Fail-safes (never a confident wrong immunization record).**
|
|
976
|
+
* - **`status` (required 1..1).** The IG ships **three conditioned status rows** (verified firsthand
|
|
977
|
+
* against `ConceptMap-segment-rxa-to-immunization.json`): **RXA-21 = "D"** (delete) → the IG-assigned
|
|
978
|
+
* `entered-in-error`; **RXA-20 not valued and RXA-21 ≠ "D"** → the IG-assigned `completed`; **RXA-20
|
|
979
|
+
* valued and RXA-21 ≠ "D"** → translated via {@link IMMUNIZATION_STATUS_MAP} (the HL70322 →
|
|
980
|
+
* event-status ConceptMap, whose targets `completed`/`not-done` are all valid `immunization-status`
|
|
981
|
+
* members). A **valued** RXA-20 the HL70322 map has no target for is flagged
|
|
982
|
+
* {@link ISSUE_CODES.TRANSFORM_CODE_UNMAPPED}, `status` left absent, and the required-`status` emit gate
|
|
983
|
+
* **withholds** the Immunization — never guessed, never coerced.
|
|
984
|
+
* - **`vaccineCode` (required 1..1) + `occurrence[x]` (required 1..1) + `patient` (required 1..1).** An
|
|
985
|
+
* absent RXA-5, RXA-3, or bundle Patient leaves the resource missing a required element, so it is
|
|
986
|
+
* withheld by the emit gate rather than emitted incomplete.
|
|
987
|
+
* - **Dose.** RXA-6/RXA-7 → `doseQuantity` via {@link quantityFromRawMagnitude}: precision-exact magnitude,
|
|
988
|
+
* non-UCUM unit preserved verbatim with `.code`/`.system` absent + flagged — never a fabricated UCUM code.
|
|
989
|
+
* - **Route/site value translation.** RXR-1 route → {@link ROUTE_VALUE_MAP} (HL70162) and
|
|
990
|
+
* RXR-2 site → {@link SITE_VALUE_MAP} (HL70550) are value-translated additively (derived target coding
|
|
991
|
+
* added, raw coding preserved); a code outside the table is preserved + flagged, never coerced.
|
|
992
|
+
* - **`vaccineCode` is NOT value-translated — by grounding, not omission.** RXA-5 has **no** `mappedVia`
|
|
993
|
+
* value ConceptMap in the IG's RXA→Immunization segment map (verified firsthand), so the code (typically
|
|
994
|
+
* CVX) is carried **structurally** (system recognized, value preserved) — a translation is never
|
|
995
|
+
* invented for it.
|
|
996
|
+
*
|
|
997
|
+
* Deferred and flagged elsewhere, not silently mapped: RXA-10 performer, RXA-17 manufacturer, RXA-27/28
|
|
998
|
+
* location, ORC-12 performer (all need Practitioner/Organization/Location resources this library does not
|
|
999
|
+
* build).
|
|
1000
|
+
*
|
|
1001
|
+
* @packageDocumentation
|
|
1002
|
+
*/
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* HL7 v2 Table 0322 (Completion Status) → FHIR `Immunization.status`, per the IG **Table HL70322 to
|
|
1006
|
+
* Event Status** ConceptMap (each `is equivalent to`). Table 0322 has exactly these **four** source
|
|
1007
|
+
* codes and every one carries a target; the two distinct targets (`completed`, `not-done`) are both
|
|
1008
|
+
* valid members of the `immunization-status` required binding, so applying this map to
|
|
1009
|
+
* `Immunization.status` is faithful. Consulted only for a **valued** RXA-20 when **RXA-21 ≠ "D"** (the
|
|
1010
|
+
* IG's `mappedVia` condition on that row); the two other IG status rows assign fixed values directly (a
|
|
1011
|
+
* delete → `entered-in-error`, an unvalued RXA-20 → `completed`) and do not consult this map. See
|
|
1012
|
+
* {@link buildStatus}.
|
|
1013
|
+
*/
|
|
1014
|
+
declare const IMMUNIZATION_STATUS_MAP: Readonly<Record<string, string>>;
|
|
1015
|
+
|
|
1016
|
+
/**
|
|
1017
|
+
* SIU_S12 → FHIR `Appointment` — the thin IG single for scheduling, grounded
|
|
1018
|
+
* firsthand on the IG **SIU_S12 message map**, the **SCH/AIS/PID → Appointment** segment maps, the
|
|
1019
|
+
* **Table HL70278 → AppointmentStatus** ConceptMap, and the **TQ → Appointment** datatype map
|
|
1020
|
+
* (`hl7.fhir.uv.v2mappings`, STU1; `ConceptMap-message-siu-s12-to-bundle.json`,
|
|
1021
|
+
* `ConceptMap-segment-sch-to-appointment.json`, `ConceptMap-segment-ais-to-appointment.json`,
|
|
1022
|
+
* `ConceptMap-segment-pid-to-appointment.json`, `ConceptMap-table-hl70278-to-appointmentstatus.json`,
|
|
1023
|
+
* `ConceptMap-datatype-tq-to-appointment.json`). Per the message map an SIU message yields **one**
|
|
1024
|
+
* `Appointment` created from the `SCH` segment; `PID` contributes the patient participant and `AIS`
|
|
1025
|
+
* the service type.
|
|
1026
|
+
*
|
|
1027
|
+
* | v2 field | FHIR target | via |
|
|
1028
|
+
* |---|---|---|
|
|
1029
|
+
* | SCH-25 Filler Status Code | `status` (required 1..1) | {@link APPOINTMENT_STATUS_MAP} (HL70278) |
|
|
1030
|
+
* | SCH-1 Placer / SCH-2 Filler Appointment ID | `identifier` | EI.1 → `Identifier.value` |
|
|
1031
|
+
* | SCH-7 Appointment Reason (CWE) | `reasonCode` | {@link toFhirCodeableConcept} (structural — SNOMED target, BYO) |
|
|
1032
|
+
* | SCH-8 Appointment Type (CWE) | `appointmentType` | {@link APPOINTMENT_TYPE_VALUE_MAP} (HL70277, value-translated) |
|
|
1033
|
+
* | SCH-9 Duration + SCH-10 Units | `minutesDuration` | integer, only when SCH-10 is minutes |
|
|
1034
|
+
* | SCH-11 Appointment Timing Quantity (TQ) | `start` / `end` (instant) | TQ.4/TQ.5 → {@link toFhirDateTime} |
|
|
1035
|
+
* | AIS-3 Universal Service Identifier (CWE) | `serviceType` | {@link toFhirCodeableConcept} |
|
|
1036
|
+
* | PID (bundle Patient) | `participant.actor` (required 1..*) | reference wiring |
|
|
1037
|
+
*
|
|
1038
|
+
* **Fail-safes (never a confident wrong appointment).**
|
|
1039
|
+
* - **`status` (required 1..1).** SCH-25 → {@link APPOINTMENT_STATUS_MAP} (HL70278). The three HL70278
|
|
1040
|
+
* codes the IG leaves unmatched (`Discontinued`, `Blocked`, `Overbook`) and an absent SCH-25 leave
|
|
1041
|
+
* `status` absent + flagged {@link ISSUE_CODES.TRANSFORM_CODE_UNMAPPED}; the required-`status` emit gate
|
|
1042
|
+
* then **withholds** the Appointment — never guessed.
|
|
1043
|
+
* - **`participant` (required 1..*).** The bundle Patient is emitted as the required participant. Its
|
|
1044
|
+
* `participant.status` (a required-bound `code` the IG supplies no source for) is emitted as a
|
|
1045
|
+
* `data-absent-reason` primitive (value `unknown`) + flagged {@link ISSUE_CODES.TRANSFORM_REQUIRED_ELEMENT_UNKNOWN}
|
|
1046
|
+
* — the spec-clean way to satisfy a required code whose value is genuinely unknown, never fabricated. An
|
|
1047
|
+
* Appointment with no resolvable Patient (and no other groundable actor this library builds) has no
|
|
1048
|
+
* participant and is withheld.
|
|
1049
|
+
* - **`start`/`end` (instant).** SCH-11's TQ.4/TQ.5 become `start`/`end` only when they are fully-zoned
|
|
1050
|
+
* instants; a naked (unzoned) timing is dropped + flagged rather than assigned a fabricated UTC offset,
|
|
1051
|
+
* mirroring `Bundle.timestamp`.
|
|
1052
|
+
* - **`appointmentType` value translation.** SCH-8 → {@link APPOINTMENT_TYPE_VALUE_MAP}
|
|
1053
|
+
* (HL70277 `Normal`/`Tentative`/`Complete` identity into `v2-0277`); a code outside the table is
|
|
1054
|
+
* preserved + flagged, never coerced. **`reasonCode` is NOT value-translated:** SCH-7's IG map target
|
|
1055
|
+
* is SNOMED CT (`table-hl70276-to-sct`) — encumbered, **not bundled** — so the reason is carried
|
|
1056
|
+
* structurally (BYO ConceptMap), never SNOMED-translated here. (The IG's SCH→Appointment map *also*
|
|
1057
|
+
* carries a redundant SCH-7 → `appointmentType[1]` row via `table-hl70277-to-v2-0277`; it is not
|
|
1058
|
+
* applied — SCH-7 carries HL70276 *reason* codes, not HL70277 *type* codes, so translating them through
|
|
1059
|
+
* the type table would only ever produce spurious unmapped flags. `appointmentType` comes from SCH-8,
|
|
1060
|
+
* the appointment-*type* field, per that same map's SCH-8 → `appointmentType[1]` row.)
|
|
1061
|
+
*
|
|
1062
|
+
* Deferred and flagged elsewhere, not silently mapped: SCH-12/16/20 contact participants and AIP/AIL/AIG
|
|
1063
|
+
* actors (need Practitioner/Location resources), SCH-26/27 `basedOn` ServiceRequest, AIS participant periods.
|
|
1064
|
+
*
|
|
1065
|
+
* @packageDocumentation
|
|
1066
|
+
*/
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* HL7 v2 Table 0278 (Filler Status Code) → FHIR `appointmentstatus` (`Appointment.status`), per the IG
|
|
1070
|
+
* **Table HL70278 to AppointmentStatus** ConceptMap (each `is equivalent to`). The three source codes
|
|
1071
|
+
* the IG leaves **unmatched** (`Discontinued`, `Blocked`, `Overbook`) are absent here on purpose — an
|
|
1072
|
+
* SCH-25 carrying one of them (or any local code) leaves `Appointment.status` absent + flagged and the
|
|
1073
|
+
* required-`status` emit gate withholds the Appointment.
|
|
1074
|
+
*/
|
|
1075
|
+
declare const APPOINTMENT_STATUS_MAP: Readonly<Record<string, string>>;
|
|
1076
|
+
|
|
1077
|
+
/**
|
|
1078
|
+
* The `$translate`-shaped ConceptMap-application engine + the **license-clean** v2-table → FHIR
|
|
1079
|
+
* value maps it applies. This is the layer that value-**translates** a coded v2
|
|
1080
|
+
* field — not merely carrying its code structurally, but mapping the source table code to the FHIR
|
|
1081
|
+
* target coding the IG's segment-map `mappedVia` ConceptMap prescribes.
|
|
1082
|
+
*
|
|
1083
|
+
* **Grounded firsthand on the raw published IG ConceptMaps** (`hl7.fhir.uv.v2mappings`, STU1 —
|
|
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**.
|
|
1089
|
+
*
|
|
1090
|
+
* **License posture.** Only maps whose target CodeSystem is **freely redistributable** are
|
|
1091
|
+
* shipped here: the HL7 THO v2 tables (`v2-0162`, `v2-0550`, `v2-0277`, `v2-0161`) and HL7 v3
|
|
1092
|
+
* (`v3-RouteOfAdministration`) are HL7's own, license-clean. The IG's `*-to-sct` value maps —
|
|
1093
|
+
* **RXR-4 method** (`table-hl70165-to-sct`) and **SCH-7 reason** (`table-hl70276-to-sct`) — translate
|
|
1094
|
+
* **into SNOMED CT**, which is **license-encumbered and is NOT bundled**; those fields stay
|
|
1095
|
+
* structurally carried (BYO ConceptMap), never value-translated here (see their message modules).
|
|
1096
|
+
*
|
|
1097
|
+
* **Additive, never mutating.** A successful translation preserves the raw source coding
|
|
1098
|
+
* (its code, display, version, and any `CWE.4/5/6` alternate triplet) and *adds* the derived target
|
|
1099
|
+
* coding — recognition never overwrites or discards what the message said; it augments it with the
|
|
1100
|
+
* IG-mapped equivalent. And it only fires when the field's primary coding is genuinely from the map's
|
|
1101
|
+
* **bound source table** (CWE.3 absent, or naming that table): a field that declares a *foreign*
|
|
1102
|
+
* coding system (a local `99…`, SNOMED, …) is carried structurally and flagged — its code is **never**
|
|
1103
|
+
* asserted to be the standard concept just because the string happens to collide with a table code.
|
|
1104
|
+
*
|
|
1105
|
+
* @packageDocumentation
|
|
1106
|
+
*/
|
|
1107
|
+
|
|
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
|
+
/**
|
|
1128
|
+
* A license-clean v2-table → FHIR value ConceptMap: it translates a **source table code** to its
|
|
1129
|
+
* {@link CodedTarget}, or `undefined` when the code is not in the map's *mapped* group (the IG's
|
|
1130
|
+
* `(unmapped)` group). Never fabricates a target.
|
|
1131
|
+
*/
|
|
1132
|
+
interface CodedValueMap {
|
|
1133
|
+
/** The IG ConceptMap id this map transcribes (its citation). */
|
|
1134
|
+
readonly name: string;
|
|
1135
|
+
/** The source table's canonical CodeSystem URI (what the raw v2 code is a member of when mapped). */
|
|
1136
|
+
readonly sourceSystem: string;
|
|
1137
|
+
/**
|
|
1138
|
+
* The CWE.3 (`nameOfCodingSystem`) mnemonics that denote this map's **bound source table** — the
|
|
1139
|
+
* HL7 forms (`"HL70162"`, `"0162"`, …). Translation applies **only** when the field's primary
|
|
1140
|
+
* coding is from the bound table: CWE.3 absent/empty (a positionally-bound bare code) **or** CWE.3 ∈
|
|
1141
|
+
* this set. A CWE that declares a **different** coding system (a local `99…`, or SNOMED) is not a
|
|
1142
|
+
* source-table code, so the map is not applied to it — the raw coding is carried structurally + its
|
|
1143
|
+
* system flagged, never asserted as the standard concept.
|
|
1144
|
+
*/
|
|
1145
|
+
readonly sourceMnemonics: ReadonlySet<string>;
|
|
1146
|
+
/** Translate a source table code to its FHIR target, or `undefined` when the IG map has no target. */
|
|
1147
|
+
readonly translate: (code: string) => CodedTarget | undefined;
|
|
1148
|
+
}
|
|
1149
|
+
/**
|
|
1150
|
+
* **RXR-1 Route** — IG `ConceptMap/table-hl70162-to-v2-0162`. Two IG groups, transcribed verbatim:
|
|
1151
|
+
* a 41-code **identity** group (source `v2-0162` → target `v2-0162`, each `equivalent`) and a 6-code
|
|
1152
|
+
* **remap** group into `v3-RouteOfAdministration` (`ID→IDINJ`, `IM→IM`, `IV→IVINJ`, `PO→PO`, `SC→SQ`,
|
|
1153
|
+
* `TD→TRNSDERM`, each `equivalent`, with the IG target displays). The two source-code sets are
|
|
1154
|
+
* disjoint. Any other v2-0162 code (or a non-table code) is unmapped → the raw coding is preserved
|
|
1155
|
+
* and flagged, never coerced.
|
|
1156
|
+
*/
|
|
1157
|
+
declare const ROUTE_VALUE_MAP: CodedValueMap;
|
|
1158
|
+
/**
|
|
1159
|
+
* **RXR-2 Administration Site** — IG `ConceptMap/table-hl70550-to-v2-0550`. A single **identity**
|
|
1160
|
+
* group of 443 body-part codes (source `v2-0550` → target `v2-0550`, each `equivalent`), transcribed
|
|
1161
|
+
* verbatim from the published STU1 ConceptMap — **including** its as-published data-quality artifacts
|
|
1162
|
+
* (`CHESTÂ`, a lone `Â`, `KIDNÂ`, where a stray U+00C2 leaked from the IG's source
|
|
1163
|
+
* encoding). They are preserved for source-fidelity; they are inert (a clean `CHEST`/`KIDN` simply
|
|
1164
|
+
* falls through to unmapped). A code not in the table is unmapped → raw coding preserved + flagged.
|
|
1165
|
+
*/
|
|
1166
|
+
declare const SITE_VALUE_MAP: CodedValueMap;
|
|
1167
|
+
/**
|
|
1168
|
+
* **SCH-8 Appointment Type** — IG `ConceptMap/table-hl70277-to-v2-0277`. A single **identity** group
|
|
1169
|
+
* of the three v2-0277 codes (`Normal`, `Tentative`, `Complete` → themselves, each `equivalent`,
|
|
1170
|
+
* source and target both `v2-0277`). Any other code is unmapped → raw coding preserved + flagged.
|
|
1171
|
+
*/
|
|
1172
|
+
declare const APPOINTMENT_TYPE_VALUE_MAP: CodedValueMap;
|
|
1173
|
+
/**
|
|
1174
|
+
* **RXO-9 Allow Substitutions** — IG `ConceptMap/table-hl70161-to-v2-0161`. A single **identity**
|
|
1175
|
+
* group of the three v2-0161 codes (`N` NOT authorized, `G` generic, `T` therapeutic → themselves,
|
|
1176
|
+
* each `equivalent`, source and target both `v2-0161`), the target for
|
|
1177
|
+
* `MedicationRequest.substitution.allowedCodeableConcept`. Any other code is unmapped → the
|
|
1178
|
+
* substitution is withheld + flagged, never a fabricated substitution permission.
|
|
1179
|
+
*/
|
|
1180
|
+
declare const SUBSTITUTION_VALUE_MAP: CodedValueMap;
|
|
1181
|
+
/**
|
|
1182
|
+
* The bound-table target for a coded element's **primary** coding, or `undefined` when the map does
|
|
1183
|
+
* not faithfully apply — i.e. the primary declares a coding system that is **not** this map's bound
|
|
1184
|
+
* table (CWE.3 present and not in {@link CodedValueMap.sourceMnemonics}), the primary code is empty, or
|
|
1185
|
+
* the code is in the IG map's `(unmapped)` group. Never asserts a standard translation for a foreign
|
|
1186
|
+
* coding system, and never fabricates a target.
|
|
1187
|
+
*
|
|
1188
|
+
* @param cwe - The parsed coded element (its `identifier` is the candidate source-table code).
|
|
1189
|
+
* @param map - The license-clean value map to consider applying.
|
|
1190
|
+
* @example
|
|
1191
|
+
* ```ts
|
|
1192
|
+
* import { translateBound, ROUTE_VALUE_MAP } from "@cosyte/transform";
|
|
1193
|
+
* translateBound({ identifier: "IM" }, ROUTE_VALUE_MAP)?.code; // "IM" (v3 route)
|
|
1194
|
+
* translateBound({ identifier: "IM", nameOfCodingSystem: "99LOCAL" }, ROUTE_VALUE_MAP); // undefined
|
|
1195
|
+
* ```
|
|
1196
|
+
*/
|
|
1197
|
+
declare function translateBound(cwe: CodedElement, map: CodedValueMap): CodedTarget | undefined;
|
|
1198
|
+
/**
|
|
1199
|
+
* Build a FHIR `CodeableConcept` node for a {@link CodedTarget} — the single translated coding, with
|
|
1200
|
+
* an optional free-`text`. Used by translate-or-withhold callers (e.g. substitution) that build the
|
|
1201
|
+
* CodeableConcept only on a successful translation.
|
|
1202
|
+
*
|
|
1203
|
+
* @param target - The translated target coding.
|
|
1204
|
+
* @param text - The original free text (`CWE.9`) to carry as `CodeableConcept.text`, if any.
|
|
1205
|
+
* @example
|
|
1206
|
+
* ```ts
|
|
1207
|
+
* import { codeableConceptFromTarget, V2_0161_SYSTEM } from "@cosyte/transform";
|
|
1208
|
+
* const cc = codeableConceptFromTarget({ system: V2_0161_SYSTEM, code: "G" });
|
|
1209
|
+
* void cc;
|
|
1210
|
+
* ```
|
|
1211
|
+
*/
|
|
1212
|
+
declare function codeableConceptFromTarget(target: CodedTarget, text?: string): FhirComplex;
|
|
1213
|
+
/**
|
|
1214
|
+
* Value-translate a coded v2 element to a FHIR `CodeableConcept` **via** a license-clean
|
|
1215
|
+
* {@link CodedValueMap}, **additively** and fail-safe — never mutating or discarding what the message
|
|
1216
|
+
* said:
|
|
1217
|
+
*
|
|
1218
|
+
* - **Primary code recognized as a bound-table code** ({@link translateBound} — CWE.3 absent or names
|
|
1219
|
+
* the bound table, and the code is in the IG map's mapped group) → the **primary** coding is emitted
|
|
1220
|
+
* in the recognized source-table system (`map.sourceSystem`, carrying `CWE.7` version) and the
|
|
1221
|
+
* **derived** target coding is *appended* when it differs (a remap such as `SC→SQ`); the **alternate
|
|
1222
|
+
* triplet** (`CWE.4/5/6`, via {@link buildCoding}, with its own system resolution + flags) and the
|
|
1223
|
+
* original `CWE.9` text are preserved. No primary unmapped flag — the value was faithfully recognized.
|
|
1224
|
+
* - **Otherwise** — the primary declares a **foreign** coding system (not the bound table), the code is
|
|
1225
|
+
* empty, or it is in the IG's `(unmapped)` group → this defers entirely to the **structural**
|
|
1226
|
+
* {@link toFhirCodeableConcept}, which preserves the raw coding (and its `CWE.4/5/6` alternate and
|
|
1227
|
+
* `CWE.7` version) and flags it ({@link ISSUE_CODES.TRANSFORM_CODE_UNMAPPED} /
|
|
1228
|
+
* `TRANSFORM_CODE_SYSTEM_UNRESOLVED`). A target is **never** fabricated, and a code that declares a
|
|
1229
|
+
* foreign system is **never** asserted to be the standard concept.
|
|
1230
|
+
*
|
|
1231
|
+
* @param cwe - The parsed `@cosyte/hl7` coded element (its `identifier` is the candidate table code).
|
|
1232
|
+
* @param map - The license-clean value map to apply (e.g. {@link ROUTE_VALUE_MAP}).
|
|
1233
|
+
* @param ctx - The transform context (threaded to the structural path + the alternate-triplet resolver).
|
|
1234
|
+
* @example
|
|
1235
|
+
* ```ts
|
|
1236
|
+
* import { toFhirCodeableConceptVia, ROUTE_VALUE_MAP } from "@cosyte/transform";
|
|
1237
|
+
* const { value } = toFhirCodeableConceptVia({ identifier: "IM" }, ROUTE_VALUE_MAP);
|
|
1238
|
+
* // coding === [{ system: ".../v2-0162", code: "IM" },
|
|
1239
|
+
* // { system: ".../v3-RouteOfAdministration", code: "IM", display: "Injection, intramuscular" }]
|
|
1240
|
+
* void value;
|
|
1241
|
+
* ```
|
|
1242
|
+
*/
|
|
1243
|
+
declare function toFhirCodeableConceptVia(cwe: CodedElement, map: CodedValueMap, ctx?: TransformContext): ConvertResult<FhirComplex>;
|
|
1244
|
+
|
|
1245
|
+
/**
|
|
1246
|
+
* Public entry point for `@cosyte/transform` — the HL7 v2 → FHIR R4 transformation library.
|
|
1247
|
+
*
|
|
1248
|
+
* `@cosyte/transform` is **not** a parser; it is a **consumer** one tier above the parser suite. It
|
|
1249
|
+
* takes already-parsed `@cosyte/hl7` composites and produces validated `@cosyte/fhir` model nodes,
|
|
1250
|
+
* grounded on the official **HL7 Version 2 to FHIR** Implementation Guide (`hl7.fhir.uv.v2mappings`,
|
|
1251
|
+
* STU Edition 1). Its whole promise is narrow and honest: **IG-grounded, fail-safe transformation
|
|
1252
|
+
* with typed, value-free diagnostics — never a confident wrong FHIR value.**
|
|
1253
|
+
*
|
|
1254
|
+
* This module ships the six safety-critical datatype converters, the immutable
|
|
1255
|
+
* `OperationOutcome`-shaped diagnostic channel and the NamingSystem resolver they consult,
|
|
1256
|
+
* the message-level assembly — HL7 v2 **ADT → FHIR Patient + Encounter**, the
|
|
1257
|
+
* **ORU^R01 → DiagnosticReport + Observation** results graph, the order-entry graph
|
|
1258
|
+
* (**ORM_O01 / OML_O21 → ServiceRequest** and **RXO → MedicationRequest**), and the thin IG
|
|
1259
|
+
* singles (**VXU_V04 → Immunization**, **SIU_S12 → Appointment**, **MDM_T02 → DocumentReference**),
|
|
1260
|
+
* all through `toFhir(msg)` — and the **terminology value-translation** layer: a
|
|
1261
|
+
* `$translate`-shaped {@link toFhirCodeableConceptVia} engine applying the license-clean IG value
|
|
1262
|
+
* ConceptMaps to the previously structural-only coded fields (route/site, appointment type, order
|
|
1263
|
+
* priority, substitution). Terminology **depth beyond these maps**, profiles, and the reverse
|
|
1264
|
+
* FHIR → v2 direction are not implemented.
|
|
1265
|
+
*
|
|
1266
|
+
* @packageDocumentation
|
|
1267
|
+
*/
|
|
1268
|
+
/** The library version string, synced with `package.json#version` by the release tooling. */
|
|
1269
|
+
declare const VERSION = "0.0.0";
|
|
1270
|
+
|
|
1271
|
+
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 };
|