ezmedicationinput 0.1.43 → 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 +4 -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 +4 -2
  13. package/dist/format.js +515 -218
  14. package/dist/i18n.d.ts +2 -2
  15. package/dist/i18n.js +641 -199
  16. package/dist/index.d.ts +0 -1
  17. package/dist/index.js +219 -168
  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 +13 -3
  41. package/dist/timing-summary.js +7 -7
  42. package/dist/types.d.ts +237 -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
@@ -0,0 +1,344 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isTimingOnlySitePhrase = isTimingOnlySitePhrase;
4
+ exports.hasExternalSurfaceModifier = hasExternalSurfaceModifier;
5
+ exports.extractExplicitSiteCandidate = extractExplicitSiteCandidate;
6
+ exports.selectBestResidualSiteCandidate = selectBestResidualSiteCandidate;
7
+ exports.inferRouteHintFromSitePhrase = inferRouteHintFromSitePhrase;
8
+ const maps_1 = require("./maps");
9
+ const meaning_1 = require("./lexer/meaning");
10
+ const types_1 = require("./types");
11
+ function getInferableDefaultRouteHint(canonical, definition) {
12
+ const routeHint = definition === null || definition === void 0 ? void 0 : definition.routeHint;
13
+ if (!routeHint) {
14
+ return undefined;
15
+ }
16
+ switch (routeHint) {
17
+ case types_1.RouteCode["Ophthalmic route"]:
18
+ return canonical.includes("eye") || canonical.includes("eyelid")
19
+ ? routeHint
20
+ : undefined;
21
+ case types_1.RouteCode["Otic route"]:
22
+ return canonical.includes("ear") ? routeHint : undefined;
23
+ case types_1.RouteCode["Nasal route"]:
24
+ return canonical.includes("nostril") ||
25
+ canonical.includes("naris") ||
26
+ canonical.includes("nares") ||
27
+ canonical === "nose"
28
+ ? routeHint
29
+ : undefined;
30
+ case types_1.RouteCode["Oral route"]:
31
+ return canonical === "mouth" ? routeHint : undefined;
32
+ case types_1.RouteCode["Sublingual route"]:
33
+ return canonical.includes("tongue") ? routeHint : undefined;
34
+ case types_1.RouteCode["Buccal route"]:
35
+ return canonical.includes("cheek") ? routeHint : undefined;
36
+ case types_1.RouteCode["Intravenous route"]:
37
+ return canonical.includes("vein") ? routeHint : undefined;
38
+ case types_1.RouteCode["Per vagina"]:
39
+ return canonical.includes("vagina") ? routeHint : undefined;
40
+ case types_1.RouteCode["Per rectum"]:
41
+ return canonical.includes("rectum") || canonical === "anus"
42
+ ? routeHint
43
+ : undefined;
44
+ case types_1.RouteCode["Topical route"]:
45
+ return canonical.startsWith("affected ") ? routeHint : undefined;
46
+ default:
47
+ return undefined;
48
+ }
49
+ }
50
+ function mergeBodySiteRouteHint(canonical, customDefinition, defaultDefinition) {
51
+ var _a;
52
+ const definition = customDefinition !== null && customDefinition !== void 0 ? customDefinition : defaultDefinition;
53
+ if (!definition) {
54
+ return undefined;
55
+ }
56
+ const routeHint = (_a = customDefinition === null || customDefinition === void 0 ? void 0 : customDefinition.routeHint) !== null && _a !== void 0 ? _a : getInferableDefaultRouteHint(canonical, defaultDefinition);
57
+ if (routeHint === definition.routeHint) {
58
+ return definition;
59
+ }
60
+ return Object.assign(Object.assign({}, definition), { routeHint });
61
+ }
62
+ function isExplicitSiteBoundaryToken(lower, options, services) {
63
+ if (!lower) {
64
+ return true;
65
+ }
66
+ if ((0, meaning_1.isWorkflowInstructionWord)(lower)) {
67
+ return true;
68
+ }
69
+ if (maps_1.EVENT_TIMING_TOKENS[lower] ||
70
+ maps_1.TIMING_ABBREVIATIONS[lower] ||
71
+ maps_1.WORD_FREQUENCIES[lower] ||
72
+ services.hasFrequencyLikeWord(lower) ||
73
+ (0, meaning_1.isCountKeywordWord)(lower) ||
74
+ services.mapFrequencyAdverb(lower) ||
75
+ services.mapIntervalUnit(lower) ||
76
+ maps_1.MEAL_KEYWORDS[lower]) {
77
+ return true;
78
+ }
79
+ if (services.hasRouteLikeWord(lower, options) || services.normalizeUnit(lower, options)) {
80
+ return true;
81
+ }
82
+ if ((0, meaning_1.isApplicationVerbWord)(lower)) {
83
+ return true;
84
+ }
85
+ if (lower === "prn" || lower === "as" || lower === "needed" || lower === "for") {
86
+ return true;
87
+ }
88
+ if (services.isNumericToken(lower) && !services.isOrdinalToken(lower)) {
89
+ return true;
90
+ }
91
+ return false;
92
+ }
93
+ function isTimingOnlySitePhrase(words) {
94
+ if (!words.length) {
95
+ return true;
96
+ }
97
+ let phrase = "";
98
+ let allTimingWords = true;
99
+ for (let index = 0; index < words.length; index += 1) {
100
+ const word = words[index];
101
+ if (index > 0) {
102
+ phrase += " ";
103
+ }
104
+ phrase += word;
105
+ if (!maps_1.EVENT_TIMING_TOKENS[word] &&
106
+ !maps_1.TIMING_ABBREVIATIONS[word] &&
107
+ !maps_1.MEAL_KEYWORDS[word]) {
108
+ allTimingWords = false;
109
+ }
110
+ }
111
+ if (maps_1.EVENT_TIMING_TOKENS[phrase] ||
112
+ maps_1.TIMING_ABBREVIATIONS[phrase] ||
113
+ maps_1.MEAL_KEYWORDS[phrase]) {
114
+ return true;
115
+ }
116
+ return allTimingWords;
117
+ }
118
+ function hasExternalSurfaceModifier(siteText) {
119
+ const normalized = (0, maps_1.normalizeBodySiteKey)(siteText);
120
+ if (!normalized) {
121
+ return false;
122
+ }
123
+ const words = normalized.split(/\s+/);
124
+ for (const word of words) {
125
+ if (word.length > 0 && (0, meaning_1.isSiteSurfaceModifierWord)(word)) {
126
+ return true;
127
+ }
128
+ }
129
+ return false;
130
+ }
131
+ function extractExplicitSiteCandidate(tokens, consumed, startIndex, options, services) {
132
+ var _a;
133
+ const anchor = tokens[startIndex];
134
+ if (!anchor || consumed.has(anchor.index)) {
135
+ return undefined;
136
+ }
137
+ const anchorLower = services.normalizeTokenLower(anchor);
138
+ if (!(0, meaning_1.isSiteAnchorWord)(anchorLower)) {
139
+ return undefined;
140
+ }
141
+ if (!services.hasExplicitSiteIntroduction(startIndex)) {
142
+ return undefined;
143
+ }
144
+ const collected = [anchor.index];
145
+ const contentWords = [];
146
+ const candidateTextParts = [anchor.original];
147
+ let hasSiteHint = false;
148
+ for (let cursor = startIndex + 1; cursor < tokens.length; cursor += 1) {
149
+ const candidate = tokens[cursor];
150
+ if (!candidate || consumed.has(candidate.index)) {
151
+ break;
152
+ }
153
+ const lower = services.normalizeTokenLower(candidate);
154
+ if (/^[;:(),]+$/.test(lower)) {
155
+ break;
156
+ }
157
+ if (services.siteFillerWords.has(lower) && contentWords.length === 0) {
158
+ collected.push(candidate.index);
159
+ candidateTextParts.push(candidate.original);
160
+ continue;
161
+ }
162
+ if ((0, meaning_1.isSiteListConnectorWord)(lower) || lower === ",") {
163
+ let hasFollowingContent = false;
164
+ for (let lookahead = cursor + 1; lookahead < tokens.length; lookahead += 1) {
165
+ const lookaheadToken = tokens[lookahead];
166
+ if (!lookaheadToken || consumed.has(lookaheadToken.index)) {
167
+ break;
168
+ }
169
+ const lookaheadLower = services.normalizeTokenLower(lookaheadToken);
170
+ if (services.siteFillerWords.has(lookaheadLower)) {
171
+ continue;
172
+ }
173
+ hasFollowingContent = !isExplicitSiteBoundaryToken(lookaheadLower, options, services);
174
+ break;
175
+ }
176
+ if (!hasFollowingContent) {
177
+ break;
178
+ }
179
+ collected.push(candidate.index);
180
+ candidateTextParts.push(candidate.original);
181
+ continue;
182
+ }
183
+ if (isExplicitSiteBoundaryToken(lower, options, services)) {
184
+ if (lower === "top") {
185
+ const nextToken = services.getNextActiveToken(cursor);
186
+ if (nextToken && services.normalizeTokenLower(nextToken) === "of") {
187
+ collected.push(candidate.index);
188
+ candidateTextParts.push(candidate.original);
189
+ contentWords.push(lower);
190
+ hasSiteHint = true;
191
+ continue;
192
+ }
193
+ }
194
+ break;
195
+ }
196
+ collected.push(candidate.index);
197
+ candidateTextParts.push(candidate.original);
198
+ if (!services.siteConnectors.has(lower) && !services.siteFillerWords.has(lower)) {
199
+ contentWords.push(lower);
200
+ if (services.isBodySiteHint(lower, services.customSiteHints) ||
201
+ (0, meaning_1.isSiteSurfaceModifierWord)(lower) ||
202
+ services.isOrdinalToken(lower)) {
203
+ hasSiteHint = true;
204
+ }
205
+ }
206
+ }
207
+ if (!contentWords.length || isTimingOnlySitePhrase(contentWords)) {
208
+ return undefined;
209
+ }
210
+ const candidateText = candidateTextParts.join(" ").replace(/\s+/g, " ").trim();
211
+ if (candidateText && ((_a = services.isInstructionLikeText) === null || _a === void 0 ? void 0 : _a.call(services, candidateText))) {
212
+ return undefined;
213
+ }
214
+ if (!hasSiteHint &&
215
+ contentWords.length === 1 &&
216
+ contentWords[0] !== "area" &&
217
+ anchorLower === "in") {
218
+ return undefined;
219
+ }
220
+ return {
221
+ tokenIndices: collected,
222
+ source: "explicit"
223
+ };
224
+ }
225
+ function selectBestResidualSiteCandidate(groups, prnSiteSuffixIndices, services) {
226
+ var _a;
227
+ let bestTokenIndices;
228
+ let bestScore = Number.NEGATIVE_INFINITY;
229
+ for (const group of groups) {
230
+ const filteredTokens = [];
231
+ const contentWords = [];
232
+ const candidateTextParts = [];
233
+ let hasWorkflowWord = false;
234
+ let knownWordCount = 0;
235
+ let modifierCount = 0;
236
+ for (const token of group.tokens) {
237
+ if (prnSiteSuffixIndices.has(token.index)) {
238
+ continue;
239
+ }
240
+ filteredTokens.push(token);
241
+ const lower = services.normalizeTokenLower(token);
242
+ if (lower.length === 0 ||
243
+ services.siteConnectors.has(lower) ||
244
+ services.siteFillerWords.has(lower) ||
245
+ (0, meaning_1.isSiteListConnectorWord)(lower) ||
246
+ lower === ",") {
247
+ continue;
248
+ }
249
+ candidateTextParts.push(token.original);
250
+ contentWords.push(lower);
251
+ if ((0, meaning_1.isWorkflowInstructionWord)(lower)) {
252
+ hasWorkflowWord = true;
253
+ }
254
+ if (services.isBodySiteHint(lower, services.customSiteHints)) {
255
+ knownWordCount += 1;
256
+ }
257
+ if ((0, meaning_1.isSiteSurfaceModifierWord)(lower) || services.isOrdinalToken(lower)) {
258
+ modifierCount += 1;
259
+ }
260
+ }
261
+ if (!filteredTokens.length) {
262
+ continue;
263
+ }
264
+ if (!contentWords.length || isTimingOnlySitePhrase(contentWords)) {
265
+ continue;
266
+ }
267
+ if (candidateTextParts.length) {
268
+ const candidateText = candidateTextParts.join(" ").replace(/\s+/g, " ").trim();
269
+ if (candidateText && ((_a = services.isInstructionLikeText) === null || _a === void 0 ? void 0 : _a.call(services, candidateText))) {
270
+ continue;
271
+ }
272
+ }
273
+ if (hasWorkflowWord) {
274
+ continue;
275
+ }
276
+ const startsWithApplicationContext = services.hasApplicationVerbBefore(filteredTokens[0].index);
277
+ const previous = services.getPreviousActiveToken(filteredTokens[0].index);
278
+ const anchoredBySiteConnector = previous
279
+ ? (0, meaning_1.isSiteAnchorWord)(services.normalizeTokenLower(previous))
280
+ : false;
281
+ if (knownWordCount === 0 &&
282
+ modifierCount === 0 &&
283
+ !anchoredBySiteConnector) {
284
+ continue;
285
+ }
286
+ const score = knownWordCount * 5 +
287
+ modifierCount * 2 +
288
+ (startsWithApplicationContext ? 2 : 0) +
289
+ (anchoredBySiteConnector ? 2 : 0) -
290
+ filteredTokens.length * 0.2 +
291
+ filteredTokens[0].index * 0.001;
292
+ if (score > bestScore) {
293
+ bestScore = score;
294
+ bestTokenIndices = [];
295
+ for (const token of filteredTokens) {
296
+ bestTokenIndices.push(token.index);
297
+ }
298
+ }
299
+ }
300
+ if (!bestTokenIndices || !bestTokenIndices.length) {
301
+ return undefined;
302
+ }
303
+ return {
304
+ tokenIndices: bestTokenIndices,
305
+ source: "residual"
306
+ };
307
+ }
308
+ function inferRouteHintFromSitePhrase(siteText, options, services) {
309
+ const canonical = (0, maps_1.normalizeBodySiteKey)(siteText);
310
+ if (!canonical) {
311
+ return undefined;
312
+ }
313
+ const customExact = services.lookupBodySiteDefinition(options === null || options === void 0 ? void 0 : options.siteCodeMap, canonical);
314
+ const exact = mergeBodySiteRouteHint(canonical, customExact, maps_1.DEFAULT_BODY_SITE_SNOMED[canonical]);
315
+ if (exact === null || exact === void 0 ? void 0 : exact.routeHint) {
316
+ return exact.routeHint;
317
+ }
318
+ if (hasExternalSurfaceModifier(siteText)) {
319
+ return undefined;
320
+ }
321
+ const words = [];
322
+ for (const word of canonical.split(/\s+/)) {
323
+ if (word.length > 0) {
324
+ words.push(word);
325
+ }
326
+ }
327
+ for (let length = words.length; length >= 1; length -= 1) {
328
+ for (let start = 0; start + length <= words.length; start += 1) {
329
+ let candidate = "";
330
+ for (let offset = 0; offset < length; offset += 1) {
331
+ if (offset > 0) {
332
+ candidate += " ";
333
+ }
334
+ candidate += words[start + offset];
335
+ }
336
+ const customDefinition = services.lookupBodySiteDefinition(options === null || options === void 0 ? void 0 : options.siteCodeMap, candidate);
337
+ const definition = mergeBodySiteRouteHint(candidate, customDefinition, maps_1.DEFAULT_BODY_SITE_SNOMED[candidate]);
338
+ if (definition === null || definition === void 0 ? void 0 : definition.routeHint) {
339
+ return definition.routeHint;
340
+ }
341
+ }
342
+ }
343
+ return undefined;
344
+ }
@@ -1,5 +1,4 @@
1
- import { ParsedSigInternal } from "./internal-types";
2
- import { EventTiming } from "./types";
1
+ import { EventTiming, FhirDayOfWeek, FhirPeriodUnit } from "./types";
3
2
  export interface TimingSummaryOptions {
4
3
  groupMealTimingsByRelation?: boolean;
5
4
  includeTimesPerDaySummary?: boolean;
@@ -11,5 +10,16 @@ export interface MealTimingGroup {
11
10
  meals: MealName[];
12
11
  codes: EventTiming[];
13
12
  }
13
+ export interface TimingSummaryInput {
14
+ frequency?: number;
15
+ frequencyMax?: number;
16
+ period?: number;
17
+ periodMax?: number;
18
+ periodUnit?: FhirPeriodUnit;
19
+ timingCode?: string;
20
+ dayOfWeek?: FhirDayOfWeek[];
21
+ when?: EventTiming[];
22
+ timeOfDay?: string[];
23
+ }
14
24
  export declare function getMealTimingGroup(when: EventTiming[], options?: TimingSummaryOptions): MealTimingGroup | undefined;
15
- export declare function inferDailyOccurrenceCount(internal: ParsedSigInternal, options?: TimingSummaryOptions): number | undefined;
25
+ export declare function inferDailyOccurrenceCount(input: TimingSummaryInput, options?: TimingSummaryOptions): number | undefined;
@@ -109,27 +109,27 @@ function getMealTimingGroup(when, options) {
109
109
  codes: groupedCodes
110
110
  };
111
111
  }
