@medplum/core 2.0.14 → 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 +1155 -1060
- 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/cache.mjs +17 -23
- package/dist/esm/cache.mjs.map +1 -1
- package/dist/esm/client.mjs +464 -395
- 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 +0 -1
- package/dist/esm/fhirmapper/tokenize.mjs.map +1 -1
- package/dist/esm/fhirpath/atoms.mjs +0 -1
- 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 +0 -2
- package/dist/esm/filter/parse.mjs.map +1 -1
- package/dist/esm/filter/tokenize.mjs +0 -1
- package/dist/esm/filter/tokenize.mjs.map +1 -1
- package/dist/esm/index.min.mjs +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 +146 -123
- package/dist/esm/schema.mjs.map +1 -1
- package/dist/esm/search/match.mjs +0 -2
- 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/cache.d.ts +3 -1
- package/dist/types/client.d.ts +127 -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/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
|
@@ -38,52 +38,20 @@
|
|
|
38
38
|
return value;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
/******************************************************************************
|
|
42
|
-
Copyright (c) Microsoft Corporation.
|
|
43
|
-
|
|
44
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
45
|
-
purpose with or without fee is hereby granted.
|
|
46
|
-
|
|
47
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
48
|
-
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
49
|
-
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
50
|
-
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
51
|
-
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
52
|
-
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
53
|
-
PERFORMANCE OF THIS SOFTWARE.
|
|
54
|
-
***************************************************************************** */
|
|
55
|
-
|
|
56
|
-
function __classPrivateFieldGet(receiver, state, kind, f) {
|
|
57
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
58
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
59
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function __classPrivateFieldSet(receiver, state, value, kind, f) {
|
|
63
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
64
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
65
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
66
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
var _LRUCache_instances, _LRUCache_max, _LRUCache_cache, _LRUCache_first;
|
|
70
41
|
/**
|
|
71
42
|
* LRU cache (least recently used)
|
|
72
43
|
* Source: https://stackoverflow.com/a/46432113
|
|
73
44
|
*/
|
|
74
45
|
class LRUCache {
|
|
75
46
|
constructor(max = 10) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
_LRUCache_cache.set(this, void 0);
|
|
79
|
-
__classPrivateFieldSet(this, _LRUCache_max, max, "f");
|
|
80
|
-
__classPrivateFieldSet(this, _LRUCache_cache, new Map(), "f");
|
|
47
|
+
this.max = max;
|
|
48
|
+
this.cache = new Map();
|
|
81
49
|
}
|
|
82
50
|
/**
|
|
83
51
|
* Deletes all values from the cache.
|
|
84
52
|
*/
|
|
85
53
|
clear() {
|
|
86
|
-
|
|
54
|
+
this.cache.clear();
|
|
87
55
|
}
|
|
88
56
|
/**
|
|
89
57
|
* Returns the value for the given key.
|
|
@@ -91,10 +59,10 @@
|
|
|
91
59
|
* @returns The value if found; undefined otherwise.
|
|
92
60
|
*/
|
|
93
61
|
get(key) {
|
|
94
|
-
const item =
|
|
62
|
+
const item = this.cache.get(key);
|
|
95
63
|
if (item) {
|
|
96
|
-
|
|
97
|
-
|
|
64
|
+
this.cache.delete(key);
|
|
65
|
+
this.cache.set(key, item);
|
|
98
66
|
}
|
|
99
67
|
return item;
|
|
100
68
|
}
|
|
@@ -104,33 +72,33 @@
|
|
|
104
72
|
* @param val The value to set.
|
|
105
73
|
*/
|
|
106
74
|
set(key, val) {
|
|
107
|
-
if (
|
|
108
|
-
|
|
75
|
+
if (this.cache.has(key)) {
|
|
76
|
+
this.cache.delete(key);
|
|
109
77
|
}
|
|
110
|
-
else if (
|
|
111
|
-
|
|
78
|
+
else if (this.cache.size >= this.max) {
|
|
79
|
+
this.cache.delete(this.first());
|
|
112
80
|
}
|
|
113
|
-
|
|
81
|
+
this.cache.set(key, val);
|
|
114
82
|
}
|
|
115
83
|
/**
|
|
116
84
|
* Deletes the value for the given key.
|
|
117
85
|
* @param key The key to delete.
|
|
118
86
|
*/
|
|
119
87
|
delete(key) {
|
|
120
|
-
|
|
88
|
+
this.cache.delete(key);
|
|
121
89
|
}
|
|
122
90
|
/**
|
|
123
91
|
* Returns the list of all keys in the cache.
|
|
124
92
|
* @returns The array of keys in the cache.
|
|
125
93
|
*/
|
|
126
94
|
keys() {
|
|
127
|
-
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;
|
|
128
100
|
}
|
|
129
101
|
}
|
|
130
|
-
_LRUCache_max = new WeakMap(), _LRUCache_cache = new WeakMap(), _LRUCache_instances = new WeakSet(), _LRUCache_first = function _LRUCache_first() {
|
|
131
|
-
// This works because the Map class maintains ordered keys.
|
|
132
|
-
return __classPrivateFieldGet(this, _LRUCache_cache, "f").keys().next().value;
|
|
133
|
-
};
|
|
134
102
|
|
|
135
103
|
/**
|
|
136
104
|
* Formats a FHIR Address as a string.
|
|
@@ -1141,20 +1109,18 @@
|
|
|
1141
1109
|
/*
|
|
1142
1110
|
* Based on: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget
|
|
1143
1111
|
*/
|
|
1144
|
-
var _EventTarget_listeners;
|
|
1145
1112
|
class EventTarget {
|
|
1146
1113
|
constructor() {
|
|
1147
|
-
|
|
1148
|
-
__classPrivateFieldSet(this, _EventTarget_listeners, {}, "f");
|
|
1114
|
+
this.listeners = {};
|
|
1149
1115
|
}
|
|
1150
1116
|
addEventListener(type, callback) {
|
|
1151
|
-
if (!
|
|
1152
|
-
|
|
1117
|
+
if (!this.listeners[type]) {
|
|
1118
|
+
this.listeners[type] = [];
|
|
1153
1119
|
}
|
|
1154
|
-
|
|
1120
|
+
this.listeners[type].push(callback);
|
|
1155
1121
|
}
|
|
1156
1122
|
removeEventListeneer(type, callback) {
|
|
1157
|
-
const array =
|
|
1123
|
+
const array = this.listeners[type];
|
|
1158
1124
|
if (!array) {
|
|
1159
1125
|
return;
|
|
1160
1126
|
}
|
|
@@ -1166,14 +1132,44 @@
|
|
|
1166
1132
|
}
|
|
1167
1133
|
}
|
|
1168
1134
|
dispatchEvent(event) {
|
|
1169
|
-
const array =
|
|
1135
|
+
const array = this.listeners[event.type];
|
|
1170
1136
|
if (array) {
|
|
1171
1137
|
array.forEach((listener) => listener.call(this, event));
|
|
1172
1138
|
}
|
|
1173
1139
|
return !event.defaultPrevented;
|
|
1174
1140
|
}
|
|
1175
1141
|
}
|
|
1176
|
-
|
|
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
|
+
}
|
|
1177
1173
|
|
|
1178
1174
|
/**
|
|
1179
1175
|
* Decodes a section of a JWT.
|
|
@@ -1190,15 +1186,6 @@
|
|
|
1190
1186
|
const jsonPayload = decodeURIComponent(uriEncodedPayload);
|
|
1191
1187
|
return JSON.parse(jsonPayload);
|
|
1192
1188
|
}
|
|
1193
|
-
function decodeBase64(data) {
|
|
1194
|
-
if (typeof window !== 'undefined') {
|
|
1195
|
-
return window.atob(data);
|
|
1196
|
-
}
|
|
1197
|
-
if (typeof Buffer !== 'undefined') {
|
|
1198
|
-
return Buffer.from(data, 'base64').toString('binary');
|
|
1199
|
-
}
|
|
1200
|
-
throw new Error('Unable to decode base64');
|
|
1201
|
-
}
|
|
1202
1189
|
/**
|
|
1203
1190
|
* Parses the JWT payload.
|
|
1204
1191
|
* @param token JWT token
|
|
@@ -1460,7 +1447,7 @@
|
|
|
1460
1447
|
return strs.length > 0 ? strs.join('; ') : 'Unknown error';
|
|
1461
1448
|
}
|
|
1462
1449
|
|
|
1463
|
-
var
|
|
1450
|
+
var _a;
|
|
1464
1451
|
/**
|
|
1465
1452
|
* The ReadablePromise class wraps a request promise suitable for React Suspense.
|
|
1466
1453
|
* See: https://blog.logrocket.com/react-suspense-data-fetching/#wrappromise-js
|
|
@@ -1469,33 +1456,30 @@
|
|
|
1469
1456
|
class ReadablePromise {
|
|
1470
1457
|
constructor(requestPromise) {
|
|
1471
1458
|
this[_a] = 'ReadablePromise';
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
__classPrivateFieldSet(this, _ReadablePromise_suspender, requestPromise.then((res) => {
|
|
1477
|
-
__classPrivateFieldSet(this, _ReadablePromise_status, 'success', "f");
|
|
1478
|
-
__classPrivateFieldSet(this, _ReadablePromise_response, res, "f");
|
|
1459
|
+
this.status = 'pending';
|
|
1460
|
+
this.suspender = requestPromise.then((res) => {
|
|
1461
|
+
this.status = 'success';
|
|
1462
|
+
this.response = res;
|
|
1479
1463
|
return res;
|
|
1480
1464
|
}, (err) => {
|
|
1481
|
-
|
|
1482
|
-
|
|
1465
|
+
this.status = 'error';
|
|
1466
|
+
this.error = err;
|
|
1483
1467
|
throw err;
|
|
1484
|
-
})
|
|
1468
|
+
});
|
|
1485
1469
|
}
|
|
1486
1470
|
/**
|
|
1487
1471
|
* Returns true if the promise is pending.
|
|
1488
1472
|
* @returns True if the Promise is pending.
|
|
1489
1473
|
*/
|
|
1490
1474
|
isPending() {
|
|
1491
|
-
return
|
|
1475
|
+
return this.status === 'pending';
|
|
1492
1476
|
}
|
|
1493
1477
|
/**
|
|
1494
1478
|
* Returns true if the promise resolved successfully.
|
|
1495
1479
|
* @returns True if the Promise resolved successfully.
|
|
1496
1480
|
*/
|
|
1497
1481
|
isOk() {
|
|
1498
|
-
return
|
|
1482
|
+
return this.status === 'success';
|
|
1499
1483
|
}
|
|
1500
1484
|
/**
|
|
1501
1485
|
* Attempts to read the value of the promise.
|
|
@@ -1505,13 +1489,13 @@
|
|
|
1505
1489
|
* @returns The resolved value of the Promise.
|
|
1506
1490
|
*/
|
|
1507
1491
|
read() {
|
|
1508
|
-
switch (
|
|
1492
|
+
switch (this.status) {
|
|
1509
1493
|
case 'pending':
|
|
1510
|
-
throw
|
|
1494
|
+
throw this.suspender;
|
|
1511
1495
|
case 'error':
|
|
1512
|
-
throw
|
|
1496
|
+
throw this.error;
|
|
1513
1497
|
default:
|
|
1514
|
-
return
|
|
1498
|
+
return this.response;
|
|
1515
1499
|
}
|
|
1516
1500
|
}
|
|
1517
1501
|
/**
|
|
@@ -1521,7 +1505,7 @@
|
|
|
1521
1505
|
* @returns A Promise for the completion of which ever callback is executed.
|
|
1522
1506
|
*/
|
|
1523
1507
|
then(onfulfilled, onrejected) {
|
|
1524
|
-
return
|
|
1508
|
+
return this.suspender.then(onfulfilled, onrejected);
|
|
1525
1509
|
}
|
|
1526
1510
|
/**
|
|
1527
1511
|
* Attaches a callback for only the rejection of the Promise.
|
|
@@ -1529,7 +1513,7 @@
|
|
|
1529
1513
|
* @returns A Promise for the completion of the callback.
|
|
1530
1514
|
*/
|
|
1531
1515
|
catch(onrejected) {
|
|
1532
|
-
return
|
|
1516
|
+
return this.suspender.catch(onrejected);
|
|
1533
1517
|
}
|
|
1534
1518
|
/**
|
|
1535
1519
|
* Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The
|
|
@@ -1538,12 +1522,11 @@
|
|
|
1538
1522
|
* @returns A Promise for the completion of the callback.
|
|
1539
1523
|
*/
|
|
1540
1524
|
finally(onfinally) {
|
|
1541
|
-
return
|
|
1525
|
+
return this.suspender.finally(onfinally);
|
|
1542
1526
|
}
|
|
1543
1527
|
}
|
|
1544
|
-
|
|
1528
|
+
_a = Symbol.toStringTag;
|
|
1545
1529
|
|
|
1546
|
-
var _ClientStorage_storage, _MemoryStorage_data;
|
|
1547
1530
|
/**
|
|
1548
1531
|
* The ClientStorage class is a utility class for storing strings and objects.
|
|
1549
1532
|
*
|
|
@@ -1553,21 +1536,20 @@
|
|
|
1553
1536
|
*/
|
|
1554
1537
|
class ClientStorage {
|
|
1555
1538
|
constructor() {
|
|
1556
|
-
|
|
1557
|
-
__classPrivateFieldSet(this, _ClientStorage_storage, typeof localStorage !== 'undefined' ? localStorage : new MemoryStorage(), "f");
|
|
1539
|
+
this.storage = typeof localStorage !== 'undefined' ? localStorage : new MemoryStorage();
|
|
1558
1540
|
}
|
|
1559
1541
|
clear() {
|
|
1560
|
-
|
|
1542
|
+
this.storage.clear();
|
|
1561
1543
|
}
|
|
1562
1544
|
getString(key) {
|
|
1563
|
-
return
|
|
1545
|
+
return this.storage.getItem(key) || undefined;
|
|
1564
1546
|
}
|
|
1565
1547
|
setString(key, value) {
|
|
1566
1548
|
if (value) {
|
|
1567
|
-
|
|
1549
|
+
this.storage.setItem(key, value);
|
|
1568
1550
|
}
|
|
1569
1551
|
else {
|
|
1570
|
-
|
|
1552
|
+
this.storage.removeItem(key);
|
|
1571
1553
|
}
|
|
1572
1554
|
}
|
|
1573
1555
|
getObject(key) {
|
|
@@ -1578,58 +1560,55 @@
|
|
|
1578
1560
|
this.setString(key, value ? stringify(value) : undefined);
|
|
1579
1561
|
}
|
|
1580
1562
|
}
|
|
1581
|
-
_ClientStorage_storage = new WeakMap();
|
|
1582
1563
|
/**
|
|
1583
1564
|
* The MemoryStorage class is a minimal in-memory implementation of the Storage interface.
|
|
1584
1565
|
*/
|
|
1585
1566
|
class MemoryStorage {
|
|
1586
1567
|
constructor() {
|
|
1587
|
-
|
|
1588
|
-
__classPrivateFieldSet(this, _MemoryStorage_data, new Map(), "f");
|
|
1568
|
+
this.data = new Map();
|
|
1589
1569
|
}
|
|
1590
1570
|
/**
|
|
1591
1571
|
* Returns the number of key/value pairs.
|
|
1592
1572
|
*/
|
|
1593
1573
|
get length() {
|
|
1594
|
-
return
|
|
1574
|
+
return this.data.size;
|
|
1595
1575
|
}
|
|
1596
1576
|
/**
|
|
1597
1577
|
* Removes all key/value pairs, if there are any.
|
|
1598
1578
|
*/
|
|
1599
1579
|
clear() {
|
|
1600
|
-
|
|
1580
|
+
this.data.clear();
|
|
1601
1581
|
}
|
|
1602
1582
|
/**
|
|
1603
1583
|
* Returns the current value associated with the given key, or null if the given key does not exist.
|
|
1604
1584
|
*/
|
|
1605
1585
|
getItem(key) {
|
|
1606
|
-
return
|
|
1586
|
+
return this.data.get(key) ?? null;
|
|
1607
1587
|
}
|
|
1608
1588
|
/**
|
|
1609
1589
|
* Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
|
|
1610
1590
|
*/
|
|
1611
1591
|
setItem(key, value) {
|
|
1612
1592
|
if (value) {
|
|
1613
|
-
|
|
1593
|
+
this.data.set(key, value);
|
|
1614
1594
|
}
|
|
1615
1595
|
else {
|
|
1616
|
-
|
|
1596
|
+
this.data.delete(key);
|
|
1617
1597
|
}
|
|
1618
1598
|
}
|
|
1619
1599
|
/**
|
|
1620
1600
|
* Removes the key/value pair with the given key, if a key/value pair with the given key exists.
|
|
1621
1601
|
*/
|
|
1622
1602
|
removeItem(key) {
|
|
1623
|
-
|
|
1603
|
+
this.data.delete(key);
|
|
1624
1604
|
}
|
|
1625
1605
|
/**
|
|
1626
1606
|
* Returns the name of the nth key, or null if n is greater than or equal to the number of key/value pairs.
|
|
1627
1607
|
*/
|
|
1628
1608
|
key(index) {
|
|
1629
|
-
return Array.from(
|
|
1609
|
+
return Array.from(this.data.keys())[index];
|
|
1630
1610
|
}
|
|
1631
1611
|
}
|
|
1632
|
-
_MemoryStorage_data = new WeakMap();
|
|
1633
1612
|
|
|
1634
1613
|
var types = {
|
|
1635
1614
|
Element: {
|
|
@@ -6402,9 +6381,7 @@
|
|
|
6402
6381
|
const globalSchema = baseSchema;
|
|
6403
6382
|
|
|
6404
6383
|
// PKCE auth based on:
|
|
6405
|
-
|
|
6406
|
-
var _MedplumClient_instances, _MedplumClient_fetch, _MedplumClient_createPdf, _MedplumClient_storage, _MedplumClient_requestCache, _MedplumClient_cacheTime, _MedplumClient_baseUrl, _MedplumClient_fhirBaseUrl, _MedplumClient_authorizeUrl, _MedplumClient_tokenUrl, _MedplumClient_logoutUrl, _MedplumClient_exchangeUrl, _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;
|
|
6407
|
-
const MEDPLUM_VERSION = "2.0.14-8c7457fd";
|
|
6384
|
+
const MEDPLUM_VERSION = "2.0.15-025c3c04";
|
|
6408
6385
|
const DEFAULT_BASE_URL = 'https://api.medplum.com/';
|
|
6409
6386
|
const DEFAULT_RESOURCE_CACHE_SIZE = 1000;
|
|
6410
6387
|
const DEFAULT_CACHE_TIME = 60000; // 60 seconds
|
|
@@ -6467,57 +6444,44 @@
|
|
|
6467
6444
|
class MedplumClient extends EventTarget {
|
|
6468
6445
|
constructor(options) {
|
|
6469
6446
|
super();
|
|
6470
|
-
_MedplumClient_instances.add(this);
|
|
6471
|
-
_MedplumClient_fetch.set(this, void 0);
|
|
6472
|
-
_MedplumClient_createPdf.set(this, void 0);
|
|
6473
|
-
_MedplumClient_storage.set(this, void 0);
|
|
6474
|
-
_MedplumClient_requestCache.set(this, void 0);
|
|
6475
|
-
_MedplumClient_cacheTime.set(this, void 0);
|
|
6476
|
-
_MedplumClient_baseUrl.set(this, void 0);
|
|
6477
|
-
_MedplumClient_fhirBaseUrl.set(this, void 0);
|
|
6478
|
-
_MedplumClient_authorizeUrl.set(this, void 0);
|
|
6479
|
-
_MedplumClient_tokenUrl.set(this, void 0);
|
|
6480
|
-
_MedplumClient_logoutUrl.set(this, void 0);
|
|
6481
|
-
_MedplumClient_exchangeUrl.set(this, void 0);
|
|
6482
|
-
_MedplumClient_onUnauthenticated.set(this, void 0);
|
|
6483
|
-
_MedplumClient_autoBatchTime.set(this, void 0);
|
|
6484
|
-
_MedplumClient_autoBatchQueue.set(this, void 0);
|
|
6485
|
-
_MedplumClient_clientId.set(this, void 0);
|
|
6486
|
-
_MedplumClient_clientSecret.set(this, void 0);
|
|
6487
|
-
_MedplumClient_autoBatchTimerId.set(this, void 0);
|
|
6488
|
-
_MedplumClient_accessToken.set(this, void 0);
|
|
6489
|
-
_MedplumClient_refreshToken.set(this, void 0);
|
|
6490
|
-
_MedplumClient_refreshPromise.set(this, void 0);
|
|
6491
|
-
_MedplumClient_profilePromise.set(this, void 0);
|
|
6492
|
-
_MedplumClient_profile.set(this, void 0);
|
|
6493
|
-
_MedplumClient_config.set(this, void 0);
|
|
6494
6447
|
if (options?.baseUrl) {
|
|
6495
6448
|
if (!options.baseUrl.startsWith('http')) {
|
|
6496
6449
|
throw new Error('Base URL must start with http or https');
|
|
6497
6450
|
}
|
|
6498
6451
|
}
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
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
|
+
}
|
|
6514
6478
|
const activeLogin = this.getActiveLogin();
|
|
6515
6479
|
if (activeLogin) {
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6480
|
+
this.accessToken = activeLogin.accessToken;
|
|
6481
|
+
this.refreshToken = activeLogin.refreshToken;
|
|
6482
|
+
this.refreshProfile().catch(console.log);
|
|
6519
6483
|
}
|
|
6520
|
-
|
|
6484
|
+
this.setupStorageListener();
|
|
6521
6485
|
}
|
|
6522
6486
|
/**
|
|
6523
6487
|
* Returns the current base URL for all API requests.
|
|
@@ -6527,14 +6491,14 @@
|
|
|
6527
6491
|
* @returns The current base URL for all API requests.
|
|
6528
6492
|
*/
|
|
6529
6493
|
getBaseUrl() {
|
|
6530
|
-
return
|
|
6494
|
+
return this.baseUrl;
|
|
6531
6495
|
}
|
|
6532
6496
|
/**
|
|
6533
6497
|
* Clears all auth state including local storage and session storage.
|
|
6534
6498
|
* @category Authentication
|
|
6535
6499
|
*/
|
|
6536
6500
|
clear() {
|
|
6537
|
-
|
|
6501
|
+
this.storage.clear();
|
|
6538
6502
|
this.clearActiveLogin();
|
|
6539
6503
|
}
|
|
6540
6504
|
/**
|
|
@@ -6543,12 +6507,12 @@
|
|
|
6543
6507
|
* @category Authentication
|
|
6544
6508
|
*/
|
|
6545
6509
|
clearActiveLogin() {
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
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;
|
|
6552
6516
|
this.dispatchEvent({ type: 'change' });
|
|
6553
6517
|
}
|
|
6554
6518
|
/**
|
|
@@ -6558,7 +6522,7 @@
|
|
|
6558
6522
|
*/
|
|
6559
6523
|
invalidateUrl(url) {
|
|
6560
6524
|
url = url.toString();
|
|
6561
|
-
|
|
6525
|
+
this.requestCache?.delete(url);
|
|
6562
6526
|
}
|
|
6563
6527
|
/**
|
|
6564
6528
|
* Invalidates all cached search results or cached requests for the given resourceType.
|
|
@@ -6567,9 +6531,11 @@
|
|
|
6567
6531
|
*/
|
|
6568
6532
|
invalidateSearches(resourceType) {
|
|
6569
6533
|
const url = 'fhir/R4/' + resourceType;
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
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
|
+
}
|
|
6573
6539
|
}
|
|
6574
6540
|
}
|
|
6575
6541
|
}
|
|
@@ -6587,30 +6553,30 @@
|
|
|
6587
6553
|
*/
|
|
6588
6554
|
get(url, options = {}) {
|
|
6589
6555
|
url = url.toString();
|
|
6590
|
-
const cached =
|
|
6556
|
+
const cached = this.getCacheEntry(url, options);
|
|
6591
6557
|
if (cached) {
|
|
6592
6558
|
return cached.value;
|
|
6593
6559
|
}
|
|
6594
6560
|
let promise;
|
|
6595
|
-
if (url.startsWith(
|
|
6561
|
+
if (url.startsWith(this.fhirBaseUrl) && this.autoBatchQueue) {
|
|
6596
6562
|
promise = new Promise((resolve, reject) => {
|
|
6597
|
-
|
|
6563
|
+
this.autoBatchQueue.push({
|
|
6598
6564
|
method: 'GET',
|
|
6599
|
-
url: url.replace(
|
|
6565
|
+
url: url.replace(this.fhirBaseUrl, ''),
|
|
6600
6566
|
options,
|
|
6601
6567
|
resolve,
|
|
6602
6568
|
reject,
|
|
6603
6569
|
});
|
|
6604
|
-
if (!
|
|
6605
|
-
|
|
6570
|
+
if (!this.autoBatchTimerId) {
|
|
6571
|
+
this.autoBatchTimerId = setTimeout(() => this.executeAutoBatch(), this.autoBatchTime);
|
|
6606
6572
|
}
|
|
6607
6573
|
});
|
|
6608
6574
|
}
|
|
6609
6575
|
else {
|
|
6610
|
-
promise =
|
|
6576
|
+
promise = this.request('GET', url, options);
|
|
6611
6577
|
}
|
|
6612
6578
|
const readablePromise = new ReadablePromise(promise);
|
|
6613
|
-
|
|
6579
|
+
this.setCacheEntry(url, readablePromise);
|
|
6614
6580
|
return readablePromise;
|
|
6615
6581
|
}
|
|
6616
6582
|
/**
|
|
@@ -6630,13 +6596,13 @@
|
|
|
6630
6596
|
post(url, body, contentType, options = {}) {
|
|
6631
6597
|
url = url.toString();
|
|
6632
6598
|
if (body) {
|
|
6633
|
-
|
|
6599
|
+
this.setRequestBody(options, body);
|
|
6634
6600
|
}
|
|
6635
6601
|
if (contentType) {
|
|
6636
|
-
|
|
6602
|
+
this.setRequestContentType(options, contentType);
|
|
6637
6603
|
}
|
|
6638
6604
|
this.invalidateUrl(url);
|
|
6639
|
-
return
|
|
6605
|
+
return this.request('POST', url, options);
|
|
6640
6606
|
}
|
|
6641
6607
|
/**
|
|
6642
6608
|
* Makes an HTTP PUT request to the specified URL.
|
|
@@ -6655,13 +6621,13 @@
|
|
|
6655
6621
|
put(url, body, contentType, options = {}) {
|
|
6656
6622
|
url = url.toString();
|
|
6657
6623
|
if (body) {
|
|
6658
|
-
|
|
6624
|
+
this.setRequestBody(options, body);
|
|
6659
6625
|
}
|
|
6660
6626
|
if (contentType) {
|
|
6661
|
-
|
|
6627
|
+
this.setRequestContentType(options, contentType);
|
|
6662
6628
|
}
|
|
6663
6629
|
this.invalidateUrl(url);
|
|
6664
|
-
return
|
|
6630
|
+
return this.request('PUT', url, options);
|
|
6665
6631
|
}
|
|
6666
6632
|
/**
|
|
6667
6633
|
* Makes an HTTP PATCH request to the specified URL.
|
|
@@ -6678,10 +6644,10 @@
|
|
|
6678
6644
|
*/
|
|
6679
6645
|
patch(url, operations, options = {}) {
|
|
6680
6646
|
url = url.toString();
|
|
6681
|
-
|
|
6682
|
-
|
|
6647
|
+
this.setRequestBody(options, operations);
|
|
6648
|
+
this.setRequestContentType(options, PATCH_CONTENT_TYPE);
|
|
6683
6649
|
this.invalidateUrl(url);
|
|
6684
|
-
return
|
|
6650
|
+
return this.request('PATCH', url, options);
|
|
6685
6651
|
}
|
|
6686
6652
|
/**
|
|
6687
6653
|
* Makes an HTTP DELETE request to the specified URL.
|
|
@@ -6699,7 +6665,7 @@
|
|
|
6699
6665
|
delete(url, options = {}) {
|
|
6700
6666
|
url = url.toString();
|
|
6701
6667
|
this.invalidateUrl(url);
|
|
6702
|
-
return
|
|
6668
|
+
return this.request('DELETE', url, options);
|
|
6703
6669
|
}
|
|
6704
6670
|
/**
|
|
6705
6671
|
* Initiates a new user flow.
|
|
@@ -6751,7 +6717,7 @@
|
|
|
6751
6717
|
async startLogin(loginRequest) {
|
|
6752
6718
|
return this.post('auth/login', {
|
|
6753
6719
|
...(await this.ensureCodeChallenge(loginRequest)),
|
|
6754
|
-
clientId: loginRequest.clientId ??
|
|
6720
|
+
clientId: loginRequest.clientId ?? this.clientId,
|
|
6755
6721
|
scope: loginRequest.scope,
|
|
6756
6722
|
});
|
|
6757
6723
|
}
|
|
@@ -6766,7 +6732,7 @@
|
|
|
6766
6732
|
async startGoogleLogin(loginRequest) {
|
|
6767
6733
|
return this.post('auth/google', {
|
|
6768
6734
|
...(await this.ensureCodeChallenge(loginRequest)),
|
|
6769
|
-
clientId: loginRequest.clientId ??
|
|
6735
|
+
clientId: loginRequest.clientId ?? this.clientId,
|
|
6770
6736
|
scope: loginRequest.scope,
|
|
6771
6737
|
});
|
|
6772
6738
|
}
|
|
@@ -6790,7 +6756,7 @@
|
|
|
6790
6756
|
* @category Authentication
|
|
6791
6757
|
*/
|
|
6792
6758
|
async signOut() {
|
|
6793
|
-
await this.post(
|
|
6759
|
+
await this.post(this.logoutUrl, {});
|
|
6794
6760
|
this.clear();
|
|
6795
6761
|
}
|
|
6796
6762
|
/**
|
|
@@ -6804,7 +6770,7 @@
|
|
|
6804
6770
|
const urlParams = new URLSearchParams(window.location.search);
|
|
6805
6771
|
const code = urlParams.get('code');
|
|
6806
6772
|
if (!code) {
|
|
6807
|
-
await
|
|
6773
|
+
await this.requestAuthorization(loginParams);
|
|
6808
6774
|
return undefined;
|
|
6809
6775
|
}
|
|
6810
6776
|
else {
|
|
@@ -6817,7 +6783,7 @@
|
|
|
6817
6783
|
* @category Authentication
|
|
6818
6784
|
*/
|
|
6819
6785
|
signOutWithRedirect() {
|
|
6820
|
-
window.location.assign(
|
|
6786
|
+
window.location.assign(this.logoutUrl);
|
|
6821
6787
|
}
|
|
6822
6788
|
/**
|
|
6823
6789
|
* Initiates sign in with an external identity provider.
|
|
@@ -6838,15 +6804,15 @@
|
|
|
6838
6804
|
* @category Authentication
|
|
6839
6805
|
*/
|
|
6840
6806
|
async exchangeExternalAccessToken(token, clientId) {
|
|
6841
|
-
clientId = clientId ||
|
|
6807
|
+
clientId = clientId || this.clientId;
|
|
6842
6808
|
if (!clientId) {
|
|
6843
6809
|
throw new Error('MedplumClient is missing clientId');
|
|
6844
6810
|
}
|
|
6845
|
-
const response = await
|
|
6811
|
+
const response = await this.fetch(this.exchangeUrl, {
|
|
6846
6812
|
method: 'POST',
|
|
6847
6813
|
headers: { 'Content-Type': 'application/json' },
|
|
6848
6814
|
body: JSON.stringify({
|
|
6849
|
-
clientId:
|
|
6815
|
+
clientId: this.clientId,
|
|
6850
6816
|
externalAccessToken: token,
|
|
6851
6817
|
}),
|
|
6852
6818
|
credentials: 'include',
|
|
@@ -6856,7 +6822,7 @@
|
|
|
6856
6822
|
throw new Error('Failed to fetch tokens');
|
|
6857
6823
|
}
|
|
6858
6824
|
const tokens = await response.json();
|
|
6859
|
-
await
|
|
6825
|
+
await this.verifyTokens(tokens);
|
|
6860
6826
|
return this.getProfile();
|
|
6861
6827
|
}
|
|
6862
6828
|
/**
|
|
@@ -6885,7 +6851,7 @@
|
|
|
6885
6851
|
* @returns The well-formed FHIR URL.
|
|
6886
6852
|
*/
|
|
6887
6853
|
fhirUrl(...path) {
|
|
6888
|
-
return new URL(
|
|
6854
|
+
return new URL(this.fhirBaseUrl + path.join('/'));
|
|
6889
6855
|
}
|
|
6890
6856
|
/**
|
|
6891
6857
|
* Builds a FHIR search URL from a search query or structured query object.
|
|
@@ -6953,7 +6919,7 @@
|
|
|
6953
6919
|
search(resourceType, query, options = {}) {
|
|
6954
6920
|
const url = this.fhirSearchUrl(resourceType, query);
|
|
6955
6921
|
const cacheKey = url.toString() + '-search';
|
|
6956
|
-
const cached =
|
|
6922
|
+
const cached = this.getCacheEntry(cacheKey, options);
|
|
6957
6923
|
if (cached) {
|
|
6958
6924
|
return cached.value;
|
|
6959
6925
|
}
|
|
@@ -6961,12 +6927,12 @@
|
|
|
6961
6927
|
const bundle = await this.get(url, options);
|
|
6962
6928
|
if (bundle.entry) {
|
|
6963
6929
|
for (const entry of bundle.entry) {
|
|
6964
|
-
|
|
6930
|
+
this.cacheResource(entry.resource);
|
|
6965
6931
|
}
|
|
6966
6932
|
}
|
|
6967
6933
|
return bundle;
|
|
6968
6934
|
})());
|
|
6969
|
-
|
|
6935
|
+
this.setCacheEntry(cacheKey, promise);
|
|
6970
6936
|
return promise;
|
|
6971
6937
|
}
|
|
6972
6938
|
/**
|
|
@@ -6996,12 +6962,12 @@
|
|
|
6996
6962
|
url.searchParams.set('_count', '1');
|
|
6997
6963
|
url.searchParams.sort();
|
|
6998
6964
|
const cacheKey = url.toString() + '-searchOne';
|
|
6999
|
-
const cached =
|
|
6965
|
+
const cached = this.getCacheEntry(cacheKey, options);
|
|
7000
6966
|
if (cached) {
|
|
7001
6967
|
return cached.value;
|
|
7002
6968
|
}
|
|
7003
6969
|
const promise = new ReadablePromise(this.search(resourceType, url.searchParams, options).then((b) => b.entry?.[0]?.resource));
|
|
7004
|
-
|
|
6970
|
+
this.setCacheEntry(cacheKey, promise);
|
|
7005
6971
|
return promise;
|
|
7006
6972
|
}
|
|
7007
6973
|
/**
|
|
@@ -7029,12 +6995,12 @@
|
|
|
7029
6995
|
searchResources(resourceType, query, options = {}) {
|
|
7030
6996
|
const url = this.fhirSearchUrl(resourceType, query);
|
|
7031
6997
|
const cacheKey = url.toString() + '-searchResources';
|
|
7032
|
-
const cached =
|
|
6998
|
+
const cached = this.getCacheEntry(cacheKey, options);
|
|
7033
6999
|
if (cached) {
|
|
7034
7000
|
return cached.value;
|
|
7035
7001
|
}
|
|
7036
7002
|
const promise = new ReadablePromise(this.search(resourceType, query, options).then((b) => b.entry?.map((e) => e.resource) ?? []));
|
|
7037
|
-
|
|
7003
|
+
this.setCacheEntry(cacheKey, promise);
|
|
7038
7004
|
return promise;
|
|
7039
7005
|
}
|
|
7040
7006
|
/**
|
|
@@ -7095,7 +7061,7 @@
|
|
|
7095
7061
|
* @returns The resource if it is available in the cache; undefined otherwise.
|
|
7096
7062
|
*/
|
|
7097
7063
|
getCached(resourceType, id) {
|
|
7098
|
-
const cached =
|
|
7064
|
+
const cached = this.requestCache?.get(this.fhirUrl(resourceType, id).toString())?.value;
|
|
7099
7065
|
return cached && cached.isOk() ? cached.read() : undefined;
|
|
7100
7066
|
}
|
|
7101
7067
|
/**
|
|
@@ -7197,7 +7163,7 @@
|
|
|
7197
7163
|
return Promise.resolve(globalSchema);
|
|
7198
7164
|
}
|
|
7199
7165
|
const cacheKey = resourceType + '-requestSchema';
|
|
7200
|
-
const cached =
|
|
7166
|
+
const cached = this.getCacheEntry(cacheKey, undefined);
|
|
7201
7167
|
if (cached) {
|
|
7202
7168
|
return cached.value;
|
|
7203
7169
|
}
|
|
@@ -7240,7 +7206,7 @@
|
|
|
7240
7206
|
}
|
|
7241
7207
|
return globalSchema;
|
|
7242
7208
|
})());
|
|
7243
|
-
|
|
7209
|
+
this.setCacheEntry(cacheKey, promise);
|
|
7244
7210
|
return promise;
|
|
7245
7211
|
}
|
|
7246
7212
|
/**
|
|
@@ -7438,7 +7404,7 @@
|
|
|
7438
7404
|
};
|
|
7439
7405
|
xhr.open('POST', url);
|
|
7440
7406
|
xhr.withCredentials = true;
|
|
7441
|
-
xhr.setRequestHeader('Authorization', 'Bearer ' +
|
|
7407
|
+
xhr.setRequestHeader('Authorization', 'Bearer ' + this.accessToken);
|
|
7442
7408
|
xhr.setRequestHeader('Cache-Control', 'no-cache, no-store, max-age=0');
|
|
7443
7409
|
xhr.setRequestHeader('Content-Type', contentType);
|
|
7444
7410
|
xhr.setRequestHeader('X-Medplum', 'extended');
|
|
@@ -7468,10 +7434,10 @@
|
|
|
7468
7434
|
* @returns The result of the create operation.
|
|
7469
7435
|
*/
|
|
7470
7436
|
async createPdf(docDefinition, filename, tableLayouts, fonts) {
|
|
7471
|
-
if (!
|
|
7437
|
+
if (!this.createPdfImpl) {
|
|
7472
7438
|
throw new Error('PDF creation not enabled');
|
|
7473
7439
|
}
|
|
7474
|
-
const blob = await
|
|
7440
|
+
const blob = await this.createPdfImpl(docDefinition, tableLayouts, fonts);
|
|
7475
7441
|
return this.createBinary(blob, filename, 'application/pdf');
|
|
7476
7442
|
}
|
|
7477
7443
|
/**
|
|
@@ -7549,7 +7515,7 @@
|
|
|
7549
7515
|
// return result ?? resource;
|
|
7550
7516
|
result = resource;
|
|
7551
7517
|
}
|
|
7552
|
-
|
|
7518
|
+
this.cacheResource(result);
|
|
7553
7519
|
return result;
|
|
7554
7520
|
}
|
|
7555
7521
|
/**
|
|
@@ -7597,7 +7563,7 @@
|
|
|
7597
7563
|
* @returns The result of the delete operation.
|
|
7598
7564
|
*/
|
|
7599
7565
|
deleteResource(resourceType, id) {
|
|
7600
|
-
|
|
7566
|
+
this.deleteCacheEntry(this.fhirUrl(resourceType, id).toString());
|
|
7601
7567
|
this.invalidateSearches(resourceType);
|
|
7602
7568
|
return this.delete(this.fhirUrl(resourceType, id));
|
|
7603
7569
|
}
|
|
@@ -7801,61 +7767,80 @@
|
|
|
7801
7767
|
* @returns The Login State
|
|
7802
7768
|
*/
|
|
7803
7769
|
getActiveLogin() {
|
|
7804
|
-
return
|
|
7770
|
+
return this.storage.getObject('activeLogin');
|
|
7805
7771
|
}
|
|
7806
7772
|
/**
|
|
7807
7773
|
* @category Authentication
|
|
7808
7774
|
*/
|
|
7809
7775
|
async setActiveLogin(login) {
|
|
7810
7776
|
this.clearActiveLogin();
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
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();
|
|
7817
7783
|
}
|
|
7818
7784
|
/**
|
|
7819
7785
|
* Returns the current access token.
|
|
7820
7786
|
* @category Authentication
|
|
7821
7787
|
*/
|
|
7822
7788
|
getAccessToken() {
|
|
7823
|
-
return
|
|
7789
|
+
return this.accessToken;
|
|
7824
7790
|
}
|
|
7825
7791
|
/**
|
|
7826
7792
|
* Sets the current access token.
|
|
7827
7793
|
* @category Authentication
|
|
7828
7794
|
*/
|
|
7829
7795
|
setAccessToken(accessToken) {
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7796
|
+
this.accessToken = accessToken;
|
|
7797
|
+
this.refreshToken = undefined;
|
|
7798
|
+
this.profile = undefined;
|
|
7799
|
+
this.config = undefined;
|
|
7834
7800
|
}
|
|
7835
7801
|
/**
|
|
7836
7802
|
* @category Authentication
|
|
7837
7803
|
*/
|
|
7838
7804
|
getLogins() {
|
|
7839
|
-
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;
|
|
7840
7825
|
}
|
|
7841
7826
|
/**
|
|
7842
7827
|
* @category Authentication
|
|
7843
7828
|
*/
|
|
7844
7829
|
isLoading() {
|
|
7845
|
-
return !!
|
|
7830
|
+
return !!this.profilePromise;
|
|
7846
7831
|
}
|
|
7847
7832
|
/**
|
|
7848
7833
|
* @category User Profile
|
|
7849
7834
|
*/
|
|
7850
7835
|
getProfile() {
|
|
7851
|
-
return
|
|
7836
|
+
return this.profile;
|
|
7852
7837
|
}
|
|
7853
7838
|
/**
|
|
7854
7839
|
* @category User Profile
|
|
7855
7840
|
*/
|
|
7856
7841
|
async getProfileAsync() {
|
|
7857
|
-
if (
|
|
7858
|
-
await
|
|
7842
|
+
if (this.profilePromise) {
|
|
7843
|
+
await this.profilePromise;
|
|
7859
7844
|
}
|
|
7860
7845
|
return this.getProfile();
|
|
7861
7846
|
}
|
|
@@ -7863,7 +7848,7 @@
|
|
|
7863
7848
|
* @category User Profile
|
|
7864
7849
|
*/
|
|
7865
7850
|
getUserConfiguration() {
|
|
7866
|
-
return
|
|
7851
|
+
return this.config;
|
|
7867
7852
|
}
|
|
7868
7853
|
/**
|
|
7869
7854
|
* Downloads the URL as a blob.
|
|
@@ -7873,13 +7858,234 @@
|
|
|
7873
7858
|
* @returns Promise to the response body as a blob.
|
|
7874
7859
|
*/
|
|
7875
7860
|
async download(url, options = {}) {
|
|
7876
|
-
if (
|
|
7877
|
-
await
|
|
7861
|
+
if (this.refreshPromise) {
|
|
7862
|
+
await this.refreshPromise;
|
|
7878
7863
|
}
|
|
7879
|
-
|
|
7880
|
-
const response = await
|
|
7864
|
+
this.addFetchOptionsDefaults(options);
|
|
7865
|
+
const response = await this.fetch(url.toString(), options);
|
|
7881
7866
|
return response.blob();
|
|
7882
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
|
+
}
|
|
7883
8089
|
/**
|
|
7884
8090
|
* Starts a new PKCE flow.
|
|
7885
8091
|
* These PKCE values are stateful, and must survive redirects and page refreshes.
|
|
@@ -7895,6 +8101,23 @@
|
|
|
7895
8101
|
sessionStorage.setItem('codeChallenge', codeChallenge);
|
|
7896
8102
|
return { codeChallengeMethod: 'S256', codeChallenge };
|
|
7897
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
|
+
}
|
|
7898
8121
|
/**
|
|
7899
8122
|
* Processes an OAuth authorization code.
|
|
7900
8123
|
* See: https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
|
|
@@ -7906,7 +8129,7 @@
|
|
|
7906
8129
|
const formBody = new URLSearchParams();
|
|
7907
8130
|
formBody.set('grant_type', 'authorization_code');
|
|
7908
8131
|
formBody.set('code', code);
|
|
7909
|
-
formBody.set('client_id', loginParams?.clientId ||
|
|
8132
|
+
formBody.set('client_id', loginParams?.clientId || this.clientId);
|
|
7910
8133
|
formBody.set('redirect_uri', loginParams?.redirectUri || getWindowOrigin());
|
|
7911
8134
|
if (typeof sessionStorage !== 'undefined') {
|
|
7912
8135
|
const codeVerifier = sessionStorage.getItem('codeVerifier');
|
|
@@ -7914,7 +8137,29 @@
|
|
|
7914
8137
|
formBody.set('code_verifier', codeVerifier);
|
|
7915
8138
|
}
|
|
7916
8139
|
}
|
|
7917
|
-
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;
|
|
7918
8163
|
}
|
|
7919
8164
|
/**
|
|
7920
8165
|
* Starts a new OAuth2 client credentials flow.
|
|
@@ -7925,13 +8170,24 @@
|
|
|
7925
8170
|
* @returns Promise that resolves to the client profile.
|
|
7926
8171
|
*/
|
|
7927
8172
|
async startClientLogin(clientId, clientSecret) {
|
|
7928
|
-
|
|
7929
|
-
|
|
8173
|
+
this.clientId = clientId;
|
|
8174
|
+
this.clientSecret = clientSecret;
|
|
7930
8175
|
const formBody = new URLSearchParams();
|
|
7931
8176
|
formBody.set('grant_type', 'client_credentials');
|
|
7932
8177
|
formBody.set('client_id', clientId);
|
|
7933
8178
|
formBody.set('client_secret', clientSecret);
|
|
7934
|
-
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);
|
|
7935
8191
|
}
|
|
7936
8192
|
/**
|
|
7937
8193
|
* Invite a user to a project.
|
|
@@ -7942,280 +8198,72 @@
|
|
|
7942
8198
|
async invite(projectId, body) {
|
|
7943
8199
|
return this.post('admin/projects/' + projectId + '/invite', body);
|
|
7944
8200
|
}
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
|
|
7952
|
-
|
|
7953
|
-
|
|
7954
|
-
|
|
7955
|
-
|
|
7956
|
-
|
|
7957
|
-
|
|
7958
|
-
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
if (__classPrivateFieldGet(this, _MedplumClient_cacheTime, "f") <= 0 || options?.cache === 'no-cache' || options?.cache === 'reload') {
|
|
7965
|
-
return undefined;
|
|
7966
|
-
}
|
|
7967
|
-
const entry = __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").get(key);
|
|
7968
|
-
if (!entry || entry.requestTime + __classPrivateFieldGet(this, _MedplumClient_cacheTime, "f") < Date.now()) {
|
|
7969
|
-
return undefined;
|
|
7970
|
-
}
|
|
7971
|
-
return entry;
|
|
7972
|
-
}, _MedplumClient_setCacheEntry = function _MedplumClient_setCacheEntry(key, value) {
|
|
7973
|
-
if (__classPrivateFieldGet(this, _MedplumClient_cacheTime, "f") > 0) {
|
|
7974
|
-
__classPrivateFieldGet(this, _MedplumClient_requestCache, "f").set(key, { requestTime: Date.now(), value });
|
|
7975
|
-
}
|
|
7976
|
-
}, _MedplumClient_cacheResource = function _MedplumClient_cacheResource(resource) {
|
|
7977
|
-
if (resource?.id) {
|
|
7978
|
-
__classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setCacheEntry).call(this, this.fhirUrl(resource.resourceType, resource.id).toString(), new ReadablePromise(Promise.resolve(resource)));
|
|
7979
|
-
}
|
|
7980
|
-
}, _MedplumClient_deleteCacheEntry = function _MedplumClient_deleteCacheEntry(key) {
|
|
7981
|
-
if (__classPrivateFieldGet(this, _MedplumClient_cacheTime, "f") > 0) {
|
|
7982
|
-
__classPrivateFieldGet(this, _MedplumClient_requestCache, "f").delete(key);
|
|
7983
|
-
}
|
|
7984
|
-
}, _MedplumClient_request =
|
|
7985
|
-
/**
|
|
7986
|
-
* Makes an HTTP request.
|
|
7987
|
-
* @param {string} method
|
|
7988
|
-
* @param {string} url
|
|
7989
|
-
* @param {string=} contentType
|
|
7990
|
-
* @param {Object=} body
|
|
7991
|
-
*/
|
|
7992
|
-
async function _MedplumClient_request(method, url, options = {}) {
|
|
7993
|
-
if (__classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f")) {
|
|
7994
|
-
await __classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f");
|
|
7995
|
-
}
|
|
7996
|
-
if (!url.startsWith('http')) {
|
|
7997
|
-
url = __classPrivateFieldGet(this, _MedplumClient_baseUrl, "f") + url;
|
|
7998
|
-
}
|
|
7999
|
-
options.method = method;
|
|
8000
|
-
__classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_addFetchOptionsDefaults).call(this, options);
|
|
8001
|
-
const response = await __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_fetchWithRetry).call(this, url, options);
|
|
8002
|
-
if (response.status === 401) {
|
|
8003
|
-
// Refresh and try again
|
|
8004
|
-
return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_handleUnauthenticated).call(this, method, url, options);
|
|
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');
|
|
8216
|
+
}
|
|
8217
|
+
const tokens = await response.json();
|
|
8218
|
+
await this.verifyTokens(tokens);
|
|
8219
|
+
return this.getProfile();
|
|
8005
8220
|
}
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
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,
|
|
8245
|
+
});
|
|
8009
8246
|
}
|
|
8010
|
-
|
|
8011
|
-
|
|
8012
|
-
|
|
8013
|
-
|
|
8014
|
-
|
|
8015
|
-
console.error('Error parsing response', response.status, err);
|
|
8016
|
-
throw err;
|
|
8017
|
-
}
|
|
8018
|
-
if (response.status >= 400) {
|
|
8019
|
-
throw new OperationOutcomeError(normalizeOperationOutcome(obj));
|
|
8020
|
-
}
|
|
8021
|
-
return obj;
|
|
8022
|
-
}, _MedplumClient_fetchWithRetry = async function _MedplumClient_fetchWithRetry(url, options) {
|
|
8023
|
-
const maxRetries = 3;
|
|
8024
|
-
const retryDelay = 200;
|
|
8025
|
-
let response = undefined;
|
|
8026
|
-
for (let retry = 0; retry < maxRetries; retry++) {
|
|
8027
|
-
response = (await __classPrivateFieldGet(this, _MedplumClient_fetch, "f").call(this, url, options));
|
|
8028
|
-
if (response.status < 500) {
|
|
8029
|
-
return response;
|
|
8030
|
-
}
|
|
8031
|
-
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
8032
|
-
}
|
|
8033
|
-
return response;
|
|
8034
|
-
}, _MedplumClient_executeAutoBatch =
|
|
8035
|
-
/**
|
|
8036
|
-
* Executes a batch of requests that were automatically batched together.
|
|
8037
|
-
*/
|
|
8038
|
-
async function _MedplumClient_executeAutoBatch() {
|
|
8039
|
-
// Get the current queue
|
|
8040
|
-
const entries = [...__classPrivateFieldGet(this, _MedplumClient_autoBatchQueue, "f")];
|
|
8041
|
-
// Clear the queue
|
|
8042
|
-
__classPrivateFieldGet(this, _MedplumClient_autoBatchQueue, "f").length = 0;
|
|
8043
|
-
// Clear the timer
|
|
8044
|
-
__classPrivateFieldSet(this, _MedplumClient_autoBatchTimerId, undefined, "f");
|
|
8045
|
-
// If there is only one request in the batch, just execute it
|
|
8046
|
-
if (entries.length === 1) {
|
|
8047
|
-
const entry = entries[0];
|
|
8247
|
+
/**
|
|
8248
|
+
* Sets up a listener for window storage events.
|
|
8249
|
+
* This synchronizes state across browser windows and browser tabs.
|
|
8250
|
+
*/
|
|
8251
|
+
setupStorageListener() {
|
|
8048
8252
|
try {
|
|
8049
|
-
|
|
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
|
+
});
|
|
8050
8261
|
}
|
|
8051
8262
|
catch (err) {
|
|
8052
|
-
|
|
8263
|
+
// Silently ignore if this environment does not support storage events
|
|
8053
8264
|
}
|
|
8054
|
-
return;
|
|
8055
8265
|
}
|
|
8056
|
-
|
|
8057
|
-
const batch = {
|
|
8058
|
-
resourceType: 'Bundle',
|
|
8059
|
-
type: 'batch',
|
|
8060
|
-
entry: entries.map((e) => ({
|
|
8061
|
-
request: {
|
|
8062
|
-
method: e.method,
|
|
8063
|
-
url: e.url,
|
|
8064
|
-
},
|
|
8065
|
-
resource: e.options.body ? JSON.parse(e.options.body) : undefined,
|
|
8066
|
-
})),
|
|
8067
|
-
};
|
|
8068
|
-
// Execute the batch request
|
|
8069
|
-
const response = (await this.post('fhir/R4', batch));
|
|
8070
|
-
// Process the response
|
|
8071
|
-
for (let i = 0; i < entries.length; i++) {
|
|
8072
|
-
const entry = entries[i];
|
|
8073
|
-
const responseEntry = response.entry?.[i];
|
|
8074
|
-
if (responseEntry?.response?.outcome && !isOk(responseEntry.response.outcome)) {
|
|
8075
|
-
entry.reject(new OperationOutcomeError(responseEntry.response.outcome));
|
|
8076
|
-
}
|
|
8077
|
-
else {
|
|
8078
|
-
entry.resolve(responseEntry?.resource);
|
|
8079
|
-
}
|
|
8080
|
-
}
|
|
8081
|
-
}, _MedplumClient_addFetchOptionsDefaults = function _MedplumClient_addFetchOptionsDefaults(options) {
|
|
8082
|
-
if (!options.headers) {
|
|
8083
|
-
options.headers = {};
|
|
8084
|
-
}
|
|
8085
|
-
const headers = options.headers;
|
|
8086
|
-
headers['X-Medplum'] = 'extended';
|
|
8087
|
-
if (!headers['Content-Type']) {
|
|
8088
|
-
headers['Content-Type'] = FHIR_CONTENT_TYPE;
|
|
8089
|
-
}
|
|
8090
|
-
if (__classPrivateFieldGet(this, _MedplumClient_accessToken, "f")) {
|
|
8091
|
-
headers['Authorization'] = 'Bearer ' + __classPrivateFieldGet(this, _MedplumClient_accessToken, "f");
|
|
8092
|
-
}
|
|
8093
|
-
if (!options.cache) {
|
|
8094
|
-
options.cache = 'no-cache';
|
|
8095
|
-
}
|
|
8096
|
-
if (!options.credentials) {
|
|
8097
|
-
options.credentials = 'include';
|
|
8098
|
-
}
|
|
8099
|
-
}, _MedplumClient_setRequestContentType = function _MedplumClient_setRequestContentType(options, contentType) {
|
|
8100
|
-
if (!options.headers) {
|
|
8101
|
-
options.headers = {};
|
|
8102
|
-
}
|
|
8103
|
-
const headers = options.headers;
|
|
8104
|
-
headers['Content-Type'] = contentType;
|
|
8105
|
-
}, _MedplumClient_setRequestBody = function _MedplumClient_setRequestBody(options, data) {
|
|
8106
|
-
if (typeof data === 'string' ||
|
|
8107
|
-
(typeof Blob !== 'undefined' && data instanceof Blob) ||
|
|
8108
|
-
(typeof File !== 'undefined' && data instanceof File) ||
|
|
8109
|
-
(typeof Uint8Array !== 'undefined' && data instanceof Uint8Array)) {
|
|
8110
|
-
options.body = data;
|
|
8111
|
-
}
|
|
8112
|
-
else if (data) {
|
|
8113
|
-
options.body = JSON.stringify(data);
|
|
8114
|
-
}
|
|
8115
|
-
}, _MedplumClient_handleUnauthenticated = function _MedplumClient_handleUnauthenticated(method, url, options) {
|
|
8116
|
-
if (__classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_refresh).call(this)) {
|
|
8117
|
-
return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, method, url, options);
|
|
8118
|
-
}
|
|
8119
|
-
this.clearActiveLogin();
|
|
8120
|
-
if (__classPrivateFieldGet(this, _MedplumClient_onUnauthenticated, "f")) {
|
|
8121
|
-
__classPrivateFieldGet(this, _MedplumClient_onUnauthenticated, "f").call(this);
|
|
8122
|
-
}
|
|
8123
|
-
return Promise.reject(new Error('Unauthenticated'));
|
|
8124
|
-
}, _MedplumClient_requestAuthorization =
|
|
8125
|
-
/**
|
|
8126
|
-
* Redirects the user to the login screen for authorization.
|
|
8127
|
-
* Clears all auth state including local storage and session storage.
|
|
8128
|
-
* See: https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
|
|
8129
|
-
*/
|
|
8130
|
-
async function _MedplumClient_requestAuthorization(loginParams) {
|
|
8131
|
-
const loginRequest = await this.ensureCodeChallenge(loginParams || {});
|
|
8132
|
-
const url = new URL(__classPrivateFieldGet(this, _MedplumClient_authorizeUrl, "f"));
|
|
8133
|
-
url.searchParams.set('response_type', 'code');
|
|
8134
|
-
url.searchParams.set('state', sessionStorage.getItem('pkceState'));
|
|
8135
|
-
url.searchParams.set('client_id', loginRequest.clientId || __classPrivateFieldGet(this, _MedplumClient_clientId, "f"));
|
|
8136
|
-
url.searchParams.set('redirect_uri', loginRequest.redirectUri || getWindowOrigin());
|
|
8137
|
-
url.searchParams.set('code_challenge_method', loginRequest.codeChallengeMethod);
|
|
8138
|
-
url.searchParams.set('code_challenge', loginRequest.codeChallenge);
|
|
8139
|
-
url.searchParams.set('scope', loginRequest.scope || 'openid profile');
|
|
8140
|
-
window.location.assign(url.toString());
|
|
8141
|
-
}, _MedplumClient_refresh = function _MedplumClient_refresh() {
|
|
8142
|
-
if (__classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f")) {
|
|
8143
|
-
return __classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f");
|
|
8144
|
-
}
|
|
8145
|
-
if (__classPrivateFieldGet(this, _MedplumClient_refreshToken, "f")) {
|
|
8146
|
-
const formBody = new URLSearchParams();
|
|
8147
|
-
formBody.set('grant_type', 'refresh_token');
|
|
8148
|
-
formBody.set('client_id', __classPrivateFieldGet(this, _MedplumClient_clientId, "f"));
|
|
8149
|
-
formBody.set('refresh_token', __classPrivateFieldGet(this, _MedplumClient_refreshToken, "f"));
|
|
8150
|
-
__classPrivateFieldSet(this, _MedplumClient_refreshPromise, __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_fetchTokens).call(this, formBody), "f");
|
|
8151
|
-
return __classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f");
|
|
8152
|
-
}
|
|
8153
|
-
if (__classPrivateFieldGet(this, _MedplumClient_clientId, "f") && __classPrivateFieldGet(this, _MedplumClient_clientSecret, "f")) {
|
|
8154
|
-
__classPrivateFieldSet(this, _MedplumClient_refreshPromise, this.startClientLogin(__classPrivateFieldGet(this, _MedplumClient_clientId, "f"), __classPrivateFieldGet(this, _MedplumClient_clientSecret, "f")), "f");
|
|
8155
|
-
return __classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f");
|
|
8156
|
-
}
|
|
8157
|
-
return undefined;
|
|
8158
|
-
}, _MedplumClient_fetchTokens =
|
|
8159
|
-
/**
|
|
8160
|
-
* Makes a POST request to the tokens endpoint.
|
|
8161
|
-
* See: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
|
8162
|
-
* @param formBody Token parameters in URL encoded format.
|
|
8163
|
-
*/
|
|
8164
|
-
async function _MedplumClient_fetchTokens(formBody) {
|
|
8165
|
-
const response = await __classPrivateFieldGet(this, _MedplumClient_fetch, "f").call(this, __classPrivateFieldGet(this, _MedplumClient_tokenUrl, "f"), {
|
|
8166
|
-
method: 'POST',
|
|
8167
|
-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
8168
|
-
body: formBody,
|
|
8169
|
-
credentials: 'include',
|
|
8170
|
-
});
|
|
8171
|
-
if (!response.ok) {
|
|
8172
|
-
this.clearActiveLogin();
|
|
8173
|
-
throw new Error('Failed to fetch tokens');
|
|
8174
|
-
}
|
|
8175
|
-
const tokens = await response.json();
|
|
8176
|
-
await __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_verifyTokens).call(this, tokens);
|
|
8177
|
-
return this.getProfile();
|
|
8178
|
-
}, _MedplumClient_verifyTokens =
|
|
8179
|
-
/**
|
|
8180
|
-
* Verifies the tokens received from the auth server.
|
|
8181
|
-
* Validates the JWT against the JWKS.
|
|
8182
|
-
* See: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
|
8183
|
-
* @param tokens
|
|
8184
|
-
*/
|
|
8185
|
-
async function _MedplumClient_verifyTokens(tokens) {
|
|
8186
|
-
const token = tokens.access_token;
|
|
8187
|
-
// Verify token has not expired
|
|
8188
|
-
const tokenPayload = parseJWTPayload(token);
|
|
8189
|
-
if (Date.now() >= tokenPayload.exp * 1000) {
|
|
8190
|
-
this.clearActiveLogin();
|
|
8191
|
-
throw new Error('Token expired');
|
|
8192
|
-
}
|
|
8193
|
-
// Verify app_client_id
|
|
8194
|
-
if (__classPrivateFieldGet(this, _MedplumClient_clientId, "f") && tokenPayload.client_id !== __classPrivateFieldGet(this, _MedplumClient_clientId, "f")) {
|
|
8195
|
-
this.clearActiveLogin();
|
|
8196
|
-
throw new Error('Token was not issued for this audience');
|
|
8197
|
-
}
|
|
8198
|
-
return this.setActiveLogin({
|
|
8199
|
-
accessToken: token,
|
|
8200
|
-
refreshToken: tokens.refresh_token,
|
|
8201
|
-
project: tokens.project,
|
|
8202
|
-
profile: tokens.profile,
|
|
8203
|
-
});
|
|
8204
|
-
}, _MedplumClient_setupStorageListener = function _MedplumClient_setupStorageListener() {
|
|
8205
|
-
try {
|
|
8206
|
-
window.addEventListener('storage', (e) => {
|
|
8207
|
-
if (e.key === null || e.key === 'activeLogin') {
|
|
8208
|
-
// Storage events fire when different tabs make changes.
|
|
8209
|
-
// On storage clear (key === null) or activeLogin change (key === 'activeLogin')
|
|
8210
|
-
// Refresh the page to ensure the active login is up to date.
|
|
8211
|
-
window.location.reload();
|
|
8212
|
-
}
|
|
8213
|
-
});
|
|
8214
|
-
}
|
|
8215
|
-
catch (err) {
|
|
8216
|
-
// Silently ignore if this environment does not support storage events
|
|
8217
|
-
}
|
|
8218
|
-
};
|
|
8266
|
+
}
|
|
8219
8267
|
/**
|
|
8220
8268
|
* Returns the default fetch method.
|
|
8221
8269
|
* The default fetch is currently only available in browser environments.
|
|
@@ -8245,7 +8293,6 @@
|
|
|
8245
8293
|
return url.endsWith('/') ? url : url + '/';
|
|
8246
8294
|
}
|
|
8247
8295
|
|
|
8248
|
-
var _ParserBuilder_prefixParselets, _ParserBuilder_infixParselets, _Parser_tokens, _Parser_prefixParselets, _Parser_infixParselets;
|
|
8249
8296
|
class PrefixOperatorAtom {
|
|
8250
8297
|
constructor(operator, child) {
|
|
8251
8298
|
this.operator = operator;
|
|
@@ -8267,15 +8314,15 @@
|
|
|
8267
8314
|
}
|
|
8268
8315
|
class ParserBuilder {
|
|
8269
8316
|
constructor() {
|
|
8270
|
-
|
|
8271
|
-
|
|
8317
|
+
this.prefixParselets = {};
|
|
8318
|
+
this.infixParselets = {};
|
|
8272
8319
|
}
|
|
8273
8320
|
registerInfix(tokenType, parselet) {
|
|
8274
|
-
|
|
8321
|
+
this.infixParselets[tokenType] = parselet;
|
|
8275
8322
|
return this;
|
|
8276
8323
|
}
|
|
8277
8324
|
registerPrefix(tokenType, parselet) {
|
|
8278
|
-
|
|
8325
|
+
this.prefixParselets[tokenType] = parselet;
|
|
8279
8326
|
return this;
|
|
8280
8327
|
}
|
|
8281
8328
|
prefix(tokenType, precedence, builder) {
|
|
@@ -8296,21 +8343,17 @@
|
|
|
8296
8343
|
});
|
|
8297
8344
|
}
|
|
8298
8345
|
construct(input) {
|
|
8299
|
-
return new Parser(input,
|
|
8346
|
+
return new Parser(input, this.prefixParselets, this.infixParselets);
|
|
8300
8347
|
}
|
|
8301
8348
|
}
|
|
8302
|
-
_ParserBuilder_prefixParselets = new WeakMap(), _ParserBuilder_infixParselets = new WeakMap();
|
|
8303
8349
|
class Parser {
|
|
8304
8350
|
constructor(tokens, prefixParselets, infixParselets) {
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
__classPrivateFieldSet(this, _Parser_tokens, tokens, "f");
|
|
8309
|
-
__classPrivateFieldSet(this, _Parser_prefixParselets, prefixParselets, "f");
|
|
8310
|
-
__classPrivateFieldSet(this, _Parser_infixParselets, infixParselets, "f");
|
|
8351
|
+
this.tokens = tokens;
|
|
8352
|
+
this.prefixParselets = prefixParselets;
|
|
8353
|
+
this.infixParselets = infixParselets;
|
|
8311
8354
|
}
|
|
8312
8355
|
hasMore() {
|
|
8313
|
-
return
|
|
8356
|
+
return this.tokens.length > 0;
|
|
8314
8357
|
}
|
|
8315
8358
|
match(expected) {
|
|
8316
8359
|
const token = this.peek();
|
|
@@ -8322,7 +8365,7 @@
|
|
|
8322
8365
|
}
|
|
8323
8366
|
consumeAndParse(precedence = Infinity) {
|
|
8324
8367
|
const token = this.consume();
|
|
8325
|
-
const prefix =
|
|
8368
|
+
const prefix = this.prefixParselets[token.id];
|
|
8326
8369
|
if (!prefix) {
|
|
8327
8370
|
throw Error(`Parse error at "${token.value}" (line ${token.line}, column ${token.column}). No matching prefix parselet.`);
|
|
8328
8371
|
}
|
|
@@ -8346,7 +8389,7 @@
|
|
|
8346
8389
|
return Infinity;
|
|
8347
8390
|
}
|
|
8348
8391
|
consume(expectedId, expectedValue) {
|
|
8349
|
-
if (!
|
|
8392
|
+
if (!this.tokens.length) {
|
|
8350
8393
|
throw Error('Cant consume unknown more tokens.');
|
|
8351
8394
|
}
|
|
8352
8395
|
if (expectedId && this.peek()?.id !== expectedId) {
|
|
@@ -8357,21 +8400,19 @@
|
|
|
8357
8400
|
const actual = this.peek();
|
|
8358
8401
|
throw Error(`Expected "${expectedValue}" but got "${actual.value}" at line ${actual.line} column ${actual.column}.`);
|
|
8359
8402
|
}
|
|
8360
|
-
return
|
|
8403
|
+
return this.tokens.shift();
|
|
8361
8404
|
}
|
|
8362
8405
|
peek() {
|
|
8363
|
-
return
|
|
8406
|
+
return this.tokens.length > 0 ? this.tokens[0] : undefined;
|
|
8364
8407
|
}
|
|
8365
8408
|
removeComments() {
|
|
8366
|
-
|
|
8409
|
+
this.tokens = this.tokens.filter((t) => t.id !== 'Comment');
|
|
8367
8410
|
}
|
|
8368
8411
|
getInfixParselet(token) {
|
|
8369
|
-
return
|
|
8412
|
+
return this.infixParselets[token.id === 'Symbol' ? token.value : token.id];
|
|
8370
8413
|
}
|
|
8371
8414
|
}
|
|
8372
|
-
_Parser_tokens = new WeakMap(), _Parser_prefixParselets = new WeakMap(), _Parser_infixParselets = new WeakMap();
|
|
8373
8415
|
|
|
8374
|
-
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;
|
|
8375
8416
|
const STANDARD_UNITS = [
|
|
8376
8417
|
'year',
|
|
8377
8418
|
'years',
|
|
@@ -8392,190 +8433,203 @@
|
|
|
8392
8433
|
];
|
|
8393
8434
|
class Tokenizer {
|
|
8394
8435
|
constructor(str, keywords, operators, options) {
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
_Tokenizer_markStack.set(this, []);
|
|
8404
|
-
__classPrivateFieldSet(this, _Tokenizer_str, str, "f");
|
|
8405
|
-
__classPrivateFieldSet(this, _Tokenizer_keywords, keywords, "f");
|
|
8406
|
-
__classPrivateFieldSet(this, _Tokenizer_operators, operators, "f");
|
|
8407
|
-
__classPrivateFieldSet(this, _Tokenizer_dateTimeLiterals, !!options?.dateTimeLiterals, "f");
|
|
8408
|
-
__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]/;
|
|
8409
8444
|
}
|
|
8410
8445
|
tokenize() {
|
|
8411
|
-
while (
|
|
8412
|
-
const token =
|
|
8446
|
+
while (this.pos.index < this.str.length) {
|
|
8447
|
+
const token = this.consumeToken();
|
|
8413
8448
|
if (token) {
|
|
8414
|
-
|
|
8449
|
+
this.result.push(token);
|
|
8415
8450
|
}
|
|
8416
8451
|
}
|
|
8417
|
-
return
|
|
8452
|
+
return this.result;
|
|
8418
8453
|
}
|
|
8419
|
-
|
|
8420
|
-
|
|
8421
|
-
return __classPrivateFieldGet(this, _Tokenizer_result, "f").slice(-1)[0];
|
|
8422
|
-
}, _Tokenizer_peekToken = function _Tokenizer_peekToken() {
|
|
8423
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_mark).call(this);
|
|
8424
|
-
const token = __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeToken).call(this);
|
|
8425
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_reset).call(this);
|
|
8426
|
-
return token;
|
|
8427
|
-
}, _Tokenizer_consumeToken = function _Tokenizer_consumeToken() {
|
|
8428
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhitespace).call(this);
|
|
8429
|
-
const c = __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this);
|
|
8430
|
-
if (!c) {
|
|
8431
|
-
return undefined;
|
|
8454
|
+
prevToken() {
|
|
8455
|
+
return this.result.slice(-1)[0];
|
|
8432
8456
|
}
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
if (c === '/' && next === '/') {
|
|
8439
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeSingleLineComment).call(this);
|
|
8440
|
-
}
|
|
8441
|
-
if (c === "'" || c === '"') {
|
|
8442
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeString).call(this, c);
|
|
8443
|
-
}
|
|
8444
|
-
if (c === '`') {
|
|
8445
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeBacktickSymbol).call(this);
|
|
8446
|
-
}
|
|
8447
|
-
if (c === '@') {
|
|
8448
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeDateTime).call(this);
|
|
8449
|
-
}
|
|
8450
|
-
if (c.match(/\d/)) {
|
|
8451
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeNumber).call(this);
|
|
8452
|
-
}
|
|
8453
|
-
if (c.match(/\w/)) {
|
|
8454
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeSymbol).call(this);
|
|
8455
|
-
}
|
|
8456
|
-
if (c === '$' && next.match(/\w/)) {
|
|
8457
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeSymbol).call(this);
|
|
8458
|
-
}
|
|
8459
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeOperator).call(this);
|
|
8460
|
-
}, _Tokenizer_consumeWhitespace = function _Tokenizer_consumeWhitespace() {
|
|
8461
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this).match(/\s/));
|
|
8462
|
-
}, _Tokenizer_consumeMultiLineComment = function _Tokenizer_consumeMultiLineComment() {
|
|
8463
|
-
const start = __classPrivateFieldGet(this, _Tokenizer_pos, "f").index;
|
|
8464
|
-
__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) !== '/');
|
|
8465
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8466
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8467
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_buildToken).call(this, 'Comment', __classPrivateFieldGet(this, _Tokenizer_str, "f").substring(start, __classPrivateFieldGet(this, _Tokenizer_pos, "f").index));
|
|
8468
|
-
}, _Tokenizer_consumeSingleLineComment = function _Tokenizer_consumeSingleLineComment() {
|
|
8469
|
-
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'));
|
|
8470
|
-
}, _Tokenizer_consumeString = function _Tokenizer_consumeString(endChar) {
|
|
8471
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8472
|
-
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));
|
|
8473
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8474
|
-
return result;
|
|
8475
|
-
}, _Tokenizer_consumeBacktickSymbol = function _Tokenizer_consumeBacktickSymbol() {
|
|
8476
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8477
|
-
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) !== '`'));
|
|
8478
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8479
|
-
return result;
|
|
8480
|
-
}, _Tokenizer_consumeDateTime = function _Tokenizer_consumeDateTime() {
|
|
8481
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this); // Consume "@"
|
|
8482
|
-
const start = __classPrivateFieldGet(this, _Tokenizer_pos, "f").index;
|
|
8483
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this).match(/[\d-]/));
|
|
8484
|
-
if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === 'T') {
|
|
8485
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8486
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this).match(/[\d:]/));
|
|
8487
|
-
if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === '.' && __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_peek).call(this).match(/\d/)) {
|
|
8488
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8489
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this).match(/\d/));
|
|
8490
|
-
}
|
|
8491
|
-
if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === 'Z') {
|
|
8492
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8493
|
-
}
|
|
8494
|
-
else if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === '+' || __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === '-') {
|
|
8495
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8496
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this).match(/[\d:]/));
|
|
8497
|
-
}
|
|
8498
|
-
}
|
|
8499
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_buildToken).call(this, 'DateTime', __classPrivateFieldGet(this, _Tokenizer_str, "f").substring(start, __classPrivateFieldGet(this, _Tokenizer_pos, "f").index));
|
|
8500
|
-
}, _Tokenizer_consumeNumber = function _Tokenizer_consumeNumber() {
|
|
8501
|
-
const start = __classPrivateFieldGet(this, _Tokenizer_pos, "f").index;
|
|
8502
|
-
let id = 'Number';
|
|
8503
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this).match(/\d/));
|
|
8504
|
-
if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === '.' && __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_peek).call(this).match(/\d/)) {
|
|
8505
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8506
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeWhile).call(this, () => __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this).match(/\d/));
|
|
8507
|
-
}
|
|
8508
|
-
if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === '-' && __classPrivateFieldGet(this, _Tokenizer_dateTimeLiterals, "f")) {
|
|
8509
|
-
// Rewind to one character before the start, and then treat as dateTime literal.
|
|
8510
|
-
__classPrivateFieldGet(this, _Tokenizer_pos, "f").index = start - 1;
|
|
8511
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeDateTime).call(this);
|
|
8512
|
-
}
|
|
8513
|
-
if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === ' ') {
|
|
8514
|
-
if (isUnitToken(__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_peekToken).call(this))) {
|
|
8515
|
-
id = 'Quantity';
|
|
8516
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_consumeToken).call(this);
|
|
8517
|
-
}
|
|
8518
|
-
}
|
|
8519
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_buildToken).call(this, id, __classPrivateFieldGet(this, _Tokenizer_str, "f").substring(start, __classPrivateFieldGet(this, _Tokenizer_pos, "f").index));
|
|
8520
|
-
}, _Tokenizer_consumeSymbol = function _Tokenizer_consumeSymbol() {
|
|
8521
|
-
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")));
|
|
8522
|
-
if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_prevToken).call(this)?.value !== '.' && __classPrivateFieldGet(this, _Tokenizer_keywords, "f").includes(value)) {
|
|
8523
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_buildToken).call(this, value, value);
|
|
8524
|
-
}
|
|
8525
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_buildToken).call(this, 'Symbol', value);
|
|
8526
|
-
}, _Tokenizer_consumeOperator = function _Tokenizer_consumeOperator() {
|
|
8527
|
-
const c = __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this);
|
|
8528
|
-
const next = __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_peek).call(this);
|
|
8529
|
-
const twoCharOp = c + next;
|
|
8530
|
-
if (__classPrivateFieldGet(this, _Tokenizer_operators, "f").includes(twoCharOp)) {
|
|
8531
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8532
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8533
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_buildToken).call(this, twoCharOp, twoCharOp);
|
|
8534
|
-
}
|
|
8535
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8536
|
-
return __classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_buildToken).call(this, c, c);
|
|
8537
|
-
}, _Tokenizer_consumeWhile = function _Tokenizer_consumeWhile(condition) {
|
|
8538
|
-
const start = __classPrivateFieldGet(this, _Tokenizer_pos, "f").index;
|
|
8539
|
-
while (__classPrivateFieldGet(this, _Tokenizer_pos, "f").index < __classPrivateFieldGet(this, _Tokenizer_str, "f").length && condition()) {
|
|
8540
|
-
__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_advance).call(this);
|
|
8541
|
-
}
|
|
8542
|
-
return __classPrivateFieldGet(this, _Tokenizer_str, "f").substring(start, __classPrivateFieldGet(this, _Tokenizer_pos, "f").index);
|
|
8543
|
-
}, _Tokenizer_curr = function _Tokenizer_curr() {
|
|
8544
|
-
return __classPrivateFieldGet(this, _Tokenizer_str, "f")[__classPrivateFieldGet(this, _Tokenizer_pos, "f").index];
|
|
8545
|
-
}, _Tokenizer_prev = function _Tokenizer_prev() {
|
|
8546
|
-
return __classPrivateFieldGet(this, _Tokenizer_str, "f")[__classPrivateFieldGet(this, _Tokenizer_pos, "f").index - 1] ?? '';
|
|
8547
|
-
}, _Tokenizer_peek = function _Tokenizer_peek() {
|
|
8548
|
-
return __classPrivateFieldGet(this, _Tokenizer_str, "f")[__classPrivateFieldGet(this, _Tokenizer_pos, "f").index + 1] ?? '';
|
|
8549
|
-
}, _Tokenizer_mark = function _Tokenizer_mark() {
|
|
8550
|
-
__classPrivateFieldGet(this, _Tokenizer_markStack, "f").push({ ...__classPrivateFieldGet(this, _Tokenizer_pos, "f") });
|
|
8551
|
-
}, _Tokenizer_reset = function _Tokenizer_reset() {
|
|
8552
|
-
const mark = __classPrivateFieldGet(this, _Tokenizer_markStack, "f").pop();
|
|
8553
|
-
if (!mark) {
|
|
8554
|
-
throw new Error('No mark to reset to');
|
|
8555
|
-
}
|
|
8556
|
-
__classPrivateFieldGet(this, _Tokenizer_pos, "f").index = mark.index;
|
|
8557
|
-
__classPrivateFieldGet(this, _Tokenizer_pos, "f").line = mark.line;
|
|
8558
|
-
__classPrivateFieldGet(this, _Tokenizer_pos, "f").column = mark.column;
|
|
8559
|
-
}, _Tokenizer_advance = function _Tokenizer_advance() {
|
|
8560
|
-
__classPrivateFieldGet(this, _Tokenizer_pos, "f").index++;
|
|
8561
|
-
if (__classPrivateFieldGet(this, _Tokenizer_instances, "m", _Tokenizer_curr).call(this) === '\n') {
|
|
8562
|
-
__classPrivateFieldGet(this, _Tokenizer_pos, "f").line++;
|
|
8563
|
-
__classPrivateFieldGet(this, _Tokenizer_pos, "f").column = 0;
|
|
8457
|
+
peekToken() {
|
|
8458
|
+
this.mark();
|
|
8459
|
+
const token = this.consumeToken();
|
|
8460
|
+
this.reset();
|
|
8461
|
+
return token;
|
|
8564
8462
|
}
|
|
8565
|
-
|
|
8566
|
-
|
|
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();
|
|
8567
8496
|
}
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
if (!mark) {
|
|
8571
|
-
throw new Error('No mark for token');
|
|
8497
|
+
consumeWhitespace() {
|
|
8498
|
+
this.consumeWhile(() => this.curr().match(/\s/));
|
|
8572
8499
|
}
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
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
|
+
}
|
|
8579
8633
|
function isUnitToken(token) {
|
|
8580
8634
|
if (token) {
|
|
8581
8635
|
if (token.id === 'String') {
|
|
@@ -10960,10 +11014,8 @@
|
|
|
10960
11014
|
return new Tokenizer(str, FHIRPATH_KEYWORDS, MAPPING_LANGUAGE_OPERATORS$1).tokenize();
|
|
10961
11015
|
}
|
|
10962
11016
|
|
|
10963
|
-
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;
|
|
10964
11017
|
class StructureMapParser {
|
|
10965
11018
|
constructor(parser) {
|
|
10966
|
-
_StructureMapParser_instances.add(this);
|
|
10967
11019
|
this.parser = parser;
|
|
10968
11020
|
this.structureMap = { resourceType: 'StructureMap' };
|
|
10969
11021
|
}
|
|
@@ -10978,16 +11030,16 @@
|
|
|
10978
11030
|
const next = this.parser.peek()?.value;
|
|
10979
11031
|
switch (next) {
|
|
10980
11032
|
case 'uses':
|
|
10981
|
-
|
|
11033
|
+
this.parseUses();
|
|
10982
11034
|
break;
|
|
10983
11035
|
case 'imports':
|
|
10984
|
-
|
|
11036
|
+
this.parseImport();
|
|
10985
11037
|
break;
|
|
10986
11038
|
case 'group':
|
|
10987
|
-
|
|
11039
|
+
this.parseGroup();
|
|
10988
11040
|
break;
|
|
10989
11041
|
case 'conceptmap':
|
|
10990
|
-
|
|
11042
|
+
this.parseConceptMap();
|
|
10991
11043
|
break;
|
|
10992
11044
|
default:
|
|
10993
11045
|
throw new Error(`Unexpected token: ${next}`);
|
|
@@ -10995,263 +11047,280 @@
|
|
|
10995
11047
|
}
|
|
10996
11048
|
return this.structureMap;
|
|
10997
11049
|
}
|
|
10998
|
-
|
|
10999
|
-
|
|
11000
|
-
|
|
11001
|
-
|
|
11002
|
-
|
|
11003
|
-
|
|
11004
|
-
|
|
11005
|
-
|
|
11006
|
-
|
|
11007
|
-
result.alias = this.parser.consume('Symbol').value;
|
|
11008
|
-
}
|
|
11009
|
-
this.parser.consume('Symbol', 'as');
|
|
11010
|
-
result.mode = this.parser.consume().value;
|
|
11011
|
-
if (!this.structureMap.structure) {
|
|
11012
|
-
this.structureMap.structure = [];
|
|
11013
|
-
}
|
|
11014
|
-
this.structureMap.structure.push(result);
|
|
11015
|
-
}, _StructureMapParser_parseImport = function _StructureMapParser_parseImport() {
|
|
11016
|
-
this.parser.consume('Symbol', 'imports');
|
|
11017
|
-
if (!this.structureMap.import) {
|
|
11018
|
-
this.structureMap.import = [];
|
|
11019
|
-
}
|
|
11020
|
-
this.structureMap.import.push(this.parser.consume('String').value);
|
|
11021
|
-
}, _StructureMapParser_parseGroup = function _StructureMapParser_parseGroup() {
|
|
11022
|
-
// 'group' identifier parameters extends? typeMode? rules
|
|
11023
|
-
// group tutorial(source src : TLeft, target tgt : TRight) {
|
|
11024
|
-
const result = {};
|
|
11025
|
-
this.parser.consume('Symbol', 'group');
|
|
11026
|
-
result.name = this.parser.consume('Symbol').value;
|
|
11027
|
-
result.input = __classPrivateFieldGet(this, _StructureMapParser_instances, "m", _StructureMapParser_parseParameters).call(this);
|
|
11028
|
-
if (this.parser.peek()?.value === 'extends') {
|
|
11029
|
-
this.parser.consume('Symbol', 'extends');
|
|
11030
|
-
result.extends = this.parser.consume('Symbol').value;
|
|
11031
|
-
}
|
|
11032
|
-
if (this.parser.peek()?.value === '<<') {
|
|
11033
|
-
this.parser.consume('<<');
|
|
11034
|
-
result.typeMode = this.parser.consume().value;
|
|
11035
|
-
if (this.parser.peek()?.value === '+') {
|
|
11036
|
-
this.parser.consume('+');
|
|
11037
|
-
result.typeMode = 'type-and-types';
|
|
11038
|
-
}
|
|
11039
|
-
this.parser.consume('>>');
|
|
11040
|
-
}
|
|
11041
|
-
else {
|
|
11042
|
-
result.typeMode = 'none';
|
|
11043
|
-
}
|
|
11044
|
-
result.rule = __classPrivateFieldGet(this, _StructureMapParser_instances, "m", _StructureMapParser_parseRules).call(this);
|
|
11045
|
-
if (!this.structureMap.group) {
|
|
11046
|
-
this.structureMap.group = [];
|
|
11047
|
-
}
|
|
11048
|
-
this.structureMap.group.push(result);
|
|
11049
|
-
}, _StructureMapParser_parseParameters = function _StructureMapParser_parseParameters() {
|
|
11050
|
-
const parameters = [];
|
|
11051
|
-
this.parser.consume('(');
|
|
11052
|
-
while (this.parser.hasMore() && this.parser.peek()?.value !== ')') {
|
|
11053
|
-
parameters.push(__classPrivateFieldGet(this, _StructureMapParser_instances, "m", _StructureMapParser_parseParameter).call(this));
|
|
11054
|
-
if (this.parser.peek()?.value === ',') {
|
|
11055
|
-
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;
|
|
11056
11059
|
}
|
|
11057
|
-
|
|
11058
|
-
|
|
11059
|
-
|
|
11060
|
-
|
|
11061
|
-
|
|
11062
|
-
|
|
11063
|
-
|
|
11064
|
-
|
|
11065
|
-
|
|
11066
|
-
|
|
11067
|
-
|
|
11068
|
-
|
|
11069
|
-
|
|
11070
|
-
}
|
|
11071
|
-
|
|
11072
|
-
|
|
11073
|
-
|
|
11074
|
-
|
|
11075
|
-
|
|
11076
|
-
|
|
11077
|
-
|
|
11078
|
-
|
|
11079
|
-
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
|
|
11085
|
-
|
|
11086
|
-
|
|
11087
|
-
|
|
11088
|
-
|
|
11089
|
-
|
|
11090
|
-
if (this.parser.peek()?.id === '{') {
|
|
11091
|
-
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('>>');
|
|
11092
11093
|
}
|
|
11093
11094
|
else {
|
|
11094
|
-
result.
|
|
11095
|
+
result.typeMode = 'none';
|
|
11095
11096
|
}
|
|
11097
|
+
result.rule = this.parseRules();
|
|
11098
|
+
if (!this.structureMap.group) {
|
|
11099
|
+
this.structureMap.group = [];
|
|
11100
|
+
}
|
|
11101
|
+
this.structureMap.group.push(result);
|
|
11096
11102
|
}
|
|
11097
|
-
|
|
11098
|
-
|
|
11099
|
-
|
|
11100
|
-
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
this.parser.consume(',');
|
|
11109
|
-
sources.push(__classPrivateFieldGet(this, _StructureMapParser_instances, "m", _StructureMapParser_parseRuleSource).call(this));
|
|
11110
|
-
}
|
|
11111
|
-
return sources;
|
|
11112
|
-
}, _StructureMapParser_parseRuleSource = function _StructureMapParser_parseRuleSource() {
|
|
11113
|
-
const result = {};
|
|
11114
|
-
const context = __classPrivateFieldGet(this, _StructureMapParser_instances, "m", _StructureMapParser_parseRuleContext).call(this);
|
|
11115
|
-
if (context.includes('.')) {
|
|
11116
|
-
const parts = context.split('.');
|
|
11117
|
-
result.context = parts[0];
|
|
11118
|
-
result.element = parts[1];
|
|
11119
|
-
}
|
|
11120
|
-
else {
|
|
11121
|
-
result.context = context;
|
|
11122
|
-
}
|
|
11123
|
-
if (this.parser.hasMore() && this.parser.peek()?.value === ':') {
|
|
11124
|
-
this.parser.consume(':');
|
|
11125
|
-
result.type = this.parser.consume().value;
|
|
11126
|
-
}
|
|
11127
|
-
if (this.parser.hasMore() && this.parser.peek()?.value === 'default') {
|
|
11128
|
-
this.parser.consume('default');
|
|
11129
|
-
this.parser.consumeAndParse();
|
|
11130
|
-
}
|
|
11131
|
-
if (this.parser.peek()?.value === 'first' ||
|
|
11132
|
-
this.parser.peek()?.value === 'not_first' ||
|
|
11133
|
-
this.parser.peek()?.value === 'last' ||
|
|
11134
|
-
this.parser.peek()?.value === 'not_last' ||
|
|
11135
|
-
this.parser.peek()?.value === 'only_one') {
|
|
11136
|
-
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;
|
|
11137
11114
|
}
|
|
11138
|
-
|
|
11139
|
-
|
|
11140
|
-
|
|
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;
|
|
11141
11127
|
}
|
|
11142
|
-
|
|
11143
|
-
|
|
11144
|
-
|
|
11145
|
-
|
|
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;
|
|
11146
11162
|
}
|
|
11147
|
-
|
|
11148
|
-
this.
|
|
11149
|
-
|
|
11150
|
-
|
|
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;
|
|
11151
11170
|
}
|
|
11152
|
-
|
|
11153
|
-
|
|
11154
|
-
|
|
11155
|
-
|
|
11156
|
-
|
|
11157
|
-
|
|
11158
|
-
|
|
11159
|
-
|
|
11160
|
-
|
|
11161
|
-
|
|
11162
|
-
|
|
11163
|
-
|
|
11164
|
-
|
|
11165
|
-
|
|
11166
|
-
|
|
11167
|
-
|
|
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;
|
|
11168
11212
|
}
|
|
11169
|
-
|
|
11170
|
-
|
|
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;
|
|
11171
11220
|
}
|
|
11172
|
-
|
|
11173
|
-
|
|
11174
|
-
|
|
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;
|
|
11175
11248
|
}
|
|
11176
|
-
|
|
11177
|
-
|
|
11178
|
-
|
|
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
|
+
}
|
|
11179
11264
|
}
|
|
11180
|
-
|
|
11181
|
-
|
|
11182
|
-
this.parser.peek()?.value === 'last' ||
|
|
11183
|
-
this.parser.peek()?.value === 'collate') {
|
|
11184
|
-
result.listMode = [this.parser.consume().value];
|
|
11265
|
+
parseRuleTargetSymbol(result, literalAtom) {
|
|
11266
|
+
result.parameter = [{ valueId: literalAtom.name }];
|
|
11185
11267
|
}
|
|
11186
|
-
|
|
11187
|
-
|
|
11188
|
-
|
|
11189
|
-
|
|
11190
|
-
|
|
11191
|
-
|
|
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
|
+
}
|
|
11192
11284
|
}
|
|
11193
|
-
|
|
11194
|
-
|
|
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
|
+
}
|
|
11195
11299
|
}
|
|
11196
|
-
|
|
11197
|
-
|
|
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;
|
|
11198
11307
|
}
|
|
11199
|
-
|
|
11200
|
-
|
|
11201
|
-
|
|
11202
|
-
|
|
11203
|
-
|
|
11204
|
-
|
|
11205
|
-
|
|
11206
|
-
|
|
11207
|
-
case 'create':
|
|
11208
|
-
result.parameter = [
|
|
11209
|
-
{
|
|
11210
|
-
valueString: (functionAtom.args?.[0]).value.value,
|
|
11211
|
-
},
|
|
11212
|
-
];
|
|
11213
|
-
break;
|
|
11214
|
-
case 'translate':
|
|
11215
|
-
result.parameter = [{}];
|
|
11216
|
-
break;
|
|
11217
|
-
default:
|
|
11218
|
-
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
|
+
];
|
|
11219
11316
|
}
|
|
11220
|
-
|
|
11221
|
-
|
|
11222
|
-
|
|
11223
|
-
|
|
11224
|
-
|
|
11225
|
-
case 'decimal':
|
|
11226
|
-
result.parameter = [{ valueDecimal: literalAtom.value.value }];
|
|
11227
|
-
break;
|
|
11228
|
-
case 'string':
|
|
11229
|
-
result.parameter = [{ valueString: literalAtom.value.value }];
|
|
11230
|
-
break;
|
|
11231
|
-
default:
|
|
11232
|
-
throw new Error('Unknown target literal type: ' + literalAtom.value.type);
|
|
11233
|
-
}
|
|
11234
|
-
}, _StructureMapParser_parseRuleContext = function _StructureMapParser_parseRuleContext() {
|
|
11235
|
-
let identifier = this.parser.consume().value;
|
|
11236
|
-
while (this.parser.peek()?.value === '.') {
|
|
11237
|
-
this.parser.consume('.');
|
|
11238
|
-
identifier += '.' + this.parser.consume().value;
|
|
11239
|
-
}
|
|
11240
|
-
return identifier;
|
|
11241
|
-
}, _StructureMapParser_parseRuleDependents = function _StructureMapParser_parseRuleDependents() {
|
|
11242
|
-
const atom = this.parser.consumeAndParse(100 /* OperatorPrecedence.Arrow */);
|
|
11243
|
-
return [
|
|
11244
|
-
{
|
|
11245
|
-
name: atom.name,
|
|
11246
|
-
variable: atom.args.map((arg) => arg.name),
|
|
11247
|
-
},
|
|
11248
|
-
];
|
|
11249
|
-
}, _StructureMapParser_parseConceptMap = function _StructureMapParser_parseConceptMap() {
|
|
11250
|
-
while (this.parser.peek()?.value !== '}') {
|
|
11251
|
-
this.parser.consume();
|
|
11317
|
+
parseConceptMap() {
|
|
11318
|
+
while (this.parser.peek()?.value !== '}') {
|
|
11319
|
+
this.parser.consume();
|
|
11320
|
+
}
|
|
11321
|
+
this.parser.consume('}');
|
|
11252
11322
|
}
|
|
11253
|
-
|
|
11254
|
-
};
|
|
11323
|
+
}
|
|
11255
11324
|
const fhirPathParserBuilder$1 = initFhirPathParserBuilder()
|
|
11256
11325
|
.registerInfix('->', { precedence: 100 /* OperatorPrecedence.Arrow */ })
|
|
11257
11326
|
.registerInfix(';', { precedence: 200 /* OperatorPrecedence.Semicolon */ });
|
|
@@ -11562,7 +11631,6 @@
|
|
|
11562
11631
|
}
|
|
11563
11632
|
}
|
|
11564
11633
|
|
|
11565
|
-
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;
|
|
11566
11634
|
/*
|
|
11567
11635
|
* This file provides schema validation utilities for FHIR JSON objects.
|
|
11568
11636
|
*
|
|
@@ -11724,14 +11792,11 @@
|
|
|
11724
11792
|
}
|
|
11725
11793
|
class FhirSchemaValidator {
|
|
11726
11794
|
constructor(root) {
|
|
11727
|
-
|
|
11728
|
-
|
|
11729
|
-
_FhirSchemaValidator_root.set(this, void 0);
|
|
11730
|
-
__classPrivateFieldSet(this, _FhirSchemaValidator_issues, [], "f");
|
|
11731
|
-
__classPrivateFieldSet(this, _FhirSchemaValidator_root, root, "f");
|
|
11795
|
+
this.issues = [];
|
|
11796
|
+
this.root = root;
|
|
11732
11797
|
}
|
|
11733
11798
|
validate() {
|
|
11734
|
-
const resource =
|
|
11799
|
+
const resource = this.root;
|
|
11735
11800
|
if (!resource) {
|
|
11736
11801
|
throw new OperationOutcomeError(validationError('Resource is null'));
|
|
11737
11802
|
}
|
|
@@ -11740,142 +11805,172 @@
|
|
|
11740
11805
|
throw new OperationOutcomeError(validationError('Missing resource type'));
|
|
11741
11806
|
}
|
|
11742
11807
|
// Check for "null" once for the entire object hierarchy
|
|
11743
|
-
checkForNull(resource, '',
|
|
11744
|
-
|
|
11745
|
-
if (
|
|
11808
|
+
checkForNull(resource, '', this.issues);
|
|
11809
|
+
this.validateObject(toTypedValue(resource), resourceType);
|
|
11810
|
+
if (this.issues.length > 0) {
|
|
11746
11811
|
throw new OperationOutcomeError({
|
|
11747
11812
|
resourceType: 'OperationOutcome',
|
|
11748
|
-
issue:
|
|
11813
|
+
issue: this.issues,
|
|
11749
11814
|
});
|
|
11750
11815
|
}
|
|
11751
11816
|
}
|
|
11752
|
-
|
|
11753
|
-
|
|
11754
|
-
|
|
11755
|
-
|
|
11756
|
-
|
|
11757
|
-
|
|
11758
|
-
|
|
11759
|
-
|
|
11760
|
-
__classPrivateFieldGet(this, _FhirSchemaValidator_instances, "m", _FhirSchemaValidator_checkAdditionalProperties).call(this, path, typedValue, propertyDefinitions);
|
|
11761
|
-
}, _FhirSchemaValidator_checkProperties = function _FhirSchemaValidator_checkProperties(path, propertyDefinitions, typedValue) {
|
|
11762
|
-
for (const [key, elementDefinition] of Object.entries(propertyDefinitions)) {
|
|
11763
|
-
__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);
|
|
11764
11825
|
}
|
|
11765
|
-
|
|
11766
|
-
|
|
11767
|
-
|
|
11768
|
-
if (isEmpty(value)) {
|
|
11769
|
-
if (elementDefinition.min !== undefined && elementDefinition.min > 0) {
|
|
11770
|
-
__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);
|
|
11771
11829
|
}
|
|
11772
|
-
return;
|
|
11773
11830
|
}
|
|
11774
|
-
|
|
11775
|
-
|
|
11776
|
-
|
|
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
|
+
}
|
|
11777
11838
|
return;
|
|
11778
11839
|
}
|
|
11779
|
-
|
|
11780
|
-
|
|
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);
|
|
11781
11855
|
}
|
|
11782
11856
|
}
|
|
11783
|
-
|
|
11784
|
-
if (
|
|
11785
|
-
|
|
11857
|
+
checkPropertyValue(path, elementDefinition, typedValue) {
|
|
11858
|
+
if (typedValue.value === null) {
|
|
11859
|
+
// Null handled separately
|
|
11786
11860
|
return;
|
|
11787
11861
|
}
|
|
11788
|
-
|
|
11789
|
-
|
|
11790
|
-
|
|
11791
|
-
|
|
11792
|
-
|
|
11793
|
-
|
|
11794
|
-
}
|
|
11795
|
-
if (isLowerCase(typedValue.type.charAt(0))) {
|
|
11796
|
-
__classPrivateFieldGet(this, _FhirSchemaValidator_instances, "m", _FhirSchemaValidator_validatePrimitiveType).call(this, elementDefinition, typedValue);
|
|
11797
|
-
}
|
|
11798
|
-
else {
|
|
11799
|
-
__classPrivateFieldGet(this, _FhirSchemaValidator_instances, "m", _FhirSchemaValidator_validateObject).call(this, typedValue, path);
|
|
11800
|
-
}
|
|
11801
|
-
}, _FhirSchemaValidator_validatePrimitiveType = function _FhirSchemaValidator_validatePrimitiveType(elementDefinition, typedValue) {
|
|
11802
|
-
const { type, value } = typedValue;
|
|
11803
|
-
if (value === null) {
|
|
11804
|
-
// Null handled separately, so this code should never be reached
|
|
11805
|
-
// Leaving this check in place for now, in case we change the null handling
|
|
11806
|
-
return;
|
|
11807
|
-
}
|
|
11808
|
-
// First, make sure the value is the correct JS type
|
|
11809
|
-
const expectedType = fhirTypeToJsType[typedValue.type];
|
|
11810
|
-
if (typeof value !== expectedType) {
|
|
11811
|
-
__classPrivateFieldGet(this, _FhirSchemaValidator_instances, "m", _FhirSchemaValidator_createIssue).call(this, elementDefinition, 'Invalid type for ' + type);
|
|
11812
|
-
return;
|
|
11813
|
-
}
|
|
11814
|
-
// Then, perform additional checks for specialty types
|
|
11815
|
-
if (expectedType === 'string') {
|
|
11816
|
-
__classPrivateFieldGet(this, _FhirSchemaValidator_instances, "m", _FhirSchemaValidator_validateString).call(this, elementDefinition, type, value);
|
|
11817
|
-
}
|
|
11818
|
-
else if (expectedType === 'number') {
|
|
11819
|
-
__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
|
+
}
|
|
11820
11868
|
}
|
|
11821
|
-
|
|
11822
|
-
|
|
11823
|
-
|
|
11824
|
-
|
|
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
|
+
}
|
|
11825
11889
|
}
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
|
|
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
|
+
}
|
|
11833
11903
|
}
|
|
11834
11904
|
}
|
|
11835
11905
|
}
|
|
11836
|
-
|
|
11837
|
-
|
|
11838
|
-
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
|
|
11843
|
-
|
|
11844
|
-
|
|
11845
|
-
|
|
11846
|
-
|
|
11847
|
-
|
|
11848
|
-
|
|
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
|
+
}
|
|
11849
11920
|
}
|
|
11850
|
-
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
|
|
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
|
+
}
|
|
11854
11926
|
}
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
|
|
11861
|
-
|
|
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
|
+
}
|
|
11862
11942
|
}
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
|
|
11866
|
-
|
|
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;
|
|
11867
11969
|
}
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
if (!(primitiveKey in typedValue.value)) {
|
|
11871
|
-
return false;
|
|
11970
|
+
createIssue(elementDefinition, message) {
|
|
11971
|
+
this.issues.push(createStructureIssue(elementDefinition.path, message));
|
|
11872
11972
|
}
|
|
11873
|
-
|
|
11874
|
-
__classPrivateFieldGet(this, _FhirSchemaValidator_instances, "m", _FhirSchemaValidator_validateObject).call(this, { type: 'Element', value: typedValue.value[key] }, path);
|
|
11875
|
-
return true;
|
|
11876
|
-
}, _FhirSchemaValidator_createIssue = function _FhirSchemaValidator_createIssue(elementDefinition, message) {
|
|
11877
|
-
__classPrivateFieldGet(this, _FhirSchemaValidator_issues, "f").push(createStructureIssue(elementDefinition.path, message));
|
|
11878
|
-
};
|
|
11973
|
+
}
|
|
11879
11974
|
function isIntegerType(propertyType) {
|
|
11880
11975
|
return (propertyType === exports.PropertyType.integer ||
|
|
11881
11976
|
propertyType === exports.PropertyType.positiveInt ||
|