fhirsmith 0.3.0

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 (277) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/FHIRsmith.png +0 -0
  3. package/README.md +277 -0
  4. package/config-template.json +144 -0
  5. package/library/folder-setup.js +58 -0
  6. package/library/html-server.js +166 -0
  7. package/library/html.js +835 -0
  8. package/library/i18nsupport.js +259 -0
  9. package/library/languages.js +779 -0
  10. package/library/logger-telnet.js +205 -0
  11. package/library/logger.js +279 -0
  12. package/library/package-manager.js +876 -0
  13. package/library/utilities.js +196 -0
  14. package/library/version-utilities.js +1056 -0
  15. package/npmprojector/config-example.json +13 -0
  16. package/npmprojector/indexer.js +394 -0
  17. package/npmprojector/npmprojector.js +395 -0
  18. package/npmprojector/readme.md +174 -0
  19. package/npmprojector/watcher.js +335 -0
  20. package/package.json +119 -0
  21. package/packages/package-crawler.js +846 -0
  22. package/packages/packages-template.html +126 -0
  23. package/packages/packages.js +2838 -0
  24. package/passwords.ini +2 -0
  25. package/publisher/publisher-template.html +208 -0
  26. package/publisher/publisher.js +2167 -0
  27. package/publisher/task-draft.js +458 -0
  28. package/registry/api.js +735 -0
  29. package/registry/crawler.js +637 -0
  30. package/registry/model.js +513 -0
  31. package/registry/readme.md +243 -0
  32. package/registry/registry-data.json +121015 -0
  33. package/registry/registry-template.html +126 -0
  34. package/registry/registry.js +1395 -0
  35. package/registry/test-runner.js +237 -0
  36. package/root-template.html +124 -0
  37. package/server.js +524 -0
  38. package/shl/private-key.pem +5 -0
  39. package/shl/public-key.pem +18 -0
  40. package/shl/shl.js +1125 -0
  41. package/shl/vhl.js +69 -0
  42. package/static/FHIRsmith128.png +0 -0
  43. package/static/FHIRsmith16.png +0 -0
  44. package/static/FHIRsmith32.png +0 -0
  45. package/static/FHIRsmith64.png +0 -0
  46. package/static/assets/css/bootstrap-fhir.css +5302 -0
  47. package/static/assets/css/bootstrap-glyphicons.css +2 -0
  48. package/static/assets/css/bootstrap.css +4097 -0
  49. package/static/assets/css/jquery-ui.css +523 -0
  50. package/static/assets/css/jquery-ui.structure.css +863 -0
  51. package/static/assets/css/jquery-ui.structure.min.css +5 -0
  52. package/static/assets/css/jquery-ui.theme.css +439 -0
  53. package/static/assets/css/jquery-ui.theme.min.css +5 -0
  54. package/static/assets/css/jquery.ui.all.css +7 -0
  55. package/static/assets/css/modules.css +18 -0
  56. package/static/assets/css/project.css +367 -0
  57. package/static/assets/css/pygments-manni.css +66 -0
  58. package/static/assets/css/tags.css +74 -0
  59. package/static/assets/css/xml.css +2 -0
  60. package/static/assets/fonts/glyphiconshalflings-regular.eot +0 -0
  61. package/static/assets/fonts/glyphiconshalflings-regular.otf +0 -0
  62. package/static/assets/fonts/glyphiconshalflings-regular.svg +175 -0
  63. package/static/assets/fonts/glyphiconshalflings-regular.ttf +0 -0
  64. package/static/assets/fonts/glyphiconshalflings-regular.woff +0 -0
  65. package/static/assets/ico/apple-touch-icon-114-precomposed.png +0 -0
  66. package/static/assets/ico/apple-touch-icon-144-precomposed.png +0 -0
  67. package/static/assets/ico/apple-touch-icon-57-precomposed.png +0 -0
  68. package/static/assets/ico/apple-touch-icon-72-precomposed.png +0 -0
  69. package/static/assets/ico/favicon.ico +0 -0
  70. package/static/assets/ico/favicon.png +0 -0
  71. package/static/assets/images/fhir-logo-www.png +0 -0
  72. package/static/assets/images/fhir-logo.png +0 -0
  73. package/static/assets/images/hl7-logo.png +0 -0
  74. package/static/assets/images/logo_ansinew.jpg +0 -0
  75. package/static/assets/images/search.png +0 -0
  76. package/static/assets/images/stripe.png +0 -0
  77. package/static/assets/images/target.png +0 -0
  78. package/static/assets/images/tx-registry-root.gif +0 -0
  79. package/static/assets/images/tx-registry.png +0 -0
  80. package/static/assets/images/tx-server.png +0 -0
  81. package/static/assets/images/tx-version.png +0 -0
  82. package/static/assets/js/bootstrap.min.js +6 -0
  83. package/static/assets/js/fhir-gw.js +259 -0
  84. package/static/assets/js/fhir.js +2 -0
  85. package/static/assets/js/html5shiv.js +8 -0
  86. package/static/assets/js/jcookie.js +96 -0
  87. package/static/assets/js/jquery-ui.min.js +6 -0
  88. package/static/assets/js/jquery.js +10716 -0
  89. package/static/assets/js/jquery.min.js +2 -0
  90. package/static/assets/js/jquery.ui.core.js +314 -0
  91. package/static/assets/js/jquery.ui.draggable.js +825 -0
  92. package/static/assets/js/jquery.ui.mouse.js +162 -0
  93. package/static/assets/js/jquery.ui.resizable.js +842 -0
  94. package/static/assets/js/jquery.ui.widget.js +268 -0
  95. package/static/assets/js/json2.js +487 -0
  96. package/static/assets/js/jtip.js +97 -0
  97. package/static/assets/js/respond.min.js +6 -0
  98. package/static/assets/js/statuspage.js +70 -0
  99. package/static/assets/js/xml.js +2 -0
  100. package/static/dist/js/bootstrap.js +1964 -0
  101. package/static/favicon.png +0 -0
  102. package/static/fhir.css +626 -0
  103. package/static/icon-fhir-16.png +0 -0
  104. package/static/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  105. package/static/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  106. package/static/images/ui-bg_flat_10_000000_40x100.png +0 -0
  107. package/static/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  108. package/static/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  109. package/static/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  110. package/static/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  111. package/static/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  112. package/static/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  113. package/static/images/ui-icons_222222_256x240.png +0 -0
  114. package/static/images/ui-icons_228ef1_256x240.png +0 -0
  115. package/static/images/ui-icons_ef8c08_256x240.png +0 -0
  116. package/static/images/ui-icons_ffd27a_256x240.png +0 -0
  117. package/static/images/ui-icons_ffffff_256x240.png +0 -0
  118. package/static/js/jquery.effects.blind.js +49 -0
  119. package/static/js/jquery.effects.bounce.js +78 -0
  120. package/static/js/jquery.effects.clip.js +54 -0
  121. package/static/js/jquery.effects.core.js +763 -0
  122. package/static/js/jquery.effects.drop.js +50 -0
  123. package/static/js/jquery.effects.explode.js +79 -0
  124. package/static/js/jquery.effects.fade.js +32 -0
  125. package/static/js/jquery.effects.fold.js +56 -0
  126. package/static/js/jquery.effects.highlight.js +50 -0
  127. package/static/js/jquery.effects.pulsate.js +51 -0
  128. package/static/js/jquery.effects.scale.js +178 -0
  129. package/static/js/jquery.effects.shake.js +57 -0
  130. package/static/js/jquery.effects.slide.js +50 -0
  131. package/static/js/jquery.effects.transfer.js +45 -0
  132. package/static/js/jquery.ui.accordion.js +611 -0
  133. package/static/js/jquery.ui.autocomplete.js +612 -0
  134. package/static/js/jquery.ui.button.js +416 -0
  135. package/static/js/jquery.ui.datepicker.js +1823 -0
  136. package/static/js/jquery.ui.dialog.js +878 -0
  137. package/static/js/jquery.ui.droppable.js +296 -0
  138. package/static/js/jquery.ui.position.js +252 -0
  139. package/static/js/jquery.ui.progressbar.js +109 -0
  140. package/static/js/jquery.ui.selectable.js +266 -0
  141. package/static/js/jquery.ui.slider.js +666 -0
  142. package/static/js/jquery.ui.sortable.js +1077 -0
  143. package/static/js/jquery.ui.tabs.js +758 -0
  144. package/stats.js +80 -0
  145. package/test-cache/vsac/vsac-valuesets.db +0 -0
  146. package/token/nginx_passport_setup.md +383 -0
  147. package/token/security_guide.md +294 -0
  148. package/token/token-template.html +330 -0
  149. package/token/token.js +1300 -0
  150. package/translations/Messages.properties +1510 -0
  151. package/translations/Messages_ar.properties +1399 -0
  152. package/translations/Messages_de.properties +836 -0
  153. package/translations/Messages_es.properties +737 -0
  154. package/translations/Messages_fr.properties +1 -0
  155. package/translations/Messages_ja.properties +893 -0
  156. package/translations/Messages_nl.properties +1357 -0
  157. package/translations/Messages_pt.properties +1302 -0
  158. package/translations/Messages_ru.properties +1 -0
  159. package/translations/Messages_uz.properties +1 -0
  160. package/translations/Messages_zh.properties +1 -0
  161. package/translations/rendering-phrases.properties +1128 -0
  162. package/translations/rendering-phrases_ar.properties +1091 -0
  163. package/translations/rendering-phrases_de.properties +6 -0
  164. package/translations/rendering-phrases_es.properties +6 -0
  165. package/translations/rendering-phrases_fr.properties +624 -0
  166. package/translations/rendering-phrases_ja.properties +21 -0
  167. package/translations/rendering-phrases_nl.properties +970 -0
  168. package/translations/rendering-phrases_pt.properties +1020 -0
  169. package/translations/rendering-phrases_ru.properties +1094 -0
  170. package/translations/rendering-phrases_uz.properties +1 -0
  171. package/translations/rendering-phrases_zh.properties +1 -0
  172. package/tx/README.md +418 -0
  173. package/tx/cm/cm-api.js +110 -0
  174. package/tx/cm/cm-database.js +735 -0
  175. package/tx/cm/cm-package.js +325 -0
  176. package/tx/cs/cs-api.js +789 -0
  177. package/tx/cs/cs-areacode.js +615 -0
  178. package/tx/cs/cs-country.js +1110 -0
  179. package/tx/cs/cs-cpt.js +785 -0
  180. package/tx/cs/cs-cs.js +1579 -0
  181. package/tx/cs/cs-currency.js +539 -0
  182. package/tx/cs/cs-db.js +1321 -0
  183. package/tx/cs/cs-hgvs.js +329 -0
  184. package/tx/cs/cs-lang.js +465 -0
  185. package/tx/cs/cs-loinc.js +1485 -0
  186. package/tx/cs/cs-mimetypes.js +238 -0
  187. package/tx/cs/cs-ndc.js +704 -0
  188. package/tx/cs/cs-omop.js +1025 -0
  189. package/tx/cs/cs-provider-api.js +43 -0
  190. package/tx/cs/cs-provider-list.js +37 -0
  191. package/tx/cs/cs-rxnorm.js +808 -0
  192. package/tx/cs/cs-snomed.js +1102 -0
  193. package/tx/cs/cs-ucum.js +514 -0
  194. package/tx/cs/cs-unii.js +271 -0
  195. package/tx/cs/cs-uri.js +218 -0
  196. package/tx/cs/cs-usstates.js +305 -0
  197. package/tx/dev.fhir.org.yml +14 -0
  198. package/tx/fixtures/test-cases-setup.json +18 -0
  199. package/tx/fixtures/test-cases.yml +16 -0
  200. package/tx/html/codesystem-operations.liquid +25 -0
  201. package/tx/html/home-metrics.liquid +247 -0
  202. package/tx/html/operations-form.liquid +148 -0
  203. package/tx/html/search-form.liquid +62 -0
  204. package/tx/html/tx-template.html +133 -0
  205. package/tx/html/valueset-operations.liquid +54 -0
  206. package/tx/importers/atc-to-fhir.js +316 -0
  207. package/tx/importers/import-loinc.module.js +1536 -0
  208. package/tx/importers/import-ndc.module.js +1088 -0
  209. package/tx/importers/import-rxnorm.module.js +898 -0
  210. package/tx/importers/import-sct.module.js +2457 -0
  211. package/tx/importers/import-unii.module.js +601 -0
  212. package/tx/importers/readme.md +453 -0
  213. package/tx/importers/subset-loinc.module.js +1081 -0
  214. package/tx/importers/subset-rxnorm.module.js +938 -0
  215. package/tx/importers/tx-import-base.js +351 -0
  216. package/tx/importers/tx-import-settings.js +310 -0
  217. package/tx/importers/tx-import.js +357 -0
  218. package/tx/library/canonical-resource.js +88 -0
  219. package/tx/library/capabilitystatement.js +292 -0
  220. package/tx/library/codesystem.js +774 -0
  221. package/tx/library/conceptmap.js +568 -0
  222. package/tx/library/designations.js +932 -0
  223. package/tx/library/errors.js +77 -0
  224. package/tx/library/extensions.js +117 -0
  225. package/tx/library/namingsystem.js +322 -0
  226. package/tx/library/operation-outcome.js +127 -0
  227. package/tx/library/parameters.js +105 -0
  228. package/tx/library/renderer.js +1559 -0
  229. package/tx/library/terminologycapabilities.js +418 -0
  230. package/tx/library/ucum-parsers.js +1029 -0
  231. package/tx/library/ucum-service.js +370 -0
  232. package/tx/library/ucum-types.js +1099 -0
  233. package/tx/library/valueset.js +543 -0
  234. package/tx/library.js +676 -0
  235. package/tx/ocl/cm-ocl.js +106 -0
  236. package/tx/ocl/cs-ocl.js +39 -0
  237. package/tx/ocl/vs-ocl.js +105 -0
  238. package/tx/operation-context.js +568 -0
  239. package/tx/params.js +613 -0
  240. package/tx/provider.js +403 -0
  241. package/tx/sct/ecl.js +1560 -0
  242. package/tx/sct/expressions.js +2077 -0
  243. package/tx/sct/structures.js +1396 -0
  244. package/tx/tx-html.js +1063 -0
  245. package/tx/tx.fhir.org.yml +39 -0
  246. package/tx/tx.js +927 -0
  247. package/tx/vs/vs-api.js +112 -0
  248. package/tx/vs/vs-database.js +786 -0
  249. package/tx/vs/vs-package.js +358 -0
  250. package/tx/vs/vs-vsac.js +366 -0
  251. package/tx/workers/batch-validate.js +129 -0
  252. package/tx/workers/batch.js +361 -0
  253. package/tx/workers/closure.js +32 -0
  254. package/tx/workers/expand.js +1845 -0
  255. package/tx/workers/lookup.js +407 -0
  256. package/tx/workers/metadata.js +467 -0
  257. package/tx/workers/operations.js +34 -0
  258. package/tx/workers/read.js +164 -0
  259. package/tx/workers/search.js +384 -0
  260. package/tx/workers/subsumes.js +334 -0
  261. package/tx/workers/translate.js +492 -0
  262. package/tx/workers/validate.js +2504 -0
  263. package/tx/workers/worker.js +904 -0
  264. package/tx/xml/capabilitystatement-xml.js +63 -0
  265. package/tx/xml/codesystem-xml.js +62 -0
  266. package/tx/xml/conceptmap-xml.js +65 -0
  267. package/tx/xml/namingsystem-xml.js +65 -0
  268. package/tx/xml/operationoutcome-xml.js +127 -0
  269. package/tx/xml/parameters-xml.js +312 -0
  270. package/tx/xml/terminologycapabilities-xml.js +64 -0
  271. package/tx/xml/valueset-xml.js +64 -0
  272. package/tx/xml/xml-base.js +603 -0
  273. package/vcl/vcl-parser.js +1098 -0
  274. package/vcl/vcl.js +253 -0
  275. package/windows-install.js +19 -0
  276. package/xig/xig-template.html +124 -0
  277. package/xig/xig.js +3049 -0
