fhirsmith 0.9.2 → 0.9.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
@@ -5,6 +5,45 @@ All notable changes to the Health Intersections Node Server will be documented i
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [v0.9.4] - 2026-05-01
9
+
10
+ ### Added
11
+
12
+ - Draft MCP specification from Ontoserver
13
+
14
+ ### Changed
15
+
16
+ - Update Danish SNOMED CT Extension to 2026-03-31 version.
17
+
18
+ ### Fixed
19
+
20
+ - fix problem with version specific code system resolution
21
+ - fix hgvs handling of error response
22
+ - update NPM dependencies
23
+
24
+ ### Tx Conformance Statement
25
+
26
+ FHIRsmith passed all 1649 HL7 terminology service tests (modes tx.fhir.org+omop+general+snomed, tests v1.9.1, runner v6.9.7)
27
+
28
+ ## [v0.9.3] - 2026-04-10
29
+
30
+ ### Added
31
+
32
+ - Add support for handling contained value sets
33
+ - Add beta support for ECL
34
+
35
+ ### Changed
36
+
37
+ - Bump vsac fetch to 1000 and improve history presentation
38
+
39
+ ### Fixed
40
+
41
+ - Fix count on empty value set
42
+
43
+ ### Tx Conformance Statement
44
+
45
+ FHIRsmith passed all 1651 HL7 terminology service tests (modes tx.fhir.org+omop+general+snomed, tests v1.9.1, runner v6.9.6)
46
+
8
47
  ## [v0.9.2] - 2026-04-14
9
48
 
10
49
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fhirsmith",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "txVersion": "1.9.1",
5
5
  "description": "A Node.js server that provides a collection of tools to serve the FHIR ecosystem",
6
6
  "main": "server.js",
