ezmedicationinput 0.1.52 → 0.1.54
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/hpsg/lexical-classes.d.ts +1 -0
- package/dist/index.cjs +350 -69
- package/dist/index.js +350 -69
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2252,6 +2252,14 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
|
|
|
2252
2252
|
names: ["axilla", "axillae", "armpit", "armpits"],
|
|
2253
2253
|
definition: { coding: { code: "34797008", display: "Axilla structure" }, routeHint: RouteCode["Topical route"] }
|
|
2254
2254
|
},
|
|
2255
|
+
{
|
|
2256
|
+
names: ["axillary hair", "armpit hair", "armpit hairs", "underarm hair", "underarm hairs"],
|
|
2257
|
+
definition: {
|
|
2258
|
+
coding: { code: "75703003", display: "Structure of hair of axilla" },
|
|
2259
|
+
text: "axillary hair",
|
|
2260
|
+
routeHint: RouteCode["Topical route"]
|
|
2261
|
+
}
|
|
2262
|
+
},
|
|
2255
2263
|
{
|
|
2256
2264
|
names: ["groin"],
|
|
2257
2265
|
definition: { coding: { code: "26893007", display: "Inguinal region structure" }, routeHint: RouteCode["Topical route"] }
|
|
@@ -2322,6 +2330,71 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
|
|
|
2322
2330
|
names: ["face"],
|
|
2323
2331
|
definition: { coding: { code: "89545001", display: "Face" }, routeHint: RouteCode["Topical route"] }
|
|
2324
2332
|
},
|
|
2333
|
+
{
|
|
2334
|
+
names: ["eyebrow", "brow"],
|
|
2335
|
+
definition: {
|
|
2336
|
+
coding: { code: "392262008", display: "Eyebrow structure" },
|
|
2337
|
+
text: "eyebrow",
|
|
2338
|
+
routeHint: RouteCode["Topical route"]
|
|
2339
|
+
}
|
|
2340
|
+
},
|
|
2341
|
+
{
|
|
2342
|
+
names: ["eyebrows", "brows"],
|
|
2343
|
+
definition: {
|
|
2344
|
+
coding: { code: "392262008", display: "Eyebrow structure" },
|
|
2345
|
+
text: "eyebrows",
|
|
2346
|
+
routeHint: RouteCode["Topical route"]
|
|
2347
|
+
}
|
|
2348
|
+
},
|
|
2349
|
+
{
|
|
2350
|
+
names: ["left eyebrow", "left brow"],
|
|
2351
|
+
definition: {
|
|
2352
|
+
coding: { code: "722011002", display: "Structure of eyebrow of left eye region" },
|
|
2353
|
+
text: "left eyebrow",
|
|
2354
|
+
routeHint: RouteCode["Topical route"]
|
|
2355
|
+
}
|
|
2356
|
+
},
|
|
2357
|
+
{
|
|
2358
|
+
names: ["right eyebrow", "right brow"],
|
|
2359
|
+
definition: {
|
|
2360
|
+
coding: { code: "722012009", display: "Structure of eyebrow of right eye region" },
|
|
2361
|
+
text: "right eyebrow",
|
|
2362
|
+
routeHint: RouteCode["Topical route"]
|
|
2363
|
+
}
|
|
2364
|
+
},
|
|
2365
|
+
{
|
|
2366
|
+
names: [
|
|
2367
|
+
"both eyebrows",
|
|
2368
|
+
"bilateral eyebrows",
|
|
2369
|
+
"each eyebrow",
|
|
2370
|
+
"left and right eyebrow",
|
|
2371
|
+
"left and right eyebrows",
|
|
2372
|
+
"right and left eyebrow",
|
|
2373
|
+
"right and left eyebrows",
|
|
2374
|
+
"left right eyebrow",
|
|
2375
|
+
"left right eyebrows"
|
|
2376
|
+
],
|
|
2377
|
+
definition: {
|
|
2378
|
+
coding: {
|
|
2379
|
+
code: buildSnomedBodySiteLateralityPostcoordinationCode(
|
|
2380
|
+
"392262008",
|
|
2381
|
+
SNOMED_CT_BILATERAL_QUALIFIER_CODE
|
|
2382
|
+
),
|
|
2383
|
+
display: "both eyebrows"
|
|
2384
|
+
},
|
|
2385
|
+
text: "both eyebrows",
|
|
2386
|
+
administrationTargetCount: 2,
|
|
2387
|
+
routeHint: RouteCode["Topical route"]
|
|
2388
|
+
}
|
|
2389
|
+
},
|
|
2390
|
+
{
|
|
2391
|
+
names: ["eyebrow hair", "eyebrow hairs", "brow hair", "brow hairs"],
|
|
2392
|
+
definition: {
|
|
2393
|
+
coding: { code: "392261001", display: "Hair structure of eyebrow" },
|
|
2394
|
+
text: "eyebrow hair",
|
|
2395
|
+
routeHint: RouteCode["Topical route"]
|
|
2396
|
+
}
|
|
2397
|
+
},
|
|
2325
2398
|
{
|
|
2326
2399
|
names: ["eyelid", "eyelids"],
|
|
2327
2400
|
definition: { coding: { code: "80243003", display: "Eyelid" }, routeHint: RouteCode["Ophthalmic route"] }
|
|
@@ -2442,6 +2515,30 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
|
|
|
2442
2515
|
names: ["perineum"],
|
|
2443
2516
|
definition: { coding: { code: "243990009", display: "Entire perineum" }, routeHint: RouteCode["Topical route"] }
|
|
2444
2517
|
},
|
|
2518
|
+
{
|
|
2519
|
+
names: ["mustache", "moustache", "mustache hair", "moustache hair"],
|
|
2520
|
+
definition: {
|
|
2521
|
+
coding: { code: "256925006", display: "Structure of hair of mustache" },
|
|
2522
|
+
text: "mustache",
|
|
2523
|
+
routeHint: RouteCode["Topical route"]
|
|
2524
|
+
}
|
|
2525
|
+
},
|
|
2526
|
+
{
|
|
2527
|
+
names: ["beard", "beard hair", "facial hair"],
|
|
2528
|
+
definition: {
|
|
2529
|
+
coding: { code: "367576007", display: "Structure of beard hair" },
|
|
2530
|
+
text: "beard",
|
|
2531
|
+
routeHint: RouteCode["Topical route"]
|
|
2532
|
+
}
|
|
2533
|
+
},
|
|
2534
|
+
{
|
|
2535
|
+
names: ["pubic hair"],
|
|
2536
|
+
definition: {
|
|
2537
|
+
coding: { code: "75776007", display: "Structure of hair of pubis" },
|
|
2538
|
+
text: "pubic hair",
|
|
2539
|
+
routeHint: RouteCode["Topical route"]
|
|
2540
|
+
}
|
|
2541
|
+
},
|
|
2445
2542
|
{
|
|
2446
2543
|
names: ["skin"],
|
|
2447
2544
|
definition: { coding: { code: "181469002", display: "Entire skin" }, routeHint: RouteCode["Topical route"] }
|
|
@@ -2452,6 +2549,38 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
|
|
|
2452
2549
|
coding: { code: "386045008", display: "Hair structure (body structure)" },
|
|
2453
2550
|
routeHint: RouteCode["Topical route"]
|
|
2454
2551
|
}
|
|
2552
|
+
},
|
|
2553
|
+
{
|
|
2554
|
+
names: ["joint"],
|
|
2555
|
+
definition: {
|
|
2556
|
+
coding: { code: "39352004", display: "Joint structure" },
|
|
2557
|
+
text: "joint",
|
|
2558
|
+
routeHint: RouteCode["Topical route"]
|
|
2559
|
+
}
|
|
2560
|
+
},
|
|
2561
|
+
{
|
|
2562
|
+
names: ["joints"],
|
|
2563
|
+
definition: {
|
|
2564
|
+
coding: { code: "81087007", display: "Joints" },
|
|
2565
|
+
text: "joints",
|
|
2566
|
+
routeHint: RouteCode["Topical route"]
|
|
2567
|
+
}
|
|
2568
|
+
},
|
|
2569
|
+
{
|
|
2570
|
+
names: ["finger joint", "finger joints"],
|
|
2571
|
+
definition: {
|
|
2572
|
+
coding: { code: "125682004", display: "Finger joint structure" },
|
|
2573
|
+
text: "finger joints",
|
|
2574
|
+
routeHint: RouteCode["Topical route"]
|
|
2575
|
+
}
|
|
2576
|
+
},
|
|
2577
|
+
{
|
|
2578
|
+
names: ["knuckle", "knuckles"],
|
|
2579
|
+
definition: {
|
|
2580
|
+
coding: { code: "70420003", display: "Metacarpophalangeal joint structure" },
|
|
2581
|
+
text: "knuckles",
|
|
2582
|
+
routeHint: RouteCode["Topical route"]
|
|
2583
|
+
}
|
|
2455
2584
|
}
|
|
2456
2585
|
];
|
|
2457
2586
|
var DEFAULT_BODY_SITE_SNOMED = objectFromEntries(
|
|
@@ -4363,6 +4492,7 @@ var lexical_classes_default = {
|
|
|
4363
4492
|
"Use shampoo": "\u0E2A\u0E23\u0E30"
|
|
4364
4493
|
},
|
|
4365
4494
|
compoundDoseUnits: [
|
|
4495
|
+
{ head: "cm", tails: [], requiresSiteContext: true, unit: "cm line" },
|
|
4366
4496
|
{ head: "cm", tails: ["ribbon", "ribbons"], unit: "cm ribbon" },
|
|
4367
4497
|
{ head: "cm", tails: ["strip", "strips"], unit: "cm strip" },
|
|
4368
4498
|
{ head: "cm", tails: ["line", "lines"], unit: "cm line" },
|
|
@@ -4371,6 +4501,8 @@ var lexical_classes_default = {
|
|
|
4371
4501
|
{ head: "fingertip", tails: ["unit", "units"], unit: "FTU" },
|
|
4372
4502
|
{ head: "eye", tails: ["drop", "drops"], unit: "drop" },
|
|
4373
4503
|
{ head: "pea-sized", tails: ["amount"], unit: "pea-sized amount" },
|
|
4504
|
+
{ head: "pea-size", tails: ["amount"], unit: "pea-sized amount" },
|
|
4505
|
+
{ head: "peasize", tails: [], tailSequences: [[]], unit: "pea-sized amount" },
|
|
4374
4506
|
{ head: "\u0E40\u0E21\u0E47\u0E14\u0E16\u0E31\u0E48\u0E27", tails: [], unit: "pea-sized amount" },
|
|
4375
4507
|
{ head: "\u0E40\u0E21\u0E47\u0E14\u0E16\u0E31\u0E48\u0E27\u0E40\u0E02\u0E35\u0E22\u0E27", tails: [], unit: "pea-sized amount" },
|
|
4376
4508
|
{ head: "\u0E40\u0E21\u0E25\u0E47\u0E14\u0E16\u0E31\u0E48\u0E27", tails: [], unit: "pea-sized amount" },
|
|
@@ -4380,7 +4512,7 @@ var lexical_classes_default = {
|
|
|
4380
4512
|
{
|
|
4381
4513
|
head: "pea",
|
|
4382
4514
|
tails: [],
|
|
4383
|
-
tailSequences: [["sized", "amount"], ["size", "amount"]],
|
|
4515
|
+
tailSequences: [["sized", "amount"], ["size", "amount"], ["amount"], ["sized"], ["size"], []],
|
|
4384
4516
|
unit: "pea-sized amount"
|
|
4385
4517
|
},
|
|
4386
4518
|
{ head: "hand", tails: ["print", "prints"], unit: "handprint" },
|
|
@@ -15026,6 +15158,11 @@ var unit_terminology_default = {
|
|
|
15026
15158
|
unit: "pea-sized amount",
|
|
15027
15159
|
kind: "product_specific_amount",
|
|
15028
15160
|
aliases: [
|
|
15161
|
+
"pea",
|
|
15162
|
+
"pea amount",
|
|
15163
|
+
"peasize",
|
|
15164
|
+
"pea-size",
|
|
15165
|
+
"pea size",
|
|
15029
15166
|
"pea sized amount",
|
|
15030
15167
|
"pea-sized",
|
|
15031
15168
|
"pea sized",
|
|
@@ -15122,10 +15259,96 @@ var unit_terminology_default = {
|
|
|
15122
15259
|
]
|
|
15123
15260
|
};
|
|
15124
15261
|
|
|
15262
|
+
// src/utils/units.ts
|
|
15263
|
+
var MASS_UNITS = {
|
|
15264
|
+
kg: 1e6,
|
|
15265
|
+
g: 1e3,
|
|
15266
|
+
mg: 1,
|
|
15267
|
+
mcg: 1e-3,
|
|
15268
|
+
ug: 1e-3,
|
|
15269
|
+
microg: 1e-3,
|
|
15270
|
+
ng: 1e-6
|
|
15271
|
+
};
|
|
15272
|
+
var VOLUME_UNITS = {
|
|
15273
|
+
l: 1e3,
|
|
15274
|
+
dl: 100,
|
|
15275
|
+
ml: 1,
|
|
15276
|
+
ul: 1e-3,
|
|
15277
|
+
microl: 1e-3,
|
|
15278
|
+
cm3: 1,
|
|
15279
|
+
tsp: 5,
|
|
15280
|
+
tbsp: 15
|
|
15281
|
+
};
|
|
15282
|
+
function getUnitCategory(unit) {
|
|
15283
|
+
if (!unit) return "other";
|
|
15284
|
+
const u = unit.toLowerCase();
|
|
15285
|
+
if (MASS_UNITS[u] !== void 0) return "mass";
|
|
15286
|
+
if (VOLUME_UNITS[u] !== void 0) return "volume";
|
|
15287
|
+
return "other";
|
|
15288
|
+
}
|
|
15289
|
+
function getBaseUnitFactor(unit) {
|
|
15290
|
+
var _a2, _b;
|
|
15291
|
+
if (!unit) return 1;
|
|
15292
|
+
const u = unit.toLowerCase();
|
|
15293
|
+
return (_b = (_a2 = MASS_UNITS[u]) != null ? _a2 : VOLUME_UNITS[u]) != null ? _b : 1;
|
|
15294
|
+
}
|
|
15295
|
+
function convertValue(value, fromUnit, toUnit, strength) {
|
|
15296
|
+
const f = fromUnit.toLowerCase();
|
|
15297
|
+
const t = toUnit.toLowerCase();
|
|
15298
|
+
if (f === t) return value;
|
|
15299
|
+
const fCat = getUnitCategory(f);
|
|
15300
|
+
const tCat = getUnitCategory(t);
|
|
15301
|
+
if (fCat === tCat && fCat !== "other") {
|
|
15302
|
+
const fFactor = getBaseUnitFactor(f);
|
|
15303
|
+
const tFactor = getBaseUnitFactor(t);
|
|
15304
|
+
return value * fFactor / tFactor;
|
|
15305
|
+
}
|
|
15306
|
+
if (strength && (fCat === "mass" && tCat === "volume" || fCat === "volume" && tCat === "mass")) {
|
|
15307
|
+
const numUnit = strength.numerator.unit.toLowerCase();
|
|
15308
|
+
const denUnit = strength.denominator.unit.toLowerCase();
|
|
15309
|
+
const numCat = getUnitCategory(numUnit);
|
|
15310
|
+
const denCat = getUnitCategory(denUnit);
|
|
15311
|
+
if (numCat !== denCat && numCat !== "other" && denCat !== "other") {
|
|
15312
|
+
const massSide = numCat === "mass" ? strength.numerator : strength.denominator;
|
|
15313
|
+
const volSide = numCat === "volume" ? strength.numerator : strength.denominator;
|
|
15314
|
+
const bridgeDensity = massSide.value * getBaseUnitFactor(massSide.unit) / (volSide.value * getBaseUnitFactor(volSide.unit));
|
|
15315
|
+
if (fCat === "mass") {
|
|
15316
|
+
const valueMg = value * getBaseUnitFactor(fromUnit);
|
|
15317
|
+
const valueMl = valueMg / bridgeDensity;
|
|
15318
|
+
return valueMl / getBaseUnitFactor(toUnit);
|
|
15319
|
+
} else {
|
|
15320
|
+
const valueMl = value * getBaseUnitFactor(fromUnit);
|
|
15321
|
+
const valueMg = valueMl * bridgeDensity;
|
|
15322
|
+
return valueMg / getBaseUnitFactor(toUnit);
|
|
15323
|
+
}
|
|
15324
|
+
}
|
|
15325
|
+
}
|
|
15326
|
+
return null;
|
|
15327
|
+
}
|
|
15328
|
+
|
|
15125
15329
|
// src/unit-lexicon.ts
|
|
15126
15330
|
var HOUSEHOLD_VOLUME_UNIT_SET = new Set(
|
|
15127
15331
|
HOUSEHOLD_VOLUME_UNITS.map((unit) => unit.toLowerCase())
|
|
15128
15332
|
);
|
|
15333
|
+
var MASS_DISPENSED_SEMISOLID_DOSAGE_FORMS = /* @__PURE__ */ new Set([
|
|
15334
|
+
"cream",
|
|
15335
|
+
"ointment",
|
|
15336
|
+
"gel",
|
|
15337
|
+
"paste",
|
|
15338
|
+
"cutaneous paste",
|
|
15339
|
+
"vaginal cream",
|
|
15340
|
+
"vaginal gel",
|
|
15341
|
+
"oral gel",
|
|
15342
|
+
"oral paste",
|
|
15343
|
+
"oromucosal gel",
|
|
15344
|
+
"oromucosal paste",
|
|
15345
|
+
"dental gel",
|
|
15346
|
+
"dental paste",
|
|
15347
|
+
"gingival gel",
|
|
15348
|
+
"nasal gel",
|
|
15349
|
+
"eye gel",
|
|
15350
|
+
"eye ointment"
|
|
15351
|
+
]);
|
|
15129
15352
|
var DOSE_UNIT_TERMINOLOGY = unit_terminology_default.terms;
|
|
15130
15353
|
var DOSE_UNIT_TERMINOLOGY_BY_KEY = /* @__PURE__ */ new Map();
|
|
15131
15354
|
var DISCRETE_UNIT_KINDS = /* @__PURE__ */ new Set([
|
|
@@ -15188,6 +15411,47 @@ function unitApproximationOverride(unit, context) {
|
|
|
15188
15411
|
}
|
|
15189
15412
|
return void 0;
|
|
15190
15413
|
}
|
|
15414
|
+
function normalizeDosageFormKey(form) {
|
|
15415
|
+
var _a2;
|
|
15416
|
+
const normalized = form == null ? void 0 : form.trim().toLowerCase();
|
|
15417
|
+
if (!normalized) {
|
|
15418
|
+
return void 0;
|
|
15419
|
+
}
|
|
15420
|
+
return (_a2 = KNOWN_DOSAGE_FORMS_TO_DOSE[normalized]) != null ? _a2 : normalized;
|
|
15421
|
+
}
|
|
15422
|
+
function getPreferredMassApproximationUnit(context) {
|
|
15423
|
+
var _a2;
|
|
15424
|
+
const normalizedDosageForm = normalizeDosageFormKey(context == null ? void 0 : context.dosageForm);
|
|
15425
|
+
if (!normalizedDosageForm || !MASS_DISPENSED_SEMISOLID_DOSAGE_FORMS.has(normalizedDosageForm)) {
|
|
15426
|
+
return void 0;
|
|
15427
|
+
}
|
|
15428
|
+
const containerUnit = (_a2 = context == null ? void 0 : context.containerUnit) == null ? void 0 : _a2.trim();
|
|
15429
|
+
if (containerUnit && getUnitCategory(containerUnit) === "mass") {
|
|
15430
|
+
return containerUnit;
|
|
15431
|
+
}
|
|
15432
|
+
const defaultUnit = normalizedDosageForm ? DEFAULT_UNIT_BY_NORMALIZED_FORM[normalizedDosageForm] : void 0;
|
|
15433
|
+
return defaultUnit && getUnitCategory(defaultUnit) === "mass" ? defaultUnit : void 0;
|
|
15434
|
+
}
|
|
15435
|
+
function bridgeApproximationToMassDispensedTopical(approximation, context) {
|
|
15436
|
+
const preferredMassUnit = getPreferredMassApproximationUnit(context);
|
|
15437
|
+
if (!preferredMassUnit || getUnitCategory(approximation.unit) !== "volume") {
|
|
15438
|
+
return approximation;
|
|
15439
|
+
}
|
|
15440
|
+
const sourceVolumeFactor = VOLUME_UNITS[approximation.unit.toLowerCase()];
|
|
15441
|
+
const targetMassFactor = MASS_UNITS[preferredMassUnit.toLowerCase()];
|
|
15442
|
+
if (!sourceVolumeFactor || !targetMassFactor) {
|
|
15443
|
+
return approximation;
|
|
15444
|
+
}
|
|
15445
|
+
const valueMl = approximation.value * sourceVolumeFactor;
|
|
15446
|
+
const valueG = valueMl;
|
|
15447
|
+
const convertedValue = valueG * MASS_UNITS.g / targetMassFactor;
|
|
15448
|
+
const bridgeBasis = "Mass-dispensed semisolid topical default bridge assumes 1 mL approximately equals 1 g unless a product-specific override is provided";
|
|
15449
|
+
return __spreadProps(__spreadValues({}, approximation), {
|
|
15450
|
+
value: convertedValue,
|
|
15451
|
+
unit: preferredMassUnit,
|
|
15452
|
+
basis: approximation.basis ? `${approximation.basis}; ${bridgeBasis}` : bridgeBasis
|
|
15453
|
+
});
|
|
15454
|
+
}
|
|
15191
15455
|
function getDoseUnitTerminologyEntry(unit) {
|
|
15192
15456
|
var _a2;
|
|
15193
15457
|
if (!unit) {
|
|
@@ -15209,7 +15473,11 @@ function getDoseUnitApproximation(unit, context) {
|
|
|
15209
15473
|
if (override) {
|
|
15210
15474
|
return override;
|
|
15211
15475
|
}
|
|
15212
|
-
|
|
15476
|
+
const approximation = terminologyEntry == null ? void 0 : terminologyEntry.approximateQuantity;
|
|
15477
|
+
if (!approximation) {
|
|
15478
|
+
return void 0;
|
|
15479
|
+
}
|
|
15480
|
+
return bridgeApproximationToMassDispensedTopical(approximation, context);
|
|
15213
15481
|
}
|
|
15214
15482
|
function getDoseUnitSemantics(unit, context) {
|
|
15215
15483
|
if (!unit) {
|
|
@@ -16456,6 +16724,64 @@ function productLexicalRule() {
|
|
|
16456
16724
|
}
|
|
16457
16725
|
function matchCompoundDoseUnit(context, start, lower) {
|
|
16458
16726
|
var _a2;
|
|
16727
|
+
const MAX_SITE_CONTEXT_TOKENS = 3;
|
|
16728
|
+
const hasSiteMeaning = (lowerValue) => {
|
|
16729
|
+
var _a3, _b, _c;
|
|
16730
|
+
return Boolean(
|
|
16731
|
+
lowerValue && (DEFAULT_BODY_SITE_SNOMED[normalizeBodySiteKey(lowerValue)] || resolveBodySitePhrase(lowerValue, (_a3 = context.options) == null ? void 0 : _a3.siteCodeMap, {
|
|
16732
|
+
bodySiteContext: (_c = (_b = context.options) == null ? void 0 : _b.context) == null ? void 0 : _c.bodySiteContext
|
|
16733
|
+
}))
|
|
16734
|
+
);
|
|
16735
|
+
};
|
|
16736
|
+
const collectForwardSitePhrases = (phraseStart) => {
|
|
16737
|
+
const parts = [];
|
|
16738
|
+
const phrases = [];
|
|
16739
|
+
for (let index = phraseStart; index < context.limit && parts.length < MAX_SITE_CONTEXT_TOKENS; index += 1) {
|
|
16740
|
+
const token = context.tokens[index];
|
|
16741
|
+
if (!token || context.state.consumed.has(token.index)) {
|
|
16742
|
+
break;
|
|
16743
|
+
}
|
|
16744
|
+
const lowerValue = normalizeTokenLower(token);
|
|
16745
|
+
if (!lowerValue || isPunctuation(lowerValue)) {
|
|
16746
|
+
break;
|
|
16747
|
+
}
|
|
16748
|
+
parts.push(lowerValue);
|
|
16749
|
+
phrases.push(parts.join(" "));
|
|
16750
|
+
}
|
|
16751
|
+
return phrases;
|
|
16752
|
+
};
|
|
16753
|
+
const matchesSiteContext = () => {
|
|
16754
|
+
const next = context.tokens[start + 1];
|
|
16755
|
+
const nextLower = next && !context.state.consumed.has(next.index) ? normalizeTokenLower(next) : void 0;
|
|
16756
|
+
if (nextLower && (ROUTE_SITE_PREPOSITIONS.has(nextLower) || SITE_ANCHORS.has(nextLower))) {
|
|
16757
|
+
const followingSitePhrases = collectForwardSitePhrases(start + 2);
|
|
16758
|
+
if (followingSitePhrases.some((phrase) => hasSiteMeaning(phrase))) {
|
|
16759
|
+
return true;
|
|
16760
|
+
}
|
|
16761
|
+
}
|
|
16762
|
+
const previous = context.tokens[start - 1];
|
|
16763
|
+
const previousLower = previous && !context.state.consumed.has(previous.index) ? normalizeTokenLower(previous) : void 0;
|
|
16764
|
+
const trailingNumberBeforeUnit = Boolean(
|
|
16765
|
+
previousLower && /^[0-9]+(?:\.[0-9]+)?$/.test(previousLower)
|
|
16766
|
+
);
|
|
16767
|
+
const siteEnd = start - (trailingNumberBeforeUnit ? 2 : 1);
|
|
16768
|
+
for (let phraseLength = 1; phraseLength <= MAX_SITE_CONTEXT_TOKENS; phraseLength += 1) {
|
|
16769
|
+
const phraseStart = siteEnd - phraseLength + 1;
|
|
16770
|
+
if (phraseStart < 0) {
|
|
16771
|
+
break;
|
|
16772
|
+
}
|
|
16773
|
+
const precedingAnchor = context.tokens[phraseStart - 1];
|
|
16774
|
+
const precedingAnchorLower = precedingAnchor && !context.state.consumed.has(precedingAnchor.index) ? normalizeTokenLower(precedingAnchor) : void 0;
|
|
16775
|
+
if (!precedingAnchorLower || !ROUTE_SITE_PREPOSITIONS.has(precedingAnchorLower) && !SITE_ANCHORS.has(precedingAnchorLower)) {
|
|
16776
|
+
continue;
|
|
16777
|
+
}
|
|
16778
|
+
const phrase = collectForwardSitePhrases(phraseStart)[phraseLength - 1];
|
|
16779
|
+
if (hasSiteMeaning(phrase)) {
|
|
16780
|
+
return true;
|
|
16781
|
+
}
|
|
16782
|
+
}
|
|
16783
|
+
return false;
|
|
16784
|
+
};
|
|
16459
16785
|
for (const compound of COMPOUND_DOSE_UNITS) {
|
|
16460
16786
|
if (compound.head !== lower) {
|
|
16461
16787
|
continue;
|
|
@@ -16485,6 +16811,10 @@ function matchCompoundDoseUnit(context, start, lower) {
|
|
|
16485
16811
|
return head ? { unit: compound.unit, tokens: [head, next] } : void 0;
|
|
16486
16812
|
}
|
|
16487
16813
|
}
|
|
16814
|
+
if (compound.requiresSiteContext && matchesSiteContext()) {
|
|
16815
|
+
const head = context.tokens[start];
|
|
16816
|
+
return head ? { unit: compound.unit, tokens: [head] } : void 0;
|
|
16817
|
+
}
|
|
16488
16818
|
}
|
|
16489
16819
|
return void 0;
|
|
16490
16820
|
}
|
|
@@ -19682,73 +20012,6 @@ function suggestSig(input, options) {
|
|
|
19682
20012
|
);
|
|
19683
20013
|
}
|
|
19684
20014
|
|
|
19685
|
-
// src/utils/units.ts
|
|
19686
|
-
var MASS_UNITS = {
|
|
19687
|
-
kg: 1e6,
|
|
19688
|
-
g: 1e3,
|
|
19689
|
-
mg: 1,
|
|
19690
|
-
mcg: 1e-3,
|
|
19691
|
-
ug: 1e-3,
|
|
19692
|
-
microg: 1e-3,
|
|
19693
|
-
ng: 1e-6
|
|
19694
|
-
};
|
|
19695
|
-
var VOLUME_UNITS = {
|
|
19696
|
-
l: 1e3,
|
|
19697
|
-
dl: 100,
|
|
19698
|
-
ml: 1,
|
|
19699
|
-
ul: 1e-3,
|
|
19700
|
-
microl: 1e-3,
|
|
19701
|
-
cm3: 1,
|
|
19702
|
-
tsp: 5,
|
|
19703
|
-
tbsp: 15
|
|
19704
|
-
};
|
|
19705
|
-
function getUnitCategory(unit) {
|
|
19706
|
-
if (!unit) return "other";
|
|
19707
|
-
const u = unit.toLowerCase();
|
|
19708
|
-
if (MASS_UNITS[u] !== void 0) return "mass";
|
|
19709
|
-
if (VOLUME_UNITS[u] !== void 0) return "volume";
|
|
19710
|
-
return "other";
|
|
19711
|
-
}
|
|
19712
|
-
function getBaseUnitFactor(unit) {
|
|
19713
|
-
var _a2, _b;
|
|
19714
|
-
if (!unit) return 1;
|
|
19715
|
-
const u = unit.toLowerCase();
|
|
19716
|
-
return (_b = (_a2 = MASS_UNITS[u]) != null ? _a2 : VOLUME_UNITS[u]) != null ? _b : 1;
|
|
19717
|
-
}
|
|
19718
|
-
function convertValue(value, fromUnit, toUnit, strength) {
|
|
19719
|
-
const f = fromUnit.toLowerCase();
|
|
19720
|
-
const t = toUnit.toLowerCase();
|
|
19721
|
-
if (f === t) return value;
|
|
19722
|
-
const fCat = getUnitCategory(f);
|
|
19723
|
-
const tCat = getUnitCategory(t);
|
|
19724
|
-
if (fCat === tCat && fCat !== "other") {
|
|
19725
|
-
const fFactor = getBaseUnitFactor(f);
|
|
19726
|
-
const tFactor = getBaseUnitFactor(t);
|
|
19727
|
-
return value * fFactor / tFactor;
|
|
19728
|
-
}
|
|
19729
|
-
if (strength && (fCat === "mass" && tCat === "volume" || fCat === "volume" && tCat === "mass")) {
|
|
19730
|
-
const numUnit = strength.numerator.unit.toLowerCase();
|
|
19731
|
-
const denUnit = strength.denominator.unit.toLowerCase();
|
|
19732
|
-
const numCat = getUnitCategory(numUnit);
|
|
19733
|
-
const denCat = getUnitCategory(denUnit);
|
|
19734
|
-
if (numCat !== denCat && numCat !== "other" && denCat !== "other") {
|
|
19735
|
-
const massSide = numCat === "mass" ? strength.numerator : strength.denominator;
|
|
19736
|
-
const volSide = numCat === "volume" ? strength.numerator : strength.denominator;
|
|
19737
|
-
const bridgeDensity = massSide.value * getBaseUnitFactor(massSide.unit) / (volSide.value * getBaseUnitFactor(volSide.unit));
|
|
19738
|
-
if (fCat === "mass") {
|
|
19739
|
-
const valueMg = value * getBaseUnitFactor(fromUnit);
|
|
19740
|
-
const valueMl = valueMg / bridgeDensity;
|
|
19741
|
-
return valueMl / getBaseUnitFactor(toUnit);
|
|
19742
|
-
} else {
|
|
19743
|
-
const valueMl = value * getBaseUnitFactor(fromUnit);
|
|
19744
|
-
const valueMg = valueMl * bridgeDensity;
|
|
19745
|
-
return valueMg / getBaseUnitFactor(toUnit);
|
|
19746
|
-
}
|
|
19747
|
-
}
|
|
19748
|
-
}
|
|
19749
|
-
return null;
|
|
19750
|
-
}
|
|
19751
|
-
|
|
19752
20015
|
// src/utils/strength.ts
|
|
19753
20016
|
function parseStrength(strength, context) {
|
|
19754
20017
|
var _a2, _b, _c;
|
|
@@ -20188,6 +20451,24 @@ function estimateIngredientQuantity(quantity, context) {
|
|
|
20188
20451
|
if (!(numerator == null ? void 0 : numerator.unit) || numerator.value === void 0 || !(denominator == null ? void 0 : denominator.unit) || denominator.value === void 0) {
|
|
20189
20452
|
return void 0;
|
|
20190
20453
|
}
|
|
20454
|
+
const quantityCategory = getUnitCategory(quantity.unit);
|
|
20455
|
+
const denominatorCategory = getUnitCategory(denominator.unit);
|
|
20456
|
+
if (quantityCategory !== "other" && quantityCategory === denominatorCategory) {
|
|
20457
|
+
const quantityInDenominatorBase = quantity.value * getBaseUnitFactor(quantity.unit);
|
|
20458
|
+
const denominatorInBase = denominator.value * getBaseUnitFactor(denominator.unit);
|
|
20459
|
+
const numeratorInBase = numerator.value * getBaseUnitFactor(numerator.unit);
|
|
20460
|
+
if (denominatorInBase !== 0) {
|
|
20461
|
+
return {
|
|
20462
|
+
value: roundCalculatedUnits(
|
|
20463
|
+
quantityInDenominatorBase * numeratorInBase / denominatorInBase / getBaseUnitFactor(numerator.unit)
|
|
20464
|
+
),
|
|
20465
|
+
unit: numerator.unit,
|
|
20466
|
+
confidence: quantity.confidence,
|
|
20467
|
+
basis: quantity.basis,
|
|
20468
|
+
source: quantity.source
|
|
20469
|
+
};
|
|
20470
|
+
}
|
|
20471
|
+
}
|
|
20191
20472
|
const converted = convertValue(
|
|
20192
20473
|
quantity.value,
|
|
20193
20474
|
quantity.unit,
|
package/dist/index.js
CHANGED
|
@@ -2152,6 +2152,14 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
|
|
|
2152
2152
|
names: ["axilla", "axillae", "armpit", "armpits"],
|
|
2153
2153
|
definition: { coding: { code: "34797008", display: "Axilla structure" }, routeHint: RouteCode["Topical route"] }
|
|
2154
2154
|
},
|
|
2155
|
+
{
|
|
2156
|
+
names: ["axillary hair", "armpit hair", "armpit hairs", "underarm hair", "underarm hairs"],
|
|
2157
|
+
definition: {
|
|
2158
|
+
coding: { code: "75703003", display: "Structure of hair of axilla" },
|
|
2159
|
+
text: "axillary hair",
|
|
2160
|
+
routeHint: RouteCode["Topical route"]
|
|
2161
|
+
}
|
|
2162
|
+
},
|
|
2155
2163
|
{
|
|
2156
2164
|
names: ["groin"],
|
|
2157
2165
|
definition: { coding: { code: "26893007", display: "Inguinal region structure" }, routeHint: RouteCode["Topical route"] }
|
|
@@ -2222,6 +2230,71 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
|
|
|
2222
2230
|
names: ["face"],
|
|
2223
2231
|
definition: { coding: { code: "89545001", display: "Face" }, routeHint: RouteCode["Topical route"] }
|
|
2224
2232
|
},
|
|
2233
|
+
{
|
|
2234
|
+
names: ["eyebrow", "brow"],
|
|
2235
|
+
definition: {
|
|
2236
|
+
coding: { code: "392262008", display: "Eyebrow structure" },
|
|
2237
|
+
text: "eyebrow",
|
|
2238
|
+
routeHint: RouteCode["Topical route"]
|
|
2239
|
+
}
|
|
2240
|
+
},
|
|
2241
|
+
{
|
|
2242
|
+
names: ["eyebrows", "brows"],
|
|
2243
|
+
definition: {
|
|
2244
|
+
coding: { code: "392262008", display: "Eyebrow structure" },
|
|
2245
|
+
text: "eyebrows",
|
|
2246
|
+
routeHint: RouteCode["Topical route"]
|
|
2247
|
+
}
|
|
2248
|
+
},
|
|
2249
|
+
{
|
|
2250
|
+
names: ["left eyebrow", "left brow"],
|
|
2251
|
+
definition: {
|
|
2252
|
+
coding: { code: "722011002", display: "Structure of eyebrow of left eye region" },
|
|
2253
|
+
text: "left eyebrow",
|
|
2254
|
+
routeHint: RouteCode["Topical route"]
|
|
2255
|
+
}
|
|
2256
|
+
},
|
|
2257
|
+
{
|
|
2258
|
+
names: ["right eyebrow", "right brow"],
|
|
2259
|
+
definition: {
|
|
2260
|
+
coding: { code: "722012009", display: "Structure of eyebrow of right eye region" },
|
|
2261
|
+
text: "right eyebrow",
|
|
2262
|
+
routeHint: RouteCode["Topical route"]
|
|
2263
|
+
}
|
|
2264
|
+
},
|
|
2265
|
+
{
|
|
2266
|
+
names: [
|
|
2267
|
+
"both eyebrows",
|
|
2268
|
+
"bilateral eyebrows",
|
|
2269
|
+
"each eyebrow",
|
|
2270
|
+
"left and right eyebrow",
|
|
2271
|
+
"left and right eyebrows",
|
|
2272
|
+
"right and left eyebrow",
|
|
2273
|
+
"right and left eyebrows",
|
|
2274
|
+
"left right eyebrow",
|
|
2275
|
+
"left right eyebrows"
|
|
2276
|
+
],
|
|
2277
|
+
definition: {
|
|
2278
|
+
coding: {
|
|
2279
|
+
code: buildSnomedBodySiteLateralityPostcoordinationCode(
|
|
2280
|
+
"392262008",
|
|
2281
|
+
SNOMED_CT_BILATERAL_QUALIFIER_CODE
|
|
2282
|
+
),
|
|
2283
|
+
display: "both eyebrows"
|
|
2284
|
+
},
|
|
2285
|
+
text: "both eyebrows",
|
|
2286
|
+
administrationTargetCount: 2,
|
|
2287
|
+
routeHint: RouteCode["Topical route"]
|
|
2288
|
+
}
|
|
2289
|
+
},
|
|
2290
|
+
{
|
|
2291
|
+
names: ["eyebrow hair", "eyebrow hairs", "brow hair", "brow hairs"],
|
|
2292
|
+
definition: {
|
|
2293
|
+
coding: { code: "392261001", display: "Hair structure of eyebrow" },
|
|
2294
|
+
text: "eyebrow hair",
|
|
2295
|
+
routeHint: RouteCode["Topical route"]
|
|
2296
|
+
}
|
|
2297
|
+
},
|
|
2225
2298
|
{
|
|
2226
2299
|
names: ["eyelid", "eyelids"],
|
|
2227
2300
|
definition: { coding: { code: "80243003", display: "Eyelid" }, routeHint: RouteCode["Ophthalmic route"] }
|
|
@@ -2342,6 +2415,30 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
|
|
|
2342
2415
|
names: ["perineum"],
|
|
2343
2416
|
definition: { coding: { code: "243990009", display: "Entire perineum" }, routeHint: RouteCode["Topical route"] }
|
|
2344
2417
|
},
|
|
2418
|
+
{
|
|
2419
|
+
names: ["mustache", "moustache", "mustache hair", "moustache hair"],
|
|
2420
|
+
definition: {
|
|
2421
|
+
coding: { code: "256925006", display: "Structure of hair of mustache" },
|
|
2422
|
+
text: "mustache",
|
|
2423
|
+
routeHint: RouteCode["Topical route"]
|
|
2424
|
+
}
|
|
2425
|
+
},
|
|
2426
|
+
{
|
|
2427
|
+
names: ["beard", "beard hair", "facial hair"],
|
|
2428
|
+
definition: {
|
|
2429
|
+
coding: { code: "367576007", display: "Structure of beard hair" },
|
|
2430
|
+
text: "beard",
|
|
2431
|
+
routeHint: RouteCode["Topical route"]
|
|
2432
|
+
}
|
|
2433
|
+
},
|
|
2434
|
+
{
|
|
2435
|
+
names: ["pubic hair"],
|
|
2436
|
+
definition: {
|
|
2437
|
+
coding: { code: "75776007", display: "Structure of hair of pubis" },
|
|
2438
|
+
text: "pubic hair",
|
|
2439
|
+
routeHint: RouteCode["Topical route"]
|
|
2440
|
+
}
|
|
2441
|
+
},
|
|
2345
2442
|
{
|
|
2346
2443
|
names: ["skin"],
|
|
2347
2444
|
definition: { coding: { code: "181469002", display: "Entire skin" }, routeHint: RouteCode["Topical route"] }
|
|
@@ -2352,6 +2449,38 @@ var DEFAULT_BODY_SITE_SNOMED_SOURCE = [
|
|
|
2352
2449
|
coding: { code: "386045008", display: "Hair structure (body structure)" },
|
|
2353
2450
|
routeHint: RouteCode["Topical route"]
|
|
2354
2451
|
}
|
|
2452
|
+
},
|
|
2453
|
+
{
|
|
2454
|
+
names: ["joint"],
|
|
2455
|
+
definition: {
|
|
2456
|
+
coding: { code: "39352004", display: "Joint structure" },
|
|
2457
|
+
text: "joint",
|
|
2458
|
+
routeHint: RouteCode["Topical route"]
|
|
2459
|
+
}
|
|
2460
|
+
},
|
|
2461
|
+
{
|
|
2462
|
+
names: ["joints"],
|
|
2463
|
+
definition: {
|
|
2464
|
+
coding: { code: "81087007", display: "Joints" },
|
|
2465
|
+
text: "joints",
|
|
2466
|
+
routeHint: RouteCode["Topical route"]
|
|
2467
|
+
}
|
|
2468
|
+
},
|
|
2469
|
+
{
|
|
2470
|
+
names: ["finger joint", "finger joints"],
|
|
2471
|
+
definition: {
|
|
2472
|
+
coding: { code: "125682004", display: "Finger joint structure" },
|
|
2473
|
+
text: "finger joints",
|
|
2474
|
+
routeHint: RouteCode["Topical route"]
|
|
2475
|
+
}
|
|
2476
|
+
},
|
|
2477
|
+
{
|
|
2478
|
+
names: ["knuckle", "knuckles"],
|
|
2479
|
+
definition: {
|
|
2480
|
+
coding: { code: "70420003", display: "Metacarpophalangeal joint structure" },
|
|
2481
|
+
text: "knuckles",
|
|
2482
|
+
routeHint: RouteCode["Topical route"]
|
|
2483
|
+
}
|
|
2355
2484
|
}
|
|
2356
2485
|
];
|
|
2357
2486
|
var DEFAULT_BODY_SITE_SNOMED = objectFromEntries(
|
|
@@ -4263,6 +4392,7 @@ var lexical_classes_default = {
|
|
|
4263
4392
|
"Use shampoo": "\u0E2A\u0E23\u0E30"
|
|
4264
4393
|
},
|
|
4265
4394
|
compoundDoseUnits: [
|
|
4395
|
+
{ head: "cm", tails: [], requiresSiteContext: true, unit: "cm line" },
|
|
4266
4396
|
{ head: "cm", tails: ["ribbon", "ribbons"], unit: "cm ribbon" },
|
|
4267
4397
|
{ head: "cm", tails: ["strip", "strips"], unit: "cm strip" },
|
|
4268
4398
|
{ head: "cm", tails: ["line", "lines"], unit: "cm line" },
|
|
@@ -4271,6 +4401,8 @@ var lexical_classes_default = {
|
|
|
4271
4401
|
{ head: "fingertip", tails: ["unit", "units"], unit: "FTU" },
|
|
4272
4402
|
{ head: "eye", tails: ["drop", "drops"], unit: "drop" },
|
|
4273
4403
|
{ head: "pea-sized", tails: ["amount"], unit: "pea-sized amount" },
|
|
4404
|
+
{ head: "pea-size", tails: ["amount"], unit: "pea-sized amount" },
|
|
4405
|
+
{ head: "peasize", tails: [], tailSequences: [[]], unit: "pea-sized amount" },
|
|
4274
4406
|
{ head: "\u0E40\u0E21\u0E47\u0E14\u0E16\u0E31\u0E48\u0E27", tails: [], unit: "pea-sized amount" },
|
|
4275
4407
|
{ head: "\u0E40\u0E21\u0E47\u0E14\u0E16\u0E31\u0E48\u0E27\u0E40\u0E02\u0E35\u0E22\u0E27", tails: [], unit: "pea-sized amount" },
|
|
4276
4408
|
{ head: "\u0E40\u0E21\u0E25\u0E47\u0E14\u0E16\u0E31\u0E48\u0E27", tails: [], unit: "pea-sized amount" },
|
|
@@ -4280,7 +4412,7 @@ var lexical_classes_default = {
|
|
|
4280
4412
|
{
|
|
4281
4413
|
head: "pea",
|
|
4282
4414
|
tails: [],
|
|
4283
|
-
tailSequences: [["sized", "amount"], ["size", "amount"]],
|
|
4415
|
+
tailSequences: [["sized", "amount"], ["size", "amount"], ["amount"], ["sized"], ["size"], []],
|
|
4284
4416
|
unit: "pea-sized amount"
|
|
4285
4417
|
},
|
|
4286
4418
|
{ head: "hand", tails: ["print", "prints"], unit: "handprint" },
|
|
@@ -14926,6 +15058,11 @@ var unit_terminology_default = {
|
|
|
14926
15058
|
unit: "pea-sized amount",
|
|
14927
15059
|
kind: "product_specific_amount",
|
|
14928
15060
|
aliases: [
|
|
15061
|
+
"pea",
|
|
15062
|
+
"pea amount",
|
|
15063
|
+
"peasize",
|
|
15064
|
+
"pea-size",
|
|
15065
|
+
"pea size",
|
|
14929
15066
|
"pea sized amount",
|
|
14930
15067
|
"pea-sized",
|
|
14931
15068
|
"pea sized",
|
|
@@ -15022,10 +15159,96 @@ var unit_terminology_default = {
|
|
|
15022
15159
|
]
|
|
15023
15160
|
};
|
|
15024
15161
|
|
|
15162
|
+
// src/utils/units.ts
|
|
15163
|
+
var MASS_UNITS = {
|
|
15164
|
+
kg: 1e6,
|
|
15165
|
+
g: 1e3,
|
|
15166
|
+
mg: 1,
|
|
15167
|
+
mcg: 1e-3,
|
|
15168
|
+
ug: 1e-3,
|
|
15169
|
+
microg: 1e-3,
|
|
15170
|
+
ng: 1e-6
|
|
15171
|
+
};
|
|
15172
|
+
var VOLUME_UNITS = {
|
|
15173
|
+
l: 1e3,
|
|
15174
|
+
dl: 100,
|
|
15175
|
+
ml: 1,
|
|
15176
|
+
ul: 1e-3,
|
|
15177
|
+
microl: 1e-3,
|
|
15178
|
+
cm3: 1,
|
|
15179
|
+
tsp: 5,
|
|
15180
|
+
tbsp: 15
|
|
15181
|
+
};
|
|
15182
|
+
function getUnitCategory(unit) {
|
|
15183
|
+
if (!unit) return "other";
|
|
15184
|
+
const u = unit.toLowerCase();
|
|
15185
|
+
if (MASS_UNITS[u] !== void 0) return "mass";
|
|
15186
|
+
if (VOLUME_UNITS[u] !== void 0) return "volume";
|
|
15187
|
+
return "other";
|
|
15188
|
+
}
|
|
15189
|
+
function getBaseUnitFactor(unit) {
|
|
15190
|
+
var _a2, _b;
|
|
15191
|
+
if (!unit) return 1;
|
|
15192
|
+
const u = unit.toLowerCase();
|
|
15193
|
+
return (_b = (_a2 = MASS_UNITS[u]) != null ? _a2 : VOLUME_UNITS[u]) != null ? _b : 1;
|
|
15194
|
+
}
|
|
15195
|
+
function convertValue(value, fromUnit, toUnit, strength) {
|
|
15196
|
+
const f = fromUnit.toLowerCase();
|
|
15197
|
+
const t = toUnit.toLowerCase();
|
|
15198
|
+
if (f === t) return value;
|
|
15199
|
+
const fCat = getUnitCategory(f);
|
|
15200
|
+
const tCat = getUnitCategory(t);
|
|
15201
|
+
if (fCat === tCat && fCat !== "other") {
|
|
15202
|
+
const fFactor = getBaseUnitFactor(f);
|
|
15203
|
+
const tFactor = getBaseUnitFactor(t);
|
|
15204
|
+
return value * fFactor / tFactor;
|
|
15205
|
+
}
|
|
15206
|
+
if (strength && (fCat === "mass" && tCat === "volume" || fCat === "volume" && tCat === "mass")) {
|
|
15207
|
+
const numUnit = strength.numerator.unit.toLowerCase();
|
|
15208
|
+
const denUnit = strength.denominator.unit.toLowerCase();
|
|
15209
|
+
const numCat = getUnitCategory(numUnit);
|
|
15210
|
+
const denCat = getUnitCategory(denUnit);
|
|
15211
|
+
if (numCat !== denCat && numCat !== "other" && denCat !== "other") {
|
|
15212
|
+
const massSide = numCat === "mass" ? strength.numerator : strength.denominator;
|
|
15213
|
+
const volSide = numCat === "volume" ? strength.numerator : strength.denominator;
|
|
15214
|
+
const bridgeDensity = massSide.value * getBaseUnitFactor(massSide.unit) / (volSide.value * getBaseUnitFactor(volSide.unit));
|
|
15215
|
+
if (fCat === "mass") {
|
|
15216
|
+
const valueMg = value * getBaseUnitFactor(fromUnit);
|
|
15217
|
+
const valueMl = valueMg / bridgeDensity;
|
|
15218
|
+
return valueMl / getBaseUnitFactor(toUnit);
|
|
15219
|
+
} else {
|
|
15220
|
+
const valueMl = value * getBaseUnitFactor(fromUnit);
|
|
15221
|
+
const valueMg = valueMl * bridgeDensity;
|
|
15222
|
+
return valueMg / getBaseUnitFactor(toUnit);
|
|
15223
|
+
}
|
|
15224
|
+
}
|
|
15225
|
+
}
|
|
15226
|
+
return null;
|
|
15227
|
+
}
|
|
15228
|
+
|
|
15025
15229
|
// src/unit-lexicon.ts
|
|
15026
15230
|
var HOUSEHOLD_VOLUME_UNIT_SET = new Set(
|
|
15027
15231
|
HOUSEHOLD_VOLUME_UNITS.map((unit) => unit.toLowerCase())
|
|
15028
15232
|
);
|
|
15233
|
+
var MASS_DISPENSED_SEMISOLID_DOSAGE_FORMS = /* @__PURE__ */ new Set([
|
|
15234
|
+
"cream",
|
|
15235
|
+
"ointment",
|
|
15236
|
+
"gel",
|
|
15237
|
+
"paste",
|
|
15238
|
+
"cutaneous paste",
|
|
15239
|
+
"vaginal cream",
|
|
15240
|
+
"vaginal gel",
|
|
15241
|
+
"oral gel",
|
|
15242
|
+
"oral paste",
|
|
15243
|
+
"oromucosal gel",
|
|
15244
|
+
"oromucosal paste",
|
|
15245
|
+
"dental gel",
|
|
15246
|
+
"dental paste",
|
|
15247
|
+
"gingival gel",
|
|
15248
|
+
"nasal gel",
|
|
15249
|
+
"eye gel",
|
|
15250
|
+
"eye ointment"
|
|
15251
|
+
]);
|
|
15029
15252
|
var DOSE_UNIT_TERMINOLOGY = unit_terminology_default.terms;
|
|
15030
15253
|
var DOSE_UNIT_TERMINOLOGY_BY_KEY = /* @__PURE__ */ new Map();
|
|
15031
15254
|
var DISCRETE_UNIT_KINDS = /* @__PURE__ */ new Set([
|
|
@@ -15088,6 +15311,47 @@ function unitApproximationOverride(unit, context) {
|
|
|
15088
15311
|
}
|
|
15089
15312
|
return void 0;
|
|
15090
15313
|
}
|
|
15314
|
+
function normalizeDosageFormKey(form) {
|
|
15315
|
+
var _a2;
|
|
15316
|
+
const normalized = form == null ? void 0 : form.trim().toLowerCase();
|
|
15317
|
+
if (!normalized) {
|
|
15318
|
+
return void 0;
|
|
15319
|
+
}
|
|
15320
|
+
return (_a2 = KNOWN_DOSAGE_FORMS_TO_DOSE[normalized]) != null ? _a2 : normalized;
|
|
15321
|
+
}
|
|
15322
|
+
function getPreferredMassApproximationUnit(context) {
|
|
15323
|
+
var _a2;
|
|
15324
|
+
const normalizedDosageForm = normalizeDosageFormKey(context == null ? void 0 : context.dosageForm);
|
|
15325
|
+
if (!normalizedDosageForm || !MASS_DISPENSED_SEMISOLID_DOSAGE_FORMS.has(normalizedDosageForm)) {
|
|
15326
|
+
return void 0;
|
|
15327
|
+
}
|
|
15328
|
+
const containerUnit = (_a2 = context == null ? void 0 : context.containerUnit) == null ? void 0 : _a2.trim();
|
|
15329
|
+
if (containerUnit && getUnitCategory(containerUnit) === "mass") {
|
|
15330
|
+
return containerUnit;
|
|
15331
|
+
}
|
|
15332
|
+
const defaultUnit = normalizedDosageForm ? DEFAULT_UNIT_BY_NORMALIZED_FORM[normalizedDosageForm] : void 0;
|
|
15333
|
+
return defaultUnit && getUnitCategory(defaultUnit) === "mass" ? defaultUnit : void 0;
|
|
15334
|
+
}
|
|
15335
|
+
function bridgeApproximationToMassDispensedTopical(approximation, context) {
|
|
15336
|
+
const preferredMassUnit = getPreferredMassApproximationUnit(context);
|
|
15337
|
+
if (!preferredMassUnit || getUnitCategory(approximation.unit) !== "volume") {
|
|
15338
|
+
return approximation;
|
|
15339
|
+
}
|
|
15340
|
+
const sourceVolumeFactor = VOLUME_UNITS[approximation.unit.toLowerCase()];
|
|
15341
|
+
const targetMassFactor = MASS_UNITS[preferredMassUnit.toLowerCase()];
|
|
15342
|
+
if (!sourceVolumeFactor || !targetMassFactor) {
|
|
15343
|
+
return approximation;
|
|
15344
|
+
}
|
|
15345
|
+
const valueMl = approximation.value * sourceVolumeFactor;
|
|
15346
|
+
const valueG = valueMl;
|
|
15347
|
+
const convertedValue = valueG * MASS_UNITS.g / targetMassFactor;
|
|
15348
|
+
const bridgeBasis = "Mass-dispensed semisolid topical default bridge assumes 1 mL approximately equals 1 g unless a product-specific override is provided";
|
|
15349
|
+
return __spreadProps(__spreadValues({}, approximation), {
|
|
15350
|
+
value: convertedValue,
|
|
15351
|
+
unit: preferredMassUnit,
|
|
15352
|
+
basis: approximation.basis ? `${approximation.basis}; ${bridgeBasis}` : bridgeBasis
|
|
15353
|
+
});
|
|
15354
|
+
}
|
|
15091
15355
|
function getDoseUnitTerminologyEntry(unit) {
|
|
15092
15356
|
var _a2;
|
|
15093
15357
|
if (!unit) {
|
|
@@ -15109,7 +15373,11 @@ function getDoseUnitApproximation(unit, context) {
|
|
|
15109
15373
|
if (override) {
|
|
15110
15374
|
return override;
|
|
15111
15375
|
}
|
|
15112
|
-
|
|
15376
|
+
const approximation = terminologyEntry == null ? void 0 : terminologyEntry.approximateQuantity;
|
|
15377
|
+
if (!approximation) {
|
|
15378
|
+
return void 0;
|
|
15379
|
+
}
|
|
15380
|
+
return bridgeApproximationToMassDispensedTopical(approximation, context);
|
|
15113
15381
|
}
|
|
15114
15382
|
function getDoseUnitSemantics(unit, context) {
|
|
15115
15383
|
if (!unit) {
|
|
@@ -16356,6 +16624,64 @@ function productLexicalRule() {
|
|
|
16356
16624
|
}
|
|
16357
16625
|
function matchCompoundDoseUnit(context, start, lower) {
|
|
16358
16626
|
var _a2;
|
|
16627
|
+
const MAX_SITE_CONTEXT_TOKENS = 3;
|
|
16628
|
+
const hasSiteMeaning = (lowerValue) => {
|
|
16629
|
+
var _a3, _b, _c;
|
|
16630
|
+
return Boolean(
|
|
16631
|
+
lowerValue && (DEFAULT_BODY_SITE_SNOMED[normalizeBodySiteKey(lowerValue)] || resolveBodySitePhrase(lowerValue, (_a3 = context.options) == null ? void 0 : _a3.siteCodeMap, {
|
|
16632
|
+
bodySiteContext: (_c = (_b = context.options) == null ? void 0 : _b.context) == null ? void 0 : _c.bodySiteContext
|
|
16633
|
+
}))
|
|
16634
|
+
);
|
|
16635
|
+
};
|
|
16636
|
+
const collectForwardSitePhrases = (phraseStart) => {
|
|
16637
|
+
const parts = [];
|
|
16638
|
+
const phrases = [];
|
|
16639
|
+
for (let index = phraseStart; index < context.limit && parts.length < MAX_SITE_CONTEXT_TOKENS; index += 1) {
|
|
16640
|
+
const token = context.tokens[index];
|
|
16641
|
+
if (!token || context.state.consumed.has(token.index)) {
|
|
16642
|
+
break;
|
|
16643
|
+
}
|
|
16644
|
+
const lowerValue = normalizeTokenLower(token);
|
|
16645
|
+
if (!lowerValue || isPunctuation(lowerValue)) {
|
|
16646
|
+
break;
|
|
16647
|
+
}
|
|
16648
|
+
parts.push(lowerValue);
|
|
16649
|
+
phrases.push(parts.join(" "));
|
|
16650
|
+
}
|
|
16651
|
+
return phrases;
|
|
16652
|
+
};
|
|
16653
|
+
const matchesSiteContext = () => {
|
|
16654
|
+
const next = context.tokens[start + 1];
|
|
16655
|
+
const nextLower = next && !context.state.consumed.has(next.index) ? normalizeTokenLower(next) : void 0;
|
|
16656
|
+
if (nextLower && (ROUTE_SITE_PREPOSITIONS.has(nextLower) || SITE_ANCHORS.has(nextLower))) {
|
|
16657
|
+
const followingSitePhrases = collectForwardSitePhrases(start + 2);
|
|
16658
|
+
if (followingSitePhrases.some((phrase) => hasSiteMeaning(phrase))) {
|
|
16659
|
+
return true;
|
|
16660
|
+
}
|
|
16661
|
+
}
|
|
16662
|
+
const previous = context.tokens[start - 1];
|
|
16663
|
+
const previousLower = previous && !context.state.consumed.has(previous.index) ? normalizeTokenLower(previous) : void 0;
|
|
16664
|
+
const trailingNumberBeforeUnit = Boolean(
|
|
16665
|
+
previousLower && /^[0-9]+(?:\.[0-9]+)?$/.test(previousLower)
|
|
16666
|
+
);
|
|
16667
|
+
const siteEnd = start - (trailingNumberBeforeUnit ? 2 : 1);
|
|
16668
|
+
for (let phraseLength = 1; phraseLength <= MAX_SITE_CONTEXT_TOKENS; phraseLength += 1) {
|
|
16669
|
+
const phraseStart = siteEnd - phraseLength + 1;
|
|
16670
|
+
if (phraseStart < 0) {
|
|
16671
|
+
break;
|
|
16672
|
+
}
|
|
16673
|
+
const precedingAnchor = context.tokens[phraseStart - 1];
|
|
16674
|
+
const precedingAnchorLower = precedingAnchor && !context.state.consumed.has(precedingAnchor.index) ? normalizeTokenLower(precedingAnchor) : void 0;
|
|
16675
|
+
if (!precedingAnchorLower || !ROUTE_SITE_PREPOSITIONS.has(precedingAnchorLower) && !SITE_ANCHORS.has(precedingAnchorLower)) {
|
|
16676
|
+
continue;
|
|
16677
|
+
}
|
|
16678
|
+
const phrase = collectForwardSitePhrases(phraseStart)[phraseLength - 1];
|
|
16679
|
+
if (hasSiteMeaning(phrase)) {
|
|
16680
|
+
return true;
|
|
16681
|
+
}
|
|
16682
|
+
}
|
|
16683
|
+
return false;
|
|
16684
|
+
};
|
|
16359
16685
|
for (const compound of COMPOUND_DOSE_UNITS) {
|
|
16360
16686
|
if (compound.head !== lower) {
|
|
16361
16687
|
continue;
|
|
@@ -16385,6 +16711,10 @@ function matchCompoundDoseUnit(context, start, lower) {
|
|
|
16385
16711
|
return head ? { unit: compound.unit, tokens: [head, next] } : void 0;
|
|
16386
16712
|
}
|
|
16387
16713
|
}
|
|
16714
|
+
if (compound.requiresSiteContext && matchesSiteContext()) {
|
|
16715
|
+
const head = context.tokens[start];
|
|
16716
|
+
return head ? { unit: compound.unit, tokens: [head] } : void 0;
|
|
16717
|
+
}
|
|
16388
16718
|
}
|
|
16389
16719
|
return void 0;
|
|
16390
16720
|
}
|
|
@@ -19582,73 +19912,6 @@ function suggestSig(input, options) {
|
|
|
19582
19912
|
);
|
|
19583
19913
|
}
|
|
19584
19914
|
|
|
19585
|
-
// src/utils/units.ts
|
|
19586
|
-
var MASS_UNITS = {
|
|
19587
|
-
kg: 1e6,
|
|
19588
|
-
g: 1e3,
|
|
19589
|
-
mg: 1,
|
|
19590
|
-
mcg: 1e-3,
|
|
19591
|
-
ug: 1e-3,
|
|
19592
|
-
microg: 1e-3,
|
|
19593
|
-
ng: 1e-6
|
|
19594
|
-
};
|
|
19595
|
-
var VOLUME_UNITS = {
|
|
19596
|
-
l: 1e3,
|
|
19597
|
-
dl: 100,
|
|
19598
|
-
ml: 1,
|
|
19599
|
-
ul: 1e-3,
|
|
19600
|
-
microl: 1e-3,
|
|
19601
|
-
cm3: 1,
|
|
19602
|
-
tsp: 5,
|
|
19603
|
-
tbsp: 15
|
|
19604
|
-
};
|
|
19605
|
-
function getUnitCategory(unit) {
|
|
19606
|
-
if (!unit) return "other";
|
|
19607
|
-
const u = unit.toLowerCase();
|
|
19608
|
-
if (MASS_UNITS[u] !== void 0) return "mass";
|
|
19609
|
-
if (VOLUME_UNITS[u] !== void 0) return "volume";
|
|
19610
|
-
return "other";
|
|
19611
|
-
}
|
|
19612
|
-
function getBaseUnitFactor(unit) {
|
|
19613
|
-
var _a2, _b;
|
|
19614
|
-
if (!unit) return 1;
|
|
19615
|
-
const u = unit.toLowerCase();
|
|
19616
|
-
return (_b = (_a2 = MASS_UNITS[u]) != null ? _a2 : VOLUME_UNITS[u]) != null ? _b : 1;
|
|
19617
|
-
}
|
|
19618
|
-
function convertValue(value, fromUnit, toUnit, strength) {
|
|
19619
|
-
const f = fromUnit.toLowerCase();
|
|
19620
|
-
const t = toUnit.toLowerCase();
|
|
19621
|
-
if (f === t) return value;
|
|
19622
|
-
const fCat = getUnitCategory(f);
|
|
19623
|
-
const tCat = getUnitCategory(t);
|
|
19624
|
-
if (fCat === tCat && fCat !== "other") {
|
|
19625
|
-
const fFactor = getBaseUnitFactor(f);
|
|
19626
|
-
const tFactor = getBaseUnitFactor(t);
|
|
19627
|
-
return value * fFactor / tFactor;
|
|
19628
|
-
}
|
|
19629
|
-
if (strength && (fCat === "mass" && tCat === "volume" || fCat === "volume" && tCat === "mass")) {
|
|
19630
|
-
const numUnit = strength.numerator.unit.toLowerCase();
|
|
19631
|
-
const denUnit = strength.denominator.unit.toLowerCase();
|
|
19632
|
-
const numCat = getUnitCategory(numUnit);
|
|
19633
|
-
const denCat = getUnitCategory(denUnit);
|
|
19634
|
-
if (numCat !== denCat && numCat !== "other" && denCat !== "other") {
|
|
19635
|
-
const massSide = numCat === "mass" ? strength.numerator : strength.denominator;
|
|
19636
|
-
const volSide = numCat === "volume" ? strength.numerator : strength.denominator;
|
|
19637
|
-
const bridgeDensity = massSide.value * getBaseUnitFactor(massSide.unit) / (volSide.value * getBaseUnitFactor(volSide.unit));
|
|
19638
|
-
if (fCat === "mass") {
|
|
19639
|
-
const valueMg = value * getBaseUnitFactor(fromUnit);
|
|
19640
|
-
const valueMl = valueMg / bridgeDensity;
|
|
19641
|
-
return valueMl / getBaseUnitFactor(toUnit);
|
|
19642
|
-
} else {
|
|
19643
|
-
const valueMl = value * getBaseUnitFactor(fromUnit);
|
|
19644
|
-
const valueMg = valueMl * bridgeDensity;
|
|
19645
|
-
return valueMg / getBaseUnitFactor(toUnit);
|
|
19646
|
-
}
|
|
19647
|
-
}
|
|
19648
|
-
}
|
|
19649
|
-
return null;
|
|
19650
|
-
}
|
|
19651
|
-
|
|
19652
19915
|
// src/utils/strength.ts
|
|
19653
19916
|
function parseStrength(strength, context) {
|
|
19654
19917
|
var _a2, _b, _c;
|
|
@@ -20088,6 +20351,24 @@ function estimateIngredientQuantity(quantity, context) {
|
|
|
20088
20351
|
if (!(numerator == null ? void 0 : numerator.unit) || numerator.value === void 0 || !(denominator == null ? void 0 : denominator.unit) || denominator.value === void 0) {
|
|
20089
20352
|
return void 0;
|
|
20090
20353
|
}
|
|
20354
|
+
const quantityCategory = getUnitCategory(quantity.unit);
|
|
20355
|
+
const denominatorCategory = getUnitCategory(denominator.unit);
|
|
20356
|
+
if (quantityCategory !== "other" && quantityCategory === denominatorCategory) {
|
|
20357
|
+
const quantityInDenominatorBase = quantity.value * getBaseUnitFactor(quantity.unit);
|
|
20358
|
+
const denominatorInBase = denominator.value * getBaseUnitFactor(denominator.unit);
|
|
20359
|
+
const numeratorInBase = numerator.value * getBaseUnitFactor(numerator.unit);
|
|
20360
|
+
if (denominatorInBase !== 0) {
|
|
20361
|
+
return {
|
|
20362
|
+
value: roundCalculatedUnits(
|
|
20363
|
+
quantityInDenominatorBase * numeratorInBase / denominatorInBase / getBaseUnitFactor(numerator.unit)
|
|
20364
|
+
),
|
|
20365
|
+
unit: numerator.unit,
|
|
20366
|
+
confidence: quantity.confidence,
|
|
20367
|
+
basis: quantity.basis,
|
|
20368
|
+
source: quantity.source
|
|
20369
|
+
};
|
|
20370
|
+
}
|
|
20371
|
+
}
|
|
20091
20372
|
const converted = convertValue(
|
|
20092
20373
|
quantity.value,
|
|
20093
20374
|
quantity.unit,
|
package/dist/types.d.ts
CHANGED
|
@@ -301,9 +301,14 @@ export declare enum FhirDayOfWeek {
|
|
|
301
301
|
Saturday = "sat",
|
|
302
302
|
Sunday = "sun"
|
|
303
303
|
}
|
|
304
|
+
export interface FhirPeriod {
|
|
305
|
+
start?: string;
|
|
306
|
+
end?: string;
|
|
307
|
+
}
|
|
304
308
|
export interface FhirTimingRepeat {
|
|
305
309
|
count?: number;
|
|
306
310
|
boundsDuration?: FhirQuantity;
|
|
311
|
+
boundsPeriod?: FhirPeriod;
|
|
307
312
|
boundsRange?: FhirRange;
|
|
308
313
|
frequency?: number;
|
|
309
314
|
frequencyMax?: number;
|