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.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";
@@ -497,8 +765,8 @@ function mergeTranslationPrimitiveElement(base, translations) {
497
765
  if (extension.url !== FHIR_TRANSLATION_EXTENSION_URL) {
498
766
  continue;
499
767
  }
500
- const { locale } = getTranslationParts(extension);
501
- if (locale) {
768
+ const { locale, content } = getTranslationParts(extension);
769
+ if (locale && content) {
502
770
  existingTranslationLocales.add(locale);
503
771
  }
504
772
  }
@@ -1167,10 +1435,11 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1167
1435
  }
1168
1436
  },
1169
1437
  {
1170
- 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"],
1171
1439
  definition: {
1172
1440
  coding: { code: "40638003", display: "Structure of both eyes" },
1173
1441
  text: "both eyes",
1442
+ administrationTargetCount: 2,
1174
1443
  i18n: { th: "\u0E15\u0E32\u0E17\u0E31\u0E49\u0E07\u0E2A\u0E2D\u0E07\u0E02\u0E49\u0E32\u0E07" },
1175
1444
  routeHint: RouteCode["Ophthalmic route"]
1176
1445
  }
@@ -1184,10 +1453,19 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1184
1453
  }
1185
1454
  },
1186
1455
  {
1187
- 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
+ ],
1188
1465
  definition: {
1189
1466
  coding: { code: "34338003", display: "Both ears" },
1190
1467
  text: "both ears",
1468
+ administrationTargetCount: 2,
1191
1469
  routeHint: RouteCode["Otic route"]
1192
1470
  }
1193
1471
  },
@@ -1205,10 +1483,18 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1205
1483
  }
1206
1484
  },
1207
1485
  {
1208
- 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
+ ],
1209
1494
  definition: {
1210
1495
  coding: { code: "181178004", display: "Entire external auditory canal" },
1211
1496
  text: "both ear canals",
1497
+ administrationTargetCount: 2,
1212
1498
  routeHint: RouteCode["Otic route"]
1213
1499
  }
1214
1500
  },
@@ -1245,8 +1531,12 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1245
1531
  }
1246
1532
  },
1247
1533
  {
1248
- names: ["nostril", "nostrils"],
1249
- 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
+ }
1250
1540
  },
1251
1541
  {
1252
1542
  names: ["left nostril", "left naris"],
@@ -1263,8 +1553,22 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1263
1553
  }
1264
1554
  },
1265
1555
  {
1266
- names: ["nares", "anterior nares"],
1267
- 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
+ }
1268
1572
  },