package/registry/api.js CHANGED
@@ -33,7 +33,8 @@ class RegistryAPI {
33
33
  const isAuth = codeSystem ? ServerRegistryUtilities.hasMatchingCodeSystem(
34
34
  codeSystem,
35
35
  server.authCSList,
36
- true // support wildcards
36
+ true, // support wildcards,
37
+ false // allow version matching
37
38
  ) : false;
38
39
 
39
40
  server.versions.forEach(versionInfo => {
@@ -398,12 +399,6 @@ class RegistryAPI {
398
399
  const matchedServers = [];
399
400
  const data = this.crawler.getData();
400
401
 
401
- // Extract base code system URL (before any pipe)
402
- let baseCodeSystem = codeSystem;
403
- if (codeSystem.includes('|')) {
404
- baseCodeSystem = codeSystem.substring(0, codeSystem.indexOf('|'));
405
- }
406
-
407
402
  data.registries.forEach(registry => {
408
403
  registry.servers.forEach(server => {
409
404
  let added = false;
@@ -421,9 +416,12 @@ class RegistryAPI {
421
416
  // Test against both the full URL and the base URL
422
417
  let content = {};
423
418
  const hasMatchingCS =
424
- ServerRegistryUtilities.hasMatchingCodeSystem(baseCodeSystem, version.codeSystems, false, content) ||
425
- (baseCodeSystem !== codeSystem &&
426
- ServerRegistryUtilities.hasMatchingCodeSystem(codeSystem, version.codeSystems, false, content));
419
+ // ServerRegistryUtilities.hasMatchingCodeSystem(baseCodeSystem, version.codeSystems, false, content) ||
420
+ // (baseCodeSystem !== codeSystem &&
421
+ ServerRegistryUtilities.hasMatchingCodeSystem(codeSystem, version.codeSystems, false, content,
422
+ // we don't want cross version matching at this point for SNOMED. If a version is specified, we want a match
423
+ // to be decided: what about other code systems?
424
+ codeSystem.includes("snomed"));
427
425
 
428
426
  if (hasMatchingCS) {
429
427
  if (isAuth) {
package/registry/model.js CHANGED
@@ -392,12 +392,12 @@ class ServerRegistryUtilities {
392
392
  return value === mask;
393
393
  }
394
394
 
395
- static hasMatchingCodeSystem(cs, list, supportMask, content) {
395
+ static hasMatchingCodeSystem(cs, list, supportMask, content, noVersionIndependentMatching = false) {
396
396
  if (!cs || list.length === 0) return false;
397
397
 
398
398
  // Handle URLs with pipes - extract base URL
399
399
  let baseCs = cs;
400
- if (cs.includes('|')) {
400
+ if (cs.includes('|') && !noVersionIndependentMatching) {
401
401
  baseCs = cs.substring(0, cs.indexOf('|'));
402
402
  }
403
403
 
@@ -1511,4 +1511,6 @@ CONFORMANCE_STATEMENT_WORD = The html source contains the word ''{0}'' but it is
1511
1511
  VALUESET_CODE_CONCEPT_HINT = {3}. Note that the display in the ValueSet does not have to match; this check exists to help check that it''s not accidentally the wrong code
1512
1512
  VALUESET_CODE_CONCEPT_HINT_VER ={3}. Note that the display in the ValueSet does not have to match; this check exists to help check that it''s not accidentally the wrong code
1513
1513
  TERMINOLOGY_TX_SYSTEM_UNSUPPORTED = The code cannot be checked because codeSystem ''{0}'' version ''{1}'' is not supported ({2})
1514
- INVALID_REGEX = The regex ''{0}'' is not valid: {1}
1514
+ INVALID_REGEX = The regex ''{0}'' is not valid: {1}
1515
+ INVALID_ECL = Invalid ECL expression: ''{0}'': ({1})
1516
+ UNSUPPORTED_ECL = The ECL expression is not supported: ''{0}'': ({1})
package/tx/cs/cs-api.js CHANGED
@@ -579,11 +579,12 @@ class CodeSystemProvider {
579
579
  * throws an exception if the search filter can't be handled
580
580
  *
581
581
  * @param {FilterExecutionContext} filterContext filtering context
582
+ * @param {boolean} forIteration - whether this filter is going to be iterated
582
583
  * @param {String} prop
583
584
  * @param {ValueSetFilterOperator} op
584
585
  * @param {String} prop
585
586
  **/
586
- async filter(filterContext, prop, op, value) { throw new Error("Must override"); } // well, only if any filters are actually supported
587
+ async filter(filterContext, forIteration, prop, op, value) { throw new Error("Must override"); } // well, only if any filters are actually supported
587
588
 
588
589
  /**
589
590
  * called once all the filters have been handled, and iteration is about to happen.
@@ -169,7 +169,7 @@ class AreaCodeServices extends CodeSystemProvider {
169
169
  return (prop === 'type' || prop === 'class') && op === '=';
170
170
  }
171
171
 
172
- async filter(filterContext, prop, op, value) {
172
+ async filter(filterContext, forIteration, prop, op, value) {
173
173
 
174
174
  assert(filterContext && filterContext instanceof FilterExecutionContext, 'filterContext must be a FilterExecutionContext');
175
175
  assert(prop != null && typeof prop === 'string', 'prop must be a non-null string');
@@ -188,7 +188,7 @@ class CountryCodeServices extends CodeSystemProvider {
188
188
  }
189
189
 
190
190
 
191
- async filter(filterContext, prop, op, value) {
191
+ async filter(filterContext, forIteration, prop, op, value) {
192
192
 
193
193
  assert(filterContext && filterContext instanceof FilterExecutionContext, 'filterContext must be a FilterExecutionContext');
194
194
  assert(prop != null && typeof prop === 'string', 'prop must be a non-null string');
package/tx/cs/cs-cpt.js CHANGED
@@ -490,7 +490,7 @@ class CPTServices extends BaseCSServices {
490
490
  return new CPTPrep(iterate);
491
491
  }
492
492
 
493
- async filter(filterContext, prop, op, value) {
493
+ async filter(filterContext, forIteration, prop, op, value) {
494
494
 
495
495
 
496
496
  let list;
package/tx/cs/cs-cs.js CHANGED
@@ -1217,7 +1217,7 @@ class FhirCodeSystemProvider extends BaseCSServices {
1217
1217
  * @param {string} value - Filter value
1218
1218
  * @returns {Promise<FhirCodeSystemProviderFilterContext>} Filter results
1219
1219
  */
1220
- async filter(filterContext, prop, op, value) {
1220
+ async filter(filterContext, forIteration, prop, op, value) {
1221
1221
 
1222
1222
 
1223
1223
  let results = null;
@@ -176,7 +176,7 @@ class Iso4217Services extends CodeSystemProvider {
176
176
  return prop === 'decimals' && op === 'equals';
177
177
  }
178
178
 
179
- async filter(filterContext, prop, op, value) {
179
+ async filter(filterContext, forIteration, prop, op, value) {
180
180
 
181
181
  assert(filterContext && filterContext instanceof FilterExecutionContext, 'filterContext must be a FilterExecutionContext');
182
182
  assert(prop != null && typeof prop === 'string', 'prop must be a non-null string');
package/tx/cs/cs-hgvs.js CHANGED
@@ -136,16 +136,24 @@ class HGVSServices extends CodeSystemProvider {
136
136
  let valid = false;
137
137
  let message = '';
138
138
 
139
- // Parse the FHIR Parameters response
140
- if (json.parameter && Array.isArray(json.parameter)) {
141
- for (const param of json.parameter) {
142
- if (param.name === 'result' && param.valueBoolean) {
143
- valid = true;
144
- } else if (param.name === 'message' && param.valueString) {
145
- if (message) message += ', ';
146
- message += param.valueString;
139
+ if (!json.resourceType) {
140
+ message = 'Invalid response format';
141
+ } else if (json.resourceType == 'OperationOutcome') {
142
+ message = json.issue?.[0]?.details?.text || 'Unknown error';
143
+ } else if (json.resourceType == 'Parameters') {
144
+ // Parse the FHIR Parameters response
145
+ if (json.parameter && Array.isArray(json.parameter)) {
146
+ for (const param of json.parameter) {
147
+ if (param.name === 'result' && param.valueBoolean) {
148
+ valid = true;
149
+ } else if (param.name === 'message' && param.valueString) {
150
+ if (message) message += ', ';
151
+ message += param.valueString;
152
+ }
147
153
  }
148
154
  }
155
+ } else {
156
+ message = 'Invalid response resource type: ' + json.resourceType;
149
157
  }
150
158
 
151
159
  resolve({ valid, message });
@@ -200,7 +208,7 @@ class HGVSServices extends CodeSystemProvider {
200
208
  throw new Error('Filters are not supported for HGVS');
201
209
  }
202
210
 
203
- async filter(filterContext, prop, op, value) {
211
+ async filter(filterContext, forIteration, prop, op, value) {
204
212
 
205
213
  throw new Error('Filters are not supported for HGVS');
206
214
  }
package/tx/cs/cs-lang.js CHANGED
@@ -259,7 +259,7 @@ class IETFLanguageCodeProvider extends CodeSystemProvider {
259
259
  return false;
260
260
  }
261
261
 
262
- async filter(filterContext, prop, op, value) {
262
+ async filter(filterContext, forIteration, prop, op, value) {
263
263
 
264
264
  assert(filterContext && filterContext instanceof FilterExecutionContext, 'filterContext must be a FilterExecutionContext');
265
265
  assert(prop != null && typeof prop === 'string', 'prop must be a non-null string');
package/tx/cs/cs-loinc.js CHANGED
@@ -658,7 +658,7 @@ class LoincServices extends BaseCSServices {
658
658
  return new LoincPrep(iterate);
659
659
  }
660
660
 
661
- async filter(filterContext, prop, op, value) {
661
+ async filter(filterContext, forIteration, prop, op, value) {
662
662
  const filter = new LoincFilterHolder();
663
663
  await this.#executeFilterQuery(prop, op, value, filter);
664
664
  filterContext.filters.push(filter);
package/tx/cs/cs-ndc.js CHANGED
@@ -405,7 +405,7 @@ class NdcServices extends CodeSystemProvider {
405
405
  ['10-digit', '11-digit', 'product'].includes(value);
406
406
  }
407
407
 
408
- async filter(filterContext, prop, op, value) {
408
+ async filter(filterContext, forIteration, prop, op, value) {
409
409
 
410
410
 
411
411
  if (prop === 'code-type' && op === '=') {
package/tx/cs/cs-omop.js CHANGED
@@ -549,7 +549,7 @@ class OMOPServices extends BaseCSServices {
549
549
  return new OMOPPrep(iterate);
550
550
  }
551
551
 
552
- async filter(filterContext, prop, op, value) {
552
+ async filter(filterContext, forIteration, prop, op, value) {
553
553
 
554
554
 
555
555
  if (prop === 'domain' && op === '=') {
@@ -366,7 +366,7 @@ class RxNormServices extends CodeSystemProvider {
366
366
  return new RxNormPrep();
367
367
  }
368
368
 
369
- async filter(filterContext, prop, op, value) {
369
+ async filter(filterContext, forIteration, prop, op, value) {
370
370
 
371
371
 
372
372
  const filter = new RxNormFilterHolder();