fhirsmith 0.18.1 → 1.0.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 (214) hide show
  1. package/ACCESSIBILITY.md +119 -0
  2. package/CHANGELOG.md +190 -9
  3. package/CONFIGURATION.md +305 -0
  4. package/LICENSE +29 -0
  5. package/README.md +29 -122
  6. package/cluster.js +1220 -0
  7. package/cluster.md +707 -0
  8. package/config-template.json +77 -8
  9. package/extension-tracker/extension-tracker-template.html +5 -8
  10. package/extension-tracker/extension-tracker.js +0 -2
  11. package/folder/folder.js +7 -6
  12. package/ips/analysis.js +560 -0
  13. package/ips/ips-template.html +5 -8
  14. package/ips/ips.js +331 -33
  15. package/ips/jurisdictions.js +65 -0
  16. package/ips/nationality.js +120 -0
  17. package/ips/pipeline.js +131 -17
  18. package/ips/presenter.js +1 -1
  19. package/ips/profile-editor.js +283 -4
  20. package/ips/readme.md +170 -20
  21. package/ips/render.js +769 -109
  22. package/ips/scripts.js +46 -0
  23. package/ips/store.js +123 -11
  24. package/ips/structure.js +247 -0
  25. package/ips/terminology.js +1 -1
  26. package/library/body-limit.js +22 -0
  27. package/library/cache-generation.js +15 -0
  28. package/library/cgroup-memory.js +133 -1
  29. package/library/cluster-counts.js +121 -0
  30. package/library/cluster-events.js +153 -0
  31. package/library/cluster-metrics.js +193 -0
  32. package/library/cluster-status.js +468 -0
  33. package/library/config-reader.js +96 -0
  34. package/library/daily-report.js +100 -2
  35. package/library/html-server.js +27 -1
  36. package/library/i18nsupport.js +23 -1
  37. package/library/keep-alive.js +18 -0
  38. package/library/languages.js +17 -3
  39. package/library/logger.js +31 -0
  40. package/library/memory-guard.js +428 -0
  41. package/library/native-memory.js +52 -0
  42. package/library/npm-audit.js +51 -25
  43. package/library/openapi-doc.js +7 -5
  44. package/library/package-manager.js +8 -0
  45. package/library/role.js +197 -0
  46. package/library/server-credentials.js +169 -0
  47. package/library/sqlite-utilities.js +59 -0
  48. package/library/ssrf-guard.js +100 -0
  49. package/library/static-response-stats.js +16 -0
  50. package/library/version-utilities.js +50 -0
  51. package/library/worker-count.js +192 -0
  52. package/nginx.md +30 -11
  53. package/npmprojector/indexer.js +18 -10
  54. package/package.json +11 -8
  55. package/packages/feed-check-page.js +117 -0
  56. package/packages/feed-check.js +279 -0
  57. package/packages/npm-check.js +57 -3
  58. package/packages/npm-signing.js +219 -0
  59. package/packages/openapi.yaml +207 -1
  60. package/packages/package-crawler.js +322 -41
  61. package/packages/packages-template.html +6 -8
  62. package/packages/packages.js +604 -57
  63. package/packages/readme.md +113 -1
  64. package/publisher/publisher-template.html +2 -5
  65. package/publisher/publisher.js +121 -110
  66. package/publisher/readme.md +75 -0
  67. package/registry/api.js +179 -1
  68. package/registry/crawler.js +86 -108
  69. package/registry/documentation/api.md +68 -2
  70. package/registry/documentation/configuration.md +34 -0
  71. package/registry/documentation/internals.md +4 -1
  72. package/registry/documentation/readme.md +3 -2
  73. package/registry/model.js +8 -0
  74. package/registry/openapi.yaml +84 -0
  75. package/registry/registry-template.html +5 -8
  76. package/registry/registry.js +58 -6
  77. package/root-bare-template.html +7 -4
  78. package/root-template.html +5 -8
  79. package/server.js +398 -198
  80. package/shl/shl.js +38 -37
  81. package/static/a11y/a11y-check.js +832 -0
  82. package/static/a11y/axe.min.js +12 -0
  83. package/static/assets/css/bootstrap-fhir.css +15 -15
  84. package/static/assets/images/flags/au.svg +18 -0
  85. package/static/assets/images/flags/unknown.svg +11 -0
  86. package/static/assets/images/flags/us.svg +63 -0
  87. package/static/assets/images/icon_audit.png +0 -0
  88. package/static/assets/images/icon_clinical.png +0 -0
  89. package/static/assets/images/icon_document.png +0 -0
  90. package/static/assets/images/icon_external.png +0 -0
  91. package/static/assets/images/icon_medication.png +0 -0
  92. package/static/assets/images/icon_organization.png +0 -0
  93. package/static/assets/images/icon_other.png +0 -0
  94. package/static/assets/images/icon_person.png +0 -0
  95. package/static/assets/images/icon_section.png +0 -0
  96. package/stats-db.js +45 -0
  97. package/stats.js +197 -10
  98. package/testing/testing-template.html +5 -8
  99. package/token/nginx_passport_setup.md +1 -1
  100. package/token/security_guide.md +1 -1
  101. package/token/token-template.html +2 -5
  102. package/token/token.js +40 -36
  103. package/translations/Messages.properties +19 -0
  104. package/tx/README.md +158 -4
  105. package/tx/closure/closure-store.js +126 -3
  106. package/tx/cm/cm-database.js +69 -57
  107. package/tx/cm/cm-package.js +50 -16
  108. package/tx/cs/cs-api.js +284 -19
  109. package/tx/cs/cs-api.md +41 -4
  110. package/tx/cs/cs-areacode.js +1 -1
  111. package/tx/cs/cs-country.js +17 -5
  112. package/tx/cs/cs-cpt.js +33 -16
  113. package/tx/cs/cs-cs.js +199 -24
  114. package/tx/cs/cs-currency.js +1 -1
  115. package/tx/cs/cs-icd11.js +21 -13
  116. package/tx/cs/cs-icf.js +22 -3
  117. package/tx/cs/cs-lang.js +2 -2
  118. package/tx/cs/cs-loinc.js +193 -67
  119. package/tx/cs/cs-mimetypes.js +12 -2
  120. package/tx/cs/cs-ndc.js +40 -25
  121. package/tx/cs/cs-omop.js +54 -29
  122. package/tx/cs/cs-rxnorm.js +300 -196
  123. package/tx/cs/cs-snomed.js +861 -95
  124. package/tx/cs/cs-ucum.js +14 -4
  125. package/tx/cs/cs-unii.js +18 -14
  126. package/tx/cs/cs-virtual.js +210 -0
  127. package/tx/ecosystem-resolver.js +413 -0
  128. package/tx/html/cluster-dash-metrics.liquid +26 -0
  129. package/tx/html/cluster-home-metrics.liquid +31 -0
  130. package/tx/html/cluster-metrics-script.liquid +251 -0
  131. package/tx/html/codesystem-operations.liquid +3 -3
  132. package/tx/html/ecl-panel.liquid +1 -1
  133. package/tx/html/external-metrics.liquid +17 -0
  134. package/tx/html/operations-form.liquid +17 -17
  135. package/tx/html/search-form.liquid +11 -11
  136. package/tx/html/status-line.liquid +58 -0
  137. package/tx/html/status-panel.liquid +140 -0
  138. package/tx/html/status-tasks.liquid +25 -0
  139. package/tx/html/tx-template.html +2 -5
  140. package/tx/importers/atc-to-fhir.js +17 -16
  141. package/tx/importers/export-icd11-codesystem.js +3 -2
  142. package/tx/importers/import-icd11.js +4 -3
  143. package/tx/importers/import-loinc.module.js +118 -56
  144. package/tx/importers/import-ndc.module.js +56 -44
  145. package/tx/importers/import-rxnorm.module.js +118 -79
  146. package/tx/importers/import-sct.module.js +125 -61
  147. package/tx/importers/import-unii.module.js +37 -36
  148. package/tx/importers/subset-loinc.module.js +49 -48
  149. package/tx/importers/subset-rxnorm.module.js +29 -28
  150. package/tx/importers/terminal.js +18 -0
  151. package/tx/importers/tx-import-base.js +13 -12
  152. package/tx/importers/tx-import-settings.js +5 -4
  153. package/tx/importers/tx-import.js +53 -52
  154. package/tx/library/canonical-resource.js +49 -0
  155. package/tx/library/codesystem.js +94 -5
  156. package/tx/library/designations.js +45 -2
  157. package/tx/library/extensions.js +13 -7
  158. package/tx/library/implicit-valuesets.js +66 -0
  159. package/tx/library/renderer.js +77 -21
  160. package/tx/library/sqlite-utilities.js +2 -0
  161. package/tx/library/tho-precedence.js +40 -0
  162. package/tx/library/ucum-parsers.js +11 -1
  163. package/tx/library/wait-for-cache.js +43 -0
  164. package/tx/library.js +321 -22
  165. package/tx/openapi-operations.js +6 -4
  166. package/tx/operation-context.js +460 -99
  167. package/tx/params.js +25 -0
  168. package/tx/problems.js +3 -5
  169. package/tx/provider.js +314 -67
  170. package/tx/resource-cache-sqlite.js +1050 -0
  171. package/tx/sct/documentation.md +45 -21
  172. package/tx/sct/ecl.js +81 -17
  173. package/tx/sct/editions.js +39 -0
  174. package/tx/sct/expressions.js +10 -1
  175. package/tx/sct/load-config.js +107 -0
  176. package/tx/sct/mmap.js +109 -0
  177. package/tx/sct/structures.js +175 -15
  178. package/tx/sct/text-index.js +5 -1
  179. package/tx/syndication/syndication-feed.js +2 -2
  180. package/tx/tx-html.js +44 -8
  181. package/tx/tx.fhir.org.yml +8 -8
  182. package/tx/tx.js +282 -80
  183. package/tx/usage-tracker.js +39 -11
  184. package/tx/vs/vs-api.js +19 -0
  185. package/tx/vs/vs-database.js +386 -62
  186. package/tx/vs/vs-package.js +44 -17
  187. package/tx/vs/vs-vsac.js +649 -101
  188. package/tx/workers/batch-validate.js +37 -10
  189. package/tx/workers/batch.js +6 -1
  190. package/tx/workers/cache-control.js +15 -4
  191. package/tx/workers/closure.js +156 -13
  192. package/tx/workers/compare.js +8 -5
  193. package/tx/workers/expand.js +1173 -289
  194. package/tx/workers/metadata.js +35 -0
  195. package/tx/workers/search.js +53 -1
  196. package/tx/workers/supplement-filters.js +145 -0
  197. package/tx/workers/translate.js +169 -4
  198. package/tx/workers/validate.js +223 -119
  199. package/tx/workers/worker.js +123 -31
  200. package/tx/xversion/xv-valueset.js +7 -4
  201. package/utilities/submit-test-report.js +3 -1
  202. package/utilities/tx-stress-test.js +482 -0
  203. package/vcl/readme.md +37 -0
  204. package/vcl/vcl-parser.js +15 -0
  205. package/xig/readme.md +52 -0
  206. package/xig/xig-template.html +5 -8
  207. package/xig/xig.js +238 -422
  208. package/changelog-template.md +0 -22
  209. package/registry/test-runner.js +0 -237
  210. package/release.sh +0 -80
  211. package/test-scripts/repro-re2-wasm-leak.js +0 -34
  212. package/tsconfig.eslint.json +0 -9
  213. package/tx/html/dash-metrics.liquid +0 -147
  214. package/tx/html/home-metrics.liquid +0 -225
