ezmedicationinput 0.1.29 → 0.1.31
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/fhir.js +19 -12
- package/dist/maps.d.ts +8 -0
- package/dist/maps.js +17 -0
- package/dist/parser.js +51 -40
- package/package.json +1 -1
package/dist/fhir.js
CHANGED
|
@@ -162,7 +162,7 @@ function toFhir(internal) {
|
|
|
162
162
|
return dosage;
|
|
163
163
|
}
|
|
164
164
|
function internalFromFhir(dosage) {
|
|
165
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13;
|
|
165
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14;
|
|
166
166
|
const internal = {
|
|
167
167
|
input: (_a = dosage.text) !== null && _a !== void 0 ? _a : "",
|
|
168
168
|
tokens: [],
|
|
@@ -212,34 +212,41 @@ function internalFromFhir(dosage) {
|
|
|
212
212
|
}
|
|
213
213
|
const reasonCoding = (_9 = (_8 = (_7 = dosage.asNeededFor) === null || _7 === void 0 ? void 0 : _7[0]) === null || _8 === void 0 ? void 0 : _8.coding) === null || _9 === void 0 ? void 0 : _9[0];
|
|
214
214
|
if (reasonCoding === null || reasonCoding === void 0 ? void 0 : reasonCoding.code) {
|
|
215
|
+
const defaultDef = (0, maps_1.findPrnReasonDefinitionByCoding)((_10 = reasonCoding.system) !== null && _10 !== void 0 ? _10 : SNOMED_SYSTEM, reasonCoding.code);
|
|
215
216
|
internal.asNeededReasonCoding = {
|
|
216
217
|
code: reasonCoding.code,
|
|
217
218
|
display: reasonCoding.display,
|
|
218
|
-
system: reasonCoding.system
|
|
219
|
+
system: reasonCoding.system,
|
|
220
|
+
i18n: defaultDef === null || defaultDef === void 0 ? void 0 : defaultDef.i18n
|
|
219
221
|
};
|
|
220
222
|
}
|
|
221
|
-
if ((
|
|
223
|
+
if ((_11 = dosage.additionalInstruction) === null || _11 === void 0 ? void 0 : _11.length) {
|
|
222
224
|
internal.additionalInstructions = dosage.additionalInstruction.map((concept) => {
|
|
223
|
-
var _a;
|
|
224
|
-
|
|
225
|
+
var _a, _b;
|
|
226
|
+
const coding = (_a = concept.coding) === null || _a === void 0 ? void 0 : _a[0];
|
|
227
|
+
const defaultDef = (coding === null || coding === void 0 ? void 0 : coding.code)
|
|
228
|
+
? (0, maps_1.findAdditionalInstructionDefinitionByCoding)((_b = coding.system) !== null && _b !== void 0 ? _b : SNOMED_SYSTEM, coding.code)
|
|
229
|
+
: undefined;
|
|
230
|
+
return {
|
|
225
231
|
text: concept.text,
|
|
226
|
-
coding: (
|
|
232
|
+
coding: (coding === null || coding === void 0 ? void 0 : coding.code)
|
|
227
233
|
? {
|
|
228
|
-
code:
|
|
229
|
-
display:
|
|
230
|
-
system:
|
|
234
|
+
code: coding.code,
|
|
235
|
+
display: coding.display,
|
|
236
|
+
system: coding.system,
|
|
237
|
+
i18n: defaultDef === null || defaultDef === void 0 ? void 0 : defaultDef.i18n
|
|
231
238
|
}
|
|
232
239
|
: undefined
|
|
233
|
-
}
|
|
240
|
+
};
|
|
234
241
|
});
|
|
235
242
|
}
|
|
236
|
-
const doseAndRate = (
|
|
243
|
+
const doseAndRate = (_12 = dosage.doseAndRate) === null || _12 === void 0 ? void 0 : _12[0];
|
|
237
244
|
if (doseAndRate === null || doseAndRate === void 0 ? void 0 : doseAndRate.doseRange) {
|
|
238
245
|
const { low, high } = doseAndRate.doseRange;
|
|
239
246
|
if ((low === null || low === void 0 ? void 0 : low.value) !== undefined && (high === null || high === void 0 ? void 0 : high.value) !== undefined) {
|
|
240
247
|
internal.doseRange = { low: low.value, high: high.value };
|
|
241
248
|
}
|
|
242
|
-
internal.unit = (
|
|
249
|
+
internal.unit = (_14 = (_13 = low === null || low === void 0 ? void 0 : low.unit) !== null && _13 !== void 0 ? _13 : high === null || high === void 0 ? void 0 : high.unit) !== null && _14 !== void 0 ? _14 : internal.unit;
|
|
243
250
|
}
|
|
244
251
|
else if (doseAndRate === null || doseAndRate === void 0 ? void 0 : doseAndRate.doseQuantity) {
|
|
245
252
|
const dose = doseAndRate.doseQuantity;
|
package/dist/maps.d.ts
CHANGED
|
@@ -74,3 +74,11 @@ export interface AdditionalInstructionDictionaryEntry {
|
|
|
74
74
|
}
|
|
75
75
|
export declare const DEFAULT_ADDITIONAL_INSTRUCTION_ENTRIES: AdditionalInstructionDictionaryEntry[];
|
|
76
76
|
export declare const DEFAULT_ADDITIONAL_INSTRUCTION_DEFINITIONS: Record<string, AdditionalInstructionDefinition>;
|
|
77
|
+
/**
|
|
78
|
+
* Finds a default PRN reason definition by its SNOMED coding.
|
|
79
|
+
*/
|
|
80
|
+
export declare function findPrnReasonDefinitionByCoding(system: string, code: string): PrnReasonDefinition | undefined;
|
|
81
|
+
/**
|
|
82
|
+
* Finds a default additional instruction definition by its SNOMED coding.
|
|
83
|
+
*/
|
|
84
|
+
export declare function findAdditionalInstructionDefinitionByCoding(system: string, code: string): AdditionalInstructionDefinition | undefined;
|
package/dist/maps.js
CHANGED
|
@@ -4,6 +4,8 @@ exports.DEFAULT_ADDITIONAL_INSTRUCTION_DEFINITIONS = exports.DEFAULT_ADDITIONAL_
|
|
|
4
4
|
exports.normalizeBodySiteKey = normalizeBodySiteKey;
|
|
5
5
|
exports.normalizePrnReasonKey = normalizePrnReasonKey;
|
|
6
6
|
exports.normalizeAdditionalInstructionKey = normalizeAdditionalInstructionKey;
|
|
7
|
+
exports.findPrnReasonDefinitionByCoding = findPrnReasonDefinitionByCoding;
|
|
8
|
+
exports.findAdditionalInstructionDefinitionByCoding = findAdditionalInstructionDefinitionByCoding;
|
|
7
9
|
const types_1 = require("./types");
|
|
8
10
|
const object_1 = require("./utils/object");
|
|
9
11
|
const SNOMED_SYSTEM = "http://snomed.info/sct";
|
|
@@ -698,6 +700,7 @@ exports.EVENT_TIMING_TOKENS = {
|
|
|
698
700
|
night: types_1.EventTiming.Night,
|
|
699
701
|
hs: types_1.EventTiming["Before Sleep"],
|
|
700
702
|
bedtime: types_1.EventTiming["Before Sleep"],
|
|
703
|
+
bed: types_1.EventTiming["Before Sleep"],
|
|
701
704
|
wake: types_1.EventTiming.Wake,
|
|
702
705
|
waking: types_1.EventTiming.Wake,
|
|
703
706
|
stat: types_1.EventTiming.Immediate
|
|
@@ -1611,3 +1614,17 @@ exports.DEFAULT_ADDITIONAL_INSTRUCTION_DEFINITIONS = (0, object_1.objectFromEntr
|
|
|
1611
1614
|
}
|
|
1612
1615
|
return entries;
|
|
1613
1616
|
}, []));
|
|
1617
|
+
/**
|
|
1618
|
+
* Finds a default PRN reason definition by its SNOMED coding.
|
|
1619
|
+
*/
|
|
1620
|
+
function findPrnReasonDefinitionByCoding(system, code) {
|
|
1621
|
+
var _a;
|
|
1622
|
+
return (_a = DEFAULT_PRN_REASON_SOURCE.find((source) => { var _a, _b; return ((_a = source.definition.coding) === null || _a === void 0 ? void 0 : _a.system) === system && ((_b = source.definition.coding) === null || _b === void 0 ? void 0 : _b.code) === code; })) === null || _a === void 0 ? void 0 : _a.definition;
|
|
1623
|
+
}
|
|
1624
|
+
/**
|
|
1625
|
+
* Finds a default additional instruction definition by its SNOMED coding.
|
|
1626
|
+
*/
|
|
1627
|
+
function findAdditionalInstructionDefinitionByCoding(system, code) {
|
|
1628
|
+
var _a;
|
|
1629
|
+
return (_a = DEFAULT_ADDITIONAL_INSTRUCTION_SOURCE.find((source) => { var _a, _b; return ((_a = source.definition.coding) === null || _a === void 0 ? void 0 : _a.system) === system && ((_b = source.definition.coding) === null || _b === void 0 ? void 0 : _b.code) === code; })) === null || _a === void 0 ? void 0 : _a.definition;
|
|
1630
|
+
}
|
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
|
|
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
|
-
|
|
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,20 @@ function applyWhenToken(internal, token, code) {
|
|
|
1518
1499
|
addWhen(internal.when, code);
|
|
1519
1500
|
mark(internal.consumed, token);
|
|
1520
1501
|
}
|
|
1502
|
+
function isTimingAnchorOrPrefix(tokens, index) {
|
|
1503
|
+
const token = tokens[index];
|
|
1504
|
+
if (!token)
|
|
1505
|
+
return false;
|
|
1506
|
+
const lower = token.lower;
|
|
1507
|
+
const nextToken = tokens[index + 1];
|
|
1508
|
+
const comboKey = nextToken ? `${lower} ${nextToken.lower}` : undefined;
|
|
1509
|
+
return Boolean(maps_1.EVENT_TIMING_TOKENS[lower] ||
|
|
1510
|
+
maps_1.TIMING_ABBREVIATIONS[lower] ||
|
|
1511
|
+
(comboKey && COMBO_EVENT_TIMINGS[comboKey]) ||
|
|
1512
|
+
(lower === "pc" || lower === "ac" || lower === "after" || lower === "before") ||
|
|
1513
|
+
(lower === "at" || lower === "@" || lower === "on" || lower === "with") ||
|
|
1514
|
+
/^\d/.test(lower));
|
|
1515
|
+
}
|
|
1521
1516
|
function parseAnchorSequence(internal, tokens, index, prefixCode) {
|
|
1522
1517
|
var _a;
|
|
1523
1518
|
const token = tokens[index];
|
|
@@ -1936,7 +1931,25 @@ function parseInternal(input, options) {
|
|
|
1936
1931
|
if (tryParseCompactQ(internal, tokens, i)) {
|
|
1937
1932
|
continue;
|
|
1938
1933
|
}
|
|
1934
|
+
// Skip connectors if they are followed by recognized timing tokens or prefixes
|
|
1935
|
+
if (MEAL_CONTEXT_CONNECTORS.has(token.lower) || token.lower === ",") {
|
|
1936
|
+
if (isTimingAnchorOrPrefix(tokens, i + 1)) {
|
|
1937
|
+
mark(internal.consumed, token);
|
|
1938
|
+
continue;
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1939
1941
|
// Event timing tokens
|
|
1942
|
+
const nextToken = tokens[i + 1];
|
|
1943
|
+
if (nextToken && !internal.consumed.has(nextToken.index)) {
|
|
1944
|
+
const lowerNext = nextToken.lower;
|
|
1945
|
+
const combo = `${token.lower} ${lowerNext}`;
|
|
1946
|
+
const comboWhen = (_f = COMBO_EVENT_TIMINGS[combo]) !== null && _f !== void 0 ? _f : maps_1.EVENT_TIMING_TOKENS[combo];
|
|
1947
|
+
if (comboWhen) {
|
|
1948
|
+
applyWhenToken(internal, token, comboWhen);
|
|
1949
|
+
mark(internal.consumed, nextToken);
|
|
1950
|
+
continue;
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1940
1953
|
if (token.lower === "pc" || token.lower === "ac" || token.lower === "after" || token.lower === "before") {
|
|
1941
1954
|
parseAnchorSequence(internal, tokens, i, (token.lower === "pc" || token.lower === "after")
|
|
1942
1955
|
? types_1.EventTiming["After Meal"]
|
|
@@ -1957,17 +1970,6 @@ function parseInternal(input, options) {
|
|
|
1957
1970
|
continue;
|
|
1958
1971
|
}
|
|
1959
1972
|
}
|
|
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
1973
|
const customWhen = (_g = options === null || options === void 0 ? void 0 : options.whenMap) === null || _g === void 0 ? void 0 : _g[token.lower];
|
|
1972
1974
|
if (customWhen) {
|
|
1973
1975
|
applyWhenToken(internal, token, customWhen);
|
|
@@ -3126,7 +3128,7 @@ function collectAdditionalInstructions(internal, tokens) {
|
|
|
3126
3128
|
if (/\s/.test(ch)) {
|
|
3127
3129
|
continue;
|
|
3128
3130
|
}
|
|
3129
|
-
if (
|
|
3131
|
+
if (/-|;|:|\.|\,/.test(ch)) {
|
|
3130
3132
|
separatorDetected = true;
|
|
3131
3133
|
}
|
|
3132
3134
|
break;
|
|
@@ -3135,9 +3137,6 @@ function collectAdditionalInstructions(internal, tokens) {
|
|
|
3135
3137
|
const sourceText = range
|
|
3136
3138
|
? internal.input.slice(range.start, range.end)
|
|
3137
3139
|
: joined;
|
|
3138
|
-
if (!separatorDetected && !/[-;:.]/.test(sourceText)) {
|
|
3139
|
-
return;
|
|
3140
|
-
}
|
|
3141
3140
|
const normalized = sourceText
|
|
3142
3141
|
.replace(/\s*[-:]+\s*/g, "; ")
|
|
3143
3142
|
.replace(/\s*(?:\r?\n)+\s*/g, "; ")
|
|
@@ -3146,6 +3145,18 @@ function collectAdditionalInstructions(internal, tokens) {
|
|
|
3146
3145
|
.split(/(?:;|\.)/)
|
|
3147
3146
|
.map((segment) => segment.trim())
|
|
3148
3147
|
.filter((segment) => segment.length > 0);
|
|
3148
|
+
// If no punctuation was detected, we only collect if at least one segment matches a known definition.
|
|
3149
|
+
// This avoids capturing random trailing text as instructions unless it's codified.
|
|
3150
|
+
if (!separatorDetected && !/[-;:.]/.test(sourceText)) {
|
|
3151
|
+
const hasKnownDefinition = segments.some((phrase) => {
|
|
3152
|
+
const canonical = (0, maps_1.normalizeAdditionalInstructionKey)(phrase);
|
|
3153
|
+
return (maps_1.DEFAULT_ADDITIONAL_INSTRUCTION_DEFINITIONS[canonical] ||
|
|
3154
|
+
findAdditionalInstructionDefinition(phrase, canonical));
|
|
3155
|
+
});
|
|
3156
|
+
if (!hasKnownDefinition) {
|
|
3157
|
+
return;
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3149
3160
|
const phrases = segments.length ? segments : [joined];
|
|
3150
3161
|
const seen = new Set();
|
|
3151
3162
|
const instructions = [];
|
|
@@ -3174,7 +3185,7 @@ function collectAdditionalInstructions(internal, tokens) {
|
|
|
3174
3185
|
: undefined
|
|
3175
3186
|
});
|
|
3176
3187
|
}
|
|
3177
|
-
else {
|
|
3188
|
+
else if (!MEAL_CONTEXT_CONNECTORS.has(phrase.toLowerCase())) {
|
|
3178
3189
|
instructions.push({ text: phrase });
|
|
3179
3190
|
}
|
|
3180
3191
|
}
|