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.
- package/CHANGELOG.md +24 -0
- package/README.md +1 -1
- package/library/cron-utilities.js +136 -0
- package/library/html-server.js +13 -29
- package/library/html.js +3 -8
- package/library/languages.js +160 -37
- package/library/package-manager.js +48 -1
- package/library/utilities.js +100 -19
- package/package.json +2 -2
- package/packages/package-crawler.js +6 -1
- package/packages/packages.js +38 -54
- package/publisher/publisher.js +19 -27
- package/registry/api.js +11 -10
- package/registry/crawler.js +31 -29
- package/registry/model.js +5 -26
- package/registry/registry.js +32 -41
- package/server.js +89 -12
- package/shl/shl.js +0 -18
- package/static/assets/js/statuspage.js +1 -9
- package/stats.js +39 -1
- package/token/token.js +14 -9
- package/translations/Messages.properties +2 -1
- package/tx/README.md +17 -6
- package/tx/cs/cs-api.js +19 -1
- package/tx/cs/cs-base.js +77 -0
- package/tx/cs/cs-country.js +46 -0
- package/tx/cs/cs-cpt.js +9 -5
- package/tx/cs/cs-cs.js +27 -13
- package/tx/cs/cs-lang.js +60 -22
- package/tx/cs/cs-loinc.js +69 -98
- package/tx/cs/cs-mimetypes.js +4 -0
- package/tx/cs/cs-ndc.js +6 -0
- package/tx/cs/cs-omop.js +16 -15
- package/tx/cs/cs-rxnorm.js +23 -1
- package/tx/cs/cs-snomed.js +283 -40
- package/tx/cs/cs-ucum.js +90 -70
- package/tx/importers/import-sct.module.js +371 -35
- package/tx/importers/readme.md +117 -7
- package/tx/library/bundle.js +5 -0
- package/tx/library/capabilitystatement.js +3 -142
- package/tx/library/codesystem.js +19 -173
- package/tx/library/conceptmap.js +4 -218
- package/tx/library/designations.js +14 -1
- package/tx/library/extensions.js +7 -0
- package/tx/library/namingsystem.js +3 -89
- package/tx/library/operation-outcome.js +8 -3
- package/tx/library/parameters.js +3 -2
- package/tx/library/renderer.js +10 -6
- package/tx/library/terminologycapabilities.js +3 -243
- package/tx/library/valueset.js +3 -235
- package/tx/library.js +100 -13
- package/tx/operation-context.js +23 -4
- package/tx/params.js +35 -38
- package/tx/provider.js +6 -5
- package/tx/sct/expressions.js +12 -3
- package/tx/tx-html.js +80 -89
- package/tx/tx.fhir.org.yml +6 -5
- package/tx/tx.js +163 -13
- package/tx/vs/vs-database.js +56 -39
- package/tx/vs/vs-package.js +21 -2
- package/tx/vs/vs-vsac.js +175 -39
- package/tx/workers/batch-validate.js +2 -0
- package/tx/workers/batch.js +2 -0
- package/tx/workers/expand.js +132 -112
- package/tx/workers/lookup.js +33 -14
- package/tx/workers/metadata.js +2 -2
- package/tx/workers/read.js +3 -2
- package/tx/workers/related.js +574 -0
- package/tx/workers/search.js +46 -9
- package/tx/workers/subsumes.js +13 -3
- package/tx/workers/translate.js +7 -3
- package/tx/workers/validate.js +258 -285
- package/tx/workers/worker.js +43 -39
- package/tx/xml/bundle-xml.js +237 -0
- package/tx/xml/xml-base.js +215 -64
- package/tx/xversion/xv-bundle.js +71 -0
- package/tx/xversion/xv-capabiliityStatement.js +137 -0
- package/tx/xversion/xv-codesystem.js +169 -0
- package/tx/xversion/xv-conceptmap.js +224 -0
- package/tx/xversion/xv-namingsystem.js +88 -0
- package/tx/xversion/xv-operationoutcome.js +27 -0
- package/tx/xversion/xv-parameters.js +87 -0
- package/tx/xversion/xv-resource.js +45 -0
- package/tx/xversion/xv-terminologyCapabilities.js +214 -0
- package/tx/xversion/xv-valueset.js +234 -0
- package/utilities/dev-proxy-server.js +126 -0
- package/utilities/explode-results.js +58 -0
- package/utilities/split-by-system.js +198 -0
- package/utilities/vsac-cs-fetcher.js +0 -0
- package/{windows-install.js → utilities/windows-install.js} +2 -0
- package/vcl/vcl.js +0 -18
- package/xig/xig.js +241 -230
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const {CanonicalResource} = require("./canonical-resource");
|
|
2
|
-
const {
|
|
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 =
|
|
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
|
|
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
|
/**
|
package/tx/library/codesystem.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { Language } = require("../../library/languages");
|
|
2
2
|
const {CanonicalResource} = require("./canonical-resource");
|
|
3
|
-
const {
|
|
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 =
|
|
34
|
-
|
|
35
|
-
|
|
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 =
|
|
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 };
|
package/tx/library/conceptmap.js
CHANGED
|
@@ -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 =
|
|
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 =
|
|
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
|
/**
|
package/tx/library/extensions.js
CHANGED
|
@@ -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
|
|