ezmedicationinput 0.1.21 → 0.1.22

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/README.md CHANGED
@@ -293,8 +293,15 @@ You can specify the number of times (total count) the medication is supposed to
293
293
  - `context`: optional medication context (dosage form, strength, container
294
294
  metadata) used to infer default units when a sig omits explicit units. Pass
295
295
  `null` to explicitly disable context-based inference.
296
- - `smartMealExpansion`: when `true`, generic AC/PC/C tokens expand into specific EventTiming combinations (e.g. `1x2 po ac` → `ACM` + `ACV`).
296
+ - `smartMealExpansion`: when `true`, generic AC/PC/C meal abbreviations and
297
+ cadence-only instructions expand into concrete with-meal EventTiming
298
+ combinations (e.g. `1x3` → breakfast/lunch/dinner). This also respects
299
+ `context.mealRelation` when provided and only applies to schedules with four
300
+ or fewer daily doses.
297
301
  - `twoPerDayPair`: controls whether 2× AC/PC/C doses expand to breakfast+dinner (default) or breakfast+lunch.
302
+ - `assumeSingleDiscreteDose`: when `true`, missing discrete doses (such as
303
+ tablets or capsules) default to a single unit when the parser can infer a
304
+ countable unit from context.
298
305
  - `eventClock`: optional map of `EventTiming` codes to HH:mm strings that drives chronological ordering of parsed `when` values.
299
306
  - `allowHouseholdVolumeUnits`: defaults to `true`; set to `false` to ignore
300
307
  teaspoon/tablespoon units during parsing and suggestions.
package/dist/parser.js CHANGED
@@ -134,6 +134,36 @@ const SITE_FILLER_WORDS = new Set([
134
134
  "my"
135
135
  ]);
136
136
  const HOUSEHOLD_VOLUME_UNIT_SET = new Set(maps_1.HOUSEHOLD_VOLUME_UNITS.map((unit) => unit.toLowerCase()));
137
+ const DISCRETE_UNIT_SET = new Set([
138
+ "tab",
139
+ "tabs",
140
+ "tablet",
141
+ "tablets",
142
+ "cap",
143
+ "caps",
144
+ "capsule",
145
+ "capsules",
146
+ "puff",
147
+ "puffs",
148
+ "spray",
149
+ "sprays",
150
+ "drop",
151
+ "drops",
152
+ "patch",
153
+ "patches",
154
+ "suppository",
155
+ "suppositories",
156
+ "implant",
157
+ "implants",
158
+ "piece",
159
+ "pieces",
160
+ "stick",
161
+ "sticks",
162
+ "pessary",
163
+ "pessaries",
164
+ "lozenge",
165
+ "lozenges"
166
+ ]);
137
167
  const OCULAR_DIRECTION_WORDS = new Set([
138
168
  "left",
139
169
  "right",
@@ -1067,10 +1097,12 @@ function reconcileMealTimingSpecificity(internal) {
1067
1097
  ]);
1068
1098
  }
1069
1099
  // Optionally replace generic meal tokens with concrete breakfast/lunch/dinner
