@medplum/core 0.4.0 → 0.5.1

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/esm/index.js CHANGED
@@ -21,42 +21,59 @@ function __awaiter(thisArg, _arguments, P, generator) {
21
21
  function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
22
22
  step((generator = generator.apply(thisArg, _arguments || [])).next());
23
23
  });
24
+ }
25
+
26
+ function __classPrivateFieldGet(receiver, state, kind, f) {
27
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
28
+ 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");
29
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
30
+ }
31
+
32
+ function __classPrivateFieldSet(receiver, state, value, kind, f) {
33
+ if (kind === "m") throw new TypeError("Private method is not writable");
34
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
35
+ 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");
36
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
24
37
  }
25
38
 
39
+ var _LRUCache_instances, _LRUCache_max, _LRUCache_cache, _LRUCache_first;
26
40
  /**
27
41
  * LRU cache (least recently used)
28
42
  * Source: https://stackoverflow.com/a/46432113
29
43
  */
30
44
  class LRUCache {
31
45
  constructor(max = 10) {
32
- this.max = max;
33
- this.cache = new Map();
46
+ _LRUCache_instances.add(this);
47
+ _LRUCache_max.set(this, void 0);
48
+ _LRUCache_cache.set(this, void 0);
49
+ __classPrivateFieldSet(this, _LRUCache_max, max, "f");
50
+ __classPrivateFieldSet(this, _LRUCache_cache, new Map(), "f");
34
51
  }
35
52
  clear() {
36
- this.cache.clear();
53
+ __classPrivateFieldGet(this, _LRUCache_cache, "f").clear();
37
54
  }
38
55
  get(key) {
39
- const item = this.cache.get(key);
56
+ const item = __classPrivateFieldGet(this, _LRUCache_cache, "f").get(key);
40
57
  if (item) {
41
- this.cache.delete(key);
42
- this.cache.set(key, item);
58
+ __classPrivateFieldGet(this, _LRUCache_cache, "f").delete(key);
59
+ __classPrivateFieldGet(this, _LRUCache_cache, "f").set(key, item);
43
60
  }
44
61
  return item;
45
62
  }
46
63
  set(key, val) {
47
- if (this.cache.has(key)) {
48
- this.cache.delete(key);
64
+ if (__classPrivateFieldGet(this, _LRUCache_cache, "f").has(key)) {
65
+ __classPrivateFieldGet(this, _LRUCache_cache, "f").delete(key);
49
66
  }
50
- else if (this.cache.size >= this.max) {
51
- this.cache.delete(this.first());
67
+ else if (__classPrivateFieldGet(this, _LRUCache_cache, "f").size >= __classPrivateFieldGet(this, _LRUCache_max, "f")) {
68
+ __classPrivateFieldGet(this, _LRUCache_cache, "f").delete(__classPrivateFieldGet(this, _LRUCache_instances, "m", _LRUCache_first).call(this));
52
69
  }
53
- this.cache.set(key, val);
54
- }
55
- first() {
56
- // This works because the Map class maintains ordered keys.
57
- return this.cache.keys().next().value;
70
+ __classPrivateFieldGet(this, _LRUCache_cache, "f").set(key, val);
58
71
  }
59
- }
72
+ }
73
+ _LRUCache_max = new WeakMap(), _LRUCache_cache = new WeakMap(), _LRUCache_instances = new WeakSet(), _LRUCache_first = function _LRUCache_first() {
74
+ // This works because the Map class maintains ordered keys.
75
+ return __classPrivateFieldGet(this, _LRUCache_cache, "f").keys().next().value;
76
+ };
60
77
 
61
78
  function formatAddress(address, options) {
62
79
  const builder = [];
@@ -125,6 +142,15 @@ function createReference(resource) {
125
142
  function getReferenceString(resource) {
126
143
  return resource.resourceType + '/' + resource.id;
127
144
  }
145
+ /**
146
+ * Returns the ID portion of a reference.
147
+ * @param reference A FHIR reference.
148
+ * @returns The ID portion of a reference.
149
+ */
150
+ function resolveId(reference) {
151
+ var _a;
152
+ return (_a = reference === null || reference === void 0 ? void 0 : reference.reference) === null || _a === void 0 ? void 0 : _a.split('/')[1];
153
+ }
128
154
  /**
129
155
  * Returns true if the resource is a "ProfileResource".
130
156
  * @param resource The FHIR resource.
@@ -347,18 +373,20 @@ function encryptSHA256(str) {
347
373
  /*
348
374
  * Based on: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget
349
375
  */
376
+ var _EventTarget_listeners;
350
377
  class EventTarget {
351
378
  constructor() {
352
- this.listeners = {};
379
+ _EventTarget_listeners.set(this, void 0);
380
+ __classPrivateFieldSet(this, _EventTarget_listeners, {}, "f");
353
381
  }
354
382
  addEventListener(type, callback) {
355
- if (!this.listeners[type]) {
356
- this.listeners[type] = [];
383
+ if (!__classPrivateFieldGet(this, _EventTarget_listeners, "f")[type]) {
384
+ __classPrivateFieldGet(this, _EventTarget_listeners, "f")[type] = [];
357
385
  }
358
- this.listeners[type].push(callback);
386
+ __classPrivateFieldGet(this, _EventTarget_listeners, "f")[type].push(callback);
359
387
  }
360
388
  removeEventListeneer(type, callback) {
361
- const array = this.listeners[type];
389
+ const array = __classPrivateFieldGet(this, _EventTarget_listeners, "f")[type];
362
390
  if (!array) {
363
391
  return;
364
392
  }
@@ -370,13 +398,14 @@ class EventTarget {
370
398
  }
371
399
  }
372
400
  dispatchEvent(event) {
373
- const array = this.listeners[event.type];
401
+ const array = __classPrivateFieldGet(this, _EventTarget_listeners, "f")[event.type];
374
402
  if (array) {
375
403
  array.forEach((listener) => listener.call(this, event));
376
404
  }
377
405
  return !event.defaultPrevented;
378
406
  }
379
- }
407
+ }
408
+ _EventTarget_listeners = new WeakMap();
380
409
 
381
410
  /**
382
411
  * Decodes a section of a JWT.
@@ -609,13 +638,17 @@ const PREFIX_OPERATORS = [
609
638
  * @returns Parsed search definition.
610
639
  */
611
640
  function parseSearchDefinition(location) {
612
- const resourceType = location.pathname.split('/').pop();
641
+ const resourceType = location.pathname
642
+ .replace(/(^\/)|(\/$)/g, '') // Remove leading and trailing slashes
643
+ .split('/')
644
+ .pop();
613
645
  const params = new URLSearchParams(location.search);
614
- const filters = [];
615
- const sortRules = [];
616
- let fields;
617
- let page = 0;
618
- let count = 10;
646
+ let filters = undefined;
647
+ let sortRules = undefined;
648
+ let fields = undefined;
649
+ let page = undefined;
650
+ let count = undefined;
651
+ let total = undefined;
619
652
  params.forEach((value, key) => {
620
653
  if (key === '_fields') {
621
654
  fields = value.split(',');
@@ -626,10 +659,15 @@ function parseSearchDefinition(location) {
626
659
  else if (key === '_count') {
627
660
  count = parseInt(value);
628
661
  }
662
+ else if (key === '_total') {
663
+ total = value;
664
+ }
629
665
  else if (key === '_sort') {
666
+ sortRules = sortRules || [];
630
667
  sortRules.push(parseSortRule(value));
631
668
  }
632
669
  else {
670
+ filters = filters || [];
633
671
  filters.push(parseSearchFilter(key, value));
634
672
  }
635
673
  });
@@ -639,6 +677,7 @@ function parseSearchDefinition(location) {
639
677
  fields,
640
678
  page,
641
679
  count,
680
+ total,
642
681
  sortRules,
643
682
  };
644
683
  }
@@ -707,13 +746,9 @@ function formatSearchQuery(definition) {
707
746
  params.push('_fields=' + definition.fields.join(','));
708
747
  }
709
748
  if (definition.filters) {
710
- definition.filters.forEach((filter) => {
711
- const modifier = MODIFIER_OPERATORS.includes(filter.operator) ? ':' + filter.operator : '';
712
- const prefix = PREFIX_OPERATORS.includes(filter.operator) ? filter.operator : '';
713
- params.push(`${filter.code}${modifier}=${prefix}${encodeURIComponent(filter.value)}`);
714
- });
749
+ definition.filters.forEach((filter) => params.push(formatFilter(filter)));
715
750
  }
716
- if (definition.sortRules) {
751
+ if (definition.sortRules && definition.sortRules.length > 0) {
717
752
  params.push(formatSortRules(definition.sortRules));
718
753
  }
719
754
  if (definition.page && definition.page > 0) {
@@ -722,12 +757,20 @@ function formatSearchQuery(definition) {
722
757
  if (definition.count && definition.count > 0) {
723
758
  params.push('_count=' + definition.count);
724
759
  }
760
+ if (definition.total) {
761
+ params.push('_total=' + encodeURIComponent(definition.total));
762
+ }
725
763
  if (params.length === 0) {
726
764
  return '';
727
765
  }
728
766
  params.sort();
729
767
  return '?' + params.join('&');
730
768
  }
769
+ function formatFilter(filter) {
770
+ const modifier = MODIFIER_OPERATORS.includes(filter.operator) ? ':' + filter.operator : '';
771
+ const prefix = PREFIX_OPERATORS.includes(filter.operator) ? filter.operator : '';
772
+ return `${filter.code}${modifier}=${prefix}${encodeURIComponent(filter.value)}`;
773
+ }
731
774
  function formatSortRules(sortRules) {
732
775
  if (!sortRules || sortRules.length === 0) {
733
776
  return '';
@@ -735,6 +778,7 @@ function formatSortRules(sortRules) {
735
778
  return '_sort=' + sortRules.map((sr) => (sr.descending ? '-' + sr.code : sr.code)).join(',');
736
779
  }
737
780
 
781
+ var _ClientStorage_storage, _MemoryStorage_data;
738
782
  /**
739
783
  * The ClientStorage class is a utility class for storing strings and objects.
740
784
  *
@@ -744,20 +788,21 @@ function formatSortRules(sortRules) {
744
788
  */
745
789
  class ClientStorage {
746
790
  constructor() {
747
- this.storage = typeof localStorage !== 'undefined' ? localStorage : new MemoryStorage();
791
+ _ClientStorage_storage.set(this, void 0);
792
+ __classPrivateFieldSet(this, _ClientStorage_storage, typeof localStorage !== 'undefined' ? localStorage : new MemoryStorage(), "f");
748
793
  }
749
794
  clear() {
750
- this.storage.clear();
795
+ __classPrivateFieldGet(this, _ClientStorage_storage, "f").clear();
751
796
  }
752
797
  getString(key) {
753
- return this.storage.getItem(key) || undefined;
798
+ return __classPrivateFieldGet(this, _ClientStorage_storage, "f").getItem(key) || undefined;
754
799
  }
755
800
  setString(key, value) {
756
801
  if (value) {
757
- this.storage.setItem(key, value);
802
+ __classPrivateFieldGet(this, _ClientStorage_storage, "f").setItem(key, value);
758
803
  }
759
804
  else {
760
- this.storage.removeItem(key);
805
+ __classPrivateFieldGet(this, _ClientStorage_storage, "f").removeItem(key);
761
806
  }
762
807
  }
763
808
  getObject(key) {
@@ -768,56 +813,59 @@ class ClientStorage {
768
813
  this.setString(key, value ? stringify(value) : undefined);
769
814
  }
770
815
  }
816
+ _ClientStorage_storage = new WeakMap();
771
817
  /**
772
818
  * The MemoryStorage class is a minimal in-memory implementation of the Storage interface.
773
819
  */
774
820
  class MemoryStorage {
775
821
  constructor() {
776
- this.data = new Map();
822
+ _MemoryStorage_data.set(this, void 0);
823
+ __classPrivateFieldSet(this, _MemoryStorage_data, new Map(), "f");
777
824
  }
778
825
  /**
779
826
  * Returns the number of key/value pairs.
780
827
  */
781
828
  get length() {
782
- return this.data.size;
829
+ return __classPrivateFieldGet(this, _MemoryStorage_data, "f").size;
783
830
  }
784
831
  /**
785
832
  * Removes all key/value pairs, if there are any.
786
833
  */
787
834
  clear() {
788
- this.data.clear();
835
+ __classPrivateFieldGet(this, _MemoryStorage_data, "f").clear();
789
836
  }
790
837
  /**
791
838
  * Returns the current value associated with the given key, or null if the given key does not exist.
792
839
  */
793
840
  getItem(key) {
794
841
  var _a;
795
- return (_a = this.data.get(key)) !== null && _a !== void 0 ? _a : null;
842
+ return (_a = __classPrivateFieldGet(this, _MemoryStorage_data, "f").get(key)) !== null && _a !== void 0 ? _a : null;
796
843
  }
797
844
  /**
798
845
  * Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
799
846
  */
800
847
  setItem(key, value) {
801
848
  if (value) {
802
- this.data.set(key, value);
849
+ __classPrivateFieldGet(this, _MemoryStorage_data, "f").set(key, value);
803
850
  }
804
851
  else {
805
- this.data.delete(key);
852
+ __classPrivateFieldGet(this, _MemoryStorage_data, "f").delete(key);
806
853
  }
807
854
  }
808
855
  /**
809
856
  * Removes the key/value pair with the given key, if a key/value pair with the given key exists.
810
857
  */
811
858
  removeItem(key) {
812
- this.data.delete(key);
859
+ __classPrivateFieldGet(this, _MemoryStorage_data, "f").delete(key);
813
860
  }
814
861
  /**
815
862
  * Returns the name of the nth key, or null if n is greater than or equal to the number of key/value pairs.
816
863
  */
817
864
  key(index) {
818
- return Array.from(this.data.keys())[index];
865
+ return Array.from(__classPrivateFieldGet(this, _MemoryStorage_data, "f").keys())[index];
819
866
  }
820
- }
867
+ }
868
+ _MemoryStorage_data = new WeakMap();
821
869
 
822
870
  /**
823
871
  * List of property types.
@@ -894,6 +942,21 @@ var PropertyType;
894
942
  function createSchema() {
895
943
  return { types: {} };
896
944
  }
945
+ function createTypeSchema(typeName, description) {
946
+ return {
947
+ display: typeName,
948
+ description,
949
+ properties: {},
950
+ searchParams: {
951
+ _lastUpdated: {
952
+ base: [typeName],
953
+ code: '_lastUpdated',
954
+ type: 'date',
955
+ expression: typeName + '.meta.lastUpdated',
956
+ },
957
+ },
958
+ };
959
+ }
897
960
  /**
898
961
  * Indexes a StructureDefinition for fast lookup.
899
962
  * See comments on IndexedStructureDefinition for more details.
@@ -906,11 +969,7 @@ function indexStructureDefinition(schema, structureDefinition) {
906
969
  if (!typeName) {
907
970
  return;
908
971
  }
909
- schema.types[typeName] = {
910
- display: typeName,
911
- description: structureDefinition.description,
912
- properties: {},
913
- };
972
+ schema.types[typeName] = createTypeSchema(typeName, structureDefinition.description);
914
973
  const elements = (_a = structureDefinition.snapshot) === null || _a === void 0 ? void 0 : _a.element;
915
974
  if (elements) {
916
975
  // Filter out any elements missing path or type
@@ -938,12 +997,8 @@ function indexType(schema, element) {
938
997
  const parts = path.split('.');
939
998
  const typeName = buildTypeName(parts);
940
999
  if (!(typeName in schema.types)) {
941
- schema.types[typeName] = {
942
- display: typeName,
943
- description: element.definition,
944
- parentType: buildTypeName(parts.slice(0, parts.length - 1)),
945
- properties: {},
946
- };
1000
+ schema.types[typeName] = createTypeSchema(typeName, element.definition);
1001
+ schema.types[typeName].parentType = buildTypeName(parts.slice(0, parts.length - 1));
947
1002
  }
948
1003
  }
949
1004
  /**
@@ -991,6 +1046,10 @@ function getPropertyDisplayName(property) {
991
1046
  // For example, for path "Patient.birthDate"
992
1047
  // the property name is "birthDate"
993
1048
  const propertyName = property.path.replaceAll('[x]', '').split('.').pop();
1049
+ // Special case for ID
1050
+ if (propertyName === 'id') {
1051
+ return 'ID';
1052
+ }
994
1053
  // Split by capital letters
995
1054
  // Capitalize the first letter of each word
996
1055
  // Join together with spaces in between
@@ -1006,6 +1065,8 @@ function getPropertyDisplayName(property) {
1006
1065
  }
1007
1066
 
1008
1067
  // PKCE auth ased on:
1068
+ // https://aws.amazon.com/blogs/security/how-to-add-authentication-single-page-web-application-with-amazon-cognito-oauth2-implementation/
1069
+ var _MedplumClient_instances, _MedplumClient_fetch, _MedplumClient_storage, _MedplumClient_schema, _MedplumClient_resourceCache, _MedplumClient_baseUrl, _MedplumClient_clientId, _MedplumClient_authorizeUrl, _MedplumClient_tokenUrl, _MedplumClient_logoutUrl, _MedplumClient_onUnauthenticated, _MedplumClient_accessToken, _MedplumClient_refreshToken, _MedplumClient_refreshPromise, _MedplumClient_profilePromise, _MedplumClient_profile, _MedplumClient_config, _MedplumClient_addLogin, _MedplumClient_refreshProfile, _MedplumClient_request, _MedplumClient_buildFetchOptions, _MedplumClient_handleUnauthenticated, _MedplumClient_startPkce, _MedplumClient_requestAuthorization, _MedplumClient_refresh, _MedplumClient_fetchTokens, _MedplumClient_verifyTokens, _MedplumClient_setupStorageListener;
1009
1070
  const DEFAULT_BASE_URL = 'https://api.medplum.com/';
1010
1071
  const DEFAULT_SCOPE = 'launch/patient openid fhirUser offline_access user/*.*';
1011
1072
  const DEFAULT_RESOURCE_CACHE_SIZE = 1000;
@@ -1016,6 +1077,23 @@ class MedplumClient extends EventTarget {
1016
1077
  constructor(options) {
1017
1078
  var _a;
1018
1079
  super();
1080
+ _MedplumClient_instances.add(this);
1081
+ _MedplumClient_fetch.set(this, void 0);
1082
+ _MedplumClient_storage.set(this, void 0);
1083
+ _MedplumClient_schema.set(this, void 0);
1084
+ _MedplumClient_resourceCache.set(this, void 0);
1085
+ _MedplumClient_baseUrl.set(this, void 0);
1086
+ _MedplumClient_clientId.set(this, void 0);
1087
+ _MedplumClient_authorizeUrl.set(this, void 0);
1088
+ _MedplumClient_tokenUrl.set(this, void 0);
1089
+ _MedplumClient_logoutUrl.set(this, void 0);
1090
+ _MedplumClient_onUnauthenticated.set(this, void 0);
1091
+ _MedplumClient_accessToken.set(this, void 0);
1092
+ _MedplumClient_refreshToken.set(this, void 0);
1093
+ _MedplumClient_refreshPromise.set(this, void 0);
1094
+ _MedplumClient_profilePromise.set(this, void 0);
1095
+ _MedplumClient_profile.set(this, void 0);
1096
+ _MedplumClient_config.set(this, void 0);
1019
1097
  if (options === null || options === void 0 ? void 0 : options.baseUrl) {
1020
1098
  if (!options.baseUrl.startsWith('http')) {
1021
1099
  throw new Error('Base URL must start with http or https');
@@ -1024,39 +1102,47 @@ class MedplumClient extends EventTarget {
1024
1102
  throw new Error('Base URL must end with a trailing slash');
1025
1103
  }
1026
1104
  }
1027
- this.fetch = (options === null || options === void 0 ? void 0 : options.fetch) || window.fetch.bind(window);
1028
- this.storage = new ClientStorage();
1029
- this.schema = createSchema();
1030
- this.resourceCache = new LRUCache((_a = options === null || options === void 0 ? void 0 : options.resourceCacheSize) !== null && _a !== void 0 ? _a : DEFAULT_RESOURCE_CACHE_SIZE);
1031
- this.baseUrl = (options === null || options === void 0 ? void 0 : options.baseUrl) || DEFAULT_BASE_URL;
1032
- this.clientId = (options === null || options === void 0 ? void 0 : options.clientId) || '';
1033
- this.authorizeUrl = (options === null || options === void 0 ? void 0 : options.authorizeUrl) || this.baseUrl + 'oauth2/authorize';
1034
- this.tokenUrl = (options === null || options === void 0 ? void 0 : options.tokenUrl) || this.baseUrl + 'oauth2/token';
1035
- this.logoutUrl = (options === null || options === void 0 ? void 0 : options.logoutUrl) || this.baseUrl + 'oauth2/logout';
1036
- this.onUnauthenticated = options === null || options === void 0 ? void 0 : options.onUnauthenticated;
1037
- this.loading = false;
1038
- this.refreshProfile().catch(console.log);
1039
- this.setupStorageListener();
1105
+ __classPrivateFieldSet(this, _MedplumClient_fetch, (options === null || options === void 0 ? void 0 : options.fetch) || window.fetch.bind(window), "f");
1106
+ __classPrivateFieldSet(this, _MedplumClient_storage, new ClientStorage(), "f");
1107
+ __classPrivateFieldSet(this, _MedplumClient_schema, createSchema(), "f");
1108
+ __classPrivateFieldSet(this, _MedplumClient_resourceCache, new LRUCache((_a = options === null || options === void 0 ? void 0 : options.resourceCacheSize) !== null && _a !== void 0 ? _a : DEFAULT_RESOURCE_CACHE_SIZE), "f");
1109
+ __classPrivateFieldSet(this, _MedplumClient_baseUrl, (options === null || options === void 0 ? void 0 : options.baseUrl) || DEFAULT_BASE_URL, "f");
1110
+ __classPrivateFieldSet(this, _MedplumClient_clientId, (options === null || options === void 0 ? void 0 : options.clientId) || '', "f");
1111
+ __classPrivateFieldSet(this, _MedplumClient_authorizeUrl, (options === null || options === void 0 ? void 0 : options.authorizeUrl) || __classPrivateFieldGet(this, _MedplumClient_baseUrl, "f") + 'oauth2/authorize', "f");
1112
+ __classPrivateFieldSet(this, _MedplumClient_tokenUrl, (options === null || options === void 0 ? void 0 : options.tokenUrl) || __classPrivateFieldGet(this, _MedplumClient_baseUrl, "f") + 'oauth2/token', "f");
1113
+ __classPrivateFieldSet(this, _MedplumClient_logoutUrl, (options === null || options === void 0 ? void 0 : options.logoutUrl) || __classPrivateFieldGet(this, _MedplumClient_baseUrl, "f") + 'oauth2/logout', "f");
1114
+ __classPrivateFieldSet(this, _MedplumClient_onUnauthenticated, options === null || options === void 0 ? void 0 : options.onUnauthenticated, "f");
1115
+ const activeLogin = this.getActiveLogin();
1116
+ if (activeLogin) {
1117
+ __classPrivateFieldSet(this, _MedplumClient_accessToken, activeLogin.accessToken, "f");
1118
+ __classPrivateFieldSet(this, _MedplumClient_refreshToken, activeLogin.refreshToken, "f");
1119
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_refreshProfile).call(this).catch(console.log);
1120
+ }
1121
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setupStorageListener).call(this);
1040
1122
  }
1041
1123
  /**
1042
1124
  * Clears all auth state including local storage and session storage.
1043
1125
  */
1044
1126
  clear() {
1045
- this.storage.clear();
1046
- this.resourceCache.clear();
1127
+ __classPrivateFieldGet(this, _MedplumClient_storage, "f").clear();
1128
+ __classPrivateFieldGet(this, _MedplumClient_resourceCache, "f").clear();
1129
+ __classPrivateFieldSet(this, _MedplumClient_accessToken, undefined, "f");
1130
+ __classPrivateFieldSet(this, _MedplumClient_refreshToken, undefined, "f");
1131
+ __classPrivateFieldSet(this, _MedplumClient_profile, undefined, "f");
1132
+ __classPrivateFieldSet(this, _MedplumClient_config, undefined, "f");
1047
1133
  this.dispatchEvent({ type: 'change' });
1048
1134
  }
1049
1135
  get(url) {
1050
- return this.request('GET', url);
1136
+ return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, 'GET', url);
1051
1137
  }
1052
1138
  post(url, body, contentType) {
1053
- return this.request('POST', url, contentType, body);
1139
+ return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, 'POST', url, contentType, body);
1054
1140
  }
1055
1141
  put(url, body, contentType) {
1056
- return this.request('PUT', url, contentType, body);
1142
+ return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, 'PUT', url, contentType, body);
1057
1143
  }
1058
1144
  delete(url) {
1059
- return this.request('DELETE', url);
1145
+ return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, 'DELETE', url);
1060
1146
  }
1061
1147
  /**
1062
1148
  * Tries to register a new user.
@@ -1078,12 +1164,12 @@ class MedplumClient extends EventTarget {
1078
1164
  */
1079
1165
  startLogin(email, password, remember) {
1080
1166
  return __awaiter(this, void 0, void 0, function* () {
1081
- yield this.startPkce();
1167
+ yield __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_startPkce).call(this);
1082
1168
  return this.post('auth/login', {
1083
- clientId: this.clientId,
1169
+ clientId: __classPrivateFieldGet(this, _MedplumClient_clientId, "f"),
1084
1170
  scope: DEFAULT_SCOPE,
1085
1171
  codeChallengeMethod: 'S256',
1086
- codeChallenge: this.storage.getString('codeChallenge'),
1172
+ codeChallenge: __classPrivateFieldGet(this, _MedplumClient_storage, "f").getString('codeChallenge'),
1087
1173
  email,
1088
1174
  password,
1089
1175
  remember: !!remember,
@@ -1099,7 +1185,7 @@ class MedplumClient extends EventTarget {
1099
1185
  */
1100
1186
  startGoogleLogin(googleResponse) {
1101
1187
  return __awaiter(this, void 0, void 0, function* () {
1102
- yield this.startPkce();
1188
+ yield __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_startPkce).call(this);
1103
1189
  return this.post('auth/google', googleResponse);
1104
1190
  });
1105
1191
  }
@@ -1120,7 +1206,7 @@ class MedplumClient extends EventTarget {
1120
1206
  const urlParams = new URLSearchParams(window.location.search);
1121
1207
  const code = urlParams.get('code');
1122
1208
  if (!code) {
1123
- this.requestAuthorization();
1209
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_requestAuthorization).call(this);
1124
1210
  return undefined;
1125
1211
  }
1126
1212
  else {
@@ -1132,7 +1218,7 @@ class MedplumClient extends EventTarget {
1132
1218
  * See: https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html
1133
1219
  */
1134
1220
  signOutWithRedirect() {
1135
- window.location.assign(this.logoutUrl);
1221
+ window.location.assign(__classPrivateFieldGet(this, _MedplumClient_logoutUrl, "f"));
1136
1222
  }
1137
1223
  /**
1138
1224
  * Builds a FHIR URL from a collection of URL path components.
@@ -1141,7 +1227,7 @@ class MedplumClient extends EventTarget {
1141
1227
  * @returns The well-formed FHIR URL.
1142
1228
  */
1143
1229
  fhirUrl(...path) {
1144
- const builder = [this.baseUrl, 'fhir/R4'];
1230
+ const builder = [__classPrivateFieldGet(this, _MedplumClient_baseUrl, "f"), 'fhir/R4'];
1145
1231
  path.forEach((p) => builder.push('/', encodeURIComponent(p)));
1146
1232
  return builder.join('');
1147
1233
  }
@@ -1172,7 +1258,7 @@ class MedplumClient extends EventTarget {
1172
1258
  * @returns The resource if it is available in the cache; undefined otherwise.
1173
1259
  */
1174
1260
  getCached(resourceType, id) {
1175
- const cached = this.resourceCache.get(resourceType + '/' + id);
1261
+ const cached = __classPrivateFieldGet(this, _MedplumClient_resourceCache, "f").get(resourceType + '/' + id);
1176
1262
  if (cached && !('then' in cached)) {
1177
1263
  return cached;
1178
1264
  }
@@ -1185,7 +1271,7 @@ class MedplumClient extends EventTarget {
1185
1271
  * @returns The resource if it is available in the cache; undefined otherwise.
1186
1272
  */
1187
1273
  getCachedReference(reference) {
1188
- const cached = this.resourceCache.get(reference.reference);
1274
+ const cached = __classPrivateFieldGet(this, _MedplumClient_resourceCache, "f").get(reference.reference);
1189
1275
  if (cached && !('then' in cached)) {
1190
1276
  return cached;
1191
1277
  }
@@ -1194,14 +1280,14 @@ class MedplumClient extends EventTarget {
1194
1280
  read(resourceType, id) {
1195
1281
  const cacheKey = resourceType + '/' + id;
1196
1282
  const promise = this.get(this.fhirUrl(resourceType, id)).then((resource) => {
1197
- this.resourceCache.set(cacheKey, resource);
1283
+ __classPrivateFieldGet(this, _MedplumClient_resourceCache, "f").set(cacheKey, resource);
1198
1284
  return resource;
1199
1285
  });
1200
- this.resourceCache.set(cacheKey, promise);
1286
+ __classPrivateFieldGet(this, _MedplumClient_resourceCache, "f").set(cacheKey, promise);
1201
1287
  return promise;
1202
1288
  }
1203
1289
  readCached(resourceType, id) {
1204
- const cached = this.resourceCache.get(resourceType + '/' + id);
1290
+ const cached = __classPrivateFieldGet(this, _MedplumClient_resourceCache, "f").get(resourceType + '/' + id);
1205
1291
  return cached ? Promise.resolve(cached) : this.read(resourceType, id);
1206
1292
  }
1207
1293
  readReference(reference) {
@@ -1228,7 +1314,7 @@ class MedplumClient extends EventTarget {
1228
1314
  * @returns The schema if immediately available, undefined otherwise.
1229
1315
  */
1230
1316
  getSchema() {
1231
- return this.schema;
1317
+ return __classPrivateFieldGet(this, _MedplumClient_schema, "f");
1232
1318
  }
1233
1319
  /**
1234
1320
  * Requests the schema for a resource type.
@@ -1238,8 +1324,8 @@ class MedplumClient extends EventTarget {
1238
1324
  */
1239
1325
  requestSchema(resourceType) {
1240
1326
  return __awaiter(this, void 0, void 0, function* () {
1241
- if (resourceType in this.schema.types) {
1242
- return Promise.resolve(this.schema);
1327
+ if (resourceType in __classPrivateFieldGet(this, _MedplumClient_schema, "f").types) {
1328
+ return Promise.resolve(__classPrivateFieldGet(this, _MedplumClient_schema, "f"));
1243
1329
  }
1244
1330
  const query = `{
1245
1331
  StructureDefinitionList(name: "${encodeURIComponent(resourceType)}") {
@@ -1265,17 +1351,19 @@ class MedplumClient extends EventTarget {
1265
1351
  SearchParameterList(base: "${encodeURIComponent(resourceType)}") {
1266
1352
  base,
1267
1353
  code,
1268
- type
1354
+ type,
1355
+ expression,
1356
+ target
1269
1357
  }
1270
1358
  }`.replace(/\s+/g, ' ');
1271
1359
  const response = (yield this.graphql(query));
1272
1360
  for (const structureDefinition of response.data.StructureDefinitionList) {
1273
- indexStructureDefinition(this.schema, structureDefinition);
1361
+ indexStructureDefinition(__classPrivateFieldGet(this, _MedplumClient_schema, "f"), structureDefinition);
1274
1362
  }
1275
1363
  for (const searchParameter of response.data.SearchParameterList) {
1276
- indexSearchParameter(this.schema, searchParameter);
1364
+ indexSearchParameter(__classPrivateFieldGet(this, _MedplumClient_schema, "f"), searchParameter);
1277
1365
  }
1278
- return this.schema;
1366
+ return __classPrivateFieldGet(this, _MedplumClient_schema, "f");
1279
1367
  });
1280
1368
  }
1281
1369
  readHistory(resourceType, id) {
@@ -1303,7 +1391,7 @@ class MedplumClient extends EventTarget {
1303
1391
  return this.put(this.fhirUrl(resource.resourceType, resource.id), resource);
1304
1392
  }
1305
1393
  patch(resourceType, id, operations) {
1306
- return this.request('PATCH', this.fhirUrl(resourceType, id), PATCH_CONTENT_TYPE, operations);
1394
+ return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, 'PATCH', this.fhirUrl(resourceType, id), PATCH_CONTENT_TYPE, operations);
1307
1395
  }
1308
1396
  deleteResource(resourceType, id) {
1309
1397
  return this.delete(this.fhirUrl(resourceType, id));
@@ -1312,159 +1400,55 @@ class MedplumClient extends EventTarget {
1312
1400
  return this.post(this.fhirUrl('$graphql'), { query }, JSON_CONTENT_TYPE);
1313
1401
  }
1314
1402
  getActiveLogin() {
1315
- return this.storage.getObject('activeLogin');
1403
+ return __classPrivateFieldGet(this, _MedplumClient_storage, "f").getObject('activeLogin');
1316
1404
  }
1317
1405
  setActiveLogin(login) {
1318
1406
  return __awaiter(this, void 0, void 0, function* () {
1319
- this.storage.setObject('activeLogin', login);
1320
- this.addLogin(login);
1321
- this.resourceCache.clear();
1322
- this.refreshPromise = undefined;
1323
- yield this.refreshProfile();
1407
+ __classPrivateFieldSet(this, _MedplumClient_accessToken, login.accessToken, "f");
1408
+ __classPrivateFieldSet(this, _MedplumClient_refreshToken, login.refreshToken, "f");
1409
+ __classPrivateFieldSet(this, _MedplumClient_profile, undefined, "f");
1410
+ __classPrivateFieldSet(this, _MedplumClient_config, undefined, "f");
1411
+ __classPrivateFieldGet(this, _MedplumClient_storage, "f").setObject('activeLogin', login);
1412
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_addLogin).call(this, login);
1413
+ __classPrivateFieldGet(this, _MedplumClient_resourceCache, "f").clear();
1414
+ __classPrivateFieldSet(this, _MedplumClient_refreshPromise, undefined, "f");
1415
+ yield __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_refreshProfile).call(this);
1324
1416
  });
1325
1417
  }
1326
1418
  getLogins() {
1327
1419
  var _a;
1328
- return (_a = this.storage.getObject('logins')) !== null && _a !== void 0 ? _a : [];
1329
- }
1330
- addLogin(newLogin) {
1331
- const logins = this.getLogins().filter((login) => { var _a, _b; return ((_a = login.profile) === null || _a === void 0 ? void 0 : _a.reference) !== ((_b = newLogin.profile) === null || _b === void 0 ? void 0 : _b.reference); });
1332
- logins.push(newLogin);
1333
- this.storage.setObject('logins', logins);
1420
+ return (_a = __classPrivateFieldGet(this, _MedplumClient_storage, "f").getObject('logins')) !== null && _a !== void 0 ? _a : [];
1334
1421
  }
1335
- refreshProfile() {
1336
- var _a;
1337
- return __awaiter(this, void 0, void 0, function* () {
1338
- const reference = (_a = this.getActiveLogin()) === null || _a === void 0 ? void 0 : _a.profile;
1339
- if (reference === null || reference === void 0 ? void 0 : reference.reference) {
1340
- this.loading = true;
1341
- this.storage.setObject('profile', yield this.readCachedReference(reference));
1342
- this.loading = false;
1343
- this.dispatchEvent({ type: 'change' });
1344
- }
1345
- return this.getProfile();
1346
- });
1422
+ isLoading() {
1423
+ return !!__classPrivateFieldGet(this, _MedplumClient_profilePromise, "f");
1347
1424
  }
1348
1425
  getProfile() {
1349
- return this.storage.getObject('profile');
1426
+ return __classPrivateFieldGet(this, _MedplumClient_profile, "f");
1350
1427
  }
1351
- isLoading() {
1352
- return this.loading;
1353
- }
1354
- /**
1355
- * Makes an HTTP request.
1356
- * @param {string} method
1357
- * @param {string} url
1358
- * @param {string=} contentType
1359
- * @param {Object=} body
1360
- */
1361
- request(method, url, contentType, body) {
1362
- var _a;
1428
+ getProfileAsync() {
1363
1429
  return __awaiter(this, void 0, void 0, function* () {
1364
- if (this.refreshPromise) {
1365
- yield this.refreshPromise;
1366
- }
1367
- if (!url.startsWith('http')) {
1368
- url = this.baseUrl + url;
1369
- }
1370
- const headers = {
1371
- 'Content-Type': contentType || FHIR_CONTENT_TYPE,
1372
- };
1373
- const accessToken = (_a = this.getActiveLogin()) === null || _a === void 0 ? void 0 : _a.accessToken;
1374
- if (accessToken) {
1375
- headers['Authorization'] = 'Bearer ' + accessToken;
1376
- }
1377
- const options = {
1378
- method: method,
1379
- cache: 'no-cache',
1380
- credentials: 'include',
1381
- headers,
1382
- };
1383
- if (body) {
1384
- if (typeof body === 'string' || (typeof File !== 'undefined' && body instanceof File)) {
1385
- options.body = body;
1386
- }
1387
- else {
1388
- options.body = stringify(body);
1389
- }
1390
- }
1391
- const response = yield this.fetch(url, options);
1392
- if (response.status === 401) {
1393
- // Refresh and try again
1394
- return this.handleUnauthenticated(method, url, contentType, body);
1395
- }
1396
- if (response.status === 204 || response.status === 304) {
1397
- // No content or change
1398
- return undefined;
1430
+ if (__classPrivateFieldGet(this, _MedplumClient_profilePromise, "f")) {
1431
+ yield __classPrivateFieldGet(this, _MedplumClient_profilePromise, "f");
1399
1432
  }
1400
- const obj = yield response.json();
1401
- if (obj.resourceType === 'OperationOutcome' && !isOk(obj)) {
1402
- return Promise.reject(obj);
1403
- }
1404
- return obj;
1433
+ return this.getProfile();
1405
1434
  });
1406
1435
  }
1407
- /**
1408
- * Handles an unauthenticated response from the server.
1409
- * First, tries to refresh the access token and retry the request.
1410
- * Otherwise, calls unauthenticated callbacks and rejects.
1411
- * @param method The HTTP method of the original request.
1412
- * @param url The URL of the original request.
1413
- * @param contentType The content type of the original request.
1414
- * @param body The body of the original request.
1415
- */
1416
- handleUnauthenticated(method, url, contentType, body) {
1417
- return __awaiter(this, void 0, void 0, function* () {
1418
- return this.refresh()
1419
- .then(() => this.request(method, url, contentType, body))
1420
- .catch((error) => {
1421
- this.clear();
1422
- if (this.onUnauthenticated) {
1423
- this.onUnauthenticated();
1424
- }
1425
- return Promise.reject(error);
1426
- });
1427
- });
1436
+ getUserConfiguration() {
1437
+ return __classPrivateFieldGet(this, _MedplumClient_config, "f");
1428
1438
  }
1429
1439
  /**
1430
- * Starts a new PKCE flow.
1431
- * These PKCE values are stateful, and must survive redirects and page refreshes.
1440
+ * Downloads the URL as a blob.
1441
+ * @param url The URL to request.
1442
+ * @returns Promise to the response body as a blob.
1432
1443
  */
1433
- startPkce() {
1444
+ download(url) {
1434
1445
  return __awaiter(this, void 0, void 0, function* () {
1435
- const pkceState = getRandomString();
1436
- this.storage.setString('pkceState', pkceState);
1437
- const codeVerifier = getRandomString();
1438
- this.storage.setString('codeVerifier', codeVerifier);
1439
- const arrayHash = yield encryptSHA256(codeVerifier);
1440
- const codeChallenge = arrayBufferToBase64(arrayHash).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
1441
- this.storage.setString('codeChallenge', codeChallenge);
1442
- });
1443
- }
1444
- /**
1445
- * Redirects the user to the login screen for authorization.
1446
- * Clears all auth state including local storage and session storage.
1447
- * See: https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
1448
- */
1449
- requestAuthorization() {
1450
- return __awaiter(this, void 0, void 0, function* () {
1451
- if (!this.authorizeUrl) {
1452
- throw new Error('Missing authorize URL');
1446
+ if (__classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f")) {
1447
+ yield __classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f");
1453
1448
  }
1454
- this.startPkce();
1455
- window.location.assign(this.authorizeUrl +
1456
- '?response_type=code' +
1457
- '&state=' +
1458
- encodeURIComponent(this.storage.getString('pkceState')) +
1459
- '&client_id=' +
1460
- encodeURIComponent(this.clientId) +
1461
- '&redirect_uri=' +
1462
- encodeURIComponent(getBaseUrl()) +
1463
- '&scope=' +
1464
- encodeURIComponent(DEFAULT_SCOPE) +
1465
- '&code_challenge_method=S256' +
1466
- '&code_challenge=' +
1467
- encodeURIComponent(this.storage.getString('codeChallenge')));
1449
+ const options = __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_buildFetchOptions).call(this, 'GET');
1450
+ const response = yield __classPrivateFieldGet(this, _MedplumClient_fetch, "f").call(this, url, options);
1451
+ return response.blob();
1468
1452
  });
1469
1453
  }
1470
1454
  /**
@@ -1473,18 +1457,18 @@ class MedplumClient extends EventTarget {
1473
1457
  * @param code The authorization code received by URL parameter.
1474
1458
  */
1475
1459
  processCode(code) {
1476
- const pkceState = this.storage.getString('pkceState');
1460
+ const pkceState = __classPrivateFieldGet(this, _MedplumClient_storage, "f").getString('pkceState');
1477
1461
  if (!pkceState) {
1478
1462
  this.clear();
1479
1463
  throw new Error('Invalid PCKE state');
1480
1464
  }
1481
- const codeVerifier = this.storage.getString('codeVerifier');
1465
+ const codeVerifier = __classPrivateFieldGet(this, _MedplumClient_storage, "f").getString('codeVerifier');
1482
1466
  if (!codeVerifier) {
1483
1467
  this.clear();
1484
1468
  throw new Error('Invalid PCKE code verifier');
1485
1469
  }
1486
- return this.fetchTokens('grant_type=authorization_code' +
1487
- (this.clientId ? '&client_id=' + encodeURIComponent(this.clientId) : '') +
1470
+ return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_fetchTokens).call(this, 'grant_type=authorization_code' +
1471
+ (__classPrivateFieldGet(this, _MedplumClient_clientId, "f") ? '&client_id=' + encodeURIComponent(__classPrivateFieldGet(this, _MedplumClient_clientId, "f")) : '') +
1488
1472
  '&code_verifier=' +
1489
1473
  encodeURIComponent(codeVerifier) +
1490
1474
  '&redirect_uri=' +
@@ -1492,102 +1476,185 @@ class MedplumClient extends EventTarget {
1492
1476
  '&code=' +
1493
1477
  encodeURIComponent(code));
1494
1478
  }
1495
- /**
1496
- * Tries to refresh the auth tokens.
1497
- * See: https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokens
1498
- */
1499
- refresh() {
1500
- var _a;
1501
- return __awaiter(this, void 0, void 0, function* () {
1502
- if (this.refreshPromise) {
1503
- return this.refreshPromise;
1504
- }
1505
- const refreshToken = (_a = this.getActiveLogin()) === null || _a === void 0 ? void 0 : _a.refreshToken;
1506
- if (!refreshToken) {
1507
- this.clear();
1508
- return Promise.reject('Invalid refresh token');
1509
- }
1510
- this.refreshPromise = this.fetchTokens('grant_type=refresh_token' +
1511
- '&client_id=' +
1512
- encodeURIComponent(this.clientId) +
1513
- '&refresh_token=' +
1514
- encodeURIComponent(refreshToken));
1515
- yield this.refreshPromise;
1516
- });
1479
+ }
1480
+ _MedplumClient_fetch = new WeakMap(), _MedplumClient_storage = new WeakMap(), _MedplumClient_schema = new WeakMap(), _MedplumClient_resourceCache = new WeakMap(), _MedplumClient_baseUrl = new WeakMap(), _MedplumClient_clientId = new WeakMap(), _MedplumClient_authorizeUrl = new WeakMap(), _MedplumClient_tokenUrl = new WeakMap(), _MedplumClient_logoutUrl = new WeakMap(), _MedplumClient_onUnauthenticated = new WeakMap(), _MedplumClient_accessToken = new WeakMap(), _MedplumClient_refreshToken = new WeakMap(), _MedplumClient_refreshPromise = new WeakMap(), _MedplumClient_profilePromise = new WeakMap(), _MedplumClient_profile = new WeakMap(), _MedplumClient_config = new WeakMap(), _MedplumClient_instances = new WeakSet(), _MedplumClient_addLogin = function _MedplumClient_addLogin(newLogin) {
1481
+ const logins = this.getLogins().filter((login) => { var _a, _b; return ((_a = login.profile) === null || _a === void 0 ? void 0 : _a.reference) !== ((_b = newLogin.profile) === null || _b === void 0 ? void 0 : _b.reference); });
1482
+ logins.push(newLogin);
1483
+ __classPrivateFieldGet(this, _MedplumClient_storage, "f").setObject('logins', logins);
1484
+ }, _MedplumClient_refreshProfile = function _MedplumClient_refreshProfile() {
1485
+ return __awaiter(this, void 0, void 0, function* () {
1486
+ __classPrivateFieldSet(this, _MedplumClient_profilePromise, new Promise((resolve, reject) => {
1487
+ this.get('auth/me')
1488
+ .then((result) => {
1489
+ __classPrivateFieldSet(this, _MedplumClient_profilePromise, undefined, "f");
1490
+ __classPrivateFieldSet(this, _MedplumClient_profile, result.profile, "f");
1491
+ __classPrivateFieldSet(this, _MedplumClient_config, result.config, "f");
1492
+ this.dispatchEvent({ type: 'change' });
1493
+ resolve(__classPrivateFieldGet(this, _MedplumClient_profile, "f"));
1494
+ })
1495
+ .catch(reject);
1496
+ }), "f");
1497
+ return __classPrivateFieldGet(this, _MedplumClient_profilePromise, "f");
1498
+ });
1499
+ }, _MedplumClient_request = function _MedplumClient_request(method, url, contentType, body) {
1500
+ return __awaiter(this, void 0, void 0, function* () {
1501
+ if (__classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f")) {
1502
+ yield __classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f");
1503
+ }
1504
+ if (!url.startsWith('http')) {
1505
+ url = __classPrivateFieldGet(this, _MedplumClient_baseUrl, "f") + url;
1506
+ }
1507
+ const options = __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_buildFetchOptions).call(this, method, contentType, body);
1508
+ const response = yield __classPrivateFieldGet(this, _MedplumClient_fetch, "f").call(this, url, options);
1509
+ if (response.status === 401) {
1510
+ // Refresh and try again
1511
+ return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_handleUnauthenticated).call(this, method, url, contentType, body);
1512
+ }
1513
+ if (response.status === 204 || response.status === 304) {
1514
+ // No content or change
1515
+ return undefined;
1516
+ }
1517
+ const obj = yield response.json();
1518
+ if (obj.resourceType === 'OperationOutcome' && !isOk(obj)) {
1519
+ return Promise.reject(obj);
1520
+ }
1521
+ return obj;
1522
+ });
1523
+ }, _MedplumClient_buildFetchOptions = function _MedplumClient_buildFetchOptions(method, contentType, body) {
1524
+ const headers = {
1525
+ 'Content-Type': contentType || FHIR_CONTENT_TYPE,
1526
+ };
1527
+ if (__classPrivateFieldGet(this, _MedplumClient_accessToken, "f")) {
1528
+ headers['Authorization'] = 'Bearer ' + __classPrivateFieldGet(this, _MedplumClient_accessToken, "f");
1529
+ }
1530
+ const options = {
1531
+ method: method,
1532
+ cache: 'no-cache',
1533
+ credentials: 'include',
1534
+ headers,
1535
+ };
1536
+ if (body) {
1537
+ if (typeof body === 'string' || (typeof File !== 'undefined' && body instanceof File)) {
1538
+ options.body = body;
1539
+ }
1540
+ else {
1541
+ options.body = stringify(body);
1542
+ }
1517
1543
  }
1518
- /**
1519
- * Makes a POST request to the tokens endpoint.
1520
- * See: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
1521
- * @param formBody Token parameters in URL encoded format.
1522
- */
1523
- fetchTokens(formBody) {
1524
- return __awaiter(this, void 0, void 0, function* () {
1525
- if (!this.tokenUrl) {
1526
- return Promise.reject('Missing token URL');
1544
+ return options;
1545
+ }, _MedplumClient_handleUnauthenticated = function _MedplumClient_handleUnauthenticated(method, url, contentType, body) {
1546
+ return __awaiter(this, void 0, void 0, function* () {
1547
+ return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_refresh).call(this)
1548
+ .then(() => __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, method, url, contentType, body))
1549
+ .catch((error) => {
1550
+ this.clear();
1551
+ if (__classPrivateFieldGet(this, _MedplumClient_onUnauthenticated, "f")) {
1552
+ __classPrivateFieldGet(this, _MedplumClient_onUnauthenticated, "f").call(this);
1527
1553
  }
1528
- return this.fetch(this.tokenUrl, {
1529
- method: 'POST',
1530
- headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
1531
- body: formBody,
1532
- })
1533
- .then((response) => {
1534
- if (!response.ok) {
1535
- return Promise.reject('Failed to fetch tokens');
1536
- }
1537
- return response.json();
1538
- })
1539
- .then((tokens) => this.verifyTokens(tokens))
1540
- .then(() => this.getProfile());
1554
+ return Promise.reject(error);
1541
1555
  });
1542
- }
1543
- /**
1544
- * Verifies the tokens received from the auth server.
1545
- * Validates the JWT against the JWKS.
1546
- * See: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
1547
- * @param tokens
1548
- */
1549
- verifyTokens(tokens) {
1550
- return __awaiter(this, void 0, void 0, function* () {
1551
- const token = tokens.access_token;
1552
- // Verify token has not expired
1553
- const tokenPayload = parseJWTPayload(token);
1554
- if (Date.now() >= tokenPayload.exp * 1000) {
1555
- this.clear();
1556
- return Promise.reject('Token expired');
1556
+ });
1557
+ }, _MedplumClient_startPkce = function _MedplumClient_startPkce() {
1558
+ return __awaiter(this, void 0, void 0, function* () {
1559
+ const pkceState = getRandomString();
1560
+ __classPrivateFieldGet(this, _MedplumClient_storage, "f").setString('pkceState', pkceState);
1561
+ const codeVerifier = getRandomString();
1562
+ __classPrivateFieldGet(this, _MedplumClient_storage, "f").setString('codeVerifier', codeVerifier);
1563
+ const arrayHash = yield encryptSHA256(codeVerifier);
1564
+ const codeChallenge = arrayBufferToBase64(arrayHash).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
1565
+ __classPrivateFieldGet(this, _MedplumClient_storage, "f").setString('codeChallenge', codeChallenge);
1566
+ });
1567
+ }, _MedplumClient_requestAuthorization = function _MedplumClient_requestAuthorization() {
1568
+ return __awaiter(this, void 0, void 0, function* () {
1569
+ if (!__classPrivateFieldGet(this, _MedplumClient_authorizeUrl, "f")) {
1570
+ throw new Error('Missing authorize URL');
1571
+ }
1572
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_startPkce).call(this);
1573
+ window.location.assign(__classPrivateFieldGet(this, _MedplumClient_authorizeUrl, "f") +
1574
+ '?response_type=code' +
1575
+ '&state=' +
1576
+ encodeURIComponent(__classPrivateFieldGet(this, _MedplumClient_storage, "f").getString('pkceState')) +
1577
+ '&client_id=' +
1578
+ encodeURIComponent(__classPrivateFieldGet(this, _MedplumClient_clientId, "f")) +
1579
+ '&redirect_uri=' +
1580
+ encodeURIComponent(getBaseUrl()) +
1581
+ '&scope=' +
1582
+ encodeURIComponent(DEFAULT_SCOPE) +
1583
+ '&code_challenge_method=S256' +
1584
+ '&code_challenge=' +
1585
+ encodeURIComponent(__classPrivateFieldGet(this, _MedplumClient_storage, "f").getString('codeChallenge')));
1586
+ });
1587
+ }, _MedplumClient_refresh = function _MedplumClient_refresh() {
1588
+ return __awaiter(this, void 0, void 0, function* () {
1589
+ if (__classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f")) {
1590
+ return __classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f");
1591
+ }
1592
+ if (!__classPrivateFieldGet(this, _MedplumClient_refreshToken, "f")) {
1593
+ this.clear();
1594
+ return Promise.reject('Invalid refresh token');
1595
+ }
1596
+ __classPrivateFieldSet(this, _MedplumClient_refreshPromise, __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_fetchTokens).call(this, 'grant_type=refresh_token' +
1597
+ '&client_id=' +
1598
+ encodeURIComponent(__classPrivateFieldGet(this, _MedplumClient_clientId, "f")) +
1599
+ '&refresh_token=' +
1600
+ encodeURIComponent(__classPrivateFieldGet(this, _MedplumClient_refreshToken, "f"))), "f");
1601
+ yield __classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f");
1602
+ });
1603
+ }, _MedplumClient_fetchTokens = function _MedplumClient_fetchTokens(formBody) {
1604
+ return __awaiter(this, void 0, void 0, function* () {
1605
+ if (!__classPrivateFieldGet(this, _MedplumClient_tokenUrl, "f")) {
1606
+ return Promise.reject('Missing token URL');
1607
+ }
1608
+ return __classPrivateFieldGet(this, _MedplumClient_fetch, "f").call(this, __classPrivateFieldGet(this, _MedplumClient_tokenUrl, "f"), {
1609
+ method: 'POST',
1610
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
1611
+ body: formBody,
1612
+ })
1613
+ .then((response) => {
1614
+ if (!response.ok) {
1615
+ return Promise.reject('Failed to fetch tokens');
1557
1616
  }
1558
- // Verify app_client_id
1559
- if (this.clientId && tokenPayload.client_id !== this.clientId) {
1560
- this.clear();
1561
- return Promise.reject('Token was not issued for this audience');
1617
+ return response.json();
1618
+ })
1619
+ .then((tokens) => __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_verifyTokens).call(this, tokens))
1620
+ .then(() => this.getProfile());
1621
+ });
1622
+ }, _MedplumClient_verifyTokens = function _MedplumClient_verifyTokens(tokens) {
1623
+ return __awaiter(this, void 0, void 0, function* () {
1624
+ const token = tokens.access_token;
1625
+ // Verify token has not expired
1626
+ const tokenPayload = parseJWTPayload(token);
1627
+ if (Date.now() >= tokenPayload.exp * 1000) {
1628
+ this.clear();
1629
+ return Promise.reject('Token expired');
1630
+ }
1631
+ // Verify app_client_id
1632
+ if (__classPrivateFieldGet(this, _MedplumClient_clientId, "f") && tokenPayload.client_id !== __classPrivateFieldGet(this, _MedplumClient_clientId, "f")) {
1633
+ this.clear();
1634
+ return Promise.reject('Token was not issued for this audience');
1635
+ }
1636
+ yield this.setActiveLogin({
1637
+ accessToken: token,
1638
+ refreshToken: tokens.refresh_token,
1639
+ project: tokens.project,
1640
+ profile: tokens.profile,
1641
+ });
1642
+ });
1643
+ }, _MedplumClient_setupStorageListener = function _MedplumClient_setupStorageListener() {
1644
+ try {
1645
+ window.addEventListener('storage', (e) => {
1646
+ if (e.key === null || e.key === 'activeLogin') {
1647
+ // Storage events fire when different tabs make changes.
1648
+ // On storage clear (key === null) or activeLogin change (key === 'activeLogin')
1649
+ // Refresh the page to ensure the active login is up to date.
1650
+ window.location.reload();
1562
1651
  }
1563
- yield this.setActiveLogin({
1564
- accessToken: token,
1565
- refreshToken: tokens.refresh_token,
1566
- project: tokens.project,
1567
- profile: tokens.profile,
1568
- });
1569
1652
  });
1570
1653
  }
1571
- /**
1572
- * Sets up a listener for window storage events.
1573
- * This synchronizes state across browser windows and browser tabs.
1574
- */
1575
- setupStorageListener() {
1576
- try {
1577
- window.addEventListener('storage', (e) => {
1578
- if (e.key === null || e.key === 'activeLogin') {
1579
- // Storage events fire when different tabs make changes.
1580
- // On storage clear (key === null) or activeLogin change (key === 'activeLogin')
1581
- // Refresh the page to ensure the active login is up to date.
1582
- window.location.reload();
1583
- }
1584
- });
1585
- }
1586
- catch (err) {
1587
- // Silently ignore if this environment does not support storage events
1588
- }
1654
+ catch (err) {
1655
+ // Silently ignore if this environment does not support storage events
1589
1656
  }
1590
- }
1657
+ };
1591
1658
  /**
1592
1659
  * Returns the base URL for the current page.
1593
1660
  */
@@ -1623,6 +1690,9 @@ var SearchParameterType;
1623
1690
  */
1624
1691
  function getSearchParameterDetails(structureDefinitions, resourceType, searchParam) {
1625
1692
  var _a, _b, _c, _d;
1693
+ if (searchParam.code === '_lastUpdated') {
1694
+ return { columnName: 'lastUpdated', type: SearchParameterType.DATETIME };
1695
+ }
1626
1696
  const columnName = convertCodeToColumnName(searchParam.code);
1627
1697
  const expression = (_a = getExpressionForResourceType(resourceType, searchParam.expression)) === null || _a === void 0 ? void 0 : _a.split('.');
1628
1698
  if (!expression) {
@@ -1631,20 +1701,21 @@ function getSearchParameterDetails(structureDefinitions, resourceType, searchPar
1631
1701
  return { columnName, type: SearchParameterType.TEXT };
1632
1702
  }
1633
1703
  let baseType = resourceType;
1704
+ let elementDefinition = undefined;
1634
1705
  let propertyType = undefined;
1635
1706
  let array = false;
1636
1707
  for (let i = 1; i < expression.length; i++) {
1637
1708
  const propertyName = expression[i];
1638
- const propertyDef = (_c = (_b = structureDefinitions.types[baseType]) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c[propertyName];
1639
- if (!propertyDef) {
1709
+ elementDefinition = (_c = (_b = structureDefinitions.types[baseType]) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c[propertyName];
1710
+ if (!elementDefinition) {
1640
1711
  // This happens on complex properties such as "collected[x]"/"collectedDateTime"/"collectedPeriod"
1641
1712
  // In the future, explore returning multiple column definitions
1642
1713
  return { columnName, type: SearchParameterType.TEXT, array };
1643
1714
  }
1644
- if (propertyDef.max === '*') {
1715
+ if (elementDefinition.max === '*') {
1645
1716
  array = true;
1646
1717
  }
1647
- propertyType = (_d = propertyDef.type) === null || _d === void 0 ? void 0 : _d[0].code;
1718
+ propertyType = (_d = elementDefinition.type) === null || _d === void 0 ? void 0 : _d[0].code;
1648
1719
  if (!propertyType) {
1649
1720
  // This happens when one of parent properties uses contentReference
1650
1721
  // In the future, explore following the reference
@@ -1660,7 +1731,7 @@ function getSearchParameterDetails(structureDefinitions, resourceType, searchPar
1660
1731
  }
1661
1732
  }
1662
1733
  const type = getSearchParameterType(searchParam, propertyType);
1663
- return { columnName, type, array };
1734
+ return { columnName, type, elementDefinition, array };
1664
1735
  }
1665
1736
  /**
1666
1737
  * Converts a hyphen-delimited code to camelCase string.
@@ -1717,5 +1788,5 @@ function simplifyExpression(input) {
1717
1788
  return result;
1718
1789
  }
1719
1790
 
1720
- export { MedplumClient, OperationOutcomeError, Operator, PropertyType, SearchParameterType, accessDenied, allOk, arrayBufferToBase64, arrayBufferToHex, assertOk, badRequest, buildTypeName, capitalize, createReference, createSchema, created, deepEquals, formatAddress, formatFamilyName, formatGivenName, formatHumanName, formatSearchQuery, getDateProperty, getDisplayString, getExpressionForResourceType, getImageSrc, getPropertyDisplayName, getReferenceString, getSearchParameterDetails, getStatus, gone, indexSearchParameter, indexStructureDefinition, isGone, isLowerCase, isNotFound, isOk, isProfileResource, notFound, notModified, parseSearchDefinition, stringify };
1791
+ export { MedplumClient, OperationOutcomeError, Operator, PropertyType, SearchParameterType, accessDenied, allOk, arrayBufferToBase64, arrayBufferToHex, assertOk, badRequest, buildTypeName, capitalize, createReference, createSchema, createTypeSchema, created, deepEquals, formatAddress, formatFamilyName, formatGivenName, formatHumanName, formatSearchQuery, getDateProperty, getDisplayString, getExpressionForResourceType, getImageSrc, getPropertyDisplayName, getReferenceString, getSearchParameterDetails, getStatus, gone, indexSearchParameter, indexStructureDefinition, isGone, isLowerCase, isNotFound, isOk, isProfileResource, notFound, notModified, parseSearchDefinition, resolveId, stringify };
1721
1792
  //# sourceMappingURL=index.js.map