@medplum/core 2.0.21 → 2.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/cjs/index.cjs +380 -288
  2. package/dist/cjs/index.cjs.map +1 -1
  3. package/dist/cjs/index.min.cjs +1 -1
  4. package/dist/esm/client.mjs +132 -102
  5. package/dist/esm/client.mjs.map +1 -1
  6. package/dist/esm/crypto.mjs +3 -1
  7. package/dist/esm/crypto.mjs.map +1 -1
  8. package/dist/esm/fhirpath/functions.mjs +179 -129
  9. package/dist/esm/fhirpath/functions.mjs.map +1 -1
  10. package/dist/esm/format.mjs +6 -4
  11. package/dist/esm/format.mjs.map +1 -1
  12. package/dist/esm/hl7.mjs +1 -1
  13. package/dist/esm/hl7.mjs.map +1 -1
  14. package/dist/esm/index.min.mjs +1 -1
  15. package/dist/esm/index.mjs +1 -0
  16. package/dist/esm/index.mjs.map +1 -1
  17. package/dist/esm/jwt.mjs +4 -2
  18. package/dist/esm/jwt.mjs.map +1 -1
  19. package/dist/esm/schema.mjs +4 -10
  20. package/dist/esm/schema.mjs.map +1 -1
  21. package/dist/esm/search/details.mjs +0 -1
  22. package/dist/esm/search/details.mjs.map +1 -1
  23. package/dist/esm/search/match.mjs +1 -0
  24. package/dist/esm/search/match.mjs.map +1 -1
  25. package/dist/esm/search/search.mjs +1 -1
  26. package/dist/esm/search/search.mjs.map +1 -1
  27. package/dist/esm/storage.mjs +8 -0
  28. package/dist/esm/storage.mjs.map +1 -1
  29. package/dist/esm/types.mjs +1 -0
  30. package/dist/esm/types.mjs.map +1 -1
  31. package/dist/esm/utils.mjs +8 -7
  32. package/dist/esm/utils.mjs.map +1 -1
  33. package/dist/types/client.d.ts +74 -64
  34. package/dist/types/crypto.d.ts +3 -1
  35. package/dist/types/hl7.d.ts +1 -1
  36. package/dist/types/index.d.ts +1 -0
  37. package/dist/types/jwt.d.ts +2 -1
  38. package/dist/types/schema.d.ts +4 -10
  39. package/dist/types/search/details.d.ts +0 -1
  40. package/dist/types/search/search.d.ts +1 -1
  41. package/dist/types/storage.d.ts +8 -0
  42. package/dist/types/typeschema/types.d.ts +0 -1
  43. package/dist/types/utils.d.ts +4 -4
  44. package/package.json +1 -1
@@ -1,3 +1,4 @@
1
+ import { encodeBase64 } from './base64.mjs';
1
2
  import { LRUCache } from './cache.mjs';
2
3
  import { getRandomString, encryptSHA256 } from './crypto.mjs';
3
4
  import { EventTarget } from './eventtarget.mjs';
@@ -7,11 +8,10 @@ import { ReadablePromise } from './readablepromise.mjs';
7
8
  import { ClientStorage } from './storage.mjs';
8
9
  import { globalSchema, indexStructureDefinition, indexSearchParameter } from './types.mjs';
9
10
  import { createReference, arrayBufferToBase64 } from './utils.mjs';
10
- import { encodeBase64 } from './base64.mjs';
11
11
 
12
12
  // PKCE auth based on:
13
13
  // https://aws.amazon.com/blogs/security/how-to-add-authentication-single-page-web-application-with-amazon-cognito-oauth2-implementation/
14
- const MEDPLUM_VERSION = "2.0.21-87271fa1" ;
14
+ const MEDPLUM_VERSION = "2.0.22-f51ac45a" ;
15
15
  const DEFAULT_BASE_URL = 'https://api.medplum.com/';
16
16
  const DEFAULT_RESOURCE_CACHE_SIZE = 1000;
17
17
  const DEFAULT_CACHE_TIME = 60000; // 60 seconds
@@ -98,7 +98,6 @@ var OAuthTokenType;
98
98
  * <head>
99
99
  * <meta name="algolia:pageRank" content="100" />
100
100
  * </head>
101
-
102
101
  */
