@medplum/core 2.0.1 → 2.0.3

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.
@@ -1174,6 +1174,211 @@
1174
1174
  return decodePayload(payload);
1175
1175
  }
1176
1176
 
1177
+ const OK_ID = 'ok';
1178
+ const CREATED_ID = 'created';
1179
+ const GONE_ID = 'gone';
1180
+ const NOT_MODIFIED_ID = 'not-modified';
1181
+ const NOT_FOUND_ID = 'not-found';
1182
+ const UNAUTHORIZED_ID = 'unauthorized';
1183
+ const FORBIDDEN_ID = 'forbidden';
1184
+ const TOO_MANY_REQUESTS_ID = 'too-many-requests';
1185
+ const allOk = {
1186
+ resourceType: 'OperationOutcome',
1187
+ id: OK_ID,
1188
+ issue: [
1189
+ {
1190
+ severity: 'information',
1191
+ code: 'informational',
1192
+ details: {
1193
+ text: 'All OK',
1194
+ },
1195
+ },
1196
+ ],
1197
+ };
1198
+ const created = {
1199
+ resourceType: 'OperationOutcome',
1200
+ id: CREATED_ID,
1201
+ issue: [
1202
+ {
1203
+ severity: 'information',
1204
+ code: 'informational',
1205
+ details: {
1206
+ text: 'Created',
1207
+ },
1208
+ },
1209
+ ],
1210
+ };
1211
+ const notModified = {
1212
+ resourceType: 'OperationOutcome',
1213
+ id: NOT_MODIFIED_ID,
1214
+ issue: [
1215
+ {
1216
+ severity: 'information',
1217
+ code: 'informational',
1218
+ details: {
1219
+ text: 'Not Modified',
1220
+ },
1221
+ },
1222
+ ],
1223
+ };
1224
+ const notFound = {
1225
+ resourceType: 'OperationOutcome',
1226
+ id: NOT_FOUND_ID,
1227
+ issue: [
1228
+ {
1229
+ severity: 'error',
1230
+ code: 'not-found',
1231
+ details: {
1232
+ text: 'Not found',
1233
+ },
1234
+ },
1235
+ ],
1236
+ };
1237
+ const unauthorized = {
1238
+ resourceType: 'OperationOutcome',
1239
+ id: UNAUTHORIZED_ID,
1240
+ issue: [
1241
+ {
1242
+ severity: 'error',
1243
+ code: 'login',
1244
+ details: {
1245
+ text: 'Unauthorized',
1246
+ },
1247
+ },
1248
+ ],
1249
+ };
1250
+ const forbidden = {
1251
+ resourceType: 'OperationOutcome',
1252
+ id: FORBIDDEN_ID,
1253
+ issue: [
1254
+ {
1255
+ severity: 'error',
1256
+ code: 'forbidden',
1257
+ details: {
1258
+ text: 'Forbidden',
1259
+ },
1260
+ },
1261
+ ],
1262
+ };
1263
+ const gone = {
1264
+ resourceType: 'OperationOutcome',
1265
+ id: GONE_ID,
1266
+ issue: [
1267
+ {
1268
+ severity: 'error',
1269
+ code: 'deleted',
1270
+ details: {
1271
+ text: 'Gone',
1272
+ },
1273
+ },
1274
+ ],
1275
+ };
1276
+ const tooManyRequests = {
1277
+ resourceType: 'OperationOutcome',
1278
+ id: TOO_MANY_REQUESTS_ID,
1279
+ issue: [
1280
+ {
1281
+ severity: 'error',
1282
+ code: 'throttled',
1283
+ details: {
1284
+ text: 'Too Many Requests',
1285
+ },
1286
+ },
1287
+ ],
1288
+ };
1289
+ function badRequest(details, expression) {
1290
+ return {
1291
+ resourceType: 'OperationOutcome',
1292
+ issue: [
1293
+ {
1294
+ severity: 'error',
1295
+ code: 'invalid',
1296
+ details: {
1297
+ text: details,
1298
+ },
1299
+ expression: expression ? [expression] : undefined,
1300
+ },
1301
+ ],
1302
+ };
1303
+ }
1304
+ function isOperationOutcome(value) {
1305
+ return typeof value === 'object' && value !== null && value.resourceType === 'OperationOutcome';
1306
+ }
1307
+ function isOk(outcome) {
1308
+ return outcome.id === OK_ID || outcome.id === CREATED_ID || outcome.id === NOT_MODIFIED_ID;
1309
+ }
1310
+ function isNotFound(outcome) {
1311
+ return outcome.id === NOT_FOUND_ID;
1312
+ }
1313
+ function isGone(outcome) {
1314
+ return outcome.id === GONE_ID;
1315
+ }
1316
+ function getStatus(outcome) {
1317
+ if (outcome.id === OK_ID) {
1318
+ return 200;
1319
+ }
1320
+ else if (outcome.id === CREATED_ID) {
1321
+ return 201;
1322
+ }
1323
+ else if (outcome.id === NOT_MODIFIED_ID) {
1324
+ return 304;
1325
+ }
1326
+ else if (outcome.id === UNAUTHORIZED_ID) {
1327
+ return 401;
1328
+ }
1329
+ else if (outcome.id === FORBIDDEN_ID) {
1330
+ return 403;
1331
+ }
1332
+ else if (outcome.id === NOT_FOUND_ID) {
1333
+ return 404;
1334
+ }
1335
+ else if (outcome.id === GONE_ID) {
1336
+ return 410;
1337
+ }
1338
+ else if (outcome.id === TOO_MANY_REQUESTS_ID) {
1339
+ return 429;
1340
+ }
1341
+ else {
1342
+ return 400;
1343
+ }
1344
+ }
1345
+ /**
1346
+ * Asserts that the operation completed successfully and that the resource is defined.
1347
+ * @param outcome The operation outcome.
1348
+ * @param resource The resource that may or may not have been returned.
1349
+ */
1350
+ function assertOk(outcome, resource) {
1351
+ if (!isOk(outcome) || resource === undefined) {
1352
+ throw new OperationOutcomeError(outcome);
1353
+ }
1354
+ }
1355
+ class OperationOutcomeError extends Error {
1356
+ constructor(outcome) {
1357
+ super(outcome?.issue?.[0].details?.text);
1358
+ this.outcome = outcome;
1359
+ }
1360
+ }
1361
+ /**
1362
+ * Normalizes an error object into a displayable error string.
1363
+ * @param error The error value which could be a string, Error, OperationOutcome, or other unknown type.
1364
+ * @returns A display string for the error.
1365
+ */
1366
+ function normalizeErrorString(error) {
1367
+ if (!error) {
1368
+ return 'Unknown error';
1369
+ }
1370
+ if (typeof error === 'string') {
1371
+ return error;
1372
+ }
1373
+ if (error instanceof Error) {
1374
+ return error.message;
1375
+ }
1376
+ if (isOperationOutcome(error)) {
1377
+ return error.issue?.[0]?.details?.text ?? 'Unknown error';
1378
+ }
1379
+ return JSON.stringify(error);
1380
+ }
1381
+
1177
1382
  var _ReadablePromise_suspender, _ReadablePromise_status, _ReadablePromise_response, _ReadablePromise_error, _a;
