@cosyte/transform 0.0.9 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var fhir = require('@cosyte/fhir');
4
- var hl7 = require('@cosyte/hl7');
5
4
  var crypto = require('crypto');
5
+ var hl7 = require('@cosyte/hl7');
6
6
 
7
7
  // src/diagnostics/codes.ts
8
8
  var ISSUE_CODES = {
@@ -54,7 +54,63 @@ var ISSUE_CODES = {
54
54
  /** A FHIR-required element's value could not be faithfully derived from the source, so it was
55
55
  * emitted with a `data-absent-reason` extension (value `unknown`) rather than fabricated, e.g.
56
56
  * `MessageHeader.source.endpoint` from an MSH-3 application namespace that is not a URL. */
57
- TRANSFORM_REQUIRED_ELEMENT_UNKNOWN: "TRANSFORM_REQUIRED_ELEMENT_UNKNOWN"
57
+ TRANSFORM_REQUIRED_ELEMENT_UNKNOWN: "TRANSFORM_REQUIRED_ELEMENT_UNKNOWN",
58
+ /** A reverse (FHIR to v2) conversion was called without the explicit message trigger it requires.
59
+ * No FHIR resource carries a v2 trigger, so it is never inferred from resource content and never
60
+ * defaulted: no message is built and no builder is called. */
61
+ TRANSFORM_MISSING_TRIGGER: "TRANSFORM_MISSING_TRIGGER",
62
+ /** A reverse (FHIR to v2) conversion was handed a resource outside the narrow set of shapes it
63
+ * supports. No v2 message is emitted and no segment layout is guessed. */
64
+ TRANSFORM_UNSUPPORTED_RESOURCE: "TRANSFORM_UNSUPPORTED_RESOURCE",
65
+ /** A resource handed to a reverse (FHIR to v2) conversion is not structurally a FHIR resource of
66
+ * its expected type (not an object, no `resourceType`, or an element carrying the wrong node
67
+ * kind). Nothing is emitted and nothing is thrown. */
68
+ TRANSFORM_RESOURCE_MALFORMED: "TRANSFORM_RESOURCE_MALFORMED",
69
+ /** A populated FHIR element has no v2 field in the narrow reverse map that covers this shape
70
+ * (the IG map defines no source for it, or its inverse is not implemented here). The element is
71
+ * surfaced and left out, never approximated into a neighbouring v2 field. */
72
+ TRANSFORM_NO_V2_TARGET: "TRANSFORM_NO_V2_TARGET",
73
+ /** A FHIR value cannot be carried into its v2 target without altering it (a lexical form v2 has
74
+ * no representation for, or content that exceeds the target's component structure). The v2 field
75
+ * is left absent rather than truncated, rounded, or coerced. */
76
+ TRANSFORM_VALUE_NOT_REPRESENTABLE: "TRANSFORM_VALUE_NOT_REPRESENTABLE",
77
+ /** A FHIR code has no usable inverse in the IG ConceptMap that governs its field: either several
78
+ * v2 source codes map onto it (so the inverse is ambiguous) or the map has no row for it at all.
79
+ * The v2 field is left absent, never resolved to one arbitrary member of the ambiguous set. */
80
+ TRANSFORM_CODE_NOT_INVERTIBLE: "TRANSFORM_CODE_NOT_INVERTIBLE",
81
+ /** A `Coding.system` is not a system this library can name with a v2 coding-system mnemonic, so
82
+ * the coding cannot be written into a v2 coded field. It is flagged rather than emitted with no
83
+ * table context or with a code from an unrelated table. */
84
+ TRANSFORM_CODE_SYSTEM_NOT_V2: "TRANSFORM_CODE_SYSTEM_NOT_V2",
85
+ /** A v2 field the segment's own attribute table marks **required** (usage `R`) is absent from an
86
+ * emitted segment, because the FHIR resource carried no source this reverse map could ground it
87
+ * from, or the source it carried had no faithful v2 form. The field is left absent per v2
88
+ * optionality rules and **declared here**: a placeholder is never written to satisfy v2 structure,
89
+ * and the receiver is never left to discover the gap. Distinct from
90
+ * {@link ISSUE_CODES.TRANSFORM_REQUIRED_ELEMENT_UNKNOWN}, which is the forward direction's
91
+ * FHIR-required element. */
92
+ TRANSFORM_V2_REQUIRED_FIELD_ABSENT: "TRANSFORM_V2_REQUIRED_FIELD_ABSENT",
93
+ /** A reverse (FHIR to v2) conversion produced **no message at all**: nothing in the resource
94
+ * grounded a single field of the target segment, and an empty segment is never emitted. Raised so
95
+ * that an empty-handed conversion is never indistinguishable from a successful one, and separate
96
+ * from the refusals that name their own cause (an absent trigger, an unsupported resource type, a
97
+ * structurally malformed resource). */
98
+ TRANSFORM_NO_V2_MESSAGE_EMITTED: "TRANSFORM_NO_V2_MESSAGE_EMITTED",
99
+ /** A segment the message carried did **not** reach any resource in the returned bundle, and the IG
100
+ * *does* publish a segment map for its name: a **library gap**, fixable here. Raised once per
101
+ * occurrence, located `NAME[k]` with `k` counted 1-based among the occurrences of that name
102
+ * (`DG1[1]`, `DG1[2]`), so a consumer can tell how much of the message the bundle represents
103
+ * without diffing the two by hand. The occurrence may have been read and refused rather than never
104
+ * read: being counted, or already named by another issue, is not the same as reaching a resource. */
105
+ TRANSFORM_SEGMENT_NOT_EMITTED: "TRANSFORM_SEGMENT_NOT_EMITTED",
106
+ /** A segment the message carried did **not** reach any resource in the returned bundle, and its
107
+ * name is **not** one the IG publishes a segment map for: a **standard gap**, not fixable here.
108
+ * Custom Z-segments and `RXE` land on this code, and so does a name this library could not
109
+ * classify at all, because a damaged segment identifier is never re-derived from the line: an
110
+ * `AL1` whose identifier arrived as `AL11` reads as unclassifiable, not as an IG omission. Such an
111
+ * occurrence is located `[#n]` by its 1-based position among the segments of the message, with no
112
+ * name part at all. */
113
+ TRANSFORM_SEGMENT_NO_IG_MAP: "TRANSFORM_SEGMENT_NO_IG_MAP"
58
114
  };
59
115
  var FATAL_CODES = {
60
116
  /** The input carried nothing to transform. */
@@ -129,6 +185,61 @@ var ISSUE_REGISTRY = Object.freeze({
129
185
  severity: "information",
130
186
  fhirIssueType: "informational",
131
187
  message: "required FHIR element could not be derived from the source; emitted with a data-absent-reason extension, never fabricated."
188
+ },
189
+ [ISSUE_CODES.TRANSFORM_MISSING_TRIGGER]: {
190
+ severity: "error",
191
+ fhirIssueType: "required",
192
+ message: "reverse conversion requires an explicit v2 trigger from the caller; no resource carries one, so none was inferred and no message was built."
193
+ },
194
+ [ISSUE_CODES.TRANSFORM_UNSUPPORTED_RESOURCE]: {
195
+ severity: "error",
196
+ fhirIssueType: "not-supported",
197
+ message: "resource type is outside the reverse converter's supported shapes; no v2 message emitted and no segment layout guessed."
198
+ },
199
+ [ISSUE_CODES.TRANSFORM_RESOURCE_MALFORMED]: {
200
+ severity: "error",
201
+ fhirIssueType: "structure",
202
+ message: "input is not a structurally well-shaped FHIR resource for this conversion; nothing emitted, nothing thrown."
203
+ },
204
+ [ISSUE_CODES.TRANSFORM_NO_V2_TARGET]: {
205
+ severity: "information",
206
+ fhirIssueType: "informational",
207
+ message: "populated FHIR element has no v2 field in this reverse map; surfaced and left out, never approximated into a neighbouring field."
208
+ },
209
+ [ISSUE_CODES.TRANSFORM_VALUE_NOT_REPRESENTABLE]: {
210
+ severity: "warning",
211
+ fhirIssueType: "value",
212
+ message: "FHIR value cannot be carried into its v2 target without altering it; the v2 field is left absent, never truncated, rounded, or coerced."
213
+ },
214
+ [ISSUE_CODES.TRANSFORM_CODE_NOT_INVERTIBLE]: {
215
+ severity: "warning",
216
+ fhirIssueType: "code-invalid",
217
+ message: "FHIR code has no usable inverse in the governing IG ConceptMap (ambiguous or absent); v2 field left absent, never resolved arbitrarily."
218
+ },
219
+ [ISSUE_CODES.TRANSFORM_CODE_SYSTEM_NOT_V2]: {
220
+ severity: "warning",
221
+ fhirIssueType: "code-invalid",
222
+ message: "coding system has no v2 coding-system mnemonic here; coding flagged rather than written with no table context or from an unrelated table."
223
+ },
224
+ [ISSUE_CODES.TRANSFORM_V2_REQUIRED_FIELD_ABSENT]: {
225
+ severity: "error",
226
+ fhirIssueType: "required",
227
+ message: "a v2-required field is absent from the emitted segment because the resource carried no source this map could ground it from; left absent per v2 optionality rules and declared here, never filled with a placeholder."
228
+ },
229
+ [ISSUE_CODES.TRANSFORM_NO_V2_MESSAGE_EMITTED]: {
230
+ severity: "error",
231
+ fhirIssueType: "processing",
232
+ message: "no v2 message was emitted: nothing in the resource grounded a single field of the target segment, and an empty segment is never emitted."
233
+ },
234
+ [ISSUE_CODES.TRANSFORM_SEGMENT_NOT_EMITTED]: {
235
+ severity: "information",
236
+ fhirIssueType: "informational",
237
+ message: "segment occurrence reached no resource in the returned bundle, and the IG publishes a segment map for its name; the bundle does not represent what this segment carried."
238
+ },
239
+ [ISSUE_CODES.TRANSFORM_SEGMENT_NO_IG_MAP]: {
240
+ severity: "information",
241
+ fhirIssueType: "informational",
242
+ message: "segment occurrence reached no resource in the returned bundle, and the IG publishes no segment map for its name, or the name could not be classified at all; the bundle does not represent what this segment carried."
132
243
  }
133
244
  });
134
245
  function issue(code, v2Location, fhirPath) {
@@ -512,6 +623,14 @@ var V3_ROUTE_OF_ADMINISTRATION_SYSTEM = "http://terminology.hl7.org/CodeSystem/v
512
623
  var V2_0550_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0550";
513
624
  var V2_0277_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0277";
514
625
  var V2_0161_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0161";
626
+ var V2_0127_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0127";
627
+ var V2_0128_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0128";
628
+ var ALLERGY_INTOLERANCE_CATEGORY_SYSTEM = "http://hl7.org/fhir/allergy-intolerance-category";
629
+ var ALLERGY_INTOLERANCE_TYPE_SYSTEM = "http://hl7.org/fhir/allergy-intolerance-type";
630
+ var ALLERGY_INTOLERANCE_CRITICALITY_SYSTEM = "http://hl7.org/fhir/allergy-intolerance-criticality";
631
+ var V2_0335_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0335";
632
+ var V2_0528_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0528";
633
+ var V3_TIMING_EVENT_SYSTEM = "http://terminology.hl7.org/CodeSystem/v3-TimingEvent";
515
634
  function valueMap(name, sourceSystem, sourceMnemonics, targets) {
516
635
  return Object.freeze({
517
636
  name,
@@ -1059,6 +1178,121 @@ var SUBSTITUTION_VALUE_MAP = valueMap(
1059
1178
  ["HL70161", "0161"],
1060
1179
  identityTargets(V2_0161_SYSTEM, ["N", "G", "T"])
1061
1180
  );
1181
+ var IG_ALLERGY_VALUE_MAPS_VERSION = "1.0.0";
1182
+ var IG_ALLERGY_VALUE_MAPS_PUBLISHED = "2025-10-07";
1183
+ var IG_ALLERGY_VALUE_MAPS_RETRIEVED = "2026-08-28";
1184
+ var IG_ALLERGY_VALUE_MAPS_SOURCE = "https://hl7.org/fhir/uv/v2mappings/ConceptMap-segment-al1-to-allergyintolerance.html";
1185
+ var ALLERGY_CATEGORY_VALUE_MAP = valueMap(
1186
+ "table-hl70127-to-allergy-intolerance-category",
1187
+ V2_0127_SYSTEM,
1188
+ ["HL70127", "0127"],
1189
+ {
1190
+ DA: { system: ALLERGY_INTOLERANCE_CATEGORY_SYSTEM, code: "medication", display: "Medication" },
1191
+ FA: { system: ALLERGY_INTOLERANCE_CATEGORY_SYSTEM, code: "food", display: "Food" },
1192
+ EA: {
1193
+ system: ALLERGY_INTOLERANCE_CATEGORY_SYSTEM,
1194
+ code: "environment",
1195
+ display: "Environment"
1196
+ },
1197
+ AA: { system: ALLERGY_INTOLERANCE_CATEGORY_SYSTEM, code: "biologic", display: "Biologic" },
1198
+ PA: {
1199
+ system: ALLERGY_INTOLERANCE_CATEGORY_SYSTEM,
1200
+ code: "environment",
1201
+ display: "Environment"
1202
+ },
1203
+ LA: {
1204
+ system: ALLERGY_INTOLERANCE_CATEGORY_SYSTEM,
1205
+ code: "environment",
1206
+ display: "Environment"
1207
+ }
1208
+ }
1209
+ );
1210
+ var ALLERGY_TYPE_VALUE_MAP = valueMap(
1211
+ "table-hl70127-to-allergy-intolerance-type",
1212
+ V2_0127_SYSTEM,
1213
+ ["HL70127", "0127"],
1214
+ {
1215
+ DA: { system: ALLERGY_INTOLERANCE_TYPE_SYSTEM, code: "allergy", display: "Allergy" },
1216
+ FA: { system: ALLERGY_INTOLERANCE_TYPE_SYSTEM, code: "allergy", display: "Allergy" },
1217
+ MA: { system: ALLERGY_INTOLERANCE_TYPE_SYSTEM, code: "allergy", display: "Allergy" },
1218
+ EA: { system: ALLERGY_INTOLERANCE_TYPE_SYSTEM, code: "allergy", display: "Allergy" },
1219
+ AA: { system: ALLERGY_INTOLERANCE_TYPE_SYSTEM, code: "allergy", display: "Allergy" },
1220
+ PA: { system: ALLERGY_INTOLERANCE_TYPE_SYSTEM, code: "allergy", display: "Allergy" },
1221
+ LA: { system: ALLERGY_INTOLERANCE_TYPE_SYSTEM, code: "allergy", display: "Allergy" }
1222
+ }
1223
+ );
1224
+ var ALLERGY_CRITICALITY_VALUE_MAP = valueMap(
1225
+ "table-hl70128-to-allergy-intolerance-criticality",
1226
+ V2_0128_SYSTEM,
1227
+ ["HL70128", "0128"],
1228
+ {
1229
+ SV: { system: ALLERGY_INTOLERANCE_CRITICALITY_SYSTEM, code: "high", display: "High Risk" },
1230
+ MI: { system: ALLERGY_INTOLERANCE_CRITICALITY_SYSTEM, code: "low", display: "Low Risk" }
1231
+ }
1232
+ );
1233
+ var ALLERGY_ORIGINAL_CATEGORY_VALUE_MAP = valueMap(
1234
+ "table-hl70127-to-v2-0127",
1235
+ V2_0127_SYSTEM,
1236
+ ["HL70127", "0127"],
1237
+ identityTargets(V2_0127_SYSTEM, ["DA", "FA", "MA", "MC", "EA", "AA", "PA", "LA"])
1238
+ );
1239
+ var ALLERGY_ORIGINAL_CRITICALITY_VALUE_MAP = valueMap(
1240
+ "table-hl70128-original-to-v2-0128",
1241
+ V2_0128_SYSTEM,
1242
+ ["HL70128", "0128"],
1243
+ identityTargets(V2_0128_SYSTEM, ["SV", "MO", "MI", "U"])
1244
+ );
1245
+ var TIMING_EVENT_VALUE_MAP = valueMap(
1246
+ "table-hl70528-to-v3-timingevent",
1247
+ V2_0528_SYSTEM,
1248
+ ["HL70528", "0528"],
1249
+ identityTargets(V3_TIMING_EVENT_SYSTEM, [
1250
+ "HS",
1251
+ "AC",
1252
+ "PC",
1253
+ "ACM",
1254
+ "ACD",
1255
+ "ACV",
1256
+ "PCM",
1257
+ "PCD",
1258
+ "PCV"
1259
+ ])
1260
+ );
1261
+ var REPEAT_PATTERN_LITERALS = Object.freeze(
1262
+ /* @__PURE__ */ new Set([
1263
+ "QAM",
1264
+ "QSHIFT",
1265
+ "QHS",
1266
+ "QPM",
1267
+ "C",
1268
+ "PRN",
1269
+ "Once",
1270
+ "A",
1271
+ "P",
1272
+ "I",
1273
+ "M",
1274
+ "D",
1275
+ "V",
1276
+ "BID",
1277
+ "TID",
1278
+ "QID",
1279
+ "QOD"
1280
+ ])
1281
+ );
1282
+ var REPEAT_PATTERN_SHAPES = Object.freeze([
1283
+ /^Q[0-9]+[SMHDWL]$/,
1284
+ /^Q[0-9]+J[1-7]$/,
1285
+ /^(?:[5-9]|[1-9][0-9]+)ID$/,
1286
+ /^U .+$/,
1287
+ /^[API]C[MDV]$/
1288
+ ]);
1289
+ function isRepeatPatternRow(code) {
1290
+ return REPEAT_PATTERN_LITERALS.has(code) || REPEAT_PATTERN_SHAPES.some((s) => s.test(code));
1291
+ }
1292
+ function isRepeatPatternCode(code) {
1293
+ if (isRepeatPatternRow(code)) return true;
1294
+ return code.startsWith("PRN") && code.length > 3 && isRepeatPatternRow(code.slice(3));
1295
+ }
1062
1296
  function codingFrom(system, code, display, version) {
1063
1297
  const props = [{ name: "system", value: fhir.primitive(system) }];
1064
1298
  if (version !== void 0 && version !== "")
@@ -1161,15 +1395,199 @@ function orderIdentifier(value, typeCode) {
1161
1395
  { name: "value", value: fhir.primitive(value) }
1162
1396
  ]);
1163
1397
  }
1398
+ function component(field, index) {
1399
+ return field.repetitions[0]?.components[index]?.subcomponents[0] ?? "";
1400
+ }
1401
+ function toFhirEntityIdentifier(field) {
1402
+ const entityId = component(field, 0);
1403
+ const authorityValued = component(field, 1) !== "" || component(field, 2) !== "" || component(field, 3) !== "";
1404
+ return {
1405
+ identifier: entityId === "" ? void 0 : fhir.complex([{ name: "value", value: fhir.primitive(entityId) }]),
1406
+ authorityValued
1407
+ };
1408
+ }
1409
+ function withCodeableText(concept, textValue) {
1410
+ return fhir.complex([
1411
+ ...concept.properties.filter((p) => p.name !== "text"),
1412
+ { name: "text", value: fhir.primitive(textValue) }
1413
+ ]);
1414
+ }
1164
1415
  function dataAbsent(reasonCode) {
1165
- const ext = fhir.complex([
1416
+ return fhir.primitive(void 0, { extension: [dataAbsentReason(reasonCode)] });
1417
+ }
1418
+ function dataAbsentReason(reasonCode) {
1419
+ return fhir.complex([
1166
1420
  { name: "url", value: fhir.primitive(DATA_ABSENT_REASON_URL) },
1167
1421
  { name: "valueCode", value: fhir.primitive(reasonCode) }
1168
1422
  ]);
1169
- return fhir.primitive(void 0, { extension: [ext] });
1423
+ }
1424
+ function dataAbsentComplex(reasonCode) {
1425
+ return fhir.complex([{ name: "extension", value: fhir.list([dataAbsentReason(reasonCode)]) }]);
1170
1426
  }
1171
1427
 
1172
- // src/messages/appointment.ts
1428
+ // src/messages/allergy-intolerance.ts
1429
+ var ALLERGY_CLINICAL_STATUS_SYSTEM = "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical";
1430
+ var ALLERGY_CLINICAL_STATUS_CODE = "active";
1431
+ var ALTERNATE_CODES_EXTENSION_URL = "http://hl7.org/fhir/StructureDefinition/alternate-codes";
1432
+ var AL1_ONSET_WITHDRAWN_AT = Object.freeze([2, 7]);
1433
+ var READABLE_VERSION = /^\d+(?:\.\d+)*$/;
1434
+ function carriesLegacyOnsetDate(version) {
1435
+ if (version === void 0 || !READABLE_VERSION.test(version)) return false;
1436
+ const parts = version.split(".").map(Number);
1437
+ for (let i = 0; i < AL1_ONSET_WITHDRAWN_AT.length; i++) {
1438
+ const part = parts[i] ?? 0;
1439
+ const bound = AL1_ONSET_WITHDRAWN_AT[i] ?? 0;
1440
+ if (part !== bound) return part < bound;
1441
+ }
1442
+ return false;
1443
+ }
1444
+ function collectAllergies(msg) {
1445
+ return msg.allSegments().filter((seg) => seg.type === "AL1");
1446
+ }
1447
+ function alternateCodesExtension(seg, field, map, ctx, issues) {
1448
+ const original = toFhirCodeableConceptVia(seg.field(field).asCwe(), map, ctx);
1449
+ issues.push(...original.issues);
1450
+ if (original.value === void 0) return void 0;
1451
+ return fhir.complex([
1452
+ { name: "url", value: fhir.primitive(ALTERNATE_CODES_EXTENSION_URL) },
1453
+ { name: "valueCodeableConcept", value: original.value }
1454
+ ]);
1455
+ }
1456
+ function translatedCodeElement(seg, field, targetMap, originalMap, carryAlternateCodes, v2Location, fhirPath, ctx, issues) {
1457
+ const extension = carryAlternateCodes ? alternateCodesExtension(seg, field, originalMap, ctx, issues) : void 0;
1458
+ const target = translateBound(seg.field(field).asCwe(), targetMap);
1459
+ if (target === void 0) {
1460
+ issues.push(issue(ISSUE_CODES.TRANSFORM_CODE_UNMAPPED, v2Location, fhirPath));
1461
+ }
1462
+ if (target === void 0 && extension === void 0) return void 0;
1463
+ return extension === void 0 ? fhir.primitive(target?.code) : fhir.primitive(target?.code, { extension: [extension] });
1464
+ }
1465
+ function buildReaction(al1) {
1466
+ const manifestations = al1.field(5).repetitions.map((rep) => rep.components[0]?.subcomponents[0] ?? "").filter((text2) => text2 !== "").map((text2) => fhir.complex([{ name: "text", value: fhir.primitive(text2) }]));
1467
+ if (manifestations.length === 0) return void 0;
1468
+ return fhir.list([fhir.complex([{ name: "manifestation", value: fhir.list(manifestations) }])]);
1469
+ }
1470
+ function buildOnset(al1, version, ctx, issues) {
1471
+ if (al1.field(6).value === "") return void 0;
1472
+ if (!carriesLegacyOnsetDate(version)) {
1473
+ issues.push(
1474
+ issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "AL1.6", "AllergyIntolerance.onsetDateTime")
1475
+ );
1476
+ return void 0;
1477
+ }
1478
+ const onset = toFhirDateTime(al1.field(6).asTs(), ctx.options);
1479
+ issues.push(...onset.issues);
1480
+ return onset.value === void 0 ? void 0 : fhir.primitive(onset.value);
1481
+ }
1482
+ function buildAllergyIntolerance(al1, patientFullUrl, version, ctx, options = {}) {
1483
+ const issues = [];
1484
+ const carryAlternateCodes = options.carryAlternateCodes ?? true;
1485
+ const allergen = toFhirCodeableConcept(al1.field(3).asCwe(), ctx);
1486
+ if (allergen.value === void 0) {
1487
+ return {
1488
+ value: void 0,
1489
+ issues: [
1490
+ ...allergen.issues,
1491
+ issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "AL1.3", "AllergyIntolerance.code")
1492
+ ]
1493
+ };
1494
+ }
1495
+ issues.push(...allergen.issues);
1496
+ const allergenTypeValued = al1.field(2).value !== "";
1497
+ const type = allergenTypeValued ? translatedCodeElement(
1498
+ al1,
1499
+ 2,
1500
+ ALLERGY_TYPE_VALUE_MAP,
1501
+ ALLERGY_ORIGINAL_CATEGORY_VALUE_MAP,
1502
+ false,
1503
+ "AL1.2",
1504
+ "AllergyIntolerance.type",
1505
+ ctx,
1506
+ issues
1507
+ ) : void 0;
1508
+ const category = allergenTypeValued ? translatedCodeElement(
1509
+ al1,
1510
+ 2,
1511
+ ALLERGY_CATEGORY_VALUE_MAP,
1512
+ ALLERGY_ORIGINAL_CATEGORY_VALUE_MAP,
1513
+ carryAlternateCodes,
1514
+ "AL1.2",
1515
+ "AllergyIntolerance.category",
1516
+ ctx,
1517
+ issues
1518
+ ) : void 0;
1519
+ const criticality = al1.field(4).value !== "" ? translatedCodeElement(
1520
+ al1,
1521
+ 4,
1522
+ ALLERGY_CRITICALITY_VALUE_MAP,
1523
+ ALLERGY_ORIGINAL_CRITICALITY_VALUE_MAP,
1524
+ carryAlternateCodes,
1525
+ "AL1.4",
1526
+ "AllergyIntolerance.criticality",
1527
+ ctx,
1528
+ issues
1529
+ ) : void 0;
1530
+ const onset = buildOnset(al1, version, ctx, issues);
1531
+ const reaction = buildReaction(al1);
1532
+ const props = [
1533
+ { name: "resourceType", value: fhir.primitive("AllergyIntolerance") },
1534
+ // The IG's fixed assignment: not derived from the message, and the reason is on the row itself.
1535
+ {
1536
+ name: "clinicalStatus",
1537
+ value: fhir.complex([
1538
+ {
1539
+ name: "coding",
1540
+ value: fhir.list([
1541
+ fhir.complex([
1542
+ { name: "system", value: fhir.primitive(ALLERGY_CLINICAL_STATUS_SYSTEM) },
1543
+ { name: "code", value: fhir.primitive(ALLERGY_CLINICAL_STATUS_CODE) }
1544
+ ])
1545
+ ])
1546
+ }
1547
+ ])
1548
+ }
1549
+ ];
1550
+ if (type !== void 0) props.push({ name: "type", value: type });
1551
+ if (category !== void 0) props.push({ name: "category", value: fhir.list([category]) });
1552
+ if (criticality !== void 0) props.push({ name: "criticality", value: criticality });
1553
+ props.push({ name: "code", value: allergen.value });
1554
+ props.push({ name: "patient", value: reference(patientFullUrl) });
1555
+ if (onset !== void 0) props.push({ name: "onsetDateTime", value: onset });
1556
+ if (reaction !== void 0) props.push({ name: "reaction", value: reaction });
1557
+ return { value: fhir.complex(props), issues };
1558
+ }
1559
+ function emitAllergyIntolerance(al1, patientFullUrl, version, ctx, gate, v2Location) {
1560
+ const built = buildAllergyIntolerance(al1, patientFullUrl, version, ctx);
1561
+ if (built.value === void 0) return built;
1562
+ if (gate(built.value)) return built;
1563
+ const issues = [...built.issues];
1564
+ const withoutExtension = buildAllergyIntolerance(al1, patientFullUrl, version, ctx, {
1565
+ carryAlternateCodes: false
1566
+ });
1567
+ if (withoutExtension.value !== void 0 && gate(withoutExtension.value)) {
1568
+ if (al1.field(2).value !== "") {
1569
+ issues.push(
1570
+ issue(
1571
+ ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED,
1572
+ "AL1.2",
1573
+ "AllergyIntolerance.category.extension"
1574
+ )
1575
+ );
1576
+ }
1577
+ if (al1.field(4).value !== "") {
1578
+ issues.push(
1579
+ issue(
1580
+ ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED,
1581
+ "AL1.4",
1582
+ "AllergyIntolerance.criticality.extension"
1583
+ )
1584
+ );
1585
+ }
1586
+ return { value: withoutExtension.value, issues };
1587
+ }
1588
+ issues.push(issue(ISSUE_CODES.TRANSFORM_RESOURCE_INVALID, v2Location, "AllergyIntolerance"));
1589
+ return { value: void 0, issues };
1590
+ }
1173
1591
  var APPOINTMENT_STATUS_MAP = Object.freeze({
1174
1592
  Pending: "pending",
1175
1593
  Waitlist: "waitlist",
@@ -1267,6 +1685,190 @@ function collectAppointment(msg) {
1267
1685
  }
1268
1686
  return { sch, ais };
1269
1687
  }
1688
+ var CONDITION_VERIFICATION_STATUS_SYSTEM = "http://terminology.hl7.org/CodeSystem/condition-ver-status";
1689
+ var CONDITION_ENTERED_IN_ERROR = "entered-in-error";
1690
+ var DG1_RETRACTION_ACTION_CODE = "D";
1691
+ function collectDiagnoses(msg) {
1692
+ return msg.allSegments().filter((seg) => seg.type === "DG1");
1693
+ }
1694
+ function deferredDiagnosisIssues(dg1) {
1695
+ const issues = [];
1696
+ if (dg1.field(16).value !== "") {
1697
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "DG1.16", "Condition.asserter"));
1698
+ }
1699
+ if (dg1.field(22).value !== "") {
1700
+ issues.push(
1701
+ issue(
1702
+ ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED,
1703
+ "DG1.22",
1704
+ "Condition.extension[condition-dueTo].valueReference"
1705
+ )
1706
+ );
1707
+ }
1708
+ return issues;
1709
+ }
1710
+ function buildVerificationStatus(dg1, issues) {
1711
+ const action = dg1.field(21).value;
1712
+ if (action === "") return void 0;
1713
+ if (action !== DG1_RETRACTION_ACTION_CODE) {
1714
+ issues.push(
1715
+ issue(ISSUE_CODES.TRANSFORM_CODE_UNMAPPED, "DG1.21", "Condition.verificationStatus")
1716
+ );
1717
+ return void 0;
1718
+ }
1719
+ return fhir.complex([
1720
+ {
1721
+ name: "coding",
1722
+ value: fhir.list([
1723
+ fhir.complex([
1724
+ { name: "system", value: fhir.primitive(CONDITION_VERIFICATION_STATUS_SYSTEM) },
1725
+ { name: "code", value: fhir.primitive(CONDITION_ENTERED_IN_ERROR) }
1726
+ ])
1727
+ ])
1728
+ }
1729
+ ]);
1730
+ }
1731
+ function buildCode(dg1, ctx, issues) {
1732
+ const description = dg1.field(4).value;
1733
+ const coded = toFhirCodeableConcept(dg1.field(3).asCwe(), ctx);
1734
+ issues.push(...coded.issues);
1735
+ if (coded.value === void 0) {
1736
+ return description === "" ? void 0 : fhir.complex([{ name: "text", value: fhir.primitive(description) }]);
1737
+ }
1738
+ return description === "" ? coded.value : withCodeableText(coded.value, description);
1739
+ }
1740
+ function buildDateTime(dg1, field, ctx, issues) {
1741
+ if (dg1.field(field).value === "") return void 0;
1742
+ const converted = toFhirDateTime(dg1.field(field).asTs(), ctx.options);
1743
+ issues.push(...converted.issues);
1744
+ return converted.value === void 0 ? void 0 : fhir.primitive(converted.value);
1745
+ }
1746
+ function buildCondition(dg1, patientFullUrl, ctx, v2Location) {
1747
+ const issues = [];
1748
+ const props = [
1749
+ { name: "resourceType", value: fhir.primitive("Condition") }
1750
+ ];
1751
+ const entity = toFhirEntityIdentifier(dg1.field(20));
1752
+ if (entity.identifier !== void 0) {
1753
+ props.push({ name: "identifier", value: fhir.list([entity.identifier]) });
1754
+ }
1755
+ if (entity.authorityValued) {
1756
+ issues.push(
1757
+ issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "DG1.20", "Condition.identifier.system")
1758
+ );
1759
+ }
1760
+ const verification = buildVerificationStatus(dg1, issues);
1761
+ if (verification !== void 0) props.push({ name: "verificationStatus", value: verification });
1762
+ const code = buildCode(dg1, ctx, issues);
1763
+ if (code === void 0) {
1764
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, v2Location, "Condition.code"));
1765
+ } else {
1766
+ props.push({ name: "code", value: code });
1767
+ }
1768
+ props.push({ name: "subject", value: reference(patientFullUrl) });
1769
+ const onset = buildDateTime(dg1, 5, ctx, issues);
1770
+ if (onset !== void 0) props.push({ name: "onsetDateTime", value: onset });
1771
+ const recorded = buildDateTime(dg1, 19, ctx, issues);
1772
+ if (recorded !== void 0) props.push({ name: "recordedDate", value: recorded });
1773
+ return { value: fhir.complex(props), issues };
1774
+ }
1775
+ var COVERAGE_SUBSCRIBER_ID_EXTENSION_URL = "http://hl7.org/fhir/5.0/StructureDefinition/extension-subscriberId";
1776
+ var COVERAGE_STATUS_UNKNOWN = "unknown";
1777
+ var IN1_SUBSCRIBER_NUMBER_TYPE = "SN";
1778
+ function collectCoverages(msg) {
1779
+ return msg.allSegments().filter((seg) => seg.type === "IN1");
1780
+ }
1781
+ function insuranceCompanyName(in1) {
1782
+ return in1.field(4).repetitions[0]?.components[0]?.subcomponents[0] ?? "";
1783
+ }
1784
+ function deferredCoverageIssues(in1) {
1785
+ const deferred = [
1786
+ [5, "Coverage.payer"],
1787
+ [10, "Coverage.policyHolder"],
1788
+ [11, "Coverage.policyHolder"],
1789
+ [16, "Coverage.subscriber"],
1790
+ [17, "Coverage.relationship"]
1791
+ ];
1792
+ const issues = [];
1793
+ for (const [field, path] of deferred) {
1794
+ if (in1.field(field).value !== "") {
1795
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, `IN1.${String(field)}`, path));
1796
+ }
1797
+ }
1798
+ return issues;
1799
+ }
1800
+ function buildIdentifier(in1, issues) {
1801
+ const cwe = in1.field(2).asCwe();
1802
+ const planId = cwe.identifier;
1803
+ if (planId === void 0 || planId === "") return void 0;
1804
+ if (cwe.nameOfCodingSystem !== void 0 && cwe.nameOfCodingSystem !== "") {
1805
+ issues.push(
1806
+ issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "IN1.2", "Coverage.identifier.system")
1807
+ );
1808
+ }
1809
+ return fhir.complex([{ name: "value", value: fhir.primitive(planId) }]);
1810
+ }
1811
+ function subscriberIdExtension(in1, field, ctx, issues) {
1812
+ const identifier = toFhirIdentifier(in1.field(field).asCx(), ctx);
1813
+ issues.push(...identifier.issues);
1814
+ if (identifier.value === void 0) return void 0;
1815
+ return fhir.complex([
1816
+ { name: "url", value: fhir.primitive(COVERAGE_SUBSCRIBER_ID_EXTENSION_URL) },
1817
+ { name: "valueIdentifier", value: identifier.value }
1818
+ ]);
1819
+ }
1820
+ function buildPeriod(in1, ctx, issues) {
1821
+ const props = [];
1822
+ for (const [field, name] of [
1823
+ [12, "start"],
1824
+ [13, "end"]
1825
+ ]) {
1826
+ if (in1.field(field).value === "") continue;
1827
+ const converted = toFhirDateTime(in1.field(field).asTs(), ctx.options);
1828
+ issues.push(...converted.issues);
1829
+ if (converted.value !== void 0) props.push({ name, value: fhir.primitive(converted.value) });
1830
+ }
1831
+ return props.length === 0 ? void 0 : fhir.complex(props);
1832
+ }
1833
+ function buildCoverage(in1, patientFullUrl, ctx, v2Location) {
1834
+ const issues = [];
1835
+ const payerName = insuranceCompanyName(in1);
1836
+ if (payerName === "") {
1837
+ return {
1838
+ value: void 0,
1839
+ issues: [issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, v2Location, "Coverage.payor")]
1840
+ };
1841
+ }
1842
+ const props = [
1843
+ { name: "resourceType", value: fhir.primitive("Coverage") }
1844
+ ];
1845
+ const extensions = [];
1846
+ if (in1.field(10).asCx().identifierTypeCode === IN1_SUBSCRIBER_NUMBER_TYPE) {
1847
+ const groupId = subscriberIdExtension(in1, 10, ctx, issues);
1848
+ if (groupId !== void 0) extensions.push(groupId);
1849
+ }
1850
+ if (in1.field(49).value !== "") {
1851
+ const insuredId = subscriberIdExtension(in1, 49, ctx, issues);
1852
+ if (insuredId !== void 0) extensions.push(insuredId);
1853
+ }
1854
+ if (extensions.length > 0) props.push({ name: "extension", value: fhir.list(extensions) });
1855
+ const identifier = buildIdentifier(in1, issues);
1856
+ if (identifier !== void 0) props.push({ name: "identifier", value: fhir.list([identifier]) });
1857
+ issues.push(issue(ISSUE_CODES.TRANSFORM_REQUIRED_ELEMENT_UNKNOWN, v2Location, "Coverage.status"));
1858
+ props.push({ name: "status", value: dataAbsent(COVERAGE_STATUS_UNKNOWN) });
1859
+ const type = toFhirCodeableConcept(in1.field(15).asCwe(), ctx);
1860
+ issues.push(...type.issues);
1861
+ if (type.value !== void 0) props.push({ name: "type", value: type.value });
1862
+ props.push({ name: "beneficiary", value: reference(patientFullUrl) });
1863
+ const period = buildPeriod(in1, ctx, issues);
1864
+ if (period !== void 0) props.push({ name: "period", value: period });
1865
+ props.push({
1866
+ name: "payor",
1867
+ value: fhir.list([fhir.complex([{ name: "display", value: fhir.primitive(payerName) }])])
1868
+ });
1869
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "IN1.4", "Coverage.payor"));
1870
+ return { value: fhir.complex(props), issues };
1871
+ }
1270
1872
  var V2_0074_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0074";
