@medplum/core 0.9.24 → 0.9.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/cjs/index.js +9 -9
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/index.min.js.map +1 -1
  4. package/dist/esm/base-schema.json.js +4872 -0
  5. package/dist/esm/base-schema.json.js.map +1 -0
  6. package/dist/{types → esm}/cache.d.ts +0 -0
  7. package/dist/esm/cache.js +70 -0
  8. package/dist/esm/cache.js.map +1 -0
  9. package/dist/{types → esm}/client.d.ts +21 -13
  10. package/dist/esm/client.js +1450 -0
  11. package/dist/esm/client.js.map +1 -0
  12. package/dist/{types → esm}/crypto.d.ts +0 -0
  13. package/dist/esm/crypto.js +23 -0
  14. package/dist/esm/crypto.js.map +1 -0
  15. package/dist/{types → esm}/eventtarget.d.ts +0 -0
  16. package/dist/esm/eventtarget.js +41 -0
  17. package/dist/esm/eventtarget.js.map +1 -0
  18. package/dist/{types → esm}/fhirpath/atoms.d.ts +0 -0
  19. package/dist/esm/fhirpath/atoms.js +329 -0
  20. package/dist/esm/fhirpath/atoms.js.map +1 -0
  21. package/dist/{types → esm}/fhirpath/date.d.ts +0 -0
  22. package/dist/esm/fhirpath/date.js +24 -0
  23. package/dist/esm/fhirpath/date.js.map +1 -0
  24. package/dist/{types → esm}/fhirpath/functions.d.ts +0 -0
  25. package/dist/esm/fhirpath/functions.js +1503 -0
  26. package/dist/esm/fhirpath/functions.js.map +1 -0
  27. package/dist/{types → esm}/fhirpath/index.d.ts +0 -0
  28. package/dist/{types → esm}/fhirpath/parse.d.ts +0 -0
  29. package/dist/esm/fhirpath/parse.js +258 -0
  30. package/dist/esm/fhirpath/parse.js.map +1 -0
  31. package/dist/{types → esm}/fhirpath/tokenize.d.ts +0 -0
  32. package/dist/esm/fhirpath/tokenize.js +185 -0
  33. package/dist/esm/fhirpath/tokenize.js.map +1 -0
  34. package/dist/{types → esm}/fhirpath/utils.d.ts +0 -0
  35. package/dist/esm/fhirpath/utils.js +372 -0
  36. package/dist/esm/fhirpath/utils.js.map +1 -0
  37. package/dist/{types → esm}/format.d.ts +0 -0
  38. package/dist/esm/format.js +51 -0
  39. package/dist/esm/format.js.map +1 -0
  40. package/dist/{types → esm}/hl7.d.ts +0 -0
  41. package/dist/esm/hl7.js +111 -0
  42. package/dist/esm/hl7.js.map +1 -0
  43. package/dist/{types → esm}/index.d.ts +0 -0
  44. package/dist/esm/index.js +15 -10763
  45. package/dist/esm/index.js.map +1 -1
  46. package/dist/esm/index.min.js.map +1 -1
  47. package/dist/{types → esm}/jwt.d.ts +0 -0
  48. package/dist/esm/jwt.js +35 -0
  49. package/dist/esm/jwt.js.map +1 -0
  50. package/dist/esm/node_modules/tslib/package.json +1 -0
  51. package/dist/esm/node_modules/tslib/tslib.es6.js +40 -0
  52. package/dist/esm/node_modules/tslib/tslib.es6.js.map +1 -0
  53. package/dist/{types → esm}/outcomes.d.ts +0 -0
  54. package/dist/esm/outcomes.js +151 -0
  55. package/dist/esm/outcomes.js.map +1 -0
  56. package/dist/{types → esm}/readablepromise.d.ts +0 -0
  57. package/dist/esm/readablepromise.js +87 -0
  58. package/dist/esm/readablepromise.js.map +1 -0
  59. package/dist/{types → esm}/search.d.ts +0 -0
  60. package/dist/esm/search.js +207 -0
  61. package/dist/esm/search.js.map +1 -0
  62. package/dist/{types → esm}/searchparams.d.ts +0 -0
  63. package/dist/esm/searchparams.js +145 -0
  64. package/dist/esm/searchparams.js.map +1 -0
  65. package/dist/{types → esm}/storage.d.ts +0 -0
  66. package/dist/esm/storage.js +94 -0
  67. package/dist/esm/storage.js.map +1 -0
  68. package/dist/{types → esm}/types.d.ts +0 -0
  69. package/dist/esm/types.js +223 -0
  70. package/dist/esm/types.js.map +1 -0
  71. package/dist/{types → esm}/utils.d.ts +0 -0
  72. package/dist/esm/utils.js +590 -0
  73. package/dist/esm/utils.js.map +1 -0
  74. package/package.json +5 -5
  75. package/cody-pdf-test.js +0 -32
  76. package/wget-log +0 -6
