fhirsmith 0.10.0 → 0.10.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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,17 @@ 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
8
 
9
+ ## [v0.10.1] - 2026-06-27
10
+
11
+ ### Fixed
12
+
13
+ - Terminology: the version-less value set lookup now resolves to the latest version when several versions of the same value set are present (e.g. VSAC date versions), instead of an arbitrary one determined by database row order
14
+ - Rendering: fixed value set links in `renderLinkComma` (read the resolver's `description`/`link` fields and fall back to the raw URI)
15
+
16
+ ### Tx Conformance Statement
17
+
18
+ FHIRsmith passed all 2503 HL7 terminology service tests (modes tx.fhir.org+omop+general+snomed, tests v1.9.1, runner v6.9.11)
19
+
9
20
  ## [v0.10.0] - 2026-06-27
10
21
 
11
22
  ### Added
@@ -35,7 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
35
46
 
36
47
  ### Tx Conformance Statement
37
48
 
38
- FHIRsmith passed all 2503 HL7 terminology service tests (modes tx.fhir.org+omop+general+snomed, tests v1.9.1, runner v6.9.11
49
+ FHIRsmith passed all 2503 HL7 terminology service tests (modes tx.fhir.org+omop+general+snomed, tests v1.9.1, runner v6.9.11)
39
50
 
40
51
  ## [v0.9.7] - 2026-06-12
41
52
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fhirsmith",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
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",
@@ -340,12 +340,12 @@ class Renderer {
340
340
  }
341
341
  }
342
342
 
343
- renderLinkComma(x, uri) {
344
- let {desc, url} = (this.linkResolver ? this.linkResolver.resolveURL(this.opContext, uri) : null) || {};
345
- if (url) {
346
- x.commaItem(desc, url);
343
+ async renderLinkComma(x, uri) {
344
+ let {description, link} = await (this.linkResolver ? this.linkResolver.resolveURL(this.opContext, uri) : null) || {};
345
+ if (link) {
346
+ x.commaItem(description, link);
347
347
  } else {
348
- x.commaItem(uri);
348
+ x.commaItem(link);
349
349
  }
350
350
  }
351
351
 
@@ -673,7 +673,7 @@ class Renderer {
673
673
  p.tx(" ");
674
674
  p.startCommaList("and");
675
675
  for (let ext of supplements) {
676
- this.renderLinkComma(p, getValuePrimitive(ext));
676
+ await this.renderLinkComma(p, getValuePrimitive(ext));
677
677
  }
678
678
  p.stopCommaList();
679
679
  p.tx(".");
@@ -762,10 +762,10 @@ class Renderer {
762
762
  li.stopCommaList();
763
763
  }
764
764
  } else if (inc.valueSet && inc.valueSet.length > 0) {
765
- li.tx(this.translatePlural(inc.valueSet.length, 'VALUE_SET_RULES_INC'));
765
+ li.tx(this.translatePlural(inc.valueSet.length, 'VALUE_SET_IMPORT')+" ");
766
766
  li.startCommaList("and");
767
767
  for (let vs of inc.valueSet) {
768
- this.renderLinkComma(li, vs);
768
+ await this.renderLinkComma(li, vs);
769
769
  }
770
770
  li.stopCommaList();
771
771
  } else {
package/tx/provider.js CHANGED
@@ -231,8 +231,8 @@ class Provider {
231
231
  return vs;
232
232
  }
233
233
  }
234
- let vs = await this.findKnownValueSet(url, version);
235
- return vs;
234
+ let vs1 = await this.findKnownValueSet(url, version);
235
+ return vs1;
236
236
  }
237
237
 
238
238
  async getConceptMapById(opContext, id) {
@@ -381,7 +381,7 @@ class Provider {
381
381
  if (vs) {
382
382
  return {
383
383
  link: this.path+"/ValueSet/"+vs.id,
384
- description: (vs.title ? vs.title : vs.name)+(version ? " v"+version : "")
384
+ description: (vs.title ? vs.title : vs.name)+(version ? " v"+version : vs.version ? " v"+vs.version : "")
385
385
  };
386
386
  }
387
387
  let cm = await this.findConceptMap(opContext, system, version);
@@ -7,6 +7,30 @@ const ValueSet = require("../library/valueset");
7
7
  // Columns that can be returned directly without parsing JSON
8
8
  const INDEXED_COLUMNS = ['id', 'url', 'version', 'date', 'description', 'name', 'publisher', 'status', 'title'];
9
9
 
10
+ /**
11
+ * Is `candidate` a newer version than `current`? Used to keep the version-less
12
+ * (bare-url) and url|major.minor map keys pointing at the latest version when several
13
+ * versions of the same value set are present. Handles both semver (IG/package value
14
+ * sets) and VSAC-style YYYYMMDD date versions (not semver, but fixed-width so they
15
+ * order correctly numerically).
16
+ * @param {string|null|undefined} candidate
17
+ * @param {string|null|undefined} current
18
+ * @returns {boolean}
19
+ */
20
+ function isNewerVersion(candidate, current) {
21
+ if (current == null) return true;
22
+ if (candidate == null) return false;
23
+ if (candidate === current) return false;
24
+ if (VersionUtilities.isSemVer(candidate) && VersionUtilities.isSemVer(current)) {
25
+ return VersionUtilities.isThisOrLater(current, candidate); // candidate >= current (semver-aware)
26
+ }
27
+ if (/^\d+$/.test(candidate) && /^\d+$/.test(current)) {
28
+ // e.g. VSAC YYYYMMDD - compare numerically (handles differing widths too)
29
+ return Number(candidate) > Number(current);
30
+ }
31
+ return candidate > current; // best-effort lexical fallback
32
+ }
33
+
10
34
  /**
11
35
  * Shared database layer for ValueSet providers
12
36
  * Handles SQLite operations for indexing and searching ValueSets
@@ -737,21 +761,22 @@ class ValueSetDatabase {
737
761
  }
738
762
 
739
763
  addToMap(valueSetMap, id, url, version, valueSet) {
740
- valueSetMap.set(url, valueSet);
741
- valueSetMap.set(id, valueSet);
764
+ // The bare-url key must resolve to the *latest* version, but rows arrive in no
765
+ // particular order (and several versions of the same url coexist - e.g. VSAC, where
766
+ // each date-version is its own row). So only overwrite it when this version is newer.
767
+ this._setIfNewer(valueSetMap, url, version, valueSet);
768
+ valueSetMap.set(id, valueSet); // id is unique per version
742
769
 
743
770
  if (version) {
744
- // Store by url|version
745
- const versionKey = `${url}|${version}`;
746
- valueSetMap.set(versionKey, valueSet);
771
+ // Store by url|version (exact - unique per version, always set)
772
+ valueSetMap.set(`${url}|${version}`, valueSet);
747
773
 
748
- // If version is semver, also store by url|major.minor
774
+ // If version is semver, also store by url|major.minor, keeping the latest patch
749
775
  try {
750
776
  if (VersionUtilities.isSemVer(version)) {
751
777
  const majorMinor = VersionUtilities.getMajMin(version);
752
778
  if (majorMinor) {
753
- const majorMinorKey = `${url}|${majorMinor}`;
754
- valueSetMap.set(majorMinorKey, valueSet);
779
+ this._setIfNewer(valueSetMap, `${url}|${majorMinor}`, version, valueSet);
755
780
  }
756
781
  }
757
782
  } catch (error) {
@@ -760,6 +785,14 @@ class ValueSetDatabase {
760
785
  }
761
786
  }
762
787
 
788
+ // Set map[key] = valueSet only if `version` is newer than the version already there.
789
+ _setIfNewer(valueSetMap, key, version, valueSet) {
790
+ const existing = valueSetMap.get(key);
791
+ if (!existing || isNewerVersion(version, existing.version)) {
792
+ valueSetMap.set(key, valueSet);
793
+ }
794
+ }
795
+
763
796
  /**
764
797
  * Search for ValueSets based on criteria
765
798
  * @param {Array<{name: string, value: string}>} searchParams - Search criteria
@@ -26,6 +26,9 @@ class PackageValueSetProvider extends AbstractValueSetProvider {
26
26
  this.sourcePackageCode = packageLoader.id();
27
27
  }
28
28
 
29
+ code() {
30
+ return this.sourcePackageCode;
31
+ }
29
32
  sourcePackage() {
30
33
  return this.sourcePackageCode;
31
34
  }
package/tx/vs/vs-vsac.js CHANGED
@@ -66,6 +66,10 @@ class VSACValueSetProvider extends AbstractValueSetProvider {
66
66
  });
67
67
  }
68
68
 
69
+ code() {
70
+ return "vsac";
71
+ }
72
+
69
73
  sourcePackage() {
70
74
  return "vsac";
71
75
  }