fhirsmith 0.6.0 → 0.7.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 (62) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +2 -0
  3. package/configurations/projector.json +21 -0
  4. package/configurations/readme.md +5 -0
  5. package/library/package-manager.js +0 -2
  6. package/library/version-utilities.js +85 -0
  7. package/package.json +1 -1
  8. package/packages/package-crawler.js +44 -9
  9. package/packages/packages.js +1 -0
  10. package/registry/crawler.js +35 -14
  11. package/registry/registry.js +3 -0
  12. package/server.js +4 -0
  13. package/tx/README.md +4 -4
  14. package/tx/cs/cs-loinc.js +5 -2
  15. package/tx/cs/cs-provider-api.js +25 -1
  16. package/tx/cs/cs-provider-list.js +2 -2
  17. package/tx/library/canonical-resource.js +6 -1
  18. package/tx/library.js +127 -10
  19. package/tx/ocl/README.md +236 -0
  20. package/tx/ocl/cache/cache-paths.cjs +32 -0
  21. package/tx/ocl/cache/cache-paths.js +2 -0
  22. package/tx/ocl/cache/cache-utils.cjs +43 -0
  23. package/tx/ocl/cache/cache-utils.js +2 -0
  24. package/tx/ocl/cm-ocl.cjs +531 -0
  25. package/tx/ocl/cm-ocl.js +1 -105
  26. package/tx/ocl/cs-ocl.cjs +1779 -0
  27. package/tx/ocl/cs-ocl.js +1 -38
  28. package/tx/ocl/fingerprint/fingerprint.cjs +67 -0
  29. package/tx/ocl/fingerprint/fingerprint.js +2 -0
  30. package/tx/ocl/http/client.cjs +31 -0
  31. package/tx/ocl/http/client.js +2 -0
  32. package/tx/ocl/http/pagination.cjs +98 -0
  33. package/tx/ocl/http/pagination.js +2 -0
  34. package/tx/ocl/jobs/background-queue.cjs +200 -0
  35. package/tx/ocl/jobs/background-queue.js +2 -0
  36. package/tx/ocl/mappers/concept-mapper.cjs +66 -0
  37. package/tx/ocl/mappers/concept-mapper.js +2 -0
  38. package/tx/ocl/model/concept-filter-context.cjs +51 -0
  39. package/tx/ocl/model/concept-filter-context.js +2 -0
  40. package/tx/ocl/shared/constants.cjs +15 -0
  41. package/tx/ocl/shared/constants.js +2 -0
  42. package/tx/ocl/shared/patches.cjs +224 -0
  43. package/tx/ocl/shared/patches.js +2 -0
  44. package/tx/ocl/vs-ocl.cjs +1848 -0
  45. package/tx/ocl/vs-ocl.js +1 -104
  46. package/tx/operation-context.js +8 -1
  47. package/tx/params.js +24 -3
  48. package/tx/provider.js +47 -0
  49. package/tx/tx-html.js +1 -1
  50. package/tx/tx.js +8 -0
  51. package/tx/vs/vs-vsac.js +4 -3
  52. package/tx/workers/batch-validate.js +3 -2
  53. package/tx/workers/batch.js +3 -2
  54. package/tx/workers/expand.js +64 -9
  55. package/tx/workers/lookup.js +5 -4
  56. package/tx/workers/read.js +2 -1
  57. package/tx/workers/related.js +3 -2
  58. package/tx/workers/search.js +4 -9
  59. package/tx/workers/subsumes.js +3 -2
  60. package/tx/workers/translate.js +4 -3
  61. package/tx/workers/validate.js +132 -40
  62. package/tx/workers/worker.js +1 -7
