fhirsmith 0.17.0 → 0.18.1

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.
Files changed (131) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/README.md +80 -13
  3. package/config-template.json +106 -1
  4. package/ips/ai-client.js +263 -0
  5. package/ips/composer.js +805 -0
  6. package/ips/editor-schema.js +289 -0
  7. package/ips/editor.js +290 -0
  8. package/ips/fhirpath-ips.js +397 -0
  9. package/ips/ips-codeable-concepts.md +1742 -0
  10. package/ips/ips-template.html +125 -0
  11. package/ips/ips-valuesets.js +159 -0
  12. package/ips/ips.js +1736 -0
  13. package/ips/membership.js +179 -0
  14. package/ips/pipeline.js +1012 -0
  15. package/ips/presenter.js +974 -0
  16. package/ips/profile-editor.js +578 -0
  17. package/ips/profiles.js +506 -0
  18. package/ips/readme.md +460 -0
  19. package/ips/render.js +1482 -0
  20. package/ips/search-params.js +83 -0
  21. package/ips/search.js +667 -0
  22. package/ips/share.js +178 -0
  23. package/ips/store.js +650 -0
  24. package/ips/terminology.js +395 -0
  25. package/ips/tx-client.js +202 -0
  26. package/ips/valuesets/CodeSystem-absent-unknown-uv-ips.json +206 -0
  27. package/ips/valuesets/ValueSet-absent-or-unknown-immunizations-uv-ips.json +59 -0
  28. package/ips/valuesets/ValueSet-ips-allergy-code.json +59 -0
  29. package/ips/valuesets/ValueSet-ips-allergy-manifestation.json +356 -0
  30. package/ips/valuesets/ValueSet-ips-allergy-substance.json +37 -0
  31. package/ips/valuesets/ValueSet-ips-body-site.json +26 -0
  32. package/ips/valuesets/ValueSet-ips-clinical-finding-or-none.json +35 -0
  33. package/ips/valuesets/ValueSet-ips-clinical-finding.json +26 -0
  34. package/ips/valuesets/ValueSet-ips-condition-code.json +72 -0
  35. package/ips/valuesets/ValueSet-ips-condition-severity.json +30 -0
  36. package/ips/valuesets/ValueSet-ips-condition-stage.json +26 -0
  37. package/ips/valuesets/ValueSet-ips-exposure-route.json +26 -0
  38. package/ips/valuesets/ValueSet-ips-imaging-procedure.json +25 -0
  39. package/ips/valuesets/ValueSet-ips-immunization-status-reason.json +43 -0
  40. package/ips/valuesets/ValueSet-ips-international-codes.json +25 -0
  41. package/ips/valuesets/ValueSet-ips-medication-ingredient.json +26 -0
  42. package/ips/valuesets/ValueSet-ips-medication-status-reason.json +37 -0
  43. package/ips/valuesets/ValueSet-ips-medication.json +50 -0
  44. package/ips/valuesets/ValueSet-ips-observation-code.json +18 -0
  45. package/ips/valuesets/ValueSet-ips-observation-interpretation.json +18 -0
  46. package/ips/valuesets/ValueSet-ips-procedure-followup.json +51 -0
  47. package/ips/valuesets/ValueSet-ips-procedure-outcome.json +30 -0
  48. package/ips/valuesets/ValueSet-ips-procedure-reason.json +37 -0
  49. package/ips/valuesets/ValueSet-ips-procedure.json +116 -0
  50. package/ips/valuesets/ValueSet-ips-route.json +46 -0
  51. package/ips/valuesets/ValueSet-ips-target-disease.json +171 -0
  52. package/ips/valuesets/ValueSet-ips-vaccine.json +31 -0
  53. package/ips/valuesets/elements.json +44 -0
  54. package/library/daily-report.js +432 -0
  55. package/library/fhir-search-values.js +138 -0
  56. package/library/html-server.js +32 -23
  57. package/library/mailer.js +77 -0
  58. package/library/narrative-check.js +21 -2
  59. package/library/npm-audit.js +25 -0
  60. package/library/package-manager.js +4 -2
  61. package/library/sqlite-session-store.js +29 -1
  62. package/package.json +8 -3
  63. package/packages/packages.js +34 -3
  64. package/publisher/publisher.js +29 -0
  65. package/registry/auth-store.js +366 -0
  66. package/registry/auth.js +872 -0
  67. package/registry/crawler.js +41 -1
  68. package/registry/documentation/api.md +162 -0
  69. package/registry/documentation/configuration.md +93 -0
  70. package/registry/documentation/documents.md +74 -0
  71. package/registry/documentation/internals.md +151 -0
  72. package/registry/documentation/readme.md +100 -0
  73. package/registry/documentation/security.md +199 -0
  74. package/registry/documents.js +309 -0
  75. package/registry/openapi.yaml +30 -0
  76. package/registry/registry-template.html +1 -1
  77. package/registry/registry.js +230 -44
  78. package/registry/uptime.js +154 -0
  79. package/server.js +105 -44
  80. package/shl/shl.js +64 -9
  81. package/stats-db.js +93 -0
  82. package/stats.js +158 -1
  83. package/testing/search.js +2 -108
  84. package/testing/store.js +5 -0
  85. package/testing/testing.js +20 -0
  86. package/token/token-template.html +5 -4
  87. package/token/token.js +17 -10
  88. package/translations/Messages.properties +10 -0
  89. package/tx/README.md +52 -0
  90. package/tx/closure/closure-store.js +14 -0
  91. package/tx/cs/cs-api.js +92 -14
  92. package/tx/cs/cs-cs.js +64 -3
  93. package/tx/cs/cs-icd11.js +366 -112
  94. package/tx/cs/cs-icf.js +431 -0
  95. package/tx/cs/cs-loinc.js +296 -27
  96. package/tx/cs/cs-omop.js +10 -3
  97. package/tx/cs/cs-snomed.js +1210 -80
  98. package/tx/cs/cs-ucum.js +6 -0
  99. package/tx/cs/icd11-expressions.js +191 -78
  100. package/tx/importers/csv-records.js +35 -0
  101. package/tx/importers/export-icd11-codesystem.js +58 -12
  102. package/tx/importers/icd11-schema.md +9 -5
  103. package/tx/importers/import-loinc.module.js +177 -41
  104. package/tx/importers/import-sct.module.js +66 -1
  105. package/tx/importers/subset-loinc.module.js +25 -6
  106. package/tx/library/codesystem.js +4 -1
  107. package/tx/library/designations.js +23 -30
  108. package/tx/library.js +98 -4
  109. package/tx/openapi-operations.js +6 -1
  110. package/tx/params.js +8 -1
  111. package/tx/provider.js +24 -6
  112. package/tx/sct/documentation.md +251 -0
  113. package/tx/sct/ecl.js +758 -46
  114. package/tx/sct/expressions.js +113 -52
  115. package/tx/sct/structures.js +8 -2
  116. package/tx/syndication/syndication-feed.js +218 -0
  117. package/tx/syndication/syndication-store.js +415 -0
  118. package/tx/tx-html.js +15 -1
  119. package/tx/tx.fhir.org.yml +3 -4
  120. package/tx/tx.js +144 -2
  121. package/tx/vs/vs-vsac.js +25 -6
  122. package/tx/workers/closure.js +20 -0
  123. package/tx/workers/expand.js +35 -8
  124. package/tx/workers/lookup.js +61 -6
  125. package/tx/workers/subsumes.js +1 -1
  126. package/tx/workers/translate.js +166 -15
  127. package/tx/workers/validate.js +138 -6
  128. package/utilities/make-sct-loinc-subset.js +250 -0
  129. package/utilities/make-sct-test-extension.js +264 -0
  130. package/xig/xig.js +32 -0
  131. package/registry/readme.md +0 -124
