ezmedicationinput 0.1.15 → 0.1.17
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 +56 -0
- package/dist/fhir.js +62 -7
- package/dist/format.js +39 -2
- package/dist/index.js +78 -4
- package/dist/internal-types.d.ts +12 -1
- package/dist/maps.d.ts +17 -1
- package/dist/maps.js +315 -1
- package/dist/parser.d.ts +2 -0
- package/dist/parser.js +817 -36
- package/dist/types.d.ts +65 -1
- package/package.json +1 -1
package/dist/parser.js
CHANGED
|
@@ -11,6 +11,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.tokenize = tokenize;
|
|
13
13
|
exports.parseInternal = parseInternal;
|
|
14
|
+
exports.applyPrnReasonCoding = applyPrnReasonCoding;
|
|
15
|
+
exports.applyPrnReasonCodingAsync = applyPrnReasonCodingAsync;
|
|
14
16
|
exports.applySiteCoding = applySiteCoding;
|
|
15
17
|
exports.applySiteCodingAsync = applySiteCodingAsync;
|
|
16
18
|
const maps_1 = require("./maps");
|
|
@@ -317,7 +319,7 @@ const OPHTHALMIC_CONTEXT_TOKENS = new Set([
|
|
|
317
319
|
"be"
|
|
318
320
|
]);
|
|
319
321
|
function normalizeTokenLower(token) {
|
|
320
|
-
return token.lower.replace(/[.{}]/g, "");
|
|
322
|
+
return token.lower.replace(/[.{};]/g, "");
|
|
321
323
|
}
|
|
322
324
|
function hasOphthalmicContextHint(tokens, index) {
|
|
323
325
|
for (let offset = -3; offset <= 3; offset++) {
|
|
@@ -758,8 +760,9 @@ const SITE_UNIT_ROUTE_HINTS = [
|
|
|
758
760
|
{ pattern: /\bvaginal\b/i, route: types_1.RouteCode["Per vagina"] }
|
|
759
761
|
];
|
|
760
762
|
function tokenize(input) {
|
|
761
|
-
const separators = /[()
|
|
763
|
+
const separators = /[(),;]/g;
|
|
762
764
|
let normalized = input.trim().replace(separators, " ");
|
|
765
|
+
normalized = normalized.replace(/\s-\s/g, " ; ");
|
|
763
766
|
normalized = normalized.replace(/(\d+(?:\.\d+)?)\s*\/\s*(d|day|days|wk|w|week|weeks|mo|month|months|hr|hrs|hour|hours|h|min|mins|minute|minutes)\b/gi, (_match, value, unit) => `${value} per ${unit}`);
|
|
764
767
|
normalized = normalized.replace(/(\d+)\s*\/\s*(\d+)/g, (match, num, den) => {
|
|
765
768
|
const numerator = parseFloat(num);
|
|
@@ -794,7 +797,7 @@ function tokenize(input) {
|
|
|
794
797
|
* Locates the span of the detected site tokens within the caller's original
|
|
795
798
|
* input so downstream consumers can highlight or replace the exact substring.
|
|
796
799
|
*/
|
|
797
|
-
function
|
|
800
|
+
function computeTokenRange(input, tokens, indices) {
|
|
798
801
|
if (!indices.length) {
|
|
799
802
|
return undefined;
|
|
800
803
|
}
|
|
@@ -1421,7 +1424,9 @@ function parseInternal(input, options) {
|
|
|
1421
1424
|
warnings: [],
|
|
1422
1425
|
siteTokenIndices: new Set(),
|
|
1423
1426
|
siteLookups: [],
|
|
1424
|
-
customSiteHints: buildCustomSiteHints(options === null || options === void 0 ? void 0 : options.siteCodeMap)
|
|
1427
|
+
customSiteHints: buildCustomSiteHints(options === null || options === void 0 ? void 0 : options.siteCodeMap),
|
|
1428
|
+
prnReasonLookups: [],
|
|
1429
|
+
additionalInstructions: []
|
|
1425
1430
|
};
|
|
1426
1431
|
const context = (_a = options === null || options === void 0 ? void 0 : options.context) !== null && _a !== void 0 ? _a : undefined;
|
|
1427
1432
|
const customRouteMap = (options === null || options === void 0 ? void 0 : options.routeMap)
|
|
@@ -1530,12 +1535,19 @@ function parseInternal(input, options) {
|
|
|
1530
1535
|
if (slice.some((part) => internal.consumed.has(part.index))) {
|
|
1531
1536
|
continue;
|
|
1532
1537
|
}
|
|
1533
|
-
const
|
|
1538
|
+
const normalizedParts = slice.filter((part) => !/^[;:(),]+$/.test(part.lower));
|
|
1539
|
+
const phrase = normalizedParts.map((part) => part.lower).join(" ");
|
|
1534
1540
|
const customCode = customRouteMap === null || customRouteMap === void 0 ? void 0 : customRouteMap.get(phrase);
|
|
1535
1541
|
const synonym = customCode
|
|
1536
1542
|
? { code: customCode, text: maps_1.ROUTE_TEXT[customCode] }
|
|
1537
1543
|
: maps_1.DEFAULT_ROUTE_SYNONYMS[phrase];
|
|
1538
1544
|
if (synonym) {
|
|
1545
|
+
if (phrase === "in" && slice.length === 1) {
|
|
1546
|
+
const prevToken = tokens[startIndex - 1];
|
|
1547
|
+
if (prevToken && !internal.consumed.has(prevToken.index)) {
|
|
1548
|
+
continue;
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1539
1551
|
setRoute(internal, synonym.code, synonym.text);
|
|
1540
1552
|
for (const part of slice) {
|
|
1541
1553
|
mark(internal.consumed, part);
|
|
@@ -1881,6 +1893,93 @@ function parseInternal(input, options) {
|
|
|
1881
1893
|
// Expand generic meal markers into specific EventTiming codes when asked to.
|
|
1882
1894
|
expandMealTimings(internal, options);
|
|
1883
1895
|
sortWhenValues(internal, options);
|
|
1896
|
+
// PRN reason text
|
|
1897
|
+
if (internal.asNeeded && prnReasonStart !== undefined) {
|
|
1898
|
+
const reasonTokens = [];
|
|
1899
|
+
const reasonIndices = [];
|
|
1900
|
+
const reasonObjects = [];
|
|
1901
|
+
for (let i = prnReasonStart; i < tokens.length; i++) {
|
|
1902
|
+
const token = tokens[i];
|
|
1903
|
+
if (internal.consumed.has(token.index)) {
|
|
1904
|
+
continue;
|
|
1905
|
+
}
|
|
1906
|
+
reasonTokens.push(token.original);
|
|
1907
|
+
reasonIndices.push(token.index);
|
|
1908
|
+
reasonObjects.push(token);
|
|
1909
|
+
mark(internal.consumed, token);
|
|
1910
|
+
}
|
|
1911
|
+
if (reasonTokens.length > 0) {
|
|
1912
|
+
let sortedIndices = reasonIndices.slice().sort((a, b) => a - b);
|
|
1913
|
+
let range = computeTokenRange(internal.input, tokens, sortedIndices);
|
|
1914
|
+
let sourceText = range ? internal.input.slice(range.start, range.end) : undefined;
|
|
1915
|
+
if (sourceText) {
|
|
1916
|
+
const cutoff = determinePrnReasonCutoff(reasonObjects, sourceText);
|
|
1917
|
+
if (cutoff !== undefined) {
|
|
1918
|
+
for (let i = cutoff; i < reasonObjects.length; i++) {
|
|
1919
|
+
internal.consumed.delete(reasonObjects[i].index);
|
|
1920
|
+
}
|
|
1921
|
+
reasonObjects.splice(cutoff);
|
|
1922
|
+
reasonTokens.splice(cutoff);
|
|
1923
|
+
reasonIndices.splice(cutoff);
|
|
1924
|
+
while (reasonTokens.length > 0) {
|
|
1925
|
+
const lastToken = reasonTokens[reasonTokens.length - 1];
|
|
1926
|
+
if (!lastToken || /^[;:.,-]+$/.test(lastToken.trim())) {
|
|
1927
|
+
const removedObject = reasonObjects.pop();
|
|
1928
|
+
if (removedObject) {
|
|
1929
|
+
internal.consumed.delete(removedObject.index);
|
|
1930
|
+
}
|
|
1931
|
+
reasonTokens.pop();
|
|
1932
|
+
const removedIndex = reasonIndices.pop();
|
|
1933
|
+
if (removedIndex !== undefined) {
|
|
1934
|
+
internal.consumed.delete(removedIndex);
|
|
1935
|
+
}
|
|
1936
|
+
continue;
|
|
1937
|
+
}
|
|
1938
|
+
break;
|
|
1939
|
+
}
|
|
1940
|
+
if (reasonTokens.length > 0) {
|
|
1941
|
+
sortedIndices = reasonIndices.slice().sort((a, b) => a - b);
|
|
1942
|
+
range = computeTokenRange(internal.input, tokens, sortedIndices);
|
|
1943
|
+
sourceText = range ? internal.input.slice(range.start, range.end) : undefined;
|
|
1944
|
+
}
|
|
1945
|
+
else {
|
|
1946
|
+
range = undefined;
|
|
1947
|
+
sourceText = undefined;
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
if (reasonTokens.length > 0) {
|
|
1952
|
+
const joined = reasonTokens.join(" ").trim();
|
|
1953
|
+
if (joined) {
|
|
1954
|
+
let sanitized = joined.replace(/\s+/g, " ").trim();
|
|
1955
|
+
let isProbe = false;
|
|
1956
|
+
const probeMatch = sanitized.match(/^\{(.+)}$/);
|
|
1957
|
+
if (probeMatch) {
|
|
1958
|
+
isProbe = true;
|
|
1959
|
+
sanitized = probeMatch[1];
|
|
1960
|
+
}
|
|
1961
|
+
sanitized = sanitized.replace(/[{}]/g, " ").replace(/\s+/g, " ").trim();
|
|
1962
|
+
const text = sanitized || joined;
|
|
1963
|
+
internal.asNeededReason = text;
|
|
1964
|
+
const normalized = text.toLowerCase();
|
|
1965
|
+
const canonical = sanitized
|
|
1966
|
+
? (0, maps_1.normalizePrnReasonKey)(sanitized)
|
|
1967
|
+
: (0, maps_1.normalizePrnReasonKey)(text);
|
|
1968
|
+
internal.prnReasonLookupRequest = {
|
|
1969
|
+
originalText: joined,
|
|
1970
|
+
text,
|
|
1971
|
+
normalized,
|
|
1972
|
+
canonical: canonical !== null && canonical !== void 0 ? canonical : "",
|
|
1973
|
+
isProbe,
|
|
1974
|
+
inputText: internal.input,
|
|
1975
|
+
sourceText,
|
|
1976
|
+
range
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
collectAdditionalInstructions(internal, tokens);
|
|
1884
1983
|
// Determine site text from leftover tokens (excluding PRN reason tokens)
|
|
1885
1984
|
const leftoverTokens = tokens.filter((t) => !internal.consumed.has(t.index));
|
|
1886
1985
|
const siteCandidateIndices = new Set();
|
|
@@ -1888,6 +1987,13 @@ function parseInternal(input, options) {
|
|
|
1888
1987
|
const normalized = normalizeTokenLower(token);
|
|
1889
1988
|
if (isBodySiteHint(normalized, internal.customSiteHints)) {
|
|
1890
1989
|
siteCandidateIndices.add(token.index);
|
|
1990
|
+
continue;
|
|
1991
|
+
}
|
|
1992
|
+
if (SITE_CONNECTORS.has(normalized)) {
|
|
1993
|
+
const next = tokens[token.index + 1];
|
|
1994
|
+
if (next && !internal.consumed.has(next.index)) {
|
|
1995
|
+
siteCandidateIndices.add(next.index);
|
|
1996
|
+
}
|
|
1891
1997
|
}
|
|
1892
1998
|
}
|
|
1893
1999
|
for (const idx of internal.siteTokenIndices) {
|
|
@@ -1949,7 +2055,7 @@ function parseInternal(input, options) {
|
|
|
1949
2055
|
.join(" ")
|
|
1950
2056
|
.trim();
|
|
1951
2057
|
if (normalizedSite) {
|
|
1952
|
-
const tokenRange =
|
|
2058
|
+
const tokenRange = computeTokenRange(internal.input, tokens, sortedIndices);
|
|
1953
2059
|
let sanitized = normalizedSite;
|
|
1954
2060
|
let isProbe = false;
|
|
1955
2061
|
const probeMatch = sanitized.match(/^\{(.+)}$/);
|
|
@@ -1964,27 +2070,30 @@ function parseInternal(input, options) {
|
|
|
1964
2070
|
sanitized = sanitized.replace(/[{}]/g, " ").replace(/\s+/g, " ").trim();
|
|
1965
2071
|
const range = refineSiteRange(internal.input, sanitized, tokenRange);
|
|
1966
2072
|
const sourceText = range ? internal.input.slice(range.start, range.end) : undefined;
|
|
2073
|
+
const displayText = normalizeSiteDisplayText(sanitized, options === null || options === void 0 ? void 0 : options.siteCodeMap);
|
|
2074
|
+
const displayLower = displayText.toLowerCase();
|
|
2075
|
+
const canonical = displayText ? (0, maps_1.normalizeBodySiteKey)(displayText) : "";
|
|
1967
2076
|
internal.siteLookupRequest = {
|
|
1968
2077
|
originalText: normalizedSite,
|
|
1969
|
-
text:
|
|
1970
|
-
normalized:
|
|
1971
|
-
canonical
|
|
2078
|
+
text: displayText,
|
|
2079
|
+
normalized: displayLower,
|
|
2080
|
+
canonical,
|
|
1972
2081
|
isProbe,
|
|
1973
2082
|
inputText: internal.input,
|
|
1974
2083
|
sourceText,
|
|
1975
2084
|
range
|
|
1976
2085
|
};
|
|
1977
|
-
if (
|
|
2086
|
+
if (displayText) {
|
|
1978
2087
|
const normalizedLower = sanitized.toLowerCase();
|
|
1979
2088
|
const strippedDescriptor = normalizeRouteDescriptorPhrase(normalizedLower);
|
|
1980
|
-
const siteWords =
|
|
2089
|
+
const siteWords = displayLower.split(/\s+/).filter((word) => word.length > 0);
|
|
1981
2090
|
const hasNonSiteWords = siteWords.some((word) => !isBodySiteHint(word, internal.customSiteHints));
|
|
1982
2091
|
const shouldAttemptRouteDescriptor = strippedDescriptor !== normalizedLower || hasNonSiteWords || strippedDescriptor === "mouth";
|
|
1983
2092
|
const appliedRouteDescriptor = shouldAttemptRouteDescriptor && maybeApplyRouteDescriptor(sanitized);
|
|
1984
2093
|
if (!appliedRouteDescriptor) {
|
|
1985
2094
|
// Preserve the clean site text for FHIR output and resolver context
|
|
1986
2095
|
// whenever we keep the original phrase.
|
|
1987
|
-
internal.siteText =
|
|
2096
|
+
internal.siteText = displayText;
|
|
1988
2097
|
if (!internal.siteSource) {
|
|
1989
2098
|
internal.siteSource = "text";
|
|
1990
2099
|
}
|
|
@@ -2000,21 +2109,6 @@ function parseInternal(input, options) {
|
|
|
2000
2109
|
}
|
|
2001
2110
|
}
|
|
2002
2111
|
}
|
|
2003
|
-
// PRN reason text
|
|
2004
|
-
if (internal.asNeeded && prnReasonStart !== undefined) {
|
|
2005
|
-
const reasonTokens = [];
|
|
2006
|
-
for (let i = prnReasonStart; i < tokens.length; i++) {
|
|
2007
|
-
const token = tokens[i];
|
|
2008
|
-
if (internal.consumed.has(token.index)) {
|
|
2009
|
-
continue;
|
|
2010
|
-
}
|
|
2011
|
-
reasonTokens.push(token.original);
|
|
2012
|
-
mark(internal.consumed, token);
|
|
2013
|
-
}
|
|
2014
|
-
if (reasonTokens.length > 0) {
|
|
2015
|
-
internal.asNeededReason = reasonTokens.join(" ");
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
2018
2112
|
if (internal.routeCode === types_1.RouteCode["Intravitreal route (qualifier value)"] &&
|
|
2019
2113
|
(!internal.siteText || !/eye/i.test(internal.siteText))) {
|
|
2020
2114
|
internal.warnings.push("Intravitreal administrations require an eye site (e.g., OD/OS/OU).");
|
|
@@ -2025,6 +2119,14 @@ function parseInternal(input, options) {
|
|
|
2025
2119
|
* Resolves parsed site text against SNOMED dictionaries and synchronous
|
|
2026
2120
|
* callbacks, applying the best match to the in-progress parse result.
|
|
2027
2121
|
*/
|
|
2122
|
+
function applyPrnReasonCoding(internal, options) {
|
|
2123
|
+
runPrnReasonResolutionSync(internal, options);
|
|
2124
|
+
}
|
|
2125
|
+
function applyPrnReasonCodingAsync(internal, options) {
|
|
2126
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2127
|
+
yield runPrnReasonResolutionAsync(internal, options);
|
|
2128
|
+
});
|
|
2129
|
+
}
|
|
2028
2130
|
function applySiteCoding(internal, options) {
|
|
2029
2131
|
runSiteCodingResolutionSync(internal, options);
|
|
2030
2132
|
}
|
|
@@ -2240,16 +2342,21 @@ function pickSiteSelection(selections, request) {
|
|
|
2240
2342
|
* the coding system to SNOMED CT when the definition omits one.
|
|
2241
2343
|
*/
|
|
2242
2344
|
function applySiteDefinition(internal, definition) {
|
|
2243
|
-
var _a;
|
|
2345
|
+
var _a, _b;
|
|
2244
2346
|
const coding = definition.coding;
|
|
2245
|
-
internal.siteCoding =
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2347
|
+
internal.siteCoding = (coding === null || coding === void 0 ? void 0 : coding.code)
|
|
2348
|
+
? {
|
|
2349
|
+
code: coding.code,
|
|
2350
|
+
display: coding.display,
|
|
2351
|
+
system: (_a = coding.system) !== null && _a !== void 0 ? _a : SNOMED_SYSTEM
|
|
2352
|
+
}
|
|
2353
|
+
: undefined;
|
|
2250
2354
|
if (definition.text) {
|
|
2251
2355
|
internal.siteText = definition.text;
|
|
2252
2356
|
}
|
|
2357
|
+
else if (!internal.siteText && ((_b = internal.siteLookupRequest) === null || _b === void 0 ? void 0 : _b.text)) {
|
|
2358
|
+
internal.siteText = internal.siteLookupRequest.text;
|
|
2359
|
+
}
|
|
2253
2360
|
}
|
|
2254
2361
|
/**
|
|
2255
2362
|
* Converts a body-site definition into a suggestion payload so all suggestion
|
|
@@ -2257,11 +2364,15 @@ function applySiteDefinition(internal, definition) {
|
|
|
2257
2364
|
*/
|
|
2258
2365
|
function definitionToSuggestion(definition) {
|
|
2259
2366
|
var _a;
|
|
2367
|
+
const coding = definition.coding;
|
|
2368
|
+
if (!(coding === null || coding === void 0 ? void 0 : coding.code)) {
|
|
2369
|
+
return undefined;
|
|
2370
|
+
}
|
|
2260
2371
|
return {
|
|
2261
2372
|
coding: {
|
|
2262
|
-
code:
|
|
2263
|
-
display:
|
|
2264
|
-
system: (_a =
|
|
2373
|
+
code: coding.code,
|
|
2374
|
+
display: coding.display,
|
|
2375
|
+
system: (_a = coding.system) !== null && _a !== void 0 ? _a : SNOMED_SYSTEM
|
|
2265
2376
|
},
|
|
2266
2377
|
text: definition.text
|
|
2267
2378
|
};
|
|
@@ -2307,6 +2418,676 @@ function collectSuggestionResult(map, result) {
|
|
|
2307
2418
|
addSuggestionToMap(map, suggestion);
|
|
2308
2419
|
}
|
|
2309
2420
|
}
|
|
2421
|
+
function findAdditionalInstructionDefinition(text, canonical) {
|
|
2422
|
+
if (!canonical) {
|
|
2423
|
+
return undefined;
|
|
2424
|
+
}
|
|
2425
|
+
for (const entry of maps_1.DEFAULT_ADDITIONAL_INSTRUCTION_ENTRIES) {
|
|
2426
|
+
if (!entry.canonical) {
|
|
2427
|
+
continue;
|
|
2428
|
+
}
|
|
2429
|
+
if (entry.canonical === canonical) {
|
|
2430
|
+
return entry.definition;
|
|
2431
|
+
}
|
|
2432
|
+
if (canonical.includes(entry.canonical) || entry.canonical.includes(canonical)) {
|
|
2433
|
+
return entry.definition;
|
|
2434
|
+
}
|
|
2435
|
+
for (const term of entry.terms) {
|
|
2436
|
+
const normalizedTerm = (0, maps_1.normalizeAdditionalInstructionKey)(term);
|
|
2437
|
+
if (!normalizedTerm) {
|
|
2438
|
+
continue;
|
|
2439
|
+
}
|
|
2440
|
+
if (canonical.includes(normalizedTerm) || normalizedTerm.includes(canonical)) {
|
|
2441
|
+
return entry.definition;
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
return undefined;
|
|
2446
|
+
}
|
|
2447
|
+
const BODY_SITE_ADJECTIVE_SUFFIXES = [
|
|
2448
|
+
"al",
|
|
2449
|
+
"ial",
|
|
2450
|
+
"ual",
|
|
2451
|
+
"ic",
|
|
2452
|
+
"ous",
|
|
2453
|
+
"ive",
|
|
2454
|
+
"ary",
|
|
2455
|
+
"ory",
|
|
2456
|
+
"atic",
|
|
2457
|
+
"etic",
|
|
2458
|
+
"ular",
|
|
2459
|
+
"otic",
|
|
2460
|
+
"ile",
|
|
2461
|
+
"eal",
|
|
2462
|
+
"inal",
|
|
2463
|
+
"aneal",
|
|
2464
|
+
"enal"
|
|
2465
|
+
];
|
|
2466
|
+
const DEFAULT_SITE_SYNONYM_KEYS = (() => {
|
|
2467
|
+
const map = new Map();
|
|
2468
|
+
for (const [key, definition] of (0, object_1.objectEntries)(maps_1.DEFAULT_BODY_SITE_SNOMED)) {
|
|
2469
|
+
if (!definition) {
|
|
2470
|
+
continue;
|
|
2471
|
+
}
|
|
2472
|
+
const normalized = key.trim();
|
|
2473
|
+
if (!normalized) {
|
|
2474
|
+
continue;
|
|
2475
|
+
}
|
|
2476
|
+
const existing = map.get(definition);
|
|
2477
|
+
if (existing) {
|
|
2478
|
+
if (existing.indexOf(normalized) === -1) {
|
|
2479
|
+
existing.push(normalized);
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
else {
|
|
2483
|
+
map.set(definition, [normalized]);
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
return map;
|
|
2487
|
+
})();
|
|
2488
|
+
function normalizeSiteDisplayText(text, customSiteMap) {
|
|
2489
|
+
var _a;
|
|
2490
|
+
const trimmed = text.trim();
|
|
2491
|
+
if (!trimmed) {
|
|
2492
|
+
return trimmed;
|
|
2493
|
+
}
|
|
2494
|
+
const canonicalInput = (0, maps_1.normalizeBodySiteKey)(trimmed);
|
|
2495
|
+
if (!canonicalInput || !isAdjectivalSitePhrase(canonicalInput)) {
|
|
2496
|
+
return trimmed;
|
|
2497
|
+
}
|
|
2498
|
+
const definition = (_a = lookupBodySiteDefinition(customSiteMap, canonicalInput)) !== null && _a !== void 0 ? _a : maps_1.DEFAULT_BODY_SITE_SNOMED[canonicalInput];
|
|
2499
|
+
if (!definition) {
|
|
2500
|
+
return trimmed;
|
|
2501
|
+
}
|
|
2502
|
+
const preferred = pickPreferredBodySitePhrase(canonicalInput, definition, customSiteMap);
|
|
2503
|
+
if (!preferred) {
|
|
2504
|
+
return trimmed;
|
|
2505
|
+
}
|
|
2506
|
+
return preferred;
|
|
2507
|
+
}
|
|
2508
|
+
function pickPreferredBodySitePhrase(canonical, definition, customSiteMap) {
|
|
2509
|
+
const synonyms = new Set();
|
|
2510
|
+
synonyms.add(canonical);
|
|
2511
|
+
if (definition.aliases) {
|
|
2512
|
+
for (const alias of definition.aliases) {
|
|
2513
|
+
const normalizedAlias = (0, maps_1.normalizeBodySiteKey)(alias);
|
|
2514
|
+
if (normalizedAlias) {
|
|
2515
|
+
synonyms.add(normalizedAlias);
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
const defaultSynonyms = DEFAULT_SITE_SYNONYM_KEYS.get(definition);
|
|
2520
|
+
if (defaultSynonyms) {
|
|
2521
|
+
for (const synonym of defaultSynonyms) {
|
|
2522
|
+
synonyms.add(synonym);
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
if (customSiteMap) {
|
|
2526
|
+
for (const [key, candidate] of (0, object_1.objectEntries)(customSiteMap)) {
|
|
2527
|
+
if (!candidate) {
|
|
2528
|
+
continue;
|
|
2529
|
+
}
|
|
2530
|
+
if (candidate === definition) {
|
|
2531
|
+
const normalizedKey = (0, maps_1.normalizeBodySiteKey)(key);
|
|
2532
|
+
if (normalizedKey) {
|
|
2533
|
+
synonyms.add(normalizedKey);
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
if (candidate.aliases) {
|
|
2537
|
+
for (const alias of candidate.aliases) {
|
|
2538
|
+
const normalizedAlias = (0, maps_1.normalizeBodySiteKey)(alias);
|
|
2539
|
+
if (normalizedAlias) {
|
|
2540
|
+
synonyms.add(normalizedAlias);
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
const candidates = Array.from(synonyms).filter((phrase) => phrase && !isAdjectivalSitePhrase(phrase));
|
|
2547
|
+
if (!candidates.length) {
|
|
2548
|
+
return undefined;
|
|
2549
|
+
}
|
|
2550
|
+
candidates.sort((a, b) => scoreBodySitePhrase(b) - scoreBodySitePhrase(a));
|
|
2551
|
+
const best = candidates[0];
|
|
2552
|
+
if (!best) {
|
|
2553
|
+
return undefined;
|
|
2554
|
+
}
|
|
2555
|
+
if ((0, maps_1.normalizeBodySiteKey)(best) === canonical) {
|
|
2556
|
+
return undefined;
|
|
2557
|
+
}
|
|
2558
|
+
return best;
|
|
2559
|
+
}
|
|
2560
|
+
function scoreBodySitePhrase(phrase) {
|
|
2561
|
+
const lower = phrase.toLowerCase();
|
|
2562
|
+
const words = lower.split(/\s+/).filter((part) => part.length > 0);
|
|
2563
|
+
let score = 0;
|
|
2564
|
+
if (!/(structure|region|entire|proper|body)/.test(lower)) {
|
|
2565
|
+
score += 3;
|
|
2566
|
+
}
|
|
2567
|
+
if (!lower.includes(" of ")) {
|
|
2568
|
+
score += 1;
|
|
2569
|
+
}
|
|
2570
|
+
if (words.length <= 2) {
|
|
2571
|
+
score += 1;
|
|
2572
|
+
}
|
|
2573
|
+
if (words.length === 1) {
|
|
2574
|
+
score += 0.5;
|
|
2575
|
+
}
|
|
2576
|
+
score -= words.length * 0.2;
|
|
2577
|
+
score -= lower.length * 0.01;
|
|
2578
|
+
return score;
|
|
2579
|
+
}
|
|
2580
|
+
function isAdjectivalSitePhrase(phrase) {
|
|
2581
|
+
const normalized = phrase.trim().toLowerCase();
|
|
2582
|
+
if (!normalized) {
|
|
2583
|
+
return false;
|
|
2584
|
+
}
|
|
2585
|
+
const words = normalized.split(/\s+/).filter((word) => word.length > 0);
|
|
2586
|
+
if (words.length !== 1) {
|
|
2587
|
+
return false;
|
|
2588
|
+
}
|
|
2589
|
+
const last = words[words.length - 1];
|
|
2590
|
+
if (last.length <= 3) {
|
|
2591
|
+
return false;
|
|
2592
|
+
}
|
|
2593
|
+
return BODY_SITE_ADJECTIVE_SUFFIXES.some((suffix) => last.endsWith(suffix));
|
|
2594
|
+
}
|
|
2595
|
+
function collectAdditionalInstructions(internal, tokens) {
|
|
2596
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2597
|
+
if (internal.additionalInstructions.length) {
|
|
2598
|
+
return;
|
|
2599
|
+
}
|
|
2600
|
+
const punctuationOnly = /^[;:.,-]+$/;
|
|
2601
|
+
const trailing = [];
|
|
2602
|
+
let expectedIndex;
|
|
2603
|
+
for (let cursor = tokens.length - 1; cursor >= 0; cursor--) {
|
|
2604
|
+
const token = tokens[cursor];
|
|
2605
|
+
if (!token) {
|
|
2606
|
+
continue;
|
|
2607
|
+
}
|
|
2608
|
+
if (internal.consumed.has(token.index)) {
|
|
2609
|
+
if (trailing.length > 0) {
|
|
2610
|
+
break;
|
|
2611
|
+
}
|
|
2612
|
+
continue;
|
|
2613
|
+
}
|
|
2614
|
+
if (expectedIndex !== undefined && token.index !== expectedIndex - 1) {
|
|
2615
|
+
break;
|
|
2616
|
+
}
|
|
2617
|
+
trailing.unshift(token);
|
|
2618
|
+
expectedIndex = token.index;
|
|
2619
|
+
}
|
|
2620
|
+
if (!trailing.length) {
|
|
2621
|
+
return;
|
|
2622
|
+
}
|
|
2623
|
+
const contentTokens = trailing.filter((token) => !punctuationOnly.test(token.original));
|
|
2624
|
+
if (!contentTokens.length) {
|
|
2625
|
+
return;
|
|
2626
|
+
}
|
|
2627
|
+
const trailingIndices = trailing.map((token) => token.index).sort((a, b) => a - b);
|
|
2628
|
+
const lastIndex = trailingIndices[trailingIndices.length - 1];
|
|
2629
|
+
for (let i = lastIndex + 1; i < tokens.length; i++) {
|
|
2630
|
+
const nextToken = tokens[i];
|
|
2631
|
+
if (!nextToken) {
|
|
2632
|
+
continue;
|
|
2633
|
+
}
|
|
2634
|
+
if (!internal.consumed.has(nextToken.index)) {
|
|
2635
|
+
return;
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
const joined = contentTokens
|
|
2639
|
+
.map((token) => token.original)
|
|
2640
|
+
.join(" ")
|
|
2641
|
+
.replace(/\s+/g, " ")
|
|
2642
|
+
.trim();
|
|
2643
|
+
if (!joined) {
|
|
2644
|
+
return;
|
|
2645
|
+
}
|
|
2646
|
+
const contentIndices = contentTokens.map((token) => token.index).sort((a, b) => a - b);
|
|
2647
|
+
const lowerInput = internal.input.toLowerCase();
|
|
2648
|
+
let trailingRange;
|
|
2649
|
+
let searchEnd = lowerInput.length;
|
|
2650
|
+
let rangeStart;
|
|
2651
|
+
let rangeEnd;
|
|
2652
|
+
for (let i = contentTokens.length - 1; i >= 0; i--) {
|
|
2653
|
+
const fragment = contentTokens[i].original.trim();
|
|
2654
|
+
if (!fragment) {
|
|
2655
|
+
continue;
|
|
2656
|
+
}
|
|
2657
|
+
const lowerFragment = fragment.toLowerCase();
|
|
2658
|
+
const foundIndex = lowerInput.lastIndexOf(lowerFragment, searchEnd - 1);
|
|
2659
|
+
if (foundIndex === -1) {
|
|
2660
|
+
rangeStart = undefined;
|
|
2661
|
+
rangeEnd = undefined;
|
|
2662
|
+
break;
|
|
2663
|
+
}
|
|
2664
|
+
rangeStart = foundIndex;
|
|
2665
|
+
if (rangeEnd === undefined) {
|
|
2666
|
+
rangeEnd = foundIndex + lowerFragment.length;
|
|
2667
|
+
}
|
|
2668
|
+
searchEnd = foundIndex;
|
|
2669
|
+
}
|
|
2670
|
+
if (rangeStart !== undefined && rangeEnd !== undefined) {
|
|
2671
|
+
trailingRange = { start: rangeStart, end: rangeEnd };
|
|
2672
|
+
}
|
|
2673
|
+
const range = trailingRange !== null && trailingRange !== void 0 ? trailingRange : computeTokenRange(internal.input, tokens, contentIndices);
|
|
2674
|
+
let separatorDetected = false;
|
|
2675
|
+
if (range) {
|
|
2676
|
+
for (let cursor = range.start - 1; cursor >= 0; cursor--) {
|
|
2677
|
+
const ch = internal.input[cursor];
|
|
2678
|
+
if (ch === "\n" || ch === "\r") {
|
|
2679
|
+
separatorDetected = true;
|
|
2680
|
+
break;
|
|
2681
|
+
}
|
|
2682
|
+
if (/\s/.test(ch)) {
|
|
2683
|
+
continue;
|
|
2684
|
+
}
|
|
2685
|
+
if (/-|;|:|\.|,/.test(ch)) {
|
|
2686
|
+
separatorDetected = true;
|
|
2687
|
+
}
|
|
2688
|
+
break;
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
const sourceText = range
|
|
2692
|
+
? internal.input.slice(range.start, range.end)
|
|
2693
|
+
: joined;
|
|
2694
|
+
if (!separatorDetected && !/[-;:.]/.test(sourceText)) {
|
|
2695
|
+
return;
|
|
2696
|
+
}
|
|
2697
|
+
const normalized = sourceText
|
|
2698
|
+
.replace(/\s*[-:]+\s*/g, "; ")
|
|
2699
|
+
.replace(/\s*(?:\r?\n)+\s*/g, "; ")
|
|
2700
|
+
.replace(/\s+/g, " ");
|
|
2701
|
+
const segments = normalized
|
|
2702
|
+
.split(/(?:;|\.)/)
|
|
2703
|
+
.map((segment) => segment.trim())
|
|
2704
|
+
.filter((segment) => segment.length > 0);
|
|
2705
|
+
const phrases = segments.length ? segments : [joined];
|
|
2706
|
+
const seen = new Set();
|
|
2707
|
+
const instructions = [];
|
|
2708
|
+
for (const phrase of phrases) {
|
|
2709
|
+
const canonical = (0, maps_1.normalizeAdditionalInstructionKey)(phrase);
|
|
2710
|
+
const definition = (_a = maps_1.DEFAULT_ADDITIONAL_INSTRUCTION_DEFINITIONS[canonical]) !== null && _a !== void 0 ? _a : findAdditionalInstructionDefinition(phrase, canonical);
|
|
2711
|
+
const key = ((_b = definition === null || definition === void 0 ? void 0 : definition.coding) === null || _b === void 0 ? void 0 : _b.code)
|
|
2712
|
+
? `code:${(_c = definition.coding.system) !== null && _c !== void 0 ? _c : SNOMED_SYSTEM}|${definition.coding.code}`
|
|
2713
|
+
: canonical
|
|
2714
|
+
? `text:${canonical}`
|
|
2715
|
+
: phrase.toLowerCase();
|
|
2716
|
+
if (key && seen.has(key)) {
|
|
2717
|
+
continue;
|
|
2718
|
+
}
|
|
2719
|
+
seen.add(key);
|
|
2720
|
+
if (definition) {
|
|
2721
|
+
instructions.push({
|
|
2722
|
+
text: (_d = definition.text) !== null && _d !== void 0 ? _d : phrase,
|
|
2723
|
+
coding: ((_e = definition.coding) === null || _e === void 0 ? void 0 : _e.code)
|
|
2724
|
+
? {
|
|
2725
|
+
code: definition.coding.code,
|
|
2726
|
+
display: definition.coding.display,
|
|
2727
|
+
system: (_f = definition.coding.system) !== null && _f !== void 0 ? _f : SNOMED_SYSTEM
|
|
2728
|
+
}
|
|
2729
|
+
: undefined
|
|
2730
|
+
});
|
|
2731
|
+
}
|
|
2732
|
+
else {
|
|
2733
|
+
instructions.push({ text: phrase });
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
if (instructions.length) {
|
|
2737
|
+
internal.additionalInstructions = instructions;
|
|
2738
|
+
for (const token of trailing) {
|
|
2739
|
+
mark(internal.consumed, token);
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
function determinePrnReasonCutoff(tokens, sourceText) {
|
|
2744
|
+
const separatorIndex = findPrnReasonSeparator(sourceText);
|
|
2745
|
+
if (separatorIndex === undefined) {
|
|
2746
|
+
return undefined;
|
|
2747
|
+
}
|
|
2748
|
+
const lowerSource = sourceText.toLowerCase();
|
|
2749
|
+
let searchOffset = 0;
|
|
2750
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
2751
|
+
const token = tokens[i];
|
|
2752
|
+
const fragment = token.original.trim();
|
|
2753
|
+
if (!fragment) {
|
|
2754
|
+
continue;
|
|
2755
|
+
}
|
|
2756
|
+
const lowerFragment = fragment.toLowerCase();
|
|
2757
|
+
const position = lowerSource.indexOf(lowerFragment, searchOffset);
|
|
2758
|
+
if (position === -1) {
|
|
2759
|
+
continue;
|
|
2760
|
+
}
|
|
2761
|
+
const end = position + lowerFragment.length;
|
|
2762
|
+
searchOffset = end;
|
|
2763
|
+
if (position >= separatorIndex) {
|
|
2764
|
+
return i;
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
return undefined;
|
|
2768
|
+
}
|
|
2769
|
+
function findPrnReasonSeparator(sourceText) {
|
|
2770
|
+
var _a;
|
|
2771
|
+
for (let i = 0; i < sourceText.length; i++) {
|
|
2772
|
+
const ch = sourceText[i];
|
|
2773
|
+
if (ch === "\n" || ch === "\r") {
|
|
2774
|
+
if (sourceText.slice(i + 1).trim().length > 0) {
|
|
2775
|
+
return i;
|
|
2776
|
+
}
|
|
2777
|
+
continue;
|
|
2778
|
+
}
|
|
2779
|
+
if (ch === ";") {
|
|
2780
|
+
if (sourceText.slice(i + 1).trim().length > 0) {
|
|
2781
|
+
return i;
|
|
2782
|
+
}
|
|
2783
|
+
continue;
|
|
2784
|
+
}
|
|
2785
|
+
if (ch === "-") {
|
|
2786
|
+
const prev = sourceText[i - 1];
|
|
2787
|
+
const next = sourceText[i + 1];
|
|
2788
|
+
const hasWhitespaceAround = (!prev || /\s/.test(prev)) && (!next || /\s/.test(next));
|
|
2789
|
+
if (hasWhitespaceAround && sourceText.slice(i + 1).trim().length > 0) {
|
|
2790
|
+
return i;
|
|
2791
|
+
}
|
|
2792
|
+
continue;
|
|
2793
|
+
}
|
|
2794
|
+
if (ch === ":" || ch === ".") {
|
|
2795
|
+
const rest = sourceText.slice(i + 1);
|
|
2796
|
+
if (!rest.trim().length) {
|
|
2797
|
+
continue;
|
|
2798
|
+
}
|
|
2799
|
+
const nextChar = rest.replace(/^\s+/, "")[0];
|
|
2800
|
+
if (!nextChar) {
|
|
2801
|
+
continue;
|
|
2802
|
+
}
|
|
2803
|
+
if (ch === "." &&
|
|
2804
|
+
/[0-9]/.test((_a = sourceText[i - 1]) !== null && _a !== void 0 ? _a : "") &&
|
|
2805
|
+
/[0-9]/.test(nextChar)) {
|
|
2806
|
+
continue;
|
|
2807
|
+
}
|
|
2808
|
+
return i;
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
return undefined;
|
|
2812
|
+
}
|
|
2813
|
+
function lookupPrnReasonDefinition(map, canonical) {
|
|
2814
|
+
if (!map) {
|
|
2815
|
+
return undefined;
|
|
2816
|
+
}
|
|
2817
|
+
const direct = map[canonical];
|
|
2818
|
+
if (direct) {
|
|
2819
|
+
return direct;
|
|
2820
|
+
}
|
|
2821
|
+
for (const [key, definition] of (0, object_1.objectEntries)(map)) {
|
|
2822
|
+
if ((0, maps_1.normalizePrnReasonKey)(key) === canonical) {
|
|
2823
|
+
return definition;
|
|
2824
|
+
}
|
|
2825
|
+
if (definition.aliases) {
|
|
2826
|
+
for (const alias of definition.aliases) {
|
|
2827
|
+
if ((0, maps_1.normalizePrnReasonKey)(alias) === canonical) {
|
|
2828
|
+
return definition;
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
return undefined;
|
|
2834
|
+
}
|
|
2835
|
+
function pickPrnReasonSelection(selections, request) {
|
|
2836
|
+
if (!selections) {
|
|
2837
|
+
return undefined;
|
|
2838
|
+
}
|
|
2839
|
+
const canonical = request.canonical;
|
|
2840
|
+
const normalizedText = (0, maps_1.normalizePrnReasonKey)(request.text);
|
|
2841
|
+
const requestRange = request.range;
|
|
2842
|
+
for (const selection of toArray(selections)) {
|
|
2843
|
+
if (!selection) {
|
|
2844
|
+
continue;
|
|
2845
|
+
}
|
|
2846
|
+
let matched = false;
|
|
2847
|
+
if (selection.range) {
|
|
2848
|
+
if (!requestRange) {
|
|
2849
|
+
continue;
|
|
2850
|
+
}
|
|
2851
|
+
if (selection.range.start !== requestRange.start ||
|
|
2852
|
+
selection.range.end !== requestRange.end) {
|
|
2853
|
+
continue;
|
|
2854
|
+
}
|
|
2855
|
+
matched = true;
|
|
2856
|
+
}
|
|
2857
|
+
if (selection.canonical) {
|
|
2858
|
+
if ((0, maps_1.normalizePrnReasonKey)(selection.canonical) !== canonical) {
|
|
2859
|
+
continue;
|
|
2860
|
+
}
|
|
2861
|
+
matched = true;
|
|
2862
|
+
}
|
|
2863
|
+
else if (selection.text) {
|
|
2864
|
+
const normalizedSelection = (0, maps_1.normalizePrnReasonKey)(selection.text);
|
|
2865
|
+
if (normalizedSelection !== canonical && normalizedSelection !== normalizedText) {
|
|
2866
|
+
continue;
|
|
2867
|
+
}
|
|
2868
|
+
matched = true;
|
|
2869
|
+
}
|
|
2870
|
+
if (!selection.range && !selection.canonical && !selection.text) {
|
|
2871
|
+
continue;
|
|
2872
|
+
}
|
|
2873
|
+
if (matched) {
|
|
2874
|
+
return selection.resolution;
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
return undefined;
|
|
2878
|
+
}
|
|
2879
|
+
function applyPrnReasonDefinition(internal, definition) {
|
|
2880
|
+
var _a;
|
|
2881
|
+
const coding = definition.coding;
|
|
2882
|
+
internal.asNeededReasonCoding = (coding === null || coding === void 0 ? void 0 : coding.code)
|
|
2883
|
+
? {
|
|
2884
|
+
code: coding.code,
|
|
2885
|
+
display: coding.display,
|
|
2886
|
+
system: (_a = coding.system) !== null && _a !== void 0 ? _a : SNOMED_SYSTEM
|
|
2887
|
+
}
|
|
2888
|
+
: undefined;
|
|
2889
|
+
if (definition.text && !internal.asNeededReason) {
|
|
2890
|
+
internal.asNeededReason = definition.text;
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
function definitionToPrnSuggestion(definition) {
|
|
2894
|
+
var _a, _b, _c, _d;
|
|
2895
|
+
return {
|
|
2896
|
+
coding: ((_a = definition.coding) === null || _a === void 0 ? void 0 : _a.code)
|
|
2897
|
+
? {
|
|
2898
|
+
code: definition.coding.code,
|
|
2899
|
+
display: definition.coding.display,
|
|
2900
|
+
system: (_b = definition.coding.system) !== null && _b !== void 0 ? _b : SNOMED_SYSTEM
|
|
2901
|
+
}
|
|
2902
|
+
: undefined,
|
|
2903
|
+
text: (_c = definition.text) !== null && _c !== void 0 ? _c : (_d = definition.coding) === null || _d === void 0 ? void 0 : _d.display
|
|
2904
|
+
};
|
|
2905
|
+
}
|
|
2906
|
+
function addReasonSuggestionToMap(map, suggestion) {
|
|
2907
|
+
var _a;
|
|
2908
|
+
if (!suggestion) {
|
|
2909
|
+
return;
|
|
2910
|
+
}
|
|
2911
|
+
const coding = suggestion.coding;
|
|
2912
|
+
const key = (coding === null || coding === void 0 ? void 0 : coding.code)
|
|
2913
|
+
? `${(_a = coding.system) !== null && _a !== void 0 ? _a : SNOMED_SYSTEM}|${coding.code}`
|
|
2914
|
+
: suggestion.text
|
|
2915
|
+
? `text:${suggestion.text.toLowerCase()}`
|
|
2916
|
+
: undefined;
|
|
2917
|
+
if (!key || map.has(key)) {
|
|
2918
|
+
return;
|
|
2919
|
+
}
|
|
2920
|
+
map.set(key, suggestion);
|
|
2921
|
+
}
|
|
2922
|
+
function collectReasonSuggestionResult(map, result) {
|
|
2923
|
+
if (!result) {
|
|
2924
|
+
return;
|
|
2925
|
+
}
|
|
2926
|
+
const suggestions = Array.isArray(result)
|
|
2927
|
+
? result
|
|
2928
|
+
: typeof result === "object" && "suggestions" in result
|
|
2929
|
+
? result.suggestions
|
|
2930
|
+
: [result];
|
|
2931
|
+
for (const suggestion of suggestions) {
|
|
2932
|
+
addReasonSuggestionToMap(map, suggestion);
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
function collectDefaultPrnReasonDefinitions(request) {
|
|
2936
|
+
const canonical = request.canonical;
|
|
2937
|
+
const normalized = request.normalized;
|
|
2938
|
+
const seen = new Set();
|
|
2939
|
+
for (const entry of maps_1.DEFAULT_PRN_REASON_ENTRIES) {
|
|
2940
|
+
if (!entry.canonical) {
|
|
2941
|
+
continue;
|
|
2942
|
+
}
|
|
2943
|
+
if (entry.canonical === canonical) {
|
|
2944
|
+
seen.add(entry.definition);
|
|
2945
|
+
continue;
|
|
2946
|
+
}
|
|
2947
|
+
if (canonical && (entry.canonical.includes(canonical) || canonical.includes(entry.canonical))) {
|
|
2948
|
+
seen.add(entry.definition);
|
|
2949
|
+
continue;
|
|
2950
|
+
}
|
|
2951
|
+
for (const term of entry.terms) {
|
|
2952
|
+
const normalizedTerm = (0, maps_1.normalizePrnReasonKey)(term);
|
|
2953
|
+
if (!normalizedTerm) {
|
|
2954
|
+
continue;
|
|
2955
|
+
}
|
|
2956
|
+
if (canonical && canonical.includes(normalizedTerm)) {
|
|
2957
|
+
seen.add(entry.definition);
|
|
2958
|
+
break;
|
|
2959
|
+
}
|
|
2960
|
+
if (normalized.includes(normalizedTerm)) {
|
|
2961
|
+
seen.add(entry.definition);
|
|
2962
|
+
break;
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
if (!seen.size) {
|
|
2967
|
+
for (const entry of maps_1.DEFAULT_PRN_REASON_ENTRIES) {
|
|
2968
|
+
seen.add(entry.definition);
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
return Array.from(seen);
|
|
2972
|
+
}
|
|
2973
|
+
function runPrnReasonResolutionSync(internal, options) {
|
|
2974
|
+
internal.prnReasonLookups = [];
|
|
2975
|
+
const request = internal.prnReasonLookupRequest;
|
|
2976
|
+
if (!request) {
|
|
2977
|
+
return;
|
|
2978
|
+
}
|
|
2979
|
+
const canonical = request.canonical;
|
|
2980
|
+
const selection = pickPrnReasonSelection(options === null || options === void 0 ? void 0 : options.prnReasonSelections, request);
|
|
2981
|
+
const customDefinition = lookupPrnReasonDefinition(options === null || options === void 0 ? void 0 : options.prnReasonMap, canonical);
|
|
2982
|
+
let resolution = selection !== null && selection !== void 0 ? selection : customDefinition;
|
|
2983
|
+
if (!resolution) {
|
|
2984
|
+
for (const resolver of toArray(options === null || options === void 0 ? void 0 : options.prnReasonResolvers)) {
|
|
2985
|
+
const result = resolver(request);
|
|
2986
|
+
if (isPromise(result)) {
|
|
2987
|
+
throw new Error("PRN reason resolver returned a Promise; use parseSigAsync for asynchronous PRN reason resolution.");
|
|
2988
|
+
}
|
|
2989
|
+
if (result) {
|
|
2990
|
+
resolution = result;
|
|
2991
|
+
break;
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
const defaultDefinition = canonical ? maps_1.DEFAULT_PRN_REASON_DEFINITIONS[canonical] : undefined;
|
|
2996
|
+
if (!resolution && defaultDefinition) {
|
|
2997
|
+
resolution = defaultDefinition;
|
|
2998
|
+
}
|
|
2999
|
+
if (resolution) {
|
|
3000
|
+
applyPrnReasonDefinition(internal, resolution);
|
|
3001
|
+
}
|
|
3002
|
+
else {
|
|
3003
|
+
internal.asNeededReasonCoding = undefined;
|
|
3004
|
+
}
|
|
3005
|
+
const needsSuggestions = request.isProbe || !resolution;
|
|
3006
|
+
if (!needsSuggestions) {
|
|
3007
|
+
return;
|
|
3008
|
+
}
|
|
3009
|
+
const suggestionMap = new Map();
|
|
3010
|
+
if (selection) {
|
|
3011
|
+
addReasonSuggestionToMap(suggestionMap, definitionToPrnSuggestion(selection));
|
|
3012
|
+
}
|
|
3013
|
+
if (customDefinition) {
|
|
3014
|
+
addReasonSuggestionToMap(suggestionMap, definitionToPrnSuggestion(customDefinition));
|
|
3015
|
+
}
|
|
3016
|
+
if (defaultDefinition) {
|
|
3017
|
+
addReasonSuggestionToMap(suggestionMap, definitionToPrnSuggestion(defaultDefinition));
|
|
3018
|
+
}
|
|
3019
|
+
for (const definition of collectDefaultPrnReasonDefinitions(request)) {
|
|
3020
|
+
addReasonSuggestionToMap(suggestionMap, definitionToPrnSuggestion(definition));
|
|
3021
|
+
}
|
|
3022
|
+
for (const resolver of toArray(options === null || options === void 0 ? void 0 : options.prnReasonSuggestionResolvers)) {
|
|
3023
|
+
const result = resolver(request);
|
|
3024
|
+
if (isPromise(result)) {
|
|
3025
|
+
throw new Error("PRN reason suggestion resolver returned a Promise; use parseSigAsync for asynchronous PRN reason suggestions.");
|
|
3026
|
+
}
|
|
3027
|
+
collectReasonSuggestionResult(suggestionMap, result);
|
|
3028
|
+
}
|
|
3029
|
+
const suggestions = Array.from(suggestionMap.values());
|
|
3030
|
+
if (suggestions.length || request.isProbe) {
|
|
3031
|
+
internal.prnReasonLookups.push({ request, suggestions });
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
function runPrnReasonResolutionAsync(internal, options) {
|
|
3035
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3036
|
+
internal.prnReasonLookups = [];
|
|
3037
|
+
const request = internal.prnReasonLookupRequest;
|
|
3038
|
+
if (!request) {
|
|
3039
|
+
return;
|
|
3040
|
+
}
|
|
3041
|
+
const canonical = request.canonical;
|
|
3042
|
+
const selection = pickPrnReasonSelection(options === null || options === void 0 ? void 0 : options.prnReasonSelections, request);
|
|
3043
|
+
const customDefinition = lookupPrnReasonDefinition(options === null || options === void 0 ? void 0 : options.prnReasonMap, canonical);
|
|
3044
|
+
let resolution = selection !== null && selection !== void 0 ? selection : customDefinition;
|
|
3045
|
+
if (!resolution) {
|
|
3046
|
+
for (const resolver of toArray(options === null || options === void 0 ? void 0 : options.prnReasonResolvers)) {
|
|
3047
|
+
const result = yield resolver(request);
|
|
3048
|
+
if (result) {
|
|
3049
|
+
resolution = result;
|
|
3050
|
+
break;
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
const defaultDefinition = canonical ? maps_1.DEFAULT_PRN_REASON_DEFINITIONS[canonical] : undefined;
|
|
3055
|
+
if (!resolution && defaultDefinition) {
|
|
3056
|
+
resolution = defaultDefinition;
|
|
3057
|
+
}
|
|
3058
|
+
if (resolution) {
|
|
3059
|
+
applyPrnReasonDefinition(internal, resolution);
|
|
3060
|
+
}
|
|
3061
|
+
else {
|
|
3062
|
+
internal.asNeededReasonCoding = undefined;
|
|
3063
|
+
}
|
|
3064
|
+
const needsSuggestions = request.isProbe || !resolution;
|
|
3065
|
+
if (!needsSuggestions) {
|
|
3066
|
+
return;
|
|
3067
|
+
}
|
|
3068
|
+
const suggestionMap = new Map();
|
|
3069
|
+
if (selection) {
|
|
3070
|
+
addReasonSuggestionToMap(suggestionMap, definitionToPrnSuggestion(selection));
|
|
3071
|
+
}
|
|
3072
|
+
if (customDefinition) {
|
|
3073
|
+
addReasonSuggestionToMap(suggestionMap, definitionToPrnSuggestion(customDefinition));
|
|
3074
|
+
}
|
|
3075
|
+
if (defaultDefinition) {
|
|
3076
|
+
addReasonSuggestionToMap(suggestionMap, definitionToPrnSuggestion(defaultDefinition));
|
|
3077
|
+
}
|
|
3078
|
+
for (const definition of collectDefaultPrnReasonDefinitions(request)) {
|
|
3079
|
+
addReasonSuggestionToMap(suggestionMap, definitionToPrnSuggestion(definition));
|
|
3080
|
+
}
|
|
3081
|
+
for (const resolver of toArray(options === null || options === void 0 ? void 0 : options.prnReasonSuggestionResolvers)) {
|
|
3082
|
+
const result = yield resolver(request);
|
|
3083
|
+
collectReasonSuggestionResult(suggestionMap, result);
|
|
3084
|
+
}
|
|
3085
|
+
const suggestions = Array.from(suggestionMap.values());
|
|
3086
|
+
if (suggestions.length || request.isProbe) {
|
|
3087
|
+
internal.prnReasonLookups.push({ request, suggestions });
|
|
3088
|
+
}
|
|
3089
|
+
});
|
|
3090
|
+
}
|
|
2310
3091
|
/**
|
|
2311
3092
|
* Wraps scalar or array configuration into an array to simplify iteration.
|
|
2312
3093
|
*/
|