ezmedicationinput 0.1.42 → 0.1.44

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.
Files changed (46) hide show
  1. package/README.md +31 -1
  2. package/dist/advice-rules.json +772 -0
  3. package/dist/advice-terminology.json +104 -0
  4. package/dist/advice.d.ts +16 -0
  5. package/dist/advice.js +1375 -0
  6. package/dist/event-trigger.d.ts +14 -0
  7. package/dist/event-trigger.js +501 -0
  8. package/dist/fhir-translations.d.ts +5 -0
  9. package/dist/fhir-translations.js +117 -0
  10. package/dist/fhir.d.ts +6 -4
  11. package/dist/fhir.js +566 -134
  12. package/dist/format.d.ts +5 -2
  13. package/dist/format.js +581 -219
  14. package/dist/i18n.d.ts +4 -2
  15. package/dist/i18n.js +725 -197
  16. package/dist/index.d.ts +0 -1
  17. package/dist/index.js +221 -169
  18. package/dist/internal-types.d.ts +5 -5
  19. package/dist/ir.d.ts +4 -0
  20. package/dist/ir.js +178 -0
  21. package/dist/lexer/lex.d.ts +2 -0
  22. package/dist/lexer/lex.js +401 -0
  23. package/dist/lexer/meaning.d.ts +71 -0
  24. package/dist/lexer/meaning.js +619 -0
  25. package/dist/lexer/surface.d.ts +2 -0
  26. package/dist/lexer/surface.js +62 -0
  27. package/dist/lexer/token-types.d.ts +36 -0
  28. package/dist/lexer/token-types.js +19 -0
  29. package/dist/maps.d.ts +6 -12
  30. package/dist/maps.js +793 -247
  31. package/dist/parser-state.d.ts +101 -0
  32. package/dist/parser-state.js +441 -0
  33. package/dist/parser.d.ts +7 -7
  34. package/dist/parser.js +3598 -1974
  35. package/dist/prn.d.ts +4 -0
  36. package/dist/prn.js +59 -0
  37. package/dist/schedule.js +230 -32
  38. package/dist/site-phrases.d.ts +35 -0
  39. package/dist/site-phrases.js +344 -0
  40. package/dist/timing-summary.d.ts +25 -0
  41. package/dist/timing-summary.js +138 -0
  42. package/dist/types.d.ts +248 -32
  43. package/dist/types.js +49 -1
  44. package/dist/utils/text.d.ts +3 -0
  45. package/dist/utils/text.js +48 -0
  46. package/package.json +1 -1
package/dist/format.js CHANGED
@@ -1,7 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatCanonicalClause = formatCanonicalClause;
3
4
  exports.formatInternal = formatInternal;
5
+ const prn_1 = require("./prn");
4
6
  const types_1 = require("./types");
