-
Notifications
You must be signed in to change notification settings - Fork 370
poc: observable signin resource #6078
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
6cbb936
62db5aa
7d23568
c22693b
9e91e93
13d3539
7b816ad
023f492
4ce2005
d0ae5ed
084c46b
68f998b
e49cd1b
64f9db8
8da5fd1
c25fee7
8c80879
c52b7fb
e0e152e
c3ec5ee
90c702a
c9aa1bf
87620c3
c25e560
8de72f0
714d264
7d5e6b3
b2d84b5
0d8d45a
c113bd8
8820c2c
e6c2610
39a7097
abcafc2
cd7afb7
fd74064
c211b68
5768162
20a49e1
c90c461
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -17,7 +17,6 @@ import type { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
EmailLinkConfig, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
EnterpriseSSOConfig, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
PassKeyConfig, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
PasskeyFactor, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
PhoneCodeConfig, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
PrepareFirstFactorParams, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
PrepareSecondFactorParams, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -39,6 +38,8 @@ import type { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Web3SignatureConfig, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Web3SignatureFactor, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} from '@clerk/types'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { devtools } from 'zustand/middleware'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { createStore } from 'zustand/vanilla'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
generateSignatureWithCoinbaseWallet, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -67,31 +68,116 @@ import { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
clerkVerifyWeb3WalletCalledBeforeCreate, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} from '../errors'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { BaseResource, UserData, Verification } from './internal'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { createResourceSlice, type ResourceStore } from './state'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type SignInSliceState = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
signin: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
status: SignInStatus | null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setStatus: (status: SignInStatus | null) => void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
error: { global: string | null; fields: Record<string, string> }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setError: (error: { global: string | null; fields: Record<string, string> }) => void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Creates a SignIn slice following the Zustand slices pattern. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* This slice handles SignIn-specific state management. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* All SignIn state is namespaced under the 'signin' key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const createSignInSlice = (set: any, _get: any): SignInSliceState => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
signin: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
status: null, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setStatus: (status: SignInStatus | null) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
set((state: any) => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
...state, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
signin: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
...state.signin, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
status: status, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
})); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
error: { global: null, fields: {} }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setError: (error: { global: string | null; fields: Record<string, string> }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
set((state: any) => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
...state, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
signin: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
...state.signin, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
error: error, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
})); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type CombinedSignInStore = ResourceStore<SignIn> & SignInSliceState; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
export class SignIn extends BaseResource implements SignInResource { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
pathRoot = '/client/sign_ins'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
createdSessionId: string | null = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
firstFactorVerification: VerificationResource = new Verification(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
id?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
status: SignInStatus | null = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
supportedIdentifiers: SignInIdentifier[] = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
identifier: string | null = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
secondFactorVerification: VerificationResource = new Verification(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
supportedFirstFactors: SignInFirstFactor[] | null = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
supportedIdentifiers: SignInIdentifier[] = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
supportedSecondFactors: SignInSecondFactor[] | null = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
firstFactorVerification: VerificationResource = new Verification(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
secondFactorVerification: VerificationResource = new Verification(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
identifier: string | null = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
createdSessionId: string | null = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
userData: UserData = new UserData(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
constructor(data: SignInJSON | SignInJSONSnapshot | null = null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
super(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Override the base _store with our combined store using slices pattern with namespacing | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this._store = createStore<CombinedSignInStore>()( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
devtools( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(set, get) => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
...createResourceSlice<SignIn>(set, get), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
...createSignInSlice(set, get), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ name: 'SignInStore' }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) as any; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+131
to
+138
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Avoid shipping devtools middleware in production bundles
- this._store = createStore<CombinedSignInStore>()(
- devtools(
- (set, get) => ({
- ...createResourceSlice<SignIn>(set, get),
- ...createSignInSlice(set, get),
- }),
- { name: 'SignInStore' },
- ),
- ) as any;
+ const initializer = (set: any, get: any) => ({
+ ...createResourceSlice<SignIn>(set, get),
+ ...createSignInSlice(set, get),
+ });
+
+ this._store = createStore<CombinedSignInStore>()(
+ process.env.NODE_ENV === 'production' ? initializer : devtools(initializer, { name: 'SignInStore' }),
+ ) as any; 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.fromJSON(data); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
create = (params: SignInCreateParams): Promise<this> => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return this._basePost({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
path: this.pathRoot, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
body: params, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Reactive status property backed by the store. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Reading and writing goes directly to/from the store. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
get status(): SignInStatus | null { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return (this._store.getState() as unknown as CombinedSignInStore).signin.status; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
set status(newStatus: SignInStatus | null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(this._store.getState() as unknown as CombinedSignInStore).signin.setStatus(newStatus); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Reactive signInError property backed by the store. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Reading and writing goes directly to/from the store. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
get signInError(): { global: string | null; fields: Record<string, string> } { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return (this._store.getState() as unknown as CombinedSignInStore).signin.error; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
set signInError(newError: { global: string | null; fields: Record<string, string> }) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(this._store.getState() as unknown as CombinedSignInStore).signin.setError(newError); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private updateError(globalError: string | null, fieldErrors: Record<string, string> = {}) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.signInError = { global: globalError, fields: fieldErrors }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
create = async (params: SignInCreateParams): Promise<SignInResource> => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const result = await this._basePost({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
path: this.pathRoot, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
body: params, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return result; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.updateError(error instanceof Error ? error.message : 'An unexpected error occurred'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
throw error; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
resetPassword = (params: ResetPasswordParams): Promise<SignInResource> => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -160,22 +246,31 @@ export class SignIn extends BaseResource implements SignInResource { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
attemptFirstFactor = (attemptFactor: AttemptFirstFactorParams): Promise<SignInResource> => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let config; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
switch (attemptFactor.strategy) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
case 'passkey': | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
config = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
publicKeyCredential: JSON.stringify(serializePublicKeyCredentialAssertion(attemptFactor.publicKeyCredential)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
default: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
config = { ...attemptFactor }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
attemptFirstFactor = async (attemptFactor: AttemptFirstFactorParams): Promise<SignInResource> => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let config; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
switch (attemptFactor.strategy) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
case 'passkey': | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
config = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
publicKeyCredential: JSON.stringify( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
serializePublicKeyCredentialAssertion(attemptFactor.publicKeyCredential), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
default: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
config = { ...attemptFactor }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return this._basePost({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
body: { ...config, strategy: attemptFactor.strategy }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
action: 'attempt_first_factor', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const result = await this._basePost({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
body: { ...config, strategy: attemptFactor.strategy }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
action: 'attempt_first_factor', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return result; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.updateError(error instanceof Error ? error.message : 'An unexpected error occurred'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
throw error; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
createEmailLinkFlow = (): CreateEmailLinkFlowReturn<SignInStartEmailLinkFlowParams, SignInResource> => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -311,7 +406,7 @@ export class SignIn extends BaseResource implements SignInResource { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// error code 4001 means the user rejected the request | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Reference: https://docs.cdp.coinbase.com/wallet-sdk/docs/errors | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (provider === 'coinbase_wallet' && err.code === 4001) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (provider === 'coinbase_wallet' && err instanceof Error && 'code' in err && err.code === 4001) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
signature = await generateSignature({ identifier, nonce: message, provider }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
throw err; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -386,19 +481,13 @@ export class SignIn extends BaseResource implements SignInResource { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (flow === 'autofill' || flow === 'discoverable') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// @ts-ignore As this is experimental we want to support it at runtime, but not at the type level | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
await this.create({ strategy: 'passkey' }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// @ts-ignore As this is experimental we want to support it at runtime, but not at the type level | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const passKeyFactor = this.supportedFirstFactors.find( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// @ts-ignore As this is experimental we want to support it at runtime, but not at the type level | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
f => f.strategy === 'passkey', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) as PasskeyFactor; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const passKeyFactor = this.supportedFirstFactors?.find(f => f.strategy === 'passkey'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (!passKeyFactor) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
clerkVerifyPasskeyCalledBeforeCreate(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// @ts-ignore As this is experimental we want to support it at runtime, but not at the type level | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
await this.prepareFirstFactor(passKeyFactor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -445,18 +534,19 @@ export class SignIn extends BaseResource implements SignInResource { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
protected fromJSON(data: SignInJSON | SignInJSONSnapshot | null): this { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (data) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.id = data.id; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.status = data.status; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.supportedIdentifiers = data.supported_identifiers; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.identifier = data.identifier; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.supportedFirstFactors = deepSnakeToCamel(data.supported_first_factors) as SignInFirstFactor[] | null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.supportedSecondFactors = deepSnakeToCamel(data.supported_second_factors) as SignInSecondFactor[] | null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.firstFactorVerification = new Verification(data.first_factor_verification); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.secondFactorVerification = new Verification(data.second_factor_verification); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.createdSessionId = data.created_session_id; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.userData = new UserData(data.user_data); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (!data) return this; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.createdSessionId = data.created_session_id; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.firstFactorVerification = new Verification(data.first_factor_verification); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.id = data.id; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.identifier = data.identifier; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.secondFactorVerification = new Verification(data.second_factor_verification); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.status = data.status; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.supportedFirstFactors = deepSnakeToCamel(data.supported_first_factors) as SignInFirstFactor[] | null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.supportedIdentifiers = data.supported_identifiers; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.supportedSecondFactors = deepSnakeToCamel(data.supported_second_factors) as SignInSecondFactor[] | null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.userData = new UserData(data.user_data); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
536
to
+549
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
- this.supportedFirstFactors = deepSnakeToCamel(data.supported_first_factors) as SignInFirstFactor[] | null;
+ this.supportedFirstFactors = data.supported_first_factors
+ ? (deepSnakeToCamel(data.supported_first_factors) as SignInFirstFactor[])
+ : null; Apply the same pattern to 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return this; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import type { ClerkAPIErrorJSON } from '@clerk/types'; | ||
import { devtools } from 'zustand/middleware'; | ||
import { createStore } from 'zustand/vanilla'; | ||
|
||
/** | ||
* Represents the possible states of a resource. | ||
*/ | ||
export type ResourceState<T> = | ||
| { status: 'idle'; data: null; error: null } | ||
| { status: 'loading'; data: null; error: null } | ||
| { status: 'error'; data: null; error: ClerkAPIErrorJSON | null } | ||
| { status: 'success'; data: T; error: null }; | ||
|
||
/** | ||
* Resource actions for the Zustand store | ||
*/ | ||
export type ResourceAction<T> = | ||
| { type: 'FETCH_START' } | ||
| { type: 'FETCH_SUCCESS'; data: T } | ||
| { type: 'FETCH_ERROR'; error: ClerkAPIErrorJSON } | ||
| { type: 'RESET' }; | ||
|
||
/** | ||
* Resource store shape using Zustand slices pattern | ||
*/ | ||
export type ResourceStore<T> = { | ||
resource: { | ||
status: 'idle' | 'loading' | 'error' | 'success'; | ||
data: T | null; | ||
error: ClerkAPIErrorJSON | null; | ||
dispatch: (action: ResourceAction<T>) => void; | ||
getData: () => T | null; | ||
getError: () => ClerkAPIErrorJSON | null; | ||
hasError: () => boolean; | ||
getStatus: () => 'idle' | 'loading' | 'error' | 'success'; | ||
}; | ||
}; | ||
|
||
/** | ||
* Creates a resource slice following the Zustand slices pattern. | ||
* This slice handles generic resource state management (loading, success, error states). | ||
* All resource state is namespaced under the 'resource' key and flattened (no nested 'state' object). | ||
*/ | ||
const createResourceSlice = <T>(set: any, get: any): ResourceStore<T> => { | ||
const dispatch = (action: ResourceAction<T>) => { | ||
set((state: any) => { | ||
let newResourceState; | ||
|
||
switch (action.type) { | ||
case 'FETCH_START': | ||
newResourceState = { | ||
status: 'loading' as const, | ||
data: state.resource.data, // Keep existing data during loading | ||
error: null, | ||
}; | ||
break; | ||
case 'FETCH_SUCCESS': | ||
newResourceState = { | ||
status: 'success' as const, | ||
data: action.data, | ||
error: null, | ||
}; | ||
break; | ||
case 'FETCH_ERROR': | ||
newResourceState = { | ||
status: 'error' as const, | ||
data: state.resource.data, // Keep existing data on error | ||
error: action.error, | ||
}; | ||
break; | ||
case 'RESET': | ||
newResourceState = { | ||
status: 'idle' as const, | ||
data: null, | ||
error: null, | ||
}; | ||
break; | ||
default: | ||
return state; | ||
} | ||
|
||
return { | ||
...state, | ||
resource: { | ||
...state.resource, | ||
...newResourceState, | ||
}, | ||
}; | ||
}); | ||
}; | ||
|
||
return { | ||
resource: { | ||
status: 'idle' as const, | ||
data: null, | ||
error: null, | ||
dispatch, | ||
getData: () => { | ||
const state = get(); | ||
return state.resource.data; | ||
}, | ||
getError: () => { | ||
const state = get(); | ||
return state.resource.error; | ||
}, | ||
hasError: () => { | ||
const state = get(); | ||
return state.resource.status === 'error'; | ||
}, | ||
getStatus: () => { | ||
const state = get(); | ||
return state.resource.status; | ||
}, | ||
}, | ||
}; | ||
}; | ||
|
||
/** | ||
* Creates a basic resource store using just the resource slice. | ||
* This is used by BaseResource for backward compatibility. | ||
*/ | ||
export const createResourceStore = <T>(name = 'ResourceStore') => { | ||
return createStore<ResourceStore<T>>()( | ||
devtools( | ||
(set, get) => ({ | ||
...createResourceSlice<T>(set, get), | ||
}), | ||
{ name }, | ||
), | ||
); | ||
}; | ||
|
||
export { createResourceSlice }; |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -30,7 +30,6 @@ import { | |||||||||
import { useCoreSignIn, useEnvironment, useSignInContext } from '../../contexts'; | ||||||||||
import { Col, descriptors, Flow, localizationKeys } from '../../customizables'; | ||||||||||
import { CaptchaElement } from '../../elements/CaptchaElement'; | ||||||||||
import { useLoadingStatus } from '../../hooks'; | ||||||||||
import { useSupportEmail } from '../../hooks/useSupportEmail'; | ||||||||||
import { useRouter } from '../../router'; | ||||||||||
import type { FormControlState } from '../../utils'; | ||||||||||
|
@@ -77,7 +76,6 @@ const useAutoFillPasskey = () => { | |||||||||
function SignInStartInternal(): JSX.Element { | ||||||||||
const card = useCardState(); | ||||||||||
const clerk = useClerk(); | ||||||||||
const status = useLoadingStatus(); | ||||||||||
const { displayConfig, userSettings, authConfig } = useEnvironment(); | ||||||||||
const signIn = useCoreSignIn(); | ||||||||||
const { navigate } = useRouter(); | ||||||||||
|
@@ -196,8 +194,24 @@ function SignInStartInternal(): JSX.Element { | |||||||||
} | ||||||||||
}, [identifierField.value, identifierAttributes]); | ||||||||||
|
||||||||||
const signInStatus = signIn.status; | ||||||||||
const signInFetchStatus = signIn.store.getState().status; | ||||||||||
|
||||||||||
useEffect(() => { | ||||||||||
console.log('Component mounted'); | ||||||||||
console.log('Initial organizationTicket:', organizationTicket); | ||||||||||
console.log('Initial signInFetchStatus:', signInFetchStatus); | ||||||||||
console.log('Initial signInStatus:', signInStatus); | ||||||||||
}, []); | ||||||||||
|
||||||||||
useEffect(() => { | ||||||||||
if (!organizationTicket) { | ||||||||||
console.log('useEffect triggered'); | ||||||||||
console.log('organizationTicket:', organizationTicket); | ||||||||||
console.log('signInFetchStatus:', signInFetchStatus); | ||||||||||
console.log('signInStatus:', signInStatus); | ||||||||||
|
||||||||||
if (!organizationTicket || signInFetchStatus === 'fetching' || signInStatus === 'complete') { | ||||||||||
console.log('Early return from useEffect'); | ||||||||||
return; | ||||||||||
} | ||||||||||
|
||||||||||
|
@@ -206,65 +220,70 @@ function SignInStartInternal(): JSX.Element { | |||||||||
if (organizationTicket) { | ||||||||||
paramsToForward.set('__clerk_ticket', organizationTicket); | ||||||||||
} | ||||||||||
// We explicitly navigate to 'create' in the combined flow to trigger a client-side navigation. Navigating to | ||||||||||
// signUpUrl triggers a full page reload when used with the hash router. | ||||||||||
console.log('Navigating to signUpUrl with params:', paramsToForward.toString()); | ||||||||||
void navigate(isCombinedFlow ? `create` : signUpUrl, { searchParams: paramsToForward }); | ||||||||||
return; | ||||||||||
} | ||||||||||
|
||||||||||
status.setLoading(); | ||||||||||
console.log('Setting card to loading state'); | ||||||||||
card.setLoading(); | ||||||||||
signIn | ||||||||||
.create({ | ||||||||||
strategy: 'ticket', | ||||||||||
ticket: organizationTicket, | ||||||||||
}) | ||||||||||
.then(res => { | ||||||||||
console.log('API response:', res); | ||||||||||
switch (res.status) { | ||||||||||
case 'needs_first_factor': | ||||||||||
console.log('Status: needs_first_factor'); | ||||||||||
if (hasOnlyEnterpriseSSOFirstFactors(res)) { | ||||||||||
console.log('Authenticating with Enterprise SSO'); | ||||||||||
return authenticateWithEnterpriseSSO(); | ||||||||||
} | ||||||||||
|
||||||||||
return navigate('factor-one'); | ||||||||||
case 'needs_second_factor': | ||||||||||
console.log('Status: needs_second_factor'); | ||||||||||
return navigate('factor-two'); | ||||||||||
case 'complete': | ||||||||||
console.log('Status: complete'); | ||||||||||
removeClerkQueryParam('__clerk_ticket'); | ||||||||||
return clerk.setActive({ | ||||||||||
session: res.createdSessionId, | ||||||||||
redirectUrl: afterSignInUrl, | ||||||||||
}); | ||||||||||
default: { | ||||||||||
console.error('Invalid API response status:', res.status); | ||||||||||
console.error(clerkInvalidFAPIResponse(res.status, supportEmail)); | ||||||||||
return; | ||||||||||
} | ||||||||||
} | ||||||||||
}) | ||||||||||
.catch(err => { | ||||||||||
console.error('Error during signIn.create:', err); | ||||||||||
return attemptToRecoverFromSignInError(err); | ||||||||||
}) | ||||||||||
.finally(() => { | ||||||||||
// Keep the card in loading state during SSO redirect to prevent UI flicker | ||||||||||
// This is necessary because there's a brief delay between initiating the SSO flow | ||||||||||
// and the actual redirect to the external Identity Provider | ||||||||||
const isRedirectingToSSOProvider = hasOnlyEnterpriseSSOFirstFactors(signIn); | ||||||||||
if (isRedirectingToSSOProvider) return; | ||||||||||
|
||||||||||
status.setIdle(); | ||||||||||
console.log('Setting card to idle state'); | ||||||||||
card.setIdle(); | ||||||||||
}); | ||||||||||
}, []); | ||||||||||
}, [organizationTicket, signInFetchStatus, signInStatus]); | ||||||||||
|
||||||||||
useEffect(() => { | ||||||||||
console.log('OAuth error handling useEffect triggered'); | ||||||||||
async function handleOauthError() { | ||||||||||
const defaultErrorHandler = () => { | ||||||||||
// Error from server may be too much information for the end user, so set a generic error | ||||||||||
console.error('Default error handler triggered'); | ||||||||||
card.setError('Unable to complete action at this time. If the problem persists please contact support.'); | ||||||||||
}; | ||||||||||
|
||||||||||
const error = signIn?.firstFactorVerification?.error; | ||||||||||
if (error) { | ||||||||||
console.log('OAuth error detected:', error); | ||||||||||
switch (error.code) { | ||||||||||
case ERROR_CODES.NOT_ALLOWED_TO_SIGN_UP: | ||||||||||
case ERROR_CODES.OAUTH_ACCESS_DENIED: | ||||||||||
|
@@ -292,6 +311,7 @@ function SignInStartInternal(): JSX.Element { | |||||||||
|
||||||||||
// TODO: This is a workaround in order to reset the sign in attempt | ||||||||||
// so that the oauth error does not persist on full page reloads. | ||||||||||
console.log('Resetting sign-in attempt'); | ||||||||||
void (await signIn.create({})); | ||||||||||
} | ||||||||||
} | ||||||||||
|
@@ -368,6 +388,8 @@ function SignInStartInternal(): JSX.Element { | |||||||||
|
||||||||||
switch (res.status) { | ||||||||||
case 'needs_identifier': | ||||||||||
console.log('needs_identifier'); | ||||||||||
console.log('res.supportedFirstFactors:', res.supportedFirstFactors); | ||||||||||
Comment on lines
+391
to
+392
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove debugging logs from signInWithFields function. case 'needs_identifier':
- console.log('needs_identifier');
- console.log('res.supportedFirstFactors:', res.supportedFirstFactors);
// Check if we need to initiate an enterprise sso flow 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||
// Check if we need to initiate an enterprise sso flow | ||||||||||
if (res.supportedFirstFactors?.some(ff => ff.strategy === 'saml' || ff.strategy === 'enterprise_sso')) { | ||||||||||
await authenticateWithEnterpriseSSO(); | ||||||||||
|
@@ -495,12 +517,16 @@ function SignInStartInternal(): JSX.Element { | |||||||||
return components[identifierField.type as keyof typeof components]; | ||||||||||
}, [identifierField.type]); | ||||||||||
|
||||||||||
if (status.isLoading || clerkStatus === 'sign_up') { | ||||||||||
if (clerkStatus === 'sign_up') { | ||||||||||
// clerkStatus being sign_up will trigger a navigation to the sign up flow, so show a loading card instead of | ||||||||||
// rendering the sign in flow. | ||||||||||
return <LoadingCard />; | ||||||||||
} | ||||||||||
|
||||||||||
if (signInStatus === 'complete') { | ||||||||||
return <div>Sign-in complete!</div>; | ||||||||||
} | ||||||||||
|
||||||||||
// @ts-expect-error `action` is not typed | ||||||||||
const { action, ...identifierFieldProps } = identifierField.props; | ||||||||||
return ( | ||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,62 @@ | ||||||||||||||||||||||||||||||||||||||||||
import type { StoreApi } from 'zustand'; | ||||||||||||||||||||||||||||||||||||||||||
import { useStore } from 'zustand'; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
interface ResourceStore<T> { | ||||||||||||||||||||||||||||||||||||||||||
state: any; | ||||||||||||||||||||||||||||||||||||||||||
dispatch: (action: any) => void; | ||||||||||||||||||||||||||||||||||||||||||
getData: () => T | null; | ||||||||||||||||||||||||||||||||||||||||||
getError: () => any; | ||||||||||||||||||||||||||||||||||||||||||
status: () => 'idle' | 'loading' | 'error' | 'success'; | ||||||||||||||||||||||||||||||||||||||||||
hasError: () => boolean; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+4
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interface doesn't match the actual store structure Based on the relevant code snippets, the actual Update the interface to match the actual structure: interface ResourceStore<T> {
- state: any;
- dispatch: (action: any) => void;
- getData: () => T | null;
- getError: () => any;
- status: () => 'idle' | 'loading' | 'error' | 'success';
- hasError: () => boolean;
+ resource: {
+ status: 'idle' | 'loading' | 'error' | 'success';
+ data: T | null;
+ error: any;
+ dispatch: (action: any) => void;
+ getData: () => T | null;
+ getError: () => any;
+ hasError: () => boolean;
+ getStatus: () => 'idle' | 'loading' | 'error' | 'success';
+ };
} Additionally, update the hooks to use the correct paths: - const useResourceState = () => useStore(store, state => state.state);
+ const useResourceState = () => useStore(store, state => state.resource); 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* React hooks for using the resource store in React components. | ||||||||||||||||||||||||||||||||||||||||||
* This provides React-specific integration for the framework-agnostic resource store. | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
export const createResourceStoreHooks = <T>(store: StoreApi<ResourceStore<T>>) => { | ||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Hook to get the entire store state | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
const useResourceStore = () => useStore(store); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Hook to get the current resource state | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
const useResourceState = () => useStore(store, state => state.state); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Hook to get the dispatch function | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
const useResourceDispatch = () => useStore(store, state => state.dispatch); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Hook to get the current data | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
const useResourceData = () => useStore(store, state => state.state.data); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Hook to get the current error | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
const useResourceError = () => useStore(store, state => state.state.error); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Hook to get the current status | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
const useResourceStatus = () => useStore(store, state => state.state.status); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Hook to check if the resource has an error | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
const useResourceHasError = () => useStore(store, state => !!state.state.error); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
return { | ||||||||||||||||||||||||||||||||||||||||||
useResourceStore, | ||||||||||||||||||||||||||||||||||||||||||
useResourceState, | ||||||||||||||||||||||||||||||||||||||||||
useResourceDispatch, | ||||||||||||||||||||||||||||||||||||||||||
useResourceData, | ||||||||||||||||||||||||||||||||||||||||||
useResourceError, | ||||||||||||||||||||||||||||||||||||||||||
useResourceStatus, | ||||||||||||||||||||||||||||||||||||||||||
useResourceHasError, | ||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||
}; |
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,69 +1,190 @@ | ||||||||||||||||||
import { useClientContext } from '@clerk/shared/react'; | ||||||||||||||||||
import { ClerkInstanceContext, useClientContext } from '@clerk/shared/react'; | ||||||||||||||||||
import { eventMethodCalled } from '@clerk/shared/telemetry'; | ||||||||||||||||||
import type { UseSignInReturn } from '@clerk/types'; | ||||||||||||||||||
import type { SetActive, SignInResource } from '@clerk/types'; | ||||||||||||||||||
import { useCallback,useContext, useMemo } from 'react'; | ||||||||||||||||||
import { useStore } from 'zustand'; | ||||||||||||||||||
|
||||||||||||||||||
import { useIsomorphicClerkContext } from '../contexts/IsomorphicClerkContext'; | ||||||||||||||||||
import { useAssertWrappedByClerkProvider } from './useAssertWrappedByClerkProvider'; | ||||||||||||||||||
|
||||||||||||||||||
type QueuedCall = { | ||||||||||||||||||
target: 'signIn' | 'setActive'; | ||||||||||||||||||
method: string; | ||||||||||||||||||
args: any[]; | ||||||||||||||||||
resolve: (value: any) => void; | ||||||||||||||||||
reject: (error: any) => void; | ||||||||||||||||||
}; | ||||||||||||||||||
|
||||||||||||||||||
/** | ||||||||||||||||||
* The `useSignIn()` hook provides access to the [`SignIn`](https://clerk.com/docs/references/javascript/sign-in) object, which allows you to check the current state of a sign-in attempt and manage the sign-in flow. You can use this to create a [custom sign-in flow](https://clerk.com/docs/custom-flows/overview#sign-in-flow). | ||||||||||||||||||
* | ||||||||||||||||||
* @unionReturnHeadings | ||||||||||||||||||
* ["Initialization", "Loaded"] | ||||||||||||||||||
* | ||||||||||||||||||
* @example | ||||||||||||||||||
* ### Check the current state of a sign-in | ||||||||||||||||||
* | ||||||||||||||||||
* The following example uses the `useSignIn()` hook to access the [`SignIn`](https://clerk.com/docs/references/javascript/sign-in) object, which contains the current sign-in attempt status and methods to create a new sign-in attempt. The `isLoaded` property is used to handle the loading state. | ||||||||||||||||||
* | ||||||||||||||||||
* <Tabs items='React,Next.js'> | ||||||||||||||||||
* <Tab> | ||||||||||||||||||
* | ||||||||||||||||||
* ```tsx {{ filename: 'src/pages/SignInPage.tsx' }} | ||||||||||||||||||
* import { useSignIn } from '@clerk/clerk-react' | ||||||||||||||||||
* | ||||||||||||||||||
* export default function SignInPage() { | ||||||||||||||||||
* const { isLoaded, signIn } = useSignIn() | ||||||||||||||||||
* | ||||||||||||||||||
* if (!isLoaded) { | ||||||||||||||||||
* // Handle loading state | ||||||||||||||||||
* return null | ||||||||||||||||||
* } | ||||||||||||||||||
* | ||||||||||||||||||
* return <div>The current sign-in attempt status is {signIn?.status}.</div> | ||||||||||||||||||
* } | ||||||||||||||||||
* ``` | ||||||||||||||||||
* | ||||||||||||||||||
* </Tab> | ||||||||||||||||||
* <Tab> | ||||||||||||||||||
* | ||||||||||||||||||
* {@include ../../docs/use-sign-in.md#nextjs-01} | ||||||||||||||||||
* | ||||||||||||||||||
* </Tab> | ||||||||||||||||||
* </Tabs> | ||||||||||||||||||
* | ||||||||||||||||||
* @example | ||||||||||||||||||
* ### Create a custom sign-in flow with `useSignIn()` | ||||||||||||||||||
* | ||||||||||||||||||
* The `useSignIn()` hook can also be used to build fully custom sign-in flows, if Clerk's prebuilt components don't meet your specific needs or if you require more control over the authentication flow. Different sign-in flows include email and password, email and phone codes, email links, and multifactor (MFA). To learn more about using the `useSignIn()` hook to create custom flows, see the [custom flow guides](https://clerk.com/docs/custom-flows/overview). | ||||||||||||||||||
* | ||||||||||||||||||
* ```empty``` | ||||||||||||||||||
* Enhanced SignInResource with observable capabilities for React. | ||||||||||||||||||
*/ | ||||||||||||||||||
export const useSignIn = (): UseSignInReturn => { | ||||||||||||||||||
useAssertWrappedByClerkProvider('useSignIn'); | ||||||||||||||||||
type ObservableSignInResource = SignInResource & { | ||||||||||||||||||
/** | ||||||||||||||||||
* The observable store state. This is not a function but the actual state. | ||||||||||||||||||
* Components using this should access it directly from the useSignIn hook result. | ||||||||||||||||||
*/ | ||||||||||||||||||
observableState?: any; | ||||||||||||||||||
}; | ||||||||||||||||||
|
||||||||||||||||||
/** | ||||||||||||||||||
* Return type for the useSignIn hook | ||||||||||||||||||
*/ | ||||||||||||||||||
type UseSignInReturn = { | ||||||||||||||||||
isLoaded: boolean; | ||||||||||||||||||
signIn: ObservableSignInResource; | ||||||||||||||||||
setActive: SetActive; | ||||||||||||||||||
/** | ||||||||||||||||||
* The observable store state. Use this to access the SignIn store state. | ||||||||||||||||||
* This value will trigger re-renders when the store state changes. | ||||||||||||||||||
*/ | ||||||||||||||||||
signInStore: any; | ||||||||||||||||||
}; | ||||||||||||||||||
|
||||||||||||||||||
/** | ||||||||||||||||||
* A stable fallback store that maintains consistent behavior when no real store exists. | ||||||||||||||||||
*/ | ||||||||||||||||||
const FALLBACK_STATE = {}; | ||||||||||||||||||
const FALLBACK_STORE = { | ||||||||||||||||||
getState: () => FALLBACK_STATE, | ||||||||||||||||||
getInitialState: () => FALLBACK_STATE, | ||||||||||||||||||
subscribe: () => () => {}, // Return unsubscribe function | ||||||||||||||||||
setState: () => {}, | ||||||||||||||||||
Comment on lines
+48
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
- subscribe: () => () => {}, // Return unsubscribe function
+ subscribe: (_listener: unknown) => () => {}, // accept listener to satisfy Zustand 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||
destroy: () => {} | ||||||||||||||||||
}; | ||||||||||||||||||
|
||||||||||||||||||
export const useSignIn = (): UseSignInReturn => { | ||||||||||||||||||
// Check if ClerkProvider context is available first | ||||||||||||||||||
const clerkInstanceContext = useContext(ClerkInstanceContext); | ||||||||||||||||||
|
||||||||||||||||||
const isomorphicClerk = useIsomorphicClerkContext(); | ||||||||||||||||||
const client = useClientContext(); | ||||||||||||||||||
|
||||||||||||||||||
isomorphicClerk.telemetry?.record(eventMethodCalled('useSignIn')); | ||||||||||||||||||
|
||||||||||||||||||
if (!client) { | ||||||||||||||||||
return { isLoaded: false, signIn: undefined, setActive: undefined }; | ||||||||||||||||||
// Only assert ClerkProvider if we have some context - this allows proxy fallback | ||||||||||||||||||
if (clerkInstanceContext) { | ||||||||||||||||||
useAssertWrappedByClerkProvider('useSignIn'); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
return { | ||||||||||||||||||
isLoaded: true, | ||||||||||||||||||
signIn: client.signIn, | ||||||||||||||||||
setActive: isomorphicClerk.setActive, | ||||||||||||||||||
}; | ||||||||||||||||||
}; | ||||||||||||||||||
isomorphicClerk?.telemetry?.record(eventMethodCalled('useSignIn')); | ||||||||||||||||||
|
||||||||||||||||||
// Get the store reference - this must be done at the top level | ||||||||||||||||||
const store = useMemo(() => { | ||||||||||||||||||
if (!client?.signIn) return FALLBACK_STORE; | ||||||||||||||||||
|
||||||||||||||||||
// Try both 'store' and '_store' properties, but default to fallback | ||||||||||||||||||
return (client.signIn as any).store || (client.signIn as any)._store || FALLBACK_STORE; | ||||||||||||||||||
}, [client?.signIn]); | ||||||||||||||||||
|
||||||||||||||||||
// Always call useStore at the top level with a consistent store reference | ||||||||||||||||||
const storeState = useStore(store); | ||||||||||||||||||
|
||||||||||||||||||
// Determine if we have a real store | ||||||||||||||||||
const hasRealStore = store !== FALLBACK_STORE; | ||||||||||||||||||
|
||||||||||||||||||
// Compute the final store state | ||||||||||||||||||
const signInStore = useMemo(() => { | ||||||||||||||||||
if (!hasRealStore) { | ||||||||||||||||||
return {}; | ||||||||||||||||||
} | ||||||||||||||||||
return storeState; | ||||||||||||||||||
}, [hasRealStore, storeState]); | ||||||||||||||||||
|
||||||||||||||||||
const callQueue: QueuedCall[] = []; | ||||||||||||||||||
|
||||||||||||||||||
Comment on lines
+91
to
+92
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Queue is recreated on every render → pending calls may be lost
Fix: hoist the queue into a stable ref (or module-level) and process it from a -import { useClientContext } from '@clerk/shared/react';
+import { useClientContext } from '@clerk/shared/react';
+import { useRef, useEffect } from 'react';
...
- const callQueue: QueuedCall[] = [];
+ const callQueueRef = useRef<QueuedCall[]>([]);
...
- callQueue.push({
+ callQueueRef.current.push({
target,
...
- while (callQueue.length > 0) {
- const queuedCall = callQueue.shift();
+ while (callQueueRef.current.length > 0) {
+ const queuedCall = callQueueRef.current.shift();
...
- if (client) {
- processQueue(client.signIn, isomorphicClerk.setActive);
+ useEffect(() => {
+ if (client) {
+ processQueue(client.signIn, isomorphicClerk.setActive);
+ }
+ }, [client]); Without this change, any call issued before Also applies to: 51-65 🤖 Prompt for AI Agents
|
||||||||||||||||||
const processQueue = useCallback((signIn: SignInResource, setActive: SetActive) => { | ||||||||||||||||||
while (callQueue.length > 0) { | ||||||||||||||||||
const queuedCall = callQueue.shift(); | ||||||||||||||||||
if (!queuedCall) continue; | ||||||||||||||||||
|
||||||||||||||||||
const { target, method, args, resolve, reject } = queuedCall; | ||||||||||||||||||
try { | ||||||||||||||||||
const targetObj = target === 'setActive' ? setActive : signIn; | ||||||||||||||||||
const result = (targetObj as any)[method](...args); | ||||||||||||||||||
resolve(result); | ||||||||||||||||||
} catch (error) { | ||||||||||||||||||
reject(error); | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
}, []); | ||||||||||||||||||
|
||||||||||||||||||
const createProxy = useCallback(<T>(target: 'signIn' | 'setActive'): T => { | ||||||||||||||||||
const proxyTarget: any = {}; | ||||||||||||||||||
|
||||||||||||||||||
return new Proxy( | ||||||||||||||||||
proxyTarget, | ||||||||||||||||||
{ | ||||||||||||||||||
get(_, prop) { | ||||||||||||||||||
// Prevent React from treating this proxy as a Promise by returning undefined for 'then' | ||||||||||||||||||
if (prop === 'then') { | ||||||||||||||||||
return undefined; | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
// Handle Symbol properties and other non-method properties | ||||||||||||||||||
if (typeof prop === 'symbol' || typeof prop !== 'string') { | ||||||||||||||||||
return undefined; | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
// For observableState property, return undefined in proxy mode | ||||||||||||||||||
if (prop === 'observableState' && target === 'signIn') { | ||||||||||||||||||
return undefined; | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
return (...args: any[]) => { | ||||||||||||||||||
return new Promise((resolve, reject) => { | ||||||||||||||||||
callQueue.push({ | ||||||||||||||||||
target, | ||||||||||||||||||
method: String(prop), | ||||||||||||||||||
args, | ||||||||||||||||||
resolve, | ||||||||||||||||||
reject, | ||||||||||||||||||
}); | ||||||||||||||||||
}); | ||||||||||||||||||
}; | ||||||||||||||||||
}, | ||||||||||||||||||
has(_, prop) { | ||||||||||||||||||
// Return false for 'then' to prevent Promise-like behavior | ||||||||||||||||||
if (prop === 'then') { | ||||||||||||||||||
return false; | ||||||||||||||||||
} | ||||||||||||||||||
// Return true for all other properties to indicate they exist on the proxy | ||||||||||||||||||
return true; | ||||||||||||||||||
}, | ||||||||||||||||||
ownKeys(_) { | ||||||||||||||||||
return Object.getOwnPropertyNames(proxyTarget); | ||||||||||||||||||
}, | ||||||||||||||||||
getOwnPropertyDescriptor(_, prop) { | ||||||||||||||||||
return Object.getOwnPropertyDescriptor(proxyTarget, prop); | ||||||||||||||||||
} | ||||||||||||||||||
}, | ||||||||||||||||||
) as T; | ||||||||||||||||||
}, []); | ||||||||||||||||||
Comment on lines
+109
to
+159
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
const { setActive } = useSignIn();
await setActive('someSessionId'); A proxy over a non-callable object cannot be invoked and will raise Fix: make the proxy target callable and implement an const createProxy = useCallback(<T>(target: 'signIn' | 'setActive'): T => {
- const proxyTarget: any = {};
+ // Function targets stay callable; object targets keep method semantics
+ const proxyTarget: any =
+ target === 'setActive' ? (..._args: unknown[]) => undefined : {};
return new Proxy(
proxyTarget,
{
+ /* Proxy invoked like a function → enqueue */
+ apply(_unused, _thisArg, args) {
+ return new Promise((resolve, reject) => {
+ callQueue.push({
+ target,
+ // Special marker for direct invocation
+ method: 'apply',
+ args,
+ resolve,
+ reject,
+ });
+ });
+ }, This keeps 🤖 Prompt for AI Agents
|
||||||||||||||||||
|
||||||||||||||||||
// Memoize the result to prevent unnecessary re-renders | ||||||||||||||||||
return useMemo(() => { | ||||||||||||||||||
if (client) { | ||||||||||||||||||
processQueue(client.signIn, isomorphicClerk.setActive); | ||||||||||||||||||
|
||||||||||||||||||
// Create an enhanced signIn object that includes the observable state | ||||||||||||||||||
const enhancedSignIn: ObservableSignInResource = Object.create(client.signIn); | ||||||||||||||||||
Object.defineProperty(enhancedSignIn, 'observableState', { | ||||||||||||||||||
value: signInStore, | ||||||||||||||||||
writable: false, | ||||||||||||||||||
enumerable: true, | ||||||||||||||||||
configurable: true | ||||||||||||||||||
}); | ||||||||||||||||||
|
||||||||||||||||||
return { | ||||||||||||||||||
isLoaded: true, | ||||||||||||||||||
signIn: enhancedSignIn, | ||||||||||||||||||
setActive: isomorphicClerk.setActive, | ||||||||||||||||||
signInStore, | ||||||||||||||||||
}; | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
return { | ||||||||||||||||||
isLoaded: true, | ||||||||||||||||||
signIn: createProxy<ObservableSignInResource>('signIn'), | ||||||||||||||||||
setActive: createProxy<SetActive>('setActive'), | ||||||||||||||||||
signInStore: {}, | ||||||||||||||||||
}; | ||||||||||||||||||
}, [client, isomorphicClerk?.setActive, signInStore, processQueue, createProxy]); | ||||||||||||||||||
}; |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,12 +1,31 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { eventMethodCalled } from '@clerk/shared/telemetry'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import type { UseSignInReturn } from '@clerk/types'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import type { SetActive,SignInResource, UseSignInReturn } from '@clerk/types'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { computed, watch } from 'vue'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import type { ToComputedRefs } from '../utils'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { toComputedRefs } from '../utils'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { useClerkContext } from './useClerkContext'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type UseSignIn = () => ToComputedRefs<UseSignInReturn>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type UseSignIn = () => ToComputedRefs<UseSignInReturn> | ToComputedRefs<DeferredUseSignInReturn>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* A deferred proxy type that represents a resource that is not yet available | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* but will be hydrated once Clerk is loaded. This prevents unsafe type casting | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* and provides proper static typing for methods that return Promises. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type Deferred<T> = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[K in keyof T]: T[K] extends (...args: infer Args) => Promise<infer Return> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
? (...args: Args) => Promise<Return> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
: T[K] extends (...args: infer Args) => infer Return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
? (...args: Args) => Promise<Return> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
: T[K]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type DeferredUseSignInReturn = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
isLoaded: true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
signIn: Deferred<SignInResource>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setActive: Deferred<SetActive>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Returns the current [`SignIn`](https://clerk.com/docs/references/javascript/sign-in) object which provides | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -20,11 +39,7 @@ type UseSignIn = () => ToComputedRefs<UseSignInReturn>; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* </script> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* <template> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* <div v-if="!isLoaded"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* <!-- Handle loading state --> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* <div v-else> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* <div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* The current sign in attempt status is {{ signIn.status }}. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* </template> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -39,16 +54,74 @@ export const useSignIn: UseSignIn = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const result = computed<UseSignInReturn>(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const result = computed<UseSignInReturn | DeferredUseSignInReturn>(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (!clerk.value || !clientCtx.value) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return { isLoaded: false, signIn: undefined, setActive: undefined }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Create proxy objects that queue calls until clerk loads | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const createProxy = <T>(target: 'signIn' | 'setActive'): Deferred<T> => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return new Proxy({}, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
get(_, prop) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Prevent Vue from treating this proxy as a Promise by returning undefined for 'then' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (prop === 'then') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Handle Symbol properties and other non-method properties | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (typeof prop === 'symbol' || typeof prop !== 'string') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return (...args: any[]) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return new Promise((resolve, reject) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Wait for next tick and try again | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setTimeout(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (clerk.value && clientCtx.value) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const targetObj = target === 'setActive' ? clerk.value.setActive : clientCtx.value.signIn; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Type-safe method call by checking if the property exists and is callable | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (targetObj && typeof targetObj === 'object' && prop in targetObj) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const method = (targetObj as any)[prop]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (typeof method === 'function') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const result = method.apply(targetObj, args); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
resolve(result); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
reject(new Error(`Property ${prop} is not a function on ${target}`)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
reject(new Error(`Method ${prop} not found on ${target}`)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
reject(error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
reject(new Error('Clerk not loaded')); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+57
to
+100
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Deferred proxy rejects after a single 0 ms retry – promises are lost if Clerk loads later
Contrast this with the React implementation which queues calls until the client is ready. Suggested minimal fix: queue the calls in memory and flush once - setTimeout(() => {
- if (clerk.value && clientCtx.value) {
- …
- } else {
- reject(new Error('Clerk not loaded'));
- }
- }, 0);
+ const attempt = () => {
+ if (clerk.value && clientCtx.value) {
+ …
+ } else {
+ // try again on next tick until either loaded or a timeout limit is hit
+ setTimeout(attempt, 10);
+ }
+ };
+ attempt(); Without this, early calls in SSR/slow-network scenarios break the “always-loaded” contract. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
has(_, prop) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Return false for 'then' to prevent Promise-like behavior | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (prop === 'then') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Return true for all other properties to indicate they exist on the proxy | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) as Deferred<T>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
isLoaded: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
signIn: createProxy<SignInResource>('signIn'), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setActive: createProxy<SetActive>('setActive'), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} satisfies DeferredUseSignInReturn; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
isLoaded: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
signIn: clientCtx.value.signIn, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setActive: clerk.value.setActive, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} satisfies UseSignInReturn; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return toComputedRefs(result); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Strong-type the slice instead of falling back to
any
Both
set
andget
can be fully typed viaStoreApi<CombinedSignInStore>
, giving us compile-time safety and IntelliSense for the callback bodies.This eliminates the need for the repetitive
state: any
casts further down.📝 Committable suggestion
🤖 Prompt for AI Agents