ezmedicationinput 0.1.1 → 0.1.3
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 +60 -20
- package/dist/context.js +10 -5
- package/dist/fhir.d.ts +1 -1
- package/dist/fhir.js +40 -32
- package/dist/format.d.ts +3 -2
- package/dist/format.js +371 -86
- package/dist/i18n.d.ts +31 -0
- package/dist/i18n.js +664 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +53 -19
- package/dist/internal-types.d.ts +33 -0
- package/dist/internal-types.js +2 -0
- package/dist/maps.d.ts +2 -0
- package/dist/maps.js +543 -350
- package/dist/package.json +3 -0
- package/dist/parser.d.ts +2 -31
- package/dist/parser.js +809 -142
- package/dist/safety.js +7 -4
- package/dist/schedule.js +148 -76
- package/dist/suggest.js +391 -79
- package/dist/types.d.ts +26 -8
- package/dist/types.js +12 -9
- package/dist/utils/array.d.ts +1 -0
- package/dist/utils/array.js +11 -0
- package/dist/utils/enum.d.ts +2 -0
- package/dist/utils/enum.js +7 -0
- package/dist/utils/object.d.ts +7 -0
- package/dist/utils/object.js +34 -0
- package/package.json +2 -2
package/dist/format.js
CHANGED
|
@@ -1,48 +1,51 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatInternal = formatInternal;
|
|
4
|
+
const types_1 = require("./types");
|
|
2
5
|
const ROUTE_SHORT = {
|
|
3
|
-
[RouteCode["Oral route"]]: "PO",
|
|
4
|
-
[RouteCode["Sublingual route"]]: "SL",
|
|
5
|
-
[RouteCode["Buccal route"]]: "BUC",
|
|
6
|
-
[RouteCode["Respiratory tract route (qualifier value)"]]: "INH",
|
|
7
|
-
[RouteCode["Nasal route"]]: "IN",
|
|
8
|
-
[RouteCode["Topical route"]]: "TOP",
|
|
9
|
-
[RouteCode["Transdermal route"]]: "TD",
|
|
10
|
-
[RouteCode["Subcutaneous route"]]: "SC",
|
|
11
|
-
[RouteCode["Intramuscular route"]]: "IM",
|
|
12
|
-
[RouteCode["Intravenous route"]]: "IV",
|
|
13
|
-
[RouteCode["Per rectum"]]: "PR",
|
|
14
|
-
[RouteCode["Per vagina"]]: "PV",
|
|
15
|
-
[RouteCode["Ophthalmic route"]]: "OPH",
|
|
16
|
-
[RouteCode["Intravitreal route (qualifier value)"]]: "IVT"
|
|
6
|
+
[types_1.RouteCode["Oral route"]]: "PO",
|
|
7
|
+
[types_1.RouteCode["Sublingual route"]]: "SL",
|
|
8
|
+
[types_1.RouteCode["Buccal route"]]: "BUC",
|
|
9
|
+
[types_1.RouteCode["Respiratory tract route (qualifier value)"]]: "INH",
|
|
10
|
+
[types_1.RouteCode["Nasal route"]]: "IN",
|
|
11
|
+
[types_1.RouteCode["Topical route"]]: "TOP",
|
|
12
|
+
[types_1.RouteCode["Transdermal route"]]: "TD",
|
|
13
|
+
[types_1.RouteCode["Subcutaneous route"]]: "SC",
|
|
14
|
+
[types_1.RouteCode["Intramuscular route"]]: "IM",
|
|
15
|
+
[types_1.RouteCode["Intravenous route"]]: "IV",
|
|
16
|
+
[types_1.RouteCode["Per rectum"]]: "PR",
|
|
17
|
+
[types_1.RouteCode["Per vagina"]]: "PV",
|
|
18
|
+
[types_1.RouteCode["Ophthalmic route"]]: "OPH",
|
|
19
|
+
[types_1.RouteCode["Intravitreal route (qualifier value)"]]: "IVT"
|
|
17
20
|
};
|
|
18
21
|
const WHEN_TEXT = {
|
|
19
|
-
[EventTiming["Before Sleep"]]: "at bedtime",
|
|
20
|
-
[EventTiming["Before Meal"]]: "before meals",
|
|
21
|
-
[EventTiming["Before Breakfast"]]: "before breakfast",
|
|
22
|
-
[EventTiming["Before Lunch"]]: "before lunch",
|
|
23
|
-
[EventTiming["Before Dinner"]]: "before dinner",
|
|
24
|
-
[EventTiming["After Meal"]]: "after meals",
|
|
25
|
-
[EventTiming["After Breakfast"]]: "after breakfast",
|
|
26
|
-
[EventTiming["After Lunch"]]: "after lunch",
|
|
27
|
-
[EventTiming["After Dinner"]]: "after dinner",
|
|
28
|
-
[EventTiming.Meal]: "with meals",
|
|
29
|
-
[EventTiming.Breakfast]: "with
|
|
30
|
-
[EventTiming.Lunch]: "with lunch",
|
|
31
|
-
[EventTiming.Dinner]: "with
|
|
32
|
-
[EventTiming.Morning]: "in the morning",
|
|
33
|
-
[EventTiming["Early Morning"]]: "in the early morning",
|
|
34
|
-
[EventTiming["Late Morning"]]: "in the late morning",
|
|
35
|
-
[EventTiming.Noon]: "at noon",
|
|
36
|
-
[EventTiming.Afternoon]: "in the afternoon",
|
|
37
|
-
[EventTiming["Early Afternoon"]]: "in the early afternoon",
|
|
38
|
-
[EventTiming["Late Afternoon"]]: "in the late afternoon",
|
|
39
|
-
[EventTiming.Evening]: "in the evening",
|
|
40
|
-
[EventTiming["Early Evening"]]: "in the early evening",
|
|
41
|
-
[EventTiming["Late Evening"]]: "in the late evening",
|
|
42
|
-
[EventTiming.Night]: "at night",
|
|
43
|
-
[EventTiming.Wake]: "after waking",
|
|
44
|
-
[EventTiming["After Sleep"]]: "after sleep",
|
|
45
|
-
[EventTiming.Immediate]: "immediately"
|
|
22
|
+
[types_1.EventTiming["Before Sleep"]]: "at bedtime",
|
|
23
|
+
[types_1.EventTiming["Before Meal"]]: "before meals",
|
|
24
|
+
[types_1.EventTiming["Before Breakfast"]]: "before breakfast",
|
|
25
|
+
[types_1.EventTiming["Before Lunch"]]: "before lunch",
|
|
26
|
+
[types_1.EventTiming["Before Dinner"]]: "before dinner",
|
|
27
|
+
[types_1.EventTiming["After Meal"]]: "after meals",
|
|
28
|
+
[types_1.EventTiming["After Breakfast"]]: "after breakfast",
|
|
29
|
+
[types_1.EventTiming["After Lunch"]]: "after lunch",
|
|
30
|
+
[types_1.EventTiming["After Dinner"]]: "after dinner",
|
|
31
|
+
[types_1.EventTiming.Meal]: "with meals",
|
|
32
|
+
[types_1.EventTiming.Breakfast]: "with breakfast",
|
|
33
|
+
[types_1.EventTiming.Lunch]: "with lunch",
|
|
34
|
+
[types_1.EventTiming.Dinner]: "with dinner",
|
|
35
|
+
[types_1.EventTiming.Morning]: "in the morning",
|
|
36
|
+
[types_1.EventTiming["Early Morning"]]: "in the early morning",
|
|
37
|
+
[types_1.EventTiming["Late Morning"]]: "in the late morning",
|
|
38
|
+
[types_1.EventTiming.Noon]: "at noon",
|
|
39
|
+
[types_1.EventTiming.Afternoon]: "in the afternoon",
|
|
40
|
+
[types_1.EventTiming["Early Afternoon"]]: "in the early afternoon",
|
|
41
|
+
[types_1.EventTiming["Late Afternoon"]]: "in the late afternoon",
|
|
42
|
+
[types_1.EventTiming.Evening]: "in the evening",
|
|
43
|
+
[types_1.EventTiming["Early Evening"]]: "in the early evening",
|
|
44
|
+
[types_1.EventTiming["Late Evening"]]: "in the late evening",
|
|
45
|
+
[types_1.EventTiming.Night]: "at night",
|
|
46
|
+
[types_1.EventTiming.Wake]: "after waking",
|
|
47
|
+
[types_1.EventTiming["After Sleep"]]: "after sleep",
|
|
48
|
+
[types_1.EventTiming.Immediate]: "immediately"
|
|
46
49
|
};
|
|
47
50
|
const DAY_NAMES = {
|
|
48
51
|
mon: "Monday",
|
|
@@ -53,6 +56,102 @@ const DAY_NAMES = {
|
|
|
53
56
|
sat: "Saturday",
|
|
54
57
|
sun: "Sunday"
|
|
55
58
|
};
|
|
59
|
+
const DEFAULT_ROUTE_GRAMMAR = { verb: "Use" };
|
|
60
|
+
const ROUTE_GRAMMAR = {
|
|
61
|
+
[types_1.RouteCode["Oral route"]]: { verb: "Take", routePhrase: "by mouth" },
|
|
62
|
+
[types_1.RouteCode["Ophthalmic route"]]: {
|
|
63
|
+
verb: "Instill",
|
|
64
|
+
routePhrase: ({ hasSite }) => (hasSite ? undefined : "in the eye"),
|
|
65
|
+
sitePreposition: "in"
|
|
66
|
+
},
|
|
67
|
+
[types_1.RouteCode["Intravitreal route (qualifier value)"]]: {
|
|
68
|
+
verb: "Inject",
|
|
69
|
+
routePhrase: ({ hasSite }) => (hasSite ? undefined : "into the eye"),
|
|
70
|
+
sitePreposition: "into"
|
|
71
|
+
},
|
|
72
|
+
[types_1.RouteCode["Topical route"]]: {
|
|
73
|
+
verb: "Apply",
|
|
74
|
+
routePhrase: ({ hasSite }) => (hasSite ? undefined : "topically"),
|
|
75
|
+
sitePreposition: "to"
|
|
76
|
+
},
|
|
77
|
+
[types_1.RouteCode["Transdermal route"]]: {
|
|
78
|
+
verb: "Apply",
|
|
79
|
+
routePhrase: ({ hasSite }) => (hasSite ? undefined : "transdermally"),
|
|
80
|
+
sitePreposition: "to"
|
|
81
|
+
},
|
|
82
|
+
[types_1.RouteCode["Subcutaneous route"]]: {
|
|
83
|
+
verb: "Inject",
|
|
84
|
+
routePhrase: ({ hasSite }) => (hasSite ? undefined : "subcutaneously"),
|
|
85
|
+
sitePreposition: "into"
|
|
86
|
+
},
|
|
87
|
+
[types_1.RouteCode["Intramuscular route"]]: {
|
|
88
|
+
verb: "Inject",
|
|
89
|
+
routePhrase: ({ hasSite }) => (hasSite ? undefined : "intramuscularly"),
|
|
90
|
+
sitePreposition: "into"
|
|
91
|
+
},
|
|
92
|
+
[types_1.RouteCode["Intravenous route"]]: {
|
|
93
|
+
verb: "Inject",
|
|
94
|
+
routePhrase: ({ hasSite }) => (hasSite ? undefined : "intravenously"),
|
|
95
|
+
sitePreposition: "into"
|
|
96
|
+
},
|
|
97
|
+
[types_1.RouteCode["Nasal route"]]: {
|
|
98
|
+
verb: "Use",
|
|
99
|
+
routePhrase: ({ hasSite }) => (hasSite ? undefined : "via nasal route"),
|
|
100
|
+
sitePreposition: "into"
|
|
101
|
+
},
|
|
102
|
+
[types_1.RouteCode["Respiratory tract route (qualifier value)"]]: {
|
|
103
|
+
verb: "Use",
|
|
104
|
+
routePhrase: ({ hasSite }) => (hasSite ? undefined : "via inhalation"),
|
|
105
|
+
sitePreposition: "into"
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
function grammarFromRouteText(text) {
|
|
109
|
+
if (!text) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
const normalized = text.trim().toLowerCase();
|
|
113
|
+
if (!normalized) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
if (normalized.includes("mouth") || normalized.includes("oral")) {
|
|
117
|
+
return ROUTE_GRAMMAR[types_1.RouteCode["Oral route"]];
|
|
118
|
+
}
|
|
119
|
+
if (normalized.includes("ophthalm")) {
|
|
120
|
+
return ROUTE_GRAMMAR[types_1.RouteCode["Ophthalmic route"]];
|
|
121
|
+
}
|
|
122
|
+
if (normalized.includes("intravitreal")) {
|
|
123
|
+
return ROUTE_GRAMMAR[types_1.RouteCode["Intravitreal route (qualifier value)"]];
|
|
124
|
+
}
|
|
125
|
+
if (normalized.includes("topical")) {
|
|
126
|
+
return ROUTE_GRAMMAR[types_1.RouteCode["Topical route"]];
|
|
127
|
+
}
|
|
128
|
+
if (normalized.includes("transdermal")) {
|
|
129
|
+
return ROUTE_GRAMMAR[types_1.RouteCode["Transdermal route"]];
|
|
130
|
+
}
|
|
131
|
+
if (normalized.includes("subcutaneous") || normalized === "sc" || normalized === "sq") {
|
|
132
|
+
return ROUTE_GRAMMAR[types_1.RouteCode["Subcutaneous route"]];
|
|
133
|
+
}
|
|
134
|
+
if (normalized.includes("intramuscular") || normalized === "im") {
|
|
135
|
+
return ROUTE_GRAMMAR[types_1.RouteCode["Intramuscular route"]];
|
|
136
|
+
}
|
|
137
|
+
if (normalized.includes("intravenous") || normalized === "iv") {
|
|
138
|
+
return ROUTE_GRAMMAR[types_1.RouteCode["Intravenous route"]];
|
|
139
|
+
}
|
|
140
|
+
if (normalized.includes("nasal")) {
|
|
141
|
+
return ROUTE_GRAMMAR[types_1.RouteCode["Nasal route"]];
|
|
142
|
+
}
|
|
143
|
+
if (normalized.includes("inhal")) {
|
|
144
|
+
return ROUTE_GRAMMAR[types_1.RouteCode["Respiratory tract route (qualifier value)"]];
|
|
145
|
+
}
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
function resolveRouteGrammar(internal) {
|
|
149
|
+
var _a, _b;
|
|
150
|
+
if (internal.routeCode && ROUTE_GRAMMAR[internal.routeCode]) {
|
|
151
|
+
return (_a = ROUTE_GRAMMAR[internal.routeCode]) !== null && _a !== void 0 ? _a : DEFAULT_ROUTE_GRAMMAR;
|
|
152
|
+
}
|
|
153
|
+
return (_b = grammarFromRouteText(internal.routeText)) !== null && _b !== void 0 ? _b : DEFAULT_ROUTE_GRAMMAR;
|
|
154
|
+
}
|
|
56
155
|
function pluralize(unit, value) {
|
|
57
156
|
if (Math.abs(value) === 1) {
|
|
58
157
|
if (unit === "tab")
|
|
@@ -83,7 +182,7 @@ function describeFrequency(internal) {
|
|
|
83
182
|
const { frequency, frequencyMax, period, periodMax, periodUnit, timingCode } = internal;
|
|
84
183
|
if (frequency !== undefined &&
|
|
85
184
|
frequencyMax !== undefined &&
|
|
86
|
-
periodUnit === FhirPeriodUnit.Day &&
|
|
185
|
+
periodUnit === types_1.FhirPeriodUnit.Day &&
|
|
87
186
|
(!period || period === 1)) {
|
|
88
187
|
if (frequency === 1 && frequencyMax === 1) {
|
|
89
188
|
return "once daily";
|
|
@@ -93,7 +192,7 @@ function describeFrequency(internal) {
|
|
|
93
192
|
}
|
|
94
193
|
return `${stripTrailingZero(frequency)} to ${stripTrailingZero(frequencyMax)} times daily`;
|
|
95
194
|
}
|
|
96
|
-
if (frequency && periodUnit === FhirPeriodUnit.Day && (!period || period === 1)) {
|
|
195
|
+
if (frequency && periodUnit === types_1.FhirPeriodUnit.Day && (!period || period === 1)) {
|
|
97
196
|
if (frequency === 1)
|
|
98
197
|
return "once daily";
|
|
99
198
|
if (frequency === 2)
|
|
@@ -104,13 +203,13 @@ function describeFrequency(internal) {
|
|
|
104
203
|
return "four times daily";
|
|
105
204
|
return `${stripTrailingZero(frequency)} times daily`;
|
|
106
205
|
}
|
|
107
|
-
if (periodUnit === FhirPeriodUnit.Hour && period) {
|
|
206
|
+
if (periodUnit === types_1.FhirPeriodUnit.Hour && period) {
|
|
108
207
|
if (periodMax && periodMax !== period) {
|
|
109
208
|
return `every ${stripTrailingZero(period)} to ${stripTrailingZero(periodMax)} hours`;
|
|
110
209
|
}
|
|
111
210
|
return `every ${stripTrailingZero(period)} hour${period === 1 ? "" : "s"}`;
|
|
112
211
|
}
|
|
113
|
-
if (periodUnit === FhirPeriodUnit.Day && period && period !== 1) {
|
|
212
|
+
if (periodUnit === types_1.FhirPeriodUnit.Day && period && period !== 1) {
|
|
114
213
|
if (period === 2 && (!periodMax || periodMax === 2)) {
|
|
115
214
|
return "every other day";
|
|
116
215
|
}
|
|
@@ -119,7 +218,7 @@ function describeFrequency(internal) {
|
|
|
119
218
|
}
|
|
120
219
|
return `every ${stripTrailingZero(period)} days`;
|
|
121
220
|
}
|
|
122
|
-
if (periodUnit === FhirPeriodUnit.Week && period) {
|
|
221
|
+
if (periodUnit === types_1.FhirPeriodUnit.Week && period) {
|
|
123
222
|
if (period === 1 && (!periodMax || periodMax === 1)) {
|
|
124
223
|
return "once weekly";
|
|
125
224
|
}
|
|
@@ -128,7 +227,7 @@ function describeFrequency(internal) {
|
|
|
128
227
|
}
|
|
129
228
|
return `every ${stripTrailingZero(period)} weeks`;
|
|
130
229
|
}
|
|
131
|
-
if (periodUnit === FhirPeriodUnit.Month && period) {
|
|
230
|
+
if (periodUnit === types_1.FhirPeriodUnit.Month && period) {
|
|
132
231
|
if (period === 1 && (!periodMax || periodMax === 1)) {
|
|
133
232
|
return "once monthly";
|
|
134
233
|
}
|
|
@@ -197,33 +296,210 @@ function formatDoseLong(internal) {
|
|
|
197
296
|
}
|
|
198
297
|
return undefined;
|
|
199
298
|
}
|
|
200
|
-
function
|
|
299
|
+
function collectWhenPhrases(internal) {
|
|
201
300
|
if (!internal.when.length) {
|
|
202
|
-
return
|
|
301
|
+
return [];
|
|
302
|
+
}
|
|
303
|
+
const unique = [];
|
|
304
|
+
const seen = new Set();
|
|
305
|
+
for (const code of internal.when) {
|
|
306
|
+
if (!seen.has(code)) {
|
|
307
|
+
seen.add(code);
|
|
308
|
+
unique.push(code);
|
|
309
|
+
}
|
|
203
310
|
}
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
.
|
|
311
|
+
const hasSpecificAfter = unique.some((code) => code === types_1.EventTiming["After Breakfast"] ||
|
|
312
|
+
code === types_1.EventTiming["After Lunch"] ||
|
|
313
|
+
code === types_1.EventTiming["After Dinner"]);
|
|
314
|
+
const hasSpecificBefore = unique.some((code) => code === types_1.EventTiming["Before Breakfast"] ||
|
|
315
|
+
code === types_1.EventTiming["Before Lunch"] ||
|
|
316
|
+
code === types_1.EventTiming["Before Dinner"]);
|
|
317
|
+
const hasSpecificWith = unique.some((code) => code === types_1.EventTiming.Breakfast ||
|
|
318
|
+
code === types_1.EventTiming.Lunch ||
|
|
319
|
+
code === types_1.EventTiming.Dinner);
|
|
320
|
+
return unique
|
|
321
|
+
.filter((code) => {
|
|
322
|
+
if (code === types_1.EventTiming["After Meal"] && hasSpecificAfter) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
if (code === types_1.EventTiming["Before Meal"] && hasSpecificBefore) {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
if (code === types_1.EventTiming.Meal && hasSpecificWith) {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
return true;
|
|
332
|
+
})
|
|
333
|
+
.map((code) => { var _a; return (_a = WHEN_TEXT[code]) !== null && _a !== void 0 ? _a : code; })
|
|
334
|
+
.filter((text) => Boolean(text));
|
|
335
|
+
}
|
|
336
|
+
function joinWithAnd(parts) {
|
|
207
337
|
if (!parts.length) {
|
|
338
|
+
return "";
|
|
339
|
+
}
|
|
340
|
+
if (parts.length === 1) {
|
|
341
|
+
return parts[0];
|
|
342
|
+
}
|
|
343
|
+
if (parts.length === 2) {
|
|
344
|
+
return `${parts[0]} and ${parts[1]}`;
|
|
345
|
+
}
|
|
346
|
+
return `${parts.slice(0, -1).join(", ")} and ${parts[parts.length - 1]}`;
|
|
347
|
+
}
|
|
348
|
+
function combineFrequencyAndEvents(frequency, events) {
|
|
349
|
+
if (!frequency) {
|
|
350
|
+
if (!events.length) {
|
|
351
|
+
return {};
|
|
352
|
+
}
|
|
353
|
+
return { event: joinWithAnd(events) };
|
|
354
|
+
}
|
|
355
|
+
if (!events.length) {
|
|
356
|
+
return { frequency };
|
|
357
|
+
}
|
|
358
|
+
if (events.length === 1 && events[0] === "at bedtime") {
|
|
359
|
+
const lowerFrequency = frequency.toLowerCase();
|
|
360
|
+
if (lowerFrequency === "twice daily" || lowerFrequency === "three times daily" || lowerFrequency === "four times daily") {
|
|
361
|
+
return { frequency: `${frequency} and ${events[0]}` };
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return { frequency, event: joinWithAnd(events) };
|
|
365
|
+
}
|
|
366
|
+
function buildRoutePhrase(internal, grammar, hasSite) {
|
|
367
|
+
var _a;
|
|
368
|
+
if (typeof grammar.routePhrase === "function") {
|
|
369
|
+
return grammar.routePhrase({ hasSite, internal });
|
|
370
|
+
}
|
|
371
|
+
if (typeof grammar.routePhrase === "string") {
|
|
372
|
+
return grammar.routePhrase;
|
|
373
|
+
}
|
|
374
|
+
const text = (_a = internal.routeText) === null || _a === void 0 ? void 0 : _a.trim();
|
|
375
|
+
if (!text) {
|
|
376
|
+
return undefined;
|
|
377
|
+
}
|
|
378
|
+
const normalized = text.toLowerCase();
|
|
379
|
+
if (normalized.startsWith("by ") || normalized.startsWith("per ") || normalized.startsWith("via ")) {
|
|
380
|
+
return text;
|
|
381
|
+
}
|
|
382
|
+
if (normalized === "oral") {
|
|
383
|
+
return "by mouth";
|
|
384
|
+
}
|
|
385
|
+
if (normalized === "intravenous") {
|
|
386
|
+
return "intravenously";
|
|
387
|
+
}
|
|
388
|
+
if (normalized === "intramuscular") {
|
|
389
|
+
return "intramuscularly";
|
|
390
|
+
}
|
|
391
|
+
if (normalized === "subcutaneous") {
|
|
392
|
+
return "subcutaneously";
|
|
393
|
+
}
|
|
394
|
+
if (normalized === "topical") {
|
|
395
|
+
return "topically";
|
|
396
|
+
}
|
|
397
|
+
if (normalized === "transdermal") {
|
|
398
|
+
return "transdermally";
|
|
399
|
+
}
|
|
400
|
+
if (normalized === "intranasal" || normalized === "nasal") {
|
|
401
|
+
return "via nasal route";
|
|
402
|
+
}
|
|
403
|
+
if (normalized.includes("inhal")) {
|
|
404
|
+
return "via inhalation";
|
|
405
|
+
}
|
|
406
|
+
return `via ${text}`;
|
|
407
|
+
}
|
|
408
|
+
function formatSite(internal, grammar) {
|
|
409
|
+
var _a;
|
|
410
|
+
const text = (_a = internal.siteText) === null || _a === void 0 ? void 0 : _a.trim();
|
|
411
|
+
if (!text) {
|
|
208
412
|
return undefined;
|
|
209
413
|
}
|
|
210
|
-
|
|
414
|
+
const lower = text.toLowerCase();
|
|
415
|
+
let preposition = grammar.sitePreposition;
|
|
416
|
+
if (!preposition) {
|
|
417
|
+
if (lower.includes("eye")) {
|
|
418
|
+
preposition = "in";
|
|
419
|
+
}
|
|
420
|
+
else if (lower.includes("nostril") || lower.includes("nose")) {
|
|
421
|
+
preposition = "into";
|
|
422
|
+
}
|
|
423
|
+
else if (lower.includes("lung") || lower.includes("airway") || lower.includes("bronch")) {
|
|
424
|
+
preposition = "into";
|
|
425
|
+
}
|
|
426
|
+
else if (lower.includes("ear")) {
|
|
427
|
+
preposition = "in";
|
|
428
|
+
}
|
|
429
|
+
else if (/(skin|arm|leg|thigh|abdomen|shoulder|hand|foot|cheek|forearm|back|buttock|hip|face|hair|scalp|forehead|chin|neck)/.test(lower)) {
|
|
430
|
+
preposition = "to";
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
preposition = "at";
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
const noun = formatSiteNoun(text, preposition);
|
|
437
|
+
return `${preposition} ${noun}`.trim();
|
|
438
|
+
}
|
|
439
|
+
function formatSiteNoun(site, preposition) {
|
|
440
|
+
const trimmed = site.trim();
|
|
441
|
+
const lower = trimmed.toLowerCase();
|
|
442
|
+
const skipArticlePrefixes = [
|
|
443
|
+
"the ",
|
|
444
|
+
"both ",
|
|
445
|
+
"each ",
|
|
446
|
+
"either ",
|
|
447
|
+
"every ",
|
|
448
|
+
"all ",
|
|
449
|
+
"bilateral ",
|
|
450
|
+
];
|
|
451
|
+
for (const prefix of skipArticlePrefixes) {
|
|
452
|
+
if (lower.startsWith(prefix)) {
|
|
453
|
+
return trimmed;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
const needsArticle = /^(left|right|upper|lower|inner|outer|mid|middle|posterior|anterior|proximal|distal|medial|lateral|dorsal|ventral)\b/.test(lower);
|
|
457
|
+
if (needsArticle || preposition === "at") {
|
|
458
|
+
return `the ${trimmed}`;
|
|
459
|
+
}
|
|
460
|
+
if (/(eye|nostril|ear|arm|leg|thigh|abdomen|hand|foot|cheek|skin|back)/.test(lower)) {
|
|
461
|
+
return `the ${trimmed}`;
|
|
462
|
+
}
|
|
463
|
+
return `the ${trimmed}`;
|
|
211
464
|
}
|
|
212
465
|
function describeDayOfWeek(internal) {
|
|
213
466
|
if (!internal.dayOfWeek.length) {
|
|
214
467
|
return undefined;
|
|
215
468
|
}
|
|
216
|
-
const days = internal.dayOfWeek.map((d) => DAY_NAMES[d]
|
|
217
|
-
if (days.length
|
|
218
|
-
return
|
|
469
|
+
const days = internal.dayOfWeek.map((d) => { var _a; return (_a = DAY_NAMES[d]) !== null && _a !== void 0 ? _a : d; });
|
|
470
|
+
if (!days.length) {
|
|
471
|
+
return undefined;
|
|
219
472
|
}
|
|
220
|
-
return `on ${days
|
|
473
|
+
return `on ${joinWithAnd(days)}`;
|
|
221
474
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
475
|
+
function formatInternal(internal, style, localization) {
|
|
476
|
+
const defaults = {
|
|
477
|
+
short: formatShort(internal),
|
|
478
|
+
long: formatLong(internal)
|
|
479
|
+
};
|
|
480
|
+
if (!localization) {
|
|
481
|
+
return defaults[style];
|
|
482
|
+
}
|
|
483
|
+
const formatDefault = (target) => defaults[target];
|
|
484
|
+
if (style === "short" && localization.formatShort) {
|
|
485
|
+
const context = {
|
|
486
|
+
style: "short",
|
|
487
|
+
internal,
|
|
488
|
+
defaultText: defaults.short,
|
|
489
|
+
formatDefault
|
|
490
|
+
};
|
|
491
|
+
return localization.formatShort(context);
|
|
492
|
+
}
|
|
493
|
+
if (style === "long" && localization.formatLong) {
|
|
494
|
+
const context = {
|
|
495
|
+
style: "long",
|
|
496
|
+
internal,
|
|
497
|
+
defaultText: defaults.long,
|
|
498
|
+
formatDefault
|
|
499
|
+
};
|
|
500
|
+
return localization.formatLong(context);
|
|
225
501
|
}
|
|
226
|
-
return
|
|
502
|
+
return defaults[style];
|
|
227
503
|
}
|
|
228
504
|
function formatShort(internal) {
|
|
229
505
|
const parts = [];
|
|
@@ -248,12 +524,12 @@ function formatShort(internal) {
|
|
|
248
524
|
}
|
|
249
525
|
else if (internal.frequency !== undefined &&
|
|
250
526
|
internal.frequencyMax !== undefined &&
|
|
251
|
-
internal.periodUnit === FhirPeriodUnit.Day &&
|
|
527
|
+
internal.periodUnit === types_1.FhirPeriodUnit.Day &&
|
|
252
528
|
(!internal.period || internal.period === 1)) {
|
|
253
529
|
parts.push(`${stripTrailingZero(internal.frequency)}-${stripTrailingZero(internal.frequencyMax)}x/d`);
|
|
254
530
|
}
|
|
255
531
|
else if (internal.frequency &&
|
|
256
|
-
internal.periodUnit === FhirPeriodUnit.Day &&
|
|
532
|
+
internal.periodUnit === types_1.FhirPeriodUnit.Day &&
|
|
257
533
|
(!internal.period || internal.period === 1)) {
|
|
258
534
|
parts.push(`${stripTrailingZero(internal.frequency)}x/d`);
|
|
259
535
|
}
|
|
@@ -283,35 +559,44 @@ function formatShort(internal) {
|
|
|
283
559
|
return parts.filter(Boolean).join(" ");
|
|
284
560
|
}
|
|
285
561
|
function formatLong(internal) {
|
|
286
|
-
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
562
|
+
var _a;
|
|
563
|
+
const grammar = resolveRouteGrammar(internal);
|
|
564
|
+
const dosePart = (_a = formatDoseLong(internal)) !== null && _a !== void 0 ? _a : "the medication";
|
|
565
|
+
const sitePart = formatSite(internal, grammar);
|
|
566
|
+
const routePart = buildRoutePhrase(internal, grammar, Boolean(sitePart));
|
|
567
|
+
const frequencyPart = describeFrequency(internal);
|
|
568
|
+
const eventParts = collectWhenPhrases(internal);
|
|
569
|
+
const timing = combineFrequencyAndEvents(frequencyPart, eventParts);
|
|
570
|
+
const dayPart = describeDayOfWeek(internal);
|
|
571
|
+
const asNeededPart = internal.asNeeded
|
|
572
|
+
? internal.asNeededReason
|
|
573
|
+
? `as needed for ${internal.asNeededReason}`
|
|
574
|
+
: "as needed"
|
|
575
|
+
: undefined;
|
|
576
|
+
const segments = [dosePart];
|
|
577
|
+
if (routePart) {
|
|
578
|
+
segments.push(routePart);
|
|
290
579
|
}
|
|
291
|
-
if (
|
|
292
|
-
|
|
580
|
+
if (timing.frequency) {
|
|
581
|
+
segments.push(timing.frequency);
|
|
293
582
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
parts.push(freqText);
|
|
583
|
+
if (timing.event) {
|
|
584
|
+
segments.push(timing.event);
|
|
297
585
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
parts.push(whenText);
|
|
586
|
+
if (dayPart) {
|
|
587
|
+
segments.push(dayPart);
|
|
301
588
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
parts.push(dayText);
|
|
589
|
+
if (asNeededPart) {
|
|
590
|
+
segments.push(asNeededPart);
|
|
305
591
|
}
|
|
306
|
-
if (
|
|
307
|
-
|
|
308
|
-
? `as needed for ${internal.asNeededReason}`
|
|
309
|
-
: "as needed");
|
|
592
|
+
if (sitePart) {
|
|
593
|
+
segments.push(sitePart);
|
|
310
594
|
}
|
|
311
|
-
|
|
312
|
-
|
|
595
|
+
const body = segments.filter(Boolean).join(" ").replace(/\s+/g, " ").trim();
|
|
596
|
+
if (!body) {
|
|
597
|
+
return `${grammar.verb}.`;
|
|
313
598
|
}
|
|
314
|
-
return
|
|
599
|
+
return `${grammar.verb} ${body}.`;
|
|
315
600
|
}
|
|
316
601
|
function stripTrailingZero(value) {
|
|
317
602
|
const text = value.toString();
|
package/dist/i18n.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ParsedSigInternal } from "./internal-types";
|
|
2
|
+
export interface SigFormatContext {
|
|
3
|
+
readonly style: "short" | "long";
|
|
4
|
+
readonly internal: ParsedSigInternal;
|
|
5
|
+
readonly defaultText: string;
|
|
6
|
+
formatDefault(style: "short" | "long"): string;
|
|
7
|
+
}
|
|
8
|
+
export interface SigShortContext extends SigFormatContext {
|
|
9
|
+
readonly style: "short";
|
|
10
|
+
}
|
|
11
|
+
export interface SigLongContext extends SigFormatContext {
|
|
12
|
+
readonly style: "long";
|
|
13
|
+
}
|
|
14
|
+
export interface SigLocalization {
|
|
15
|
+
readonly locale: string;
|
|
16
|
+
formatShort?(context: SigShortContext): string;
|
|
17
|
+
formatLong?(context: SigLongContext): string;
|
|
18
|
+
}
|
|
19
|
+
export interface SigLocalizationConfig extends Partial<Omit<SigLocalization, "locale">> {
|
|
20
|
+
locale?: string;
|
|
21
|
+
inherit?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function registerSigLocalization(localization: SigLocalization): void;
|
|
24
|
+
export declare function getRegisteredSigLocalizations(): SigLocalization[];
|
|
25
|
+
export declare function resolveSigLocalization(locale?: string, config?: SigLocalizationConfig): SigLocalization | undefined;
|
|
26
|
+
export type SigTranslation = SigLocalization;
|
|
27
|
+
export type SigTranslationConfig = SigLocalizationConfig;
|
|
28
|
+
export declare const registerSigTranslation: typeof registerSigLocalization;
|
|
29
|
+
export declare const getRegisteredSigTranslations: typeof getRegisteredSigLocalizations;
|
|
30
|
+
export declare function resolveSigTranslation(locale?: string, config?: SigTranslationConfig): SigTranslation | undefined;
|
|
31
|
+
export declare const THAI_SITE_TRANSLATIONS: Record<string, string>;
|