@medplum/core 0.9.10 → 0.9.11

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
@@ -804,106 +804,6 @@ class OperationOutcomeError extends Error {
804
804
  }
805
805
  }
806
806
 
807
- /*
808
- * This file attempts a unified "generatePdf" function that works both client-side and server-side.
809
- * On client-side, it checks for a global "pdfMake" variable.
810
- * On server-side, it dynamically loads "pdfmake" from the node_modules.
811
- */
812
- function generatePdf(docDefinition, tableLayouts, fonts) {
813
- return __awaiter(this, void 0, void 0, function* () {
814
- // Setup sane defaults
815
- // See: https://pdfmake.github.io/docs/0.1/document-definition-object/styling/
816
- docDefinition.pageSize = docDefinition.pageSize || 'LETTER';
817
- docDefinition.pageMargins = docDefinition.pageMargins || [60, 60, 60, 60];
818
- docDefinition.pageOrientation = docDefinition.pageOrientation || 'portrait';
819
- docDefinition.defaultStyle = docDefinition.defaultStyle || {};
820
- docDefinition.defaultStyle.font = docDefinition.defaultStyle.font || 'Helvetica';
821
- docDefinition.defaultStyle.fontSize = docDefinition.defaultStyle.fontSize || 11;
822
- docDefinition.defaultStyle.lineHeight = docDefinition.defaultStyle.lineHeight || 2.0;
823
- if (typeof window !== 'undefined' && typeof pdfMake !== 'undefined') {
824
- return generatePdfClientSide(docDefinition, tableLayouts, fonts);
825
- }
826
- if (typeof process !== 'undefined' && typeof require !== 'undefined') {
827
- return generatePdfServerSide(docDefinition, tableLayouts, fonts);
828
- }
829
- throw new Error('Unable to determine PDF environment');
830
- });
831
- }
832
- function generatePdfServerSide(docDefinition, tableLayouts, fonts) {
833
- return __awaiter(this, void 0, void 0, function* () {
834
- if (!fonts) {
835
- fonts = {
836
- Helvetica: {
837
- normal: 'Helvetica',
838
- bold: 'Helvetica-Bold',
839
- italics: 'Helvetica-Oblique',
840
- bolditalics: 'Helvetica-BoldOblique',
841
- },
842
- };
843
- }
844
- return new Promise((resolve, reject) => {
845
- try {
846
- // eslint-disable-next-line @typescript-eslint/no-var-requires
847
- const PdfPrinter = require('pdfmake');
848
- const printer = new PdfPrinter(fonts);
849
- const pdfDoc = printer.createPdfKitDocument(docDefinition, { tableLayouts });
850
- const chunks = [];
851
- pdfDoc.on('data', (chunk) => chunks.push(chunk));
852
- pdfDoc.on('end', () => resolve(concat(chunks)));
853
- pdfDoc.on('error', reject);
854
- pdfDoc.end();
855
- }
856
- catch (err) {
857
- reject(err);
858
- }
859
- });
860
- });
861
- }
862
- function generatePdfClientSide(docDefinition, tableLayouts, fonts) {
863
- return __awaiter(this, void 0, void 0, function* () {
864
- if (!fonts) {
865
- fonts = {
866
- Helvetica: {
867
- normal: 'https://static.medplum.com/fonts/Helvetica.ttf',
868
- bold: 'https://static.medplum.com/fonts/Helvetica-bold.ttf',
869
- italics: 'https://static.medplum.com/fonts/Helvetica-italic.ttf',
870
- bolditalics: 'https://static.medplum.com/fonts/Helvetica-bold-italic.ttf',
871
- },
872
- Roboto: {
873
- normal: 'https://static.medplum.com/fonts/Roboto-Regular.ttf',
874
- bold: 'https://static.medplum.com/fonts/Roboto-Medium.ttf',
875
- italics: 'https://static.medplum.com/fonts/Roboto-Italic.ttf',
876
- bolditalics: 'https://static.medplum.com/fonts/Roboto-MediumItalic.ttf',
877
- },
878
- Avenir: {
879
- normal: 'https://static.medplum.com/fonts/avenir.ttf',
880
- },
881
- };
882
- }
883
- return new Promise((resolve) => {
884
- pdfMake.createPdf(docDefinition, tableLayouts, fonts).getBlob(resolve);
885
- });
886
- });
887
- }
888
- /**
889
- * Concatenates an array of Uint8Arrays into a single Uint8Array.
890
- * @param arrays An array of arrays of bytes.
891
- * @returns A single array of bytes.
892
- */
893
- function concat(arrays) {
894
- let len = 0;
895
- for (const array of arrays) {
896
- len += array.length;
897
- }
898
- const result = new Uint8Array(len);
899
- let index = 0;
900
- for (const array of arrays) {
901
- result.set(array, index);
902
- index += array.length;
903
- }
904
- return result;
905
- }
906
-
907
807
  var _ReadablePromise_suspender, _ReadablePromise_status, _ReadablePromise_response, _ReadablePromise_error, _a;
