@medplum/core 2.0.14 → 2.0.16
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 +1244 -1101
- 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 +500 -413
- 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/format.mjs +24 -15
- package/dist/esm/format.mjs.map +1 -1
- package/dist/esm/index.min.mjs +1 -1
- package/dist/esm/index.mjs +2 -2
- package/dist/esm/jwt.mjs +2 -9
- package/dist/esm/jwt.mjs.map +1 -1
- package/dist/esm/outcomes.mjs +15 -1
- package/dist/esm/outcomes.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/search/search.mjs +15 -9
- package/dist/esm/search/search.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 +153 -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/format.d.ts +3 -1
- package/dist/types/outcomes.d.ts +1 -0
- package/dist/types/readablepromise.d.ts +4 -1
- package/dist/types/schema.d.ts +33 -1
- package/dist/types/storage.d.ts +2 -2
- package/package.json +1 -1
- package/dist/esm/node_modules/tslib/tslib.es6.mjs +0 -30
- package/dist/esm/node_modules/tslib/tslib.es6.mjs.map +0 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -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.
|
|
@@ -354,8 +322,9 @@
|
|
|
354
322
|
return capitalize(builder.join(' ').trim());
|
|
355
323
|
}
|
|
356
324
|
/**
|
|
357
|
-
* Returns a human-readable string for a FHIR Range datatype, taking into account
|
|
325
|
+
* Returns a human-readable string for a FHIR Range datatype, taking into account one-sided ranges
|
|
358
326
|
* @param range A FHIR Range element
|
|
327
|
+
* @param precision Number of decimal places to display in the rendered quantity values
|
|
359
328
|
* @param exclusive If true, one-sided ranges will be rendered with the '>' or '<' bounds rather than '>=' or '<='
|
|
360
329
|
* @returns A human-readable string representation of the Range
|
|
361
330
|
*/
|
|
@@ -363,33 +332,41 @@
|
|
|
363
332
|
if (exclusive && precision === undefined) {
|
|
364
333
|
throw new Error('Precision must be specified for exclusive ranges');
|
|
365
334
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
335
|
+
// Extract high and low range endpoints, explicitly ignoring any comparator
|
|
336
|
+
// since Range uses SimpleQuantity variants (see http://www.hl7.org/fhir/datatypes.html#Range)
|
|
337
|
+
const low = range?.low && { ...range.low, comparator: undefined };
|
|
338
|
+
const high = range?.high && { ...range.high, comparator: undefined };
|
|
339
|
+
if (low?.value === undefined && high?.value === undefined) {
|
|
369
340
|
return '';
|
|
370
341
|
}
|
|
371
|
-
if (
|
|
342
|
+
if (low?.value !== undefined && high?.value === undefined) {
|
|
343
|
+
// Lower bound only
|
|
372
344
|
if (exclusive && precision !== undefined) {
|
|
373
|
-
|
|
374
|
-
return `> ${formatQuantity(
|
|
345
|
+
low.value = preciseDecrement(low.value, precision);
|
|
346
|
+
return `> ${formatQuantity(low, precision)}`;
|
|
375
347
|
}
|
|
376
|
-
return `>= ${formatQuantity(
|
|
348
|
+
return `>= ${formatQuantity(low, precision)}`;
|
|
377
349
|
}
|
|
378
|
-
if (
|
|
350
|
+
else if (low?.value === undefined && high?.value !== undefined) {
|
|
351
|
+
// Upper bound only
|
|
379
352
|
if (exclusive && precision !== undefined) {
|
|
380
|
-
|
|
381
|
-
return `< ${formatQuantity(
|
|
353
|
+
high.value = preciseIncrement(high.value, precision);
|
|
354
|
+
return `< ${formatQuantity(high, precision)}`;
|
|
382
355
|
}
|
|
383
|
-
return `<= ${formatQuantity(
|
|
356
|
+
return `<= ${formatQuantity(high, precision)}`;
|
|
384
357
|
}
|
|
385
|
-
|
|
386
|
-
|
|
358
|
+
else {
|
|
359
|
+
// Double-sided range
|
|
360
|
+
if (low?.unit === high?.unit) {
|
|
361
|
+
delete low?.unit; // Format like "X - Y units" instead of "X units - Y units"
|
|
362
|
+
}
|
|
363
|
+
return `${formatQuantity(low, precision)} - ${formatQuantity(high, precision)}`;
|
|
387
364
|
}
|
|
388
|
-
return `${formatQuantity(low, precision)} - ${formatQuantity(high, precision)}`;
|
|
389
365
|
}
|
|
390
366
|
/**
|
|
391
367
|
* Returns a human-readable string for a FHIR Quantity datatype, taking into account units and comparators
|
|
392
368
|
* @param quantity A FHIR Quantity element
|
|
369
|
+
* @param precision Number of decimal places to display in the rendered quantity values
|
|
393
370
|
* @returns A human-readable string representation of the Quantity
|
|
394
371
|
*/
|
|
395
372
|
function formatQuantity(quantity, precision) {
|
|
@@ -1141,20 +1118,18 @@
|
|
|
1141
1118
|
/*
|
|
1142
1119
|
* Based on: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget
|
|
1143
1120
|
*/
|
|
1144
|
-
var _EventTarget_listeners;
|
|
1145
1121
|
class EventTarget {
|
|
1146
1122
|
constructor() {
|
|
1147
|
-
|
|
1148
|
-
__classPrivateFieldSet(this, _EventTarget_listeners, {}, "f");
|
|
1123
|
+
this.listeners = {};
|
|
1149
1124
|
}
|
|
1150
1125
|
addEventListener(type, callback) {
|
|
1151
|
-
if (!
|
|
1152
|
-
|
|
1126
|
+
if (!this.listeners[type]) {
|
|
1127
|
+
this.listeners[type] = [];
|
|
1153
1128
|
}
|
|
1154
|
-
|
|
1129
|
+
this.listeners[type].push(callback);
|
|
1155
1130
|
}
|
|
1156
1131
|
removeEventListeneer(type, callback) {
|
|
1157
|
-
const array =
|
|
1132
|
+
const array = this.listeners[type];
|
|
1158
1133
|
if (!array) {
|
|
1159
1134
|
return;
|
|
1160
1135
|
}
|
|
@@ -1166,14 +1141,44 @@
|
|
|
1166
1141
|
}
|
|
1167
1142
|
}
|
|
1168
1143
|
dispatchEvent(event) {
|
|
1169
|
-
const array =
|
|
1144
|
+
const array = this.listeners[event.type];
|
|
1170
1145
|
if (array) {
|
|
1171
1146
|
array.forEach((listener) => listener.call(this, event));
|
|
1172
1147
|
}
|
|
1173
1148
|
return !event.defaultPrevented;
|
|
1174
1149
|
}
|
|
1175
1150
|
}
|
|
1176
|
-
|
|
1151
|
+
|
|
1152
|
+
/**
|
|
1153
|
+
* Decodes a base64 string.
|
|
1154
|
+
* Handles both browser and Node environments.
|
|
1155
|
+
* @param data The base-64 encoded input string.
|
|
1156
|
+
* @returns The decoded string.
|
|
1157
|
+
*/
|
|
1158
|
+
function decodeBase64(data) {
|
|
1159
|
+
if (typeof window !== 'undefined') {
|
|
1160
|
+
return window.atob(data);
|
|
1161
|
+
}
|
|
1162
|
+
if (typeof Buffer !== 'undefined') {
|
|
1163
|
+
return Buffer.from(data, 'base64').toString('binary');
|
|
1164
|
+
}
|
|
1165
|
+
throw new Error('Unable to decode base64');
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Encodes a base64 string.
|
|
1169
|
+
* Handles both browser and Node environments.
|
|
1170
|
+
* @param data The unencoded input string.
|
|
1171
|
+
* @returns The base-64 encoded string.
|
|
1172
|
+
*/
|
|
1173
|
+
function encodeBase64(data) {
|
|
1174
|
+
if (typeof window !== 'undefined') {
|
|
1175
|
+
return window.btoa(data);
|
|
1176
|
+
}
|
|
1177
|
+
if (typeof Buffer !== 'undefined') {
|
|
1178
|
+
return Buffer.from(data, 'binary').toString('base64');
|
|
1179
|
+
}
|
|
1180
|
+
throw new Error('Unable to encode base64');
|
|
1181
|
+
}
|
|
1177
1182
|
|
|
1178
1183
|
/**
|
|
1179
1184
|
* Decodes a section of a JWT.
|
|
@@ -1190,15 +1195,6 @@
|
|
|
1190
1195
|
const jsonPayload = decodeURIComponent(uriEncodedPayload);
|
|
1191
1196
|
return JSON.parse(jsonPayload);
|
|
1192
1197
|
}
|
|
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
1198
|
/**
|
|
1203
1199
|
* Parses the JWT payload.
|
|
1204
1200
|
* @param token JWT token
|
|
@@ -1216,6 +1212,7 @@
|
|
|
1216
1212
|
const UNAUTHORIZED_ID = 'unauthorized';
|
|
1217
1213
|
const FORBIDDEN_ID = 'forbidden';
|
|
1218
1214
|
const TOO_MANY_REQUESTS_ID = 'too-many-requests';
|
|
1215
|
+
const ACCEPTED_ID = 'accepted';
|
|
1219
1216
|
const allOk = {
|
|
1220
1217
|
resourceType: 'OperationOutcome',
|
|
1221
1218
|
id: OK_ID,
|
|
@@ -1320,6 +1317,19 @@
|
|
|
1320
1317
|
},
|
|
1321
1318
|
],
|
|
1322
1319
|
};
|
|
1320
|
+
const accepted = {
|
|
1321
|
+
resourceType: 'OperationOutcome',
|
|
1322
|
+
id: ACCEPTED_ID,
|
|
1323
|
+
issue: [
|
|
1324
|
+
{
|
|
1325
|
+
severity: 'information',
|
|
1326
|
+
code: 'informational',
|
|
1327
|
+
details: {
|
|
1328
|
+
text: 'Accepted',
|
|
1329
|
+
},
|
|
1330
|
+
},
|
|
1331
|
+
],
|
|
1332
|
+
};
|
|
1323
1333
|
function badRequest(details, expression) {
|
|
1324
1334
|
return {
|
|
1325
1335
|
resourceType: 'OperationOutcome',
|
|
@@ -1460,7 +1470,7 @@
|
|
|
1460
1470
|
return strs.length > 0 ? strs.join('; ') : 'Unknown error';
|
|
1461
1471
|
}
|
|
1462
1472
|
|
|
1463
|
-
var
|
|
1473
|
+
var _a;
|
|
1464
1474
|
/**
|
|
1465
1475
|
* The ReadablePromise class wraps a request promise suitable for React Suspense.
|
|
1466
1476
|
* See: https://blog.logrocket.com/react-suspense-data-fetching/#wrappromise-js
|
|
@@ -1469,33 +1479,30 @@
|
|
|
1469
1479
|
class ReadablePromise {
|
|
1470
1480
|
constructor(requestPromise) {
|
|
1471
1481
|
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");
|
|
1482
|
+
this.status = 'pending';
|
|
1483
|
+
this.suspender = requestPromise.then((res) => {
|
|
1484
|
+
this.status = 'success';
|
|
1485
|
+
this.response = res;
|
|
1479
1486
|
return res;
|
|
1480
1487
|
}, (err) => {
|
|
1481
|
-
|
|
1482
|
-
|
|
1488
|
+
this.status = 'error';
|
|
1489
|
+
this.error = err;
|
|
1483
1490
|
throw err;
|
|
1484
|
-
})
|
|
1491
|
+
});
|
|
1485
1492
|
}
|
|
1486
1493
|
/**
|
|
1487
1494
|
* Returns true if the promise is pending.
|
|
1488
1495
|
* @returns True if the Promise is pending.
|
|
1489
1496
|
*/
|
|
1490
1497
|
isPending() {
|
|
1491
|
-
return
|
|
1498
|
+
return this.status === 'pending';
|
|
1492
1499
|
}
|
|
1493
1500
|
/**
|
|
1494
1501
|
* Returns true if the promise resolved successfully.
|
|
1495
1502
|
* @returns True if the Promise resolved successfully.
|
|
1496
1503
|
*/
|
|
1497
1504
|
isOk() {
|
|
1498
|
-
return
|
|
1505
|
+
return this.status === 'success';
|
|
1499
1506
|
}
|
|
1500
1507
|
/**
|
|
1501
1508
|
* Attempts to read the value of the promise.
|
|
@@ -1505,13 +1512,13 @@
|
|
|
1505
1512
|
* @returns The resolved value of the Promise.
|
|
1506
1513
|
*/
|
|
1507
1514
|
read() {
|
|
1508
|
-
switch (
|
|
1515
|
+
switch (this.status) {
|
|
1509
1516
|
case 'pending':
|
|
1510
|
-
throw
|
|
1517
|
+
throw this.suspender;
|
|
1511
1518
|
case 'error':
|
|
1512
|
-
throw
|
|
1519
|
+
throw this.error;
|
|
1513
1520
|
default:
|
|
1514
|
-
return
|
|
1521
|
+
return this.response;
|
|
1515
1522
|
}
|
|
1516
1523
|
}
|
|
1517
1524
|
/**
|
|
@@ -1521,7 +1528,7 @@
|
|
|
1521
1528
|
* @returns A Promise for the completion of which ever callback is executed.
|
|
1522
1529
|
*/
|
|
1523
1530
|
then(onfulfilled, onrejected) {
|
|
1524
|
-
return
|
|
1531
|
+
return this.suspender.then(onfulfilled, onrejected);
|
|
1525
1532
|
}
|
|
1526
1533
|
/**
|
|
1527
1534
|
* Attaches a callback for only the rejection of the Promise.
|
|
@@ -1529,7 +1536,7 @@
|
|
|
1529
1536
|
* @returns A Promise for the completion of the callback.
|
|
1530
1537
|
*/
|
|
1531
1538
|
catch(onrejected) {
|
|
1532
|
-
return
|
|
1539
|
+
return this.suspender.catch(onrejected);
|
|
1533
1540
|
}
|
|
1534
1541
|
/**
|
|
1535
1542
|
* Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The
|
|
@@ -1538,12 +1545,11 @@
|
|
|
1538
1545
|
* @returns A Promise for the completion of the callback.
|
|
1539
1546
|
*/
|
|
1540
1547
|
finally(onfinally) {
|
|
1541
|
-
return
|
|
1548
|
+
return this.suspender.finally(onfinally);
|
|
1542
1549
|
}
|
|
1543
1550
|
}
|
|
1544
|
-
|
|
1551
|
+
_a = Symbol.toStringTag;
|
|
1545
1552
|
|
|
1546
|
-
var _ClientStorage_storage, _MemoryStorage_data;
|
|
1547
1553
|
/**
|
|
1548
1554
|
* The ClientStorage class is a utility class for storing strings and objects.
|
|
1549
1555
|
*
|
|
@@ -1553,21 +1559,20 @@
|
|
|
1553
1559
|
*/
|
|
1554
1560
|
class ClientStorage {
|
|
1555
1561
|
constructor() {
|
|
1556
|
-
|
|
1557
|
-
__classPrivateFieldSet(this, _ClientStorage_storage, typeof localStorage !== 'undefined' ? localStorage : new MemoryStorage(), "f");
|
|
1562
|
+
this.storage = typeof localStorage !== 'undefined' ? localStorage : new MemoryStorage();
|
|
1558
1563
|
}
|
|
1559
1564
|
clear() {
|
|
1560
|
-
|
|
1565
|
+
this.storage.clear();
|
|
1561
1566
|
}
|
|
1562
1567
|
getString(key) {
|
|
1563
|
-
return
|
|
1568
|
+
return this.storage.getItem(key) || undefined;
|
|
1564
1569
|
}
|
|
1565
1570
|
setString(key, value) {
|
|
1566
1571
|
if (value) {
|
|
1567
|
-
|
|
1572
|
+
this.storage.setItem(key, value);
|
|
1568
1573
|
}
|
|
1569
1574
|
else {
|
|
1570
|
-
|
|
1575
|
+
this.storage.removeItem(key);
|
|
1571
1576
|
}
|
|
1572
1577
|
}
|
|
1573
1578
|
getObject(key) {
|
|
@@ -1578,58 +1583,55 @@
|
|
|
1578
1583
|
this.setString(key, value ? stringify(value) : undefined);
|
|
1579
1584
|
}
|
|
1580
1585
|
}
|
|
1581
|
-
_ClientStorage_storage = new WeakMap();
|
|
1582
1586
|
/**
|
|
1583
1587
|
* The MemoryStorage class is a minimal in-memory implementation of the Storage interface.
|
|
1584
1588
|
*/
|
|
1585
1589
|
class MemoryStorage {
|
|
1586
1590
|
constructor() {
|
|
1587
|
-
|
|
1588
|
-
__classPrivateFieldSet(this, _MemoryStorage_data, new Map(), "f");
|
|
1591
|
+
this.data = new Map();
|
|
1589
1592
|
}
|
|
1590
1593
|
/**
|
|
1591
1594
|
* Returns the number of key/value pairs.
|
|
1592
1595
|
*/
|
|
1593
1596
|
get length() {
|
|
1594
|
-
return
|
|
1597
|
+
return this.data.size;
|
|
1595
1598
|
}
|
|
1596
1599
|
/**
|
|
1597
1600
|
* Removes all key/value pairs, if there are any.
|
|
1598
1601
|
*/
|
|
1599
1602
|
clear() {
|
|
1600
|
-
|
|
1603
|
+
this.data.clear();
|
|
1601
1604
|
}
|
|
1602
1605
|
/**
|
|
1603
1606
|
* Returns the current value associated with the given key, or null if the given key does not exist.
|
|
1604
1607
|
*/
|
|
1605
1608
|
getItem(key) {
|
|
1606
|
-
return
|
|
1609
|
+
return this.data.get(key) ?? null;
|
|
1607
1610
|
}
|
|
1608
1611
|
/**
|
|
1609
1612
|
* Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
|
|
1610
1613
|
*/
|
|
1611
1614
|
setItem(key, value) {
|
|
1612
1615
|
if (value) {
|
|
1613
|
-
|
|
1616
|
+
this.data.set(key, value);
|
|
1614
1617
|
}
|
|
1615
1618
|
else {
|
|
1616
|
-
|
|
1619
|
+
this.data.delete(key);
|
|
1617
1620
|
}
|
|
1618
1621
|
}
|
|
1619
1622
|
/**
|
|
1620
1623
|
* Removes the key/value pair with the given key, if a key/value pair with the given key exists.
|
|
1621
1624
|
*/
|
|
1622
1625
|
removeItem(key) {
|
|
1623
|
-
|
|
1626
|
+
this.data.delete(key);
|
|
1624
1627
|
}
|
|
1625
1628
|
/**
|
|
1626
1629
|
* Returns the name of the nth key, or null if n is greater than or equal to the number of key/value pairs.
|
|
1627
1630
|
*/
|
|
1628
1631
|
key(index) {
|
|
1629
|
-
return Array.from(
|
|
1632
|
+
return Array.from(this.data.keys())[index];
|
|
1630
1633
|
}
|
|
1631
1634
|
}
|
|
1632
|
-
_MemoryStorage_data = new WeakMap();
|
|
1633
1635
|
|
|
1634
1636
|
var types = {
|
|
1635
1637
|
Element: {
|
|
@@ -6402,9 +6404,7 @@
|
|
|
6402
6404
|
const globalSchema = baseSchema;
|
|
6403
6405
|
|
|
6404
6406
|
// 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";
|
|
6407
|
+
const MEDPLUM_VERSION = "2.0.16-816ad9bf";
|
|
6408
6408
|
const DEFAULT_BASE_URL = 'https://api.medplum.com/';
|
|
6409
6409
|
const DEFAULT_RESOURCE_CACHE_SIZE = 1000;
|
|
6410
6410
|
const DEFAULT_CACHE_TIME = 60000; // 60 seconds
|
|
@@ -6412,6 +6412,35 @@
|
|
|
6412
6412
|
const FHIR_CONTENT_TYPE = 'application/fhir+json';
|
|
6413
6413
|
const PATCH_CONTENT_TYPE = 'application/json-patch+json';
|
|
6414
6414
|
const system = { resourceType: 'Device', id: 'system', deviceName: [{ name: 'System' }] };
|
|
6415
|
+
/**
|
|
6416
|
+
* OAuth 2.0 Grant Type Identifiers
|
|
6417
|
+
* Standard identifiers defined here: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#name-grant-types
|
|
6418
|
+
* Token exchange extension defined here: https://datatracker.ietf.org/doc/html/rfc8693
|
|
6419
|
+
*/
|
|
6420
|
+
exports.OAuthGrantType = void 0;
|
|
6421
|
+
(function (OAuthGrantType) {
|
|
6422
|
+
OAuthGrantType["ClientCredentials"] = "client_credentials";
|
|
6423
|
+
OAuthGrantType["AuthorizationCode"] = "authorization_code";
|
|
6424
|
+
OAuthGrantType["RefreshToken"] = "refresh_token";
|
|
6425
|
+
OAuthGrantType["TokenExchange"] = "urn:ietf:params:oauth:grant-type:token-exchange";
|
|
6426
|
+
})(exports.OAuthGrantType || (exports.OAuthGrantType = {}));
|
|
6427
|
+
/**
|
|
6428
|
+
* OAuth 2.0 Token Type Identifiers
|
|
6429
|
+
* See: https://datatracker.ietf.org/doc/html/rfc8693#name-token-type-identifiers
|
|
6430
|
+
*/
|
|
6431
|
+
exports.OAuthTokenType = void 0;
|
|
6432
|
+
(function (OAuthTokenType) {
|
|
6433
|
+
/** Indicates that the token is an OAuth 2.0 access token issued by the given authorization server. */
|
|
6434
|
+
OAuthTokenType["AccessToken"] = "urn:ietf:params:oauth:token-type:access_token";
|
|
6435
|
+
/** Indicates that the token is an OAuth 2.0 refresh token issued by the given authorization server. */
|
|
6436
|
+
OAuthTokenType["RefreshToken"] = "urn:ietf:params:oauth:token-type:refresh_token";
|
|
6437
|
+
/** Indicates that the token is an ID Token as defined in Section 2 of [OpenID.Core]. */
|
|
6438
|
+
OAuthTokenType["IdToken"] = "urn:ietf:params:oauth:token-type:id_token";
|
|
6439
|
+
/** Indicates that the token is a base64url-encoded SAML 1.1 [OASIS.saml-core-1.1] assertion. */
|
|
6440
|
+
OAuthTokenType["Saml1Token"] = "urn:ietf:params:oauth:token-type:saml1";
|
|
6441
|
+
/** Indicates that the token is a base64url-encoded SAML 2.0 [OASIS.saml-core-2.0-os] assertion. */
|
|
6442
|
+
OAuthTokenType["Saml2Token"] = "urn:ietf:params:oauth:token-type:saml2";
|
|
6443
|
+
})(exports.OAuthTokenType || (exports.OAuthTokenType = {}));
|
|
6415
6444
|
/**
|
|
6416
6445
|
* The MedplumClient class provides a client for the Medplum FHIR server.
|
|
6417
6446
|
*
|
|
@@ -6467,57 +6496,43 @@
|
|
|
6467
6496
|
class MedplumClient extends EventTarget {
|
|
6468
6497
|
constructor(options) {
|
|
6469
6498
|
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
6499
|
if (options?.baseUrl) {
|
|
6495
6500
|
if (!options.baseUrl.startsWith('http')) {
|
|
6496
6501
|
throw new Error('Base URL must start with http or https');
|
|
6497
6502
|
}
|
|
6498
6503
|
}
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6504
|
+
this.fetch = options?.fetch || getDefaultFetch();
|
|
6505
|
+
this.storage = options?.storage || new ClientStorage();
|
|
6506
|
+
this.createPdfImpl = options?.createPdf;
|
|
6507
|
+
this.baseUrl = ensureTrailingSlash(options?.baseUrl) || DEFAULT_BASE_URL;
|
|
6508
|
+
this.fhirBaseUrl = this.baseUrl + (ensureTrailingSlash(options?.fhirUrlPath) || 'fhir/R4/');
|
|
6509
|
+
this.clientId = options?.clientId || '';
|
|
6510
|
+
this.authorizeUrl = options?.authorizeUrl || this.baseUrl + 'oauth2/authorize';
|
|
6511
|
+
this.tokenUrl = options?.tokenUrl || this.baseUrl + 'oauth2/token';
|
|
6512
|
+
this.logoutUrl = options?.logoutUrl || this.baseUrl + 'oauth2/logout';
|
|
6513
|
+
this.onUnauthenticated = options?.onUnauthenticated;
|
|
6514
|
+
this.cacheTime = options?.cacheTime ?? DEFAULT_CACHE_TIME;
|
|
6515
|
+
if (this.cacheTime > 0) {
|
|
6516
|
+
this.requestCache = new LRUCache(options?.resourceCacheSize ?? DEFAULT_RESOURCE_CACHE_SIZE);
|
|
6517
|
+
}
|
|
6518
|
+
else {
|
|
6519
|
+
this.requestCache = undefined;
|
|
6520
|
+
}
|
|
6521
|
+
if (options?.autoBatchTime) {
|
|
6522
|
+
this.autoBatchTime = options?.autoBatchTime ?? 0;
|
|
6523
|
+
this.autoBatchQueue = [];
|
|
6524
|
+
}
|
|
6525
|
+
else {
|
|
6526
|
+
this.autoBatchTime = 0;
|
|
6527
|
+
this.autoBatchQueue = undefined;
|
|
6528
|
+
}
|
|
6514
6529
|
const activeLogin = this.getActiveLogin();
|
|
6515
6530
|
if (activeLogin) {
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6531
|
+
this.accessToken = activeLogin.accessToken;
|
|
6532
|
+
this.refreshToken = activeLogin.refreshToken;
|
|
6533
|
+
this.refreshProfile().catch(console.log);
|
|
6519
6534
|
}
|
|
6520
|
-
|
|
6535
|
+
this.setupStorageListener();
|
|
6521
6536
|
}
|
|
6522
6537
|
/**
|
|
6523
6538
|
* Returns the current base URL for all API requests.
|
|
@@ -6527,14 +6542,14 @@
|
|
|
6527
6542
|
* @returns The current base URL for all API requests.
|
|
6528
6543
|
*/
|
|
6529
6544
|
getBaseUrl() {
|
|
6530
|
-
return
|
|
6545
|
+
return this.baseUrl;
|
|
6531
6546
|
}
|
|
6532
6547
|
/**
|
|
6533
6548
|
* Clears all auth state including local storage and session storage.
|
|
6534
6549
|
* @category Authentication
|
|
6535
6550
|
*/
|
|
6536
6551
|
clear() {
|
|
6537
|
-
|
|
6552
|
+
this.storage.clear();
|
|
6538
6553
|
this.clearActiveLogin();
|
|
6539
6554
|
}
|
|
6540
6555
|
/**
|
|
@@ -6543,12 +6558,12 @@
|
|
|
6543
6558
|
* @category Authentication
|
|
6544
6559
|
*/
|
|
6545
6560
|
clearActiveLogin() {
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6561
|
+
this.storage.setString('activeLogin', undefined);
|
|
6562
|
+
this.requestCache?.clear();
|
|
6563
|
+
this.accessToken = undefined;
|
|
6564
|
+
this.refreshToken = undefined;
|
|
6565
|
+
this.profile = undefined;
|
|
6566
|
+
this.config = undefined;
|
|
6552
6567
|
this.dispatchEvent({ type: 'change' });
|
|
6553
6568
|
}
|
|
6554
6569
|
/**
|
|
@@ -6558,7 +6573,7 @@
|
|
|
6558
6573
|
*/
|
|
6559
6574
|
invalidateUrl(url) {
|
|
6560
6575
|
url = url.toString();
|
|
6561
|
-
|
|
6576
|
+
this.requestCache?.delete(url);
|
|
6562
6577
|
}
|
|
6563
6578
|
/**
|
|
6564
6579
|
* Invalidates all cached search results or cached requests for the given resourceType.
|
|
@@ -6566,10 +6581,12 @@
|
|
|
6566
6581
|
* @param resourceType The resource type to invalidate.
|
|
6567
6582
|
*/
|
|
6568
6583
|
invalidateSearches(resourceType) {
|
|
6569
|
-
const url =
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6584
|
+
const url = this.fhirBaseUrl + resourceType;
|
|
6585
|
+
if (this.requestCache) {
|
|
6586
|
+
for (const key of this.requestCache.keys()) {
|
|
6587
|
+
if (key.endsWith(url) || key.includes(url + '?')) {
|
|
6588
|
+
this.requestCache.delete(key);
|
|
6589
|
+
}
|
|
6573
6590
|
}
|
|
6574
6591
|
}
|
|
6575
6592
|
}
|
|
@@ -6587,30 +6604,30 @@
|
|
|
6587
6604
|
*/
|
|
6588
6605
|
get(url, options = {}) {
|
|
6589
6606
|
url = url.toString();
|
|
6590
|
-
const cached =
|
|
6607
|
+
const cached = this.getCacheEntry(url, options);
|
|
6591
6608
|
if (cached) {
|
|
6592
6609
|
return cached.value;
|
|
6593
6610
|
}
|
|
6594
6611
|
let promise;
|
|
6595
|
-
if (url.startsWith(
|
|
6612
|
+
if (url.startsWith(this.fhirBaseUrl) && this.autoBatchQueue) {
|
|
6596
6613
|
promise = new Promise((resolve, reject) => {
|
|
6597
|
-
|
|
6614
|
+
this.autoBatchQueue.push({
|
|
6598
6615
|
method: 'GET',
|
|
6599
|
-
url: url.replace(
|
|
6616
|
+
url: url.replace(this.fhirBaseUrl, ''),
|
|
6600
6617
|
options,
|
|
6601
6618
|
resolve,
|
|
6602
6619
|
reject,
|
|
6603
6620
|
});
|
|
6604
|
-
if (!
|
|
6605
|
-
|
|
6621
|
+
if (!this.autoBatchTimerId) {
|
|
6622
|
+
this.autoBatchTimerId = setTimeout(() => this.executeAutoBatch(), this.autoBatchTime);
|
|
6606
6623
|
}
|
|
6607
6624
|
});
|
|
6608
6625
|
}
|
|
6609
6626
|
else {
|
|
6610
|
-
promise =
|
|
6627
|
+
promise = this.request('GET', url, options);
|
|
6611
6628
|
}
|
|
6612
6629
|
const readablePromise = new ReadablePromise(promise);
|
|
6613
|
-
|
|
6630
|
+
this.setCacheEntry(url, readablePromise);
|
|
6614
6631
|
return readablePromise;
|
|
6615
6632
|
}
|
|
6616
6633
|
/**
|
|
@@ -6630,13 +6647,13 @@
|
|
|
6630
6647
|
post(url, body, contentType, options = {}) {
|
|
6631
6648
|
url = url.toString();
|
|
6632
6649
|
if (body) {
|
|
6633
|
-
|
|
6650
|
+
this.setRequestBody(options, body);
|
|
6634
6651
|
}
|
|
6635
6652
|
if (contentType) {
|
|
6636
|
-
|
|
6653
|
+
this.setRequestContentType(options, contentType);
|
|
6637
6654
|
}
|
|
6638
6655
|
this.invalidateUrl(url);
|
|
6639
|
-
return
|
|
6656
|
+
return this.request('POST', url, options);
|
|
6640
6657
|
}
|
|
6641
6658
|
/**
|
|
6642
6659
|
* Makes an HTTP PUT request to the specified URL.
|
|
@@ -6655,13 +6672,13 @@
|
|
|
6655
6672
|
put(url, body, contentType, options = {}) {
|
|
6656
6673
|
url = url.toString();
|
|
6657
6674
|
if (body) {
|
|
6658
|
-
|
|
6675
|
+
this.setRequestBody(options, body);
|
|
6659
6676
|
}
|
|
6660
6677
|
if (contentType) {
|
|
6661
|
-
|
|
6678
|
+
this.setRequestContentType(options, contentType);
|
|
6662
6679
|
}
|
|
6663
6680
|
this.invalidateUrl(url);
|
|
6664
|
-
return
|
|
6681
|
+
return this.request('PUT', url, options);
|
|
6665
6682
|
}
|
|
6666
6683
|
/**
|
|
6667
6684
|
* Makes an HTTP PATCH request to the specified URL.
|
|
@@ -6678,10 +6695,10 @@
|
|
|
6678
6695
|
*/
|
|
6679
6696
|
patch(url, operations, options = {}) {
|
|
6680
6697
|
url = url.toString();
|
|
6681
|
-
|
|
6682
|
-
|
|
6698
|
+
this.setRequestBody(options, operations);
|
|
6699
|
+
this.setRequestContentType(options, PATCH_CONTENT_TYPE);
|
|
6683
6700
|
this.invalidateUrl(url);
|
|
6684
|
-
return
|
|
6701
|
+
return this.request('PATCH', url, options);
|
|
6685
6702
|
}
|
|
6686
6703
|
/**
|
|
6687
6704
|
* Makes an HTTP DELETE request to the specified URL.
|
|
@@ -6699,7 +6716,7 @@
|
|
|
6699
6716
|
delete(url, options = {}) {
|
|
6700
6717
|
url = url.toString();
|
|
6701
6718
|
this.invalidateUrl(url);
|
|
6702
|
-
return
|
|
6719
|
+
return this.request('DELETE', url, options);
|
|
6703
6720
|
}
|
|
6704
6721
|
/**
|
|
6705
6722
|
* Initiates a new user flow.
|
|
@@ -6751,7 +6768,7 @@
|
|
|
6751
6768
|
async startLogin(loginRequest) {
|
|
6752
6769
|
return this.post('auth/login', {
|
|
6753
6770
|
...(await this.ensureCodeChallenge(loginRequest)),
|
|
6754
|
-
clientId: loginRequest.clientId ??
|
|
6771
|
+
clientId: loginRequest.clientId ?? this.clientId,
|
|
6755
6772
|
scope: loginRequest.scope,
|
|
6756
6773
|
});
|
|
6757
6774
|
}
|
|
@@ -6766,7 +6783,7 @@
|
|
|
6766
6783
|
async startGoogleLogin(loginRequest) {
|
|
6767
6784
|
return this.post('auth/google', {
|
|
6768
6785
|
...(await this.ensureCodeChallenge(loginRequest)),
|
|
6769
|
-
clientId: loginRequest.clientId ??
|
|
6786
|
+
clientId: loginRequest.clientId ?? this.clientId,
|
|
6770
6787
|
scope: loginRequest.scope,
|
|
6771
6788
|
});
|
|
6772
6789
|
}
|
|
@@ -6790,7 +6807,7 @@
|
|
|
6790
6807
|
* @category Authentication
|
|
6791
6808
|
*/
|
|
6792
6809
|
async signOut() {
|
|
6793
|
-
await this.post(
|
|
6810
|
+
await this.post(this.logoutUrl, {});
|
|
6794
6811
|
this.clear();
|
|
6795
6812
|
}
|
|
6796
6813
|
/**
|
|
@@ -6804,7 +6821,7 @@
|
|
|
6804
6821
|
const urlParams = new URLSearchParams(window.location.search);
|
|
6805
6822
|
const code = urlParams.get('code');
|
|
6806
6823
|
if (!code) {
|
|
6807
|
-
await
|
|
6824
|
+
await this.requestAuthorization(loginParams);
|
|
6808
6825
|
return undefined;
|
|
6809
6826
|
}
|
|
6810
6827
|
else {
|
|
@@ -6817,7 +6834,7 @@
|
|
|
6817
6834
|
* @category Authentication
|
|
6818
6835
|
*/
|
|
6819
6836
|
signOutWithRedirect() {
|
|
6820
|
-
window.location.assign(
|
|
6837
|
+
window.location.assign(this.logoutUrl);
|
|
6821
6838
|
}
|
|
6822
6839
|
/**
|
|
6823
6840
|
* Initiates sign in with an external identity provider.
|
|
@@ -6838,26 +6855,16 @@
|
|
|
6838
6855
|
* @category Authentication
|
|
6839
6856
|
*/
|
|
6840
6857
|
async exchangeExternalAccessToken(token, clientId) {
|
|
6841
|
-
clientId = clientId ||
|
|
6858
|
+
clientId = clientId || this.clientId;
|
|
6842
6859
|
if (!clientId) {
|
|
6843
6860
|
throw new Error('MedplumClient is missing clientId');
|
|
6844
6861
|
}
|
|
6845
|
-
const
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
}),
|
|
6852
|
-
credentials: 'include',
|
|
6853
|
-
});
|
|
6854
|
-
if (!response.ok) {
|
|
6855
|
-
this.clearActiveLogin();
|
|
6856
|
-
throw new Error('Failed to fetch tokens');
|
|
6857
|
-
}
|
|
6858
|
-
const tokens = await response.json();
|
|
6859
|
-
await __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_verifyTokens).call(this, tokens);
|
|
6860
|
-
return this.getProfile();
|
|
6862
|
+
const formBody = new URLSearchParams();
|
|
6863
|
+
formBody.set('grant_type', exports.OAuthGrantType.TokenExchange);
|
|
6864
|
+
formBody.set('subject_token_type', exports.OAuthTokenType.AccessToken);
|
|
6865
|
+
formBody.set('client_id', clientId);
|
|
6866
|
+
formBody.set('subject_token', token);
|
|
6867
|
+
return this.fetchTokens(formBody);
|
|
6861
6868
|
}
|
|
6862
6869
|
/**
|
|
6863
6870
|
* Builds the external identity provider redirect URI.
|
|
@@ -6885,7 +6892,7 @@
|
|
|
6885
6892
|
* @returns The well-formed FHIR URL.
|
|
6886
6893
|
*/
|
|
6887
6894
|
fhirUrl(...path) {
|
|
6888
|
-
return new URL(
|
|
6895
|
+
return new URL(this.fhirBaseUrl + path.join('/'));
|
|
6889
6896
|
}
|
|
6890
6897
|
/**
|
|
6891
6898
|
* Builds a FHIR search URL from a search query or structured query object.
|
|
@@ -6953,7 +6960,7 @@
|
|
|
6953
6960
|
search(resourceType, query, options = {}) {
|
|
6954
6961
|
const url = this.fhirSearchUrl(resourceType, query);
|
|
6955
6962
|
const cacheKey = url.toString() + '-search';
|
|
6956
|
-
const cached =
|
|
6963
|
+
const cached = this.getCacheEntry(cacheKey, options);
|
|
6957
6964
|
if (cached) {
|
|
6958
6965
|
return cached.value;
|
|
6959
6966
|
}
|
|
@@ -6961,12 +6968,12 @@
|
|
|
6961
6968
|
const bundle = await this.get(url, options);
|
|
6962
6969
|
if (bundle.entry) {
|
|
6963
6970
|
for (const entry of bundle.entry) {
|
|
6964
|
-
|
|
6971
|
+
this.cacheResource(entry.resource);
|
|
6965
6972
|
}
|
|
6966
6973
|
}
|
|
6967
6974
|
return bundle;
|
|
6968
6975
|
})());
|
|
6969
|
-
|
|
6976
|
+
this.setCacheEntry(cacheKey, promise);
|
|
6970
6977
|
return promise;
|
|
6971
6978
|
}
|
|
6972
6979
|
/**
|
|
@@ -6996,12 +7003,12 @@
|
|
|
6996
7003
|
url.searchParams.set('_count', '1');
|
|
6997
7004
|
url.searchParams.sort();
|
|
6998
7005
|
const cacheKey = url.toString() + '-searchOne';
|
|
6999
|
-
const cached =
|
|
7006
|
+
const cached = this.getCacheEntry(cacheKey, options);
|
|
7000
7007
|
if (cached) {
|
|
7001
7008
|
return cached.value;
|
|
7002
7009
|
}
|
|
7003
7010
|
const promise = new ReadablePromise(this.search(resourceType, url.searchParams, options).then((b) => b.entry?.[0]?.resource));
|
|
7004
|
-
|
|
7011
|
+
this.setCacheEntry(cacheKey, promise);
|
|
7005
7012
|
return promise;
|
|
7006
7013
|
}
|
|
7007
7014
|
/**
|
|
@@ -7029,12 +7036,12 @@
|
|
|
7029
7036
|
searchResources(resourceType, query, options = {}) {
|
|
7030
7037
|
const url = this.fhirSearchUrl(resourceType, query);
|
|
7031
7038
|
const cacheKey = url.toString() + '-searchResources';
|
|
7032
|
-
const cached =
|
|
7039
|
+
const cached = this.getCacheEntry(cacheKey, options);
|
|
7033
7040
|
if (cached) {
|
|
7034
7041
|
return cached.value;
|
|
7035
7042
|
}
|
|
7036
7043
|
const promise = new ReadablePromise(this.search(resourceType, query, options).then((b) => b.entry?.map((e) => e.resource) ?? []));
|
|
7037
|
-
|
|
7044
|
+
this.setCacheEntry(cacheKey, promise);
|
|
7038
7045
|
return promise;
|
|
7039
7046
|
}
|
|
7040
7047
|
/**
|
|
@@ -7095,7 +7102,7 @@
|
|
|
7095
7102
|
* @returns The resource if it is available in the cache; undefined otherwise.
|
|
7096
7103
|
*/
|
|
7097
7104
|
getCached(resourceType, id) {
|
|
7098
|
-
const cached =
|
|
7105
|
+
const cached = this.requestCache?.get(this.fhirUrl(resourceType, id).toString())?.value;
|
|
7099
7106
|
return cached && cached.isOk() ? cached.read() : undefined;
|
|
7100
7107
|
}
|
|
7101
7108
|
/**
|
|
@@ -7197,7 +7204,7 @@
|
|
|
7197
7204
|
return Promise.resolve(globalSchema);
|
|
7198
7205
|
}
|
|
7199
7206
|
const cacheKey = resourceType + '-requestSchema';
|
|
7200
|
-
const cached =
|
|
7207
|
+
const cached = this.getCacheEntry(cacheKey, undefined);
|
|
7201
7208
|
if (cached) {
|
|
7202
7209
|
return cached.value;
|
|
7203
7210
|
}
|
|
@@ -7240,7 +7247,7 @@
|
|
|
7240
7247
|
}
|
|
7241
7248
|
return globalSchema;
|
|
7242
7249
|
})());
|
|
7243
|
-
|
|
7250
|
+
this.setCacheEntry(cacheKey, promise);
|
|
7244
7251
|
return promise;
|
|
7245
7252
|
}
|
|
7246
7253
|
/**
|
|
@@ -7438,7 +7445,7 @@
|
|
|
7438
7445
|
};
|
|
7439
7446
|
xhr.open('POST', url);
|
|
7440
7447
|
xhr.withCredentials = true;
|
|
7441
|
-
xhr.setRequestHeader('Authorization', 'Bearer ' +
|
|
7448
|
+
xhr.setRequestHeader('Authorization', 'Bearer ' + this.accessToken);
|
|
7442
7449
|
xhr.setRequestHeader('Cache-Control', 'no-cache, no-store, max-age=0');
|
|
7443
7450
|
xhr.setRequestHeader('Content-Type', contentType);
|
|
7444
7451
|
xhr.setRequestHeader('X-Medplum', 'extended');
|
|
@@ -7468,10 +7475,10 @@
|
|
|
7468
7475
|
* @returns The result of the create operation.
|
|
7469
7476
|
*/
|
|
7470
7477
|
async createPdf(docDefinition, filename, tableLayouts, fonts) {
|
|
7471
|
-
if (!
|
|
7478
|
+
if (!this.createPdfImpl) {
|
|
7472
7479
|
throw new Error('PDF creation not enabled');
|
|
7473
7480
|
}
|
|
7474
|
-
const blob = await
|
|
7481
|
+
const blob = await this.createPdfImpl(docDefinition, tableLayouts, fonts);
|
|
7475
7482
|
return this.createBinary(blob, filename, 'application/pdf');
|
|
7476
7483
|
}
|
|
7477
7484
|
/**
|
|
@@ -7549,7 +7556,7 @@
|
|
|
7549
7556
|
// return result ?? resource;
|
|
7550
7557
|
result = resource;
|
|
7551
7558
|
}
|
|
7552
|
-
|
|
7559
|
+
this.cacheResource(result);
|
|
7553
7560
|
return result;
|
|
7554
7561
|
}
|
|
7555
7562
|
/**
|
|
@@ -7597,7 +7604,7 @@
|
|
|
7597
7604
|
* @returns The result of the delete operation.
|
|
7598
7605
|
*/
|
|
7599
7606
|
deleteResource(resourceType, id) {
|
|
7600
|
-
|
|
7607
|
+
this.deleteCacheEntry(this.fhirUrl(resourceType, id).toString());
|
|
7601
7608
|
this.invalidateSearches(resourceType);
|
|
7602
7609
|
return this.delete(this.fhirUrl(resourceType, id));
|
|
7603
7610
|
}
|
|
@@ -7689,7 +7696,7 @@
|
|
|
7689
7696
|
* @returns The FHIR batch/transaction response bundle.
|
|
7690
7697
|
*/
|
|
7691
7698
|
executeBatch(bundle) {
|
|
7692
|
-
return this.post(
|
|
7699
|
+
return this.post(this.fhirBaseUrl.slice(0, -1), bundle);
|
|
7693
7700
|
}
|
|
7694
7701
|
/**
|
|
7695
7702
|
* Sends an email using the Medplum Email API.
|
|
@@ -7801,61 +7808,80 @@
|
|
|
7801
7808
|
* @returns The Login State
|
|
7802
7809
|
*/
|
|
7803
7810
|
getActiveLogin() {
|
|
7804
|
-
return
|
|
7811
|
+
return this.storage.getObject('activeLogin');
|
|
7805
7812
|
}
|
|
7806
7813
|
/**
|
|
7807
7814
|
* @category Authentication
|
|
7808
7815
|
*/
|
|
7809
7816
|
async setActiveLogin(login) {
|
|
7810
7817
|
this.clearActiveLogin();
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
await
|
|
7818
|
+
this.accessToken = login.accessToken;
|
|
7819
|
+
this.refreshToken = login.refreshToken;
|
|
7820
|
+
this.storage.setObject('activeLogin', login);
|
|
7821
|
+
this.addLogin(login);
|
|
7822
|
+
this.refreshPromise = undefined;
|
|
7823
|
+
await this.refreshProfile();
|
|
7817
7824
|
}
|
|
7818
7825
|
/**
|
|
7819
7826
|
* Returns the current access token.
|
|
7820
7827
|
* @category Authentication
|
|
7821
7828
|
*/
|
|
7822
7829
|
getAccessToken() {
|
|
7823
|
-
return
|
|
7830
|
+
return this.accessToken;
|
|
7824
7831
|
}
|
|
7825
7832
|
/**
|
|
7826
7833
|
* Sets the current access token.
|
|
7827
7834
|
* @category Authentication
|
|
7828
7835
|
*/
|
|
7829
7836
|
setAccessToken(accessToken) {
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7837
|
+
this.accessToken = accessToken;
|
|
7838
|
+
this.refreshToken = undefined;
|
|
7839
|
+
this.profile = undefined;
|
|
7840
|
+
this.config = undefined;
|
|
7834
7841
|
}
|
|
7835
7842
|
/**
|
|
7836
7843
|
* @category Authentication
|
|
7837
7844
|
*/
|
|
7838
7845
|
getLogins() {
|
|
7839
|
-
return
|
|
7846
|
+
return this.storage.getObject('logins') ?? [];
|
|
7847
|
+
}
|
|
7848
|
+
addLogin(newLogin) {
|
|
7849
|
+
const logins = this.getLogins().filter((login) => login.profile?.reference !== newLogin.profile?.reference);
|
|
7850
|
+
logins.push(newLogin);
|
|
7851
|
+
this.storage.setObject('logins', logins);
|
|
7852
|
+
}
|
|
7853
|
+
async refreshProfile() {
|
|
7854
|
+
this.profilePromise = new Promise((resolve, reject) => {
|
|
7855
|
+
this.get('auth/me')
|
|
7856
|
+
.then((result) => {
|
|
7857
|
+
this.profilePromise = undefined;
|
|
7858
|
+
this.profile = result.profile;
|
|
7859
|
+
this.config = result.config;
|
|
7860
|
+
this.dispatchEvent({ type: 'change' });
|
|
7861
|
+
resolve(this.profile);
|
|
7862
|
+
})
|
|
7863
|
+
.catch(reject);
|
|
7864
|
+
});
|
|
7865
|
+
return this.profilePromise;
|
|
7840
7866
|
}
|
|
7841
7867
|
/**
|
|
7842
7868
|
* @category Authentication
|
|
7843
7869
|
*/
|
|
7844
7870
|
isLoading() {
|
|
7845
|
-
return !!
|
|
7871
|
+
return !!this.profilePromise;
|
|
7846
7872
|
}
|
|
7847
7873
|
/**
|
|
7848
7874
|
* @category User Profile
|
|
7849
7875
|
*/
|
|
7850
7876
|
getProfile() {
|
|
7851
|
-
return
|
|
7877
|
+
return this.profile;
|
|
7852
7878
|
}
|
|
7853
7879
|
/**
|
|
7854
7880
|
* @category User Profile
|
|
7855
7881
|
*/
|
|
7856
7882
|
async getProfileAsync() {
|
|
7857
|
-
if (
|
|
7858
|
-
await
|
|
7883
|
+
if (this.profilePromise) {
|
|
7884
|
+
await this.profilePromise;
|
|
7859
7885
|
}
|
|
7860
7886
|
return this.getProfile();
|
|
7861
7887
|
}
|
|
@@ -7863,7 +7889,7 @@
|
|
|
7863
7889
|
* @category User Profile
|
|
7864
7890
|
*/
|
|
7865
7891
|
getUserConfiguration() {
|
|
7866
|
-
return
|
|
7892
|
+
return this.config;
|
|
7867
7893
|
}
|
|
7868
7894
|
/**
|
|
7869
7895
|
* Downloads the URL as a blob.
|
|
@@ -7873,13 +7899,234 @@
|
|
|
7873
7899
|
* @returns Promise to the response body as a blob.
|
|
7874
7900
|
*/
|
|
7875
7901
|
async download(url, options = {}) {
|
|
7876
|
-
if (
|
|
7877
|
-
await
|
|
7902
|
+
if (this.refreshPromise) {
|
|
7903
|
+
await this.refreshPromise;
|
|
7878
7904
|
}
|
|
7879
|
-
|
|
7880
|
-
const response = await
|
|
7905
|
+
this.addFetchOptionsDefaults(options);
|
|
7906
|
+
const response = await this.fetch(url.toString(), options);
|
|
7881
7907
|
return response.blob();
|
|
7882
7908
|
}
|
|
7909
|
+
//
|
|
7910
|
+
// Private helpers
|
|
7911
|
+
//
|
|
7912
|
+
/**
|
|
7913
|
+
* Returns the cache entry if available and not expired.
|
|
7914
|
+
* @param key The cache key to retrieve.
|
|
7915
|
+
* @param options Optional fetch options for cache settings.
|
|
7916
|
+
* @returns The cached entry if found.
|
|
7917
|
+
*/
|
|
7918
|
+
getCacheEntry(key, options) {
|
|
7919
|
+
if (!this.requestCache || options?.cache === 'no-cache' || options?.cache === 'reload') {
|
|
7920
|
+
return undefined;
|
|
7921
|
+
}
|
|
7922
|
+
const entry = this.requestCache.get(key);
|
|
7923
|
+
if (!entry || entry.requestTime + this.cacheTime < Date.now()) {
|
|
7924
|
+
return undefined;
|
|
7925
|
+
}
|
|
7926
|
+
return entry;
|
|
7927
|
+
}
|
|
7928
|
+
/**
|
|
7929
|
+
* Adds a readable promise to the cache.
|
|
7930
|
+
* @param key The cache key to store.
|
|
7931
|
+
* @param value The readable promise to store.
|
|
7932
|
+
*/
|
|
7933
|
+
setCacheEntry(key, value) {
|
|
7934
|
+
if (this.requestCache) {
|
|
7935
|
+
this.requestCache.set(key, { requestTime: Date.now(), value });
|
|
7936
|
+
}
|
|
7937
|
+
}
|
|
7938
|
+
/**
|
|
7939
|
+
* Adds a concrete value as the cache entry for the given resource.
|
|
7940
|
+
* This is used in cases where the resource is loaded indirectly.
|
|
7941
|
+
* For example, when a resource is loaded as part of a Bundle.
|
|
7942
|
+
* @param resource The resource to cache.
|
|
7943
|
+
*/
|
|
7944
|
+
cacheResource(resource) {
|
|
7945
|
+
if (resource?.id) {
|
|
7946
|
+
this.setCacheEntry(this.fhirUrl(resource.resourceType, resource.id).toString(), new ReadablePromise(Promise.resolve(resource)));
|
|
7947
|
+
}
|
|
7948
|
+
}
|
|
7949
|
+
/**
|
|
7950
|
+
* Deletes a cache entry.
|
|
7951
|
+
* @param key The cache key to delete.
|
|
7952
|
+
*/
|
|
7953
|
+
deleteCacheEntry(key) {
|
|
7954
|
+
if (this.requestCache) {
|
|
7955
|
+
this.requestCache.delete(key);
|
|
7956
|
+
}
|
|
7957
|
+
}
|
|
7958
|
+
/**
|
|
7959
|
+
* Makes an HTTP request.
|
|
7960
|
+
* @param {string} method
|
|
7961
|
+
* @param {string} url
|
|
7962
|
+
* @param {string=} contentType
|
|
7963
|
+
* @param {Object=} body
|
|
7964
|
+
*/
|
|
7965
|
+
async request(method, url, options = {}) {
|
|
7966
|
+
if (this.refreshPromise) {
|
|
7967
|
+
await this.refreshPromise;
|
|
7968
|
+
}
|
|
7969
|
+
if (!url.startsWith('http')) {
|
|
7970
|
+
url = this.baseUrl + url;
|
|
7971
|
+
}
|
|
7972
|
+
options.method = method;
|
|
7973
|
+
this.addFetchOptionsDefaults(options);
|
|
7974
|
+
const response = await this.fetchWithRetry(url, options);
|
|
7975
|
+
if (response.status === 401) {
|
|
7976
|
+
// Refresh and try again
|
|
7977
|
+
return this.handleUnauthenticated(method, url, options);
|
|
7978
|
+
}
|
|
7979
|
+
if (response.status === 204 || response.status === 304) {
|
|
7980
|
+
// No content or change
|
|
7981
|
+
return undefined;
|
|
7982
|
+
}
|
|
7983
|
+
let obj = undefined;
|
|
7984
|
+
try {
|
|
7985
|
+
obj = await response.json();
|
|
7986
|
+
}
|
|
7987
|
+
catch (err) {
|
|
7988
|
+
console.error('Error parsing response', response.status, err);
|
|
7989
|
+
throw err;
|
|
7990
|
+
}
|
|
7991
|
+
if (response.status >= 400) {
|
|
7992
|
+
throw new OperationOutcomeError(normalizeOperationOutcome(obj));
|
|
7993
|
+
}
|
|
7994
|
+
return obj;
|
|
7995
|
+
}
|
|
7996
|
+
async fetchWithRetry(url, options) {
|
|
7997
|
+
const maxRetries = 3;
|
|
7998
|
+
const retryDelay = 200;
|
|
7999
|
+
let response = undefined;
|
|
8000
|
+
for (let retry = 0; retry < maxRetries; retry++) {
|
|
8001
|
+
response = (await this.fetch(url, options));
|
|
8002
|
+
if (response.status < 500) {
|
|
8003
|
+
return response;
|
|
8004
|
+
}
|
|
8005
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
8006
|
+
}
|
|
8007
|
+
return response;
|
|
8008
|
+
}
|
|
8009
|
+
/**
|
|
8010
|
+
* Executes a batch of requests that were automatically batched together.
|
|
8011
|
+
*/
|
|
8012
|
+
async executeAutoBatch() {
|
|
8013
|
+
// Get the current queue
|
|
8014
|
+
const entries = [...this.autoBatchQueue];
|
|
8015
|
+
// Clear the queue
|
|
8016
|
+
this.autoBatchQueue.length = 0;
|
|
8017
|
+
// Clear the timer
|
|
8018
|
+
this.autoBatchTimerId = undefined;
|
|
8019
|
+
// If there is only one request in the batch, just execute it
|
|
8020
|
+
if (entries.length === 1) {
|
|
8021
|
+
const entry = entries[0];
|
|
8022
|
+
try {
|
|
8023
|
+
entry.resolve(await this.request(entry.method, this.fhirBaseUrl + entry.url, entry.options));
|
|
8024
|
+
}
|
|
8025
|
+
catch (err) {
|
|
8026
|
+
entry.reject(new OperationOutcomeError(normalizeOperationOutcome(err)));
|
|
8027
|
+
}
|
|
8028
|
+
return;
|
|
8029
|
+
}
|
|
8030
|
+
// Build the batch request
|
|
8031
|
+
const batch = {
|
|
8032
|
+
resourceType: 'Bundle',
|
|
8033
|
+
type: 'batch',
|
|
8034
|
+
entry: entries.map((e) => ({
|
|
8035
|
+
request: {
|
|
8036
|
+
method: e.method,
|
|
8037
|
+
url: e.url,
|
|
8038
|
+
},
|
|
8039
|
+
resource: e.options.body ? JSON.parse(e.options.body) : undefined,
|
|
8040
|
+
})),
|
|
8041
|
+
};
|
|
8042
|
+
// Execute the batch request
|
|
8043
|
+
const response = (await this.post(this.fhirBaseUrl.slice(0, -1), batch));
|
|
8044
|
+
// Process the response
|
|
8045
|
+
for (let i = 0; i < entries.length; i++) {
|
|
8046
|
+
const entry = entries[i];
|
|
8047
|
+
const responseEntry = response.entry?.[i];
|
|
8048
|
+
if (responseEntry?.response?.outcome && !isOk(responseEntry.response.outcome)) {
|
|
8049
|
+
entry.reject(new OperationOutcomeError(responseEntry.response.outcome));
|
|
8050
|
+
}
|
|
8051
|
+
else {
|
|
8052
|
+
entry.resolve(responseEntry?.resource);
|
|
8053
|
+
}
|
|
8054
|
+
}
|
|
8055
|
+
}
|
|
8056
|
+
/**
|
|
8057
|
+
* Adds default options to the fetch options.
|
|
8058
|
+
* @param options The options to add defaults to.
|
|
8059
|
+
*/
|
|
8060
|
+
addFetchOptionsDefaults(options) {
|
|
8061
|
+
let headers = options.headers;
|
|
8062
|
+
if (!headers) {
|
|
8063
|
+
headers = {};
|
|
8064
|
+
options.headers = headers;
|
|
8065
|
+
}
|
|
8066
|
+
headers['X-Medplum'] = 'extended';
|
|
8067
|
+
if (options.body && !headers['Content-Type']) {
|
|
8068
|
+
headers['Content-Type'] = FHIR_CONTENT_TYPE;
|
|
8069
|
+
}
|
|
8070
|
+
if (this.accessToken) {
|
|
8071
|
+
headers['Authorization'] = 'Bearer ' + this.accessToken;
|
|
8072
|
+
}
|
|
8073
|
+
if (this.basicAuth) {
|
|
8074
|
+
headers['Authorization'] = 'Basic ' + this.basicAuth;
|
|
8075
|
+
}
|
|
8076
|
+
if (!options.cache) {
|
|
8077
|
+
options.cache = 'no-cache';
|
|
8078
|
+
}
|
|
8079
|
+
if (!options.credentials) {
|
|
8080
|
+
options.credentials = 'include';
|
|
8081
|
+
}
|
|
8082
|
+
}
|
|
8083
|
+
/**
|
|
8084
|
+
* Sets the "Content-Type" header on fetch options.
|
|
8085
|
+
* @param options The fetch options.
|
|
8086
|
+
* @param contentType The new content type to set.
|
|
8087
|
+
*/
|
|
8088
|
+
setRequestContentType(options, contentType) {
|
|
8089
|
+
if (!options.headers) {
|
|
8090
|
+
options.headers = {};
|
|
8091
|
+
}
|
|
8092
|
+
const headers = options.headers;
|
|
8093
|
+
headers['Content-Type'] = contentType;
|
|
8094
|
+
}
|
|
8095
|
+
/**
|
|
8096
|
+
* Sets the body on fetch options.
|
|
8097
|
+
* @param options The fetch options.
|
|
8098
|
+
* @param data The new content body.
|
|
8099
|
+
*/
|
|
8100
|
+
setRequestBody(options, data) {
|
|
8101
|
+
if (typeof data === 'string' ||
|
|
8102
|
+
(typeof Blob !== 'undefined' && data instanceof Blob) ||
|
|
8103
|
+
(typeof File !== 'undefined' && data instanceof File) ||
|
|
8104
|
+
(typeof Uint8Array !== 'undefined' && data instanceof Uint8Array)) {
|
|
8105
|
+
options.body = data;
|
|
8106
|
+
}
|
|
8107
|
+
else if (data) {
|
|
8108
|
+
options.body = JSON.stringify(data);
|
|
8109
|
+
}
|
|
8110
|
+
}
|
|
8111
|
+
/**
|
|
8112
|
+
* Handles an unauthenticated response from the server.
|
|
8113
|
+
* First, tries to refresh the access token and retry the request.
|
|
8114
|
+
* Otherwise, calls unauthenticated callbacks and rejects.
|
|
8115
|
+
* @param method The HTTP method of the original request.
|
|
8116
|
+
* @param url The URL of the original request.
|
|
8117
|
+
* @param contentType The content type of the original request.
|
|
8118
|
+
* @param body The body of the original request.
|
|
8119
|
+
*/
|
|
8120
|
+
handleUnauthenticated(method, url, options) {
|
|
8121
|
+
if (this.refresh()) {
|
|
8122
|
+
return this.request(method, url, options);
|
|
8123
|
+
}
|
|
8124
|
+
this.clearActiveLogin();
|
|
8125
|
+
if (this.onUnauthenticated) {
|
|
8126
|
+
this.onUnauthenticated();
|
|
8127
|
+
}
|
|
8128
|
+
return Promise.reject(new Error('Unauthenticated'));
|
|
8129
|
+
}
|
|
7883
8130
|
/**
|
|
7884
8131
|
* Starts a new PKCE flow.
|
|
7885
8132
|
* These PKCE values are stateful, and must survive redirects and page refreshes.
|
|
@@ -7895,6 +8142,23 @@
|
|
|
7895
8142
|
sessionStorage.setItem('codeChallenge', codeChallenge);
|
|
7896
8143
|
return { codeChallengeMethod: 'S256', codeChallenge };
|
|
7897
8144
|
}
|
|
8145
|
+
/**
|
|
8146
|
+
* Redirects the user to the login screen for authorization.
|
|
8147
|
+
* Clears all auth state including local storage and session storage.
|
|
8148
|
+
* See: https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
|
|
8149
|
+
*/
|
|
8150
|
+
async requestAuthorization(loginParams) {
|
|
8151
|
+
const loginRequest = await this.ensureCodeChallenge(loginParams || {});
|
|
8152
|
+
const url = new URL(this.authorizeUrl);
|
|
8153
|
+
url.searchParams.set('response_type', 'code');
|
|
8154
|
+
url.searchParams.set('state', sessionStorage.getItem('pkceState'));
|
|
8155
|
+
url.searchParams.set('client_id', loginRequest.clientId || this.clientId);
|
|
8156
|
+
url.searchParams.set('redirect_uri', loginRequest.redirectUri || getWindowOrigin());
|
|
8157
|
+
url.searchParams.set('code_challenge_method', loginRequest.codeChallengeMethod);
|
|
8158
|
+
url.searchParams.set('code_challenge', loginRequest.codeChallenge);
|
|
8159
|
+
url.searchParams.set('scope', loginRequest.scope || 'openid profile');
|
|
8160
|
+
window.location.assign(url.toString());
|
|
8161
|
+
}
|
|
7898
8162
|
/**
|
|
7899
8163
|
* Processes an OAuth authorization code.
|
|
7900
8164
|
* See: https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
|
|
@@ -7904,9 +8168,9 @@
|
|
|
7904
8168
|
*/
|
|
7905
8169
|
processCode(code, loginParams) {
|
|
7906
8170
|
const formBody = new URLSearchParams();
|
|
7907
|
-
formBody.set('grant_type',
|
|
8171
|
+
formBody.set('grant_type', exports.OAuthGrantType.AuthorizationCode);
|
|
7908
8172
|
formBody.set('code', code);
|
|
7909
|
-
formBody.set('client_id', loginParams?.clientId ||
|
|
8173
|
+
formBody.set('client_id', loginParams?.clientId || this.clientId);
|
|
7910
8174
|
formBody.set('redirect_uri', loginParams?.redirectUri || getWindowOrigin());
|
|
7911
8175
|
if (typeof sessionStorage !== 'undefined') {
|
|
7912
8176
|
const codeVerifier = sessionStorage.getItem('codeVerifier');
|
|
@@ -7914,7 +8178,29 @@
|
|
|
7914
8178
|
formBody.set('code_verifier', codeVerifier);
|
|
7915
8179
|
}
|
|
7916
8180
|
}
|
|
7917
|
-
return
|
|
8181
|
+
return this.fetchTokens(formBody);
|
|
8182
|
+
}
|
|
8183
|
+
/**
|
|
8184
|
+
* Tries to refresh the auth tokens.
|
|
8185
|
+
* See: https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokens
|
|
8186
|
+
*/
|
|
8187
|
+
refresh() {
|
|
8188
|
+
if (this.refreshPromise) {
|
|
8189
|
+
return this.refreshPromise;
|
|
8190
|
+
}
|
|
8191
|
+
if (this.refreshToken) {
|
|
8192
|
+
const formBody = new URLSearchParams();
|
|
8193
|
+
formBody.set('grant_type', exports.OAuthGrantType.RefreshToken);
|
|
8194
|
+
formBody.set('client_id', this.clientId);
|
|
8195
|
+
formBody.set('refresh_token', this.refreshToken);
|
|
8196
|
+
this.refreshPromise = this.fetchTokens(formBody);
|
|
8197
|
+
return this.refreshPromise;
|
|
8198
|
+
}
|
|
8199
|
+
if (this.clientId && this.clientSecret) {
|
|
8200
|
+
this.refreshPromise = this.startClientLogin(this.clientId, this.clientSecret);
|
|
8201
|
+
return this.refreshPromise;
|
|
8202
|
+
}
|
|
8203
|
+
return undefined;
|
|
7918
8204
|
}
|
|
7919
8205
|
/**
|
|
7920
8206
|
* Starts a new OAuth2 client credentials flow.
|
|
@@ -7925,13 +8211,24 @@
|
|
|
7925
8211
|
* @returns Promise that resolves to the client profile.
|
|
7926
8212
|
*/
|
|
7927
8213
|
async startClientLogin(clientId, clientSecret) {
|
|
7928
|
-
|
|
7929
|
-
|
|
8214
|
+
this.clientId = clientId;
|
|
8215
|
+
this.clientSecret = clientSecret;
|
|
7930
8216
|
const formBody = new URLSearchParams();
|
|
7931
|
-
formBody.set('grant_type',
|
|
8217
|
+
formBody.set('grant_type', exports.OAuthGrantType.ClientCredentials);
|
|
7932
8218
|
formBody.set('client_id', clientId);
|
|
7933
8219
|
formBody.set('client_secret', clientSecret);
|
|
7934
|
-
return
|
|
8220
|
+
return this.fetchTokens(formBody);
|
|
8221
|
+
}
|
|
8222
|
+
/**
|
|
8223
|
+
* Sets the client ID and secret for basic auth.
|
|
8224
|
+
* @category Authentication
|
|
8225
|
+
* @param clientId The client ID.
|
|
8226
|
+
* @param clientSecret The client secret.
|
|
8227
|
+
*/
|
|
8228
|
+
setBasicAuth(clientId, clientSecret) {
|
|
8229
|
+
this.clientId = clientId;
|
|
8230
|
+
this.clientSecret = clientSecret;
|
|
8231
|
+
this.basicAuth = encodeBase64(clientId + ':' + clientSecret);
|
|
7935
8232
|
}
|
|
7936
8233
|
/**
|
|
7937
8234
|
* Invite a user to a project.
|
|
@@ -7942,280 +8239,72 @@
|
|
|
7942
8239
|
async invite(projectId, body) {
|
|
7943
8240
|
return this.post('admin/projects/' + projectId + '/invite', body);
|
|
7944
8241
|
}
|
|
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);
|
|
8242
|
+
/**
|
|
8243
|
+
* Makes a POST request to the tokens endpoint.
|
|
8244
|
+
* See: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
|
8245
|
+
* @param formBody Token parameters in URL encoded format.
|
|
8246
|
+
*/
|
|
8247
|
+
async fetchTokens(formBody) {
|
|
8248
|
+
const response = await this.fetch(this.tokenUrl, {
|
|
8249
|
+
method: 'POST',
|
|
8250
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
8251
|
+
body: formBody,
|
|
8252
|
+
credentials: 'include',
|
|
8253
|
+
});
|
|
8254
|
+
if (!response.ok) {
|
|
8255
|
+
this.clearActiveLogin();
|
|
8256
|
+
throw new Error('Failed to fetch tokens');
|
|
8257
|
+
}
|
|
8258
|
+
const tokens = await response.json();
|
|
8259
|
+
await this.verifyTokens(tokens);
|
|
8260
|
+
return this.getProfile();
|
|
8005
8261
|
}
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
8262
|
+
/**
|
|
8263
|
+
* Verifies the tokens received from the auth server.
|
|
8264
|
+
* Validates the JWT against the JWKS.
|
|
8265
|
+
* See: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
|
8266
|
+
* @param tokens
|
|
8267
|
+
*/
|
|
8268
|
+
async verifyTokens(tokens) {
|
|
8269
|
+
const token = tokens.access_token;
|
|
8270
|
+
// Verify token has not expired
|
|
8271
|
+
const tokenPayload = parseJWTPayload(token);
|
|
8272
|
+
if (Date.now() >= tokenPayload.exp * 1000) {
|
|
8273
|
+
this.clearActiveLogin();
|
|
8274
|
+
throw new Error('Token expired');
|
|
8275
|
+
}
|
|
8276
|
+
// Verify app_client_id
|
|
8277
|
+
if (this.clientId && tokenPayload.client_id !== this.clientId) {
|
|
8278
|
+
this.clearActiveLogin();
|
|
8279
|
+
throw new Error('Token was not issued for this audience');
|
|
8280
|
+
}
|
|
8281
|
+
return this.setActiveLogin({
|
|
8282
|
+
accessToken: token,
|
|
8283
|
+
refreshToken: tokens.refresh_token,
|
|
8284
|
+
project: tokens.project,
|
|
8285
|
+
profile: tokens.profile,
|
|
8286
|
+
});
|
|
8009
8287
|
}
|
|
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];
|
|
8288
|
+
/**
|
|
8289
|
+
* Sets up a listener for window storage events.
|
|
8290
|
+
* This synchronizes state across browser windows and browser tabs.
|
|
8291
|
+
*/
|
|
8292
|
+
setupStorageListener() {
|
|
8048
8293
|
try {
|
|
8049
|
-
|
|
8294
|
+
window.addEventListener('storage', (e) => {
|
|
8295
|
+
if (e.key === null || e.key === 'activeLogin') {
|
|
8296
|
+
// Storage events fire when different tabs make changes.
|
|
8297
|
+
// On storage clear (key === null) or activeLogin change (key === 'activeLogin')
|
|
8298
|
+
// Refresh the page to ensure the active login is up to date.
|
|
8299
|
+
window.location.reload();
|
|
8300
|
+
}
|
|
8301
|
+
});
|
|
8050
8302
|
}
|
|
8051
8303
|
catch (err) {
|
|
8052
|
-
|
|
8053
|
-
}
|
|
8054
|
-
return;
|
|
8055
|
-
}
|
|
8056
|
-
// Build the batch request
|
|
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));
|
|
8304
|
+
// Silently ignore if this environment does not support storage events
|
|
8076
8305
|
}
|
|
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
8306
|
}
|
|
8215
|
-
|
|
8216
|
-
// Silently ignore if this environment does not support storage events
|
|
8217
|
-
}
|
|
8218
|
-
};
|
|
8307
|
+
}
|
|
8219
8308
|
/**
|
|
8220
8309
|
* Returns the default fetch method.
|
|
8221
8310
|
* The default fetch is currently only available in browser environments.
|
|
@@ -8245,7 +8334,6 @@
|
|
|
8245
8334
|
return url.endsWith('/') ? url : url + '/';
|
|
8246
8335
|
}
|
|
8247
8336
|
|
|
8248
|
-
var _ParserBuilder_prefixParselets, _ParserBuilder_infixParselets, _Parser_tokens, _Parser_prefixParselets, _Parser_infixParselets;
|
|
8249
8337
|
class PrefixOperatorAtom {
|
|
8250
8338
|
constructor(operator, child) {
|
|
8251
8339
|
this.operator = operator;
|
|
@@ -8267,15 +8355,15 @@
|
|
|
8267
8355
|
}
|
|
8268
8356
|
class ParserBuilder {
|
|
8269
8357
|
constructor() {
|
|
8270
|
-
|
|
8271
|
-
|
|
8358
|
+
this.prefixParselets = {};
|
|
8359
|
+
this.infixParselets = {};
|
|
8272
8360
|
}
|
|
8273
8361
|
registerInfix(tokenType, parselet) {
|
|
8274
|
-
|
|
8362
|
+
this.infixParselets[tokenType] = parselet;
|
|
8275
8363
|
return this;
|
|
8276
8364
|
}
|
|
8277
8365
|
registerPrefix(tokenType, parselet) {
|
|
8278
|
-
|
|
8366
|
+
this.prefixParselets[tokenType] = parselet;
|
|
8279
8367
|
return this;
|
|
8280
8368
|
}
|
|
8281
8369
|
prefix(tokenType, precedence, builder) {
|
|
@@ -8296,21 +8384,17 @@
|
|
|
8296
8384
|
});
|
|
8297
8385
|
}
|
|
8298
8386
|
construct(input) {
|
|
8299
|
-
return new Parser(input,
|
|
8387
|
+
return new Parser(input, this.prefixParselets, this.infixParselets);
|
|
8300
8388
|
}
|
|
8301
8389
|
}
|
|
8302
|
-
_ParserBuilder_prefixParselets = new WeakMap(), _ParserBuilder_infixParselets = new WeakMap();
|
|
8303
8390
|
class Parser {
|
|
8304
8391
|
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");
|
|
8392
|
+
this.tokens = tokens;
|
|
8393
|
+
this.prefixParselets = prefixParselets;
|
|
8394
|
+
this.infixParselets = infixParselets;
|
|
8311
8395
|
}
|
|
8312
8396
|
hasMore() {
|
|
8313
|
-
return
|
|
8397
|
+
return this.tokens.length > 0;
|
|
8314
8398
|
}
|
|
8315
8399
|
match(expected) {
|
|
8316
8400
|
const token = this.peek();
|
|
@@ -8322,7 +8406,7 @@
|
|
|
8322
8406
|
}
|
|
8323
8407
|
consumeAndParse(precedence = Infinity) {
|
|
8324
8408
|
const token = this.consume();
|
|
8325
|
-
const prefix =
|
|
8409
|
+
const prefix = this.prefixParselets[token.id];
|
|
8326
8410
|
if (!prefix) {
|
|
8327
8411
|
throw Error(`Parse error at "${token.value}" (line ${token.line}, column ${token.column}). No matching prefix parselet.`);
|
|
8328
8412
|
}
|
|
@@ -8346,7 +8430,7 @@
|
|
|
8346
8430
|
return Infinity;
|
|
8347
8431
|
}
|
|
8348
8432
|
consume(expectedId, expectedValue) {
|
|
8349
|
-
if (!
|
|
8433
|
+
if (!this.tokens.length) {
|
|
8350
8434
|
throw Error('Cant consume unknown more tokens.');
|
|
8351
8435
|
}
|
|
8352
8436
|
if (expectedId && this.peek()?.id !== expectedId) {
|
|
@@ -8357,21 +8441,19 @@
|
|
|
8357
8441
|
const actual = this.peek();
|
|
8358
8442
|
throw Error(`Expected "${expectedValue}" but got "${actual.value}" at line ${actual.line} column ${actual.column}.`);
|
|
8359
8443
|
}
|
|
8360
|
-
return
|
|
8444
|
+
return this.tokens.shift();
|
|
8361
8445
|
}
|
|
8362
8446
|
peek() {
|
|
8363
|
-
return
|
|
8447
|
+
return this.tokens.length > 0 ? this.tokens[0] : undefined;
|
|
8364
8448
|
}
|
|
8365
8449
|
removeComments() {
|
|
8366
|
-
|
|
8450
|
+
this.tokens = this.tokens.filter((t) => t.id !== 'Comment');
|
|
8367
8451
|
}
|
|
8368
8452
|
getInfixParselet(token) {
|
|
8369
|
-
return
|
|
8453
|
+
return this.infixParselets[token.id === 'Symbol' ? token.value : token.id];
|
|
8370
8454
|
}
|
|
8371
8455
|
}
|
|
8372
|
-
_Parser_tokens = new WeakMap(), _Parser_prefixParselets = new WeakMap(), _Parser_infixParselets = new WeakMap();
|
|
8373
8456
|
|
|
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
8457
|
const STANDARD_UNITS = [
|
|
8376
8458
|
'year',
|
|
8377
8459
|
'years',
|
|
@@ -8392,190 +8474,203 @@
|
|
|
8392
8474
|
];
|
|
8393
8475
|
class Tokenizer {
|
|
8394
8476
|
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");
|
|
8477
|
+
this.result = [];
|
|
8478
|
+
this.pos = { index: 0, line: 1, column: 0 };
|
|
8479
|
+
this.markStack = [];
|
|
8480
|
+
this.str = str;
|
|
8481
|
+
this.keywords = keywords;
|
|
8482
|
+
this.operators = operators;
|
|
8483
|
+
this.dateTimeLiterals = !!options?.dateTimeLiterals;
|
|
8484
|
+
this.symbolRegex = options?.symbolRegex ?? /[$\w]/;
|
|
8409
8485
|
}
|
|
8410
8486
|
tokenize() {
|
|
8411
|
-
while (
|
|
8412
|
-
const token =
|
|
8487
|
+
while (this.pos.index < this.str.length) {
|
|
8488
|
+
const token = this.consumeToken();
|
|
8413
8489
|
if (token) {
|
|
8414
|
-
|
|
8490
|
+
this.result.push(token);
|
|
8415
8491
|
}
|
|
8416
8492
|
}
|
|
8417
|
-
return
|
|
8493
|
+
return this.result;
|
|
8418
8494
|
}
|
|
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;
|
|
8495
|
+
prevToken() {
|
|
8496
|
+
return this.result.slice(-1)[0];
|
|
8432
8497
|
}
|
|
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;
|
|
8498
|
+
peekToken() {
|
|
8499
|
+
this.mark();
|
|
8500
|
+
const token = this.consumeToken();
|
|
8501
|
+
this.reset();
|
|
8502
|
+
return token;
|
|
8564
8503
|
}
|
|
8565
|
-
|
|
8566
|
-
|
|
8504
|
+
consumeToken() {
|
|
8505
|
+
this.consumeWhitespace();
|
|
8506
|
+
const c = this.curr();
|
|
8507
|
+
if (!c) {
|
|
8508
|
+
return undefined;
|
|
8509
|
+
}
|
|
8510
|
+
this.mark();
|
|
8511
|
+
const next = this.peek();
|
|
8512
|
+
if (c === '/' && next === '*') {
|
|
8513
|
+
return this.consumeMultiLineComment();
|
|
8514
|
+
}
|
|
8515
|
+
if (c === '/' && next === '/') {
|
|
8516
|
+
return this.consumeSingleLineComment();
|
|
8517
|
+
}
|
|
8518
|
+
if (c === "'" || c === '"') {
|
|
8519
|
+
return this.consumeString(c);
|
|
8520
|
+
}
|
|
8521
|
+
if (c === '`') {
|
|
8522
|
+
return this.consumeBacktickSymbol();
|
|
8523
|
+
}
|
|
8524
|
+
if (c === '@') {
|
|
8525
|
+
return this.consumeDateTime();
|
|
8526
|
+
}
|
|
8527
|
+
if (c.match(/\d/)) {
|
|
8528
|
+
return this.consumeNumber();
|
|
8529
|
+
}
|
|
8530
|
+
if (c.match(/\w/)) {
|
|
8531
|
+
return this.consumeSymbol();
|
|
8532
|
+
}
|
|
8533
|
+
if (c === '$' && next.match(/\w/)) {
|
|
8534
|
+
return this.consumeSymbol();
|
|
8535
|
+
}
|
|
8536
|
+
return this.consumeOperator();
|
|
8567
8537
|
}
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
if (!mark) {
|
|
8571
|
-
throw new Error('No mark for token');
|
|
8538
|
+
consumeWhitespace() {
|
|
8539
|
+
this.consumeWhile(() => this.curr().match(/\s/));
|
|
8572
8540
|
}
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8541
|
+
consumeMultiLineComment() {
|
|
8542
|
+
const start = this.pos.index;
|
|
8543
|
+
this.consumeWhile(() => this.curr() !== '*' || this.peek() !== '/');
|
|
8544
|
+
this.advance();
|
|
8545
|
+
this.advance();
|
|
8546
|
+
return this.buildToken('Comment', this.str.substring(start, this.pos.index));
|
|
8547
|
+
}
|
|
8548
|
+
consumeSingleLineComment() {
|
|
8549
|
+
return this.buildToken('Comment', this.consumeWhile(() => this.curr() !== '\n'));
|
|
8550
|
+
}
|
|
8551
|
+
consumeString(endChar) {
|
|
8552
|
+
this.advance();
|
|
8553
|
+
const result = this.buildToken('String', this.consumeWhile(() => this.prev() === '\\' || this.curr() !== endChar));
|
|
8554
|
+
this.advance();
|
|
8555
|
+
return result;
|
|
8556
|
+
}
|
|
8557
|
+
consumeBacktickSymbol() {
|
|
8558
|
+
this.advance();
|
|
8559
|
+
const result = this.buildToken('Symbol', this.consumeWhile(() => this.curr() !== '`'));
|
|
8560
|
+
this.advance();
|
|
8561
|
+
return result;
|
|
8562
|
+
}
|
|
8563
|
+
consumeDateTime() {
|
|
8564
|
+
this.advance(); // Consume "@"
|
|
8565
|
+
const start = this.pos.index;
|
|
8566
|
+
this.consumeWhile(() => this.curr().match(/[\d-]/));
|
|
8567
|
+
if (this.curr() === 'T') {
|
|
8568
|
+
this.advance();
|
|
8569
|
+
this.consumeWhile(() => this.curr().match(/[\d:]/));
|
|
8570
|
+
if (this.curr() === '.' && this.peek().match(/\d/)) {
|
|
8571
|
+
this.advance();
|
|
8572
|
+
this.consumeWhile(() => this.curr().match(/\d/));
|
|
8573
|
+
}
|
|
8574
|
+
if (this.curr() === 'Z') {
|
|
8575
|
+
this.advance();
|
|
8576
|
+
}
|
|
8577
|
+
else if (this.curr() === '+' || this.curr() === '-') {
|
|
8578
|
+
this.advance();
|
|
8579
|
+
this.consumeWhile(() => this.curr().match(/[\d:]/));
|
|
8580
|
+
}
|
|
8581
|
+
}
|
|
8582
|
+
return this.buildToken('DateTime', this.str.substring(start, this.pos.index));
|
|
8583
|
+
}
|
|
8584
|
+
consumeNumber() {
|
|
8585
|
+
const start = this.pos.index;
|
|
8586
|
+
let id = 'Number';
|
|
8587
|
+
this.consumeWhile(() => this.curr().match(/\d/));
|
|
8588
|
+
if (this.curr() === '.' && this.peek().match(/\d/)) {
|
|
8589
|
+
this.advance();
|
|
8590
|
+
this.consumeWhile(() => this.curr().match(/\d/));
|
|
8591
|
+
}
|
|
8592
|
+
if (this.curr() === '-' && this.dateTimeLiterals) {
|
|
8593
|
+
// Rewind to one character before the start, and then treat as dateTime literal.
|
|
8594
|
+
this.pos.index = start - 1;
|
|
8595
|
+
return this.consumeDateTime();
|
|
8596
|
+
}
|
|
8597
|
+
if (this.curr() === ' ') {
|
|
8598
|
+
if (isUnitToken(this.peekToken())) {
|
|
8599
|
+
id = 'Quantity';
|
|
8600
|
+
this.consumeToken();
|
|
8601
|
+
}
|
|
8602
|
+
}
|
|
8603
|
+
return this.buildToken(id, this.str.substring(start, this.pos.index));
|
|
8604
|
+
}
|
|
8605
|
+
consumeSymbol() {
|
|
8606
|
+
const value = this.consumeWhile(() => this.curr().match(this.symbolRegex));
|
|
8607
|
+
if (this.prevToken()?.value !== '.' && this.keywords.includes(value)) {
|
|
8608
|
+
return this.buildToken(value, value);
|
|
8609
|
+
}
|
|
8610
|
+
return this.buildToken('Symbol', value);
|
|
8611
|
+
}
|
|
8612
|
+
consumeOperator() {
|
|
8613
|
+
const c = this.curr();
|
|
8614
|
+
const next = this.peek();
|
|
8615
|
+
const twoCharOp = c + next;
|
|
8616
|
+
if (this.operators.includes(twoCharOp)) {
|
|
8617
|
+
this.advance();
|
|
8618
|
+
this.advance();
|
|
8619
|
+
return this.buildToken(twoCharOp, twoCharOp);
|
|
8620
|
+
}
|
|
8621
|
+
this.advance();
|
|
8622
|
+
return this.buildToken(c, c);
|
|
8623
|
+
}
|
|
8624
|
+
consumeWhile(condition) {
|
|
8625
|
+
const start = this.pos.index;
|
|
8626
|
+
while (this.pos.index < this.str.length && condition()) {
|
|
8627
|
+
this.advance();
|
|
8628
|
+
}
|
|
8629
|
+
return this.str.substring(start, this.pos.index);
|
|
8630
|
+
}
|
|
8631
|
+
curr() {
|
|
8632
|
+
return this.str[this.pos.index];
|
|
8633
|
+
}
|
|
8634
|
+
prev() {
|
|
8635
|
+
return this.str[this.pos.index - 1] ?? '';
|
|
8636
|
+
}
|
|
8637
|
+
peek() {
|
|
8638
|
+
return this.str[this.pos.index + 1] ?? '';
|
|
8639
|
+
}
|
|
8640
|
+
mark() {
|
|
8641
|
+
this.markStack.push({ ...this.pos });
|
|
8642
|
+
}
|
|
8643
|
+
reset() {
|
|
8644
|
+
const mark = this.markStack.pop();
|
|
8645
|
+
if (!mark) {
|
|
8646
|
+
throw new Error('No mark to reset to');
|
|
8647
|
+
}
|
|
8648
|
+
this.pos.index = mark.index;
|
|
8649
|
+
this.pos.line = mark.line;
|
|
8650
|
+
this.pos.column = mark.column;
|
|
8651
|
+
}
|
|
8652
|
+
advance() {
|
|
8653
|
+
this.pos.index++;
|
|
8654
|
+
if (this.curr() === '\n') {
|
|
8655
|
+
this.pos.line++;
|
|
8656
|
+
this.pos.column = 0;
|
|
8657
|
+
}
|
|
8658
|
+
else {
|
|
8659
|
+
this.pos.column++;
|
|
8660
|
+
}
|
|
8661
|
+
}
|
|
8662
|
+
buildToken(id, value) {
|
|
8663
|
+
const mark = this.markStack.pop();
|
|
8664
|
+
if (!mark) {
|
|
8665
|
+
throw new Error('No mark for token');
|
|
8666
|
+
}
|
|
8667
|
+
return {
|
|
8668
|
+
id,
|
|
8669
|
+
value,
|
|
8670
|
+
...mark,
|
|
8671
|
+
};
|
|
8672
|
+
}
|
|
8673
|
+
}
|
|
8579
8674
|
function isUnitToken(token) {
|
|
8580
8675
|
if (token) {
|
|
8581
8676
|
if (token.id === 'String') {
|
|
@@ -10960,10 +11055,8 @@
|
|
|
10960
11055
|
return new Tokenizer(str, FHIRPATH_KEYWORDS, MAPPING_LANGUAGE_OPERATORS$1).tokenize();
|
|
10961
11056
|
}
|
|
10962
11057
|
|
|
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
11058
|
class StructureMapParser {
|
|
10965
11059
|
constructor(parser) {
|
|
10966
|
-
_StructureMapParser_instances.add(this);
|
|
10967
11060
|
this.parser = parser;
|
|
10968
11061
|
this.structureMap = { resourceType: 'StructureMap' };
|
|
10969
11062
|
}
|
|
@@ -10978,16 +11071,16 @@
|
|
|
10978
11071
|
const next = this.parser.peek()?.value;
|
|
10979
11072
|
switch (next) {
|
|
10980
11073
|
case 'uses':
|
|
10981
|
-
|
|
11074
|
+
this.parseUses();
|
|
10982
11075
|
break;
|
|
10983
11076
|
case 'imports':
|
|
10984
|
-
|
|
11077
|
+
this.parseImport();
|
|
10985
11078
|
break;
|
|
10986
11079
|
case 'group':
|
|
10987
|
-
|
|
11080
|
+
this.parseGroup();
|
|
10988
11081
|
break;
|
|
10989
11082
|
case 'conceptmap':
|
|
10990
|
-
|
|
11083
|
+
this.parseConceptMap();
|
|
10991
11084
|
break;
|
|
10992
11085
|
default:
|
|
10993
11086
|
throw new Error(`Unexpected token: ${next}`);
|
|
@@ -10995,263 +11088,280 @@
|
|
|
10995
11088
|
}
|
|
10996
11089
|
return this.structureMap;
|
|
10997
11090
|
}
|
|
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(',');
|
|
11091
|
+
parseUses() {
|
|
11092
|
+
// 'uses' url structureAlias? 'as' modelMode
|
|
11093
|
+
// uses "http://hl7.org/fhir/StructureDefinition/tutorial-left" as source
|
|
11094
|
+
this.parser.consume('Symbol', 'uses');
|
|
11095
|
+
const result = {};
|
|
11096
|
+
result.url = this.parser.consume('String').value;
|
|
11097
|
+
if (this.parser.peek()?.value === 'alias') {
|
|
11098
|
+
this.parser.consume('Symbol', 'alias');
|
|
11099
|
+
result.alias = this.parser.consume('Symbol').value;
|
|
11056
11100
|
}
|
|
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);
|
|
11101
|
+
this.parser.consume('Symbol', 'as');
|
|
11102
|
+
result.mode = this.parser.consume().value;
|
|
11103
|
+
if (!this.structureMap.structure) {
|
|
11104
|
+
this.structureMap.structure = [];
|
|
11105
|
+
}
|
|
11106
|
+
this.structureMap.structure.push(result);
|
|
11107
|
+
}
|
|
11108
|
+
parseImport() {
|
|
11109
|
+
this.parser.consume('Symbol', 'imports');
|
|
11110
|
+
if (!this.structureMap.import) {
|
|
11111
|
+
this.structureMap.import = [];
|
|
11112
|
+
}
|
|
11113
|
+
this.structureMap.import.push(this.parser.consume('String').value);
|
|
11114
|
+
}
|
|
11115
|
+
parseGroup() {
|
|
11116
|
+
// 'group' identifier parameters extends? typeMode? rules
|
|
11117
|
+
// group tutorial(source src : TLeft, target tgt : TRight) {
|
|
11118
|
+
const result = {};
|
|
11119
|
+
this.parser.consume('Symbol', 'group');
|
|
11120
|
+
result.name = this.parser.consume('Symbol').value;
|
|
11121
|
+
result.input = this.parseParameters();
|
|
11122
|
+
if (this.parser.peek()?.value === 'extends') {
|
|
11123
|
+
this.parser.consume('Symbol', 'extends');
|
|
11124
|
+
result.extends = this.parser.consume('Symbol').value;
|
|
11125
|
+
}
|
|
11126
|
+
if (this.parser.peek()?.value === '<<') {
|
|
11127
|
+
this.parser.consume('<<');
|
|
11128
|
+
result.typeMode = this.parser.consume().value;
|
|
11129
|
+
if (this.parser.peek()?.value === '+') {
|
|
11130
|
+
this.parser.consume('+');
|
|
11131
|
+
result.typeMode = 'type-and-types';
|
|
11132
|
+
}
|
|
11133
|
+
this.parser.consume('>>');
|
|
11092
11134
|
}
|
|
11093
11135
|
else {
|
|
11094
|
-
result.
|
|
11136
|
+
result.typeMode = 'none';
|
|
11095
11137
|
}
|
|
11138
|
+
result.rule = this.parseRules();
|
|
11139
|
+
if (!this.structureMap.group) {
|
|
11140
|
+
this.structureMap.group = [];
|
|
11141
|
+
}
|
|
11142
|
+
this.structureMap.group.push(result);
|
|
11096
11143
|
}
|
|
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;
|
|
11144
|
+
parseParameters() {
|
|
11145
|
+
const parameters = [];
|
|
11146
|
+
this.parser.consume('(');
|
|
11147
|
+
while (this.parser.hasMore() && this.parser.peek()?.value !== ')') {
|
|
11148
|
+
parameters.push(this.parseParameter());
|
|
11149
|
+
if (this.parser.peek()?.value === ',') {
|
|
11150
|
+
this.parser.consume(',');
|
|
11151
|
+
}
|
|
11152
|
+
}
|
|
11153
|
+
this.parser.consume(')');
|
|
11154
|
+
return parameters;
|
|
11137
11155
|
}
|
|
11138
|
-
|
|
11139
|
-
|
|
11140
|
-
|
|
11156
|
+
parseParameter() {
|
|
11157
|
+
// inputMode identifier type?
|
|
11158
|
+
// ':' identifier
|
|
11159
|
+
// source src : TLeft
|
|
11160
|
+
const result = {};
|
|
11161
|
+
result.mode = this.parser.consume().value;
|
|
11162
|
+
result.name = this.parser.consume('Symbol').value;
|
|
11163
|
+
if (this.parser.peek()?.value === ':') {
|
|
11164
|
+
this.parser.consume(':');
|
|
11165
|
+
result.type = this.parser.consume('Symbol').value;
|
|
11166
|
+
}
|
|
11167
|
+
return result;
|
|
11141
11168
|
}
|
|
11142
|
-
|
|
11143
|
-
|
|
11144
|
-
|
|
11145
|
-
|
|
11169
|
+
parseRules() {
|
|
11170
|
+
const rules = [];
|
|
11171
|
+
this.parser.consume('{');
|
|
11172
|
+
while (this.parser.hasMore() && this.parser.peek()?.value !== '}') {
|
|
11173
|
+
rules.push(this.parseRule());
|
|
11174
|
+
}
|
|
11175
|
+
this.parser.consume('}');
|
|
11176
|
+
return rules;
|
|
11177
|
+
}
|
|
11178
|
+
parseRule() {
|
|
11179
|
+
const result = {
|
|
11180
|
+
source: this.parseRuleSources(),
|
|
11181
|
+
};
|
|
11182
|
+
if (this.parser.peek()?.value === '->') {
|
|
11183
|
+
this.parser.consume('->');
|
|
11184
|
+
result.target = this.parseRuleTargets();
|
|
11185
|
+
}
|
|
11186
|
+
if (this.parser.peek()?.value === 'then') {
|
|
11187
|
+
this.parser.consume('Symbol', 'then');
|
|
11188
|
+
if (this.parser.peek()?.id === '{') {
|
|
11189
|
+
result.rule = this.parseRules();
|
|
11190
|
+
}
|
|
11191
|
+
else {
|
|
11192
|
+
result.dependent = this.parseRuleDependents();
|
|
11193
|
+
}
|
|
11194
|
+
}
|
|
11195
|
+
if (this.parser.peek()?.id === 'String') {
|
|
11196
|
+
result.name = this.parser.consume().value;
|
|
11197
|
+
}
|
|
11198
|
+
else {
|
|
11199
|
+
result.name = result.source?.[0]?.element;
|
|
11200
|
+
}
|
|
11201
|
+
this.parser.consume(';');
|
|
11202
|
+
return result;
|
|
11146
11203
|
}
|
|
11147
|
-
|
|
11148
|
-
this.
|
|
11149
|
-
|
|
11150
|
-
|
|
11204
|
+
parseRuleSources() {
|
|
11205
|
+
const sources = [this.parseRuleSource()];
|
|
11206
|
+
while (this.parser.hasMore() && this.parser.peek()?.value === ',') {
|
|
11207
|
+
this.parser.consume(',');
|
|
11208
|
+
sources.push(this.parseRuleSource());
|
|
11209
|
+
}
|
|
11210
|
+
return sources;
|
|
11151
11211
|
}
|
|
11152
|
-
|
|
11153
|
-
|
|
11154
|
-
|
|
11155
|
-
|
|
11156
|
-
|
|
11157
|
-
|
|
11158
|
-
|
|
11159
|
-
|
|
11160
|
-
|
|
11161
|
-
|
|
11162
|
-
|
|
11163
|
-
|
|
11164
|
-
|
|
11165
|
-
|
|
11166
|
-
|
|
11167
|
-
|
|
11212
|
+
parseRuleSource() {
|
|
11213
|
+
const result = {};
|
|
11214
|
+
const context = this.parseRuleContext();
|
|
11215
|
+
if (context.includes('.')) {
|
|
11216
|
+
const parts = context.split('.');
|
|
11217
|
+
result.context = parts[0];
|
|
11218
|
+
result.element = parts[1];
|
|
11219
|
+
}
|
|
11220
|
+
else {
|
|
11221
|
+
result.context = context;
|
|
11222
|
+
}
|
|
11223
|
+
if (this.parser.hasMore() && this.parser.peek()?.value === ':') {
|
|
11224
|
+
this.parser.consume(':');
|
|
11225
|
+
result.type = this.parser.consume().value;
|
|
11226
|
+
}
|
|
11227
|
+
if (this.parser.hasMore() && this.parser.peek()?.value === 'default') {
|
|
11228
|
+
this.parser.consume('default');
|
|
11229
|
+
this.parser.consumeAndParse();
|
|
11230
|
+
}
|
|
11231
|
+
if (this.parser.peek()?.value === 'first' ||
|
|
11232
|
+
this.parser.peek()?.value === 'not_first' ||
|
|
11233
|
+
this.parser.peek()?.value === 'last' ||
|
|
11234
|
+
this.parser.peek()?.value === 'not_last' ||
|
|
11235
|
+
this.parser.peek()?.value === 'only_one') {
|
|
11236
|
+
result.listMode = this.parser.consume().value;
|
|
11237
|
+
}
|
|
11238
|
+
if (this.parser.peek()?.value === 'as') {
|
|
11239
|
+
this.parser.consume('Symbol', 'as');
|
|
11240
|
+
result.variable = this.parser.consume().value;
|
|
11241
|
+
}
|
|
11242
|
+
if (this.parser.peek()?.value === 'where') {
|
|
11243
|
+
this.parser.consume('Symbol', 'where');
|
|
11244
|
+
const whereFhirPath = this.parser.consumeAndParse(100 /* OperatorPrecedence.Arrow */);
|
|
11245
|
+
result.condition = whereFhirPath.toString();
|
|
11246
|
+
}
|
|
11247
|
+
if (this.parser.peek()?.value === 'check') {
|
|
11248
|
+
this.parser.consume('Symbol', 'check');
|
|
11249
|
+
const checkFhirPath = this.parser.consumeAndParse(100 /* OperatorPrecedence.Arrow */);
|
|
11250
|
+
result.check = checkFhirPath.toString();
|
|
11251
|
+
}
|
|
11252
|
+
return result;
|
|
11168
11253
|
}
|
|
11169
|
-
|
|
11170
|
-
|
|
11254
|
+
parseRuleTargets() {
|
|
11255
|
+
const targets = [this.parseRuleTarget()];
|
|
11256
|
+
while (this.parser.hasMore() && this.parser.peek()?.value === ',') {
|
|
11257
|
+
this.parser.consume(',');
|
|
11258
|
+
targets.push(this.parseRuleTarget());
|
|
11259
|
+
}
|
|
11260
|
+
return targets;
|
|
11171
11261
|
}
|
|
11172
|
-
|
|
11173
|
-
|
|
11174
|
-
|
|
11262
|
+
parseRuleTarget() {
|
|
11263
|
+
const result = {};
|
|
11264
|
+
const context = this.parseRuleContext();
|
|
11265
|
+
if (context.includes('.')) {
|
|
11266
|
+
const parts = context.split('.');
|
|
11267
|
+
result.contextType = 'variable';
|
|
11268
|
+
result.context = parts[0];
|
|
11269
|
+
result.element = parts[1];
|
|
11270
|
+
}
|
|
11271
|
+
else {
|
|
11272
|
+
result.context = context;
|
|
11273
|
+
}
|
|
11274
|
+
if (this.parser.peek()?.value === '=') {
|
|
11275
|
+
this.parser.consume('=');
|
|
11276
|
+
this.parseRuleTargetTransform(result);
|
|
11277
|
+
}
|
|
11278
|
+
if (this.parser.peek()?.value === 'as') {
|
|
11279
|
+
this.parser.consume('Symbol', 'as');
|
|
11280
|
+
result.variable = this.parser.consume().value;
|
|
11281
|
+
}
|
|
11282
|
+
if (this.parser.peek()?.value === 'first' ||
|
|
11283
|
+
this.parser.peek()?.value === 'share' ||
|
|
11284
|
+
this.parser.peek()?.value === 'last' ||
|
|
11285
|
+
this.parser.peek()?.value === 'collate') {
|
|
11286
|
+
result.listMode = [this.parser.consume().value];
|
|
11287
|
+
}
|
|
11288
|
+
return result;
|
|
11175
11289
|
}
|
|
11176
|
-
|
|
11177
|
-
|
|
11178
|
-
|
|
11290
|
+
parseRuleTargetTransform(result) {
|
|
11291
|
+
result.transform = 'copy';
|
|
11292
|
+
const transformFhirPath = this.parser.consumeAndParse(6 /* OperatorPrecedence.As */);
|
|
11293
|
+
if (transformFhirPath instanceof SymbolAtom) {
|
|
11294
|
+
this.parseRuleTargetSymbol(result, transformFhirPath);
|
|
11295
|
+
}
|
|
11296
|
+
else if (transformFhirPath instanceof FunctionAtom) {
|
|
11297
|
+
this.parseRuleTargetFunction(result, transformFhirPath);
|
|
11298
|
+
}
|
|
11299
|
+
else if (transformFhirPath instanceof LiteralAtom) {
|
|
11300
|
+
this.parseRuleTargetLiteral(result, transformFhirPath);
|
|
11301
|
+
}
|
|
11302
|
+
else {
|
|
11303
|
+
throw new Error(`Unexpected FHIRPath: ${transformFhirPath}`);
|
|
11304
|
+
}
|
|
11179
11305
|
}
|
|
11180
|
-
|
|
11181
|
-
|
|
11182
|
-
this.parser.peek()?.value === 'last' ||
|
|
11183
|
-
this.parser.peek()?.value === 'collate') {
|
|
11184
|
-
result.listMode = [this.parser.consume().value];
|
|
11306
|
+
parseRuleTargetSymbol(result, literalAtom) {
|
|
11307
|
+
result.parameter = [{ valueId: literalAtom.name }];
|
|
11185
11308
|
}
|
|
11186
|
-
|
|
11187
|
-
|
|
11188
|
-
|
|
11189
|
-
|
|
11190
|
-
|
|
11191
|
-
|
|
11309
|
+
parseRuleTargetFunction(result, functionAtom) {
|
|
11310
|
+
const functionName = functionAtom.name;
|
|
11311
|
+
switch (functionName) {
|
|
11312
|
+
case 'create':
|
|
11313
|
+
result.parameter = [
|
|
11314
|
+
{
|
|
11315
|
+
valueString: (functionAtom.args?.[0]).value.value,
|
|
11316
|
+
},
|
|
11317
|
+
];
|
|
11318
|
+
break;
|
|
11319
|
+
case 'translate':
|
|
11320
|
+
result.parameter = [{}];
|
|
11321
|
+
break;
|
|
11322
|
+
default:
|
|
11323
|
+
throw new Error('Unknown target function: ' + functionName);
|
|
11324
|
+
}
|
|
11192
11325
|
}
|
|
11193
|
-
|
|
11194
|
-
|
|
11326
|
+
parseRuleTargetLiteral(result, literalAtom) {
|
|
11327
|
+
switch (literalAtom.value.type) {
|
|
11328
|
+
case 'boolean':
|
|
11329
|
+
result.parameter = [{ valueBoolean: literalAtom.value.value }];
|
|
11330
|
+
break;
|
|
11331
|
+
case 'decimal':
|
|
11332
|
+
result.parameter = [{ valueDecimal: literalAtom.value.value }];
|
|
11333
|
+
break;
|
|
11334
|
+
case 'string':
|
|
11335
|
+
result.parameter = [{ valueString: literalAtom.value.value }];
|
|
11336
|
+
break;
|
|
11337
|
+
default:
|
|
11338
|
+
throw new Error('Unknown target literal type: ' + literalAtom.value.type);
|
|
11339
|
+
}
|
|
11195
11340
|
}
|
|
11196
|
-
|
|
11197
|
-
|
|
11341
|
+
parseRuleContext() {
|
|
11342
|
+
let identifier = this.parser.consume().value;
|
|
11343
|
+
while (this.parser.peek()?.value === '.') {
|
|
11344
|
+
this.parser.consume('.');
|
|
11345
|
+
identifier += '.' + this.parser.consume().value;
|
|
11346
|
+
}
|
|
11347
|
+
return identifier;
|
|
11198
11348
|
}
|
|
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);
|
|
11349
|
+
parseRuleDependents() {
|
|
11350
|
+
const atom = this.parser.consumeAndParse(100 /* OperatorPrecedence.Arrow */);
|
|
11351
|
+
return [
|
|
11352
|
+
{
|
|
11353
|
+
name: atom.name,
|
|
11354
|
+
variable: atom.args.map((arg) => arg.name),
|
|
11355
|
+
},
|
|
11356
|
+
];
|
|
11219
11357
|
}
|
|
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();
|
|
11358
|
+
parseConceptMap() {
|
|
11359
|
+
while (this.parser.peek()?.value !== '}') {
|
|
11360
|
+
this.parser.consume();
|
|
11361
|
+
}
|
|
11362
|
+
this.parser.consume('}');
|
|
11252
11363
|
}
|
|
11253
|
-
|
|
11254
|
-
};
|
|
11364
|
+
}
|
|
11255
11365
|
const fhirPathParserBuilder$1 = initFhirPathParserBuilder()
|
|
11256
11366
|
.registerInfix('->', { precedence: 100 /* OperatorPrecedence.Arrow */ })
|
|
11257
11367
|
.registerInfix(';', { precedence: 200 /* OperatorPrecedence.Semicolon */ });
|
|
@@ -11562,7 +11672,6 @@
|
|
|
11562
11672
|
}
|
|
11563
11673
|
}
|
|
11564
11674
|
|
|
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
11675
|
/*
|
|
11567
11676
|
* This file provides schema validation utilities for FHIR JSON objects.
|
|
11568
11677
|
*
|
|
@@ -11724,14 +11833,11 @@
|
|
|
11724
11833
|
}
|
|
11725
11834
|
class FhirSchemaValidator {
|
|
11726
11835
|
constructor(root) {
|
|
11727
|
-
|
|
11728
|
-
|
|
11729
|
-
_FhirSchemaValidator_root.set(this, void 0);
|
|
11730
|
-
__classPrivateFieldSet(this, _FhirSchemaValidator_issues, [], "f");
|
|
11731
|
-
__classPrivateFieldSet(this, _FhirSchemaValidator_root, root, "f");
|
|
11836
|
+
this.issues = [];
|
|
11837
|
+
this.root = root;
|
|
11732
11838
|
}
|
|
11733
11839
|
validate() {
|
|
11734
|
-
const resource =
|
|
11840
|
+
const resource = this.root;
|
|
11735
11841
|
if (!resource) {
|
|
11736
11842
|
throw new OperationOutcomeError(validationError('Resource is null'));
|
|
11737
11843
|
}
|
|
@@ -11740,142 +11846,172 @@
|
|
|
11740
11846
|
throw new OperationOutcomeError(validationError('Missing resource type'));
|
|
11741
11847
|
}
|
|
11742
11848
|
// Check for "null" once for the entire object hierarchy
|
|
11743
|
-
checkForNull(resource, '',
|
|
11744
|
-
|
|
11745
|
-
if (
|
|
11849
|
+
checkForNull(resource, '', this.issues);
|
|
11850
|
+
this.validateObject(toTypedValue(resource), resourceType);
|
|
11851
|
+
if (this.issues.length > 0) {
|
|
11746
11852
|
throw new OperationOutcomeError({
|
|
11747
11853
|
resourceType: 'OperationOutcome',
|
|
11748
|
-
issue:
|
|
11854
|
+
issue: this.issues,
|
|
11749
11855
|
});
|
|
11750
11856
|
}
|
|
11751
11857
|
}
|
|
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);
|
|
11858
|
+
validateObject(typedValue, path) {
|
|
11859
|
+
const definition = globalSchema.types[typedValue.type];
|
|
11860
|
+
if (!definition) {
|
|
11861
|
+
throw new OperationOutcomeError(validationError('Unknown type: ' + typedValue.type));
|
|
11862
|
+
}
|
|
11863
|
+
const propertyDefinitions = definition.properties;
|
|
11864
|
+
this.checkProperties(path, propertyDefinitions, typedValue);
|
|
11865
|
+
this.checkAdditionalProperties(path, typedValue, propertyDefinitions);
|
|
11764
11866
|
}
|
|
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'));
|
|
11867
|
+
checkProperties(path, propertyDefinitions, typedValue) {
|
|
11868
|
+
for (const [key, elementDefinition] of Object.entries(propertyDefinitions)) {
|
|
11869
|
+
this.checkProperty(path + '.' + key, elementDefinition, typedValue);
|
|
11771
11870
|
}
|
|
11772
|
-
return;
|
|
11773
11871
|
}
|
|
11774
|
-
|
|
11775
|
-
|
|
11776
|
-
|
|
11872
|
+
checkProperty(path, elementDefinition, typedValue) {
|
|
11873
|
+
const propertyName = path.split('.').pop();
|
|
11874
|
+
const value = getTypedPropertyValue(typedValue, propertyName);
|
|
11875
|
+
if (isEmpty(value)) {
|
|
11876
|
+
if (elementDefinition.min !== undefined && elementDefinition.min > 0) {
|
|
11877
|
+
this.issues.push(createStructureIssue(path, 'Missing required property'));
|
|
11878
|
+
}
|
|
11777
11879
|
return;
|
|
11778
11880
|
}
|
|
11779
|
-
|
|
11780
|
-
|
|
11881
|
+
if (elementDefinition.max === '*') {
|
|
11882
|
+
if (!Array.isArray(value)) {
|
|
11883
|
+
this.issues.push(createStructureIssue(path, 'Expected array for property'));
|
|
11884
|
+
return;
|
|
11885
|
+
}
|
|
11886
|
+
for (const item of value) {
|
|
11887
|
+
this.checkPropertyValue(path, elementDefinition, item);
|
|
11888
|
+
}
|
|
11889
|
+
}
|
|
11890
|
+
else {
|
|
11891
|
+
if (Array.isArray(value)) {
|
|
11892
|
+
this.issues.push(createStructureIssue(path, 'Expected single value for property'));
|
|
11893
|
+
return;
|
|
11894
|
+
}
|
|
11895
|
+
this.checkPropertyValue(path, elementDefinition, value);
|
|
11781
11896
|
}
|
|
11782
11897
|
}
|
|
11783
|
-
|
|
11784
|
-
if (
|
|
11785
|
-
|
|
11898
|
+
checkPropertyValue(path, elementDefinition, typedValue) {
|
|
11899
|
+
if (typedValue.value === null) {
|
|
11900
|
+
// Null handled separately
|
|
11786
11901
|
return;
|
|
11787
11902
|
}
|
|
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);
|
|
11903
|
+
if (isLowerCase(typedValue.type.charAt(0))) {
|
|
11904
|
+
this.validatePrimitiveType(elementDefinition, typedValue);
|
|
11905
|
+
}
|
|
11906
|
+
else {
|
|
11907
|
+
this.validateObject(typedValue, path);
|
|
11908
|
+
}
|
|
11820
11909
|
}
|
|
11821
|
-
|
|
11822
|
-
|
|
11823
|
-
|
|
11824
|
-
|
|
11910
|
+
validatePrimitiveType(elementDefinition, typedValue) {
|
|
11911
|
+
const { type, value } = typedValue;
|
|
11912
|
+
if (value === null) {
|
|
11913
|
+
// Null handled separately, so this code should never be reached
|
|
11914
|
+
// Leaving this check in place for now, in case we change the null handling
|
|
11915
|
+
return;
|
|
11916
|
+
}
|
|
11917
|
+
// First, make sure the value is the correct JS type
|
|
11918
|
+
const expectedType = fhirTypeToJsType[typedValue.type];
|
|
11919
|
+
if (typeof value !== expectedType) {
|
|
11920
|
+
this.createIssue(elementDefinition, 'Invalid type for ' + type);
|
|
11921
|
+
return;
|
|
11922
|
+
}
|
|
11923
|
+
// Then, perform additional checks for specialty types
|
|
11924
|
+
if (expectedType === 'string') {
|
|
11925
|
+
this.validateString(elementDefinition, type, value);
|
|
11926
|
+
}
|
|
11927
|
+
else if (expectedType === 'number') {
|
|
11928
|
+
this.validateNumber(elementDefinition, type, value);
|
|
11929
|
+
}
|
|
11825
11930
|
}
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
|
|
11931
|
+
validateString(elementDefinition, type, value) {
|
|
11932
|
+
if (!value.trim()) {
|
|
11933
|
+
this.createIssue(elementDefinition, 'Invalid empty string');
|
|
11934
|
+
return;
|
|
11935
|
+
}
|
|
11936
|
+
// Try to get the regex
|
|
11937
|
+
const valueDefinition = globalSchema.types[type]?.properties?.['value'];
|
|
11938
|
+
if (valueDefinition?.type) {
|
|
11939
|
+
const regex = getExtensionValue(valueDefinition.type[0], 'http://hl7.org/fhir/StructureDefinition/regex');
|
|
11940
|
+
if (regex) {
|
|
11941
|
+
if (!value.match(new RegExp(regex))) {
|
|
11942
|
+
this.createIssue(elementDefinition, 'Invalid ' + type + ' format');
|
|
11943
|
+
}
|
|
11833
11944
|
}
|
|
11834
11945
|
}
|
|
11835
11946
|
}
|
|
11836
|
-
|
|
11837
|
-
|
|
11838
|
-
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
|
|
11843
|
-
|
|
11844
|
-
|
|
11845
|
-
|
|
11846
|
-
|
|
11847
|
-
|
|
11848
|
-
|
|
11947
|
+
validateNumber(elementDefinition, type, value) {
|
|
11948
|
+
if (isNaN(value) || !isFinite(value)) {
|
|
11949
|
+
this.createIssue(elementDefinition, 'Invalid ' + type + ' value');
|
|
11950
|
+
return;
|
|
11951
|
+
}
|
|
11952
|
+
if (isIntegerType(type) && !Number.isInteger(value)) {
|
|
11953
|
+
this.createIssue(elementDefinition, 'Number is not an integer');
|
|
11954
|
+
}
|
|
11955
|
+
if (type === exports.PropertyType.positiveInt && value <= 0) {
|
|
11956
|
+
this.createIssue(elementDefinition, 'Number is less than or equal to zero');
|
|
11957
|
+
}
|
|
11958
|
+
if (type === exports.PropertyType.unsignedInt && value < 0) {
|
|
11959
|
+
this.createIssue(elementDefinition, 'Number is negative');
|
|
11960
|
+
}
|
|
11849
11961
|
}
|
|
11850
|
-
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
|
|
11962
|
+
checkAdditionalProperties(path, typedValue, propertyDefinitions) {
|
|
11963
|
+
const object = typedValue.value;
|
|
11964
|
+
for (const key of Object.keys(object)) {
|
|
11965
|
+
this.checkAdditionalProperty(path, key, typedValue, propertyDefinitions);
|
|
11966
|
+
}
|
|
11854
11967
|
}
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
|
|
11861
|
-
|
|
11968
|
+
/**
|
|
11969
|
+
* Checks if the given property is allowed on the given object.
|
|
11970
|
+
* @param path The path of the current object.
|
|
11971
|
+
* @param key The key of a property to check.
|
|
11972
|
+
* @param typedValue The current object.
|
|
11973
|
+
* @param propertyDefinitions The property definitions of the current object.
|
|
11974
|
+
*/
|
|
11975
|
+
checkAdditionalProperty(path, key, typedValue, propertyDefinitions) {
|
|
11976
|
+
if (!baseResourceProperties.has(key) &&
|
|
11977
|
+
!(key in propertyDefinitions) &&
|
|
11978
|
+
!isChoiceOfType(key, typedValue, propertyDefinitions) &&
|
|
11979
|
+
!this.checkPrimitiveElement(path, key, typedValue)) {
|
|
11980
|
+
const expression = `${path}.${key}`;
|
|
11981
|
+
this.issues.push(createStructureIssue(expression, `Invalid additional property "${expression}"`));
|
|
11982
|
+
}
|
|
11862
11983
|
}
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
|
|
11866
|
-
|
|
11984
|
+
/**
|
|
11985
|
+
* Checks the element for a primitive.
|
|
11986
|
+
*
|
|
11987
|
+
* FHIR elements with primitive data types are represented in two parts:
|
|
11988
|
+
* 1) A JSON property with the name of the element, which has a JSON type of number, boolean, or string
|
|
11989
|
+
* 2) a JSON property with _ prepended to the name of the element, which, if present, contains the value's id and/or extensions
|
|
11990
|
+
*
|
|
11991
|
+
* See: https://hl7.org/fhir/json.html#primitive
|
|
11992
|
+
*
|
|
11993
|
+
* @param path The path to the property
|
|
11994
|
+
* @param key
|
|
11995
|
+
* @param typedValue
|
|
11996
|
+
*/
|
|
11997
|
+
checkPrimitiveElement(path, key, typedValue) {
|
|
11998
|
+
// Primitive element starts with underscore
|
|
11999
|
+
if (!key.startsWith('_')) {
|
|
12000
|
+
return false;
|
|
12001
|
+
}
|
|
12002
|
+
// Validate the non-underscore property exists
|
|
12003
|
+
const primitiveKey = key.slice(1);
|
|
12004
|
+
if (!(primitiveKey in typedValue.value)) {
|
|
12005
|
+
return false;
|
|
12006
|
+
}
|
|
12007
|
+
// Then validate the element
|
|
12008
|
+
this.validateObject({ type: 'Element', value: typedValue.value[key] }, path);
|
|
12009
|
+
return true;
|
|
11867
12010
|
}
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
if (!(primitiveKey in typedValue.value)) {
|
|
11871
|
-
return false;
|
|
12011
|
+
createIssue(elementDefinition, message) {
|
|
12012
|
+
this.issues.push(createStructureIssue(elementDefinition.path, message));
|
|
11872
12013
|
}
|
|
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
|
-
};
|
|
12014
|
+
}
|
|
11879
12015
|
function isIntegerType(propertyType) {
|
|
11880
12016
|
return (propertyType === exports.PropertyType.integer ||
|
|
11881
12017
|
propertyType === exports.PropertyType.positiveInt ||
|
|
@@ -12183,7 +12319,18 @@
|
|
|
12183
12319
|
* @returns A parsed SearchRequest.
|
|
12184
12320
|
*/
|
|
12185
12321
|
function parseSearchRequest(resourceType, query) {
|
|
12186
|
-
|
|
12322
|
+
const queryArray = [];
|
|
12323
|
+
for (const [key, value] of Object.entries(query)) {
|
|
12324
|
+
if (Array.isArray(value)) {
|
|
12325
|
+
for (let i = 0; i < value.length; i++) {
|
|
12326
|
+
queryArray.push([key, value[i]]);
|
|
12327
|
+
}
|
|
12328
|
+
}
|
|
12329
|
+
else {
|
|
12330
|
+
queryArray.push([key, value || '']);
|
|
12331
|
+
}
|
|
12332
|
+
}
|
|
12333
|
+
return parseSearchImpl(resourceType, queryArray);
|
|
12187
12334
|
}
|
|
12188
12335
|
/**
|
|
12189
12336
|
* Parses a search URL into a search request.
|
|
@@ -12192,7 +12339,7 @@
|
|
|
12192
12339
|
*/
|
|
12193
12340
|
function parseSearchUrl(url) {
|
|
12194
12341
|
const resourceType = url.pathname.split('/').filter(Boolean).pop();
|
|
12195
|
-
return parseSearchImpl(resourceType,
|
|
12342
|
+
return parseSearchImpl(resourceType, url.searchParams.entries());
|
|
12196
12343
|
}
|
|
12197
12344
|
/**
|
|
12198
12345
|
* Parses a URL string into a SearchRequest.
|
|
@@ -12206,13 +12353,8 @@
|
|
|
12206
12353
|
const searchRequest = {
|
|
12207
12354
|
resourceType,
|
|
12208
12355
|
};
|
|
12209
|
-
for (const [key, value] of
|
|
12210
|
-
|
|
12211
|
-
value.forEach((element) => parseKeyValue(searchRequest, key, element));
|
|
12212
|
-
}
|
|
12213
|
-
else {
|
|
12214
|
-
parseKeyValue(searchRequest, key, value ?? '');
|
|
12215
|
-
}
|
|
12356
|
+
for (const [key, value] of query) {
|
|
12357
|
+
parseKeyValue(searchRequest, key, value);
|
|
12216
12358
|
}
|
|
12217
12359
|
return searchRequest;
|
|
12218
12360
|
}
|
|
@@ -12613,6 +12755,7 @@
|
|
|
12613
12755
|
exports.UnaryOperatorAtom = UnaryOperatorAtom;
|
|
12614
12756
|
exports.UnionAtom = UnionAtom;
|
|
12615
12757
|
exports.XorAtom = XorAtom;
|
|
12758
|
+
exports.accepted = accepted;
|
|
12616
12759
|
exports.allOk = allOk;
|
|
12617
12760
|
exports.arrayBufferToBase64 = arrayBufferToBase64;
|
|
12618
12761
|
exports.arrayBufferToHex = arrayBufferToHex;
|