ezmedicationinput 0.1.39 → 0.1.40
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/index.js +194 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -58,6 +58,15 @@ Object.defineProperty(exports, "DEFAULT_BODY_SITE_SNOMED_SOURCE", { enumerable:
|
|
|
58
58
|
Object.defineProperty(exports, "DEFAULT_ROUTE_SYNONYMS", { enumerable: true, get: function () { return maps_1.DEFAULT_ROUTE_SYNONYMS; } });
|
|
59
59
|
Object.defineProperty(exports, "DEFAULT_UNIT_BY_ROUTE", { enumerable: true, get: function () { return maps_1.DEFAULT_UNIT_BY_ROUTE; } });
|
|
60
60
|
Object.defineProperty(exports, "KNOWN_DOSAGE_FORMS_TO_DOSE", { enumerable: true, get: function () { return maps_1.KNOWN_DOSAGE_FORMS_TO_DOSE; } });
|
|
61
|
+
const REPEAT_NON_ANCHOR_KEYS = [
|
|
62
|
+
"count",
|
|
63
|
+
"frequency",
|
|
64
|
+
"frequencyMax",
|
|
65
|
+
"period",
|
|
66
|
+
"periodMax",
|
|
67
|
+
"periodUnit",
|
|
68
|
+
"offset"
|
|
69
|
+
];
|
|
61
70
|
function parseMealDashValues(token) {
|
|
62
71
|
if (!/^[0-9]+(?:\.[0-9]+)?(?:-[0-9]+(?:\.[0-9]+)?){2,3}$/.test(token)) {
|
|
63
72
|
return undefined;
|
|
@@ -155,6 +164,189 @@ function toSegmentMeta(segments) {
|
|
|
155
164
|
range: { start: segment.start, end: segment.end }
|
|
156
165
|
}));
|
|
157
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Deep equality helper for plain JSON-like parser output objects.
|
|
169
|
+
*
|
|
170
|
+
* @param left Left-side value.
|
|
171
|
+
* @param right Right-side value.
|
|
172
|
+
* @returns `true` when both values are structurally equal.
|
|
173
|
+
*/
|
|
174
|
+
function deepEqual(left, right) {
|
|
175
|
+
if (left === right) {
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
if (left === null || right === null) {
|
|
179
|
+
return left === right;
|
|
180
|
+
}
|
|
181
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
182
|
+
if (!Array.isArray(left) || !Array.isArray(right)) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
if (left.length !== right.length) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
for (let i = 0; i < left.length; i += 1) {
|
|
189
|
+
if (!deepEqual(left[i], right[i])) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
if (typeof left !== "object" || typeof right !== "object") {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
const leftRecord = left;
|
|
199
|
+
const rightRecord = right;
|
|
200
|
+
const leftKeys = Object.keys(leftRecord).filter((key) => leftRecord[key] !== undefined);
|
|
201
|
+
const rightKeys = Object.keys(rightRecord).filter((key) => rightRecord[key] !== undefined);
|
|
202
|
+
if (leftKeys.length !== rightKeys.length) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
for (const key of leftKeys) {
|
|
206
|
+
if (!Object.prototype.hasOwnProperty.call(rightRecord, key)) {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
if (!deepEqual(leftRecord[key], rightRecord[key])) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Compares two string arrays as sets.
|
|
217
|
+
*
|
|
218
|
+
* @param left Left array.
|
|
219
|
+
* @param right Right array.
|
|
220
|
+
* @returns `true` when both arrays contain the same unique values.
|
|
221
|
+
*/
|
|
222
|
+
function sameStringSet(left, right) {
|
|
223
|
+
const a = left !== null && left !== void 0 ? left : [];
|
|
224
|
+
const b = right !== null && right !== void 0 ? right : [];
|
|
225
|
+
if (a.length !== b.length) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
const set = new Set(a);
|
|
229
|
+
if (set.size !== b.length) {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
for (const value of b) {
|
|
233
|
+
if (!set.has(value)) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Determines whether a repeat block only uses merge-safe anchor fields.
|
|
241
|
+
*
|
|
242
|
+
* @param repeat FHIR timing repeat payload.
|
|
243
|
+
* @returns `true` when repeat contains only `when`/`timeOfDay`/`dayOfWeek`.
|
|
244
|
+
*/
|
|
245
|
+
function isMergeableAnchorRepeat(repeat) {
|
|
246
|
+
if (!repeat) {
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
for (const key of REPEAT_NON_ANCHOR_KEYS) {
|
|
250
|
+
if (repeat[key] !== undefined) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Checks whether two parsed items can be merged without changing semantics.
|
|
258
|
+
*
|
|
259
|
+
* @param base Existing merged item candidate.
|
|
260
|
+
* @param next Incoming parsed item.
|
|
261
|
+
* @returns `true` when both items differ only by merge-safe timing anchors.
|
|
262
|
+
*/
|
|
263
|
+
function canMergeTimingOnly(base, next) {
|
|
264
|
+
const baseTiming = base.fhir.timing;
|
|
265
|
+
const nextTiming = next.fhir.timing;
|
|
266
|
+
const baseRepeat = baseTiming === null || baseTiming === void 0 ? void 0 : baseTiming.repeat;
|
|
267
|
+
const nextRepeat = nextTiming === null || nextTiming === void 0 ? void 0 : nextTiming.repeat;
|
|
268
|
+
if (!baseRepeat || !nextRepeat) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
if (!isMergeableAnchorRepeat(baseRepeat) || !isMergeableAnchorRepeat(nextRepeat)) {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
if (!sameStringSet(baseRepeat.dayOfWeek, nextRepeat.dayOfWeek)) {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
if (!deepEqual(baseTiming === null || baseTiming === void 0 ? void 0 : baseTiming.code, nextTiming === null || nextTiming === void 0 ? void 0 : nextTiming.code)) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
if (!deepEqual(baseTiming === null || baseTiming === void 0 ? void 0 : baseTiming.event, nextTiming === null || nextTiming === void 0 ? void 0 : nextTiming.event)) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
return (deepEqual(base.fhir.doseAndRate, next.fhir.doseAndRate) &&
|
|
284
|
+
deepEqual(base.fhir.route, next.fhir.route) &&
|
|
285
|
+
deepEqual(base.fhir.site, next.fhir.site) &&
|
|
286
|
+
deepEqual(base.fhir.additionalInstruction, next.fhir.additionalInstruction) &&
|
|
287
|
+
deepEqual(base.fhir.asNeededBoolean, next.fhir.asNeededBoolean) &&
|
|
288
|
+
deepEqual(base.fhir.asNeededFor, next.fhir.asNeededFor));
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Returns a stable unique list preserving first-seen order.
|
|
292
|
+
*
|
|
293
|
+
* @param values Input values.
|
|
294
|
+
* @returns Deduplicated values in insertion order.
|
|
295
|
+
*/
|
|
296
|
+
function uniqueStrings(values) {
|
|
297
|
+
const seen = new Set();
|
|
298
|
+
const output = [];
|
|
299
|
+
for (const value of values) {
|
|
300
|
+
if (!seen.has(value)) {
|
|
301
|
+
seen.add(value);
|
|
302
|
+
output.push(value);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return output;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Merges two parse results that are known to be timing-compatible.
|
|
309
|
+
*
|
|
310
|
+
* @param base Existing merged result.
|
|
311
|
+
* @param next Next result to fold into `base`.
|
|
312
|
+
* @param options Parse options used to render localized text.
|
|
313
|
+
* @returns New merged parse result.
|
|
314
|
+
*/
|
|
315
|
+
function mergeParseResults(base, next, options) {
|
|
316
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
317
|
+
const baseRepeat = (_b = (_a = base.fhir.timing) === null || _a === void 0 ? void 0 : _a.repeat) !== null && _b !== void 0 ? _b : {};
|
|
318
|
+
const nextRepeat = (_d = (_c = next.fhir.timing) === null || _c === void 0 ? void 0 : _c.repeat) !== null && _d !== void 0 ? _d : {};
|
|
319
|
+
const mergedWhen = uniqueStrings([...((_e = baseRepeat.when) !== null && _e !== void 0 ? _e : []), ...((_f = nextRepeat.when) !== null && _f !== void 0 ? _f : [])]);
|
|
320
|
+
const mergedTimeOfDay = uniqueStrings([...((_g = baseRepeat.timeOfDay) !== null && _g !== void 0 ? _g : []), ...((_h = nextRepeat.timeOfDay) !== null && _h !== void 0 ? _h : [])]).sort();
|
|
321
|
+
const mergedRepeat = Object.assign(Object.assign(Object.assign(Object.assign({}, baseRepeat), (nextRepeat.dayOfWeek ? { dayOfWeek: nextRepeat.dayOfWeek } : {})), (mergedWhen.length ? { when: mergedWhen } : {})), (mergedTimeOfDay.length ? { timeOfDay: mergedTimeOfDay } : {}));
|
|
322
|
+
const mergedFhir = Object.assign(Object.assign({}, base.fhir), { timing: Object.assign(Object.assign({}, ((_j = base.fhir.timing) !== null && _j !== void 0 ? _j : {})), { repeat: mergedRepeat }) });
|
|
323
|
+
const shortText = formatSig(mergedFhir, "short", options);
|
|
324
|
+
const longText = formatSig(mergedFhir, "long", options);
|
|
325
|
+
mergedFhir.text = longText;
|
|
326
|
+
return {
|
|
327
|
+
fhir: mergedFhir,
|
|
328
|
+
shortText,
|
|
329
|
+
longText,
|
|
330
|
+
warnings: uniqueStrings([...((_k = base.warnings) !== null && _k !== void 0 ? _k : []), ...((_l = next.warnings) !== null && _l !== void 0 ? _l : [])]),
|
|
331
|
+
meta: Object.assign(Object.assign({}, base.meta), { consumedTokens: uniqueStrings([...((_m = base.meta.consumedTokens) !== null && _m !== void 0 ? _m : []), ...((_o = next.meta.consumedTokens) !== null && _o !== void 0 ? _o : [])]), leftoverText: uniqueStrings([base.meta.leftoverText, next.meta.leftoverText].filter((value) => !!value)).join(" ").trim() || undefined, siteLookups: [...((_p = base.meta.siteLookups) !== null && _p !== void 0 ? _p : []), ...((_q = next.meta.siteLookups) !== null && _q !== void 0 ? _q : [])], prnReasonLookups: [...((_r = base.meta.prnReasonLookups) !== null && _r !== void 0 ? _r : []), ...((_s = next.meta.prnReasonLookups) !== null && _s !== void 0 ? _s : [])] })
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Appends a parsed segment result to the batch, reusing the current item when
|
|
336
|
+
* timing-only expansion can be represented as a single dosage element.
|
|
337
|
+
*
|
|
338
|
+
* @param items Accumulated batch items.
|
|
339
|
+
* @param next Newly parsed segment result.
|
|
340
|
+
* @param options Parse options used to format merged text.
|
|
341
|
+
*/
|
|
342
|
+
function appendParseResult(items, next, options) {
|
|
343
|
+
const previous = items[items.length - 1];
|
|
344
|
+
if (previous && canMergeTimingOnly(previous, next)) {
|
|
345
|
+
items[items.length - 1] = mergeParseResults(previous, next, options);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
items.push(next);
|
|
349
|
+
}
|
|
158
350
|
function parseSig(input, options) {
|
|
159
351
|
const segments = expandMealDashSegments((0, segment_1.splitSigSegments)(input), options);
|
|
160
352
|
const carry = {};
|
|
@@ -166,7 +358,7 @@ function parseSig(input, options) {
|
|
|
166
358
|
(0, parser_1.applySiteCoding)(internal, options);
|
|
167
359
|
const result = buildParseResult(internal, options);
|
|
168
360
|
rebaseParseResult(result, input, segment.start);
|
|
169
|
-
results
|
|
361
|
+
appendParseResult(results, result, options);
|
|
170
362
|
updateCarryForward(carry, internal);
|
|
171
363
|
}
|
|
172
364
|
const legacy = resolveLegacyParseResult(results, input, options);
|
|
@@ -232,7 +424,7 @@ function parseSigAsync(input, options) {
|
|
|
232
424
|
yield (0, parser_1.applySiteCodingAsync)(internal, options);
|
|
233
425
|
const result = buildParseResult(internal, options);
|
|
234
426
|
rebaseParseResult(result, input, segment.start);
|
|
235
|
-
results
|
|
427
|
+
appendParseResult(results, result, options);
|
|
236
428
|
updateCarryForward(carry, internal);
|
|
237
429
|
}
|
|
238
430
|
const legacy = resolveLegacyParseResult(results, input, options);
|