@ovok/core 0.4.32 → 0.4.33

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,9 @@
1
1
  export declare const REGISTRATION_FAILED_MESSAGE = "Registration failed.";
2
+ export type OvokAccountErrorCode = "account_not_found" | "account_expired";
3
+ /** A safe, stable account error that does not include the account reference. */
4
+ export declare class OvokAccountError extends Error {
5
+ readonly code: OvokAccountErrorCode;
6
+ constructor(code: OvokAccountErrorCode);
7
+ }
2
8
  /** Returns true when signup was refused because the address already has an account. */
3
9
  export declare function isRegistrationFailed(error: unknown): boolean;
@@ -1,9 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.REGISTRATION_FAILED_MESSAGE = void 0;
3
+ exports.OvokAccountError = exports.REGISTRATION_FAILED_MESSAGE = void 0;
4
4
  exports.isRegistrationFailed = isRegistrationFailed;
5
5
  const ovok_error_1 = require("../errors/ovok-error");
6
6
  exports.REGISTRATION_FAILED_MESSAGE = "Registration failed.";
7
+ /** A safe, stable account error that does not include the account reference. */
8
+ class OvokAccountError extends Error {
9
+ constructor(code) {
10
+ super(code === "account_not_found"
11
+ ? "Saved account was not found."
12
+ : "This account's session has expired. Sign in again.");
13
+ this.code = code;
14
+ this.name = "OvokAccountError";
15
+ }
16
+ }
17
+ exports.OvokAccountError = OvokAccountError;
7
18
  /** Returns true when signup was refused because the address already has an account. */
