fhirsmith 0.9.6 → 0.10.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 (49) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/library/folder-content-loader.js +91 -0
  3. package/npmprojector/npmprojector.js +2 -6
  4. package/package.json +1 -1
  5. package/packages/package-crawler.js +123 -6
  6. package/packages/packages.js +104 -0
  7. package/publisher/publisher.js +290 -12
  8. package/registry/crawler.js +85 -6
  9. package/registry/registry.js +24 -7
  10. package/server.js +11 -2
  11. package/stats.js +26 -18
  12. package/translations/Messages.properties +2 -1
  13. package/tx/cs/cs-cs.js +8 -0
  14. package/tx/cs/cs-loinc.js +1 -0
  15. package/tx/cs/cs-provider-list.js +2 -1
  16. package/tx/cs/cs-snomed.js +142 -59
  17. package/tx/data/snomed-testing.cache +0 -0
  18. package/tx/html/home-metrics.liquid +10 -10
  19. package/tx/library/canonical-resource.js +4 -2
  20. package/tx/library/codesystem.js +31 -0
  21. package/tx/library/conceptmap.js +24 -0
  22. package/tx/library/designations.js +27 -20
  23. package/tx/library/renderer.js +303 -22
  24. package/tx/library/ucum-types.js +4 -1
  25. package/tx/library/valueset.js +46 -0
  26. package/tx/library.js +65 -21
  27. package/tx/operation-context.js +122 -27
  28. package/tx/params.js +36 -8
  29. package/tx/provider.js +6 -3
  30. package/tx/tx-html.js +34 -0
  31. package/tx/tx.js +92 -30
  32. package/tx/vs/vs-vsac.js +157 -9
  33. package/tx/workers/cache-control.js +186 -0
  34. package/tx/workers/{related.js → compare.js} +27 -27
  35. package/tx/workers/expand.js +100 -96
  36. package/tx/workers/lookup.js +6 -0
  37. package/tx/workers/metadata.js +11 -6
  38. package/tx/workers/read.js +1 -1
  39. package/tx/workers/translate.js +20 -29
  40. package/tx/workers/validate.js +18 -10
  41. package/tx/workers/worker.js +134 -47
  42. package/tx/xversion/xv-bundle.js +1 -2
  43. package/tx/xversion/xv-codesystem.js +5 -2
  44. package/tx/xversion/xv-parameters.js +4 -4
  45. package/tx/xversion/xv-resource.js +2 -2
  46. package/tx/xversion/xv-terminologyCapabilities.js +11 -6
  47. package/tx/xversion/xv-valueset.js +7 -7
  48. package/publisher/task-draft.js +0 -463
  49. package/tx/data/OperationDefinition-ValueSet-related.json +0 -133
@@ -1,11 +1,11 @@
1
1
  //
2
- // Related Worker - Handles ValueSet $related operation
2
+ // Compare Worker - Handles ValueSet $compare operation
3
3
  //
4
- // GET /ValueSet/{id}/$related
5
- // GET /ValueSet/$related?url=...&version=...
6
- // POST /ValueSet/$related (form body or Parameters with url)
7
- // POST /ValueSet/$related (body is ValueSet resource)
8
- // POST /ValueSet/$related (body is Parameters with valueSet parameter)
4
+ // GET /ValueSet/{id}/$compare
5
+ // GET /ValueSet/$compare?url=...&version=...
6
+ // POST /ValueSet/$compare (form body or Parameters with url)
7
+ // POST /ValueSet/$compare (body is ValueSet resource)
8
+ // POST /ValueSet/$compare (body is Parameters with valueSet parameter)
9
9
  //
10
10
 
11
11
  const { TerminologyWorker } = require('./worker');
@@ -18,7 +18,7 @@ const {SearchFilterText} = require("../library/designations");
18
18
  const {ArrayMatcher} = require("../../library/utilities");
19
19
  const {debugLog} = require("../operation-context");
20
20
 
