@ovok/core 0.4.37 → 0.4.38

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 (99) hide show
  1. package/dist/client/auth/account-storage.d.ts +1 -0
  2. package/dist/client/auth/account-storage.js +2 -1
  3. package/dist/{utils → client}/auth/exchange-code.d.ts +1 -1
  4. package/dist/{utils → client}/auth/generate-code-challenge-and-verifier.js +5 -17
  5. package/dist/{utils → client}/auth/handle-tenant-code.d.ts +1 -1
  6. package/dist/{utils → client}/auth/handle-tenant-code.js +1 -1
  7. package/dist/client/auth/methods/appleLogin.js +2 -6
  8. package/dist/client/auth/methods/changePassword.js +2 -1
  9. package/dist/client/auth/methods/getSessions.js +2 -1
  10. package/dist/client/auth/methods/googleLogin.js +2 -6
  11. package/dist/client/auth/methods/login.js +26 -44
  12. package/dist/client/auth/methods/logout.js +2 -1
  13. package/dist/client/auth/methods/requestDeleteUser.js +5 -4
  14. package/dist/client/auth/methods/revokeSessions.js +2 -1
  15. package/dist/client/auth/methods/socialLogin.d.ts +7 -0
  16. package/dist/client/auth/methods/socialLogin.js +11 -0
  17. package/dist/client/auth/session-fetch.js +5 -6
  18. package/dist/client/auth/session-http.js +8 -19
  19. package/dist/client/auth/sha256.d.ts +2 -0
  20. package/dist/client/auth/sha256.js +65 -0
  21. package/dist/{utils → client}/auth/start-login.d.ts +2 -2
  22. package/dist/client/change-history/methods.js +2 -1
  23. package/dist/client/cms/methods.js +16 -28
  24. package/dist/client/consent/methods.js +3 -2
  25. package/dist/client/email/methods.js +2 -1
  26. package/dist/client/errors/ovok-error-utils.d.ts +6 -0
  27. package/dist/client/errors/ovok-error-utils.js +10 -1
  28. package/dist/client/errors/ovok-error.js +6 -13
  29. package/dist/client/errors/rate-limit-error.js +2 -6
  30. package/dist/client/experience/methods.js +13 -12
  31. package/dist/client/invites/methods.js +8 -12
  32. package/dist/client/notifications/methods.js +4 -3
  33. package/dist/client/observation/fhir-systems.d.ts +4 -0
  34. package/dist/client/observation/fhir-systems.js +7 -0
  35. package/dist/client/observation/methods/generateObservationBodiesByMeasurement.js +23 -29
  36. package/dist/client/observation/methods/getLatestObservationsByCodes.js +6 -9
  37. package/dist/client/observation/methods/observationsToMeasurements.js +44 -91
  38. package/dist/client/observation/services/base-observation-service.js +4 -3
  39. package/dist/client/observation/services/blood-pressure.js +1 -4
  40. package/dist/client/observation/services/{baby-sleep.d.ts → component-observation-services.d.ts} +18 -13
  41. package/dist/client/observation/services/component-observation-services.js +69 -0
  42. package/dist/client/observation/services/creator.d.ts +1 -2
  43. package/dist/client/observation/services/creator.js +14 -18
  44. package/dist/client/observation/services/ecg-observation-fragments.js +5 -6
  45. package/dist/client/observation/services/ecg.js +3 -2
  46. package/dist/client/observation/services/lipid-panel.js +8 -12
  47. package/dist/client/observation/services/measurement-time.d.ts +4 -0
  48. package/dist/client/observation/services/measurement-time.js +6 -0
  49. package/dist/client/observation/services/single-value-services.d.ts +5 -0
  50. package/dist/client/observation/services/single-value-services.js +6 -1
  51. package/dist/client/observation/services/uric-acid-service.js +6 -7
  52. package/dist/client/observation/types/generate-measurement/HealthConnectTypes.d.ts +2 -458
  53. package/dist/client/observation/types/generate-measurement/HealthConnectTypes.js +0 -6
  54. package/dist/client/offline/offline-measurement-queue.js +31 -28
  55. package/dist/client/ovok-client.d.ts +5 -2
  56. package/dist/client/ovok-client.js +46 -42
  57. package/dist/client/patient/methods.js +25 -25
  58. package/dist/client/patient-sharing/methods.js +4 -3
  59. package/dist/client/platform/methods.js +17 -16
  60. package/dist/client/platform/types.d.ts +3 -6
  61. package/dist/client/questionnaire-response/methods/createQuestionnaireResponseWithObservations.js +2 -1
  62. package/dist/client/questionnaire-response/methods/submitQuestionnaireResponse.js +2 -1
  63. package/dist/client/request-options.d.ts +2 -0
  64. package/dist/client/request-options.js +6 -0
  65. package/dist/client/signals/errors.js +2 -6
  66. package/dist/client/signals/methods.js +14 -13
  67. package/dist/client/slim/live-read.d.ts +4 -0
  68. package/dist/client/slim/live-read.js +87 -0
  69. package/dist/client/slim/methods.d.ts +3 -0
  70. package/dist/client/slim/methods.js +63 -146
  71. package/dist/client/storage.js +3 -2
  72. package/dist/conformance/capability-requirements.js +1 -1
  73. package/dist/conformance/capability.js +8 -7
  74. package/dist/hooks/account-hooks.js +1 -1
  75. package/dist/hooks/api-hooks.d.ts +5 -6
  76. package/dist/hooks/api-hooks.js +10 -24
  77. package/dist/hooks/cms-hooks.js +8 -13
  78. package/dist/hooks/experience-hooks.js +1 -1
  79. package/dist/hooks/index.d.ts +2 -2
  80. package/dist/hooks/index.js +4 -4
  81. package/dist/hooks/invites-hooks.js +0 -1
  82. package/dist/hooks/notification-hooks.d.ts +4 -1
  83. package/dist/hooks/notification-hooks.js +28 -35
  84. package/dist/hooks/notification-invalidation.d.ts +1 -1
  85. package/dist/hooks/notification-note-hooks.js +13 -28
  86. package/dist/hooks/observation-hooks.js +2 -4
  87. package/dist/hooks/patient-hooks.js +1 -1
  88. package/dist/hooks/patient-sharing-hooks.js +1 -2
  89. package/dist/hooks/poll-hooks.js +10 -8
  90. package/dist/hooks/signals-alert-hooks.js +4 -6
  91. package/package.json +1 -1
  92. package/dist/client/observation/services/baby-sleep.js +0 -69
  93. package/dist/client/observation/services/respiratory-therapy.d.ts +0 -28
  94. package/dist/client/observation/services/respiratory-therapy.js +0 -69
  95. package/dist/utils/auth/sha256.d.ts +0 -8
  96. package/dist/utils/auth/sha256.js +0 -300
  97. /package/dist/{utils → client}/auth/exchange-code.js +0 -0
  98. /package/dist/{utils → client}/auth/generate-code-challenge-and-verifier.d.ts +0 -0
  99. /package/dist/{utils → client}/auth/start-login.js +0 -0
