ezmedicationinput 0.1.49 → 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.js CHANGED
@@ -38,60 +38,6 @@ var __async = (__this, __arguments, generator) => {
38
38
  });
39
39
  };
40
40
 
41
- // src/prn.ts
42
- function getCanonicalPrnReasonText(reason) {
43
- var _a2, _b;
44
- return (_b = reason == null ? void 0 : reason.text) != null ? _b : (_a2 = reason == null ? void 0 : reason.coding) == null ? void 0 : _a2.display;
45
- }
46
- function joinCanonicalPrnReasonTexts(reasons, conjunction = "or") {
47
- var _a2;
48
- if (!(reasons == null ? void 0 : reasons.length)) {
49
- return void 0;
50
- }
51
- const texts = [];
52
- for (const reason of reasons) {
53
- const text = (_a2 = getCanonicalPrnReasonText(reason)) == null ? void 0 : _a2.trim();
54
- if (!text) {
55
- continue;
56
- }
57
- texts.push(text);
58
- }
59
- switch (texts.length) {
60
- case 0:
61
- return void 0;
62
- case 1:
63
- return texts[0];
64
- case 2:
65
- return `${texts[0]} ${conjunction} ${texts[1]}`;
66
- default: {
67
- let combined = "";
68
- for (let index = 0; index < texts.length; index += 1) {
69
- if (index === 0) {
70
- combined = texts[index];
71
- continue;
72
- }
73
- if (index === texts.length - 1) {
74
- combined += ` ${conjunction} ${texts[index]}`;
75
- continue;
76
- }
77
- combined += `, ${texts[index]}`;
78
- }
79
- return combined;
80
- }
81
- }
82
- }
83
- function getPreferredCanonicalPrnReasonText(reason, reasons, conjunction = "or") {
84
- var _a2;
85
- const direct = (_a2 = getCanonicalPrnReasonText(reason)) == null ? void 0 : _a2.trim();
86
- if (!(reasons == null ? void 0 : reasons.length)) {
87
- return direct;
88
- }
89
- if (!direct) {
90
- return joinCanonicalPrnReasonTexts(reasons, conjunction);
91
- }
92
- return /[,/;]/.test(direct) || /\b(?:or|and|and\/or)\b/i.test(direct) || /\s(?:หรือ|และ)\s/.test(direct) ? joinCanonicalPrnReasonTexts(reasons, conjunction) : direct;
93
- }
94
-
95
41
  // src/types.ts