112
- function inferDailyOccurrenceCount(internal, options) {
113
- var _a;
112
+ function inferDailyOccurrenceCount(input, options) {
113
+ var _a, _b, _c, _d;
114
114
  if (!(options === null || options === void 0 ? void 0 : options.includeTimesPerDaySummary)) {
115
115
  return undefined;
116
116
  }
117
- if (internal.frequency !== undefined || internal.frequencyMax !== undefined || internal.timingCode) {
117
+ if (input.frequency !== undefined || input.frequencyMax !== undefined || input.timingCode) {
118
118
  return undefined;
119
119
  }
120
- if (internal.period !== undefined || internal.periodMax !== undefined || internal.periodUnit !== undefined) {
120
+ if (input.period !== undefined || input.periodMax !== undefined || input.periodUnit !== undefined) {
121
121
  return undefined;
122
122
  }
123
- if (internal.dayOfWeek.length > 0) {
123
+ if (((_b = (_a = input.dayOfWeek) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0) {
124
124
  return undefined;
125
125
  }
126
- const uniqueWhen = uniqueValues(internal.when);
126
+ const uniqueWhen = uniqueValues((_c = input.when) !== null && _c !== void 0 ? _c : []);
127
127
  for (let i = 0; i < uniqueWhen.length; i += 1) {
128
128
  if (!INFERABLE_DAILY_EVENT_TIMINGS.has(uniqueWhen[i])) {
129
129
  return undefined;
130
130
  }
131
131
  }
132
- const uniqueTimes = uniqueValues((_a = internal.timeOfDay) !== null && _a !== void 0 ? _a : []);
132
+ const uniqueTimes = uniqueValues((_d = input.timeOfDay) !== null && _d !== void 0 ? _d : []);
133
133
  const occurrences = uniqueWhen.length + uniqueTimes.length;
134
134
  if (occurrences === 0) {
135
135
  return undefined;