ezmedicationinput 0.1.30 → 0.1.32

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/maps.js CHANGED
@@ -700,6 +700,8 @@ exports.EVENT_TIMING_TOKENS = {
700
700
  night: types_1.EventTiming.Night,
701
701
  hs: types_1.EventTiming["Before Sleep"],
702
702
  bedtime: types_1.EventTiming["Before Sleep"],
703
+ bed: types_1.EventTiming["Before Sleep"],
704
+ sleep: types_1.EventTiming["Before Sleep"],
703
705
  wake: types_1.EventTiming.Wake,
704
706
  waking: types_1.EventTiming.Wake,
705
707
  stat: types_1.EventTiming.Immediate
@@ -1334,6 +1336,18 @@ const DEFAULT_PRN_REASON_SOURCE = [
1334
1336
  i18n: { th: "นอนหลับ" }
1335
1337
  }
1336
1338
  },
1339
+ {
1340
+ names: ["sleepiness", "sleepy", "drowsiness", "drowsy"],
1341
+ definition: {
1342
+ coding: {
1343
+ system: SNOMED_SYSTEM,
1344
+ code: "79519003",
1345
+ display: "Drowsiness"
1346
+ },
1347
+ text: "Sleepiness",
1348
+ i18n: { th: "ง่วงนอน" }
1349
+ }
1350
+ },
1337
1351
  {
1338
1352
  names: ["cough", "coughing"],
1339
1353
  definition: {
package/dist/parser.js CHANGED
@@ -190,6 +190,9 @@ const COMBO_EVENT_TIMINGS = {
190
190
  "early evening": types_1.EventTiming["Early Evening"],
191
191
  "late evening": types_1.EventTiming["Late Evening"],
192
192
  "after sleep": types_1.EventTiming["After Sleep"],
193
+ "before bed": types_1.EventTiming["Before Sleep"],
194
+ "before bedtime": types_1.EventTiming["Before Sleep"],
195
+ "before sleep": types_1.EventTiming["Before Sleep"],
193
196
  "upon waking": types_1.EventTiming.Wake
194
197
  };
195
198
  const MEAL_CONTEXT_CONNECTORS = new Set(["and", "or", "&", "+", "plus"]);
@@ -781,39 +784,17 @@ function tryParseTimeBasedSchedule(internal, tokens, index) {
781
784
  const token = tokens[index];
782
785
  if (internal.consumed.has(token.index))
783
786
  return false;
784
- // Handle connectors like "and at" or just "and" before a time.
785
- // This prevents rogue "and" from leaking into Additional Instructions
786
- // when it serves as a connector between schedule parts.
787
- let isAndPrefix = false;
788
787
  let isAtPrefix = token.lower === "@" || token.lower === "at";
789
- if (token.lower === "and" && !isAtPrefix) {
790
- const next = tokens[index + 1];
791
- if (next && !internal.consumed.has(next.index)) {
792
- const nextLower = next.lower;
793
- // If "and" is followed by "at", "@", or a number, it's a connector for this time block
794
- if (nextLower === "@" || nextLower === "at" || /^\d/.test(nextLower)) {
795
- isAndPrefix = true;
796
- if (nextLower === "@" || nextLower === "at") {
797
- isAtPrefix = true;
798
- }
799
- }
800
- }
801
- }
802
- if (!isAtPrefix && !isAndPrefix && !/^\d/.test(token.lower))
788
+ if (!isAtPrefix && !/^\d/.test(token.lower))
803
789
  return false;
804
790
  let nextIndex = index;
805
- if (isAndPrefix)
806
- nextIndex++;
807
791
  if (isAtPrefix)
808
792
  nextIndex++;
809
793
  const times = [];
810
794
  const consumedIndices = [];
811
795
  const timeTokens = [];
812
- if (isAndPrefix)
813
- consumedIndices.push(index);
814
796
  if (isAtPrefix) {
815
- // If we have "and at", at is the second token (index + 1)
816
- consumedIndices.push(isAndPrefix ? index + 1 : index);
797
+ consumedIndices.push(index);
817
798
  }
818
799
  while (nextIndex < tokens.length) {
819
800
  const nextToken = tokens[nextIndex];
@@ -1518,6 +1499,24 @@ function applyWhenToken(internal, token, code) {
1518
1499
  addWhen(internal.when, code);
1519
1500
  mark(internal.consumed, token);
1520
1501
  }
1502
+ function isTimingAnchorOrPrefix(tokens, index, prnReasonStart) {
1503
+ const token = tokens[index];
1504
+ if (!token)
1505
+ return false;
1506
+ // Cautious handling of "sleep" in PRN zone
1507
+ if (prnReasonStart !== undefined && index >= prnReasonStart && token.lower === "sleep") {
1508
+ return false;
1509
+ }
1510
+ const lower = token.lower;
1511
+ const nextToken = tokens[index + 1];
1512
+ const comboKey = nextToken ? `${lower} ${nextToken.lower}` : undefined;
1513
+ return Boolean(maps_1.EVENT_TIMING_TOKENS[lower] ||
1514
+ maps_1.TIMING_ABBREVIATIONS[lower] ||
1515
+ (comboKey && COMBO_EVENT_TIMINGS[comboKey]) ||
1516
+ (lower === "pc" || lower === "ac" || lower === "after" || lower === "before") ||
1517
+ (lower === "at" || lower === "@" || lower === "on" || lower === "with") ||
1518
+ /^\d/.test(lower));
1519
+ }
1521
1520
  function parseAnchorSequence(internal, tokens, index, prefixCode) {
1522
1521
  var _a;
1523
1522
  const token = tokens[index];
@@ -1936,7 +1935,25 @@ function parseInternal(input, options) {
1936
1935
  if (tryParseCompactQ(internal, tokens, i)) {
1937
1936
  continue;
1938
1937
  }
1938
+ // Skip connectors if they are followed by recognized timing tokens or prefixes
1939
+ if (MEAL_CONTEXT_CONNECTORS.has(token.lower) || token.lower === ",") {
1940
+ if (isTimingAnchorOrPrefix(tokens, i + 1, prnReasonStart)) {
1941
+ mark(internal.consumed, token);
1942
+ continue;
1943
+ }
1944
+ }
1939
1945
  // Event timing tokens
1946
+ const nextToken = tokens[i + 1];
1947
+ if (nextToken && !internal.consumed.has(nextToken.index)) {
1948
+ const lowerNext = nextToken.lower;
1949
+ const combo = `${token.lower} ${lowerNext}`;
1950
+ const comboWhen = (_f = COMBO_EVENT_TIMINGS[combo]) !== null && _f !== void 0 ? _f : maps_1.EVENT_TIMING_TOKENS[combo];
1951
+ if (comboWhen) {
1952
+ applyWhenToken(internal, token, comboWhen);
1953
+ mark(internal.consumed, nextToken);
1954
+ continue;
1955
+ }
1956
+ }
1940
1957
  if (token.lower === "pc" || token.lower === "ac" || token.lower === "after" || token.lower === "before") {
1941
1958
  parseAnchorSequence(internal, tokens, i, (token.lower === "pc" || token.lower === "after")
1942
1959
  ? types_1.EventTiming["After Meal"]
@@ -1957,17 +1974,6 @@ function parseInternal(input, options) {
1957
1974
  continue;
1958
1975
  }
1959
1976
  }
1960
- const nextToken = tokens[i + 1];
1961
- if (nextToken && !internal.consumed.has(nextToken.index)) {
1962
- const lowerNext = nextToken.lower;
1963
- const combo = `${token.lower} ${lowerNext}`;
1964
- const comboWhen = (_f = COMBO_EVENT_TIMINGS[combo]) !== null && _f !== void 0 ? _f : maps_1.EVENT_TIMING_TOKENS[combo];
1965
- if (comboWhen) {
1966
- applyWhenToken(internal, token, comboWhen);
1967
- mark(internal.consumed, nextToken);
1968
- continue;
1969
- }
1970
- }
1971
1977
  const customWhen = (_g = options === null || options === void 0 ? void 0 : options.whenMap) === null || _g === void 0 ? void 0 : _g[token.lower];
1972
1978
  if (customWhen) {
1973
1979
  applyWhenToken(internal, token, customWhen);
@@ -1975,8 +1981,15 @@ function parseInternal(input, options) {
1975
1981
  }
1976
1982
  const whenCode = maps_1.EVENT_TIMING_TOKENS[token.lower];
1977
1983
  if (whenCode) {
1978
- applyWhenToken(internal, token, whenCode);
1979
- continue;
1984
+ // If we are in the PRN zone, be cautious about common reason words like "sleep"
1985
+ // unless they were already handled by combo/anchor logic (which happens above).
1986
+ if (prnReasonStart !== undefined && i >= prnReasonStart && token.lower === "sleep") {
1987
+ // Leave for PRN reason
1988
+ }
1989
+ else {
1990
+ applyWhenToken(internal, token, whenCode);
1991
+ continue;
1992
+ }
1980
1993
  }
1981
1994
  // Day of week
1982
1995
  const day = maps_1.DAY_OF_WEEK_TOKENS[token.lower];
@@ -3183,7 +3196,7 @@ function collectAdditionalInstructions(internal, tokens) {
3183
3196
  : undefined
3184
3197
  });
3185
3198
  }
3186
- else {
3199
+ else if (!MEAL_CONTEXT_CONNECTORS.has(phrase.toLowerCase())) {
3187
3200
  instructions.push({ text: phrase });
3188
3201
  }
3189
3202
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ezmedicationinput",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "description": "Parse concise medication sigs into FHIR R5 Dosage JSON",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",