@medplum/core 2.0.24 → 2.0.25
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/dist/cjs/index.cjs +28 -14114
- package/dist/cjs/index.cjs.map +7 -1
- package/dist/esm/index.mjs +31 -29
- package/dist/esm/index.mjs.map +7 -1
- package/dist/types/utils.d.ts +14 -0
- package/package.json +2 -2
- package/dist/cjs/index.min.cjs +0 -1
- package/dist/esm/access.mjs +0 -142
- package/dist/esm/access.mjs.map +0 -1
- package/dist/esm/base-schema.json.mjs +0 -4408
- package/dist/esm/base-schema.json.mjs.map +0 -1
- package/dist/esm/base64.mjs +0 -33
- package/dist/esm/base64.mjs.map +0 -1
- package/dist/esm/bundle.mjs +0 -36
- package/dist/esm/bundle.mjs.map +0 -1
- package/dist/esm/cache.mjs +0 -64
- package/dist/esm/cache.mjs.map +0 -1
- package/dist/esm/client.mjs +0 -2168
- package/dist/esm/client.mjs.map +0 -1
- package/dist/esm/crypto.mjs +0 -22
- package/dist/esm/crypto.mjs.map +0 -1
- package/dist/esm/eventtarget.mjs +0 -36
- package/dist/esm/eventtarget.mjs.map +0 -1
- package/dist/esm/fhirlexer/parse.mjs +0 -122
- package/dist/esm/fhirlexer/parse.mjs.map +0 -1
- package/dist/esm/fhirlexer/tokenize.mjs +0 -231
- package/dist/esm/fhirlexer/tokenize.mjs.map +0 -1
- package/dist/esm/fhirmapper/parse.mjs +0 -329
- package/dist/esm/fhirmapper/parse.mjs.map +0 -1
- package/dist/esm/fhirmapper/tokenize.mjs +0 -13
- package/dist/esm/fhirmapper/tokenize.mjs.map +0 -1
- package/dist/esm/fhirpath/atoms.mjs +0 -347
- package/dist/esm/fhirpath/atoms.mjs.map +0 -1
- package/dist/esm/fhirpath/date.mjs +0 -24
- package/dist/esm/fhirpath/date.mjs.map +0 -1
- package/dist/esm/fhirpath/functions.mjs +0 -1626
- package/dist/esm/fhirpath/functions.mjs.map +0 -1
- package/dist/esm/fhirpath/parse.mjs +0 -145
- package/dist/esm/fhirpath/parse.mjs.map +0 -1
- package/dist/esm/fhirpath/tokenize.mjs +0 -10
- package/dist/esm/fhirpath/tokenize.mjs.map +0 -1
- package/dist/esm/fhirpath/utils.mjs +0 -377
- package/dist/esm/fhirpath/utils.mjs.map +0 -1
- package/dist/esm/filter/parse.mjs +0 -101
- package/dist/esm/filter/parse.mjs.map +0 -1
- package/dist/esm/filter/tokenize.mjs +0 -16
- package/dist/esm/filter/tokenize.mjs.map +0 -1
- package/dist/esm/filter/types.mjs +0 -34
- package/dist/esm/filter/types.mjs.map +0 -1
- package/dist/esm/format.mjs +0 -390
- package/dist/esm/format.mjs.map +0 -1
- package/dist/esm/hl7.mjs +0 -242
- package/dist/esm/hl7.mjs.map +0 -1
- package/dist/esm/index.min.mjs +0 -1
- package/dist/esm/jwt.mjs +0 -30
- package/dist/esm/jwt.mjs.map +0 -1
- package/dist/esm/node_modules/tslib/package.json +0 -1
- package/dist/esm/outcomes.mjs +0 -295
- package/dist/esm/outcomes.mjs.map +0 -1
- package/dist/esm/readablepromise.mjs +0 -82
- package/dist/esm/readablepromise.mjs.map +0 -1
- package/dist/esm/schema.mjs +0 -417
- package/dist/esm/schema.mjs.map +0 -1
- package/dist/esm/search/details.mjs +0 -162
- package/dist/esm/search/details.mjs.map +0 -1
- package/dist/esm/search/match.mjs +0 -166
- package/dist/esm/search/match.mjs.map +0 -1
- package/dist/esm/search/search.mjs +0 -378
- package/dist/esm/search/search.mjs.map +0 -1
- package/dist/esm/sftp.mjs +0 -24
- package/dist/esm/sftp.mjs.map +0 -1
- package/dist/esm/storage.mjs +0 -95
- package/dist/esm/storage.mjs.map +0 -1
- package/dist/esm/types.mjs +0 -370
- package/dist/esm/types.mjs.map +0 -1
- package/dist/esm/typeschema/types.mjs +0 -278
- package/dist/esm/typeschema/types.mjs.map +0 -1
- package/dist/esm/typeschema/validation.mjs +0 -262
- package/dist/esm/typeschema/validation.mjs.map +0 -1
- package/dist/esm/utils.mjs +0 -632
- package/dist/esm/utils.mjs.map +0 -1
package/dist/esm/utils.mjs
DELETED
|
@@ -1,632 +0,0 @@
|
|
|
1
|
-
import { formatHumanName } from './format.mjs';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Creates a reference resource.
|
|
5
|
-
* @param resource The FHIR reesource.
|
|
6
|
-
* @returns A reference resource.
|
|
7
|
-
*/
|
|
8
|
-
function createReference(resource) {
|
|
9
|
-
const reference = getReferenceString(resource);
|
|
10
|
-
const display = getDisplayString(resource);
|
|
11
|
-
return display === reference ? { reference } : { reference, display };
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Returns a reference string for a resource.
|
|
15
|
-
* @param resource The FHIR resource.
|
|
16
|
-
* @returns A reference string of the form resourceType/id.
|
|
17
|
-
*/
|
|
18
|
-
function getReferenceString(resource) {
|
|
19
|
-
return resource.resourceType + '/' + resource.id;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Returns the ID portion of a reference.
|
|
23
|
-
* @param reference A FHIR reference.
|
|
24
|
-
* @returns The ID portion of a reference.
|
|
25
|
-
*/
|
|
26
|
-
function resolveId(reference) {
|
|
27
|
-
return reference?.reference?.split('/')[1];
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Returns true if the resource is a "ProfileResource".
|
|
31
|
-
* @param resource The FHIR resource.
|
|
32
|
-
* @returns True if the resource is a "ProfileResource".
|
|
33
|
-
*/
|
|
34
|
-
function isProfileResource(resource) {
|
|
35
|
-
return (resource.resourceType === 'Patient' ||
|
|
36
|
-
resource.resourceType === 'Practitioner' ||
|
|
37
|
-
resource.resourceType === 'RelatedPerson');
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Returns a display string for the resource.
|
|
41
|
-
* @param resource The input resource.
|
|
42
|
-
* @returns Human friendly display string.
|
|
43
|
-
*/
|
|
44
|
-
function getDisplayString(resource) {
|
|
45
|
-
if (isProfileResource(resource)) {
|
|
46
|
-
const profileName = getProfileResourceDisplayString(resource);
|
|
47
|
-
if (profileName) {
|
|
48
|
-
return profileName;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if (resource.resourceType === 'Device') {
|
|
52
|
-
const deviceName = getDeviceDisplayString(resource);
|
|
53
|
-
if (deviceName) {
|
|
54
|
-
return deviceName;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
if (resource.resourceType === 'Observation') {
|
|
58
|
-
if ('code' in resource && resource.code?.text) {
|
|
59
|
-
return resource.code.text;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (resource.resourceType === 'User') {
|
|
63
|
-
if (resource.email) {
|
|
64
|
-
return resource.email;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
if ('name' in resource && resource.name && typeof resource.name === 'string') {
|
|
68
|
-
return resource.name;
|
|
69
|
-
}
|
|
70
|
-
return getReferenceString(resource);
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Returns a display string for a profile resource if one is found.
|
|
74
|
-
* @param resource The profile resource.
|
|
75
|
-
* @returns The display name if one is found.
|
|
76
|
-
*/
|
|
77
|
-
function getProfileResourceDisplayString(resource) {
|
|
78
|
-
const names = resource.name;
|
|
79
|
-
if (names && names.length > 0) {
|
|
80
|
-
return formatHumanName(names[0]);
|
|
81
|
-
}
|
|
82
|
-
return undefined;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Returns a display string for a device resource if one is found.
|
|
86
|
-
* @param device The device resource.
|
|
87
|
-
* @returns The display name if one is found.
|
|
88
|
-
*/
|
|
89
|
-
function getDeviceDisplayString(device) {
|
|
90
|
-
const names = device.deviceName;
|
|
91
|
-
if (names && names.length > 0) {
|
|
92
|
-
return names[0].name;
|
|
93
|
-
}
|
|
94
|
-
return undefined;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Returns an image URL for the resource, if one is available.
|
|
98
|
-
* @param resource The input resource.
|
|
99
|
-
* @returns The image URL for the resource or undefined.
|
|
100
|
-
*/
|
|
101
|
-
function getImageSrc(resource) {
|
|
102
|
-
if (!('photo' in resource)) {
|
|
103
|
-
return undefined;
|
|
104
|
-
}
|
|
105
|
-
const photo = resource.photo;
|
|
106
|
-
if (!photo) {
|
|
107
|
-
return undefined;
|
|
108
|
-
}
|
|
109
|
-
if (Array.isArray(photo)) {
|
|
110
|
-
for (const p of photo) {
|
|
111
|
-
const url = getPhotoImageSrc(p);
|
|
112
|
-
if (url) {
|
|
113
|
-
return url;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
return getPhotoImageSrc(photo);
|
|
119
|
-
}
|
|
120
|
-
return undefined;
|
|
121
|
-
}
|
|
122
|
-
function getPhotoImageSrc(photo) {
|
|
123
|
-
if (photo.url && photo.contentType && photo.contentType.startsWith('image/')) {
|
|
124
|
-
return photo.url;
|
|
125
|
-
}
|
|
126
|
-
return undefined;
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Returns a Date property as a Date.
|
|
130
|
-
* When working with JSON objects, Dates are often serialized as ISO-8601 strings.
|
|
131
|
-
* When that happens, we need to safely convert to a proper Date object.
|
|
132
|
-
* @param date The date property value, which could be a string or a Date object.
|
|
133
|
-
* @returns A Date object.
|
|
134
|
-
*/
|
|
135
|
-
function getDateProperty(date) {
|
|
136
|
-
return date ? new Date(date) : undefined;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Calculates the age in years from the birth date.
|
|
140
|
-
* @param birthDateStr The birth date or start date in ISO-8601 format YYYY-MM-DD.
|
|
141
|
-
* @param endDateStr Optional end date in ISO-8601 format YYYY-MM-DD. Default value is today.
|
|
142
|
-
* @returns The age in years, months, and days.
|
|
143
|
-
*/
|
|
144
|
-
function calculateAge(birthDateStr, endDateStr) {
|
|
145
|
-
const startDate = new Date(birthDateStr);
|
|
146
|
-
startDate.setUTCHours(0, 0, 0, 0);
|
|
147
|
-
const endDate = endDateStr ? new Date(endDateStr) : new Date();
|
|
148
|
-
endDate.setUTCHours(0, 0, 0, 0);
|
|
149
|
-
const startYear = startDate.getUTCFullYear();
|
|
150
|
-
const startMonth = startDate.getUTCMonth();
|
|
151
|
-
const startDay = startDate.getUTCDate();
|
|
152
|
-
const endYear = endDate.getUTCFullYear();
|
|
153
|
-
const endMonth = endDate.getUTCMonth();
|
|
154
|
-
const endDay = endDate.getUTCDate();
|
|
155
|
-
let years = endYear - startYear;
|
|
156
|
-
if (endMonth < startMonth || (endMonth === startMonth && endDay < startDay)) {
|
|
157
|
-
years--;
|
|
158
|
-
}
|
|
159
|
-
let months = endYear * 12 + endMonth - (startYear * 12 + startMonth);
|
|
160
|
-
if (endDay < startDay) {
|
|
161
|
-
months--;
|
|
162
|
-
}
|
|
163
|
-
const days = Math.floor((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));
|
|
164
|
-
return { years, months, days };
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Calculates the age string for display using the age appropriate units.
|
|
168
|
-
* If the age is greater than or equal to 2 years, then the age is displayed in years.
|
|
169
|
-
* If the age is greater than or equal to 1 month, then the age is displayed in months.
|
|
170
|
-
* Otherwise, the age is displayed in days.
|
|
171
|
-
* @param birthDateStr The birth date or start date in ISO-8601 format YYYY-MM-DD.
|
|
172
|
-
* @param endDateStr Optional end date in ISO-8601 format YYYY-MM-DD. Default value is today.
|
|
173
|
-
* @returns The age string.
|
|
174
|
-
*/
|
|
175
|
-
function calculateAgeString(birthDateStr, endDateStr) {
|
|
176
|
-
const { years, months, days } = calculateAge(birthDateStr, endDateStr);
|
|
177
|
-
if (years >= 2) {
|
|
178
|
-
return years.toString().padStart(3, '0') + 'Y';
|
|
179
|
-
}
|
|
180
|
-
else if (months >= 1) {
|
|
181
|
-
return months.toString().padStart(3, '0') + 'M';
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
return days.toString().padStart(3, '0') + 'D';
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Returns all questionnaire answers as a map by link ID.
|
|
189
|
-
* @param response The questionnaire response resource.
|
|
190
|
-
* @returns Questionnaire answers mapped by link ID.
|
|
191
|
-
*/
|
|
192
|
-
function getQuestionnaireAnswers(response) {
|
|
193
|
-
const result = {};
|
|
194
|
-
buildQuestionnaireAnswerItems(response.item, result);
|
|
195
|
-
return result;
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Recursively builds the questionnaire answer items map.
|
|
199
|
-
* @param items The current questionnaire response items.
|
|
200
|
-
* @param result The cumulative result map.
|
|
201
|
-
*/
|
|
202
|
-
function buildQuestionnaireAnswerItems(items, result) {
|
|
203
|
-
if (items) {
|
|
204
|
-
for (const item of items) {
|
|
205
|
-
if (item.linkId && item.answer && item.answer.length > 0) {
|
|
206
|
-
result[item.linkId] = item.answer[0];
|
|
207
|
-
}
|
|
208
|
-
buildQuestionnaireAnswerItems(item.item, result);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Returns the resource identifier for the given system.
|
|
214
|
-
*
|
|
215
|
-
* If multiple identifiers exist with the same system, the first one is returned.
|
|
216
|
-
*
|
|
217
|
-
* If the system is not found, then returns undefined.
|
|
218
|
-
* @param resource The resource to check.
|
|
219
|
-
* @param system The identifier system.
|
|
220
|
-
* @returns The identifier value if found; otherwise undefined.
|
|
221
|
-
*/
|
|
222
|
-
function getIdentifier(resource, system) {
|
|
223
|
-
const identifiers = resource.identifier;
|
|
224
|
-
if (!identifiers) {
|
|
225
|
-
return undefined;
|
|
226
|
-
}
|
|
227
|
-
const array = Array.isArray(identifiers) ? identifiers : [identifiers];
|
|
228
|
-
for (const identifier of array) {
|
|
229
|
-
if (identifier.system === system) {
|
|
230
|
-
return identifier.value;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
return undefined;
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Returns an extension value by extension URLs.
|
|
237
|
-
* @param resource The base resource.
|
|
238
|
-
* @param urls Array of extension URLs. Each entry represents a nested extension.
|
|
239
|
-
* @returns The extension value if found; undefined otherwise.
|
|
240
|
-
*/
|
|
241
|
-
function getExtensionValue(resource, ...urls) {
|
|
242
|
-
// Let curr be the current resource or extension. Extensions can be nested.
|
|
243
|
-
let curr = resource;
|
|
244
|
-
// For each of the urls, try to find a matching nested extension.
|
|
245
|
-
for (let i = 0; i < urls.length && curr; i++) {
|
|
246
|
-
curr = curr?.extension?.find((e) => e.url === urls[i]);
|
|
247
|
-
}
|
|
248
|
-
return curr?.valueString;
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Returns an extension by extension URLs.
|
|
252
|
-
* @param resource The base resource.
|
|
253
|
-
* @param urls Array of extension URLs. Each entry represents a nested extension.
|
|
254
|
-
* @returns The extension object if found; undefined otherwise.
|
|
255
|
-
*/
|
|
256
|
-
function getExtension(resource, ...urls) {
|
|
257
|
-
// Let curr be the current resource or extension. Extensions can be nested.
|
|
258
|
-
let curr = resource;
|
|
259
|
-
// For each of the urls, try to find a matching nested extension.
|
|
260
|
-
for (let i = 0; i < urls.length && curr; i++) {
|
|
261
|
-
curr = curr?.extension?.find((e) => e.url === urls[i]);
|
|
262
|
-
}
|
|
263
|
-
return curr;
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* FHIR JSON stringify.
|
|
267
|
-
* Removes properties with empty string values.
|
|
268
|
-
* Removes objects with zero properties.
|
|
269
|
-
* See: https://www.hl7.org/fhir/json.html
|
|
270
|
-
* @param value The input value.
|
|
271
|
-
* @param pretty Optional flag to pretty-print the JSON.
|
|
272
|
-
* @returns The resulting JSON string.
|
|
273
|
-
*/
|
|
274
|
-
function stringify(value, pretty) {
|
|
275
|
-
return JSON.stringify(value, stringifyReplacer, pretty ? 2 : undefined);
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* Evaluates JSON key/value pairs for FHIR JSON stringify.
|
|
279
|
-
* Removes properties with empty string values.
|
|
280
|
-
* Removes objects with zero properties.
|
|
281
|
-
* @param k Property key.
|
|
282
|
-
* @param v Property value.
|
|
283
|
-
* @returns The replaced value.
|
|
284
|
-
*/
|
|
285
|
-
function stringifyReplacer(k, v) {
|
|
286
|
-
return !isArrayKey(k) && isEmpty(v) ? undefined : v;
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Returns true if the key is an array key.
|
|
290
|
-
* @param k The property key.
|
|
291
|
-
* @returns True if the key is an array key.
|
|
292
|
-
*/
|
|
293
|
-
function isArrayKey(k) {
|
|
294
|
-
return !!k.match(/\d+$/);
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* Returns true if the value is empty (null, undefined, empty string, or empty object).
|
|
298
|
-
* @param v Any value.
|
|
299
|
-
* @returns True if the value is an empty string or an empty object.
|
|
300
|
-
*/
|
|
301
|
-
function isEmpty(v) {
|
|
302
|
-
if (v === null || v === undefined) {
|
|
303
|
-
return true;
|
|
304
|
-
}
|
|
305
|
-
const t = typeof v;
|
|
306
|
-
return (t === 'string' && v === '') || (t === 'object' && Object.keys(v).length === 0);
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Resource equality.
|
|
310
|
-
* Ignores meta.versionId and meta.lastUpdated.
|
|
311
|
-
* @param object1 The first object.
|
|
312
|
-
* @param object2 The second object.
|
|
313
|
-
* @param path Optional path string.
|
|
314
|
-
* @returns True if the objects are equal.
|
|
315
|
-
*/
|
|
316
|
-
function deepEquals(object1, object2, path) {
|
|
317
|
-
if (object1 === object2) {
|
|
318
|
-
return true;
|
|
319
|
-
}
|
|
320
|
-
if (isEmpty(object1) && isEmpty(object2)) {
|
|
321
|
-
return true;
|
|
322
|
-
}
|
|
323
|
-
if (isEmpty(object1) || isEmpty(object2)) {
|
|
324
|
-
return false;
|
|
325
|
-
}
|
|
326
|
-
if (Array.isArray(object1) && Array.isArray(object2)) {
|
|
327
|
-
return deepEqualsArray(object1, object2);
|
|
328
|
-
}
|
|
329
|
-
if (Array.isArray(object1) || Array.isArray(object2)) {
|
|
330
|
-
return false;
|
|
331
|
-
}
|
|
332
|
-
if (isObject(object1) && isObject(object2)) {
|
|
333
|
-
return deepEqualsObject(object1, object2, path);
|
|
334
|
-
}
|
|
335
|
-
if (isObject(object1) || isObject(object2)) {
|
|
336
|
-
return false;
|
|
337
|
-
}
|
|
338
|
-
return false;
|
|
339
|
-
}
|
|
340
|
-
function deepEqualsArray(array1, array2) {
|
|
341
|
-
if (array1.length !== array2.length) {
|
|
342
|
-
return false;
|
|
343
|
-
}
|
|
344
|
-
for (let i = 0; i < array1.length; i++) {
|
|
345
|
-
if (!deepEquals(array1[i], array2[i])) {
|
|
346
|
-
return false;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
return true;
|
|
350
|
-
}
|
|
351
|
-
function deepEqualsObject(object1, object2, path) {
|
|
352
|
-
const keySet = new Set();
|
|
353
|
-
Object.keys(object1).forEach((k) => keySet.add(k));
|
|
354
|
-
Object.keys(object2).forEach((k) => keySet.add(k));
|
|
355
|
-
if (path === 'meta') {
|
|
356
|
-
keySet.delete('versionId');
|
|
357
|
-
keySet.delete('lastUpdated');
|
|
358
|
-
keySet.delete('author');
|
|
359
|
-
}
|
|
360
|
-
for (const key of keySet) {
|
|
361
|
-
const val1 = object1[key];
|
|
362
|
-
const val2 = object2[key];
|
|
363
|
-
if (!deepEquals(val1, val2, key)) {
|
|
364
|
-
return false;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
return true;
|
|
368
|
-
}
|
|
369
|
-
/**
|
|
370
|
-
* Creates a deep clone of the input value.
|
|
371
|
-
*
|
|
372
|
-
* Limitations:
|
|
373
|
-
* - Only supports JSON primitives and arrays.
|
|
374
|
-
* - Does not support Functions, lambdas, etc.
|
|
375
|
-
* - Does not support circular references.
|
|
376
|
-
*
|
|
377
|
-
* See: https://web.dev/structured-clone/
|
|
378
|
-
* See: https://stackoverflow.com/questions/40488190/how-is-structured-clone-algorithm-different-from-deep-copy
|
|
379
|
-
* @param input The input to clone.
|
|
380
|
-
* @returns A deep clone of the input.
|
|
381
|
-
*/
|
|
382
|
-
function deepClone(input) {
|
|
383
|
-
return JSON.parse(JSON.stringify(input));
|
|
384
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* Returns true if the input string is a UUID.
|
|
387
|
-
* @param input The input string.
|
|
388
|
-
* @returns True if the input string matches the UUID format.
|
|
389
|
-
*/
|
|
390
|
-
function isUUID(input) {
|
|
391
|
-
return !!input.match(/^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/i);
|
|
392
|
-
}
|
|
393
|
-
/**
|
|
394
|
-
* Returns true if the input is an object.
|
|
395
|
-
* @param obj The candidate object.
|
|
396
|
-
* @returns True if the input is a non-null non-undefined object.
|
|
397
|
-
*/
|
|
398
|
-
function isObject(obj) {
|
|
399
|
-
return obj !== null && typeof obj === 'object';
|
|
400
|
-
}
|
|
401
|
-
/**
|
|
402
|
-
* Returns true if the input array is an array of strings.
|
|
403
|
-
* @param arr Input array.
|
|
404
|
-
* @returns True if the input array is an array of strings.
|
|
405
|
-
*/
|
|
406
|
-
function isStringArray(arr) {
|
|
407
|
-
return arr.every((e) => typeof e === 'string');
|
|
408
|
-
}
|
|
409
|
-
// Precompute hex octets
|
|
410
|
-
// See: https://stackoverflow.com/a/55200387
|
|
411
|
-
const byteToHex = [];
|
|
412
|
-
for (let n = 0; n < 256; n++) {
|
|
413
|
-
byteToHex.push(n.toString(16).padStart(2, '0'));
|
|
414
|
-
}
|
|
415
|
-
/**
|
|
416
|
-
* Converts an ArrayBuffer to hex string.
|
|
417
|
-
* See: https://stackoverflow.com/a/55200387
|
|
418
|
-
* @param arrayBuffer The input array buffer.
|
|
419
|
-
* @returns The resulting hex string.
|
|
420
|
-
*/
|
|
421
|
-
function arrayBufferToHex(arrayBuffer) {
|
|
422
|
-
const bytes = new Uint8Array(arrayBuffer);
|
|
423
|
-
const result = new Array(bytes.length);
|
|
424
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
425
|
-
result[i] = byteToHex[bytes[i]];
|
|
426
|
-
}
|
|
427
|
-
return result.join('');
|
|
428
|
-
}
|
|
429
|
-
/**
|
|
430
|
-
* Converts an ArrayBuffer to a base-64 encoded string.
|
|
431
|
-
* @param arrayBuffer The input array buffer.
|
|
432
|
-
* @returns The base-64 encoded string.
|
|
433
|
-
*/
|
|
434
|
-
function arrayBufferToBase64(arrayBuffer) {
|
|
435
|
-
const bytes = new Uint8Array(arrayBuffer);
|
|
436
|
-
const result = [];
|
|
437
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
438
|
-
result[i] = String.fromCharCode(bytes[i]);
|
|
439
|
-
}
|
|
440
|
-
return window.btoa(result.join(''));
|
|
441
|
-
}
|
|
442
|
-
function capitalize(word) {
|
|
443
|
-
return word.charAt(0).toUpperCase() + word.substring(1);
|
|
444
|
-
}
|
|
445
|
-
function isLowerCase(c) {
|
|
446
|
-
return c === c.toLowerCase() && c !== c.toUpperCase();
|
|
447
|
-
}
|
|
448
|
-
/**
|
|
449
|
-
* Tries to find a code string for a given system within a given codeable concept.
|
|
450
|
-
* @param concept The codeable concept.
|
|
451
|
-
* @param system The system string.
|
|
452
|
-
* @returns The code if found; otherwise undefined.
|
|
453
|
-
*/
|
|
454
|
-
function getCodeBySystem(concept, system) {
|
|
455
|
-
return concept.coding?.find((coding) => coding.system === system)?.code;
|
|
456
|
-
}
|
|
457
|
-
/**
|
|
458
|
-
* Sets a code for a given system within a given codeable concept.
|
|
459
|
-
* @param concept The codeable concept.
|
|
460
|
-
* @param system The system string.
|
|
461
|
-
* @param code The code value.
|
|
462
|
-
*/
|
|
463
|
-
function setCodeBySystem(concept, system, code) {
|
|
464
|
-
if (!concept.coding) {
|
|
465
|
-
concept.coding = [];
|
|
466
|
-
}
|
|
467
|
-
const coding = concept.coding.find((c) => c.system === system);
|
|
468
|
-
if (coding) {
|
|
469
|
-
coding.code = code;
|
|
470
|
-
}
|
|
471
|
-
else {
|
|
472
|
-
concept.coding.push({ system, code });
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
/**
|
|
476
|
-
* Tries to find an observation interval for the given patient and value.
|
|
477
|
-
* @param definition The observation definition.
|
|
478
|
-
* @param patient The patient.
|
|
479
|
-
* @param value The observation value.
|
|
480
|
-
* @param category Optional interval category restriction.
|
|
481
|
-
* @returns The observation interval if found; otherwise undefined.
|
|
482
|
-
*/
|
|
483
|
-
function findObservationInterval(definition, patient, value, category) {
|
|
484
|
-
return definition.qualifiedInterval?.find((interval) => observationIntervalMatchesPatient(interval, patient) &&
|
|
485
|
-
observationIntervalMatchesValue(interval, value, definition.quantitativeDetails?.decimalPrecision) &&
|
|
486
|
-
(category === undefined || interval.category === category));
|
|
487
|
-
}
|
|
488
|
-
/**
|
|
489
|
-
* Tries to find an observation reference range for the given patient and condition names.
|
|
490
|
-
* @param definition The observation definition.
|
|
491
|
-
* @param patient The patient.
|
|
492
|
-
* @param names The condition names.
|
|
493
|
-
* @returns The observation interval if found; otherwise undefined.
|
|
494
|
-
*/
|
|
495
|
-
function findObservationReferenceRange(definition, patient, names) {
|
|
496
|
-
return definition.qualifiedInterval?.find((interval) => observationIntervalMatchesPatient(interval, patient) && names.includes(interval.condition));
|
|
497
|
-
}
|
|
498
|
-
/**
|
|
499
|
-
* Returns true if the patient matches the observation interval.
|
|
500
|
-
* @param interval The observation interval.
|
|
501
|
-
* @param patient The patient.
|
|
502
|
-
* @returns True if the patient matches the observation interval.
|
|
503
|
-
*/
|
|
504
|
-
function observationIntervalMatchesPatient(interval, patient) {
|
|
505
|
-
return observationIntervalMatchesGender(interval, patient) && observationIntervalMatchesAge(interval, patient);
|
|
506
|
-
}
|
|
507
|
-
/**
|
|
508
|
-
* Returns true if the patient gender matches the observation interval.
|
|
509
|
-
* @param interval The observation interval.
|
|
510
|
-
* @param patient The patient.
|
|
511
|
-
* @returns True if the patient gender matches the observation interval.
|
|
512
|
-
*/
|
|
513
|
-
function observationIntervalMatchesGender(interval, patient) {
|
|
514
|
-
return !interval.gender || interval.gender === patient.gender;
|
|
515
|
-
}
|
|
516
|
-
/**
|
|
517
|
-
* Returns true if the patient age matches the observation interval.
|
|
518
|
-
* @param interval The observation interval.
|
|
519
|
-
* @param patient The patient.
|
|
520
|
-
* @returns True if the patient age matches the observation interval.
|
|
521
|
-
*/
|
|
522
|
-
function observationIntervalMatchesAge(interval, patient) {
|
|
523
|
-
return !interval.age || matchesRange(calculateAge(patient.birthDate).years, interval.age);
|
|
524
|
-
}
|
|
525
|
-
/**
|
|
526
|
-
* Returns true if the value matches the observation interval.
|
|
527
|
-
* @param interval The observation interval.
|
|
528
|
-
* @param value The observation value.
|
|
529
|
-
* @param precision Optional precision in number of digits.
|
|
530
|
-
* @returns True if the value matches the observation interval.
|
|
531
|
-
*/
|
|
532
|
-
function observationIntervalMatchesValue(interval, value, precision) {
|
|
533
|
-
return !!interval.range && matchesRange(value, interval.range, precision);
|
|
534
|
-
}
|
|
535
|
-
/**
|
|
536
|
-
* Returns true if the value is in the range accounting for precision.
|
|
537
|
-
* @param value The numeric value.
|
|
538
|
-
* @param range The numeric range.
|
|
539
|
-
* @param precision Optional precision in number of digits.
|
|
540
|
-
* @returns True if the value is within the range.
|
|
541
|
-
*/
|
|
542
|
-
function matchesRange(value, range, precision) {
|
|
543
|
-
return ((range.low?.value === undefined || preciseGreaterThanOrEquals(value, range.low.value, precision)) &&
|
|
544
|
-
(range.high?.value === undefined || preciseLessThanOrEquals(value, range.high.value, precision)));
|
|
545
|
-
}
|
|
546
|
-
/**
|
|
547
|
-
* Returns the input number rounded to the specified number of digits.
|
|
548
|
-
* @param a The input number.
|
|
549
|
-
* @param precision The precision in number of digits.
|
|
550
|
-
* @returns The number rounded to the specified number of digits.
|
|
551
|
-
*/
|
|
552
|
-
function preciseRound(a, precision) {
|
|
553
|
-
return parseFloat(a.toFixed(precision));
|
|
554
|
-
}
|
|
555
|
-
/**
|
|
556
|
-
* Returns true if the two numbers are equal to the given precision.
|
|
557
|
-
* @param a The first number.
|
|
558
|
-
* @param b The second number.
|
|
559
|
-
* @param precision Optional precision in number of digits.
|
|
560
|
-
* @returns True if the two numbers are equal to the given precision.
|
|
561
|
-
*/
|
|
562
|
-
function preciseEquals(a, b, precision) {
|
|
563
|
-
return toPreciseInteger(a, precision) === toPreciseInteger(b, precision);
|
|
564
|
-
}
|
|
565
|
-
/**
|
|
566
|
-
* Returns true if the first number is less than the second number to the given precision.
|
|
567
|
-
* @param a The first number.
|
|
568
|
-
* @param b The second number.
|
|
569
|
-
* @param precision Optional precision in number of digits.
|
|
570
|
-
* @returns True if the first number is less than the second number to the given precision.
|
|
571
|
-
*/
|
|
572
|
-
function preciseLessThan(a, b, precision) {
|
|
573
|
-
return toPreciseInteger(a, precision) < toPreciseInteger(b, precision);
|
|
574
|
-
}
|
|
575
|
-
/**
|
|
576
|
-
* Returns true if the first number is greater than the second number to the given precision.
|
|
577
|
-
* @param a The first number.
|
|
578
|
-
* @param b The second number.
|
|
579
|
-
* @param precision Optional precision in number of digits.
|
|
580
|
-
* @returns True if the first number is greater than the second number to the given precision.
|
|
581
|
-
*/
|
|
582
|
-
function preciseGreaterThan(a, b, precision) {
|
|
583
|
-
return toPreciseInteger(a, precision) > toPreciseInteger(b, precision);
|
|
584
|
-
}
|
|
585
|
-
/**
|
|
586
|
-
* Returns true if the first number is less than or equal to the second number to the given precision.
|
|
587
|
-
* @param a The first number.
|
|
588
|
-
* @param b The second number.
|
|
589
|
-
* @param precision Optional precision in number of digits.
|
|
590
|
-
* @returns True if the first number is less than or equal to the second number to the given precision.
|
|
591
|
-
*/
|
|
592
|
-
function preciseLessThanOrEquals(a, b, precision) {
|
|
593
|
-
return toPreciseInteger(a, precision) <= toPreciseInteger(b, precision);
|
|
594
|
-
}
|
|
595
|
-
/**
|
|
596
|
-
* Returns true if the first number is greater than or equal to the second number to the given precision.
|
|
597
|
-
* @param a The first number.
|
|
598
|
-
* @param b The second number.
|
|
599
|
-
* @param precision Optional precision in number of digits.
|
|
600
|
-
* @returns True if the first number is greater than or equal to the second number to the given precision.
|
|
601
|
-
*/
|
|
602
|
-
function preciseGreaterThanOrEquals(a, b, precision) {
|
|
603
|
-
return toPreciseInteger(a, precision) >= toPreciseInteger(b, precision);
|
|
604
|
-
}
|
|
605
|
-
/**
|
|
606
|
-
* Returns an integer representation of the number with the given precision.
|
|
607
|
-
* For example, if precision is 2, then 1.2345 will be returned as 123.
|
|
608
|
-
* @param a The number.
|
|
609
|
-
* @param precision Optional precision in number of digits.
|
|
610
|
-
* @returns The integer with the given precision.
|
|
611
|
-
*/
|
|
612
|
-
function toPreciseInteger(a, precision) {
|
|
613
|
-
if (precision === undefined) {
|
|
614
|
-
return a;
|
|
615
|
-
}
|
|
616
|
-
return Math.round(a * Math.pow(10, precision));
|
|
617
|
-
}
|
|
618
|
-
/**
|
|
619
|
-
* Finds the first resource in the input array that matches the specified code and system.
|
|
620
|
-
* @param resources - The array of resources to search.
|
|
621
|
-
* @param code - The code to search for.
|
|
622
|
-
* @param system - The system to search for.
|
|
623
|
-
* @returns The first resource in the input array that matches the specified code and system, or undefined if no such resource is found.
|
|
624
|
-
*/
|
|
625
|
-
function findResourceByCode(resources, code, system) {
|
|
626
|
-
return resources.find((r) => typeof code === 'string'
|
|
627
|
-
? getCodeBySystem(r.code || {}, system) === code
|
|
628
|
-
: getCodeBySystem(r.code || {}, system) === getCodeBySystem(code, system));
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
export { arrayBufferToBase64, arrayBufferToHex, calculateAge, calculateAgeString, capitalize, createReference, deepClone, deepEquals, findObservationInterval, findObservationReferenceRange, findResourceByCode, getCodeBySystem, getDateProperty, getDisplayString, getExtension, getExtensionValue, getIdentifier, getImageSrc, getQuestionnaireAnswers, getReferenceString, isEmpty, isLowerCase, isObject, isProfileResource, isStringArray, isUUID, matchesRange, preciseEquals, preciseGreaterThan, preciseGreaterThanOrEquals, preciseLessThan, preciseLessThanOrEquals, preciseRound, resolveId, setCodeBySystem, stringify };
|
|
632
|
-
//# sourceMappingURL=utils.mjs.map
|