diff --git a/.changeset/dark-sides-beg.md b/.changeset/dark-sides-beg.md new file mode 100644 index 00000000000..857f265b835 --- /dev/null +++ b/.changeset/dark-sides-beg.md @@ -0,0 +1,5 @@ +--- +'@clerk/clerk-js': patch +--- + +Fix issue where `SignIn` and `SignUp` instances were unable to be serialized with `JSON.stringify` due to a circular reference. diff --git a/packages/clerk-js/src/core/resources/SignIn.ts b/packages/clerk-js/src/core/resources/SignIn.ts index 8888af69a0a..73f43de78e6 100644 --- a/packages/clerk-js/src/core/resources/SignIn.ts +++ b/packages/clerk-js/src/core/resources/SignIn.ts @@ -631,67 +631,71 @@ class SignInFuture implements SignInFutureResource { verifyBackupCode: this.verifyBackupCode.bind(this), }; - constructor(readonly resource: SignIn) {} + readonly #resource: SignIn; + + constructor(resource: SignIn) { + this.#resource = resource; + } get id() { - return this.resource.id; + return this.#resource.id; } get identifier() { - return this.resource.identifier; + return this.#resource.identifier; } get createdSessionId() { - return this.resource.createdSessionId; + return this.#resource.createdSessionId; } get userData() { - return this.resource.userData; + return this.#resource.userData; } get status() { // @TODO hooks-revamp: Consolidate this fallback val with stateProxy - return this.resource.status || 'needs_identifier'; + return this.#resource.status || 'needs_identifier'; } get supportedFirstFactors() { - return this.resource.supportedFirstFactors ?? []; + return this.#resource.supportedFirstFactors ?? []; } get supportedSecondFactors() { - return this.resource.supportedSecondFactors ?? []; + return this.#resource.supportedSecondFactors ?? []; } get isTransferable() { - return this.resource.firstFactorVerification.status === 'transferable'; + return this.#resource.firstFactorVerification.status === 'transferable'; } get existingSession() { if ( - this.resource.firstFactorVerification.status === 'failed' && - this.resource.firstFactorVerification.error?.code === 'identifier_already_signed_in' && - this.resource.firstFactorVerification.error?.meta?.sessionId + this.#resource.firstFactorVerification.status === 'failed' && + this.#resource.firstFactorVerification.error?.code === 'identifier_already_signed_in' && + this.#resource.firstFactorVerification.error?.meta?.sessionId ) { - return { sessionId: this.resource.firstFactorVerification.error?.meta?.sessionId }; + return { sessionId: this.#resource.firstFactorVerification.error?.meta?.sessionId }; } return undefined; } get firstFactorVerification() { - return this.resource.firstFactorVerification; + return this.#resource.firstFactorVerification; } get secondFactorVerification() { - return this.resource.secondFactorVerification; + return this.#resource.secondFactorVerification; } async sendResetPasswordEmailCode(): Promise<{ error: ClerkError | null }> { - if (!this.resource.id) { + if (!this.#resource.id) { throw new Error('Cannot reset password without a sign in.'); } - return runAsyncResourceTask(this.resource, async () => { - const resetPasswordEmailCodeFactor = this.resource.supportedFirstFactors?.find( + return runAsyncResourceTask(this.#resource, async () => { + const resetPasswordEmailCodeFactor = this.#resource.supportedFirstFactors?.find( f => f.strategy === 'reset_password_email_code', ); @@ -702,7 +706,7 @@ class SignInFuture implements SignInFutureResource { } const { emailAddressId } = resetPasswordEmailCodeFactor; - await this.resource.__internal_basePost({ + await this.#resource.__internal_basePost({ body: { emailAddressId, strategy: 'reset_password_email_code' }, action: 'prepare_first_factor', }); @@ -711,8 +715,8 @@ class SignInFuture implements SignInFutureResource { async verifyResetPasswordEmailCode(params: SignInFutureEmailCodeVerifyParams): Promise<{ error: ClerkError | null }> { const { code } = params; - return runAsyncResourceTask(this.resource, async () => { - await this.resource.__internal_basePost({ + return runAsyncResourceTask(this.#resource, async () => { + await this.#resource.__internal_basePost({ body: { code, strategy: 'reset_password_email_code' }, action: 'attempt_first_factor', }); @@ -721,8 +725,8 @@ class SignInFuture implements SignInFutureResource { async submitResetPassword(params: SignInFutureResetPasswordSubmitParams): Promise<{ error: ClerkError | null }> { const { password, signOutOfOtherSessions = true } = params; - return runAsyncResourceTask(this.resource, async () => { - await this.resource.__internal_basePost({ + return runAsyncResourceTask(this.#resource, async () => { + await this.#resource.__internal_basePost({ body: { password, signOutOfOtherSessions }, action: 'reset_password', }); @@ -731,14 +735,14 @@ class SignInFuture implements SignInFutureResource { private async _create(params: SignInFutureCreateParams): Promise { const locale = getBrowserLocale(); - await this.resource.__internal_basePost({ - path: this.resource.pathRoot, + await this.#resource.__internal_basePost({ + path: this.#resource.pathRoot, body: locale ? { locale, ...params } : params, }); } async create(params: SignInFutureCreateParams): Promise<{ error: ClerkError | null }> { - return runAsyncResourceTask(this.resource, async () => { + return runAsyncResourceTask(this.#resource, async () => { await this._create(params); }); } @@ -748,13 +752,13 @@ class SignInFuture implements SignInFutureResource { throw new Error('Only one of identifier, emailAddress, or phoneNumber can be provided'); } - return runAsyncResourceTask(this.resource, async () => { + return runAsyncResourceTask(this.#resource, async () => { // TODO @userland-errors: const identifier = params.identifier || params.emailAddress || params.phoneNumber; - const previousIdentifier = this.resource.identifier; + const previousIdentifier = this.#resource.identifier; const locale = getBrowserLocale(); - await this.resource.__internal_basePost({ - path: this.resource.pathRoot, + await this.#resource.__internal_basePost({ + path: this.#resource.pathRoot, body: { identifier: identifier || previousIdentifier, password: params.password, @@ -766,19 +770,19 @@ class SignInFuture implements SignInFutureResource { async sendEmailCode(params: SignInFutureEmailCodeSendParams = {}): Promise<{ error: ClerkError | null }> { const { emailAddress, emailAddressId } = params; - if (!this.resource.id && emailAddressId) { + if (!this.#resource.id && emailAddressId) { throw new Error( 'signIn.emailCode.sendCode() cannot be called with an emailAddressId if an existing signIn does not exist.', ); } - if (!this.resource.id && !emailAddress) { + if (!this.#resource.id && !emailAddress) { throw new Error( 'signIn.emailCode.sendCode() cannot be called without an emailAddress if an existing signIn does not exist.', ); } - return runAsyncResourceTask(this.resource, async () => { + return runAsyncResourceTask(this.#resource, async () => { if (emailAddress) { await this._create({ identifier: emailAddress }); } @@ -788,7 +792,7 @@ class SignInFuture implements SignInFutureResource { throw new ClerkRuntimeError('Email code factor not found', { code: 'factor_not_found' }); } - await this.resource.__internal_basePost({ + await this.#resource.__internal_basePost({ body: { emailAddressId: emailCodeFactor.emailAddressId, strategy: 'email_code' }, action: 'prepare_first_factor', }); @@ -797,8 +801,8 @@ class SignInFuture implements SignInFutureResource { async verifyEmailCode(params: SignInFutureEmailCodeVerifyParams): Promise<{ error: ClerkError | null }> { const { code } = params; - return runAsyncResourceTask(this.resource, async () => { - await this.resource.__internal_basePost({ + return runAsyncResourceTask(this.#resource, async () => { + await this.#resource.__internal_basePost({ body: { code, strategy: 'email_code' }, action: 'attempt_first_factor', }); @@ -807,19 +811,19 @@ class SignInFuture implements SignInFutureResource { async sendEmailLink(params: SignInFutureEmailLinkSendParams): Promise<{ error: ClerkError | null }> { const { emailAddress, verificationUrl, emailAddressId } = params; - if (!this.resource.id && emailAddressId) { + if (!this.#resource.id && emailAddressId) { throw new Error( 'signIn.emailLink.sendLink() cannot be called with an emailAddressId if an existing signIn does not exist.', ); } - if (!this.resource.id && !emailAddress) { + if (!this.#resource.id && !emailAddress) { throw new Error( 'signIn.emailLink.sendLink() cannot be called without an emailAddress if an existing signIn does not exist.', ); } - return runAsyncResourceTask(this.resource, async () => { + return runAsyncResourceTask(this.#resource, async () => { if (emailAddress) { await this._create({ identifier: emailAddress }); } @@ -836,7 +840,7 @@ class SignInFuture implements SignInFutureResource { absoluteVerificationUrl = window.location.origin + verificationUrl; } - await this.resource.__internal_basePost({ + await this.#resource.__internal_basePost({ body: { emailAddressId: emailLinkFactor.emailAddressId, redirectUrl: absoluteVerificationUrl, @@ -848,12 +852,12 @@ class SignInFuture implements SignInFutureResource { } async waitForEmailLinkVerification(): Promise<{ error: ClerkError | null }> { - return runAsyncResourceTask(this.resource, async () => { + return runAsyncResourceTask(this.#resource, async () => { const { run, stop } = Poller(); await new Promise((resolve, reject) => { void run(async () => { try { - const res = await this.resource.__internal_baseGet(); + const res = await this.#resource.__internal_baseGet(); const status = res.firstFactorVerification.status; if (status === 'verified' || status === 'expired') { stop(); @@ -870,19 +874,19 @@ class SignInFuture implements SignInFutureResource { async sendPhoneCode(params: SignInFuturePhoneCodeSendParams = {}): Promise<{ error: ClerkError | null }> { const { phoneNumber, phoneNumberId, channel = 'sms' } = params; - if (!this.resource.id && phoneNumberId) { + if (!this.#resource.id && phoneNumberId) { throw new Error( 'signIn.phoneCode.sendCode() cannot be called with an phoneNumberId if an existing signIn does not exist.', ); } - if (!this.resource.id && !phoneNumber) { + if (!this.#resource.id && !phoneNumber) { throw new Error( 'signIn.phoneCode.sendCode() cannot be called without an phoneNumber if an existing signIn does not exist.', ); } - return runAsyncResourceTask(this.resource, async () => { + return runAsyncResourceTask(this.#resource, async () => { if (phoneNumber) { await this._create({ identifier: phoneNumber }); } @@ -892,7 +896,7 @@ class SignInFuture implements SignInFutureResource { throw new ClerkRuntimeError('Phone code factor not found', { code: 'factor_not_found' }); } - await this.resource.__internal_basePost({ + await this.#resource.__internal_basePost({ body: { phoneNumberId: phoneCodeFactor.phoneNumberId, strategy: 'phone_code', channel }, action: 'prepare_first_factor', }); @@ -901,8 +905,8 @@ class SignInFuture implements SignInFutureResource { async verifyPhoneCode(params: SignInFuturePhoneCodeVerifyParams): Promise<{ error: ClerkError | null }> { const { code } = params; - return runAsyncResourceTask(this.resource, async () => { - await this.resource.__internal_basePost({ + return runAsyncResourceTask(this.#resource, async () => { + await this.#resource.__internal_basePost({ body: { code, strategy: 'phone_code' }, action: 'attempt_first_factor', }); @@ -912,7 +916,7 @@ class SignInFuture implements SignInFutureResource { async sso(params: SignInFutureSSOParams): Promise<{ error: ClerkError | null }> { const { strategy, redirectUrl, redirectCallbackUrl, popup, oidcPrompt, enterpriseConnectionId, identifier } = params; - return runAsyncResourceTask(this.resource, async () => { + return runAsyncResourceTask(this.#resource, async () => { let actionCompleteRedirectUrl = redirectUrl; try { new URL(redirectUrl); @@ -937,7 +941,7 @@ class SignInFuture implements SignInFutureResource { }); if (strategy === 'enterprise_sso') { - await this.resource.__internal_basePost({ + await this.#resource.__internal_basePost({ body: { ...routes, oidcPrompt, @@ -948,13 +952,13 @@ class SignInFuture implements SignInFutureResource { }); } - const { status, externalVerificationRedirectURL } = this.resource.firstFactorVerification; + const { status, externalVerificationRedirectURL } = this.#resource.firstFactorVerification; if (status === 'unverified' && externalVerificationRedirectURL) { if (popup) { await _futureAuthenticateWithPopup(SignIn.clerk, { popup, externalVerificationRedirectURL }); // Pick up the modified SignIn resource - await this.resource.reload(); + await this.#resource.reload(); } else { windowNavigate(externalVerificationRedirectURL); } @@ -966,7 +970,7 @@ class SignInFuture implements SignInFutureResource { const { strategy } = params; const provider = strategy.replace('web3_', '').replace('_signature', '') as Web3Provider; - return runAsyncResourceTask(this.resource, async () => { + return runAsyncResourceTask(this.#resource, async () => { let identifier; let generateSignature; switch (provider) { @@ -992,14 +996,14 @@ class SignInFuture implements SignInFutureResource { await this._create({ identifier }); - const web3FirstFactor = this.resource.supportedFirstFactors?.find( + const web3FirstFactor = this.#resource.supportedFirstFactors?.find( f => f.strategy === strategy, ) as Web3SignatureFactor; if (!web3FirstFactor) { throw new ClerkRuntimeError('Web3 first factor not found', { code: 'factor_not_found' }); } - await this.resource.__internal_basePost({ + await this.#resource.__internal_basePost({ body: { web3WalletId: web3FirstFactor.web3WalletId, strategy }, action: 'prepare_first_factor', }); @@ -1026,7 +1030,7 @@ class SignInFuture implements SignInFutureResource { } } - await this.resource.__internal_basePost({ + await this.#resource.__internal_basePost({ body: { signature, strategy }, action: 'attempt_first_factor', }); @@ -1052,7 +1056,7 @@ class SignInFuture implements SignInFutureResource { }); } - return runAsyncResourceTask(this.resource, async () => { + return runAsyncResourceTask(this.#resource, async () => { if (flow === 'autofill' || flow === 'discoverable') { await this._create({ strategy: 'passkey' }); } else { @@ -1061,7 +1065,7 @@ class SignInFuture implements SignInFutureResource { if (!passKeyFactor) { throw new ClerkRuntimeError('Passkey factor not found', { code: 'factor_not_found' }); } - await this.resource.__internal_basePost({ + await this.#resource.__internal_basePost({ body: { strategy: 'passkey' }, action: 'prepare_first_factor', }); @@ -1094,7 +1098,7 @@ class SignInFuture implements SignInFutureResource { throw new ClerkWebAuthnError(error.message, { code: 'passkey_retrieval_failed' }); } - await this.resource.__internal_basePost({ + await this.#resource.__internal_basePost({ body: { publicKeyCredential: JSON.stringify(serializePublicKeyCredentialAssertion(publicKeyCredential)), strategy: 'passkey', @@ -1105,15 +1109,15 @@ class SignInFuture implements SignInFutureResource { } async sendMFAPhoneCode(): Promise<{ error: ClerkError | null }> { - return runAsyncResourceTask(this.resource, async () => { - const phoneCodeFactor = this.resource.supportedSecondFactors?.find(f => f.strategy === 'phone_code'); + return runAsyncResourceTask(this.#resource, async () => { + const phoneCodeFactor = this.#resource.supportedSecondFactors?.find(f => f.strategy === 'phone_code'); if (!phoneCodeFactor) { throw new ClerkRuntimeError('Phone code factor not found', { code: 'factor_not_found' }); } const { phoneNumberId } = phoneCodeFactor; - await this.resource.__internal_basePost({ + await this.#resource.__internal_basePost({ body: { phoneNumberId, strategy: 'phone_code' }, action: 'prepare_second_factor', }); @@ -1122,8 +1126,8 @@ class SignInFuture implements SignInFutureResource { async verifyMFAPhoneCode(params: SignInFutureMFAPhoneCodeVerifyParams): Promise<{ error: ClerkError | null }> { const { code } = params; - return runAsyncResourceTask(this.resource, async () => { - await this.resource.__internal_basePost({ + return runAsyncResourceTask(this.#resource, async () => { + await this.#resource.__internal_basePost({ body: { code, strategy: 'phone_code' }, action: 'attempt_second_factor', }); @@ -1132,8 +1136,8 @@ class SignInFuture implements SignInFutureResource { async verifyTOTP(params: SignInFutureTOTPVerifyParams): Promise<{ error: ClerkError | null }> { const { code } = params; - return runAsyncResourceTask(this.resource, async () => { - await this.resource.__internal_basePost({ + return runAsyncResourceTask(this.#resource, async () => { + await this.#resource.__internal_basePost({ body: { code, strategy: 'totp' }, action: 'attempt_second_factor', }); @@ -1142,8 +1146,8 @@ class SignInFuture implements SignInFutureResource { async verifyBackupCode(params: SignInFutureBackupCodeVerifyParams): Promise<{ error: ClerkError | null }> { const { code } = params; - return runAsyncResourceTask(this.resource, async () => { - await this.resource.__internal_basePost({ + return runAsyncResourceTask(this.#resource, async () => { + await this.#resource.__internal_basePost({ body: { code, strategy: 'backup_code' }, action: 'attempt_second_factor', }); @@ -1158,15 +1162,15 @@ class SignInFuture implements SignInFutureResource { async finalize(params?: SignInFutureFinalizeParams): Promise<{ error: ClerkError | null }> { const { navigate } = params || {}; - if (!this.resource.createdSessionId) { + if (!this.#resource.createdSessionId) { throw new Error('Cannot finalize sign-in without a created session.'); } - return runAsyncResourceTask(this.resource, async () => { + return runAsyncResourceTask(this.#resource, async () => { // Reload the client to prevent an issue where the created session is not picked up. await SignIn.clerk.client?.reload(); - await SignIn.clerk.setActive({ session: this.resource.createdSessionId, navigate }); + await SignIn.clerk.setActive({ session: this.#resource.createdSessionId, navigate }); }); } @@ -1184,12 +1188,12 @@ class SignInFuture implements SignInFutureResource { emailAddressId, phoneNumberId, }: SelectFirstFactorParams): EmailCodeFactor | EmailLinkFactor | PhoneCodeFactor | null { - if (!this.resource.supportedFirstFactors) { + if (!this.#resource.supportedFirstFactors) { return null; } if (emailAddressId) { - const factor = this.resource.supportedFirstFactors.find( + const factor = this.#resource.supportedFirstFactors.find( f => f.strategy === strategy && f.emailAddressId === emailAddressId, ) as EmailCodeFactor | EmailLinkFactor; if (factor) { @@ -1198,7 +1202,7 @@ class SignInFuture implements SignInFutureResource { } if (phoneNumberId) { - const factor = this.resource.supportedFirstFactors.find( + const factor = this.#resource.supportedFirstFactors.find( f => f.strategy === strategy && f.phoneNumberId === phoneNumberId, ) as PhoneCodeFactor; if (factor) { @@ -1207,15 +1211,15 @@ class SignInFuture implements SignInFutureResource { } // Try to find a factor that matches the identifier. - const factorForIdentifier = this.resource.supportedFirstFactors.find( - f => f.strategy === strategy && f.safeIdentifier === this.resource.identifier, + const factorForIdentifier = this.#resource.supportedFirstFactors.find( + f => f.strategy === strategy && f.safeIdentifier === this.#resource.identifier, ) as EmailCodeFactor | EmailLinkFactor | PhoneCodeFactor; if (factorForIdentifier) { return factorForIdentifier; } // If no factor is found matching the identifier, try to find a factor that matches the strategy. - const factorForStrategy = this.resource.supportedFirstFactors.find(f => f.strategy === strategy) as + const factorForStrategy = this.#resource.supportedFirstFactors.find(f => f.strategy === strategy) as | EmailCodeFactor | EmailLinkFactor | PhoneCodeFactor; diff --git a/packages/clerk-js/src/core/resources/SignUp.ts b/packages/clerk-js/src/core/resources/SignUp.ts index bb520ccdcea..f234ded2ddd 100644 --- a/packages/clerk-js/src/core/resources/SignUp.ts +++ b/packages/clerk-js/src/core/resources/SignUp.ts @@ -580,101 +580,105 @@ class SignUpFuture implements SignUpFutureResource { verifyPhoneCode: this.verifyPhoneCode.bind(this), }; - constructor(readonly resource: SignUp) {} + readonly #resource: SignUp; + + constructor(resource: SignUp) { + this.#resource = resource; + } get id() { - return this.resource.id; + return this.#resource.id; } get requiredFields() { - return this.resource.requiredFields; + return this.#resource.requiredFields; } get optionalFields() { - return this.resource.optionalFields; + return this.#resource.optionalFields; } get missingFields() { - return this.resource.missingFields; + return this.#resource.missingFields; } get status() { // @TODO hooks-revamp: Consolidate this fallback val with stateProxy - return this.resource.status || 'missing_requirements'; + return this.#resource.status || 'missing_requirements'; } get username() { - return this.resource.username; + return this.#resource.username; } get firstName() { - return this.resource.firstName; + return this.#resource.firstName; } get lastName() { - return this.resource.lastName; + return this.#resource.lastName; } get emailAddress() { - return this.resource.emailAddress; + return this.#resource.emailAddress; } get phoneNumber() { - return this.resource.phoneNumber; + return this.#resource.phoneNumber; } get web3Wallet() { - return this.resource.web3wallet; + return this.#resource.web3wallet; } get hasPassword() { - return this.resource.hasPassword; + return this.#resource.hasPassword; } get unsafeMetadata() { - return this.resource.unsafeMetadata; + return this.#resource.unsafeMetadata; } get createdSessionId() { - return this.resource.createdSessionId; + return this.#resource.createdSessionId; } get createdUserId() { - return this.resource.createdUserId; + return this.#resource.createdUserId; } get abandonAt() { - return this.resource.abandonAt; + return this.#resource.abandonAt; } get legalAcceptedAt() { - return this.resource.legalAcceptedAt; + return this.#resource.legalAcceptedAt; } get locale() { - return this.resource.locale; + return this.#resource.locale; } get unverifiedFields() { - return this.resource.unverifiedFields; + return this.#resource.unverifiedFields; } get isTransferable() { // TODO: we can likely remove the error code check as the status should be sufficient return ( - this.resource.verifications.externalAccount.status === 'transferable' && - this.resource.verifications.externalAccount.error?.code === 'external_account_exists' + this.#resource.verifications.externalAccount.status === 'transferable' && + this.#resource.verifications.externalAccount.error?.code === 'external_account_exists' ); } get existingSession() { if ( - (this.resource.verifications.externalAccount.status === 'failed' || - this.resource.verifications.externalAccount.status === 'unverified') && - this.resource.verifications.externalAccount.error?.code === 'identifier_already_signed_in' && - this.resource.verifications.externalAccount.error?.meta?.sessionId + (this.#resource.verifications.externalAccount.status === 'failed' || + this.#resource.verifications.externalAccount.status === 'unverified') && + this.#resource.verifications.externalAccount.error?.code === 'identifier_already_signed_in' && + this.#resource.verifications.externalAccount.error?.meta?.sessionId ) { - return { sessionId: this.resource.verifications.externalAccount.error?.meta?.sessionId }; + return { sessionId: this.#resource.verifications.externalAccount.error?.meta?.sessionId }; } return undefined; @@ -708,28 +712,28 @@ class SignUpFuture implements SignUpFutureResource { locale: params.locale ?? getBrowserLocale(), }; - await this.resource.__internal_basePost({ path: this.resource.pathRoot, body }); + await this.#resource.__internal_basePost({ path: this.#resource.pathRoot, body }); } async create(params: SignUpFutureCreateParams): Promise<{ error: ClerkError | null }> { - return runAsyncResourceTask(this.resource, async () => { + return runAsyncResourceTask(this.#resource, async () => { await this._create(params); }); } async update(params: SignUpFutureUpdateParams): Promise<{ error: ClerkError | null }> { - return runAsyncResourceTask(this.resource, async () => { + return runAsyncResourceTask(this.#resource, async () => { const body: Record = { ...params, unsafeMetadata: params.unsafeMetadata ? normalizeUnsafeMetadata(params.unsafeMetadata) : undefined, }; - await this.resource.__internal_basePatch({ path: this.resource.pathRoot, body }); + await this.#resource.__internal_basePatch({ path: this.#resource.pathRoot, body }); }); } async password(params: SignUpFuturePasswordParams): Promise<{ error: ClerkError | null }> { - return runAsyncResourceTask(this.resource, async () => { + return runAsyncResourceTask(this.#resource, async () => { const { captchaToken, captchaWidgetType, captchaError } = await this.getCaptchaToken(); const body: Record = { @@ -741,13 +745,13 @@ class SignUpFuture implements SignUpFutureResource { unsafeMetadata: params.unsafeMetadata ? normalizeUnsafeMetadata(params.unsafeMetadata) : undefined, }; - await this.resource.__internal_basePost({ path: this.resource.pathRoot, body }); + await this.#resource.__internal_basePost({ path: this.#resource.pathRoot, body }); }); } async sendEmailCode(): Promise<{ error: ClerkError | null }> { - return runAsyncResourceTask(this.resource, async () => { - await this.resource.__internal_basePost({ + return runAsyncResourceTask(this.#resource, async () => { + await this.#resource.__internal_basePost({ body: { strategy: 'email_code' }, action: 'prepare_verification', }); @@ -756,8 +760,8 @@ class SignUpFuture implements SignUpFutureResource { async verifyEmailCode(params: SignUpFutureEmailCodeVerifyParams): Promise<{ error: ClerkError | null }> { const { code } = params; - return runAsyncResourceTask(this.resource, async () => { - await this.resource.__internal_basePost({ + return runAsyncResourceTask(this.#resource, async () => { + await this.#resource.__internal_basePost({ body: { strategy: 'email_code', code }, action: 'attempt_verification', }); @@ -766,16 +770,16 @@ class SignUpFuture implements SignUpFutureResource { async sendPhoneCode(params: SignUpFuturePhoneCodeSendParams): Promise<{ error: ClerkError | null }> { const { phoneNumber, channel = 'sms' } = params; - return runAsyncResourceTask(this.resource, async () => { - if (!this.resource.id) { + return runAsyncResourceTask(this.#resource, async () => { + if (!this.#resource.id) { const { captchaToken, captchaWidgetType, captchaError } = await this.getCaptchaToken(); - await this.resource.__internal_basePost({ - path: this.resource.pathRoot, + await this.#resource.__internal_basePost({ + path: this.#resource.pathRoot, body: { phoneNumber, captchaToken, captchaWidgetType, captchaError }, }); } - await this.resource.__internal_basePost({ + await this.#resource.__internal_basePost({ body: { strategy: 'phone_code', channel }, action: 'prepare_verification', }); @@ -784,8 +788,8 @@ class SignUpFuture implements SignUpFutureResource { async verifyPhoneCode(params: SignUpFuturePhoneCodeVerifyParams): Promise<{ error: ClerkError | null }> { const { code } = params; - return runAsyncResourceTask(this.resource, async () => { - await this.resource.__internal_basePost({ + return runAsyncResourceTask(this.#resource, async () => { + await this.#resource.__internal_basePost({ body: { strategy: 'phone_code', code }, action: 'attempt_verification', }); @@ -804,7 +808,7 @@ class SignUpFuture implements SignUpFutureResource { emailAddress, popup, } = params; - return runAsyncResourceTask(this.resource, async () => { + return runAsyncResourceTask(this.#resource, async () => { const { captchaToken, captchaWidgetType, captchaError } = await this.getCaptchaToken(); let redirectUrlComplete = redirectUrl; @@ -828,8 +832,8 @@ class SignUpFuture implements SignUpFutureResource { } const authenticateFn = () => { - return this.resource.__internal_basePost({ - path: this.resource.pathRoot, + return this.#resource.__internal_basePost({ + path: this.#resource.pathRoot, body: { strategy, ...routes, @@ -854,13 +858,13 @@ class SignUpFuture implements SignUpFutureResource { throw e; }); - const { status, externalVerificationRedirectURL } = this.resource.verifications.externalAccount; + const { status, externalVerificationRedirectURL } = this.#resource.verifications.externalAccount; if (status === 'unverified' && externalVerificationRedirectURL) { if (popup) { await _futureAuthenticateWithPopup(SignUp.clerk, { popup, externalVerificationRedirectURL }); // Pick up the modified SignUp resource - await this.resource.reload(); + await this.#resource.reload(); } else { windowNavigate(externalVerificationRedirectURL); } @@ -872,7 +876,7 @@ class SignUpFuture implements SignUpFutureResource { const { strategy, unsafeMetadata, legalAccepted } = params; const provider = strategy.replace('web3_', '').replace('_signature', '') as Web3Provider; - return runAsyncResourceTask(this.resource, async () => { + return runAsyncResourceTask(this.#resource, async () => { let identifier; let generateSignature; switch (provider) { @@ -897,14 +901,14 @@ class SignUpFuture implements SignUpFutureResource { } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const web3Wallet = identifier || this.resource.web3wallet!; + const web3Wallet = identifier || this.#resource.web3wallet!; await this._create({ web3Wallet, unsafeMetadata, legalAccepted }); - await this.resource.__internal_basePost({ + await this.#resource.__internal_basePost({ body: { strategy }, action: 'prepare_verification', }); - const { message } = this.resource.verifications.web3Wallet; + const { message } = this.#resource.verifications.web3Wallet; if (!message) { clerkVerifyWeb3WalletCalledBeforeCreate('SignUp'); } @@ -927,7 +931,7 @@ class SignUpFuture implements SignUpFutureResource { } } - await this.resource.__internal_basePost({ + await this.#resource.__internal_basePost({ body: { signature, strategy }, action: 'attempt_verification', }); @@ -941,12 +945,12 @@ class SignUpFuture implements SignUpFutureResource { async finalize(params?: SignUpFutureFinalizeParams): Promise<{ error: ClerkError | null }> { const { navigate } = params || {}; - return runAsyncResourceTask(this.resource, async () => { - if (!this.resource.createdSessionId) { + return runAsyncResourceTask(this.#resource, async () => { + if (!this.#resource.createdSessionId) { throw new Error('Cannot finalize sign-up without a created session.'); } - await SignUp.clerk.setActive({ session: this.resource.createdSessionId, navigate }); + await SignUp.clerk.setActive({ session: this.#resource.createdSessionId, navigate }); }); } } diff --git a/packages/clerk-js/src/core/resources/__tests__/SignIn.test.ts b/packages/clerk-js/src/core/resources/__tests__/SignIn.test.ts index d168a79d57d..8460a87ce49 100644 --- a/packages/clerk-js/src/core/resources/__tests__/SignIn.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/SignIn.test.ts @@ -16,6 +16,12 @@ vi.mock('../../../utils/authenticateWithPopup', async () => { import { _futureAuthenticateWithPopup } from '../../../utils/authenticateWithPopup'; describe('SignIn', () => { + it('can be serialized with JSON.stringify', () => { + const signIn = new SignIn(); + const snapshot = JSON.stringify(signIn); + expect(snapshot).toBeDefined(); + }); + describe('signIn.create', () => { afterEach(() => { vi.clearAllMocks(); @@ -71,6 +77,12 @@ describe('SignIn', () => { }); describe('SignInFuture', () => { + it('can be serialized with JSON.stringify', () => { + const signIn = new SignIn(); + const snapshot = JSON.stringify(signIn.__internal_future); + expect(snapshot).toBeDefined(); + }); + describe('selectFirstFactor', () => { beforeAll(() => { const signInCreatedJSON = { diff --git a/packages/clerk-js/src/core/resources/__tests__/SignUp.test.ts b/packages/clerk-js/src/core/resources/__tests__/SignUp.test.ts index a0f6dbf653b..d6771c120c4 100644 --- a/packages/clerk-js/src/core/resources/__tests__/SignUp.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/SignUp.test.ts @@ -26,7 +26,19 @@ vi.mock('../../../utils/captcha/CaptchaChallenge', () => ({ })); describe('SignUp', () => { + it('can be serialized with JSON.stringify', () => { + const signUp = new SignUp(); + const snapshot = JSON.stringify(signUp); + expect(snapshot).toBeDefined(); + }); + describe('SignUpFuture', () => { + it('can be serialized with JSON.stringify', () => { + const signUp = new SignUp(); + const snapshot = JSON.stringify(signUp.__internal_future); + expect(snapshot).toBeDefined(); + }); + describe('create', () => { afterEach(() => { vi.clearAllMocks();