ezmedicationinput 0.1.23 → 0.1.24

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.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { FhirDosage, FormatOptions, ParseOptions, ParseResult } from "./types";
1
+ import { FhirDosage, FormatOptions, LintResult, ParseOptions, ParseResult } from "./types";
2
2
  export { parseInternal } from "./parser";
3
3
  export { suggestSig } from "./suggest";
4
4
  export * from "./types";
@@ -6,6 +6,7 @@ export { nextDueDoses } from "./schedule";
6
6
  export { getRegisteredSigLocalizations, registerSigLocalization, resolveSigLocalization, resolveSigTranslation } from "./i18n";
7
7
  export type { SigLocalization, SigLocalizationConfig, SigTranslation, SigTranslationConfig } from "./i18n";
8
8
  export declare function parseSig(input: string, options?: ParseOptions): ParseResult;
9
+ export declare function lintSig(input: string, options?: ParseOptions): LintResult;
9
10
  export declare function parseSigAsync(input: string, options?: ParseOptions): Promise<ParseResult>;
10
11
  export declare function formatSig(dosage: FhirDosage, style?: "short" | "long", options?: FormatOptions): string;
11
12
  export declare function fromFhirDosage(dosage: FhirDosage, options?: FormatOptions): ParseResult;
package/dist/index.js CHANGED
@@ -25,6 +25,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.resolveSigTranslation = exports.resolveSigLocalization = exports.registerSigLocalization = exports.getRegisteredSigLocalizations = exports.nextDueDoses = exports.suggestSig = exports.parseInternal = void 0;
27
27
  exports.parseSig = parseSig;
28
+ exports.lintSig = lintSig;
28
29
  exports.parseSigAsync = parseSigAsync;
29
30
  exports.formatSig = formatSig;
30
31
  exports.fromFhirDosage = fromFhirDosage;
@@ -50,6 +51,25 @@ function parseSig(input, options) {
50
51
  (0, parser_1.applySiteCoding)(internal, options);
51
52
  return buildParseResult(internal, options);
52
53
  }
