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