908
808
  /**
909
809
  * The ReadablePromise class wraps a request promise suitable for React Suspense.
@@ -980,206 +880,6 @@ class ReadablePromise {
980
880
  }
981
881
  _ReadablePromise_suspender = new WeakMap(), _ReadablePromise_status = new WeakMap(), _ReadablePromise_response = new WeakMap(), _ReadablePromise_error = new WeakMap(), _a = Symbol.toStringTag;
982
882
 
983
- const DEFAULT_SEARCH_COUNT = 20;
984
- /**
985
- * Search operators.
986
- * These operators represent "modifiers" and "prefixes" in FHIR search.
987
- * See: https://www.hl7.org/fhir/search.html
988
- */
989
- var Operator;
990
- (function (Operator) {
991
- Operator["EQUALS"] = "eq";
992
- Operator["NOT_EQUALS"] = "ne";
993
- // Numbers
994
- Operator["GREATER_THAN"] = "gt";
995
- Operator["LESS_THAN"] = "lt";
996
- Operator["GREATER_THAN_OR_EQUALS"] = "ge";
997
- Operator["LESS_THAN_OR_EQUALS"] = "le";
998
- // Dates
999
- Operator["STARTS_AFTER"] = "sa";
1000
- Operator["ENDS_BEFORE"] = "eb";
1001
- Operator["APPROXIMATELY"] = "ap";
1002
- // String
1003
- Operator["CONTAINS"] = "contains";
1004
- Operator["EXACT"] = "exact";
1005
- // Token
1006
- Operator["TEXT"] = "text";
1007
- Operator["ABOVE"] = "above";
1008
- Operator["BELOW"] = "below";
1009
- Operator["IN"] = "in";
1010
- Operator["NOT_IN"] = "not-in";
1011
- Operator["OF_TYPE"] = "of-type";
1012
- })(Operator || (Operator = {}));
1013
- const MODIFIER_OPERATORS = [
1014
- Operator.CONTAINS,
1015
- Operator.EXACT,
1016
- Operator.TEXT,
1017
- Operator.ABOVE,
1018
- Operator.BELOW,
1019
- Operator.IN,
1020
- Operator.NOT_IN,
1021
- Operator.OF_TYPE,
1022
- ];
1023
- const PREFIX_OPERATORS = [
1024
- Operator.NOT_EQUALS,
1025
- Operator.GREATER_THAN,
1026
- Operator.LESS_THAN,
1027
- Operator.GREATER_THAN_OR_EQUALS,
1028
- Operator.LESS_THAN_OR_EQUALS,
1029
- Operator.STARTS_AFTER,
1030
- Operator.ENDS_BEFORE,
1031
- Operator.APPROXIMATELY,
1032
- ];
1033
- /**
1034
- * Parses a URL into a SearchRequest.
1035
- *
1036
- * See the FHIR search spec: http://hl7.org/fhir/r4/search.html
1037
- *
1038
- * @param url The URL to parse.
1039
- * @returns Parsed search definition.
1040
- */
1041
- function parseSearchDefinition(url) {
1042
- const location = new URL(url, 'https://example.com/');
1043
- const resourceType = location.pathname
1044
- .replace(/(^\/)|(\/$)/g, '') // Remove leading and trailing slashes
1045
- .split('/')
1046
- .pop();
1047
- const params = new URLSearchParams(location.search);
1048
- let filters = undefined;
1049
- let sortRules = undefined;
1050
- let fields = undefined;
1051
- let offset = undefined;
1052
- let count = undefined;
1053
- let total = undefined;
1054
- params.forEach((value, key) => {
1055
- if (key === '_fields') {
1056
- fields = value.split(',');
1057
- }
1058
- else if (key === '_offset') {
1059
- offset = parseInt(value);
1060
- }
1061
- else if (key === '_count') {
1062
- count = parseInt(value);
1063
- }
1064
- else if (key === '_total') {
1065
- total = value;
1066
- }
1067
- else if (key === '_sort') {
1068
- sortRules = sortRules || [];
1069
- sortRules.push(parseSortRule(value));
1070
- }
1071
- else {
1072
- filters = filters || [];
1073
- filters.push(parseSearchFilter(key, value));
1074
- }
1075
- });
1076
- return {
1077
- resourceType,
1078
- filters,
1079
- fields,
1080
- offset,
1081
- count,
1082
- total,
1083
- sortRules,
1084
- };
1085
- }
1086
- /**
1087
- * Parses a URL query parameter into a sort rule.
1088
- *
1089
- * By default, the sort rule is the field name.
1090
- *
1091
- * Sort rules can be reversed into descending order by prefixing the field name with a minus sign.
1092
- *
1093
- * See sorting: http://hl7.org/fhir/r4/search.html#_sort
1094
- *
1095
- * @param value The URL parameter value.
1096
- * @returns The parsed sort rule.
1097
- */
1098
- function parseSortRule(value) {
1099
- if (value.startsWith('-')) {
1100
- return { code: value.substring(1), descending: true };
1101
- }
1102
- else {
1103
- return { code: value };
1104
- }
1105
- }
1106
- /**
1107
- * Parses a URL query parameter into a search filter.
1108
- *
1109
- * FHIR search filters can be specified as modifiers or prefixes.
1110
- *
1111
- * For string properties, modifiers are appended to the key, e.g. "name:contains=eve".
1112
- *
1113
- * For date and numeric properties, prefixes are prepended to the value, e.g. "birthdate=gt2000".
1114
- *
1115
- * See the FHIR search spec: http://hl7.org/fhir/r4/search.html
1116
- *
1117
- * @param key The URL parameter key.
1118
- * @param value The URL parameter value.
1119
- * @returns The parsed search filter.
1120
- */
1121
- function parseSearchFilter(key, value) {
1122
- let code = key;
1123
- let operator = Operator.EQUALS;
1124
- for (const modifier of MODIFIER_OPERATORS) {
1125
- const modifierIndex = code.indexOf(':' + modifier);
1126
- if (modifierIndex !== -1) {
1127
- operator = modifier;
1128
- code = code.substring(0, modifierIndex);
1129
- }
1130
- }
1131
- for (const prefix of PREFIX_OPERATORS) {
1132
- if (value.match(new RegExp('^' + prefix + '\\d'))) {
1133
- operator = prefix;
1134
- value = value.substring(prefix.length);
1135
- }
1136
- }
1137
- return { code, operator, value };
1138
- }
1139
- /**
1140
- * Formats a search definition object into a query string.
1141
- * Note: The return value does not include the resource type.
1142
- * @param {!SearchRequest} definition The search definition.
1143
- * @returns Formatted URL.
1144
- */
1145
- function formatSearchQuery(definition) {
1146
- const params = [];
1147
- if (definition.fields) {
1148
- params.push('_fields=' + definition.fields.join(','));
1149
- }
1150
- if (definition.filters) {
1151
- definition.filters.forEach((filter) => params.push(formatFilter(filter)));
1152
- }
1153
- if (definition.sortRules && definition.sortRules.length > 0) {
1154
- params.push(formatSortRules(definition.sortRules));
1155
- }
1156
- if (definition.offset !== undefined) {
1157
- params.push('_offset=' + definition.offset);
1158
- }
1159
- if (definition.count !== undefined) {
1160
- params.push('_count=' + definition.count);
1161
- }
1162
- if (definition.total !== undefined) {
1163
- params.push('_total=' + definition.total);
1164
- }
1165
- if (params.length === 0) {
1166
- return '';
1167
- }
1168
- params.sort();
1169
- return '?' + params.join('&');
1170
- }
1171
- function formatFilter(filter) {
1172
- const modifier = MODIFIER_OPERATORS.includes(filter.operator) ? ':' + filter.operator : '';
1173
- const prefix = PREFIX_OPERATORS.includes(filter.operator) ? filter.operator : '';
1174
- return `${filter.code}${modifier}=${prefix}${encodeURIComponent(filter.value)}`;
1175
- }
1176
- function formatSortRules(sortRules) {
1177
- if (!sortRules || sortRules.length === 0) {
1178
- return '';
1179
- }
1180
- return '_sort=' + sortRules.map((sr) => (sr.descending ? '-' + sr.code : sr.code)).join(',');
1181
- }
1182
-
1183
883
  var _ClientStorage_storage, _MemoryStorage_data;