1271
1873
  var DIAGNOSTIC_REPORT_STATUS_MAP = Object.freeze({
1272
1874
  O: "registered",
@@ -1278,7 +1880,7 @@ var DIAGNOSTIC_REPORT_STATUS_MAP = Object.freeze({
1278
1880
  F: "final",
1279
1881
  X: "cancelled"
1280
1882
  });
1281
- function buildDiagnosticReport(obr, resultFullUrls, subjectFullUrl, encounterFullUrl, ctx) {
1883
+ function buildDiagnosticReport(obr, resultFullUrls, subjectFullUrl, encounterFullUrl, ctx, specimenFullUrls = []) {
1282
1884
  const issues = [];
1283
1885
  const props = [
1284
1886
  { name: "resourceType", value: fhir.primitive("DiagnosticReport") }
@@ -1352,6 +1954,9 @@ function buildDiagnosticReport(obr, resultFullUrls, subjectFullUrl, encounterFul
1352
1954
  );
1353
1955
  }
1354
1956
  }
1957
+ if (specimenFullUrls.length > 0) {
1958
+ props.push({ name: "specimen", value: fhir.list(specimenFullUrls.map((u) => reference(u))) });
1959
+ }
1355
1960
  if (resultFullUrls.length > 0) {
1356
1961
  props.push({ name: "result", value: fhir.list(resultFullUrls.map((u) => reference(u))) });
1357
1962
  }
@@ -1556,6 +2161,127 @@ function buildEncounter(visit, subjectFullUrl, ctx) {
1556
2161
  if (props.length === 1) return { value: void 0, issues };
1557
2162
  return { value: fhir.complex(props), issues };
1558
2163
  }
2164
+ function withEncounterDiagnosis(encounter, conditionFullUrls) {
2165
+ if (conditionFullUrls.length === 0) return encounter;
2166
+ const entries = conditionFullUrls.map(
2167
+ (url) => fhir.complex([{ name: "condition", value: reference(url) }])
2168
+ );
2169
+ return fhir.complex([
2170
+ ...encounter.properties.filter((p) => p.name !== "diagnosis"),
2171
+ { name: "diagnosis", value: fhir.list(entries) }
2172
+ ]);
2173
+ }
2174
+ function emitEncounterDiagnosis(encounter, conditionFullUrls, gate) {
2175
+ const linked = withEncounterDiagnosis(encounter, conditionFullUrls);
2176
+ if (linked === encounter || gate(linked)) return { value: linked, issues: [] };
2177
+ return {
2178
+ value: encounter,
2179
+ issues: [issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "DG1", "Encounter.diagnosis")]
2180
+ };
2181
+ }
2182
+ var PROCEDURE_STATUS_UNKNOWN = "unknown";
2183
+ var WHOLE_MINUTES = /^\d+$/;
2184
+ var ZONED_DATE_TIME = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
2185
+ function collectProcedures(msg) {
2186
+ return msg.allSegments().filter((seg) => seg.type === "PR1");
2187
+ }
2188
+ function addMinutes(start, minutes) {
2189
+ const parts = ZONED_DATE_TIME.exec(start);
2190
+ if (parts === null) return void 0;
2191
+ const [, year, month, day, hour, minute, second, fraction, zone] = parts;
2192
+ const wall = /* @__PURE__ */ new Date(0);
2193
+ wall.setUTCFullYear(Number(year), Number(month) - 1, Number(day));
2194
+ wall.setUTCHours(Number(hour), Number(minute), Number(second), 0);
2195
+ const shifted = new Date(wall.getTime() + minutes * 6e4);
2196
+ if (Number.isNaN(shifted.getTime())) return void 0;
2197
+ const pad2 = (value, width) => String(value).padStart(width, "0");
2198
+ const rendered = `${pad2(shifted.getUTCFullYear(), 4)}-${pad2(shifted.getUTCMonth() + 1, 2)}-${pad2(shifted.getUTCDate(), 2)}T${pad2(shifted.getUTCHours(), 2)}:${pad2(shifted.getUTCMinutes(), 2)}:${pad2(shifted.getUTCSeconds(), 2)}`;
2199
+ return `${rendered}${fraction ?? ""}${zone ?? ""}`;
2200
+ }
2201
+ function deferredProcedureIssues(pr1) {
2202
+ const deferred = [
2203
+ [8, "Procedure.performer.actor"],
2204
+ [11, "Procedure.performer.actor"],
2205
+ [12, "Procedure.performer.actor"],
2206
+ [16, "Procedure.code.value"],
2207
+ [23, "Procedure.location"],
2208
+ [25, "Procedure.partOf"]
2209
+ ];
2210
+ const issues = [];
2211
+ for (const [field, path] of deferred) {
2212
+ if (pr1.field(field).value !== "") {
2213
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, `PR1.${String(field)}`, path));
2214
+ }
2215
+ }
2216
+ return issues;
2217
+ }
2218
+ function buildCode2(pr1, ctx, issues) {
2219
+ const cne = pr1.field(3).asCwe();
2220
+ const description = pr1.field(4).value;
2221
+ const originalTextValued = cne.originalText !== void 0 && cne.originalText !== "";
2222
+ const coded = toFhirCodeableConcept(cne, ctx);
2223
+ issues.push(...coded.issues);
2224
+ if (description !== "" && originalTextValued) {
2225
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "PR1.4", "Procedure.code.text"));
2226
+ return coded.value;
2227
+ }
2228
+ if (coded.value === void 0) {
2229
+ return description === "" ? void 0 : fhir.complex([{ name: "text", value: fhir.primitive(description) }]);
2230
+ }
2231
+ return description === "" ? coded.value : withCodeableText(coded.value, description);
2232
+ }
2233
+ function buildPerformed(pr1, ctx, issues) {
2234
+ if (pr1.field(5).value === "") return void 0;
2235
+ const converted = toFhirDateTime(pr1.field(5).asTs(), ctx.options);
2236
+ issues.push(...converted.issues);
2237
+ if (converted.value === void 0) return void 0;
2238
+ const minutesRaw = pr1.field(7).value;
2239
+ if (minutesRaw === "") {
2240
+ return { name: "performedDateTime", value: fhir.primitive(converted.value) };
2241
+ }
2242
+ const end = WHOLE_MINUTES.test(minutesRaw) ? addMinutes(converted.value, Number(minutesRaw)) : void 0;
2243
+ if (end === void 0) {
2244
+ issues.push(
2245
+ issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "PR1.7", "Procedure.performedPeriod.end")
2246
+ );
2247
+ return { name: "performedDateTime", value: fhir.primitive(converted.value) };
2248
+ }
2249
+ return {
2250
+ name: "performedPeriod",
2251
+ value: fhir.complex([
2252
+ { name: "start", value: fhir.primitive(converted.value) },
2253
+ { name: "end", value: fhir.primitive(end) }
2254
+ ])
2255
+ };
2256
+ }
2257
+ function buildProcedure(pr1, patientFullUrl, ctx) {
2258
+ const issues = [];
2259
+ const props = [
2260
+ { name: "resourceType", value: fhir.primitive("Procedure") }
2261
+ ];
2262
+ const entity = toFhirEntityIdentifier(pr1.field(19));
2263
+ if (entity.identifier !== void 0) {
2264
+ props.push({ name: "identifier", value: fhir.list([entity.identifier]) });
2265
+ }
2266
+ if (entity.authorityValued) {
2267
+ issues.push(
2268
+ issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "PR1.19", "Procedure.identifier.system")
2269
+ );
2270
+ }
2271
+ props.push({ name: "status", value: fhir.primitive(PROCEDURE_STATUS_UNKNOWN) });
2272
+ const category = toFhirCodeableConcept(pr1.field(6).asCwe(), ctx);
2273
+ issues.push(...category.issues);
2274
+ if (category.value !== void 0) props.push({ name: "category", value: category.value });
2275
+ const code = buildCode2(pr1, ctx, issues);
2276
+ if (code !== void 0) props.push({ name: "code", value: code });
2277
+ props.push({ name: "subject", value: reference(patientFullUrl) });
2278
+ const performed = buildPerformed(pr1, ctx, issues);
2279
+ if (performed !== void 0) props.push(performed);
2280
+ const reason = toFhirCodeableConcept(pr1.field(15).asCwe(), ctx);
2281
+ issues.push(...reason.issues);
2282
+ if (reason.value !== void 0) props.push({ name: "reasonCode", value: fhir.list([reason.value]) });
2283
+ return { value: fhir.complex(props), issues };
2284
+ }
1559
2285
  var EMIT_SCHEMAS = Object.freeze([
1560
2286
  {
1561
2287
  type: "Encounter",
@@ -1620,6 +2346,88 @@ var EMIT_SCHEMAS = Object.freeze([
1620
2346
  occurrenceDateTime: { min: 1, max: 1, types: ["dateTime"] }
1621
2347
  }
1622
2348
  },
2349
+ {
2350
+ // Condition: subject (1..1 Reference) is required. A DG1 with no bundle Patient to anchor never
2351
+ // reaches this gate (the assembly withholds it first); the entry is the defence behind that.
2352
+ type: "Condition",
2353
+ elements: {
2354
+ subject: { min: 1, max: 1, types: ["Reference"] }
2355
+ }
2356
+ },
2357
+ {
2358
+ // Procedure: status (1..1 code) and subject (1..1 Reference) are both required. The status
2359
+ // binding is R4's required EventStatus set, so the value the PR1 map's own row directs is
2360
+ // checked rather than trusted, and a Procedure carrying anything else is withheld.
2361
+ type: "Procedure",
2362
+ elements: {
2363
+ status: {
2364
+ min: 1,
2365
+ max: 1,
2366
+ types: ["code"],
2367
+ binding: {
2368
+ strength: "required",
2369
+ codes: [
2370
+ "preparation",
2371
+ "in-progress",
2372
+ "not-done",
2373
+ "on-hold",
2374
+ "stopped",
2375
+ "completed",
2376
+ "entered-in-error",
2377
+ "unknown"
2378
+ ]
2379
+ }
2380
+ },
2381
+ subject: { min: 1, max: 1, types: ["Reference"] }
2382
+ }
2383
+ },
2384
+ {
2385
+ // Coverage: status (1..1 code), beneficiary (1..1 Reference) and payor (1..* Reference) are all
2386
+ // required. The status is modelled WITHOUT its required binding on purpose: no published row
2387
+ // grounds a coverage status, so the element ships value-absent with a data-absent-reason
2388
+ // extension, which satisfies the cardinality while asserting no member of the value set.
2389
+ type: "Coverage",
2390
+ elements: {
2391
+ status: { min: 1, max: 1, types: ["code"] },
2392
+ beneficiary: { min: 1, max: 1, types: ["Reference"] },
2393
+ payor: { min: 1, max: fhir.UNBOUNDED, types: ["Reference"] }
2394
+ }
2395
+ },
2396
+ {
2397
+ // AllergyIntolerance: patient (1..1 Reference) is required, so an AL1 with no bundle Patient to
2398
+ // anchor is withheld rather than emitted with a dangling reference. The three coded elements carry
2399
+ // their R4 required bindings: an unmapped code leaves its element absent (never a neighbour), and a
2400
+ // value-absent element carrying only the IG's alternate-codes extension is not a code to check.
2401
+ type: "AllergyIntolerance",
2402
+ elements: {
2403
+ clinicalStatus: { min: 0, max: 1, types: ["CodeableConcept"] },
2404
+ type: {
2405
+ min: 0,
2406
+ max: 1,
2407
+ types: ["code"],
2408
+ binding: { strength: "required", codes: ["allergy", "intolerance"] }
2409
+ },
2410
+ category: {
2411
+ min: 0,
2412
+ max: fhir.UNBOUNDED,
2413
+ types: ["code"],
2414
+ binding: {
2415
+ strength: "required",
2416
+ codes: ["food", "medication", "environment", "biologic"]
2417
+ }
2418
+ },
2419
+ criticality: {
2420
+ min: 0,
2421
+ max: 1,
2422
+ types: ["code"],
2423
+ binding: { strength: "required", codes: ["low", "high", "unable-to-assess"] }
2424
+ },
2425
+ code: { min: 0, max: 1, types: ["CodeableConcept"] },
2426
+ patient: { min: 1, max: 1, types: ["Reference"] },
2427
+ onsetDateTime: { min: 0, max: 1, types: ["dateTime"] },
2428
+ reaction: { min: 0, max: fhir.UNBOUNDED, types: ["BackboneElement"] }
2429
+ }
2430
+ },
1623
2431
  {
1624
2432
  // Appointment: status (1..1 code) and participant (1..* BackboneElement) are both required, so an
1625
2433
  // Appointment whose SCH-25 status could not be grounded via HL70278, or that has no resolvable Patient
@@ -1772,16 +2580,340 @@ function buildImmunization(rxa, rxr, orc, patientFullUrl, encounterFullUrl, ctx)
1772
2580
  }
1773
2581
  return { value: fhir.complex(props), issues };
1774
2582
  }
1775
- var STATUS_UNKNOWN = "unknown";
1776
- var ORDER_INTENT = "order";
1777
- var UNSIGNED_INT = /^(0|[1-9][0-9]*)$/;
1778
- function codeableFrom(seg, index, ctx, issues) {
1779
- if (seg.field(index).value === "") return void 0;
1780
- const cc = toFhirCodeableConcept(seg.field(index).asCwe(), ctx);
1781
- issues.push(...cc.issues);
1782
- return cc.value;
2583
+ var UNITS_OF_TIME = Object.freeze(
2584
+ /* @__PURE__ */ new Set(["s", "min", "h", "d", "wk", "mo", "a"])
2585
+ );
2586
+ var UCUM_MNEMONICS = Object.freeze(/* @__PURE__ */ new Set(["UCUM", "ISO+"]));
2587
+ var HL70335_MNEMONICS = Object.freeze(/* @__PURE__ */ new Set(["HL70335", "0335"]));
2588
+ var CONDITION_TEXT_PATH = "MedicationRequest.dosageInstruction.additionalInstruction.text";
2589
+ var INSTRUCTION_TEXT_PATH = "MedicationRequest.text";
2590
+ function timingPath(target) {
2591
+ return target === "MedicationRequest" ? "MedicationRequest.dosageInstruction.timing" : "ServiceRequest.occurrenceTiming";
1783
2592
  }
1784
- function translatedFrom2(seg, index, map, ctx, issues) {
2593
+ var EXPLICIT_NULL = '""';
2594
+ function carriesValue(sub) {
2595
+ return sub !== "" && sub !== EXPLICIT_NULL;
2596
+ }
2597
+ function repetitionValued(rep) {
2598
+ return rep.components.some((c) => c.subcomponents.some(carriesValue));
2599
+ }
2600
+ function isValued(seg, index) {
2601
+ return seg.field(index).repetitions.some(repetitionValued);
2602
+ }
2603
+ function subcomponents(rep, component3) {
2604
+ return rep.components[component3 - 1]?.subcomponents ?? [];
2605
+ }
2606
+ function componentValued(rep, component3) {
2607
+ return subcomponents(rep, component3).some(carriesValue);
2608
+ }
2609
+ function subcomponent(rep, component3, sub) {
2610
+ return subcomponents(rep, component3)[sub - 1] ?? "";
2611
+ }
2612
+ function restoreStructuralEmpties(text2, repetitions, enc) {
2613
+ const padded = (part, count) => {
2614
+ const subs = part.split(enc.subcomponent);
2615
+ if (subs.length >= count) return part;
2616
+ return [...subs, ...Array.from({ length: count - subs.length }, () => "")].join(
2617
+ enc.subcomponent
2618
+ );
2619
+ };
2620
+ return text2.split(enc.repetition).map((repetition, r) => {
2621
+ const components = repetitions[r]?.components ?? [];
2622
+ const parts = repetition.split(enc.component);
2623
+ const restored = parts.map(
2624
+ (part, c) => padded(part, components[c]?.subcomponents.length ?? 0)
2625
+ );
2626
+ for (const dropped of components.slice(parts.length))
2627
+ restored.push(padded("", dropped.subcomponents.length));
2628
+ return restored.join(enc.component);
2629
+ }).join(enc.repetition);
2630
+ }
2631
+ function freeText(seg, field) {
2632
+ const f = seg.field(field);
2633
+ if (f.repetitions.length === 0) return { kind: "absent" };
2634
+ const rendered = hl7.renderText(
2635
+ restoreStructuralEmpties(f.text, f.repetitions, seg.enc),
2636
+ seg.enc
2637
+ ).text;
2638
+ return rendered === "" ? { kind: "unplaceable" } : { kind: "text", text: rendered };
2639
+ }
2640
+ function txValued(seg, field) {
2641
+ return freeText(seg, field).kind !== "absent";
2642
+ }
2643
+ var SCHEDULE_NARROWING = Object.freeze([
2644
+ [4, ".event"],
2645
+ [5, ".repeat.offset"],
2646
+ // R4 places every `bounds[x]` on `Timing.repeat`, not on `Timing`, so the element the IG's
2647
+ // TQ1-6 rows would have reached is `...timing.repeat.boundsDuration`: a consumer routing on
2648
+ // `fhirPath` must not be handed a path that resolves to nothing.
2649
+ [6, ".repeat.boundsDuration"],
2650
+ [12, ""],
2651
+ [13, ".repeat.duration"],
2652
+ [14, ".repeat.countMax"]
2653
+ ]);
2654
+ var EXPRESSIBLE_RPT_COMPONENTS = Object.freeze(/* @__PURE__ */ new Set([1, 5, 6, 8]));
2655
+ function endPrecedesStart(start, end) {
2656
+ const zoned = (v) => {
2657
+ if (!v.includes("T")) return void 0;
2658
+ const ms = Date.parse(v);
2659
+ return Number.isNaN(ms) ? void 0 : ms;
2660
+ };
2661
+ const startMs = zoned(start);
2662
+ const endMs = zoned(end);
2663
+ if (startMs !== void 0 && endMs !== void 0) return endMs < startMs;
2664
+ const datePart2 = (v) => v.split("T")[0] ?? v;
2665
+ const widen = (v, high) => {
2666
+ const d = datePart2(v);
2667
+ if (d.length === 4) return high ? `${d}-12-31` : `${d}-01-01`;
2668
+ if (d.length === 7) return high ? `${d}-31` : `${d}-01`;
2669
+ return d;
2670
+ };
2671
+ return widen(end, true) < widen(start, false);
2672
+ }
2673
+ function timingCode(code) {
2674
+ return fhir.complex([
2675
+ {
2676
+ name: "coding",
2677
+ value: fhir.list([
2678
+ fhir.complex([
2679
+ { name: "system", value: fhir.primitive(V2_0335_SYSTEM) },
2680
+ { name: "code", value: fhir.primitive(code) }
2681
+ ])
2682
+ ])
2683
+ }
2684
+ ]);
2685
+ }
2686
+ function readRepeatPattern(tq1, base, draft, issues) {
2687
+ const valued = tq1.field(3).repetitions.filter(repetitionValued);
2688
+ const rpt = valued[0];
2689
+ if (rpt === void 0) return;
2690
+ if (valued.length > 1) {
2691
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "TQ1.3", base));
2692
+ draft.refused = true;
2693
+ return;
2694
+ }
2695
+ const carried = rpt.components.length;
2696
+ for (let component3 = 1; component3 <= carried; component3 += 1) {
2697
+ if (EXPRESSIBLE_RPT_COMPONENTS.has(component3)) continue;
2698
+ if (!componentValued(rpt, component3)) continue;
2699
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, `TQ1.3.${String(component3)}`, base));
2700
+ draft.refused = true;
2701
+ }
2702
+ if (componentValued(rpt, 1)) {
2703
+ const code = subcomponent(rpt, 1, 1);
2704
+ const mnemonic = subcomponent(rpt, 1, 3);
2705
+ const fromTable = mnemonic === "" || HL70335_MNEMONICS.has(mnemonic);
2706
+ if (fromTable && isRepeatPatternCode(code)) {
2707
+ draft.code = timingCode(code);
2708
+ } else {
2709
+ issues.push(issue(ISSUE_CODES.TRANSFORM_CODE_UNMAPPED, "TQ1.3.1", `${base}.code`));
2710
+ draft.refused = true;
2711
+ }
2712
+ }
2713
+ if (componentValued(rpt, 5)) {
2714
+ const raw = subcomponent(rpt, 5, 1);
2715
+ let period;
2716
+ try {
2717
+ period = fhir.primitive(fhir.decimal(raw));
2718
+ } catch {
2719
+ period = void 0;
2720
+ }
2721
+ if (period === void 0 || raw.startsWith("-")) {
2722
+ issues.push(
2723
+ issue(ISSUE_CODES.TRANSFORM_QUANTITY_VALUE_INVALID, "TQ1.3.5", `${base}.repeat.period`)
2724
+ );
2725
+ draft.refused = true;
2726
+ } else {
2727
+ draft.period = period;
2728
+ }
2729
+ }
2730
+ if (componentValued(rpt, 6)) {
2731
+ const unit = subcomponent(rpt, 6, 1);
2732
+ const mnemonic = subcomponent(rpt, 6, 3);
2733
+ const fromUcum = mnemonic === "" || UCUM_MNEMONICS.has(mnemonic);
2734
+ if (fromUcum && UNITS_OF_TIME.has(unit)) {
2735
+ draft.periodUnit = unit;
2736
+ } else {
2737
+ issues.push(
2738
+ issue(ISSUE_CODES.TRANSFORM_CODE_UNMAPPED, "TQ1.3.6", `${base}.repeat.periodUnit`)
2739
+ );
2740
+ draft.refused = true;
2741
+ }
2742
+ }
2743
+ const periodValued = componentValued(rpt, 5);
2744
+ const unitValued = componentValued(rpt, 6);
2745
+ if (periodValued && !unitValued && draft.period !== void 0) {
2746
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "TQ1.3.5", `${base}.repeat.period`));
2747
+ draft.refused = true;
2748
+ }
2749
+ if (unitValued && !periodValued && draft.periodUnit !== void 0) {
2750
+ issues.push(
2751
+ issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "TQ1.3.6", `${base}.repeat.periodUnit`)
2752
+ );
2753
+ draft.refused = true;
2754
+ }
2755
+ if (componentValued(rpt, 8)) {
2756
+ const event = subcomponent(rpt, 8, 1);
2757
+ const mnemonic = subcomponent(rpt, 8, 3);
2758
+ const target = translateBound(
2759
+ { identifier: event, nameOfCodingSystem: mnemonic },
2760
+ TIMING_EVENT_VALUE_MAP
2761
+ );
2762
+ if (target === void 0) {
2763
+ issues.push(issue(ISSUE_CODES.TRANSFORM_CODE_UNMAPPED, "TQ1.3.8", `${base}.repeat.when`));
2764
+ draft.refused = true;
2765
+ } else {
2766
+ draft.when = target.code;
2767
+ }
2768
+ }
2769
+ }
2770
+ function readBounds(tq1, base, ctx, draft, issues) {
2771
+ const endpoint = (field, name) => {
2772
+ if (!isValued(tq1, field)) return void 0;
2773
+ const converted = toFhirDateTime(tq1.field(field).asTs(), ctx.options);
2774
+ issues.push(...converted.issues);
2775
+ if (converted.value === void 0) {
2776
+ issues.push(
2777
+ issue(
2778
+ ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED,
2779
+ `TQ1.${String(field)}`,
2780
+ `${base}.repeat.boundsPeriod.${name}`
2781
+ )
2782
+ );
2783
+ draft.refused = true;
2784
+ }
2785
+ return converted.value;
2786
+ };
2787
+ draft.boundsStart = endpoint(7, "start");
2788
+ draft.boundsEnd = endpoint(8, "end");
2789
+ if (draft.boundsStart !== void 0 && draft.boundsEnd !== void 0 && endPrecedesStart(draft.boundsStart, draft.boundsEnd)) {
2790
+ issues.push(
2791
+ issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "TQ1.8", `${base}.repeat.boundsPeriod.end`)
2792
+ );
2793
+ draft.refused = true;
2794
+ }
2795
+ }
2796
+ function assembleTiming(draft) {
2797
+ if (draft.refused) return void 0;
2798
+ const bounds = [];
2799
+ if (draft.boundsStart !== void 0)
2800
+ bounds.push({ name: "start", value: fhir.primitive(draft.boundsStart) });
2801
+ if (draft.boundsEnd !== void 0)
2802
+ bounds.push({ name: "end", value: fhir.primitive(draft.boundsEnd) });
2803
+ const repeat = [];
2804
+ if (bounds.length > 0) repeat.push({ name: "boundsPeriod", value: fhir.complex(bounds) });
2805
+ if (draft.period !== void 0) repeat.push({ name: "period", value: draft.period });
2806
+ if (draft.periodUnit !== void 0)
2807
+ repeat.push({ name: "periodUnit", value: fhir.primitive(draft.periodUnit) });
2808
+ if (draft.when !== void 0) repeat.push({ name: "when", value: fhir.list([fhir.primitive(draft.when)]) });
2809
+ const timing = [];
2810
+ if (repeat.length > 0) timing.push({ name: "repeat", value: fhir.complex(repeat) });
2811
+ if (draft.code !== void 0) timing.push({ name: "code", value: draft.code });
2812
+ return timing.length === 0 ? void 0 : fhir.complex(timing);
2813
+ }
2814
+ function nothing(issues) {
2815
+ return {
2816
+ timing: void 0,
2817
+ conditionText: void 0,
2818
+ instructionText: void 0,
2819
+ issues,
2820
+ contributes: false
2821
+ };
2822
+ }
2823
+ function readTq1(tq1s, target, ctx) {
2824
+ const issues = [];
2825
+ const base = timingPath(target);
2826
+ if (tq1s.length === 0) return nothing(issues);
2827
+ if (tq1s.length > 1) {
2828
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "TQ1", base));
2829
+ return nothing(issues);
2830
+ }
2831
+ const tq1 = tq1s[0];
2832
+ if (tq1 === void 0) return nothing(issues);
2833
+ if (isValued(tq1, 2)) {
2834
+ issues.push(
2835
+ issue(
2836
+ ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED,
2837
+ "TQ1.2",
2838
+ target === "MedicationRequest" ? "MedicationRequest.dosageInstruction.doseAndRate.doseQuantity" : "ServiceRequest.quantityQuantity"
2839
+ )
2840
+ );
2841
+ }
2842
+ if (isValued(tq1, 9)) {
2843
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "TQ1.9", `${target}.priority`));
2844
+ }
2845
+ const draft = {
2846
+ refused: false,
2847
+ code: void 0,
2848
+ period: void 0,
2849
+ periodUnit: void 0,
2850
+ when: void 0,
2851
+ boundsStart: void 0,
2852
+ boundsEnd: void 0
2853
+ };
2854
+ for (const [field, suffix] of SCHEDULE_NARROWING) {
2855
+ if (!isValued(tq1, field)) continue;
2856
+ issues.push(
2857
+ issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, `TQ1.${String(field)}`, `${base}${suffix}`)
2858
+ );
2859
+ draft.refused = true;
2860
+ }
2861
+ readRepeatPattern(tq1, base, draft, issues);
2862
+ readBounds(tq1, base, ctx, draft, issues);
2863
+ const timing = assembleTiming(draft);
2864
+ let conditionText;
2865
+ let instructionText;
2866
+ if (target === "MedicationRequest") {
2867
+ const condition = freeText(tq1, 10);
2868
+ if (condition.kind === "text") conditionText = condition.text;
2869
+ else if (condition.kind === "unplaceable")
2870
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "TQ1.10", CONDITION_TEXT_PATH));
2871
+ const instruction = freeText(tq1, 11);
2872
+ if (instruction.kind === "unplaceable") {
2873
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "TQ1.11", INSTRUCTION_TEXT_PATH));
2874
+ } else if (instruction.kind === "text") {
2875
+ if (instruction.text.trim() === "")
2876
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "TQ1.11", INSTRUCTION_TEXT_PATH));
2877
+ else instructionText = instruction.text;
2878
+ }
2879
+ } else {
2880
+ if (txValued(tq1, 10))
2881
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "TQ1.10", "ServiceRequest"));
2882
+ if (txValued(tq1, 11))
2883
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "TQ1.11", "ServiceRequest.note"));
2884
+ }
2885
+ return {
2886
+ timing,
2887
+ conditionText,
2888
+ instructionText,
2889
+ issues,
2890
+ contributes: timing !== void 0 || conditionText !== void 0 || instructionText !== void 0
2891
+ };
2892
+ }
2893
+ function escapeXml(value) {
2894
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
2895
+ }
2896
+ function narrative(value) {
2897
+ return fhir.complex([
2898
+ { name: "status", value: fhir.primitive("additional") },
2899
+ {
2900
+ name: "div",
2901
+ value: fhir.primitive(`<div xmlns="http://www.w3.org/1999/xhtml">${escapeXml(value)}</div>`)
2902
+ }
2903
+ ]);
2904
+ }
2905
+
2906
+ // src/messages/medication-request.ts
2907
+ var STATUS_UNKNOWN = "unknown";
2908
+ var ORDER_INTENT = "order";
2909
+ var UNSIGNED_INT = /^(0|[1-9][0-9]*)$/;
2910
+ function codeableFrom(seg, index, ctx, issues) {
2911
+ if (seg.field(index).value === "") return void 0;
2912
+ const cc = toFhirCodeableConcept(seg.field(index).asCwe(), ctx);
2913
+ issues.push(...cc.issues);
2914
+ return cc.value;
2915
+ }
2916
+ function translatedFrom2(seg, index, map, ctx, issues) {
1785
2917
  if (seg.field(index).value === "") return void 0;
1786
2918
  const cc = toFhirCodeableConceptVia(seg.field(index).asCwe(), map, ctx);
1787
2919
  issues.push(...cc.issues);
@@ -1801,8 +2933,17 @@ function doseEndpoint(rxo, amountIndex, units, ctx, issues) {
1801
2933
  issues.push(...q.issues);
1802
2934
  return q.value;
1803
2935
  }
1804
- function buildDosageInstruction(rxo, rxr, ctx, issues) {
2936
+ function buildDosageInstruction(rxo, rxr, tq1, ctx, issues) {
1805
2937
  const props = [];
2938
+ if (tq1?.conditionText !== void 0) {
2939
+ props.push({
2940
+ name: "additionalInstruction",
2941
+ value: fhir.list([fhir.complex([{ name: "text", value: fhir.primitive(tq1.conditionText) }])])
2942
+ });
2943
+ }
2944
+ if (tq1?.timing !== void 0) {
2945
+ props.push({ name: "timing", value: tq1.timing });
2946
+ }
1806
2947
  if (rxr !== void 0) {
1807
2948
  const route = translatedFrom2(rxr, 1, ROUTE_VALUE_MAP, ctx, issues);
1808
2949
  if (route !== void 0) props.push({ name: "route", value: route });
@@ -1858,8 +2999,9 @@ function buildDispenseRequest(rxo, ctx, issues) {
1858
2999
  }
1859
3000
  return props.length === 0 ? void 0 : fhir.complex(props);
1860
3001
  }
1861
- function buildMedicationRequest(rxo, rxr, orc, subjectFullUrl, encounterFullUrl, ctx) {
3002
+ function buildMedicationRequest(rxo, rxr, orc, tq1, subjectFullUrl, encounterFullUrl, ctx) {
1862
3003
  const issues = [];
3004
+ if (tq1 !== void 0) issues.push(...tq1.issues);
1863
3005
  if (rxo === void 0 || rxo.field(1).value === "") return { value: void 0, issues };
1864
3006
  const medication = toFhirCodeableConcept(rxo.field(1).asCwe(), ctx);
1865
3007
  issues.push(...medication.issues);
@@ -1867,6 +3009,9 @@ function buildMedicationRequest(rxo, rxr, orc, subjectFullUrl, encounterFullUrl,
1867
3009
  const props = [
1868
3010
  { name: "resourceType", value: fhir.primitive("MedicationRequest") }
1869
3011
  ];
3012
+ if (tq1?.instructionText !== void 0) {
3013
+ props.push({ name: "text", value: narrative(tq1.instructionText) });
3014
+ }
1870
3015
  props.push({ name: "status", value: fhir.primitive(STATUS_UNKNOWN) });
1871
3016
  issues.push(
1872
3017
  issue(ISSUE_CODES.TRANSFORM_REQUIRED_ELEMENT_UNKNOWN, "RXO", "MedicationRequest.status")
@@ -1883,7 +3028,7 @@ function buildMedicationRequest(rxo, rxr, orc, subjectFullUrl, encounterFullUrl,
1883
3028
  if (authored.value !== void 0)
1884
3029
  props.push({ name: "authoredOn", value: fhir.primitive(authored.value) });
1885
3030
  }
1886
- const dosage = buildDosageInstruction(rxo, rxr, ctx, issues);
3031
+ const dosage = buildDosageInstruction(rxo, rxr, tq1, ctx, issues);
1887
3032
  if (dosage !== void 0) props.push({ name: "dosageInstruction", value: fhir.list([dosage]) });
1888
3033
  const dispense = buildDispenseRequest(rxo, ctx, issues);
1889
3034
  if (dispense !== void 0) props.push({ name: "dispenseRequest", value: dispense });
@@ -1938,6 +3083,40 @@ function buildMessageHeader(meta, focusFullUrls) {
1938
3083
  }
1939
3084
  return { value: fhir.complex(props), issues };
1940
3085
  }
3086
+ var DEFERRED_NOTE_ROWS = Object.freeze([
3087
+ [4, "Annotation.extension"],
3088
+ [5, "Annotation.authorReference"]
3089
+ ]);
3090
+ function annotationTime(nte, ctx, issues) {
3091
+ if (nte.field(6).value === "") return void 0;
3092
+ const entered = toFhirDateTime(nte.field(6).asTs(), ctx.options);
3093
+ issues.push(...entered.issues);
3094
+ return entered.value === void 0 ? void 0 : fhir.primitive(entered.value);
3095
+ }
3096
+ function buildAnnotations(notes, ctx, issues) {
3097
+ const annotations = [];
3098
+ for (const nte of notes) {
3099
+ for (const [field, path] of DEFERRED_NOTE_ROWS) {
3100
+ if (nte.field(field).value !== "") {
3101
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, `NTE.${String(field)}`, path));
3102
+ }
3103
+ }
3104
+ const comment = nte.field(3);
3105
+ if (comment.repetitions.length === 0) continue;
3106
+ const time = annotationTime(nte, ctx, issues);
3107
+ for (const repetition of comment.text.split(nte.enc.repetition)) {
3108
+ const rendered = hl7.renderText(repetition, nte.enc).text;
3109
+ if (rendered === "") continue;
3110
+ const props = [];
3111
+ if (time !== void 0) props.push({ name: "time", value: time });
3112
+ props.push({ name: "text", value: fhir.primitive(rendered) });
3113
+ annotations.push(fhir.complex(props));
3114
+ }
3115
+ }
3116
+ return annotations.length === 0 ? void 0 : fhir.list(annotations);
3117
+ }
3118
+
3119
+ // src/messages/observation.ts
1941
3120
  var V3_OBSERVATION_INTERPRETATION_SYSTEM = "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation";
1942
3121
  var HL70078_INTERPRETATION_CODES = /* @__PURE__ */ new Set([
1943
3122
  "<",
@@ -1989,6 +3168,10 @@ var OBSERVATION_STATUS_MAP = Object.freeze({
1989
3168
  W: "entered-in-error",
1990
3169
  X: "cancelled"
1991
3170
  });
3171
+ var OBSERVATION_VALUE_ATTACHMENT_EXTENSION_URL = "https://hl7.org/fhir/5.0/StructureDefinition/extension-Observation.valueAttachment";
3172
+ var ED_BASE64_ENCODING = "Base64";
3173
+ var SAMPLED_DATA_ABSENT_POINT = "E";
3174
+ var SAMPLED_DATA_UNGROUNDED = "unknown";
1992
3175
  var SN_COMPARATORS = /* @__PURE__ */ new Set([">", "<", ">=", "<="]);
1993
3176
  var CODED_VALUE_TYPES = /* @__PURE__ */ new Set(["CWE", "CE", "CF", "CNE", "IS"]);
1994
3177
  var DATETIME_VALUE_TYPES = /* @__PURE__ */ new Set(["DT", "DTM", "TS"]);
@@ -1997,6 +3180,117 @@ function rawComponent2(field, index) {
1997
3180
  const c = field.repetitions[0]?.components[index]?.subcomponents[0];
1998
3181
  return c === void 0 || c === "" ? void 0 : c;
1999
3182
  }
3183
+ function fieldEmpty(field) {
3184
+ return field.repetitions.every(
3185
+ (r) => r.components.every((c) => c.subcomponents.every((s) => s === ""))
3186
+ );
3187
+ }
3188
+ function buildPeriodValue(field, ctx, issues) {
3189
+ const props = [];
3190
+ for (const [index, name] of [
3191
+ [0, "start"],
3192
+ [1, "end"]
3193
+ ]) {
3194
+ const raw = rawComponent2(field, index);
3195
+ if (raw === void 0) continue;
3196
+ const converted = toFhirDateTime(hl7.parseDtm(raw), ctx.options);
3197
+ issues.push(...converted.issues);
3198
+ if (converted.value !== void 0) props.push({ name, value: fhir.primitive(converted.value) });
3199
+ }
3200
+ return props.length === 0 ? void 0 : fhir.complex(props);
3201
+ }
3202
+ function rangeBound(raw, fhirPath, issues) {
3203
+ if (raw === void 0) return void 0;
3204
+ try {
3205
+ return fhir.complex([{ name: "value", value: fhir.primitive(fhir.decimal(raw)) }]);
3206
+ } catch {
3207
+ issues.push(issue(ISSUE_CODES.TRANSFORM_QUANTITY_VALUE_INVALID, "OBX.5", fhirPath));
3208
+ return void 0;
3209
+ }
3210
+ }
3211
+ function buildRangeValue(field, issues) {
3212
+ const low = rangeBound(rawComponent2(field, 0), "Range.low.value", issues);
3213
+ const high = rangeBound(rawComponent2(field, 1), "Range.high.value", issues);
3214
+ const props = [];
3215
+ if (low !== void 0) props.push({ name: "low", value: low });
3216
+ if (high !== void 0) props.push({ name: "high", value: high });
3217
+ return props.length === 0 ? void 0 : fhir.complex(props);
3218
+ }
3219
+ function timeValue(raw) {
3220
+ const parts = /^(\d{2})(\d{2})(\d{2})(\.\d{1,4})?$/.exec(raw);
3221
+ if (parts === null) return void 0;
3222
+ const candidate = `${parts[1] ?? ""}:${parts[2] ?? ""}:${parts[3] ?? ""}${parts[4] ?? ""}`;
3223
+ return fhir.validatePrimitiveValue(candidate, "time") === "ok" ? candidate : void 0;
3224
+ }
3225
+ function buildSampledData(field, issues) {
3226
+ const rows = field.repetitions.map((r) => r.components.map((c) => c.subcomponents[0] ?? ""));
3227
+ if (rows.length === 0) return void 0;
3228
+ const width = Math.max(...rows.map((r) => r.length));
3229
+ const dimensions = rows.length === 1 ? 1 : width;
3230
+ const points = [];
3231
+ for (const row of rows) {
3232
+ for (let i = 0; i < width; i++) {
3233
+ const cell = row[i] ?? "";
3234
+ if (cell === "") {
3235
+ points.push(SAMPLED_DATA_ABSENT_POINT);
3236
+ continue;
3237
+ }
3238
+ try {
3239
+ fhir.decimal(cell);
3240
+ points.push(cell);
3241
+ } catch {
3242
+ issues.push(
3243
+ issue(ISSUE_CODES.TRANSFORM_QUANTITY_VALUE_INVALID, "OBX.5", "SampledData.data")
3244
+ );
3245
+ return void 0;
3246
+ }
3247
+ }
3248
+ }
3249
+ for (const path of ["SampledData.origin", "SampledData.period"]) {
3250
+ issues.push(issue(ISSUE_CODES.TRANSFORM_REQUIRED_ELEMENT_UNKNOWN, "OBX.5", path));
3251
+ }
3252
+ return fhir.complex([
3253
+ { name: "origin", value: dataAbsentComplex(SAMPLED_DATA_UNGROUNDED) },
3254
+ { name: "period", value: dataAbsent(SAMPLED_DATA_UNGROUNDED) },
3255
+ { name: "dimensions", value: fhir.primitive(fhir.decimal(String(dimensions))) },
3256
+ { name: "data", value: fhir.primitive(points.join(" ")) }
3257
+ ]);
3258
+ }
3259
+ function buildAttachmentExtension(field, issues) {
3260
+ const payload = rawComponent2(field, 4);
3261
+ if (payload === void 0) return void 0;
3262
+ const attachment = [];
3263
+ const subtype = rawComponent2(field, 2);
3264
+ const typeOfData = rawComponent2(field, 1);
3265
+ if (subtype === void 0 && typeOfData !== void 0) {
3266
+ issues.push(issue(ISSUE_CODES.TRANSFORM_CODE_UNMAPPED, "OBX.5", "Attachment.contentType"));
3267
+ attachment.push({
3268
+ name: "extension",
3269
+ value: fhir.list([
3270
+ fhir.complex([
3271
+ { name: "url", value: fhir.primitive(ALTERNATE_CODES_EXTENSION_URL) },
3272
+ {
3273
+ name: "valueCodeableConcept",
3274
+ value: fhir.complex([
3275
+ {
3276
+ name: "coding",
3277
+ value: fhir.list([fhir.complex([{ name: "code", value: fhir.primitive(typeOfData) }])])
3278
+ }
3279
+ ])
3280
+ }
3281
+ ])
3282
+ ])
3283
+ });
3284
+ }
3285
+ if (subtype !== void 0) attachment.push({ name: "contentType", value: fhir.primitive(subtype) });
3286
+ attachment.push({ name: "data", value: fhir.primitive(payload) });
3287
+ return fhir.list([
3288
+ fhir.complex([
3289
+ { name: "url", value: fhir.primitive(OBSERVATION_VALUE_ATTACHMENT_EXTENSION_URL) },
3290
+ { name: "valueAttachment", value: fhir.complex(attachment) }
3291
+ ])
3292
+ ]);
3293
+ }
2000
3294
  function snText(field) {
2001
3295
  return [0, 1, 2, 3].map((i) => rawComponent2(field, i) ?? "").join("");
2002
3296
  }
@@ -2052,10 +3346,39 @@ function buildSnValue(field, units, ctx, issues) {
2052
3346
  }
2053
3347
  return valueStringProp(snText(field));
2054
3348
  }
3349
+ var COMPOSITE_VALUE_TYPES = /* @__PURE__ */ new Set(["DR", "NR", "TM", "NA", "ED"]);
3350
+ function buildMappedValue(field, vt, ctx, issues) {
3351
+ if (vt === "DR") {
3352
+ const period = buildPeriodValue(field, ctx, issues);
3353
+ return period === void 0 ? void 0 : { name: "valuePeriod", value: period };
3354
+ }
3355
+ if (vt === "NR") {
3356
+ const range = buildRangeValue(field, issues);
3357
+ return range === void 0 ? void 0 : { name: "valueRange", value: range };
3358
+ }
3359
+ if (vt === "TM") {
3360
+ const time = timeValue(field.text);
3361
+ return time === void 0 ? void 0 : { name: "valueTime", value: fhir.primitive(time) };
3362
+ }
3363
+ if (vt === "NA") {
3364
+ const sampled = buildSampledData(field, issues);
3365
+ return sampled === void 0 ? void 0 : { name: "valueSampledData", value: sampled };
3366
+ }
3367
+ if (rawComponent2(field, 3) !== ED_BASE64_ENCODING) return void 0;
3368
+ const extension = buildAttachmentExtension(field, issues);
3369
+ return extension === void 0 ? void 0 : { name: "extension", value: extension };
3370
+ }
2055
3371
  function buildValue(obx, valueType, ctx, issues) {
2056
3372
  const field = obx.field(5);
2057
3373
  const rawValue = field.value;
2058
3374
  const vt = valueType.toUpperCase();
3375
+ if (COMPOSITE_VALUE_TYPES.has(vt)) {
3376
+ if (fieldEmpty(field)) return void 0;
3377
+ const mapped = buildMappedValue(field, vt, ctx, issues);
3378
+ if (mapped !== void 0) return mapped;
3379
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "OBX.5", "Observation.value[x]"));
3380
+ return valueStringProp(field.text);
3381
+ }
2059
3382
  if (vt === "NM") {
2060
3383
  const q = quantityFromRawMagnitude(field.asNm().raw, obx.field(6).asCwe(), ctx);
2061
3384
  issues.push(...q.issues);
@@ -2108,7 +3431,7 @@ function buildInterpretation(obx, issues) {
2108
3431
  }
2109
3432
  return codings.length === 0 ? void 0 : fhir.list(codings);
2110
3433
  }
2111
- function buildObservation(obx, subjectFullUrl, encounterFullUrl, ctx) {
3434
+ function buildObservation(obx, subjectFullUrl, encounterFullUrl, ctx, notes = []) {
2112
3435
  const issues = [];
2113
3436
  const props = [
2114
3437
  { name: "resourceType", value: fhir.primitive("Observation") }
@@ -2142,6 +3465,8 @@ function buildObservation(obx, subjectFullUrl, encounterFullUrl, ctx) {
2142
3465
  if (value !== void 0) props.push(value);
2143
3466
  const interpretation = buildInterpretation(obx, issues);
2144
3467
  if (interpretation !== void 0) props.push({ name: "interpretation", value: interpretation });
3468
+ const note = buildAnnotations(notes, ctx, issues);
3469
+ if (note !== void 0) props.push({ name: "note", value: note });
2145
3470
  const refRange = obx.field(7).value;
2146
3471
  if (refRange !== "") {
2147
3472
  props.push({
@@ -2158,7 +3483,13 @@ function collectOrderGroups(msg) {
2158
3483
  let current;
2159
3484
  let rxeCount = 0;
2160
3485
  const open = () => {
2161
- const g = { orc: void 0, obr: void 0, rxo: void 0, rxrs: [] };
3486
+ const g = {
3487
+ orc: void 0,
3488
+ obr: void 0,
3489
+ rxo: void 0,
3490
+ rxrs: [],
3491
+ tq1s: []
3492
+ };
2162
3493
  groups.push(g);
2163
3494
  current = g;
2164
3495
  return g;
@@ -2179,13 +3510,22 @@ function collectOrderGroups(msg) {
2179
3510
  case "RXR":
2180
3511
  if (current !== void 0) current.rxrs.push(seg);
2181
3512
  break;
3513
+ case "TQ1":
3514
+ if (current !== void 0) current.tq1s.push(seg);
3515
+ break;
2182
3516
  case "RXE":
2183
3517
  rxeCount += 1;
2184
3518
  break;
2185
3519
  }
2186
3520
  }
2187
3521
  return {
2188
- groups: groups.map((g) => ({ orc: g.orc, obr: g.obr, rxo: g.rxo, rxrs: g.rxrs })),
3522
+ groups: groups.map((g) => ({
3523
+ orc: g.orc,
3524
+ obr: g.obr,
3525
+ rxo: g.rxo,
3526
+ rxrs: g.rxrs,
3527
+ tq1s: g.tq1s
3528
+ })),
2189
3529
  rxeCount
2190
3530
  };
2191
3531
  }
@@ -2194,14 +3534,29 @@ function collectOrderGroups(msg) {
2194
3534
  function collectReportGroups(msg) {
2195
3535
  const groups = [];
2196
3536
  let current;
3537
+ let openObservation;
2197
3538
  let orphanObxCount = 0;
2198
3539
  for (const seg of msg.allSegments()) {
3540
+ if (seg.type === "NTE") {
3541
+ if (openObservation !== void 0) openObservation.notes.push(seg);
3542
+ continue;
3543
+ }
3544
+ if (seg.type === "OBX") {
3545
+ if (current === void 0) {
3546
+ orphanObxCount += 1;
3547
+ openObservation = void 0;
3548
+ continue;
3549
+ }
3550
+ openObservation = { obx: seg, notes: [] };
3551
+ current.observations.push(openObservation);
3552
+ continue;
3553
+ }
3554
+ openObservation = void 0;
2199
3555
  if (seg.type === "OBR") {
2200
- current = { obr: seg, observations: [] };
3556
+ current = { obr: seg, observations: [], specimens: [] };
2201
3557
  groups.push(current);
2202
- } else if (seg.type === "OBX") {
2203
- if (current === void 0) orphanObxCount += 1;
2204
- else current.observations.push(seg);
3558
+ } else if (seg.type === "SPM" && current !== void 0) {
3559
+ current.specimens.push(seg);
2205
3560
  }
2206
3561
  }
2207
3562
  return { groups, orphanObxCount };
@@ -2303,6 +3658,131 @@ function buildRelatedPerson(nk1, patientFullUrl, ctx, setIndex) {
2303
3658
  if (!hasContent) return { value: void 0, issues };
2304
3659
  return { value: fhir.complex(props), issues };
2305
3660
  }
3661
+
3662
+ // src/messages/ig-segment-maps.ts
3663
+ var IG_SEGMENT_MAPS_VERSION = "1.0.0";
3664
+ var IG_SEGMENT_MAPS_PUBLISHED = "2025-10-07";
3665
+ var IG_SEGMENT_MAPS_RETRIEVED = "2026-08-22";
3666
+ var IG_SEGMENT_MAPS_SOURCE = "https://hl7.org/fhir/uv/v2mappings/segment_maps.html";
3667
+ var IG_MAPPED_SEGMENT_NAMES = Object.freeze(
3668
+ /* @__PURE__ */ new Set([
3669
+ "AIG",
3670
+ "AIL",
3671
+ "AIP",
3672
+ "AIS",
3673
+ "AL1",
3674
+ "DG1",
3675
+ "EVN",
3676
+ "IAM",
3677
+ "IN1",
3678
+ "IN3",
3679
+ "MRG",
3680
+ "MSA",
3681
+ "MSH",
3682
+ "NK1",
3683
+ "NTE",
3684
+ "OBR",
3685
+ "OBX",
3686
+ "ORC",
3687
+ "PD1",
3688
+ "PID",
3689
+ "PR1",
3690
+ "PRT",
3691
+ "PV1",
3692
+ "PV2",
3693
+ "ROL",
3694
+ "RXA",
3695
+ "RXO",
3696
+ "RXR",
3697
+ "SCH",
3698
+ "SFT",
3699
+ "SPM",
3700
+ "TQ1",
3701
+ "TXA"
3702
+ ])
3703
+ );
3704
+ function isIgMappedSegmentName(name) {
3705
+ return IG_MAPPED_SEGMENT_NAMES.has(name);
3706
+ }
3707
+
3708
+ // src/messages/segment-completeness.ts
3709
+ var SEGMENT_IDENTIFIER_SHAPE = /^[A-Z][A-Za-z0-9]{2}$/;
3710
+ function isWellFormedSegmentName(name) {
3711
+ return SEGMENT_IDENTIFIER_SHAPE.test(name);
3712
+ }
3713
+ function enumerateSegmentOccurrences(msg) {
3714
+ const occurrences = [];
3715
+ const seenPerName = /* @__PURE__ */ new Map();
3716
+ for (const [i, segment] of msg.allSegments().entries()) {
3717
+ if (segment.type === "" && segment.fields.length === 0) continue;
3718
+ if (isWellFormedSegmentName(segment.type)) {
3719
+ const nameIndex = (seenPerName.get(segment.type) ?? 0) + 1;
3720
+ seenPerName.set(segment.type, nameIndex);
3721
+ occurrences.push({ segment, ordinal: i + 1, name: segment.type, nameIndex });
3722
+ } else {
3723
+ occurrences.push({ segment, ordinal: i + 1, name: void 0, nameIndex: void 0 });
3724
+ }
3725
+ }
3726
+ return occurrences;
3727
+ }
3728
+ function segmentOccurrenceLocation(occurrence) {
3729
+ return occurrence.name !== void 0 && occurrence.nameIndex !== void 0 ? `${occurrence.name}[${String(occurrence.nameIndex)}]` : `[#${String(occurrence.ordinal)}]`;
3730
+ }
3731
+ var SegmentReachLedger = class {
3732
+ occurrences;
3733
+ reached = /* @__PURE__ */ new Set();
3734
+ /**
3735
+ * @param msg - The parsed message whose occurrences are being tracked.
3736
+ */
3737
+ constructor(msg) {
3738
+ this.occurrences = enumerateSegmentOccurrences(msg);
3739
+ }
3740
+ /**
3741
+ * Record that each given segment contributed to a resource now present in the bundle. `undefined`
3742
+ * entries are ignored, so a call site can pass an optional part of a group without branching.
3743
+ */
3744
+ mark(...segments) {
3745
+ for (const segment of segments) {
3746
+ if (segment !== void 0) this.reached.add(segment);
3747
+ }
3748
+ }
3749
+ /**
3750
+ * Record that the FIRST occurrence of `type` contributed. The parser's derived views (`patient`,
3751
+ * `visit`, `meta`) are built from the first segment of their type, so this is how a resource built
3752
+ * from a view marks the occurrence it came from.
3753
+ */
3754
+ markFirstOfType(type) {
3755
+ const found = this.occurrences.find((o) => o.name === type);
3756
+ if (found !== void 0) this.reached.add(found.segment);
3757
+ }
3758
+ /**
3759
+ * Record that the `index`-th (0-based) occurrence of `type` contributed: the counterpart of
3760
+ * {@link SegmentReachLedger.markFirstOfType} for the repeating views (`nextOfKin()`), whose
3761
+ * entries are in document order.
3762
+ */
3763
+ markNthOfType(type, index) {
3764
+ const matching = this.occurrences.filter((o) => o.name === type);
3765
+ const found = matching[index];
3766
+ if (found !== void 0) this.reached.add(found.segment);
3767
+ }
3768
+ /** The occurrences that contributed to no resource present in the bundle, in message order. */
3769
+ reportable() {
3770
+ return this.occurrences.filter((o) => !this.reached.has(o.segment));
3771
+ }
3772
+ /**
3773
+ * One value-free issue per reportable occurrence, in message order: the library-gap code when the
3774
+ * IG publishes a segment map for the name, the standard-gap code otherwise, and the standard-gap
3775
+ * code for every name that could not be classified at all.
3776
+ */
3777
+ issues() {
3778
+ return this.reportable().map(
3779
+ (occurrence) => issue(
3780
+ occurrence.name !== void 0 && isIgMappedSegmentName(occurrence.name) ? ISSUE_CODES.TRANSFORM_SEGMENT_NOT_EMITTED : ISSUE_CODES.TRANSFORM_SEGMENT_NO_IG_MAP,
3781
+ segmentOccurrenceLocation(occurrence)
3782
+ )
3783
+ );
3784
+ }
3785
+ };
2306
3786
  var REQUEST_STATUS_MAP = Object.freeze({
2307
3787
  AF: "active",
2308
3788
  CA: "active",
@@ -2333,8 +3813,9 @@ var ORDER_INTENT2 = "order";
2333
3813
  function fieldValue(seg, index) {
2334
3814
  return seg === void 0 ? "" : seg.field(index).value;
2335
3815
  }
2336
- function buildServiceRequest(orc, obr, subjectFullUrl, encounterFullUrl, ctx) {
3816
+ function buildServiceRequest(orc, obr, tq1, subjectFullUrl, encounterFullUrl, ctx) {
2337
3817
  const issues = [];
3818
+ if (tq1 !== void 0) issues.push(...tq1.issues);
2338
3819
  if (orc === void 0 && obr === void 0) return { value: void 0, issues };
2339
3820
  const props = [
2340
3821
  { name: "resourceType", value: fhir.primitive("ServiceRequest") }
@@ -2388,8 +3869,16 @@ function buildServiceRequest(orc, obr, subjectFullUrl, encounterFullUrl, ctx) {
2388
3869
  if (obr !== void 0 && obr.field(6).value !== "") {
2389
3870
  const occurrence = toFhirDateTime(obr.field(6).asTs(), ctx.options);
2390
3871
  issues.push(...occurrence.issues);
2391
- if (occurrence.value !== void 0)
3872
+ if (occurrence.value !== void 0 && tq1?.timing === void 0) {
2392
3873
  props.push({ name: "occurrenceDateTime", value: fhir.primitive(occurrence.value) });
3874
+ } else if (occurrence.value !== void 0) {
3875
+ issues.push(
3876
+ issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "OBR.6", "ServiceRequest.occurrenceDateTime")
3877
+ );
3878
+ }
3879
+ }
3880
+ if (tq1?.timing !== void 0) {
3881
+ props.push({ name: "occurrenceTiming", value: tq1.timing });
2393
3882
  }
2394
3883
  if (obr !== void 0 && obr.field(31).value !== "") {
2395
3884
  const reason = toFhirCodeableConcept(obr.field(31).asCwe(), ctx);
@@ -2398,6 +3887,122 @@ function buildServiceRequest(orc, obr, subjectFullUrl, encounterFullUrl, ctx) {
2398
3887
  }
2399
3888
  return { value: fhir.complex(props), issues };
2400
3889
  }
3890
+ var SPM_SHIPMENT_IDENTIFIER_TYPE = "SHIP";
3891
+ var DEFERRED_SPECIMEN_ROWS = Object.freeze([
3892
+ [3, "Specimen.parent"],
3893
+ [12, "Specimen.collection.quantity"],
3894
+ [20, "Specimen.status"]
3895
+ ]);
3896
+ function subcomponent2(spm, field, component3, sub) {
3897
+ return spm.field(field).repetitions[0]?.components[component3 - 1]?.subcomponents[sub - 1] ?? "";
3898
+ }
3899
+ function codeable(spm, field, ctx, issues) {
3900
+ const converted = toFhirCodeableConcept(spm.field(field).asCwe(), ctx);
3901
+ issues.push(...converted.issues);
3902
+ return converted.value;
3903
+ }
3904
+ function identifierFrom(spm, field, ctx, issues) {
3905
+ const converted = toFhirIdentifier(spm.field(field).asCx(), ctx);
3906
+ issues.push(...converted.issues);
3907
+ return converted.value;
3908
+ }
3909
+ function buildIdentifiers(spm, ctx, issues) {
3910
+ const identifiers = [];
3911
+ for (const component3 of [1, 2]) {
3912
+ const entityId = subcomponent2(spm, 2, component3, 1);
3913
+ if (entityId !== "") {
3914
+ identifiers.push(fhir.complex([{ name: "value", value: fhir.primitive(entityId) }]));
3915
+ }
3916
+ }
3917
+ const other = identifierFrom(spm, 31, ctx, issues);
3918
+ if (other !== void 0) identifiers.push(other);
3919
+ const shipment = orderIdentifier(spm.field(32).value, SPM_SHIPMENT_IDENTIFIER_TYPE);
3920
+ if (shipment !== void 0) identifiers.push(shipment);
3921
+ return identifiers;
3922
+ }
3923
+ function buildCollection(spm, ctx, issues) {
3924
+ const props = [];
3925
+ const method = codeable(spm, 7, ctx, issues);
3926
+ if (method !== void 0) props.push({ name: "method", value: method });
3927
+ const bodySite = codeable(spm, 8, ctx, issues);
3928
+ if (bodySite !== void 0) props.push({ name: "bodySite", value: bodySite });
3929
+ const start = subcomponent2(spm, 17, 1, 1);
3930
+ const end = subcomponent2(spm, 17, 2, 1);
3931
+ const bounds = [];
3932
+ for (const [raw, name] of [
3933
+ [start, "start"],
3934
+ [end, "end"]
3935
+ ]) {
3936
+ if (raw === "") continue;
3937
+ const converted = toFhirDateTime(hl7.parseDtm(raw), ctx.options);
3938
+ issues.push(...converted.issues);
3939
+ if (converted.value !== void 0) bounds.push({ name, value: fhir.primitive(converted.value) });
3940
+ }
3941
+ if (end !== "" && bounds.length > 0) {
3942
+ props.push({ name: "collectedPeriod", value: fhir.complex(bounds) });
3943
+ } else if (bounds[0] !== void 0) {
3944
+ props.push({ name: "collectedDateTime", value: bounds[0].value });
3945
+ }
3946
+ return props.length === 0 ? void 0 : fhir.complex(props);
3947
+ }
3948
+ function buildContainer(spm, ctx, issues) {
3949
+ const props = [];
3950
+ const type = codeable(spm, 27, ctx, issues);
3951
+ if (type !== void 0) props.push({ name: "type", value: type });
3952
+ const additive = codeable(spm, 6, ctx, issues);
3953
+ if (additive !== void 0) props.push({ name: "additiveCodeableConcept", value: additive });
3954
+ return props.length === 0 ? void 0 : fhir.list([fhir.complex(props)]);
3955
+ }
3956
+ function buildDescriptionNotes(spm) {
3957
+ const annotations = [];
3958
+ for (const repetition of spm.field(14).repetitions) {
3959
+ const text2 = repetition.components[0]?.subcomponents[0] ?? "";
3960
+ if (text2 === "") continue;
3961
+ annotations.push(fhir.complex([{ name: "text", value: fhir.primitive(text2) }]));
3962
+ }
3963
+ return annotations.length === 0 ? void 0 : fhir.list(annotations);
3964
+ }
3965
+ function buildSpecimen(spm, subjectFullUrl, ctx) {
3966
+ const issues = [];
3967
+ for (const [field, path] of DEFERRED_SPECIMEN_ROWS) {
3968
+ if (spm.field(field).value !== "") {
3969
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, `SPM.${String(field)}`, path));
3970
+ }
3971
+ }
3972
+ const props = [];
3973
+ const identifiers = buildIdentifiers(spm, ctx, issues);
3974
+ if (identifiers.length > 0) props.push({ name: "identifier", value: fhir.list(identifiers) });
3975
+ const accession = identifierFrom(spm, 30, ctx, issues);
3976
+ if (accession !== void 0) props.push({ name: "accessionIdentifier", value: accession });
3977
+ const type = codeable(spm, 4, ctx, issues);
3978
+ if (type !== void 0) props.push({ name: "type", value: type });
3979
+ if (subjectFullUrl !== void 0) {
3980
+ props.push({
3981
+ name: "subject",
3982
+ value: fhir.complex([{ name: "reference", value: fhir.primitive(subjectFullUrl) }])
3983
+ });
3984
+ }
3985
+ if (spm.field(18).value !== "") {
3986
+ const received = toFhirDateTime(spm.field(18).asTs(), ctx.options);
3987
+ issues.push(...received.issues);
3988
+ if (received.value !== void 0) {
3989
+ props.push({ name: "receivedTime", value: fhir.primitive(received.value) });
3990
+ }
3991
+ }
3992
+ const collection = buildCollection(spm, ctx, issues);
3993
+ if (collection !== void 0) props.push({ name: "collection", value: collection });
3994
+ const container = buildContainer(spm, ctx, issues);
3995
+ if (container !== void 0) props.push({ name: "container", value: container });
3996
+ const condition = codeable(spm, 24, ctx, issues);
3997
+ if (condition !== void 0) props.push({ name: "condition", value: fhir.list([condition]) });
3998
+ const note = buildDescriptionNotes(spm);
3999
+ if (note !== void 0) props.push({ name: "note", value: note });
4000
+ if (props.every((p) => p.name === "subject")) return { value: void 0, issues };
4001
+ return {
4002
+ value: fhir.complex([{ name: "resourceType", value: fhir.primitive("Specimen") }, ...props]),
4003
+ issues
4004
+ };
4005
+ }
2401
4006
 
2402
4007
  // src/messages/to-fhir.ts
2403
4008
  var IG_MAPPED_ADT_TRIGGERS = /* @__PURE__ */ new Set([
@@ -2414,9 +4019,12 @@ var IG_MAPPED_ORDER_TRIGGERS = /* @__PURE__ */ new Set(["ORM^O01", "OML^O21"]);
2414
4019
  var IG_MAPPED_IMMUNIZATION_TRIGGERS = /* @__PURE__ */ new Set(["V04"]);
2415
4020
  var IG_MAPPED_APPOINTMENT_TRIGGERS = /* @__PURE__ */ new Set(["S12"]);
2416
4021
  var IG_MAPPED_DOCUMENT_TRIGGERS = /* @__PURE__ */ new Set(["T02"]);
2417
- function passesEmitGate(resource, v2Location, fhirPath, issues) {
4022
+ function clearsEmitGate(resource) {
2418
4023
  const result = fhir.resourceType(resource) === "Patient" ? fhir.validateResource(resource, { mode: "strict" }) : fhir.validateResource(resource, { mode: "lenient", schemas: EMIT_SCHEMAS });
2419
- if (result.valid) return true;
4024
+ return result.valid;
4025
+ }
4026
+ function passesEmitGate(resource, v2Location, fhirPath, issues) {
4027
+ if (clearsEmitGate(resource)) return true;
2420
4028
  issues.push(issue(ISSUE_CODES.TRANSFORM_RESOURCE_INVALID, v2Location, fhirPath));
2421
4029
  return false;
2422
4030
  }
@@ -2455,6 +4063,7 @@ function toFhir(msg, opts = {}) {
2455
4063
  const namingSystem = opts.namingSystem ?? createNamingSystem();
2456
4064
  const ctx = { namingSystem, options: opts };
2457
4065
  const ids = new IdAllocator(opts);
4066
+ const reach = new SegmentReachLedger(msg);
2458
4067
  const code = msg.meta.messageCode;
2459
4068
  const trigger = msg.meta.triggerEvent;
2460
4069
  const messageMapGrounded = trigger !== void 0 && (code === "ADT" && IG_MAPPED_ADT_TRIGGERS.has(trigger) || code === "ORU" && IG_MAPPED_ORU_TRIGGERS.has(trigger) || code === "VXU" && IG_MAPPED_IMMUNIZATION_TRIGGERS.has(trigger) || code === "SIU" && IG_MAPPED_APPOINTMENT_TRIGGERS.has(trigger) || code === "MDM" && IG_MAPPED_DOCUMENT_TRIGGERS.has(trigger) || IG_MAPPED_ORDER_TRIGGERS.has(`${code ?? ""}^${trigger}`));
@@ -2470,16 +4079,20 @@ function toFhir(msg, opts = {}) {
2470
4079
  if (built.value !== void 0 && passesEmitGate(built.value, "PID", "Patient", issues)) {
2471
4080
  patientFullUrl = ids.next();
2472
4081
  focalEntries.push({ fullUrl: patientFullUrl, resource: built.value });
4082
+ reach.markFirstOfType("PID");
2473
4083
  }
2474
4084
  }
2475
4085
  let encounterFullUrl;
4086
+ let encounterEntryIndex = -1;
2476
4087
  const visitView = msg.visit;
2477
4088
  if (visitView !== void 0) {
2478
4089
  const built = buildEncounter(visitView, patientFullUrl, ctx);
2479
4090
  issues.push(...built.issues);
2480
4091
  if (built.value !== void 0 && passesEmitGate(built.value, "PV1", "Encounter", issues)) {
2481
4092
  encounterFullUrl = ids.next();
4093
+ encounterEntryIndex = focalEntries.length;
2482
4094
  focalEntries.push({ fullUrl: encounterFullUrl, resource: built.value });
4095
+ reach.markFirstOfType("PV1");
2483
4096
  }
2484
4097
  }
2485
4098
  const nextOfKin = msg.nextOfKin();
@@ -2491,11 +4104,116 @@ function toFhir(msg, opts = {}) {
2491
4104
  issues.push(...built.issues);
2492
4105
  if (built.value !== void 0 && passesEmitGate(built.value, `NK1[${String(i)}]`, "RelatedPerson", issues)) {
2493
4106
  focalEntries.push({ fullUrl: ids.next(), resource: built.value });
4107
+ reach.markNthOfType("NK1", i);
2494
4108
  }
2495
4109
  }
2496
4110
  } else if (nextOfKin.length > 0) {
2497
4111
  issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "NK1", "RelatedPerson.patient"));
2498
4112
  }
4113
+ const allergyFullUrls = [];
4114
+ const allergies = collectAllergies(msg);
4115
+ for (let i = 0; i < allergies.length; i++) {
4116
+ const al1 = allergies[i];
4117
+ if (al1 === void 0) continue;
4118
+ const location = `AL1[${String(i)}]`;
4119
+ if (patientFullUrl === void 0) {
4120
+ issues.push(
4121
+ issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, location, "AllergyIntolerance.patient")
4122
+ );
4123
+ continue;
4124
+ }
4125
+ const emitted = emitAllergyIntolerance(
4126
+ al1,
4127
+ patientFullUrl,
4128
+ msg.meta.version,
4129
+ ctx,
4130
+ clearsEmitGate,
4131
+ location
4132
+ );
4133
+ issues.push(...emitted.issues);
4134
+ if (emitted.value === void 0) continue;
4135
+ const url = ids.next();
4136
+ allergyFullUrls.push(url);
4137
+ focalEntries.push({ fullUrl: url, resource: emitted.value });
4138
+ reach.mark(al1);
4139
+ }
4140
+ const conditionFullUrls = [];
4141
+ const diagnoses = collectDiagnoses(msg);
4142
+ for (let i = 0; i < diagnoses.length; i++) {
4143
+ const dg1 = diagnoses[i];
4144
+ if (dg1 === void 0) continue;
4145
+ const location = `DG1[${String(i)}]`;
4146
+ issues.push(...deferredDiagnosisIssues(dg1));
4147
+ if (patientFullUrl === void 0) {
4148
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, location, "Condition.subject"));
4149
+ continue;
4150
+ }
4151
+ const built = buildCondition(dg1, patientFullUrl, ctx, location);
4152
+ issues.push(...built.issues);
4153
+ if (built.value === void 0) continue;
4154
+ if (!passesEmitGate(built.value, location, "Condition", issues)) continue;
4155
+ const url = ids.next();
4156
+ conditionFullUrls.push(url);
4157
+ focalEntries.push({ fullUrl: url, resource: built.value });
4158
+ reach.mark(dg1);
4159
+ }
4160
+ if (diagnoses.length > 0) {
4161
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "DG1", "EpisodeOfCare"));
4162
+ }
4163
+ if (conditionFullUrls.length > 0 && encounterEntryIndex >= 0) {
4164
+ const emitted = focalEntries[encounterEntryIndex];
4165
+ if (emitted !== void 0) {
4166
+ const linked = emitEncounterDiagnosis(emitted.resource, conditionFullUrls, clearsEmitGate);
4167
+ issues.push(...linked.issues);
4168
+ if (linked.value !== void 0) {
4169
+ focalEntries[encounterEntryIndex] = { fullUrl: emitted.fullUrl, resource: linked.value };
4170
+ }
4171
+ }
4172
+ }
4173
+ const procedureFullUrls = [];
4174
+ const procedures = collectProcedures(msg);
4175
+ for (let i = 0; i < procedures.length; i++) {
4176
+ const pr1 = procedures[i];
4177
+ if (pr1 === void 0) continue;
4178
+ const location = `PR1[${String(i)}]`;
4179
+ issues.push(...deferredProcedureIssues(pr1));
4180
+ if (patientFullUrl === void 0) {
4181
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, location, "Procedure.subject"));
4182
+ continue;
4183
+ }
4184
+ const built = buildProcedure(pr1, patientFullUrl, ctx);
4185
+ issues.push(...built.issues);
4186
+ if (built.value === void 0) continue;
4187
+ if (!passesEmitGate(built.value, location, "Procedure", issues)) continue;
4188
+ const url = ids.next();
4189
+ procedureFullUrls.push(url);
4190
+ focalEntries.push({ fullUrl: url, resource: built.value });
4191
+ reach.mark(pr1);
4192
+ }
4193
+ const coverages = collectCoverages(msg);
4194
+ const visitSegment = msg.allSegments().find((seg) => seg.type === "PV1");
4195
+ if (coverages.length > 0 && visitSegment !== void 0 && visitSegment.field(20).value !== "") {
4196
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, "PV1.20", "Coverage"));
4197
+ }
4198
+ const coverageFullUrls = [];
4199
+ for (let i = 0; i < coverages.length; i++) {
4200
+ const in1 = coverages[i];
4201
+ if (in1 === void 0) continue;
4202
+ const location = `IN1[${String(i)}]`;
4203
+ issues.push(...deferredCoverageIssues(in1));
4204
+ if (patientFullUrl === void 0) {
4205
+ issues.push(issue(ISSUE_CODES.TRANSFORM_ELEMENT_DROPPED, location, "Coverage.beneficiary"));
4206
+ continue;
4207
+ }
4208
+ const built = buildCoverage(in1, patientFullUrl, ctx, location);
4209
+ issues.push(...built.issues);
4210
+ if (built.value === void 0) continue;
4211
+ if (!passesEmitGate(built.value, location, "Coverage", issues)) continue;
4212
+ const url = ids.next();
4213
+ coverageFullUrls.push(url);
4214
+ focalEntries.push({ fullUrl: url, resource: built.value });
4215
+ reach.mark(in1);
4216
+ }
2499
4217
  const diagnosticReportFullUrls = [];
2500
4218
  if (code === "ORU") {
2501
4219
  const { groups, orphanObxCount } = collectReportGroups(msg);
@@ -2506,28 +4224,54 @@ function toFhir(msg, opts = {}) {
2506
4224
  const resultFullUrls = [];
2507
4225
  const observationEntries = [];
2508
4226
  for (let i = 0; i < group.observations.length; i++) {
2509
- const obx = group.observations[i];
2510
- if (obx === void 0) continue;
2511
- const built = buildObservation(obx, patientFullUrl, encounterFullUrl, ctx);
4227
+ const observation = group.observations[i];
4228
+ if (observation === void 0) continue;
4229
+ const built = buildObservation(
4230
+ observation.obx,
4231
+ patientFullUrl,
4232
+ encounterFullUrl,
4233
+ ctx,
4234
+ observation.notes
4235
+ );
2512
4236
  issues.push(...built.issues);
2513
4237
  if (built.value !== void 0 && passesEmitGate(built.value, `OBX[${String(i)}]`, "Observation", issues)) {
2514
4238
  const url = ids.next();
2515
4239
  resultFullUrls.push(url);
2516
4240
  observationEntries.push({ fullUrl: url, resource: built.value });
4241
+ reach.mark(observation.obx, ...observation.notes);
2517
4242
  }
2518
4243
  }
4244
+ const specimenFullUrls = [];
4245
+ const specimenEntries = [];
4246
+ const emittedSpecimens = [];
4247
+ for (let i = 0; i < group.specimens.length; i++) {
4248
+ const spm = group.specimens[i];
4249
+ if (spm === void 0) continue;
4250
+ const built = buildSpecimen(spm, patientFullUrl, ctx);
4251
+ issues.push(...built.issues);
4252
+ if (built.value === void 0) continue;
4253
+ if (!passesEmitGate(built.value, `SPM[${String(i)}]`, "Specimen", issues)) continue;
4254
+ const url = ids.next();
4255
+ specimenFullUrls.push(url);
4256
+ specimenEntries.push({ fullUrl: url, resource: built.value });
4257
+ emittedSpecimens.push(spm);
4258
+ }
2519
4259
  const report = buildDiagnosticReport(
2520
4260
  group.obr,
2521
4261
  resultFullUrls,
2522
4262
  patientFullUrl,
2523
4263
  encounterFullUrl,
2524
- ctx
4264
+ ctx,
4265
+ specimenFullUrls
2525
4266
  );
2526
4267
  issues.push(...report.issues);
2527
4268
  if (report.value !== void 0 && passesEmitGate(report.value, "OBR", "DiagnosticReport", issues)) {
2528
4269
  const reportUrl = ids.next();
2529
4270
  diagnosticReportFullUrls.push(reportUrl);
2530
4271
  focalEntries.push({ fullUrl: reportUrl, resource: report.value });
4272
+ reach.mark(group.obr);
4273
+ focalEntries.push(...specimenEntries);
4274
+ reach.mark(...emittedSpecimens);
2531
4275
  }
2532
4276
  focalEntries.push(...observationEntries);
2533
4277
  }
@@ -2540,9 +4284,11 @@ function toFhir(msg, opts = {}) {
2540
4284
  }
2541
4285
  for (const group of groups) {
2542
4286
  if (group.obr !== void 0 || group.orc !== void 0 && group.rxo === void 0) {
4287
+ const tq1 = readTq1(group.tq1s, "ServiceRequest", ctx);
2543
4288
  const built = buildServiceRequest(
2544
4289
  group.orc,
2545
4290
  group.obr,
4291
+ tq1,
2546
4292
  patientFullUrl,
2547
4293
  encounterFullUrl,
2548
4294
  ctx
@@ -2553,13 +4299,16 @@ function toFhir(msg, opts = {}) {
2553
4299
  const url = ids.next();
2554
4300
  orderResourceFullUrls.push(url);
2555
4301
  focalEntries.push({ fullUrl: url, resource: built.value });
4302
+ reach.mark(group.orc, group.obr, tq1.contributes ? group.tq1s[0] : void 0);
2556
4303
  }
2557
4304
  }
2558
4305
  if (group.rxo !== void 0) {
4306
+ const tq1 = readTq1(group.tq1s, "MedicationRequest", ctx);
2559
4307
  const built = buildMedicationRequest(
2560
4308
  group.rxo,
2561
4309
  group.rxrs[0],
2562
4310
  group.orc,
4311
+ tq1,
2563
4312
  patientFullUrl,
2564
4313
  encounterFullUrl,
2565
4314
  ctx
@@ -2569,6 +4318,12 @@ function toFhir(msg, opts = {}) {
2569
4318
  const url = ids.next();
2570
4319
  orderResourceFullUrls.push(url);
2571
4320
  focalEntries.push({ fullUrl: url, resource: built.value });
4321
+ reach.mark(
4322
+ group.rxo,
4323
+ group.rxrs[0],
4324
+ group.orc,
4325
+ tq1.contributes ? group.tq1s[0] : void 0
4326
+ );
2572
4327
  }
2573
4328
  }
2574
4329
  }
@@ -2589,6 +4344,7 @@ function toFhir(msg, opts = {}) {
2589
4344
  const url = ids.next();
2590
4345
  immunizationFullUrls.push(url);
2591
4346
  focalEntries.push({ fullUrl: url, resource: built.value });
4347
+ reach.mark(group.rxa, group.rxr, group.orc);
2592
4348
  }
2593
4349
  }
2594
4350
  for (const seg of msg.allSegments()) {
@@ -2597,6 +4353,7 @@ function toFhir(msg, opts = {}) {
2597
4353
  issues.push(...built.issues);
2598
4354
  if (built.value !== void 0 && passesEmitGate(built.value, "OBX", "Observation", issues)) {
2599
4355
  focalEntries.push({ fullUrl: ids.next(), resource: built.value });
4356
+ reach.mark(seg);
2600
4357
  }
2601
4358
  }
2602
4359
  }
@@ -2610,6 +4367,7 @@ function toFhir(msg, opts = {}) {
2610
4367
  const url = ids.next();
2611
4368
  appointmentFullUrls.push(url);
2612
4369
  focalEntries.push({ fullUrl: url, resource: built.value });
4370
+ reach.mark(sch, ais);
2613
4371
  }
2614
4372
  }
2615
4373
  }
@@ -2623,12 +4381,17 @@ function toFhir(msg, opts = {}) {
2623
4381
  const url = ids.next();
2624
4382
  documentFullUrls.push(url);
2625
4383
  focalEntries.push({ fullUrl: url, resource: built.value });
4384
+ reach.mark(txa, ...obxs);
2626
4385
  }
2627
4386
  }
2628
4387
  }
2629
4388
  const focus = [
2630
4389
  patientFullUrl,
2631
4390
  encounterFullUrl,
4391
+ ...allergyFullUrls,
4392
+ ...conditionFullUrls,
4393
+ ...procedureFullUrls,
4394
+ ...coverageFullUrls,
2632
4395
  ...diagnosticReportFullUrls,
2633
4396
  ...orderResourceFullUrls,
2634
4397
  ...immunizationFullUrls,
@@ -2640,63 +4403,829 @@ function toFhir(msg, opts = {}) {
2640
4403
  const entries = [];
2641
4404
  if (header.value !== void 0 && passesEmitGate(header.value, "MSH", "MessageHeader", issues)) {
2642
4405
  entries.push({ fullUrl: ids.next(), resource: header.value });
4406
+ reach.markFirstOfType("MSH");
2643
4407
  }
2644
4408
  entries.push(...focalEntries);
2645
4409
  const bundle = buildBundle(msg, entries, ctx, issues);
4410
+ issues.push(...reach.issues());
2646
4411
  return { bundle, issues };
2647
4412
  }
4413
+ function itemsOf(node) {
4414
+ if (node === void 0) return [];
4415
+ return fhir.isList(node) ? node.items : [node];
4416
+ }
4417
+ function at(node, name) {
4418
+ return fhir.getProperty(node, name);
4419
+ }
4420
+ function readString(node, location, fhirPath, issues) {
4421
+ if (node === void 0) return void 0;
4422
+ if (!fhir.isPrimitive(node) || typeof node.value !== "string") {
4423
+ issues.push(issue(ISSUE_CODES.TRANSFORM_RESOURCE_MALFORMED, location, fhirPath));
4424
+ return void 0;
4425
+ }
4426
+ return node.value === "" ? void 0 : node.value;
4427
+ }
4428
+ function readStrings(node, location, fhirPath, issues) {
4429
+ const out = [];
4430
+ for (const item of itemsOf(node)) {
4431
+ const value = readString(item, location, fhirPath, issues);
4432
+ if (value !== void 0) out.push(value);
4433
+ }
4434
+ return out;
4435
+ }
4436
+ function readComplexes(node, location, fhirPath, issues) {
4437
+ const out = [];
4438
+ for (const item of itemsOf(node)) {
4439
+ if (fhir.isComplex(item)) out.push(item);
4440
+ else issues.push(issue(ISSUE_CODES.TRANSFORM_RESOURCE_MALFORMED, location, fhirPath));
4441
+ }
4442
+ return out;
4443
+ }
4444
+ function readNumberText(node, location, fhirPath, issues) {
4445
+ if (node === void 0) return void 0;
4446
+ const value = fhir.isPrimitive(node) ? node.value : void 0;
4447
+ if (value === void 0 || typeof value === "boolean") {
4448
+ issues.push(issue(ISSUE_CODES.TRANSFORM_RESOURCE_MALFORMED, location, fhirPath));
4449
+ return void 0;
4450
+ }
4451
+ return typeof value === "string" ? value : value.raw;
4452
+ }
4453
+
4454
+ // src/reverse/v2.ts
4455
+ function component2(value) {
4456
+ return { subcomponents: value === void 0 || value === "" ? [] : [value] };
4457
+ }
4458
+ function v2Field(repetitions) {
4459
+ const present = repetitions.filter(
4460
+ (components) => components.some((value) => value !== void 0 && value !== "")
4461
+ );
4462
+ if (present.length === 0) return void 0;
4463
+ return {
4464
+ isNull: false,
4465
+ repetitions: present.map((components) => {
4466
+ let last = components.length;
4467
+ while (last > 0 && (components[last - 1] === void 0 || components[last - 1] === ""))
4468
+ last--;
4469
+ return { components: components.slice(0, last).map(component2) };
4470
+ })
4471
+ };
4472
+ }
4473
+ function segmentFields(byPosition) {
4474
+ const positions = [...byPosition.keys()];
4475
+ const highest = positions.length === 0 ? 0 : Math.max(...positions);
4476
+ const out = [];
4477
+ for (let position = 1; position <= highest; position++) {
4478
+ out.push(byPosition.get(position) ?? "");
4479
+ }
4480
+ return out;
4481
+ }
4482
+ var FHIR_DATETIME = /^(\d{4})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2})(\.\d{1,4})?(Z|[+-]\d{2}:\d{2}))?)?)?$/;
4483
+ function v2Timestamp(value) {
4484
+ const parts = FHIR_DATETIME.exec(value);
4485
+ if (parts === null) return void 0;
4486
+ const [, year, month, day, hour, minute, second, fraction, zone] = parts;
4487
+ let out = year ?? "";
4488
+ if (month !== void 0) out += month;
4489
+ if (day !== void 0) out += day;
4490
+ if (hour === void 0) return out;
4491
+ out += `${hour}${minute ?? ""}${second ?? ""}${fraction ?? ""}`;
4492
+ return out + (zone === "Z" ? "+0000" : (zone ?? "").replace(":", ""));
4493
+ }
4494
+ function v2Date(value) {
4495
+ return value.includes("T") ? void 0 : v2Timestamp(value);
4496
+ }
4497
+ var V2_NUMBER = /^-?(0|[1-9]\d*)(\.\d+)?$/;
4498
+ function v2Number(raw) {
4499
+ return V2_NUMBER.test(raw) ? raw : void 0;
4500
+ }
4501
+ function invertCodeMap(forward) {
4502
+ const sources = /* @__PURE__ */ new Map();
4503
+ for (const [v2Code, fhirCode] of Object.entries(forward)) {
4504
+ const seen = sources.get(fhirCode);
4505
+ if (seen === void 0) sources.set(fhirCode, [v2Code]);
4506
+ else seen.push(v2Code);
4507
+ }
4508
+ const inverse = {};
4509
+ for (const [fhirCode, v2Codes] of sources) {
4510
+ const only = v2Codes[0];
4511
+ if (v2Codes.length === 1 && only !== void 0) inverse[fhirCode] = only;
4512
+ }
4513
+ return Object.freeze(inverse);
4514
+ }
4515
+
4516
+ // src/reverse/coding.ts
4517
+ function reverseContext(options = {}) {
4518
+ const mnemonics = invertCodeMap({ ...DEFAULT_V2_CODE_SYSTEMS, ...options.codeSystems });
4519
+ const authorities = options.assigningAuthorities ?? {};
4520
+ return {
4521
+ mnemonicFor: (system) => Object.hasOwn(mnemonics, system) ? mnemonics[system] : void 0,
4522
+ namespaceFor: (system) => Object.hasOwn(authorities, system) ? authorities[system] : void 0,
4523
+ envelope: options.envelope ?? {}
4524
+ };
4525
+ }
4526
+ function readCoding(coding2, ctx, location, fhirPath, issues) {
4527
+ const code = readString(at(coding2, "code"), location, `${fhirPath}.code`, issues);
4528
+ const display = readString(at(coding2, "display"), location, `${fhirPath}.display`, issues);
4529
+ const system = readString(at(coding2, "system"), location, `${fhirPath}.system`, issues);
4530
+ if (code === void 0) return void 0;
4531
+ const mnemonic = system === void 0 ? void 0 : ctx.mnemonicFor(system);
4532
+ if (mnemonic === void 0) {
4533
+ issues.push(issue(ISSUE_CODES.TRANSFORM_CODE_SYSTEM_NOT_V2, location, `${fhirPath}.system`));
4534
+ return void 0;
4535
+ }
4536
+ return { code, display, mnemonic };
4537
+ }
4538
+ function codeableToCwe(concept, ctx, location, fhirPath, issues) {
4539
+ const codings = [];
4540
+ for (const coding2 of readComplexes(
4541
+ at(concept, "coding"),
4542
+ location,
4543
+ `${fhirPath}.coding`,
4544
+ issues
4545
+ )) {
4546
+ const read = readCoding(coding2, ctx, location, `${fhirPath}.coding`, issues);
4547
+ if (read !== void 0) codings.push(read);
4548
+ }
4549
+ const text2 = readString(at(concept, "text"), location, `${fhirPath}.text`, issues);
4550
+ if (codings.length === 0 && text2 === void 0) return void 0;
4551
+ if (codings.length > 2) {
4552
+ issues.push(
4553
+ issue(ISSUE_CODES.TRANSFORM_VALUE_NOT_REPRESENTABLE, location, `${fhirPath}.coding`)
4554
+ );
4555
+ }
4556
+ const primary = codings[0];
4557
+ const alternate = codings[1];
4558
+ return [
4559
+ primary?.code,
4560
+ primary?.display,
4561
+ primary?.mnemonic,
4562
+ alternate?.code,
4563
+ alternate?.display,
4564
+ alternate?.mnemonic,
4565
+ void 0,
4566
+ void 0,
4567
+ text2
4568
+ ];
4569
+ }
4570
+ function codeInSystem(concept, system, location, fhirPath, issues) {
4571
+ let found;
4572
+ let foreign = false;
4573
+ for (const coding2 of readComplexes(
4574
+ at(concept, "coding"),
4575
+ location,
4576
+ `${fhirPath}.coding`,
4577
+ issues
4578
+ )) {
4579
+ const codingSystem = readString(at(coding2, "system"), location, `${fhirPath}.system`, issues);
4580
+ const code = readString(at(coding2, "code"), location, `${fhirPath}.code`, issues);
4581
+ if (code === void 0) continue;
4582
+ if (codingSystem === system) found ??= code;
4583
+ else foreign = true;
4584
+ }
4585
+ if (found === void 0 && foreign) {
4586
+ issues.push(issue(ISSUE_CODES.TRANSFORM_CODE_SYSTEM_NOT_V2, location, `${fhirPath}.system`));
4587
+ }
4588
+ return found;
4589
+ }
4590
+ var NAMED_RESOURCES = /* @__PURE__ */ new Set([
4591
+ "Appointment",
4592
+ "Bundle",
4593
+ "DiagnosticReport",
4594
+ "DocumentReference",
4595
+ "Encounter",
4596
+ "Immunization",
4597
+ "MedicationRequest",
4598
+ "MessageHeader",
4599
+ "Observation",
4600
+ "OperationOutcome",
4601
+ "Patient",
4602
+ "RelatedPerson",
4603
+ "ServiceRequest"
4604
+ ]);
4605
+ function isNonEmptyString(value) {
4606
+ return typeof value === "string" && value.trim() !== "";
4607
+ }
4608
+ var BARE_TRIGGER = /^[^\s|^~\\&\r\n]+$/;
4609
+ function hasTrigger(trigger, issues) {
4610
+ if (!isNonEmptyString(trigger)) {
4611
+ issues.push(issue(ISSUE_CODES.TRANSFORM_MISSING_TRIGGER, "MSH.9.2", "trigger"));
4612
+ return false;
4613
+ }
4614
+ if (!BARE_TRIGGER.test(trigger)) {
4615
+ issues.push(issue(ISSUE_CODES.TRANSFORM_VALUE_NOT_REPRESENTABLE, "MSH.9.2", "trigger"));
4616
+ return false;
4617
+ }
4618
+ return true;
4619
+ }
4620
+ function readResource(input, expected, location, issues) {
4621
+ if (!fhir.isComplex(input)) {
4622
+ issues.push(issue(ISSUE_CODES.TRANSFORM_RESOURCE_MALFORMED, location, "Resource"));
4623
+ return void 0;
4624
+ }
4625
+ const type = fhir.resourceType(input);
4626
+ if (type === void 0) {
4627
+ issues.push(issue(ISSUE_CODES.TRANSFORM_RESOURCE_MALFORMED, location, "Resource.resourceType"));
4628
+ return void 0;
4629
+ }
4630
+ if (type !== expected) {
4631
+ const named = NAMED_RESOURCES.has(type) ? type : "Resource";
4632
+ issues.push(issue(ISSUE_CODES.TRANSFORM_UNSUPPORTED_RESOURCE, location, named));
4633
+ return void 0;
4634
+ }
4635
+ return input;
4636
+ }
4637
+ function flagUnmapped(resource, mapped, targets, resourceName, segment, issues) {
4638
+ for (const property of resource.properties) {
4639
+ if (mapped.has(property.name)) continue;
4640
+ const known = Object.hasOwn(targets, property.name);
4641
+ issues.push(
4642
+ issue(
4643
+ ISSUE_CODES.TRANSFORM_NO_V2_TARGET,
4644
+ known ? targets[property.name] ?? segment : segment,
4645
+ known ? `${resourceName}.${property.name}` : resourceName
4646
+ )
4647
+ );
4648
+ }
4649
+ }
4650
+ function emitMessage(shape, trigger, byPosition, ctx, issues) {
4651
+ if (byPosition.size === 0) {
4652
+ issues.push(
4653
+ issue(ISSUE_CODES.TRANSFORM_NO_V2_MESSAGE_EMITTED, shape.segment, shape.resourceName)
4654
+ );
4655
+ return void 0;
4656
+ }
4657
+ for (const field of shape.required) {
4658
+ if (byPosition.has(field.position)) continue;
4659
+ issues.push(
4660
+ issue(ISSUE_CODES.TRANSFORM_V2_REQUIRED_FIELD_ABSENT, field.location, field.fhirPath)
4661
+ );
4662
+ }
4663
+ return hl7.buildMessage({ ...ctx.envelope, type: `${shape.messageCode}^${trigger}` }).addSegment(
4664
+ shape.segment,
4665
+ segmentFields(byPosition)
4666
+ );
4667
+ }
4668
+
4669
+ // src/reverse/patient.ts
4670
+ var GENDER_TO_V2 = invertCodeMap(ADMINISTRATIVE_GENDER_MAP);
4671
+ var NAME_USE_TO_V2 = invertCodeMap(NAME_USE_MAP);
4672
+ var ADDRESS_USE_TO_V2 = invertCodeMap(ADDRESS_USE_MAP);
4673
+ var PATIENT_MAPPED = /* @__PURE__ */ new Set([
4674
+ "resourceType",
4675
+ "identifier",
4676
+ "name",
4677
+ "birthDate",
4678
+ "gender",
4679
+ "address"
4680
+ ]);
4681
+ var PID_REQUIRED = [
4682
+ // PID-3 Patient Identifier List, v2.5.1 item 00106.
4683
+ { position: 3, location: "PID.3", fhirPath: "Patient.identifier" },
4684
+ // PID-5 Patient Name, v2.5.1 item 00108.
4685
+ { position: 5, location: "PID.5", fhirPath: "Patient.name" }
4686
+ ];
4687
+ var PATIENT_SHAPE = {
4688
+ messageCode: "ADT",
4689
+ segment: "PID",
4690
+ resourceName: "Patient",
4691
+ required: PID_REQUIRED
4692
+ };
4693
+ var PATIENT_UNMAPPED = Object.freeze({
4694
+ telecom: "PID.13",
4695
+ communication: "PID.15",
4696
+ maritalStatus: "PID.16",
4697
+ multipleBirthBoolean: "PID.24",
4698
+ multipleBirthInteger: "PID.25",
4699
+ deceasedDateTime: "PID.29",
4700
+ deceasedBoolean: "PID.30",
4701
+ contact: "NK1"
4702
+ });
4703
+ function invertible(inverse, code, location, fhirPath, issues) {
4704
+ if (Object.hasOwn(inverse, code)) return inverse[code];
4705
+ issues.push(issue(ISSUE_CODES.TRANSFORM_CODE_NOT_INVERTIBLE, location, fhirPath));
4706
+ return void 0;
4707
+ }
4708
+ function identifierComponents(identifier, ctx, issues) {
4709
+ const value = readString(at(identifier, "value"), "CX.1", "Patient.identifier.value", issues);
4710
+ if (value === void 0) {
4711
+ issues.push(issue(ISSUE_CODES.TRANSFORM_NO_V2_TARGET, "CX.1", "Patient.identifier"));
4712
+ return void 0;
4713
+ }
4714
+ const system = readString(at(identifier, "system"), "CX.4", "Patient.identifier.system", issues);
4715
+ let namespace;
4716
+ if (system !== void 0) {
4717
+ namespace = ctx.namespaceFor(system);
4718
+ if (namespace === void 0) {
4719
+ issues.push(issue(ISSUE_CODES.TRANSFORM_NO_V2_TARGET, "CX.4", "Patient.identifier.system"));
4720
+ }
4721
+ }
4722
+ const typeConcept = readComplexes(
4723
+ at(identifier, "type"),
4724
+ "CX.5",
4725
+ "Patient.identifier.type",
4726
+ issues
4727
+ )[0];
4728
+ const typeCode = typeConcept === void 0 ? void 0 : codeInSystem(typeConcept, V2_0203_SYSTEM, "CX.5", "Patient.identifier.type", issues);
4729
+ return [value, void 0, void 0, namespace, typeCode];
4730
+ }
4731
+ function nameComponents(name, issues) {
4732
+ const family = readString(at(name, "family"), "XPN.1", "Patient.name.family", issues);
4733
+ const given = readStrings(at(name, "given"), "XPN.2", "Patient.name.given", issues);
4734
+ const prefix = readStrings(at(name, "prefix"), "XPN.5", "Patient.name.prefix", issues);
4735
+ const suffix = readStrings(at(name, "suffix"), "XPN.4", "Patient.name.suffix", issues);
4736
+ const use = readString(at(name, "use"), "XPN.7", "Patient.name.use", issues);
4737
+ if (given.length > 2) {
4738
+ issues.push(
4739
+ issue(ISSUE_CODES.TRANSFORM_VALUE_NOT_REPRESENTABLE, "XPN.3", "Patient.name.given")
4740
+ );
4741
+ }
4742
+ if (prefix.length > 1) {
4743
+ issues.push(
4744
+ issue(ISSUE_CODES.TRANSFORM_VALUE_NOT_REPRESENTABLE, "XPN.5", "Patient.name.prefix")
4745
+ );
4746
+ }
4747
+ if (suffix.length > 1) {
4748
+ issues.push(
4749
+ issue(ISSUE_CODES.TRANSFORM_VALUE_NOT_REPRESENTABLE, "XPN.4", "Patient.name.suffix")
4750
+ );
4751
+ }
4752
+ const nameType = use === void 0 ? void 0 : invertible(NAME_USE_TO_V2, use, "XPN.7", "Patient.name.use", issues);
4753
+ return [family, given[0], given[1], suffix[0], prefix[0], void 0, nameType];
4754
+ }
4755
+ function addressComponents(address, issues) {
4756
+ const line = readStrings(at(address, "line"), "XAD.1", "Patient.address.line", issues);
4757
+ const city = readString(at(address, "city"), "XAD.3", "Patient.address.city", issues);
4758
+ const state = readString(at(address, "state"), "XAD.4", "Patient.address.state", issues);
4759
+ const postal = readString(
4760
+ at(address, "postalCode"),
4761
+ "XAD.5",
4762
+ "Patient.address.postalCode",
4763
+ issues
4764
+ );
4765
+ const country = readString(at(address, "country"), "XAD.6", "Patient.address.country", issues);
4766
+ const district = readString(at(address, "district"), "XAD.9", "Patient.address.district", issues);
4767
+ const use = readString(at(address, "use"), "XAD.7", "Patient.address.use", issues);
4768
+ const type = readString(at(address, "type"), "XAD.7", "Patient.address.type", issues);
4769
+ if (line.length > 2) {
4770
+ issues.push(
4771
+ issue(ISSUE_CODES.TRANSFORM_VALUE_NOT_REPRESENTABLE, "XAD.2", "Patient.address.line")
4772
+ );
4773
+ }
4774
+ const addressType = use === void 0 ? void 0 : invertible(ADDRESS_USE_TO_V2, use, "XAD.7", "Patient.address.use", issues);
4775
+ if (type !== void 0) {
4776
+ issues.push(issue(ISSUE_CODES.TRANSFORM_CODE_NOT_INVERTIBLE, "XAD.7", "Patient.address.type"));
4777
+ }
4778
+ return [line[0], line[1], city, state, postal, country, addressType, void 0, district];
4779
+ }
4780
+ function pidFields(patient, ctx, issues) {
4781
+ const fields = /* @__PURE__ */ new Map();
4782
+ const identifiers = [];
4783
+ for (const identifier of readComplexes(
4784
+ at(patient, "identifier"),
4785
+ "PID.3",
4786
+ "Patient.identifier",
4787
+ issues
4788
+ )) {
4789
+ const components = identifierComponents(identifier, ctx, issues);
4790
+ if (components !== void 0) identifiers.push(components);
4791
+ }
4792
+ const pid3 = v2Field(identifiers);
4793
+ if (pid3 !== void 0) fields.set(3, pid3);
4794
+ const names = readComplexes(at(patient, "name"), "PID.5", "Patient.name", issues).map(
4795
+ (name) => nameComponents(name, issues)
4796
+ );
4797
+ const pid5 = v2Field(names);
4798
+ if (pid5 !== void 0) fields.set(5, pid5);
4799
+ const birthDate = readString(at(patient, "birthDate"), "PID.7", "Patient.birthDate", issues);
4800
+ if (birthDate !== void 0) {
4801
+ const dtm = v2Date(birthDate);
4802
+ if (dtm === void 0) {
4803
+ issues.push(
4804
+ issue(ISSUE_CODES.TRANSFORM_VALUE_NOT_REPRESENTABLE, "PID.7", "Patient.birthDate")
4805
+ );
4806
+ } else {
4807
+ const pid7 = v2Field([[dtm]]);
4808
+ if (pid7 !== void 0) fields.set(7, pid7);
4809
+ }
4810
+ }
4811
+ const gender = readString(at(patient, "gender"), "PID.8", "Patient.gender", issues);
4812
+ const sex = gender === void 0 ? void 0 : invertible(GENDER_TO_V2, gender, "PID.8", "Patient.gender", issues);
4813
+ const pid8 = sex === void 0 ? void 0 : v2Field([[sex]]);
4814
+ if (pid8 !== void 0) fields.set(8, pid8);
4815
+ const addresses = readComplexes(at(patient, "address"), "PID.11", "Patient.address", issues).map(
4816
+ (address) => addressComponents(address, issues)
4817
+ );
4818
+ const pid11 = v2Field(addresses);
4819
+ if (pid11 !== void 0) fields.set(11, pid11);
4820
+ return fields;
4821
+ }
4822
+ function toV2Patient(resource, trigger, options = {}) {
4823
+ const issues = [];
4824
+ const triggerOk = hasTrigger(trigger, issues);
4825
+ const patient = readResource(resource, "Patient", "PID", issues);
4826
+ if (!triggerOk || patient === void 0) return { value: void 0, issues };
4827
+ flagUnmapped(patient, PATIENT_MAPPED, PATIENT_UNMAPPED, "Patient", "PID", issues);
4828
+ const ctx = reverseContext(options);
4829
+ const fields = pidFields(patient, ctx, issues);
4830
+ const value = emitMessage(PATIENT_SHAPE, trigger, fields, ctx, issues);
4831
+ return { value, issues };
4832
+ }
4833
+
4834
+ // src/reverse/observation.ts
4835
+ var OBSERVATION_STATUS_TO_V2 = invertCodeMap(OBSERVATION_STATUS_MAP);
4836
+ var SN_COMPARATORS2 = /* @__PURE__ */ new Set(["<", "<=", ">=", ">"]);
4837
+ var OBSERVATION_MAPPED = /* @__PURE__ */ new Set([
4838
+ "resourceType",
4839
+ "code",
4840
+ "status",
4841
+ "effectiveDateTime",
4842
+ "interpretation",
4843
+ "referenceRange",
4844
+ "valueQuantity",
4845
+ "valueCodeableConcept",
4846
+ "valueString",
4847
+ "valueDateTime"
4848
+ ]);
4849
+ var OBX_REQUIRED = [
4850
+ // OBX-3 Observation Identifier. UNREACHABLE TODAY BY CONSTRUCTION, and kept
4851
+ // anyway: `obxFields` returns no field at all when it cannot build OBX-3, so an
4852
+ // emitted OBX always carries one and this row can only fire if that early return
4853
+ // is ever relaxed. It guards the next edit rather than changing this one.
4854
+ { position: 3, location: "OBX.3", fhirPath: "Observation.code" },
4855
+ // OBX-11 Observation Result Status.
4856
+ { position: 11, location: "OBX.11", fhirPath: "Observation.status" }
4857
+ ];
4858
+ var OBSERVATION_SHAPE = {
4859
+ messageCode: "ORU",
4860
+ segment: "OBX",
4861
+ resourceName: "Observation",
4862
+ required: OBX_REQUIRED
4863
+ };
4864
+ var OBSERVATION_UNMAPPED = Object.freeze({
4865
+ valueBoolean: "OBX.5",
4866
+ valueInteger: "OBX.5",
4867
+ valueRange: "OBX.5",
4868
+ valueRatio: "OBX.5",
4869
+ valueTime: "OBX.5",
4870
+ valuePeriod: "OBX.5",
4871
+ valueSampledData: "OBX.5",
4872
+ valueAttachment: "OBX.5",
4873
+ dataAbsentReason: "OBX.5",
4874
+ effectivePeriod: "OBX.14",
4875
+ effectiveInstant: "OBX.14",
4876
+ issued: "OBX.19",
4877
+ performer: "OBX.16",
4878
+ method: "OBX.17",
4879
+ specimen: "OBX.18",
4880
+ device: "OBX.18",
4881
+ bodySite: "OBX.20",
4882
+ identifier: "OBX.21",
4883
+ note: "NTE",
4884
+ subject: "PID",
4885
+ encounter: "PV1",
4886
+ basedOn: "OBR",
4887
+ category: "OBR",
4888
+ component: "OBX",
4889
+ hasMember: "OBX",
4890
+ derivedFrom: "OBX"
4891
+ });
4892
+ function unitComponents(quantity, ctx, issues) {
4893
+ const unit = readString(at(quantity, "unit"), "OBX.6", "Observation.valueQuantity.unit", issues);
4894
+ const code = readString(at(quantity, "code"), "OBX.6", "Observation.valueQuantity.code", issues);
4895
+ const system = readString(
4896
+ at(quantity, "system"),
4897
+ "OBX.6",
4898
+ "Observation.valueQuantity.system",
4899
+ issues
4900
+ );
4901
+ const mnemonic = system === void 0 ? void 0 : ctx.mnemonicFor(system);
4902
+ if (code !== void 0 && mnemonic !== void 0) return [code, unit, mnemonic];
4903
+ if (code !== void 0) {
4904
+ issues.push(
4905
+ issue(ISSUE_CODES.TRANSFORM_CODE_SYSTEM_NOT_V2, "OBX.6", "Observation.valueQuantity.system")
4906
+ );
4907
+ }
4908
+ return unit === void 0 ? void 0 : [void 0, unit];
4909
+ }
4910
+ function quantityValue(quantity, ctx, issues) {
4911
+ const raw = readNumberText(
4912
+ at(quantity, "value"),
4913
+ "OBX.5",
4914
+ "Observation.valueQuantity.value",
4915
+ issues
4916
+ );
4917
+ const magnitude = raw === void 0 ? void 0 : v2Number(raw);
4918
+ if (magnitude === void 0) {
4919
+ if (raw !== void 0) {
4920
+ issues.push(
4921
+ issue(
4922
+ ISSUE_CODES.TRANSFORM_VALUE_NOT_REPRESENTABLE,
4923
+ "OBX.5",
4924
+ "Observation.valueQuantity.value"
4925
+ )
4926
+ );
4927
+ }
4928
+ return void 0;
4929
+ }
4930
+ const units = unitComponents(quantity, ctx, issues);
4931
+ const comparator = readString(
4932
+ at(quantity, "comparator"),
4933
+ "OBX.5",
4934
+ "Observation.valueQuantity.comparator",
4935
+ issues
4936
+ );
4937
+ if (comparator === void 0) return { valueType: "NM", value: [magnitude], units };
4938
+ if (!SN_COMPARATORS2.has(comparator)) {
4939
+ issues.push(
4940
+ issue(
4941
+ ISSUE_CODES.TRANSFORM_VALUE_NOT_REPRESENTABLE,
4942
+ "OBX.5",
4943
+ "Observation.valueQuantity.comparator"
4944
+ )
4945
+ );
4946
+ return void 0;
4947
+ }
4948
+ return { valueType: "SN", value: [comparator, magnitude], units };
4949
+ }
4950
+ function observationValue(observation, ctx, issues) {
4951
+ const quantity = readComplexes(
4952
+ at(observation, "valueQuantity"),
4953
+ "OBX.5",
4954
+ "Observation.valueQuantity",
4955
+ issues
4956
+ )[0];
4957
+ if (quantity !== void 0) return quantityValue(quantity, ctx, issues);
4958
+ const concept = readComplexes(
4959
+ at(observation, "valueCodeableConcept"),
4960
+ "OBX.5",
4961
+ "Observation.valueCodeableConcept",
4962
+ issues
4963
+ )[0];
4964
+ if (concept !== void 0) {
4965
+ const cwe = codeableToCwe(concept, ctx, "OBX.5", "Observation.valueCodeableConcept", issues);
4966
+ return cwe === void 0 ? void 0 : { valueType: "CWE", value: cwe, units: void 0 };
4967
+ }
4968
+ const text2 = readString(
4969
+ at(observation, "valueString"),
4970
+ "OBX.5",
4971
+ "Observation.valueString",
4972
+ issues
4973
+ );
4974
+ if (text2 !== void 0) return { valueType: "ST", value: [text2], units: void 0 };
4975
+ const instant = readString(
4976
+ at(observation, "valueDateTime"),
4977
+ "OBX.5",
4978
+ "Observation.valueDateTime",
4979
+ issues
4980
+ );
4981
+ if (instant !== void 0) {
4982
+ const dtm = v2Timestamp(instant);
4983
+ if (dtm === void 0) {
4984
+ issues.push(
4985
+ issue(ISSUE_CODES.TRANSFORM_VALUE_NOT_REPRESENTABLE, "OBX.5", "Observation.valueDateTime")
4986
+ );
4987
+ return void 0;
4988
+ }
4989
+ return { valueType: "DTM", value: [dtm], units: void 0 };
4990
+ }
4991
+ return void 0;
4992
+ }
4993
+ function interpretationField(observation, issues) {
4994
+ const flags = [];
4995
+ for (const concept of readComplexes(
4996
+ at(observation, "interpretation"),
4997
+ "OBX.8",
4998
+ "Observation.interpretation",
4999
+ issues
5000
+ )) {
5001
+ const code = codeInSystem(
5002
+ concept,
5003
+ V3_OBSERVATION_INTERPRETATION_SYSTEM,
5004
+ "OBX.8",
5005
+ "Observation.interpretation",
5006
+ issues
5007
+ );
5008
+ if (code === void 0) continue;
5009
+ if (HL70078_INTERPRETATION_CODES.has(code)) flags.push([code]);
5010
+ else {
5011
+ issues.push(
5012
+ issue(ISSUE_CODES.TRANSFORM_CODE_NOT_INVERTIBLE, "OBX.8", "Observation.interpretation")
5013
+ );
5014
+ }
5015
+ }
5016
+ return v2Field(flags);
5017
+ }
5018
+ function referenceRangeField(observation, issues) {
5019
+ const ranges = readComplexes(
5020
+ at(observation, "referenceRange"),
5021
+ "OBX.7",
5022
+ "Observation.referenceRange",
5023
+ issues
5024
+ );
5025
+ if (ranges.length > 1) {
5026
+ issues.push(
5027
+ issue(ISSUE_CODES.TRANSFORM_VALUE_NOT_REPRESENTABLE, "OBX.7", "Observation.referenceRange")
5028
+ );
5029
+ }
5030
+ const first = ranges[0];
5031
+ if (first === void 0) return void 0;
5032
+ const text2 = readString(at(first, "text"), "OBX.7", "Observation.referenceRange.text", issues);
5033
+ if (text2 === void 0) {
5034
+ issues.push(issue(ISSUE_CODES.TRANSFORM_NO_V2_TARGET, "OBX.7", "Observation.referenceRange"));
5035
+ return void 0;
5036
+ }
5037
+ return v2Field([[text2]]);
5038
+ }
5039
+ function obxFields(observation, ctx, issues) {
5040
+ const fields = /* @__PURE__ */ new Map();
5041
+ const concept = readComplexes(at(observation, "code"), "OBX.3", "Observation.code", issues)[0];
5042
+ if (concept === void 0) {
5043
+ issues.push(issue(ISSUE_CODES.TRANSFORM_RESOURCE_MALFORMED, "OBX.3", "Observation.code"));
5044
+ return fields;
5045
+ }
5046
+ const cwe = codeableToCwe(concept, ctx, "OBX.3", "Observation.code", issues);
5047
+ const obx3 = cwe === void 0 ? void 0 : v2Field([cwe]);
5048
+ if (obx3 === void 0) return fields;
5049
+ fields.set(3, obx3);
5050
+ const value = observationValue(observation, ctx, issues);
5051
+ if (value !== void 0) {
5052
+ const obx2 = v2Field([[value.valueType]]);
5053
+ const obx5 = v2Field([value.value]);
5054
+ if (obx2 !== void 0 && obx5 !== void 0) {
5055
+ fields.set(2, obx2);
5056
+ fields.set(5, obx5);
5057
+ }
5058
+ const obx6 = value.units === void 0 ? void 0 : v2Field([value.units]);
5059
+ if (obx6 !== void 0) fields.set(6, obx6);
5060
+ }
5061
+ const referenceRange = referenceRangeField(observation, issues);
5062
+ if (referenceRange !== void 0) fields.set(7, referenceRange);
5063
+ const interpretation = interpretationField(observation, issues);
5064
+ if (interpretation !== void 0) fields.set(8, interpretation);
5065
+ const status = readString(at(observation, "status"), "OBX.11", "Observation.status", issues);
5066
+ if (status !== void 0) {
5067
+ const code = Object.hasOwn(OBSERVATION_STATUS_TO_V2, status) ? OBSERVATION_STATUS_TO_V2[status] : void 0;
5068
+ if (code === void 0) {
5069
+ issues.push(issue(ISSUE_CODES.TRANSFORM_CODE_NOT_INVERTIBLE, "OBX.11", "Observation.status"));
5070
+ } else {
5071
+ const obx11 = v2Field([[code]]);
5072
+ if (obx11 !== void 0) fields.set(11, obx11);
5073
+ }
5074
+ }
5075
+ const effective = readString(
5076
+ at(observation, "effectiveDateTime"),
5077
+ "OBX.14",
5078
+ "Observation.effectiveDateTime",
5079
+ issues
5080
+ );
5081
+ if (effective !== void 0) {
5082
+ const dtm = v2Timestamp(effective);
5083
+ if (dtm === void 0) {
5084
+ issues.push(
5085
+ issue(
5086
+ ISSUE_CODES.TRANSFORM_VALUE_NOT_REPRESENTABLE,
5087
+ "OBX.14",
5088
+ "Observation.effectiveDateTime"
5089
+ )
5090
+ );
5091
+ } else {
5092
+ const obx14 = v2Field([[dtm]]);
5093
+ if (obx14 !== void 0) fields.set(14, obx14);
5094
+ }
5095
+ }
5096
+ return fields;
5097
+ }
5098
+ function toV2Observation(resource, trigger, options = {}) {
5099
+ const issues = [];
5100
+ const triggerOk = hasTrigger(trigger, issues);
5101
+ const observation = readResource(resource, "Observation", "OBX", issues);
5102
+ if (!triggerOk || observation === void 0) return { value: void 0, issues };
5103
+ flagUnmapped(observation, OBSERVATION_MAPPED, OBSERVATION_UNMAPPED, "Observation", "OBX", issues);
5104
+ const ctx = reverseContext(options);
5105
+ const fields = obxFields(observation, ctx, issues);
5106
+ const value = emitMessage(OBSERVATION_SHAPE, trigger, fields, ctx, issues);
5107
+ return { value, issues };
5108
+ }
2648
5109
 
2649
5110
  // src/index.ts
2650
- var VERSION = "0.0.9";
5111
+ var VERSION = "0.1.1";
2651
5112
 
2652
5113
  exports.ADDRESS_TYPE_MAP = ADDRESS_TYPE_MAP;
2653
5114
  exports.ADDRESS_USE_MAP = ADDRESS_USE_MAP;
5115
+ exports.ADDRESS_USE_TO_V2 = ADDRESS_USE_TO_V2;
2654
5116
  exports.ADMINISTRATIVE_GENDER_MAP = ADMINISTRATIVE_GENDER_MAP;
5117
+ exports.AL1_ONSET_WITHDRAWN_AT = AL1_ONSET_WITHDRAWN_AT;
5118
+ exports.ALLERGY_CATEGORY_VALUE_MAP = ALLERGY_CATEGORY_VALUE_MAP;
5119
+ exports.ALLERGY_CLINICAL_STATUS_CODE = ALLERGY_CLINICAL_STATUS_CODE;
5120
+ exports.ALLERGY_CLINICAL_STATUS_SYSTEM = ALLERGY_CLINICAL_STATUS_SYSTEM;
5121
+ exports.ALLERGY_CRITICALITY_VALUE_MAP = ALLERGY_CRITICALITY_VALUE_MAP;
5122
+ exports.ALLERGY_INTOLERANCE_CATEGORY_SYSTEM = ALLERGY_INTOLERANCE_CATEGORY_SYSTEM;
5123
+ exports.ALLERGY_INTOLERANCE_CRITICALITY_SYSTEM = ALLERGY_INTOLERANCE_CRITICALITY_SYSTEM;
5124
+ exports.ALLERGY_INTOLERANCE_TYPE_SYSTEM = ALLERGY_INTOLERANCE_TYPE_SYSTEM;
5125
+ exports.ALLERGY_ORIGINAL_CATEGORY_VALUE_MAP = ALLERGY_ORIGINAL_CATEGORY_VALUE_MAP;
5126
+ exports.ALLERGY_ORIGINAL_CRITICALITY_VALUE_MAP = ALLERGY_ORIGINAL_CRITICALITY_VALUE_MAP;
5127
+ exports.ALLERGY_TYPE_VALUE_MAP = ALLERGY_TYPE_VALUE_MAP;
5128
+ exports.ALTERNATE_CODES_EXTENSION_URL = ALTERNATE_CODES_EXTENSION_URL;
2655
5129
  exports.APPOINTMENT_STATUS_MAP = APPOINTMENT_STATUS_MAP;
2656
5130
  exports.APPOINTMENT_TYPE_VALUE_MAP = APPOINTMENT_TYPE_VALUE_MAP;
5131
+ exports.CONDITION_ENTERED_IN_ERROR = CONDITION_ENTERED_IN_ERROR;
5132
+ exports.CONDITION_VERIFICATION_STATUS_SYSTEM = CONDITION_VERIFICATION_STATUS_SYSTEM;
5133
+ exports.COVERAGE_STATUS_UNKNOWN = COVERAGE_STATUS_UNKNOWN;
5134
+ exports.COVERAGE_SUBSCRIBER_ID_EXTENSION_URL = COVERAGE_SUBSCRIBER_ID_EXTENSION_URL;
2657
5135
  exports.DEFAULT_V2_CODE_SYSTEMS = DEFAULT_V2_CODE_SYSTEMS;
5136
+ exports.DG1_RETRACTION_ACTION_CODE = DG1_RETRACTION_ACTION_CODE;
2658
5137
  exports.DIAGNOSTIC_REPORT_STATUS_MAP = DIAGNOSTIC_REPORT_STATUS_MAP;
5138
+ exports.ED_BASE64_ENCODING = ED_BASE64_ENCODING;
2659
5139
  exports.ENCOUNTER_CLASS_V3_MAP = ENCOUNTER_CLASS_V3_MAP;
2660
5140
  exports.ENCOUNTER_STATUS_MAP = ENCOUNTER_STATUS_MAP;
2661
5141
  exports.FATAL_CODES = FATAL_CODES;
5142
+ exports.GENDER_TO_V2 = GENDER_TO_V2;
2662
5143
  exports.HL70078_INTERPRETATION_CODES = HL70078_INTERPRETATION_CODES;
5144
+ exports.IG_ALLERGY_VALUE_MAPS_PUBLISHED = IG_ALLERGY_VALUE_MAPS_PUBLISHED;
5145
+ exports.IG_ALLERGY_VALUE_MAPS_RETRIEVED = IG_ALLERGY_VALUE_MAPS_RETRIEVED;
5146
+ exports.IG_ALLERGY_VALUE_MAPS_SOURCE = IG_ALLERGY_VALUE_MAPS_SOURCE;
5147
+ exports.IG_ALLERGY_VALUE_MAPS_VERSION = IG_ALLERGY_VALUE_MAPS_VERSION;
2663
5148
  exports.IG_MAPPED_ADT_TRIGGERS = IG_MAPPED_ADT_TRIGGERS;
2664
5149
  exports.IG_MAPPED_APPOINTMENT_TRIGGERS = IG_MAPPED_APPOINTMENT_TRIGGERS;
2665
5150
  exports.IG_MAPPED_DOCUMENT_TRIGGERS = IG_MAPPED_DOCUMENT_TRIGGERS;
2666
5151
  exports.IG_MAPPED_IMMUNIZATION_TRIGGERS = IG_MAPPED_IMMUNIZATION_TRIGGERS;
2667
5152
  exports.IG_MAPPED_ORDER_TRIGGERS = IG_MAPPED_ORDER_TRIGGERS;
2668
5153
  exports.IG_MAPPED_ORU_TRIGGERS = IG_MAPPED_ORU_TRIGGERS;
5154
+ exports.IG_MAPPED_SEGMENT_NAMES = IG_MAPPED_SEGMENT_NAMES;
5155
+ exports.IG_SEGMENT_MAPS_PUBLISHED = IG_SEGMENT_MAPS_PUBLISHED;
5156
+ exports.IG_SEGMENT_MAPS_RETRIEVED = IG_SEGMENT_MAPS_RETRIEVED;
5157
+ exports.IG_SEGMENT_MAPS_SOURCE = IG_SEGMENT_MAPS_SOURCE;
5158
+ exports.IG_SEGMENT_MAPS_VERSION = IG_SEGMENT_MAPS_VERSION;
2669
5159
  exports.IMMUNIZATION_STATUS_MAP = IMMUNIZATION_STATUS_MAP;
5160
+ exports.IN1_SUBSCRIBER_NUMBER_TYPE = IN1_SUBSCRIBER_NUMBER_TYPE;
2670
5161
  exports.ISSUE_CODES = ISSUE_CODES;
2671
5162
  exports.ISSUE_REGISTRY = ISSUE_REGISTRY;
2672
5163
  exports.NAME_USE_MAP = NAME_USE_MAP;
5164
+ exports.NAME_USE_TO_V2 = NAME_USE_TO_V2;
2673
5165
  exports.OBSERVATION_STATUS_MAP = OBSERVATION_STATUS_MAP;
5166
+ exports.OBSERVATION_STATUS_TO_V2 = OBSERVATION_STATUS_TO_V2;
5167
+ exports.OBSERVATION_VALUE_ATTACHMENT_EXTENSION_URL = OBSERVATION_VALUE_ATTACHMENT_EXTENSION_URL;
5168
+ exports.PROCEDURE_STATUS_UNKNOWN = PROCEDURE_STATUS_UNKNOWN;
2674
5169
  exports.REQUEST_STATUS_MAP = REQUEST_STATUS_MAP;
2675
5170
  exports.ROUTE_VALUE_MAP = ROUTE_VALUE_MAP;
5171
+ exports.SAMPLED_DATA_ABSENT_POINT = SAMPLED_DATA_ABSENT_POINT;
5172
+ exports.SAMPLED_DATA_UNGROUNDED = SAMPLED_DATA_UNGROUNDED;
5173
+ exports.SEGMENT_IDENTIFIER_SHAPE = SEGMENT_IDENTIFIER_SHAPE;
2676
5174
  exports.SERVICE_REQUEST_PRIORITY_MAP = SERVICE_REQUEST_PRIORITY_MAP;
2677
5175
  exports.SITE_VALUE_MAP = SITE_VALUE_MAP;
5176
+ exports.SPM_SHIPMENT_IDENTIFIER_TYPE = SPM_SHIPMENT_IDENTIFIER_TYPE;
2678
5177
  exports.SUBSTITUTION_VALUE_MAP = SUBSTITUTION_VALUE_MAP;
5178
+ exports.SegmentReachLedger = SegmentReachLedger;
2679
5179
  exports.TRANSFORM_ISSUE_SYSTEM = TRANSFORM_ISSUE_SYSTEM;
5180
+ exports.V2_0127_SYSTEM = V2_0127_SYSTEM;
5181
+ exports.V2_0128_SYSTEM = V2_0128_SYSTEM;
2680
5182
  exports.V2_0161_SYSTEM = V2_0161_SYSTEM;
2681
5183
  exports.V2_0162_SYSTEM = V2_0162_SYSTEM;
2682
5184
  exports.V2_0203_SYSTEM = V2_0203_SYSTEM;
2683
5185
  exports.V2_0277_SYSTEM = V2_0277_SYSTEM;
2684
5186
  exports.V2_0550_SYSTEM = V2_0550_SYSTEM;
5187
+ exports.V3_OBSERVATION_INTERPRETATION_SYSTEM = V3_OBSERVATION_INTERPRETATION_SYSTEM;
2685
5188
  exports.V3_ROUTE_OF_ADMINISTRATION_SYSTEM = V3_ROUTE_OF_ADMINISTRATION_SYSTEM;
2686
5189
  exports.VERSION = VERSION;
5190
+ exports.addMinutes = addMinutes;
5191
+ exports.buildAnnotations = buildAnnotations;
5192
+ exports.buildCondition = buildCondition;
5193
+ exports.buildCoverage = buildCoverage;
5194
+ exports.buildProcedure = buildProcedure;
5195
+ exports.buildSpecimen = buildSpecimen;
5196
+ exports.carriesLegacyOnsetDate = carriesLegacyOnsetDate;
2687
5197
  exports.codeableConceptFromTarget = codeableConceptFromTarget;
5198
+ exports.collectAllergies = collectAllergies;
5199
+ exports.collectCoverages = collectCoverages;
5200
+ exports.collectDiagnoses = collectDiagnoses;
5201
+ exports.collectProcedures = collectProcedures;
2688
5202
  exports.createNamingSystem = createNamingSystem;
5203
+ exports.deferredCoverageIssues = deferredCoverageIssues;
5204
+ exports.deferredDiagnosisIssues = deferredDiagnosisIssues;
5205
+ exports.deferredProcedureIssues = deferredProcedureIssues;
5206
+ exports.emitEncounterDiagnosis = emitEncounterDiagnosis;
5207
+ exports.enumerateSegmentOccurrences = enumerateSegmentOccurrences;
2689
5208
  exports.fhirIssueTypeFor = fhirIssueTypeFor;
5209
+ exports.insuranceCompanyName = insuranceCompanyName;
5210
+ exports.invertCodeMap = invertCodeMap;
5211
+ exports.isIgMappedSegmentName = isIgMappedSegmentName;
5212
+ exports.isWellFormedSegmentName = isWellFormedSegmentName;
2690
5213
  exports.issue = issue;
5214
+ exports.segmentOccurrenceLocation = segmentOccurrenceLocation;
2691
5215
  exports.toFhir = toFhir;
2692
5216
  exports.toFhirAddress = toFhirAddress;
2693
5217
  exports.toFhirCodeableConcept = toFhirCodeableConcept;
2694
5218
  exports.toFhirCodeableConceptVia = toFhirCodeableConceptVia;
2695
5219
  exports.toFhirDateTime = toFhirDateTime;
5220
+ exports.toFhirEntityIdentifier = toFhirEntityIdentifier;
2696
5221
  exports.toFhirHumanName = toFhirHumanName;
2697
5222
  exports.toFhirIdentifier = toFhirIdentifier;
2698
5223
  exports.toFhirQuantity = toFhirQuantity;
2699
5224
  exports.toOperationOutcome = toOperationOutcome;
5225
+ exports.toV2Observation = toV2Observation;
5226
+ exports.toV2Patient = toV2Patient;
2700
5227
  exports.translateBound = translateBound;
5228
+ exports.withCodeableText = withCodeableText;
5229
+ exports.withEncounterDiagnosis = withEncounterDiagnosis;
2701
5230
  //# sourceMappingURL=index.cjs.map
2702
5231
  //# sourceMappingURL=index.cjs.map