@@ -0,0 +1,125 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <html xml:lang="en" lang="en">
4
+ <head>
5
+ <title>FHIRsmith: [%title%]</title>
6
+
7
+ <meta charset="utf-8"/>
8
+ <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
9
+ <meta content="http://hl7.org/fhir" name="author"/>
10
+ <meta charset="utf-8" http-equiv="X-UA-Compatible" content="IE=edge" />
11
+
12
+ <link rel="stylesheet" href="/fhir.css"/>
13
+
14
+
15
+ <!-- Bootstrap core CSS -->
16
+ <link rel="stylesheet" href="/assets/css/bootstrap.css"/>
17
+ <link rel="stylesheet" href="/assets/css/bootstrap-fhir.css"/>
18
+
19
+ <!-- Project extras -->
20
+ <link rel="stylesheet" href="/assets/css/project.css"/>
21
+ <link rel="stylesheet" href="/assets/css/pygments-manni.css"/>
22
+
23
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
24
+ <!-- [if lt IE 9]>
25
+ <script src="/assets/js/html5shiv.js"></script>
26
+ <script src="/assets/js/respond.min.js"></script>
27
+ <![endif] -->
28
+
29
+ <!-- Favicons -->
30
+ <link sizes="144x144" rel="apple-touch-icon-precomposed" href="/assets/ico/apple-touch-icon-144-precomposed.png"/>
31
+ <link sizes="114x114" rel="apple-touch-icon-precomposed" href="/assets/ico/apple-touch-icon-114-precomposed.png"/>
32
+ <link sizes="72x72" rel="apple-touch-icon-precomposed" href="/assets/ico/apple-touch-icon-72-precomposed.png"/>
33
+ <link rel="apple-touch-icon-precomposed" href="/assets/ico/apple-touch-icon-57-precomposed.png"/>
34
+ <link rel="shortcut icon" href="/assets/ico/favicon.png"/>
35
+ <script type="text/javascript" src="/assets/js/json2.js"></script>
36
+ <script type="text/javascript" src="/assets/js/statuspage.js"></script>
37
+ <script type="text/javascript" src="/assets/js/jquery.min.js"></script>
38
+ <script type="text/javascript" src="/assets/js/jquery-ui.min.js"></script>
39
+ <link rel="stylesheet" href="/assets/css/jquery.ui.all.css">
40
+ <script type="text/javascript" src="/assets/js/jquery.ui.core.js"></script>
41
+ <script type="text/javascript" src="/assets/js/jquery.ui.widget.js"></script>
42
+ <script type="text/javascript" src="/assets/js/jquery.ui.mouse.js"></script>
43
+ <script type="text/javascript" src="/assets/js/jquery.ui.resizable.js"></script>
44
+ <script type="text/javascript" src="/assets/js/jquery.ui.draggable.js"></script>
45
+ <script type="text/javascript" src="/assets/js/jtip.js"></script>
46
+ <script type="text/javascript" src="/assets/js/jcookie.js"></script>
47
+ <script type="text/javascript" src="/assets/js/fhir-gw.js"></script>
48
+ </head>
49
+
50
+ <body>
51
+
52
+ <div id="segment-navbar" class="segment"> <!-- segment-breadcrumb -->
53
+ <div id="stripe"> </div>
54
+ <div class="container"> <!-- container -->
55
+ <div style="background-color: #ad1f2f; padding: 6px; color: white;"> <!-- container -->
56
+
57
+ <a href="http://www.hl7.org/fhir" style="color: gold" title="Fast Healthcare Interoperability Resources - Home Page"><img border="0" src="/icon-fhir-16.png" style="vertical-align: text-bottom"/> <b>FHIR</b></a> &copy; HL7.org &nbsp;|&nbsp;
58
+ <a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" style="color: gold"><img border="0" src="/FHIRsmith16.png" style="vertical-align: text-bottom"/> FHIRsmith</a> [%ver%] &nbsp;|&nbsp;
59
+ <a href="/" style="color: gold"> Server Home</a> &nbsp;|&nbsp;
60
+ <a href="/ips" style="color: gold">IPS</a>[%context-link%]
61
+ </div> <!-- /container -->
62
+ </div> <!-- /container -->
63
+ </div>
64
+
65
+ <!-- /segment-breadcrumb -->
66
+
67
+ <div id="segment-content" class="segment"> <!-- segment-content -->
68
+ <div class="container"> <!-- container -->
69
+ <div class="row">
70
+ <div class="inner-wrapper">
71
+ <div class="col-12">
72
+
73
+ <h2><img border="0" src="/FHIRsmith32.png" style="vertical-align: text-bottom"/> [%title%] </h2>
74
+
75
+ [%content%]
76
+
77
+
78
+ </div>
79
+
80
+
81
+ </div> <!-- /inner-wrapper -->
82
+ </div> <!-- /row -->
83
+ </div> <!-- /container -->
84
+ </div> <!-- /segment-content -->
85
+
86
+
87
+ <div id="segment-footer" class="segment"> <!-- segment-footer -->
88
+ <div class="container"> <!-- container -->
89
+ <div class="inner-wrapper">
90
+ <p>
91
+ <a href="http://www.hl7.org/fhir" style="color: gold" title="Fast Healthcare Interoperability Resources - Home Page"><img border="0" src="/icon-fhir-16.png" style="vertical-align: text-bottom"/> <b>FHIR</b></a> &copy; HL7.org 2011+. &nbsp;|&nbsp;
92
+ <a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" style="color: gold"><img border="0" src="/FHIRsmith16.png" style="vertical-align: text-bottom"/> FHIRsmith</a> [%ver%] &copy; HealthIntersections.com.au 2023+ &nbsp;|&nbsp;
93
+ &nbsp; ([%ms%] ms)
94
+ [%sponsorMessage%]
95
+ </p>
96
+ </div> <!-- /inner-wrapper -->
97
+ </div> <!-- /container -->
98
+ </div> <!-- /segment-footer -->
99
+
100
+
101
+ <div id="segment-post-footer" class="segment hidden"> <!-- segment-post-footer -->
102
+ <div class="container"> <!-- container -->
103
+ </div> <!-- /container -->
104
+ </div> <!-- /segment-post-footer -->
105
+
106
+
107
+
108
+
109
+
110
+ <!-- JS and analytics only. -->
111
+ <!-- Bootstrap core JavaScript
112
+ ================================================== -->
113
+ <!-- Placed at the end of the document so the pages load faster -->
114
+ <!-- <script src="/assets/js/jquery.js"/> -->
115
+ <script src="/assets/js/bootstrap.min.js"/>
116
+ <script src="/assets/js/respond.min.js"/>
117
+
118
+ <script src="/assets/js/fhir.js"/>
119
+
120
+ <!-- Analytics Below
121
+ ================================================== -->
122
+
123
+ </body>
124
+
125
+ </html>
@@ -0,0 +1,159 @@
1
+ //
2
+ // Copyright 2026, Health Intersections Pty Ltd (http://www.healthintersections.com.au)
3
+ //
4
+ // Licensed under BSD-3: https://opensource.org/license/bsd-3-clause
5
+ //
6
+
7
+ /**
8
+ * The value sets the terminology check holds IPS documents to - the module's own criteria
9
+ * for international exchange, not the IPS profiles' bindings. They are FHIR ValueSets in
10
+ * ips/valuesets/ (ValueSet-*.json), and elements.json there says which value set each
11
+ * CodeableConcept element is checked against, by path (Condition.code,
12
+ * MedicationStatement.medicationCodeableConcept, ...). Elements not listed have no value
13
+ * set of their own, and only the fallback applies: any SNOMED CT International or LOINC
14
+ * code (the fallback value set), or any code in an HL7 terminology (pipeline.js decides
15
+ * that by the system's URL, and asks the terminology server whether the code exists).
16
+ *
17
+ * The folder also carries the resources the value sets refer to and a terminology server
18
+ * might not have (the IPS absent/unknown codes): they go with each question as tx-resource.
19
+ *
20
+ * @module ips/ips-valuesets
21
+ */
22
+
23
+ const fs = require('fs');
24
+ const path = require('path');
25
+
26
+ const FOLDER = path.join(__dirname, 'valuesets');
27
+
28
+ // an HL7 terminology: anything HL7 publishes - THO, and the code systems of the FHIR
29
+ // specification and its implementation guides. Not the http://hl7.org/fhir/sid/ URLs: those
30
+ // name other people's terminologies (ICD-10, CVX, NDC...), international or not
31
+ const HL7_SYSTEM_PREFIXES = ['http://terminology.hl7.org/', 'http://hl7.org/fhir/'];
32
+ const NOT_HL7_PREFIXES = ['http://hl7.org/fhir/sid/'];
33
+
34
+ function unversioned(url) {
35
+ return typeof url === 'string' ? url.split('|')[0] : url;
36
+ }
37
+
38
+ /** Whether a code system URL is one of HL7's. */
39
+ function isHl7System(system) {
40
+ return typeof system === 'string' && HL7_SYSTEM_PREFIXES.some(p => system.startsWith(p)) && !NOT_HL7_PREFIXES.some(p => system.startsWith(p));
41
+ }
42
+
43
+ class IpsValueSets {
44
+ /** @param {string} [folder] - where the files are (tests) */
45
+ constructor(folder = FOLDER) {
46
+ this.folder = folder;
47
+ // canonical -> ValueSet
48
+ this.valueSets = new Map();
49
+ // canonical -> CodeSystem
50
+ this.codeSystems = new Map();
51
+ // element path -> canonical
52
+ this.elements = new Map();
53
+ this.fallback = null;
54
+ this.loaded = false;
55
+ }
56
+
57
+ /** Read the folder. Synchronous: the files are the module's own, and few. */
58
+ load() {
59
+ for (const name of fs.readdirSync(this.folder)) {
60
+ if (!name.endsWith('.json') || name === 'elements.json') {
61
+ continue;
62
+ }
63
+ const r = JSON.parse(fs.readFileSync(path.join(this.folder, name), 'utf8'));
64
+ if (r.resourceType === 'ValueSet' && typeof r.url === 'string') {
65
+ this.valueSets.set(unversioned(r.url), r);
66
+ } else if (r.resourceType === 'CodeSystem' && typeof r.url === 'string') {
67
+ this.codeSystems.set(unversioned(r.url), r);
68
+ } else {
69
+ throw new Error(`${name} is not a ValueSet or CodeSystem with a url`);
70
+ }
71
+ }
72
+ const map = JSON.parse(fs.readFileSync(path.join(this.folder, 'elements.json'), 'utf8'));
73
+ for (const [element, url] of Object.entries(map.elements || {})) {
74
+ if (!this.valueSets.has(url)) {
75
+ throw new Error(`elements.json: ${element} is checked against ${url}, which is not in ${this.folder}`);
76
+ }
77
+ this.elements.set(element, url);
78
+ }
79
+ if (typeof map.fallback !== 'string' || !this.valueSets.has(map.fallback)) {
80
+ throw new Error('elements.json: the fallback value set is missing');
81
+ }
82
+ this.fallback = map.fallback;
83
+ for (const vs of this.valueSets.values()) {
84
+ for (const part of ['include', 'exclude']) {
85
+ for (const inc of vs.compose?.[part] || []) {
86
+ for (const v of inc.valueSet || []) {
87
+ if (!this.valueSets.has(unversioned(v))) {
88
+ throw new Error(`${vs.url} includes ${v}, which is not in ${this.folder}`);
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
94
+ this.loaded = true;
95
+ return this;
96
+ }
97
+
98
+ /**
99
+ * The value set an element is checked against, or null when it has none of its own.
100
+ * @param {string} element - the path without indices, as terminology.js reports it
101
+ */
102
+ /** All the value sets: [{url, version, title}], by title. */
103
+ all() {
104
+ return [...this.valueSets.values()].map(v => ({ url: v.url, version: v.version || null, title: v.title || v.name || v.url }))
105
+ .sort((a, b) => a.title.localeCompare(b.title));
106
+ }
107
+
108
+ valueSetFor(element) {
109
+ return this.elements.get(element) || null;
110
+ }
111
+
112
+ /** The ValueSet resource for a canonical, if it is one of ours. */
113
+ valueSet(url) {
114
+ return this.valueSets.get(unversioned(url)) || null;
115
+ }
116
+
117
+ /**
118
+ * What goes with a question about a value set, besides the value set itself: the value
119
+ * sets it includes (transitively), and the code systems of this folder that any of them
120
+ * draw on.
121
+ */
122
+ referencedResources(url) {
123
+ const out = [];
124
+ const seen = new Set([unversioned(url)]);
125
+ const queue = [unversioned(url)];
126
+ const systems = new Set();
127
+ while (queue.length > 0) {
128
+ const vs = this.valueSets.get(queue.shift());
129
+ for (const part of ['include', 'exclude']) {
130
+ for (const inc of vs?.compose?.[part] || []) {
131
+ if (typeof inc.system === 'string') {
132
+ systems.add(unversioned(inc.system));
133
+ }
134
+ for (const v of inc.valueSet || []) {
135
+ const u = unversioned(v);
136
+ if (!seen.has(u)) {
137
+ seen.add(u);
138
+ queue.push(u);
139
+ out.push(this.valueSets.get(u));
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+ for (const s of systems) {
146
+ if (this.codeSystems.has(s)) {
147
+ out.push(this.codeSystems.get(s));
148
+ }
149
+ }
150
+ return out;
151
+ }
152
+
153
+ /** The elements with a value set, and their value sets (for the documentation and tests). */
154
+ table() {
155
+ return [...this.elements.entries()].map(([element, url]) => ({ element, valueSet: url }));
156
+ }
157
+ }
158
+
159
+ module.exports = { IpsValueSets, isHl7System, HL7_SYSTEM_PREFIXES, FOLDER };