1178
1383
  /**
1179
1384
  * The ReadablePromise class wraps a request promise suitable for React Suspense.
@@ -5736,6 +5941,13 @@
5736
5941
  code: "string"
5737
5942
  }
5738
5943
  ]
5944
+ },
5945
+ useSubject: {
5946
+ type: [
5947
+ {
5948
+ code: "boolean"
5949
+ }
5950
+ ]
5739
5951
  }
5740
5952
  }
5741
5953
  }
@@ -5945,6 +6157,30 @@
5945
6157
  type: 'reference',
5946
6158
  expression: resourceType + '.meta.compartment',
5947
6159
  },
6160
+ _profile: {
6161
+ base: [resourceType],
6162
+ code: '_profile',
6163
+ type: 'uri',
6164
+ expression: resourceType + '.meta.profile',
6165
+ },
6166
+ _security: {
6167
+ base: [resourceType],
6168
+ code: '_security',
6169
+ type: 'token',
6170
+ expression: resourceType + '.meta.security',
6171
+ },
6172
+ _source: {
6173
+ base: [resourceType],
6174
+ code: '_source',
6175
+ type: 'uri',
6176
+ expression: resourceType + '.meta.source',
6177
+ },
6178
+ _tag: {
6179
+ base: [resourceType],
6180
+ code: '_tag',
6181
+ type: 'token',
6182
+ expression: resourceType + '.meta.tag',
6183
+ },
5948
6184
  };
5949
6185
  }
5950
6186
  typeSchema.searchParams[searchParam.code] = searchParam;
@@ -6022,14 +6258,15 @@
6022
6258
 
6023
6259
  // PKCE auth based on:
6024
6260
  // https://aws.amazon.com/blogs/security/how-to-add-authentication-single-page-web-application-with-amazon-cognito-oauth2-implementation/
6025
- var _MedplumClient_instances, _MedplumClient_fetch, _MedplumClient_createPdf, _MedplumClient_storage, _MedplumClient_requestCache, _MedplumClient_cacheTime, _MedplumClient_baseUrl, _MedplumClient_fhirBaseUrl, _MedplumClient_authorizeUrl, _MedplumClient_tokenUrl, _MedplumClient_logoutUrl, _MedplumClient_onUnauthenticated, _MedplumClient_autoBatchTime, _MedplumClient_autoBatchQueue, _MedplumClient_clientId, _MedplumClient_clientSecret, _MedplumClient_autoBatchTimerId, _MedplumClient_accessToken, _MedplumClient_refreshToken, _MedplumClient_refreshPromise, _MedplumClient_profilePromise, _MedplumClient_profile, _MedplumClient_config, _MedplumClient_addLogin, _MedplumClient_refreshProfile, _MedplumClient_getCacheEntry, _MedplumClient_setCacheEntry, _MedplumClient_request, _MedplumClient_executeAutoBatch, _MedplumClient_addFetchOptionsDefaults, _MedplumClient_setRequestContentType, _MedplumClient_setRequestBody, _MedplumClient_handleUnauthenticated, _MedplumClient_requestAuthorization, _MedplumClient_refresh, _MedplumClient_fetchTokens, _MedplumClient_verifyTokens, _MedplumClient_setupStorageListener;
6026
- const MEDPLUM_VERSION = "2.0.1-89a5b1c5";
6261
+ var _MedplumClient_instances, _MedplumClient_fetch, _MedplumClient_createPdf, _MedplumClient_storage, _MedplumClient_requestCache, _MedplumClient_cacheTime, _MedplumClient_baseUrl, _MedplumClient_fhirBaseUrl, _MedplumClient_authorizeUrl, _MedplumClient_tokenUrl, _MedplumClient_logoutUrl, _MedplumClient_onUnauthenticated, _MedplumClient_autoBatchTime, _MedplumClient_autoBatchQueue, _MedplumClient_clientId, _MedplumClient_clientSecret, _MedplumClient_autoBatchTimerId, _MedplumClient_accessToken, _MedplumClient_refreshToken, _MedplumClient_refreshPromise, _MedplumClient_profilePromise, _MedplumClient_profile, _MedplumClient_config, _MedplumClient_addLogin, _MedplumClient_refreshProfile, _MedplumClient_getCacheEntry, _MedplumClient_setCacheEntry, _MedplumClient_cacheResource, _MedplumClient_deleteCacheEntry, _MedplumClient_request, _MedplumClient_fetchWithRetry, _MedplumClient_executeAutoBatch, _MedplumClient_addFetchOptionsDefaults, _MedplumClient_setRequestContentType, _MedplumClient_setRequestBody, _MedplumClient_handleUnauthenticated, _MedplumClient_requestAuthorization, _MedplumClient_refresh, _MedplumClient_fetchTokens, _MedplumClient_verifyTokens, _MedplumClient_setupStorageListener;
6262
+ const MEDPLUM_VERSION = "2.0.3-e39c01ab";
6027
6263
  const DEFAULT_BASE_URL = 'https://api.medplum.com/';
6028
6264
  const DEFAULT_RESOURCE_CACHE_SIZE = 1000;
6029
6265
  const DEFAULT_CACHE_TIME = 60000; // 60 seconds
6030
6266
  const JSON_CONTENT_TYPE = 'application/json';
6031
6267
  const FHIR_CONTENT_TYPE = 'application/fhir+json';
6032
6268
  const PATCH_CONTENT_TYPE = 'application/json-patch+json';
6269
+ const system = { resourceType: 'Device', id: 'system', deviceName: [{ name: 'System' }] };
6033
6270
  /**
6034
6271
  * The MedplumClient class provides a client for the Medplum FHIR server.
6035
6272
  *
@@ -6113,7 +6350,7 @@
6113
6350
  throw new Error('Base URL must start with http or https');
6114
6351
  }
6115
6352
  }
6116
- __classPrivateFieldSet(this, _MedplumClient_fetch, options?.fetch || window.fetch.bind(window), "f");
6353
+ __classPrivateFieldSet(this, _MedplumClient_fetch, options?.fetch || getDefaultFetch(), "f");
6117
6354
  __classPrivateFieldSet(this, _MedplumClient_createPdf, options?.createPdf, "f");
6118
6355
  __classPrivateFieldSet(this, _MedplumClient_storage, new ClientStorage(), "f");
6119
6356
  __classPrivateFieldSet(this, _MedplumClient_requestCache, new LRUCache(options?.resourceCacheSize ?? DEFAULT_RESOURCE_CACHE_SIZE), "f");
@@ -6405,7 +6642,8 @@
6405
6642
  * Does not invalidate tokens with the server.
6406
6643
  * @category Authentication
6407
6644
  */
