ezmedicationinput 0.1.5 → 0.1.6
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/suggest.js +205 -56
- package/package.json +1 -1
package/dist/suggest.js
CHANGED
|
@@ -206,11 +206,40 @@ function normalizeSpacing(value) {
|
|
|
206
206
|
.trim()
|
|
207
207
|
.replace(/\s+/g, " ");
|
|
208
208
|
}
|
|
209
|
+
function removeWhitespaceCharacters(value) {
|
|
210
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
211
|
+
const code = value.charCodeAt(index);
|
|
212
|
+
if (code <= 32) {
|
|
213
|
+
const result = [];
|
|
214
|
+
for (let inner = 0; inner < value.length; inner += 1) {
|
|
215
|
+
const currentCode = value.charCodeAt(inner);
|
|
216
|
+
if (currentCode > 32) {
|
|
217
|
+
result.push(value.charAt(inner));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return result.join("");
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return value;
|
|
224
|
+
}
|
|
225
|
+
function removeDashes(value) {
|
|
226
|
+
if (value.indexOf("-") === -1) {
|
|
227
|
+
return value;
|
|
228
|
+
}
|
|
229
|
+
const result = [];
|
|
230
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
231
|
+
const char = value.charAt(index);
|
|
232
|
+
if (char !== "-") {
|
|
233
|
+
result.push(char);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return result.join("");
|
|
237
|
+
}
|
|
209
238
|
function getUnitVariants(unit) {
|
|
210
239
|
var _a;
|
|
211
240
|
const canonical = (_a = resolveCanonicalUnit(unit)) !== null && _a !== void 0 ? _a : normalizeSpacing(unit);
|
|
212
241
|
const normalizedCanonical = normalizeKey(canonical);
|
|
213
|
-
const variants = new
|
|
242
|
+
const variants = new Map();
|
|
214
243
|
const push = (candidate) => {
|
|
215
244
|
if (!candidate) {
|
|
216
245
|
return;
|
|
@@ -219,7 +248,11 @@ function getUnitVariants(unit) {
|
|
|
219
248
|
if (!normalizedCandidate) {
|
|
220
249
|
return;
|
|
221
250
|
}
|
|
222
|
-
|
|
251
|
+
const lower = normalizedCandidate.toLowerCase();
|
|
252
|
+
if (variants.has(lower)) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
variants.set(lower, { value: normalizedCandidate, lower });
|
|
223
256
|
};
|
|
224
257
|
push(canonical);
|
|
225
258
|
push(unit);
|
|
@@ -229,7 +262,7 @@ function getUnitVariants(unit) {
|
|
|
229
262
|
push(candidate);
|
|
230
263
|
}
|
|
231
264
|
}
|
|
232
|
-
return [...variants];
|
|
265
|
+
return [...variants.values()];
|
|
233
266
|
}
|
|
234
267
|
function buildIntervalTokens(input) {
|
|
235
268
|
const intervals = new Set();
|
|
@@ -289,16 +322,21 @@ function buildWhenSequences() {
|
|
|
289
322
|
return sequences;
|
|
290
323
|
}
|
|
291
324
|
const PRECOMPUTED_WHEN_SEQUENCES = buildWhenSequences();
|
|
292
|
-
function
|
|
325
|
+
function tokenizeLowercaseForMatching(value) {
|
|
293
326
|
return value
|
|
294
|
-
.toLowerCase()
|
|
295
327
|
.split(/\s+/)
|
|
296
328
|
.map((token) => token.replace(/^[^a-z0-9-]+|[^a-z0-9-]+$/g, ""))
|
|
297
329
|
.filter((token) => token.length > 0)
|
|
298
330
|
.filter((token) => !OPTIONAL_MATCH_TOKENS.has(token));
|
|
299
331
|
}
|
|
332
|
+
function tokenizeForMatching(value) {
|
|
333
|
+
return tokenizeLowercaseForMatching(value.toLowerCase());
|
|
334
|
+
}
|
|
335
|
+
function canonicalizeLowercaseForMatching(value) {
|
|
336
|
+
return tokenizeLowercaseForMatching(value).join(" ");
|
|
337
|
+
}
|
|
300
338
|
function canonicalizeForMatching(value) {
|
|
301
|
-
return
|
|
339
|
+
return canonicalizeLowercaseForMatching(value.toLowerCase());
|
|
302
340
|
}
|
|
303
341
|
function tokensMatch(prefixTokens, candidateTokens) {
|
|
304
342
|
if (prefixTokens.length === 0) {
|
|
@@ -343,12 +381,13 @@ function buildUnitRoutePairs(contextUnit, options) {
|
|
|
343
381
|
if (!cleanRoute) {
|
|
344
382
|
return;
|
|
345
383
|
}
|
|
346
|
-
const
|
|
384
|
+
const routeLower = cleanRoute.toLowerCase();
|
|
385
|
+
const key = `${normalizedUnit}::${routeLower}`;
|
|
347
386
|
if (seen.has(key)) {
|
|
348
387
|
return;
|
|
349
388
|
}
|
|
350
389
|
seen.add(key);
|
|
351
|
-
pairs.push({ unit: canonicalUnit, route: cleanRoute });
|
|
390
|
+
pairs.push({ unit: canonicalUnit, route: cleanRoute, routeLower });
|
|
352
391
|
};
|
|
353
392
|
addPair(contextUnit);
|
|
354
393
|
for (const preference of DEFAULT_UNIT_ROUTE_ORDER) {
|
|
@@ -404,53 +443,93 @@ function buildDoseValues(input) {
|
|
|
404
443
|
}
|
|
405
444
|
return [...values];
|
|
406
445
|
}
|
|
446
|
+
const CANDIDATE_FINGERPRINT_CACHE = new Map();
|
|
447
|
+
function getCandidateFingerprint(candidateLower) {
|
|
448
|
+
let fingerprint = CANDIDATE_FINGERPRINT_CACHE.get(candidateLower);
|
|
449
|
+
if (!fingerprint) {
|
|
450
|
+
fingerprint = {};
|
|
451
|
+
CANDIDATE_FINGERPRINT_CACHE.set(candidateLower, fingerprint);
|
|
452
|
+
}
|
|
453
|
+
return fingerprint;
|
|
454
|
+
}
|
|
407
455
|
function generateCandidateDirections(pairs, doseValues, prnReasons, intervalTokens, whenSequences, limit, matcher) {
|
|
408
456
|
const suggestions = [];
|
|
409
457
|
const seen = new Set();
|
|
410
|
-
const
|
|
411
|
-
|
|
458
|
+
const doseVariantMap = new Map();
|
|
459
|
+
for (const dose of doseValues) {
|
|
460
|
+
const normalized = normalizeSpacing(dose);
|
|
412
461
|
if (!normalized) {
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
const lower = normalized.toLowerCase();
|
|
465
|
+
if (!doseVariantMap.has(lower)) {
|
|
466
|
+
doseVariantMap.set(lower, { value: normalized, lower });
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
const doseVariants = [...doseVariantMap.values()];
|
|
470
|
+
const push = (value, lower) => {
|
|
471
|
+
if (!lower) {
|
|
413
472
|
return false;
|
|
414
473
|
}
|
|
415
|
-
|
|
416
|
-
if (seen.has(key)) {
|
|
474
|
+
if (seen.has(lower)) {
|
|
417
475
|
return false;
|
|
418
476
|
}
|
|
419
|
-
|
|
420
|
-
if (!matcher(normalized)) {
|
|
477
|
+
if (!matcher(value, lower)) {
|
|
421
478
|
return false;
|
|
422
479
|
}
|
|
423
|
-
|
|
480
|
+
seen.add(lower);
|
|
481
|
+
suggestions.push(value);
|
|
424
482
|
return suggestions.length >= limit;
|
|
425
483
|
};
|
|
426
484
|
for (const pair of pairs) {
|
|
427
485
|
const unitVariants = getUnitVariants(pair.unit);
|
|
486
|
+
const route = pair.route;
|
|
487
|
+
const routeLower = pair.routeLower;
|
|
428
488
|
for (const code of FREQUENCY_CODES) {
|
|
489
|
+
const codeSuffix = ` ${code}`;
|
|
429
490
|
for (const unitVariant of unitVariants) {
|
|
430
|
-
|
|
431
|
-
|
|
491
|
+
const unitRoute = `${unitVariant.value} ${route}`;
|
|
492
|
+
const unitRouteLower = `${unitVariant.lower} ${routeLower}`;
|
|
493
|
+
for (const doseVariant of doseVariants) {
|
|
494
|
+
const candidate = `${doseVariant.value} ${unitRoute}${codeSuffix}`;
|
|
495
|
+
const candidateLower = `${doseVariant.lower} ${unitRouteLower}${codeSuffix}`;
|
|
496
|
+
if (push(candidate, candidateLower)) {
|
|
432
497
|
return suggestions;
|
|
433
498
|
}
|
|
434
499
|
}
|
|
435
500
|
}
|
|
436
|
-
|
|
501
|
+
const candidate = `${route}${codeSuffix}`;
|
|
502
|
+
const candidateLower = `${routeLower}${codeSuffix}`;
|
|
503
|
+
if (push(candidate, candidateLower)) {
|
|
437
504
|
return suggestions;
|
|
438
505
|
}
|
|
439
506
|
}
|
|
440
507
|
for (const interval of intervalTokens) {
|
|
508
|
+
const intervalSuffix = ` ${interval}`;
|
|
441
509
|
for (const unitVariant of unitVariants) {
|
|
442
|
-
|
|
443
|
-
|
|
510
|
+
const unitRoute = `${unitVariant.value} ${route}`;
|
|
511
|
+
const unitRouteLower = `${unitVariant.lower} ${routeLower}`;
|
|
512
|
+
for (const doseVariant of doseVariants) {
|
|
513
|
+
const base = `${doseVariant.value} ${unitRoute}`;
|
|
514
|
+
const baseLower = `${doseVariant.lower} ${unitRouteLower}`;
|
|
515
|
+
const intervalCandidate = `${base}${intervalSuffix}`;
|
|
516
|
+
const intervalCandidateLower = `${baseLower}${intervalSuffix}`;
|
|
517
|
+
if (push(intervalCandidate, intervalCandidateLower)) {
|
|
444
518
|
return suggestions;
|
|
445
519
|
}
|
|
446
520
|
for (const reason of prnReasons) {
|
|
447
|
-
|
|
521
|
+
const reasonSuffix = `${intervalSuffix} prn ${reason}`;
|
|
522
|
+
const reasonCandidate = `${base}${reasonSuffix}`;
|
|
523
|
+
const reasonCandidateLower = `${baseLower}${reasonSuffix}`;
|
|
524
|
+
if (push(reasonCandidate, reasonCandidateLower)) {
|
|
448
525
|
return suggestions;
|
|
449
526
|
}
|
|
450
527
|
}
|
|
451
528
|
}
|
|
452
529
|
}
|
|
453
|
-
|
|
530
|
+
const candidate = `${route}${intervalSuffix}`;
|
|
531
|
+
const candidateLower = `${routeLower}${intervalSuffix}`;
|
|
532
|
+
if (push(candidate, candidateLower)) {
|
|
454
533
|
return suggestions;
|
|
455
534
|
}
|
|
456
535
|
}
|
|
@@ -459,77 +538,130 @@ function generateCandidateDirections(pairs, doseValues, prnReasons, intervalToke
|
|
|
459
538
|
if (!freqToken) {
|
|
460
539
|
continue;
|
|
461
540
|
}
|
|
462
|
-
|
|
541
|
+
const base = `1x${freq} ${route}`;
|
|
542
|
+
const baseLower = `1x${freq} ${routeLower}`;
|
|
543
|
+
const freqCandidate = `${base} ${freqToken}`;
|
|
544
|
+
const freqCandidateLower = `${baseLower} ${freqToken}`;
|
|
545
|
+
if (push(freqCandidate, freqCandidateLower)) {
|
|
463
546
|
return suggestions;
|
|
464
547
|
}
|
|
465
548
|
for (const when of CORE_WHEN_TOKENS) {
|
|
466
|
-
|
|
549
|
+
const whenCandidate = `${base} ${when}`;
|
|
550
|
+
const whenCandidateLower = `${baseLower} ${when}`;
|
|
551
|
+
if (push(whenCandidate, whenCandidateLower)) {
|
|
467
552
|
return suggestions;
|
|
468
553
|
}
|
|
469
554
|
}
|
|
470
555
|
}
|
|
471
556
|
for (const whenSequence of whenSequences) {
|
|
472
|
-
const suffix = whenSequence.join(" ")
|
|
557
|
+
const suffix = ` ${whenSequence.join(" ")}`;
|
|
473
558
|
for (const unitVariant of unitVariants) {
|
|
474
|
-
|
|
475
|
-
|
|
559
|
+
const unitRoute = `${unitVariant.value} ${route}`;
|
|
560
|
+
const unitRouteLower = `${unitVariant.lower} ${routeLower}`;
|
|
561
|
+
for (const doseVariant of doseVariants) {
|
|
562
|
+
const base = `${doseVariant.value} ${unitRoute}`;
|
|
563
|
+
const baseLower = `${doseVariant.lower} ${unitRouteLower}`;
|
|
564
|
+
const candidate = `${base}${suffix}`;
|
|
565
|
+
const candidateLower = `${baseLower}${suffix}`;
|
|
566
|
+
if (push(candidate, candidateLower)) {
|
|
476
567
|
return suggestions;
|
|
477
568
|
}
|
|
478
569
|
}
|
|
479
570
|
}
|
|
480
|
-
|
|
571
|
+
const candidate = `${route}${suffix}`;
|
|
572
|
+
const candidateLower = `${routeLower}${suffix}`;
|
|
573
|
+
if (push(candidate, candidateLower)) {
|
|
481
574
|
return suggestions;
|
|
482
575
|
}
|
|
483
576
|
}
|
|
484
577
|
for (const reason of prnReasons) {
|
|
578
|
+
const reasonSuffix = ` prn ${reason}`;
|
|
485
579
|
for (const unitVariant of unitVariants) {
|
|
486
|
-
|
|
487
|
-
|
|
580
|
+
const unitRoute = `${unitVariant.value} ${route}`;
|
|
581
|
+
const unitRouteLower = `${unitVariant.lower} ${routeLower}`;
|
|
582
|
+
for (const doseVariant of doseVariants) {
|
|
583
|
+
const base = `${doseVariant.value} ${unitRoute}`;
|
|
584
|
+
const baseLower = `${doseVariant.lower} ${unitRouteLower}`;
|
|
585
|
+
const candidate = `${base}${reasonSuffix}`;
|
|
586
|
+
const candidateLower = `${baseLower}${reasonSuffix}`;
|
|
587
|
+
if (push(candidate, candidateLower)) {
|
|
488
588
|
return suggestions;
|
|
489
589
|
}
|
|
490
590
|
}
|
|
491
591
|
}
|
|
492
|
-
|
|
592
|
+
const candidate = `${route}${reasonSuffix}`;
|
|
593
|
+
const candidateLower = `${routeLower}${reasonSuffix}`;
|
|
594
|
+
if (push(candidate, candidateLower)) {
|
|
493
595
|
return suggestions;
|
|
494
596
|
}
|
|
495
597
|
}
|
|
496
598
|
}
|
|
497
599
|
return suggestions;
|
|
498
600
|
}
|
|
499
|
-
function matchesPrefix(
|
|
500
|
-
|
|
601
|
+
function matchesPrefix(_candidate, candidateLower, context) {
|
|
602
|
+
var _a, _b, _c, _d, _e, _f;
|
|
603
|
+
if (!context.raw) {
|
|
501
604
|
return true;
|
|
502
605
|
}
|
|
503
|
-
|
|
504
|
-
if (normalizedCandidate.startsWith(prefix)) {
|
|
606
|
+
if (!context.hasCanonical && !context.hasTokens) {
|
|
505
607
|
return true;
|
|
506
608
|
}
|
|
507
|
-
|
|
508
|
-
if (compactCandidate.startsWith(prefixCompact)) {
|
|
609
|
+
if (candidateLower.startsWith(context.raw)) {
|
|
509
610
|
return true;
|
|
510
611
|
}
|
|
511
|
-
const
|
|
512
|
-
if (
|
|
513
|
-
|
|
612
|
+
const fingerprint = getCandidateFingerprint(candidateLower);
|
|
613
|
+
if (context.requiresCompact) {
|
|
614
|
+
const compactCandidate = (_a = fingerprint.compact) !== null && _a !== void 0 ? _a : (fingerprint.compact = removeWhitespaceCharacters(candidateLower));
|
|
615
|
+
if (compactCandidate.startsWith(context.compact)) {
|
|
616
|
+
return true;
|
|
617
|
+
}
|
|
514
618
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
619
|
+
if (context.requiresNoDashes) {
|
|
620
|
+
const candidateNoDashes = (_b = fingerprint.noDashes) !== null && _b !== void 0 ? _b : (fingerprint.noDashes = removeDashes(candidateLower));
|
|
621
|
+
if (candidateNoDashes.startsWith(context.noDashes)) {
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
518
624
|
}
|
|
519
|
-
const
|
|
520
|
-
|
|
521
|
-
|
|
625
|
+
const getCandidateTokens = () => {
|
|
626
|
+
if (!fingerprint.tokens) {
|
|
627
|
+
fingerprint.tokens = tokenizeLowercaseForMatching(candidateLower);
|
|
628
|
+
}
|
|
629
|
+
return fingerprint.tokens;
|
|
630
|
+
};
|
|
631
|
+
if (context.hasCanonical) {
|
|
632
|
+
const canonicalCandidate = (_c = fingerprint.canonical) !== null && _c !== void 0 ? _c : (fingerprint.canonical = getCandidateTokens().join(" "));
|
|
633
|
+
if (canonicalCandidate.startsWith(context.canonical)) {
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
if (context.requiresCanonicalCompact) {
|
|
637
|
+
const canonicalCompact = (_d = fingerprint.canonicalCompact) !== null && _d !== void 0 ? _d : (fingerprint.canonicalCompact = removeWhitespaceCharacters(canonicalCandidate));
|
|
638
|
+
if (canonicalCompact.startsWith(context.canonicalCompact)) {
|
|
639
|
+
return true;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
if (context.requiresCanonicalNoDashes) {
|
|
643
|
+
const canonicalNoDashes = (_e = fingerprint.canonicalNoDashes) !== null && _e !== void 0 ? _e : (fingerprint.canonicalNoDashes = removeDashes(canonicalCandidate));
|
|
644
|
+
if (canonicalNoDashes.startsWith(context.canonicalNoDashes)) {
|
|
645
|
+
return true;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
522
648
|
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
649
|
+
if (context.hasTokens) {
|
|
650
|
+
const resolvedTokens = getCandidateTokens();
|
|
651
|
+
if (tokensMatch(context.tokens, resolvedTokens)) {
|
|
652
|
+
return true;
|
|
653
|
+
}
|
|
654
|
+
if (context.requiresTokenNoDashes) {
|
|
655
|
+
const candidateTokensNoDashes = (_f = fingerprint.tokensNoDashes) !== null && _f !== void 0 ? _f : (fingerprint.tokensNoDashes = resolvedTokens.map((token) => removeDashes(token)));
|
|
656
|
+
if (tokensMatch(context.tokensNoDashes, candidateTokensNoDashes)) {
|
|
657
|
+
return true;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
526
660
|
}
|
|
527
|
-
|
|
528
|
-
if (canonicalNoDashes.startsWith(prefixCanonicalNoDashes)) {
|
|
661
|
+
else if (context.requiresTokenNoDashes) {
|
|
529
662
|
return true;
|
|
530
663
|
}
|
|
531
|
-
|
|
532
|
-
return tokensMatch(prefixTokensNoDashes, candidateTokensNoDashes);
|
|
664
|
+
return false;
|
|
533
665
|
}
|
|
534
666
|
function suggestSig(input, options) {
|
|
535
667
|
var _a, _b;
|
|
@@ -540,17 +672,34 @@ function suggestSig(input, options) {
|
|
|
540
672
|
const prefix = normalizeSpacing(input.toLowerCase());
|
|
541
673
|
const prefixCompact = prefix.replace(/\s+/g, "");
|
|
542
674
|
const prefixNoDashes = prefix.replace(/-/g, "");
|
|
543
|
-
const
|
|
675
|
+
const prefixTokens = tokenizeLowercaseForMatching(prefix);
|
|
676
|
+
const prefixCanonical = prefixTokens.join(" ");
|
|
544
677
|
const prefixCanonicalCompact = prefixCanonical.replace(/\s+/g, "");
|
|
545
678
|
const prefixCanonicalNoDashes = prefixCanonical.replace(/-/g, "");
|
|
546
|
-
const prefixTokens = tokenizeForMatching(prefixCanonical);
|
|
547
679
|
const prefixTokensNoDashes = prefixTokens.map((token) => token.replace(/-/g, ""));
|
|
680
|
+
const prefixContext = {
|
|
681
|
+
raw: prefix,
|
|
682
|
+
compact: prefixCompact,
|
|
683
|
+
noDashes: prefixNoDashes,
|
|
684
|
+
canonical: prefixCanonical,
|
|
685
|
+
canonicalCompact: prefixCanonicalCompact,
|
|
686
|
+
canonicalNoDashes: prefixCanonicalNoDashes,
|
|
687
|
+
tokens: prefixTokens,
|
|
688
|
+
tokensNoDashes: prefixTokensNoDashes,
|
|
689
|
+
hasCanonical: prefixCanonical.length > 0,
|
|
690
|
+
hasTokens: prefixTokens.length > 0,
|
|
691
|
+
requiresCompact: prefixCompact !== prefix,
|
|
692
|
+
requiresNoDashes: prefixNoDashes !== prefix,
|
|
693
|
+
requiresCanonicalCompact: prefixCanonicalCompact !== prefixCanonical,
|
|
694
|
+
requiresCanonicalNoDashes: prefixCanonicalNoDashes !== prefixCanonical,
|
|
695
|
+
requiresTokenNoDashes: prefixTokens.some((token, index) => token !== prefixTokensNoDashes[index]),
|
|
696
|
+
};
|
|
548
697
|
const contextUnit = (0, context_1.inferUnitFromContext)((_b = options === null || options === void 0 ? void 0 : options.context) !== null && _b !== void 0 ? _b : undefined);
|
|
549
698
|
const pairs = buildUnitRoutePairs(contextUnit, options);
|
|
550
699
|
const doseValues = buildDoseValues(input);
|
|
551
700
|
const prnReasons = buildPrnReasons(options === null || options === void 0 ? void 0 : options.prnReasons);
|
|
552
701
|
const intervalTokens = buildIntervalTokens(input);
|
|
553
702
|
const whenSequences = PRECOMPUTED_WHEN_SEQUENCES;
|
|
554
|
-
const matcher = (candidate) => matchesPrefix(candidate,
|
|
703
|
+
const matcher = (candidate, candidateLower) => matchesPrefix(candidate, candidateLower, prefixContext);
|
|
555
704
|
return generateCandidateDirections(pairs, doseValues, prnReasons, intervalTokens, whenSequences, limit, matcher);
|
|
556
705
|
}
|