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