@openmrs/esm-api 10.0.1-pre.5607 → 10.0.1-pre.5616

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.
@@ -1,3 +1,3 @@
1
- [0] Successfully compiled: 14 files with swc (84.12ms)
1
+ [0] Successfully compiled: 14 files with swc (89.42ms)
2
2
  [0] swc --strip-leading-paths src -d dist exited with code 0
3
3
  [1] tsc --project tsconfig.build.json exited with code 0
@@ -7,6 +7,8 @@ export type LoadedSessionStore = {
7
7
  export type UnloadedSessionStore = {
8
8
  loaded: false;
9
9
  session: null;
10
+ /** Set when fetching the session failed before any session had loaded. */
11
+ error?: Error;
10
12
  };
11
13
  /** @internal */
12
14
  export declare const sessionStore: import("zustand").StoreApi<SessionStore>;
@@ -15,7 +17,8 @@ export declare const sessionStore: import("zustand").StoreApi<SessionStore>;
15
17
  * current user's session. If the session hasn't been loaded, was loaded more than
16
18
  * a minute ago, or is in the middle of being refetched, the Promise waits for the
17
19
  * fetch in question rather than resolving with data that may be out of date. The
18
- * session it resolves with is therefore never more than a minute old.
20
+ * session it resolves with is therefore never more than a minute old, unless that fetch fails,
21
+ * in which case it resolves with the last session that loaded.
19
22
  *
20
23
  * The function accepts an optional `opts` object with an `includeAuthStatus` boolean
21
24
  * property that defaults to `true`. When `true`, the entire {@link Session} object
@@ -94,7 +97,12 @@ export declare function setUserLanguage(data: Session): void;
94
97
  * the user. All subscribers to the session store will be notified of the
95
98
  * new user once the new version of the user object is downloaded.
96
99
  *
97
- * @returns A Promise resolving to the updated session store state.
100
+ * If the server rejects the request as unauthenticated (401 or 403), the store records a
101
+ * logged-out session. If the request fails for any other reason, a session that has already
102
+ * loaded is kept; otherwise the store records the error.
103
+ *
104
+ * @returns A Promise resolving to the updated session store state. It rejects with the
105
+ * store state if the request fails for a reason other than authentication.
98
106
  *
99
107
  * @example
100
108
  * ```js
@@ -189,7 +197,25 @@ export declare function getSessionLocation(): Promise<SessionLocation | undefine
189
197
  * await setSessionLocation('location-uuid-here', abortController);
190
198
  * ```
191
199
  */
192
- export declare function setSessionLocation(locationUuid: string, abortController: AbortController): Promise<any>;
200
+ export declare function setSessionLocation(locationUuid: string, abortController?: AbortController): Promise<any>;
201
+ /**
202
+ * Sets the locale for the current session only, leaving the user's default locale unchanged.
203
+ * Use {@link setUserProperties} with a `defaultLocale` property to change the default instead.
204
+ *
205
+ * @param locale The locale in Java's `Locale#toString()` form, e.g. `en_GB`.
206
+ * @param abortController Optional AbortController to allow cancellation of the request.
207
+ * @returns A Promise that resolves with the updated SessionStore after refetching the current user.
208
+ * It rejects with an {@link OpenmrsFetchError} if the server does not accept the locale.
209
+ *
210
+ * @example
211
+ * ```ts
212
+ * import { setSessionLocale } from '@openmrs/esm-api';
213
+ * await setSessionLocale('fr');
214
+ * ```
215
+ *
216
+ * @internal
217
+ */
218
+ export declare function setSessionLocale(locale: string, abortController?: AbortController): Promise<SessionStore>;
193
219
  /**
194
220
  * Updates the user properties for a specific user. User properties are key-value
195
221
  * pairs that store user-specific settings and preferences. After updating the
@@ -216,4 +242,40 @@ export declare function setSessionLocation(locationUuid: string, abortController
216
242
  export declare function setUserProperties(userUuid: string, userProperties: {
217
243
  [x: string]: string;
218
244
  }, abortController?: AbortController): Promise<SessionStore>;
245
+ /**
246
+ * Submits a TOTP code to complete a two-factor login challenge. When the code is accepted, the
247
+ * session store is refreshed with the now-authenticated session.
248
+ *
249
+ * @param code The code from the user's authenticator app.
250
+ * @param rememberDevice Whether the server should skip the challenge on this device in future.
251
+ * @returns A Promise resolving to the session. Its `authenticated` property is `false` if the
252
+ * code was not accepted. It rejects with an {@link OpenmrsFetchError} if the server rejects the
253
+ * request, in which case `responseBody` may explain why.
254
+ *
255
+ * @example
256
+ * ```ts
257
+ * import { verifyTotpCode } from '@openmrs/esm-api';
258
+ * const session = await verifyTotpCode('123456', true);
259
+ * ```
260
+ *
261
+ * @internal
262
+ */
263
+ export declare function verifyTotpCode(code: string, rememberDevice?: boolean): Promise<Session>;
264
+ /**
265
+ * Ends the current session on the server and records a logged-out session in the session store.
266
+ * A server that has already ended the session (401 or 403) counts as a successful logout.
267
+ *
268
+ * Other data cached for the logged-in user, such as SWR caches, is the caller's to clear.
269
+ *
270
+ * @returns A Promise that resolves once the session store holds a logged-out session.
271
+ *
272
+ * @example
273
+ * ```ts
274
+ * import { logout } from '@openmrs/esm-api';
275
+ * await logout();
276
+ * ```
277
+ *
278
+ * @internal
279
+ */
280
+ export declare function logout(): Promise<void>;
219
281
  //# sourceMappingURL=current-user.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"current-user.d.ts","sourceRoot":"","sources":["../src/current-user.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAiB,MAAM,SAAS,CAAC;AAEtG,MAAM,MAAM,YAAY,GAAG,kBAAkB,GAAG,oBAAoB,CAAC;AAErE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,IAAI,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,KAAK,CAAC;IACd,OAAO,EAAE,IAAI,CAAC;CACf,CAAC;AAEF,gBAAgB;AAChB,eAAO,MAAM,YAAY,0CAGvB,CAAC;AAoCH;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,iBAAS,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;AAC5C;;;;;GAKG;AACH,iBAAS,cAAc,CAAC,IAAI,EAAE;IAAE,iBAAiB,EAAE,IAAI,CAAA;CAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAC7E;;;;;GAKG;AACH,iBAAS,cAAc,CAAC,IAAI,EAAE;IAAE,iBAAiB,EAAE,KAAK,CAAA;CAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;AAmBnF,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,6CAG9B;AAiBD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,QAc5C;AAwBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,yBAwBtE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,SAK/B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAC3B,iBAAiB,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,EACzC,IAAI,EAAE;IAAE,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;CAAE,WAa3D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,0BAmB9B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAG/E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,kBAAkB,CAAC,YAAY,EAAE,MAAM,EAAE,eAAe,EAAE,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,CAW7G;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE;IACd,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACrB,EACD,eAAe,CAAC,EAAE,eAAe,GAChC,OAAO,CAAC,YAAY,CAAC,CAcvB"}
1
+ {"version":3,"file":"current-user.d.ts","sourceRoot":"","sources":["../src/current-user.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAiB,MAAM,SAAS,CAAC;AAEtG,MAAM,MAAM,YAAY,GAAG,kBAAkB,GAAG,oBAAoB,CAAC;AAErE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,IAAI,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,KAAK,CAAC;IACd,OAAO,EAAE,IAAI,CAAC;IACd,0EAA0E;IAC1E,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,CAAC;AAEF,gBAAgB;AAChB,eAAO,MAAM,YAAY,0CAGvB,CAAC;AAoCH;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,iBAAS,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;AAC5C;;;;;GAKG;AACH,iBAAS,cAAc,CAAC,IAAI,EAAE;IAAE,iBAAiB,EAAE,IAAI,CAAA;CAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAC7E;;;;;GAKG;AACH,iBAAS,cAAc,CAAC,IAAI,EAAE;IAAE,iBAAiB,EAAE,KAAK,CAAA;CAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;AAyBnF,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,6CAG9B;AAiBD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,QAc5C;AAwBD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,yBAyBtE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,SAQ/B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAC3B,iBAAiB,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,EACzC,IAAI,EAAE;IAAE,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;CAAE,WAa3D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,0BAmB9B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAG/E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,kBAAkB,CAAC,YAAY,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,CAY9G;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC,CAW/G;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE;IACd,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACrB,EACD,eAAe,CAAC,EAAE,eAAe,GAChC,OAAO,CAAC,YAAY,CAAC,CAcvB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAiB3F;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAY5C"}
@@ -1,7 +1,7 @@
1
1
  /** @module @category API */ import { reportError } from "@openmrs/esm-error-handling";
2
2
  import { createGlobalStore } from "@openmrs/esm-state";
3
3
  import { isUndefined } from "lodash-es";
4
- import { openmrsFetch, restBaseUrl, sessionEndpoint } from "./openmrs-fetch.js";
4
+ import { OpenmrsFetchError, openmrsFetch, restBaseUrl, sessionEndpoint } from "./openmrs-fetch.js";
5
5
  /** @internal */ export const sessionStore = createGlobalStore('session', {
6
6
  loaded: false,
7
7
  session: null
@@ -36,16 +36,22 @@ let inFlightRefresh = null;
36
36
  function getCurrentUser(opts) {
37
37
  const includeAuthStatus = opts?.includeAuthStatus ?? true;
38
38
  const select = (session)=>includeAuthStatus ? session : session.user;
39
- if (!refreshSessionIfStale()) {
39
+ const refresh = refreshSessionIfStale();
40
+ if (!refresh) {
40
41
  return Promise.resolve(select(sessionStore.getState().session));
41
42
  }
42
43
  return new Promise((resolve)=>{
43
- const unsubscribe = sessionStore.subscribe((state)=>{
44
+ const resolveIfLoaded = ()=>{
45
+ const state = sessionStore.getState();
44
46
  if (state.loaded) {
45
47
  unsubscribe();
46
48
  resolve(select(state.session));
47
49
  }
48
- });
50
+ };
51
+ const unsubscribe = sessionStore.subscribe(resolveIfLoaded);
52
+ // A failed refresh keeps an already-loaded session without updating the store, so the
53
+ // subscription alone would never fire.
54
+ refresh.then(resolveIfLoaded, resolveIfLoaded);
49
55
  });
50
56
  }
51
57
  export { getCurrentUser };
@@ -128,7 +134,12 @@ function isSuperUser(user) {
128
134
  * the user. All subscribers to the session store will be notified of the
129
135
  * new user once the new version of the user object is downloaded.
130
136
  *
131
- * @returns A Promise resolving to the updated session store state.
137
+ * If the server rejects the request as unauthenticated (401 or 403), the store records a
138
+ * logged-out session. If the request fails for any other reason, a session that has already
139
+ * loaded is kept; otherwise the store records the error.
140
+ *
141
+ * @returns A Promise resolving to the updated session store state. It rejects with the
142
+ * store state if the request fails for a reason other than authentication.
132
143
  *
133
144
  * @example
134
145
  * ```js
@@ -142,7 +153,8 @@ function isSuperUser(user) {
142
153
  headers['Authorization'] = `Basic ${window.btoa(`${username}:${password}`)}`;
143
154
  }
144
155
  const refresh = handleSessionResponse(openmrsFetch(sessionEndpoint, {
145
- headers
156
+ headers,
157
+ rejectAuthFailure: true
146
158
  }));
147
159
  // Publish the request so that readers can wait on it instead of reading the session it is about to
148
160
  // replace. Each call still issues its own request; this only tracks the most recent one.
@@ -173,7 +185,7 @@ function isSuperUser(user) {
173
185
  authenticated: false,
174
186
  sessionId: ''
175
187
  }
176
- });
188
+ }, true);
177
189
  }
178
190
  /**
179
191
  * Checks whether the given user has access based on the required privilege(s).
@@ -281,9 +293,39 @@ function isSuperUser(user) {
281
293
  headers: {
282
294
  'Content-Type': 'application/json'
283
295
  },
284
- signal: abortController.signal
296
+ rejectAuthFailure: true,
297
+ signal: abortController?.signal
285
298
  }));
286
299
  }
300
+ /**
301
+ * Sets the locale for the current session only, leaving the user's default locale unchanged.
302
+ * Use {@link setUserProperties} with a `defaultLocale` property to change the default instead.
303
+ *
304
+ * @param locale The locale in Java's `Locale#toString()` form, e.g. `en_GB`.
305
+ * @param abortController Optional AbortController to allow cancellation of the request.
306
+ * @returns A Promise that resolves with the updated SessionStore after refetching the current user.
307
+ * It rejects with an {@link OpenmrsFetchError} if the server does not accept the locale.
308
+ *
309
+ * @example
310
+ * ```ts
311
+ * import { setSessionLocale } from '@openmrs/esm-api';
312
+ * await setSessionLocale('fr');
313
+ * ```
314
+ *
315
+ * @internal
316
+ */ export async function setSessionLocale(locale, abortController) {
317
+ await openmrsFetch(sessionEndpoint, {
318
+ method: 'POST',
319
+ body: {
320
+ locale
321
+ },
322
+ headers: {
323
+ 'Content-Type': 'application/json'
324
+ },
325
+ signal: abortController?.signal
326
+ });
327
+ return refetchCurrentUser();
328
+ }
287
329
  /**
288
330
  * Updates the user properties for a specific user. User properties are key-value
289
331
  * pairs that store user-specific settings and preferences. After updating the
@@ -322,33 +364,114 @@ function isSuperUser(user) {
322
364
  });
323
365
  return refetchCurrentUser();
324
366
  }
367
+ /**
368
+ * Submits a TOTP code to complete a two-factor login challenge. When the code is accepted, the
369
+ * session store is refreshed with the now-authenticated session.
370
+ *
371
+ * @param code The code from the user's authenticator app.
372
+ * @param rememberDevice Whether the server should skip the challenge on this device in future.
373
+ * @returns A Promise resolving to the session. Its `authenticated` property is `false` if the
374
+ * code was not accepted. It rejects with an {@link OpenmrsFetchError} if the server rejects the
375
+ * request, in which case `responseBody` may explain why.
376
+ *
377
+ * @example
378
+ * ```ts
379
+ * import { verifyTotpCode } from '@openmrs/esm-api';
380
+ * const session = await verifyTotpCode('123456', true);
381
+ * ```
382
+ *
383
+ * @internal
384
+ */ export async function verifyTotpCode(code, rememberDevice = false) {
385
+ const response = await openmrsFetch(rememberDevice ? `${sessionEndpoint}?rememberMe=true` : sessionEndpoint, {
386
+ headers: {
387
+ 'X-Totp-Code': code
388
+ }
389
+ });
390
+ if (response.data?.authenticated) {
391
+ // `refetchCurrentUser` only resolves once the store holds a session.
392
+ const store = await refetchCurrentUser();
393
+ return store.session;
394
+ }
395
+ return response.data;
396
+ }
397
+ /**
398
+ * Ends the current session on the server and records a logged-out session in the session store.
399
+ * A server that has already ended the session (401 or 403) counts as a successful logout.
400
+ *
401
+ * Other data cached for the logged-in user, such as SWR caches, is the caller's to clear.
402
+ *
403
+ * @returns A Promise that resolves once the session store holds a logged-out session.
404
+ *
405
+ * @example
406
+ * ```ts
407
+ * import { logout } from '@openmrs/esm-api';
408
+ * await logout();
409
+ * ```
410
+ *
411
+ * @internal
412
+ */ export async function logout() {
413
+ try {
414
+ await openmrsFetch(sessionEndpoint, {
415
+ method: 'DELETE',
416
+ rejectAuthFailure: true
417
+ });
418
+ } catch (err) {
419
+ if (!isAuthFailure(err)) {
420
+ throw err;
421
+ }
422
+ }
423
+ clearCurrentUser();
424
+ // The session just cleared is already correct, so a failed refresh loses nothing.
425
+ await refetchCurrentUser().catch(()=>{});
426
+ }
325
427
  function handleSessionResponse(result) {
326
428
  return new Promise((resolve, reject)=>{
327
429
  result.then((res)=>{
328
- let nextState;
329
430
  if (typeof res?.data === 'object') {
330
- nextState = {
431
+ const nextState = {
331
432
  loaded: true,
332
433
  session: res.data
333
434
  };
334
- sessionStore.setState(nextState);
435
+ sessionStore.setState(nextState, true);
335
436
  resolve(nextState);
336
437
  } else {
337
- nextState = {
338
- loaded: false,
339
- session: null
340
- };
341
- sessionStore.setState(nextState);
342
- reject(nextState);
438
+ reject(recordSessionFailure(Error('The session endpoint did not respond with a session')));
343
439
  }
344
440
  }).catch((err)=>{
441
+ // An auth failure is the session endpoint's answer for "not logged in", not a failed lookup.
442
+ if (isAuthFailure(err)) {
443
+ if (isUnauthenticatedSession(err.responseBody)) {
444
+ sessionStore.setState({
445
+ loaded: true,
446
+ session: err.responseBody
447
+ }, true);
448
+ } else {
449
+ clearCurrentUser();
450
+ }
451
+ resolve(sessionStore.getState());
452
+ return;
453
+ }
345
454
  reportError(`Failed to fetch new session information: ${err}`);
346
- const nextState = {
347
- loaded: false,
348
- session: null
349
- };
350
- sessionStore.setState(nextState);
351
- reject(nextState);
455
+ reject(recordSessionFailure(err));
352
456
  });
353
457
  });
354
458
  }
459
+ function isAuthFailure(err) {
460
+ return err instanceof OpenmrsFetchError && (err.response.status === 401 || err.response.status === 403);
461
+ }
462
+ function isUnauthenticatedSession(body) {
463
+ return typeof body === 'object' && body !== null && body.authenticated === false;
464
+ }
465
+ /**
466
+ * A session that has already loaded is kept when a later fetch fails, since it is still the best answer
467
+ * available. With nothing to fall back on, the error is recorded so the app shell can show an error page.
468
+ */ function recordSessionFailure(err) {
469
+ if (!sessionStore.getState().loaded) {
470
+ sessionStore.setState({
471
+ loaded: false,
472
+ session: null,
473
+ error: err instanceof Error ? err : Error(String(err))
474
+ }, true);
475
+ }
476
+ return sessionStore.getState();
477
+ }
@@ -78,6 +78,11 @@ export declare class OpenmrsFetchError extends Error implements FetchError {
78
78
  export interface FetchConfig extends Omit<RequestInit, 'body' | 'headers'> {
79
79
  headers?: FetchHeaders;
80
80
  body?: FetchBody | string;
81
+ /**
82
+ * When `true`, a response that triggers the auth-failure redirect still navigates, but the
83
+ * returned promise rejects with an `OpenmrsFetchError` instead of staying pending.
84
+ */
85
+ rejectAuthFailure?: boolean;
81
86
  }
82
87
  type ResponseBody = string | FetchResponseJson;
83
88
  export interface FetchHeaders {
@@ -1 +1 @@
1
- {"version":3,"file":"openmrs-fetch.d.ts","sourceRoot":"","sources":["../src/openmrs-fetch.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAG7C,mEAAmE;AACnE,eAAO,MAAM,WAAW,gBAAgB,CAAC;AACzC,uEAAuE;AACvE,eAAO,MAAM,WAAW,iBAAiB,CAAC;AAC1C,sDAAsD;AACtD,eAAO,MAAM,eAAe,wBAA2B,CAAC;AAExD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,UASnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,WAAgB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAkM1G;AAED,qBAAa,iBAAkB,SAAQ,KAAM,YAAW,UAAU;gBACpD,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,GAAG,IAAI,EAAE,iBAAiB,EAAE,KAAK;IAQxG,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAAC;CACjD;AAED,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IACxE,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,IAAI,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;CAC3B;AAED,KAAK,YAAY,GAAG,MAAM,GAAG,iBAAiB,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,UAAU,SAAS;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;CACnC"}
1
+ {"version":3,"file":"openmrs-fetch.d.ts","sourceRoot":"","sources":["../src/openmrs-fetch.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAG7C,mEAAmE;AACnE,eAAO,MAAM,WAAW,gBAAgB,CAAC;AACzC,uEAAuE;AACvE,eAAO,MAAM,WAAW,iBAAiB,CAAC;AAC1C,sDAAsD;AACtD,eAAO,MAAM,eAAe,wBAA2B,CAAC;AAExD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,UASnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,WAAgB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAqL1G;AAED,qBAAa,iBAAkB,SAAQ,KAAM,YAAW,UAAU;gBACpD,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,GAAG,IAAI,EAAE,iBAAiB,EAAE,KAAK;IAQxG,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAAC;CACjD;AAED,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IACxE,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,IAAI,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,KAAK,YAAY,GAAG,MAAM,GAAG,iBAAiB,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,UAAU,SAAS;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;CACnC"}
@@ -104,26 +104,26 @@ import { defaultRedirectAuthFailureUrl } from "./config-schema.js";
104
104
  if (!fetchInit.headers) {
105
105
  fetchInit.headers = {};
106
106
  }
107
- /* Automatically stringify javascript objects being sent in the
108
- * request body.
109
- */ if (isPlainObject(fetchInit.body)) {
107
+ // Automatically stringify javascript objects being sent in the
108
+ // request body.
109
+ if (isPlainObject(fetchInit.body)) {
110
110
  fetchInit.body = JSON.stringify(fetchInit.body);
111
111
  }
112
- /* Add a request header to tell the server to respond with json,
113
- * since frontend code almost always wants json and the OpenMRS
114
- * server won't give you json unless you explicitly ask for it.
115
- * If a different Accept header is preferred, pass it into the fetchInit.
116
- * If no Accept header is desired, pass it in explicitly as null.
117
- */ if (typeof fetchInit.headers.Accept === 'undefined') {
112
+ // Add a request header to tell the server to respond with json,
113
+ // since frontend code almost always wants json and the OpenMRS
114
+ // server won't give you json unless you explicitly ask for it.
115
+ // If a different Accept header is preferred, pass it into the fetchInit.
116
+ // If no Accept header is desired, pass it in explicitly as null.
117
+ if (typeof fetchInit.headers.Accept === 'undefined') {
118
118
  fetchInit.headers.Accept = 'application/json';
119
119
  }
120
120
  if (fetchInit.headers.Accept === null) {
121
121
  delete fetchInit.headers.Accept;
122
122
  }
123
- /* This tells the OpenMRS REST API not to return a WWW-Authenticate
124
- * header. Returning that header is useful when using the API, but
125
- * not from a UI.
126
- */ let isRestRequest = false;
123
+ // This tells the OpenMRS REST API not to return a WWW-Authenticate
124
+ // header. Returning that header is useful when using the API, but
125
+ // not from a UI.
126
+ let isRestRequest = false;
127
127
  try {
128
128
  const requestUrl = new URL(url, window.location.href);
129
129
  const restUrl = new URL(makeUrl(restBaseUrl), window.location.href);
@@ -158,22 +158,20 @@ import { defaultRedirectAuthFailureUrl } from "./config-schema.js";
158
158
  url = urlUrl.toString();
159
159
  }
160
160
  }
161
- /* We capture the stacktrace before making the request, so that if an error occurs we can
162
- * log a full stacktrace that includes the code that made the request and handled the response
163
- * Otherwise, we could run into situations where the stacktrace doesn't even show which code
164
- * called @openmrs/api.
165
- */ const requestStacktrace = Error();
161
+ // We capture the stacktrace before making the request, so that if an error occurs we can
162
+ // log a full stacktrace that includes the code that made the request and handled the response
163
+ // Otherwise, we could run into situations where the stacktrace doesn't even show which code
164
+ // called @openmrs/api.
165
+ const requestStacktrace = Error();
166
166
  return window.fetch(url, fetchInit).then(async (r)=>{
167
167
  const response = r;
168
168
  const { redirectAuthFailure, followRedirects } = await getConfig('@openmrs/esm-api');
169
169
  if (response.ok) {
170
- /*
171
- * Backend modules can trigger SPA redirects by returning a `Location` header.
172
- * This is required because `fetch()` hides HTTP redirects from the application.
173
- *
174
- * - Session endpoint (2xx): Authentication challenge URLs (e.g. TOTP,2FA).
175
- * - HTTP 204: Logout redirect URLs (e.g. Keycloak IdP logout). Refer: OA-41 #1231
176
- */ const location = response.headers.get('location');
170
+ // Backend modules can trigger SPA redirects by returning a `Location` header.
171
+ // This is required because `fetch()` hides HTTP redirects from the application.
172
+ // Session endpoint (2xx): Authentication challenge URLs (e.g. TOTP,2FA).
173
+ // HTTP 204: Logout redirect URLs (e.g. Keycloak IdP logout). Refer: OA-41 #1231
174
+ const location = response.headers.get('location');
177
175
  const shouldRedirect = followRedirects && location && (url === makeUrl(sessionEndpoint) || response.status === 204);
178
176
  if (shouldRedirect) {
179
177
  navigate({
@@ -181,10 +179,10 @@ import { defaultRedirectAuthFailureUrl } from "./config-schema.js";
181
179
  });
182
180
  }
183
181
  if (response.status === 204) {
184
- /* HTTP 204 - No Content
185
- * We should not try to download the empty response as json. Instead,
186
- * we return null since there is no response body.
187
- */ response.data = null;
182
+ // HTTP 204 - No Content
183
+ // We should not try to download the empty response as json. Instead,
184
+ // we return null since there is no response body.
185
+ response.data = null;
188
186
  return response;
189
187
  } else {
190
188
  // HTTP 200s - The request succeeded
@@ -200,13 +198,12 @@ import { defaultRedirectAuthFailureUrl } from "./config-schema.js";
200
198
  });
201
199
  }
202
200
  } else {
203
- /* HTTP response status is not in 200s. Usually this will mean
204
- * either HTTP 400s (bad request from browser) or HTTP 500s (server error)
205
- * Our goal is to come up with best possible stacktrace and error message
206
- * to help developers understand the problem and debug
207
- */ /*
208
- * Redirect to given url when redirect on auth failure is enabled
209
- */ if (url === makeUrl(sessionEndpoint) && response.status === 403 || redirectAuthFailure.enabled && redirectAuthFailure.errors.includes(response.status)) {
201
+ // HTTP response status is not in 200s. Usually this will mean
202
+ // either HTTP 400s (bad request from browser) or HTTP 500s (server error)
203
+ // Our goal is to come up with best possible stacktrace and error message
204
+ // to help developers understand the problem and debug
205
+ // Redirect to given url when redirect on auth failure is enabled
206
+ if (url === makeUrl(sessionEndpoint) && response.status === 403 || redirectAuthFailure.enabled && redirectAuthFailure.errors.includes(response.status)) {
210
207
  clearHistory();
211
208
  // by default, redirect to the url specified in the config.
212
209
  // If blank, use the location header from the response.
@@ -215,28 +212,27 @@ import { defaultRedirectAuthFailureUrl } from "./config-schema.js";
215
212
  navigate({
216
213
  to: location
217
214
  });
218
- /* We sometimes don't really want this promise to resolve since there's no response data,
219
- * nor do we want it to reject because that would trigger error handling. We instead
220
- * want it to remain in pending status while the navigation occurs.
221
- */ return redirectAuthFailure.resolvePromise ? Promise.resolve() : new Promise(()=>{});
222
- } else {
223
- // Attempt to download a response body, if it has one
224
- return response.clone().text().then((responseText)=>{
225
- let responseBody = responseText;
226
- try {
227
- responseBody = JSON.parse(responseText);
228
- } catch (err) {
229
- // Server didn't respond with json, so just go with the response text string
230
- }
231
- /* Make the fetch promise go into "rejected" status, with the best
232
- * possible stacktrace and error message.
233
- */ throw new OpenmrsFetchError(url, response, responseBody, requestStacktrace);
234
- }, (err)=>{
235
- /* We weren't able to download a response body for this error.
236
- * Time to just give the best possible stacktrace and error message.
237
- */ throw new OpenmrsFetchError(url, response, null, requestStacktrace);
238
- });
215
+ const rejectAuthFailures = fetchInit.rejectAuthFailure === true;
216
+ if (!rejectAuthFailures) {
217
+ return redirectAuthFailure.resolvePromise ? Promise.resolve() : new Promise(()=>{});
218
+ }
239
219
  }
220
+ // Attempt to download a response body, if it has one
221
+ return response.clone().text().then((responseText)=>{
222
+ let responseBody = responseText;
223
+ try {
224
+ responseBody = JSON.parse(responseText);
225
+ } catch (err) {
226
+ // Server didn't respond with json, so just go with the response text string
227
+ }
228
+ // Make the fetch promise go into "rejected" status, with the best
229
+ // possible stacktrace and error message.
230
+ throw new OpenmrsFetchError(url, response, responseBody, requestStacktrace);
231
+ }, ()=>{
232
+ // We weren't able to download a response body for this error.
233
+ // Time to just give the best possible stacktrace and error message.
234
+ throw new OpenmrsFetchError(url, response, null, requestStacktrace);
235
+ });
240
236
  }
241
237
  });
242
238
  }
package/dist/public.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { clearCurrentUser, getCurrentUser, getLoggedInUser, getSessionStore, getSessionLocation, refetchCurrentUser, setSessionLocation, setUserLanguage, setUserProperties, userHasAccess, type LoadedSessionStore, type SessionStore, type UnloadedSessionStore, } from './current-user';
2
2
  export * from './environment';
3
3
  export * from './types';
4
- export * from './openmrs-fetch';
4
+ export { fhirBaseUrl, makeUrl, openmrsFetch, OpenmrsFetchError, restBaseUrl, sessionEndpoint, type FetchConfig, type FetchError, type FetchHeaders, type FetchResponseJson, } from './openmrs-fetch';
5
5
  export * from './openmrs-backend-dependencies';
6
6
  //# sourceMappingURL=public.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../src/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,oBAAoB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,SAAS,CAAC;AACxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,gCAAgC,CAAC"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../src/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,oBAAoB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,WAAW,EACX,OAAO,EACP,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,cAAc,gCAAgC,CAAC"}
package/dist/public.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { clearCurrentUser, getCurrentUser, getLoggedInUser, getSessionStore, getSessionLocation, refetchCurrentUser, setSessionLocation, setUserLanguage, setUserProperties, userHasAccess } from "./current-user.js";
2
2
  export * from "./environment.js";
3
3
  export * from "./types/index.js";
4
- export * from "./openmrs-fetch.js";
4
+ export { fhirBaseUrl, makeUrl, openmrsFetch, OpenmrsFetchError, restBaseUrl, sessionEndpoint } from "./openmrs-fetch.js";
5
5
  export * from "./openmrs-backend-dependencies.js";
package/mock-jest.ts CHANGED
@@ -17,5 +17,8 @@ export const clearCurrentUser = jest.fn();
17
17
  export const refetchCurrentUser = jest.fn();
18
18
  export const setUserLanguage = jest.fn();
19
19
  export const setUserProperties = jest.fn();
20
+ export const setSessionLocale = jest.fn(() => Promise.resolve());
21
+ export const verifyTotpCode = jest.fn();
22
+ export const logout = jest.fn(() => Promise.resolve());
20
23
  export const userHasAccess = jest.fn();
21
24
  export { OpenmrsFetchError } from './src/openmrs-fetch';
package/mock.ts CHANGED
@@ -18,5 +18,8 @@ export const clearCurrentUser = vi.fn();
18
18
  export const refetchCurrentUser = vi.fn();
19
19
  export const setUserLanguage = vi.fn();
20
20
  export const setUserProperties = vi.fn();
21
+ export const setSessionLocale = vi.fn(() => Promise.resolve());
22
+ export const verifyTotpCode = vi.fn();
23
+ export const logout = vi.fn(() => Promise.resolve());
21
24
  export const userHasAccess = vi.fn();
22
25
  export { OpenmrsFetchError } from './src/openmrs-fetch';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-api",
3
- "version": "10.0.1-pre.5607",
3
+ "version": "10.0.1-pre.5616",
4
4
  "license": "MPL-2.0",
5
5
  "description": "The javascript module for interacting with the OpenMRS API",
6
6
  "type": "module",
@@ -57,16 +57,16 @@
57
57
  "lodash-es": "^4.17.21"
58
58
  },
59
59
  "peerDependencies": {
60
- "@openmrs/esm-config": "^10.0.1-pre.5607",
61
- "@openmrs/esm-error-handling": "^10.0.1-pre.5607",
62
- "@openmrs/esm-globals": "^10.0.1-pre.5607",
63
- "@openmrs/esm-navigation": "^10.0.1-pre.5607"
60
+ "@openmrs/esm-config": "^10.0.1-pre.5616",
61
+ "@openmrs/esm-error-handling": "^10.0.1-pre.5616",
62
+ "@openmrs/esm-globals": "^10.0.1-pre.5616",
63
+ "@openmrs/esm-navigation": "^10.0.1-pre.5616"
64
64
  },
65
65
  "devDependencies": {
66
- "@openmrs/esm-config": "10.0.1-pre.5607",
67
- "@openmrs/esm-error-handling": "10.0.1-pre.5607",
68
- "@openmrs/esm-globals": "10.0.1-pre.5607",
69
- "@openmrs/esm-navigation": "10.0.1-pre.5607",
66
+ "@openmrs/esm-config": "10.0.1-pre.5616",
67
+ "@openmrs/esm-error-handling": "10.0.1-pre.5616",
68
+ "@openmrs/esm-globals": "10.0.1-pre.5616",
69
+ "@openmrs/esm-navigation": "10.0.1-pre.5616",
70
70
  "@swc/cli": "0.8.1",
71
71
  "@swc/core": "1.15.21",
72
72
  "@vitest/coverage-v8": "^4.1.2",