From a2d6ba10e412365a1fda2a19691e170a34cb8459 Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Thu, 14 Aug 2025 10:50:48 -0500 Subject: [PATCH 1/8] feat(clerk-js,types): Signals fetchStatus --- packages/clerk-js/src/core/events.ts | 3 +++ packages/clerk-js/src/core/resources/SignIn.ts | 2 -- packages/clerk-js/src/core/signals.ts | 12 +++++------- packages/clerk-js/src/core/state.ts | 12 ++++++++++-- packages/types/src/signIn.ts | 1 - 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/clerk-js/src/core/events.ts b/packages/clerk-js/src/core/events.ts index 858aa6d10ee..6fb12dc2fa1 100644 --- a/packages/clerk-js/src/core/events.ts +++ b/packages/clerk-js/src/core/events.ts @@ -10,11 +10,13 @@ export const events = { SessionTokenResolved: 'session:tokenResolved', ResourceUpdate: 'resource:update', ResourceError: 'resource:error', + ResourceFetch: 'resource:fetch', } as const; type TokenUpdatePayload = { token: TokenResource | null }; export type ResourceUpdatePayload = { resource: BaseResource }; export type ResourceErrorPayload = { resource: BaseResource; error: unknown }; +export type ResourceFetchPayload = { resource: BaseResource; status: 'idle' | 'fetching' }; type InternalEvents = { [events.TokenUpdate]: TokenUpdatePayload; @@ -23,6 +25,7 @@ type InternalEvents = { [events.SessionTokenResolved]: null; [events.ResourceUpdate]: ResourceUpdatePayload; [events.ResourceError]: ResourceErrorPayload; + [events.ResourceFetch]: ResourceFetchPayload; }; export const eventBus = createEventBus(); diff --git a/packages/clerk-js/src/core/resources/SignIn.ts b/packages/clerk-js/src/core/resources/SignIn.ts index efda65c29ad..17fafcfce2c 100644 --- a/packages/clerk-js/src/core/resources/SignIn.ts +++ b/packages/clerk-js/src/core/resources/SignIn.ts @@ -493,8 +493,6 @@ class SignInFuture implements SignInFutureResource { submitPassword: this.submitResetPassword.bind(this), }; - fetchStatus: 'idle' | 'fetching' = 'idle'; - constructor(readonly resource: SignIn) {} get status() { diff --git a/packages/clerk-js/src/core/signals.ts b/packages/clerk-js/src/core/signals.ts index 5e10161485c..c2e7330a481 100644 --- a/packages/clerk-js/src/core/signals.ts +++ b/packages/clerk-js/src/core/signals.ts @@ -4,20 +4,18 @@ import { computed, signal } from 'alien-signals'; import type { SignIn } from './resources/SignIn'; -export const signInSignal = signal<{ resource: SignIn | null }>({ resource: null }); +export const signInResourceSignal = signal<{ resource: SignIn | null }>({ resource: null }); export const signInErrorSignal = signal<{ error: unknown }>({ error: null }); +export const signInFetchSignal = signal<{ status: 'idle' | 'fetching' }>({ status: 'idle' }); export const signInComputedSignal = computed(() => { - const signIn = signInSignal().resource; + const signIn = signInResourceSignal().resource; const error = signInErrorSignal().error; + const fetchStatus = signInFetchSignal().status; const errors = errorsToParsedErrors(error); - if (!signIn) { - return { errors, signIn: null }; - } - - return { errors, signIn: signIn.__internal_future }; + return { errors, fetchStatus, signIn: signIn ? signIn.__internal_future : null }; }); /** diff --git a/packages/clerk-js/src/core/state.ts b/packages/clerk-js/src/core/state.ts index 94255e8a6e6..cdb778bc728 100644 --- a/packages/clerk-js/src/core/state.ts +++ b/packages/clerk-js/src/core/state.ts @@ -4,11 +4,12 @@ import { computed, effect } from 'alien-signals'; import { eventBus } from './events'; import type { BaseResource } from './resources/Base'; import { SignIn } from './resources/SignIn'; -import { signInComputedSignal, signInErrorSignal, signInSignal } from './signals'; +import { signInComputedSignal, signInErrorSignal, signInFetchSignal, signInResourceSignal } from './signals'; export class State implements StateInterface { - signInResourceSignal = signInSignal; + signInResourceSignal = signInResourceSignal; signInErrorSignal = signInErrorSignal; + signInFetchSignal = signInFetchSignal; signInSignal = signInComputedSignal; __internal_effect = effect; @@ -17,6 +18,7 @@ export class State implements StateInterface { constructor() { eventBus.on('resource:update', this.onResourceUpdated); eventBus.on('resource:error', this.onResourceError); + eventBus.on('resource:fetch', this.onResourceFetch); } private onResourceError = (payload: { resource: BaseResource; error: unknown }) => { @@ -30,4 +32,10 @@ export class State implements StateInterface { this.signInResourceSignal({ resource: payload.resource }); } }; + + private onResourceFetch = (payload: { resource: BaseResource; status: 'idle' | 'fetching' }) => { + if (payload.resource instanceof SignIn) { + this.signInFetchSignal({ status: payload.status }); + } + }; } diff --git a/packages/types/src/signIn.ts b/packages/types/src/signIn.ts index 7c7dc10b7de..4c504e1daa8 100644 --- a/packages/types/src/signIn.ts +++ b/packages/types/src/signIn.ts @@ -126,7 +126,6 @@ export interface SignInResource extends ClerkResource { } export interface SignInFutureResource { - fetchStatus: 'idle' | 'fetching'; availableStrategies: SignInFirstFactor[]; status: SignInStatus | null; create: (params: { From 5065c9b2d5f391c60164df85466444a4d956fab3 Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Thu, 14 Aug 2025 11:07:10 -0500 Subject: [PATCH 2/8] feat(clerk-js): Add runAsyncTask --- .../src/utils/__tests__/runAsyncTask.spec.ts | 65 +++++++++++++++++++ packages/clerk-js/src/utils/runAsyncTask.ts | 30 +++++++++ 2 files changed, 95 insertions(+) create mode 100644 packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts create mode 100644 packages/clerk-js/src/utils/runAsyncTask.ts diff --git a/packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts b/packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts new file mode 100644 index 00000000000..7c18341346a --- /dev/null +++ b/packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts @@ -0,0 +1,65 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; + +import { eventBus } from '../../core/events'; +import { runAsyncTask } from '../runAsyncTask'; + +describe('runAsyncTask', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + const resource = {} as any; // runAsyncTask doesn't depend on resource being a BaseResource + + it('emits fetching/idle and returns result on success', async () => { + const emitSpy = vi.spyOn(eventBus, 'emit'); + const task = vi.fn().mockResolvedValue('ok'); + + const { result, error } = await runAsyncTask(resource, task); + + expect(task).toHaveBeenCalledTimes(1); + expect(result).toBe('ok'); + expect(error).toBeNull(); + + expect(emitSpy).toHaveBeenNthCalledWith(1, 'resource:error', { + resource, + error: null, + }); + expect(emitSpy).toHaveBeenNthCalledWith(2, 'resource:fetch', { + resource, + status: 'fetching', + }); + expect(emitSpy).toHaveBeenNthCalledWith(3, 'resource:fetch', { + resource, + status: 'idle', + }); + }); + + it('emits error and returns error on failure', async () => { + const emitSpy = vi.spyOn(eventBus, 'emit'); + const thrown = new Error('fail'); + const task = vi.fn().mockRejectedValue(thrown); + + const { result, error } = await runAsyncTask(resource, task); + + expect(task).toHaveBeenCalledTimes(1); + expect(result).toBeUndefined(); + expect(error).toBe(thrown); + + expect(emitSpy).toHaveBeenNthCalledWith(1, 'resource:error', { + resource, + error: null, + }); + expect(emitSpy).toHaveBeenNthCalledWith(2, 'resource:fetch', { + resource, + status: 'fetching', + }); + expect(emitSpy).toHaveBeenNthCalledWith(3, 'resource:error', { + resource, + error: thrown, + }); + expect(emitSpy).toHaveBeenNthCalledWith(4, 'resource:fetch', { + resource, + status: 'idle', + }); + }); +}); diff --git a/packages/clerk-js/src/utils/runAsyncTask.ts b/packages/clerk-js/src/utils/runAsyncTask.ts new file mode 100644 index 00000000000..1f94ccc21ca --- /dev/null +++ b/packages/clerk-js/src/utils/runAsyncTask.ts @@ -0,0 +1,30 @@ +import { eventBus } from '../core/events'; +import type { BaseResource } from '../core/resources/internal'; + +/** + * Wrap an async task with handling for emitting error and fetch events, which reduces boilerplate. Used in our Custom + * Flow APIs. + */ +export async function runAsyncTask( + resource: BaseResource, + task: () => Promise, +): Promise<{ result?: T; error: unknown }> { + eventBus.emit('resource:error', { resource, error: null }); + eventBus.emit('resource:fetch', { + resource, + status: 'fetching', + }); + + try { + const result = await task(); + return { result, error: null }; + } catch (err) { + eventBus.emit('resource:error', { resource, error: err }); + return { error: err }; + } finally { + eventBus.emit('resource:fetch', { + resource, + status: 'idle', + }); + } +} From 550d029a55d80508f30be56f586843ec102376de Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Thu, 14 Aug 2025 11:24:32 -0500 Subject: [PATCH 3/8] feat(clerk-js,types): Refactor to use runAsyncTask --- .../clerk-js/src/core/resources/SignIn.ts | 93 ++++--------------- packages/types/src/state.ts | 1 + 2 files changed, 21 insertions(+), 73 deletions(-) diff --git a/packages/clerk-js/src/core/resources/SignIn.ts b/packages/clerk-js/src/core/resources/SignIn.ts index 17fafcfce2c..a3310cdc95a 100644 --- a/packages/clerk-js/src/core/resources/SignIn.ts +++ b/packages/clerk-js/src/core/resources/SignIn.ts @@ -69,6 +69,7 @@ import { } from '../errors'; import { eventBus } from '../events'; import { BaseResource, UserData, Verification } from './internal'; +import { runAsyncTask } from '../../utils/runAsyncTask'; export class SignIn extends BaseResource implements SignInResource { pathRoot = '/client/sign_ins'; @@ -504,8 +505,7 @@ class SignInFuture implements SignInFutureResource { } async sendResetPasswordEmailCode(): Promise<{ error: unknown }> { - eventBus.emit('resource:error', { resource: this.resource, error: null }); - try { + return runAsyncTask(this.resource, async () => { if (!this.resource.id) { throw new Error('Cannot reset password without a sign in.'); } @@ -523,27 +523,16 @@ class SignInFuture implements SignInFutureResource { body: { emailAddressId, strategy: 'reset_password_email_code' }, action: 'prepare_first_factor', }); - } catch (err: unknown) { - eventBus.emit('resource:error', { resource: this.resource, error: err }); - return { error: err }; - } - - return { error: null }; + }); } async verifyResetPasswordEmailCode({ code }: { code: string }): Promise<{ error: unknown }> { - eventBus.emit('resource:error', { resource: this.resource, error: null }); - try { + return runAsyncTask(this.resource, async () => { await this.resource.__internal_basePost({ body: { code, strategy: 'reset_password_email_code' }, action: 'attempt_first_factor', }); - } catch (err: unknown) { - eventBus.emit('resource:error', { resource: this.resource, error: err }); - return { error: err }; - } - - return { error: null }; + }); } async submitResetPassword({ @@ -553,18 +542,12 @@ class SignInFuture implements SignInFutureResource { password: string; signOutOfOtherSessions?: boolean; }): Promise<{ error: unknown }> { - eventBus.emit('resource:error', { resource: this.resource, error: null }); - try { + return runAsyncTask(this.resource, async () => { await this.resource.__internal_basePost({ body: { password, signOutOfOtherSessions }, action: 'reset_password', }); - } catch (err: unknown) { - eventBus.emit('resource:error', { resource: this.resource, error: err }); - return { error: err }; - } - - return { error: null }; + }); } async create(params: { @@ -573,39 +556,26 @@ class SignInFuture implements SignInFutureResource { redirectUrl?: string; actionCompleteRedirectUrl?: string; }): Promise<{ error: unknown }> { - eventBus.emit('resource:error', { resource: this.resource, error: null }); - try { + return runAsyncTask(this.resource, async () => { await this.resource.__internal_basePost({ path: this.resource.pathRoot, body: params, }); - - return { error: null }; - } catch (err: unknown) { - eventBus.emit('resource:error', { resource: this.resource, error: err }); - return { error: err }; - } + }); } async password({ identifier, password }: { identifier?: string; password: string }): Promise<{ error: unknown }> { - eventBus.emit('resource:error', { resource: this.resource, error: null }); - const previousIdentifier = this.resource.identifier; - try { + return runAsyncTask(this.resource, async () => { + const previousIdentifier = this.resource.identifier; await this.resource.__internal_basePost({ path: this.resource.pathRoot, body: { identifier: identifier || previousIdentifier, password }, }); - } catch (err: unknown) { - eventBus.emit('resource:error', { resource: this.resource, error: err }); - return { error: err }; - } - - return { error: null }; + }); } async sendEmailCode({ email }: { email: string }): Promise<{ error: unknown }> { - eventBus.emit('resource:error', { resource: this.resource, error: null }); - try { + return runAsyncTask(this.resource, async () => { if (!this.resource.id) { await this.create({ identifier: email }); } @@ -621,27 +591,16 @@ class SignInFuture implements SignInFutureResource { body: { emailAddressId, strategy: 'email_code' }, action: 'prepare_first_factor', }); - } catch (err: unknown) { - eventBus.emit('resource:error', { resource: this.resource, error: err }); - return { error: err }; - } - - return { error: null }; + }); } async verifyEmailCode({ code }: { code: string }): Promise<{ error: unknown }> { - eventBus.emit('resource:error', { resource: this.resource, error: null }); - try { + return runAsyncTask(this.resource, async () => { await this.resource.__internal_basePost({ body: { code, strategy: 'email_code' }, action: 'attempt_first_factor', }); - } catch (err: unknown) { - eventBus.emit('resource:error', { resource: this.resource, error: err }); - return { error: err }; - } - - return { error: null }; + }); } async sso({ @@ -655,8 +614,7 @@ class SignInFuture implements SignInFutureResource { redirectUrl: string; redirectUrlComplete: string; }): Promise<{ error: unknown }> { - eventBus.emit('resource:error', { resource: this.resource, error: null }); - try { + return runAsyncTask(this.resource, async () => { if (flow !== 'auto') { throw new Error('modal flow is not supported yet'); } @@ -676,27 +634,16 @@ class SignInFuture implements SignInFutureResource { if (status === 'unverified' && externalVerificationRedirectURL) { windowNavigate(externalVerificationRedirectURL); } - } catch (err: unknown) { - eventBus.emit('resource:error', { resource: this.resource, error: err }); - return { error: err }; - } - - return { error: null }; + }); } async finalize(): Promise<{ error: unknown }> { - eventBus.emit('resource:error', { resource: this.resource, error: null }); - try { + return runAsyncTask(this.resource, async () => { if (!this.resource.createdSessionId) { throw new Error('Cannot finalize sign-in without a created session.'); } await SignIn.clerk.setActive({ session: this.resource.createdSessionId }); - } catch (err: unknown) { - eventBus.emit('resource:error', { resource: this.resource, error: err }); - return { error: err }; - } - - return { error: null }; + }); } } diff --git a/packages/types/src/state.ts b/packages/types/src/state.ts index 200fe15cebd..007bd92f295 100644 --- a/packages/types/src/state.ts +++ b/packages/types/src/state.ts @@ -32,6 +32,7 @@ export interface State { signInSignal: { (): { errors: Errors; + fetchStatus: 'idle' | 'fetching'; signIn: SignInFutureResource | null; }; }; From e2bd52f9121487070a6d0321dd65f6d9105eaa6c Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Thu, 14 Aug 2025 11:25:17 -0500 Subject: [PATCH 4/8] chore(repo): Add changeset --- .changeset/twelve-crabs-return.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/twelve-crabs-return.md diff --git a/.changeset/twelve-crabs-return.md b/.changeset/twelve-crabs-return.md new file mode 100644 index 00000000000..4f618671b13 --- /dev/null +++ b/.changeset/twelve-crabs-return.md @@ -0,0 +1,6 @@ +--- +'@clerk/clerk-js': minor +'@clerk/types': minor +--- + +[Experimental] Signal `fetchStatus` support. From 5164243e2d6514157abb562b9d3d82c7b5755c90 Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Thu, 14 Aug 2025 11:33:48 -0500 Subject: [PATCH 5/8] feat(clerk-js,types): Support navigate param to finalize --- packages/clerk-js/src/core/resources/SignIn.ts | 5 +++-- packages/types/src/signIn.ts | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/clerk-js/src/core/resources/SignIn.ts b/packages/clerk-js/src/core/resources/SignIn.ts index a3310cdc95a..89f77c85804 100644 --- a/packages/clerk-js/src/core/resources/SignIn.ts +++ b/packages/clerk-js/src/core/resources/SignIn.ts @@ -26,6 +26,7 @@ import type { ResetPasswordParams, ResetPasswordPhoneCodeFactorConfig, SamlConfig, + SetActiveNavigate, SignInCreateParams, SignInFirstFactor, SignInFutureResource, @@ -637,13 +638,13 @@ class SignInFuture implements SignInFutureResource { }); } - async finalize(): Promise<{ error: unknown }> { + async finalize({ navigate }: { navigate?: SetActiveNavigate }): Promise<{ error: unknown }> { return runAsyncTask(this.resource, async () => { if (!this.resource.createdSessionId) { throw new Error('Cannot finalize sign-in without a created session.'); } - await SignIn.clerk.setActive({ session: this.resource.createdSessionId }); + await SignIn.clerk.setActive({ session: this.resource.createdSessionId, navigate }); }); } } diff --git a/packages/types/src/signIn.ts b/packages/types/src/signIn.ts index 4c504e1daa8..f1f7ff23920 100644 --- a/packages/types/src/signIn.ts +++ b/packages/types/src/signIn.ts @@ -1,3 +1,4 @@ +import type { SetActiveNavigate } from './clerk'; import type { BackupCodeAttempt, BackupCodeFactor, @@ -150,7 +151,7 @@ export interface SignInFutureResource { redirectUrl: string; redirectUrlComplete: string; }) => Promise<{ error: unknown }>; - finalize: () => Promise<{ error: unknown }>; + finalize: (params: { navigate?: SetActiveNavigate }) => Promise<{ error: unknown }>; } export type SignInStatus = From d7f905a2ec798bf6947759deb0ad45b28c515668 Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Thu, 14 Aug 2025 11:40:09 -0500 Subject: [PATCH 6/8] fix(clerk-js): Don't push null to raw/global errors --- packages/clerk-js/src/core/signals.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/clerk-js/src/core/signals.ts b/packages/clerk-js/src/core/signals.ts index c2e7330a481..88cc6118cde 100644 --- a/packages/clerk-js/src/core/signals.ts +++ b/packages/clerk-js/src/core/signals.ts @@ -40,6 +40,10 @@ function errorsToParsedErrors(error: unknown): Errors { global: [], }; + if (!error) { + return parsedErrors; + } + if (!isClerkAPIResponseError(error)) { parsedErrors.raw.push(error); parsedErrors.global.push(error); From d99ed165d2e2e5dab44a0740c493d947cd6016b0 Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Thu, 14 Aug 2025 12:18:46 -0500 Subject: [PATCH 7/8] chore(clerk-js): Lint --- packages/clerk-js/src/core/resources/SignIn.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/clerk-js/src/core/resources/SignIn.ts b/packages/clerk-js/src/core/resources/SignIn.ts index 89f77c85804..056426be8bf 100644 --- a/packages/clerk-js/src/core/resources/SignIn.ts +++ b/packages/clerk-js/src/core/resources/SignIn.ts @@ -59,6 +59,7 @@ import { webAuthnGetCredential as webAuthnGetCredentialOnWindow, } from '../../utils/passkeys'; import { createValidatePassword } from '../../utils/passwords/password'; +import { runAsyncTask } from '../../utils/runAsyncTask'; import { clerkInvalidFAPIResponse, clerkInvalidStrategy, @@ -70,7 +71,6 @@ import { } from '../errors'; import { eventBus } from '../events'; import { BaseResource, UserData, Verification } from './internal'; -import { runAsyncTask } from '../../utils/runAsyncTask'; export class SignIn extends BaseResource implements SignInResource { pathRoot = '/client/sign_ins'; From 37b024d687ce411c96a9392dd9bdbf1c38792447 Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Thu, 14 Aug 2025 13:15:31 -0500 Subject: [PATCH 8/8] fix(clerk-js): Rename to runAsyncResourceTask --- .../clerk-js/src/core/resources/SignIn.ts | 20 +++++++++---------- ...k.spec.ts => runAsyncResourceTask.spec.ts} | 6 +++--- ...unAsyncTask.ts => runAsyncResourceTask.ts} | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) rename packages/clerk-js/src/utils/__tests__/{runAsyncTask.spec.ts => runAsyncResourceTask.spec.ts} (88%) rename packages/clerk-js/src/utils/{runAsyncTask.ts => runAsyncResourceTask.ts} (94%) diff --git a/packages/clerk-js/src/core/resources/SignIn.ts b/packages/clerk-js/src/core/resources/SignIn.ts index 056426be8bf..707b2b97261 100644 --- a/packages/clerk-js/src/core/resources/SignIn.ts +++ b/packages/clerk-js/src/core/resources/SignIn.ts @@ -59,7 +59,7 @@ import { webAuthnGetCredential as webAuthnGetCredentialOnWindow, } from '../../utils/passkeys'; import { createValidatePassword } from '../../utils/passwords/password'; -import { runAsyncTask } from '../../utils/runAsyncTask'; +import { runAsyncResourceTask } from '../../utils/runAsyncResourceTask'; import { clerkInvalidFAPIResponse, clerkInvalidStrategy, @@ -506,7 +506,7 @@ class SignInFuture implements SignInFutureResource { } async sendResetPasswordEmailCode(): Promise<{ error: unknown }> { - return runAsyncTask(this.resource, async () => { + return runAsyncResourceTask(this.resource, async () => { if (!this.resource.id) { throw new Error('Cannot reset password without a sign in.'); } @@ -528,7 +528,7 @@ class SignInFuture implements SignInFutureResource { } async verifyResetPasswordEmailCode({ code }: { code: string }): Promise<{ error: unknown }> { - return runAsyncTask(this.resource, async () => { + return runAsyncResourceTask(this.resource, async () => { await this.resource.__internal_basePost({ body: { code, strategy: 'reset_password_email_code' }, action: 'attempt_first_factor', @@ -543,7 +543,7 @@ class SignInFuture implements SignInFutureResource { password: string; signOutOfOtherSessions?: boolean; }): Promise<{ error: unknown }> { - return runAsyncTask(this.resource, async () => { + return runAsyncResourceTask(this.resource, async () => { await this.resource.__internal_basePost({ body: { password, signOutOfOtherSessions }, action: 'reset_password', @@ -557,7 +557,7 @@ class SignInFuture implements SignInFutureResource { redirectUrl?: string; actionCompleteRedirectUrl?: string; }): Promise<{ error: unknown }> { - return runAsyncTask(this.resource, async () => { + return runAsyncResourceTask(this.resource, async () => { await this.resource.__internal_basePost({ path: this.resource.pathRoot, body: params, @@ -566,7 +566,7 @@ class SignInFuture implements SignInFutureResource { } async password({ identifier, password }: { identifier?: string; password: string }): Promise<{ error: unknown }> { - return runAsyncTask(this.resource, async () => { + return runAsyncResourceTask(this.resource, async () => { const previousIdentifier = this.resource.identifier; await this.resource.__internal_basePost({ path: this.resource.pathRoot, @@ -576,7 +576,7 @@ class SignInFuture implements SignInFutureResource { } async sendEmailCode({ email }: { email: string }): Promise<{ error: unknown }> { - return runAsyncTask(this.resource, async () => { + return runAsyncResourceTask(this.resource, async () => { if (!this.resource.id) { await this.create({ identifier: email }); } @@ -596,7 +596,7 @@ class SignInFuture implements SignInFutureResource { } async verifyEmailCode({ code }: { code: string }): Promise<{ error: unknown }> { - return runAsyncTask(this.resource, async () => { + return runAsyncResourceTask(this.resource, async () => { await this.resource.__internal_basePost({ body: { code, strategy: 'email_code' }, action: 'attempt_first_factor', @@ -615,7 +615,7 @@ class SignInFuture implements SignInFutureResource { redirectUrl: string; redirectUrlComplete: string; }): Promise<{ error: unknown }> { - return runAsyncTask(this.resource, async () => { + return runAsyncResourceTask(this.resource, async () => { if (flow !== 'auto') { throw new Error('modal flow is not supported yet'); } @@ -639,7 +639,7 @@ class SignInFuture implements SignInFutureResource { } async finalize({ navigate }: { navigate?: SetActiveNavigate }): Promise<{ error: unknown }> { - return runAsyncTask(this.resource, async () => { + return runAsyncResourceTask(this.resource, async () => { if (!this.resource.createdSessionId) { throw new Error('Cannot finalize sign-in without a created session.'); } diff --git a/packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts b/packages/clerk-js/src/utils/__tests__/runAsyncResourceTask.spec.ts similarity index 88% rename from packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts rename to packages/clerk-js/src/utils/__tests__/runAsyncResourceTask.spec.ts index 7c18341346a..2fd83b89f76 100644 --- a/packages/clerk-js/src/utils/__tests__/runAsyncTask.spec.ts +++ b/packages/clerk-js/src/utils/__tests__/runAsyncResourceTask.spec.ts @@ -1,7 +1,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { eventBus } from '../../core/events'; -import { runAsyncTask } from '../runAsyncTask'; +import { runAsyncResourceTask } from '../runAsyncResourceTask'; describe('runAsyncTask', () => { afterEach(() => { @@ -14,7 +14,7 @@ describe('runAsyncTask', () => { const emitSpy = vi.spyOn(eventBus, 'emit'); const task = vi.fn().mockResolvedValue('ok'); - const { result, error } = await runAsyncTask(resource, task); + const { result, error } = await runAsyncResourceTask(resource, task); expect(task).toHaveBeenCalledTimes(1); expect(result).toBe('ok'); @@ -39,7 +39,7 @@ describe('runAsyncTask', () => { const thrown = new Error('fail'); const task = vi.fn().mockRejectedValue(thrown); - const { result, error } = await runAsyncTask(resource, task); + const { result, error } = await runAsyncResourceTask(resource, task); expect(task).toHaveBeenCalledTimes(1); expect(result).toBeUndefined(); diff --git a/packages/clerk-js/src/utils/runAsyncTask.ts b/packages/clerk-js/src/utils/runAsyncResourceTask.ts similarity index 94% rename from packages/clerk-js/src/utils/runAsyncTask.ts rename to packages/clerk-js/src/utils/runAsyncResourceTask.ts index 1f94ccc21ca..c927573a388 100644 --- a/packages/clerk-js/src/utils/runAsyncTask.ts +++ b/packages/clerk-js/src/utils/runAsyncResourceTask.ts @@ -5,7 +5,7 @@ import type { BaseResource } from '../core/resources/internal'; * Wrap an async task with handling for emitting error and fetch events, which reduces boilerplate. Used in our Custom * Flow APIs. */ -export async function runAsyncTask( +export async function runAsyncResourceTask( resource: BaseResource, task: () => Promise, ): Promise<{ result?: T; error: unknown }> {