ezmedicationinput 0.1.42 → 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 +31 -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 +5 -2
  13. package/dist/format.js +581 -219
  14. package/dist/i18n.d.ts +4 -2
  15. package/dist/i18n.js +725 -197
  16. package/dist/index.d.ts +0 -1
  17. package/dist/index.js +221 -169
  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 +25 -0
  41. package/dist/timing-summary.js +138 -0
  42. package/dist/types.d.ts +248 -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
package/dist/types.d.ts CHANGED
@@ -3,14 +3,28 @@ export interface FhirCoding {
3
3
  system?: string;
4
4
  code?: string;
5
5
  display?: string;
6
+ _display?: FhirPrimitiveElement;
7
+ i18n?: Record<string, string>;
6
8
  }
7
9
  export interface FhirCodeableConcept {
8
10
  coding?: FhirCoding[];
9
11
  text?: string;
12
+ _text?: FhirPrimitiveElement;
13
+ }
14
+ export interface FhirExtension {
15
+ url: string;
16
+ extension?: FhirExtension[];
17
+ valueCode?: string;
18
+ valueString?: string;
19
+ }
20
+ export interface FhirPrimitiveElement {
21
+ extension?: FhirExtension[];
10
22
  }
11
23
  export interface FhirQuantity {
12
24
  value?: number;
13
25
  unit?: string;
26
+ system?: string;
27
+ code?: string;
14
28
  }