1269
1573
  {
1270
1574
  names: ["nose"],
@@ -1790,6 +2094,24 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
1790
2094
  names: ["affected area", "affected areas", "affected site", "\u0E1A\u0E23\u0E34\u0E40\u0E27\u0E13\u0E17\u0E35\u0E48\u0E40\u0E1B\u0E47\u0E19"],
1791
2095
  definition: { text: "affected area", routeHint: RouteCode["Topical route"] }
1792
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
+ },
1793
2115
  {
1794
2116
  names: ["left head", "left side of head"],
1795
2117
  definition: { coding: { code: "64237003", display: "Structure of left half of head" }, routeHint: RouteCode["Topical route"] }
@@ -4369,6 +4691,7 @@ var BODY_SITE_FEATURE_SCORE_BONUS = bodySiteFeatureScoreBonus(lexical_classes_de
4369
4691
  var CONNECTORS = setOf(lexical_classes_default.connectors);
4370
4692
  var ROUTE_SITE_PREPOSITIONS = setOf(lexical_classes_default.routeSitePrepositions);
4371
4693
  var SITE_DISPLAY_FILLERS = SITE_FILLERS;
4694
+ var SITE_MULTIPLICITY_WORDS = setOf(["both", "each", "bilateral"]);
4372
4695
  var NON_SITE_ANCHORED_PHRASES = setOf(lexical_classes_default.nonSiteAnchoredPhrases);
4373
4696
  var EXTERNAL_SITE_LOCATIVE_PREFIXES = setOf(lexical_classes_default.externalSiteLocativePrefixes);
4374
4697
  var ROUTE_BLOCKED_BY_FOLLOWING_PARTITIVE_HEADS = setOf(
@@ -4857,195 +5180,61 @@ function buildSpatialRelation(features, sourceText, customSiteMap) {
4857
5180
  case "nominal":
4858
5181
  return void 0;
4859
5182
  }
4860
- }
4861
- function inferPreferredPreposition(canonical, features, definition) {
4862
- if (features.kind === "locative") {
4863
- return void 0;
4864
- }
4865
- const routeHint = definition == null ? void 0 : definition.routeHint;
4866
- if (routeHint === RouteCode["Topical route"] || routeHint === RouteCode["Transdermal route"]) {
4867
- return "to";
4868
- }
4869
- 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"]) {
4870
- return routeHint === RouteCode["Nasal route"] ? "into" : "to";
4871
- }
4872
- const words = canonical.split(/\s+/).filter((word) => word.length > 0);
4873
- for (const word of words) {
4874
- if (OTIC_SITE_WORDS.has(word) || OPHTHALMIC_SITE_WORDS.has(word)) {
4875
- return "in";
4876
- }
4877
- if (NASAL_SITE_WORDS.has(word)) {
4878
- return "into";
4879
- }
4880
- }
4881
- return void 0;
4882
- }
4883
- function resolveBodySitePhrase(text, customSiteMap, context) {
4884
- var _a2, _b, _c, _d, _e, _f, _g;
4885
- const trimmed = text.trim().replace(/\s+/g, " ");
4886
- if (!trimmed) {
4887
- return void 0;
4888
- }
4889
- const lookupCanonical = normalizeBodySiteKey(trimmed);
4890
- const contextualCanonical = resolveContextualBodySiteAlias(lookupCanonical, context);
4891
- const displaySourceText = contextualCanonical != null ? contextualCanonical : trimmed;
4892
- const displayText = normalizeSiteDisplayText(displaySourceText, customSiteMap);
4893
- const canonical = normalizeBodySiteKey(displayText);
4894
- 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];
4895
- const coding = buildBodySiteCoding(definition);
4896
- const finalDisplayText = (_f = definition == null ? void 0 : definition.text) != null ? _f : displayText;
4897
- const features = parseBodySiteFeatures(finalDisplayText, coding, customSiteMap);
4898
- const spatialRelation = (_g = definition == null ? void 0 : definition.spatialRelation) != null ? _g : buildSpatialRelation(features, finalDisplayText, customSiteMap);
4899
- return {
4900
- lookupCanonical,
4901
- resolutionCanonical: contextualCanonical != null ? contextualCanonical : lookupCanonical,
4902
- canonical: normalizeBodySiteKey(finalDisplayText) || canonical,
4903
- displayText: finalDisplayText,
4904
- coding,
4905
- spatialRelation,
4906
- definition,
4907
- features,
4908
- englishObjectText: renderBodySiteObject(features),
4909
- preferredPreposition: inferPreferredPreposition(
4910
- normalizeBodySiteKey(finalDisplayText) || canonical,
4911
- features,
4912
- definition
4913
- )
4914
- };
4915
- }
4916
-
4917
- // src/timing-summary.ts
4918
- var MEAL_TIMING_DETAILS = {
4919
- ["ACM" /* Before Breakfast */]: { relation: "before", meal: "breakfast" },
4920
- ["ACD" /* Before Lunch */]: { relation: "before", meal: "lunch" },
4921
- ["ACV" /* Before Dinner */]: { relation: "before", meal: "dinner" },
4922
- ["PCM" /* After Breakfast */]: { relation: "after", meal: "breakfast" },
4923
- ["PCD" /* After Lunch */]: { relation: "after", meal: "lunch" },
4924
- ["PCV" /* After Dinner */]: { relation: "after", meal: "dinner" },
4925
- ["CM" /* Breakfast */]: { relation: "with", meal: "breakfast" },
4926
- ["CD" /* Lunch */]: { relation: "with", meal: "lunch" },
4927
- ["CV" /* Dinner */]: { relation: "with", meal: "dinner" }
4928
- };
4929
- var MEAL_ORDER = {
4930
- breakfast: 0,
4931
- lunch: 1,
4932
- dinner: 2
4933
- };
4934
- var INFERABLE_DAILY_EVENT_TIMINGS = /* @__PURE__ */ new Set([
4935
- "HS" /* Before Sleep */,
4936
- "ACM" /* Before Breakfast */,
4937
- "ACD" /* Before Lunch */,
4938
- "ACV" /* Before Dinner */,
4939
- "PCM" /* After Breakfast */,
4940
- "PCD" /* After Lunch */,
4941
- "PCV" /* After Dinner */,
4942
- "CM" /* Breakfast */,
4943
- "CD" /* Lunch */,
4944
- "CV" /* Dinner */,
4945
- "MORN" /* Morning */,
4946
- "MORN.early" /* Early Morning */,
4947
- "MORN.late" /* Late Morning */,
4948
- "NOON" /* Noon */,
4949
- "AFT" /* Afternoon */,
4950
- "AFT.early" /* Early Afternoon */,
4951
- "AFT.late" /* Late Afternoon */,
4952
- "EVE" /* Evening */,
4953
- "EVE.early" /* Early Evening */,
4954
- "EVE.late" /* Late Evening */,
4955
- "NIGHT" /* Night */,
4956
- "WAKE" /* Wake */,
4957
- "PHS" /* After Sleep */
4958
- ]);
4959
- function uniqueValues(values) {
4960
- const seen = /* @__PURE__ */ new Set();
4961
- const result = [];
4962
- for (const value of values) {
4963
- if (!seen.has(value)) {
4964
- seen.add(value);
4965
- result.push(value);
4966
- }
4967
- }
4968
- return result;
4969
- }
4970
- function getMealTimingGroup(when, options) {
4971
- if (!(options == null ? void 0 : options.groupMealTimingsByRelation)) {
4972
- return void 0;
4973
- }
4974
- const uniqueWhen2 = uniqueValues(when);
4975
- if (uniqueWhen2.length < 2) {
4976
- return void 0;
4977
- }
4978
- let relation;
4979
- const meals = [];
4980
- const groupedCodes = [];
4981
- let sawFirstMeal = false;
4982
- for (let i = 0; i < uniqueWhen2.length; i += 1) {
4983
- const code = uniqueWhen2[i];
4984
- const detail = MEAL_TIMING_DETAILS[code];
4985
- if (!detail) {
4986
- if (sawFirstMeal) {
4987
- break;
4988
- }
4989
- continue;
4990
- }
4991
- if (!sawFirstMeal) {
4992
- sawFirstMeal = true;
4993
- }
4994
- if (!relation) {
4995
- relation = detail.relation;
4996
- } else if (relation !== detail.relation && detail.relation !== "with") {
4997
- break;
4998
- }
4999
- meals.push(detail.meal);
5000
- groupedCodes.push(code);
5001
- }
5002
- if (groupedCodes.length < 2) {
5003
- return void 0;
5004
- }
5005
- for (let i = 1; i < meals.length; i += 1) {
5006
- const current = meals[i];
5007
- let j = i - 1;
5008
- while (j >= 0 && MEAL_ORDER[meals[j]] > MEAL_ORDER[current]) {
5009
- meals[j + 1] = meals[j];
5010
- j -= 1;
5011
- }
5012
- meals[j + 1] = current;
5013
- }
5014
- if (!relation) {
5015
- return void 0;
5016
- }
5017
- return {
5018
- relation,
5019
- meals,
5020
- codes: groupedCodes
5021
- };
5022
- }
5023
- function inferDailyOccurrenceCount(input, options) {
5024
- var _a2, _b, _c, _d;
5025
- if (!(options == null ? void 0 : options.includeTimesPerDaySummary)) {
5026
- return void 0;
5027
- }
5028
- if (input.frequency !== void 0 || input.frequencyMax !== void 0 || input.timingCode) {
5183
+ }
5184
+ function inferPreferredPreposition(canonical, features, definition) {
5185
+ if (features.kind === "locative") {
5029
5186
  return void 0;
5030
5187
  }
5031
- if (input.period !== void 0 || input.periodMax !== void 0 || input.periodUnit !== void 0) {
5032
- return void 0;
5188
+ const routeHint = definition == null ? void 0 : definition.routeHint;
5189
+ if (routeHint === RouteCode["Topical route"] || routeHint === RouteCode["Transdermal route"]) {
5190
+ return "to";
5033
5191
  }
5034
- if (((_b = (_a2 = input.dayOfWeek) == null ? void 0 : _a2.length) != null ? _b : 0) > 0) {
5035
- 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";
5036
5194
  }
5037
- const uniqueWhen2 = uniqueValues((_c = input.when) != null ? _c : []);
5038
- for (let i = 0; i < uniqueWhen2.length; i += 1) {
5039
- if (!INFERABLE_DAILY_EVENT_TIMINGS.has(uniqueWhen2[i])) {
5040
- 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";
5041
5202
  }
5042
5203
  }
5043
- const uniqueTimes = uniqueValues((_d = input.timeOfDay) != null ? _d : []);
5044
- const occurrences = uniqueWhen2.length + uniqueTimes.length;
5045
- 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) {
5046
5210
  return void 0;
5047
5211
  }
5048
- 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
+ };
5049
5238
  }