6408
- signOut() {
6645
+ async signOut() {
6646
+ await this.post(__classPrivateFieldGet(this, _MedplumClient_logoutUrl, "f"), {});
6409
6647
  this.clear();
6410
6648
  }
6411
6649
  /**
@@ -6440,16 +6678,29 @@
6440
6678
  * @param clientId The external client ID.
6441
6679
  * @param redirectUri The external identity provider redirect URI.
6442
6680
  * @param baseLogin The Medplum login request.
6681
+ * @category Authentication
6443
6682
  */
6444
6683
  async signInWithExternalAuth(authorizeUrl, clientId, redirectUri, baseLogin) {
6445
6684
  const loginRequest = await this.ensureCodeChallenge(baseLogin);
6685
+ window.location.assign(this.getExternalAuthRedirectUri(authorizeUrl, clientId, redirectUri, loginRequest));
6686
+ }
6687
+ /**
6688
+ * Builds the external identity provider redirect URI.
6689
+ * @param authorizeUrl The external authorization URL.
6690
+ * @param clientId The external client ID.
6691
+ * @param redirectUri The external identity provider redirect URI.
6692
+ * @param loginRequest The Medplum login request.
6693
+ * @returns The external identity provider redirect URI.
6694
+ * @category Authentication
6695
+ */
6696
+ getExternalAuthRedirectUri(authorizeUrl, clientId, redirectUri, loginRequest) {
6446
6697
  const url = new URL(authorizeUrl);
6447
6698
  url.searchParams.set('response_type', 'code');
6448
6699
  url.searchParams.set('client_id', clientId);
6449
6700
  url.searchParams.set('redirect_uri', redirectUri);
6450
6701
  url.searchParams.set('scope', 'openid profile email');
6451
6702
  url.searchParams.set('state', JSON.stringify(loginRequest));
6452
- window.location.assign(url.toString());
6703
+ return url.toString();
6453
6704
  }
6454
6705
  /**
6455
6706
  * Builds a FHIR URL from a collection of URL path components.
@@ -6524,7 +6775,23 @@
6524
6775
  * @returns Promise to the search result bundle.
6525
6776
  */
6526
6777
  search(resourceType, query, options = {}) {
6527
- return this.get(this.fhirSearchUrl(resourceType, query), options);
6778
+ const url = this.fhirSearchUrl(resourceType, query);
6779
+ const cacheKey = url.toString() + '-search';
6780
+ const cached = __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_getCacheEntry).call(this, cacheKey, options);
6781
+ if (cached) {
6782
+ return cached.value;
6783
+ }
6784
+ const promise = new ReadablePromise((async () => {
6785
+ const bundle = await this.get(url, options);
6786
+ if (bundle.entry) {
6787
+ for (const entry of bundle.entry) {
6788
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_cacheResource).call(this, entry.resource);
6789
+ }
6790
+ }
6791
+ return bundle;
6792
+ })());
6793
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setCacheEntry).call(this, cacheKey, promise);
6794
+ return promise;
6528
6795
  }