1184
884
  /**
1185
885
  * The ClientStorage class is a utility class for storing strings and objects.
@@ -1468,7 +1168,7 @@ function getPropertyDisplayName(property) {
1468
1168
 
1469
1169
  // PKCE auth ased on:
1470
1170
  // https://aws.amazon.com/blogs/security/how-to-add-authentication-single-page-web-application-with-amazon-cognito-oauth2-implementation/
1471
- 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;
1171
+ var _MedplumClient_instances, _MedplumClient_fetch, _MedplumClient_createPdf, _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;
1472
1172
  const DEFAULT_BASE_URL = 'https://api.medplum.com/';
1473
1173
  const DEFAULT_SCOPE = 'launch/patient openid fhirUser offline_access user/*.*';
1474
1174
  const DEFAULT_RESOURCE_CACHE_SIZE = 1000;
@@ -1528,6 +1228,7 @@ class MedplumClient extends EventTarget {
1528
1228
  super();
1529
1229
  _MedplumClient_instances.add(this);
1530
1230
  _MedplumClient_fetch.set(this, void 0);
1231
+ _MedplumClient_createPdf.set(this, void 0);
1531
1232
  _MedplumClient_storage.set(this, void 0);
1532
1233
  _MedplumClient_schema.set(this, void 0);
1533
1234
  _MedplumClient_requestCache.set(this, void 0);
@@ -1552,6 +1253,7 @@ class MedplumClient extends EventTarget {
1552
1253
  }
1553
1254
  }
1554
1255
  __classPrivateFieldSet(this, _MedplumClient_fetch, (options === null || options === void 0 ? void 0 : options.fetch) || window.fetch.bind(window), "f");
1256
+ __classPrivateFieldSet(this, _MedplumClient_createPdf, options === null || options === void 0 ? void 0 : options.createPdf, "f");
1555
1257
  __classPrivateFieldSet(this, _MedplumClient_storage, new ClientStorage(), "f");
1556
1258
  __classPrivateFieldSet(this, _MedplumClient_schema, createSchema(), "f");
1557
1259
  __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");
@@ -1796,12 +1498,9 @@ class MedplumClient extends EventTarget {
1796
1498
  * @param query The FHIR search query or structured query object.
1797
1499
  * @returns The well-formed FHIR URL.
1798
1500
  */