5050
5239
 
5051
5240
  // src/format.ts
@@ -5539,73 +5728,22 @@ function summarizeMealTimingGroup(group) {
5539
5728
  }
5540
5729
  return `${relationText} ${joinWithAnd(group.meals)}`;
5541
5730
  }
5542
- function collectWhenPhrases(schedule, options) {
5543
- var _a2, _b, _c;
5544
- const when = (_a2 = schedule == null ? void 0 : schedule.when) != null ? _a2 : [];
5545
- if (!when.length) {
5546
- return [];
5547
- }
5548
- const unique = [];
5549
- const seen = /* @__PURE__ */ new Set();
5550
- let hasSpecificAfter = false;
5551
- let hasSpecificBefore = false;
5552
- let hasSpecificWith = false;
5553
- for (const code of when) {
5554
- if (!seen.has(code)) {
5555
- seen.add(code);
5556
- unique.push(code);
5557
- if (code === "PCM" /* After Breakfast */ || code === "PCD" /* After Lunch */ || code === "PCV" /* After Dinner */) {
5558
- hasSpecificAfter = true;
5559
- }
5560
- if (code === "ACM" /* Before Breakfast */ || code === "ACD" /* Before Lunch */ || code === "ACV" /* Before Dinner */) {
5561
- hasSpecificBefore = true;
5562
- }
5563
- if (code === "CM" /* Breakfast */ || code === "CD" /* Lunch */ || code === "CV" /* Dinner */) {
5564
- hasSpecificWith = true;
5565
- }
5566
- }
5567
- }
5568
- const filtered = [];
5569
- for (const code of unique) {
5570
- if (code === "PC" /* After Meal */ && hasSpecificAfter) {
5571
- continue;
5572
- }
5573
- if (code === "AC" /* Before Meal */ && hasSpecificBefore) {
5574
- continue;
5575
- }
5576
- if (code === "C" /* Meal */ && hasSpecificWith) {
5577
- continue;
5578
- }
5579
- filtered.push(code);
5580
- }
5581
- const mealGroup = getMealTimingGroup(filtered, options);
5582
- if (!mealGroup) {
5583
- const phrases2 = [];
5584
- for (const code of filtered) {
5585
- const text = (_b = WHEN_TEXT[code]) != null ? _b : code;
5586
- if (text) {
5587
- phrases2.push(text);
5588
- }
5589
- }
5590
- return phrases2;
5591
- }
5592
- const groupedCodes = new Set(mealGroup.codes);
5593
- const phrases = [];
5594
- let insertedGroup = false;
5595
- for (const code of filtered) {
5596
- if (groupedCodes.has(code)) {
5597
- if (!insertedGroup) {
5598
- phrases.push(summarizeMealTimingGroup(mealGroup));
5599
- insertedGroup = true;
5600
- }
5601
- 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";
5602
5738
  }
5603
- const text = (_c = WHEN_TEXT[code]) != null ? _c : code;
5604
- if (text) {
5605
- phrases.push(text);
5739
+ if (dailyCount === 2 || dailyCount === 3 || dailyCount === 4) {
5740
+ return "conjunction";
5606
5741
  }
5742
+ return "separate";
5607
5743
  }
5608
- return phrases;
5744
+ };
5745
+ function collectWhenPhrases(schedule, options) {
5746
+ return collectLocalizedWhenPhrases(schedule, EN_TIMING_GRAMMAR, options);
5609
5747
  }
5610
5748
  function joinWithAnd(parts) {
5611
5749
  if (!parts.length) {
@@ -5619,23 +5757,14 @@ function joinWithAnd(parts) {
5619
5757
  }
5620
5758
  return `${parts.slice(0, -1).join(", ")} and ${parts[parts.length - 1]}`;
5621
5759
  }
5622
- function combineFrequencyAndEvents(frequency, events) {
5623
- if (!frequency) {
5624
- if (!events.length) {
5625
- return {};
5626
- }
5627
- return { event: joinWithAnd(events) };
5628
- }
5629
- if (!events.length) {
5630
- return { frequency };
5631
- }
5632
- if (events.length === 1 && events[0] === "at bedtime") {
5633
- const lowerFrequency = frequency.toLowerCase();
5634
- if (lowerFrequency === "twice daily" || lowerFrequency === "three times daily" || lowerFrequency === "four times daily") {
5635
- return { frequency: `${frequency} and ${events[0]}` };
5636
- }
5637
- }
5638
- 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
+ );
5639
5768
  }
5640
5769
  function buildRoutePhrase(clause, grammar, hasSite) {
5641
5770
  var _a2, _b;
@@ -5940,7 +6069,7 @@ function formatLong(clause, options) {
5940
6069
  eventParts.push(`at ${timeStrings.join(", ")}`);
5941
6070
  }
5942
6071
  }
5943
- const timing = combineFrequencyAndEvents(frequencyPart, eventParts);
6072
+ const timing = combineFrequencyAndEvents(schedule, frequencyPart, eventParts, options);
5944
6073
  const dayPart = describeDayOfWeek(schedule);
5945
6074
  const countPart = schedule.count !== void 0 && !standaloneOccurrenceCount ? `for ${stripTrailingZero(schedule.count)} ${schedule.count === 1 ? "dose" : "doses"}` : void 0;
5946
6075
  const durationPart = describeDuration(schedule);
@@ -9735,6 +9864,81 @@ function listSupportedBodySiteGrammar() {
9735
9864
  };
9736
9865
  }
