@cosyte/terminology 0.0.12 → 0.1.0
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/CHANGELOG.md +168 -0
- package/README.md +102 -10
- package/dist/index.cjs +254 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +221 -31
- package/dist/index.d.ts +221 -31
- package/dist/index.mjs +254 -51
- package/dist/index.mjs.map +1 -1
- package/package.json +48 -26
- package/vendor/ucum/NOTICE.md +3 -1
package/dist/index.mjs
CHANGED
|
@@ -70,19 +70,43 @@ var DIAGNOSTIC_CODES = {
|
|
|
70
70
|
TERM_CONCEPT_HEADER_NOT_BILLABLE: "TERM_CONCEPT_HEADER_NOT_BILLABLE",
|
|
71
71
|
/**
|
|
72
72
|
* A ValueSet `compose` part could **not be expanded**: an intensional `include`/`exclude` whose
|
|
73
|
-
* code system was not supplied, an unresolvable referenced value set,
|
|
74
|
-
* engine does not implement
|
|
73
|
+
* code system was not supplied, an unresolvable referenced value set, a `filter` operator the
|
|
74
|
+
* engine does not implement, or a component whose declared code system version disagrees with the
|
|
75
|
+
* supplied release (or names a pin that release carries no version to confirm). Surfaced as a
|
|
76
|
+
* typed outcome that marks the expansion **incomplete**;
|
|
75
77
|
* the engine **never** fabricates a member and **never** returns a silently-empty "no members"
|
|
76
78
|
* answer for a part it could not compute (a false "not a member" is a clinical error).
|
|
77
79
|
*/
|
|
78
80
|
TERM_VALUESET_CANNOT_EXPAND: "TERM_VALUESET_CANNOT_EXPAND",
|
|
79
81
|
/**
|
|
80
82
|
* A **pre-computed** `ValueSet.expansion` is **incomplete**: its `total` exceeds the number of
|
|
81
|
-
* `contains` entries,
|
|
82
|
-
* hazard)
|
|
83
|
-
*
|
|
83
|
+
* `contains` entries, it is flagged too-costly (the VSAC `$expand` >1200-code truncation
|
|
84
|
+
* hazard), or it is marked `unclosed` (the value set is unbounded because it includes
|
|
85
|
+
* post-coordinated content, so no expansion can enumerate it). The engine **never treats an
|
|
86
|
+
* incomplete expansion as complete membership**: a code absent from one is `undetermined`, never
|
|
87
|
+
* a confident "not a member". The diagnostic's `detail` says which of those it was.
|
|
84
88
|
*/
|
|
85
89
|
TERM_VALUESET_EXPANSION_TRUNCATED: "TERM_VALUESET_EXPANSION_TRUNCATED",
|
|
90
|
+
/**
|
|
91
|
+
* A `ValueSet.compose` component **enumerated** a code the release supplied for its `system` does
|
|
92
|
+
* not define, so the code was **not admitted** as a member. The never-fabricate invariant applied
|
|
93
|
+
* to an extensional selection: enumerating a code is the value set's claim that it exists, and
|
|
94
|
+
* asserting membership for it while holding the very release that shows it does not is the engine
|
|
95
|
+
* deciding against its own evidence.
|
|
96
|
+
*
|
|
97
|
+
* Raised **only** where the evidence is in hand: a release keyed by the component's `system` that
|
|
98
|
+
* the component's declared `version` (if any) agrees with. No release for that system, a declared
|
|
99
|
+
* version the supplied release does not agree with, or a component naming no `system` at all
|
|
100
|
+
* leaves the engine with no contrary evidence, so every enumerated code is carried and **no
|
|
101
|
+
* diagnostic is raised**: absence of evidence is not evidence the code is undefined.
|
|
102
|
+
*
|
|
103
|
+
* The answer stays **decided**, unlike {@link TERM_VALUESET_CANNOT_EXPAND}: the engine computed
|
|
104
|
+
* membership on the evidence it was given, so `expand` reports `complete: true` and
|
|
105
|
+
* `validateCodeInValueSet` reports a decided non-member for the dropped code, rather than a lower
|
|
106
|
+
* bound or a refusal. One diagnostic is raised per affected component, located by that
|
|
107
|
+
* component's own index path, however many of its codes were dropped.
|
|
108
|
+
*/
|
|
109
|
+
TERM_VALUESET_ENUMERATED_CODE_UNDEFINED: "TERM_VALUESET_ENUMERATED_CODE_UNDEFINED",
|
|
86
110
|
/**
|
|
87
111
|
* A UCUM unit expression is **not valid**: it does not parse against the UCUM grammar, or it
|
|
88
112
|
* names an atom absent from the vendored UCUM table. A **first-class typed outcome, never an
|
|
@@ -487,7 +511,7 @@ function provenanceOf(map, group, sourceSystem) {
|
|
|
487
511
|
if (group?.target !== void 0) out.targetSystem = group.target;
|
|
488
512
|
return Object.freeze(out);
|
|
489
513
|
}
|
|
490
|
-
function unmappedResult(source, mode, provenance, fixedTarget, otherMapUrl) {
|
|
514
|
+
function unmappedResult(source, mode, provenance, fixedTarget, otherMapUrl, notRelated = []) {
|
|
491
515
|
const out = {
|
|
492
516
|
unmapped: true,
|
|
493
517
|
code: "TERM_TRANSLATE_UNMAPPED",
|
|
@@ -497,13 +521,15 @@ function unmappedResult(source, mode, provenance, fixedTarget, otherMapUrl) {
|
|
|
497
521
|
};
|
|
498
522
|
if (fixedTarget !== void 0) out.fixedTarget = fixedTarget;
|
|
499
523
|
if (otherMapUrl !== void 0) out.otherMapUrl = otherMapUrl;
|
|
524
|
+
if (notRelated.length > 0) out.notRelated = Object.freeze(notRelated);
|
|
500
525
|
return Object.freeze(out);
|
|
501
526
|
}
|
|
502
527
|
function translate(sourceCoding, map) {
|
|
503
528
|
const source = coding(sourceCoding);
|
|
504
529
|
const system = source.system;
|
|
505
|
-
const
|
|
506
|
-
|
|
530
|
+
const rows = [];
|
|
531
|
+
const notRelated = [];
|
|
532
|
+
let firstRowGroup;
|
|
507
533
|
let sourceCodePresent = false;
|
|
508
534
|
let fallbackGroup;
|
|
509
535
|
for (const group of map.group) {
|
|
@@ -520,23 +546,45 @@ function translate(sourceCoding, map) {
|
|
|
520
546
|
...t.display !== void 0 ? { display: t.display } : {},
|
|
521
547
|
...group.targetVersion !== void 0 ? { version: group.targetVersion } : {}
|
|
522
548
|
});
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
549
|
+
if (t.equivalence === "disjoint") {
|
|
550
|
+
const assertion = {
|
|
551
|
+
target: targetCoding,
|
|
552
|
+
relationship: equivalenceToRelationship(t.equivalence),
|
|
553
|
+
equivalence: t.equivalence
|
|
554
|
+
};
|
|
555
|
+
if (t.comment !== void 0) assertion.comment = t.comment;
|
|
556
|
+
const frozen = Object.freeze(assertion);
|
|
557
|
+
notRelated.push(frozen);
|
|
558
|
+
rows.push(frozen);
|
|
559
|
+
} else {
|
|
560
|
+
const match = {
|
|
561
|
+
target: targetCoding,
|
|
562
|
+
relationship: equivalenceToRelationship(t.equivalence),
|
|
563
|
+
equivalence: t.equivalence
|
|
564
|
+
};
|
|
565
|
+
if (t.comment !== void 0) match.comment = t.comment;
|
|
566
|
+
rows.push(Object.freeze(match));
|
|
567
|
+
}
|
|
568
|
+
if (firstRowGroup === void 0) firstRowGroup = group;
|
|
531
569
|
}
|
|
532
570
|
}
|
|
533
571
|
}
|
|
534
|
-
if (
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
572
|
+
if (rows.length > 0) {
|
|
573
|
+
if (notRelated.length < rows.length) {
|
|
574
|
+
return Object.freeze({
|
|
575
|
+
unmapped: false,
|
|
576
|
+
matches: Object.freeze(rows),
|
|
577
|
+
provenance: provenanceOf(map, firstRowGroup, system)
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
return unmappedResult(
|
|
581
|
+
source,
|
|
582
|
+
"none",
|
|
583
|
+
provenanceOf(map, firstRowGroup, system),
|
|
584
|
+
void 0,
|
|
585
|
+
void 0,
|
|
586
|
+
notRelated
|
|
587
|
+
);
|
|
540
588
|
}
|
|
541
589
|
if (!sourceCodePresent && fallbackGroup?.unmapped !== void 0) {
|
|
542
590
|
const um = fallbackGroup.unmapped;
|
|
@@ -559,7 +607,7 @@ function translate(sourceCoding, map) {
|
|
|
559
607
|
return unmappedResult(
|
|
560
608
|
source,
|
|
561
609
|
"none",
|
|
562
|
-
provenanceOf(map,
|
|
610
|
+
provenanceOf(map, firstRowGroup, system),
|
|
563
611
|
void 0,
|
|
564
612
|
void 0
|
|
565
613
|
);
|
|
@@ -1069,6 +1117,7 @@ function validateCode(cs, code) {
|
|
|
1069
1117
|
|
|
1070
1118
|
// src/valueset/load.ts
|
|
1071
1119
|
var TOO_COSTLY_EXTENSION = "http://hl7.org/fhir/StructureDefinition/valueset-toocostly";
|
|
1120
|
+
var UNCLOSED_EXTENSION = "http://hl7.org/fhir/StructureDefinition/valueset-unclosed";
|
|
1072
1121
|
function malformed2(path, fault) {
|
|
1073
1122
|
throw new TerminologyError(FATAL_CODES.TERM_VALUESET_MALFORMED, `ValueSet ${path}: ${fault}`);
|
|
1074
1123
|
}
|
|
@@ -1147,7 +1196,13 @@ function loadCompose(raw, path) {
|
|
|
1147
1196
|
(c, i) => loadComponent(c, `${path}.exclude[${String(i)}]`)
|
|
1148
1197
|
)
|
|
1149
1198
|
);
|
|
1150
|
-
|
|
1199
|
+
const out = { include, exclude };
|
|
1200
|
+
if (raw["inactive"] !== void 0) {
|
|
1201
|
+
const inactive = getBoolean(raw, "inactive");
|
|
1202
|
+
if (inactive === void 0) malformed2(`${path}.inactive`, "'inactive' is not a boolean");
|
|
1203
|
+
out.inactive = inactive;
|
|
1204
|
+
}
|
|
1205
|
+
return Object.freeze(out);
|
|
1151
1206
|
}
|
|
1152
1207
|
function loadContains(raw, path) {
|
|
1153
1208
|
if (!isJsonObject(raw)) malformed2(path, "contains entry is not an object");
|
|
@@ -1167,6 +1222,18 @@ function isTooCostly(raw) {
|
|
|
1167
1222
|
}
|
|
1168
1223
|
return false;
|
|
1169
1224
|
}
|
|
1225
|
+
function isUnclosed(raw) {
|
|
1226
|
+
for (const ext of getArray(raw, "extension") ?? []) {
|
|
1227
|
+
if (typeof ext === "string") {
|
|
1228
|
+
if (ext === UNCLOSED_EXTENSION) return true;
|
|
1229
|
+
continue;
|
|
1230
|
+
}
|
|
1231
|
+
if (getString(ext, "url") !== UNCLOSED_EXTENSION) continue;
|
|
1232
|
+
if (getBoolean(ext, "valueBoolean") === false) continue;
|
|
1233
|
+
return true;
|
|
1234
|
+
}
|
|
1235
|
+
return false;
|
|
1236
|
+
}
|
|
1170
1237
|
function loadExpansion(raw, path) {
|
|
1171
1238
|
if (!isJsonObject(raw)) malformed2(path, "expansion is not an object");
|
|
1172
1239
|
const contains = Object.freeze(
|
|
@@ -1175,8 +1242,9 @@ function loadExpansion(raw, path) {
|
|
|
1175
1242
|
)
|
|
1176
1243
|
);
|
|
1177
1244
|
const total = getNumber(raw, "total");
|
|
1178
|
-
const
|
|
1179
|
-
const
|
|
1245
|
+
const unclosed = isUnclosed(raw);
|
|
1246
|
+
const truncated2 = total !== void 0 && total > contains.length || isTooCostly(raw) || unclosed;
|
|
1247
|
+
const out = { contains, truncated: truncated2, unclosed };
|
|
1180
1248
|
if (total !== void 0) out.total = total;
|
|
1181
1249
|
return Object.freeze(out);
|
|
1182
1250
|
}
|
|
@@ -1246,11 +1314,12 @@ function valueSet(value) {
|
|
|
1246
1314
|
value.split(",").map((v) => v.trim()).filter((v) => v.length > 0)
|
|
1247
1315
|
);
|
|
1248
1316
|
}
|
|
1249
|
-
function
|
|
1317
|
+
function propertyValues(concept, property) {
|
|
1318
|
+
const values = [];
|
|
1250
1319
|
for (const p of concept.properties) {
|
|
1251
|
-
if (p.code === property)
|
|
1320
|
+
if (p.code === property) values.push(typeof p.value === "string" ? p.value : String(p.value));
|
|
1252
1321
|
}
|
|
1253
|
-
return
|
|
1322
|
+
return values;
|
|
1254
1323
|
}
|
|
1255
1324
|
var UNSUPPORTED = Object.freeze({ matched: false, supported: false });
|
|
1256
1325
|
function matchesFilter(concept, filter, sub) {
|
|
@@ -1266,21 +1335,30 @@ function matchesFilter(concept, filter, sub) {
|
|
|
1266
1335
|
case "is-not-a":
|
|
1267
1336
|
return { matched: !isA(sub, concept.code, filter.value), supported: true };
|
|
1268
1337
|
case "=": {
|
|
1269
|
-
|
|
1270
|
-
return {
|
|
1338
|
+
if (onCode) return { matched: concept.code === filter.value, supported: true };
|
|
1339
|
+
return {
|
|
1340
|
+
matched: propertyValues(concept, filter.property).includes(filter.value),
|
|
1341
|
+
supported: true
|
|
1342
|
+
};
|
|
1271
1343
|
}
|
|
1272
1344
|
case "in": {
|
|
1273
1345
|
const set = valueSet(filter.value);
|
|
1274
|
-
|
|
1275
|
-
return {
|
|
1346
|
+
if (onCode) return { matched: set.has(concept.code), supported: true };
|
|
1347
|
+
return {
|
|
1348
|
+
matched: propertyValues(concept, filter.property).some((v) => set.has(v)),
|
|
1349
|
+
supported: true
|
|
1350
|
+
};
|
|
1276
1351
|
}
|
|
1277
1352
|
case "not-in": {
|
|
1278
1353
|
const set = valueSet(filter.value);
|
|
1279
|
-
|
|
1280
|
-
return {
|
|
1354
|
+
if (onCode) return { matched: !set.has(concept.code), supported: true };
|
|
1355
|
+
return {
|
|
1356
|
+
matched: propertyValues(concept, filter.property).every((v) => !set.has(v)),
|
|
1357
|
+
supported: true
|
|
1358
|
+
};
|
|
1281
1359
|
}
|
|
1282
1360
|
case "exists": {
|
|
1283
|
-
const present = onCode ? true :
|
|
1361
|
+
const present = onCode ? true : propertyValues(concept, filter.property).length > 0;
|
|
1284
1362
|
const wantPresent = filter.value.trim().toLowerCase() === "true";
|
|
1285
1363
|
return { matched: present === wantPresent, supported: true };
|
|
1286
1364
|
}
|
|
@@ -1321,6 +1399,43 @@ function truncated(detail, path) {
|
|
|
1321
1399
|
d.path = path;
|
|
1322
1400
|
return Object.freeze(d);
|
|
1323
1401
|
}
|
|
1402
|
+
function enumeratedUndefined(detail, path) {
|
|
1403
|
+
const d = {
|
|
1404
|
+
code: "TERM_VALUESET_ENUMERATED_CODE_UNDEFINED",
|
|
1405
|
+
detail
|
|
1406
|
+
};
|
|
1407
|
+
if (path !== void 0) d.path = path;
|
|
1408
|
+
return Object.freeze(d);
|
|
1409
|
+
}
|
|
1410
|
+
function expansionIncompleteDetail(unclosed) {
|
|
1411
|
+
return unclosed ? "pre-computed expansion is incomplete (marked unclosed: the value set is unbounded, so the snapshot is a sample of its membership)" : "pre-computed expansion is incomplete (truncated or too-costly)";
|
|
1412
|
+
}
|
|
1413
|
+
var ACTIVITY_UNCHECKABLE_DETAIL = "active-only value set: no usable code system release to check whether a selected code is active";
|
|
1414
|
+
var ENUMERATED_UNDEFINED_DETAIL = "enumerated code is not defined by the supplied code system release, so it is not a member";
|
|
1415
|
+
function usableRelease(component, system, ctx) {
|
|
1416
|
+
if (system === void 0) return void 0;
|
|
1417
|
+
const cs = ctx.codeSystems?.get(system);
|
|
1418
|
+
if (cs === void 0) return void 0;
|
|
1419
|
+
if (system === component.system && component.version !== void 0 && cs.version !== component.version) {
|
|
1420
|
+
return void 0;
|
|
1421
|
+
}
|
|
1422
|
+
return cs;
|
|
1423
|
+
}
|
|
1424
|
+
function screenActive(members, component, ctx) {
|
|
1425
|
+
const kept = /* @__PURE__ */ new Map();
|
|
1426
|
+
let uncheckable = false;
|
|
1427
|
+
for (const [key, member] of members) {
|
|
1428
|
+
const cs = usableRelease(component, member.system, ctx);
|
|
1429
|
+
if (cs === void 0) {
|
|
1430
|
+
uncheckable = true;
|
|
1431
|
+
continue;
|
|
1432
|
+
}
|
|
1433
|
+
const concept = cs.concepts.get(member.code);
|
|
1434
|
+
if (concept?.status !== void 0 && !concept.status.active) continue;
|
|
1435
|
+
kept.set(key, member);
|
|
1436
|
+
}
|
|
1437
|
+
return { members: kept, uncheckable };
|
|
1438
|
+
}
|
|
1324
1439
|
function underPath(d, prefix) {
|
|
1325
1440
|
const out = { code: d.code, detail: d.detail };
|
|
1326
1441
|
out.path = d.path === void 0 ? prefix : `${prefix}/${d.path}`;
|
|
@@ -1358,7 +1473,7 @@ function expandReferencedValueSets(urls, ctx, visited, path) {
|
|
|
1358
1473
|
}
|
|
1359
1474
|
return { members: acc ?? /* @__PURE__ */ new Map(), complete, diagnostics };
|
|
1360
1475
|
}
|
|
1361
|
-
function expandComponent(component, ctx, visited, path) {
|
|
1476
|
+
function expandComponent(component, ctx, visited, path, activeOnly = false) {
|
|
1362
1477
|
const diagnostics = [];
|
|
1363
1478
|
let complete = true;
|
|
1364
1479
|
const { system, version, concept, filter, valueSet: valueSet2 } = component;
|
|
@@ -1368,9 +1483,18 @@ function expandComponent(component, ctx, visited, path) {
|
|
|
1368
1483
|
let base = null;
|
|
1369
1484
|
if (hasConcept) {
|
|
1370
1485
|
base = /* @__PURE__ */ new Map();
|
|
1486
|
+
const release = usableRelease(component, system, ctx);
|
|
1487
|
+
let anyUndefined = false;
|
|
1371
1488
|
for (const c of concept) {
|
|
1372
|
-
|
|
1489
|
+
const defined = release?.concepts.get(c.code);
|
|
1490
|
+
if (release !== void 0 && defined === void 0) {
|
|
1491
|
+
anyUndefined = true;
|
|
1492
|
+
continue;
|
|
1493
|
+
}
|
|
1494
|
+
const display = c.display ?? defined?.display;
|
|
1495
|
+
base.set(codingKey(system, c.code), makeCoding(system, c.code, display, version));
|
|
1373
1496
|
}
|
|
1497
|
+
if (anyUndefined) diagnostics.push(enumeratedUndefined(ENUMERATED_UNDEFINED_DETAIL, path));
|
|
1374
1498
|
} else if (hasFilter || system !== void 0 && !hasVs) {
|
|
1375
1499
|
if (system === void 0) {
|
|
1376
1500
|
diagnostics.push(cannotExpand("intensional filter without a code system 'system'", path));
|
|
@@ -1381,6 +1505,27 @@ function expandComponent(component, ctx, visited, path) {
|
|
|
1381
1505
|
diagnostics.push(cannotExpand("code system not supplied for intensional include", path));
|
|
1382
1506
|
return { members: /* @__PURE__ */ new Map(), complete: false, diagnostics };
|
|
1383
1507
|
}
|
|
1508
|
+
let stampedVersion = version;
|
|
1509
|
+
if (version !== void 0 && cs.version !== version) {
|
|
1510
|
+
if (cs.version === void 0) {
|
|
1511
|
+
diagnostics.push(
|
|
1512
|
+
cannotExpand(
|
|
1513
|
+
"supplied code system declares no version, so the component's declared version is unconfirmed",
|
|
1514
|
+
path
|
|
1515
|
+
)
|
|
1516
|
+
);
|
|
1517
|
+
complete = false;
|
|
1518
|
+
stampedVersion = void 0;
|
|
1519
|
+
} else {
|
|
1520
|
+
diagnostics.push(
|
|
1521
|
+
cannotExpand(
|
|
1522
|
+
"supplied code system version disagrees with the component's declared version",
|
|
1523
|
+
path
|
|
1524
|
+
)
|
|
1525
|
+
);
|
|
1526
|
+
return { members: /* @__PURE__ */ new Map(), complete: false, diagnostics };
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1384
1529
|
if (hasFilter) {
|
|
1385
1530
|
const unsupported = unsupportedOps(filter);
|
|
1386
1531
|
if (unsupported.length > 0) {
|
|
@@ -1393,7 +1538,7 @@ function expandComponent(component, ctx, visited, path) {
|
|
|
1393
1538
|
if (matchesAllFilters(concpt, filter, sub).matched) {
|
|
1394
1539
|
base.set(
|
|
1395
1540
|
codingKey(system, concpt.code),
|
|
1396
|
-
makeCoding(system, concpt.code, concpt.display,
|
|
1541
|
+
makeCoding(system, concpt.code, concpt.display, stampedVersion)
|
|
1397
1542
|
);
|
|
1398
1543
|
}
|
|
1399
1544
|
}
|
|
@@ -1402,7 +1547,7 @@ function expandComponent(component, ctx, visited, path) {
|
|
|
1402
1547
|
for (const concpt of cs.concepts.values()) {
|
|
1403
1548
|
base.set(
|
|
1404
1549
|
codingKey(system, concpt.code),
|
|
1405
|
-
makeCoding(system, concpt.code, concpt.display,
|
|
1550
|
+
makeCoding(system, concpt.code, concpt.display, stampedVersion)
|
|
1406
1551
|
);
|
|
1407
1552
|
}
|
|
1408
1553
|
}
|
|
@@ -1416,7 +1561,14 @@ function expandComponent(component, ctx, visited, path) {
|
|
|
1416
1561
|
for (const [k, c] of [...base]) if (c.system !== system) base.delete(k);
|
|
1417
1562
|
}
|
|
1418
1563
|
}
|
|
1419
|
-
|
|
1564
|
+
const members = base ?? /* @__PURE__ */ new Map();
|
|
1565
|
+
if (!activeOnly) return { members, complete, diagnostics };
|
|
1566
|
+
const screened = screenActive(members, component, ctx);
|
|
1567
|
+
if (screened.uncheckable) {
|
|
1568
|
+
diagnostics.push(cannotExpand(ACTIVITY_UNCHECKABLE_DETAIL, path));
|
|
1569
|
+
complete = false;
|
|
1570
|
+
}
|
|
1571
|
+
return { members: screened.members, complete, diagnostics };
|
|
1420
1572
|
}
|
|
1421
1573
|
function expandInternal(vs, ctx, visited) {
|
|
1422
1574
|
if (vs.expansion !== void 0) {
|
|
@@ -1432,9 +1584,7 @@ function expandInternal(vs, ctx, visited) {
|
|
|
1432
1584
|
let complete = true;
|
|
1433
1585
|
if (vs.expansion.truncated) {
|
|
1434
1586
|
complete = false;
|
|
1435
|
-
diagnostics.push(
|
|
1436
|
-
truncated("pre-computed expansion is incomplete (truncated or too-costly)", "expansion")
|
|
1437
|
-
);
|
|
1587
|
+
diagnostics.push(truncated(expansionIncompleteDetail(vs.expansion.unclosed), "expansion"));
|
|
1438
1588
|
}
|
|
1439
1589
|
const out = {
|
|
1440
1590
|
complete,
|
|
@@ -1448,8 +1598,9 @@ function expandInternal(vs, ctx, visited) {
|
|
|
1448
1598
|
const included = /* @__PURE__ */ new Map();
|
|
1449
1599
|
const diagnostics = [];
|
|
1450
1600
|
let complete = true;
|
|
1601
|
+
const activeOnly = vs.compose.inactive === false;
|
|
1451
1602
|
for (const [i, inc] of vs.compose.include.entries()) {
|
|
1452
|
-
const r = expandComponent(inc, ctx, visited, `compose.include[${String(i)}]
|
|
1603
|
+
const r = expandComponent(inc, ctx, visited, `compose.include[${String(i)}]`, activeOnly);
|
|
1453
1604
|
if (!r.complete) complete = false;
|
|
1454
1605
|
for (const d of r.diagnostics) diagnostics.push(d);
|
|
1455
1606
|
for (const [k, c] of r.members) if (!included.has(k)) included.set(k, c);
|
|
@@ -1487,14 +1638,27 @@ function cannotExpand2(detail, path) {
|
|
|
1487
1638
|
if (path !== void 0) d.path = path;
|
|
1488
1639
|
return Object.freeze(d);
|
|
1489
1640
|
}
|
|
1641
|
+
function expansionIncompleteDetail2(unclosed) {
|
|
1642
|
+
return unclosed ? "pre-computed expansion is incomplete (marked unclosed: the value set is unbounded, so the snapshot is a sample of its membership)" : "pre-computed expansion is incomplete (truncated or too-costly)";
|
|
1643
|
+
}
|
|
1644
|
+
var ACTIVITY_UNCHECKABLE_DETAIL2 = "active-only value set: no usable code system release to check whether a selected code is active";
|
|
1645
|
+
function usableRelease2(component, system, ctx) {
|
|
1646
|
+
if (system === void 0) return void 0;
|
|
1647
|
+
const cs = ctx.codeSystems?.get(system);
|
|
1648
|
+
if (cs === void 0) return void 0;
|
|
1649
|
+
if (system === component.system && component.version !== void 0 && cs.version !== component.version) {
|
|
1650
|
+
return void 0;
|
|
1651
|
+
}
|
|
1652
|
+
return cs;
|
|
1653
|
+
}
|
|
1490
1654
|
function underPath2(d, prefix) {
|
|
1491
1655
|
const out = { code: d.code, detail: d.detail };
|
|
1492
1656
|
out.path = d.path === void 0 ? prefix : `${prefix}/${d.path}`;
|
|
1493
1657
|
return Object.freeze(out);
|
|
1494
1658
|
}
|
|
1495
|
-
function matchComponent(target, component, ctx, visited, path) {
|
|
1659
|
+
function matchComponent(target, component, ctx, visited, path, activeOnly = false) {
|
|
1496
1660
|
const diagnostics = [];
|
|
1497
|
-
const { system, concept, filter, valueSet: valueSet2 } = component;
|
|
1661
|
+
const { system, version, concept, filter, valueSet: valueSet2 } = component;
|
|
1498
1662
|
const hasConcept = concept !== void 0;
|
|
1499
1663
|
const hasFilter = filter !== void 0 && filter.length > 0;
|
|
1500
1664
|
const hasVs = valueSet2 !== void 0 && valueSet2.length > 0;
|
|
@@ -1503,7 +1667,8 @@ function matchComponent(target, component, ctx, visited, path) {
|
|
|
1503
1667
|
if (system !== void 0 && target.system !== void 0 && system !== target.system) {
|
|
1504
1668
|
baseMatched = false;
|
|
1505
1669
|
} else if (hasConcept) {
|
|
1506
|
-
|
|
1670
|
+
const release = usableRelease2(component, system, ctx);
|
|
1671
|
+
baseMatched = concept.some((c) => c.code === target.code) && (release === void 0 || release.concepts.get(target.code) !== void 0);
|
|
1507
1672
|
} else if (hasFilter || system !== void 0 && !hasVs) {
|
|
1508
1673
|
if (system === void 0) {
|
|
1509
1674
|
diagnostics.push(cannotExpand2("intensional filter without a code system 'system'", path));
|
|
@@ -1515,6 +1680,24 @@ function matchComponent(target, component, ctx, visited, path) {
|
|
|
1515
1680
|
diagnostics.push(cannotExpand2("code system not supplied for intensional include", path));
|
|
1516
1681
|
baseMatched = false;
|
|
1517
1682
|
baseComplete = false;
|
|
1683
|
+
} else if (version !== void 0 && cs.version !== version) {
|
|
1684
|
+
if (cs.version === void 0) {
|
|
1685
|
+
diagnostics.push(
|
|
1686
|
+
cannotExpand2(
|
|
1687
|
+
"supplied code system declares no version, so the component's declared version is unconfirmed",
|
|
1688
|
+
path
|
|
1689
|
+
)
|
|
1690
|
+
);
|
|
1691
|
+
} else {
|
|
1692
|
+
diagnostics.push(
|
|
1693
|
+
cannotExpand2(
|
|
1694
|
+
"supplied code system version disagrees with the component's declared version",
|
|
1695
|
+
path
|
|
1696
|
+
)
|
|
1697
|
+
);
|
|
1698
|
+
}
|
|
1699
|
+
baseMatched = false;
|
|
1700
|
+
baseComplete = false;
|
|
1518
1701
|
} else if (hasFilter) {
|
|
1519
1702
|
if (unsupportedOps(filter).length > 0) {
|
|
1520
1703
|
diagnostics.push(cannotExpand2("unsupported filter operator", path));
|
|
@@ -1529,6 +1712,7 @@ function matchComponent(target, component, ctx, visited, path) {
|
|
|
1529
1712
|
}
|
|
1530
1713
|
}
|
|
1531
1714
|
}
|
|
1715
|
+
if (system !== void 0 && target.system === void 0 && baseComplete) baseMatched = false;
|
|
1532
1716
|
let refDefiniteFalse = false;
|
|
1533
1717
|
let refUndetermined = false;
|
|
1534
1718
|
if (hasVs) {
|
|
@@ -1561,6 +1745,17 @@ function matchComponent(target, component, ctx, visited, path) {
|
|
|
1561
1745
|
return { matched: false, complete: true, diagnostics };
|
|
1562
1746
|
}
|
|
1563
1747
|
if (baseDefiniteTrue && refDefiniteTrue) {
|
|
1748
|
+
if (activeOnly) {
|
|
1749
|
+
const cs = usableRelease2(component, target.system, ctx);
|
|
1750
|
+
if (cs === void 0) {
|
|
1751
|
+
diagnostics.push(cannotExpand2(ACTIVITY_UNCHECKABLE_DETAIL2, path));
|
|
1752
|
+
return { matched: false, complete: false, diagnostics };
|
|
1753
|
+
}
|
|
1754
|
+
const concept2 = cs.concepts.get(target.code);
|
|
1755
|
+
if (concept2?.status !== void 0 && !concept2.status.active) {
|
|
1756
|
+
return { matched: false, complete: true, diagnostics };
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1564
1759
|
return { matched: true, complete: true, diagnostics };
|
|
1565
1760
|
}
|
|
1566
1761
|
return { matched: false, complete: false, diagnostics };
|
|
@@ -1584,7 +1779,7 @@ function validateInternal(target, vs, ctx, visited) {
|
|
|
1584
1779
|
if (found) return decided(true, target);
|
|
1585
1780
|
if (vs.expansion.truncated) {
|
|
1586
1781
|
return undetermined(target, [
|
|
1587
|
-
cannotExpand2(
|
|
1782
|
+
cannotExpand2(expansionIncompleteDetail2(vs.expansion.unclosed), "expansion")
|
|
1588
1783
|
]);
|
|
1589
1784
|
}
|
|
1590
1785
|
return decided(false, target);
|
|
@@ -1593,8 +1788,16 @@ function validateInternal(target, vs, ctx, visited) {
|
|
|
1593
1788
|
const diagnostics = [];
|
|
1594
1789
|
let includedDefinite = false;
|
|
1595
1790
|
let anyIncludeUndetermined = false;
|
|
1791
|
+
const activeOnly = vs.compose.inactive === false;
|
|
1596
1792
|
for (const [i, inc] of vs.compose.include.entries()) {
|
|
1597
|
-
const m = matchComponent(
|
|
1793
|
+
const m = matchComponent(
|
|
1794
|
+
target,
|
|
1795
|
+
inc,
|
|
1796
|
+
ctx,
|
|
1797
|
+
visited,
|
|
1798
|
+
`compose.include[${String(i)}]`,
|
|
1799
|
+
activeOnly
|
|
1800
|
+
);
|
|
1598
1801
|
for (const d of m.diagnostics) diagnostics.push(d);
|
|
1599
1802
|
if (m.complete && m.matched) includedDefinite = true;
|
|
1600
1803
|
if (!m.complete) anyIncludeUndetermined = true;
|
|
@@ -4774,7 +4977,7 @@ function approximateMatch(graph, query, options = {}) {
|
|
|
4774
4977
|
}
|
|
4775
4978
|
|
|
4776
4979
|
// src/index.ts
|
|
4777
|
-
var VERSION = "0.0
|
|
4980
|
+
var VERSION = "0.1.0";
|
|
4778
4981
|
|
|
4779
4982
|
export { COMPLEX_MAP_SOURCE_SYSTEM, DIAGNOSTIC_CODES, FATAL_CODES, HL7_V2_OID_ROOT, HL7_V2_URL_PREFIX, ICD10CM_ORDER_FILE_FIELDS, ICD10CM_SYSTEM, ICD9CM_SYSTEM, MAP_CATEGORIES, NO_MAP_CATEGORIES, RELA, RELA_INVERSE, RXNORM_SYSTEM, SNOMEDCT_SYSTEM, SYSTEM_IDENTITIES, TERM_TYPES, TerminologyError, VERSION, applyComplexMap, applyGem, approximateMatch, asTermType, brandsFor, buildSubsumption, codeableConcept, coding, consistsOf, defaultStatusMapper, doseFormsOf, expand, genericFor, getArray, getBoolean, getConcept, getNumber, getString, ingredientsOf, invertGem, isA, isJsonObject, isSynonymTermType, isUnknownSystem, loadCodeSystem, loadComplexMap, loadConceptMap, loadGems, loadRxNormGraph, loadUcumEssence, loadValueSet, lookup, makeStatus, matchesFilter, parseCsv, parseCsvSource, parseFhirCodeSystem, parseFixedWidth, parseRrf, parseRrfLine, parseUcum, reduce, relatedByRela, resolveNdc, resolveSystem, sliceField, translate, ucumEqual, unsupportedOps, validateCode, validateCodeInValueSet, validateUcum };
|
|
4780
4983
|
//# sourceMappingURL=index.mjs.map
|