1070
- // EventTiming codes when the cadence makes the intent obvious.
1100
+ // EventTiming codes when the cadence or explicit meal abbreviations make the
1101
+ // intent obvious.
1071
1102
  function expandMealTimings(internal, options) {
1072
1103
  var _a, _b, _c;
1073
- if (!(options === null || options === void 0 ? void 0 : options.smartMealExpansion)) {
1104
+ const allowSmartExpansion = (options === null || options === void 0 ? void 0 : options.smartMealExpansion) === true;
1105
+ if (!allowSmartExpansion) {
1074
1106
  return;
1075
1107
  }
1076
1108
  if (internal.when.some((code) => SPECIFIC_MEAL_TIMINGS.has(code))) {
@@ -1080,10 +1112,11 @@ function expandMealTimings(internal, options) {
1080
1112
  if (!frequency || frequency < 1 || frequency > 4) {
1081
1113
  return;
1082
1114
  }
1083
- const hasGeneralMealToken = (0, array_1.arrayIncludes)(internal.when, types_1.EventTiming["Before Meal"]) ||
1084
- (0, array_1.arrayIncludes)(internal.when, types_1.EventTiming["After Meal"]) ||
1085
- (0, array_1.arrayIncludes)(internal.when, types_1.EventTiming.Meal);
1086
1115
  const needsDefaultExpansion = internal.when.length === 0 && frequency >= 2;
1116
+ const hasBeforeMeal = (0, array_1.arrayIncludes)(internal.when, types_1.EventTiming["Before Meal"]);
1117
+ const hasAfterMeal = (0, array_1.arrayIncludes)(internal.when, types_1.EventTiming["After Meal"]);
1118
+ const hasWithMeal = (0, array_1.arrayIncludes)(internal.when, types_1.EventTiming.Meal);
1119
+ const hasGeneralMealToken = hasBeforeMeal || hasAfterMeal || hasWithMeal;
1087
1120
  if (!hasGeneralMealToken && !needsDefaultExpansion) {
1088
1121
  return;
1089
1122
  }
@@ -1103,7 +1136,7 @@ function expandMealTimings(internal, options) {
1103
1136
  if (internal.frequencyMax !== undefined || internal.periodMax !== undefined) {
1104
1137
  return;
1105
1138
  }
1106
- const pairPreference = (_a = options.twoPerDayPair) !== null && _a !== void 0 ? _a : "breakfast+dinner";
1139
+ const pairPreference = (_a = options === null || options === void 0 ? void 0 : options.twoPerDayPair) !== null && _a !== void 0 ? _a : "breakfast+dinner";
1107
1140
  const replacements = [];
1108
1141
  const addReplacement = (general, base, removeGeneral) => {
1109
1142
  const specifics = computeMealExpansions(base, frequency, pairPreference);
@@ -1111,13 +1144,13 @@ function expandMealTimings(internal, options) {
1111
1144
  replacements.push({ general, specifics, removeGeneral });
1112
1145
  }
1113
1146
  };
1114
- if ((0, array_1.arrayIncludes)(internal.when, types_1.EventTiming["Before Meal"])) {
1147
+ if (hasBeforeMeal) {
1115
1148
  addReplacement(types_1.EventTiming["Before Meal"], "before", true);
1116
1149
  }
1117
- if ((0, array_1.arrayIncludes)(internal.when, types_1.EventTiming["After Meal"])) {
1150
+ if (hasAfterMeal) {
1118
1151
  addReplacement(types_1.EventTiming["After Meal"], "after", true);
1119
1152
  }
1120
- if ((0, array_1.arrayIncludes)(internal.when, types_1.EventTiming.Meal)) {
1153
+ if (hasWithMeal) {
1121
1154
  addReplacement(types_1.EventTiming.Meal, "with", true);
1122
1155
  }
1123
1156
  if (needsDefaultExpansion) {
@@ -1869,6 +1902,13 @@ function parseInternal(input, options) {
1869
1902
  internal.unit = fallbackUnit;
1870
1903
  }
1871
1904
  }
1905
+ if ((options === null || options === void 0 ? void 0 : options.assumeSingleDiscreteDose) &&
1906
+ internal.dose === undefined &&
1907
+ internal.doseRange === undefined &&
1908
+ internal.unit !== undefined &&
1909
+ isDiscreteUnit(internal.unit)) {
1910
+ internal.dose = 1;
1911
+ }
1872
1912
  // Frequency defaults when timing code implies it
1873
1913
  if (internal.frequency === undefined &&
1874
1914
  internal.period === undefined &&
@@ -3276,6 +3316,12 @@ function enforceHouseholdUnitPolicy(unit, options) {
3276
3316
  }
3277
3317
  return unit;
3278
3318
  }
3319
+ function isDiscreteUnit(unit) {
3320
+ if (!unit) {
3321
+ return false;
3322
+ }
3323
+ return DISCRETE_UNIT_SET.has(unit.trim().toLowerCase());
3324
+ }
3279
3325
  function inferUnitFromRouteHints(internal) {
3280
3326
  if (internal.routeCode) {
3281
3327
  const unit = maps_1.DEFAULT_UNIT_BY_ROUTE[internal.routeCode];
package/dist/types.d.ts CHANGED
@@ -429,9 +429,17 @@ export interface ParseOptions extends FormatOptions {
429
429
  eventClock?: EventClockMap;
430
430
  allowDiscouraged?: boolean;
431
431
  /**
432
- * When enabled the parser will expand generic meal timing tokens (AC/PC/C)
433
- * into specific breakfast/lunch/dinner (and bedtime) EventTiming entries
434
- * based on the detected daily frequency.
432
+ * When enabled the parser will assume a single discrete unit (e.g., one
433
+ * tablet/capsule) when no explicit dose is provided and the inferred unit is
434
+ * countable.
435
+ */
436
+ assumeSingleDiscreteDose?: boolean;
437
+ /**
438
+ * Enables inferring with-meal timings when explicit meal language is present
439
+ * or implied by cadence alone. Generic meal abbreviations (AC/PC/C) and
440
+ * cadence-only instructions expand into specific breakfast/lunch/dinner (and
441
+ * bedtime) EventTiming entries. Works in conjunction with
442
+ * `context.mealRelation` when provided.
435
443
  */
436
444
  smartMealExpansion?: boolean;
437
445
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ezmedicationinput",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "description": "Parse concise medication sigs into FHIR R5 Dosage JSON",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",