Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/big-queens-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@clerk/clerk-js': minor
'@clerk/types': minor
---

[Experimental] Signal SignUp APIs
48 changes: 48 additions & 0 deletions packages/clerk-js/src/core/resources/SignUp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
PreparePhoneNumberVerificationParams,
PrepareVerificationParams,
PrepareWeb3WalletVerificationParams,
SetActiveNavigate,
SignUpAuthenticateWithWeb3Params,
SignUpCreateParams,
SignUpField,
Expand Down Expand Up @@ -40,6 +41,7 @@ import { _authenticateWithPopup } from '../../utils/authenticateWithPopup';
import { CaptchaChallenge } from '../../utils/captcha/CaptchaChallenge';
import { createValidatePassword } from '../../utils/passwords/password';
import { normalizeUnsafeMetadata } from '../../utils/resourceParams';
import { runAsyncResourceTask } from '../../utils/runAsyncResourceTask';
import {
clerkInvalidFAPIResponse,
clerkMissingOptionError,
Expand Down Expand Up @@ -470,9 +472,55 @@ export class SignUp extends BaseResource implements SignUpResource {
}

class SignUpFuture implements SignUpFutureResource {
verifications = {
sendEmailCode: this.sendEmailCode.bind(this),
verifyEmailCode: this.verifyEmailCode.bind(this),
};

constructor(readonly resource: SignUp) {}

get status() {
return this.resource.status;
}

get unverifiedFields() {
return this.resource.unverifiedFields;
}

async password({ emailAddress, password }: { emailAddress: string; password: string }): Promise<{ error: unknown }> {
return runAsyncResourceTask(this.resource, async () => {
await this.resource.__internal_basePost({
path: this.resource.pathRoot,
body: { emailAddress, password },
});
});
}
Comment on lines +490 to +497
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Use existing create flow for password sign-ups (captcha, normalization, consistency)

Calling __internal_basePost here bypasses the logic in create (captcha handling, normalization, retries). Reuse create for parity with existing flows and to reduce divergence.

Apply this diff:

-  async password({ emailAddress, password }: { emailAddress: string; password: string }): Promise<{ error: unknown }> {
-    return runAsyncResourceTask(this.resource, async () => {
-      await this.resource.__internal_basePost({
-        path: this.resource.pathRoot,
-        body: { emailAddress, password },
-      });
-    });
-  }
+  async password({ emailAddress, password }: { emailAddress: string; password: string }): Promise<{ error: unknown }> {
+    return runAsyncResourceTask(this.resource, async () => {
+      await this.resource.create({ emailAddress, password });
+    });
+  }

Notes:

  • This preserves captcha gating and any environment reload behavior present in create.
  • It also ensures any parameter normalization used by create is applied (e.g., naming conventions).

🏁 Script executed:

#!/bin/bash
set -e

echo "Searching for 'async create' implementation in SignUp.ts..."
rg -nP --type=ts 'async\s+create\b' -C5 -A30 packages/clerk-js/src/core/resources/SignUp.ts

Length of output: 237


🏁 Script executed:

#!/bin/bash
set -e

echo "Listing TypeScript files in resources directory:"
fd -e ts packages/clerk-js/src/core/resources

echo -e "\nSearching for 'async create' definitions in resources directory:"
rg -nP --type=ts 'async\s+create\b' -C3 -n packages/clerk-js/src/core/resources

echo -e "\nSearching for '__internal_basePost' definition sites:"
rg -nP '__internal_basePost\b' -C3 -n packages/clerk-js/src/core/resources

Length of output: 613


🏁 Script executed:

#!/bin/bash
set -e

echo "1) Listing all TS files under packages/clerk-js/src/core/resources:"
find packages/clerk-js/src/core/resources -type f -name '*.ts'

echo -e "\n2) Searching for 'async create' definitions across all TS files:"
rg -nP --type=ts 'async\s+create\b' -C3

echo -e "\n3) Searching for any 'create(' calls within resource classes:"
find packages/clerk-js/src/core/resources -type f -name '*.ts' -print0 \
  | xargs -0 rg -nP '\bcreate\s*\(' -C3

echo -e "\n4) Searching for '__internal_basePost' definitions across resource files:"
rg -nP '__internal_basePost\b' -n -C3 --type=ts packages/clerk-js/src/core/resources

echo -e "\n5) Locating the definition of runAsyncResourceTask:"
rg -n 'runAsyncResourceTask' -n -C3 --type=ts packages/clerk-js/src/core

Length of output: 50867



---

🏁 Script executed:

```shell
#!/bin/bash
set -e

echo "1) Locate BaseResource class definition in Base.ts:"
rg -nP 'class\s+BaseResource' -C10 packages/clerk-js/src/core/resources/Base.ts

echo -e "\n2) Search for 'create' method definitions in BaseResource:"
rg -nP '^\s*(static\s+)?async\s+create\b' -n -C5 packages/clerk-js/src/core/resources/Base.ts || true

echo -e "\n3) Search for non-async 'create' declarations (e.g., property) in BaseResource:"
rg -nP '^\s*(static\s+)?create\s*=' -n -C5 packages/clerk-js/src/core/resources/Base.ts || true

echo -e "\n4) Inspect initial 200 lines of Base.ts for context:"
sed -n '1,200p' packages/clerk-js/src/core/resources/Base.ts | sed -e 's/$/↵/'

echo -e "\n5) Look for create overrides in SignUp.ts:"
rg -nP '^\s*(static\s+)?async\s+create\b' -n -C5 packages/clerk-js/src/core/resources/SignUp.ts || true
rg -nP '\bcreate\s*=' -n -C5 packages/clerk-js/src/core/resources/SignUp.ts || true

Length of output: 9002


🏁 Script executed:

#!/bin/bash
set -e

echo "Searching for _basePost definition in BaseResource:"
rg -nP 'protected\s+async\s+_basePost' -n -C5 packages/clerk-js/src/core/resources/Base.ts || true

echo -e "\nSearching for any _basePost implementation elsewhere in core:"
rg -nP 'async\s+_basePost' -n -C5 packages/clerk-js/src/core || true

Length of output: 2717


🏁 Script executed:

#!/bin/bash
set -e

echo "Extracting 'create' implementation in SignUp.ts around its definition:"
rg -nP 'create\s*=\s*async' -n -C15 packages/clerk-js/src/core/resources/SignUp.ts

Length of output: 1561


🏁 Script executed:

#!/bin/bash
set -e

echo "Extracting full 'create' method in SignUp.ts:"
# Extract from the start of create to the matching closing brace by dumping a large context.
sed -n '100,260p' packages/clerk-js/src/core/resources/SignUp.ts

Length of output: 6016


🏁 Script executed:

#!/bin/bash
set -e

echo "Extracting top of SignUp.ts for type definitions:"
sed -n '1,80p' packages/clerk-js/src/core/resources/SignUp.ts

Length of output: 2799


🏁 Script executed:

#!/bin/bash
set -e

echo "Locating runAsyncResourceTask file:"
file_path=$(find packages/clerk-js/src/core -type f -name 'runAsyncResourceTask.ts')
echo "Found at: $file_path"

echo -e "\nDisplaying its contents:"
sed -n '1,200p' "$file_path"

Length of output: 334


Refactor password sign-up to reuse the existing create flow

Calling __internal_basePost in the future’s password method bypasses the captcha gating, parameter normalization, and retry logic in SignUp.create. Swap it to invoke resource.create(...) so it stays in sync with other flows.

Locations:

  • packages/clerk-js/src/core/resources/SignUp.ts: lines 490–497

Proposed change:

-  async password({ emailAddress, password }: { emailAddress: string; password: string }): Promise<{ error: unknown }> {
-    return runAsyncResourceTask(this.resource, async () => {
-      await this.resource.__internal_basePost({
-        path: this.resource.pathRoot,
-        body: { emailAddress, password },
-      });
-    });
-  }
+  async password({ emailAddress, password }: { emailAddress: string; password: string }): Promise<{ error: unknown }> {
+    return runAsyncResourceTask(this.resource, async () => {
+      await this.resource.create({ emailAddress, password });
+    });
+  }

This preserves captcha challenges, client reload behavior, retries, and any metadata normalization in create.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async password({ emailAddress, password }: { emailAddress: string; password: string }): Promise<{ error: unknown }> {
return runAsyncResourceTask(this.resource, async () => {
await this.resource.__internal_basePost({
path: this.resource.pathRoot,
body: { emailAddress, password },
});
});
}
async password({ emailAddress, password }: { emailAddress: string; password: string }): Promise<{ error: unknown }> {
return runAsyncResourceTask(this.resource, async () => {
await this.resource.create({ emailAddress, password });
});
}
🤖 Prompt for AI Agents
In packages/clerk-js/src/core/resources/SignUp.ts around lines 490 to 497, the
password sign-up method directly calls this.resource.__internal_basePost which
bypasses captcha gating, parameter normalization, retry logic and other behavior
implemented in SignUp.create; change the method to call this.resource.create({
strategy: "password", emailAddress, password }) (or the equivalent signature
used by create) wrapped in runAsyncResourceTask so it reuses SignUp.create's
flow and preserves captcha, retries, reload behavior and metadata normalization.


async sendEmailCode(): Promise<{ error: unknown }> {
return runAsyncResourceTask(this.resource, async () => {
await this.resource.__internal_basePost({
body: { strategy: 'email_code' },
action: 'prepare_verification',
});
});
}

async verifyEmailCode({ code }: { code: string }): Promise<{ error: unknown }> {
return runAsyncResourceTask(this.resource, async () => {
await this.resource.__internal_basePost({
body: { strategy: 'email_code', code },
action: 'attempt_verification',
});
});
}

async finalize({ navigate }: { navigate?: SetActiveNavigate }): Promise<{ error: unknown }> {
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 });
});
}
}
8 changes: 8 additions & 0 deletions packages/types/src/signUp.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { PhoneCodeChannel } from 'phoneCodeChannel';

import type { FirstNameAttribute, LastNameAttribute, LegalAcceptedAttribute, PasswordAttribute } from './attributes';
import type { SetActiveNavigate } from './clerk';
import type { AttemptEmailAddressVerificationParams, PrepareEmailAddressVerificationParams } from './emailAddress';
import type {
EmailAddressIdentifier,
Expand Down Expand Up @@ -120,6 +121,13 @@ export interface SignUpResource extends ClerkResource {

export interface SignUpFutureResource {
status: SignUpStatus | null;
unverifiedFields: SignUpIdentificationField[];
verifications: {
sendEmailCode: () => Promise<{ error: unknown }>;
verifyEmailCode: (params: { code: string }) => Promise<{ error: unknown }>;
};
password: (params: { emailAddress: string; password: string }) => Promise<{ error: unknown }>;
finalize: (params: { navigate?: SetActiveNavigate }) => Promise<{ error: unknown }>;
}

export type SignUpStatus = 'missing_requirements' | 'complete' | 'abandoned';
Expand Down
Loading