@medplum/core 0.9.1 → 0.9.4

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
@@ -69,6 +69,9 @@ class LRUCache {
69
69
  }
70
70
  __classPrivateFieldGet(this, _LRUCache_cache, "f").set(key, val);
71
71
  }
72
+ delete(key) {
73
+ __classPrivateFieldGet(this, _LRUCache_cache, "f").delete(key);
74
+ }
72
75
  }
73
76
  _LRUCache_max = new WeakMap(), _LRUCache_cache = new WeakMap(), _LRUCache_instances = new WeakSet(), _LRUCache_first = function _LRUCache_first() {
74
77
  // This works because the Map class maintains ordered keys.
@@ -395,37 +398,71 @@ function isEmpty(v) {
395
398
  /**
396
399
  * Resource equality.
397
400
  * Ignores meta.versionId and meta.lastUpdated.
398
- * See: https://dmitripavlutin.com/how-to-compare-objects-in-javascript/#4-deep-equality
399
401
  * @param object1 The first object.
400
402
  * @param object2 The second object.
401
403
  * @returns True if the objects are equal.
402
404
  */
403
405
  function deepEquals(object1, object2, path) {
404
- let keys1 = Object.keys(object1);
405
- let keys2 = Object.keys(object2);
406
- if (path === 'meta') {
407
- keys1 = keys1.filter((k) => k !== 'versionId' && k !== 'lastUpdated' && k !== 'author');
408
- keys2 = keys2.filter((k) => k !== 'versionId' && k !== 'lastUpdated' && k !== 'author');
406
+ if (object1 === object2) {
407
+ return true;
408
+ }
409
+ if (isEmpty(object1) && isEmpty(object2)) {
410
+ return true;
411
+ }
412
+ if (isEmpty(object1) || isEmpty(object2)) {
413
+ return false;
414
+ }
415
+ if (Array.isArray(object1) && Array.isArray(object2)) {
416
+ return deepEqualsArray(object1, object2);
417
+ }
418
+ if (Array.isArray(object1) || Array.isArray(object2)) {
419
+ return false;
420
+ }
421
+ if (isObject(object1) && isObject(object2)) {
422
+ return deepEqualsObject(object1, object2, path);
423
+ }
424
+ if (isObject(object1) || isObject(object2)) {
425
+ return false;
409
426
  }
410
- if (keys1.length !== keys2.length) {
427
+ return false;
428
+ }
429
+ function deepEqualsArray(array1, array2) {
430
+ if (array1.length !== array2.length) {
411
431
  return false;
412
432
  }
413
- for (const key of keys1) {
433
+ for (let i = 0; i < array1.length; i++) {
434
+ if (!deepEquals(array1[i], array2[i])) {
435
+ return false;
436
+ }
437
+ }
438
+ return true;
439
+ }
440
+ function deepEqualsObject(object1, object2, path) {
441
+ const keySet = new Set();
442
+ Object.keys(object1).forEach((k) => keySet.add(k));
443
+ Object.keys(object2).forEach((k) => keySet.add(k));
444
+ if (path === 'meta') {
445
+ keySet.delete('versionId');
446
+ keySet.delete('lastUpdated');
447
+ keySet.delete('author');
448
+ }
449
+ for (const key of keySet) {
414
450
  const val1 = object1[key];
415
451
  const val2 = object2[key];
416
- if (isObject(val1) && isObject(val2)) {
417
- if (!deepEquals(val1, val2, key)) {
418
- return false;
419
- }
420
- }
421
- else {
422
- if (val1 !== val2) {
423
- return false;
424
- }
452
+ if (!deepEquals(val1, val2, key)) {
453
+ return false;
425
454
  }
426
455
  }
427
456
  return true;
428
457
  }
458
+ /**
459
+ * Returns true if the input string is a UUID.
460
+ * @param input The input string.
461
+ * @returns True if the input string matches the UUID format.
462
+ */
463
+ function isUUID(input) {
464
+ return !!input.match(/^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/i);
465
+ }
429
466
  /**
430
467
  * Returns true if the input is an object.
431
468
  * @param object The candidate object.
@@ -476,7 +513,7 @@ function arrayBufferToBase64(arrayBuffer) {
476
513
  return window.btoa(result.join(''));
477
514
  }
478
515
  function capitalize(word) {
479
- return word.charAt(0).toUpperCase() + word.substr(1);
516
+ return word.charAt(0).toUpperCase() + word.substring(1);
480
517
  }
481
518
  function isLowerCase(c) {
482
519
  return c === c.toLowerCase();
@@ -719,6 +756,83 @@ class OperationOutcomeError extends Error {
719
756
  }
720
757
  }
721
758
 
759
+ var _ReadablePromise_suspender, _ReadablePromise_status, _ReadablePromise_response, _ReadablePromise_error, _a;
760
+ /**
761
+ * The ReadablePromise class wraps a request promise suitable for React Suspense.
762
+ * See: https://blog.logrocket.com/react-suspense-data-fetching/#wrappromise-js
763
+ * See: https://github.com/ovieokeh/suspense-data-fetching/blob/master/lib/api/wrapPromise.js
764
+ */
765
+ class ReadablePromise {
766
+ constructor(requestPromise) {
767
+ this[_a] = 'ReadablePromise';
768
+ _ReadablePromise_suspender.set(this, void 0);
769
+ _ReadablePromise_status.set(this, 'pending');
770
+ _ReadablePromise_response.set(this, void 0);
771
+ _ReadablePromise_error.set(this, void 0);
772
+ __classPrivateFieldSet(this, _ReadablePromise_suspender, requestPromise.then((res) => {
773
+ __classPrivateFieldSet(this, _ReadablePromise_status, 'success', "f");
774
+ __classPrivateFieldSet(this, _ReadablePromise_response, res, "f");
775
+ return res;
776
+ }, (err) => {
777
+ __classPrivateFieldSet(this, _ReadablePromise_status, 'error', "f");
778
+ __classPrivateFieldSet(this, _ReadablePromise_error, err, "f");
779
+ throw err;
780
+ }), "f");
781
+ }
782
+ /**
783
+ * Returns true if the promise is pending.
784
+ * @returns True if the Promise is pending.
785
+ */
786
+ isPending() {
787
+ return __classPrivateFieldGet(this, _ReadablePromise_status, "f") === 'pending';
788
+ }
789
+ /**
790
+ * Attempts to read the value of the promise.
791
+ * If the promise is pending, this method will throw a promise.
792
+ * If the promise rejected, this method will throw the rejection reason.
793
+ * If the promise resolved, this method will return the resolved value.
794
+ * @returns The resolved value of the Promise.
795
+ */
796
+ read() {
797
+ switch (__classPrivateFieldGet(this, _ReadablePromise_status, "f")) {
798
+ case 'pending':
799
+ throw __classPrivateFieldGet(this, _ReadablePromise_suspender, "f");
800
+ case 'error':
801
+ throw __classPrivateFieldGet(this, _ReadablePromise_error, "f");
802
+ default:
803
+ return __classPrivateFieldGet(this, _ReadablePromise_response, "f");
804
+ }
805
+ }
806
+ /**
807
+ * Attaches callbacks for the resolution and/or rejection of the Promise.
808
+ * @param onfulfilled The callback to execute when the Promise is resolved.
809
+ * @param onrejected The callback to execute when the Promise is rejected.
810
+ * @returns A Promise for the completion of which ever callback is executed.
811
+ */
812
+ then(onfulfilled, onrejected) {
813
+ return __classPrivateFieldGet(this, _ReadablePromise_suspender, "f").then(onfulfilled, onrejected);
814
+ }
815
+ /**
816
+ * Attaches a callback for only the rejection of the Promise.
817
+ * @param onrejected The callback to execute when the Promise is rejected.
818
+ * @returns A Promise for the completion of the callback.
819
+ */
820
+ catch(onrejected) {
821
+ return __classPrivateFieldGet(this, _ReadablePromise_suspender, "f").catch(onrejected);
822
+ }
823
+ /**
824
+ * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The
825
+ * resolved value cannot be modified from the callback.
826
+ * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).
827
+ * @returns A Promise for the completion of the callback.
828
+ */
829
+ finally(onfinally) {
830
+ return __classPrivateFieldGet(this, _ReadablePromise_suspender, "f").finally(onfinally);
831
+ }
832
+ }
833
+ _ReadablePromise_suspender = new WeakMap(), _ReadablePromise_status = new WeakMap(), _ReadablePromise_response = new WeakMap(), _ReadablePromise_error = new WeakMap(), _a = Symbol.toStringTag;
834
+
835
+ const DEFAULT_SEARCH_COUNT = 20;
722
836
  /**
723
837
  * Search operators.
724
838
  * These operators represent "modifiers" and "prefixes" in FHIR search.
@@ -786,15 +900,15 @@ function parseSearchDefinition(url) {
786
900
  let filters = undefined;
787
901
  let sortRules = undefined;
788
902
  let fields = undefined;
789
- let page = undefined;
903
+ let offset = undefined;
790
904
  let count = undefined;
791
905
  let total = undefined;
792
906
  params.forEach((value, key) => {
793
907
  if (key === '_fields') {
794
908
  fields = value.split(',');
795
909
  }
796
- else if (key === '_page') {
797
- page = parseInt(value);
910
+ else if (key === '_offset') {
911
+ offset = parseInt(value);
798
912
  }
799
913
  else if (key === '_count') {
800
914
  count = parseInt(value);
@@ -815,7 +929,7 @@ function parseSearchDefinition(url) {
815
929
  resourceType,
816
930
  filters,
817
931
  fields,
818
- page,
932
+ offset,
819
933
  count,
820
934
  total,
821
935
  sortRules,
@@ -891,13 +1005,13 @@ function formatSearchQuery(definition) {
891
1005
  if (definition.sortRules && definition.sortRules.length > 0) {
892
1006
  params.push(formatSortRules(definition.sortRules));
893
1007
  }
894
- if (definition.page && definition.page > 0) {
895
- params.push('_page=' + definition.page);
1008
+ if (definition.offset !== undefined) {
1009
+ params.push('_offset=' + definition.offset);
896
1010
  }
897
- if (definition.count && definition.count > 0) {
1011
+ if (definition.count !== undefined) {
898
1012
  params.push('_count=' + definition.count);
899
1013
  }
900
- if (definition.total) {
1014
+ if (definition.total !== undefined) {
901
1015
  params.push('_total=' + encodeURIComponent(definition.total));
902
1016
  }
903
1017
  if (params.length === 0) {
@@ -1206,7 +1320,7 @@ function getPropertyDisplayName(property) {
1206
1320
 
1207
1321
  // PKCE auth ased on:
1208
1322
  // https://aws.amazon.com/blogs/security/how-to-add-authentication-single-page-web-application-with-amazon-cognito-oauth2-implementation/
1209
- 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_addFetchOptionsDefaults, _MedplumClient_setRequestContentType, _MedplumClient_setRequestBody, _MedplumClient_handleUnauthenticated, _MedplumClient_startPkce, _MedplumClient_requestAuthorization, _MedplumClient_refresh, _MedplumClient_fetchTokens, _MedplumClient_verifyTokens, _MedplumClient_setupStorageListener;
1323
+ var _MedplumClient_instances, _MedplumClient_fetch, _MedplumClient_storage, _MedplumClient_schema, _MedplumClient_requestCache, _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_addFetchOptionsDefaults, _MedplumClient_setRequestContentType, _MedplumClient_setRequestBody, _MedplumClient_handleUnauthenticated, _MedplumClient_startPkce, _MedplumClient_requestAuthorization, _MedplumClient_refresh, _MedplumClient_fetchTokens, _MedplumClient_verifyTokens, _MedplumClient_setupStorageListener;
1210
1324
  const DEFAULT_BASE_URL = 'https://api.medplum.com/';
1211
1325
  const DEFAULT_SCOPE = 'launch/patient openid fhirUser offline_access user/*.*';
1212
1326
  const DEFAULT_RESOURCE_CACHE_SIZE = 1000;
@@ -1269,7 +1383,7 @@ class MedplumClient extends EventTarget {
1269
1383
  _MedplumClient_fetch.set(this, void 0);
1270
1384
  _MedplumClient_storage.set(this, void 0);
1271
1385
  _MedplumClient_schema.set(this, void 0);
1272
- _MedplumClient_resourceCache.set(this, void 0);
1386
+ _MedplumClient_requestCache.set(this, void 0);
1273
1387
  _MedplumClient_baseUrl.set(this, void 0);
1274
1388
  _MedplumClient_clientId.set(this, void 0);
1275
1389
  _MedplumClient_authorizeUrl.set(this, void 0);
@@ -1293,7 +1407,7 @@ class MedplumClient extends EventTarget {
1293
1407
  __classPrivateFieldSet(this, _MedplumClient_fetch, (options === null || options === void 0 ? void 0 : options.fetch) || window.fetch.bind(window), "f");
1294
1408
  __classPrivateFieldSet(this, _MedplumClient_storage, new ClientStorage(), "f");
1295
1409
  __classPrivateFieldSet(this, _MedplumClient_schema, createSchema(), "f");
1296
- __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");
1410
+ __classPrivateFieldSet(this, _MedplumClient_requestCache, new LRUCache((_a = options === null || options === void 0 ? void 0 : options.resourceCacheSize) !== null && _a !== void 0 ? _a : DEFAULT_RESOURCE_CACHE_SIZE), "f");
1297
1411
  __classPrivateFieldSet(this, _MedplumClient_baseUrl, (options === null || options === void 0 ? void 0 : options.baseUrl) || DEFAULT_BASE_URL, "f");
1298
1412
  __classPrivateFieldSet(this, _MedplumClient_clientId, (options === null || options === void 0 ? void 0 : options.clientId) || '', "f");
1299
1413
  __classPrivateFieldSet(this, _MedplumClient_authorizeUrl, (options === null || options === void 0 ? void 0 : options.authorizeUrl) || __classPrivateFieldGet(this, _MedplumClient_baseUrl, "f") + 'oauth2/authorize', "f");
@@ -1313,7 +1427,7 @@ class MedplumClient extends EventTarget {
1313
1427
  */
1314
1428
  clear() {
1315
1429
  __classPrivateFieldGet(this, _MedplumClient_storage, "f").clear();
1316
- __classPrivateFieldGet(this, _MedplumClient_resourceCache, "f").clear();
1430
+ __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").clear();
1317
1431
  __classPrivateFieldSet(this, _MedplumClient_accessToken, undefined, "f");
1318
1432
  __classPrivateFieldSet(this, _MedplumClient_refreshToken, undefined, "f");
1319
1433
  __classPrivateFieldSet(this, _MedplumClient_profile, undefined, "f");
@@ -1332,7 +1446,15 @@ class MedplumClient extends EventTarget {
1332
1446
  * @returns Promise to the response content.
1333
1447
  */
1334
1448
  get(url, options = {}) {
1335
- return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, 'GET', url, options);
1449
+ if (!(options === null || options === void 0 ? void 0 : options.cache)) {
1450
+ const cached = __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").get(url);
1451
+ if (cached) {
1452
+ return cached;
1453
+ }
1454
+ }
1455
+ const promise = new ReadablePromise(__classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, 'GET', url, options));
1456
+ __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").set(url, promise);
1457
+ return promise;
1336
1458
  }
1337
1459
  /**
1338
1460
  * Makes an HTTP POST request to the specified URL.
@@ -1354,6 +1476,7 @@ class MedplumClient extends EventTarget {
1354
1476
  if (contentType) {
1355
1477
  __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setRequestContentType).call(this, options, contentType);
1356
1478
  }
1479
+ __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").delete(url);
1357
1480
  return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, 'POST', url, options);
1358
1481
  }
1359
1482
  /**
@@ -1376,6 +1499,7 @@ class MedplumClient extends EventTarget {
1376
1499
  if (contentType) {
1377
1500
  __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setRequestContentType).call(this, options, contentType);
1378
1501
  }
1502
+ __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").delete(url);
1379
1503
  return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, 'PUT', url, options);
1380
1504
  }
1381
1505
  /**
@@ -1393,6 +1517,7 @@ class MedplumClient extends EventTarget {
1393
1517
  patch(url, operations, options = {}) {
1394
1518
  __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setRequestBody).call(this, options, operations);
1395
1519
  __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setRequestContentType).call(this, options, PATCH_CONTENT_TYPE);
1520
+ __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").delete(url);
1396
1521
  return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, 'PATCH', url, options);
1397
1522
  }
1398
1523
  /**
@@ -1407,6 +1532,7 @@ class MedplumClient extends EventTarget {
1407
1532
  * @returns Promise to the response content.
1408
1533
  */
1409
1534
  delete(url, options = {}) {
1535
+ __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").delete(url);
1410
1536
  return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, 'DELETE', url, options);
1411
1537
  }
1412
1538
  /**
@@ -1626,11 +1752,8 @@ class MedplumClient extends EventTarget {
1626
1752
  * @returns The resource if it is available in the cache; undefined otherwise.
1627
1753
  */
1628
1754
  getCached(resourceType, id) {
1629
- const cached = __classPrivateFieldGet(this, _MedplumClient_resourceCache, "f").get(resourceType + '/' + id);
1630
- if (cached && !('then' in cached)) {
1631
- return cached;
1632
- }
1633
- return undefined;
1755
+ const cached = __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").get(this.fhirUrl(resourceType, id));
1756
+ return cached && !cached.isPending() ? cached.read() : undefined;
1634
1757
  }
1635
1758
  /**
1636
1759
  * Returns a cached resource if it is available.
@@ -1639,11 +1762,9 @@ class MedplumClient extends EventTarget {
1639
1762
  * @returns The resource if it is available in the cache; undefined otherwise.
1640
1763
  */
1641
1764
  getCachedReference(reference) {
1642
- const cached = __classPrivateFieldGet(this, _MedplumClient_resourceCache, "f").get(reference.reference);
1643
- if (cached && !('then' in cached)) {
1644
- return cached;
1645
- }
1646
- return undefined;
1765
+ const refString = reference.reference;
1766
+ const [resourceType, id] = refString.split('/');
1767
+ return this.getCached(resourceType, id);
1647
1768
  }
1648
1769
  /**
1649
1770
  * Reads a resource by resource type and ID.
@@ -1662,13 +1783,7 @@ class MedplumClient extends EventTarget {
1662
1783
  * @returns The resource if available; undefined otherwise.
1663
1784
  */
1664
1785
  readResource(resourceType, id) {
1665
- const cacheKey = resourceType + '/' + id;
1666
- const promise = this.get(this.fhirUrl(resourceType, id)).then((resource) => {
1667
- __classPrivateFieldGet(this, _MedplumClient_resourceCache, "f").set(cacheKey, resource);
1668
- return resource;
1669
- });
1670
- __classPrivateFieldGet(this, _MedplumClient_resourceCache, "f").set(cacheKey, promise);
1671
- return promise;
1786
+ return this.get(this.fhirUrl(resourceType, id));
1672
1787
  }
1673
1788
  /**
1674
1789
  * Reads a resource by resource type and ID using the in-memory resource cache.
@@ -1689,8 +1804,7 @@ class MedplumClient extends EventTarget {
1689
1804
  * @returns The resource if available; undefined otherwise.
1690
1805
  */
1691
1806
  readCached(resourceType, id) {
1692
- const cached = __classPrivateFieldGet(this, _MedplumClient_resourceCache, "f").get(resourceType + '/' + id);
1693
- return cached ? Promise.resolve(cached) : this.readResource(resourceType, id);
1807
+ return this.get(this.fhirUrl(resourceType, id));
1694
1808
  }
1695
1809
  /**
1696
1810
  * Reads a resource by `Reference`.
@@ -1713,7 +1827,7 @@ class MedplumClient extends EventTarget {
1713
1827
  readReference(reference) {
1714
1828
  const refString = reference === null || reference === void 0 ? void 0 : reference.reference;
1715
1829
  if (!refString) {
1716
- return Promise.reject('Missing reference');
1830
+ return new ReadablePromise(Promise.reject('Missing reference'));
1717
1831
  }
1718
1832
  const [resourceType, id] = refString.split('/');
1719
1833
  return this.readResource(resourceType, id);
@@ -1741,7 +1855,7 @@ class MedplumClient extends EventTarget {
1741
1855
  readCachedReference(reference) {
1742
1856
  const refString = reference === null || reference === void 0 ? void 0 : reference.reference;
1743
1857
  if (!refString) {
1744
- return Promise.reject('Missing reference');
1858
+ return new ReadablePromise(Promise.reject('Missing reference'));
1745
1859
  }
1746
1860
  const [resourceType, id] = refString.split('/');
1747
1861
  return this.readCached(resourceType, id);
@@ -1788,7 +1902,7 @@ class MedplumClient extends EventTarget {
1788
1902
  }
1789
1903
  }
1790
1904
  }
1791
- SearchParameterList(base: "${encodeURIComponent(resourceType)}") {
1905
+ SearchParameterList(base: "${encodeURIComponent(resourceType)}", _count: 100) {
1792
1906
  base,
1793
1907
  code,
1794
1908
  type,
@@ -1878,6 +1992,50 @@ class MedplumClient extends EventTarget {
1878
1992
  }
1879
1993
  return this.post(this.fhirUrl(resource.resourceType), resource);
1880
1994
  }
1995
+ /**
1996
+ * Conditionally create a new FHIR resource only if some equivalent resource does not already exist on the server.
1997
+ *
1998
+ * The return value is the existing resource or the newly created resource, including the ID and meta.
1999
+ *
2000
+ * Example:
2001
+ *
2002
+ * ```typescript
2003
+ * const result = await medplum.createResourceIfNoneExist(
2004
+ * 'Patient?identifier=123',
2005
+ * {
2006
+ * resourceType: 'Patient',
2007
+ * identifier: [{
2008
+ * system: 'http://example.com/mrn',
2009
+ * value: '123'
2010
+ * }]
2011
+ * name: [{
2012
+ * family: 'Smith',
2013
+ * given: ['John']
2014
+ * }]
2015
+ * });
2016
+ * console.log(result.id);
2017
+ * ```
2018
+ *
2019
+ * This method is syntactic sugar for:
2020
+ *
2021
+ * ```typescript
2022
+ * return searchOne(query) ?? createResource(resource);
2023
+ * ```
2024
+ *
2025
+ * The query parameter only contains the search parameters (what would be in the URL following the "?").
2026
+ *
2027
+ * See the FHIR "conditional create" operation for full details: https://www.hl7.org/fhir/http.html#ccreate
2028
+ *
2029
+ * @param resource The FHIR resource to create.
2030
+ * @param query The search query for an equivalent resource.
2031
+ * @returns The result of the create operation.
2032
+ */
2033
+ createResourceIfNoneExist(resource, query) {
2034
+ var _a;
2035
+ return __awaiter(this, void 0, void 0, function* () {
2036
+ return (_a = (yield this.searchOne(`${resource.resourceType}?${query}`))) !== null && _a !== void 0 ? _a : this.createResource(resource);
2037
+ });
2038
+ }
1881
2039
  /**
1882
2040
  * Creates a FHIR `Binary` resource with the provided data content.
1883
2041
  *
@@ -1896,11 +2054,45 @@ class MedplumClient extends EventTarget {
1896
2054
  *
1897
2055
  * See the FHIR "create" operation for full details: https://www.hl7.org/fhir/http.html#create
1898
2056
  *
1899
- * @param resource The FHIR resource to create.
2057
+ * @param data The binary data to upload.
2058
+ * @param filename Optional filename for the binary.
2059
+ * @param contentType Content type for the binary.
1900
2060
  * @returns The result of the create operation.
1901
2061
  */
1902
2062
  createBinary(data, filename, contentType) {
1903
- return this.post(this.fhirUrl('Binary') + '?_filename=' + encodeURIComponent(filename), data, contentType);
2063
+ let url = this.fhirUrl('Binary');
2064
+ if (filename) {
2065
+ url += '?_filename=' + encodeURIComponent(filename);
2066
+ }
2067
+ return this.post(url, data, contentType);
2068
+ }
2069
+ /**
2070
+ * Creates a PDF as a FHIR `Binary` resource based on pdfmake document definition.
2071
+ *
2072
+ * The return value is the newly created resource, including the ID and meta.
2073
+ *
2074
+ * The `docDefinition` parameter is a pdfmake document definition.
2075
+ *
2076
+ * Example:
2077
+ *
2078
+ * ```typescript
2079
+ * const result = await medplum.createPdf({
2080
+ * content: ['Hello world']
2081
+ * });
2082
+ * console.log(result.id);
2083
+ * ```
2084
+ *
2085
+ * See the pdfmake document definition for full details: https://pdfmake.github.io/docs/0.1/document-definition-object/
2086
+ *
2087
+ * @param docDefinition The FHIR resource to create.
2088
+ * @returns The result of the create operation.
2089
+ */
2090
+ createPdf(docDefinition, filename) {
2091
+ let url = this.fhirUrl('Binary') + '/$pdf';
2092
+ if (filename) {
2093
+ url += '?_filename=' + encodeURIComponent(filename);
2094
+ }
2095
+ return this.post(url, docDefinition, 'application/json');
1904
2096
  }
1905
2097
  /**
1906
2098
  * Updates a FHIR resource.
@@ -1993,7 +2185,7 @@ class MedplumClient extends EventTarget {
1993
2185
  __classPrivateFieldSet(this, _MedplumClient_config, undefined, "f");
1994
2186
  __classPrivateFieldGet(this, _MedplumClient_storage, "f").setObject('activeLogin', login);
1995
2187
  __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_addLogin).call(this, login);
1996
- __classPrivateFieldGet(this, _MedplumClient_resourceCache, "f").clear();
2188
+ __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").clear();
1997
2189
  __classPrivateFieldSet(this, _MedplumClient_refreshPromise, undefined, "f");
1998
2190
  yield __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_refreshProfile).call(this);
1999
2191
  });
@@ -2075,7 +2267,7 @@ class MedplumClient extends EventTarget {
2075
2267
  });
2076
2268
  }
2077
2269
  }
2078
- _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) {
2270
+ _MedplumClient_fetch = new WeakMap(), _MedplumClient_storage = new WeakMap(), _MedplumClient_schema = new WeakMap(), _MedplumClient_requestCache = 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) {
2079
2271
  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); });
2080
2272
  logins.push(newLogin);
2081
2273
  __classPrivateFieldGet(this, _MedplumClient_storage, "f").setObject('logins', logins);
@@ -2114,7 +2306,7 @@ _MedplumClient_fetch = new WeakMap(), _MedplumClient_storage = new WeakMap(), _M
2114
2306
  return undefined;
2115
2307
  }
2116
2308
  const obj = yield response.json();
2117
- if (obj.resourceType === 'OperationOutcome' && !isOk(obj)) {
2309
+ if ((obj === null || obj === void 0 ? void 0 : obj.resourceType) === 'OperationOutcome' && !isOk(obj)) {
2118
2310
  return Promise.reject(obj);
2119
2311
  }
2120
2312
  return obj;
@@ -2376,209 +2568,6 @@ class Hl7Field {
2376
2568
  }
2377
2569
  }
2378
2570
 
2379
- var _LegacyRepositoryClient_client;
2380
- /**
2381
- * The LegacyRepositoryClient is a supplementary API client that matches the legacy "Repository" API.
2382
- * The "Repository" API is deprecated and will be removed in a future release.
2383
- * This LegacyRepositoryClient is also deprecated and will be removed in a future release.
2384
- * @deprecated
2385
- */
2386
- class LegacyRepositoryClient {
2387
- constructor(client) {
2388
- _LegacyRepositoryClient_client.set(this, void 0);
2389
- __classPrivateFieldSet(this, _LegacyRepositoryClient_client, client, "f");
2390
- }
2391
- /**
2392
- * Creates a resource.
2393
- *
2394
- * See: https://www.hl7.org/fhir/http.html#create
2395
- *
2396
- * @param resource The resource to create.
2397
- * @returns Operation outcome and the new resource.
2398
- * @deprecated
2399
- */
2400
- createResource(resource) {
2401
- return __awaiter(this, void 0, void 0, function* () {
2402
- try {
2403
- const result = yield __classPrivateFieldGet(this, _LegacyRepositoryClient_client, "f").createResource(resource);
2404
- return [created, result];
2405
- }
2406
- catch (error) {
2407
- return [error, undefined];
2408
- }
2409
- });
2410
- }
2411
- /**
2412
- * Returns a resource.
2413
- *
2414
- * See: https://www.hl7.org/fhir/http.html#read
2415
- *
2416
- * @param resourceType The FHIR resource type.
2417
- * @param id The FHIR resource ID.
2418
- * @returns Operation outcome and a resource.
2419
- * @deprecated
2420
- */
2421
- readResource(resourceType, id) {
2422
- return __awaiter(this, void 0, void 0, function* () {
2423
- try {
2424
- const resource = yield __classPrivateFieldGet(this, _LegacyRepositoryClient_client, "f").readResource(resourceType, id);
2425
- return [allOk, resource];
2426
- }
2427
- catch (error) {
2428
- return [error, undefined];
2429
- }
2430
- });
2431
- }
2432
- /**
2433
- * Returns a resource by FHIR reference.
2434
- *
2435
- * See: https://www.hl7.org/fhir/http.html#read
2436
- *
2437
- * @param reference The FHIR reference.
2438
- * @returns Operation outcome and a resource.
2439
- * @deprecated
2440
- */
2441
- readReference(reference) {
2442
- return __awaiter(this, void 0, void 0, function* () {
2443
- try {
2444
- const resource = yield __classPrivateFieldGet(this, _LegacyRepositoryClient_client, "f").readReference(reference);
2445
- return [allOk, resource];
2446
- }
2447
- catch (error) {
2448
- return [error, undefined];
2449
- }
2450
- });
2451
- }
2452
- /**
2453
- * Returns resource history.
2454
- *
2455
- * See: https://www.hl7.org/fhir/http.html#history
2456
- *
2457
- * @param resourceType The FHIR resource type.
2458
- * @param id The FHIR resource ID.
2459
- * @returns Operation outcome and a history bundle.
2460
- * @deprecated
2461
- */
2462
- readHistory(resourceType, id) {
2463
- return __awaiter(this, void 0, void 0, function* () {
2464
- try {
2465
- const resource = yield __classPrivateFieldGet(this, _LegacyRepositoryClient_client, "f").readHistory(resourceType, id);
2466
- return [allOk, resource];
2467
- }
2468
- catch (error) {
2469
- return [error, undefined];
2470
- }
2471
- });
2472
- }
2473
- /**
2474
- * Returns a resource version.
2475
- *
2476
- * See: https://www.hl7.org/fhir/http.html#vread
2477
- *
2478
- * @param resourceType The FHIR resource type.
2479
- * @param id The FHIR resource ID.
2480
- * @param vid The version ID.
2481
- * @returns Operation outcome and a resource.
2482
- * @deprecated
2483
- */
2484
- readVersion(resourceType, id, vid) {
2485
- return __awaiter(this, void 0, void 0, function* () {
2486
- try {
2487
- const resource = yield __classPrivateFieldGet(this, _LegacyRepositoryClient_client, "f").readVersion(resourceType, id, vid);
2488
- return [allOk, resource];
2489
- }
2490
- catch (error) {
2491
- return [error, undefined];
2492
- }
2493
- });
2494
- }
2495
- /**
2496
- * Updates a resource.
2497
- *
2498
- * See: https://www.hl7.org/fhir/http.html#update
2499
- *
2500
- * @param resource The resource to update.
2501
- * @returns Operation outcome and the updated resource.
2502
- * @deprecated
2503
- */
2504
- updateResource(resource) {
2505
- return __awaiter(this, void 0, void 0, function* () {
2506
- try {
2507
- const updated = yield __classPrivateFieldGet(this, _LegacyRepositoryClient_client, "f").updateResource(resource);
2508
- return [allOk, updated];
2509
- }
2510
- catch (error) {
2511
- return [error, undefined];
2512
- }
2513
- });
2514
- }
2515
- /**
2516
- * Deletes a resource.
2517
- *
2518
- * See: https://www.hl7.org/fhir/http.html#delete
2519
- *
2520
- * @param resourceType The FHIR resource type.
2521
- * @param id The resource ID.
2522
- * @returns Operation outcome.
2523
- * @deprecated
2524
- */
2525
- deleteResource(resourceType, id) {
2526
- return __awaiter(this, void 0, void 0, function* () {
2527
- try {
2528
- yield __classPrivateFieldGet(this, _LegacyRepositoryClient_client, "f").deleteResource(resourceType, id);
2529
- return [allOk, undefined];
2530
- }
2531
- catch (error) {
2532
- return [error, undefined];
2533
- }
2534
- });
2535
- }
2536
- /**
2537
- * Patches a resource.
2538
- *
2539
- * See: https://www.hl7.org/fhir/http.html#patch
2540
- *
2541
- * @param resourceType The FHIR resource type.
2542
- * @param id The resource ID.
2543
- * @param patch Array of JSONPatch operations.
2544
- * @returns Operation outcome and the resource.
2545
- * @deprecated
2546
- */
2547
- patchResource(resourceType, id, patch) {
2548
- return __awaiter(this, void 0, void 0, function* () {
2549
- try {
2550
- const resource = yield __classPrivateFieldGet(this, _LegacyRepositoryClient_client, "f").patchResource(resourceType, id, patch);
2551
- return [allOk, resource];
2552
- }
2553
- catch (error) {
2554
- return [error, undefined];
2555
- }
2556
- });
2557
- }
2558
- /**
2559
- * Searches for resources.
2560
- *
2561
- * See: https://www.hl7.org/fhir/http.html#search
2562
- *
2563
- * @param searchRequest The search request.
2564
- * @returns The search result bundle.
2565
- * @deprecated
2566
- */
2567
- search(query) {
2568
- return __awaiter(this, void 0, void 0, function* () {
2569
- const searchRequest = typeof query === 'string' ? parseSearchDefinition(query) : query;
2570
- try {
2571
- const bundle = yield __classPrivateFieldGet(this, _LegacyRepositoryClient_client, "f").search(searchRequest);
2572
- return [allOk, bundle];
2573
- }
2574
- catch (error) {
2575
- return [error, undefined];
2576
- }
2577
- });
2578
- }
2579
- }
2580
- _LegacyRepositoryClient_client = new WeakMap();
2581
-
2582
2571
  var SearchParameterType;
2583
2572
  (function (SearchParameterType) {
2584
2573
  SearchParameterType["BOOLEAN"] = "BOOLEAN";
@@ -2705,5 +2694,5 @@ function simplifyExpression(input) {
2705
2694
  return result;
2706
2695
  }
2707
2696
 
2708
- export { COMPONENT_SEPARATOR, FIELD_SEPARATOR, Hl7Field, Hl7Message, Hl7Segment, LegacyRepositoryClient, MedplumClient, OperationOutcomeError, Operator, PropertyType, SEGMENT_SEPARATOR, SearchParameterType, accessDenied, allOk, arrayBufferToBase64, arrayBufferToHex, assertOk, badRequest, buildTypeName, calculateAge, calculateAgeString, capitalize, createReference, createSchema, createTypeSchema, created, deepEquals, formatAddress, formatFamilyName, formatGivenName, formatHumanName, formatSearchQuery, getDateProperty, getDisplayString, getExpressionForResourceType, getExtensionValue, getImageSrc, getPropertyDisplayName, getQuestionnaireAnswers, getReferenceString, getSearchParameterDetails, getStatus, gone, indexSearchParameter, indexStructureDefinition, isGone, isLowerCase, isNotFound, isObject, isOk, isProfileResource, isStringArray, notFound, notModified, parseSearchDefinition, resolveId, stringify };
2697
+ export { COMPONENT_SEPARATOR, DEFAULT_SEARCH_COUNT, FIELD_SEPARATOR, Hl7Field, Hl7Message, Hl7Segment, LRUCache, MedplumClient, OperationOutcomeError, Operator, PropertyType, ReadablePromise, SEGMENT_SEPARATOR, SearchParameterType, accessDenied, allOk, arrayBufferToBase64, arrayBufferToHex, assertOk, badRequest, buildTypeName, calculateAge, calculateAgeString, capitalize, createReference, createSchema, createTypeSchema, created, deepEquals, formatAddress, formatFamilyName, formatGivenName, formatHumanName, formatSearchQuery, getDateProperty, getDisplayString, getExpressionForResourceType, getExtensionValue, getImageSrc, getPropertyDisplayName, getQuestionnaireAnswers, getReferenceString, getSearchParameterDetails, getStatus, gone, indexSearchParameter, indexStructureDefinition, isGone, isLowerCase, isNotFound, isObject, isOk, isProfileResource, isStringArray, isUUID, notFound, notModified, parseSearchDefinition, resolveId, stringify };
2709
2698
  //# sourceMappingURL=index.js.map