8
19
  function isRegistrationFailed(error) {
9
20
  const details = (0, ovok_error_1.readOvokError)(error);
@@ -21,10 +21,16 @@ function acceptInvite(body, requestOptions) {
21
21
  var _a;
22
22
  const resourceType = (_a = this.getProfile()) === null || _a === void 0 ? void 0 : _a.resourceType;
23
23
  if (resourceType === "Practitioner") {
24
- return this.acceptPractitionerInvite(body, ...(requestOptions === undefined ? [] : [requestOptions]));
24
+ return this.acceptPractitionerInvite(body, ...(requestOptions === undefined ? [] : [requestOptions])).then((result) => {
25
+ this.invalidateUrl("/v1/patient-sharing/shares");
26
+ return result;
27
+ });
25
28
  }
26
29
  if (resourceType === "Patient") {
27
- return this.acceptPatientInvite(body, ...(requestOptions === undefined ? [] : [requestOptions]));
30
+ return this.acceptPatientInvite(body, ...(requestOptions === undefined ? [] : [requestOptions])).then((result) => {
31
+ this.invalidateUrl("/v1/patient-sharing/shares");
32
+ return result;
33
+ });
28
34
  }
29
35
  throw new Error("Accepting an invite requires a signed-in Patient or Practitioner profile.");
30
36
  }
@@ -22,6 +22,12 @@ export type OfflineMeasurementFlushResult = {
22
22
  queued: number;
23
23
  exhausted: number;
24
24
  responses: Record<string, Bundle>;
25
+ failures: Record<string, OfflineMeasurementFailure>;
26
+ };
27
+ export type OfflineMeasurementFailure = {
28
+ name: string;
29
+ code?: string;
30
+ status?: number;
25
31
  };
26
32
  type MeasurementTransport = (bundle: Bundle, requestOptions?: MedplumRequestOptions) => Promise<Bundle>;
27
33
  declare const stableMeasurementId: (observation: Observation) => string;
@@ -81,29 +81,32 @@ class OfflineMeasurementQueue {
81
81
  this.storage = storage;
82
82
  this.transport = transport;
83
83
  this.operation = Promise.resolve();
84
- this.enqueueAndFlush = (observations, requestOptions) => this.runExclusive(async () => {
85
- var _a, _b, _c;
86
- await ((_b = (_a = this.storage).getInitPromise) === null || _b === void 0 ? void 0 : _b.call(_a));
87
- const queue = this.readQueue();
88
- const entries = observations.map(entryForObservation);
89
- const ownerKey = (_c = this.getOwnerKey) === null || _c === void 0 ? void 0 : _c.call(this);
90
- if (this.getOwnerKey && !ownerKey) {
91
- throw new Error("Offline measurements require an active account to prevent cross-account delivery.");
92
- }
93
- const id = stableValueId({ ownerKey, entries });
94
- if (!queue.some((item) => item.id === id)) {
95
- queue.push({
96
- id,
97
- ownerKey,
98
- queuedAt: new Date().toISOString(),
99
- attempt: 0,
100
- nextAttemptAt: 0,
101
- entries,
102
- });
103
- this.writeQueue(queue);
104
- }
105
- return { id, flush: await this.flushQueue(queue, {}, ...(requestOptions === undefined ? [] : [requestOptions])) };
106
- });
84
+ this.enqueueAndFlush = (observations, requestOptions) => {
85
+ var _a;
86
+ const ownerKey = (_a = this.getOwnerKey) === null || _a === void 0 ? void 0 : _a.call(this);
87
+ return this.runExclusive(async () => {
88
+ var _a, _b;
89
+ await ((_b = (_a = this.storage).getInitPromise) === null || _b === void 0 ? void 0 : _b.call(_a));
90
+ const queue = this.readQueue();
91
+ const entries = observations.map(entryForObservation);
92
+ if (this.getOwnerKey && !ownerKey) {
93
+ throw new Error("Offline measurements require an active account to prevent cross-account delivery.");
94
+ }
95
+ const id = stableValueId({ ownerKey, entries });
96
+ if (!queue.some((item) => item.id === id)) {
97
+ queue.push({
98
+ id,
99
+ ownerKey,
100
+ queuedAt: new Date().toISOString(),
101
+ attempt: 0,
102
+ nextAttemptAt: 0,
103
+ entries,
104
+ });
105
+ this.writeQueue(queue);
106
+ }
107
+ return { id, flush: await this.flushQueue(queue, {}, ...(requestOptions === undefined ? [] : [requestOptions])) };
108
+ });
109
+ };
107
110
  this.flush = (options = {}, requestOptions) => this.runExclusive(async () => {
108
111
  var _a, _b;
109
112
  await ((_b = (_a = this.storage).getInitPromise) === null || _b === void 0 ? void 0 : _b.call(_a));
@@ -142,11 +145,14 @@ class OfflineMeasurementQueue {
142
145
  queued: 0,
143
146
  exhausted: 0,
144
147
  responses: {},
148
+ failures: {},
145
149
  };
146
150
  const now = Date.now();
147
- const ownerKey = (_a = this.getOwnerKey) === null || _a === void 0 ? void 0 : _a.call(this);
148
151
  let changed = false;
149
152
  for (const item of [...queue]) {
153
+ if (item.lastFailure) {
154
+ result.failures[item.id] = item.lastFailure;
155
+ }
150
156
  if (item.exhausted && options.resetExhausted) {
151
157
  item.attempt = 0;
152
158
  item.exhausted = false;
@@ -163,6 +169,7 @@ class OfflineMeasurementQueue {
163
169
  }
164
170
  continue;
165
171
  }
172
+ const ownerKey = (_a = this.getOwnerKey) === null || _a === void 0 ? void 0 : _a.call(this);
166
173
  if (this.getOwnerKey && (!ownerKey || item.ownerKey !== ownerKey)) {
167
174
  result.queued++;
168
175
  continue;
@@ -183,7 +190,7 @@ class OfflineMeasurementQueue {
183
190
  result.responses[item.id] = response;
184
191
  changed = true;
185
192
  }
186
- catch (_b) {
193
+ catch (error) {
187
194
  item.attempt = attempt;
188
195
  item.exhausted = attempt >= this.maxRetries;
189
196
  item.nextAttemptAt = item.exhausted
@@ -191,6 +198,14 @@ class OfflineMeasurementQueue {
191
198
  : Date.now() +
192
199
  Math.min(this.maxRetryBackoffMs, this.retryBackoffMs * 2 ** (attempt - 1));
193
200
  changed = true;
201
+ const failure = error;
202
+ const lastFailure = {
203
+ name: typeof (failure === null || failure === void 0 ? void 0 : failure.name) === "string" ? failure.name : "Error",
204
+ ...(typeof (failure === null || failure === void 0 ? void 0 : failure.code) === "string" ? { code: failure.code } : {}),
205
+ ...(typeof (failure === null || failure === void 0 ? void 0 : failure.status) === "number" ? { status: failure.status } : {}),
206
+ };
207
+ item.lastFailure = lastFailure;
208
+ result.failures[item.id] = lastFailure;
194
209
  if (item.exhausted) {
195
210
  result.exhausted++;
196
211
  }
@@ -1,4 +1,4 @@
1
- import { IClientStorage, LoginState, MedplumClient, MedplumClientOptions, MedplumRequestOptions, SubscriptionManager } from "@medplum/core";
1
+ import { IClientStorage, LoginState, MedplumClient, MedplumClientOptions, MedplumRequestOptions, ReadablePromise, SubscriptionManager } from "@medplum/core";
2
2
  import { Bundle, CapabilityStatement, Observation } from "@medplum/fhirtypes";
3
3
  import { OvokPublicApiEndpoint } from "../conformance/capability";
4
4
  import * as aiFhirMethods from "./ai-fhir/methods";
@@ -50,18 +50,25 @@ export type OvokClientOptions = Omit<MedplumClientOptions, "onUnauthenticated">
50
50
  socialLoginClientId?: string;
51
51
  requestTimeoutMs?: number;
52
52
  offlineQueue?: OfflineMeasurementQueueOptions;
53
+ /** Keep the saved login when a refresh cannot reach the server. */
54
+ keepSessionOnRefreshUnavailable?: boolean;
53
55
  };
54
56
  export declare class OvokClient extends MedplumClient {
55
57
  protected socialLoginClientId: string;
56
58
  clientStorage: IClientStorage | undefined;
57
59
  private readonly hasExplicitStorageAdapter;
58
60
  private readonly offlineQueueOptions;
61
+ private readonly clientOptions;
62
+ private readonly keepSessionOnRefreshUnavailable;
63
+ private readonly refreshState;
59
64
  private offlineMeasurementQueue;
60
65
  /**
61
66
  * @param config - The configuration for the client.
62
67
  * @param config.socialLoginClientId - The social login client id to use for the client.
63
68
  */
64
69
  constructor(config: OvokClientOptions);
70
+ get<T = unknown>(url: URL | string, options?: MedplumRequestOptions): ReadablePromise<T>;
71
+ refreshIfExpired(gracePeriod?: number): Promise<void>;
65
72
  post<T = unknown>(url: URL | string, body?: unknown, contentType?: string, options?: MedplumRequestOptions): Promise<T>;
66
73
  /** Lists saved accounts without exposing their access or refresh tokens. */
67
74
  getSavedAccounts(): OvokSavedAccount[];
@@ -96,6 +103,7 @@ export declare class OvokClient extends MedplumClient {
96
103
  clear(): void;
97
104
  private getLogin;
98
105
  private getExpiredAccountIds;
106
+ private createAccountSessionClient;
99
107
  private setAccountExpired;
100
108
  private removeSavedAccount;
101
109
  private syncSavedAccountProfile;
@@ -39,6 +39,7 @@ const core_1 = require("@medplum/core");
39
39
  const capability_1 = require("../conformance/capability");
40
40
  const aiFhirMethods = __importStar(require("./ai-fhir/methods"));
41
41
  const account_storage_1 = require("./auth/account-storage");
42
+ const errors_1 = require("./auth/errors");
42
43
  const authMethods = __importStar(require("./auth/methods"));
43
44
  const botMethods = __importStar(require("./bot/methods"));
44
45
  const changeHistoryMethods = __importStar(require("./change-history/methods"));
@@ -59,7 +60,7 @@ const patientMethods = __importStar(require("./patient/methods"));
59
60
  const patientSharingMethods = __importStar(require("./patient-sharing/methods"));
60
61
  const platformMethods = __importStar(require("./platform/methods"));
61
62
  const questionnaireResponseMethods = __importStar(require("./questionnaire-response/methods"));
62
- const errors_1 = require("./signals/errors");
63
+ const errors_2 = require("./signals/errors");
63
64
  const signalsMethods = __importStar(require("./signals/methods"));
64
65
  const slimMethods = __importStar(require("./slim/methods"));
65
66
  exports.MAX_CONDITIONAL_OBSERVATION_ENTRIES = 8;
@@ -217,6 +218,7 @@ class OvokClient extends core_1.MedplumClient {
217
218
  let clearActiveLogin = () => undefined;
218
219
  let terminalRefreshToken;
219
220
  let terminalRefreshError;
221
+ const refreshState = { unavailable: false };
220
222
  const isTokenRefresh = (url, options) => new URL(url, normalizedBaseUrl).href === tokenUrl &&
221
223
  (options === null || options === void 0 ? void 0 : options.method) === "POST" &&
222
224
  typeof options.body === "string" &&
@@ -338,6 +340,9 @@ class OvokClient extends core_1.MedplumClient {
338
340
  const response = await fetchOnce(url, options);
339
341
  if (isTemporaryRefreshFailure(response.status)) {
340
342
  void ((_a = response.body) === null || _a === void 0 ? void 0 : _a.cancel().catch(() => undefined));
343
+ if (config.keepSessionOnRefreshUnavailable) {
344
+ refreshState.unavailable = true;
345
+ }
341
346
  throw refreshUnavailableError(response);
342
347
  }
343
348
  return response;
@@ -355,6 +360,11 @@ class OvokClient extends core_1.MedplumClient {
355
360
  if ((signal === null || signal === void 0 ? void 0 : signal.aborted) || !isNetworkError(error)) {
356
361
  throw error;
357
362
  }
363
+ if (config.keepSessionOnRefreshUnavailable) {
364
+ refreshState.unavailable = true;
365
+ lastNetworkError = error;
366
+ break;
367
+ }
358
368
  if (Date.now() >= expiresAt) {
359
369
  lastNetworkError = error;
360
370
  break;
@@ -369,6 +379,10 @@ class OvokClient extends core_1.MedplumClient {
369
379
  if (!isTemporaryRefreshFailure(lastResponse.status)) {
370
380
  return lastResponse;
371
381
  }
382
+ if (config.keepSessionOnRefreshUnavailable) {
383
+ refreshState.unavailable = true;
384
+ break;
385
+ }
372
386
  const remainingMs = expiresAt - Date.now();
373
387
  if (remainingMs <= 0) {
374
388
  break;
@@ -388,6 +402,13 @@ class OvokClient extends core_1.MedplumClient {
388
402
  type: "refresh-unavailable",
389
403
  ...(status === undefined ? {} : { status }),
390
404
  };
405
+ if (config.keepSessionOnRefreshUnavailable) {
406
+ refreshState.unavailable = true;
407
+ if (lastResponse) {
408
+ return lastResponse;
409
+ }
410
+ throw lastNetworkError !== null && lastNetworkError !== void 0 ? lastNetworkError : refreshUnavailableError(undefined);
411
+ }
391
412
  unauthenticatedReason = reason;
392
413
  markActiveAccountExpired();
393
414
  if (lastResponse) {
@@ -405,6 +426,9 @@ class OvokClient extends core_1.MedplumClient {
405
426
  storage,
406
427
  onUnauthenticated: () => {
407
428
  var _a;
429
+ if (refreshState.unavailable) {
430
+ return;
431
+ }
408
432
  const reason = unauthenticatedReason !== null && unauthenticatedReason !== void 0 ? unauthenticatedReason : {
409
433
  type: "request-unauthorized",
410
434
  status: 401,
@@ -417,6 +441,9 @@ class OvokClient extends core_1.MedplumClient {
417
441
  this.socialLoginClientId = "";
418
442
  clearActiveLogin = this.clearActiveLogin.bind(this);
419
443
  this.hasExplicitStorageAdapter = config.storage !== undefined;
444
+ this.clientOptions = config;
445
+ this.keepSessionOnRefreshUnavailable = config.keepSessionOnRefreshUnavailable === true;
446
+ this.refreshState = refreshState;
420
447
  this.socialLoginClientId = (_e = config.socialLoginClientId) !== null && _e !== void 0 ? _e : "";
421
448
  this.addEventListener("profileRefreshed", () => this.syncSavedAccountProfile());
422
449
  this.bindMethods(authMethods);
@@ -437,14 +464,46 @@ class OvokClient extends core_1.MedplumClient {
437
464
  this.bindMethods(patientMethods);
438
465
  this.bindMethods(patientSharingMethods);
439
466
  this.bindMethods(signalsMethods, (error) => {
440
- const mapped = (0, errors_1.mapSignalsError)((0, rate_limit_error_1.mapRateLimitError)(error), {
467
+ const mapped = (0, errors_2.mapSignalsError)((0, rate_limit_error_1.mapRateLimitError)(error), {
441
468
  signalsRequest: true,
442
469
  });
443
- return (0, errors_1.isSignalsError)(mapped) ? mapped : (0, ovok_error_1.mapOvokApiError)(mapped);
470
+ return (0, errors_2.isSignalsError)(mapped) ? mapped : (0, ovok_error_1.mapOvokApiError)(mapped);
444
471
  });
445
472
  this.clientStorage = storage;
446
473
  this.offlineQueueOptions = (_f = config.offlineQueue) !== null && _f !== void 0 ? _f : {};
447
474
  }
475
+ get(url, options) {
476
+ const request = super.get(url, options);
477
+ void request.catch(() => this.invalidateUrl(url));
478
+ return request;
479
+ }
480
+ async refreshIfExpired(gracePeriod) {
481
+ var _a;
482
+ const activeLogin = this.getActiveLogin();
483
+ const accessToken = this.getAccessToken();
484
+ const expiresAt = accessToken ? (0, core_1.tryGetJwtExpiration)(accessToken) : undefined;
485
+ try {
486
+ await super.refreshIfExpired(gracePeriod);
487
+ }
488
+ catch (error) {
489
+ const activeAccount = this.getSavedAccounts().find((account) => { var _a; return account.id === ((_a = activeLogin === null || activeLogin === void 0 ? void 0 : activeLogin.profile) === null || _a === void 0 ? void 0 : _a.reference); });
490
+ if (this.keepSessionOnRefreshUnavailable &&
491
+ this.refreshState.unavailable &&
492
+ activeLogin &&
493
+ accessToken &&
494
+ !(activeAccount === null || activeAccount === void 0 ? void 0 : activeAccount.expired) &&
495
+ expiresAt !== undefined &&
496
+ expiresAt > Date.now()) {
497
+ (_a = this.clientStorage) === null || _a === void 0 ? void 0 : _a.setObject("activeLogin", activeLogin);
498
+ this.setAccessToken(accessToken, activeLogin.refreshToken);
499
+ return;
500
+ }
501
+ throw error;
502
+ }
503
+ finally {
504
+ this.refreshState.unavailable = false;
505
+ }
506
+ }
448
507
  post(url, body, contentType, options) {
449
508
  const pathname = new URL(url.toString(), this.getBaseUrl()).pathname;
450
509
  if (!isRetrySafePost(pathname)) {
@@ -476,10 +535,10 @@ class OvokClient extends core_1.MedplumClient {
476
535
  var _a, _b;
477
536
  const login = this.getLogin(accountId);
478
537
  if (!login) {
479
- throw new Error(`No saved account found for ${accountId}.`);
538
+ throw new errors_1.OvokAccountError("account_not_found");
480
539
  }
481
540
  if (this.getExpiredAccountIds().includes(accountId)) {
482
- throw new Error("This account's session has expired. Sign in again.");
541
+ throw new errors_1.OvokAccountError("account_expired");
483
542
  }
484
543
  const previousLogin = this.getActiveLogin();
485
544
  try {
@@ -507,32 +566,25 @@ class OvokClient extends core_1.MedplumClient {
507
566
  var _a;
508
567
  const login = this.getLogin(accountId);
509
568
  if (!login) {
510
- throw new Error(`No saved account found for ${accountId}.`);
569
+ throw new errors_1.OvokAccountError("account_not_found");
511
570
  }
512
571
  const previousLogin = this.getActiveLogin();
513
572
  const previousAccountId = (_a = previousLogin === null || previousLogin === void 0 ? void 0 : previousLogin.profile) === null || _a === void 0 ? void 0 : _a.reference;
573
+ const wasActive = previousAccountId === accountId;
574
+ const sessionClient = this.createAccountSessionClient(login);
514
575
  try {
515
- await this.setActiveLogin(login);
516
- await this.delete("auth/session/current", ...(requestOptions === undefined ? [] : [requestOptions]));
576
+ await sessionClient.delete("auth/session/current", ...(requestOptions === undefined ? [] : [requestOptions]));
517
577
  }
518
578
  catch (error) {
519
- if (this.getExpiredAccountIds().includes(accountId)) {
520
- this.removeSavedAccount(accountId);
521
- if (previousLogin && previousAccountId && previousAccountId !== accountId) {
522
- await this.restoreActiveLogin(previousLogin);
523
- }
524
- else {
525
- this.clearActiveLogin();
526
- }
527
- return;
528
- }
529
- if (previousLogin && previousAccountId !== accountId) {
530
- await this.restoreActiveLogin(previousLogin);
579
+ if (!sessionClient.getSavedAccounts().some((account) => account.id === accountId && account.expired)) {
580
+ throw error;
531
581
  }
532
- throw error;
533
582
  }
534
- this.clearActiveLogin();
535
583
  this.removeSavedAccount(accountId);
584
+ if (!wasActive) {
585
+ return;
586
+ }
587
+ this.clearActiveLogin();
536
588
  const availableAccountIds = new Set(this.getSavedAccounts().filter((account) => !account.expired).map((account) => account.id));
537
589
  let nextLogin;
538
590
  if (previousAccountId && previousAccountId !== accountId) {
@@ -604,6 +656,7 @@ class OvokClient extends core_1.MedplumClient {
604
656
  queued: 0,
605
657
  exhausted: 0,
606
658
  responses: {},
659
+ failures: {},
607
660
  };
608
661
  }
609
662
  return queue.flush(options, ...(requestOptions === undefined ? [] : [requestOptions]));
@@ -673,6 +726,19 @@ class OvokClient extends core_1.MedplumClient {
673
726
  var _a, _b;
674
727
  return ((_b = (_a = this.clientStorage) === null || _a === void 0 ? void 0 : _a.getObject(account_storage_1.EXPIRED_ACCOUNTS_STORAGE_KEY)) !== null && _b !== void 0 ? _b : []);
675
728
  }
729
+ createAccountSessionClient(login) {
730
+ const storage = new core_1.ClientStorage(new core_1.MemoryStorage(), this.clientOptions.storagePrefix);
731
+ storage.setObject("activeLogin", login);
732
+ storage.setObject(account_storage_1.LOGINS_STORAGE_KEY, [login]);
733
+ const client = new OvokClient({
734
+ ...this.clientOptions,
735
+ storage,
736
+ offlineQueue: undefined,
737
+ onUnauthenticated: undefined,
738
+ });
739
+ client.setAccessToken(login.accessToken, login.refreshToken);
740
+ return client;
741
+ }
676
742
  setAccountExpired(accountId, expired) {
677
743
  if (this.clientStorage) {
678
744
  (0, account_storage_1.setStoredAccountExpired)(this.clientStorage, accountId, expired);
@@ -11,7 +11,10 @@ function createPatientSharingInvite(body, requestOptions) {
11
11
  return this.post("/v1/patient-sharing/invites", body, undefined, requestOptions);
12
12
  }
13
13
  function redeemPatientSharingInvite(body, requestOptions) {
14
- return this.post("/v1/patient-sharing/invites/redeem", body, undefined, requestOptions);
14
+ return this.post("/v1/patient-sharing/invites/redeem", body, undefined, requestOptions).then((share) => {
15
+ this.invalidateUrl("/v1/patient-sharing/shares");
16
+ return share;
17
+ });
15
18
  }
16
19
  function listPatientSharing(requestOptions) {
17
20
  return this.get("/v1/patient-sharing/shares", ...(requestOptions === undefined ? [] : [requestOptions])).then((shares) => {
@@ -24,6 +27,7 @@ function listPatientSharing(requestOptions) {
24
27
  }
25
28
  async function revokePatientSharing(consentId, requestOptions) {
26
29
  await this.delete(`/v1/patient-sharing/shares/${segment(consentId)}`, ...(requestOptions === undefined ? [] : [requestOptions]));
30
+ this.invalidateUrl("/v1/patient-sharing/shares");
27
31
  }
28
32
  function getPatientSharingSettings(requestOptions) {
29
33
  return this.get("/v1/patient-sharing/settings", ...(requestOptions === undefined ? [] : [requestOptions]));
@@ -48,7 +48,7 @@ exports.OVOK_CORE_REQUIREMENTS = {
48
48
  description: "The FHIR surface a server must provide for the @ovok/core client library's own methods to function.",
49
49
  software: {
50
50
  name: "@ovok/core",
51
- version: "0.4.32",
51
+ version: "0.4.33",
52
52
  },
53
53
  fhirVersion: "4.0.1",
54
54
  format: ["json"],
@@ -3,9 +3,15 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.useAcceptInvite = exports.useInvitePatient = exports.useInvitePractitioner = void 0;
5
5
  const api_hooks_1 = require("./api-hooks");
6
+ const notification_invalidation_1 = require("./notification-invalidation");
6
7
  const useInvitePractitioner = () => (0, api_hooks_1.useOvokMutation)((client, body) => client.invitePractitioner(body));
7
8
  exports.useInvitePractitioner = useInvitePractitioner;
8
9
  const useInvitePatient = () => (0, api_hooks_1.useOvokMutation)((client, body) => client.invitePatient(body));
9
10
  exports.useInvitePatient = useInvitePatient;
10
- const useAcceptInvite = () => (0, api_hooks_1.useOvokMutation)((client, body) => client.acceptInvite(body));
11
+ const useAcceptInvite = () => (0, api_hooks_1.useOvokMutation)(async (client, body) => {
12
+ const result = await client.acceptInvite(body);
13
+ client.invalidateUrl("/v1/patient-sharing/shares");
14
+ (0, notification_invalidation_1.notifyNotificationChanges)(client, { type: "patient-sharing" });
15
+ return result;
16
+ });
11
17
  exports.useAcceptInvite = useAcceptInvite;
@@ -3,6 +3,8 @@ import { OvokClient } from "../client/ovok-client";
3
3
  import { SignalsAlert, SignalsRawAlert } from "../client/signals/types";
4
4
  import { SlimNotification } from "../client/slim/types";
5
5
  export type NotificationChange = {
6
+ type: "patient-sharing";
7
+ } | {
6
8
  type: "slim";
7
9
  notification: SlimNotification | SlimMedicalNotificationRow;
8
10
  } | {
@@ -37,18 +37,32 @@ Object.defineProperty(exports, "__esModule", { value: true });
37
37
  exports.useUpdatePatientSharingSettings = exports.usePatientSharingSettings = exports.useRevokePatientSharing = exports.useRedeemPatientSharingInvite = exports.useCreatePatientSharingInvite = exports.usePatientSharing = void 0;
38
38
  const React = __importStar(require("react"));
39
39
  const api_hooks_1 = require("./api-hooks");
40
+ const notification_invalidation_1 = require("./notification-invalidation");
40
41
  const index_1 = require("./index");
41
42
  const usePatientSharing = () => {
42
43
  const client = (0, index_1.useClient)();
43
44
  const load = React.useCallback((signal) => client.listPatientSharing({ signal }), [client]);
44
- return (0, api_hooks_1.useOvokRequest)(load);
45
+ const state = (0, api_hooks_1.useOvokRequestWithUpdater)(load);
46
+ React.useEffect(() => (0, notification_invalidation_1.subscribeToNotificationChanges)(client, (change) => {
47
+ if ((change === null || change === void 0 ? void 0 : change.type) === "patient-sharing") {
48
+ state.reload();
49
+ }
50
+ }), [client, state.reload]);
51
+ return state;
45
52
  };
46
53
  exports.usePatientSharing = usePatientSharing;
47
54
  const useCreatePatientSharingInvite = () => (0, api_hooks_1.useOvokMutation)((client, params) => client.createPatientSharingInvite(params));
48
55
  exports.useCreatePatientSharingInvite = useCreatePatientSharingInvite;
49
- const useRedeemPatientSharingInvite = () => (0, api_hooks_1.useOvokMutation)((client, params) => client.redeemPatientSharingInvite(params));
56
+ const useRedeemPatientSharingInvite = () => (0, api_hooks_1.useOvokMutation)(async (client, params) => {
57
+ const share = await client.redeemPatientSharingInvite(params);
58
+ (0, notification_invalidation_1.notifyNotificationChanges)(client, { type: "patient-sharing" });
59
+ return share;
60
+ });
50
61
  exports.useRedeemPatientSharingInvite = useRedeemPatientSharingInvite;
51
- const useRevokePatientSharing = () => (0, api_hooks_1.useOvokMutation)((client, consentId) => client.revokePatientSharing(consentId));
62
+ const useRevokePatientSharing = () => (0, api_hooks_1.useOvokMutation)(async (client, consentId) => {
63
+ await client.revokePatientSharing(consentId);
64
+ (0, notification_invalidation_1.notifyNotificationChanges)(client, { type: "patient-sharing" });
65
+ });
52
66
  exports.useRevokePatientSharing = useRevokePatientSharing;
53
67
  const usePatientSharingSettings = () => {
54
68
  const client = (0, index_1.useClient)();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ovok/core",
3
- "version": "0.4.32",
3
+ "version": "0.4.33",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",