fhirsmith 0.4.2 → 0.5.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.
Files changed (92) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +1 -1
  3. package/library/cron-utilities.js +136 -0
  4. package/library/html-server.js +13 -29
  5. package/library/html.js +3 -8
  6. package/library/languages.js +160 -37
  7. package/library/package-manager.js +48 -1
  8. package/library/utilities.js +100 -19
  9. package/package.json +2 -2
  10. package/packages/package-crawler.js +6 -1
  11. package/packages/packages.js +38 -54
  12. package/publisher/publisher.js +19 -27
  13. package/registry/api.js +11 -10
  14. package/registry/crawler.js +31 -29
  15. package/registry/model.js +5 -26
  16. package/registry/registry.js +32 -41
  17. package/server.js +89 -12
  18. package/shl/shl.js +0 -18
  19. package/static/assets/js/statuspage.js +1 -9
  20. package/stats.js +39 -1
  21. package/token/token.js +14 -9
  22. package/translations/Messages.properties +2 -1
  23. package/tx/README.md +17 -6
  24. package/tx/cs/cs-api.js +19 -1
  25. package/tx/cs/cs-base.js +77 -0
  26. package/tx/cs/cs-country.js +46 -0
  27. package/tx/cs/cs-cpt.js +9 -5
  28. package/tx/cs/cs-cs.js +27 -13
  29. package/tx/cs/cs-lang.js +60 -22
  30. package/tx/cs/cs-loinc.js +69 -98
  31. package/tx/cs/cs-mimetypes.js +4 -0
  32. package/tx/cs/cs-ndc.js +6 -0
  33. package/tx/cs/cs-omop.js +16 -15
  34. package/tx/cs/cs-rxnorm.js +23 -1
  35. package/tx/cs/cs-snomed.js +283 -40
  36. package/tx/cs/cs-ucum.js +90 -70
  37. package/tx/importers/import-sct.module.js +371 -35
  38. package/tx/importers/readme.md +117 -7
  39. package/tx/library/bundle.js +5 -0
  40. package/tx/library/capabilitystatement.js +3 -142
  41. package/tx/library/codesystem.js +19 -173
  42. package/tx/library/conceptmap.js +4 -218
  43. package/tx/library/designations.js +14 -1
  44. package/tx/library/extensions.js +7 -0
  45. package/tx/library/namingsystem.js +3 -89
  46. package/tx/library/operation-outcome.js +8 -3
  47. package/tx/library/parameters.js +3 -2
  48. package/tx/library/renderer.js +10 -6
  49. package/tx/library/terminologycapabilities.js +3 -243
  50. package/tx/library/valueset.js +3 -235
  51. package/tx/library.js +100 -13
  52. package/tx/operation-context.js +23 -4
  53. package/tx/params.js +35 -38
  54. package/tx/provider.js +6 -5
  55. package/tx/sct/expressions.js +12 -3
  56. package/tx/tx-html.js +80 -89
  57. package/tx/tx.fhir.org.yml +6 -5
  58. package/tx/tx.js +163 -13
  59. package/tx/vs/vs-database.js +56 -39
  60. package/tx/vs/vs-package.js +21 -2
  61. package/tx/vs/vs-vsac.js +175 -39
  62. package/tx/workers/batch-validate.js +2 -0
  63. package/tx/workers/batch.js +2 -0
  64. package/tx/workers/expand.js +132 -112
  65. package/tx/workers/lookup.js +33 -14
  66. package/tx/workers/metadata.js +2 -2
  67. package/tx/workers/read.js +3 -2
  68. package/tx/workers/related.js +574 -0
  69. package/tx/workers/search.js +46 -9
  70. package/tx/workers/subsumes.js +13 -3
  71. package/tx/workers/translate.js +7 -3
  72. package/tx/workers/validate.js +258 -285
  73. package/tx/workers/worker.js +43 -39
  74. package/tx/xml/bundle-xml.js +237 -0
  75. package/tx/xml/xml-base.js +215 -64
  76. package/tx/xversion/xv-bundle.js +71 -0
  77. package/tx/xversion/xv-capabiliityStatement.js +137 -0
  78. package/tx/xversion/xv-codesystem.js +169 -0
  79. package/tx/xversion/xv-conceptmap.js +224 -0
  80. package/tx/xversion/xv-namingsystem.js +88 -0
  81. package/tx/xversion/xv-operationoutcome.js +27 -0
  82. package/tx/xversion/xv-parameters.js +87 -0
  83. package/tx/xversion/xv-resource.js +45 -0
  84. package/tx/xversion/xv-terminologyCapabilities.js +214 -0
  85. package/tx/xversion/xv-valueset.js +234 -0
  86. package/utilities/dev-proxy-server.js +126 -0
  87. package/utilities/explode-results.js +58 -0
  88. package/utilities/split-by-system.js +198 -0
  89. package/utilities/vsac-cs-fetcher.js +0 -0
  90. package/{windows-install.js → utilities/windows-install.js} +2 -0
  91. package/vcl/vcl.js +0 -18
  92. package/xig/xig.js +241 -230
