@medplum/core 0.9.23 → 0.9.26

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 (78) hide show
  1. package/dist/cjs/index.js +12 -0
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/index.min.js +1 -1
  4. package/dist/cjs/index.min.js.map +1 -1
  5. package/dist/esm/base-schema.json.js +4872 -0
  6. package/dist/esm/base-schema.json.js.map +1 -0
  7. package/dist/{types → esm}/cache.d.ts +0 -0
  8. package/dist/esm/cache.js +70 -0
  9. package/dist/esm/cache.js.map +1 -0
  10. package/dist/{types → esm}/client.d.ts +0 -0
  11. package/dist/esm/client.js +1450 -0
  12. package/dist/esm/client.js.map +1 -0
  13. package/dist/{types → esm}/crypto.d.ts +0 -0
  14. package/dist/esm/crypto.js +23 -0
  15. package/dist/esm/crypto.js.map +1 -0
  16. package/dist/{types → esm}/eventtarget.d.ts +0 -0
  17. package/dist/esm/eventtarget.js +41 -0
  18. package/dist/esm/eventtarget.js.map +1 -0
  19. package/dist/{types → esm}/fhirpath/atoms.d.ts +0 -0
  20. package/dist/esm/fhirpath/atoms.js +329 -0
  21. package/dist/esm/fhirpath/atoms.js.map +1 -0
  22. package/dist/{types → esm}/fhirpath/date.d.ts +0 -0
  23. package/dist/esm/fhirpath/date.js +24 -0
  24. package/dist/esm/fhirpath/date.js.map +1 -0
  25. package/dist/{types → esm}/fhirpath/functions.d.ts +0 -0
  26. package/dist/esm/fhirpath/functions.js +1503 -0
  27. package/dist/esm/fhirpath/functions.js.map +1 -0
  28. package/dist/{types → esm}/fhirpath/index.d.ts +0 -0
  29. package/dist/{types → esm}/fhirpath/parse.d.ts +0 -0
  30. package/dist/esm/fhirpath/parse.js +258 -0
  31. package/dist/esm/fhirpath/parse.js.map +1 -0
  32. package/dist/{types → esm}/fhirpath/tokenize.d.ts +0 -0
  33. package/dist/esm/fhirpath/tokenize.js +185 -0
  34. package/dist/esm/fhirpath/tokenize.js.map +1 -0
  35. package/dist/{types → esm}/fhirpath/utils.d.ts +0 -0
  36. package/dist/esm/fhirpath/utils.js +372 -0
  37. package/dist/esm/fhirpath/utils.js.map +1 -0
  38. package/dist/{types → esm}/format.d.ts +0 -0
  39. package/dist/esm/format.js +51 -0
  40. package/dist/esm/format.js.map +1 -0
  41. package/dist/{types → esm}/hl7.d.ts +0 -0
  42. package/dist/esm/hl7.js +111 -0
  43. package/dist/esm/hl7.js.map +1 -0
  44. package/dist/{types → esm}/index.d.ts +0 -0
  45. package/dist/esm/index.js +15 -10752
  46. package/dist/esm/index.js.map +1 -1
  47. package/dist/esm/index.min.js +1 -1
  48. package/dist/esm/index.min.js.map +1 -1
  49. package/dist/{types → esm}/jwt.d.ts +0 -0
  50. package/dist/esm/jwt.js +35 -0
  51. package/dist/esm/jwt.js.map +1 -0
  52. package/dist/esm/node_modules/tslib/tslib.es6.js +40 -0
  53. package/dist/esm/node_modules/tslib/tslib.es6.js.map +1 -0
  54. package/dist/{types → esm}/outcomes.d.ts +0 -0
  55. package/dist/esm/outcomes.js +151 -0
  56. package/dist/esm/outcomes.js.map +1 -0
  57. package/dist/{types → esm}/readablepromise.d.ts +0 -0
  58. package/dist/esm/readablepromise.js +87 -0
  59. package/dist/esm/readablepromise.js.map +1 -0
  60. package/dist/{types → esm}/search.d.ts +0 -0
  61. package/dist/esm/search.js +207 -0
  62. package/dist/esm/search.js.map +1 -0
  63. package/dist/{types → esm}/searchparams.d.ts +0 -0
  64. package/dist/esm/searchparams.js +145 -0
  65. package/dist/esm/searchparams.js.map +1 -0
  66. package/dist/{types → esm}/storage.d.ts +0 -0
  67. package/dist/esm/storage.js +94 -0
  68. package/dist/esm/storage.js.map +1 -0
  69. package/dist/{types → esm}/types.d.ts +0 -0
  70. package/dist/esm/types.js +223 -0
  71. package/dist/esm/types.js.map +1 -0
  72. package/dist/{types → esm}/utils.d.ts +8 -0
  73. package/dist/esm/utils.js +590 -0
  74. package/dist/esm/utils.js.map +1 -0
  75. package/package.json +5 -5
  76. package/stats.html +4034 -0
  77. package/tsconfig.cjs.json +7 -0
  78. package/tsconfig.esm.json +7 -0