9737
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
+
9738
9942
  // src/parser-state.ts
9739
9943
  var ParserState = class {
9740
9944
  constructor(input, tokens, customSiteHints) {
@@ -10105,6 +10309,20 @@ var ParserState = class {
10105
10309
  sourceText: value.sourceText
10106
10310
  };
10107
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
+ }
10108
10326
  get additionalInstructions() {
10109
10327
  if (!this.clause.additionalInstructions) {
10110
10328
  this.clause.additionalInstructions = [];
@@ -10177,7 +10395,7 @@ var ParserState = class {
10177
10395
  if (!site) {
10178
10396
  return;
10179
10397
  }
10180
- 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) {
10181
10399
  delete this.clause.site;
10182
10400
  }
10183
10401
  }
@@ -10538,7 +10756,7 @@ function appendWarning(warnings, warning) {
10538
10756
  return warnings;
10539
10757
  }
10540
10758
  function canonicalToFhir(clause, textOverride, options) {
10541
- 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;
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;
10542
10760
  const dosage = {};
10543
10761
  const repeat = {};
10544
10762
  let hasRepeat = false;
@@ -10639,20 +10857,35 @@ function canonicalToFhir(clause, textOverride, options) {
10639
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)) {
10640
10858
  const siteCoding = selectCanonicalSiteCoding(clause.site, options);
10641
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
+ ];
10642
10875
  dosage.site = __spreadProps(__spreadValues({
10643
- text: (_q = clause.site) == null ? void 0 : _q.text
10876
+ text: (_v = clause.site) == null ? void 0 : _v.text
10644
10877
  }, siteTextElement ? { _text: siteTextElement } : {}), {
10645
10878
  coding: buildSiteCodingArray(siteCoding, options),
10646
- extension: buildBodySiteSpatialRelationExtensions((_r = clause.site) == null ? void 0 : _r.spatialRelation)
10879
+ extension: siteExtensions.length ? siteExtensions : void 0
10647
10880
  });
10648
10881
  }
10649
- 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)) {
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)) {
10650
10883
  dosage.method = {
10651
- text: (_w = clause.method) == null ? void 0 : _w.text,
10652
- _text: clonePrimitiveElement((_x = clause.method) == null ? void 0 : _x._text),
10653
- coding: ((_z = (_y = clause.method) == null ? void 0 : _y.coding) == null ? void 0 : _z.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) ? [
10654
10887
  {
10655
- system: (_A = clause.method.coding.system) != null ? _A : SNOMED_SYSTEM5,
10888
+ system: (_E = clause.method.coding.system) != null ? _E : SNOMED_SYSTEM5,
10656
10889
  code: clause.method.coding.code,
10657
10890
  display: clause.method.coding.display,
10658
10891
  _display: clonePrimitiveElement(clause.method.coding._display)
@@ -10660,14 +10893,14 @@ function canonicalToFhir(clause, textOverride, options) {
10660
10893
  ] : void 0
10661
10894
  };
10662
10895
  }
10663
- if ((_B = clause.additionalInstructions) == null ? void 0 : _B.length) {
10896
+ if ((_F = clause.additionalInstructions) == null ? void 0 : _F.length) {
10664
10897
  dosage.additionalInstruction = [];
10665
10898
  for (const instruction of clause.additionalInstructions) {
10666
10899
  dosage.additionalInstruction.push({
10667
10900
  text: instruction.text,
10668
- coding: ((_C = instruction.coding) == null ? void 0 : _C.code) ? [
10901
+ coding: ((_G = instruction.coding) == null ? void 0 : _G.code) ? [
10669
10902
  {
10670
- system: (_D = instruction.coding.system) != null ? _D : SNOMED_SYSTEM5,
10903
+ system: (_H = instruction.coding.system) != null ? _H : SNOMED_SYSTEM5,
10671
10904
  code: instruction.coding.code,
10672
10905
  display: instruction.coding.display
10673
10906
  }
@@ -10675,9 +10908,9 @@ function canonicalToFhir(clause, textOverride, options) {
10675
10908
  });
10676
10909
  }
10677
10910
  }
10678
- if ((_E = clause.prn) == null ? void 0 : _E.enabled) {
10911
+ if ((_I = clause.prn) == null ? void 0 : _I.enabled) {
10679
10912
  dosage.asNeededBoolean = true;
10680
- const reasons = ((_F = clause.prn.reasons) == null ? void 0 : _F.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] : [];
10681
10914
  if (reasons.length) {
10682
10915
  dosage.asNeededFor = [];
10683
10916
  for (const reason of reasons) {
@@ -10685,18 +10918,18 @@ function canonicalToFhir(clause, textOverride, options) {
10685
10918
  if (reason.text) {
10686
10919
  concept.text = reason.text;
10687
10920
  }
10688
- const reasonTextElement = (options == null ? void 0 : options.includeTranslationExtensions) ? buildTranslationPrimitiveElement((_H = reason.i18n) != null ? _H : (_G = reason.coding) == null ? void 0 : _G.i18n) : void 0;
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;
10689
10922
  if (reasonTextElement) {
10690
10923
  concept._text = reasonTextElement;
10691
10924
  }
10692
- if ((_I = reason.coding) == null ? void 0 : _I.code) {
10925
+ if ((_M = reason.coding) == null ? void 0 : _M.code) {
10693
10926
  const displayElement = (options == null ? void 0 : options.includeTranslationExtensions) ? mergeTranslationPrimitiveElement(
10694
10927
  reason.coding._display,
10695
10928
  reason.coding.i18n
10696
10929
  ) : clonePrimitiveElement(reason.coding._display);
10697
10930
  concept.coding = [
10698
10931
  __spreadProps(__spreadValues({
10699
- system: (_J = reason.coding.system) != null ? _J : SNOMED_SYSTEM5,
10932
+ system: (_N = reason.coding.system) != null ? _N : SNOMED_SYSTEM5,
10700
10933
  code: reason.coding.code,
10701
10934
  display: reason.coding.display
10702
10935
  }, displayElement ? { _display: displayElement } : {}), {
@@ -10737,11 +10970,13 @@ function canonicalFromFhir(dosage) {
10737
10970
  const siteSpatialRelation = parseBodySiteSpatialRelationExtension(dosage.site);
10738
10971
  const siteText = getFallbackSiteText(dosage.site);
10739
10972
  const siteI18n = codeableConceptTranslationI18n(dosage.site, siteCoding);
10740
- if (siteText || (siteCoding == null ? void 0 : siteCoding.code) || siteSpatialRelation) {
10973
+ const siteAdministrationTargetCount = getBodySiteAdministrationTargetCount(dosage.site);
10974
+ if (siteText || (siteCoding == null ? void 0 : siteCoding.code) || siteSpatialRelation || siteAdministrationTargetCount !== void 0) {
10741
10975
  clause.site = {
10742
10976
  text: siteText,
10743
10977
  i18n: siteI18n.text,
10744
10978
  spatialRelation: siteSpatialRelation,
10979
+ administrationTargetCount: siteAdministrationTargetCount,
10745
10980
  coding: (siteCoding == null ? void 0 : siteCoding.code) ? {
10746
10981
  code: siteCoding.code,
10747
10982
  display: siteCoding.display,
@@ -11822,73 +12057,22 @@ function summarizeMealTimingGroupThai(group) {
11822
12057
  }
11823
12058
  return `${relationText[group.relation]}${joinMealNamesThai(meals)}`;
11824
12059
  }
11825
- function collectWhenPhrasesThai(schedule, options) {
11826
- var _a2;
11827
- const when = (_a2 = schedule == null ? void 0 : schedule.when) != null ? _a2 : [];
11828
- if (!when.length) {
11829
- return [];
11830
- }
11831
- const unique = [];
11832
- const seen = /* @__PURE__ */ new Set();
11833
- let hasSpecificAfter = false;
11834
- let hasSpecificBefore = false;
11835
- let hasSpecificWith = false;
11836
- for (const code of when) {
11837
- if (!seen.has(code)) {
11838
- seen.add(code);
11839
- unique.push(code);
11840
- if (code === "PCM" /* After Breakfast */ || code === "PCD" /* After Lunch */ || code === "PCV" /* After Dinner */) {
11841
- hasSpecificAfter = true;
11842
- }
11843
- if (code === "ACM" /* Before Breakfast */ || code === "ACD" /* Before Lunch */ || code === "ACV" /* Before Dinner */) {
11844
- hasSpecificBefore = true;
11845
- }
11846
- if (code === "CM" /* Breakfast */ || code === "CD" /* Lunch */ || code === "CV" /* Dinner */) {
11847
- hasSpecificWith = true;
11848
- }
11849
- }
11850
- }
11851
- const filtered = [];
11852
- for (const code of unique) {
11853
- if (code === "PC" /* After Meal */ && hasSpecificAfter) {
11854
- continue;
11855
- }
11856
- if (code === "AC" /* Before Meal */ && hasSpecificBefore) {
11857
- continue;
11858
- }
11859
- if (code === "C" /* Meal */ && hasSpecificWith) {
11860
- continue;
11861
- }
11862
- filtered.push(code);
11863
- }
11864
- const mealGroup = getMealTimingGroup(filtered, options);
11865
- if (!mealGroup) {
11866
- const phrases2 = [];
11867
- for (const code of filtered) {
11868
- const text = WHEN_TEXT_THAI[code];
11869
- if (text) {
11870
- phrases2.push(text);
11871
- }
11872
- }
11873
- return phrases2;
11874
- }
11875
- const groupedCodes = new Set(mealGroup.codes);
11876
- const phrases = [];
11877
- let insertedGroup = false;
11878
- for (const code of filtered) {
11879
- if (groupedCodes.has(code)) {
11880
- if (!insertedGroup) {
11881
- phrases.push(summarizeMealTimingGroupThai(mealGroup));
11882
- insertedGroup = true;
11883
- }
11884
- 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";
11885
12067
  }
11886
- const text = WHEN_TEXT_THAI[code];
11887
- if (text) {
11888
- phrases.push(text);
12068
+ if (dailyCount === 2 || dailyCount === 3 || dailyCount === 4) {
12069
+ return "conjunction";
11889
12070
  }
12071
+ return "separate";
11890
12072
  }
11891
- return phrases;
12073
+ };
12074
+ function collectWhenPhrasesThai(schedule, options) {
12075
+ return collectLocalizedWhenPhrases(schedule, TH_TIMING_GRAMMAR, options);
11892
12076
  }
11893
12077
  function joinWithAndThai(parts) {
11894
12078
  if (!parts.length) {
@@ -11902,20 +12086,14 @@ function joinWithAndThai(parts) {
11902
12086
  }
11903
12087
  return `${parts.slice(0, -1).join(", ")} \u0E41\u0E25\u0E30 ${parts[parts.length - 1]}`;
11904
12088
  }
11905
- function combineFrequencyAndEventsThai(frequency, events) {
11906
- if (!frequency) {
11907
- if (!events.length) {
11908
- return {};
11909
- }
11910
- return { event: joinWithAndThai(events) };
11911
- }
11912
- if (!events.length) {
11913
- return { frequency };
11914
- }
11915
- if (events.length === 1 && events[0] === "\u0E01\u0E48\u0E2D\u0E19\u0E19\u0E2D\u0E19" && frequency.includes("\u0E27\u0E31\u0E19\u0E25\u0E30")) {
11916
- return { frequency: `${frequency} \u0E41\u0E25\u0E30 ${events[0]}` };
11917
- }
11918
- 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
+ );
11919
12097
  }
11920
12098
  function isOralRouteThai(clause) {
11921
12099
  var _a2, _b, _c;
@@ -11974,7 +12152,7 @@ function buildRoutePhraseThai(clause, grammar, hasSite) {
11974
12152
  return text;
11975
12153
  }
11976
12154
  function formatSiteThai(clause, grammar) {
11977
- 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;
11978
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());
11979
12157
  const lower = text == null ? void 0 : text.toLowerCase();
11980
12158
  const codingCode = (_g = (_f = clause.site) == null ? void 0 : _f.coding) == null ? void 0 : _g.code;
@@ -11989,14 +12167,14 @@ function formatSiteThai(clause, grammar) {
11989
12167
  if (isVaginalRoute && isVaginaSite) {
11990
12168
  return void 0;
11991
12169
  }
11992
- 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);
11993
12171
  if (!translated) {
11994
12172
  return void 0;
11995
12173
  }
11996
- 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"]) {
11997
12175
  return `\u0E40\u0E02\u0E49\u0E32${translated}`;
11998
12176
  }
11999
- const preposition = (_o = grammar.sitePreposition) != null ? _o : "\u0E17\u0E35\u0E48";
12177
+ const preposition = (_v = grammar.sitePreposition) != null ? _v : "\u0E17\u0E35\u0E48";
12000
12178
  const separator = /^[\u0E00-\u0E7F]/.test(translated) ? "" : " ";
12001
12179
  return `${preposition}${separator}${translated}`.trim();
12002
12180
  }
@@ -12294,7 +12472,7 @@ function formatLongThai(clause, options) {
12294
12472
  eventParts.push(`\u0E40\u0E27\u0E25\u0E32 ${timeStrings.join(", ")}`);
12295
12473
  }
12296
12474
  }
12297
- const timing = combineFrequencyAndEventsThai(frequencyPart, eventParts);
12475
+ const timing = combineFrequencyAndEventsThai(schedule, frequencyPart, eventParts, options);
12298
12476
  const dayPart = describeDayOfWeekThai(schedule);
12299
12477
  const countPart = schedule.count !== void 0 && !standaloneOccurrenceCount ? `\u0E08\u0E33\u0E19\u0E27\u0E19 ${stripTrailingZero2(schedule.count)} \u0E04\u0E23\u0E31\u0E49\u0E07` : void 0;
12300
12478
  const durationPart = describeDurationThai(schedule);
@@ -12726,6 +12904,12 @@ function sameOptionalScalar(left, right) {
12726
12904
  function mergeOptionalScalar(left, right) {
12727
12905
  return left !== void 0 ? left : right;
12728
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
+ }
12729
12913
  function sameCoding(left, right) {
12730
12914
  var _a2, _b;
12731
12915
  if (!(left == null ? void 0 : left.code) || !(right == null ? void 0 : right.code)) {
@@ -12788,6 +12972,7 @@ function mergeSite(left, right, context) {
12788
12972
  }
12789
12973
  return {
12790
12974
  text: mergeOptionalScalar(left.text, right.text),
12975
+ i18n: mergeI18nRecords2(left.i18n, right.i18n),
12791
12976
  source: mergeOptionalScalar(left.source, right.source),
12792
12977
  coding: mergeOptionalScalar(left.coding, right.coding),
12793
12978
  spatialRelation: mergeOptionalScalar(left.spatialRelation, right.spatialRelation),
@@ -13135,6 +13320,7 @@ var TOKEN_SITE_CANDIDATES = {
13135
13320
  le: [{ text: "left eye", route: RouteCode["Ophthalmic route"], source: "abbreviation" }],
13136
13321
  ou: [{ text: "both eyes", route: RouteCode["Ophthalmic route"], source: "abbreviation" }],
13137
13322
  be: [{ text: "both eyes", route: RouteCode["Ophthalmic route"], source: "abbreviation" }],
13323
+ au: [{ text: "both ears", route: RouteCode["Otic route"], source: "abbreviation" }],
13138
13324
  vod: [
13139
13325
  {
13140
13326
  text: "right eye",
@@ -15684,6 +15870,9 @@ function inferRouteFromContext(ctx) {
15684
15870
  // src/hpsg/rules/site-rules.ts
15685
15871
  function siteBoundary(lower, context) {
15686
15872
  var _a2, _b, _c;
15873
+ if (SITE_MULTIPLICITY_WORDS.has(lower)) {
15874
+ return false;
15875
+ }
15687
15876
  const siteLike = Boolean(resolveBodySitePhrase(lower, (_a2 = context.options) == null ? void 0 : _a2.siteCodeMap, {
15688
15877
  bodySiteContext: (_c = (_b = context.options) == null ? void 0 : _b.context) == null ? void 0 : _c.bodySiteContext
15689
15878
  }));
@@ -15745,7 +15934,7 @@ function shouldSuppressEyeSiteAbbreviation(context, start, lower) {
15745
15934
  }
15746
15935
  function siteLexicalRule() {
15747
15936
  return lexicalRule("hpsg.lex.site", (context, start) => {
15748
- 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;
15749
15938
  const token = (_a2 = tokensAvailable(context, start, 1)) == null ? void 0 : _a2[0];
15750
15939
  if (!token) {
15751
15940
  return [];
@@ -15811,7 +16000,7 @@ function siteLexicalRule() {
15811
16000
  continue;
15812
16001
  }
15813
16002
  phraseTokens.push(candidate);
15814
- if (!SITE_DISPLAY_FILLERS.has(candidateLower)) {
16003
+ if (!SITE_DISPLAY_FILLERS.has(candidateLower) || SITE_MULTIPLICITY_WORDS.has(candidateLower)) {
15815
16004
  displayTokens.push(candidate);
15816
16005
  }
15817
16006
  }
@@ -15857,6 +16046,7 @@ function siteLexicalRule() {
15857
16046
  valence: {
15858
16047
  site: {
15859
16048
  text: displayText,
16049
+ i18n: (_r = resolved == null ? void 0 : resolved.definition) == null ? void 0 : _r.i18n,
15860
16050
  source: "text",
15861
16051
  coding: resolved == null ? void 0 : resolved.coding,
15862
16052
  spatialRelation: resolved == null ? void 0 : resolved.spatialRelation,
@@ -15884,7 +16074,7 @@ function siteLexicalRule() {
15884
16074
  }
15885
16075
  function bareSiteLexicalRule() {
15886
16076
  return lexicalRule("hpsg.lex.site.bare", (context, start) => {
15887
- var _a2, _b, _c, _d, _e, _f, _g, _h;
16077
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i;
15888
16078
  const signs = [];
15889
16079
  const first = (_a2 = tokensAvailable(context, start, 1)) == null ? void 0 : _a2[0];
15890
16080
  if (!first) {
@@ -15930,6 +16120,7 @@ function bareSiteLexicalRule() {
15930
16120
  valence: {
15931
16121
  site: {
15932
16122
  text: displayText,
16123
+ i18n: (_h = resolved.definition) == null ? void 0 : _h.i18n,
15933
16124
  source: "text",
15934
16125
  coding: resolved.coding,
15935
16126
  spatialRelation: resolved.spatialRelation,
@@ -15937,7 +16128,7 @@ function bareSiteLexicalRule() {
15937
16128
  originalText,
15938
16129
  text: sourceText,
15939
16130
  normalized: sourceText.toLowerCase(),
15940
- canonical: (_h = resolved.resolutionCanonical) != null ? _h : normalizeBodySiteKey(displayText),
16131
+ canonical: (_i = resolved.resolutionCanonical) != null ? _i : normalizeBodySiteKey(displayText),
15941
16132
  isProbe,
15942
16133
  inputText: context.state.input,
15943
16134
  sourceText,
@@ -17964,6 +18155,9 @@ function applySiteDefinition(internal, definition) {
17964
18155
  } else if ((_b = internal.siteLookupRequest) == null ? void 0 : _b.text) {
17965
18156
  internal.siteText = internal.siteLookupRequest.text;
17966
18157
  }
18158
+ if (definition.administrationTargetCount !== void 0) {
18159
+ internal.siteAdministrationTargetCount = definition.administrationTargetCount;
18160
+ }
17967
18161
  if (definition.spatialRelation) {
17968
18162
  internal.siteSpatialRelation = definition.spatialRelation;
17969
18163
  } else if ((_c = internal.siteLookupRequest) == null ? void 0 : _c.spatialRelation) {
@@ -18316,6 +18510,9 @@ function seedKnownSiteCoding(state) {
18316
18510
  display: definition.coding.display,
18317
18511
  i18n: mergeI18nRecords(definition.i18n, definition.coding.i18n)
18318
18512
  };
18513
+ if (state.siteAdministrationTargetCount === void 0) {
18514
+ state.siteAdministrationTargetCount = definition.administrationTargetCount;
18515
+ }
18319
18516
  }
18320
18517
  function parseClauseState(input, options) {
18321
18518
  const tokens = tokenize(input);
@@ -19646,6 +19843,21 @@ function normalizeClock(clock) {
19646
19843
  }
19647
19844
  return `${pad(hour)}:${pad(minute)}:${pad(second)}`;
19648
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
+ }
19649
19861
  function getDateTimeFormat(timeZone) {
19650
19862
  let formatter = dateTimeFormatCache.get(timeZone);
19651
19863
  if (!formatter) {
@@ -21065,7 +21277,8 @@ function calculateTotalUnitsSingle(options) {
21065
21277
  2e3
21066
21278
  );
21067
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;
21068
- let totalUnits = roundCalculatedUnits(count * doseQuantity);
21280
+ const targetMultiplier = getAdministrationTargetMultiplier(dosage, context);
21281
+ let totalUnits = roundCalculatedUnits(count * doseQuantity * targetMultiplier);
21069
21282
  if (roundToMultiple && roundToMultiple > 0) {
21070
21283
  totalUnits = roundCalculatedUnits(Math.ceil(totalUnits / roundToMultiple) * roundToMultiple);
21071
21284
  }
@@ -21621,7 +21834,7 @@ function cloneBodySiteCoding2(coding) {
21621
21834
  };
21622
21835
  }
21623
21836
  function buildNormalizedMetaFromClause(clause, fhir, options) {
21624
- 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;
21625
21838
  const additionalInstructions = ((_a2 = clause.additionalInstructions) == null ? void 0 : _a2.length) ? clause.additionalInstructions.map((instruction) => ({
21626
21839
  text: instruction.text,
21627
21840
  coding: cloneCoding2(instruction.coding)
@@ -21634,22 +21847,23 @@ function buildNormalizedMetaFromClause(clause, fhir, options) {
21634
21847
  unit: (_h = clause.dose) == null ? void 0 : _h.unit,
21635
21848
  unitKind: unitSemantics == null ? void 0 : unitSemantics.kind,
21636
21849
  unitSemantics,
21637
- 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) ? {
21638
- 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,
21639
21852
  coding: siteCoding,
21640
- 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
21641
21855
  } : void 0,
21642
- method: ((_o = clause.method) == null ? void 0 : _o.text) || ((_q = (_p = clause.method) == null ? void 0 : _p.coding) == null ? void 0 : _q.code) ? {
21643
- text: (_r = clause.method) == null ? void 0 : _r.text,
21644
- 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)
21645
21859
  } : void 0,
21646
21860
  patientInstruction: clause.patientInstruction,
21647
- 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) ? {
21648
- text: (_z = (_y = clause.prn) == null ? void 0 : _y.reason) == null ? void 0 : _z.text,
21649
- coding: cloneCoding2((_B = (_A = clause.prn) == null ? void 0 : _A.reason) == null ? void 0 : _B.coding),
21650
- 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)
21651
21865
  } : void 0,
21652
- 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) => ({
21653
21867
  text: reason.text,
21654
21868
  coding: cloneCoding2(reason.coding),
21655
21869
  spatialRelation: cloneBodySiteSpatialRelation(reason.spatialRelation)
@@ -21847,6 +22061,7 @@ export {
21847
22061
  AdviceModality,
21848
22062
  AdvicePolarity,
21849
22063
  AdviceRelation,
22064
+ BODY_SITE_ADMINISTRATION_TARGET_COUNT_EXTENSION_URL,
21850
22065
  BODY_SITE_SPATIAL_RELATION_EXTENSION_URL,
21851
22066
  DEFAULT_BODY_SITE_SNOMED,
21852
22067
  DEFAULT_BODY_SITE_SNOMED_SOURCE,
@@ -21871,6 +22086,7 @@ export {
21871
22086
  SNOMED_CT_TOPOGRAPHICAL_MODIFIER_CODE,
21872
22087
  SNOMED_CT_TOPOGRAPHICAL_MODIFIER_DISPLAY,
21873
22088
  SNOMED_SYSTEM,
22089
+ buildBodySiteAdministrationTargetCountExtension,
21874
22090
  buildBodySiteSpatialRelationExtension,
21875
22091
  buildBodySiteSpatialRelationExtensions,
21876
22092
  buildBodySiteTopographicalModifierCoding,
@@ -21884,6 +22100,7 @@ export {
21884
22100
  formatSig,
21885
22101
  formatSigBatch,
21886
22102
  fromFhirDosage,
22103
+ getBodySiteAdministrationTargetCount,
21887
22104
  getBodySiteCode,
21888
22105
  getBodySiteCodeAsync,
21889
22106
  getBodySiteText,
@@ -21902,6 +22119,7 @@ export {
21902
22119
  lookupBodySite,
21903
22120
  lookupBodySiteAsync,
21904
22121
  nextDueDoses,
22122
+ parseBodySiteAdministrationTargetCountExtension,
21905
22123
  parseBodySiteSpatialRelationExtension,
21906
22124
  parseSig,
21907
22125
  parseSigAsync,