15
29
  export interface FhirRange {
16
30
  low?: FhirQuantity;
@@ -242,6 +256,8 @@ export declare enum FhirDayOfWeek {
242
256
  }
243
257
  export interface FhirTimingRepeat {
244
258
  count?: number;
259
+ boundsDuration?: FhirQuantity;
260
+ boundsRange?: FhirRange;
245
261
  frequency?: number;
246
262
  frequencyMax?: number;
247
263
  period?: number;
@@ -267,9 +283,11 @@ export interface FhirDoseAndRate {
267
283
  }
268
284
  export interface FhirDosage {
269
285
  text?: string;
286
+ patientInstruction?: string;
270
287
  timing?: FhirTiming;
271
288
  route?: FhirCodeableConcept;
272
289
  site?: FhirCodeableConcept;
290
+ method?: FhirCodeableConcept;
273
291
  additionalInstruction?: FhirCodeableConcept[];
274
292
  asNeededBoolean?: boolean;
275
293
  asNeededFor?: FhirCodeableConcept[];
@@ -296,6 +314,17 @@ export interface MedicationContext {
296
314
  export interface FormatOptions {
297
315
  locale?: "en" | "th" | string;
298
316
  i18n?: SigTranslationConfig;
317
+ /**
318
+ * Collapses repeated meal relation phrases into a grouped phrase when all
319
+ * meal anchors share the same relation (for example, "after breakfast,
320
+ * lunch and dinner" instead of repeating "after" for each meal).
321
+ */
322
+ groupMealTimingsByRelation?: boolean;
323
+ /**
324
+ * Adds a per-day frequency summary when it can be derived safely from the
325
+ * schedule (for example, "three times daily" or "วันละ 3 ครั้ง").
326
+ */
327
+ includeTimesPerDaySummary?: boolean;
299
328
  }
300
329
  export interface FormatBatchOptions extends FormatOptions {
301
330
  /**
@@ -308,21 +337,23 @@ export interface BodySiteCode {
308
337
  code: string;
309
338
  display?: string;
310
339
  system?: string;
340
+ i18n?: Record<string, string>;
311
341
  }
312
342
  export interface BodySiteDefinition {
313
343
  coding?: BodySiteCode;
314
344
  text?: string;
345
+ routeHint?: RouteCode;
315
346
  /**
316
347
  * Optional phrases that should resolve to the same coding as this entry.
317
348
  * Aliases are normalized with the same logic as map keys so callers can
318
349
  * provide punctuation-heavy variants such as "first bicuspid, left".
319
350
  */
320
- aliases?: string[];
351
+ aliases?: readonly string[];
321
352
  }
322
353
  export interface CodeableConceptDefinition {
323
354
  coding?: FhirCoding;
324
355
  text?: string;
325
- aliases?: string[];
356
+ aliases?: readonly string[];
326
357
  /** Optional translations for different locales (e.g., { "th": "ปวด" }) */
327
358
  i18n?: Record<string, string>;
328
359
  }
@@ -330,6 +361,71 @@ export interface PrnReasonDefinition extends CodeableConceptDefinition {
330
361
  }
331
362
  export interface AdditionalInstructionDefinition extends CodeableConceptDefinition {
332
363
  }
364
+ export declare enum AdvicePolarity {
365
+ Affirm = "affirm",
366
+ Negate = "negate"
367
+ }
368
+ export declare enum AdviceForce {
369
+ Instruction = "instruction",
370
+ Warning = "warning",
371
+ Caution = "caution",
372
+ Sequence = "sequence"
373
+ }
374
+ export declare enum AdviceModality {
375
+ May = "may",
376
+ Can = "can",
377
+ Might = "might",
378
+ Could = "could",
379
+ Should = "should",
380
+ Must = "must"
381
+ }
382
+ export declare enum AdviceRelation {
383
+ With = "with",
384
+ Without = "without",
385
+ Before = "before",
386
+ After = "after",
387
+ During = "during",
388
+ Then = "then",
389
+ Until = "until",
390
+ For = "for",
391
+ In = "in",
392
+ On = "on"
393
+ }
394
+ export declare enum AdviceArgumentRole {
395
+ Theme = "theme",
396
+ Object = "object",
397
+ Substance = "substance",
398
+ MealState = "meal_state",
399
+ Activity = "activity",
400
+ Material = "material",
401
+ Site = "site",
402
+ Amount = "amount",
403
+ Duration = "duration",
404
+ Time = "time",
405
+ Free = "free"
406
+ }
407
+ export interface AdviceArgument {
408
+ role: AdviceArgumentRole;
409
+ text: string;
410
+ normalized?: string;
411
+ conceptId?: string;
412
+ span?: TextRange;
413
+ }
414
+ export interface AdviceFrame {
415
+ force: AdviceForce;
416
+ polarity?: AdvicePolarity;
417
+ modality?: AdviceModality;
418
+ predicate: {
419
+ lemma: string;
420
+ semanticClass?: string;
421
+ };
422
+ relation?: AdviceRelation;
423
+ args: AdviceArgument[];
424
+ span: TextRange;
425
+ sourceText: string;
426
+ sequenceIndex?: number;
427
+ coding?: FhirCoding;
428
+ }
333
429
  export interface TextRange {
334
430
  /** Inclusive start index of the matched substring within the original input. */
335
431
  start: number;
@@ -422,6 +518,30 @@ export type SiteCodeResolver = (request: SiteCodeLookupRequest) => SiteCodeResol
422
518
  * caller's full input and the character range of the detected site phrase.
423
519
  */
424
520
  export type SiteCodeSuggestionResolver = (request: SiteCodeLookupRequest) => SiteCodeSuggestionsResult | SiteCodeSuggestion[] | SiteCodeSuggestion | null | undefined | Promise<SiteCodeSuggestionsResult | SiteCodeSuggestion[] | SiteCodeSuggestion | null | undefined>;
521
+ export interface SmartMealExpansionScope {
522
+ /**
523
+ * Optional allowlist of routes that may use smart meal expansion. When
524
+ * provided, non-matching routes are excluded unless a dosage form matches
525
+ * `includeDosageForms`.
526
+ */
527
+ includeRoutes?: RouteCode[];
528
+ /**
529
+ * Optional denylist of routes that must not use smart meal expansion.
530
+ * Exclusions take precedence over includes.
531
+ */
532
+ excludeRoutes?: RouteCode[];
533
+ /**
534
+ * Optional allowlist of dosage forms that may use smart meal expansion.
535
+ * Values are matched case-insensitively after dosage-form normalization.
536
+ */
537
+ includeDosageForms?: string[];
538
+ /**
539
+ * Optional denylist of dosage forms that must not use smart meal expansion.
540
+ * Values are matched case-insensitively after dosage-form normalization.
541
+ * Exclusions take precedence over includes.
542
+ */
543
+ excludeDosageForms?: string[];
544
+ }
425
545
  export interface ParseOptions extends FormatOptions {
426
546
  /**
427
547
  * Optional medication context that assists with default unit inference.
@@ -457,6 +577,12 @@ export interface ParseOptions extends FormatOptions {
457
577
  * `context.mealRelation` when provided.
458
578
  */
459
579
  smartMealExpansion?: boolean;
580
+ /**
581
+ * Optional route/dosage-form policy overrides for smart meal expansion.
582
+ * When omitted, the parser uses its built-in default heuristic.
583
+ * Exclusions take precedence over includes.
584
+ */
585
+ smartMealExpansionScope?: SmartMealExpansionScope;
460
586
  /**
461
587
  * Enables parsing meal dash shorthand like `1-0-1` / `1-0-0-1` into
462
588
  * multiple dosage clauses aligned to breakfast/lunch/dinner/(bedtime).
@@ -515,6 +641,95 @@ export interface ParseOptions extends FormatOptions {
515
641
  */
516
642
  prnReasonSuggestionResolvers?: PrnReasonSuggestionResolver | PrnReasonSuggestionResolver[];
517
643
  }
644
+ export interface CanonicalDoseRange {
645
+ low?: number;
646
+ high?: number;
647
+ }
648
+ export interface CanonicalDoseExpr {
649
+ value?: number;
650
+ range?: CanonicalDoseRange;
651
+ unit?: string;
652
+ evidence?: CanonicalEvidence[];
653
+ }
654
+ export interface CanonicalRouteExpr {
655
+ code?: RouteCode;
656
+ text?: string;
657
+ inferred?: boolean;
658
+ evidence?: CanonicalEvidence[];
659
+ }
660
+ export interface CanonicalSiteExpr {
661
+ text?: string;
662
+ coding?: BodySiteCode;
663
+ source?: "abbreviation" | "text" | "selection" | "resolver";
664
+ inferred?: boolean;
665
+ evidence?: CanonicalEvidence[];
666
+ }
667
+ export interface CanonicalMethodExpr {
668
+ text?: string;
669
+ _text?: FhirPrimitiveElement;
670
+ coding?: FhirCoding;
671
+ evidence?: CanonicalEvidence[];
672
+ }
673
+ export interface CanonicalScheduleExpr {
674
+ timingCode?: string;
675
+ count?: number;
676
+ duration?: number;
677
+ durationMax?: number;
678
+ durationUnit?: FhirPeriodUnit;
679
+ frequency?: number;
680
+ frequencyMax?: number;
681
+ period?: number;
682
+ periodMax?: number;
683
+ periodUnit?: FhirPeriodUnit;
684
+ dayOfWeek?: FhirDayOfWeek[];
685
+ when?: EventTiming[];
686
+ timeOfDay?: string[];
687
+ evidence?: CanonicalEvidence[];
688
+ }
689
+ export interface CanonicalPrnReasonExpr {
690
+ text?: string;
691
+ coding?: FhirCoding;
692
+ }
693
+ export interface CanonicalPrnExpr {
694
+ enabled: boolean;
695
+ reason?: CanonicalPrnReasonExpr;
696
+ reasons?: CanonicalPrnReasonExpr[];
697
+ evidence?: CanonicalEvidence[];
698
+ }
699
+ export interface CanonicalAdditionalInstructionExpr {
700
+ text?: string;
701
+ coding?: FhirCoding;
702
+ frames?: AdviceFrame[];
703
+ evidence?: CanonicalEvidence[];
704
+ }
705
+ export interface CanonicalSourceSpan extends TextRange {
706
+ text: string;
707
+ tokenIndices?: number[];
708
+ }
709
+ export interface CanonicalEvidence {
710
+ rule: string;
711
+ spans: CanonicalSourceSpan[];
712
+ score?: number;
713
+ note?: string;
714
+ }
715
+ export interface CanonicalSigClause {
716
+ kind: "administration";
717
+ rawText: string;
718
+ span?: TextRange;
719
+ raw: CanonicalSourceSpan;
720
+ dose?: CanonicalDoseExpr;
721
+ route?: CanonicalRouteExpr;
722
+ site?: CanonicalSiteExpr;
723
+ method?: CanonicalMethodExpr;
724
+ schedule?: CanonicalScheduleExpr;
725
+ prn?: CanonicalPrnExpr;
726
+ patientInstruction?: string;
727
+ additionalInstructions?: CanonicalAdditionalInstructionExpr[];
728
+ leftovers: CanonicalSourceSpan[];
729
+ evidence: CanonicalEvidence[];
730
+ confidence: number;
731
+ warnings?: string[];
732
+ }
518
733
  export interface ParseResult {
519
734
  fhir: FhirDosage;
520
735
  shortText: string;
@@ -523,21 +738,9 @@ export interface ParseResult {
523
738
  meta: {
524
739
  consumedTokens: string[];
525
740
  leftoverText?: string;
526
- normalized: {
527
- route?: RouteCode;
528
- unit?: string;
529
- site?: {
530
- text?: string;
531
- coding?: BodySiteCode;
532
- };
533
- prnReason?: {
534
- text?: string;
535
- coding?: FhirCoding;
536
- };
537
- additionalInstructions?: Array<{
538
- text?: string;
539
- coding?: FhirCoding;
540
- }>;
741
+ normalized: ParseNormalizedMeta;
742
+ canonical: {
743
+ clauses: CanonicalSigClause[];
541
744
  };
542
745
  siteLookups?: Array<{
543
746
  request: SiteCodeLookupRequest;
@@ -549,6 +752,31 @@ export interface ParseResult {
549
752
  }>;
550
753
  };
551
754
  }
755
+ export interface ParseNormalizedMeta {
756
+ route?: RouteCode;
757
+ unit?: string;
758
+ site?: {
759
+ text?: string;
760
+ coding?: BodySiteCode;
761
+ };
762
+ method?: {
763
+ text?: string;
764
+ coding?: FhirCoding;
765
+ };
766
+ patientInstruction?: string;
767
+ prnReason?: {
768
+ text?: string;
769
+ coding?: FhirCoding;
770
+ };
771
+ prnReasons?: Array<{
772
+ text?: string;
773
+ coding?: FhirCoding;
774
+ }>;
775
+ additionalInstructions?: Array<{
776
+ text?: string;
777
+ coding?: FhirCoding;
778
+ }>;
779
+ }
552
780
  export interface ParseBatchSegmentMeta {
553
781
  index: number;
554
782
  text: string;
@@ -577,21 +805,9 @@ export interface ParseBatchResult {
577
805
  meta: {
578
806
  consumedTokens: string[];
579
807
  leftoverText?: string;
580
- normalized: {
581
- route?: RouteCode;
582
- unit?: string;
583
- site?: {
584
- text?: string;
585
- coding?: BodySiteCode;
586
- };
587
- prnReason?: {
588
- text?: string;
589
- coding?: FhirCoding;
590
- };
591
- additionalInstructions?: Array<{
592
- text?: string;
593
- coding?: FhirCoding;
594
- }>;
808
+ normalized: ParseNormalizedMeta;
809
+ canonical: {
810
+ clauses: CanonicalSigClause[];
595
811
  };
596
812
  siteLookups?: Array<{
597
813
  request: SiteCodeLookupRequest;
package/dist/types.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RouteCode = exports.FhirDayOfWeek = exports.FhirPeriodUnit = exports.SNOMEDCTRouteCodes = exports.EventTiming = void 0;
3
+ exports.AdviceArgumentRole = exports.AdviceRelation = exports.AdviceModality = exports.AdviceForce = exports.AdvicePolarity = exports.RouteCode = exports.FhirDayOfWeek = exports.FhirPeriodUnit = exports.SNOMEDCTRouteCodes = exports.EventTiming = void 0;
4
4
  /**
5
5
  * Follows https://build.fhir.org/valueset-event-timing.html
6
6
  * Real-world event relating to the schedule.
@@ -226,3 +226,51 @@ var FhirDayOfWeek;
226
226
  FhirDayOfWeek["Sunday"] = "sun";
227
227
  })(FhirDayOfWeek || (exports.FhirDayOfWeek = FhirDayOfWeek = {}));
228
228
  exports.RouteCode = SNOMEDCTRouteCodes;
229
+ var AdvicePolarity;
230
+ (function (AdvicePolarity) {
231
+ AdvicePolarity["Affirm"] = "affirm";
232
+ AdvicePolarity["Negate"] = "negate";
233
+ })(AdvicePolarity || (exports.AdvicePolarity = AdvicePolarity = {}));
234
+ var AdviceForce;
235
+ (function (AdviceForce) {
236
+ AdviceForce["Instruction"] = "instruction";
237
+ AdviceForce["Warning"] = "warning";
238
+ AdviceForce["Caution"] = "caution";
239
+ AdviceForce["Sequence"] = "sequence";
240
+ })(AdviceForce || (exports.AdviceForce = AdviceForce = {}));
241
+ var AdviceModality;
242
+ (function (AdviceModality) {
243
+ AdviceModality["May"] = "may";
244
+ AdviceModality["Can"] = "can";
245
+ AdviceModality["Might"] = "might";
246
+ AdviceModality["Could"] = "could";
247
+ AdviceModality["Should"] = "should";
248
+ AdviceModality["Must"] = "must";
249
+ })(AdviceModality || (exports.AdviceModality = AdviceModality = {}));
250
+ var AdviceRelation;
251
+ (function (AdviceRelation) {
252
+ AdviceRelation["With"] = "with";
253
+ AdviceRelation["Without"] = "without";
254
+ AdviceRelation["Before"] = "before";
255
+ AdviceRelation["After"] = "after";
256
+ AdviceRelation["During"] = "during";
257
+ AdviceRelation["Then"] = "then";
258
+ AdviceRelation["Until"] = "until";
259
+ AdviceRelation["For"] = "for";
260
+ AdviceRelation["In"] = "in";
261
+ AdviceRelation["On"] = "on";
262
+ })(AdviceRelation || (exports.AdviceRelation = AdviceRelation = {}));
263
+ var AdviceArgumentRole;
264
+ (function (AdviceArgumentRole) {
265
+ AdviceArgumentRole["Theme"] = "theme";
266
+ AdviceArgumentRole["Object"] = "object";
267
+ AdviceArgumentRole["Substance"] = "substance";
268
+ AdviceArgumentRole["MealState"] = "meal_state";
269
+ AdviceArgumentRole["Activity"] = "activity";
270
+ AdviceArgumentRole["Material"] = "material";
271
+ AdviceArgumentRole["Site"] = "site";
272
+ AdviceArgumentRole["Amount"] = "amount";
273
+ AdviceArgumentRole["Duration"] = "duration";
274
+ AdviceArgumentRole["Time"] = "time";
275
+ AdviceArgumentRole["Free"] = "free";
276
+ })(AdviceArgumentRole || (exports.AdviceArgumentRole = AdviceArgumentRole = {}));
@@ -0,0 +1,3 @@
1
+ export declare function isWhitespaceChar(char: string): boolean;
2
+ export declare function isLoosePhraseSeparatorChar(char: string): boolean;
3
+ export declare function normalizeLoosePhraseKey(value: string): string;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isWhitespaceChar = isWhitespaceChar;
4
+ exports.isLoosePhraseSeparatorChar = isLoosePhraseSeparatorChar;
5
+ exports.normalizeLoosePhraseKey = normalizeLoosePhraseKey;
6
+ function isWhitespaceChar(char) {
7
+ const code = char.charCodeAt(0);
8
+ return (code <= 0x20 ||
9
+ code === 0x00a0 ||
10
+ code === 0x1680 ||
11
+ (code >= 0x2000 && code <= 0x200a) ||
12
+ code === 0x2028 ||
13
+ code === 0x2029 ||
14
+ code === 0x202f ||
15
+ code === 0x205f ||
16
+ code === 0x3000);
17
+ }
18
+ function isAsciiPunctuationCode(code) {
19
+ return ((code >= 0x21 && code <= 0x2f) ||
20
+ (code >= 0x3a && code <= 0x40) ||
21
+ (code >= 0x5b && code <= 0x60) ||
22
+ (code >= 0x7b && code <= 0x7e));
23
+ }
24
+ function isLoosePhraseSeparatorChar(char) {
25
+ const code = char.charCodeAt(0);
26
+ return (isWhitespaceChar(char) ||
27
+ isAsciiPunctuationCode(code) ||
28
+ (code >= 0x2000 && code <= 0x206f) ||
29
+ (code >= 0x2e00 && code <= 0x2e7f) ||
30
+ (code >= 0x3000 && code <= 0x303f));
31
+ }
32
+ function normalizeLoosePhraseKey(value) {
33
+ const lowered = value.trim().toLowerCase();
34
+ let normalized = "";
35
+ let pendingSpace = false;
36
+ for (const char of lowered) {
37
+ if (isLoosePhraseSeparatorChar(char)) {
38
+ pendingSpace = normalized.length > 0;
39
+ continue;
40
+ }
41
+ if (pendingSpace) {
42
+ normalized += " ";
43
+ pendingSpace = false;
44
+ }
45
+ normalized += char;
46
+ }
47
+ return normalized.trim();
48
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ezmedicationinput",
3
- "version": "0.1.42",
3
+ "version": "0.1.44",
4
4
  "description": "Parse concise medication sigs into FHIR R5 Dosage JSON",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",