@cosyte/terminology 0.0.6 → 0.0.8
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 +140 -0
- package/README.md +11 -1
- package/dist/index.cjs +77 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -14
- package/dist/index.d.ts +50 -14
- package/dist/index.mjs +77 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -25,6 +25,146 @@ this file is maintained by hand (Changesets handles the version bump and publish
|
|
|
25
25
|
|
|
26
26
|
### Fixed
|
|
27
27
|
|
|
28
|
+
- **A loaded GEM map, complex map and RxNorm drug graph froze their wrapper and handed out plain
|
|
29
|
+
`Map`s inside it, so whoever held one could delete a diagnosis mapping, add a target the steward's
|
|
30
|
+
file never authored, or empty a medication's ingredient edges** (`TERMINOLOGY-INNER-MAPS-UNSEALED`).
|
|
31
|
+
`Object.freeze` cannot reach a `Map` — its entries live in an internal slot rather than in
|
|
32
|
+
properties — so freezing the model left `set` / `delete` / `clear` working on every index it points
|
|
33
|
+
at, and the `readonly` on those fields was a compile-time claim only.
|
|
34
|
+
|
|
35
|
+
Measured on `dd8465b`, before any fix: one `push` into `loadGems(…).entries.get("0010")` made
|
|
36
|
+
`applyGem` answer with a target that is in no GEM file; `entries.delete("0010")` turned the same
|
|
37
|
+
source into a typed `TERM_CROSSWALK_UNMAPPED`; `graph.edges.clear()` left `ingredientsOf` answering
|
|
38
|
+
`targets: []` for every drug while `edgeCount` still reported the loaded figure; and
|
|
39
|
+
`graph.concepts.set(…)` put a concept the caller's release never shipped where navigation would
|
|
40
|
+
return it. The candidate and rule lists were nominally `readonly` in the same way — writing over
|
|
41
|
+
element `0` landed too.
|
|
42
|
+
|
|
43
|
+
A code system's `concepts`, a GEM's and a complex map's `entries`, and a drug graph's `concepts` /
|
|
44
|
+
`edges` / `ndcs` are each now a **read-only view** of a map rather than the map itself — those six
|
|
45
|
+
indexes, not every field this package types `ReadonlyMap` — and every list, entry and warning
|
|
46
|
+
reachable from one of those models is frozen. Reads are exactly a `Map`'s: `get`, `has`, `size`,
|
|
47
|
+
`keys`, `values`, `entries`, `forEach`, iteration, `new Map(view)`.
|
|
48
|
+
|
|
49
|
+
**Two behaviour changes to know before you write against one.** A view is not a `Map` instance, so
|
|
50
|
+
`instanceof Map` is now `false` on `CodeSystem.concepts`, a `GemMap`'s and a `ComplexMap`'s
|
|
51
|
+
`entries`, and a graph's `concepts` / `edges` / `ndcs`; it prints as a plain object, `Object.keys`
|
|
52
|
+
lists its own methods, and `JSON.stringify` renders it as `{"size":2}` where a `Map` rendered `{}`.
|
|
53
|
+
And a model carrying one therefore **cannot be structured-cloned** — `structuredClone` and
|
|
54
|
+
`worker.postMessage` raise a `DataCloneError`, `v8.serialize` a plain `Error`, where before each
|
|
55
|
+
produced a working copy. Clone what the other side needs instead: `new Map(cs.concepts)`. A loaded
|
|
56
|
+
`ConceptMap`, `ValueSet` or unit table carries no view and clones as it always did. That refusal is
|
|
57
|
+
deliberate rather than incidental. Hiding the view's methods would make the clone _succeed_ and
|
|
58
|
+
hand back a model
|
|
59
|
+
whose indexes were empty while `count` / `conceptCount` / `edgeCount` still reported the loaded
|
|
60
|
+
figures — this defect's own shape, arriving silently. It is pinned as a test, not left as prose.
|
|
61
|
+
|
|
62
|
+
Not being a `Map` is also what closes the second route rather than only the first: replacing a
|
|
63
|
+
`Map`'s three mutators leaves `Map.prototype.set.call(theMap, …)` reaching the internal slot
|
|
64
|
+
directly, and a view has no slot to reach. `CodeSystem.concepts` was sealed that weaker way — a
|
|
65
|
+
fabricated concept could be forced into a loaded release and `lookup` would return it — and is a
|
|
66
|
+
view now too.
|
|
67
|
+
|
|
68
|
+
**The bundled UCUM table is deliberately not part of this and is unchanged.** `atomByCode` and
|
|
69
|
+
`prefixByCode` stay real `Map`s whose `set` / `delete` / `clear` are replaced by a refusal, so an
|
|
70
|
+
entry forced through `Map.prototype` still lands there. That residual is bounded and stays
|
|
71
|
+
documented: `parseUcum` resolves an atom by scanning the frozen `atoms` array, never through a
|
|
72
|
+
lookup map, so a forced entry changes no validation, reduction or `ucumEqual` answer — only the
|
|
73
|
+
caller's own `get` is lied to.
|
|
74
|
+
|
|
75
|
+
The guarantee is that the loaded model does not change, not that a write throws: a named mutator
|
|
76
|
+
raises a `TypeError` in either mode, while a frozen list or entry throws in strict mode and is
|
|
77
|
+
silently ignored in sloppy mode, which the CJS build lets a caller observe. Every route is
|
|
78
|
+
therefore asserted by the **reading** it would have changed as well as by the refusal, and a
|
|
79
|
+
reachability sweep walks **all seven** `load*` models for anything still writable, so a field added
|
|
80
|
+
later is covered without anyone maintaining a list of routes. The UCUM table is pinned in that
|
|
81
|
+
sweep as the named exception rather than skipped, so converting it later has to be deliberate. The
|
|
82
|
+
sweep carries its own negative controls: a planted mutable object and a raw `Map` behind a frozen
|
|
83
|
+
wrapper both have to be reported.
|
|
84
|
+
|
|
85
|
+
- **`parseEssence`'s example imported it from `@cosyte/terminology/ucum/essence`, a subpath the
|
|
86
|
+
package's `exports` does not declare.** It is package-internal and not re-exported from the entry
|
|
87
|
+
point; the example says so and imports it the way this package's own code does. Consumers reach the
|
|
88
|
+
table through `loadUcumEssence()`, which is unchanged.
|
|
89
|
+
|
|
90
|
+
- **Three `/* v8 ignore */` hints in `src/ucum/essence.ts` were inert, and are now plain comments.**
|
|
91
|
+
The branches they claimed to exclude were reported uncovered either way — measured with and
|
|
92
|
+
without a `--` reason suffix and under `--coverage.experimentalAstAwareRemapping` — so the hints
|
|
93
|
+
stated a coverage exclusion that no run performed. The package's posture is the one
|
|
94
|
+
`vitest.config.ts` already records for `rxnorm/`: an unreachable `noUncheckedIndexedAccess`
|
|
95
|
+
fallback stays visibly uncovered under a directory floor of 90, rather than being hidden by an
|
|
96
|
+
assertion about reachability that the next reader inherits with no way to check it. No coverage
|
|
97
|
+
figure moved, which is what proves they were inert.
|
|
98
|
+
|
|
99
|
+
- **A GEM source with more than one combination scenario had no deterministic test**, so the
|
|
100
|
+
comparator that orders them was reached only when a `fast-check` draw happened to generate a second
|
|
101
|
+
scenario — `Array.prototype.sort` never calls one for a single-element array. Scenarios are
|
|
102
|
+
alternative valid representations of the same source, so their order is part of the answer. Covered
|
|
103
|
+
now by a fixture whose file order is deliberately scenario 2 first. Found while measuring this
|
|
104
|
+
slice; `vitest run --coverage --exclude 'test/property/**'` reports `crosswalk/` at 100% functions
|
|
105
|
+
with it and 95.45% without.
|
|
106
|
+
|
|
107
|
+
- **The UCUM unit table `loadUcumEssence()` hands out was `readonly` to TypeScript only, so a unit's
|
|
108
|
+
definition could be rewritten in place — and doing so fabricated a unit equivalence that outlived
|
|
109
|
+
putting the table back** (`TERMINOLOGY-MUTABLE-ESSENCE-TABLE`). One table is shared by every caller
|
|
110
|
+
and by the engine's own reducer, `essence.ts` froze nothing, and `reduce` memoizes each atom's
|
|
111
|
+
reduction against the atom object. So corrupting a loaded atom **before that atom's first
|
|
112
|
+
reduction** wrote the memo, and the reading survived restoring the table exactly as shipped.
|
|
113
|
+
|
|
114
|
+
Measured on `bf153cb`, and live on published `0.0.5` and `0.0.6` alike — `0.0.6` is that commit plus
|
|
115
|
+
a version bump. Defining the loaded litre as `1` with no prior
|
|
116
|
+
touch of `L`: `ucumEqual("L", "1")` and `ucumEqual("mg/L", "mg")` both answered `true` where the
|
|
117
|
+
same calls without it answered `false`, and `mmol/L` fell from `6.0221407599999985e+23 × m-3` to a
|
|
118
|
+
dimensionless `6.02214076e+20` — **a concentration comparing equal to a mass**. Two further routes
|
|
119
|
+
to the same readings on that build: writing the nested definition object in place
|
|
120
|
+
(`atom.value.unit = "1"`), and replacing an element of the `atoms` array, which is the one
|
|
121
|
+
`parseUcum` scans, so it hands the parser a forged atom without touching the memo at all.
|
|
122
|
+
|
|
123
|
+
This is the half that keying the memo on the atom **object** did not reach. That closed the route
|
|
124
|
+
where the atom was one you had _built_; this is the route where the atom is one you were _handed_,
|
|
125
|
+
and the consequence class is identical. Neither substitutes for the other.
|
|
126
|
+
|
|
127
|
+
The table is frozen when it is parsed, deeply: every atom, every atom's definition object, every
|
|
128
|
+
prefix, and both arrays. Shallow would have been worth nothing — freezing the atom alone leaves
|
|
129
|
+
`atom.value.unit`, which reproduces all three readings. `atomByCode` and `prefixByCode` refuse
|
|
130
|
+
`set`, `delete` and `clear`, which `Object.freeze` cannot do for a `Map`. `parseEssence` freezes
|
|
131
|
+
what it returns on the same rule.
|
|
132
|
+
|
|
133
|
+
**The guarantee is that the table does not change, not that a write throws.** `Object.freeze`
|
|
134
|
+
refuses a write by throwing in strict mode and ignoring it in sloppy mode, and this package ships a
|
|
135
|
+
CJS build a sloppy-mode caller can require; the table is the one that was parsed either way. The
|
|
136
|
+
tests assert the readings, and the `TypeError` only as how a strict-mode caller observes it.
|
|
137
|
+
|
|
138
|
+
**Two things this does not claim, both measured rather than reasoned.** `Map.prototype.set.call()`
|
|
139
|
+
can still force an entry into a lookup map — no `Map` can be made immutable in place — and what
|
|
140
|
+
that reaches is bounded and pinned in both directions: `parseUcum` resolves an atom by scanning
|
|
141
|
+
`atoms`, never through `atomByCode`, so a forced entry changes **no** reduction and no `ucumEqual`
|
|
142
|
+
answer, only what the caller's own `get` returns. And the values `parseUcum` and `reduce` hand back
|
|
143
|
+
are deliberately still per-call and mutable.
|
|
144
|
+
|
|
145
|
+
**No reduction changes.** 3,080 expressions built from the table — every atom bare, squared,
|
|
146
|
+
inverted and annotated; every prefix over rotating metric atoms; rotating ratios, products and
|
|
147
|
+
grouped powers; and the shapes the README and the tests name — plus 312 `ucumEqual` pairs, reduce
|
|
148
|
+
byte-identically on `bf153cb` and on this tree (3,393 output lines, one sha256).
|
|
149
|
+
|
|
150
|
+
One knock-on, stated because getting it wrong is easy: freezing the table removes **a** route to
|
|
151
|
+
`reduce`'s cyclic-definition guard, not the last one. Two routes that look like they should reach
|
|
152
|
+
it do not, and both are now pinned as tests — naming a table atom in a definition (it is resolved
|
|
153
|
+
against the loaded table, so your atom can _name_ one but never _be_ one; a self-referential `Pa`
|
|
154
|
+
taken off a table copy resolves to the shipped pascal and answers `1000 × g.m-1.s-2`), and
|
|
155
|
+
corrupting a loaded atom, which is what is now refused. The route that does reach it needs no table
|
|
156
|
+
at all: `UcumAtom.value` is `readonly` to TypeScript, which an **accessor** satisfies, and the
|
|
157
|
+
definition is read after the atom is marked in progress, so a getter can re-enter `reduce` for the
|
|
158
|
+
same atom. That is a supported public call, and the guard is pinned by it under a 100,000-byte atom
|
|
159
|
+
code — so all three of `reduce`'s messages are again asserted whole, against both `message` and
|
|
160
|
+
`err.stack`, with the caller's own code as the unbounded value in each.
|
|
161
|
+
|
|
162
|
+
Also here: the `/* v8 ignore next */` on `reduce`'s `atom.base` line is removed. It justified
|
|
163
|
+
itself with a property of the bundled table — every base atom there carries a `dim` — for a line
|
|
164
|
+
reachable through the exported surface with a caller-built `{ base: true }` atom that carries none.
|
|
165
|
+
That arm is answered on the atom's own code, is now covered by a test rather than excluded, and
|
|
166
|
+
changes nothing for the table's own base units.
|
|
167
|
+
|
|
28
168
|
- **A `UnitNode` you assembled yourself could change how `reduce` and `ucumEqual` answer for a unit
|
|
29
169
|
parsed out of the bundled UCUM table, for the rest of the process**
|
|
30
170
|
(`TERMINOLOGY-ATOMMEMO-POISONING`). `reduce` is exported and takes a caller-built node, and
|
package/README.md
CHANGED
|
@@ -208,7 +208,17 @@ _names_, listed under [What is bundled](#what-is-bundled).
|
|
|
208
208
|
NDC you asked about, so log the `code` and the locus rather than the whole result — a code _in
|
|
209
209
|
patient context_ can be PHI.
|
|
210
210
|
- **Zero runtime dependencies. Dual ESM + CJS.** Node stdlib only; built with `tsup`, validated with
|
|
211
|
-
`attw`.
|
|
211
|
+
`attw`. A release you load is frozen, its indexes included: a code system's concepts, a GEM's or a
|
|
212
|
+
complex map's entries and a drug graph's concepts, edges and NDCs are each a read-only **view** of a
|
|
213
|
+
map rather than the map itself, so nothing holding one can add, delete or clear an entry, whichever
|
|
214
|
+
way it tries. A view is not a `Map` instance, so a model carrying one cannot be `structuredClone`d
|
|
215
|
+
or posted to a worker — copy out what you need (`new Map(cs.concepts)`). The bundled unit table is frozen deeply, atom by atom and definition by
|
|
216
|
+
definition, because it is shared with the engine's own reducer; its two lookup maps refuse `set`,
|
|
217
|
+
`delete` and `clear` by name, and an entry forced past that through `Map.prototype` changes nothing
|
|
218
|
+
the engine reads, because `parseUcum` resolves an atom by scanning the frozen atom array rather than
|
|
219
|
+
through a lookup map. The AST and
|
|
220
|
+
the reduction that `parseUcum` and `reduce` hand back are per-call values you own, and mutating one
|
|
221
|
+
changes nothing for anyone else.
|
|
212
222
|
|
|
213
223
|
## What is bundled
|
|
214
224
|
|
package/dist/index.cjs
CHANGED
|
@@ -567,6 +567,31 @@ function translate(sourceCoding, map) {
|
|
|
567
567
|
);
|
|
568
568
|
}
|
|
569
569
|
|
|
570
|
+
// src/common/frozen-map.ts
|
|
571
|
+
function frozenMap(source, what) {
|
|
572
|
+
const refuse = () => {
|
|
573
|
+
throw new TypeError(`Cannot mutate ${what} (it is immutable)`);
|
|
574
|
+
};
|
|
575
|
+
const view = {
|
|
576
|
+
get size() {
|
|
577
|
+
return source.size;
|
|
578
|
+
},
|
|
579
|
+
get: (key) => source.get(key),
|
|
580
|
+
has: (key) => source.has(key),
|
|
581
|
+
keys: () => source.keys(),
|
|
582
|
+
values: () => source.values(),
|
|
583
|
+
entries: () => source.entries(),
|
|
584
|
+
forEach: (callback, thisArg) => {
|
|
585
|
+
for (const [key, value] of source) callback.call(thisArg, value, key, view);
|
|
586
|
+
},
|
|
587
|
+
[Symbol.iterator]: () => source[Symbol.iterator](),
|
|
588
|
+
set: refuse,
|
|
589
|
+
delete: refuse,
|
|
590
|
+
clear: refuse
|
|
591
|
+
};
|
|
592
|
+
return Object.freeze(view);
|
|
593
|
+
}
|
|
594
|
+
|
|
570
595
|
// src/codesystem/status.ts
|
|
571
596
|
var defaultStatusMapper = (raw) => {
|
|
572
597
|
switch (raw.trim().toUpperCase()) {
|
|
@@ -973,15 +998,6 @@ function parseRrf(source) {
|
|
|
973
998
|
}
|
|
974
999
|
|
|
975
1000
|
// src/codesystem/load.ts
|
|
976
|
-
function sealConceptMap(map) {
|
|
977
|
-
const guard = () => {
|
|
978
|
-
throw new TypeError("Cannot mutate a loaded CodeSystem's concepts map (it is immutable)");
|
|
979
|
-
};
|
|
980
|
-
for (const method of ["set", "delete", "clear"]) {
|
|
981
|
-
Object.defineProperty(map, method, { value: guard, writable: false, configurable: false });
|
|
982
|
-
}
|
|
983
|
-
return map;
|
|
984
|
-
}
|
|
985
1001
|
function loadCodeSystem(source) {
|
|
986
1002
|
let concepts;
|
|
987
1003
|
let warnings;
|
|
@@ -1019,7 +1035,7 @@ function loadCodeSystem(source) {
|
|
|
1019
1035
|
}
|
|
1020
1036
|
}
|
|
1021
1037
|
out.count = concepts.size;
|
|
1022
|
-
out.concepts =
|
|
1038
|
+
out.concepts = frozenMap(concepts, "a loaded CodeSystem's concepts map");
|
|
1023
1039
|
out.warnings = Object.freeze(warnings);
|
|
1024
1040
|
return Object.freeze(out);
|
|
1025
1041
|
}
|
|
@@ -3733,7 +3749,29 @@ function parseEssence(xml) {
|
|
|
3733
3749
|
atoms.sort((a, b) => b.code.length - a.code.length);
|
|
3734
3750
|
const atomByCode = new Map(atoms.map((a) => [a.code, a]));
|
|
3735
3751
|
const prefixByCode = new Map(prefixes.map((p) => [p.code, p]));
|
|
3736
|
-
return { prefixes, atoms, atomByCode, prefixByCode };
|
|
3752
|
+
return freezeEssence({ prefixes, atoms, atomByCode, prefixByCode });
|
|
3753
|
+
}
|
|
3754
|
+
function refuseTableWrite() {
|
|
3755
|
+
throw new TypeError("the UCUM unit table is immutable");
|
|
3756
|
+
}
|
|
3757
|
+
function sealLookup(map) {
|
|
3758
|
+
return Object.defineProperties(map, {
|
|
3759
|
+
set: { value: refuseTableWrite },
|
|
3760
|
+
delete: { value: refuseTableWrite },
|
|
3761
|
+
clear: { value: refuseTableWrite }
|
|
3762
|
+
});
|
|
3763
|
+
}
|
|
3764
|
+
function freezeEssence(essence) {
|
|
3765
|
+
for (const atom of essence.atoms) {
|
|
3766
|
+
if (atom.value) Object.freeze(atom.value);
|
|
3767
|
+
Object.freeze(atom);
|
|
3768
|
+
}
|
|
3769
|
+
for (const prefix of essence.prefixes) Object.freeze(prefix);
|
|
3770
|
+
Object.freeze(essence.atoms);
|
|
3771
|
+
Object.freeze(essence.prefixes);
|
|
3772
|
+
sealLookup(essence.atomByCode);
|
|
3773
|
+
sealLookup(essence.prefixByCode);
|
|
3774
|
+
return Object.freeze(essence);
|
|
3737
3775
|
}
|
|
3738
3776
|
var cached;
|
|
3739
3777
|
function loadUcumEssence() {
|
|
@@ -4047,21 +4085,25 @@ function loadGems(source) {
|
|
|
4047
4085
|
if (trimmed === "") continue;
|
|
4048
4086
|
const fields = trimmed.split(/\s+/);
|
|
4049
4087
|
if (fields.length !== 3) {
|
|
4050
|
-
warnings.push(
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4088
|
+
warnings.push(
|
|
4089
|
+
Object.freeze({
|
|
4090
|
+
code: "TERM_GEM_MALFORMED_ROW",
|
|
4091
|
+
line: i + 1,
|
|
4092
|
+
detail: `expected 3 whitespace-delimited fields, found ${String(fields.length)}`
|
|
4093
|
+
})
|
|
4094
|
+
);
|
|
4055
4095
|
continue;
|
|
4056
4096
|
}
|
|
4057
4097
|
const [src, tgt, flagStr] = fields;
|
|
4058
4098
|
const flags = decodeFlags(flagStr);
|
|
4059
4099
|
if (flags === void 0) {
|
|
4060
|
-
warnings.push(
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4100
|
+
warnings.push(
|
|
4101
|
+
Object.freeze({
|
|
4102
|
+
code: "TERM_GEM_MALFORMED_ROW",
|
|
4103
|
+
line: i + 1,
|
|
4104
|
+
detail: "flag field is not a 5-digit code"
|
|
4105
|
+
})
|
|
4106
|
+
);
|
|
4065
4107
|
continue;
|
|
4066
4108
|
}
|
|
4067
4109
|
const entry = { source: src, flags };
|
|
@@ -4072,11 +4114,12 @@ function loadGems(source) {
|
|
|
4072
4114
|
else entries.set(src, [frozen]);
|
|
4073
4115
|
count++;
|
|
4074
4116
|
}
|
|
4117
|
+
for (const list of entries.values()) Object.freeze(list);
|
|
4075
4118
|
return Object.freeze({
|
|
4076
4119
|
direction: source.direction,
|
|
4077
4120
|
...source.version !== void 0 ? { version: source.version } : {},
|
|
4078
4121
|
count,
|
|
4079
|
-
entries,
|
|
4122
|
+
entries: frozenMap(entries, "a loaded GEM map's entries"),
|
|
4080
4123
|
warnings: Object.freeze(warnings)
|
|
4081
4124
|
});
|
|
4082
4125
|
}
|
|
@@ -4224,11 +4267,13 @@ function parseRf2(content) {
|
|
|
4224
4267
|
const groupN = Number(at(f, iGroup));
|
|
4225
4268
|
const priorityN = Number(at(f, iPriority));
|
|
4226
4269
|
if (source === "" || !Number.isFinite(groupN) || !Number.isFinite(priorityN)) {
|
|
4227
|
-
warnings.push(
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4270
|
+
warnings.push(
|
|
4271
|
+
Object.freeze({
|
|
4272
|
+
code: "TERM_COMPLEX_MAP_MALFORMED_ROW",
|
|
4273
|
+
line: i + 1,
|
|
4274
|
+
detail: "row is missing referencedComponentId / mapGroup / mapPriority"
|
|
4275
|
+
})
|
|
4276
|
+
);
|
|
4232
4277
|
continue;
|
|
4233
4278
|
}
|
|
4234
4279
|
rows.push({
|
|
@@ -4271,7 +4316,7 @@ function loadComplexMap(input) {
|
|
|
4271
4316
|
}
|
|
4272
4317
|
const out = {
|
|
4273
4318
|
count,
|
|
4274
|
-
entries,
|
|
4319
|
+
entries: frozenMap(entries, "a loaded complex map's entries"),
|
|
4275
4320
|
warnings
|
|
4276
4321
|
};
|
|
4277
4322
|
if (input.version !== void 0) out.version = input.version;
|
|
@@ -4565,9 +4610,9 @@ function loadRxNormGraph(source) {
|
|
|
4565
4610
|
const ndcs = source.sat !== void 0 ? parseNdcs(source.sat, warnings) : /* @__PURE__ */ new Map();
|
|
4566
4611
|
for (const list of edges.values()) Object.freeze(list);
|
|
4567
4612
|
const out = {
|
|
4568
|
-
concepts,
|
|
4569
|
-
edges,
|
|
4570
|
-
ndcs,
|
|
4613
|
+
concepts: frozenMap(concepts, "a loaded RxNorm graph's concepts"),
|
|
4614
|
+
edges: frozenMap(edges, "a loaded RxNorm graph's edges"),
|
|
4615
|
+
ndcs: frozenMap(ndcs, "a loaded RxNorm graph's NDC index"),
|
|
4571
4616
|
conceptCount: concepts.size,
|
|
4572
4617
|
edgeCount,
|
|
4573
4618
|
warnings: Object.freeze(warnings)
|
|
@@ -4731,7 +4776,7 @@ function approximateMatch(graph, query, options = {}) {
|
|
|
4731
4776
|
}
|
|
4732
4777
|
|
|
4733
4778
|
// src/index.ts
|
|
4734
|
-
var VERSION = "0.0.
|
|
4779
|
+
var VERSION = "0.0.8";
|
|
4735
4780
|
|
|
4736
4781
|
exports.COMPLEX_MAP_SOURCE_SYSTEM = COMPLEX_MAP_SOURCE_SYSTEM;
|
|
4737
4782
|
exports.DIAGNOSTIC_CODES = DIAGNOSTIC_CODES;
|