6529
6796
  /**
6530
6797
  * Sends a FHIR search request for a single resource.
@@ -6633,6 +6900,9 @@
6633
6900
  if (!refString) {
6634
6901
  return undefined;
6635
6902
  }
6903
+ if (refString === 'system') {
6904
+ return system;
6905
+ }
6636
6906
  const [resourceType, id] = refString.split('/');
6637
6907
  if (!resourceType || !id) {
6638
6908
  return undefined;
@@ -6685,6 +6955,9 @@
6685
6955
  if (!refString) {
6686
6956
  return new ReadablePromise(Promise.reject(new Error('Missing reference')));
6687
6957
  }
6958
+ if (refString === 'system') {
6959
+ return new ReadablePromise(Promise.resolve(system));
6960
+ }
6688
6961
  const [resourceType, id] = refString.split('/');
6689
6962
  if (!resourceType || !id) {
6690
6963
  return new ReadablePromise(Promise.reject(new Error('Invalid reference')));
@@ -6709,11 +6982,17 @@
6709
6982
  * @param resourceType The FHIR resource type.
6710
6983
  * @returns Promise to a schema with the requested resource type.
6711
6984
  */
6712
- async requestSchema(resourceType) {
6985
+ requestSchema(resourceType) {
6713
6986
  if (resourceType in globalSchema.types) {
6714
- return globalSchema;
6987
+ return Promise.resolve(globalSchema);
6715
6988
  }
6716
- const query = `{
6989
+ const cacheKey = resourceType + '-requestSchema';
6990
+ const cached = __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_getCacheEntry).call(this, cacheKey, undefined);
6991
+ if (cached) {
6992
+ return cached.value;
6993
+ }
6994
+ const promise = new ReadablePromise((async () => {
6995
+ const query = `{
6717
6996
  StructureDefinitionList(name: "${resourceType}") {
6718
6997
  name,
6719
6998
  description,
@@ -6742,14 +7021,17 @@
6742
7021
  target
6743
7022
  }
6744
7023
  }`.replace(/\s+/g, ' ');
6745
- const response = (await this.graphql(query));
6746
- for (const structureDefinition of response.data.StructureDefinitionList) {
6747
- indexStructureDefinition(structureDefinition);
6748
- }
6749
- for (const searchParameter of response.data.SearchParameterList) {
6750
- indexSearchParameter(searchParameter);
6751
- }
6752
- return globalSchema;
7024
+ const response = (await this.graphql(query));
7025
+ for (const structureDefinition of response.data.StructureDefinitionList) {
7026
+ indexStructureDefinition(structureDefinition);
7027
+ }
7028
+ for (const searchParameter of response.data.SearchParameterList) {
7029
+ indexSearchParameter(searchParameter);
7030
+ }
7031
+ return globalSchema;
7032
+ })());
7033
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setCacheEntry).call(this, cacheKey, promise);
7034
+ return promise;
6753
7035
  }
6754
7036
  /**
6755
7037
  * Reads resource history by resource type and ID.
@@ -7050,10 +7332,15 @@
7050
7332
  throw new Error('Missing id');
7051
7333
  }
7052
7334
  this.invalidateSearches(resource.resourceType);
7053
- const result = await this.put(this.fhirUrl(resource.resourceType, resource.id), resource);
7054
- // On 304 not modified, result will be undefined
7055
- // Return the user input instead
7056
- return result ?? resource;
7335
+ let result = await this.put(this.fhirUrl(resource.resourceType, resource.id), resource);
7336
+ if (!result) {
7337
+ // On 304 not modified, result will be undefined
7338
+ // Return the user input instead
7339
+ // return result ?? resource;
7340
+ result = resource;
7341
+ }
7342
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_cacheResource).call(this, result);
7343
+ return result;
7057
7344
  }
7058
7345
  /**
7059
7346
  * Updates a FHIR resource using JSONPatch operations.
@@ -7100,6 +7387,7 @@
7100
7387
  * @returns The result of the delete operation.
7101
7388
  */
7102
7389
  deleteResource(resourceType, id) {
7390
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_deleteCacheEntry).call(this, this.fhirUrl(resourceType, id).toString());
7103
7391
  this.invalidateSearches(resourceType);
7104
7392
  return this.delete(this.fhirUrl(resourceType, id));
7105
7393
  }
@@ -7384,7 +7672,7 @@
7384
7672
  formBody.set('grant_type', 'authorization_code');
7385
7673
  formBody.set('client_id', __classPrivateFieldGet(this, _MedplumClient_clientId, "f"));
7386
7674
  formBody.set('code', code);
7387
- formBody.set('redirect_uri', getBaseUrl());
7675
+ formBody.set('redirect_uri', getWindowOrigin());
7388
7676
  const codeVerifier = sessionStorage.getItem('codeVerifier');
7389
7677
  if (codeVerifier) {
7390
7678
  formBody.set('code_verifier', codeVerifier);
@@ -7439,6 +7727,14 @@
7439
7727
  if (__classPrivateFieldGet(this, _MedplumClient_cacheTime, "f") > 0) {
7440
7728
  __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").set(key, { requestTime: Date.now(), value });
7441
7729
  }
7730
+ }, _MedplumClient_cacheResource = function _MedplumClient_cacheResource(resource) {
7731
+ if (resource?.id) {
7732
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setCacheEntry).call(this, this.fhirUrl(resource.resourceType, resource.id).toString(), new ReadablePromise(Promise.resolve(resource)));
7733
+ }
7734
+ }, _MedplumClient_deleteCacheEntry = function _MedplumClient_deleteCacheEntry(key) {
7735
+ if (__classPrivateFieldGet(this, _MedplumClient_cacheTime, "f") > 0) {
7736
+ __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").delete(key);
7737
+ }
7442
7738
  }, _MedplumClient_request =
7443
7739
  /**
7444
7740
  * Makes an HTTP request.
@@ -7456,7 +7752,7 @@
7456
7752
  }
7457
7753
  options.method = method;
7458
7754
  __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_addFetchOptionsDefaults).call(this, options);
7459
- const response = await __classPrivateFieldGet(this, _MedplumClient_fetch, "f").call(this, url, options);
7755
+ const response = await __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_fetchWithRetry).call(this, url, options);
7460
7756
  if (response.status === 401) {
7461
7757
  // Refresh and try again
7462
7758
  return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_handleUnauthenticated).call(this, method, url, options);
@@ -7465,11 +7761,30 @@
7465
7761
  // No content or change
7466
7762
  return undefined;
7467
7763
  }
7468
- const obj = await response.json();
7764
+ let obj = undefined;
7765
+ try {
7766
+ obj = await response.json();
7767
+ }
7768
+ catch (err) {
7769
+ console.error('Error parsing response', response.status, err);
7770
+ throw err;
7771
+ }
7469
7772
  if (response.status >= 400) {
7470
7773
  throw obj;
7471
7774
  }
7472
7775
  return obj;
7776
+ }, _MedplumClient_fetchWithRetry = async function _MedplumClient_fetchWithRetry(url, options) {
7777
+ const maxRetries = 3;
7778
+ const retryDelay = 200;
7779
+ let response = undefined;
7780
+ for (let retry = 0; retry < maxRetries; retry++) {
7781
+ response = (await __classPrivateFieldGet(this, _MedplumClient_fetch, "f").call(this, url, options));
7782
+ if (response.status < 500) {
7783
+ return response;
7784
+ }
7785
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
7786
+ }
7787
+ return response;
7473
7788
  }, _MedplumClient_executeAutoBatch =
7474
7789
  /**
7475
7790
  * Executes a batch of requests that were automatically batched together.
@@ -7505,7 +7820,12 @@
7505
7820
  for (let i = 0; i < entries.length; i++) {
7506
7821
  const entry = entries[i];
7507
7822
  const responseEntry = response.entry?.[i];
7508
- entry.resolve(responseEntry?.resource);
7823
+ if (responseEntry?.response?.outcome && !isOk(responseEntry.response.outcome)) {
7824
+ entry.reject(responseEntry.response.outcome);
7825
+ }
7826
+ else {
7827
+ entry.resolve(responseEntry?.resource);
7828
+ }
7509
7829
  }
7510
7830
  }, _MedplumClient_addFetchOptionsDefaults = function _MedplumClient_addFetchOptionsDefaults(options) {
7511
7831
  if (!options.headers) {
@@ -7562,7 +7882,7 @@
7562
7882
  url.searchParams.set('response_type', 'code');
7563
7883
  url.searchParams.set('state', sessionStorage.getItem('pkceState'));
7564
7884
  url.searchParams.set('client_id', loginRequest.clientId || __classPrivateFieldGet(this, _MedplumClient_clientId, "f"));
7565
- url.searchParams.set('redirect_uri', loginRequest.redirectUri || getBaseUrl());
7885
+ url.searchParams.set('redirect_uri', loginRequest.redirectUri || getWindowOrigin());
7566
7886
  url.searchParams.set('code_challenge_method', loginRequest.codeChallengeMethod);
7567
7887
  url.searchParams.set('code_challenge', loginRequest.codeChallenge);
7568
7888
  url.searchParams.set('scope', loginRequest.scope || 'openid profile');
@@ -7645,12 +7965,34 @@
7645
7965
  // Silently ignore if this environment does not support storage events
7646
7966
  }
7647
7967
  };
7968
+ /**
7969
+ * Returns the current window if available.
7970
+ * All access to the current window should use this to support SSR such as Next.js.
7971
+ * @returns The current window or undefined if not available.
7972
+ */
7973
+ function getWindow() {
7974
+ return typeof window === 'undefined' ? undefined : window;
7975
+ }
7976
+ /**
7977
+ * Returns the default fetch method.
7978
+ * The default fetch is currently only available in browser environments.
7979
+ * If you want to use SSR such as Next.js, you should pass a custom fetch function.
7980
+ * @returns The default fetch function for the current environment.
7981
+ */
7982
+ function getDefaultFetch() {
7983
+ const window = getWindow();
7984
+ if (!window) {
7985
+ throw new Error('Fetch not available in this environment');
7986
+ }
7987
+ return window.fetch.bind(window);
7988
+ }
7648
7989
  /**
7649
7990
  * Returns the base URL for the current page.
7650
7991
  * @category HTTP
7651
7992
  */
7652
- function getBaseUrl() {
7653
- return window.location.protocol + '//' + window.location.host + '/';
7993
+ function getWindowOrigin() {
7994
+ const window = getWindow();
7995
+ return window ? window.location.protocol + '//' + window.location.host + '/' : '';
7654
7996
  }
7655
7997
  function ensureTrailingSlash(url) {
7656
7998
  if (!url) {
@@ -11082,209 +11424,6 @@
11082
11424
  return operator === exports.Operator.NOT_EQUALS || operator === exports.Operator.NOT;
11083
11425
  }
11084
11426
 
11085
- const OK_ID = 'ok';
11086
- const CREATED_ID = 'created';
11087
- const GONE_ID = 'gone';
11088
- const NOT_MODIFIED_ID = 'not-modified';
11089
- const NOT_FOUND_ID = 'not-found';
11090
- const UNAUTHORIZED_ID = 'unauthorized';
11091
- const FORBIDDEN_ID = 'forbidden';
11092
- const TOO_MANY_REQUESTS_ID = 'too-many-requests';
11093
- const allOk = {
11094
- resourceType: 'OperationOutcome',
11095
- id: OK_ID,
11096
- issue: [
11097
- {
11098
- severity: 'information',
11099
- code: 'informational',
11100
- details: {
11101
- text: 'All OK',
11102
- },
11103
- },
11104
- ],
11105
- };
11106
- const created = {
11107
- resourceType: 'OperationOutcome',
11108
- id: CREATED_ID,
11109
- issue: [
11110
- {
11111
- severity: 'information',
11112
- code: 'informational',
11113
- details: {
11114
- text: 'Created',
11115
- },
11116
- },
11117
- ],
11118
- };
11119
- const notModified = {
11120
- resourceType: 'OperationOutcome',
11121
- id: NOT_MODIFIED_ID,
11122
- issue: [
11123
- {
11124
- severity: 'information',
11125
- code: 'informational',
11126
- details: {
11127
- text: 'Not Modified',
11128
- },
11129
- },
11130
- ],
11131
- };
11132
- const notFound = {
11133
- resourceType: 'OperationOutcome',
11134
- id: NOT_FOUND_ID,
11135
- issue: [
11136
- {
11137
- severity: 'error',
11138
- code: 'not-found',
11139
- details: {
11140
- text: 'Not found',
11141
- },
11142
- },
11143
- ],
11144
- };
11145
- const unauthorized = {
11146
- resourceType: 'OperationOutcome',
11147
- id: UNAUTHORIZED_ID,
11148
- issue: [
11149
- {
11150
- severity: 'error',
11151
- code: 'login',
11152
- details: {
11153
- text: 'Unauthorized',
11154
- },
11155
- },
11156
- ],
11157
- };
11158
- const forbidden = {
11159
- resourceType: 'OperationOutcome',
11160
- id: FORBIDDEN_ID,
11161
- issue: [
11162
- {
11163
- severity: 'error',
11164
- code: 'forbidden',
11165
- details: {
11166
- text: 'Forbidden',
11167
- },
11168
- },
11169
- ],
11170
- };
11171
- const gone = {
11172
- resourceType: 'OperationOutcome',
11173
- id: GONE_ID,
11174
- issue: [
11175
- {
11176
- severity: 'error',
11177
- code: 'deleted',
11178
- details: {
11179
- text: 'Gone',
11180
- },
11181
- },
11182
- ],
11183
- };
11184
- const tooManyRequests = {
11185
- resourceType: 'OperationOutcome',
11186
- id: TOO_MANY_REQUESTS_ID,
11187
- issue: [
11188
- {
11189
- severity: 'error',
11190
- code: 'throttled',
11191
- details: {
11192
- text: 'Too Many Requests',
11193
- },
11194
- },
11195
- ],
11196
- };
11197
- function badRequest(details, expression) {
11198
- return {
11199
- resourceType: 'OperationOutcome',
11200
- issue: [
11201
- {
11202
- severity: 'error',
11203
- code: 'invalid',
11204
- details: {
11205
- text: details,
11206
- },
11207
- expression: expression ? [expression] : undefined,
11208
- },
11209
- ],
11210
- };
11211
- }
11212
- function isOk(outcome) {
11213
- return outcome.id === OK_ID || outcome.id === CREATED_ID || outcome.id === NOT_MODIFIED_ID;
11214
- }
11215
- function isNotFound(outcome) {
11216
- return outcome.id === NOT_FOUND_ID;
11217
- }
11218
- function isGone(outcome) {
11219
- return outcome.id === GONE_ID;
11220
- }
11221
- function getStatus(outcome) {
11222
- if (outcome.id === OK_ID) {
11223
- return 200;
11224
- }
11225
- else if (outcome.id === CREATED_ID) {
11226
- return 201;
11227
- }
11228
- else if (outcome.id === NOT_MODIFIED_ID) {
11229
- return 304;
11230
- }
11231
- else if (outcome.id === UNAUTHORIZED_ID) {
11232
- return 401;
11233
- }
11234
- else if (outcome.id === FORBIDDEN_ID) {
11235
- return 403;
11236
- }
11237
- else if (outcome.id === NOT_FOUND_ID) {
11238
- return 404;
11239
- }
11240
- else if (outcome.id === GONE_ID) {
11241
- return 410;
11242
- }
11243
- else if (outcome.id === TOO_MANY_REQUESTS_ID) {
11244
- return 429;
11245
- }
11246
- else {
11247
- return 400;
11248
- }
11249
- }
11250
- /**
11251
- * Asserts that the operation completed successfully and that the resource is defined.
11252
- * @param outcome The operation outcome.
11253
- * @param resource The resource that may or may not have been returned.
11254
- */
11255
- function assertOk(outcome, resource) {
11256
- if (!isOk(outcome) || resource === undefined) {
11257
- throw new OperationOutcomeError(outcome);
11258
- }
11259
- }
11260
- class OperationOutcomeError extends Error {
11261
- constructor(outcome) {
11262
- super(outcome?.issue?.[0].details?.text);
11263
- this.outcome = outcome;
11264
- }
11265
- }
11266
- /**
11267
- * Normalizes an error object into a displayable error string.
11268
- * @param error The error value which could be a string, Error, OperationOutcome, or other unknown type.
11269
- * @returns A display string for the error.
11270
- */
11271
- function normalizeErrorString(error) {
11272
- if (!error) {
11273
- return 'Unknown error';
11274
- }
11275
- if (typeof error === 'string') {
11276
- return error;
11277
- }
11278
- if (error instanceof Error) {
11279
- return error.message;
11280
- }
11281
- if (typeof error === 'object' && 'resourceType' in error) {
11282
- const outcome = error;
11283
- return outcome.issue?.[0]?.details?.text ?? 'Unknown error';
11284
- }
11285
- return JSON.stringify(error);
11286
- }
11287
-
11288
11427
  exports.AndAtom = AndAtom;
11289
11428
  exports.ArithemticOperatorAtom = ArithemticOperatorAtom;
11290
11429
  exports.AsAtom = AsAtom;
@@ -11390,6 +11529,7 @@
11390
11529
  exports.isNotFound = isNotFound;
11391
11530
  exports.isObject = isObject$1;
11392
11531
  exports.isOk = isOk;
11532
+ exports.isOperationOutcome = isOperationOutcome;
11393
11533
  exports.isPeriod = isPeriod;
11394
11534
  exports.isProfileResource = isProfileResource;
11395
11535
  exports.isQuantity = isQuantity;