@@ -0,0 +1,1503 @@
1
+ import { PropertyType } from '../types.js';
2
+ import { calculateAge } from '../utils.js';
3
+ import { SymbolAtom, DotAtom } from './atoms.js';
4
+ import { parseDateString } from './date.js';
5
+ import { booleanToTypedValue, toJsBoolean, removeDuplicates, isQuantity, fhirPathIs, toTypedValue } from './utils.js';
6
+
7
+ /**
8
+ * Temporary placholder for unimplemented methods.
9
+ */
10
+ const stub = () => [];
11
+ const functions = {
12
+ /*
13
+ * 5.1 Existence
14
+ * See: https://hl7.org/fhirpath/#existence
15
+ */
16
+ /**
17
+ * Returns true if the input collection is empty ({ }) and false otherwise.
18
+ *
19
+ * See: https://hl7.org/fhirpath/#empty-boolean
20
+ *
21
+ * @param input The input collection.
22
+ * @returns True if the input collection is empty ({ }) and false otherwise.
23
+ */
24
+ empty: (input) => {
25
+ return booleanToTypedValue(input.length === 0);
26
+ },
27
+ /**
28
+ * Returns true if the collection has unknown elements, and false otherwise.
29
+ * This is the opposite of empty(), and as such is a shorthand for empty().not().
30
+ * If the input collection is empty ({ }), the result is false.
31
+ *
32
+ * The function can also take an optional criteria to be applied to the collection
33
+ * prior to the determination of the exists. In this case, the function is shorthand
34
+ * for where(criteria).exists().
35
+ *
36
+ * See: https://hl7.org/fhirpath/#existscriteria-expression-boolean
37
+ *
38
+ * @param input
39
+ * @param criteria
40
+ * @returns True if the collection has unknown elements, and false otherwise.
41
+ */
42
+ exists: (input, criteria) => {
43
+ if (criteria) {
44
+ return booleanToTypedValue(input.filter((e) => toJsBoolean(criteria.eval([e]))).length > 0);
45
+ }
46
+ else {
47
+ return booleanToTypedValue(input.length > 0);
48
+ }
49
+ },
50
+ /**
51
+ * Returns true if for every element in the input collection, criteria evaluates to true.
52
+ * Otherwise, the result is false.
53
+ *
54
+ * If the input collection is empty ({ }), the result is true.
55
+ *
56
+ * See: https://hl7.org/fhirpath/#allcriteria-expression-boolean
57
+ *
58
+ * @param input The input collection.
59
+ * @param criteria The evaluation criteria.
60
+ * @returns True if for every element in the input collection, criteria evaluates to true.
61
+ */
62
+ all: (input, criteria) => {
63
+ return booleanToTypedValue(input.every((e) => toJsBoolean(criteria.eval([e]))));
64
+ },
65
+ /**
66
+ * Takes a collection of Boolean values and returns true if all the items are true.
67
+ * If unknown items are false, the result is false.
68
+ * If the input is empty ({ }), the result is true.
69
+ *
70
+ * See: https://hl7.org/fhirpath/#alltrue-boolean
71
+ *
72
+ * @param input The input collection.
73
+ * @param criteria The evaluation criteria.
74
+ * @returns True if all the items are true.
75
+ */
76
+ allTrue: (input) => {
77
+ for (const value of input) {
78
+ if (!value.value) {
79
+ return booleanToTypedValue(false);
80
+ }
81
+ }
82
+ return booleanToTypedValue(true);
83
+ },
84
+ /**
85
+ * Takes a collection of Boolean values and returns true if unknown of the items are true.
86
+ * If all the items are false, or if the input is empty ({ }), the result is false.
87
+ *
88
+ * See: https://hl7.org/fhirpath/#anytrue-boolean
89
+ *
90
+ * @param input The input collection.
91
+ * @param criteria The evaluation criteria.
92
+ * @returns True if unknown of the items are true.
93
+ */
94
+ anyTrue: (input) => {
95
+ for (const value of input) {
96
+ if (value.value) {
97
+ return booleanToTypedValue(true);
98
+ }
99
+ }
100
+ return booleanToTypedValue(false);
101
+ },
102
+ /**
103
+ * Takes a collection of Boolean values and returns true if all the items are false.
104
+ * If unknown items are true, the result is false.
105
+ * If the input is empty ({ }), the result is true.
106
+ *
107
+ * See: https://hl7.org/fhirpath/#allfalse-boolean
108
+ *
109
+ * @param input The input collection.
110
+ * @param criteria The evaluation criteria.
111
+ * @returns True if all the items are false.
112
+ */
113
+ allFalse: (input) => {
114
+ for (const value of input) {
115
+ if (value.value) {
116
+ return booleanToTypedValue(false);
117
+ }
118
+ }
119
+ return booleanToTypedValue(true);
120
+ },
121
+ /**
122
+ * Takes a collection of Boolean values and returns true if unknown of the items are false.
123
+ * If all the items are true, or if the input is empty ({ }), the result is false.
124
+ *
125
+ * See: https://hl7.org/fhirpath/#anyfalse-boolean
126
+ *
127
+ * @param input The input collection.
128
+ * @param criteria The evaluation criteria.
129
+ * @returns True if for every element in the input collection, criteria evaluates to true.
130
+ */
131
+ anyFalse: (input) => {
132
+ for (const value of input) {
133
+ if (!value.value) {
134
+ return booleanToTypedValue(true);
135
+ }
136
+ }
137
+ return booleanToTypedValue(false);
138
+ },
139
+ /**
140
+ * Returns true if all items in the input collection are members of the collection passed
141
+ * as the other argument. Membership is determined using the = (Equals) (=) operation.
142
+ *
143
+ * Conceptually, this function is evaluated by testing each element in the input collection
144
+ * for membership in the other collection, with a default of true. This means that if the
145
+ * input collection is empty ({ }), the result is true, otherwise if the other collection
146
+ * is empty ({ }), the result is false.
147
+ *
148
+ * See: http://hl7.org/fhirpath/#subsetofother-collection-boolean
149
+ */
150
+ subsetOf: stub,
151
+ /**
152
+ * Returns true if all items in the collection passed as the other argument are members of
153
+ * the input collection. Membership is determined using the = (Equals) (=) operation.
154
+ *
155
+ * Conceptually, this function is evaluated by testing each element in the other collection
156
+ * for membership in the input collection, with a default of true. This means that if the
157
+ * other collection is empty ({ }), the result is true, otherwise if the input collection
158
+ * is empty ({ }), the result is false.
159
+ *
160
+ * See: http://hl7.org/fhirpath/#supersetofother-collection-boolean
161
+ */
162
+ supersetOf: stub,
163
+ /**
164
+ * Returns the integer count of the number of items in the input collection.
165
+ * Returns 0 when the input collection is empty.
166
+ *
167
+ * See: https://hl7.org/fhirpath/#count-integer
168
+ *
169
+ * @param input The input collection.
170
+ * @returns The integer count of the number of items in the input collection.
171
+ */
172
+ count: (input) => {
173
+ return [{ type: PropertyType.integer, value: input.length }];
174
+ },
175
+ /**
176
+ * Returns a collection containing only the unique items in the input collection.
177
+ * To determine whether two items are the same, the = (Equals) (=) operator is used,
178
+ * as defined below.
179
+ *
180
+ * If the input collection is empty ({ }), the result is empty.
181
+ *
182
+ * Note that the order of elements in the input collection is not guaranteed to be
183
+ * preserved in the result.
184
+ *
185
+ * See: https://hl7.org/fhirpath/#distinct-collection
186
+ *
187
+ * @param input The input collection.
188
+ * @returns The integer count of the number of items in the input collection.
189
+ */
190
+ distinct: (input) => {
191
+ const result = [];
192
+ for (const value of input) {
193
+ if (!result.some((e) => e.value === value.value)) {
194
+ result.push(value);
195
+ }
196
+ }
197
+ return result;
198
+ },
199
+ /**
200
+ * Returns true if all the items in the input collection are distinct.
201
+ * To determine whether two items are distinct, the = (Equals) (=) operator is used,
202
+ * as defined below.
203
+ *
204
+ * See: https://hl7.org/fhirpath/#isdistinct-boolean
205
+ *
206
+ * @param input The input collection.
207
+ * @returns The integer count of the number of items in the input collection.
208
+ */
209
+ isDistinct: (input) => {
210
+ return booleanToTypedValue(input.length === functions.distinct(input).length);
211
+ },
212
+ /*
213
+ * 5.2 Filtering and projection
214
+ */
215
+ /**
216
+ * Returns a collection containing only those elements in the input collection
217
+ * for which the stated criteria expression evaluates to true.
218
+ * Elements for which the expression evaluates to false or empty ({ }) are not
219
+ * included in the result.
220
+ *
221
+ * If the input collection is empty ({ }), the result is empty.
222
+ *
223
+ * If the result of evaluating the condition is other than a single boolean value,
224
+ * the evaluation will end and signal an error to the calling environment,
225
+ * consistent with singleton evaluation of collections behavior.
226
+ *
227
+ * See: https://hl7.org/fhirpath/#wherecriteria-expression-collection
228
+ *
229
+ * @param input The input collection.
230
+ * @param condition The condition atom.
231
+ * @returns A collection containing only those elements in the input collection for which the stated criteria expression evaluates to true.
232
+ */
233
+ where: (input, criteria) => {
234
+ return input.filter((e) => toJsBoolean(criteria.eval([e])));
235
+ },
236
+ /**
237
+ * Evaluates the projection expression for each item in the input collection.
238
+ * The result of each evaluation is added to the output collection. If the
239
+ * evaluation results in a collection with multiple items, all items are added
240
+ * to the output collection (collections resulting from evaluation of projection
241
+ * are flattened). This means that if the evaluation for an element results in
242
+ * the empty collection ({ }), no element is added to the result, and that if
243
+ * the input collection is empty ({ }), the result is empty as well.
244
+ *
245
+ * See: http://hl7.org/fhirpath/#selectprojection-expression-collection
246
+ */
247
+ select: (input, criteria) => {
248
+ return input.map((e) => criteria.eval([e])).flat();
249
+ },
250
+ /**
251
+ * A version of select that will repeat the projection and add it to the output
252
+ * collection, as long as the projection yields new items (as determined by
253
+ * the = (Equals) (=) operator).
254
+ *
255
+ * See: http://hl7.org/fhirpath/#repeatprojection-expression-collection
256
+ */
257
+ repeat: stub,
258
+ /**
259
+ * Returns a collection that contains all items in the input collection that
260
+ * are of the given type or a subclass thereof. If the input collection is
261
+ * empty ({ }), the result is empty. The type argument is an identifier that
262
+ * must resolve to the name of a type in a model
263
+ *
264
+ * See: http://hl7.org/fhirpath/#oftypetype-type-specifier-collection
265
+ */
266
+ ofType: stub,
267
+ /*
268
+ * 5.3 Subsetting
269
+ */
270
+ /**
271
+ * Will return the single item in the input if there is just one item.
272
+ * If the input collection is empty ({ }), the result is empty.
273
+ * If there are multiple items, an error is signaled to the evaluation environment.
274
+ * This function is useful for ensuring that an error is returned if an assumption
275
+ * about cardinality is violated at run-time.
276
+ *
277
+ * See: https://hl7.org/fhirpath/#single-collection
278
+ *
279
+ * @param input The input collection.
280
+ * @returns The single item in the input if there is just one item.
281
+ */
282
+ single: (input) => {
283
+ if (input.length > 1) {
284
+ throw new Error('Expected input length one for single()');
285
+ }
286
+ return input.length === 0 ? [] : input.slice(0, 1);
287
+ },
288
+ /**
289
+ * Returns a collection containing only the first item in the input collection.
290
+ * This function is equivalent to item[0], so it will return an empty collection if the input collection has no items.
291
+ *
292
+ * See: https://hl7.org/fhirpath/#first-collection
293
+ *
294
+ * @param input The input collection.
295
+ * @returns A collection containing only the first item in the input collection.
296
+ */
297
+ first: (input) => {
298
+ return input.length === 0 ? [] : input.slice(0, 1);
299
+ },
300
+ /**
301
+ * Returns a collection containing only the last item in the input collection.
302
+ * Will return an empty collection if the input collection has no items.
303
+ *
304
+ * See: https://hl7.org/fhirpath/#last-collection
305
+ *
306
+ * @param input The input collection.
307
+ * @returns A collection containing only the last item in the input collection.
308
+ */
309
+ last: (input) => {
310
+ return input.length === 0 ? [] : input.slice(input.length - 1, input.length);
311
+ },
312
+ /**
313
+ * Returns a collection containing all but the first item in the input collection.
314
+ * Will return an empty collection if the input collection has no items, or only one item.
315
+ *
316
+ * See: https://hl7.org/fhirpath/#tail-collection
317
+ *
318
+ * @param input The input collection.
319
+ * @returns A collection containing all but the first item in the input collection.
320
+ */
321
+ tail: (input) => {
322
+ return input.length === 0 ? [] : input.slice(1, input.length);
323
+ },
324
+ /**
325
+ * Returns a collection containing all but the first num items in the input collection.
326
+ * Will return an empty collection if there are no items remaining after the
327
+ * indicated number of items have been skipped, or if the input collection is empty.
328
+ * If num is less than or equal to zero, the input collection is simply returned.
329
+ *
330
+ * See: https://hl7.org/fhirpath/#skipnum-integer-collection
331
+ *
332
+ * @param input The input collection.
333
+ * @returns A collection containing all but the first item in the input collection.
334
+ */
335
+ skip: (input, num) => {
336
+ var _a;
337
+ const numValue = (_a = num.eval(input)[0]) === null || _a === void 0 ? void 0 : _a.value;
338
+ if (typeof numValue !== 'number') {
339
+ throw new Error('Expected a number for skip(num)');
340
+ }
341
+ if (numValue >= input.length) {
342
+ return [];
343
+ }
344
+ if (numValue <= 0) {
345
+ return input;
346
+ }
347
+ return input.slice(numValue, input.length);
348
+ },
349
+ /**
350
+ * Returns a collection containing the first num items in the input collection,
351
+ * or less if there are less than num items.
352
+ * If num is less than or equal to 0, or if the input collection is empty ({ }),
353
+ * take returns an empty collection.
354
+ *
355
+ * See: https://hl7.org/fhirpath/#takenum-integer-collection
356
+ *
357
+ * @param input The input collection.
358
+ * @returns A collection containing the first num items in the input collection.
359
+ */
360
+ take: (input, num) => {
361
+ var _a;
362
+ const numValue = (_a = num.eval(input)[0]) === null || _a === void 0 ? void 0 : _a.value;
363
+ if (typeof numValue !== 'number') {
364
+ throw new Error('Expected a number for take(num)');
365
+ }
366
+ if (numValue >= input.length) {
367
+ return input;
368
+ }
369
+ if (numValue <= 0) {
370
+ return [];
371
+ }
372
+ return input.slice(0, numValue);
373
+ },
374
+ /**
375
+ * Returns the set of elements that are in both collections.
376
+ * Duplicate items will be eliminated by this function.
377
+ * Order of items is not guaranteed to be preserved in the result of this function.
378
+ *
379
+ * See: http://hl7.org/fhirpath/#intersectother-collection-collection
380
+ */
381
+ intersect: (input, other) => {
382
+ if (!other) {
383
+ return input;
384
+ }
385
+ const otherArray = other.eval(input);
386
+ const result = [];
387
+ for (const value of input) {
388
+ if (!result.some((e) => e.value === value.value) && otherArray.some((e) => e.value === value.value)) {
389
+ result.push(value);
390
+ }
391
+ }
392
+ return result;
393
+ },
394
+ /**
395
+ * Returns the set of elements that are not in the other collection.
396
+ * Duplicate items will not be eliminated by this function, and order will be preserved.
397
+ *
398
+ * e.g. (1 | 2 | 3).exclude(2) returns (1 | 3).
399
+ *
400
+ * See: http://hl7.org/fhirpath/#excludeother-collection-collection
401
+ */
402
+ exclude: (input, other) => {
403
+ if (!other) {
404
+ return input;
405
+ }
406
+ const otherArray = other.eval(input);
407
+ const result = [];
408
+ for (const value of input) {
409
+ if (!otherArray.some((e) => e.value === value.value)) {
410
+ result.push(value);
411
+ }
412
+ }
413
+ return result;
414
+ },
415
+ /*
416
+ * 5.4. Combining
417
+ *
418
+ * See: https://hl7.org/fhirpath/#combining
419
+ */
420
+ /**
421
+ * Merge the two collections into a single collection,
422
+ * eliminating unknown duplicate values (using = (Equals) (=) to determine equality).
423
+ * There is no expectation of order in the resulting collection.
424
+ *
425
+ * In other words, this function returns the distinct list of elements from both inputs.
426
+ *
427
+ * See: http://hl7.org/fhirpath/#unionother-collection
428
+ */
429
+ union: (input, other) => {
430
+ if (!other) {
431
+ return input;
432
+ }
433
+ const otherArray = other.eval(input);
434
+ return removeDuplicates([...input, ...otherArray]);
435
+ },
436
+ /**
437
+ * Merge the input and other collections into a single collection
438
+ * without eliminating duplicate values. Combining an empty collection
439
+ * with a non-empty collection will return the non-empty collection.
440
+ *
441
+ * There is no expectation of order in the resulting collection.
442
+ *
443
+ * See: http://hl7.org/fhirpath/#combineother-collection-collection
444
+ */
445
+ combine: (input, other) => {
446
+ if (!other) {
447
+ return input;
448
+ }
449
+ const otherArray = other.eval(input);
450
+ return [...input, ...otherArray];
451
+ },
452
+ /*
453
+ * 5.5. Conversion
454
+ *
455
+ * See: https://hl7.org/fhirpath/#conversion
456
+ */
457
+ /**
458
+ * The iif function in FHIRPath is an immediate if,
459
+ * also known as a conditional operator (such as C’s ? : operator).
460
+ *
461
+ * The criterion expression is expected to evaluate to a Boolean.
462
+ *
463
+ * If criterion is true, the function returns the value of the true-result argument.
464
+ *
465
+ * If criterion is false or an empty collection, the function returns otherwise-result,
466
+ * unless the optional otherwise-result is not given, in which case the function returns an empty collection.
467
+ *
468
+ * Note that short-circuit behavior is expected in this function. In other words,
469
+ * true-result should only be evaluated if the criterion evaluates to true,
470
+ * and otherwise-result should only be evaluated otherwise. For implementations,
471
+ * this means delaying evaluation of the arguments.
472
+ *
473
+ * @param input
474
+ * @param criterion
475
+ * @param trueResult
476
+ * @param otherwiseResult
477
+ * @returns
478
+ */
479
+ iif: (input, criterion, trueResult, otherwiseResult) => {
480
+ const evalResult = criterion.eval(input);
481
+ if (evalResult.length > 1 || (evalResult.length === 1 && typeof evalResult[0].value !== 'boolean')) {
482
+ throw new Error('Expected criterion to evaluate to a Boolean');
483
+ }
484
+ if (toJsBoolean(evalResult)) {
485
+ return trueResult.eval(input);
486
+ }
487
+ if (otherwiseResult) {
488
+ return otherwiseResult.eval(input);
489
+ }
490
+ return [];
491
+ },
492
+ /**
493
+ * Converts an input collection to a boolean.
494
+ *
495
+ * If the input collection contains a single item, this function will return a single boolean if:
496
+ * 1) the item is a Boolean
497
+ * 2) the item is an Integer and is equal to one of the possible integer representations of Boolean values
498
+ * 3) the item is a Decimal that is equal to one of the possible decimal representations of Boolean values
499
+ * 4) the item is a String that is equal to one of the possible string representations of Boolean values
500
+ *
501
+ * If the item is not one the above types, or the item is a String, Integer, or Decimal, but is not equal to one of the possible values convertible to a Boolean, the result is empty.
502
+ *
503
+ * See: https://hl7.org/fhirpath/#toboolean-boolean
504
+ *
505
+ * @param input
506
+ * @returns
507
+ */
508
+ toBoolean: (input) => {
509
+ if (input.length === 0) {
510
+ return [];
511
+ }
512
+ const [{ value }] = validateInput(input, 1);
513
+ if (typeof value === 'boolean') {
514
+ return [{ type: PropertyType.boolean, value }];
515
+ }
516
+ if (typeof value === 'number') {
517
+ if (value === 0 || value === 1) {
518
+ return booleanToTypedValue(!!value);
519
+ }
520
+ }
521
+ if (typeof value === 'string') {
522
+ const lowerStr = value.toLowerCase();
523
+ if (['true', 't', 'yes', 'y', '1', '1.0'].includes(lowerStr)) {
524
+ return booleanToTypedValue(true);
525
+ }
526
+ if (['false', 'f', 'no', 'n', '0', '0.0'].includes(lowerStr)) {
527
+ return booleanToTypedValue(false);
528
+ }
529
+ }
530
+ return [];
531
+ },
532
+ /**
533
+ * If the input collection contains a single item, this function will return true if:
534
+ * 1) the item is a Boolean
535
+ * 2) the item is an Integer that is equal to one of the possible integer representations of Boolean values
536
+ * 3) the item is a Decimal that is equal to one of the possible decimal representations of Boolean values
537
+ * 4) the item is a String that is equal to one of the possible string representations of Boolean values
538
+ *
539
+ * If the item is not one of the above types, or the item is a String, Integer, or Decimal, but is not equal to one of the possible values convertible to a Boolean, the result is false.
540
+ *
541
+ * Possible values for Integer, Decimal, and String are described in the toBoolean() function.
542
+ *
543
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
544
+ *
545
+ * If the input collection is empty, the result is empty.
546
+ *
547
+ * See: http://hl7.org/fhirpath/#convertstoboolean-boolean
548
+ *
549
+ * @param input
550
+ * @returns
551
+ */
552
+ convertsToBoolean: (input) => {
553
+ if (input.length === 0) {
554
+ return [];
555
+ }
556
+ return booleanToTypedValue(functions.toBoolean(input).length === 1);
557
+ },
558
+ /**
559
+ * Returns the integer representation of the input.
560
+ *
561
+ * If the input collection contains a single item, this function will return a single integer if:
562
+ * 1) the item is an Integer
563
+ * 2) the item is a String and is convertible to an integer
564
+ * 3) the item is a Boolean, where true results in a 1 and false results in a 0.
565
+ *
566
+ * If the item is not one the above types, the result is empty.
567
+ *
568
+ * If the item is a String, but the string is not convertible to an integer (using the regex format (\\+|-)?\d+), the result is empty.
569
+ *
570
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
571
+ *
572
+ * If the input collection is empty, the result is empty.
573
+ *
574
+ * See: https://hl7.org/fhirpath/#tointeger-integer
575
+ *
576
+ * @param input The input collection.
577
+ * @returns The string representation of the input.
578
+ */
579
+ toInteger: (input) => {
580
+ if (input.length === 0) {
581
+ return [];
582
+ }
583
+ const [{ value }] = validateInput(input, 1);
584
+ if (typeof value === 'number') {
585
+ return [{ type: PropertyType.integer, value }];
586
+ }
587
+ if (typeof value === 'string' && value.match(/^[+-]?\d+$/)) {
588
+ return [{ type: PropertyType.integer, value: parseInt(value, 10) }];
589
+ }
590
+ if (typeof value === 'boolean') {
591
+ return [{ type: PropertyType.integer, value: value ? 1 : 0 }];
592
+ }
593
+ return [];
594
+ },
595
+ /**
596
+ * Returns true if the input can be converted to string.
597
+ *
598
+ * If the input collection contains a single item, this function will return true if:
599
+ * 1) the item is an Integer
600
+ * 2) the item is a String and is convertible to an Integer
601
+ * 3) the item is a Boolean
602
+ * 4) If the item is not one of the above types, or the item is a String, but is not convertible to an Integer (using the regex format (\\+|-)?\d+), the result is false.
603
+ *
604
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
605
+ *
606
+ * If the input collection is empty, the result is empty.
607
+ *
608
+ * See: https://hl7.org/fhirpath/#convertstointeger-boolean
609
+ *
610
+ * @param input The input collection.
611
+ * @returns
612
+ */
613
+ convertsToInteger: (input) => {
614
+ if (input.length === 0) {
615
+ return [];
616
+ }
617
+ return booleanToTypedValue(functions.toInteger(input).length === 1);
618
+ },
619
+ /**
620
+ * If the input collection contains a single item, this function will return a single date if:
621
+ * 1) the item is a Date
622
+ * 2) the item is a DateTime
623
+ * 3) the item is a String and is convertible to a Date
624
+ *
625
+ * If the item is not one of the above types, the result is empty.
626
+ *
627
+ * If the item is a String, but the string is not convertible to a Date (using the format YYYY-MM-DD), the result is empty.
628
+ *
629
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
630
+ *
631
+ * If the input collection is empty, the result is empty.
632
+ *
633
+ * See: https://hl7.org/fhirpath/#todate-date
634
+ */
635
+ toDate: (input) => {
636
+ if (input.length === 0) {
637
+ return [];
638
+ }
639
+ const [{ value }] = validateInput(input, 1);
640
+ if (typeof value === 'string' && value.match(/^\d{4}(-\d{2}(-\d{2})?)?/)) {
641
+ return [{ type: PropertyType.date, value: parseDateString(value) }];
642
+ }
643
+ return [];
644
+ },
645
+ /**
646
+ * If the input collection contains a single item, this function will return true if:
647
+ * 1) the item is a Date
648
+ * 2) the item is a DateTime
649
+ * 3) the item is a String and is convertible to a Date
650
+ *
651
+ * If the item is not one of the above types, or is not convertible to a Date (using the format YYYY-MM-DD), the result is false.
652
+ *
653
+ * If the item contains a partial date (e.g. '2012-01'), the result is a partial date.
654
+ *
655
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
656
+ *
657
+ * If the input collection is empty, the result is empty.
658
+ *
659
+ * See: https://hl7.org/fhirpath/#convertstodate-boolean
660
+ */
661
+ convertsToDate: (input) => {
662
+ if (input.length === 0) {
663
+ return [];
664
+ }
665
+ return booleanToTypedValue(functions.toDate(input).length === 1);
666
+ },
667
+ /**
668
+ * If the input collection contains a single item, this function will return a single datetime if:
669
+ * 1) the item is a DateTime
670
+ * 2) the item is a Date, in which case the result is a DateTime with the year, month, and day of the Date, and the time components empty (not set to zero)
671
+ * 3) the item is a String and is convertible to a DateTime
672
+ *
673
+ * If the item is not one of the above types, the result is empty.
674
+ *
675
+ * If the item is a String, but the string is not convertible to a DateTime (using the format YYYY-MM-DDThh:mm:ss.fff(+|-)hh:mm), the result is empty.
676
+ *
677
+ * If the item contains a partial datetime (e.g. '2012-01-01T10:00'), the result is a partial datetime.
678
+ *
679
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
680
+ *
681
+ * If the input collection is empty, the result is empty.
682
+
683
+ * See: https://hl7.org/fhirpath/#todatetime-datetime
684
+ *
685
+ * @param input
686
+ * @returns
687
+ */
688
+ toDateTime: (input) => {
689
+ if (input.length === 0) {
690
+ return [];
691
+ }
692
+ const [{ value }] = validateInput(input, 1);
693
+ if (typeof value === 'string' && value.match(/^\d{4}(-\d{2}(-\d{2})?)?/)) {
694
+ return [{ type: PropertyType.dateTime, value: parseDateString(value) }];
695
+ }
696
+ return [];
697
+ },
698
+ /**
699
+ * If the input collection contains a single item, this function will return true if:
700
+ * 1) the item is a DateTime
701
+ * 2) the item is a Date
702
+ * 3) the item is a String and is convertible to a DateTime
703
+ *
704
+ * If the item is not one of the above types, or is not convertible to a DateTime (using the format YYYY-MM-DDThh:mm:ss.fff(+|-)hh:mm), the result is false.
705
+ *
706
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
707
+ *
708
+ * If the input collection is empty, the result is empty.
709
+ *
710
+ * See: https://hl7.org/fhirpath/#convertstodatetime-boolean
711
+ *
712
+ * @param input
713
+ * @returns
714
+ */
715
+ convertsToDateTime: (input) => {
716
+ if (input.length === 0) {
717
+ return [];
718
+ }
719
+ return booleanToTypedValue(functions.toDateTime(input).length === 1);
720
+ },
721
+ /**
722
+ * If the input collection contains a single item, this function will return a single decimal if:
723
+ * 1) the item is an Integer or Decimal
724
+ * 2) the item is a String and is convertible to a Decimal
725
+ * 3) the item is a Boolean, where true results in a 1.0 and false results in a 0.0.
726
+ * 4) If the item is not one of the above types, the result is empty.
727
+ *
728
+ * If the item is a String, but the string is not convertible to a Decimal (using the regex format (\\+|-)?\d+(\.\d+)?), the result is empty.
729
+ *
730
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
731
+ *
732
+ * If the input collection is empty, the result is empty.
733
+ *
734
+ * See: https://hl7.org/fhirpath/#decimal-conversion-functions
735
+ *
736
+ * @param input The input collection.
737
+ * @returns
738
+ */
739
+ toDecimal: (input) => {
740
+ if (input.length === 0) {
741
+ return [];
742
+ }
743
+ const [{ value }] = validateInput(input, 1);
744
+ if (typeof value === 'number') {
745
+ return [{ type: PropertyType.decimal, value }];
746
+ }
747
+ if (typeof value === 'string' && value.match(/^-?\d{1,9}(\.\d{1,9})?$/)) {
748
+ return [{ type: PropertyType.decimal, value: parseFloat(value) }];
749
+ }
750
+ if (typeof value === 'boolean') {
751
+ return [{ type: PropertyType.decimal, value: value ? 1 : 0 }];
752
+ }
753
+ return [];
754
+ },
755
+ /**
756
+ * If the input collection contains a single item, this function will true if:
757
+ * 1) the item is an Integer or Decimal
758
+ * 2) the item is a String and is convertible to a Decimal
759
+ * 3) the item is a Boolean
760
+ *
761
+ * If the item is not one of the above types, or is not convertible to a Decimal (using the regex format (\\+|-)?\d+(\.\d+)?), the result is false.
762
+ *
763
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
764
+ *
765
+ * If the input collection is empty, the result is empty.
766
+
767
+ * See: https://hl7.org/fhirpath/#convertstodecimal-boolean
768
+ *
769
+ * @param input The input collection.
770
+ * @returns
771
+ */
772
+ convertsToDecimal: (input) => {
773
+ if (input.length === 0) {
774
+ return [];
775
+ }
776
+ return booleanToTypedValue(functions.toDecimal(input).length === 1);
777
+ },
778
+ /**
779
+ * If the input collection contains a single item, this function will return a single quantity if:
780
+ * 1) the item is an Integer, or Decimal, where the resulting quantity will have the default unit ('1')
781
+ * 2) the item is a Quantity
782
+ * 3) the item is a String and is convertible to a Quantity
783
+ * 4) the item is a Boolean, where true results in the quantity 1.0 '1', and false results in the quantity 0.0 '1'
784
+ *
785
+ * If the item is not one of the above types, the result is empty.
786
+ *
787
+ * See: https://hl7.org/fhirpath/#quantity-conversion-functions
788
+ *
789
+ * @param input The input collection.
790
+ * @returns
791
+ */
792
+ toQuantity: (input) => {
793
+ if (input.length === 0) {
794
+ return [];
795
+ }
796
+ const [{ value }] = validateInput(input, 1);
797
+ if (isQuantity(value)) {
798
+ return [{ type: PropertyType.Quantity, value }];
799
+ }
800
+ if (typeof value === 'number') {
801
+ return [{ type: PropertyType.Quantity, value: { value, unit: '1' } }];
802
+ }
803
+ if (typeof value === 'string' && value.match(/^-?\d{1,9}(\.\d{1,9})?/)) {
804
+ return [{ type: PropertyType.Quantity, value: { value: parseFloat(value), unit: '1' } }];
805
+ }
806
+ if (typeof value === 'boolean') {
807
+ return [{ type: PropertyType.Quantity, value: { value: value ? 1 : 0, unit: '1' } }];
808
+ }
809
+ return [];
810
+ },
811
+ /**
812
+ * If the input collection contains a single item, this function will return true if:
813
+ * 1) the item is an Integer, Decimal, or Quantity
814
+ * 2) the item is a String that is convertible to a Quantity
815
+ * 3) the item is a Boolean
816
+ *
817
+ * If the item is not one of the above types, or is not convertible to a Quantity using the following regex format:
818
+ *
819
+ * (?'value'(\+|-)?\d+(\.\d+)?)\s*('(?'unit'[^']+)'|(?'time'[a-zA-Z]+))?
820
+ *
821
+ * then the result is false.
822
+ *
823
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
824
+ *
825
+ * If the input collection is empty, the result is empty.
826
+ *
827
+ * If the unit argument is provided, it must be the string representation of a UCUM code (or a FHIRPath calendar duration keyword), and is used to determine whether the input quantity can be converted to the given unit, according to the unit conversion rules specified by UCUM. If the input quantity can be converted, the result is true, otherwise, the result is false.
828
+ *
829
+ * See: https://hl7.org/fhirpath/#convertstoquantityunit-string-boolean
830
+ *
831
+ * @param input The input collection.
832
+ * @returns
833
+ */
834
+ convertsToQuantity: (input) => {
835
+ if (input.length === 0) {
836
+ return [];
837
+ }
838
+ return booleanToTypedValue(functions.toQuantity(input).length === 1);
839
+ },
840
+ /**
841
+ * Returns the string representation of the input.
842
+ *
843
+ * If the input collection contains a single item, this function will return a single String if:
844
+ *
845
+ * 1) the item in the input collection is a String
846
+ * 2) the item in the input collection is an Integer, Decimal, Date, Time, DateTime, or Quantity the output will contain its String representation
847
+ * 3) the item is a Boolean, where true results in 'true' and false in 'false'.
848
+ *
849
+ * If the item is not one of the above types, the result is false.
850
+ *
851
+ * See: https://hl7.org/fhirpath/#tostring-string
852
+ *
853
+ * @param input The input collection.
854
+ * @returns The string representation of the input.
855
+ */
856
+ toString: (input) => {
857
+ if (input.length === 0) {
858
+ return [];
859
+ }
860
+ const [{ value }] = validateInput(input, 1);
861
+ if (value === null || value === undefined) {
862
+ return [];
863
+ }
864
+ if (isQuantity(value)) {
865
+ return [{ type: PropertyType.string, value: `${value.value} '${value.unit}'` }];
866
+ }
867
+ return [{ type: PropertyType.string, value: value.toString() }];
868
+ },
869
+ /**
870
+ * Returns true if the input can be converted to string.
871
+ *
872
+ * If the input collection contains a single item, this function will return true if:
873
+ * 1) the item is a String
874
+ * 2) the item is an Integer, Decimal, Date, Time, or DateTime
875
+ * 3) the item is a Boolean
876
+ * 4) the item is a Quantity
877
+ *
878
+ * If the item is not one of the above types, the result is false.
879
+ *
880
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
881
+ *
882
+ * If the input collection is empty, the result is empty.
883
+ *
884
+ * See: https://hl7.org/fhirpath/#tostring-string
885
+ *
886
+ * @param input The input collection.
887
+ * @returns
888
+ */
889
+ convertsToString: (input) => {
890
+ if (input.length === 0) {
891
+ return [];
892
+ }
893
+ return booleanToTypedValue(functions.toString(input).length === 1);
894
+ },
895
+ /**
896
+ * If the input collection contains a single item, this function will return a single time if:
897
+ * 1) the item is a Time
898
+ * 2) the item is a String and is convertible to a Time
899
+ *
900
+ * If the item is not one of the above types, the result is empty.
901
+ *
902
+ * If the item is a String, but the string is not convertible to a Time (using the format hh:mm:ss.fff(+|-)hh:mm), the result is empty.
903
+ *
904
+ * If the item contains a partial time (e.g. '10:00'), the result is a partial time.
905
+ *
906
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
907
+ *
908
+ * If the input collection is empty, the result is empty.
909
+ *
910
+ * See: https://hl7.org/fhirpath/#totime-time
911
+ *
912
+ * @param input
913
+ * @returns
914
+ */
915
+ toTime: (input) => {
916
+ if (input.length === 0) {
917
+ return [];
918
+ }
919
+ const [{ value }] = validateInput(input, 1);
920
+ if (typeof value === 'string') {
921
+ const match = value.match(/^T?(\d{2}(:\d{2}(:\d{2})?)?)/);
922
+ if (match) {
923
+ return [{ type: PropertyType.time, value: parseDateString('T' + match[1]) }];
924
+ }
925
+ }
926
+ return [];
927
+ },
928
+ /**
929
+ * If the input collection contains a single item, this function will return true if:
930
+ * 1) the item is a Time
931
+ * 2) the item is a String and is convertible to a Time
932
+ *
933
+ * If the item is not one of the above types, or is not convertible to a Time (using the format hh:mm:ss.fff(+|-)hh:mm), the result is false.
934
+ *
935
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
936
+ *
937
+ * If the input collection is empty, the result is empty.
938
+ *
939
+ * See: https://hl7.org/fhirpath/#convertstotime-boolean
940
+ *
941
+ * @param input
942
+ * @returns
943
+ */
944
+ convertsToTime: (input) => {
945
+ if (input.length === 0) {
946
+ return [];
947
+ }
948
+ return booleanToTypedValue(functions.toTime(input).length === 1);
949
+ },
950
+ /*
951
+ * 5.6. String Manipulation.
952
+ *
953
+ * See: https://hl7.org/fhirpath/#string-manipulation
954
+ */
955
+ /**
956
+ * Returns the 0-based index of the first position substring is found in the input string, or -1 if it is not found.
957
+ *
958
+ * If substring is an empty string (''), the function returns 0.
959
+ *
960
+ * If the input or substring is empty ({ }), the result is empty ({ }).
961
+ *
962
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
963
+ *
964
+ * See: https://hl7.org/fhirpath/#indexofsubstring-string-integer
965
+ *
966
+ * @param input The input collection.
967
+ * @returns The index of the substring.
968
+ */
969
+ indexOf: (input, substringAtom) => {
970
+ return applyStringFunc((str, substring) => str.indexOf(substring), input, substringAtom);
971
+ },
972
+ /**
973
+ * Returns the part of the string starting at position start (zero-based). If length is given, will return at most length number of characters from the input string.
974
+ *
975
+ * If start lies outside the length of the string, the function returns empty ({ }). If there are less remaining characters in the string than indicated by length, the function returns just the remaining characters.
976
+ *
977
+ * If the input or start is empty, the result is empty.
978
+ *
979
+ * If an empty length is provided, the behavior is the same as if length had not been provided.
980
+ *
981
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
982
+ *
983
+ * @param input The input collection.
984
+ * @returns The index of the substring.
985
+ */
986
+ substring: (input, startAtom, lengthAtom) => {
987
+ return applyStringFunc((str, start, length) => {
988
+ const startIndex = start;
989
+ const endIndex = length ? startIndex + length : str.length;
990
+ return startIndex < 0 || startIndex >= str.length ? undefined : str.substring(startIndex, endIndex);
991
+ }, input, startAtom, lengthAtom);
992
+ },
993
+ /**
994
+ *
995
+ * @param input The input collection.
996
+ * @returns The index of the substring.
997
+ */
998
+ startsWith: (input, prefixAtom) => {
999
+ return applyStringFunc((str, prefix) => str.startsWith(prefix), input, prefixAtom);
1000
+ },
1001
+ /**
1002
+ *
1003
+ * @param input The input collection.
1004
+ * @returns The index of the substring.
1005
+ */
1006
+ endsWith: (input, suffixAtom) => {
1007
+ return applyStringFunc((str, suffix) => str.endsWith(suffix), input, suffixAtom);
1008
+ },
1009
+ /**
1010
+ *
1011
+ * @param input The input collection.
1012
+ * @returns The index of the substring.
1013
+ */
1014
+ contains: (input, substringAtom) => {
1015
+ return applyStringFunc((str, substring) => str.includes(substring), input, substringAtom);
1016
+ },
1017
+ /**
1018
+ *
1019
+ * @param input The input collection.
1020
+ * @returns The index of the substring.
1021
+ */
1022
+ upper: (input) => {
1023
+ return applyStringFunc((str) => str.toUpperCase(), input);
1024
+ },
1025
+ /**
1026
+ *
1027
+ * @param input The input collection.
1028
+ * @returns The index of the substring.
1029
+ */
1030
+ lower: (input) => {
1031
+ return applyStringFunc((str) => str.toLowerCase(), input);
1032
+ },
1033
+ /**
1034
+ *
1035
+ * @param input The input collection.
1036
+ * @returns The index of the substring.
1037
+ */
1038
+ replace: (input, patternAtom, substitionAtom) => {
1039
+ return applyStringFunc((str, pattern, substition) => str.replaceAll(pattern, substition), input, patternAtom, substitionAtom);
1040
+ },
1041
+ /**
1042
+ *
1043
+ * @param input The input collection.
1044
+ * @returns The index of the substring.
1045
+ */
1046
+ matches: (input, regexAtom) => {
1047
+ return applyStringFunc((str, regex) => !!str.match(regex), input, regexAtom);
1048
+ },
1049
+ /**
1050
+ *
1051
+ * @param input The input collection.
1052
+ * @returns The index of the substring.
1053
+ */
1054
+ replaceMatches: (input, regexAtom, substitionAtom) => {
1055
+ return applyStringFunc((str, pattern, substition) => str.replaceAll(pattern, substition), input, regexAtom, substitionAtom);
1056
+ },
1057
+ /**
1058
+ *
1059
+ * @param input The input collection.
1060
+ * @returns The index of the substring.
1061
+ */
1062
+ length: (input) => {
1063
+ return applyStringFunc((str) => str.length, input);
1064
+ },
1065
+ /**
1066
+ * Returns the list of characters in the input string. If the input collection is empty ({ }), the result is empty.
1067
+ *
1068
+ * See: https://hl7.org/fhirpath/#tochars-collection
1069
+ *
1070
+ * @param input The input collection.
1071
+ */
1072
+ toChars: (input) => {
1073
+ return applyStringFunc((str) => (str ? str.split('') : undefined), input);
1074
+ },
1075
+ /*
1076
+ * 5.7. Math
1077
+ */
1078
+ /**
1079
+ * Returns the absolute value of the input. When taking the absolute value of a quantity, the unit is unchanged.
1080
+ *
1081
+ * If the input collection is empty, the result is empty.
1082
+ *
1083
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
1084
+ *
1085
+ * See: https://hl7.org/fhirpath/#abs-integer-decimal-quantity
1086
+ *
1087
+ * @param input The input collection.
1088
+ * @returns A collection containing the result.
1089
+ */
1090
+ abs: (input) => {
1091
+ return applyMathFunc(Math.abs, input);
1092
+ },
1093
+ /**
1094
+ * Returns the first integer greater than or equal to the input.
1095
+ *
1096
+ * If the input collection is empty, the result is empty.
1097
+ *
1098
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
1099
+ *
1100
+ * See: https://hl7.org/fhirpath/#ceiling-integer
1101
+ *
1102
+ * @param input The input collection.
1103
+ * @returns A collection containing the result.
1104
+ */
1105
+ ceiling: (input) => {
1106
+ return applyMathFunc(Math.ceil, input);
1107
+ },
1108
+ /**
1109
+ * Returns e raised to the power of the input.
1110
+ *
1111
+ * If the input collection contains an Integer, it will be implicitly converted to a Decimal and the result will be a Decimal.
1112
+ *
1113
+ * If the input collection is empty, the result is empty.
1114
+ *
1115
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
1116
+ *
1117
+ * See: https://hl7.org/fhirpath/#exp-decimal
1118
+ *
1119
+ * @param input The input collection.
1120
+ * @returns A collection containing the result.
1121
+ */
1122
+ exp: (input) => {
1123
+ return applyMathFunc(Math.exp, input);
1124
+ },
1125
+ /**
1126
+ * Returns the first integer less than or equal to the input.
1127
+ *
1128
+ * If the input collection is empty, the result is empty.
1129
+ *
1130
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
1131
+ *
1132
+ * See: https://hl7.org/fhirpath/#floor-integer
1133
+ *
1134
+ * @param input The input collection.
1135
+ * @returns A collection containing the result.
1136
+ */
1137
+ floor: (input) => {
1138
+ return applyMathFunc(Math.floor, input);
1139
+ },
1140
+ /**
1141
+ * Returns the natural logarithm of the input (i.e. the logarithm base e).
1142
+ *
1143
+ * When used with an Integer, it will be implicitly converted to a Decimal.
1144
+ *
1145
+ * If the input collection is empty, the result is empty.
1146
+ *
1147
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
1148
+ *
1149
+ * See: https://hl7.org/fhirpath/#ln-decimal
1150
+ *
1151
+ * @param input The input collection.
1152
+ * @returns A collection containing the result.
1153
+ */
1154
+ ln: (input) => {
1155
+ return applyMathFunc(Math.log, input);
1156
+ },
1157
+ /**
1158
+ * Returns the logarithm base base of the input number.
1159
+ *
1160
+ * When used with Integers, the arguments will be implicitly converted to Decimal.
1161
+ *
1162
+ * If base is empty, the result is empty.
1163
+ *
1164
+ * If the input collection is empty, the result is empty.
1165
+ *
1166
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
1167
+ *
1168
+ * See: https://hl7.org/fhirpath/#logbase-decimal-decimal
1169
+ *
1170
+ * @param input The input collection.
1171
+ * @returns A collection containing the result.
1172
+ */
1173
+ log: (input, baseAtom) => {
1174
+ return applyMathFunc((value, base) => Math.log(value) / Math.log(base), input, baseAtom);
1175
+ },
1176
+ /**
1177
+ * Raises a number to the exponent power. If this function is used with Integers, the result is an Integer. If the function is used with Decimals, the result is a Decimal. If the function is used with a mixture of Integer and Decimal, the Integer is implicitly converted to a Decimal and the result is a Decimal.
1178
+ *
1179
+ * If the power cannot be represented (such as the -1 raised to the 0.5), the result is empty.
1180
+ *
1181
+ * If the input is empty, or exponent is empty, the result is empty.
1182
+ *
1183
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
1184
+ *
1185
+ * See: https://hl7.org/fhirpath/#powerexponent-integer-decimal-integer-decimal
1186
+ *
1187
+ * @param input The input collection.
1188
+ * @returns A collection containing the result.
1189
+ */
1190
+ power: (input, expAtom) => {
1191
+ return applyMathFunc(Math.pow, input, expAtom);
1192
+ },
1193
+ /**
1194
+ * Rounds the decimal to the nearest whole number using a traditional round (i.e. 0.5 or higher will round to 1). If specified, the precision argument determines the decimal place at which the rounding will occur. If not specified, the rounding will default to 0 decimal places.
1195
+ *
1196
+ * If specified, the number of digits of precision must be >= 0 or the evaluation will end and signal an error to the calling environment.
1197
+ *
1198
+ * If the input collection contains a single item of type Integer, it will be implicitly converted to a Decimal.
1199
+ *
1200
+ * If the input collection is empty, the result is empty.
1201
+ *
1202
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
1203
+ *
1204
+ * See: https://hl7.org/fhirpath/#roundprecision-integer-decimal
1205
+ *
1206
+ * @param input The input collection.
1207
+ * @returns A collection containing the result.
1208
+ */
1209
+ round: (input) => {
1210
+ return applyMathFunc(Math.round, input);
1211
+ },
1212
+ /**
1213
+ * Returns the square root of the input number as a Decimal.
1214
+ *
1215
+ * If the square root cannot be represented (such as the square root of -1), the result is empty.
1216
+ *
1217
+ * If the input collection is empty, the result is empty.
1218
+ *
1219
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
1220
+ *
1221
+ * Note that this function is equivalent to raising a number of the power of 0.5 using the power() function.
1222
+ *
1223
+ * See: https://hl7.org/fhirpath/#sqrt-decimal
1224
+ *
1225
+ * @param input The input collection.
1226
+ * @returns A collection containing the result.
1227
+ */
1228
+ sqrt: (input) => {
1229
+ return applyMathFunc(Math.sqrt, input);
1230
+ },
1231
+ /**
1232
+ * Returns the integer portion of the input.
1233
+ *
1234
+ * If the input collection is empty, the result is empty.
1235
+ *
1236
+ * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.
1237
+ *
1238
+ * See: https://hl7.org/fhirpath/#truncate-integer
1239
+ *
1240
+ * @param input The input collection.
1241
+ * @returns A collection containing the result.
1242
+ */
1243
+ truncate: (input) => {
1244
+ return applyMathFunc((x) => x | 0, input);
1245
+ },
1246
+ /*
1247
+ * 5.8. Tree navigation
1248
+ */
1249
+ children: stub,
1250
+ descendants: stub,
1251
+ /*
1252
+ * 5.9. Utility functions
1253
+ */
1254
+ /**
1255
+ * Adds a String representation of the input collection to the diagnostic log,
1256
+ * using the name argument as the name in the log. This log should be made available
1257
+ * to the user in some appropriate fashion. Does not change the input, so returns
1258
+ * the input collection as output.
1259
+ *
1260
+ * If the projection argument is used, the trace would log the result of evaluating
1261
+ * the project expression on the input, but still return the input to the trace
1262
+ * function unchanged.
1263
+ *
1264
+ * See: https://hl7.org/fhirpath/#tracename-string-projection-expression-collection
1265
+ *
1266
+ * @param input The input collection.
1267
+ * @param nameAtom The log name.
1268
+ */
1269
+ trace: (input, nameAtom) => {
1270
+ console.log('trace', input, nameAtom);
1271
+ return input;
1272
+ },
1273
+ /**
1274
+ * Returns the current date and time, including timezone offset.
1275
+ *
1276
+ * See: https://hl7.org/fhirpath/#now-datetime
1277
+ */
1278
+ now: () => {
1279
+ return [{ type: PropertyType.dateTime, value: new Date().toISOString() }];
1280
+ },
1281
+ /**
1282
+ * Returns the current time.
1283
+ *
1284
+ * See: https://hl7.org/fhirpath/#timeofday-time
1285
+ */
1286
+ timeOfDay: () => {
1287
+ return [{ type: PropertyType.time, value: new Date().toISOString().substring(11) }];
1288
+ },
1289
+ /**
1290
+ * Returns the current date.
1291
+ *
1292
+ * See: https://hl7.org/fhirpath/#today-date
1293
+ */
1294
+ today: () => {
1295
+ return [{ type: PropertyType.date, value: new Date().toISOString().substring(0, 10) }];
1296
+ },
1297
+ /**
1298
+ * Calculates the difference between two dates or date/times.
1299
+ *
1300
+ * This is not part of the official FHIRPath spec.
1301
+ *
1302
+ * IBM FHIR issue: https://github.com/IBM/FHIR/issues/1014
1303
+ * IBM FHIR PR: https://github.com/IBM/FHIR/pull/1023
1304
+ */
1305
+ between: (input, startAtom, endAtom, unitsAtom) => {
1306
+ var _a;
1307
+ const startDate = functions.toDateTime(startAtom.eval(input));
1308
+ if (startDate.length === 0) {
1309
+ throw new Error('Invalid start date');
1310
+ }
1311
+ const endDate = functions.toDateTime(endAtom.eval(input));
1312
+ if (endDate.length === 0) {
1313
+ throw new Error('Invalid end date');
1314
+ }
1315
+ const unit = (_a = unitsAtom.eval(input)[0]) === null || _a === void 0 ? void 0 : _a.value;
1316
+ if (unit !== 'years' && unit !== 'months' && unit !== 'days') {
1317
+ throw new Error('Invalid units');
1318
+ }
1319
+ const age = calculateAge(startDate[0].value, endDate[0].value);
1320
+ return [{ type: PropertyType.Quantity, value: { value: age[unit], unit } }];
1321
+ },
1322
+ /*
1323
+ * 6.3 Types
1324
+ */
1325
+ /**
1326
+ * The is() function is supported for backwards compatibility with previous
1327
+ * implementations of FHIRPath. Just as with the is keyword, the type argument
1328
+ * is an identifier that must resolve to the name of a type in a model.
1329
+ *
1330
+ * For implementations with compile-time typing, this requires special-case
1331
+ * handling when processing the argument to treat it as a type specifier rather
1332
+ * than an identifier expression:
1333
+ *
1334
+ * @param input
1335
+ * @param typeAtom
1336
+ * @returns
1337
+ */
1338
+ is: (input, typeAtom) => {
1339
+ let typeName = '';
1340
+ if (typeAtom instanceof SymbolAtom) {
1341
+ typeName = typeAtom.name;
1342
+ }
1343
+ else if (typeAtom instanceof DotAtom) {
1344
+ typeName = typeAtom.left.name + '.' + typeAtom.right.name;
1345
+ }
1346
+ if (!typeName) {
1347
+ return [];
1348
+ }
1349
+ return input.map((value) => ({ type: PropertyType.boolean, value: fhirPathIs(value, typeName) }));
1350
+ },
1351
+ /*
1352
+ * 6.5 Boolean logic
1353
+ */
1354
+ /**
1355
+ * 6.5.3. not() : Boolean
1356
+ *
1357
+ * Returns true if the input collection evaluates to false, and false if it evaluates to true. Otherwise, the result is empty ({ }):
1358
+ *
1359
+ * @param input
1360
+ * @returns
1361
+ */
1362
+ not: (input) => {
1363
+ return functions.toBoolean(input).map((value) => ({ type: PropertyType.boolean, value: !value.value }));
1364
+ },
1365
+ /*
1366
+ * Additional functions
1367
+ * See: https://hl7.org/fhir/fhirpath.html#functions
1368
+ */
1369
+ /**
1370
+ * For each item in the collection, if it is a string that is a uri (or canonical or url), locate the target of the reference, and add it to the resulting collection. If the item does not resolve to a resource, the item is ignored and nothing is added to the output collection.
1371
+ * The items in the collection may also represent a Reference, in which case the Reference.reference is resolved.
1372
+ * @param input The input collection.
1373
+ * @returns
1374
+ */
1375
+ resolve: (input) => {
1376
+ return input
1377
+ .map((e) => {
1378
+ const value = e.value;
1379
+ let refStr;
1380
+ if (typeof value === 'string') {
1381
+ refStr = value;
1382
+ }
1383
+ else if (typeof value === 'object') {
1384
+ const ref = value;
1385
+ if (ref.resource) {
1386
+ return toTypedValue(ref.resource);
1387
+ }
1388
+ refStr = ref.reference;
1389
+ }
1390
+ if (!refStr) {
1391
+ return { type: PropertyType.BackboneElement, value: null };
1392
+ }
1393
+ const [resourceType, id] = refStr.split('/');
1394
+ return { type: PropertyType.BackboneElement, value: { resourceType, id } };
1395
+ })
1396
+ .filter((e) => !!e.value);
1397
+ },
1398
+ /**
1399
+ * The as operator can be used to treat a value as a specific type.
1400
+ * @param input The input value.
1401
+ * @returns The value as the specific type.
1402
+ */
1403
+ as: (input) => {
1404
+ return input;
1405
+ },
1406
+ /*
1407
+ * 12. Formal Specifications
1408
+ */
1409
+ /**
1410
+ * Returns the type of the input.
1411
+ *
1412
+ * 12.2. Model Information
1413
+ *
1414
+ * The model information returned by the reflection function type() is specified as an
1415
+ * XML Schema document (xsd) and included in this specification at the following link:
1416
+ * https://hl7.org/fhirpath/modelinfo.xsd
1417
+ *
1418
+ * See: https://hl7.org/fhirpath/#model-information
1419
+ *
1420
+ * @param input The input collection.
1421
+ * @returns
1422
+ */
1423
+ type: (input) => {
1424
+ return input.map(({ value }) => {
1425
+ if (typeof value === 'boolean') {
1426
+ return { type: PropertyType.BackboneElement, value: { namespace: 'System', name: 'Boolean' } };
1427
+ }
1428
+ if (typeof value === 'number') {
1429
+ return { type: PropertyType.BackboneElement, value: { namespace: 'System', name: 'Integer' } };
1430
+ }
1431
+ if (value && typeof value === 'object' && 'resourceType' in value) {
1432
+ return {
1433
+ type: PropertyType.BackboneElement,
1434
+ value: { namespace: 'FHIR', name: value.resourceType },
1435
+ };
1436
+ }
1437
+ return { type: PropertyType.BackboneElement, value: null };
1438
+ });
1439
+ },
1440
+ conformsTo: (input, systemAtom) => {
1441
+ const system = systemAtom.eval(input)[0].value;
1442
+ if (!system.startsWith('http://hl7.org/fhir/StructureDefinition/')) {
1443
+ throw new Error('Expected a StructureDefinition URL');
1444
+ }
1445
+ const expectedResourceType = system.replace('http://hl7.org/fhir/StructureDefinition/', '');
1446
+ return input.map((value) => {
1447
+ var _a;
1448
+ return ({
1449
+ type: PropertyType.boolean,
1450
+ value: ((_a = value.value) === null || _a === void 0 ? void 0 : _a.resourceType) === expectedResourceType,
1451
+ });
1452
+ });
1453
+ },
1454
+ };
1455
+ /*
1456
+ * Helper utilities
1457
+ */
1458
+ function applyStringFunc(func, input, ...argsAtoms) {
1459
+ if (input.length === 0) {
1460
+ return [];
1461
+ }
1462
+ const [{ value }] = validateInput(input, 1);
1463
+ if (typeof value !== 'string') {
1464
+ throw new Error('String function cannot be called with non-string');
1465
+ }
1466
+ const result = func(value, ...argsAtoms.map((atom) => { var _a, _b; return atom && ((_b = (_a = atom.eval(input)) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.value); }));
1467
+ if (result === undefined) {
1468
+ return [];
1469
+ }
1470
+ if (Array.isArray(result)) {
1471
+ return result.map(toTypedValue);
1472
+ }
1473
+ return [toTypedValue(result)];
1474
+ }
1475
+ function applyMathFunc(func, input, ...argsAtoms) {
1476
+ if (input.length === 0) {
1477
+ return [];
1478
+ }
1479
+ const [{ value }] = validateInput(input, 1);
1480
+ const quantity = isQuantity(value);
1481
+ const numberInput = quantity ? value.value : value;
1482
+ if (typeof numberInput !== 'number') {
1483
+ throw new Error('Math function cannot be called with non-number');
1484
+ }
1485
+ const result = func(numberInput, ...argsAtoms.map((atom) => { var _a, _b; return (_b = (_a = atom.eval(input)) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.value; }));
1486
+ const type = quantity ? PropertyType.Quantity : input[0].type;
1487
+ const returnValue = quantity ? Object.assign(Object.assign({}, value), { value: result }) : result;
1488
+ return [{ type, value: returnValue }];
1489
+ }
1490
+ function validateInput(input, count) {
1491
+ if (input.length !== count) {
1492
+ throw new Error(`Expected ${count} arguments`);
1493
+ }
1494
+ for (const element of input) {
1495
+ if (element === null || element === undefined) {
1496
+ throw new Error('Expected non-null argument');
1497
+ }
1498
+ }
1499
+ return input;
1500
+ }
1501
+
1502
+ export { functions };
1503
+ //# sourceMappingURL=functions.js.map