7
+ const timing_summary_1 = require("./timing-summary");
5
8
  const ROUTE_SHORT = {
6
9
  [types_1.RouteCode["Oral route"]]: "PO",
7
10
  [types_1.RouteCode["Sublingual route"]]: "SL",
@@ -56,9 +59,17 @@ const DAY_NAMES = {
56
59
  sat: "Saturday",
57
60
  sun: "Sunday"
58
61
  };
62
+ const EN_TIMES_PER_DAY = {
63
+ 1: "once daily",
64
+ 2: "twice daily",
65
+ 3: "three times daily",
66
+ 4: "four times daily"
67
+ };
68
+ const SLOWLY_QUALIFIER_CODE = "419443000";
69
+ const EMPTY_STOMACH_QUALIFIER_CODE = "717154004";
59
70
  const DEFAULT_ROUTE_GRAMMAR = { verb: "Use" };
60
71
  const ROUTE_GRAMMAR = {
61
- [types_1.RouteCode["Oral route"]]: { verb: "Take", routePhrase: "by mouth" },
72
+ [types_1.RouteCode["Oral route"]]: { verb: "Take", routePhrase: "orally" },
62
73
  [types_1.RouteCode["Ophthalmic route"]]: {
63
74
  verb: "Instill",
64
75
  routePhrase: ({ hasSite }) => (hasSite ? undefined : "in the eye"),
@@ -74,6 +85,11 @@ const ROUTE_GRAMMAR = {
74
85
  routePhrase: ({ hasSite }) => (hasSite ? undefined : "rectally"),
75
86
  sitePreposition: "into"
76
87
  },
88
+ [types_1.RouteCode["Per vagina"]]: {
89
+ verb: "Insert",
90
+ routePhrase: ({ hasSite }) => (hasSite ? undefined : "vaginally"),
91
+ sitePreposition: "into"
92
+ },
77
93
  [types_1.RouteCode["Topical route"]]: {
78
94
  verb: "Apply",
79
95
  routePhrase: ({ hasSite }) => (hasSite ? undefined : "topically"),
@@ -110,6 +126,10 @@ const ROUTE_GRAMMAR = {
110
126
  sitePreposition: "into"
111
127
  }
112
128
  };
129
+ function scheduleOf(clause) {
130
+ var _a;
131
+ return (_a = clause.schedule) !== null && _a !== void 0 ? _a : {};
132
+ }
113
133
  function grammarFromRouteText(text) {
114
134
  if (!text) {
115
135
  return undefined;
@@ -145,6 +165,9 @@ function grammarFromRouteText(text) {
145
165
  if (normalized.includes("rectal") || normalized.includes("rectum")) {
146
166
  return ROUTE_GRAMMAR[types_1.RouteCode["Per rectum"]];
147
167
  }
168
+ if (normalized.includes("vagin")) {
169
+ return ROUTE_GRAMMAR[types_1.RouteCode["Per vagina"]];
170
+ }
148
171
  if (normalized.includes("nasal")) {
149
172
  return ROUTE_GRAMMAR[types_1.RouteCode["Nasal route"]];
150
173
  }
@@ -153,41 +176,89 @@ function grammarFromRouteText(text) {
153
176
  }
154
177
  return undefined;
155
178
  }
156
- function resolveRouteGrammar(internal) {
179
+ function resolveRouteGrammar(clause) {
180
+ var _a, _b, _c, _d;
181
+ const routeCode = (_a = clause.route) === null || _a === void 0 ? void 0 : _a.code;
182
+ if (routeCode && ROUTE_GRAMMAR[routeCode]) {
183
+ return (_b = ROUTE_GRAMMAR[routeCode]) !== null && _b !== void 0 ? _b : DEFAULT_ROUTE_GRAMMAR;
184
+ }
185
+ return (_d = grammarFromRouteText((_c = clause.route) === null || _c === void 0 ? void 0 : _c.text)) !== null && _d !== void 0 ? _d : DEFAULT_ROUTE_GRAMMAR;
186
+ }
187
+ function resolveMethodVerb(clause, grammar) {
157
188
  var _a, _b;
158
- if (internal.routeCode && ROUTE_GRAMMAR[internal.routeCode]) {
159
- return (_a = ROUTE_GRAMMAR[internal.routeCode]) !== null && _a !== void 0 ? _a : DEFAULT_ROUTE_GRAMMAR;
189
+ const methodText = (_b = (_a = clause.method) === null || _a === void 0 ? void 0 : _a.text) === null || _b === void 0 ? void 0 : _b.trim();
190
+ if (methodText) {
191
+ return methodText;
160
192
  }
161
- return (_b = grammarFromRouteText(internal.routeText)) !== null && _b !== void 0 ? _b : DEFAULT_ROUTE_GRAMMAR;
193
+ return grammar.verb;
162
194
  }
163
195
  function pluralize(unit, value) {
164
196
  if (Math.abs(value) === 1) {
165
- if (unit === "tab")
166
- return "tablet";
167
- if (unit === "cap")
168
- return "capsule";
197
+ switch (unit) {
198
+ case "tab":
199
+ return "tablet";
200
+ case "cap":
201
+ return "capsule";
202
+ default:
203
+ return unit;
204
+ }
205
+ }
206
+ if (unit.endsWith(" ribbon")) {
169
207
  return unit;
170
208
  }
171
- if (unit === "tab" || unit === "tablet")
172
- return "tablets";
173
- if (unit === "cap" || unit === "capsule")
174
- return "capsules";
175
- if (unit === "mL")
176
- return "mL";
177
- if (unit === "mg")
178
- return "mg";
179
- if (unit === "puff")
180
- return value === 1 ? "puff" : "puffs";
181
- if (unit === "patch")
182
- return value === 1 ? "patch" : "patches";
183
- if (unit === "drop")
184
- return value === 1 ? "drop" : "drops";
185
- if (unit === "suppository")
186
- return value === 1 ? "suppository" : "suppositories";
187
- return unit;
209
+ switch (unit) {
210
+ case "tab":
211
+ case "tablet":
212
+ return "tablets";
213
+ case "cap":
214
+ case "capsule":
215
+ return "capsules";
216
+ case "mL":
217
+ case "mg":
218
+ return unit;
219
+ case "puff":
220
+ return "puffs";
221
+ case "patch":
222
+ return "patches";
223
+ case "drop":
224
+ return "drops";
225
+ case "suppository":
226
+ return "suppositories";
227
+ case "pump":
228
+ return "pumps";
229
+ case "squeeze":
230
+ return "squeezes";
231
+ case "applicatorful":
232
+ return "applicatorfuls";
233
+ case "capful":
234
+ return "capfuls";
235
+ case "scoop":
236
+ return "scoops";
237
+ case "application":
238
+ return "applications";
239
+ case "fingertip unit":
240
+ return "fingertip units";
241
+ case "finger length":
242
+ return "finger lengths";
243
+ default:
244
+ return unit;
245
+ }
246
+ }
247
+ function formatPatientInstructionSentence(text) {
248
+ const trimmed = text === null || text === void 0 ? void 0 : text.trim();
249
+ if (!trimmed) {
250
+ return undefined;
251
+ }
252
+ const sentence = /^[.!?]$/.test(trimmed.slice(-1)) ? trimmed : `${trimmed}.`;
253
+ return sentence.charAt(0).toUpperCase() + sentence.slice(1);
188
254
  }
189
- function describeFrequency(internal) {
190
- const { frequency, frequencyMax, period, periodMax, periodUnit, timingCode } = internal;
255
+ function describeFrequency(schedule) {
256
+ const frequency = schedule === null || schedule === void 0 ? void 0 : schedule.frequency;
257
+ const frequencyMax = schedule === null || schedule === void 0 ? void 0 : schedule.frequencyMax;
258
+ const period = schedule === null || schedule === void 0 ? void 0 : schedule.period;
259
+ const periodMax = schedule === null || schedule === void 0 ? void 0 : schedule.periodMax;
260
+ const periodUnit = schedule === null || schedule === void 0 ? void 0 : schedule.periodUnit;
261
+ const timingCode = schedule === null || schedule === void 0 ? void 0 : schedule.timingCode;
191
262
  if (frequency !== undefined &&
192
263
  frequencyMax !== undefined &&
193
264
  periodUnit === types_1.FhirPeriodUnit.Day &&
@@ -201,16 +272,18 @@ function describeFrequency(internal) {
201
272
  return `${stripTrailingZero(frequency)} to ${stripTrailingZero(frequencyMax)} times daily`;
202
273
  }
203
274
  if (frequency && periodUnit === types_1.FhirPeriodUnit.Day && (!period || period === 1)) {
204
- if (frequency === 1)
205
- return "once daily";
206
- if (frequency === 2)
207
- return "twice daily";
208
- if (frequency === 3)
209
- return "three times daily";
210
- if (frequency === 4)
211
- return "four times daily";
275
+ const dailyText = EN_TIMES_PER_DAY[frequency];
276
+ if (dailyText) {
277
+ return dailyText;
278
+ }
212
279
  return `${stripTrailingZero(frequency)} times daily`;
213
280
  }
281
+ if (periodUnit === types_1.FhirPeriodUnit.Minute && period) {
282
+ if (periodMax && periodMax !== period) {
283
+ return `every ${stripTrailingZero(period)} to ${stripTrailingZero(periodMax)} minutes`;
284
+ }
285
+ return `every ${stripTrailingZero(period)} minute${period === 1 ? "" : "s"}`;
286
+ }
214
287
  if (periodUnit === types_1.FhirPeriodUnit.Hour && period) {
215
288
  if (periodMax && periodMax !== period) {
216
289
  return `every ${stripTrailingZero(period)} to ${stripTrailingZero(periodMax)} hours`;
@@ -244,6 +317,15 @@ function describeFrequency(internal) {
244
317
  }
245
318
  return `every ${stripTrailingZero(period)} months`;
246
319
  }
320
+ if (periodUnit === types_1.FhirPeriodUnit.Year && period) {
321
+ if (period === 1 && (!periodMax || periodMax === 1)) {
322
+ return "once yearly";
323
+ }
324
+ if (periodMax && periodMax !== period) {
325
+ return `every ${stripTrailingZero(period)} to ${stripTrailingZero(periodMax)} years`;
326
+ }
327
+ return `every ${stripTrailingZero(period)} years`;
328
+ }
247
329
  if (timingCode) {
248
330
  if (timingCode === "WK") {
249
331
  return "once weekly";
@@ -265,81 +347,200 @@ function describeFrequency(internal) {
265
347
  }
266
348
  }
267
349
  if (frequency && periodUnit === undefined && period === undefined) {
268
- if (frequency === 1)
350
+ if (frequency === 1) {
269
351
  return "once";
352
+ }
270
353
  return `${stripTrailingZero(frequency)} times`;
271
354
  }
272
355
  return undefined;
273
356
  }
274
- function formatDoseShort(internal) {
275
- if (internal.doseRange) {
276
- const { low, high } = internal.doseRange;
277
- const base = `${stripTrailingZero(low)}-${stripTrailingZero(high)}`;
278
- if (internal.unit) {
279
- return `${base} ${internal.unit}`;
357
+ function describeFrequencyCount(count) {
358
+ if (!count || count <= 0) {
359
+ return undefined;
360
+ }
361
+ const dailyText = EN_TIMES_PER_DAY[count];
362
+ if (dailyText) {
363
+ return dailyText;
364
+ }
365
+ return `${stripTrailingZero(count)} times daily`;
366
+ }
367
+ function describeStandaloneOccurrenceCount(schedule) {
368
+ var _a, _b, _c;
369
+ const count = schedule === null || schedule === void 0 ? void 0 : schedule.count;
370
+ if (!count || count <= 0) {
371
+ return undefined;
372
+ }
373
+ if ((schedule === null || schedule === void 0 ? void 0 : schedule.frequency) !== undefined ||
374
+ (schedule === null || schedule === void 0 ? void 0 : schedule.frequencyMax) !== undefined ||
375
+ (schedule === null || schedule === void 0 ? void 0 : schedule.period) !== undefined ||
376
+ (schedule === null || schedule === void 0 ? void 0 : schedule.periodMax) !== undefined ||
377
+ (schedule === null || schedule === void 0 ? void 0 : schedule.periodUnit) !== undefined ||
378
+ ((_a = schedule === null || schedule === void 0 ? void 0 : schedule.dayOfWeek) === null || _a === void 0 ? void 0 : _a.length) ||
379
+ ((_b = schedule === null || schedule === void 0 ? void 0 : schedule.when) === null || _b === void 0 ? void 0 : _b.length) ||
380
+ ((_c = schedule === null || schedule === void 0 ? void 0 : schedule.timeOfDay) === null || _c === void 0 ? void 0 : _c.length) ||
381
+ (schedule === null || schedule === void 0 ? void 0 : schedule.duration) !== undefined ||
382
+ (schedule === null || schedule === void 0 ? void 0 : schedule.durationMax) !== undefined ||
383
+ (schedule === null || schedule === void 0 ? void 0 : schedule.durationUnit) !== undefined ||
384
+ (schedule === null || schedule === void 0 ? void 0 : schedule.timingCode)) {
385
+ return undefined;
386
+ }
387
+ switch (count) {
388
+ case 1:
389
+ return "once";
390
+ case 2:
391
+ return "twice";
392
+ default:
393
+ return `${stripTrailingZero(count)} times`;
394
+ }
395
+ }
396
+ function formatDoseShort(dose) {
397
+ if (!dose) {
398
+ return undefined;
399
+ }
400
+ if (dose.range) {
401
+ if (dose.range.low !== undefined && dose.range.high !== undefined) {
402
+ const base = `${stripTrailingZero(dose.range.low)}-${stripTrailingZero(dose.range.high)}`;
403
+ if (dose.unit) {
404
+ return `${base} ${dose.unit}`;
405
+ }
406
+ return base;
407
+ }
408
+ if (dose.range.low !== undefined) {
409
+ const base = `>=${stripTrailingZero(dose.range.low)}`;
410
+ if (dose.unit) {
411
+ return `${base} ${dose.unit}`;
412
+ }
413
+ return base;
414
+ }
415
+ if (dose.range.high !== undefined) {
416
+ const base = `<=${stripTrailingZero(dose.range.high)}`;
417
+ if (dose.unit) {
418
+ return `${base} ${dose.unit}`;
419
+ }
420
+ return base;
280
421
  }
281
- return base;
282
422
  }
283
- if (internal.dose !== undefined) {
284
- const dosePart = internal.unit
285
- ? `${stripTrailingZero(internal.dose)} ${internal.unit}`
286
- : `${stripTrailingZero(internal.dose)}`;
287
- return dosePart.trim();
423
+ if (dose.value !== undefined) {
424
+ if (dose.unit) {
425
+ return `${stripTrailingZero(dose.value)} ${dose.unit}`;
426
+ }
427
+ return `${stripTrailingZero(dose.value)}`;
288
428
  }
289
429
  return undefined;
290
430
  }
291
- function formatDoseLong(internal) {
292
- if (internal.doseRange) {
293
- const { low, high } = internal.doseRange;
294
- if (internal.unit) {
295
- return `${stripTrailingZero(low)} to ${stripTrailingZero(high)} ${pluralize(internal.unit, high)}`;
431
+ function formatDoseLong(dose) {
432
+ if (!dose) {
433
+ return undefined;
434
+ }
435
+ if (dose.range) {
436
+ if (dose.range.low !== undefined && dose.range.high !== undefined) {
437
+ if (dose.unit) {
438
+ return `${stripTrailingZero(dose.range.low)} to ${stripTrailingZero(dose.range.high)} ${pluralize(dose.unit, dose.range.high)}`;
439
+ }
440
+ return `${stripTrailingZero(dose.range.low)} to ${stripTrailingZero(dose.range.high)}`;
441
+ }
442
+ if (dose.range.low !== undefined) {
443
+ if (dose.unit) {
444
+ return `at least ${stripTrailingZero(dose.range.low)} ${pluralize(dose.unit, dose.range.low)}`;
445
+ }
446
+ return `at least ${stripTrailingZero(dose.range.low)}`;
447
+ }
448
+ if (dose.range.high !== undefined) {
449
+ if (dose.unit) {
450
+ return `up to ${stripTrailingZero(dose.range.high)} ${pluralize(dose.unit, dose.range.high)}`;
451
+ }
452
+ return `up to ${stripTrailingZero(dose.range.high)}`;
296
453
  }
297
- return `${stripTrailingZero(low)} to ${stripTrailingZero(high)}`;
298
454
  }
299
- if (internal.dose !== undefined) {
300
- if (internal.unit) {
301
- return `${stripTrailingZero(internal.dose)} ${pluralize(internal.unit, internal.dose)}`;
455
+ if (dose.value !== undefined) {
456
+ if (dose.unit) {
457
+ return `${stripTrailingZero(dose.value)} ${pluralize(dose.unit, dose.value)}`;
302
458
  }
303
- return `${stripTrailingZero(internal.dose)}`;
459
+ return `${stripTrailingZero(dose.value)}`;
304
460
  }
305
461
  return undefined;
306
462
  }
307
- function collectWhenPhrases(internal) {
308
- if (!internal.when.length) {
463
+ function summarizeMealTimingGroup(group) {
464
+ let relationText = "with";
465
+ if (group.relation === "before") {
466
+ relationText = "before";
467
+ }
468
+ else if (group.relation === "after") {
469
+ relationText = "after";
470
+ }
471
+ return `${relationText} ${joinWithAnd(group.meals)}`;
472
+ }
473
+ function collectWhenPhrases(schedule, options) {
474
+ var _a, _b, _c;
475
+ const when = (_a = schedule === null || schedule === void 0 ? void 0 : schedule.when) !== null && _a !== void 0 ? _a : [];
476
+ if (!when.length) {
309
477
  return [];
310
478
  }
311
479
  const unique = [];
312
480
  const seen = new Set();
313
- for (const code of internal.when) {
481
+ let hasSpecificAfter = false;
482
+ let hasSpecificBefore = false;
483
+ let hasSpecificWith = false;
484
+ for (const code of when) {
314
485
  if (!seen.has(code)) {
315
486
  seen.add(code);
316
487
  unique.push(code);
488
+ if (code === types_1.EventTiming["After Breakfast"] ||
489
+ code === types_1.EventTiming["After Lunch"] ||
490
+ code === types_1.EventTiming["After Dinner"]) {
491
+ hasSpecificAfter = true;
492
+ }
493
+ if (code === types_1.EventTiming["Before Breakfast"] ||
494
+ code === types_1.EventTiming["Before Lunch"] ||
495
+ code === types_1.EventTiming["Before Dinner"]) {
496
+ hasSpecificBefore = true;
497
+ }
498
+ if (code === types_1.EventTiming.Breakfast || code === types_1.EventTiming.Lunch || code === types_1.EventTiming.Dinner) {
499
+ hasSpecificWith = true;
500
+ }
317
501
  }
318
502
  }
319
- const hasSpecificAfter = unique.some((code) => code === types_1.EventTiming["After Breakfast"] ||
320
- code === types_1.EventTiming["After Lunch"] ||
321
- code === types_1.EventTiming["After Dinner"]);
322
- const hasSpecificBefore = unique.some((code) => code === types_1.EventTiming["Before Breakfast"] ||
323
- code === types_1.EventTiming["Before Lunch"] ||
324
- code === types_1.EventTiming["Before Dinner"]);
325
- const hasSpecificWith = unique.some((code) => code === types_1.EventTiming.Breakfast ||
326
- code === types_1.EventTiming.Lunch ||
327
- code === types_1.EventTiming.Dinner);
328
- return unique
329
- .filter((code) => {
503
+ const filtered = [];
504
+ for (const code of unique) {
330
505
  if (code === types_1.EventTiming["After Meal"] && hasSpecificAfter) {
331
- return false;
506
+ continue;
332
507
  }
333
508
  if (code === types_1.EventTiming["Before Meal"] && hasSpecificBefore) {
334
- return false;
509
+ continue;
335
510
  }
336
511
  if (code === types_1.EventTiming.Meal && hasSpecificWith) {
337
- return false;
512
+ continue;
338
513
  }
339
- return true;
340
- })
341
- .map((code) => { var _a; return (_a = WHEN_TEXT[code]) !== null && _a !== void 0 ? _a : code; })
342
- .filter((text) => Boolean(text));
514
+ filtered.push(code);
515
+ }
516
+ const mealGroup = (0, timing_summary_1.getMealTimingGroup)(filtered, options);
517
+ if (!mealGroup) {
518
+ const phrases = [];
519
+ for (const code of filtered) {
520
+ const text = (_b = WHEN_TEXT[code]) !== null && _b !== void 0 ? _b : code;
521
+ if (text) {
522
+ phrases.push(text);
523
+ }
524
+ }
525
+ return phrases;
526
+ }
527
+ const groupedCodes = new Set(mealGroup.codes);
528
+ const phrases = [];
529
+ let insertedGroup = false;
530
+ for (const code of filtered) {
531
+ if (groupedCodes.has(code)) {
532
+ if (!insertedGroup) {
533
+ phrases.push(summarizeMealTimingGroup(mealGroup));
534
+ insertedGroup = true;
535
+ }
536
+ continue;
537
+ }
538
+ const text = (_c = WHEN_TEXT[code]) !== null && _c !== void 0 ? _c : code;
539
+ if (text) {
540
+ phrases.push(text);
541
+ }
542
+ }
543
+ return phrases;
343
544
  }
344
545
  function joinWithAnd(parts) {
345
546
  if (!parts.length) {
@@ -365,21 +566,23 @@ function combineFrequencyAndEvents(frequency, events) {
365
566
  }
366
567
  if (events.length === 1 && events[0] === "at bedtime") {
367
568
  const lowerFrequency = frequency.toLowerCase();
368
- if (lowerFrequency === "twice daily" || lowerFrequency === "three times daily" || lowerFrequency === "four times daily") {
569
+ if (lowerFrequency === "twice daily" ||
570
+ lowerFrequency === "three times daily" ||
571
+ lowerFrequency === "four times daily") {
369
572
  return { frequency: `${frequency} and ${events[0]}` };
370
573
  }
371
574
  }
372
575
  return { frequency, event: joinWithAnd(events) };
373
576
  }
374
- function buildRoutePhrase(internal, grammar, hasSite) {
375
- var _a;
577
+ function buildRoutePhrase(clause, grammar, hasSite) {
578
+ var _a, _b;
376
579
  if (typeof grammar.routePhrase === "function") {
377
- return grammar.routePhrase({ hasSite, internal });
580
+ return grammar.routePhrase({ hasSite, clause });
378
581
  }
379
582
  if (typeof grammar.routePhrase === "string") {
380
583
  return grammar.routePhrase;
381
584
  }
382
- const text = (_a = internal.routeText) === null || _a === void 0 ? void 0 : _a.trim();
585
+ const text = (_b = (_a = clause.route) === null || _a === void 0 ? void 0 : _a.text) === null || _b === void 0 ? void 0 : _b.trim();
383
586
  if (!text) {
384
587
  return undefined;
385
588
  }
@@ -388,7 +591,7 @@ function buildRoutePhrase(internal, grammar, hasSite) {
388
591
  return text;
389
592
  }
390
593
  if (normalized === "oral") {
391
- return "by mouth";
594
+ return "orally";
392
595
  }
393
596
  if (normalized === "intravenous") {
394
597
  return "intravenously";
@@ -413,17 +616,34 @@ function buildRoutePhrase(internal, grammar, hasSite) {
413
616
  }
414
617
  return `via ${text}`;
415
618
  }
416
- function formatSite(internal, grammar) {
417
- var _a;
418
- const text = (_a = internal.siteText) === null || _a === void 0 ? void 0 : _a.trim();
619
+ function formatSite(clause, grammar) {
620
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
621
+ let text = (_b = (_a = clause.site) === null || _a === void 0 ? void 0 : _a.text) === null || _b === void 0 ? void 0 : _b.trim();
622
+ if (!text) {
623
+ const display = (_e = (_d = (_c = clause.site) === null || _c === void 0 ? void 0 : _c.coding) === null || _d === void 0 ? void 0 : _d.display) === null || _e === void 0 ? void 0 : _e.trim();
624
+ if (display) {
625
+ text = display.charAt(0).toLowerCase() + display.slice(1);
626
+ }
627
+ else {
628
+ text = (_h = (_g = (_f = clause.site) === null || _f === void 0 ? void 0 : _f.coding) === null || _g === void 0 ? void 0 : _g.code) === null || _h === void 0 ? void 0 : _h.trim();
629
+ }
630
+ }
419
631
  if (!text) {
420
632
  return undefined;
421
633
  }
422
634
  const lower = text.toLowerCase();
423
- if (internal.routeCode === types_1.RouteCode["Per rectum"]) {
424
- if (lower === "rectum" || lower === "rectal") {
425
- return undefined;
426
- }
635
+ const routeText = (_k = (_j = clause.route) === null || _j === void 0 ? void 0 : _j.text) === null || _k === void 0 ? void 0 : _k.trim().toLowerCase();
636
+ const isRectalRoute = ((_l = clause.route) === null || _l === void 0 ? void 0 : _l.code) === types_1.RouteCode["Per rectum"] ||
637
+ routeText === "rectum" ||
638
+ routeText === "rectal";
639
+ const isVaginalRoute = ((_m = clause.route) === null || _m === void 0 ? void 0 : _m.code) === types_1.RouteCode["Per vagina"] ||
640
+ routeText === "vagina" ||
641
+ routeText === "vaginal";
642
+ if (isRectalRoute && (lower === "rectum" || lower === "rectal")) {
643
+ return undefined;
644
+ }
645
+ if (isVaginalRoute && (lower === "vagina" || lower === "vaginal")) {
646
+ return undefined;
427
647
  }
428
648
  let preposition = grammar.sitePreposition;
429
649
  if (!preposition) {
@@ -439,7 +659,7 @@ function formatSite(internal, grammar) {
439
659
  else if (lower.includes("ear")) {
440
660
  preposition = "in";
441
661
  }
442
- else if (/(skin|arm|leg|thigh|abdomen|shoulder|hand|foot|cheek|forearm|back|buttock|hip|face|hair|scalp|forehead|chin|neck)/.test(lower)) {
662
+ else if (/(skin|head|temple|arm|leg|thigh|abdomen|shoulder|elbow|wrist|ankle|knee|hand|foot|cheek|forearm|back|chest|breast|axilla|armpit|groin|lip|buttock|hip|face|hair|scalp|forehead|eyelid|chin|neck)/.test(lower)) {
443
663
  preposition = "to";
444
664
  }
445
665
  else {
@@ -452,15 +672,7 @@ function formatSite(internal, grammar) {
452
672
  function formatSiteNoun(site, preposition) {
453
673
  const trimmed = site.trim();
454
674
  const lower = trimmed.toLowerCase();
455
- const skipArticlePrefixes = [
456
- "the ",
457
- "both ",
458
- "each ",
459
- "either ",
460
- "every ",
461
- "all ",
462
- "bilateral ",
463
- ];
675
+ const skipArticlePrefixes = ["the ", "both ", "each ", "either ", "every ", "all ", "bilateral "];
464
676
  for (const prefix of skipArticlePrefixes) {
465
677
  if (lower.startsWith(prefix)) {
466
678
  return trimmed;
@@ -470,107 +682,157 @@ function formatSiteNoun(site, preposition) {
470
682
  if (needsArticle || preposition === "at") {
471
683
  return `the ${trimmed}`;
472
684
  }
473
- if (/(eye|nostril|ear|arm|leg|thigh|abdomen|hand|foot|cheek|skin|back)/.test(lower)) {
474
- return `the ${trimmed}`;
475
- }
476
685
  return `the ${trimmed}`;
477
686
  }
478
- function describeDayOfWeek(internal) {
479
- if (!internal.dayOfWeek.length) {
687
+ function describeDayOfWeek(schedule) {
688
+ var _a, _b;
689
+ const dayOfWeek = (_a = schedule === null || schedule === void 0 ? void 0 : schedule.dayOfWeek) !== null && _a !== void 0 ? _a : [];
690
+ if (!dayOfWeek.length) {
480
691
  return undefined;
481
692
  }
482
- const days = internal.dayOfWeek.map((d) => { var _a; return (_a = DAY_NAMES[d]) !== null && _a !== void 0 ? _a : d; });
483
- if (!days.length) {
693
+ const days = [];
694
+ for (const day of dayOfWeek) {
695
+ days.push((_b = DAY_NAMES[day]) !== null && _b !== void 0 ? _b : day);
696
+ }
697
+ return days.length ? `on ${joinWithAnd(days)}` : undefined;
698
+ }
699
+ function formatDurationShort(schedule) {
700
+ if (schedule.duration === undefined || !schedule.durationUnit) {
484
701
  return undefined;
485
702
  }
486
- return `on ${joinWithAnd(days)}`;
703
+ const base = stripTrailingZero(schedule.duration);
704
+ const qualifier = schedule.durationMax !== undefined && schedule.durationMax !== schedule.duration
705
+ ? `${base}-${stripTrailingZero(schedule.durationMax)}`
706
+ : base;
707
+ return `x${qualifier}${schedule.durationUnit}`;
487
708
  }
488
- function formatInternal(internal, style, localization) {
489
- const defaults = {
490
- short: formatShort(internal),
491
- long: formatLong(internal)
492
- };
493
- if (!localization) {
494
- return defaults[style];
709
+ function describeDuration(schedule) {
710
+ if (!schedule || schedule.duration === undefined || !schedule.durationUnit) {
711
+ return undefined;
495
712
  }
496
- const formatDefault = (target) => defaults[target];
497
- if (style === "short" && localization.formatShort) {
498
- const context = {
499
- style: "short",
500
- internal,
501
- defaultText: defaults.short,
502
- formatDefault
503
- };
504
- return localization.formatShort(context);
713
+ const unit = schedule.durationUnit;
714
+ const label = (value) => {
715
+ switch (unit) {
716
+ case types_1.FhirPeriodUnit.Minute:
717
+ return value === 1 ? "minute" : "minutes";
718
+ case types_1.FhirPeriodUnit.Hour:
719
+ return value === 1 ? "hour" : "hours";
720
+ case types_1.FhirPeriodUnit.Day:
721
+ return value === 1 ? "day" : "days";
722
+ case types_1.FhirPeriodUnit.Week:
723
+ return value === 1 ? "week" : "weeks";
724
+ case types_1.FhirPeriodUnit.Month:
725
+ return value === 1 ? "month" : "months";
726
+ case types_1.FhirPeriodUnit.Year:
727
+ return value === 1 ? "year" : "years";
728
+ default:
729
+ return value === 1 ? "unit" : "units";
730
+ }
731
+ };
732
+ if (schedule.durationMax !== undefined && schedule.durationMax !== schedule.duration) {
733
+ return `for ${stripTrailingZero(schedule.duration)} to ${stripTrailingZero(schedule.durationMax)} ${label(schedule.durationMax)}`;
505
734
  }
506
- if (style === "long" && localization.formatLong) {
507
- const context = {
508
- style: "long",
509
- internal,
510
- defaultText: defaults.long,
511
- formatDefault
512
- };
513
- return localization.formatLong(context);
735
+ return `for ${stripTrailingZero(schedule.duration)} ${label(schedule.duration)}`;
736
+ }
737
+ function shouldUseGenericMedicationObject(clause) {
738
+ var _a, _b;
739
+ const methodText = (_b = (_a = clause.method) === null || _a === void 0 ? void 0 : _a.text) === null || _b === void 0 ? void 0 : _b.trim();
740
+ switch (methodText) {
741
+ case "Apply sunscreen":
742
+ case "Reapply sunscreen":
743
+ case "Use shampoo":
744
+ return false;
745
+ default:
746
+ return true;
514
747
  }
515
- return defaults[style];
516
748
  }
517
- function formatShort(internal) {
749
+ function shouldSuppressRoutePhrase(clause, grammar, verb) {
518
750
  var _a;
751
+ if (((_a = clause.route) === null || _a === void 0 ? void 0 : _a.code) !== types_1.RouteCode["Oral route"]) {
752
+ return false;
753
+ }
754
+ if (grammar.routePhrase !== "orally") {
755
+ return false;
756
+ }
757
+ switch (verb) {
758
+ case "Drink":
759
+ case "Swallow":
760
+ return true;
761
+ default:
762
+ return false;
763
+ }
764
+ }
765
+ function formatShort(clause) {
766
+ var _a, _b, _c, _d, _e, _f;
767
+ const schedule = scheduleOf(clause);
519
768
  const parts = [];
520
- const dosePart = formatDoseShort(internal);
769
+ const dosePart = formatDoseShort(clause.dose);
521
770
  if (dosePart) {
522
771
  parts.push(dosePart);
523
772
  }
524
- if (internal.routeCode) {
525
- const short = ROUTE_SHORT[internal.routeCode];
773
+ const routeCode = (_a = clause.route) === null || _a === void 0 ? void 0 : _a.code;
774
+ const routeText = (_b = clause.route) === null || _b === void 0 ? void 0 : _b.text;
775
+ if (routeCode) {
776
+ const short = ROUTE_SHORT[routeCode];
526
777
  if (short) {
527
778
  parts.push(short);
528
779
  }
529
- else if (internal.routeText) {
530
- parts.push(internal.routeText);
780
+ else if (routeText) {
781
+ parts.push(routeText);
531
782
  }
532
783
  }
533
- else if (internal.routeText) {
534
- parts.push(internal.routeText);
784
+ else if (routeText) {
785
+ parts.push(routeText);
535
786
  }
536
- if (internal.timingCode) {
537
- parts.push(internal.timingCode);
787
+ if (schedule.timingCode) {
788
+ parts.push(schedule.timingCode);
538
789
  }
539
- else if (internal.frequency !== undefined &&
540
- internal.frequencyMax !== undefined &&
541
- internal.periodUnit === types_1.FhirPeriodUnit.Day &&
542
- (!internal.period || internal.period === 1)) {
543
- parts.push(`${stripTrailingZero(internal.frequency)}-${stripTrailingZero(internal.frequencyMax)}x/d`);
790
+ else if (schedule.frequency !== undefined &&
791
+ schedule.frequencyMax !== undefined &&
792
+ schedule.periodUnit === types_1.FhirPeriodUnit.Day &&
793
+ (!schedule.period || schedule.period === 1)) {
794
+ parts.push(`${stripTrailingZero(schedule.frequency)}-${stripTrailingZero(schedule.frequencyMax)}x/d`);
544
795
  }
545
- else if (internal.frequency &&
546
- internal.periodUnit === types_1.FhirPeriodUnit.Day &&
547
- (!internal.period || internal.period === 1)) {
548
- parts.push(`${stripTrailingZero(internal.frequency)}x/d`);
796
+ else if (schedule.frequency &&
797
+ schedule.periodUnit === types_1.FhirPeriodUnit.Day &&
798
+ (!schedule.period || schedule.period === 1)) {
799
+ parts.push(`${stripTrailingZero(schedule.frequency)}x/d`);
549
800
  }
550
- else if (internal.period && internal.periodUnit) {
551
- const base = stripTrailingZero(internal.period);
552
- const qualifier = internal.periodMax && internal.periodMax !== internal.period
553
- ? `${base}-${stripTrailingZero(internal.periodMax)}`
801
+ else if (schedule.period && schedule.periodUnit) {
802
+ const base = stripTrailingZero(schedule.period);
803
+ const qualifier = schedule.periodMax && schedule.periodMax !== schedule.period
804
+ ? `${base}-${stripTrailingZero(schedule.periodMax)}`
554
805
  : base;
555
- parts.push(`Q${qualifier}${internal.periodUnit.toUpperCase()}`);
806
+ parts.push(`Q${qualifier}${schedule.periodUnit.toUpperCase()}`);
807
+ }
808
+ if ((_c = schedule.when) === null || _c === void 0 ? void 0 : _c.length) {
809
+ parts.push(schedule.when.join(" "));
556
810
  }
557
- if (internal.when.length) {
558
- parts.push(internal.when.join(" "));
811
+ if ((_d = schedule.dayOfWeek) === null || _d === void 0 ? void 0 : _d.length) {
812
+ const days = [];
813
+ for (const day of schedule.dayOfWeek) {
814
+ days.push(day.charAt(0).toUpperCase() + day.slice(1, 3));
815
+ }
816
+ parts.push(days.join(","));
559
817
  }
560
- if (internal.dayOfWeek.length) {
561
- parts.push(internal.dayOfWeek
562
- .map((d) => d.charAt(0).toUpperCase() + d.slice(1, 3))
563
- .join(","));
818
+ if ((_e = schedule.timeOfDay) === null || _e === void 0 ? void 0 : _e.length) {
819
+ const times = [];
820
+ for (const time of schedule.timeOfDay) {
821
+ times.push(time.slice(0, 5));
822
+ }
823
+ parts.push(times.join(","));
564
824
  }
565
- if ((_a = internal.timeOfDay) === null || _a === void 0 ? void 0 : _a.length) {
566
- parts.push(internal.timeOfDay.map(t => t.slice(0, 5)).join(","));
825
+ if (schedule.count !== undefined) {
826
+ parts.push(`x${stripTrailingZero(schedule.count)}`);
567
827
  }
568
- if (internal.count !== undefined) {
569
- parts.push(`x${stripTrailingZero(internal.count)}`);
828
+ const durationShort = formatDurationShort(schedule);
829
+ if (durationShort) {
830
+ parts.push(durationShort);
570
831
  }
571
- if (internal.asNeeded) {
572
- if (internal.asNeededReason) {
573
- parts.push(`PRN ${internal.asNeededReason}`);
832
+ if ((_f = clause.prn) === null || _f === void 0 ? void 0 : _f.enabled) {
833
+ const reason = (0, prn_1.getPreferredCanonicalPrnReasonText)(clause.prn.reason, clause.prn.reasons);
834
+ if (reason) {
835
+ parts.push(`PRN ${reason}`);
574
836
  }
575
837
  else {
576
838
  parts.push("PRN");
@@ -578,40 +840,50 @@ function formatShort(internal) {
578
840
  }
579
841
  return parts.filter(Boolean).join(" ");
580
842
  }
581
- function formatLong(internal) {
582
- var _a, _b;
583
- const grammar = resolveRouteGrammar(internal);
584
- const dosePart = (_a = formatDoseLong(internal)) !== null && _a !== void 0 ? _a : "the medication";
585
- const sitePart = formatSite(internal, grammar);
586
- const routePart = buildRoutePhrase(internal, grammar, Boolean(sitePart));
587
- const frequencyPart = describeFrequency(internal);
588
- const eventParts = collectWhenPhrases(internal);
589
- if ((_b = internal.timeOfDay) === null || _b === void 0 ? void 0 : _b.length) {
843
+ function formatLong(clause, options) {
844
+ var _a, _b, _c, _d, _e, _f;
845
+ const schedule = scheduleOf(clause);
846
+ const grammar = resolveRouteGrammar(clause);
847
+ const verb = resolveMethodVerb(clause, grammar);
848
+ const explicitDosePart = formatDoseLong(clause.dose);
849
+ const dosePart = explicitDosePart !== null && explicitDosePart !== void 0 ? explicitDosePart : (shouldUseGenericMedicationObject(clause) ? "the medication" : undefined);
850
+ const sitePart = formatSite(clause, grammar);
851
+ const routePart = shouldSuppressRoutePhrase(clause, grammar, verb)
852
+ ? undefined
853
+ : buildRoutePhrase(clause, grammar, Boolean(sitePart));
854
+ const standaloneOccurrenceCount = describeStandaloneOccurrenceCount(schedule);
855
+ const frequencyPart = (_b = (_a = describeFrequency(schedule)) !== null && _a !== void 0 ? _a : standaloneOccurrenceCount) !== null && _b !== void 0 ? _b : describeFrequencyCount((0, timing_summary_1.inferDailyOccurrenceCount)(schedule, options));
856
+ const eventParts = collectWhenPhrases(schedule, options);
857
+ if ((_c = schedule.timeOfDay) === null || _c === void 0 ? void 0 : _c.length) {
590
858
  const timeStrings = [];
591
- for (const time of internal.timeOfDay) {
859
+ for (const time of schedule.timeOfDay) {
592
860
  const parts = time.split(":");
593
- const h = parseInt(parts[0], 10);
594
- const m = parseInt(parts[1], 10);
595
- const isAm = h < 12;
596
- const displayH = h % 12 || 12;
597
- const displayM = m < 10 ? `0${m}` : `${m}`;
598
- timeStrings.push(`${displayH}:${displayM}${isAm ? " am" : " pm"}`);
599
- }
600
- if (timeStrings.length > 0) {
861
+ const hours = Number(parts[0]);
862
+ const minutes = Number(parts[1]);
863
+ if (!Number.isFinite(hours) || !Number.isFinite(minutes)) {
864
+ continue;
865
+ }
866
+ const isAm = hours < 12;
867
+ const displayHours = hours % 12 || 12;
868
+ const displayMinutes = minutes < 10 ? `0${minutes}` : `${minutes}`;
869
+ timeStrings.push(`${displayHours}:${displayMinutes}${isAm ? " am" : " pm"}`);
870
+ }
871
+ if (timeStrings.length) {
601
872
  eventParts.push(`at ${timeStrings.join(", ")}`);
602
873
  }
603
874
  }
604
875
  const timing = combineFrequencyAndEvents(frequencyPart, eventParts);
605
- const dayPart = describeDayOfWeek(internal);
606
- const countPart = internal.count
607
- ? `for ${stripTrailingZero(internal.count)} ${internal.count === 1 ? "dose" : "doses"}`
876
+ const dayPart = describeDayOfWeek(schedule);
877
+ const countPart = schedule.count !== undefined && !standaloneOccurrenceCount
878
+ ? `for ${stripTrailingZero(schedule.count)} ${schedule.count === 1 ? "dose" : "doses"}`
608
879
  : undefined;
609
- const asNeededPart = internal.asNeeded
610
- ? internal.asNeededReason
611
- ? `as needed for ${internal.asNeededReason}`
612
- : "as needed"
613
- : undefined;
614
- const segments = [dosePart];
880
+ const durationPart = describeDuration(schedule);
881
+ const reason = (0, prn_1.getPreferredCanonicalPrnReasonText)((_d = clause.prn) === null || _d === void 0 ? void 0 : _d.reason, (_e = clause.prn) === null || _e === void 0 ? void 0 : _e.reasons);
882
+ const asNeededPart = ((_f = clause.prn) === null || _f === void 0 ? void 0 : _f.enabled) ? (reason ? `as needed for ${reason}` : "as needed") : undefined;
883
+ const segments = [];
884
+ if (dosePart) {
885
+ segments.push(dosePart);
886
+ }
615
887
  if (routePart) {
616
888
  segments.push(routePart);
617
889
  }
@@ -627,6 +899,9 @@ function formatLong(internal) {
627
899
  if (countPart) {
628
900
  segments.push(countPart);
629
901
  }
902
+ if (durationPart) {
903
+ segments.push(durationPart);
904
+ }
630
905
  if (asNeededPart) {
631
906
  segments.push(asNeededPart);
632
907
  }
@@ -634,34 +909,121 @@ function formatLong(internal) {
634
909
  segments.push(sitePart);
635
910
  }
636
911
  const body = segments.filter(Boolean).join(" ").replace(/\s+/g, " ").trim();
912
+ const instructionPhrases = [];
913
+ const instructionText = formatAdditionalInstructions(clause);
914
+ if (instructionText) {
915
+ instructionPhrases.push(instructionText);
916
+ }
917
+ const patientInstruction = formatPatientInstructionSentence(clause.patientInstruction);
918
+ if (patientInstruction) {
919
+ instructionPhrases.push(patientInstruction);
920
+ }
921
+ const trailingInstructionText = instructionPhrases.join(" ").trim() || undefined;
637
922
  if (!body) {
638
- const instructionText = formatAdditionalInstructions(internal);
639
- if (!instructionText) {
640
- return `${grammar.verb}.`;
923
+ if (!trailingInstructionText) {
924
+ return `${verb}.`;
641
925
  }
642
- return `${grammar.verb}. ${instructionText}`.trim();
926
+ return `${verb}. ${trailingInstructionText}`.trim();
643
927
  }
644
- const instructionText = formatAdditionalInstructions(internal);
645
- const baseSentence = `${grammar.verb} ${body}.`;
646
- return instructionText ? `${baseSentence} ${instructionText}` : baseSentence;
928
+ const baseSentence = `${verb} ${body}.`;
929
+ return trailingInstructionText ? `${baseSentence} ${trailingInstructionText}` : baseSentence;
647
930
  }
648
- function formatAdditionalInstructions(internal) {
649
- var _a;
650
- if (!((_a = internal.additionalInstructions) === null || _a === void 0 ? void 0 : _a.length)) {
931
+ function formatAdditionalInstructions(clause) {
932
+ var _a, _b, _c, _d, _e, _f;
933
+ const instructions = (_a = clause.additionalInstructions) !== null && _a !== void 0 ? _a : [];
934
+ if (!instructions.length) {
651
935
  return undefined;
652
936
  }
653
- const phrases = internal.additionalInstructions
654
- .map((instruction) => { var _a; return instruction.text || ((_a = instruction.coding) === null || _a === void 0 ? void 0 : _a.display); })
655
- .filter((text) => Boolean(text))
656
- .map((text) => text.trim())
657
- .filter((text) => text.length > 0);
937
+ const phrases = [];
938
+ const grammar = resolveRouteGrammar(clause);
939
+ const verb = resolveMethodVerb(clause, grammar);
940
+ for (const instruction of instructions) {
941
+ if (((_b = instruction.coding) === null || _b === void 0 ? void 0 : _b.code) === SLOWLY_QUALIFIER_CODE) {
942
+ const contextual = verb ? `${verb} slowly` : "Slowly";
943
+ phrases.push(contextual);
944
+ continue;
945
+ }
946
+ if (((_c = instruction.coding) === null || _c === void 0 ? void 0 : _c.code) === EMPTY_STOMACH_QUALIFIER_CODE ||
947
+ ((_d = instruction.frames) === null || _d === void 0 ? void 0 : _d.some((frame) => frame.relation === types_1.AdviceRelation.On &&
948
+ frame.args.some((arg) => arg.role === types_1.AdviceArgumentRole.MealState &&
949
+ arg.conceptId === "empty_stomach")))) {
950
+ phrases.push("On an empty stomach");
951
+ continue;
952
+ }
953
+ const text = (_e = instruction.text) !== null && _e !== void 0 ? _e : (_f = instruction.coding) === null || _f === void 0 ? void 0 : _f.display;
954
+ if (!text) {
955
+ continue;
956
+ }
957
+ const trimmed = text.trim();
958
+ if (trimmed) {
959
+ phrases.push(trimmed);
960
+ }
961
+ }
658
962
  if (!phrases.length) {
659
963
  return undefined;
660
964
  }
661
- return phrases
662
- .map((phrase) => (/[.!?]$/.test(phrase) ? phrase : `${phrase}.`))
663
- .join(" ")
664
- .trim();
965
+ return phrases.map((phrase) => (/[.!?]$/.test(phrase) ? phrase : `${phrase}.`)).join(" ").trim();
966
+ }
967
+ function firstCanonicalClause(internal) {
968
+ const clauses = internal.clauses;
969
+ if (clauses.length > 0) {
970
+ return clauses[0];
971
+ }
972
+ return {
973
+ kind: "administration",
974
+ rawText: internal.input,
975
+ raw: { start: 0, end: internal.input.length, text: internal.input },
976
+ leftovers: [],
977
+ evidence: [],
978
+ confidence: 0
979
+ };
980
+ }
981
+ function formatCanonicalClause(clause, style, localization, options) {
982
+ let shortDefault;
983
+ let longDefault;
984
+ const formatDefault = (target) => {
985
+ switch (target) {
986
+ case "short":
987
+ if (shortDefault === undefined) {
988
+ shortDefault = formatShort(clause);
989
+ }
990
+ return shortDefault;
991
+ case "long":
992
+ if (longDefault === undefined) {
993
+ longDefault = formatLong(clause, options);
994
+ }
995
+ return longDefault;
996
+ }
997
+ };
998
+ if (!localization) {
999
+ return formatDefault(style);
1000
+ }
1001
+ if (style === "short" && localization.formatShort) {
1002
+ const context = {
1003
+ style: "short",
1004
+ clause,
1005
+ defaultText: formatDefault("short"),
1006
+ groupMealTimingsByRelation: Boolean(options === null || options === void 0 ? void 0 : options.groupMealTimingsByRelation),
1007
+ includeTimesPerDaySummary: Boolean(options === null || options === void 0 ? void 0 : options.includeTimesPerDaySummary),
1008
+ formatDefault
1009
+ };
1010
+ return localization.formatShort(context);
1011
+ }
1012
+ if (style === "long" && localization.formatLong) {
1013
+ const context = {
1014
+ style: "long",
1015
+ clause,
1016
+ defaultText: formatDefault("long"),
1017
+ groupMealTimingsByRelation: Boolean(options === null || options === void 0 ? void 0 : options.groupMealTimingsByRelation),
1018
+ includeTimesPerDaySummary: Boolean(options === null || options === void 0 ? void 0 : options.includeTimesPerDaySummary),
1019
+ formatDefault
1020
+ };
1021
+ return localization.formatLong(context);
1022
+ }
1023
+ return formatDefault(style);
1024
+ }
1025
+ function formatInternal(internal, style, localization, options) {
1026
+ return formatCanonicalClause(firstCanonicalClause(internal), style, localization, options);
665
1027
  }
666
1028
  function stripTrailingZero(value) {
667
1029
  const text = value.toString();