@medplum/core 2.0.13 → 2.0.15
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 +1259 -1065
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.min.cjs +1 -1
- package/dist/esm/base64.mjs +33 -0
- package/dist/esm/base64.mjs.map +1 -0
- package/dist/esm/bundle.mjs +36 -0
- package/dist/esm/bundle.mjs.map +1 -0
- package/dist/esm/cache.mjs +17 -23
- package/dist/esm/cache.mjs.map +1 -1
- package/dist/esm/client.mjs +523 -385
- package/dist/esm/client.mjs.map +1 -1
- package/dist/esm/eventtarget.mjs +6 -11
- package/dist/esm/eventtarget.mjs.map +1 -1
- package/dist/esm/fhirlexer/parse.mjs +15 -23
- package/dist/esm/fhirlexer/parse.mjs.map +1 -1
- package/dist/esm/fhirlexer/tokenize.mjs +190 -180
- package/dist/esm/fhirlexer/tokenize.mjs.map +1 -1
- package/dist/esm/fhirmapper/parse.mjs +264 -252
- package/dist/esm/fhirmapper/parse.mjs.map +1 -1
- package/dist/esm/fhirmapper/tokenize.mjs +2 -4
- package/dist/esm/fhirmapper/tokenize.mjs.map +1 -1
- package/dist/esm/fhirpath/atoms.mjs +13 -20
- package/dist/esm/fhirpath/atoms.mjs.map +1 -1
- package/dist/esm/fhirpath/parse.mjs +0 -1
- package/dist/esm/fhirpath/parse.mjs.map +1 -1
- package/dist/esm/fhirpath/tokenize.mjs +0 -1
- package/dist/esm/fhirpath/tokenize.mjs.map +1 -1
- package/dist/esm/filter/parse.mjs +1 -4
- package/dist/esm/filter/parse.mjs.map +1 -1
- package/dist/esm/filter/tokenize.mjs +2 -4
- package/dist/esm/filter/tokenize.mjs.map +1 -1
- package/dist/esm/index.min.mjs +1 -1
- package/dist/esm/index.mjs +1 -0
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/jwt.mjs +2 -9
- package/dist/esm/jwt.mjs.map +1 -1
- package/dist/esm/readablepromise.mjs +18 -23
- package/dist/esm/readablepromise.mjs.map +1 -1
- package/dist/esm/schema.mjs +149 -127
- package/dist/esm/schema.mjs.map +1 -1
- package/dist/esm/search/match.mjs +1 -4
- package/dist/esm/search/match.mjs.map +1 -1
- package/dist/esm/storage.mjs +13 -19
- package/dist/esm/storage.mjs.map +1 -1
- package/dist/types/base64.d.ts +14 -0
- package/dist/types/bundle.d.ts +11 -0
- package/dist/types/cache.d.ts +3 -1
- package/dist/types/client.d.ts +164 -1
- package/dist/types/eventtarget.d.ts +1 -1
- package/dist/types/fhirlexer/parse.d.ts +5 -2
- package/dist/types/fhirlexer/tokenize.d.ts +28 -1
- package/dist/types/fhirpath/atoms.d.ts +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/readablepromise.d.ts +4 -1
- package/dist/types/schema.d.ts +33 -1
- package/dist/types/storage.d.ts +2 -2
- package/package.json +1 -1
- package/dist/esm/node_modules/tslib/tslib.es6.mjs +0 -30
- package/dist/esm/node_modules/tslib/tslib.es6.mjs.map +0 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -4,52 +4,54 @@
|
|
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.medplum = global.medplum || {}, global.medplum.core = {})));
|
|
5
5
|
})(this, (function (exports) { 'use strict';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return
|
|
7
|
+
/**
|
|
8
|
+
* More on Bundles can be found here
|
|
9
|
+
* http://hl7.org/fhir/R4/bundle.html
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Takes a bundle and creates a Transaction Type bundle
|
|
13
|
+
* @param bundle The Bundle object that we'll receive from the search query
|
|
14
|
+
* @returns transaction type bundle
|
|
15
|
+
*/
|
|
16
|
+
function convertToTransactionBundle(bundle) {
|
|
17
|
+
for (const entry of bundle.entry || []) {
|
|
18
|
+
delete entry?.resource?.meta;
|
|
19
|
+
entry.fullUrl = 'urn:uuid:' + entry?.resource?.id;
|
|
20
|
+
delete entry?.resource?.id;
|
|
21
|
+
}
|
|
22
|
+
const input = bundle.entry;
|
|
23
|
+
const jsonString = JSON.stringify({
|
|
24
|
+
resourceType: 'Bundle',
|
|
25
|
+
type: 'transaction',
|
|
26
|
+
entry: input?.map((entry) => ({
|
|
27
|
+
fullUrl: entry.fullUrl,
|
|
28
|
+
request: { method: 'POST', url: entry.resource.resourceType },
|
|
29
|
+
resource: entry.resource,
|
|
30
|
+
})),
|
|
31
|
+
}, replacer, 2);
|
|
32
|
+
return JSON.parse(jsonString);
|
|
33
|
+
}
|
|
34
|
+
function replacer(key, value) {
|
|
35
|
+
if (key === 'reference' && typeof value === 'string' && value.includes('/')) {
|
|
36
|
+
return 'urn:uuid:' + value.split('/')[1];
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
33
39
|
}
|
|
34
40
|
|
|
35
|
-
var _LRUCache_instances, _LRUCache_max, _LRUCache_cache, _LRUCache_first;
|
|
36
41
|
/**
|
|
37
42
|
* LRU cache (least recently used)
|
|
38
43
|
* Source: https://stackoverflow.com/a/46432113
|
|
39
44
|
*/
|
|
40
45
|
class LRUCache {
|
|
41
46
|
constructor(max = 10) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
_LRUCache_cache.set(this, void 0);
|
|
45
|
-
__classPrivateFieldSet(this, _LRUCache_max, max, "f");
|
|
46
|
-
__classPrivateFieldSet(this, _LRUCache_cache, new Map(), "f");
|
|
47
|
+
this.max = max;
|
|
48
|
+
this.cache = new Map();
|
|
47
49
|
}
|
|
48
50
|
/**
|
|
49
51
|
* Deletes all values from the cache.
|
|
50
52
|
*/
|
|
51
53
|
clear() {
|
|
52
|
-
|
|
54
|
+
this.cache.clear();
|
|
53
55
|
}
|
|
54
56
|
/**
|
|
55
57
|
* Returns the value for the given key.
|
|
@@ -57,10 +59,10 @@
|
|
|
57
59
|
* @returns The value if found; undefined otherwise.
|
|
58
60
|
*/
|
|
59
61
|
get(key) {
|
|
60
|
-
const item =
|
|
62
|
+
const item = this.cache.get(key);
|
|
61
63
|
if (item) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
this.cache.delete(key);
|
|
65
|
+
this.cache.set(key, item);
|
|
64
66
|
}
|
|
65
67
|
return item;
|
|
66
68
|
}
|
|
@@ -70,33 +72,33 @@
|
|
|
70
72
|
* @param val The value to set.
|
|
71
73
|
*/
|
|
72
74
|
set(key, val) {
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
+
if (this.cache.has(key)) {
|
|
76
|
+
this.cache.delete(key);
|
|
75
77
|
}
|
|
76
|
-
else if (
|
|
77
|
-
|
|
78
|
+
else if (this.cache.size >= this.max) {
|
|
79
|
+
this.cache.delete(this.first());
|
|
78
80
|
}
|
|
79
|
-
|
|
81
|
+
this.cache.set(key, val);
|
|
80
82
|
}
|
|
81
83
|
/**
|
|
82
84
|
* Deletes the value for the given key.
|
|
83
85
|
* @param key The key to delete.
|
|
84
86
|
*/
|
|
85
87
|
delete(key) {
|
|
86
|
-
|
|
88
|
+
this.cache.delete(key);
|
|
87
89
|
}
|
|
88
90
|
/**
|
|
89
91
|
* Returns the list of all keys in the cache.
|
|
90
92
|
* @returns The array of keys in the cache.
|
|
91
93
|
*/
|
|
92
94
|
keys() {
|
|
93
|
-
return
|
|
95
|
+
return this.cache.keys();
|
|
96
|
+
}
|
|
97
|
+
first() {
|
|
98
|
+
// This works because the Map class maintains ordered keys.
|
|
99
|
+
return this.cache.keys().next().value;
|
|
94
100
|
}
|
|
95
101
|
}
|
|
96
|
-
_LRUCache_max = new WeakMap(), _LRUCache_cache = new WeakMap(), _LRUCache_instances = new WeakSet(), _LRUCache_first = function _LRUCache_first() {
|
|
97
|
-
// This works because the Map class maintains ordered keys.
|
|
98
|
-
return __classPrivateFieldGet(this, _LRUCache_cache, "f").keys().next().value;
|
|
99
|
-
};
|
|
100
102
|
|
|
101
103
|
/**
|
|
102
104
|
* Formats a FHIR Address as a string.
|
|
@@ -1107,20 +1109,18 @@
|
|
|
1107
1109
|
/*
|
|
1108
1110
|
* Based on: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget
|
|
1109
1111
|
*/
|
|
1110
|
-
var _EventTarget_listeners;
|
|
1111
1112
|
class EventTarget {
|
|
1112
1113
|
constructor() {
|
|
1113
|
-
|
|
1114
|
-
__classPrivateFieldSet(this, _EventTarget_listeners, {}, "f");
|
|
1114
|
+
this.listeners = {};
|
|
1115
1115
|
}
|
|
1116
1116
|
addEventListener(type, callback) {
|
|
1117
|
-
if (!
|
|
1118
|
-
|
|
1117
|
+
if (!this.listeners[type]) {
|
|
1118
|
+
this.listeners[type] = [];
|
|
1119
1119
|
}
|
|
1120
|
-
|
|
1120
|
+
this.listeners[type].push(callback);
|
|
1121
1121
|
}
|
|
1122
1122
|
removeEventListeneer(type, callback) {
|
|
1123
|
-
const array =
|
|
1123
|
+
const array = this.listeners[type];
|
|
1124
1124
|
if (!array) {
|
|
1125
1125
|
return;
|
|
1126
1126
|
}
|
|
@@ -1132,14 +1132,44 @@
|
|
|
1132
1132
|
}
|
|
1133
1133
|
}
|
|
1134
1134
|
dispatchEvent(event) {
|
|
1135
|
-
const array =
|
|
1135
|
+
const array = this.listeners[event.type];
|
|
1136
1136
|
if (array) {
|
|
1137
1137
|
array.forEach((listener) => listener.call(this, event));
|
|
1138
1138
|
}
|
|
1139
1139
|
return !event.defaultPrevented;
|
|
1140
1140
|
}
|
|
1141
1141
|
}
|
|
1142
|
-
|
|
1142
|
+
|
|
1143
|
+
/**
|
|
1144
|
+
* Decodes a base64 string.
|
|
1145
|
+
* Handles both browser and Node environments.
|
|
1146
|
+
* @param data The base-64 encoded input string.
|
|
1147
|
+
* @returns The decoded string.
|
|
1148
|
+
*/
|
|
1149
|
+
function decodeBase64(data) {
|
|
1150
|
+
if (typeof window !== 'undefined') {
|
|
1151
|
+
return window.atob(data);
|
|
1152
|
+
}
|
|
1153
|
+
if (typeof Buffer !== 'undefined') {
|
|
1154
|
+
return Buffer.from(data, 'base64').toString('binary');
|
|
1155
|
+
}
|
|
1156
|
+
throw new Error('Unable to decode base64');
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* Encodes a base64 string.
|
|
1160
|
+
* Handles both browser and Node environments.
|
|
1161
|
+
* @param data The unencoded input string.
|
|
1162
|
+
* @returns The base-64 encoded string.
|
|
1163
|
+
*/
|
|
1164
|
+
function encodeBase64(data) {
|
|
1165
|
+
if (typeof window !== 'undefined') {
|
|
1166
|
+
return window.btoa(data);
|
|
1167
|
+
}
|
|
1168
|
+
if (typeof Buffer !== 'undefined') {
|
|
1169
|
+
return Buffer.from(data, 'binary').toString('base64');
|
|
1170
|
+
}
|
|
1171
|
+
throw new Error('Unable to encode base64');
|
|
1172
|
+
}
|
|
1143
1173
|
|
|
1144
1174
|
/**
|
|
1145
1175
|
* Decodes a section of a JWT.
|
|
@@ -1156,15 +1186,6 @@
|
|
|
1156
1186
|
const jsonPayload = decodeURIComponent(uriEncodedPayload);
|
|
1157
1187
|
return JSON.parse(jsonPayload);
|
|
1158
1188
|
}
|
|
1159
|
-
function decodeBase64(data) {
|
|
1160
|
-
if (typeof window !== 'undefined') {
|
|
1161
|
-
return window.atob(data);
|
|
1162
|
-
}
|
|
1163
|
-
if (typeof Buffer !== 'undefined') {
|
|
1164
|
-
return Buffer.from(data, 'base64').toString('binary');
|
|
1165
|
-
}
|
|
1166
|
-
throw new Error('Unable to decode base64');
|
|
1167
|
-
}
|
|
1168
1189
|
/**
|
|
1169
1190
|
* Parses the JWT payload.
|
|
1170
1191
|
* @param token JWT token
|
|
@@ -1426,7 +1447,7 @@
|
|
|
1426
1447
|
return strs.length > 0 ? strs.join('; ') : 'Unknown error';
|
|
1427
1448
|
}
|
|
1428
1449
|
|
|
1429
|
-
var
|
|
1450
|
+
var _a;
|
|
1430
1451
|
/**
|
|
1431
1452
|
* The ReadablePromise class wraps a request promise suitable for React Suspense.
|
|
1432
1453
|
* See: https://blog.logrocket.com/react-suspense-data-fetching/#wrappromise-js
|
|
@@ -1435,33 +1456,30 @@
|
|
|
1435
1456
|
class ReadablePromise {
|
|
1436
1457
|
constructor(requestPromise) {
|
|
1437
1458
|
this[_a] = 'ReadablePromise';
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
__classPrivateFieldSet(this, _ReadablePromise_suspender, requestPromise.then((res) => {
|
|
1443
|
-
__classPrivateFieldSet(this, _ReadablePromise_status, 'success', "f");
|
|
1444
|
-
__classPrivateFieldSet(this, _ReadablePromise_response, res, "f");
|
|
1459
|
+
this.status = 'pending';
|
|
1460
|
+
this.suspender = requestPromise.then((res) => {
|
|
1461
|
+
this.status = 'success';
|
|
1462
|
+
this.response = res;
|
|
1445
1463
|
return res;
|
|
1446
1464
|
}, (err) => {
|
|
1447
|
-
|
|
1448
|
-
|
|
1465
|
+
this.status = 'error';
|
|
1466
|
+
this.error = err;
|
|
1449
1467
|
throw err;
|
|
1450
|
-
})
|
|
1468
|
+
});
|
|
1451
1469
|
}
|
|
1452
1470
|
/**
|
|
1453
1471
|
* Returns true if the promise is pending.
|
|
1454
1472
|
* @returns True if the Promise is pending.
|
|
1455
1473
|
*/
|
|
1456
1474
|
isPending() {
|
|
1457
|
-
return
|
|
1475
|
+
return this.status === 'pending';
|
|
1458
1476
|
}
|
|
1459
1477
|
/**
|
|
1460
1478
|
* Returns true if the promise resolved successfully.
|
|
1461
1479
|
* @returns True if the Promise resolved successfully.
|
|
1462
1480
|
*/
|
|
1463
1481
|
isOk() {
|
|
1464
|
-
return
|
|
1482
|
+
return this.status === 'success';
|
|
1465
1483
|
}
|
|
1466
1484
|
/**
|
|
1467
1485
|
* Attempts to read the value of the promise.
|
|
@@ -1471,13 +1489,13 @@
|
|
|
1471
1489
|
* @returns The resolved value of the Promise.
|
|
1472
1490
|
*/
|
|
1473
1491
|
read() {
|
|
1474
|
-
switch (
|
|
1492
|
+
switch (this.status) {
|
|
1475
1493
|
case 'pending':
|
|
1476
|
-
throw
|
|
1494
|
+
throw this.suspender;
|
|
1477
1495
|
case 'error':
|
|
1478
|
-
throw
|
|
1496
|
+
throw this.error;
|
|
1479
1497
|
default:
|
|
1480
|
-
return
|
|
1498
|
+
return this.response;
|
|
1481
1499
|
}
|
|
1482
1500
|
}
|
|
1483
1501
|
/**
|
|
@@ -1487,7 +1505,7 @@
|
|
|
1487
1505
|
* @returns A Promise for the completion of which ever callback is executed.
|
|
1488
1506
|
*/
|
|
1489
1507
|
then(onfulfilled, onrejected) {
|
|
1490
|
-
return
|
|
1508
|
+
return this.suspender.then(onfulfilled, onrejected);
|
|
1491
1509
|
}
|
|
1492
1510
|
/**
|
|
1493
1511
|
* Attaches a callback for only the rejection of the Promise.
|
|
@@ -1495,7 +1513,7 @@
|
|
|
1495
1513
|
* @returns A Promise for the completion of the callback.
|
|
1496
1514
|
*/
|
|
1497
1515
|
catch(onrejected) {
|
|
1498
|
-
return
|
|
1516
|
+
return this.suspender.catch(onrejected);
|
|
1499
1517
|
}
|
|
1500
1518
|
/**
|
|
1501
1519
|
* Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The
|
|
@@ -1504,12 +1522,11 @@
|
|
|
1504
1522
|
* @returns A Promise for the completion of the callback.
|
|
1505
1523
|
*/
|
|
1506
1524
|
finally(onfinally) {
|
|
1507
|
-
return
|
|
1525
|
+
return this.suspender.finally(onfinally);
|
|
1508
1526
|
}
|
|
1509
1527
|
}
|
|
1510
|
-
|
|
1528
|
+
_a = Symbol.toStringTag;
|
|
1511
1529
|
|
|
1512
|
-
var _ClientStorage_storage, _MemoryStorage_data;
|
|
1513
1530
|
/**
|
|
1514
1531
|
* The ClientStorage class is a utility class for storing strings and objects.
|
|
1515
1532
|
*
|
|
@@ -1519,21 +1536,20 @@
|
|
|
1519
1536
|
*/
|
|
1520
1537
|
class ClientStorage {
|
|
1521
1538
|
constructor() {
|
|
1522
|
-
|
|
1523
|
-
__classPrivateFieldSet(this, _ClientStorage_storage, typeof localStorage !== 'undefined' ? localStorage : new MemoryStorage(), "f");
|
|
1539
|
+
this.storage = typeof localStorage !== 'undefined' ? localStorage : new MemoryStorage();
|
|
1524
1540
|
}
|
|
1525
1541
|
clear() {
|
|
1526
|
-
|
|
1542
|
+
this.storage.clear();
|
|
1527
1543
|
}
|
|
1528
1544
|
getString(key) {
|
|
1529
|
-
return
|
|
1545
|
+
return this.storage.getItem(key) || undefined;
|
|
1530
1546
|
}
|
|
1531
1547
|
setString(key, value) {
|
|
1532
1548
|
if (value) {
|
|
1533
|
-
|
|
1549
|
+
this.storage.setItem(key, value);
|
|
1534
1550
|
}
|
|
1535
1551
|
else {
|
|
1536
|
-
|
|
1552
|
+
this.storage.removeItem(key);
|
|
1537
1553
|
}
|
|
1538
1554
|
}
|
|
1539
1555
|
getObject(key) {
|
|
@@ -1544,58 +1560,55 @@
|
|
|
1544
1560
|
this.setString(key, value ? stringify(value) : undefined);
|
|
1545
1561
|
}
|
|
1546
1562
|
}
|
|
1547
|
-
_ClientStorage_storage = new WeakMap();
|
|
1548
1563
|
/**
|
|
1549
1564
|
* The MemoryStorage class is a minimal in-memory implementation of the Storage interface.
|
|
1550
1565
|
*/
|
|
1551
1566
|
class MemoryStorage {
|
|
1552
1567
|
constructor() {
|
|
1553
|
-
|
|
1554
|
-
__classPrivateFieldSet(this, _MemoryStorage_data, new Map(), "f");
|
|
1568
|
+
this.data = new Map();
|
|
1555
1569
|
}
|
|
1556
1570
|
/**
|
|
1557
1571
|
* Returns the number of key/value pairs.
|
|
1558
1572
|
*/
|
|
1559
1573
|
get length() {
|
|
1560
|
-
return
|
|
1574
|
+
return this.data.size;
|
|
1561
1575
|
}
|
|
1562
1576
|
/**
|
|
1563
1577
|
* Removes all key/value pairs, if there are any.
|
|
1564
1578
|
*/
|
|
1565
1579
|
clear() {
|
|
1566
|
-
|
|
1580
|
+
this.data.clear();
|
|
1567
1581
|
}
|
|
1568
1582
|
/**
|
|
1569
1583
|
* Returns the current value associated with the given key, or null if the given key does not exist.
|
|
1570
1584
|
*/
|
|
1571
1585
|
getItem(key) {
|
|
1572
|
-
return
|
|
1586
|
+
return this.data.get(key) ?? null;
|
|
1573
1587
|
}
|
|
1574
1588
|
/**
|
|
1575
1589
|
* Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
|
|
1576
1590
|
*/
|
|
1577
1591
|
setItem(key, value) {
|
|
1578
1592
|
if (value) {
|
|
1579
|
-
|
|
1593
|
+
this.data.set(key, value);
|
|
1580
1594
|
}
|
|
1581
1595
|
else {
|
|
1582
|
-
|
|
1596
|
+
this.data.delete(key);
|
|
1583
1597
|
}
|
|
1584
1598
|
}
|
|
1585
1599
|
/**
|
|
1586
1600
|
* Removes the key/value pair with the given key, if a key/value pair with the given key exists.
|
|
1587
1601
|
*/
|
|
1588
1602
|
removeItem(key) {
|
|
1589
|
-
|
|
1603
|
+
this.data.delete(key);
|
|
1590
1604
|
}
|
|
1591
1605
|
/**
|
|
1592
1606
|
* Returns the name of the nth key, or null if n is greater than or equal to the number of key/value pairs.
|
|
1593
1607
|
*/
|
|
1594
1608
|
key(index) {
|
|
1595
|
-
return Array.from(
|
|
1609
|
+
return Array.from(this.data.keys())[index];
|
|
1596
1610
|
}
|
|
1597
1611
|
}
|
|
1598
|
-
_MemoryStorage_data = new WeakMap();
|
|
1599
1612
|
|
|
1600
1613
|
var types = {
|
|
1601
1614
|
Element: {
|
|
@@ -6368,9 +6381,7 @@
|
|
|
6368
6381
|
const globalSchema = baseSchema;
|
|
6369
6382
|
|
|
6370
6383
|
// PKCE auth based on:
|
|
6371
|
-
|
|
6372
|
-
var _MedplumClient_instances, _MedplumClient_fetch, _MedplumClient_createPdf, _MedplumClient_storage, _MedplumClient_requestCache, _MedplumClient_cacheTime, _MedplumClient_baseUrl, _MedplumClient_fhirBaseUrl, _MedplumClient_authorizeUrl, _MedplumClient_tokenUrl, _MedplumClient_logoutUrl, _MedplumClient_onUnauthenticated, _MedplumClient_autoBatchTime, _MedplumClient_autoBatchQueue, _MedplumClient_clientId, _MedplumClient_clientSecret, _MedplumClient_autoBatchTimerId, _MedplumClient_accessToken, _MedplumClient_refreshToken, _MedplumClient_refreshPromise, _MedplumClient_profilePromise, _MedplumClient_profile, _MedplumClient_config, _MedplumClient_addLogin, _MedplumClient_refreshProfile, _MedplumClient_getCacheEntry, _MedplumClient_setCacheEntry, _MedplumClient_cacheResource, _MedplumClient_deleteCacheEntry, _MedplumClient_request, _MedplumClient_fetchWithRetry, _MedplumClient_executeAutoBatch, _MedplumClient_addFetchOptionsDefaults, _MedplumClient_setRequestContentType, _MedplumClient_setRequestBody, _MedplumClient_handleUnauthenticated, _MedplumClient_requestAuthorization, _MedplumClient_refresh, _MedplumClient_fetchTokens, _MedplumClient_verifyTokens, _MedplumClient_setupStorageListener;
|
|
6373
|
-
const MEDPLUM_VERSION = "2.0.13-ee64d72c";
|
|
6384
|
+
const MEDPLUM_VERSION = "2.0.15-025c3c04";
|
|
6374
6385
|
const DEFAULT_BASE_URL = 'https://api.medplum.com/';
|
|
6375
6386
|
const DEFAULT_RESOURCE_CACHE_SIZE = 1000;
|
|
6376
6387
|
const DEFAULT_CACHE_TIME = 60000; // 60 seconds
|
|
@@ -6433,55 +6444,44 @@
|
|
|
6433
6444
|
class MedplumClient extends EventTarget {
|
|
6434
6445
|
constructor(options) {
|
|
6435
6446
|
super();
|
|
6436
|
-
_MedplumClient_instances.add(this);
|
|
6437
|
-
_MedplumClient_fetch.set(this, void 0);
|
|
6438
|
-
_MedplumClient_createPdf.set(this, void 0);
|
|
6439
|
-
_MedplumClient_storage.set(this, void 0);
|
|
6440
|
-
_MedplumClient_requestCache.set(this, void 0);
|
|
6441
|
-
_MedplumClient_cacheTime.set(this, void 0);
|
|
6442
|
-
_MedplumClient_baseUrl.set(this, void 0);
|
|
6443
|
-
_MedplumClient_fhirBaseUrl.set(this, void 0);
|
|
6444
|
-
_MedplumClient_authorizeUrl.set(this, void 0);
|
|
6445
|
-
_MedplumClient_tokenUrl.set(this, void 0);
|
|
6446
|
-
_MedplumClient_logoutUrl.set(this, void 0);
|
|
6447
|
-
_MedplumClient_onUnauthenticated.set(this, void 0);
|
|
6448
|
-
_MedplumClient_autoBatchTime.set(this, void 0);
|
|
6449
|
-
_MedplumClient_autoBatchQueue.set(this, void 0);
|
|
6450
|
-
_MedplumClient_clientId.set(this, void 0);
|
|
6451
|
-
_MedplumClient_clientSecret.set(this, void 0);
|
|
6452
|
-
_MedplumClient_autoBatchTimerId.set(this, void 0);
|
|
6453
|
-
_MedplumClient_accessToken.set(this, void 0);
|
|
6454
|
-
_MedplumClient_refreshToken.set(this, void 0);
|
|
6455
|
-
_MedplumClient_refreshPromise.set(this, void 0);
|
|
6456
|
-
_MedplumClient_profilePromise.set(this, void 0);
|
|
6457
|
-
_MedplumClient_profile.set(this, void 0);
|
|
6458
|
-
_MedplumClient_config.set(this, void 0);
|
|
6459
6447
|
if (options?.baseUrl) {
|
|
6460
6448
|
if (!options.baseUrl.startsWith('http')) {
|
|
6461
6449
|
throw new Error('Base URL must start with http or https');
|
|
6462
6450
|
}
|
|
6463
6451
|
}
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6452
|
+
this.fetch = options?.fetch || getDefaultFetch();
|
|
6453
|
+
this.storage = options?.storage || new ClientStorage();
|
|
6454
|
+
this.createPdfImpl = options?.createPdf;
|
|
6455
|
+
this.baseUrl = ensureTrailingSlash(options?.baseUrl) || DEFAULT_BASE_URL;
|
|
6456
|
+
this.fhirBaseUrl = this.baseUrl + (ensureTrailingSlash(options?.fhirUrlPath) || 'fhir/R4/');
|
|
6457
|
+
this.clientId = options?.clientId || '';
|
|
6458
|
+
this.authorizeUrl = options?.authorizeUrl || this.baseUrl + 'oauth2/authorize';
|
|
6459
|
+
this.tokenUrl = options?.tokenUrl || this.baseUrl + 'oauth2/token';
|
|
6460
|
+
this.logoutUrl = options?.logoutUrl || this.baseUrl + 'oauth2/logout';
|
|
6461
|
+
this.exchangeUrl = this.baseUrl + 'auth/exchange';
|
|
6462
|
+
this.onUnauthenticated = options?.onUnauthenticated;
|
|
6463
|
+
this.cacheTime = options?.cacheTime ?? DEFAULT_CACHE_TIME;
|
|
6464
|
+
if (this.cacheTime > 0) {
|
|
6465
|
+
this.requestCache = new LRUCache(options?.resourceCacheSize ?? DEFAULT_RESOURCE_CACHE_SIZE);
|
|
6466
|
+
}
|
|
6467
|
+
else {
|
|
6468
|
+
this.requestCache = undefined;
|
|
6469
|
+
}
|
|
6470
|
+
if (options?.autoBatchTime) {
|
|
6471
|
+
this.autoBatchTime = options?.autoBatchTime ?? 0;
|
|
6472
|
+
this.autoBatchQueue = [];
|
|
6473
|
+
}
|
|
6474
|
+
else {
|
|
6475
|
+
this.autoBatchTime = 0;
|
|
6476
|
+
this.autoBatchQueue = undefined;
|
|
6477
|
+
}
|
|
6478
6478
|
const activeLogin = this.getActiveLogin();
|
|
6479
6479
|
if (activeLogin) {
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6480
|
+
this.accessToken = activeLogin.accessToken;
|
|
6481
|
+
this.refreshToken = activeLogin.refreshToken;
|
|
6482
|
+
this.refreshProfile().catch(console.log);
|
|
6483
6483
|
}
|
|
6484
|
-
|
|
6484
|
+
this.setupStorageListener();
|
|
6485
6485
|
}
|
|
6486
6486
|
/**
|
|
6487
6487
|
* Returns the current base URL for all API requests.
|
|
@@ -6491,14 +6491,14 @@
|
|
|
6491
6491
|
* @returns The current base URL for all API requests.
|
|
6492
6492
|
*/
|
|
6493
6493
|
getBaseUrl() {
|
|
6494
|
-
return
|
|
6494
|
+
return this.baseUrl;
|
|
6495
6495
|
}
|
|
6496
6496
|
/**
|
|
6497
6497
|
* Clears all auth state including local storage and session storage.
|
|
6498
6498
|
* @category Authentication
|
|
6499
6499
|
*/
|
|
6500
6500
|
clear() {
|
|
6501
|
-
|
|
6501
|
+
this.storage.clear();
|
|
6502
6502
|
this.clearActiveLogin();
|
|
6503
6503
|
}
|
|
6504
6504
|
/**
|
|
@@ -6507,12 +6507,12 @@
|
|
|
6507
6507
|
* @category Authentication
|
|
6508
6508
|
*/
|
|
6509
6509
|
clearActiveLogin() {
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6510
|
+
this.storage.setString('activeLogin', undefined);
|
|
6511
|
+
this.requestCache?.clear();
|
|
6512
|
+
this.accessToken = undefined;
|
|
6513
|
+
this.refreshToken = undefined;
|
|
6514
|
+
this.profile = undefined;
|
|
6515
|
+
this.config = undefined;
|
|
6516
6516
|
this.dispatchEvent({ type: 'change' });
|
|
6517
6517
|
}
|
|
6518
6518
|
/**
|
|
@@ -6522,7 +6522,7 @@
|
|
|
6522
6522
|
*/
|
|
6523
6523
|
invalidateUrl(url) {
|
|
6524
6524
|
url = url.toString();
|
|
6525
|
-
|
|
6525
|
+
this.requestCache?.delete(url);
|
|
6526
6526
|
}
|
|
6527
6527
|
/**
|
|
6528
6528
|
* Invalidates all cached search results or cached requests for the given resourceType.
|
|
@@ -6531,9 +6531,11 @@
|
|
|
6531
6531
|
*/
|
|
6532
6532
|
invalidateSearches(resourceType) {
|
|
6533
6533
|
const url = 'fhir/R4/' + resourceType;
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6534
|
+
if (this.requestCache) {
|
|
6535
|
+
for (const key of this.requestCache.keys()) {
|
|
6536
|
+
if (key.endsWith(url) || key.includes(url + '?')) {
|
|
6537
|
+
this.requestCache.delete(key);
|
|
6538
|
+
}
|
|
6537
6539
|
}
|
|
6538
6540
|
}
|
|
6539
6541
|
}
|
|
@@ -6551,30 +6553,30 @@
|
|
|
6551
6553
|
*/
|
|
6552
6554
|
get(url, options = {}) {
|
|
6553
6555
|
url = url.toString();
|
|
6554
|
-
const cached =
|
|
6556
|
+
const cached = this.getCacheEntry(url, options);
|
|
6555
6557
|
if (cached) {
|
|
6556
6558
|
return cached.value;
|
|
6557
6559
|
}
|
|
6558
6560
|
let promise;
|
|
6559
|
-
if (url.startsWith(
|
|
6561
|
+
if (url.startsWith(this.fhirBaseUrl) && this.autoBatchQueue) {
|
|
6560
6562
|
promise = new Promise((resolve, reject) => {
|
|
6561
|
-
|
|
6563
|
+
this.autoBatchQueue.push({
|
|
6562
6564
|
method: 'GET',
|
|
6563
|
-
url: url.replace(
|
|
6565
|
+
url: url.replace(this.fhirBaseUrl, ''),
|
|
6564
6566
|
options,
|
|
6565
6567
|
resolve,
|
|
6566
6568
|
reject,
|
|
6567
6569
|
});
|
|
6568
|
-
if (!
|
|
6569
|
-
|
|
6570
|
+
if (!this.autoBatchTimerId) {
|
|
6571
|
+
this.autoBatchTimerId = setTimeout(() => this.executeAutoBatch(), this.autoBatchTime);
|
|
6570
6572
|
}
|
|
6571
6573
|
});
|
|
6572
6574
|
}
|
|
6573
6575
|
else {
|
|
6574
|
-
promise =
|
|
6576
|
+
promise = this.request('GET', url, options);
|
|
6575
6577
|
}
|
|
6576
6578
|
const readablePromise = new ReadablePromise(promise);
|
|
6577
|
-
|
|
6579
|
+
this.setCacheEntry(url, readablePromise);
|
|
6578
6580
|
return readablePromise;
|
|
6579
6581
|
}
|
|
6580
6582
|
/**
|
|
@@ -6594,13 +6596,13 @@
|
|
|
6594
6596
|
post(url, body, contentType, options = {}) {
|
|
6595
6597
|
url = url.toString();
|
|
6596
6598
|
if (body) {
|
|
6597
|
-
|
|
6599
|
+
this.setRequestBody(options, body);
|
|
6598
6600
|
}
|
|
6599
6601
|
if (contentType) {
|
|
6600
|
-
|
|
6602
|
+
this.setRequestContentType(options, contentType);
|
|
6601
6603
|
}
|
|
6602
6604
|
this.invalidateUrl(url);
|
|
6603
|
-
return
|
|
6605
|
+
return this.request('POST', url, options);
|
|
6604
6606
|
}
|
|
6605
6607
|
/**
|
|
6606
6608
|
* Makes an HTTP PUT request to the specified URL.
|
|
@@ -6619,13 +6621,13 @@
|
|
|
6619
6621
|
put(url, body, contentType, options = {}) {
|
|
6620
6622
|
url = url.toString();
|
|
6621
6623
|
if (body) {
|
|
6622
|
-
|
|
6624
|
+
this.setRequestBody(options, body);
|
|
6623
6625
|
}
|
|
6624
6626
|
if (contentType) {
|
|
6625
|
-
|
|
6627
|
+
this.setRequestContentType(options, contentType);
|
|
6626
6628
|
}
|
|
6627
6629
|
this.invalidateUrl(url);
|
|
6628
|
-
return
|
|
6630
|
+
return this.request('PUT', url, options);
|
|
6629
6631
|
}
|
|
6630
6632
|
/**
|
|
6631
6633
|
* Makes an HTTP PATCH request to the specified URL.
|
|
@@ -6642,10 +6644,10 @@
|
|
|
6642
6644
|
*/
|
|
6643
6645
|
patch(url, operations, options = {}) {
|
|
6644
6646
|
url = url.toString();
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
+
this.setRequestBody(options, operations);
|
|
6648
|
+
this.setRequestContentType(options, PATCH_CONTENT_TYPE);
|
|
6647
6649
|
this.invalidateUrl(url);
|
|
6648
|
-
return
|
|
6650
|
+
return this.request('PATCH', url, options);
|
|
6649
6651
|
}
|
|
6650
6652
|
/**
|
|
6651
6653
|
* Makes an HTTP DELETE request to the specified URL.
|
|
@@ -6663,7 +6665,7 @@
|
|
|
6663
6665
|
delete(url, options = {}) {
|
|
6664
6666
|
url = url.toString();
|
|
6665
6667
|
this.invalidateUrl(url);
|
|
6666
|
-
return
|
|
6668
|
+
return this.request('DELETE', url, options);
|
|
6667
6669
|
}
|
|
6668
6670
|
/**
|
|
6669
6671
|
* Initiates a new user flow.
|
|
@@ -6715,7 +6717,7 @@
|
|
|
6715
6717
|
async startLogin(loginRequest) {
|
|
6716
6718
|
return this.post('auth/login', {
|
|
6717
6719
|
...(await this.ensureCodeChallenge(loginRequest)),
|
|
6718
|
-
clientId: loginRequest.clientId ??
|
|
6720
|
+
clientId: loginRequest.clientId ?? this.clientId,
|
|
6719
6721
|
scope: loginRequest.scope,
|
|
6720
6722
|
});
|
|
6721
6723
|
}
|
|
@@ -6730,7 +6732,7 @@
|
|
|
6730
6732
|
async startGoogleLogin(loginRequest) {
|
|
6731
6733
|
return this.post('auth/google', {
|
|
6732
6734
|
...(await this.ensureCodeChallenge(loginRequest)),
|
|
6733
|
-
clientId: loginRequest.clientId ??
|
|
6735
|
+
clientId: loginRequest.clientId ?? this.clientId,
|
|
6734
6736
|
scope: loginRequest.scope,
|
|
6735
6737
|
});
|
|
6736
6738
|
}
|
|
@@ -6754,7 +6756,7 @@
|
|
|
6754
6756
|
* @category Authentication
|
|
6755
6757
|
*/
|
|
6756
6758
|
async signOut() {
|
|
6757
|
-
await this.post(
|
|
6759
|
+
await this.post(this.logoutUrl, {});
|
|
6758
6760
|
this.clear();
|
|
6759
6761
|
}
|
|
6760
6762
|
/**
|
|
@@ -6768,7 +6770,7 @@
|
|
|
6768
6770
|
const urlParams = new URLSearchParams(window.location.search);
|
|
6769
6771
|
const code = urlParams.get('code');
|
|
6770
6772
|
if (!code) {
|
|
6771
|
-
await
|
|
6773
|
+
await this.requestAuthorization(loginParams);
|
|
6772
6774
|
return undefined;
|
|
6773
6775
|
}
|
|
6774
6776
|
else {
|
|
@@ -6781,7 +6783,7 @@
|
|
|
6781
6783
|
* @category Authentication
|
|
6782
6784
|
*/
|
|
6783
6785
|
signOutWithRedirect() {
|
|
6784
|
-
window.location.assign(
|
|
6786
|
+
window.location.assign(this.logoutUrl);
|
|
6785
6787
|
}
|
|
6786
6788
|
/**
|
|
6787
6789
|
* Initiates sign in with an external identity provider.
|
|
@@ -6795,6 +6797,34 @@
|
|
|
6795
6797
|
const loginRequest = await this.ensureCodeChallenge(baseLogin);
|
|
6796
6798
|
window.location.assign(this.getExternalAuthRedirectUri(authorizeUrl, clientId, redirectUri, loginRequest));
|
|
6797
6799
|
}
|
|
6800
|
+
/**
|
|
6801
|
+
* Exchange an external access token for a Medplum access token.
|
|
6802
|
+
* @param token The access token that was generated by the external identity provider.
|
|
6803
|
+
* @param clientId The ID of the `ClientApplication` in your Medplum project that will be making the exchange request.
|
|
6804
|
+
* @category Authentication
|
|
6805
|
+
*/
|
|
6806
|
+
async exchangeExternalAccessToken(token, clientId) {
|
|
6807
|
+
clientId = clientId || this.clientId;
|
|
6808
|
+
if (!clientId) {
|
|
6809
|
+
throw new Error('MedplumClient is missing clientId');
|
|
6810
|
+
}
|
|
6811
|
+
const response = await this.fetch(this.exchangeUrl, {
|
|
6812
|
+
method: 'POST',
|
|
6813
|
+
headers: { 'Content-Type': 'application/json' },
|
|
6814
|
+
body: JSON.stringify({
|
|
6815
|
+
clientId: this.clientId,
|
|
6816
|
+
externalAccessToken: token,
|
|
6817
|
+
}),
|
|
6818
|
+
credentials: 'include',
|
|
6819
|
+
});
|
|
6820
|
+
if (!response.ok) {
|
|
6821
|
+
this.clearActiveLogin();
|
|
6822
|
+
throw new Error('Failed to fetch tokens');
|
|
6823
|
+
}
|
|
6824
|
+
const tokens = await response.json();
|
|
6825
|
+
await this.verifyTokens(tokens);
|
|
6826
|
+
return this.getProfile();
|
|
6827
|
+
}
|
|
6798
6828
|
/**
|
|
6799
6829
|
* Builds the external identity provider redirect URI.
|
|
6800
6830
|
* @param authorizeUrl The external authorization URL.
|
|
@@ -6821,7 +6851,7 @@
|
|
|
6821
6851
|
* @returns The well-formed FHIR URL.
|
|
6822
6852
|
*/
|
|
6823
6853
|
fhirUrl(...path) {
|
|
6824
|
-
return new URL(
|
|
6854
|
+
return new URL(this.fhirBaseUrl + path.join('/'));
|
|
6825
6855
|
}
|
|
6826
6856
|
/**
|
|
6827
6857
|
* Builds a FHIR search URL from a search query or structured query object.
|
|
@@ -6889,7 +6919,7 @@
|
|
|
6889
6919
|
search(resourceType, query, options = {}) {
|
|
6890
6920
|
const url = this.fhirSearchUrl(resourceType, query);
|
|
6891
6921
|
const cacheKey = url.toString() + '-search';
|
|
6892
|
-
const cached =
|
|
6922
|
+
const cached = this.getCacheEntry(cacheKey, options);
|
|
6893
6923
|
if (cached) {
|
|
6894
6924
|
return cached.value;
|
|
6895
6925
|
}
|
|
@@ -6897,12 +6927,12 @@
|
|
|
6897
6927
|
const bundle = await this.get(url, options);
|
|
6898
6928
|
if (bundle.entry) {
|
|
6899
6929
|
for (const entry of bundle.entry) {
|
|
6900
|
-
|
|
6930
|
+
this.cacheResource(entry.resource);
|
|
6901
6931
|
}
|
|
6902
6932
|
}
|
|
6903
6933
|
return bundle;
|
|
6904
6934
|
})());
|
|
6905
|
-
|
|
6935
|
+
this.setCacheEntry(cacheKey, promise);
|
|
6906
6936
|
return promise;
|
|
6907
6937
|
}
|
|
6908
6938
|
/**
|
|
@@ -6932,12 +6962,12 @@
|
|
|
6932
6962
|
url.searchParams.set('_count', '1');
|
|
6933
6963
|
url.searchParams.sort();
|
|
6934
6964
|
const cacheKey = url.toString() + '-searchOne';
|
|
6935
|
-
const cached =
|
|
6965
|
+
const cached = this.getCacheEntry(cacheKey, options);
|
|
6936
6966
|
if (cached) {
|
|
6937
6967
|
return cached.value;
|
|
6938
6968
|
}
|
|
6939
6969
|
const promise = new ReadablePromise(this.search(resourceType, url.searchParams, options).then((b) => b.entry?.[0]?.resource));
|
|
6940
|
-
|
|
6970
|
+
this.setCacheEntry(cacheKey, promise);
|
|
6941
6971
|
return promise;
|
|
6942
6972
|
}
|
|
6943
6973
|
/**
|
|
@@ -6965,14 +6995,48 @@
|
|
|
6965
6995
|
searchResources(resourceType, query, options = {}) {
|
|
6966
6996
|
const url = this.fhirSearchUrl(resourceType, query);
|
|
6967
6997
|
const cacheKey = url.toString() + '-searchResources';
|
|
6968
|
-
const cached =
|
|
6998
|
+
const cached = this.getCacheEntry(cacheKey, options);
|
|
6969
6999
|
if (cached) {
|
|
6970
7000
|
return cached.value;
|
|
6971
7001
|
}
|
|
6972
7002
|
const promise = new ReadablePromise(this.search(resourceType, query, options).then((b) => b.entry?.map((e) => e.resource) ?? []));
|
|
6973
|
-
|
|
7003
|
+
this.setCacheEntry(cacheKey, promise);
|
|
6974
7004
|
return promise;
|
|
6975
7005
|
}
|
|
7006
|
+
/**
|
|
7007
|
+
* Creates an
|
|
7008
|
+
* [async generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator)
|
|
7009
|
+
* over a series of FHIR search requests for paginated search results. Each iteration of the generator yields
|
|
7010
|
+
* the array of resources on each page.
|
|
7011
|
+
*
|
|
7012
|
+
*
|
|
7013
|
+
* ```typescript
|
|
7014
|
+
* for await (const page of medplum.searchResourcePages('Patient', { _count: 10 })) {
|
|
7015
|
+
* for (const patient of page) {
|
|
7016
|
+
* console.log(`Processing Patient resource with ID: ${patient.id}`);
|
|
7017
|
+
* }
|
|
7018
|
+
* }
|
|
7019
|
+
* ```
|
|
7020
|
+
*
|
|
7021
|
+
* @category Search
|
|
7022
|
+
* @param resourceType The FHIR resource type.
|
|
7023
|
+
* @param query Optional FHIR search query or structured query object. Can be any valid input to the URLSearchParams() constructor.
|
|
7024
|
+
* @param options Optional fetch options.
|
|
7025
|
+
* @returns An async generator, where each result is an array of resources for each page.
|
|
7026
|
+
*/
|
|
7027
|
+
async *searchResourcePages(resourceType, query, options = {}) {
|
|
7028
|
+
let url = this.fhirSearchUrl(resourceType, query);
|
|
7029
|
+
while (url) {
|
|
7030
|
+
const searchParams = new URL(url).searchParams;
|
|
7031
|
+
const bundle = await this.search(resourceType, searchParams, options);
|
|
7032
|
+
const nextLink = bundle?.link?.find((link) => link.relation === 'next');
|
|
7033
|
+
if (!bundle?.entry?.length && !nextLink) {
|
|
7034
|
+
break;
|
|
7035
|
+
}
|
|
7036
|
+
yield bundle?.entry?.map((e) => e.resource) ?? [];
|
|
7037
|
+
url = nextLink?.url ? new URL(nextLink?.url) : undefined;
|
|
7038
|
+
}
|
|
7039
|
+
}
|
|
6976
7040
|
/**
|
|
6977
7041
|
* Searches a ValueSet resource using the "expand" operation.
|
|
6978
7042
|
* See: https://www.hl7.org/fhir/operation-valueset-expand.html
|
|
@@ -6997,7 +7061,7 @@
|
|
|
6997
7061
|
* @returns The resource if it is available in the cache; undefined otherwise.
|
|
6998
7062
|
*/
|
|
6999
7063
|
getCached(resourceType, id) {
|
|
7000
|
-
const cached =
|
|
7064
|
+
const cached = this.requestCache?.get(this.fhirUrl(resourceType, id).toString())?.value;
|
|
7001
7065
|
return cached && cached.isOk() ? cached.read() : undefined;
|
|
7002
7066
|
}
|
|
7003
7067
|
/**
|
|
@@ -7099,7 +7163,7 @@
|
|
|
7099
7163
|
return Promise.resolve(globalSchema);
|
|
7100
7164
|
}
|
|
7101
7165
|
const cacheKey = resourceType + '-requestSchema';
|
|
7102
|
-
const cached =
|
|
7166
|
+
const cached = this.getCacheEntry(cacheKey, undefined);
|
|
7103
7167
|
if (cached) {
|
|
7104
7168
|
return cached.value;
|
|
7105
7169
|
}
|
|
@@ -7142,7 +7206,7 @@
|
|
|
7142
7206
|
}
|
|
7143
7207
|
return globalSchema;
|
|
7144
7208
|
})());
|
|
7145
|
-
|
|
7209
|
+
this.setCacheEntry(cacheKey, promise);
|
|
7146
7210
|
return promise;
|
|
7147
7211
|
}
|
|
7148
7212
|
/**
|
|
@@ -7340,7 +7404,7 @@
|
|
|
7340
7404
|
};
|
|
7341
7405
|
xhr.open('POST', url);
|
|
7342
7406
|
xhr.withCredentials = true;
|
|
7343
|
-
xhr.setRequestHeader('Authorization', 'Bearer ' +
|
|
7407
|
+
xhr.setRequestHeader('Authorization', 'Bearer ' + this.accessToken);
|
|
7344
7408
|
xhr.setRequestHeader('Cache-Control', 'no-cache, no-store, max-age=0');
|
|
7345
7409
|
xhr.setRequestHeader('Content-Type', contentType);
|
|
7346
7410
|
xhr.setRequestHeader('X-Medplum', 'extended');
|
|
@@ -7370,10 +7434,10 @@
|
|
|
7370
7434
|
* @returns The result of the create operation.
|
|
7371
7435
|
*/
|
|
7372
7436
|
async createPdf(docDefinition, filename, tableLayouts, fonts) {
|
|
7373
|
-
if (!
|
|
7437
|
+
if (!this.createPdfImpl) {
|
|
7374
7438
|
throw new Error('PDF creation not enabled');
|
|
7375
7439
|
}
|
|
7376
|
-
const blob = await
|
|
7440
|
+
const blob = await this.createPdfImpl(docDefinition, tableLayouts, fonts);
|
|
7377
7441
|
return this.createBinary(blob, filename, 'application/pdf');
|
|
7378
7442
|
}
|
|
7379
7443
|
/**
|
|
@@ -7451,7 +7515,7 @@
|
|
|
7451
7515
|
// return result ?? resource;
|
|
7452
7516
|
result = resource;
|
|
7453
7517
|
}
|
|
7454
|
-
|
|
7518
|
+
this.cacheResource(result);
|
|
7455
7519
|
return result;
|
|
7456
7520
|
}
|
|
7457
7521
|
/**
|
|
@@ -7499,7 +7563,7 @@
|
|
|
7499
7563
|
* @returns The result of the delete operation.
|
|
7500
7564
|
*/
|
|
7501
7565
|
deleteResource(resourceType, id) {
|
|
7502
|
-
|
|
7566
|
+
this.deleteCacheEntry(this.fhirUrl(resourceType, id).toString());
|
|
7503
7567
|
this.invalidateSearches(resourceType);
|
|
7504
7568
|
return this.delete(this.fhirUrl(resourceType, id));
|
|
7505
7569
|
}
|
|
@@ -7703,61 +7767,80 @@
|
|
|
7703
7767
|
* @returns The Login State
|
|
7704
7768
|
*/
|
|
7705
7769
|
getActiveLogin() {
|
|
7706
|
-
return
|
|
7770
|
+
return this.storage.getObject('activeLogin');
|
|
7707
7771
|
}
|
|
7708
7772
|
/**
|
|
7709
7773
|
* @category Authentication
|
|
7710
7774
|
*/
|
|
7711
7775
|
async setActiveLogin(login) {
|
|
7712
7776
|
this.clearActiveLogin();
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
await
|
|
7777
|
+
this.accessToken = login.accessToken;
|
|
7778
|
+
this.refreshToken = login.refreshToken;
|
|
7779
|
+
this.storage.setObject('activeLogin', login);
|
|
7780
|
+
this.addLogin(login);
|
|
7781
|
+
this.refreshPromise = undefined;
|
|
7782
|
+
await this.refreshProfile();
|
|
7719
7783
|
}
|
|
7720
7784
|
/**
|
|
7721
7785
|
* Returns the current access token.
|
|
7722
7786
|
* @category Authentication
|
|
7723
7787
|
*/
|
|
7724
7788
|
getAccessToken() {
|
|
7725
|
-
return
|
|
7789
|
+
return this.accessToken;
|
|
7726
7790
|
}
|
|
7727
7791
|
/**
|
|
7728
7792
|
* Sets the current access token.
|
|
7729
7793
|
* @category Authentication
|
|
7730
7794
|
*/
|
|
7731
7795
|
setAccessToken(accessToken) {
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
|
|
7796
|
+
this.accessToken = accessToken;
|
|
7797
|
+
this.refreshToken = undefined;
|
|
7798
|
+
this.profile = undefined;
|
|
7799
|
+
this.config = undefined;
|
|
7736
7800
|
}
|
|
7737
7801
|
/**
|
|
7738
7802
|
* @category Authentication
|
|
7739
7803
|
*/
|
|
7740
7804
|
getLogins() {
|
|
7741
|
-
return
|
|
7805
|
+
return this.storage.getObject('logins') ?? [];
|
|
7806
|
+
}
|
|
7807
|
+
addLogin(newLogin) {
|
|
7808
|
+
const logins = this.getLogins().filter((login) => login.profile?.reference !== newLogin.profile?.reference);
|
|
7809
|
+
logins.push(newLogin);
|
|
7810
|
+
this.storage.setObject('logins', logins);
|
|
7811
|
+
}
|
|
7812
|
+
async refreshProfile() {
|
|
7813
|
+
this.profilePromise = new Promise((resolve, reject) => {
|
|
7814
|
+
this.get('auth/me')
|
|
7815
|
+
.then((result) => {
|
|
7816
|
+
this.profilePromise = undefined;
|
|
7817
|
+
this.profile = result.profile;
|
|
7818
|
+
this.config = result.config;
|
|
7819
|
+
this.dispatchEvent({ type: 'change' });
|
|
7820
|
+
resolve(this.profile);
|
|
7821
|
+
})
|
|
7822
|
+
.catch(reject);
|
|
7823
|
+
});
|
|
7824
|
+
return this.profilePromise;
|
|
7742
7825
|
}
|
|
7743
7826
|
/**
|
|
7744
7827
|
* @category Authentication
|
|
7745
7828
|
*/
|
|
7746
7829
|
isLoading() {
|
|
7747
|
-
return !!
|
|
7830
|
+
return !!this.profilePromise;
|
|
7748
7831
|
}
|
|
7749
7832
|
/**
|
|
7750
7833
|
* @category User Profile
|
|
7751
7834
|
*/
|
|
7752
7835
|
getProfile() {
|
|
7753
|
-
return
|
|
7836
|
+
return this.profile;
|
|
7754
7837
|
}
|
|
7755
7838
|
/**
|
|
7756
7839
|
* @category User Profile
|
|
7757
7840
|
*/
|
|
7758
7841
|
async getProfileAsync() {
|
|
7759
|
-
if (
|
|
7760
|
-
await
|
|
7842
|
+
if (this.profilePromise) {
|
|
7843
|
+
await this.profilePromise;
|
|
7761
7844
|
}
|
|
7762
7845
|
return this.getProfile();
|
|
7763
7846
|
}
|
|
@@ -7765,7 +7848,7 @@
|
|
|
7765
7848
|
* @category User Profile
|
|
7766
7849
|
*/
|
|
7767
7850
|
getUserConfiguration() {
|
|
7768
|
-
return
|
|
7851
|
+
return this.config;
|
|
7769
7852
|
}
|
|
7770
7853
|
/**
|
|
7771
7854
|
* Downloads the URL as a blob.
|
|
@@ -7775,13 +7858,234 @@
|
|
|
7775
7858
|
* @returns Promise to the response body as a blob.
|
|
7776
7859
|
*/
|
|
7777
7860
|
async download(url, options = {}) {
|
|
7778
|
-
if (
|
|
7779
|
-
await
|
|
7861
|
+
if (this.refreshPromise) {
|
|
7862
|
+
await this.refreshPromise;
|
|
7780
7863
|
}
|
|
7781
|
-
|
|
7782
|
-
const response = await
|
|
7864
|
+
this.addFetchOptionsDefaults(options);
|
|
7865
|
+
const response = await this.fetch(url.toString(), options);
|
|
7783
7866
|
return response.blob();
|
|
7784
7867
|
}
|
|
7868
|
+
//
|
|
7869
|
+
// Private helpers
|
|
7870
|
+
//
|
|
7871
|
+
/**
|
|
7872
|
+
* Returns the cache entry if available and not expired.
|
|
7873
|
+
* @param key The cache key to retrieve.
|
|
7874
|
+
* @param options Optional fetch options for cache settings.
|
|
7875
|
+
* @returns The cached entry if found.
|
|
7876
|
+
*/
|
|
7877
|
+
getCacheEntry(key, options) {
|
|
7878
|
+
if (!this.requestCache || options?.cache === 'no-cache' || options?.cache === 'reload') {
|
|
7879
|
+
return undefined;
|
|
7880
|
+
}
|
|
7881
|
+
const entry = this.requestCache.get(key);
|
|
7882
|
+
if (!entry || entry.requestTime + this.cacheTime < Date.now()) {
|
|
7883
|
+
return undefined;
|
|
7884
|
+
}
|
|
7885
|
+
return entry;
|
|
7886
|
+
}
|
|
7887
|
+
/**
|
|
7888
|
+
* Adds a readable promise to the cache.
|
|
7889
|
+
* @param key The cache key to store.
|
|
7890
|
+
* @param value The readable promise to store.
|
|
7891
|
+
*/
|
|
7892
|
+
setCacheEntry(key, value) {
|
|
7893
|
+
if (this.requestCache) {
|
|
7894
|
+
this.requestCache.set(key, { requestTime: Date.now(), value });
|
|
7895
|
+
}
|
|
7896
|
+
}
|
|
7897
|
+
/**
|
|
7898
|
+
* Adds a concrete value as the cache entry for the given resource.
|
|
7899
|
+
* This is used in cases where the resource is loaded indirectly.
|
|
7900
|
+
* For example, when a resource is loaded as part of a Bundle.
|
|
7901
|
+
* @param resource The resource to cache.
|
|
7902
|
+
*/
|
|
7903
|
+
cacheResource(resource) {
|
|
7904
|
+
if (resource?.id) {
|
|
7905
|
+
this.setCacheEntry(this.fhirUrl(resource.resourceType, resource.id).toString(), new ReadablePromise(Promise.resolve(resource)));
|
|
7906
|
+
}
|
|
7907
|
+
}
|
|
7908
|
+
/**
|
|
7909
|
+
* Deletes a cache entry.
|
|
7910
|
+
* @param key The cache key to delete.
|
|
7911
|
+
*/
|
|
7912
|
+
deleteCacheEntry(key) {
|
|
7913
|
+
if (this.requestCache) {
|
|
7914
|
+
this.requestCache.delete(key);
|
|
7915
|
+
}
|
|
7916
|
+
}
|
|
7917
|
+
/**
|
|
7918
|
+
* Makes an HTTP request.
|
|
7919
|
+
* @param {string} method
|
|
7920
|
+
* @param {string} url
|
|
7921
|
+
* @param {string=} contentType
|
|
7922
|
+
* @param {Object=} body
|
|
7923
|
+
*/
|
|
7924
|
+
async request(method, url, options = {}) {
|
|
7925
|
+
if (this.refreshPromise) {
|
|
7926
|
+
await this.refreshPromise;
|
|
7927
|
+
}
|
|
7928
|
+
if (!url.startsWith('http')) {
|
|
7929
|
+
url = this.baseUrl + url;
|
|
7930
|
+
}
|
|
7931
|
+
options.method = method;
|
|
7932
|
+
this.addFetchOptionsDefaults(options);
|
|
7933
|
+
const response = await this.fetchWithRetry(url, options);
|
|
7934
|
+
if (response.status === 401) {
|
|
7935
|
+
// Refresh and try again
|
|
7936
|
+
return this.handleUnauthenticated(method, url, options);
|
|
7937
|
+
}
|
|
7938
|
+
if (response.status === 204 || response.status === 304) {
|
|
7939
|
+
// No content or change
|
|
7940
|
+
return undefined;
|
|
7941
|
+
}
|
|
7942
|
+
let obj = undefined;
|
|
7943
|
+
try {
|
|
7944
|
+
obj = await response.json();
|
|
7945
|
+
}
|
|
7946
|
+
catch (err) {
|
|
7947
|
+
console.error('Error parsing response', response.status, err);
|
|
7948
|
+
throw err;
|
|
7949
|
+
}
|
|
7950
|
+
if (response.status >= 400) {
|
|
7951
|
+
throw new OperationOutcomeError(normalizeOperationOutcome(obj));
|
|
7952
|
+
}
|
|
7953
|
+
return obj;
|
|
7954
|
+
}
|
|
7955
|
+
async fetchWithRetry(url, options) {
|
|
7956
|
+
const maxRetries = 3;
|
|
7957
|
+
const retryDelay = 200;
|
|
7958
|
+
let response = undefined;
|
|
7959
|
+
for (let retry = 0; retry < maxRetries; retry++) {
|
|
7960
|
+
response = (await this.fetch(url, options));
|
|
7961
|
+
if (response.status < 500) {
|
|
7962
|
+
return response;
|
|
7963
|
+
}
|
|
7964
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
7965
|
+
}
|
|
7966
|
+
return response;
|
|
7967
|
+
}
|
|
7968
|
+
/**
|
|
7969
|
+
* Executes a batch of requests that were automatically batched together.
|
|
7970
|
+
*/
|
|
7971
|
+
async executeAutoBatch() {
|
|
7972
|
+
// Get the current queue
|
|
7973
|
+
const entries = [...this.autoBatchQueue];
|
|
7974
|
+
// Clear the queue
|
|
7975
|
+
this.autoBatchQueue.length = 0;
|
|
7976
|
+
// Clear the timer
|
|
7977
|
+
this.autoBatchTimerId = undefined;
|
|
7978
|
+
// If there is only one request in the batch, just execute it
|
|
7979
|
+
if (entries.length === 1) {
|
|
7980
|
+
const entry = entries[0];
|
|
7981
|
+
try {
|
|
7982
|
+
entry.resolve(await this.request(entry.method, this.fhirBaseUrl + entry.url, entry.options));
|
|
7983
|
+
}
|
|
7984
|
+
catch (err) {
|
|
7985
|
+
entry.reject(new OperationOutcomeError(normalizeOperationOutcome(err)));
|
|
7986
|
+
}
|
|
7987
|
+
return;
|
|
7988
|
+
}
|
|
7989
|
+
// Build the batch request
|
|
7990
|
+
const batch = {
|
|
7991
|
+
resourceType: 'Bundle',
|
|
7992
|
+
type: 'batch',
|
|
7993
|
+
entry: entries.map((e) => ({
|
|
7994
|
+
request: {
|
|
7995
|
+
method: e.method,
|
|
7996
|
+
url: e.url,
|
|
7997
|
+
},
|
|
7998
|
+
resource: e.options.body ? JSON.parse(e.options.body) : undefined,
|
|
7999
|
+
})),
|
|
8000
|
+
};
|
|
8001
|
+
// Execute the batch request
|
|
8002
|
+
const response = (await this.post('fhir/R4', batch));
|
|
8003
|
+
// Process the response
|
|
8004
|
+
for (let i = 0; i < entries.length; i++) {
|
|
8005
|
+
const entry = entries[i];
|
|
8006
|
+
const responseEntry = response.entry?.[i];
|
|
8007
|
+
if (responseEntry?.response?.outcome && !isOk(responseEntry.response.outcome)) {
|
|
8008
|
+
entry.reject(new OperationOutcomeError(responseEntry.response.outcome));
|
|
8009
|
+
}
|
|
8010
|
+
else {
|
|
8011
|
+
entry.resolve(responseEntry?.resource);
|
|
8012
|
+
}
|
|
8013
|
+
}
|
|
8014
|
+
}
|
|
8015
|
+
/**
|
|
8016
|
+
* Adds default options to the fetch options.
|
|
8017
|
+
* @param options The options to add defaults to.
|
|
8018
|
+
*/
|
|
8019
|
+
addFetchOptionsDefaults(options) {
|
|
8020
|
+
let headers = options.headers;
|
|
8021
|
+
if (!headers) {
|
|
8022
|
+
headers = {};
|
|
8023
|
+
options.headers = headers;
|
|
8024
|
+
}
|
|
8025
|
+
headers['X-Medplum'] = 'extended';
|
|
8026
|
+
if (options.body && !headers['Content-Type']) {
|
|
8027
|
+
headers['Content-Type'] = FHIR_CONTENT_TYPE;
|
|
8028
|
+
}
|
|
8029
|
+
if (this.accessToken) {
|
|
8030
|
+
headers['Authorization'] = 'Bearer ' + this.accessToken;
|
|
8031
|
+
}
|
|
8032
|
+
if (this.basicAuth) {
|
|
8033
|
+
headers['Authorization'] = 'Basic ' + this.basicAuth;
|
|
8034
|
+
}
|
|
8035
|
+
if (!options.cache) {
|
|
8036
|
+
options.cache = 'no-cache';
|
|
8037
|
+
}
|
|
8038
|
+
if (!options.credentials) {
|
|
8039
|
+
options.credentials = 'include';
|
|
8040
|
+
}
|
|
8041
|
+
}
|
|
8042
|
+
/**
|
|
8043
|
+
* Sets the "Content-Type" header on fetch options.
|
|
8044
|
+
* @param options The fetch options.
|
|
8045
|
+
* @param contentType The new content type to set.
|
|
8046
|
+
*/
|
|
8047
|
+
setRequestContentType(options, contentType) {
|
|
8048
|
+
if (!options.headers) {
|
|
8049
|
+
options.headers = {};
|
|
8050
|
+
}
|
|
8051
|
+
const headers = options.headers;
|
|
8052
|
+
headers['Content-Type'] = contentType;
|
|
8053
|
+
}
|
|
8054
|
+
/**
|
|
8055
|
+
* Sets the body on fetch options.
|
|
8056
|
+
* @param options The fetch options.
|
|
8057
|
+
* @param data The new content body.
|
|
8058
|
+
*/
|
|
8059
|
+
setRequestBody(options, data) {
|
|
8060
|
+
if (typeof data === 'string' ||
|
|
8061
|
+
(typeof Blob !== 'undefined' && data instanceof Blob) ||
|
|
8062
|
+
(typeof File !== 'undefined' && data instanceof File) ||
|
|
8063
|
+
(typeof Uint8Array !== 'undefined' && data instanceof Uint8Array)) {
|
|
8064
|
+
options.body = data;
|
|
8065
|
+
}
|
|
8066
|
+
else if (data) {
|
|
8067
|
+
options.body = JSON.stringify(data);
|
|
8068
|
+
}
|
|
8069
|
+
}
|
|
8070
|
+
/**
|
|
8071
|
+
* Handles an unauthenticated response from the server.
|
|
8072
|
+
* First, tries to refresh the access token and retry the request.
|
|
8073
|
+
* Otherwise, calls unauthenticated callbacks and rejects.
|
|
8074
|
+
* @param method The HTTP method of the original request.
|
|
8075
|
+
* @param url The URL of the original request.
|
|
8076
|
+
* @param contentType The content type of the original request.
|
|
8077
|
+
* @param body The body of the original request.
|
|
8078
|
+
*/
|
|
8079
|
+
handleUnauthenticated(method, url, options) {
|
|
8080
|
+
if (this.refresh()) {
|
|
8081
|
+
return this.request(method, url, options);
|
|
8082
|
+
}
|
|
8083
|
+
this.clearActiveLogin();
|
|
8084
|
+
if (this.onUnauthenticated) {
|
|
8085
|
+
this.onUnauthenticated();
|
|
8086
|
+
}
|
|
8087
|
+
return Promise.reject(new Error('Unauthenticated'));
|
|
8088
|
+
}
|
|
7785
8089
|
/**
|
|
7786
8090
|
* Starts a new PKCE flow.
|
|
7787
8091
|
* These PKCE values are stateful, and must survive redirects and page refreshes.
|
|
@@ -7797,6 +8101,23 @@
|
|
|
7797
8101
|
sessionStorage.setItem('codeChallenge', codeChallenge);
|
|
7798
8102
|
return { codeChallengeMethod: 'S256', codeChallenge };
|
|
7799
8103
|
}
|
|
8104
|
+
/**
|
|
8105
|
+
* Redirects the user to the login screen for authorization.
|
|
8106
|
+
* Clears all auth state including local storage and session storage.
|
|
8107
|
+
* See: https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
|
|
8108
|
+
*/
|
|
8109
|
+
async requestAuthorization(loginParams) {
|
|
8110
|
+
const loginRequest = await this.ensureCodeChallenge(loginParams || {});
|
|
8111
|
+
const url = new URL(this.authorizeUrl);
|
|
8112
|
+
url.searchParams.set('response_type', 'code');
|
|
8113
|
+
url.searchParams.set('state', sessionStorage.getItem('pkceState'));
|
|
8114
|
+
url.searchParams.set('client_id', loginRequest.clientId || this.clientId);
|
|
8115
|
+
url.searchParams.set('redirect_uri', loginRequest.redirectUri || getWindowOrigin());
|
|
8116
|
+
url.searchParams.set('code_challenge_method', loginRequest.codeChallengeMethod);
|
|
8117
|
+
url.searchParams.set('code_challenge', loginRequest.codeChallenge);
|
|
8118
|
+
url.searchParams.set('scope', loginRequest.scope || 'openid profile');
|
|
8119
|
+
window.location.assign(url.toString());
|
|
8120
|
+
}
|
|
7800
8121
|
/**
|
|
7801
8122
|
* Processes an OAuth authorization code.
|
|
7802
8123
|
* See: https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
|
|
@@ -7808,7 +8129,7 @@
|
|
|
7808
8129
|
const formBody = new URLSearchParams();
|
|
7809
8130
|
formBody.set('grant_type', 'authorization_code');
|
|
7810
8131
|
formBody.set('code', code);
|
|
7811
|
-
formBody.set('client_id', loginParams?.clientId ||
|
|
8132
|
+
formBody.set('client_id', loginParams?.clientId || this.clientId);
|
|
7812
8133
|
formBody.set('redirect_uri', loginParams?.redirectUri || getWindowOrigin());
|
|
7813
8134
|
if (typeof sessionStorage !== 'undefined') {
|
|
7814
8135
|
const codeVerifier = sessionStorage.getItem('codeVerifier');
|
|
@@ -7816,7 +8137,29 @@
|
|
|
7816
8137
|
formBody.set('code_verifier', codeVerifier);
|
|
7817
8138
|
}
|
|
7818
8139
|
}
|
|
7819
|
-
return
|
|
8140
|
+
return this.fetchTokens(formBody);
|
|
8141
|
+
}
|
|
8142
|
+
/**
|
|
8143
|
+
* Tries to refresh the auth tokens.
|
|
8144
|
+
* See: https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokens
|
|
8145
|
+
*/
|
|
8146
|
+
refresh() {
|
|
8147
|
+
if (this.refreshPromise) {
|
|
8148
|
+
return this.refreshPromise;
|
|
8149
|
+
}
|
|
8150
|
+
if (this.refreshToken) {
|
|
8151
|
+
const formBody = new URLSearchParams();
|
|
8152
|
+
formBody.set('grant_type', 'refresh_token');
|
|
8153
|
+
formBody.set('client_id', this.clientId);
|
|
8154
|
+
formBody.set('refresh_token', this.refreshToken);
|
|
8155
|
+
this.refreshPromise = this.fetchTokens(formBody);
|
|
8156
|
+
return this.refreshPromise;
|
|
8157
|
+
}
|
|
8158
|
+
if (this.clientId && this.clientSecret) {
|
|
8159
|
+
this.refreshPromise = this.startClientLogin(this.clientId, this.clientSecret);
|
|
8160
|
+
return this.refreshPromise;
|
|
8161
|
+
}
|
|
8162
|
+
return undefined;
|
|
7820
8163
|
}
|
|
7821
8164
|
/**
|
|
7822
8165
|
* Starts a new OAuth2 client credentials flow.
|
|
@@ -7827,13 +8170,24 @@
|
|
|
7827
8170
|
* @returns Promise that resolves to the client profile.
|
|
7828
8171
|
*/
|
|
7829
8172
|
async startClientLogin(clientId, clientSecret) {
|
|
7830
|
-
|
|
7831
|
-
|
|
8173
|
+
this.clientId = clientId;
|
|
8174
|
+
this.clientSecret = clientSecret;
|
|
7832
8175
|
const formBody = new URLSearchParams();
|
|
7833
8176
|
formBody.set('grant_type', 'client_credentials');
|
|
7834
8177
|
formBody.set('client_id', clientId);
|
|
7835
8178
|
formBody.set('client_secret', clientSecret);
|
|
7836
|
-
return
|
|
8179
|
+
return this.fetchTokens(formBody);
|
|
8180
|
+
}
|
|
8181
|
+
/**
|
|
8182
|
+
* Sets the client ID and secret for basic auth.
|
|
8183
|
+
* @category Authentication
|
|
8184
|
+
* @param clientId The client ID.
|
|
8185
|
+
* @param clientSecret The client secret.
|
|
8186
|
+
*/
|
|
8187
|
+
setBasicAuth(clientId, clientSecret) {
|
|
8188
|
+
this.clientId = clientId;
|
|
8189
|
+
this.clientSecret = clientSecret;
|
|
8190
|
+
this.basicAuth = encodeBase64(clientId + ':' + clientSecret);
|
|
7837
8191
|
}
|
|
7838
8192
|
/**
|
|
7839
8193
|
* Invite a user to a project.
|
|
@@ -7844,275 +8198,72 @@
|
|
|
7844
8198
|
async invite(projectId, body) {
|
|
7845
8199
|
return this.post('admin/projects/' + projectId + '/invite', body);
|
|
7846
8200
|
}
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
.catch(reject);
|
|
7863
|
-
}), "f");
|
|
7864
|
-
return __classPrivateFieldGet(this, _MedplumClient_profilePromise, "f");
|
|
7865
|
-
}, _MedplumClient_getCacheEntry = function _MedplumClient_getCacheEntry(key, options) {
|
|
7866
|
-
if (__classPrivateFieldGet(this, _MedplumClient_cacheTime, "f") <= 0 || options?.cache === 'no-cache' || options?.cache === 'reload') {
|
|
7867
|
-
return undefined;
|
|
7868
|
-
}
|
|
7869
|
-
const entry = __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").get(key);
|
|
7870
|
-
if (!entry || entry.requestTime + __classPrivateFieldGet(this, _MedplumClient_cacheTime, "f") < Date.now()) {
|
|
7871
|
-
return undefined;
|
|
7872
|
-
}
|
|
7873
|
-
return entry;
|
|
7874
|
-
}, _MedplumClient_setCacheEntry = function _MedplumClient_setCacheEntry(key, value) {
|
|
7875
|
-
if (__classPrivateFieldGet(this, _MedplumClient_cacheTime, "f") > 0) {
|
|
7876
|
-
__classPrivateFieldGet(this, _MedplumClient_requestCache, "f").set(key, { requestTime: Date.now(), value });
|
|
7877
|
-
}
|
|
7878
|
-
}, _MedplumClient_cacheResource = function _MedplumClient_cacheResource(resource) {
|
|
7879
|
-
if (resource?.id) {
|
|
7880
|
-
__classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setCacheEntry).call(this, this.fhirUrl(resource.resourceType, resource.id).toString(), new ReadablePromise(Promise.resolve(resource)));
|
|
7881
|
-
}
|
|
7882
|
-
}, _MedplumClient_deleteCacheEntry = function _MedplumClient_deleteCacheEntry(key) {
|
|
7883
|
-
if (__classPrivateFieldGet(this, _MedplumClient_cacheTime, "f") > 0) {
|
|
7884
|
-
__classPrivateFieldGet(this, _MedplumClient_requestCache, "f").delete(key);
|
|
7885
|
-
}
|
|
7886
|
-
}, _MedplumClient_request =
|
|
7887
|
-
/**
|
|
7888
|
-
* Makes an HTTP request.
|
|
7889
|
-
* @param {string} method
|
|
7890
|
-
* @param {string} url
|
|
7891
|
-
* @param {string=} contentType
|
|
7892
|
-
* @param {Object=} body
|
|
7893
|
-
*/
|
|
7894
|
-
async function _MedplumClient_request(method, url, options = {}) {
|
|
7895
|
-
if (__classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f")) {
|
|
7896
|
-
await __classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f");
|
|
7897
|
-
}
|
|
7898
|
-
if (!url.startsWith('http')) {
|
|
7899
|
-
url = __classPrivateFieldGet(this, _MedplumClient_baseUrl, "f") + url;
|
|
7900
|
-
}
|
|
7901
|
-
options.method = method;
|
|
7902
|
-
__classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_addFetchOptionsDefaults).call(this, options);
|
|
7903
|
-
const response = await __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_fetchWithRetry).call(this, url, options);
|
|
7904
|
-
if (response.status === 401) {
|
|
7905
|
-
// Refresh and try again
|
|
7906
|
-
return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_handleUnauthenticated).call(this, method, url, options);
|
|
7907
|
-
}
|
|
7908
|
-
if (response.status === 204 || response.status === 304) {
|
|
7909
|
-
// No content or change
|
|
7910
|
-
return undefined;
|
|
7911
|
-
}
|
|
7912
|
-
let obj = undefined;
|
|
7913
|
-
try {
|
|
7914
|
-
obj = await response.json();
|
|
7915
|
-
}
|
|
7916
|
-
catch (err) {
|
|
7917
|
-
console.error('Error parsing response', response.status, err);
|
|
7918
|
-
throw err;
|
|
7919
|
-
}
|
|
7920
|
-
if (response.status >= 400) {
|
|
7921
|
-
throw new OperationOutcomeError(normalizeOperationOutcome(obj));
|
|
7922
|
-
}
|
|
7923
|
-
return obj;
|
|
7924
|
-
}, _MedplumClient_fetchWithRetry = async function _MedplumClient_fetchWithRetry(url, options) {
|
|
7925
|
-
const maxRetries = 3;
|
|
7926
|
-
const retryDelay = 200;
|
|
7927
|
-
let response = undefined;
|
|
7928
|
-
for (let retry = 0; retry < maxRetries; retry++) {
|
|
7929
|
-
response = (await __classPrivateFieldGet(this, _MedplumClient_fetch, "f").call(this, url, options));
|
|
7930
|
-
if (response.status < 500) {
|
|
7931
|
-
return response;
|
|
7932
|
-
}
|
|
7933
|
-
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
7934
|
-
}
|
|
7935
|
-
return response;
|
|
7936
|
-
}, _MedplumClient_executeAutoBatch =
|
|
7937
|
-
/**
|
|
7938
|
-
* Executes a batch of requests that were automatically batched together.
|
|
7939
|
-
*/
|
|
7940
|
-
async function _MedplumClient_executeAutoBatch() {
|
|
7941
|
-
// Get the current queue
|
|
7942
|
-
const entries = [...__classPrivateFieldGet(this, _MedplumClient_autoBatchQueue, "f")];
|
|
7943
|
-
// Clear the queue
|
|
7944
|
-
__classPrivateFieldGet(this, _MedplumClient_autoBatchQueue, "f").length = 0;
|
|
7945
|
-
// Clear the timer
|
|
7946
|
-
__classPrivateFieldSet(this, _MedplumClient_autoBatchTimerId, undefined, "f");
|
|
7947
|
-
// If there is only one request in the batch, just execute it
|
|
7948
|
-
if (entries.length === 1) {
|
|
7949
|
-
const entry = entries[0];
|
|
7950
|
-
entry.resolve(await __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, entry.method, __classPrivateFieldGet(this, _MedplumClient_fhirBaseUrl, "f") + entry.url, entry.options));
|
|
7951
|
-
return;
|
|
7952
|
-
}
|
|
7953
|
-
// Build the batch request
|
|
7954
|
-
const batch = {
|
|
7955
|
-
resourceType: 'Bundle',
|
|
7956
|
-
type: 'batch',
|
|
7957
|
-
entry: entries.map((e) => ({
|
|
7958
|
-
request: {
|
|
7959
|
-
method: e.method,
|
|
7960
|
-
url: e.url,
|
|
7961
|
-
},
|
|
7962
|
-
resource: e.options.body ? JSON.parse(e.options.body) : undefined,
|
|
7963
|
-
})),
|
|
7964
|
-
};
|
|
7965
|
-
// Execute the batch request
|
|
7966
|
-
const response = (await this.post('fhir/R4', batch));
|
|
7967
|
-
// Process the response
|
|
7968
|
-
for (let i = 0; i < entries.length; i++) {
|
|
7969
|
-
const entry = entries[i];
|
|
7970
|
-
const responseEntry = response.entry?.[i];
|
|
7971
|
-
if (responseEntry?.response?.outcome && !isOk(responseEntry.response.outcome)) {
|
|
7972
|
-
entry.reject(new OperationOutcomeError(responseEntry.response.outcome));
|
|
8201
|
+
/**
|
|
8202
|
+
* Makes a POST request to the tokens endpoint.
|
|
8203
|
+
* See: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
|
8204
|
+
* @param formBody Token parameters in URL encoded format.
|
|
8205
|
+
*/
|
|
8206
|
+
async fetchTokens(formBody) {
|
|
8207
|
+
const response = await this.fetch(this.tokenUrl, {
|
|
8208
|
+
method: 'POST',
|
|
8209
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
8210
|
+
body: formBody,
|
|
8211
|
+
credentials: 'include',
|
|
8212
|
+
});
|
|
8213
|
+
if (!response.ok) {
|
|
8214
|
+
this.clearActiveLogin();
|
|
8215
|
+
throw new Error('Failed to fetch tokens');
|
|
7973
8216
|
}
|
|
7974
|
-
|
|
7975
|
-
|
|
7976
|
-
|
|
7977
|
-
}
|
|
7978
|
-
}, _MedplumClient_addFetchOptionsDefaults = function _MedplumClient_addFetchOptionsDefaults(options) {
|
|
7979
|
-
if (!options.headers) {
|
|
7980
|
-
options.headers = {};
|
|
7981
|
-
}
|
|
7982
|
-
const headers = options.headers;
|
|
7983
|
-
headers['X-Medplum'] = 'extended';
|
|
7984
|
-
if (!headers['Content-Type']) {
|
|
7985
|
-
headers['Content-Type'] = FHIR_CONTENT_TYPE;
|
|
7986
|
-
}
|
|
7987
|
-
if (__classPrivateFieldGet(this, _MedplumClient_accessToken, "f")) {
|
|
7988
|
-
headers['Authorization'] = 'Bearer ' + __classPrivateFieldGet(this, _MedplumClient_accessToken, "f");
|
|
7989
|
-
}
|
|
7990
|
-
if (!options.cache) {
|
|
7991
|
-
options.cache = 'no-cache';
|
|
7992
|
-
}
|
|
7993
|
-
if (!options.credentials) {
|
|
7994
|
-
options.credentials = 'include';
|
|
7995
|
-
}
|
|
7996
|
-
}, _MedplumClient_setRequestContentType = function _MedplumClient_setRequestContentType(options, contentType) {
|
|
7997
|
-
if (!options.headers) {
|
|
7998
|
-
options.headers = {};
|
|
7999
|
-
}
|
|
8000
|
-
const headers = options.headers;
|
|
8001
|
-
headers['Content-Type'] = contentType;
|
|
8002
|
-
}, _MedplumClient_setRequestBody = function _MedplumClient_setRequestBody(options, data) {
|
|
8003
|
-
if (typeof data === 'string' ||
|
|
8004
|
-
(typeof Blob !== 'undefined' && data instanceof Blob) ||
|
|
8005
|
-
(typeof File !== 'undefined' && data instanceof File) ||
|
|
8006
|
-
(typeof Uint8Array !== 'undefined' && data instanceof Uint8Array)) {
|
|
8007
|
-
options.body = data;
|
|
8008
|
-
}
|
|
8009
|
-
else if (data) {
|
|
8010
|
-
options.body = JSON.stringify(data);
|
|
8011
|
-
}
|
|
8012
|
-
}, _MedplumClient_handleUnauthenticated = function _MedplumClient_handleUnauthenticated(method, url, options) {
|
|
8013
|
-
if (__classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_refresh).call(this)) {
|
|
8014
|
-
return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, method, url, options);
|
|
8015
|
-
}
|
|
8016
|
-
this.clearActiveLogin();
|
|
8017
|
-
if (__classPrivateFieldGet(this, _MedplumClient_onUnauthenticated, "f")) {
|
|
8018
|
-
__classPrivateFieldGet(this, _MedplumClient_onUnauthenticated, "f").call(this);
|
|
8019
|
-
}
|
|
8020
|
-
return Promise.reject(new Error('Unauthenticated'));
|
|
8021
|
-
}, _MedplumClient_requestAuthorization =
|
|
8022
|
-
/**
|
|
8023
|
-
* Redirects the user to the login screen for authorization.
|
|
8024
|
-
* Clears all auth state including local storage and session storage.
|
|
8025
|
-
* See: https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
|
|
8026
|
-
*/
|
|
8027
|
-
async function _MedplumClient_requestAuthorization(loginParams) {
|
|
8028
|
-
const loginRequest = await this.ensureCodeChallenge(loginParams || {});
|
|
8029
|
-
const url = new URL(__classPrivateFieldGet(this, _MedplumClient_authorizeUrl, "f"));
|
|
8030
|
-
url.searchParams.set('response_type', 'code');
|
|
8031
|
-
url.searchParams.set('state', sessionStorage.getItem('pkceState'));
|
|
8032
|
-
url.searchParams.set('client_id', loginRequest.clientId || __classPrivateFieldGet(this, _MedplumClient_clientId, "f"));
|
|
8033
|
-
url.searchParams.set('redirect_uri', loginRequest.redirectUri || getWindowOrigin());
|
|
8034
|
-
url.searchParams.set('code_challenge_method', loginRequest.codeChallengeMethod);
|
|
8035
|
-
url.searchParams.set('code_challenge', loginRequest.codeChallenge);
|
|
8036
|
-
url.searchParams.set('scope', loginRequest.scope || 'openid profile');
|
|
8037
|
-
window.location.assign(url.toString());
|
|
8038
|
-
}, _MedplumClient_refresh = function _MedplumClient_refresh() {
|
|
8039
|
-
if (__classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f")) {
|
|
8040
|
-
return __classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f");
|
|
8041
|
-
}
|
|
8042
|
-
if (__classPrivateFieldGet(this, _MedplumClient_refreshToken, "f")) {
|
|
8043
|
-
const formBody = new URLSearchParams();
|
|
8044
|
-
formBody.set('grant_type', 'refresh_token');
|
|
8045
|
-
formBody.set('client_id', __classPrivateFieldGet(this, _MedplumClient_clientId, "f"));
|
|
8046
|
-
formBody.set('refresh_token', __classPrivateFieldGet(this, _MedplumClient_refreshToken, "f"));
|
|
8047
|
-
__classPrivateFieldSet(this, _MedplumClient_refreshPromise, __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_fetchTokens).call(this, formBody), "f");
|
|
8048
|
-
return __classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f");
|
|
8049
|
-
}
|
|
8050
|
-
if (__classPrivateFieldGet(this, _MedplumClient_clientId, "f") && __classPrivateFieldGet(this, _MedplumClient_clientSecret, "f")) {
|
|
8051
|
-
__classPrivateFieldSet(this, _MedplumClient_refreshPromise, this.startClientLogin(__classPrivateFieldGet(this, _MedplumClient_clientId, "f"), __classPrivateFieldGet(this, _MedplumClient_clientSecret, "f")), "f");
|
|
8052
|
-
return __classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f");
|
|
8053
|
-
}
|
|
8054
|
-
return undefined;
|
|
8055
|
-
}, _MedplumClient_fetchTokens =
|
|
8056
|
-
/**
|
|
8057
|
-
* Makes a POST request to the tokens endpoint.
|
|
8058
|
-
* See: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
|
8059
|
-
* @param formBody Token parameters in URL encoded format.
|
|
8060
|
-
*/
|
|
8061
|
-
async function _MedplumClient_fetchTokens(formBody) {
|
|
8062
|
-
const response = await __classPrivateFieldGet(this, _MedplumClient_fetch, "f").call(this, __classPrivateFieldGet(this, _MedplumClient_tokenUrl, "f"), {
|
|
8063
|
-
method: 'POST',
|
|
8064
|
-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
8065
|
-
body: formBody,
|
|
8066
|
-
credentials: 'include',
|
|
8067
|
-
});
|
|
8068
|
-
if (!response.ok) {
|
|
8069
|
-
this.clearActiveLogin();
|
|
8070
|
-
throw new Error('Failed to fetch tokens');
|
|
8071
|
-
}
|
|
8072
|
-
const tokens = await response.json();
|
|
8073
|
-
await __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_verifyTokens).call(this, tokens);
|
|
8074
|
-
return this.getProfile();
|
|
8075
|
-
}, _MedplumClient_verifyTokens =
|
|
8076
|
-
/**
|
|
8077
|
-
* Verifies the tokens received from the auth server.
|
|
8078
|
-
* Validates the JWT against the JWKS.
|
|
8079
|
-
* See: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
|
8080
|
-
* @param tokens
|
|
8081
|
-
*/
|
|
8082
|
-
async function _MedplumClient_verifyTokens(tokens) {
|
|
8083
|
-
const token = tokens.access_token;
|
|
8084
|
-
// Verify token has not expired
|
|
8085
|
-
const tokenPayload = parseJWTPayload(token);
|
|
8086
|
-
if (Date.now() >= tokenPayload.exp * 1000) {
|
|
8087
|
-
this.clearActiveLogin();
|
|
8088
|
-
throw new Error('Token expired');
|
|
8089
|
-
}
|
|
8090
|
-
// Verify app_client_id
|
|
8091
|
-
if (__classPrivateFieldGet(this, _MedplumClient_clientId, "f") && tokenPayload.client_id !== __classPrivateFieldGet(this, _MedplumClient_clientId, "f")) {
|
|
8092
|
-
this.clearActiveLogin();
|
|
8093
|
-
throw new Error('Token was not issued for this audience');
|
|
8217
|
+
const tokens = await response.json();
|
|
8218
|
+
await this.verifyTokens(tokens);
|
|
8219
|
+
return this.getProfile();
|
|
8094
8220
|
}
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8221
|
+
/**
|
|
8222
|
+
* Verifies the tokens received from the auth server.
|
|
8223
|
+
* Validates the JWT against the JWKS.
|
|
8224
|
+
* See: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
|
8225
|
+
* @param tokens
|
|
8226
|
+
*/
|
|
8227
|
+
async verifyTokens(tokens) {
|
|
8228
|
+
const token = tokens.access_token;
|
|
8229
|
+
// Verify token has not expired
|
|
8230
|
+
const tokenPayload = parseJWTPayload(token);
|
|
8231
|
+
if (Date.now() >= tokenPayload.exp * 1000) {
|
|
8232
|
+
this.clearActiveLogin();
|
|
8233
|
+
throw new Error('Token expired');
|
|
8234
|
+
}
|
|
8235
|
+
// Verify app_client_id
|
|
8236
|
+
if (this.clientId && tokenPayload.client_id !== this.clientId) {
|
|
8237
|
+
this.clearActiveLogin();
|
|
8238
|
+
throw new Error('Token was not issued for this audience');
|
|
8239
|
+
}
|
|
8240
|
+
return this.setActiveLogin({
|
|
8241
|
+
accessToken: token,
|
|
8242
|
+
refreshToken: tokens.refresh_token,
|
|
8243
|
+
project: tokens.project,
|
|
8244
|
+
profile: tokens.profile,
|
|
8110
8245
|
});
|
|
8111
8246
|
}
|
|
8112
|
-
|
|
8113
|
-
|
|
8247
|
+
/**
|
|
8248
|
+
* Sets up a listener for window storage events.
|
|
8249
|
+
* This synchronizes state across browser windows and browser tabs.
|
|
8250
|
+
*/
|
|
8251
|
+
setupStorageListener() {
|
|
8252
|
+
try {
|
|
8253
|
+
window.addEventListener('storage', (e) => {
|
|
8254
|
+
if (e.key === null || e.key === 'activeLogin') {
|
|
8255
|
+
// Storage events fire when different tabs make changes.
|
|
8256
|
+
// On storage clear (key === null) or activeLogin change (key === 'activeLogin')
|
|
8257
|
+
// Refresh the page to ensure the active login is up to date.
|
|
8258
|
+
window.location.reload();
|
|
8259
|
+
}
|
|
8260
|
+
});
|
|
8261
|
+
}
|
|
8262
|
+
catch (err) {
|
|
8263
|
+
// Silently ignore if this environment does not support storage events
|
|
8264
|
+
}
|
|
8114
8265
|
}
|
|
8115
|
-
}
|
|
8266
|
+
}
|
|
8116
8267
|
/**
|
|
8117
8268
|
* Returns the default fetch method.
|
|
8118
8269
|
* The default fetch is currently only available in browser environments.
|
|
@@ -8142,7 +8293,6 @@
|
|
|
8142
8293
|
return url.endsWith('/') ? url : url + '/';
|
|
8143
8294
|
}
|
|
8144
8295
|
|
|
8145
|
-
var _ParserBuilder_prefixParselets, _ParserBuilder_infixParselets, _Parser_tokens, _Parser_prefixParselets, _Parser_infixParselets;
|
|
8146
8296
|
class PrefixOperatorAtom {
|
|
8147
8297
|
constructor(operator, child) {
|
|
8148
8298
|
this.operator = operator;
|
|
@@ -8164,15 +8314,15 @@
|
|
|
8164
8314
|
}
|
|
8165
8315
|
class ParserBuilder {
|
|
8166
8316
|
constructor() {
|
|
8167
|
-
|
|
8168
|
-
|
|
8317
|
+
this.prefixParselets = {};
|
|
8318
|
+
this.infixParselets = {};
|
|
8169
8319
|
}
|
|
8170
8320
|
registerInfix(tokenType, parselet) {
|
|
8171
|
-
|
|
8321
|
+
this.infixParselets[tokenType] = parselet;
|
|
8172
8322
|
return this;
|
|
8173
8323
|
}
|
|
8174
8324
|
registerPrefix(tokenType, parselet) {
|
|
8175
|
-
|
|
8325
|
+
this.prefixParselets[tokenType] = parselet;
|
|
8176
8326
|
return this;
|
|
8177
8327
|
}
|
|
8178
8328
|
prefix(tokenType, precedence, builder) {
|
|
@@ -8193,21 +8343,17 @@
|
|
|
8193
8343
|
});
|
|
8194
8344
|
}
|
|
8195
8345
|
construct(input) {
|
|
8196
|
-
return new Parser(input,
|
|
8346
|
+
return new Parser(input, this.prefixParselets, this.infixParselets);
|
|
8197
8347
|
}
|
|
8198
8348
|
}
|
|
8199
|
-
_ParserBuilder_prefixParselets = new WeakMap(), _ParserBuilder_infixParselets = new WeakMap();
|
|
8200
8349
|
class Parser {
|
|
8201
8350
|
constructor(tokens, prefixParselets, infixParselets) {
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
__classPrivateFieldSet(this, _Parser_tokens, tokens, "f");
|
|
8206
|
-
__classPrivateFieldSet(this, _Parser_prefixParselets, prefixParselets, "f");
|
|
8207
|
-
__classPrivateFieldSet(this, _Parser_infixParselets, infixParselets, "f");
|
|
8351
|
+
this.tokens = tokens;
|
|
8352
|
+
this.prefixParselets = prefixParselets;
|
|
8353
|
+
this.infixParselets = infixParselets;
|
|
8208
8354
|
}
|
|
8209
8355
|
hasMore() {
|
|
8210
|
-
return
|
|
8356
|
+
return this.tokens.length > 0;
|
|
8211
8357
|
}
|
|
8212
8358
|
match(expected) {
|
|
8213
8359
|
const token = this.peek();
|
|
@@ -8219,7 +8365,7 @@
|
|
|
8219
8365
|
}
|
|
8220
8366
|
consumeAndParse(precedence = Infinity) {
|
|
8221
8367
|
const token = this.consume();
|
|
8222
|
-
const prefix =
|
|
8368
|
+
const prefix = this.prefixParselets[token.id];
|
|
8223
8369
|
if (!prefix) {
|
|
8224
8370
|
throw Error(`Parse error at "${token.value}" (line ${token.line}, column ${token.column}). No matching prefix parselet.`);
|
|
8225
8371
|
}
|
|
@@ -8243,7 +8389,7 @@
|
|
|
8243
8389
|
return Infinity;
|
|
8244
8390
|
}
|
|
8245
8391
|
consume(expectedId, expectedValue) {
|
|
8246
|
-
if (!
|
|
8392
|
+
if (!this.tokens.length) {
|
|
8247
8393
|
throw Error('Cant consume unknown more tokens.');
|
|
8248
8394
|
}
|
|
8249
8395
|
if (expectedId && this.peek()?.id !== expectedId) {
|
|
@@ -8254,21 +8400,19 @@
|
|
|
8254
8400
|
const actual = this.peek();
|
|
8255
8401
|
throw Error(`Expected "${expectedValue}" but got "${actual.value}" at line ${actual.line} column ${actual.column}.`);
|
|
8256
8402
|
}
|
|
8257
|
-
return
|
|
8403
|
+
return this.tokens.shift();
|
|
8258
8404
|
}
|
|
8259
8405
|
peek() {
|
|
8260
|
-
return
|
|
8406
|
+
return this.tokens.length > 0 ? this.tokens[0] : undefined;
|
|
8261
8407
|
}
|
|
8262
8408
|
removeComments() {
|
|
8263
|
-
|
|
8409
|
+
this.tokens = this.tokens.filter((t) => t.id !== 'Comment');
|
|
8264
8410
|
}
|
|
8265
8411
|
getInfixParselet(token) {
|
|
8266
|
-
return
|
|
8412
|
+
return this.infixParselets[token.id === 'Symbol' ? token.value : token.id];
|
|
8267
8413
|
}
|
|
8268
8414
|
}
|
|
8269
|
-
_Parser_tokens = new WeakMap(), _Parser_prefixParselets = new WeakMap(), _Parser_infixParselets = new WeakMap();
|
|
8270
8415
|
|
|
8271
|
-
var _Tokenizer_instances, _Tokenizer_str, _Tokenizer_keywords, _Tokenizer_operators, _Tokenizer_dateTimeLiterals, _Tokenizer_symbolRegex, _Tokenizer_result, _Tokenizer_pos, _Tokenizer_markStack, _Tokenizer_prevToken, _Tokenizer_peekToken, _Tokenizer_consumeToken, _Tokenizer_consumeWhitespace, _Tokenizer_consumeMultiLineComment, _Tokenizer_consumeSingleLineComment, _Tokenizer_consumeString, _Tokenizer_consumeBacktickSymbol, _Tokenizer_consumeDateTime, _Tokenizer_consumeNumber, _Tokenizer_consumeSymbol, _Tokenizer_consumeOperator, _Tokenizer_consumeWhile, _Tokenizer_curr, _Tokenizer_prev, _Tokenizer_peek, _Tokenizer_mark, _Tokenizer_reset, _Tokenizer_advance, _Tokenizer_buildToken;
|
|
8272
8416
|
const STANDARD_UNITS = [
|
|
8273
8417
|
'year',
|
|
8274
8418
|
'years',
|
|
@@ -8289,190 +8433,203 @@
|
|
|
8289
8433
|
];
|
|
8290
8434
|
class Tokenizer {
|
|
8291
8435
|
constructor(str, keywords, operators, options) {
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
_Tokenizer_markStack.set(this, []);
|
|
8301
|
-
__classPrivateFieldSet(this, _Tokenizer_str, str, "f");
|
|
8302
|
-
__classPrivateFieldSet(this, _Tokenizer_keywords, keywords, "f");
|
|
8303
|
-
__classPrivateFieldSet(this, _Tokenizer_operators, operators, "f");
|
|
8304
|
-
__classPrivateFieldSet(this, _Tokenizer_dateTimeLiterals, !!options?.dateTimeLiterals, "f");
|
|
8305
|
-
__classPrivateFieldSet(this, _Tokenizer_symbolRegex, options?.symbolRegex ?? /[$\w]/, "f");
|
|
8436
|
+
this.result = [];
|
|
8437
|
+
this.pos = { index: 0, line: 1, column: 0 };
|
|
8438
|
+
this.markStack = [];
|
|
8439
|
+
this.str = str;
|
|
8440
|
+
this.keywords = keywords;
|
|
8441
|
+
this.operators = operators;
|
|
8442
|
+
this.dateTimeLiterals = !!options?.dateTimeLiterals;
|
|
8443
|
+
this.symbolRegex = options?.symbolRegex ?? /[$\w]/;
|
|
8306
8444
|
}
|
|
8307
8445
|
tokenize() {
|
|
8308
|
-
while (
|
|
8309
|
-
const token =
|
|
8446
|
+
while (this.pos.index < this.str.length) {
|
|
8447
|
+
const token = this.consumeToken();
|
|
8310
8448
|
if (token) {
|
|
8311
|
-
|
|
8449
|
+
this.result.push(token);
|
|
8312
8450
|
}
|
|
8313
8451
|
}
|
|
8314
|
-
return
|
|
8452
|
+
return this.result;
|
|
8315
8453
|
}
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
return __classPrivateFieldGet(this, _Tokenizer_result, "f").slice(-1)[0];
|
|
8319
|
-
}, _Tokenizer_peekToken = function _Tokenizer_peekToken() {
|
|
8320
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_mark).call(this);
|
|
8321
|
-
const token = __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeToken).call(this);
|
|
8322
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_reset).call(this);
|
|
8323
|
-
return token;
|
|
8324
|
-
}, _Tokenizer_consumeToken = function _Tokenizer_consumeToken() {
|
|
8325
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhitespace).call(this);
|
|
8326
|
-
const c = __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this);
|
|
8327
|
-
if (!c) {
|
|
8328
|
-
return undefined;
|
|
8454
|
+
prevToken() {
|
|
8455
|
+
return this.result.slice(-1)[0];
|
|
8329
8456
|
}
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
if (c === '/' && next === '/') {
|
|
8336
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeSingleLineComment).call(this);
|
|
8337
|
-
}
|
|
8338
|
-
if (c === "'" || c === '"') {
|
|
8339
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeString).call(this, c);
|
|
8340
|
-
}
|
|
8341
|
-
if (c === '`') {
|
|
8342
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeBacktickSymbol).call(this);
|
|
8343
|
-
}
|
|
8344
|
-
if (c === '@') {
|
|
8345
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeDateTime).call(this);
|
|
8346
|
-
}
|
|
8347
|
-
if (c.match(/\d/)) {
|
|
8348
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeNumber).call(this);
|
|
8349
|
-
}
|
|
8350
|
-
if (c.match(/\w/)) {
|
|
8351
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeSymbol).call(this);
|
|
8352
|
-
}
|
|
8353
|
-
if (c === '$' && next.match(/\w/)) {
|
|
8354
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeSymbol).call(this);
|
|
8355
|
-
}
|
|
8356
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeOperator).call(this);
|
|
8357
|
-
}, _Tokenizer_consumeWhitespace = function _Tokenizer_consumeWhitespace() {
|
|
8358
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this).match(/\s/));
|
|
8359
|
-
}, _Tokenizer_consumeMultiLineComment = function _Tokenizer_consumeMultiLineComment() {
|
|
8360
|
-
const start = __classPrivateFieldGet(this, _Tokenizer_pos, "f").index;
|
|
8361
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) !== '*' || __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_peek).call(this) !== '/');
|
|
8362
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8363
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8364
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_buildToken).call(this, 'Comment', __classPrivateFieldGet(this, _Tokenizer_str, "f").substring(start, __classPrivateFieldGet(this, _Tokenizer_pos, "f").index));
|
|
8365
|
-
}, _Tokenizer_consumeSingleLineComment = function _Tokenizer_consumeSingleLineComment() {
|
|
8366
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_buildToken).call(this, 'Comment', __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) !== '\n'));
|
|
8367
|
-
}, _Tokenizer_consumeString = function _Tokenizer_consumeString(endChar) {
|
|
8368
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8369
|
-
const result = __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_buildToken).call(this, 'String', __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_prev).call(this) === '\\' || __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) !== endChar));
|
|
8370
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8371
|
-
return result;
|
|
8372
|
-
}, _Tokenizer_consumeBacktickSymbol = function _Tokenizer_consumeBacktickSymbol() {
|
|
8373
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8374
|
-
const result = __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_buildToken).call(this, 'Symbol', __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) !== '`'));
|
|
8375
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8376
|
-
return result;
|
|
8377
|
-
}, _Tokenizer_consumeDateTime = function _Tokenizer_consumeDateTime() {
|
|
8378
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this); // Consume "@"
|
|
8379
|
-
const start = __classPrivateFieldGet(this, _Tokenizer_pos, "f").index;
|
|
8380
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this).match(/[\d-]/));
|
|
8381
|
-
if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === 'T') {
|
|
8382
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8383
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this).match(/[\d:]/));
|
|
8384
|
-
if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === '.' && __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_peek).call(this).match(/\d/)) {
|
|
8385
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8386
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this).match(/\d/));
|
|
8387
|
-
}
|
|
8388
|
-
if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === 'Z') {
|
|
8389
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8390
|
-
}
|
|
8391
|
-
else if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === '+' || __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === '-') {
|
|
8392
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8393
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this).match(/[\d:]/));
|
|
8394
|
-
}
|
|
8395
|
-
}
|
|
8396
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_buildToken).call(this, 'DateTime', __classPrivateFieldGet(this, _Tokenizer_str, "f").substring(start, __classPrivateFieldGet(this, _Tokenizer_pos, "f").index));
|
|
8397
|
-
}, _Tokenizer_consumeNumber = function _Tokenizer_consumeNumber() {
|
|
8398
|
-
const start = __classPrivateFieldGet(this, _Tokenizer_pos, "f").index;
|
|
8399
|
-
let id = 'Number';
|
|
8400
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this).match(/\d/));
|
|
8401
|
-
if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === '.' && __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_peek).call(this).match(/\d/)) {
|
|
8402
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8403
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this).match(/\d/));
|
|
8404
|
-
}
|
|
8405
|
-
if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === '-' && __classPrivateFieldGet(this, _Tokenizer_dateTimeLiterals, "f")) {
|
|
8406
|
-
// Rewind to one character before the start, and then treat as dateTime literal.
|
|
8407
|
-
__classPrivateFieldGet(this, _Tokenizer_pos, "f").index = start - 1;
|
|
8408
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeDateTime).call(this);
|
|
8409
|
-
}
|
|
8410
|
-
if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === ' ') {
|
|
8411
|
-
if (isUnitToken(__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_peekToken).call(this))) {
|
|
8412
|
-
id = 'Quantity';
|
|
8413
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeToken).call(this);
|
|
8414
|
-
}
|
|
8415
|
-
}
|
|
8416
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_buildToken).call(this, id, __classPrivateFieldGet(this, _Tokenizer_str, "f").substring(start, __classPrivateFieldGet(this, _Tokenizer_pos, "f").index));
|
|
8417
|
-
}, _Tokenizer_consumeSymbol = function _Tokenizer_consumeSymbol() {
|
|
8418
|
-
const value = __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this).match(__classPrivateFieldGet(this, _Tokenizer_symbolRegex, "f")));
|
|
8419
|
-
if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_prevToken).call(this)?.value !== '.' && __classPrivateFieldGet(this, _Tokenizer_keywords, "f").includes(value)) {
|
|
8420
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_buildToken).call(this, value, value);
|
|
8421
|
-
}
|
|
8422
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_buildToken).call(this, 'Symbol', value);
|
|
8423
|
-
}, _Tokenizer_consumeOperator = function _Tokenizer_consumeOperator() {
|
|
8424
|
-
const c = __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this);
|
|
8425
|
-
const next = __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_peek).call(this);
|
|
8426
|
-
const twoCharOp = c + next;
|
|
8427
|
-
if (__classPrivateFieldGet(this, _Tokenizer_operators, "f").includes(twoCharOp)) {
|
|
8428
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8429
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8430
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_buildToken).call(this, twoCharOp, twoCharOp);
|
|
8431
|
-
}
|
|
8432
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8433
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_buildToken).call(this, c, c);
|
|
8434
|
-
}, _Tokenizer_consumeWhile = function _Tokenizer_consumeWhile(condition) {
|
|
8435
|
-
const start = __classPrivateFieldGet(this, _Tokenizer_pos, "f").index;
|
|
8436
|
-
while (__classPrivateFieldGet(this, _Tokenizer_pos, "f").index < __classPrivateFieldGet(this, _Tokenizer_str, "f").length && condition()) {
|
|
8437
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8438
|
-
}
|
|
8439
|
-
return __classPrivateFieldGet(this, _Tokenizer_str, "f").substring(start, __classPrivateFieldGet(this, _Tokenizer_pos, "f").index);
|
|
8440
|
-
}, _Tokenizer_curr = function _Tokenizer_curr() {
|
|
8441
|
-
return __classPrivateFieldGet(this, _Tokenizer_str, "f")[__classPrivateFieldGet(this, _Tokenizer_pos, "f").index];
|
|
8442
|
-
}, _Tokenizer_prev = function _Tokenizer_prev() {
|
|
8443
|
-
return __classPrivateFieldGet(this, _Tokenizer_str, "f")[__classPrivateFieldGet(this, _Tokenizer_pos, "f").index - 1] ?? '';
|
|
8444
|
-
}, _Tokenizer_peek = function _Tokenizer_peek() {
|
|
8445
|
-
return __classPrivateFieldGet(this, _Tokenizer_str, "f")[__classPrivateFieldGet(this, _Tokenizer_pos, "f").index + 1] ?? '';
|
|
8446
|
-
}, _Tokenizer_mark = function _Tokenizer_mark() {
|
|
8447
|
-
__classPrivateFieldGet(this, _Tokenizer_markStack, "f").push({ ...__classPrivateFieldGet(this, _Tokenizer_pos, "f") });
|
|
8448
|
-
}, _Tokenizer_reset = function _Tokenizer_reset() {
|
|
8449
|
-
const mark = __classPrivateFieldGet(this, _Tokenizer_markStack, "f").pop();
|
|
8450
|
-
if (!mark) {
|
|
8451
|
-
throw new Error('No mark to reset to');
|
|
8452
|
-
}
|
|
8453
|
-
__classPrivateFieldGet(this, _Tokenizer_pos, "f").index = mark.index;
|
|
8454
|
-
__classPrivateFieldGet(this, _Tokenizer_pos, "f").line = mark.line;
|
|
8455
|
-
__classPrivateFieldGet(this, _Tokenizer_pos, "f").column = mark.column;
|
|
8456
|
-
}, _Tokenizer_advance = function _Tokenizer_advance() {
|
|
8457
|
-
__classPrivateFieldGet(this, _Tokenizer_pos, "f").index++;
|
|
8458
|
-
if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === '\n') {
|
|
8459
|
-
__classPrivateFieldGet(this, _Tokenizer_pos, "f").line++;
|
|
8460
|
-
__classPrivateFieldGet(this, _Tokenizer_pos, "f").column = 0;
|
|
8457
|
+
peekToken() {
|
|
8458
|
+
this.mark();
|
|
8459
|
+
const token = this.consumeToken();
|
|
8460
|
+
this.reset();
|
|
8461
|
+
return token;
|
|
8461
8462
|
}
|
|
8462
|
-
|
|
8463
|
-
|
|
8463
|
+
consumeToken() {
|
|
8464
|
+
this.consumeWhitespace();
|
|
8465
|
+
const c = this.curr();
|
|
8466
|
+
if (!c) {
|
|
8467
|
+
return undefined;
|
|
8468
|
+
}
|
|
8469
|
+
this.mark();
|
|
8470
|
+
const next = this.peek();
|
|
8471
|
+
if (c === '/' && next === '*') {
|
|
8472
|
+
return this.consumeMultiLineComment();
|
|
8473
|
+
}
|
|
8474
|
+
if (c === '/' && next === '/') {
|
|
8475
|
+
return this.consumeSingleLineComment();
|
|
8476
|
+
}
|
|
8477
|
+
if (c === "'" || c === '"') {
|
|
8478
|
+
return this.consumeString(c);
|
|
8479
|
+
}
|
|
8480
|
+
if (c === '`') {
|
|
8481
|
+
return this.consumeBacktickSymbol();
|
|
8482
|
+
}
|
|
8483
|
+
if (c === '@') {
|
|
8484
|
+
return this.consumeDateTime();
|
|
8485
|
+
}
|
|
8486
|
+
if (c.match(/\d/)) {
|
|
8487
|
+
return this.consumeNumber();
|
|
8488
|
+
}
|
|
8489
|
+
if (c.match(/\w/)) {
|
|
8490
|
+
return this.consumeSymbol();
|
|
8491
|
+
}
|
|
8492
|
+
if (c === '$' && next.match(/\w/)) {
|
|
8493
|
+
return this.consumeSymbol();
|
|
8494
|
+
}
|
|
8495
|
+
return this.consumeOperator();
|
|
8464
8496
|
}
|
|
8465
|
-
|
|
8466
|
-
|
|
8467
|
-
if (!mark) {
|
|
8468
|
-
throw new Error('No mark for token');
|
|
8497
|
+
consumeWhitespace() {
|
|
8498
|
+
this.consumeWhile(() => this.curr().match(/\s/));
|
|
8469
8499
|
}
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8500
|
+
consumeMultiLineComment() {
|
|
8501
|
+
const start = this.pos.index;
|
|
8502
|
+
this.consumeWhile(() => this.curr() !== '*' || this.peek() !== '/');
|
|
8503
|
+
this.advance();
|
|
8504
|
+
this.advance();
|
|
8505
|
+
return this.buildToken('Comment', this.str.substring(start, this.pos.index));
|
|
8506
|
+
}
|
|
8507
|
+
consumeSingleLineComment() {
|
|
8508
|
+
return this.buildToken('Comment', this.consumeWhile(() => this.curr() !== '\n'));
|
|
8509
|
+
}
|
|
8510
|
+
consumeString(endChar) {
|
|
8511
|
+
this.advance();
|
|
8512
|
+
const result = this.buildToken('String', this.consumeWhile(() => this.prev() === '\\' || this.curr() !== endChar));
|
|
8513
|
+
this.advance();
|
|
8514
|
+
return result;
|
|
8515
|
+
}
|
|
8516
|
+
consumeBacktickSymbol() {
|
|
8517
|
+
this.advance();
|
|
8518
|
+
const result = this.buildToken('Symbol', this.consumeWhile(() => this.curr() !== '`'));
|
|
8519
|
+
this.advance();
|
|
8520
|
+
return result;
|
|
8521
|
+
}
|
|
8522
|
+
consumeDateTime() {
|
|
8523
|
+
this.advance(); // Consume "@"
|
|
8524
|
+
const start = this.pos.index;
|
|
8525
|
+
this.consumeWhile(() => this.curr().match(/[\d-]/));
|
|
8526
|
+
if (this.curr() === 'T') {
|
|
8527
|
+
this.advance();
|
|
8528
|
+
this.consumeWhile(() => this.curr().match(/[\d:]/));
|
|
8529
|
+
if (this.curr() === '.' && this.peek().match(/\d/)) {
|
|
8530
|
+
this.advance();
|
|
8531
|
+
this.consumeWhile(() => this.curr().match(/\d/));
|
|
8532
|
+
}
|
|
8533
|
+
if (this.curr() === 'Z') {
|
|
8534
|
+
this.advance();
|
|
8535
|
+
}
|
|
8536
|
+
else if (this.curr() === '+' || this.curr() === '-') {
|
|
8537
|
+
this.advance();
|
|
8538
|
+
this.consumeWhile(() => this.curr().match(/[\d:]/));
|
|
8539
|
+
}
|
|
8540
|
+
}
|
|
8541
|
+
return this.buildToken('DateTime', this.str.substring(start, this.pos.index));
|
|
8542
|
+
}
|
|
8543
|
+
consumeNumber() {
|
|
8544
|
+
const start = this.pos.index;
|
|
8545
|
+
let id = 'Number';
|
|
8546
|
+
this.consumeWhile(() => this.curr().match(/\d/));
|
|
8547
|
+
if (this.curr() === '.' && this.peek().match(/\d/)) {
|
|
8548
|
+
this.advance();
|
|
8549
|
+
this.consumeWhile(() => this.curr().match(/\d/));
|
|
8550
|
+
}
|
|
8551
|
+
if (this.curr() === '-' && this.dateTimeLiterals) {
|
|
8552
|
+
// Rewind to one character before the start, and then treat as dateTime literal.
|
|
8553
|
+
this.pos.index = start - 1;
|
|
8554
|
+
return this.consumeDateTime();
|
|
8555
|
+
}
|
|
8556
|
+
if (this.curr() === ' ') {
|
|
8557
|
+
if (isUnitToken(this.peekToken())) {
|
|
8558
|
+
id = 'Quantity';
|
|
8559
|
+
this.consumeToken();
|
|
8560
|
+
}
|
|
8561
|
+
}
|
|
8562
|
+
return this.buildToken(id, this.str.substring(start, this.pos.index));
|
|
8563
|
+
}
|
|
8564
|
+
consumeSymbol() {
|
|
8565
|
+
const value = this.consumeWhile(() => this.curr().match(this.symbolRegex));
|
|
8566
|
+
if (this.prevToken()?.value !== '.' && this.keywords.includes(value)) {
|
|
8567
|
+
return this.buildToken(value, value);
|
|
8568
|
+
}
|
|
8569
|
+
return this.buildToken('Symbol', value);
|
|
8570
|
+
}
|
|
8571
|
+
consumeOperator() {
|
|
8572
|
+
const c = this.curr();
|
|
8573
|
+
const next = this.peek();
|
|
8574
|
+
const twoCharOp = c + next;
|
|
8575
|
+
if (this.operators.includes(twoCharOp)) {
|
|
8576
|
+
this.advance();
|
|
8577
|
+
this.advance();
|
|
8578
|
+
return this.buildToken(twoCharOp, twoCharOp);
|
|
8579
|
+
}
|
|
8580
|
+
this.advance();
|
|
8581
|
+
return this.buildToken(c, c);
|
|
8582
|
+
}
|
|
8583
|
+
consumeWhile(condition) {
|
|
8584
|
+
const start = this.pos.index;
|
|
8585
|
+
while (this.pos.index < this.str.length && condition()) {
|
|
8586
|
+
this.advance();
|
|
8587
|
+
}
|
|
8588
|
+
return this.str.substring(start, this.pos.index);
|
|
8589
|
+
}
|
|
8590
|
+
curr() {
|
|
8591
|
+
return this.str[this.pos.index];
|
|
8592
|
+
}
|
|
8593
|
+
prev() {
|
|
8594
|
+
return this.str[this.pos.index - 1] ?? '';
|
|
8595
|
+
}
|
|
8596
|
+
peek() {
|
|
8597
|
+
return this.str[this.pos.index + 1] ?? '';
|
|
8598
|
+
}
|
|
8599
|
+
mark() {
|
|
8600
|
+
this.markStack.push({ ...this.pos });
|
|
8601
|
+
}
|
|
8602
|
+
reset() {
|
|
8603
|
+
const mark = this.markStack.pop();
|
|
8604
|
+
if (!mark) {
|
|
8605
|
+
throw new Error('No mark to reset to');
|
|
8606
|
+
}
|
|
8607
|
+
this.pos.index = mark.index;
|
|
8608
|
+
this.pos.line = mark.line;
|
|
8609
|
+
this.pos.column = mark.column;
|
|
8610
|
+
}
|
|
8611
|
+
advance() {
|
|
8612
|
+
this.pos.index++;
|
|
8613
|
+
if (this.curr() === '\n') {
|
|
8614
|
+
this.pos.line++;
|
|
8615
|
+
this.pos.column = 0;
|
|
8616
|
+
}
|
|
8617
|
+
else {
|
|
8618
|
+
this.pos.column++;
|
|
8619
|
+
}
|
|
8620
|
+
}
|
|
8621
|
+
buildToken(id, value) {
|
|
8622
|
+
const mark = this.markStack.pop();
|
|
8623
|
+
if (!mark) {
|
|
8624
|
+
throw new Error('No mark for token');
|
|
8625
|
+
}
|
|
8626
|
+
return {
|
|
8627
|
+
id,
|
|
8628
|
+
value,
|
|
8629
|
+
...mark,
|
|
8630
|
+
};
|
|
8631
|
+
}
|
|
8632
|
+
}
|
|
8476
8633
|
function isUnitToken(token) {
|
|
8477
8634
|
if (token) {
|
|
8478
8635
|
if (token.id === 'String') {
|
|
@@ -10380,7 +10537,6 @@
|
|
|
10380
10537
|
return input;
|
|
10381
10538
|
}
|
|
10382
10539
|
|
|
10383
|
-
var _SymbolAtom_instances, _SymbolAtom_evalValue;
|
|
10384
10540
|
class FhirPathAtom {
|
|
10385
10541
|
constructor(original, child) {
|
|
10386
10542
|
this.original = original;
|
|
@@ -10420,32 +10576,28 @@
|
|
|
10420
10576
|
}
|
|
10421
10577
|
class SymbolAtom {
|
|
10422
10578
|
constructor(name) {
|
|
10423
|
-
_SymbolAtom_instances.add(this);
|
|
10424
10579
|
this.name = name;
|
|
10425
10580
|
}
|
|
10426
10581
|
eval(context) {
|
|
10427
10582
|
if (this.name === '$this') {
|
|
10428
10583
|
return context;
|
|
10429
10584
|
}
|
|
10430
|
-
return context
|
|
10431
|
-
|
|
10432
|
-
|
|
10433
|
-
|
|
10585
|
+
return context.flatMap((e) => this.evalValue(e)).filter((e) => e?.value !== undefined);
|
|
10586
|
+
}
|
|
10587
|
+
evalValue(typedValue) {
|
|
10588
|
+
const input = typedValue.value;
|
|
10589
|
+
if (!input || typeof input !== 'object') {
|
|
10590
|
+
return undefined;
|
|
10591
|
+
}
|
|
10592
|
+
if (isResource(input) && input.resourceType === this.name) {
|
|
10593
|
+
return typedValue;
|
|
10594
|
+
}
|
|
10595
|
+
return getTypedPropertyValue(typedValue, this.name);
|
|
10434
10596
|
}
|
|
10435
10597
|
toString() {
|
|
10436
10598
|
return this.name;
|
|
10437
10599
|
}
|
|
10438
10600
|
}
|
|
10439
|
-
_SymbolAtom_instances = new WeakSet(), _SymbolAtom_evalValue = function _SymbolAtom_evalValue(typedValue) {
|
|
10440
|
-
const input = typedValue.value;
|
|
10441
|
-
if (!input || typeof input !== 'object') {
|
|
10442
|
-
return undefined;
|
|
10443
|
-
}
|
|
10444
|
-
if (isResource(input) && input.resourceType === this.name) {
|
|
10445
|
-
return typedValue;
|
|
10446
|
-
}
|
|
10447
|
-
return getTypedPropertyValue(typedValue, this.name);
|
|
10448
|
-
};
|
|
10449
10601
|
class EmptySetAtom {
|
|
10450
10602
|
eval() {
|
|
10451
10603
|
return [];
|
|
@@ -10862,10 +11014,8 @@
|
|
|
10862
11014
|
return new Tokenizer(str, FHIRPATH_KEYWORDS, MAPPING_LANGUAGE_OPERATORS$1).tokenize();
|
|
10863
11015
|
}
|
|
10864
11016
|
|
|
10865
|
-
var _StructureMapParser_instances, _StructureMapParser_parseUses, _StructureMapParser_parseImport, _StructureMapParser_parseGroup, _StructureMapParser_parseParameters, _StructureMapParser_parseParameter, _StructureMapParser_parseRules, _StructureMapParser_parseRule, _StructureMapParser_parseRuleSources, _StructureMapParser_parseRuleSource, _StructureMapParser_parseRuleTargets, _StructureMapParser_parseRuleTarget, _StructureMapParser_parseRuleTargetTransform, _StructureMapParser_parseRuleTargetSymbol, _StructureMapParser_parseRuleTargetFunction, _StructureMapParser_parseRuleTargetLiteral, _StructureMapParser_parseRuleContext, _StructureMapParser_parseRuleDependents, _StructureMapParser_parseConceptMap;
|
|
10866
11017
|
class StructureMapParser {
|
|
10867
11018
|
constructor(parser) {
|
|
10868
|
-
_StructureMapParser_instances.add(this);
|
|
10869
11019
|
this.parser = parser;
|
|
10870
11020
|
this.structureMap = { resourceType: 'StructureMap' };
|
|
10871
11021
|
}
|
|
@@ -10880,16 +11030,16 @@
|
|
|
10880
11030
|
const next = this.parser.peek()?.value;
|
|
10881
11031
|
switch (next) {
|
|
10882
11032
|
case 'uses':
|
|
10883
|
-
|
|
11033
|
+
this.parseUses();
|
|
10884
11034
|
break;
|
|
10885
11035
|
case 'imports':
|
|
10886
|
-
|
|
11036
|
+
this.parseImport();
|
|
10887
11037
|
break;
|
|
10888
11038
|
case 'group':
|
|
10889
|
-
|
|
11039
|
+
this.parseGroup();
|
|
10890
11040
|
break;
|
|
10891
11041
|
case 'conceptmap':
|
|
10892
|
-
|
|
11042
|
+
this.parseConceptMap();
|
|
10893
11043
|
break;
|
|
10894
11044
|
default:
|
|
10895
11045
|
throw new Error(`Unexpected token: ${next}`);
|
|
@@ -10897,263 +11047,280 @@
|
|
|
10897
11047
|
}
|
|
10898
11048
|
return this.structureMap;
|
|
10899
11049
|
}
|
|
10900
|
-
|
|
10901
|
-
|
|
10902
|
-
|
|
10903
|
-
|
|
10904
|
-
|
|
10905
|
-
|
|
10906
|
-
|
|
10907
|
-
|
|
10908
|
-
|
|
10909
|
-
result.alias = this.parser.consume('Symbol').value;
|
|
10910
|
-
}
|
|
10911
|
-
this.parser.consume('Symbol', 'as');
|
|
10912
|
-
result.mode = this.parser.consume().value;
|
|
10913
|
-
if (!this.structureMap.structure) {
|
|
10914
|
-
this.structureMap.structure = [];
|
|
10915
|
-
}
|
|
10916
|
-
this.structureMap.structure.push(result);
|
|
10917
|
-
}, _StructureMapParser_parseImport = function _StructureMapParser_parseImport() {
|
|
10918
|
-
this.parser.consume('Symbol', 'imports');
|
|
10919
|
-
if (!this.structureMap.import) {
|
|
10920
|
-
this.structureMap.import = [];
|
|
10921
|
-
}
|
|
10922
|
-
this.structureMap.import.push(this.parser.consume('String').value);
|
|
10923
|
-
}, _StructureMapParser_parseGroup = function _StructureMapParser_parseGroup() {
|
|
10924
|
-
// 'group' identifier parameters extends? typeMode? rules
|
|
10925
|
-
// group tutorial(source src : TLeft, target tgt : TRight) {
|
|
10926
|
-
const result = {};
|
|
10927
|
-
this.parser.consume('Symbol', 'group');
|
|
10928
|
-
result.name = this.parser.consume('Symbol').value;
|
|
10929
|
-
result.input = __classPrivateFieldGet(this, _StructureMapParser_instances, "m", _StructureMapParser_parseParameters).call(this);
|
|
10930
|
-
if (this.parser.peek()?.value === 'extends') {
|
|
10931
|
-
this.parser.consume('Symbol', 'extends');
|
|
10932
|
-
result.extends = this.parser.consume('Symbol').value;
|
|
10933
|
-
}
|
|
10934
|
-
if (this.parser.peek()?.value === '<<') {
|
|
10935
|
-
this.parser.consume('<<');
|
|
10936
|
-
result.typeMode = this.parser.consume().value;
|
|
10937
|
-
if (this.parser.peek()?.value === '+') {
|
|
10938
|
-
this.parser.consume('+');
|
|
10939
|
-
result.typeMode = 'type-and-types';
|
|
10940
|
-
}
|
|
10941
|
-
this.parser.consume('>>');
|
|
10942
|
-
}
|
|
10943
|
-
else {
|
|
10944
|
-
result.typeMode = 'none';
|
|
10945
|
-
}
|
|
10946
|
-
result.rule = __classPrivateFieldGet(this, _StructureMapParser_instances, "m", _StructureMapParser_parseRules).call(this);
|
|
10947
|
-
if (!this.structureMap.group) {
|
|
10948
|
-
this.structureMap.group = [];
|
|
10949
|
-
}
|
|
10950
|
-
this.structureMap.group.push(result);
|
|
10951
|
-
}, _StructureMapParser_parseParameters = function _StructureMapParser_parseParameters() {
|
|
10952
|
-
const parameters = [];
|
|
10953
|
-
this.parser.consume('(');
|
|
10954
|
-
while (this.parser.hasMore() && this.parser.peek()?.value !== ')') {
|
|
10955
|
-
parameters.push(__classPrivateFieldGet(this, _StructureMapParser_instances, "m", _StructureMapParser_parseParameter).call(this));
|
|
10956
|
-
if (this.parser.peek()?.value === ',') {
|
|
10957
|
-
this.parser.consume(',');
|
|
11050
|
+
parseUses() {
|
|
11051
|
+
// 'uses' url structureAlias? 'as' modelMode
|
|
11052
|
+
// uses "http://hl7.org/fhir/StructureDefinition/tutorial-left" as source
|
|
11053
|
+
this.parser.consume('Symbol', 'uses');
|
|
11054
|
+
const result = {};
|
|
11055
|
+
result.url = this.parser.consume('String').value;
|
|
11056
|
+
if (this.parser.peek()?.value === 'alias') {
|
|
11057
|
+
this.parser.consume('Symbol', 'alias');
|
|
11058
|
+
result.alias = this.parser.consume('Symbol').value;
|
|
10958
11059
|
}
|
|
10959
|
-
|
|
10960
|
-
|
|
10961
|
-
|
|
10962
|
-
|
|
10963
|
-
|
|
10964
|
-
|
|
10965
|
-
|
|
10966
|
-
|
|
10967
|
-
|
|
10968
|
-
|
|
10969
|
-
|
|
10970
|
-
|
|
10971
|
-
|
|
10972
|
-
}
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
|
|
10976
|
-
|
|
10977
|
-
|
|
10978
|
-
|
|
10979
|
-
|
|
10980
|
-
|
|
10981
|
-
|
|
10982
|
-
|
|
10983
|
-
|
|
10984
|
-
|
|
10985
|
-
|
|
10986
|
-
|
|
10987
|
-
|
|
10988
|
-
|
|
10989
|
-
|
|
10990
|
-
|
|
10991
|
-
|
|
10992
|
-
if (this.parser.peek()?.id === '{') {
|
|
10993
|
-
result.rule = __classPrivateFieldGet(this, _StructureMapParser_instances, "m", _StructureMapParser_parseRules).call(this);
|
|
11060
|
+
this.parser.consume('Symbol', 'as');
|
|
11061
|
+
result.mode = this.parser.consume().value;
|
|
11062
|
+
if (!this.structureMap.structure) {
|
|
11063
|
+
this.structureMap.structure = [];
|
|
11064
|
+
}
|
|
11065
|
+
this.structureMap.structure.push(result);
|
|
11066
|
+
}
|
|
11067
|
+
parseImport() {
|
|
11068
|
+
this.parser.consume('Symbol', 'imports');
|
|
11069
|
+
if (!this.structureMap.import) {
|
|
11070
|
+
this.structureMap.import = [];
|
|
11071
|
+
}
|
|
11072
|
+
this.structureMap.import.push(this.parser.consume('String').value);
|
|
11073
|
+
}
|
|
11074
|
+
parseGroup() {
|
|
11075
|
+
// 'group' identifier parameters extends? typeMode? rules
|
|
11076
|
+
// group tutorial(source src : TLeft, target tgt : TRight) {
|
|
11077
|
+
const result = {};
|
|
11078
|
+
this.parser.consume('Symbol', 'group');
|
|
11079
|
+
result.name = this.parser.consume('Symbol').value;
|
|
11080
|
+
result.input = this.parseParameters();
|
|
11081
|
+
if (this.parser.peek()?.value === 'extends') {
|
|
11082
|
+
this.parser.consume('Symbol', 'extends');
|
|
11083
|
+
result.extends = this.parser.consume('Symbol').value;
|
|
11084
|
+
}
|
|
11085
|
+
if (this.parser.peek()?.value === '<<') {
|
|
11086
|
+
this.parser.consume('<<');
|
|
11087
|
+
result.typeMode = this.parser.consume().value;
|
|
11088
|
+
if (this.parser.peek()?.value === '+') {
|
|
11089
|
+
this.parser.consume('+');
|
|
11090
|
+
result.typeMode = 'type-and-types';
|
|
11091
|
+
}
|
|
11092
|
+
this.parser.consume('>>');
|
|
10994
11093
|
}
|
|
10995
11094
|
else {
|
|
10996
|
-
result.
|
|
11095
|
+
result.typeMode = 'none';
|
|
10997
11096
|
}
|
|
11097
|
+
result.rule = this.parseRules();
|
|
11098
|
+
if (!this.structureMap.group) {
|
|
11099
|
+
this.structureMap.group = [];
|
|
11100
|
+
}
|
|
11101
|
+
this.structureMap.group.push(result);
|
|
10998
11102
|
}
|
|
10999
|
-
|
|
11000
|
-
|
|
11001
|
-
|
|
11002
|
-
|
|
11003
|
-
|
|
11004
|
-
|
|
11005
|
-
|
|
11006
|
-
|
|
11007
|
-
|
|
11008
|
-
|
|
11009
|
-
|
|
11010
|
-
this.parser.consume(',');
|
|
11011
|
-
sources.push(__classPrivateFieldGet(this, _StructureMapParser_instances, "m", _StructureMapParser_parseRuleSource).call(this));
|
|
11012
|
-
}
|
|
11013
|
-
return sources;
|
|
11014
|
-
}, _StructureMapParser_parseRuleSource = function _StructureMapParser_parseRuleSource() {
|
|
11015
|
-
const result = {};
|
|
11016
|
-
const context = __classPrivateFieldGet(this, _StructureMapParser_instances, "m", _StructureMapParser_parseRuleContext).call(this);
|
|
11017
|
-
if (context.includes('.')) {
|
|
11018
|
-
const parts = context.split('.');
|
|
11019
|
-
result.context = parts[0];
|
|
11020
|
-
result.element = parts[1];
|
|
11021
|
-
}
|
|
11022
|
-
else {
|
|
11023
|
-
result.context = context;
|
|
11024
|
-
}
|
|
11025
|
-
if (this.parser.hasMore() && this.parser.peek()?.value === ':') {
|
|
11026
|
-
this.parser.consume(':');
|
|
11027
|
-
result.type = this.parser.consume().value;
|
|
11028
|
-
}
|
|
11029
|
-
if (this.parser.hasMore() && this.parser.peek()?.value === 'default') {
|
|
11030
|
-
this.parser.consume('default');
|
|
11031
|
-
this.parser.consumeAndParse();
|
|
11032
|
-
}
|
|
11033
|
-
if (this.parser.peek()?.value === 'first' ||
|
|
11034
|
-
this.parser.peek()?.value === 'not_first' ||
|
|
11035
|
-
this.parser.peek()?.value === 'last' ||
|
|
11036
|
-
this.parser.peek()?.value === 'not_last' ||
|
|
11037
|
-
this.parser.peek()?.value === 'only_one') {
|
|
11038
|
-
result.listMode = this.parser.consume().value;
|
|
11103
|
+
parseParameters() {
|
|
11104
|
+
const parameters = [];
|
|
11105
|
+
this.parser.consume('(');
|
|
11106
|
+
while (this.parser.hasMore() && this.parser.peek()?.value !== ')') {
|
|
11107
|
+
parameters.push(this.parseParameter());
|
|
11108
|
+
if (this.parser.peek()?.value === ',') {
|
|
11109
|
+
this.parser.consume(',');
|
|
11110
|
+
}
|
|
11111
|
+
}
|
|
11112
|
+
this.parser.consume(')');
|
|
11113
|
+
return parameters;
|
|
11039
11114
|
}
|
|
11040
|
-
|
|
11041
|
-
|
|
11042
|
-
|
|
11115
|
+
parseParameter() {
|
|
11116
|
+
// inputMode identifier type?
|
|
11117
|
+
// ':' identifier
|
|
11118
|
+
// source src : TLeft
|
|
11119
|
+
const result = {};
|
|
11120
|
+
result.mode = this.parser.consume().value;
|
|
11121
|
+
result.name = this.parser.consume('Symbol').value;
|
|
11122
|
+
if (this.parser.peek()?.value === ':') {
|
|
11123
|
+
this.parser.consume(':');
|
|
11124
|
+
result.type = this.parser.consume('Symbol').value;
|
|
11125
|
+
}
|
|
11126
|
+
return result;
|
|
11043
11127
|
}
|
|
11044
|
-
|
|
11045
|
-
|
|
11046
|
-
|
|
11047
|
-
|
|
11128
|
+
parseRules() {
|
|
11129
|
+
const rules = [];
|
|
11130
|
+
this.parser.consume('{');
|
|
11131
|
+
while (this.parser.hasMore() && this.parser.peek()?.value !== '}') {
|
|
11132
|
+
rules.push(this.parseRule());
|
|
11133
|
+
}
|
|
11134
|
+
this.parser.consume('}');
|
|
11135
|
+
return rules;
|
|
11136
|
+
}
|
|
11137
|
+
parseRule() {
|
|
11138
|
+
const result = {
|
|
11139
|
+
source: this.parseRuleSources(),
|
|
11140
|
+
};
|
|
11141
|
+
if (this.parser.peek()?.value === '->') {
|
|
11142
|
+
this.parser.consume('->');
|
|
11143
|
+
result.target = this.parseRuleTargets();
|
|
11144
|
+
}
|
|
11145
|
+
if (this.parser.peek()?.value === 'then') {
|
|
11146
|
+
this.parser.consume('Symbol', 'then');
|
|
11147
|
+
if (this.parser.peek()?.id === '{') {
|
|
11148
|
+
result.rule = this.parseRules();
|
|
11149
|
+
}
|
|
11150
|
+
else {
|
|
11151
|
+
result.dependent = this.parseRuleDependents();
|
|
11152
|
+
}
|
|
11153
|
+
}
|
|
11154
|
+
if (this.parser.peek()?.id === 'String') {
|
|
11155
|
+
result.name = this.parser.consume().value;
|
|
11156
|
+
}
|
|
11157
|
+
else {
|
|
11158
|
+
result.name = result.source?.[0]?.element;
|
|
11159
|
+
}
|
|
11160
|
+
this.parser.consume(';');
|
|
11161
|
+
return result;
|
|
11048
11162
|
}
|
|
11049
|
-
|
|
11050
|
-
this.
|
|
11051
|
-
|
|
11052
|
-
|
|
11163
|
+
parseRuleSources() {
|
|
11164
|
+
const sources = [this.parseRuleSource()];
|
|
11165
|
+
while (this.parser.hasMore() && this.parser.peek()?.value === ',') {
|
|
11166
|
+
this.parser.consume(',');
|
|
11167
|
+
sources.push(this.parseRuleSource());
|
|
11168
|
+
}
|
|
11169
|
+
return sources;
|
|
11053
11170
|
}
|
|
11054
|
-
|
|
11055
|
-
|
|
11056
|
-
|
|
11057
|
-
|
|
11058
|
-
|
|
11059
|
-
|
|
11060
|
-
|
|
11061
|
-
|
|
11062
|
-
|
|
11063
|
-
|
|
11064
|
-
|
|
11065
|
-
|
|
11066
|
-
|
|
11067
|
-
|
|
11068
|
-
|
|
11069
|
-
|
|
11171
|
+
parseRuleSource() {
|
|
11172
|
+
const result = {};
|
|
11173
|
+
const context = this.parseRuleContext();
|
|
11174
|
+
if (context.includes('.')) {
|
|
11175
|
+
const parts = context.split('.');
|
|
11176
|
+
result.context = parts[0];
|
|
11177
|
+
result.element = parts[1];
|
|
11178
|
+
}
|
|
11179
|
+
else {
|
|
11180
|
+
result.context = context;
|
|
11181
|
+
}
|
|
11182
|
+
if (this.parser.hasMore() && this.parser.peek()?.value === ':') {
|
|
11183
|
+
this.parser.consume(':');
|
|
11184
|
+
result.type = this.parser.consume().value;
|
|
11185
|
+
}
|
|
11186
|
+
if (this.parser.hasMore() && this.parser.peek()?.value === 'default') {
|
|
11187
|
+
this.parser.consume('default');
|
|
11188
|
+
this.parser.consumeAndParse();
|
|
11189
|
+
}
|
|
11190
|
+
if (this.parser.peek()?.value === 'first' ||
|
|
11191
|
+
this.parser.peek()?.value === 'not_first' ||
|
|
11192
|
+
this.parser.peek()?.value === 'last' ||
|
|
11193
|
+
this.parser.peek()?.value === 'not_last' ||
|
|
11194
|
+
this.parser.peek()?.value === 'only_one') {
|
|
11195
|
+
result.listMode = this.parser.consume().value;
|
|
11196
|
+
}
|
|
11197
|
+
if (this.parser.peek()?.value === 'as') {
|
|
11198
|
+
this.parser.consume('Symbol', 'as');
|
|
11199
|
+
result.variable = this.parser.consume().value;
|
|
11200
|
+
}
|
|
11201
|
+
if (this.parser.peek()?.value === 'where') {
|
|
11202
|
+
this.parser.consume('Symbol', 'where');
|
|
11203
|
+
const whereFhirPath = this.parser.consumeAndParse(100 /* OperatorPrecedence.Arrow */);
|
|
11204
|
+
result.condition = whereFhirPath.toString();
|
|
11205
|
+
}
|
|
11206
|
+
if (this.parser.peek()?.value === 'check') {
|
|
11207
|
+
this.parser.consume('Symbol', 'check');
|
|
11208
|
+
const checkFhirPath = this.parser.consumeAndParse(100 /* OperatorPrecedence.Arrow */);
|
|
11209
|
+
result.check = checkFhirPath.toString();
|
|
11210
|
+
}
|
|
11211
|
+
return result;
|
|
11070
11212
|
}
|
|
11071
|
-
|
|
11072
|
-
|
|
11213
|
+
parseRuleTargets() {
|
|
11214
|
+
const targets = [this.parseRuleTarget()];
|
|
11215
|
+
while (this.parser.hasMore() && this.parser.peek()?.value === ',') {
|
|
11216
|
+
this.parser.consume(',');
|
|
11217
|
+
targets.push(this.parseRuleTarget());
|
|
11218
|
+
}
|
|
11219
|
+
return targets;
|
|
11073
11220
|
}
|
|
11074
|
-
|
|
11075
|
-
|
|
11076
|
-
|
|
11221
|
+
parseRuleTarget() {
|
|
11222
|
+
const result = {};
|
|
11223
|
+
const context = this.parseRuleContext();
|
|
11224
|
+
if (context.includes('.')) {
|
|
11225
|
+
const parts = context.split('.');
|
|
11226
|
+
result.contextType = 'variable';
|
|
11227
|
+
result.context = parts[0];
|
|
11228
|
+
result.element = parts[1];
|
|
11229
|
+
}
|
|
11230
|
+
else {
|
|
11231
|
+
result.context = context;
|
|
11232
|
+
}
|
|
11233
|
+
if (this.parser.peek()?.value === '=') {
|
|
11234
|
+
this.parser.consume('=');
|
|
11235
|
+
this.parseRuleTargetTransform(result);
|
|
11236
|
+
}
|
|
11237
|
+
if (this.parser.peek()?.value === 'as') {
|
|
11238
|
+
this.parser.consume('Symbol', 'as');
|
|
11239
|
+
result.variable = this.parser.consume().value;
|
|
11240
|
+
}
|
|
11241
|
+
if (this.parser.peek()?.value === 'first' ||
|
|
11242
|
+
this.parser.peek()?.value === 'share' ||
|
|
11243
|
+
this.parser.peek()?.value === 'last' ||
|
|
11244
|
+
this.parser.peek()?.value === 'collate') {
|
|
11245
|
+
result.listMode = [this.parser.consume().value];
|
|
11246
|
+
}
|
|
11247
|
+
return result;
|
|
11077
11248
|
}
|
|
11078
|
-
|
|
11079
|
-
|
|
11080
|
-
|
|
11249
|
+
parseRuleTargetTransform(result) {
|
|
11250
|
+
result.transform = 'copy';
|
|
11251
|
+
const transformFhirPath = this.parser.consumeAndParse(6 /* OperatorPrecedence.As */);
|
|
11252
|
+
if (transformFhirPath instanceof SymbolAtom) {
|
|
11253
|
+
this.parseRuleTargetSymbol(result, transformFhirPath);
|
|
11254
|
+
}
|
|
11255
|
+
else if (transformFhirPath instanceof FunctionAtom) {
|
|
11256
|
+
this.parseRuleTargetFunction(result, transformFhirPath);
|
|
11257
|
+
}
|
|
11258
|
+
else if (transformFhirPath instanceof LiteralAtom) {
|
|
11259
|
+
this.parseRuleTargetLiteral(result, transformFhirPath);
|
|
11260
|
+
}
|
|
11261
|
+
else {
|
|
11262
|
+
throw new Error(`Unexpected FHIRPath: ${transformFhirPath}`);
|
|
11263
|
+
}
|
|
11081
11264
|
}
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
this.parser.peek()?.value === 'last' ||
|
|
11085
|
-
this.parser.peek()?.value === 'collate') {
|
|
11086
|
-
result.listMode = [this.parser.consume().value];
|
|
11265
|
+
parseRuleTargetSymbol(result, literalAtom) {
|
|
11266
|
+
result.parameter = [{ valueId: literalAtom.name }];
|
|
11087
11267
|
}
|
|
11088
|
-
|
|
11089
|
-
|
|
11090
|
-
|
|
11091
|
-
|
|
11092
|
-
|
|
11093
|
-
|
|
11268
|
+
parseRuleTargetFunction(result, functionAtom) {
|
|
11269
|
+
const functionName = functionAtom.name;
|
|
11270
|
+
switch (functionName) {
|
|
11271
|
+
case 'create':
|
|
11272
|
+
result.parameter = [
|
|
11273
|
+
{
|
|
11274
|
+
valueString: (functionAtom.args?.[0]).value.value,
|
|
11275
|
+
},
|
|
11276
|
+
];
|
|
11277
|
+
break;
|
|
11278
|
+
case 'translate':
|
|
11279
|
+
result.parameter = [{}];
|
|
11280
|
+
break;
|
|
11281
|
+
default:
|
|
11282
|
+
throw new Error('Unknown target function: ' + functionName);
|
|
11283
|
+
}
|
|
11094
11284
|
}
|
|
11095
|
-
|
|
11096
|
-
|
|
11285
|
+
parseRuleTargetLiteral(result, literalAtom) {
|
|
11286
|
+
switch (literalAtom.value.type) {
|
|
11287
|
+
case 'boolean':
|
|
11288
|
+
result.parameter = [{ valueBoolean: literalAtom.value.value }];
|
|
11289
|
+
break;
|
|
11290
|
+
case 'decimal':
|
|
11291
|
+
result.parameter = [{ valueDecimal: literalAtom.value.value }];
|
|
11292
|
+
break;
|
|
11293
|
+
case 'string':
|
|
11294
|
+
result.parameter = [{ valueString: literalAtom.value.value }];
|
|
11295
|
+
break;
|
|
11296
|
+
default:
|
|
11297
|
+
throw new Error('Unknown target literal type: ' + literalAtom.value.type);
|
|
11298
|
+
}
|
|
11097
11299
|
}
|
|
11098
|
-
|
|
11099
|
-
|
|
11300
|
+
parseRuleContext() {
|
|
11301
|
+
let identifier = this.parser.consume().value;
|
|
11302
|
+
while (this.parser.peek()?.value === '.') {
|
|
11303
|
+
this.parser.consume('.');
|
|
11304
|
+
identifier += '.' + this.parser.consume().value;
|
|
11305
|
+
}
|
|
11306
|
+
return identifier;
|
|
11100
11307
|
}
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
case 'create':
|
|
11110
|
-
result.parameter = [
|
|
11111
|
-
{
|
|
11112
|
-
valueString: (functionAtom.args?.[0]).value.value,
|
|
11113
|
-
},
|
|
11114
|
-
];
|
|
11115
|
-
break;
|
|
11116
|
-
case 'translate':
|
|
11117
|
-
result.parameter = [{}];
|
|
11118
|
-
break;
|
|
11119
|
-
default:
|
|
11120
|
-
throw new Error('Unknown target function: ' + functionName);
|
|
11308
|
+
parseRuleDependents() {
|
|
11309
|
+
const atom = this.parser.consumeAndParse(100 /* OperatorPrecedence.Arrow */);
|
|
11310
|
+
return [
|
|
11311
|
+
{
|
|
11312
|
+
name: atom.name,
|
|
11313
|
+
variable: atom.args.map((arg) => arg.name),
|
|
11314
|
+
},
|
|
11315
|
+
];
|
|
11121
11316
|
}
|
|
11122
|
-
|
|
11123
|
-
|
|
11124
|
-
|
|
11125
|
-
|
|
11126
|
-
|
|
11127
|
-
case 'decimal':
|
|
11128
|
-
result.parameter = [{ valueDecimal: literalAtom.value.value }];
|
|
11129
|
-
break;
|
|
11130
|
-
case 'string':
|
|
11131
|
-
result.parameter = [{ valueString: literalAtom.value.value }];
|
|
11132
|
-
break;
|
|
11133
|
-
default:
|
|
11134
|
-
throw new Error('Unknown target literal type: ' + literalAtom.value.type);
|
|
11135
|
-
}
|
|
11136
|
-
}, _StructureMapParser_parseRuleContext = function _StructureMapParser_parseRuleContext() {
|
|
11137
|
-
let identifier = this.parser.consume().value;
|
|
11138
|
-
while (this.parser.peek()?.value === '.') {
|
|
11139
|
-
this.parser.consume('.');
|
|
11140
|
-
identifier += '.' + this.parser.consume().value;
|
|
11141
|
-
}
|
|
11142
|
-
return identifier;
|
|
11143
|
-
}, _StructureMapParser_parseRuleDependents = function _StructureMapParser_parseRuleDependents() {
|
|
11144
|
-
const atom = this.parser.consumeAndParse(100 /* OperatorPrecedence.Arrow */);
|
|
11145
|
-
return [
|
|
11146
|
-
{
|
|
11147
|
-
name: atom.name,
|
|
11148
|
-
variable: atom.args.map((arg) => arg.name),
|
|
11149
|
-
},
|
|
11150
|
-
];
|
|
11151
|
-
}, _StructureMapParser_parseConceptMap = function _StructureMapParser_parseConceptMap() {
|
|
11152
|
-
while (this.parser.peek()?.value !== '}') {
|
|
11153
|
-
this.parser.consume();
|
|
11317
|
+
parseConceptMap() {
|
|
11318
|
+
while (this.parser.peek()?.value !== '}') {
|
|
11319
|
+
this.parser.consume();
|
|
11320
|
+
}
|
|
11321
|
+
this.parser.consume('}');
|
|
11154
11322
|
}
|
|
11155
|
-
|
|
11156
|
-
};
|
|
11323
|
+
}
|
|
11157
11324
|
const fhirPathParserBuilder$1 = initFhirPathParserBuilder()
|
|
11158
11325
|
.registerInfix('->', { precedence: 100 /* OperatorPrecedence.Arrow */ })
|
|
11159
11326
|
.registerInfix(';', { precedence: 200 /* OperatorPrecedence.Semicolon */ });
|
|
@@ -11464,7 +11631,6 @@
|
|
|
11464
11631
|
}
|
|
11465
11632
|
}
|
|
11466
11633
|
|
|
11467
|
-
var _FhirSchemaValidator_instances, _FhirSchemaValidator_issues, _FhirSchemaValidator_root, _FhirSchemaValidator_validateObject, _FhirSchemaValidator_checkProperties, _FhirSchemaValidator_checkProperty, _FhirSchemaValidator_checkPropertyValue, _FhirSchemaValidator_validatePrimitiveType, _FhirSchemaValidator_validateString, _FhirSchemaValidator_validateNumber, _FhirSchemaValidator_checkAdditionalProperties, _FhirSchemaValidator_checkAdditionalProperty, _FhirSchemaValidator_checkPrimitiveElement, _FhirSchemaValidator_createIssue;
|
|
11468
11634
|
/*
|
|
11469
11635
|
* This file provides schema validation utilities for FHIR JSON objects.
|
|
11470
11636
|
*
|
|
@@ -11626,14 +11792,11 @@
|
|
|
11626
11792
|
}
|
|
11627
11793
|
class FhirSchemaValidator {
|
|
11628
11794
|
constructor(root) {
|
|
11629
|
-
|
|
11630
|
-
|
|
11631
|
-
_FhirSchemaValidator_root.set(this, void 0);
|
|
11632
|
-
__classPrivateFieldSet(this, _FhirSchemaValidator_issues, [], "f");
|
|
11633
|
-
__classPrivateFieldSet(this, _FhirSchemaValidator_root, root, "f");
|
|
11795
|
+
this.issues = [];
|
|
11796
|
+
this.root = root;
|
|
11634
11797
|
}
|
|
11635
11798
|
validate() {
|
|
11636
|
-
const resource =
|
|
11799
|
+
const resource = this.root;
|
|
11637
11800
|
if (!resource) {
|
|
11638
11801
|
throw new OperationOutcomeError(validationError('Resource is null'));
|
|
11639
11802
|
}
|
|
@@ -11642,142 +11805,172 @@
|
|
|
11642
11805
|
throw new OperationOutcomeError(validationError('Missing resource type'));
|
|
11643
11806
|
}
|
|
11644
11807
|
// Check for "null" once for the entire object hierarchy
|
|
11645
|
-
checkForNull(resource, '',
|
|
11646
|
-
|
|
11647
|
-
if (
|
|
11808
|
+
checkForNull(resource, '', this.issues);
|
|
11809
|
+
this.validateObject(toTypedValue(resource), resourceType);
|
|
11810
|
+
if (this.issues.length > 0) {
|
|
11648
11811
|
throw new OperationOutcomeError({
|
|
11649
11812
|
resourceType: 'OperationOutcome',
|
|
11650
|
-
issue:
|
|
11813
|
+
issue: this.issues,
|
|
11651
11814
|
});
|
|
11652
11815
|
}
|
|
11653
11816
|
}
|
|
11654
|
-
|
|
11655
|
-
|
|
11656
|
-
|
|
11657
|
-
|
|
11658
|
-
|
|
11659
|
-
|
|
11660
|
-
|
|
11661
|
-
|
|
11662
|
-
__classPrivateFieldGet(this, _FhirSchemaValidator_instances, "m", _FhirSchemaValidator_checkAdditionalProperties).call(this, path, typedValue, propertyDefinitions);
|
|
11663
|
-
}, _FhirSchemaValidator_checkProperties = function _FhirSchemaValidator_checkProperties(path, propertyDefinitions, typedValue) {
|
|
11664
|
-
for (const [key, elementDefinition] of Object.entries(propertyDefinitions)) {
|
|
11665
|
-
__classPrivateFieldGet(this, _FhirSchemaValidator_instances, "m", _FhirSchemaValidator_checkProperty).call(this, path + '.' + key, elementDefinition, typedValue);
|
|
11817
|
+
validateObject(typedValue, path) {
|
|
11818
|
+
const definition = globalSchema.types[typedValue.type];
|
|
11819
|
+
if (!definition) {
|
|
11820
|
+
throw new OperationOutcomeError(validationError('Unknown type: ' + typedValue.type));
|
|
11821
|
+
}
|
|
11822
|
+
const propertyDefinitions = definition.properties;
|
|
11823
|
+
this.checkProperties(path, propertyDefinitions, typedValue);
|
|
11824
|
+
this.checkAdditionalProperties(path, typedValue, propertyDefinitions);
|
|
11666
11825
|
}
|
|
11667
|
-
|
|
11668
|
-
|
|
11669
|
-
|
|
11670
|
-
if (isEmpty(value)) {
|
|
11671
|
-
if (elementDefinition.min !== undefined && elementDefinition.min > 0) {
|
|
11672
|
-
__classPrivateFieldGet(this, _FhirSchemaValidator_issues, "f").push(createStructureIssue(path, 'Missing required property'));
|
|
11826
|
+
checkProperties(path, propertyDefinitions, typedValue) {
|
|
11827
|
+
for (const [key, elementDefinition] of Object.entries(propertyDefinitions)) {
|
|
11828
|
+
this.checkProperty(path + '.' + key, elementDefinition, typedValue);
|
|
11673
11829
|
}
|
|
11674
|
-
return;
|
|
11675
11830
|
}
|
|
11676
|
-
|
|
11677
|
-
|
|
11678
|
-
|
|
11831
|
+
checkProperty(path, elementDefinition, typedValue) {
|
|
11832
|
+
const propertyName = path.split('.').pop();
|
|
11833
|
+
const value = getTypedPropertyValue(typedValue, propertyName);
|
|
11834
|
+
if (isEmpty(value)) {
|
|
11835
|
+
if (elementDefinition.min !== undefined && elementDefinition.min > 0) {
|
|
11836
|
+
this.issues.push(createStructureIssue(path, 'Missing required property'));
|
|
11837
|
+
}
|
|
11679
11838
|
return;
|
|
11680
11839
|
}
|
|
11681
|
-
|
|
11682
|
-
|
|
11840
|
+
if (elementDefinition.max === '*') {
|
|
11841
|
+
if (!Array.isArray(value)) {
|
|
11842
|
+
this.issues.push(createStructureIssue(path, 'Expected array for property'));
|
|
11843
|
+
return;
|
|
11844
|
+
}
|
|
11845
|
+
for (const item of value) {
|
|
11846
|
+
this.checkPropertyValue(path, elementDefinition, item);
|
|
11847
|
+
}
|
|
11848
|
+
}
|
|
11849
|
+
else {
|
|
11850
|
+
if (Array.isArray(value)) {
|
|
11851
|
+
this.issues.push(createStructureIssue(path, 'Expected single value for property'));
|
|
11852
|
+
return;
|
|
11853
|
+
}
|
|
11854
|
+
this.checkPropertyValue(path, elementDefinition, value);
|
|
11683
11855
|
}
|
|
11684
11856
|
}
|
|
11685
|
-
|
|
11686
|
-
if (
|
|
11687
|
-
|
|
11857
|
+
checkPropertyValue(path, elementDefinition, typedValue) {
|
|
11858
|
+
if (typedValue.value === null) {
|
|
11859
|
+
// Null handled separately
|
|
11688
11860
|
return;
|
|
11689
11861
|
}
|
|
11690
|
-
|
|
11691
|
-
|
|
11692
|
-
|
|
11693
|
-
|
|
11694
|
-
|
|
11695
|
-
|
|
11696
|
-
}
|
|
11697
|
-
if (isLowerCase(typedValue.type.charAt(0))) {
|
|
11698
|
-
__classPrivateFieldGet(this, _FhirSchemaValidator_instances, "m", _FhirSchemaValidator_validatePrimitiveType).call(this, elementDefinition, typedValue);
|
|
11699
|
-
}
|
|
11700
|
-
else {
|
|
11701
|
-
__classPrivateFieldGet(this, _FhirSchemaValidator_instances, "m", _FhirSchemaValidator_validateObject).call(this, typedValue, path);
|
|
11702
|
-
}
|
|
11703
|
-
}, _FhirSchemaValidator_validatePrimitiveType = function _FhirSchemaValidator_validatePrimitiveType(elementDefinition, typedValue) {
|
|
11704
|
-
const { type, value } = typedValue;
|
|
11705
|
-
if (value === null) {
|
|
11706
|
-
// Null handled separately, so this code should never be reached
|
|
11707
|
-
// Leaving this check in place for now, in case we change the null handling
|
|
11708
|
-
return;
|
|
11709
|
-
}
|
|
11710
|
-
// First, make sure the value is the correct JS type
|
|
11711
|
-
const expectedType = fhirTypeToJsType[typedValue.type];
|
|
11712
|
-
if (typeof value !== expectedType) {
|
|
11713
|
-
__classPrivateFieldGet(this, _FhirSchemaValidator_instances, "m", _FhirSchemaValidator_createIssue).call(this, elementDefinition, 'Invalid type for ' + type);
|
|
11714
|
-
return;
|
|
11715
|
-
}
|
|
11716
|
-
// Then, perform additional checks for specialty types
|
|
11717
|
-
if (expectedType === 'string') {
|
|
11718
|
-
__classPrivateFieldGet(this, _FhirSchemaValidator_instances, "m", _FhirSchemaValidator_validateString).call(this, elementDefinition, type, value);
|
|
11719
|
-
}
|
|
11720
|
-
else if (expectedType === 'number') {
|
|
11721
|
-
__classPrivateFieldGet(this, _FhirSchemaValidator_instances, "m", _FhirSchemaValidator_validateNumber).call(this, elementDefinition, type, value);
|
|
11862
|
+
if (isLowerCase(typedValue.type.charAt(0))) {
|
|
11863
|
+
this.validatePrimitiveType(elementDefinition, typedValue);
|
|
11864
|
+
}
|
|
11865
|
+
else {
|
|
11866
|
+
this.validateObject(typedValue, path);
|
|
11867
|
+
}
|
|
11722
11868
|
}
|
|
11723
|
-
|
|
11724
|
-
|
|
11725
|
-
|
|
11726
|
-
|
|
11869
|
+
validatePrimitiveType(elementDefinition, typedValue) {
|
|
11870
|
+
const { type, value } = typedValue;
|
|
11871
|
+
if (value === null) {
|
|
11872
|
+
// Null handled separately, so this code should never be reached
|
|
11873
|
+
// Leaving this check in place for now, in case we change the null handling
|
|
11874
|
+
return;
|
|
11875
|
+
}
|
|
11876
|
+
// First, make sure the value is the correct JS type
|
|
11877
|
+
const expectedType = fhirTypeToJsType[typedValue.type];
|
|
11878
|
+
if (typeof value !== expectedType) {
|
|
11879
|
+
this.createIssue(elementDefinition, 'Invalid type for ' + type);
|
|
11880
|
+
return;
|
|
11881
|
+
}
|
|
11882
|
+
// Then, perform additional checks for specialty types
|
|
11883
|
+
if (expectedType === 'string') {
|
|
11884
|
+
this.validateString(elementDefinition, type, value);
|
|
11885
|
+
}
|
|
11886
|
+
else if (expectedType === 'number') {
|
|
11887
|
+
this.validateNumber(elementDefinition, type, value);
|
|
11888
|
+
}
|
|
11727
11889
|
}
|
|
11728
|
-
|
|
11729
|
-
|
|
11730
|
-
|
|
11731
|
-
|
|
11732
|
-
|
|
11733
|
-
|
|
11734
|
-
|
|
11890
|
+
validateString(elementDefinition, type, value) {
|
|
11891
|
+
if (!value.trim()) {
|
|
11892
|
+
this.createIssue(elementDefinition, 'Invalid empty string');
|
|
11893
|
+
return;
|
|
11894
|
+
}
|
|
11895
|
+
// Try to get the regex
|
|
11896
|
+
const valueDefinition = globalSchema.types[type]?.properties?.['value'];
|
|
11897
|
+
if (valueDefinition?.type) {
|
|
11898
|
+
const regex = getExtensionValue(valueDefinition.type[0], 'http://hl7.org/fhir/StructureDefinition/regex');
|
|
11899
|
+
if (regex) {
|
|
11900
|
+
if (!value.match(new RegExp(regex))) {
|
|
11901
|
+
this.createIssue(elementDefinition, 'Invalid ' + type + ' format');
|
|
11902
|
+
}
|
|
11735
11903
|
}
|
|
11736
11904
|
}
|
|
11737
11905
|
}
|
|
11738
|
-
|
|
11739
|
-
|
|
11740
|
-
|
|
11741
|
-
|
|
11742
|
-
|
|
11743
|
-
|
|
11744
|
-
|
|
11745
|
-
|
|
11746
|
-
|
|
11747
|
-
|
|
11748
|
-
|
|
11749
|
-
|
|
11750
|
-
|
|
11906
|
+
validateNumber(elementDefinition, type, value) {
|
|
11907
|
+
if (isNaN(value) || !isFinite(value)) {
|
|
11908
|
+
this.createIssue(elementDefinition, 'Invalid ' + type + ' value');
|
|
11909
|
+
return;
|
|
11910
|
+
}
|
|
11911
|
+
if (isIntegerType(type) && !Number.isInteger(value)) {
|
|
11912
|
+
this.createIssue(elementDefinition, 'Number is not an integer');
|
|
11913
|
+
}
|
|
11914
|
+
if (type === exports.PropertyType.positiveInt && value <= 0) {
|
|
11915
|
+
this.createIssue(elementDefinition, 'Number is less than or equal to zero');
|
|
11916
|
+
}
|
|
11917
|
+
if (type === exports.PropertyType.unsignedInt && value < 0) {
|
|
11918
|
+
this.createIssue(elementDefinition, 'Number is negative');
|
|
11919
|
+
}
|
|
11751
11920
|
}
|
|
11752
|
-
|
|
11753
|
-
|
|
11754
|
-
|
|
11755
|
-
|
|
11921
|
+
checkAdditionalProperties(path, typedValue, propertyDefinitions) {
|
|
11922
|
+
const object = typedValue.value;
|
|
11923
|
+
for (const key of Object.keys(object)) {
|
|
11924
|
+
this.checkAdditionalProperty(path, key, typedValue, propertyDefinitions);
|
|
11925
|
+
}
|
|
11756
11926
|
}
|
|
11757
|
-
|
|
11758
|
-
|
|
11759
|
-
|
|
11760
|
-
|
|
11761
|
-
|
|
11762
|
-
|
|
11763
|
-
|
|
11927
|
+
/**
|
|
11928
|
+
* Checks if the given property is allowed on the given object.
|
|
11929
|
+
* @param path The path of the current object.
|
|
11930
|
+
* @param key The key of a property to check.
|
|
11931
|
+
* @param typedValue The current object.
|
|
11932
|
+
* @param propertyDefinitions The property definitions of the current object.
|
|
11933
|
+
*/
|
|
11934
|
+
checkAdditionalProperty(path, key, typedValue, propertyDefinitions) {
|
|
11935
|
+
if (!baseResourceProperties.has(key) &&
|
|
11936
|
+
!(key in propertyDefinitions) &&
|
|
11937
|
+
!isChoiceOfType(key, typedValue, propertyDefinitions) &&
|
|
11938
|
+
!this.checkPrimitiveElement(path, key, typedValue)) {
|
|
11939
|
+
const expression = `${path}.${key}`;
|
|
11940
|
+
this.issues.push(createStructureIssue(expression, `Invalid additional property "${expression}"`));
|
|
11941
|
+
}
|
|
11764
11942
|
}
|
|
11765
|
-
|
|
11766
|
-
|
|
11767
|
-
|
|
11768
|
-
|
|
11943
|
+
/**
|
|
11944
|
+
* Checks the element for a primitive.
|
|
11945
|
+
*
|
|
11946
|
+
* FHIR elements with primitive data types are represented in two parts:
|
|
11947
|
+
* 1) A JSON property with the name of the element, which has a JSON type of number, boolean, or string
|
|
11948
|
+
* 2) a JSON property with _ prepended to the name of the element, which, if present, contains the value's id and/or extensions
|
|
11949
|
+
*
|
|
11950
|
+
* See: https://hl7.org/fhir/json.html#primitive
|
|
11951
|
+
*
|
|
11952
|
+
* @param path The path to the property
|
|
11953
|
+
* @param key
|
|
11954
|
+
* @param typedValue
|
|
11955
|
+
*/
|
|
11956
|
+
checkPrimitiveElement(path, key, typedValue) {
|
|
11957
|
+
// Primitive element starts with underscore
|
|
11958
|
+
if (!key.startsWith('_')) {
|
|
11959
|
+
return false;
|
|
11960
|
+
}
|
|
11961
|
+
// Validate the non-underscore property exists
|
|
11962
|
+
const primitiveKey = key.slice(1);
|
|
11963
|
+
if (!(primitiveKey in typedValue.value)) {
|
|
11964
|
+
return false;
|
|
11965
|
+
}
|
|
11966
|
+
// Then validate the element
|
|
11967
|
+
this.validateObject({ type: 'Element', value: typedValue.value[key] }, path);
|
|
11968
|
+
return true;
|
|
11769
11969
|
}
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
if (!(primitiveKey in typedValue.value)) {
|
|
11773
|
-
return false;
|
|
11970
|
+
createIssue(elementDefinition, message) {
|
|
11971
|
+
this.issues.push(createStructureIssue(elementDefinition.path, message));
|
|
11774
11972
|
}
|
|
11775
|
-
|
|
11776
|
-
__classPrivateFieldGet(this, _FhirSchemaValidator_instances, "m", _FhirSchemaValidator_validateObject).call(this, { type: 'Element', value: typedValue.value[key] }, path);
|
|
11777
|
-
return true;
|
|
11778
|
-
}, _FhirSchemaValidator_createIssue = function _FhirSchemaValidator_createIssue(elementDefinition, message) {
|
|
11779
|
-
__classPrivateFieldGet(this, _FhirSchemaValidator_issues, "f").push(createStructureIssue(elementDefinition.path, message));
|
|
11780
|
-
};
|
|
11973
|
+
}
|
|
11781
11974
|
function isIntegerType(propertyType) {
|
|
11782
11975
|
return (propertyType === exports.PropertyType.integer ||
|
|
11783
11976
|
propertyType === exports.PropertyType.positiveInt ||
|
|
@@ -12526,6 +12719,7 @@
|
|
|
12526
12719
|
exports.calculateAgeString = calculateAgeString;
|
|
12527
12720
|
exports.capitalize = capitalize;
|
|
12528
12721
|
exports.checkForNull = checkForNull;
|
|
12722
|
+
exports.convertToTransactionBundle = convertToTransactionBundle;
|
|
12529
12723
|
exports.createReference = createReference;
|
|
12530
12724
|
exports.createStructureIssue = createStructureIssue;
|
|
12531
12725
|
exports.created = created;
|