From d28680c63b7a2aab1d74cc49ca42e7b1d9b52d37 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Tue, 11 Feb 2025 17:08:55 +0200 Subject: [PATCH 1/8] fix(clerk-js): Avoid updating client when removing all sessions --- .changeset/new-fishes-rescue.md | 5 +++++ integration/testUtils/index.ts | 12 ++++++++---- integration/tests/sign-out-smoke.test.ts | 2 +- packages/clerk-js/src/core/resources/Base.ts | 8 +++++--- packages/clerk-js/src/core/resources/Client.ts | 11 ++++++++++- 5 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 .changeset/new-fishes-rescue.md diff --git a/.changeset/new-fishes-rescue.md b/.changeset/new-fishes-rescue.md new file mode 100644 index 00000000000..f5da358c0a7 --- /dev/null +++ b/.changeset/new-fishes-rescue.md @@ -0,0 +1,5 @@ +--- +'@clerk/clerk-js': patch +--- + +Avoid updating client when removing all sessions. diff --git a/integration/testUtils/index.ts b/integration/testUtils/index.ts index 52b8bf58ab9..f593b29127d 100644 --- a/integration/testUtils/index.ts +++ b/integration/testUtils/index.ts @@ -36,10 +36,14 @@ const createExpectPageObject = ({ page }: TestArgs) => { expect(redirect.status()).toBe(307); expect(redirect.headers()['x-clerk-auth-status']).toContain('handshake'); }, - toBeSignedOut: () => { - return page.waitForFunction(() => { - return !window.Clerk?.user; - }); + toBeSignedOut: (args?: { timeOut: number }) => { + return page.waitForFunction( + () => { + return !window.Clerk?.user; + }, + null, + { timeout: args?.timeOut }, + ); }, toBeSignedIn: async () => { return page.waitForFunction(() => { diff --git a/integration/tests/sign-out-smoke.test.ts b/integration/tests/sign-out-smoke.test.ts index 129947774c7..d53d4b1d5cd 100644 --- a/integration/tests/sign-out-smoke.test.ts +++ b/integration/tests/sign-out-smoke.test.ts @@ -43,7 +43,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('sign out await m.po.expect.toBeSignedOut(); }); - await mainTab.po.expect.toBeSignedOut(); + await mainTab.po.expect.toBeSignedOut({ timeOut: 2 * 1_000 }); }); test('sign out persisting client', async ({ page, context }) => { diff --git a/packages/clerk-js/src/core/resources/Base.ts b/packages/clerk-js/src/core/resources/Base.ts index 105e255a37c..ab857848cbe 100644 --- a/packages/clerk-js/src/core/resources/Base.ts +++ b/packages/clerk-js/src/core/resources/Base.ts @@ -11,6 +11,7 @@ import { ClerkAPIResponseError, ClerkRuntimeError, Client } from './internal'; export type BaseFetchOptions = ClerkResourceReloadParams & { forceUpdateClient?: boolean; fetchMaxTries?: number; + skipUpdateClient?: boolean; }; export type BaseMutateParams = { @@ -18,6 +19,7 @@ export type BaseMutateParams = { body?: any; method?: HTTPMethod; path?: string; + skipUpdateClient?: boolean; }; function assertProductionKeysOnDev(statusCode: number, payloadErrors?: ClerkAPIErrorJSON[]) { @@ -105,7 +107,7 @@ export abstract class BaseResource { } // TODO: Link to Client payload piggybacking design document - if (requestInit.method !== 'GET' || opts.forceUpdateClient) { + if ((requestInit.method !== 'GET' || opts.forceUpdateClient) && !opts.skipUpdateClient) { this._updateClient(payload); } @@ -180,8 +182,8 @@ export abstract class BaseResource { } protected async _baseMutate(params: BaseMutateParams): Promise { - const { action, body, method, path } = params; - const json = await BaseResource._fetch({ method, path: path || this.path(action), body }); + const { action, body, method, path, skipUpdateClient } = params; + const json = await BaseResource._fetch({ method, path: path || this.path(action), body }, { skipUpdateClient }); return this.fromJSON((json?.response || json) as J); } diff --git a/packages/clerk-js/src/core/resources/Client.ts b/packages/clerk-js/src/core/resources/Client.ts index b7558c49a07..b95e830e36e 100644 --- a/packages/clerk-js/src/core/resources/Client.ts +++ b/packages/clerk-js/src/core/resources/Client.ts @@ -83,7 +83,16 @@ export class Client extends BaseResource implements ClientResource { removeSessions(): Promise { return this._baseDelete({ path: this.path() + '/sessions', - }) as unknown as Promise; + /** + * Skipping updating the client matches the behaviour of `client.destroy`, which allows broadcasting a sign-out event, + * and delays emitting until `setActive` is called within `Clerk.signOut()` + * TODO: Remove this property while persisting the desired outcome + */ + skipUpdateClient: true, + }).then(e => { + SessionTokenCache.clear(); + return e as unknown as ClientResource; + }); } clearCache(): void { From 4e926cded851efe4304deade35914be3a0da95c4 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Tue, 11 Feb 2025 18:14:29 +0200 Subject: [PATCH 2/8] allow unit tests to pass --- packages/clerk-js/src/core/resources/Base.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/clerk-js/src/core/resources/Base.ts b/packages/clerk-js/src/core/resources/Base.ts index ab857848cbe..296f6985256 100644 --- a/packages/clerk-js/src/core/resources/Base.ts +++ b/packages/clerk-js/src/core/resources/Base.ts @@ -183,7 +183,12 @@ export abstract class BaseResource { protected async _baseMutate(params: BaseMutateParams): Promise { const { action, body, method, path, skipUpdateClient } = params; - const json = await BaseResource._fetch({ method, path: path || this.path(action), body }, { skipUpdateClient }); + let json; + if (skipUpdateClient) { + json = await BaseResource._fetch({ method, path: path || this.path(action), body }, { skipUpdateClient }); + } else { + json = await BaseResource._fetch({ method, path: path || this.path(action), body }); + } return this.fromJSON((json?.response || json) as J); } From e8c1e297982cfa12a098659685e2850b205c0f0a Mon Sep 17 00:00:00 2001 From: panteliselef Date: Thu, 13 Feb 2025 15:18:22 +0200 Subject: [PATCH 3/8] do not depend on client to broadcast --- packages/clerk-js/src/core/clerk.ts | 28 +++++++++++++------ .../components/UserProfile/DeleteUserForm.tsx | 5 ++-- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 8e70cfbbd25..dda7f4493c9 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -364,7 +364,7 @@ export class Clerk implements ClerkInterface { } public signOut: SignOut = async (callbackOrOptions?: SignOutCallback | SignOutOptions, options?: SignOutOptions) => { - if (!this.client || this.client.sessions.length === 0) { + if (!this.client) { return; } const opts = callbackOrOptions && typeof callbackOrOptions === 'object' ? callbackOrOptions : options || {}; @@ -373,6 +373,8 @@ export class Clerk implements ClerkInterface { const handleSetActive = () => { const signOutCallback = typeof callbackOrOptions === 'function' ? callbackOrOptions : undefined; + + this.#broadcastSignOutEvent(); if (signOutCallback) { return this.setActive({ session: null, @@ -880,11 +882,11 @@ export class Clerk implements ClerkInterface { // If this.session exists, then signOut was triggered by the current tab // and should emit. Other tabs should not emit the same event again - const shouldSignOutSession = this.session && newSession === null; - if (shouldSignOutSession) { - this.#broadcastSignOutEvent(); - eventBus.dispatch(events.TokenUpdate, { token: null }); - } + // const shouldSignOutSession = this.session && newSession === null; + // if (shouldSignOutSession) { + // this.#broadcastSignOutEvent(); + // eventBus.dispatch(events.TokenUpdate, { token: null }); + // } //1. setLastActiveSession to passed user session (add a param). // Note that this will also update the session's active organization @@ -1504,10 +1506,15 @@ export class Clerk implements ClerkInterface { }); }; + // TODO: Deprecate this one, and mark it as internal. Is there actual benefit for external developers to use this ? Should they ever reach for it ? public handleUnauthenticated = async (opts = { broadcast: true }): Promise => { if (!this.client || !this.session) { return; } + const clearSession = () => { + eventBus.dispatch(events.TokenUpdate, { token: null }); + return this.setActive({ session: null }); + }; try { const newClient = await Client.getOrCreateInstance().fetch(); this.updateClient(newClient); @@ -1517,11 +1524,11 @@ export class Clerk implements ClerkInterface { if (opts.broadcast) { this.#broadcastSignOutEvent(); } - return this.setActive({ session: null }); + return clearSession(); } catch (err) { // Handle the 403 Forbidden if (err.status === 403) { - return this.setActive({ session: null }); + return clearSession(); } else { throw err; } @@ -2025,9 +2032,12 @@ export class Clerk implements ClerkInterface { } }); + /** + * Background tabs get notified of a signout event from active tab. + */ this.#broadcastChannel?.addEventListener('message', ({ data }) => { if (data.type === 'signout') { - void this.handleUnauthenticated(); + void this.handleUnauthenticated({ broadcast: false }); } }); }; diff --git a/packages/clerk-js/src/ui/components/UserProfile/DeleteUserForm.tsx b/packages/clerk-js/src/ui/components/UserProfile/DeleteUserForm.tsx index 5a90cfadd95..253b2ff9b4e 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/DeleteUserForm.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/DeleteUserForm.tsx @@ -15,7 +15,7 @@ export const DeleteUserForm = withCardStateProvider((props: DeleteUserFormProps) const { user } = useUser(); const { t } = useLocalizations(); const { otherSessions } = useMultipleSessions({ user }); - const { setActive } = useClerk(); + const { signOut } = useClerk(); const [deleteUserWithReverification] = useReverification(() => user?.delete()); const confirmationField = useFormControl('deleteConfirmation', '', { @@ -38,8 +38,7 @@ export const DeleteUserForm = withCardStateProvider((props: DeleteUserFormProps) await deleteUserWithReverification(); const redirectUrl = otherSessions.length === 0 ? afterSignOutUrl : afterMultiSessionSingleSignOutUrl; - return await setActive({ - session: null, + return await signOut({ redirectUrl, }); } catch (e) { From 8c98956d4e26e7582c03f97930afb450709dc195 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Thu, 13 Feb 2025 17:47:42 +0200 Subject: [PATCH 4/8] avoid using signOut on delete --- packages/clerk-js/src/core/clerk.ts | 14 +++++++------- packages/clerk-js/src/core/resources/Client.ts | 2 +- packages/clerk-js/src/core/resources/User.ts | 5 ++++- .../ui/components/UserProfile/DeleteUserForm.tsx | 5 +++-- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index dda7f4493c9..aba525205df 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -364,7 +364,7 @@ export class Clerk implements ClerkInterface { } public signOut: SignOut = async (callbackOrOptions?: SignOutCallback | SignOutOptions, options?: SignOutOptions) => { - if (!this.client) { + if (!this.client || this.client.sessions.length === 0) { return; } const opts = callbackOrOptions && typeof callbackOrOptions === 'object' ? callbackOrOptions : options || {}; @@ -1511,10 +1511,6 @@ export class Clerk implements ClerkInterface { if (!this.client || !this.session) { return; } - const clearSession = () => { - eventBus.dispatch(events.TokenUpdate, { token: null }); - return this.setActive({ session: null }); - }; try { const newClient = await Client.getOrCreateInstance().fetch(); this.updateClient(newClient); @@ -1524,11 +1520,11 @@ export class Clerk implements ClerkInterface { if (opts.broadcast) { this.#broadcastSignOutEvent(); } - return clearSession(); + return this.setActive({ session: null }); } catch (err) { // Handle the 403 Forbidden if (err.status === 403) { - return clearSession(); + return this.setActive({ session: null }); } else { throw err; } @@ -2078,6 +2074,10 @@ export class Clerk implements ClerkInterface { this.#broadcastChannel?.postMessage({ type: 'signout' }); }; + public __internal_broadcastSignOutEvent = () => { + this.#broadcastSignOutEvent(); + }; + #setTransitiveState = () => { this.session = undefined; this.organization = undefined; diff --git a/packages/clerk-js/src/core/resources/Client.ts b/packages/clerk-js/src/core/resources/Client.ts index b95e830e36e..e4a27332d9d 100644 --- a/packages/clerk-js/src/core/resources/Client.ts +++ b/packages/clerk-js/src/core/resources/Client.ts @@ -88,7 +88,7 @@ export class Client extends BaseResource implements ClientResource { * and delays emitting until `setActive` is called within `Clerk.signOut()` * TODO: Remove this property while persisting the desired outcome */ - skipUpdateClient: true, + skipUpdateClient: false, }).then(e => { SessionTokenCache.clear(); return e as unknown as ClientResource; diff --git a/packages/clerk-js/src/core/resources/User.ts b/packages/clerk-js/src/core/resources/User.ts index e8c491707b7..7ec0289e616 100644 --- a/packages/clerk-js/src/core/resources/User.ts +++ b/packages/clerk-js/src/core/resources/User.ts @@ -241,7 +241,10 @@ export class User extends BaseResource implements UserResource { }; delete = (): Promise => { - return this._baseDelete({ path: '/me' }); + return this._baseDelete({ path: '/me' }).then(res => { + User.clerk.__internal_broadcastSignOutEvent(); + return res; + }); }; getSessions = async (): Promise => { diff --git a/packages/clerk-js/src/ui/components/UserProfile/DeleteUserForm.tsx b/packages/clerk-js/src/ui/components/UserProfile/DeleteUserForm.tsx index 253b2ff9b4e..5a90cfadd95 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/DeleteUserForm.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/DeleteUserForm.tsx @@ -15,7 +15,7 @@ export const DeleteUserForm = withCardStateProvider((props: DeleteUserFormProps) const { user } = useUser(); const { t } = useLocalizations(); const { otherSessions } = useMultipleSessions({ user }); - const { signOut } = useClerk(); + const { setActive } = useClerk(); const [deleteUserWithReverification] = useReverification(() => user?.delete()); const confirmationField = useFormControl('deleteConfirmation', '', { @@ -38,7 +38,8 @@ export const DeleteUserForm = withCardStateProvider((props: DeleteUserFormProps) await deleteUserWithReverification(); const redirectUrl = otherSessions.length === 0 ? afterSignOutUrl : afterMultiSessionSingleSignOutUrl; - return await signOut({ + return await setActive({ + session: null, redirectUrl, }); } catch (e) { From 8e613d345e5fe8afe536295e5fc583fa7d984602 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Thu, 13 Feb 2025 17:52:47 +0200 Subject: [PATCH 5/8] clean up --- packages/clerk-js/src/core/clerk.ts | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index aba525205df..36307bac127 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -374,7 +374,8 @@ export class Clerk implements ClerkInterface { const handleSetActive = () => { const signOutCallback = typeof callbackOrOptions === 'function' ? callbackOrOptions : undefined; - this.#broadcastSignOutEvent(); + // Notify other tabs that user is signing out. + this.__internal_broadcastSignOutEvent(); if (signOutCallback) { return this.setActive({ session: null, @@ -880,14 +881,6 @@ export class Clerk implements ClerkInterface { await onBeforeSetActive(); - // If this.session exists, then signOut was triggered by the current tab - // and should emit. Other tabs should not emit the same event again - // const shouldSignOutSession = this.session && newSession === null; - // if (shouldSignOutSession) { - // this.#broadcastSignOutEvent(); - // eventBus.dispatch(events.TokenUpdate, { token: null }); - // } - //1. setLastActiveSession to passed user session (add a param). // Note that this will also update the session's active organization // id. @@ -1518,7 +1511,7 @@ export class Clerk implements ClerkInterface { return; } if (opts.broadcast) { - this.#broadcastSignOutEvent(); + this.__internal_broadcastSignOutEvent(); } return this.setActive({ session: null }); } catch (err) { @@ -2070,12 +2063,8 @@ export class Clerk implements ClerkInterface { } }; - #broadcastSignOutEvent = () => { - this.#broadcastChannel?.postMessage({ type: 'signout' }); - }; - public __internal_broadcastSignOutEvent = () => { - this.#broadcastSignOutEvent(); + this.#broadcastChannel?.postMessage({ type: 'signout' }); }; #setTransitiveState = () => { From b71c79bb22e85e6d610e4586e9bdf9de30d48be2 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Mon, 17 Feb 2025 22:31:32 +0200 Subject: [PATCH 6/8] revert skipping client update --- packages/clerk-js/src/core/resources/Base.ts | 13 +++---------- packages/clerk-js/src/core/resources/Client.ts | 6 ------ 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/packages/clerk-js/src/core/resources/Base.ts b/packages/clerk-js/src/core/resources/Base.ts index 296f6985256..105e255a37c 100644 --- a/packages/clerk-js/src/core/resources/Base.ts +++ b/packages/clerk-js/src/core/resources/Base.ts @@ -11,7 +11,6 @@ import { ClerkAPIResponseError, ClerkRuntimeError, Client } from './internal'; export type BaseFetchOptions = ClerkResourceReloadParams & { forceUpdateClient?: boolean; fetchMaxTries?: number; - skipUpdateClient?: boolean; }; export type BaseMutateParams = { @@ -19,7 +18,6 @@ export type BaseMutateParams = { body?: any; method?: HTTPMethod; path?: string; - skipUpdateClient?: boolean; }; function assertProductionKeysOnDev(statusCode: number, payloadErrors?: ClerkAPIErrorJSON[]) { @@ -107,7 +105,7 @@ export abstract class BaseResource { } // TODO: Link to Client payload piggybacking design document - if ((requestInit.method !== 'GET' || opts.forceUpdateClient) && !opts.skipUpdateClient) { + if (requestInit.method !== 'GET' || opts.forceUpdateClient) { this._updateClient(payload); } @@ -182,13 +180,8 @@ export abstract class BaseResource { } protected async _baseMutate(params: BaseMutateParams): Promise { - const { action, body, method, path, skipUpdateClient } = params; - let json; - if (skipUpdateClient) { - json = await BaseResource._fetch({ method, path: path || this.path(action), body }, { skipUpdateClient }); - } else { - json = await BaseResource._fetch({ method, path: path || this.path(action), body }); - } + const { action, body, method, path } = params; + const json = await BaseResource._fetch({ method, path: path || this.path(action), body }); return this.fromJSON((json?.response || json) as J); } diff --git a/packages/clerk-js/src/core/resources/Client.ts b/packages/clerk-js/src/core/resources/Client.ts index e4a27332d9d..fb0b9b707c7 100644 --- a/packages/clerk-js/src/core/resources/Client.ts +++ b/packages/clerk-js/src/core/resources/Client.ts @@ -83,12 +83,6 @@ export class Client extends BaseResource implements ClientResource { removeSessions(): Promise { return this._baseDelete({ path: this.path() + '/sessions', - /** - * Skipping updating the client matches the behaviour of `client.destroy`, which allows broadcasting a sign-out event, - * and delays emitting until `setActive` is called within `Clerk.signOut()` - * TODO: Remove this property while persisting the desired outcome - */ - skipUpdateClient: false, }).then(e => { SessionTokenCache.clear(); return e as unknown as ClientResource; From 0bcd8b19a4d83928eb23c57805c2a99622b55ff4 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Mon, 17 Feb 2025 22:33:43 +0200 Subject: [PATCH 7/8] update changeset --- .changeset/new-fishes-rescue.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/new-fishes-rescue.md b/.changeset/new-fishes-rescue.md index f5da358c0a7..7878052008b 100644 --- a/.changeset/new-fishes-rescue.md +++ b/.changeset/new-fishes-rescue.md @@ -2,4 +2,4 @@ '@clerk/clerk-js': patch --- -Avoid updating client when removing all sessions. +Bug fix: Broadcast a sign out event to all opened tabs when `Clerk.signOut()` or `User.delete()` is called. From 3cd011e2a04a263bf497efc45a1db928321c9e50 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Tue, 18 Feb 2025 11:33:20 +0200 Subject: [PATCH 8/8] use event bus --- packages/clerk-js/src/core/clerk.ts | 15 +++++++++------ packages/clerk-js/src/core/events.ts | 2 ++ packages/clerk-js/src/core/resources/User.ts | 3 ++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 9b8f3307cea..05853300d2b 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -376,7 +376,7 @@ export class Clerk implements ClerkInterface { const signOutCallback = typeof callbackOrOptions === 'function' ? callbackOrOptions : undefined; // Notify other tabs that user is signing out. - this.__internal_broadcastSignOutEvent(); + eventBus.dispatch(events.UserSignOut, null); if (signOutCallback) { return this.setActive({ session: null, @@ -1541,7 +1541,7 @@ export class Clerk implements ClerkInterface { return; } if (opts.broadcast) { - this.__internal_broadcastSignOutEvent(); + eventBus.dispatch(events.UserSignOut, null); } return this.setActive({ session: null }); } catch (err) { @@ -2065,6 +2065,13 @@ export class Clerk implements ClerkInterface { void this.handleUnauthenticated({ broadcast: false }); } }); + + /** + * Allow resources within the singleton to notify other tabs about a signout event (scoped to a single tab) + */ + eventBus.on(events.UserSignOut, () => { + this.#broadcastChannel?.postMessage({ type: 'signout' }); + }); }; // TODO: Be more conservative about touches. Throttle, don't touch when only one user, etc @@ -2099,10 +2106,6 @@ export class Clerk implements ClerkInterface { } }; - public __internal_broadcastSignOutEvent = () => { - this.#broadcastChannel?.postMessage({ type: 'signout' }); - }; - #setTransitiveState = () => { this.session = undefined; this.organization = undefined; diff --git a/packages/clerk-js/src/core/events.ts b/packages/clerk-js/src/core/events.ts index fb5ecc263cd..7401dd91370 100644 --- a/packages/clerk-js/src/core/events.ts +++ b/packages/clerk-js/src/core/events.ts @@ -2,6 +2,7 @@ import type { TokenResource } from '@clerk/types'; export const events = { TokenUpdate: 'token:update', + UserSignOut: 'user:signOut', } as const; type ClerkEvent = (typeof events)[keyof typeof events]; @@ -11,6 +12,7 @@ type TokenUpdatePayload = { token: TokenResource | null }; type EventPayload = { [events.TokenUpdate]: TokenUpdatePayload; + [events.UserSignOut]: null; }; const createEventBus = () => { diff --git a/packages/clerk-js/src/core/resources/User.ts b/packages/clerk-js/src/core/resources/User.ts index 7ec0289e616..10d35d5c644 100644 --- a/packages/clerk-js/src/core/resources/User.ts +++ b/packages/clerk-js/src/core/resources/User.ts @@ -35,6 +35,7 @@ import type { import { unixEpochToDate } from '../../utils/date'; import { normalizeUnsafeMetadata } from '../../utils/resourceParams'; import { getFullName } from '../../utils/user'; +import { eventBus, events } from '../events'; import { BackupCode } from './BackupCode'; import { BaseResource, @@ -242,7 +243,7 @@ export class User extends BaseResource implements UserResource { delete = (): Promise => { return this._baseDelete({ path: '/me' }).then(res => { - User.clerk.__internal_broadcastSignOutEvent(); + eventBus.dispatch(events.UserSignOut, null); return res; }); };