@cosyte/terminology 0.0.1 → 0.0.4

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 CHANGED
@@ -9,8 +9,284 @@ this file is maintained by hand (Changesets handles the version bump and publish
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
- The first pre-alpha release (`0.0.1`) will ship the initial public API surface. The package begins
13
- its public history at `0.0.x`, per the cosyte version ladder (`0.0.x` until first alpha).
12
+ ### Fixed
13
+
14
+ - **`LICENSE` was unqualified MIT over a tree that also distributes third-party material**
15
+ (`TERMINOLOGY-LICENSE-THIRD-PARTY`, residuals 1 and 2 of `TERMINOLOGY-RESIDUALS`). The README had
16
+ already grown a "What is bundled" carve-out; the licence file had not, so the two consumer-facing
17
+ licence statements disagreed. The MIT text is unchanged and still covers this package's own code.
18
+ A `THIRD-PARTY MATERIALS` section is appended, stating for each item what is distributed and under
19
+ whose copyright: the **UCUM unit table** (`ucum-essence.xml` v2.2, revision-date 2024-06-17,
20
+ copyright 1999-2024 Regenstrief Institute, Inc., verbatim under the UCUM Copyright Notice and
21
+ License, embedded byte-for-byte in the build) with its grant recorded as **revocable**, its
22
+ no-different-standard and no-derivative-works restrictions, its warranty disclaimer and a pointer
23
+ to `vendor/ucum/NOTICE.md`, which ships in the tarball and remains the notice of record; the
24
+ **UCUM functional-test suite** (EPL v1.0, repository-only, not distributed on npm);
25
+ **`SYSTEM_IDENTITIES`**; the **SNOMED CT identifiers the crosswalk resolver names**; and
26
+ **RxNorm's relationship and term-type names**. The notice itself is deliberately untouched: it was
27
+ verified against `ucum.org/license` on 2026-07-30 and already carries all four things the licence
28
+ requires of a distributor.
29
+ The last of those closes the second residual. "The engine ships no RxNorm **content**" was
30
+ unscoped in the way the UCUM claim once was, because `RELA` / `RELA_INVERSE` / `TERM_TYPES` do
31
+ ship RxNorm's relationship and term-type names; every site now reads "no RxNorm **release**", the
32
+ same narrowing already applied to the code-system claim, and the names that do ship are named.
33
+ `Ships no SNOMED content` on the complex-map loader is narrowed to "no SNOMED CT release or
34
+ refset" for the same reason. **No engine behaviour, API surface or bundled content changes.**
35
+ Both surfaces that carry the claim move together, as they must: `README.md` and the package JSDoc
36
+ compiled into `dist/index.d.ts` / `dist/index.d.cts`, plus four `docs-content/` pages, two of
37
+ which carried a grammatically **closed** bundled-list that the new `LICENSE` would otherwise have
38
+ contradicted. The enumerated items are derived from the build, not from prose, and every list
39
+ reads "includes the following" rather than closing the set. Beyond the named constants,
40
+ individual SNOMED CT, ICD-10-CM and **RxNorm** identifiers ship inside the API documentation
41
+ examples, with their real names and term types, and those examples reach a consumer in three
42
+ places rather than one: `README.md`, the compiled declaration files, and the build's sourcemaps,
43
+ whose `sourcesContent` carries every source comment verbatim. No absolute negative is written
44
+ over those files: a draft said "no RxNorm release, drug concept or NDC is distributed", which the
45
+ build falsifies (`RXCUI` `316151` ships as "lisinopril 10 MG"), so the claim is scoped to the
46
+ release rather than reworded around it.
47
+ Gated by `test/license-third-party.test.ts` (bytes read with `node:fs`, never a `grep` pipeline):
48
+ the MIT grant is intact; the third-party section names the UCUM copyright holder, licence URL,
49
+ revocability, no-derivative-works restriction, warranty disclaimer and `NOTICE.md` pointer;
50
+ `LICENSE`, `README.md`, the package JSDoc and the four `docs-content/` pages all name the same
51
+ distributed items, so one surface cannot be updated while another is left contradicting it; no
52
+ prose file in `LICENSE`, `README.md`, `docs-content/` or **all** of `src/` contains the phrase
53
+ "RxNorm content" or "SNOMED content"; and every checked-in path in `package.json`'s `files`
54
+ exists, which is the check `0.0.1` lacked when it pointed consumers at a `NOTICE.md` the tarball
55
+ did not contain. The anchor list is explicit rather than inferred, so a sixth bundled item still
56
+ needs the anchors updated by hand.
57
+ That guard bans the **phrase**, not a determiner pattern, and the reason is recorded because it
58
+ was got wrong first: a version matching `no\s+(\*\*)?RxNorm(\*\*)?\s+content` put the optional
59
+ bold markers after the space, so it matched `no **RxNorm content` and missed `**no** RxNorm
60
+ content` — the form every historical instance actually took — and read green over the whole
61
+ pre-fix tree, 0 of 6 real forms firing. A self-test now asserts the matcher fires on all eight
62
+ historical spellings and stays quiet on the correct wording. The file list also **walks** `src/`
63
+ rather than naming files, because a hand-written list omitted `src/rxnorm/rela.ts` and
64
+ `src/rxnorm/tty.ts`, whose doc comments compile into the declaration files.
65
+
66
+ - **Three published documentation pages carried a link form that MDX cannot compile, and it stopped
67
+ docs.cosyte.com building for every package** (`TERMINOLOGY-MDX-AUTOLINK`). `docs-content/intro.md`,
68
+ `troubleshooting.md` and `concepts-archetype.md` linked the UCUM licence as the CommonMark autolink
69
+ `<https://ucum.org/license>`. `cosyte/docs` compiles fetched content as **MDX**, which is stricter:
70
+ it reads the `<` as the start of an element, `https` as a tag name and `:` as a namespace separator,
71
+ then fails on the `/` with `Unexpected character '/' (U+002F) before local name`. Docusaurus SSG
72
+ failure is **global**, so three lines in this package's tarball did not degrade one page, they
73
+ stopped the whole documentation site deploying, for all nine packages it hosts. It went live when
74
+ `0.0.2` published (2026-07-29T19:20Z) and broke the next push to that repository's `main`.
75
+ Rewritten as `[https://ucum.org/license](https://ucum.org/license)`, which renders identically and
76
+ is the form MDX's own error message recommends. **No engine behaviour, API surface or bundled
77
+ content changes.**
78
+ `README.md`, `vendor/ucum/NOTICE.md` and `scripts/generate-ucum-essence-data.ts` use the same
79
+ autolink form and are **deliberately untouched**: verified against the published `0.0.2` assets,
80
+ `docs-content.tar.gz` ships only the narrative pages plus `sidebars.json` and `source.tar.gz` ships
81
+ only `package.json` + `src/`, so nothing outside `docs-content/` reaches MDX, and the form renders
82
+ correctly on GitHub and the registry where those files are actually read.
83
+
84
+ - **The package claimed to bundle no copyrighted content while bundling the UCUM table, and its
85
+ attribution never reached the tarball** (`TERMINOLOGY-LICENSING-ACCURACY`). Four statements shipped
86
+ in `0.0.1`, live on the registry and on docs.cosyte.com, that the tree falsifies:
87
+ 1. the unscoped "**zero copyrighted terminology content is bundled**" claim (README, the JSDoc
88
+ compiled into `dist/index.d.ts` / `dist/index.d.cts`, and four `docs-content/` pages) is
89
+ falsified by `ucum-essence.xml` — copyright Regenstrief Institute, Inc. — being embedded
90
+ byte-for-byte in `dist/index.mjs` and `dist/index.cjs`. The vendored XML carries no copyright
91
+ line of its own, so the shipped copy asserted nothing;
92
+ 2. `vendor/ucum/NOTICE.md` was **absent from `package.json`'s `files`**, so the attribution never
93
+ reached a consumer and the README pointed at a file the package did not contain. Measured by
94
+ packing the base tree: 10 files, no `NOTICE.md`;
95
+ 3. `src/crosswalk/categories.ts` bundles four SNOMED CT map-category concepts — id **and** steward
96
+ description — in the same doc comment that said "no SNOMED CT content is bundled";
97
+ 4. the README listed **LOINC** among "public-domain content packs" one line before listing it as
98
+ copyrighted, and listed **UCUM** among packs that "are not bundled" while bundling it.
99
+
100
+ Founder call 2026-07-29: keep the bundled table, make the claim true. Every consumer surface now
101
+ says **"no code-system release is bundled"** and then names what is: the UCUM unit table
102
+ (`ucum-essence.xml` v2.2, copyright ©1999–2024 Regenstrief Institute, Inc., verbatim under the UCUM
103
+ Copyright Notice and License, with its warranty disclaimer), the code-system identity pairings, and
104
+ the SNOMED CT concepts the crosswalk resolver names — the four map-category concepts **and** the two
105
+ gender findings (`248152002` / `248153007`) a gender `IFA` rule is written against, each with its
106
+ description (copyright © International Health Terminology Standards Development Organisation).
107
+ `vendor/ucum/NOTICE.md` is now in `files` and the packed tarball carries it, which also repairs the
108
+ dangling README pointer. On the README, the declaration files and the docs intro the list is written
109
+ as **open** ("includes the following"); a draft instead called it "three things … exact rather than
110
+ approximate", and that self-certification was falsified from `dist` in one grep by the gender
111
+ findings it had missed.
112
+
113
+ The README and the `src/` doc comments were corrected **together** and the built declaration files
114
+ read back, because fixing one and not the other is what made the previous attempt contradict itself
115
+ and forced a revert to base. Legal conclusions were **cut** rather than reworded, measured on the
116
+ base across `README.md`, `docs-content/`, `src/` and `vendor/`: **"license-clean" ×5**
117
+ (`src/codesystem/fhir.ts`, `src/codesystem/load.ts`, `src/codesystem/types.ts`,
118
+ `src/valueset/types.ts`, `vendor/ucum/NOTICE.md`) and a **permitted-use assertion ×2**
119
+ (`src/ucum/essence.ts`, `vendor/ucum/NOTICE.md`). All seven are now zero. The docs state what is
120
+ distributed and under whose copyright, and say plainly that this is not legal advice. The gate's own
121
+ tarball
122
+ drift tripwire caught the new public surface: `vendor/ucum/NOTICE.md` is now scanned by
123
+ `check:no-internal-refs`, and the internal roadmap citation it carried was removed before it shipped.
124
+
125
+ No runtime behaviour changes.
126
+
127
+ - **A RxNorm concept could load under a term type it does not have.** `loadRxNormGraph` recognized 18
128
+ of RxNorm's term types and typed each concept from whichever recognized `RXNCONSO` atom appeared
129
+ first in the file. Five real term types were not among the 18 (`SCDF`, `SBDF`, `SCDFP`, `SBDFP`,
130
+ `SCDGP`, two of which this package's own documentation already named as subjects of authored
131
+ `has_ingredient` rows), and `PSN` / `SY` / `TMSY` were, so a drug of an unrecognized type that also
132
+ carried a synonym atom loaded under the **synonym's** term type instead of being skipped. `RXCUI`
133
+ 370659, the `SCDF` "hydroxyzine Oral Tablet", loaded as a Tall Man Lettering synonym with no
134
+ warning, and then answered navigation queries under that type. The failure did not need an
135
+ unrecognized type: any concept whose synonym atom preceded its defining one was mislabelled the
136
+ same way, including an ordinary `SCD`.
137
+
138
+ Both halves are fixed together, because either alone leaves the class open. The term-type
139
+ vocabulary now carries all of RxNorm's, each under its published name, and `BPCK` reads "Brand Name
140
+ Pack" as the NLM writes it. And a concept is now typed by a **defining** atom wherever that sits in
141
+ the file: the NLM defines `PSN` / `SY` / `TMSY` as a "synonym of another TTY", so they type a name
142
+ rather than a concept and can no longer establish one. An `RXCUI` that no defining atom could type
143
+ is left out of the graph and reported as a new `TERM_RXNORM_UNTYPED_CONCEPT` warning, which also
144
+ covers concepts that were previously dropped in silence. Refusing is the safer direction: an absent
145
+ concept is already a first-class typed answer, while a concept whose term type reads `TMSY` is a
146
+ fabricated claim about what a drug is, and a caller cannot tell it from a real one.
147
+
148
+ `ingredientsOf` keeps its contract exactly: it follows authored edges only and synthesizes nothing.
149
+ Concepts of the five restored types now load, so they answer with the `has_ingredient` rows their
150
+ release really carries, and the reference now records what those are, verified per relation rather
151
+ than inferred from a sibling: an `SCDF` reaches the ingredient `IN`, an `SBDF` reaches the **brand
152
+ name** rather than the active ingredient, and the precise forms carry no ingredient edge at all.
153
+ An `SCDFP` instead reaches its basis-of-strength substances by `has_boss`, a one-to-many relation
154
+ whose targets can be precise ingredients or plain ingredients, and which `ingredientsOf`
155
+ deliberately does not follow. Also exports `isSynonymTermType`.
156
+
157
+ - **The documented RxNorm ingredient topology now matches the one RxNorm authors.** The package's
158
+ own documentation taught an edge that does not exist. The API reference for `ingredientsOf`, the
159
+ RxNorm type and guide overviews, the quickstart, the concepts guide, the README and the loader's
160
+ worked example all modelled a clinical drug linking straight to its ingredient (`SCD
161
+ has_ingredient IN`). RxNorm authors no such relationship in any release: the ingredient edge is
162
+ authored on the components and dose-form concepts around the clinical drug rather than on the drug
163
+ itself, a branded concept's own ingredient edge lands on its **brand name** rather than on the
164
+ active ingredient, and `IN ingredient_of` reaches those components rather than the drug. Because
165
+ the reference text ships inside the type declarations, an installed copy of the package was
166
+ teaching it too, and `ingredientsOf` on a clinical drug returning nothing read as a gap in the
167
+ engine rather than as the honest answer it is. Those places now state the authored topology, and
168
+ it is stated as the pairings RxNorm authors rather than as a closed set: the clinical side
169
+ (`SCDC`/`SCDF`/`SCDG`) reaches the `IN`, the branded side (`SBD`/`SBDC`/`SBDF`/`SBDG`) reaches the
170
+ `BN`, and the reader is told to check the term type that comes back. They also spell out how to
171
+ reach an _active_ ingredient, which is not the same walk from a clinical drug as from a branded
172
+ one: an `SBD`'s `consists_of` returns its branded component as well as the clinical one, and only
173
+ the clinical one leads to the `IN`. The quickstart carries new executable examples of both
174
+ traversals, so the guidance is checked against the shipped code rather than asserted in prose.
175
+ Behaviour is unchanged: no loader, navigation or resolution result differs.
176
+ - **The npm description described a different package.** `@cosyte/terminology` shipped to the
177
+ registry describing itself as a "Terminology parser, serializer, and builder ... lenient on parse,
178
+ spec-clean on emit" - unmodified scaffold boilerplate, still in use by the packages that really
179
+ are parsers. This one is explicitly **not** a parser, mirrors the FHIR Terminology Module rather
180
+ than a parser API, and has neither a serializer nor a builder. The most-read line of a published
181
+ package therefore named three things it does not have and omitted what it does. The description
182
+ and the `parser` / `serializer` keywords now say what the engine is and what it operates over.
183
+
184
+ - **`VERSION` now reports the version you actually installed.** `@cosyte/terminology@0.0.1` shipped
185
+ exporting `VERSION === "0.0.0"`: `package.json` was bumped by Changesets, the constant in the source
186
+ was not. `docs-content/installation.md` documents printing exactly that constant as the install smoke
187
+ test, so the one check an installer is told to run was the one that lied. The constant now reads
188
+ `0.0.1`, and it can no longer drift: the release `version` script rewrites it from `package.json`, and
189
+ a test compares the two, so a skipped sync fails the build instead of publishing.
190
+ - **Docs stated the wrong publish status in five places.** The README status block, the README install
191
+ note, `docs-content/intro.md`, `docs-content/installation.md`, and the project guide all said
192
+ `@cosyte/terminology` was "not yet published to npm" and told readers to consume it from source or a
193
+ workspace link. It has been on npm since `0.0.1`. All five now say the package is published, and
194
+ deliberately do not name a version: a version literal in prose is the same drift this release just
195
+ built a machine guard against, and the registry is the authority. (The historical `[0.0.1]` entry
196
+ below is left as written: it records what the docs said at the time, hours before the release
197
+ landed.)
198
+
199
+ ### Added
200
+
201
+ - **A gate over `docs-content/` that fails on an MDX-hostile link form**
202
+ (`test/docs-content-mdx-safety.test.ts`). A published release is immutable and the documentation
203
+ site re-fetches it indefinitely, so a bad link in a tarball cannot be recalled: fixing this
204
+ repository's `main` does nothing for the version that shipped it. The gate reports the offending
205
+ file and line with the replacement form, runs inside the required `ci / verify` context (so it
206
+ cannot be silently un-required by being split into its own job), and carries a **self-test** proving
207
+ the matcher can see a real autolink, so a green result cannot come from a pattern that matches
208
+ nothing. It reads bytes directly rather than shelling out to `grep`, which this repository has
209
+ already been burned by. Scoped to `docs-content/` only, for the reason above.
210
+
211
+ ### Changed
212
+
213
+ - **Internal project bookkeeping is gone from every surface a consumer reads, and a gate now keeps
214
+ it out.** Per the founder directive of 2026-07-27, what a consumer reads says what the software
215
+ does and what changed; item identifiers, phase and roadmap-section language, ADR numbers and
216
+ meta-repo paths belong in this file, the changeset, the commit and the roadmap. Measured on
217
+ `1158f96` with the final rule set: the public markdown carried 10 lines across four
218
+ `docs-content/` pages, `src/` doc comments produced 154 gate hits across 28 of the 38 source
219
+ files (91 from the line pass and 63 from the paragraph-reflow pass, which re-reports all but one
220
+ of the same places), and the npm `description`/`keywords` and `src/` string literals carried
221
+ none. The doc comments are the
222
+ surface that matters most and the one no docs review looks at: they compile verbatim into
223
+ `dist/index.d.ts` and `dist/index.d.cts`, so **89 lines of the published declaration files**
224
+ carried this text into every consumer's editor. All are now zero.
225
+
226
+ Two shapes no rule catches were cleared by hand, and they are recorded as places rather than as a
227
+ count: clause-terminal uses of "phase" (in the README, the troubleshooting page, and the
228
+ concept-map, crosswalk, module and RxNorm reference docs), and two bare roadmap-section citations
229
+ written without the word "roadmap" next to them. **A zero from a rule set is not a zero**, which
230
+ is why the by-hand half is stated at all.
231
+
232
+ Two statements were **restated rather than deleted**, because removing the planning pointer alone
233
+ would have widened each into a capability the code does not provide: subsumption is documented as
234
+ not computed across two separate releases (it reads the loaded system's own `parent` edges), and
235
+ the `ICD10CM_ORDER_FILE_FIELDS` preset is documented as not confirmed against an authoritative
236
+ machine-readable source. The NLM RxNorm Technical Documentation citations were
237
+ deliberately left standing: they are the normative grounding for the `RXNREL` edge-direction
238
+ convention, and a rule keyed on `§` alone would have deleted the reference a consumer needs in
239
+ order to check that direction. A self-test now asserts that no rule matches them, so closing that
240
+ gap has to be a decision.
241
+
242
+ - **`pnpm check:no-internal-refs` is a new repository gate, and it runs in its own CI job.** It
243
+ scans `README.md`, `LICENSE`, `docs-content/`, the npm `description` and `keywords`, `src/` doc
244
+ comments and `src/` string literals, line by line and paragraph-joined, and refuses to report
245
+ green from a scan that did not read all of its input. It deliberately does **not** scan
246
+ `CHANGELOG.md`, `.changeset/`, `CLAUDE.md` or `//` comments, which is where the identifiers
247
+ belong. Its rules key on known project prefixes and never on the `WORD-N` shape: measured here, a
248
+ shape rule matches 89 hyphenated uppercase tokens on this tree and **all 89 are the consumer's
249
+ reference material** (`ICD-10-CM`, `ICD-9`, `RFC-4180`, and the UCUM expressions `OHM-1`, `CM-1`
250
+ and `KG-1.S-2`, which are units with negative exponents), while the prefix-keyed rule matches
251
+ none. Negative self-tests pin that, so a later simplification reds instead of silently deleting a
252
+ code system's identifier from a terminology engine's documentation. One rule diverges from the
253
+ sibling copies on the same reasoning: the "unit of work" rule no longer keys on "the", because a
254
+ fixed-width order file's **field slice** is this package's public API and the sibling pattern was
255
+ wrong eight times out of nine here.
256
+
257
+ - **The RxNorm drug graph's edge direction is now pinned by tests in both directions.** RxNorm
258
+ relationships are directional, and reading one backwards is a wrong-medication defect rather than a
259
+ cosmetic one: the ingredient of a tablet would come back as a tablet that is an "ingredient of" its
260
+ own ingredient, and the generic behind a brand would come back as the brand behind a generic. The
261
+ convention is now pinned per relation family (ingredient, tradename, dose form, components and
262
+ packs), forward and reverse, against the sample relationship rows published in the RxNorm technical
263
+ documentation, so an inverted reading cannot pass by trading one assertion for another. The pinned
264
+ fixtures follow the topology RxNorm actually authors, including the two places it surprises people:
265
+ a clinical drug carries no direct ingredient edge (the ingredient is reached through its
266
+ component), and a branded drug's direct ingredient edge points at its brand name. Behaviour is
267
+ unchanged: the loader and the navigation surface answer exactly as they did before.
268
+ - **`VERSION` is declared `string` rather than the version literal.** Its emitted type was previously
269
+ narrowed to the exact release string, so every version bump was a `.d.ts` change and code comparing
270
+ `VERSION` against another string could be rejected by the type checker. Widening it makes the
271
+ declared type stable across releases. Runtime value is unchanged.
272
+
273
+ ### Security
274
+
275
+ - **The CI checks that run on a pull request now block the merge.** Until now `@cosyte/terminology`
276
+ had no branch-protection ruleset at all, so `ci` (typecheck, lint, format, PHI scan, tests, coverage
277
+ gate, build, `attw`, dual ESM/CJS smoke) and CodeQL could all go red and the merge would still land
278
+ on `main`, and `main` publishes. A repository ruleset now requires those checks, restricted to the
279
+ GitHub Actions app so a status of the same name cannot be posted by anything else, and blocks branch
280
+ deletion and force-push on `main`. Scope of the claim: this makes a red check binding, it does not
281
+ make a check correct, and nothing inside this repository can observe the ruleset. See the note in
282
+ `.github/workflows/ci.yml` before splitting a required job.
283
+ - **Dependency updates are now watched.** The repository had no Dependabot configuration, so its zero
284
+ open update PRs meant nothing was looking, not that nothing was stale.
285
+
286
+ ## [0.0.1] - 2026-07-21
287
+
288
+ The first pre-alpha release. The package begins its public history at `0.0.x`, per the cosyte version
289
+ ladder (`0.0.x` until first alpha).
14
290
 
15
291
  ### Security
16
292
 
@@ -242,8 +518,5 @@ its public history at `0.0.x`, per the cosyte version ladder (`0.0.x` until firs
242
518
  `WARNING_CODES` entry, and the parse-oriented option/warning types). This package is an engine, not
243
519
  a parser; that placeholder surface never shipped.
244
520
 
245
- ### Fixed
246
-
247
- ### Security
248
-
249
- [Unreleased]: https://github.com/cosyte/terminology/commits/main
521
+ [Unreleased]: https://github.com/cosyte/terminology/compare/v0.0.1...HEAD
522
+ [0.0.1]: https://github.com/cosyte/terminology/releases/tag/v0.0.1
package/LICENSE CHANGED
@@ -19,3 +19,86 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
+
23
+
24
+ ================================================================================
25
+ THIRD-PARTY MATERIALS
26
+ ================================================================================
27
+
28
+ The MIT license above covers Cosyte's own code. This repository and the published
29
+ npm package also distribute third-party materials. Those materials stay under
30
+ their own copyright and license terms, and the MIT grant above does not extend to
31
+ them. What is distributed includes the following.
32
+
33
+ 1. The UCUM unit table (vendor/ucum/ucum-essence.xml)
34
+
35
+ The Unified Code for Units of Measure (UCUM), also known as the "UCUM
36
+ Specification," is copyright (c) 1999-2024, Regenstrief Institute, Inc. All
37
+ rights reserved.
38
+
39
+ Version 2.2, revision-date 2024-06-17, is reproduced verbatim under the UCUM
40
+ Copyright Notice and License, https://ucum.org/license, and is embedded
41
+ byte-for-byte in the published build. That License grants a worldwide,
42
+ non-exclusive, no-charge, royalty-free, revocable license to reproduce,
43
+ publicly display, publicly perform and distribute the Work, and it permits
44
+ developing and commercializing software that interoperates with UCUM. It does
45
+ not permit using the Work to develop or promulgate a different standard, and it
46
+ does not permit derivative works: the content may not be modified and its
47
+ meaning must remain unchanged. This copy is unmodified, and no derivative of it
48
+ is distributed.
49
+
50
+ Disclaimer of warranties: the UCUM Specification is provided "as is" without
51
+ warranty of any kind. See the License at the URL above for the full disclaimer.
52
+
53
+ The complete notice ships inside this package at vendor/ucum/NOTICE.md. That
54
+ file, not this summary, is the notice, and it is the copy to read.
55
+
56
+ 2. The UCUM functional-test suite (vendor/ucum/UcumFunctionalTests.xml)
57
+
58
+ Copyright (c) 2008-2009 Grahame Grieve and other contributors (Gunther Schadow,
59
+ Lloyd Mackenzie, Vladimir Alexiev), reproduced verbatim under the Eclipse
60
+ Public License v1.0, https://www.eclipse.org/legal/epl-v10.html. It is used
61
+ only as a test fixture in this repository: package.json's "files" does not name
62
+ it, so it is not distributed in the npm package.
63
+
64
+ 3. Code-system identity facts (SYSTEM_IDENTITIES)
65
+
66
+ The OID and canonical-URI pairings, names and mnemonics for the code systems
67
+ the resolver recognizes. These identify the systems; no system's code list is
68
+ distributed.
69
+
70
+ 4. SNOMED CT concept identifiers named by the crosswalk resolver
71
+
72
+ SNOMED CT identifiers are distributed with their descriptions: the complex-map
73
+ category concepts in MAP_CATEGORIES (447637006, 447638001, 447639009,
74
+ 447640006) that a map row's mapCategoryId refers to, and the gender findings
75
+ 248152002 (Female) and 248153007 (Male) that a gender IFA rule is written
76
+ against. Further SNOMED CT identifiers, and ICD-10-CM codes, appear in the API
77
+ documentation examples, which are distributed in the compiled declaration
78
+ files (dist/index.d.ts, dist/index.d.cts) and in the build's sourcemaps, whose
79
+ sourcesContent carries every source comment verbatim.
80
+ SNOMED CT is copyright (c) International Health Terminology Standards
81
+ Development Organisation. No SNOMED CT release or refset is distributed; you
82
+ supply the map rows under your own SNOMED CT licence.
83
+
84
+ 5. RxNorm relationship and term-type names (RELA, RELA_INVERSE, TERM_TYPES)
85
+
86
+ The drug-graph API is written against RxNorm's own vocabulary, and those names
87
+ are distributed: the relationship names in RELA and RELA_INVERSE
88
+ (has_ingredient, ingredient_of, tradename_of, and the rest), and the term-type
89
+ codes in TERM_TYPES, each with its name as published by the U.S. National
90
+ Library of Medicine ("SCD" reads "Semantic Clinical Drug"). Individual RxNorm
91
+ identifiers also appear in the API documentation examples, with their RxNorm
92
+ names and term types (for instance RXCUI 316151, "lisinopril 10 MG"), and in
93
+ README.md as well as the declaration files and sourcemaps named above. RxNorm
94
+ is produced by the National Library of Medicine. No RxNorm release is
95
+ distributed; you supply it.
96
+
97
+ Beyond what is named above, no code-system release is distributed with this
98
+ package. SNOMED CT, CPT, LOINC, UMLS and RxNorm releases, the CMS GEM files, the
99
+ NLM complex-map refset and VSAC value sets are all supplied by the caller, under
100
+ whatever terms their steward sets.
101
+
102
+ This section states what is distributed and under whose copyright. It is not legal
103
+ advice, and it makes no claim about whether any particular use of these materials
104
+ is permitted to you.
package/README.md CHANGED
@@ -6,12 +6,14 @@
6
6
  `@cosyte/terminology` is **not a wire parser**. It mirrors the FHIR **Terminology Module**
7
7
  (`$translate`, `$lookup`, `$validate-code`, `$expand`, …), operating over **consumer-supplied** FHIR
8
8
  resources. It is the sibling engine `@cosyte/transform` and, later, the parsers' code-system
9
- recognition consume. It ships the **engine, never copyrighted terminology content** — SNOMED CT, CPT,
10
- full LOINC/UMLS, and VSAC value sets are strictly bring-your-own; code-system _identities_ (OID ↔
11
- canonical URI) are published facts, grounded firsthand and encoded.
12
-
13
- > **Status:** pre-alpha (`0.0.x`), **not yet published to npm.** The **engine is complete** — every
14
- > operation below ships today. What is still to come is bundled _content_, not capability. The surface:
9
+ recognition consume. It ships the **engine, and no code-system release** — SNOMED CT, CPT, LOINC,
10
+ UMLS/RxNorm, and VSAC value sets are strictly bring-your-own. It is not content-free, though: what it
11
+ _does_ bundle, including the UCUM unit table, the code-system _identities_ (OID ↔ canonical URI), the
12
+ SNOMED CT concepts the crosswalk resolver names and RxNorm's relationship and term-type names, is
13
+ listed with its copyright under [What is bundled](#what-is-bundled).
14
+
15
+ > **Status:** pre-alpha (`0.0.x`), **published on npm.** The **engine is complete** — every
16
+ > operation below ships today. The surface:
15
17
  >
16
18
  > - the code-system **identity resolver** (`resolveSystem`) and the ConceptMap **`$translate`** engine
17
19
  > (`loadConceptMap` / `translate`);
@@ -23,22 +25,23 @@ canonical URI) are published facts, grounded firsthand and encoded.
23
25
  > magnitude conversion**) — the official UCUM functional-test suite is the conformance gate;
24
26
  > - the **crosswalk resolvers** — the CMS **ICD-9↔ICD-10 GEMs** (`loadGems` / `applyGem`,
25
27
  > public-domain) and the NLM **SNOMED CT → ICD-10-CM complex map** (`loadComplexMap` /
26
- > `applyComplexMap`, BYO — zero SNOMED content bundled), never inverted;
28
+ > `applyComplexMap`, BYO — no SNOMED CT refset bundled), never inverted;
27
29
  > - the **RxNorm drug graph** (`loadRxNormGraph` + `ingredientsOf` / `genericFor` / `brandsFor` /
28
30
  > `doseFormsOf` / `consistsOf` / `resolveNdc` / `approximateMatch`) over a **BYO** RxNorm RRF release,