54
+ function lintSig(input, options) {
55
+ const internal = (0, parser_1.parseInternal)(input, options);
56
+ (0, parser_1.applyPrnReasonCoding)(internal, options);
57
+ (0, parser_1.applySiteCoding)(internal, options);
58
+ const result = buildParseResult(internal, options);
59
+ const groups = (0, parser_1.findUnparsedTokenGroups)(internal);
60
+ const issues = groups.map((group) => {
61
+ const text = group.range
62
+ ? internal.input.slice(group.range.start, group.range.end)
63
+ : group.tokens.map((token) => token.original).join(" ");
64
+ return {
65
+ message: "Unrecognized text",
66
+ text: text.trim() || text,
67
+ tokens: group.tokens.map((token) => token.original),
68
+ range: group.range
69
+ };
70
+ });
71
+ return { result, issues };
72
+ }
53
73
  function parseSigAsync(input, options) {
54
74
  return __awaiter(this, void 0, void 0, function* () {
55
75
  const internal = (0, parser_1.parseInternal)(input, options);
package/dist/parser.d.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  import { ParsedSigInternal, Token } from "./internal-types";
2
- import { ParseOptions } from "./types";
2
+ import { ParseOptions, TextRange } from "./types";
3
3
  export declare function tokenize(input: string): Token[];
4
+ export declare function findUnparsedTokenGroups(internal: ParsedSigInternal): Array<{
5
+ tokens: Token[];
6
+ range?: TextRange;
7
+ }>;
4
8
  export declare function parseInternal(input: string, options?: ParseOptions): ParsedSigInternal;
5
9
  /**
6
10
  * Resolves parsed site text against SNOMED dictionaries and synchronous
package/dist/parser.js CHANGED
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.tokenize = tokenize;
13
+ exports.findUnparsedTokenGroups = findUnparsedTokenGroups;
13
14
  exports.parseInternal = parseInternal;
14
15
  exports.applyPrnReasonCoding = applyPrnReasonCoding;
15
16
  exports.applyPrnReasonCodingAsync = applyPrnReasonCodingAsync;
@@ -885,6 +886,68 @@ function refineSiteRange(input, sanitized, tokenRange) {
885
886
  }
886
887
  return { start: startIndex, end: startIndex + lowerSanitized.length };
887
888
  }
889
+ function findUnparsedTokenGroups(internal) {
890
+ const leftoverTokens = internal.tokens
891
+ .filter((token) => !internal.consumed.has(token.index))
892
+ .sort((a, b) => a.index - b.index);
893
+ if (leftoverTokens.length === 0) {
894
+ return [];
895
+ }
896
+ const groups = [];
897
+ let currentGroup = [];
898
+ let previousIndex;
899
+ let minimumStart = 0;
900
+ const locateRange = (tokensToLocate, initial) => {
901
+ const lowerInput = internal.input.toLowerCase();
902
+ let searchStart = minimumStart;
903
+ let rangeStart;
904
+ let rangeEnd;
905
+ for (const token of tokensToLocate) {
906
+ const segment = token.original.trim();
907
+ if (!segment) {
908
+ continue;
909
+ }
910
+ const lowerSegment = segment.toLowerCase();
911
+ const foundIndex = lowerInput.indexOf(lowerSegment, searchStart);
912
+ if (foundIndex === -1) {
913
+ return initial;
914
+ }
915
+ if (rangeStart === undefined) {
916
+ rangeStart = foundIndex;
917
+ }
918
+ const segmentEnd = foundIndex + lowerSegment.length;
919
+ rangeEnd = rangeEnd === undefined ? segmentEnd : Math.max(rangeEnd, segmentEnd);
920
+ searchStart = segmentEnd;
921
+ }
922
+ if (rangeStart === undefined || rangeEnd === undefined) {
923
+ return initial;
924
+ }
925
+ return { start: rangeStart, end: rangeEnd };
926
+ };
927
+ const flush = () => {
928
+ if (!currentGroup.length) {
929
+ return;
930
+ }
931
+ const indices = currentGroup.map((token) => token.index);
932
+ const initialRange = computeTokenRange(internal.input, internal.tokens, indices);
933
+ const range = locateRange(currentGroup, initialRange);
934
+ groups.push({ tokens: currentGroup, range });
935
+ if (range) {
936
+ minimumStart = Math.max(minimumStart, range.end);
937
+ }
938
+ currentGroup = [];
939
+ previousIndex = undefined;
940
+ };
941
+ for (const token of leftoverTokens) {
942
+ if (previousIndex !== undefined && token.index !== previousIndex + 1) {
943
+ flush();
944
+ }
945
+ currentGroup.push(token);
946
+ previousIndex = token.index;
947
+ }
948
+ flush();
949
+ return groups;
950
+ }
888
951
  function splitToken(token) {
889
952
  if (/^[0-9]+(?:\.[0-9]+)?$/.test(token)) {
890
953
  return [token];
package/dist/types.d.ts CHANGED
@@ -527,6 +527,22 @@ export interface ParseResult {
527
527
  }>;
528
528
  };
529
529
  }
530
+ export interface LintIssue {
531
+ /** Human-readable description of why the segment could not be parsed. */
532
+ message: string;
533
+ /** Original substring that triggered the issue. */
534
+ text: string;
535
+ /** Tokens contributing to the unparsed segment. */
536
+ tokens: string[];
537
+ /** Location of {@link text} relative to the caller's original input. */
538
+ range?: TextRange;
539
+ }
540
+ export interface LintResult {
541
+ /** Standard parse output including FHIR representation and metadata. */
542
+ result: ParseResult;
543
+ /** Segments of the input that could not be interpreted. */
544
+ issues: LintIssue[];
545
+ }
530
546
  /**
531
547
  * Maps EventTiming codes (or other institution-specific timing strings) to
532
548
  * 24-hour clock representations such as "08:00".
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ezmedicationinput",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "description": "Parse concise medication sigs into FHIR R5 Dosage JSON",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",