ezmedicationinput 0.1.50 → 0.1.52

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"] }
@@ -4465,6 +4791,7 @@ var BODY_SITE_FEATURE_SCORE_BONUS = bodySiteFeatureScoreBonus(lexical_classes_de
4465
4791
  var CONNECTORS = setOf(lexical_classes_default.connectors);
4466
4792
  var ROUTE_SITE_PREPOSITIONS = setOf(lexical_classes_default.routeSitePrepositions);
4467
4793
  var SITE_DISPLAY_FILLERS = SITE_FILLERS;
4794
+ var SITE_MULTIPLICITY_WORDS = setOf(["both", "each", "bilateral"]);
4468
4795
  var NON_SITE_ANCHORED_PHRASES = setOf(lexical_classes_default.nonSiteAnchoredPhrases);
4469
4796
  var EXTERNAL_SITE_LOCATIVE_PREFIXES = setOf(lexical_classes_default.externalSiteLocativePrefixes);
4470
4797
  var ROUTE_BLOCKED_BY_FOLLOWING_PARTITIVE_HEADS = setOf(
@@ -4953,195 +5280,61 @@ function buildSpatialRelation(features, sourceText, customSiteMap) {
4953
5280
  case "nominal":
4954
5281
  return void 0;
4955
5282
  }
4956
- }
4957
- function inferPreferredPreposition(canonical, features, definition) {
4958
- if (features.kind === "locative") {
4959
- return void 0;
4960
- }
4961
- const routeHint = definition == null ? void 0 : definition.routeHint;
4962
- if (routeHint === RouteCode["Topical route"] || routeHint === RouteCode["Transdermal route"]) {
4963
- return "to";
4964
- }
4965
- if (routeHint === RouteCode["Per rectum"] || routeHint === RouteCode["Per vagina"] || routeHint === RouteCode["Subcutaneous route"] || routeHint === RouteCode["Intramuscular route"] || routeHint === RouteCode["Intravenous route"] || routeHint === RouteCode["Nasal route"]) {
4966
- return routeHint === RouteCode["Nasal route"] ? "into" : "to";
4967
- }
4968
- const words = canonical.split(/\s+/).filter((word) => word.length > 0);
4969
- for (const word of words) {
4970
- if (OTIC_SITE_WORDS.has(word) || OPHTHALMIC_SITE_WORDS.has(word)) {
4971
- return "in";
4972
- }
4973
- if (NASAL_SITE_WORDS.has(word)) {
4974
- return "into";
4975
- }
4976
- }
4977
- return void 0;
4978
- }
4979
- function resolveBodySitePhrase(text, customSiteMap, context) {
4980
- var _a2, _b, _c, _d, _e, _f, _g;
4981
- const trimmed = text.trim().replace(/\s+/g, " ");
4982
- if (!trimmed) {
4983
- return void 0;
4984
- }
4985
- const lookupCanonical = normalizeBodySiteKey(trimmed);
4986
- const contextualCanonical = resolveContextualBodySiteAlias(lookupCanonical, context);
4987
- const displaySourceText = contextualCanonical != null ? contextualCanonical : trimmed;
4988
- const displayText = normalizeSiteDisplayText(displaySourceText, customSiteMap);
4989
- const canonical = normalizeBodySiteKey(displayText);
4990
- const definition = (_e = (_d = (_c = (_b = (_a2 = lookupBodySiteDefinition(customSiteMap, lookupCanonical)) != null ? _a2 : contextualCanonical ? lookupBodySiteDefinition(customSiteMap, contextualCanonical) : void 0) != null ? _b : contextualCanonical ? DEFAULT_BODY_SITE_SNOMED[contextualCanonical] : void 0) != null ? _c : DEFAULT_BODY_SITE_SNOMED[lookupCanonical]) != null ? _d : lookupBodySiteDefinition(customSiteMap, canonical)) != null ? _e : DEFAULT_BODY_SITE_SNOMED[canonical];
4991
- const coding = buildBodySiteCoding(definition);
4992
- const finalDisplayText = (_f = definition == null ? void 0 : definition.text) != null ? _f : displayText;
4993
- const features = parseBodySiteFeatures(finalDisplayText, coding, customSiteMap);
4994
- const spatialRelation = (_g = definition == null ? void 0 : definition.spatialRelation) != null ? _g : buildSpatialRelation(features, finalDisplayText, customSiteMap);
4995
- return {
4996
- lookupCanonical,
4997
- resolutionCanonical: contextualCanonical != null ? contextualCanonical : lookupCanonical,
4998
- canonical: normalizeBodySiteKey(finalDisplayText) || canonical,
4999
- displayText: finalDisplayText,
5000
- coding,
5001
- spatialRelation,
5002
- definition,
5003
- features,
5004
- englishObjectText: renderBodySiteObject(features),
5005
- preferredPreposition: inferPreferredPreposition(
5006
- normalizeBodySiteKey(finalDisplayText) || canonical,
5007
- features,
5008
- definition
5009
- )
5010
- };
5011
- }
5012
-
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) {
5283
+ }
5284
+ function inferPreferredPreposition(canonical, features, definition) {
5285
+ if (features.kind === "locative") {
5125
5286
  return void 0;
5126
5287
  }
5127
- if (input.period !== void 0 || input.periodMax !== void 0 || input.periodUnit !== void 0) {
5128
- return void 0;
5288
+ const routeHint = definition == null ? void 0 : definition.routeHint;
5289
+ if (routeHint === RouteCode["Topical route"] || routeHint === RouteCode["Transdermal route"]) {
5290
+ return "to";
5129
5291
  }
5130
- if (((_b = (_a2 = input.dayOfWeek) == null ? void 0 : _a2.length) != null ? _b : 0) > 0) {
5131
- return void 0;
5292
+ if (routeHint === RouteCode["Per rectum"] || routeHint === RouteCode["Per vagina"] || routeHint === RouteCode["Subcutaneous route"] || routeHint === RouteCode["Intramuscular route"] || routeHint === RouteCode["Intravenous route"] || routeHint === RouteCode["Nasal route"]) {
5293
+ return routeHint === RouteCode["Nasal route"] ? "into" : "to";
5132
5294
  }
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;
5295
+ const words = canonical.split(/\s+/).filter((word) => word.length > 0);
5296
+ for (const word of words) {
5297
+ if (OTIC_SITE_WORDS.has(word) || OPHTHALMIC_SITE_WORDS.has(word)) {
5298
+ return "in";
5299
+ }
5300
+ if (NASAL_SITE_WORDS.has(word)) {
5301
+ return "into";
5137
5302
  }
5138
5303
  }
5139
- const uniqueTimes = uniqueValues((_d = input.timeOfDay) != null ? _d : []);
5140
- const occurrences = uniqueWhen2.length + uniqueTimes.length;
5141
- if (occurrences === 0) {
5304
+ return void 0;
5305
+ }
5306
+ function resolveBodySitePhrase(text, customSiteMap, context) {
5307
+ var _a2, _b, _c, _d, _e, _f, _g;
5308
+ const trimmed = text.trim().replace(/\s+/g, " ");
5309
+ if (!trimmed) {
5142
5310
  return void 0;
5143
5311
  }
5144
- return occurrences;
5312
+ const lookupCanonical = normalizeBodySiteKey(trimmed);
5313
+ const contextualCanonical = resolveContextualBodySiteAlias(lookupCanonical, context);
5314
+ const displaySourceText = contextualCanonical != null ? contextualCanonical : trimmed;
5315
+ const displayText = normalizeSiteDisplayText(displaySourceText, customSiteMap);
5316
+ const canonical = normalizeBodySiteKey(displayText);
5317
+ const definition = (_e = (_d = (_c = (_b = (_a2 = lookupBodySiteDefinition(customSiteMap, lookupCanonical)) != null ? _a2 : contextualCanonical ? lookupBodySiteDefinition(customSiteMap, contextualCanonical) : void 0) != null ? _b : contextualCanonical ? DEFAULT_BODY_SITE_SNOMED[contextualCanonical] : void 0) != null ? _c : DEFAULT_BODY_SITE_SNOMED[lookupCanonical]) != null ? _d : lookupBodySiteDefinition(customSiteMap, canonical)) != null ? _e : DEFAULT_BODY_SITE_SNOMED[canonical];
5318
+ const coding = buildBodySiteCoding(definition);
5319
+ const finalDisplayText = (_f = definition == null ? void 0 : definition.text) != null ? _f : displayText;
5320
+ const features = parseBodySiteFeatures(finalDisplayText, coding, customSiteMap);
5321
+ const spatialRelation = (_g = definition == null ? void 0 : definition.spatialRelation) != null ? _g : buildSpatialRelation(features, finalDisplayText, customSiteMap);
5322
+ return {
5323
+ lookupCanonical,
5324
+ resolutionCanonical: contextualCanonical != null ? contextualCanonical : lookupCanonical,
5325
+ canonical: normalizeBodySiteKey(finalDisplayText) || canonical,
5326
+ displayText: finalDisplayText,
5327
+ coding,
5328
+ spatialRelation,
5329
+ definition,
5330
+ features,
5331
+ englishObjectText: renderBodySiteObject(features),
5332
+ preferredPreposition: inferPreferredPreposition(
5333
+ normalizeBodySiteKey(finalDisplayText) || canonical,
5334
+ features,
5335
+ definition
5336
+ )
5337
+ };
5145
5338
  }
5146
5339
 
5147
5340
  // src/format.ts
@@ -5635,73 +5828,22 @@ function summarizeMealTimingGroup(group) {
5635
5828
  }
5636
5829
  return `${relationText} ${joinWithAnd(group.meals)}`;
5637
5830
  }
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;
5831
+ var EN_TIMING_GRAMMAR = {
5832
+ whenText: WHEN_TEXT,
5833
+ joinList: joinWithAnd,
5834
+ summarizeMealTimingGroup,
5835
+ bedtimeJoinStyle: (dailyCount) => {
5836
+ if (dailyCount === 1) {
5837
+ return "adjacent";
5698
5838
  }
5699
- const text = (_c = WHEN_TEXT[code]) != null ? _c : code;
5700
- if (text) {
5701
- phrases.push(text);
5839
+ if (dailyCount === 2 || dailyCount === 3 || dailyCount === 4) {
5840
+ return "conjunction";
5702
5841
  }
5842
+ return "separate";
5703
5843
  }
5704
- return phrases;
5844
+ };
5845
+ function collectWhenPhrases(schedule, options) {
5846
+ return collectLocalizedWhenPhrases(schedule, EN_TIMING_GRAMMAR, options);
5705
5847
  }
5706
5848
  function joinWithAnd(parts) {
5707
5849
  if (!parts.length) {
@@ -5715,23 +5857,14 @@ function joinWithAnd(parts) {
5715
5857
  }
5716
5858
  return `${parts.slice(0, -1).join(", ")} and ${parts[parts.length - 1]}`;
5717
5859
  }
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) };
5860
+ function combineFrequencyAndEvents(schedule, frequency, events, options) {
5861
+ return combineLocalizedFrequencyAndEvents(
5862
+ schedule,
5863
+ frequency,
5864
+ events,
5865
+ EN_TIMING_GRAMMAR,
5866
+ options
5867
+ );
5735
5868
  }
5736
5869
  function buildRoutePhrase(clause, grammar, hasSite) {
5737
5870
  var _a2, _b;
@@ -6036,7 +6169,7 @@ function formatLong(clause, options) {
6036
6169
  eventParts.push(`at ${timeStrings.join(", ")}`);
6037
6170
  }
6038
6171
  }
6039
- const timing = combineFrequencyAndEvents(frequencyPart, eventParts);
6172
+ const timing = combineFrequencyAndEvents(schedule, frequencyPart, eventParts, options);
6040
6173
  const dayPart = describeDayOfWeek(schedule);
6041
6174
  const countPart = schedule.count !== void 0 && !standaloneOccurrenceCount ? `for ${stripTrailingZero(schedule.count)} ${schedule.count === 1 ? "dose" : "doses"}` : void 0;
6042
6175
  const durationPart = describeDuration(schedule);
@@ -9831,6 +9964,81 @@ function listSupportedBodySiteGrammar() {
9831
9964
  };
9832
9965
  }
9833
9966
 
9967
+ // src/body-site-target.ts
9968
+ var BODY_SITE_ADMINISTRATION_TARGET_COUNT_EXTENSION_URL = "urn:ezmedicationinput:body-site-administration-target-count";
9969
+ function normalizeAdministrationTargetCount(value) {
9970
+ if (!Number.isFinite(value) || value === void 0) {
9971
+ return void 0;
9972
+ }
9973
+ const rounded = Math.trunc(value);
9974
+ return rounded >= 2 ? rounded : void 0;
9975
+ }
9976
+ function siteTextFromLike(site, options) {
9977
+ if (!site) {
9978
+ return void 0;
9979
+ }
9980
+ if (typeof site === "string") {
9981
+ return site;
9982
+ }
9983
+ if ("text" in site && site.text) {
9984
+ return site.text;
9985
+ }
9986
+ const coding = "coding" in site ? Array.isArray(site.coding) ? site.coding.find((candidate) => candidate == null ? void 0 : candidate.code) : site.coding : void 0;
9987
+ return (coding == null ? void 0 : coding.code) ? getBodySiteText(
9988
+ {
9989
+ system: coding.system,
9990
+ code: coding.code,
9991
+ display: coding.display
9992
+ },
9993
+ { siteCodeMap: options == null ? void 0 : options.siteCodeMap }
9994
+ ) : void 0;
9995
+ }
9996
+ function buildBodySiteAdministrationTargetCountExtension(targetCount) {
9997
+ const normalized = normalizeAdministrationTargetCount(targetCount);
9998
+ if (normalized === void 0) {
9999
+ return void 0;
10000
+ }
10001
+ return {
10002
+ url: BODY_SITE_ADMINISTRATION_TARGET_COUNT_EXTENSION_URL,
10003
+ valueInteger: normalized
10004
+ };
10005
+ }
10006
+ function parseBodySiteAdministrationTargetCountExtension(concept) {
10007
+ var _a2;
10008
+ const extension = (_a2 = concept == null ? void 0 : concept.extension) == null ? void 0 : _a2.find(
10009
+ (candidate) => candidate.url === BODY_SITE_ADMINISTRATION_TARGET_COUNT_EXTENSION_URL
10010
+ );
10011
+ return normalizeAdministrationTargetCount(extension == null ? void 0 : extension.valueInteger);
10012
+ }
10013
+ function getBodySiteAdministrationTargetCount(site, options) {
10014
+ var _a2;
10015
+ if (!site) {
10016
+ return void 0;
10017
+ }
10018
+ if (typeof site !== "string" && "administrationTargetCount" in site) {
10019
+ const direct = normalizeAdministrationTargetCount(site.administrationTargetCount);
10020
+ if (direct !== void 0) {
10021
+ return direct;
10022
+ }
10023
+ }
10024
+ if (typeof site !== "string") {
10025
+ if ("extension" in site) {
10026
+ const fromExtension = parseBodySiteAdministrationTargetCountExtension(site);
10027
+ if (fromExtension !== void 0) {
10028
+ return fromExtension;
10029
+ }
10030
+ }
10031
+ }
10032
+ const text = siteTextFromLike(site, options);
10033
+ if (!text) {
10034
+ return void 0;
10035
+ }
10036
+ const resolved = resolveBodySitePhrase(text, options == null ? void 0 : options.siteCodeMap, {
10037
+ bodySiteContext: options == null ? void 0 : options.bodySiteContext
10038
+ });
10039
+ return normalizeAdministrationTargetCount((_a2 = resolved == null ? void 0 : resolved.definition) == null ? void 0 : _a2.administrationTargetCount);
10040
+ }
10041
+
9834
10042
  // src/parser-state.ts
9835
10043
  var ParserState = class {
9836
10044
  constructor(input, tokens, customSiteHints) {
@@ -10201,6 +10409,20 @@ var ParserState = class {
10201
10409
  sourceText: value.sourceText
10202
10410
  };
10203
10411
  }
10412
+ get siteAdministrationTargetCount() {
10413
+ var _a2;
10414
+ return (_a2 = this.clause.site) == null ? void 0 : _a2.administrationTargetCount;
10415
+ }
10416
+ set siteAdministrationTargetCount(value) {
10417
+ if (value === void 0) {
10418
+ if (this.clause.site) {
10419
+ delete this.clause.site.administrationTargetCount;
10420
+ this.cleanupSite();
10421
+ }
10422
+ return;
10423
+ }
10424
+ this.ensureSite().administrationTargetCount = value;
10425
+ }
10204
10426
  get additionalInstructions() {
10205
10427
  if (!this.clause.additionalInstructions) {
10206
10428
  this.clause.additionalInstructions = [];
@@ -10273,7 +10495,7 @@ var ParserState = class {
10273
10495
  if (!site) {
10274
10496
  return;
10275
10497
  }
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) {
10498
+ 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
10499
  delete this.clause.site;
10278
10500
  }
10279
10501
  }
@@ -10634,7 +10856,7 @@ function appendWarning(warnings, warning) {
10634
10856
  return warnings;
10635
10857
  }
10636
10858
  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;
10859
+ 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
10860
  const dosage = {};
10639
10861
  const repeat = {};
10640
10862
  let hasRepeat = false;
@@ -10735,20 +10957,35 @@ function canonicalToFhir(clause, textOverride, options) {
10735
10957
  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
10958
  const siteCoding = selectCanonicalSiteCoding(clause.site, options);
10737
10959
  const siteTextElement = (options == null ? void 0 : options.includeTranslationExtensions) ? buildTranslationPrimitiveElement((_p = clause.site.i18n) != null ? _p : siteCoding == null ? void 0 : siteCoding.i18n) : void 0;
10960
+ const siteTargetCount = (_r = (_q = clause.site) == null ? void 0 : _q.administrationTargetCount) != null ? _r : getBodySiteAdministrationTargetCount(clause.site);
10961
+ const inferredSiteTargetCount = getBodySiteAdministrationTargetCount({
10962
+ text: (_s = clause.site) == null ? void 0 : _s.text,
10963
+ coding: siteCoding ? {
10964
+ code: siteCoding.code,
10965
+ display: siteCoding.display,
10966
+ system: siteCoding.system,
10967
+ i18n: siteCoding.i18n
10968
+ } : void 0
10969
+ });
10970
+ const siteTargetCountExtension = siteTargetCount !== void 0 && siteTargetCount !== inferredSiteTargetCount ? buildBodySiteAdministrationTargetCountExtension(siteTargetCount) : void 0;
10971
+ const siteExtensions = [
10972
+ ...(_u = buildBodySiteSpatialRelationExtensions((_t = clause.site) == null ? void 0 : _t.spatialRelation)) != null ? _u : [],
10973
+ ...siteTargetCountExtension ? [siteTargetCountExtension] : []
10974
+ ];
10738
10975
  dosage.site = __spreadProps(__spreadValues({
10739
- text: (_q = clause.site) == null ? void 0 : _q.text
10976
+ text: (_v = clause.site) == null ? void 0 : _v.text
10740
10977
  }, siteTextElement ? { _text: siteTextElement } : {}), {
10741
10978
  coding: buildSiteCodingArray(siteCoding, options),
10742
- extension: buildBodySiteSpatialRelationExtensions((_r = clause.site) == null ? void 0 : _r.spatialRelation)
10979
+ extension: siteExtensions.length ? siteExtensions : void 0
10743
10980
  });
10744
10981
  }
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)) {
10982
+ 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
10983
  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) ? [
10984
+ text: (_A = clause.method) == null ? void 0 : _A.text,
10985
+ _text: clonePrimitiveElement((_B = clause.method) == null ? void 0 : _B._text),
10986
+ coding: ((_D = (_C = clause.method) == null ? void 0 : _C.coding) == null ? void 0 : _D.code) ? [
10750
10987
  {
10751
- system: (_A = clause.method.coding.system) != null ? _A : SNOMED_SYSTEM5,
10988
+ system: (_E = clause.method.coding.system) != null ? _E : SNOMED_SYSTEM5,
10752
10989
  code: clause.method.coding.code,
10753
10990
  display: clause.method.coding.display,
10754
10991
  _display: clonePrimitiveElement(clause.method.coding._display)
@@ -10756,14 +10993,14 @@ function canonicalToFhir(clause, textOverride, options) {
10756
10993
  ] : void 0
10757
10994
  };
10758
10995
  }
10759
- if ((_B = clause.additionalInstructions) == null ? void 0 : _B.length) {
10996
+ if ((_F = clause.additionalInstructions) == null ? void 0 : _F.length) {
10760
10997
  dosage.additionalInstruction = [];
10761
10998
  for (const instruction of clause.additionalInstructions) {
10762
10999
  dosage.additionalInstruction.push({
10763
11000
  text: instruction.text,
10764
- coding: ((_C = instruction.coding) == null ? void 0 : _C.code) ? [
11001
+ coding: ((_G = instruction.coding) == null ? void 0 : _G.code) ? [
10765
11002
  {
10766
- system: (_D = instruction.coding.system) != null ? _D : SNOMED_SYSTEM5,
11003
+ system: (_H = instruction.coding.system) != null ? _H : SNOMED_SYSTEM5,
10767
11004
  code: instruction.coding.code,
10768
11005
  display: instruction.coding.display
10769
11006
  }
@@ -10771,9 +11008,9 @@ function canonicalToFhir(clause, textOverride, options) {
10771
11008
  });
10772
11009
  }
10773
11010
  }
10774
- if ((_E = clause.prn) == null ? void 0 : _E.enabled) {
11011
+ if ((_I = clause.prn) == null ? void 0 : _I.enabled) {
10775
11012
  dosage.asNeededBoolean = true;
10776
- const reasons = ((_F = clause.prn.reasons) == null ? void 0 : _F.length) ? clause.prn.reasons : clause.prn.reason ? [clause.prn.reason] : [];
11013
+ const reasons = ((_J = clause.prn.reasons) == null ? void 0 : _J.length) ? clause.prn.reasons : clause.prn.reason ? [clause.prn.reason] : [];
10777
11014
  if (reasons.length) {
10778
11015
  dosage.asNeededFor = [];
10779
11016
  for (const reason of reasons) {
@@ -10781,18 +11018,18 @@ function canonicalToFhir(clause, textOverride, options) {
10781
11018
  if (reason.text) {
10782
11019
  concept.text = reason.text;
10783
11020
  }
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;
11021
+ 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
11022
  if (reasonTextElement) {
10786
11023
  concept._text = reasonTextElement;
10787
11024
  }
10788
- if ((_I = reason.coding) == null ? void 0 : _I.code) {
11025
+ if ((_M = reason.coding) == null ? void 0 : _M.code) {
10789
11026
  const displayElement = (options == null ? void 0 : options.includeTranslationExtensions) ? mergeTranslationPrimitiveElement(
10790
11027
  reason.coding._display,
10791
11028
  reason.coding.i18n
10792
11029
  ) : clonePrimitiveElement(reason.coding._display);
10793
11030
  concept.coding = [
10794
11031
  __spreadProps(__spreadValues({
10795
- system: (_J = reason.coding.system) != null ? _J : SNOMED_SYSTEM5,
11032
+ system: (_N = reason.coding.system) != null ? _N : SNOMED_SYSTEM5,
10796
11033
  code: reason.coding.code,
10797
11034
  display: reason.coding.display
10798
11035
  }, displayElement ? { _display: displayElement } : {}), {
@@ -10833,11 +11070,13 @@ function canonicalFromFhir(dosage) {
10833
11070
  const siteSpatialRelation = parseBodySiteSpatialRelationExtension(dosage.site);
10834
11071
  const siteText = getFallbackSiteText(dosage.site);
10835
11072
  const siteI18n = codeableConceptTranslationI18n(dosage.site, siteCoding);
10836
- if (siteText || (siteCoding == null ? void 0 : siteCoding.code) || siteSpatialRelation) {
11073
+ const siteAdministrationTargetCount = getBodySiteAdministrationTargetCount(dosage.site);
11074
+ if (siteText || (siteCoding == null ? void 0 : siteCoding.code) || siteSpatialRelation || siteAdministrationTargetCount !== void 0) {
10837
11075
  clause.site = {
10838
11076
  text: siteText,
10839
11077
  i18n: siteI18n.text,
10840
11078
  spatialRelation: siteSpatialRelation,
11079
+ administrationTargetCount: siteAdministrationTargetCount,
10841
11080
  coding: (siteCoding == null ? void 0 : siteCoding.code) ? {
10842
11081
  code: siteCoding.code,
10843
11082
  display: siteCoding.display,
@@ -11918,73 +12157,22 @@ function summarizeMealTimingGroupThai(group) {
11918
12157
  }
11919
12158
  return `${relationText[group.relation]}${joinMealNamesThai(meals)}`;
11920
12159
  }
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;
12160
+ var TH_TIMING_GRAMMAR = {
12161
+ whenText: WHEN_TEXT_THAI,
12162
+ joinList: joinWithAndThai,
12163
+ summarizeMealTimingGroup: summarizeMealTimingGroupThai,
12164
+ bedtimeJoinStyle: (dailyCount) => {
12165
+ if (dailyCount === 1) {
12166
+ return "adjacent";
11981
12167
  }
11982
- const text = WHEN_TEXT_THAI[code];
11983
- if (text) {
11984
- phrases.push(text);
12168
+ if (dailyCount === 2 || dailyCount === 3 || dailyCount === 4) {
12169
+ return "conjunction";
11985
12170
  }
12171
+ return "separate";
11986
12172
  }
11987
- return phrases;
12173
+ };
12174
+ function collectWhenPhrasesThai(schedule, options) {
12175
+ return collectLocalizedWhenPhrases(schedule, TH_TIMING_GRAMMAR, options);
11988
12176
  }
11989
12177
  function joinWithAndThai(parts) {
11990
12178
  if (!parts.length) {
@@ -11998,20 +12186,14 @@ function joinWithAndThai(parts) {
11998
12186
  }
11999
12187
  return `${parts.slice(0, -1).join(", ")} \u0E41\u0E25\u0E30 ${parts[parts.length - 1]}`;
12000
12188
  }
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) };
12189
+ function combineFrequencyAndEventsThai(schedule, frequency, events, options) {
12190
+ return combineLocalizedFrequencyAndEvents(
12191
+ schedule,
12192
+ frequency,
12193
+ events,
12194
+ TH_TIMING_GRAMMAR,
12195
+ options
12196
+ );
12015
12197
  }
12016
12198
  function isOralRouteThai(clause) {
12017
12199
  var _a2, _b, _c;
@@ -12070,7 +12252,7 @@ function buildRoutePhraseThai(clause, grammar, hasSite) {
12070
12252
  return text;
12071
12253
  }
12072
12254
  function formatSiteThai(clause, grammar) {
12073
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
12255
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
12074
12256
  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
12257
  const lower = text == null ? void 0 : text.toLowerCase();
12076
12258
  const codingCode = (_g = (_f = clause.site) == null ? void 0 : _f.coding) == null ? void 0 : _g.code;
@@ -12085,14 +12267,14 @@ function formatSiteThai(clause, grammar) {
12085
12267
  if (isVaginalRoute && isVaginaSite) {
12086
12268
  return void 0;
12087
12269
  }
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);
12270
+ 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
12271
  if (!translated) {
12090
12272
  return void 0;
12091
12273
  }
12092
- if (((_n = clause.route) == null ? void 0 : _n.code) === RouteCode["Nasal route"]) {
12274
+ if (((_u = clause.route) == null ? void 0 : _u.code) === RouteCode["Nasal route"]) {
12093
12275
  return `\u0E40\u0E02\u0E49\u0E32${translated}`;
12094
12276
  }
12095
- const preposition = (_o = grammar.sitePreposition) != null ? _o : "\u0E17\u0E35\u0E48";
12277
+ const preposition = (_v = grammar.sitePreposition) != null ? _v : "\u0E17\u0E35\u0E48";
12096
12278
  const separator = /^[\u0E00-\u0E7F]/.test(translated) ? "" : " ";
12097
12279
  return `${preposition}${separator}${translated}`.trim();
12098
12280
  }
@@ -12390,7 +12572,7 @@ function formatLongThai(clause, options) {
12390
12572
  eventParts.push(`\u0E40\u0E27\u0E25\u0E32 ${timeStrings.join(", ")}`);
12391
12573
  }
12392
12574
  }
12393
- const timing = combineFrequencyAndEventsThai(frequencyPart, eventParts);
12575
+ const timing = combineFrequencyAndEventsThai(schedule, frequencyPart, eventParts, options);
12394
12576
  const dayPart = describeDayOfWeekThai(schedule);
12395
12577
  const countPart = schedule.count !== void 0 && !standaloneOccurrenceCount ? `\u0E08\u0E33\u0E19\u0E27\u0E19 ${stripTrailingZero2(schedule.count)} \u0E04\u0E23\u0E31\u0E49\u0E07` : void 0;
12396
12578
  const durationPart = describeDurationThai(schedule);
@@ -12822,6 +13004,12 @@ function sameOptionalScalar(left, right) {
12822
13004
  function mergeOptionalScalar(left, right) {
12823
13005
  return left !== void 0 ? left : right;
12824
13006
  }
13007
+ function mergeI18nRecords2(left, right) {
13008
+ if (!left && !right) {
13009
+ return void 0;
13010
+ }
13011
+ return __spreadValues(__spreadValues({}, left != null ? left : {}), right != null ? right : {});
13012
+ }
12825
13013
  function sameCoding(left, right) {
12826
13014
  var _a2, _b;
12827
13015
  if (!(left == null ? void 0 : left.code) || !(right == null ? void 0 : right.code)) {
@@ -12884,6 +13072,7 @@ function mergeSite(left, right, context) {
12884
13072
  }
12885
13073
  return {
12886
13074
  text: mergeOptionalScalar(left.text, right.text),
13075
+ i18n: mergeI18nRecords2(left.i18n, right.i18n),
12887
13076
  source: mergeOptionalScalar(left.source, right.source),
12888
13077
  coding: mergeOptionalScalar(left.coding, right.coding),
12889
13078
  spatialRelation: mergeOptionalScalar(left.spatialRelation, right.spatialRelation),
@@ -13231,6 +13420,7 @@ var TOKEN_SITE_CANDIDATES = {
13231
13420
  le: [{ text: "left eye", route: RouteCode["Ophthalmic route"], source: "abbreviation" }],
13232
13421
  ou: [{ text: "both eyes", route: RouteCode["Ophthalmic route"], source: "abbreviation" }],
13233
13422
  be: [{ text: "both eyes", route: RouteCode["Ophthalmic route"], source: "abbreviation" }],
13423
+ au: [{ text: "both ears", route: RouteCode["Otic route"], source: "abbreviation" }],
13234
13424
  vod: [
13235
13425
  {
13236
13426
  text: "right eye",
@@ -15780,6 +15970,9 @@ function inferRouteFromContext(ctx) {
15780
15970
  // src/hpsg/rules/site-rules.ts
15781
15971
  function siteBoundary(lower, context) {
15782
15972
  var _a2, _b, _c;
15973
+ if (SITE_MULTIPLICITY_WORDS.has(lower)) {
15974
+ return false;
15975
+ }
15783
15976
  const siteLike = Boolean(resolveBodySitePhrase(lower, (_a2 = context.options) == null ? void 0 : _a2.siteCodeMap, {
15784
15977
  bodySiteContext: (_c = (_b = context.options) == null ? void 0 : _b.context) == null ? void 0 : _c.bodySiteContext
15785
15978
  }));
@@ -15841,7 +16034,7 @@ function shouldSuppressEyeSiteAbbreviation(context, start, lower) {
15841
16034
  }
15842
16035
  function siteLexicalRule() {
15843
16036
  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;
16037
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
15845
16038
  const token = (_a2 = tokensAvailable(context, start, 1)) == null ? void 0 : _a2[0];
15846
16039
  if (!token) {
15847
16040
  return [];
@@ -15907,7 +16100,7 @@ function siteLexicalRule() {
15907
16100
  continue;
15908
16101
  }
15909
16102
  phraseTokens.push(candidate);
15910
- if (!SITE_DISPLAY_FILLERS.has(candidateLower)) {
16103
+ if (!SITE_DISPLAY_FILLERS.has(candidateLower) || SITE_MULTIPLICITY_WORDS.has(candidateLower)) {
15911
16104
  displayTokens.push(candidate);
15912
16105
  }
15913
16106
  }
@@ -15953,6 +16146,7 @@ function siteLexicalRule() {
15953
16146
  valence: {
15954
16147
  site: {
15955
16148
  text: displayText,
16149
+ i18n: (_r = resolved == null ? void 0 : resolved.definition) == null ? void 0 : _r.i18n,
15956
16150
  source: "text",
15957
16151
  coding: resolved == null ? void 0 : resolved.coding,
15958
16152
  spatialRelation: resolved == null ? void 0 : resolved.spatialRelation,
@@ -15980,7 +16174,7 @@ function siteLexicalRule() {
15980
16174
  }
15981
16175
  function bareSiteLexicalRule() {
15982
16176
  return lexicalRule("hpsg.lex.site.bare", (context, start) => {
15983
- var _a2, _b, _c, _d, _e, _f, _g, _h;
16177
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i;
15984
16178
  const signs = [];
15985
16179
  const first = (_a2 = tokensAvailable(context, start, 1)) == null ? void 0 : _a2[0];
15986
16180
  if (!first) {
@@ -16026,6 +16220,7 @@ function bareSiteLexicalRule() {
16026
16220
  valence: {
16027
16221
  site: {
16028
16222
  text: displayText,
16223
+ i18n: (_h = resolved.definition) == null ? void 0 : _h.i18n,
16029
16224
  source: "text",
16030
16225
  coding: resolved.coding,
16031
16226
  spatialRelation: resolved.spatialRelation,
@@ -16033,7 +16228,7 @@ function bareSiteLexicalRule() {
16033
16228
  originalText,
16034
16229
  text: sourceText,
16035
16230
  normalized: sourceText.toLowerCase(),
16036
- canonical: (_h = resolved.resolutionCanonical) != null ? _h : normalizeBodySiteKey(displayText),
16231
+ canonical: (_i = resolved.resolutionCanonical) != null ? _i : normalizeBodySiteKey(displayText),
16037
16232
  isProbe,
16038
16233
  inputText: context.state.input,
16039
16234
  sourceText,
@@ -18060,6 +18255,9 @@ function applySiteDefinition(internal, definition) {
18060
18255
  } else if ((_b = internal.siteLookupRequest) == null ? void 0 : _b.text) {
18061
18256
  internal.siteText = internal.siteLookupRequest.text;
18062
18257
  }
18258
+ if (definition.administrationTargetCount !== void 0) {
18259
+ internal.siteAdministrationTargetCount = definition.administrationTargetCount;
18260
+ }
18063
18261
  if (definition.spatialRelation) {
18064
18262
  internal.siteSpatialRelation = definition.spatialRelation;
18065
18263
  } else if ((_c = internal.siteLookupRequest) == null ? void 0 : _c.spatialRelation) {
@@ -18412,6 +18610,9 @@ function seedKnownSiteCoding(state) {
18412
18610
  display: definition.coding.display,
18413
18611
  i18n: mergeI18nRecords(definition.i18n, definition.coding.i18n)
18414
18612
  };
18613
+ if (state.siteAdministrationTargetCount === void 0) {
18614
+ state.siteAdministrationTargetCount = definition.administrationTargetCount;
18615
+ }
18415
18616
  }
18416
18617
  function parseClauseState(input, options) {
18417
18618
  const tokens = tokenize(input);
@@ -19742,6 +19943,21 @@ function normalizeClock(clock) {
19742
19943
  }
19743
19944
  return `${pad(hour)}:${pad(minute)}:${pad(second)}`;
19744
19945
  }
19946
+ function doseUnitSupportsPerTargetMultiplication(doseUnit, context) {
19947
+ const semantics = getDoseUnitSemantics(doseUnit, context);
19948
+ if (!semantics) {
19949
+ return false;
19950
+ }
19951
+ return semantics.kind !== "metric" && semantics.kind !== "biologic_unit";
19952
+ }
19953
+ function getAdministrationTargetMultiplier(dosage, context) {
19954
+ var _a2, _b, _c, _d;
19955
+ const doseUnit = (_c = (_b = (_a2 = dosage.doseAndRate) == null ? void 0 : _a2[0]) == null ? void 0 : _b.doseQuantity) == null ? void 0 : _c.unit;
19956
+ if (!doseUnitSupportsPerTargetMultiplication(doseUnit, context)) {
19957
+ return 1;
19958
+ }
19959
+ return (_d = getBodySiteAdministrationTargetCount(dosage.site)) != null ? _d : 1;
19960
+ }
19745
19961
  function getDateTimeFormat(timeZone) {
19746
19962
  let formatter = dateTimeFormatCache.get(timeZone);
19747
19963
  if (!formatter) {
@@ -21161,7 +21377,8 @@ function calculateTotalUnitsSingle(options) {
21161
21377
  2e3
21162
21378
  );
21163
21379
  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);
21380
+ const targetMultiplier = getAdministrationTargetMultiplier(dosage, context);
21381
+ let totalUnits = roundCalculatedUnits(count * doseQuantity * targetMultiplier);
21165
21382
  if (roundToMultiple && roundToMultiple > 0) {
21166
21383
  totalUnits = roundCalculatedUnits(Math.ceil(totalUnits / roundToMultiple) * roundToMultiple);
21167
21384
  }
@@ -21717,7 +21934,7 @@ function cloneBodySiteCoding2(coding) {
21717
21934
  };
21718
21935
  }
21719
21936
  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;
21937
+ 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
21938
  const additionalInstructions = ((_a2 = clause.additionalInstructions) == null ? void 0 : _a2.length) ? clause.additionalInstructions.map((instruction) => ({
21722
21939
  text: instruction.text,
21723
21940
  coding: cloneCoding2(instruction.coding)
@@ -21730,22 +21947,23 @@ function buildNormalizedMetaFromClause(clause, fhir, options) {
21730
21947
  unit: (_h = clause.dose) == null ? void 0 : _h.unit,
21731
21948
  unitKind: unitSemantics == null ? void 0 : unitSemantics.kind,
21732
21949
  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,
21950
+ 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 ? {
21951
+ text: (_n = clause.site) == null ? void 0 : _n.text,
21735
21952
  coding: siteCoding,
21736
- spatialRelation: cloneBodySiteSpatialRelation((_n = clause.site) == null ? void 0 : _n.spatialRelation)
21953
+ spatialRelation: cloneBodySiteSpatialRelation((_o = clause.site) == null ? void 0 : _o.spatialRelation),
21954
+ administrationTargetCount: (_p = clause.site) == null ? void 0 : _p.administrationTargetCount
21737
21955
  } : 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)
21956
+ method: ((_q = clause.method) == null ? void 0 : _q.text) || ((_s = (_r = clause.method) == null ? void 0 : _r.coding) == null ? void 0 : _s.code) ? {
21957
+ text: (_t = clause.method) == null ? void 0 : _t.text,
21958
+ coding: cloneCoding2((_u = clause.method) == null ? void 0 : _u.coding)
21741
21959
  } : void 0,
21742
21960
  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)
21961
+ 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) ? {
21962
+ text: (_B = (_A = clause.prn) == null ? void 0 : _A.reason) == null ? void 0 : _B.text,
21963
+ coding: cloneCoding2((_D = (_C = clause.prn) == null ? void 0 : _C.reason) == null ? void 0 : _D.coding),
21964
+ spatialRelation: cloneBodySiteSpatialRelation((_F = (_E = clause.prn) == null ? void 0 : _E.reason) == null ? void 0 : _F.spatialRelation)
21747
21965
  } : void 0,
21748
- prnReasons: ((_F = (_E = clause.prn) == null ? void 0 : _E.reasons) == null ? void 0 : _F.length) ? clause.prn.reasons.map((reason) => ({
21966
+ prnReasons: ((_H = (_G = clause.prn) == null ? void 0 : _G.reasons) == null ? void 0 : _H.length) ? clause.prn.reasons.map((reason) => ({
21749
21967
  text: reason.text,
21750
21968
  coding: cloneCoding2(reason.coding),
21751
21969
  spatialRelation: cloneBodySiteSpatialRelation(reason.spatialRelation)
@@ -21944,6 +22162,7 @@ function resolvePrimaryLintResult(results, input, options) {
21944
22162
  AdviceModality,
21945
22163
  AdvicePolarity,
21946
22164
  AdviceRelation,
22165
+ BODY_SITE_ADMINISTRATION_TARGET_COUNT_EXTENSION_URL,
21947
22166
  BODY_SITE_SPATIAL_RELATION_EXTENSION_URL,
21948
22167
  DEFAULT_BODY_SITE_SNOMED,
21949
22168
  DEFAULT_BODY_SITE_SNOMED_SOURCE,
@@ -21968,6 +22187,7 @@ function resolvePrimaryLintResult(results, input, options) {
21968
22187
  SNOMED_CT_TOPOGRAPHICAL_MODIFIER_CODE,
21969
22188
  SNOMED_CT_TOPOGRAPHICAL_MODIFIER_DISPLAY,
21970
22189
  SNOMED_SYSTEM,
22190
+ buildBodySiteAdministrationTargetCountExtension,
21971
22191
  buildBodySiteSpatialRelationExtension,
21972
22192
  buildBodySiteSpatialRelationExtensions,
21973
22193
  buildBodySiteTopographicalModifierCoding,
@@ -21981,6 +22201,7 @@ function resolvePrimaryLintResult(results, input, options) {
21981
22201
  formatSig,
21982
22202
  formatSigBatch,
21983
22203
  fromFhirDosage,
22204
+ getBodySiteAdministrationTargetCount,
21984
22205
  getBodySiteCode,
21985
22206
  getBodySiteCodeAsync,
21986
22207
  getBodySiteText,
@@ -21999,6 +22220,7 @@ function resolvePrimaryLintResult(results, input, options) {
21999
22220
  lookupBodySite,
22000
22221
  lookupBodySiteAsync,
22001
22222
  nextDueDoses,
22223
+ parseBodySiteAdministrationTargetCountExtension,
22002
22224
  parseBodySiteSpatialRelationExtension,
22003
22225
  parseSig,
22004
22226
  parseSigAsync,