@@ -1,5 +1,5 @@
1
1
  const {CanonicalResource} = require("./canonical-resource");
2
- const {VersionUtilities} = require("../../library/version-utilities");
2
+ const {capabilityStatementFromR5, capabilityStatementToR5} = require("../xversion/xv-capabiliityStatement");
3
3
 
4
4
  /**
5
5
  * Represents a FHIR CapabilityStatement resource with version conversion support
@@ -15,7 +15,7 @@ class CapabilityStatement extends CanonicalResource {
15
15
  constructor(jsonObj, fhirVersion = 'R5') {
16
16
  super(jsonObj, fhirVersion);
17
17
  // Convert to R5 format internally (modifies input for performance)
18
- this.jsonObj = this._convertToR5(jsonObj, fhirVersion);
18
+ this.jsonObj = capabilityStatementToR5(jsonObj, fhirVersion);
19
19
  this.validate();
20
20
  this.id = this.jsonObj.id;
21
21
  }
@@ -46,146 +46,7 @@ class CapabilityStatement extends CanonicalResource {
46
46
  * @returns {Object} JSON object
47
47
  */
48
48
  toJSON(version = 'R5') {
49
- return this._convertFromR5(this.jsonObj, version);
50
- }
51
-
52
- /**
53
- * Converts input CapabilityStatement to R5 format (modifies input object for performance)
54
- * @param {Object} jsonObj - The input CapabilityStatement object
55
- * @param {string} version - Source FHIR version
56
- * @returns {Object} The same object, potentially modified to R5 format
57
- * @private
58
- */
59
- _convertToR5(jsonObj, version) {
60
- if (version === 'R5') {
61
- return jsonObj; // Already R5, no conversion needed
62
- }
63
-
64
- if (version === 'R3') {
65
- // R3: resourceType was "CapabilityStatement" (same as R4/R5)
66
- // Convert identifier from single object to array if present
67
- if (jsonObj.identifier && !Array.isArray(jsonObj.identifier)) {
68
- jsonObj.identifier = [jsonObj.identifier];
69
- }
70
- return jsonObj;
71
- }
72
-
73
- if (version === 'R4') {
74
- // R4 to R5: No major structural changes needed
75
- return jsonObj;
76
- }
77
-
78
- throw new Error(`Unsupported FHIR version: ${version}`);
79
- }
80
-
81
- /**
82
- * Converts R5 CapabilityStatement to target version format (clones object first)
83
- * @param {Object} r5Obj - The R5 format CapabilityStatement object
84
- * @param {string} targetVersion - Target FHIR version
85
- * @returns {Object} New object in target version format
86
- * @private
87
- */
88
- _convertFromR5(r5Obj, targetVersion) {
89
- if (VersionUtilities.isR5Ver(targetVersion)) {
90
- return r5Obj; // No conversion needed
91
- }
92
-
93
- // Clone the object to avoid modifying the original
94
- const cloned = JSON.parse(JSON.stringify(r5Obj));
95
-
96
- if (VersionUtilities.isR4Ver(targetVersion)) {
97
- return this._convertR5ToR4(cloned);
98
- } else if (VersionUtilities.isR3Ver(targetVersion)) {
99
- return this._convertR5ToR3(cloned);
100
- }
101
-
102
- throw new Error(`Unsupported target FHIR version: ${targetVersion}`);
103
- }
104
-
105
- /**
106
- * Converts R5 CapabilityStatement to R4 format
107
- * @param {Object} r5Obj - Cloned R5 CapabilityStatement object
108
- * @returns {Object} R4 format CapabilityStatement
109
- * @private
110
- */
111
- _convertR5ToR4(r5Obj) {
112
- // Remove R5-specific elements
113
- if (r5Obj.versionAlgorithmString) {
114
- delete r5Obj.versionAlgorithmString;
115
- }
116
- if (r5Obj.versionAlgorithmCoding) {
117
- delete r5Obj.versionAlgorithmCoding;
118
- }
119
-
120
- return r5Obj;
121
- }
122
-
123
- /**
124
- * Converts R5 CapabilityStatement to R3 format
125
- * @param {Object} r5Obj - Cloned R5 CapabilityStatement object
126
- * @returns {Object} R3 format CapabilityStatement
127
- * @private
128
- */
129
- _convertR5ToR3(r5Obj) {
130
- // First apply R4 conversions
131
- const r4Obj = this._convertR5ToR4(r5Obj);
132
-
133
- // Convert identifier array back to single object
134
- if (r4Obj.identifier && Array.isArray(r4Obj.identifier)) {
135
- if (r4Obj.identifier.length > 0) {
136
- r4Obj.identifier = r4Obj.identifier[0];
137
- } else {
138
- delete r4Obj.identifier;
139
- }
140
- }
141
-
142
- // Convert valueCanonical to valueUri throughout the object
143
- this._convertCanonicalToUri(r5Obj);
144
-
145
-
146
- // Convert rest.operation.definition from canonical string to Reference object
147
- for (const rest of r4Obj.rest || []) {
148
- for (const operation of rest.operation || []) {
149
- if (typeof operation.definition === 'string') {
150
- operation.definition = {reference: operation.definition};
151
- }
152
- for (const resource of rest.resource || []) {
153
- delete resource.operation;
154
- }
155
- }
156
- }
157
-
158
- return r4Obj;
159
- }
160
-
161
- /**
162
- * Recursively converts valueCanonical to valueUri in an object
163
- * R3 doesn't have canonical type, so valueCanonical must become valueUri
164
- * @param {Object} obj - Object to convert
165
- * @private
166
- */
167
- _convertCanonicalToUri(obj) {
168
- if (!obj || typeof obj !== 'object') {
169
- return;
170
- }
171
-
172
- if (Array.isArray(obj)) {
173
- obj.forEach(item => this._convertCanonicalToUri(item));
174
- return;
175
- }
176
-
177
- // Convert valueCanonical to valueUri
178
- if (obj.valueCanonical !== undefined) {
179
- obj.valueUri = obj.valueCanonical;
180
- delete obj.valueCanonical;
181
- }
182
-
183
- // Recurse into all properties
184
- for (const key of Object.keys(obj)) {
185
- if (typeof obj[key] === 'object') {
186
- this._convertCanonicalToUri(obj[key]);
187
- }
188
- }
49
+ return capabilityStatementFromR5(this.jsonObj, version);
189
50
  }