21
- class RelatedWorker extends TerminologyWorker {
21
+ class CompareWorker extends TerminologyWorker {
22
22
  showLogic = false;
23
23
 
24
24
  /**
@@ -37,18 +37,18 @@ class RelatedWorker extends TerminologyWorker {
37
37
  * @returns {string}
38
38
  */
39
39
  opName() {
40
- return 'related';
40
+ return 'compare';
41
41
  }
42
42
 
43
43
  /**
44
- * Handle a type-level $related request
45
- * GET/POST /ValueSet/$related
44
+ * Handle a type-level $compare request
45
+ * GET/POST /ValueSet/$compare
46
46
  * @param {express.Request} req - Express request
47
47
  * @param {express.Response} res - Express response
48
48
  */
49
49
  async handle(req, res) {
50
50
  try {
51
- await this.handleTypeLevelRelated(req, res);
51
+ await this.handleTypeLevelCompare(req, res);
52
52
  } catch (error) {
53
53
  this.log.error(error);
54
54
  debugLog(error);
@@ -76,14 +76,14 @@ class RelatedWorker extends TerminologyWorker {
76
76
  }
77
77
 
78
78
  /**
79
- * Handle an instance-level $related request
80
- * GET/POST /ValueSet/{id}/$related
79
+ * Handle an instance-level $compare request
80
+ * GET/POST /ValueSet/{id}/$compare
81
81
  * @param {express.Request} req - Express request
82
82
  * @param {express.Response} res - Express response
83
83
  */
84
84
  async handleInstance(req, res) {
85
85
  try {
86
- await this.handleInstanceLevelRelated(req, res);
86
+ await this.handleInstanceLevelCompare(req, res);
87
87
  } catch (error) {
88
88
  this.log.error(error);
89
89
  debugLog(error);
@@ -105,11 +105,11 @@ class RelatedWorker extends TerminologyWorker {
105
105
  }
106
106
 
107
107
  /**
108
- * Handle type-level $related: /ValueSet/$related
108
+ * Handle type-level $compare: /ValueSet/$compare
109
109
  * ValueSet identified by url, or provided directly in body
110
110
  */
111
- async handleTypeLevelRelated(req, res) {
112
- this.deadCheck('related-type-level');
111
+ async handleTypeLevelCompare(req, res) {
112
+ this.deadCheck('compare-type-level');
113
113
 
114
114
  let params = req.body;
115
115
  this.addHttpParams(req, params);
@@ -121,16 +121,16 @@ class RelatedWorker extends TerminologyWorker {
121
121
  let thisVS = await this.readValueSet(res, "this", params, txp);
122
122
  let otherVS = await this.readValueSet(res, "other", params, txp);
123
123
 
124
- const result = await this.doRelated(txp, thisVS, otherVS);
124
+ const result = await this.doCompare(txp, thisVS, otherVS);
125
125
  return res.json(result);
126
126
  }
127
127
 
128
128
  /**
129
- * Handle instance-level related: /ValueSet/{id}/$related
129
+ * Handle instance-level compare: /ValueSet/{id}/$compare
130
130
  * ValueSet identified by resource ID
131
131
  */
132
- async handleInstanceLevelRelated(req, res) {
133
- this.deadCheck('related-instance-level');
132
+ async handleInstanceLevelCompare(req, res) {
133
+ this.deadCheck('compare-instance-level');
134
134
 
135
135
  let params = req.body;
136
136
  this.addHttpParams(req, params);
@@ -147,7 +147,7 @@ class RelatedWorker extends TerminologyWorker {
147
147
  }
148
148
  let otherVS = await this.readValueSet(res, "other", params, txp);
149
149
 
150
- const result = await this.doRelated(txp, thisVS, otherVS);
150
+ const result = await this.doCompare(txp, thisVS, otherVS);
151
151
  return res.json(result);
152
152
  }
153
153
 
@@ -198,7 +198,7 @@ class RelatedWorker extends TerminologyWorker {
198
198
  }
199
199
  }
200
200
 
201
- async doRelated(txp, thisVS, otherVS) {
201
+ async doCompare(txp, thisVS, otherVS) {
202
202
 
203
203
  // ok, we have to compare the composes. we don't care about anything else
204
204
  const thisC = thisVS.jsonObj.compose;
@@ -206,12 +206,12 @@ class RelatedWorker extends TerminologyWorker {
206
206
  if (!thisC) {
207
207
  return this.makeOutcome("indeterminate", `The ValueSet ${thisVS.vurl} has no compose`);
208
208
  }
209
- Extensions.checkNoModifiers(thisC, 'RelatedWorker.doRelated', 'compose', thisVS.vurl)
209
+ Extensions.checkNoModifiers(thisC, 'CompareWorker.doCompare', 'compose', thisVS.vurl)
210
210
  this.checkNoLockedDate(thisVS.vurl, thisC);
211
211
  if (!otherC) {
212
212
  return this.makeOutcome("indeterminate", `The ValueSet ${otherVS.vurl} has no compose`);
213
213
  }
214
- Extensions.checkNoModifiers(otherC, 'RelatedWorker.doRelated', 'compose', otherVS.vurl)
214
+ Extensions.checkNoModifiers(otherC, 'CompareWorker.doCompare', 'compose', otherVS.vurl)
215
215
  this.checkNoLockedDate(otherVS.vurl, otherC);
216
216
 
217
217
  let systems = new Map(); // tracks whether the comparison is version dependent or not
@@ -268,7 +268,7 @@ class RelatedWorker extends TerminologyWorker {
268
268
  } else if (status.left) {
269
269
  outcome = this.makeOutcome("superset", `The valueSet ${thisVS.vurl} is a super-set of the valueSet ${otherVS.vurl}`);
270
270
  } else {
271
- outcome = this.makeOutcome("subset", `The valueSet ${thisVS.vurl} is a seb-set of the valueSet ${otherVS.vurl}`);
271
+ outcome = this.makeOutcome("subset", `The valueSet ${thisVS.vurl} is a sub-set of the valueSet ${otherVS.vurl}`);
272
272
  }
273
273
  if (txp.diagnostics) {
274
274
  outcome.parameter.push({name: 'performed-expansion', valueBoolean: exp ? true : false})
@@ -712,5 +712,5 @@ class RelatedWorker extends TerminologyWorker {
712
712
  }
713
713
 
714
714
  module.exports = {
715
- RelatedWorker
715
+ CompareWorker
716
716
  };
@@ -647,11 +647,11 @@ class ValueSetExpander {
647
647
  } else if (cs.contentMode() === 'supplement') {
648
648
  throw new Issue('error', 'business-rule', null, null, 'The code system definition for ' + cset.system + ' defines a supplement, so this expansion cannot be performed', 'invalid');
649
649
  } else if (cs.contentMode() === 'fragment') {
650
- this.addParamUri(exp, 'used-fragment', cs.system() + '|' + cs.version());
650
+ this.addParamUri(exp, 'used-fragment', cs.system()+ (cs.version() ? '|' + cs.version(): ""));
651
651
  Extensions.addBoolean(exp, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed", true);
652
652
  Extensions.addString(exp, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed-reason","This extension is based on a fragment of the code system " + cset.system);
653
653
  } else {
654
- this.addParamUri(exp, cs.contentMode(), cs.system() + '|' + cs.version());
654
+ this.addParamUri(exp, cs.contentMode(), cs.system() + cs.system()+ (cs.version() ? '|' + cs.version(): ""));
655
655
  Extensions.addBoolean(exp, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed", true);
656
656
  Extensions.addString(exp, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed-reason","This extension is based on a fragment of the code system " + cset.system);
657
657
  }
@@ -948,7 +948,7 @@ class ValueSetExpander {
948
948
  let vs = await this.worker.findValueSet(s, '', vsSrc);
949
949
  const ivs = new ImportedValueSet(await this.expandValueSet(s, '', vs, filter, notClosed));
950
950
  this.checkResourceCanonicalStatus(expansion, ivs.valueSet, this.valueSet);
951
- if (!vs.isContained) {
951
+ if (!vs.isContained && ivs.valueSet.vurl) {
952
952
  this.addParamUri(expansion, 'used-valueset', ivs.valueSet.vurl);
953
953
  }
954
954
  valueSets.push(ivs);
@@ -961,124 +961,128 @@ class ValueSetExpander {
961
961
  const cs = await this.worker.findCodeSystem(cset.system, cset.version, this.params, ['complete', 'fragment'], false,
962
962
  true, true, null, this.requiredSupplements);
963
963
 
964
- this.worker.checkSupplements(cs, cset, this.requiredSupplements, this.usedSupplements);
965
- this.checkResourceCanonicalStatus(expansion, cs, this.valueSet);
966
- const sv = this.canonical(await cs.system(), await cs.version());
967
- this.addParamUri(expansion, 'used-codesystem', sv);
964
+ if (cs == null) {
965
+ // nothing?
966
+ } else {
967
+ this.worker.checkSupplements(cs, cset, this.requiredSupplements, this.usedSupplements);
968
+ this.checkResourceCanonicalStatus(expansion, cs, this.valueSet);
969
+ const sv = this.canonical(await cs.system(), await cs.version());
970
+ this.addParamUri(expansion, 'used-codesystem', sv);
968
971
 
969
- for (const u of cset.valueSet || []) {
970
- this.worker.deadCheck('processCodes#3');
971
- const s = this.worker.pinValueSet(u);
972
- this.worker.opContext.log('import value set ' + s);
973
- let vs = await this.worker.findValueSet(s, '', vsSrc);
974
- const ivs = new ImportedValueSet(await this.expandValueSet(s, '', vs, filter, notClosed));
975
- this.checkResourceCanonicalStatus(expansion, ivs.valueSet, this.valueSet);
976
- if (!vs.isContained) {
977
- this.addParamUri(expansion, 'used-valueset', this.worker.makeVurl(ivs.valueSet));
972
+ for (const u of cset.valueSet || []) {
973
+ this.worker.deadCheck('processCodes#3');
974
+ const s = this.worker.pinValueSet(u);
975
+ this.worker.opContext.log('import value set ' + s);
976
+ let vs = await this.worker.findValueSet(s, '', vsSrc);
977
+ const ivs = new ImportedValueSet(await this.expandValueSet(s, '', vs, filter, notClosed));
978
+ this.checkResourceCanonicalStatus(expansion, ivs.valueSet, this.valueSet);
979
+ if (!vs.isContained) {
980
+ this.addParamUri(expansion, 'used-valueset', this.worker.makeVurl(ivs.valueSet));
981
+ }
982
+ valueSets.push(ivs);
978
983
  }
979
- valueSets.push(ivs);
980
- }
981
984
 
982
- if (!cset.concept && !cset.filter) {
983
- this.worker.opContext.log('handle system');
984
- if (!cset.valueSet) {
985
- if (!this.excludeSpecialCase) {
986
- // excluding a whole system - we don't list the codes in this case
987
- this.excludedSystems.add(cset.system + (this.doingVersion && cset.version ? '|' + cset.version : ''));
985
+ if (!cset.concept && !cset.filter) {
986
+ this.worker.opContext.log('handle system');
987
+ if (!cset.valueSet) {
988
+ if (!this.excludeSpecialCase) {
989
+ // excluding a whole system - we don't list the codes in this case
990
+ this.excludedSystems.add(cset.system + (this.doingVersion && cset.version ? '|' + cset.version : ''));
991
+ } else {
992
+ const iter = await cs.iteratorAll();
993
+ if (iter) {
994
+ let c = await cs.nextContext(iter);
995
+ while (c) {
996
+ this.worker.deadCheck('processCodes#3aa');
997
+ this.excludeCode(cs, cs.system(), cs.version(), await cs.code(c), expansion, valueSets, vsSrc.url);
998
+ c = await cs.nextContext(iter);
999
+ }
1000
+ }
1001
+ }
988
1002
  } else {
1003
+ if (cs.isNotClosed(filter)) {
1004
+ if (cs.specialEnumeration()) {
1005
+ Extensions.addBoolean(expansion, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed", true);
1006
+ Extensions.addString(expansion, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed-reason", 'The code System "' + cs.system() + " has a grammar and so has infinite members. This extension is based on " + cs.specialEnumeration());
1007
+ } else {
1008
+ throw new Issue("error", "too-costly", null, null, 'The code System "' + cs.system() + '" has a grammar, and cannot be enumerated directly', null, 422).withDiagnostics(this.worker.opContext.diagnostics());
1009
+ }
1010
+ }
1011
+
989
1012
  const iter = await cs.iteratorAll();
990
1013
  if (iter) {
991
1014
  let c = await cs.nextContext(iter);
992
1015
  while (c) {
993
- this.worker.deadCheck('processCodes#3aa');
994
- this.excludeCode(cs, cs.system(), cs.version(), await cs.code(c), expansion, valueSets, vsSrc.url);
1016
+ this.worker.deadCheck('processCodes#3a');
1017
+ if (await this.passesFilters(cs, c, prep, filters, 0)) {
1018
+ this.excludeCode(cs, cs.system(), cs.version(), await cs.code(c), expansion, valueSets, vsSrc.url);
1019
+ }
995
1020
  c = await cs.nextContext(iter);
996
1021
  }
997
1022
  }
998
1023
  }
999
- } else {
1000
- if (cs.isNotClosed(filter)) {
1001
- if (cs.specialEnumeration()) {
1002
- Extensions.addBoolean(expansion, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed", true);
1003
- Extensions.addString(expansion, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed-reason", 'The code System "' + cs.system() + " has a grammar and so has infinite members. This extension is based on " + cs.specialEnumeration());
1004
- } else {
1005
- throw new Issue("error", "too-costly", null, null, 'The code System "' + cs.system() + '" has a grammar, and cannot be enumerated directly', null, 422).withDiagnostics(this.worker.opContext.diagnostics());
1006
- }
1007
- }
1008
-
1009
- const iter = await cs.iteratorAll();
1010
- if (iter) {
1011
- let c = await cs.nextContext(iter);
1012
- while (c) {
1013
- this.worker.deadCheck('processCodes#3a');
1014
- if (await this.passesFilters(cs, c, prep, filters, 0)) {
1015
- this.excludeCode(cs, cs.system(), cs.version(), await cs.code(c), expansion, valueSets, vsSrc.url);
1016
- }
1017
- c = await cs.nextContext(iter);
1018
- }
1019
- }
1020
1024
  }
1021
- }
1022
1025
 
1023
- if (cset.concept) {
1024
- this.worker.opContext.log('iterate concepts');
1025
- const cds = new Designations(this.worker.i18n.languageDefinitions);
1026
- for (const cc of cset.concept) {
1027
- this.worker.deadCheck('processCodes#3');
1028
- cds.clear();
1029
- Extensions.checkNoModifiers(cc, 'ValueSetExpander.processCodes', 'set concept reference', vsSrc.vurl);
1030
- const cctxt = await cs.locate(cc.code, this.allAltCodes);
1031
- if (cctxt && cctxt.context && (!this.params.activeOnly || !await cs.isInactive(cctxt.context)) && await this.passesFilters(cs, cctxt.context, prep, filters, 0)) {
1032
- if (filter.passesDesignations(cds) || filter.passes(cc.code)) {
1033
- let ov = Extensions.readString(cc, 'http://hl7.org/fhir/StructureDefinition/itemWeight');
1034
- if (!ov) {
1035
- ov = await cs.itemWeight(cctxt.context);
1026
+ if (cset.concept) {
1027
+ this.worker.opContext.log('iterate concepts');
1028
+ const cds = new Designations(this.worker.i18n.languageDefinitions);
1029
+ for (const cc of cset.concept) {
1030
+ this.worker.deadCheck('processCodes#3');
1031
+ cds.clear();
1032
+ Extensions.checkNoModifiers(cc, 'ValueSetExpander.processCodes', 'set concept reference', vsSrc.vurl);
1033
+ const cctxt = await cs.locate(cc.code, this.allAltCodes);
1034
+ if (cctxt && cctxt.context && (!this.params.activeOnly || !await cs.isInactive(cctxt.context)) && await this.passesFilters(cs, cctxt.context, prep, filters, 0)) {
1035
+ if (filter.passesDesignations(cds) || filter.passes(cc.code)) {
1036
+ let ov = Extensions.readString(cc, 'http://hl7.org/fhir/StructureDefinition/itemWeight');
1037
+ if (!ov) {
1038
+ ov = await cs.itemWeight(cctxt.context);
1039
+ }
1040
+ this.excludeCode(cs, await cs.system(), await cs.version(), cc.code, expansion, valueSets, vsSrc.url);
1036
1041
  }
1037
- this.excludeCode(cs, await cs.system(), await cs.version(), cc.code, expansion, valueSets, vsSrc.url);
1038
1042
  }
1039
1043
  }
1040
1044
  }
1041
- }
1042
1045
 
1043
- if (cset.filter) {
1044
- this.worker.opContext.log('prep filters');
1045
- const prep = await cs.getPrepContext(true);
1046
- if (!filter.isNull) {
1047
- await cs.searchFilter(filter, prep, true);
1048
- }
1046
+ if (cset.filter) {
1047
+ this.worker.opContext.log('prep filters');
1048
+ const prep = await cs.getPrepContext(true);
1049
+ if (!filter.isNull) {
1050
+ await cs.searchFilter(filter, prep, true);
1051
+ }
1049
1052
 
1050
- if (cs.specialEnumeration()) {
1051
- await cs.specialFilter(prep, true);
1052
- Extensions.addBoolean(expansion, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed", true);
1053
- Extensions.addString(expansion, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed-reason", 'The code System "' + cs.system() + " has a grammar and so has infinite members. This extension is based on " + cs.specialEnumeration());
1054
- }
1053
+ if (cs.specialEnumeration()) {
1054
+ await cs.specialFilter(prep, true);
1055
+ Extensions.addBoolean(expansion, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed", true);
1056
+ Extensions.addString(expansion, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed-reason", 'The code System "' + cs.system() + " has a grammar and so has infinite members. This extension is based on " + cs.specialEnumeration());
1057
+ }
1055
1058
 
1056
- let first = true;
1057
- for (let fc of cset.filter) {
1058
- this.worker.deadCheck('processCodes#4a');
1059
- Extensions.checkNoModifiers(fc, 'ValueSetExpander.processCodes', 'filter', vsSrc.vurl);
1060
- await cs.filter(prep, first, fc.property, fc.op, fc.value);
1061
- first = false;
1062
- }
1059
+ let first = true;
1060
+ for (let fc of cset.filter) {
1061
+ this.worker.deadCheck('processCodes#4a');
1062
+ Extensions.checkNoModifiers(fc, 'ValueSetExpander.processCodes', 'filter', vsSrc.vurl);
1063
+ await cs.filter(prep, first, fc.property, fc.op, fc.value);
1064
+ first = false;
1065
+ }
1063
1066
 
1064
- this.worker.opContext.log('iterate filters');
1065
- const fset = await cs.executeFilters(prep);
1066
- if (await cs.filtersNotClosed(prep)) {
1067
- notClosed.value = true;
1068
- }
1069
- //let count = 0;
1070
- while (await cs.filterMore(prep, fset[0])) {
1071
- this.worker.deadCheck('processCodes#5');
1072
- const c = await cs.filterConcept(prep, fset[0]);
1073
- const ok = (!this.params.activeOnly || !await cs.isInactive(c)) && (await this.passesFilters(cs, c, prep, fset, 1));
1074
- if (ok) {
1075
- //count++;
1076
- if (this.passesImports(valueSets, await cs.system(), await cs.code(c), 0)) {
1077
- this.excludeCode(cs, await cs.system(), await cs.version(), await cs.code(c), expansion, null, vsSrc.url);
1067
+ this.worker.opContext.log('iterate filters');
1068
+ const fset = await cs.executeFilters(prep);
1069
+ if (await cs.filtersNotClosed(prep)) {
1070
+ notClosed.value = true;
1071
+ }
1072
+ //let count = 0;
1073
+ while (await cs.filterMore(prep, fset[0])) {
1074
+ this.worker.deadCheck('processCodes#5');
1075
+ const c = await cs.filterConcept(prep, fset[0]);
1076
+ const ok = (!this.params.activeOnly || !await cs.isInactive(c)) && (await this.passesFilters(cs, c, prep, fset, 1));
1077
+ if (ok) {
1078
+ //count++;
1079
+ if (this.passesImports(valueSets, await cs.system(), await cs.code(c), 0)) {
1080
+ this.excludeCode(cs, await cs.system(), await cs.version(), await cs.code(c), expansion, null, vsSrc.url);
1081
+ }
1078
1082
  }
1079
1083
  }
1084
+ this.worker.opContext.log('iterate filters finished');
1080
1085
  }
1081
- this.worker.opContext.log('iterate filters finished');
1082
1086
  }
1083
1087
  }
1084
1088
  }
@@ -374,6 +374,12 @@ class LookupWorker extends TerminologyWorker {
374
374
  });
375
375
  }
376
376
 
377
+ if (designation.status && designation.status !== 'active') {
378
+ designationParts.push({
379
+ name: 'status',
380
+ valueCode: designation.status
381
+ });
382
+ }
377
383
  designationParts.push({
378
384
  name: 'value',
379
385
  valueString: designation.value
@@ -233,7 +233,7 @@ class MetadataHandler {
233
233
  operation: [
234
234
  { name: 'expand', definition: 'http://hl7.org/fhir/OperationDefinition/ValueSet-expand' },
235
235
  { name: 'validate-code', definition: 'http://hl7.org/fhir/OperationDefinition/ValueSet-validate-code' },
236
- { name: 'related', definition: 'https://raw.githubusercontent.com/HealthIntersections/FHIRsmith/refs/heads/main/tx/data/OperationDefinition-ValueSet-related.json' }
236
+ { name: 'compare', definition: 'http://hl7.org/fhir/tools/OperationDefinition/ValueSet-compare' }
237
237
  ]
238
238
  },
239
239
  {
@@ -266,7 +266,8 @@ class MetadataHandler {
266
266
  { name: 'validate-code', definition: 'http://hl7.org/fhir/OperationDefinition/Resource-validate-code' },
267
267
  { name: 'translate', definition: 'http://hl7.org/fhir/OperationDefinition/ConceptMap-translate' },
268
268
  { name: 'closure', definition: 'http://hl7.org/fhir/OperationDefinition/ConceptMap-closure' },
269
- { name: 'related', definition: 'https://raw.githubusercontent.com/HealthIntersections/FHIRsmith/refs/heads/main/tx/data/OperationDefinition-ValueSet-related.json' },
269
+ { name: 'compare', definition: 'http://hl7.org/fhir/tools/OperationDefinition/ValueSet-compare' },
270
+ { name: 'cache-control', definition: 'http://hl7.org/fhir/tools/OperationDefinition/cache-control' },
270
271
  { name: 'versions', definition: 'http://hl7.org/fhir/OperationDefinition/fhir-versions' }
271
272
  ]
272
273
  }
@@ -395,10 +396,14 @@ class MetadataHandler {
395
396
  buildExpansionCapabilities() {
396
397
  return {
397
398
  parameter: [
398
- {
399
- name: 'cache-id',
400
- 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'
401
- },
399
+ // NOTE: `cache-id` is deliberately not advertised as an expansion parameter.
400
+ // Caching is now discovered via the $cache-control operation in the
401
+ // CapabilityStatement, not by advertising a cache-id parameter here. The old
402
+ // advertisement implied the implicit "auto-create on first sight" protocol,
403
+ // which has been replaced: a cache must be created explicitly with
404
+ // $cache-control?mode=start before its cache-id can be used. Clients that
405
+ // only know the old mechanism will see no cache-id parameter and simply not
406
+ // cache (inlining each request), which is correct, just not optimised.
402
407
  {
403
408
  name: 'tx-resource',
404
409
  documentation: 'Additional valuesets needed for evaluation e.g. value sets referred to from the import statement of the value set being expanded'
@@ -179,7 +179,7 @@ class ReadWorker extends TerminologyWorker {
179
179
  issue: [{
180
180
  severity: 'error',
181
181
  code: 'not-found',
182
- diagnostics: `ValueSet/${id} not found`
182
+ diagnostics: `ConceptMap/${id} not found`
183
183
  }]
184
184
  });
185
185
  }
@@ -205,10 +205,8 @@ class TranslateWorker extends TerminologyWorker {
205
205
  targetSystem = params.get('targetSystem');
206
206
  }
207
207
  }
208
- let explicit = true;
209
208
  // If no explicit concept map, we need to find one based on source/target
210
209
  if (conceptMaps.length == 0) {
211
- explicit = false;
212
210
  if (reverse) {
213
211
  await this.findConceptMapsInAdditionalResources(conceptMaps,targetSystem, targetScope, sourceScope, coding.system);
214
212
  await this.provider.findConceptMapForTranslation(this.opContext, conceptMaps, targetSystem, targetScope, sourceScope, coding.system, coding.code);
@@ -222,7 +220,7 @@ class TranslateWorker extends TerminologyWorker {
222
220
  }
223
221
 
224
222
  // Perform the translation
225
- const result = await this.doTranslate(conceptMaps, coding, targetScope, targetSystem, txp, reverse, explicit);
223
+ const result = await this.doTranslate(conceptMaps, coding, targetScope, targetSystem, txp, reverse);
226
224
  return res.status(200).json(result);
227
225
  }
228
226
 
@@ -322,7 +320,7 @@ class TranslateWorker extends TerminologyWorker {
322
320
  return result;
323
321
  }
324
322
 
325
- translateUsingGroupsForwards(cm, coding, targetScope, targetSystem, params, output, explicit) {
323
+ translateUsingGroupsForwards(cm, coding, targetScope, targetSystem, params, output) {
326
324
  let result = false;
327
325
  const matches = cm.listTranslations(coding, targetScope, targetSystem);
328
326
  if (matches.length > 0) {
@@ -385,12 +383,10 @@ class TranslateWorker extends TerminologyWorker {
385
383
  part: productParts
386
384
  });
387
385
  }
388
- if (!explicit) {
389
- matchParts.push({
390
- name: 'originMap',
391
- valueCanonical: cm.vurl
392
- });
393
- }
386
+ matchParts.push({
387
+ name: 'originMap',
388
+ valueCanonical: cm.vurl
389
+ });
394
390
  output.push({
395
391
  name: 'match',
396
392
  part: matchParts
@@ -403,7 +399,7 @@ class TranslateWorker extends TerminologyWorker {
403
399
  return result;
404
400
  }
405
401
 
406
- translateUsingGroupsReverse(cm, coding, targetScope, targetSystem, params, output, explicit) {
402
+ translateUsingGroupsReverse(cm, coding, targetScope, targetSystem, params, output) {
407
403
  let result = false;
408
404
  const matches = cm.listTranslationsReverse(coding, targetScope, targetSystem);
409
405
  if (matches.length > 0) {
@@ -474,12 +470,10 @@ class TranslateWorker extends TerminologyWorker {
474
470
  part: productParts
475
471
  });
476
472
  }
477
- if (!explicit) {
478
- matchParts.push({
479
- name: 'originMap',
480
- valueCanonical: cm.vurl
481
- });
482
- }
473
+ matchParts.push({
474
+ name: 'originMap',
475
+ valueCanonical: cm.vurl
476
+ });
483
477
  output.push({
484
478
  name: 'match',
485
479
  part: matchParts
@@ -491,7 +485,7 @@ class TranslateWorker extends TerminologyWorker {
491
485
  return result;
492
486
  }
493
487
 
494
- async translateUsingCodeSystem(cm, coding, target, params, output, reverse, explicit) {
488
+ async translateUsingCodeSystem(cm, coding, target, params, output, reverse) {
495
489
  let result = false;
496
490
  const factory = cm.jsonObj.internalSource;
497
491
  let prov = await factory.build(this.opContext, []);
@@ -537,12 +531,10 @@ class TranslateWorker extends TerminologyWorker {
537
531
  valueString: t.message
538
532
  });
539
533
  }
540
- if (!explicit) {
541
- matchParts.push({
542
- name: 'originMap',
543
- valueCanonical: cm.vurl
544
- });
545
- }
534
+ matchParts.push({
535
+ name: 'originMap',
536
+ valueCanonical: cm.vurl
537
+ });
546
538
  output.push({
547
539
  name: 'match',
548
540
  part: matchParts
@@ -560,10 +552,9 @@ class TranslateWorker extends TerminologyWorker {
560
552
  * @param {string} targetSystem - Target code system (optional)
561
553
  * @param {Parameters} params - Full parameters object
562
554
  * @param {boolean} reverse - Full parameters object*
563
- * @param {boolean} explicit - If the concept map was named explicitly
564
555
  * @returns {Object} Parameters resource with translate result
565
556
  */
566
- async doTranslate(conceptMaps, coding, targetScope, targetSystem, params, reverse, explicit) {
557
+ async doTranslate(conceptMaps, coding, targetScope, targetSystem, params, reverse) {
567
558
  this.deadCheck('doTranslate');
568
559
 
569
560
  const result = [];
@@ -572,11 +563,11 @@ class TranslateWorker extends TerminologyWorker {
572
563
  let added = false;
573
564
  for (const cm of conceptMaps) {
574
565
  if (cm.jsonObj.internalSource) {
575
- added = await this.translateUsingCodeSystem(cm, coding, targetSystem, params, result, reverse, explicit) || added;
566
+ added = await this.translateUsingCodeSystem(cm, coding, targetSystem, params, result, reverse) || added;
576
567
  } else if (reverse) {
577
- added = this.translateUsingGroupsReverse(cm, coding, targetScope, targetSystem, params, result, reverse, explicit) || added;
568
+ added = this.translateUsingGroupsReverse(cm, coding, targetScope, targetSystem, params, result) || added;
578
569
  } else{
579
- added = this.translateUsingGroupsForwards(cm, coding, targetScope, targetSystem, params, result, reverse, explicit) || added;
570
+ added = this.translateUsingGroupsForwards(cm, coding, targetScope, targetSystem, params, result) || added;
580
571
  }
581
572
  }
582
573
  result.push({