96
42
  var EventTiming = /* @__PURE__ */ ((EventTiming4) => {
97
43
  EventTiming4["Before Sleep"] = "HS";
@@ -359,6 +305,328 @@ var AdviceArgumentRole = /* @__PURE__ */ ((AdviceArgumentRole2) => {
359
305
  return AdviceArgumentRole2;
360
306
  })(AdviceArgumentRole || {});
361
307
 
308
+ // src/timing-summary.ts
309
+ var MEAL_TIMING_DETAILS = {
310
+ ["ACM" /* Before Breakfast */]: { relation: "before", meal: "breakfast" },
311
+ ["ACD" /* Before Lunch */]: { relation: "before", meal: "lunch" },
312
+ ["ACV" /* Before Dinner */]: { relation: "before", meal: "dinner" },
313
+ ["PCM" /* After Breakfast */]: { relation: "after", meal: "breakfast" },
314
+ ["PCD" /* After Lunch */]: { relation: "after", meal: "lunch" },
315
+ ["PCV" /* After Dinner */]: { relation: "after", meal: "dinner" },
316
+ ["CM" /* Breakfast */]: { relation: "with", meal: "breakfast" },
317
+ ["CD" /* Lunch */]: { relation: "with", meal: "lunch" },
318
+ ["CV" /* Dinner */]: { relation: "with", meal: "dinner" }
319
+ };
320
+ var MEAL_ORDER = {
321
+ breakfast: 0,
322
+ lunch: 1,
323
+ dinner: 2
324
+ };
325
+ var INFERABLE_DAILY_EVENT_TIMINGS = /* @__PURE__ */ new Set([
326
+ "HS" /* Before Sleep */,
327
+ "ACM" /* Before Breakfast */,
328
+ "ACD" /* Before Lunch */,
329
+ "ACV" /* Before Dinner */,
330
+ "PCM" /* After Breakfast */,
331
+ "PCD" /* After Lunch */,
332
+ "PCV" /* After Dinner */,
333
+ "CM" /* Breakfast */,
334
+ "CD" /* Lunch */,
335
+ "CV" /* Dinner */,
336
+ "MORN" /* Morning */,
337
+ "MORN.early" /* Early Morning */,
338
+ "MORN.late" /* Late Morning */,
339
+ "NOON" /* Noon */,
340
+ "AFT" /* Afternoon */,
341
+ "AFT.early" /* Early Afternoon */,
342
+ "AFT.late" /* Late Afternoon */,
343
+ "EVE" /* Evening */,
344
+ "EVE.early" /* Early Evening */,
345
+ "EVE.late" /* Late Evening */,
346
+ "NIGHT" /* Night */,
347
+ "WAKE" /* Wake */,
348
+ "PHS" /* After Sleep */
349
+ ]);
350
+ function uniqueValues(values) {
351
+ const seen = /* @__PURE__ */ new Set();
352
+ const result = [];
353
+ for (const value of values) {
354
+ if (!seen.has(value)) {
355
+ seen.add(value);
356
+ result.push(value);
357
+ }
358
+ }
359
+ return result;
360
+ }
361
+ function getMealTimingGroup(when, options) {
362
+ if (!(options == null ? void 0 : options.groupMealTimingsByRelation)) {
363
+ return void 0;
364
+ }
365
+ const uniqueWhen2 = uniqueValues(when);
366
+ if (uniqueWhen2.length < 2) {
367
+ return void 0;
368
+ }
369
+ let relation;
370
+ const meals = [];
371
+ const groupedCodes = [];
372
+ let sawFirstMeal = false;
373
+ for (let i = 0; i < uniqueWhen2.length; i += 1) {
374
+ const code = uniqueWhen2[i];
375
+ const detail = MEAL_TIMING_DETAILS[code];
376
+ if (!detail) {
377
+ if (sawFirstMeal) {
378
+ break;
379
+ }
380
+ continue;
381
+ }
382
+ if (!sawFirstMeal) {
383
+ sawFirstMeal = true;
384
+ }
385
+ if (!relation) {
386
+ relation = detail.relation;
387
+ } else if (relation !== detail.relation && detail.relation !== "with") {
388
+ break;
389
+ }
390
+ meals.push(detail.meal);
391
+ groupedCodes.push(code);
392
+ }
393
+ if (groupedCodes.length < 2) {
394
+ return void 0;
395
+ }
396
+ for (let i = 1; i < meals.length; i += 1) {
397
+ const current = meals[i];
398
+ let j = i - 1;
399
+ while (j >= 0 && MEAL_ORDER[meals[j]] > MEAL_ORDER[current]) {
400
+ meals[j + 1] = meals[j];
401
+ j -= 1;
402
+ }
403
+ meals[j + 1] = current;
404
+ }
405
+ if (!relation) {
406
+ return void 0;
407
+ }
408
+ return {
409
+ relation,
410
+ meals,
411
+ codes: groupedCodes
412
+ };
413
+ }
414
+ function inferDailyOccurrenceCount(input, options) {
415
+ var _a2, _b, _c, _d;
416
+ if (!(options == null ? void 0 : options.includeTimesPerDaySummary)) {
417
+ return void 0;
418
+ }
419
+ if (input.frequency !== void 0 || input.frequencyMax !== void 0 || input.timingCode) {
420
+ return void 0;
421
+ }
422
+ if (input.period !== void 0 || input.periodMax !== void 0 || input.periodUnit !== void 0) {
423
+ return void 0;
424
+ }
425
+ if (((_b = (_a2 = input.dayOfWeek) == null ? void 0 : _a2.length) != null ? _b : 0) > 0) {
426
+ return void 0;
427
+ }
428
+ const uniqueWhen2 = uniqueValues((_c = input.when) != null ? _c : []);
429
+ for (let i = 0; i < uniqueWhen2.length; i += 1) {
430
+ if (!INFERABLE_DAILY_EVENT_TIMINGS.has(uniqueWhen2[i])) {
431
+ return void 0;
432
+ }
433
+ }
434
+ const uniqueTimes = uniqueValues((_d = input.timeOfDay) != null ? _d : []);
435
+ const occurrences = uniqueWhen2.length + uniqueTimes.length;
436
+ if (occurrences === 0) {
437
+ return void 0;
438
+ }
439
+ return occurrences;
440
+ }
441
+
442
+ // src/localized-timing.ts
443
+ function uniqueWhenEvents(schedule) {
444
+ var _a2;
445
+ const when = (_a2 = schedule == null ? void 0 : schedule.when) != null ? _a2 : [];
446
+ if (!when.length) {
447
+ return [];
448
+ }
449
+ const unique = [];
450
+ const seen = /* @__PURE__ */ new Set();
451
+ for (const code of when) {
452
+ if (!seen.has(code)) {
453
+ seen.add(code);
454
+ unique.push(code);
455
+ }
456
+ }
457
+ return unique;
458
+ }
459
+ function filterLocalizedWhenEvents(schedule) {
460
+ const unique = uniqueWhenEvents(schedule);
461
+ const specificAfter = /* @__PURE__ */ new Set();
462
+ const specificBefore = /* @__PURE__ */ new Set();
463
+ const specificWith = /* @__PURE__ */ new Set();
464
+ for (const code of unique) {
465
+ if (code === "PCM" /* After Breakfast */ || code === "PCD" /* After Lunch */ || code === "PCV" /* After Dinner */) {
466
+ specificAfter.add(code);
467
+ }
468
+ if (code === "ACM" /* Before Breakfast */ || code === "ACD" /* Before Lunch */ || code === "ACV" /* Before Dinner */) {
469
+ specificBefore.add(code);
470
+ }
471
+ if (code === "CM" /* Breakfast */ || code === "CD" /* Lunch */ || code === "CV" /* Dinner */) {
472
+ specificWith.add(code);
473
+ }
474
+ }
475
+ const hasAllAfter = specificAfter.has("PCM" /* After Breakfast */) && specificAfter.has("PCD" /* After Lunch */) && specificAfter.has("PCV" /* After Dinner */);
476
+ const hasAllBefore = specificBefore.has("ACM" /* Before Breakfast */) && specificBefore.has("ACD" /* Before Lunch */) && specificBefore.has("ACV" /* Before Dinner */);
477
+ const hasAllWith = specificWith.has("CM" /* Breakfast */) && specificWith.has("CD" /* Lunch */) && specificWith.has("CV" /* Dinner */);
478
+ const filtered = [];
479
+ for (const code of unique) {
480
+ if (code === "PC" /* After Meal */ && hasAllAfter) {
481
+ continue;
482
+ }
483
+ if (code === "AC" /* Before Meal */ && hasAllBefore) {
484
+ continue;
485
+ }
486
+ if (code === "C" /* Meal */ && hasAllWith) {
487
+ continue;
488
+ }
489
+ filtered.push(code);
490
+ }
491
+ return filtered;
492
+ }
493
+ function collectLocalizedWhenPhrases(schedule, grammar, options) {
494
+ const filtered = filterLocalizedWhenEvents(schedule);
495
+ if (!filtered.length) {
496
+ return [];
497
+ }
498
+ const mealGroup = getMealTimingGroup(filtered, options);
499
+ if (!mealGroup) {
500
+ const phrases2 = [];
501
+ for (const code of filtered) {
502
+ const text = grammar.whenText[code];
503
+ if (text) {
504
+ phrases2.push(text);
505
+ }
506
+ }
507
+ return phrases2;
508
+ }
509
+ const groupedCodes = new Set(mealGroup.codes);
510
+ const phrases = [];
511
+ let insertedGroup = false;
512
+ for (const code of filtered) {
513
+ if (groupedCodes.has(code)) {
514
+ if (!insertedGroup) {
515
+ phrases.push(grammar.summarizeMealTimingGroup(mealGroup));
516
+ insertedGroup = true;
517
+ }
518
+ continue;
519
+ }
520
+ const text = grammar.whenText[code];
521
+ if (text) {
522
+ phrases.push(text);
523
+ }
524
+ }
525
+ return phrases;
526
+ }
527
+ function inferExplicitDailyFrequency(schedule) {
528
+ var _a2;
529
+ if (!schedule) {
530
+ return void 0;
531
+ }
532
+ if (schedule.frequency !== void 0 && schedule.frequencyMax === void 0 && schedule.periodUnit === "d" /* Day */ && (schedule.period === void 0 || schedule.period === 1) && schedule.periodMax === void 0) {
533
+ return schedule.frequency;
534
+ }
535
+ switch ((_a2 = schedule.timingCode) == null ? void 0 : _a2.toUpperCase()) {
536
+ case "QD":
537
+ return 1;
538
+ case "BID":
539
+ return 2;
540
+ case "TID":
541
+ return 3;
542
+ case "QID":
543
+ return 4;
544
+ default:
545
+ return void 0;
546
+ }
547
+ }
548
+ function isBedtimeOnlyWhen(schedule) {
549
+ const filtered = filterLocalizedWhenEvents(schedule);
550
+ return filtered.length === 1 && filtered[0] === "HS" /* Before Sleep */;
551
+ }
552
+ function combineLocalizedFrequencyAndEvents(schedule, frequency, events, grammar, options) {
553
+ var _a2, _b, _c;
554
+ if (!frequency) {
555
+ if (!events.length) {
556
+ return {};
557
+ }
558
+ return { event: grammar.joinList(events) };
559
+ }
560
+ if (!events.length) {
561
+ return { frequency };
562
+ }
563
+ if (events.length === 1 && isBedtimeOnlyWhen(schedule)) {
564
+ const dailyCount = (_a2 = inferExplicitDailyFrequency(schedule)) != null ? _a2 : inferDailyOccurrenceCount(schedule != null ? schedule : {}, options);
565
+ const style = (_c = (_b = grammar.bedtimeJoinStyle) == null ? void 0 : _b.call(grammar, dailyCount)) != null ? _c : "separate";
566
+ if (style === "adjacent") {
567
+ return { frequency: `${frequency} ${events[0]}` };
568
+ }
569
+ if (style === "conjunction") {
570
+ return { frequency: grammar.joinList([frequency, events[0]]) };
571
+ }
572
+ }
573
+ return { frequency, event: grammar.joinList(events) };
574
+ }
575
+
576
+ // src/prn.ts
577
+ function getCanonicalPrnReasonText(reason) {
578
+ var _a2, _b;
579
+ return (_b = reason == null ? void 0 : reason.text) != null ? _b : (_a2 = reason == null ? void 0 : reason.coding) == null ? void 0 : _a2.display;
580
+ }
581
+ function joinCanonicalPrnReasonTexts(reasons, conjunction = "or") {
582
+ var _a2;
583
+ if (!(reasons == null ? void 0 : reasons.length)) {
584
+ return void 0;
585
+ }
586
+ const texts = [];
587
+ for (const reason of reasons) {
588
+ const text = (_a2 = getCanonicalPrnReasonText(reason)) == null ? void 0 : _a2.trim();
589
+ if (!text) {
590
+ continue;
591
+ }
592
+ texts.push(text);
593
+ }
594
+ switch (texts.length) {
595
+ case 0:
596
+ return void 0;
597
+ case 1:
598
+ return texts[0];
599
+ case 2:
600
+ return `${texts[0]} ${conjunction} ${texts[1]}`;
601
+ default: {
602
+ let combined = "";
603
+ for (let index = 0; index < texts.length; index += 1) {
604
+ if (index === 0) {
605
+ combined = texts[index];
606
+ continue;
607
+ }
608
+ if (index === texts.length - 1) {
609
+ combined += ` ${conjunction} ${texts[index]}`;
610
+ continue;
611
+ }
612
+ combined += `, ${texts[index]}`;
613
+ }
614
+ return combined;
615
+ }
616
+ }
617
+ }
618
+ function getPreferredCanonicalPrnReasonText(reason, reasons, conjunction = "or") {
619
+ var _a2;
620
+ const direct = (_a2 = getCanonicalPrnReasonText(reason)) == null ? void 0 : _a2.trim();
621
+ if (!(reasons == null ? void 0 : reasons.length)) {
622
+ return direct;
623
+ }
624
+ if (!direct) {
625
+ return joinCanonicalPrnReasonTexts(reasons, conjunction);
626
+ }
627
+ return /[,/;]/.test(direct) || /\b(?:or|and|and\/or)\b/i.test(direct) || /\s(?:หรือ|และ)\s/.test(direct) ? joinCanonicalPrnReasonTexts(reasons, conjunction) : direct;
628
+ }
629
+
362
630
  // src/snomed.ts
363
631
  var SNOMED_SYSTEM = "http://snomed.info/sct";
364
632
  var SNOMED_CT_FINDING_SITE_ATTRIBUTE_CODE = "363698007";
@@ -385,6 +653,24 @@ function normalizeLocaleKey(locale) {
385
653
  function cloneI18nRecord(i18n) {
386
654
  return i18n ? __spreadValues({}, i18n) : void 0;
387
655
  }
656
+ function mergeI18nRecords(...records) {
657
+ var _a2;
658
+ const merged = {};
659
+ for (const record of records) {
660
+ if (!record) {
661
+ continue;
662
+ }
663
+ for (const locale in record) {
664
+ const normalizedLocale = normalizeLocaleKey(locale);
665
+ const content = (_a2 = record[locale]) == null ? void 0 : _a2.trim();
666
+ if (!normalizedLocale || !content) {
667
+ continue;
668
+ }
669
+ merged[normalizedLocale] = content;
670
+ }
671
+ }
672
+ return Object.keys(merged).length ? merged : void 0;
673
+ }
388
674
  function cloneExtension(extension) {
389
675
  var _a2, _b, _c, _d;
390
676
  return {
@@ -471,6 +757,32 @@ function buildTranslationPrimitiveElement(translations, base) {
471
757
  extension: nextExtensions
472
758
  };
473
759
  }
760
+ function mergeTranslationPrimitiveElement(base, translations) {
761
+ var _a2, _b, _c, _d;
762
+ const nextExtensions = (_b = (_a2 = base == null ? void 0 : base.extension) == null ? void 0 : _a2.map(cloneExtension)) != null ? _b : [];
763
+ const existingTranslationLocales = /* @__PURE__ */ new Set();
764
+ for (const extension of nextExtensions) {
765
+ if (extension.url !== FHIR_TRANSLATION_EXTENSION_URL) {
766
+ continue;
767
+ }
768
+ const { locale, content } = getTranslationParts(extension);
769
+ if (locale && content) {
770
+ existingTranslationLocales.add(locale);
771
+ }
772
+ }
773
+ const additions = (_d = (_c = buildTranslationPrimitiveElement(translations)) == null ? void 0 : _c.extension) != null ? _d : [];
774
+ for (const extension of additions) {
775
+ const { locale } = getTranslationParts(extension);
776
+ if (locale && existingTranslationLocales.has(locale)) {
777
+ continue;
778
+ }
779
+ if (locale) {
780
+ existingTranslationLocales.add(locale);
781
+ }
782
+ nextExtensions.push(extension);
783
+ }
784
+ return nextExtensions.length ? { extension: nextExtensions } : void 0;
785
+ }
474
786
  function getTranslationParts(extension) {
475
787
  var _a2, _b;
476
788
  const parts = (_a2 = extension.extension) != null ? _a2 : [];
@@ -514,6 +826,24 @@ function getPrimitiveTranslation(element, locale) {
514
826
  }
515
827
  return languagePrefixMatch;
516
828
  }
829
+ function getPrimitiveTranslations(element) {
830
+ var _a2;
831
+ if (!((_a2 = element == null ? void 0 : element.extension) == null ? void 0 : _a2.length)) {
832
+ return void 0;
833
+ }
834
+ const translations = {};
835
+ for (const extension of element.extension) {
836
+ if (extension.url !== FHIR_TRANSLATION_EXTENSION_URL) {
837
+ continue;
838
+ }
839
+ const { locale, content } = getTranslationParts(extension);
840
+ if (!locale || !content) {
841
+ continue;
842
+ }
843
+ translations[locale] = content;
844
+ }
845
+ return Object.keys(translations).length ? translations : void 0;
846
+ }
517
847
 
518
848
  // src/body-site-spatial.ts
519
849
  var SNOMED_SYSTEM2 = "http://snomed.info/sct";
@@ -1105,10 +1435,11 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1105
1435
  }
1106
1436
  },
1107
1437
  {
1108
- 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"],
1438
+ 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"],
1109
1439
  definition: {
1110
1440
  coding: { code: "40638003", display: "Structure of both eyes" },
1111
1441
  text: "both eyes",
1442
+ administrationTargetCount: 2,
1112
1443
  i18n: { th: "\u0E15\u0E32\u0E17\u0E31\u0E49\u0E07\u0E2A\u0E2D\u0E07\u0E02\u0E49\u0E32\u0E07" },
1113
1444
  routeHint: RouteCode["Ophthalmic route"]
1114
1445
  }
@@ -1122,10 +1453,19 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1122
1453
  }
1123
1454
  },
1124
1455
  {
1125
- 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"],
1456
+ names: [
1457
+ "ears",
1458
+ "both ears",
1459
+ "bilateral ears",
1460
+ "each ear",
1461
+ "inside ears",
1462
+ "\u0E2B\u0E39\u0E17\u0E31\u0E49\u0E07\u0E2A\u0E2D\u0E07\u0E02\u0E49\u0E32\u0E07",
1463
+ "\u0E2B\u0E39\u0E2A\u0E2D\u0E07\u0E02\u0E49\u0E32\u0E07"
1464
+ ],
1126
1465
  definition: {
1127
1466
  coding: { code: "34338003", display: "Both ears" },
1128
1467
  text: "both ears",
1468
+ administrationTargetCount: 2,
1129
1469
  routeHint: RouteCode["Otic route"]
1130
1470
  }
1131
1471
  },
@@ -1143,10 +1483,18 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1143
1483
  }
