ezmedicationinput 0.1.9 → 0.1.12
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 +101 -1
- package/dist/fhir.js +28 -7
- package/dist/i18n.js +65 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +89 -28
- package/dist/internal-types.d.ts +8 -1
- package/dist/maps.d.ts +8 -1
- package/dist/maps.js +313 -5
- package/dist/parser.d.ts +10 -0
- package/dist/parser.js +372 -18
- package/dist/schedule.js +159 -1
- package/dist/types.d.ts +84 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
- Applies medication context to infer default units when they are omitted.
|
|
15
15
|
- Surfaces warnings when discouraged tokens (`QD`, `QOD`, `BLD`) are used and optionally rejects them.
|
|
16
16
|
- Generates upcoming administration timestamps from FHIR dosage data via `nextDueDoses` using configurable clinic clocks.
|
|
17
|
+
- 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.
|
|
17
18
|
|
|
18
19
|
## Installation
|
|
19
20
|
|
|
@@ -110,6 +111,105 @@ When `when` is populated, `timeOfDay` is intentionally omitted to stay within HL
|
|
|
110
111
|
|
|
111
112
|
Routes always include SNOMED CT codings. Every code from the SNOMED Route of Administration value set is represented so you can confidently pass parsed results into downstream FHIR services that expect coded routes.
|
|
112
113
|
|
|
114
|
+
### SNOMED body-site coding & interactive probes
|
|
115
|
+
|
|
116
|
+
Spelled-out application sites are automatically coded when the phrase is known to the bundled SNOMED CT anatomy dictionary. The normalized site text is also surfaced in `Dosage.site.text` and in the `ParseResult.meta.normalized.site` object.
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { parseSig } from "ezmedicationinput";
|
|
120
|
+
|
|
121
|
+
const result = parseSig("apply cream to left arm twice daily");
|
|
122
|
+
|
|
123
|
+
result.fhir.site?.coding?.[0];
|
|
124
|
+
// → { system: "http://snomed.info/sct", code: "368208006", display: "Left upper arm structure" }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
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.
|
|
128
|
+
|
|
129
|
+
You can extend or replace the built-in codings via `ParseOptions`:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import { parseSigAsync } from "ezmedicationinput";
|
|
133
|
+
|
|
134
|
+
const result = await parseSigAsync("apply to {left temple} nightly", {
|
|
135
|
+
siteCodeMap: {
|
|
136
|
+
"left temple": {
|
|
137
|
+
coding: {
|
|
138
|
+
system: "http://example.org/custom",
|
|
139
|
+
code: "LTEMP",
|
|
140
|
+
display: "Left temple"
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
siteCodeResolvers: async (request) => {
|
|
145
|
+
if (request.canonical === "mole on scalp") {
|
|
146
|
+
return {
|
|
147
|
+
coding: { system: "http://snomed.info/sct", code: "39937001", display: "Scalp structure" },
|
|
148
|
+
text: request.text
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return undefined;
|
|
152
|
+
},
|
|
153
|
+
siteCodeSuggestionResolvers: async (request) => {
|
|
154
|
+
if (request.isProbe) {
|
|
155
|
+
return [
|
|
156
|
+
{
|
|
157
|
+
coding: { system: "http://snomed.info/sct", code: "39937001", display: "Scalp structure" },
|
|
158
|
+
text: "Scalp"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
coding: { system: "http://snomed.info/sct", code: "280447003", display: "Temple region of head" },
|
|
162
|
+
text: "Temple"
|
|
163
|
+
}
|
|
164
|
+
];
|
|
165
|
+
}
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
result.meta.siteLookups;
|
|
171
|
+
// → [{ request: { text: "left temple", isProbe: true, ... }, suggestions: [...] }]
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
- `siteCodeMap` lets you supply deterministic overrides for normalized site phrases.
|
|
175
|
+
- `siteCodeResolvers` (sync or async) can call external services to resolve sites on demand.
|
|
176
|
+
- `siteCodeSuggestionResolvers` return candidate codes; their results populate `meta.siteLookups[0].suggestions`.
|
|
177
|
+
- Each resolver receives the full `SiteCodeLookupRequest`, including the original input, the cleaned site text, and a `{ start, end }` range you can use to highlight the substring in UI workflows.
|
|
178
|
+
- `parseSigAsync` behaves like `parseSig` but awaits asynchronous resolvers and suggestion providers.
|
|
179
|
+
|
|
180
|
+
#### Site resolver signatures
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
export interface SiteCodeLookupRequest {
|
|
184
|
+
originalText: string; // Sanitized phrase before brace/whitespace cleanup
|
|
185
|
+
text: string; // Brace-free, whitespace-collapsed site text
|
|
186
|
+
normalized: string; // Lower-case variant of `text`
|
|
187
|
+
canonical: string; // Normalized key for dictionary lookups
|
|
188
|
+
isProbe: boolean; // True when the sig used `{placeholder}` syntax
|
|
189
|
+
inputText: string; // Full sig string the parser received
|
|
190
|
+
sourceText?: string; // Substring extracted from `inputText`
|
|
191
|
+
range?: { start: number; end: number }; // Character range of `sourceText`
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export type SiteCodeResolver = (
|
|
195
|
+
request: SiteCodeLookupRequest
|
|
196
|
+
) => SiteCodeResolution | null | undefined | Promise<SiteCodeResolution | null | undefined>;
|
|
197
|
+
|
|
198
|
+
export type SiteCodeSuggestionResolver = (
|
|
199
|
+
request: SiteCodeLookupRequest
|
|
200
|
+
) =>
|
|
201
|
+
| SiteCodeSuggestionsResult
|
|
202
|
+
| SiteCodeSuggestion[]
|
|
203
|
+
| SiteCodeSuggestion
|
|
204
|
+
| null
|
|
205
|
+
| undefined
|
|
206
|
+
| Promise<SiteCodeSuggestionsResult | SiteCodeSuggestion[] | SiteCodeSuggestion | null | undefined>;
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
`SiteCodeResolution`, `SiteCodeSuggestion`, and `SiteCodeSuggestionsResult` mirror the values shown in the example above. Resolvers can use `request.range` (start inclusive, end exclusive) together with `request.sourceText` to paint highlights or replace the detected phrase in client applications.
|
|
210
|
+
|
|
211
|
+
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`.
|
|
212
|
+
|
|
113
213
|
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}`
|
|
114
214
|
|
|
115
215
|
### Advanced parsing options
|
|
@@ -128,7 +228,7 @@ You can specify the number of times (total count) the medication is supposed to
|
|
|
128
228
|
|
|
129
229
|
### Next due dose generation
|
|
130
230
|
|
|
131
|
-
`nextDueDoses` produces upcoming administration timestamps from an existing FHIR `Dosage`. Supply the evaluation window (`from`), optionally the order start (`orderedAt`), and clinic clock details such as a time zone and event timing anchors.
|
|
231
|
+
`nextDueDoses` produces upcoming administration timestamps from an existing FHIR `Dosage`. Supply the evaluation window (`from`), optionally the order start (`orderedAt`), and clinic clock details such as a time zone and event timing anchors. When a `Timing.repeat.count` cap exists and prior occurrences have already been administered, pass `priorCount` to indicate how many doses were consumed before the `from` timestamp so remaining administrations are calculated correctly without re-traversing the timeline.
|
|
132
232
|
|
|
133
233
|
```ts
|
|
134
234
|
import { EventTiming, nextDueDoses, parseSig } from "ezmedicationinput";
|
package/dist/fhir.js
CHANGED
|
@@ -9,7 +9,7 @@ const object_1 = require("./utils/object");
|
|
|
9
9
|
const array_1 = require("./utils/array");
|
|
10
10
|
const SNOMED_SYSTEM = "http://snomed.info/sct";
|
|
11
11
|
function toFhir(internal) {
|
|
12
|
-
var _a, _b;
|
|
12
|
+
var _a, _b, _c, _d, _e;
|
|
13
13
|
const dosage = {};
|
|
14
14
|
const repeat = {};
|
|
15
15
|
let hasRepeat = false;
|
|
@@ -100,8 +100,20 @@ function toFhir(internal) {
|
|
|
100
100
|
dosage.route = { text };
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
|
-
if (internal.siteText) {
|
|
104
|
-
|
|
103
|
+
if (internal.siteText || ((_c = internal.siteCoding) === null || _c === void 0 ? void 0 : _c.code)) {
|
|
104
|
+
const coding = ((_d = internal.siteCoding) === null || _d === void 0 ? void 0 : _d.code)
|
|
105
|
+
? [
|
|
106
|
+
{
|
|
107
|
+
system: (_e = internal.siteCoding.system) !== null && _e !== void 0 ? _e : SNOMED_SYSTEM,
|
|
108
|
+
code: internal.siteCoding.code,
|
|
109
|
+
display: internal.siteCoding.display
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
: undefined;
|
|
113
|
+
dosage.site = {
|
|
114
|
+
text: internal.siteText,
|
|
115
|
+
coding
|
|
116
|
+
};
|
|
105
117
|
}
|
|
106
118
|
if (internal.asNeeded) {
|
|
107
119
|
dosage.asNeededBoolean = true;
|
|
@@ -116,7 +128,7 @@ function toFhir(internal) {
|
|
|
116
128
|
return dosage;
|
|
117
129
|
}
|
|
118
130
|
function internalFromFhir(dosage) {
|
|
119
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5;
|
|
131
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7;
|
|
120
132
|
const internal = {
|
|
121
133
|
input: (_a = dosage.text) !== null && _a !== void 0 ? _a : "",
|
|
122
134
|
tokens: [],
|
|
@@ -139,7 +151,8 @@ function internalFromFhir(dosage) {
|
|
|
139
151
|
siteText: (_y = dosage.site) === null || _y === void 0 ? void 0 : _y.text,
|
|
140
152
|
asNeeded: dosage.asNeededBoolean,
|
|
141
153
|
asNeededReason: (_0 = (_z = dosage.asNeededFor) === null || _z === void 0 ? void 0 : _z[0]) === null || _0 === void 0 ? void 0 : _0.text,
|
|
142
|
-
siteTokenIndices: new Set()
|
|
154
|
+
siteTokenIndices: new Set(),
|
|
155
|
+
siteLookups: []
|
|
143
156
|
};
|
|
144
157
|
const routeCoding = (_2 = (_1 = dosage.route) === null || _1 === void 0 ? void 0 : _1.coding) === null || _2 === void 0 ? void 0 : _2.find((code) => code.system === SNOMED_SYSTEM);
|
|
145
158
|
if (routeCoding === null || routeCoding === void 0 ? void 0 : routeCoding.code) {
|
|
@@ -150,13 +163,21 @@ function internalFromFhir(dosage) {
|
|
|
150
163
|
internal.routeText = maps_1.ROUTE_TEXT[mapped];
|
|
151
164
|
}
|
|
152
165
|
}
|
|
153
|
-
const
|
|
166
|
+
const siteCoding = (_4 = (_3 = dosage.site) === null || _3 === void 0 ? void 0 : _3.coding) === null || _4 === void 0 ? void 0 : _4.find((code) => code.system === SNOMED_SYSTEM);
|
|
167
|
+
if (siteCoding === null || siteCoding === void 0 ? void 0 : siteCoding.code) {
|
|
168
|
+
internal.siteCoding = {
|
|
169
|
+
code: siteCoding.code,
|
|
170
|
+
display: siteCoding.display,
|
|
171
|
+
system: siteCoding.system
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const doseAndRate = (_5 = dosage.doseAndRate) === null || _5 === void 0 ? void 0 : _5[0];
|
|
154
175
|
if (doseAndRate === null || doseAndRate === void 0 ? void 0 : doseAndRate.doseRange) {
|
|
155
176
|
const { low, high } = doseAndRate.doseRange;
|
|
156
177
|
if ((low === null || low === void 0 ? void 0 : low.value) !== undefined && (high === null || high === void 0 ? void 0 : high.value) !== undefined) {
|
|
157
178
|
internal.doseRange = { low: low.value, high: high.value };
|
|
158
179
|
}
|
|
159
|
-
internal.unit = (
|
|
180
|
+
internal.unit = (_7 = (_6 = low === null || low === void 0 ? void 0 : low.unit) !== null && _6 !== void 0 ? _6 : high === null || high === void 0 ? void 0 : high.unit) !== null && _7 !== void 0 ? _7 : internal.unit;
|
|
160
181
|
}
|
|
161
182
|
else if (doseAndRate === null || doseAndRate === void 0 ? void 0 : doseAndRate.doseQuantity) {
|
|
162
183
|
const dose = doseAndRate.doseQuantity;
|
package/dist/i18n.js
CHANGED
|
@@ -126,39 +126,103 @@ const DAY_NAMES_THAI = {
|
|
|
126
126
|
sun: "วันอาทิตย์"
|
|
127
127
|
};
|
|
128
128
|
exports.THAI_SITE_TRANSLATIONS = {
|
|
129
|
+
eye: "ตา",
|
|
130
|
+
eyes: "ตา",
|
|
129
131
|
"right eye": "ตาขวา",
|
|
130
132
|
"left eye": "ตาซ้าย",
|
|
131
133
|
"both eyes": "ตาทั้งสองข้าง",
|
|
134
|
+
"bilateral eyes": "ตาทั้งสองข้าง",
|
|
132
135
|
"right ear": "หูขวา",
|
|
133
136
|
"left ear": "หูซ้าย",
|
|
134
137
|
"both ears": "หูทั้งสองข้าง",
|
|
138
|
+
"bilateral ears": "หูทั้งสองข้าง",
|
|
135
139
|
ear: "หู",
|
|
136
140
|
ears: "หูทั้งสองข้าง",
|
|
141
|
+
nostril: "รูจมูก",
|
|
142
|
+
nostrils: "รูจมูกทั้งสองข้าง",
|
|
137
143
|
"right nostril": "รูจมูกขวา",
|
|
138
144
|
"left nostril": "รูจมูกซ้าย",
|
|
139
145
|
"both nostrils": "รูจมูกทั้งสองข้าง",
|
|
146
|
+
"left naris": "รูจมูกซ้าย",
|
|
147
|
+
"right naris": "รูจมูกขวา",
|
|
148
|
+
nares: "รูจมูกทั้งสองข้าง",
|
|
149
|
+
"anterior nares": "รูจมูกด้านหน้า",
|
|
150
|
+
nose: "จมูก",
|
|
151
|
+
mouth: "ปาก",
|
|
152
|
+
tongue: "ลิ้น",
|
|
153
|
+
tongues: "ลิ้น",
|
|
140
154
|
"right arm": "แขนขวา",
|
|
141
155
|
"left arm": "แขนซ้าย",
|
|
142
156
|
"both arms": "แขนทั้งสองข้าง",
|
|
157
|
+
arm: "แขน",
|
|
158
|
+
"upper arm": "ต้นแขน",
|
|
159
|
+
"left upper arm": "ต้นแขนซ้าย",
|
|
160
|
+
"right upper arm": "ต้นแขนขวา",
|
|
161
|
+
"bilateral arms": "แขนทั้งสองข้าง",
|
|
143
162
|
"right leg": "ขาขวา",
|
|
144
163
|
"left leg": "ขาซ้าย",
|
|
145
164
|
"both legs": "ขาทั้งสองข้าง",
|
|
165
|
+
leg: "ขา",
|
|
166
|
+
"lower leg": "ขาส่วนล่าง",
|
|
167
|
+
"left lower leg": "ขาส่วนล่างซ้าย",
|
|
168
|
+
"right lower leg": "ขาส่วนล่างขวา",
|
|
169
|
+
"bilateral legs": "ขาทั้งสองข้าง",
|
|
146
170
|
"right hand": "มือขวา",
|
|
147
171
|
"left hand": "มือซ้าย",
|
|
148
172
|
"both hands": "มือทั้งสองข้าง",
|
|
173
|
+
hand: "มือ",
|
|
174
|
+
hands: "มือทั้งสองข้าง",
|
|
149
175
|
"right foot": "เท้าขวา",
|
|
150
176
|
"left foot": "เท้าซ้าย",
|
|
151
177
|
"both feet": "เท้าทั้งสองข้าง",
|
|
178
|
+
foot: "เท้า",
|
|
179
|
+
feet: "เท้า",
|
|
152
180
|
abdomen: "ช่องท้อง",
|
|
181
|
+
abdominal: "ช่องท้อง",
|
|
153
182
|
belly: "ท้อง",
|
|
154
183
|
back: "แผ่นหลัง",
|
|
184
|
+
scalp: "หนังศีรษะ",
|
|
185
|
+
face: "ใบหน้า",
|
|
155
186
|
cheek: "แก้ม",
|
|
156
187
|
cheeks: "แก้มทั้งสองข้าง",
|
|
188
|
+
forehead: "หน้าผาก",
|
|
189
|
+
chin: "คาง",
|
|
190
|
+
neck: "คอ",
|
|
157
191
|
forearm: "ปลายแขน",
|
|
192
|
+
"left forearm": "ปลายแขนซ้าย",
|
|
193
|
+
"right forearm": "ปลายแขนขวา",
|
|
158
194
|
shoulder: "ไหล่",
|
|
159
195
|
shoulders: "ไหล่ทั้งสองข้าง",
|
|
160
196
|
thigh: "ต้นขา",
|
|
161
|
-
thighs: "ต้นขาทั้งสองข้าง"
|
|
197
|
+
thighs: "ต้นขาทั้งสองข้าง",
|
|
198
|
+
"left thigh": "ต้นขาซ้าย",
|
|
199
|
+
"right thigh": "ต้นขาขวา",
|
|
200
|
+
gum: "เหงือก",
|
|
201
|
+
gums: "เหงือก",
|
|
202
|
+
tooth: "ฟัน",
|
|
203
|
+
teeth: "ฟัน",
|
|
204
|
+
buttock: "สะโพก",
|
|
205
|
+
buttocks: "สะโพกทั้งสองข้าง",
|
|
206
|
+
gluteal: "สะโพก",
|
|
207
|
+
glute: "สะโพก",
|
|
208
|
+
"left buttock": "สะโพกซ้าย",
|
|
209
|
+
"left gluteal": "สะโพกซ้าย",
|
|
210
|
+
"right buttock": "สะโพกขวา",
|
|
211
|
+
"right gluteal": "สะโพกขวา",
|
|
212
|
+
muscle: "กล้ามเนื้อ",
|
|
213
|
+
muscles: "กล้ามเนื้อทั้งหมด",
|
|
214
|
+
vein: "หลอดเลือดดำ",
|
|
215
|
+
veins: "หลอดเลือดดำทั้งหมด",
|
|
216
|
+
vagina: "ช่องคลอด",
|
|
217
|
+
vaginal: "บริเวณช่องคลอด",
|
|
218
|
+
penis: "อวัยวะเพศชาย",
|
|
219
|
+
penile: "บริเวณอวัยวะเพศชาย",
|
|
220
|
+
rectum: "ไส้ตรง",
|
|
221
|
+
rectal: "บริเวณทวารหนัก",
|
|
222
|
+
anus: "ทวารหนัก",
|
|
223
|
+
perineum: "ฝีเย็บ",
|
|
224
|
+
skin: "ผิวหนัง",
|
|
225
|
+
hair: "เส้นผม"
|
|
162
226
|
};
|
|
163
227
|
const DEFAULT_THAI_ROUTE_GRAMMAR = { verb: "ใช้" };
|
|
164
228
|
const THAI_ROUTE_GRAMMAR = {
|
package/dist/index.d.ts
CHANGED
|
@@ -6,5 +6,6 @@ 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 parseSigAsync(input: string, options?: ParseOptions): Promise<ParseResult>;
|
|
9
10
|
export declare function formatSig(dosage: FhirDosage, style?: "short" | "long", options?: FormatOptions): string;
|
|
10
11
|
export declare function fromFhirDosage(dosage: FhirDosage, options?: FormatOptions): ParseResult;
|
package/dist/index.js
CHANGED
|
@@ -13,9 +13,19 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
|
|
|
13
13
|
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
17
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
18
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
19
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
20
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
21
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
22
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
23
|
+
});
|
|
24
|
+
};
|
|
16
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
26
|
exports.resolveSigTranslation = exports.resolveSigLocalization = exports.registerSigLocalization = exports.getRegisteredSigLocalizations = exports.nextDueDoses = exports.suggestSig = exports.parseInternal = void 0;
|
|
18
27
|
exports.parseSig = parseSig;
|
|
28
|
+
exports.parseSigAsync = parseSigAsync;
|
|
19
29
|
exports.formatSig = formatSig;
|
|
20
30
|
exports.fromFhirDosage = fromFhirDosage;
|
|
21
31
|
const format_1 = require("./format");
|
|
@@ -36,6 +46,56 @@ Object.defineProperty(exports, "resolveSigLocalization", { enumerable: true, get
|
|
|
36
46
|
Object.defineProperty(exports, "resolveSigTranslation", { enumerable: true, get: function () { return i18n_2.resolveSigTranslation; } });
|
|
37
47
|
function parseSig(input, options) {
|
|
38
48
|
const internal = (0, parser_1.parseInternal)(input, options);
|
|
49
|
+
(0, parser_1.applySiteCoding)(internal, options);
|
|
50
|
+
return buildParseResult(internal, options);
|
|
51
|
+
}
|
|
52
|
+
function parseSigAsync(input, options) {
|
|
53
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
54
|
+
const internal = (0, parser_1.parseInternal)(input, options);
|
|
55
|
+
yield (0, parser_1.applySiteCodingAsync)(internal, options);
|
|
56
|
+
return buildParseResult(internal, options);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function formatSig(dosage, style = "short", options) {
|
|
60
|
+
const internal = (0, fhir_1.internalFromFhir)(dosage);
|
|
61
|
+
const localization = (0, i18n_1.resolveSigLocalization)(options === null || options === void 0 ? void 0 : options.locale, options === null || options === void 0 ? void 0 : options.i18n);
|
|
62
|
+
return (0, format_1.formatInternal)(internal, style, localization);
|
|
63
|
+
}
|
|
64
|
+
function fromFhirDosage(dosage, options) {
|
|
65
|
+
var _a, _b, _c;
|
|
66
|
+
const internal = (0, fhir_1.internalFromFhir)(dosage);
|
|
67
|
+
const localization = (0, i18n_1.resolveSigLocalization)(options === null || options === void 0 ? void 0 : options.locale, options === null || options === void 0 ? void 0 : options.i18n);
|
|
68
|
+
const shortText = (0, format_1.formatInternal)(internal, "short", localization);
|
|
69
|
+
const computedLong = (0, format_1.formatInternal)(internal, "long", localization);
|
|
70
|
+
const longText = localization ? computedLong : (_a = dosage.text) !== null && _a !== void 0 ? _a : computedLong;
|
|
71
|
+
return {
|
|
72
|
+
fhir: dosage,
|
|
73
|
+
shortText,
|
|
74
|
+
longText,
|
|
75
|
+
warnings: [],
|
|
76
|
+
meta: {
|
|
77
|
+
consumedTokens: [],
|
|
78
|
+
normalized: {
|
|
79
|
+
route: internal.routeCode,
|
|
80
|
+
unit: internal.unit,
|
|
81
|
+
site: internal.siteText || ((_b = internal.siteCoding) === null || _b === void 0 ? void 0 : _b.code)
|
|
82
|
+
? {
|
|
83
|
+
text: internal.siteText,
|
|
84
|
+
coding: ((_c = internal.siteCoding) === null || _c === void 0 ? void 0 : _c.code)
|
|
85
|
+
? {
|
|
86
|
+
code: internal.siteCoding.code,
|
|
87
|
+
display: internal.siteCoding.display,
|
|
88
|
+
system: internal.siteCoding.system
|
|
89
|
+
}
|
|
90
|
+
: undefined
|
|
91
|
+
}
|
|
92
|
+
: undefined
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function buildParseResult(internal, options) {
|
|
98
|
+
var _a;
|
|
39
99
|
const localization = (0, i18n_1.resolveSigLocalization)(options === null || options === void 0 ? void 0 : options.locale, options === null || options === void 0 ? void 0 : options.i18n);
|
|
40
100
|
const shortText = (0, format_1.formatInternal)(internal, "short", localization);
|
|
41
101
|
const longText = (0, format_1.formatInternal)(internal, "long", localization);
|
|
@@ -47,6 +107,26 @@ function parseSig(input, options) {
|
|
|
47
107
|
.filter((token) => internal.consumed.has(token.index))
|
|
48
108
|
.map((token) => token.original);
|
|
49
109
|
const leftoverTokens = internal.tokens.filter((token) => !internal.consumed.has(token.index));
|
|
110
|
+
const siteCoding = ((_a = internal.siteCoding) === null || _a === void 0 ? void 0 : _a.code)
|
|
111
|
+
? {
|
|
112
|
+
code: internal.siteCoding.code,
|
|
113
|
+
display: internal.siteCoding.display,
|
|
114
|
+
system: internal.siteCoding.system
|
|
115
|
+
}
|
|
116
|
+
: undefined;
|
|
117
|
+
const siteLookups = internal.siteLookups.length
|
|
118
|
+
? internal.siteLookups.map((entry) => ({
|
|
119
|
+
request: entry.request,
|
|
120
|
+
suggestions: entry.suggestions.map((suggestion) => ({
|
|
121
|
+
coding: {
|
|
122
|
+
code: suggestion.coding.code,
|
|
123
|
+
display: suggestion.coding.display,
|
|
124
|
+
system: suggestion.coding.system
|
|
125
|
+
},
|
|
126
|
+
text: suggestion.text
|
|
127
|
+
}))
|
|
128
|
+
}))
|
|
129
|
+
: undefined;
|
|
50
130
|
return {
|
|
51
131
|
fhir,
|
|
52
132
|
shortText,
|
|
@@ -59,34 +139,15 @@ function parseSig(input, options) {
|
|
|
59
139
|
: undefined,
|
|
60
140
|
normalized: {
|
|
61
141
|
route: internal.routeCode,
|
|
62
|
-
unit: internal.unit
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
function fromFhirDosage(dosage, options) {
|
|
73
|
-
var _a;
|
|
74
|
-
const internal = (0, fhir_1.internalFromFhir)(dosage);
|
|
75
|
-
const localization = (0, i18n_1.resolveSigLocalization)(options === null || options === void 0 ? void 0 : options.locale, options === null || options === void 0 ? void 0 : options.i18n);
|
|
76
|
-
const shortText = (0, format_1.formatInternal)(internal, "short", localization);
|
|
77
|
-
const computedLong = (0, format_1.formatInternal)(internal, "long", localization);
|
|
78
|
-
const longText = localization ? computedLong : (_a = dosage.text) !== null && _a !== void 0 ? _a : computedLong;
|
|
79
|
-
return {
|
|
80
|
-
fhir: dosage,
|
|
81
|
-
shortText,
|
|
82
|
-
longText,
|
|
83
|
-
warnings: [],
|
|
84
|
-
meta: {
|
|
85
|
-
consumedTokens: [],
|
|
86
|
-
normalized: {
|
|
87
|
-
route: internal.routeCode,
|
|
88
|
-
unit: internal.unit
|
|
89
|
-
}
|
|
142
|
+
unit: internal.unit,
|
|
143
|
+
site: internal.siteText || siteCoding
|
|
144
|
+
? {
|
|
145
|
+
text: internal.siteText,
|
|
146
|
+
coding: siteCoding
|
|
147
|
+
}
|
|
148
|
+
: undefined
|
|
149
|
+
},
|
|
150
|
+
siteLookups
|
|
90
151
|
}
|
|
91
152
|
};
|
|
92
153
|
}
|
package/dist/internal-types.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import { EventTiming, FhirDayOfWeek, FhirPeriodUnit, RouteCode } from "./types";
|
|
1
|
+
import { EventTiming, FhirCoding, FhirDayOfWeek, FhirPeriodUnit, RouteCode, SiteCodeLookupRequest, SiteCodeSuggestion } from "./types";
|
|
2
|
+
export interface SiteLookupDetail {
|
|
3
|
+
request: SiteCodeLookupRequest;
|
|
4
|
+
suggestions: SiteCodeSuggestion[];
|
|
5
|
+
}
|
|
2
6
|
export interface Token {
|
|
3
7
|
original: string;
|
|
4
8
|
lower: string;
|
|
@@ -31,4 +35,7 @@ export interface ParsedSigInternal {
|
|
|
31
35
|
siteText?: string;
|
|
32
36
|
siteSource?: "abbreviation" | "text";
|
|
33
37
|
siteTokenIndices: Set<number>;
|
|
38
|
+
siteCoding?: FhirCoding;
|
|
39
|
+
siteLookupRequest?: SiteCodeLookupRequest;
|
|
40
|
+
siteLookups: SiteLookupDetail[];
|
|
34
41
|
}
|
package/dist/maps.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EventTiming, FhirDayOfWeek, FhirPeriodUnit, RouteCode, SNOMEDCTRouteCodes } from "./types";
|
|
1
|
+
import { BodySiteDefinition, EventTiming, FhirDayOfWeek, FhirPeriodUnit, RouteCode, SNOMEDCTRouteCodes } from "./types";
|
|
2
2
|
/**
|
|
3
3
|
* SNOMED CT codings aligned with every known RouteCode. Keeping the structure
|
|
4
4
|
* data-driven ensures any additions to the enumeration are surfaced
|
|
@@ -19,6 +19,13 @@ export interface RouteSynonym {
|
|
|
19
19
|
text: string;
|
|
20
20
|
}
|
|
21
21
|
export declare const DEFAULT_ROUTE_SYNONYMS: Record<string, RouteSynonym>;
|
|
22
|
+
/**
|
|
23
|
+
* Normalizes body-site phrases into lookup keys by trimming, lower-casing, and
|
|
24
|
+
* collapsing whitespace. Custom site maps should normalize their keys with the
|
|
25
|
+
* same logic to ensure consistent lookups.
|
|
26
|
+
*/
|
|
27
|
+
export declare function normalizeBodySiteKey(value: string): string;
|
|
28
|
+
export declare const DEFAULT_BODY_SITE_SNOMED: Record<string, BodySiteDefinition>;
|
|
22
29
|
export declare const HOUSEHOLD_VOLUME_UNITS: readonly ["tsp", "tbsp"];
|
|
23
30
|
export declare const DEFAULT_UNIT_SYNONYMS: Record<string, string>;
|
|
24
31
|
export interface FrequencyDescriptor {
|