@@ -1,5 +1,6 @@
1
1
  import { IClientStorage } from "@medplum/core";
2
2
  export declare const LOGINS_STORAGE_KEY = "logins";
3
+ export declare const ACTIVE_LOGIN_STORAGE_KEY = "activeLogin";
3
4
  export declare const EXPIRED_ACCOUNTS_STORAGE_KEY = "ovok.core.expired-accounts";
4
5
  export declare const setStoredAccountExpired: (storage: IClientStorage, accountId: string, expired: boolean) => void;
5
6
  export declare const removeStoredAccount: (storage: IClientStorage, accountId: string) => void;
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.removeStoredAccount = exports.setStoredAccountExpired = exports.EXPIRED_ACCOUNTS_STORAGE_KEY = exports.LOGINS_STORAGE_KEY = void 0;
3
+ exports.removeStoredAccount = exports.setStoredAccountExpired = exports.EXPIRED_ACCOUNTS_STORAGE_KEY = exports.ACTIVE_LOGIN_STORAGE_KEY = exports.LOGINS_STORAGE_KEY = void 0;
4
4
  exports.LOGINS_STORAGE_KEY = "logins";
5
+ exports.ACTIVE_LOGIN_STORAGE_KEY = "activeLogin";
5
6
  exports.EXPIRED_ACCOUNTS_STORAGE_KEY = "ovok.core.expired-accounts";