1144
1484
  },
1145
1485
  {
1146
- names: ["ear canals", "both ear canals", "inside ear canals", "both external auditory canals"],
1486
+ names: [
1487
+ "ear canals",
1488
+ "both ear canals",
1489
+ "each ear canal",
1490
+ "inside ear canals",
1491
+ "both external auditory canals",
1492
+ "each external auditory canal"
1493
+ ],
1147
1494
  definition: {
1148
1495
  coding: { code: "181178004", display: "Entire external auditory canal" },
1149
1496
  text: "both ear canals",
1497
+ administrationTargetCount: 2,
1150
1498
  routeHint: RouteCode["Otic route"]
1151
1499
  }
1152
1500
  },
@@ -1183,8 +1531,12 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1183
1531
  }
1184
1532
  },
1185
1533
  {
1186
- names: ["nostril", "nostrils"],
1187
- definition: { coding: { code: "1797002", display: "Naris" }, routeHint: RouteCode["Nasal route"] }
1534
+ names: ["nostril"],
1535
+ definition: {
1536
+ coding: { code: "1797002", display: "Naris" },
1537
+ text: "nostril",
1538
+ routeHint: RouteCode["Nasal route"]
1539
+ }
1188
1540
  },
1189
1541
  {
1190
1542
  names: ["left nostril", "left naris"],
@@ -1201,8 +1553,22 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1201
1553
  }
1202
1554
  },
1203
1555
  {
1204
- names: ["nares", "anterior nares"],
1205
- definition: { coding: { code: "244506005", display: "Anterior nares" }, routeHint: RouteCode["Nasal route"] }
1556
+ names: [
1557
+ "nostrils",
1558
+ "both nostrils",
1559
+ "bilateral nostrils",
1560
+ "each nostril",
1561
+ "nares",
1562
+ "anterior nares",
1563
+ "\u0E23\u0E39\u0E08\u0E21\u0E39\u0E01\u0E17\u0E31\u0E49\u0E07\u0E2A\u0E2D\u0E07\u0E02\u0E49\u0E32\u0E07"
1564
+ ],
1565
+ definition: {
1566
+ coding: { code: "244506005", display: "Anterior nares" },
1567
+ text: "both nostrils",
1568
+ administrationTargetCount: 2,
1569
+ i18n: { th: "\u0E23\u0E39\u0E08\u0E21\u0E39\u0E01\u0E17\u0E31\u0E49\u0E07\u0E2A\u0E2D\u0E07\u0E02\u0E49\u0E32\u0E07" },
1570
+ routeHint: RouteCode["Nasal route"]
1571
+ }
1206
1572
  },
1207
1573
  {
1208
1574
  names: ["nose"],
@@ -1728,6 +2094,24 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1728
2094
  names: ["affected area", "affected areas", "affected site", "\u0E1A\u0E23\u0E34\u0E40\u0E27\u0E13\u0E17\u0E35\u0E48\u0E40\u0E1B\u0E47\u0E19"],
1729
2095
  definition: { text: "affected area", routeHint: RouteCode["Topical route"] }
1730
2096
  },