103
102
  class MedplumClient extends EventTarget {
104
103
  constructor(options) {
@@ -165,6 +164,9 @@ class MedplumClient extends EventTarget {
165
164
  * @category Authentication
166
165
  */
167
166
  clearActiveLogin() {
167
+ if (this.basicAuth) {
168
+ return;
169
+ }
168
170
  this.storage.setString('activeLogin', undefined);
169
171
  this.requestCache?.clear();
170
172
  this.accessToken = undefined;
@@ -210,7 +212,6 @@ class MedplumClient extends EventTarget {
210
212
  * This is a lower level method for custom requests.
211
213
  * For common operations, we recommend using higher level methods
212
214
  * such as `readResource()`, `search()`, etc.
213
- *
214
215
  * @category HTTP
215
216
  * @param url The target URL.
216
217
  * @param options Optional fetch options.
@@ -250,7 +251,6 @@ class MedplumClient extends EventTarget {
250
251
  * This is a lower level method for custom requests.
251
252
  * For common operations, we recommend using higher level methods
252
253
  * such as `createResource()`.
253
- *
254
254
  * @category HTTP
255
255
  * @param url The target URL.
256
256
  * @param body The content body. Strings and `File` objects are passed directly. Other objects are converted to JSON.
@@ -275,7 +275,6 @@ class MedplumClient extends EventTarget {
275
275
  * This is a lower level method for custom requests.
276
276
  * For common operations, we recommend using higher level methods
277
277
  * such as `updateResource()`.
278
- *
279
278
  * @category HTTP
280
279
  * @param url The target URL.
281
280
  * @param body The content body. Strings and `File` objects are passed directly. Other objects are converted to JSON.
@@ -300,7 +299,6 @@ class MedplumClient extends EventTarget {
300
299
  * This is a lower level method for custom requests.
301
300
  * For common operations, we recommend using higher level methods
302
301
  * such as `patchResource()`.
303
- *
304
302
  * @category HTTP
305
303
  * @param url The target URL.
306
304
  * @param operations Array of JSONPatch operations.
@@ -321,13 +319,12 @@ class MedplumClient extends EventTarget {
321
319
  * This is a lower level method for custom requests.
322
320
  * For common operations, we recommend using higher level methods
323
321
  * such as `deleteResource()`.
324
- *
325
322
  * @category HTTP
326
323
  * @param url The target URL.
327
324
  * @param options Optional fetch options.
328
325
  * @returns Promise to the response content.
329
326
  */
330
- delete(url, options = {}) {
327
+ delete(url, options) {
331
328
  url = url.toString();
332
329
  this.invalidateUrl(url);
333
330
  return this.request('DELETE', url, options);
@@ -338,53 +335,54 @@ class MedplumClient extends EventTarget {
338
335
  * This method is part of the two different user registration flows:
339
336
  * 1) New Practitioner and new Project
340
337
  * 2) New Patient registration
341
- *
342
338
  * @category Authentication
343
339
  * @param newUserRequest Register request including email and password.
340
+ * @param options Optional fetch options.
344
341
  * @returns Promise to the authentication response.
345
342
  */
346
- async startNewUser(newUserRequest) {
343
+ async startNewUser(newUserRequest, options) {
347
344
  const { codeChallengeMethod, codeChallenge } = await this.startPkce();
348
345
  return this.post('auth/newuser', {
349
346
  ...newUserRequest,
350
347
  codeChallengeMethod,
351
348
  codeChallenge,
352
- });
349
+ }, undefined, options);
353
350
  }
354
351
  /**
355
352
  * Initiates a new project flow.
356
353
  *
357
354
  * This requires a partial login from `startNewUser` or `startNewGoogleUser`.
358
- *
359
355
  * @param newProjectRequest Register request including email and password.
356
+ * @param options Optional fetch options.
360
357
  * @returns Promise to the authentication response.
361
358
  */
362
- async startNewProject(newProjectRequest) {
363
- return this.post('auth/newproject', newProjectRequest);
359
+ async startNewProject(newProjectRequest, options) {
360
+ return this.post('auth/newproject', newProjectRequest, undefined, options);
364
361
  }
365
362
  /**
366
363
  * Initiates a new patient flow.
367
364
  *
368
365
  * This requires a partial login from `startNewUser` or `startNewGoogleUser`.
369
- *
370
366
  * @param newPatientRequest Register request including email and password.
367
+ * @param options Optional fetch options.
371
368
  * @returns Promise to the authentication response.
372
369
  */
373
- async startNewPatient(newPatientRequest) {
374
- return this.post('auth/newpatient', newPatientRequest);
370
+ async startNewPatient(newPatientRequest, options) {
371
+ return this.post('auth/newpatient', newPatientRequest, undefined, options);
375
372
  }
376
373
  /**
377
374
  * Initiates a user login flow.
378
375
  * @category Authentication
379
376
  * @param loginRequest Login request including email and password.
377
+ * @param options Optional fetch options.
380
378
  * @returns Promise to the authentication response.
381
379
  */
382
- async startLogin(loginRequest) {
380
+ async startLogin(loginRequest, options) {
383
381
  return this.post('auth/login', {
384
382
  ...(await this.ensureCodeChallenge(loginRequest)),
385
383
  clientId: loginRequest.clientId ?? this.clientId,
386
384
  scope: loginRequest.scope,
387
- });
385
+ }, undefined, options);
388
386
  }
389
387
  /**
390
388
  * Tries to sign in with Google authentication.
@@ -392,14 +390,15 @@ class MedplumClient extends EventTarget {
392
390
  * See: https://developers.google.com/identity/gsi/web/guides/handle-credential-responses-js-functions
393
391
  * @category Authentication
394
392
  * @param loginRequest Login request including Google credential response.
393
+ * @param options Optional fetch options.
395
394
  * @returns Promise to the authentication response.
396
395
  */
397
- async startGoogleLogin(loginRequest) {
396
+ async startGoogleLogin(loginRequest, options) {
398
397
  return this.post('auth/google', {
399
398
  ...(await this.ensureCodeChallenge(loginRequest)),
400
399
  clientId: loginRequest.clientId ?? this.clientId,
401
400
  scope: loginRequest.scope,
402
- });
401
+ }, undefined, options);
403
402
  }
404
403
  /**
405
404
  * Returns the PKCE code challenge and method.
@@ -430,6 +429,7 @@ class MedplumClient extends EventTarget {
430
429
  * This may result in navigating away to the sign in page.
431
430
  * @category Authentication
432
431
  * @param loginParams Optional login parameters.
432
+ * @returns The user profile resource if available.
433
433
  */
434
434
  async signInWithRedirect(loginParams) {
435
435
  const urlParams = new URLSearchParams(window.location.search);
@@ -466,6 +466,7 @@ class MedplumClient extends EventTarget {
466
466
  * Exchange an external access token for a Medplum access token.
467
467
  * @param token The access token that was generated by the external identity provider.
468
468
  * @param clientId The ID of the `ClientApplication` in your Medplum project that will be making the exchange request.
469
+ * @returns The user profile resource.
469
470
  * @category Authentication
470
471
  */
471
472
  async exchangeExternalAccessToken(token, clientId) {
@@ -564,14 +565,13 @@ class MedplumClient extends EventTarget {
564
565
  * ```
565
566
  *
566
567
  * See FHIR search for full details: https://www.hl7.org/fhir/search.html
567
- *
568
568
  * @category Search
569
569
  * @param resourceType The FHIR resource type.
570
570
  * @param query Optional FHIR search query or structured query object. Can be any valid input to the URLSearchParams() constructor.
571
571
  * @param options Optional fetch options.
572
572
  * @returns Promise to the search result bundle.
573
573
  */
574
- search(resourceType, query, options = {}) {
574
+ search(resourceType, query, options) {
575
575
  const url = this.fhirSearchUrl(resourceType, query);
576
576
  const cacheKey = url.toString() + '-search';
577
577
  const cached = this.getCacheEntry(cacheKey, options);
@@ -605,14 +605,13 @@ class MedplumClient extends EventTarget {
605
605
  * The return value is the resource, if available; otherwise, undefined.
606
606
  *
607
607
  * See FHIR search for full details: https://www.hl7.org/fhir/search.html
608
- *
609
608
  * @category Search
610
609
  * @param resourceType The FHIR resource type.
611
610
  * @param query Optional FHIR search query or structured query object. Can be any valid input to the URLSearchParams() constructor.
612
611
  * @param options Optional fetch options.
613
612
  * @returns Promise to the first search result.
614
613
  */
615
- searchOne(resourceType, query, options = {}) {
614
+ searchOne(resourceType, query, options) {
616
615
  const url = this.fhirSearchUrl(resourceType, query);
617
616
  url.searchParams.set('_count', '1');
618
617
  url.searchParams.sort();
@@ -640,14 +639,13 @@ class MedplumClient extends EventTarget {
640
639
  * The return value is an array of resources.
641
640
  *
642
641
  * See FHIR search for full details: https://www.hl7.org/fhir/search.html
643
- *
644
642
  * @category Search
645
643
  * @param resourceType The FHIR resource type.
646
644
  * @param query Optional FHIR search query or structured query object. Can be any valid input to the URLSearchParams() constructor.
647
645
  * @param options Optional fetch options.
648
646
  * @returns Promise to the array of search results.
649
647
  */
650
- searchResources(resourceType, query, options = {}) {
648
+ searchResources(resourceType, query, options) {
651
649
  const url = this.fhirSearchUrl(resourceType, query);
652
650
  const cacheKey = url.toString() + '-searchResources';
653
651
  const cached = this.getCacheEntry(cacheKey, options);
@@ -672,14 +670,13 @@ class MedplumClient extends EventTarget {
672
670
  * }
673
671
  * }
674
672
  * ```
675
- *
676
673
  * @category Search
677
674
  * @param resourceType The FHIR resource type.
678
675
  * @param query Optional FHIR search query or structured query object. Can be any valid input to the URLSearchParams() constructor.
679
676
  * @param options Optional fetch options.
680
- * @returns An async generator, where each result is an array of resources for each page.
677
+ * @yields An async generator, where each result is an array of resources for each page.
681
678
  */
682
- async *searchResourcePages(resourceType, query, options = {}) {
679
+ async *searchResourcePages(resourceType, query, options) {
683
680
  let url = this.fhirSearchUrl(resourceType, query);
684
681
  while (url) {
685
682
  const searchParams = new URL(url).searchParams;
@@ -695,14 +692,13 @@ class MedplumClient extends EventTarget {
695
692
  /**
696
693
  * Searches a ValueSet resource using the "expand" operation.
697
694
  * See: https://www.hl7.org/fhir/operation-valueset-expand.html
698
- *
699
695
  * @category Search
700
696
  * @param system The ValueSet system url.
701
697
  * @param filter The search string.
702
698
  * @param options Optional fetch options.
703
699
  * @returns Promise to expanded ValueSet.
704
700
  */
705
- searchValueSet(system, filter, options = {}) {
701
+ searchValueSet(system, filter, options) {
706
702
  const url = this.fhirUrl('ValueSet', '$expand');
707
703
  url.searchParams.set('url', system);
708
704
  url.searchParams.set('filter', filter);
@@ -722,8 +718,7 @@ class MedplumClient extends EventTarget {
722
718
  /**
723
719
  * Returns a cached resource if it is available.
724
720
  * @category Caching
725
- * @param resourceType The FHIR resource type.
726
- * @param id The FHIR resource ID.
721
+ * @param reference The FHIR reference.
727
722
  * @returns The resource if it is available in the cache; undefined otherwise.
728
723
  */
729
724
  getCachedReference(reference) {
@@ -751,14 +746,13 @@ class MedplumClient extends EventTarget {
751
746
  * ```
752
747
  *
753
748
  * See the FHIR "read" operation for full details: https://www.hl7.org/fhir/http.html#read
754
- *
755
749
  * @category Read
756
750
  * @param resourceType The FHIR resource type.
757
751
  * @param id The resource ID.
758
752
  * @param options Optional fetch options.
759
753
  * @returns The resource if available; undefined otherwise.
760
754
  */
761
- readResource(resourceType, id, options = {}) {
755
+ readResource(resourceType, id, options) {
762
756
  return this.get(this.fhirUrl(resourceType, id), options);
763
757
  }
764
758
  /**
@@ -775,13 +769,12 @@ class MedplumClient extends EventTarget {
775
769
  * ```
776
770
  *
777
771
  * See the FHIR "read" operation for full details: https://www.hl7.org/fhir/http.html#read
778
- *
779
772
  * @category Read
780
773
  * @param reference The FHIR reference object.
781
774
  * @param options Optional fetch options.
782
775
  * @returns The resource if available; undefined otherwise.
783
776
  */
784
- readReference(reference, options = {}) {
777
+ readReference(reference, options) {
785
778
  const refString = reference?.reference;
786
779
  if (!refString) {
787
780
  return new ReadablePromise(Promise.reject(new Error('Missing reference')));
@@ -877,14 +870,13 @@ class MedplumClient extends EventTarget {
877
870
  * ```
878
871
  *
879
872
  * See the FHIR "history" operation for full details: https://www.hl7.org/fhir/http.html#history
880
- *
881
873
  * @category Read
882
874
  * @param resourceType The FHIR resource type.
883
875
  * @param id The resource ID.
884
876
  * @param options Optional fetch options.
885
877
  * @returns Promise to the resource history.
886
878
  */
887
- readHistory(resourceType, id, options = {}) {
879
+ readHistory(resourceType, id, options) {
888
880
  return this.get(this.fhirUrl(resourceType, id, '_history'), options);
889
881
  }
890
882
  /**
@@ -898,7 +890,6 @@ class MedplumClient extends EventTarget {
898
890
  * ```
899
891
  *
900
892
  * See the FHIR "vread" operation for full details: https://www.hl7.org/fhir/http.html#vread
901
- *
902
893
  * @category Read
903
894
  * @param resourceType The FHIR resource type.
904
895
  * @param id The resource ID.
@@ -906,7 +897,7 @@ class MedplumClient extends EventTarget {
906
897
  * @param options Optional fetch options.
907
898
  * @returns The resource if available; undefined otherwise.
908
899
  */
909
- readVersion(resourceType, id, vid, options = {}) {
900
+ readVersion(resourceType, id, vid, options) {
910
901
  return this.get(this.fhirUrl(resourceType, id, '_history', vid), options);
911
902
  }
912
903
  /**
@@ -920,13 +911,12 @@ class MedplumClient extends EventTarget {
920
911
  * ```
921
912
  *
922
913
  * See the FHIR "patient-everything" operation for full details: https://hl7.org/fhir/operation-patient-everything.html
923
- *
924
914
  * @category Read
925
915
  * @param id The Patient Id
926
916
  * @param options Optional fetch options.
927
917
  * @returns A Bundle of all Resources related to the Patient
928
918
  */
929
- readPatientEverything(id, options = {}) {
919
+ readPatientEverything(id, options) {
930
920
  return this.get(this.fhirUrl('Patient', id, '$everything'), options);
931
921
  }
932
922
  /**
@@ -948,17 +938,17 @@ class MedplumClient extends EventTarget {
948
938
  * ```
949
939
  *
950
940
  * See the FHIR "create" operation for full details: https://www.hl7.org/fhir/http.html#create
951
- *
952
941
  * @category Create
953
942
  * @param resource The FHIR resource to create.
943
+ * @param options Optional fetch options.
954
944
  * @returns The result of the create operation.
955
945
  */
956
- createResource(resource) {
946
+ createResource(resource, options) {
957
947
  if (!resource.resourceType) {
958
948
  throw new Error('Missing resourceType');
959
949
  }
960
950
  this.invalidateSearches(resource.resourceType);
961
- return this.post(this.fhirUrl(resource.resourceType), resource);
951
+ return this.post(this.fhirUrl(resource.resourceType), resource, undefined, options);
962
952
  }
963
953
  /**
964
954
  * Conditionally create a new FHIR resource only if some equivalent resource does not already exist on the server.
@@ -994,14 +984,15 @@ class MedplumClient extends EventTarget {
994
984
  * The query parameter only contains the search parameters (what would be in the URL following the "?").
995
985
  *
996
986
  * See the FHIR "conditional create" operation for full details: https://www.hl7.org/fhir/http.html#ccreate
997
- *
998
987
  * @category Create
999
988
  * @param resource The FHIR resource to create.
1000
989
  * @param query The search query for an equivalent resource (should not include resource type or "?").
990
+ * @param options Optional fetch options.
1001
991
  * @returns The result of the create operation.
1002
992
  */
1003
- async createResourceIfNoneExist(resource, query) {
1004
- return ((await this.searchOne(resource.resourceType, query)) ?? this.createResource(resource));
993
+ async createResourceIfNoneExist(resource, query, options) {
994
+ return ((await this.searchOne(resource.resourceType, query, options)) ??
995
+ this.createResource(resource, options));
1005
996
  }
1006
997
  /**
1007
998
  * Creates a FHIR `Binary` resource with the provided data content.
@@ -1020,11 +1011,11 @@ class MedplumClient extends EventTarget {
1020
1011
  * ```
1021
1012
  *
1022
1013
  * See the FHIR "create" operation for full details: https://www.hl7.org/fhir/http.html#create
1023
- *
1024
1014
  * @category Create
1025
1015
  * @param data The binary data to upload.
1026
1016
  * @param filename Optional filename for the binary.
1027
1017
  * @param contentType Content type for the binary.
1018
+ * @param onProgress Optional callback for progress events.
1028
1019
  * @returns The result of the create operation.
1029
1020
  */
1030
1021
  createBinary(data, filename, contentType, onProgress) {
@@ -1083,9 +1074,11 @@ class MedplumClient extends EventTarget {
1083
1074
  * ```
1084
1075
  *
1085
1076
  * See the pdfmake document definition for full details: https://pdfmake.github.io/docs/0.1/document-definition-object/
1086
- *
1087
1077
  * @category Media
1088
1078
  * @param docDefinition The PDF document definition.
1079
+ * @param filename Optional filename for the PDF binary resource.
1080
+ * @param tableLayouts Optional pdfmake custom table layout.
1081
+ * @param fonts Optional pdfmake custom font dictionary.
1089
1082
  * @returns The result of the create operation.
1090
1083
  */
1091
1084
  async createPdf(docDefinition, filename, tableLayouts, fonts) {
@@ -1099,13 +1092,13 @@ class MedplumClient extends EventTarget {
1099
1092
  * Creates a FHIR `Communication` resource with the provided data content.
1100
1093
  *
1101
1094
  * This is a convenience method to handle commmon cases where a `Communication` resource is created with a `payload`.
1102
- *
1103
1095
  * @category Create
1104
1096
  * @param resource The FHIR resource to comment on.
1105
1097
  * @param text The text of the comment.
1098
+ * @param options Optional fetch options.
1106
1099
  * @returns The result of the create operation.
1107
1100
  */
1108
- createComment(resource, text) {
1101
+ createComment(resource, text, options) {
1109
1102
  const profile = this.getProfile();
1110
1103
  let encounter = undefined;
1111
1104
  let subject = undefined;
@@ -1128,7 +1121,7 @@ class MedplumClient extends EventTarget {
1128
1121
  sender: profile ? createReference(profile) : undefined,
1129
1122
  sent: new Date().toISOString(),
1130
1123
  payload: [{ contentString: text }],
1131
- });
1124
+ }, options);
1132
1125
  }
1133
1126
  /**
1134
1127
  * Updates a FHIR resource.
@@ -1150,12 +1143,12 @@ class MedplumClient extends EventTarget {
1150
1143
  * ```
1151
1144
  *
1152
1145
  * See the FHIR "update" operation for full details: https://www.hl7.org/fhir/http.html#update
1153
- *
1154
1146
  * @category Write
1155
1147
  * @param resource The FHIR resource to update.
1148
+ * @param options Optional fetch options.
1156
1149
  * @returns The result of the update operation.
1157
1150
  */
1158
- async updateResource(resource) {
1151
+ async updateResource(resource, options) {
1159
1152
  if (!resource.resourceType) {
1160
1153
  throw new Error('Missing resourceType');
1161
1154
  }
@@ -1163,7 +1156,7 @@ class MedplumClient extends EventTarget {
1163
1156
  throw new Error('Missing id');
1164
1157
  }
1165
1158
  this.invalidateSearches(resource.resourceType);
1166
- let result = await this.put(this.fhirUrl(resource.resourceType, resource.id), resource);
1159
+ let result = await this.put(this.fhirUrl(resource.resourceType, resource.id), resource, undefined, options);
1167
1160
  if (!result) {
1168
1161
  // On 304 not modified, result will be undefined
1169
1162
  // Return the user input instead
@@ -1190,16 +1183,16 @@ class MedplumClient extends EventTarget {
1190
1183
  * See the FHIR "update" operation for full details: https://www.hl7.org/fhir/http.html#patch
1191
1184
  *
1192
1185
  * See the JSONPatch specification for full details: https://tools.ietf.org/html/rfc6902
1193
- *
1194
1186
  * @category Write
1195
1187
  * @param resourceType The FHIR resource type.
1196
1188
  * @param id The resource ID.
1197
1189
  * @param operations The JSONPatch operations.
1190
+ * @param options Optional fetch options.
1198
1191
  * @returns The result of the patch operations.
1199
1192
  */
1200
- patchResource(resourceType, id, operations) {
1193
+ patchResource(resourceType, id, operations, options) {
1201
1194
  this.invalidateSearches(resourceType);
1202
- return this.patch(this.fhirUrl(resourceType, id), operations);
1195
+ return this.patch(this.fhirUrl(resourceType, id), operations, options);
1203
1196
  }
1204
1197
  /**
1205
1198
  * Deletes a FHIR resource by resource type and ID.
@@ -1211,16 +1204,16 @@ class MedplumClient extends EventTarget {
1211
1204
  * ```
1212
1205
  *
1213
1206
  * See the FHIR "delete" operation for full details: https://www.hl7.org/fhir/http.html#delete
1214
- *
1215
1207
  * @category Delete
1216
1208
  * @param resourceType The FHIR resource type.
1217
1209
  * @param id The resource ID.
1210
+ * @param options Optional fetch options.
1218
1211
  * @returns The result of the delete operation.
1219
1212
  */
1220
- deleteResource(resourceType, id) {
1213
+ deleteResource(resourceType, id, options) {
1221
1214
  this.deleteCacheEntry(this.fhirUrl(resourceType, id).toString());
1222
1215
  this.invalidateSearches(resourceType);
1223
- return this.delete(this.fhirUrl(resourceType, id));
1216
+ return this.delete(this.fhirUrl(resourceType, id), options);
1224
1217
  }
1225
1218
  /**
1226
1219
  * Executes the validate operation with the provided resource.
@@ -1235,12 +1228,12 @@ class MedplumClient extends EventTarget {
1235
1228
  * ```
1236
1229
  *
1237
1230
  * See the FHIR "$validate" operation for full details: https://www.hl7.org/fhir/resource-operation-validate.html
1238
- *
1239
1231
  * @param resource The FHIR resource.
1232
+ * @param options Optional fetch options.
1240
1233
  * @returns The validate operation outcome.
1241
1234
  */
1242
- validateResource(resource) {
1243
- return this.post(this.fhirUrl(resource.resourceType, '$validate'), resource);
1235
+ validateResource(resource, options) {
1236
+ return this.post(this.fhirUrl(resource.resourceType, '$validate'), resource, undefined, options);
1244
1237
  }
1245
1238
  /**
1246
1239
  * Executes a bot by ID or Identifier.
@@ -1250,7 +1243,7 @@ class MedplumClient extends EventTarget {
1250
1243
  * @param options Optional fetch options.
1251
1244
  * @returns The Bot return value.
1252
1245
  */
1253
- executeBot(idOrIdentifier, body, contentType, options = {}) {
1246
+ executeBot(idOrIdentifier, body, contentType, options) {
1254
1247
  let url;
1255
1248
  if (typeof idOrIdentifier === 'string') {
1256
1249
  const id = idOrIdentifier;
@@ -1310,7 +1303,7 @@ class MedplumClient extends EventTarget {
1310
1303
  * @param options Optional fetch options.
1311
1304
  * @returns The FHIR batch/transaction response bundle.
1312
1305
  */
1313
- executeBatch(bundle, options = {}) {
1306
+ executeBatch(bundle, options) {
1314
1307
  return this.post(this.fhirBaseUrl.slice(0, -1), bundle, undefined, options);
1315
1308
  }
1316
1309
  /**
@@ -1347,11 +1340,12 @@ class MedplumClient extends EventTarget {
1347
1340
  *
1348
1341
  * See options here: https://nodemailer.com/extras/mailcomposer/
1349
1342
  * @category Media
1350
- * @param options The MailComposer options.
1343
+ * @param email The MailComposer options.
1344
+ * @param options Optional fetch options.
1351
1345
  * @returns Promise to the operation outcome.
1352
1346
  */
1353
- sendEmail(email) {
1354
- return this.post('email/v1/send', email, 'application/json');
1347
+ sendEmail(email, options) {
1348
+ return this.post('email/v1/send', email, 'application/json', options);
1355
1349
  }
1356
1350
  /**
1357
1351
  * Executes a GraphQL query.
@@ -1393,7 +1387,6 @@ class MedplumClient extends EventTarget {
1393
1387
  * See the GraphQL documentation for more details: https://graphql.org/learn/
1394
1388
  *
1395
1389
  * See the FHIR GraphQL documentation for FHIR specific details: https://www.hl7.org/fhir/graphql.html
1396
- *
1397
1390
  * @category Read
1398
1391
  * @param query The GraphQL query.
1399
1392
  * @param operationName Optional GraphQL operation name.
@@ -1408,15 +1401,15 @@ class MedplumClient extends EventTarget {
1408
1401
  *
1409
1402
  * Executes the $graph operation on this resource to fetch a Bundle of resources linked to the target resource
1410
1403
  * according to a graph definition
1411
-
1412
1404
  * @category Read
1413
1405
  * @param resourceType The FHIR resource type.
1414
1406
  * @param id The resource ID.
1415
1407
  * @param graphName `name` parameter of the GraphDefinition
1408
+ * @param options Optional fetch options.
1416
1409
  * @returns A Bundle
1417
1410
  */
1418
- readResourceGraph(resourceType, id, graphName) {
1419
- return this.get(`${this.fhirUrl(resourceType, id)}/$graph?graph=${graphName}`);
1411
+ readResourceGraph(resourceType, id, graphName, options) {
1412
+ return this.get(`${this.fhirUrl(resourceType, id)}/$graph?graph=${graphName}`, options);
1420
1413
  }
1421
1414
  /**
1422
1415
  * @category Authentication
@@ -1426,11 +1419,16 @@ class MedplumClient extends EventTarget {
1426
1419
  return this.storage.getObject('activeLogin');
1427
1420
  }
1428
1421
  /**
1422
+ * Sets the active login.
1423
+ * @param login The new active login state.
1429
1424
  * @category Authentication
1430
1425
  */
1431
1426
  async setActiveLogin(login) {
1432
1427
  this.clearActiveLogin();
1433
1428
  this.accessToken = login.accessToken;
1429
+ if (this.basicAuth) {
1430
+ return;
1431
+ }
1434
1432
  this.refreshToken = login.refreshToken;
1435
1433
  this.storage.setObject('activeLogin', login);
1436
1434
  this.addLogin(login);
@@ -1439,6 +1437,7 @@ class MedplumClient extends EventTarget {
1439
1437
  }
1440
1438
  /**
1441
1439
  * Returns the current access token.
1440
+ * @returns The current access token.
1442
1441
  * @category Authentication
1443
1442
  */
1444
1443
  getAccessToken() {
@@ -1446,6 +1445,7 @@ class MedplumClient extends EventTarget {
1446
1445
  }
1447
1446
  /**
1448
1447
  * Sets the current access token.
1448
+ * @param accessToken The new access token.
1449
1449
  * @category Authentication
1450
1450
  */
1451
1451
  setAccessToken(accessToken) {
@@ -1455,6 +1455,8 @@ class MedplumClient extends EventTarget {
1455
1455
  this.config = undefined;
1456
1456
  }
1457
1457
  /**
1458
+ * Returns the list of available logins.
1459
+ * @returns The list of available logins.
1458
1460
  * @category Authentication
1459
1461
  */
1460
1462
  getLogins() {
@@ -1467,6 +1469,9 @@ class MedplumClient extends EventTarget {
1467
1469
  }
1468
1470
  async refreshProfile() {
1469
1471
  this.profilePromise = new Promise((resolve, reject) => {
1472
+ if (this.basicAuth) {
1473
+ return;
1474
+ }
1470
1475
  this.get('auth/me')
1471
1476
  .then((result) => {
1472
1477
  this.profilePromise = undefined;
@@ -1480,18 +1485,26 @@ class MedplumClient extends EventTarget {
1480
1485
  return this.profilePromise;
1481
1486
  }
1482
1487
  /**
1488
+ * Returns true if the client is waiting for authentication.
1489
+ * @returns True if the client is waiting for authentication.
1483
1490
  * @category Authentication
1484
1491
  */
1485
1492
  isLoading() {
1486
1493
  return !!this.profilePromise;
1487
1494
  }
1488
1495
  /**
1496
+ * Returns the current user profile resource if available.
1497
+ * This method does not wait for loading promises.
1498
+ * @returns The current user profile resource if available.
1489
1499
  * @category User Profile
1490
1500
  */
1491
1501
  getProfile() {
1492
1502
  return this.profile;
1493
1503
  }
1494
1504
  /**
1505
+ * Returns the current user profile resource if available.
1506
+ * This method waits for loading promises.
1507
+ * @returns The current user profile resource if available.
1495
1508
  * @category User Profile
1496
1509
  */
1497
1510
  async getProfileAsync() {
@@ -1501,6 +1514,8 @@ class MedplumClient extends EventTarget {
1501
1514
  return this.getProfile();
1502
1515
  }
1503
1516
  /**
1517
+ * Returns the current user configuration if available.
1518
+ * @returns The current user configuration if available.
1504
1519
  * @category User Profile
1505
1520
  */
1506
1521
  getUserConfiguration() {
@@ -1508,9 +1523,9 @@ class MedplumClient extends EventTarget {
1508
1523
  }
1509
1524
  /**
1510
1525
  * Downloads the URL as a blob.
1511
- *
1512
1526
  * @category Read
1513
1527
  * @param url The URL to request.
1528
+ * @param options Optional fetch request init options.
1514
1529
  * @returns Promise to the response body as a blob.
1515
1530
  */
1516
1531
  async download(url, options = {}) {
@@ -1524,12 +1539,13 @@ class MedplumClient extends EventTarget {
1524
1539
  /**
1525
1540
  * Upload media to the server and create a Media instance for the uploaded content.
1526
1541
  * @param contents The contents of the media file, as a string, Uint8Array, File, or Blob.
1527
- * @param contentType The media type of the content
1528
- * @param filename The name of the file to be uploaded, or undefined if not applicable
1529
- * @param additionalFields Additional fields for Media
1542
+ * @param contentType The media type of the content.
1543
+ * @param filename The name of the file to be uploaded, or undefined if not applicable.
1544
+ * @param additionalFields Additional fields for Media.
1545
+ * @param options Optional fetch options.
1530
1546
  * @returns Promise that resolves to the created Media
1531
1547
  */
1532
- async uploadMedia(contents, contentType, filename, additionalFields) {
1548
+ async uploadMedia(contents, contentType, filename, additionalFields, options) {
1533
1549
  const binary = await this.createBinary(contents, filename, contentType);
1534
1550
  return this.createResource({
1535
1551
  ...additionalFields,
@@ -1539,11 +1555,10 @@ class MedplumClient extends EventTarget {
1539
1555
  url: 'Binary/' + binary.id,
1540
1556
  title: filename,
1541
1557
  },
1542
- });
1558
+ }, options);
1543
1559
  }
1544
1560
  /**
1545
1561
  * Performs Bulk Data Export operation request flow. See The FHIR "Bulk Data Export" for full details: https://build.fhir.org/ig/HL7/bulk-data/export.html#bulk-data-export
1546
- *
1547
1562
  * @param exportLevel Optional export level. Defaults to system level export. 'Group/:id' - Group of Patients, 'Patient' - All Patients.
1548
1563
  * @param resourceTypes A string of comma-delimited FHIR resource types.
1549
1564
  * @param since Resources will be included in the response if their state has changed after the supplied time (e.g. if Resource.meta.lastUpdated is later than the supplied _since time).
@@ -1559,7 +1574,6 @@ class MedplumClient extends EventTarget {
1559
1574
  if (since) {
1560
1575
  url.searchParams.set('_since', since);
1561
1576
  }
1562
- options.method = exportLevel ? 'GET' : 'POST';
1563
1577
  this.addFetchOptionsDefaults(options);
1564
1578
  const headers = options.headers;
1565
1579
  headers['Prefer'] = 'respond-async';
@@ -1623,10 +1637,10 @@ class MedplumClient extends EventTarget {
1623
1637
  }
1624
1638
  /**
1625
1639
  * Makes an HTTP request.
1626
- * @param {string} method
1627
- * @param {string} url
1628
- * @param {string=} contentType
1629
- * @param {Object=} body
1640
+ * @param method The HTTP method (GET, POST, etc).
1641
+ * @param url The target URL.
1642
+ * @param options Optional fetch request init options.
1643
+ * @returns The JSON content body if available.
1630
1644
  */
1631
1645
  async request(method, url, options = {}) {
1632
1646
  if (this.refreshPromise) {
@@ -1691,9 +1705,7 @@ class MedplumClient extends EventTarget {
1691
1705
  let resultResponse;
1692
1706
  const retryDelay = 200;
1693
1707
  while (checkStatus) {
1694
- const fetchOptions = {
1695
- method: 'GET',
1696
- };
1708
+ const fetchOptions = {};
1697
1709
  this.addFetchOptionsDefaults(fetchOptions);
1698
1710
  const statusResponse = await this.fetchWithRetry(statusUrl, fetchOptions);
1699
1711
  if (statusResponse.status !== 202) {
@@ -1769,7 +1781,7 @@ class MedplumClient extends EventTarget {
1769
1781
  if (this.accessToken) {
1770
1782
  headers['Authorization'] = 'Bearer ' + this.accessToken;
1771
1783
  }
1772
- if (this.basicAuth) {
1784
+ else if (this.basicAuth) {
1773
1785
  headers['Authorization'] = 'Basic ' + this.basicAuth;
1774
1786
  }
1775
1787
  if (!options.cache) {
@@ -1813,8 +1825,8 @@ class MedplumClient extends EventTarget {
1813
1825
  * Otherwise, calls unauthenticated callbacks and rejects.
1814
1826
  * @param method The HTTP method of the original request.
1815
1827
  * @param url The URL of the original request.
1816
- * @param contentType The content type of the original request.
1817
- * @param body The body of the original request.
1828
+ * @param options Optional fetch request init options.
1829
+ * @returns The result of the retry.
1818
1830
  */
1819
1831
  handleUnauthenticated(method, url, options) {
1820
1832
  if (this.refresh()) {
@@ -1830,6 +1842,7 @@ class MedplumClient extends EventTarget {
1830
1842
  * Starts a new PKCE flow.
1831
1843
  * These PKCE values are stateful, and must survive redirects and page refreshes.
1832
1844
  * @category Authentication
1845
+ * @returns The PKCE code challenge details.
1833
1846
  */
1834
1847
  async startPkce() {
1835
1848
  const pkceState = getRandomString();
@@ -1844,7 +1857,8 @@ class MedplumClient extends EventTarget {
1844
1857
  /**
1845
1858
  * Redirects the user to the login screen for authorization.
1846
1859
  * Clears all auth state including local storage and session storage.
1847
- * See: https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
1860
+ * @param loginParams The authorization login parameters.
1861
+ * @see https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
1848
1862
  */
1849
1863
  async requestAuthorization(loginParams) {
1850
1864
  const loginRequest = await this.ensureCodeChallenge(loginParams || {});
@@ -1863,6 +1877,7 @@ class MedplumClient extends EventTarget {
1863
1877
  * See: https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
1864
1878
  * @param code The authorization code received by URL parameter.
1865
1879
  * @param loginParams Optional login parameters.
1880
+ * @returns The user profile resource.
1866
1881
  * @category Authentication
1867
1882
  */
1868
1883
  processCode(code, loginParams) {
@@ -1881,7 +1896,8 @@ class MedplumClient extends EventTarget {
1881
1896
  }
1882
1897
  /**
1883
1898
  * Tries to refresh the auth tokens.
1884
- * See: https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokens
1899
+ * @returns The refresh promise if available; otherwise undefined.
1900
+ * @see https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokens
1885
1901
  */
1886
1902
  refresh() {
1887
1903
  if (this.refreshPromise) {
@@ -1934,7 +1950,6 @@ class MedplumClient extends EventTarget {
1934
1950
  * // Example Search
1935
1951
  * await medplum.searchResources('Patient')
1936
1952
  * ```
1937
- *
1938
1953
  * @category Authentication
1939
1954
  * @param clientId The client ID.
1940
1955
  * @param clientSecret The client secret.
@@ -1957,14 +1972,20 @@ class MedplumClient extends EventTarget {
1957
1972
  * Makes a POST request to the tokens endpoint.
1958
1973
  * See: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
1959
1974
  * @param formBody Token parameters in URL encoded format.
1975
+ * @returns The user profile resource.
1960
1976
  */
1961
1977
  async fetchTokens(formBody) {
1962
- const response = await this.fetch(this.tokenUrl, {
1978
+ const options = {
1963
1979
  method: 'POST',
1964
1980
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
1965
1981
  body: formBody,
1966
1982
  credentials: 'include',
1967
- });
1983
+ };
1984
+ const headers = options.headers;
1985
+ if (this.basicAuth) {
1986
+ headers['Authorization'] = `Basic ${this.basicAuth}`;
1987
+ }
1988
+ const response = await this.fetch(this.tokenUrl, options);
1968
1989
  if (!response.ok) {
1969
1990
  this.clearActiveLogin();
1970
1991
  throw new Error('Failed to fetch tokens');
@@ -1977,7 +1998,8 @@ class MedplumClient extends EventTarget {
1977
1998
  * Verifies the tokens received from the auth server.
1978
1999
  * Validates the JWT against the JWKS.
1979
2000
  * See: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
1980
- * @param tokens
2001
+ * @param tokens The token response.
2002
+ * @returns Promise to complete.
1981
2003
  */
1982
2004
  async verifyTokens(tokens) {
1983
2005
  const token = tokens.access_token;
@@ -1988,7 +2010,14 @@ class MedplumClient extends EventTarget {
1988
2010
  throw new Error('Token expired');
1989
2011
  }
1990
2012
  // Verify app_client_id
1991
- if (this.clientId && tokenPayload.client_id !== this.clientId) {
2013
+ // external tokenPayload
2014
+ if (tokenPayload.cid) {
2015
+ if (tokenPayload.cid !== this.clientId) {
2016
+ this.clearActiveLogin();
2017
+ throw new Error('Token was not issued for this audience');
2018
+ }
2019
+ }
2020
+ else if (this.clientId && tokenPayload.client_id !== this.clientId) {
1992
2021
  this.clearActiveLogin();
1993
2022
  throw new Error('Token was not issued for this audience');
1994
2023
  }
@@ -2042,6 +2071,7 @@ function getDefaultFetch() {
2042
2071
  }
2043
2072
  /**
2044
2073
  * Returns the base URL for the current page.
2074
+ * @returns The window origin string.
2045
2075
  * @category HTTP
2046
2076
  */
2047
2077
  function getWindowOrigin() {