package/tx/ocl/vs-ocl.js CHANGED
@@ -1,105 +1,2 @@
1
- /**
2
- * Abstract base class for value set providers
3
- * Defines the interface that all value set providers must implement
4
- */
5
- // eslint-disable-next-line no-unused-vars
6
- class OCLValueSetProvider {
7
- /**
8
- * {int} Unique number assigned to this provider
9
- */
10
- spaceId;
1
+ module.exports = require('./vs-ocl.cjs');
11
2
 
12
- /**
13
- * ensure that the ids on the value sets are unique, if they are
14
- * in the global namespace
15
- *
16
- * @param {Set<String>} ids
17
- */
18
- // eslint-disable-next-line no-unused-vars
19
- assignIds(ids) {
20
- throw new Error('assignIds must be implemented by AbstractValueSetProvider subclass');
21
- }
22
-
23
- /**
24
- * Fetches a specific value set by URL and version
25
- * @param {string} url - The URL/identifier of the value set
26
- * @param {string} version - The version of the value set
27
- * @returns {Promise<ValueSet>} The requested value set
28
- * @throws {Error} Must be implemented by subclasses
29
- */
30
- // eslint-disable-next-line no-unused-vars
31
- async fetchValueSet(url, version) {
32
- throw new Error('fetchValueSet must be implemented by subclass');
33
- }
34
-
35
- /**
36
- * Fetches a specific value set by id. ValueSet providers must enforce that value set ids are unique
37
- * either globally (as enforced by assignIds) or in their space
38
- *
39
- * @param {string} id - The id of the value set
40
- * @returns {Promise<ValueSet>} The requested value set
41
- * @throws {Error} Must be implemented by subclasses
42
- */
43
- // eslint-disable-next-line no-unused-vars
44
- async fetchValueSetById(id) {
45
- throw new Error('fetchValueSetById must be implemented by subclass');
46
- }
47
-
48
- /**
49
- * Searches for value sets based on provided criteria
50
- * @param {Array<{name: string, value: string}>} searchParams - List of name/value pairs for search criteria
51
- * @returns {Promise<Array<ValueSet>>} List of matching value sets
52
- * @throws {Error} Must be implemented by subclasses
53
- */
54
- // eslint-disable-next-line no-unused-vars
55
- async searchValueSets(searchParams, elements = null) {
56
- throw new Error('searchValueSets must be implemented by subclass');
57
- }
58
-
59
- /**
60
- *
61
- * @returns {number} total number of value sets
62
- */
63
- vsCount() {
64
- return 0;
65
- }
66
-
67
- /**
68
- * Validates search parameters
69
- * @param {Array<{name: string, value: string}>} searchParams - Search parameters to validate
70
- * @protected
71
- */
72
- _validateSearchParams(searchParams) {
73
- if (!Array.isArray(searchParams)) {
74
- throw new Error('Search parameters must be an array');
75
- }
76
-
77
- for (const param of searchParams) {
78
- if (!param || typeof param !== 'object') {
79
- throw new Error('Each search parameter must be an object');
80
- }
81
- if (typeof param.name !== 'string' || typeof param.value !== 'string') {
82
- throw new Error('Search parameter must have string name and value properties');
83
- }
84
- }
85
- }
86
-
87
- /**
88
- * Validates URL and version parameters
89
- * @param {string} url - URL to validate
90
- * @param {string} version - Version to validate
91
- * @protected
92
- */
93
- _validateFetchParams(url, version) {
94
- if (typeof url !== 'string' || !url.trim()) {
95
- throw new Error('URL must be a non-empty string');
96
- }
97
- if (version != null && typeof version !== 'string') {
98
- throw new Error('Version must be a string');
99
- }
100
- }
101
- }
102
-
103
- module.exports = {
104
- AbstractValueSetProvider
105
- };
@@ -19,6 +19,12 @@ function isDebugging() {
19
19
  );
20
20
  }
21
21
 
22
+ function debugLog(error, message) {
23
+ if (isDebugging()) {
24
+ console.log(error, message);
25
+ }
26
+ }
27
+
22
28
 