package/registry/model.js CHANGED
@@ -362,6 +362,8 @@ class ServerRegistries {
362
362
  constructor() {
363
363
  this.address = '';
364
364
  this.doco = '';
365
+ this.registration = ''; // URL: how to register your own server in this ecosystem
366
+ this.ecosystemDoco = ''; // URL: documentation for this ecosystem
365
367
  this.lastRun = null; // Date object
366
368
  this.outcome = '';
367
369
  this.registries = []; // Array of ServerRegistry
@@ -386,6 +388,8 @@ class ServerRegistries {
386
388
  this.lastRun = source.lastRun;
387
389
  this.outcome = source.outcome;
388
390
  this.doco = source.doco;
391
+ this.registration = source.registration;
392
+ this.ecosystemDoco = source.ecosystemDoco;
389
393
 
390
394
  source.registries.forEach(sourceRegistry => {
391
395
  const existing = this.getRegistry(sourceRegistry.code);
@@ -402,6 +406,8 @@ class ServerRegistries {
402
406
  version: '1',
403
407
  address: this.address,
404
408
  doco: this.doco,
409
+ registration: this.registration,
410
+ 'ecosystem-documentation': this.ecosystemDoco,
405
411
  'last-run': this.lastRun?.toISOString() || '',
406
412
  outcome: this.outcome,
407
413
  registries: this.registries.map(r => r.toJSON())
@@ -416,6 +422,8 @@ class ServerRegistries {
416
422
  const instance = new ServerRegistries();
417
423
  instance.address = json.address || '';
418
424
  instance.doco = json.doco || '';
425
+ instance.registration = json.registration || '';
426
+ instance.ecosystemDoco = json['ecosystem-documentation'] || '';
419
427
  instance.lastRun = json['last-run'] ? new Date(json['last-run']) : null;
420
428
  instance.outcome = json.outcome || '';
421
429
  instance.registries = (json.registries || []).map(r => ServerRegistry.fromJSON(r));
@@ -25,6 +25,9 @@ info:
25
25
  * **Resolution** (`GET /resolve`): which server should be used for this code system
26
26
  or value set?
27
27
 
28
+ It also offers **server choice** (`GET /choose-server`): given all the code systems a
29
+ request involves, and the client's default server, which one server should it go to?
30
+
28
31
  Servers can claim to be **authoritative** for code systems and value sets (by URL
29
32
  mask) in their registration. A server that hosts the content but makes no claim is a
30
33
  **candidate**. A server is only ever returned for content it actually hosts.
@@ -182,6 +185,77 @@ paths:
182
185
  schema: { $ref: '#/components/schemas/Error' }
183
186
  '401': { $ref: '#/components/responses/Unauthorized' }
184
187
 
188
+ /choose-server:
189
+ get:
190
+ tags: [resolution]
191
+ operationId: chooseServer
192
+ summary: Choose one server for a request involving several code systems
193
+ description: |
194
+ For clients that aren't the HL7 Java tooling: the same choice the Java tooling makes
195
+ (TerminologyClientManager.chooseServer), made from the registry's own data. Returns
196
+ the server to send the request to, and a log of how it was chosen.
197
+
198
+ The client works out the code systems the request involves - from the codings, and
199
+ the value set's includes and excludes, following imported value sets - and applies
200
+ any versions fixed by expansion parameters. If it can't follow an import, it should
201
+ use its default server without asking.
202
+
203
+ In order, the choice is:
204
+
205
+ 1. a server authoritative for all the code systems
206
+ 2. a server authoritative for one, and authoritative or a candidate for the others
207
+ 3. for several code systems including SNOMED CT, the choice for SNOMED CT alone; for
208
+ a code system tx.fhir.org handles specially (LOINC, UCUM, RxNorm ...), the default
209
+ 4. a server that is a candidate for all of them, and reports a usable (complete or
210
+ fragment) copy of each, or no content mode at all
211
+ 5. the server authoritative for the most of them
212
+ 6. the server the value set comes from (resolved by `valueSet`), else the default
213
+
214
+ In 2 and 4, a code system that no server hosts doesn't count against a server.
215
+ parameters:
216
+ - name: fhirVersion
217
+ in: query
218
+ required: true
219
+ description: The FHIR version, as a release code (`R4`, `R4B`, `R5` ...) or a version (`4.0.1`, `4.0`).
220
+ schema: { type: string }
221
+ example: R4
222
+ - name: system
223
+ in: query
224
+ description: |
225
+ A code system the request involves, `url` or `url|version`. Repeat for each one
226
+ (`system=a&system=b`). With none, the answer is the default server.
227
+ schema: { type: array, items: { type: string } }
228
+ style: form
229
+ explode: true
230
+ example: [http://loinc.org, http://snomed.info/sct|http://snomed.info/sct/32506021000036107]
231
+ - name: default
232
+ in: query
233
+ required: true
234
+ description: The client's default terminology server (the one it would use without the ecosystem).
235
+ schema: { type: string, format: uri }
236
+ example: http://tx.fhir.org/r4
237
+ - name: valueSet
238
+ in: query
239
+ description: The value set the request is about, `url` or `url|version`, if there is one. Used for the log, and in step 6.
240
+ schema: { type: string }
241
+ - $ref: '#/components/parameters/language'
242
+ - name: usage
243
+ in: query
244
+ description: What the client is doing, for servers that restrict their use (as for `/resolve`).
245
+ schema: { type: string }
246
+ responses:
247
+ '200':
248
+ description: The chosen server
249
+ content:
250
+ application/json:
251
+ schema: { $ref: '#/components/schemas/ChooseServerResult' }
252
+ '400':
253
+ description: A required parameter is missing, or a URL is malformed
254
+ content:
255
+ application/json:
256
+ schema: { $ref: '#/components/schemas/Error' }
257
+ '401': { $ref: '#/components/responses/Unauthorized' }
258
+
185
259
  components:
186
260
  securitySchemes:
187
261
  bearer:
@@ -311,6 +385,16 @@ components:
311
385
  type: array
312
386
  items: { $ref: '#/components/schemas/ResolvedServer' }
313
387
 
388
+ ChooseServerResult:
389
+ type: object
390
+ required: [server, log]
391
+ properties:
392
+ server: { type: string, format: uri, description: The FHIR endpoint to send the request to }
393
+ log:
394
+ type: array
395
+ items: { type: string }
396
+ description: How the server was chosen - what the registry says about each code system, then the rule that decided. For people, not for parsing
397
+
314
398
  ResolvedServer:
315
399
  allOf:
316
400
  - $ref: '#/components/schemas/SecurityFlags'
@@ -55,8 +55,6 @@
55
55
  <div class="container"> <!-- container -->
56
56
  <div style="background-color: #ad1f2f; padding: 6px; color: white;"> <!-- container -->
57
57
 
58
- <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;
59
- <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;
60
58
  <a href="/" style="color: gold"> Server Home</a> &nbsp;|&nbsp;
61
59
  <a href="/tx-reg" style="color: gold">Registry Home</a> &nbsp;|&nbsp;
62
60
  <a href="/tx-reg/resolve" style="color: gold">Resolve</a> &nbsp;|&nbsp;
@@ -75,7 +73,7 @@
75
73
  <div class="inner-wrapper">
76
74
  <div class="col-9">
77
75
 
78
- <h2><img border="0" src="../FHIRsmith32.png" style="vertical-align: text-bottom"/> [%title%] </h2>
76
+ <h2><a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" title="FHIRsmith [%ver%]"><img border="0" src="/FHIRsmith32.png" alt="FHIRsmith Logo" style="vertical-align: text-bottom"/></a> [%title%] </h2>
79
77
 
80
78
  [%content%]
81
79
 
@@ -93,8 +91,7 @@
93
91
  <div class="container"> <!-- container -->
94
92
  <div class="inner-wrapper">
95
93
  <p>
96
- <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;
97
- <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;
94
+ <a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" style="color: gold"><img border="0" src="/FHIRsmith16.png" alt="FHIRsmith Logo" style="vertical-align: text-bottom"/> FHIRsmith</a> [%ver%] &copy; HealthIntersections.com.au 2023+ &nbsp;|&nbsp;
98
95
  Terminology Registry [%crawler-status%] &nbsp;|
99
96
  &nbsp; ([%ms%] ms)
100
97
  [%sponsorMessage%]
@@ -118,10 +115,10 @@
118
115
  ================================================== -->
119
116
  <!-- Placed at the end of the document so the pages load faster -->
120
117
  <!-- <script src="/assets/js/jquery.js"/> -->
121
- <script src="/assets/js/bootstrap.min.js"/>
122
- <script src="/assets/js/respond.min.js"/>
118
+ <script src="/assets/js/bootstrap.min.js"></script>
119
+ <script src="/assets/js/respond.min.js"></script>
123
120
 
124
- <script src="/assets/js/fhir.js"/>
121
+ <script src="/assets/js/fhir.js"></script>
125
122
 
126
123
  <!-- Analytics Below
127
124
  ================================================== -->
@@ -20,6 +20,7 @@ const { RegistryAuth } = require('./auth');
20
20
  // parameters are ignored) because existing ecosystem clients rely on that.
21
21
  const DISCOVERY_PARAMS = ['registry', 'server', 'fhirVersion', 'url', 'authoritativeOnly', 'language'];
22
22
  const RESOLVE_PARAMS = ['fhirVersion', 'url', 'version', 'valueSet', 'authoritativeOnly', 'language', 'usage'];
23
+ const CHOOSE_SERVER_PARAMS = ['fhirVersion', 'system', 'default', 'valueSet', 'usage', 'language'];
23
24
 
24
25
  // A link to a URL from a registry list or a crawled server: those are other people's
25
26
  // content, so only an http(s) URL is linked, and it is escaped
@@ -90,6 +91,7 @@ class RegistryModule {
90
91
  timeout: config.timeout || 30000,
91
92
  userAgent: config.userAgent || 'FHIRRegistryServer/1.0',
92
93
  apiKeys: config.apiKeys || {},
94
+ oauth: config.oauth || {},
93
95
  maxResponseMB: config.maxResponseMB,
94
96
  uptimeWindowDays: config.uptimeWindowDays,
95
97
  uptimeRetentionDays: config.uptimeRetentionDays
@@ -325,6 +327,8 @@ class RegistryModule {
325
327
  // Main registry page
326
328
  this.router.get('/', apiLimiter, apiGuard, session, this.handleMainPage.bind(this));
327
329
  this.router.get('/resolve', apiLimiter, apiGuard, session, this.handleResolveEndpoint.bind(this));
330
+ // JSON only - there's no page for a browser, so it's always guarded
331
+ this.router.get('/choose-server', apiLimiter, guard, this.handleChooseServerEndpoint.bind(this));
328
332
  this.router.get('/log', session, this.handleLogEndpoint.bind(this));
329
333
  this.router.get('/software', session, this.handleSoftwarePage.bind(this));
330
334
 
@@ -807,8 +811,15 @@ class RegistryModule {
807
811
  const lastRunDate = new Date(stats.lastRun);
808
812
  lastUpdatedText = `Last Updated: ${lastRunDate.toLocaleString()}`;
809
813
  }
810
- html += `<p>${lastUpdatedText}. <a href="https://github.com/FHIR/ig-registry/blob/master/tx-registry-doco.md">Register your own server</a>`+
811
- ` - see <a href="https://build.fhir.org/ig/HL7/fhir-tx-ecosystem-ig/ecosystem.html">Documentation</a></p>`;
814
+ // what goes here depends on the ecosystem, so it comes from the master registration file
815
+ html += `<p>${lastUpdatedText}.`;
816
+ if (data.registration) {
817
+ html += ` <a href="${safeHref(data.registration)}">Register your own server</a>`;
818
+ }
819
+ if (data.ecosystemDoco) {
820
+ html += `${data.registration ? ' -' : ''} see <a href="${safeHref(data.ecosystemDoco)}">Documentation</a>`;
821
+ }
822
+ html += '</p>';
812
823
 
813
824
  html += '<table class="grid">';
814
825
  html += '<thead><tr>';
@@ -1503,13 +1514,53 @@ class RegistryModule {
1503
1514
  }
1504
1515
  }
1505
1516
 
1517
+ /**
1518
+ * GET /choose-server: which one server to use for a request involving a set of code
1519
+ * systems (see RegistryAPI.chooseServer). system repeats; the other parameters take their
1520
+ * first value
1521
+ */
1522
+ handleChooseServerEndpoint(req, res) {
1523
+ const start = Date.now();
1524
+ try {
1525
+ const params = this._normalizeQueryParams(req.query);
1526
+ const {fhirVersion, valueSet, usage, language} = params;
1527
+ const defaultServer = params.default;
1528
+ const raw = req.query.system;
1529
+ const systems = (raw == null ? [] : Array.isArray(raw) ? raw : [raw]).map(String).filter(s => s);
1530
+
1531
+ if (!fhirVersion) {
1532
+ return res.status(400).json({error: 'A FHIR version is required'});
1533
+ }
1534
+ if (!defaultServer || !this._isValidUrl(defaultServer, ['http:', 'https:'])) {
1535
+ return res.status(400).json({error: 'A default server (an http or https URL) is required'});
1536
+ }
1537
+ for (const system of systems) {
1538
+ if (!this._isValidUrl(system.split('|')[0])) {
1539
+ return res.status(400).json({error: 'Invalid code system URL format'});
1540
+ }
1541
+ }
1542
+ if (valueSet && !this._isValidUrl(valueSet.split('|')[0])) {
1543
+ return res.status(400).json({error: 'Invalid value set URL format'});
1544
+ }
1545
+
1546
+ const result = this.api.chooseServer(fhirVersion, systems, defaultServer, valueSet, usage, language);
1547
+ this.logger.info(`Chose ${result.server} for ${systems.join(', ') || '(no systems)'} (FHIR ${fhirVersion}, usage=${usage}, language=${language || 'none'})`);
1548
+ res.json(result);
1549
+ } catch (error) {
1550
+ this.logger.error('Error in choose-server endpoint:', error);
1551
+ res.status(400).json({error: error.message});
1552
+ } finally {
1553
+ this.stats.countRequest('choose-server', Date.now() - start);
1554
+ }
1555
+ }
1556
+
1506
1557
  buildResolveResultContent(result, fhirVersion, resourceUrl, usage, language) {
1507
1558
  let html = '';
1508
1559
 
1509
1560
  // Query information section
1510
1561
  html += '<div class="card mb-4">';
1511
1562
  html += '<div class="card-header">';
1512
- html += '<h2 class="card-title">Query Information</h2>';
1563
+ html += '<h3 class="card-title">Query Information</h3>';
1513
1564
  html += '</div>';
1514
1565
  html += '<div class="card-body">';
1515
1566
  html += `<p><strong>FHIR Version:</strong> ${escape(fhirVersion)}</p>`;
@@ -1527,7 +1578,7 @@ class RegistryModule {
1527
1578
  // Authoritative servers section
1528
1579
  html += '<div class="card mb-4">';
1529
1580
  html += '<div class="card-header">';
1530
- html += '<h2 class="card-title">Authoritative Servers</h2>';
1581
+ html += '<h3 class="card-title">Authoritative Servers</h3>';
1531
1582
  html += '</div>';
1532
1583
  html += '<div class="card-body">';
1533
1584
 
@@ -1566,7 +1617,7 @@ class RegistryModule {
1566
1617
  // Candidate servers section
1567
1618
  html += '<div class="card mb-4">';
1568
1619
  html += '<div class="card-header">';
1569
- html += '<h2 class="card-title">Candidate Servers</h2>';
1620
+ html += '<h3 class="card-title">Candidate Servers</h3>';
1570
1621
  html += '</div>';
1571
1622
  html += '<div class="card-body">';
1572
1623
 
@@ -1817,7 +1868,7 @@ class RegistryModule {
1817
1868
  case 'error': levelStyle = 'color: #d9534f; font-weight: bold;'; break;
1818
1869
  case 'warn': levelStyle = 'color: #f0ad4e;'; break;
1819
1870
  case 'debug': levelStyle = 'color: #5cb85c;'; break;
1820
- default: levelStyle = 'color: #0275d8;'; // info
1871
+ default: levelStyle = 'color: #0056b3;'; // info
1821
1872
  }
1822
1873
 
1823
1874
  // Format: [time] [LEVEL] message
@@ -1833,6 +1884,7 @@ class RegistryModule {
1833
1884
 
1834
1885
  RegistryModule.DISCOVERY_PARAMS = DISCOVERY_PARAMS;
1835
1886
  RegistryModule.RESOLVE_PARAMS = RESOLVE_PARAMS;
1887
+ RegistryModule.CHOOSE_SERVER_PARAMS = CHOOSE_SERVER_PARAMS;
1836
1888
 
1837
1889
  module.exports = RegistryModule;
1838
1890
  module.exports.commonBaseUrl = commonBaseUrl;
@@ -71,9 +71,12 @@
71
71
 
72
72
  <body style="margin-left: 10px">
73
73
 
74
- <h2>[%title%] </h2>
74
+ <!-- the page's one h1, the size of an h2 as before -->
75
+ <h1 style="font-size:1.5em;margin:0.83em 0">[%title%] </h1>
75
76
 
77
+ <main>
76
78
  [%content%]
79
+ </main>
77
80
 
78
81
 
79
82
  <!-- JS and analytics only. -->
@@ -81,10 +84,10 @@
81
84
  ================================================== -->
82
85
  <!-- Placed at the end of the document so the pages load faster -->
83
86
  <!-- <script src="/assets/js/jquery.js"/> -->
84
- <script src="/assets/js/bootstrap.min.js"/>
85
- <script src="/assets/js/respond.min.js"/>
87
+ <script src="/assets/js/bootstrap.min.js"></script>
88
+ <script src="/assets/js/respond.min.js"></script>
86
89
 
87
- <script src="/assets/js/fhir.js"/>
90
+ <script src="/assets/js/fhir.js"></script>
88
91
 
89
92
  <!-- Analytics Below
90
93
  ================================================== -->
@@ -53,8 +53,6 @@
53
53
  <div id="stripe"> </div>
54
54
  <div class="container"> <!-- container -->
55
55
  <div style="background-color: #ad1f2f; padding: 6px; color: white;"> <!-- container -->
56
- <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;
57
- <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;
58
56
  <a href="/" style="color: gold"> Server Home</a>
59
57
  &nbsp;
60
58
  &nbsp;
@@ -70,7 +68,7 @@
70
68
  <div class="inner-wrapper">
71
69
  <div class="col-9">
72
70
 
73
- <h2><img border="0" src="/FHIRsmith32.png" style="vertical-align: text-bottom"/> FHIRsmith: [%title%] </h2>
71
+ <h2><a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" title="FHIRsmith [%ver%]"><img border="0" src="/FHIRsmith32.png" alt="FHIRsmith Logo" style="vertical-align: text-bottom"/></a> FHIRsmith: [%title%] </h2>
74
72
 
75
73
  [%content%]
76
74
 
@@ -89,8 +87,7 @@
89
87
  <div class="container"> <!-- container -->
90
88
  <div class="inner-wrapper">
91
89
  <p>
92
- <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;
93
- <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;|
90
+ <a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" style="color: gold"><img border="0" src="/FHIRsmith16.png" alt="FHIRsmith Logo" style="vertical-align: text-bottom"/> FHIRsmith</a> [%ver%] &copy; HealthIntersections.com.au 2023+ &nbsp;|
94
91
  &nbsp; ([%ms%] ms)
95
92
  [%sponsorMessage%]
96
93
  </p>
@@ -113,10 +110,10 @@
113
110
  ================================================== -->
114
111
  <!-- Placed at the end of the document so the pages load faster -->
115
112
  <!-- <script src="/assets/js/jquery.js"/> -->
116
- <script src="/assets/js/bootstrap.min.js"/>
117
- <script src="/assets/js/respond.min.js"/>
113
+ <script src="/assets/js/bootstrap.min.js"></script>
114
+ <script src="/assets/js/respond.min.js"></script>
118
115
 
119
- <script src="/assets/js/fhir.js"/>
116
+ <script src="/assets/js/fhir.js"></script>
120
117
 
121
118
  <!-- Analytics Below
122
119
  ================================================== -->