@@ -0,0 +1,1450 @@
1
+ import { __classPrivateFieldSet, __classPrivateFieldGet, __awaiter } from './node_modules/tslib/tslib.es6.js';
2
+ import { LRUCache } from './cache.js';
3
+ import { getRandomString, encryptSHA256 } from './crypto.js';
4
+ import { EventTarget } from './eventtarget.js';
5
+ import { parseJWTPayload } from './jwt.js';
6
+ import { ReadablePromise } from './readablepromise.js';
7
+ import { ClientStorage } from './storage.js';
8
+ import { globalSchema, indexStructureDefinition, indexSearchParameter } from './types.js';
9
+ import { createReference, arrayBufferToBase64 } from './utils.js';
10
+
11
+ // PKCE auth ased on:
12
+ // https://aws.amazon.com/blogs/security/how-to-add-authentication-single-page-web-application-with-amazon-cognito-oauth2-implementation/
13
+ var _MedplumClient_instances, _MedplumClient_fetch, _MedplumClient_createPdf, _MedplumClient_storage, _MedplumClient_requestCache, _MedplumClient_cacheTime, _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_getCacheEntry, _MedplumClient_setCacheEntry, _MedplumClient_request, _MedplumClient_addFetchOptionsDefaults, _MedplumClient_setRequestContentType, _MedplumClient_setRequestBody, _MedplumClient_handleUnauthenticated, _MedplumClient_startPkce, _MedplumClient_requestAuthorization, _MedplumClient_refresh, _MedplumClient_fetchTokens, _MedplumClient_verifyTokens, _MedplumClient_setupStorageListener;
14
+ const DEFAULT_BASE_URL = 'https://api.medplum.com/';
15
+ const DEFAULT_SCOPE = 'launch/patient openid fhirUser offline_access user/*.*';
16
+ const DEFAULT_RESOURCE_CACHE_SIZE = 1000;
17
+ const DEFAULT_CACHE_TIME = 10000; // 10 seconds
18
+ const JSON_CONTENT_TYPE = 'application/json';
19
+ const FHIR_CONTENT_TYPE = 'application/fhir+json';
20
+ const PATCH_CONTENT_TYPE = 'application/json-patch+json';
21
+ /**
22
+ * The MedplumClient class provides a client for the Medplum FHIR server.
23
+ *
24
+ * The client can be used in the browser, in a NodeJS application, or in a Medplum Bot.
25
+ *
26
+ * The client provides helpful methods for common operations such as:
27
+ * 1) Authenticating
28
+ * 2) Creating resources
29
+ * 2) Reading resources
30
+ * 3) Updating resources
31
+ * 5) Deleting resources
32
+ * 6) Searching
33
+ * 7) Making GraphQL queries
34
+ *
35
+ * Here is a quick example of how to use the client:
36
+ *
37
+ * ```typescript
38
+ * import { MedplumClient } from '@medplum/core';
39
+ * const medplum = new MedplumClient();
40
+ * ```
41
+ *
42
+ * Create a `Patient`:
43
+ *
44
+ * ```typescript
45
+ * const patient = await medplum.createResource({
46
+ * resourceType: 'Patient',
47
+ * name: [{
48
+ * given: ['Alice'],
49
+ * family: 'Smith'
50
+ * }]
51
+ * });
52
+ * ```
53
+ *
54
+ * Read a `Patient` by ID:
55
+ *
56
+ * ```typescript
57
+ * const patient = await medplum.readResource('Patient', '123');
58
+ * console.log(patient.name[0].given[0]);
59
+ * ```
60
+ *
61
+ * Search for a `Patient` by name:
62
+ *
63
+ * ```typescript
64
+ * const bundle = await medplum.search('Patient', 'name=Alice');
65
+ * console.log(bundle.total);
66
+ * ```
67
+ */
68
+ class MedplumClient extends EventTarget {
69
+ constructor(options) {
70
+ var _a, _b;
71
+ super();
72
+ _MedplumClient_instances.add(this);
73
+ _MedplumClient_fetch.set(this, void 0);
74
+ _MedplumClient_createPdf.set(this, void 0);
75
+ _MedplumClient_storage.set(this, void 0);
76
+ _MedplumClient_requestCache.set(this, void 0);
77
+ _MedplumClient_cacheTime.set(this, void 0);
78
+ _MedplumClient_baseUrl.set(this, void 0);
79
+ _MedplumClient_clientId.set(this, void 0);
80
+ _MedplumClient_authorizeUrl.set(this, void 0);
81
+ _MedplumClient_tokenUrl.set(this, void 0);
82
+ _MedplumClient_logoutUrl.set(this, void 0);
83
+ _MedplumClient_onUnauthenticated.set(this, void 0);
84
+ _MedplumClient_accessToken.set(this, void 0);
85
+ _MedplumClient_refreshToken.set(this, void 0);
86
+ _MedplumClient_refreshPromise.set(this, void 0);
87
+ _MedplumClient_profilePromise.set(this, void 0);
88
+ _MedplumClient_profile.set(this, void 0);
89
+ _MedplumClient_config.set(this, void 0);
90
+ if (options === null || options === void 0 ? void 0 : options.baseUrl) {
91
+ if (!options.baseUrl.startsWith('http')) {
92
+ throw new Error('Base URL must start with http or https');
93
+ }
94
+ if (!options.baseUrl.endsWith('/')) {
95
+ throw new Error('Base URL must end with a trailing slash');
96
+ }
97
+ }
98
+ __classPrivateFieldSet(this, _MedplumClient_fetch, (options === null || options === void 0 ? void 0 : options.fetch) || window.fetch.bind(window), "f");
99
+ __classPrivateFieldSet(this, _MedplumClient_createPdf, options === null || options === void 0 ? void 0 : options.createPdf, "f");
100
+ __classPrivateFieldSet(this, _MedplumClient_storage, new ClientStorage(), "f");
101
+ __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");
102
+ __classPrivateFieldSet(this, _MedplumClient_cacheTime, (_b = options === null || options === void 0 ? void 0 : options.cacheTime) !== null && _b !== void 0 ? _b : DEFAULT_CACHE_TIME, "f");
103
+ __classPrivateFieldSet(this, _MedplumClient_baseUrl, (options === null || options === void 0 ? void 0 : options.baseUrl) || DEFAULT_BASE_URL, "f");
104
+ __classPrivateFieldSet(this, _MedplumClient_clientId, (options === null || options === void 0 ? void 0 : options.clientId) || '', "f");
105
+ __classPrivateFieldSet(this, _MedplumClient_authorizeUrl, (options === null || options === void 0 ? void 0 : options.authorizeUrl) || __classPrivateFieldGet(this, _MedplumClient_baseUrl, "f") + 'oauth2/authorize', "f");
106
+ __classPrivateFieldSet(this, _MedplumClient_tokenUrl, (options === null || options === void 0 ? void 0 : options.tokenUrl) || __classPrivateFieldGet(this, _MedplumClient_baseUrl, "f") + 'oauth2/token', "f");
107
+ __classPrivateFieldSet(this, _MedplumClient_logoutUrl, (options === null || options === void 0 ? void 0 : options.logoutUrl) || __classPrivateFieldGet(this, _MedplumClient_baseUrl, "f") + 'oauth2/logout', "f");
108
+ __classPrivateFieldSet(this, _MedplumClient_onUnauthenticated, options === null || options === void 0 ? void 0 : options.onUnauthenticated, "f");
109
+ const activeLogin = this.getActiveLogin();
110
+ if (activeLogin) {
111
+ __classPrivateFieldSet(this, _MedplumClient_accessToken, activeLogin.accessToken, "f");
112
+ __classPrivateFieldSet(this, _MedplumClient_refreshToken, activeLogin.refreshToken, "f");
113
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_refreshProfile).call(this).catch(console.log);
114
+ }
115
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setupStorageListener).call(this);
116
+ }
117
+ /**
118
+ * Returns the current base URL for all API requests.
119
+ * By default, this is set to `https://api.medplum.com/`.
120
+ * This can be overridden by setting the `baseUrl` option when creating the client.
121
+ * @category HTTP
122
+ * @returns The current base URL for all API requests.
123
+ */
124
+ getBaseUrl() {
125
+ return __classPrivateFieldGet(this, _MedplumClient_baseUrl, "f");
126
+ }
127
+ /**
128
+ * Clears all auth state including local storage and session storage.
129
+ * @category Authentication
130
+ */
131
+ clear() {
132
+ __classPrivateFieldGet(this, _MedplumClient_storage, "f").clear();
133
+ __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").clear();
134
+ __classPrivateFieldSet(this, _MedplumClient_accessToken, undefined, "f");
135
+ __classPrivateFieldSet(this, _MedplumClient_refreshToken, undefined, "f");
136
+ __classPrivateFieldSet(this, _MedplumClient_profile, undefined, "f");
137
+ __classPrivateFieldSet(this, _MedplumClient_config, undefined, "f");
138
+ this.dispatchEvent({ type: 'change' });
139
+ }
140
+ /**
141
+ * Invalidates any cached values or cached requests for the given URL.
142
+ * @category Caching
143
+ * @param url The URL to invalidate.
144
+ */
145
+ invalidateUrl(url) {
146
+ url = url.toString();
147
+ __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").delete(url);
148
+ }
149
+ /**
150
+ * Invalidates all cached search results or cached requests for the given resourceType.
151
+ * @category Caching
152
+ * @param resourceType The resource type to invalidate.
153
+ */
154
+ invalidateSearches(resourceType) {
155
+ const url = 'fhir/R4/' + resourceType;
156
+ for (const key of __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").keys()) {
157
+ if (key.endsWith(url) || key.includes(url + '?')) {
158
+ __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").delete(key);
159
+ }
160
+ }
161
+ }
162
+ /**
163
+ * Makes an HTTP GET request to the specified URL.
164
+ *
165
+ * This is a lower level method for custom requests.
166
+ * For common operations, we recommend using higher level methods
167
+ * such as `readResource()`, `search()`, etc.
168
+ *
169
+ * @category HTTP
170
+ * @param url The target URL.
171
+ * @param options Optional fetch options.
172
+ * @returns Promise to the response content.
173
+ */
174
+ get(url, options = {}) {
175
+ url = url.toString();
176
+ const cached = __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_getCacheEntry).call(this, url, options);
177
+ if (cached) {
178
+ return cached.value;
179
+ }
180
+ const promise = new ReadablePromise(__classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, 'GET', url, options));
181
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setCacheEntry).call(this, url, promise);
182
+ return promise;
183
+ }
184
+ /**
185
+ * Makes an HTTP POST request to the specified URL.
186
+ *
187
+ * This is a lower level method for custom requests.
188
+ * For common operations, we recommend using higher level methods
189
+ * such as `createResource()`.
190
+ *
191
+ * @category HTTP
192
+ * @param url The target URL.
193
+ * @param body The content body. Strings and `File` objects are passed directly. Other objects are converted to JSON.
194
+ * @param contentType The content type to be included in the "Content-Type" header.
195
+ * @param options Optional fetch options.
196
+ * @returns Promise to the response content.
197
+ */
198
+ post(url, body, contentType, options = {}) {
199
+ url = url.toString();
200
+ if (body) {
201
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setRequestBody).call(this, options, body);
202
+ }
203
+ if (contentType) {
204
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setRequestContentType).call(this, options, contentType);
205
+ }
206
+ this.invalidateUrl(url);
207
+ return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, 'POST', url, options);
208
+ }
209
+ /**
210
+ * Makes an HTTP PUT request to the specified URL.
211
+ *
212
+ * This is a lower level method for custom requests.
213
+ * For common operations, we recommend using higher level methods
214
+ * such as `updateResource()`.
215
+ *
216
+ * @category HTTP
217
+ * @param url The target URL.
218
+ * @param body The content body. Strings and `File` objects are passed directly. Other objects are converted to JSON.
219
+ * @param contentType The content type to be included in the "Content-Type" header.
220
+ * @param options Optional fetch options.
221
+ * @returns Promise to the response content.
222
+ */
223
+ put(url, body, contentType, options = {}) {
224
+ url = url.toString();
225
+ if (body) {
226
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setRequestBody).call(this, options, body);
227
+ }
228
+ if (contentType) {
229
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setRequestContentType).call(this, options, contentType);
230
+ }
231
+ this.invalidateUrl(url);
232
+ return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, 'PUT', url, options);
233
+ }
234
+ /**
235
+ * Makes an HTTP PATCH request to the specified URL.
236
+ *
237
+ * This is a lower level method for custom requests.
238
+ * For common operations, we recommend using higher level methods
239
+ * such as `patchResource()`.
240
+ *
241
+ * @category HTTP
242
+ * @param url The target URL.
243
+ * @param operations Array of JSONPatch operations.
244
+ * @param options Optional fetch options.
245
+ * @returns Promise to the response content.
246
+ */
247
+ patch(url, operations, options = {}) {
248
+ url = url.toString();
249
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setRequestBody).call(this, options, operations);
250
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setRequestContentType).call(this, options, PATCH_CONTENT_TYPE);
251
+ this.invalidateUrl(url);
252
+ return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, 'PATCH', url, options);
253
+ }
254
+ /**
255
+ * Makes an HTTP DELETE request to the specified URL.
256
+ *
257
+ *
258
+ * This is a lower level method for custom requests.
259
+ * For common operations, we recommend using higher level methods
260
+ * such as `deleteResource()`.
261
+ *
262
+ * @category HTTP
263
+ * @param url The target URL.
264
+ * @param options Optional fetch options.
265
+ * @returns Promise to the response content.
266
+ */
267
+ delete(url, options = {}) {
268
+ url = url.toString();
269
+ this.invalidateUrl(url);
270
+ return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, 'DELETE', url, options);
271
+ }
272
+ /**
273
+ * Initiates a new user flow.
274
+ *
275
+ * This method is part of the two different user registration flows:
276
+ * 1) New Practitioner and new Project
277
+ * 2) New Patient registration
278
+ *
279
+ * @category Authentication
280
+ * @param newUserRequest Register request including email and password.
281
+ * @returns Promise to the authentication response.
282
+ */
283
+ startNewUser(newUserRequest) {
284
+ return __awaiter(this, void 0, void 0, function* () {
285
+ yield __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_startPkce).call(this);
286
+ return this.post('auth/newuser', Object.assign(Object.assign({}, newUserRequest), { codeChallengeMethod: 'S256', codeChallenge: __classPrivateFieldGet(this, _MedplumClient_storage, "f").getString('codeChallenge') }));
287
+ });
288
+ }
289
+ /**
290
+ * Initiates a new project flow.
291
+ *
292
+ * This requires a partial login from `startNewUser` or `startNewGoogleUser`.
293
+ *
294
+ * @param newProjectRequest Register request including email and password.
295
+ * @param login The partial login to complete. This should come from the `startNewUser` method.
296
+ * @returns Promise to the authentication response.
297
+ */
298
+ startNewProject(newProjectRequest, login) {
299
+ return __awaiter(this, void 0, void 0, function* () {
300
+ return this.post('auth/newproject', Object.assign(Object.assign({}, newProjectRequest), login));
301
+ });
302
+ }
303
+ /**
304
+ * Initiates a new patient flow.
305
+ *
306
+ * This requires a partial login from `startNewUser` or `startNewGoogleUser`.
307
+ *
308
+ * @param newPatientRequest Register request including email and password.
309
+ * @param login The partial login to complete. This should come from the `startNewUser` method.
310
+ * @returns Promise to the authentication response.
311
+ */
312
+ startNewPatient(newPatientRequest, login) {
313
+ return __awaiter(this, void 0, void 0, function* () {
314
+ return this.post('auth/newpatient', Object.assign(Object.assign({}, newPatientRequest), login));
315
+ });
316
+ }
317
+ /**
318
+ * Initiates a user login flow.
319
+ * @category Authentication
320
+ * @param loginRequest Login request including email and password.
321
+ * @returns Promise to the authentication response.
322
+ */
323
+ startLogin(loginRequest) {
324
+ var _a, _b;
325
+ return __awaiter(this, void 0, void 0, function* () {
326
+ yield __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_startPkce).call(this);
327
+ return this.post('auth/login', Object.assign(Object.assign({}, loginRequest), { clientId: (_a = loginRequest.clientId) !== null && _a !== void 0 ? _a : __classPrivateFieldGet(this, _MedplumClient_clientId, "f"), scope: (_b = loginRequest.scope) !== null && _b !== void 0 ? _b : DEFAULT_SCOPE, codeChallengeMethod: 'S256', codeChallenge: __classPrivateFieldGet(this, _MedplumClient_storage, "f").getString('codeChallenge') }));
328
+ });
329
+ }
330
+ /**
331
+ * Tries to sign in with Google authentication.
332
+ * The response parameter is the result of a Google authentication.
333
+ * See: https://developers.google.com/identity/gsi/web/guides/handle-credential-responses-js-functions
334
+ * @category Authentication
335
+ * @param loginRequest Login request including Google credential response.
336
+ * @returns Promise to the authentication response.
337
+ */
338
+ startGoogleLogin(loginRequest) {
339
+ var _a, _b;
340
+ return __awaiter(this, void 0, void 0, function* () {
341
+ yield __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_startPkce).call(this);
342
+ return this.post('auth/google', Object.assign(Object.assign({}, loginRequest), { clientId: (_a = loginRequest.clientId) !== null && _a !== void 0 ? _a : __classPrivateFieldGet(this, _MedplumClient_clientId, "f"), scope: (_b = loginRequest.scope) !== null && _b !== void 0 ? _b : DEFAULT_SCOPE }));
343
+ });
344
+ }
345
+ /**
346
+ * Signs out locally.
347
+ * Does not invalidate tokens with the server.
348
+ * @category Authentication
349
+ */
350
+ signOut() {
351
+ this.clear();
352
+ return Promise.resolve();
353
+ }
354
+ /**
355
+ * Tries to sign in the user.
356
+ * Returns true if the user is signed in.
357
+ * This may result in navigating away to the sign in page.
358
+ * @category Authentication
359
+ */
360
+ signInWithRedirect() {
361
+ const urlParams = new URLSearchParams(window.location.search);
362
+ const code = urlParams.get('code');
363
+ if (!code) {
364
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_requestAuthorization).call(this);
365
+ return undefined;
366
+ }
367
+ else {
368
+ return this.processCode(code);
369
+ }
370
+ }
371
+ /**
372
+ * Tries to sign out the user.
373
+ * See: https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html
374
+ * @category Authentication
375
+ */
376
+ signOutWithRedirect() {
377
+ window.location.assign(__classPrivateFieldGet(this, _MedplumClient_logoutUrl, "f"));
378
+ }
379
+ /**
380
+ * Builds a FHIR URL from a collection of URL path components.
381
+ * For example, `buildUrl('/Patient', '123')` returns `fhir/R4/Patient/123`.
382
+ * @category HTTP
383
+ * @param path The path component of the URL.
384
+ * @returns The well-formed FHIR URL.
385
+ */
386
+ fhirUrl(...path) {
387
+ return new URL(__classPrivateFieldGet(this, _MedplumClient_baseUrl, "f") + 'fhir/R4/' + path.join('/'));
388
+ }
389
+ /**
390
+ * Builds a FHIR search URL from a search query or structured query object.
391
+ * @category HTTP
392
+ * @category Search
393
+ * @param query The FHIR search query or structured query object.
394
+ * @returns The well-formed FHIR URL.
395
+ */
396
+ fhirSearchUrl(resourceType, query) {
397
+ const url = this.fhirUrl(resourceType);
398
+ if (query) {
399
+ url.search = query.toString();
400
+ }
401
+ return url;
402
+ }
403
+ /**
404
+ * Sends a FHIR search request.
405
+ *
406
+ * Example using a FHIR search string:
407
+ *
408
+ * ```typescript
409
+ * const bundle = await client.search('Patient', 'name=Alice');
410
+ * console.log(bundle);
411
+ * ```
412
+ *
413
+ * The return value is a FHIR bundle:
414
+ *
415
+ * ```json
416
+ * {
417
+ * "resourceType": "Bundle",
418
+ * "type": "searchest",
419
+ * "total": 1,
420
+ * "entry": [
421
+ * {
422
+ * "resource": {
423
+ * "resourceType": "Patient",
424
+ * "name": [
425
+ * {
426
+ * "given": [
427
+ * "George"
428
+ * ],
429
+ * "family": "Washington"
430
+ * }
431
+ * ],
432
+ * }
433
+ * }
434
+ * ]
435
+ * }
436
+ * ```
437
+ *
438
+ * See FHIR search for full details: https://www.hl7.org/fhir/search.html
439
+ *
440
+ * @category Search
441
+ * @param query The search query as either a string or a structured search object.
442
+ * @returns Promise to the search result bundle.
443
+ */
444
+ search(resourceType, query, options = {}) {
445
+ return this.get(this.fhirSearchUrl(resourceType, query), options);
446
+ }
447
+ /**
448
+ * Sends a FHIR search request for a single resource.
449
+ *
450
+ * This is a convenience method for `search()` that returns the first resource rather than a `Bundle`.
451
+ *
452
+ * Example using a FHIR search string:
453
+ *
454
+ * ```typescript
455
+ * const patient = await client.searchOne('Patient', 'identifier=123');
456
+ * console.log(patient);
457
+ * ```
458
+ *
459
+ * The return value is the resource, if available; otherwise, undefined.
460
+ *
461
+ * See FHIR search for full details: https://www.hl7.org/fhir/search.html
462
+ *
463
+ * @category Search
464
+ * @param query The search query as either a string or a structured search object.
465
+ * @returns Promise to the search result bundle.
466
+ */
467
+ searchOne(resourceType, query, options = {}) {
468
+ const url = this.fhirSearchUrl(resourceType, query);
469
+ url.searchParams.set('_count', '1');
470
+ url.searchParams.sort();
471
+ const cacheKey = url.toString() + '-searchOne';
472
+ const cached = __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_getCacheEntry).call(this, cacheKey, options);
473
+ if (cached) {
474
+ return cached.value;
475
+ }
476
+ 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; }));
477
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setCacheEntry).call(this, cacheKey, promise);
478
+ return promise;
479
+ }
480
+ /**
481
+ * Sends a FHIR search request for an array of resources.
482
+ *
483
+ * This is a convenience method for `search()` that returns the resources as an array rather than a `Bundle`.
484
+ *
485
+ * Example using a FHIR search string:
486
+ *
487
+ * ```typescript
488
+ * const patients = await client.searchResources('Patient', 'name=Alice');
489
+ * console.log(patients);
490
+ * ```
491
+ *
492
+ * The return value is an array of resources.
493
+ *
494
+ * See FHIR search for full details: https://www.hl7.org/fhir/search.html
495
+ *
496
+ * @category Search
497
+ * @param query The search query as either a string or a structured search object.
498
+ * @returns Promise to the search result bundle.
499
+ */
500
+ searchResources(resourceType, query, options = {}) {
501
+ const url = this.fhirSearchUrl(resourceType, query);
502
+ const cacheKey = url.toString() + '-searchResources';
503
+ const cached = __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_getCacheEntry).call(this, cacheKey, options);
504
+ if (cached) {
505
+ return cached.value;
506
+ }
507
+ 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 : []; }));
508
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_setCacheEntry).call(this, cacheKey, promise);
509
+ return promise;
510
+ }
511
+ /**
512
+ * Searches a ValueSet resource using the "expand" operation.
513
+ * See: https://www.hl7.org/fhir/operation-valueset-expand.html
514
+ *
515
+ * @category Search
516
+ * @param system The ValueSet system url.
517
+ * @param filter The search string.
518
+ * @returns Promise to expanded ValueSet.
519
+ */
520
+ searchValueSet(system, filter, options = {}) {
521
+ const url = this.fhirUrl('ValueSet', '$expand');
522
+ url.searchParams.set('url', system);
523
+ url.searchParams.set('filter', filter);
524
+ return this.get(url.toString(), options);
525
+ }
526
+ /**
527
+ * Returns a cached resource if it is available.
528
+ * @category Caching
529
+ * @param resourceType The FHIR resource type.
530
+ * @param id The FHIR resource ID.
531
+ * @returns The resource if it is available in the cache; undefined otherwise.
532
+ */
533
+ getCached(resourceType, id) {
534
+ var _a;
535
+ const cached = (_a = __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").get(this.fhirUrl(resourceType, id).toString())) === null || _a === void 0 ? void 0 : _a.value;
536
+ return cached && cached.isOk() ? cached.read() : undefined;
537
+ }
538
+ /**
539
+ * Returns a cached resource if it is available.
540
+ * @category Caching
541
+ * @param resourceType The FHIR resource type.
542
+ * @param id The FHIR resource ID.
543
+ * @returns The resource if it is available in the cache; undefined otherwise.
544
+ */
545
+ getCachedReference(reference) {
546
+ const refString = reference.reference;
547
+ if (!refString) {
548
+ return undefined;
549
+ }
550
+ const [resourceType, id] = refString.split('/');
551
+ if (!resourceType || !id) {
552
+ return undefined;
553
+ }
554
+ return this.getCached(resourceType, id);
555
+ }
556
+ /**
557
+ * Reads a resource by resource type and ID.
558
+ *
559
+ * Example:
560
+ *
561
+ * ```typescript
562
+ * const patient = await medplum.readResource('Patient', '123');
563
+ * console.log(patient);
564
+ * ```
565
+ *
566
+ * See the FHIR "read" operation for full details: https://www.hl7.org/fhir/http.html#read
567
+ *
568
+ * @category Caching
569
+ * @param resourceType The FHIR resource type.
570
+ * @param id The resource ID.
571
+ * @returns The resource if available; undefined otherwise.
572
+ */
573
+ readResource(resourceType, id) {
574
+ return this.get(this.fhirUrl(resourceType, id));
575
+ }
576
+ /**
577
+ * Reads a resource by `Reference`.
578
+ *
579
+ * This is a convenience method for `readResource()` that accepts a `Reference` object.
580
+ *
581
+ * Example:
582
+ *
583
+ * ```typescript
584
+ * const serviceRequest = await medplum.readResource('ServiceRequest', '123');
585
+ * const patient = await medplum.readReference(serviceRequest.subject);
586
+ * console.log(patient);
587
+ * ```
588
+ *
589
+ * See the FHIR "read" operation for full details: https://www.hl7.org/fhir/http.html#read
590
+ *
591
+ * @category Read
592
+ * @param reference The FHIR reference object.
593
+ * @returns The resource if available; undefined otherwise.
594
+ */
595
+ readReference(reference) {
596
+ const refString = reference === null || reference === void 0 ? void 0 : reference.reference;
597
+ if (!refString) {
598
+ return new ReadablePromise(Promise.reject(new Error('Missing reference')));
599
+ }
600
+ const [resourceType, id] = refString.split('/');
601
+ if (!resourceType || !id) {
602
+ return new ReadablePromise(Promise.reject(new Error('Invalid reference')));
603
+ }
604
+ return this.readResource(resourceType, id);
605
+ }
606
+ /**
607
+ * Returns a cached schema for a resource type.
608
+ * If the schema is not cached, returns undefined.
609
+ * It is assumed that a client will call requestSchema before using this method.
610
+ * @category Schema
611
+ * @param resourceType The FHIR resource type.
612
+ * @returns The schema if immediately available, undefined otherwise.
613
+ * @deprecated Use globalSchema instead.
614
+ */
615
+ getSchema() {
616
+ return globalSchema;
617
+ }
618
+ /**
619
+ * Requests the schema for a resource type.
620
+ * If the schema is already cached, the promise is resolved immediately.
621
+ * @category Schema
622
+ * @param resourceType The FHIR resource type.
623
+ * @returns Promise to a schema with the requested resource type.
624
+ */
625
+ requestSchema(resourceType) {
626
+ return __awaiter(this, void 0, void 0, function* () {
627
+ if (resourceType in globalSchema.types) {
628
+ return globalSchema;
629
+ }
630
+ const query = `{
631
+ StructureDefinitionList(name: "${resourceType}") {
632
+ name,
633
+ description,
634
+ snapshot {
635
+ element {
636
+ id,
637
+ path,
638
+ min,
639
+ max,
640
+ type {
641
+ code,
642
+ targetProfile
643
+ },
644
+ binding {
645
+ valueSet
646
+ },
647
+ definition
648
+ }
649
+ }
650
+ }
651
+ SearchParameterList(base: "${resourceType}", _count: 100) {
652
+ base,
653
+ code,
654
+ type,
655
+ expression,
656
+ target
657
+ }
658
+ }`.replace(/\s+/g, ' ');
659
+ const response = (yield this.graphql(query));
660
+ for (const structureDefinition of response.data.StructureDefinitionList) {
661
+ indexStructureDefinition(globalSchema, structureDefinition);
662
+ }
663
+ for (const searchParameter of response.data.SearchParameterList) {
664
+ indexSearchParameter(globalSchema, searchParameter);
665
+ }
666
+ return globalSchema;
667
+ });
668
+ }
669
+ /**
670
+ * Reads resource history by resource type and ID.
671
+ *
672
+ * The return value is a bundle of all versions of the resource.
673
+ *
674
+ * Example:
675
+ *
676
+ * ```typescript
677
+ * const history = await medplum.readHistory('Patient', '123');
678
+ * console.log(history);
679
+ * ```
680
+ *
681
+ * See the FHIR "history" operation for full details: https://www.hl7.org/fhir/http.html#history
682
+ *
683
+ * @category Read
684
+ * @param resourceType The FHIR resource type.
685
+ * @param id The resource ID.
686
+ * @returns Promise to the resource history.
687
+ */
688
+ readHistory(resourceType, id) {
689
+ return this.get(this.fhirUrl(resourceType, id, '_history'));
690
+ }
691
+ /**
692
+ * Reads a specific version of a resource by resource type, ID, and version ID.
693
+ *
694
+ * Example:
695
+ *
696
+ * ```typescript
697
+ * const version = await medplum.readVersion('Patient', '123', '456');
698
+ * console.log(version);
699
+ * ```
700
+ *
701
+ * See the FHIR "vread" operation for full details: https://www.hl7.org/fhir/http.html#vread
702
+ *
703
+ * @category Read
704
+ * @param resourceType The FHIR resource type.
705
+ * @param id The resource ID.
706
+ * @returns The resource if available; undefined otherwise.
707
+ */
708
+ readVersion(resourceType, id, vid) {
709
+ return this.get(this.fhirUrl(resourceType, id, '_history', vid));
710
+ }
711
+ /**
712
+ *
713
+ * @category Read
714
+ * @param id The Patient Id
715
+ * @returns A Bundle of all Resources related to the Patient
716
+ */
717
+ readPatientEverything(id) {
718
+ return this.get(this.fhirUrl('Patient', id, '$everything'));
719
+ }
720
+ /**
721
+ * Creates a new FHIR resource.
722
+ *
723
+ * The return value is the newly created resource, including the ID and meta.
724
+ *
725
+ * Example:
726
+ *
727
+ * ```typescript
728
+ * const result = await medplum.createResource({
729
+ * resourceType: 'Patient',
730
+ * name: [{
731
+ * family: 'Smith',
732
+ * given: ['John']
733
+ * }]
734
+ * });
735
+ * console.log(result.id);
736
+ * ```
737
+ *
738
+ * See the FHIR "create" operation for full details: https://www.hl7.org/fhir/http.html#create
739
+ *
740
+ * @category Create
741
+ * @param resource The FHIR resource to create.
742
+ * @returns The result of the create operation.
743
+ */
744
+ createResource(resource) {
745
+ if (!resource.resourceType) {
746
+ throw new Error('Missing resourceType');
747
+ }
748
+ this.invalidateSearches(resource.resourceType);
749
+ return this.post(this.fhirUrl(resource.resourceType), resource);
750
+ }
751
+ /**
752
+ * Conditionally create a new FHIR resource only if some equivalent resource does not already exist on the server.
753
+ *
754
+ * The return value is the existing resource or the newly created resource, including the ID and meta.
755
+ *
756
+ * Example:
757
+ *
758
+ * ```typescript
759
+ * const result = await medplum.createResourceIfNoneExist(
760
+ * 'Patient?identifier=123',
761
+ * {
762
+ * resourceType: 'Patient',
763
+ * identifier: [{
764
+ * system: 'http://example.com/mrn',
765
+ * value: '123'
766
+ * }]
767
+ * name: [{
768
+ * family: 'Smith',
769
+ * given: ['John']
770
+ * }]
771
+ * });
772
+ * console.log(result.id);
773
+ * ```
774
+ *
775
+ * This method is syntactic sugar for:
776
+ *
777
+ * ```typescript
778
+ * return searchOne(query) ?? createResource(resource);
779
+ * ```
780
+ *
781
+ * The query parameter only contains the search parameters (what would be in the URL following the "?").
782
+ *
783
+ * See the FHIR "conditional create" operation for full details: https://www.hl7.org/fhir/http.html#ccreate
784
+ *
785
+ * @category Create
786
+ * @param resource The FHIR resource to create.
787
+ * @param query The search query for an equivalent resource.
788
+ * @returns The result of the create operation.
789
+ */
790
+ createResourceIfNoneExist(resource, query) {
791
+ var _a;
792
+ return __awaiter(this, void 0, void 0, function* () {
793
+ return ((_a = (yield this.searchOne(resource.resourceType, query))) !== null && _a !== void 0 ? _a : this.createResource(resource));
794
+ });
795
+ }
796
+ /**
797
+ * Creates a FHIR `Binary` resource with the provided data content.
798
+ *
799
+ * The return value is the newly created resource, including the ID and meta.
800
+ *
801
+ * The `data` parameter can be a string or a `File` object.
802
+ *
803
+ * A `File` object often comes from a `<input type="file">` element.
804
+ *
805
+ * Example:
806
+ *
807
+ * ```typescript
808
+ * const result = await medplum.createBinary(myFile, 'test.jpg', 'image/jpeg');
809
+ * console.log(result.id);
810
+ * ```
811
+ *
812
+ * See the FHIR "create" operation for full details: https://www.hl7.org/fhir/http.html#create
813
+ *
814
+ * @category Create
815
+ * @param data The binary data to upload.
816
+ * @param filename Optional filename for the binary.
817
+ * @param contentType Content type for the binary.
818
+ * @returns The result of the create operation.
819
+ */
820
+ createBinary(data, filename, contentType) {
821
+ const url = this.fhirUrl('Binary');
822
+ if (filename) {
823
+ url.searchParams.set('_filename', filename);
824
+ }
825
+ return this.post(url, data, contentType);
826
+ }
827
+ /**
828
+ * Creates a PDF as a FHIR `Binary` resource based on pdfmake document definition.
829
+ *
830
+ * The return value is the newly created resource, including the ID and meta.
831
+ *
832
+ * The `docDefinition` parameter is a pdfmake document definition.
833
+ *
834
+ * Example:
835
+ *
836
+ * ```typescript
837
+ * const result = await medplum.createPdf({
838
+ * content: ['Hello world']
839
+ * });
840
+ * console.log(result.id);
841
+ * ```
842
+ *
843
+ * See the pdfmake document definition for full details: https://pdfmake.github.io/docs/0.1/document-definition-object/
844
+ *
845
+ * @category Media
846
+ * @param docDefinition The PDF document definition.
847
+ * @returns The result of the create operation.
848
+ */
849
+ createPdf(docDefinition, filename, tableLayouts, fonts) {
850
+ return __awaiter(this, void 0, void 0, function* () {
851
+ if (!__classPrivateFieldGet(this, _MedplumClient_createPdf, "f")) {
852
+ throw new Error('PDF creation not enabled');
853
+ }
854
+ const blob = yield __classPrivateFieldGet(this, _MedplumClient_createPdf, "f").call(this, docDefinition, tableLayouts, fonts);
855
+ return this.createBinary(blob, filename, 'application/pdf');
856
+ });
857
+ }
858
+ /**
859
+ * Creates a FHIR `Communication` resource with the provided data content.
860
+ *
861
+ * This is a convenience method to handle commmon cases where a `Communication` resource is created with a `payload`.
862
+ *
863
+ * @category Create
864
+ * @param resource The FHIR resource to comment on.
865
+ * @param text The text of the comment.
866
+ * @returns The result of the create operation.
867
+ */
868
+ createComment(resource, text) {
869
+ const profile = this.getProfile();
870
+ let encounter = undefined;
871
+ let subject = undefined;
872
+ if (resource.resourceType === 'Encounter') {
873
+ encounter = createReference(resource);
874
+ subject = resource.subject;
875
+ }
876
+ if (resource.resourceType === 'ServiceRequest') {
877
+ encounter = resource.encounter;
878
+ subject = resource.subject;
879
+ }
880
+ if (resource.resourceType === 'Patient') {
881
+ subject = createReference(resource);
882
+ }
883
+ return this.createResource({
884
+ resourceType: 'Communication',
885
+ basedOn: [createReference(resource)],
886
+ encounter,
887
+ subject,
888
+ sender: profile ? createReference(profile) : undefined,
889
+ sent: new Date().toISOString(),
890
+ payload: [{ contentString: text }],
891
+ });
892
+ }
893
+ /**
894
+ * Updates a FHIR resource.
895
+ *
896
+ * The return value is the updated resource, including the ID and meta.
897
+ *
898
+ * Example:
899
+ *
900
+ * ```typescript
901
+ * const result = await medplum.updateResource({
902
+ * resourceType: 'Patient',
903
+ * id: '123',
904
+ * name: [{
905
+ * family: 'Smith',
906
+ * given: ['John']
907
+ * }]
908
+ * });
909
+ * console.log(result.meta.versionId);
910
+ * ```
911
+ *
912
+ * See the FHIR "update" operation for full details: https://www.hl7.org/fhir/http.html#update
913
+ *
914
+ * @category Write
915
+ * @param resource The FHIR resource to update.
916
+ * @returns The result of the update operation.
917
+ */
918
+ updateResource(resource) {
919
+ if (!resource.resourceType) {
920
+ throw new Error('Missing resourceType');
921
+ }
922
+ if (!resource.id) {
923
+ throw new Error('Missing id');
924
+ }
925
+ this.invalidateSearches(resource.resourceType);
926
+ return this.put(this.fhirUrl(resource.resourceType, resource.id), resource);
927
+ }
928
+ /**
929
+ * Updates a FHIR resource using JSONPatch operations.
930
+ *
931
+ * The return value is the updated resource, including the ID and meta.
932
+ *
933
+ * Example:
934
+ *
935
+ * ```typescript
936
+ * const result = await medplum.patchResource('Patient', '123', [
937
+ * {op: 'replace', path: '/name/0/family', value: 'Smith'},
938
+ * ]);
939
+ * console.log(result.meta.versionId);
940
+ * ```
941
+ *
942
+ * See the FHIR "update" operation for full details: https://www.hl7.org/fhir/http.html#patch
943
+ *
944
+ * See the JSONPatch specification for full details: https://tools.ietf.org/html/rfc6902
945
+ *
946
+ * @category Write
947
+ * @param resourceType The FHIR resource type.
948
+ * @param id The resource ID.
949
+ * @param operations The JSONPatch operations.
950
+ * @returns The result of the patch operations.
951
+ */
952
+ patchResource(resourceType, id, operations) {
953
+ this.invalidateSearches(resourceType);
954
+ return this.patch(this.fhirUrl(resourceType, id), operations);
955
+ }
956
+ /**
957
+ * Deletes a FHIR resource by resource type and ID.
958
+ *
959
+ * Example:
960
+ *
961
+ * ```typescript
962
+ * await medplum.deleteResource('Patient', '123');
963
+ * ```
964
+ *
965
+ * See the FHIR "delete" operation for full details: https://www.hl7.org/fhir/http.html#delete
966
+ *
967
+ * @category Delete
968
+ * @param resourceType The FHIR resource type.
969
+ * @param id The resource ID.
970
+ * @returns The result of the delete operation.
971
+ */
972
+ deleteResource(resourceType, id) {
973
+ this.invalidateSearches(resourceType);
974
+ return this.delete(this.fhirUrl(resourceType, id));
975
+ }
976
+ /**
977
+ * Executes a batch or transaction of FHIR operations.
978
+ *
979
+ * Example:
980
+ *
981
+ * ```typescript
982
+ * await medplum.executeBatch({
983
+ * "resourceType": "Bundle",
984
+ * "type": "transaction",
985
+ * "entry": [
986
+ * {
987
+ * "fullUrl": "urn:uuid:61ebe359-bfdc-4613-8bf2-c5e300945f0a",
988
+ * "resource": {
989
+ * "resourceType": "Patient",
990
+ * "name": [{ "use": "official", "given": ["Alice"], "family": "Smith" }],
991
+ * "gender": "female",
992
+ * "birthDate": "1974-12-25"
993
+ * },
994
+ * "request": {
995
+ * "method": "POST",
996
+ * "url": "Patient"
997
+ * }
998
+ * },
999
+ * {
1000
+ * "fullUrl": "urn:uuid:88f151c0-a954-468a-88bd-5ae15c08e059",
1001
+ * "resource": {
1002
+ * "resourceType": "Patient",
1003
+ * "identifier": [{ "system": "http:/example.org/fhir/ids", "value": "234234" }],
1004
+ * "name": [{ "use": "official", "given": ["Bob"], "family": "Jones" }],
1005
+ * "gender": "male",
1006
+ * "birthDate": "1974-12-25"
1007
+ * },
1008
+ * "request": {
1009
+ * "method": "POST",
1010
+ * "url": "Patient",
1011
+ * "ifNoneExist": "identifier=http:/example.org/fhir/ids|234234"
1012
+ * }
1013
+ * }
1014
+ * ]
1015
+ * });
1016
+ * ```
1017
+ *
1018
+ * See The FHIR "batch/transaction" section for full details: https://hl7.org/fhir/http.html#transaction
1019
+ * @category Batch
1020
+ * @param bundle The FHIR batch/transaction bundle.
1021
+ * @returns The FHIR batch/transaction response bundle.
1022
+ */
1023
+ executeBatch(bundle) {
1024
+ return this.post('fhir/R4', bundle);
1025
+ }
1026
+ /**
1027
+ * Sends an email using the Medplum Email API.
1028
+ *
1029
+ * Builds the email using nodemailer MailComposer.
1030
+ *
1031
+ * Examples:
1032
+ *
1033
+ * Send a simple text email:
1034
+ *
1035
+ * ```typescript
1036
+ * await medplum.sendEmail({
1037
+ * to: 'alice@example.com',
1038
+ * cc: 'bob@example.com',
1039
+ * subject: 'Hello',
1040
+ * text: 'Hello Alice',
1041
+ * });
1042
+ * ```
1043
+ *
1044
+ * Send an email with a `Binary` attachment:
1045
+ *
1046
+ * ```typescript
1047
+ * await medplum.sendEmail({
1048
+ * to: 'alice@example.com',
1049
+ * subject: 'Email with attachment',
1050
+ * text: 'See the attached report',
1051
+ * attachments: [{
1052
+ * filename: 'report.pdf',
1053
+ * path: "Binary/" + binary.id
1054
+ * }]
1055
+ * });
1056
+ * ```
1057
+ *
1058
+ * See options here: https://nodemailer.com/extras/mailcomposer/
1059
+ * @category Media
1060
+ * @param options The MailComposer options.
1061
+ * @returns Promise to the operation outcome.
1062
+ */
1063
+ sendEmail(email) {
1064
+ return this.post('email/v1/send', email, 'application/json');
1065
+ }
1066
+ /**
1067
+ * Executes a GraphQL query.
1068
+ *
1069
+ * Example:
1070
+ *
1071
+ * ```typescript
1072
+ * const result = await medplum.graphql(`{
1073
+ * Patient(id: "123") {
1074
+ * resourceType
1075
+ * id
1076
+ * name {
1077
+ * given
1078
+ * family
1079
+ * }
1080
+ * }
1081
+ * }`);
1082
+ * ```
1083
+ *
1084
+ * Advanced queries such as named operations and variable substitution are supported:
1085
+ *
1086
+ * ```typescript
1087
+ * const result = await medplum.graphql(
1088
+ * `query GetPatientById($patientId: ID!) {
1089
+ * Patient(id: $patientId) {
1090
+ * resourceType
1091
+ * id
1092
+ * name {
1093
+ * given
1094
+ * family
1095
+ * }
1096
+ * }
1097
+ * }`,
1098
+ * 'GetPatientById',
1099
+ * { patientId: '123' }
1100
+ * );
1101
+ * ```
1102
+ *
1103
+ * See the GraphQL documentation for more details: https://graphql.org/learn/
1104
+ *
1105
+ * See the FHIR GraphQL documentation for FHIR specific details: https://www.hl7.org/fhir/graphql.html
1106
+ *
1107
+ * @category Read
1108
+ * @param query The GraphQL query.
1109
+ * @param operationName Optional GraphQL operation name.
1110
+ * @param variables Optional GraphQL variables.
1111
+ * @param options Optional fetch options.
1112
+ * @returns The GraphQL result.
1113
+ */
1114
+ graphql(query, operationName, variables, options) {
1115
+ return this.post(this.fhirUrl('$graphql'), { query, operationName, variables }, JSON_CONTENT_TYPE, options);
1116
+ }
1117
+ /**
1118
+ * @category Authentication
1119
+ * @returns The Login State
1120
+ */
1121
+ getActiveLogin() {
1122
+ return __classPrivateFieldGet(this, _MedplumClient_storage, "f").getObject('activeLogin');
1123
+ }
1124
+ /**
1125
+ * @category Authentication
1126
+ */
1127
+ setActiveLogin(login) {
1128
+ return __awaiter(this, void 0, void 0, function* () {
1129
+ __classPrivateFieldSet(this, _MedplumClient_accessToken, login.accessToken, "f");
1130
+ __classPrivateFieldSet(this, _MedplumClient_refreshToken, login.refreshToken, "f");
1131
+ __classPrivateFieldSet(this, _MedplumClient_profile, undefined, "f");
1132
+ __classPrivateFieldSet(this, _MedplumClient_config, undefined, "f");
1133
+ __classPrivateFieldGet(this, _MedplumClient_storage, "f").setObject('activeLogin', login);
1134
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_addLogin).call(this, login);
1135
+ __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").clear();
1136
+ __classPrivateFieldSet(this, _MedplumClient_refreshPromise, undefined, "f");
1137
+ yield __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_refreshProfile).call(this);
1138
+ });
1139
+ }
1140
+ /**
1141
+ * @category Authentication
1142
+ */
1143
+ getAccessToken() {
1144
+ return __classPrivateFieldGet(this, _MedplumClient_accessToken, "f");
1145
+ }
1146
+ /**
1147
+ * @category Authentication
1148
+ */
1149
+ setAccessToken(accessToken) {
1150
+ __classPrivateFieldSet(this, _MedplumClient_accessToken, accessToken, "f");
1151
+ __classPrivateFieldSet(this, _MedplumClient_refreshToken, undefined, "f");
1152
+ __classPrivateFieldSet(this, _MedplumClient_profile, undefined, "f");
1153
+ __classPrivateFieldSet(this, _MedplumClient_config, undefined, "f");
1154
+ }
1155
+ /**
1156
+ * @category Authentication
1157
+ */
1158
+ getLogins() {
1159
+ var _a;
1160
+ return (_a = __classPrivateFieldGet(this, _MedplumClient_storage, "f").getObject('logins')) !== null && _a !== void 0 ? _a : [];
1161
+ }
1162
+ /**
1163
+ * @category Authentication
1164
+ */
1165
+ isLoading() {
1166
+ return !!__classPrivateFieldGet(this, _MedplumClient_profilePromise, "f");
1167
+ }
1168
+ /**
1169
+ * @category User Profile
1170
+ */
1171
+ getProfile() {
1172
+ return __classPrivateFieldGet(this, _MedplumClient_profile, "f");
1173
+ }
1174
+ /**
1175
+ * @category User Profile
1176
+ */
1177
+ getProfileAsync() {
1178
+ return __awaiter(this, void 0, void 0, function* () {
1179
+ if (__classPrivateFieldGet(this, _MedplumClient_profilePromise, "f")) {
1180
+ yield __classPrivateFieldGet(this, _MedplumClient_profilePromise, "f");
1181
+ }
1182
+ return this.getProfile();
1183
+ });
1184
+ }
1185
+ /**
1186
+ * @category User Profile
1187
+ */
1188
+ getUserConfiguration() {
1189
+ return __classPrivateFieldGet(this, _MedplumClient_config, "f");
1190
+ }
1191
+ /**
1192
+ * Downloads the URL as a blob.
1193
+ *
1194
+ * @category Read
1195
+ * @param url The URL to request.
1196
+ * @returns Promise to the response body as a blob.
1197
+ */
1198
+ download(url, options = {}) {
1199
+ return __awaiter(this, void 0, void 0, function* () {
1200
+ if (__classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f")) {
1201
+ yield __classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f");
1202
+ }
1203
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_addFetchOptionsDefaults).call(this, options);
1204
+ const response = yield __classPrivateFieldGet(this, _MedplumClient_fetch, "f").call(this, url.toString(), options);
1205
+ return response.blob();
1206
+ });
1207
+ }
1208
+ /**
1209
+ * Processes an OAuth authorization code.
1210
+ * See: https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
1211
+ * @param code The authorization code received by URL parameter.
1212
+ */
1213
+ processCode(code) {
1214
+ const pkceState = __classPrivateFieldGet(this, _MedplumClient_storage, "f").getString('pkceState');
1215
+ if (!pkceState) {
1216
+ this.clear();
1217
+ throw new Error('Invalid PCKE state');
1218
+ }
1219
+ const codeVerifier = __classPrivateFieldGet(this, _MedplumClient_storage, "f").getString('codeVerifier');
1220
+ if (!codeVerifier) {
1221
+ this.clear();
1222
+ throw new Error('Invalid PCKE code verifier');
1223
+ }
1224
+ const formBody = new URLSearchParams();
1225
+ formBody.set('grant_type', 'authorization_code');
1226
+ formBody.set('client_id', __classPrivateFieldGet(this, _MedplumClient_clientId, "f"));
1227
+ formBody.set('code_verifier', codeVerifier);
1228
+ formBody.set('code', code);
1229
+ formBody.set('redirect_uri', getBaseUrl());
1230
+ return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_fetchTokens).call(this, formBody);
1231
+ }
1232
+ /**
1233
+ * Starts a new OAuth2 client credentials flow.
1234
+ * See: https://datatracker.ietf.org/doc/html/rfc6749#section-4.4
1235
+ * @category Authentication
1236
+ * @param clientId The client ID.
1237
+ * @param clientSecret The client secret.
1238
+ * @returns Promise that resolves to the client profile.
1239
+ */
1240
+ startClientLogin(clientId, clientSecret) {
1241
+ return __awaiter(this, void 0, void 0, function* () {
1242
+ const formBody = new URLSearchParams();
1243
+ formBody.set('grant_type', 'client_credentials');
1244
+ formBody.set('client_id', clientId);
1245
+ formBody.set('client_secret', clientSecret);
1246
+ return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_fetchTokens).call(this, formBody);
1247
+ });
1248
+ }
1249
+ }
1250
+ _MedplumClient_fetch = new WeakMap(), _MedplumClient_createPdf = new WeakMap(), _MedplumClient_storage = new WeakMap(), _MedplumClient_requestCache = new WeakMap(), _MedplumClient_cacheTime = 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) {
1251
+ 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); });
1252
+ logins.push(newLogin);
1253
+ __classPrivateFieldGet(this, _MedplumClient_storage, "f").setObject('logins', logins);
1254
+ }, _MedplumClient_refreshProfile = function _MedplumClient_refreshProfile() {
1255
+ return __awaiter(this, void 0, void 0, function* () {
1256
+ __classPrivateFieldSet(this, _MedplumClient_profilePromise, new Promise((resolve, reject) => {
1257
+ this.get('auth/me')
1258
+ .then((result) => {
1259
+ __classPrivateFieldSet(this, _MedplumClient_profilePromise, undefined, "f");
1260
+ __classPrivateFieldSet(this, _MedplumClient_profile, result.profile, "f");
1261
+ __classPrivateFieldSet(this, _MedplumClient_config, result.config, "f");
1262
+ this.dispatchEvent({ type: 'change' });
1263
+ resolve(__classPrivateFieldGet(this, _MedplumClient_profile, "f"));
1264
+ })
1265
+ .catch(reject);
1266
+ }), "f");
1267
+ return __classPrivateFieldGet(this, _MedplumClient_profilePromise, "f");
1268
+ });
1269
+ }, _MedplumClient_getCacheEntry = function _MedplumClient_getCacheEntry(key, options) {
1270
+ if (__classPrivateFieldGet(this, _MedplumClient_cacheTime, "f") <= 0 || (options === null || options === void 0 ? void 0 : options.cache) === 'no-cache' || (options === null || options === void 0 ? void 0 : options.cache) === 'reload') {
1271
+ return undefined;
1272
+ }
1273
+ const entry = __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").get(key);
1274
+ if (!entry || entry.requestTime + __classPrivateFieldGet(this, _MedplumClient_cacheTime, "f") < Date.now()) {
1275
+ return undefined;
1276
+ }
1277
+ return entry;
1278
+ }, _MedplumClient_setCacheEntry = function _MedplumClient_setCacheEntry(key, value) {
1279
+ if (__classPrivateFieldGet(this, _MedplumClient_cacheTime, "f") > 0) {
1280
+ __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").set(key, { requestTime: Date.now(), value });
1281
+ }
1282
+ }, _MedplumClient_request = function _MedplumClient_request(method, url, options = {}) {
1283
+ return __awaiter(this, void 0, void 0, function* () {
1284
+ if (__classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f")) {
1285
+ yield __classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f");
1286
+ }
1287
+ if (!url.startsWith('http')) {
1288
+ url = __classPrivateFieldGet(this, _MedplumClient_baseUrl, "f") + url;
1289
+ }
1290
+ options.method = method;
1291
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_addFetchOptionsDefaults).call(this, options);
1292
+ const response = yield __classPrivateFieldGet(this, _MedplumClient_fetch, "f").call(this, url, options);
1293
+ if (response.status === 401) {
1294
+ // Refresh and try again
1295
+ return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_handleUnauthenticated).call(this, method, url, options);
1296
+ }
1297
+ if (response.status === 204 || response.status === 304) {
1298
+ // No content or change
1299
+ return undefined;
1300
+ }
1301
+ const obj = yield response.json();
1302
+ if (response.status >= 400) {
1303
+ throw obj;
1304
+ }
1305
+ return obj;
1306
+ });
1307
+ }, _MedplumClient_addFetchOptionsDefaults = function _MedplumClient_addFetchOptionsDefaults(options) {
1308
+ if (!options.headers) {
1309
+ options.headers = {};
1310
+ }
1311
+ const headers = options.headers;
1312
+ if (!headers['Content-Type']) {
1313
+ headers['Content-Type'] = FHIR_CONTENT_TYPE;
1314
+ }
1315
+ if (__classPrivateFieldGet(this, _MedplumClient_accessToken, "f")) {
1316
+ headers['Authorization'] = 'Bearer ' + __classPrivateFieldGet(this, _MedplumClient_accessToken, "f");
1317
+ }
1318
+ if (!options.cache) {
1319
+ options.cache = 'no-cache';
1320
+ }
1321
+ if (!options.credentials) {
1322
+ options.credentials = 'include';
1323
+ }
1324
+ }, _MedplumClient_setRequestContentType = function _MedplumClient_setRequestContentType(options, contentType) {
1325
+ if (!options.headers) {
1326
+ options.headers = {};
1327
+ }
1328
+ const headers = options.headers;
1329
+ headers['Content-Type'] = contentType;
1330
+ }, _MedplumClient_setRequestBody = function _MedplumClient_setRequestBody(options, data) {
1331
+ if (typeof data === 'string' ||
1332
+ (typeof Blob !== 'undefined' && data instanceof Blob) ||
1333
+ (typeof File !== 'undefined' && data instanceof File) ||
1334
+ (typeof Uint8Array !== 'undefined' && data instanceof Uint8Array)) {
1335
+ options.body = data;
1336
+ }
1337
+ else if (data) {
1338
+ options.body = JSON.stringify(data);
1339
+ }
1340
+ }, _MedplumClient_handleUnauthenticated = function _MedplumClient_handleUnauthenticated(method, url, options) {
1341
+ return __awaiter(this, void 0, void 0, function* () {
1342
+ return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_refresh).call(this)
1343
+ .then(() => __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, method, url, options))
1344
+ .catch((error) => {
1345
+ this.clear();
1346
+ if (__classPrivateFieldGet(this, _MedplumClient_onUnauthenticated, "f")) {
1347
+ __classPrivateFieldGet(this, _MedplumClient_onUnauthenticated, "f").call(this);
1348
+ }
1349
+ return Promise.reject(error);
1350
+ });
1351
+ });
1352
+ }, _MedplumClient_startPkce = function _MedplumClient_startPkce() {
1353
+ return __awaiter(this, void 0, void 0, function* () {
1354
+ const pkceState = getRandomString();
1355
+ __classPrivateFieldGet(this, _MedplumClient_storage, "f").setString('pkceState', pkceState);
1356
+ const codeVerifier = getRandomString();
1357
+ __classPrivateFieldGet(this, _MedplumClient_storage, "f").setString('codeVerifier', codeVerifier);
1358
+ const arrayHash = yield encryptSHA256(codeVerifier);
1359
+ const codeChallenge = arrayBufferToBase64(arrayHash).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
1360
+ __classPrivateFieldGet(this, _MedplumClient_storage, "f").setString('codeChallenge', codeChallenge);
1361
+ });
1362
+ }, _MedplumClient_requestAuthorization = function _MedplumClient_requestAuthorization() {
1363
+ __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_startPkce).call(this);
1364
+ const url = new URL(__classPrivateFieldGet(this, _MedplumClient_authorizeUrl, "f"));
1365
+ url.searchParams.set('response_type', 'code');
1366
+ url.searchParams.set('state', __classPrivateFieldGet(this, _MedplumClient_storage, "f").getString('pkceState'));
1367
+ url.searchParams.set('client_id', __classPrivateFieldGet(this, _MedplumClient_clientId, "f"));
1368
+ url.searchParams.set('redirect_uri', getBaseUrl());
1369
+ url.searchParams.set('scope', DEFAULT_SCOPE);
1370
+ url.searchParams.set('code_challenge_method', 'S256');
1371
+ url.searchParams.set('code_challenge', __classPrivateFieldGet(this, _MedplumClient_storage, "f").getString('codeChallenge'));
1372
+ window.location.assign(url.toString());
1373
+ }, _MedplumClient_refresh = function _MedplumClient_refresh() {
1374
+ return __awaiter(this, void 0, void 0, function* () {
1375
+ if (__classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f")) {
1376
+ return __classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f");
1377
+ }
1378
+ if (!__classPrivateFieldGet(this, _MedplumClient_refreshToken, "f")) {
1379
+ this.clear();
1380
+ throw new Error('Invalid refresh token');
1381
+ }
1382
+ const formBody = new URLSearchParams();
1383
+ formBody.set('grant_type', 'refresh_token');
1384
+ formBody.set('client_id', __classPrivateFieldGet(this, _MedplumClient_clientId, "f"));
1385
+ formBody.set('refresh_token', __classPrivateFieldGet(this, _MedplumClient_refreshToken, "f"));
1386
+ __classPrivateFieldSet(this, _MedplumClient_refreshPromise, __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_fetchTokens).call(this, formBody), "f");
1387
+ yield __classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f");
1388
+ });
1389
+ }, _MedplumClient_fetchTokens = function _MedplumClient_fetchTokens(formBody) {
1390
+ return __awaiter(this, void 0, void 0, function* () {
1391
+ return __classPrivateFieldGet(this, _MedplumClient_fetch, "f").call(this, __classPrivateFieldGet(this, _MedplumClient_tokenUrl, "f"), {
1392
+ method: 'POST',
1393
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
1394
+ body: formBody,
1395
+ })
1396
+ .then((response) => {
1397
+ if (!response.ok) {
1398
+ throw new Error('Failed to fetch tokens');
1399
+ }
1400
+ return response.json();
1401
+ })
1402
+ .then((tokens) => __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_verifyTokens).call(this, tokens))
1403
+ .then(() => this.getProfile());
1404
+ });
1405
+ }, _MedplumClient_verifyTokens = function _MedplumClient_verifyTokens(tokens) {
1406
+ return __awaiter(this, void 0, void 0, function* () {
1407
+ const token = tokens.access_token;
1408
+ // Verify token has not expired
1409
+ const tokenPayload = parseJWTPayload(token);
1410
+ if (Date.now() >= tokenPayload.exp * 1000) {
1411
+ this.clear();
1412
+ throw new Error('Token expired');
1413
+ }
1414
+ // Verify app_client_id
1415
+ if (__classPrivateFieldGet(this, _MedplumClient_clientId, "f") && tokenPayload.client_id !== __classPrivateFieldGet(this, _MedplumClient_clientId, "f")) {
1416
+ this.clear();
1417
+ throw new Error('Token was not issued for this audience');
1418
+ }
1419
+ yield this.setActiveLogin({
1420
+ accessToken: token,
1421
+ refreshToken: tokens.refresh_token,
1422
+ project: tokens.project,
1423
+ profile: tokens.profile,
1424
+ });
1425
+ });
1426
+ }, _MedplumClient_setupStorageListener = function _MedplumClient_setupStorageListener() {
1427
+ try {
1428
+ window.addEventListener('storage', (e) => {
1429
+ if (e.key === null || e.key === 'activeLogin') {
1430
+ // Storage events fire when different tabs make changes.
1431
+ // On storage clear (key === null) or activeLogin change (key === 'activeLogin')
1432
+ // Refresh the page to ensure the active login is up to date.
1433
+ window.location.reload();
1434
+ }
1435
+ });
1436
+ }
1437
+ catch (err) {
1438
+ // Silently ignore if this environment does not support storage events
1439
+ }
1440
+ };
1441
+ /**
1442
+ * Returns the base URL for the current page.
1443
+ * @category HTTP
1444
+ */
1445
+ function getBaseUrl() {
1446
+ return window.location.protocol + '//' + window.location.host + '/';
1447
+ }
1448
+
1449
+ export { MedplumClient };
1450
+ //# sourceMappingURL=client.js.map