@@ -0,0 +1,467 @@
1
+ //
2
+ // Metadata Handler - Handles /metadata endpoint and $versions operation
3
+ //
4
+ // GET /metadata - Returns CapabilityStatement
5
+ // GET /metadata?mode=terminology - Returns TerminologyCapabilities
6
+ // GET /$versions - Returns supported FHIR versions
7
+ //
8
+
9
+ const {CapabilityStatement} = require("../library/capabilitystatement");
10
+ const {TerminologyCapabilities} = require("../library/terminologycapabilities");
11
+
12
+ /**
13
+ * Metadata handler for FHIR terminology server
14
+ * Used by TXModule to handle /metadata requests
15
+ */
16
+ class MetadataHandler {
17
+ host;
18
+
19
+ /**
20
+ * @param {Object} config - Server configuration
21
+ */
22
+ constructor(config = {}) {
23
+ this.config = config;
24
+ this.host = config.host;
25
+ }
26
+
27
+ /**
28
+ * Handle GET /metadata request
29
+ * @param {express.Request} req - Express request (with txEndpoint and txProvider attached)
30
+ * @param {express.Response} res - Express response
31
+ */
32
+ async handle(req, res) {
33
+ const mode = req.query.mode;
34
+ const endpoint = req.txEndpoint;
35
+ const provider = req.txProvider;
36
+
37
+ if (mode === 'terminology') {
38
+ this.logInfo = 'termcaps';
39
+ const tc = new TerminologyCapabilities(await this.buildTerminologyCapabilities(endpoint, provider));
40
+ return res.json(tc.toJSON(endpoint.fhirVersion));
41
+ }
42
+ this.logInfo = 'metadata';
43
+
44
+ // Default: return CapabilityStatement
45
+ const cs = new CapabilityStatement(this.buildCapabilityStatement(endpoint, provider));
46
+ return res.json(cs.toJSON(endpoint.fhirVersion));
47
+ }
48
+
49
+ /**
50
+ * Handle GET /$versions request
51
+ * @param {express.Request} req - Express request (with txEndpoint attached)
52
+ * @param {express.Response} res - Express response
53
+ */
54
+ handleVersions(req, res) {
55
+ const endpoint = req.txEndpoint;
56
+ const fhirVersion = this.getShortFhirVersion(endpoint.fhirVersion);
57
+
58
+ // Check Accept header to determine response format
59
+ const accept = req.get('Accept') || '';
60
+ const isFhirJson = accept.includes('application/fhir+json') ||
61
+ accept.includes('application/fhir+xml');
62
+
63
+ if (isFhirJson) {
64
+ // Return FHIR Parameters resource
65
+ return res.json({
66
+ resourceType: 'Parameters',
67
+ parameter: [
68
+ {
69
+ name: 'version',
70
+ valueCode: fhirVersion
71
+ },
72
+ {
73
+ name: 'default',
74
+ valueCode: fhirVersion
75
+ }
76
+ ]
77
+ });
78
+ } else {
79
+ // Return simple JSON
80
+ return res.json({
81
+ versions: [fhirVersion],
82
+ default: fhirVersion
83
+ });
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Get short FHIR version (e.g., "4.0" from "4.0.1" or "4.0")
89
+ * @param {string} version - FHIR version string
90
+ * @returns {string} Short version (X.0 format)
91
+ */
92
+ getShortFhirVersion(version) {
93
+ if (!version) return '4.0';
94
+
95
+ // If already short format (e.g., "4.0"), return as-is
96
+ const parts = String(version).split('.');
97
+ if (parts.length >= 2) {
98
+ return `${parts[0]}.${parts[1]}`;
99
+ }
100
+ return version;
101
+ }
102
+
103
+ /**
104
+ * Build CapabilityStatement for an endpoint
105
+ * @param {Object} endpoint - Endpoint info {path, fhirVersion, context}
106
+ * @param {Object} provider - Provider for code systems and resources
107
+ * @returns {Object} CapabilityStatement resource
108
+ */
109
+ buildCapabilityStatement(endpoint) {
110
+ const now = new Date().toISOString();
111
+ const fhirVersion = this.mapFhirVersion(endpoint.fhirVersion);
112
+ const baseUrl = this.config.baseUrl || `https://${this.host}${endpoint.path}`;
113
+ const serverVersion = this.config.serverVersion || '1.0.0';
114
+
115
+ return {
116
+ resourceType: 'CapabilityStatement',
117
+ id: this.config.id || 'FhirServer',
118
+ 'extension' : [
119
+ {
120
+ 'extension' : [
121
+ {
122
+ 'url' : 'definition',
123
+ 'valueCanonical' : 'http://hl7.org/fhir/uv/tx-tests/FeatureDefinition/test-version'
124
+ },
125
+ {
126
+ 'url' : 'value',
127
+ 'valueCode' : '1.8.0'
128
+ },
129
+ {
130
+ 'extension' : [
131
+ {
132
+ 'url' : 'name',
133
+ 'valueCode' : 'mode'
134
+ },
135
+ {
136
+ 'url' : 'value',
137
+ 'valueCode' : 'tx.fhir.org'
138
+ }
139
+ ],
140
+ 'url' : 'qualifier'
141
+ }
142
+ ],
143
+ 'url' : 'http://hl7.org/fhir/uv/application-feature/StructureDefinition/feature'
144
+ },
145
+ {
146
+ 'extension' : [
147
+ {
148
+ 'url' : 'definition',
149
+ 'valueCanonical' : 'http://hl7.org/fhir/uv/tx-ecosystem/FeatureDefinition/CodeSystemAsParameter'
150
+ },
151
+ {
152
+ 'url' : 'value',
153
+ 'valueBoolean' : true
154
+ }
155
+ ],
156
+ 'url' : 'http://hl7.org/fhir/uv/application-feature/StructureDefinition/feature'
157
+ }
158
+ ],
159
+ url: `${baseUrl}/CapabilityStatement/tx`,
160
+ version: `${fhirVersion}-${serverVersion}`,
161
+ name: this.config.name || 'FHIRTerminologyServer',
162
+ title: this.config.title || 'FHIR Terminology Server Conformance Statement',
163
+ status: 'active',
164
+ date: now,
165
+ contact: this.config.contact || [
166
+ {
167
+ telecom: [
168
+ {
169
+ system: 'other',
170
+ value: this.config.contactUrl || 'http://example.org/'
171
+ }
172
+ ]
173
+ }
174
+ ],
175
+ description: this.config.description || 'FHIR Terminology Server',
176
+ kind: 'instance',
177
+ instantiates: [
178
+ 'http://hl7.org/fhir/CapabilityStatement/terminology-server'
179
+ ],
180
+ software: {
181
+ name: this.config.softwareName || 'FHIR Terminology Server',
182
+ version: serverVersion,
183
+ releaseDate: this.config.releaseDate || now
184
+ },
185
+ implementation: {
186
+ description: `FHIR Server running at ${baseUrl}`,
187
+ url: baseUrl
188
+ },
189
+ fhirVersion: fhirVersion,
190
+ format: ['application/fhir+xml', 'application/fhir+json'],
191
+ rest: [
192
+ {
193
+ mode: 'server',
194
+ security: {
195
+ cors: true
196
+ },
197
+ resource: [
198
+ {
199
+ type: 'CodeSystem',
200
+ interaction: [
201
+ { code: 'read', documentation: 'Read a code system' },
202
+ { code: 'search-type', documentation: 'Search the code systems' }
203
+ ],
204
+ searchParam: [
205
+ { name: 'url', type: 'uri' },
206
+ { name: 'version', type: 'token' },
207
+ { name: 'name', type: 'string' },
208
+ { name: 'title', type: 'string' },
209
+ { name: 'status', type: 'token' },
210
+ { name: '_id', type: 'token' }
211
+ ],
212
+ operation: [
213
+ { name: 'lookup', definition: 'http://hl7.org/fhir/OperationDefinition/CodeSystem-lookup' },
214
+ { name: 'validate-code', definition: 'http://hl7.org/fhir/OperationDefinition/CodeSystem-validate-code' },
215
+ { name: 'subsumes', definition: 'http://hl7.org/fhir/OperationDefinition/CodeSystem-subsumes' }
216
+ ]
217
+ },
218
+ {
219
+ type: 'ValueSet',
220
+ interaction: [
221
+ { code: 'read', documentation: 'Read a ValueSet' },
222
+ { code: 'search-type', documentation: 'Search the value sets' }
223
+ ],
224
+ searchParam: [
225
+ { name: 'url', type: 'uri' },
226
+ { name: 'version', type: 'token' },
227
+ { name: 'name', type: 'string' },
228
+ { name: 'title', type: 'string' },
229
+ { name: 'status', type: 'token' },
230
+ { name: '_id', type: 'token' }
231
+ ],
232
+ operation: [
233
+ { name: 'expand', definition: 'http://hl7.org/fhir/OperationDefinition/ValueSet-expand' },
234
+ { name: 'validate-code', definition: 'http://hl7.org/fhir/OperationDefinition/ValueSet-validate-code' }
235
+ ]
236
+ },
237
+ {
238
+ type: 'ConceptMap',
239
+ interaction: [
240
+ { code: 'read', documentation: 'Read a ConceptMap' },
241
+ { code: 'search-type', documentation: 'Search the concept maps' }
242
+ ],
243
+ searchParam: [
244
+ { name: 'url', type: 'uri' },
245
+ { name: 'version', type: 'token' },
246
+ { name: 'name', type: 'string' },
247
+ { name: 'title', type: 'string' },
248
+ { name: 'status', type: 'token' },
249
+ { name: '_id', type: 'token' }
250
+ ],
251
+ operation: [
252
+ { name: 'translate', definition: 'http://hl7.org/fhir/OperationDefinition/ConceptMap-translate' },
253
+ { name: 'closure', definition: 'http://hl7.org/fhir/OperationDefinition/ConceptMap-closure' }
254
+ ]
255
+ }
256
+ ],
257
+ interaction: [
258
+ { code: 'transaction' }
259
+ ],
260
+ operation: [
261
+ { name: 'expand', definition: 'http://hl7.org/fhir/OperationDefinition/ValueSet-expand' },
262
+ { name: 'lookup', definition: 'http://hl7.org/fhir/OperationDefinition/CodeSystem-lookup' },
263
+ { name: 'subsumes', definition: 'http://hl7.org/fhir/OperationDefinition/CodeSystem-subsumes' },
264
+ { name: 'validate-code', definition: 'http://hl7.org/fhir/OperationDefinition/Resource-validate-code' },
265
+ { name: 'translate', definition: 'http://hl7.org/fhir/OperationDefinition/ConceptMap-translate' },
266
+ { name: 'closure', definition: 'http://hl7.org/fhir/OperationDefinition/ConceptMap-closure' },
267
+ { name: 'versions', definition: 'http://hl7.org/fhir/OperationDefinition/fhir-versions' }
268
+ ]
269
+ }
270
+ ]
271
+ };
272
+ }
273
+
274
+ /**
275
+ * Build TerminologyCapabilities resource
276
+ * @param {Object} endpoint - Endpoint info
277
+ * @param {Object} provider - Provider for code systems and resources
278
+ * @returns {Object} TerminologyCapabilities resource
279
+ */
280
+ async buildTerminologyCapabilities(endpoint, provider) {
281
+ const now = new Date().toISOString();
282
+ const baseUrl = this.config.baseUrl || `https://${this.host}${endpoint.path}`;
283
+ const serverVersion = this.config.serverVersion || '1.0.0';
284
+
285
+ const tc = {
286
+ resourceType: 'TerminologyCapabilities',
287
+ id: this.config.id || 'FhirServer',
288
+ url: `${baseUrl}/TerminologyCapabilities/tx`,
289
+ version: serverVersion,
290
+ name: this.config.name || 'FHIRTerminologyServerCapabilities',
291
+ title: this.config.title || 'FHIR Terminology Server Capability Statement',
292
+ status: 'active',
293
+ date: now,
294
+ contact: this.config.contact || [
295
+ {
296
+ telecom: [
297
+ {
298
+ system: 'other',
299
+ value: this.config.contactUrl || 'http://example.org/'
300
+ }
301
+ ]
302
+ }
303
+ ],
304
+ description: this.config.description || 'Terminology Capability Statement for FHIR Terminology Server',
305
+ kind: 'instance',
306
+ codeSystem: await this.buildCodeSystemEntries(provider),
307
+ expansion: this.buildExpansionCapabilities(),
308
+ validateCode: this.buildValidateCodeCapabilities(),
309
+ translation: this.buildTranslationCapabilities()
310
+ };
311
+
312
+ return tc;
313
+ }
314
+
315
+ /**
316
+ * Build codeSystem entries from provider
317
+ * @param {Object} provider - Provider with codeSystems and codeSystemFactories
318
+ * @returns {Object[]} Array of codeSystem entries
319
+ */
320
+ async buildCodeSystemEntries(provider) {
321
+ const seenSystems = new Map(); // url -> entry for deduplication
322
+
323
+ // Process provider.codeSystems (direct CodeSystem resources)
324
+ if (provider && provider.codeSystems) {
325
+ for (const cs of provider.codeSystems.values()) {
326
+ const url = cs.url || (cs.jsonObj && cs.jsonObj.url);
327
+ const version = cs.version || (cs.jsonObj && cs.jsonObj.version);
328
+
329
+ if (url) {
330
+ this.addCodeSystemEntry(seenSystems, url, version);
331
+ }
332
+ }
333
+ }
334
+
335
+ // Process provider.codeSystemFactories (factory providers)
336
+ if (provider && provider.codeSystemFactories) {
337
+ for (const factory of provider.codeSystemFactories.values()) {
338
+ const url = factory.system();
339
+ const version = factory.version();
340
+
341
+ if (url) {
342
+ this.addCodeSystemEntry(seenSystems, url, version);
343
+ }
344
+ }
345
+ }
346
+
347
+ // Convert map to array and sort by URI
348
+ const entries = Array.from(seenSystems.values());
349
+ entries.sort((a, b) => (a.uri || '').localeCompare(b.uri || ''));
350
+
351
+ return entries.length > 0 ? entries : undefined;
352
+ }
353
+
354
+ /**
355
+ * Add or update a code system entry
356
+ * @param {Map} seenSystems - Map of URL to entry
357
+ * @param {string} url - Code system URL
358
+ * @param {string} version - Code system version (may be null)
359
+ */
360
+ addCodeSystemEntry(seenSystems, url, version) {
361
+ if (!seenSystems.has(url)) {
362
+ // Create new entry
363
+ const entry = { uri: url };
364
+ if (version) {
365
+ entry.version = [{ code: version }];
366
+ }
367
+ seenSystems.set(url, entry);
368
+ } else if (version) {
369
+ // Add version to existing entry
370
+ const entry = seenSystems.get(url);
371
+ if (!entry.version) {
372
+ entry.version = [];
373
+ }
374
+ // Check if version already exists
375
+ if (!entry.version.some(v => v.code === version)) {
376
+ entry.version.push({ code: version });
377
+ }
378
+ }
379
+ }
380
+
381
+ /**
382
+ * Build expansion capabilities
383
+ * @returns {Object} Expansion capabilities object
384
+ */
385
+ buildExpansionCapabilities() {
386
+ return {
387
+ parameter: [
388
+ {
389
+ name: 'cache-id',
390
+ documentation: 'This server supports caching terminology resources between calls. Clients only need to send value sets and codesystems once; thereafter they are automatically in scope for calls with the same cache-id. The cache is retained for 30 min from last call'
391
+ },
392
+ {
393
+ name: 'tx-resource',
394
+ documentation: 'Additional valuesets needed for evaluation e.g. value sets referred to from the import statement of the value set being expanded'
395
+ },
396
+ { name: '_incomplete' },
397
+ { name: 'abstract' },
398
+ { name: 'activeOnly' },
399
+ { name: 'check-system-version' },
400
+ { name: 'count' },
401
+ { name: 'default-to-latest-version' },
402
+ { name: 'displayLanguage' },
403
+ { name: 'excludeNested' },
404
+ { name: 'excludeNotForUI' },
405
+ { name: 'excludePostCoordinated' },
406
+ { name: 'force-system-version' },
407
+ { name: 'inactive' },
408
+ { name: 'includeAlternateCodes' },
409
+ { name: 'includeDefinition' },
410
+ { name: 'includeDesignations' },
411
+ { name: 'incomplete-ok' },
412
+ { name: 'limitedExpansion' },
413
+ {
414
+ name: 'mode',
415
+ documentation: '=lenient-display-validation'
416
+ },
417
+ { name: 'no-cache' },
418
+ { name: 'offset' },
419
+ { name: 'profile' },
420
+ { name: 'property' },
421
+ { name: 'system-version' },
422
+ {
423
+ name: 'valueSetMode',
424
+ documentation: '= CHECK_MEMBERSHIP_ONLY | NO_MEMBERSHIP_CHECK'
425
+ }
426
+ ]
427
+ };
428
+ }
429
+
430
+ /**
431
+ * Build validateCode capabilities
432
+ * @returns {Object} ValidateCode capabilities object
433
+ */
434
+ buildValidateCodeCapabilities() {
435
+ return {
436
+ "translations" : true
437
+ };
438
+ }
439
+
440
+ /**
441
+ * Build translation capabilities
442
+ * @returns {Object} Translation capabilities object
443
+ */
444
+ buildTranslationCapabilities() {
445
+ return {
446
+ needsMap : false
447
+ };
448
+ }
449
+
450
+ /**
451
+ * Map short FHIR version to full version string
452
+ * @param {string} version - Short version (e.g., '4.0')
453
+ * @returns {string} Full version (e.g., '4.0.1')
454
+ */
455
+ mapFhirVersion(version) {
456
+ const versionMap = {
457
+ '3.0': '3.0.2',
458
+ '4.0': '4.0.1',
459
+ '4.3': '4.3.0',
460
+ '5.0': '5.0.0',
461
+ '6.0': '6.0.0'
462
+ };
463
+ return versionMap[version] || version || '4.0.1';
464
+ }
465
+ }
466
+
467
+ module.exports = { MetadataHandler };
@@ -0,0 +1,34 @@
1
+
2
+ const {TerminologyWorker} = require("./worker");
3
+
4
+ /**
5
+ * this handles assembling the information for the operations form
6
+ */
7
+ class OperationsWorker extends TerminologyWorker {
8
+ /**
9
+ * @param {OperationContext} opContext - Operation context
10
+ * @param {Logger} log - Logger instance
11
+ * @param {Provider} provider - Provider for code systems and resources
12
+ * @param {LanguageDefinitions} languages - Language definitions
13
+ * @param {I18nSupport} i18n - Internationalization support
14
+ */
15
+ constructor(opContext, log, provider, languages, i18n) {
16
+ super(opContext, log, provider, languages, i18n);
17
+ }
18
+
19
+ /**
20
+ * Get operation name
21
+ * @returns {string}
22
+ */
23
+ opName() {
24
+ return 'search';
25
+ }
26
+
27
+ async handle(req, res) {
28
+ const formData = { resourceType : "Operations" };
29
+ formData.valueSets = await this.provider.listAllValueSets();
30
+ return res.json(formData);
31
+ }
32
+ }
33
+
34
+ module.exports = { OperationsWorker };
@@ -0,0 +1,164 @@
1
+ //
2
+ // Read Worker - Handles resource read operations
3
+ //
4
+ // GET /{type}/{id}
5
+ //
6
+
7
+ const { TerminologyWorker } = require('./worker');
8
+
9
+ class ReadWorker extends TerminologyWorker {
10
+ /**
11
+ * @param {OperationContext} opContext - Operation context
12
+ * @param {Logger} log - Logger instance
13
+ * @param {Provider} provider - Provider for code systems and resources
14
+ * @param {LanguageDefinitions} languages - Language definitions
15
+ * @param {I18nSupport} i18n - Internationalization support
16
+ */
17
+ constructor(opContext, log, provider, languages, i18n) {
18
+ super(opContext, log, provider, languages, i18n);
19
+ }
20
+
21
+ /**
22
+ * Get operation name
23
+ * @returns {string}
24
+ */
25
+ opName() {
26
+ return 'read';
27
+ }
28
+ /**
29
+ * Handle a read request
30
+ * @param {express.Request} req - Express request (with txProvider attached)
31
+ * @param {express.Response} res - Express response
32
+ * @param {string} resourceType - The resource type (CodeSystem, ValueSet, ConceptMap)
33
+ * @param {Object} log - Logger instance
34
+ */
35
+ async handle(req, res, resourceType) {
36
+ const { id } = req.params;
37
+
38
+ this.log.debug(`Read ${resourceType}/${id}`);
39
+
40
+ try {
41
+ switch (resourceType) {
42
+ case 'CodeSystem':
43
+ return await this.handleCodeSystem(req, res, id);
44
+
45
+ case 'ValueSet':
46
+ return await this.handleValueSet(req, res, id);
47
+
48
+ case 'ConceptMap':
49
+ return res.status(501).json({
50
+ resourceType: 'OperationOutcome',
51
+ issue: [{
52
+ severity: 'error',
53
+ code: 'not-supported',
54
+ diagnostics: 'ConceptMap read not yet implemented'
55
+ }]
56
+ });
57
+
58
+ default:
59
+ return res.status(404).json({
60
+ resourceType: 'OperationOutcome',
61
+ issue: [{
62
+ severity: 'error',
63
+ code: 'not-found',
64
+ diagnostics: `Unknown resource type: ${resourceType}`
65
+ }]
66
+ });
67
+ }
68
+ } catch (error) {
69
+ req.logInfo = this.usedSources.join("|")+" - error"+(error.msgId ? " "+error.msgId : "");
70
+ this.log.error(error);
71
+ return res.status(500).json({
72
+ resourceType: 'OperationOutcome',
73
+ issue: [{
74
+ severity: 'error',
75
+ code: 'exception',
76
+ diagnostics: error.message
77
+ }]
78
+ });
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Handle CodeSystem read
84
+ */
85
+ async handleCodeSystem(req, res, id) {
86
+ let cs = this.provider.getCodeSystemById(this.opContext, id);
87
+ if (cs != null) {
88
+ return res.json(cs.jsonObj);
89
+ }
90
+
91
+ if (id.startsWith("x-")) {
92
+ cs = this.provider.getCodeSystemFactoryById(this.opContext, id.substring(2));
93
+ if (cs != null) {
94
+ let json = {
95
+ resourceType: "CodeSystem",
96
+ id: "x-" + cs.id(),
97
+ url: cs.system(),
98
+ name: cs.name(),
99
+ status: "active",
100
+ description: "This is a place holder for the code system which is fully supported through internal means (not by this code system)",
101
+ content: "not-present"
102
+ }
103
+ if (cs.version()) {
104
+ json.version = cs.version();
105
+ }
106
+ if (cs.iteratable()) {
107
+ json.content = "conplete",
108
+ json.concept = [];
109
+ let csp = cs.build(this.opContext, []);
110
+ let iter = await csp.iteratorAll();
111
+ let c = await csp.nextContext(iter);
112
+ while (c) {
113
+ let cc = {
114
+ code: await csp.code(c),
115
+ display: await csp.display(c)
116
+ }
117
+ let def = await csp.definition(c);
118
+ if (def) {
119
+ cc.definition = def;
120
+ }
121
+ json.concept.push(cc);
122
+ c = await csp.nextContext(iter);
123
+ }
124
+
125
+ }
126
+ return res.json(json);
127
+ }
128
+ }
129
+
130
+ return res.status(404).json({
131
+ resourceType: 'OperationOutcome',
132
+ issue: [{
133
+ severity: 'error',
134
+ code: 'not-found',
135
+ diagnostics: `CodeSystem/${id} not found`
136
+ }]
137
+ });
138
+ }
139
+
140
+ /**
141
+ * Handle ValueSet read
142
+ */
143
+ async handleValueSet(req, res, id) {
144
+ // Iterate through valueSetProviders in order
145
+ for (const vsp of this.provider.valueSetProviders) {
146
+ this.deadCheck('handleValueSet-loop');
147
+ const vs = await vsp.fetchValueSetById(id);
148
+ if (vs) {
149
+ return res.json(vs.jsonObj);
150
+ }
151
+ }
152
+
153
+ return res.status(404).json({
154
+ resourceType: 'OperationOutcome',
155
+ issue: [{
156
+ severity: 'error',
157
+ code: 'not-found',
158
+ diagnostics: `ValueSet/${id} not found`
159
+ }]
160
+ });
161
+ }
162
+ }
163
+
164
+ module.exports = ReadWorker;