6
7
  const setStoredAccountExpired = (storage, accountId, expired) => {
7
8
  var _a;
@@ -1,6 +1,6 @@
1
1
  import type { MedplumRequestOptions } from "@medplum/core";
2
- import { OvokClient } from "../../client/ovok-client";
3
2
  import { AuthResponse } from "../../types";
3
+ import { OvokClient } from "../ovok-client";
4
4
  type ExchangeCodeProps = {
5
5
  client: OvokClient;
6
6
  sessionCode: string;
@@ -6,26 +6,16 @@ function generateRandomString(length) {
6
6
  const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
7
7
  let result = "";
8
8
  const randomValues = new Uint8Array(length);
9
- if (typeof crypto !== "undefined" && crypto.getRandomValues) {
10
- crypto.getRandomValues(randomValues);
11
- }
12
- else {
13
- for (let i = 0; i < length; i++) {
14
- randomValues[i] = Math.floor(Math.random() * 256);
15
- }
9
+ // A predictable verifier defeats PKCE, so there is no Math.random fallback.
10
+ if (typeof crypto === "undefined" || typeof crypto.getRandomValues !== "function") {
11
+ throw new Error("PKCE needs crypto.getRandomValues. On React Native, install a polyfill such as react-native-get-random-values (or use @ovok/native, which provides one).");
16
12
  }
13
+ crypto.getRandomValues(randomValues);
17
14
  for (let i = 0; i < length; i++) {
18
15
  result += charset[randomValues[i] % charset.length];
19
16
  }
20
17
  return result;
21
18
  }
22
- function hexToUint8Array(hex) {
23
- const bytes = new Uint8Array(hex.length / 2);
24
- for (let i = 0; i < hex.length; i += 2) {
25
- bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
26
- }
27
- return bytes;
28
- }
29
19
  function base64URLEncode(data) {
30
20
  let binary = "";
31
21
  for (let i = 0; i < data.length; i++) {
@@ -35,9 +25,7 @@ function base64URLEncode(data) {
35
25
  }
36
26
  function generateCodeChallengeAndVerifier() {
37
27
  const codeVerifier = generateRandomString(128);
38
- const hexHash = sha256_1.sha256.hex(codeVerifier);
39
- const hashBytes = hexToUint8Array(hexHash);
40
- const codeChallenge = base64URLEncode(hashBytes);
28
+ const codeChallenge = base64URLEncode((0, sha256_1.sha256Bytes)(codeVerifier));
41
29
  return {
42
30
  codeVerifier,
43
31
  codeChallenge,
@@ -1,4 +1,4 @@
1
- import { StartLoginResponse } from "../../client/auth/types/StartLoginResponse";
1
+ import { StartLoginResponse } from "./types/StartLoginResponse";
2
2
  export declare const handleTenantCode: ({ profiles, tenantCode, type, }: {
3
3
  profiles: StartLoginResponse["profiles"];
4
4
  tenantCode?: string;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handleTenantCode = void 0;
4
- const LoginError_1 = require("../../client/auth/types/LoginError");
4
+ const LoginError_1 = require("./types/LoginError");
5
5
  const handleTenantCode = ({ profiles, tenantCode, type, }) => {
6
6
  if (type !== "Practitioner") {
7
7
  if (!tenantCode) {
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.appleLogin = appleLogin;
4
+ const socialLogin_1 = require("./socialLogin");
4
5
  async function appleLogin(body, requestOptions) {
5
- const { internalId, ...rest } = body;
6
- const queryParams = new URLSearchParams({
7
- client: `ClientApplication/${this.socialLoginClientId}`,
8
- internalId,
9
- });
10
- return this.post(`auth/external/apple?${queryParams.toString()}`, rest, "application/json", requestOptions);
6
+ return socialLogin_1.socialLogin.call(this, "apple", body, requestOptions);
11
7
  }
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.changePassword = changePassword;
4
+ const request_options_1 = require("../../request-options");
4
5
  async function changePassword(body, requestOptions) {
5
6
  const { oldPassword, newPassword } = body;
6
- return this.patch("v2/auth/change-password", { oldPassword, newPassword }, ...(requestOptions === undefined ? [] : [requestOptions]));
7
+ return this.patch("v2/auth/change-password", { oldPassword, newPassword }, ...(0, request_options_1.optionalArg)(requestOptions));
7
8
  }
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getSessions = getSessions;
4
+ const request_options_1 = require("../../request-options");
4
5
  /** Lists the server-side sessions for the current account. */
5
6
  async function getSessions(requestOptions) {
6
- return this.get("auth/session", ...(requestOptions === undefined ? [] : [requestOptions]));
7
+ return this.get("auth/session", ...(0, request_options_1.optionalArg)(requestOptions));
7
8
  }
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.googleLogin = googleLogin;
4
+ const socialLogin_1 = require("./socialLogin");
4
5
  async function googleLogin(body, requestOptions) {
5
- const { internalId, ...rest } = body;
6
- const queryParams = new URLSearchParams({
7
- client: `ClientApplication/${this.socialLoginClientId}`,
8
- internalId,
9
- });
10
- return this.post(`auth/external/google?${queryParams.toString()}`, rest, "application/json", requestOptions);
6
+ return socialLogin_1.socialLogin.call(this, "google", body, requestOptions);
11
7
  }
@@ -2,11 +2,20 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.startTenantLogin = startTenantLogin;
4
4
  exports.login = login;
5
- const exchange_code_1 = require("../../../utils/auth/exchange-code");
6
- const generate_code_challenge_and_verifier_1 = require("../../../utils/auth/generate-code-challenge-and-verifier");
7
- const handle_tenant_code_1 = require("../../../utils/auth/handle-tenant-code");
8
- const start_login_1 = require("../../../utils/auth/start-login");
5
+ const request_options_1 = require("../../request-options");
6
+ const exchange_code_1 = require("../exchange-code");
7
+ const generate_code_challenge_and_verifier_1 = require("../generate-code-challenge-and-verifier");
8
+ const handle_tenant_code_1 = require("../handle-tenant-code");
9
+ const start_login_1 = require("../start-login");
9
10
  const LoginError_1 = require("../types/LoginError");
11
+ /** The MFA step: verifying the code yields a session code that `exchange` turns into a session. */
12
+ const mfaStep = (client, type, loginId, exchange, requestOptions) => ({
13
+ nextStep: "mfa",
14
+ verify: async (code) => {
15
+ const { sessionCode } = await client.post(`auth/tenant/${type}/login/mfa`, { loginId, mfaToken: code }, "application/json", requestOptions);
16
+ return exchange(sessionCode);
17
+ },
18
+ });
10
19
  /** Starts practitioner sign-in and lets the app choose among returned projects. */
11
20
  async function startTenantLogin(body, requestOptions) {
12
21
  const { codeVerifier, codeChallenge } = (0, generate_code_challenge_and_verifier_1.generateCodeChallengeAndVerifier)();
@@ -23,36 +32,23 @@ async function startTenantLogin(body, requestOptions) {
23
32
  if (!tenantCode) {
24
33
  throw new LoginError_1.LoginError("tenant_code_required", "Choose a project before completing sign-in.", profiles);
25
34
  }
26
- if (startResponse.nextStep === "mfa") {
27
- return {
28
- nextStep: "mfa",
29
- verify: async (code) => {
30
- const mfaResponse = await this.post("auth/tenant/Practitioner/login/mfa", { loginId: startResponse.loginId, mfaToken: code }, "application/json", requestOptions);
31
- return (0, exchange_code_1.exchangeCode)({
32
- client: this,
33
- type: "Practitioner",
34
- sessionCode: mfaResponse.sessionCode,
35
- codeVerifier,
36
- tenantCode,
37
- requestOptions,
38
- });
39
- },
40
- };
41
- }
42
- return (0, exchange_code_1.exchangeCode)({
35
+ const exchange = (sessionCode) => (0, exchange_code_1.exchangeCode)({
43
36
  client: this,
44
37
  type: "Practitioner",
45
- sessionCode: startResponse.sessionCode,
38
+ sessionCode,
46
39
  codeVerifier,
47
40
  tenantCode,
48
41
  requestOptions,
49
42
  });
43
+ return startResponse.nextStep === "mfa"
44
+ ? mfaStep(this, "Practitioner", startResponse.loginId, exchange, requestOptions)
45
+ : exchange(startResponse.sessionCode);
50
46
  },
51
47
  };
52
48
  }
53
49
  async function login(body, requestOptions) {
54
50
  if (body.type === "Practitioner") {
55
- const start = await startTenantLogin.call(this, body, ...(requestOptions === undefined ? [] : [requestOptions]));
51
+ const start = await startTenantLogin.call(this, body, ...(0, request_options_1.optionalArg)(requestOptions));
56
52
  return start.exchange(body.tenantCode);
57
53
  }
58
54
  const { codeVerifier, codeChallenge } = (0, generate_code_challenge_and_verifier_1.generateCodeChallengeAndVerifier)();
@@ -62,30 +58,16 @@ async function login(body, requestOptions) {
62
58
  codeChallenge,
63
59
  requestOptions,
64
60
  });
65
- if (startResponse.nextStep === "mfa") {
66
- return {
67
- nextStep: "mfa",
68
- verify: async (code) => {
69
- const mfaResponse = await this.post(`auth/tenant/${body.type}/login/mfa`, {
70
- loginId: startResponse.loginId,
71
- mfaToken: code,
72
- }, "application/json", requestOptions);
73
- return (0, exchange_code_1.exchangeCode)({
74
- client: this,
75
- type: "Patient",
76
- sessionCode: mfaResponse.sessionCode,
77
- codeVerifier,
78
- requestOptions,
79
- });
80
- },
81
- };
82
- }
83
- (0, handle_tenant_code_1.handleTenantCode)({ profiles: startResponse.profiles, tenantCode: body.tenantCode, type: "Patient" });
84
- return (0, exchange_code_1.exchangeCode)({
61
+ const exchange = (sessionCode) => (0, exchange_code_1.exchangeCode)({
85
62
  client: this,
86
63
  type: "Patient",
87
- sessionCode: startResponse.sessionCode,
64
+ sessionCode,
88
65
  codeVerifier,
89
66
  requestOptions,
90
67
  });
68
+ if (startResponse.nextStep === "mfa") {
69
+ return mfaStep(this, body.type, startResponse.loginId, exchange, requestOptions);
70
+ }
71
+ (0, handle_tenant_code_1.handleTenantCode)({ profiles: startResponse.profiles, tenantCode: body.tenantCode, type: "Patient" });
72
+ return exchange(startResponse.sessionCode);
91
73
  }
@@ -1,13 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.logout = logout;
4
+ const request_options_1 = require("../../request-options");
4
5
  const account_storage_1 = require("../account-storage");
5
6
  /** Revokes the current server session and always removes its local account. */
6
7
  async function logout(requestOptions) {
7
8
  var _a, _b;
8
9
  const accountId = (_b = (_a = this.getActiveLogin()) === null || _a === void 0 ? void 0 : _a.profile) === null || _b === void 0 ? void 0 : _b.reference;
9
10
  try {
10
- await this.delete("auth/session/current", ...(requestOptions === undefined ? [] : [requestOptions]));
11
+ await this.delete("auth/session/current", ...(0, request_options_1.optionalArg)(requestOptions));
11
12
  }
12
13
  finally {
13
14
  this.clearActiveLogin();
@@ -9,8 +9,9 @@ exports.requestDeleteUser = requestDeleteUser;
9
9
  * @returns {Promise<RequestDeleteUserResponse>} Response containing scheduled deletion status and user information
10
10
  */
11
11
  async function requestDeleteUser(body, requestOptions) {
12
- return this.delete("auth/delete", { ...{
13
- body: JSON.stringify(body),
14
- headers: { "Content-Type": "application/json" },
15
- }, ...requestOptions });
12
+ return this.delete("auth/delete", {
13
+ body: JSON.stringify(body),
14
+ headers: { "Content-Type": "application/json" },
15
+ ...requestOptions,
16
+ });
16
17
  }
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.revokeSessions = revokeSessions;
4
+ const request_options_1 = require("../../request-options");
4
5
  /** Revokes the selected server-side session or session group. */
5
6
  async function revokeSessions(option, requestOptions) {
6
- await this.delete(`auth/session/${encodeURIComponent(option)}`, ...(requestOptions === undefined ? [] : [requestOptions]));
7
+ await this.delete(`auth/session/${encodeURIComponent(option)}`, ...(0, request_options_1.optionalArg)(requestOptions));
7
8
  if (option === "current") {
8
9
  this.clearActiveLogin();
9
10
  }
@@ -0,0 +1,7 @@
1
+ import type { MedplumRequestOptions } from "@medplum/core";
2
+ import { OvokClient } from "../../ovok-client";
3
+ import { AppleLoginBody } from "../types/AppleLoginBody";
4
+ import { AuthResponse } from "../types/AuthResponse";
5
+ import { GoogleLoginBody } from "../types/GoogleLoginBody";
6
+ /** Signs in with a provider token through the social-login client. Not bound onto OvokClient itself. */
7
+ export declare function socialLogin(this: OvokClient, provider: "apple" | "google", { internalId, ...rest }: AppleLoginBody | GoogleLoginBody, requestOptions?: MedplumRequestOptions): Promise<AuthResponse>;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.socialLogin = socialLogin;
4
+ /** Signs in with a provider token through the social-login client. Not bound onto OvokClient itself. */
5
+ function socialLogin(provider, { internalId, ...rest }, requestOptions) {
6
+ const queryParams = new URLSearchParams({
7
+ client: `ClientApplication/${this.socialLoginClientId}`,
8
+ internalId,
9
+ });
10
+ return this.post(`auth/external/${provider}?${queryParams.toString()}`, rest, "application/json", requestOptions);
11
+ }
@@ -19,15 +19,14 @@ function createSessionFetch({ config, storage, fetchImplementation, normalizedBa
19
19
  (options === null || options === void 0 ? void 0 : options.method) === "POST" &&
20
20
  typeof options.body === "string" &&
21
21
  new URLSearchParams(options.body).get("grant_type") === "refresh_token";
22
+ const activeAccountId = () => { var _a, _b; return (_b = (_a = storage.getObject(account_storage_1.ACTIVE_LOGIN_STORAGE_KEY)) === null || _a === void 0 ? void 0 : _a.profile) === null || _b === void 0 ? void 0 : _b.reference; };
22
23
  const markActiveAccountExpired = () => {
23
- var _a, _b;
24
- const accountId = (_b = (_a = storage.getObject("activeLogin")) === null || _a === void 0 ? void 0 : _a.profile) === null || _b === void 0 ? void 0 : _b.reference;
24
+ const accountId = activeAccountId();
25
25
  if (accountId) {
26
26
  (0, account_storage_1.setStoredAccountExpired)(storage, accountId, true);
27
27
  }
28
28
  };
29
29
  const fetchOnce = async (url, options) => {
30
- var _a, _b;
31
30
  const response = await (0, session_http_1.fetchWithTimeout)(fetchImplementation, url, options, config.requestTimeoutMs);
32
31
  const requestUrl = new URL(url, normalizedBaseUrl);
33
32
  if (isTokenRefresh(url, options)) {
@@ -36,7 +35,7 @@ function createSessionFetch({ config, storage, fetchImplementation, normalizedBa
36
35
  try {
37
36
  invalidGrant = (await response.clone().json()).error === "invalid_grant";
38
37
  }
39
- catch (_c) {
38
+ catch (_a) {
40
39
  // Do not sign out unless the response confirms that the refresh token was revoked.
41
40
  }
42
41
  if (!invalidGrant) {
@@ -51,7 +50,7 @@ function createSessionFetch({ config, storage, fetchImplementation, normalizedBa
51
50
  markActiveAccountExpired();
52
51
  }
53
52
  else if (response.ok) {
54
- const accountId = (_b = (_a = storage.getObject("activeLogin")) === null || _a === void 0 ? void 0 : _a.profile) === null || _b === void 0 ? void 0 : _b.reference;
53
+ const accountId = activeAccountId();
55
54
  if (accountId) {
56
55
  (0, account_storage_1.setStoredAccountExpired)(storage, accountId, false);
57
56
  }
@@ -82,7 +81,7 @@ function createSessionFetch({ config, storage, fetchImplementation, normalizedBa
82
81
  }
83
82
  return response;
84
83
  }
85
- const login = storage.getObject("activeLogin");
84
+ const login = storage.getObject(account_storage_1.ACTIVE_LOGIN_STORAGE_KEY);
86
85
  if (terminalRefreshError) {
87
86
  if ((login === null || login === void 0 ? void 0 : login.accessToken) && login.accessToken !== terminalRefreshToken) {
88
87
  terminalRefreshToken = undefined;
@@ -16,11 +16,7 @@ const retryAfterMs = (response) => {
16
16
  return Number.isFinite(date) ? Math.max(0, date - Date.now()) : undefined;
17
17
  };
18
18
  exports.retryAfterMs = retryAfterMs;
19
- const timeoutError = () => {
20
- const error = new Error("The request timed out.");
21
- error.name = "TimeoutError";
22
- return error;
23
- };
19
+ const timeoutError = () => (0, ovok_error_utils_1.namedError)("TimeoutError", "The request timed out.");
24
20
  const withHttpErrorMetadata = async (response) => {
25
21
  var _a, _b;
26
22
  if (response.ok || typeof response.clone !== "function") {
@@ -94,9 +90,7 @@ exports.withHttpErrorMetadata = withHttpErrorMetadata;
94
90
  const waitForRetry = (milliseconds, signal) => new Promise((resolve, reject) => {
95
91
  var _a;
96
92
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
97
- const error = new Error("Token refresh aborted");
98
- error.name = "AbortError";
99
- reject((_a = signal.reason) !== null && _a !== void 0 ? _a : error);
93
+ reject((_a = signal.reason) !== null && _a !== void 0 ? _a : (0, ovok_error_utils_1.namedError)("AbortError", "Token refresh aborted"));
100
94
  return;
101
95
  }
102
96
  const timer = setTimeout(() => {
@@ -106,9 +100,7 @@ const waitForRetry = (milliseconds, signal) => new Promise((resolve, reject) =>
106
100
  const abort = () => {
107
101
  var _a;
108
102
  clearTimeout(timer);
109
- const error = new Error("Token refresh aborted");
110
- error.name = "AbortError";
111
- reject((_a = signal === null || signal === void 0 ? void 0 : signal.reason) !== null && _a !== void 0 ? _a : error);
103
+ reject((_a = signal === null || signal === void 0 ? void 0 : signal.reason) !== null && _a !== void 0 ? _a : (0, ovok_error_utils_1.namedError)("AbortError", "Token refresh aborted"));
112
104
  };
113
105
  signal === null || signal === void 0 ? void 0 : signal.addEventListener("abort", abort, { once: true });
114
106
  });
@@ -116,17 +108,14 @@ exports.waitForRetry = waitForRetry;
116
108
  const isTemporaryRefreshFailure = (status) => status === 429 || (status >= 500 && status <= 599);
117
109
  exports.isTemporaryRefreshFailure = isTemporaryRefreshFailure;
118
110
  const refreshUnavailableError = (response) => {
119
- const error = new Error("Token refresh is temporarily unavailable");
120
- error.name = "NetworkError";
121
- Object.assign(error, { cause: response });
122
- return error;
111
+ return Object.assign((0, ovok_error_utils_1.namedError)("NetworkError", "Token refresh is temporarily unavailable"), { cause: response });
123
112
  };
124
113
  exports.refreshUnavailableError = refreshUnavailableError;
125
114
  const refreshResponseError = (response) => {
126
- const error = new Error(`Token refresh returned HTTP ${response.status}`);
127
- error.name = "TokenRefreshError";
128
- Object.assign(error, { status: response.status, cause: response });
129
- return error;
115
+ return Object.assign((0, ovok_error_utils_1.namedError)("TokenRefreshError", `Token refresh returned HTTP ${response.status}`), {
116
+ status: response.status,
117
+ cause: response,
118
+ });
130
119
  };
131
120
  exports.refreshResponseError = refreshResponseError;
132
121
  const isNetworkError = (error) => {
@@ -0,0 +1,2 @@
1
+ /** SHA-256 digest of an ASCII string (a PKCE verifier is `[A-Za-z0-9-._~]`). */
2
+ export declare const sha256Bytes: (ascii: string) => Uint8Array;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ // SHA-256 (FIPS 180-4), synchronous and dependency-free: React Native has no `crypto.subtle`,
3
+ // and the PKCE login start is synchronous.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.sha256Bytes = void 0;
6
+ const K = new Uint32Array([
7
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
8
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
9
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
10
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
11
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
12
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
13
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
14
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
15
+ ]);
16
+ const rotr = (x, n) => (x >>> n) | (x << (32 - n));
17
+ /** SHA-256 digest of an ASCII string (a PKCE verifier is `[A-Za-z0-9-._~]`). */
18
+ const sha256Bytes = (ascii) => {
19
+ const blocks = new Uint8Array(Math.ceil((ascii.length + 9) / 64) * 64);
20
+ const view = new DataView(blocks.buffer);
21
+ for (let i = 0; i < ascii.length; i += 1) {
22
+ const code = ascii.charCodeAt(i);
23
+ if (code > 0x7f) {
24
+ throw new RangeError("sha256Bytes hashes ASCII input only.");
25
+ }
26
+ blocks[i] = code;
27
+ }
28
+ blocks[ascii.length] = 0x80;
29
+ view.setUint32(blocks.length - 4, ascii.length * 8);
30
+ const hash = new Uint32Array([
31
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
32
+ ]);
33
+ const w = new Uint32Array(64);
34
+ for (let offset = 0; offset < blocks.length; offset += 64) {
35
+ for (let t = 0; t < 16; t += 1) {
36
+ w[t] = view.getUint32(offset + t * 4);
37
+ }
38
+ for (let t = 16; t < 64; t += 1) {
39
+ const s0 = rotr(w[t - 15], 7) ^ rotr(w[t - 15], 18) ^ (w[t - 15] >>> 3);
40
+ const s1 = rotr(w[t - 2], 17) ^ rotr(w[t - 2], 19) ^ (w[t - 2] >>> 10);
41
+ w[t] = w[t - 16] + s0 + w[t - 7] + s1;
42
+ }
43
+ let [a, b, c, d, e, f, g, h] = hash;
44
+ for (let t = 0; t < 64; t += 1) {
45
+ const t1 = (h + (rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)) + ((e & f) ^ (~e & g)) + K[t] + w[t]) >>> 0;
46
+ const t2 = ((rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)) + ((a & b) ^ (a & c) ^ (b & c))) >>> 0;
47
+ h = g;
48
+ g = f;
49
+ f = e;
50
+ e = (d + t1) >>> 0;
51
+ d = c;
52
+ c = b;
53
+ b = a;
54
+ a = (t1 + t2) >>> 0;
55
+ }
56
+ [a, b, c, d, e, f, g, h].forEach((value, index) => {
57
+ hash[index] += value;
58
+ });
59
+ }
60
+ const digest = new Uint8Array(32);
61
+ const digestView = new DataView(digest.buffer);
62
+ hash.forEach((word, index) => digestView.setUint32(index * 4, word));
63
+ return digest;
64
+ };
65
+ exports.sha256Bytes = sha256Bytes;
@@ -1,7 +1,7 @@
1
1
  import type { MedplumRequestOptions } from "@medplum/core";
2
- import { StartLoginResponse } from "../../client/auth/types/StartLoginResponse";
3
- import { OvokClient } from "../../client/ovok-client";
4
2
  import { LoginBody } from "../../types";
3
+ import { OvokClient } from "../ovok-client";
4
+ import { StartLoginResponse } from "./types/StartLoginResponse";
5
5
  type StartLoginProps = {
6
6
  client: OvokClient;
7
7
  body: LoginBody;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.serializeChangeHistoryQuery = serializeChangeHistoryQuery;
4
4
  exports.listChangeHistory = listChangeHistory;
5
+ const request_options_1 = require("../request-options");
5
6
  function serializeChangeHistoryQuery(params = {}) {
6
7
  const search = new URLSearchParams();
7
8
  for (const [key, value] of Object.entries(params)) {
@@ -21,5 +22,5 @@ function serializeChangeHistoryQuery(params = {}) {
21
22
  return encoded ? `?${encoded}` : "";
22
23
  }
23
24
  function listChangeHistory(params = {}, requestOptions) {
24
- return this.get(`/v1/slim/change-history${serializeChangeHistoryQuery(params)}`, ...(requestOptions === undefined ? [] : [requestOptions]));
25
+ return this.get(`/v1/slim/change-history${serializeChangeHistoryQuery(params)}`, ...(0, request_options_1.optionalArg)(requestOptions));
25
26
  }
@@ -6,31 +6,19 @@ exports.getLegalPage = getLegalPage;
6
6
  exports.listReleaseNotes = listReleaseNotes;
7
7
  exports.getTranslations = getTranslations;
8
8
  const sign_in_required_error_1 = require("../errors/sign-in-required-error");
9
+ const query_string_1 = require("../query-string");
10
+ const request_options_1 = require("../request-options");
9
11
  const locale_1 = require("./locale");
10
12
  const collectionPath = (collection) => `/v1/public/cms/collections/${encodeURIComponent(collection)}/items`;
11
- const cmsQuery = ({ locale, tenantCode, environment, page, limit, sort }) => {
12
- const params = new URLSearchParams();
13
- if (locale) {
14
- params.set("locale", (0, locale_1.toCmsLocale)(locale));
15
- }
16
- if (tenantCode) {
17
- params.set("tenant", tenantCode);
18
- }
19
- if (environment) {
20
- params.set("environment", environment);
21
- }
22
- if (page !== undefined) {
23
- params.set("page", String(page));
24
- }
25
- if (limit !== undefined) {
26
- params.set("limit", String(limit));
27
- }
28
- if (sort) {
29
- params.set("sort", sort);
30
- }
31
- const query = params.toString();
32
- return query ? `?${query}` : "";
33
- };
13
+ // Empty strings count as absent, like undefined.
14
+ const cmsQuery = ({ locale, tenantCode, environment, page, limit, sort }) => (0, query_string_1.toQueryString)({
15
+ locale: locale ? (0, locale_1.toCmsLocale)(locale) : undefined,
16
+ tenant: tenantCode || undefined,
17
+ environment,
18
+ page,
19
+ limit,
20
+ sort: sort || undefined,
21
+ });
34
22
  /**
35
23
  * Signed out, the backend serves only a few collections, read by tenant code;
36
24
  * any other read is a 401, which MedplumClient answers by clearing its
@@ -53,7 +41,7 @@ async function getCmsDocument(collection, slug, options = {}, requestOptions) {
53
41
  throw new Error("A CMS document read needs a slug.");
54
42
  }
55
43
  assertReadable(this, collection, options);
56
- return this.get(`${collectionPath(collection)}/${encodeURIComponent(slug)}${cmsQuery(options)}`, ...(requestOptions === undefined ? [] : [requestOptions]));
44
+ return this.get(`${collectionPath(collection)}/${encodeURIComponent(slug)}${cmsQuery(options)}`, ...(0, request_options_1.optionalArg)(requestOptions));
57
45
  }
58
46
  /**
59
47
  * A page of a CMS collection's published documents, merged with the shared
@@ -61,17 +49,17 @@ async function getCmsDocument(collection, slug, options = {}, requestOptions) {
61
49
  */
62
50
  async function listCmsDocuments(collection, options = {}, requestOptions) {
63
51
  assertReadable(this, collection, options);
64
- return this.get(`${collectionPath(collection)}${cmsQuery(options)}`, ...(requestOptions === undefined ? [] : [requestOptions]));
52
+ return this.get(`${collectionPath(collection)}${cmsQuery(options)}`, ...(0, request_options_1.optionalArg)(requestOptions));
65
53
  }
66
54
  /** A legal page ("terms-and-conditions", "data-privacy", …). Readable before sign-in with a tenant code. */
67
55
  function getLegalPage(slug, options = {}, requestOptions) {
68
56
  var _a;
69
- return this.getCmsDocument("legal-pages", slug, { ...options, allowSignedOut: (_a = options.allowSignedOut) !== null && _a !== void 0 ? _a : true }, ...(requestOptions === undefined ? [] : [requestOptions]));
57
+ return this.getCmsDocument("legal-pages", slug, { ...options, allowSignedOut: (_a = options.allowSignedOut) !== null && _a !== void 0 ? _a : true }, ...(0, request_options_1.optionalArg)(requestOptions));
70
58
  }
71
59
  /** Release notes, newest first unless `sort` says otherwise. Needs a session. */
72
60
  function listReleaseNotes(options = {}, requestOptions) {
73
61
  var _a;
74
- return this.listCmsDocuments("release-notes", { ...options, sort: (_a = options.sort) !== null && _a !== void 0 ? _a : "-publishedAt" }, ...(requestOptions === undefined ? [] : [requestOptions]));
62
+ return this.listCmsDocuments("release-notes", { ...options, sort: (_a = options.sort) !== null && _a !== void 0 ? _a : "-publishedAt" }, ...(0, request_options_1.optionalArg)(requestOptions));
75
63
  }
76
64
  /** A translations read takes pages of 50 documents, the CMS's most, and at most this many. */
77
65
  const MAX_TRANSLATION_PAGES = 20;
@@ -127,7 +115,7 @@ async function getTranslations(options = {}, requestOptions) {
127
115
  const readOptions = { ...options, allowSignedOut: (_a = options.allowSignedOut) !== null && _a !== void 0 ? _a : true };
128
116
  const translations = {};
129
117
  for (let page = 1; page <= MAX_TRANSLATION_PAGES; page++) {
130
- const { docs, hasNextPage } = await this.listCmsDocuments("translations", { ...readOptions, page, limit: 50 }, ...(requestOptions === undefined ? [] : [requestOptions]));
118
+ const { docs, hasNextPage } = await this.listCmsDocuments("translations", { ...readOptions, page, limit: 50 }, ...(0, request_options_1.optionalArg)(requestOptions));
131
119
  addTranslations(translations, docs);
132
120
  if (!hasNextPage) {
133
121
  break;
@@ -4,15 +4,16 @@ exports.getLegalConsent = getLegalConsent;
4
4
  exports.setLegalConsent = setLegalConsent;
5
5
  exports.getMedicalConsent = getMedicalConsent;
6
6
  exports.setMedicalConsent = setMedicalConsent;
7
+ const request_options_1 = require("../request-options");
7
8
  function getLegalConsent(requestOptions) {
8
- return this.get("/v1/me/consent/legal", ...(requestOptions === undefined ? [] : [requestOptions]));
9
+ return this.get("/v1/me/consent/legal", ...(0, request_options_1.optionalArg)(requestOptions));
9
10
  }
10
11
  function setLegalConsent(accepted, requestOptions) {
11
12
  const body = { termsAndConditionsConsent: accepted };
12
13
  return this.post("/v1/me/consent/legal", body, undefined, requestOptions);
13
14
  }
14
15
  function getMedicalConsent(requestOptions) {
15
- return this.get("/v1/me/consent/medical", ...(requestOptions === undefined ? [] : [requestOptions]));
16
+ return this.get("/v1/me/consent/medical", ...(0, request_options_1.optionalArg)(requestOptions));
16
17
  }
17
18
  function setMedicalConsent(accepted, requestOptions) {
18
19
  const body = { medicalSettingsNotificationsConsent: accepted };
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getEmailTemplates = getEmailTemplates;
4
+ const request_options_1 = require("../request-options");
4
5
  function getEmailTemplates(requestOptions) {
5
- return this.get("/v1/email/templates", ...(requestOptions === undefined ? [] : [requestOptions]));
6
+ return this.get("/v1/email/templates", ...(0, request_options_1.optionalArg)(requestOptions));
6
7
  }