23
29
  class TimeTracker {
24
30
  constructor() {
@@ -584,5 +590,6 @@ module.exports = {
584
590
  TimeTracker,
585
591
  ResourceCache,
586
592
  ExpansionCache,
587
- isDebugging
593
+ isDebugging,
594
+ debugLog
588
595
  };
package/tx/params.js CHANGED
@@ -66,6 +66,7 @@ class TxParameters {
66
66
  this.FDisplayWarning = false;
67
67
  this.FMembershipOnly = false;
68
68
  this.FDiagnostics = false;
69
+ this.FVersionsMatch = false;
69
70
 
70
71
  this.hasActiveOnly = false;
71
72
  this.hasExcludeNested = false;
@@ -77,6 +78,7 @@ class TxParameters {
77
78
  this.hasDefaultToLatestVersion = false;
78
79
  this.hasDisplayWarning = false;
79
80
  this.hasMembershipOnly = false;
81
+ this.hasVersionsMatch = false;
80
82
  }
81
83
 
82
84
  readParams(params) {
@@ -199,6 +201,10 @@ class TxParameters {
199
201
  if (getValuePrimitive(p) == true) this.membershipOnly = true;
200
202
  break;
201
203
  }
204
+ case 'versionsMatch' : {
205
+ if (getValuePrimitive(p) == true) this.FVersionsMatch = true;
206
+ break;
207
+ }
202
208
  case 'profile' : {
203
209
  let value = p.resource;
204
210
  if (value && (value.resourceType === 'Parameters' || value.resourceType === 'ExpansionProfile')) {
@@ -387,6 +393,15 @@ class TxParameters {
387
393
  this.hasMembershipOnly = true;
388
394
  }
389
395
 
396
+ get versionsMatch() {
397
+ return this.FVersionsMatch;
398
+ }
399
+
400
+ set versionsMatch(value) {
401
+ this.FVersionsMatch = value;
402
+ this.hasVersionsMatch = true;
403
+ }
404
+ e
390
405
  get versionRules() {
391
406
  return this.FVersionRules;
392
407
  }
@@ -412,6 +427,10 @@ class TxParameters {
412
427
  if (name === 'designation') {
413
428
  this.designations.push(getValuePrimitive(value));
414
429
  }
430
+
431
+ if (name === 'versionsMatch') {
432
+ this.versionsMatch = getValuePrimitive(value) === 'true';
433
+ }
415
434
  }
416
435
  }
417
436
 
@@ -502,6 +521,7 @@ class TxParameters {
502
521
  b('include-designations', this.FIncludeDesignations);
503
522
  b('include-definition', this.FIncludeDefinition);
504
523
  b('membership-only', this.FMembershipOnly);
524
+ b('versions-match', this.FVersionsMatch);
505
525
  b('default-to-latest', this.FDefaultToLatestVersion);
506
526
  b('display-warning', this.FDisplayWarning);
507
527
 
@@ -526,11 +546,11 @@ class TxParameters {
526
546
  };
527
547
 
528
548
  let s = '|'+this.count+'|'+this.limit+'|'+this.offset+
529
- this.FUid + '|' + b(this.FMembershipOnly) + '|' + this.FProperties.join(',') + '|' +
549
+ this.FUid + '|' + b(this.FMembershipOnly) + '|' + b(this.FVersionsMatch)+'|' + this.FProperties.join(',') + '|' +
530
550
  b(this.FActiveOnly) + b(this.FDisplayWarning) + b(this.FExcludeNested) + b(this.FGenerateNarrative) + b(this.FExcludeNotForUI) + b(this.FExcludePostCoordinated) +
531
551
  b(this.FIncludeDesignations) + b(this.FIncludeDefinition) + b(this.hasActiveOnly) + b(this.hasExcludeNested) + b(this.hasGenerateNarrative) +
532
552
  b(this.hasExcludeNotForUI) + b(this.hasExcludePostCoordinated) + b(this.hasIncludeDesignations) + this.sort+'|'+
533
- b(this.hasIncludeDefinition) + b(this.hasDefaultToLatestVersion) + b(this.hasDisplayWarning) + b(this.hasExcludeNotForUI) + b(this.hasMembershipOnly) + b(this.FDefaultToLatestVersion);
553
+ b(this.hasIncludeDefinition) + b(this.hasDefaultToLatestVersion) + b(this.hasDisplayWarning) + b(this.hasExcludeNotForUI) + b(this.hasMembershipOnly) + b(this.hasVersionsMatch) + b(this.FDefaultToLatestVersion);
534
554
 
535
555
  if (this.hasHTTPLanguages) {
536
556
  s = s + this.FHTTPLanguages.asString(true) + '|';
@@ -577,6 +597,7 @@ class TxParameters {
577
597
  this.FIncludeDefinition = other.FIncludeDefinition;
578
598
  this.FUid = other.FUid;
579
599
  this.FMembershipOnly = other.FMembershipOnly;
600
+ this.FVersionsMatch = other.FVersionsMatch;
580
601
  this.FDefaultToLatestVersion = other.FDefaultToLatestVersion;
581
602
  this.FDisplayWarning = other.FDisplayWarning;
582
603
  this.FDiagnostics = other.FDiagnostics;
@@ -588,7 +609,7 @@ class TxParameters {
588
609
  this.hasIncludeDesignations = other.hasIncludeDesignations;
589
610
  this.hasIncludeDefinition = other.hasIncludeDefinition;
590
611
  this.hasDefaultToLatestVersion = other.hasDefaultToLatestVersion;
591
- this.hasMembershipOnly = other.hasMembershipOnly;
612
+ this.hasVersionsMatch = other.hasVersionsMatch;
592
613
  this.hasDisplayWarning = other.hasDisplayWarning;
593
614
  this.sort = other.sort;
594
615
 
package/tx/provider.js CHANGED
@@ -23,6 +23,7 @@ const {PackageConceptMapProvider} = require("./cm/cm-package");
23
23
  class Provider {
24
24
  i18n;
25
25
  fhirVersion;
26
+ context;
26
27
 
27
28
  /**
28
29
  * {Map<String, CodeSystemFactoryProvider>} A list of code system factories that contains all the preloaded native code systems
@@ -34,6 +35,11 @@ class Provider {
34
35
  */
35
36
  codeSystems;
36
37
 
38
+ /**
39
+ * {List<AbstractCodeSystemProvider>} code system providers, for maintaing the code system list
40
+ */
41
+ codeSystemProviders
42
+
37
43
  /**
38
44
  * {List<AbstractValueSetProvider>} A list of value set providers that know how to provide value sets by request
39
45
  */
@@ -425,6 +431,47 @@ class Provider {
425
431
  }
426
432
  }
427
433
  return false;
434
+ }x
435
+
436
+ async updateCodeSystemList() {
437
+ for (let csp of this.codeSystemProviders) {
438
+ let changes = await csp.getCodeSystemChanges(this.fhirVersion, this.context);
439
+ if (changes) {
440
+ for (let cs of changes.added || []) {
441
+ this.addCodeSystem(cs);
442
+ }
443
+ for (let cs of changes.changed || []) {
444
+ this.addCodeSystem(cs);
445
+ }
446
+ for (let cs of changes.deleted || []) {
447
+ this.deleteCodeSystem(cs);
448
+ }
449
+ }
450
+ }
451
+ }
452
+
453
+ addCodeSystem(cs) {
454
+ const existing = this.codeSystems.get(cs.url);
455
+ if (!existing || cs.isMoreRecent(existing)) {
456
+ this.codeSystems.set(cs.url, cs);
457
+ }
458
+ if (cs.version) {
459
+ this.codeSystems.set(cs.vurl, cs);
460
+ }
461
+ }
462
+
463
+ deleteCodeSystem(cs) {
464
+ this.codeSystems.delete(cs.vurl);
465
+ this.codeSystems.delete(cs.url);
466
+ let existing = null;
467
+ for (let t of this.codeSystems.values()) {
468
+ if (!existing || t.isMoreRecent(existing)) {
469
+ existing = t;
470
+ }
471
+ }
472
+ if (existing) {
473
+ this.codeSystems.set(cs.url, cs);
474
+ }
428
475
  }
429
476
 
430
477
  }
package/tx/tx-html.js CHANGED
@@ -1246,7 +1246,7 @@ class TxHtmlRenderer {
1246
1246
  }
1247
1247
 
1248
1248
  buildSourceOptions(provider) {
1249
- let result = '';
1249
+ let result = '<option value=""></option>';
1250
1250
  result += `<option value="internal">internal</option>`;
1251
1251
  for (let sp of provider.listValueSetSourceCodes()) {
1252
1252
  result += `<option value="${sp}">${sp}</option>`;
package/tx/tx.js CHANGED
@@ -247,6 +247,14 @@ class TXModule {
247
247
  if (this.stats) {
248
248
  this.stats.addTask("Client Cache", "5 min");
249
249
  }
250
+ this.timers.push(setInterval(async () => {
251
+ try {
252
+ await endpointInfo.provider.updateCodeSystemList();
253
+ } catch (error) {
254
+ this.log.error(`Error updating CodeSystem list for ${endpointPath}: ${error.message}`);
255
+ }
256
+ }, 60 * 1000));
257
+ this.log.info(`CodeSystem list update scheduled for ${endpointPath}`);
250
258
  this.timers.push(setInterval(() => {
251
259
  endpointInfo.resourceCache.prune(cacheTimeoutMs);
252
260
  }, pruneIntervalMs));
package/tx/vs/vs-vsac.js CHANGED
@@ -4,6 +4,7 @@ const { AbstractValueSetProvider } = require('./vs-api');
4
4
  const { ValueSetDatabase } = require('./vs-database');
5
5
  const { VersionUtilities } = require('../../library/version-utilities');
6
6
  const folders = require('../../library/folder-setup');
7
+ const {debugLog} = require("../operation-context");
7
8
 
8
9
  /**
9
10
  * VSAC (Value Set Authority Center) ValueSet provider
@@ -178,7 +179,7 @@ class VSACValueSetProvider extends AbstractValueSetProvider {
178
179
  await this.processContentAndHistory(q, tracking, this.queue.length);
179
180
  } catch (error) {
180
181
  this.requeue.push(q)
181
- console.log(error);
182
+ debugLog(error);
182
183
  this.stats.task('VSAC Sync', error.message);
183
184
  }
184
185
  // `running (${totalFetched} fetched, ${totalNew} new)`)
@@ -190,7 +191,7 @@ class VSACValueSetProvider extends AbstractValueSetProvider {
190
191
  try {
191
192
  await this.processContentAndHistory(q, tracking, this.requeue.length);
192
193
  } catch (error) {
193
- console.log(error);
194
+ debugLog(error);
194
195
  this.stats.task('VSAC Sync', error.message);
195
196
  }
196
197
  // `running (${totalFetched} fetched, ${totalNew} new)`)
@@ -201,7 +202,7 @@ class VSACValueSetProvider extends AbstractValueSetProvider {
201
202
  await this._reloadMap();
202
203
  console.log(`VSAC refresh completed. Total: ${tracking.totalFetched} ValueSets, Deleted: ${tracking.deletedCount}`);
203
204
  } catch (error) {
204
- console.log(error, 'Error during VSAC refresh:');
205
+ debugLog(error, 'Error during VSAC refresh:');
205
206
  this.stats.task('VSAC Sync', `Error (${error.message})`);
206
207
  throw error;
207
208
  } finally {
@@ -15,6 +15,7 @@ const { TerminologyWorker } = require('./worker');
15
15
  const {OperationOutcome, Issue} = require("../library/operation-outcome");
16
16
  const {Parameters} = require("../library/parameters");
17
17
  const {ValidateWorker} = require("./validate");
18
+ const {debugLog} = require("../operation-context");
18
19
 
19
20
  class BatchValidateWorker extends TerminologyWorker {
20
21
 
@@ -82,7 +83,7 @@ class BatchValidateWorker extends TerminologyWorker {
82
83
  output.push({name: "validation", resource : p});
83
84
  } catch (error) {
84
85
  this.log.error(error);
85
- this.debugLog(error);
86
+ debugLog(error);
86
87
  if (error instanceof Issue) {
87
88
  let op = new OperationOutcome();
88
89
  op.addIssue(error);
@@ -98,7 +99,7 @@ class BatchValidateWorker extends TerminologyWorker {
98
99
  return res.json(result);
99
100
  } catch (error) {
100
101
  this.log.error(error);
101
- this.debugLog(error);
102
+ debugLog(error);
102
103
  return res.status(error.statusCode || 500).json(this.operationOutcome(
103
104
  'error', error.issueCode || 'exception', error.message));
104
105
  }
@@ -6,6 +6,7 @@
6
6
 
7
7
  const { TerminologyWorker } = require('./worker');
8
8
  const { Issue, OperationOutcome } = require('../library/operation-outcome');
9
+ const {debugLog} = require("../operation-context");
9
10
 
10
11
  class BatchWorker extends TerminologyWorker {
11
12
  /**
@@ -40,7 +41,7 @@ class BatchWorker extends TerminologyWorker {
40
41
  await this.handleBatch(req, res);
41
42
  } catch (error) {
42
43
  this.log.error(error);
43
- this.debugLog(error);
44
+ debugLog(error);
44
45
  if (error instanceof Issue) {
45
46
  const oo = new OperationOutcome();
46
47
  oo.addIssue(error);
@@ -160,7 +161,7 @@ class BatchWorker extends TerminologyWorker {
160
161
 
161
162
  } catch (error) {
162
163
  this.log.error(error);
163
- this.debugLog(error);
164
+ debugLog(error);
164
165
  const statusCode = error.statusCode || 500;
165
166
  const issueCode = error.issueCode || 'exception';
166
167
 
@@ -18,6 +18,7 @@ const {Issue, OperationOutcome} = require("../library/operation-outcome");
18
18
  const crypto = require('crypto');
19
19
  const ValueSet = require("../library/valueset");
20
20
  const {VersionUtilities} = require("../../library/version-utilities");
21
+ const {debugLog} = require("../operation-context");
21
22
 
22
23
  // Expansion limits (from Pascal constants)
23
24
  const EXTERNAL_DEFAULT_LIMIT = 1000;
@@ -197,6 +198,7 @@ class ValueSetCounter {
197
198
  class ValueSetExpander {
198
199
  worker;
199
200
  params;
201
+ doingVersion = true;
200
202
  excludedSystems = new Set();
201
203
  excluded = new Set();
202
204
  hasExclusions = false;
@@ -495,7 +497,8 @@ class ValueSetExpander {
495
497
  }
496
498
  }
497
499
 
498
- this.excluded.add(system + '|' + version + '#' + code);
500
+ let key = (this.doingVersion && !this.params.versionsMatch ? system + '|' + version : system) + '#' + code
501
+ this.excluded.add(key);
499
502
  }
500
503
 
501
504
  async checkCanExpandValueSet(uri, version) {
@@ -922,8 +925,20 @@ class ValueSetExpander {
922
925
  if (!cset.concept && !cset.filter) {
923
926
  this.worker.opContext.log('handle system');
924
927
  if (!cset.valueSet) {
925
- // excluding a whole system - we don't list the codes in this case
926
- this.excludedSystems.add(cset.system + (cset.version ? '|'+cset.version : ''));
928
+ if (!this.excludeSpecialCase) {
929
+ // excluding a whole system - we don't list the codes in this case
930
+ this.excludedSystems.add(cset.system + (this.doingVersion && cset.version ? '|' + cset.version : ''));
931
+ } else {
932
+ const iter = await cs.iteratorAll();
933
+ if (iter) {
934
+ let c = await cs.nextContext(iter);
935
+ while (c) {
936
+ this.worker.deadCheck('processCodes#3aa');
937
+ this.excludeCode(cs, cs.system(), cs.version(), await cs.code(c), expansion, valueSets, vsSrc.url);
938
+ c = await cs.nextContext(iter);
939
+ }
940
+ }
941
+ }
927
942
  } else {
928
943
  if (cs.isNotClosed(filter)) {
929
944
  if (cs.specialEnumeration()) {
@@ -1081,6 +1096,7 @@ class ValueSetExpander {
1081
1096
  async handleCompose(source, filter, expansion, notClosed, vsInfo) {
1082
1097
  this.worker.opContext.log('compose #1');
1083
1098
 
1099
+ this.doingVersion = false;
1084
1100
  const ts = new Map();
1085
1101
  for (const c of source.jsonObj.compose.include || []) {
1086
1102
  this.worker.deadCheck('handleCompose#2');
@@ -1097,6 +1113,8 @@ class ValueSetExpander {
1097
1113
  if (vsInfo.handleByCS) {
1098
1114
  await this.processCodes("ValueSet.compose", source, source.jsonObj.compose, filter, expansion, this.excludeInactives(source), notClosed, vsInfo);
1099
1115
  } else {
1116
+ this.checkForExclusionVersionSpecialCase(source, expansion);
1117
+
1100
1118
  let i = 0;
1101
1119
  for (const c of source.jsonObj.compose.exclude || []) {
1102
1120
  this.worker.deadCheck('handleCompose#4');
@@ -1104,7 +1122,14 @@ class ValueSetExpander {
1104
1122
  }
1105
1123
 
1106
1124
  i = 0;
1107
- for (const c of source.jsonObj.compose.include || []) {
1125
+ const includes = [...(source.jsonObj.compose.include || [])];
1126
+ includes.sort((a, b) => {
1127
+ if (a.system === b.system && a.version && b.version) {
1128
+ return -VersionUtilities.compareVersionsGeneral(a.version, b.version);
1129
+ }
1130
+ return 0;
1131
+ });
1132
+ for (const c of includes) {
1108
1133
  this.worker.deadCheck('handleCompose#5');
1109
1134
  await this.includeCodes(c, "ValueSet.compose.include[" + i + "]", source, source.jsonObj.compose, filter, expansion, this.excludeInactives(source), notClosed);
1110
1135
  i++;
@@ -1197,6 +1222,9 @@ class ValueSetExpander {
1197
1222
  if (this.params.hasExcludeNested) {
1198
1223
  this.addParamBool(exp, 'excludeNested', this.params.excludeNested);
1199
1224
  }
1225
+ if (this.params.versionsMatch) {
1226
+ this.addParamBool(exp, 'versionsMatch', this.params.versionsMatch);
1227
+ }
1200
1228
  if (this.params.hasActiveOnly) {
1201
1229
  this.addParamBool(exp, 'activeOnly', this.params.activeOnly);
1202
1230
  }
@@ -1481,14 +1509,16 @@ class ValueSetExpander {
1481
1509
  if (this.excludedSystems.has(system)) {
1482
1510
  return true;
1483
1511
  }
1484
- if (this.excludedSystems.has(system+'|'+version)) {
1512
+ let key = this.doingVersion && !this.params.versionsMatch? system+'|'+version : system;
1513
+ if (this.excludedSystems.has(key)) {
1485
1514
  return true;
1486
1515
  }
1487
- return this.excluded.has(system+'|'+version+'#'+code);
1516
+ key = (this.doingVersion && !this.params.versionsMatch ? system+'|'+version : system)+'#'+code;
1517
+ return this.excluded.has(key);
1488
1518
  }
1489
1519
 
1490
1520
  keyS(system, version, code) {
1491
- return system+"~"+(this.doingVersion ? version+"~" : "")+code;
1521
+ return system+"~"+(this.doingVersion && !this.params.versionsMatch ? version+"~" : "")+code;
1492
1522
  }
1493
1523
 
1494
1524
  keyC(contains) {
@@ -1636,6 +1666,31 @@ class ValueSetExpander {
1636
1666
  }
1637
1667
  return null;
1638
1668
  }
1669
+
1670
+
1671
+ // special case: excluding a different version from the include
1672
+ checkForExclusionVersionSpecialCase(source, exp) {
1673
+ if (!this.params.hasVersionsMatch) {
1674
+
1675
+
1676
+ const includes = source.jsonObj.compose.include || [];
1677
+ const excludes = source.jsonObj.compose.exclude || [];
1678
+
1679
+ if (includes.length > 0 && excludes.length > 0) {
1680
+ const system = includes[0].system;
1681
+ const allSameSystem = includes.every(i => i.system === system && i.version)
1682
+ && excludes.every(e => e.system === system && e.version);
1683
+ const noOverlap = !includes.some(i => excludes.some(e => e.version === i.version));
1684
+
1685
+ if (allSameSystem && noOverlap) {
1686
+ this.params.versionsMatch = true;
1687
+ this.excludeSpecialCase = true;
1688
+ this.addParamBool(exp, 'versionsMatch', this.params.versionsMatch);
1689
+
1690
+ }
1691
+ }
1692
+ }
1693
+ }
1639
1694
  }
1640
1695
 
1641
1696
  class ExpandWorker extends TerminologyWorker {
@@ -1676,7 +1731,7 @@ class ExpandWorker extends TerminologyWorker {
1676
1731
  await this.handleTypeLevelExpand(req, res);
1677
1732
  } catch (error) {
1678
1733
  this.log.error(error);
1679
- this.debugLog(error);
1734
+ debugLog(error);
1680
1735
  req.logInfo = this.usedSources.join("|")+" - error"+(error.msgId ? " "+error.msgId : "");
1681
1736
  const statusCode = error.statusCode || 500;
1682
1737
  if (error instanceof Issue) {
@@ -1711,7 +1766,7 @@ class ExpandWorker extends TerminologyWorker {
1711
1766
  await this.handleInstanceLevelExpand(req, res);
1712
1767
  } catch (error) {
1713
1768
  this.log.error(error);
1714
- this.debugLog(error);
1769
+ debugLog(error);
1715
1770
  req.logInfo = this.usedSources.join("|")+" - error"+(error.msgId ? " "+error.msgId : "");
1716
1771
  const statusCode = error.statusCode || 500;
1717
1772
  const issueCode = error.issueCode || 'exception';
@@ -13,6 +13,7 @@ const { Designations} = require("../library/designations");
13
13
  const {TxParameters} = require("../params");
14
14
  const {Parameters} = require("../library/parameters");
15
15
  const {Issue, OperationOutcome} = require("../library/operation-outcome");
16
+ const {debugLog} = require("../operation-context");
16
17
 
17
18
  class LookupWorker extends TerminologyWorker {
18
19
  /**
@@ -45,7 +46,7 @@ class LookupWorker extends TerminologyWorker {
45
46
  await this.handleTypeLevelLookup(req, res);
46
47
  } catch (error) {
47
48
  this.log.error(error);
48
- this.debugLog(error);
49
+ debugLog(error);
49
50
  req.logInfo = this.usedSources.join("|")+" - error"+(error.msgId ? " "+error.msgId : "");
50
51
  const statusCode = error.statusCode || 500;
51
52
  const issueCode = error.issueCode || 'exception';
@@ -72,7 +73,7 @@ class LookupWorker extends TerminologyWorker {
72
73
  await this.handleInstanceLevelLookup(req, res);
73
74
  } catch (error) {
74
75
  this.log.error(error);
75
- this.debugLog(error);
76
+ debugLog(error);
76
77
  req.logInfo = this.usedSources.join("|")+" - error"+(error.msgId ? " "+error.msgId : "");
77
78
  const issueCode = error.issueCode || 'exception';
78
79
  return res.status(400).json({
@@ -159,7 +160,7 @@ class LookupWorker extends TerminologyWorker {
159
160
  return res.status(200).json(result);
160
161
  } catch (error) {
161
162
  this.log.error(error);
162
- this.debugLog(error);
163
+ debugLog(error);
163
164
  req.logInfo = this.usedSources.join("|")+" - error"+(error.msgId ? " "+error.msgId : "");
164
165
  if (error instanceof Issue) {
165
166
  let oo = new OperationOutcome();
@@ -223,7 +224,7 @@ class LookupWorker extends TerminologyWorker {
223
224
  return res.status(200).json(result);
224
225
  } catch (error) {
225
226
  this.log.error(error);
226
- this.debugLog(error);
227
+ debugLog(error);
227
228
  req.logInfo = this.usedSources.join("|")+" - error"+(error.msgId ? " "+error.msgId : "");
228
229
  if (error instanceof Issue) {
229
230
  let oo = new OperationOutcome();
@@ -5,6 +5,7 @@
5
5
  //
6
6
 
7
7
  const { TerminologyWorker } = require('./worker');
8
+ const {debugLog} = require("../operation-context");
8
9
 
9
10
  class ReadWorker extends TerminologyWorker {
10
11
  /**
@@ -67,7 +68,7 @@ class ReadWorker extends TerminologyWorker {
67
68
  }
68
69
  } catch (error) {
69
70
  this.log.error(error);
70
- this.debugLog(error);
71
+ debugLog(error);
71
72
  req.logInfo = this.usedSources.join("|")+" - error"+(error.msgId ? " "+error.msgId : "");
72
73
  return res.status(500).json({
73
74
  resourceType: 'OperationOutcome',
@@ -16,6 +16,7 @@ const ValueSet = require("../library/valueset");
16
16
  const {ValueSetExpander} = require("./expand");
17
17
  const {SearchFilterText} = require("../library/designations");
18
18
  const {ArrayMatcher} = require("../../library/utilities");
19
+ const {debugLog} = require("../operation-context");
19
20
 
20
21
 
21
22
  class RelatedWorker extends TerminologyWorker {
@@ -49,7 +50,7 @@ class RelatedWorker extends TerminologyWorker {
49
50
  await this.handleTypeLevelRelated(req, res);
50
51
  } catch (error) {
51
52
  this.log.error(error);
52
- this.debugLog(error);
53
+ debugLog(error);
53
54
  req.logInfo = this.usedSources.join("|")+" - error"+(error.msgId ? " "+error.msgId : "");
54
55
  const statusCode = error.statusCode || 500;
55
56
  if (error instanceof Issue) {
@@ -84,7 +85,7 @@ class RelatedWorker extends TerminologyWorker {
84
85
  await this.handleInstanceLevelRelated(req, res);
85
86
  } catch (error) {
86
87
  this.log.error(error);
87
- this.debugLog(error);
88
+ debugLog(error);
88
89
  req.logInfo = this.usedSources.join("|")+" - error"+(error.msgId ? " "+error.msgId : "");
89
90
  const statusCode = error.statusCode || 500;
90
91
  const issueCode = error.issueCode || 'exception';