2097
+ {
2098
+ names: ["lesion", "skin lesion"],
2099
+ definition: {
2100
+ coding: { code: "95324001", display: "Skin lesion" },
2101
+ text: "lesion",
2102
+ routeHint: RouteCode["Topical route"],
2103
+ i18n: { th: "\u0E23\u0E2D\u0E22\u0E42\u0E23\u0E04" }
2104
+ }
2105
+ },
2106
+ {
2107
+ names: ["lesions", "skin lesions"],
2108
+ definition: {
2109
+ coding: { code: "95324001", display: "Skin lesion" },
2110
+ text: "lesions",
2111
+ routeHint: RouteCode["Topical route"],
2112
+ i18n: { th: "\u0E23\u0E2D\u0E22\u0E42\u0E23\u0E04" }
2113
+ }
2114
+ },
1731
2115
  {
1732
2116
  names: ["left head", "left side of head"],
1733
2117
  definition: { coding: { code: "64237003", display: "Structure of left half of head" }, routeHint: RouteCode["Topical route"] }
@@ -4307,6 +4691,7 @@ var BODY_SITE_FEATURE_SCORE_BONUS = bodySiteFeatureScoreBonus(lexical_classes_de
4307
4691
  var CONNECTORS = setOf(lexical_classes_default.connectors);
4308
4692
  var ROUTE_SITE_PREPOSITIONS = setOf(lexical_classes_default.routeSitePrepositions);
4309
4693
  var SITE_DISPLAY_FILLERS = SITE_FILLERS;
4694
+ var SITE_MULTIPLICITY_WORDS = setOf(["both", "each", "bilateral"]);
4310
4695
  var NON_SITE_ANCHORED_PHRASES = setOf(lexical_classes_default.nonSiteAnchoredPhrases);
4311
4696
  var EXTERNAL_SITE_LOCATIVE_PREFIXES = setOf(lexical_classes_default.externalSiteLocativePrefixes);
4312
4697
  var ROUTE_BLOCKED_BY_FOLLOWING_PARTITIVE_HEADS = setOf(
@@ -4500,7 +4885,7 @@ function buildBodySiteCoding(definition) {
4500
4885
  code: coding.code,
4501
4886
  display: coding.display,
4502
4887
  system: (_a2 = coding.system) != null ? _a2 : SNOMED_SYSTEM3,
4503
- i18n: definition == null ? void 0 : definition.i18n
4888
+ i18n: mergeI18nRecords(definition == null ? void 0 : definition.i18n, coding.i18n)
4504
4889
  };
4505
4890
  }
4506
4891
  function cloneBodySiteCode(coding) {
@@ -4512,7 +4897,7 @@ function cloneBodySiteCode(coding) {
4512
4897
  code: coding.code,
4513
4898
  display: coding.display,
4514
4899
  system: (_a2 = coding.system) != null ? _a2 : SNOMED_SYSTEM3,
4515
- i18n: coding.i18n
4900
+ i18n: mergeI18nRecords(coding.i18n)
4516
4901
  };
4517
4902
  }
4518
4903
  function lookupDefinitionForCanonical(canonical, customSiteMap) {
@@ -4804,186 +5189,52 @@ function inferPreferredPreposition(canonical, features, definition) {
4804
5189
  if (routeHint === RouteCode["Topical route"] || routeHint === RouteCode["Transdermal route"]) {
4805
5190
  return "to";
4806
5191
  }
4807
- 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"]) {
4808
- return routeHint === RouteCode["Nasal route"] ? "into" : "to";
4809
- }
4810
- const words = canonical.split(/\s+/).filter((word) => word.length > 0);
4811
- for (const word of words) {
4812
- if (OTIC_SITE_WORDS.has(word) || OPHTHALMIC_SITE_WORDS.has(word)) {
4813
- return "in";
4814
- }
4815
- if (NASAL_SITE_WORDS.has(word)) {
4816
- return "into";
4817
- }
4818
- }
4819
- return void 0;
4820
- }
4821
- function resolveBodySitePhrase(text, customSiteMap, context) {
4822
- var _a2, _b, _c, _d, _e, _f, _g;
4823
- const trimmed = text.trim().replace(/\s+/g, " ");
4824
- if (!trimmed) {
4825
- return void 0;
4826
- }
4827
- const lookupCanonical = normalizeBodySiteKey(trimmed);
4828
- const contextualCanonical = resolveContextualBodySiteAlias(lookupCanonical, context);
4829
- const displaySourceText = contextualCanonical != null ? contextualCanonical : trimmed;
4830
- const displayText = normalizeSiteDisplayText(displaySourceText, customSiteMap);
4831
- const canonical = normalizeBodySiteKey(displayText);
4832
- 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];
4833
- const coding = buildBodySiteCoding(definition);
4834
- const finalDisplayText = (_f = definition == null ? void 0 : definition.text) != null ? _f : displayText;
4835
- const features = parseBodySiteFeatures(finalDisplayText, coding, customSiteMap);
4836
- const spatialRelation = (_g = definition == null ? void 0 : definition.spatialRelation) != null ? _g : buildSpatialRelation(features, finalDisplayText, customSiteMap);
4837
- return {
4838
- lookupCanonical,
4839
- resolutionCanonical: contextualCanonical != null ? contextualCanonical : lookupCanonical,
4840
- canonical: normalizeBodySiteKey(finalDisplayText) || canonical,
4841
- displayText: finalDisplayText,
4842
- coding,
4843
- spatialRelation,
4844
- definition,
4845
- features,
4846
- englishObjectText: renderBodySiteObject(features),
4847
- preferredPreposition: inferPreferredPreposition(
4848
- normalizeBodySiteKey(finalDisplayText) || canonical,
4849
- features,
4850
- definition
4851
- )
4852
- };
4853
- }
4854
-
4855
- // src/timing-summary.ts
4856
- var MEAL_TIMING_DETAILS = {
4857
- ["ACM" /* Before Breakfast */]: { relation: "before", meal: "breakfast" },
4858
- ["ACD" /* Before Lunch */]: { relation: "before", meal: "lunch" },
4859
- ["ACV" /* Before Dinner */]: { relation: "before", meal: "dinner" },
4860
- ["PCM" /* After Breakfast */]: { relation: "after", meal: "breakfast" },
4861
- ["PCD" /* After Lunch */]: { relation: "after", meal: "lunch" },
4862
- ["PCV" /* After Dinner */]: { relation: "after", meal: "dinner" },
4863
- ["CM" /* Breakfast */]: { relation: "with", meal: "breakfast" },
4864
- ["CD" /* Lunch */]: { relation: "with", meal: "lunch" },
4865
- ["CV" /* Dinner */]: { relation: "with", meal: "dinner" }
4866
- };
4867
- var MEAL_ORDER = {
4868
- breakfast: 0,
4869
- lunch: 1,
4870
- dinner: 2
4871
- };
4872
- var INFERABLE_DAILY_EVENT_TIMINGS = /* @__PURE__ */ new Set([
4873
- "HS" /* Before Sleep */,
4874
- "ACM" /* Before Breakfast */,
4875
- "ACD" /* Before Lunch */,
4876
- "ACV" /* Before Dinner */,
4877
- "PCM" /* After Breakfast */,
4878
- "PCD" /* After Lunch */,
4879
- "PCV" /* After Dinner */,
4880
- "CM" /* Breakfast */,
4881
- "CD" /* Lunch */,
4882
- "CV" /* Dinner */,
4883
- "MORN" /* Morning */,
4884
- "MORN.early" /* Early Morning */,
4885
- "MORN.late" /* Late Morning */,
4886
- "NOON" /* Noon */,
4887
- "AFT" /* Afternoon */,
4888
- "AFT.early" /* Early Afternoon */,
4889
- "AFT.late" /* Late Afternoon */,
4890
- "EVE" /* Evening */,
4891
- "EVE.early" /* Early Evening */,
4892
- "EVE.late" /* Late Evening */,
4893
- "NIGHT" /* Night */,
4894
- "WAKE" /* Wake */,
4895
- "PHS" /* After Sleep */
4896
- ]);
4897
- function uniqueValues(values) {
4898
- const seen = /* @__PURE__ */ new Set();
4899
- const result = [];
4900
- for (const value of values) {
4901
- if (!seen.has(value)) {
4902
- seen.add(value);
4903
- result.push(value);
4904
- }
4905
- }
4906
- return result;
4907
- }
4908
- function getMealTimingGroup(when, options) {
4909
- if (!(options == null ? void 0 : options.groupMealTimingsByRelation)) {
4910
- return void 0;
4911
- }
4912
- const uniqueWhen2 = uniqueValues(when);
4913
- if (uniqueWhen2.length < 2) {
4914
- return void 0;
4915
- }
4916
- let relation;
4917
- const meals = [];
4918
- const groupedCodes = [];
4919
- let sawFirstMeal = false;
4920
- for (let i = 0; i < uniqueWhen2.length; i += 1) {
4921
- const code = uniqueWhen2[i];
4922
- const detail = MEAL_TIMING_DETAILS[code];
4923
- if (!detail) {
4924
- if (sawFirstMeal) {
4925
- break;
4926
- }
4927
- continue;
4928
- }
4929
- if (!sawFirstMeal) {
4930
- sawFirstMeal = true;
4931
- }
4932
- if (!relation) {
4933
- relation = detail.relation;
4934
- } else if (relation !== detail.relation && detail.relation !== "with") {
4935
- break;
4936
- }
4937
- meals.push(detail.meal);
4938
- groupedCodes.push(code);
4939
- }
4940
- if (groupedCodes.length < 2) {
4941
- return void 0;
4942
- }
4943
- for (let i = 1; i < meals.length; i += 1) {
4944
- const current = meals[i];
4945
- let j = i - 1;
4946
- while (j >= 0 && MEAL_ORDER[meals[j]] > MEAL_ORDER[current]) {
4947
- meals[j + 1] = meals[j];
4948
- j -= 1;
4949
- }
4950
- meals[j + 1] = current;
4951
- }
4952
- if (!relation) {
4953
- return void 0;
4954
- }
4955
- return {
4956
- relation,
4957
- meals,
4958
- codes: groupedCodes
4959
- };
4960
- }
4961
- function inferDailyOccurrenceCount(input, options) {
4962
- var _a2, _b, _c, _d;
4963
- if (!(options == null ? void 0 : options.includeTimesPerDaySummary)) {
4964
- return void 0;
4965
- }
4966
- if (input.frequency !== void 0 || input.frequencyMax !== void 0 || input.timingCode) {
4967
- return void 0;
4968
- }
4969
- if (input.period !== void 0 || input.periodMax !== void 0 || input.periodUnit !== void 0) {
4970
- return void 0;
4971
- }
4972
- if (((_b = (_a2 = input.dayOfWeek) == null ? void 0 : _a2.length) != null ? _b : 0) > 0) {
4973
- return void 0;
5192
+ 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"]) {
5193
+ return routeHint === RouteCode["Nasal route"] ? "into" : "to";
4974
5194
  }
4975
- const uniqueWhen2 = uniqueValues((_c = input.when) != null ? _c : []);
4976
- for (let i = 0; i < uniqueWhen2.length; i += 1) {
4977
- if (!INFERABLE_DAILY_EVENT_TIMINGS.has(uniqueWhen2[i])) {
4978
- return void 0;
5195
+ const words = canonical.split(/\s+/).filter((word) => word.length > 0);
5196
+ for (const word of words) {
5197
+ if (OTIC_SITE_WORDS.has(word) || OPHTHALMIC_SITE_WORDS.has(word)) {
5198
+ return "in";
5199
+ }
5200
+ if (NASAL_SITE_WORDS.has(word)) {
5201
+ return "into";
4979
5202
  }
4980
5203
  }
4981
- const uniqueTimes = uniqueValues((_d = input.timeOfDay) != null ? _d : []);
4982
- const occurrences = uniqueWhen2.length + uniqueTimes.length;
4983
- if (occurrences === 0) {
5204
+ return void 0;
5205
+ }
5206
+ function resolveBodySitePhrase(text, customSiteMap, context) {
5207
+ var _a2, _b, _c, _d, _e, _f, _g;
5208
+ const trimmed = text.trim().replace(/\s+/g, " ");
5209
+ if (!trimmed) {
4984
5210
  return void 0;
4985
5211
  }
4986
- return occurrences;
5212
+ const lookupCanonical = normalizeBodySiteKey(trimmed);
5213
+ const contextualCanonical = resolveContextualBodySiteAlias(lookupCanonical, context);
5214
+ const displaySourceText = contextualCanonical != null ? contextualCanonical : trimmed;
5215
+ const displayText = normalizeSiteDisplayText(displaySourceText, customSiteMap);
5216
+ const canonical = normalizeBodySiteKey(displayText);
5217
+ 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];
5218
+ const coding = buildBodySiteCoding(definition);
5219
+ const finalDisplayText = (_f = definition == null ? void 0 : definition.text) != null ? _f : displayText;
5220
+ const features = parseBodySiteFeatures(finalDisplayText, coding, customSiteMap);
5221
+ const spatialRelation = (_g = definition == null ? void 0 : definition.spatialRelation) != null ? _g : buildSpatialRelation(features, finalDisplayText, customSiteMap);
5222
+ return {
5223
+ lookupCanonical,
5224
+ resolutionCanonical: contextualCanonical != null ? contextualCanonical : lookupCanonical,
5225
+ canonical: normalizeBodySiteKey(finalDisplayText) || canonical,
5226
+ displayText: finalDisplayText,
5227
+ coding,
5228
+ spatialRelation,
5229
+ definition,
5230
+ features,
5231
+ englishObjectText: renderBodySiteObject(features),
5232
+ preferredPreposition: inferPreferredPreposition(
5233
+ normalizeBodySiteKey(finalDisplayText) || canonical,
5234
+ features,
5235
+ definition
5236
+ )
5237
+ };
4987
5238
  }
4988
5239
 
4989
5240
  // src/format.ts
@@ -5477,73 +5728,22 @@ function summarizeMealTimingGroup(group) {
5477
5728
  }
5478
5729
  return `${relationText} ${joinWithAnd(group.meals)}`;
5479
5730
  }
5480
- function collectWhenPhrases(schedule, options) {
5481
- var _a2, _b, _c;
5482
- const when = (_a2 = schedule == null ? void 0 : schedule.when) != null ? _a2 : [];
5483
- if (!when.length) {
5484
- return [];
5485
- }
5486
- const unique = [];
5487
- const seen = /* @__PURE__ */ new Set();
5488
- let hasSpecificAfter = false;
5489
- let hasSpecificBefore = false;
5490
- let hasSpecificWith = false;
5491
- for (const code of when) {
5492
- if (!seen.has(code)) {
5493
- seen.add(code);
5494
- unique.push(code);
5495
- if (code === "PCM" /* After Breakfast */ || code === "PCD" /* After Lunch */ || code === "PCV" /* After Dinner */) {
5496
- hasSpecificAfter = true;
5497
- }
5498
- if (code === "ACM" /* Before Breakfast */ || code === "ACD" /* Before Lunch */ || code === "ACV" /* Before Dinner */) {
5499
- hasSpecificBefore = true;
5500
- }
5501
- if (code === "CM" /* Breakfast */ || code === "CD" /* Lunch */ || code === "CV" /* Dinner */) {
5502
- hasSpecificWith = true;
5503
- }
5504
- }
5505
- }
5506
- const filtered = [];
5507
- for (const code of unique) {
5508
- if (code === "PC" /* After Meal */ && hasSpecificAfter) {
5509
- continue;
5510
- }
5511
- if (code === "AC" /* Before Meal */ && hasSpecificBefore) {
5512
- continue;
5513
- }
5514
- if (code === "C" /* Meal */ && hasSpecificWith) {
5515
- continue;
5516
- }
5517
- filtered.push(code);
5518
- }
5519
- const mealGroup = getMealTimingGroup(filtered, options);
5520
- if (!mealGroup) {
5521
- const phrases2 = [];
5522
- for (const code of filtered) {
5523
- const text = (_b = WHEN_TEXT[code]) != null ? _b : code;
5524
- if (text) {
5525
- phrases2.push(text);
5526
- }
5527
- }
5528
- return phrases2;
5529
- }
5530
- const groupedCodes = new Set(mealGroup.codes);
5531
- const phrases = [];
5532
- let insertedGroup = false;
5533
- for (const code of filtered) {
5534
- if (groupedCodes.has(code)) {
5535
- if (!insertedGroup) {
5536
- phrases.push(summarizeMealTimingGroup(mealGroup));
5537
- insertedGroup = true;
5538
- }
5539
- continue;
5731
+ var EN_TIMING_GRAMMAR = {
5732
+ whenText: WHEN_TEXT,
5733
+ joinList: joinWithAnd,
5734
+ summarizeMealTimingGroup,
5735
+ bedtimeJoinStyle: (dailyCount) => {
5736
+ if (dailyCount === 1) {
5737
+ return "adjacent";
5540
5738
  }
5541
- const text = (_c = WHEN_TEXT[code]) != null ? _c : code;
5542
- if (text) {
5543
- phrases.push(text);
5739
+ if (dailyCount === 2 || dailyCount === 3 || dailyCount === 4) {
5740
+ return "conjunction";
5544
5741
  }
5742
+ return "separate";
5545
5743
  }
5546
- return phrases;
5744
+ };
5745
+ function collectWhenPhrases(schedule, options) {
5746
+ return collectLocalizedWhenPhrases(schedule, EN_TIMING_GRAMMAR, options);
5547
5747
  }
5548
5748
  function joinWithAnd(parts) {
5549
5749
  if (!parts.length) {
@@ -5557,23 +5757,14 @@ function joinWithAnd(parts) {
5557
5757
  }
5558
5758
  return `${parts.slice(0, -1).join(", ")} and ${parts[parts.length - 1]}`;
5559
5759
  }
5560
- function combineFrequencyAndEvents(frequency, events) {
5561
- if (!frequency) {
5562
- if (!events.length) {
5563
- return {};
5564
- }
5565
- return { event: joinWithAnd(events) };
5566
- }
5567
- if (!events.length) {
5568
- return { frequency };
5569
- }
5570
- if (events.length === 1 && events[0] === "at bedtime") {
5571
- const lowerFrequency = frequency.toLowerCase();
5572
- if (lowerFrequency === "twice daily" || lowerFrequency === "three times daily" || lowerFrequency === "four times daily") {
5573
- return { frequency: `${frequency} and ${events[0]}` };
5574
- }
5575
- }
5576
- return { frequency, event: joinWithAnd(events) };
5760
+ function combineFrequencyAndEvents(schedule, frequency, events, options) {
5761
+ return combineLocalizedFrequencyAndEvents(
5762
+ schedule,
5763
+ frequency,
5764
+ events,
5765
+ EN_TIMING_GRAMMAR,
5766
+ options
5767
+ );
5577
5768
  }
5578
5769
  function buildRoutePhrase(clause, grammar, hasSite) {
5579
5770
  var _a2, _b;
@@ -5878,7 +6069,7 @@ function formatLong(clause, options) {
5878
6069
  eventParts.push(`at ${timeStrings.join(", ")}`);
5879
6070
  }
5880
6071
  }
5881
- const timing = combineFrequencyAndEvents(frequencyPart, eventParts);
6072
+ const timing = combineFrequencyAndEvents(schedule, frequencyPart, eventParts, options);
5882
6073
  const dayPart = describeDayOfWeek(schedule);
5883
6074
  const countPart = schedule.count !== void 0 && !standaloneOccurrenceCount ? `for ${stripTrailingZero(schedule.count)} ${schedule.count === 1 ? "dose" : "doses"}` : void 0;
5884
6075
  const durationPart = describeDuration(schedule);
@@ -9673,6 +9864,81 @@ function listSupportedBodySiteGrammar() {
9673
9864
  };
9674
9865
  }
9675
9866
 
9867
+ // src/body-site-target.ts
9868
+ var BODY_SITE_ADMINISTRATION_TARGET_COUNT_EXTENSION_URL = "urn:ezmedicationinput:body-site-administration-target-count";
9869
+ function normalizeAdministrationTargetCount(value) {
9870
+ if (!Number.isFinite(value) || value === void 0) {
9871
+ return void 0;
9872
+ }
9873
+ const rounded = Math.trunc(value);
9874
+ return rounded >= 2 ? rounded : void 0;
9875
+ }
9876
+ function siteTextFromLike(site, options) {
9877
+ if (!site) {
9878
+ return void 0;
9879
+ }
9880
+ if (typeof site === "string") {
9881
+ return site;
9882
+ }
9883
+ if ("text" in site && site.text) {
9884
+ return site.text;
9885
+ }
9886
+ const coding = "coding" in site ? Array.isArray(site.coding) ? site.coding.find((candidate) => candidate == null ? void 0 : candidate.code) : site.coding : void 0;
9887
+ return (coding == null ? void 0 : coding.code) ? getBodySiteText(
9888
+ {
9889
+ system: coding.system,
9890
+ code: coding.code,
9891
+ display: coding.display
9892
+ },
9893
+ { siteCodeMap: options == null ? void 0 : options.siteCodeMap }
9894
+ ) : void 0;
9895
+ }
9896
+ function buildBodySiteAdministrationTargetCountExtension(targetCount) {
9897
+ const normalized = normalizeAdministrationTargetCount(targetCount);
9898
+ if (normalized === void 0) {
9899
+ return void 0;
9900
+ }
9901
+ return {
9902
+ url: BODY_SITE_ADMINISTRATION_TARGET_COUNT_EXTENSION_URL,
9903
+ valueInteger: normalized
9904
+ };
9905
+ }
9906
+ function parseBodySiteAdministrationTargetCountExtension(concept) {
9907
+ var _a2;
9908
+ const extension = (_a2 = concept == null ? void 0 : concept.extension) == null ? void 0 : _a2.find(
9909
+ (candidate) => candidate.url === BODY_SITE_ADMINISTRATION_TARGET_COUNT_EXTENSION_URL
9910
+ );
9911
+ return normalizeAdministrationTargetCount(extension == null ? void 0 : extension.valueInteger);
9912
+ }
9913
+ function getBodySiteAdministrationTargetCount(site, options) {
9914
+ var _a2;
9915
+ if (!site) {
9916
+ return void 0;
9917
+ }
9918
+ if (typeof site !== "string" && "administrationTargetCount" in site) {
9919
+ const direct = normalizeAdministrationTargetCount(site.administrationTargetCount);
9920
+ if (direct !== void 0) {
9921
+ return direct;
9922
+ }
9923
+ }
9924
+ if (typeof site !== "string") {
9925
+ if ("extension" in site) {
9926
+ const fromExtension = parseBodySiteAdministrationTargetCountExtension(site);
9927
+ if (fromExtension !== void 0) {
9928
+ return fromExtension;
9929
+ }
9930
+ }
9931
+ }
9932
+ const text = siteTextFromLike(site, options);
9933
+ if (!text) {
9934
+ return void 0;
9935
+ }
9936
+ const resolved = resolveBodySitePhrase(text, options == null ? void 0 : options.siteCodeMap, {
9937
+ bodySiteContext: options == null ? void 0 : options.bodySiteContext
9938
+ });
9939
+ return normalizeAdministrationTargetCount((_a2 = resolved == null ? void 0 : resolved.definition) == null ? void 0 : _a2.administrationTargetCount);
9940
+ }
9941
+
9676
9942
  // src/parser-state.ts
9677
9943
  var ParserState = class {
9678
9944
  constructor(input, tokens, customSiteHints) {
@@ -9935,6 +10201,7 @@ var ParserState = class {
9935
10201
  for (const reason of value) {
9936
10202
  reasons.push({
9937
10203
  text: reason.text,
10204
+ i18n: cloneI18nRecord(reason.i18n),
9938
10205
  spatialRelation: cloneBodySiteSpatialRelation(reason.spatialRelation),
9939
10206
  coding: reason.coding ? {
9940
10207
  code: reason.coding.code,
@@ -9942,7 +10209,7 @@ var ParserState = class {
9942
10209
  system: reason.coding.system,
9943
10210
  extension: cloneExtensions(reason.coding.extension),
9944
10211
  _display: clonePrimitiveElement(reason.coding._display),
9945
- i18n: reason.coding.i18n
10212
+ i18n: cloneI18nRecord(reason.coding.i18n)
9946
10213
  } : void 0
9947
10214
  });
9948
10215
  }
@@ -9974,7 +10241,7 @@ var ParserState = class {
9974
10241
  system: value.system,
9975
10242
  extension: cloneExtensions(value.extension),
9976
10243
  _display: clonePrimitiveElement(value._display),
9977
- i18n: value.i18n
10244
+ i18n: cloneI18nRecord(value.i18n)
9978
10245
  } : void 0;
9979
10246
  }
9980
10247
  get siteText() {
@@ -10007,7 +10274,7 @@ var ParserState = class {
10007
10274
  code: value.code,
10008
10275
  display: value.display,
10009
10276
  system: value.system,
10010
- i18n: value.i18n
10277
+ i18n: cloneI18nRecord(value.i18n)
10011
10278
  } : void 0;
10012
10279
  }
10013
10280
  get siteSpatialRelation() {
@@ -10042,6 +10309,20 @@ var ParserState = class {
10042
10309
  sourceText: value.sourceText
10043
10310
  };
10044
10311
  }
10312
+ get siteAdministrationTargetCount() {
10313
+ var _a2;
10314
+ return (_a2 = this.clause.site) == null ? void 0 : _a2.administrationTargetCount;
10315
+ }
10316
+ set siteAdministrationTargetCount(value) {
10317
+ if (value === void 0) {
10318
+ if (this.clause.site) {
10319
+ delete this.clause.site.administrationTargetCount;
10320
+ this.cleanupSite();
10321
+ }
10322
+ return;
10323
+ }
10324
+ this.ensureSite().administrationTargetCount = value;
10325
+ }
10045
10326
  get additionalInstructions() {
10046
10327
  if (!this.clause.additionalInstructions) {
10047
10328
  this.clause.additionalInstructions = [];
@@ -10114,7 +10395,7 @@ var ParserState = class {
10114
10395
  if (!site) {
10115
10396
  return;
10116
10397
  }
10117
- 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) {
10398
+ 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) {
10118
10399
  delete this.clause.site;
10119
10400
  }
10120
10401
  }
@@ -10192,12 +10473,12 @@ function selectCanonicalSiteCoding(site, options) {
10192
10473
  );
10193
10474
  return (options == null ? void 0 : options.bodySitePostcoordination) === true ? postcoordinated != null ? postcoordinated : site == null ? void 0 : site.coding : (_a2 = site == null ? void 0 : site.coding) != null ? _a2 : postcoordinated;
10194
10475
  }
10195
- function buildSiteCodingArray(siteCoding) {
10476
+ function buildSiteCodingArray(siteCoding, options) {
10196
10477
  var _a2;
10197
10478
  if (!(siteCoding == null ? void 0 : siteCoding.code)) {
10198
10479
  return void 0;
10199
10480
  }
10200
- const displayElement = buildTranslationPrimitiveElement(siteCoding.i18n);
10481
+ const displayElement = (options == null ? void 0 : options.includeTranslationExtensions) ? buildTranslationPrimitiveElement(siteCoding.i18n) : void 0;
10201
10482
  return [
10202
10483
  __spreadValues({
10203
10484
  system: (_a2 = siteCoding.system) != null ? _a2 : SNOMED_SYSTEM5,
@@ -10217,6 +10498,18 @@ function getFallbackSiteText(site) {
10217
10498
  display: siteCoding.display
10218
10499
  }) : void 0;
10219
10500
  }
10501
+ function codeableConceptTranslationI18n(concept, coding, fallback) {
10502
+ return {
10503
+ text: mergeI18nRecords(
10504
+ fallback,
10505
+ getPrimitiveTranslations(concept == null ? void 0 : concept._text)
10506
+ ),
10507
+ display: mergeI18nRecords(
10508
+ coding == null ? void 0 : coding.i18n,
10509
+ getPrimitiveTranslations(coding == null ? void 0 : coding._display)
10510
+ )
10511
+ };
10512
+ }
10220
10513
  function lowerFirst(value) {
10221
10514
  const trimmed = value == null ? void 0 : value.trim();
10222
10515
  return trimmed ? trimmed.charAt(0).toLowerCase() + trimmed.slice(1) : void 0;
@@ -10463,7 +10756,7 @@ function appendWarning(warnings, warning) {
10463
10756
  return warnings;
10464
10757
  }
10465
10758
  function canonicalToFhir(clause, textOverride, options) {
10466
- 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;
10759
+ 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;
10467
10760
  const dosage = {};
10468
10761
  const repeat = {};
10469
10762
  let hasRepeat = false;
@@ -10563,21 +10856,36 @@ function canonicalToFhir(clause, textOverride, options) {
10563
10856
  }
10564
10857
  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)) {
10565
10858
  const siteCoding = selectCanonicalSiteCoding(clause.site, options);
10566
- const siteTextElement = buildTranslationPrimitiveElement(siteCoding == null ? void 0 : siteCoding.i18n);
10859
+ const siteTextElement = (options == null ? void 0 : options.includeTranslationExtensions) ? buildTranslationPrimitiveElement((_p = clause.site.i18n) != null ? _p : siteCoding == null ? void 0 : siteCoding.i18n) : void 0;
10860
+ const siteTargetCount = (_r = (_q = clause.site) == null ? void 0 : _q.administrationTargetCount) != null ? _r : getBodySiteAdministrationTargetCount(clause.site);
10861
+ const inferredSiteTargetCount = getBodySiteAdministrationTargetCount({
10862
+ text: (_s = clause.site) == null ? void 0 : _s.text,
10863
+ coding: siteCoding ? {
10864
+ code: siteCoding.code,
10865
+ display: siteCoding.display,
10866
+ system: siteCoding.system,
10867
+ i18n: siteCoding.i18n
10868
+ } : void 0
10869
+ });
10870
+ const siteTargetCountExtension = siteTargetCount !== void 0 && siteTargetCount !== inferredSiteTargetCount ? buildBodySiteAdministrationTargetCountExtension(siteTargetCount) : void 0;
10871
+ const siteExtensions = [
10872
+ ...(_u = buildBodySiteSpatialRelationExtensions((_t = clause.site) == null ? void 0 : _t.spatialRelation)) != null ? _u : [],
10873
+ ...siteTargetCountExtension ? [siteTargetCountExtension] : []
10874
+ ];
10567
10875
  dosage.site = __spreadProps(__spreadValues({
10568
- text: (_p = clause.site) == null ? void 0 : _p.text
10876
+ text: (_v = clause.site) == null ? void 0 : _v.text
10569
10877
  }, siteTextElement ? { _text: siteTextElement } : {}), {
10570
- coding: buildSiteCodingArray(siteCoding),
10571
- extension: buildBodySiteSpatialRelationExtensions((_q = clause.site) == null ? void 0 : _q.spatialRelation)
10878
+ coding: buildSiteCodingArray(siteCoding, options),
10879
+ extension: siteExtensions.length ? siteExtensions : void 0
10572
10880
  });
10573
10881
  }
10574
- if (((_r = clause.method) == null ? void 0 : _r.text) || ((_s = clause.method) == null ? void 0 : _s._text) || ((_u = (_t = clause.method) == null ? void 0 : _t.coding) == null ? void 0 : _u.code)) {
10882
+ 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)) {
10575
10883
  dosage.method = {
10576
- text: (_v = clause.method) == null ? void 0 : _v.text,
10577
- _text: clonePrimitiveElement((_w = clause.method) == null ? void 0 : _w._text),
10578
- coding: ((_y = (_x = clause.method) == null ? void 0 : _x.coding) == null ? void 0 : _y.code) ? [
10884
+ text: (_A = clause.method) == null ? void 0 : _A.text,
10885
+ _text: clonePrimitiveElement((_B = clause.method) == null ? void 0 : _B._text),
10886
+ coding: ((_D = (_C = clause.method) == null ? void 0 : _C.coding) == null ? void 0 : _D.code) ? [
10579
10887
  {
10580
- system: (_z = clause.method.coding.system) != null ? _z : SNOMED_SYSTEM5,
10888
+ system: (_E = clause.method.coding.system) != null ? _E : SNOMED_SYSTEM5,
10581
10889
  code: clause.method.coding.code,
10582
10890
  display: clause.method.coding.display,
10583
10891
  _display: clonePrimitiveElement(clause.method.coding._display)
@@ -10585,14 +10893,14 @@ function canonicalToFhir(clause, textOverride, options) {
10585
10893
  ] : void 0
10586
10894
  };
10587
10895
  }
10588
- if ((_A = clause.additionalInstructions) == null ? void 0 : _A.length) {
10896
+ if ((_F = clause.additionalInstructions) == null ? void 0 : _F.length) {
10589
10897
  dosage.additionalInstruction = [];
10590
10898
  for (const instruction of clause.additionalInstructions) {
10591
10899
  dosage.additionalInstruction.push({
10592
10900
  text: instruction.text,
10593
- coding: ((_B = instruction.coding) == null ? void 0 : _B.code) ? [
10901
+ coding: ((_G = instruction.coding) == null ? void 0 : _G.code) ? [
10594
10902
  {
10595
- system: (_C = instruction.coding.system) != null ? _C : SNOMED_SYSTEM5,
10903
+ system: (_H = instruction.coding.system) != null ? _H : SNOMED_SYSTEM5,
10596
10904
  code: instruction.coding.code,
10597
10905
  display: instruction.coding.display
10598
10906
  }
@@ -10600,9 +10908,9 @@ function canonicalToFhir(clause, textOverride, options) {
10600
10908
  });
10601
10909
  }
10602
10910
  }
10603
- if ((_D = clause.prn) == null ? void 0 : _D.enabled) {
10911
+ if ((_I = clause.prn) == null ? void 0 : _I.enabled) {
10604
10912
  dosage.asNeededBoolean = true;
10605
- const reasons = ((_E = clause.prn.reasons) == null ? void 0 : _E.length) ? clause.prn.reasons : clause.prn.reason ? [clause.prn.reason] : [];
10913
+ const reasons = ((_J = clause.prn.reasons) == null ? void 0 : _J.length) ? clause.prn.reasons : clause.prn.reason ? [clause.prn.reason] : [];
10606
10914
  if (reasons.length) {
10607
10915
  dosage.asNeededFor = [];
10608
10916
  for (const reason of reasons) {
@@ -10610,15 +10918,18 @@ function canonicalToFhir(clause, textOverride, options) {
10610
10918
  if (reason.text) {
10611
10919
  concept.text = reason.text;
10612
10920
  }
10613
- const reasonTextElement = buildTranslationPrimitiveElement((_F = reason.coding) == null ? void 0 : _F.i18n);
10921
+ const reasonTextElement = (options == null ? void 0 : options.includeTranslationExtensions) ? buildTranslationPrimitiveElement((_L = reason.i18n) != null ? _L : (_K = reason.coding) == null ? void 0 : _K.i18n) : void 0;
10614
10922
  if (reasonTextElement) {
10615
10923
  concept._text = reasonTextElement;
10616
10924
  }
10617
- if ((_G = reason.coding) == null ? void 0 : _G.code) {
10618
- const displayElement = (_H = clonePrimitiveElement(reason.coding._display)) != null ? _H : buildTranslationPrimitiveElement(reason.coding.i18n);
10925
+ if ((_M = reason.coding) == null ? void 0 : _M.code) {
10926
+ const displayElement = (options == null ? void 0 : options.includeTranslationExtensions) ? mergeTranslationPrimitiveElement(
10927
+ reason.coding._display,
10928
+ reason.coding.i18n
10929
+ ) : clonePrimitiveElement(reason.coding._display);
10619
10930
  concept.coding = [
10620
10931
  __spreadProps(__spreadValues({
10621
- system: (_I = reason.coding.system) != null ? _I : SNOMED_SYSTEM5,
10932
+ system: (_N = reason.coding.system) != null ? _N : SNOMED_SYSTEM5,
10622
10933
  code: reason.coding.code,
10623
10934
  display: reason.coding.display
10624
10935
  }, displayElement ? { _display: displayElement } : {}), {
@@ -10658,14 +10969,19 @@ function canonicalFromFhir(dosage) {
10658
10969
  const siteCoding = selectPreferredSiteCoding(dosage.site);
10659
10970
  const siteSpatialRelation = parseBodySiteSpatialRelationExtension(dosage.site);
10660
10971
  const siteText = getFallbackSiteText(dosage.site);
10661
- if (siteText || (siteCoding == null ? void 0 : siteCoding.code) || siteSpatialRelation) {
10972
+ const siteI18n = codeableConceptTranslationI18n(dosage.site, siteCoding);
10973
+ const siteAdministrationTargetCount = getBodySiteAdministrationTargetCount(dosage.site);
10974
+ if (siteText || (siteCoding == null ? void 0 : siteCoding.code) || siteSpatialRelation || siteAdministrationTargetCount !== void 0) {
10662
10975
  clause.site = {
10663
10976
  text: siteText,
10977
+ i18n: siteI18n.text,
10664
10978
  spatialRelation: siteSpatialRelation,
10979
+ administrationTargetCount: siteAdministrationTargetCount,
10665
10980
  coding: (siteCoding == null ? void 0 : siteCoding.code) ? {
10666
10981
  code: siteCoding.code,
10667
10982
  display: siteCoding.display,
10668
- system: siteCoding.system
10983
+ system: siteCoding.system,
10984
+ i18n: siteI18n.display
10669
10985
  } : void 0,
10670
10986
  source: "text"
10671
10987
  };
@@ -10720,16 +11036,21 @@ function canonicalFromFhir(dosage) {
10720
11036
  };
10721
11037
  }
10722
11038
  const prnReasons = ((_y = dosage.asNeededFor) == null ? void 0 : _y.length) ? dosage.asNeededFor.map((concept) => {
10723
- var _a3;
11039
+ var _a3, _b2;
10724
11040
  const coding = (_a3 = concept.coding) == null ? void 0 : _a3.find((code) => Boolean(code.code));
11041
+ const defaultDef = (coding == null ? void 0 : coding.code) ? findPrnReasonDefinitionByCoding((_b2 = coding.system) != null ? _b2 : SNOMED_SYSTEM5, coding.code) : void 0;
11042
+ const i18n = codeableConceptTranslationI18n(concept, coding, defaultDef == null ? void 0 : defaultDef.i18n);
10725
11043
  return {
10726
11044
  text: getFallbackPrnReasonText(concept),
11045
+ i18n: i18n.text,
10727
11046
  spatialRelation: parseBodySiteSpatialRelationExtension(concept),
10728
11047
  coding: (coding == null ? void 0 : coding.code) ? {
10729
11048
  code: coding.code,
10730
11049
  display: coding.display,
10731
11050
  system: coding.system,
10732
- extension: cloneExtensions(coding.extension)
11051
+ extension: cloneExtensions(coding.extension),
11052
+ _display: clonePrimitiveElement(coding._display),
11053
+ i18n: i18n.display
10733
11054
  } : void 0
10734
11055
  };
10735
11056
  }) : void 0;
@@ -11736,73 +12057,22 @@ function summarizeMealTimingGroupThai(group) {
11736
12057
  }
11737
12058
  return `${relationText[group.relation]}${joinMealNamesThai(meals)}`;
11738
12059
  }
11739
- function collectWhenPhrasesThai(schedule, options) {
11740
- var _a2;
11741
- const when = (_a2 = schedule == null ? void 0 : schedule.when) != null ? _a2 : [];
11742
- if (!when.length) {
11743
- return [];
11744
- }
11745
- const unique = [];
11746
- const seen = /* @__PURE__ */ new Set();
11747
- let hasSpecificAfter = false;
11748
- let hasSpecificBefore = false;
11749
- let hasSpecificWith = false;
11750
- for (const code of when) {
11751
- if (!seen.has(code)) {
11752
- seen.add(code);
11753
- unique.push(code);
11754
- if (code === "PCM" /* After Breakfast */ || code === "PCD" /* After Lunch */ || code === "PCV" /* After Dinner */) {
11755
- hasSpecificAfter = true;
11756
- }
11757
- if (code === "ACM" /* Before Breakfast */ || code === "ACD" /* Before Lunch */ || code === "ACV" /* Before Dinner */) {
11758
- hasSpecificBefore = true;
11759
- }
11760
- if (code === "CM" /* Breakfast */ || code === "CD" /* Lunch */ || code === "CV" /* Dinner */) {
11761
- hasSpecificWith = true;
11762
- }
11763
- }
11764
- }
11765
- const filtered = [];
11766
- for (const code of unique) {
11767
- if (code === "PC" /* After Meal */ && hasSpecificAfter) {
11768
- continue;
11769
- }
11770
- if (code === "AC" /* Before Meal */ && hasSpecificBefore) {
11771
- continue;
11772
- }
11773
- if (code === "C" /* Meal */ && hasSpecificWith) {
11774
- continue;
11775
- }
11776
- filtered.push(code);
11777
- }
11778
- const mealGroup = getMealTimingGroup(filtered, options);
11779
- if (!mealGroup) {
11780
- const phrases2 = [];
11781
- for (const code of filtered) {
11782
- const text = WHEN_TEXT_THAI[code];
11783
- if (text) {
11784
- phrases2.push(text);
11785
- }
11786
- }
11787
- return phrases2;
11788
- }
11789
- const groupedCodes = new Set(mealGroup.codes);
11790
- const phrases = [];
11791
- let insertedGroup = false;
11792
- for (const code of filtered) {
11793
- if (groupedCodes.has(code)) {
11794
- if (!insertedGroup) {
11795
- phrases.push(summarizeMealTimingGroupThai(mealGroup));
11796
- insertedGroup = true;
11797
- }
11798
- continue;
12060
+ var TH_TIMING_GRAMMAR = {
12061
+ whenText: WHEN_TEXT_THAI,
12062
+ joinList: joinWithAndThai,
12063
+ summarizeMealTimingGroup: summarizeMealTimingGroupThai,
12064
+ bedtimeJoinStyle: (dailyCount) => {
12065
+ if (dailyCount === 1) {
12066
+ return "adjacent";
11799
12067
  }
11800
- const text = WHEN_TEXT_THAI[code];
11801
- if (text) {
11802
- phrases.push(text);
12068
+ if (dailyCount === 2 || dailyCount === 3 || dailyCount === 4) {
12069
+ return "conjunction";
11803
12070
  }
12071
+ return "separate";
11804
12072
  }
11805
- return phrases;
12073
+ };
12074
+ function collectWhenPhrasesThai(schedule, options) {
12075
+ return collectLocalizedWhenPhrases(schedule, TH_TIMING_GRAMMAR, options);
11806
12076
  }
11807
12077
  function joinWithAndThai(parts) {
11808
12078
  if (!parts.length) {
@@ -11816,20 +12086,14 @@ function joinWithAndThai(parts) {
11816
12086
  }
11817
12087
  return `${parts.slice(0, -1).join(", ")} \u0E41\u0E25\u0E30 ${parts[parts.length - 1]}`;
11818
12088
  }
11819
- function combineFrequencyAndEventsThai(frequency, events) {
11820
- if (!frequency) {
11821
- if (!events.length) {
11822
- return {};
11823
- }
11824
- return { event: joinWithAndThai(events) };
11825
- }
11826
- if (!events.length) {
11827
- return { frequency };
11828
- }
11829
- if (events.length === 1 && events[0] === "\u0E01\u0E48\u0E2D\u0E19\u0E19\u0E2D\u0E19" && frequency.includes("\u0E27\u0E31\u0E19\u0E25\u0E30")) {
11830
- return { frequency: `${frequency} \u0E41\u0E25\u0E30 ${events[0]}` };
11831
- }
11832
- return { frequency, event: joinWithAndThai(events) };
12089
+ function combineFrequencyAndEventsThai(schedule, frequency, events, options) {
12090
+ return combineLocalizedFrequencyAndEvents(
12091
+ schedule,
12092
+ frequency,
12093
+ events,
12094
+ TH_TIMING_GRAMMAR,
12095
+ options
12096
+ );
11833
12097
  }
11834
12098
  function isOralRouteThai(clause) {
11835
12099
  var _a2, _b, _c;
@@ -11888,7 +12152,7 @@ function buildRoutePhraseThai(clause, grammar, hasSite) {
11888
12152
  return text;
11889
12153
  }
11890
12154
  function formatSiteThai(clause, grammar) {
11891
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
12155
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
11892
12156
  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());
11893
12157
  const lower = text == null ? void 0 : text.toLowerCase();
11894
12158
  const codingCode = (_g = (_f = clause.site) == null ? void 0 : _f.coding) == null ? void 0 : _g.code;
@@ -11903,14 +12167,14 @@ function formatSiteThai(clause, grammar) {
11903
12167
  if (isVaginalRoute && isVaginaSite) {
11904
12168
  return void 0;
11905
12169
  }
11906
- 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);
12170
+ 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);
11907
12171
  if (!translated) {
11908
12172
  return void 0;
11909
12173
  }
11910
- if (((_n = clause.route) == null ? void 0 : _n.code) === RouteCode["Nasal route"]) {
12174
+ if (((_u = clause.route) == null ? void 0 : _u.code) === RouteCode["Nasal route"]) {
11911
12175
  return `\u0E40\u0E02\u0E49\u0E32${translated}`;
11912
12176
  }
11913
- const preposition = (_o = grammar.sitePreposition) != null ? _o : "\u0E17\u0E35\u0E48";
12177
+ const preposition = (_v = grammar.sitePreposition) != null ? _v : "\u0E17\u0E35\u0E48";
11914
12178
  const separator = /^[\u0E00-\u0E7F]/.test(translated) ? "" : " ";
11915
12179
  return `${preposition}${separator}${translated}`.trim();
11916
12180
  }
@@ -12208,7 +12472,7 @@ function formatLongThai(clause, options) {
12208
12472
  eventParts.push(`\u0E40\u0E27\u0E25\u0E32 ${timeStrings.join(", ")}`);
12209
12473
  }
12210
12474
  }
12211
- const timing = combineFrequencyAndEventsThai(frequencyPart, eventParts);
12475
+ const timing = combineFrequencyAndEventsThai(schedule, frequencyPart, eventParts, options);
12212
12476
  const dayPart = describeDayOfWeekThai(schedule);
12213
12477
  const countPart = schedule.count !== void 0 && !standaloneOccurrenceCount ? `\u0E08\u0E33\u0E19\u0E27\u0E19 ${stripTrailingZero2(schedule.count)} \u0E04\u0E23\u0E31\u0E49\u0E07` : void 0;
12214
12478
  const durationPart = describeDurationThai(schedule);
@@ -12640,6 +12904,12 @@ function sameOptionalScalar(left, right) {
12640
12904
  function mergeOptionalScalar(left, right) {
12641
12905
  return left !== void 0 ? left : right;
12642
12906
  }
12907
+ function mergeI18nRecords2(left, right) {
12908
+ if (!left && !right) {
12909
+ return void 0;
12910
+ }
12911
+ return __spreadValues(__spreadValues({}, left != null ? left : {}), right != null ? right : {});
12912
+ }
12643
12913
  function sameCoding(left, right) {
12644
12914
  var _a2, _b;
12645
12915
  if (!(left == null ? void 0 : left.code) || !(right == null ? void 0 : right.code)) {
@@ -12702,6 +12972,7 @@ function mergeSite(left, right, context) {
12702
12972
  }
12703
12973
  return {
12704
12974
  text: mergeOptionalScalar(left.text, right.text),
12975
+ i18n: mergeI18nRecords2(left.i18n, right.i18n),
12705
12976
  source: mergeOptionalScalar(left.source, right.source),
12706
12977
  coding: mergeOptionalScalar(left.coding, right.coding),
12707
12978
  spatialRelation: mergeOptionalScalar(left.spatialRelation, right.spatialRelation),
@@ -13049,6 +13320,7 @@ var TOKEN_SITE_CANDIDATES = {
13049
13320
  le: [{ text: "left eye", route: RouteCode["Ophthalmic route"], source: "abbreviation" }],
13050
13321
  ou: [{ text: "both eyes", route: RouteCode["Ophthalmic route"], source: "abbreviation" }],
13051
13322
  be: [{ text: "both eyes", route: RouteCode["Ophthalmic route"], source: "abbreviation" }],
13323
+ au: [{ text: "both ears", route: RouteCode["Otic route"], source: "abbreviation" }],
13052
13324
  vod: [
13053
13325
  {
13054
13326
  text: "right eye",
@@ -15598,6 +15870,9 @@ function inferRouteFromContext(ctx) {
15598
15870
  // src/hpsg/rules/site-rules.ts
15599
15871
  function siteBoundary(lower, context) {
15600
15872
  var _a2, _b, _c;
15873
+ if (SITE_MULTIPLICITY_WORDS.has(lower)) {
15874
+ return false;
15875
+ }
15601
15876
  const siteLike = Boolean(resolveBodySitePhrase(lower, (_a2 = context.options) == null ? void 0 : _a2.siteCodeMap, {
15602
15877
  bodySiteContext: (_c = (_b = context.options) == null ? void 0 : _b.context) == null ? void 0 : _c.bodySiteContext
15603
15878
  }));
@@ -15659,7 +15934,7 @@ function shouldSuppressEyeSiteAbbreviation(context, start, lower) {
15659
15934
  }
15660
15935
  function siteLexicalRule() {
15661
15936
  return lexicalRule("hpsg.lex.site", (context, start) => {
15662
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
15937
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
15663
15938
  const token = (_a2 = tokensAvailable(context, start, 1)) == null ? void 0 : _a2[0];
15664
15939
  if (!token) {
15665
15940
  return [];
@@ -15725,7 +16000,7 @@ function siteLexicalRule() {
15725
16000
  continue;
15726
16001
  }
15727
16002
  phraseTokens.push(candidate);
15728
- if (!SITE_DISPLAY_FILLERS.has(candidateLower)) {
16003
+ if (!SITE_DISPLAY_FILLERS.has(candidateLower) || SITE_MULTIPLICITY_WORDS.has(candidateLower)) {
15729
16004
  displayTokens.push(candidate);
15730
16005
  }
15731
16006
  }
@@ -15771,6 +16046,7 @@ function siteLexicalRule() {
15771
16046
  valence: {
15772
16047
  site: {
15773
16048
  text: displayText,
16049
+ i18n: (_r = resolved == null ? void 0 : resolved.definition) == null ? void 0 : _r.i18n,
15774
16050
  source: "text",
15775
16051
  coding: resolved == null ? void 0 : resolved.coding,
15776
16052
  spatialRelation: resolved == null ? void 0 : resolved.spatialRelation,
@@ -15798,7 +16074,7 @@ function siteLexicalRule() {
15798
16074
  }
15799
16075
  function bareSiteLexicalRule() {
15800
16076
  return lexicalRule("hpsg.lex.site.bare", (context, start) => {
15801
- var _a2, _b, _c, _d, _e, _f, _g, _h;
16077
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i;
15802
16078
  const signs = [];
15803
16079
  const first = (_a2 = tokensAvailable(context, start, 1)) == null ? void 0 : _a2[0];
15804
16080
  if (!first) {
@@ -15844,6 +16120,7 @@ function bareSiteLexicalRule() {
15844
16120
  valence: {
15845
16121
  site: {
15846
16122
  text: displayText,
16123
+ i18n: (_h = resolved.definition) == null ? void 0 : _h.i18n,
15847
16124
  source: "text",
15848
16125
  coding: resolved.coding,
15849
16126
  spatialRelation: resolved.spatialRelation,
@@ -15851,7 +16128,7 @@ function bareSiteLexicalRule() {
15851
16128
  originalText,
15852
16129
  text: sourceText,
15853
16130
  normalized: sourceText.toLowerCase(),
15854
- canonical: (_h = resolved.resolutionCanonical) != null ? _h : normalizeBodySiteKey(displayText),
16131
+ canonical: (_i = resolved.resolutionCanonical) != null ? _i : normalizeBodySiteKey(displayText),
15855
16132
  isProbe,
15856
16133
  inputText: context.state.input,
15857
16134
  sourceText,
@@ -16558,7 +16835,7 @@ function applyRouteSiteDefault(state) {
16558
16835
  system: (_i = definition.coding.system) != null ? _i : "http://snomed.info/sct",
16559
16836
  code: definition.coding.code,
16560
16837
  display: definition.coding.display,
16561
- i18n: definition.i18n
16838
+ i18n: mergeI18nRecords(definition.i18n, definition.coding.i18n)
16562
16839
  };
16563
16840
  }
16564
16841
  }
@@ -17871,13 +18148,16 @@ function applySiteDefinition(internal, definition) {
17871
18148
  code: coding.code,
17872
18149
  display: coding.display,
17873
18150
  system: (_a2 = coding.system) != null ? _a2 : SNOMED_SYSTEM9,
17874
- i18n: definition.i18n
18151
+ i18n: mergeI18nRecords(definition.i18n, coding.i18n)
17875
18152
  } : void 0;
17876
18153
  if (definition.text) {
17877
18154
  internal.siteText = definition.text;
17878
18155
  } else if ((_b = internal.siteLookupRequest) == null ? void 0 : _b.text) {
17879
18156
  internal.siteText = internal.siteLookupRequest.text;
17880
18157
  }
18158
+ if (definition.administrationTargetCount !== void 0) {
18159
+ internal.siteAdministrationTargetCount = definition.administrationTargetCount;
18160
+ }
17881
18161
  if (definition.spatialRelation) {
17882
18162
  internal.siteSpatialRelation = definition.spatialRelation;
17883
18163
  } else if ((_c = internal.siteLookupRequest) == null ? void 0 : _c.spatialRelation) {
@@ -18228,8 +18508,11 @@ function seedKnownSiteCoding(state) {
18228
18508
  system: (_b = definition.coding.system) != null ? _b : SNOMED_SYSTEM10,
18229
18509
  code: definition.coding.code,
18230
18510
  display: definition.coding.display,
18231
- i18n: definition.i18n
18511
+ i18n: mergeI18nRecords(definition.i18n, definition.coding.i18n)
18232
18512
  };
18513
+ if (state.siteAdministrationTargetCount === void 0) {
18514
+ state.siteAdministrationTargetCount = definition.administrationTargetCount;
18515
+ }
18233
18516
  }
18234
18517
  function parseClauseState(input, options) {
18235
18518
  const tokens = tokenize(input);
@@ -19560,6 +19843,21 @@ function normalizeClock(clock) {
19560
19843
  }
19561
19844
  return `${pad(hour)}:${pad(minute)}:${pad(second)}`;
19562
19845
  }
19846
+ function doseUnitSupportsPerTargetMultiplication(doseUnit, context) {
19847
+ const semantics = getDoseUnitSemantics(doseUnit, context);
19848
+ if (!semantics) {
19849
+ return false;
19850
+ }
19851
+ return semantics.kind !== "metric" && semantics.kind !== "biologic_unit";
19852
+ }
19853
+ function getAdministrationTargetMultiplier(dosage, context) {
19854
+ var _a2, _b, _c, _d;
19855
+ const doseUnit = (_c = (_b = (_a2 = dosage.doseAndRate) == null ? void 0 : _a2[0]) == null ? void 0 : _b.doseQuantity) == null ? void 0 : _c.unit;
19856
+ if (!doseUnitSupportsPerTargetMultiplication(doseUnit, context)) {
19857
+ return 1;
19858
+ }
19859
+ return (_d = getBodySiteAdministrationTargetCount(dosage.site)) != null ? _d : 1;
19860
+ }
19563
19861
  function getDateTimeFormat(timeZone) {
19564
19862
  let formatter = dateTimeFormatCache.get(timeZone);
19565
19863
  if (!formatter) {
@@ -20979,7 +21277,8 @@ function calculateTotalUnitsSingle(options) {
20979
21277
  2e3
20980
21278
  );
20981
21279
  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;
20982
- let totalUnits = roundCalculatedUnits(count * doseQuantity);
21280
+ const targetMultiplier = getAdministrationTargetMultiplier(dosage, context);
21281
+ let totalUnits = roundCalculatedUnits(count * doseQuantity * targetMultiplier);
20983
21282
  if (roundToMultiple && roundToMultiple > 0) {
20984
21283
  totalUnits = roundCalculatedUnits(Math.ceil(totalUnits / roundToMultiple) * roundToMultiple);
20985
21284
  }
@@ -21530,11 +21829,12 @@ function cloneBodySiteCoding2(coding) {
21530
21829
  return {
21531
21830
  code: coding.code,
21532
21831
  display: coding.display,
21533
- system: coding.system
21832
+ system: coding.system,
21833
+ i18n: cloneI18nRecord(coding.i18n)
21534
21834
  };
21535
21835
  }
21536
21836
  function buildNormalizedMetaFromClause(clause, fhir, options) {
21537
- 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;
21837
+ 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;
21538
21838
  const additionalInstructions = ((_a2 = clause.additionalInstructions) == null ? void 0 : _a2.length) ? clause.additionalInstructions.map((instruction) => ({
21539
21839
  text: instruction.text,
21540
21840
  coding: cloneCoding2(instruction.coding)
@@ -21547,22 +21847,23 @@ function buildNormalizedMetaFromClause(clause, fhir, options) {
21547
21847
  unit: (_h = clause.dose) == null ? void 0 : _h.unit,
21548
21848
  unitKind: unitSemantics == null ? void 0 : unitSemantics.kind,
21549
21849
  unitSemantics,
21550
- 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) ? {
21551
- text: (_m = clause.site) == null ? void 0 : _m.text,
21850
+ 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 ? {
21851
+ text: (_n = clause.site) == null ? void 0 : _n.text,
21552
21852
  coding: siteCoding,
21553
- spatialRelation: cloneBodySiteSpatialRelation((_n = clause.site) == null ? void 0 : _n.spatialRelation)
21853
+ spatialRelation: cloneBodySiteSpatialRelation((_o = clause.site) == null ? void 0 : _o.spatialRelation),
21854
+ administrationTargetCount: (_p = clause.site) == null ? void 0 : _p.administrationTargetCount
21554
21855
  } : void 0,
21555
- method: ((_o = clause.method) == null ? void 0 : _o.text) || ((_q = (_p = clause.method) == null ? void 0 : _p.coding) == null ? void 0 : _q.code) ? {
21556
- text: (_r = clause.method) == null ? void 0 : _r.text,
21557
- coding: cloneCoding2((_s = clause.method) == null ? void 0 : _s.coding)
21856
+ method: ((_q = clause.method) == null ? void 0 : _q.text) || ((_s = (_r = clause.method) == null ? void 0 : _r.coding) == null ? void 0 : _s.code) ? {
21857
+ text: (_t = clause.method) == null ? void 0 : _t.text,
21858
+ coding: cloneCoding2((_u = clause.method) == null ? void 0 : _u.coding)
21558
21859
  } : void 0,
21559
21860
  patientInstruction: clause.patientInstruction,
21560
- 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) ? {
21561
- text: (_z = (_y = clause.prn) == null ? void 0 : _y.reason) == null ? void 0 : _z.text,
21562
- coding: cloneCoding2((_B = (_A = clause.prn) == null ? void 0 : _A.reason) == null ? void 0 : _B.coding),
21563
- spatialRelation: cloneBodySiteSpatialRelation((_D = (_C = clause.prn) == null ? void 0 : _C.reason) == null ? void 0 : _D.spatialRelation)
21861
+ 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) ? {
21862
+ text: (_B = (_A = clause.prn) == null ? void 0 : _A.reason) == null ? void 0 : _B.text,
21863
+ coding: cloneCoding2((_D = (_C = clause.prn) == null ? void 0 : _C.reason) == null ? void 0 : _D.coding),
21864
+ spatialRelation: cloneBodySiteSpatialRelation((_F = (_E = clause.prn) == null ? void 0 : _E.reason) == null ? void 0 : _F.spatialRelation)
21564
21865
  } : void 0,
21565
- prnReasons: ((_F = (_E = clause.prn) == null ? void 0 : _E.reasons) == null ? void 0 : _F.length) ? clause.prn.reasons.map((reason) => ({
21866
+ prnReasons: ((_H = (_G = clause.prn) == null ? void 0 : _G.reasons) == null ? void 0 : _H.length) ? clause.prn.reasons.map((reason) => ({
21566
21867
  text: reason.text,
21567
21868
  coding: cloneCoding2(reason.coding),
21568
21869
  spatialRelation: cloneBodySiteSpatialRelation(reason.spatialRelation)
@@ -21577,7 +21878,8 @@ function buildParseResult(state, options) {
21577
21878
  const shortText = formatCanonicalClause(clause, "short", localization, options);
21578
21879
  const longText = formatCanonicalClause(clause, "long", localization, options);
21579
21880
  const fhir = canonicalToFhir(clause, longText, {
21580
- bodySitePostcoordination: options == null ? void 0 : options.bodySitePostcoordination
21881
+ bodySitePostcoordination: options == null ? void 0 : options.bodySitePostcoordination,
21882
+ includeTranslationExtensions: Boolean((options == null ? void 0 : options.locale) || (options == null ? void 0 : options.i18n))
21581
21883
  });
21582
21884
  const consumedTokens = [];
21583
21885
  const leftoverParts = [];
@@ -21759,6 +22061,7 @@ export {
21759
22061
  AdviceModality,
21760
22062
  AdvicePolarity,
21761
22063
  AdviceRelation,
22064
+ BODY_SITE_ADMINISTRATION_TARGET_COUNT_EXTENSION_URL,
21762
22065
  BODY_SITE_SPATIAL_RELATION_EXTENSION_URL,
21763
22066
  DEFAULT_BODY_SITE_SNOMED,
21764
22067
  DEFAULT_BODY_SITE_SNOMED_SOURCE,
@@ -21783,6 +22086,7 @@ export {
21783
22086
  SNOMED_CT_TOPOGRAPHICAL_MODIFIER_CODE,
21784
22087
  SNOMED_CT_TOPOGRAPHICAL_MODIFIER_DISPLAY,
21785
22088
  SNOMED_SYSTEM,
22089
+ buildBodySiteAdministrationTargetCountExtension,
21786
22090
  buildBodySiteSpatialRelationExtension,
21787
22091
  buildBodySiteSpatialRelationExtensions,
21788
22092
  buildBodySiteTopographicalModifierCoding,
@@ -21796,6 +22100,7 @@ export {
21796
22100
  formatSig,
21797
22101
  formatSigBatch,
21798
22102
  fromFhirDosage,
22103
+ getBodySiteAdministrationTargetCount,
21799
22104
  getBodySiteCode,
21800
22105
  getBodySiteCodeAsync,
21801
22106
  getBodySiteText,
@@ -21814,6 +22119,7 @@ export {
21814
22119
  lookupBodySite,
21815
22120
  lookupBodySiteAsync,
21816
22121
  nextDueDoses,
22122
+ parseBodySiteAdministrationTargetCountExtension,
21817
22123
  parseBodySiteSpatialRelationExtension,
21818
22124
  parseSig,
21819
22125
  parseSigAsync,