@cosyte/terminology 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -210,7 +210,7 @@ var FATAL_CODES = {
210
210
  /**
211
211
  * An **inversion** of a directional crosswalk was requested: a forbidden operation. The GEMs and
212
212
  * the SNOMED→ICD-10-CM map are authoritative in **one direction only**; a forward map is not the
213
- * inverse of the backward map. Thrown by {@link ../crosswalk/gems.invertGem} so the
213
+ * inverse of the backward map. Thrown by {@link invertGem} so the
214
214
  * never-invert refusal is a first-class, discoverable contract rather than a silent absence.
215
215
  */
216
216
  TERM_MAP_NOT_INVERTIBLE: "TERM_MAP_NOT_INVERTIBLE"
@@ -762,10 +762,10 @@ function parseCsvSource(source) {
762
762
  const codeIdx = requireHeader(header, source.columns.code, "code");
763
763
  const displayIdx = requireHeader(header, source.columns.display, "display");
764
764
  const statusIdx = source.columns.status !== void 0 ? requireHeader(header, source.columns.status, "status") : void 0;
765
- const propIdx = (source.columns.properties ?? []).map((n) => ({
766
- name: n,
767
- index: requireHeader(header, n, "property")
768
- }));
765
+ const propIdx = [];
766
+ for (const n of source.columns.properties ?? []) {
767
+ propIdx.push({ name: n, index: requireHeader(header, n, "property") });
768
+ }
769
769
  const needCols = Math.max(codeIdx, displayIdx, statusIdx ?? 0, ...propIdx.map((p) => p.index)) + 1;
770
770
  const concepts = /* @__PURE__ */ new Map();
771
771
  for (let r = 1; r < rows.length; r++) {
@@ -854,9 +854,10 @@ function collectConcepts(rawConcepts, into, warnings, parentCode) {
854
854
  if (code !== void 0 && code !== "" && !into.has(code)) {
855
855
  const props = [];
856
856
  for (const p of getArray(raw, "property") ?? []) {
857
+ if (!isJsonObject(p)) continue;
857
858
  const pcode = getString(p, "code");
858
859
  if (pcode === void 0) continue;
859
- const value = isJsonObject(p) ? readPropertyValue(p) : void 0;
860
+ const value = readPropertyValue(p);
860
861
  if (value !== void 0) props.push(Object.freeze({ code: pcode, value }));
861
862
  }
862
863
  if (parentCode !== void 0 && !props.some((p) => p.code === "parent" && p.value === parentCode)) {
@@ -919,9 +920,21 @@ var ICD10CM_ORDER_FILE_FIELDS = Object.freeze({
919
920
  Object.freeze({ name: "shortDescription", slice: Object.freeze({ start: 16, end: 76 }) })
920
921
  ])
921
922
  });
923
+ function requirePropertyFields(fields) {
924
+ for (const p of fields.properties ?? []) {
925
+ const entry = p;
926
+ if (typeof entry !== "object" || entry === null) {
927
+ throw new TerminologyError(
928
+ FATAL_CODES.TERM_CODESYSTEM_MALFORMED,
929
+ "fixed-width source: a configured property field is not an object"
930
+ );
931
+ }
932
+ }
933
+ }
922
934
  function parseFixedWidth(source) {
923
935
  const statusMap = source.statusMap ?? defaultStatusMapper;
924
936
  const fields = source.fields;
937
+ requirePropertyFields(fields);
925
938
  const concepts = /* @__PURE__ */ new Map();
926
939
  const warnings = [];
927
940
  const lines = source.content.split(/\r?\n/);
@@ -983,7 +996,16 @@ function parseRrfLine(line) {
983
996
  function maxColumn(source) {
984
997
  const { code, display, status, properties } = source.columns;
985
998
  let max = Math.max(code, display, status ?? 0);
986
- for (const p of properties ?? []) max = Math.max(max, p.column);
999
+ for (const p of properties ?? []) {
1000
+ const entry = p;
1001
+ if (typeof entry !== "object" || entry === null) {
1002
+ throw new TerminologyError(
1003
+ FATAL_CODES.TERM_CODESYSTEM_MALFORMED,
1004
+ "RRF source: a configured property column is not an object"
1005
+ );
1006
+ }
1007
+ max = Math.max(max, p.column);
1008
+ }
987
1009
  return max;
988
1010
  }
989
1011
  function cell(cells, index) {
@@ -1390,8 +1412,7 @@ function makeCoding(system, code, display, version) {
1390
1412
  return coding(init);
1391
1413
  }
1392
1414
  function cannotExpand(detail, path) {
1393
- const d = { code: "TERM_VALUESET_CANNOT_EXPAND", detail };
1394
- if (path !== void 0) d.path = path;
1415
+ const d = { code: "TERM_VALUESET_CANNOT_EXPAND", detail, path };
1395
1416
  return Object.freeze(d);
1396
1417
  }
1397
1418
  function truncated(detail, path) {
@@ -1405,9 +1426,9 @@ function truncated(detail, path) {
1405
1426
  function enumeratedUndefined(detail, path) {
1406
1427
  const d = {
1407
1428
  code: "TERM_VALUESET_ENUMERATED_CODE_UNDEFINED",
1408
- detail
1429
+ detail,
1430
+ path
1409
1431
  };
1410
- if (path !== void 0) d.path = path;
1411
1432
  return Object.freeze(d);
1412
1433
  }
1413
1434
  function expansionIncompleteDetail(unclosed) {
@@ -1441,7 +1462,8 @@ function screenActive(members, component, ctx) {
1441
1462
  }
1442
1463
  function underPath(d, prefix) {
1443
1464
  const out = { code: d.code, detail: d.detail };
1444
- out.path = d.path === void 0 ? prefix : `${prefix}/${d.path}`;
1465
+ if (d.path === void 0) out.path = prefix;
1466
+ else out.path = `${prefix}/${d.path}`;
1445
1467
  return Object.freeze(out);
1446
1468
  }
1447
1469
  function intersectInto(into, other) {
@@ -1637,8 +1659,7 @@ function expand(vs, ctx = {}) {
1637
1659
 
1638
1660
  // src/valueset/validate.ts
1639
1661
  function cannotExpand2(detail, path) {
1640
- const d = { code: "TERM_VALUESET_CANNOT_EXPAND", detail };
1641
- if (path !== void 0) d.path = path;
1662
+ const d = { code: "TERM_VALUESET_CANNOT_EXPAND", detail, path };
1642
1663
  return Object.freeze(d);
1643
1664
  }
1644
1665
  function expansionIncompleteDetail2(unclosed) {
@@ -1656,7 +1677,8 @@ function usableRelease2(component, system, ctx) {
1656
1677
  }
1657
1678
  function underPath2(d, prefix) {
1658
1679
  const out = { code: d.code, detail: d.detail };
1659
- out.path = d.path === void 0 ? prefix : `${prefix}/${d.path}`;
1680
+ if (d.path === void 0) out.path = prefix;
1681
+ else out.path = `${prefix}/${d.path}`;
1660
1682
  return Object.freeze(out);
1661
1683
  }
1662
1684
  function matchComponent(target, component, ctx, visited, path, activeOnly = false) {
@@ -1716,10 +1738,13 @@ function matchComponent(target, component, ctx, visited, path, activeOnly = fals
1716
1738
  }
1717
1739
  }
1718
1740
  if (system !== void 0 && target.system === void 0 && baseComplete) baseMatched = false;
1741
+ if (baseMatched === null && !hasVs) return { matched: false, complete: true, diagnostics };
1719
1742
  let refDefiniteFalse = false;
1720
1743
  let refUndetermined = false;
1721
1744
  if (hasVs) {
1745
+ let refsRead = 0;
1722
1746
  for (const [i, url] of valueSet2.entries()) {
1747
+ refsRead++;
1723
1748
  const refPath = `${path}.valueSet[${String(i)}]`;
1724
1749
  if (visited.has(url)) {
1725
1750
  diagnostics.push(cannotExpand2("cyclic value set reference", refPath));
@@ -1740,6 +1765,7 @@ function matchComponent(target, component, ctx, visited, path, activeOnly = fals
1740
1765
  refDefiniteFalse = true;
1741
1766
  }
1742
1767
  }
1768
+ if (refsRead === 0) refDefiniteFalse = true;
1743
1769
  }
1744
1770
  const baseDefiniteFalse = baseMatched === false && baseComplete;
1745
1771
  const baseDefiniteTrue = baseMatched === null || baseMatched === true && baseComplete;
@@ -3991,6 +4017,9 @@ function atBoundary(s, pos) {
3991
4017
  const c = s[pos];
3992
4018
  return c === "." || c === "/" || c === ")";
3993
4019
  }
4020
+ function prefixRank(m) {
4021
+ return m.prefix ? 1 : 0;
4022
+ }
3994
4023
  function simpleMatches(essence, s, pos) {
3995
4024
  const out = [];
3996
4025
  for (const atom of essence.atoms) {
@@ -4005,7 +4034,7 @@ function simpleMatches(essence, s, pos) {
4005
4034
  }
4006
4035
  }
4007
4036
  }
4008
- out.sort((a, b) => b.len - a.len || (a.prefix ? 1 : 0) - (b.prefix ? 1 : 0));
4037
+ out.sort((a, b) => b.len - a.len || prefixRank(a) - prefixRank(b));
4009
4038
  return out;
4010
4039
  }
4011
4040
  var MAX_PAREN_DEPTH = 100;
@@ -4410,7 +4439,7 @@ var MAP_CATEGORIES = {
4410
4439
  NO_MAP: "447638001",
4411
4440
  /** `447639009`: "Map of source concept is context dependent" (needs runtime patient context). */
4412
4441
  CONTEXT_DEPENDENT: "447639009",
4413
- /** `447640006`: "Source concept is ambiguous" (surface candidates, never pick one). */
4442
+ /** `447640006`: "Source concept is ambiguous" (carried through on the result; the rule decides). */
4414
4443
  AMBIGUOUS: "447640006"
4415
4444
  };
4416
4445
  var NO_MAP_CATEGORIES = /* @__PURE__ */ new Set([MAP_CATEGORIES.NO_MAP]);
@@ -4543,8 +4572,15 @@ function evaluateConjunct(clause, context) {
4543
4572
  const bound = Number(ageMatch[2]);
4544
4573
  if (context.ageYears === void 0 || !Number.isFinite(bound)) return "unknown";
4545
4574
  const age = context.ageYears;
4546
- const ok = op === "<=" ? age <= bound : op === ">=" ? age >= bound : op === "<" ? age < bound : op === ">" ? age > bound : op === "=" ? age === bound : void 0;
4547
- if (ok === void 0) return "unknown";
4575
+ let ok;
4576
+ if (op === "<=") ok = age <= bound;
4577
+ else if (op === ">=") ok = age >= bound;
4578
+ else if (op === "<") ok = age < bound;
4579
+ else if (op === ">") ok = age > bound;
4580
+ else {
4581
+ if (op === "=") ok = age === bound;
4582
+ else return "unknown";
4583
+ }
4548
4584
  return ok ? "match" : "no-match";
4549
4585
  }
4550
4586
  return "unknown";
@@ -4963,7 +4999,8 @@ function jaccard(a, b) {
4963
4999
  let inter = 0;
4964
5000
  for (const t of a) if (b.has(t)) inter++;
4965
5001
  const union = a.size + b.size - inter;
4966
- return union === 0 ? 0 : inter / union;
5002
+ if (union === 0) return 0;
5003
+ return inter / union;
4967
5004
  }
4968
5005
  function approximateMatch(graph, query, options = {}) {
4969
5006
  const minScore = options.minScore ?? 0.34;
@@ -4980,7 +5017,7 @@ function approximateMatch(graph, query, options = {}) {
4980
5017
  }
4981
5018
 
4982
5019
  // src/index.ts
4983
- var VERSION = "0.1.5";
5020
+ var VERSION = "0.1.6";
4984
5021
 
4985
5022
  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 };
4986
5023
  //# sourceMappingURL=index.mjs.map