29
31
  > edges read in RxNorm's documented direction and never inverted, an absent `RXCUI`/NDC typed, never
30
32
  > fabricated.
31
33
  >
32
- > **Bring your own data.** The engine is whole; **no code-system _content_ is bundled yet.** It runs
33
- > over the FHIR resources and standard releases _you_ supply. The bundleable public-domain content packs
34
- > (RxNorm Prescribable, ICD-10-CM, UCUM, LOINC) are still to come — they are blocked on getting a genuine
35
- > verbatim public-domain release into the build, not on engine work — and the copyrighted content
36
- > (SNOMED CT / CPT / full LOINC / UMLS / VSAC) is bring-your-own permanently by license.
34
+ > **Bring your own data.** The engine is whole; **no code-system release is bundled.** It runs over
35
+ > the FHIR resources and standard releases _you_ supply — SNOMED CT, CPT, LOINC, UMLS/RxNorm, the CMS
36
+ > GEM files, the NLM complex-map refset and VSAC value sets are all bring-your-own, under whatever
37
+ > terms their steward sets. The content packs that would change that (RxNorm Prescribable, ICD-10-CM)
38
+ > are not bundled either. For the things that **are** bundled, and under whose copyright, see
39
+ > [What is bundled](#what-is-bundled).
37
40
 
38
41
  ## Install
39
42
 
40
- > **Not on npm yet.** `@cosyte/terminology` is pre-alpha and **unpublished** — the command below is how
41
- > you will install it once the first `0.0.x` release lands. Until then the package is source-only.
43
+ > **On npm.** `@cosyte/terminology` is **published**, on the pre-alpha `0.0.x` ladder. The command
44
+ > below installs the real package; the registry, not this page, is the authority on the exact version.
42
45
 
43
46
  ```bash
44
47
  npm install @cosyte/terminology
@@ -111,9 +114,12 @@ ucumEqual("mg", "g"); // false — different units (this is not conversion)
111
114
  ucumEqual("Cel", "K"); // false — a special (non-linear) unit is never equated with a linear one
112
115
  ```
113
116
 
114
- The UCUM table is the official `ucum-essence.xml`, vendored **verbatim** and parsed at runtime; the
115
- engine ships no derivative of it and passes the official UCUM functional-test suite. See
116
- `vendor/ucum/NOTICE.md`.
117
+ The UCUM table is the official `ucum-essence.xml` (version 2.2), and it **is bundled with this
118
+ package** — embedded verbatim in the published build and parsed at runtime. It is copyright
119
+ ©1999–2024 Regenstrief Institute, Inc., reproduced under the UCUM Copyright Notice and License
120
+ (<https://ucum.org/license>); the engine ships no derivative of it and passes the official UCUM
121
+ functional-test suite. The full notice ships in the package at `vendor/ucum/NOTICE.md` — see
122
+ [What is bundled](#what-is-bundled).
117
123
 
118
124
  > **Case-sensitive (c/s) mode only.** UCUM's normative interchange mode — the one FHIR/HL7 bind to —
119
125
  > is case-sensitive (`m` = metre, `M` = mega, `Pa` = pascal, `pA` = picoampere). The
@@ -128,8 +134,34 @@ dose-form graph. Relationships are read in RxNorm's documented direction (`RELA`
128
134
  the **second** `RXCUI` has to the **first**); navigation follows only authored edges — the engine
129
135
  never synthesizes an inverse, and never fabricates a concept, edge, or NDC.
130
136
 
137
+ Authored edges only means the graph you get is RxNorm's, including where its shape surprises you.
138
+ `has_ingredient` is authored from the **clinical** side (`SCDC`/`SCDF`/`SCDG`) to the ingredient
139
+ `IN`, and from the **branded** side (`SBD`/`SBDC`/`SBDF`/`SBDG`) to the **brand name** `BN` rather
140
+ than to the active ingredient. RxNorm authors no ingredient edge on a clinical drug (`SCD`) at all,
141
+ so that query answers with an honest empty set rather than a guess. To reach an active ingredient,
142
+ walk to the **clinical** component and take its edge: `consistsOf` then `ingredientsOf`. From an
143
+ `SBD`, `consistsOf` returns both its branded component (`SBDC`, whose ingredient edge is the brand
144
+ name again) and the clinical `SCDC`; it is the `SCDC` that leads to the `IN`. The **precise** forms
145
+ (`SCDFP`, `SBDFP`, `SCDGP`) carry no ingredient edge at all: an `SCDFP` instead reaches its
146
+ basis-of-strength substances by `has_boss`, a one-to-many relation with a different meaning whose
147
+ targets can be a `PIN` or a plain `IN`, and which `ingredientsOf` does not follow. Read the `tty` of
148
+ what comes back rather than assuming it.
149
+
150
+ A concept is typed by its **defining** atom, whichever line of `RXNCONSO` that sits on. `PSN`, `SY`
151
+ and `TMSY` type a _name_ rather than a concept, so they never establish an `RXCUI`'s term type. An
152
+ `RXCUI` no defining atom could type is left out of the graph and reported as
153
+ `TERM_RXNORM_UNTYPED_CONCEPT` on `graph.warnings`, rather than loaded under a synonym's term type:
154
+ a concept that answers with a term type it does not have is a fabricated claim about a drug, and
155
+ this engine refuses instead.
156
+
131
157
  ```ts
132
- import { loadRxNormGraph, ingredientsOf, genericFor, resolveNdc } from "@cosyte/terminology";
158
+ import {
159
+ loadRxNormGraph,
160
+ ingredientsOf,
161
+ consistsOf,
162
+ genericFor,
163
+ resolveNdc,
164
+ } from "@cosyte/terminology";
133
165
 
134
166
  const graph = loadRxNormGraph({
135
167
  conso: rxnconsoRrf,
@@ -138,14 +170,17 @@ const graph = loadRxNormGraph({
138
170
  version: "RXNORM_2026AA",
139
171
  });
140
172
 
141
- ingredientsOf(graph, "314076"); // { found: true, targets: [ { rxcui: "29046", tty: "IN", name: "lisinopril", … } ] }
142
- genericFor(graph, "311354"); // an SBD → its SCD, via the authored tradename_of edge
173
+ ingredientsOf(graph, "316151"); // an SCDC → { found: true, targets: [ { rxcui: "29046", tty: "IN", name: "lisinopril", … } ] }
174
+ ingredientsOf(graph, "314076"); // an SCD → { found: true, targets: [] }, because no such edge is authored
175
+ consistsOf(graph, "314076"); // → the SCDC, the first of the two hops to the ingredient
176
+ genericFor(graph, "104377"); // an SBD → its SCD, via the authored tradename_of edge
143
177
  resolveNdc(graph, "00000000001"); // { resolved: true, rxcui: "314076", status: "active", asOf: "RXNORM_2026AA" }
144
178
  ingredientsOf(graph, "99999999"); // { found: false, code: "TERM_RXNORM_UNKNOWN_RXCUI" } — never a guess
145
179
  ```
146
180
 
147
- The engine ships **no** RxNorm content — you supply the release (the public-domain Current Prescribable
148
- Content subset, or the full BYO release). Bundling the public-domain pack is a later phase.
181
+ The engine ships **no** RxNorm release; you supply it (the public-domain Current Prescribable
182
+ Content subset, or the full BYO release). What it does ship is RxNorm's relationship and term-type
183
+ _names_, listed under [What is bundled](#what-is-bundled).
149
184
 
150
185
  ## The invariants this engine is built on
151
186
 
@@ -154,8 +189,9 @@ Content subset, or the full BYO release). Bundling the public-domain pack is a l
154
189
  `unknown`, never a guessed URI.
155
190
  - **Never invert.** A directional map is read in its authored direction only; reverse translation
156
191
  needs an explicit inverse map, never a mechanical inversion.
157
- - **BYO data, engine-only.** Zero copyrighted terminology content is bundled (a licensing wall, not a
158
- gap); the engine operates over your own FHIR resources.
192
+ - **BYO data, engine-only.** No code-system release is bundled (a licensing wall, not a gap); the
193
+ engine operates over your own FHIR resources. The UCUM unit table and the identity sets that
194
+ **are** bundled are named, with their copyright, under [What is bundled](#what-is-bundled).
159
195
  - **Liberal load, conservative assertion.** Malformed input degrades to a typed diagnostic; a 1:many
160
196
  mapping returns the full candidate set, never collapsed to one.
161
197
  - **Value-free diagnostics.** A diagnostic carries a code + system + version, never patient context —
@@ -163,6 +199,41 @@ Content subset, or the full BYO release). Bundling the public-domain pack is a l
163
199
  - **Zero runtime dependencies. Dual ESM + CJS.** Node stdlib only; built with `tsup`, validated with
164
200
  `attw`. Immutable by construction (every returned value is deep-frozen).
165
201
 
202
+ ## What is bundled
203
+
204
+ The engine bundles **no code-system release**. What it _does_ bundle includes the following, named
205
+ with its copyright:
206
+
207
+ - **The UCUM unit table** — `ucum-essence.xml`, version 2.2, revision-date 2024-06-17. The Unified
208
+ Code for Units of Measure (UCUM) is copyright ©1999–2024 Regenstrief Institute, Inc., all rights
209
+ reserved, and is reproduced **verbatim** under the UCUM Copyright Notice and License
210
+ (<https://ucum.org/license>). It is embedded byte-for-byte in the published build and parsed at
211
+ runtime; no modified or derivative copy is distributed. The UCUM Specification is provided "as is"
212
+ **without warranty of any kind** — see the License for the full disclaimer. The complete notice
213
+ ships with this package at `vendor/ucum/NOTICE.md`.
214
+ - **Code-system identity facts** — the OID ↔ canonical-URI pairings (`SYSTEM_IDENTITIES`). These
215
+ identify the systems; they are not any system's code list.
216
+ - **The SNOMED CT concepts the crosswalk resolver names**, each with its description: the four
217
+ map-category concepts in `MAP_CATEGORIES` (`447637006`, `447638001`, `447639009`, `447640006`) that
218
+ a complex-map row's `mapCategoryId` refers to, and the two gender findings (`248152002` Female,
219
+ `248153007` Male) that a gender `IFA` rule is written against. Further SNOMED CT identifiers, and
220
+ ICD-10-CM codes, appear in the API documentation examples, which are distributed in the compiled
221
+ declaration files and in the build's sourcemaps. SNOMED CT is copyright
222
+ © International Health Terminology Standards Development Organisation. **No SNOMED CT release or
223
+ refset is bundled** — you supply the map rows under your own SNOMED CT licence.
224
+ - **The RxNorm relationship and term-type names** the drug-graph API is written against: the
225
+ relationship names in `RELA` / `RELA_INVERSE` (`has_ingredient`, `tradename_of`, …), and the
226
+ term-type codes in `TERM_TYPES`, each with its name as published by the U.S. National Library of
227
+ Medicine (`SCD` reads "Semantic Clinical Drug"). Individual RxNorm identifiers also appear in the
228
+ API documentation examples, with their RxNorm names and term types (for instance `RXCUI` `316151`,
229
+ "lisinopril 10 MG"), and in this README as well as the places named above. RxNorm is produced by the
230
+ National Library of Medicine. **No RxNorm release is bundled**; you supply it.
231
+
232
+ This section states what is distributed and under whose copyright. It is not legal advice, and makes
233
+ no claim about whether any particular use of these materials is permitted to you.
234
+
166
235
  ## License
167
236
 
168
- MIT © Cosyte
237
+ MIT © Cosyte — this package's own code. The bundled third-party materials above stay under their own
238
+ copyright and licences; the `LICENSE` file that ships with this package carries the MIT text and a
239
+ third-party notices section naming each of them.