1799
- fhirSearchUrl(query) {
1800
- if (typeof query === 'string') {
1801
- return this.fhirUrl(query);
1802
- }
1803
- const url = this.fhirUrl(query.resourceType);
1804
- url.search = formatSearchQuery(query);
1501
+ fhirSearchUrl(resourceType, query) {
1502
+ const url = this.fhirUrl(resourceType);
1503
+ url.search = query.toString();
1805
1504
  return url;
1806
1505
  }
1807
1506
  /**
@@ -1858,8 +1557,8 @@ class MedplumClient extends EventTarget {
1858
1557
  * @param query The search query as either a string or a structured search object.
1859
1558
  * @returns Promise to the search result bundle.
1860
1559
  */
1861
- search(query, options = {}) {
1862
- return this.get(this.fhirSearchUrl(query), options);
1560
+ search(resourceType, query, options = {}) {
1561
+ return this.get(this.fhirSearchUrl(resourceType, query), options);
1863
1562
  }
1864
1563
  /**
1865
1564
  * Sends a FHIR search request for a single resource.
@@ -1880,17 +1579,18 @@ class MedplumClient extends EventTarget {
1880
1579
  * @param query The search query as either a string or a structured search object.
1881
1580
  * @returns Promise to the search result bundle.
1882
1581
  */
1883
- searchOne(query, options = {}) {
1884
- const search = typeof query === 'string' ? parseSearchDefinition(query) : query;
1885
- search.count = 1;
1886
- const cacheKey = this.fhirSearchUrl(query).toString() + '-searchOne';
1582
+ searchOne(resourceType, query, options = {}) {
1583
+ const url = this.fhirSearchUrl(resourceType, query);
1584
+ url.searchParams.set('_count', '1');
1585
+ url.searchParams.sort();
1586
+ const cacheKey = url.toString() + '-searchOne';
1887
1587
  if (!(options === null || options === void 0 ? void 0 : options.cache)) {
1888
1588
  const cached = __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").get(cacheKey);
1889
1589
  if (cached) {
1890
1590
  return cached;
1891
1591
  }
1892
1592
  }
1893
- const promise = new ReadablePromise(this.search(search, options).then((b) => { var _a, _b; return (_b = (_a = b.entry) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.resource; }));
1593
+ const promise = new ReadablePromise(this.search(resourceType, url.searchParams, options).then((b) => { var _a, _b; return (_b = (_a = b.entry) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.resource; }));
1894
1594
  __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").set(cacheKey, promise);
1895
1595
  return promise;
1896
1596
  }
@@ -1913,15 +1613,16 @@ class MedplumClient extends EventTarget {
1913
1613
  * @param query The search query as either a string or a structured search object.
1914
1614
  * @returns Promise to the search result bundle.
1915
1615
  */
1916
- searchResources(query, options = {}) {
1917
- const cacheKey = this.fhirSearchUrl(query).toString() + '-searchResources';
1616
+ searchResources(resourceType, query, options = {}) {
1617
+ const url = this.fhirSearchUrl(resourceType, query);
1618
+ const cacheKey = url.toString() + '-searchResources';
1918
1619
  if (!(options === null || options === void 0 ? void 0 : options.cache)) {
1919
1620
  const cached = __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").get(cacheKey);
1920
1621
  if (cached) {
1921
1622
  return cached;
1922
1623
  }
1923
1624
  }
1924
- const promise = new ReadablePromise(this.search(query, options).then((b) => { var _a, _b; return (_b = (_a = b.entry) === null || _a === void 0 ? void 0 : _a.map((e) => e.resource)) !== null && _b !== void 0 ? _b : []; }));
1625
+ const promise = new ReadablePromise(this.search(resourceType, query, options).then((b) => { var _a, _b; return (_b = (_a = b.entry) === null || _a === void 0 ? void 0 : _a.map((e) => e.resource)) !== null && _b !== void 0 ? _b : []; }));
1925
1626
  __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").set(cacheKey, promise);
1926
1627
  return promise;
1927
1628
  }
@@ -2178,7 +1879,7 @@ class MedplumClient extends EventTarget {
2178
1879
  createResourceIfNoneExist(resource, query) {
2179
1880
  var _a;
2180
1881
  return __awaiter(this, void 0, void 0, function* () {
2181
- return (_a = (yield this.searchOne(`${resource.resourceType}?${query}`))) !== null && _a !== void 0 ? _a : this.createResource(resource);
1882
+ return ((_a = (yield this.searchOne(resource.resourceType, query))) !== null && _a !== void 0 ? _a : this.createResource(resource));
2182
1883
  });
2183
1884
  }
2184
1885
  /**
@@ -2234,7 +1935,10 @@ class MedplumClient extends EventTarget {
2234
1935
  */
2235
1936
  createPdf(docDefinition, filename, tableLayouts, fonts) {
2236
1937
  return __awaiter(this, void 0, void 0, function* () {
2237
- const blob = yield generatePdf(docDefinition, tableLayouts, fonts);
1938
+ if (!__classPrivateFieldGet(this, _MedplumClient_createPdf, "f")) {
1939
+ throw new Error('PDF creation not enabled');
1940
+ }
1941
+ const blob = yield __classPrivateFieldGet(this, _MedplumClient_createPdf, "f").call(this, docDefinition, tableLayouts, fonts);
2238
1942
  return this.createBinary(blob, filename, 'application/pdf');
2239
1943
  });
2240
1944
  }
@@ -2497,7 +2201,7 @@ class MedplumClient extends EventTarget {
2497
2201
  });
2498
2202
  }
2499
2203
  }
2500
- _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) {
2204
+ _MedplumClient_fetch = new WeakMap(), _MedplumClient_createPdf = 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) {
2501
2205
  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); });
2502
2206
  logins.push(newLogin);
2503
2207
  __classPrivateFieldGet(this, _MedplumClient_storage, "f").setObject('logins', logins);
@@ -5326,6 +5030,206 @@ class Hl7Field {
5326
5030
  }
5327
5031
  }
5328
5032
 
5033
+ const DEFAULT_SEARCH_COUNT = 20;
5034
+ /**
5035
+ * Search operators.
5036
+ * These operators represent "modifiers" and "prefixes" in FHIR search.
5037
+ * See: https://www.hl7.org/fhir/search.html
5038
+ */
5039
+ var Operator;
5040
+ (function (Operator) {
5041
+ Operator["EQUALS"] = "eq";
5042
+ Operator["NOT_EQUALS"] = "ne";
5043
+ // Numbers
5044
+ Operator["GREATER_THAN"] = "gt";
5045
+ Operator["LESS_THAN"] = "lt";
5046
+ Operator["GREATER_THAN_OR_EQUALS"] = "ge";
5047
+ Operator["LESS_THAN_OR_EQUALS"] = "le";
5048
+ // Dates
5049
+ Operator["STARTS_AFTER"] = "sa";
5050
+ Operator["ENDS_BEFORE"] = "eb";
5051
+ Operator["APPROXIMATELY"] = "ap";
5052
+ // String
5053
+ Operator["CONTAINS"] = "contains";
5054
+ Operator["EXACT"] = "exact";
5055
+ // Token
5056
+ Operator["TEXT"] = "text";
5057
+ Operator["ABOVE"] = "above";
5058
+ Operator["BELOW"] = "below";
5059
+ Operator["IN"] = "in";
5060
+ Operator["NOT_IN"] = "not-in";
5061
+ Operator["OF_TYPE"] = "of-type";
5062
+ })(Operator || (Operator = {}));
5063
+ const MODIFIER_OPERATORS = [
5064
+ Operator.CONTAINS,
5065
+ Operator.EXACT,
5066
+ Operator.TEXT,
5067
+ Operator.ABOVE,
5068
+ Operator.BELOW,
5069
+ Operator.IN,
5070
+ Operator.NOT_IN,
5071
+ Operator.OF_TYPE,
5072
+ ];
5073
+ const PREFIX_OPERATORS = [
5074
+ Operator.NOT_EQUALS,
5075
+ Operator.GREATER_THAN,
5076
+ Operator.LESS_THAN,
5077
+ Operator.GREATER_THAN_OR_EQUALS,
5078
+ Operator.LESS_THAN_OR_EQUALS,
5079
+ Operator.STARTS_AFTER,
5080
+ Operator.ENDS_BEFORE,
5081
+ Operator.APPROXIMATELY,
5082
+ ];
5083
+ /**
5084
+ * Parses a URL into a SearchRequest.
5085
+ *
5086
+ * See the FHIR search spec: http://hl7.org/fhir/r4/search.html
5087
+ *
5088
+ * @param url The URL to parse.
5089
+ * @returns Parsed search definition.
5090
+ */
5091
+ function parseSearchDefinition(url) {
5092
+ const location = new URL(url, 'https://example.com/');
5093
+ const resourceType = location.pathname
5094
+ .replace(/(^\/)|(\/$)/g, '') // Remove leading and trailing slashes
5095
+ .split('/')
5096
+ .pop();
5097
+ const params = new URLSearchParams(location.search);
5098
+ let filters = undefined;
5099
+ let sortRules = undefined;
5100
+ let fields = undefined;
5101
+ let offset = undefined;
5102
+ let count = undefined;
5103
+ let total = undefined;
5104
+ params.forEach((value, key) => {
5105
+ if (key === '_fields') {
5106
+ fields = value.split(',');
5107
+ }
5108
+ else if (key === '_offset') {
5109
+ offset = parseInt(value);
5110
+ }
5111
+ else if (key === '_count') {
5112
+ count = parseInt(value);
5113
+ }
5114
+ else if (key === '_total') {
5115
+ total = value;
5116
+ }
5117
+ else if (key === '_sort') {
5118
+ sortRules = sortRules || [];
5119
+ sortRules.push(parseSortRule(value));
5120
+ }
5121
+ else {
5122
+ filters = filters || [];
5123
+ filters.push(parseSearchFilter(key, value));
5124
+ }
5125
+ });
5126
+ return {
5127
+ resourceType,
5128
+ filters,
5129
+ fields,
5130
+ offset,
5131
+ count,
5132
+ total,
5133
+ sortRules,
5134
+ };
5135
+ }
5136
+ /**
5137
+ * Parses a URL query parameter into a sort rule.
5138
+ *
5139
+ * By default, the sort rule is the field name.
5140
+ *
5141
+ * Sort rules can be reversed into descending order by prefixing the field name with a minus sign.
5142
+ *
5143
+ * See sorting: http://hl7.org/fhir/r4/search.html#_sort
5144
+ *
5145
+ * @param value The URL parameter value.
5146
+ * @returns The parsed sort rule.
5147
+ */
5148
+ function parseSortRule(value) {
5149
+ if (value.startsWith('-')) {
5150
+ return { code: value.substring(1), descending: true };
5151
+ }
5152
+ else {
5153
+ return { code: value };
5154
+ }
5155
+ }
5156
+ /**
5157
+ * Parses a URL query parameter into a search filter.
5158
+ *
5159
+ * FHIR search filters can be specified as modifiers or prefixes.
5160
+ *
5161
+ * For string properties, modifiers are appended to the key, e.g. "name:contains=eve".
5162
+ *
5163
+ * For date and numeric properties, prefixes are prepended to the value, e.g. "birthdate=gt2000".
5164
+ *
5165
+ * See the FHIR search spec: http://hl7.org/fhir/r4/search.html
5166
+ *
5167
+ * @param key The URL parameter key.
5168
+ * @param value The URL parameter value.
5169
+ * @returns The parsed search filter.
5170
+ */
5171
+ function parseSearchFilter(key, value) {
5172
+ let code = key;
5173
+ let operator = Operator.EQUALS;
5174
+ for (const modifier of MODIFIER_OPERATORS) {
5175
+ const modifierIndex = code.indexOf(':' + modifier);
5176
+ if (modifierIndex !== -1) {
5177
+ operator = modifier;
5178
+ code = code.substring(0, modifierIndex);
5179
+ }
5180
+ }
5181
+ for (const prefix of PREFIX_OPERATORS) {
5182
+ if (value.match(new RegExp('^' + prefix + '\\d'))) {
5183
+ operator = prefix;
5184
+ value = value.substring(prefix.length);
5185
+ }
5186
+ }
5187
+ return { code, operator, value };
5188
+ }
5189
+ /**
5190
+ * Formats a search definition object into a query string.
5191
+ * Note: The return value does not include the resource type.
5192
+ * @param {!SearchRequest} definition The search definition.
5193
+ * @returns Formatted URL.
5194
+ */
5195
+ function formatSearchQuery(definition) {
5196
+ const params = [];
5197
+ if (definition.fields) {
5198
+ params.push('_fields=' + definition.fields.join(','));
5199
+ }
5200
+ if (definition.filters) {
5201
+ definition.filters.forEach((filter) => params.push(formatFilter(filter)));
5202
+ }
5203
+ if (definition.sortRules && definition.sortRules.length > 0) {
5204
+ params.push(formatSortRules(definition.sortRules));
5205
+ }
5206
+ if (definition.offset !== undefined) {
5207
+ params.push('_offset=' + definition.offset);
5208
+ }
5209
+ if (definition.count !== undefined) {
5210
+ params.push('_count=' + definition.count);
5211
+ }
5212
+ if (definition.total !== undefined) {
5213
+ params.push('_total=' + definition.total);
5214
+ }
5215
+ if (params.length === 0) {
5216
+ return '';
5217
+ }
5218
+ params.sort();
5219
+ return '?' + params.join('&');
5220
+ }
5221
+ function formatFilter(filter) {
5222
+ const modifier = MODIFIER_OPERATORS.includes(filter.operator) ? ':' + filter.operator : '';
5223
+ const prefix = PREFIX_OPERATORS.includes(filter.operator) ? filter.operator : '';
5224
+ return `${filter.code}${modifier}=${prefix}${encodeURIComponent(filter.value)}`;
5225
+ }
5226
+ function formatSortRules(sortRules) {
5227
+ if (!sortRules || sortRules.length === 0) {
5228
+ return '';
5229
+ }
5230
+ return '_sort=' + sortRules.map((sr) => (sr.descending ? '-' + sr.code : sr.code)).join(',');
5231
+ }
5232
+
5329
5233
  var SearchParameterType;
5330
5234
  (function (SearchParameterType) {
5331
5235
  SearchParameterType["BOOLEAN"] = "BOOLEAN";