190
51
 
191
52
  /**
@@ -1,6 +1,7 @@
1
1
  const { Language } = require("../../library/languages");
2
2
  const {CanonicalResource} = require("./canonical-resource");
3
- const {VersionUtilities} = require("../../library/version-utilities");
3
+ const {codeSystemFromR5, codeSystemToR5} = require("../xversion/xv-codesystem");
4
+ const {getValuePrimitive} = require("../../library/utilities");
4
5
 
5
6
  const CodeSystemContentMode = Object.freeze({
6
7
  Complete: 'complete',
@@ -27,12 +28,14 @@ class CodeSystem extends CanonicalResource {
27
28
  * @param {string} jsonObj.status - Publication status (draft|active|retired|unknown)
28
29
  * @param {Object[]} [jsonObj.concept] - Array of concept definitions
29
30
  */
30
- constructor(jsonObj, fhirVersion = 'R5') {
31
+ constructor(jsonObj, fhirVersion = 'R5', noMaps = false) {
31
32
  super(jsonObj, fhirVersion);
32
33
  // Convert to R5 format internally (modifies input for performance)
33
- this.jsonObj = this._convertToR5(this.jsonObj, fhirVersion);
34
- this.validate();
35
- this.buildMaps();
34
+ this.jsonObj = codeSystemToR5(this.jsonObj, fhirVersion);
35
+ if (!noMaps) {
36
+ this.validate();
37
+ this.buildMaps();
38
+ }
36
39
  }
37
40
 
38
41
  /**
@@ -81,175 +84,10 @@ class CodeSystem extends CanonicalResource {
81
84
  * @returns {string} JSON string
82
85
  */
83
86
  toJSONString(version = 'R5') {
84
- const outputObj = this._convertFromR5(this.jsonObj, version);
87
+ const outputObj = codeSystemFromR5(this.jsonObj, version);
85
88
  return JSON.stringify(outputObj);
86
89
  }
87
90
 
88
- /**
89
- * Converts input CodeSystem to R5 format (modifies input object for performance)
90
- * @param {Object} jsonObj - The input CodeSystem object
91
- * @param {string} version - Source FHIR version
92
- * @returns {Object} The same object, potentially modified to R5 format
93
- * @private
94
- */
95
- _convertToR5(jsonObj, version) {
96
- if (version === 'R5') {
97
- return jsonObj; // Already R5, no conversion needed
98
- }
99
-
100
- if (version === 'R3') {
101
- // R3 to R5: Convert identifier from single object to array
102
- if (jsonObj.identifier && !Array.isArray(jsonObj.identifier)) {
103
- jsonObj.identifier = [jsonObj.identifier];
104
- }
105
- return jsonObj;
106
- }
107
-
108
- if (version === 'R4') {
109
- // R4 to R5: identifier is already an array, no conversion needed
110
- return jsonObj;
111
- }
112
-
113
- throw new Error(`Unsupported FHIR version: ${version}`);
114
- }
115
-
116
- /**
117
- * Converts R5 CodeSystem to target version format (clones object first)
118
- * @param {Object} r5Obj - The R5 format CodeSystem object
119
- * @param {string} targetVersion - Target FHIR version
120
- * @returns {Object} New object in target version format
121
- * @private
122
- */
123
- _convertFromR5(r5Obj, targetVersion) {
124
- if (VersionUtilities.isR5Ver(targetVersion)) {
125
- return r5Obj; // No conversion needed
126
- }
127
-
128
- // Clone the object to avoid modifying the original
129
- const cloned = JSON.parse(JSON.stringify(r5Obj));
130
-
131
- if (VersionUtilities.isR4Ver(targetVersion)) {
132
- return this._convertR5ToR4(cloned);
133
- } else if (VersionUtilities.isR3Ver(targetVersion)) {
134
- return this._convertR5ToR3(cloned);
135
- }
136
-
137
- throw new Error(`Unsupported target FHIR version: ${targetVersion}`);
138
- }
139
-
140
- /**
141
- * Converts R5 CodeSystem to R4 format
142
- * @param {Object} r5Obj - Cloned R5 CodeSystem object
143
- * @returns {Object} R4 format CodeSystem
144
- * @private
145
- */
146
- _convertR5ToR4(r5Obj) {
147
- // Remove R5-specific elements that don't exist in R4
148
- if (r5Obj.versionAlgorithmString) {
149
- delete r5Obj.versionAlgorithmString;
150
- }
151
- if (r5Obj.versionAlgorithmCoding) {
152
- delete r5Obj.versionAlgorithmCoding;
153
- }
154
-
155
- // Filter out R5-only filter operators
156
- if (r5Obj.filter && Array.isArray(r5Obj.filter)) {
157
- r5Obj.filter = r5Obj.filter.map(filter => {
158
- if (filter.operator && Array.isArray(filter.operator)) {
159
- // Remove R5-only operators like 'generalizes'
160
- filter.operator = filter.operator.filter(op =>
161
- !this._isR5OnlyFilterOperator(op)
162
- );
163
- }
164
- return filter;
165
- }).filter(filter =>
166
- // Remove filters that have no valid operators left
167
- !filter.operator || filter.operator.length > 0
168
- );
169
- }
170
-
171
- return r5Obj;
172
- }
173
-
174
- /**
175
- * Converts R5 CodeSystem to R3 format
176
- * @param {Object} r5Obj - Cloned R5 CodeSystem object
177
- * @returns {Object} R3 format CodeSystem
178
- * @private
179
- */
180
- _convertR5ToR3(r5Obj) {
181
- // First apply R4 conversions
182
- const r4Obj = this._convertR5ToR4(r5Obj);
183
-
184
- // R5/R4 to R3: Convert identifier from array back to single object
185
- if (r4Obj.identifier && Array.isArray(r4Obj.identifier)) {
186
- if (r4Obj.identifier.length > 0) {
187
- // Take the first identifier if multiple exist
188
- r4Obj.identifier = r4Obj.identifier[0];
189
- } else {
190
- // Remove empty array
191
- delete r4Obj.identifier;
192
- }
193
- }
194
-
195
- // Remove additional R4-specific elements that don't exist in R3
196
- if (r4Obj.supplements) {
197
- delete r4Obj.supplements;
198
- }
199
-
200
- // R3 has more limited filter operator support
201
- if (r4Obj.filter && Array.isArray(r4Obj.filter)) {
202
- r4Obj.filter = r4Obj.filter.map(filter => {
203
- if (filter.operator && Array.isArray(filter.operator)) {
204
- // Keep only R3-compatible operators
205
- filter.operator = filter.operator.filter(op =>
206
- this._isR3CompatibleFilterOperator(op)
207
- );
208
- }
209
- return filter;
210
- }).filter(filter =>
211
- // Remove filters that have no valid operators left
212
- !filter.operator || filter.operator.length > 0
213
- );
214
- }
215
-
216
- return r4Obj;
217
- }
218
-
219
- /**
220
- * Checks if a filter operator is R5-only
221
- * @param {string} operator - Filter operator code
222
- * @returns {boolean} True if operator is R5-only
223
- * @private
224
- */
225
- _isR5OnlyFilterOperator(operator) {
226
- const r5OnlyOperators = [
227
- 'generalizes', // Added in R5
228
- // Add other R5-only operators as they're identified
229
- ];
230
- return r5OnlyOperators.includes(operator);
231
- }
232
-
233
- /**
234
- * Checks if a filter operator is compatible with R3
235
- * @param {string} operator - Filter operator code
236
- * @returns {boolean} True if operator is R3-compatible
237
- * @private
238
- */
239
- _isR3CompatibleFilterOperator(operator) {
240
- const r3CompatibleOperators = [
241
- '=', // Equal
242
- 'is-a', // Is-A relationship
243
- 'descendent-of', // Descendant of (note: R3 spelling)
244
- 'is-not-a', // Is-Not-A relationship
245
- 'regex', // Regular expression
246
- 'in', // In set
247
- 'not-in', // Not in set
248
- 'exists', // Property exists
249
- ];
250
- return r3CompatibleOperators.includes(operator);
251
- }
252
-
253
91
  /**
254
92
  * Validates that this is a proper CodeSystem resource
255
93
  * @throws {Error} If validation fails
@@ -510,11 +348,11 @@ class CodeSystem extends CanonicalResource {
510
348
  }
511
349
 
512
350
  concept.property.forEach(property => {
513
- if (property.code === 'parent' && property.valueCode) {
351
+ if ((property.code === 'parent' || this.isPropUri(property.code, 'http://hl7.org/fhir/concept-properties#parent')) && property.valueCode) {
514
352
  // This concept has a parent
515
353
  this._addToChildToParentsMap(concept.code, property.valueCode);
516
354
  this._addToParentToChildrenMap(property.valueCode, concept.code);
517
- } else if (property.code === 'child' && property.valueCode) {
355
+ } else if ((property.code === 'child' || this.isPropUri(property.code, 'http://hl7.org/fhir/concept-properties#child')) && property.valueCode) {
518
356
  // This concept has a child
519
357
  this._addToParentToChildrenMap(concept.code, property.valueCode);
520
358
  this._addToChildToParentsMap(property.valueCode, concept.code);
@@ -769,6 +607,14 @@ class CodeSystem extends CanonicalResource {
769
607
  set id(value) {
770
608
  this.jsonObj.id = value;
771
609
  }
610
+
611
+ isLangPack() {
612
+ return (this.jsonObj.extension || []).find(x => x.url == 'http://hl7.org/fhir/StructureDefinition/codesystem-supplement-type' && getValuePrimitive(x) == 'lang-pack');
613
+ }
614
+
615
+ isPropUri(code, uri) {
616
+ return (this.jsonObj.property || []).find(x => x.code == code && x.uri == uri);
617
+ }
772
618
  }
773
619
 
774
620
  module.exports = { CodeSystem, CodeSystemContentMode };
@@ -1,5 +1,6 @@
1
1
  const {CanonicalResource} = require("./canonical-resource");
2
2
  const {VersionUtilities} = require("../../library/version-utilities");
3
+ const {conceptMapToR5, conceptMapFromR5} = require("../xversion/xv-conceptmap");
3
4
 
4
5
  /**
5
6
  * Represents a FHIR ConceptMap resource with version conversion support
@@ -16,7 +17,7 @@ class ConceptMap extends CanonicalResource {
16
17
  constructor(jsonObj, fhirVersion = 'R5') {
17
18
  super(jsonObj, fhirVersion);
18
19
  // Convert to R5 format internally (modifies input for performance)
19
- this.jsonObj = this._convertToR5(jsonObj, fhirVersion);
20
+ this.jsonObj = conceptMapToR5(jsonObj, fhirVersion);
20
21
  this.validate();
21
22
  this.id = this.jsonObj.id;
22
23
  }
@@ -37,226 +38,11 @@ class ConceptMap extends CanonicalResource {
37
38
  * @returns {string} JSON string
38
39
  */
39
40
  toJSONString(version = 'R5') {
40
- const outputObj = this._convertFromR5(this.jsonObj, version);
41
+ const outputObj = conceptMapFromR5(this.jsonObj, version);
41
42
  return JSON.stringify(outputObj);
42
43
  }
43
44
 
44
- /**
45
- * Converts input ConceptMap to R5 format (modifies input object for performance)
46
- * @param {Object} jsonObj - The input ConceptMap object
47
- * @param {string} version - Source FHIR version
48
- * @returns {Object} The same object, potentially modified to R5 format
49
- * @private
50
- */
51
- _convertToR5(jsonObj, version) {
52
- if (VersionUtilities.isR5Ver(version)) {
53
- return jsonObj; // Already R5, no conversion needed
54
- }
55
-
56
- if (VersionUtilities.isR3Ver(version) || VersionUtilities.isR4Ver(version)) {
57
- // Convert identifier from single object to array
58
- if (jsonObj.identifier && !Array.isArray(jsonObj.identifier)) {
59
- jsonObj.identifier = [jsonObj.identifier];
60
- }
61
-
62
- // Convert source/target to sourceScope/targetScope
63
- if (jsonObj.source !== undefined) {
64
- // Combine source + sourceVersion if both exist
65
- if (jsonObj.sourceVersion) {
66
- jsonObj.sourceScope = `${jsonObj.source}|${jsonObj.sourceVersion}`;
67
- delete jsonObj.sourceVersion;
68
- } else {
69
- jsonObj.sourceScope = jsonObj.source;
70
- }
71
- delete jsonObj.source;
72
- }
73
-
74
- if (jsonObj.target !== undefined) {
75
- // Combine target + targetVersion if both exist
76
- if (jsonObj.targetVersion) {
77
- jsonObj.targetScope = `${jsonObj.target}|${jsonObj.targetVersion}`;
78
- delete jsonObj.targetVersion;
79
- } else {
80
- jsonObj.targetScope = jsonObj.target;
81
- }
82
- delete jsonObj.target;
83
- }
84
-
85
- // Convert equivalence to relationship in group.element.target
86
- if (jsonObj.group && Array.isArray(jsonObj.group)) {
87
- jsonObj.group.forEach(group => {
88
- if (group.element && Array.isArray(group.element)) {
89
- group.element.forEach(element => {
90
- if (element.target && Array.isArray(element.target)) {
91
- element.target.forEach(target => {
92
- if (target.equivalence && !target.relationship) {
93
- // Convert equivalence to relationship and keep both
94
- target.relationship = this._convertEquivalenceToRelationship(target.equivalence);
95
- // Keep equivalence for backward compatibility
96
- }
97
- });
98
- }
99
- });
100
- }
101
- });
102
- }
103
-
104
- return jsonObj;
105
- }
106
-
107
- throw new Error(`Unsupported FHIR version: ${version}`);
108
- }
109
-
110
- /**
111
- * Converts R5 ConceptMap to target version format (clones object first)
112
- * @param {Object} r5Obj - The R5 format ConceptMap object
113
- * @param {string} targetVersion - Target FHIR version
114
- * @returns {Object} New object in target version format
115
- * @private
116
- */
117
- _convertFromR5(r5Obj, targetVersion) {
118
- if (VersionUtilities.isR5Ver(targetVersion)) {
119
- return r5Obj; // No conversion needed
120
- }
121
-
122
- // Clone the object to avoid modifying the original
123
- const cloned = JSON.parse(JSON.stringify(r5Obj));
124
-
125
- if (VersionUtilities.isR4Ver(targetVersion)) {
126
- return this._convertR5ToR4(cloned);
127
- } else if (VersionUtilities.isR3Ver(targetVersion)) {
128
- return this._convertR5ToR3(cloned);
129
- }
130
-
131
- throw new Error(`Unsupported target FHIR version: ${targetVersion}`);
132
- }
133
-
134
- /**
135
- * Converts R5 ConceptMap to R4 format
136
- * @param {Object} r5Obj - Cloned R5 ConceptMap object
137
- * @returns {Object} R4 format ConceptMap
138
- * @private
139
- */
140
- _convertR5ToR4(r5Obj) {
141
- // Remove R5-specific elements
142
- if (r5Obj.versionAlgorithmString) {
143
- delete r5Obj.versionAlgorithmString;
144
- }
145
- if (r5Obj.versionAlgorithmCoding) {
146
- delete r5Obj.versionAlgorithmCoding;
147
- }
148
- if (r5Obj.property) {
149
- delete r5Obj.property;
150
- }
151
- if (r5Obj.additionalAttribute) {
152
- delete r5Obj.additionalAttribute;
153
- }
154
-
155
- // Convert identifier array back to single object
156
- if (r5Obj.identifier && Array.isArray(r5Obj.identifier)) {
157
- if (r5Obj.identifier.length > 0) {
158
- r5Obj.identifier = r5Obj.identifier[0]; // Take first identifier
159
- } else {
160
- delete r5Obj.identifier;
161
- }
162
- }
163
-
164
- // Convert sourceScope/targetScope back to source/target + version
165
- if (r5Obj.sourceScope) {
166
- const parts = r5Obj.sourceScope.split('|');
167
- r5Obj.source = parts[0];
168
- if (parts.length > 1) {
169
- r5Obj.sourceVersion = parts[1];
170
- }
171
- delete r5Obj.sourceScope;
172
- }
173
-
174
- if (r5Obj.targetScope) {
175
- const parts = r5Obj.targetScope.split('|');
176
- r5Obj.target = parts[0];
177
- if (parts.length > 1) {
178
- r5Obj.targetVersion = parts[1];
179
- }
180
- delete r5Obj.targetScope;
181
- }
182
-
183
- // Convert relationship back to equivalence in group.element.target
184
- if (r5Obj.group && Array.isArray(r5Obj.group)) {
185
- r5Obj.group.forEach(group => {
186
- if (group.element && Array.isArray(group.element)) {
187
- group.element.forEach(element => {
188
- if (element.target && Array.isArray(element.target)) {
189
- element.target.forEach(target => {
190
- // If we have both equivalence and relationship, prefer equivalence for R4
191
- if (target.relationship && !target.equivalence) {
192
- target.equivalence = this._convertRelationshipToEquivalence(target.relationship);
193
- }
194
- // Remove R5-only relationship field
195
- delete target.relationship;
196
- });
197
- }
198
- });
199
- }
200
- });
201
- }
202
-
203
- return r5Obj;
204
- }
205
-
206
- /**
207
- * Converts R5 ConceptMap to R3 format
208
- * @param {Object} r5Obj - Cloned R5 ConceptMap object
209
- * @returns {Object} R3 format ConceptMap
210
- * @private
211
- */
212
- _convertR5ToR3(r5Obj) {
213
- // First apply R4 conversions
214
- const r4Obj = this._convertR5ToR4(r5Obj);
215
-
216
- // R3 has the same structure as R4 for the elements we care about
217
- return r4Obj;
218
- }
219
-
220
- /**
221
- * Converts R3/R4 equivalence to R5 relationship
222
- * @param {string} equivalence - R3/R4 equivalence value
223
- * @returns {string} R5 relationship value
224
- * @private
225
- */
226
- _convertEquivalenceToRelationship(equivalence) {
227
- const equivalenceToRelationship = {
228
- 'relatedto': 'related-to',
229
- 'equivalent': 'equivalent',
230
- 'equal': 'equivalent',
231
- 'wider': 'source-is-broader-than-target',
232
- 'subsumes': 'source-is-broader-than-target',
233
- 'narrower': 'source-is-narrower-than-target',
234
- 'specializes': 'source-is-narrower-than-target',
235
- 'inexact': 'not-related-to',
236
- 'unmatched': 'not-related-to',
237
- 'disjoint': 'not-related-to'
238
- };
239
- return equivalenceToRelationship[equivalence] || 'related-to';
240
- }
241
-
242
- /**
243
- * Converts R5 relationship back to R3/R4 equivalence
244
- * @param {string} relationship - R5 relationship value
245
- * @returns {string} R3/R4 equivalence value
246
- * @private
247
- */
248
- _convertRelationshipToEquivalence(relationship) {
249
- const relationshipToEquivalence = {
250
- 'related-to': 'relatedto',
251
- 'equivalent': 'equivalent',
252
- 'source-is-broader-than-target': 'wider',
253
- 'source-is-narrower-than-target': 'narrower',
254
- 'not-related-to': 'unmatched'
255
- };
256
- return relationshipToEquivalence[relationship] || 'relatedto';
257
- }
258
-
259
- /**
45
+ /**
260
46
  * Gets the FHIR version this ConceptMap was loaded from
261
47
  * @returns {string} FHIR version ('R3', 'R4', or 'R5')
262
48
  */
@@ -562,6 +562,16 @@ class Designations {
562
562
 
563
563
  if (!langList || langList.length == 0) {
564
564
  // No language list, prefer base designations
565
+ for (const cd of this.designations) {
566
+ if (this._isPreferred(cd) && cd.isActive()) {
567
+ return cd;
568
+ }
569
+ }
570
+ for (const cd of this.designations) {
571
+ if (this.isDisplay(cd) && cd.isActive()) {
572
+ return cd;
573
+ }
574
+ }
565
575
  for (const cd of this.designations) {
566
576
  if (this.isDisplay(cd)) {
567
577
  return cd;
@@ -717,7 +727,10 @@ class Designations {
717
727
  (cd.use.system === DesignationUse.DISPLAY.system &&
718
728
  cd.use.code === DesignationUse.DISPLAY.code) ||
719
729
  (cd.use.system === DesignationUse.PREFERRED.system &&
720
- cd.use.code === DesignationUse.PREFERRED.code);
730
+ cd.use.code === DesignationUse.PREFERRED.code) ||
731
+ // snomed
732
+ (cd.use.system === 'http://snomed.info/sct' &&
733
+ cd.use.code === '900000000000013009');
721
734
  }
722
735
 
723
736
  /**
@@ -111,6 +111,13 @@ const Extensions = {
111
111
  exp.extension = [];
112
112
  }
113
113
  exp.extension.push({ url : url, valueBoolean : b });
114
+ },
115
+
116
+ addString(exp, url, s) {
117
+ if (!exp.extension) {
118
+ exp.extension = [];
119
+ }
120
+ exp.extension.push({ url : url, valueString : s });
114
121
  }
115
122
  }
116
123