ezmedicationinput 0.1.50 → 0.1.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -62,6 +62,7 @@ __export(index_exports, {
62
62
  AdviceModality: () => AdviceModality,
63
63
  AdvicePolarity: () => AdvicePolarity,
64
64
  AdviceRelation: () => AdviceRelation,
65
+ BODY_SITE_ADMINISTRATION_TARGET_COUNT_EXTENSION_URL: () => BODY_SITE_ADMINISTRATION_TARGET_COUNT_EXTENSION_URL,
65
66
  BODY_SITE_SPATIAL_RELATION_EXTENSION_URL: () => BODY_SITE_SPATIAL_RELATION_EXTENSION_URL,
66
67
  DEFAULT_BODY_SITE_SNOMED: () => DEFAULT_BODY_SITE_SNOMED,
67
68
  DEFAULT_BODY_SITE_SNOMED_SOURCE: () => DEFAULT_BODY_SITE_SNOMED_SOURCE,
@@ -86,6 +87,7 @@ __export(index_exports, {
86
87
  SNOMED_CT_TOPOGRAPHICAL_MODIFIER_CODE: () => SNOMED_CT_TOPOGRAPHICAL_MODIFIER_CODE,
87
88
  SNOMED_CT_TOPOGRAPHICAL_MODIFIER_DISPLAY: () => SNOMED_CT_TOPOGRAPHICAL_MODIFIER_DISPLAY,
88
89
  SNOMED_SYSTEM: () => SNOMED_SYSTEM,
90
+ buildBodySiteAdministrationTargetCountExtension: () => buildBodySiteAdministrationTargetCountExtension,
89
91
  buildBodySiteSpatialRelationExtension: () => buildBodySiteSpatialRelationExtension,
90
92
  buildBodySiteSpatialRelationExtensions: () => buildBodySiteSpatialRelationExtensions,
91
93
  buildBodySiteTopographicalModifierCoding: () => buildBodySiteTopographicalModifierCoding,
@@ -99,6 +101,7 @@ __export(index_exports, {
99
101
  formatSig: () => formatSig,
100
102
  formatSigBatch: () => formatSigBatch,
101
103
  fromFhirDosage: () => fromFhirDosage,
104
+ getBodySiteAdministrationTargetCount: () => getBodySiteAdministrationTargetCount,
102
105
  getBodySiteCode: () => getBodySiteCode,
103
106
  getBodySiteCodeAsync: () => getBodySiteCodeAsync,
104
107
  getBodySiteText: () => getBodySiteText,
@@ -117,6 +120,7 @@ __export(index_exports, {
117
120
  lookupBodySite: () => lookupBodySite,
118
121
  lookupBodySiteAsync: () => lookupBodySiteAsync,
119
122
  nextDueDoses: () => nextDueDoses,
123
+ parseBodySiteAdministrationTargetCountExtension: () => parseBodySiteAdministrationTargetCountExtension,
120
124
  parseBodySiteSpatialRelationExtension: () => parseBodySiteSpatialRelationExtension,
121
125
  parseSig: () => parseSig,
122
126
  parseSigAsync: () => parseSigAsync,
@@ -134,60 +138,6 @@ __export(index_exports, {
134
138
  });
135
139
  module.exports = __toCommonJS(index_exports);
136
140
 
137
- // src/prn.ts
138
- function getCanonicalPrnReasonText(reason) {
139
- var _a2, _b;
140
- return (_b = reason == null ? void 0 : reason.text) != null ? _b : (_a2 = reason == null ? void 0 : reason.coding) == null ? void 0 : _a2.display;
141
- }
142
- function joinCanonicalPrnReasonTexts(reasons, conjunction = "or") {
143
- var _a2;
144
- if (!(reasons == null ? void 0 : reasons.length)) {
145
- return void 0;
146
- }
147
- const texts = [];
148
- for (const reason of reasons) {
149
- const text = (_a2 = getCanonicalPrnReasonText(reason)) == null ? void 0 : _a2.trim();
150
- if (!text) {
151
- continue;
152
- }
153
- texts.push(text);
154
- }
155
- switch (texts.length) {
156
- case 0:
157
- return void 0;
158
- case 1:
159
- return texts[0];
160
- case 2:
161
- return `${texts[0]} ${conjunction} ${texts[1]}`;
162
- default: {
163
- let combined = "";
164
- for (let index = 0; index < texts.length; index += 1) {
165
- if (index === 0) {
166
- combined = texts[index];
167
- continue;
168
- }
169
- if (index === texts.length - 1) {
170
- combined += ` ${conjunction} ${texts[index]}`;
171
- continue;
172
- }
173
- combined += `, ${texts[index]}`;
174
- }
175
- return combined;
176
- }
177
- }
178
- }
179
- function getPreferredCanonicalPrnReasonText(reason, reasons, conjunction = "or") {
180
- var _a2;
181
- const direct = (_a2 = getCanonicalPrnReasonText(reason)) == null ? void 0 : _a2.trim();
182
- if (!(reasons == null ? void 0 : reasons.length)) {
183
- return direct;
184
- }
185
- if (!direct) {
186
- return joinCanonicalPrnReasonTexts(reasons, conjunction);
187
- }
188
- return /[,/;]/.test(direct) || /\b(?:or|and|and\/or)\b/i.test(direct) || /\s(?:หรือ|และ)\s/.test(direct) ? joinCanonicalPrnReasonTexts(reasons, conjunction) : direct;
189
- }
190
-
191
141
  // src/types.ts
192
142
  var EventTiming = /* @__PURE__ */ ((EventTiming4) => {
193
143
  EventTiming4["Before Sleep"] = "HS";
@@ -455,6 +405,328 @@ var AdviceArgumentRole = /* @__PURE__ */ ((AdviceArgumentRole2) => {
455
405
  return AdviceArgumentRole2;
456
406
  })(AdviceArgumentRole || {});
457
407
 
408
+ // src/timing-summary.ts
409
+ var MEAL_TIMING_DETAILS = {
410
+ ["ACM" /* Before Breakfast */]: { relation: "before", meal: "breakfast" },
411
+ ["ACD" /* Before Lunch */]: { relation: "before", meal: "lunch" },
412
+ ["ACV" /* Before Dinner */]: { relation: "before", meal: "dinner" },
413
+ ["PCM" /* After Breakfast */]: { relation: "after", meal: "breakfast" },
414
+ ["PCD" /* After Lunch */]: { relation: "after", meal: "lunch" },
415
+ ["PCV" /* After Dinner */]: { relation: "after", meal: "dinner" },
416
+ ["CM" /* Breakfast */]: { relation: "with", meal: "breakfast" },
417
+ ["CD" /* Lunch */]: { relation: "with", meal: "lunch" },
418
+ ["CV" /* Dinner */]: { relation: "with", meal: "dinner" }
419
+ };
420
+ var MEAL_ORDER = {
421
+ breakfast: 0,
422
+ lunch: 1,
423
+ dinner: 2
424
+ };
425
+ var INFERABLE_DAILY_EVENT_TIMINGS = /* @__PURE__ */ new Set([
426
+ "HS" /* Before Sleep */,
427
+ "ACM" /* Before Breakfast */,
428
+ "ACD" /* Before Lunch */,
429
+ "ACV" /* Before Dinner */,
430
+ "PCM" /* After Breakfast */,
431
+ "PCD" /* After Lunch */,
432
+ "PCV" /* After Dinner */,
433
+ "CM" /* Breakfast */,
434
+ "CD" /* Lunch */,
435
+ "CV" /* Dinner */,
436
+ "MORN" /* Morning */,
437
+ "MORN.early" /* Early Morning */,
438
+ "MORN.late" /* Late Morning */,
439
+ "NOON" /* Noon */,
440
+ "AFT" /* Afternoon */,
441
+ "AFT.early" /* Early Afternoon */,
442
+ "AFT.late" /* Late Afternoon */,
443
+ "EVE" /* Evening */,
444
+ "EVE.early" /* Early Evening */,
445
+ "EVE.late" /* Late Evening */,
446
+ "NIGHT" /* Night */,
447
+ "WAKE" /* Wake */,
448
+ "PHS" /* After Sleep */
449
+ ]);
450
+ function uniqueValues(values) {
451
+ const seen = /* @__PURE__ */ new Set();
452
+ const result = [];
453
+ for (const value of values) {
454
+ if (!seen.has(value)) {
455
+ seen.add(value);
456
+ result.push(value);
457
+ }
458
+ }
459
+ return result;
460
+ }
461
+ function getMealTimingGroup(when, options) {
462
+ if (!(options == null ? void 0 : options.groupMealTimingsByRelation)) {
463
+ return void 0;
464
+ }
465
+ const uniqueWhen2 = uniqueValues(when);
466
+ if (uniqueWhen2.length < 2) {
467
+ return void 0;
468
+ }
469
+ let relation;
470
+ const meals = [];
471
+ const groupedCodes = [];
472
+ let sawFirstMeal = false;
473
+ for (let i = 0; i < uniqueWhen2.length; i += 1) {
474
+ const code = uniqueWhen2[i];
475
+ const detail = MEAL_TIMING_DETAILS[code];
476
+ if (!detail) {
477
+ if (sawFirstMeal) {
478
+ break;
479
+ }
480
+ continue;
481
+ }
482
+ if (!sawFirstMeal) {
483
+ sawFirstMeal = true;
484
+ }
485
+ if (!relation) {
486
+ relation = detail.relation;
487
+ } else if (relation !== detail.relation && detail.relation !== "with") {
488
+ break;
489
+ }
490
+ meals.push(detail.meal);
491
+ groupedCodes.push(code);
492
+ }
493
+ if (groupedCodes.length < 2) {
494
+ return void 0;
495
+ }
496
+ for (let i = 1; i < meals.length; i += 1) {
497
+ const current = meals[i];
498
+ let j = i - 1;
499
+ while (j >= 0 && MEAL_ORDER[meals[j]] > MEAL_ORDER[current]) {
500
+ meals[j + 1] = meals[j];
501
+ j -= 1;
502
+ }
503
+ meals[j + 1] = current;
504
+ }
505
+ if (!relation) {
506
+ return void 0;
507
+ }
508
+ return {
509
+ relation,
510
+ meals,
511
+ codes: groupedCodes
512
+ };
513
+ }
514
+ function inferDailyOccurrenceCount(input, options) {
515
+ var _a2, _b, _c, _d;
516
+ if (!(options == null ? void 0 : options.includeTimesPerDaySummary)) {
517
+ return void 0;
518
+ }
519
+ if (input.frequency !== void 0 || input.frequencyMax !== void 0 || input.timingCode) {
520
+ return void 0;
521
+ }
522
+ if (input.period !== void 0 || input.periodMax !== void 0 || input.periodUnit !== void 0) {
523
+ return void 0;
524
+ }
525
+ if (((_b = (_a2 = input.dayOfWeek) == null ? void 0 : _a2.length) != null ? _b : 0) > 0) {
526
+ return void 0;
527
+ }
528
+ const uniqueWhen2 = uniqueValues((_c = input.when) != null ? _c : []);
529
+ for (let i = 0; i < uniqueWhen2.length; i += 1) {
530
+ if (!INFERABLE_DAILY_EVENT_TIMINGS.has(uniqueWhen2[i])) {
531
+ return void 0;
532
+ }
533
+ }
534
+ const uniqueTimes = uniqueValues((_d = input.timeOfDay) != null ? _d : []);
535
+ const occurrences = uniqueWhen2.length + uniqueTimes.length;
536
+ if (occurrences === 0) {
537
+ return void 0;
538
+ }
539
+ return occurrences;
540
+ }
541
+
542
+ // src/localized-timing.ts
543
+ function uniqueWhenEvents(schedule) {
544
+ var _a2;
545
+ const when = (_a2 = schedule == null ? void 0 : schedule.when) != null ? _a2 : [];
546
+ if (!when.length) {
547
+ return [];
548
+ }
549
+ const unique = [];
550
+ const seen = /* @__PURE__ */ new Set();
551
+ for (const code of when) {
552
+ if (!seen.has(code)) {
553
+ seen.add(code);
554
+ unique.push(code);
555
+ }
556
+ }
557
+ return unique;
558
+ }
559
+ function filterLocalizedWhenEvents(schedule) {
560
+ const unique = uniqueWhenEvents(schedule);
561
+ const specificAfter = /* @__PURE__ */ new Set();
562
+ const specificBefore = /* @__PURE__ */ new Set();
563
+ const specificWith = /* @__PURE__ */ new Set();
564
+ for (const code of unique) {
565
+ if (code === "PCM" /* After Breakfast */ || code === "PCD" /* After Lunch */ || code === "PCV" /* After Dinner */) {
566
+ specificAfter.add(code);
567
+ }
568
+ if (code === "ACM" /* Before Breakfast */ || code === "ACD" /* Before Lunch */ || code === "ACV" /* Before Dinner */) {
569
+ specificBefore.add(code);
570
+ }
571
+ if (code === "CM" /* Breakfast */ || code === "CD" /* Lunch */ || code === "CV" /* Dinner */) {
572
+ specificWith.add(code);
573
+ }
574
+ }
575
+ const hasAllAfter = specificAfter.has("PCM" /* After Breakfast */) && specificAfter.has("PCD" /* After Lunch */) && specificAfter.has("PCV" /* After Dinner */);
576
+ const hasAllBefore = specificBefore.has("ACM" /* Before Breakfast */) && specificBefore.has("ACD" /* Before Lunch */) && specificBefore.has("ACV" /* Before Dinner */);
577
+ const hasAllWith = specificWith.has("CM" /* Breakfast */) && specificWith.has("CD" /* Lunch */) && specificWith.has("CV" /* Dinner */);
578
+ const filtered = [];
579
+ for (const code of unique) {
580
+ if (code === "PC" /* After Meal */ && hasAllAfter) {
581
+ continue;
582
+ }
583
+ if (code === "AC" /* Before Meal */ && hasAllBefore) {
584
+ continue;
585
+ }
586
+ if (code === "C" /* Meal */ && hasAllWith) {
587
+ continue;
588
+ }
589
+ filtered.push(code);
590
+ }
591
+ return filtered;
592
+ }
593
+ function collectLocalizedWhenPhrases(schedule, grammar, options) {
594
+ const filtered = filterLocalizedWhenEvents(schedule);
595
+ if (!filtered.length) {
596
+ return [];
597
+ }
598
+ const mealGroup = getMealTimingGroup(filtered, options);
599
+ if (!mealGroup) {
600
+ const phrases2 = [];
601
+ for (const code of filtered) {
602
+ const text = grammar.whenText[code];
603
+ if (text) {
604
+ phrases2.push(text);
605
+ }
606
+ }
607
+ return phrases2;
608
+ }
609
+ const groupedCodes = new Set(mealGroup.codes);
610
+ const phrases = [];
611
+ let insertedGroup = false;
612
+ for (const code of filtered) {
613
+ if (groupedCodes.has(code)) {
614
+ if (!insertedGroup) {
615
+ phrases.push(grammar.summarizeMealTimingGroup(mealGroup));
616
+ insertedGroup = true;
617
+ }
618
+ continue;
619
+ }
620
+ const text = grammar.whenText[code];
621
+ if (text) {
622
+ phrases.push(text);
623
+ }
624
+ }
625
+ return phrases;
626
+ }
627
+ function inferExplicitDailyFrequency(schedule) {
628
+ var _a2;
629
+ if (!schedule) {
630
+ return void 0;
631
+ }
632
+ if (schedule.frequency !== void 0 && schedule.frequencyMax === void 0 && schedule.periodUnit === "d" /* Day */ && (schedule.period === void 0 || schedule.period === 1) && schedule.periodMax === void 0) {
633
+ return schedule.frequency;
634
+ }
635
+ switch ((_a2 = schedule.timingCode) == null ? void 0 : _a2.toUpperCase()) {
636
+ case "QD":
637
+ return 1;
638
+ case "BID":
639
+ return 2;
640
+ case "TID":
641
+ return 3;
642
+ case "QID":
643
+ return 4;
644
+ default:
645
+ return void 0;
646
+ }
647
+ }
648
+ function isBedtimeOnlyWhen(schedule) {
649
+ const filtered = filterLocalizedWhenEvents(schedule);
650
+ return filtered.length === 1 && filtered[0] === "HS" /* Before Sleep */;
651
+ }
652
+ function combineLocalizedFrequencyAndEvents(schedule, frequency, events, grammar, options) {
653
+ var _a2, _b, _c;
654
+ if (!frequency) {
655
+ if (!events.length) {
656
+ return {};
657
+ }
658
+ return { event: grammar.joinList(events) };
659
+ }
660
+ if (!events.length) {
661
+ return { frequency };
662
+ }
663
+ if (events.length === 1 && isBedtimeOnlyWhen(schedule)) {
664
+ const dailyCount = (_a2 = inferExplicitDailyFrequency(schedule)) != null ? _a2 : inferDailyOccurrenceCount(schedule != null ? schedule : {}, options);
665
+ const style = (_c = (_b = grammar.bedtimeJoinStyle) == null ? void 0 : _b.call(grammar, dailyCount)) != null ? _c : "separate";
666
+ if (style === "adjacent") {
667
+ return { frequency: `${frequency} ${events[0]}` };
668
+ }
669
+ if (style === "conjunction") {
670
+ return { frequency: grammar.joinList([frequency, events[0]]) };
671
+ }
672
+ }
673
+ return { frequency, event: grammar.joinList(events) };
674
+ }
675
+
676
+ // src/prn.ts
677
+ function getCanonicalPrnReasonText(reason) {
678
+ var _a2, _b;
679
+ return (_b = reason == null ? void 0 : reason.text) != null ? _b : (_a2 = reason == null ? void 0 : reason.coding) == null ? void 0 : _a2.display;
680
+ }
681
+ function joinCanonicalPrnReasonTexts(reasons, conjunction = "or") {
682
+ var _a2;
683
+ if (!(reasons == null ? void 0 : reasons.length)) {
684
+ return void 0;
685
+ }
686
+ const texts = [];
687
+ for (const reason of reasons) {
688
+ const text = (_a2 = getCanonicalPrnReasonText(reason)) == null ? void 0 : _a2.trim();
689
+ if (!text) {
690
+ continue;
691
+ }
692
+ texts.push(text);
693
+ }
694
+ switch (texts.length) {
695
+ case 0:
696
+ return void 0;
697
+ case 1:
698
+ return texts[0];
699
+ case 2:
700
+ return `${texts[0]} ${conjunction} ${texts[1]}`;
701
+ default: {
702
+ let combined = "";
703
+ for (let index = 0; index < texts.length; index += 1) {
704
+ if (index === 0) {
705
+ combined = texts[index];
706
+ continue;
707
+ }
708
+ if (index === texts.length - 1) {
709
+ combined += ` ${conjunction} ${texts[index]}`;
710
+ continue;
711
+ }
712
+ combined += `, ${texts[index]}`;
713
+ }
714
+ return combined;
715
+ }
716
+ }
717
+ }
718
+ function getPreferredCanonicalPrnReasonText(reason, reasons, conjunction = "or") {
719
+ var _a2;
720
+ const direct = (_a2 = getCanonicalPrnReasonText(reason)) == null ? void 0 : _a2.trim();
721
+ if (!(reasons == null ? void 0 : reasons.length)) {
722
+ return direct;
723
+ }
724
+ if (!direct) {
725
+ return joinCanonicalPrnReasonTexts(reasons, conjunction);
726
+ }
727
+ return /[,/;]/.test(direct) || /\b(?:or|and|and\/or)\b/i.test(direct) || /\s(?:หรือ|และ)\s/.test(direct) ? joinCanonicalPrnReasonTexts(reasons, conjunction) : direct;
728
+ }
729
+
458
730
  // src/snomed.ts
459
731
  var SNOMED_SYSTEM = "http://snomed.info/sct";
460
732
  var SNOMED_CT_FINDING_SITE_ATTRIBUTE_CODE = "363698007";
@@ -593,8 +865,8 @@ function mergeTranslationPrimitiveElement(base, translations) {
593
865
  if (extension.url !== FHIR_TRANSLATION_EXTENSION_URL) {
594
866
  continue;
595
867
  }
596
- const { locale } = getTranslationParts(extension);
597
- if (locale) {
868
+ const { locale, content } = getTranslationParts(extension);
869
+ if (locale && content) {
598
870
  existingTranslationLocales.add(locale);
599
871
  }
600
872
  }
@@ -1263,10 +1535,11 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1263
1535
  }
1264
1536
  },
1265
1537
  {
1266
- names: ["both eyes", "bilateral eyes", "\u0E15\u0E32\u0E17\u0E31\u0E49\u0E07\u0E2A\u0E2D\u0E07\u0E02\u0E49\u0E32\u0E07", "\u0E15\u0E32\u0E2A\u0E2D\u0E07\u0E02\u0E49\u0E32\u0E07"],
1538
+ names: ["both eyes", "bilateral eyes", "each eye", "\u0E15\u0E32\u0E17\u0E31\u0E49\u0E07\u0E2A\u0E2D\u0E07\u0E02\u0E49\u0E32\u0E07", "\u0E15\u0E32\u0E2A\u0E2D\u0E07\u0E02\u0E49\u0E32\u0E07"],
1267
1539
  definition: {
1268
1540
  coding: { code: "40638003", display: "Structure of both eyes" },
1269
1541
  text: "both eyes",
1542
+ administrationTargetCount: 2,
1270
1543
  i18n: { th: "\u0E15\u0E32\u0E17\u0E31\u0E49\u0E07\u0E2A\u0E2D\u0E07\u0E02\u0E49\u0E32\u0E07" },
1271
1544
  routeHint: RouteCode["Ophthalmic route"]
1272
1545
  }
@@ -1280,10 +1553,19 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1280
1553
  }
1281
1554
  },
1282
1555
  {
1283
- names: ["ears", "both ears", "bilateral ears", "inside ears", "\u0E2B\u0E39\u0E17\u0E31\u0E49\u0E07\u0E2A\u0E2D\u0E07\u0E02\u0E49\u0E32\u0E07", "\u0E2B\u0E39\u0E2A\u0E2D\u0E07\u0E02\u0E49\u0E32\u0E07"],
1556
+ names: [
1557
+ "ears",
1558
+ "both ears",
1559
+ "bilateral ears",
1560
+ "each ear",
1561
+ "inside ears",
1562
+ "\u0E2B\u0E39\u0E17\u0E31\u0E49\u0E07\u0E2A\u0E2D\u0E07\u0E02\u0E49\u0E32\u0E07",
1563
+ "\u0E2B\u0E39\u0E2A\u0E2D\u0E07\u0E02\u0E49\u0E32\u0E07"
1564
+ ],
1284
1565
  definition: {
1285
1566
  coding: { code: "34338003", display: "Both ears" },
1286
1567
  text: "both ears",
1568
+ administrationTargetCount: 2,
1287
1569
  routeHint: RouteCode["Otic route"]
1288
1570
  }
1289
1571
  },
@@ -1301,10 +1583,18 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1301
1583
  }
1302
1584
  },
1303
1585
  {
1304
- names: ["ear canals", "both ear canals", "inside ear canals", "both external auditory canals"],
1586
+ names: [
1587
+ "ear canals",
1588
+ "both ear canals",
1589
+ "each ear canal",
1590
+ "inside ear canals",
1591
+ "both external auditory canals",
1592
+ "each external auditory canal"
1593
+ ],
1305
1594
  definition: {
1306
1595
  coding: { code: "181178004", display: "Entire external auditory canal" },
1307
1596
  text: "both ear canals",
1597
+ administrationTargetCount: 2,
1308
1598
  routeHint: RouteCode["Otic route"]
1309
1599
  }
1310
1600
  },
@@ -1341,8 +1631,12 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1341
1631
  }
1342
1632
  },
1343
1633
  {
1344
- names: ["nostril", "nostrils"],
1345
- definition: { coding: { code: "1797002", display: "Naris" }, routeHint: RouteCode["Nasal route"] }
1634
+ names: ["nostril"],
1635
+ definition: {
1636
+ coding: { code: "1797002", display: "Naris" },
1637
+ text: "nostril",
1638
+ routeHint: RouteCode["Nasal route"]
1639
+ }
1346
1640
  },
1347
1641
  {
1348
1642
  names: ["left nostril", "left naris"],
@@ -1359,8 +1653,22 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1359
1653
  }
1360
1654
  },
1361
1655
  {
1362
- names: ["nares", "anterior nares"],
1363
- definition: { coding: { code: "244506005", display: "Anterior nares" }, routeHint: RouteCode["Nasal route"] }
1656
+ names: [
1657
+ "nostrils",
1658
+ "both nostrils",
1659
+ "bilateral nostrils",
1660
+ "each nostril",
1661
+ "nares",
1662
+ "anterior nares",
1663
+ "\u0E23\u0E39\u0E08\u0E21\u0E39\u0E01\u0E17\u0E31\u0E49\u0E07\u0E2A\u0E2D\u0E07\u0E02\u0E49\u0E32\u0E07"
1664
+ ],
1665
+ definition: {
1666
+ coding: { code: "244506005", display: "Anterior nares" },
1667
+ text: "both nostrils",
1668
+ administrationTargetCount: 2,
1669
+ i18n: { th: "\u0E23\u0E39\u0E08\u0E21\u0E39\u0E01\u0E17\u0E31\u0E49\u0E07\u0E2A\u0E2D\u0E07\u0E02\u0E49\u0E32\u0E07" },
1670
+ routeHint: RouteCode["Nasal route"]
1671
+ }
1364
1672
  },
1365
1673
  {
1366
1674
  names: ["nose"],
@@ -1886,6 +2194,24 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1886
2194
  names: ["affected area", "affected areas", "affected site", "\u0E1A\u0E23\u0E34\u0E40\u0E27\u0E13\u0E17\u0E35\u0E48\u0E40\u0E1B\u0E47\u0E19"],
1887
2195
  definition: { text: "affected area", routeHint: RouteCode["Topical route"] }
1888
2196
  },
2197
+ {
2198
+ names: ["lesion", "skin lesion"],
2199
+ definition: {
2200
+ coding: { code: "95324001", display: "Skin lesion" },
2201
+ text: "lesion",
2202
+ routeHint: RouteCode["Topical route"],
2203
+ i18n: { th: "\u0E23\u0E2D\u0E22\u0E42\u0E23\u0E04" }
2204
+ }
2205
+ },
2206
+ {
2207
+ names: ["lesions", "skin lesions"],
2208
+ definition: {
2209
+ coding: { code: "95324001", display: "Skin lesion" },
2210
+ text: "lesions",
2211
+ routeHint: RouteCode["Topical route"],
2212
+ i18n: { th: "\u0E23\u0E2D\u0E22\u0E42\u0E23\u0E04" }
2213
+ }
2214
+ },
1889
2215
  {
1890
2216
  names: ["left head", "left side of head"],
1891
2217
  definition: { coding: { code: "64237003", display: "Structure of left half of head" }, routeHint: RouteCode["Topical route"] }
@@ -1926,6 +2252,14 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1926
2252
  names: ["axilla", "axillae", "armpit", "armpits"],
1927
2253
  definition: { coding: { code: "34797008", display: "Axilla structure" }, routeHint: RouteCode["Topical route"] }
1928
2254
  },
2255
+ {
2256
+ names: ["axillary hair", "armpit hair", "armpit hairs", "underarm hair", "underarm hairs"],
2257
+ definition: {
2258
+ coding: { code: "75703003", display: "Structure of hair of axilla" },
2259
+ text: "axillary hair",
2260
+ routeHint: RouteCode["Topical route"]
2261
+ }
2262
+ },
1929
2263
  {
1930
2264
  names: ["groin"],
1931
2265
  definition: { coding: { code: "26893007", display: "Inguinal region structure" }, routeHint: RouteCode["Topical route"] }
@@ -1996,6 +2330,71 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1996
2330
  names: ["face"],
1997
2331
  definition: { coding: { code: "89545001", display: "Face" }, routeHint: RouteCode["Topical route"] }
1998
2332
  },
2333
+ {
2334
+ names: ["eyebrow", "brow"],
2335
+ definition: {
2336
+ coding: { code: "392262008", display: "Eyebrow structure" },
2337
+ text: "eyebrow",
2338
+ routeHint: RouteCode["Topical route"]
2339
+ }
2340
+ },
2341
+ {
2342
+ names: ["eyebrows", "brows"],
2343
+ definition: {
2344
+ coding: { code: "392262008", display: "Eyebrow structure" },
2345
+ text: "eyebrows",
2346
+ routeHint: RouteCode["Topical route"]
2347
+ }
2348
+ },
2349
+ {
2350
+ names: ["left eyebrow", "left brow"],
2351
+ definition: {
2352
+ coding: { code: "722011002", display: "Structure of eyebrow of left eye region" },
2353
+ text: "left eyebrow",
2354
+ routeHint: RouteCode["Topical route"]
2355
+ }
2356
+ },
2357
+ {
2358
+ names: ["right eyebrow", "right brow"],
2359
+ definition: {
2360
+ coding: { code: "722012009", display: "Structure of eyebrow of right eye region" },
2361
+ text: "right eyebrow",
2362
+ routeHint: RouteCode["Topical route"]
2363
+ }
2364
+ },
2365
+ {
2366
+ names: [
2367
+ "both eyebrows",
2368
+ "bilateral eyebrows",
2369
+ "each eyebrow",
2370
+ "left and right eyebrow",
2371
+ "left and right eyebrows",
2372
+ "right and left eyebrow",
2373
+ "right and left eyebrows",
2374
+ "left right eyebrow",
2375
+ "left right eyebrows"
2376
+ ],
2377
+ definition: {
2378
+ coding: {
2379
+ code: buildSnomedBodySiteLateralityPostcoordinationCode(
2380
+ "392262008",
2381
+ SNOMED_CT_BILATERAL_QUALIFIER_CODE
2382
+ ),
2383
+ display: "both eyebrows"
2384
+ },
2385
+ text: "both eyebrows",
2386
+ administrationTargetCount: 2,
2387
+ routeHint: RouteCode["Topical route"]
2388
+ }
2389
+ },
2390
+ {
2391
+ names: ["eyebrow hair", "eyebrow hairs", "brow hair", "brow hairs"],
2392
+ definition: {
2393
+ coding: { code: "392261001", display: "Hair structure of eyebrow" },
2394
+ text: "eyebrow hair",
2395
+ routeHint: RouteCode["Topical route"]
2396
+ }
2397
+ },
1999
2398
  {
2000
2399
  names: ["eyelid", "eyelids"],
2001
2400
  definition: { coding: { code: "80243003", display: "Eyelid" }, routeHint: RouteCode["Ophthalmic route"] }
@@ -2116,14 +2515,70 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
2116
2515
  names: ["perineum"],
2117
2516
  definition: { coding: { code: "243990009", display: "Entire perineum" }, routeHint: RouteCode["Topical route"] }
2118
2517
  },
2518
+ {
2519
+ names: ["mustache", "moustache", "mustache hair", "moustache hair"],
2520
+ definition: {
2521
+ coding: { code: "256925006", display: "Structure of hair of mustache" },
2522
+ text: "mustache",
2523
+ routeHint: RouteCode["Topical route"]
2524
+ }
2525
+ },
2526
+ {
2527
+ names: ["beard", "beard hair", "facial hair"],
2528
+ definition: {
2529
+ coding: { code: "367576007", display: "Structure of beard hair" },
2530
+ text: "beard",
2531
+ routeHint: RouteCode["Topical route"]
2532
+ }
2533
+ },
2534
+ {
2535
+ names: ["pubic hair"],
2536
+ definition: {
2537
+ coding: { code: "75776007", display: "Structure of hair of pubis" },
2538
+ text: "pubic hair",
2539
+ routeHint: RouteCode["Topical route"]
2540
+ }
2541
+ },
2119
2542
  {
2120
2543
  names: ["skin"],
2121
2544
  definition: { coding: { code: "181469002", display: "Entire skin" }, routeHint: RouteCode["Topical route"] }
2122
2545
  },
2123
2546
  {
2124
- names: ["hair"],
2547
+ names: ["hair"],
2548
+ definition: {
2549
+ coding: { code: "386045008", display: "Hair structure (body structure)" },
2550
+ routeHint: RouteCode["Topical route"]
2551
+ }
2552
+ },
2553
+ {
2554
+ names: ["joint"],
2555
+ definition: {
2556
+ coding: { code: "39352004", display: "Joint structure" },
2557
+ text: "joint",
2558
+ routeHint: RouteCode["Topical route"]
2559
+ }
2560
+ },
2561
+ {
2562
+ names: ["joints"],
2563
+ definition: {
2564
+ coding: { code: "81087007", display: "Joints" },
2565
+ text: "joints",
2566
+ routeHint: RouteCode["Topical route"]
2567
+ }
2568
+ },
2569
+ {
2570
+ names: ["finger joint", "finger joints"],
2571
+ definition: {
2572
+ coding: { code: "125682004", display: "Finger joint structure" },
2573
+ text: "finger joints",
2574
+ routeHint: RouteCode["Topical route"]
2575
+ }
2576
+ },
2577
+ {
2578
+ names: ["knuckle", "knuckles"],
2125
2579
  definition: {
2126
- coding: { code: "386045008", display: "Hair structure (body structure)" },
2580
+ coding: { code: "70420003", display: "Metacarpophalangeal joint structure" },
2581
+ text: "knuckles",
2127
2582
  routeHint: RouteCode["Topical route"]
2128
2583
  }
2129
2584
  }
@@ -4037,6 +4492,7 @@ var lexical_classes_default = {
4037
4492
  "Use shampoo": "\u0E2A\u0E23\u0E30"
4038
4493
  },
4039
4494
  compoundDoseUnits: [
4495
+ { head: "cm", tails: [], requiresSiteContext: true, unit: "cm line" },
4040
4496
  { head: "cm", tails: ["ribbon", "ribbons"], unit: "cm ribbon" },
4041
4497
  { head: "cm", tails: ["strip", "strips"], unit: "cm strip" },
4042
4498
  { head: "cm", tails: ["line", "lines"], unit: "cm line" },
@@ -4045,6 +4501,8 @@ var lexical_classes_default = {
4045
4501
  { head: "fingertip", tails: ["unit", "units"], unit: "FTU" },
4046
4502
  { head: "eye", tails: ["drop", "drops"], unit: "drop" },
4047
4503
  { head: "pea-sized", tails: ["amount"], unit: "pea-sized amount" },
4504
+ { head: "pea-size", tails: ["amount"], unit: "pea-sized amount" },
4505
+ { head: "peasize", tails: [], tailSequences: [[]], unit: "pea-sized amount" },
4048
4506
  { head: "\u0E40\u0E21\u0E47\u0E14\u0E16\u0E31\u0E48\u0E27", tails: [], unit: "pea-sized amount" },
4049
4507
  { head: "\u0E40\u0E21\u0E47\u0E14\u0E16\u0E31\u0E48\u0E27\u0E40\u0E02\u0E35\u0E22\u0E27", tails: [], unit: "pea-sized amount" },
4050
4508
  { head: "\u0E40\u0E21\u0E25\u0E47\u0E14\u0E16\u0E31\u0E48\u0E27", tails: [], unit: "pea-sized amount" },
@@ -4054,7 +4512,7 @@ var lexical_classes_default = {
4054
4512
  {
4055
4513
  head: "pea",
4056
4514
  tails: [],
4057
- tailSequences: [["sized", "amount"], ["size", "amount"]],
4515
+ tailSequences: [["sized", "amount"], ["size", "amount"], ["amount"], ["sized"], ["size"], []],
4058
4516
  unit: "pea-sized amount"
4059
4517
  },
4060
4518
  { head: "hand", tails: ["print", "prints"], unit: "handprint" },
@@ -4465,6 +4923,7 @@ var BODY_SITE_FEATURE_SCORE_BONUS = bodySiteFeatureScoreBonus(lexical_classes_de
4465
4923
  var CONNECTORS = setOf(lexical_classes_default.connectors);
4466
4924
  var ROUTE_SITE_PREPOSITIONS = setOf(lexical_classes_default.routeSitePrepositions);
4467
4925
  var SITE_DISPLAY_FILLERS = SITE_FILLERS;
4926
+ var SITE_MULTIPLICITY_WORDS = setOf(["both", "each", "bilateral"]);
4468
4927
  var NON_SITE_ANCHORED_PHRASES = setOf(lexical_classes_default.nonSiteAnchoredPhrases);
4469
4928
  var EXTERNAL_SITE_LOCATIVE_PREFIXES = setOf(lexical_classes_default.externalSiteLocativePrefixes);
4470
4929
  var ROUTE_BLOCKED_BY_FOLLOWING_PARTITIVE_HEADS = setOf(
@@ -5010,140 +5469,6 @@ function resolveBodySitePhrase(text, customSiteMap, context) {
5010
5469
  };
5011
5470
  }
5012
5471
 
5013
- // src/timing-summary.ts
5014
- var MEAL_TIMING_DETAILS = {
5015
- ["ACM" /* Before Breakfast */]: { relation: "before", meal: "breakfast" },
5016
- ["ACD" /* Before Lunch */]: { relation: "before", meal: "lunch" },
5017
- ["ACV" /* Before Dinner */]: { relation: "before", meal: "dinner" },
5018
- ["PCM" /* After Breakfast */]: { relation: "after", meal: "breakfast" },
5019
- ["PCD" /* After Lunch */]: { relation: "after", meal: "lunch" },
5020
- ["PCV" /* After Dinner */]: { relation: "after", meal: "dinner" },
5021
- ["CM" /* Breakfast */]: { relation: "with", meal: "breakfast" },
5022
- ["CD" /* Lunch */]: { relation: "with", meal: "lunch" },
5023
- ["CV" /* Dinner */]: { relation: "with", meal: "dinner" }
5024
- };
5025
- var MEAL_ORDER = {
5026
- breakfast: 0,
5027
- lunch: 1,
5028
- dinner: 2
5029
- };
5030
- var INFERABLE_DAILY_EVENT_TIMINGS = /* @__PURE__ */ new Set([
5031
- "HS" /* Before Sleep */,
5032
- "ACM" /* Before Breakfast */,
5033
- "ACD" /* Before Lunch */,
5034
- "ACV" /* Before Dinner */,
5035
- "PCM" /* After Breakfast */,
5036
- "PCD" /* After Lunch */,
5037
- "PCV" /* After Dinner */,
5038
- "CM" /* Breakfast */,
5039
- "CD" /* Lunch */,
5040
- "CV" /* Dinner */,
5041
- "MORN" /* Morning */,
5042
- "MORN.early" /* Early Morning */,
5043
- "MORN.late" /* Late Morning */,
5044
- "NOON" /* Noon */,
5045
- "AFT" /* Afternoon */,
5046
- "AFT.early" /* Early Afternoon */,
5047
- "AFT.late" /* Late Afternoon */,
5048
- "EVE" /* Evening */,
5049
- "EVE.early" /* Early Evening */,
5050
- "EVE.late" /* Late Evening */,
5051
- "NIGHT" /* Night */,
5052
- "WAKE" /* Wake */,
5053
- "PHS" /* After Sleep */
5054
- ]);
5055
- function uniqueValues(values) {
5056
- const seen = /* @__PURE__ */ new Set();
5057
- const result = [];
5058
- for (const value of values) {
5059
- if (!seen.has(value)) {
5060
- seen.add(value);
5061
- result.push(value);
5062
- }
5063
- }
5064
- return result;
5065
- }
5066
- function getMealTimingGroup(when, options) {
5067
- if (!(options == null ? void 0 : options.groupMealTimingsByRelation)) {
5068
- return void 0;
5069
- }
5070
- const uniqueWhen2 = uniqueValues(when);
5071
- if (uniqueWhen2.length < 2) {
5072
- return void 0;
5073
- }
5074
- let relation;
5075
- const meals = [];
5076
- const groupedCodes = [];
5077
- let sawFirstMeal = false;
5078
- for (let i = 0; i < uniqueWhen2.length; i += 1) {
5079
- const code = uniqueWhen2[i];
5080
- const detail = MEAL_TIMING_DETAILS[code];
5081
- if (!detail) {
5082
- if (sawFirstMeal) {
5083
- break;
5084
- }
5085
- continue;
5086
- }
5087
- if (!sawFirstMeal) {
5088
- sawFirstMeal = true;
5089
- }
5090
- if (!relation) {
5091
- relation = detail.relation;
5092
- } else if (relation !== detail.relation && detail.relation !== "with") {
5093
- break;
5094
- }
5095
- meals.push(detail.meal);
5096
- groupedCodes.push(code);
5097
- }
5098
- if (groupedCodes.length < 2) {
5099
- return void 0;
5100
- }
5101
- for (let i = 1; i < meals.length; i += 1) {
5102
- const current = meals[i];
5103
- let j = i - 1;
5104
- while (j >= 0 && MEAL_ORDER[meals[j]] > MEAL_ORDER[current]) {
5105
- meals[j + 1] = meals[j];
5106
- j -= 1;
5107
- }
5108
- meals[j + 1] = current;
5109
- }
5110
- if (!relation) {
5111
- return void 0;
5112
- }
5113
- return {
5114
- relation,
5115
- meals,
5116
- codes: groupedCodes
5117
- };
5118
- }
5119
- function inferDailyOccurrenceCount(input, options) {
5120
- var _a2, _b, _c, _d;
5121
- if (!(options == null ? void 0 : options.includeTimesPerDaySummary)) {
5122
- return void 0;
5123
- }
5124
- if (input.frequency !== void 0 || input.frequencyMax !== void 0 || input.timingCode) {
5125
- return void 0;
5126
- }
5127
- if (input.period !== void 0 || input.periodMax !== void 0 || input.periodUnit !== void 0) {
5128
- return void 0;
5129
- }
5130
- if (((_b = (_a2 = input.dayOfWeek) == null ? void 0 : _a2.length) != null ? _b : 0) > 0) {
5131
- return void 0;
5132
- }
5133
- const uniqueWhen2 = uniqueValues((_c = input.when) != null ? _c : []);
5134
- for (let i = 0; i < uniqueWhen2.length; i += 1) {
5135
- if (!INFERABLE_DAILY_EVENT_TIMINGS.has(uniqueWhen2[i])) {
5136
- return void 0;
5137
- }
5138
- }
5139
- const uniqueTimes = uniqueValues((_d = input.timeOfDay) != null ? _d : []);
5140
- const occurrences = uniqueWhen2.length + uniqueTimes.length;
5141
- if (occurrences === 0) {
5142
- return void 0;
5143
- }
5144
- return occurrences;
5145
- }
5146
-
5147
5472
  // src/format.ts
5148
5473
  var ROUTE_SHORT = {
5149
5474
  [RouteCode["Oral route"]]: "PO",
@@ -5635,73 +5960,22 @@ function summarizeMealTimingGroup(group) {
5635
5960
  }
5636
5961
  return `${relationText} ${joinWithAnd(group.meals)}`;
5637
5962
  }
5638
- function collectWhenPhrases(schedule, options) {
5639
- var _a2, _b, _c;
5640
- const when = (_a2 = schedule == null ? void 0 : schedule.when) != null ? _a2 : [];
5641
- if (!when.length) {
5642
- return [];
5643
- }
5644
- const unique = [];
5645
- const seen = /* @__PURE__ */ new Set();
5646
- let hasSpecificAfter = false;
5647
- let hasSpecificBefore = false;
5648
- let hasSpecificWith = false;
5649
- for (const code of when) {
5650
- if (!seen.has(code)) {
5651
- seen.add(code);
5652
- unique.push(code);
5653
- if (code === "PCM" /* After Breakfast */ || code === "PCD" /* After Lunch */ || code === "PCV" /* After Dinner */) {
5654
- hasSpecificAfter = true;
5655
- }
5656
- if (code === "ACM" /* Before Breakfast */ || code === "ACD" /* Before Lunch */ || code === "ACV" /* Before Dinner */) {
5657
- hasSpecificBefore = true;
5658
- }
5659
- if (code === "CM" /* Breakfast */ || code === "CD" /* Lunch */ || code === "CV" /* Dinner */) {
5660
- hasSpecificWith = true;
5661
- }
5662
- }
5663
- }
5664
- const filtered = [];
5665
- for (const code of unique) {
5666
- if (code === "PC" /* After Meal */ && hasSpecificAfter) {
5667
- continue;
5668
- }
5669
- if (code === "AC" /* Before Meal */ && hasSpecificBefore) {
5670
- continue;
5671
- }
5672
- if (code === "C" /* Meal */ && hasSpecificWith) {
5673
- continue;
5674
- }
5675
- filtered.push(code);
5676
- }
5677
- const mealGroup = getMealTimingGroup(filtered, options);
5678
- if (!mealGroup) {
5679
- const phrases2 = [];
5680
- for (const code of filtered) {
5681
- const text = (_b = WHEN_TEXT[code]) != null ? _b : code;
5682
- if (text) {
5683
- phrases2.push(text);
5684
- }
5685
- }
5686
- return phrases2;
5687
- }
5688
- const groupedCodes = new Set(mealGroup.codes);
5689
- const phrases = [];
5690
- let insertedGroup = false;
5691
- for (const code of filtered) {
5692
- if (groupedCodes.has(code)) {
5693
- if (!insertedGroup) {
5694
- phrases.push(summarizeMealTimingGroup(mealGroup));
5695
- insertedGroup = true;
5696
- }
5697
- continue;
5963
+ var EN_TIMING_GRAMMAR = {
5964
+ whenText: WHEN_TEXT,
5965
+ joinList: joinWithAnd,
5966
+ summarizeMealTimingGroup,
5967
+ bedtimeJoinStyle: (dailyCount) => {
5968
+ if (dailyCount === 1) {
5969
+ return "adjacent";
5698
5970
  }
5699
- const text = (_c = WHEN_TEXT[code]) != null ? _c : code;
5700
- if (text) {
5701
- phrases.push(text);
5971
+ if (dailyCount === 2 || dailyCount === 3 || dailyCount === 4) {
5972
+ return "conjunction";
5702
5973
  }
5974
+ return "separate";
5703
5975
  }
5704
- return phrases;
5976
+ };
5977
+ function collectWhenPhrases(schedule, options) {
5978
+ return collectLocalizedWhenPhrases(schedule, EN_TIMING_GRAMMAR, options);
5705
5979
  }
5706
5980
  function joinWithAnd(parts) {
5707
5981
  if (!parts.length) {
@@ -5715,23 +5989,14 @@ function joinWithAnd(parts) {
5715
5989
  }
5716
5990
  return `${parts.slice(0, -1).join(", ")} and ${parts[parts.length - 1]}`;
5717
5991
  }
5718
- function combineFrequencyAndEvents(frequency, events) {
5719
- if (!frequency) {
5720
- if (!events.length) {
5721
- return {};
5722
- }
5723
- return { event: joinWithAnd(events) };
5724
- }
5725
- if (!events.length) {
5726
- return { frequency };
5727
- }
5728
- if (events.length === 1 && events[0] === "at bedtime") {
5729
- const lowerFrequency = frequency.toLowerCase();
5730
- if (lowerFrequency === "twice daily" || lowerFrequency === "three times daily" || lowerFrequency === "four times daily") {
5731
- return { frequency: `${frequency} and ${events[0]}` };
5732
- }
5733
- }
5734
- return { frequency, event: joinWithAnd(events) };
5992
+ function combineFrequencyAndEvents(schedule, frequency, events, options) {
5993
+ return combineLocalizedFrequencyAndEvents(
5994
+ schedule,
5995
+ frequency,
5996
+ events,
5997
+ EN_TIMING_GRAMMAR,
5998
+ options
5999
+ );
5735
6000
  }
5736
6001
  function buildRoutePhrase(clause, grammar, hasSite) {
5737
6002
  var _a2, _b;
@@ -6036,7 +6301,7 @@ function formatLong(clause, options) {
6036
6301
  eventParts.push(`at ${timeStrings.join(", ")}`);
6037
6302
  }
6038
6303
  }
6039
- const timing = combineFrequencyAndEvents(frequencyPart, eventParts);
6304
+ const timing = combineFrequencyAndEvents(schedule, frequencyPart, eventParts, options);
6040
6305
  const dayPart = describeDayOfWeek(schedule);
6041
6306
  const countPart = schedule.count !== void 0 && !standaloneOccurrenceCount ? `for ${stripTrailingZero(schedule.count)} ${schedule.count === 1 ? "dose" : "doses"}` : void 0;
6042
6307
  const durationPart = describeDuration(schedule);
@@ -9831,6 +10096,81 @@ function listSupportedBodySiteGrammar() {
9831
10096
  };
9832
10097
  }
9833
10098
 
10099
+ // src/body-site-target.ts
10100
+ var BODY_SITE_ADMINISTRATION_TARGET_COUNT_EXTENSION_URL = "urn:ezmedicationinput:body-site-administration-target-count";
10101
+ function normalizeAdministrationTargetCount(value) {
10102
+ if (!Number.isFinite(value) || value === void 0) {
10103
+ return void 0;
10104
+ }
10105
+ const rounded = Math.trunc(value);
10106
+ return rounded >= 2 ? rounded : void 0;
10107
+ }
10108
+ function siteTextFromLike(site, options) {
10109
+ if (!site) {
10110
+ return void 0;
10111
+ }
10112
+ if (typeof site === "string") {
10113
+ return site;
10114
+ }
10115
+ if ("text" in site && site.text) {
10116
+ return site.text;
10117
+ }
10118
+ const coding = "coding" in site ? Array.isArray(site.coding) ? site.coding.find((candidate) => candidate == null ? void 0 : candidate.code) : site.coding : void 0;
10119
+ return (coding == null ? void 0 : coding.code) ? getBodySiteText(
10120
+ {
10121
+ system: coding.system,
10122
+ code: coding.code,
10123
+ display: coding.display
10124
+ },
10125
+ { siteCodeMap: options == null ? void 0 : options.siteCodeMap }
10126
+ ) : void 0;
10127
+ }
10128
+ function buildBodySiteAdministrationTargetCountExtension(targetCount) {
10129
+ const normalized = normalizeAdministrationTargetCount(targetCount);
10130
+ if (normalized === void 0) {
10131
+ return void 0;
10132
+ }
10133
+ return {
10134
+ url: BODY_SITE_ADMINISTRATION_TARGET_COUNT_EXTENSION_URL,
10135
+ valueInteger: normalized
10136
+ };
10137
+ }
10138
+ function parseBodySiteAdministrationTargetCountExtension(concept) {
10139
+ var _a2;
10140
+ const extension = (_a2 = concept == null ? void 0 : concept.extension) == null ? void 0 : _a2.find(
10141
+ (candidate) => candidate.url === BODY_SITE_ADMINISTRATION_TARGET_COUNT_EXTENSION_URL
10142
+ );
10143
+ return normalizeAdministrationTargetCount(extension == null ? void 0 : extension.valueInteger);
10144
+ }
10145
+ function getBodySiteAdministrationTargetCount(site, options) {
10146
+ var _a2;
10147
+ if (!site) {
10148
+ return void 0;
10149
+ }
10150
+ if (typeof site !== "string" && "administrationTargetCount" in site) {
10151
+ const direct = normalizeAdministrationTargetCount(site.administrationTargetCount);
10152
+ if (direct !== void 0) {
10153
+ return direct;
10154
+ }
10155
+ }
10156
+ if (typeof site !== "string") {
10157
+ if ("extension" in site) {
10158
+ const fromExtension = parseBodySiteAdministrationTargetCountExtension(site);
10159
+ if (fromExtension !== void 0) {
10160
+ return fromExtension;
10161
+ }
10162
+ }
10163
+ }
10164
+ const text = siteTextFromLike(site, options);
10165
+ if (!text) {
10166
+ return void 0;
10167
+ }
10168
+ const resolved = resolveBodySitePhrase(text, options == null ? void 0 : options.siteCodeMap, {
10169
+ bodySiteContext: options == null ? void 0 : options.bodySiteContext
10170
+ });
10171
+ return normalizeAdministrationTargetCount((_a2 = resolved == null ? void 0 : resolved.definition) == null ? void 0 : _a2.administrationTargetCount);
10172
+ }
10173
+
9834
10174
  // src/parser-state.ts
9835
10175
  var ParserState = class {
9836
10176
  constructor(input, tokens, customSiteHints) {
@@ -10201,6 +10541,20 @@ var ParserState = class {
10201
10541
  sourceText: value.sourceText
10202
10542
  };
10203
10543
  }
10544
+ get siteAdministrationTargetCount() {
10545
+ var _a2;
10546
+ return (_a2 = this.clause.site) == null ? void 0 : _a2.administrationTargetCount;
10547
+ }
10548
+ set siteAdministrationTargetCount(value) {
10549
+ if (value === void 0) {
10550
+ if (this.clause.site) {
10551
+ delete this.clause.site.administrationTargetCount;
10552
+ this.cleanupSite();
10553
+ }
10554
+ return;
10555
+ }
10556
+ this.ensureSite().administrationTargetCount = value;
10557
+ }
10204
10558
  get additionalInstructions() {
10205
10559
  if (!this.clause.additionalInstructions) {
10206
10560
  this.clause.additionalInstructions = [];
@@ -10273,7 +10627,7 @@ var ParserState = class {
10273
10627
  if (!site) {
10274
10628
  return;
10275
10629
  }
10276
- if (site.text === void 0 && site.coding === void 0 && site.spatialRelation === void 0 && site.source === void 0 && site.inferred === void 0 && site.evidence === void 0) {
10630
+ if (site.text === void 0 && site.coding === void 0 && site.spatialRelation === void 0 && site.administrationTargetCount === void 0 && site.source === void 0 && site.inferred === void 0 && site.evidence === void 0) {
10277
10631
  delete this.clause.site;
10278
10632
  }
10279
10633
  }
@@ -10634,7 +10988,7 @@ function appendWarning(warnings, warning) {
10634
10988
  return warnings;
10635
10989
  }
10636
10990
  function canonicalToFhir(clause, textOverride, options) {
10637
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J;
10991
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N;
10638
10992
  const dosage = {};
10639
10993
  const repeat = {};
10640
10994
  let hasRepeat = false;
@@ -10735,20 +11089,35 @@ function canonicalToFhir(clause, textOverride, options) {
10735
11089
  if (((_l = clause.site) == null ? void 0 : _l.text) || ((_n = (_m = clause.site) == null ? void 0 : _m.coding) == null ? void 0 : _n.code) || ((_o = clause.site) == null ? void 0 : _o.spatialRelation)) {
10736
11090
  const siteCoding = selectCanonicalSiteCoding(clause.site, options);
10737
11091
  const siteTextElement = (options == null ? void 0 : options.includeTranslationExtensions) ? buildTranslationPrimitiveElement((_p = clause.site.i18n) != null ? _p : siteCoding == null ? void 0 : siteCoding.i18n) : void 0;
11092
+ const siteTargetCount = (_r = (_q = clause.site) == null ? void 0 : _q.administrationTargetCount) != null ? _r : getBodySiteAdministrationTargetCount(clause.site);
11093
+ const inferredSiteTargetCount = getBodySiteAdministrationTargetCount({
11094
+ text: (_s = clause.site) == null ? void 0 : _s.text,
11095
+ coding: siteCoding ? {
11096
+ code: siteCoding.code,
11097
+ display: siteCoding.display,
11098
+ system: siteCoding.system,
11099
+ i18n: siteCoding.i18n
11100
+ } : void 0
11101
+ });
11102
+ const siteTargetCountExtension = siteTargetCount !== void 0 && siteTargetCount !== inferredSiteTargetCount ? buildBodySiteAdministrationTargetCountExtension(siteTargetCount) : void 0;
11103
+ const siteExtensions = [
11104
+ ...(_u = buildBodySiteSpatialRelationExtensions((_t = clause.site) == null ? void 0 : _t.spatialRelation)) != null ? _u : [],
11105
+ ...siteTargetCountExtension ? [siteTargetCountExtension] : []
11106
+ ];
10738
11107
  dosage.site = __spreadProps(__spreadValues({
10739
- text: (_q = clause.site) == null ? void 0 : _q.text
11108
+ text: (_v = clause.site) == null ? void 0 : _v.text
10740
11109
  }, siteTextElement ? { _text: siteTextElement } : {}), {
10741
11110
  coding: buildSiteCodingArray(siteCoding, options),
10742
- extension: buildBodySiteSpatialRelationExtensions((_r = clause.site) == null ? void 0 : _r.spatialRelation)
11111
+ extension: siteExtensions.length ? siteExtensions : void 0
10743
11112
  });
10744
11113
  }
10745
- if (((_s = clause.method) == null ? void 0 : _s.text) || ((_t = clause.method) == null ? void 0 : _t._text) || ((_v = (_u = clause.method) == null ? void 0 : _u.coding) == null ? void 0 : _v.code)) {
11114
+ if (((_w = clause.method) == null ? void 0 : _w.text) || ((_x = clause.method) == null ? void 0 : _x._text) || ((_z = (_y = clause.method) == null ? void 0 : _y.coding) == null ? void 0 : _z.code)) {
10746
11115
  dosage.method = {
10747
- text: (_w = clause.method) == null ? void 0 : _w.text,
10748
- _text: clonePrimitiveElement((_x = clause.method) == null ? void 0 : _x._text),
10749
- coding: ((_z = (_y = clause.method) == null ? void 0 : _y.coding) == null ? void 0 : _z.code) ? [
11116
+ text: (_A = clause.method) == null ? void 0 : _A.text,
11117
+ _text: clonePrimitiveElement((_B = clause.method) == null ? void 0 : _B._text),
11118
+ coding: ((_D = (_C = clause.method) == null ? void 0 : _C.coding) == null ? void 0 : _D.code) ? [
10750
11119
  {
10751
- system: (_A = clause.method.coding.system) != null ? _A : SNOMED_SYSTEM5,
11120
+ system: (_E = clause.method.coding.system) != null ? _E : SNOMED_SYSTEM5,
10752
11121
  code: clause.method.coding.code,
10753
11122
  display: clause.method.coding.display,
10754
11123
  _display: clonePrimitiveElement(clause.method.coding._display)
@@ -10756,14 +11125,14 @@ function canonicalToFhir(clause, textOverride, options) {
10756
11125
  ] : void 0
10757
11126
  };
10758
11127
  }
10759
- if ((_B = clause.additionalInstructions) == null ? void 0 : _B.length) {
11128
+ if ((_F = clause.additionalInstructions) == null ? void 0 : _F.length) {
10760
11129
  dosage.additionalInstruction = [];
10761
11130
  for (const instruction of clause.additionalInstructions) {
10762
11131
  dosage.additionalInstruction.push({
10763
11132
  text: instruction.text,
10764
- coding: ((_C = instruction.coding) == null ? void 0 : _C.code) ? [
11133
+ coding: ((_G = instruction.coding) == null ? void 0 : _G.code) ? [
10765
11134
  {
10766
- system: (_D = instruction.coding.system) != null ? _D : SNOMED_SYSTEM5,
11135
+ system: (_H = instruction.coding.system) != null ? _H : SNOMED_SYSTEM5,
10767
11136
  code: instruction.coding.code,
10768
11137
  display: instruction.coding.display
10769
11138
  }
@@ -10771,9 +11140,9 @@ function canonicalToFhir(clause, textOverride, options) {
10771
11140
  });
10772
11141
  }
10773
11142
  }
10774
- if ((_E = clause.prn) == null ? void 0 : _E.enabled) {
11143
+ if ((_I = clause.prn) == null ? void 0 : _I.enabled) {
10775
11144
  dosage.asNeededBoolean = true;
10776
- const reasons = ((_F = clause.prn.reasons) == null ? void 0 : _F.length) ? clause.prn.reasons : clause.prn.reason ? [clause.prn.reason] : [];
11145
+ const reasons = ((_J = clause.prn.reasons) == null ? void 0 : _J.length) ? clause.prn.reasons : clause.prn.reason ? [clause.prn.reason] : [];
10777
11146
  if (reasons.length) {
10778
11147
  dosage.asNeededFor = [];
10779
11148
  for (const reason of reasons) {
@@ -10781,18 +11150,18 @@ function canonicalToFhir(clause, textOverride, options) {
10781
11150
  if (reason.text) {
10782
11151
  concept.text = reason.text;
10783
11152
  }
10784
- const reasonTextElement = (options == null ? void 0 : options.includeTranslationExtensions) ? buildTranslationPrimitiveElement((_H = reason.i18n) != null ? _H : (_G = reason.coding) == null ? void 0 : _G.i18n) : void 0;
11153
+ const reasonTextElement = (options == null ? void 0 : options.includeTranslationExtensions) ? buildTranslationPrimitiveElement((_L = reason.i18n) != null ? _L : (_K = reason.coding) == null ? void 0 : _K.i18n) : void 0;
10785
11154
  if (reasonTextElement) {
10786
11155
  concept._text = reasonTextElement;
10787
11156
  }
10788
- if ((_I = reason.coding) == null ? void 0 : _I.code) {
11157
+ if ((_M = reason.coding) == null ? void 0 : _M.code) {
10789
11158
  const displayElement = (options == null ? void 0 : options.includeTranslationExtensions) ? mergeTranslationPrimitiveElement(
10790
11159
  reason.coding._display,
10791
11160
  reason.coding.i18n
10792
11161
  ) : clonePrimitiveElement(reason.coding._display);
10793
11162
  concept.coding = [
10794
11163
  __spreadProps(__spreadValues({
10795
- system: (_J = reason.coding.system) != null ? _J : SNOMED_SYSTEM5,
11164
+ system: (_N = reason.coding.system) != null ? _N : SNOMED_SYSTEM5,
10796
11165
  code: reason.coding.code,
10797
11166
  display: reason.coding.display
10798
11167
  }, displayElement ? { _display: displayElement } : {}), {
@@ -10833,11 +11202,13 @@ function canonicalFromFhir(dosage) {
10833
11202
  const siteSpatialRelation = parseBodySiteSpatialRelationExtension(dosage.site);
10834
11203
  const siteText = getFallbackSiteText(dosage.site);
10835
11204
  const siteI18n = codeableConceptTranslationI18n(dosage.site, siteCoding);
10836
- if (siteText || (siteCoding == null ? void 0 : siteCoding.code) || siteSpatialRelation) {
11205
+ const siteAdministrationTargetCount = getBodySiteAdministrationTargetCount(dosage.site);
11206
+ if (siteText || (siteCoding == null ? void 0 : siteCoding.code) || siteSpatialRelation || siteAdministrationTargetCount !== void 0) {
10837
11207
  clause.site = {
10838
11208
  text: siteText,
10839
11209
  i18n: siteI18n.text,
10840
11210
  spatialRelation: siteSpatialRelation,
11211
+ administrationTargetCount: siteAdministrationTargetCount,
10841
11212
  coding: (siteCoding == null ? void 0 : siteCoding.code) ? {
10842
11213
  code: siteCoding.code,
10843
11214
  display: siteCoding.display,
@@ -11918,73 +12289,22 @@ function summarizeMealTimingGroupThai(group) {
11918
12289
  }
11919
12290
  return `${relationText[group.relation]}${joinMealNamesThai(meals)}`;
11920
12291
  }
11921
- function collectWhenPhrasesThai(schedule, options) {
11922
- var _a2;
11923
- const when = (_a2 = schedule == null ? void 0 : schedule.when) != null ? _a2 : [];
11924
- if (!when.length) {
11925
- return [];
11926
- }
11927
- const unique = [];
11928
- const seen = /* @__PURE__ */ new Set();
11929
- let hasSpecificAfter = false;
11930
- let hasSpecificBefore = false;
11931
- let hasSpecificWith = false;
11932
- for (const code of when) {
11933
- if (!seen.has(code)) {
11934
- seen.add(code);
11935
- unique.push(code);
11936
- if (code === "PCM" /* After Breakfast */ || code === "PCD" /* After Lunch */ || code === "PCV" /* After Dinner */) {
11937
- hasSpecificAfter = true;
11938
- }
11939
- if (code === "ACM" /* Before Breakfast */ || code === "ACD" /* Before Lunch */ || code === "ACV" /* Before Dinner */) {
11940
- hasSpecificBefore = true;
11941
- }
11942
- if (code === "CM" /* Breakfast */ || code === "CD" /* Lunch */ || code === "CV" /* Dinner */) {
11943
- hasSpecificWith = true;
11944
- }
11945
- }
11946
- }
11947
- const filtered = [];
11948
- for (const code of unique) {
11949
- if (code === "PC" /* After Meal */ && hasSpecificAfter) {
11950
- continue;
11951
- }
11952
- if (code === "AC" /* Before Meal */ && hasSpecificBefore) {
11953
- continue;
11954
- }
11955
- if (code === "C" /* Meal */ && hasSpecificWith) {
11956
- continue;
11957
- }
11958
- filtered.push(code);
11959
- }
11960
- const mealGroup = getMealTimingGroup(filtered, options);
11961
- if (!mealGroup) {
11962
- const phrases2 = [];
11963
- for (const code of filtered) {
11964
- const text = WHEN_TEXT_THAI[code];
11965
- if (text) {
11966
- phrases2.push(text);
11967
- }
11968
- }
11969
- return phrases2;
11970
- }
11971
- const groupedCodes = new Set(mealGroup.codes);
11972
- const phrases = [];
11973
- let insertedGroup = false;
11974
- for (const code of filtered) {
11975
- if (groupedCodes.has(code)) {
11976
- if (!insertedGroup) {
11977
- phrases.push(summarizeMealTimingGroupThai(mealGroup));
11978
- insertedGroup = true;
11979
- }
11980
- continue;
12292
+ var TH_TIMING_GRAMMAR = {
12293
+ whenText: WHEN_TEXT_THAI,
12294
+ joinList: joinWithAndThai,
12295
+ summarizeMealTimingGroup: summarizeMealTimingGroupThai,
12296
+ bedtimeJoinStyle: (dailyCount) => {
12297
+ if (dailyCount === 1) {
12298
+ return "adjacent";
11981
12299
  }
11982
- const text = WHEN_TEXT_THAI[code];
11983
- if (text) {
11984
- phrases.push(text);
12300
+ if (dailyCount === 2 || dailyCount === 3 || dailyCount === 4) {
12301
+ return "conjunction";
11985
12302
  }
12303
+ return "separate";
11986
12304
  }
11987
- return phrases;
12305
+ };
12306
+ function collectWhenPhrasesThai(schedule, options) {
12307
+ return collectLocalizedWhenPhrases(schedule, TH_TIMING_GRAMMAR, options);
11988
12308
  }
11989
12309
  function joinWithAndThai(parts) {
11990
12310
  if (!parts.length) {
@@ -11998,20 +12318,14 @@ function joinWithAndThai(parts) {
11998
12318
  }
11999
12319
  return `${parts.slice(0, -1).join(", ")} \u0E41\u0E25\u0E30 ${parts[parts.length - 1]}`;
12000
12320
  }
12001
- function combineFrequencyAndEventsThai(frequency, events) {
12002
- if (!frequency) {
12003
- if (!events.length) {
12004
- return {};
12005
- }
12006
- return { event: joinWithAndThai(events) };
12007
- }
12008
- if (!events.length) {
12009
- return { frequency };
12010
- }
12011
- if (events.length === 1 && events[0] === "\u0E01\u0E48\u0E2D\u0E19\u0E19\u0E2D\u0E19" && frequency.includes("\u0E27\u0E31\u0E19\u0E25\u0E30")) {
12012
- return { frequency: `${frequency} \u0E41\u0E25\u0E30 ${events[0]}` };
12013
- }
12014
- return { frequency, event: joinWithAndThai(events) };
12321
+ function combineFrequencyAndEventsThai(schedule, frequency, events, options) {
12322
+ return combineLocalizedFrequencyAndEvents(
12323
+ schedule,
12324
+ frequency,
12325
+ events,
12326
+ TH_TIMING_GRAMMAR,
12327
+ options
12328
+ );
12015
12329
  }
12016
12330
  function isOralRouteThai(clause) {
12017
12331
  var _a2, _b, _c;
@@ -12070,7 +12384,7 @@ function buildRoutePhraseThai(clause, grammar, hasSite) {
12070
12384
  return text;
12071
12385
  }
12072
12386
  function formatSiteThai(clause, grammar) {
12073
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
12387
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
12074
12388
  const text = ((_b = (_a2 = clause.site) == null ? void 0 : _a2.text) == null ? void 0 : _b.trim()) || ((_e = (_d = (_c = clause.site) == null ? void 0 : _c.coding) == null ? void 0 : _d.display) == null ? void 0 : _e.trim());
12075
12389
  const lower = text == null ? void 0 : text.toLowerCase();
12076
12390
  const codingCode = (_g = (_f = clause.site) == null ? void 0 : _f.coding) == null ? void 0 : _g.code;
@@ -12085,14 +12399,14 @@ function formatSiteThai(clause, grammar) {
12085
12399
  if (isVaginalRoute && isVaginaSite) {
12086
12400
  return void 0;
12087
12401
  }
12088
- const translated = text ? translateSiteThai(text, codingCode, (_l = clause.site) == null ? void 0 : _l.spatialRelation) : translateSpatialSiteThai(void 0, (_m = clause.site) == null ? void 0 : _m.spatialRelation);
12402
+ const translated = (_t = (_q = (_m = (_l = clause.site) == null ? void 0 : _l.i18n) == null ? void 0 : _m.th) != null ? _q : (_p = (_o = (_n = clause.site) == null ? void 0 : _n.coding) == null ? void 0 : _o.i18n) == null ? void 0 : _p.th) != null ? _t : text ? translateSiteThai(text, codingCode, (_r = clause.site) == null ? void 0 : _r.spatialRelation) : translateSpatialSiteThai(void 0, (_s = clause.site) == null ? void 0 : _s.spatialRelation);
12089
12403
  if (!translated) {
12090
12404
  return void 0;
12091
12405
  }
12092
- if (((_n = clause.route) == null ? void 0 : _n.code) === RouteCode["Nasal route"]) {
12406
+ if (((_u = clause.route) == null ? void 0 : _u.code) === RouteCode["Nasal route"]) {
12093
12407
  return `\u0E40\u0E02\u0E49\u0E32${translated}`;
12094
12408
  }
12095
- const preposition = (_o = grammar.sitePreposition) != null ? _o : "\u0E17\u0E35\u0E48";
12409
+ const preposition = (_v = grammar.sitePreposition) != null ? _v : "\u0E17\u0E35\u0E48";
12096
12410
  const separator = /^[\u0E00-\u0E7F]/.test(translated) ? "" : " ";
12097
12411
  return `${preposition}${separator}${translated}`.trim();
12098
12412
  }
@@ -12390,7 +12704,7 @@ function formatLongThai(clause, options) {
12390
12704
  eventParts.push(`\u0E40\u0E27\u0E25\u0E32 ${timeStrings.join(", ")}`);
12391
12705
  }
12392
12706
  }
12393
- const timing = combineFrequencyAndEventsThai(frequencyPart, eventParts);
12707
+ const timing = combineFrequencyAndEventsThai(schedule, frequencyPart, eventParts, options);
12394
12708
  const dayPart = describeDayOfWeekThai(schedule);
12395
12709
  const countPart = schedule.count !== void 0 && !standaloneOccurrenceCount ? `\u0E08\u0E33\u0E19\u0E27\u0E19 ${stripTrailingZero2(schedule.count)} \u0E04\u0E23\u0E31\u0E49\u0E07` : void 0;
12396
12710
  const durationPart = describeDurationThai(schedule);
@@ -12822,6 +13136,12 @@ function sameOptionalScalar(left, right) {
12822
13136
  function mergeOptionalScalar(left, right) {
12823
13137
  return left !== void 0 ? left : right;
12824
13138
  }
13139
+ function mergeI18nRecords2(left, right) {
13140
+ if (!left && !right) {
13141
+ return void 0;
13142
+ }
13143
+ return __spreadValues(__spreadValues({}, left != null ? left : {}), right != null ? right : {});
13144
+ }
12825
13145
  function sameCoding(left, right) {
12826
13146
  var _a2, _b;
12827
13147
  if (!(left == null ? void 0 : left.code) || !(right == null ? void 0 : right.code)) {
@@ -12884,6 +13204,7 @@ function mergeSite(left, right, context) {
12884
13204
  }
12885
13205
  return {
12886
13206
  text: mergeOptionalScalar(left.text, right.text),
13207
+ i18n: mergeI18nRecords2(left.i18n, right.i18n),
12887
13208
  source: mergeOptionalScalar(left.source, right.source),
12888
13209
  coding: mergeOptionalScalar(left.coding, right.coding),
12889
13210
  spatialRelation: mergeOptionalScalar(left.spatialRelation, right.spatialRelation),
@@ -13231,6 +13552,7 @@ var TOKEN_SITE_CANDIDATES = {
13231
13552
  le: [{ text: "left eye", route: RouteCode["Ophthalmic route"], source: "abbreviation" }],
13232
13553
  ou: [{ text: "both eyes", route: RouteCode["Ophthalmic route"], source: "abbreviation" }],
13233
13554
  be: [{ text: "both eyes", route: RouteCode["Ophthalmic route"], source: "abbreviation" }],
13555
+ au: [{ text: "both ears", route: RouteCode["Otic route"], source: "abbreviation" }],
13234
13556
  vod: [
13235
13557
  {
13236
13558
  text: "right eye",
@@ -14836,6 +15158,11 @@ var unit_terminology_default = {
14836
15158
  unit: "pea-sized amount",
14837
15159
  kind: "product_specific_amount",
14838
15160
  aliases: [
15161
+ "pea",
15162
+ "pea amount",
15163
+ "peasize",
15164
+ "pea-size",
15165
+ "pea size",
14839
15166
  "pea sized amount",
14840
15167
  "pea-sized",
14841
15168
  "pea sized",
@@ -14932,10 +15259,96 @@ var unit_terminology_default = {
14932
15259
  ]
14933
15260
  };
14934
15261
 
15262
+ // src/utils/units.ts
15263
+ var MASS_UNITS = {
15264
+ kg: 1e6,
15265
+ g: 1e3,
15266
+ mg: 1,
15267
+ mcg: 1e-3,
15268
+ ug: 1e-3,
15269
+ microg: 1e-3,
15270
+ ng: 1e-6
15271
+ };
15272
+ var VOLUME_UNITS = {
15273
+ l: 1e3,
15274
+ dl: 100,
15275
+ ml: 1,
15276
+ ul: 1e-3,
15277
+ microl: 1e-3,
15278
+ cm3: 1,
15279
+ tsp: 5,
15280
+ tbsp: 15
15281
+ };
15282
+ function getUnitCategory(unit) {
15283
+ if (!unit) return "other";
15284
+ const u = unit.toLowerCase();
15285
+ if (MASS_UNITS[u] !== void 0) return "mass";
15286
+ if (VOLUME_UNITS[u] !== void 0) return "volume";
15287
+ return "other";
15288
+ }
15289
+ function getBaseUnitFactor(unit) {
15290
+ var _a2, _b;
15291
+ if (!unit) return 1;
15292
+ const u = unit.toLowerCase();
15293
+ return (_b = (_a2 = MASS_UNITS[u]) != null ? _a2 : VOLUME_UNITS[u]) != null ? _b : 1;
15294
+ }
15295
+ function convertValue(value, fromUnit, toUnit, strength) {
15296
+ const f = fromUnit.toLowerCase();
15297
+ const t = toUnit.toLowerCase();
15298
+ if (f === t) return value;
15299
+ const fCat = getUnitCategory(f);
15300
+ const tCat = getUnitCategory(t);
15301
+ if (fCat === tCat && fCat !== "other") {
15302
+ const fFactor = getBaseUnitFactor(f);
15303
+ const tFactor = getBaseUnitFactor(t);
15304
+ return value * fFactor / tFactor;
15305
+ }
15306
+ if (strength && (fCat === "mass" && tCat === "volume" || fCat === "volume" && tCat === "mass")) {
15307
+ const numUnit = strength.numerator.unit.toLowerCase();
15308
+ const denUnit = strength.denominator.unit.toLowerCase();
15309
+ const numCat = getUnitCategory(numUnit);
15310
+ const denCat = getUnitCategory(denUnit);
15311
+ if (numCat !== denCat && numCat !== "other" && denCat !== "other") {
15312
+ const massSide = numCat === "mass" ? strength.numerator : strength.denominator;
15313
+ const volSide = numCat === "volume" ? strength.numerator : strength.denominator;
15314
+ const bridgeDensity = massSide.value * getBaseUnitFactor(massSide.unit) / (volSide.value * getBaseUnitFactor(volSide.unit));
15315
+ if (fCat === "mass") {
15316
+ const valueMg = value * getBaseUnitFactor(fromUnit);
15317
+ const valueMl = valueMg / bridgeDensity;
15318
+ return valueMl / getBaseUnitFactor(toUnit);
15319
+ } else {
15320
+ const valueMl = value * getBaseUnitFactor(fromUnit);
15321
+ const valueMg = valueMl * bridgeDensity;
15322
+ return valueMg / getBaseUnitFactor(toUnit);
15323
+ }
15324
+ }
15325
+ }
15326
+ return null;
15327
+ }
15328
+
14935
15329
  // src/unit-lexicon.ts
14936
15330
  var HOUSEHOLD_VOLUME_UNIT_SET = new Set(
14937
15331
  HOUSEHOLD_VOLUME_UNITS.map((unit) => unit.toLowerCase())
14938
15332
  );
15333
+ var MASS_DISPENSED_SEMISOLID_DOSAGE_FORMS = /* @__PURE__ */ new Set([
15334
+ "cream",
15335
+ "ointment",
15336
+ "gel",
15337
+ "paste",
15338
+ "cutaneous paste",
15339
+ "vaginal cream",
15340
+ "vaginal gel",
15341
+ "oral gel",
15342
+ "oral paste",
15343
+ "oromucosal gel",
15344
+ "oromucosal paste",
15345
+ "dental gel",
15346
+ "dental paste",
15347
+ "gingival gel",
15348
+ "nasal gel",
15349
+ "eye gel",
15350
+ "eye ointment"
15351
+ ]);
14939
15352
  var DOSE_UNIT_TERMINOLOGY = unit_terminology_default.terms;
14940
15353
  var DOSE_UNIT_TERMINOLOGY_BY_KEY = /* @__PURE__ */ new Map();
14941
15354
  var DISCRETE_UNIT_KINDS = /* @__PURE__ */ new Set([
@@ -14998,6 +15411,47 @@ function unitApproximationOverride(unit, context) {
14998
15411
  }
14999
15412
  return void 0;
15000
15413
  }
15414
+ function normalizeDosageFormKey(form) {
15415
+ var _a2;
15416
+ const normalized = form == null ? void 0 : form.trim().toLowerCase();
15417
+ if (!normalized) {
15418
+ return void 0;
15419
+ }
15420
+ return (_a2 = KNOWN_DOSAGE_FORMS_TO_DOSE[normalized]) != null ? _a2 : normalized;
15421
+ }
15422
+ function getPreferredMassApproximationUnit(context) {
15423
+ var _a2;
15424
+ const normalizedDosageForm = normalizeDosageFormKey(context == null ? void 0 : context.dosageForm);
15425
+ if (!normalizedDosageForm || !MASS_DISPENSED_SEMISOLID_DOSAGE_FORMS.has(normalizedDosageForm)) {
15426
+ return void 0;
15427
+ }
15428
+ const containerUnit = (_a2 = context == null ? void 0 : context.containerUnit) == null ? void 0 : _a2.trim();
15429
+ if (containerUnit && getUnitCategory(containerUnit) === "mass") {
15430
+ return containerUnit;
15431
+ }
15432
+ const defaultUnit = normalizedDosageForm ? DEFAULT_UNIT_BY_NORMALIZED_FORM[normalizedDosageForm] : void 0;
15433
+ return defaultUnit && getUnitCategory(defaultUnit) === "mass" ? defaultUnit : void 0;
15434
+ }
15435
+ function bridgeApproximationToMassDispensedTopical(approximation, context) {
15436
+ const preferredMassUnit = getPreferredMassApproximationUnit(context);
15437
+ if (!preferredMassUnit || getUnitCategory(approximation.unit) !== "volume") {
15438
+ return approximation;
15439
+ }
15440
+ const sourceVolumeFactor = VOLUME_UNITS[approximation.unit.toLowerCase()];
15441
+ const targetMassFactor = MASS_UNITS[preferredMassUnit.toLowerCase()];
15442
+ if (!sourceVolumeFactor || !targetMassFactor) {
15443
+ return approximation;
15444
+ }
15445
+ const valueMl = approximation.value * sourceVolumeFactor;
15446
+ const valueG = valueMl;
15447
+ const convertedValue = valueG * MASS_UNITS.g / targetMassFactor;
15448
+ const bridgeBasis = "Mass-dispensed semisolid topical default bridge assumes 1 mL approximately equals 1 g unless a product-specific override is provided";
15449
+ return __spreadProps(__spreadValues({}, approximation), {
15450
+ value: convertedValue,
15451
+ unit: preferredMassUnit,
15452
+ basis: approximation.basis ? `${approximation.basis}; ${bridgeBasis}` : bridgeBasis
15453
+ });
15454
+ }
15001
15455
  function getDoseUnitTerminologyEntry(unit) {
15002
15456
  var _a2;
15003
15457
  if (!unit) {
@@ -15019,7 +15473,11 @@ function getDoseUnitApproximation(unit, context) {
15019
15473
  if (override) {
15020
15474
  return override;
15021
15475
  }
15022
- return terminologyEntry == null ? void 0 : terminologyEntry.approximateQuantity;
15476
+ const approximation = terminologyEntry == null ? void 0 : terminologyEntry.approximateQuantity;
15477
+ if (!approximation) {
15478
+ return void 0;
15479
+ }
15480
+ return bridgeApproximationToMassDispensedTopical(approximation, context);
15023
15481
  }
15024
15482
  function getDoseUnitSemantics(unit, context) {
15025
15483
  if (!unit) {
@@ -15780,6 +16238,9 @@ function inferRouteFromContext(ctx) {
15780
16238
  // src/hpsg/rules/site-rules.ts
15781
16239
  function siteBoundary(lower, context) {
15782
16240
  var _a2, _b, _c;
16241
+ if (SITE_MULTIPLICITY_WORDS.has(lower)) {
16242
+ return false;
16243
+ }
15783
16244
  const siteLike = Boolean(resolveBodySitePhrase(lower, (_a2 = context.options) == null ? void 0 : _a2.siteCodeMap, {
15784
16245
  bodySiteContext: (_c = (_b = context.options) == null ? void 0 : _b.context) == null ? void 0 : _c.bodySiteContext
15785
16246
  }));
@@ -15841,7 +16302,7 @@ function shouldSuppressEyeSiteAbbreviation(context, start, lower) {
15841
16302
  }
15842
16303
  function siteLexicalRule() {
15843
16304
  return lexicalRule("hpsg.lex.site", (context, start) => {
15844
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
16305
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
15845
16306
  const token = (_a2 = tokensAvailable(context, start, 1)) == null ? void 0 : _a2[0];
15846
16307
  if (!token) {
15847
16308
  return [];
@@ -15907,7 +16368,7 @@ function siteLexicalRule() {
15907
16368
  continue;
15908
16369
  }
15909
16370
  phraseTokens.push(candidate);
15910
- if (!SITE_DISPLAY_FILLERS.has(candidateLower)) {
16371
+ if (!SITE_DISPLAY_FILLERS.has(candidateLower) || SITE_MULTIPLICITY_WORDS.has(candidateLower)) {
15911
16372
  displayTokens.push(candidate);
15912
16373
  }
15913
16374
  }
@@ -15953,6 +16414,7 @@ function siteLexicalRule() {
15953
16414
  valence: {
15954
16415
  site: {
15955
16416
  text: displayText,
16417
+ i18n: (_r = resolved == null ? void 0 : resolved.definition) == null ? void 0 : _r.i18n,
15956
16418
  source: "text",
15957
16419
  coding: resolved == null ? void 0 : resolved.coding,
15958
16420
  spatialRelation: resolved == null ? void 0 : resolved.spatialRelation,
@@ -15980,7 +16442,7 @@ function siteLexicalRule() {
15980
16442
  }
15981
16443
  function bareSiteLexicalRule() {
15982
16444
  return lexicalRule("hpsg.lex.site.bare", (context, start) => {
15983
- var _a2, _b, _c, _d, _e, _f, _g, _h;
16445
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i;
15984
16446
  const signs = [];
15985
16447
  const first = (_a2 = tokensAvailable(context, start, 1)) == null ? void 0 : _a2[0];
15986
16448
  if (!first) {
@@ -16026,6 +16488,7 @@ function bareSiteLexicalRule() {
16026
16488
  valence: {
16027
16489
  site: {
16028
16490
  text: displayText,
16491
+ i18n: (_h = resolved.definition) == null ? void 0 : _h.i18n,
16029
16492
  source: "text",
16030
16493
  coding: resolved.coding,
16031
16494
  spatialRelation: resolved.spatialRelation,
@@ -16033,7 +16496,7 @@ function bareSiteLexicalRule() {
16033
16496
  originalText,
16034
16497
  text: sourceText,
16035
16498
  normalized: sourceText.toLowerCase(),
16036
- canonical: (_h = resolved.resolutionCanonical) != null ? _h : normalizeBodySiteKey(displayText),
16499
+ canonical: (_i = resolved.resolutionCanonical) != null ? _i : normalizeBodySiteKey(displayText),
16037
16500
  isProbe,
16038
16501
  inputText: context.state.input,
16039
16502
  sourceText,
@@ -16261,6 +16724,64 @@ function productLexicalRule() {
16261
16724
  }
16262
16725
  function matchCompoundDoseUnit(context, start, lower) {
16263
16726
  var _a2;
16727
+ const MAX_SITE_CONTEXT_TOKENS = 3;
16728
+ const hasSiteMeaning = (lowerValue) => {
16729
+ var _a3, _b, _c;
16730
+ return Boolean(
16731
+ lowerValue && (DEFAULT_BODY_SITE_SNOMED[normalizeBodySiteKey(lowerValue)] || resolveBodySitePhrase(lowerValue, (_a3 = context.options) == null ? void 0 : _a3.siteCodeMap, {
16732
+ bodySiteContext: (_c = (_b = context.options) == null ? void 0 : _b.context) == null ? void 0 : _c.bodySiteContext
16733
+ }))
16734
+ );
16735
+ };
16736
+ const collectForwardSitePhrases = (phraseStart) => {
16737
+ const parts = [];
16738
+ const phrases = [];
16739
+ for (let index = phraseStart; index < context.limit && parts.length < MAX_SITE_CONTEXT_TOKENS; index += 1) {
16740
+ const token = context.tokens[index];
16741
+ if (!token || context.state.consumed.has(token.index)) {
16742
+ break;
16743
+ }
16744
+ const lowerValue = normalizeTokenLower(token);
16745
+ if (!lowerValue || isPunctuation(lowerValue)) {
16746
+ break;
16747
+ }
16748
+ parts.push(lowerValue);
16749
+ phrases.push(parts.join(" "));
16750
+ }
16751
+ return phrases;
16752
+ };
16753
+ const matchesSiteContext = () => {
16754
+ const next = context.tokens[start + 1];
16755
+ const nextLower = next && !context.state.consumed.has(next.index) ? normalizeTokenLower(next) : void 0;
16756
+ if (nextLower && (ROUTE_SITE_PREPOSITIONS.has(nextLower) || SITE_ANCHORS.has(nextLower))) {
16757
+ const followingSitePhrases = collectForwardSitePhrases(start + 2);
16758
+ if (followingSitePhrases.some((phrase) => hasSiteMeaning(phrase))) {
16759
+ return true;
16760
+ }
16761
+ }
16762
+ const previous = context.tokens[start - 1];
16763
+ const previousLower = previous && !context.state.consumed.has(previous.index) ? normalizeTokenLower(previous) : void 0;
16764
+ const trailingNumberBeforeUnit = Boolean(
16765
+ previousLower && /^[0-9]+(?:\.[0-9]+)?$/.test(previousLower)
16766
+ );
16767
+ const siteEnd = start - (trailingNumberBeforeUnit ? 2 : 1);
16768
+ for (let phraseLength = 1; phraseLength <= MAX_SITE_CONTEXT_TOKENS; phraseLength += 1) {
16769
+ const phraseStart = siteEnd - phraseLength + 1;
16770
+ if (phraseStart < 0) {
16771
+ break;
16772
+ }
16773
+ const precedingAnchor = context.tokens[phraseStart - 1];
16774
+ const precedingAnchorLower = precedingAnchor && !context.state.consumed.has(precedingAnchor.index) ? normalizeTokenLower(precedingAnchor) : void 0;
16775
+ if (!precedingAnchorLower || !ROUTE_SITE_PREPOSITIONS.has(precedingAnchorLower) && !SITE_ANCHORS.has(precedingAnchorLower)) {
16776
+ continue;
16777
+ }
16778
+ const phrase = collectForwardSitePhrases(phraseStart)[phraseLength - 1];
16779
+ if (hasSiteMeaning(phrase)) {
16780
+ return true;
16781
+ }
16782
+ }
16783
+ return false;
16784
+ };
16264
16785
  for (const compound of COMPOUND_DOSE_UNITS) {
16265
16786
  if (compound.head !== lower) {
16266
16787
  continue;
@@ -16290,6 +16811,10 @@ function matchCompoundDoseUnit(context, start, lower) {
16290
16811
  return head ? { unit: compound.unit, tokens: [head, next] } : void 0;
16291
16812
  }
16292
16813
  }
16814
+ if (compound.requiresSiteContext && matchesSiteContext()) {
16815
+ const head = context.tokens[start];
16816
+ return head ? { unit: compound.unit, tokens: [head] } : void 0;
16817
+ }
16293
16818
  }
16294
16819
  return void 0;
16295
16820
  }
@@ -18060,6 +18585,9 @@ function applySiteDefinition(internal, definition) {
18060
18585
  } else if ((_b = internal.siteLookupRequest) == null ? void 0 : _b.text) {
18061
18586
  internal.siteText = internal.siteLookupRequest.text;
18062
18587
  }
18588
+ if (definition.administrationTargetCount !== void 0) {
18589
+ internal.siteAdministrationTargetCount = definition.administrationTargetCount;
18590
+ }
18063
18591
  if (definition.spatialRelation) {
18064
18592
  internal.siteSpatialRelation = definition.spatialRelation;
18065
18593
  } else if ((_c = internal.siteLookupRequest) == null ? void 0 : _c.spatialRelation) {
@@ -18412,6 +18940,9 @@ function seedKnownSiteCoding(state) {
18412
18940
  display: definition.coding.display,
18413
18941
  i18n: mergeI18nRecords(definition.i18n, definition.coding.i18n)
18414
18942
  };
18943
+ if (state.siteAdministrationTargetCount === void 0) {
18944
+ state.siteAdministrationTargetCount = definition.administrationTargetCount;
18945
+ }
18415
18946
  }
18416
18947
  function parseClauseState(input, options) {
18417
18948
  const tokens = tokenize(input);
@@ -19481,73 +20012,6 @@ function suggestSig(input, options) {
19481
20012
  );
19482
20013
  }
19483
20014
 
19484
- // src/utils/units.ts
19485
- var MASS_UNITS = {
19486
- kg: 1e6,
19487
- g: 1e3,
19488
- mg: 1,
19489
- mcg: 1e-3,
19490
- ug: 1e-3,
19491
- microg: 1e-3,
19492
- ng: 1e-6
19493
- };
19494
- var VOLUME_UNITS = {
19495
- l: 1e3,
19496
- dl: 100,
19497
- ml: 1,
19498
- ul: 1e-3,
19499
- microl: 1e-3,
19500
- cm3: 1,
19501
- tsp: 5,
19502
- tbsp: 15
19503
- };
19504
- function getUnitCategory(unit) {
19505
- if (!unit) return "other";
19506
- const u = unit.toLowerCase();
19507
- if (MASS_UNITS[u] !== void 0) return "mass";
19508
- if (VOLUME_UNITS[u] !== void 0) return "volume";
19509
- return "other";
19510
- }
19511
- function getBaseUnitFactor(unit) {
19512
- var _a2, _b;
19513
- if (!unit) return 1;
19514
- const u = unit.toLowerCase();
19515
- return (_b = (_a2 = MASS_UNITS[u]) != null ? _a2 : VOLUME_UNITS[u]) != null ? _b : 1;
19516
- }
19517
- function convertValue(value, fromUnit, toUnit, strength) {
19518
- const f = fromUnit.toLowerCase();
19519
- const t = toUnit.toLowerCase();
19520
- if (f === t) return value;
19521
- const fCat = getUnitCategory(f);
19522
- const tCat = getUnitCategory(t);
19523
- if (fCat === tCat && fCat !== "other") {
19524
- const fFactor = getBaseUnitFactor(f);
19525
- const tFactor = getBaseUnitFactor(t);
19526
- return value * fFactor / tFactor;
19527
- }
19528
- if (strength && (fCat === "mass" && tCat === "volume" || fCat === "volume" && tCat === "mass")) {
19529
- const numUnit = strength.numerator.unit.toLowerCase();
19530
- const denUnit = strength.denominator.unit.toLowerCase();
19531
- const numCat = getUnitCategory(numUnit);
19532
- const denCat = getUnitCategory(denUnit);
19533
- if (numCat !== denCat && numCat !== "other" && denCat !== "other") {
19534
- const massSide = numCat === "mass" ? strength.numerator : strength.denominator;
19535
- const volSide = numCat === "volume" ? strength.numerator : strength.denominator;
19536
- const bridgeDensity = massSide.value * getBaseUnitFactor(massSide.unit) / (volSide.value * getBaseUnitFactor(volSide.unit));
19537
- if (fCat === "mass") {
19538
- const valueMg = value * getBaseUnitFactor(fromUnit);
19539
- const valueMl = valueMg / bridgeDensity;
19540
- return valueMl / getBaseUnitFactor(toUnit);
19541
- } else {
19542
- const valueMl = value * getBaseUnitFactor(fromUnit);
19543
- const valueMg = valueMl * bridgeDensity;
19544
- return valueMg / getBaseUnitFactor(toUnit);
19545
- }
19546
- }
19547
- }
19548
- return null;
19549
- }
19550
-
19551
20015
  // src/utils/strength.ts
19552
20016
  function parseStrength(strength, context) {
19553
20017
  var _a2, _b, _c;
@@ -19742,6 +20206,21 @@ function normalizeClock(clock) {
19742
20206
  }
19743
20207
  return `${pad(hour)}:${pad(minute)}:${pad(second)}`;
19744
20208
  }
20209
+ function doseUnitSupportsPerTargetMultiplication(doseUnit, context) {
20210
+ const semantics = getDoseUnitSemantics(doseUnit, context);
20211
+ if (!semantics) {
20212
+ return false;
20213
+ }
20214
+ return semantics.kind !== "metric" && semantics.kind !== "biologic_unit";
20215
+ }
20216
+ function getAdministrationTargetMultiplier(dosage, context) {
20217
+ var _a2, _b, _c, _d;
20218
+ const doseUnit = (_c = (_b = (_a2 = dosage.doseAndRate) == null ? void 0 : _a2[0]) == null ? void 0 : _b.doseQuantity) == null ? void 0 : _c.unit;
20219
+ if (!doseUnitSupportsPerTargetMultiplication(doseUnit, context)) {
20220
+ return 1;
20221
+ }
20222
+ return (_d = getBodySiteAdministrationTargetCount(dosage.site)) != null ? _d : 1;
20223
+ }
19745
20224
  function getDateTimeFormat(timeZone) {
19746
20225
  let formatter = dateTimeFormatCache.get(timeZone);
19747
20226
  if (!formatter) {
@@ -19972,6 +20451,24 @@ function estimateIngredientQuantity(quantity, context) {
19972
20451
  if (!(numerator == null ? void 0 : numerator.unit) || numerator.value === void 0 || !(denominator == null ? void 0 : denominator.unit) || denominator.value === void 0) {
19973
20452
  return void 0;
19974
20453
  }
20454
+ const quantityCategory = getUnitCategory(quantity.unit);
20455
+ const denominatorCategory = getUnitCategory(denominator.unit);
20456
+ if (quantityCategory !== "other" && quantityCategory === denominatorCategory) {
20457
+ const quantityInDenominatorBase = quantity.value * getBaseUnitFactor(quantity.unit);
20458
+ const denominatorInBase = denominator.value * getBaseUnitFactor(denominator.unit);
20459
+ const numeratorInBase = numerator.value * getBaseUnitFactor(numerator.unit);
20460
+ if (denominatorInBase !== 0) {
20461
+ return {
20462
+ value: roundCalculatedUnits(
20463
+ quantityInDenominatorBase * numeratorInBase / denominatorInBase / getBaseUnitFactor(numerator.unit)
20464
+ ),
20465
+ unit: numerator.unit,
20466
+ confidence: quantity.confidence,
20467
+ basis: quantity.basis,
20468
+ source: quantity.source
20469
+ };
20470
+ }
20471
+ }
19975
20472
  const converted = convertValue(
19976
20473
  quantity.value,
19977
20474
  quantity.unit,
@@ -21161,7 +21658,8 @@ function calculateTotalUnitsSingle(options) {
21161
21658
  2e3
21162
21659
  );
21163
21660
  const doseQuantity = (_j = (_i = (_h = (_g = dosage.doseAndRate) == null ? void 0 : _g[0]) == null ? void 0 : _h.doseQuantity) == null ? void 0 : _i.value) != null ? _j : 0;
21164
- let totalUnits = roundCalculatedUnits(count * doseQuantity);
21661
+ const targetMultiplier = getAdministrationTargetMultiplier(dosage, context);
21662
+ let totalUnits = roundCalculatedUnits(count * doseQuantity * targetMultiplier);
21165
21663
  if (roundToMultiple && roundToMultiple > 0) {
21166
21664
  totalUnits = roundCalculatedUnits(Math.ceil(totalUnits / roundToMultiple) * roundToMultiple);
21167
21665
  }
@@ -21717,7 +22215,7 @@ function cloneBodySiteCoding2(coding) {
21717
22215
  };
21718
22216
  }
21719
22217
  function buildNormalizedMetaFromClause(clause, fhir, options) {
21720
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F;
22218
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H;
21721
22219
  const additionalInstructions = ((_a2 = clause.additionalInstructions) == null ? void 0 : _a2.length) ? clause.additionalInstructions.map((instruction) => ({
21722
22220
  text: instruction.text,
21723
22221
  coding: cloneCoding2(instruction.coding)
@@ -21730,22 +22228,23 @@ function buildNormalizedMetaFromClause(clause, fhir, options) {
21730
22228
  unit: (_h = clause.dose) == null ? void 0 : _h.unit,
21731
22229
  unitKind: unitSemantics == null ? void 0 : unitSemantics.kind,
21732
22230
  unitSemantics,
21733
- site: ((_i = clause.site) == null ? void 0 : _i.text) || ((_k = (_j = clause.site) == null ? void 0 : _j.coding) == null ? void 0 : _k.code) || ((_l = clause.site) == null ? void 0 : _l.spatialRelation) ? {
21734
- text: (_m = clause.site) == null ? void 0 : _m.text,
22231
+ site: ((_i = clause.site) == null ? void 0 : _i.text) || ((_k = (_j = clause.site) == null ? void 0 : _j.coding) == null ? void 0 : _k.code) || ((_l = clause.site) == null ? void 0 : _l.spatialRelation) || ((_m = clause.site) == null ? void 0 : _m.administrationTargetCount) !== void 0 ? {
22232
+ text: (_n = clause.site) == null ? void 0 : _n.text,
21735
22233
  coding: siteCoding,
21736
- spatialRelation: cloneBodySiteSpatialRelation((_n = clause.site) == null ? void 0 : _n.spatialRelation)
22234
+ spatialRelation: cloneBodySiteSpatialRelation((_o = clause.site) == null ? void 0 : _o.spatialRelation),
22235
+ administrationTargetCount: (_p = clause.site) == null ? void 0 : _p.administrationTargetCount
21737
22236
  } : void 0,
21738
- method: ((_o = clause.method) == null ? void 0 : _o.text) || ((_q = (_p = clause.method) == null ? void 0 : _p.coding) == null ? void 0 : _q.code) ? {
21739
- text: (_r = clause.method) == null ? void 0 : _r.text,
21740
- coding: cloneCoding2((_s = clause.method) == null ? void 0 : _s.coding)
22237
+ method: ((_q = clause.method) == null ? void 0 : _q.text) || ((_s = (_r = clause.method) == null ? void 0 : _r.coding) == null ? void 0 : _s.code) ? {
22238
+ text: (_t = clause.method) == null ? void 0 : _t.text,
22239
+ coding: cloneCoding2((_u = clause.method) == null ? void 0 : _u.coding)
21741
22240
  } : void 0,
21742
22241
  patientInstruction: clause.patientInstruction,
21743
- prnReason: ((_u = (_t = clause.prn) == null ? void 0 : _t.reason) == null ? void 0 : _u.text) || ((_x = (_w = (_v = clause.prn) == null ? void 0 : _v.reason) == null ? void 0 : _w.coding) == null ? void 0 : _x.code) ? {
21744
- text: (_z = (_y = clause.prn) == null ? void 0 : _y.reason) == null ? void 0 : _z.text,
21745
- coding: cloneCoding2((_B = (_A = clause.prn) == null ? void 0 : _A.reason) == null ? void 0 : _B.coding),
21746
- spatialRelation: cloneBodySiteSpatialRelation((_D = (_C = clause.prn) == null ? void 0 : _C.reason) == null ? void 0 : _D.spatialRelation)
22242
+ prnReason: ((_w = (_v = clause.prn) == null ? void 0 : _v.reason) == null ? void 0 : _w.text) || ((_z = (_y = (_x = clause.prn) == null ? void 0 : _x.reason) == null ? void 0 : _y.coding) == null ? void 0 : _z.code) ? {
22243
+ text: (_B = (_A = clause.prn) == null ? void 0 : _A.reason) == null ? void 0 : _B.text,
22244
+ coding: cloneCoding2((_D = (_C = clause.prn) == null ? void 0 : _C.reason) == null ? void 0 : _D.coding),
22245
+ spatialRelation: cloneBodySiteSpatialRelation((_F = (_E = clause.prn) == null ? void 0 : _E.reason) == null ? void 0 : _F.spatialRelation)
21747
22246
  } : void 0,
21748
- prnReasons: ((_F = (_E = clause.prn) == null ? void 0 : _E.reasons) == null ? void 0 : _F.length) ? clause.prn.reasons.map((reason) => ({
22247
+ prnReasons: ((_H = (_G = clause.prn) == null ? void 0 : _G.reasons) == null ? void 0 : _H.length) ? clause.prn.reasons.map((reason) => ({
21749
22248
  text: reason.text,
21750
22249
  coding: cloneCoding2(reason.coding),
21751
22250
  spatialRelation: cloneBodySiteSpatialRelation(reason.spatialRelation)
@@ -21944,6 +22443,7 @@ function resolvePrimaryLintResult(results, input, options) {
21944
22443
  AdviceModality,
21945
22444
  AdvicePolarity,
21946
22445
  AdviceRelation,
22446
+ BODY_SITE_ADMINISTRATION_TARGET_COUNT_EXTENSION_URL,
21947
22447
  BODY_SITE_SPATIAL_RELATION_EXTENSION_URL,
21948
22448
  DEFAULT_BODY_SITE_SNOMED,
21949
22449
  DEFAULT_BODY_SITE_SNOMED_SOURCE,
@@ -21968,6 +22468,7 @@ function resolvePrimaryLintResult(results, input, options) {
21968
22468
  SNOMED_CT_TOPOGRAPHICAL_MODIFIER_CODE,
21969
22469
  SNOMED_CT_TOPOGRAPHICAL_MODIFIER_DISPLAY,
21970
22470
  SNOMED_SYSTEM,
22471
+ buildBodySiteAdministrationTargetCountExtension,
21971
22472
  buildBodySiteSpatialRelationExtension,
21972
22473
  buildBodySiteSpatialRelationExtensions,
21973
22474
  buildBodySiteTopographicalModifierCoding,
@@ -21981,6 +22482,7 @@ function resolvePrimaryLintResult(results, input, options) {
21981
22482
  formatSig,
21982
22483
  formatSigBatch,
21983
22484
  fromFhirDosage,
22485
+ getBodySiteAdministrationTargetCount,
21984
22486
  getBodySiteCode,
21985
22487
  getBodySiteCodeAsync,
21986
22488
  getBodySiteText,
@@ -21999,6 +22501,7 @@ function resolvePrimaryLintResult(results, input, options) {
21999
22501
  lookupBodySite,
22000
22502
  lookupBodySiteAsync,
22001
22503
  nextDueDoses,
22504
+ parseBodySiteAdministrationTargetCountExtension,
22002
22505
  parseBodySiteSpatialRelationExtension,
22003
22506
  parseSig,
22004
22507
  parseSigAsync,