ezmedicationinput 0.1.45 → 0.1.47
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/README.md +294 -2
- package/dist/advice.d.ts +9 -0
- package/dist/body-site-grammar.d.ts +39 -0
- package/dist/body-site-lookup.d.ts +84 -0
- package/dist/body-site-resolution.d.ts +9 -0
- package/dist/body-site-spatial.d.ts +6 -0
- package/dist/clause-tail-grammar.d.ts +18 -0
- package/dist/fhir-translations.d.ts +4 -1
- package/dist/fhir.d.ts +9 -1
- package/dist/hpsg/chart.d.ts +14 -0
- package/dist/hpsg/clause-parser.d.ts +3 -0
- package/dist/hpsg/defaults.d.ts +9 -0
- package/dist/hpsg/lexical-classes.d.ts +97 -0
- package/dist/hpsg/method-lexicon.d.ts +15 -0
- package/dist/hpsg/projection.d.ts +12 -0
- package/dist/hpsg/rule-context.d.ts +32 -0
- package/dist/hpsg/rules/core-rules.d.ts +8 -0
- package/dist/hpsg/rules/instruction-rules.d.ts +4 -0
- package/dist/hpsg/rules/prn-rules.d.ts +3 -0
- package/dist/hpsg/rules/product-route.d.ts +2 -0
- package/dist/hpsg/rules/site-rules.d.ts +4 -0
- package/dist/hpsg/rules/timing-rules.d.ts +12 -0
- package/dist/hpsg/segmenter.d.ts +6 -0
- package/dist/hpsg/signature.d.ts +116 -0
- package/dist/hpsg/timing-lexicon.d.ts +31 -0
- package/dist/hpsg/unification.d.ts +8 -0
- package/dist/index.cjs +15918 -12849
- package/dist/index.d.ts +6 -0
- package/dist/index.js +15875 -12849
- package/dist/maps.d.ts +4 -2
- package/dist/parser-state.d.ts +4 -1
- package/dist/parser.d.ts +3 -13
- package/dist/prn-reason-coding.d.ts +8 -0
- package/dist/site-coding.d.ts +5 -0
- package/dist/snomed-postcoordination.d.ts +25 -0
- package/dist/snomed.d.ts +13 -0
- package/dist/types.d.ts +110 -16
- package/dist/unit-lexicon.d.ts +8 -0
- package/package.json +1 -1
- package/dist/segment.d.ts +0 -6
package/README.md
CHANGED
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
- Surfaces warnings when discouraged tokens (`QD`, `QOD`, `BLD`) are used and optionally rejects them.
|
|
18
18
|
- Generates upcoming administration timestamps from FHIR dosage data via `nextDueDoses` using configurable clinic clocks.
|
|
19
19
|
- Auto-codes common body-site phrases (e.g. "left arm", "right eye") with SNOMED CT anatomy concepts and supports interactive lookup flows for ambiguous sites.
|
|
20
|
+
- Represents spatial body-site phrases such as `below ear`, `right side of abdomen`, `between fingers`, and Thai forms like `ระหว่างนิ้วมือ` through structured site metadata.
|
|
21
|
+
- Exposes body-site lookup/suggestion/listing helpers and SNOMED postcoordination helpers for UI search and terminology workflows.
|
|
22
|
+
|
|
23
|
+
## Parser architecture
|
|
24
|
+
|
|
25
|
+
The parser is built around an HPSG-style lexical/sign grammar: tokens become typed signs, signs unify into clause-level structures, and the final projection emits FHIR `Dosage`. It is **not** a pure academic HPSG implementation; deterministic projection is still required for FHIR shape, terminology lookup, formatting, and compatibility behavior.
|
|
20
26
|
|
|
21
27
|
## Installation
|
|
22
28
|
|
|
@@ -255,10 +261,175 @@ result.fhir.site?.coding?.[0];
|
|
|
255
261
|
// → { system: "http://snomed.info/sct", code: "368208006", display: "Left upper arm structure" }
|
|
256
262
|
```
|
|
257
263
|
|
|
264
|
+
Spatial site phrases are preserved as structured metadata on `Dosage.site.extension`
|
|
265
|
+
and in `ParseResult.meta.normalized.site.spatialRelation`.
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
const result = parseSig("apply to area between fingers");
|
|
269
|
+
|
|
270
|
+
result.fhir.site?.text;
|
|
271
|
+
// → "area between fingers"
|
|
272
|
+
|
|
273
|
+
result.meta.normalized.site?.spatialRelation;
|
|
274
|
+
// → {
|
|
275
|
+
// relationText: "between",
|
|
276
|
+
// targetText: "fingers",
|
|
277
|
+
// targetCoding: {
|
|
278
|
+
// system: "http://snomed.info/sct",
|
|
279
|
+
// code: "7569003",
|
|
280
|
+
// display: "Finger structure"
|
|
281
|
+
// }
|
|
282
|
+
// }
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Only relations that exist in the FHIR BodyStructure relative-location ValueSet
|
|
286
|
+
receive `spatialRelation.relationCoding` (for example `Above`, `Beneath`,
|
|
287
|
+
`Posterior`, `Upper`, `Lower`, and `Lateral`). Other useful spatial language,
|
|
288
|
+
such as `between`, `around`, `near`, and `inside`, is preserved as
|
|
289
|
+
`relationText` without pretending there is an official code for it.
|
|
290
|
+
|
|
291
|
+
Thai and mixed-language site phrases resolve through the same site grammar:
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
parseSig("apply ระหว่างนิ้วมือ", { locale: "th" }).longText;
|
|
295
|
+
// → "ทา บริเวณระหว่างนิ้วมือ."
|
|
296
|
+
|
|
297
|
+
parseSig("apply ระหว่างนิ้วเท้า", { locale: "th" }).longText;
|
|
298
|
+
// → "ทา บริเวณระหว่างนิ้วเท้า."
|
|
299
|
+
|
|
300
|
+
parseSig("apply ระหว่างนิ้ว", {
|
|
301
|
+
locale: "th",
|
|
302
|
+
context: { bodySiteContext: "feet" }
|
|
303
|
+
}).longText;
|
|
304
|
+
// → "ทา บริเวณระหว่างนิ้วเท้า."
|
|
305
|
+
```
|
|
306
|
+
|
|
258
307
|
When the parser encounters an unfamiliar site, it leaves the text untouched and records nothing in `meta.siteLookups`. Wrapping the phrase in braces (e.g. `apply to {mole on scalp}`) preserves the same parsing behavior but flags the entry as a **probe** so `meta.siteLookups` always contains the request. This allows UIs to display lookup widgets even before a matching code exists. Braces are optional when the site is already recognized—they simply make the clinician's intent explicit.
|
|
259
308
|
|
|
260
309
|
Unknown body sites still populate `Dosage.site.text` and `ParseResult.meta.normalized.site.text`, allowing UIs to echo the verbatim phrase while terminology lookups run asynchronously.
|
|
261
310
|
|
|
311
|
+
For typeahead/search UI, use the exported body-site helpers directly:
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
import {
|
|
315
|
+
getBodySiteCode,
|
|
316
|
+
getBodySiteCodeAsync,
|
|
317
|
+
getBodySiteText,
|
|
318
|
+
getBodySiteTextAsync,
|
|
319
|
+
listSupportedBodySiteGrammar,
|
|
320
|
+
listSupportedBodySiteText,
|
|
321
|
+
lookupBodySite,
|
|
322
|
+
suggestBodySiteText,
|
|
323
|
+
suggestBodySites
|
|
324
|
+
} from "ezmedicationinput";
|
|
325
|
+
|
|
326
|
+
getBodySiteCode("left ass");
|
|
327
|
+
// → { system: "http://snomed.info/sct", code: "723979003", display: "Structure of left buttock" }
|
|
328
|
+
|
|
329
|
+
getBodySiteText("723979003");
|
|
330
|
+
// → "left buttock"
|
|
331
|
+
|
|
332
|
+
getBodySiteText("22253000:363698007=723979003");
|
|
333
|
+
// → "left buttock"
|
|
334
|
+
|
|
335
|
+
getBodySiteCode("top of head");
|
|
336
|
+
// → { system: "http://snomed.info/sct", code: "69536005:106233006=261183002", display: "top of head" }
|
|
337
|
+
|
|
338
|
+
getBodySiteCode("top of head", { postcoordination: false });
|
|
339
|
+
// → undefined
|
|
340
|
+
|
|
341
|
+
getBodySiteText("69536005:106233006=261183002");
|
|
342
|
+
// → "top of head"
|
|
343
|
+
|
|
344
|
+
getBodySiteCode("right big toe");
|
|
345
|
+
// → { system: "http://snomed.info/sct", code: "78883009:272741003=24028007", display: "right great toe" }
|
|
346
|
+
|
|
347
|
+
getBodySiteText("78883009:272741003=24028007");
|
|
348
|
+
// → "right great toe"
|
|
349
|
+
|
|
350
|
+
getBodySiteText("22253000:363698007=723979003", {
|
|
351
|
+
parsePostcoordination: false
|
|
352
|
+
});
|
|
353
|
+
// → undefined
|
|
354
|
+
|
|
355
|
+
lookupBodySite("ระหว่างนิ้ว", { bodySiteContext: "feet" });
|
|
356
|
+
// → { text: "between toes", spatialRelation: { relationText: "between", ... }, ... }
|
|
357
|
+
|
|
358
|
+
suggestBodySites("หนัง", { limit: 5 });
|
|
359
|
+
// → [{ text: "scalp", coding: { code: "41695006", ... }, ... }]
|
|
360
|
+
|
|
361
|
+
suggestBodySiteText("นิ้วโป้ง", { limit: 5 });
|
|
362
|
+
// → ["thumb", "great toe", ...]
|
|
363
|
+
|
|
364
|
+
listSupportedBodySiteText({ limit: 10 });
|
|
365
|
+
// → ["abdomen", "affected area", ...]
|
|
366
|
+
|
|
367
|
+
listSupportedBodySiteGrammar().siteAnchors;
|
|
368
|
+
// → ["above", "around", "at", "beneath", "below", "between", ...]
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
`getBodySiteCode` and `getBodySiteText` are convenience wrappers for the common
|
|
372
|
+
phrase-to-code and code-to-label cases. `getBodySiteCode` returns direct
|
|
373
|
+
pre-coordinated body-site codings when available; otherwise it can build a
|
|
374
|
+
SNOMED topographical-modifier expression for coded spatial phrases such as
|
|
375
|
+
`top of head` or `below ear`, and SNOMED laterality expressions for digit sites
|
|
376
|
+
such as `right big toe`. Parsed medication orders use the same behavior for
|
|
377
|
+
`Dosage.site.coding` by default and still preserve the structured spatial
|
|
378
|
+
extension; pass `bodySitePostcoordination: false` to `parseSig` when a consumer
|
|
379
|
+
only accepts literal body-site codes. `getBodySiteText` resolves finding-site,
|
|
380
|
+
topographical-modifier, and laterality postcoordination by default. Pass
|
|
381
|
+
`postcoordination: false` or `parsePostcoordination: false` to require literal
|
|
382
|
+
body-site codes only.
|
|
383
|
+
|
|
384
|
+
`lookupBodySite` returns the full resolved metadata, including spatial relation
|
|
385
|
+
details. `suggestBodySites` returns ranked bundled/custom candidates for
|
|
386
|
+
autocomplete, while `suggestBodySiteText` returns only display labels.
|
|
387
|
+
`listSupportedBodySiteText` exposes the bundled/custom label inventory for UI
|
|
388
|
+
preloading, and `listSupportedBodySiteGrammar` exposes the supported site
|
|
389
|
+
anchors/prepositions, locative relations, partitive heads/modifiers, and
|
|
390
|
+
SNOMED-coded spatial relation metadata. Lookup helpers accept `siteCodeMap`;
|
|
391
|
+
phrase-based helpers also accept `bodySiteContext`, used only for genuinely
|
|
392
|
+
ambiguous shorthand such as Thai `ระหว่างนิ้ว`.
|
|
393
|
+
|
|
394
|
+
Standalone lookup helpers can also call sync or async terminology hooks:
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
getBodySiteCode("clinic site", {
|
|
398
|
+
siteCodeMap: {
|
|
399
|
+
"clinic site": {
|
|
400
|
+
coding: {
|
|
401
|
+
system: "http://example.org/sites",
|
|
402
|
+
code: "CLINIC-SITE",
|
|
403
|
+
display: "Clinic site"
|
|
404
|
+
},
|
|
405
|
+
text: "clinic site"
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
await getBodySiteCodeAsync("remote site", {
|
|
411
|
+
siteCodeResolvers: async (request) => {
|
|
412
|
+
if (request.canonical !== "remote site") return undefined;
|
|
413
|
+
return {
|
|
414
|
+
coding: {
|
|
415
|
+
system: "http://example.org/sites",
|
|
416
|
+
code: "REMOTE-SITE",
|
|
417
|
+
display: "Remote site"
|
|
418
|
+
},
|
|
419
|
+
text: "remote site"
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
await getBodySiteTextAsync(
|
|
425
|
+
{ system: "http://example.org/sites", code: "REMOTE-SITE" },
|
|
426
|
+
{
|
|
427
|
+
siteTextResolvers: async (request) =>
|
|
428
|
+
request.originalCoding.code === "REMOTE-SITE" ? "remote site" : undefined
|
|
429
|
+
}
|
|
430
|
+
);
|
|
431
|
+
```
|
|
432
|
+
|
|
262
433
|
You can extend or replace the built-in codings via `ParseOptions`:
|
|
263
434
|
|
|
264
435
|
```ts
|
|
@@ -336,6 +507,7 @@ export interface SiteCodeLookupRequest {
|
|
|
336
507
|
text: string; // Brace-free, whitespace-collapsed site text
|
|
337
508
|
normalized: string; // Lower-case variant of `text`
|
|
338
509
|
canonical: string; // Normalized key for dictionary lookups
|
|
510
|
+
spatialRelation?: BodySiteSpatialRelation; // Parsed relation + target, when present
|
|
339
511
|
isProbe: boolean; // True when the sig used `{placeholder}` syntax
|
|
340
512
|
inputText: string; // Full sig string the parser received
|
|
341
513
|
sourceText?: string; // Substring extracted from `inputText`
|
|
@@ -361,6 +533,93 @@ export type SiteCodeSuggestionResolver = (
|
|
|
361
533
|
|
|
362
534
|
Consumers that only need synchronous resolution can continue calling `parseSig`. If any synchronous resolver accidentally returns a Promise, an error is thrown with guidance to switch to `parseSigAsync`.
|
|
363
535
|
|
|
536
|
+
#### Standalone body-site helper resolver signatures
|
|
537
|
+
|
|
538
|
+
The standalone helper callbacks are intentionally smaller than parser callbacks
|
|
539
|
+
because they only resolve one phrase or one code at a time.
|
|
540
|
+
|
|
541
|
+
```ts
|
|
542
|
+
export interface BodySiteLookupRequest {
|
|
543
|
+
originalText: string;
|
|
544
|
+
text: string;
|
|
545
|
+
normalized: string;
|
|
546
|
+
canonical: string;
|
|
547
|
+
bodySiteContext?: string;
|
|
548
|
+
spatialRelation?: BodySiteSpatialRelation;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
export type BodySiteResolver = (
|
|
552
|
+
request: BodySiteLookupRequest
|
|
553
|
+
) => BodySiteDefinition | null | undefined | Promise<BodySiteDefinition | null | undefined>;
|
|
554
|
+
|
|
555
|
+
export interface BodySiteTextLookupRequest {
|
|
556
|
+
coding: BodySiteCode; // decoded literal site when postcoordination is enabled
|
|
557
|
+
originalCoding: BodySiteCode; // original input coding/code
|
|
558
|
+
parsedPostcoordination?: {
|
|
559
|
+
type: "topographicalModifier" | "laterality" | "findingSite";
|
|
560
|
+
siteCode: string;
|
|
561
|
+
modifierCode?: string;
|
|
562
|
+
lateralityCode?: string;
|
|
563
|
+
focusCode?: string;
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
export type BodySiteTextResolver = (
|
|
568
|
+
request: BodySiteTextLookupRequest
|
|
569
|
+
) => string | null | undefined | Promise<string | null | undefined>;
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
#### SNOMED finding-site postcoordination helpers
|
|
573
|
+
|
|
574
|
+
When a PRN reason has a symptom plus site (for example `pain at abdomen`), the
|
|
575
|
+
library can represent the coded symptom with a SNOMED finding-site expression.
|
|
576
|
+
The helpers are exported for callers that need the same representation outside
|
|
577
|
+
the parser.
|
|
578
|
+
|
|
579
|
+
This is separate from body-site postcoordination. PRN findings use
|
|
580
|
+
`363698007 | Finding site |`; spatial body-site phrases use
|
|
581
|
+
`106233006 | Topographical modifier |`; laterality on body sites uses
|
|
582
|
+
`272741003 | Laterality |`.
|
|
583
|
+
|
|
584
|
+
```ts
|
|
585
|
+
import {
|
|
586
|
+
buildSnomedBodySiteLateralityPostcoordinationCode,
|
|
587
|
+
buildSnomedFindingSiteCoding,
|
|
588
|
+
buildSnomedFindingSitePostcoordinationCode,
|
|
589
|
+
hasSnomedFindingSitePostcoordination
|
|
590
|
+
} from "ezmedicationinput";
|
|
591
|
+
|
|
592
|
+
buildSnomedBodySiteLateralityPostcoordinationCode("78883009", "24028007");
|
|
593
|
+
// → "78883009:272741003=24028007"
|
|
594
|
+
|
|
595
|
+
buildSnomedFindingSitePostcoordinationCode("22253000", "85562004");
|
|
596
|
+
// → "22253000:363698007=85562004"
|
|
597
|
+
|
|
598
|
+
hasSnomedFindingSitePostcoordination("22253000:363698007=85562004");
|
|
599
|
+
// → true
|
|
600
|
+
|
|
601
|
+
buildSnomedFindingSiteCoding({
|
|
602
|
+
focusCoding: {
|
|
603
|
+
system: "http://snomed.info/sct",
|
|
604
|
+
code: "22253000",
|
|
605
|
+
display: "Pain"
|
|
606
|
+
},
|
|
607
|
+
siteCoding: {
|
|
608
|
+
system: "http://snomed.info/sct",
|
|
609
|
+
code: "85562004",
|
|
610
|
+
display: "Hand"
|
|
611
|
+
},
|
|
612
|
+
display: "Pain at hand"
|
|
613
|
+
});
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
The spatial site extension helpers are also exported:
|
|
617
|
+
`buildBodySiteSpatialRelationExtension`,
|
|
618
|
+
`buildBodySiteSpatialRelationExtensions`,
|
|
619
|
+
`parseBodySiteSpatialRelationExtension`,
|
|
620
|
+
`cloneBodySiteSpatialRelation`, and
|
|
621
|
+
`BODY_SITE_SPATIAL_RELATION_EXTENSION_URL`.
|
|
622
|
+
|
|
364
623
|
You can specify the number of times (total count) the medication is supposed to be used by ending with `for {number} times`, `x {number} doses`, or simply `x {number}`
|
|
365
624
|
|
|
366
625
|
### Advanced parsing options
|
|
@@ -368,8 +627,12 @@ You can specify the number of times (total count) the medication is supposed to
|
|
|
368
627
|
`parseSig` accepts a `ParseOptions` object. Highlights:
|
|
369
628
|
|
|
370
629
|
- `context`: optional medication context (dosage form, strength, container
|
|
371
|
-
metadata) used to infer
|
|
372
|
-
`null` to explicitly disable
|
|
630
|
+
metadata, and optional `bodySiteContext`) used to infer defaults and
|
|
631
|
+
disambiguate shorthand body-site phrases. Pass `null` to explicitly disable
|
|
632
|
+
context-based inference.
|
|
633
|
+
- `context.bodySiteContext`: optional anatomical context for ambiguous site
|
|
634
|
+
shorthand. Example: Thai `ระหว่างนิ้ว` defaults to fingers, but resolves to
|
|
635
|
+
toes when `bodySiteContext` is `"feet"`, `"foot"`, or another foot/toe phrase.
|
|
373
636
|
- `smartMealExpansion`: when `true`, generic AC/PC/C meal abbreviations and
|
|
374
637
|
cadence-only instructions expand into concrete with-meal EventTiming
|
|
375
638
|
combinations (e.g. `1x3` → breakfast/lunch/dinner). This also respects
|
|
@@ -462,6 +725,35 @@ const result = calculateTotalUnits({
|
|
|
462
725
|
|
|
463
726
|
It can also handle strength-based conversions (e.g. calculating how many 100mL bottles are needed for a 500mg TID dose of a 250mg/5mL suspension).
|
|
464
727
|
|
|
728
|
+
Natural SIG units are classified from `unit-terminology.json` instead of being treated as one physics-style unit enum. Parsed results expose `meta.normalized.unitKind` / `unitSemantics`, and callers can inspect the same vocabulary with `listDoseUnitTerminology()` or `getDoseUnitSemantics(unit)`.
|
|
729
|
+
|
|
730
|
+
```ts
|
|
731
|
+
const parsed = parseSig("apply 2 FTU to face twice daily");
|
|
732
|
+
|
|
733
|
+
calculateTotalUnits({
|
|
734
|
+
dosage: parsed.fhir,
|
|
735
|
+
from: "2024-01-01T08:00:00Z",
|
|
736
|
+
durationValue: 7,
|
|
737
|
+
durationUnit: "d",
|
|
738
|
+
timeZone: "Asia/Bangkok"
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
// → { totalUnits: 28, totalApproximateQuantity: { value: 14, unit: "g", ... } }
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
Built-in approximations are intentionally conservative and overridable. Current defaults include `FTU -> 0.5 g`, `drop -> 0.05 mL`, `pea-sized amount -> 0.25 mL`, and `shot glass -> 29.5735 mL`; product/device-specific labels should pass `context.unitApproximationMap` when a better value is known. Package-level phrases such as `half bottle` can also be bridged to the inner amount by setting `context.packageUnit`, `containerValue`, and `containerUnit`.
|
|
745
|
+
|
|
746
|
+
The terminology layer also classifies non-metric SIG units that should not be blindly converted:
|
|
747
|
+
|
|
748
|
+
- `body_area_proxy`: `palm`, `handprint`, `% BSA`
|
|
749
|
+
- `product_specific_amount`: `pea-sized amount`, `shot glass`, `finger length`, `dropperful`, `applicatorful`, `capful`, `scoop`
|
|
750
|
+
- `device_actuation`: `puff`, `spray`, `pump`, `actuation`, `click`
|
|
751
|
+
- `length_of_product`: `cm ribbon`, `inch ribbon`, `cm strip`, `cm line`, `inch line`
|
|
752
|
+
- `counted_presentation`: `patch`, `ring`, `vial`, `ampule`, `nebule`, `respule`, `packet`, `sachet`, `stick-pack`, and similar package/presentation units
|
|
753
|
+
- `infusion_rate`: `drop/min` / `gtt/min`, exposed as terminology but not parsed as `doseQuantity` until rate parsing and drop-factor conversion are modeled
|
|
754
|
+
|
|
755
|
+
`calculateTotalUnits` counts presentation units such as patches and rings as discrete administrations. It does not invent gram/mL conversions for puffs, pumps, applicatorfuls, capfuls, ribbons, body-area proxies, clicks, or release-rate products unless the caller supplies product-specific context.
|
|
756
|
+
|
|
465
757
|
### Strength parsing
|
|
466
758
|
|
|
467
759
|
Use `parseStrength` to normalize medication strength strings into FHIR-compliant **Quantity** or **Ratio** structures. It understands percentages, ratios, and composite strengths.
|
package/dist/advice.d.ts
CHANGED
|
@@ -13,4 +13,13 @@ export interface AdviceParseContext {
|
|
|
13
13
|
}
|
|
14
14
|
export declare function parseAdditionalInstructions(sourceText: string, span: TextRange, context?: AdviceParseContext): ParsedAdditionalInstruction[];
|
|
15
15
|
export declare function findAdditionalInstructionDefinitionByCoding(system: string, code: string): AdditionalInstructionDefinition | undefined;
|
|
16
|
+
export declare const __test__: {
|
|
17
|
+
createDefinitionFromSource(source: {
|
|
18
|
+
system?: string;
|
|
19
|
+
code: string;
|
|
20
|
+
display: string;
|
|
21
|
+
text: string;
|
|
22
|
+
thai: string;
|
|
23
|
+
}): AdditionalInstructionDefinition;
|
|
24
|
+
};
|
|
16
25
|
export declare function buildAdditionalInstructionFramesFromCoding(system: string, code: string, sourceText?: string, span?: TextRange): AdviceFrame[] | undefined;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { BodySiteDefinition, BodySiteSpatialRelation, FhirCoding } from "./types";
|
|
2
|
+
export type BodySiteGrammarKind = "nominal" | "partitive" | "locative";
|
|
3
|
+
export type BodySiteLocativeRelation = "behind" | "around" | "under" | "above" | "below" | "beneath" | "near" | "outside" | "inside" | "between";
|
|
4
|
+
export interface BodySiteNominalFeatures {
|
|
5
|
+
kind: "nominal";
|
|
6
|
+
text: string;
|
|
7
|
+
canonical: string;
|
|
8
|
+
coding?: FhirCoding;
|
|
9
|
+
article: "definite" | "bare";
|
|
10
|
+
}
|
|
11
|
+
export interface BodySitePartitiveFeatures {
|
|
12
|
+
kind: "partitive";
|
|
13
|
+
part: string;
|
|
14
|
+
relationKey?: string;
|
|
15
|
+
whole: BodySiteNominalFeatures;
|
|
16
|
+
}
|
|
17
|
+
export interface BodySiteLocativeFeatures {
|
|
18
|
+
kind: "locative";
|
|
19
|
+
relation: BodySiteLocativeRelation;
|
|
20
|
+
target: BodySiteNominalFeatures | BodySitePartitiveFeatures;
|
|
21
|
+
}
|
|
22
|
+
export type BodySiteFeatureStructure = BodySiteNominalFeatures | BodySitePartitiveFeatures | BodySiteLocativeFeatures;
|
|
23
|
+
export interface ResolvedBodySitePhrase {
|
|
24
|
+
lookupCanonical: string;
|
|
25
|
+
resolutionCanonical: string;
|
|
26
|
+
canonical: string;
|
|
27
|
+
displayText: string;
|
|
28
|
+
coding?: FhirCoding;
|
|
29
|
+
spatialRelation?: BodySiteSpatialRelation;
|
|
30
|
+
definition?: BodySiteDefinition;
|
|
31
|
+
features: BodySiteFeatureStructure;
|
|
32
|
+
englishObjectText: string;
|
|
33
|
+
preferredPreposition?: "to" | "at" | "in" | "into";
|
|
34
|
+
}
|
|
35
|
+
export interface BodySitePhraseContext {
|
|
36
|
+
bodySiteContext?: string;
|
|
37
|
+
}
|
|
38
|
+
export declare function lookupBodySiteDefinition(map: Record<string, BodySiteDefinition> | undefined, canonical: string): BodySiteDefinition | undefined;
|
|
39
|
+
export declare function resolveBodySitePhrase(text: string, customSiteMap?: Record<string, BodySiteDefinition>, context?: BodySitePhraseContext): ResolvedBodySitePhrase | undefined;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { BodySiteCode, BodySiteDefinition, BodySiteSpatialRelation, FhirCoding } from "./types";
|
|
2
|
+
export interface BodySiteLookupOptions {
|
|
3
|
+
siteCodeMap?: Record<string, BodySiteDefinition>;
|
|
4
|
+
siteCodeResolvers?: BodySiteResolver | BodySiteResolver[];
|
|
5
|
+
bodySiteContext?: string;
|
|
6
|
+
/**
|
|
7
|
+
* Defaults to true. When true, phrase-to-code lookup can return a
|
|
8
|
+
* SNOMED-coded topographical modifier expression for spatial body-site
|
|
9
|
+
* phrases that do not have a direct pre-coordinated body-site code.
|
|
10
|
+
*/
|
|
11
|
+
postcoordination?: boolean;
|
|
12
|
+
limit?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface BodySiteTextOptions {
|
|
15
|
+
siteCodeMap?: Record<string, BodySiteDefinition>;
|
|
16
|
+
siteTextResolvers?: BodySiteTextResolver | BodySiteTextResolver[];
|
|
17
|
+
system?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Defaults to true. When true, SNOMED finding-site postcoordination strings
|
|
20
|
+
* such as "22253000:363698007=723979003" and topographical modifier
|
|
21
|
+
* expressions such as "69536005:106233006=261183002" are resolved.
|
|
22
|
+
*/
|
|
23
|
+
postcoordination?: boolean;
|
|
24
|
+
parsePostcoordination?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface BodySiteLookupResult {
|
|
27
|
+
text: string;
|
|
28
|
+
canonical: string;
|
|
29
|
+
lookupCanonical: string;
|
|
30
|
+
resolutionCanonical: string;
|
|
31
|
+
matchedText: string;
|
|
32
|
+
coding?: BodySiteCode;
|
|
33
|
+
spatialRelation?: BodySiteSpatialRelation;
|
|
34
|
+
definition?: BodySiteDefinition;
|
|
35
|
+
score: number;
|
|
36
|
+
}
|
|
37
|
+
export interface BodySiteLookupRequest {
|
|
38
|
+
originalText: string;
|
|
39
|
+
text: string;
|
|
40
|
+
normalized: string;
|
|
41
|
+
canonical: string;
|
|
42
|
+
bodySiteContext?: string;
|
|
43
|
+
spatialRelation?: BodySiteSpatialRelation;
|
|
44
|
+
}
|
|
45
|
+
export interface BodySiteTextLookupRequest {
|
|
46
|
+
coding: BodySiteCode;
|
|
47
|
+
originalCoding: BodySiteCode;
|
|
48
|
+
parsedPostcoordination?: {
|
|
49
|
+
type: "topographicalModifier" | "laterality" | "findingSite";
|
|
50
|
+
siteCode: string;
|
|
51
|
+
modifierCode?: string;
|
|
52
|
+
lateralityCode?: string;
|
|
53
|
+
focusCode?: string;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export type BodySiteResolver = (request: BodySiteLookupRequest) => BodySiteDefinition | null | undefined | Promise<BodySiteDefinition | null | undefined>;
|
|
57
|
+
export type BodySiteTextResolver = (request: BodySiteTextLookupRequest) => string | null | undefined | Promise<string | null | undefined>;
|
|
58
|
+
export interface BodySiteVocabularyOptions {
|
|
59
|
+
siteCodeMap?: Record<string, BodySiteDefinition>;
|
|
60
|
+
bodySiteContext?: string;
|
|
61
|
+
limit?: number;
|
|
62
|
+
}
|
|
63
|
+
export interface BodySiteGrammarVocabulary {
|
|
64
|
+
siteAnchors: string[];
|
|
65
|
+
siteSelfDisplayAnchors: string[];
|
|
66
|
+
locativeRelations: string[];
|
|
67
|
+
partitiveHeads: string[];
|
|
68
|
+
partitiveModifiers: string[];
|
|
69
|
+
partitiveConnectors: string[];
|
|
70
|
+
spatialRelationCodings: Record<string, FhirCoding>;
|
|
71
|
+
}
|
|
72
|
+
type BodySiteCodeInput = string | BodySiteCode | FhirCoding;
|
|
73
|
+
export declare function buildBodySiteTopographicalModifierCoding(relation: BodySiteSpatialRelation | undefined, display?: string, options?: Pick<BodySiteLookupOptions, "postcoordination">): BodySiteCode | undefined;
|
|
74
|
+
export declare function lookupBodySite(input: string, options?: BodySiteLookupOptions): BodySiteLookupResult | undefined;
|
|
75
|
+
export declare function lookupBodySiteAsync(input: string, options?: BodySiteLookupOptions): Promise<BodySiteLookupResult | undefined>;
|
|
76
|
+
export declare function getBodySiteCode(input: string, options?: BodySiteLookupOptions): BodySiteCode | undefined;
|
|
77
|
+
export declare function getBodySiteCodeAsync(input: string, options?: BodySiteLookupOptions): Promise<BodySiteCode | undefined>;
|
|
78
|
+
export declare function getBodySiteText(input: BodySiteCodeInput, options?: BodySiteTextOptions): string | undefined;
|
|
79
|
+
export declare function getBodySiteTextAsync(input: BodySiteCodeInput, options?: BodySiteTextOptions): Promise<string | undefined>;
|
|
80
|
+
export declare function suggestBodySites(input: string, options?: BodySiteLookupOptions): BodySiteLookupResult[];
|
|
81
|
+
export declare function suggestBodySiteText(input: string, options?: BodySiteLookupOptions): string[];
|
|
82
|
+
export declare function listSupportedBodySiteText(options?: BodySiteVocabularyOptions): string[];
|
|
83
|
+
export declare function listSupportedBodySiteGrammar(): BodySiteGrammarVocabulary;
|
|
84
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ParserState } from "./parser-state";
|
|
2
|
+
import { FhirCoding } from "./types";
|
|
3
|
+
export interface BodySiteResolutionCandidates {
|
|
4
|
+
canonicals: string[];
|
|
5
|
+
codings: FhirCoding[];
|
|
6
|
+
normalizedSiteText: string;
|
|
7
|
+
normalizedSiteCodingDisplay: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function collectParsedBodySiteCandidates(internal: ParserState): BodySiteResolutionCandidates;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { BodySiteSpatialRelation, FhirCodeableConcept, FhirExtension } from "./types";
|
|
2
|
+
export declare const BODY_SITE_SPATIAL_RELATION_EXTENSION_URL = "urn:ezmedicationinput:body-site-spatial-relation";
|
|
3
|
+
export declare function cloneBodySiteSpatialRelation(relation: BodySiteSpatialRelation | undefined): BodySiteSpatialRelation | undefined;
|
|
4
|
+
export declare function buildBodySiteSpatialRelationExtension(relation: BodySiteSpatialRelation | undefined): FhirExtension | undefined;
|
|
5
|
+
export declare function buildBodySiteSpatialRelationExtensions(relation: BodySiteSpatialRelation | undefined): FhirExtension[] | undefined;
|
|
6
|
+
export declare function parseBodySiteSpatialRelationExtension(concept: FhirCodeableConcept | undefined): BodySiteSpatialRelation | undefined;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AdviceParseContext, ParsedAdditionalInstruction } from "./advice";
|
|
2
|
+
import { Token } from "./parser-state";
|
|
3
|
+
import { TextRange } from "./types";
|
|
4
|
+
export interface TailTokenSegment {
|
|
5
|
+
startOffset: number;
|
|
6
|
+
tokens: Token[];
|
|
7
|
+
leadingSeparatorTokens: Token[];
|
|
8
|
+
range: TextRange;
|
|
9
|
+
text: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ParsedInstructionTokenSegment {
|
|
12
|
+
segment: TailTokenSegment;
|
|
13
|
+
instructions: ParsedAdditionalInstruction[];
|
|
14
|
+
}
|
|
15
|
+
export declare function splitTailTokenSegments(input: string, tokens: Token[]): TailTokenSegment[];
|
|
16
|
+
export declare function findStructuredInstructionTailOffset(input: string, tokens: Token[], context: AdviceParseContext): number | undefined;
|
|
17
|
+
export declare function hasInstructionBoundaryBeforeToken(tokens: Token[], tokenIndex: number): boolean;
|
|
18
|
+
export declare function parseInstructionTokenSegments(input: string, tokens: Token[], context: AdviceParseContext): ParsedInstructionTokenSegment[];
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { FhirPrimitiveElement } from "./types";
|
|
1
|
+
import { FhirExtension, FhirPrimitiveElement } from "./types";
|
|
2
2
|
export declare const FHIR_TRANSLATION_EXTENSION_URL = "http://hl7.org/fhir/StructureDefinition/translation";
|
|
3
|
+
export declare function cloneI18nRecord(i18n: Record<string, string> | undefined): Record<string, string> | undefined;
|
|
4
|
+
export declare function cloneExtension(extension: FhirExtension): FhirExtension;
|
|
5
|
+
export declare function cloneExtensions(extensions: FhirExtension[] | undefined): FhirExtension[] | undefined;
|
|
3
6
|
export declare function clonePrimitiveElement(element: FhirPrimitiveElement | undefined): FhirPrimitiveElement | undefined;
|
|
4
7
|
export declare function buildTranslationPrimitiveElement(translations: Record<string, string> | undefined, base?: FhirPrimitiveElement): FhirPrimitiveElement | undefined;
|
|
5
8
|
export declare function getPrimitiveTranslation(element: FhirPrimitiveElement | undefined, locale: string | undefined): string | undefined;
|
package/dist/fhir.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { ParserState } from "./parser-state";
|
|
2
2
|
import { CanonicalSigClause, FhirDosage } from "./types";
|
|
3
|
-
export
|
|
3
|
+
export interface FhirProjectionOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Defaults to true. When true, structured spatial body-site phrases such as
|
|
6
|
+
* "top of head" can emit a SNOMED topographical modifier expression in
|
|
7
|
+
* Dosage.site.coding while preserving the spatial-relation extension.
|
|
8
|
+
*/
|
|
9
|
+
bodySitePostcoordination?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function canonicalToFhir(clause: CanonicalSigClause, textOverride?: string, options?: FhirProjectionOptions): FhirDosage;
|
|
4
12
|
export declare function toFhir(state: ParserState): FhirDosage;
|
|
5
13
|
export declare function canonicalFromFhir(dosage: FhirDosage): CanonicalSigClause;
|
|
6
14
|
export declare function parserStateFromFhir(dosage: FhirDosage): ParserState;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Token } from "../parser-state";
|
|
2
|
+
import { HpsgGrammar, HpsgSign } from "./signature";
|
|
3
|
+
export interface HpsgChartContext {
|
|
4
|
+
tokens: Token[];
|
|
5
|
+
}
|
|
6
|
+
export interface HpsgChartOptions {
|
|
7
|
+
limit?: number;
|
|
8
|
+
maxIterations?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface HpsgChartParseResult {
|
|
11
|
+
signs: HpsgSign[];
|
|
12
|
+
best?: HpsgSign;
|
|
13
|
+
}
|
|
14
|
+
export declare function parseHpsgChart<TContext extends HpsgChartContext>(context: TContext, grammar: HpsgGrammar<TContext>, options?: HpsgChartOptions): HpsgChartParseResult;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ParserState } from "../parser-state";
|
|
2
|
+
import { ParseOptions, RouteCode } from "../types";
|
|
3
|
+
export interface HpsgDefaultConstraintDeps {
|
|
4
|
+
setRoute: (state: ParserState, code: RouteCode, text?: string) => void;
|
|
5
|
+
}
|
|
6
|
+
export declare function applyHpsgDefaultConstraints(state: ParserState, tokens: readonly {
|
|
7
|
+
lower: string;
|
|
8
|
+
index: number;
|
|
9
|
+
}[], options: ParseOptions | undefined, deps: HpsgDefaultConstraintDeps): void;
|