@cosyte/terminology 0.0.1 → 0.0.2
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 +198 -7
- package/README.md +84 -24
- package/dist/index.cjs +66 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +329 -184
- package/dist/index.d.ts +329 -184
- package/dist/index.mjs +66 -27
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -6
- package/vendor/ucum/NOTICE.md +48 -0
package/dist/index.mjs
CHANGED
|
@@ -73,13 +73,13 @@ var DIAGNOSTIC_CODES = {
|
|
|
73
73
|
* code system was not supplied, an unresolvable referenced value set, or a `filter` operator the
|
|
74
74
|
* engine does not implement. Surfaced as a typed outcome that marks the expansion **incomplete**;
|
|
75
75
|
* the engine **never** fabricates a member and **never** returns a silently-empty "no members"
|
|
76
|
-
* answer for a part it could not compute (
|
|
76
|
+
* answer for a part it could not compute (a false "not a member" is a clinical error).
|
|
77
77
|
*/
|
|
78
78
|
TERM_VALUESET_CANNOT_EXPAND: "TERM_VALUESET_CANNOT_EXPAND",
|
|
79
79
|
/**
|
|
80
80
|
* A **pre-computed** `ValueSet.expansion` is **incomplete** — its `total` exceeds the number of
|
|
81
|
-
* `contains` entries, or it is flagged too-costly (the VSAC `$expand` >1200-code truncation
|
|
82
|
-
*
|
|
81
|
+
* `contains` entries, or it is flagged too-costly (the VSAC `$expand` >1200-code truncation
|
|
82
|
+
* hazard). The engine **never treats a truncated expansion as complete membership**: a code
|
|
83
83
|
* absent from a truncated expansion is `undetermined`, never a confident "not a member".
|
|
84
84
|
*/
|
|
85
85
|
TERM_VALUESET_EXPANSION_TRUNCATED: "TERM_VALUESET_EXPANSION_TRUNCATED",
|
|
@@ -87,7 +87,7 @@ var DIAGNOSTIC_CODES = {
|
|
|
87
87
|
* A UCUM unit expression is **not valid** — it does not parse against the UCUM grammar, or it
|
|
88
88
|
* names an atom absent from the vendored UCUM table. A **first-class typed outcome, never an
|
|
89
89
|
* error and never a guess**: {@link validateUcum} returns `{ valid: false, reason }`, never a
|
|
90
|
-
* coerced or "nearest" unit (the never-fabricate invariant, applied to units
|
|
90
|
+
* coerced or "nearest" unit (the never-fabricate invariant, applied to units). The
|
|
91
91
|
* `reason` is value-free (a grammar fault or an unknown-atom shape), never PHI.
|
|
92
92
|
*/
|
|
93
93
|
TERM_UCUM_INVALID: "TERM_UCUM_INVALID",
|
|
@@ -95,7 +95,7 @@ var DIAGNOSTIC_CODES = {
|
|
|
95
95
|
* A crosswalk source code has an **authored No-Map** — the steward declares it has no valid target
|
|
96
96
|
* in the other classification (a GEM `NoDx`/`NoPCS` sentinel, or a SNOMED complex-map `447638001`
|
|
97
97
|
* "cannot be classified" category). A **first-class typed outcome, never an error and never a
|
|
98
|
-
* guess** (the never-fabricate invariant, applied to crosswalks
|
|
98
|
+
* guess** (the never-fabricate invariant, applied to crosswalks). Distinct from
|
|
99
99
|
* {@link TERM_CROSSWALK_UNMAPPED}: the source *is* in the map, and the map says "no target".
|
|
100
100
|
*/
|
|
101
101
|
TERM_CROSSWALK_NO_MAP: "TERM_CROSSWALK_NO_MAP",
|
|
@@ -109,7 +109,7 @@ var DIAGNOSTIC_CODES = {
|
|
|
109
109
|
/**
|
|
110
110
|
* A SNOMED→ICD-10-CM complex-map group's decision needs **patient context** (an `IFA` age band or
|
|
111
111
|
* gender) the caller did not supply. The engine surfaces the candidate rules + advice and **refuses
|
|
112
|
-
* to pick a branch** it lacks the data for
|
|
112
|
+
* to pick a branch** it lacks the data for — never a silently-chosen target.
|
|
113
113
|
*/
|
|
114
114
|
TERM_CROSSWALK_CONTEXT_REQUIRED: "TERM_CROSSWALK_CONTEXT_REQUIRED",
|
|
115
115
|
/**
|
|
@@ -131,17 +131,27 @@ var DIAGNOSTIC_CODES = {
|
|
|
131
131
|
* **silently** — they are expected, not faults.
|
|
132
132
|
*/
|
|
133
133
|
TERM_RXNORM_MALFORMED_ROW: "TERM_RXNORM_MALFORMED_ROW",
|
|
134
|
+
/**
|
|
135
|
+
* An `RXCUI` appeared in the release's `SAB=RXNORM` atoms but **none of them could establish its
|
|
136
|
+
* term type**, so it is **not** in the graph. Either every atom carried a synonym-class `TTY`
|
|
137
|
+
* (`PSN`/`SY`/`TMSY`, which Appendix 5 defines as a "synonym of another TTY" and which therefore
|
|
138
|
+
* types a *name* rather than a concept) or every atom carried a `TTY` this engine does not model.
|
|
139
|
+
* The concept is skipped rather than typed from a synonym atom: an absent concept is already a
|
|
140
|
+
* first-class typed answer (`TERM_RXNORM_UNKNOWN_RXCUI`), while a concept whose term type reads
|
|
141
|
+
* `TMSY` is a fabricated claim about what a drug *is* that a caller cannot detect.
|
|
142
|
+
*/
|
|
143
|
+
TERM_RXNORM_UNTYPED_CONCEPT: "TERM_RXNORM_UNTYPED_CONCEPT",
|
|
134
144
|
/**
|
|
135
145
|
* A RxNorm graph navigation was asked about an `RXCUI` **absent from the loaded release** — a
|
|
136
146
|
* first-class typed outcome, never a fabricated concept and never an empty "success" (the
|
|
137
|
-
* never-fabricate invariant, applied to the drug graph
|
|
147
|
+
* never-fabricate invariant, applied to the drug graph). Distinct from a *present*
|
|
138
148
|
* concept that simply has no edge of the requested relationship (that is a found result with empty
|
|
139
149
|
* targets).
|
|
140
150
|
*/
|
|
141
151
|
TERM_RXNORM_UNKNOWN_RXCUI: "TERM_RXNORM_UNKNOWN_RXCUI",
|
|
142
152
|
/**
|
|
143
153
|
* An NDC could **not be resolved** to any `RXCUI` in the loaded RxNorm release — a typed, surfaced
|
|
144
|
-
* absence, never a guessed `RXCUI`. NDC↔RXCUI is many:1 and temporal
|
|
154
|
+
* absence, never a guessed `RXCUI`. NDC↔RXCUI is many:1 and temporal, so a resolution
|
|
145
155
|
* is always release-scoped; an NDC not present in the loaded release is this diagnostic.
|
|
146
156
|
*/
|
|
147
157
|
TERM_RXNORM_NDC_UNMAPPED: "TERM_RXNORM_NDC_UNMAPPED"
|
|
@@ -176,7 +186,7 @@ var FATAL_CODES = {
|
|
|
176
186
|
/**
|
|
177
187
|
* An **inversion** of a directional crosswalk was requested — a forbidden operation. The GEMs and
|
|
178
188
|
* the SNOMED→ICD-10-CM map are authoritative in **one direction only**; a forward map is not the
|
|
179
|
-
* inverse of the backward map
|
|
189
|
+
* inverse of the backward map. Thrown by {@link ../crosswalk/gems.invertGem} so the
|
|
180
190
|
* never-invert refusal is a first-class, discoverable contract rather than a silent absence.
|
|
181
191
|
*/
|
|
182
192
|
TERM_MAP_NOT_INVERTIBLE: "TERM_MAP_NOT_INVERTIBLE"
|
|
@@ -4358,12 +4368,17 @@ var TERM_TYPES = Object.freeze({
|
|
|
4358
4368
|
BN: "Brand Name",
|
|
4359
4369
|
SCDC: "Semantic Clinical Drug Component",
|
|
4360
4370
|
SBDC: "Semantic Branded Drug Component",
|
|
4371
|
+
SCDF: "Semantic Clinical Drug Form",
|
|
4372
|
+
SBDF: "Semantic Branded Drug Form",
|
|
4373
|
+
SCDFP: "Semantic Clinical Drug Form Precise",
|
|
4374
|
+
SBDFP: "Semantic Branded Drug Form Precise",
|
|
4361
4375
|
SCD: "Semantic Clinical Drug",
|
|
4362
4376
|
SBD: "Semantic Branded Drug",
|
|
4363
4377
|
SCDG: "Semantic Clinical Drug Group",
|
|
4364
4378
|
SBDG: "Semantic Branded Drug Group",
|
|
4379
|
+
SCDGP: "Semantic Clinical Drug Form Group Precise",
|
|
4365
4380
|
GPCK: "Generic Pack",
|
|
4366
|
-
BPCK: "
|
|
4381
|
+
BPCK: "Brand Name Pack",
|
|
4367
4382
|
DF: "Dose Form",
|
|
4368
4383
|
DFG: "Dose Form Group",
|
|
4369
4384
|
PSN: "Prescribable Name",
|
|
@@ -4372,9 +4387,13 @@ var TERM_TYPES = Object.freeze({
|
|
|
4372
4387
|
ET: "Entry Term"
|
|
4373
4388
|
});
|
|
4374
4389
|
var KNOWN = new Set(Object.keys(TERM_TYPES));
|
|
4390
|
+
var SYNONYM = /* @__PURE__ */ new Set(["PSN", "SY", "TMSY"]);
|
|
4375
4391
|
function asTermType(tty) {
|
|
4376
4392
|
return KNOWN.has(tty) ? tty : void 0;
|
|
4377
4393
|
}
|
|
4394
|
+
function isSynonymTermType(tty) {
|
|
4395
|
+
return SYNONYM.has(tty);
|
|
4396
|
+
}
|
|
4378
4397
|
|
|
4379
4398
|
// src/rxnorm/load.ts
|
|
4380
4399
|
var SAB_RXNORM = "RXNORM";
|
|
@@ -4388,13 +4407,13 @@ function cell2(cells, index) {
|
|
|
4388
4407
|
}
|
|
4389
4408
|
function parseConso(content, warnings) {
|
|
4390
4409
|
const concepts = /* @__PURE__ */ new Map();
|
|
4391
|
-
const
|
|
4392
|
-
|
|
4393
|
-
|
|
4410
|
+
const conso = [];
|
|
4411
|
+
const untyped = /* @__PURE__ */ new Map();
|
|
4412
|
+
for (const [i, raw] of content.split(/\r?\n/).entries()) {
|
|
4394
4413
|
if (raw === "") continue;
|
|
4395
4414
|
const cells = parseRrfLine(raw);
|
|
4396
4415
|
if (cells.length <= CONSO.STR) {
|
|
4397
|
-
|
|
4416
|
+
conso.push(
|
|
4398
4417
|
Object.freeze({
|
|
4399
4418
|
code: "TERM_RXNORM_MALFORMED_ROW",
|
|
4400
4419
|
file: "RXNCONSO",
|
|
@@ -4405,11 +4424,9 @@ function parseConso(content, warnings) {
|
|
|
4405
4424
|
continue;
|
|
4406
4425
|
}
|
|
4407
4426
|
if (cell2(cells, CONSO.SAB) !== SAB_RXNORM) continue;
|
|
4408
|
-
const tty = asTermType(cell2(cells, CONSO.TTY) ?? "");
|
|
4409
|
-
if (tty === void 0) continue;
|
|
4410
4427
|
const rxcui = cell2(cells, CONSO.RXCUI);
|
|
4411
4428
|
if (rxcui === void 0 || rxcui === "") {
|
|
4412
|
-
|
|
4429
|
+
conso.push(
|
|
4413
4430
|
Object.freeze({
|
|
4414
4431
|
code: "TERM_RXNORM_MALFORMED_ROW",
|
|
4415
4432
|
file: "RXNCONSO",
|
|
@@ -4419,7 +4436,13 @@ function parseConso(content, warnings) {
|
|
|
4419
4436
|
);
|
|
4420
4437
|
continue;
|
|
4421
4438
|
}
|
|
4439
|
+
const tty = asTermType(cell2(cells, CONSO.TTY) ?? "");
|
|
4440
|
+
if (tty === void 0 || isSynonymTermType(tty)) {
|
|
4441
|
+
if (!concepts.has(rxcui) && !untyped.has(rxcui)) untyped.set(rxcui, i + 1);
|
|
4442
|
+
continue;
|
|
4443
|
+
}
|
|
4422
4444
|
if (concepts.has(rxcui)) continue;
|
|
4445
|
+
untyped.delete(rxcui);
|
|
4423
4446
|
const name = cell2(cells, CONSO.STR) ?? "";
|
|
4424
4447
|
const suppress = cell2(cells, CONSO.SUPPRESS) ?? "N";
|
|
4425
4448
|
concepts.set(
|
|
@@ -4427,14 +4450,24 @@ function parseConso(content, warnings) {
|
|
|
4427
4450
|
Object.freeze({ rxcui, tty, name, suppressed: suppress !== "" && suppress !== "N" })
|
|
4428
4451
|
);
|
|
4429
4452
|
}
|
|
4453
|
+
for (const line of untyped.values()) {
|
|
4454
|
+
conso.push(
|
|
4455
|
+
Object.freeze({
|
|
4456
|
+
code: "TERM_RXNORM_UNTYPED_CONCEPT",
|
|
4457
|
+
file: "RXNCONSO",
|
|
4458
|
+
line,
|
|
4459
|
+
detail: "no RXNORM atom established this RXCUI's term type (every atom was synonym-class or an unmodelled TTY); skipped rather than typed from a synonym"
|
|
4460
|
+
})
|
|
4461
|
+
);
|
|
4462
|
+
}
|
|
4463
|
+
conso.sort((a, b) => a.line - b.line);
|
|
4464
|
+
for (const w of conso) warnings.push(w);
|
|
4430
4465
|
return concepts;
|
|
4431
4466
|
}
|
|
4432
4467
|
function parseRel(content, warnings) {
|
|
4433
4468
|
const edges = /* @__PURE__ */ new Map();
|
|
4434
4469
|
let edgeCount = 0;
|
|
4435
|
-
const
|
|
4436
|
-
for (let i = 0; i < lines.length; i++) {
|
|
4437
|
-
const raw = lines[i] ?? "";
|
|
4470
|
+
for (const [i, raw] of content.split(/\r?\n/).entries()) {
|
|
4438
4471
|
if (raw === "") continue;
|
|
4439
4472
|
const cells = parseRrfLine(raw);
|
|
4440
4473
|
if (cells.length <= REL.RELA) {
|
|
@@ -4463,9 +4496,7 @@ function parseRel(content, warnings) {
|
|
|
4463
4496
|
}
|
|
4464
4497
|
function parseNdcs(content, warnings) {
|
|
4465
4498
|
const ndcs = /* @__PURE__ */ new Map();
|
|
4466
|
-
const
|
|
4467
|
-
for (let i = 0; i < lines.length; i++) {
|
|
4468
|
-
const raw = lines[i] ?? "";
|
|
4499
|
+
for (const [i, raw] of content.split(/\r?\n/).entries()) {
|
|
4469
4500
|
if (raw === "") continue;
|
|
4470
4501
|
const cells = parseRrfLine(raw);
|
|
4471
4502
|
if (cells.length <= SAT.ATV) {
|
|
@@ -4517,9 +4548,17 @@ function loadRxNormGraph(source) {
|
|
|
4517
4548
|
|
|
4518
4549
|
// src/rxnorm/rela.ts
|
|
4519
4550
|
var RELA = {
|
|
4520
|
-
/**
|
|
4551
|
+
/**
|
|
4552
|
+
* `has_ingredient`: subject *has ingredient* object. Authored from the clinical side
|
|
4553
|
+
* (`SCDC`/`SCDF`/`SCDG`) to an `IN` and from the branded side (`SBD`/`SBDC`/`SBDF`/`SBDG`) to a
|
|
4554
|
+
* `BN`; RxNorm authors no `SCD ⟶ IN` row.
|
|
4555
|
+
*/
|
|
4521
4556
|
HAS_INGREDIENT: "has_ingredient",
|
|
4522
|
-
/**
|
|
4557
|
+
/**
|
|
4558
|
+
* `ingredient_of`: subject *is an ingredient of* object. The authored inverse, so an `IN` reaches
|
|
4559
|
+
* clinical components and dose forms and a `BN` reaches branded concepts (the `SBD` included).
|
|
4560
|
+
* Neither reaches an `SCD`.
|
|
4561
|
+
*/
|
|
4523
4562
|
INGREDIENT_OF: "ingredient_of",
|
|
4524
4563
|
/** `has_precise_ingredient` — subject *has precise ingredient* object (a `PIN`). */
|
|
4525
4564
|
HAS_PRECISE_INGREDIENT: "has_precise_ingredient",
|
|
@@ -4662,8 +4701,8 @@ function approximateMatch(graph, query, options = {}) {
|
|
|
4662
4701
|
}
|
|
4663
4702
|
|
|
4664
4703
|
// src/index.ts
|
|
4665
|
-
var VERSION = "0.0.
|
|
4704
|
+
var VERSION = "0.0.2";
|
|
4666
4705
|
|
|
4667
|
-
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, 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 };
|
|
4706
|
+
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 };
|
|
4668
4707
|
//# sourceMappingURL=index.mjs.map
|
|
4669
4708
|
//# sourceMappingURL=index.mjs.map
|